Add functions for color matrix generation and chroma co-siting in the renderer interface

This commit is contained in:
Cameron Gutman
2025-11-05 21:59:13 -06:00
parent af37002a60
commit 229f5e4cea

View File

@@ -2,11 +2,14 @@
#include "SDL_compat.h"
#include <array>
#include "streaming/video/decoder.h"
#include "streaming/video/overlaymanager.h"
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/pixdesc.h>
#ifdef HAVE_DRM
#include <libavutil/hwcontext_drm.h>
@@ -325,6 +328,156 @@ public:
}
}
AVPixelFormat getFrameSwPixelFormat(const AVFrame* frame) {
// For hwaccel formats, we want to get the real underlying format
if (frame->hw_frames_ctx) {
return ((AVHWFramesContext*)frame->hw_frames_ctx->data)->sw_format;
}
else {
return (AVPixelFormat)frame->format;
}
}
int getFrameBitsPerChannel(const AVFrame* frame) {
const AVPixFmtDescriptor* formatDesc = av_pix_fmt_desc_get(getFrameSwPixelFormat(frame));
if (!formatDesc) {
// This shouldn't be possible but handle it anyway
SDL_assert(formatDesc);
return 8;
}
// This assumes plane 0 is exclusively the Y component
return formatDesc->comp[0].depth;
}
void getFramePremultipliedCscConstants(const AVFrame* frame, std::array<float, 9> &cscMatrix, std::array<float, 3> &offsets) {
static const std::array<float, 9> k_CscMatrix_Bt601 = {
1.0f, 1.0f, 1.0f,
0.0f, -0.3441f, 1.7720f,
1.4020f, -0.7141f, 0.0f,
};
static const std::array<float, 9> k_CscMatrix_Bt709 = {
1.0f, 1.0f, 1.0f,
0.0f, -0.1873f, 1.8556f,
1.5748f, -0.4681f, 0.0f,
};
static const std::array<float, 9> k_CscMatrix_Bt2020 = {
1.0f, 1.0f, 1.0f,
0.0f, -0.1646f, 1.8814f,
1.4746f, -0.5714f, 0.0f,
};
bool fullRange = isFrameFullRange(frame);
int bitsPerChannel = getFrameBitsPerChannel(frame);
int channelRange = (1 << bitsPerChannel);
double yMin = (fullRange ? 0 : (16 << (bitsPerChannel - 8)));
double yMax = (fullRange ? (channelRange - 1) : (235 << (bitsPerChannel - 8)));
double yScale = (channelRange - 1) / (yMax - yMin);
double uvMin = (fullRange ? 0 : (16 << (bitsPerChannel - 8)));
double uvMax = (fullRange ? (channelRange - 1) : (240 << (bitsPerChannel - 8)));
double uvScale = (channelRange - 1) / (uvMax - uvMin);
// Calculate YUV offsets
offsets[0] = yMin / (double)(channelRange - 1);
offsets[1] = (channelRange / 2) / (double)(channelRange - 1);
offsets[2] = (channelRange / 2) / (double)(channelRange - 1);
// Start with the standard full range color matrix
switch (getFrameColorspace(frame)) {
default:
case COLORSPACE_REC_601:
cscMatrix = k_CscMatrix_Bt601;
break;
case COLORSPACE_REC_709:
cscMatrix = k_CscMatrix_Bt709;
break;
case COLORSPACE_REC_2020:
cscMatrix = k_CscMatrix_Bt2020;
break;
}
// Scale the color matrix according to the color range
for (int i = 0; i < 3; i++) {
cscMatrix[i] *= yScale;
}
for (int i = 3; i < 9; i++) {
cscMatrix[i] *= uvScale;
}
}
void getFrameChromaCositingOffsets(const AVFrame* frame, std::array<float, 2> &chromaOffsets) {
const AVPixFmtDescriptor* formatDesc = av_pix_fmt_desc_get(getFrameSwPixelFormat(frame));
if (!formatDesc) {
SDL_assert(formatDesc);
chromaOffsets.fill(0);
return;
}
SDL_assert(formatDesc->log2_chroma_w <= 1);
SDL_assert(formatDesc->log2_chroma_h <= 1);
switch (frame->chroma_location) {
default:
case AVCHROMA_LOC_LEFT:
chromaOffsets[0] = 0.5;
chromaOffsets[1] = 0;
break;
case AVCHROMA_LOC_CENTER:
chromaOffsets[0] = 0;
chromaOffsets[1] = 0;
break;
case AVCHROMA_LOC_TOPLEFT:
chromaOffsets[0] = 0.5;
chromaOffsets[1] = 0.5;
break;
case AVCHROMA_LOC_TOP:
chromaOffsets[0] = 0;
chromaOffsets[1] = 0.5;
break;
case AVCHROMA_LOC_BOTTOMLEFT:
chromaOffsets[0] = 0.5;
chromaOffsets[1] = -0.5;
break;
case AVCHROMA_LOC_BOTTOM:
chromaOffsets[0] = 0;
chromaOffsets[1] = -0.5;
break;
}
// Force the offsets to 0 if chroma is not subsampled in that dimension
if (formatDesc->log2_chroma_w == 0) {
chromaOffsets[0] = 0;
}
if (formatDesc->log2_chroma_h == 0) {
chromaOffsets[1] = 0;
}
}
// Returns if the frame format has changed since the last call to this function
bool hasFrameFormatChanged(const AVFrame* frame) {
AVPixelFormat format = getFrameSwPixelFormat(frame);
if (frame->width == m_LastFrameWidth &&
frame->height == m_LastFrameHeight &&
format == m_LastFramePixelFormat &&
frame->color_range == m_LastColorRange &&
frame->color_primaries == m_LastColorPrimaries &&
frame->color_trc == m_LastColorTrc &&
frame->colorspace == m_LastColorSpace &&
frame->chroma_location == m_LastChromaLocation) {
return false;
}
m_LastFrameWidth = frame->width;
m_LastFrameHeight = frame->height;
m_LastFramePixelFormat = format;
m_LastColorRange = frame->color_range;
m_LastColorPrimaries = frame->color_primaries;
m_LastColorTrc = frame->color_trc;
m_LastColorSpace = frame->colorspace;
m_LastChromaLocation = frame->chroma_location;
return true;
}
// IOverlayRenderer
virtual void notifyOverlayUpdated(Overlay::OverlayType) override {
// Nothing
@@ -373,4 +526,14 @@ protected:
private:
RendererType m_Type;
// Properties watched by hasFrameFormatChanged()
int m_LastFrameWidth = 0;
int m_LastFrameHeight = 0;
AVPixelFormat m_LastFramePixelFormat = AV_PIX_FMT_NONE;
AVColorRange m_LastColorRange = AVCOL_RANGE_UNSPECIFIED;
AVColorPrimaries m_LastColorPrimaries = AVCOL_PRI_UNSPECIFIED;
AVColorTransferCharacteristic m_LastColorTrc = AVCOL_TRC_UNSPECIFIED;
AVColorSpace m_LastColorSpace = AVCOL_SPC_UNSPECIFIED;
AVChromaLocation m_LastChromaLocation = AVCHROMA_LOC_UNSPECIFIED;
};