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 /*****************************************************************************
27 *****************************************************************************/
33 #include "chromecast.h"
34 #include <vlc_dialog.h>
37 #include <vlc_block.h>
38 #include <vlc_modules.h>
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
)
47 , default_muxer(psz_default_muxer
)
48 , default_mime(psz_default_mime
)
50 , b_supports_video(has_video
)
53 , transcode_attempt_idx( 0 )
54 , previous_state( Authenticating
)
56 assert(p_intf
!= NULL
);
61 sout_StreamChainDelete(p_out
, NULL
);
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
);
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
;
79 sout_stream_id_sys_t
*GetSubId( sout_stream_t
*, sout_stream_id_sys_t
* );
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
;
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 /*****************************************************************************
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 /*****************************************************************************
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®" )
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
136 # define CONVERSION_QUALITY_DEFAULT CONVERSION_QUALITY_MEDIUM
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.")
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)
172 add_integer(SOUT_CFG_PREFIX
"port", CHROMECAST_CONTROL_PORT
, PORT_TEXT
, PORT_LONGTEXT
, false)
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 )
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
)
188 struct sout_stream_id_sys_t
191 sout_stream_id_sys_t
*p_sub_id
;
194 /*****************************************************************************
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
)
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;
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(); )
238 p_sys
->out_streams
.erase(out_it
);
245 es_format_Clean( &p_sys_id
->fmt
);
247 p_sys
->streams
.erase( it
);
248 p_sys
->es_changed
= true;
254 if ( p_sys
->out_streams
.empty() )
256 p_sys
->p_intf
->requestPlayerStop();
258 sout_StreamChainDelete( p_sys
->p_out
, NULL
);
261 p_sys
->transcode_attempt_idx
= 0;
267 * 0: Accept HEVC/VP9 & all supported audio formats
268 * 1: Transcode to h264 & accept all supported audio formats if the video codec
270 * 2: Transcode to H264 & MP3
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
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 )
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 )
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
);
327 msg_Dbg(p_stream
, "could not create sout chain:%s", sout
.c_str());
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
);
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 );
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
;
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
);
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
);
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())
401 std::stringstream ssout
;
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." ) );
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
;
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
;
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
;
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
;
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
;
460 /* TODO: provide audio samplerate and channels */
461 ssout
<< "transcode{";
463 if ( i_codec_audio
== 0 && p_original_audio
)
466 && p_original_audio
->audio
.i_channels
> 2 && module_exists( "vorbis" ) )
467 i_codec_audio
= VLC_CODEC_VORBIS
;
469 i_codec_audio
= VLC_CODEC_MP3
;
471 msg_Dbg( p_stream
, "Converting audio to %.4s", (const char*)&i_codec_audio
);
473 vlc_fourcc_to_char( i_codec_audio
, s_fourcc
);
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
);
484 vlc_fourcc_to_char( i_codec_video
, s_fourcc
);
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
;
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" );
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
<< "},";
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
)
521 ssout
<< "http{dst=:" << i_port
<< "/stream"
522 << ",mux=" << default_muxer
523 << ",access=http{mime=" << mime
<< "}}";
527 if ( startSoutChain( p_stream
) )
529 /* tell the chromecast to load the content */
530 p_intf
->setHasInput( mime
);
534 p_intf
->requestPlayerStop();
536 sout_StreamChainDelete( p_out
, NULL
);
543 sout_stream_id_sys_t
*sout_stream_sys_t::GetSubId( sout_stream_t
*p_stream
,
544 sout_stream_id_sys_t
*id
)
548 assert( p_stream
->p_sys
== this );
550 if ( UpdateOutput( p_stream
) == false )
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" );
563 static int Send(sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
,
566 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
568 id
= p_sys
->GetSubId( p_stream
, id
);
571 block_Release( p_buffer
);
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
);
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
);
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();
627 if ( !p_sys
->p_out
->pf_control
)
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
;
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
;
648 std::stringstream ss
;
650 vlc_interrupt_t
*p_interrupt
= vlc_interrupt_create();
651 if (unlikely(p_interrupt
== NULL
))
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" );
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() );
675 catch (const std::bad_alloc
& )
683 psz_mux
= var_GetNonEmptyString(p_stream
, SOUT_CFG_PREFIX
"mux");
688 psz_var_mime
= var_GetNonEmptyString(p_stream
, SOUT_CFG_PREFIX
"mime");
689 if (psz_var_mime
== NULL
)
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());
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
))
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
;
726 vlc_interrupt_destroy(p_interrupt
);
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
;