使用ffmpeg 的 filter 给图片添加水印

使用ffmpeg 的 filter 给图片添加水印。
main.c

#include <stdio.h>

#include <libavfilter/avfilter.h>
#include <libavfilter/buffersrc.h>
#include <libavfilter/buffersink.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>

AVFilterContext* mainsrc_ctx = NULL;
AVFilterContext* logosrc_ctx = NULL;
AVFilterContext* resultsink_ctx = NULL;
AVFilterGraph* filter_graph = NULL;

//初始化过滤器处理
static int init_filters(const AVFrame* main_frame, const AVFrame* logo_frame, int x, int y)
{
    
    
    int ret = 0;
    AVFilterInOut* inputs = NULL;
    AVFilterInOut* outputs = NULL;
    char filter_args[1024] = {
    
    0};

    //初始化用于整个过滤处理的封装
    filter_graph = avfilter_graph_alloc();
    if(!filter_graph)
    {
    
    
        printf("%d : avfilter_graph_alloc() failed!\n", __LINE__);
        return -1;
    }

    //所有使用到过滤器处理的命令
    snprintf(filter_args, sizeof(filter_args),
             "buffer=video_size=%dx%d:pix_fmt=%d:time_base=1/25:pixel_aspect=%d/%d[main];" // Parsed_buffer_0
             "buffer=video_size=%dx%d:pix_fmt=%d:time_base=1/25:pixel_aspect=%d/%d[logo];" // Parsed_bufer_1
             "[main][logo]overlay=%d:%d[result];" // Parsed_overlay_2
             "[result]buffersink", // Parsed_buffer_sink_3
             main_frame->width, main_frame->height, main_frame->format, main_frame->sample_aspect_ratio.num, main_frame->sample_aspect_ratio.den,
             logo_frame->width, logo_frame->height, logo_frame->format, logo_frame->sample_aspect_ratio.num, logo_frame->sample_aspect_ratio.den,
             x, y);
    //添加过滤器处理到AVFilterGraph
    ret = avfilter_graph_parse2(filter_graph, filter_args, &inputs, &outputs);
    if(ret < 0)
    {
    
    
        printf("%d : avfilter_graph_parse2() failed!\n", __LINE__);
        return ret;
    }

    //配置AVFilterGraph的过滤器处理
    ret = avfilter_graph_config(filter_graph, NULL);
    if(ret < 0)
    {
    
    
        printf("%d : avfilter_graph_config() failed!\n", __LINE__);
        return ret;
    }

    //获取AVFilterGraph内的过滤器
    mainsrc_ctx = avfilter_graph_get_filter(filter_graph, "Parsed_buffer_0");
    logosrc_ctx = avfilter_graph_get_filter(filter_graph, "Parsed_buffer_1");
    resultsink_ctx = avfilter_graph_get_filter(filter_graph, "Parsed_buffersink_3");

    avfilter_inout_free(&inputs);
    avfilter_inout_free(&outputs);
    return 0;
}

//添加水印
static int main_mix_logo(AVFrame* main_frame, AVFrame* logo_frame, AVFrame *result_frame)
{
    
    
    int ret = 0;
    //添加主图
    ret = av_buffersrc_add_frame(mainsrc_ctx, main_frame);
    if(ret < 0)
        return ret;
    //添加logo
    ret = av_buffersrc_add_frame(logosrc_ctx, logo_frame);
    if(ret < 0)
        return ret;
    //获取合成图
    ret = av_buffersink_get_frame(resultsink_ctx, result_frame);

    return ret;

}

static AVFrame* get_jpeg(const char* filename)
{
    
    
    int ret = 0;
    AVFormatContext* format_ctx = NULL;
    //打开文件
    if((ret = avformat_open_input(&format_ctx, filename, NULL, NULL)) != 0)
    {
    
    
        printf("%d : avformat_open_input() failed!\n");
        return NULL;
    }

    //获取媒体文件信息
    avformat_find_stream_info(format_ctx, NULL);

    AVCodec* codec = NULL;
    AVCodecContext* codec_ctx = NULL;
    int video_stream_index = -1;
    //获取流
    video_stream_index = av_find_best_stream(format_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0);
    if(video_stream_index < 0)
        goto cleanup;

    codec_ctx = avcodec_alloc_context3(codec);
    //关联解码器上下文
    ret = avcodec_open2(codec_ctx, codec, NULL);
    if(ret < 0)
        goto cleanup;

    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;
    //从文件中读一帧
    ret = av_read_frame(format_ctx, &pkt);
    if(ret < 0)
        goto cleanup;

    //进行解码
    ret = avcodec_send_packet(codec_ctx, &pkt);
    if(ret < 0)
        goto cleanup;
    //
    AVFrame* frame = av_frame_alloc();
    ret = avcodec_receive_frame(codec_ctx, frame);
    if(ret < 0)
        av_frame_free(&frame);

cleanup:
    if(format_ctx)
        avformat_close_input(&format_ctx);
    if(codec_ctx)
        avcodec_free_context(&codec_ctx);

    return frame;
}

