编码器示例代码

本主题演示一些示例代码,这些代码将 Windows Media Audio (WMA) 编码器包装在名为 的 CWmaEncoderC++ 类中。

以下几个主题引用了此代码:

在媒体基础中,编码器实现为 媒体基础转换 (MRT) 并公开 IMFTransform 接口。

类声明

enum EncodeMode
{
    EncodeMode_CBR,
    EncodeMode_VBR_Quality,
    EncodeMode_VBR_Peak,
    EncodeMode_VBR_Unconstrained,
};

struct LeakyBucket
{
    DWORD dwBitrate;
    DWORD msBufferSize;
    DWORD msInitialBufferFullness;
};

class CWmaEncoder 
{
public:

    CWmaEncoder() 
        : m_pMFT(NULL), m_dwInputID(0), m_dwOutputID(0), m_pOutputType(NULL)
    {
    }

    ~CWmaEncoder()
    {
        SafeRelease(&m_pMFT);
        SafeRelease(&m_pOutputType);
    }

    HRESULT Initialize();
    HRESULT SetEncodingType(EncodeMode mode);
    HRESULT SetInputType(IMFMediaType* pMediaType);
    HRESULT GetOutputType(IMFMediaType** ppMediaType);
    HRESULT GetLeakyBucket1(LeakyBucket *pBucket);
    HRESULT ProcessInput(IMFSample* pSample);
    HRESULT ProcessOutput(IMFSample **ppSample);
    HRESULT Drain();
       
protected:
    DWORD           m_dwInputID;     // Input stream ID.
    DWORD           m_dwOutputID;    // Output stream ID.

    IMFTransform    *m_pMFT;         // Pointer to the encoder MFT.
    IMFMediaType    *m_pOutputType;  // Output media type of the encoder.

};

Initialize

方法 Initialize 创建 WMA 编码器的实例。

HRESULT CWmaEncoder::Initialize() 
{
    CLSID *pCLSIDs = NULL;   // Pointer to an array of CLISDs. 
    UINT32 count = 0;      // Size of the array.

    IMFMediaType* pOutMediaType = NULL;
    
    // Look for a encoder.
    MFT_REGISTER_TYPE_INFO toutinfo;
    toutinfo.guidMajorType = MFMediaType_Audio;
    toutinfo.guidSubtype = MFAudioFormat_WMAudioV9;

    HRESULT hr = S_OK;

    hr = MFTEnum(
        MFT_CATEGORY_AUDIO_ENCODER,
        0,                  // Reserved
        NULL,               // Input type to match. 
        &toutinfo,          // Output type to match.
        NULL,               // Attributes to match. (None.)
        &pCLSIDs,           // Receives a pointer to an array of CLSIDs.
        &count              // Receives the size of the array.
        );
    
    if (SUCCEEDED(hr))
    {
        if (count == 0)
        {
            hr = MF_E_TOPO_CODEC_NOT_FOUND;
        }
    }

    //Create the MFT decoder
    if (SUCCEEDED(hr))
    {
        hr = CoCreateInstance(pCLSIDs[0], NULL, 
            CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pMFT));
    }

    return hr;
}

SetEncodingType

HRESULT CWmaEncoder::SetEncodingType(EncodeMode mode)
{
    if (!m_pMFT)
    {
        return MF_E_NOT_INITIALIZED;
    }

    IPropertyStore* pProp = NULL;

    PROPVARIANT var;

    //Query the encoder for its property store

    HRESULT hr = m_pMFT->QueryInterface(IID_PPV_ARGS(&pProp));
    if (FAILED(hr))
    {
        goto done;
    }

    switch (mode)
    {
    case EncodeMode_CBR:
        //Set the VBR property to FALSE, which indicates CBR encoding
        //By default, encoding mode is set to CBR
        var.vt = VT_BOOL;
        var.boolVal = FALSE;
        hr = pProp->SetValue(MFPKEY_VBRENABLED, var);
        break;


    default:
        hr = E_NOTIMPL;
    }
    
done:
    SafeRelease(&pProp);
    return hr;
}

SetInputType

