diff --git a/app/shaders/vt_renderer.metal b/app/shaders/vt_renderer.metal index d9fa552a..6db53679 100644 --- a/app/shaders/vt_renderer.metal +++ b/app/shaders/vt_renderer.metal @@ -10,6 +10,7 @@ struct CscParams { float3 matrix[3]; float3 offsets; + float2 chromaOffset; float bitnessScaleFactor; }; @@ -25,8 +26,10 @@ fragment float4 ps_draw_biplanar(Vertex v [[ stage_in ]], texture2d luminancePlane [[ texture(0) ]], texture2d chrominancePlane [[ texture(1) ]]) { + float2 chromaOffset = float2(cscParams.chromaOffset.x / chrominancePlane.get_width(), + cscParams.chromaOffset.y / chrominancePlane.get_height()); float3 yuv = float3(luminancePlane.sample(s, v.texCoords).r, - chrominancePlane.sample(s, v.texCoords).rg); + chrominancePlane.sample(s, v.texCoords + chromaOffset).rg); yuv *= cscParams.bitnessScaleFactor; yuv -= cscParams.offsets; @@ -43,9 +46,13 @@ fragment float4 ps_draw_triplanar(Vertex v [[ stage_in ]], texture2d chrominancePlaneU [[ texture(1) ]], texture2d chrominancePlaneV [[ texture(2) ]]) { + float2 chromaOffsetU = float2(cscParams.chromaOffset.x / chrominancePlaneU.get_width(), + cscParams.chromaOffset.y / chrominancePlaneU.get_height()); + float2 chromaOffsetV = float2(cscParams.chromaOffset.x / chrominancePlaneV.get_width(), + cscParams.chromaOffset.y / chrominancePlaneV.get_height()); float3 yuv = float3(luminancePlane.sample(s, v.texCoords).r, - chrominancePlaneU.sample(s, v.texCoords).r, - chrominancePlaneV.sample(s, v.texCoords).r); + chrominancePlaneU.sample(s, v.texCoords + chromaOffsetU).r, + chrominancePlaneV.sample(s, v.texCoords + chromaOffsetV).r); yuv *= cscParams.bitnessScaleFactor; yuv -= cscParams.offsets; diff --git a/app/streaming/video/ffmpeg-renderers/vt_metal.mm b/app/streaming/video/ffmpeg-renderers/vt_metal.mm index a5ce75cc..a2691166 100644 --- a/app/streaming/video/ffmpeg-renderers/vt_metal.mm +++ b/app/streaming/video/ffmpeg-renderers/vt_metal.mm @@ -31,6 +31,7 @@ struct CscParams struct ParamBuffer { CscParams cscParams; + vector_float2 chromaOffset; float bitnessScaleFactor; }; @@ -313,6 +314,40 @@ public: paramBuffer.cscParams.matrix[i][2] *= uvScale; } + switch (frame->chroma_location) { + default: + case AVCHROMA_LOC_LEFT: + paramBuffer.chromaOffset[0] = 0; + paramBuffer.chromaOffset[1] = 0.5; + break; + case AVCHROMA_LOC_CENTER: + paramBuffer.chromaOffset[0] = 0.5; + paramBuffer.chromaOffset[1] = 0.5; + break; + case AVCHROMA_LOC_TOPLEFT: + paramBuffer.chromaOffset[0] = 0; + paramBuffer.chromaOffset[1] = 0; + break; + case AVCHROMA_LOC_TOP: + paramBuffer.chromaOffset[0] = 0.5; + paramBuffer.chromaOffset[1] = 0; + break; + case AVCHROMA_LOC_BOTTOMLEFT: + paramBuffer.chromaOffset[0] = 0; + paramBuffer.chromaOffset[1] = 1.0; + break; + case AVCHROMA_LOC_BOTTOM: + paramBuffer.chromaOffset[0] = 0.5; + paramBuffer.chromaOffset[1] = 1.0; + break; + } + + if (m_VideoFormat & VIDEO_FORMAT_MASK_YUV444) { + // 4:4:4 has no subsampling + paramBuffer.chromaOffset[0] = 0; + paramBuffer.chromaOffset[1] = 0; + } + // Set the EDR metadata for HDR10 to enable OS tonemapping if (frame->color_trc == AVCOL_TRC_SMPTE2084 && m_MasteringDisplayColorVolume != nullptr) { m_MetalLayer.EDRMetadata = [CAEDRMetadata HDR10MetadataWithDisplayInfo:(__bridge NSData*)m_MasteringDisplayColorVolume @@ -676,6 +711,7 @@ public: m_Window = params->window; m_FrameRateRange = CAFrameRateRangeMake(params->frameRate, params->frameRate, params->frameRate); + m_VideoFormat = params->videoFormat; id device = getMetalDevice(); if (!device) { @@ -972,6 +1008,7 @@ private: id m_CommandQueue; id m_SwMappingTextures[MAX_VIDEO_PLANES]; SDL_MetalView m_MetalView; + int m_VideoFormat; int m_LastColorSpace; bool m_LastFullRange; int m_LastFrameWidth;