Commit e657ddcb authored by Steve Lhomme's avatar Steve Lhomme Committed by Jean-Baptiste Kempf

chromecast: fix the time displayed after seeking

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.
Signed-off-by: Jean-Baptiste Kempf's avatarJean-Baptiste Kempf <jb@videolan.org>
parent c3348c4b
......@@ -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 );
}
......
......@@ -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 */
......@@ -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
......
......@@ -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();
}
......@@ -41,7 +41,10 @@ struct demux_sys_t
demux_sys_t(demux_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_Demux( p_demux->p_next );
}
protected:
demux_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_t *p_demux_filter )
......@@ -120,6 +160,63 @@ static int Control( demux_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_vaControl( p_demux_filter->p_next, 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_vaControl( p_demux_filter->p_next, i_query, args );
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment