diff options
author | Rainer Hochecker <fernetmenta@online.de> | 2015-09-06 14:10:50 +0200 |
---|---|---|
committer | Rainer Hochecker <fernetmenta@online.de> | 2015-12-06 20:33:45 +0100 |
commit | 337907d462fd8e16fe45ee403c146625923a982b (patch) | |
tree | 78880c44f513337985913242aad4e7f713c4c539 | |
parent | 3f39ddaf4c67fabcff3ff0406b4e63099e1162b5 (diff) |
move audio sync to AE
20 files changed, 567 insertions, 297 deletions
diff --git a/addons/library.kodi.audioengine/libKODI_audioengine.h b/addons/library.kodi.audioengine/libKODI_audioengine.h index 58e59cf9d0..bb4093094f 100644 --- a/addons/library.kodi.audioengine/libKODI_audioengine.h +++ b/addons/library.kodi.audioengine/libKODI_audioengine.h @@ -318,7 +318,7 @@ public: * @note This function may return false if the stream is not resampling, if you wish to use this be sure to set the AESTREAM_FORCE_RESAMPLE option * @param ratio the new sample rate ratio, calculated by ((double)desiredRate / (double)GetSampleRate()) */ - virtual bool SetResampleRatio(double Ratio); + virtual void SetResampleRatio(double Ratio); /** * Sginal a clock change diff --git a/lib/addons/library.kodi.audioengine/libKODI_audioengine.cpp b/lib/addons/library.kodi.audioengine/libKODI_audioengine.cpp index e688d70e85..0d51941a2e 100644 --- a/lib/addons/library.kodi.audioengine/libKODI_audioengine.cpp +++ b/lib/addons/library.kodi.audioengine/libKODI_audioengine.cpp @@ -224,9 +224,9 @@ double CAddonAEStream::GetResampleRatio() return ((CB_AudioEngineLib*)m_Callbacks)->AEStream_GetResampleRatio(((AddonCB*)m_AddonHandle)->addonData, m_StreamHandle); } -bool CAddonAEStream::SetResampleRatio(double Ratio) +void CAddonAEStream::SetResampleRatio(double Ratio) { - return ((CB_AudioEngineLib*)m_Callbacks)->AEStream_SetResampleRatio(((AddonCB*)m_AddonHandle)->addonData, m_StreamHandle, Ratio); + ((CB_AudioEngineLib*)m_Callbacks)->AEStream_SetResampleRatio(((AddonCB*)m_AddonHandle)->addonData, m_StreamHandle, Ratio); } void CAddonAEStream::Discontinuity() diff --git a/xbmc/addons/AddonCallbacks.h b/xbmc/addons/AddonCallbacks.h index 076fc35326..6ab24ec475 100644 --- a/xbmc/addons/AddonCallbacks.h +++ b/xbmc/addons/AddonCallbacks.h @@ -429,7 +429,7 @@ typedef const unsigned int (*AudioEngine_Stream_GetSampleRate)(void *addonD typedef const unsigned int (*AudioEngine_Stream_GetEncodedSampleRate)(void *addonData, AEStreamHandle *handle); typedef const AEDataFormat (*AudioEngine_Stream_GetDataFormat)(void *addonData, AEStreamHandle *handle); typedef double (*AudioEngine_Stream_GetResampleRatio)(void *addonData, AEStreamHandle *handle); -typedef bool (*AudioEngine_Stream_SetResampleRatio)(void *addonData, AEStreamHandle *handle, double Ratio); +typedef void (*AudioEngine_Stream_SetResampleRatio)(void *addonData, AEStreamHandle *handle, double Ratio); typedef void (*AudioEngine_Stream_Discontinuity)(void *addonData, AEStreamHandle *handle); typedef struct CB_AudioEngineLib diff --git a/xbmc/addons/AddonCallbacksAudioEngine.cpp b/xbmc/addons/AddonCallbacksAudioEngine.cpp index 45ef5c2e06..2a70117696 100644 --- a/xbmc/addons/AddonCallbacksAudioEngine.cpp +++ b/xbmc/addons/AddonCallbacksAudioEngine.cpp @@ -401,17 +401,17 @@ double CAddonCallbacksAudioEngine::AEStream_GetResampleRatio(void *AddonData, AE return ((IAEStream*)StreamHandle)->GetResampleRatio(); } -bool CAddonCallbacksAudioEngine::AEStream_SetResampleRatio(void *AddonData, AEStreamHandle *StreamHandle, double Ratio) +void CAddonCallbacksAudioEngine::AEStream_SetResampleRatio(void *AddonData, AEStreamHandle *StreamHandle, double Ratio) { // prevent compiler warnings void *addonData = AddonData; if (!addonData || !StreamHandle) { CLog::Log(LOGERROR, "libKODI_audioengine - %s - invalid stream data", __FUNCTION__); - return false; + return; } - return ((IAEStream*)StreamHandle)->SetResampleRatio(Ratio); + ((IAEStream*)StreamHandle)->SetResampleRatio(Ratio); } void CAddonCallbacksAudioEngine::AEStream_Discontinuity(void *AddonData, AEStreamHandle *StreamHandle) diff --git a/xbmc/addons/AddonCallbacksAudioEngine.h b/xbmc/addons/AddonCallbacksAudioEngine.h index 4facdfcfb7..3106cea7ba 100644 --- a/xbmc/addons/AddonCallbacksAudioEngine.h +++ b/xbmc/addons/AddonCallbacksAudioEngine.h @@ -205,7 +205,7 @@ public: * @note This function may return false if the stream is not resampling, if you wish to use this be sure to set the AESTREAM_FORCE_RESAMPLE option * @param ratio the new sample rate ratio, calculated by ((double)desiredRate / (double)GetSampleRate()) */ - static bool AEStream_SetResampleRatio(void *AddonData, AEStreamHandle *StreamHandle, double Ratio); + static void AEStream_SetResampleRatio(void *AddonData, AEStreamHandle *StreamHandle, double Ratio); /** * Sginal a clock change diff --git a/xbmc/cores/AudioEngine/AEFactory.cpp b/xbmc/cores/AudioEngine/AEFactory.cpp index 7b80501541..56b7e3d781 100644 --- a/xbmc/cores/AudioEngine/AEFactory.cpp +++ b/xbmc/cores/AudioEngine/AEFactory.cpp @@ -271,20 +271,20 @@ void CAEFactory::Shutdown() } IAEStream *CAEFactory::MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, - unsigned int encodedSampleRate, CAEChannelInfo channelLayout, unsigned int options) + unsigned int encodedSampleRate, CAEChannelInfo channelLayout, unsigned int options, IAEClockCallback *clock) { if(AE) - return AE->MakeStream(dataFormat, sampleRate, encodedSampleRate, channelLayout, options); + return AE->MakeStream(dataFormat, sampleRate, encodedSampleRate, channelLayout, options, clock); return NULL; } -IAEStream *CAEFactory::FreeStream(IAEStream *stream) +bool CAEFactory::FreeStream(IAEStream *stream) { if(AE) return AE->FreeStream(stream); - return NULL; + return false; } void CAEFactory::GarbageCollect() diff --git a/xbmc/cores/AudioEngine/AEFactory.h b/xbmc/cores/AudioEngine/AEFactory.h index ba303fd515..1525f2ed47 100644 --- a/xbmc/cores/AudioEngine/AEFactory.h +++ b/xbmc/cores/AudioEngine/AEFactory.h @@ -60,8 +60,8 @@ public: static void SetVolume(const float volume); static void Shutdown(); static IAEStream *MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, - unsigned int encodedSampleRate, CAEChannelInfo channelLayout, unsigned int options = 0); - static IAEStream *FreeStream(IAEStream *stream); + unsigned int encodedSampleRate, CAEChannelInfo channelLayout, unsigned int options = 0, IAEClockCallback *clock = NULL); + static bool FreeStream(IAEStream *stream); static void GarbageCollect(); static void SettingOptionsAudioDevicesFiller(const CSetting *setting, std::vector< std::pair<std::string, std::string> > &list, std::string ¤t, void *data); diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp index 6d34e25755..3998947bc3 100644 --- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp +++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp @@ -94,12 +94,40 @@ void CEngineStats::GetDelay(AEDelayStatus& status, CActiveAEStream *stream) { CSingleLock lock(m_lock); status = m_sinkDelay; - status.delay += (double)m_bufferedSamples / m_sinkSampleRate; + if (stream->m_resampleBuffers) + status.delay += stream->m_bufferedTime / stream->m_resampleBuffers->m_resampleRatio; status.delay += m_sinkLatency; status.delay += stream->m_bufferedTime / stream->m_streamResampleRatio; } +// this is used to sync a/v so we need to add sink latency here +void CEngineStats::GetSyncInfo(CAESyncInfo& info, CActiveAEStream *stream) +{ + CSingleLock lock(m_lock); + AEDelayStatus status; + status = m_sinkDelay; + status.delay += (double)m_bufferedSamples / m_sinkSampleRate; + + status.delay += m_sinkLatency; + if (stream->m_resampleBuffers) + status.delay += stream->m_bufferedTime / stream->m_resampleBuffers->m_resampleRatio; + + info.delay = status.GetDelay(); + info.error = stream->m_syncError.GetLastError(info.errortime); + info.state = CAESyncInfo::SYNC_OFF; + if (stream->m_resampleBuffers) + info.rr = stream->m_resampleBuffers->m_resampleRatio; + + if (stream->m_pClock) + { + if (stream->m_syncClock) + info.state = CAESyncInfo::SYNC_ACTIVE; + else + info.state = CAESyncInfo::SYNC_PLAY; + } +} + float CEngineStats::GetCacheTime(CActiveAEStream *stream) { CSingleLock lock(m_lock); @@ -283,6 +311,15 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg) case CActiveAEControlProtocol::APPFOCUSED: m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::APPFOCUSED, msg->data, sizeof(bool)); return; + case CActiveAEControlProtocol::STREAMRESAMPLEMODE: + MsgStreamParameter *par; + par = (MsgStreamParameter*)msg->data; + if (par->stream) + { + par->stream->m_resampleMode = par->parameter.int_par; + par->stream->m_resampleIntegral = 0.0; + } + return; default: break; } @@ -304,6 +341,7 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg) CActiveAEStream *stream; stream = *(CActiveAEStream**)msg->data; DiscardStream(stream); + msg->Reply(CActiveAEDataProtocol::ACC); return; case CActiveAEDataProtocol::FREESOUND: sound = *(CActiveAESound**)msg->data; @@ -519,6 +557,7 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg) case CActiveAEControlProtocol::RESUMESTREAM: stream = *(CActiveAEStream**)msg->data; stream->m_paused = false; + stream->m_syncClock = CActiveAEStream::STARTSYNC; streaming = true; m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::STREAMING, &streaming, sizeof(bool)); m_extTimeout = 0; @@ -638,6 +677,7 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg) case CActiveAEDataProtocol::FREESTREAM: stream = *(CActiveAEStream**)msg->data; DiscardStream(stream); + msg->Reply(CActiveAEDataProtocol::ACC); if (m_streams.empty()) { if (m_extKeepConfig) @@ -762,6 +802,7 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg) CActiveAEStream *stream; stream = *(CActiveAEStream**)msg->data; stream->m_paused = false; + stream->m_syncClock = CActiveAEStream::STARTSYNC; m_state = AE_TOP_CONFIGURED_PLAY; m_extTimeout = 0; return; @@ -1223,6 +1264,10 @@ void CActiveAE::Configure(AEAudioFormat *desiredFmt) m_vizInitialized = false; } } + + // buffers need to sync + m_silenceBuffers = new CActiveAEBufferPool(outputFormat); + m_silenceBuffers->Create(500); } // resample buffers for sink @@ -1292,6 +1337,8 @@ CActiveAEStream* CActiveAE::CreateStream(MsgStreamNew *streamMsg) stream->m_fadingSamples = 0; stream->m_started = false; stream->m_clockId = m_stats.Discontinuity(true); + stream->m_resampleMode = 0; + stream->m_syncClock = CActiveAEStream::STARTSYNC; if (streamMsg->options & AESTREAM_PAUSED) { @@ -1307,6 +1354,8 @@ CActiveAEStream* CActiveAE::CreateStream(MsgStreamNew *streamMsg) stream->m_bypassDSP = true; } + stream->m_pClock = streamMsg->clock; + m_streams.push_back(stream); return stream; @@ -1351,6 +1400,7 @@ void CActiveAE::SFlushStream(CActiveAEStream *stream) stream->m_streamPort->Purge(); stream->m_bufferedTime = 0.0; stream->m_paused = false; + stream->m_syncClock = CActiveAEStream::STARTSYNC; // flush the engine if we only have a single stream if (m_streams.size() == 1) @@ -1799,6 +1849,28 @@ bool CActiveAE::RunStages() if (m_stats.GetWaterLevel() < MAX_WATER_LEVEL && (m_mode != MODE_TRANSCODE || (m_encoderBuffers && !m_encoderBuffers->m_freeSamples.empty()))) { + // calculate sync error + for (it = m_streams.begin(); it != m_streams.end(); ++it) + { + if ((*it)->m_paused || !(*it)->m_started || !(*it)->m_resampleBuffers || !(*it)->m_pClock) + continue; + + if ((*it)->m_resampleBuffers->m_outputSamples.empty()) + continue; + + CSampleBuffer *buf = (*it)->m_resampleBuffers->m_outputSamples.front(); + if (buf->timestamp) + { + AEDelayStatus status; + m_stats.GetDelay(status); + double pts = buf->timestamp - (buf->pkt_start_offset * 1000 / buf->pkt->config.sample_rate); + double delay = status.GetDelay() * 1000; + double playingPts = pts - delay; + double error = playingPts - (*it)->m_pClock->GetClock(); + (*it)->m_syncError.Add(error); + } + } + // mix streams and sounds sounds if (m_mode != MODE_RAW) { @@ -1839,6 +1911,14 @@ bool CActiveAE::RunStages() if (!(*it)->m_resampleBuffers->m_outputSamples.empty()) { + CSampleBuffer *tmp = SyncStream(*it); + if (tmp) + { + if (!out) + out = tmp; + continue; + } + (*it)->m_started = true; if (!out) @@ -2101,8 +2181,13 @@ bool CActiveAE::RunStages() { if (!(*it)->m_resampleBuffers->m_outputSamples.empty() && !(*it)->m_paused) { - buffer = (*it)->m_resampleBuffers->m_outputSamples.front(); - (*it)->m_resampleBuffers->m_outputSamples.pop_front(); + (*it)->m_started = true; + buffer = SyncStream(*it); + if (!buffer) + { + buffer = (*it)->m_resampleBuffers->m_outputSamples.front(); + (*it)->m_resampleBuffers->m_outputSamples.pop_front(); + } m_stats.AddSamples(buffer->pkt->nb_samples, m_streams); m_sinkBuffers->m_inputSamples.push_back(buffer); } @@ -2148,6 +2233,140 @@ bool CActiveAE::HasWork() return false; } +CSampleBuffer* CActiveAE::SyncStream(CActiveAEStream *stream) +{ + CSampleBuffer *ret = NULL; + + if (!stream->m_pClock) + return ret; + + if (stream->m_syncClock == CActiveAEStream::STARTSYNC) + { + stream->m_syncClock = CActiveAEStream::MUTE; + stream->m_syncError.Flush(100); + stream->m_resampleBuffers->m_resampleRatio = 1.0; + stream->m_resampleIntegral = 0; + CLog::Log(LOGDEBUG,"ActiveAE - start sync of audio stream"); + } + + double error; + double threshold = 100; + if (stream->m_resampleMode) + threshold *= 2; + + bool newerror = stream->m_syncError.Get(error, stream->m_syncClock ? 100 : 1000); + + if (newerror && fabs(error) > threshold && stream->m_syncClock == CActiveAEStream::INSYNC) + { + stream->m_syncClock = CActiveAEStream::ADJUST; + stream->m_resampleBuffers->m_resampleRatio = 1.0; + stream->m_resampleIntegral = 0; + stream->m_lastSyncError = error; + CLog::Log(LOGDEBUG,"ActiveAE::SyncStream - average error %f above threshold of %f", error, threshold); + } + else if (newerror && stream->m_syncClock == CActiveAEStream::MUTE) + { + stream->m_syncClock = CActiveAEStream::ADJUST; + stream->m_lastSyncError = error; + CLog::Log(LOGDEBUG,"ActiveAE::SyncStream - average error of %f, start adjusting", error); + } + + if (stream->m_syncClock == CActiveAEStream::MUTE) + { + CSampleBuffer *buf = stream->m_resampleBuffers->m_outputSamples.front(); + for(int i=0; i<buf->pkt->planes; i++) + { + memset(buf->pkt->data[i], 0, buf->pkt->linesize); + } + } + else if (stream->m_syncClock == CActiveAEStream::ADJUST) + { + if (error > 0) + { + ret = m_silenceBuffers->GetFreeBuffer(); + if (ret) + { + int framesToDelay = error / 1000 * ret->pkt->config.sample_rate; + if (framesToDelay > ret->pkt->max_nb_samples) + framesToDelay = ret->pkt->max_nb_samples; + if (m_mode == MODE_TRANSCODE) + { + if (framesToDelay > m_encoderFormat.m_frames / 2) + framesToDelay = m_encoderFormat.m_frames; + else + framesToDelay = 0; + } + ret->pkt->nb_samples = framesToDelay; + for(int i=0; i<ret->pkt->planes; i++) + { + memset(ret->pkt->data[i], 0, ret->pkt->linesize); + } + stream->m_syncError.Correction(-framesToDelay*1000/ret->pkt->config.sample_rate); + error -= framesToDelay*1000/ret->pkt->config.sample_rate; + } + } + else + { + CSampleBuffer *buf = stream->m_resampleBuffers->m_outputSamples.front(); + int framesToSkip = -error / 1000 * buf->pkt->config.sample_rate; + if (framesToSkip > buf->pkt->nb_samples) + framesToSkip = buf->pkt->nb_samples; + if (m_mode == MODE_TRANSCODE) + { + if (framesToSkip > m_encoderFormat.m_frames / 2) + framesToSkip = buf->pkt->nb_samples; + else + framesToSkip = 0; + } + int bytesToSkip = framesToSkip*buf->pkt->bytes_per_sample/buf->pkt->planes; + for(int i=0; i<buf->pkt->planes; i++) + { + memmove(buf->pkt->data[i], buf->pkt->data[i]+bytesToSkip, buf->pkt->linesize - bytesToSkip); + } + buf->pkt->nb_samples -= framesToSkip; + stream->m_syncError.Correction(framesToSkip*1000/buf->pkt->config.sample_rate); + error += framesToSkip*1000/buf->pkt->config.sample_rate; + } + + if (fabs(error) < 30) + { + if (stream->m_lastSyncError > threshold * 2) + { + stream->m_syncClock = CActiveAEStream::MUTE; + stream->m_syncError.Flush(100); + CLog::Log(LOGDEBUG,"ActiveAE::SyncStream - average error %f, last average error: %f", error, stream->m_lastSyncError); + stream->m_lastSyncError = error; + } + else + { + stream->m_syncClock = CActiveAEStream::INSYNC; + stream->m_syncError.Flush(1000); + stream->m_resampleIntegral = 0; + stream->m_resampleBuffers->m_resampleRatio = 1.0; + CLog::Log(LOGDEBUG,"ActiveAE::SyncStream - average error %f below threshold of %f", error, 30.0); + } + } + + return ret; + } + + if (!newerror || stream->m_syncClock != CActiveAEStream::INSYNC) + return ret; + + if (stream->m_resampleMode) + { + if (stream->m_resampleBuffers) + { + stream->m_resampleBuffers->m_resampleRatio = stream->CalcResampleRatio(error); + } + } + else if (stream->m_resampleBuffers) + { + stream->m_resampleBuffers->m_resampleRatio = 1.0; + } + return ret; +} + void CActiveAE::MixSounds(CSoundPacket &dstSample) { if (m_sounds_playing.empty()) @@ -2854,7 +3073,7 @@ bool CActiveAE::ResampleSound(CActiveAESound *sound) // Streams //----------------------------------------------------------------------------- -IAEStream *CActiveAE::MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSampleRate, CAEChannelInfo& channelLayout, unsigned int options) +IAEStream *CActiveAE::MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSampleRate, CAEChannelInfo& channelLayout, unsigned int options, IAEClockCallback *clock) { if (IsSuspended()) return NULL; @@ -2873,6 +3092,7 @@ IAEStream *CActiveAE::MakeStream(enum AEDataFormat dataFormat, unsigned int samp MsgStreamNew msg; msg.format = format; msg.options = options; + msg.clock = clock; Message *reply; if (m_dataPort.SendOutMessageSync(CActiveAEDataProtocol::NEWSTREAM, @@ -2893,10 +3113,22 @@ IAEStream *CActiveAE::MakeStream(enum AEDataFormat dataFormat, unsigned int samp return NULL; } -IAEStream *CActiveAE::FreeStream(IAEStream *stream) +bool CActiveAE::FreeStream(IAEStream *stream) { - m_dataPort.SendOutMessage(CActiveAEDataProtocol::FREESTREAM, &stream, sizeof(IAEStream*)); - return NULL; + Message *reply; + if (m_dataPort.SendOutMessageSync(CActiveAEDataProtocol::FREESTREAM, + &reply,1000, + &stream, sizeof(IAEStream*))) + { + bool success = reply->signal == CActiveAEControlProtocol::ACC; + reply->Release(); + if (success) + { + return true; + } + } + CLog::Log(LOGERROR, "CActiveAE::FreeStream - failed"); + return false; } void CActiveAE::FlushStream(CActiveAEStream *stream) @@ -2962,6 +3194,15 @@ void CActiveAE::SetStreamResampleRatio(CActiveAEStream *stream, double ratio) &msg, sizeof(MsgStreamParameter)); } +void CActiveAE::SetStreamResampleMode(CActiveAEStream *stream, int mode) +{ + MsgStreamParameter msg; + msg.stream = stream; + msg.parameter.int_par = mode; + m_controlPort.SendOutMessage(CActiveAEControlProtocol::STREAMRESAMPLEMODE, + &msg, sizeof(MsgStreamParameter)); +} + void CActiveAE::SetStreamFFmpegInfo(CActiveAEStream *stream, int profile, enum AVMatrixEncoding matrix_encoding, enum AVAudioServiceType audio_service_type) { MsgStreamFFmpegInfo msg; diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.h b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.h index e5406b619d..2f2bca18a1 100644 --- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.h +++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.h @@ -88,6 +88,7 @@ public: STREAMVOLUME, STREAMAMP, STREAMRESAMPLERATIO, + STREAMRESAMPLEMODE, STREAMFADE, STREAMFFMPEGINFO, STOPSOUND, @@ -133,6 +134,7 @@ struct MsgStreamNew { AEAudioFormat format; unsigned int options; + IAEClockCallback *clock; }; struct MsgStreamSample @@ -176,6 +178,7 @@ public: void AddSamples(int samples, std::list<CActiveAEStream*> &streams); void GetDelay(AEDelayStatus& status); void GetDelay(AEDelayStatus& status, CActiveAEStream *stream); + void GetSyncInfo(CAESyncInfo& info, CActiveAEStream *stream); float GetCacheTime(CActiveAEStream *stream); float GetCacheTotal(CActiveAEStream *stream); float GetWaterLevel(); @@ -234,8 +237,8 @@ public: virtual void SetSoundMode(const int mode); /* returns a new stream for data in the specified format */ - virtual IAEStream *MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSampleRate, CAEChannelInfo& channelLayout, unsigned int options = 0); - virtual IAEStream *FreeStream(IAEStream *stream); + virtual IAEStream *MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSampleRate, CAEChannelInfo& channelLayout, unsigned int options = 0, IAEClockCallback *clock = NULL); + virtual bool FreeStream(IAEStream *stream); /* returns a new sound object */ virtual IAESound *MakeSound(const std::string& file); @@ -268,6 +271,7 @@ protected: uint8_t **AllocSoundSample(SampleConfig &config, int &samples, int &bytes_per_sample, int &planes, int &linesize); void FreeSoundSample(uint8_t **data); void GetDelay(AEDelayStatus& status, CActiveAEStream *stream) { m_stats.GetDelay(status, stream); } + void GetSyncInfo(CAESyncInfo& info, CActiveAEStream *stream) { m_stats.GetSyncInfo(info, stream); } int64_t GetPlayingPTS() { return m_stats.GetPlayingPTS(); } int Discontinuity() { return m_stats.Discontinuity(); } float GetCacheTime(CActiveAEStream *stream) { return m_stats.GetCacheTime(stream); } @@ -279,6 +283,7 @@ protected: void SetStreamReplaygain(CActiveAEStream *stream, float rgain); void SetStreamVolume(CActiveAEStream *stream, float volume); void SetStreamResampleRatio(CActiveAEStream *stream, double ratio); + void SetStreamResampleMode(CActiveAEStream *stream, int mode); void SetStreamFFmpegInfo(CActiveAEStream *stream, int profile, enum AVMatrixEncoding matrix_encoding, enum AVAudioServiceType audio_service_type); void SetStreamFade(CActiveAEStream *stream, float from, float target, unsigned int millis); @@ -307,6 +312,7 @@ protected: bool RunStages(); bool HasWork(); + CSampleBuffer* SyncStream(CActiveAEStream *stream); void ResampleSounds(); bool ResampleSound(CActiveAESound *sound); diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp index 1e38b4e9bc..3ae120c36a 100644 --- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp +++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp @@ -464,7 +464,10 @@ bool CActiveAEBufferPoolResample::ResampleBuffers(int64_t timestamp) { if (!timestamp) { - m_lastSamplePts = in->timestamp; + if (in->timestamp) + m_lastSamplePts = in->timestamp; + else + in->pkt_start_offset = 0; m_procSample->clockId = in->clockId; } else @@ -475,13 +478,13 @@ bool CActiveAEBufferPoolResample::ResampleBuffers(int64_t timestamp) } // pts of last sample we added to the buffer - m_lastSamplePts += (in->pkt->nb_samples-in->pkt_start_offset)/m_format.m_sampleRate * 1000; + m_lastSamplePts += (in->pkt->nb_samples-in->pkt_start_offset) * 1000 / m_format.m_sampleRate; } // calculate pts for last sample in m_procSample int bufferedSamples = m_resampler->GetBufferedSamples(); m_procSample->pkt_start_offset = m_procSample->pkt->nb_samples; - m_procSample->timestamp = m_lastSamplePts - bufferedSamples/m_format.m_sampleRate*1000; + m_procSample->timestamp = m_lastSamplePts - bufferedSamples * 1000 / m_format.m_sampleRate; if ((m_drain || m_changeResampler || m_changeDSP) && m_empty) { diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp index 01ac1129ab..5a138fe9c2 100644 --- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp +++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp @@ -61,9 +61,11 @@ CActiveAEStream::CActiveAEStream(AEAudioFormat *format) m_remapper = NULL; m_remapBuffer = NULL; m_streamResampleRatio = 1.0; + m_streamResampleMode = 0; m_profile = 0; m_matrixEncoding = AV_MATRIX_ENCODING_NONE; m_audioServiceType = AV_AUDIO_SERVICE_TYPE_MAIN; + m_pClock = NULL; } CActiveAEStream::~CActiveAEStream() @@ -192,6 +194,34 @@ void CActiveAEStream::RemapBuffer() } } +double CActiveAEStream::CalcResampleRatio(double error) +{ + //reset the integral on big errors, failsafe + if (fabs(error) > 1000) + m_resampleIntegral = 0; + else if (fabs(error) > 5) + m_resampleIntegral += error / 1000 / 200; + + double proportional = 0.0; + + double proportionaldiv = 0.02 * fabs(error / 1000); + if (proportionaldiv < 2.0) + proportionaldiv = 2.0; + else if (proportionaldiv > 40.0) + proportionaldiv = 40.0; + + proportional = error / 1000 / proportionaldiv; + + double clockspeed = 1.0; + if (m_pClock) + clockspeed = m_pClock->GetClockSpeed(); + + double ret = 1.0 / clockspeed + proportional + m_resampleIntegral; +// CLog::Log(LOGNOTICE,"----- error: %f, rr: %f, prop: %f, int: %f", +// error, ret, proportional, m_resampleIntegral); + return ret; +} + unsigned int CActiveAEStream::GetSpace() { CSingleLock lock(m_streamLock); @@ -283,6 +313,13 @@ double CActiveAEStream::GetDelay() return status.GetDelay(); } +CAESyncInfo CActiveAEStream::GetSyncInfo() +{ + CAESyncInfo info; + AE.GetSyncInfo(info, this); + return info; +} + int64_t CActiveAEStream::GetPlayingPTS() { return AE.GetPlayingPTS(); @@ -440,12 +477,18 @@ double CActiveAEStream::GetResampleRatio() return m_streamResampleRatio; } -bool CActiveAEStream::SetResampleRatio(double ratio) +void CActiveAEStream::SetResampleRatio(double ratio) { if (ratio != m_streamResampleRatio) AE.SetStreamResampleRatio(this, ratio); m_streamResampleRatio = ratio; - return true; +} + +void CActiveAEStream::SetResampleMode(int mode) +{ + if (mode != m_streamResampleMode) + AE.SetStreamResampleMode(this, mode); + m_streamResampleMode = mode; } void CActiveAEStream::SetFFmpegInfo(int profile, enum AVMatrixEncoding matrix_encoding, enum AVAudioServiceType audio_service_type) diff --git a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.h b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.h index 0e08eed75e..de8cebc128 100644 --- a/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.h +++ b/xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.h @@ -26,6 +26,69 @@ namespace ActiveAE { +class CSyncError +{ +public: + CSyncError() + { + Flush(); + } + void Add(double error) + { + m_buffer += error; + m_count++; + } + + void Flush(int interval = 100) + { + m_buffer = 0.0f; + m_lastError = 0.0; + m_count = 0; + m_timer.Set(interval); + } + + bool Get(double& error, int interval = 100) + { + if(m_timer.IsTimePast()) + { + error = Get(); + Flush(interval); + m_lastError = error; + return true; + } + else + { + error = m_lastError; + return false; + } + } + + double GetLastError(unsigned int &time) + { + time = m_timer.GetStartTime(); + return m_lastError; + } + + void Correction(double correction) + { + m_lastError += correction; + } + +protected: + double Get() + { + if(m_count) + return m_buffer / m_count; + else + return 0.0; + } + double m_buffer; + double m_lastError; + int m_count; + XbmcThreads::EndTime m_timer; +}; + + class CActiveAEStream : public IAEStream { protected: @@ -39,11 +102,13 @@ protected: void ResetFreeBuffers(); void InitRemapper(); void RemapBuffer(); + double CalcResampleRatio(double error); public: virtual unsigned int GetSpace(); virtual unsigned int AddData(uint8_t* const *data, unsigned int offset, unsigned int frames, double pts = 0.0); virtual double GetDelay(); + virtual CAESyncInfo GetSyncInfo(); virtual int64_t GetPlayingPTS(); virtual bool IsBuffering(); virtual double GetCacheTime(); @@ -72,7 +137,8 @@ public: virtual const enum AEDataFormat GetDataFormat() const; virtual double GetResampleRatio(); - virtual bool SetResampleRatio(double ratio); + virtual void SetResampleRatio(double ratio); + virtual void SetResampleMode(int mode); virtual void RegisterAudioCallback(IAudioCallback* pCallback); virtual void UnRegisterAudioCallback(); virtual void FadeVolume(float from, float to, unsigned int time); @@ -88,6 +154,7 @@ protected: float m_streamRgain; float m_streamAmplify; double m_streamResampleRatio; + int m_streamResampleMode; unsigned int m_streamSpace; bool m_streamDraining; bool m_streamDrained; @@ -125,9 +192,21 @@ protected: float m_fadingTarget; int m_fadingTime; int m_profile; + int m_resampleMode; + double m_resampleIntegral; enum AVMatrixEncoding m_matrixEncoding; enum AVAudioServiceType m_audioServiceType; bool m_forceResampler; + IAEClockCallback *m_pClock; + CSyncError m_syncError; + double m_lastSyncError; + enum + { + INSYNC = 0, + STARTSYNC, + MUTE, + ADJUST + } m_syncClock; }; } diff --git a/xbmc/cores/AudioEngine/Interfaces/AE.h b/xbmc/cores/AudioEngine/Interfaces/AE.h index 190d351865..aab0a5ffe8 100644 --- a/xbmc/cores/AudioEngine/Interfaces/AE.h +++ b/xbmc/cores/AudioEngine/Interfaces/AE.h @@ -36,6 +36,7 @@ class IAEStream; class IAESound; class IAEPacketizer; class IAudioCallback; +class IAEClockCallback; /* sound options */ #define AE_SOUND_OFF 0 /* disable sounds */ @@ -158,7 +159,7 @@ public: * @param options A bit field of stream options (see: enum AEStreamOptions) * @return a new IAEStream that will accept data in the requested format */ - virtual IAEStream *MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSampleRate, CAEChannelInfo& channelLayout, unsigned int options = 0) = 0; + virtual IAEStream *MakeStream(enum AEDataFormat dataFormat, unsigned int sampleRate, unsigned int encodedSampleRate, CAEChannelInfo& channelLayout, unsigned int options = 0, IAEClockCallback *clock = NULL) = 0; /** * This method will remove the specifyed stream from the engine. @@ -166,7 +167,7 @@ public: * @param stream The stream to be altered * @return NULL */ - virtual IAEStream *FreeStream(IAEStream *stream) = 0; + virtual bool FreeStream(IAEStream *stream) = 0; /** * Creates a new IAESound that is ready to play the specified file diff --git a/xbmc/cores/AudioEngine/Interfaces/AEStream.h b/xbmc/cores/AudioEngine/Interfaces/AEStream.h index b6bcb5e513..aa2a633e5e 100644 --- a/xbmc/cores/AudioEngine/Interfaces/AEStream.h +++ b/xbmc/cores/AudioEngine/Interfaces/AEStream.h @@ -29,6 +29,32 @@ extern "C" { } /** + * Callback interafce for VideoPlayer clock needed by AE for sync + */ +class IAEClockCallback +{ +public: + virtual double GetClock() = 0; + virtual double GetClockSpeed() { return 1.0; }; +}; + +class CAESyncInfo +{ +public: + double delay; + double error; + double rr; + unsigned int errortime; + enum AESyncState + { + SYNC_OFF, + SYNC_PLAY, + SYNC_ACTIVE + }; + AESyncState state; +}; + +/** * IAEStream Stream Interface for streaming audio */ class IAEStream @@ -63,6 +89,12 @@ public: virtual double GetDelay() = 0; /** + * Returns info about audio to clock synchronization + * @return CAESyncInfo + */ + virtual CAESyncInfo GetSyncInfo() = 0; + + /** * Returns playing PTS * @return millis */ @@ -204,7 +236,12 @@ public: * @note This function may return false if the stream is not resampling, if you wish to use this be sure to set the AESTREAM_FORCE_RESAMPLE option * @param ratio the new sample rate ratio, calculated by ((double)desiredRate / (double)GetSampleRate()) */ - virtual bool SetResampleRatio(double ratio) = 0; + virtual void SetResampleRatio(double ratio) = 0; + + /** + * Sets the resamplling on/ff + */ + virtual void SetResampleMode(int mode) = 0; /** * Registers the audio callback to call with each block of data, this is used by Audio Visualizations diff --git a/xbmc/cores/VideoPlayer/DVDAudio.cpp b/xbmc/cores/VideoPlayer/DVDAudio.cpp index f420300ff0..794b82ebfc 100644 --- a/xbmc/cores/VideoPlayer/DVDAudio.cpp +++ b/xbmc/cores/VideoPlayer/DVDAudio.cpp @@ -27,8 +27,7 @@ #include "cores/AudioEngine/Interfaces/AEStream.h" #include "settings/MediaSettings.h" -CDVDAudio::CDVDAudio(volatile bool &bStop) - : m_bStop(bStop) +CDVDAudio::CDVDAudio(volatile bool &bStop, CDVDClock *clock) : m_bStop(bStop), m_pClock(clock) { m_pAudioStream = NULL; m_bPassthrough = false; @@ -38,6 +37,8 @@ CDVDAudio::CDVDAudio(volatile bool &bStop) m_bPaused = true; m_playingPts = DVD_NOPTS_VALUE; //silence coverity uninitialized warning, is set elsewhere m_timeOfPts = 0.0; //silence coverity uninitialized warning, is set elsewhere + m_syncError = 0.0; + m_syncErrorTime = 0; } CDVDAudio::~CDVDAudio() @@ -67,7 +68,8 @@ bool CDVDAudio::Create(const DVDAudioFrame &audioframe, AVCodecID codec, bool ne audioframe.sample_rate, audioframe.encoded_sample_rate, audioframe.channel_layout, - options + options, + this ); if (!m_pAudioStream) return false; @@ -114,6 +116,20 @@ unsigned int CDVDAudio::AddPackets(const DVDAudioFrame &audioframe) if(!m_pAudioStream) return 0; + CAESyncInfo info = m_pAudioStream->GetSyncInfo(); + unsigned int newTime = info.errortime; + if (info.state == CAESyncInfo::SYNC_ACTIVE) + { + m_syncErrorTime = 0; + m_syncError = 0.0; + } + else if (newTime != m_syncErrorTime) + { + m_syncErrorTime = info.errortime; + m_syncError = info.error / 1000 * DVD_TIME_BASE; + m_resampleRatio = info.rr; + } + //Calculate a timeout when this definitely should be done double timeout; timeout = DVD_SEC_TO_TIME(m_pAudioStream->GetDelay() + audioframe.nb_frames*audioframe.framesize * m_SecondsPerByte); @@ -125,7 +141,8 @@ unsigned int CDVDAudio::AddPackets(const DVDAudioFrame &audioframe) unsigned int offset = 0; do { - unsigned int copied = m_pAudioStream->AddData(audioframe.data, offset, frames); + double pts = (offset == 0) ? audioframe.pts / DVD_TIME_BASE * 1000 : 0.0; + unsigned int copied = m_pAudioStream->AddData(audioframe.data, offset, frames, pts); offset += copied; frames -= copied; if (frames <= 0) @@ -216,6 +233,8 @@ void CDVDAudio::Flush() m_pAudioStream->Flush(); } m_playingPts = DVD_NOPTS_VALUE; + m_syncError = 0.0; + m_syncErrorTime = 0; } bool CDVDAudio::IsValidFormat(const DVDAudioFrame &audioframe) @@ -263,13 +282,6 @@ double CDVDAudio::GetCacheTotal() return m_pAudioStream->GetCacheTotal(); } -void CDVDAudio::SetPlayingPts(double pts) -{ - CSingleLock lock(m_critSection); - m_playingPts = pts - GetDelay(); - m_timeOfPts = CDVDClock::GetAbsoluteClock(); -} - double CDVDAudio::GetPlayingPts() { if (m_playingPts == DVD_NOPTS_VALUE) @@ -289,3 +301,44 @@ double CDVDAudio::GetPlayingPts() m_playingPts += played; return m_playingPts; } + +double CDVDAudio::GetSyncError() +{ + return m_syncError; +} + +void CDVDAudio::SetSyncErrorCorrection(double correction) +{ + m_syncError += correction; +} + +double CDVDAudio::GetResampleRatio() +{ + return m_resampleRatio; +} + +void CDVDAudio::SetResampleMode(int mode) +{ + CSingleLock lock (m_critSection); + if(m_pAudioStream) + { + m_pAudioStream->SetResampleMode(mode); + } +} + +double CDVDAudio::GetClock() +{ + double absolute; + if (m_pClock) + return m_pClock->GetClock(absolute) / DVD_TIME_BASE * 1000; + else + return 0.0; +} + +double CDVDAudio::GetClockSpeed() +{ + if (m_pClock) + return m_pClock->GetClockSpeed(); + else + return 1.0; +} diff --git a/xbmc/cores/VideoPlayer/DVDAudio.h b/xbmc/cores/VideoPlayer/DVDAudio.h index 16c26b2985..838bf76051 100644 --- a/xbmc/cores/VideoPlayer/DVDAudio.h +++ b/xbmc/cores/VideoPlayer/DVDAudio.h @@ -27,7 +27,7 @@ #include "PlatformDefs.h" #include "cores/AudioEngine/Utils/AEChannelInfo.h" -class IAEStream; +#include "cores/AudioEngine/Interfaces/AEStream.h" extern "C" { #include "libavcodec/avcodec.h" @@ -36,11 +36,12 @@ extern "C" { typedef struct stDVDAudioFrame DVDAudioFrame; class CSingleLock; +class CDVDClock; -class CDVDAudio +class CDVDAudio : IAEClockCallback { public: - CDVDAudio(volatile bool& bStop); + CDVDAudio(volatile bool& bStop, CDVDClock *clock); ~CDVDAudio(); void SetVolume(float fVolume); @@ -52,11 +53,13 @@ public: bool IsValidFormat(const DVDAudioFrame &audioframe); void Destroy(); unsigned int AddPackets(const DVDAudioFrame &audioframe); - double GetDelay(); // returns the time it takes to play a packet if we add one at this time double GetPlayingPts(); - void SetPlayingPts(double pts); double GetCacheTime(); // returns total amount of data cached in audio output at this time double GetCacheTotal(); // returns total amount the audio device can buffer + double GetSyncError(); + void SetSyncErrorCorrection(double correction); + double GetResampleRatio(); + void SetResampleMode(int mode); void Flush(); void Finish(); void Drain(); @@ -64,10 +67,19 @@ public: void SetSpeed(int iSpeed); void SetResampleRatio(double ratio); + double GetClock(); + double GetClockSpeed(); IAEStream *m_pAudioStream; + protected: + + double GetDelay(); // returns the time it takes to play a packet if we add one at this time + double m_playingPts; double m_timeOfPts; + double m_syncError; + unsigned int m_syncErrorTime; + double m_resampleRatio; CCriticalSection m_critSection; int m_iBitrate; @@ -78,6 +90,5 @@ protected: bool m_bPaused; volatile bool& m_bStop; - //counter that will go from 0 to m_iSpeed-1 and reset, data will only be output when speedstep is 0 - //int m_iSpeedStep; + CDVDClock *m_pClock; }; diff --git a/xbmc/cores/VideoPlayer/VideoPlayer.cpp b/xbmc/cores/VideoPlayer/VideoPlayer.cpp index dacba94bcb..fd19ea04d7 100644 --- a/xbmc/cores/VideoPlayer/VideoPlayer.cpp +++ b/xbmc/cores/VideoPlayer/VideoPlayer.cpp @@ -2007,15 +2007,19 @@ bool CVideoPlayer::CheckPlayerInit(CCurrentStream& current) current.startpts = current.dts; bool setclock = false; - if(m_playSpeed == DVD_PLAYSPEED_NORMAL) + if (m_playSpeed == DVD_PLAYSPEED_NORMAL) { - if( current.player == VideoPlayer_AUDIO) - setclock = m_clock.GetMaster() == MASTER_CLOCK_AUDIO - || m_clock.GetMaster() == MASTER_CLOCK_AUDIO_VIDEOREF - || !m_CurrentVideo.inited; - else if(current.player == VideoPlayer_VIDEO) - setclock = m_clock.GetMaster() == MASTER_CLOCK_VIDEO - || !m_CurrentAudio.inited; + if (current.player == VideoPlayer_AUDIO) + { + setclock = (m_clock.GetMaster() == MASTER_CLOCK_AUDIO) + || (m_clock.GetMaster() == MASTER_CLOCK_AUDIO_VIDEOREF) + || !m_CurrentVideo.inited; + } + else if (current.player == VideoPlayer_VIDEO) + { + setclock = (m_clock.GetMaster() == MASTER_CLOCK_VIDEO) + || !m_CurrentAudio.inited; + } } else { diff --git a/xbmc/cores/VideoPlayer/VideoPlayerAudio.cpp b/xbmc/cores/VideoPlayer/VideoPlayerAudio.cpp index 5ba8bbc453..f8fa3b71bb 100644 --- a/xbmc/cores/VideoPlayer/VideoPlayerAudio.cpp +++ b/xbmc/cores/VideoPlayer/VideoPlayerAudio.cpp @@ -99,7 +99,7 @@ CVideoPlayerAudio::CVideoPlayerAudio(CDVDClock* pClock, CDVDMessageQueue& parent : CThread("VideoPlayerAudio") , m_messageQueue("audio") , m_messageParent(parent) -, m_dvdAudio((bool&)m_bStop) +, m_dvdAudio((bool&)m_bStop, pClock) { m_pClock = pClock; m_pAudioCodec = NULL; @@ -108,14 +108,9 @@ CVideoPlayerAudio::CVideoPlayerAudio(CDVDClock* pClock, CDVDMessageQueue& parent m_stalled = true; m_started = false; m_silence = false; - m_resampleratio = 1.0; m_synctype = SYNC_DISCON; m_setsynctype = SYNC_DISCON; m_prevsynctype = -1; - m_error = 0; - m_errors.Flush(); - m_syncclock = true; - m_integral = 0; m_prevskipped = false; m_maxspeedadjust = 0.0; @@ -185,11 +180,7 @@ void CVideoPlayerAudio::OpenStream( CDVDStreamInfo &hints, CDVDAudioCodec* codec m_setsynctype = SYNC_RESAMPLE; m_prevsynctype = -1; - m_error = 0; - m_errors.Flush(); - m_integral = 0; m_prevskipped = false; - m_syncclock = true; m_silence = false; m_maxspeedadjust = 5.0; @@ -364,11 +355,8 @@ int CVideoPlayerAudio::DecodeFrame(DVDAudioFrame &audioframe) m_audioClock = pMsgGeneralResync->m_timestamp; m_ptsInput.Flush(); - m_dvdAudio.SetPlayingPts(m_audioClock); if (pMsgGeneralResync->m_clock) - m_pClock->Discontinuity(m_dvdAudio.GetPlayingPts()); - m_syncclock = true; - m_errors.Flush(); + m_pClock->Discontinuity(m_audioClock - DVD_MSEC_TO_TIME(300)); } else if (pMsg->IsType(CDVDMsg::GENERAL_RESET)) { @@ -381,9 +369,8 @@ int CVideoPlayerAudio::DecodeFrame(DVDAudioFrame &audioframe) { m_dvdAudio.Flush(); m_ptsInput.Flush(); - m_syncclock = true; - m_stalled = true; - m_started = false; + m_stalled = true; + m_started = false; if (m_pAudioCodec) m_pAudioCodec->Reset(); @@ -435,13 +422,10 @@ int CVideoPlayerAudio::DecodeFrame(DVDAudioFrame &audioframe) if (speed != m_speed) { m_dvdAudio.Resume(); - m_syncclock = true; - m_errors.Flush(); } } else { - m_dvdAudio.Flush(); m_dvdAudio.Pause(); } m_speed = speed; @@ -482,7 +466,7 @@ void CVideoPlayerAudio::UpdatePlayerInfo() //print the inverse of the resample ratio, since that makes more sense //if the resample ratio is 0.5, then we're playing twice as fast if (m_synctype == SYNC_RESAMPLE) - s << ", rr:" << std::fixed << std::setprecision(5) << 1.0 / m_resampleratio; + s << ", rr:" << std::fixed << std::setprecision(5) << 1.0 / m_dvdAudio.GetResampleRatio(); s << ", att:" << std::fixed << std::setprecision(1) << log(GetCurrentAttenuation()) * 20.0f << " dB"; @@ -570,19 +554,14 @@ void CVideoPlayerAudio::Process() } // Zero out the frame data if we are supposed to silence the audio - if (m_silence || m_syncclock) + if (m_silence) { int size = audioframe.nb_frames * audioframe.framesize / audioframe.planes; for (unsigned int i=0; i<audioframe.planes; i++) memset(audioframe.data[i], 0, size); } - if(result & DECODE_FLAG_DROP) - { - // keep output times in sync - m_dvdAudio.SetPlayingPts(m_audioClock); - } - else + if(!(result & DECODE_FLAG_DROP)) { SetSyncType(audioframe.passthrough); @@ -600,17 +579,7 @@ void CVideoPlayerAudio::Process() { m_started = true; m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, VideoPlayer_AUDIO)); - m_errors.Flush(); } - - if( m_dvdAudio.GetPlayingPts() == DVD_NOPTS_VALUE ) - continue; - - if( m_speed != DVD_PLAYSPEED_NORMAL ) - continue; - - if (packetadded) - HandleSyncError(audioframe.duration); } } @@ -641,67 +610,16 @@ void CVideoPlayerAudio::SetSyncType(bool passthrough) int synctype = (m_synctype >= 0 && m_synctype <= 2) ? m_synctype : 3; CLog::Log(LOGDEBUG, "CVideoPlayerAudio:: synctype set to %i: %s", m_synctype, synctypes[synctype]); m_prevsynctype = m_synctype; + if (m_synctype == SYNC_RESAMPLE) + m_dvdAudio.SetResampleMode(1); + else + m_dvdAudio.SetResampleMode(0); } } -void CVideoPlayerAudio::HandleSyncError(double duration) +bool CVideoPlayerAudio::OutputPacket(DVDAudioFrame &audioframe) { - double absolute; - double clock = m_pClock->GetClock(absolute); - double error = m_dvdAudio.GetPlayingPts() - clock; - double threshold1 = DVD_MSEC_TO_TIME(100); - double threshold2 = DVD_MSEC_TO_TIME(50); - - // adjust thresholds - // some codecs like flac have a very large frame length - if (threshold1 < 1.5 * duration) - threshold1 = 1.5 * duration; - if (threshold2 < duration) - threshold2 = duration; - - // as long as we are in sync mode, don't calculate the average - // error because drop/dupe changes the value - if (m_syncclock && fabs(error) > threshold1) - { - m_errors.Flush(); - m_integral = 0.0; - m_resampleratio = 0.0; - return; - } - - // inertia of resampling - if (m_synctype == SYNC_RESAMPLE) - threshold1 *= 2; - - m_errors.Add(error); - - // check if measured error for 2 seconds - // when moving from big erros and we are still above threshold2, calculate errors every - // 500ms in order to get first resample ratio early. If we don't adjust rr early, error - // may get above threshold1 again. Too small values for interval result in worse average errors - - if (!m_errors.Get(m_error, m_syncclock ? 100 : 2000)) - return; - - if (fabs(m_error) > threshold1) - { - m_syncclock = true; - m_errors.Flush(100); - m_integral = 0.0; - m_resampleratio = 0.0; - CLog::Log(LOGDEBUG,"CVideoPlayerAudio::HandleSyncError - average error %f above threshold of %f", - m_error, threshold1); - return; - } - else if (m_syncclock && fabs(m_error) < threshold2) - { - m_syncclock = false; - // we are about to get stable, increase interval - m_errors.Flush(1000); - m_integral = 0.0; - CLog::Log(LOGDEBUG,"CVideoPlayerAudio::HandleSyncError - average error %f below threshold of %f", - m_error, threshold2); - } + double syncerror = m_dvdAudio.GetSyncError(); if (m_synctype == SYNC_DISCON) { @@ -713,103 +631,28 @@ void CVideoPlayerAudio::HandleSyncError(double duration) //make error a multiple of limit, rounded towards zero, //so it won't interfere with the sync methods in CRenderManager::WaitPresentTime - if (m_error > 0.0) - error = limit * floor(m_error / limit); + if (syncerror > 0.0) + error = limit * floor(syncerror / limit); else - error = limit * ceil(m_error / limit); + error = limit * ceil(syncerror / limit); } else { limit = DVD_MSEC_TO_TIME(10); - error = m_error; + error = syncerror; } - m_pClock->Update(clock+error, absolute, limit - 0.001, "CVideoPlayerAudio::HandleSyncError2"); - } - else if (m_synctype == SYNC_RESAMPLE) - { - //reset the integral on big errors, failsafe - if (fabs(m_error) > DVD_TIME_BASE) - m_integral = 0; - else if (fabs(m_error) > DVD_MSEC_TO_TIME(5)) - m_integral += m_error / DVD_TIME_BASE / INTEGRAL; - - double proportional = 0.0; - - //on big errors use more proportional - if (fabs(m_error / DVD_TIME_BASE) > 0.0) - { - double proportionaldiv = PROPORTIONAL * (PROPREF / fabs(m_error / DVD_TIME_BASE)); - if (proportionaldiv < PROPDIVMIN) - proportionaldiv = PROPDIVMIN; - else if (proportionaldiv > PROPDIVMAX) - proportionaldiv = PROPDIVMAX; - - proportional = m_error / DVD_TIME_BASE / proportionaldiv; - } - m_resampleratio = 1.0 / m_pClock->GetClockSpeed() + proportional + m_integral; - } -} - -bool CVideoPlayerAudio::OutputPacket(DVDAudioFrame &audioframe) -{ - if (m_syncclock) - { double absolute; double clock = m_pClock->GetClock(absolute); - double error = m_dvdAudio.GetPlayingPts() - clock; - m_dvdAudio.SetResampleRatio(1.0); - - // sync audio by skipping or dropping frames if we are above or - // below a given threshold. the constants are aligned with known - // durations: DTS = 11ms, AC3 = 32ms - // during this stage audio is muted - if (error > DVD_MSEC_TO_TIME(10)) - { - unsigned int nb_frames = audioframe.nb_frames; - double duration = audioframe.duration; - - // reduce large packets for better sync, i.e. FLAC can have 96ms packets - // 32ms because I know this works good for AC3 - if (audioframe.duration > DVD_MSEC_TO_TIME(32) && audioframe.sample_rate) - { - audioframe.nb_frames = 0.032 * audioframe.sample_rate; - audioframe.duration = ((double)audioframe.nb_frames * DVD_TIME_BASE) / audioframe.sample_rate; - } - - int dups = std::min(DVD_MSEC_TO_TIME(100), error) / audioframe.duration; - if (dups > 0) - CLog::Log(LOGNOTICE,"CVideoPlayerAudio::OutputPacket duplicate %d packets of duration %d", - dups, DVD_TIME_TO_MSEC(audioframe.duration)); - for (int i = 0; i < dups; i++) - { - m_dvdAudio.AddPackets(audioframe); - } - - audioframe.nb_frames = nb_frames; - audioframe.duration = duration; - - m_dvdAudio.AddPackets(audioframe); - } - else if (error < -DVD_MSEC_TO_TIME(32)) - { - m_dvdAudio.SetPlayingPts(audioframe.pts); - CLog::Log(LOGNOTICE,"CVideoPlayerAudio::OutputPacket skipping a packets of duration %d", - DVD_TIME_TO_MSEC(audioframe.duration)); - } - else + if (m_pClock->Update(clock + error, absolute, limit - 0.001, "CVideoPlayerAudio::OutputPacket")) { - m_dvdAudio.AddPackets(audioframe); + m_dvdAudio.SetSyncErrorCorrection(-error); } } - else if (m_synctype == SYNC_DISCON) - { - m_dvdAudio.AddPackets(audioframe); - } - else if (m_synctype == SYNC_SKIPDUP) + if (m_synctype == SYNC_SKIPDUP) { double limit = std::max(DVD_MSEC_TO_TIME(10), audioframe.duration * 2.0 / 3.0); - if (m_error < -limit) + if (syncerror < -limit) { m_prevskipped = !m_prevskipped; if (m_prevskipped) @@ -817,22 +660,21 @@ bool CVideoPlayerAudio::OutputPacket(DVDAudioFrame &audioframe) else { CLog::Log(LOGDEBUG, "CVideoPlayerAudio:: Dropping packet of %d ms", DVD_TIME_TO_MSEC(audioframe.duration)); - m_error += audioframe.duration; + m_dvdAudio.SetSyncErrorCorrection(audioframe.duration); } } - else if(m_error > limit) + else if(syncerror > limit) { CLog::Log(LOGDEBUG, "CVideoPlayerAudio:: Duplicating packet of %d ms", DVD_TIME_TO_MSEC(audioframe.duration)); m_dvdAudio.AddPackets(audioframe); m_dvdAudio.AddPackets(audioframe); - m_error -= audioframe.duration; + m_dvdAudio.SetSyncErrorCorrection(-audioframe.duration); } else m_dvdAudio.AddPackets(audioframe); } - else if (m_synctype == SYNC_RESAMPLE) + else { - m_dvdAudio.SetResampleRatio(m_resampleratio); m_dvdAudio.AddPackets(audioframe); } diff --git a/xbmc/cores/VideoPlayer/VideoPlayerAudio.h b/xbmc/cores/VideoPlayer/VideoPlayerAudio.h index f85e0869bd..9465c54d11 100644 --- a/xbmc/cores/VideoPlayer/VideoPlayerAudio.h +++ b/xbmc/cores/VideoPlayer/VideoPlayerAudio.h @@ -53,51 +53,6 @@ public: void Flush(); }; -class CDVDErrorAverage -{ -public: - CDVDErrorAverage() - { - Flush(); - } - void Add(double error) - { - m_buffer += error; - m_count++; - } - - void Flush(int interval = 100) - { - m_buffer = 0.0f; - m_count = 0; - m_timer.Set(interval); - } - - double Get() - { - if(m_count) - return m_buffer / m_count; - else - return 0.0; - } - - bool Get(double& error, int interval = 100) - { - if(m_timer.IsTimePast()) - { - error = Get(); - Flush(interval); - return true; - } - else - return false; - } - - double m_buffer; //place to store average errors - int m_count; //number of errors stored - XbmcThreads::EndTime m_timer; -}; - class CVideoPlayerAudio : public CThread, public IDVDStreamPlayerAudio { public: @@ -218,17 +173,10 @@ protected: int m_setsynctype; int m_prevsynctype; //so we can print to the log - double m_error; //last average error - void SetSyncType(bool passthrough); - void HandleSyncError(double duration); - CDVDErrorAverage m_errors; - bool m_syncclock; - double m_integral; //integral correction for resampler bool m_prevskipped; double m_maxspeedadjust; - double m_resampleratio; //resample ratio when using SYNC_RESAMPLE, used for the codec info struct SInfo { diff --git a/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp b/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp index 54e7ce133e..57b09e5736 100644 --- a/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp +++ b/xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp @@ -373,14 +373,16 @@ void CVideoPlayerVideo::Process() double absolute = m_pClock->GetAbsoluteClock(); double delay = m_FlipTimeStamp - absolute; - if( delay > frametime ) delay = frametime; - else if( delay < 0 ) delay = 0; + if (delay > frametime) + delay = frametime; + else if (delay < 0) + delay = 0; m_FlipTimePts = pts -frametime; - if(pMsgGeneralResync->m_clock) + if (pMsgGeneralResync->m_clock) { CLog::Log(LOGDEBUG, "CVideoPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 1)", pts); - m_pClock->Discontinuity(m_FlipTimePts, absolute); + m_pClock->Discontinuity(m_FlipTimePts - DVD_MSEC_TO_TIME(300), absolute); } else CLog::Log(LOGDEBUG, "CVideoPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 0)", pts); |