配置 MPEG4MediaSink

Posted

技术标签:

【中文标题】配置 MPEG4MediaSink【英文标题】:Configuring the MPEG4MediaSink 【发布时间】:2018-08-26 10:56:04 【问题描述】:

我正在尝试构建一个简单的演示 WMF 应用程序,它使用媒体源、管道和媒体接收器来复制 MP4 文件。这是基本程序

    创建媒体源,并从中获取演示描述符 从 表示描述符 从每个流中获取当前的视频和音频 IMFMediaTypes 描述符。勾选表示视频媒体子类型为 H264 并且音频媒体子类型是 AAC 使用 MFCreateMPEG4MediaSink 打开媒体接收器,这两个媒体 类型和输出文件名。 使用流设置视频和音频源节点,并 演示描述符 使用媒体接收器设置视频和音频接收器节点。 将所有节点添加到拓扑中 将源节点连接到汇节点 构建拓扑并运行。

我收到的错误是媒体会话回调中的 MF_E_INVALIDMEDIATYPE。如果我注释掉音频流,将 null 输入到音频类型的 MFCreateMPEG4MediaSink 调用中,并且不为其设置任何节点,那么副本就可以正常工作——我可以播放生成的只有视频的 mp4 文件。

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

我将不胜感激任何关于可能需要做什么才能使音频流正常工作的见解或建议。

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

【问题讨论】:

使用 mftrace.exe 运行您的应用程序。很可能,它会准确地告诉您为什么会收到 MF_E_INVALIDMEDIATYPE 错误。 docs.microsoft.com/en-us/windows/desktop/medfound/using-mftraceblogs.msdn.microsoft.com/mf/2010/08/11/… 感谢您的建议,但我认为这不是我的选择。我正在使用 WMF.net 和 C#。不过,我不认为这是 WMF.net 的问题——我改编的许多 C++ 示例似乎都可以正常工作。不过,关于 mp4 文件接收器的配置,我自己在这里。似乎没有很多关于配置它的信息 - 大多数示例只是使用接收器创建接收器编写器,然后使用它。 您使用哪种语言并不重要。只需构建一个尝试设置该 AAC 音频流并失败的版本,使用 mftrace.exe 运行它,然后读取日志。 谢谢。我一直在尝试这样做。我在 Windows 10 上,该版本的 SDK 似乎没有 mftrace.exe。如果没有(看起来像)对注册表进行一系列痛苦的手动编辑,Windows 7 sdk 显然将无法安装。很抱歉对此感到痛苦,但您使用的是 Windows 10 吗?如果是这样,您是如何安装 mftrace 的? 是的,我在 Win10 上。不,我没有安装任何东西,它是 Windows SDK 的一部分,所以我已经安装了 22 个不同版本的工具,它们都在 C:\Program Files (x86)\Windows Kits 下。 【参考方案1】:

只是为了好玩,我很快就重写了你的程序:

//----------------------------------------------------------------------------------------------
// 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;

【讨论】:

这太棒了! - 简单而线性,非常适合学习。谢谢您的发布。我很高兴发现它的结构与我的 C# 版本几乎相同。我们可以提供的这种示例代码越多,恕我直言。我可能会在 10 月中旬之前在 github 上提供我的代码。我正在编写一本针对新手的关于 WMF 的(免费)书籍,并附有一套示例代码。 我会将我为 *** 编写的所有示例代码以及 MFNode 项目和其他项目放在 github 上。如果您希望我关注您的书以发表意见,我会很乐意这样做,即使它是 C#,:-) 谢谢!我希望有知识的人看看它。这本书的目标读者是相当称职的 C# 程序员,他们在 WMF 或 COM 方面做得不多(即我)。这是一本“入门”的书。当我开始学习 WMF 时,我写下了所有我不清楚的东西。我大约有 120 页,预计在完成之前它会运行到大约 200 页左右。在这种情况下,有大量对 C++ 程序员有用的背景讨论。这本书将免费提供,我希望能在 10 月中旬出版。 还有一套示例代码。当我有一些像样的东西时,我会与你联系 - 我会重视反馈。通过您的 SourceForge 帐户向您发送消息的最佳方式是什么? 我创建了 repo Github:github.com/mofo7777。也许您可以向 github 帐户发送消息,或打开 *** 聊天。【参考方案2】:

从 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

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

EDIT1

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

【讨论】:

非常感谢您。你的编辑原来是关键。问题不在媒体类型中 - 它在媒体接收器的索引中。我在下面提供了更多详细信息。【参考方案3】:

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

为了记录(考虑到它可能对其他人有用),这是我的音频媒体类型的设置。一旦我对流接收器问题进行了排序,这种媒体类型配置就可以正常工作。这些被分解为上述示例音频媒体类型内容中相同、不同、额外和缺失的项目。

一样

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

【讨论】:

以上是关于配置 MPEG4MediaSink的主要内容,如果未能解决你的问题,请参考以下文章

Tomcat卷五---Web 应用配置,管理配置和JVM配置

SpringBoot:配置文件的作用配置文件的格式properties配置文件yml配置文件

IDEA的常用配置,maven配置,git配置操作

SpringCloud Nacos配置管理 -- 统一配置管理(添加配置微服务配置拉取)

Android Gradle 插件LintOptions 配置 ⑦ ( explainIssues 属性配置 | htmlOutput 属性配置 | htmlReport 属性配置 )

Android Gradle 插件LintOptions 配置 ⑤ ( absolutePaths 属性配置 | check 属性配置 | checkAllWarnings 属性配置 )