Media Foundation音频/视频捕获到MPEG4FileSink会产生不正确的持续时间

时间:2017-08-30 13:10:18

标签: c++ video media video-capture ms-media-foundation

我正在使用Media Foundation框架处理媒体流应用程序。我使用了一些来自互联网和Anton Polinger的样本。不幸的是,在将流保存到mp4文件后,文件元数据已损坏。它的持续时间不正确(根据我的电脑工作时间,例如30小时),错误的比特率。经过长时间的努力,我已经修复了单流(视频或音频),但是当我尝试录制音频和视频时,这个问题又重新出现了。我的拓扑结构出了问题,但是我无法理解这里有什么,可能还有专家呢?

我获取音频和视频源,将其包装到IMFCollection中,通过MFCreateAggregateSource创建聚合源。 我为聚合源中的每个源创建源节点:

section-boxed

之后我将每个源连接到转换(H264编码器和AAC编码器)和MPEG4FileSink:

Com::IMFTopologyNodePtr 
TopologyBuilder::CreateSourceNode(Com::IMFStreamDescriptorPtr 
streamDescriptor)
{
    HRESULT hr = S_OK;
    Com::IMFTopologyNodePtr pNode;
    // Create the topology node, indicating that it must be a source node.
    hr = MFCreateTopologyNode(MF_TOPOLOGY_SOURCESTREAM_NODE, &pNode);
    THROW_ON_FAIL(hr, "Unable to create topology node for source");

    // Associate the node with the source by passing in a pointer to the media source,
    // and indicating that it is the source
    hr = pNode->SetUnknown(MF_TOPONODE_SOURCE, _sourceDefinition->GetMediaSource());
    THROW_ON_FAIL(hr, "Unable to set source as object for topology node");

    // Set the node presentation descriptor attribute of the node by passing
    // in a pointer to the presentation descriptor
    hr = pNode->SetUnknown(MF_TOPONODE_PRESENTATION_DESCRIPTOR, _sourceDefinition->GetPresentationDescriptor());
    THROW_ON_FAIL(hr, "Unable to set MF_TOPONODE_PRESENTATION_DESCRIPTOR to node");

    // Set the node stream descriptor attribute by passing in a pointer to the stream
    // descriptor
    hr = pNode->SetUnknown(MF_TOPONODE_STREAM_DESCRIPTOR, streamDescriptor);
    THROW_ON_FAIL(hr, "Unable to set MF_TOPONODE_STREAM_DESCRIPTOR to node");

    return pNode;
}

当输出类型应用于音频变换时,它有15个属性而不是8个,包括应该应用于视频的MF_MT_AVG_BITRATE,据我所知。在我的情况下,它是192000年,它与视频流上的MF_MT_AVG_BITRATE不同。 我的AAC媒体类型是通过这种方法创建的:

