mirror of
https://github.com/BelledonneCommunications/linphone-sdk
synced 2026-08-07 00:23:14 -04:00
146 lines
5.2 KiB
C++
146 lines
5.2 KiB
C++
/*
|
|
* Copyright (c) 2010-2025 Belledonne Communications SARL.
|
|
*
|
|
* This file is part of mswasapi library - modular sound and video processing and streaming Windows Audio Session API
|
|
* sound card plugin for mediastreamer2 (see https://gitlab.linphone.org/BC/public/mswasapi).
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "mswasapi_writer.h"
|
|
#include "mediastreamer2/flowcontrol.h"
|
|
#include "mediastreamer2/mscommon.h"
|
|
#include "mediastreamer2/msticker.h"
|
|
#include "utils.h"
|
|
|
|
static const int flowControlInterval = 5000; // ms
|
|
static const int flowControlThreshold = 40; // ms
|
|
|
|
#ifdef MS2_WINDOWS_UNIVERSAL
|
|
bool MSWASAPIWriter::smInstantiated = false;
|
|
#endif
|
|
|
|
MSWASAPIWriter::MSWASAPIWriter(MSFilter *filter)
|
|
: MSWasapi(filter, MS_SND_CARD_CAP_PLAYBACK), mAudioRenderClient(NULL) {
|
|
}
|
|
|
|
MSWASAPIWriter::~MSWASAPIWriter() {
|
|
}
|
|
|
|
int MSWASAPIWriter::activate() {
|
|
return MSWasapi::activate(false, (void **)&mAudioRenderClient);
|
|
}
|
|
|
|
int MSWASAPIWriter::deactivate() {
|
|
SAFE_RELEASE(mAudioRenderClient)
|
|
return MSWasapi::deactivate();
|
|
}
|
|
|
|
/* We go here everytick, as the filter is declared with the IS_PUMP flag.*/
|
|
int MSWASAPIWriter::feed(MSFilter *f) {
|
|
HRESULT result;
|
|
UINT32 numFramesPadding = 0;
|
|
UINT32 numFramesWritable;
|
|
mblk_t *im;
|
|
|
|
if (!isStarted()) goto error;
|
|
|
|
result = mAudioClient->GetCurrentPadding(&numFramesPadding);
|
|
REPORT_ERROR("mswasapi: Could not get current buffer padding for the MSWASAPI audio output interface [0x%X]", result);
|
|
numFramesWritable = mBufferFrameCount - numFramesPadding;
|
|
REPORT_ERROR("mswasapi: Could not get the frame size for the MSWASAPI audio output interface [0x%X]", result);
|
|
|
|
while ((im = ms_queue_get(f->inputs[0])) != NULL) {
|
|
int inputFrames = (int)(msgdsize(im) / mNBlockAlign);
|
|
int writtenFrames = inputFrames;
|
|
if (msgdsize(im) % 8 !=
|
|
0) // Get one more space to put unexpected data. This is a workaround to a bug from special audio driver
|
|
// that could write/use outside the requested buffer and where it is not on fully 8 octets
|
|
++inputFrames;
|
|
msgpullup(im, -1);
|
|
if (inputFrames >= (int)numFramesWritable) {
|
|
/*This case should not happen because of upstream flow control, except in rare disaster cases.*/
|
|
if (mMinFrameCount != -1)
|
|
ms_error("mswasapi: cannot write output buffer of %i samples, not enough space [%i=%i-%i].",
|
|
inputFrames, numFramesWritable, mBufferFrameCount, numFramesPadding);
|
|
} else {
|
|
BYTE *buffer;
|
|
result = mAudioRenderClient->GetBuffer(inputFrames, &buffer);
|
|
if (result == S_OK) {
|
|
memcpy(buffer, im->b_rptr, im->b_wptr - im->b_rptr);
|
|
result = mAudioRenderClient->ReleaseBuffer(writtenFrames, 0); // Use only the needed frame
|
|
} else {
|
|
ms_error("mswasapi: Could not get buffer from the MSWASAPI audio output interface %i [0x%X]", inputFrames,
|
|
result);
|
|
}
|
|
}
|
|
freemsg(im);
|
|
}
|
|
/* Compute the minimum number of queued samples during a 5 seconds period.*/
|
|
if (mMinFrameCount == -1) mMinFrameCount = numFramesPadding;
|
|
if ((int)numFramesPadding < mMinFrameCount) mMinFrameCount = (int)numFramesPadding;
|
|
if (f->ticker->time % flowControlInterval == 0) {
|
|
int minExcessMs = (mMinFrameCount * 1000) / mCurrentRate;
|
|
if (minExcessMs >= flowControlThreshold) {
|
|
/* Send a notification to request the flow controller to drop samples in excess.*/
|
|
MSAudioFlowControlDropEvent ev;
|
|
ev.flow_control_interval_ms = flowControlInterval;
|
|
ev.drop_ms = minExcessMs / 2;
|
|
if (ev.drop_ms > 0) {
|
|
ms_warning("mswasapi: output buffer was filled with at least %i ms in the last %i ms, asking to drop.",
|
|
(int)ev.drop_ms, flowControlInterval);
|
|
ms_filter_notify(f, MS_AUDIO_FLOW_CONTROL_DROP_EVENT, &ev);
|
|
}
|
|
} else {
|
|
ms_debug("mswasapi: output buffer was filled with a minimum of %i ms in the last %i ms, all is good.",
|
|
(int)((mMinFrameCount * 1000) / mCurrentRate), flowControlInterval);
|
|
}
|
|
mMinFrameCount = -1;
|
|
}
|
|
|
|
error:
|
|
ms_queue_flush(f->inputs[0]);
|
|
return 0;
|
|
}
|
|
|
|
#ifndef MS2_WINDOWS_PHONE
|
|
// Called from main mswasapi.
|
|
MSWASAPIWriterPtr MSWASAPIWriterNew(MSFilter *f) {
|
|
MSWASAPIWriterPtr w = new MSWASAPIWriterWrapper();
|
|
#ifdef MS2_WINDOWS_UNIVERSAL
|
|
w->writer = Make<MSWASAPIWriter>(f);
|
|
#else
|
|
w->writer = new MSWASAPIWriter(f);
|
|
#endif
|
|
return w;
|
|
}
|
|
void MSWASAPIWriterDelete(MSWASAPIWriterPtr ptr) {
|
|
#ifdef MS2_WINDOWS_UNIVERSAL
|
|
ptr->writer->setInstantiated(false);
|
|
#endif
|
|
ptr->writer->unregister();
|
|
ptr->writer->Release();
|
|
ptr->writer = nullptr;
|
|
delete ptr;
|
|
}
|
|
#else
|
|
MSWASAPIWriterPtr MSWASAPIWriterNew(MSFilter *f) {
|
|
return (MSWASAPIWriterPtr) new MSWASAPIWriter(f);
|
|
}
|
|
|
|
void MSWASAPIWriterDelete(MSWASAPIWriterPtr ptr) {
|
|
ptr->writer->unregister();
|
|
delete ptr;
|
|
}
|
|
#endif
|