ffmpeg 代码实现视频添加水印功能

备忘:

用ffmpeg给视频添加水印的命令为:


ffmpeg -i in.mp4 -i xxx.png -filter_complex "overlay=5:5"  out.mp4


代码实现如下, 主要是用了参考了ffmpeg.c里面的filter_complex的代码:

#include <unistd.h>
#include <stdio.h>
#include <assert.h>

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfiltergraph.h>
#include <libavfilter/avcodec.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libswscale/swscale.h>

const char *filter_descr = "overlay=5:5"; 

#define ENABLE_YUV_FILE 1

AVFormatContext *input_fmt_ctx;
AVCodecContext *input_dec_ctx;

AVFormatContext *overlay_fmt_ctx;
AVCodecContext *overlay_dec_ctx;

int input_video_stream_idx, overlay_video_stream_idx;

AVFilterGraph *filter_graph;

AVFilterInOut *inputs;
AVFilterInOut *outputs;

AVFilterContext *buffersrc_ctx;
AVFilterContext *bufferoverlay_ctx;
AVFilterContext *buffersink_ctx;

int ret;
int got_frame;

int video_eof_reached = 0;
int overlay_eof_reached = 0;

int active_stream_index = -1;

FILE* fp_yuv;

void yuv420p_save(AVFrame *pFrame);
int video_transcode_step(AVFrame* mVideoFrame);
int overlay_transcode_step(AVFrame* mOverlayFrame);
int video_output_eof_packet(const char* tag, 
	AVStream* ist, AVFilterContext* ifilter);