void TopologyBuilder::CreateFileSinkOutputNode(PCWSTR filePath)
{
    HRESULT hr = S_OK;
    DWORD sink_count;

    Com::IMFByteStreamPtr byte_stream;
    Com::IMFTransformPtr transform;

    LPCWSTR lpcwstrFilePath = filePath;
    hr = MFCreateFile(
    MF_ACCESSMODE_WRITE, MF_OPENMODE_FAIL_IF_NOT_EXIST, MF_FILEFLAGS_NONE,
    lpcwstrFilePath, &byte_stream);
    THROW_ON_FAIL(hr, L"Unable to create and open file");

// Video stream
    Com::IMFMediaTypePtr in_mf_video_media_type = _sourceDefinition->GetCurrentVideoMediaType();

    Com::IMFMediaTypePtr out_mf_media_type = CreateMediaType(MFMediaType_Video, MFVideoFormat_H264);
    hr = CopyType(in_mf_video_media_type, out_mf_media_type);
    THROW_ON_FAIL(hr, L"Unable to copy type parameters");

    if (GetSubtype(in_mf_video_media_type) != MEDIASUBTYPE_H264)
    {
        transform.Attach(CreateAndInitCoderMft(MFT_CATEGORY_VIDEO_ENCODER, out_mf_media_type));
        THROW_ON_NULL(transform);
    }

    if (transform)
    {
        Com::IMFMediaTypePtr transformMediaType;
        hr = transform->GetOutputCurrentType(0, &transformMediaType);
        THROW_ON_FAIL(hr, L"Unable to get current output type");

        UINT32 pcbBlobSize = 0;
        hr = transformMediaType->GetBlobSize(MF_MT_MPEG_SEQUENCE_HEADER, &pcbBlobSize);
        THROW_ON_FAIL(hr, L"Unable to get blob size of MF_MT_MPEG_SEQUENCE_HEADER");

        std::vector<UINT8> blob(pcbBlobSize);
        hr = transformMediaType->GetBlob(MF_MT_MPEG_SEQUENCE_HEADER, &blob.front(), blob.size(), NULL);
        THROW_ON_FAIL(hr, L"Unable to get blob MF_MT_MPEG_SEQUENCE_HEADER");

        hr = out_mf_media_type->SetBlob(MF_MT_MPEG_SEQUENCE_HEADER, &blob.front(), blob.size());
        THROW_ON_FAIL(hr, L"Unable to set blob of MF_MT_MPEG_SEQUENCE_HEADER");
    }

    // Audio stream
    Com::IMFMediaTypePtr out_mf_audio_media_type;
    Com::IMFTransformPtr transformAudio;
    Com::IMFMediaTypePtr mediaTypeTmp = _sourceDefinition->GetCurrentAudioMediaType();
    Com::IMFMediaTypePtr in_mf_audio_media_type;
    if (mediaTypeTmp != NULL)
    {
        std::unique_ptr<MediaTypesFactory> factory(new MediaTypesFactory());
        if (!IsMediaTypeSupportedByAacEncoder(mediaTypeTmp))
        {
            UINT32 channels;
            hr = mediaTypeTmp->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &channels);
            THROW_ON_FAIL(hr, L"Unable to get MF_MT_AUDIO_NUM_CHANNELS fron source media type");
            in_mf_audio_media_type = factory->CreatePCM(factory->DEFAULT_SAMPLE_RATE, channels);
        }
        else
        {
            in_mf_audio_media_type.Attach(mediaTypeTmp.Detach());
        }

        out_mf_audio_media_type = factory->CreateAAC(in_mf_audio_media_type, factory->HIGH_ENCODED_BITRATE);
        GUID subType = GetSubtype(in_mf_audio_media_type);
        if (GetSubtype(in_mf_audio_media_type) != MFAudioFormat_AAC)
        {
            // add encoder to Aac
        transformAudio.Attach(CreateAndInitCoderMft(MFT_CATEGORY_AUDIO_ENCODER, out_mf_audio_media_type));
        }
    }

    Com::IMFMediaSinkPtr pFileSink;
    hr = MFCreateMPEG4MediaSink(byte_stream, out_mf_media_type,     out_mf_audio_media_type, &pFileSink);
    THROW_ON_FAIL(hr, L"Unable to create mpeg4 media sink");

    Com::IMFTopologyNodePtr pOutputNodeVideo;
    hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pOutputNodeVideo);
    THROW_ON_FAIL(hr, L"Unable to create output node");

    hr = pFileSink->GetStreamSinkCount(&sink_count);
    THROW_ON_FAIL(hr, L"Unable to get stream sink count from mediasink");

    if (sink_count == 0)
    {
        THROW_ON_FAIL(E_UNEXPECTED, L"Sink count should be greater than 0");
    }

    Com::IMFStreamSinkPtr stream_sink_video;
    hr = pFileSink->GetStreamSinkByIndex(0, &stream_sink_video);
    THROW_ON_FAIL(hr, L"Unable to get stream sink by index");

    hr = pOutputNodeVideo->SetObject(stream_sink_video);
    THROW_ON_FAIL(hr, L"Unable to set stream sink as output node object");

    hr = _pTopology->AddNode(pOutputNodeVideo);
    THROW_ON_FAIL(hr, L"Unable to add file sink output node");

    pOutputNodeVideo = AddEncoderIfNeed(_pTopology, transform, in_mf_video_media_type, pOutputNodeVideo);

    _outVideoNodes.push_back(pOutputNodeVideo);

    Com::IMFTopologyNodePtr pOutputNodeAudio;

    if (in_mf_audio_media_type != NULL)
    {
        hr = MFCreateTopologyNode(MF_TOPOLOGY_OUTPUT_NODE, &pOutputNodeAudio);
        THROW_ON_FAIL(hr, L"Unable to create output node");

        Com::IMFStreamSinkPtr stream_sink_audio;
        hr = pFileSink->GetStreamSinkByIndex(1, &stream_sink_audio);
        THROW_ON_FAIL(hr, L"Unable to get stream sink by index");

        hr = pOutputNodeAudio->SetObject(stream_sink_audio);
        THROW_ON_FAIL(hr, L"Unable to set stream sink as output node object");

        hr = _pTopology->AddNode(pOutputNodeAudio);
        THROW_ON_FAIL(hr, L"Unable to add file sink output node");

        if (transformAudio)
        {
            Com::IMFTopologyNodePtr outputTransformNodeAudio;
            AddTransformNode(_pTopology, transformAudio, pOutputNodeAudio, &outputTransformNodeAudio);

            _outAudioNode = outputTransformNodeAudio;
        }
        else
    {
            _outAudioNode = pOutputNodeAudio;
        }
    }
}

