音频转码 via Media Foundation

转码(transcoding)其实就是把音频从一种编码转换成另一种编码的过程,如 MP3 → WMA。基本流程如下图:
transcoding

Media Foundation 简介

Media Foundation (简称 MF)是微软在 Windows Vista上 推出的新一代多媒体应用库,目的是提供 Windows 平台一个统一的多媒体影音解决方案,开发者可以通过 MF 播放视频或声音文件、进行多媒体文件格式转码,或者将一连串图片编码为视频等等。

MF 是 DirectShow 为主的旧式多媒体应用程序接口的替代者与继承者,在微软的计划下将逐步汰换 DirectShow 技术。MF 要求 Windows Vista 或更高版本,不支持较早期的 Windows 版本,特别是 Windows XP。

MF 长于高质量的音频和视频播放,高清内容(如 HDTV,高清电视)和数字版权管理(DRM)访问控制。MF 在不同的 Windows 版本上能力不同,如 Windows 7 上就添加了 h.264 编码支持。Windows 8 上则提供数种更高质量的设置。

MF 提供了两种编程模型,第一种是以 Media Session 为主的 Media pipeline 模型,但是该模型太过复杂,且曝露过多底层细节,故微软于 Windows 7 上推出第二种编程模型,内含 SourceReaderTranscode APISinkWriterMFPlay 等高度封装模块,大大简化了 MF 的使用难度。

# 本文使用了第二种(简单的)编程模型。

Media Foundation 转码音频

Transcoding 流程图

mf transcoding

Transcoding 代码

以下是整个 MF 转码过程的概要代码,略去 TopoBuilder 类的实现:

HRESULT CTranscodeApi::transcodeFile(PCWSTR pszInput, PCWSTR pszOutput)
{
    HRESULT hr = S_OK;
    CComPtr<IMFTopology> pTopology; 
    
    hr = MFCreateMediaSession(NULL, &m_pSession);
    RETURN_IF_FAILED(hr);
    
    hr = m_pSession->BeginGetEvent((IMFAsyncCallback*)this, NULL);
    RETURN_IF_FAILED(hr);
    
    hr = _setTranscodeProfile(pszOutput);
    RETURN_IF_FAILED(hr);
    
    hr = m_topoBuilder.createTranscodeTopology(pszInput, pszOutput);
    RETURN_IF_FAILED(hr);
    
    pTopology = m_topoBuilder.getTopology();
    hr = m_pSession->SetTopology(0, pTopology);
    if (SUCCEEDED(hr)) {
        PROPVARIANT varStart;
        PropVariantClear(&varStart);
        hr = m_pSession->Start(&GUID_NULL, &varStart);
    }
    
    return hr;
}

setTranscodeProfile 函数

Windows7以上系统默认支持 AAC 和 WMA encoder。

HRESULT CTranscodeApi::_setTranscodeProfile(LPCTSTR pszOutputFile)
{
    HRESULT hr = E_NOTIMPL;

    LPCTSTR ext = pszOutputFile + _tcslen(pszOutputFile) - 3;
    if (_tcsicmp(ext, _T("aac")) == 0) {
        hr = m_topoBuilder.SetTranscodeProfile(
            MFAudioFormat_AAC,           
            GUID_NULL,               
            MFTranscodeContainerType_MPEG4);     
    }
    else if (_tcsicmp(ext, _T("wma")) == 0) {
        hr = m_topoBuilder.SetTranscodeProfile(
            MFAudioFormat_WMAudioV9,           
            GUID_NULL,                
            MFTranscodeContainerType_ASF);     
    }

    return hr;
}

HRESULT CTranscodeTopoBuilder::setTranscodeProfile(
    const GUID& audioFormat,    // target audio format
    const GUID& videoFormat,    // target video format
    const GUID& containerType)  // target file container 
{
    HRESULT hr = S_OK;
    hr = MFCreateTranscodeProfile(&m_pTranscodeProfile);
    RETURN_IF_FAILED(hr);
        
    hr = _setAudioAttributes(audioFormat);
    RETURN_IF_FAILED(hr);

    hr = _setVideoAttributes(videoFormat);
    RETURN_IF_FAILED(hr);

    hr = _setContainerAttributes(containerType);
    RETURN_IF_FAILED(hr);

    return hr;
}
CTranscodeTopoBuilder::_setAudioAttributes 函数

枚举所有支持的输出媒体类型并选择第一个。

