aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRainer Hochecker <fernetmenta@online.de>2015-09-06 14:10:50 +0200
committerRainer Hochecker <fernetmenta@online.de>2015-12-06 20:33:45 +0100
commit337907d462fd8e16fe45ee403c146625923a982b (patch)
tree78880c44f513337985913242aad4e7f713c4c539
parent3f39ddaf4c67fabcff3ff0406b4e63099e1162b5 (diff)
move audio sync to AE
-rw-r--r--addons/library.kodi.audioengine/libKODI_audioengine.h2
-rw-r--r--lib/addons/library.kodi.audioengine/libKODI_audioengine.cpp4
-rw-r--r--xbmc/addons/AddonCallbacks.h2
-rw-r--r--xbmc/addons/AddonCallbacksAudioEngine.cpp6
-rw-r--r--xbmc/addons/AddonCallbacksAudioEngine.h2
-rw-r--r--xbmc/cores/AudioEngine/AEFactory.cpp8
-rw-r--r--xbmc/cores/AudioEngine/AEFactory.h4
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.cpp255
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAE.h10
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEBuffer.cpp9
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.cpp47
-rw-r--r--xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAEStream.h81
-rw-r--r--xbmc/cores/AudioEngine/Interfaces/AE.h5
-rw-r--r--xbmc/cores/AudioEngine/Interfaces/AEStream.h39
-rw-r--r--xbmc/cores/VideoPlayer/DVDAudio.cpp75
-rw-r--r--xbmc/cores/VideoPlayer/DVDAudio.h25
-rw-r--r--xbmc/cores/VideoPlayer/VideoPlayer.cpp20
-rw-r--r--xbmc/cores/VideoPlayer/VideoPlayerAudio.cpp208
-rw-r--r--xbmc/cores/VideoPlayer/VideoPlayerAudio.h52
-rw-r--r--xbmc/cores/VideoPlayer/VideoPlayerVideo.cpp10
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 &current, 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);