다음을 통해 공유


자습서: 1-Pass Windows Media Encoding

인코딩은 디지털 미디어를 한 형식에서 다른 형식으로 변환하는 프로세스를 나타냅니다. 예를 들어 ASF(Advanced Systems Format) 사양에 정의된 대로 MP3 오디오를 Windows Media Audio 형식으로 변환합니다.

참고 ASF 사양은 압축되거나 압축되지 않은 모든 형식의 미디어 데이터를 포함할 수 있는 출력 파일(.wma 또는 .wmv)에 대한 컨테이너 형식을 정의합니다. 예를 들어 .wma 파일과 같은 ASF 컨테이너는 MP3 형식의 미디어 데이터를 포함할 수 있습니다. 인코딩 프로세스는 파일에 포함된 데이터의 실제 형식을 변환합니다.

이 자습서에서는 명확한 콘텐츠(DRM으로 보호되지 않는) 입력 원본을 Windows Media 콘텐츠에 인코딩하고 파이프라인 계층 ASF 구성 요소를 사용하여 새 ASF 파일(.wm*)에 데이터를 쓰는 방법을 보여 줍니다. 이러한 구성 요소는 미디어 세션의 인스턴스에 의해 제어되는 부분 인코딩 토폴로지 빌드에 사용됩니다.

이 자습서에서는 입력 및 출력 파일 이름과 인코딩 모드를 인수로 사용하는 콘솔 애플리케이션을 만듭니다. 입력 파일은 압축 또는 압축되지 않은 미디어 형식일 수 있습니다. 유효한 인코딩 모드는 "CBR"(상수 비트 전송률) 또는 "VBR"(가변 비트 전송률)입니다. 애플리케이션은 입력 파일 이름으로 지정된 원본을 나타내는 미디어 원본을 만들고, ASF 파일 싱크를 만들어 원본 파일의 인코딩된 내용을 ASF 파일에 보관합니다. 시나리오를 구현하기 쉽게 유지하기 위해 출력 파일에는 오디오 스트림 하나와 비디오 스트림이 하나만 있습니다. 애플리케이션은 오디오 스트림 형식 변환을 위한 Windows Media Audio 9.1 Professional 코덱과 비디오 스트림에 대한 Windows Media Video 9 코덱을 삽입합니다.

일정한 비트 전송률 인코딩의 경우 인코딩 세션이 시작되기 전에 인코더는 달성해야 하는 대상 비트 속도를 알고 있어야 합니다. 이 자습서에서 "CBR" 모드의 경우 애플리케이션은 미디어 형식 협상 중에 인코더에서 검색되는 첫 번째 출력 미디어 형식과 함께 사용할 수 있는 비트 전송률을 대상 비트 전송률로 사용합니다. 가변 비트 전송률 인코딩의 경우 이 자습서에서는 품질 수준을 설정하여 가변 비트 전송률로 인코딩하는 방법을 보여 줍니다. 오디오 스트림은 품질 수준 98로 인코딩되고 비디오 스트림은 품질 수준 95로 인코딩됩니다.

다음 절차에서는 1단계 인코딩 모드를 사용하여 ASF 컨테이너에서 Windows Media 콘텐츠를 인코딩하는 단계를 요약합니다.

  1. 원본 확인자를 사용하여 지정된 미디어 원본을 만듭니다.
  2. 미디어 원본의 스트림을 열거합니다.
  3. ASF 미디어 싱크를 만들고 인코딩해야 하는 미디어 원본의 스트림에 따라 스트림 싱크를 추가합니다.
  4. 필요한 인코딩 속성을 사용하여 미디어 싱크를 구성합니다.
  5. 출력 파일의 스트림에 대한 Windows Media 인코더를 만듭니다.
  6. 미디어 싱크에 설정된 속성을 사용하여 인코더를 구성합니다.
  7. 부분 인코딩 토폴로지 빌드
  8. 미디어 세션을 인스턴스화하고 미디어 세션에서 토폴로지 설정
  9. 미디어 세션을 제어하고 미디어 세션에서 모든 관련 이벤트를 가져오면 인코딩 세션을 시작합니다.
  10. VBR 인코딩의 경우 인코더에서 인코딩 속성 값을 가져와 미디어 싱크에 설정합니다.
  11. 인코딩 세션을 닫고 종료합니다.

이 자습서에는 다음 섹션이 포함되어 있습니다.

필수 구성 요소

이 자습서에서는 다음을 가정합니다.

  • 당신은 Media Foundation에서 ASF 개체와 함께 작업하기 위해 제공한 ASF 파일 구조파이프라인 계층 ASF 구성 요소에 대해 잘 알고 있습니다. 이러한 구성 요소에는 다음 개체가 포함됩니다.

  • Windows Media 인코더 및 다양한 인코딩 유형, 특히 상수 비트 전송률 인코딩 Quality-Based 가변 비트 전송률 인코딩 익숙합니다.

  • 인코더 MFT 작업에 익숙합니다. 특히 인코더의 인스턴스를 만들고 인코더에서 입력 및 출력 형식을 설정합니다.

  • 토폴로지 개체 및 인코딩 토폴로지를 빌드하는 방법에 대해 잘 알고 있습니다. 토폴로지 및 토폴로지 노드에 대한 자세한 내용은 토폴로지 만들기를 참조하세요.

  • 미디어 세션이벤트 모델 및 흐름 제어 작업에 익숙합니다. 자세한 내용은 미디어 세션 이벤트참조하세요.

프로젝트 설정

  1. 원본 파일에 다음 헤더를 포함합니다.

    #include <new>
    #include <mfidl.h>            // Media Foundation interfaces
    #include <mfapi.h>            // Media Foundation platform APIs
    #include <mferror.h>        // Media Foundation error codes
    #include <wmcontainer.h>    // ASF-specific components
    #include <wmcodecdsp.h>        // Windows Media DSP interfaces
    #include <Dmo.h>            // DMO objects
    #include <uuids.h>            // Definition for FORMAT_VideoInfo
    #include <propvarutil.h>
    
    
  2. 다음 라이브러리 파일에 연결합니다.

    // The required link libraries 
    #pragma comment(lib, "mfplat")
    #pragma comment(lib, "mf")
    #pragma comment(lib, "mfuuid")
    #pragma comment(lib, "msdmo")
    #pragma comment(lib, "strmiids")
    #pragma comment(lib, "propsys")
    
    
  3. SafeRelease 함수를 선언합니다.

    template <class T> void SafeRelease(T **ppT)
    {
        if (*ppT)
        {
            (*ppT)->Release();
            *ppT = NULL;
        }
    }
    
  4. ENCODING_MODE 열거형을 선언하여 CBR 및 VBR 인코딩 형식을 정의합니다.

    // Encoding mode
    typedef enum ENCODING_MODE {
      NONE  = 0x00000000,
      CBR   = 0x00000001,
      VBR   = 0x00000002,
    } ;
    
    
  5. 비디오 스트림의 버퍼 창에 대한 상수 정의

    // Video buffer window
    const INT32 VIDEO_WINDOW_MSEC = 3000;
    
    

미디어 원본 만들기

입력 원본에 대한 미디어 원본을 만듭니다. Media Foundation은 MP3, MP4/3GP, AVI/WAVE 등 다양한 미디어 형식에 대한 기본 제공 미디어 원본을 제공합니다. Media Foundation에서 지원하는 형식에 대한 자세한 내용은 Media Foundation 지원되는 미디어 형식참조하세요.

미디어 소스를 생성하려면 소스 해석기을 사용합니다. 이 개체는 원본 파일에 대해 지정된 URL을 분석하고 적절한 미디어 원본을 만듭니다.

다음 전화를 걸어주세요.

다음 코드 예제에서는 지정된 파일에 대한 미디어 원본을 만드는 함수 CreateMediaSource를 보여 있습니다.

//  Create a media source from a URL.
HRESULT CreateMediaSource(PCWSTR sURL, IMFMediaSource **ppSource)
{
    MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;

    IMFSourceResolver* pSourceResolver = NULL;
    IUnknown* pSource = NULL;

    // Create the source resolver.
    HRESULT hr = MFCreateSourceResolver(&pSourceResolver);
    if (FAILED(hr))
    {
        goto done;
    }

    // Use the source resolver to create the media source.

    // Note: For simplicity this sample uses the synchronous method to create 
    // the media source. However, creating a media source can take a noticeable
    // amount of time, especially for a network source. For a more responsive 
    // UI, use the asynchronous BeginCreateObjectFromURL method.

    hr = pSourceResolver->CreateObjectFromURL(
        sURL,                       // URL of the source.
        MF_RESOLUTION_MEDIASOURCE,  // Create a source object.
        NULL,                       // Optional property store.
        &ObjectType,        // Receives the created object type. 
        &pSource            // Receives a pointer to the media source.
        );
    if (FAILED(hr))
    {
        goto done;
    }

    // Get the IMFMediaSource interface from the media source.
    hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource));

done:
    SafeRelease(&pSourceResolver);
    SafeRelease(&pSource);
    return hr;
}

ASF 파일 싱크 생성하기

인코딩 세션이 끝날 때 인코딩된 미디어 데이터를 ASF 파일에 보관하는 ASF 파일 싱크의 인스턴스를 만듭니다.

이 자습서에서는 ASF 파일 싱크에 대한 활성화 개체를 만듭니다. 필요한 스트림 싱크를 만들려면 파일 싱크에 다음 정보가 필요합니다.

  • 최종 파일에 인코딩하고 기록할 스트림입니다.
  • 인코딩 유형, 인코딩 패스 수 및 관련 속성과 같은 미디어 콘텐츠를 인코딩하는 데 사용되는 인코딩 속성입니다.
  • 새는 버킷 매개 변수(비트 속도 및 버퍼 크기)를 자동으로 조정해야 하는지 여부를 미디어 싱크에 나타내는 전역 파일 속성입니다.

스트림 정보는 ASF Profile 개체에 구성되고 인코딩 및 전역 속성은 ASF ContentInfo 개체에서 관리하는 속성 저장소에 설정됩니다.

ASF 파일 싱크에 대한 개요는 ASF 미디어 싱크을 참조하세요.

ASF 프로필 개체 만들기

ASF 파일 싱크가 인코딩된 미디어 데이터를 ASF 파일에 쓰려면 싱크에서 스트림의 수와 스트림 싱크를 만들 스트림의 유형을 알고 있어야 합니다. 이 자습서에서는 미디어 원본에서 해당 정보를 추출하고 해당 출력 스트림을 만듭니다. 출력 스트림을 하나의 오디오 스트림과 하나의 비디오 스트림으로 제한합니다. 원본에서 선택한 각 스트림에 대해 대상 스트림을 만들고 프로필에 추가합니다.

