void CVideoPlayerAudio::Process() { CLog::Log(LOGNOTICE, "running thread: CVideoPlayerAudio::Process()"); DVDAudioFrame audioframe; audioframe.nb_frames = 0; audioframe.framesOut = 0; m_audioStats.Start(); bool onlyPrioMsgs = false; while (!m_bStop) { CDVDMsg* pMsg; int timeout = (int)(1000 * m_audioSink.GetCacheTime()); // 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 */ m_processInfo.IsTempoAllowed(static_cast<float>(m_speed)/DVD_PLAYSPEED_NORMAL) || 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; if (m_paused) priority = 1; if (onlyPrioMsgs) { priority = 1; timeout = 0; } MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, timeout, priority); onlyPrioMsgs = false; if (MSGQ_IS_ERROR(ret)) { CLog::Log(LOGERROR, "Got MSGQ_ABORT or MSGO_IS_ERROR return true"); break; } else if (ret == MSGQ_TIMEOUT) { if (ProcessDecoderOutput(audioframe)) { onlyPrioMsgs = true; continue; } // if we only wanted priority messages, this isn't a stall if (priority) continue; if (m_processInfo.IsTempoAllowed(static_cast<float>(m_speed)/DVD_PLAYSPEED_NORMAL) && !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 (static_cast<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), level: %d, cache: %f", pts, m_messageQueue.GetLevel(), m_audioSink.GetDelay()); double delay = m_audioSink.GetDelay(); if (pts > m_audioClock - delay + 0.5 * DVD_TIME_BASE) { m_audioSink.Flush(); } m_audioClock = pts + delay; if (m_speed != DVD_PLAYSPEED_PAUSE) m_audioSink.Resume(); m_syncState = IDVDStreamPlayer::SYNC_INSYNC; m_syncTimer.Set(3000); } else if (pMsg->IsType(CDVDMsg::GENERAL_RESET)) { if (m_pAudioCodec) m_pAudioCodec->Reset(); m_audioSink.Flush(); m_stalled = true; m_audioClock = 0; audioframe.nb_frames = 0; m_syncState = IDVDStreamPlayer::SYNC_STARTING; } else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) { bool sync = static_cast<CDVDMsgBool*>(pMsg)->m_value; m_audioSink.Flush(); m_stalled = true; m_audioClock = 0; audioframe.nb_frames = 0; if (sync) { m_syncState = IDVDStreamPlayer::SYNC_STARTING; m_audioSink.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 (m_processInfo.IsTempoAllowed(static_cast<float>(speed)/DVD_PLAYSPEED_NORMAL)) { if (speed != m_speed) { if (m_syncState == IDVDStreamPlayer::SYNC_INSYNC) { m_audioSink.Resume(); m_stalled = false; } } } else { m_audioSink.Pause(); } m_speed = (int)speed; } 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::GENERAL_PAUSE)) { m_paused = static_cast<CDVDMsgBool*>(pMsg)->m_value; CLog::Log(LOGDEBUG, "CVideoPlayerAudio - CDVDMsg::GENERAL_PAUSE: %d", m_paused); } else if (pMsg->IsType(CDVDMsg::PLAYER_REQUEST_STATE)) { SStateMsg msg; msg.player = VideoPlayer_AUDIO; msg.syncState = m_syncState; m_messageParent.Put(new CDVDMsgType<SStateMsg>(CDVDMsg::PLAYER_REPORT_STATE, msg)); } else if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET)) { DemuxPacket* pPacket = static_cast<CDVDMsgDemuxerPacket*>(pMsg)->GetPacket(); bool bPacketDrop = static_cast<CDVDMsgDemuxerPacket*>(pMsg)->GetPacketDrop(); if (bPacketDrop || (!m_processInfo.IsTempoAllowed(static_cast<float>(m_speed)/DVD_PLAYSPEED_NORMAL) && m_syncState == IDVDStreamPlayer::SYNC_INSYNC)) { pMsg->Release(); continue; } if (!m_pAudioCodec->AddData(*pPacket)) { m_messageQueue.PutBack(pMsg->Acquire()); onlyPrioMsgs = true; pMsg->Release(); continue; } m_audioStats.AddSampleBytes(pPacket->iSize); UpdatePlayerInfo(); if (ProcessDecoderOutput(audioframe)) { onlyPrioMsgs = true; } } // demuxer packet pMsg->Release(); } }
void CVideoPlayerVideo::Process() { CLog::Log(LOGNOTICE, "running thread: video_thread"); double pts = 0; double frametime = (double)DVD_TIME_BASE / m_fFrameRate; bool bRequestDrop = false; int iDropDirective; bool onlyPrioMsgs = false; m_videoStats.Start(); m_droppingStats.Reset(); m_iDroppedFrames = 0; m_rewindStalled = false; m_outputSate = OUTPUT_NORMAL; while (!m_bStop) { int iQueueTimeOut = (int)(m_stalled ? frametime : frametime * 10) / 1000; int iPriority = 0; if (m_syncState == IDVDStreamPlayer::SYNC_WAITSYNC) iPriority = 1; if (m_paused) iPriority = 1; if (onlyPrioMsgs) { iPriority = 1; iQueueTimeOut = 1; } CDVDMsg* pMsg; MsgQueueReturnCode ret = GetMessage(&pMsg, iQueueTimeOut, iPriority); onlyPrioMsgs = false; if (MSGQ_IS_ERROR(ret)) { CLog::Log(LOGERROR, "Got MSGQ_ABORT or MSGO_IS_ERROR return true"); break; } else if (ret == MSGQ_TIMEOUT) { if (m_outputSate == OUTPUT_AGAIN && m_picture.videoBuffer) { m_outputSate = OutputPicture(&m_picture); if (m_outputSate == OUTPUT_AGAIN) { onlyPrioMsgs = true; continue; } } // don't ask for a new frame if we can't deliver it to renderer else if ((m_speed != DVD_PLAYSPEED_PAUSE || m_processInfo.IsFrameAdvance() || m_syncState != IDVDStreamPlayer::SYNC_INSYNC) && !m_paused) { if (ProcessDecoderOutput(frametime, pts)) { onlyPrioMsgs = true; continue; } } // if we only wanted priority messages, this isn't a stall if (iPriority) continue; //Okey, start rendering at stream fps now instead, we are likely in a stillframe if (!m_stalled) { // squeeze pictures out while (!m_bStop && m_pVideoCodec) { m_pVideoCodec->SetCodecControl(DVD_CODEC_CTRL_DRAIN); if (!ProcessDecoderOutput(frametime, pts)) break; } CLog::Log(LOGINFO, "CVideoPlayerVideo - Stillframe detected, switching to forced %f fps", m_fFrameRate); m_stalled = true; pts += frametime * 4; } // Waiting timed out, output last picture if (m_picture.videoBuffer) { m_picture.pts = pts; m_outputSate = OutputPicture(&m_picture); pts += frametime; } continue; } if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE)) { if (static_cast<CDVDMsgGeneralSynchronize*>(pMsg)->Wait(100, SYNCSOURCE_VIDEO)) { CLog::Log(LOGDEBUG, "CVideoPlayerVideo - CDVDMsg::GENERAL_SYNCHRONIZE"); } else SendMessage(pMsg->Acquire(), 1); /* push back as prio message, to process other prio messages */ m_droppingStats.Reset(); } else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC)) { pts = static_cast<CDVDMsgDouble*>(pMsg)->m_value; m_syncState = IDVDStreamPlayer::SYNC_INSYNC; m_droppingStats.Reset(); m_rewindStalled = false; m_renderManager.ShowVideo(true); CLog::Log(LOGDEBUG, "CVideoPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f)", pts); } else if (pMsg->IsType(CDVDMsg::VIDEO_SET_ASPECT)) { CLog::Log(LOGDEBUG, "CVideoPlayerVideo - CDVDMsg::VIDEO_SET_ASPECT"); m_fForcedAspectRatio = static_cast<float>(*static_cast<CDVDMsgDouble*>(pMsg)); } else if (pMsg->IsType(CDVDMsg::GENERAL_RESET)) { if(m_pVideoCodec) m_pVideoCodec->Reset(); if (m_picture.videoBuffer) { m_picture.videoBuffer->Release(); m_picture.videoBuffer = nullptr; } m_packets.clear(); m_droppingStats.Reset(); m_syncState = IDVDStreamPlayer::SYNC_STARTING; m_renderManager.ShowVideo(false); m_rewindStalled = false; } else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (CVideoPlayerVideo::Flush()) { bool sync = static_cast<CDVDMsgBool*>(pMsg)->m_value; if(m_pVideoCodec) m_pVideoCodec->Reset(); if (m_picture.videoBuffer) { m_picture.videoBuffer->Release(); m_picture.videoBuffer = nullptr; } m_packets.clear(); pts = 0; m_rewindStalled = false; m_ptsTracker.Flush(); //we need to recalculate the framerate //! @todo this needs to be set on a streamchange instead ResetFrameRateCalc(); m_droppingStats.Reset(); m_stalled = true; if (sync) { m_syncState = IDVDStreamPlayer::SYNC_STARTING; m_renderManager.ShowVideo(false); } m_renderManager.DiscardBuffer(); } else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED)) { m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value; if (m_pVideoCodec) m_pVideoCodec->SetSpeed(m_speed); m_droppingStats.Reset(); } else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE)) { CDVDMsgVideoCodecChange* msg(static_cast<CDVDMsgVideoCodecChange*>(pMsg)); while (!m_bStop && m_pVideoCodec) { m_pVideoCodec->SetCodecControl(DVD_CODEC_CTRL_DRAIN); bool cont = ProcessDecoderOutput(frametime, pts); if (!cont) break; } OpenStream(msg->m_hints, msg->m_codec); msg->m_codec = NULL; if (m_picture.videoBuffer) { m_picture.videoBuffer->Release(); m_picture.videoBuffer = nullptr; } } else if (pMsg->IsType(CDVDMsg::VIDEO_DRAIN)) { while (!m_bStop && m_pVideoCodec) { m_pVideoCodec->SetCodecControl(DVD_CODEC_CTRL_DRAIN); if (!ProcessDecoderOutput(frametime, pts)) break; } } else if (pMsg->IsType(CDVDMsg::GENERAL_PAUSE)) { m_paused = static_cast<CDVDMsgBool*>(pMsg)->m_value; CLog::Log(LOGDEBUG, "CVideoPlayerVideo - CDVDMsg::GENERAL_PAUSE: %d", m_paused); } else if (pMsg->IsType(CDVDMsg::PLAYER_REQUEST_STATE)) { SStateMsg msg; msg.player = VideoPlayer_VIDEO; msg.syncState = m_syncState; m_messageParent.Put(new CDVDMsgType<SStateMsg>(CDVDMsg::PLAYER_REPORT_STATE, msg)); } else if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET)) { DemuxPacket* pPacket = static_cast<CDVDMsgDemuxerPacket*>(pMsg)->GetPacket(); bool bPacketDrop = static_cast<CDVDMsgDemuxerPacket*>(pMsg)->GetPacketDrop(); if (m_stalled) { CLog::Log(LOGINFO, "CVideoPlayerVideo - Stillframe left, switching to normal playback"); m_stalled = false; } bRequestDrop = false; iDropDirective = CalcDropRequirement(pts); if ((iDropDirective & DROP_VERYLATE) && m_bAllowDrop && !bPacketDrop) { bRequestDrop = true; } if (iDropDirective & DROP_DROPPED) { m_iDroppedFrames++; m_ptsTracker.Flush(); } if (m_messageQueue.GetDataSize() == 0 || m_speed < 0) { bRequestDrop = false; m_iDroppedRequest = 0; m_iLateFrames = 0; } int codecControl = 0; if (iDropDirective & DROP_BUFFER_LEVEL) codecControl |= DVD_CODEC_CTRL_HURRY; if (m_speed > DVD_PLAYSPEED_NORMAL) codecControl |= DVD_CODEC_CTRL_NO_POSTPROC; if (bPacketDrop) codecControl |= DVD_CODEC_CTRL_DROP; if (bRequestDrop) codecControl |= DVD_CODEC_CTRL_DROP_ANY; if (!m_renderManager.Supports(RENDERFEATURE_ROTATION)) codecControl |= DVD_CODEC_CTRL_ROTATE; m_pVideoCodec->SetCodecControl(codecControl); if (m_pVideoCodec->AddData(*pPacket)) { // buffer packets so we can recover should decoder flush for some reason if (m_pVideoCodec->GetConvergeCount() > 0) { m_packets.emplace_back(pMsg, 0); if (m_packets.size() > m_pVideoCodec->GetConvergeCount() || m_packets.size() * frametime > DVD_SEC_TO_TIME(10)) m_packets.pop_front(); } m_videoStats.AddSampleBytes(pPacket->iSize); if (ProcessDecoderOutput(frametime, pts)) { onlyPrioMsgs = true; } } else { SendMessageBack(pMsg->Acquire()); onlyPrioMsgs = true; } } // all data is used by the decoder, we can safely free it now pMsg->Release(); } }