本篇主要介绍音频的采集技术,使用的是WASAPI技术。

技术简介

WASAPI全称是Windows Audio Session API(Windows音频会话API),是从Windows Vista之后引入的UAA(Universal Audio Architecture)音频架构所属的API。

主要流程和代码

1、初始化采集器

int WasapiCaptor::init(bool isMic, const std::string& deviceId, const std::string& deviceName)
{int err = ERROR_CODE_OK;if (!m_comInited) {err = ERROR_CODE_COM_INIT_FAILED;return err;}if (m_inited) {return err;}m_isMic = isMic;m_deviceId = deviceId;m_deviceName = deviceName;do {HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,__uuidof(IMMDeviceEnumerator), (void**)&m_deviceEnumerator);if (FAILED(hr)) {err = ERROR_CODE_COM_CREATE_INSTANCE_FAILED;break;}hr = m_deviceEnumerator->GetDevice(HELPER::StringConverter::convertUtf8ToUnicode(m_deviceId).c_str(), &m_endpointDevice);if (FAILED(hr)) {err = ERROR_CODE_COM_GET_AUDIO_ENDPOINT_FAILED;break;}hr = m_endpointDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&m_captureClient);if (FAILED(hr)) {err = ERROR_CODE_COM_ACTIVE_DEVICE_FAILED;break;}hr = m_captureClient->GetMixFormat(&m_waveFmt);if (FAILED(hr)) {err = ERROR_CODE_COM_GET_MIX_FORMAT_FAILED;break;}initFromMixFormat(m_waveFmt);DWORD streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;if (!m_isMic) {streamFlags |= AUDCLNT_STREAMFLAGS_LOOPBACK;}hr = m_captureClient->Initialize(AUDCLNT_SHAREMODE_SHARED, streamFlags, WASAPI_CAPTOR_REF_TIME_PER_SEC, 0, m_waveFmt, nullptr);if (FAILED(hr)) {err = ERROR_CODE_COM_INIT_AUDIO_CLIENT_FAILED;break;}// For ouotput mode, capture event will not signal when there is nothing rendering,// so we run a render thread and rendering silent pcm data all the time.if (!m_isMic) {err = initRenderer();HCMDR_ERROR_CODE_BREAK(err);}hr = m_captureClient->GetBufferSize(&m_captureSampleCount);if (FAILED(hr)) {err = ERROR_CODE_COM_GET_BUFFER_SIZE_FAILED;break;}hr = m_captureClient->GetService(__uuidof(IAudioCaptureClient), (void**)&m_captor);if (FAILED(hr)) {err = ERROR_CODE_COM_GET_CAPTOR_FAILED;break;}m_captureEvent = CreateEventA(nullptr, FALSE, FALSE, nullptr);if (m_captureEvent == nullptr) {err = ERROR_CODE_COM_CREATE_EVENT_FAILED;break;}m_stopEvent = CreateEventA(nullptr, TRUE, FALSE, nullptr);if (m_stopEvent == nullptr) {err = ERROR_CODE_COM_CREATE_EVENT_FAILED;break;}hr = m_captureClient->SetEventHandle(m_captureEvent);if (FAILED(hr)) {err = ERROR_CODE_COM_SET_EVENT_FAILED;break;}m_inited = true;} while (0);if (err != ERROR_CODE_OK) {LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] wasapi captor init, error: %s", __FUNCTION__, HCMDR_GET_ERROR_DESC(err));cleanup();}return err;
}

从wave format中初始化音频属性

