配置MPEG4MediaSink

时间:2018-08-26 10:56:05

标签: c++ windows ms-media-foundation

我正在尝试构建一个简单的演示WMF应用程序,该应用程序使用Media Source,Pipeline和Media Sink复制MP4文件。这是基本步骤

  1. 创建媒体源,并从中获取演示文稿描述符
  2. 从以下网址获取第一个活动的视频和音频流描述符:     演示描述符
  3. 从每个流中获取当前的视频和音频IMFMediaTypes 描述符。检查表明视频媒体子类型为H264 音频媒体子类型为AAC
  4. 使用两种媒体MFCreateMPEG4MediaSink打开媒体接收器 类型和输出文件名。
  5. 使用流和设置视频和音频源节点 演示描述符
  6. 使用媒体接收器设置视频和音频接收器节点。
  7. 将所有节点添加到拓扑
  8. 将源节点连接到宿节点
  9. 构建拓扑并运行。

我在Media Session回叫中遇到的错误是MF_E_INVALIDMEDIATYPE。如果我注释掉音频流,则将NULL输入音频类型的MFCreateMPEG4MediaSink调用,并且不为其设置任何节点,则复制效果很好–我可以播放生成的仅包含视频的mp4文件。

MPEG4MediaSink应该能够处理AAC音频。但是我怀疑媒体类型中有些东西是不喜欢的(记住它是直接从源流派生而没有更改的。)

对于使音频流正常运行可能需要做的任何见解或建议,我将不胜感激。

我已经使用MFCreateMP3MediaSink成功地对MP3文件进行了类似的复制-但在这种情况下,媒体类型为MP3。

3 个答案:

答案 0 :(得分:0)

从Mediafoundation示例中,您具有LogMediaType函数LogMediaType

能否向我们显示AAC音频的MediaType日志。

通常,这是AAC所需的一些属性(某些值可能会有所不同):

  • MF_MT_MAJOR_TYPE:MFMediaType_Audio
  • MF_MT_SUBTYPE:MFAudioFormat_AAC或MEDIASUBTYPE_RAW_AAC1
  • MF_MT_AUDIO_PREFER_WAVEFORMATEX:是
  • MF_MT_AAC_PAYLOAD_TYPE:0
  • MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION:0
  • MF_MT_AUDIO_NUM_CHANNELS:6
  • MF_MT_AUDIO_SAMPLES_PER_SECOND:48000
  • MF_MT_AUDIO_BLOCK_ALIGNMENT:24
  • MF_MT_AUDIO_AVG_BYTES_PER_SECOND:1152000
  • MF_MT_AUDIO_BITS_PER_SAMPLE:32
  • MF_MT_AUDIO_CHANNEL_MASK:63
  • MF_MT_USER_DATA:字节数组== AudioSpecificConfig(取决于MFAudioFormat_AAC或MEDIASUBTYPE_RAW_AAC1)

在SourceResolver中,您还可以看到那些属性:

  • MF_MT_MPEG4_SAMPLE_DESCRIPTION:字节数组
  • MF_MT_MPEG4_CURRENT_SAMPLE_ENTRY:0
  • MF_MT_AVG_BITRATE:x
  • MF_MT_AM_FORMAT_TYPE:clsid
  • MF_MT_ALL_SAMPLES_INDEPENDENT:1
  • MF_MT_FIXED_SIZE_SAMPLES:1
  • MF_MT_SAMPLE_SIZE:1

使用Microsoft提供的对象时,MF_MT_USER_DATA非常重要。如果缺少某些属性,请尝试手动添加它们。

EDIT1

是否将音频源节点连接到正确的IMFStreamSink。您是否使用了IMFMediaSink的正确索引?

答案 1 :(得分:0)