HRESULT CWmaEncoder::SetInputType(IMFMediaType* pMediaType)
{
    if (!m_pMFT)
    {
        return MF_E_NOT_INITIALIZED;
    }

    SafeRelease(&m_pOutputType);

    IMFMediaType *pOutputType = NULL;

    HRESULT hr = m_pMFT->GetStreamIDs(1, &m_dwInputID, 1, &m_dwOutputID);

    if (hr == E_NOTIMPL)
    {
        // The stream identifiers are zero-based.
        m_dwInputID = 0;
        m_dwOutputID = 0;
        hr = S_OK;
    }
    else if (FAILED(hr))
    {
        goto done;
    }

    // Set the input type to the one passed by the application
    hr = m_pMFT->SetInputType(m_dwInputID, pMediaType, 0);
    if (FAILED(hr))
    {
        goto done;
    }

    // Loop through the available output types
    for (DWORD iType = 0; ; iType++)
    {
        hr = m_pMFT->GetOutputAvailableType(m_dwOutputID, iType, &pOutputType);
        if (FAILED(hr))
        {
            goto done;
        }
        
        hr = m_pMFT->SetOutputType(m_dwOutputID, pOutputType, 0);

        if (SUCCEEDED(hr))
        {
            m_pOutputType = pOutputType;
            m_pOutputType->AddRef();
            break;
        }

        SafeRelease(&pOutputType);
    }

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

GetOutputType

HRESULT CWmaEncoder::GetOutputType(IMFMediaType** ppMediaType)
{
    if (m_pOutputType == NULL)
    {
        return MF_E_TRANSFORM_TYPE_NOT_SET;
    }
        
    *ppMediaType = m_pOutputType;
    (*ppMediaType)->AddRef();

    return S_OK;
};

GetLeakyBucket1

HRESULT CWmaEncoder::GetLeakyBucket1(LeakyBucket *pBucket)
{
    if (m_pMFT == NULL || m_pOutputType == NULL)
    {
        return MF_E_NOT_INITIALIZED;
    }

    ZeroMemory(pBucket, sizeof(*pBucket));

    // Get the bit rate.

    pBucket->dwBitrate = 8 * MFGetAttributeUINT32(
        m_pOutputType, MF_MT_AUDIO_AVG_BYTES_PER_SECOND, 0);


    // Get the buffer window.

    IWMCodecLeakyBucket *pLeakyBuckets = NULL;

    HRESULT hr = m_pMFT->QueryInterface(IID_PPV_ARGS(&pLeakyBuckets));
    if (SUCCEEDED(hr))
    {
        ULONG ulBuffer = 0;

        hr = pLeakyBuckets->GetBufferSizeBits(&ulBuffer);

        if (SUCCEEDED(hr))
        {
            pBucket->msBufferSize = ulBuffer / (pBucket->dwBitrate / 1000);    
        }

        pLeakyBuckets->Release();
    }

    return S_OK;
}

ProcessInput

HRESULT CWmaEncoder::ProcessInput(IMFSample* pSample)
{
    if (m_pMFT == NULL)
    {
        return MF_E_NOT_INITIALIZED;
    }

    return m_pMFT->ProcessInput(m_dwInputID, pSample, 0);
}

ProcessOutput

HRESULT CWmaEncoder::ProcessOutput(IMFSample **ppSample)
{
    if (m_pMFT == NULL)
    {
        return MF_E_NOT_INITIALIZED;
    }

    *ppSample = NULL;

    IMFMediaBuffer* pBufferOut = NULL;
    IMFSample* pSampleOut = NULL;

    DWORD dwStatus;
  
    MFT_OUTPUT_STREAM_INFO mftStreamInfo = { 0 };
    MFT_OUTPUT_DATA_BUFFER mftOutputData = { 0 };

    HRESULT hr = m_pMFT->GetOutputStreamInfo(m_dwOutputID, &mftStreamInfo);
    if (FAILED(hr))
    {
        goto done;
    }

    //create a buffer for the output sample
    hr = MFCreateMemoryBuffer(mftStreamInfo.cbSize, &pBufferOut);
    if (FAILED(hr))
    {
        goto done;
    }

    //Create the output sample
    hr = MFCreateSample(&pSampleOut);
    if (FAILED(hr))
    {
        goto done;
    }

    //Add the output buffer 
    hr = pSampleOut->AddBuffer(pBufferOut);
    if (FAILED(hr))
    {
        goto done;
    }
 
    //Set the output sample
    mftOutputData.pSample = pSampleOut;

    //Set the output id
    mftOutputData.dwStreamID = m_dwOutputID;

    //Generate the output sample
    hr = m_pMFT->ProcessOutput(0, 1, &mftOutputData, &dwStatus);
    if (hr == MF_E_TRANSFORM_NEED_MORE_INPUT)
    {
        hr = S_OK;
        goto done;
    }

    // TODO: Handle MF_E_TRANSFORM_STREAM_CHANGE

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

    *ppSample = pSampleOut;
    (*ppSample)->AddRef();

done:
    SafeRelease(&pBufferOut);
    SafeRelease(&pSampleOut);
    return hr;
};

清空

HRESULT CWmaEncoder::Drain()
{
    if (m_pMFT == NULL)
    {
        return MF_E_NOT_INITIALIZED;
    }

    return m_pMFT->ProcessMessage(MFT_MESSAGE_COMMAND_DRAIN, m_dwInputID);
}

教程:使用 CBR 编码编写 WMA 文件

Windows Media 编解码器