double OMXPlayerVideo::NextOverlay(double pts) { double delta_start, delta_stop, min_delta = DVD_NOPTS_VALUE; CSingleLock lock(*m_pOverlayContainer); VecOverlays* pVecOverlays = m_pOverlayContainer->GetOverlays(); VecOverlaysIter it = pVecOverlays->begin(); //Find the minimum time before a subtitle is added or removed while (it != pVecOverlays->end()) { CDVDOverlay* pOverlay = *it++; if(!pOverlay->bForced && !m_bRenderSubs) continue; double pts2 = pOverlay->bForced ? pts : pts - m_iSubtitleDelay; delta_start = pOverlay->iPTSStartTime - pts2; delta_stop = pOverlay->iPTSStopTime - pts2; // when currently on screen, we periodically update to allow (limited rate) ASS animation if (delta_start <= 0.0 && delta_stop > 0.0 && (min_delta == DVD_NOPTS_VALUE || DVD_MSEC_TO_TIME(100) < min_delta)) min_delta = DVD_MSEC_TO_TIME(100); else if (delta_start > 0.0 && (min_delta == DVD_NOPTS_VALUE || delta_start < min_delta)) min_delta = delta_start; else if (delta_stop > 0.0 && (min_delta == DVD_NOPTS_VALUE || delta_stop < min_delta)) min_delta = delta_stop; } return min_delta == DVD_NOPTS_VALUE ? pts+DVD_MSEC_TO_TIME(500) : pts+min_delta; }
int CDVDOverlayCodecFFmpeg::Decode(DemuxPacket *pPacket) { if (!m_pCodecContext || !pPacket) return 1; int gotsub = 0, len = 0; FreeSubtitle(m_Subtitle); AVPacket avpkt; av_init_packet(&avpkt); avpkt.data = pPacket->pData; avpkt.size = pPacket->iSize; avpkt.pts = pPacket->pts == DVD_NOPTS_VALUE ? AV_NOPTS_VALUE : (int64_t)pPacket->pts; avpkt.dts = pPacket->dts == DVD_NOPTS_VALUE ? AV_NOPTS_VALUE : (int64_t)pPacket->dts; len = avcodec_decode_subtitle2(m_pCodecContext, &m_Subtitle, &gotsub, &avpkt); if (len < 0) { CLog::Log(LOGERROR, "%s - avcodec_decode_subtitle returned failure", __FUNCTION__); Flush(); return OC_ERROR; } if (len != avpkt.size) CLog::Log(LOGWARNING, "%s - avcodec_decode_subtitle didn't consume the full packet", __FUNCTION__); if (!gotsub) return OC_BUFFER; double pts_offset = 0.0; if (m_pCodecContext->codec_id == AV_CODEC_ID_HDMV_PGS_SUBTITLE && m_Subtitle.format == 0) { // for pgs subtitles the packet pts of the end_segments are wrong // instead use the subtitle pts to calc the offset here // see http://git.videolan.org/?p=ffmpeg.git;a=commit;h=2939e258f9d1fff89b3b68536beb931b54611585 if (m_Subtitle.pts != AV_NOPTS_VALUE && pPacket->pts != DVD_NOPTS_VALUE) { pts_offset = m_Subtitle.pts - pPacket->pts ; } } m_StartTime = DVD_MSEC_TO_TIME(m_Subtitle.start_display_time); m_StopTime = DVD_MSEC_TO_TIME(m_Subtitle.end_display_time); //adapt start and stop time to our packet pts bool dummy = false; CDVDOverlayCodec::GetAbsoluteTimes(m_StartTime, m_StopTime, pPacket, dummy, pts_offset); m_SubtitleIndex = 0; return OC_OVERLAY; }
bool CDVDClock::Update(double clock, double absolute, double limit, const char* log) { CExclusiveLock lock(m_critSection); double was_absolute = SystemToAbsolute(m_startClock); double was_clock = m_iDisc + absolute - was_absolute; lock.Leave(); double error = std::abs(clock - was_clock); // skip minor updates while speed adjust is active // -> adjusting buffer levels if (m_speedAdjust != 0 && error < DVD_MSEC_TO_TIME(100)) { return false; } else if (error > limit) { Discontinuity(clock, absolute); CLog::Log(LOGDEBUG, "CDVDClock::Discontinuity - %s - was:%f, should be:%f, error:%f" , log , was_clock , clock , clock - was_clock); return true; } else return false; }
double OMXReader::NormalizeFrameduration(double frameduration) { //if the duration is within 20 microseconds of a common duration, use that const double durations[] = {DVD_TIME_BASE * 1.001 / 24.0, DVD_TIME_BASE / 24.0, DVD_TIME_BASE / 25.0, DVD_TIME_BASE * 1.001 / 30.0, DVD_TIME_BASE / 30.0, DVD_TIME_BASE / 50.0, DVD_TIME_BASE * 1.001 / 60.0, DVD_TIME_BASE / 60.0 }; double lowestdiff = DVD_TIME_BASE; int selected = -1; for (size_t i = 0; i < sizeof(durations) / sizeof(durations[0]); i++) { double diff = fabs(frameduration - durations[i]); if (diff < DVD_MSEC_TO_TIME(0.02) && diff < lowestdiff) { selected = i; lowestdiff = diff; } } if (selected != -1) { return durations[selected]; } else { return frameduration; } }
void OMXPlayerVideo::Output(double pts, bool bDropPacket) { if (!g_renderManager.IsStarted()) { CLog::Log(LOGERROR, "%s - renderer not started", __FUNCTION__); return; } if (CThread::m_bStop) return; // we aim to submit subtitles 100ms early const double preroll = DVD_MSEC_TO_TIME(100); double media_pts = m_av_clock->OMXMediaTime(); if (m_nextOverlay != DVD_NOPTS_VALUE && media_pts + preroll <= m_nextOverlay) return; int buffer = g_renderManager.WaitForBuffer(CThread::m_bStop); if (buffer < 0) return; double subtitle_pts = m_nextOverlay; double time = subtitle_pts != DVD_NOPTS_VALUE ? subtitle_pts - media_pts : 0.0; if (m_nextOverlay != DVD_NOPTS_VALUE) media_pts = m_nextOverlay; m_nextOverlay = NextOverlay(media_pts); ProcessOverlays(media_pts); time += m_av_clock->GetAbsoluteClock(); g_renderManager.FlipPage(CThread::m_bStop, time/DVD_TIME_BASE); }
double CDVDCodecUtils::NormalizeFrameduration(double frameduration, bool *match) { //if the duration is within 20 microseconds of a common duration, use that const double durations[] = {DVD_TIME_BASE * 1.001 / 24.0, DVD_TIME_BASE / 24.0, DVD_TIME_BASE / 25.0, DVD_TIME_BASE * 1.001 / 30.0, DVD_TIME_BASE / 30.0, DVD_TIME_BASE / 50.0, DVD_TIME_BASE * 1.001 / 60.0, DVD_TIME_BASE / 60.0}; double lowestdiff = DVD_TIME_BASE; int selected = -1; for (size_t i = 0; i < ARRAY_SIZE(durations); i++) { double diff = fabs(frameduration - durations[i]); if (diff < DVD_MSEC_TO_TIME(0.02) && diff < lowestdiff) { selected = i; lowestdiff = diff; } } if (selected != -1) { if (match) *match = true; return durations[selected]; } else { if (match) *match = false; return frameduration; } }
double CDVDClock::ErrorAdjust(double error, const char* log) { CSingleLock lock(m_critSection); double clock, absolute, adjustment; clock = GetClock(absolute); // skip minor updates while speed adjust is active // -> adjusting buffer levels if (m_speedAdjust != 0 && error < DVD_MSEC_TO_TIME(100)) { return 0; } adjustment = error; if (m_vSyncAdjust != 0) { if (error > 0.5 * m_frameTime) adjustment = m_frameTime; else if (error < -0.5 * m_frameTime) adjustment = -m_frameTime; else adjustment = 0; } if (adjustment == 0) return 0; Discontinuity(clock+adjustment, absolute); CLog::Log(LOGDEBUG, "CDVDClock::ErrorAdjust - %s - error:%f, adjusted:%f", log, error, adjustment); return adjustment; }
bool OMXReader::SeekTime(int time, bool backwords, double *startpts, bool doLoopOnFail) { if(time < 0) time = 0; if(!avFormatContext) return false; if(fileObject && !fileObject->IoControl(IOCTRL_SEEK_POSSIBLE, NULL)) { ofLog(OF_LOG_VERBOSE, "%s - input stream reports it is not seekable", __FUNCTION__); return false; } lock(); //FlushRead(); if(avioContext) { avioContext->buf_ptr = avioContext->buf_end; } int64_t seek_pts = (int64_t)time * (AV_TIME_BASE / 1000); if (avFormatContext->start_time != (int64_t)AV_NOPTS_VALUE) { seek_pts += avFormatContext->start_time; }; int ret = av_seek_frame(avFormatContext, -1, seek_pts, backwords ? AVSEEK_FLAG_BACKWARD : 0); if(ret >= 0) { updateCurrentPTS(); }else { //ofLogVerbose(__func__) << "av_seek_frame returned >= 0, no updateCurrentPTS" << ret; fileObject->rewindFile(); } // in this case the start time is requested time if(startpts) { *startpts = DVD_MSEC_TO_TIME(time); } isEOF = false; if (fileObject && fileObject->getIsEOF() && ret <= 0) { isEOF = true; ret = 0; } unlock(); return (ret >= 0); }
bool OMXReader::SeekTime(int time, bool backwords, double *startpts) { if(time < 0) time = 0; if(!m_pFormatContext) return false; if(m_pFile && !m_pFile->IoControl(IOCTRL_SEEK_POSSIBLE, NULL)) { CLog::Log(LOGDEBUG, "%s - input stream reports it is not seekable", __FUNCTION__); return false; } Lock(); //FlushRead(); if(m_ioContext) m_ioContext->buf_ptr = m_ioContext->buf_end; int64_t seek_pts = (int64_t)time * (AV_TIME_BASE / 1000); if (m_pFormatContext->start_time != (int64_t)AV_NOPTS_VALUE) seek_pts += m_pFormatContext->start_time; int ret = m_dllAvFormat.av_seek_frame(m_pFormatContext, -1, seek_pts, backwords ? AVSEEK_FLAG_BACKWARD : 0); if(ret >= 0) { UpdateCurrentPTS(); } else { //m_pFile->rewindFile(); m_pFile->Seek(0); UpdateCurrentPTS(); wasFileRewound = true; } // in this case the start time is requested time if(startpts) *startpts = DVD_MSEC_TO_TIME(time); // demuxer will return failure, if you seek to eof m_eof = false; if (m_pFile && m_pFile->IsEOF() && ret <= 0) { m_eof = true; ret = 0; } CLog::Log(LOGDEBUG, "OMXReader::SeekTime(%d) - seek ended up on time %d",time,(int)(m_iCurrentPts / DVD_TIME_BASE * 1000)); UnLock(); return (ret >= 0); }
bool OMXReader::SeekTime(int64_t seek_ms, int seek_flags, double *startpts) { if(seek_ms < 0) seek_ms = 0; if(!m_pFile || !m_pFormatContext) return false; if(!m_pFile->IoControl(IOCTRL_SEEK_POSSIBLE, NULL)) return false; Lock(); //FlushRead(); if(m_ioContext) m_ioContext->buf_ptr = m_ioContext->buf_end; int64_t seek_pts = (int64_t)seek_ms * (AV_TIME_BASE / 1000); if (m_pFormatContext->start_time != (int64_t)AV_NOPTS_VALUE) seek_pts += m_pFormatContext->start_time; /* seek behind eof */ if((seek_pts / AV_TIME_BASE) > (GetStreamLength() / 1000)) { m_eof = true; UnLock(); return true; } int ret = m_dllAvFormat.av_seek_frame(m_pFormatContext, -1, seek_pts, seek_flags ? AVSEEK_FLAG_BACKWARD : 0); if(ret >= 0) { UpdateCurrentPTS(); m_eof = false; } if(m_iCurrentPts == DVD_NOPTS_VALUE) { CLog::Log(LOGDEBUG, "OMXReader::SeekTime - unknown position after seek"); } else { CLog::Log(LOGDEBUG, "OMXReader::SeekTime - seek ended up on time %d",(int)(m_iCurrentPts / DVD_TIME_BASE * 1000)); } if(startpts) *startpts = DVD_MSEC_TO_TIME(seek_ms); UnLock(); return (ret >= 0); }
bool CDVDDemuxFFmpeg::SeekTime(int time, bool backwords, double *startpts) { if(time < 0) time = 0; if (m_pInput->IsStreamType(DVDSTREAM_TYPE_DVD)) { CLog::Log(LOGDEBUG, "%s - seeking using navigator", __FUNCTION__); if (((CDVDInputStreamNavigator*)m_pInput)->SeekTime(time)) { // since seek happens behind demuxers back, we have to reset it // this won't be a problem if we setup ffmpeg properly later Reset(); // todo, calculate starting pts in this case return true; } return false; } if(!m_pInput->Seek(0, SEEK_POSSIBLE) && !m_pInput->IsStreamType(DVDSTREAM_TYPE_FFMPEG)) { CLog::Log(LOGDEBUG, "%s - input stream reports it is not seekable", __FUNCTION__); return false; } __int64 seek_pts = (__int64)time * (AV_TIME_BASE / 1000); if (m_pFormatContext->start_time != (int64_t)AV_NOPTS_VALUE) seek_pts += m_pFormatContext->start_time; Lock(); int ret = m_dllAvFormat.av_seek_frame(m_pFormatContext, -1, seek_pts, backwords ? AVSEEK_FLAG_BACKWARD : 0); if(ret >= 0) UpdateCurrentPTS(); Unlock(); if(m_iCurrentPts == DVD_NOPTS_VALUE) CLog::Log(LOGDEBUG, "%s - unknown position after seek", __FUNCTION__); else CLog::Log(LOGDEBUG, "%s - seek ended up on time %d", __FUNCTION__, (int)(m_iCurrentPts / DVD_TIME_BASE * 1000)); // in this case the start time is requested time if(startpts) *startpts = DVD_MSEC_TO_TIME(time); // demuxer will return failure, if you seek to eof if (m_pInput->IsEOF() && ret <= 0) return true; return (ret >= 0); }
bool CDVDDemuxVobsub::SeekTime(int time, bool backwords, double* startpts) { double pts = DVD_MSEC_TO_TIME(time); m_Timestamp = m_Timestamps.begin(); for (;m_Timestamp != m_Timestamps.end();++m_Timestamp) { if(m_Timestamp->pts > pts) break; } for (unsigned i=0;i<m_Streams.size() && m_Timestamps.begin() != m_Timestamp;i++) { --m_Timestamp; } return true; }
bool CDVDPlayerAudio::OutputPacket(DVDAudioFrame &audioframe) { double syncerror = m_dvdAudio.GetSyncError(); if (m_synctype == SYNC_DISCON && fabs(syncerror) > DVD_MSEC_TO_TIME(10)) { double correction = m_pClock->ErrorAdjust(syncerror, "CDVDPlayerAudio::OutputPacket"); if (correction != 0) { m_dvdAudio.SetSyncErrorCorrection(-correction); } } m_dvdAudio.AddPackets(audioframe); return true; }
double OMXClock::OMXMediaTime(bool lock /* = true */) { double pts = 0.0; if(m_omx_clock.GetComponent() == NULL) return 0; double now = GetAbsoluteClock(); if (now - m_last_media_time_read > DVD_MSEC_TO_TIME(100) || m_last_media_time == 0.0) { if(lock) Lock(); OMX_ERRORTYPE omx_err = OMX_ErrorNone; OMX_TIME_CONFIG_TIMESTAMPTYPE timeStamp; OMX_INIT_STRUCTURE(timeStamp); timeStamp.nPortIndex = m_omx_clock.GetInputPort(); omx_err = m_omx_clock.GetConfig(OMX_IndexConfigTimeCurrentMediaTime, &timeStamp); if(omx_err != OMX_ErrorNone) { LOG_ERROR << "OMXClock::MediaTime error getting OMX_IndexConfigTimeCurrentMediaTime"; if(lock) UnLock(); return 0; } pts = FromOMXTime(timeStamp.nTimestamp); //CLog::Log(LOGINFO, "OMXClock::MediaTime %.2f (%.2f, %.2f)", pts, m_last_media_time, now - m_last_media_time_read); m_last_media_time = pts; m_last_media_time_read = now; if(lock) UnLock(); } else { double speed = m_pause ? 0.0 : (double)m_omx_speed / DVD_PLAYSPEED_NORMAL; pts = m_last_media_time + (now - m_last_media_time_read) * speed; //CLog::Log(LOGINFO, "OMXClock::MediaTime cached %.2f (%.2f, %.2f)", pts, m_last_media_time, now - m_last_media_time_read); } return pts; }
bool CVideoPlayerAudio::OutputPacket(DVDAudioFrame &audioframe) { double syncerror = m_dvdAudio.GetSyncError(); if (m_synctype == SYNC_DISCON) { double limit, error; limit = DVD_MSEC_TO_TIME(10); error = syncerror; double absolute; double clock = m_pClock->GetClock(absolute); if (m_pClock->Update(clock + error, absolute, limit - 0.001, "CVideoPlayerAudio::OutputPacket")) { m_dvdAudio.SetSyncErrorCorrection(-error); } } m_dvdAudio.AddPackets(audioframe); return true; }
double CDVDClock::ErrorAdjust(double error, const char* log) { CSingleLock lock(m_critSection); double clock, absolute, adjustment; clock = GetClock(absolute); // skip minor updates while speed adjust is active // -> adjusting buffer levels if (m_speedAdjust != 0 && error < DVD_MSEC_TO_TIME(100)) { return 0; } adjustment = error; if (m_vSyncAdjust != 0) { // Audio ahead is more noticeable then audio behind video. // Correct if aufio is more than 20ms ahead or more then // 27ms behind. In a worst case scenario we switch from // 20ms ahead to 21ms behind (for fps of 23.976) if (error > 0.02 * DVD_TIME_BASE) adjustment = m_frameTime; else if (error < -0.027 * DVD_TIME_BASE) adjustment = -m_frameTime; else adjustment = 0; } if (adjustment == 0) return 0; Discontinuity(clock+adjustment, absolute); CLog::Log(LOGDEBUG, "CDVDClock::ErrorAdjust - %s - error:%f, adjusted:%f", log, error, adjustment); return adjustment; }
void OMXPlayerVideo::Output(int iGroupId, double pts, bool bDropPacket) { if (!g_renderManager.IsStarted()) { CLog::Log(LOGERROR, "%s - renderer not started", __FUNCTION__); return; } // calculate the time we need to delay this picture before displaying double iSleepTime, iClockSleep, iFrameSleep, iPlayingClock, iCurrentClock, iFrameDuration; iPlayingClock = m_av_clock->GetClock(iCurrentClock, false); // snapshot current clock iClockSleep = pts - iPlayingClock; //sleep calculated by pts to clock comparison iFrameSleep = m_FlipTimeStamp - iCurrentClock; // sleep calculated by duration of frame iFrameDuration = (double)DVD_TIME_BASE / m_fFrameRate; //pPacket->duration; // correct sleep times based on speed if(m_speed) { iClockSleep = iClockSleep * DVD_PLAYSPEED_NORMAL / m_speed; iFrameSleep = iFrameSleep * DVD_PLAYSPEED_NORMAL / abs(m_speed); iFrameDuration = iFrameDuration * DVD_PLAYSPEED_NORMAL / abs(m_speed); } else { iClockSleep = 0; iFrameSleep = 0; } // dropping to a very low framerate is not correct (it should not happen at all) iClockSleep = min(iClockSleep, DVD_MSEC_TO_TIME(500)); iFrameSleep = min(iFrameSleep, DVD_MSEC_TO_TIME(500)); if( m_stalled ) iSleepTime = iFrameSleep; else iSleepTime = iFrameSleep + (iClockSleep - iFrameSleep) / m_autosync; // present the current pts of this frame to user, and include the actual // presentation delay, to allow him to adjust for it if( m_stalled ) m_iCurrentPts = DVD_NOPTS_VALUE; else m_iCurrentPts = pts - max(0.0, iSleepTime); // timestamp when we think next picture should be displayed based on current duration m_FlipTimeStamp = iCurrentClock; m_FlipTimeStamp += max(0.0, iSleepTime); m_FlipTimeStamp += iFrameDuration; if( m_speed < 0 ) { if( iClockSleep < -DVD_MSEC_TO_TIME(200)) return; } if(bDropPacket) return; #if 0 if( m_speed != DVD_PLAYSPEED_NORMAL) { // calculate frame dropping pattern to render at this speed // we do that by deciding if this or next frame is closest // to the flip timestamp double current = fabs(m_dropbase - m_droptime); double next = fabs(m_dropbase - (m_droptime + iFrameDuration)); double frametime = (double)DVD_TIME_BASE / m_fFrameRate; m_droptime += iFrameDuration; #ifndef PROFILE if( next < current /*&& !(pPicture->iFlags & DVP_FLAG_NOSKIP) */) return /*result | EOS_DROPPED*/; #endif while(!m_bStop && m_dropbase < m_droptime) m_dropbase += frametime; while(!m_bStop && m_dropbase - frametime > m_droptime) m_dropbase -= frametime; } else { m_droptime = 0.0f; m_dropbase = 0.0f; } #else m_droptime = 0.0f; m_dropbase = 0.0f; #endif // DVDPlayer sleeps until m_iSleepEndTime here before calling FlipPage. // Video playback in asynchronous in OMXPlayer, so we don't want to do that here, as it prevents the video fifo from being kept full. // So, we keep track of when FlipPage would have been called on DVDPlayer and return early if it is not time. // m_iSleepEndTime == DVD_NOPTS_VALUE means we are not waiting to call FlipPage, otherwise it is the time we want to call FlipPage if (m_iSleepEndTime == DVD_NOPTS_VALUE) { m_iSleepEndTime = iCurrentClock + iSleepTime; } if (!CThread::m_bStop && m_av_clock->GetAbsoluteClock(false) < m_iSleepEndTime + DVD_MSEC_TO_TIME(500)) return; double pts_media = m_av_clock->OMXMediaTime(false, false); ProcessOverlays(iGroupId, pts_media); g_renderManager.FlipPage(CThread::m_bStop, m_iSleepEndTime / DVD_TIME_BASE, -1, FS_NONE); m_iSleepEndTime = DVD_NOPTS_VALUE; //m_av_clock->WaitAbsoluteClock((iCurrentClock + iSleepTime)); }
CDVDOverlay* CDVDOverlayCodecFFmpeg::GetOverlay() { if(m_SubtitleIndex<0) return NULL; if(m_Subtitle.num_rects == 0 && m_SubtitleIndex == 0) { // we must add an empty overlay to replace the previous one CDVDOverlay* o = new CDVDOverlay(DVDOVERLAY_TYPE_NONE); o->iPTSStartTime = 0; o->iPTSStopTime = 0; o->replace = true; m_SubtitleIndex++; return o; } if(m_Subtitle.format == 0) { if(m_SubtitleIndex >= (int)m_Subtitle.num_rects) return NULL; #if LIBAVCODEC_VERSION_INT >= (52<<10) if(m_Subtitle.rects[m_SubtitleIndex] == NULL) return NULL; AVSubtitleRect& rect = *m_Subtitle.rects[m_SubtitleIndex]; #else AVSubtitleRect& rect = m_Subtitle.rects[m_SubtitleIndex]; #endif CDVDOverlayImage* overlay = new CDVDOverlayImage(); overlay->iPTSStartTime = DVD_MSEC_TO_TIME(m_Subtitle.start_display_time); overlay->iPTSStopTime = DVD_MSEC_TO_TIME(m_Subtitle.end_display_time); overlay->replace = true; overlay->linesize = rect.w; overlay->data = (BYTE*)malloc(rect.w * rect.h); overlay->palette = (uint32_t*)malloc(rect.nb_colors*4); overlay->palette_colors = rect.nb_colors; overlay->x = rect.x; overlay->y = rect.y; overlay->width = rect.w; overlay->height = rect.h; int right = overlay->x + overlay->width; int bottom = overlay->y + overlay->height; if(m_height == 0 && m_pCodecContext->height) m_height = m_pCodecContext->height; if(m_width == 0 && m_pCodecContext->width) m_width = m_pCodecContext->width; if(bottom > m_height) { if (bottom <= 480) m_height = 480; else if(bottom <= 576) m_height = 576; else if(bottom <= 720) m_height = 720; else if(bottom <= 1080) m_height = 1080; else m_height = bottom; } if(right > m_width) { if (right <= 720) m_width = 720; else if(right <= 1024) m_width = 1024; else if(right <= 1280) m_width = 1280; else if(right <= 1920) m_width = 1920; else m_width = right; } overlay->source_width = m_width; overlay->source_height = m_height; #if LIBAVCODEC_VERSION_INT >= (52<<10) BYTE* s = rect.pict.data[0]; BYTE* t = overlay->data; for(int i=0;i<rect.h;i++) { memcpy(t, s, rect.w); s += rect.pict.linesize[0]; t += overlay->linesize; } for(int i=0;i<rect.nb_colors;i++) overlay->palette[i] = Endian_SwapLE32(((uint32_t *)rect.pict.data[1])[i]); m_dllAvUtil.av_free(rect.pict.data[0]); m_dllAvUtil.av_free(rect.pict.data[1]); m_dllAvUtil.av_freep(&m_Subtitle.rects[m_SubtitleIndex]); #else BYTE* s = rect.bitmap; BYTE* t = overlay->data; for(int i=0;i<rect.h;i++) { memcpy(t, s, rect.w); s += rect.linesize; t += overlay->linesize; } memcpy(overlay->palette, rect.rgba_palette, rect.nb_colors*4); m_dllAvUtil.av_freep(&rect.bitmap); m_dllAvUtil.av_freep(&rect.rgba_palette); #endif m_SubtitleIndex++; return overlay; } return NULL; }
bool OMXDoProcessing(struct SOmxPlayerState &m_OmxPlayerState, int m_playSpeed, IDVDStreamPlayerVideo *m_dvdPlayerVideo, IDVDStreamPlayerAudio *m_dvdPlayerAudio, CCurrentStream m_CurrentAudio, CCurrentStream m_CurrentVideo, bool m_HasVideo, bool m_HasAudio) { bool reopen_stream = false; double now = CDVDClock::GetAbsoluteClock(); if (m_OmxPlayerState.last_check_time == 0.0 || m_OmxPlayerState.last_check_time + DVD_MSEC_TO_TIME(20) <= now) { m_OmxPlayerState.last_check_time = now; m_OmxPlayerState.stamp = m_OmxPlayerState.av_clock.OMXMediaTime(); const bool m_Pause = m_playSpeed == DVD_PLAYSPEED_PAUSE; const bool not_accepts_data = (!m_dvdPlayerAudio->AcceptsData() && m_HasAudio) || (!m_dvdPlayerVideo->AcceptsData() && m_HasVideo >= 0); /* when the video/audio fifos are low, we pause clock, when high we resume */ double audio_pts = floor(m_dvdPlayerAudio->GetCurrentPts()); double video_pts = floor(m_dvdPlayerVideo->GetCurrentPts()); float audio_fifo = audio_pts / DVD_TIME_BASE - m_OmxPlayerState.stamp * 1e-6; float video_fifo = video_pts / DVD_TIME_BASE - m_OmxPlayerState.stamp * 1e-6; float threshold = 0.1f; bool audio_fifo_low = false, video_fifo_low = false, audio_fifo_high = false, video_fifo_high = false; if (m_OmxPlayerState.interlace_method == VS_INTERLACEMETHOD_MAX) m_OmxPlayerState.interlace_method = g_renderManager.AutoInterlaceMethod(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod); // if deinterlace setting has changed, we should close and open video if (m_OmxPlayerState.current_deinterlace != CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode || (m_OmxPlayerState.current_deinterlace != VS_DEINTERLACEMODE_OFF && m_OmxPlayerState.interlace_method != g_renderManager.AutoInterlaceMethod(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod))) { CLog::Log(LOGNOTICE, "%s - Reopen stream due to interlace change (%d,%d,%d,%d)", __FUNCTION__, m_OmxPlayerState.current_deinterlace, CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode, m_OmxPlayerState.interlace_method, g_renderManager.AutoInterlaceMethod(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod)); m_OmxPlayerState.current_deinterlace = CMediaSettings::Get().GetCurrentVideoSettings().m_DeinterlaceMode; m_OmxPlayerState.interlace_method = g_renderManager.AutoInterlaceMethod(CMediaSettings::Get().GetCurrentVideoSettings().m_InterlaceMethod); reopen_stream = true; } m_OmxPlayerState.video_fifo = (int)(100.0*(m_dvdPlayerVideo->GetDecoderBufferSize()-m_dvdPlayerVideo->GetDecoderFreeSpace())/m_dvdPlayerVideo->GetDecoderBufferSize()); m_OmxPlayerState.audio_fifo = (int)(100.0*audio_fifo/m_dvdPlayerAudio->GetCacheTotal()); #ifdef _DEBUG static unsigned count; if ((count++ & 7) == 0) { char response[80]; if (m_dvdPlayerVideo->GetDecoderBufferSize() && m_dvdPlayerAudio->GetCacheTotal()) vc_gencmd(response, sizeof response, "render_bar 4 video_fifo %d %d %d %d", m_OmxPlayerState.video_fifo, (int)(100.0*video_fifo/m_dvdPlayerAudio->GetCacheTotal()), 0, 100); if (m_dvdPlayerAudio->GetCacheTotal()) vc_gencmd(response, sizeof response, "render_bar 5 audio_fifo %d %d %d %d", m_OmxPlayerState.audio_fifo, (int)(100.0*m_dvdPlayerAudio->GetDelay()/m_dvdPlayerAudio->GetCacheTotal()), 0, 100); vc_gencmd(response, sizeof response, "render_bar 6 video_queue %d %d %d %d", m_dvdPlayerVideo->GetLevel(), 0, 0, 100); vc_gencmd(response, sizeof response, "render_bar 7 audio_queue %d %d %d %d", m_dvdPlayerAudio->GetLevel(), 0, 0, 100); } #endif if (audio_pts != DVD_NOPTS_VALUE) { audio_fifo_low = m_HasAudio && audio_fifo < threshold; audio_fifo_high = audio_pts != DVD_NOPTS_VALUE && audio_fifo >= m_OmxPlayerState.threshold; } if (video_pts != DVD_NOPTS_VALUE) { video_fifo_low = m_HasVideo && video_fifo < threshold; video_fifo_high = video_pts != DVD_NOPTS_VALUE && video_fifo >= m_OmxPlayerState.threshold; } if (!m_HasAudio && m_HasVideo) audio_fifo_high = true; if (!m_HasVideo && m_HasAudio) video_fifo_high = true; #ifdef _DEBUG CLog::Log(LOGDEBUG, "%s::%s M:%.6f-%.6f (A:%.6f V:%.6f) PEF:%d%d%d S:%.2f A:%.2f V:%.2f/T:%.2f (A:%d%d V:%d%d) A:%d%% V:%d%% (%.2f,%.2f)", "CDVDPlayer", __FUNCTION__, m_OmxPlayerState.stamp*1e-6, m_OmxPlayerState.av_clock.OMXClockAdjustment()*1e-6, audio_pts*1e-6, video_pts*1e-6, m_OmxPlayerState.av_clock.OMXIsPaused(), m_OmxPlayerState.bOmxSentEOFs, not_accepts_data, m_playSpeed * (1.0f/DVD_PLAYSPEED_NORMAL), audio_pts == DVD_NOPTS_VALUE ? 0.0:audio_fifo, video_pts == DVD_NOPTS_VALUE ? 0.0:video_fifo, m_OmxPlayerState.threshold, audio_fifo_low, audio_fifo_high, video_fifo_low, video_fifo_high, m_dvdPlayerAudio->GetLevel(), m_dvdPlayerVideo->GetLevel(), m_dvdPlayerAudio->GetDelay(), (float)m_dvdPlayerAudio->GetCacheTotal()); #endif if(!m_Pause && (m_OmxPlayerState.bOmxSentEOFs || not_accepts_data || (audio_fifo_high && video_fifo_high) || m_playSpeed != DVD_PLAYSPEED_NORMAL)) { if (m_OmxPlayerState.av_clock.OMXIsPaused()) { CLog::Log(LOGDEBUG, "%s::%s Resume %.2f,%.2f (A:%d%d V:%d%d) EOF:%d FULL:%d T:%.2f", "CDVDPlayer", __FUNCTION__, audio_fifo, video_fifo, audio_fifo_low, audio_fifo_high, video_fifo_low, video_fifo_high, m_OmxPlayerState.bOmxSentEOFs, not_accepts_data, m_OmxPlayerState.threshold); m_OmxPlayerState.av_clock.OMXResume(); } } else if ((m_Pause || audio_fifo_low || video_fifo_low) && m_playSpeed == DVD_PLAYSPEED_NORMAL) { if (!m_OmxPlayerState.av_clock.OMXIsPaused()) { if (!m_Pause) m_OmxPlayerState.threshold = std::min(2.0f*m_OmxPlayerState.threshold, 16.0f); CLog::Log(LOGDEBUG, "%s::%s Pause %.2f,%.2f (A:%d%d V:%d%d) EOF:%d FULL:%d T:%.2f", "CDVDPlayer", __FUNCTION__, audio_fifo, video_fifo, audio_fifo_low, audio_fifo_high, video_fifo_low, video_fifo_high, m_OmxPlayerState.bOmxSentEOFs, not_accepts_data, m_OmxPlayerState.threshold); m_OmxPlayerState.av_clock.OMXPause(); } } } return reopen_stream; }
void CDVDPlayerAudio::HandleSyncError(double duration) { 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,"CDVDPlayerAudio::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,"CDVDPlayerAudio::HandleSyncError - average error %f below threshold of %f", m_error, threshold2); } if (m_synctype == SYNC_DISCON) { double limit, error; if (g_VideoReferenceClock.GetRefreshRate(&limit) > 0) { //when the videoreferenceclock is running, the discontinuity limit is one vblank period limit *= DVD_TIME_BASE; //make error a multiple of limit, rounded towards zero, //so it won't interfere with the sync methods in CXBMCRenderManager::WaitPresentTime if (m_error > 0.0) error = limit * floor(m_error / limit); else error = limit * ceil(m_error / limit); } else { limit = DVD_MSEC_TO_TIME(10); error = m_error; } m_pClock->Update(clock+error, absolute, limit - 0.001, "CDVDPlayerAudio::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; } }
void OMXPlayerVideo::Output(double pts) { if(m_syncclock) { double delay = m_FlipTimeStamp - m_av_clock->GetAbsoluteClock(); if( delay > m_frametime ) delay = m_frametime; else if( delay < 0 ) delay = 0; //printf("OMXPlayerVideo - GENERAL_RESYNC(%f, 1) delay %f\n", pts, m_FlipTimeStamp); m_av_clock->Discontinuity(pts - delay); m_syncclock = false; } double iSleepTime, iClockSleep, iFrameSleep, iPlayingClock, iCurrentClock, iFrameDuration; iPlayingClock = m_av_clock->GetClock(iCurrentClock, false); // snapshot current clock iClockSleep = pts - iPlayingClock; //sleep calculated by pts to clock comparison iFrameSleep = m_FlipTimeStamp - iCurrentClock; // sleep calculated by duration of frame iFrameDuration = m_frametime; // correct sleep times based on speed if(m_speed) { iClockSleep = iClockSleep * DVD_PLAYSPEED_NORMAL / m_speed; iFrameSleep = iFrameSleep * DVD_PLAYSPEED_NORMAL / abs(m_speed); iFrameDuration = iFrameDuration * DVD_PLAYSPEED_NORMAL / abs(m_speed); } else { iClockSleep = 0; iFrameSleep = 0; } // dropping to a very low framerate is not correct (it should not happen at all) iClockSleep = min(iClockSleep, DVD_MSEC_TO_TIME(500)); iFrameSleep = min(iFrameSleep, DVD_MSEC_TO_TIME(500)); bool m_stalled = false; int m_autosync = 1; if( m_stalled ) iSleepTime = iFrameSleep; else iSleepTime = iFrameSleep + (iClockSleep - iFrameSleep) / m_autosync; // present the current pts of this frame to user, and include the actual // presentation delay, to allow him to adjust for it if( m_stalled ) m_iCurrentPts = DVD_NOPTS_VALUE; else m_iCurrentPts = pts - max(0.0, iSleepTime); m_av_clock->SetPTS(m_iCurrentPts); // timestamp when we think next picture should be displayed based on current duration m_FlipTimeStamp = iCurrentClock; m_FlipTimeStamp += max(0.0, iSleepTime); m_FlipTimeStamp += iFrameDuration; while(m_av_clock->GetAbsoluteClock(false) < (iCurrentClock + iSleepTime + DVD_MSEC_TO_TIME(500)) ) { OMXClock::OMXSleep(10); } /* printf("iPlayingClock %f iCurrentClock %f iClockSleep %f iFrameSleep %f iFrameDuration %f WaitAbsolut %f m_FlipTimeStamp %f pts %f\n", iPlayingClock / DVD_TIME_BASE, iCurrentClock / DVD_TIME_BASE, iClockSleep / DVD_TIME_BASE, iFrameSleep / DVD_TIME_BASE, iFrameDuration / DVD_TIME_BASE, (iCurrentClock + iSleepTime) / DVD_TIME_BASE, m_FlipTimeStamp / DVD_TIME_BASE, pts / DVD_TIME_BASE); */ //g_renderManager.FlipPage(CThread::m_bStop, (iCurrentClock + iSleepTime) / DVD_TIME_BASE, -1, mDisplayField); m_av_clock->WaitAbsoluteClock((iCurrentClock + iSleepTime)); // guess next frame pts. iDuration is always valid if (m_speed != 0) m_pts += m_frametime * m_speed / abs(m_speed); }
bool CDVDPlayerAudio::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)) { if (AE_IS_RAW_RAW(audioframe.data_format)) { double correction = int(std::min(DVD_MSEC_TO_TIME(100), error) / audioframe.duration) * audioframe.duration; if (correction > 0) { // Force clock sync to audio CLog::Log(LOGNOTICE,"CDVDPlayerAudio::OutputPacket forcing clock sync for passthrough - dup error(%f), clock(%f), correction(%f)", error, clock, correction); m_pClock->Update(clock+correction, absolute, 0.0, "CDVDPlayerAudio::OutputPacket"); } m_dvdAudio.AddPackets(audioframe); } else { 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,"CDVDPlayerAudio::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)) { if (AE_IS_RAW_RAW(audioframe.data_format)) { double correction = audioframe.duration; // Force clock sync to audio CLog::Log(LOGNOTICE,"CDVDPlayerAudio::OutputPacket forcing clock sync for passthrough - skip error(%f), clock(%f), correction(%f)", error, clock, correction); m_pClock->Update(clock-correction, absolute, 0.0, "CDVDPlayerAudio::OutputPacket"); m_dvdAudio.AddPackets(audioframe); } else { m_dvdAudio.SetPlayingPts(audioframe.pts); CLog::Log(LOGNOTICE,"CDVDPlayerAudio::OutputPacket skipping a packets of duration %d", DVD_TIME_TO_MSEC(audioframe.duration)); } } else { m_dvdAudio.AddPackets(audioframe); } } else if (m_synctype == SYNC_DISCON) { m_dvdAudio.AddPackets(audioframe); } else if (m_synctype == SYNC_SKIPDUP) { double limit = std::max(DVD_MSEC_TO_TIME(10), audioframe.duration * 2.0 / 3.0); if (m_error < -limit) { m_prevskipped = !m_prevskipped; if (m_prevskipped) m_dvdAudio.AddPackets(audioframe); else { CLog::Log(LOGDEBUG, "CDVDPlayerAudio:: Dropping packet of %d ms", DVD_TIME_TO_MSEC(audioframe.duration)); m_error += audioframe.duration; } } else if(m_error > limit) { CLog::Log(LOGDEBUG, "CDVDPlayerAudio:: Duplicating packet of %d ms", DVD_TIME_TO_MSEC(audioframe.duration)); m_dvdAudio.AddPackets(audioframe); m_dvdAudio.AddPackets(audioframe); m_error -= audioframe.duration; } else m_dvdAudio.AddPackets(audioframe); } else if (m_synctype == SYNC_RESAMPLE) { m_dvdAudio.SetResampleRatio(m_resampleratio); m_dvdAudio.AddPackets(audioframe); } return true; }
bool OMXReader::SeekTime(int time, bool backwords, double *startpts, bool doLoopOnFail)//doLoopOnFail = true { if(time < 0) { time = 0; } if(!m_pFormatContext) { return false; } if(m_pFile && !m_pFile->IoControl(IOCTRL_SEEK_POSSIBLE, NULL)) { ofLog(OF_LOG_VERBOSE, "%s - input stream reports it is not seekable", __FUNCTION__); return false; } Lock(); //FlushRead(); if(m_ioContext) { m_ioContext->buf_ptr = m_ioContext->buf_end; } int64_t seek_pts = (int64_t)time * (AV_TIME_BASE / 1000); if (m_pFormatContext->start_time != (int64_t)AV_NOPTS_VALUE) { seek_pts += m_pFormatContext->start_time; } // virtual int av_seek_frame(AVFormatContext *s, int stream_index, int64_t timestamp, int flags) { return ::av_seek_frame(s, stream_index, timestamp, flags); } int ret = m_dllAvFormat.av_seek_frame(m_pFormatContext, -1, seek_pts, backwords ? AVSEEK_FLAG_BACKWARD : 0); //int ret =avformat_seek_file(m_pFormatContext, -1, 0, seek_pts, m_pFormatContext->duration*1000, AVSEEK_FLAG_ANY); if(ret >= 0) { ofLogVerbose(__func__) << "av_seek_frame PASS: - returned: " << ret; UpdateCurrentPTS(); } else { ofLogVerbose(__func__) << "av_seek_frame returned >= 0, - rewinding file" << ret; if (doLoopOnFail) { m_pFile->rewindFile(); wasFileRewound = true; } UpdateCurrentPTS(); } // in this case the start time is requested time if(startpts) { *startpts = DVD_MSEC_TO_TIME(time); } // demuxer will return failure, if you seek to eof m_eof = false; if (m_pFile && m_pFile->IsEOF() && ret <= 0) { m_eof = true; ret = 0; } //int landedTime = (int)(m_iCurrentPts / DVD_TIME_BASE * 1000); //ofLogVerbose(__func__) << "Seek ended up on time: " << landedTime; UnLock(); return (ret >= 0); }
/*------------------------------------------------------------------------------ | OMX_MediaProcessor::mediaDecoding +-----------------------------------------------------------------------------*/ void OMX_MediaProcessor::mediaDecoding() { // See description in the qmakefile. //#define ENABLE_PROFILE_MAIN_LOOP //#define ENABLE_PAUSE_FOR_BUFFERING LOG_VERBOSE(LOG_TAG, "Decoding thread started."); emit playbackStarted(); // Prealloc. #ifdef ENABLE_PAUSE_FOR_BUFFERING float stamp = 0; float audio_pts = 0; float video_pts = 0; float audio_fifo = 0; float video_fifo = 0; float threshold = 0; bool audio_fifo_low = false, video_fifo_low = false, audio_fifo_high = false, video_fifo_high = false; float m_threshold = 1.0f; //std::min(0.1f, audio_fifo_size * 0.1f); #endif // ENABLE_PAUSE_FOR_BUFFERING bool sentStarted = false; double last_seek_pos = 0; bool sendEos = false; double m_last_check_time = 0.0; m_av_clock->OMXReset(m_has_video, m_has_audio); m_av_clock->OMXStateExecute(); sentStarted = true; while (!m_pendingStop) { #ifdef ENABLE_PROFILE_MAIN_LOOP static qint64 tot = 0; static qint64 totNum = 0; static QElapsedTimer timer; static int count = 0; if (tot == 0) { timer.start(); tot++; } else tot += timer.restart(); totNum++; if ((count++)%30 == 0) { //LOG_VERBOSE(LOG_TAG, "Elapsed: %lld", timer.restart()); LOG_VERBOSE(LOG_TAG, "Average: %f.", (double)tot/totNum); } #endif double now = m_av_clock->GetAbsoluteClock(); bool update = false; if (m_last_check_time == 0.0 || m_last_check_time + DVD_MSEC_TO_TIME(20) <= now) { update = true; m_last_check_time = now; } // If a request is pending then consider done here. m_mutexPending.lock(); if (m_pendingPause) { m_waitPendingCommand.wakeAll(); m_pendingPause = false; } m_mutexPending.unlock(); // TODO: Use a semaphore instead. if (m_state == STATE_PAUSED) { OMXClock::OMXSleep(2); continue; } if (m_seekFlush || m_incr != 0) { double seek_pos = 0; double pts = m_av_clock->OMXMediaTime(); //seek_pos = (pts / DVD_TIME_BASE) + m_incr; seek_pos = (pts ? pts / DVD_TIME_BASE : last_seek_pos) + m_incr; last_seek_pos = seek_pos; seek_pos *= 1000.0; if(m_omx_reader->SeekTime((int)seek_pos, m_incr < 0.0f, &startpts)) { unsigned t = (unsigned)(startpts*1e-6); auto dur = m_omx_reader->GetStreamLength() / 1000; log_info("Seek to: %02d:%02d:%02d\n", (t/3600), (t/60)%60, t%60); flushStreams(startpts); } m_player_video->Close(); sentStarted = false; if (m_omx_reader->IsEof()) break; if (m_has_video && !m_player_video->Open( *m_hints_video, m_av_clock, m_textureData, VS_DEINTERLACEMODE_OFF, /* deinterlace */ OMX_ImageFilterAnaglyphNone, ENABLE_HDMI_CLOCK_SYNC, true, /* threaded */ 1.0, /* display aspect, unused */ 0, /* display */ 0, /* layer */ m_video_queue_size, m_video_fifo_size )) { m_incr = 0; break; } m_incr = 0; #ifdef ENABLE_PAUSE_FOR_BUFFERING m_av_clock->OMXPause(); #endif #ifdef ENABLE_SUBTITLES if (m_has_subtitle) m_player_subtitles.Resume(); #endif unsigned t = (unsigned)(startpts*1e-6); LOG_VERBOSE(LOG_TAG, "Seeked to: %02d:%02d:%02d\n", (t/3600), (t/60)%60, t%60); m_packetAfterSeek = false; m_seekFlush = false; } else if (m_packetAfterSeek && TRICKPLAY(m_av_clock->OMXPlaySpeed())) { double seek_pos = 0; double pts = 0; pts = m_av_clock->OMXMediaTime(); seek_pos = (pts/DVD_TIME_BASE); seek_pos *= 1000.0; #if 1 if (m_omx_reader->SeekTime((int)seek_pos, m_av_clock->OMXPlaySpeed() < 0, &startpts)) { ; //FlushStreams(DVD_NOPTS_VALUE); } #endif // 0 CLog::Log(LOGDEBUG, "Seeked %.0f %.0f %.0f\n", DVD_MSEC_TO_TIME(seek_pos), startpts, m_av_clock->OMXMediaTime()); //unsigned t = (unsigned)(startpts*1e-6); unsigned t = (unsigned)(pts*1e-6); printf("Seek to: %02d:%02d:%02d\n", (t/3600), (t/60)%60, t%60); m_packetAfterSeek = false; } // TODO: Better error handling. if (m_player_audio->Error()) { LOG_ERROR(LOG_TAG, "Audio player error. emergency exit!"); break; } if (update) { #ifdef ENABLE_PAUSE_FOR_BUFFERING /* when the video/audio fifos are low, we pause clock, when high we resume */ stamp = m_av_clock->OMXMediaTime(); audio_pts = m_player_audio->GetCurrentPTS(); video_pts = m_player_video->GetCurrentPTS(); if (0 && m_av_clock->OMXIsPaused()) { double old_stamp = stamp; if (audio_pts != DVD_NOPTS_VALUE && (stamp == 0 || audio_pts < stamp)) stamp = audio_pts; if (video_pts != DVD_NOPTS_VALUE && (stamp == 0 || video_pts < stamp)) stamp = video_pts; if (old_stamp != stamp) { m_av_clock->OMXMediaTime(stamp); stamp = m_av_clock->OMXMediaTime(); } } audio_fifo = audio_pts == DVD_NOPTS_VALUE ? 0.0f : audio_pts / DVD_TIME_BASE - stamp * 1e-6; video_fifo = video_pts == DVD_NOPTS_VALUE ? 0.0f : video_pts / DVD_TIME_BASE - stamp * 1e-6; threshold = min(0.1f, (float)m_player_audio->GetCacheTotal()*0.1f); #endif // ENABLE_PAUSE_FOR_BUFFERING #if 0 static int count; if ((count++ & 15) == 0) { LOG_VERBOSE(LOG_TAG, "M: %8.02f V : %8.02f %8d %8d A : %8.02f %8.02f/%8.02f Cv : %8d Ca : %8d \r", stamp, audio_fifo, m_player_video->GetDecoderBufferSize(), m_player_video->GetDecoderFreeSpace(), video_fifo, m_player_audio->GetDelay(), m_player_audio->GetCacheTotal(), m_player_video->GetCached(), m_player_audio->GetCached()); } #endif #if 0 if(m_tv_show_info) { static unsigned count; if ((count++ & 15) == 0) { char response[80]; if (m_player_video.GetDecoderBufferSize() && m_player_audio.GetCacheTotal()) vc_gencmd(response, sizeof response, "render_bar 4 video_fifo %d %d %d %d", (int)(100.0*m_player_video.GetDecoderBufferSize()-m_player_video.GetDecoderFreeSpace())/m_player_video.GetDecoderBufferSize(), (int)(100.0*video_fifo/m_player_audio.GetCacheTotal()), 0, 100); if (m_player_audio.GetCacheTotal()) vc_gencmd(response, sizeof response, "render_bar 5 audio_fifo %d %d %d %d", (int)(100.0*audio_fifo/m_player_audio.GetCacheTotal()), (int)(100.0*m_player_audio.GetDelay()/m_player_audio.GetCacheTotal()), 0, 100); vc_gencmd(response, sizeof response, "render_bar 6 video_queue %d %d %d %d", m_player_video.GetLevel(), 0, 0, 100); vc_gencmd(response, sizeof response, "render_bar 7 audio_queue %d %d %d %d", m_player_audio.GetLevel(), 0, 0, 100); } } #endif #ifdef ENABLE_PAUSE_FOR_BUFFERING if (audio_pts != DVD_NOPTS_VALUE) { audio_fifo_low = m_has_audio && audio_fifo < threshold; audio_fifo_high = !m_has_audio || (audio_pts != DVD_NOPTS_VALUE && audio_fifo > m_threshold); } if (video_pts != DVD_NOPTS_VALUE) { video_fifo_low = m_has_video && video_fifo < threshold; video_fifo_high = !m_has_video || (video_pts != DVD_NOPTS_VALUE && video_fifo > m_threshold); } // Enable this to enable pause for buffering. if (m_state != STATE_PAUSED && (m_omx_reader->IsEof() || m_omx_pkt || TRICKPLAY(m_av_clock->OMXPlaySpeed()) || (audio_fifo_high && video_fifo_high))) { if (m_av_clock->OMXIsPaused()) { CLog::Log(LOGDEBUG, "Resume %.2f,%.2f (%d,%d,%d,%d) EOF:%d PKT:%p\n", audio_fifo, video_fifo, audio_fifo_low, video_fifo_low, audio_fifo_high, video_fifo_high, m_omx_reader->IsEof(), m_omx_pkt); log_verbose("Pausing for buffering..."); //m_av_clock->OMXStateExecute(); m_av_clock->OMXResume(); } } else if (m_state == STATE_PAUSED || audio_fifo_low || video_fifo_low) { if (!m_av_clock->OMXIsPaused()) { if (m_state != STATE_PAUSED) m_threshold = std::min(2.0f*m_threshold, 16.0f); CLog::Log(LOGDEBUG, "Pause %.2f,%.2f (%d,%d,%d,%d) %.2f\n", audio_fifo, video_fifo, audio_fifo_low, video_fifo_low, audio_fifo_high, video_fifo_high, m_threshold); log_verbose("Buffering completed. Resuming..."); m_av_clock->OMXPause(); } } #endif } if (!sentStarted) { CLog::Log(LOGDEBUG, "COMXPlayer::HandleMessages - player started RESET"); m_av_clock->OMXReset(m_has_video, m_has_audio); m_av_clock->OMXStateExecute(); sentStarted = true; } if (!m_omx_pkt) m_omx_pkt = m_omx_reader->Read(); if (m_omx_pkt) sendEos = false; if (m_omx_reader->IsEof() && !m_omx_pkt) { // demuxer EOF, but may have not played out data yet if ((m_has_video && m_player_video->GetCached()) || (m_has_audio && m_player_audio->GetCached())) { OMXClock::OMXSleep(10); continue; } if (m_loop) { m_incr = m_loop_from - (m_av_clock->OMXMediaTime() ? m_av_clock->OMXMediaTime() / DVD_TIME_BASE : last_seek_pos); continue; } if (!sendEos && m_has_video) m_player_video->SubmitEOS(); if (!sendEos && m_has_audio) m_player_audio->SubmitEOS(); sendEos = true; if ((m_has_video && !m_player_video->IsEOS()) || (m_has_audio && !m_player_audio->IsEOS()) ) { OMXClock::OMXSleep(10); continue; } break; } if (!m_omx_pkt) m_omx_pkt = m_omx_reader->Read(); if(m_omx_pkt) sendEos = false; if(m_omx_reader->IsEof() && !m_omx_pkt) { // demuxer EOF, but may have not played out data yet if ( (m_has_video && m_player_video->GetCached()) || (m_has_audio && m_player_audio->GetCached()) ) { OMXClock::OMXSleep(10); continue; } if (!sendEos && m_has_video) m_player_video->SubmitEOS(); if (!sendEos && m_has_audio) m_player_audio->SubmitEOS(); sendEos = true; if ( (m_has_video && !m_player_video->IsEOS()) || (m_has_audio && !m_player_audio->IsEOS()) ) { OMXClock::OMXSleep(10); continue; } break; } if(m_has_video && m_omx_pkt && m_omx_reader->IsActive(OMXSTREAM_VIDEO, m_omx_pkt->stream_index)) { if (TRICKPLAY(m_av_clock->OMXPlaySpeed())) { m_packetAfterSeek = true; } if(m_player_video->AddPacket(m_omx_pkt)) m_omx_pkt = NULL; else OMXClock::OMXSleep(10); } else if(m_has_audio && m_omx_pkt && !TRICKPLAY(m_av_clock->OMXPlaySpeed()) && m_omx_pkt->codec_type == AVMEDIA_TYPE_AUDIO) { if(m_player_audio->AddPacket(m_omx_pkt)) m_omx_pkt = NULL; else OMXClock::OMXSleep(10); } #ifdef ENABLE_SUBTITLES else if(m_has_subtitle && m_omx_pkt && !TRICKPLAY(m_av_clock->OMXPlaySpeed()) && m_omx_pkt->codec_type == AVMEDIA_TYPE_SUBTITLE) { auto result = m_player_subtitles.AddPacket(m_omx_pkt, m_omx_reader.GetRelativeIndex(m_omx_pkt->stream_index)); if (result) m_omx_pkt = NULL; else OMXClock::OMXSleep(10); } #endif else { if(m_omx_pkt) { m_omx_reader->FreePacket(m_omx_pkt); m_omx_pkt = NULL; } else OMXClock::OMXSleep(10); } } LOG_VERBOSE(LOG_TAG, "Stopping OMX clock..."); //m_av_clock->OMXResume(); m_av_clock->OMXStop(); m_av_clock->OMXStateIdle(); //m_av_clock->OMXStateExecute(); //m_av_clock->OMXReset(m_has_video, m_has_audio); // Restart video player. It seems it is necessary to close // and open again. omxplayer does this also when seeking so // maybe there is no other way to restart it. m_player_video->Close(); if (m_has_video) if (!m_player_video->Open( *m_hints_video, m_av_clock, m_textureData, VS_DEINTERLACEMODE_OFF, /* deinterlace */ OMX_ImageFilterAnaglyphNone, ENABLE_HDMI_CLOCK_SYNC, true, /* threaded */ 1.0, /* display aspect, unused */ 0, /* display, unused */ 0, /* layer */ m_video_queue_size, m_video_fifo_size )) { LOG_ERROR(LOG_TAG, "Failed to reopen media."); } // TODO: Handle failure. setState(STATE_STOPPED); emit playbackCompleted(); // Actually change the state here and reset flags. m_mutexPending.lock(); if (m_pendingStop) { m_pendingStop = false; m_waitPendingCommand.wakeAll(); } m_mutexPending.unlock(); }
void OMXEGLImagePlayer::Output(double pts) { if(m_syncclock) { double delay = m_FlipTimeStamp - m_av_clock->GetAbsoluteClock(); if( delay > m_frametime ) { delay = m_frametime; }else { if( delay < 0 ) { delay = 0; } } //printf("OMXEGLImagePlayer - GENERAL_RESYNC(%f, 1) delay %f\n", pts, m_FlipTimeStamp); m_av_clock->Discontinuity(pts - delay); m_syncclock = false; } double iSleepTime, iClockSleep, iFrameSleep, iPlayingClock, iCurrentClock, iFrameDuration; iPlayingClock = m_av_clock->GetClock(iCurrentClock, false); // snapshot current clock iClockSleep = pts - iPlayingClock; //sleep calculated by pts to clock comparison iFrameSleep = m_FlipTimeStamp - iCurrentClock; // sleep calculated by duration of frame iFrameDuration = m_frametime; // correct sleep times based on speed if(m_speed) { iClockSleep = iClockSleep * DVD_PLAYSPEED_NORMAL / m_speed; iFrameSleep = iFrameSleep * DVD_PLAYSPEED_NORMAL / abs(m_speed); iFrameDuration = iFrameDuration * DVD_PLAYSPEED_NORMAL / abs(m_speed); } else { iClockSleep = 0; iFrameSleep = 0; } // dropping to a very low framerate is not correct (it should not happen at all) iClockSleep = min(iClockSleep, DVD_MSEC_TO_TIME(500)); iFrameSleep = min(iFrameSleep, DVD_MSEC_TO_TIME(500)); bool m_stalled = false; int m_autosync = 1; if( m_stalled ) { iSleepTime = iFrameSleep; } else { iSleepTime = iFrameSleep + (iClockSleep - iFrameSleep) / m_autosync; } // present the current pts of this frame to user, and include the actual // presentation delay, to allow him to adjust for it if( m_stalled ) { m_iCurrentPts = DVD_NOPTS_VALUE; } else { m_iCurrentPts = pts - max(0.0, iSleepTime); } m_av_clock->SetPTS(m_iCurrentPts); // timestamp when we think next picture should be displayed based on current duration m_FlipTimeStamp = iCurrentClock; m_FlipTimeStamp += max(0.0, iSleepTime); m_FlipTimeStamp += iFrameDuration; if(doDebugging) { sprintf(debugInfoBuffer, "iPlayingClock %f \n\ iCurrentClock %f \n\ iClockSleep %f \n\ iFrameSleep %f \n\ iFrameDuration %f \n\ WaitAbsolut %f \n\ m_FlipTimeStamp %f \n\ pts %f \n\ currentFrame %d \n\ Cached Video %8d", iPlayingClock / DVD_TIME_BASE, iCurrentClock / DVD_TIME_BASE, iClockSleep / DVD_TIME_BASE, iFrameSleep / DVD_TIME_BASE, iFrameDuration / DVD_TIME_BASE, (iCurrentClock + iSleepTime) / DVD_TIME_BASE, m_FlipTimeStamp / DVD_TIME_BASE, pts / DVD_TIME_BASE, (int)((m_FlipTimeStamp / DVD_TIME_BASE)*m_fps), m_cached_size); debugInfo = (string) debugInfoBuffer; } //ofLogVerbose() << debugInfo; if(m_av_clock->GetAbsoluteClock(false) < (iCurrentClock + iSleepTime + DVD_MSEC_TO_TIME(500)) ) { //ofLogVerbose() << "OMXEGLImagePlayer::Output returning early"; return; } m_av_clock->WaitAbsoluteClock((iCurrentClock + iSleepTime)); // guess next frame pts. iDuration is always valid if (m_speed != 0) { m_pts += m_frametime * m_speed / abs(m_speed); } }
void OMXPlayerVideo::Output(int iGroupId, double pts, bool bDropPacket) { if (!g_renderManager.IsStarted()) { CLog::Log(LOGERROR, "%s - renderer not started", __FUNCTION__); return; } // calculate the time we need to delay this picture before displaying double iSleepTime, iClockSleep, iFrameSleep, iPlayingClock, iCurrentClock, iFrameDuration; iPlayingClock = m_av_clock->GetClock(iCurrentClock, false); // snapshot current clock iClockSleep = pts - iPlayingClock; //sleep calculated by pts to clock comparison iFrameSleep = m_FlipTimeStamp - iCurrentClock; // sleep calculated by duration of frame iFrameDuration = (double)DVD_TIME_BASE / m_fFrameRate; //pPacket->duration; // correct sleep times based on speed if(m_speed) { iClockSleep = iClockSleep * DVD_PLAYSPEED_NORMAL / m_speed; iFrameSleep = iFrameSleep * DVD_PLAYSPEED_NORMAL / abs(m_speed); iFrameDuration = iFrameDuration * DVD_PLAYSPEED_NORMAL / abs(m_speed); } else { iClockSleep = 0; iFrameSleep = 0; } // dropping to a very low framerate is not correct (it should not happen at all) iClockSleep = min(iClockSleep, DVD_MSEC_TO_TIME(500)); iFrameSleep = min(iFrameSleep, DVD_MSEC_TO_TIME(500)); if( m_stalled ) iSleepTime = iFrameSleep; else iSleepTime = iFrameSleep + (iClockSleep - iFrameSleep) / m_autosync; // present the current pts of this frame to user, and include the actual // presentation delay, to allow him to adjust for it if( m_stalled ) m_iCurrentPts = DVD_NOPTS_VALUE; else m_iCurrentPts = pts - max(0.0, iSleepTime); // timestamp when we think next picture should be displayed based on current duration m_FlipTimeStamp = iCurrentClock; m_FlipTimeStamp += max(0.0, iSleepTime); m_FlipTimeStamp += iFrameDuration; if( m_speed < 0 ) { if( iClockSleep < -DVD_MSEC_TO_TIME(200)) return; } if(bDropPacket) return; #if 0 if( m_speed != DVD_PLAYSPEED_NORMAL) { // calculate frame dropping pattern to render at this speed // we do that by deciding if this or next frame is closest // to the flip timestamp double current = fabs(m_dropbase - m_droptime); double next = fabs(m_dropbase - (m_droptime + iFrameDuration)); double frametime = (double)DVD_TIME_BASE / m_fFrameRate; m_droptime += iFrameDuration; #ifndef PROFILE if( next < current /*&& !(pPicture->iFlags & DVP_FLAG_NOSKIP) */) return /*result | EOS_DROPPED*/; #endif while(!m_bStop && m_dropbase < m_droptime) m_dropbase += frametime; while(!m_bStop && m_dropbase - frametime > m_droptime) m_dropbase -= frametime; } else { m_droptime = 0.0f; m_dropbase = 0.0f; } #else m_droptime = 0.0f; m_dropbase = 0.0f; #endif int buffer = g_renderManager.WaitForBuffer(m_bStop, 0); if (buffer < 0) return; double pts_overlay = m_av_clock->OMXMediaTime(false, false) + 2* (double)DVD_TIME_BASE / g_graphicsContext.GetFPS(); ProcessOverlays(iGroupId, pts_overlay); double timestamp = CDVDClock::GetAbsoluteClock(false) + 2 / g_graphicsContext.GetFPS(); g_renderManager.FlipPage(CThread::m_bStop, timestamp, -1, FS_NONE); }
void OMXPlayerAudio::HandleSyncError(double duration) { double clock = m_av_clock->GetClock(); double error = m_audioClock - clock; int64_t now; if( fabs(error) > DVD_MSEC_TO_TIME(100) || m_syncclock ) { m_av_clock->Discontinuity(clock+error); /* if(m_speed == DVD_PLAYSPEED_NORMAL) CLog::Log(LOGDEBUG, "OMXPlayerAudio:: Discontinuity - was:%f, should be:%f, error:%f\n", clock, clock+error, error); */ m_errorbuff = 0; m_errorcount = 0; m_skipdupcount = 0; m_error = 0; m_syncclock = false; m_errortime = m_av_clock->CurrentHostCounter(); return; } if (m_speed != DVD_PLAYSPEED_NORMAL) { m_errorbuff = 0; m_errorcount = 0; m_integral = 0; m_skipdupcount = 0; m_error = 0; m_errortime = m_av_clock->CurrentHostCounter(); return; } //check if measured error for 1 second now = m_av_clock->CurrentHostCounter(); if ((now - m_errortime) >= m_freq) { m_errortime = now; m_error = m_errorbuff / m_errorcount; m_errorbuff = 0; m_errorcount = 0; if (m_synctype == SYNC_DISCON) { double limit, error; if (m_av_clock->GetRefreshRate(&limit) > 0) { //when the videoreferenceclock is running, the discontinuity limit is one vblank period limit *= DVD_TIME_BASE; //make error a multiple of limit, rounded towards zero, //so it won't interfere with the sync methods in CXBMCRenderManager::WaitPresentTime if (m_error > 0.0) error = limit * floor(m_error / limit); else error = limit * ceil(m_error / limit); } else { limit = DVD_MSEC_TO_TIME(10); error = m_error; } /* limit = DVD_MSEC_TO_TIME(10); error = m_error; */ if (fabs(error) > limit - 0.001) { m_av_clock->Discontinuity(clock+error); /* if(m_speed == DVD_PLAYSPEED_NORMAL) CLog::Log(LOGDEBUG, "COMXPlayerAudio:: Discontinuity - was:%f, should be:%f, error:%f", clock, clock+error, error); */ } } /* else if (m_synctype == SYNC_SKIPDUP && m_skipdupcount == 0 && fabs(m_error) > DVD_MSEC_TO_TIME(10)) if (m_skipdupcount == 0 && fabs(m_error) > DVD_MSEC_TO_TIME(10)) { //check how many packets to skip/duplicate m_skipdupcount = (int)(m_error / duration); //if less than one frame off, see if it's more than two thirds of a frame, so we can get better in sync if (m_skipdupcount == 0 && fabs(m_error) > duration / 3 * 2) m_skipdupcount = (int)(m_error / (duration / 3 * 2)); if (m_skipdupcount > 0) CLog::Log(LOGDEBUG, "OMXPlayerAudio:: Duplicating %i packet(s) of %.2f ms duration", m_skipdupcount, duration / DVD_TIME_BASE * 1000.0); else if (m_skipdupcount < 0) CLog::Log(LOGDEBUG, "OMXPlayerAudio:: Skipping %i packet(s) of %.2f ms duration ", m_skipdupcount * -1, duration / DVD_TIME_BASE * 1000.0); } */ } }
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; }
void OMXPlayerVideo::Output(int iGroupId, double pts, bool bDropPacket) { if (!g_renderManager.IsConfigured() || m_video_width != m_width || m_video_height != m_height || m_fps != m_fFrameRate) { m_width = m_video_width; m_height = m_video_height; m_fps = m_fFrameRate; unsigned flags = 0; ERenderFormat format = RENDER_FMT_BYPASS; if(m_bAllowFullscreen) { flags |= CONF_FLAGS_FULLSCREEN; m_bAllowFullscreen = false; // only allow on first configure } if(m_flags & CONF_FLAGS_FORMAT_SBS) { if(g_Windowing.Support3D(m_video_width, m_video_height, D3DPRESENTFLAG_MODE3DSBS)) { CLog::Log(LOGNOTICE, "3DSBS movie found"); flags |= CONF_FLAGS_FORMAT_SBS; } } CLog::Log(LOGDEBUG,"%s - change configuration. %dx%d. framerate: %4.2f. format: BYPASS", __FUNCTION__, m_width, m_height, m_fps); if(!g_renderManager.Configure(m_video_width, m_video_height, m_video_width, m_video_height, m_fps, flags, format, 0, m_hints.orientation)) { CLog::Log(LOGERROR, "%s - failed to configure renderer", __FUNCTION__); return; } } if (!g_renderManager.IsStarted()) { CLog::Log(LOGERROR, "%s - renderer not started", __FUNCTION__); return; } // calculate the time we need to delay this picture before displaying double iSleepTime, iClockSleep, iFrameSleep, iPlayingClock, iCurrentClock, iFrameDuration; iPlayingClock = m_av_clock->GetClock(iCurrentClock, false); // snapshot current clock iClockSleep = pts - iPlayingClock; //sleep calculated by pts to clock comparison iFrameSleep = m_FlipTimeStamp - iCurrentClock; // sleep calculated by duration of frame iFrameDuration = (double)DVD_TIME_BASE / m_fFrameRate; //pPacket->duration; // correct sleep times based on speed if(m_speed) { iClockSleep = iClockSleep * DVD_PLAYSPEED_NORMAL / m_speed; iFrameSleep = iFrameSleep * DVD_PLAYSPEED_NORMAL / abs(m_speed); iFrameDuration = iFrameDuration * DVD_PLAYSPEED_NORMAL / abs(m_speed); } else { iClockSleep = 0; iFrameSleep = 0; } // dropping to a very low framerate is not correct (it should not happen at all) iClockSleep = min(iClockSleep, DVD_MSEC_TO_TIME(500)); iFrameSleep = min(iFrameSleep, DVD_MSEC_TO_TIME(500)); if( m_stalled ) iSleepTime = iFrameSleep; else iSleepTime = iFrameSleep + (iClockSleep - iFrameSleep) / m_autosync; // present the current pts of this frame to user, and include the actual // presentation delay, to allow him to adjust for it if( m_stalled ) m_iCurrentPts = DVD_NOPTS_VALUE; else m_iCurrentPts = pts - max(0.0, iSleepTime); // timestamp when we think next picture should be displayed based on current duration m_FlipTimeStamp = iCurrentClock; m_FlipTimeStamp += max(0.0, iSleepTime); m_FlipTimeStamp += iFrameDuration; if( m_speed < 0 ) { if( iClockSleep < -DVD_MSEC_TO_TIME(200)) return; } if(bDropPacket) return; #if 0 if( m_speed != DVD_PLAYSPEED_NORMAL) { // calculate frame dropping pattern to render at this speed // we do that by deciding if this or next frame is closest // to the flip timestamp double current = fabs(m_dropbase - m_droptime); double next = fabs(m_dropbase - (m_droptime + iFrameDuration)); double frametime = (double)DVD_TIME_BASE / m_fFrameRate; m_droptime += iFrameDuration; #ifndef PROFILE if( next < current /*&& !(pPicture->iFlags & DVP_FLAG_NOSKIP) */) return /*result | EOS_DROPPED*/; #endif while(!m_bStop && m_dropbase < m_droptime) m_dropbase += frametime; while(!m_bStop && m_dropbase - frametime > m_droptime) m_dropbase -= frametime; } else { m_droptime = 0.0f; m_dropbase = 0.0f; } #else m_droptime = 0.0f; m_dropbase = 0.0f; #endif double pts_media = m_av_clock->OMXMediaTime(); ProcessOverlays(iGroupId, pts_media); while(!CThread::m_bStop && m_av_clock->GetAbsoluteClock(false) < (iCurrentClock + iSleepTime + DVD_MSEC_TO_TIME(500)) ) Sleep(1); g_renderManager.FlipPage(CThread::m_bStop, (iCurrentClock + iSleepTime) / DVD_TIME_BASE, -1, FS_NONE); //m_av_clock->WaitAbsoluteClock((iCurrentClock + iSleepTime)); }
bool CVideoPlayerVideo::ProcessDecoderOutput(double &frametime, double &pts) { CDVDVideoCodec::VCReturn decoderState = m_pVideoCodec->GetPicture(&m_picture); if (decoderState == CDVDVideoCodec::VC_BUFFER) { return false; } // if decoder was flushed, we need to seek back again to resume rendering if (decoderState == CDVDVideoCodec::VC_FLUSHED) { CLog::Log(LOGDEBUG, "CVideoPlayerVideo - video decoder was flushed"); while (!m_packets.empty()) { CDVDMsgDemuxerPacket* msg = static_cast<CDVDMsgDemuxerPacket*>(m_packets.front().message->Acquire()); m_packets.pop_front(); SendMessage(msg, 10); } m_pVideoCodec->Reset(); m_packets.clear(); //picture.iFlags &= ~DVP_FLAG_ALLOCATED; m_renderManager.DiscardBuffer(); return false; } if (decoderState == CDVDVideoCodec::VC_REOPEN) { while (!m_packets.empty()) { CDVDMsgDemuxerPacket* msg = static_cast<CDVDMsgDemuxerPacket*>(m_packets.front().message->Acquire()); m_packets.pop_front(); SendMessage(msg, 10); } m_pVideoCodec->Reopen(); m_packets.clear(); m_renderManager.DiscardBuffer(); return false; } // if decoder had an error, tell it to reset to avoid more problems if (decoderState == CDVDVideoCodec::VC_ERROR) { CLog::Log(LOGDEBUG, "CVideoPlayerVideo - video decoder returned error"); return false; } if (decoderState == CDVDVideoCodec::VC_EOF) { if (m_syncState == IDVDStreamPlayer::SYNC_STARTING) { SStartMsg msg; msg.player = VideoPlayer_VIDEO; msg.cachetime = DVD_MSEC_TO_TIME(50); msg.cachetotal = DVD_MSEC_TO_TIME(100); msg.timestamp = DVD_NOPTS_VALUE; m_messageParent.Put(new CDVDMsgType<SStartMsg>(CDVDMsg::PLAYER_STARTED, msg)); } return false; } // check for a new picture if (decoderState == CDVDVideoCodec::VC_PICTURE) { bool hasTimestamp = true; m_picture.iDuration = frametime; // 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 (m_picture.dts == DVD_NOPTS_VALUE && m_picture.pts == DVD_NOPTS_VALUE) { m_picture.pts = pts; hasTimestamp = false; } else if (m_picture.pts == DVD_NOPTS_VALUE) m_picture.pts = m_picture.dts; // use forced aspect if any if (m_fForcedAspectRatio != 0.0f) { m_picture.iDisplayWidth = (int) (m_picture.iDisplayHeight * m_fForcedAspectRatio); if (m_picture.iDisplayWidth > m_picture.iWidth) { m_picture.iDisplayWidth = m_picture.iWidth; m_picture.iDisplayHeight = (int) (m_picture.iDisplayWidth / m_fForcedAspectRatio); } } // set stereo mode if not set by decoder if (m_picture.stereoMode.empty()) { std::string stereoMode; switch(m_processInfo.GetVideoSettings().m_StereoMode) { case RENDER_STEREO_MODE_SPLIT_VERTICAL: stereoMode = "left_right"; if (m_processInfo.GetVideoSettings().m_StereoInvert) stereoMode = "right_left"; break; case RENDER_STEREO_MODE_SPLIT_HORIZONTAL: stereoMode = "top_bottom"; if (m_processInfo.GetVideoSettings().m_StereoInvert) stereoMode = "bottom_top"; break; default: stereoMode = m_hints.stereo_mode; break; } if (!stereoMode.empty() && stereoMode != "mono") { m_picture.stereoMode = stereoMode; } } // if frame has a pts (usually originiating from demux packet), use that if (m_picture.pts != DVD_NOPTS_VALUE) { pts = m_picture.pts; } double extraDelay = 0.0; if (m_picture.iRepeatPicture) { extraDelay = m_picture.iRepeatPicture * m_picture.iDuration; m_picture.iDuration += extraDelay; } m_picture.pts = pts + extraDelay; // guess next frame pts. iDuration is always valid if (m_speed != 0) pts += m_picture.iDuration * m_speed / abs(m_speed); m_outputSate = OutputPicture(&m_picture); if (m_outputSate == OUTPUT_AGAIN) { return true; } else if (m_outputSate == OUTPUT_ABORT) { return false; } else if ((m_outputSate == OUTPUT_DROPPED) && !(m_picture.iFlags & DVP_FLAG_DROPPED)) { m_iDroppedFrames++; m_ptsTracker.Flush(); } if (m_syncState == IDVDStreamPlayer::SYNC_STARTING && m_outputSate != OUTPUT_DROPPED && !(m_picture.iFlags & DVP_FLAG_DROPPED)) { m_syncState = IDVDStreamPlayer::SYNC_WAITSYNC; SStartMsg msg; msg.player = VideoPlayer_VIDEO; msg.cachetime = DVD_MSEC_TO_TIME(50); //! @todo implement msg.cachetotal = DVD_MSEC_TO_TIME(100); //! @todo implement msg.timestamp = hasTimestamp ? (pts + m_renderManager.GetDelay() * 1000) : DVD_NOPTS_VALUE; m_messageParent.Put(new CDVDMsgType<SStartMsg>(CDVDMsg::PLAYER_STARTED, msg)); } frametime = (double)DVD_TIME_BASE / m_fFrameRate; } return true; }