/*------------------------------------------------------------------------------
|    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);
}
Exemple #2
0
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();
}