要想将摄像头采集的视频流和桌面采集的视频流混成一股流,需要再次借助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