void CVideoPlayerAudio::OpenStream(CDVDStreamInfo &hints, CDVDAudioCodec* codec) { SAFE_DELETE(m_pAudioCodec); m_pAudioCodec = codec; m_processInfo.ResetAudioCodecInfo(); /* store our stream hints */ m_streaminfo = hints; /* update codec information from what codec gave out, if any */ int channelsFromCodec = m_pAudioCodec->GetFormat().m_channelLayout.Count(); int samplerateFromCodec = m_pAudioCodec->GetFormat().m_sampleRate; if (channelsFromCodec > 0) m_streaminfo.channels = channelsFromCodec; if (samplerateFromCodec > 0) m_streaminfo.samplerate = samplerateFromCodec; /* check if we only just got sample rate, in which case the previous call * to CreateAudioCodec() couldn't have started passthrough */ if (hints.samplerate != m_streaminfo.samplerate) SwitchCodecIfNeeded(); m_audioClock = 0; m_stalled = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET) == 0; m_synctype = SYNC_DISCON; m_setsynctype = SYNC_DISCON; if (CServiceBroker::GetSettings().GetBool(CSettings::SETTING_VIDEOPLAYER_USEDISPLAYASCLOCK)) m_setsynctype = SYNC_RESAMPLE; else if (hints.realtime) m_setsynctype = SYNC_RESAMPLE; m_prevsynctype = -1; m_prevskipped = false; m_maxspeedadjust = 5.0; m_messageParent.Put(new CDVDMsg(CDVDMsg::PLAYER_AVCHANGE)); m_syncState = IDVDStreamPlayer::SYNC_STARTING; }
void CDVDPlayerAudio::OpenStream( CDVDStreamInfo &hints, CDVDAudioCodec* codec ) { SAFE_DELETE(m_pAudioCodec); m_pAudioCodec = codec; /* store our stream hints */ m_streaminfo = hints; /* update codec information from what codec gave out, if any */ int channelsFromCodec = m_pAudioCodec->GetEncodedChannels(); int samplerateFromCodec = m_pAudioCodec->GetEncodedSampleRate(); if (channelsFromCodec > 0) m_streaminfo.channels = channelsFromCodec; if (samplerateFromCodec > 0) m_streaminfo.samplerate = samplerateFromCodec; /* check if we only just got sample rate, in which case the previous call * to CreateAudioCodec() couldn't have started passthrough */ if (hints.samplerate != m_streaminfo.samplerate) SwitchCodecIfNeeded(); m_audioClock = 0; m_stalled = m_messageQueue.GetPacketCount(CDVDMsg::DEMUXER_PACKET) == 0; m_started = false; m_synctype = SYNC_DISCON; m_setsynctype = SYNC_DISCON; if (CSettings::GetInstance().GetBool(CSettings::SETTING_VIDEOPLAYER_USEDISPLAYASCLOCK)) 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; g_dataCacheCore.SignalAudioInfoChange(); }
void CVideoPlayerAudio::Process() { CLog::Log(LOGNOTICE, "running thread: CVideoPlayerAudio::Process()"); DVDAudioFrame audioframe; m_audioStats.Start(); while (!m_bStop) { CDVDMsg* pMsg; int timeout = (int)(1000 * m_dvdAudio.GetCacheTime()) + 100; // read next packet and return -1 on error int priority = 1; //Do we want a new audio frame? if (m_syncState == IDVDStreamPlayer::SYNC_STARTING || /* when not started */ ALLOW_AUDIO(m_speed) || /* when playing normally */ m_speed < DVD_PLAYSPEED_PAUSE || /* when rewinding */ (m_speed > DVD_PLAYSPEED_NORMAL && m_audioClock < m_pClock->GetClock())) /* when behind clock in ff */ priority = 0; if (m_syncState == IDVDStreamPlayer::SYNC_WAITSYNC) priority = 1; // consider stream stalled if queue is empty // we can't sync audio to clock with an empty queue if (ALLOW_AUDIO(m_speed) && !m_stalled) { timeout = 0; } MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, timeout, priority); if (MSGQ_IS_ERROR(ret)) { CLog::Log(LOGERROR, "Got MSGQ_ABORT or MSGO_IS_ERROR return true"); break; } else if (ret == MSGQ_TIMEOUT) { // Flush as the audio output may keep looping if we don't if (ALLOW_AUDIO(m_speed) && !m_stalled && m_syncState == IDVDStreamPlayer::SYNC_INSYNC) { // while AE sync is active, we still have time to fill buffers if (m_syncTimer.IsTimePast()) { CLog::Log(LOGNOTICE, "CVideoPlayerAudio::Process - stream stalled"); m_stalled = true; } } if (timeout == 0) Sleep(10); continue; } // handle messages if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE)) { if(((CDVDMsgGeneralSynchronize*)pMsg)->Wait( 100, SYNCSOURCE_AUDIO )) CLog::Log(LOGDEBUG, "CVideoPlayerAudio - CDVDMsg::GENERAL_SYNCHRONIZE"); else m_messageQueue.Put(pMsg->Acquire(), 1); // push back as prio message, to process other prio messages } else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC)) { //player asked us to set internal clock double pts = static_cast<CDVDMsgDouble*>(pMsg)->m_value; CLog::Log(LOGDEBUG, "CVideoPlayerAudio - CDVDMsg::GENERAL_RESYNC(%f)", pts); m_audioClock = pts + m_dvdAudio.GetDelay(); if (m_speed != DVD_PLAYSPEED_PAUSE) m_dvdAudio.Resume(); m_syncState = IDVDStreamPlayer::SYNC_INSYNC; m_syncTimer.Set(3000); } else if (pMsg->IsType(CDVDMsg::GENERAL_RESET)) { if (m_pAudioCodec) m_pAudioCodec->Reset(); m_syncState = IDVDStreamPlayer::SYNC_STARTING; } else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) { bool sync = static_cast<CDVDMsgBool*>(pMsg)->m_value; m_dvdAudio.Flush(); m_stalled = true; m_audioClock = 0; if (sync) { m_syncState = IDVDStreamPlayer::SYNC_STARTING; m_dvdAudio.Pause(); } if (m_pAudioCodec) m_pAudioCodec->Reset(); } else if (pMsg->IsType(CDVDMsg::GENERAL_EOF)) { CLog::Log(LOGDEBUG, "CVideoPlayerAudio - CDVDMsg::GENERAL_EOF"); } else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED)) { double speed = static_cast<CDVDMsgInt*>(pMsg)->m_value; if (ALLOW_AUDIO(speed)) { if (speed != m_speed) { if (m_syncState == IDVDStreamPlayer::SYNC_INSYNC) m_dvdAudio.Resume(); } } else { m_dvdAudio.Pause(); } m_speed = speed; } else if (pMsg->IsType(CDVDMsg::AUDIO_SILENCE)) { m_silence = static_cast<CDVDMsgBool*>(pMsg)->m_value; CLog::Log(LOGDEBUG, "CVideoPlayerAudio - CDVDMsg::AUDIO_SILENCE(%f, %d)" , m_audioClock, m_silence); } else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE)) { CDVDMsgAudioCodecChange* msg(static_cast<CDVDMsgAudioCodecChange*>(pMsg)); OpenStream(msg->m_hints, msg->m_codec); msg->m_codec = NULL; } else if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET)) { DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket(); bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop(); int consumed = m_pAudioCodec->Decode(pPacket->pData, pPacket->iSize, pPacket->dts, pPacket->pts); if (consumed < 0) { CLog::Log(LOGERROR, "CVideoPlayerAudio::DecodeFrame - Decode Error. Skipping audio packet (%d)", consumed); m_pAudioCodec->Reset(); pMsg->Release(); continue; } m_audioStats.AddSampleBytes(pPacket->iSize); UpdatePlayerInfo(); // loop while no error and decoder produces output while (!m_bStop) { // get decoded data and the size of it m_pAudioCodec->GetData(audioframe); if (audioframe.nb_frames == 0) { if (consumed >= pPacket->iSize) break; int ret = m_pAudioCodec->Decode(pPacket->pData+consumed, pPacket->iSize-consumed, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE); if (ret < 0) { CLog::Log(LOGERROR, "CVideoPlayerAudio::DecodeFrame - Decode Error. Skipping audio packet (%d)", ret); m_pAudioCodec->Reset(); break; } consumed += ret; continue; } audioframe.hasTimestamp = true; if (audioframe.pts == DVD_NOPTS_VALUE) { audioframe.pts = m_audioClock; audioframe.hasTimestamp = false; } else { m_audioClock = audioframe.pts; } //Drop when not playing normally if (!ALLOW_AUDIO(m_speed) && m_syncState == IDVDStreamPlayer::SYNC_INSYNC) { break; } if (audioframe.format.m_sampleRate && m_streaminfo.samplerate != (int) audioframe.format.m_sampleRate) { // The sample rate has changed or we just got it for the first time // for this stream. See if we should enable/disable passthrough due // to it. m_streaminfo.samplerate = audioframe.format.m_sampleRate; if (SwitchCodecIfNeeded()) { break; } } // demuxer reads metatags that influence channel layout if (m_streaminfo.codec == AV_CODEC_ID_FLAC && m_streaminfo.channellayout) audioframe.format.m_channelLayout = CAEUtil::GetAEChannelLayout(m_streaminfo.channellayout); // we have succesfully decoded an audio frame, setup renderer to match if (!m_dvdAudio.IsValidFormat(audioframe)) { if(m_speed) m_dvdAudio.Drain(); m_dvdAudio.Destroy(); if (!m_dvdAudio.Create(audioframe, m_streaminfo.codec, m_setsynctype == SYNC_RESAMPLE)) CLog::Log(LOGERROR, "%s - failed to create audio renderer", __FUNCTION__); if (m_syncState == IDVDStreamPlayer::SYNC_INSYNC) m_dvdAudio.Resume(); m_streaminfo.channels = audioframe.format.m_channelLayout.Count(); m_messageParent.Put(new CDVDMsg(CDVDMsg::PLAYER_AVCHANGE)); } // Zero out the frame data if we are supposed to silence the audio 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); } SetSyncType(audioframe.passthrough); if (!bPacketDrop) { OutputPacket(audioframe); // signal to our parent that we have initialized if(m_syncState == IDVDStreamPlayer::SYNC_STARTING) { double cachetotal = DVD_SEC_TO_TIME(m_dvdAudio.GetCacheTotal()); double cachetime = m_dvdAudio.GetDelay(); if (cachetime >= cachetotal * 0.5) { m_syncState = IDVDStreamPlayer::SYNC_WAITSYNC; m_stalled = false; SStartMsg msg; msg.player = VideoPlayer_AUDIO; msg.cachetotal = cachetotal; msg.cachetime = cachetime; msg.timestamp = audioframe.hasTimestamp ? audioframe.pts : DVD_NOPTS_VALUE; m_messageParent.Put(new CDVDMsgType<SStartMsg>(CDVDMsg::PLAYER_STARTED, msg)); } } } // guess next pts m_audioClock += audioframe.duration; int ret = m_pAudioCodec->Decode(nullptr, 0, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE); if (ret < 0) { CLog::Log(LOGERROR, "CVideoPlayerAudio::DecodeFrame - Decode Error. Skipping audio packet (%d)", ret); m_pAudioCodec->Reset(); break; } } // while decoder produces output } // demuxer packet pMsg->Release(); } }
bool CVideoPlayerAudio::ProcessDecoderOutput(DVDAudioFrame &audioframe) { if (audioframe.nb_frames <= audioframe.framesOut) { m_pAudioCodec->GetData(audioframe); if (audioframe.nb_frames == 0) { return false; } audioframe.hasTimestamp = true; if (audioframe.pts == DVD_NOPTS_VALUE) { audioframe.pts = m_audioClock; audioframe.hasTimestamp = false; } else { m_audioClock = audioframe.pts; } if (audioframe.format.m_sampleRate && m_streaminfo.samplerate != (int) audioframe.format.m_sampleRate) { // The sample rate has changed or we just got it for the first time // for this stream. See if we should enable/disable passthrough due // to it. m_streaminfo.samplerate = audioframe.format.m_sampleRate; if (SwitchCodecIfNeeded()) { audioframe.nb_frames = 0; return false; } } // if stream switches to realtime, disable pass through // or switch to resample if (m_processInfo.IsRealtimeStream() && m_synctype != SYNC_RESAMPLE) { m_synctype = SYNC_RESAMPLE; if (SwitchCodecIfNeeded()) { audioframe.nb_frames = 0; return false; } } // demuxer reads metatags that influence channel layout if (m_streaminfo.codec == AV_CODEC_ID_FLAC && m_streaminfo.channellayout) audioframe.format.m_channelLayout = CAEUtil::GetAEChannelLayout(m_streaminfo.channellayout); // we have successfully decoded an audio frame, setup renderer to match if (!m_audioSink.IsValidFormat(audioframe)) { if (m_speed) m_audioSink.Drain(); m_audioSink.Destroy(false); if (!m_audioSink.Create(audioframe, m_streaminfo.codec, m_synctype == SYNC_RESAMPLE)) CLog::Log(LOGERROR, "%s - failed to create audio renderer", __FUNCTION__); m_audioSink.SetDynamicRangeCompression((long)(m_processInfo.GetVideoSettings().m_VolumeAmplification * 100)); if (m_syncState == IDVDStreamPlayer::SYNC_INSYNC) m_audioSink.Resume(); } SetSyncType(audioframe.passthrough); } { double syncerror = m_audioSink.GetSyncError(); if (m_synctype == SYNC_DISCON && fabs(syncerror) > DVD_MSEC_TO_TIME(10)) { double correction = m_pClock->ErrorAdjust(syncerror, "CVideoPlayerAudio::OutputPacket"); if (correction != 0) { m_audioSink.SetSyncErrorCorrection(-correction); } } } int framesOutput = m_audioSink.AddPackets(audioframe); // guess next pts m_audioClock += audioframe.duration * ((double)framesOutput / audioframe.nb_frames); audioframe.framesOut += framesOutput; // signal to our parent that we have initialized if (m_syncState == IDVDStreamPlayer::SYNC_STARTING) { double cachetotal = m_audioSink.GetCacheTotal(); double cachetime = m_audioSink.GetCacheTime(); if (cachetime >= cachetotal * 0.75) { m_syncState = IDVDStreamPlayer::SYNC_WAITSYNC; m_stalled = false; SStartMsg msg; msg.player = VideoPlayer_AUDIO; msg.cachetotal = m_audioSink.GetMaxDelay() * DVD_TIME_BASE; msg.cachetime = m_audioSink.GetDelay(); msg.timestamp = audioframe.hasTimestamp ? audioframe.pts : DVD_NOPTS_VALUE; m_messageParent.Put(new CDVDMsgType<SStartMsg>(CDVDMsg::PLAYER_STARTED, msg)); m_streaminfo.channels = audioframe.format.m_channelLayout.Count(); m_processInfo.SetAudioChannels(audioframe.format.m_channelLayout); m_processInfo.SetAudioSampleRate(audioframe.format.m_sampleRate); m_processInfo.SetAudioBitsPerSample(audioframe.bits_per_sample); m_processInfo.SetAudioDecoderName(m_pAudioCodec->GetName()); m_messageParent.Put(new CDVDMsg(CDVDMsg::PLAYER_AVCHANGE)); } } return true; }
// decode one audio frame and returns its uncompressed size int CVideoPlayerAudio::DecodeFrame(DVDAudioFrame &audioframe) { int result = 0; // make sure the sent frame is clean audioframe.nb_frames = 0; while (!m_bStop) { bool switched = false; /* NOTE: the audio packet can contain several frames */ while( !m_bStop && m_decode.size > 0 ) { if( !m_pAudioCodec ) return DECODE_FLAG_ERROR; /* the packet dts refers to the first audioframe that starts in the packet */ double dts = m_ptsInput.Get(m_decode.size + m_pAudioCodec->GetBufferSize(), true); if (dts != DVD_NOPTS_VALUE) m_audioClock = dts; int len = m_pAudioCodec->Decode(m_decode.data, m_decode.size); if (len < 0 || len > m_decode.size) { /* if error, we skip the packet */ CLog::Log(LOGERROR, "CVideoPlayerAudio::DecodeFrame - Decode Error. Skipping audio packet (%d)", len); m_decode.Release(); m_pAudioCodec->Reset(); return DECODE_FLAG_ERROR; } m_audioStats.AddSampleBytes(len); m_decode.data += len; m_decode.size -= len; // get decoded data and the size of it m_pAudioCodec->GetData(audioframe); if (audioframe.nb_frames == 0) continue; if (audioframe.pts == DVD_NOPTS_VALUE) audioframe.pts = m_audioClock; if (audioframe.format.m_sampleRate && m_streaminfo.samplerate != (int) audioframe.format.m_sampleRate) { // The sample rate has changed or we just got it for the first time // for this stream. See if we should enable/disable passthrough due // to it. m_streaminfo.samplerate = audioframe.format.m_sampleRate; if (!switched && SwitchCodecIfNeeded()) { // passthrough has been enabled/disabled, reprocess the packet m_decode.data -= len; m_decode.size += len; switched = true; continue; } } // increase audioclock to after the packet m_audioClock += audioframe.duration; // if demux source want's us to not display this, continue if(m_decode.msg->GetPacketDrop()) result |= DECODE_FLAG_DROP; return result; } // free the current packet m_decode.Release(); if (m_messageQueue.ReceivedAbortRequest()) return DECODE_FLAG_ABORT; CDVDMsg* pMsg; int timeout = (int)(1000 * m_dvdAudio.GetCacheTime()) + 100; // read next packet and return -1 on error int priority = 1; //Do we want a new audio frame? if (m_syncState == IDVDStreamPlayer::SYNC_STARTING || /* when not started */ ALLOW_AUDIO(m_speed) || /* when playing normally */ m_speed < DVD_PLAYSPEED_PAUSE || /* when rewinding */ (m_speed > DVD_PLAYSPEED_NORMAL && m_audioClock < m_pClock->GetClock())) /* when behind clock in ff */ priority = 0; if (m_syncState == IDVDStreamPlayer::SYNC_WAITSYNC) priority = 1; // consider stream stalled if queue is empty // we can't sync audio to clock with an empty queue if (ALLOW_AUDIO(m_speed)) { timeout = 0; } MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, timeout, priority); if (ret == MSGQ_TIMEOUT) return DECODE_FLAG_TIMEOUT; if (MSGQ_IS_ERROR(ret)) return DECODE_FLAG_ABORT; if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET)) { m_decode.Attach((CDVDMsgDemuxerPacket*)pMsg); m_ptsInput.Add( m_decode.size, m_decode.dts ); } else if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE)) { if(((CDVDMsgGeneralSynchronize*)pMsg)->Wait( 100, SYNCSOURCE_AUDIO )) CLog::Log(LOGDEBUG, "CVideoPlayerAudio - CDVDMsg::GENERAL_SYNCHRONIZE"); else m_messageQueue.Put(pMsg->Acquire(), 1); /* push back as prio message, to process other prio messages */ } else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC)) { //player asked us to set internal clock double pts = static_cast<CDVDMsgDouble*>(pMsg)->m_value; CLog::Log(LOGDEBUG, "CVideoPlayerAudio - CDVDMsg::GENERAL_RESYNC(%f)", pts); m_audioClock = pts; m_ptsInput.Flush(); if (m_speed != DVD_PLAYSPEED_PAUSE) m_dvdAudio.Resume(); m_syncState = IDVDStreamPlayer::SYNC_INSYNC; } else if (pMsg->IsType(CDVDMsg::GENERAL_RESET)) { if (m_pAudioCodec) m_pAudioCodec->Reset(); m_decode.Release(); } else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) { bool sync = static_cast<CDVDMsgBool*>(pMsg)->m_value; m_dvdAudio.Flush(); m_ptsInput.Flush(); m_stalled = true; if (sync) { m_syncState = IDVDStreamPlayer::SYNC_STARTING; m_dvdAudio.Pause(); } if (m_pAudioCodec) m_pAudioCodec->Reset(); m_decode.Release(); } else if (pMsg->IsType(CDVDMsg::PLAYER_DISPLAYTIME)) { SPlayerState& state = ((CDVDMsgType<SPlayerState>*)pMsg)->m_value; if(state.time_src == ETIMESOURCE_CLOCK) state.time = DVD_TIME_TO_MSEC(m_pClock->GetClock(state.timestamp) + state.time_offset); else state.timestamp = CDVDClock::GetAbsoluteClock(); state.player = VideoPlayer_AUDIO; m_messageParent.Put(pMsg->Acquire()); } else if (pMsg->IsType(CDVDMsg::GENERAL_EOF)) { CLog::Log(LOGDEBUG, "CVideoPlayerAudio - CDVDMsg::GENERAL_EOF"); } else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED)) { double speed = static_cast<CDVDMsgInt*>(pMsg)->m_value; if (ALLOW_AUDIO(speed)) { if (speed != m_speed) { if (m_syncState == IDVDStreamPlayer::SYNC_INSYNC) m_dvdAudio.Resume(); } } else { m_dvdAudio.Pause(); } m_speed = speed; } else if (pMsg->IsType(CDVDMsg::AUDIO_SILENCE)) { m_silence = static_cast<CDVDMsgBool*>(pMsg)->m_value; CLog::Log(LOGDEBUG, "CVideoPlayerAudio - CDVDMsg::AUDIO_SILENCE(%f, %d)" , m_audioClock, m_silence); } else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE)) { CDVDMsgAudioCodecChange* msg(static_cast<CDVDMsgAudioCodecChange*>(pMsg)); OpenStream(msg->m_hints, msg->m_codec); msg->m_codec = NULL; } pMsg->Release(); } return 0; }