이 단계를 구현하려면 다음 개체가 필요합니다.

  • ASF 프로필
  • 미디어 소스에 대한 프레젠테이션 설명
  • 미디어 소스에서 선택한 스트림에 대한 스트림 설명자입니다.
  • 선택한 스트림에 대한 미디어 형식입니다.

다음 단계에서는 ASF 프로필 및 대상 스트림을 만드는 프로세스를 설명합니다.

ASF 프로필을 만들려면

  1. MFCreateASFProfile 호출하여 빈 프로필 개체를 만듭니다.

  2. IMFMediaSource::CreatePresentationDescriptor 호출하여 이 자습서의 "미디어 원본 만들기" 섹션에 설명된 단계에서 만든 미디어 원본에 대한 프레젠테이션 설명자를 만듭니다.

  3. IMFPresentationDescriptor::GetStreamDescriptorCount 호출하여 미디어 원본의 스트림 수를 가져옵니다.

  4. 미디어 원본의 각 스트림에 대해 IMFPresentationDescriptor::GetStreamDescriptorByIndex 호출하고 스트림의 스트림 설명자를 가져옵니다.

  5. IMFStreamDescriptor::GetMediaTypeHandler, 그 다음에 IMFMediaTypeHandler::GetMediaTypeByIndex 호출하고 스트림에 대한 첫 번째 미디어 형식을 가져옵니다.

    참고 복잡한 호출을 방지하려면 스트림당 하나의 미디어 형식만 있다고 가정하고 스트림의 첫 번째 미디어 형식을 선택합니다. 복잡한 스트림의 경우 미디어 형식 처리기에서 각 미디어 형식을 열거하고 인코딩할 미디어 형식을 선택해야 합니다.

  6. IMFMediaType::GetMajorType호출하여 스트림의 주요 형식을 가져와서 스트림에 오디오 또는 비디오가 포함되어 있는지 확인합니다.

  7. 스트림의 주 유형에 따라 대상 미디어 형식을 만듭니다. 이러한 미디어 형식은 인코더가 인코딩 세션 중에 사용할 형식 정보를 보유합니다. 이 자습서의 다음 섹션에서는 대상 미디어 형식을 만드는 프로세스에 대해 설명합니다.

    • 압축 오디오 미디어 유형 만들기
    • 압축된 비디오 미디어 형식 만들기
  8. 대상 미디어 유형에 따라 스트림을 만들고, 애플리케이션의 요구 사항에 따라 스트림을 구성하고, 스트림을 프로필에 추가합니다. 자세한 내용은 ASF 파일 싱크에 스트림 정보 추가을 참조하세요.

    1. IMFASFProfile::CreateStream 호출하고 대상 미디어 형식을 전달하여 출력 스트림을 만듭니다. 이 메서드는 스트림 개체의 IMFASFStreamConfig 인터페이스를 검색합니다.
    2. 스트림을 구성합니다.
      • IMFASFStreamConfig::SetStreamNumber 호출하여 스트림에 숫자를 할당합니다. 이 설정은 필수입니다.
      • 필요에 따라 IMFASFStreamConfig 메서드 및 관련 스트림 구성 특성을 호출하여 새는 버킷 매개 변수, 페이로드 확장, 각 스트림에 대한 상호 제외를 구성합니다.
    3. ASF ContentInfo 개체를 사용하여 스트림 수준 인코딩 속성을 추가합니다. 이 단계에 대한 자세한 내용은 이 자습서의 "ASF ContentInfo 개체 만들기" 섹션을 참조하세요.
    4. IMFASFProfile::SetStream 호출하여 스트림을 프로필에 추가합니다.

    다음 코드 예제에서는 출력 오디오 스트림을 만듭니다.

    //-------------------------------------------------------------------
    //  CreateAudioStream
    //  Create an audio stream and add it to the profile.
    //
    //  pProfile: A pointer to the ASF profile.
    //  wStreamNumber: Stream number to assign for the new stream.
    //-------------------------------------------------------------------
    
    HRESULT CreateAudioStream(IMFASFProfile* pProfile, WORD wStreamNumber)
    {
        if (!pProfile)
        {
            return E_INVALIDARG;
        }
        if (wStreamNumber < 1 || wStreamNumber > 127 )
        {
            return MF_E_INVALIDSTREAMNUMBER;
        }
    
        IMFMediaType* pAudioType = NULL;
        IMFASFStreamConfig* pAudioStream = NULL;
    
        //Create an output type from the encoder
        HRESULT hr = GetOutputTypeFromWMAEncoder(&pAudioType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Create a new stream with the audio type
        hr = pProfile->CreateStream(pAudioType, &pAudioStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Set stream number
        hr = pAudioStream->SetStreamNumber(wStreamNumber);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Add the stream to the profile
        hr = pProfile->SetStream(pAudioStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
        wprintf_s(L"Audio Stream created. Stream Number: %d.\n", wStreamNumber);
    
    done:
        SafeRelease(&pAudioStream);
        SafeRelease(&pAudioType);
        return hr;
    }
    

    다음 코드 예제에서는 출력 비디오 스트림을 만듭니다.

    //-------------------------------------------------------------------
    //  CreateVideoStream
    //  Create an video stream and add it to the profile.
    //
    //  pProfile: A pointer to the ASF profile.
    //  wStreamNumber: Stream number to assign for the new stream.
    //    pType: A pointer to the source's video media type.
    //-------------------------------------------------------------------
    
    HRESULT CreateVideoStream(IMFASFProfile* pProfile, WORD wStreamNumber, IMFMediaType* pType)
    {
        if (!pProfile)
        {
            return E_INVALIDARG;
        }
        if (wStreamNumber < 1 || wStreamNumber > 127 )
        {
            return MF_E_INVALIDSTREAMNUMBER;
        }
    
        HRESULT hr = S_OK;
    
    
        IMFMediaType* pVideoType = NULL;
        IMFASFStreamConfig* pVideoStream = NULL;
    
        UINT32 dwBitRate = 0;
    
        //Create a new video type from the source type
        hr = CreateCompressedVideoType(pType, &pVideoType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Create a new stream with the video type
        hr = pProfile->CreateStream(pVideoType, &pVideoStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
    
        //Set a valid stream number
        hr = pVideoStream->SetStreamNumber(wStreamNumber);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Add the stream to the profile
        hr = pProfile->SetStream(pVideoStream);
        if (FAILED(hr))
        {
            goto done;
        }
    
        wprintf_s(L"Video Stream created. Stream Number: %d .\n", wStreamNumber);
    
    done:
    
        SafeRelease(&pVideoStream);
        SafeRelease(&pVideoType);
    
        return hr;
    }
    

다음 코드 예제에서는 원본의 스트림에 따라 출력 스트림을 만듭니다.

    //For each stream in the source, add target stream information in the profile
    for (DWORD iStream = 0; iStream < dwSrcStream; iStream++)
    {
        hr = pPD->GetStreamDescriptorByIndex(
            iStream, &fSelected, &pStreamDesc);
        if (FAILED(hr))
        {
            goto done;
        }

        if (!fSelected)
        {
            continue;
        }

        //Get the media type handler
        hr = pStreamDesc->GetMediaTypeHandler(&pHandler);
        if (FAILED(hr))
        {
            goto done;
        }

        //Get the first media type 
        hr = pHandler->GetMediaTypeByIndex(0, &pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }

        //Get the major type
        hr = pMediaType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }

        // If this is a video stream, create the target video stream
        if (guidMajor == MFMediaType_Video)
        {
            //The stream level encoding properties will be set in this call
            hr = CreateVideoStream(pProfile, wStreamID, pMediaType);

            if (FAILED(hr))
            {
                goto done;
            }
        }
        
        // If this is an audio stream, create the target audio stream
        if (guidMajor == MFMediaType_Audio)
        {
            //The stream level encoding properties will be set in this call
            hr = CreateAudioStream(pProfile, wStreamID);

            if (FAILED(hr))
            {
                goto done;
            }
        }

        //Get stream's encoding property
        hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamID, &pContentInfoProps);
        if (FAILED(hr))
        {
            goto done;
        }

        //Set the stream-level encoding properties
        hr = SetEncodingProperties(guidMajor, pContentInfoProps);       
        if (FAILED(hr))
        {
            goto done;
        }


        SafeRelease(&pMediaType);
        SafeRelease(&pStreamDesc);
        SafeRelease(&pContentInfoProps);
        wStreamID++;
    }

압축 오디오 미디어 유형 만들기

출력 파일에 오디오 스트림을 포함하려면 필요한 특성을 설정하여 인코딩된 형식의 특성을 지정하여 오디오 형식을 만듭니다. 오디오 형식이 Windows Media 오디오 인코더와 호환되는지 확인하려면 인코더 MFT를 인스턴스화하고, 인코딩 속성을 설정하고, IMFTransform::GetOutputAvailableType호출하여 미디어 형식을 가져옵니다. 사용 가능한 모든 형식을 반복하고, 각 미디어 형식의 특성을 가져오고, 요구 사항에 가장 가까운 형식을 선택하여 필요한 출력 형식을 가져옵니다. 이 자습서에서는 인코더에서 지원하는 출력 미디어 형식 목록에서 사용 가능한 첫 번째 형식을 가져옵니다.

참고 Windows 7의 경우 Media Foundation은 호환되는 오디오 유형의 목록을 검색하는 MFTranscodeGetAudioOutputAvailableTypes새 함수를 제공합니다. 이 함수는 CBR 인코딩에 대한 미디어 형식만 가져옵니다.

전체 오디오 형식에는 다음 특성이 설정되어 있어야 합니다.

다음 코드 예제에서는 Windows Media Audio 인코더에서 호환되는 형식을 가져오면 압축된 오디오 형식을 만듭니다. SetEncodingProperties에 대한 구현은 이 자습서의 "ASF ContentInfo 개체 만들기" 섹션에 나와 있습니다.

//-------------------------------------------------------------------
//  GetOutputTypeFromWMAEncoder
//  Gets a compatible output type from the Windows Media audio encoder.
//
//  ppAudioType: Receives a pointer to the media type.
//-------------------------------------------------------------------

HRESULT GetOutputTypeFromWMAEncoder(IMFMediaType** ppAudioType)
{
    if (!ppAudioType)
    {
        return E_POINTER;
    }

    IMFTransform* pMFT = NULL;
    IMFMediaType* pType = NULL;
    IPropertyStore* pProps = NULL;

    //We need to find a suitable output media type
    //We need to create the encoder to get the available output types
    //and discard the instance.

    CLSID *pMFTCLSIDs = NULL;   // Pointer to an array of CLISDs. 
    UINT32 cCLSID = 0;            // Size of the array.

    MFT_REGISTER_TYPE_INFO tinfo;
    
    tinfo.guidMajorType = MFMediaType_Audio;
    tinfo.guidSubtype = MFAudioFormat_WMAudioV9;

    // Look for an encoder.
    HRESULT hr = MFTEnum(
        MFT_CATEGORY_AUDIO_ENCODER,
        0,                  // Reserved
        NULL,                // Input type to match. None.
        &tinfo,             // WMV encoded type.
        NULL,               // Attributes to match. (None.)
        &pMFTCLSIDs,        // Receives a pointer to an array of CLSIDs.
        &cCLSID             // Receives the size of the array.
        );
    if (FAILED(hr))
    {
        goto done;
    }

    // MFTEnum can return zero matches.
    if (cCLSID == 0)
    {
        hr = MF_E_TOPO_CODEC_NOT_FOUND;
        goto done;
    }
    else
    {
        // Create the MFT decoder
        hr = CoCreateInstance(pMFTCLSIDs[0], NULL,
            CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMFT));

        if (FAILED(hr))
        {
            goto done;
        }

    }

    // Get the encoder's property store

    hr = pMFT->QueryInterface(IID_PPV_ARGS(&pProps));
    if (FAILED(hr))
    {
        goto done;
    }
    
    //Set encoding properties
    hr = SetEncodingProperties(MFMediaType_Audio, pProps);
    if (FAILED(hr))
    {
        goto done;
    }

    //Get the first output type
    //You can loop through the available output types to choose 
    //the one that meets your target requirements
    hr = pMFT->GetOutputAvailableType(0, 0, &pType);
    if (FAILED(hr))
    {
        goto done;
    }

    //Return to the caller
    *ppAudioType = pType;
    (*ppAudioType)->AddRef();
    
done:
    SafeRelease(&pProps);
    SafeRelease(&pType);
    SafeRelease(&pMFT);
    CoTaskMemFree(pMFTCLSIDs);
    return hr;
}

압축된 비디오 미디어 형식 만들기

출력 파일에 비디오 스트림을 포함하려면 완전히 인코딩된 비디오 형식을 만듭니다. 전체 미디어 형식에는 원하는 비트 전송률 및 코덱 프라이빗 데이터가 포함되어야 합니다.

전체 비디오 미디어 형식을 만들 수 있는 두 가지 방법이 있습니다.

  • 빈 미디어 형식을 만들고 원본 비디오 형식에서 미디어 형식 특성을 복사하고 GUID 상수 MFVideoFormat_WMV3 사용하여 MF_MT_SUBTYPE 특성을 덮어씁 수 있습니다.

    전체 비디오 형식에는 다음 특성이 설정되어 있어야 합니다.

    • MF_MT_MAJOR_TYPE
    • MF_MT_SUBTYPE
    • MF_MT_FRAME_RATE (프레임 속도)
    • MF_MT_FRAME_SIZE
    • MF_MT_INTERLACE_MODE
    • MF_MT_PIXEL_ASPECT_RATIO
    • MF_MT_AVG_BITRATE
    • MF_MT_USER_DATA

    다음 코드 예제에서는 소스의 비디오 형식에서 압축된 비디오 형식을 만듭니다.

    //-------------------------------------------------------------------
    //  CreateCompressedVideoType
    //  Creates an output type from source's video media type.
    //
    //    pType: A pointer to the source's video media type.
    //  ppVideoType: Receives a pointer to the media type.
    //-------------------------------------------------------------------
    
    HRESULT CreateCompressedVideoType(
            IMFMediaType* pType, 
            IMFMediaType** ppVideoType)
    {
        if (!pType)
        {
            return E_INVALIDARG;
        }
        if (!ppVideoType)
        {
            return E_POINTER;
        }
    
        HRESULT hr = S_OK;
        MFRatio fps = { 0 };
        MFRatio par = { 0 };
        UINT32 width = 0, height = 0;
        UINT32 interlace = MFVideoInterlace_Unknown;
        UINT32 fSamplesIndependent = 0;
        UINT32 cBitrate = 0;
    
        IMFMediaType* pMediaType = NULL;
    
        GUID guidMajor = GUID_NULL;
    
        hr = pType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }
    
        if (guidMajor != MFMediaType_Video )
        {
            hr = MF_E_INVALID_FORMAT;
            goto done;
        }
    
        hr = MFCreateMediaType(&pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pType->CopyAllItems(pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Fill the missing attributes
        //1. Frame rate
        hr = MFGetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            fps.Numerator = 30000;
            fps.Denominator = 1001;
    
            hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, fps.Numerator, fps.Denominator);
            if (FAILED(hr))
            {
                goto done;
            }
        }
    
        ////2. Frame size
        hr = MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &width, &height);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            width = 1280;
            height = 720;
    
            hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, width, height);
            if (FAILED(hr))
            {
                goto done;
            }
        }
    
        ////3. Interlace mode
    
        if (FAILED(pMediaType->GetUINT32(MF_MT_INTERLACE_MODE, &interlace)))
        {
            hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        ////4. Pixel aspect Ratio
        hr = MFGetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&par.Numerator, (UINT32*)&par.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            par.Numerator = par.Denominator = 1;
            hr = MFSetAttributeRatio(pMediaType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32)par.Numerator, (UINT32)par.Denominator);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        //6. bit rate
        if (FAILED(pMediaType->GetUINT32(MF_MT_AVG_BITRATE, &cBitrate)))
        {
            hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, 1000000);
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        hr = pMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_WMV3);
        if (FAILED(hr))
        {
            goto done;
        }
    
        // Return the pointer to the caller.
        *ppVideoType = pMediaType;
        (*ppVideoType)->AddRef();
    
    
    done:
        SafeRelease(&pMediaType);
        return hr;
    }
    
    
  • 인코딩 속성을 설정한 다음 IMFTransform::GetOutputAvailableType호출하여 Windows Media 비디오 인코더에서 호환되는 미디어 형식을 가져옵니다. 이 메서드는 부분 형식을 반환합니다. 다음 정보를 추가하여 부분 형식을 전체 형식으로 변환해야 합니다.

    • MF_MT_AVG_BITRATE 특성에서 비디오 비트 속도를 설정합니다.
    • MF_MT_USER_DATA 특성을 설정하여 코덱 프라이빗 데이터를 추가합니다. 자세한 지침은 WMV 인코더 구성하는"프라이빗 코덱 데이터"를 참조하세요.

    IWMCodecPrivateData::GetPrivateData 코덱 프라이빗 데이터를 반환하기 전에 비트 속도를 확인하므로 코덱 프라이빗 데이터를 가져오기 전에 비트 속도를 설정해야 합니다.

    다음 코드 예제에서는 Windows Media Video 인코더에서 호환되는 형식을 가져오면 압축된 비디오 형식을 만듭니다. 또한 압축되지 않은 비디오 형식을 만들고 인코더의 입력으로 설정합니다. 이는 도우미 함수 CreateUncompressedVideoType에서 구현됩니다. GetOutputTypeFromWMVEncoder는 코덱 프라이빗 데이터를 추가하여 반환된 부분 형식을 전체 형식으로 변환합니다. SetEncodingProperties에 대한 구현은 이 자습서의 "ASF ContentInfo 개체 만들기" 섹션에 나와 있습니다.

    //-------------------------------------------------------------------
    //  GetOutputTypeFromWMVEncoder
    //  Gets a compatible output type from the Windows Media video encoder.
    //
    //  pType: A pointer to the source video stream's media type.
    //  ppVideoType: Receives a pointer to the media type.
    //-------------------------------------------------------------------
    
    HRESULT GetOutputTypeFromWMVEncoder(IMFMediaType* pType, IMFMediaType** ppVideoType)
    {
        if (!ppVideoType)
        {
            return E_POINTER;
        }
    
        IMFTransform* pMFT = NULL;
        IPropertyStore* pProps = NULL;
        IMFMediaType* pInputType = NULL;
        IMFMediaType* pOutputType = NULL;
    
        UINT32 cBitrate = 0;
    
        //We need to find a suitable output media type
        //We need to create the encoder to get the available output types
        //and discard the instance.
    
        CLSID *pMFTCLSIDs = NULL;   // Pointer to an array of CLISDs. 
        UINT32 cCLSID = 0;          // Size of the array.
    
        MFT_REGISTER_TYPE_INFO tinfo;
    
        tinfo.guidMajorType = MFMediaType_Video;
        tinfo.guidSubtype = MFVideoFormat_WMV3;
    
        // Look for an encoder.
        HRESULT hr = MFTEnum(
            MFT_CATEGORY_VIDEO_ENCODER,
            0,                  // Reserved
            NULL,               // Input type to match. None.
            &tinfo,             // WMV encoded type.
            NULL,               // Attributes to match. (None.)
            &pMFTCLSIDs,        // Receives a pointer to an array of CLSIDs.
            &cCLSID             // Receives the size of the array.
            );
        if (FAILED(hr))
        {
            goto done;
        }
    
        // MFTEnum can return zero matches.
        if (cCLSID == 0)
        {
            hr = MF_E_TOPO_CODEC_NOT_FOUND;
            goto done;
        }
        else
        {
            //Create the MFT decoder
            hr = CoCreateInstance(pMFTCLSIDs[0], NULL,
                CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pMFT));
            if (FAILED(hr))
            {
                goto done;
            }
    
        }
    
        //Get the video encoder property store
        hr = pMFT->QueryInterface(IID_PPV_ARGS(&pProps));
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Set encoding properties
        hr = SetEncodingProperties(MFMediaType_Video, pProps);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = CreateUncompressedVideoType(pType, &pInputType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pMFT->SetInputType(0, pInputType, 0);
    
        //Get the first output type
        //You can loop through the available output types to choose 
        //the one that meets your target requirements
        hr =  pMFT->GetOutputAvailableType(0, 0, &pOutputType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pType->GetUINT32(MF_MT_AVG_BITRATE, &cBitrate);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Now set the bit rate
        hr = pOutputType->SetUINT32(MF_MT_AVG_BITRATE, cBitrate);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = AddPrivateData(pMFT, pOutputType);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Return to the caller
        *ppVideoType = pOutputType;
        (*ppVideoType)->AddRef();
    
    done:
        SafeRelease(&pProps);
        SafeRelease(&pOutputType);
        SafeRelease(&pInputType);
        SafeRelease(&pMFT);
        CoTaskMemFree(pMFTCLSIDs);
        return hr;
    }
    

    다음 코드 예제에서는 압축되지 않은 비디오 형식을 만듭니다.

    //-------------------------------------------------------------------
    //  CreateUncompressedVideoType
    //  Creates an uncompressed video type.
    //
    //    pType: A pointer to the source's video media type.
    //  ppVideoType: Receives a pointer to the media type.
    //-------------------------------------------------------------------
    
    HRESULT CreateUncompressedVideoType(IMFMediaType* pType, IMFMediaType** ppMediaType)
    {
        if (!pType)
        {
            return E_INVALIDARG;
        }
        if (!ppMediaType)
        {
            return E_POINTER;
        }
    
        MFRatio fps = { 0 };
        MFRatio par = { 0 };
        UINT32 width = 0, height = 0;
        UINT32 interlace = MFVideoInterlace_Unknown;
        UINT32 cBitrate = 0;
    
        IMFMediaType* pMediaType = NULL;
    
        GUID guidMajor = GUID_NULL;
    
        HRESULT hr = pType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }
    
        if (guidMajor != MFMediaType_Video )
        {
            hr = MF_E_INVALID_FORMAT;
            goto done;
        }
    
        hr = MFGetAttributeRatio(pType, MF_MT_FRAME_RATE, (UINT32*)&fps.Numerator, (UINT32*)&fps.Denominator);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            fps.Numerator = 30000;
            fps.Denominator = 1001;
    
        }
        hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height);
        if (hr == MF_E_ATTRIBUTENOTFOUND)
        {
            width = 1280;
            height = 720;
    
        }
    
        interlace = MFGetAttributeUINT32(pType, MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive);
    
        hr = MFGetAttributeRatio(pType, MF_MT_PIXEL_ASPECT_RATIO, (UINT32*)&par.Numerator, (UINT32*)&par.Denominator);
        if (FAILED(hr))
        {
            par.Numerator = par.Denominator = 1;
        }
    
        cBitrate = MFGetAttributeUINT32(pType, MF_MT_AVG_BITRATE, 1000000);
    
        hr = MFCreateMediaType(&pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->SetGUID(MF_MT_MAJOR_TYPE, guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = MFSetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, fps.Numerator, fps.Denominator);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = MFSetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, width, height);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->SetUINT32(MF_MT_INTERLACE_MODE, 2);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pMediaType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
        if (FAILED(hr))
        {
            goto done;
        }
    
        hr = pMediaType->SetUINT32(MF_MT_AVG_BITRATE, cBitrate);
        if (FAILED(hr))
        {
            goto done;
        }
    
        // Return the pointer to the caller.
        *ppMediaType = pMediaType;
        (*ppMediaType)->AddRef();
    
    
    done:
        SafeRelease(&pMediaType);
        return hr;
    }
    

    다음 코드 예제에서는 지정된 비디오 미디어 형식에 코덱 프라이빗 데이터를 추가합니다.

    //
    // AddPrivateData
    // Appends the private codec data to a media type.
    //
    // pMFT: The video encoder
    // pTypeOut: A video type from the encoder's type list.
    //
    // The function modifies pTypeOut by adding the codec data.
    //
    
    HRESULT AddPrivateData(IMFTransform *pMFT, IMFMediaType *pTypeOut)
    {
        HRESULT hr = S_OK;
        ULONG cbData = 0;
        BYTE *pData = NULL;
    
        IWMCodecPrivateData *pPrivData = NULL;
    
        DMO_MEDIA_TYPE mtOut = { 0 };
    
        // Convert the type to a DMO type.
        hr = MFInitAMMediaTypeFromMFMediaType(
            pTypeOut, 
            FORMAT_VideoInfo, 
            (AM_MEDIA_TYPE*)&mtOut
            );
    
        if (SUCCEEDED(hr))
        {
            hr = pMFT->QueryInterface(IID_PPV_ARGS(&pPrivData));
        }
    
        if (SUCCEEDED(hr))
        {
            hr = pPrivData->SetPartialOutputType(&mtOut);
        }
    
        //
        // Get the private codec data
        //
    
        // First get the buffer size.
        if (SUCCEEDED(hr))
        {
            hr = pPrivData->GetPrivateData(NULL, &cbData);
        }
    
        if (SUCCEEDED(hr))
        {
            pData = new BYTE[cbData];
    
            if (pData == NULL)
            {
                hr = E_OUTOFMEMORY;
            }
        }
    
        // Now get the data.
        if (SUCCEEDED(hr))
        {
            hr = pPrivData->GetPrivateData(pData, &cbData);
        }
    
        // Add the data to the media type.
        if (SUCCEEDED(hr))
        {
            hr = pTypeOut->SetBlob(MF_MT_USER_DATA, pData, cbData);
        }
    
        delete [] pData;
        MoFreeMediaType(&mtOut);
        SafeRelease(&pPrivData);
        return hr;
    }
    