static int open_input_file(const char *filename)
{
    int ret;
    AVCodec *dec;

    if ((ret = avformat_open_input(&input_fmt_ctx, filename, NULL, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");
        return ret;
    }

    if ((ret = avformat_find_stream_info(input_fmt_ctx, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
        return ret;
    }

    /* select the video stream */
    ret = av_find_best_stream(input_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file\n");
        return ret;
    }
    input_video_stream_idx = ret;
    input_dec_ctx = input_fmt_ctx->streams[input_video_stream_idx]->codec;

    /* init the video decoder */
    if ((ret = avcodec_open2(input_dec_ctx, dec, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot open video decoder\n");
        return ret;
    }

    return 0;
}


static int open_overlay_file(const char *filename)
{
    int ret;
    AVCodec *dec;

    if ((ret = avformat_open_input(&overlay_fmt_ctx, filename, NULL, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n");
        return ret;
    }

    if ((ret = avformat_find_stream_info(overlay_fmt_ctx, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n");
        return ret;
    }

    /* select the video stream */
    ret = av_find_best_stream(overlay_fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0);
    if (ret < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file\n");
        return ret;
    }
    overlay_video_stream_idx = ret;
    overlay_dec_ctx = overlay_fmt_ctx->streams[overlay_video_stream_idx]->codec;

    /* init the video decoder */
    if ((ret = avcodec_open2(overlay_dec_ctx, dec, NULL)) < 0) {
        av_log(NULL, AV_LOG_ERROR, "Cannot open video decoder\n");
        return ret;
    }

    printf("overlay format = %s\n", overlay_fmt_ctx->iformat->name);

    return 0;
}


static int video_config_input_filter(AVFilterInOut* inputs, AVFilterContext** input_filter_ctx)
{
	char args[512];
	memset(args, 0, sizeof(args));

	AVFilterContext *first_filter = inputs->filter_ctx;
	int pad_idx = inputs->pad_idx;

	AVFilter *filter = avfilter_get_by_name("buffer");
//	AVRational time_base = input_dec_ctx->time_base;
	AVStream* video_st = input_fmt_ctx->streams[input_video_stream_idx];
	AVRational time_base = video_st->time_base;

	snprintf(args, sizeof(args),
            "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d:sws_param=flags=%d:frame_rate=%d/%d",
            input_dec_ctx->width, input_dec_ctx->height, input_dec_ctx->pix_fmt,
            input_dec_ctx->time_base.num, input_dec_ctx->time_base.den,
            input_dec_ctx->sample_aspect_ratio.num, input_dec_ctx->sample_aspect_ratio.den,
	    SWS_BILINEAR + ((video_st->codec->flags&CODEC_FLAG_BITEXACT) ? SWS_BITEXACT:0),
	    video_st->r_frame_rate.num, video_st->r_frame_rate.den);

       printf("input args = %s\n", args);

	ret = avfilter_graph_create_filter(input_filter_ctx, filter, "src_in", args, NULL, filter_graph);

	if (ret < 0 ) {
		printf("video config input filter fail.\n");
		return -1;
	}
	ret = avfilter_link(*input_filter_ctx, 0, first_filter, pad_idx);
	assert(ret >= 0);

	printf("video_config_input_filter avfilter_link ret = %d\n", ret);

	return ret;

}

static int video_config_overlay_filter(AVFilterInOut* inputs, AVFilterContext** overlay_filter_ctx )
{
	char args[512];
	memset(args, 0, sizeof(args));

	AVFilterContext *first_filter = inputs->filter_ctx;

	int pad_idx = inputs->pad_idx;

	AVFilter *filter = avfilter_get_by_name("buffer");

	//AVRational time_base = overlay_dec_ctx->time_base;
	AVStream* overlay_st = overlay_fmt_ctx->streams[overlay_video_stream_idx];
	AVRational time_base = overlay_st->time_base;

	snprintf(args, sizeof(args),
            "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d:sws_param=flags=%d:frame_rate=%d/%d",
            overlay_dec_ctx->width, overlay_dec_ctx->height, overlay_dec_ctx->pix_fmt,
            time_base.num, time_base.den,
	    overlay_dec_ctx->sample_aspect_ratio.num, overlay_dec_ctx->sample_aspect_ratio.den,
	    SWS_BILINEAR + ((overlay_st->codec->flags&CODEC_FLAG_BITEXACT) ? SWS_BITEXACT:0),
            overlay_st->r_frame_rate.num, overlay_st->r_frame_rate.den);

       printf("overlay args = %s\n", args);

	ret = avfilter_graph_create_filter(overlay_filter_ctx, filter, "overlay_in", args, NULL, filter_graph);

	if (ret < 0 ) {
		printf("video config overlay filter fail.\n");
		return -1;
	}

	ret = avfilter_link(*overlay_filter_ctx, 0, first_filter, pad_idx);
	assert(ret >= 0);

	printf("video_config_overlay_filter ret = %d\n", ret);

	avfilter_inout_free(&inputs);

	return ret;

}

static int video_config_output_filter(AVFilterInOut* outputs, AVFilterContext** out_filter_ctx)
{
	char args[512];

	AVFilterContext *last_filter = outputs->filter_ctx;
	int pad_idx  = outputs->pad_idx;
	AVFilter *buffersink = avfilter_get_by_name("ffbuffersink");
	
	int ret = avfilter_graph_create_filter(out_filter_ctx, buffersink, "video_out", NULL, NULL, filter_graph);
	assert(ret >= 0);

	if (ret < 0)
		return ret;

	ret = avfilter_link(last_filter, pad_idx, *out_filter_ctx, 0);
	assert(ret >= 0);
	if (ret < 0)
		return ret;

	avfilter_inout_free(&outputs);

	return 0;

}

static int init_input_filters()
{
	filter_graph->scale_sws_opts = av_strdup("flags=0x4");
        av_opt_set(filter_graph, "aresample_swr_opts", "", 0);

	ret = avfilter_graph_parse2(filter_graph, filter_descr, &inputs, &outputs);

	assert(inputs && inputs->next && !inputs->next->next);	

	ret = video_config_input_filter(inputs, &buffersrc_ctx);

	ret = video_config_overlay_filter(inputs->next, &bufferoverlay_ctx);

	return ret;
}

static int init_output_filters()
{
	return video_config_output_filter(outputs, &buffersink_ctx);
}

int reap_filters() {
	AVFilterBufferRef *picref;

	while (1) {
		ret = av_buffersink_get_buffer_ref(buffersink_ctx, &picref, AV_BUFFERSINK_FLAG_NO_REQUEST);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
			//printf("reap_filters fail ret = %d\n", ret);
			return 0; // no frame filtered.
		}	

		printf("samplesref -------------------\n");

		AVFrame* filtered_frame = avcodec_alloc_frame();
		avcodec_get_frame_defaults(filtered_frame);

		avfilter_copy_buf_props(filtered_frame, picref);
	
		yuv420p_save(filtered_frame);
	
		avfilter_unref_bufferp(&picref);
	}

}

int transcode_from_filter(AVFilterContext** ifilters, int* eof_reached_arr, int* active_stream_indext) {
	int ret = 0;

	ret = avfilter_graph_request_oldest(filter_graph);

	if (ret >= 0) {
		return ret;
        }
	if (ret == AVERROR_EOF) {
		return ret;
	}
	if (ret != AVERROR(EAGAIN)) {
		return ret;
	}

	int nb_requests_max = 0;
	int i;
	for (i = 0; i < 2; i++) {
		int eof_reached = eof_reached_arr[i];
		if (eof_reached) {
			continue;
		}
		AVFilterContext* ifilter = ifilters[i];
		int nb_requests = av_buffersrc_get_nb_failed_requests(ifilter);
		if (nb_requests > nb_requests_max) {
		    nb_requests_max = nb_requests;
		    *active_stream_indext = i;
		}
	}

	return ret;

}


int main()
{
	avcodec_register_all();
	av_register_all();
	avfilter_register_all();
	avformat_network_init();


	char* video_file = "outFileSrc.mp4";
	char* overlay_video_file = "my_logo.png"; // light1.mp4 


#if ENABLE_YUV_FILE
	const char* yuvFile = "outWater.yuv";
	
	fp_yuv = fopen(yuvFile, "wb");

#endif

	open_input_file(video_file);
	open_overlay_file(overlay_video_file);

	filter_graph = avfilter_graph_alloc();
	if (!filter_graph) {
		printf("filter graph alloc fail.\n");
		return -1;
	}
	
	init_input_filters();
	init_output_filters();	

	if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
            return ret;

	AVFrame* mVideoFrame = avcodec_alloc_frame();
	AVFrame* mOverlayFrame = avcodec_alloc_frame();

	while(1) {
		if (video_eof_reached && overlay_eof_reached) {
	            printf("stream EOF.\n");
		    break;
		}

		AVFilterContext* ifilters[] = {buffersrc_ctx, bufferoverlay_ctx};
		int eof_reacheds[] = {video_eof_reached, overlay_eof_reached};

		ret = transcode_from_filter(ifilters, eof_reacheds, &active_stream_index);

		if (ret >= 0) {
			ret = reap_filters();
			assert(ret >= 0);
			continue;
	        }
		if (ret == AVERROR_EOF) {
			ret = reap_filters();
			assert(ret >= 0);
			continue;
		}

		if (ret == AVERROR(EAGAIN) && active_stream_index < 0) {
			continue;
		}
		assert(active_stream_index >= 0);

		printf("active_stream_index = %d\n", active_stream_index);

		if (active_stream_index == 0) {
			video_transcode_step(mVideoFrame);
			continue;
		}

		overlay_transcode_step(mOverlayFrame);
		
	}

	if (input_dec_ctx)
		avcodec_close(input_dec_ctx);
	avformat_close_input(&input_fmt_ctx);

	if (overlay_dec_ctx)
		avcodec_close(overlay_dec_ctx);
	avformat_close_input(&overlay_fmt_ctx);



	printf("my_filtering_video3 end -------\n");
	return 0;
}

int video_transcode_step(AVFrame* mVideoFrame) {
	int ret = 0;
	AVPacket pkt;

	ret = av_read_frame(input_fmt_ctx, &pkt);
	if (ret == AVERROR(EAGAIN)) {
		return 0;
	}
	if (ret < 0) {
		video_eof_reached = 1;
		assert(ret == AVERROR_EOF);
		ret = video_output_eof_packet("video_eof", input_fmt_ctx->streams[input_video_stream_idx], buffersrc_ctx);
		assert (ret >= 0);
		return ret;

	}

	if (pkt.stream_index != input_video_stream_idx) {
//		av_free(&pkt);
		return ret;
	}

	ret = avcodec_decode_video2(input_dec_ctx, mVideoFrame, &got_frame, &pkt);
	if (ret < 0) {
		printf("Error decoding input video\n");
	}

	if (got_frame) {
		int64_t best_effort_timestamp = av_frame_get_best_effort_timestamp(mVideoFrame);
	    	mVideoFrame->pts = best_effort_timestamp;

		if (av_buffersrc_add_frame(buffersrc_ctx, mVideoFrame, AV_BUFFERSRC_FLAG_PUSH) < 0) {
			av_log(NULL, AV_LOG_ERROR, "Error while feeding the video filtergraph\n");
			return -1;
		}
		reap_filters();
	}
	
	return 0;

}

int overlay_transcode_step(AVFrame* mOverlayFrame) {
	int ret = 0;
	AVPacket pkt;

	ret = av_read_frame(overlay_fmt_ctx, &pkt);
	if (ret == AVERROR(EAGAIN)) {
		return 0;
	}

	if (ret < 0) {
		overlay_eof_reached = 1;
		ret = video_output_eof_packet("overlay_eof", overlay_fmt_ctx->streams[input_video_stream_idx], bufferoverlay_ctx);
		assert(ret >=0 );
		return ret;
	}

	if (pkt.stream_index != overlay_video_stream_idx) {
		av_free_packet(&pkt);
		return ret;
	}

	ret = avcodec_decode_video2(overlay_dec_ctx, mOverlayFrame, &got_frame, &pkt);
	if (ret < 0) {
		printf("Error decoding overlay video\n");
	}

	if (got_frame) {
		int64_t best_effort_timestamp = av_frame_get_best_effort_timestamp(mOverlayFrame);

	    	mOverlayFrame->pts = best_effort_timestamp;

		if (av_buffersrc_add_frame(bufferoverlay_ctx, mOverlayFrame, AV_BUFFERSRC_FLAG_PUSH) < 0) {
			av_log(NULL, AV_LOG_ERROR, "Error while feeding the audio filtergraph\n");
			return -1;
		}
	}
	return 0;

}

/**
* output EOF packet to filter to flush
*/
int video_output_eof_packet(const char* tag, 
	AVStream* ist, AVFilterContext* ifilter)
{
    int ret = 0;
    
    // alloc frame if NULL
    AVFrame* decoded_frame = avcodec_alloc_frame();
    
    AVPacket pkt;
    av_init_packet(&pkt);
    pkt.data = NULL;
    pkt.size = 0;
    
    int got_frame = 0;
    ret = avcodec_decode_video2(ist->codec, decoded_frame, &got_frame, &pkt);
    // EOF, assert got nothing and ret is 0.
    // TODO: here we still got frame, different to ffmpeg.
    assert(ret >= 0);
    
    // flush filter
    av_buffersrc_add_ref(ifilter, NULL, 0);
    
    printf("[%s] filter -> eof packet.\n", tag);
    
    return ret;
}


/** 
 * save yuv420p frame [YUV] 
 */  
void yuv420p_save(AVFrame *pFrame)  
{  
    int i = 0;  
  
    int width = pFrame->width, height = pFrame->height;  
    int height_half = height / 2, width_half = width / 2;  
    int y_wrap = pFrame->linesize[0];  
    int u_wrap = pFrame->linesize[1];  
    int v_wrap = pFrame->linesize[2];  
  
    unsigned char *y_buf = pFrame->data[0];  
    unsigned char *u_buf = pFrame->data[1];  
    unsigned char *v_buf = pFrame->data[2];  
  
    //save y  
    for (i = 0; i < height; i++)  
        fwrite(y_buf + i * y_wrap, 1, width, fp_yuv);  

    //save u  
    for (i = 0; i < height_half; i++)  
        fwrite(u_buf + i * u_wrap, 1, width_half, fp_yuv);  

    //save v  
    for (i = 0; i < height_half; i++)  
        fwrite(v_buf + i * v_wrap, 1, width_half, fp_yuv);  
  
    fflush(fp_yuv);  
} 

PS: 用到avfilter的 ,如果有两个输入 ,不管音频还是视频, 代码基本都是这样 (如 视频的overlay   音频的amix 等等)



 

  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Ffmpeg是一个功能强大的视频处理工具,可以通过在视频添加水印来保护视频内容的版权,以下是使用C语言实现视频添加水印的步骤: 1.打开视频文件 ```c AVFormatContext *fmt_ctx = avformat_alloc_context(); if(avformat_open_input(&fmt_ctx, "input.mp4", NULL, NULL) < 0) { printf("无法打开输入文件\n"); exit(1); } ``` 2.查找视频流 ```c if(avformat_find_stream_info(fmt_ctx, NULL) < 0) { printf("无法查找视频流信息\n"); exit(1); } int video_index = -1; for(int i=0; i<fmt_ctx->nb_streams; i++) { if(fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { video_index = i; break; } } if(video_index == -1) { printf("无法找到视频流\n"); exit(1); } AVCodecParameters *codec_par = fmt_ctx->streams[video_index]->codecpar; ``` 3.打开视频解码器 ```c AVCodec *codec = avcodec_find_decoder(codec_par->codec_id); if(codec == NULL) { printf("无法找到解码器\n"); exit(1); } AVCodecContext *codec_ctx = avcodec_alloc_context3(codec); if(avcodec_parameters_to_context(codec_ctx, codec_par) < 0) { printf("无法将解码器参数转换为上下文\n"); exit(1); } if(avcodec_open2(codec_ctx, codec, NULL) < 0) { printf("无法打开解码器\n"); exit(1); } ``` 4.创建输出文件 ```c AVFormatContext *out_fmt_ctx = NULL; if(avformat_alloc_output_context2(&out_fmt_ctx, NULL, NULL, "output.mp4") < 0) { printf("无法创建输出文件\n"); exit(1); } AVStream *out_stream = avformat_new_stream(out_fmt_ctx, codec); if(out_stream == NULL) { printf("无法创建输出流\n"); exit(1); } if(avcodec_parameters_copy(out_stream->codecpar, codec_par) < 0) { printf("无法复制编解码器参数\n"); exit(1); } if(avio_open(&out_fmt_ctx->pb, "output.mp4", AVIO_FLAG_WRITE) < 0) { printf("无法打开输出文件\n"); exit(1); } if(avformat_write_header(out_fmt_ctx, NULL) < 0) { printf("无法写入输出文件头\n"); exit(1); } ``` 5.创建水印 ```c AVFilterContext *buffersrc_ctx; AVFilterContext *buffersink_ctx; AVFilterGraph *filter_graph; const char *filter_descr = "movie=watermark.png[watermark];[in][watermark]overlay=x=100:y=100[out]"; avfilter_register_all(); AVFilter *buffersrc = avfilter_get_by_name("buffer"); AVFilter *buffersink = avfilter_get_by_name("buffersink"); AVFilterInOut *outputs = avfilter_inout_alloc(); AVFilterInOut *inputs = avfilter_inout_alloc(); enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值