从似乎在 Windows 10 上有不正确数据的 IMFSample 读取 YUY2 数据

Reading YUY2 data from an IMFSample that appears to have improper data on Windows 10

我正在开发一个使用 IMFSourceReader 从视频文件中读取数据的应用程序。我正在使用 DXVA 来提高性能。我在处理一个特定的全高清 H.264 编码 AVI 文件时遇到问题。根据我目前的调查,我认为 IMFSample 包含不正确的数据。我的工作流程如下:

  1. 使用 D3D 管理器创建源 reader 以启用硬件加速。
  2. 将当前媒体类型设置为 YUY2 因为 DXVA 没有 解码为任何 RGB 色彩空间。
  3. 调用 ReadSample 获取 IMFSample。工作正常。
  4. 使用VideoProcessorBlt执行YUY2到BGRA32 转换。对于这个特定的文件,它会出错 E_INVALIDARGS 错误代码。决定自己进行转换。
  5. 已使用 IMFSample::ConvertToContiguousBuffer 接收 IMFMediaBuffer。锁定此缓冲区时,间距被报告为 1280 字节。我认为这是不正确的,因为对于全高清视频,间距应该是 (1920 + 960 + 960 = 3840 字节)。

根据我对YUY2布局的理解,我转储了原始内存并提取了Y、U和V分量。你可以在下面找到它。所以,数据在那里,但我不相信它被布置为 YUY2。在解释数据方面需要一些帮助。

我的阅读代码如下:

    // Direct3D surface that stores the result of the YUV2RGB conversion
CComPtr<IDirect3DSurface9> _pTargetSurface;


IDirectXVideoAccelerationService* vidAccelService;
initVidAccelerator(&vidAccelService); // Omitting the code for this.

// Create a new surface for doing the color conversion, set it up to store X8R8G8B8 data.
hr = vidAccelService->CreateSurface( static_cast<UINT>( 1920 ),
                                     static_cast<UINT>( 1080 ),
                                     0,                                    // no back buffers
                                     D3DFMT_X8R8G8B8,                 // data format
                                     D3DPOOL_DEFAULT,                      // default memory pool
                                     0,                                    // reserved
                                     DXVA2_VideoProcessorRenderTarget,     // to use with the Blit operation
                                     &_pTargetSurface,                     // surface used to store frame
                                     NULL);


GUID processorGUID;
DXVA2_VideoDesc videoDescriptor;
D3DFORMAT processorFmt;
UINT numSubStreams;

IDirectXVideoProcessor* _vpd;
initVideoProcessor(&vpd); // Omitting the code for this

// We get the videoProcessor parameters on creation, and fill up the videoProcessBltParams accordingly.
_vpd->GetCreationParameters(&processorGUID, &videoDescriptor, &processorFmt, &numSubStreams);

RECT targetRECT; // { 0, 0, width, height } as left, top, right, bottom
targetRECT.left = 0;
targetRECT.right = videoDescriptor.SampleWidth;
targetRECT.top = 0;
targetRECT.bottom = videoDescriptor.SampleHeight;
SIZE targetSIZE; // { width, height }
targetSIZE.cx = videoDescriptor.SampleWidth;
targetSIZE.cy = videoDescriptor.SampleHeight;

// Parameters that are required to use the video processor to perform 
// YUV2RGB and other video processing operations
DXVA2_VideoProcessBltParams _frameBltParams;

_frameBltParams.TargetRect = targetRECT;
_frameBltParams.ConstrictionSize = targetSIZE;
_frameBltParams.StreamingFlags = 0; // reserved.

_frameBltParams.BackgroundColor.Y = 0x0000;
_frameBltParams.BackgroundColor.Cb = 0x0000;
_frameBltParams.BackgroundColor.Cr = 0x0000;
_frameBltParams.BackgroundColor.Alpha = 0xFFFF;

// copy attributes from videoDescriptor obtained above.
_frameBltParams.DestFormat.VideoChromaSubsampling = videoDescriptor.SampleFormat.VideoChromaSubsampling;
_frameBltParams.DestFormat.NominalRange = videoDescriptor.SampleFormat.NominalRange;
_frameBltParams.DestFormat.VideoTransferMatrix = videoDescriptor.SampleFormat.VideoTransferMatrix;
_frameBltParams.DestFormat.VideoLighting = videoDescriptor.SampleFormat.VideoLighting;
_frameBltParams.DestFormat.VideoPrimaries = videoDescriptor.SampleFormat.VideoPrimaries;
_frameBltParams.DestFormat.VideoTransferFunction = videoDescriptor.SampleFormat.VideoTransferFunction;

