You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Media Foundation音频捕获问题:ReadSample函数始终无返回

问题描述

使用Media Foundation捕获麦克风音频时,ReadSample函数始终阻塞无返回。所有HRESULT检查均通过,更换设备、修改MFT SUBTYPE格式后问题依旧,无明确错误提示,无法确定排查方向。

代码文件

main.cpp

#include "../../lib/audio_utils.h"
#include "../CaptureAndDisplayVideoDevice/AudioDeviceCapturer.h"

static HRESULT stub_function()
{
    HRESULT hr = S_OK;

    std::vector<audio_device_info> audio_devices_information;
    if (FAILED(hr = get_audio_devices_info(audio_devices_information)))
        return hr;

    IMFMediaSource* media_source;
    if(FAILED(hr = get_device_source_from_audio_device_info(audio_devices_information[0], &media_source)))
        return hr;

    AudioDeviceCapturer* adc = new AudioDeviceCapturer(media_source);

    DWORD stream;
    DWORD flags;
    LONGLONG timestamp;

    while (true)
    {
        CComPtr<IMFSample> sample = nullptr;
        // this is reading in syncronous blocking mode, MF supports also async calls
        hr = adc->m_source_reader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &stream, &flags, &timestamp, &sample);

        if (flags & MF_SOURCE_READERF_STREAMTICK)
            continue;

        CComPtr<IMFMediaBuffer> buffer;
    }
}

int main()
{
    stub_function();
}

AudioDeviceCapturer.h

#pragma once

#define COBJMACROS
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <mfapi.h>
#include <mfidl.h>
#include <mfreadwrite.h>

#include <stdio.h>
#include <intrin.h>

#include <atlbase.h>

// std
#include <iostream>
#include <string>

#pragma comment (lib, "ole32.lib")
#pragma comment (lib, "mf.lib")
#pragma comment (lib, "mfplat.lib")
#pragma comment (lib, "mfuuid.lib")
#pragma comment (lib, "mfreadwrite.lib")

class AudioDeviceCapturer
{
public:
    IMFMediaSource* m_device_source;
    IMFSourceReader* m_source_reader;

    AudioDeviceCapturer(IMFMediaSource* device_source) : m_device_source(device_source)
    {
        HRESULT hr = S_OK;

        ATLENSURE_SUCCEEDED(MFStartup(MF_VERSION, MFSTARTUP_NOSOCKET));

        //
        {
            CComPtr<IMFAttributes> encoder_attributes;

            MFCreateAttributes(&encoder_attributes, 2);

            hr = MFCreateSourceReaderFromMediaSource(m_device_source, encoder_attributes, &m_source_reader);
        }

        CComPtr<IMFMediaType> type;

        ATLENSURE_SUCCEEDED(MFCreateMediaType(&type));

        ATLENSURE_SUCCEEDED(type->SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio));

        ATLENSURE_SUCCEEDED(type->SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM));
    }
private:
};

audio_utils.h

#pragma once

#include <Windows.h>
#include <cstdint>
#include <tuple>
#include <assert.h>
#include <mfapi.h>
#include <mfidl.h>
#include <atlbase.h>
#include <iostream>
#include <vector>

// libs
#pragma comment(lib, "mfplat.lib")
#pragma comment(lib, "mf.lib")
#pragma comment(lib, "evr.lib")
#pragma comment(lib, "mfuuid.lib")
#pragma comment(lib, "Winmm.lib")

struct audio_device_info
{
    wchar_t name[255];
    wchar_t endpoint_id[255];
};