ASF ContentInfo 개체 만들기

ASF ContentInfo 개체는 주로 ASF 헤더 개체 정보를 저장하도록 설계된 WMContainer 수준 구성 요소입니다. ASF 파일 싱크는 인코딩된 파일의 ASF 헤더 개체를 작성하는 데 사용할 정보(속성 저장소)를 저장하기 위해 ContentInfo 개체를 구현합니다. 이렇게 하려면 인코딩 세션을 시작하기 전에 ContentInfo 개체에 대해 다음 정보 집합을 만들고 구성해야 합니다.

  1. MFCreateASFContentInfo 호출하여 빈 ContentInfo 개체를 만듭니다.

    다음 코드 예제에서는 빈 ContentInfo 개체를 만듭니다.

        // Create an empty ContentInfo object
        hr = MFCreateASFContentInfo(&pContentInfo);
        if (FAILED(hr))
        {
            goto done;
        }
    
    
  2. IMFASFContentInfo::GetEncodingConfigurationPropertyStore 호출하여 파일 싱크의 스트림 수준 속성 저장소를 가져옵니다. 이 호출에서는 ASF 프로필을 만드는 동안 스트림에 할당한 스트림 번호를 입력해야 합니다.

  3. 파일 싱크의 스트림 속성 저장소에서 스트림 수준 인코딩 속성 설정합니다. 자세한 내용은 파일 싱크 설정 속성의 "스트림 인코딩 속성"을 참조하세요.

    다음 코드 예제에서는 파일 싱크의 속성 저장소에서 스트림 수준 속성을 설정합니다.

            //Get stream's encoding property
            hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamID, &pContentInfoProps);
            if (FAILED(hr))
            {
                goto done;
            }
    
            //Set the stream-level encoding properties
            hr = SetEncodingProperties(guidMajor, pContentInfoProps);       
            if (FAILED(hr))
            {
                goto done;
            }
    
    
    

    다음 코드 예제에서는 SetEncodingProperties에 대한 구현을 보여줍니다. 이 함수는 CBR 및 VBR에 대한 스트림 수준 인코딩 속성을 설정합니다.

    //-------------------------------------------------------------------
    //  SetEncodingProperties
    //  Create a media source from a URL.
    //
    //  guidMT:  Major type of the stream, audio or video
    //  pProps:  A pointer to the property store in which 
    //           to set the required encoding properties.
    //-------------------------------------------------------------------
    
    HRESULT SetEncodingProperties (const GUID guidMT, IPropertyStore* pProps)
    {
        if (!pProps)
        {
            return E_INVALIDARG;
        }
    
        if (EncodingMode == NONE)
        {
            return MF_E_NOT_INITIALIZED;
        }
    
        HRESULT hr = S_OK;
    
        PROPVARIANT var;
    
        switch (EncodingMode)
        {
            case CBR:
                // Set VBR to false.
                hr = InitPropVariantFromBoolean(FALSE, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_VBRENABLED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Set the video buffer window.
                if (guidMT == MFMediaType_Video)
                {
                    hr = InitPropVariantFromInt32(VIDEO_WINDOW_MSEC, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_VIDEOWINDOW, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                break;
    
            case VBR:
                //Set VBR to true.
                hr = InitPropVariantFromBoolean(TRUE, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_VBRENABLED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Number of encoding passes is 1.
    
                hr = InitPropVariantFromInt32(1, &var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                hr = pProps->SetValue(MFPKEY_PASSESUSED, var);
                if (FAILED(hr))
                {
                    goto done;
                }
    
                // Set the quality level.
    
                if (guidMT == MFMediaType_Audio)
                {
                    hr = InitPropVariantFromUInt32(98, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_DESIRED_VBRQUALITY, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                else if (guidMT == MFMediaType_Video)
                {
                    hr = InitPropVariantFromUInt32(95, &var);
                    if (FAILED(hr))
                    {
                        goto done;
                    }
    
                    hr = pProps->SetValue(MFPKEY_VBRQUALITY, var);    
                    if (FAILED(hr))
                    {
                        goto done;
                    }
                }
                break;
    
            default:
                hr = E_UNEXPECTED;
                break;
        }    
    
    done:
        PropVariantClear(&var);
        return hr;
    }
    
  4. IMFASFContentInfo::GetEncodingConfigurationPropertyStore 호출하여 파일 싱크의 전역 속성 저장소를 가져옵니다. 이 호출에서는 첫 번째 매개 변수에 0을 전달해야 합니다. 자세한 내용은 파일 싱크설정 속성의 "전역 파일 싱크 속성"을 참조하세요.

  5. ASF 멀티플렉서의 누출 버킷 값이 올바르게 조정되도록 MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE을 VARIANT_TRUE로 설정하십시오. 이 속성에 대한 자세한 내용은 Multiplexer 개체만들기에서 "멀티플렉서 초기화 및 새는 버킷 설정"을 참조하세요.

    다음 코드 예제에서는 파일 싱크의 속성 저장소에서 스트림 수준 속성을 설정합니다.

        //Now we need to set global properties that will be set on the media sink
        hr = pContentInfo->GetEncodingConfigurationPropertyStore(0, &pContentInfoProps);
        if (FAILED(hr))
        {
            goto done;
        }
    
        //Auto adjust Bitrate
        var.vt = VT_BOOL;
        var.boolVal = VARIANT_TRUE;
    
        hr = pContentInfoProps->SetValue(MFPKEY_ASFMEDIASINK_AUTOADJUST_BITRATE, var);
    
        //Initialize with the profile
        hr = pContentInfo->SetProfile(pProfile);
        if (FAILED(hr))
        {
            goto done;
        }
    

    메모

    파일 싱크에 대한 전역 수준에서 설정할 수 있는 다른 속성이 있습니다. 자세한 내용은 ContentInfo 개체 설정 속성에서 "인코더 설정을 사용하여 ContentInfo 개체 구성"을 참조하세요.

     

구성된 ASF ContentInfo를 사용하여 ASF 파일 싱크에 대한 활성화 개체를 만듭니다(다음 섹션에서 설명).

활성화 개체를 사용하여 프로세스 외부 파일 싱크(MFCreateASFMediaSinkActivate)를 생성할 경우, 구성된 ContentInfo 개체를 사용하여 ASF 미디어 싱크를 인스턴스화할 수 있습니다(다음 섹션에서 설명). 1단계에서 설명한 대로 빈 ContentInfo 개체를 만드는 대신 프로세스 내 파일 싱크(MFCreateASFMediaSink)를 만드는 경우 파일 싱크에서 IMFMediaSink::QueryInterface 호출하여 IMFASFContentInfo 인터페이스에 대한 참조를 가져옵니다. 그런 다음 이 섹션에 설명된 대로 ContentInfo 개체를 구성해야 합니다.

ASF 파일 싱크 만들기

자습서의 이 단계에서는 이전 단계에서 만든 구성된 ASF ContentInfo를 사용하여 MFCreateASFMediaSinkActivate 함수를 호출하여 ASF 파일 싱크에 대한 활성화 개체를 만듭니다. 자세한 내용은 ASF 파일 싱크 생성을 참조하세요.

다음 코드 예제에서는 파일 싱크에 대 한 활성화 개체를 만듭니다.

    //Create the activation object for the  file sink
    hr = MFCreateASFMediaSinkActivate(sURL, pContentInfo, &pActivate);
    if (FAILED(hr))
    {
        goto done;
    }

부분 인코딩 토폴로지 빌드

다음으로 미디어 원본, 필요한 Windows Media 인코더 및 ASF 파일 싱크에 대한 토폴로지 노드를 만들어 부분 인코딩 토폴로지 빌드합니다. 토폴로지 노드를 추가한 후 원본, 변환 및 싱크 노드를 연결합니다. 토폴로지 노드를 추가하기 전에 MFCreateTopology호출하여 빈 토폴로지 개체를 만들어야 합니다.

미디어 원본에 대한 원본 토폴로지 노드 만들기

이 단계에서는 미디어 원본에 대한 원본 토폴로지 노드를 만듭니다.

이 노드를 만들려면 다음 참조가 필요합니다.

  • 이 자습서의 "미디어 원본 만들기" 섹션에 설명된 단계에서 만든 미디어 원본에 대한 포인터입니다.
  • 미디어 원본의 프레젠테이션 설명자에 대한 포인터입니다. IMFMediaSource::CreatePresentationDescriptor호출하여 미디어 소스의 IMFPresentationDescriptor 인터페이스에 대한 참조를 가져올 수 있습니다.
  • 이 자습서의 "ASF 프로필 개체 만들기" 섹션에 설명된 단계에서 대상 스트림을 만든 미디어 원본의 각 스트림에 대한 스트림 설명자에 대한 포인터입니다.

원본 노드 및 코드 예제를 만드는 방법에 대한 자세한 내용은 원본 노드 만들기참조하세요.

다음 코드 예제에서는 원본 노드 및 필요한 변환 노드를 추가하여 부분 토폴로지 만들기 이 코드는 도우미 함수 AddSourceNode 및 AddTransformOutputNodes를 호출합니다. 이러한 함수는 이 자습서의 뒷부분에 포함되어 있습니다.

//-------------------------------------------------------------------
//  BuildPartialTopology
//  Create a partial encoding topology by adding the source and the sink.
//
//  pSource:  A pointer to the media source to enumerate the source streams.
//  pSinkActivate: A pointer to the activation object for ASF file sink.
//  ppTopology:  Receives a pointer to the topology.
//-------------------------------------------------------------------

HRESULT BuildPartialTopology(
       IMFMediaSource *pSource, 
       IMFActivate* pSinkActivate,
       IMFTopology** ppTopology)
{
    if (!pSource || !pSinkActivate)
    {
        return E_INVALIDARG;
    }
    if (!ppTopology)
    {
        return E_POINTER;
    }

    HRESULT hr = S_OK;

    IMFPresentationDescriptor* pPD = NULL;
    IMFStreamDescriptor *pStreamDesc = NULL;
    IMFMediaTypeHandler* pMediaTypeHandler = NULL;
    IMFMediaType* pSrcType = NULL;

    IMFTopology* pTopology = NULL;
    IMFTopologyNode* pSrcNode = NULL;
    IMFTopologyNode* pEncoderNode = NULL;
    IMFTopologyNode* pOutputNode = NULL;


    DWORD cElems = 0;
    DWORD dwSrcStream = 0;
    DWORD StreamID = 0;
    GUID guidMajor = GUID_NULL;
    BOOL fSelected = FALSE;


    //Create the topology that represents the encoding pipeline
    hr = MFCreateTopology (&pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

        
    hr = pSource->CreatePresentationDescriptor(&pPD);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pPD->GetStreamDescriptorCount(&dwSrcStream);
    if (FAILED(hr))
    {
        goto done;
    }

    for (DWORD iStream = 0; iStream < dwSrcStream; iStream++)
    {
        hr = pPD->GetStreamDescriptorByIndex(
            iStream, &fSelected, &pStreamDesc);
        if (FAILED(hr))
        {
            goto done;
        }

        if (!fSelected)
        {
            continue;
        }

        hr = AddSourceNode(pTopology, pSource, pPD, pStreamDesc, &pSrcNode);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pStreamDesc->GetMediaTypeHandler (&pMediaTypeHandler);
        if (FAILED(hr))
        {
            goto done;
        }
        
        hr = pStreamDesc->GetStreamIdentifier(&StreamID);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pMediaTypeHandler->GetMediaTypeByIndex(0, &pSrcType);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pSrcType->GetMajorType(&guidMajor);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = AddTransformOutputNodes(pTopology, pSinkActivate, pSrcType, &pEncoderNode);
        if (FAILED(hr))
        {
            goto done;
        }

        //now we have the transform node, connect it to the source node
        hr = pSrcNode->ConnectOutput(0, pEncoderNode, 0);
        if (FAILED(hr))
        {
            goto done;
        }
                

        SafeRelease(&pStreamDesc);
        SafeRelease(&pMediaTypeHandler);
        SafeRelease(&pSrcType);
        SafeRelease(&pEncoderNode);
        SafeRelease(&pOutputNode);
        guidMajor = GUID_NULL;
    }

    *ppTopology = pTopology;
   (*ppTopology)->AddRef();


    wprintf_s(L"Partial Topology Built.\n");

done:
    SafeRelease(&pStreamDesc);
    SafeRelease(&pMediaTypeHandler);
    SafeRelease(&pSrcType);
    SafeRelease(&pEncoderNode);
    SafeRelease(&pOutputNode);
    SafeRelease(&pTopology);

    return hr;
}

다음 코드 예제에서는 원본 토폴로지 노드를 만들고 인코딩 토폴로지에 추가합니다. 이전의 토폴로지 개체, 원본 스트림을 열거하기 위한 미디어 소스, 미디어 소스의 프레젠테이션 설명자, 그리고 미디어 소스의 스트림 설명자에 대한 포인터를 받아들입니다. 호출자는 원본 토폴로지 노드에 대한 포인터를 받습니다.

// Add a source node to a topology.
HRESULT AddSourceNode(
    IMFTopology *pTopology,           // Topology.
    IMFMediaSource *pSource,          // Media source.
    IMFPresentationDescriptor *pPD,   // Presentation descriptor.
    IMFStreamDescriptor *pSD,         // Stream descriptor.
    IMFTopologyNode **ppNode)         // Receives the node pointer.
{
    IMFTopologyNode *pNode = NULL;

    // Create the node.
    HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the attributes.
    hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPD);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pSD);
    if (FAILED(hr))
    {
        goto done;
    }
    
    // Add the node to the topology.
    hr = pTopology->AddNode(pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the pointer to the caller.
    *ppNode = pNode;
    (*ppNode)->AddRef();

done:
    SafeRelease(&pNode);
    return hr;
}

필수 인코더 인스턴스화 및 변환 노드 만들기

Media Foundation 파이프라인은 인코딩해야 하는 스트림에 필요한 Windows Media 인코더를 자동으로 삽입하지 않습니다. 애플리케이션은 인코더를 수동으로 추가해야 합니다. 이렇게 하려면 이 자습서의 "ASF 프로필 개체 만들기" 섹션에 설명된 단계에서 만든 ASF 프로필의 스트림을 열거합니다. 원본의 각 스트림과 프로필의 해당 스트림에 대해 필요한 인코더를 인스턴스화합니다. 이 단계에서는 이 자습서의 "ASF 파일 싱크 만들기" 섹션에 설명된 단계에서 만든 파일 싱크의 활성화 개체에 대한 포인터가 필요합니다.

활성화 개체를 통해 인코더를 만드는 방법에 대한 개요는 인코더의 활성화 개체사용하세요.

다음 절차에서는 필요한 인코더를 인스턴스화하는 데 필요한 단계를 설명합니다.

  1. 파일 싱크 활성화에서 IMFActivate::ActivateObject 호출한 다음, QueryInterface호출하여 파일 싱크에서 IMFASFContentInfo 쿼리하여 싱크의 ContentInfo 개체에 대한 참조를 가져옵니다.

  2. IMFASFContentInfo::GetProfile호출하여 ContentInfo 개체와 연결된 ASF 프로필을 가져옵니다.

  3. 프로필의 스트림을 열거합니다. 이를 위해 스트림 수와 각 스트림의 IMFASFStreamConfig 인터페이스에 대한 참조가 필요합니다.

    다음 메서드를 호출합니다.

  4. 각 스트림에 대해 ContentInfo 개체에서 주 형식 및 스트림의 인코딩 속성을 가져옵니다.

    다음 메서드를 호출합니다.

  5. 스트림, 오디오 또는 비디오의 유형에 따라 MFCreateWMAEncoderActivate 호출하거나 MFCreateWMVEncoderActivate호출하여 인코더에 대한 활성화 개체를 인스턴스화합니다.

    이러한 함수를 호출하려면 다음 참조가 필요합니다.

  6. 오디오 스트림에 대한 새는 버킷 매개 변수를 업데이트합니다.

    MFCreateWMAEncoderActivate Windows Media 오디오 코덱에 대한 기본 인코더 MFT의 출력 형식을 설정합니다. 출력 미디어 형식이 설정되면 인코더는 출력 미디어 형식에서 평균 비트 전송률을 가져오고, 버퍼 창 범위 비트 속도를 계산하며, 인코딩 세션 동안 사용할 누수 버킷 값을 설정합니다. 인코더를 쿼리하거나 사용자 지정 값을 설정하여 파일 싱크에서 이러한 값을 업데이트할 수 있습니다. 값을 업데이트하려면 다음 정보 집합이 필요합니다.

    DWORD 배열을 만들고 오디오 스트림 싱크의 MFPKEY_ASFSTREAMSINK_CORRECTED_LEAKYBUCKET 속성에 값을 설정합니다. 업데이트된 값을 제공하지 않으면 미디어 세션에서 적절하게 설정합니다.

    자세한 내용은 새는 버킷 버퍼 모델을 참조하세요.

5단계에서 만든 활성화 개체를 토폴로지 변환 토폴로지 노드로 추가해야 합니다. 자세한 내용 및 코드 예제는 변환 노드 만들기"활성화 개체에서 변환 노드 만들기"를 참조하세요.

다음 코드 예제에서는 필수 인코더 활성화를 만들고 추가합니다. 이전에 만든 토폴로지 개체, 파일 싱크의 활성화 개체 및 원본 스트림의 미디어 형식에 대한 포인터를 사용합니다. 또한 인코딩 토폴로지에서 싱크 노드를 만들고 추가하는 AddOutputNode(다음 코드 예제 참조)를 호출합니다. 호출자는 원본 토폴로지 노드에 대한 포인터를 받습니다.

//-------------------------------------------------------------------
//  AddTransformOutputNodes
//  Creates and adds the sink node to the encoding topology.
//  Creates and adds the required encoder activates.

//  pTopology:  A pointer to the topology.
//  pSinkActivate:  A pointer to the file sink's activation object.
//  pSourceType: A pointer to the source stream's media type.
//  ppNode:  Receives a pointer to the topology node.
//-------------------------------------------------------------------

HRESULT AddTransformOutputNodes(
    IMFTopology* pTopology,
    IMFActivate* pSinkActivate,
    IMFMediaType* pSourceType,
    IMFTopologyNode **ppNode    // Receives the node pointer.
    )
{
    if (!pTopology || !pSinkActivate || !pSourceType)
    {
        return E_INVALIDARG;
    }

    IMFTopologyNode* pEncNode = NULL;
    IMFTopologyNode* pOutputNode = NULL;
    IMFASFContentInfo* pContentInfo = NULL;
    IMFASFProfile* pProfile = NULL;
    IMFASFStreamConfig* pStream = NULL;
    IMFMediaType* pMediaType = NULL;
    IPropertyStore* pProps = NULL;
    IMFActivate *pEncoderActivate = NULL;
    IMFMediaSink *pSink = NULL; 

    GUID guidMT = GUID_NULL;
    GUID guidMajor = GUID_NULL;

    DWORD cStreams = 0;
    WORD wStreamNumber = 0;

    HRESULT hr = S_OK;
        
    hr = pSourceType->GetMajorType(&guidMajor);
    if (FAILED(hr))
    {
        goto done;
    }

    // Create the node.
    hr = MFCreateTopologyNode(MF_TOPOLOGY_TRANSFORM_NODE, &pEncNode);
    if (FAILED(hr))
    {
        goto done;
    }
    
    //Activate the sink
    hr = pSinkActivate->ActivateObject(__uuidof(IMFMediaSink), (void**)&pSink);
    if (FAILED(hr))
    {
        goto done;
    }
    //find the media type in the sink
    //Get content info from the sink
    hr = pSink->QueryInterface(__uuidof(IMFASFContentInfo), (void**)&pContentInfo);
    if (FAILED(hr))
    {
        goto done;
    }
    

    hr = pContentInfo->GetProfile(&pProfile);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pProfile->GetStreamCount(&cStreams);
    if (FAILED(hr))
    {
        goto done;
    }

    for(DWORD index = 0; index < cStreams ; index++)
    {
        hr = pProfile->GetStream(index, &wStreamNumber, &pStream);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pStream->GetMediaType(&pMediaType);
        if (FAILED(hr))
        {
            goto done;
        }
        hr = pMediaType->GetMajorType(&guidMT);
        if (FAILED(hr))
        {
            goto done;
        }
        if (guidMT!=guidMajor)
        {
            SafeRelease(&pStream);
            SafeRelease(&pMediaType);
            guidMT = GUID_NULL;

            continue;
        }
        //We need to activate the encoder
        hr = pContentInfo->GetEncodingConfigurationPropertyStore(wStreamNumber, &pProps);
        if (FAILED(hr))
        {
            goto done;
        }

        if (guidMT == MFMediaType_Audio)
        {
             hr = MFCreateWMAEncoderActivate(pMediaType, pProps, &pEncoderActivate);
            if (FAILED(hr))
            {
                goto done;
            }
            wprintf_s(L"Audio Encoder created. Stream Number: %d .\n", wStreamNumber);

            break;
        }
        if (guidMT == MFMediaType_Video)
        {
            hr = MFCreateWMVEncoderActivate(pMediaType, pProps, &pEncoderActivate);
            if (FAILED(hr))
            {
                goto done;
            }
            wprintf_s(L"Video Encoder created. Stream Number: %d .\n", wStreamNumber);

            break;
        }
    }

    // Set the object pointer.
    hr = pEncNode->SetObject(pEncoderActivate);
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the node to the topology.
    hr = pTopology->AddNode(pEncNode);
    if (FAILED(hr))
    {
        goto done;
    }

    //Add the output node to this node.
    hr = AddOutputNode(pTopology, pSinkActivate, wStreamNumber, &pOutputNode);
    if (FAILED(hr))
    {
        goto done;
    }

    //now we have the output node, connect it to the transform node
    hr = pEncNode->ConnectOutput(0, pOutputNode, 0);
    if (FAILED(hr))
    {
        goto done;
    }

   // Return the pointer to the caller.
    *ppNode = pEncNode;
    (*ppNode)->AddRef();


done:
    SafeRelease(&pEncNode);
    SafeRelease(&pOutputNode);
    SafeRelease(&pEncoderActivate);
    SafeRelease(&pMediaType);
    SafeRelease(&pProps);
    SafeRelease(&pStream);
    SafeRelease(&pProfile);
    SafeRelease(&pContentInfo);
    SafeRelease(&pSink);
    return hr;
}

파일 싱크에 대한 출력 토폴로지 노드 만들기

이 단계에서는 ASF 파일 싱크에 대한 출력 토폴로지 노드를 만듭니다.

이 노드를 만들려면 다음 참조가 필요합니다.

  • 이 자습서의 "ASF 파일 싱크 만들기" 섹션에 설명된 단계에서 만든 활성화 개체에 대한 포인터입니다.
  • 파일 싱크에 추가된 스트림 싱크를 식별하기 위한 스트림 번호입니다. 스트림 번호는 스트림을 만드는 동안 설정된 스트림의 ID와 일치합니다.

출력 노드 및 코드 예제를 만드는 방법에 대한 자세한 내용은 출력 노드 만들기 "활성화 개체에서 출력 노드 만들기"를 참조하세요.

파일 싱크에 활성화 개체를 사용하지 않는 경우 ASF 파일 싱크의 스트림 싱크를 열거하고 각 스트림 싱크를 토폴로지의 출력 노드로 설정해야 합니다. 스트림 싱크 열거에 대한 정보는 "스트림 싱크 열거"를 참조하세요. 자세한 내용은 ASF 파일 싱크 "스트림 정보 추가"에 있습니다.

다음 코드 예제에서는 인코딩 토폴로지에서 싱크 노드를 만들고 추가합니다. 이전에 만든 토폴로지 개체, 파일 싱크의 활성화 개체 및 스트림의 ID 번호에 대한 포인터를 사용합니다. 호출자는 원본 토폴로지 노드에 대한 포인터를 받습니다.

// Add an output node to a topology.
HRESULT AddOutputNode(
    IMFTopology *pTopology,     // Topology.
    IMFActivate *pActivate,     // Media sink activation object.
    DWORD dwId,                 // Identifier of the stream sink.
    IMFTopologyNode **ppNode)   // Receives the node pointer.
{
    IMFTopologyNode *pNode = NULL;

    // Create the node.
    HRESULT hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the object pointer.
    hr = pNode->SetObject(pActivate);
    if (FAILED(hr))
    {
        goto done;
    }

    // Set the stream sink ID attribute.
    hr = pNode->SetUINT32(MF_TOPONODE_STREAMID, dwId);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pNode->SetUINT32(MF_TOPONODE_NOSHUTDOWN_ON_REMOVE, FALSE);
    if (FAILED(hr))
    {
        goto done;
    }

    // Add the node to the topology.
    hr = pTopology->AddNode(pNode);
    if (FAILED(hr))
    {
        goto done;
    }

    // Return the pointer to the caller.
    *ppNode = pNode;
    (*ppNode)->AddRef();

done:
    SafeRelease(&pNode);
    return hr;
}

다음 코드 예제에서는 지정된 미디어 싱크에 대한 스트림 싱크를 열거합니다.

//-------------------------------------------------------------------
//  EnumerateStreamSinks
//  Enumerates the stream sinks within the specified media sink.
//
//  pSink:  A pointer to the media sink.
//-------------------------------------------------------------------
HRESULT EnumerateStreamSinks (IMFMediaSink* pSink)
{
    if (!pSink)
    {
        return E_INVALIDARG;
    }
    
    IMFStreamSink* pStreamSink = NULL;
    DWORD cStreamSinks = 0;

    HRESULT hr = pSink->GetStreamSinkCount(&cStreamSinks);
    if (FAILED(hr))
    {
        goto done;
    }

    for(DWORD index = 0; index < cStreamSinks; index++)
    {
        hr = pSink->GetStreamSinkByIndex (index, &pStreamSink);
        if (FAILED(hr))
        {
            goto done;
        }

        //Use the stream sink
        //Not shown.
    }
done:
    SafeRelease(&pStreamSink);

    return hr;
}

원본, 변환, 그리고 싱크 노드를 연결하십시오.

이 단계에서는 이 자습서의 "필수 인코더 인스턴스화 및 변환 노드 만들기" 섹션에 설명된 단계에서 만든 인코딩 활성화를 참조하는 변환 노드에 원본 노드를 연결합니다. 변환 노드는 파일 싱크에 대한 활성화 개체를 포함하는 출력 노드에 연결됩니다.

인코딩 세션 처리

이 단계에서는 다음 단계를 수행합니다.

  1. MFCreateMediaSession 호출하여 인코딩 세션을 만듭니다.

  2. IMFMediaSession::SetTopology 호출하여 세션에서 인코딩 토폴로지 설정 호출이 완료되면 미디어 세션은 토폴로지 노드를 평가하고 지정된 압축된 소스를 압축되지 않은 샘플로 변환하여 인코더에 대한 입력으로 피드하는 디코더와 같은 추가 변환 개체를 삽입합니다.

  3. IMFMediaSession::GetEvent 호출하여 미디어 세션에서 발생한 이벤트를 요청합니다.

    이벤트 루프에서 미디어 세션에서 발생한 이벤트에 따라 인코딩 세션을 시작하고 닫습니다. IMFMediaSession::SetTopology 호출로 인해 미디어 세션에서 MF_TOPOSTATUS_READY 플래그가 설정된 MESessionTopologyStatus 이벤트가 발생합니다. 모든 이벤트는 비동기적으로 생성되며 애플리케이션은 이러한 이벤트를 동기적으로 또는 비동기적으로 검색할 수 있습니다. 이 자습서의 애플리케이션은 콘솔 애플리케이션이며 사용자 인터페이스 스레드를 차단하는 것은 문제가 되지 않으므로 미디어 세션에서 동기적으로 이벤트를 가져옵니다.

    이벤트를 비동기적으로 가져오기 위해 애플리케이션은 IMFAsyncCallback 인터페이스를 구현해야 합니다. 이 인터페이스의 예제 구현에 대한 자세한 내용은 Media Foundation사용하여 미디어 파일을 재생하는 방법의 "세션 이벤트 처리"를 참조하세요.

미디어 세션 이벤트를 가져오기 위한 이벤트 루프에서, IMFMediaSession::SetTopology가 완료되고 토폴로지가 해결되면 발생하는 MESessionTopologyStatus 이벤트를 기다립니다. MESessionTopologyStatus 이벤트를 가져오면 IMFMediaSession::Start호출하여 인코딩 세션을 시작합니다. 미디어 세션은 모든 인코딩 작업이 완료되면 MEEndOfPresentation 이벤트를 생성합니다. 이 이벤트는 VBR 인코딩에 대해 처리되어야 하며 이 자습서의 다음 섹션 "VBR 인코딩용 파일 싱크의 인코딩 속성 업데이트" 섹션에서 설명합니다.

미디어 세션은 인코딩 세션이 완료되면 ASF 헤더 개체를 생성하고 파일을 최종화한 후 MESessionClosed 이벤트를 발생시킵니다. 이 이벤트는 미디어 세션에서 적절한 종료 작업을 수행하여 처리해야 합니다. 종료 작업을 시작하려면 IMFMediaSession::Shutdown호출합니다. 인코딩 세션이 닫힌 후에는 동일한 미디어 세션 인스턴스에서 인코딩할 다른 토폴로지로 설정할 수 없습니다. 다른 파일을 인코딩하려면 현재 미디어 세션을 닫고 해제해야 하며 새로 만든 미디어 세션에서 새 토폴로지 설정해야 합니다. 다음 코드 예제에서는 미디어 세션을 만들고 인코딩 토폴로지 및 미디어 세션 이벤트를 처리합니다.

다음 코드 예제에서는 미디어 세션을 만들고, 인코딩 토폴로지 및 미디어 세션의 이벤트를 처리하여 인코딩 세션을 제어합니다.

//-------------------------------------------------------------------
//  Encode
//  Controls the encoding session and handles events from the media session.
//
//  pTopology:  A pointer to the encoding topology.
//-------------------------------------------------------------------

HRESULT Encode(IMFTopology *pTopology)
{
    if (!pTopology)
    {
        return E_INVALIDARG;
    }
    
    IMFMediaSession *pSession = NULL;
    IMFMediaEvent* pEvent = NULL;
    IMFTopology* pFullTopology = NULL;
    IUnknown* pTopoUnk = NULL;


    MediaEventType meType = MEUnknown;  // Event type

    HRESULT hr = S_OK;
    HRESULT hrStatus = S_OK;            // Event status
                
    MF_TOPOSTATUS TopoStatus = MF_TOPOSTATUS_INVALID; // Used with MESessionTopologyStatus event.    
        

    hr = MFCreateMediaSession(NULL, &pSession);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pSession->SetTopology(MFSESSION_SETTOPOLOGY_IMMEDIATE, pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

    //Get media session events synchronously
    while (1)
    {
        hr = pSession->GetEvent(0, &pEvent);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEvent->GetType(&meType);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEvent->GetStatus(&hrStatus);
        if (FAILED(hr))
        {
            goto done;
        }
        if (FAILED(hrStatus))
        {
            hr = hrStatus;
            goto done;
        }

       switch(meType)
        {
            case MESessionTopologyStatus:
                {
                    // Get the status code.
                    MF_TOPOSTATUS status = (MF_TOPOSTATUS)MFGetAttributeUINT32(
                                             pEvent, MF_EVENT_TOPOLOGY_STATUS, MF_TOPOSTATUS_INVALID);

                    if (status == MF_TOPOSTATUS_READY)
                    {
                        PROPVARIANT var;
                        PropVariantInit(&var);
                        wprintf_s(L"Topology resolved and set on the media session.\n");

                        hr = pSession->Start(NULL, &var);
                        if (FAILED(hr))
                        {
                            goto done;
                        }

                    }
                    if (status == MF_TOPOSTATUS_STARTED_SOURCE)
                    {
                        wprintf_s(L"Encoding started.\n");
                        break;
                    }
                    if (status == MF_TOPOSTATUS_ENDED)
                    {
                        wprintf_s(L"Encoding complete.\n");
                        hr = pSession->Close();
                        if (FAILED(hr))
                        {
                            goto done;
                        }

                        break;
                    }
                }
                break;


            case MESessionEnded:
                wprintf_s(L"Encoding complete.\n");
                hr = pSession->Close();
                if (FAILED(hr))
                {
                    goto done;
                }
                break;

            case MEEndOfPresentation:
                {
                    if (EncodingMode == VBR)
                    {
                        hr = pSession->GetFullTopology(MFSESSION_GETFULLTOPOLOGY_CURRENT, 0, &pFullTopology);
                        if (FAILED(hr))
                        {
                            goto done;
                        }
                        hr = PostEncodingUpdate(pFullTopology);
                        if (FAILED(hr))
                        {
                            goto done;
                        }
                        wprintf_s(L"Updated sinks for VBR. \n");
                    }
                }
                break;

            case MESessionClosed:
                wprintf_s(L"Encoding session closed.\n");

                hr = pSession->Shutdown();
                goto done;
        }
        if (FAILED(hr))
        {
            goto done;
        }

        SafeRelease(&pEvent);

    }
done:
    SafeRelease(&pEvent);
    SafeRelease(&pSession);
    SafeRelease(&pFullTopology);
    SafeRelease(&pTopoUnk);
    return hr;
}

파일 싱크에서 인코딩 속성 업데이트

인코딩 비트 전송률 및 정확한 새는 버킷 값과 같은 특정 인코딩 속성은 특히 VBR 인코딩의 경우 인코딩이 완료될 때까지 알 수 없습니다. 올바른 값을 얻으려면 애플리케이션은 인코딩 세션이 완료되었음을 나타내는 MEEndOfPresentation 이벤트를 기다려야 합니다. ASF 헤더 개체가 정확한 값을 반영할 수 있도록 싱크에서 새는 버킷 값을 업데이트해야 합니다.

다음 절차에서는 인코딩 토폴로지의 노드를 순회하여 파일 싱크 노드를 얻고 필요한 리키 버킷 속성을 설정하는 데 필요한 단계를 설명합니다.

ASF 파일 싱크 포스트 인코딩 속성 값을 업데이트하려면

  1. IMFTopology::GetOutputNodeCollection 호출하여 인코딩 토폴로지에서 출력 노드 컬렉션을 가져옵니다.
  2. 각 노드에 대해 IMFTopologyNode::GetObject호출하여 노드의 스트림 싱크에 대한 포인터를 가져옵니다. IMFTopologyNode::GetObject가 반환한 IUnknown 포인터에서 IMFStreamSink 인터페이스를 쿼리하십시오.
  3. 각 스트림 싱크에 대해 IMFTopologyNode::GetInput호출하여 다운스트림 노드(인코더)를 가져옵니다.
  4. 노드를 쿼리하여 인코더 노드에서 IMFTransform 포인터를 가져옵니다.
  5. IPropertyStore 포인터에 대한 인코더를 쿼리하여 인코더에서 인코딩 속성 저장소를 가져옵니다.
  6. IPropertyStore 포인터에 대한 스트림 싱크를 쿼리하여 스트림 싱크의 속성 저장소를 가져옵니다.
  7. IPropertyStore::GetValue 호출하여 인코더의 속성 저장소에서 필요한 속성 값을 가져와서 IPropertyStore::SetValue호출하여 스트림 싱크의 속성 저장소에 복사합니다.

다음 표에서는 비디오 스트림의 스트림 싱크에서 설정해야 하는 포스트 인코딩 속성 값을 보여 있습니다.

인코딩 형식 속성 이름(GetValue) 속성 이름(SetValue)
상수 비트 전송률 인코딩 MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
Quality-Based 가변 비트 전송률 인코딩 MFPKEY_BAVG
MFPKEY_RAVG
MFPKEY_BMAX
MFPKEY_RMAX
MFPKEY_STAT_BAVG
MFPKEY_STAT_RAVG
MFPKEY_STAT_BMAX
MFPKEY_STAT_RMAX

 

다음 코드 예제에서는 인코딩 후 속성 값을 설정합니다.

//-------------------------------------------------------------------
//  PostEncodingUpdate
//  Updates the file sink with encoding properties set on the encoder
//  during the encoding session.
    //1. Get the output nodes
    //2. For each node, get the downstream node
    //3. For the downstream node, get the MFT
    //4. Get the property store
    //5. Get the required values
    //6. Set them on the stream sink
//
//  pTopology: A pointer to the full topology retrieved from the media session.
//-------------------------------------------------------------------

HRESULT PostEncodingUpdate(IMFTopology *pTopology)
{
    if (!pTopology)
    {
        return E_INVALIDARG;
    }

    HRESULT hr = S_OK;

    IMFCollection* pOutputColl = NULL;
    IUnknown* pNodeUnk = NULL;
    IMFMediaType* pType = NULL;
    IMFTopologyNode* pNode = NULL;
    IUnknown* pSinkUnk = NULL;
    IMFStreamSink* pStreamSink = NULL;
    IMFTopologyNode* pEncoderNode = NULL;
    IUnknown* pEncoderUnk = NULL;
    IMFTransform* pEncoder = NULL;
    IPropertyStore* pStreamSinkProps = NULL;
    IPropertyStore* pEncoderProps = NULL;

    GUID guidMajorType = GUID_NULL;

    PROPVARIANT var;
    PropVariantInit( &var );

    DWORD cElements = 0;

    hr = pTopology->GetOutputNodeCollection( &pOutputColl);
    if (FAILED(hr))
    {
        goto done;
    }


    hr = pOutputColl->GetElementCount(&cElements);
    if (FAILED(hr))
    {
        goto done;
    }


    for(DWORD index = 0; index < cElements; index++)
    {
        hr = pOutputColl->GetElement(index, &pNodeUnk);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNodeUnk->QueryInterface(IID_IMFTopologyNode, (void**)&pNode);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNode->GetInputPrefType(0, &pType);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pType->GetMajorType( &guidMajorType );
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNode->GetObject(&pSinkUnk);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pSinkUnk->QueryInterface(IID_IMFStreamSink, (void**)&pStreamSink);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pNode->GetInput( 0, &pEncoderNode, NULL );
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEncoderNode->GetObject(&pEncoderUnk);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEncoderUnk->QueryInterface(IID_IMFTransform, (void**)&pEncoder);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pStreamSink->QueryInterface(IID_IPropertyStore, (void**)&pStreamSinkProps);
        if (FAILED(hr))
        {
            goto done;
        }

        hr = pEncoder->QueryInterface(IID_IPropertyStore, (void**)&pEncoderProps);
        if (FAILED(hr))
        {
            goto done;
        }

        if( guidMajorType == MFMediaType_Video )
        {            
            hr = pEncoderProps->GetValue( MFPKEY_BAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BAVG, var );
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_RAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RAVG, var);
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_BMAX, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BMAX, var);
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_RMAX, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RMAX, var);
            if (FAILED(hr))
            {
                goto done;
            }
        }
        else if( guidMajorType == MFMediaType_Audio )
        {      
            hr = pEncoderProps->GetValue( MFPKEY_STAT_BAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BAVG, var );
            if (FAILED(hr))
            {
                goto done;
            }
            
            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_STAT_RAVG, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RAVG, var );
            if (FAILED(hr))
            {
                goto done;
            }
            
            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_STAT_BMAX, &var);
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_BMAX, var);
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_STAT_RMAX, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_STAT_RMAX, var );         
            if (FAILED(hr))
            {
                goto done;
            }

            PropVariantClear( &var );
            hr = pEncoderProps->GetValue( MFPKEY_WMAENC_AVGBYTESPERSEC, &var );
            if (FAILED(hr))
            {
                goto done;
            }
            hr = pStreamSinkProps->SetValue( MFPKEY_WMAENC_AVGBYTESPERSEC, var );         
            if (FAILED(hr))
            {
                goto done;
            }
        } 
        PropVariantClear( &var ); 
   }
done:
    SafeRelease (&pOutputColl);
    SafeRelease (&pNodeUnk);
    SafeRelease (&pType);
    SafeRelease (&pNode);
    SafeRelease (&pSinkUnk);
    SafeRelease (&pStreamSink);
    SafeRelease (&pEncoderNode);
    SafeRelease (&pEncoderUnk);
    SafeRelease (&pEncoder);
    SafeRelease (&pStreamSinkProps);
    SafeRelease (&pEncoderProps);

    return hr;
   
}

메인 구현

다음 코드 예제에서는 콘솔 애플리케이션의 기본 기능을 보여 줍니다.

int wmain(int argc, wchar_t* argv[])
{
    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);

    if (argc != 4)
    {
        wprintf_s(L"Usage: %s inputaudio.mp3, %s output.wm*, %Encoding Type: CBR, VBR\n");
        return 0;
    }


    HRESULT             hr = S_OK;

    IMFMediaSource* pSource = NULL;
    IMFTopology* pTopology = NULL;
    IMFActivate* pFileSinkActivate = NULL;

    hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
    if (FAILED(hr))
    {
        goto done;
    }

    //Set the requested encoding mode
    if (wcscmp(argv[3], L"CBR")==0)
    {
        EncodingMode = CBR;
    }
    else if (wcscmp(argv[3], L"VBR")==0)
    {
        EncodingMode = VBR;
    }
    else
    {
        EncodingMode = CBR;
    }

    // Start up Media Foundation platform.
    hr = MFStartup(MF_VERSION);
    if (FAILED(hr))
    {
        goto done;
    }

    //Create the media source
    hr = CreateMediaSource(argv[1], &pSource);
    if (FAILED(hr))
    {
        goto done;
    }

    //Create the file sink activate
    hr = CreateMediaSink(argv[2], pSource, &pFileSinkActivate);
    if (FAILED(hr))
    {
        goto done;
    }

    //Build the encoding topology.
    hr = BuildPartialTopology(pSource, pFileSinkActivate, &pTopology);
    if (FAILED(hr))
    {
        goto done;
    }

    //Instantiate the media session and start encoding
    hr = Encode(pTopology);
    if (FAILED(hr))
    {
        goto done;
    }


done:
    // Clean up.
    SafeRelease(&pSource);
    SafeRelease(&pTopology);
    SafeRelease(&pFileSinkActivate);

    MFShutdown();
    CoUninitialize();

    if (FAILED(hr))
    {
        wprintf_s(L"Could not create the output file due to 0x%X\n", hr);
    }
    return 0;
}

