Windows下FFMPEG调用测试(转码加水印)

使用FFMPEG的avcodec,解码加水印,编码

注意是软编解码,延时会比较大,只是一个例子

demo比较乱,仅示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


extern "C" {

#include <libavcodec/avcodec.h>
#include <libavutil/frame.h>

#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>

    char av_error[AV_ERROR_MAX_STRING_SIZE] = { 0 };
#define av_err2str(errnum) av_make_error_string(av_error, AV_ERROR_MAX_STRING_SIZE, errnum)

#define INBUF_SIZE 4096
    static FILE* fout;
    static FILE* foutH264;
    AVCodecContext* enc_ctx = NULL;
    AVPacket* pkt2 = av_packet_alloc();
    const char* fileOutH264name = "D:/Emscripten/cmaketest/123523.264";

static void pgm_save(AVFrame* frame, int xsize, int ysize,
        const char* filename)
    {

        int i;
        if (!fout)
        {
            fout = fopen(filename, "wb");
        }


        //linesize中存储的是宽度,不是总长度
        //写入顺序,Y  V  U =YV12 ;解码后的数据是YUV顺序存储的
        for (i = 0; i < ysize; i++)
            fwrite(frame->data[0] + i * frame->linesize[0], 1, xsize, fout);

        for (i = 0; i < ysize / 2; i++)
            fwrite(frame->data[2] + i * frame->linesize[2], 1, xsize / 2, fout);

        for (i = 0; i < ysize / 2; i++)
            fwrite(frame->data[1] + i * frame->linesize[1], 1, xsize / 2, fout);

    }


    /**
     * 为YUV420P格式的AVFrame添加水印
     * @param frame         目标帧(需确保为YUV420P格式)
     * @param watermark_y   Y分量水印数据
     * @param watermark_w   水印宽度
     * @param watermark_h   水印高度
     * @param start_x       水印左上角X坐标
     * @param start_y       水印左上角Y坐标
     * @param alpha         透明度(0.0~1.0,1为完全不透明)
     */
    void add_watermark_to_yuv420p(
        AVFrame* frame,
        uint8_t* watermark_y,
        int watermark_w,
        int watermark_h,
        int start_x,
        int start_y,
        float alpha
    ) {
        // ----------------- Y 分量处理 -----------------
        for (int y = 0; y < watermark_h; y++) {
            // 计算目标行位置(防止越界)
            int dst_y = start_y + y;
            if (dst_y >= frame->height) break;

            // 获取Y平面行指针
            uint8_t* dst_line = frame->data[0] + dst_y * frame->linesize[0];

            for (int x = 0; x < watermark_w; x++) {
                int dst_x = start_x + x;
                if (dst_x >= frame->width) break;

                // Y分量混合:alpha混合算法
                uint8_t src_y = watermark_y[y * watermark_w + x];
                dst_line[dst_x] = (uint8_t)(src_y * alpha);
            }
        }

      //不需要修改UV分量
    }

    static void encode(AVCodecContext* enc_ctx, AVFrame* frame, AVPacket* pkt)
    {

        static int index = 0;
       
        int ret;
        if (foutH264 == NULL)
        {
            foutH264 = fopen(fileOutH264name, "wb");
            if (!foutH264) {
                fprintf(stderr, "Could not open %s\n", fileOutH264name);
                exit(1);
            }
        }
        frame->pts += index;
        index++;
        /* send the frame to the encoder */
        if (frame)
            printf("Send frame %3ld\n", frame->pts);

        ret = avcodec_send_frame(enc_ctx, frame);
        if (ret < 0) {
            fprintf(stderr, "Error sending a frame for encoding\n");
            exit(1);
        }

        while (ret >= 0) {
            ret = avcodec_receive_packet(enc_ctx, pkt);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
                return;
            else if (ret < 0) {
                fprintf(stderr, "Error during encoding\n");
                exit(1);
            }

            //printf("Write packet %3ld (size=%5d)\n", pkt->pts, pkt->size);
            int ref = fwrite(pkt->data, 1, pkt->size, foutH264);
            printf("write size :%d\n",ref);
            fflush(foutH264);
            av_packet_unref(pkt);
        }
    }

    static  void initEnc_1()
   {
        const AVCodec* codec;
        const char* codec_name = "libx264";
        //查找编码器;还可以使用AVcodec_find_encoder(condec_ID)
        codec = avcodec_find_encoder_by_name(codec_name);
        if (!codec) {
            fprintf(stderr, "Codec '%s' not found\n", codec_name);
            exit(1);
        }
       
        enc_ctx = avcodec_alloc_context3(codec);
        if (!enc_ctx) {
            fprintf(stderr, "Could not allocate video codec context\n");
            exit(1);
        }

        /* put sample parameters */
        enc_ctx->bit_rate = 400000;
        /* resolution must be a multiple of two */
        enc_ctx->width = 1920;
        enc_ctx->height = 1080;
        /* frames per second */
        enc_ctx->time_base.num = 1;
        enc_ctx->time_base.den = 25;//时间基1/25
        enc_ctx->framerate.num = 25; //fps=25
        enc_ctx->framerate.den = 1;
        enc_ctx->gop_size = 30;
        enc_ctx->max_b_frames = 1;
        enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;//YV 12 格式
        if (codec->id == AV_CODEC_ID_H264)
            av_opt_set(enc_ctx->priv_data, "preset", "slow", 0);

        /* open it */
        int ret = avcodec_open2(enc_ctx, codec, NULL);
        if (ret < 0) {
            fprintf(stderr, "Could not open codec: %s\n", av_err2str(ret));
            exit(1);
        }
   }



    static void decode(AVCodecContext* dec_ctx, AVFrame* frame, AVPacket* pkt,
        const char* filename)
    {
        char buf[1024];
        int ret;

        ret = avcodec_send_packet(dec_ctx, pkt);
        if (ret < 0) {
            fprintf(stderr, "Error sending a packet for decoding\n");
            return;
//这里不要exit,因为你有时候可能解析的是非IDR的多slice帧,并且找不到SPS,那就要解码器吧一屏所有的slice解码完才会有输出;
} while (ret >= 0) { ret = avcodec_receive_frame(dec_ctx, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) return; else if (ret < 0) { fprintf(stderr, "Error during decoding\n"); exit(1); } printf("saving frame %3ld\n", dec_ctx->frame_num); printf("YUV pts:%ld %ld ,%ld\n", frame->pts,frame->pict_type); fflush(stdout); //添加水印 // 定义水印参数 // 生成100x100的"大"文字水印(Y分量) uint8_t watermark_y[100 * 100]; memset(watermark_y, 0x3F, sizeof(watermark_y)); // -----------------绘制水印 ----------------- for (int y = 20; y < 40; y++) { // for (int x = 20; x < 80; x++) { watermark_y[y * 100 + x] = 0x00; } } add_watermark_to_yuv420p( frame, watermark_y, 100, 100, 0, 0, 1.0f // 70%不透明度 ); // pgm_save(frame, frame->width, frame->height, filename); //编码 // frame->pts += 1;//时间基 1/25 if (!pkt2) exit(1); encode(enc_ctx, frame, pkt2); } } static int InPutH264data() { } int main(int argc, char** argv) { const char* filename, * outfilename; const AVCodec* codec; AVCodecParserContext* parser; AVCodecContext* c = NULL; FILE* f; AVFrame* frame; uint8_t inbuf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE]; uint8_t* data; size_t data_size; int ret; int eof; AVPacket* pkt; //初始化编码 initEnc_1(); //注意直接解码要从I帧开始,所以如果H264开头不是关键帧是要报错的 filename = "D:/Emscripten/cmaketest/catch.264"; outfilename = "D:/Emscripten/cmaketest/catch_out123.yuv"; pkt = av_packet_alloc(); if (!pkt) exit(1); /* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */ memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE); /* find the MPEG-1 video decoder */ codec = avcodec_find_decoder(AV_CODEC_ID_H264); if (!codec) { fprintf(stderr, "Codec not found\n"); exit(1); } //用来给H264断帧 parser = av_parser_init(codec->id); if (!parser) { fprintf(stderr, "parser not found\n"); exit(1); } c = avcodec_alloc_context3(codec); if (!c) { fprintf(stderr, "Could not allocate video codec context\n"); exit(1); } /* For some codecs, such as msmpeg4 and mpeg4, width and height MUST be initialized there because this information is not available in the bitstream. */ /* open it */ if (avcodec_open2(c, codec, NULL) < 0) { fprintf(stderr, "Could not open codec\n"); exit(1); } f = fopen(filename, "rb"); if (!f) { fprintf(stderr, "Could not open %s\n", filename); exit(1); } frame = av_frame_alloc(); if (!frame) { fprintf(stderr, "Could not allocate video frame\n"); exit(1); } do { /* read raw data from the input file */ data_size = fread(inbuf, 1, INBUF_SIZE, f); if (ferror(f)) break; eof = !data_size; /* use the parser to split the data into frames */ data = inbuf;
//如果读文件的时候帧带长度字段可以直接赋值给pkt不需要解析断帧
while (data_size > 0 || eof) { ret = av_parser_parse2(parser, c, &pkt->data, &pkt->size, data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0); if (ret < 0) { fprintf(stderr, "Error while parsing\n"); exit(1); } data += ret; data_size -= ret; if (pkt->size) decode(c, frame, pkt, outfilename); else if (eof) break; } } while (!eof); /* flush the decoder */ decode(c, frame, NULL, outfilename); fclose(f); av_parser_close(parser); avcodec_free_context(&c); av_frame_free(&frame); av_packet_free(&pkt); if (fout) { fclose(fout); fout = nullptr; } if (foutH264) { fclose(foutH264); foutH264 = nullptr; } return 0; } }

 非IDR的H264;竖向多slice;(有时候安卓还有横向的多slice)

 

posted on 2025-04-24 18:46  邗影  阅读(8)  评论(0)    收藏  举报

导航