1 /*****************************************************************************
2 * chromecast_ctrl.cpp: Chromecast module for vlc
3 *****************************************************************************
4 * Copyright © 2014-2015 VideoLAN
6 * Authors: Adrien Maglo <magsoft@videolan.org>
7 * Jean-Baptiste Kempf <jb@videolan.org>
8 * Steve Lhomme <robux4@videolabs.io>
9 * Hugo Beauzée-Luyssen <hugo@beauzee.fr>
11 * This program is free software; you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation; either version 2.1 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 *****************************************************************************/
26 /*****************************************************************************
28 *****************************************************************************/
34 #include "chromecast.h"
39 #include <vlc_stream.h>
41 #include "../../misc/webservices/json.h"
43 /* deadline regarding pings sent from receiver */
44 #define PING_WAIT_TIME 6000
45 #define PING_WAIT_RETRIES 1
47 static int httpd_file_fill_cb( httpd_file_sys_t
*data
, httpd_file_t
*http_file
,
48 uint8_t *psz_request
, uint8_t **pp_data
, int *pi_data
);
50 static const char* StateToStr( States s
)
55 return "Authenticating";
83 vlc_assert_unreachable();
86 /*****************************************************************************
87 * intf_sys_t: class definition
88 *****************************************************************************/
89 intf_sys_t::intf_sys_t(vlc_object_t
* const p_this
, int port
, std::string device_addr
,
90 int device_port
, vlc_interrupt_t
*p_interrupt
, httpd_host_t
*httpd_host
)
92 , m_streaming_port(port
)
93 , m_mediaSessionId( 0 )
94 , m_communication( p_this
, device_addr
.c_str(), device_port
)
95 , m_state( Authenticating
)
96 , m_request_stop( false )
97 , m_request_load( false )
101 , m_ctl_thread_interrupt(p_interrupt
)
102 , m_httpd_host(httpd_host
)
104 , m_time_playback_started( VLC_TS_INVALID
)
105 , m_ts_local_start( VLC_TS_INVALID
)
106 , m_ts_seek( VLC_TS_INVALID
)
107 , m_length( VLC_TS_INVALID
)
108 , m_pingRetriesLeft( PING_WAIT_RETRIES
)
110 vlc_mutex_init(&m_lock
);
111 vlc_cond_init( &m_stateChangedCond
);
112 vlc_cond_init( &m_pace_cond
);
114 const char *psz_artmime
= "application/octet-stream";
115 m_httpd_file
= httpd_FileNew( m_httpd_host
, "/art", psz_artmime
, NULL
, NULL
,
116 httpd_file_fill_cb
, (httpd_file_sys_t
*) this );
118 std::stringstream ss
;
119 ss
<< "http://" << m_communication
.getServerIp() << ":" << port
;
120 m_art_http_ip
= ss
.str();
122 m_common
.p_opaque
= this;
123 m_common
.pf_get_position
= get_position
;
124 m_common
.pf_get_time
= get_time
;
125 m_common
.pf_set_length
= set_length
;
126 m_common
.pf_set_initial_time
= set_initial_time
;
127 m_common
.pf_pace
= pace
;
128 m_common
.pf_set_pause_state
= set_pause_state
;
129 m_common
.pf_set_meta
= set_meta
;
131 assert( var_Type( m_module
->obj
.parent
->obj
.parent
, CC_SHARED_VAR_NAME
) == 0 );
132 if (var_Create( m_module
->obj
.parent
->obj
.parent
, CC_SHARED_VAR_NAME
, VLC_VAR_ADDRESS
) == VLC_SUCCESS
)
133 var_SetAddress( m_module
->obj
.parent
->obj
.parent
, CC_SHARED_VAR_NAME
, &m_common
);
135 // Start the Chromecast event thread.
136 if (vlc_clone(&m_chromecastThread
, ChromecastThread
, this,
137 VLC_THREAD_PRIORITY_LOW
))
138 throw std::runtime_error( "error creating cc thread" );
141 intf_sys_t::~intf_sys_t()
143 var_Destroy( m_module
->obj
.parent
->obj
.parent
, CC_SHARED_VAR_NAME
);
145 vlc_mutex_lock(&m_lock
);
155 // Generate the close messages.
156 m_communication
.msgReceiverClose( m_appTransportId
);
161 m_communication
.msgReceiverClose(DEFAULT_CHOMECAST_RECEIVER
);
166 vlc_mutex_unlock(&m_lock
);
168 vlc_interrupt_kill( m_ctl_thread_interrupt
);
170 vlc_join(m_chromecastThread
, NULL
);
172 vlc_interrupt_destroy( m_ctl_thread_interrupt
);
175 vlc_meta_Delete(m_meta
);
178 httpd_FileDelete( m_httpd_file
);
182 vlc_cond_destroy(&m_stateChangedCond
);
183 vlc_cond_destroy(&m_pace_cond
);
184 vlc_mutex_destroy(&m_lock
);
187 int intf_sys_t::httpd_file_fill( uint8_t *psz_request
, uint8_t **pp_data
, int *pi_data
)
191 vlc_mutex_lock( &m_lock
);
194 vlc_mutex_unlock( &m_lock
);
197 char *psz_art
= strdup( m_art_url
);
198 vlc_mutex_unlock( &m_lock
);
200 stream_t
*s
= vlc_stream_NewURL( m_module
, psz_art
);
206 if( vlc_stream_GetSize( s
, &size
) != VLC_SUCCESS
207 || size
> INT64_C( 10000000 ) )
209 msg_Warn( m_module
, "art stream is too big or invalid" );
210 vlc_stream_Delete( s
);
214 *pp_data
= (uint8_t *)malloc( size
);
217 vlc_stream_Delete( s
);
221 ssize_t read
= vlc_stream_Read( s
, *pp_data
, size
);
222 vlc_stream_Delete( s
);
224 if( read
< 0 || (size_t)read
!= size
)
235 static int httpd_file_fill_cb( httpd_file_sys_t
*data
, httpd_file_t
*http_file
,
236 uint8_t *psz_request
, uint8_t **pp_data
, int *pi_data
)
239 intf_sys_t
*p_sys
= static_cast<intf_sys_t
*>((void *)data
);
240 return p_sys
->httpd_file_fill( psz_request
, pp_data
, pi_data
);
243 void intf_sys_t::prepareHttpArtwork()
248 const char *psz_art
= m_meta
? vlc_meta_Get( m_meta
, vlc_meta_ArtworkURL
) : NULL
;
249 /* Abort if there is no art or if the art is already served */
250 if( !psz_art
|| strncmp( psz_art
, "http", 4) == 0 )
254 m_art_url
= strdup( psz_art
);
256 std::stringstream ss
;
257 ss
<< m_art_http_ip
<< "/art";
258 vlc_meta_Set( m_meta
, vlc_meta_ArtworkURL
, ss
.str().c_str() );
261 void intf_sys_t::tryLoad()
263 if( !m_request_load
)
266 if ( !isStateReady() )
268 if ( m_state
== Dead
)
270 msg_Warn( m_module
, "no Chromecast hook possible");
271 m_request_load
= false;
273 else if( m_state
== Connected
)
275 msg_Dbg( m_module
, "Starting the media receiver application" );
276 // Don't use setState as we don't want to signal the condition in this case.
278 m_communication
.msgReceiverLaunchApp();
283 m_request_load
= false;
285 // We should now be in the ready state, and therefor have a valid transportId
286 assert( m_appTransportId
.empty() == false );
287 // Reset the mediaSessionID to allow the new session to become the current one.
288 // we cannot start a new load when the last one is still processing
289 m_communication
.msgPlayerLoad( m_appTransportId
, m_streaming_port
, m_mime
, m_meta
);
293 void intf_sys_t::setHasInput( const std::string mime_type
)
295 vlc_mutex_locker
locker(&m_lock
);
296 msg_Dbg( m_module
, "Loading content" );
298 m_request_stop
= false;
300 this->m_mime
= mime_type
;
302 /* new input: clear message queue */
303 std::queue
<QueueableMessages
> empty
;
304 std::swap(m_msgQueue
, empty
);
306 prepareHttpArtwork();
309 m_mediaSessionId
= 0;
310 m_request_load
= true;
313 vlc_cond_signal( &m_stateChangedCond
);
316 bool intf_sys_t::isStatePlaying() const
331 bool intf_sys_t::isStateReady() const
347 void intf_sys_t::setPacing(bool do_pace
)
349 vlc_mutex_lock( &m_lock
);
350 if( m_pace
== do_pace
)
352 vlc_mutex_unlock( &m_lock
);
356 vlc_mutex_unlock( &m_lock
);
357 vlc_cond_signal( &m_pace_cond
);
360 static void interrupt_wake_up_cb( void *data
)
362 intf_sys_t
*p_sys
= static_cast<intf_sys_t
*>((void *)data
);
363 p_sys
->interrupt_wake_up();
366 void intf_sys_t::interrupt_wake_up()
368 vlc_mutex_locker
locker( &m_lock
);
369 m_interrupted
= true;
370 vlc_cond_signal( &m_pace_cond
);
373 void intf_sys_t::pace()
375 vlc_mutex_locker
locker(&m_lock
);
379 m_interrupted
= false;
380 vlc_interrupt_register( interrupt_wake_up_cb
, this );
382 while( m_pace
&& !m_interrupted
)
383 vlc_cond_wait( &m_pace_cond
, &m_lock
);
384 vlc_interrupt_unregister();
388 * @brief Process a message received from the Chromecast
389 * @param msg the CastMessage to process
390 * @return 0 if the message has been successfuly processed else -1
392 void intf_sys_t::processMessage(const castchannel::CastMessage
&msg
)
394 const std::string
& namespace_
= msg
.namespace_();
397 msg_Dbg( m_module
, "processMessage: %s->%s %s", namespace_
.c_str(), msg
.destination_id().c_str(), msg
.payload_utf8().c_str());
400 if (namespace_
== NAMESPACE_DEVICEAUTH
)
401 processAuthMessage( msg
);
402 else if (namespace_
== NAMESPACE_HEARTBEAT
)
403 processHeartBeatMessage( msg
);
404 else if (namespace_
== NAMESPACE_RECEIVER
)
405 processReceiverMessage( msg
);
406 else if (namespace_
== NAMESPACE_MEDIA
)
407 processMediaMessage( msg
);
408 else if (namespace_
== NAMESPACE_CONNECTION
)
409 processConnectionMessage( msg
);
412 msg_Err( m_module
, "Unknown namespace: %s", msg
.namespace_().c_str());
416 void intf_sys_t::queueMessage( QueueableMessages msg
)
418 // Assume lock is held by the called
419 m_msgQueue
.push( msg
);
420 vlc_interrupt_raise( m_ctl_thread_interrupt
);
425 /*****************************************************************************
427 *****************************************************************************/
428 void* intf_sys_t::ChromecastThread(void* p_data
)
430 intf_sys_t
*p_sys
= static_cast<intf_sys_t
*>(p_data
);
435 void intf_sys_t::mainLoop()
438 vlc_interrupt_set( m_ctl_thread_interrupt
);
440 // State was already initialized as Authenticating
441 m_communication
.msgAuth();
443 while ( !vlc_killed() )
445 if ( !handleMessages() )
447 // Reset the interrupt state to avoid commands not being sent (since
448 // the context is still flagged as interrupted)
449 vlc_interrupt_unregister();
450 vlc_mutex_locker
lock( &m_lock
);
451 while ( m_msgQueue
.empty() == false )
453 QueueableMessages msg
= m_msgQueue
.front();
457 if( isStatePlaying() )
459 if ( m_mediaSessionId
== 0 )
460 m_request_stop
= true;
463 m_communication
.msgPlayerStop( m_appTransportId
, m_mediaSessionId
);
464 setState( Stopping
);
470 if( !isStatePlaying() || m_mediaSessionId
== 0 || m_ts_seek
== VLC_TS_INVALID
)
472 char current_time
[32];
473 if( snprintf( current_time
, sizeof(current_time
), "%.3f",
474 double( m_ts_seek
) / 1000000.0 ) >= (int)sizeof(current_time
) )
476 msg_Err( m_module
, "snprintf() truncated string for mediaSessionId" );
477 current_time
[sizeof(current_time
) - 1] = '\0';
479 m_ts_seek
= VLC_TS_INVALID
;
480 /* send a fake time to seek to, to make sure the device flushes its buffers */
481 m_communication
.msgPlayerSeek( m_appTransportId
, m_mediaSessionId
, current_time
);
491 void intf_sys_t::processAuthMessage( const castchannel::CastMessage
& msg
)
493 castchannel::DeviceAuthMessage authMessage
;
494 if ( authMessage
.ParseFromString(msg
.payload_binary()) == false )
496 msg_Warn( m_module
, "Failed to parse the payload" );
500 if (authMessage
.has_error())
502 msg_Err( m_module
, "Authentification error: %d", authMessage
.error().error_type());
504 else if (!authMessage
.has_response())
506 msg_Err( m_module
, "Authentification message has no response field");
510 vlc_mutex_locker
locker(&m_lock
);
511 setState( Connecting
);
512 m_communication
.msgConnect(DEFAULT_CHOMECAST_RECEIVER
);
513 m_communication
.msgReceiverGetStatus();
517 void intf_sys_t::processHeartBeatMessage( const castchannel::CastMessage
& msg
)
519 json_value
*p_data
= json_parse(msg
.payload_utf8().c_str());
520 std::string
type((*p_data
)["type"]);
524 msg_Dbg( m_module
, "PING received from the Chromecast");
525 m_communication
.msgPong();
527 else if (type
== "PONG")
529 msg_Dbg( m_module
, "PONG received from the Chromecast");
530 m_pingRetriesLeft
= PING_WAIT_RETRIES
;
534 msg_Warn( m_module
, "Heartbeat command not supported: %s", type
.c_str());
537 json_value_free(p_data
);
540 void intf_sys_t::processReceiverMessage( const castchannel::CastMessage
& msg
)
542 json_value
*p_data
= json_parse(msg
.payload_utf8().c_str());
543 std::string
type((*p_data
)["type"]);
545 if (type
== "RECEIVER_STATUS")
547 json_value applications
= (*p_data
)["status"]["applications"];
548 const json_value
*p_app
= NULL
;
550 for (unsigned i
= 0; i
< applications
.u
.array
.length
; ++i
)
552 if ( strcmp( applications
[i
]["appId"], APP_ID
) == 0 )
554 if ( (const char*)applications
[i
]["transportId"] != NULL
)
556 p_app
= &applications
[i
];
562 vlc_mutex_locker
locker(&m_lock
);
567 // We were connecting & fetching the current status.
568 // The media receiver app is running, we are ready to proceed
571 msg_Dbg( m_module
, "Media receiver application was already running" );
572 m_appTransportId
= (const char*)(*p_app
)["transportId"];
573 m_communication
.msgConnect( m_appTransportId
);
578 setState( Connected
);
582 // We already asked for the media receiver application to start
585 msg_Dbg( m_module
, "Media receiver application has been started." );
586 m_appTransportId
= (const char*)(*p_app
)["transportId"];
587 m_communication
.msgConnect( m_appTransportId
);
600 msg_Warn( m_module
, "Media receiver application got closed." );
601 setState( Connected
);
602 m_appTransportId
= "";
603 m_mediaSessionId
= 0;
607 // We might receive a RECEIVER_STATUS while being connected, when pinging/asking the status
610 // else: fall through and warn
612 msg_Warn( m_module
, "Unexpected RECEIVER_STATUS with state %s. "
613 "Checking media status",
614 StateToStr( m_state
) );
615 // This is likely because the chromecast refused the playback, but
616 // let's check by explicitely probing the media status
617 m_communication
.msgPlayerGetStatus( m_appTransportId
);
621 else if (type
== "LAUNCH_ERROR")
623 json_value reason
= (*p_data
)["reason"];
624 msg_Err( m_module
, "Failed to start the MediaPlayer: %s",
625 (const char *)reason
);
626 vlc_mutex_locker
locker(&m_lock
);
627 m_appTransportId
= "";
628 m_mediaSessionId
= 0;
633 msg_Warn( m_module
, "Receiver command not supported: %s",
634 msg
.payload_utf8().c_str());
637 json_value_free(p_data
);
640 void intf_sys_t::processMediaMessage( const castchannel::CastMessage
& msg
)
642 json_value
*p_data
= json_parse(msg
.payload_utf8().c_str());
643 std::string
type((*p_data
)["type"]);
645 if (type
== "MEDIA_STATUS")
647 json_value status
= (*p_data
)["status"];
649 int64_t sessionId
= (json_int_t
) status
[0]["mediaSessionId"];
650 if (m_mediaSessionId
!= sessionId
&& m_mediaSessionId
!= 0 )
652 msg_Dbg( m_module
, "Ignoring message for a different media session" );
653 json_value_free(p_data
);
657 msg_Dbg( m_module
, "Player state: %s sessionId: %" PRId64
,
658 status
[0]["playerState"].operator const char *(),
661 std::string newPlayerState
= (const char*)status
[0]["playerState"];
662 std::string idleReason
= (const char*)status
[0]["idleReason"];
664 vlc_mutex_locker
locker( &m_lock
);
666 if (newPlayerState
== "IDLE" || newPlayerState
.empty() == true )
668 /* Idle state is expected when the media receiver application is
669 * started. In case the state is still Buffering, it denotes an error.
670 * In most case, we'd receive a RECEIVER_STATUS message, which causes
671 * use to ask for the MEDIA_STATUS before assuming an error occured.
672 * If the chromecast silently gave up on playing our stream, we also
673 * might have an empty status array.
674 * If the media load indeed failed, we need to try another
675 * transcode/remux configuration, or give up.
676 * In case we are now loading, we might also receive an INTERRUPTED
677 * state for the previous session, which we wouldn't ignore earlier
678 * since our mediaSessionID was reset to 0.
679 * In this case, don't assume we're being taken over, as we are
680 * actually doing the take over.
682 if ( m_state
!= Ready
&& m_state
!= LoadFailed
&& m_state
!= Loading
)
684 // The playback stopped
685 m_time_playback_started
= VLC_TS_INVALID
;
686 if ( idleReason
== "INTERRUPTED" )
688 setState( TakenOver
);
689 // Do not reset the mediaSessionId to ensure we refuse all
690 // other MEDIA_STATUS from the new session.
692 else if ( m_state
== Buffering
)
693 setState( LoadFailed
);
696 if (idleReason
== "FINISHED")
704 if ( m_mediaSessionId
== 0 )
706 m_mediaSessionId
= sessionId
;
707 msg_Dbg( m_module
, "New mediaSessionId: %" PRId64
, m_mediaSessionId
);
712 m_request_stop
= false;
713 m_communication
.msgPlayerStop( m_appTransportId
, m_mediaSessionId
);
714 setState( Stopping
);
716 else if (newPlayerState
== "PLAYING")
718 msg_Dbg( m_module
, "Playback started now:%" PRId64
" i_ts_local_start:%" PRId64
,
719 m_time_playback_started
, m_ts_local_start
);
720 if ( m_state
!= Playing
)
722 /* TODO reset demux PCR ? */
723 m_time_playback_started
= mdate();
727 else if (newPlayerState
== "BUFFERING")
729 if ( m_state
!= Buffering
)
731 /* EOF when state goes from Playing to Buffering. There can
732 * be a lot of false positives (when seeking or when the cc
733 * request more input) but this state is fetched only when
734 * the input has reached EOF. */
736 m_time_playback_started
= VLC_TS_INVALID
;
737 setState( Buffering
);
740 else if (newPlayerState
== "PAUSED")
742 if ( m_state
!= Paused
)
745 msg_Dbg( m_module
, "Playback paused: date_play_start: %" PRId64
, m_time_playback_started
);
748 if ( m_time_playback_started
!= VLC_TS_INVALID
&& m_state
== Playing
)
750 /* this is a pause generated remotely, adjust the playback time */
751 m_ts_local_start
+= mdate() - m_time_playback_started
;
753 msg_Dbg( m_module
, "updated i_ts_local_start:%" PRId64
, m_ts_local_start
);
756 m_time_playback_started
= VLC_TS_INVALID
;
760 else if ( newPlayerState
== "LOADING" )
762 if ( m_state
!= Loading
)
764 msg_Dbg( m_module
, "Chromecast is loading the stream" );
769 msg_Warn( m_module
, "Unknown Chromecast MEDIA_STATUS state %s", newPlayerState
.c_str());
772 else if (type
== "LOAD_FAILED")
774 msg_Err( m_module
, "Media load failed");
775 vlc_mutex_locker
locker(&m_lock
);
776 setState( LoadFailed
);
778 else if (type
== "LOAD_CANCELLED")
780 msg_Dbg( m_module
, "LOAD canceled by another command");
782 else if (type
== "INVALID_REQUEST")
784 msg_Dbg( m_module
, "We sent an invalid request reason:%s", (const char*)(*p_data
)["reason"] );
788 msg_Warn( m_module
, "Media command not supported: %s",
789 msg
.payload_utf8().c_str());
792 json_value_free(p_data
);
795 void intf_sys_t::processConnectionMessage( const castchannel::CastMessage
& msg
)
797 json_value
*p_data
= json_parse(msg
.payload_utf8().c_str());
798 std::string
type((*p_data
)["type"]);
799 json_value_free(p_data
);
801 if ( type
== "CLOSE" )
803 // Close message indicates an application is being closed, not the connection.
804 // From this point on, we need to relaunch the media receiver app
805 vlc_mutex_locker
locker(&m_lock
);
806 m_appTransportId
= "";
807 m_mediaSessionId
= 0;
808 setState( Connected
);
812 msg_Warn( m_module
, "Connection command not supported: %s",
817 bool intf_sys_t::handleMessages()
819 uint8_t p_packet
[PACKET_MAX_LEN
];
820 size_t i_payloadSize
= 0;
821 size_t i_received
= 0;
822 bool b_timeout
= false;
823 mtime_t i_begin_time
= mdate();
826 * +------------------------------------+------------------------------+
827 * | Payload size (uint32_t big endian) | Payload data |
828 * +------------------------------------+------------------------------+
832 // If we haven't received the payload size yet, let's wait for it. Otherwise, we know
833 // how many bytes to read
834 ssize_t i_ret
= m_communication
.receive( p_packet
+ i_received
,
835 i_payloadSize
+ PACKET_HEADER_LEN
- i_received
,
836 PING_WAIT_TIME
- ( mdate() - i_begin_time
) / CLOCK_FREQ
,
840 if ( errno
== EINTR
)
842 // An error occured, we give up
843 msg_Err( m_module
, "The connection to the Chromecast died (receiving).");
844 vlc_mutex_locker
locker(&m_lock
);
848 else if ( b_timeout
== true )
850 // If no commands were queued to be sent, we timed out. Let's ping the chromecast
851 if ( m_pingRetriesLeft
== 0 )
853 vlc_mutex_locker
locker(&m_lock
);
855 msg_Warn( m_module
, "No PING response from the chromecast" );
859 m_communication
.msgPing();
860 m_communication
.msgReceiverGetStatus();
863 assert( i_ret
!= 0 );
865 if ( i_payloadSize
== 0 )
867 i_payloadSize
= U32_AT( p_packet
);
868 if ( i_payloadSize
> PACKET_MAX_LEN
- PACKET_HEADER_LEN
)
870 msg_Err( m_module
, "Payload size is too long: dropping connection" );
871 vlc_mutex_locker
locker(&m_lock
);
877 assert( i_received
<= i_payloadSize
+ PACKET_HEADER_LEN
);
878 if ( i_received
== i_payloadSize
+ PACKET_HEADER_LEN
)
881 castchannel::CastMessage msg
;
882 msg
.ParseFromArray(p_packet
+ PACKET_HEADER_LEN
, i_payloadSize
);
887 void intf_sys_t::requestPlayerStop()
889 vlc_mutex_locker
locker(&m_lock
);
891 m_request_load
= false;
892 m_ts_seek
= VLC_TS_INVALID
;
894 if( !isStatePlaying() )
897 queueMessage( Stop
);
900 States
intf_sys_t::state() const
902 vlc_mutex_locker
locker( &m_lock
);
906 void intf_sys_t::requestPlayerSeek(mtime_t pos
)
908 vlc_mutex_locker
locker(&m_lock
);
909 if( !isStatePlaying() || m_mediaSessionId
== 0 )
912 queueMessage( Seek
);
915 void intf_sys_t::setOnSeekDoneCb(on_seek_done_itf on_seek_done
, void *on_seek_done_data
)
917 vlc_mutex_locker
locker(&m_lock
);
918 m_on_seek_done
= on_seek_done
;
919 m_on_seek_done_data
= on_seek_done_data
;
922 void intf_sys_t::setPauseState(bool paused
)
924 msg_Dbg( m_module
, "%s state", paused
? "paused" : "playing" );
925 vlc_mutex_locker
locker( &m_lock
);
928 if ( m_mediaSessionId
!= 0 )
930 m_communication
.msgPlayerPlay( m_appTransportId
, m_mediaSessionId
);
935 if ( m_mediaSessionId
!= 0 && m_state
!= Paused
)
937 m_communication
.msgPlayerPause( m_appTransportId
, m_mediaSessionId
);
942 bool intf_sys_t::isFinishedPlaying()
944 vlc_mutex_locker
locker(&m_lock
);
945 return m_state
== LoadFailed
|| m_state
== Dead
|| m_eof
;
948 void intf_sys_t::setMeta(vlc_meta_t
*p_meta
)
950 vlc_mutex_locker
locker(&m_lock
);
952 vlc_meta_Delete(m_meta
);
956 mtime_t
intf_sys_t::getPlaybackTimestamp() const
961 return ( mdate() - m_time_playback_started
) + m_ts_local_start
;
962 #ifdef CHROMECAST_VERBOSE
964 msg_Dbg(m_module
, "receiver idle using buffering time %" PRId64
, m_ts_local_start
);
967 msg_Dbg(m_module
, "receiver buffering using buffering time %" PRId64
, m_ts_local_start
);
970 msg_Dbg(m_module
, "receiver paused using buffering time %" PRId64
, m_ts_local_start
);
976 return m_ts_local_start
;
979 double intf_sys_t::getPlaybackPosition() const
982 return (double) getPlaybackTimestamp() / (double)( m_length
);
986 void intf_sys_t::setInitialTime(mtime_t time
)
989 m_ts_local_start
= time
;
992 void intf_sys_t::setState( States state
)
994 if ( m_state
!= state
)
997 msg_Dbg( m_module
, "Switching from state %s to %s", StateToStr( m_state
), StateToStr( state
) );
999 if (state
== Seeking
)
1000 if (m_on_seek_done
!= NULL
)
1001 m_on_seek_done(m_on_seek_done_data
);
1013 vlc_cond_signal( &m_stateChangedCond
);
1017 mtime_t
intf_sys_t::get_time(void *pt
)
1019 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1020 vlc_mutex_locker
locker( &p_this
->m_lock
);
1021 return p_this
->getPlaybackTimestamp();
1024 double intf_sys_t::get_position(void *pt
)
1026 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1027 vlc_mutex_locker
locker( &p_this
->m_lock
);
1028 return p_this
->getPlaybackPosition();
1031 void intf_sys_t::set_initial_time(void *pt
, mtime_t time
)
1033 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1034 vlc_mutex_locker
locker( &p_this
->m_lock
);
1035 return p_this
->setInitialTime( time
);
1038 void intf_sys_t::set_length(void *pt
, mtime_t length
)
1040 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1041 p_this
->m_length
= length
;
1044 void intf_sys_t::pace(void *pt
)
1046 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1050 void intf_sys_t::set_pause_state(void *pt
, bool paused
)
1052 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1053 p_this
->setPauseState( paused
);
1056 void intf_sys_t::set_meta(void *pt
, vlc_meta_t
*p_meta
)
1058 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1059 p_this
->setMeta( p_meta
);