【音视频】视频混流-avfilter(2-2)

该博客介绍了如何使用ffmpeg的avfilter库将摄像头和桌面采集的视频流混合成一个视频流,通过设置filterDesc实现画中画效果。主要流程包括初始化视频混流器、启动视频filter线程、视频流输入及停止混流。文章还强调了YUV420P数据处理的重要性以及在FfmpegMuxer类中的数据回调和参数修改。
摘要由CSDN通过智能技术生成

要想将摄像头采集的视频流和桌面采集的视频流混成一股流,需要再次借助ffmpeg的avfilter功能库。

技术简介

借助fmpeg的filter功能,将两股视频合成一股视频流,从而实现两股视频的画中画效果

使用模块(库)

使用ffmpeg的avfilter库

主要流程和代码

1、初始化视频混流器。最需要关注的是filterDesc,即"[in0]setpts=PTS-STARTPTS,scale=%dx%d[main];[in1]setpts=PTS-STARTPTS,scale=%dx%d[over];[main][over]overlay=x=W-w:y=H-h:format=0[out]",因为这个最关键,这句话的意思是将[in0]设置从0开始的时间戳、scale视频大小并输出为标签[main],同理,[in1]输出为标签[over],然后将[over]在[main]上面使用overlay设置左上角位置、输出format为yuv420p并输出标签[out]

int VideoMixer::init(const VIDEO_FILTER_CTX& outCtx, const VIDEO_FILTER_CTX* inCtx0, const VIDEO_FILTER_CTX* inCtx1)
{
   
	int err = ERROR_CODE_OK;

	if (m_inited) {
   
		return err;
	}

	if (inCtx0 == nullptr || inCtx1 == nullptr) {
   
		err = ERROR_CODE_PARAMS_ERROR;
		return err;
	}

	do {
   
		m_filterGraph = avfilter_graph_alloc();
		if (m_filterGraph == nullptr) {
   
			err = ERROR_CODE_FILTER_ALLOC_GRAPH_FAILED;
			break;
		}

		m_filterInCtxs = new VIDEO_FILTER_CTX[VIDEO_MIXER_FILTER_IN_CTX_MAX_SIZE];
		memcpy_s(&m_filterInCtxs[0], sizeof(VIDEO_FILTER_CTX), inCtx0, sizeof(VIDEO_FILTER_CTX));
		memcpy_s(&m_filterInCtxs[1], sizeof(VIDEO_FILTER_CTX), inCtx1, sizeof(VIDEO_FILTER_CTX));
		m_filterOutCtx = outCtx;

		for (int i = 0; i < VIDEO_MIXER_FILTER_IN_CTX_MAX_SIZE; i++) {
   
			m_filterInCtxs[i].filterInout = avfilter_inout_alloc();
		}
		m_filterOutCtx.filterInout = avfilter_inout_alloc();

		char filterInArgs[2][512] = {
    0 };
		for (int i = 0; i < VIDEO_MIXER_FILTER_IN_CTX_MAX_SIZE; i++) {
   
			sprintf_s(filterInArgs[i], sizeof(filterInArgs[i]), "video_size=%dx%d:pix_fmt=%d:frame_rate=%d:time_base=%d/%d:pixel_aspect=%d/%d",
				m_filterInCtxs[i].width, m_filterInCtxs[i].height, m_filterInCtxs[i].pixelFmt, m_filterInCtxs[i].framerate,
				m_filterInCtxs[i].timebase.num, m_filterInCtxs[i].timebase.den, m_filterInCtxs[i].pixelAspect.num, m_filterInCtxs[i].pixelAspect.den);
		}

		int ret = 0;
		for (int i = 0; i < VIDEO_MIXER_FILTER_IN_CTX_MAX_SIZE; i++) {
   
			char filterName[4] = {
    0 };
			sprintf_s(filterName, sizeof(filterName), "in%d", i);
			ret = avfilter_graph_create_filter(&m_filterInCtxs[i].filterCtx, avfilter_get_by_name("buffer"), filterName, filterInArgs[i], nullptr, m_filterGraph);
			if (ret < 0) {
   
				err = ERROR_CODE_FILTER_CREATE_FILTER_FAILED;
				break;
			}
		}
		if (err != ERROR_CODE_OK) {
   
			break;
		}

		ret = avfilter_graph_create_filter(&m_filterOutCtx.filterCtx, avfilter_get_by_name("buffersink"), "out", nullptr, nullptr, m_filterGraph);
		if (ret < 0) {
   
			err = ERROR_CODE_FILTER_CREATE_FILTER_FAILED;
			break;
		}

		av_opt_set_bin(m_filterOutCtx.filterCtx, "pix_fmts", (uint8_t*)&m_filterOutCtx.pixelFmt, sizeof(m_filterOutCtx.pixelFmt), AV_OPT_SEARCH_CHILDREN);

		for (int i = 0; i < VIDEO_MIXER_FILTER_IN_CTX_MAX_SIZE; i++) {
   
			char filterName[4] = {
    0 };
			sprintf_s(filterName, sizeof(filterName), "in%d", i);
			m_filterInCtxs[i].filterInout->name = av_strdup(filterName);
			m_filterInCtxs[i].filterInout->filter_ctx = m_filterInCtxs[i].filterCtx;
			m_filterInCtxs[i].filterInout->pad_idx = 0;
			if (i < VIDEO_MIXER_FILTER_IN_CTX_MAX_SIZE - 1) {
   
				m_filterInCtxs[i].filterInout->next = m_filterInCtxs[i + 1].filterInout;
			}
			else {
   
				m_filterInCtxs
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值