void CDVDPlayerVideo::Process() { CLog::Log(LOGNOTICE, "running thread: video_thread"); DVDVideoPicture picture; CPulldownCorrection pulldown; CDVDVideoPPFFmpeg mPostProcess(""); CStdString sPostProcessType; bool bPostProcessDeint = false; memset(&picture, 0, sizeof(DVDVideoPicture)); double pts = 0; double frametime = (double)DVD_TIME_BASE / m_fFrameRate; int iDropped = 0; //frames dropped in a row bool bRequestDrop = false; m_videoStats.Start(); while (!m_bStop) { int iQueueTimeOut = (int)(m_stalled ? frametime / 4 : frametime * 10) / 1000; int iPriority = (m_speed == DVD_PLAYSPEED_PAUSE && m_started) ? 1 : 0; CDVDMsg* pMsg; MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, iQueueTimeOut, iPriority); if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT) { CLog::Log(LOGERROR, "Got MSGQ_ABORT or MSGO_IS_ERROR return true"); break; } else if (ret == MSGQ_TIMEOUT) { // 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 ) { if(m_started) CLog::Log(LOGINFO, "CDVDPlayerVideo - Stillframe detected, switching to forced %f fps", m_fFrameRate); m_stalled = true; pts+= frametime*4; } //Waiting timed out, output last picture if( picture.iFlags & DVP_FLAG_ALLOCATED ) { //Remove interlaced flag before outputting //no need to output this as if it was interlaced picture.iFlags &= ~DVP_FLAG_INTERLACED; picture.iFlags |= DVP_FLAG_NOSKIP; OutputPicture(&picture, pts); pts+= frametime; } continue; } if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE)) { ((CDVDMsgGeneralSynchronize*)pMsg)->Wait( &m_bStop, SYNCSOURCE_VIDEO ); CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_SYNCHRONIZE"); pMsg->Release(); /* we may be very much off correct pts here, but next picture may be a still*/ /* make sure it isn't dropped */ m_iNrOfPicturesNotToSkip = 5; continue; } else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC)) { CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg; if(pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE) pts = pMsgGeneralResync->m_timestamp; double delay = m_FlipTimeStamp - m_pClock->GetAbsoluteClock(); if( delay > frametime ) delay = frametime; else if( delay < 0 ) delay = 0; if(pMsgGeneralResync->m_clock) { CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 1)", pts); m_pClock->Discontinuity(pts - delay); } else CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 0)", pts); pMsgGeneralResync->Release(); continue; } else if (pMsg->IsType(CDVDMsg::GENERAL_DELAY)) { if (m_speed != DVD_PLAYSPEED_PAUSE) { double timeout = static_cast<CDVDMsgDouble*>(pMsg)->m_value; CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_DELAY(%f)", timeout); timeout *= (double)DVD_PLAYSPEED_NORMAL / abs(m_speed); timeout += CDVDClock::GetAbsoluteClock(); while(!m_bStop && CDVDClock::GetAbsoluteClock() < timeout) Sleep(1); } } else if (pMsg->IsType(CDVDMsg::VIDEO_SET_ASPECT)) { CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::VIDEO_SET_ASPECT"); m_fForcedAspectRatio = *((CDVDMsgDouble*)pMsg); } else if (pMsg->IsType(CDVDMsg::GENERAL_RESET)) { if(m_pVideoCodec) m_pVideoCodec->Reset(); picture.iFlags &= ~DVP_FLAG_ALLOCATED; m_packets.clear(); m_started = false; } else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (CDVDPlayerVideo::Flush()) { if(m_pVideoCodec) m_pVideoCodec->Reset(); picture.iFlags &= ~DVP_FLAG_ALLOCATED; m_packets.clear(); m_pullupCorrection.Flush(); //we need to recalculate the framerate //TODO: this needs to be set on a streamchange instead ResetFrameRateCalc(); m_stalled = true; m_started = false; } else if (pMsg->IsType(CDVDMsg::VIDEO_NOSKIP)) { // libmpeg2 is also returning incomplete frames after a dvd cell change // so the first few pictures are not the correct ones to display in some cases // just display those together with the correct one. // (setting it to 2 will skip some menu stills, 5 is working ok for me). m_iNrOfPicturesNotToSkip = 5; } else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED)) { m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value; if(m_speed == DVD_PLAYSPEED_PAUSE) m_iNrOfPicturesNotToSkip = 0; } else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED)) { if(m_started) m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO)); } else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE)) { CDVDMsgVideoCodecChange* msg(static_cast<CDVDMsgVideoCodecChange*>(pMsg)); OpenStream(msg->m_hints, msg->m_codec); msg->m_codec = NULL; picture.iFlags &= ~DVP_FLAG_ALLOCATED; } if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET)) { DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket(); bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop(); if (m_stalled) { CLog::Log(LOGINFO, "CDVDPlayerVideo - Stillframe left, switching to normal playback"); m_stalled = false; //don't allow the first frames after a still to be dropped //sometimes we get multiple stills (long duration frames) after each other //in normal mpegs m_iNrOfPicturesNotToSkip = 5; } else if( iDropped*frametime > DVD_MSEC_TO_TIME(100) && m_iNrOfPicturesNotToSkip == 0 ) { // if we dropped too many pictures in a row, insert a forced picture m_iNrOfPicturesNotToSkip = 1; } #ifdef PROFILE bRequestDrop = false; #else if (m_messageQueue.GetDataSize() == 0 || m_speed < 0) { bRequestDrop = false; m_iDroppedRequest = 0; m_iLateFrames = 0; } #endif // if player want's us to drop this packet, do so nomatter what if(bPacketDrop) bRequestDrop = true; // tell codec if next frame should be dropped // problem here, if one packet contains more than one frame // both frames will be dropped in that case instead of just the first // decoder still needs to provide an empty image structure, with correct flags m_pVideoCodec->SetDropState(bRequestDrop); // ask codec to do deinterlacing if possible EDEINTERLACEMODE mDeintMode = g_settings.m_currentVideoSettings.m_DeinterlaceMode; EINTERLACEMETHOD mInt = g_renderManager.AutoInterlaceMethod(g_settings.m_currentVideoSettings.m_InterlaceMethod); unsigned int mFilters = 0; if (mDeintMode != VS_DEINTERLACEMODE_OFF) { if (mInt == VS_INTERLACEMETHOD_DEINTERLACE) mFilters = CDVDVideoCodec::FILTER_DEINTERLACE_ANY; else if(mInt == VS_INTERLACEMETHOD_DEINTERLACE_HALF) mFilters = CDVDVideoCodec::FILTER_DEINTERLACE_ANY | CDVDVideoCodec::FILTER_DEINTERLACE_HALFED; if (mDeintMode == VS_DEINTERLACEMODE_AUTO && mFilters) mFilters |= CDVDVideoCodec::FILTER_DEINTERLACE_FLAGGED; } mFilters = m_pVideoCodec->SetFilters(mFilters); int iDecoderState = m_pVideoCodec->Decode(pPacket->pData, pPacket->iSize, pPacket->dts, pPacket->pts); // buffer packets so we can recover should decoder flush for some reason if(m_pVideoCodec->GetConvergeCount() > 0) { m_packets.push_back(DVDMessageListItem(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); // assume decoder dropped a picture if it didn't give us any // picture from a demux packet, this should be reasonable // for libavformat as a demuxer as it normally packetizes // pictures when they come from demuxer if(bRequestDrop && !bPacketDrop && (iDecoderState & VC_BUFFER) && !(iDecoderState & VC_PICTURE)) { m_iDroppedFrames++; iDropped++; } // loop while no error while (!m_bStop) { // if decoder was flushed, we need to seek back again to resume rendering if (iDecoderState & VC_FLUSHED) { CLog::Log(LOGDEBUG, "CDVDPlayerVideo - video decoder was flushed"); while(!m_packets.empty()) { CDVDMsgDemuxerPacket* msg = (CDVDMsgDemuxerPacket*)m_packets.front().message->Acquire(); m_packets.pop_front(); // all packets except the last one should be dropped // if prio packets and current packet should be dropped, this is likely a new reset msg->m_drop = !m_packets.empty() || (iPriority > 0 && bPacketDrop); m_messageQueue.Put(msg, iPriority + 10); } m_pVideoCodec->Reset(); m_packets.clear(); break; } // if decoder had an error, tell it to reset to avoid more problems if (iDecoderState & VC_ERROR) { CLog::Log(LOGDEBUG, "CDVDPlayerVideo - video decoder returned error"); break; } // check for a new picture if (iDecoderState & VC_PICTURE) { do { // note: decoder may have multiple buffers to return to us, so // keep popping out buffers until there are no more: m_pVideoCodec->ClearPicture(&picture); if (! m_pVideoCodec->GetPicture(&picture)) break; sPostProcessType.clear(); picture.iGroupId = pPacket->iGroupId; if(picture.iDuration == 0.0) picture.iDuration = frametime; if(bPacketDrop) picture.iFlags |= DVP_FLAG_DROPPED; if (m_iNrOfPicturesNotToSkip > 0) { picture.iFlags |= DVP_FLAG_NOSKIP; m_iNrOfPicturesNotToSkip--; } // validate picture timing, // if both dts/pts invalid, use pts calulated from picture.iDuration // if pts invalid use dts, else use picture.pts as passed if (picture.dts == DVD_NOPTS_VALUE && picture.pts == DVD_NOPTS_VALUE) picture.pts = pts; else if (picture.pts == DVD_NOPTS_VALUE) picture.pts = picture.dts; /* use forced aspect if any */ if( m_fForcedAspectRatio != 0.0f ) picture.iDisplayWidth = (int) (picture.iDisplayHeight * m_fForcedAspectRatio); //Deinterlace if codec said format was interlaced or if we have selected we want to deinterlace //this video if ((mDeintMode == VS_DEINTERLACEMODE_AUTO && (picture.iFlags & DVP_FLAG_INTERLACED)) || mDeintMode == VS_DEINTERLACEMODE_FORCE) { if(mInt == VS_INTERLACEMETHOD_SW_BLEND) { if (!sPostProcessType.empty()) sPostProcessType += ","; sPostProcessType += g_advancedSettings.m_videoPPFFmpegDeint; bPostProcessDeint = true; } } if (g_settings.m_currentVideoSettings.m_PostProcess) { if (!sPostProcessType.empty()) sPostProcessType += ","; // This is what mplayer uses for its "high-quality filter combination" sPostProcessType += g_advancedSettings.m_videoPPFFmpegPostProc; } if (!sPostProcessType.empty()) { mPostProcess.SetType(sPostProcessType, bPostProcessDeint); if (mPostProcess.Process(&picture)) mPostProcess.GetPicture(&picture); } /* if frame has a pts (usually originiating from demux packet), use that */ if(picture.pts != DVD_NOPTS_VALUE) { if(pulldown.enabled()) picture.pts += pulldown.pts(); pts = picture.pts; } if(pulldown.enabled()) { picture.iDuration = pulldown.dur(); pulldown.next(); } if (picture.iRepeatPicture) picture.iDuration *= picture.iRepeatPicture + 1; #if 1 int iResult = OutputPicture(&picture, pts); #elif 0 // testing NV12 rendering functions DVDVideoPicture* pTempNV12Picture = CDVDCodecUtils::ConvertToNV12Picture(&picture); int iResult = OutputPicture(pTempNV12Picture, pts); CDVDCodecUtils::FreePicture(pTempNV12Picture); #elif 0 // testing YUY2 or UYVY rendering functions // WARNING: since this scales a full YV12 frame, weaving artifacts will show on interlaced content // even with the deinterlacer on DVDVideoPicture* pTempYUVPackedPicture = CDVDCodecUtils::ConvertToYUV422PackedPicture(&picture, DVDVideoPicture::FMT_UYVY); //DVDVideoPicture* pTempYUVPackedPicture = CDVDCodecUtils::ConvertToYUV422PackedPicture(&picture, DVDVideoPicture::FMT_YUY2); int iResult = OutputPicture(pTempYUVPackedPicture, pts); CDVDCodecUtils::FreePicture(pTempYUVPackedPicture); #endif if(m_started == false) { m_codecname = m_pVideoCodec->GetName(); m_started = true; m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO)); } // guess next frame pts. iDuration is always valid if (m_speed != 0) pts += picture.iDuration * m_speed / abs(m_speed); if( iResult & EOS_ABORT ) { //if we break here and we directly try to decode again wihout //flushing the video codec things break for some reason //i think the decoder (libmpeg2 atleast) still has a pointer //to the data, and when the packet is freed that will fail. iDecoderState = m_pVideoCodec->Decode(NULL, 0, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE); break; } if( (iResult & EOS_DROPPED) && !bPacketDrop ) { m_iDroppedFrames++; iDropped++; } else iDropped = 0; bRequestDrop = (iResult & EOS_VERYLATE) == EOS_VERYLATE; } while (true); } /* if (iDecoderState & VC_USERDATA) { // found some userdata while decoding a frame // could be closed captioning DVDVideoUserData videoUserData; if (m_pVideoCodec->GetUserData(&videoUserData)) { ProcessVideoUserData(&videoUserData, pts); } } */ // if the decoder needs more data, we just break this loop // and try to get more data from the videoQueue if (iDecoderState & VC_BUFFER) break; // the decoder didn't need more data, flush the remaning buffer iDecoderState = m_pVideoCodec->Decode(NULL, 0, DVD_NOPTS_VALUE, DVD_NOPTS_VALUE); } } // all data is used by the decoder, we can safely free it now pMsg->Release(); } // we need to let decoder release any picture retained resources. m_pVideoCodec->ClearPicture(&picture); }
void OMXPlayerVideo::Process() { double pts = 0; double frametime = (double)DVD_TIME_BASE / m_fFrameRate; bool bRequestDrop = false; m_videoStats.Start(); while(!m_bStop) { CDVDMsg* pMsg; int iQueueTimeOut = (int)(m_stalled ? frametime / 4 : frametime * 10) / 1000; int iPriority = (m_speed == DVD_PLAYSPEED_PAUSE && m_started) ? 1 : 0; MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, iQueueTimeOut, iPriority); if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT) { CLog::Log(LOGERROR, "Got MSGQ_ABORT or MSGO_IS_ERROR return true"); break; } else if (ret == MSGQ_TIMEOUT) { // 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 ) { if(m_started) CLog::Log(LOGINFO, "COMXPlayerVideo - Stillframe detected, switching to forced %f fps", m_fFrameRate); m_stalled = true; pts += frametime*4; } pts += frametime; continue; } if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE)) { if(((CDVDMsgGeneralSynchronize*)pMsg)->Wait(100, SYNCSOURCE_VIDEO)) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_SYNCHRONIZE"); } else m_messageQueue.Put(pMsg->Acquire(), 1); /* push back as prio message, to process other prio messages */ pMsg->Release(); continue; } else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC)) { CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg; if(pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE) pts = pMsgGeneralResync->m_timestamp; double delay = m_FlipTimeStamp - m_av_clock->GetAbsoluteClock(); if( delay > frametime ) delay = frametime; else if( delay < 0 ) delay = 0; if(pMsgGeneralResync->m_clock) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 1)", pts); m_av_clock->Discontinuity(pts - delay); //m_av_clock->OMXUpdateClock(pts - delay); } else CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 0)", pts); pMsgGeneralResync->Release(); continue; } else if (pMsg->IsType(CDVDMsg::GENERAL_DELAY)) { if (m_speed != DVD_PLAYSPEED_PAUSE) { double timeout = static_cast<CDVDMsgDouble*>(pMsg)->m_value; CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_DELAY(%f)", timeout); timeout *= (double)DVD_PLAYSPEED_NORMAL / abs(m_speed); timeout += m_av_clock->GetAbsoluteClock(); while(!m_bStop && m_av_clock->GetAbsoluteClock() < timeout) Sleep(1); } } else if (pMsg->IsType(CDVDMsg::VIDEO_SET_ASPECT)) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::VIDEO_SET_ASPECT"); m_fForcedAspectRatio = *((CDVDMsgDouble*)pMsg); } else if (pMsg->IsType(CDVDMsg::GENERAL_RESET)) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_RESET"); m_av_clock->Lock(); m_av_clock->OMXStop(false); m_omxVideo.Reset(); m_av_clock->OMXReset(false); m_av_clock->UnLock(); m_started = false; m_iSleepEndTime = DVD_NOPTS_VALUE; } else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (COMXPlayerVideo::Flush()) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_FLUSH"); m_stalled = true; m_started = false; m_iSleepEndTime = DVD_NOPTS_VALUE; m_av_clock->Lock(); m_av_clock->OMXStop(false); m_omxVideo.Reset(); m_av_clock->OMXReset(false); m_av_clock->UnLock(); } else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED)) { m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value; } else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED)) { if(m_started) m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO)); } else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE)) { COMXMsgVideoCodecChange* msg(static_cast<COMXMsgVideoCodecChange*>(pMsg)); OpenStream(msg->m_hints, msg->m_codec); msg->m_codec = NULL; } else if (pMsg->IsType(CDVDMsg::GENERAL_EOF) && !m_audio_count) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_EOF"); WaitCompletion(); } else if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET)) { DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket(); bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop(); if (m_messageQueue.GetDataSize() == 0 || m_speed < 0) { bRequestDrop = false; } // if player want's us to drop this packet, do so nomatter what if(bPacketDrop) bRequestDrop = true; m_omxVideo.SetDropState(bRequestDrop); while (!m_bStop) { if((int)m_omxVideo.GetFreeSpace() < pPacket->iSize) { Sleep(10); continue; } if (m_stalled) { CLog::Log(LOGINFO, "COMXPlayerVideo - Stillframe left, switching to normal playback"); m_stalled = false; } double output_pts = 0; // validate picture timing, // if both dts/pts invalid, use pts calulated from picture.iDuration // if pts invalid use dts, else use picture.pts as passed if (pPacket->dts == DVD_NOPTS_VALUE && pPacket->pts == DVD_NOPTS_VALUE) output_pts = pts; else if (pPacket->pts == DVD_NOPTS_VALUE) output_pts = pts; else output_pts = pPacket->pts; if(pPacket->pts != DVD_NOPTS_VALUE) pPacket->pts += m_iVideoDelay; if(pPacket->dts != DVD_NOPTS_VALUE) pPacket->dts += m_iVideoDelay; if(pPacket->duration == 0) pPacket->duration = frametime; if(output_pts != DVD_NOPTS_VALUE) pts = output_pts; m_omxVideo.Decode(pPacket->pData, pPacket->iSize, pPacket->dts, pPacket->pts); Output(pPacket->iGroupId, output_pts, bRequestDrop); if(m_started == false) { m_codecname = m_omxVideo.GetDecoderName(); m_started = true; m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO)); } // guess next frame pts. iDuration is always valid if (m_speed != 0) pts += pPacket->duration * m_speed / abs(m_speed); break; } bRequestDrop = false; m_videoStats.AddSampleBytes(pPacket->iSize); } pMsg->Release(); } }
void OMXPlayerVideo::Process() { double pts = 0; double frametime = (double)DVD_TIME_BASE / m_fFrameRate; bool bRequestDrop = false; m_videoStats.Start(); while(!m_bStop) { CDVDMsg* pMsg; int iQueueTimeOut = (int)(m_stalled ? frametime / 4 : frametime * 10) / 1000; int iPriority = (m_speed == DVD_PLAYSPEED_PAUSE && m_started) ? 1 : 0; MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, iQueueTimeOut, iPriority); if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT) { CLog::Log(LOGERROR, "OMXPlayerVideo: Got MSGQ_IS_ERROR(%d) Aborting", (int)ret); break; } else if (ret == MSGQ_TIMEOUT) { continue; } if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE)) { if(((CDVDMsgGeneralSynchronize*)pMsg)->Wait(100, SYNCSOURCE_VIDEO)) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_SYNCHRONIZE"); } else m_messageQueue.Put(pMsg->Acquire(), 1); /* push back as prio message, to process other prio messages */ pMsg->Release(); continue; } else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC)) { CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg; if(pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE) pts = pMsgGeneralResync->m_timestamp; double delay = m_FlipTimeStamp - m_av_clock->GetAbsoluteClock(); if( delay > frametime ) delay = frametime; else if( delay < 0 ) delay = 0; if(pMsgGeneralResync->m_clock) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 1)", pts); m_av_clock->Discontinuity(pts - delay); //m_av_clock->OMXUpdateClock(pts - delay); } else CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 0)", pts); pMsgGeneralResync->Release(); continue; } else if (pMsg->IsType(CDVDMsg::GENERAL_DELAY)) { if (m_speed != DVD_PLAYSPEED_PAUSE) { double timeout = static_cast<CDVDMsgDouble*>(pMsg)->m_value; CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_DELAY(%f)", timeout); timeout *= (double)DVD_PLAYSPEED_NORMAL / abs(m_speed); timeout += m_av_clock->GetAbsoluteClock(); while(!m_bStop && m_av_clock->GetAbsoluteClock() < timeout) Sleep(1); } } else if (pMsg->IsType(CDVDMsg::VIDEO_SET_ASPECT)) { m_fForcedAspectRatio = *((CDVDMsgDouble*)pMsg); CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::VIDEO_SET_ASPECT %.2f", m_fForcedAspectRatio); } else if (pMsg->IsType(CDVDMsg::GENERAL_RESET)) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_RESET"); m_av_clock->Lock(); m_av_clock->OMXStop(false); m_omxVideo.Reset(); m_av_clock->OMXReset(false); m_av_clock->UnLock(); m_started = false; m_iSleepEndTime = DVD_NOPTS_VALUE; } else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (COMXPlayerVideo::Flush()) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_FLUSH"); m_stalled = true; m_started = false; m_iSleepEndTime = DVD_NOPTS_VALUE; m_av_clock->Lock(); m_av_clock->OMXStop(false); m_omxVideo.Reset(); m_av_clock->OMXReset(false); m_av_clock->UnLock(); m_flush = false; } else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED)) { if (m_speed != static_cast<CDVDMsgInt*>(pMsg)->m_value) { m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value; CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::PLAYER_SETSPEED %d", m_speed); } } else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED)) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::PLAYER_STARTED %d", m_started); if(m_started) m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO)); } else if (pMsg->IsType(CDVDMsg::PLAYER_DISPLAYTIME)) { COMXPlayer::SPlayerState& state = ((CDVDMsgType<COMXPlayer::SPlayerState>*)pMsg)->m_value; if(state.time_src == COMXPlayer::ETIMESOURCE_CLOCK) state.time = DVD_TIME_TO_MSEC(m_av_clock->OMXMediaTime(true)); //state.time = DVD_TIME_TO_MSEC(m_av_clock->GetClock(state.timestamp) + state.time_offset); else state.timestamp = m_av_clock->GetAbsoluteClock(); state.player = DVDPLAYER_VIDEO; m_messageParent.Put(pMsg->Acquire()); } else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE)) { COMXMsgVideoCodecChange* msg(static_cast<COMXMsgVideoCodecChange*>(pMsg)); OpenStream(msg->m_hints, msg->m_codec); msg->m_codec = NULL; } else if (pMsg->IsType(CDVDMsg::GENERAL_EOF) && !m_audio_count) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_EOF"); WaitCompletion(); } else if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET)) { DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket(); bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop(); #ifdef _DEBUG CLog::Log(LOGINFO, "Video: dts:%.0f pts:%.0f size:%d (s:%d f:%d d:%d l:%d) s:%d %d/%d late:%d\n", pPacket->dts, pPacket->pts, (int)pPacket->iSize, m_started, m_flush, bPacketDrop, m_stalled, m_speed, 0, 0, m_av_clock->OMXLateCount(1)); #endif if (m_messageQueue.GetDataSize() == 0 || m_speed < 0) { bRequestDrop = false; } // if player want's us to drop this packet, do so nomatter what if(bPacketDrop) bRequestDrop = true; m_omxVideo.SetDropState(bRequestDrop); while (!m_bStop) { // discard if flushing as clocks may be stopped and we'll never submit it if (m_flush) break; if((int)m_omxVideo.GetFreeSpace() < pPacket->iSize) { Sleep(10); continue; } if (m_stalled) { CLog::Log(LOGINFO, "COMXPlayerVideo - Stillframe left, switching to normal playback"); m_stalled = false; } double output_pts = 0; // validate picture timing, // if both dts/pts invalid, use pts calulated from picture.iDuration // if pts invalid use dts, else use picture.pts as passed if (pPacket->dts == DVD_NOPTS_VALUE && pPacket->pts == DVD_NOPTS_VALUE) output_pts = pts; else if (pPacket->pts == DVD_NOPTS_VALUE) output_pts = pts; else output_pts = pPacket->pts; if(pPacket->pts != DVD_NOPTS_VALUE) pPacket->pts += m_iVideoDelay; if(pPacket->dts != DVD_NOPTS_VALUE) pPacket->dts += m_iVideoDelay; if(pPacket->duration == 0) pPacket->duration = frametime; if(output_pts != DVD_NOPTS_VALUE) pts = output_pts; m_omxVideo.Decode(pPacket->pData, pPacket->iSize, pPacket->dts, pPacket->pts); Output(pPacket->iGroupId, output_pts, bRequestDrop); if(m_started == false) { m_codecname = m_omxVideo.GetDecoderName(); m_started = true; m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO)); } // guess next frame pts. iDuration is always valid if (m_speed != 0) pts += pPacket->duration * m_speed / abs(m_speed); break; } bRequestDrop = false; m_videoStats.AddSampleBytes(pPacket->iSize); } pMsg->Release(); } }
void CDVDPlayerVideo::Process() { CLog::Log(LOGNOTICE, "running thread: video_thread"); DVDVideoPicture picture; CDVDVideoPPFFmpeg mDeinterlace(CDVDVideoPPFFmpeg::ED_DEINT_FFMPEG); memset(&picture, 0, sizeof(DVDVideoPicture)); double pts = 0; double frametime = (double)DVD_TIME_BASE / m_fFrameRate; int iDropped = 0; //frames dropped in a row bool bRequestDrop = false; m_videoStats.Start(); while (!m_bStop) { while (!m_bStop && m_speed == DVD_PLAYSPEED_PAUSE && !m_messageQueue.RecievedAbortRequest() && m_iNrOfPicturesNotToSkip==0) Sleep(5); int iQueueTimeOut = (int)(m_stalled ? frametime / 4 : frametime * 10) / 1000; CDVDMsg* pMsg; MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, iQueueTimeOut); if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT) { CLog::Log(LOGERROR, "Got MSGQ_ABORT or MSGO_IS_ERROR return true"); break; } else if (ret == MSGQ_TIMEOUT) { //Okey, start rendering at stream fps now instead, we are likely in a stillframe if( !m_stalled && m_started ) { CLog::Log(LOGINFO, "CDVDPlayerVideo - Stillframe detected, switching to forced %f fps", m_fFrameRate); m_stalled = true; pts+= frametime*4; } //Waiting timed out, output last picture if( picture.iFlags & DVP_FLAG_ALLOCATED ) { //Remove interlaced flag before outputting //no need to output this as if it was interlaced picture.iFlags &= ~DVP_FLAG_INTERLACED; picture.iFlags |= DVP_FLAG_NOSKIP; OutputPicture(&picture, pts); pts+= frametime; } continue; } if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE)) { ((CDVDMsgGeneralSynchronize*)pMsg)->Wait( &m_bStop, SYNCSOURCE_AUDIO ); CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_SYNCHRONIZE"); pMsg->Release(); /* we may be very much off correct pts here, but next picture may be a still*/ /* make sure it isn't dropped */ m_iNrOfPicturesNotToSkip = 5; continue; } else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC)) { CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg; if(pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE) pts = pMsgGeneralResync->m_timestamp; if(pMsgGeneralResync->m_clock) { double delay = m_FlipTimeStamp - m_pClock->GetAbsoluteClock(); if( delay > frametime ) delay = frametime; else if( delay < 0 ) delay = 0; m_pClock->Discontinuity(CLOCK_DISC_NORMAL, pts, delay); CLog::Log(LOGDEBUG, "CDVDPlayerVideo:: Resync - clock:%f, delay:%f", pts, delay); } pMsgGeneralResync->Release(); continue; } else if (pMsg->IsType(CDVDMsg::VIDEO_SET_ASPECT)) { CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::VIDEO_SET_ASPECT"); m_fForcedAspectRatio = *((CDVDMsgDouble*)pMsg); } else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (CDVDPlayerVideo::Flush()) { EnterCriticalSection(&m_critCodecSection); if(m_pVideoCodec) m_pVideoCodec->Reset(); LeaveCriticalSection(&m_critCodecSection); } else if (pMsg->IsType(CDVDMsg::VIDEO_NOSKIP)) { // libmpeg2 is also returning incomplete frames after a dvd cell change // so the first few pictures are not the correct ones to display in some cases // just display those together with the correct one. // (setting it to 2 will skip some menu stills, 5 is working ok for me). m_iNrOfPicturesNotToSkip = 5; } if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET)) { DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket(); bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop(); m_started = true; if (m_stalled) { CLog::Log(LOGINFO, "CDVDPlayerVideo - Stillframe left, switching to normal playback"); m_stalled = false; //don't allow the first frames after a still to be dropped //sometimes we get multiple stills (long duration frames) after each other //in normal mpegs m_iNrOfPicturesNotToSkip = 5; } else if( iDropped*frametime > DVD_MSEC_TO_TIME(100) ) { // if we dropped too many pictures in a row, insert a forced picture m_iNrOfPicturesNotToSkip++; } #ifdef PROFILE bRequestDrop = false; #else if (m_iNrOfPicturesNotToSkip > 0) bRequestDrop = false; if (m_speed < 0) bRequestDrop = false; if (m_bDropFrames == false) bRequestDrop = false; #endif // if player want's us to drop this packet, do so nomatter what if(bPacketDrop) bRequestDrop = true; EnterCriticalSection(&m_critCodecSection); // tell codec if next frame should be dropped // problem here, if one packet contains more than one frame // both frames will be dropped in that case instead of just the first // decoder still needs to provide an empty image structure, with correct flags m_pVideoCodec->SetDropState(bRequestDrop); int iDecoderState = m_pVideoCodec->Decode(pPacket->pData, pPacket->iSize, pPacket->pts); m_videoStats.AddSampleBytes(pPacket->iSize); // assume decoder dropped a picture if it didn't give us any // picture from a demux packet, this should be reasonable // for libavformat as a demuxer as it normally packetizes // pictures when they come from demuxer if(bRequestDrop && !bPacketDrop && (iDecoderState & VC_BUFFER)) { m_iDroppedFrames++; iDropped++; } // loop while no error while (!m_bStop && !(iDecoderState & VC_ERROR)) { // check for a new picture if (iDecoderState & VC_PICTURE) { // try to retrieve the picture (should never fail!), unless there is a demuxer bug ofcours memset(&picture, 0, sizeof(DVDVideoPicture)); if (m_pVideoCodec->GetPicture(&picture)) { picture.iGroupId = pPacket->iGroupId; if(picture.iDuration == 0) picture.iDuration = frametime; if(bPacketDrop) picture.iFlags |= DVP_FLAG_DROPPED; if (m_iNrOfPicturesNotToSkip > 0) { picture.iFlags |= DVP_FLAG_NOSKIP; m_iNrOfPicturesNotToSkip--; } /* try to figure out a pts for this frame */ if(picture.pts == DVD_NOPTS_VALUE && pPacket->dts != DVD_NOPTS_VALUE) picture.pts = pPacket->dts; /* use forced aspect if any */ if( m_fForcedAspectRatio != 0.0f ) picture.iDisplayWidth = (int) (picture.iDisplayHeight * m_fForcedAspectRatio); //Deinterlace if codec said format was interlaced or if we have selected we want to deinterlace //this video EINTERLACEMETHOD mInt = g_stSettings.m_currentVideoSettings.m_InterlaceMethod; if( mInt == VS_INTERLACEMETHOD_DEINTERLACE ) { mDeinterlace.Process(&picture); mDeinterlace.GetPicture(&picture); } else if( mInt == VS_INTERLACEMETHOD_RENDER_WEAVE || mInt == VS_INTERLACEMETHOD_RENDER_WEAVE_INVERTED ) { /* if we are syncing frames, dvdplayer will be forced to play at a given framerate */ /* unless we directly sync to the correct pts, we won't get a/v sync as video can never catch up */ picture.iFlags |= DVP_FLAG_NOAUTOSYNC; } /* if frame has a pts (usually originiating from demux packet), use that */ if(picture.pts != DVD_NOPTS_VALUE) pts = picture.pts; int iResult; do { try { iResult = OutputPicture(&picture, pts); } catch (...) { CLog::Log(LOGERROR, "%s - Exception caught when outputing picture", __FUNCTION__); iResult = EOS_ABORT; } if (iResult == EOS_ABORT) break; // guess next frame pts. iDuration is always valid pts += picture.iDuration * m_speed / abs(m_speed); } while (!m_bStop && picture.iRepeatPicture-- > 0); if( iResult & EOS_ABORT ) { //if we break here and we directly try to decode again wihout //flushing the video codec things break for some reason //i think the decoder (libmpeg2 atleast) still has a pointer //to the data, and when the packet is freed that will fail. iDecoderState = m_pVideoCodec->Decode(NULL, 0, DVD_NOPTS_VALUE); break; } if( (iResult & EOS_DROPPED) && !bPacketDrop ) { m_iDroppedFrames++; iDropped++; } else iDropped = 0; bRequestDrop = (iResult & EOS_VERYLATE) == EOS_VERYLATE; } else { CLog::Log(LOGWARNING, "Decoder Error getting videoPicture."); m_pVideoCodec->Reset(); } } /* if (iDecoderState & VC_USERDATA) { // found some userdata while decoding a frame // could be closed captioning DVDVideoUserData videoUserData; if (m_pVideoCodec->GetUserData(&videoUserData)) { ProcessVideoUserData(&videoUserData, pts); } } */ // if the decoder needs more data, we just break this loop // and try to get more data from the videoQueue if (iDecoderState & VC_BUFFER) break; // the decoder didn't need more data, flush the remaning buffer iDecoderState = m_pVideoCodec->Decode(NULL, 0, DVD_NOPTS_VALUE); } // if decoder had an error, tell it to reset to avoid more problems if( iDecoderState & VC_ERROR ) m_pVideoCodec->Reset(); LeaveCriticalSection(&m_critCodecSection); } // all data is used by the decoder, we can safely free it now pMsg->Release(); } }
void OMXPlayerVideo::Process() { double frametime = (double)DVD_TIME_BASE / m_fFrameRate; bool bRequestDrop = false; m_videoStats.Start(); while(!m_bStop) { CDVDMsg* pMsg; int iQueueTimeOut = (int)(m_stalled ? frametime / 4 : frametime * 10) / 1000; int iPriority = (m_speed == DVD_PLAYSPEED_PAUSE && m_started) ? 1 : 0; MsgQueueReturnCode ret = m_messageQueue.Get(&pMsg, iQueueTimeOut, iPriority); if (MSGQ_IS_ERROR(ret) || ret == MSGQ_ABORT) { CLog::Log(LOGERROR, "OMXPlayerVideo: Got MSGQ_IS_ERROR(%d) Aborting", (int)ret); break; } else if (ret == MSGQ_TIMEOUT) { continue; } if (pMsg->IsType(CDVDMsg::GENERAL_SYNCHRONIZE)) { if(((CDVDMsgGeneralSynchronize*)pMsg)->Wait(100, SYNCSOURCE_VIDEO)) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_SYNCHRONIZE"); } else m_messageQueue.Put(pMsg->Acquire(), 1); /* push back as prio message, to process other prio messages */ pMsg->Release(); continue; } else if (pMsg->IsType(CDVDMsg::GENERAL_RESYNC)) { CDVDMsgGeneralResync* pMsgGeneralResync = (CDVDMsgGeneralResync*)pMsg; double delay = 0; if(pMsgGeneralResync->m_clock && pMsgGeneralResync->m_timestamp != DVD_NOPTS_VALUE) { CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, %f, 1)", m_iCurrentPts, pMsgGeneralResync->m_timestamp); m_av_clock->Discontinuity(pMsgGeneralResync->m_timestamp - delay); } else CLog::Log(LOGDEBUG, "CDVDPlayerVideo - CDVDMsg::GENERAL_RESYNC(%f, 0)", m_iCurrentPts); m_nextOverlay = DVD_NOPTS_VALUE; m_iCurrentPts = DVD_NOPTS_VALUE; pMsgGeneralResync->Release(); continue; } else if (pMsg->IsType(CDVDMsg::GENERAL_DELAY)) { double timeout = static_cast<CDVDMsgDouble*>(pMsg)->m_value; CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_DELAY(%f)", timeout); } else if (pMsg->IsType(CDVDMsg::VIDEO_SET_ASPECT)) { m_fForcedAspectRatio = *((CDVDMsgDouble*)pMsg); CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::VIDEO_SET_ASPECT %.2f", m_fForcedAspectRatio); } else if (pMsg->IsType(CDVDMsg::GENERAL_RESET)) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_RESET"); m_omxVideo.Reset(); m_started = false; m_nextOverlay = DVD_NOPTS_VALUE; m_iCurrentPts = DVD_NOPTS_VALUE; } else if (pMsg->IsType(CDVDMsg::GENERAL_FLUSH)) // private message sent by (COMXPlayerVideo::Flush()) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_FLUSH"); m_stalled = true; m_started = false; m_nextOverlay = DVD_NOPTS_VALUE; m_iCurrentPts = DVD_NOPTS_VALUE; m_omxVideo.Reset(); m_flush = false; } else if (pMsg->IsType(CDVDMsg::PLAYER_SETSPEED)) { if (m_speed != static_cast<CDVDMsgInt*>(pMsg)->m_value) { m_speed = static_cast<CDVDMsgInt*>(pMsg)->m_value; CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::PLAYER_SETSPEED %d", m_speed); } } else if (pMsg->IsType(CDVDMsg::PLAYER_STARTED)) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::PLAYER_STARTED %d", m_started); if(m_started) m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO)); } else if (pMsg->IsType(CDVDMsg::PLAYER_DISPLAYTIME)) { COMXPlayer::SPlayerState& state = ((CDVDMsgType<COMXPlayer::SPlayerState>*)pMsg)->m_value; double pts = m_iCurrentPts; double stamp = m_av_clock->OMXMediaTime(); if(state.time_src == COMXPlayer::ETIMESOURCE_CLOCK) state.time = stamp == 0.0 ? state.time : DVD_TIME_TO_MSEC(stamp + state.time_offset); else state.time = stamp == 0.0 || pts == DVD_NOPTS_VALUE ? state.time : state.time + DVD_TIME_TO_MSEC(stamp - pts); state.timestamp = m_av_clock->GetAbsoluteClock(); state.player = DVDPLAYER_VIDEO; m_messageParent.Put(pMsg->Acquire()); } else if (pMsg->IsType(CDVDMsg::GENERAL_STREAMCHANGE)) { COMXMsgVideoCodecChange* msg(static_cast<COMXMsgVideoCodecChange*>(pMsg)); OpenStream(msg->m_hints, msg->m_codec); msg->m_codec = NULL; } else if (pMsg->IsType(CDVDMsg::GENERAL_EOF)) { CLog::Log(LOGDEBUG, "COMXPlayerVideo - CDVDMsg::GENERAL_EOF"); SubmitEOS(); } else if (pMsg->IsType(CDVDMsg::DEMUXER_PACKET)) { DemuxPacket* pPacket = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacket(); bool bPacketDrop = ((CDVDMsgDemuxerPacket*)pMsg)->GetPacketDrop(); #ifdef _DEBUG CLog::Log(LOGINFO, "Video: dts:%.0f pts:%.0f size:%d (s:%d f:%d d:%d l:%d) s:%d %d/%d late:%d\n", pPacket->dts, pPacket->pts, (int)pPacket->iSize, m_started, m_flush, bPacketDrop, m_stalled, m_speed, 0, 0, 0); #endif if (m_messageQueue.GetDataSize() == 0 || m_speed < 0) { bRequestDrop = false; } // if player want's us to drop this packet, do so nomatter what if(bPacketDrop) bRequestDrop = true; m_omxVideo.SetDropState(bRequestDrop); while (!m_bStop) { // discard if flushing as clocks may be stopped and we'll never submit it if (m_flush) break; if((int)m_omxVideo.GetFreeSpace() < pPacket->iSize) { Sleep(10); continue; } if (m_stalled) { CLog::Log(LOGINFO, "COMXPlayerVideo - Stillframe left, switching to normal playback"); m_stalled = false; } // some packed bitstream AVI files set almost all pts values to DVD_NOPTS_VALUE, but have a scattering of real pts values. // the valid pts values match the dts values. // if a stream has had more than 4 valid pts values in the last 16, the use UNKNOWN, otherwise use dts m_history_valid_pts = (m_history_valid_pts << 1) | (pPacket->pts != DVD_NOPTS_VALUE); double pts = pPacket->pts; if(count_bits(m_history_valid_pts & 0xffff) < 4) pts = pPacket->dts; if (pts != DVD_NOPTS_VALUE) pts += m_iVideoDelay; m_omxVideo.Decode(pPacket->pData, pPacket->iSize, pts); Output(pts, bRequestDrop); if(pts != DVD_NOPTS_VALUE) m_iCurrentPts = pts; if(m_started == false) { m_codecname = m_omxVideo.GetDecoderName(); m_started = true; m_messageParent.Put(new CDVDMsgInt(CDVDMsg::PLAYER_STARTED, DVDPLAYER_VIDEO)); } break; } bRequestDrop = false; m_videoStats.AddSampleBytes(pPacket->iSize); } pMsg->Release(); } }