From 165f86154d2b9e970cbe727902f27b2b4a839a85 Mon Sep 17 00:00:00 2001 From: Anton Fedchin Date: Sat, 17 Mar 2018 01:09:24 +0300 Subject: AudioEngine: XAudio - fix audio playback for live streams. --- xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp | 39 +++++++----- xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h | 91 ++++++++++++++++----------- 2 files changed, 77 insertions(+), 53 deletions(-) diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp b/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp index 029e858480..2efbaf5673 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp +++ b/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.cpp @@ -70,6 +70,7 @@ CAESinkXAudio::CAESinkXAudio() : m_dwFrameSize(0), m_dwBufferLen(0), m_sinkFrames(0), + m_framesInBuffers(0), m_running(false), m_initialized(false), m_isSuspended(false), @@ -168,6 +169,7 @@ void CAESinkXAudio::Deinitialize() m_sourceVoice->Stop(); m_sourceVoice->FlushSourceBuffers(); m_sinkFrames = 0; + m_framesInBuffers = 0; } catch (...) { @@ -243,13 +245,17 @@ unsigned int CAESinkXAudio::AddPackets(uint8_t **data, unsigned int frames, unsi LARGE_INTEGER timerFreq; #endif size_t dataLenght = frames * m_format.m_frameSize; - uint8_t* buff = new uint8_t[dataLenght]; - memcpy(buff, data[0] + offset * m_format.m_frameSize, dataLenght); + + struct buffer_ctx *ctx = new buffer_ctx; + ctx->data = new uint8_t[dataLenght]; + ctx->frames = frames; + ctx->sink = this; + memcpy(ctx->data, data[0] + offset * m_format.m_frameSize, dataLenght); XAUDIO2_BUFFER xbuffer = { 0 }; xbuffer.AudioBytes = dataLenght; - xbuffer.pAudioData = buff; - xbuffer.pContext = buff; + xbuffer.pAudioData = ctx->data; + xbuffer.pContext = ctx; if (!m_running) //first time called, pre-fill buffer then start voice { @@ -257,19 +263,20 @@ unsigned int CAESinkXAudio::AddPackets(uint8_t **data, unsigned int frames, unsi hr = m_sourceVoice->SubmitSourceBuffer(&xbuffer); if (FAILED(hr)) { - CLog::Log(LOGERROR, __FUNCTION__ " SourceVoice submit buffer failed due to %s", WASAPIErrToStr(hr)); - delete[] buff; + CLog::LogF(LOGERROR, "voice submit buffer failed due to %s", WASAPIErrToStr(hr)); + delete ctx; return 0; } hr = m_sourceVoice->Start(0, XAUDIO2_COMMIT_NOW); if (FAILED(hr)) { - CLog::Log(LOGERROR, __FUNCTION__ " SourceVoice start failed due to %s", WASAPIErrToStr(hr)); + CLog::LogF(LOGERROR, "voice start failed due to %s", WASAPIErrToStr(hr)); m_isDirty = true; //flag new device or re-init needed - delete[] buff; + delete ctx; return INT_MAX; } m_sinkFrames += frames; + m_framesInBuffers += frames; m_running = true; //signal that we're processing frames return frames; } @@ -281,15 +288,16 @@ unsigned int CAESinkXAudio::AddPackets(uint8_t **data, unsigned int frames, unsi #endif /* Wait for Audio Driver to tell us it's got a buffer available */ - XAUDIO2_VOICE_STATE state; - while (m_sourceVoice->GetState(&state), state.BuffersQueued >= XAUDIO_BUFFERS_IN_QUEUE) + //XAUDIO2_VOICE_STATE state; + //while (m_sourceVoice->GetState(&state), state.BuffersQueued >= XAUDIO_BUFFERS_IN_QUEUE) + while (m_format.m_frames * XAUDIO_BUFFERS_IN_QUEUE <= m_framesInBuffers.load()) { DWORD eventAudioCallback; eventAudioCallback = WaitForSingleObjectEx(m_voiceCallback.mBufferEnd.get(), 1100, TRUE); if (eventAudioCallback != WAIT_OBJECT_0) { - CLog::Log(LOGERROR, __FUNCTION__": Endpoint Buffer timed out"); - delete[] buff; + CLog::LogF(LOGERROR, "voice buffer timed out"); + delete ctx; return INT_MAX; } } @@ -305,7 +313,7 @@ unsigned int CAESinkXAudio::AddPackets(uint8_t **data, unsigned int frames, unsi if (m_avgTimeWaiting < 3.0) { - CLog::Log(LOGDEBUG, __FUNCTION__": Possible AQ Loss: Avg. Time Waiting for Audio Driver callback : %dmsec", (int)m_avgTimeWaiting); + CLog::LogF(LOGDEBUG, "Possible AQ Loss: Avg. Time Waiting for Audio Driver callback : %dmsec", (int)m_avgTimeWaiting); } #endif @@ -313,13 +321,14 @@ unsigned int CAESinkXAudio::AddPackets(uint8_t **data, unsigned int frames, unsi if (FAILED(hr)) { #ifdef _DEBUG - CLog::Log(LOGERROR, __FUNCTION__": SubmitSourceBuffer failed due to %s", WASAPIErrToStr(hr)); + CLog::LogF(LOGERROR, "submiting buffer failed due to %s", WASAPIErrToStr(hr)); #endif - delete[] buff; + delete ctx; return INT_MAX; } m_sinkFrames += frames; + m_framesInBuffers += frames; return frames; } diff --git a/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h b/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h index 6ddabaabc5..4812a52ea9 100644 --- a/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h +++ b/xbmc/cores/AudioEngine/Sinks/AESinkXAudio.h @@ -32,44 +32,6 @@ #include #pragma comment(lib,"xaudio2.lib") -struct VoiceCallback : public IXAudio2VoiceCallback -{ - VoiceCallback() - { - mBufferEnd.reset(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); - if (!mBufferEnd) - { - throw std::exception("CreateEvent"); - } - } - virtual ~VoiceCallback() { } - - STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) override {} - STDMETHOD_(void, OnVoiceProcessingPassEnd)() override {} - STDMETHOD_(void, OnStreamEnd)() override {} - STDMETHOD_(void, OnBufferStart)(void*) override {} - STDMETHOD_(void, OnBufferEnd)(void* context) override - { - SetEvent(mBufferEnd.get()); - uint8_t *buff = static_cast(context); - delete[] buff; - } - - STDMETHOD_(void, OnLoopEnd)(void*) override {} - STDMETHOD_(void, OnVoiceError)(void*, HRESULT) override {} - - struct handle_closer - { - void operator()(HANDLE h) - { - assert(h != INVALID_HANDLE_VALUE); - if (h) - CloseHandle(h); - } - }; - std::unique_ptr mBufferEnd; -}; - class CAESinkXAudio : public IAESink { public: @@ -93,6 +55,58 @@ public: static void EnumerateDevicesEx(AEDeviceInfoList &deviceInfoList, bool force = false); private: + struct buffer_ctx + { + uint8_t *data; + uint32_t frames; + CAESinkXAudio* sink; + + ~buffer_ctx() + { + delete[] data; + sink->m_framesInBuffers -= frames; + sink = nullptr; + } + }; + + struct VoiceCallback : public IXAudio2VoiceCallback + { + VoiceCallback() + { + mBufferEnd.reset(CreateEventEx(nullptr, nullptr, 0, EVENT_MODIFY_STATE | SYNCHRONIZE)); + if (!mBufferEnd) + { + throw std::exception("CreateEvent"); + } + } + virtual ~VoiceCallback() { } + + STDMETHOD_(void, OnVoiceProcessingPassStart) (UINT32) override {} + STDMETHOD_(void, OnVoiceProcessingPassEnd)() override {} + STDMETHOD_(void, OnStreamEnd)() override {} + STDMETHOD_(void, OnBufferStart)(void*) override {} + STDMETHOD_(void, OnBufferEnd)(void* context) override + { + SetEvent(mBufferEnd.get()); + struct buffer_ctx *ctx = static_cast(context); + delete ctx; + } + + STDMETHOD_(void, OnLoopEnd)(void*) override {} + STDMETHOD_(void, OnVoiceError)(void*, HRESULT) override {} + + struct handle_closer + { + void operator()(HANDLE h) + { + assert(h != INVALID_HANDLE_VALUE); + if (h) + CloseHandle(h); + } + }; + std::unique_ptr mBufferEnd; + }; + bool InitializeInternal(std::string deviceId, AEAudioFormat &format); bool IsUSBDevice(); @@ -116,6 +130,7 @@ private: unsigned int m_dwFrameSize; unsigned int m_dwBufferLen; uint64_t m_sinkFrames; + std::atomic m_framesInBuffers; double m_avgTimeWaiting; /* time between next buffer of data from SoftAE and driver call for data */ -- cgit v1.2.3