HRESULT get_audio_devices_info(std::vector<audio_device_info>& audio_devices_information)
{
    HRESULT hr;

    hr = MFStartup(MF_VERSION, MFSTARTUP_NOSOCKET);

    uint32_t count;
    IMFActivate** devices;

    CComPtr<IMFAttributes> attr;

    hr = MFCreateAttributes(&attr, 1);

    hr = attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID);

    hr = MFEnumDeviceSources(attr, &devices, &count);

    printf("Detected %u devices:\n", count);

    for (uint32_t i = 0; i < count; i++)
    {
        audio_device_info audio_device {};

        uint32_t length;
        LPWSTR name;
        LPWSTR endpoint_id;

        hr = devices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &name, &length);

        hr = devices[i]->GetAllocatedString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID, &endpoint_id, &length);

        wcscpy_s(audio_device.name, name);
        wcscpy_s(audio_device.endpoint_id, endpoint_id);


        {
            IMFMediaSource* video_device_source;
            CComPtr<IMFAttributes> attr;
            ATLENSURE_SUCCEEDED(MFCreateAttributes(&attr, 2));
            ATLENSURE_SUCCEEDED(attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID));
            ATLENSURE_SUCCEEDED(attr->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID, endpoint_id));
            ATLENSURE_SUCCEEDED(MFCreateDeviceSource(attr, &video_device_source));

            IMFPresentationDescriptor* pPD = nullptr;
            IMFStreamDescriptor* pSD = nullptr;
            IMFMediaTypeHandler* pHandler = nullptr;
            IMFMediaType* pType = nullptr;

            ATLENSURE_SUCCEEDED(video_device_source->CreatePresentationDescriptor(&pPD));

            BOOL fSelected;
            ATLENSURE_SUCCEEDED(pPD->GetStreamDescriptorByIndex(0, &fSelected, &pSD));

            ATLENSURE_SUCCEEDED(pSD->GetMediaTypeHandler(&pHandler));

            DWORD cTypes = 0;
            ATLENSURE_SUCCEEDED(pHandler->GetMediaTypeCount(&cTypes));

            for (DWORD i = 0; i < cTypes; i++)
            {
                ATLENSURE_SUCCEEDED(pHandler->GetMediaTypeByIndex(i, &pType));

                GUID Subtype;
                hr = pType->GetGUID(MF_MT_SUBTYPE, &Subtype);
                wchar_t SubtypeText[64];
                StringFromGUID2(Subtype, SubtypeText, static_cast<int>(std::size(SubtypeText)));
            }
        }

        audio_devices_information.push_back(audio_device);

        printf("%S = %S\n", name, endpoint_id);

        CoTaskMemFree(name);
        CoTaskMemFree(endpoint_id);

        devices[i]->Release();
    }

    CoTaskMemFree(devices);

    MFShutdown();

    return hr;
}

HRESULT get_device_source_from_audio_device_info(audio_device_info device_info, IMFMediaSource** video_device_source)
{
    HRESULT hr = S_OK;

    ATLENSURE_SUCCEEDED(MFStartup(MF_VERSION, MFSTARTUP_NOSOCKET));

    CComPtr<IMFAttributes> attr;
    ATLENSURE_SUCCEEDED(MFCreateAttributes(&attr, 2));

    ATLENSURE_SUCCEEDED(attr->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID));

    ATLENSURE_SUCCEEDED(attr->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID, device_info.endpoint_id));

    ATLENSURE_SUCCEEDED(MFCreateDeviceSource(attr, video_device_source));

    MFShutdown();

    return hr;
}


#ifndef DEBUG // works in VS
#define DebugPrint(x) 
#else
#define DebugPrint(x) do { std::cout << x << std::endl; } while (0)
#endif
排查与修复要点
  • MFStartup/MFShutdown调用时机错误
    工具函数get_audio_devices_info和get_device_source_from_audio_device_info中频繁调用MFStartup和MFShutdown,导致创建的MediaSource依赖的MF组件被提前销毁。应只在程序启动时调用一次MFStartup,退出时调用一次MFShutdown,移除工具函数内的这两个调用。

  • 未将自定义MediaType应用到SourceReader
    AudioDeviceCapturer中创建了PCM格式的MediaType,但未调用IMFSourceReader::SetCurrentMediaType将其设置给SourceReader,导致格式协商失败。在创建MediaType后添加代码:

    ATLENSURE_SUCCEEDED(m_source_reader->SetCurrentMediaType(MF_SOURCE_READER_FIRST_AUDIO_STREAM, nullptr, type));
    
  • MediaSource引用计数管理不当
    get_device_source_from_audio_device_info返回的IMFMediaSource*未被正确持有引用,AudioDeviceCapturer构造函数中应调用m_device_source->AddRef(),或改用CComPtr管理指针,避免生命周期异常。

  • ReadSample错误处理缺失
    循环中未处理ReadSample返回的错误状态,也未检查sample是否有效。修改循环逻辑:

    while (true)
    {
        CComPtr<IMFSample> sample = nullptr;
        hr = adc->m_source_reader->ReadSample(MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &stream, &flags, &timestamp, &sample);
        
        if (FAILED(hr))
        {
            // 处理错误,比如退出循环
            break;
        }
    
        if (flags & MF_SOURCE_READERF_STREAMTICK)
            continue;
    
        if (sample == nullptr)
            continue;
    
        // 处理buffer
        CComPtr<IMFMediaBuffer> buffer;
        ATLENSURE_SUCCEEDED(sample->GetBufferByIndex(0, &buffer));
        // 后续音频数据处理逻辑
    }
    
  • 麦克风权限检查
    Windows 10及以上系统需确保程序拥有麦克风访问权限,可在系统设置的「隐私和安全性」→「麦克风」中确认权限已开启。

内容的提问来源于stack exchange,提问作者Meme Machine

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 16:00:20