출력 파일 테스트

다음 목록에서는 인코딩된 파일을 테스트하기 위한 확인 목록을 설명합니다. 이러한 값은 인코딩된 파일을 마우스 오른쪽 단추로 클릭하고 상황에 맞는 메뉴에서 속성 선택하여 표시할 수 있는 파일 속성 대화 상자에서 확인할 수 있습니다.

  • 인코딩된 파일의 경로가 정확합니다.
  • 파일 크기가 0KB를 초과하고 재생 기간이 원본 파일의 기간과 일치합니다.
  • 비디오 스트림의 경우 프레임 너비와 높이, 프레임 속도를 확인합니다. 이러한 값은 "ASF 프로필 개체 만들기" 섹션에 설명된 단계에서 만든 ASF 프로필에서 지정한 값과 일치해야 합니다.
  • 오디오 스트림의 경우 비트 전송률은 대상 미디어 형식에서 지정한 값에 가깝습니다.
  • Windows Media Player에서 파일을 열고 인코딩 품질을 확인합니다.
  • ASFViewer에서 ASF 파일을 열어 ASF 파일의 구조를 확인합니다. 이 도구는 Microsoft 웹 사이트에서 다운로드할 수 있습니다.

일반적인 오류 코드 및 디버깅 팁

다음 목록에서는 수신할 수 있는 일반적인 오류 코드와 디버깅 팁을 설명합니다.

  • IMFSourceResolver::CreateObjectFromURL 호출하면 애플리케이션이 중단됩니다.

    MFStartup호출하여 Media Foundation 플랫폼을 초기화했는지 확인합니다. 이 함수는 IMFSourceResolver::CreateObjectFromURL같이 비동기 작업을 시작하는 모든 메서드에서 사용되는 비동기 플랫폼을 내부적으로 설정합니다.

  • IMFSourceResolver::CreateObjectFromURL는 HRESULT 0x80070002를 반환합니다. "시스템이 지정한 파일을 찾을 수 없습니다."

    첫 번째 인수에서 사용자가 지정한 입력 파일 이름이 있는지 확인합니다.

  • HRESULT는 0x80070020 "프로세스는 다른 프로세스에서 사용 중이므로 파일에 액세스할 수 없습니다. "

    입력 및 출력 파일이 현재 시스템의 다른 리소스에서 사용되고 있지 않은지 확인합니다.

  • IMFTransform 메서드에 대한 호출은 MF_E_INVALIDMEDIATYPE 반환합니다.

    다음 조건이 충족되는지 확인합니다.

    • 입력 형식 또는 지정한 출력 형식은 인코더가 지원하는 미디어 형식과 호환됩니다.
    • 지정한 미디어 유형이 완료되었습니다. 미디어 유형을 완료하려면 이 자습서의 "압축된 오디오 미디어 형식 만들기" 및 "압축된 비디오 미디어 형식 만들기" 섹션의 필수 특성을 참조하세요.
    • 코덱 프라이빗 데이터를 추가할 부분 미디어 형식에서 대상 비트 전송률을 설정했는지 확인합니다.
  • 미디어 세션은 이벤트 상태에서 MF_E_UNSUPPORTED_D3D_TYPE을 반환합니다.

    이 오류는 원본의 미디어 유형이 Windows Media Video 인코더에서 지원되지 않는 혼합 인터레이스 모드를 나타내는 경우 반환됩니다. 압축된 비디오 미디어 유형이 프로그레시브 모드를 사용하도록 설정된 경우 파이프라인은 인터레이스 해제 변환을 사용해야 합니다. 파이프라인이 일치 항목(이 오류 코드로 표시됨)을 찾을 수 없으므로 디코더와 인코더 노드 사이에 인터레이커 해제(코드 변환 비디오 프로세서)를 수동으로 삽입해야 합니다.

  • 미디어 세션은 이벤트 상태의 E_INVALIDARG 반환합니다.

    이 오류는 원본의 미디어 형식 특성이 Windows Media 인코더에 설정된 출력 미디어 형식의 특성과 호환되지 않을 때 반환됩니다.

  • IWMCodecPrivateData::GetPrivateData "쿼리 문자열을 평가하려고 구문 오류가 발생했습니다"0x80040203 HRESULT를 반환합니다.

    입력 형식이 인코더 MFT에 설정되어 있는지 확인합니다.

파이프라인 계층 ASF 구성 요소