谢谢大家的帮助!!在这种情况下,问题是在创建输出节点时使用了硬编码流索引0。为了我的辩护,各种WMF样本似乎仅显示每个接收器一个流,因此使用的硬编码流索引为0。我将代码建立在这些示例的基础上,并将该部分分解为实用程序库,并立即忘记了它是特殊情况。当然,MP4接收器使用两个流,并且在解析拓扑时,音频中断使用流索引0的用法。

为了记录下来(以为可能对其他人有用),这里是我的音频媒体类型的设置。一旦我对streamink问题进行了排序,这种媒体类型配置就可以正常工作。这些问题被分解为与上面示例音频媒体类型内容相同,不同,多余和缺失的项目。

相同

MF_MT_MAJOR_TYPE=MFMediaType_Audio (73647561-0000-0010-8000-00aa00389b71)
MF_MT_SUBTYPE=AAC (00001610-0000-0010-8000-00aa00389b71)
MF_MT_AUDIO_PREFER_WAVEFORMATEX=1
MF_MT_AAC_PAYLOAD_TYPE=0
MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION=0
MF_MT_AUDIO_NUM_CHANNELS=6
MF_MT_AUDIO_SAMPLES_PER_SECOND=48000

不同

MF_MT_AUDIO_BLOCK_ALIGNMENT=1
MF_MT_AUDIO_AVG_BYTES_PER_SECOND=47995
MF_MT_AUDIO_BITS_PER_SAMPLE=16

未知

MF_MT_USER_DATA=00,00,00,00,00,00,00,00,00,00,00,00,11,b0

额外

MF_MT_AVG_BITRATE=383960
MF_MT_MPEG4_SAMPLE_DESCRIPTION=00,00,00,67,73,74,73,64,00,00,00,00,00,00,00,01...
MF_MT_AM_FORMAT_TYPE=Unknown (05589f81-c356-11ce-bf01-00aa0055595a)
MF_MT_MPEG4_CURRENT_SAMPLE_ENTRY=0
MF_MT_FIXED_SIZE_SAMPLES=1
MF_MT_ALL_SAMPLES_INDEPENDENT=1
MF_MT_SAMPLE_SIZE=1

遗失

MF_MT_AUDIO_CHANNEL_MASK : 63

答案 2 :(得分:0)

为了好玩,我快速重写了您的程序:

//----------------------------------------------------------------------------------------------
// Main.cpp
//----------------------------------------------------------------------------------------------
#pragma once
#define WIN32_LEAN_AND_MEAN
#define STRICT

#pragma comment(lib, "mf")
#pragma comment(lib, "mfplat")
#pragma comment(lib, "mfuuid")

#include <WinSDKVer.h>
#include <new>
#include <windows.h>

#include <mfapi.h>
#include <mfidl.h>
#include <wchar.h>

#define MP4_SOURCE_VIDEO_MEDIA_FILE L"big_buck_bunny_720p_50mb.mp4"
#define MP4_FINAL_VIDEO_MEDIA_FILE L"final.mp4"

HRESULT ProcessConverter(LPCWSTR);
HRESULT ConfigureSource(LPCWSTR, IMFMediaSource**, IMFMediaType**, IMFMediaType**, IMFTopologyNode**, IMFTopologyNode**);
HRESULT CreateMediaSource(LPCWSTR, IMFMediaSource**);
HRESULT ConfigureMediaTypeSource(IMFMediaSource*, IMFPresentationDescriptor*, IMFStreamDescriptor*, IMFMediaType**, IMFMediaType**, IMFTopologyNode**, IMFTopologyNode**);
HRESULT CreateTopologyNodeSink(IMFMediaSink*, IMFTopologyNode**, IMFTopologyNode**, IMFMediaType*, IMFMediaType*);
HRESULT CreateSourceStreamNode(IMFMediaSource*, IMFPresentationDescriptor*, IMFStreamDescriptor*, IMFTopologyNode**);
HRESULT ConfigureSinkNode(IMFMediaTypeHandler*, IMFStreamSink*, IMFTopologyNode**, IMFMediaType*);
HRESULT ConfigureTopologyNode(IMFTopology*, IMFTopologyNode*, IMFTopologyNode*, IMFTopologyNode*, IMFTopologyNode*);
HRESULT RunMediaSession(IMFMediaSession*);

