SHA256
Fix Output Color Tags And CPU Conversion Matrix
This commit is contained in:
1 file changed
+69
-3
@@ -201,6 +201,12 @@ namespace ADOFAIRenderer.Util.FFmpeg
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private AVPixelFormat _dstPixFmt = AVPixelFormat.AV_PIX_FMT_YUV420P;
|
private AVPixelFormat _dstPixFmt = AVPixelFormat.AV_PIX_FMT_YUV420P;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Color range of the encoder input and output (codec context color_range of the selected encoder).
|
||||||
|
/// Full range (JPEG) for every encoder except libvvenc, limited range (MPEG) for libvvenc.
|
||||||
|
/// </summary>
|
||||||
|
private AVColorRange _colorRange = AVColorRange.AVCOL_RANGE_JPEG;
|
||||||
|
|
||||||
/// <summary>Pixel format of the finally selected and running encoder.</summary>
|
/// <summary>Pixel format of the finally selected and running encoder.</summary>
|
||||||
public AVPixelFormat SelectedPixelFormat => _dstPixFmt;
|
public AVPixelFormat SelectedPixelFormat => _dstPixFmt;
|
||||||
|
|
||||||
@@ -399,7 +405,19 @@ namespace ADOFAIRenderer.Util.FFmpeg
|
|||||||
// ── Basic video parameters configuration ──
|
// ── Basic video parameters configuration ──
|
||||||
_pCodecContext->width = _settings.Width;
|
_pCodecContext->width = _settings.Width;
|
||||||
_pCodecContext->height = _settings.Height;
|
_pCodecContext->height = _settings.Height;
|
||||||
_pCodecContext->color_range = AVColorRange.AVCOL_RANGE_JPEG;
|
|
||||||
|
// ── Output color tags ──
|
||||||
|
// Matrix: BT.709, the RGB→YUV matrix of both conversion paths
|
||||||
|
// (GPU: RGB2YUV_BT709_Full in ColorConversion.compute, CPU: ConfigureSwsColorspace).
|
||||||
|
// Players use this tag to convert the YUV frames back to RGB.
|
||||||
|
// Range: full (0-255) for every encoder except libvvenc, limited (16-235) for libvvenc.
|
||||||
|
// The FFmpeg libvvenc wrapper passes the matrix/primaries/transfer tags to VVenC but not the range,
|
||||||
|
// so a VVC stream is read as limited range; its input is converted to limited range to match.
|
||||||
|
// Reference: https://github.com/FFmpeg/FFmpeg/blob/release/8.0/libavcodec/libvvenc.c (vvenc_set_color_format)
|
||||||
|
_pCodecContext->color_range = encoder == VideoEncoder.LibVvenc
|
||||||
|
? AVColorRange.AVCOL_RANGE_MPEG
|
||||||
|
: AVColorRange.AVCOL_RANGE_JPEG;
|
||||||
|
_pCodecContext->colorspace = AVColorSpace.AVCOL_SPC_BT709;
|
||||||
// thread_count = 0: FFmpeg automatically allocates threads according to the system CPU core count.
|
// thread_count = 0: FFmpeg automatically allocates threads according to the system CPU core count.
|
||||||
// For libvvenc, the FFmpeg wrapper passes thread_count as the VVenC internal thread count,
|
// For libvvenc, the FFmpeg wrapper passes thread_count as the VVenC internal thread count,
|
||||||
// so setting 0 (auto) is required for parallel encoding utilizing all cores.
|
// so setting 0 (auto) is required for parallel encoding utilizing all cores.
|
||||||
@@ -756,6 +774,7 @@ namespace ADOFAIRenderer.Util.FFmpeg
|
|||||||
var accelStatus = isHardware ? "Hardware Acceleration Enabled" : "Software CPU Usage";
|
var accelStatus = isHardware ? "Hardware Acceleration Enabled" : "Software CPU Usage";
|
||||||
Melon<RenderMod>.Logger.Msg($"Video encoder successfully initialized: {encoder.Name} ({accelStatus})");
|
Melon<RenderMod>.Logger.Msg($"Video encoder successfully initialized: {encoder.Name} ({accelStatus})");
|
||||||
_dstPixFmt = selectedPixFmt;
|
_dstPixFmt = selectedPixFmt;
|
||||||
|
_colorRange = _pCodecContext->color_range;
|
||||||
openedSuccessfully = true;
|
openedSuccessfully = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -855,6 +874,8 @@ namespace ADOFAIRenderer.Util.FFmpeg
|
|||||||
if (_pSwsContext == null)
|
if (_pSwsContext == null)
|
||||||
throw new Exception("Failed to initialize software scaler.");
|
throw new Exception("Failed to initialize software scaler.");
|
||||||
|
|
||||||
|
ConfigureSwsColorspace();
|
||||||
|
|
||||||
_isInitialized = true;
|
_isInitialized = true;
|
||||||
_loggedConversionMode = false;
|
_loggedConversionMode = false;
|
||||||
}
|
}
|
||||||
@@ -1240,7 +1261,51 @@ namespace ADOFAIRenderer.Util.FFmpeg
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the format, size and color range of the destination frame (_pDstFrame) and allocates a new pixel buffer for it.
|
/// Sets the RGB→YUV conversion of the CPU conversion path (_pSwsContext) to the BT.709 matrix
|
||||||
|
/// and the color range of the encoder (_colorRange), so that it produces the same colors as the GPU path
|
||||||
|
/// (RGB2YUV_BT709_Full in ColorConversion.compute) and as the tags of the output stream.
|
||||||
|
/// A new libswscale context uses the BT.601 matrix (SWS_CS_DEFAULT = SWS_CS_ITU601) and the limited range.
|
||||||
|
///
|
||||||
|
/// For RGB input, "table" holds the coefficients of the output YUV space and dstRange its range (1 = full, 0 = limited).
|
||||||
|
/// The source (RGB) values, brightness, contrast and saturation keep the values of the context.
|
||||||
|
///
|
||||||
|
/// Reference: https://ffmpeg.org/doxygen/trunk/group__libsws.html (sws_setColorspaceDetails, sws_getCoefficients)
|
||||||
|
/// Reference: https://github.com/FFmpeg/FFmpeg/blob/release/8.0/libswscale/swscale.h (SWS_CS_ITU709, SWS_CS_DEFAULT)
|
||||||
|
/// </summary>
|
||||||
|
private void ConfigureSwsColorspace()
|
||||||
|
{
|
||||||
|
int* currentInvTable;
|
||||||
|
int* currentTable;
|
||||||
|
int srcRange, dstRange, brightness, contrast, saturation;
|
||||||
|
|
||||||
|
var ret = ffmpeg.sws_getColorspaceDetails(_pSwsContext, ¤tInvTable, &srcRange, ¤tTable, &dstRange,
|
||||||
|
&brightness, &contrast, &saturation);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
Melon<RenderMod>.Logger.Warning($"[Encoder] Cannot read the colorspace details of the software scaler: {GetFFmpegErrorString(ret)} (Code: {ret}). The CPU conversion path keeps the libswscale defaults.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var coefficients = ffmpeg.sws_getCoefficients(ffmpeg.SWS_CS_ITU709);
|
||||||
|
var invTable = new int_array4();
|
||||||
|
var table = new int_array4();
|
||||||
|
for (uint i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
invTable[i] = currentInvTable[i];
|
||||||
|
table[i] = coefficients[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
var fullRange = _colorRange == AVColorRange.AVCOL_RANGE_JPEG;
|
||||||
|
ret = ffmpeg.sws_setColorspaceDetails(_pSwsContext, invTable, srcRange, table, fullRange ? 1 : 0,
|
||||||
|
brightness, contrast, saturation);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
Melon<RenderMod>.Logger.Warning($"[Encoder] Cannot set the colorspace details of the software scaler: {GetFFmpegErrorString(ret)} (Code: {ret}). The CPU conversion path keeps the libswscale defaults.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the format, size, color range and matrix of the destination frame (_pDstFrame) and allocates a new pixel buffer for it.
|
||||||
/// av_frame_get_buffer allocates memory matching format, width and height and sets the data[]/linesize[] pointers.
|
/// av_frame_get_buffer allocates memory matching format, width and height and sets the data[]/linesize[] pointers.
|
||||||
/// Second argument 0 means default alignment.
|
/// Second argument 0 means default alignment.
|
||||||
/// Reference: https://ffmpeg.org/doxygen/trunk/group__lavu__frame.html (av_frame_get_buffer)
|
/// Reference: https://ffmpeg.org/doxygen/trunk/group__lavu__frame.html (av_frame_get_buffer)
|
||||||
@@ -1250,7 +1315,8 @@ namespace ADOFAIRenderer.Util.FFmpeg
|
|||||||
_pDstFrame->format = (int)_dstPixFmt;
|
_pDstFrame->format = (int)_dstPixFmt;
|
||||||
_pDstFrame->width = _settings.Width;
|
_pDstFrame->width = _settings.Width;
|
||||||
_pDstFrame->height = _settings.Height;
|
_pDstFrame->height = _settings.Height;
|
||||||
_pDstFrame->color_range = AVColorRange.AVCOL_RANGE_JPEG;
|
_pDstFrame->color_range = _colorRange;
|
||||||
|
_pDstFrame->colorspace = AVColorSpace.AVCOL_SPC_BT709;
|
||||||
|
|
||||||
var ret = ffmpeg.av_frame_get_buffer(_pDstFrame, 0);
|
var ret = ffmpeg.av_frame_get_buffer(_pDstFrame, 0);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
|
|||||||
Reference in new issue
Block a user