diff --git a/src/surface.c b/src/surface.c index 432a46a..58ca9cb 100644 --- a/src/surface.c +++ b/src/surface.c @@ -672,18 +672,64 @@ VAStatus RequestExportSurfaceHandle(VADriverContextP context, surface_object->destination_sizes[i]; } - surface_descriptor->num_layers = 1; + /* + * Layer construction depends on the consumer's request flags + * (VA_EXPORT_SURFACE_*_LAYERS): + * + * COMPOSED_LAYERS (default, mpv): one layer carrying both + * Y and UV planes (drm_format=NV12, num_planes=2). Mesa + * imports as a single NV12 EGLImage. + * + * SEPARATE_LAYERS (Firefox 150 RDD): two layers, Y as a + * single-plane R8 layer, UV as a single-plane GR88 layer. + * Firefox's GetVAAPISurfaceDescriptor passes + * VA_EXPORT_SURFACE_SEPARATE_LAYERS so its DMABufSurfaceYUV + * import code can address Y and UV planes independently. + * Without this branch, Firefox parsed our COMPOSED layout + * as if it were SEPARATE, found bogus layer-1 data, and + * silently fell back to FFmpeg(FFVPX) software decode. + * + * The earlier path 0001 mplane port assumed a single COMPOSED + * shape — fine for mpv but breaks any consumer requesting + * SEPARATE. Honor the flag. + */ + if ((flags & VA_EXPORT_SURFACE_SEPARATE_LAYERS) && planes_count == 2) { + surface_descriptor->num_layers = 2; - surface_descriptor->layers[0].drm_format = video_format->drm_format; - surface_descriptor->layers[0].num_planes = planes_count; + /* Layer 0: Y plane as DRM_FORMAT_R8 (1 byte/pixel luma). */ + surface_descriptor->layers[0].drm_format = DRM_FORMAT_R8; + surface_descriptor->layers[0].num_planes = 1; + surface_descriptor->layers[0].object_index[0] = + export_fds_count == 1 ? 0 : 0; + surface_descriptor->layers[0].offset[0] = + surface_object->destination_offsets[0]; + surface_descriptor->layers[0].pitch[0] = + surface_object->destination_bytesperlines[0]; - for (i = 0; i < planes_count; i++) { - surface_descriptor->layers[0].object_index[i] = - export_fds_count == 1 ? 0 : i; - surface_descriptor->layers[0].offset[i] = - surface_object->destination_offsets[i]; - surface_descriptor->layers[0].pitch[i] = - surface_object->destination_bytesperlines[i]; + /* Layer 1: UV plane as DRM_FORMAT_GR88 (interleaved + * U+V, 2 bytes/pixel chroma at half resolution). */ + surface_descriptor->layers[1].drm_format = DRM_FORMAT_GR88; + surface_descriptor->layers[1].num_planes = 1; + surface_descriptor->layers[1].object_index[0] = + export_fds_count == 1 ? 0 : 1; + surface_descriptor->layers[1].offset[0] = + surface_object->destination_offsets[1]; + surface_descriptor->layers[1].pitch[0] = + surface_object->destination_bytesperlines[1]; + } else { + /* COMPOSED_LAYERS / default: one layer with all planes. */ + surface_descriptor->num_layers = 1; + surface_descriptor->layers[0].drm_format = video_format->drm_format; + surface_descriptor->layers[0].num_planes = planes_count; + + for (i = 0; i < planes_count; i++) { + surface_descriptor->layers[0].object_index[i] = + export_fds_count == 1 ? 0 : i; + surface_descriptor->layers[0].offset[i] = + surface_object->destination_offsets[i]; + surface_descriptor->layers[0].pitch[i] = + surface_object->destination_bytesperlines[i]; + } } /*