template <class T> inline void SAFE_RELEASE(T*& p){

    if(p){
        p->Release();
        p = NULL;
    }
}

void main() {

    HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);

    if(SUCCEEDED(hr)) {

        hr = MFStartup(MF_VERSION, MFSTARTUP_LITE);

        if(SUCCEEDED(hr)) {

            hr = ProcessConverter(MP4_SOURCE_VIDEO_MEDIA_FILE);

            hr = MFShutdown();
        }

        CoUninitialize();
    }
}

HRESULT ProcessConverter(LPCWSTR wszVideoFile){

    HRESULT hr = S_OK;
    IMFMediaSource* pSource = NULL;
    IMFMediaType* pVideoMediaType = NULL;
    IMFMediaType* pAudioMediaType = NULL;
    IMFByteStream* pByteStream = NULL;
    IMFMediaSink* pMediaSink = NULL;
    IMFTopology* pTopology = NULL;
    IMFTopologyNode* pVideoSourceNode = NULL;
    IMFTopologyNode* pAudioSourceNode = NULL;
    IMFTopologyNode* pVideoSinkNode = NULL;
    IMFTopologyNode* pAudioSinkNode = NULL;
    IMFMediaSession* pSession = NULL;

    hr = MFCreateFile(MF_ACCESSMODE_WRITE, MF_OPENMODE_DELETE_IF_EXIST, MF_FILEFLAGS_NONE, MP4_FINAL_VIDEO_MEDIA_FILE, &pByteStream);

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

    hr = ConfigureSource(wszVideoFile, &pSource, &pVideoMediaType, &pAudioMediaType, &pVideoSourceNode, &pAudioSourceNode);

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

    hr = MFCreateMPEG4MediaSink(pByteStream, pVideoMediaType, pAudioMediaType, &pMediaSink);

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

    hr = CreateTopologyNodeSink(pMediaSink, &pVideoSinkNode, &pAudioSinkNode, pVideoMediaType, pAudioMediaType);

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

    hr = MFCreateTopology(&pTopology);

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

    hr = ConfigureTopologyNode(pTopology, pVideoSourceNode, pAudioSourceNode, pVideoSinkNode, pAudioSinkNode);

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

    hr = MFCreateMediaSession(NULL, &pSession);

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

    hr = pSession->SetTopology(0, pTopology);

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

    hr = RunMediaSession(pSession);

done:

    if(pSession){

        hr = pSession->Close();

        // todo : normally wait for close event, here just Sleep
        Sleep(1000);
    }

    if(pMediaSink){

        hr = pMediaSink->Shutdown();
        SAFE_RELEASE(pMediaSink);
    }

    if(pSource){

        hr = pSource->Shutdown();
        SAFE_RELEASE(pSource);
    }

    if(pSession){

        hr = pSession->Shutdown();
        SAFE_RELEASE(pSession);
    }

    SAFE_RELEASE(pByteStream);
    SAFE_RELEASE(pAudioMediaType);
    SAFE_RELEASE(pVideoMediaType);

    SAFE_RELEASE(pAudioSinkNode);
    SAFE_RELEASE(pVideoSinkNode);
    SAFE_RELEASE(pAudioSourceNode);
    SAFE_RELEASE(pVideoSourceNode);

    SAFE_RELEASE(pTopology);

    return hr;
}

