我正在编写一个视频会议软件,我有一个用libavcoded解码成IYUV的H.264流,然后在无窗口模式下渲染到一个带有VMR9的窗口。我使用DirectShow图表来执行此操作。
为了避免不必要的转换为RGB和返回(参见link),我将IYUV视频转换为YUY2,然后将其传递给VMR9,并使用libswscale。
我注意到,视频分辨率为848x480,输出视频被破坏,所以我进一步调查并发现,对于某些分辨率,视频总是被破坏。为了排除libswscale的详细说明,我添加了对IYUV +填充到IYUV转换的支持,并且它适用于所有分辨率。
尽管如此,我仍然愿意避免慢速IYUV,因此我实施了对NV12(使用libswscale)和YV12(手动,基本上与IYUV相同)的支持。在两台不同的计算机上进行了一些测试后,我得出了奇怪的结果。
resolution YUY2 NV12 IYUV YV12
PC 1 (my laptop)
640x360 ok broken ok broken
848x480 broken broken ok broken
960x540 broken broken ok broken
1024x576 ok ok ok ok
1280x720 ok ok ok broken
1920x1080 ok broken ok broken
PC 2
640x360 ok ok ok ok
848x480 ok broken ok broken
960x540 ok ok ok ok
1024x576 ok ok ok ok
1280x720 ok broken ok ok
1920x1080 ok ok ok ok
为排除VMR9故障,我用EVR替换它,但结果相同。
我知道内存对齐需要填充,并且填充的大小取决于所使用的CPU(libavcodec doc),这可以解释两台计算机之间的差异(首先是Intel i7-3820QM,第二台Intel Core 2 Quad Q6600)。我认为它与填充有关,因为图像以某种方式被破坏。
你可以在图像的下半部分看到我的蓝色T恤,在上面看到我的脸。
要遵循的是转换代码。使用libswscale执行NV12和YUY2转换,而手动执行IYUV和YV12。
int pixels = _outputFrame->width * _outputFrame->height;
if (_outputFormat == "YUY2") {
int stride = _outputFrame->width * 2;
sws_scale(_convertCtx, _outputFrame->data, _outputFrame->linesize, 0, _outputFrame->height, &out, &stride);
}
else if (_outputFormat == "NV12") {
int stride[] = { _outputFrame->width, _outputFrame->width };
uint8_t * dst[] = { out, out + pixels };
sws_scale(_convertCtx, _outputFrame->data, _outputFrame->linesize, 0, _outputFrame->height, dst, stride);
}
else if (_outputFormat == "IYUV") { // clean ffmpeg padding
for (int i = 0; i < _outputFrame->height; i++) // copy Y
memcpy(out + i * _outputFrame->width, _outputFrame->data[0] + i * _outputFrame->linesize[0] , _outputFrame->width);
for (int i = 0; i < _outputFrame->height / 2; i++) // copy U
memcpy(out + pixels + i * _outputFrame->width / 2, _outputFrame->data[1] + i * _outputFrame->linesize[1] , _outputFrame->width / 2);
for (int i = 0; i < _outputFrame->height / 2; i++) // copy V
memcpy(out + pixels + pixels/4 + i * _outputFrame->width / 2, _outputFrame->data[2] + i * _outputFrame->linesize[2] , _outputFrame->width / 2);
}
else if (_outputFormat == "YV12") { // like IYUV, but U is inverted with V plane
for (int i = 0; i < _outputFrame->height; i++) // copy Y
memcpy(out + i * _outputFrame->width, _outputFrame->data[0] + i * _outputFrame->linesize[0], _outputFrame->width);
for (int i = 0; i < _outputFrame->height / 2; i++) // copy V
memcpy(out + pixels + i * _outputFrame->width / 2, _outputFrame->data[2] + i * _outputFrame->linesize[2], _outputFrame->width / 2);
for (int i = 0; i < _outputFrame->height / 2; i++) // copy U
memcpy(out + pixels + pixels / 4 + i * _outputFrame->width / 2, _outputFrame->data[1] + i * _outputFrame->linesize[1], _outputFrame->width / 2);
}
out
是输出缓冲区。 _outputFrame
是libavcodec输出AVFrame。 _convertCtx
初始化如下。
if (_outputFormat == "YUY2")
_convertCtx = sws_getContext(_width, _height, AV_PIX_FMT_YUV420P,
_width, _height, AV_PIX_FMT_YUYV422, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
else if (_outputFormat == "NV12")
_convertCtx = sws_getContext(_width, _height, AV_PIX_FMT_YUV420P,
_width, _height, AV_PIX_FMT_NV12, SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
问题: