다음을 통해 공유


미디어 파운데이션 변환 디코딩

Xbox 게임 개발 키트(GDK)는 미디어 기반의 소스 판독기 인터페이스를 통해 H264 및 HEVC 파일/데이터 스트림의 하드웨어 및 소프트웨어 디코딩을 지원합니다. 소스 판독기의 데스크톱 버전과 달리 Xbox GDK 버전은 데스크톱 기능 세트의 하위 집합만 지원합니다.

지원되는 코덱

하드웨어 디코딩

코덱 최대 해상도/프레임 속도(fps) 프로필 본체 유형
H264 최대 1080p/30 기준/주/높음 모든 Xbox 콘솔
HEVC 최대 1080p/30 Main/Main10 Xbox One S, Xbox One X
HEVC 최대 4k/30 Main/Main10 Xbox Series S, Xbox Series X

소프트웨어 디코딩

H264 소프트웨어 디코딩은 4096x2160@30 fps(5.2 수준)까지 지원됩니다. 그러나 Xbox Series S/X가 > 1080p 해상도를 편안하게 디코딩할 수 있는 콘솔 유형에 따라 성능이 달라집니다.

HEVC 소프트웨어 디코딩은 콘솔 유형에 따라 성능 수준으로 최대 1920x1080@30 fps까지 지원됩니다. 하지만 H264 소프트웨어 디코딩에 비해 CPU를 훨씬 많이 사용하므로 HEVC용 하드웨어 디코딩을 사용하는 것이 좋습니다.

미디어 데이터 디코딩

소스 판독기를 사용하여 미디어 데이터를 디코딩하는 단계는 다음과 같습니다.

  1. 소스 판독기 만들기
  2. 하드웨어(또는 소프트웨어) 가속 사용
  3. 출력 형식 열거
  4. 출력 형식 설정
  5. 미디어 데이터 처리
  6. 그래픽(또는 기타 D3D12) 명령 큐와 동기화
  7. 형식 및 색 영역 변환

참조 코드는 GDK 샘플과 함께 제공된 Mp4Reader 샘플을 참조하세요(여기의 코드 조각은 따라하기 쉽도록 Mp4reader에서 가져온 것입니다).

1단계: 소스 판독기 만들기

소스 판독기의 인스턴스를 만들려면 포인터를 바이트 스트림에 가져가는 MFCreateSourceReaderFromByteStream 함수를 호출합니다. 이 함수는 또한 소스 리볼버를 사용하여 미디어 소스를 만들 수 있습니다.

MFCreateSourceReaderFromByteStream 함수의 pByteStream 매개 변수는 소스 판독기에서 다양한 옵션을 설정하는 데 사용되는 IMFAttributes 인터페이스로 포인터를 가져갑니다(IMFAttributes::Set 메서드에 대한 참조 항목에서 설명된 대로). 이 매개 변수를 nullptr(으)로 설정하는 경우 기본 동작이 사용되므로 권장하지 않습니다. 최소한 소스 판독기는 하드웨어 가속 디코딩을 활성화할 수 있도록 MF_SOURCE_READER_D3D_MANAGER 특성을 항상 지정해야 합니다. MFCreateSourceReaderFromByteStream 함수는 포인터를 IMFSourceReader 인터페이스로 출력합니다.

메모 데스크톱의 원본 판독기는 동기 모드와 비동기 모드를 모두 지원합니다. Xbox 게임 개발 키트(GDK)는 기본적으로 설정된 동기식만 지원합니다.

기본 HLS/Smoothstreaming 지원을 위해 소스 판독기를 만들기 위한 입력뿐만 아니라 m3u8 또는 Smoothstreaming 매니페스트 URL을 지정할 수 있습니다. Mp4 파일 입력의 경우 몇 가지 제한 사항이 있습니다.

  1. AAC-LC 오디오 및 AC3만 지원되는 오디오 형식입니다.
  2. HEVC 데이터의 경우 형식 태그는 'hvc1'이어야 합니다('hev1'은 지원되지 않음).

C++

    // Initialize the Media Foundation platform.
    DX::ThrowIfFailed(MFStartup(MF_VERSION));

    // Call the MFCreateDXGIDeviceManager function to create the Direct3D device manager
    DX::ThrowIfFailed(MFCreateDXGIDeviceManager(&uResetToken, &pDXVAManager));

    // Call the MFResetDXGIDeviceManagerX function with a pointer to the Direct3D device
    DX::ThrowIfFailed(MFResetDXGIDeviceManagerX(pDXVAManager.Get(), device, uResetToken));

    // Create an attribute store
    DX::ThrowIfFailed(pDXVAManager.AsIID(__uuidof(IUnknown), &punkDeviceMgr));
    DX::ThrowIfFailed(MFCreateAttributes(&pMFAttributes, 3));
    DX::ThrowIfFailed(pMFAttributes->SetUnknown(MF_SOURCE_READER_D3D_MANAGER, punkDeviceMgr.Get()));
    DX::ThrowIfFailed(pMFAttributes->SetUINT32(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS, TRUE));
    DX::ThrowIfFailed(pMFAttributes->SetUINT32(MF_SOURCE_READER_DISABLE_DXVA, FALSE));
    // Don't set the MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING MFAttribute to TRUE. It is too slow.

    // Create the source reader.
    DX::ThrowIfFailed(MFCreateSourceReaderFromURL(INPUT_FILE_PATH, pMFAttributes.Get(), &m_pReader));

2단계: 하드웨어(또는 소프트웨어) 가속 사용

소스 판독기는 하드웨어 가속 비디오 디코딩을 위해 Microsoft DirectX 비디오(DXVA) 2.0과 호환됩니다. 소스 판독기와 함께 DXVA를 사용하려면 다음 단계를 수행합니다.

  1. MFCreateDXGIDeviceManager 함수를 호출하여 Direct3D 장치 관리자를 만듭니다. 이 함수는 포인터를 IMFDXGIDeviceManager 인터페이스로 가져갑니다.
  2. Direct3D 디바이스에 대한 포인터를 사용하여 MFResetDXGIDeviceManagerX 메서드를 호출합니다.
  3. MFCreateAttributes 함수를 호출하여 특성 저장소를 만듭니다.
  4. SetUnknown을 호출하여 MF_SOURCE_READER_D3D_MANAGER 특성을 IMFDXGIDeviceManager 인터페이스로 설정합니다.
  5. 또한 MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS을(를) 사용하도록 설정하고 MF_SOURCE_READER_DISABLE_DXVA을(를) FALSE로 설정합니다.
  6. 소스 판독기를 만듭니다. 생성 함수의 pAttributes 매개 변수에 있는 특성 저장소를 전달합니다.

소프트웨어 디코딩

소프트웨어 디코딩을 사용하도록 설정하려면 MF_SOURCE_READER_DISABLE_DXVA을(를) TRUE로 설정하기만 하면 됩니다. 소프트웨어 디코딩을 사용하면 스레드 선호도 마스크, 작업자 스레드 수 및 스레드 우선 순위 수준과 같은 비디오 디코더의 세분화된 스레드 제어를 위해 아래와 같이 여러 다른 매개 변수를 구성할 수 있습니다.

C++

    // Set some threading parameters for software decode
    Microsoft::WRL::ComPtr<IMFTransform> transform;
    Microsoft::WRL::ComPtr<IMFAttributes> attributes;
    DX::ThrowIfFailed(m_pReader->GetServiceForStream(MF_SOURCE_READER_FIRST_VIDEO_STREAM, GUID_NULL, IID_PPV_ARGS(transform.GetAddressOf())));
    transform->GetAttributes(attributes.GetAddressOf());
    attributes->SetUINT32(CODECAPI_AVDecVideoThreadAffinityMask, 0x0F); // Use the first 4 cores. Input value is the same as Win32 SetThreadAffinityMask
    attributes->SetUINT32(CODECAPI_AVDecNumWorkerThreads, 0x4); // 4 threads
    attributes->SetUINT32(CODECAPI_AVPriorityControl, THREAD_PRIORITY_HIGHEST); // Set thread priority. Same as Win32 SetThreadPriority for priorty levels

3단계: 출력 형식 열거

모든 미디어 소스에는 하나 이상의 스트림이 있습니다. 예를 들어, 비디오 파일에는 하나의 비디오 스트림과 하나의 오디오 스트림이 포함될 수 있습니다. 각 스트림 형식은 IMFMediaType 인터페이스로 표시되는 미디어 유형을 사용하여 설명합니다. 미디어 유형에 대한 자세한 내용은 미디어 유형을 참조하세요. 원본 판독기에서 다운로드하는 데이터 형식을 이해하려면 미디어 유형을 검토해야 합니다.

처음에 모든 스트림에 있는 기본 형식은 IMFSourceReader::GetCurrentMediaType 메서드를 호출하여 찾을 수 있습니다.

각 스트림의 경우, 미디어 소스는 해당 스트림에 대해 가능한 미디어 유형의 목록을 제공합니다. 유형의 수는 소스에 따라 다릅니다. 소스가 미디어 파일을 나타내는 경우 일반적으로 유형은 스트림당 하나 뿐입니다. 반면에 웹캠은 비디오를 여러 가지 다양한 형식으로 스트림할 수 있습니다. 이 경우에 앱은 미디어 유형의 목록에서 사용할 형식을 선택할 수 있습니다.

스트림에 대한 미디어 유형 중 하나를 가져오려면 IMFSourceReader::GetNativeMediaType 메서드를 호출합니다. 이 메서드는 스트림 인덱스와 스트림 미디어 유형 목록의 색인이라는 두 개의 인덱스 매개 변수가 필요합니다. 모든 유형의 스트림을 열거하려면 스트림 인덱스를 일정하게 유지하면서 목록 인덱스를 증분합니다. 목록 인덱스가 범위를 벗어나면 다음 코드와 같이 GetNativeMediaType(이)가 MF_E_NO_MORE_TYPES(을)를 반환합니다.

C++

HRESULT EnumerateTypesForStream(IMFSourceReader *pReader, DWORD dwStreamIndex)
{
  HRESULT hr = S_OK;
  DWORD dwMediaTypeIndex = 0;

  while (SUCCEEDED(hr))
  {
    IMFMediaType *pType = NULL;
    hr = pReader->GetNativeMediaType(dwStreamIndex, dwMediaTypeIndex, &pType);
    if (hr == MF_E_NO_MORE_TYPES)
    {
      hr = S_OK;
      break;
    }
    else if (SUCCEEDED(hr))
    {
      // Examine the media type here.

      pType->Release();
    }
    ++dwMediaTypeIndex;
  }
  return hr;
}  

모든 스트림의 미디어 유형을 열거하려면 스트림 인덱스를 증분합니다. 스트림 인덱스가 범위를 벗어나면 IMFSourceReader::GetNativeMediaTypeMF_E_INVALIDSTREAMNUMBER(을)를 반환합니다.

4단계: 출력 형식 설정

스트림을 디코딩하려면, 원하는 압축되지 않은 형식을 설명하는 새로운 미디어 유형을 만듭니다. 디코더의 경우 다음과 같이 미디어 유형을 만듭니다.

  1. MFCreateMediaType을 호출하여 새 미디어 유형을 만듭니다.
  2. MF_MT_MAJOR_TYPE 특성을 설정하여 오디오 또는 비디오를 지정합니다.
  3. 다코딩 형식의 하위 유형을 지정하도록 MF_MT_SUBTYPE 특성을 설정합니다. 사용할 수 있는 하위 유형은 오디오 하위 유형 GUID비디오 하위 유형 GUID를 참조하십시오. Xbox에서는 8비트 비디오 디코딩용 NV12와 10비트 비디오 디코딩용 P010만 지원합니다.
  4. SetCurrentMediaType을 호출합니다.

소스 판독기는 디코더를 자동으로 로드합니다. 디코딩 형식에 대한 자세한 정보를 얻으려면 SetCurrentMediaType에 호출한 후에 IMFMediaTypeHandler::GetCurrentMediaType을 호출합니다. 다음 코드는 NV12에 대한 비디오 스트림과 부동 소수점 오디오에 대한 오디오 스트림을 구성합니다.

C++

HRESULT ConfigureDecoder(IMFSourceReader *pReader, DWORD dwStreamIndex)
{
    ComPtr<IMFMediaType> pNativeType;
    ComPtr<IMFMediaType> pType;
    GUID majorType, subtype;

    // Find the native format of the stream.
    DX::ThrowIfFailed(pReader->GetNativeMediaType(dwStreamIndex, 0, &pNativeType));

    // Find the major type.
    DX::ThrowIfFailed(pNativeType->GetGUID(MF_MT_MAJOR_TYPE, &majorType));

    // Define the output type.
    DX::ThrowIfFailed(MFCreateMediaType(&pType));

    DX::ThrowIfFailed(pType->SetGUID(MF_MT_MAJOR_TYPE, majorType));

    // Select a subtype.
    if (majorType == MFMediaType_Video)
    {
        // NV12 for 8 bit (or P010 for 10 bit)are the only supported output types of Xbox One HW decoders
        subtype = MFVideoFormat_NV12;
    }
    else if (majorType == MFMediaType_Audio)
    {
        subtype = MFAudioFormat_Float;
    }
    else
    {
        // Unrecognized type. Skip.
        return;
    }

    DX::ThrowIfFailed(pType->SetGUID(MF_MT_SUBTYPE, subtype));

    // Set the uncompressed format.
    DX::ThrowIfFailed(pReader->SetCurrentMediaType(dwStreamIndex, nullptr, pType.Get()));
}  

5 단계: 미디어 데이터 처리

소스에서 미디어 데이터를 얻으려면 다음 코드에 나와 있는 것처럼 IMFSourceReader::ReadSample 메서드를 호출합니다.

C++

DWORD streamIndex, flags;
LONGLONG llTimeStamp;

hr = pReader->ReadSample(
  MF_SOURCE_READER_ANY_STREAM,    // Stream index.
  0,                              // Flags.
  &streamIndex,                   // Receives the actual stream index.
  &flags,                         // Receives status flags.
  &llTimeStamp,                   // Receives the time stamp.
  &pSample                        // Receives the sample or nullptr.
  );  

첫 번째 매개 변수는 데이터를 얻으려고 하는 스트림 인덱스입니다. MF_SOURCE_READER_ANY_STREAM(을)를 지정하여 모든 스트림에서 다음에 사용할 수 있는 데이터를 얻을 수도 있습니다. 두 번째 매개 변수에는 선택적 플래그가 포함됩니다. 이 플래그의 목록은 MF_SOURCE_READER_CONTROL_FLAG를 참조하십시오. 세 번째 매개 변수는 실제로 데이터를 생성하는 스트림 인덱스를 수신합니다. 첫 번째 매개 변수를 MF_SOURCE_READER_ANY_STREAM(으)로 설정하면 이 정보가 필요합니다. 네 번째 매개 변수는 스트림의 형식 변경과 같이 데이터를 읽으면서 발생할 수 있는 다양한 이벤트를 나타내는 상태 플래그를 수신합니다. 상태 플래그 목록을 보려면 MF_SOURCE_READER_FLAG를 참조하세요.

미디어 소스가 요청된 스트림 데이터를 생성할 수 있는 경우, ReadSample의 마지막 포인터는 미디어 샘플 개체의 IMFSample 미디어 샘플 개체의 인터페이스로 포인터를 수신합니다. 미디어 샘플을 사용하여 다음을 합니다.

  • 미디어 데이터로 포인터를 가져옵니다.
  • 표시 시간 및 샘플 기간을 가져옵니다.
  • 인터레이싱, 필드 우위 및 샘플의 기타 측면을 설명하는 특성을 가져옵니다.

미디어 데이터의 내용은 스트림의 형식에 따라 다릅니다. 압축되지 않은 비디오 스트림의 경우, 각 미디어 샘플에는 하나의 비디오 프레임이 포함됩니다. 압축되지 않은 오디오 스트림의 경우, 각 미디어 샘플에는 일련의 오디오 프레임이 포함됩니다.

ReadSample 메서드는 S_OK(을)를 반환할 수 있지만 pSample 매개 변수에서 미디어 샘플은 반환하지 못합니다. 예를 들어 파일 끝에 도달하면 ReadSample(이)가 dwFlags에서 MF_SOURCE_READERF_ENDOFSTREAM 플래그를 설정하고 pSample(을)를 nullptr(으)로 설정합니다. 이 경우에 ReadSample 메서드는 pSample 매개 변수가 nullptr(으)로 설정되어 있어도 오류가 발생하지 않았기 때문에 S_OK(을)를 반환합니다. 따라서 역참조하기 전에 항상 pSample 값을 확인해야 합니다.

비디오 샘플에는 ReadSample호출 후 몇 가지 추가 처리가 필요합니다. 여기에는 MF_MT_FRAME_SIZE 또는 MF_MT_MINIMUM_DISPLAY_APERTURE 특성에서 비디오 폭 및 높이를 확인하고 동기화를 처리하는 작업이 포함됩니다.

다음 코드 조각은 ReadSample 호출한 다음 첫 번째 비디오 스트림에 대해 메서드에서 반환된 정보를 확인합니다. 오디오 스트림 데이터를 처리하는 방법에 대한 Mp4reader 샘플을 참조하세요. 다음 섹션에서는 하드웨어 비디오 디코딩 작업에 필요한 동기화에 대해 설명합니다.

C++

  // Retreive sample from source reader
    ComPtr<IMFSample> pOutputSample;

    hr = m_pReader->ReadSample(
        uint32_t(MF_SOURCE_READER_FIRST_VIDEO_STREAM), // Stream index.
        0,                                             // Flags.
        &streamIndex,                                  // Receives the actual stream index. 
        &dwStreamFlags,                                // Receives status flags.
        &llTimestamp,                                  // Receives the time stamp.
        &pOutputSample                                 // Receives the sample or nullptr. If this parameter receives a non-NULL pointer, the caller must release the  
                                                        // interface.
    );

    if (SUCCEEDED(hr))
    {
        if (dwStreamFlags & MF_SOURCE_READERF_ENDOFSTREAM)
        {
            m_videoDone = true;
        }

        if (dwStreamFlags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED)
        {
            // The format changed. Reconfigure the decoder.
            ConfigureSourceReaderOutput(m_pReader.Get(), streamIndex);
        }

        if (pOutputSample)
        {
            if (m_videoWidth == 0 || m_videoHeight == 0
                || (dwStreamFlags & MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED) || (dwStreamFlags & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED))
            {
                // Update video width and height
                ComPtr<IMFMediaType> pMediaType;

                if (SUCCEEDED(m_pReader->GetCurrentMediaType(uint32_t(MF_SOURCE_READER_FIRST_VIDEO_STREAM), &pMediaType)))
                {
                    MFVideoArea videoArea = {};
                    if (SUCCEEDED(pMediaType->GetBlob(MF_MT_MINIMUM_DISPLAY_APERTURE, (uint8_t*)&videoArea, sizeof(MFVideoArea), nullptr)))
                    {
                        m_videoWidth = UINT(videoArea.Area.cx);
                        m_videoHeight = UINT(videoArea.Area.cy);
                    }
                    else
                    {
                        DX::ThrowIfFailed(MFGetAttributeSize(pMediaType.Get(), MF_MT_FRAME_SIZE, &m_videoWidth, &m_videoHeight));
                    }
                }
            }

            if (m_pOutputVideoSample)
            {
                DX::ThrowIfFailed(MFD3D12GpuSignalSampleFree(m_pVideoRender->GetVideoProcessCommandQueue(), m_pOutputVideoSample.Get()));
                m_pOutputVideoSample.Reset();
            }

            // The output buffer may still used by decoding ( although decode returns the buffer from CPU), put a wait single on the GPU to wait to the decoding to complete
            DX::ThrowIfFailed(MFD3D12GpuWaitForSampleReady(m_pVideoRender->GetVideoProcessCommandQueue(), pOutputSample.Get()));

            m_pOutputVideoSample = pOutputSample;
            ++m_numberOfFramesDecoded;
        }
    }

검색은 IMFSourceReader::SetCurrentPosition API를 통해 지원됩니다.

C++

    PROPVARIANT position;
    position.vt = VT_I8;
    position.hVal.QuadPart = hnsPosition;
    m_pReader->SetCurrentPosition(GUID_NULL, position);

6단계: 그래픽(또는 기타 D3D12 명령 큐)과 동기화

Xbox GDK에서는 하드웨어 디코더의 비디오 샘플이 그래픽 또는 다른 D3D12 큐와 올바르게 동기화되도록 하는 것은 호출자에게 달려 있습니다. 이에 도움이 되는 2가지 새로운 API가 있습니다.

  1. EnqueueResourceReadyWait(은)는 샘플을 처리하기 전에 디코드가 완료될 때까지 대기하도록 전달된 명령 큐에 대기 시간을 추가합니다. 이 작업은 ReadSample 후 다른 큐에서 비디오 샘플 처리를 시작하기 직전에 호출해야 합니다.
  2. EnqueueResourceRelease(은)는 디코더가 전달된 명령 큐에서 보류 중인 모든 작업을 대기하도록 합니다(내부 펜스에 신호를 보내도록 명령 큐에 요청함). 다시 사용하기 위해 샘플을 다시 풀로 반환하려면 다른 명령 큐에서 모든 처리가 완료된 후에 이 작업을 호출해야 합니다.

C++

    // For D3D12, application must call MFD3D12GpuSignalForSampleFree after it has finished processing the video sample
    HRESULT MFD3D12GpuSignalSampleFree(
        _In_ ID3D12CommandQueue* pCmdQueue,
        _In_ IMFSample* pVideoSample)
    {       
        ComPtr<IMFMediaBuffer> pBuffer;
        HRESULT hr = pVideoSample->GetBufferByIndex(0, &pBuffer);
        if (SUCCEEDED(hr))
        {
            ComPtr<IMFDXGIBuffer>  pDXGIBuffer;
            hr = pBuffer->QueryInterface<IMFDXGIBuffer>(&pDXGIBuffer);
            if (SUCCEEDED(hr))
            {
                ComPtr<IMFD3D12SynchronizationObjectCommands> pMFSyncObj;
                hr = pDXGIBuffer->GetUnknown(MF_D3D12_SYNCHRONIZATION_OBJECT, IID_PPV_ARGS(&pMFSyncObj));
                if (SUCCEEDED(hr))
                {
                    //GPU signal the sample can be freed for decoding
                    hr = pMFSyncObj->EnqueueResourceRelease(pCmdQueue);
                }
            }
        }
        return hr;
    }

    // For D3D12, application must call MFD3D12GpuWaitForSampleReady to make sure the GPU waits for decode to complete before process the sample using GPU code
    HRESULT MFD3D12GpuWaitForSampleReady(
        _In_ ID3D12CommandQueue* pCmdQueue,
        _In_ IMFSample* pVideoSample)
    {
        ComPtr<IMFMediaBuffer> pBuffer;
        HRESULT hr = pVideoSample->GetBufferByIndex(0, &pBuffer);
        if (SUCCEEDED(hr))
        {
            ComPtr<IMFDXGIBuffer> pDXGIBuffer;
            hr = pBuffer->QueryInterface<IMFDXGIBuffer>(&pDXGIBuffer);
            if (SUCCEEDED(hr))
            {
                ComPtr<IMFD3D12SynchronizationObjectCommands> pMFSyncObj;
                hr = pDXGIBuffer->GetUnknown(MF_D3D12_SYNCHRONIZATION_OBJECT, IID_PPV_ARGS(&pMFSyncObj));
                if (SUCCEEDED(hr))
                {
                    // GPU wait until the decoding completed
                    hr = pMFSyncObj->EnqueueResourceReadyWait(pCmdQueue);
                }
            }
        }
        return hr;
    }

소프트웨어 디코딩에는 이러한 동기화 호출이 필요하지 않습니다. ReadSample 반환된 샘플에는 항상 전체 디코딩된 전체 프레임이 포함됩니다.

7단계: 형식 및 색 영역 변환

이제 D3D12 비디오 프로세서 API를 사용하여 디코딩된 출력의 색 영역 변환 및 형식 변환을 수행할 수 있습니다. Xbox GDK는 YUV(DXGI_FORMAT_NV12/DXGI_FORMAT_P010)에서 RGB로(다른 8비트 RGB/무형식 변형 포함DXGI_FORMAT_R10G10B10A2_UNORM/DXGI_FORMAT_R8G8B8A8_UNORM) 형식 변환만 지원합니다.

필요한 경우, YUV-RGB 형식 변환 외에도 비디오 프로세서를 사용하여 동시에 색 영역 변환을 수행할 수 있습니다. 지원되는 변환은 다음과 같습니다.

  1. BT.2020에서 BT.709로
  2. BT.709에서 BT.2020으로

적절한 색 영역 형식은 DXGI_COLOR_SPACE_TYPE 열거형을 참조하세요.

비디오 프로세서 만들기

ID3D12VideoDevice::CreateVideoProcessor를 호출하여 ID3D12VideoProcessor인스턴스를 만듭니다. 이 비디오 프로세서는 필요한 중간 메모리, 캐시된 처리 데이터 또는 기타 임시 작업 공간을 포함하여 비디오 처리 세션의 상태를 유지합니다. 비디오 프로세서 생성 인수는 ID3D12VideoProcessCommandList1::ProcessFrames 시간에 수행되거나 사용할 수 있는 작업을 지정합니다.

C++

    D3D12_VIDEO_PROCESS_INPUT_STREAM_DESC inputStreamDesc{};
    inputStreamDesc.Format = DXGI_FORMAT_NV12;
    inputStreamDesc.ColorSpace = DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709;
    inputStreamDesc.SourceSizeRange = D3D12_VIDEO_SIZE_RANGE{ g_MaxVideoWidth, g_MaxVideoHeight, 1, 1 };
    inputStreamDesc.DestinationSizeRange = D3D12_VIDEO_SIZE_RANGE{ g_MaxVideoWidth, g_MaxVideoHeight, 1, 1 };

    D3D12_VIDEO_PROCESS_OUTPUT_STREAM_DESC outputStreamDesc{};
    outputStreamDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
    outputStreamDesc.ColorSpace = DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709;

    hr = pVideoDevice->CreateVideoProcessor(0, &outputStreamDesc, 1, &inputStreamDesc, IID_GRAPHICS_PPV_ARGS(m_pVideoProcessor.GetAddressOf()));

비디오 프로세서를 사용하여 여러 명령 목록의 명령을 녹화할 수 있지만 한 번에 하나의 명령 목록에만 연결할 수 있습니다. 애플리케이션은 비디오 프로세서에 대한 액세스를 동기화합니다. 또한 애플리케이션은 GPU에서 실행되는 순서대로 비디오 프로세서에 대한 비디오 처리 명령을 기록해야 합니다.

IMFSample에서 출력 텍스처 쿼리

다음 코드 조각은 비디오 처리 또는 기타 그래픽 작업에 사용하기 위해 IMFSample에서 디코딩된 D3D12 출력 텍스처를 쿼리하는 방법을 보여 줍니다.

C++

    IMFSample* pSample = pOutputDecodedSample;
    HRESULT hr = pSample->GetBufferCount(&bufferCount);
    if (FAILED(hr))
        return hr;
    assert(bufferCount == 1);

    ComPtr<IMFMediaBuffer> spBuffer;
    hr = pSample->GetBufferByIndex(0, spBuffer.GetAddressOf());
    if (FAILED(hr))
        return hr;

    ComPtr<IMFDXGIBuffer> spDXGIBuffer;
    hr = spBuffer.Get()->QueryInterface(spDXGIBuffer.GetAddressOf());
    if (FAILED(hr))
        return hr;

    ComPtr<ID3D12Resource> spResourceTexture;
    hr = spDXGIBuffer->GetResource(IID_GRAPHICS_PPV_ARGS(spResourceTexture.GetAddressOf()));
    if (FAILED(hr))
        return hr;

    UINT32 uiIndexSrc;
    hr = spDXGIBuffer->GetSubresourceIndex(&uiIndexSrc);
    if (FAILED(hr))
        return hr;

비디오 프로세서 명령 실행

비디오 프로세서 명령을 만들고 실행하려면 D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS 명령 목록/큐/할당자를 사용하여 표준 D3D12 작업 제출 모델을 사용합니다. 아래 샘플 코드는 만드는 방법을 보여줍니다.

C++

HRESULT hr = pDevice->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS, IID_GRAPHICS_PPV_ARGS(m_pVpCommandAllocator.GetAddressOf()));
    if (FAILED(hr))
        return hr;

    D3D12_COMMAND_QUEUE_DESC descQueue{};
    descQueue.Type = D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS;
    descQueue.Priority = 0;
    descQueue.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;

    hr = pDevice->CreateCommandQueue(&descQueue, IID_GRAPHICS_PPV_ARGS(m_pVpCommandQueue.GetAddressOf()));
    if (FAILED(hr))
        return hr;

    hr = pDevice->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_VIDEO_PROCESS, m_pVpCommandAllocator.Get(), nullptr, __uuidof(ID3D12CommandList), reinterpret_cast<void**>(m_pVpCommandList.GetAddressOf()));
    if (FAILED(hr))
        return hr;

비디오 처리 작업에 대한 모든 입력 및 출력 인수는 입력 인수 구조, D3D12_VIDEO_PROCESS_INPUT_STREAM_ARGUMENTS및 출력 인수 구조인 D3D12_VIDEO_PROCESS_OUTPUT_STREAM_ARGUMENTS로 구성됩니다. 애플리케이션은 실행하려는 비디오 처리 작업을 기록하려면 ID3D12VideoProcessCommandList::P rocessFrames를 호출해야 합니다.

명령 목록이 기록되면 비디오 프로세서 명령 큐에서 ID3D12CommandQueue::ExecuteCommandLists를 호출하여 프레임 처리를 GPU에 제출합니다. Xbox Series S/X에서는 다중 스레드 액세스로 인해 예기치 않은 동작이 발생할 수 있으므로 비디오 프로세서 명령 큐 및 그래픽 명령 큐에 대한 액세스를 동기화해야 합니다.