[vlc-devel] [PATCH 05/10] chromecast: fix the time displayed after seeking

Steve Lhomme robux4 at videolabs.io
Tue May 31 13:15:32 CEST 2016


When seeking the Chromecast will flush its buffering and start playing
after it has enough data. We wait until it says it's playing after a seek
to guess the playback time it's resuming to.
---
 modules/stream_out/chromecast/cast.cpp             |  2 +-
 modules/stream_out/chromecast/chromecast.h         | 14 +++-
 modules/stream_out/chromecast/chromecast_common.h  |  3 +
 modules/stream_out/chromecast/chromecast_ctrl.cpp  | 83 +++++++++++++++++-
 modules/stream_out/chromecast/chromecast_demux.cpp | 97 ++++++++++++++++++++++
 5 files changed, 193 insertions(+), 6 deletions(-)

diff --git a/modules/stream_out/chromecast/cast.cpp b/modules/stream_out/chromecast/cast.cpp
index 69747f4..c0ad59d 100644
--- a/modules/stream_out/chromecast/cast.cpp
+++ b/modules/stream_out/chromecast/cast.cpp
@@ -375,7 +375,7 @@ static void Flush( sout_stream_t *p_stream, sout_stream_id_sys_t *id )
         return;
 
     /* a seek on the Chromecast flushes its buffers */
-    p_sys->p_intf->requestPlayerSeek();
+    p_sys->p_intf->requestPlayerSeek( VLC_TS_INVALID );
 
     sout_StreamFlush( p_sys->p_out, id );
 }
diff --git a/modules/stream_out/chromecast/chromecast.h b/modules/stream_out/chromecast/chromecast.h
index 53557ef..48017cc 100644
--- a/modules/stream_out/chromecast/chromecast.h
+++ b/modules/stream_out/chromecast/chromecast.h
@@ -90,7 +90,7 @@ struct intf_sys_t
 
     void setHasInput( bool has_input, const std::string mime_type = "");
 
-    void requestPlayerSeek();
+    void requestPlayerSeek(mtime_t pos);
     void requestPlayerStop();
 
 private:
@@ -127,10 +127,12 @@ private:
 #endif
             conn_status = status;
             vlc_cond_broadcast(&loadCommandCond);
+            vlc_cond_signal(&seekCommandCond);
         }
     }
 
     void waitAppStarted();
+    void waitSeekDone();
 
     int connectChromecast();
     void disconnectChromecast();
@@ -217,6 +219,13 @@ private:
     mtime_t           i_ts_local_start;
     mtime_t           i_length;
 
+    /* playback time reported by the receiver, used to wait for seeking point */
+    mtime_t           m_chromecast_start_time;
+    /* seek time with Chromecast relative timestamp */
+    mtime_t           m_seek_request_time;
+
+    vlc_cond_t   seekCommandCond;
+
     int recvPacket(bool &b_msgReceived, uint32_t &i_payloadSize,
                    unsigned *pi_received, uint8_t *p_data, bool *pb_pingTimeout,
                    int *pi_wait_delay, int *pi_wait_retries);
@@ -229,6 +238,9 @@ private:
     static double get_position(void*);
 
     static void wait_app_started(void*);
+
+    static void request_seek(void*, mtime_t pos);
+    static void wait_seek_done(void*);
 };
 
 #endif /* VLC_CHROMECAST_H */
diff --git a/modules/stream_out/chromecast/chromecast_common.h b/modules/stream_out/chromecast/chromecast_common.h
index 04d6586..632b37b 100644
--- a/modules/stream_out/chromecast/chromecast_common.h
+++ b/modules/stream_out/chromecast/chromecast_common.h
@@ -40,6 +40,9 @@ typedef struct
     double (*pf_get_position)(void*);
 
     void (*pf_wait_app_started)(void*);
+
+    void (*pf_request_seek)(void*, mtime_t pos);
+    void (*pf_wait_seek_done)(void*);
 } chromecast_common;
 
 # ifdef __cplusplus
diff --git a/modules/stream_out/chromecast/chromecast_ctrl.cpp b/modules/stream_out/chromecast/chromecast_ctrl.cpp
index 3d690c0..e3f5b38 100644
--- a/modules/stream_out/chromecast/chromecast_ctrl.cpp
+++ b/modules/stream_out/chromecast/chromecast_ctrl.cpp
@@ -113,15 +113,20 @@ intf_sys_t::intf_sys_t(vlc_object_t * const p_this, int port, std::string device
  , m_time_playback_started( VLC_TS_INVALID )
  , i_ts_local_start( VLC_TS_INVALID )
  , i_length( VLC_TS_INVALID )
+ , m_chromecast_start_time( VLC_TS_INVALID )
+ , m_seek_request_time( VLC_TS_INVALID )
 {
     vlc_mutex_init(&lock);
     vlc_cond_init(&loadCommandCond);
+    vlc_cond_init(&seekCommandCond);
 
     common.p_opaque = this;
     common.pf_get_position     = get_position;
     common.pf_get_time         = get_time;
     common.pf_set_length       = set_length;
     common.pf_wait_app_started = wait_app_started;
+    common.pf_request_seek     = request_seek;
+    common.pf_wait_seek_done   = wait_seek_done;
 
     assert( var_Type( p_module->p_parent->p_parent, CC_SHARED_VAR_NAME) == 0 );
     if (var_Create( p_module->p_parent->p_parent, CC_SHARED_VAR_NAME, VLC_VAR_ADDRESS ) == VLC_SUCCESS )
@@ -163,6 +168,11 @@ intf_sys_t::~intf_sys_t()
 
     vlc_interrupt_destroy( p_ctl_thread_interrupt );
 
+    // make sure we unblock the demuxer
+    m_seek_request_time = VLC_TS_INVALID;
+    vlc_cond_signal(&seekCommandCond);
+
+    vlc_cond_destroy(&seekCommandCond);
     vlc_cond_destroy(&loadCommandCond);
     vlc_mutex_destroy(&lock);
 }
@@ -558,18 +568,37 @@ void intf_sys_t::processMessage(const castchannel::CastMessage &msg)
 #endif
                 switch( receiverState )
                 {
+                case RECEIVER_BUFFERING:
+                    if ( double(status[0]["currentTime"]) == 0.0 )
+                    {
+                        receiverState = oldPlayerState;
+                        msg_Dbg( p_module, "Invalid buffering time, keep previous state %d", oldPlayerState);
+                    }
+                    else
+                    {
+                        m_chromecast_start_time = (1 + mtime_t( double( status[0]["currentTime"] ) ) ) * 1000000L;
+                        msg_Dbg( p_module, "Playback pending with an offset of %" PRId64, m_chromecast_start_time);
+                    }
+                    m_time_playback_started = VLC_TS_INVALID;
+                    break;
+
                 case RECEIVER_PLAYING:
                     /* TODO reset demux PCR ? */
+                    if (unlikely(m_chromecast_start_time == VLC_TS_INVALID)) {
+                        msg_Warn( p_module, "start playing without buffering" );
+                        m_chromecast_start_time = (1 + mtime_t( double( status[0]["currentTime"] ) ) ) * 1000000L;
+                    }
                     setPlayerStatus(CMD_PLAYBACK_SENT);
                     m_time_playback_started = mdate();
 #ifndef NDEBUG
-                    msg_Dbg( p_module, "Playback started now:%" PRId64 " i_ts_local_start:%" PRId64, m_time_playback_started, i_ts_local_start);
+                    msg_Dbg( p_module, "Playback started with an offset of %" PRId64 " now:%" PRId64 " i_ts_local_start:%" PRId64, m_chromecast_start_time, m_time_playback_started, i_ts_local_start);
 #endif
                     break;
 
                 case RECEIVER_PAUSED:
+                    m_chromecast_start_time = (1 + mtime_t( double( status[0]["currentTime"] ) ) ) * 1000000L;
 #ifndef NDEBUG
-                    msg_Dbg( p_module, "Playback paused date_play_start:%" PRId64, m_time_playback_started);
+                    msg_Dbg( p_module, "Playback paused with an offset of %" PRId64 " date_play_start:%" PRId64, m_chromecast_start_time, m_time_playback_started);
 #endif
 
                     if ( m_time_playback_started != VLC_TS_INVALID && oldPlayerState == RECEIVER_PLAYING )
@@ -591,6 +620,12 @@ void intf_sys_t::processMessage(const castchannel::CastMessage &msg)
                 }
             }
 
