本文共 6659 字,大约阅读时间需要 22 分钟。
使用ffmpeg添加图片水印的实现方法
在视频处理领域,ffmpeg是一个强大的工具,能够实现丰富的功能之一就是图片水印的添加。以下将详细介绍如何利用ffmpeg的filter功能,为图片添加水印。
首先,我们需要包含必要的库文件,确保编译环境的支持。代码中我们引入了标准输入输出流文件头,ffmpeg的核心编解码器头文件,以及filter处理相关的头文件。代码结构如下:
#include <stdio.h>#include <libavfilter/avfilter.h>#include <libavfilter/buffersrc.h>#include <libavfilter/buffersink.h>#include <libavformat/avformat.h>#include <libavcodec/avcodec.h>
接下来,我们定义了几个上下文变量,用于处理主图、Logo图以及最终的合成图。这些变量的作用是维护ffmpeg处理过程中的各个阶段。
AVFilterContext* mainsrc_ctx = NULL;AVFilterContext* logosrc_ctx = NULL;AVFilterContext* resultsink_ctx = NULL;AVFilterGraph* filter_graph = NULL;
初始化过滤器处理函数init_filters的实现步骤如下:
代码实现如下:
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];" "buffer=video_size=%dx%d:pix_fmt=%d:time_base=1/25:pixel_aspect=%d/%d[logo];" "[main][logo]overlay=%d:%d[result];" "[result]buffersink", 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);// 解析并配置过滤器图表ret = avfilter_graph_parse2(filter_graph, filter_args, &inputs, &outputs);if (ret < 0) { printf("%d : avfilter_graph_parse2() failed!\n", __LINE__); return ret;}// 配置过滤器图表ret = avfilter_graph_config(filter_graph, NULL);if (ret < 0) { printf("%d : avfilter_graph_config() failed!\n", __LINE__); return ret;}// 获取各个上下文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;
}
添加水印的实现主要包含两个步骤:添加主图和Logo图,并对最终结果进行合成。具体实现如下:
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;
}
实现图片读取的函数get_jpeg的具体实现如下:
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", __LINE__); 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);if (!codec_ctx) goto cleanup;// 初始化解码器上下文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); goto cleanup;}
cleanup:if (format_ctx) avformat_close_input(&format_ctx);if (codec_ctx) avcodec_free_context(&codec_ctx);return frame;}
将处理后的图像保存为jpeg格式的函数savejpeg的实现如下:
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;// 添加编码器选项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", __LINE__); 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", __LINE__); return -4;}// 接收编码数据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", __LINE__); ret = -1; break; } if (fwrite((char*)pkt.data, 1, pkt.size, outfile) == pkt.size) { ret = 0; } else { printf("%d : fwrite failed!\n", __LINE__); ret = -1; break; } fclose(outfile); ret = 0; break;}avcodec_free_context(&jpeg_codec_ctx);return ret;
}
最后,主函数main的实现步骤如下:
代码实现如下:
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;}
转载地址:http://rnqn.baihongyu.com/