/*------------------------------------------------------------------------------ | OMX_MediaProcessor::setSpeed +-----------------------------------------------------------------------------*/ inline void OMX_MediaProcessor::setSpeed(int iSpeed) { if (!m_av_clock) return; m_omx_reader->SetSpeed(iSpeed); // flush when in trickplay mode if (TRICKPLAY(iSpeed) || TRICKPLAY(m_av_clock->OMXPlaySpeed())) flushStreams(DVD_NOPTS_VALUE); m_av_clock->OMXSetSpeed(iSpeed); }
bool OMXClock::OMXSetSpeed(int speed, bool lock /* = true */, bool pause_resume /* = false */) { ofLog(OF_LOG_VERBOSE, "OMXClock::OMXSetSpeed(%d)", speed); if(m_omx_clock.GetComponent() == NULL) { return false; } if(lock) { Lock(); } OMX_ERRORTYPE error = OMX_ErrorNone; OMX_TIME_CONFIG_SCALETYPE scaleType; OMX_INIT_STRUCTURE(scaleType); OMX_TIME_CONFIG_ACTIVEREFCLOCKTYPE refClock; OMX_INIT_STRUCTURE(refClock); if(m_has_audio && !TRICKPLAY(speed)) { refClock.eClock = OMX_TIME_RefClockAudio; } else { refClock.eClock = OMX_TIME_RefClockVideo; } error = m_omx_clock.SetConfig(OMX_IndexConfigTimeActiveRefClock, &refClock); if(error != OMX_ErrorNone) { ofLogError(__func__) << "SetConfig OMX_IndexConfigTimeActiveRefClock FAIL: " << COMXCore::getOMXError(error); return false; } if (TRICKPLAY(speed)) { OMXStep(-1, false); } else { OMXStep(0, false); } if (0 && TRICKPLAY(speed)) { scaleType.xScale = 0; } else { scaleType.xScale = (speed << 16) / DVD_PLAYSPEED_NORMAL; } error = m_omx_clock.SetConfig(OMX_IndexConfigTimeScale, &scaleType); if(error != OMX_ErrorNone) { ofLogError(__func__) << "SetConfig OMX_IndexConfigTimeScale FAIL: " << COMXCore::getOMXError(error); if(lock) { UnLock(); } return false; } if (!pause_resume) { m_omx_speed = speed; } if(lock) { UnLock(); } return true; }
/*------------------------------------------------------------------------------ | 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(); }