chromecast: send/transcode only one audio/video track
[vlc.git] / modules / stream_out / chromecast / cast.cpp
blob46dc3ddcfcf86c712d29159a02d21c4f59d66da3
1 /*****************************************************************************
2 * cast.cpp: Chromecast sout 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>
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.
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.
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 *****************************************************************************/
25 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
33 #include "chromecast.h"
34 #include <vlc_dialog.h>
36 #include <vlc_sout.h>
37 #include <vlc_block.h>
38 #include <vlc_modules.h>
40 #include <cassert>
42 struct sout_stream_sys_t
44 sout_stream_sys_t(intf_sys_t * const intf, bool has_video, int port,
45 const char *psz_default_muxer, const char *psz_default_mime)
46 : p_out(NULL)
47 , default_muxer(psz_default_muxer)
48 , default_mime(psz_default_mime)
49 , p_intf(intf)
50 , b_supports_video(has_video)
51 , i_port(port)
52 , es_changed( true )
53 , transcode_attempt_idx( 0 )
54 , previous_state( Authenticating )
56 assert(p_intf != NULL);
59 ~sout_stream_sys_t()
61 sout_StreamChainDelete(p_out, NULL);
62 delete p_intf;
65 bool canDecodeVideo( vlc_fourcc_t i_codec ) const;
66 bool canDecodeAudio( sout_stream_t* p_stream, vlc_fourcc_t i_codec,
67 const audio_format_t* p_fmt ) const;
68 bool startSoutChain(sout_stream_t* p_stream);
70 sout_stream_t *p_out;
71 std::string sout;
72 const std::string default_muxer;
73 const std::string default_mime;
75 intf_sys_t * const p_intf;
76 const bool b_supports_video;
77 const int i_port;
79 sout_stream_id_sys_t *GetSubId( sout_stream_t*, sout_stream_id_sys_t* );
81 bool es_changed;
82 std::vector<sout_stream_id_sys_t*> streams;
83 std::vector<sout_stream_id_sys_t*> out_streams;
84 unsigned int transcode_attempt_idx;
85 States previous_state;
87 private:
88 bool UpdateOutput( sout_stream_t * );
91 #define SOUT_CFG_PREFIX "sout-chromecast-"
93 static const vlc_fourcc_t DEFAULT_TRANSCODE_VIDEO = VLC_CODEC_H264;
94 static const unsigned int MAX_TRANSCODE_PASS = 3;
95 static const char DEFAULT_MUXER[] = "avformat{mux=matroska,options={live=1}}}";
98 /*****************************************************************************
99 * Local prototypes
100 *****************************************************************************/
101 static int Open(vlc_object_t *);
102 static void Close(vlc_object_t *);
104 static const char *const ppsz_sout_options[] = {
105 "ip", "port", "http-port", "mux", "mime", "video", NULL
108 /*****************************************************************************
109 * Module descriptor
110 *****************************************************************************/
112 #define HTTP_PORT_TEXT N_("HTTP port")
113 #define HTTP_PORT_LONGTEXT N_("This sets the HTTP port of the local server " \
114 "used to stream the media to the Chromecast.")
115 #define HAS_VIDEO_TEXT N_("Video")
116 #define HAS_VIDEO_LONGTEXT N_("The Chromecast receiver can receive video.")
117 #define MUX_TEXT N_("Muxer")
118 #define MUX_LONGTEXT N_("This sets the muxer used to stream to the Chromecast.")
119 #define MIME_TEXT N_("MIME content type")
120 #define MIME_LONGTEXT N_("This sets the media MIME content type sent to the Chromecast.")
121 #define PERF_TEXT N_( "Performance warning" )
122 #define PERF_LONGTEXT N_( "Display a performance warning when transcoding" )
123 #define AUDIO_PASSTHROUGH_TEXT N_( "Enable Audio passthrough" )
124 #define AUDIO_PASSTHROUGH_LONGTEXT N_( "Disable if your receiver does not support Dolby®" )
126 enum {
127 CONVERSION_QUALITY_HIGH = 0,
128 CONVERSION_QUALITY_MEDIUM = 1,
129 CONVERSION_QUALITY_LOW = 2,
130 CONVERSION_QUALITY_LOWCPU = 3,
133 #if defined (__ANDROID__) || defined (__arm__) || (defined (TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
134 # define CONVERSION_QUALITY_DEFAULT CONVERSION_QUALITY_LOW
135 #else
136 # define CONVERSION_QUALITY_DEFAULT CONVERSION_QUALITY_MEDIUM
137 #endif
139 static const int conversion_quality_list[] = {
140 CONVERSION_QUALITY_HIGH,
141 CONVERSION_QUALITY_MEDIUM,
142 CONVERSION_QUALITY_LOW,
143 CONVERSION_QUALITY_LOWCPU,
145 static const char *const conversion_quality_list_text[] = {
146 N_( "High (high quality and high bandwith)" ),
147 N_( "Medium (medium quality and medium bandwidth)" ),
148 N_( "Low (low quality and low bandwith)" ),
149 N_( "Low CPU (low quality but high bandwith)" ),
152 #define CONVERSION_QUALITY_TEXT N_( "Conversion quality" )
153 #define CONVERSION_QUALITY_LONGTEXT N_( "Change this option to increase conversion speed or quality." )
155 #define IP_ADDR_TEXT N_("IP Address")
156 #define IP_ADDR_LONGTEXT N_("IP Address of the Chromecast.")
157 #define PORT_TEXT N_("Chromecast port")
158 #define PORT_LONGTEXT N_("The port used to talk to the Chromecast.")
160 vlc_module_begin ()
162 set_shortname(N_("Chromecast"))
163 set_description(N_("Chromecast stream output"))
164 set_capability("sout stream", 0)
165 add_shortcut("chromecast")
166 set_category(CAT_SOUT)
167 set_subcategory(SUBCAT_SOUT_STREAM)
168 set_callbacks(Open, Close)
170 add_string(SOUT_CFG_PREFIX "ip", NULL, IP_ADDR_TEXT, IP_ADDR_LONGTEXT, false)
171 change_private()
172 add_integer(SOUT_CFG_PREFIX "port", CHROMECAST_CONTROL_PORT, PORT_TEXT, PORT_LONGTEXT, false)
173 change_private()
174 add_integer(SOUT_CFG_PREFIX "http-port", HTTP_PORT, HTTP_PORT_TEXT, HTTP_PORT_LONGTEXT, false)
175 add_bool(SOUT_CFG_PREFIX "video", true, HAS_VIDEO_TEXT, HAS_VIDEO_LONGTEXT, false)
176 add_string(SOUT_CFG_PREFIX "mux", DEFAULT_MUXER, MUX_TEXT, MUX_LONGTEXT, false)
177 add_string(SOUT_CFG_PREFIX "mime", "video/x-matroska", MIME_TEXT, MIME_LONGTEXT, false)
178 add_integer(SOUT_CFG_PREFIX "show-perf-warning", 1, PERF_TEXT, PERF_LONGTEXT, true )
179 change_private()
180 add_bool(SOUT_CFG_PREFIX "audio-passthrough", true, AUDIO_PASSTHROUGH_TEXT, AUDIO_PASSTHROUGH_LONGTEXT, false )
181 add_integer(SOUT_CFG_PREFIX "conversion-quality", CONVERSION_QUALITY_DEFAULT,
182 CONVERSION_QUALITY_TEXT, CONVERSION_QUALITY_LONGTEXT, false );
183 change_integer_list(conversion_quality_list, conversion_quality_list_text)
185 vlc_module_end ()
188 struct sout_stream_id_sys_t
190 es_format_t fmt;
191 sout_stream_id_sys_t *p_sub_id;
194 /*****************************************************************************
195 * Sout callbacks
196 *****************************************************************************/
197 static sout_stream_id_sys_t *Add(sout_stream_t *p_stream, const es_format_t *p_fmt)
199 sout_stream_sys_t *p_sys = p_stream->p_sys;
201 if (!p_sys->b_supports_video)
203 if (p_fmt->i_cat != AUDIO_ES)
204 return NULL;
207 sout_stream_id_sys_t *p_sys_id = (sout_stream_id_sys_t *)malloc( sizeof(sout_stream_id_sys_t) );
208 if (p_sys_id != NULL)
210 es_format_Copy( &p_sys_id->fmt, p_fmt );
211 p_sys_id->p_sub_id = NULL;
213 p_sys->streams.push_back( p_sys_id );
214 p_sys->es_changed = true;
216 return p_sys_id;
220 static void Del(sout_stream_t *p_stream, sout_stream_id_sys_t *id)
222 sout_stream_sys_t *p_sys = p_stream->p_sys;
224 for (std::vector<sout_stream_id_sys_t*>::iterator it = p_sys->streams.begin();
225 it != p_sys->streams.end(); )
227 sout_stream_id_sys_t *p_sys_id = *it;
228 if ( p_sys_id == id )
230 if ( p_sys_id->p_sub_id != NULL )
232 sout_StreamIdDel( p_sys->p_out, p_sys_id->p_sub_id );
233 for (std::vector<sout_stream_id_sys_t*>::iterator out_it = p_sys->out_streams.begin();
234 out_it != p_sys->out_streams.end(); )
236 if (*out_it == id)
238 p_sys->out_streams.erase(out_it);
239 break;
241 out_it++;
245 es_format_Clean( &p_sys_id->fmt );
246 free( p_sys_id );
247 p_sys->streams.erase( it );
248 p_sys->es_changed = true;
249 break;
251 it++;
254 if ( p_sys->out_streams.empty() )
256 p_sys->p_intf->requestPlayerStop();
258 sout_StreamChainDelete( p_sys->p_out, NULL );
259 p_sys->p_out = NULL;
260 p_sys->sout = "";
261 p_sys->transcode_attempt_idx = 0;
266 * Transcode steps:
267 * 0: Accept HEVC/VP9 & all supported audio formats
268 * 1: Transcode to h264 & accept all supported audio formats if the video codec
269 * was HEVC/VP9
270 * 2: Transcode to H264 & MP3
272 * Additionally:
273 * - Allow (E)AC3 passthrough depending on the audio-passthrough
274 * config value, except for the final step, where we just give up and transcode
275 * everything.
276 * - Disallow multichannel AAC
278 * Supported formats: https://developers.google.com/cast/docs/media
281 bool sout_stream_sys_t::canDecodeVideo( vlc_fourcc_t i_codec ) const
283 if ( transcode_attempt_idx != 0 )
284 return false;
285 if ( i_codec == VLC_CODEC_HEVC || i_codec == VLC_CODEC_VP9 )
286 return transcode_attempt_idx == 0;
287 return i_codec == VLC_CODEC_H264 || i_codec == VLC_CODEC_VP8;
290 bool sout_stream_sys_t::canDecodeAudio( sout_stream_t *p_stream,
291 vlc_fourcc_t i_codec,
292 const audio_format_t* p_fmt ) const
294 if ( transcode_attempt_idx == MAX_TRANSCODE_PASS - 1 )
295 return false;
296 if ( i_codec == VLC_CODEC_A52 || i_codec == VLC_CODEC_EAC3 )
298 return var_InheritBool( p_stream, SOUT_CFG_PREFIX "audio-passthrough" );
300 if ( i_codec == VLC_FOURCC('h', 'a', 'a', 'c') ||
301 i_codec == VLC_FOURCC('l', 'a', 'a', 'c') ||
302 i_codec == VLC_FOURCC('s', 'a', 'a', 'c') ||
303 i_codec == VLC_CODEC_MPGA ||
304 i_codec == VLC_CODEC_MP4A )
306 return p_fmt->i_channels <= 2;
308 return i_codec == VLC_CODEC_VORBIS || i_codec == VLC_CODEC_OPUS ||
309 i_codec == VLC_CODEC_MP3;
312 bool sout_stream_sys_t::startSoutChain( sout_stream_t *p_stream )
314 if ( unlikely( p_out != NULL ) )
316 for ( size_t i = 0; i < streams.size(); i++ )
318 if ( streams[i]->p_sub_id != NULL )
319 sout_StreamIdDel( p_out, streams[i]->p_sub_id );
321 sout_StreamChainDelete( p_out, NULL );
324 msg_Dbg( p_stream, "Creating chain %s", sout.c_str() );
325 p_out = sout_StreamChainNew( p_stream->p_sout, sout.c_str(), NULL, NULL);
326 if (p_out == NULL) {
327 msg_Dbg(p_stream, "could not create sout chain:%s", sout.c_str());
328 return false;
331 /* check the streams we can actually add */
332 for (std::vector<sout_stream_id_sys_t*>::iterator it = out_streams.begin();
333 it != out_streams.end(); )
335 sout_stream_id_sys_t *p_sys_id = *it;
336 p_sys_id->p_sub_id = sout_StreamIdAdd( p_out, &p_sys_id->fmt );
337 if ( p_sys_id->p_sub_id == NULL )
339 msg_Err( p_stream, "can't handle %4.4s stream", (char *)&p_sys_id->fmt.i_codec );
340 es_format_Clean( &p_sys_id->fmt );
341 it = out_streams.erase( it );
343 else
344 ++it;
346 return out_streams.empty() == false;
349 bool sout_stream_sys_t::UpdateOutput( sout_stream_t *p_stream )
351 assert( p_stream->p_sys == this );
353 if ( !es_changed )
354 return true;
356 es_changed = false;
358 bool canRemux = true;
359 vlc_fourcc_t i_codec_video = 0, i_codec_audio = 0;
360 const es_format_t *p_original_audio = NULL;
361 const es_format_t *p_original_video = NULL;
362 out_streams.clear();
364 for (std::vector<sout_stream_id_sys_t*>::iterator it = streams.begin(); it != streams.end(); ++it)
366 const es_format_t *p_es = &(*it)->fmt;
367 if (p_es->i_cat == AUDIO_ES && p_original_audio == NULL)
369 if ( !canDecodeAudio( p_stream, p_es->i_codec, &p_es->audio ) )
371 msg_Dbg( p_stream, "can't remux audio track %d codec %4.4s", p_es->i_id, (const char*)&p_es->i_codec );
372 canRemux = false;
374 else if (i_codec_audio == 0)
375 i_codec_audio = p_es->i_codec;
376 p_original_audio = p_es;
377 out_streams.push_back(*it);
379 else if (b_supports_video)
381 if (p_es->i_cat == VIDEO_ES && p_original_video == NULL)
383 if (!canDecodeVideo( p_es->i_codec ))
385 msg_Dbg( p_stream, "can't remux video track %d codec %4.4s",
386 p_es->i_id, (const char*)&p_es->i_codec );
387 canRemux = false;
389 else if (i_codec_video == 0)
390 i_codec_video = p_es->i_codec;
391 p_original_video = p_es;
392 out_streams.push_back(*it);
394 /* TODO: else handle ttml/webvtt */
398 if (out_streams.empty())
399 return true;
401 std::stringstream ssout;
402 if ( !canRemux )
404 if ( var_InheritInteger( p_stream, SOUT_CFG_PREFIX "show-perf-warning" ) )
406 int res = vlc_dialog_wait_question( p_stream,
407 VLC_DIALOG_QUESTION_WARNING,
408 _("Cancel"), _("OK"), _("Ok, Don't warn me again"),
409 _("Performance warning"),
410 _("Casting this video requires conversion. "
411 "This conversion can use all the available power and "
412 "could quickly drain your battery." ) );
413 if ( res <= 0 )
414 return false;
415 if ( res == 2 )
416 config_PutInt(p_stream, SOUT_CFG_PREFIX "show-perf-warning", 0 );
419 static const char video_maxres_hd[] = "maxwidth=1920,maxheight=1080";
420 static const char video_maxres_720p[] = "maxwidth=1280,maxheight=720";
421 static const char video_x264_preset_veryfast[] = "veryfast";
422 static const char video_x264_preset_ultrafast[] = "ultrafast";
424 const int i_quality = var_InheritInteger( p_stream, SOUT_CFG_PREFIX "conversion-quality" );
425 const char *psz_video_maxres;
426 const char *psz_video_x264_preset;
427 unsigned i_video_x264_crf_hd, i_video_x264_crf_720p;
428 bool b_audio_mp3;
430 switch ( i_quality )
432 case CONVERSION_QUALITY_HIGH:
433 psz_video_maxres = video_maxres_hd;
434 i_video_x264_crf_hd = i_video_x264_crf_720p = 21;
435 psz_video_x264_preset = video_x264_preset_veryfast;
436 b_audio_mp3 = false;
437 break;
438 case CONVERSION_QUALITY_MEDIUM:
439 psz_video_maxres = video_maxres_hd;
440 i_video_x264_crf_hd = 23;
441 i_video_x264_crf_720p = 21;
442 psz_video_x264_preset = video_x264_preset_veryfast;
443 b_audio_mp3 = false;
444 break;
445 case CONVERSION_QUALITY_LOW:
446 psz_video_maxres = video_maxres_720p;
447 i_video_x264_crf_hd = i_video_x264_crf_720p = 23;
448 psz_video_x264_preset = video_x264_preset_veryfast;
449 b_audio_mp3 = true;
450 break;
451 default:
452 case CONVERSION_QUALITY_LOWCPU:
453 psz_video_maxres = video_maxres_720p;
454 i_video_x264_crf_hd = i_video_x264_crf_720p = 23;
455 psz_video_x264_preset = video_x264_preset_ultrafast;
456 b_audio_mp3 = true;
457 break;
460 /* TODO: provide audio samplerate and channels */
461 ssout << "transcode{";
462 char s_fourcc[5];
463 if ( i_codec_audio == 0 && p_original_audio )
465 if ( !b_audio_mp3
466 && p_original_audio->audio.i_channels > 2 && module_exists( "vorbis" ) )
467 i_codec_audio = VLC_CODEC_VORBIS;
468 else
469 i_codec_audio = VLC_CODEC_MP3;
471 msg_Dbg( p_stream, "Converting audio to %.4s", (const char*)&i_codec_audio );
472 ssout << "acodec=";
473 vlc_fourcc_to_char( i_codec_audio, s_fourcc );
474 s_fourcc[4] = '\0';
475 ssout << s_fourcc << ',';
476 if( i_codec_audio == VLC_CODEC_VORBIS )
477 ssout << "aenc=vorbis{quality=6},";
479 if ( b_supports_video && i_codec_video == 0 )
481 i_codec_video = DEFAULT_TRANSCODE_VIDEO;
482 msg_Dbg( p_stream, "Converting video to %.4s", (const char*)&i_codec_video );
483 ssout << "vcodec=";
484 vlc_fourcc_to_char( i_codec_video, s_fourcc );
485 s_fourcc[4] = '\0';
486 ssout << s_fourcc << ',' << psz_video_maxres << ',';
488 const video_format_t *p_vid =
489 p_original_video ? &p_original_video->video : NULL;
490 const bool b_hdres = p_vid == NULL || p_vid->i_height == 0 || p_vid->i_height >= 800;
491 unsigned i_video_x264_crf = b_hdres ? i_video_x264_crf_hd : i_video_x264_crf_720p;
493 if( p_vid == NULL
494 || p_vid->i_frame_rate == 0 || p_vid->i_frame_rate_base == 0
495 || ( p_vid->i_frame_rate / p_vid->i_frame_rate_base ) > 30 )
497 /* Even force 24fps if the frame rate is unknown */
498 msg_Warn( p_stream, "lowering frame rate to 24fps" );
499 ssout << "fps=24,";
502 if( i_codec_video == VLC_CODEC_H264 )
504 if ( module_exists("x264") )
505 ssout << "venc=x264{preset=" << psz_video_x264_preset
506 << ",crf=" << i_video_x264_crf << "},";
509 ssout << "}:";
511 std::string mime;
512 if ( !b_supports_video && default_muxer == DEFAULT_MUXER )
513 mime = "audio/x-matroska";
514 else if ( i_codec_audio == VLC_CODEC_VORBIS &&
515 i_codec_video == VLC_CODEC_VP8 &&
516 default_muxer == DEFAULT_MUXER )
517 mime = "video/webm";
518 else
519 mime = default_mime;
521 ssout << "http{dst=:" << i_port << "/stream"
522 << ",mux=" << default_muxer
523 << ",access=http{mime=" << mime << "}}";
525 sout = ssout.str();
527 if ( startSoutChain( p_stream ) )
529 /* tell the chromecast to load the content */
530 p_intf->setHasInput( mime );
532 else
534 p_intf->requestPlayerStop();
536 sout_StreamChainDelete( p_out, NULL );
537 p_out = NULL;
538 sout = "";
540 return true;
543 sout_stream_id_sys_t *sout_stream_sys_t::GetSubId( sout_stream_t *p_stream,
544 sout_stream_id_sys_t *id )
546 size_t i;
548 assert( p_stream->p_sys == this );
550 if ( UpdateOutput( p_stream ) == false )
551 return NULL;
553 for (i = 0; i < streams.size(); ++i)
555 if ( id == (sout_stream_id_sys_t*) streams[i] )
556 return streams[i]->p_sub_id;
559 msg_Err( p_stream, "unknown stream ID" );
560 return NULL;
563 static int Send(sout_stream_t *p_stream, sout_stream_id_sys_t *id,
564 block_t *p_buffer)
566 sout_stream_sys_t *p_sys = p_stream->p_sys;
568 id = p_sys->GetSubId( p_stream, id );
569 if ( id == NULL )
571 block_Release( p_buffer );
572 return VLC_EGENERIC;
574 States s = p_sys->p_intf->state();
575 if ( p_sys->previous_state != s )
577 if ( s == LoadFailed && p_sys->es_changed == false )
579 if ( p_sys->transcode_attempt_idx > MAX_TRANSCODE_PASS - 1 )
581 msg_Err( p_stream, "All attempts failed. Giving up." );
582 block_Release( p_buffer );
583 return VLC_EGENERIC;
585 p_sys->transcode_attempt_idx++;
586 p_sys->es_changed = true;
587 msg_Warn( p_stream, "Load failed detected. Switching to next "
588 "configuration index: %u", p_sys->transcode_attempt_idx );
590 else if ( s == Playing || s == Paused )
592 msg_Dbg( p_stream, "Playback started: Current configuration (%u) "
593 "accepted", p_sys->transcode_attempt_idx );
595 p_sys->previous_state = s;
598 return sout_StreamIdSend(p_sys->p_out, id, p_buffer);
601 static void Flush( sout_stream_t *p_stream, sout_stream_id_sys_t *id )
603 sout_stream_sys_t *p_sys = p_stream->p_sys;
605 id = p_sys->GetSubId( p_stream, id );
606 if ( id == NULL )
607 return;
609 /* a seek on the Chromecast flushes its buffers */
610 p_sys->p_intf->requestPlayerSeek( VLC_TS_INVALID );
612 sout_StreamFlush( p_sys->p_out, id );
615 static int Control(sout_stream_t *p_stream, int i_query, va_list args)
617 sout_stream_sys_t *p_sys = p_stream->p_sys;
619 if (i_query == SOUT_STREAM_EMPTY)
621 bool *b = va_arg( args, bool * );
622 /* check if the Chromecast to be done playing */
623 *b = p_sys->p_intf == NULL || p_sys->p_intf->isFinishedPlaying();
624 return VLC_SUCCESS;
627 if ( !p_sys->p_out->pf_control )
628 return VLC_EGENERIC;
630 return p_sys->p_out->pf_control( p_sys->p_out, i_query, args );
633 /*****************************************************************************
634 * Open: connect to the Chromecast and initialize the sout
635 *****************************************************************************/
636 static int Open(vlc_object_t *p_this)
638 sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(p_this);
639 sout_stream_sys_t *p_sys = NULL;
640 intf_sys_t *p_intf = NULL;
641 char *psz_ip = NULL;
642 char *psz_mux = NULL;
643 char *psz_var_mime = NULL;
644 sout_stream_t *p_sout = NULL;
645 bool b_supports_video = true;
646 int i_local_server_port;
647 int i_device_port;
648 std::stringstream ss;
650 vlc_interrupt_t *p_interrupt = vlc_interrupt_create();
651 if (unlikely(p_interrupt == NULL))
652 goto error;
654 config_ChainParse(p_stream, SOUT_CFG_PREFIX, ppsz_sout_options, p_stream->p_cfg);
656 psz_ip = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "ip");
657 if ( psz_ip == NULL )
659 msg_Err( p_this, "missing Chromecast IP address" );
660 goto error;
663 i_device_port = var_InheritInteger(p_stream, SOUT_CFG_PREFIX "port");
664 i_local_server_port = var_InheritInteger(p_stream, SOUT_CFG_PREFIX "http-port");
668 p_intf = new intf_sys_t( p_this, i_local_server_port, psz_ip, i_device_port, p_interrupt );
670 catch (const std::runtime_error& err )
672 msg_Err( p_this, "cannot load the Chromecast controller (%s)", err.what() );
673 goto error;
675 catch (const std::bad_alloc& )
677 p_intf = NULL;
678 goto error;
681 p_interrupt = NULL;
683 psz_mux = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX "mux");
684 if (psz_mux == NULL)
686 goto error;
688 psz_var_mime = var_GetNonEmptyString(p_stream, SOUT_CFG_PREFIX "mime");
689 if (psz_var_mime == NULL)
690 goto error;
692 /* check if we can open the proper sout */
693 ss << "http{dst=:" << i_local_server_port << "/stream"
694 << ",mux=" << psz_mux
695 << ",access=http{mime=" << psz_var_mime << "}}";
697 p_sout = sout_StreamChainNew( p_stream->p_sout, ss.str().c_str(), NULL, NULL);
698 if (p_sout == NULL) {
699 msg_Dbg(p_stream, "could not create sout chain:%s", ss.str().c_str());
700 goto error;
702 sout_StreamChainDelete( p_sout, NULL );
704 b_supports_video = var_GetBool(p_stream, SOUT_CFG_PREFIX "video");
706 p_sys = new(std::nothrow) sout_stream_sys_t( p_intf, b_supports_video, i_local_server_port,
707 psz_mux, psz_var_mime );
708 if (unlikely(p_sys == NULL))
709 goto error;
711 // Set the sout callbacks.
712 p_stream->pf_add = Add;
713 p_stream->pf_del = Del;
714 p_stream->pf_send = Send;
715 p_stream->pf_flush = Flush;
716 p_stream->pf_control = Control;
718 p_stream->p_sys = p_sys;
719 free(psz_ip);
720 free(psz_mux);
721 free(psz_var_mime);
722 return VLC_SUCCESS;
724 error:
725 if (p_interrupt)
726 vlc_interrupt_destroy(p_interrupt);
727 delete p_intf;
728 free(psz_ip);
729 free(psz_mux);
730 free(psz_var_mime);
731 delete p_sys;
732 return VLC_EGENERIC;
735 /*****************************************************************************
736 * Close: destroy interface
737 *****************************************************************************/
738 static void Close(vlc_object_t *p_this)
740 sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(p_this);
742 delete p_stream->p_sys;