void WasapiCaptor::initFromMixFormat(WAVEFORMATEX* waveFmt)
{DWORD channelMask = 0;if (waveFmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {WAVEFORMATEXTENSIBLE* waveFmtEx = (WAVEFORMATEXTENSIBLE*)waveFmt;channelMask = waveFmtEx->dwChannelMask;}m_channelLayout = convertChannelLayout(channelMask, waveFmt->nChannels);m_channels = waveFmt->nChannels;m_bitrate = waveFmt->nAvgBytesPerSec;m_samplerate = waveFmt->nSamplesPerSec;m_bitsPerSample = waveFmt->wBitsPerSample;// Sample format for wasapim_sampleFmt = AV_SAMPLE_FMT_FLT;
}

初始化音频渲染器,即扬声器

int WasapiCaptor::initRenderer()
{int err = ERROR_CODE_OK;do {HRESULT hr = m_endpointDevice->Activate(__uuidof(IAudioClient), CLSCTX_ALL, nullptr, (void**)&m_renderClient);if (FAILED(hr)) {err = ERROR_CODE_COM_ACTIVE_DEVICE_FAILED;break;}WAVEFORMATEX* waveFmt = nullptr;hr = m_renderClient->GetMixFormat(&waveFmt);if (FAILED(hr)) {err = ERROR_CODE_COM_GET_MIX_FORMAT_FAILED;break;}DWORD streamFlags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK;hr = m_renderClient->Initialize(AUDCLNT_SHAREMODE_SHARED, streamFlags, WASAPI_CAPTOR_REF_TIME_PER_SEC, 0, waveFmt, nullptr);if (FAILED(hr)) {err = ERROR_CODE_COM_INIT_AUDIO_CLIENT_FAILED;break;}CoTaskMemFree(waveFmt);hr = m_renderClient->GetService(__uuidof(IAudioRenderClient), (void**)&m_renderer);if (FAILED(hr)) {err = ERROR_CODE_COM_GET_CAPTOR_FAILED;break;}m_renderEvent = CreateEventA(nullptr, FALSE, FALSE, nullptr);if (m_renderEvent == nullptr) {err = ERROR_CODE_COM_CREATE_EVENT_FAILED;break;}hr = m_renderClient->SetEventHandle(m_renderEvent);if (FAILED(hr)) {err = ERROR_CODE_COM_SET_EVENT_FAILED;break;}hr = m_renderClient->GetBufferSize(&m_renderSampleCount);if (FAILED(hr)) {err = ERROR_CODE_COM_GET_BUFFER_SIZE_FAILED;break;}BYTE* buffer = nullptr;hr = m_renderer->GetBuffer(m_renderSampleCount, &buffer);if (FAILED(hr)) {err = ERROR_CODE_COM_GET_BUFFER_FAILED;break;}hr = m_renderer->ReleaseBuffer(m_renderSampleCount, AUDCLNT_BUFFERFLAGS_SILENT);if (FAILED(hr)) {err = ERROR_CODE_COM_RELEASE_BUFFER_FAILED;break;}} while (0);return err;
}

2、开启音频采集线程,包括麦克风和扬声器采集

int WasapiCaptor::start()
{int err = ERROR_CODE_OK;if (m_running) {LOGGER::Logger::log(LOGGER::LOG_TYPE_WARN, "[%s] wasapi captor already running", __FUNCTION__);return err;}if (!m_inited) {err = ERROR_CODE_UNINITIALIZED;LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] wasapi captor not yet initialized: %s",__FUNCTION__, HCMDR_GET_ERROR_DESC(err));return err;}         HRESULT hr = S_OK;if (!m_isMic) {hr = m_renderClient->Start();if (FAILED(hr)) {err = ERROR_CODE_COM_START_FAILED;LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] start rendering failed: %s", __FUNCTION__, HCMDR_GET_ERROR_DESC(err));return err;}}hr = m_captureClient->Start();if (FAILED(hr)) {err = ERROR_CODE_COM_START_FAILED;LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] start capturing failed: %s", __FUNCTION__, HCMDR_GET_ERROR_DESC(err));return err;}m_running = true;if (!m_isMic) {m_renderThread = std::thread(std::bind(&WasapiCaptor::renderProcess, this));}m_thread = std::thread(std::bind(&WasapiCaptor::captureProcess, this));return err;
}

扬声器采集线程函数

void WasapiCaptor::renderProcess()
{HANDLE events[2] = { m_stopEvent, m_renderEvent };while (m_running && WaitForMultipleObjects(2, events, FALSE, INFINITE) != WAIT_OBJECT_0) {uint32_t paddingCount = 0;HRESULT hr = m_renderClient->GetCurrentPadding(&paddingCount);if (FAILED(hr)) {break;}if (m_renderSampleCount == paddingCount) {if (m_onAudioCaptureError != nullptr) {m_onAudioCaptureError(ERROR_CODE_COM_UNEXPECTED_PADDING_COUNT, m_index);break;}}BYTE* buffer = nullptr;hr = m_renderer->GetBuffer(m_renderSampleCount - paddingCount, &buffer);if (FAILED(hr)) {if (m_onAudioCaptureError != nullptr) {m_onAudioCaptureError(ERROR_CODE_COM_GET_BUFFER_FAILED, m_index);}break;}hr = m_renderer->ReleaseBuffer(m_renderSampleCount - paddingCount, AUDCLNT_BUFFERFLAGS_SILENT);if (FAILED(hr)) {if (m_onAudioCaptureError != nullptr) {m_onAudioCaptureError(ERROR_CODE_COM_RELEASE_BUFFER_FAILED, m_index);}break;}}
}