_frameBltParams.DestFormat.SampleFormat = DXVA2_SampleProgressiveFrame;

// The default values are used for all these parameters.
DXVA2_ValueRange pRangePABrightness;
_vpd->GetProcAmpRange(DXVA2_ProcAmp_Brightness, &pRangePABrightness);
DXVA2_ValueRange pRangePAContrast;
_vpd->GetProcAmpRange(DXVA2_ProcAmp_Contrast, &pRangePAContrast);
DXVA2_ValueRange pRangePAHue;
_vpd->GetProcAmpRange(DXVA2_ProcAmp_Hue, &pRangePAHue);
DXVA2_ValueRange pRangePASaturation;
_vpd->GetProcAmpRange(DXVA2_ProcAmp_Saturation, &pRangePASaturation);
_frameBltParams.ProcAmpValues = { pRangePABrightness.DefaultValue, pRangePAContrast.DefaultValue,
    pRangePAHue.DefaultValue, pRangePASaturation.DefaultValue };

_frameBltParams.Alpha = DXVA2_Fixed32OpaqueAlpha();
_frameBltParams.DestData = DXVA2_SampleData_TFF;

// Input video sample for the Blt operation
DXVA2_VideoSample _frameVideoSample;

_frameVideoSample.SampleFormat.VideoChromaSubsampling = videoDescriptor.SampleFormat.VideoChromaSubsampling;
_frameVideoSample.SampleFormat.NominalRange = videoDescriptor.SampleFormat.NominalRange;
_frameVideoSample.SampleFormat.VideoTransferMatrix = videoDescriptor.SampleFormat.VideoTransferMatrix;
_frameVideoSample.SampleFormat.VideoLighting = videoDescriptor.SampleFormat.VideoLighting;
_frameVideoSample.SampleFormat.VideoPrimaries = videoDescriptor.SampleFormat.VideoPrimaries;
_frameVideoSample.SampleFormat.VideoTransferFunction = videoDescriptor.SampleFormat.VideoTransferFunction;

_frameVideoSample.SrcRect = targetRECT;
_frameVideoSample.DstRect = targetRECT;
_frameVideoSample.PlanarAlpha = DXVA2_Fixed32OpaqueAlpha();
_frameVideoSample.SampleData = DXVA2_SampleData_TFF;


CComPtr<IMFSample> sample; // Assume that this was read in from a call to ReadSample

CComPtr<IMFMediaBuffer> buffer;
HRESULT hr = sample->GetBufferByIndex(0, &buffer);
CComPtr<IDirect3DSurface9>  pSrcSurface;

// From the MediaBuffer, we get the Source Surface using MFGetService
hr = MFGetService( buffer, MR_BUFFER_SERVICE, __uuidof(IDirect3DSurface9), (void**)&pSrcSurface );

// Update the videoProcessBltParams with frame specific values.
LONGLONG sampleStartTime;
sample->GetSampleTime(&sampleStartTime);
_frameBltParams.TargetFrame = sampleStartTime;

LONGLONG sampleDuration;
sample->GetSampleDuration(&sampleDuration);

_frameVideoSample.Start = sampleStartTime;
_frameVideoSample.End = sampleStartTime + sampleDuration;
_frameVideoSample.SrcSurface = pSrcSurface;

// Run videoProcessBlt using the parameters setup (this is used for color conversion)
// The returned code is E_INVALIDARGS
hr = _vpd->VideoProcessBlt( _pTargetSurface,    // target surface
                            &_frameBltParams,   // parameters
                            &_frameVideoSample, // video sample structure
                            1,                      // one sample
                            NULL);                  // reserved

调用 ReadSample of IMFSourceReader or inside OnReadSample callback function of the IMFSourceReaderCallback implementation, you might receive the MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED flag. It means that the current media type has changed for one or more streams. To get the current media type call the IMFSourceReader::GetCurrentMediaType 方法后。 在您的情况下,您需要(再次)查询视频流的 IMFMediaType 的 MF_MT_FRAME_SIZE 属性,以获得新的正确视频分辨率。您应该使用新的视频分辨率来设置 VideoProcessorBlt 参数的源矩形和目标矩形的 "width" 和 "height" 值。