sout/chromecast: intf_sys_t: prefer static_cast
[vlc.git] / modules / stream_out / chromecast / chromecast_ctrl.cpp
blob1f2fbadf7dc829677a207db16452e6b018632760
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 /*****************************************************************************
27 * Preamble
28 *****************************************************************************/
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
34 #include "chromecast.h"
36 #include <cassert>
37 #include <cerrno>
39 #include "../../misc/webservices/json.h"
41 /* deadline regarding pings sent from receiver */
42 #define PING_WAIT_TIME 6000
43 #define PING_WAIT_RETRIES 1
45 static const mtime_t SEEK_FORWARD_OFFSET = 1000000;
47 static const char* StateToStr( States s )
49 switch (s )
51 case Authenticating:
52 return "Authenticating";
53 case Connecting:
54 return "Connecting";
55 case Connected:
56 return "Connected";
57 case Launching:
58 return "Lauching";
59 case Ready:
60 return "Ready";
61 case Loading:
62 return "Loading";
63 case Buffering:
64 return "Buffering";
65 case Playing:
66 return "Playing";
67 case Paused:
68 return "Paused";
69 case Seeking:
70 return "Seeking";
71 case Dead:
72 return "Dead";
74 vlc_assert_unreachable();
77 /*****************************************************************************
78 * intf_sys_t: class definition
79 *****************************************************************************/
80 intf_sys_t::intf_sys_t(vlc_object_t * const p_this, int port, std::string device_addr, int device_port, vlc_interrupt_t *p_interrupt)
81 : m_module(p_this)
82 , m_streaming_port(port)
83 , m_communication( p_this, device_addr.c_str(), device_port )
84 , m_state( Authenticating )
85 , m_ctl_thread_interrupt(p_interrupt)
86 , m_time_playback_started( VLC_TS_INVALID )
87 , m_ts_local_start( VLC_TS_INVALID )
88 , m_length( VLC_TS_INVALID )
89 , m_chromecast_start_time( VLC_TS_INVALID )
90 , m_pingRetriesLeft( PING_WAIT_RETRIES )
92 vlc_mutex_init(&m_lock);
93 vlc_cond_init( &m_stateChangedCond );
95 m_common.p_opaque = this;
96 m_common.pf_get_position = get_position;
97 m_common.pf_get_time = get_time;
98 m_common.pf_set_length = set_length;
99 m_common.pf_wait_app_started = wait_app_started;
100 m_common.pf_request_seek = request_seek;
101 m_common.pf_wait_seek_done = wait_seek_done;
102 m_common.pf_set_pause_state = set_pause_state;
103 m_common.pf_set_artwork = set_artwork;
104 m_common.pf_set_title = set_title;
106 assert( var_Type( m_module->obj.parent->obj.parent, CC_SHARED_VAR_NAME) == 0 );
107 if (var_Create( m_module->obj.parent->obj.parent, CC_SHARED_VAR_NAME, VLC_VAR_ADDRESS ) == VLC_SUCCESS )
108 var_SetAddress( m_module->obj.parent->obj.parent, CC_SHARED_VAR_NAME, &m_common );
110 // Start the Chromecast event thread.
111 if (vlc_clone(&m_chromecastThread, ChromecastThread, this,
112 VLC_THREAD_PRIORITY_LOW))
114 msg_Err( m_module, "Could not start the Chromecast talking thread");
118 intf_sys_t::~intf_sys_t()
120 var_Destroy( m_module->obj.parent->obj.parent, CC_SHARED_VAR_NAME );
122 switch ( m_state )
124 case Ready:
125 case Loading:
126 case Buffering:
127 case Playing:
128 case Paused:
129 case Seeking:
130 // Generate the close messages.
131 m_communication.msgReceiverClose( m_appTransportId );
132 // ft
133 case Connecting:
134 case Connected:
135 case Launching:
136 m_communication.msgReceiverClose(DEFAULT_CHOMECAST_RECEIVER);
137 // ft
138 default:
139 break;
142 vlc_interrupt_kill( m_ctl_thread_interrupt );
144 vlc_join(m_chromecastThread, NULL);
146 vlc_interrupt_destroy( m_ctl_thread_interrupt );
148 vlc_cond_destroy(&m_stateChangedCond);
149 vlc_mutex_destroy(&m_lock);
152 void intf_sys_t::setHasInput( const std::string mime_type )
154 vlc_mutex_locker locker(&m_lock);
155 msg_Dbg( m_module, "Loading content for session:%s", m_mediaSessionId.c_str() );
157 this->m_mime = mime_type;
159 waitAppStarted();
160 if ( m_state == Dead )
162 msg_Warn( m_module, "no Chromecast hook possible");
163 return;
165 // We should now be in the ready state, and therefor have a valid transportId
166 assert( m_state == Ready && m_appTransportId.empty() == false );
167 // we cannot start a new load when the last one is still processing
168 m_ts_local_start = VLC_TS_0;
169 m_communication.msgPlayerLoad( m_appTransportId, m_streaming_port, m_title, m_artwork, mime_type );
170 setState( Loading );
174 * @brief Process a message received from the Chromecast
175 * @param msg the CastMessage to process
176 * @return 0 if the message has been successfuly processed else -1
178 void intf_sys_t::processMessage(const castchannel::CastMessage &msg)
180 const std::string & namespace_ = msg.namespace_();
182 #ifndef NDEBUG
183 msg_Dbg( m_module, "processMessage: %s->%s %s", namespace_.c_str(), msg.destination_id().c_str(), msg.payload_utf8().c_str());
184 #endif
186 if (namespace_ == NAMESPACE_DEVICEAUTH)
187 processAuthMessage( msg );
188 else if (namespace_ == NAMESPACE_HEARTBEAT)
189 processHeartBeatMessage( msg );
190 else if (namespace_ == NAMESPACE_RECEIVER)
191 processReceiverMessage( msg );
192 else if (namespace_ == NAMESPACE_MEDIA)
193 processMediaMessage( msg );
194 else if (namespace_ == NAMESPACE_CONNECTION)
195 processConnectionMessage( msg );
196 else
198 msg_Err( m_module, "Unknown namespace: %s", msg.namespace_().c_str());
204 /*****************************************************************************
205 * Chromecast thread
206 *****************************************************************************/
207 void* intf_sys_t::ChromecastThread(void* p_data)
209 intf_sys_t *p_sys = static_cast<intf_sys_t*>(p_data);
210 p_sys->mainLoop();
211 return NULL;
214 void intf_sys_t::mainLoop()
216 vlc_savecancel();
217 vlc_interrupt_set( m_ctl_thread_interrupt );
219 // State was already initialized as Authenticating
220 m_communication.msgAuth();
222 while ( !vlc_killed() && handleMessages() )
226 void intf_sys_t::processAuthMessage( const castchannel::CastMessage& msg )
228 castchannel::DeviceAuthMessage authMessage;
229 if ( authMessage.ParseFromString(msg.payload_binary()) == false )
231 msg_Warn( m_module, "Failed to parse the payload" );
232 return;
235 if (authMessage.has_error())
237 msg_Err( m_module, "Authentification error: %d", authMessage.error().error_type());
239 else if (!authMessage.has_response())
241 msg_Err( m_module, "Authentification message has no response field");
243 else
245 vlc_mutex_locker locker(&m_lock);
246 setState( Connecting );
247 m_communication.msgConnect(DEFAULT_CHOMECAST_RECEIVER);
248 m_communication.msgReceiverGetStatus();
252 void intf_sys_t::processHeartBeatMessage( const castchannel::CastMessage& msg )
254 json_value *p_data = json_parse(msg.payload_utf8().c_str());
255 std::string type((*p_data)["type"]);
257 if (type == "PING")
259 msg_Dbg( m_module, "PING received from the Chromecast");
260 m_communication.msgPong();
262 else if (type == "PONG")
264 msg_Dbg( m_module, "PONG received from the Chromecast");
265 m_pingRetriesLeft = PING_WAIT_RETRIES;
267 else
269 msg_Warn( m_module, "Heartbeat command not supported: %s", type.c_str());
272 json_value_free(p_data);
275 void intf_sys_t::processReceiverMessage( const castchannel::CastMessage& msg )
277 json_value *p_data = json_parse(msg.payload_utf8().c_str());
278 std::string type((*p_data)["type"]);
280 if (type == "RECEIVER_STATUS")
282 json_value applications = (*p_data)["status"]["applications"];
283 const json_value *p_app = NULL;
285 for (unsigned i = 0; i < applications.u.array.length; ++i)
287 if ( strcmp( applications[i]["appId"], APP_ID ) == 0 )
289 if ( (const char*)applications[i]["transportId"] != NULL)
291 p_app = &applications[i];
292 break;
297 vlc_mutex_locker locker(&m_lock);
299 switch ( m_state )
301 case Connecting:
302 // We were connecting & fetching the current status.
303 // The media receiver app is running, we are ready to proceed
304 if ( p_app != NULL )
306 msg_Dbg( m_module, "Media receiver application was already running" );
307 m_appTransportId = (const char*)(*p_app)["transportId"];
308 setState( Ready );
309 m_communication.msgConnect( m_appTransportId );
311 else
313 setState( Connected );
315 break;
316 case Launching:
317 // We already asked for the media receiver application to start
318 if ( p_app != NULL )
320 msg_Dbg( m_module, "Media receiver application has been started." );
321 setState( Ready );
322 m_appTransportId = (const char*)(*p_app)["transportId"];
323 m_communication.msgConnect( m_appTransportId );
325 break;
326 case Loading:
327 case Playing:
328 case Paused:
329 case Seeking:
330 case Ready:
331 if ( p_app == NULL )
333 msg_Warn( m_module, "Media receiver application got closed." );
334 setState( Connected );
335 m_appTransportId = "";
336 m_mediaSessionId = "";
338 break;
339 case Connected:
340 // We might receive a RECEIVER_STATUS while being connected, when pinging/asking the status
341 if ( p_app == NULL )
342 break;
343 // else: fall through and warn
344 default:
345 msg_Warn( m_module, "Unexpected RECEIVER_STATUS with state %d", m_state );
346 break;
349 else if (type == "LAUNCH_ERROR")
351 json_value reason = (*p_data)["reason"];
352 msg_Err( m_module, "Failed to start the MediaPlayer: %s",
353 (const char *)reason);
354 vlc_mutex_locker locker(&m_lock);
355 setState( Dead );
357 else
359 msg_Warn( m_module, "Receiver command not supported: %s",
360 msg.payload_utf8().c_str());
363 json_value_free(p_data);
366 void intf_sys_t::processMediaMessage( const castchannel::CastMessage& msg )
368 json_value *p_data = json_parse(msg.payload_utf8().c_str());
369 std::string type((*p_data)["type"]);
371 if (type == "MEDIA_STATUS")
373 json_value status = (*p_data)["status"];
374 msg_Dbg( m_module, "Player state: %s sessionId:%d",
375 status[0]["playerState"].operator const char *(),
376 (int)(json_int_t) status[0]["mediaSessionId"]);
378 std::string newPlayerState = (const char*)status[0]["playerState"];
379 std::string idleReason = (const char*)status[0]["idleReason"];
381 vlc_mutex_locker locker( &m_lock );
383 if (newPlayerState == "IDLE")
385 if ( m_state != Ready )
387 // The playback stopped
388 m_mediaSessionId = "";
389 m_time_playback_started = VLC_TS_INVALID;
390 setState( Ready );
393 else
395 char session_id[32];
396 if( snprintf( session_id, sizeof(session_id), "%" PRId64, (json_int_t) status[0]["mediaSessionId"] ) >= (int)sizeof(session_id) )
398 msg_Err( m_module, "snprintf() truncated string for mediaSessionId" );
399 session_id[sizeof(session_id) - 1] = '\0';
401 if (session_id[0] && m_mediaSessionId != session_id) {
402 if (!m_mediaSessionId.empty())
403 msg_Warn( m_module, "different mediaSessionId detected %s was %s", session_id, this->m_mediaSessionId.c_str());
404 m_mediaSessionId = session_id;
407 if (newPlayerState == "PLAYING")
409 msg_Dbg( m_module, "Playback started with an offset of %" PRId64 " now:%" PRId64 " i_ts_local_start:%" PRId64,
410 m_chromecast_start_time, m_time_playback_started, m_ts_local_start);
411 if ( m_state != Playing )
413 /* TODO reset demux PCR ? */
414 if (unlikely(m_chromecast_start_time == VLC_TS_INVALID)) {
415 msg_Warn( m_module, "start playing without buffering" );
416 m_chromecast_start_time = (1 + mtime_t( double( status[0]["currentTime"] ) ) ) * 1000000L;
418 m_time_playback_started = mdate();
419 setState( Playing );
422 else if (newPlayerState == "BUFFERING")
424 if ( m_state != Buffering )
426 if ( double(status[0]["currentTime"]) == 0.0 )
428 msg_Dbg( m_module, "Invalid buffering time, keep current state");
430 else
432 m_chromecast_start_time = (1 + mtime_t( double( status[0]["currentTime"] ) ) ) * 1000000L;
433 msg_Dbg( m_module, "Playback pending with an offset of %" PRId64, m_chromecast_start_time);
434 m_time_playback_started = VLC_TS_INVALID;
435 setState( Buffering );
439 else if (newPlayerState == "PAUSED")
441 if ( m_state != Paused )
443 m_chromecast_start_time = (1 + mtime_t( double( status[0]["currentTime"] ) ) ) * 1000000L;
444 #ifndef NDEBUG
445 msg_Dbg( m_module, "Playback paused with an offset of %" PRId64 " date_play_start:%" PRId64, m_chromecast_start_time, m_time_playback_started);
446 #endif
448 if ( m_time_playback_started != VLC_TS_INVALID && m_state == Playing )
450 /* this is a pause generated remotely, adjust the playback time */
451 m_ts_local_start += mdate() - m_time_playback_started;
452 #ifndef NDEBUG
453 msg_Dbg( m_module, "updated i_ts_local_start:%" PRId64, m_ts_local_start);
454 #endif
456 m_time_playback_started = VLC_TS_INVALID;
457 setState( Paused );
460 else if ( newPlayerState == "LOADING" )
462 if ( m_state != Loading )
464 msg_Dbg( m_module, "Chromecast is loading the stream" );
465 setState( Loading );
468 else if (!newPlayerState.empty())
469 msg_Warn( m_module, "Unknown Chromecast MEDIA_STATUS state %s", newPlayerState.c_str());
472 else if (type == "LOAD_FAILED")
474 msg_Err( m_module, "Media load failed");
475 vlc_mutex_locker locker(&m_lock);
476 /* close the app to restart it */
477 if ( m_state == Launching )
478 m_communication.msgReceiverClose(m_appTransportId);
479 else
480 m_communication.msgReceiverGetStatus();
482 else if (type == "LOAD_CANCELLED")
484 msg_Dbg( m_module, "LOAD canceled by another command");
486 else if (type == "INVALID_REQUEST")
488 msg_Dbg( m_module, "We sent an invalid request reason:%s", (const char*)(*p_data)["reason"] );
490 else
492 msg_Warn( m_module, "Media command not supported: %s",
493 msg.payload_utf8().c_str());
496 json_value_free(p_data);
499 void intf_sys_t::processConnectionMessage( const castchannel::CastMessage& msg )
501 json_value *p_data = json_parse(msg.payload_utf8().c_str());
502 std::string type((*p_data)["type"]);
503 json_value_free(p_data);
505 if ( type == "CLOSE" )
507 // Close message indicates an application is being closed, not the connection.
508 // From this point on, we need to relaunch the media receiver app
509 vlc_mutex_locker locker(&m_lock);
510 m_appTransportId = "";
511 m_mediaSessionId = "";
512 setState( Connected );
514 else
516 msg_Warn( m_module, "Connection command not supported: %s",
517 type.c_str());
521 bool intf_sys_t::handleMessages()
523 uint8_t p_packet[PACKET_MAX_LEN];
524 size_t i_payloadSize = 0;
525 size_t i_received = 0;
526 bool b_timeout = false;
527 mtime_t i_begin_time = mdate();
529 /* Packet structure:
530 * +------------------------------------+------------------------------+
531 * | Payload size (uint32_t big endian) | Payload data |
532 * +------------------------------------+------------------------------+
534 while ( true )
536 // If we haven't received the payload size yet, let's wait for it. Otherwise, we know
537 // how many bytes to read
538 ssize_t i_ret = m_communication.receive( p_packet + i_received,
539 i_payloadSize + PACKET_HEADER_LEN - i_received,
540 PING_WAIT_TIME - ( mdate() - i_begin_time ) / CLOCK_FREQ,
541 &b_timeout );
542 if ( i_ret < 0 )
544 if ( errno == EINTR )
545 return true;
546 // An error occured, we give up
547 msg_Err( m_module, "The connection to the Chromecast died (receiving).");
548 vlc_mutex_locker locker(&m_lock);
549 setState( Dead );
550 return false;
552 else if ( b_timeout == true )
554 // If no commands were queued to be sent, we timed out. Let's ping the chromecast
555 if ( m_pingRetriesLeft == 0 )
557 vlc_mutex_locker locker(&m_lock);
558 m_state = Dead;
559 msg_Warn( m_module, "No PING response from the chromecast" );
560 return false;
562 --m_pingRetriesLeft;
563 m_communication.msgPing();
564 m_communication.msgReceiverGetStatus();
565 return true;
567 assert( i_ret != 0 );
568 i_received += i_ret;
569 if ( i_payloadSize == 0 )
571 i_payloadSize = U32_AT( p_packet );
572 if ( i_payloadSize > PACKET_MAX_LEN - PACKET_HEADER_LEN )
574 msg_Err( m_module, "Payload size is too long: dropping conection" );
575 vlc_mutex_locker locker(&m_lock);
576 m_state = Dead;
577 return false;
579 continue;
581 assert( i_received <= i_payloadSize + PACKET_HEADER_LEN );
582 if ( i_received == i_payloadSize + PACKET_HEADER_LEN )
583 break;
585 castchannel::CastMessage msg;
586 msg.ParseFromArray(p_packet + PACKET_HEADER_LEN, i_payloadSize);
587 processMessage(msg);
588 return true;
591 void intf_sys_t::requestPlayerStop()
593 vlc_mutex_locker locker(&m_lock);
594 if ( m_mediaSessionId.empty() == true )
595 return;
596 m_communication.msgPlayerStop( m_appTransportId, m_mediaSessionId );
599 void intf_sys_t::requestPlayerSeek(mtime_t pos)
601 vlc_mutex_locker locker(&m_lock);
602 if ( m_mediaSessionId.empty() == true )
603 return;
604 if ( pos != VLC_TS_INVALID )
605 m_ts_local_start = pos;
606 char current_time[32];
607 mtime_t seek_request_time = mdate() + SEEK_FORWARD_OFFSET;
608 if( snprintf( current_time, sizeof(current_time), "%.3f", double( seek_request_time ) / 1000000.0 ) >= (int)sizeof(current_time) )
610 msg_Err( m_module, "snprintf() truncated string for mediaSessionId" );
611 current_time[sizeof(current_time) - 1] = '\0';
613 /* send a fake time to seek to, to make sure the device flushes its buffers */
614 m_communication.msgPlayerSeek( m_appTransportId, m_mediaSessionId, current_time );
615 setState( Seeking );
618 void intf_sys_t::setPauseState(bool paused)
620 msg_Dbg( m_module, "%s state for %s", paused ? "paused" : "playing", m_title.c_str() );
621 vlc_mutex_locker locker( &m_lock );
622 if ( !paused )
624 if ( !m_mediaSessionId.empty() )
626 m_communication.msgPlayerPlay( m_appTransportId, m_mediaSessionId );
629 else
631 if ( !m_mediaSessionId.empty() && m_state != Paused )
633 m_communication.msgPlayerPause( m_appTransportId, m_mediaSessionId );
638 void intf_sys_t::waitAppStarted()
640 while ( m_state != Ready && m_state != Dead )
642 if ( m_state == Connected )
644 msg_Dbg( m_module, "Starting the media receiver application" );
645 // Don't use setState as we don't want to signal the condition in this case.
646 m_state = Launching;
647 m_communication.msgReceiverLaunchApp();
649 msg_Dbg( m_module, "Waiting for Chromecast media receiver app to be ready" );
650 vlc_cond_wait(&m_stateChangedCond, &m_lock);
652 msg_Dbg( m_module, "Done waiting for application. transportId: %s", m_appTransportId.c_str() );
655 void intf_sys_t::waitSeekDone()
657 vlc_mutex_locker locker(&m_lock);
658 while ( m_state == Seeking )
660 #ifndef NDEBUG
661 msg_Dbg( m_module, "waiting for Chromecast seek" );
662 #endif
663 vlc_cond_wait(&m_stateChangedCond, &m_lock);
664 #ifndef NDEBUG
665 msg_Dbg( m_module, "finished waiting for Chromecast seek" );
666 #endif
670 bool intf_sys_t::isFinishedPlaying()
672 vlc_mutex_locker locker(&m_lock);
673 return m_state == Ready;
676 void intf_sys_t::setTitle(const char* psz_title)
678 if ( psz_title )
679 m_title = psz_title;
680 else
681 m_title = "";
684 void intf_sys_t::setArtwork(const char* psz_artwork)
686 if ( psz_artwork )
687 m_artwork = psz_artwork;
688 else
689 m_artwork = "";
692 mtime_t intf_sys_t::getPlaybackTimestamp() const
694 switch( m_state )
696 case Playing:
697 return ( mdate() - m_time_playback_started ) + m_ts_local_start;
698 case Ready:
699 msg_Dbg(m_module, "receiver idle using buffering time %" PRId64, m_ts_local_start);
700 break;
701 case Buffering:
702 msg_Dbg(m_module, "receiver buffering using buffering time %" PRId64, m_ts_local_start);
703 break;
704 case Paused:
705 msg_Dbg(m_module, "receiver paused using buffering time %" PRId64, m_ts_local_start);
706 break;
707 default:
708 break;
710 return m_ts_local_start;
713 double intf_sys_t::getPlaybackPosition() const
715 if( m_length > 0 )
716 return (double) getPlaybackTimestamp() / (double)( m_length );
717 return 0.0;
720 void intf_sys_t::setState( States state )
722 if ( m_state != state )
724 #ifndef NDEBUG
725 msg_Dbg( m_module, "Switching from state %s to %s", StateToStr( m_state ), StateToStr( state ) );
726 #endif
727 m_state = state;
728 vlc_cond_signal( &m_stateChangedCond );
732 mtime_t intf_sys_t::get_time(void *pt)
734 intf_sys_t *p_this = static_cast<intf_sys_t*>(pt);
735 vlc_mutex_locker locker( &p_this->m_lock );
736 return p_this->getPlaybackTimestamp();
739 double intf_sys_t::get_position(void *pt)
741 intf_sys_t *p_this = static_cast<intf_sys_t*>(pt);
742 vlc_mutex_locker locker( &p_this->m_lock );
743 return p_this->getPlaybackPosition();
746 void intf_sys_t::set_length(void *pt, mtime_t length)
748 intf_sys_t *p_this = static_cast<intf_sys_t*>(pt);
749 p_this->m_length = length;
752 void intf_sys_t::wait_app_started(void *pt)
754 intf_sys_t *p_this = static_cast<intf_sys_t*>(pt);
755 vlc_mutex_locker locker( &p_this->m_lock);
756 p_this->waitAppStarted();
759 void intf_sys_t::request_seek(void *pt, mtime_t pos)
761 intf_sys_t *p_this = static_cast<intf_sys_t*>(pt);
762 p_this->requestPlayerSeek(pos);
765 void intf_sys_t::wait_seek_done(void *pt)
767 intf_sys_t *p_this = static_cast<intf_sys_t*>(pt);
768 p_this->waitSeekDone();
771 void intf_sys_t::set_pause_state(void *pt, bool paused)
773 intf_sys_t *p_this = static_cast<intf_sys_t*>(pt);
774 p_this->setPauseState( paused );
777 void intf_sys_t::set_title(void *pt, const char *psz_title)
779 intf_sys_t *p_this = static_cast<intf_sys_t*>(pt);
780 p_this->setTitle( psz_title );
783 void intf_sys_t::set_artwork(void *pt, const char *psz_artwork)
785 intf_sys_t *p_this = static_cast<intf_sys_t*>(pt);
786 p_this->setArtwork( psz_artwork );