HRESULT CTranscodeTopoBuilder::_setAudioAttributes(const GUID& audioFormat)
{
    HRESULT hr = S_OK;
    CComPtr<IMFCollection> pAudioTypeCollection;
    CComPtr<IMFAttributes> pAudioAttrs;
      
    // enumerate all of the audio encoders that match the specified
    // parameters and find all audio types that can be generated. 
    DWORD dwFlags = (MFT_ENUM_FLAG_ALL & (~MFT_ENUM_FLAG_FIELDOFUSE))   
        | MFT_ENUM_FLAG_SORTANDFILTER;  
    hr = MFTranscodeGetAudioOutputAvailableTypes(
        audioFormat,                      // specify the requested audio format
        dwFlags,                          // get all MFTs except for the FOU, and sort
        NULL,                             // no custom attributes
        &pAudioTypeCollection);           // store result in specified collection
    RETURN_IF_FAILED(hr);
    
    // get the first element from the collection of media types, 
    // copy all the information of the first type into a new attribute
    // collection, and return the attribute collection
    hr = getTypeAttributesFromTypeCollection(pAudioTypeCollection, 0, pAudioAttrs);
    RETURN_IF_FAILED(hr);

    hr = m_pTranscodeProfile->SetAudioAttributes(pAudioAttrs);
    RETURN_IF_FAILED(hr);

    return hr;
}
CTranscodeTopoBuilder::_setContainerAttributes 函数

设置媒体容器的类型,如 MP3,AVI 等。

HRESULT CTranscodeTopoBuilder::_setContainerAttributes(const GUID& containerType)
{
    HRESULT hr = S_OK;
    CComPtr<IMFAttributes> pContainerAttributes;

    RETURN_IF_NULL(m_pTranscodeProfile);

    hr = MFCreateAttributes(&pContainerAttributes, 1);
    RETURN_IF_FAILED(hr);

    // store an attribute that indicates that we want to write to an ASF file
    hr = pContainerAttributes->SetGUID(
        MF_TRANSCODE_CONTAINERTYPE,         // attribute ID GUID - container type
        containerType);                     // generate the specified container
    RETURN_IF_FAILED(hr);

    // store the container attributes in the transcode profile
    hr = m_pTranscodeProfile->SetContainerAttributes(pContainerAttributes);
    RETURN_IF_FAILED(hr);

    return hr;
}

CTranscodeTopoBuilder::createTranscodeTopology 函数

MF 提供了 transcoding 专用的 topology。

HRESULT CTranscodeTopoBuilder::createTranscodeTopology(PCWSTR pszInput, PCWSTR pszOutput)
{
    HRESULT hr = S_OK;
    
    // standard media source creation
    hr = createMediaSource(pszInput, m_pSource);
    RETURN_IF_FAILED(hr);

    // create the actual transcode topology based on the transcode profile
    hr = MFCreateTranscodeTopology(
        m_pSource,                      // the source of the content to transcode
        pszOutput,                      // output filename
        m_pTranscodeProfile,            // transcode profile to use
        &m_pTopology);                  // resulting topology
    RETURN_IF_FAILED(hr);

    return hr;
}

IMFAsyncCallback::Invoke 函数

Media Session 的 所有事件 都会回调这里。当收到 MESessionEnded 事件时就说明转码结束了。
注意:转码未完时要重新 BeginGetEvent,不然就傻傻等不到下一个事件了。

HRESULT CTranscodeApi::Invoke(IMFAsyncResult* pAsyncResult)
{
    CComPtr<IMFMediaEvent> pEvent;
    HRESULT hr = S_OK;
    MediaEventType eventType;
    
    // Get the event from the event queue.
    hr = m_pSession->EndGetEvent(pAsyncResult, &pEvent);
    RETURN_IF_FAILED(hr);

    // Get the event type.
    hr = pEvent->GetType(&eventType);
    RETURN_IF_FAILED(hr);

    if (eventType == MESessionEnded) {
        hr = m_pSession->Close();
    }
    else if (eventType == MESessionClosed) {
        SetEvent(m_closeCompleteEvent);
    }
    else {
        hr = m_pSession->BeginGetEvent(this, NULL);
    }
    RETURN_IF_FAILED(hr);
    
    return S_OK;
}

其他框架的转码

  • 关于 FFmpeg 的音频转码请参考 这里
  • 关于 DirectShow 的音频转码请参考 这里

Blueware
EOF

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值