WebRTC实现背景声音的混流
2021-03-14 14:30
标签:efi soft capture 直播 ioc 采集 during restart release 在Windows上使用WebRTC做视频采集,然后使用RTMP进行直播推流。默认情况下WebRTC只会采集麦克风的声音,而不会采集机器的背景声音。需要编码实现背景声音的采集和混音功能。 Windows提供的API中有音频采集的相关方法,官方也给出了简单的说明和示例,虽然不能运行:)。所以可以通过Windows的API来采集PCM格式的音频,然后通过WebRTC的群聊混音机制来进行音频合成 音频采集部分 声音合成 创建 webrtc::AudioConferenceMixer *audio_mixer_ = nullptr; 在使用多路声音的时候进行混音 WebRTC实现背景声音的混流 标签:efi soft capture 直播 ioc 采集 during restart release 原文地址:https://www.cnblogs.com/vectorli/p/12810196.html背景
思路
核心代码
DWORD AudioCaptureCore::DoCaptureThread()
{
keepRecording_ = true;
HANDLE waitArray[2] = { _hShutdownCaptureEvent, _hCaptureSamplesReadyEvent };
HRESULT hr = S_OK;
LARGE_INTEGER t1;
LARGE_INTEGER t2;
int32_t time(0);
BYTE* syncBuffer = NULL;
UINT32 syncBufIndex = 0;
_readSamples = 0;
// Initialize COM as MTA in this thread.
ScopedCOMInitializer comInit(ScopedCOMInitializer::kMTA);
if (!comInit.succeeded()) {
WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
"failed to initialize COM in capture thread");
return 1;
}
hr = InitCaptureThreadPriority();
if (FAILED(hr))
{
return hr;
}
_Lock();
REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC;
REFERENCE_TIME hnsActualDuration;
UINT32 bufferLength;
UINT32 numFramesAvailable;
IMMDeviceEnumerator *pEnumerator = NULL;
IMMDevice *pDevice = NULL;
WAVEFORMATEX *pwfx = NULL;
UINT32 packetLength = 0;
BOOL bDone = FALSE;
BYTE *pData;
DWORD flags;
hr = CoCreateInstance(CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
IID_IMMDeviceEnumerator, (void**)&pEnumerator);
EXIT_ON_ERROR(hr);
hr = pEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, &pDevice);
EXIT_ON_ERROR(hr);
hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&_ptrAudioClientIn);
EXIT_ON_ERROR(hr);
//
hr = _ptrAudioClientIn->GetMixFormat(&pwfx);
EXIT_ON_ERROR(hr);
WAVEFORMATEX waveFormat;
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
waveFormat.nChannels = 2;
waveFormat.nSamplesPerSec = pwfx->nSamplesPerSec;
waveFormat.nAvgBytesPerSec = pwfx->nSamplesPerSec * 4;
waveFormat.wBitsPerSample = 16;
waveFormat.nBlockAlign = 4;
waveFormat.cbSize = 0;
*pwfx = waveFormat;
hr = _ptrAudioClientIn->Initialize(AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_LOOPBACK,
hnsRequestedDuration,
0,
pwfx,
NULL);
EXIT_ON_ERROR(hr);
// Set the VoE format equal to the AEC output format.
_recAudioFrameSize = pwfx->nBlockAlign;
_recSampleRate = pwfx->nSamplesPerSec;
_recBlockSize = pwfx->nSamplesPerSec / 100;
_recChannels = pwfx->nChannels;
if (_ptrAudioBuffer)
{
// Update the audio buffer with the selected parameters
_ptrAudioBuffer->SetRecordingSampleRate(_recSampleRate);
_ptrAudioBuffer->SetRecordingChannels((uint8_t)_recChannels);
}
else
{
// We can enter this state during CoreAudioIsSupported() when no AudioDeviceImplementation
// has been created, hence the AudioDeviceBuffer does not exist.
// It is OK to end up here since we don‘t initiate any media in CoreAudioIsSupported().
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "AudioDeviceBuffer must be attached before streaming can start");
}
// Get the size of the allocated buffer.
hr = _ptrAudioClientIn->GetBufferSize(&bufferLength);
EXIT_ON_ERROR(hr);
hr = _ptrAudioClientIn->GetService(__uuidof(IAudioCaptureClient), (void**)&_ptrCaptureClient);
EXIT_ON_ERROR(hr);
// Notify the audio sink which format to use.
// 如上一行注释,以下的代码是将获取到的音频格式传给另外的类(自己定义的),同样的,因为
// 手动制定了音频格式,所以就不需要通知了
// hr = pMySink->SetFormat(pwfx);
// EXIT_ON_ERROR(hr)
// Calculate the actual duration of the allocated buffer.
hnsActualDuration = (double)REFTIMES_PER_SEC * bufferLength / pwfx->nSamplesPerSec;
//hr = _ptrAudioClientIn->Start(); // Start recording.
//EXIT_ON_ERROR(hr);
// Get size of capturing buffer (length is expressed as the number of audio frames the buffer can hold).
// This value is fixed during the capturing session.
//
if (_ptrAudioClientIn == NULL)
{
WEBRTC_TRACE(kTraceError, kTraceAudioDevice, _id,
"input state has been modified before capture loop starts.");
return 1;
}
hr = _ptrAudioClientIn->GetBufferSize(&bufferLength);
EXIT_ON_ERROR(hr);
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] size of buffer : %u", bufferLength);
// Allocate memory for sync buffer.
// It is used for compensation between native 44.1 and internal 44.0 and
// for cases when the capture buffer is larger than 10ms.
//
const UINT32 syncBufferSize = 2 * (bufferLength * _recAudioFrameSize);
syncBuffer = new BYTE[syncBufferSize];
if (syncBuffer == NULL)
{
return (DWORD)E_POINTER;
}
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] size of sync buffer : %u [bytes]", syncBufferSize);
// Get maximum latency for the current stream (will not change for the lifetime of the IAudioClient object).
//
REFERENCE_TIME latency;
_ptrAudioClientIn->GetStreamLatency(&latency);
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] max stream latency : %u (%3.2f ms)",
(DWORD)latency, (double)(latency / 10000.0));
// Get the length of the periodic interval separating successive processing passes by
// the audio engine on the data in the endpoint buffer.
//
REFERENCE_TIME devPeriod = 0;
REFERENCE_TIME devPeriodMin = 0;
_ptrAudioClientIn->GetDevicePeriod(&devPeriod, &devPeriodMin);
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] device period : %u (%3.2f ms)",
(DWORD)devPeriod, (double)(devPeriod / 10000.0));
double extraDelayMS = (double)((latency + devPeriod) / 10000.0);
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] extraDelayMS : %3.2f", extraDelayMS);
double endpointBufferSizeMS = 10.0 * ((double)bufferLength / (double)_recBlockSize);
WEBRTC_TRACE(kTraceInfo, kTraceAudioDevice, _id, "[CAPT] endpointBufferSizeMS : %3.2f", endpointBufferSizeMS);
// Start up the capturing stream.
//
hr = _ptrAudioClientIn->Start();
EXIT_ON_ERROR(hr);
_UnLock();
// Set event which will ensure that the calling thread modifies the recording state to true.
//
SetEvent(_hCaptureStartedEvent);
// >> ---------------------------- THREAD LOOP ----------------------------
while (keepRecording_)
{
BYTE *pData = 0;
UINT32 framesAvailable = 0;
DWORD flags = 0;
UINT64 recTime = 0;
UINT64 recPos = 0;
std::cout "bgm audio capturing" std::endl;
_Lock();
// Sanity check to ensure that essential states are not modified
// during the unlocked period.
if (_ptrCaptureClient == NULL || _ptrAudioClientIn == NULL)
{
_UnLock();
WEBRTC_TRACE(kTraceCritical, kTraceAudioDevice, _id,
"input state has been modified during unlocked period");
goto Exit;
}
// Find out how much capture data is available
//
hr = _ptrCaptureClient->GetBuffer(&pData, // packet which is ready to be read by used
&framesAvailable, // #frames in the captured packet (can be zero)
&flags, // support flags (check)
&recPos, // device position of first audio frame in data packet
&recTime); // value of performance counter at the time of recording the first audio frame
if (SUCCEEDED(hr))
{
if (AUDCLNT_S_BUFFER_EMPTY == hr)
{
// Buffer was empty => start waiting for a new capture notification event
_UnLock();
continue;
}
if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
{
// Treat all of the data in the packet as silence and ignore the actual data values.
WEBRTC_TRACE(kTraceWarning, kTraceAudioDevice, _id, "AUDCLNT_BUFFERFLAGS_SILENT");
pData = NULL;
}
assert(framesAvailable != 0);
if (pData)
{
CopyMemory(&syncBuffer[syncBufIndex*_recAudioFrameSize], pData, framesAvailable*_recAudioFrameSize);
}
else
{
ZeroMemory(&syncBuffer[syncBufIndex*_recAudioFrameSize], framesAvailable*_recAudioFrameSize);
}
assert(syncBufferSize >= (syncBufIndex*_recAudioFrameSize) + framesAvailable*_recAudioFrameSize);
// Release the capture buffer
//
hr = _ptrCaptureClient->ReleaseBuffer(framesAvailable);
EXIT_ON_ERROR(hr);
_readSamples += framesAvailable;
syncBufIndex += framesAvailable;
QueryPerformanceCounter(&t1);
// Get the current recording and playout delay.
uint32_t sndCardRecDelay = (uint32_t)
(((((UINT64)t1.QuadPart * _perfCounterFactor) - recTime)
/ 10000) + (10 * syncBufIndex) / _recBlockSize - 10);
uint32_t sndCardPlayDelay =
static_cast
int32_t AnyRtmpCore::RecordedDataIsAvailable(const void* audioSamples, const size_t nSamples,
const size_t nBytesPerSample, const size_t nChannels, const uint32_t samplesPerSec, const uint32_t totalDelayMS,
const int32_t clockDrift, const uint32_t currentMicLevel, const bool keyPressed, uint32_t& newMicLevel)
{
std::cout "[-----------] record data avaliable " std::endl;
rtc::CritScope cs(&cs_audio_record_);
if (microphone_enable_ && bgm_enable_) {
audio_device_mixer_ptr_->RecordedDataIsAvailable(audioSamples, nSamples,
nBytesPerSample, nChannels, samplesPerSec, totalDelayMS,
clockDrift, currentMicLevel, keyPressed, newMicLevel);
if (audio_mixer_) {
audio_mixer_->Process();
}
}
else
{
// 当只有一种声音时,不进行混音
if (audio_record_callback_) {
if (audio_record_sample_hz_ != samplesPerSec || nChannels != audio_record_channels_) {
int16_t temp_output[kMaxDataSizeSamples];
int samples_per_channel_int = resampler_record_.Resample10Msec((int16_t*)audioSamples, samplesPerSec * nChannels,
audio_record_sample_hz_ * audio_record_channels_, 1, kMaxDataSizeSamples, temp_output);
audio_record_callback_->OnRecordAudio(temp_output, audio_record_sample_hz_ / 100, nBytesPerSample, audio_record_channels_, audio_record_sample_hz_, totalDelayMS);
}
else {
audio_record_callback_->OnRecordAudio(audioSamples, nSamples, nBytesPerSample, audio_record_channels_, samplesPerSec, totalDelayMS);
}
}
}
return 0;
}
下一篇:配置对港口的HTTPS访问