튜토리얼: 단일 패스 Windows 미디어 인코딩

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

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

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

이 튜토리얼에선 입력 및 출력 파일 이름과 인코딩 모드를 인수로 사용하는 콘솔 애플리케이션을 만들게 됩니다. 입력 파일은 압축되었거나 또는 압축되지 않은 미디어 포멧일 수 있습니다. 유효한 인코딩 모드는 "CBR"(고정 비트 전송률) 또는 "VBR"(가변 비트 전송률)입니다. 애플리케이션은 입력 파일 이름으로 지정된 소스를 나타내는 미디어 소스를 만들고, ASF 파일 싱크를 만들어 소스 파일 인코딩 내용을 ASF 파일에 보관합니다. 시나리오를 간단하게 진행하기 위해 출력 파일에는 오직 오디오 스트림 하나와 비디오 스트림 하나만 있습니다. 애플리케이션은 오디오 스트림 포멧 변환을 위한 Windows 미디어 오디오 9.1 Professional 코덱과 비디오 스트림을 위해 Windows 미디어 비디오 9 코덱을 삽입합니다.

고정 비트 전송률 인코딩의 경우 인코딩 세션이 시작되기 전에 인코더는 달성해야 하는 목표 비트 속도를 인지하고 있어야 합니다. 이 튜토리얼에서 "CBR" 모드의 경우 애플리케이션은 미디어 형식 검토 중 인코더에서 추출된 첫 번째 출력 미디어 포멧에 사용할 수 있는 비트 전송률을 목표 비트 전송률로 사용합니다. 가변 비트 전송률 인코딩의 경우 이 튜토리얼 에서는 품질 수준을 설정하여 가변 비트 전송률로 인코딩하는 방법을 보여 줍니다. 오디오 스트림은 품질 수준 98로 인코딩되고 비디오 스트림은 95로 인코딩됩니다.

다음 절차는 단일 패스 인코딩 모드를 사용하여 ASF 컨테이너에서 Windows 미디어 콘텐츠를 인코딩하는 단계가 요약되어 있습니다.

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

이 튜토리얼에는 다음과 같은 섹션이 있습니다.

필수 조건

이 튜토리얼은 다음 사항들을 가정합니다.

Project 설정

  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. 세이프릴리즈 함수를 공표합니다.

    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;
    
    

미디어 소스 생성

입력 소스에 대한 미디어 소스를 만듭니다. 미디어 파운데이션은 MP3, MP4/3GP, AVI/WAVE 등 다양한 미디어 포멧에 대해 내장된 미디어 소스를 제공합니다. 미디어 파운데이션에서 지원하는 포멧에 대한 자세한 내용은 미디어 파운데이션에서 지원되는 미디어 포멧을 참조 하세요.

미디어 소스를 생성하려면 소스 리졸버를 사용하세요. 이 개체는 소스 파일에 지정된 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 프로필 개체에 구성되고 인코딩 및 전역 속성은 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 미디어 오디오 인코더와 호환되는지 확인하려면 인코더 MFT를 인스턴스화 하고, 인코딩 속성을 설정한 후, IMFTransform::GetOutputAvailableType을 호출하여 미디어 형식을 가져옵니다. 사용 가능한 모든 형식을 반복하여, 각 미디어 형식의 특성을 가져오고, 요구 사항에 가장 가까운 형식을 선택하여 필요한 출력 형식을 가져옵니다. 이 튜토리얼에서는 인코더에서 지원하는 출력 미디어 형식 목록에서 사용 가능한 첫 번째 형식을 가져옵니다.

참고 Windows 7의 미디어 파운데이션은 호환되는 오디오 유형의 목록을 추출하는 새 함수인 MFTranscodeGetAudioOutputAvailableTypes 를 제공합니다. 이 함수는 CBR 인코딩에 대한 미디어 형식만 가져옵니다.

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

다음 예제 코드는 Windows 미디어 오디오 인코더에서 호환되는 형식을 얻어 압축된 오디오 형식을 생성합니다. 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 미디어 비디오 인코더에서 호환되는 미디어 형식을 가져옵니다. 이 메서드는 부분 형식을 출력합니다. 다음 정보를 추가하여 부분 형식을 전체 형식으로 변환하세요.

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

    IWMCodecPrivateData::GetPrivateData는 코덱 프라이빗 데이터를 출력하기 전에 비트 전송률을 검사하기 때문에 코덱 프라이빗 데이터를 가져오기 전, 비트 전송률을 설정해야 합니다.

    다음 예제 코드는 Windows 미디어 비디오 인코더에서 호환되는 형식을 얻어 압축된 비디오 형식을 생성합니다. 또한 압축되지 않은 비디오 형식도 만들고 이를 인코더의 입력으로 설정합니다. 이는 응용 함수 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 로 설정합니다. 이 속성에 대한 자세한 내용은 멀티플렉서 개체 만들기에서 "멀티플렉서 초기화 및 리키버킷 설정"을 참조하세요.

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

        //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 미디어 인코더 및 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;
}

요구된 인코더 인스턴스화 및 변환 노드 생성

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

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

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

  1. 파일 싱크 활성화 작업에서 IMFActivate::ActivateObject 호출 후 QueryInterface를 호출하여 IMFASFContentInfo을 쿼리하여 싱크의 ContentInfo 개체의 참조를 얻습니다.

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

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

    다음 메서드를 호출하세요.

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

    다음 메서드를 호출하세요.

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

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

  6. 오디오 스트림의 리키버킷 매개 변수를 업데이트합니다.

    MFCreateWMAEncoderActivate 는 Windows 미디어 오디오 코덱의 기본 인코더 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 인터페이스를 구현해야 합니다. 이 인터페이스의 구현 예제 및 자세한 내용은 미디어 파운데이션을 사용하여 미디어 파일을 재생하는 방법의 "세션 이벤트 처리"를 참조하세요.

미디어 세션 이벤트를 얻기 위한 이벤트 루프에서 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
품질 기반 가변 비트 전송률 인코딩 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 미디어 플레이어에서 열고 인코딩 품질을 확인하세요.
  • ASFViewer에서 ASF 파일을 열고 ASF 파일의 구조를 확인합니다. 이 도구는 이 Microsoft 웹 사이트에서 다운로드할 수 있습니다.

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

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

  • IMFSourceResolver::CreateObjectFromURL 호출은 애플리케이션을 중지시킵니다.

    MFStartup을 호출하여 미디어 파운데이션이 초기화 됬는지 반드시 확인하세요. 이 함수는 내부적으로 IMFSourceResolver::CreateObjectFromURL과 같은 비동기 작업을 시작하는 모든 메서드에 사용되는 비동기 플랫폼을 설정합니다.

  • IMFSourceResolver::CreateObjectFromURL 은 HRESULT 0x80070002를 출력합니다. "시스템에서 지정된 파일을 찾을 수 없습니다.

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

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

    입력 및 출력 파일이 시스템의 다른 리소스에서 사용중인지 반드시 확인하세요.

  • IMFTransform 메서드 호출은 MF_E_INVALIDMEDIATYPE을 출력합니다.

    다음 상황이 맞는지 확인합니다.

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

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

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

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

  • IWMCodecPrivateData::GetPrivateData 는 HRESULT 0x80040203을 출력합니다. "쿼리 스트링을 심사하려는 중 문법 오류가 발생했습니다."

    인코더 MFT에 입력 형식이 설정되어 있는지 반드시 확인하세요.

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