麦克风采集线程函数

void WasapiCaptor::captureProcess()
{HANDLE events[2] = { m_stopEvent, m_captureEvent };// While the system will never not signal this capture event in windows7,// and only signal when something is rendring, so we just wait some ms for speakerDWORD waitTime = m_isMic ? INFINITE : WASAPI_CAPTOR_RENDER_EVENT_WAIT_TIME;AVFrame* frame = av_frame_alloc();while (m_running && WaitForMultipleObjects(2, events, FALSE, waitTime) != WAIT_OBJECT_0) {int err = capture(frame);if (err != ERROR_CODE_OK) {if (m_onAudioCaptureError != nullptr) {m_onAudioCaptureError(err, m_index);break;}}}av_frame_free(&frame);
}

真正的麦克风采集函数

int WasapiCaptor::capture(AVFrame* frame)
{int err = ERROR_CODE_OK;while (m_running) {uint32_t sampleCount = 0;HRESULT hr = m_captor->GetNextPacketSize(&sampleCount);if (FAILED(hr)) {err = ERROR_CODE_COM_GET_PACKET_SIZE_FAILED;if (hr != AUDCLNT_E_DEVICE_INVALIDATED) {LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] get next packet size, error: %s, hr: %ld", __FUNCTION__,HCMDR_GET_ERROR_DESC(err), hr);}break;}if (sampleCount == 0) {break;}BYTE* buffer = nullptr;DWORD flags = 0;UINT64 devicePos = 0, ts = 0;hr = m_captor->GetBuffer(&buffer, &sampleCount, &flags, &devicePos, &ts);if (FAILED(hr)) {err = ERROR_CODE_COM_GET_BUFFER_FAILED;if (hr != AUDCLNT_E_DEVICE_INVALIDATED) {LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] get buffer, error: %s, hr: %ld", __FUNCTION__,HCMDR_GET_ERROR_DESC(err), hr);}break;}int sampleSize = m_bitsPerSample / 8 * m_channels;// wasapi time unit is 100nsframe->pts = ts * WASAPI_CAPTOR_NS_UNIT;frame->pkt_dts = frame->pts;frame->format = m_sampleFmt;frame->channels = m_channels;frame->sample_rate = m_samplerate;frame->nb_samples = sampleCount;frame->pkt_size = sampleCount * sampleSize;av_samples_fill_arrays(frame->data, frame->linesize, buffer, m_channels, sampleCount, m_sampleFmt, 1);if (m_onAudioCaptureData != nullptr) {m_onAudioCaptureData(frame, m_index);}hr = m_captor->ReleaseBuffer(sampleCount);if (FAILED(hr)) {err = ERROR_CODE_COM_RELEASE_BUFFER_FAILED;LOGGER::Logger::log(LOGGER::LOG_TYPE_ERROR, "[%s] release buffer, error: %s", __FUNCTION__, HCMDR_GET_ERROR_DESC(err));break;}}return err;
}

3、停止采集

int WasapiCaptor::stop()
{int err = ERROR_CODE_OK;if (!m_running) {return err;}m_running = false;SetEvent(m_stopEvent);if (m_renderThread.joinable()) {m_renderThread.join();}if (m_thread.joinable()) {m_thread.join();}if (m_captureClient != nullptr) {m_captureClient->Stop();}if (m_renderClient != nullptr) {m_renderClient->Stop();}return err;
}

至此,可以采集到pcm音频数据了。