static int savejpeg(const char* filename, const AVFrame* frame)
{
    
    

    //查找相应编码器
    AVCodec* jpeg_codec = avcodec_find_encoder(AV_CODEC_ID_MJPEG);
    if(!jpeg_codec)
        return -1;
    AVCodecContext* jpeg_codec_ctx = avcodec_alloc_context3(jpeg_codec);
    if(!jpeg_codec_ctx)
        return -2;

    //设置jpeg相关参数
    jpeg_codec_ctx->pix_fmt = AV_PIX_FMT_YUVJ420P;
    jpeg_codec_ctx->width = frame->width;
    jpeg_codec_ctx->height = frame->height;
    jpeg_codec_ctx->time_base.num = 1;
    jpeg_codec_ctx->time_base.den = 25;
    jpeg_codec_ctx->framerate.num = 25;
    jpeg_codec_ctx->framerate.den = 1;
    AVDictionary* encoder_opts = NULL;
    //encoder_opts 为空就会分配内存的
    av_dict_set(&encoder_opts, "flags", "+qscale", 0);
    av_dict_set(&encoder_opts, "qmax", "2", 0);
    av_dict_set(&encoder_opts, "qmin", "2", 0);
    int ret = avcodec_open2(jpeg_codec_ctx, jpeg_codec, &encoder_opts);
    if(ret < 0)
    {
    
    
        avcodec_free_context(&jpeg_codec_ctx);
        printf("%d : avcodec_open2() failed!\n");
        return -3;
    }
    av_dict_free(&encoder_opts);

    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;

    //编码
    ret = avcodec_send_frame(jpeg_codec_ctx, frame);
    if(ret < 0)
    {
    
    
        avcodec_free_context(&jpeg_codec_ctx);
        printf("%d : avcodec_send_frame() failed!\n");
        return -4;
    }

    ret = 0;
    while(ret >= 0)
    {
    
    
        //得到编码数据
        ret = avcodec_receive_packet(jpeg_codec_ctx, &pkt);
        if(ret == AVERROR(EAGAIN))
            continue;
        if(ret == AVERROR_EOF)
        {
    
    
            ret = 0;
            break;
        }
        FILE* outfile = fopen(filename, "wb");
        if(!outfile)
        {
    
    
            printf("%d : fopen() failed!\n");
            ret = -1;
            break;
        }
        //写入文件
        if(fwrite((char*)pkt.data, 1, pkt.size, outfile) == pkt.size)
        {
    
    
            ret = 0;
        }
        else
        {
    
    
            printf("%d : fwrite failed!\n");
            ret = -1;
        }
        fclose(outfile);
        ret = 0;
        break;
    }

    avcodec_free_context(&jpeg_codec_ctx);
    return ret;
}

int main()
{
    
    
    printf("Hello watermarkmix!\n");

    AVFrame *main_frame = get_jpeg("main.jpg");
    AVFrame *logo_frame = get_jpeg("logo.jpg");
    AVFrame* result_frame = av_frame_alloc();
    int ret = 0;

    if(ret = init_filters(main_frame, logo_frame, 100, 200) < 0) {
    
    
        printf("%d : init_filters failed\n", __LINE__);
        goto end;
    }


    if(main_mix_logo(main_frame, logo_frame, result_frame) < 0) {
    
    
        printf("%d : main_picture_mix_logo failed\n", __LINE__);
        goto end;
    }


    savejpeg("output.jpg", result_frame);
end:
    if(main_frame)
        av_frame_free(&main_frame);
    if(logo_frame)
        av_frame_free(&logo_frame);
    if(result_frame)
        av_frame_free(&result_frame);
    if(filter_graph)
        avfilter_graph_free(&filter_graph);
    printf("finish\n");

    printf("End watermarkmix!\n");
    return 0;
}







猜你喜欢

转载自blog.csdn.net/m0_37599645/article/details/112759962