+            if (receiverState == RECEIVER_BUFFERING && m_seek_request_time != VLC_TS_INVALID)
+            {
+                msg_Dbg( p_module, "Chromecast seeking possibly done");
+                vlc_cond_signal( &seekCommandCond );
+            }
+
             if ( cmd_status != CMD_LOAD_SENT && receiverState == RECEIVER_IDLE && has_input )
             {
                 msg_Dbg( p_module, "the device missed the LOAD command");
@@ -633,6 +668,9 @@ void intf_sys_t::processMessage(const castchannel::CastMessage &msg)
             setHasInput( false );
             vlc_mutex_locker locker(&lock);
             setConnectionStatus(CHROMECAST_CONNECTION_DEAD);
+            // make sure we unblock the demuxer
+            m_seek_request_time = VLC_TS_INVALID;
+            vlc_cond_signal(&seekCommandCond);
         }
         else
         {
@@ -915,7 +953,7 @@ bool intf_sys_t::handleMessages()
     if ( requested_seek.exchange(false) && !mediaSessionId.empty() )
     {
         char current_time[32];
-        mtime_t m_seek_request_time = mdate() + SEEK_FORWARD_OFFSET;
+        m_seek_request_time = mdate() + SEEK_FORWARD_OFFSET;
         if( snprintf( current_time, sizeof(current_time), "%.3f", double( m_seek_request_time ) / 1000000.0 ) >= (int)sizeof(current_time) )
         {
             msg_Err( p_module, "snprintf() truncated string for mediaSessionId" );
@@ -971,8 +1009,11 @@ void intf_sys_t::requestPlayerStop()
     notifySendRequest();
 }
 
-void intf_sys_t::requestPlayerSeek()
+void intf_sys_t::requestPlayerSeek(mtime_t pos)
 {
+    vlc_mutex_locker locker(&lock);
+    if ( pos != VLC_TS_INVALID )
+        i_ts_local_start = pos;
     requested_seek = true;
     notifySendRequest();
 }
@@ -987,6 +1028,28 @@ void intf_sys_t::waitAppStarted()
     vlc_cleanup_pop();
 }
 
+void intf_sys_t::waitSeekDone()
+{
+    vlc_mutex_locker locker(&lock);
+    if ( m_seek_request_time != VLC_TS_INVALID )
+    {
+        mutex_cleanup_push(&lock);
+        while ( m_chromecast_start_time < m_seek_request_time &&
+                conn_status == CHROMECAST_APP_STARTED )
+        {
+#ifndef NDEBUG
+            msg_Dbg( p_module, "waiting for Chromecast seek" );
+#endif
+            vlc_cond_wait(&seekCommandCond, &lock);
+#ifndef NDEBUG
+            msg_Dbg( p_module, "finished waiting for Chromecast seek" );
+#endif
+        }
+        vlc_cleanup_pop();
+        m_seek_request_time = VLC_TS_INVALID;
+    }
+}
+
 mtime_t intf_sys_t::get_time(void *pt)
 {
     intf_sys_t *p_this = reinterpret_cast<intf_sys_t*>(pt);
@@ -1012,3 +1075,15 @@ void intf_sys_t::wait_app_started(void *pt)
     intf_sys_t *p_this = reinterpret_cast<intf_sys_t*>(pt);
     p_this->waitAppStarted();
 }
+
+void intf_sys_t::request_seek(void *pt, mtime_t pos)
+{
+    intf_sys_t *p_this = reinterpret_cast<intf_sys_t*>(pt);
+    p_this->requestPlayerSeek(pos);
+}
+
+void intf_sys_t::wait_seek_done(void *pt)
+{
+    intf_sys_t *p_this = reinterpret_cast<intf_sys_t*>(pt);
+    p_this->waitSeekDone();
+}
diff --git a/modules/stream_out/chromecast/chromecast_demux.cpp b/modules/stream_out/chromecast/chromecast_demux.cpp
index d8b7b41..1dc4ac3 100644
--- a/modules/stream_out/chromecast/chromecast_demux.cpp
+++ b/modules/stream_out/chromecast/chromecast_demux.cpp
@@ -41,7 +41,10 @@ struct demux_sys_t
     demux_sys_t(demux_filter_t * const demux, chromecast_common * const renderer)
         :p_demux(demux)
         ,p_renderer(renderer)
+        ,i_length(-1)
         ,demuxReady(false)
+        ,canSeek(false)
+        ,m_seektime( VLC_TS_INVALID )
     {
     }
 
@@ -63,8 +66,33 @@ struct demux_sys_t
         return p_renderer->pf_get_position( p_renderer->p_opaque );
     }
 