HRESULT ConfigureSource(LPCWSTR wszVideoFile, IMFMediaSource** ppSource, IMFMediaType** ppVideoMediaType, IMFMediaType** ppAudioMediaType, IMFTopologyNode** ppVideoSourceNode, IMFTopologyNode** ppVAudioSourceNode){

    HRESULT hr = S_OK;
    IMFPresentationDescriptor* pPresentationDescriptor = NULL;
    IMFStreamDescriptor* pStreamDescriptor = NULL;
    DWORD dwStreamCount = 0;
    BOOL bSelected = FALSE;

    hr = CreateMediaSource(wszVideoFile, ppSource);

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

    hr = (*ppSource)->CreatePresentationDescriptor(&pPresentationDescriptor);

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

    hr = pPresentationDescriptor->GetStreamDescriptorCount(&dwStreamCount);

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

    for(DWORD dwStream = 0; dwStream < dwStreamCount; dwStream++){

        hr = pPresentationDescriptor->GetStreamDescriptorByIndex(dwStream, &bSelected, &pStreamDescriptor);

        if(FAILED(hr)){
            break;
        }

        if(bSelected){

            hr = ConfigureMediaTypeSource(*ppSource, pPresentationDescriptor, pStreamDescriptor, ppVideoMediaType, ppAudioMediaType, ppVideoSourceNode, ppVAudioSourceNode);
        }

        SAFE_RELEASE(pStreamDescriptor);

        if(FAILED(hr) || ((*ppVideoMediaType) && (*ppAudioMediaType))){
            break;
        }
    }

done:

    SAFE_RELEASE(pStreamDescriptor);
    SAFE_RELEASE(pPresentationDescriptor);

    // We just only if video and audio stream are presents
    if((*ppVideoMediaType) == NULL && (*ppAudioMediaType) == NULL)
        hr = E_FAIL;

    return hr;
}

HRESULT CreateMediaSource(LPCWSTR wszVideoFile, IMFMediaSource** ppSource){

    MF_OBJECT_TYPE ObjectType = MF_OBJECT_INVALID;

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

    HRESULT hr = MFCreateSourceResolver(&pSourceResolver);

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

    hr = pSourceResolver->CreateObjectFromURL(wszVideoFile, MF_RESOLUTION_MEDIASOURCE, NULL, &ObjectType, &pSource);

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

    hr = pSource->QueryInterface(IID_PPV_ARGS(ppSource));

done:

    SAFE_RELEASE(pSourceResolver);
    SAFE_RELEASE(pSource);

    return hr;
}

HRESULT ConfigureMediaTypeSource(IMFMediaSource* pSource, IMFPresentationDescriptor* pPresentationDescriptor, IMFStreamDescriptor* pStreamDescriptor, IMFMediaType** ppVideoMediaType,
    IMFMediaType** ppAudioMediaType, IMFTopologyNode** ppVideoSourceNode, IMFTopologyNode** ppAudioSourceNode){

    HRESULT hr = S_OK;
    IMFMediaTypeHandler* pHandler = NULL;
    IMFMediaType* pMediaType = NULL;
    DWORD dwTypeCount = 0;
    GUID MajorType = GUID_NULL;

    hr = pStreamDescriptor->GetMediaTypeHandler(&pHandler);

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

    hr = pHandler->GetMediaTypeCount(&dwTypeCount);

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

    for(DWORD dwType = 0; dwType < dwTypeCount; dwType++){

        hr = pHandler->GetMediaTypeByIndex(dwType, &pMediaType);

        if(hr == S_OK){

            hr = pMediaType->GetMajorType(&MajorType);

            if(hr == S_OK){

                if(MajorType == MFMediaType_Video && (*ppVideoMediaType) == NULL){

                    hr = pHandler->SetCurrentMediaType(pMediaType);

                    if(hr == S_OK){

                        //LogMediaType(pMediaType);

                        hr = CreateSourceStreamNode(pSource, pPresentationDescriptor, pStreamDescriptor, ppVideoSourceNode);

                        if(hr == S_OK){
                            *ppVideoMediaType = pMediaType;
                            (*ppVideoMediaType)->AddRef();
                            break;
                        }
                    }
                }
                else if(MajorType == MFMediaType_Audio && (*ppAudioMediaType) == NULL){

                    hr = pHandler->SetCurrentMediaType(pMediaType);

                    if(hr == S_OK){

                        //LogMediaType(pMediaType);

                        hr = CreateSourceStreamNode(pSource, pPresentationDescriptor, pStreamDescriptor, ppAudioSourceNode);

                        if(hr == S_OK){
                            *ppAudioMediaType = pMediaType;
                            (*ppAudioMediaType)->AddRef();
                            break;
                        }
                    }
                }
            }
        }

        SAFE_RELEASE(pMediaType);
    }

done:

    SAFE_RELEASE(pMediaType);
    SAFE_RELEASE(pHandler);

    return hr;
}

HRESULT CreateSourceStreamNode(IMFMediaSource* pSource, IMFPresentationDescriptor* pPresentationDescriptor, IMFStreamDescriptor* pStreamDescriptor, IMFTopologyNode** ppNode){

    HRESULT hr = S_OK;
    IMFTopologyNode* pNode = NULL;

    hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode);

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

    hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, pSource);

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

    hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, pPresentationDescriptor);

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

    hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, pStreamDescriptor);

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

    *ppNode = pNode;
    (*ppNode)->AddRef();

done:

    SAFE_RELEASE(pNode);

    return hr;
}

HRESULT CreateTopologyNodeSink(IMFMediaSink* pMediaSink, IMFTopologyNode** ppVideoSinkNode, IMFTopologyNode** ppAudioSinkNode, IMFMediaType* pVideoMediaType, IMFMediaType* pAudioMediaType){

    HRESULT hr = S_OK;
    DWORD dwCount = 0;
    IMFStreamSink* pStreamSink = NULL;
    IMFMediaTypeHandler* pHandler = NULL;
    GUID MajorType = GUID_NULL;

    hr = pMediaSink->GetStreamSinkCount(&dwCount);

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

    for(DWORD dwIndex = 0; dwIndex < dwCount; dwIndex++){

        hr = pMediaSink->GetStreamSinkByIndex(dwIndex, &pStreamSink);

        if(hr == S_OK){

            hr = pStreamSink->GetMediaTypeHandler(&pHandler);

            if(hr == S_OK){

                hr = pHandler->GetMajorType(&MajorType);

                if(hr == S_OK){

                    if(MajorType == MFMediaType_Video)
                        hr = ConfigureSinkNode(pHandler, pStreamSink, ppVideoSinkNode, pVideoMediaType);
                    else if(MajorType == MFMediaType_Audio)
                        hr = ConfigureSinkNode(pHandler, pStreamSink, ppAudioSinkNode, pAudioMediaType);
                }

                if(hr == S_OK && (*ppVideoSinkNode) != NULL && (*ppAudioSinkNode) != NULL){
                    break;
                }
            }

            SAFE_RELEASE(pHandler);
        }

        SAFE_RELEASE(pStreamSink);
    }

done:

    SAFE_RELEASE(pHandler);
    SAFE_RELEASE(pStreamSink);

    if((*ppVideoSinkNode) == NULL || (*ppAudioSinkNode) == NULL)
            hr = E_FAIL;

    return hr;
}

HRESULT ConfigureSinkNode(IMFMediaTypeHandler* pHandler, IMFStreamSink* pStreamSink, IMFTopologyNode** ppSinkNode, IMFMediaType* pMediaType){

    HRESULT hr = S_OK;
    IMFTopologyNode* pNode = NULL;

    hr = pHandler->SetCurrentMediaType(pMediaType);

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

    hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pNode);

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

    hr = pNode->SetObject(pStreamSink);

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

    *ppSinkNode = pNode;
    (*ppSinkNode)->AddRef();

done:

    SAFE_RELEASE(pNode);

    return hr;
}

