libavcodec / swscale的输出损坏,具体取决于分辨率

时间:2014-06-03 19:44:21

标签: c++ video ffmpeg libavcodec

我正在编写一个视频会议软件,我有一个用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)。我认为它与填充有关,因为图像以某种方式被破坏。 You can see my blue t-shirt in lower part of image. 你可以在图像的下半部分看到我的蓝色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);

问题:

  1. 手动转换是否正确?
  2. 我的假设是否正确?
  3. 前两个答案是肯定的,问题出在哪里?特别是......
  4. 为什么它只出现一些决议而不是其他?
  5. 我可以提供哪些其他信息?

0 个答案:

没有答案