【问题标题】:Windows Media Foundation recording audioWindows Media Foundation 录制音频
【发布时间】:2012-10-06 16:53:33
【问题描述】:

我正在使用 windows media foundation api 来枚举我的麦克风和可用的摄像头,它们都可以工作。

这是我的枚举代码:

class deviceInput {
public:
    deviceInput( REFGUID source );
    ~deviceInput();

    int listDevices(bool refresh = false);
    IMFActivate *getDevice(unsigned int deviceId);
    const WCHAR *getDeviceName(unsigned int deviceId);

private:
    void Clear();
    HRESULT EnumerateDevices();

    UINT32      m_count;
    IMFActivate **m_devices;
    REFGUID     m_source;
};

deviceInput::deviceInput( REFGUID source )
    : m_devices( NULL )
    , m_count( 0 )
    , m_source( source )
{   }

deviceInput::~deviceInput()
{
    Clear();
}

int deviceInput::listDevices(bool refresh)
{
    if ( refresh || !m_devices ) {
        if ( FAILED(this->EnumerateDevices()) ) return -1;
    }
    return m_count;
}

IMFActivate *deviceInput::getDevice(unsigned int deviceId)
{
    if ( deviceId >= m_count ) return NULL;

    IMFActivate *device = m_devices[deviceId];
    device->AddRef();

    return device;
}

const WCHAR *deviceInput::getDeviceName(unsigned int deviceId)
{
    if ( deviceId >= m_count ) return NULL;

    HRESULT hr = S_OK;
    WCHAR *devName = NULL;
    UINT32 length;

    hr = m_devices[deviceId]->GetAllocatedString( MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &devName, &length );
    if ( FAILED(hr) ) return NULL;

    return devName;
}

void deviceInput::Clear()
{
    if ( m_devices ) {
        for (UINT32 i = 0; i < m_count; i++) SafeRelease( &m_devices[i] );
        CoTaskMemFree( m_devices );
    }
    m_devices = NULL;
    m_count = 0;
}

HRESULT deviceInput::EnumerateDevices()
{
    HRESULT hr = S_OK;
    IMFAttributes *pAttributes = NULL;

    Clear();

    hr = MFCreateAttributes(&pAttributes, 1);
    if ( SUCCEEDED(hr) ) hr = pAttributes->SetGUID( MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, m_source );
    if ( SUCCEEDED(hr) ) hr = MFEnumDeviceSources( pAttributes, &m_devices, &m_count );

    SafeRelease( &pAttributes );

    return hr;
}

要获取音频或摄像头捕获设备,我指定MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUIDMF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID,这没问题,我可以获取设备名称以及 IMFActivate。我有将网络摄像头录制到输出视频文件的代码,但是,我很难弄清楚如何将音频录制到文件中。我的印象是我需要使用 IMFSinkWriter,但我找不到任何使用音频捕获 IMFActivate 和 IMFSinkWriter 的示例。

我不是一个 Windows api 程序员,所以我确信有一个相当直截了当的答案,但 COM 的东西只是让我有点过头了。至于音频格式,我真的不在乎,只要它进入一个文件 - 可以是 wav、wma 或其他。即使我正在录制视频,我也需要将视频和音频文件分开,所以我无法弄清楚如何将音频添加到我的视频编码中。

【问题讨论】:

    标签: c++ winapi mfc ms-media-foundation


    【解决方案1】:

    您是否很难在Record directshow audio device to file 中管理 DirectShow 音频捕获?

    使用 Media Foundation 进行捕获几乎没有任何简单。更不用说一般来说 DirectShow 上有更多的资源......

    MSDN 为您提供了一个WavSink Sample,它将音频捕获到文件中:

    展示了如何在 Microsoft Media Foundation 中实现自定义媒体接收器。该示例实现了一个归档接收器,它将未压缩的 PCM 音频写入 .wav 文件。

    我不确定他们为什么决定不将此作为标准组件。 Media Foundation 在很多方面不如 DirectShow,他们至少可以把这个小东西当成优势。不管怎样,你已经有了样本,这看起来是一个好的开始。

    【讨论】:

    • 是的,我注意到了 WavSink 示例,问题在于它实际上是一个转码器;它需要一个 pcm 音频文件,并将其转换为 *.wav 文件,因此它并没有真正告诉我如何直接从设备获取音频数据。我使用的是 directshow,但我的老板大力鼓励我(按照告诉我的方式)使用媒体基金会。
    • 无论如何,它都会为您提供最重要的部分。是的,您需要使拓扑不转码,而是使用真正的音频捕获设备。
    【解决方案2】:

    对于迟到的回复,我深表歉意,我希望您仍然可以发现这很有价值。我最近完成了一个与您类似的项目(将网络摄像头视频与选定的麦克风一起录制到带有音频的单个视频文件中)。关键是创建一个聚合媒体源。

    // http://msdn.microsoft.com/en-us/library/windows/desktop/dd388085(v=vs.85).aspx
    HRESULT CreateAggregateMediaSource(IMFMediaSource *videoSource,
                                       IMFMediaSource *audioSource,
                                       IMFMediaSource **aggregateSource)
    {
        *aggregateSource = nullptr;
        IMFCollection *pCollection = nullptr;
    
        HRESULT hr = ::MFCreateCollection(&pCollection);
    
        if (S_OK == hr)
            hr = pCollection->AddElement(videoSource);
    
        if (S_OK == hr)
            hr = pCollection->AddElement(audioSource);
    
        if (S_OK == hr)
            hr = ::MFCreateAggregateSource(pCollection, aggregateSource);
    
        SafeRelease(&pCollection);
        return hr;
    }
    

    配置接收器写入器时,您将添加 2 个流(一个用于音频,一个用于视频)。 当然,您还将为输入流类型正确配置编写器。

    HRESULT        hr                  = S_OK;
    IMFMediaType  *videoInputType      = nullptr;
    IMFMediaType  *videoOutputType     = nullptr;
    DWORD          videoOutStreamIndex = 0u;
    DWORD          audioOutStreamIndex = 0u;
    IMFSinkWriter *writer              = nullptr;
    
    // [other create and configure writer]
    
    if (S_OK == hr))
        hr = writer->AddStream(videoOutputType, &videoOutStreamIndex);    
    
    // [more configuration code]
    
    if (S_OK == hr)
        hr = writer->AddStream(audioOutputType, &audioOutStreamIndex);
    

    那么在阅读示例时,您需要密切关注阅读器的流索引,并适当地将它们发送给作者。您还需要密切注意编解码器期望的格式。比如IEEE float vs PCM等。祝你好运,希望还不算晚。

    【讨论】:

    • 我已经有很长时间没有从事那个项目了,并且已经被其他一些人接管了,他们把它带到了不同的方向。无论如何,感谢您对示例代码的明确回答,非常感谢,也许其他人会发现它非常有用:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-20
    相关资源
    最近更新 更多