FFMpeg、libav、sws_scale 和 yuv 到 rgb 的转换问题 - 颜色不正确

FFMpeg, libav, Problem with sws_scale and yuv to rgb conversion - colors are not correct

我一直在尝试在 Delphi 中创建自己的视频播放器,而 ffmpeg 效果很好,直到我遇到颜色空间或颜色转换的障碍。

已经尝试了几天的所有方法,但无法弄清楚。 我正在使用 AV_PIX_FMT_RGB48LE 作为输出...

旧上下文创建:

      videoConvContext := sws_getContext(AVStreamInit.codec.width, AVStreamInit.codec.height, AVStreamInit.codec.pix_fmt, AVStreamInit.codec.width, AVStreamInit.codec.height, AV_PIX_FMT_RGB48LE, SWS_POINT, nil, nil, nil);

新上下文创建:

    dstRange := 1;
    srcRange := 1;
    if AVStreamInit.codec.color_range = AVCOL_RANGE_MPEG then
      srcRange := 0;

    videoConvContext := sws_alloc_context();

    av_opt_set_int(videoConvContext, 'sws_flags', SWS_POINT or SWS_PRINT_INFO, 0);

    av_opt_set_int(videoConvContext, 'srcw', AVStreamInit.codec.width, 0);
    av_opt_set_int(videoConvContext, 'srch', AVStreamInit.codec.height, 0);
    av_opt_set_int(videoConvContext, 'src_format', Integer(AVStreamInit.codec.pix_fmt), 0);

    av_opt_set_int(videoConvContext, 'dstw', AVStreamInit.codec.width, 0);
    av_opt_set_int(videoConvContext, 'dsth', AVStreamInit.codec.height, 0);
    av_opt_set_int(videoConvContext, 'dst_format', Integer(AV_PIX_FMT_RGB48LE), 0);

    i1 := sws_getCoefficients2(Integer(AVStreamInit.codec.colorspace));
    i2 := sws_getCoefficients2(SWS_CS_ITU709);

    ret := sws_setColorspaceDetails2(videoConvContext, i1, srcRange, i2, dstRange, 0, 1 shl 16, 1 shl 16);

    sws_init_context(videoConvContext, nil, nil);

结果完全一样....

sws_scale 的输出没有正确的颜色,这里有 2 个屏幕截图

好的 - 来自 mpc-hc

不正常 - 来自我的播放器 sws_scale(直接原始提取,因此没有进行颜色处理)

这个视频有pix_fmtAV_PIX_FMT_YUV420P10LE,但它发生在其他YUV输入上,但不是很明显。还有其他 RGB 输出(8 位等)... 这里的 Netflix 徽标是纯红色,但 sws_scale 输出呈现橙色...

Sws_scale代码仅供参考:

sws_scale(videoConvContext, @AVPacketBuffer.AVFrameCopy.Data, @AVPacketBuffer.AVFrameCopy.linesize, 0, AVStream.codec.height, @BitmapXBuffer.data, @linesize);

BitmapXBuffer.data 是正确大小的信号缓冲区...

AVFrameCopy - 是原始帧的副本:

  if Assigned(AVPacketBuffer.AVFrameCopy) then
      av_frame_free(@AVPacketBuffer.AVFrameCopy);

    AVPacketBuffer.AVFrameCopy := av_frame_alloc();
    AVPacketBuffer.AVFrameCopy.format := AVPacketBuffer.AVFrame.format;
    AVPacketBuffer.AVFrameCopy.width := AVPacketBuffer.AVFrame.width;
    AVPacketBuffer.AVFrameCopy.height := AVPacketBuffer.AVFrame.height;
    AVPacketBuffer.AVFrameCopy.channels := AVPacketBuffer.AVFrame.channels;
    AVPacketBuffer.AVFrameCopy.channel_layout := AVPacketBuffer.AVFrame.channel_layout;
    AVPacketBuffer.AVFrameCopy.nb_samples := AVPacketBuffer.AVFrame.nb_samples;
    av_frame_get_buffer(AVPacketBuffer.AVFrameCopy, 32);
    av_frame_copy(AVPacketBuffer.AVFrameCopy, AVPacketBuffer.AVFrame);
    av_frame_copy_props(AVPacketBuffer.AVFrameCopy, AVPacketBuffer.AVFrame);

谢谢!

回答我自己的问题 - 上面的代码是正确的, 令我困惑的是 dst_table,但只有当目标颜色 space 是 YUV 时才有意义。

Netflix 徽标也偏橙色,但不是那种正确的橙色。从 sRGB 转换为线性 RGB 为我解决了这个问题,但这只是我的情况。

所以,以上面的代码为例:D