【音视频】音频采集-WASAPI(四)相关推荐

  1. android触屏音文件地址,Android音视频-音频采集

    Android的音视频开发是我暂定的一个职业发展的一个方向,通过自学记录一些记了又忘记的知识. 音频基础知识 采样率(samplerate) 蓝色代表模拟音频信号,红色的点代表采样得到的量化数值. 采 ...

  2. AVI音视频封装格式学习(四)——linux系统C语言AVI格式音视频封装应用

    拖了很久的AVI音视频封装实例,花了一天时间终于调完了,兼容性不是太好,但作为参考学习使用应该没有问题.RIFF和AVI以及WAV格式,可以参考前面的一些文章.这里详细介绍将一个H264视频流和一个2 ...

  3. 即时通讯音视频开发(十四):实时音视频数据传输协议介绍

    概述 随着移动互联网的快速发展以及智能终端性能的逐步提高,智能终端间进行实时音视频通讯成为移动互联网发展的一个重要方向.那么如何保证智能终端之间实时音视频数据通讯成为一个很现实的问题. 实际上,实时音 ...

  4. 从零开始学习音视频编程技术(四) FFMPEG的使用

    零开始学习音视频编程技术(四) FFMPEG的使用 原文地址:http://blog.yundiantech.com/?log=blog&id=7 音视频开发中最常做的就是编解码的操作了,以H ...

  5. FFMPEG音视频同步-音视频实时采集并编码推流

    FFMPEG音视频同步-音视频实时采集并编码推流 //------------------------------------------------------------------------- ...

  6. Android IOS WebRTC 音视频开发总结(四二)-- webrtc开发者大会

    Android IOS WebRTC 音视频开发总结(四二)-- webrtc开发者大会 本文主要介绍11月要在北京举办的webrtc开发者全球大会,文章来自博客园RTC.Blacker,支持原创,转 ...

  7. android mediarecorder 输出到流_音视频的采集、编码、封包成 mp4 输出

    使用 Android Camera API 完成音视频的采集.编码.封包成 mp4 输出 基于android.hardware.Camera,创建一个横屏应用,实时预览摄像头图像,实现录像并输出MP4 ...

  8. 直播打断事件处理(音视频SDK高级功能四)

    本篇是即构科技音视频SDK高级功能第四篇,ZegoLiveRoom SDK 打断事件处理,以iOS环境为例. 1.简介 使用 ZegoLiveRoom SDK 直播过程中,可能会被来电.切后台等事件打 ...

  9. 音视频开发(十四):OpenGL 与 OpenGL ES2区别

    什么是OpenGL ES? OpenGL(全写Open Graphics Library)是指定义了一个跨编程语言.跨平台的编程接口规格的专业的图形程序接口.它用于三维图像(二维的亦可),是一个功能强 ...

最新文章

  1. Javascript中的树结构
  2. linux dns chroot配置文件,linxu chroot DNS 配置
  3. hiho一下120周 后缀数组一·重复旋律
  4. 下一代Android或官方支持“App2sd”
  5. struts2开发action 的三种方法以及通配符、路径匹配原则、常量
  6. JFreeChart(七)之气泡图表​​​​​​​
  7. 比tween更有效的tween包.
  8. python博客项目评论_Python 爬虫入门——小项目实战(自动私信博客园某篇博客下的评论人,随机发送一条笑话,完整代码在博文最后)...
  9. 【学习笔记】常用的c/c++面试题
  10. 联想服务器系统初始化失败怎么回事,win10重置初始化失败怎么解决
  11. JZOJ2499 东风谷早苗
  12. 游戏开发入门(一)游戏发展史
  13. iOS GameCenter
  14. 扫描枪识别条码为乱码
  15. CUPS学习二:CUPS概念介绍。
  16. 解决Linux下PermissionDenied问题(给文件授权)
  17. 那些容易被忽视但又非常重要的安全知识
  18. 这是一篇1==“1”?励志:缓解压力 的文字
  19. python常见脚本
  20. 基于UDP的TFTP文件下载与上传

热门文章

  1. CAD文字显示为方框怎么办?CAD文字显示为方框的解决办法
  2. PHPMAILER关于25端口不能用,更换其他端口
  3. 2020东南大学计算机学院夏令营记录
  4. structc 开源框架介绍
  5. Java规范的注释方式(注释方式:单行注释、多行注释、文档注释)
  6. 2021-07-22相机检校
  7. perfect squares java,Perfect Squares
  8. layui-table中勾选框部分勾选的暂时解决办法
  9. 从《浪潮之巅》看互联网
  10. 阿里华为员工跳槽到微软,被鄙视了:他们工资低,整天996,把气氛搞得很差!...