给视频添加图片水印可用以下命令:
ffmpeg -i 123.mp4 -vf "movie=logo.png[wm1];[in][wm1] overlay=main_w-overlay_w-10:main_h-overlay_h-10[out] " test.mp4
下面来说明一下这条命令中基本参数的含义:
-i :表示输入
123.mp4: 表示要处理的视频源
-vf:滤镜相关,视频裁剪,水印等等操作都需要它完成
logo.png:要添加的水印图片地址
overlay:水印参数
main_w-overlay_w-10:水印在x轴的位置
main_h-overlay_h-10:水印在y轴的位置
main_w:视频宽度
main_h:视频高度
overlay_w:水印图片宽度
overlay_h:水印图片高度
代码实现,这里封装了滤镜水印命令,方便调用
//
// 水印坐标方向
//
typedef enum _DIRECTION
{
left_top = 1, //左上角
left_bottom = 2, //左下角
right_top = 3, //右上角
right_bottom = 4 //右下角
}DIRECTION;
typedef struct _DIRECTION_STR
{
char* left_top_str = "%d:%d"; //左上角
char* left_bottom_str = "%d:main_h-overlay_h-%d"; //左下角
char* right_top_str = "main_w-overlay_w-%d:%d"; //右上角
char* right_bottom_str = "main_w-overlay_w-%d:main_h-overlay_h-%d"; //右下角
//char* right_bottom_str = "w-tw-%d:h-th-%d"; //右下角
char* in_str = "[in]";
char* out_str = "[out]";
char* a_str = "[a]";
}DIRECTION_STR;
//
// 水印内容结构体,图片路径,坐标等信息
//
typedef struct _WATERMARK_PICTURE_INFO
{
char picname[128];
DIRECTION direction;
int x;
int y;
}WATERMARK_PICTURE_INFO;
//
// 图片水印命令结构体
//
typedef struct _WATERMARK_PICTURE_STR
{
char* movie_command_str = "movie=%s"; //图片水印:%s--图片名称[可包含路径]
char* wm_command_str = "[wm%d]"; //%d--水印序号[可添加多个水印]
char* overlay_command_str = "%soverlay=%s"; //%s--水印坐标
}WATERMARK_PICTURE;
.h文件
class CWatermarkPicture
{
public:
CWatermarkPicture();
~CWatermarkPicture();
public:
int InitWatermark_Filters(AVCodecContext *pCodecCtx, WATERMARK_PICTURE_INFO** wInfos);
AVFilterContext* GetBuffersrc_ctx(){ return buffersrc_ctx; };
AVFilterContext* GetBuffersink_ctx(){ return buffersink_ctx; };
void Dispose();
private:
int InitDescr(char* filter_descr, WATERMARK_PICTURE_INFO** wInfos);
private:
AVFilterGraph *filter_graph;
AVFilterContext *buffersrc_ctx;
AVFilterContext *buffersink_ctx;
};
.cpp文件
CWatermarkPicture::CWatermarkPicture()
{
}
CWatermarkPicture::~CWatermarkPicture()
{
}
void CWatermarkPicture::Dispose()
{
if (buffersrc_ctx)
avfilter_free(buffersrc_ctx);
if (buffersink_ctx)
avfilter_free(buffersink_ctx);
if (filter_graph)
avfilter_graph_free(&filter_graph);
}
int CWatermarkPicture::InitDescr(char* filter_descr, WATERMARK_PICTURE_INFO** wInfos)
{
int wInfos_leng = sizeof(wInfos) / sizeof(WATERMARK_PICTURE_INFO*);
if (wInfos_leng <= 0)
return -1;
char picInfo[1024] = "";
char directionInfo[1024] = "";
WATERMARK_PICTURE watermark;
DIRECTION_STR direction_str;
//遍历水印集合
for (int i = 0; i < wInfos_leng; i++)
{
char oneInfo1[512];
snprintf(oneInfo1, sizeof(oneInfo1), watermark.movie_command_str, wInfos[i]->picname);
char oneInfo2[16];
snprintf(oneInfo2, sizeof(oneInfo2), watermark.wm_command_str, i + 1);
char oneInfo3[64];
switch (wInfos[i]->direction)
{
case DIRECTION::left_top:
snprintf(oneInfo3, sizeof(oneInfo3), direction_str.left_top_str, wInfos[i]->x, wInfos[i]->y);
break;
case DIRECTION::left_bottom:
snprintf(oneInfo3, sizeof(oneInfo3), direction_str.left_bottom_str, wInfos[i]->x, wInfos[i]->y);
break;
case DIRECTION::right_top:
snprintf(oneInfo3, sizeof(oneInfo3), direction_str.right_top_str, wInfos[i]->x, wInfos[i]->y);
break;
case DIRECTION::right_bottom:
snprintf(oneInfo3, sizeof(oneInfo3), direction_str.right_bottom_str, wInfos[i]->x, wInfos[i]->y);
break;
}
char oneInfo4[512];
snprintf(oneInfo4, sizeof(oneInfo4), watermark.overlay_command_str, oneInfo2, oneInfo3);
strcat(picInfo, oneInfo1);
strcat(picInfo, oneInfo2);
strcat(picInfo, ";");
if (i == 0)
strcat(directionInfo, direction_str.in_str);
strcat(directionInfo, oneInfo4);
if (wInfos_leng > 1 && i < wInfos_leng - 1)
strcat(directionInfo, direction_str.a_str);
if (i < wInfos_leng - 1)
strcat(directionInfo, ";");
}
strcat(directionInfo, direction_str.out_str);
strcat(filter_descr, picInfo);
strcat(filter_descr, directionInfo);
return 0;
}
int CWatermarkPicture::InitWatermark_Filters(AVCodecContext *pCodecCtx, WATERMARK_PICTURE_INFO** wInfos)
{
int result = 0;
char filters_descr[1024] = "";
if (result = InitDescr(filters_descr, wInfos) < 0)
goto End;
if (!pCodecCtx)
goto End;
AVFilter* buffersrc = avfilter_get_by_name("buffer");
AVFilter* buffersink = avfilter_get_by_name("ffbuffersink");
AVBufferSinkParams *buffersink_params;
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
filter_graph = avfilter_graph_alloc();
char arg[512];
snprintf(arg, sizeof(arg),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->time_base.num, pCodecCtx->time_base.den,
pCodecCtx->sample_aspect_ratio.num, pCodecCtx->sample_aspect_ratio.den);
result = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", arg, NULL, filter_graph);
if (result)
{
goto End;
}
buffersink_params = av_buffersink_params_alloc();
buffersink_params->pixel_fmts = pix_fmts;
result = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", NULL, buffersink_params, filter_graph);
av_free(buffersink_params);
if (result)
{
goto End;
}
outputs->name = av_strdup("in");
outputs->filter_ctx = buffersrc_ctx;
outputs->pad_idx = 0;
outputs->next = NULL;
inputs->name = av_strdup("out");
inputs->filter_ctx = buffersink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
if ((result = avfilter_graph_parse_ptr(filter_graph, filters_descr, &inputs, &outputs, NULL)) < 0)
goto End;
if ((result = avfilter_graph_config(filter_graph, NULL)) < 0)
goto End;
End:
return result;
}
封装好了,这里创建水印类,这里可以添加多个图片水印,例子只添加了一个图片水印
WATERMARK_PICTURE_INFO** wInfos = new WATERMARK_PICTURE_INFO*[1];
wInfos[0] = new WATERMARK_PICTURE_INFO();
snprintf(wInfos[0]->picname, sizeof(wInfos[0]->picname),"%s", "logo.png");
wInfos[0]->direction = DIRECTION::right_bottom;
wInfos[0]->x = 10;
wInfos[0]->y = 5;
CWatermarkPicture pic = {};
if (pic.InitWatermark_Filters(pCodecCtx, wInfos) < 0)
return 0;
输入解码后的图像帧和获取滤镜后的帧
// 输入图像帧
if (av_buffersrc_add_frame(pic.GetBuffersrc_ctx(), pFrame) < 0) {
break;
}
// 获取滤镜后的图像帧
ret = av_buffersink_get_frame(pic.GetBuffersink_ctx(), pFrame_out);