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"
40 #include <vlc_stream.h>
42 #include "../../misc/webservices/json.h"
44 /* deadline regarding pings sent from receiver */
45 #define PING_WAIT_TIME 6000
46 #define PING_WAIT_RETRIES 1
48 static int httpd_file_fill_cb( httpd_file_sys_t
*data
, httpd_file_t
*http_file
,
49 uint8_t *psz_request
, uint8_t **pp_data
, int *pi_data
);
51 static const char* StateToStr( States s
)
56 return "Authenticating";
84 vlc_assert_unreachable();
87 /*****************************************************************************
88 * intf_sys_t: class definition
89 *****************************************************************************/
90 intf_sys_t::intf_sys_t(vlc_object_t
* const p_this
, int port
, std::string device_addr
,
91 int device_port
, httpd_host_t
*httpd_host
)
93 , m_streaming_port(port
)
94 , m_mediaSessionId( 0 )
95 , m_on_seek_done( NULL
)
96 , m_on_seek_done_data( NULL
)
97 , m_on_paused_changed( NULL
)
98 , m_on_paused_changed_data( NULL
)
99 , m_communication( p_this
, device_addr
.c_str(), device_port
)
100 , m_state( Authenticating
)
101 , m_played_once( false )
102 , m_request_stop( false )
103 , m_request_load( false )
107 , m_httpd_host(httpd_host
)
111 , m_time_playback_started( VLC_TS_INVALID
)
112 , m_ts_local_start( VLC_TS_INVALID
)
113 , m_length( VLC_TS_INVALID
)
114 , m_pingRetriesLeft( PING_WAIT_RETRIES
)
116 m_ctl_thread_interrupt
= vlc_interrupt_create();
117 if( unlikely(m_ctl_thread_interrupt
== NULL
) )
118 throw std::runtime_error( "error creating interrupt context" );
120 vlc_mutex_init(&m_lock
);
121 vlc_cond_init( &m_stateChangedCond
);
122 vlc_cond_init( &m_pace_cond
);
124 std::stringstream ss
;
125 ss
<< "http://" << m_communication
.getServerIp() << ":" << port
;
126 m_art_http_ip
= ss
.str();
128 m_common
.p_opaque
= this;
129 m_common
.pf_set_on_paused_changed_cb
= set_on_paused_changed_cb
;
130 m_common
.pf_get_position
= get_position
;
131 m_common
.pf_get_time
= get_time
;
132 m_common
.pf_set_length
= set_length
;
133 m_common
.pf_set_initial_time
= set_initial_time
;
134 m_common
.pf_pace
= pace
;
135 m_common
.pf_set_pause_state
= set_pause_state
;
136 m_common
.pf_set_meta
= set_meta
;
138 assert( var_Type( m_module
->obj
.parent
->obj
.parent
, CC_SHARED_VAR_NAME
) == 0 );
139 if (var_Create( m_module
->obj
.parent
->obj
.parent
, CC_SHARED_VAR_NAME
, VLC_VAR_ADDRESS
) == VLC_SUCCESS
)
140 var_SetAddress( m_module
->obj
.parent
->obj
.parent
, CC_SHARED_VAR_NAME
, &m_common
);
142 // Start the Chromecast event thread.
143 if (vlc_clone(&m_chromecastThread
, ChromecastThread
, this,
144 VLC_THREAD_PRIORITY_LOW
))
146 vlc_interrupt_destroy( m_ctl_thread_interrupt
);
147 vlc_cond_destroy( &m_stateChangedCond
);
148 vlc_cond_destroy( &m_pace_cond
);
149 var_SetAddress( m_module
->obj
.parent
->obj
.parent
, CC_SHARED_VAR_NAME
, NULL
);
150 throw std::runtime_error( "error creating cc thread" );
154 intf_sys_t::~intf_sys_t()
156 var_Destroy( m_module
->obj
.parent
->obj
.parent
, CC_SHARED_VAR_NAME
);
158 vlc_mutex_lock(&m_lock
);
168 // Generate the close messages.
169 m_communication
.msgReceiverClose( m_appTransportId
);
174 m_communication
.msgReceiverClose(DEFAULT_CHOMECAST_RECEIVER
);
179 vlc_mutex_unlock(&m_lock
);
181 vlc_interrupt_kill( m_ctl_thread_interrupt
);
183 vlc_join(m_chromecastThread
, NULL
);
185 vlc_interrupt_destroy( m_ctl_thread_interrupt
);
188 vlc_meta_Delete(m_meta
);
191 httpd_FileDelete( m_httpd_file
);
195 vlc_cond_destroy(&m_stateChangedCond
);
196 vlc_cond_destroy(&m_pace_cond
);
197 vlc_mutex_destroy(&m_lock
);
200 int intf_sys_t::httpd_file_fill( uint8_t *psz_request
, uint8_t **pp_data
, int *pi_data
)
204 vlc_mutex_lock( &m_lock
);
207 vlc_mutex_unlock( &m_lock
);
210 char *psz_art
= strdup( m_art_url
);
211 vlc_mutex_unlock( &m_lock
);
213 stream_t
*s
= vlc_stream_NewURL( m_module
, psz_art
);
219 if( vlc_stream_GetSize( s
, &size
) != VLC_SUCCESS
220 || size
> INT64_C( 10000000 ) )
222 msg_Warn( m_module
, "art stream is too big or invalid" );
223 vlc_stream_Delete( s
);
227 *pp_data
= (uint8_t *)malloc( size
);
230 vlc_stream_Delete( s
);
234 ssize_t read
= vlc_stream_Read( s
, *pp_data
, size
);
235 vlc_stream_Delete( s
);
237 if( read
< 0 || (size_t)read
!= size
)
248 static int httpd_file_fill_cb( httpd_file_sys_t
*data
, httpd_file_t
*http_file
,
249 uint8_t *psz_request
, uint8_t **pp_data
, int *pi_data
)
252 intf_sys_t
*p_sys
= static_cast<intf_sys_t
*>((void *)data
);
253 return p_sys
->httpd_file_fill( psz_request
, pp_data
, pi_data
);
256 void intf_sys_t::prepareHttpArtwork()
258 const char *psz_art
= m_meta
? vlc_meta_Get( m_meta
, vlc_meta_ArtworkURL
) : NULL
;
259 /* Abort if there is no art or if the art is already served */
260 if( !psz_art
|| strncmp( psz_art
, "http", 4) == 0 )
263 std::stringstream ss_art_idx
;
265 if( m_art_url
&& strcmp( m_art_url
, psz_art
) == 0 )
267 /* Same art: use the previous cached artwork url */
268 assert( m_art_idx
!= 0 );
269 ss_art_idx
<< "/art" << (m_art_idx
- 1);
273 /* New art: create a new httpd file instance with a new url. The
274 * artwork has to be different since the CC will cache the content. */
276 ss_art_idx
<< "/art" << m_art_idx
;
279 vlc_mutex_unlock( &m_lock
);
282 httpd_FileDelete( m_httpd_file
);
283 m_httpd_file
= httpd_FileNew( m_httpd_host
, ss_art_idx
.str().c_str(),
284 "application/octet-stream", NULL
, NULL
,
285 httpd_file_fill_cb
, (httpd_file_sys_t
*) this );
287 vlc_mutex_lock( &m_lock
);
292 m_art_url
= strdup( psz_art
);
295 std::stringstream ss
;
296 ss
<< m_art_http_ip
<< ss_art_idx
.str();
297 vlc_meta_Set( m_meta
, vlc_meta_ArtworkURL
, ss
.str().c_str() );
300 void intf_sys_t::tryLoad()
302 if( !m_request_load
)
305 if ( !isStateReady() )
307 if ( m_state
== Dead
)
309 msg_Warn( m_module
, "no Chromecast hook possible");
310 m_request_load
= false;
312 else if( m_state
== Connected
)
314 msg_Dbg( m_module
, "Starting the media receiver application" );
315 // Don't use setState as we don't want to signal the condition in this case.
317 m_communication
.msgReceiverLaunchApp();
322 m_request_load
= false;
324 // We should now be in the ready state, and therefor have a valid transportId
325 assert( m_appTransportId
.empty() == false );
326 // Reset the mediaSessionID to allow the new session to become the current one.
327 // we cannot start a new load when the last one is still processing
328 m_communication
.msgPlayerLoad( m_appTransportId
, m_streaming_port
, m_mime
, m_meta
);
332 void intf_sys_t::setHasInput( const std::string mime_type
)
334 vlc_mutex_locker
locker(&m_lock
);
335 msg_Dbg( m_module
, "Loading content" );
337 m_request_stop
= false;
339 this->m_mime
= mime_type
;
341 /* new input: clear message queue */
342 std::queue
<QueueableMessages
> empty
;
343 std::swap(m_msgQueue
, empty
);
345 prepareHttpArtwork();
347 m_played_once
= false;
349 m_mediaSessionId
= 0;
350 m_request_load
= true;
353 vlc_cond_signal( &m_stateChangedCond
);
356 bool intf_sys_t::isStateError() const
369 bool intf_sys_t::isStatePlaying() const
384 bool intf_sys_t::isStateReady() const
400 void intf_sys_t::setPacing(bool do_pace
)
402 vlc_mutex_lock( &m_lock
);
403 if( m_pace
== do_pace
)
405 vlc_mutex_unlock( &m_lock
);
409 vlc_mutex_unlock( &m_lock
);
410 vlc_cond_signal( &m_pace_cond
);
413 static void interrupt_wake_up_cb( void *data
)
415 intf_sys_t
*p_sys
= static_cast<intf_sys_t
*>((void *)data
);
416 p_sys
->interrupt_wake_up();
419 void intf_sys_t::interrupt_wake_up()
421 vlc_mutex_locker
locker( &m_lock
);
422 m_interrupted
= true;
423 vlc_cond_signal( &m_pace_cond
);
426 bool intf_sys_t::pace()
428 vlc_mutex_locker
locker(&m_lock
);
432 m_interrupted
= false;
433 vlc_interrupt_register( interrupt_wake_up_cb
, this );
436 mtime_t deadline
= mdate() + INT64_C(500000);
437 while( m_pace
&& !m_interrupted
&& ret
== 0 )
438 ret
= vlc_cond_timedwait( &m_pace_cond
, &m_lock
, deadline
);
439 vlc_interrupt_unregister();
445 * @brief Process a message received from the Chromecast
446 * @param msg the CastMessage to process
447 * @return 0 if the message has been successfuly processed else -1
449 void intf_sys_t::processMessage(const castchannel::CastMessage
&msg
)
451 const std::string
& namespace_
= msg
.namespace_();
454 msg_Dbg( m_module
, "processMessage: %s->%s %s", namespace_
.c_str(), msg
.destination_id().c_str(), msg
.payload_utf8().c_str());
457 if (namespace_
== NAMESPACE_DEVICEAUTH
)
458 processAuthMessage( msg
);
459 else if (namespace_
== NAMESPACE_HEARTBEAT
)
460 processHeartBeatMessage( msg
);
461 else if (namespace_
== NAMESPACE_RECEIVER
)
462 processReceiverMessage( msg
);
463 else if (namespace_
== NAMESPACE_MEDIA
)
464 processMediaMessage( msg
);
465 else if (namespace_
== NAMESPACE_CONNECTION
)
466 processConnectionMessage( msg
);
469 msg_Err( m_module
, "Unknown namespace: %s", msg
.namespace_().c_str());
473 void intf_sys_t::queueMessage( QueueableMessages msg
)
475 // Assume lock is held by the called
476 m_msgQueue
.push( msg
);
477 vlc_interrupt_raise( m_ctl_thread_interrupt
);
480 /*****************************************************************************
482 *****************************************************************************/
483 void* intf_sys_t::ChromecastThread(void* p_data
)
485 intf_sys_t
*p_sys
= static_cast<intf_sys_t
*>(p_data
);
490 void intf_sys_t::mainLoop()
493 vlc_interrupt_set( m_ctl_thread_interrupt
);
495 // State was already initialized as Authenticating
496 m_communication
.msgAuth();
498 while ( !vlc_killed() )
500 if ( !handleMessages() )
502 // Reset the interrupt state to avoid commands not being sent (since
503 // the context is still flagged as interrupted)
504 vlc_interrupt_unregister();
505 vlc_mutex_locker
lock( &m_lock
);
506 while ( m_msgQueue
.empty() == false )
508 QueueableMessages msg
= m_msgQueue
.front();
520 void intf_sys_t::processAuthMessage( const castchannel::CastMessage
& msg
)
522 castchannel::DeviceAuthMessage authMessage
;
523 if ( authMessage
.ParseFromString(msg
.payload_binary()) == false )
525 msg_Warn( m_module
, "Failed to parse the payload" );
529 if (authMessage
.has_error())
531 msg_Err( m_module
, "Authentification error: %d", authMessage
.error().error_type());
533 else if (!authMessage
.has_response())
535 msg_Err( m_module
, "Authentification message has no response field");
539 vlc_mutex_locker
locker(&m_lock
);
540 setState( Connecting
);
541 m_communication
.msgConnect(DEFAULT_CHOMECAST_RECEIVER
);
542 m_communication
.msgReceiverGetStatus();
546 void intf_sys_t::processHeartBeatMessage( const castchannel::CastMessage
& msg
)
548 json_value
*p_data
= json_parse(msg
.payload_utf8().c_str());
549 std::string
type((*p_data
)["type"]);
553 msg_Dbg( m_module
, "PING received from the Chromecast");
554 m_communication
.msgPong();
556 else if (type
== "PONG")
558 msg_Dbg( m_module
, "PONG received from the Chromecast");
559 m_pingRetriesLeft
= PING_WAIT_RETRIES
;
563 msg_Warn( m_module
, "Heartbeat command not supported: %s", type
.c_str());
566 json_value_free(p_data
);
569 void intf_sys_t::processReceiverMessage( const castchannel::CastMessage
& msg
)
571 json_value
*p_data
= json_parse(msg
.payload_utf8().c_str());
572 std::string
type((*p_data
)["type"]);
574 if (type
== "RECEIVER_STATUS")
576 json_value applications
= (*p_data
)["status"]["applications"];
577 const json_value
*p_app
= NULL
;
579 for (unsigned i
= 0; i
< applications
.u
.array
.length
; ++i
)
581 if ( strcmp( applications
[i
]["appId"], APP_ID
) == 0 )
583 if ( (const char*)applications
[i
]["transportId"] != NULL
)
585 p_app
= &applications
[i
];
591 vlc_mutex_locker
locker(&m_lock
);
596 // We were connecting & fetching the current status.
597 // The media receiver app is running, we are ready to proceed
600 msg_Dbg( m_module
, "Media receiver application was already running" );
601 m_appTransportId
= (const char*)(*p_app
)["transportId"];
602 m_communication
.msgConnect( m_appTransportId
);
607 setState( Connected
);
611 // We already asked for the media receiver application to start
614 msg_Dbg( m_module
, "Media receiver application has been started." );
615 m_appTransportId
= (const char*)(*p_app
)["transportId"];
616 m_communication
.msgConnect( m_appTransportId
);
629 msg_Warn( m_module
, "Media receiver application got closed." );
630 setState( Connected
);
631 m_appTransportId
= "";
632 m_mediaSessionId
= 0;
636 // We might receive a RECEIVER_STATUS while being connected, when pinging/asking the status
639 // else: fall through and warn
641 msg_Warn( m_module
, "Unexpected RECEIVER_STATUS with state %s. "
642 "Checking media status",
643 StateToStr( m_state
) );
644 // This is likely because the chromecast refused the playback, but
645 // let's check by explicitely probing the media status
646 m_communication
.msgPlayerGetStatus( m_appTransportId
);
650 else if (type
== "LAUNCH_ERROR")
652 json_value reason
= (*p_data
)["reason"];
653 msg_Err( m_module
, "Failed to start the MediaPlayer: %s",
654 (const char *)reason
);
655 vlc_mutex_locker
locker(&m_lock
);
656 m_appTransportId
= "";
657 m_mediaSessionId
= 0;
662 msg_Warn( m_module
, "Receiver command not supported: %s",
663 msg
.payload_utf8().c_str());
666 json_value_free(p_data
);
669 void intf_sys_t::processMediaMessage( const castchannel::CastMessage
& msg
)
671 json_value
*p_data
= json_parse(msg
.payload_utf8().c_str());
672 std::string
type((*p_data
)["type"]);
674 vlc_mutex_locker
locker( &m_lock
);
676 if (type
== "MEDIA_STATUS")
678 json_value status
= (*p_data
)["status"];
680 int64_t sessionId
= (json_int_t
) status
[0]["mediaSessionId"];
681 if (m_mediaSessionId
!= sessionId
&& m_mediaSessionId
!= 0 )
683 msg_Dbg( m_module
, "Ignoring message for a different media session" );
684 json_value_free(p_data
);
688 msg_Dbg( m_module
, "Player state: %s sessionId: %" PRId64
,
689 status
[0]["playerState"].operator const char *(),
692 std::string newPlayerState
= (const char*)status
[0]["playerState"];
693 std::string idleReason
= (const char*)status
[0]["idleReason"];
696 if (newPlayerState
== "IDLE" || newPlayerState
.empty() == true )
698 /* Idle state is expected when the media receiver application is
699 * started. In case the state is still Buffering, it denotes an error.
700 * In most case, we'd receive a RECEIVER_STATUS message, which causes
701 * use to ask for the MEDIA_STATUS before assuming an error occured.
702 * If the chromecast silently gave up on playing our stream, we also
703 * might have an empty status array.
704 * If the media load indeed failed, we need to try another
705 * transcode/remux configuration, or give up.
706 * In case we are now loading, we might also receive an INTERRUPTED
707 * state for the previous session, which we wouldn't ignore earlier
708 * since our mediaSessionID was reset to 0.
709 * In this case, don't assume we're being taken over, as we are
710 * actually doing the take over.
712 if ( m_state
!= Ready
&& m_state
!= LoadFailed
&& m_state
!= Loading
)
714 // The playback stopped
715 m_time_playback_started
= VLC_TS_INVALID
;
716 if ( idleReason
== "INTERRUPTED" )
718 setState( TakenOver
);
719 // Do not reset the mediaSessionId to ensure we refuse all
720 // other MEDIA_STATUS from the new session.
722 else if ( m_state
== Buffering
)
723 setState( LoadFailed
);
726 if (idleReason
== "FINISHED")
734 if ( m_mediaSessionId
== 0 )
736 m_mediaSessionId
= sessionId
;
737 msg_Dbg( m_module
, "New mediaSessionId: %" PRId64
, m_mediaSessionId
);
742 m_request_stop
= false;
743 m_communication
.msgPlayerStop( m_appTransportId
, m_mediaSessionId
);
744 setState( Stopping
);
746 else if (newPlayerState
== "PLAYING")
748 msg_Dbg( m_module
, "Playback started now:%" PRId64
" i_ts_local_start:%" PRId64
,
749 m_time_playback_started
, m_ts_local_start
);
750 if ( m_state
!= Playing
)
752 /* TODO reset demux PCR ? */
753 m_time_playback_started
= mdate();
757 else if (newPlayerState
== "BUFFERING")
759 if ( m_state
!= Buffering
)
761 /* EOF when state goes from Playing to Buffering. There can
762 * be a lot of false positives (when seeking or when the cc
763 * request more input) but this state is fetched only when
764 * the input has reached EOF. */
766 m_time_playback_started
= VLC_TS_INVALID
;
767 setState( Buffering
);
770 else if (newPlayerState
== "PAUSED")
772 if ( m_state
!= Paused
)
775 msg_Dbg( m_module
, "Playback paused: date_play_start: %" PRId64
, m_time_playback_started
);
778 if ( m_time_playback_started
!= VLC_TS_INVALID
&& m_state
== Playing
)
780 /* this is a pause generated remotely, adjust the playback time */
781 m_ts_local_start
+= mdate() - m_time_playback_started
;
783 msg_Dbg( m_module
, "updated i_ts_local_start:%" PRId64
, m_ts_local_start
);
786 m_time_playback_started
= VLC_TS_INVALID
;
790 else if ( newPlayerState
== "LOADING" )
792 if ( m_state
!= Loading
)
794 msg_Dbg( m_module
, "Chromecast is loading the stream" );
799 msg_Warn( m_module
, "Unknown Chromecast MEDIA_STATUS state %s", newPlayerState
.c_str());
802 else if (type
== "LOAD_FAILED")
804 msg_Err( m_module
, "Media load failed");
805 setState( LoadFailed
);
807 else if (type
== "LOAD_CANCELLED")
809 msg_Dbg( m_module
, "LOAD canceled by another command");
811 else if (type
== "INVALID_REQUEST")
813 msg_Dbg( m_module
, "We sent an invalid request reason:%s", (const char*)(*p_data
)["reason"] );
817 msg_Warn( m_module
, "Media command not supported: %s",
818 msg
.payload_utf8().c_str());
821 json_value_free(p_data
);
824 void intf_sys_t::processConnectionMessage( const castchannel::CastMessage
& msg
)
826 json_value
*p_data
= json_parse(msg
.payload_utf8().c_str());
827 std::string
type((*p_data
)["type"]);
828 json_value_free(p_data
);
830 if ( type
== "CLOSE" )
832 // Close message indicates an application is being closed, not the connection.
833 // From this point on, we need to relaunch the media receiver app
834 vlc_mutex_locker
locker(&m_lock
);
835 m_appTransportId
= "";
836 m_mediaSessionId
= 0;
837 setState( Connected
);
841 msg_Warn( m_module
, "Connection command not supported: %s",
846 bool intf_sys_t::handleMessages()
848 uint8_t p_packet
[PACKET_MAX_LEN
];
849 size_t i_payloadSize
= 0;
850 size_t i_received
= 0;
851 bool b_timeout
= false;
852 mtime_t i_begin_time
= mdate();
855 * +------------------------------------+------------------------------+
856 * | Payload size (uint32_t big endian) | Payload data |
857 * +------------------------------------+------------------------------+
861 // If we haven't received the payload size yet, let's wait for it. Otherwise, we know
862 // how many bytes to read
863 ssize_t i_ret
= m_communication
.receive( p_packet
+ i_received
,
864 i_payloadSize
+ PACKET_HEADER_LEN
- i_received
,
865 PING_WAIT_TIME
- ( mdate() - i_begin_time
) / CLOCK_FREQ
,
869 if ( errno
== EINTR
)
871 // An error occured, we give up
872 msg_Err( m_module
, "The connection to the Chromecast died (receiving).");
873 vlc_mutex_locker
locker(&m_lock
);
877 else if ( b_timeout
== true )
879 // If no commands were queued to be sent, we timed out. Let's ping the chromecast
880 vlc_mutex_locker
locker(&m_lock
);
881 if ( m_pingRetriesLeft
== 0 )
884 msg_Warn( m_module
, "No PING response from the chromecast" );
888 m_communication
.msgPing();
889 m_communication
.msgReceiverGetStatus();
892 assert( i_ret
!= 0 );
894 if ( i_payloadSize
== 0 )
896 i_payloadSize
= U32_AT( p_packet
);
897 if ( i_payloadSize
> PACKET_MAX_LEN
- PACKET_HEADER_LEN
)
899 msg_Err( m_module
, "Payload size is too long: dropping connection" );
900 vlc_mutex_locker
locker(&m_lock
);
906 assert( i_received
<= i_payloadSize
+ PACKET_HEADER_LEN
);
907 if ( i_received
== i_payloadSize
+ PACKET_HEADER_LEN
)
910 castchannel::CastMessage msg
;
911 msg
.ParseFromArray(p_packet
+ PACKET_HEADER_LEN
, i_payloadSize
);
916 void intf_sys_t::doStop()
918 if( !isStatePlaying() )
921 if ( m_mediaSessionId
== 0 )
922 m_request_stop
= true;
925 m_communication
.msgPlayerStop( m_appTransportId
, m_mediaSessionId
);
926 setState( Stopping
);
930 void intf_sys_t::requestPlayerStop()
932 vlc_mutex_locker
locker(&m_lock
);
934 std::queue
<QueueableMessages
> empty
;
935 std::swap(m_msgQueue
, empty
);
937 m_request_load
= false;
941 if( !isStatePlaying() )
943 queueMessage( Stop
);
949 States
intf_sys_t::state() const
951 vlc_mutex_locker
locker( &m_lock
);
955 std::string
intf_sys_t::timeVLCToCC(mtime_t time
)
957 std::stringstream ss
;
958 ss
.setf(std::ios_base::fixed
, std::ios_base::floatfield
);
959 ss
<< std::setprecision(6) << (double (time
) / 1000000.0);
963 bool intf_sys_t::requestPlayerSeek(mtime_t pos
)
965 vlc_mutex_locker
locker(&m_lock
);
966 if( !isStatePlaying() || m_mediaSessionId
== 0 )
969 unsigned ret
= m_communication
.msgPlayerSeek( m_appTransportId
, m_mediaSessionId
,
970 timeVLCToCC( pos
) );
971 if( ret
!= ChromecastCommunication::kInvalidId
)
979 void intf_sys_t::setOnSeekDoneCb(on_seek_done_itf on_seek_done
, void *on_seek_done_data
)
981 vlc_mutex_locker
locker(&m_lock
);
982 m_on_seek_done
= on_seek_done
;
983 m_on_seek_done_data
= on_seek_done_data
;
986 void intf_sys_t::setOnPausedChangedCb(on_paused_changed_itf on_paused_changed
,
987 void *on_paused_changed_data
)
989 vlc_mutex_locker
locker(&m_lock
);
990 m_on_paused_changed
= on_paused_changed
;
991 m_on_paused_changed_data
= on_paused_changed_data
;
994 void intf_sys_t::setPauseState(bool paused
)
996 vlc_mutex_locker
locker( &m_lock
);
997 if ( m_mediaSessionId
== 0 )
1000 msg_Dbg( m_module
, "%s state", paused
? "paused" : "playing" );
1002 m_communication
.msgPlayerPlay( m_appTransportId
, m_mediaSessionId
);
1003 else if ( m_state
!= Paused
)
1004 m_communication
.msgPlayerPause( m_appTransportId
, m_mediaSessionId
);
1007 bool intf_sys_t::isFinishedPlaying()
1009 vlc_mutex_locker
locker(&m_lock
);
1010 return m_state
== LoadFailed
|| m_state
== Dead
|| m_eof
;
1013 void intf_sys_t::setMeta(vlc_meta_t
*p_meta
)
1015 vlc_mutex_locker
locker(&m_lock
);
1017 vlc_meta_Delete(m_meta
);
1021 mtime_t
intf_sys_t::getPlaybackTimestamp() const
1026 return ( mdate() - m_time_playback_started
) + m_ts_local_start
;
1027 #ifdef CHROMECAST_VERBOSE
1029 msg_Dbg(m_module
, "receiver idle using buffering time %" PRId64
, m_ts_local_start
);
1032 msg_Dbg(m_module
, "receiver buffering using buffering time %" PRId64
, m_ts_local_start
);
1035 msg_Dbg(m_module
, "receiver paused using buffering time %" PRId64
, m_ts_local_start
);
1041 return m_ts_local_start
;
1044 double intf_sys_t::getPlaybackPosition() const
1047 return (double) getPlaybackTimestamp() / (double)( m_length
);
1051 void intf_sys_t::setInitialTime(mtime_t time
)
1054 m_ts_local_start
= time
;
1057 void intf_sys_t::setState( States state
)
1059 if ( m_state
!= state
)
1062 msg_Dbg( m_module
, "Switching from state %s to %s", StateToStr( m_state
), StateToStr( state
) );
1064 if (m_state
== Seeking
)
1065 if (m_on_seek_done
!= NULL
)
1066 m_on_seek_done(m_on_seek_done_data
);
1077 if (m_played_once
&& m_on_paused_changed
!= NULL
)
1078 m_on_paused_changed(m_on_paused_changed_data
, true);
1081 if (m_played_once
&& m_on_paused_changed
!= NULL
)
1082 m_on_paused_changed(m_on_paused_changed_data
, false);
1083 m_played_once
= true;
1088 vlc_cond_signal( &m_stateChangedCond
);
1092 mtime_t
intf_sys_t::get_time(void *pt
)
1094 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1095 vlc_mutex_locker
locker( &p_this
->m_lock
);
1096 return p_this
->getPlaybackTimestamp();
1099 double intf_sys_t::get_position(void *pt
)
1101 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1102 vlc_mutex_locker
locker( &p_this
->m_lock
);
1103 return p_this
->getPlaybackPosition();
1106 void intf_sys_t::set_initial_time(void *pt
, mtime_t time
)
1108 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1109 vlc_mutex_locker
locker( &p_this
->m_lock
);
1110 return p_this
->setInitialTime( time
);
1113 void intf_sys_t::set_on_paused_changed_cb(void *pt
,
1114 on_paused_changed_itf itf
, void *data
)
1116 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1117 p_this
->setOnPausedChangedCb(itf
, data
);
1120 void intf_sys_t::set_length(void *pt
, mtime_t length
)
1122 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1123 p_this
->m_length
= length
;
1126 bool intf_sys_t::pace(void *pt
)
1128 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1129 return p_this
->pace();
1132 void intf_sys_t::set_pause_state(void *pt
, bool paused
)
1134 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1135 p_this
->setPauseState( paused
);
1138 void intf_sys_t::set_meta(void *pt
, vlc_meta_t
*p_meta
)
1140 intf_sys_t
*p_this
= static_cast<intf_sys_t
*>(pt
);
1141 p_this
->setMeta( p_meta
);