如果有人可以帮助我或解释我错在哪里,那将是很棒的。 感谢。

2 个答案:

答案 0 :(得分:2)

在我的项目中CaptureManager我遇到了类似的问题 - 而我编写的代码用于将来自许多网络摄像头的实时视频录制到一个文件中。经过媒体基金会的长期研究,我发现了两个重要的事实: 1.实时源 - 网络摄像头和麦克风不从0开始 - 根据规格样本,应从0时间戳开始 - Live Sources - “第一个样本应该有零时间戳。” - 但实时源设置当前系统时间。 2.我从您的代码中看到您使用Media Session - 它是一个带有IMFMediaSession接口的对象。我认为你是从MFCreateMediaSession函数创建的。此函数创建会话的默认版本,该版本针对从文件播放媒体进行了优化,默认情况下,样本从0开始。 在我看来,主要问题是默认媒体会话不检查来自源的媒体样本的时间戳,因为从媒体文件它们从零开始或从StartPosition开始。但是,实时源不是从0开始 - 它们应该或必须但不是。 所以,我的建议 - 用IMFTransform编写类,它将是源代码和编码器之间的“代理”转换 - 这个“代理”转换必须修复来自实时源的媒体样本的时间戳:1。当它接收来自的第一个媒体样本时实时源,它保存第一个媒体样本的实际时间戳,如参考时间,并将第一个媒体样本的时间戳设置为零,所有来自此实时源的下一个媒体样本的时间戳必须减去此参考时间并设置为媒体样本的时间戳。 另外,请检查调用IMFFinalizableMediaSink的代码。

的问候。

答案 1 :(得分:0)

在某些情况下,MP4元数据可能会被错误地初始化(例如like this),但是在您描述的场景中,问题就像是有效载荷数据而不是您首先设置管道的方式。 / p>

解码器和转换器通常通过将样本从输入复制到输出来传递样本的时间戳,因此如果出现问题则它们不会指示失败 - 您仍然可以将输出写入文件中。如果您有样本时间问题,非常长的录音,溢出错误,那么接收器可能在处理您的数据时遇到问题。如果利率用大分子/分母表示。重要的是源产生的采样时间。

您可能希望尝试录制较短的录制内容,也可以录制仅限视频和仅录制音频,这可能有助于识别提供导致问题的数据的流。

此外,您可能需要检查生成的MP4文件原子/框以确定标题框是否包含不正确的数据,或者数据本身是否标记错误,在哪个轨道上以及如何准确(特别是开始正常然后会产生奇怪的空白)在中间)。