1 /***************************************************************************** 2 * chromecast.cpp: Chromecast module for vlc 3 ***************************************************************************** 4 * Copyright © 2014-2015 VideoLAN 5 * 6 * Authors: Adrien Maglo <magsoft@videolan.org> 7 * Jean-Baptiste Kempf <jb@videolan.org> 8 * Steve Lhomme <robux4@videolabs.io> 9 * 10 * This program is free software; you can redistribute it and/or modify it 11 * under the terms of the GNU Lesser General Public License as published by 12 * the Free Software Foundation; either version 2.1 of the License, or 13 * (at your option) any later version. 14 * 15 * This program is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public License 21 * along with this program; if not, write to the Free Software Foundation, 22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. 23 *****************************************************************************/ 24 25 /***************************************************************************** 26 * Preamble 27 *****************************************************************************/ 28 29 #ifndef VLC_CHROMECAST_H 30 #define VLC_CHROMECAST_H 31 32 #include <vlc_common.h> 33 #include <vlc_plugin.h> 34 #include <vlc_tls.h> 35 #include <vlc_interrupt.h> 36 #include <vlc_httpd.h> 37 38 #include <atomic> 39 #include <sstream> 40 #include <queue> 41 42 #ifndef PROTOBUF_INLINE_NOT_IN_HEADERS 43 # define PROTOBUF_INLINE_NOT_IN_HEADERS 0 44 #endif 45 #include "cast_channel.pb.h" 46 #include "chromecast_common.h" 47 48 #define PACKET_HEADER_LEN 4 49 50 // Media player Chromecast app id 51 static const std::string DEFAULT_CHOMECAST_RECEIVER = "receiver-0"; 52 /* see https://developers.google.com/cast/docs/reference/messages */ 53 static const std::string NAMESPACE_MEDIA = "urn:x-cast:com.google.cast.media"; 54 static const std::string NAMESPACE_DEVICEAUTH = "urn:x-cast:com.google.cast.tp.deviceauth"; 55 static const std::string NAMESPACE_CONNECTION = "urn:x-cast:com.google.cast.tp.connection"; 56 static const std::string NAMESPACE_HEARTBEAT = "urn:x-cast:com.google.cast.tp.heartbeat"; 57 static const std::string NAMESPACE_RECEIVER = "urn:x-cast:com.google.cast.receiver"; 58 59 60 #define CHROMECAST_CONTROL_PORT 8009 61 #define HTTP_PORT 8010 62 63 #define PACKET_MAX_LEN 10 * 1024 64 65 //#define CHROMECAST_VERBOSE 66 67 // Media player Chromecast app id 68 #define APP_ID "CC1AD845" // Default media player aka DEFAULT_MEDIA_RECEIVER_APPLICATION_ID 69 70 enum States 71 { 72 // An authentication request has been sent 73 Authenticating, 74 // We are sending a connection request 75 Connecting, 76 // We are connected to the chromecast but the receiver app is not running. 77 Connected, 78 // We are launching the media receiver app 79 Launching, 80 // The application is ready, but idle 81 Ready, 82 // The chromecast rejected the media 83 LoadFailed, 84 // A media session is being initiated 85 Loading, 86 Buffering, 87 Playing, 88 Paused, 89 Stopping, 90 Stopped, 91 // Something went wrong and the connection is dead. 92 Dead, 93 // Another playback started on the same cast device 94 TakenOver, 95 }; 96 97 class ChromecastCommunication 98 { 99 public: 100 ChromecastCommunication( vlc_object_t* module, 101 std::string serverPath, unsigned int serverPort, 102 const char* targetIP, unsigned int devicePort ); 103 ~ChromecastCommunication(); 104 /** 105 * @brief disconnect close the connection with the chromecast 106 */ 107 void disconnect(); 108 109 static const unsigned kInvalidId = 0; 110 111 /* All msg*() methods return kInvalidId on error, 1 or the receiver/player 112 * request ID on success */ 113 114 unsigned msgPing(); 115 unsigned msgPong(); 116 unsigned msgConnect( const std::string& destinationId ); 117 118 unsigned msgReceiverLaunchApp(); 119 unsigned msgReceiverGetStatus(); 120 unsigned msgReceiverClose(const std::string& destinationId); 121 unsigned msgAuth(); 122 unsigned msgPlayerLoad( const std::string& destinationId, 123 const std::string& mime, const vlc_meta_t *p_meta ); 124 unsigned msgPlayerPlay( const std::string& destinationId, int64_t mediaSessionId ); 125 unsigned msgPlayerStop( const std::string& destinationId, int64_t mediaSessionId ); 126 unsigned msgPlayerPause( const std::string& destinationId, int64_t mediaSessionId ); 127 unsigned msgPlayerGetStatus( const std::string& destinationId ); 128 unsigned msgPlayerSeek( const std::string& destinationId, int64_t mediaSessionId, 129 const std::string & currentTime ); 130 unsigned msgPlayerSetVolume( const std::string& destinationId, int64_t mediaSessionId, 131 float volume, bool mute); 132 ssize_t receive( uint8_t *p_data, size_t i_size, int i_timeout, bool *pb_timeout ); 133 getServerIp()134 const std::string getServerIp() 135 { 136 return m_serverIp; 137 } 138 private: 139 int sendMessage(const castchannel::CastMessage &msg); 140 141 int buildMessage(const std::string & namespace_, 142 const std::string & payload, 143 const std::string & destinationId = DEFAULT_CHOMECAST_RECEIVER, 144 castchannel::CastMessage_PayloadType payloadType = castchannel::CastMessage_PayloadType_STRING); 145 int pushMediaPlayerMessage( const std::string& destinationId, const std::stringstream & payload ); 146 std::string GetMedia( const std::string& mime, const vlc_meta_t *p_meta ); 147 unsigned getNextReceiverRequestId(); 148 unsigned getNextRequestId(); 149 150 private: 151 vlc_object_t* m_module; 152 vlc_tls_creds_t *m_creds; 153 vlc_tls_t *m_tls; 154 unsigned m_receiver_requestId; 155 unsigned m_requestId; 156 std::string m_serverIp; 157 const std::string m_serverPath; 158 const unsigned m_serverPort; 159 }; 160 161 /***************************************************************************** 162 * intf_sys_t: description and status of interface 163 *****************************************************************************/ 164 struct intf_sys_t 165 { 166 enum QueueableMessages 167 { 168 Stop, 169 }; 170 intf_sys_t(vlc_object_t * const p_this, int local_port, std::string device_addr, 171 int device_port, httpd_host_t *); 172 ~intf_sys_t(); 173 174 void setRetryOnFail(bool); 175 void setHasInput(const std::string mime_type = ""); 176 177 void setOnInputEventCb(on_input_event_itf on_input_event, void *on_input_event_data); 178 void setDemuxEnabled(bool enabled, on_paused_changed_itf on_paused_changed, 179 void *on_paused_changed_data); 180 void requestPlayerStop(); 181 States state() const; 182 183 void setPacing(bool do_pace); 184 int pace(); 185 void sendInputEvent(enum cc_input_event event, union cc_input_arg arg); 186 mtime_t getPauseDelay(); 187 188 unsigned int getHttpStreamPort() const; 189 std::string getHttpStreamPath() const; 190 std::string getHttpArtRoot() const; 191 192 int httpd_file_fill( uint8_t *psz_request, uint8_t **pp_data, int *pi_data ); 193 void interrupt_wake_up(); 194 private: 195 void reinit(); 196 bool handleMessages(); 197 198 bool processMessage(const castchannel::CastMessage &msg); 199 void queueMessage( QueueableMessages msg ); 200 201 void setPauseState(bool paused); 202 bool isFinishedPlaying(); 203 bool isStateError() const; 204 bool isStatePlaying() const; 205 bool isStateReady() const; 206 void tryLoad(); 207 void doStop(); 208 209 void setMeta( vlc_meta_t *p_meta ); 210 211 mtime_t getPlaybackTimestamp(); 212 213 double getPlaybackPosition() const; 214 215 void setInitialTime( mtime_t time ); 216 // Sets the current state and signal the associated wait cond. 217 // This must be called with the lock held 218 void setState( States state ); 219 220 void mainLoop(); 221 void processAuthMessage( const castchannel::CastMessage& msg ); 222 void processHeartBeatMessage( const castchannel::CastMessage& msg ); 223 bool processReceiverMessage( const castchannel::CastMessage& msg ); 224 void processMediaMessage( const castchannel::CastMessage& msg ); 225 void processConnectionMessage( const castchannel::CastMessage& msg ); 226 227 private: 228 static void* ChromecastThread(void* p_data); 229 230 static mtime_t get_time(void*); 231 232 static int pace(void*); 233 static void send_input_event(void *, enum cc_input_event event, union cc_input_arg arg); 234 static void set_demux_enabled(void *, bool, on_paused_changed_itf, void *); 235 236 static void set_pause_state(void*, bool paused); 237 238 static void set_meta(void*, vlc_meta_t *p_meta); 239 240 void prepareHttpArtwork(); 241 242 static mtime_t timeCCToVLC(double); 243 static std::string timeVLCToCC(mtime_t); 244 245 private: 246 vlc_object_t * const m_module; 247 const int m_device_port; 248 std::string m_mime; 249 std::string m_device_addr; 250 251 std::string m_appTransportId; 252 unsigned m_last_request_id; 253 int64_t m_mediaSessionId; 254 255 mutable vlc_mutex_t m_lock; 256 vlc_cond_t m_stateChangedCond; 257 vlc_cond_t m_pace_cond; 258 vlc_thread_t m_chromecastThread; 259 260 on_input_event_itf m_on_input_event; 261 void *m_on_input_event_data; 262 263 on_paused_changed_itf m_on_paused_changed; 264 void *m_on_paused_changed_data; 265 266 ChromecastCommunication *m_communication; 267 std::queue<QueueableMessages> m_msgQueue; 268 States m_state; 269 bool m_retry_on_fail; 270 bool m_played_once; 271 bool m_request_stop; 272 bool m_request_load; 273 bool m_paused; 274 bool m_input_eof; 275 bool m_cc_eof; 276 bool m_pace; 277 bool m_interrupted; 278 279 vlc_meta_t *m_meta; 280 281 vlc_interrupt_t *m_ctl_thread_interrupt; 282 283 struct httpd_info_t { 284 httpd_info_t( httpd_host_t* host, int port ); 285 ~httpd_info_t(); 286 287 httpd_host_t *m_host; 288 int m_port; 289 httpd_url_t *m_url; 290 std::string m_root; 291 } const m_httpd; 292 293 httpd_file_t *m_httpd_file; 294 std::string m_art_http_ip; 295 char *m_art_url; 296 unsigned m_art_idx; 297 298 mtime_t m_cc_time_last_request_date; 299 mtime_t m_cc_time_date; 300 mtime_t m_cc_time; 301 302 /* shared structure with the demux-filter */ 303 chromecast_common m_common; 304 305 /* Heartbeat */ 306 uint8_t m_pingRetriesLeft; 307 }; 308 309 #endif /* VLC_CHROMECAST_H */ 310