HRESULT ConfigureTopologyNode(IMFTopology* pTopology, IMFTopologyNode* pVideoSourceNode, IMFTopologyNode* pAudioSourceNode, IMFTopologyNode* pVideoSinkNode, IMFTopologyNode* pAudioSinkNode){

    HRESULT hr = S_OK;

    hr = pTopology->AddNode(pVideoSourceNode);

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

    hr = pTopology->AddNode(pAudioSourceNode);

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

    hr = pTopology->AddNode(pVideoSinkNode);

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

    hr = pTopology->AddNode(pAudioSinkNode);

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

    hr = pVideoSourceNode->ConnectOutput(0, pVideoSinkNode, 0);

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

    hr = pAudioSourceNode->ConnectOutput(0, pAudioSinkNode, 0);

done:

    return hr;
}

HRESULT RunMediaSession(IMFMediaSession* pSession){

    HRESULT hr = S_OK;

    BOOL bSessionEvent = TRUE;

    while(bSessionEvent){

        HRESULT hrStatus = S_OK;
        IMFMediaEvent* pEvent = NULL;
        MediaEventType meType = MEUnknown;

        MF_TOPOSTATUS TopoStatus = MF_TOPOSTATUS_INVALID;

        hr = pSession->GetEvent(0, &pEvent);

        if(SUCCEEDED(hr)){
            hr = pEvent->GetStatus(&hrStatus);
        }

        if(SUCCEEDED(hr)){
            hr = pEvent->GetType(&meType);
        }

        if(SUCCEEDED(hr) && SUCCEEDED(hrStatus)){

            switch(meType){

                case MESessionTopologySet:
                    wprintf(L"MESessionTopologySet\n");
                    break;

                case MESessionTopologyStatus:

                    hr = pEvent->GetUINT32(MF_EVENT_TOPOLOGY_STATUS, (UINT32*)&TopoStatus);

                    if(SUCCEEDED(hr)){

                        switch(TopoStatus){

                            case MF_TOPOSTATUS_READY:
                            {
                                wprintf(L"MESessionTopologyStatus: MF_TOPOSTATUS_READY\n");
                                PROPVARIANT varStartPosition;
                                PropVariantInit(&varStartPosition);
                                hr = pSession->Start(&GUID_NULL, &varStartPosition);
                                PropVariantClear(&varStartPosition);
                            }
                            break;

                            case MF_TOPOSTATUS_STARTED_SOURCE:
                                wprintf(L"MESessionTopologyStatus: MF_TOPOSTATUS_STARTED_SOURCE\n");
                                break;

                            case MF_TOPOSTATUS_ENDED:
                                wprintf(L"MESessionTopologyStatus: MF_TOPOSTATUS_ENDED\n");
                                break;

                            default:
                                wprintf(L"MESessionTopologyStatus: %d\n", TopoStatus);
                                break;
                        }
                    }
                    break;

                case MESessionStarted:
                    wprintf(L"MESessionStarted\n");
                    break;

                case MESessionEnded:
                    wprintf(L"MESessionEnded\n");
                    hr = pSession->Stop();
                    break;

                case MESessionStopped:
                    wprintf(L"MESessionStopped\n");
                    hr = pSession->Close();
                    break;

                case MESessionClosed:
                    wprintf(L"MESessionClosed\n");
                    bSessionEvent = FALSE;
                    break;

                case MESessionNotifyPresentationTime:
                    wprintf(L"MESessionNotifyPresentationTime\n");
                    break;

                case MESessionCapabilitiesChanged:
                    wprintf(L"MESessionCapabilitiesChanged\n");
                    break;

                case MEEndOfPresentation:
                    wprintf(L"MEEndOfPresentation\n");
                    break;

                default:
                    wprintf(L"Media session event: %d\n", meType);
                    break;
            }

            SAFE_RELEASE(pEvent);

            if(FAILED(hr) || FAILED(hrStatus)){
                bSessionEvent = FALSE;
            }
        }
    }

    return hr;
}