Skip to content
Permalink
Browse files

move audio sync to AE

  • Loading branch information
FernetMenta authored and popcornmix committed Sep 6, 2015
1 parent e446508 commit dfa91a003a7c56373149f2bd2c0ce69c450de971
@@ -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()
@@ -59,8 +59,8 @@ class CAEFactory
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);
@@ -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::SNYC_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,8 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
case CActiveAEControlProtocol::RESUMESTREAM:
stream = *(CActiveAEStream**)msg->data;
stream->m_paused = false;
stream->m_syncClock = true;
stream->m_syncError.Flush(100);
streaming = true;
m_sink.m_controlPort.SendOutMessage(CSinkControlProtocol::STREAMING, &streaming, sizeof(bool));
m_extTimeout = 0;
@@ -638,6 +678,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 +803,8 @@ void CActiveAE::StateMachine(int signal, Protocol *port, Message *msg)
CActiveAEStream *stream;
stream = *(CActiveAEStream**)msg->data;
stream->m_paused = false;
stream->m_syncClock = true;
stream->m_syncError.Flush(100);
m_state = AE_TOP_CONFIGURED_PLAY;
m_extTimeout = 0;
return;
@@ -1223,6 +1266,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 +1339,7 @@ CActiveAEStream* CActiveAE::CreateStream(MsgStreamNew *streamMsg)
stream->m_fadingSamples = 0;
stream->m_started = false;
stream->m_clockId = m_stats.Discontinuity(true);
stream->m_resampleMode = 0;

if (streamMsg->options & AESTREAM_PAUSED)
{
@@ -1307,6 +1355,8 @@ CActiveAEStream* CActiveAE::CreateStream(MsgStreamNew *streamMsg)
stream->m_bypassDSP = true;
}

stream->m_pClock = streamMsg->clock;

m_streams.push_back(stream);

return stream;
@@ -1351,6 +1401,8 @@ void CActiveAE::SFlushStream(CActiveAEStream *stream)
stream->m_streamPort->Purge();
stream->m_bufferedTime = 0.0;
stream->m_paused = false;
stream->m_syncClock = true;
stream->m_syncError.Flush(100);

// flush the engine if we only have a single stream
if (m_streams.size() == 1)
@@ -1799,6 +1851,30 @@ 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 / buf->pkt->config.sample_rate * 1000);
double delay = status.GetDelay() * 1000;
double playingPts = pts - delay;
if (playingPts < 0)
playingPts = 0;
double error = playingPts - (*it)->m_pClock->GetClock();
(*it)->m_syncError.Add(error);
}
}

// mix streams and sounds sounds
if (m_mode != MODE_RAW)
{
@@ -1839,6 +1915,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 +2185,12 @@ 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();
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 +2236,95 @@ bool CActiveAE::HasWork()
return false;
}

CSampleBuffer* CActiveAE::SyncStream(CActiveAEStream *stream)
{
CSampleBuffer *ret = NULL;

if (!stream->m_pClock)
return ret;

double error;
bool newerror = stream->m_syncError.Get(error, stream->m_syncClock ? 100 : 1000);

if (newerror && fabs(error) > 100 && !stream->m_syncClock)
{
stream->m_syncClock = true;
stream->m_resampleBuffers->m_resampleRatio = 1.0;
CLog::Log(LOGDEBUG,"ActiveAE - average error %f above threshold of %f", error, 100.0);
}

if (stream->m_syncClock)
{
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;
ret->pkt->nb_samples = framesToDelay;
if (m_mode == MODE_TRANSCODE)
{
if (framesToDelay > m_encoderFormat.m_frames / 2)
framesToDelay = m_encoderFormat.m_frames;
else
framesToDelay = 0;
}
stream->m_syncError.Correction(-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, bytesToSkip);
}
buf->pkt->nb_samples -= framesToSkip;
stream->m_syncError.Correction(framesToSkip*1000/buf->pkt->config.sample_rate);
}

if (fabs(error) < 50)
{
stream->m_syncClock = false;
stream->m_syncError.Flush(1000);
stream->m_resampleIntegral = 0;
stream->m_resampleBuffers->m_resampleRatio = 1.0;
CLog::Log(LOGDEBUG,"ActiveAE - average error %f below threshold of %f", error, 50.0);
}

return ret;
}

if (!newerror)
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 +3031,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 +3050,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 +3071,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 +3152,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;

0 comments on commit dfa91a0

Please sign in to comment.
You can’t perform that action at this time.