使用Windows Media Foundation中的Sink Writer添加到视频的音频示例

cro*_*roc 1 audio windows-runtime c++-cx ms-media-foundation

我可以使用我在此处学习的图像编写视频文件.它使用IMFSampleIMFSinkWriter.现在我想为它添加音频.假设有Audio.wma文件,我希望将该音频写入该视频文件中.

但是在这个样本中无法弄清楚如何做到这一点.像输入和输出类型设置,IMFSample音频缓冲区的创建等等.如果有人能告诉我如何使用接收器编写器将音频添加到视频文件,那将是一件好事.

Jef*_*eff 5

Media Foundation非常适合使用,我相信您将能够快速修改您的项目以完成此任务.

概述:
创建新文件IMFMediaSource以从音频文件中读取样本,将音频流添加到接收器,最后使用相应的流索引交错接收器写入.

细节:

修改VideoGenerator::InitializeSinkWriter(..)函数以正确初始化接收器以容纳音频流.在该函数中,正确创建audioTypeOutaudioTypeIn(IMFMediaType).为清晰起见,您可能需要将mediaTypeOutmediaTypeIn重命名为videoTypeOutvideoTypeIn,如下所示:

ComPtr<IMFMediaType>  videoTypeOut;  // <-- previously mediaTypeOut
ComPtr<IMFMediaType>  videoTypeIn;   // <-- previously mediaTypeIn
ComPtr<IMFMediaType>  audioTypeOut = nullptr;
ComPtr<IMFMediaType>  audioTypeIn = nullptr;
Run Code Online (Sandbox Code Playgroud)

接下来,配置与您的视频类型兼容的输出音频类型.由于您似乎正在创建Windows媒体视频,因此您可能希望使用MFAudioFormat_WMAudioV9.为了确保通道,采样率和每个采样的位数是正确的,我通常列举可用的类型并找到所需的特性,类似于以下(错误检查已被省略):

ComPtr<IMFCollection> availableTypes = nullptr;
HRESULT hr = MFTranscodeGetAudioOutputAvailableTypes(MFAudioFormat_WMAudioV9, MFT_ENUM_FLAG_ALL, NULL, availableTypes.GetAddressOf());

DWORD count = 0;
hr = availableTypes->GetElementCount(&count));  // Get the number of elements in the list.

ComPtr<IUnknown>     pUnkAudioType = nullptr;
ComPtr<IMFMediaType> audioOutputType = nullptr;
for (DWORD i = 0; i < count; ++i)
{
    hr = availableTypes->GetElement(i, pUnkAudioType.GetAddressOf());
    hr = pUnkAudioType.Get()->QueryInterface(IID_PPV_ARGS(audioTypeOut.GetAddressOf()));

    // compare channels, sampleRate, and bitsPerSample to target numbers
    {
        // audioTypeOut is set!
        break;
    }

    audioOutputType.Reset();
}

availableTypes.Reset();
Run Code Online (Sandbox Code Playgroud)

如果audioTypeOut设置成功,则将该类型的流添加到接收器并获取结果索引:

hr = sinkWriter->AddStream(audioTypeOut.Get(), &audioStreamIndex);
audioTypeOut.Reset();  // <-- audioTypeOut not needed anymore
Run Code Online (Sandbox Code Playgroud)

最后,对于接收器,必须设置音频输入类型,这取决于您正在读取的文件和音频源(IMFMediaSource).更多内容,但是将音频输入添加到接收器将类似于以下内容:

ComPtr<IMFMediaType> audioTypeIn = nullptr;  // <-- declaration from above
// NOTE: audioReader is an IMFMediaSource used to read the audio file
hr = audioReader->GetCurrentMediaType((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, audioTypeIn.GetAddressOf());
hr = sinkWriter->SetInputMediaType(_audioOutStreamIndex, audioTypeIn.Get(), nullptr);
audioTypeIn.Reset();
Run Code Online (Sandbox Code Playgroud)

有许多示例可用于创建audioReader(IMFMediaSource)并从文件中读取样本,但这一个很简单直接.代码在这里.

最后,写下您会发现非常简单的音频,因为接收器可以直接IMFSample从文件中读取样本().您可以管理写入,但一种解决方案是交错写入(视频/音频).处理音频样本的持续时间,但您需要重新定义时间戳.写入接收器时,请确保您具有正确的流索引.

使用和异步回调读取样本:

// if you are using an async callback, the function would look similar to the following:
HRESULT OnReadAudioSample(HRESULT status, DWORD streamIndex, DWORD streamFlags, LONGLONG timestamp, IMFSample *sample)
{
    // .. other code
    hr = sample->SetSampleTime(timestamp - _baseRecordTime);
    hr = sinkWriter->WriteSample(audioStreamIndex, sample);
    // .. other code

    // trigger the next asyc read...
    hr = audioReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, nullptr, nullptr, nullptr);
}
Run Code Online (Sandbox Code Playgroud)

同步读取样本:

// otherwise, you will only use a synchronous read
hr = audioReader->ReadSample((DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, nullptr, &dwFlags, &timestamp, &sample);
hr = sample->SetSampleTime(timestamp - _baseRecordTime);
hr = sinkWriter->WriteSample(audioStreamIndex, sample);
hr = WriteFrame(target.get(), rtStart, rtDuration);  // <-- write video frame as before
Run Code Online (Sandbox Code Playgroud)

听起来像一个有趣的小项目.祝你好运,玩得开心,希望这有帮助!