+    void setCanSeek( bool canSeek )
+    {
+        this->canSeek = canSeek;
+    }
+
+    bool seekTo( double pos )
+    {
+        if (i_length == -1)
+            return false;
+        return seekTo( mtime_t( i_length * pos ) );
+    }
+
+    bool seekTo( mtime_t i_pos )
+    {
+        if ( !canSeek )
+            return false;
+
+        /* seeking will be handled with the Chromecast */
+        m_seektime = i_pos;
+        p_renderer->pf_request_seek( p_renderer->p_opaque, i_pos );
+
+        return true;
+    }
+
     void setLength( mtime_t length )
     {
+        this->i_length = length;
         p_renderer->pf_set_length( p_renderer->p_opaque, length );
     }
 
@@ -78,13 +106,25 @@ struct demux_sys_t
             msg_Dbg(p_demux, "ready to demux");
         }
 
+        /* hold the data while seeking */
+        /* wait until the device is buffering for data after the seek command */
+        if ( m_seektime != VLC_TS_INVALID )
+        {
+            p_renderer->pf_wait_seek_done( p_renderer->p_opaque );
+            m_seektime = VLC_TS_INVALID;
+        }
+
         return demux_FilterDemuxNext( p_demux );
     }
 
 protected:
     demux_filter_t     * const p_demux;
     chromecast_common  * const p_renderer;
+    mtime_t       i_length;
     bool          demuxReady;
+    bool          canSeek;
+    /* seek time kept while waiting for the chromecast to "seek" */
+    mtime_t       m_seektime;
 };
 
 static int Demux( demux_filter_t *p_demux_filter )
@@ -120,6 +160,63 @@ static int Control( demux_filter_t *p_demux_filter, int i_query, va_list args)
         va_end( ap );
         return ret;
     }
+
+    case DEMUX_CAN_SEEK:
+    {
+        int ret;
+        va_list ap;
+
+        va_copy( ap, args );
+        ret = demux_vaFilterControlNext( p_demux_filter, i_query, args );
+        if( ret == VLC_SUCCESS )
+            p_sys->setCanSeek( *va_arg( ap, bool* ) );
+        va_end( ap );
+        return ret;
+    }
+
+    case DEMUX_SET_POSITION:
+    {
+        va_list ap;
+
+        va_copy( ap, args );
+        double pos = va_arg( ap, double );
+        va_end( ap );
+
+        if ( p_sys->getPlaybackTime() == VLC_TS_INVALID )
+        {
+            msg_Dbg( p_demux_filter, "internal seek to %f when the playback didn't start", pos );
+            break; // seek before device started, likely on-the-fly restart
+        }
+
+        if ( !p_sys->seekTo( pos ) )
+        {
+            msg_Err( p_demux_filter, "failed to seek to %f", pos );
+            return VLC_EGENERIC;
+        }
+        break;
+    }
+
+    case DEMUX_SET_TIME:
+    {
+        va_list ap;
+
+        va_copy( ap, args );
+        mtime_t pos = va_arg( ap, mtime_t );
+        va_end( ap );
+
+        if ( p_sys->getPlaybackTime() == VLC_TS_INVALID )
+        {
+            msg_Dbg( p_demux_filter, "internal seek to %" PRId64 " when the playback didn't start", pos );
+            break; // seek before device started, likely on-the-fly restart
+        }
+
+        if ( !p_sys->seekTo( pos ) )
+        {
+            msg_Err( p_demux_filter, "failed to seek to time %" PRId64, pos );
+            return VLC_EGENERIC;
+        }
+        break;
+    }
     }
 
     return demux_vaFilterControlNext( p_demux_filter, i_query, args );
-- 
2.7.0



More information about the vlc-devel mailing list