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>
39 #include <vlc_httpd.h>
43 #define TRANSCODING_NONE 0x0
44 #define TRANSCODING_VIDEO 0x1
45 #define TRANSCODING_AUDIO 0x2
48 /* TODO: works only with internal spu and transcoding/blending for now */
52 struct sout_access_out_sys_t
54 sout_access_out_sys_t(httpd_host_t
*httpd_host
, intf_sys_t
* const intf
,
56 ~sout_access_out_sys_t();
60 void prepare(sout_stream_t
*p_stream
, const std::string
&mime
);
61 int url_cb(httpd_client_t
*cl
, httpd_message_t
*answer
, const httpd_message_t
*query
);
62 void fifo_put_back(block_t
*);
63 ssize_t
write(sout_access_out_t
*p_access
, block_t
*p_block
);
70 void putCopy(block_t
*p_block
);
73 intf_sys_t
* const m_intf
;
75 httpd_client_t
*m_client
;
78 block_t
*m_copy_chain
;
79 block_t
**m_copy_last
;
85 struct sout_stream_sys_t
87 sout_stream_sys_t(httpd_host_t
*httpd_host
, intf_sys_t
* const intf
, bool has_video
, int port
)
88 : httpd_host(httpd_host
)
89 , access_out_live(httpd_host
, intf
, "/stream")
92 , b_supports_video(has_video
)
94 , first_video_keyframe_pts( -1 )
96 , cc_has_input( false )
98 , cc_flushing( false )
100 , out_force_reload( false )
101 , perf_warning_shown( false )
102 , transcoding_state( TRANSCODING_NONE
)
103 , venc_opt_idx ( -1 )
104 , out_streams_added( 0 )
106 assert(p_intf
!= NULL
);
107 vlc_mutex_init(&lock
);
112 vlc_mutex_destroy(&lock
);
115 bool canDecodeVideo( vlc_fourcc_t i_codec
) const;
116 bool canDecodeAudio( sout_stream_t
* p_stream
, vlc_fourcc_t i_codec
,
117 const audio_format_t
* p_fmt
) const;
118 bool startSoutChain(sout_stream_t
* p_stream
,
119 const std::vector
<sout_stream_id_sys_t
*> &new_streams
,
120 const std::string
&sout
, int new_transcoding_state
);
121 void stopSoutChain(sout_stream_t
* p_stream
);
122 sout_stream_id_sys_t
*GetSubId( sout_stream_t
*, sout_stream_id_sys_t
*, bool update
= true );
123 bool isFlushing( sout_stream_t
* );
124 void setNextTranscodingState();
125 bool transcodingCanFallback() const;
127 httpd_host_t
*httpd_host
;
128 sout_access_out_sys_t access_out_live
;
130 sout_stream_t
*p_out
;
133 vlc_mutex_t lock
; /* for input events cb */
135 intf_sys_t
* const p_intf
;
136 const bool b_supports_video
;
139 sout_stream_id_sys_t
* video_proxy_id
;
140 mtime_t first_video_keyframe_pts
;
147 bool out_force_reload
;
148 bool perf_warning_shown
;
149 int transcoding_state
;
151 std::vector
<sout_stream_id_sys_t
*> streams
;
152 std::vector
<sout_stream_id_sys_t
*> out_streams
;
153 unsigned int out_streams_added
;
154 unsigned int spu_streams_count
;
157 std::string
GetVencOption( sout_stream_t
*, vlc_fourcc_t
*,
158 const video_format_t
*, int );
159 std::string
GetAcodecOption( sout_stream_t
*, vlc_fourcc_t
*, const audio_format_t
*, int );
160 std::string
GetVcodecOption( sout_stream_t
*, vlc_fourcc_t
*, const video_format_t
*, int );
161 bool UpdateOutput( sout_stream_t
* );
164 struct sout_stream_id_sys_t
167 sout_stream_id_sys_t
*p_sub_id
;
171 #define SOUT_CFG_PREFIX "sout-chromecast-"
173 static const char DEFAULT_MUXER
[] = "avformat{mux=matroska,options={live=1}}";
174 static const char DEFAULT_MUXER_WEBM
[] = "avformat{mux=webm,options={live=1}}";
177 /*****************************************************************************
179 *****************************************************************************/
180 static int Open(vlc_object_t
*);
181 static void Close(vlc_object_t
*);
182 static int ProxyOpen(vlc_object_t
*);
183 static int AccessOpen(vlc_object_t
*);
184 static void AccessClose(vlc_object_t
*);
186 static const char *const ppsz_sout_options
[] = {
187 "ip", "port", "http-port", "video", NULL
190 /*****************************************************************************
192 *****************************************************************************/
194 #define HTTP_PORT_TEXT N_("HTTP port")
195 #define HTTP_PORT_LONGTEXT N_("This sets the HTTP port of the local server " \
196 "used to stream the media to the Chromecast.")
197 #define PERF_TEXT N_( "Performance warning" )
198 #define PERF_LONGTEXT N_( "Display a performance warning when transcoding" )
199 #define AUDIO_PASSTHROUGH_TEXT N_( "Enable Audio passthrough" )
200 #define AUDIO_PASSTHROUGH_LONGTEXT N_( "Disable if your receiver does not support Dolby®." )
203 CONVERSION_QUALITY_HIGH
= 0,
204 CONVERSION_QUALITY_MEDIUM
= 1,
205 CONVERSION_QUALITY_LOW
= 2,
206 CONVERSION_QUALITY_LOWCPU
= 3,
209 #if defined (__ANDROID__) || defined (__arm__) || (defined (TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
210 # define CONVERSION_QUALITY_DEFAULT CONVERSION_QUALITY_LOW
212 # define CONVERSION_QUALITY_DEFAULT CONVERSION_QUALITY_MEDIUM
215 static const int conversion_quality_list
[] = {
216 CONVERSION_QUALITY_HIGH
,
217 CONVERSION_QUALITY_MEDIUM
,
218 CONVERSION_QUALITY_LOW
,
219 CONVERSION_QUALITY_LOWCPU
,
221 static const char *const conversion_quality_list_text
[] = {
222 N_( "High (high quality and high bandwidth)" ),
223 N_( "Medium (medium quality and medium bandwidth)" ),
224 N_( "Low (low quality and low bandwidth)" ),
225 N_( "Low CPU (low quality but high bandwidth)" ),
228 #define CONVERSION_QUALITY_TEXT N_( "Conversion quality" )
229 #define CONVERSION_QUALITY_LONGTEXT N_( "Change this option to increase conversion speed or quality." )
231 #define IP_ADDR_TEXT N_("IP Address")
232 #define IP_ADDR_LONGTEXT N_("IP Address of the Chromecast.")
233 #define PORT_TEXT N_("Chromecast port")
234 #define PORT_LONGTEXT N_("The port used to talk to the Chromecast.")
236 /* Fifo size after we tell the demux to pace */
237 #define HTTPD_BUFFER_PACE INT64_C(2 * 1024 * 1024) /* 2 MB */
238 /* Fifo size after we drop packets (should not happen) */
239 #define HTTPD_BUFFER_MAX INT64_C(32 * 1024 * 1024) /* 32 MB */
240 #define HTTPD_BUFFER_COPY_MAX INT64_C(10 * 1024 * 1024) /* 10 MB */
244 set_shortname(N_("Chromecast"))
245 set_description(N_("Chromecast stream output"))
246 set_capability("sout stream", 0)
247 add_shortcut("chromecast")
248 set_category(CAT_SOUT
)
249 set_subcategory(SUBCAT_SOUT_STREAM
)
250 set_callbacks(Open
, Close
)
252 add_string(SOUT_CFG_PREFIX
"ip", NULL
, NULL
, NULL
, false)
254 add_integer(SOUT_CFG_PREFIX
"port", CHROMECAST_CONTROL_PORT
, NULL
, NULL
, false)
256 add_bool(SOUT_CFG_PREFIX
"video", true, NULL
, NULL
, false)
258 add_integer(SOUT_CFG_PREFIX
"http-port", HTTP_PORT
, HTTP_PORT_TEXT
, HTTP_PORT_LONGTEXT
, false)
259 add_obsolete_string(SOUT_CFG_PREFIX
"mux")
260 add_obsolete_string(SOUT_CFG_PREFIX
"mime")
261 add_integer(SOUT_CFG_PREFIX
"show-perf-warning", 1, PERF_TEXT
, PERF_LONGTEXT
, true )
263 add_bool(SOUT_CFG_PREFIX
"audio-passthrough", false, AUDIO_PASSTHROUGH_TEXT
, AUDIO_PASSTHROUGH_LONGTEXT
, false )
264 add_integer(SOUT_CFG_PREFIX
"conversion-quality", CONVERSION_QUALITY_DEFAULT
,
265 CONVERSION_QUALITY_TEXT
, CONVERSION_QUALITY_LONGTEXT
, false );
266 change_integer_list(conversion_quality_list
, conversion_quality_list_text
)
269 /* sout proxy that start the cc input when all streams are loaded */
270 add_shortcut("chromecast-proxy")
271 set_capability("sout stream", 0)
272 set_callbacks(ProxyOpen
, NULL
)
274 set_subcategory(SUBCAT_SOUT_ACO
)
275 add_shortcut("chromecast-http")
276 set_capability("sout access", 0)
277 set_callbacks(AccessOpen
, AccessClose
)
280 static sout_stream_id_sys_t
*ProxyAdd(sout_stream_t
*p_stream
, const es_format_t
*p_fmt
)
282 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
283 sout_stream_id_sys_t
*id
= sout_StreamIdAdd(p_stream
->p_next
, p_fmt
);
286 if (p_fmt
->i_cat
== VIDEO_ES
)
287 p_sys
->video_proxy_id
= id
;
288 p_sys
->out_streams_added
++;
293 static void ProxyDel(sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
)
295 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
296 p_sys
->out_streams_added
--;
297 if (id
== p_sys
->video_proxy_id
)
298 p_sys
->video_proxy_id
= NULL
;
299 return sout_StreamIdDel(p_stream
->p_next
, id
);
302 static int ProxySend(sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
,
305 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
306 if (p_sys
->cc_has_input
307 || p_sys
->out_streams_added
>= p_sys
->out_streams
.size() - p_sys
->spu_streams_count
)
309 if (p_sys
->has_video
)
311 // In case of video, the first block must be a keyframe
312 if (id
== p_sys
->video_proxy_id
)
314 if (p_sys
->first_video_keyframe_pts
== -1
315 && p_buffer
->i_flags
& BLOCK_FLAG_TYPE_I
)
316 p_sys
->first_video_keyframe_pts
= p_buffer
->i_pts
;
318 else // no keyframe for audio
319 p_buffer
->i_flags
&= ~BLOCK_FLAG_TYPE_I
;
321 if (p_buffer
->i_pts
< p_sys
->first_video_keyframe_pts
322 || p_sys
->first_video_keyframe_pts
== -1)
324 block_Release(p_buffer
);
329 mtime_t pause_delay
= p_sys
->p_intf
->getPauseDelay();
330 if( p_buffer
->i_pts
!= VLC_TS_INVALID
)
331 p_buffer
->i_pts
-= pause_delay
;
332 if( p_buffer
->i_dts
!= VLC_TS_INVALID
)
333 p_buffer
->i_dts
-= pause_delay
;
335 int ret
= sout_StreamIdSend(p_stream
->p_next
, id
, p_buffer
);
336 if (ret
== VLC_SUCCESS
&& !p_sys
->cc_has_input
)
338 /* Start the chromecast only when all streams are added into the
339 * last sout (the http one) */
340 p_sys
->p_intf
->setHasInput(p_sys
->mime
);
341 p_sys
->cc_has_input
= true;
347 block_Release(p_buffer
);
352 static void ProxyFlush(sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
)
354 sout_StreamFlush(p_stream
->p_next
, id
);
357 static int ProxyOpen(vlc_object_t
*p_this
)
359 sout_stream_t
*p_stream
= reinterpret_cast<sout_stream_t
*>(p_this
);
360 sout_stream_sys_t
*p_sys
= (sout_stream_sys_t
*) var_InheritAddress(p_this
, SOUT_CFG_PREFIX
"sys");
361 if (p_sys
== NULL
|| p_stream
->p_next
== NULL
)
364 p_stream
->p_sys
= (sout_stream_sys_t
*) p_sys
;
365 p_sys
->out_streams_added
= 0;
367 p_stream
->pf_add
= ProxyAdd
;
368 p_stream
->pf_del
= ProxyDel
;
369 p_stream
->pf_send
= ProxySend
;
370 p_stream
->pf_flush
= ProxyFlush
;
374 static int httpd_url_cb(httpd_callback_sys_t
*data
, httpd_client_t
*cl
,
375 httpd_message_t
*answer
, const httpd_message_t
*query
)
377 sout_access_out_sys_t
*p_sys
= reinterpret_cast<sout_access_out_sys_t
*>(data
);
378 return p_sys
->url_cb(cl
, answer
, query
);
381 sout_access_out_sys_t::sout_access_out_sys_t(httpd_host_t
*httpd_host
,
382 intf_sys_t
* const intf
,
390 m_fifo
= block_FifoNew();
392 throw std::runtime_error( "block_FifoNew failed" );
393 m_url
= httpd_UrlNew(httpd_host
, psz_url
, NULL
, NULL
);
396 block_FifoRelease(m_fifo
);
397 throw std::runtime_error( "httpd_UrlNew failed" );
399 httpd_UrlCatch(m_url
, HTTPD_MSG_GET
, httpd_url_cb
,
400 (httpd_callback_sys_t
*)this);
404 sout_access_out_sys_t::~sout_access_out_sys_t()
406 httpd_UrlDelete(m_url
);
407 block_FifoRelease(m_fifo
);
410 void sout_access_out_sys_t::clearUnlocked()
412 block_ChainRelease(vlc_fifo_DequeueAllUnlocked(m_fifo
));
415 block_Release(m_header
);
422 void sout_access_out_sys_t::initCopy()
424 block_ChainRelease(m_copy_chain
);
426 m_copy_last
= &m_copy_chain
;
430 void sout_access_out_sys_t::putCopy(block_t
*p_block
)
432 while (m_copy_size
>= HTTPD_BUFFER_COPY_MAX
)
434 assert(m_copy_chain
);
435 block_t
*copy
= m_copy_chain
;
436 m_copy_chain
= copy
->p_next
;
437 m_copy_size
-= copy
->i_buffer
;
442 assert(m_copy_size
== 0);
443 m_copy_last
= &m_copy_chain
;
445 block_ChainLastAppend(&m_copy_last
, p_block
);
446 m_copy_size
+= p_block
->i_buffer
;
449 void sout_access_out_sys_t::restoreCopy()
453 fifo_put_back(m_copy_chain
);
459 void sout_access_out_sys_t::clear()
461 vlc_fifo_Lock(m_fifo
);
463 vlc_fifo_Unlock(m_fifo
);
464 vlc_fifo_Signal(m_fifo
);
467 void sout_access_out_sys_t::stop()
469 vlc_fifo_Lock(m_fifo
);
471 m_intf
->setPacing(false);
473 vlc_fifo_Unlock(m_fifo
);
474 vlc_fifo_Signal(m_fifo
);
477 void sout_access_out_sys_t::prepare(sout_stream_t
*p_stream
, const std::string
&mime
)
479 var_SetAddress(p_stream
->p_sout
, SOUT_CFG_PREFIX
"access-out-sys", this);
481 vlc_fifo_Lock(m_fifo
);
483 m_intf
->setPacing(false);
486 vlc_fifo_Unlock(m_fifo
);
489 void sout_access_out_sys_t::fifo_put_back(block_t
*p_block
)
491 block_t
*p_fifo
= vlc_fifo_DequeueAllUnlocked(m_fifo
);
492 vlc_fifo_QueueUnlocked(m_fifo
, p_block
);
493 vlc_fifo_QueueUnlocked(m_fifo
, p_fifo
);
496 int sout_access_out_sys_t::url_cb(httpd_client_t
*cl
, httpd_message_t
*answer
,
497 const httpd_message_t
*query
)
499 if (!answer
|| !query
|| !cl
)
502 vlc_fifo_Lock(m_fifo
);
504 if (!answer
->i_body_offset
)
506 /* When doing a lot a load requests, we can serve data to a client that
507 * will be closed (the close request is already sent). In that case, we
508 * should also serve data used by the old client to the new one. */
513 /* Send data per 512kB minimum */
514 size_t i_min_buffer
= 524288;
515 while (m_client
&& vlc_fifo_GetBytes(m_fifo
) < i_min_buffer
&& !m_eof
)
516 vlc_fifo_Wait(m_fifo
);
518 block_t
*p_block
= NULL
;
519 if (m_client
&& vlc_fifo_GetBytes(m_fifo
) > 0)
521 /* if less data is available, then we must be EOF */
522 if (vlc_fifo_GetBytes(m_fifo
) < i_min_buffer
)
525 i_min_buffer
= vlc_fifo_GetBytes(m_fifo
);
527 block_t
*p_first
= vlc_fifo_DequeueUnlocked(m_fifo
);
530 size_t i_total_size
= p_first
->i_buffer
;
531 block_t
*p_next
= NULL
, *p_cur
= p_first
;
533 while (i_total_size
< i_min_buffer
)
535 p_next
= vlc_fifo_DequeueUnlocked(m_fifo
);
537 i_total_size
+= p_next
->i_buffer
;
538 p_cur
->p_next
= p_next
;
539 p_cur
= p_cur
->p_next
;
541 assert(i_total_size
>= i_min_buffer
);
545 p_block
= block_Alloc(i_total_size
);
547 block_ChainExtract(p_first
, p_block
->p_buffer
, p_block
->i_buffer
);
548 block_ChainRelease(p_first
);
553 if (vlc_fifo_GetBytes(m_fifo
) < HTTPD_BUFFER_PACE
)
554 m_intf
->setPacing(false);
557 answer
->i_proto
= HTTPD_PROTO_HTTP
;
558 answer
->i_version
= 0;
559 answer
->i_type
= HTTPD_MSG_ANSWER
;
560 answer
->i_status
= 200;
564 if (answer
->i_body_offset
== 0)
566 httpd_MsgAdd(answer
, "Content-type", "%s", m_mime
.c_str());
567 httpd_MsgAdd(answer
, "Cache-Control", "no-cache");
568 httpd_MsgAdd(answer
, "Connection", "close");
571 const bool send_header
= answer
->i_body_offset
== 0 && m_header
!= NULL
;
572 size_t i_answer_size
= p_block
->i_buffer
;
574 i_answer_size
+= m_header
->i_buffer
;
576 answer
->p_body
= (uint8_t *) malloc(i_answer_size
);
579 answer
->i_body
= i_answer_size
;
580 answer
->i_body_offset
+= answer
->i_body
;
581 size_t i_block_offset
= 0;
584 memcpy(answer
->p_body
, m_header
->p_buffer
, m_header
->i_buffer
);
585 i_block_offset
= m_header
->i_buffer
;
587 memcpy(&answer
->p_body
[i_block_offset
], p_block
->p_buffer
, p_block
->i_buffer
);
593 httpd_MsgAdd(answer
, "Connection", "close");
595 vlc_fifo_Unlock(m_fifo
);
599 ssize_t
sout_access_out_sys_t::write(sout_access_out_t
*p_access
, block_t
*p_block
)
601 size_t i_len
= p_block
->i_buffer
;
603 vlc_fifo_Lock(m_fifo
);
605 if (p_block
->i_flags
& BLOCK_FLAG_HEADER
)
608 block_Release(m_header
);
613 /* Drop buffer is the fifo is really full */
614 if (vlc_fifo_GetBytes(m_fifo
) >= HTTPD_BUFFER_PACE
)
616 /* XXX: Hackisk way to pace between the sout (controlled by the
617 * decoder thread) and the demux filter (controlled by the input
618 * thread). When the httpd FIFO reaches a specific size, we tell
619 * the demux filter to pace and wait a little before queing this
620 * block, but not too long since we don't want to block decoder
621 * thread controls. If the pacing fails (should not happen), we
622 * drop the first block in order to make room. The demux filter
623 * will be unpaced when the data is read from the httpd thread. */
625 m_intf
->setPacing(true);
627 while (vlc_fifo_GetBytes(m_fifo
) >= HTTPD_BUFFER_MAX
)
629 block_t
*p_drop
= vlc_fifo_DequeueUnlocked(m_fifo
);
630 msg_Warn(p_access
, "httpd buffer full: dropping %zuB", p_drop
->i_buffer
);
631 block_Release(p_drop
);
634 vlc_fifo_QueueUnlocked(m_fifo
, p_block
);
639 vlc_fifo_Unlock(m_fifo
);
640 vlc_fifo_Signal(m_fifo
);
645 void sout_access_out_sys_t::close()
647 vlc_fifo_Lock(m_fifo
);
649 m_intf
->setPacing(false);
650 vlc_fifo_Unlock(m_fifo
);
651 vlc_fifo_Signal(m_fifo
);
654 ssize_t
AccessWrite(sout_access_out_t
*p_access
, block_t
*p_block
)
656 sout_access_out_sys_t
*p_sys
= p_access
->p_sys
;
657 return p_sys
->write(p_access
, p_block
);
660 static int AccessControl(sout_access_out_t
*p_access
, int i_query
, va_list args
)
666 case ACCESS_OUT_CONTROLS_PACE
:
667 *va_arg(args
, bool *) = true;
675 static int AccessOpen(vlc_object_t
*p_this
)
677 sout_access_out_t
*p_access
= (sout_access_out_t
*)p_this
;
679 sout_access_out_sys_t
*p_sys
= (sout_access_out_sys_t
*)
680 var_InheritAddress(p_access
, SOUT_CFG_PREFIX
"access-out-sys");
684 p_access
->pf_write
= AccessWrite
;
685 p_access
->pf_control
= AccessControl
;
686 p_access
->p_sys
= p_sys
;
691 static void AccessClose(vlc_object_t
*p_this
)
693 sout_access_out_t
*p_access
= (sout_access_out_t
*)p_this
;
694 sout_access_out_sys_t
*p_sys
= p_access
->p_sys
;
699 /*****************************************************************************
701 *****************************************************************************/
702 static sout_stream_id_sys_t
*Add(sout_stream_t
*p_stream
, const es_format_t
*p_fmt
)
704 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
705 vlc_mutex_locker
locker(&p_sys
->lock
);
707 if (!p_sys
->b_supports_video
)
709 if (p_fmt
->i_cat
!= AUDIO_ES
)
713 sout_stream_id_sys_t
*p_sys_id
= (sout_stream_id_sys_t
*)malloc( sizeof(sout_stream_id_sys_t
) );
714 if (p_sys_id
!= NULL
)
716 es_format_Copy( &p_sys_id
->fmt
, p_fmt
);
717 p_sys_id
->p_sub_id
= NULL
;
718 p_sys_id
->flushed
= false;
720 p_sys
->streams
.push_back( p_sys_id
);
721 p_sys
->es_changed
= true;
727 static void DelInternal(sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
,
730 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
732 for (std::vector
<sout_stream_id_sys_t
*>::iterator it
= p_sys
->streams
.begin();
733 it
!= p_sys
->streams
.end(); )
735 sout_stream_id_sys_t
*p_sys_id
= *it
;
736 if ( p_sys_id
== id
)
738 if ( p_sys_id
->p_sub_id
!= NULL
)
740 sout_StreamIdDel( p_sys
->p_out
, p_sys_id
->p_sub_id
);
741 for (std::vector
<sout_stream_id_sys_t
*>::iterator out_it
= p_sys
->out_streams
.begin();
742 out_it
!= p_sys
->out_streams
.end(); )
746 p_sys
->out_streams
.erase(out_it
);
747 p_sys
->es_changed
= reset_config
;
748 p_sys
->out_force_reload
= reset_config
;
749 if( p_sys_id
->fmt
.i_cat
== VIDEO_ES
)
750 p_sys
->has_video
= false;
751 else if( p_sys_id
->fmt
.i_cat
== SPU_ES
)
752 p_sys
->spu_streams_count
--;
759 es_format_Clean( &p_sys_id
->fmt
);
761 p_sys
->streams
.erase( it
);
767 if ( p_sys
->out_streams
.empty() )
769 p_sys
->stopSoutChain(p_stream
);
770 p_sys
->p_intf
->requestPlayerStop();
771 p_sys
->access_out_live
.clear();
772 p_sys
->transcoding_state
= TRANSCODING_NONE
;
776 static void Del(sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
)
778 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
780 vlc_mutex_locker
locker(&p_sys
->lock
);
781 DelInternal(p_stream
, id
, true);
786 * 0: Accept HEVC/VP9 & all supported audio formats
787 * 1: Transcode to h264 & accept all supported audio formats if the video codec
789 * 2: Transcode to H264 & MP3
792 * - Allow (E)AC3 passthrough depending on the audio-passthrough
793 * config value, except for the final step, where we just give up and transcode
795 * - Disallow multichannel AAC
797 * Supported formats: https://developers.google.com/cast/docs/media
800 bool sout_stream_sys_t::canDecodeVideo( vlc_fourcc_t i_codec
) const
802 if( transcoding_state
& TRANSCODING_VIDEO
)
804 return i_codec
== VLC_CODEC_H264
|| i_codec
== VLC_CODEC_HEVC
805 || i_codec
== VLC_CODEC_VP8
|| i_codec
== VLC_CODEC_VP9
;
808 bool sout_stream_sys_t::canDecodeAudio( sout_stream_t
*p_stream
,
809 vlc_fourcc_t i_codec
,
810 const audio_format_t
* p_fmt
) const
812 if( transcoding_state
& TRANSCODING_AUDIO
)
814 if ( i_codec
== VLC_CODEC_A52
|| i_codec
== VLC_CODEC_EAC3
)
816 return var_InheritBool( p_stream
, SOUT_CFG_PREFIX
"audio-passthrough" );
818 if ( i_codec
== VLC_FOURCC('h', 'a', 'a', 'c') ||
819 i_codec
== VLC_FOURCC('l', 'a', 'a', 'c') ||
820 i_codec
== VLC_FOURCC('s', 'a', 'a', 'c') ||
821 i_codec
== VLC_CODEC_MPGA
||
822 i_codec
== VLC_CODEC_MP4A
)
824 return p_fmt
->i_channels
<= 2;
826 return i_codec
== VLC_CODEC_VORBIS
|| i_codec
== VLC_CODEC_OPUS
||
827 i_codec
== VLC_CODEC_MP3
;
830 void sout_stream_sys_t::stopSoutChain(sout_stream_t
*p_stream
)
834 if ( unlikely( p_out
!= NULL
) )
836 for ( size_t i
= 0; i
< out_streams
.size(); i
++ )
838 if ( out_streams
[i
]->p_sub_id
!= NULL
)
840 sout_StreamIdDel( p_out
, out_streams
[i
]->p_sub_id
);
841 out_streams
[i
]->p_sub_id
= NULL
;
845 sout_StreamChainDelete( p_out
, NULL
);
850 bool sout_stream_sys_t::startSoutChain(sout_stream_t
*p_stream
,
851 const std::vector
<sout_stream_id_sys_t
*> &new_streams
,
852 const std::string
&sout
, int new_transcoding_state
)
854 stopSoutChain( p_stream
);
856 msg_Dbg( p_stream
, "Creating chain %s", sout
.c_str() );
857 cc_has_input
= false;
859 first_video_keyframe_pts
= -1;
860 video_proxy_id
= NULL
;
862 out_streams
= new_streams
;
863 spu_streams_count
= 0;
864 transcoding_state
= new_transcoding_state
;
866 access_out_live
.prepare( p_stream
, mime
);
868 p_out
= sout_StreamChainNew( p_stream
->p_sout
, sout
.c_str(), NULL
, NULL
);
870 msg_Dbg(p_stream
, "could not create sout chain:%s", sout
.c_str());
872 access_out_live
.clear();
876 /* check the streams we can actually add */
877 for (std::vector
<sout_stream_id_sys_t
*>::iterator it
= out_streams
.begin();
878 it
!= out_streams
.end(); )
880 sout_stream_id_sys_t
*p_sys_id
= *it
;
881 p_sys_id
->p_sub_id
= sout_StreamIdAdd( p_out
, &p_sys_id
->fmt
);
882 if ( p_sys_id
->p_sub_id
== NULL
)
884 msg_Err( p_stream
, "can't handle %4.4s stream", (char *)&p_sys_id
->fmt
.i_codec
);
885 es_format_Clean( &p_sys_id
->fmt
);
886 it
= out_streams
.erase( it
);
890 if( p_sys_id
->fmt
.i_cat
== VIDEO_ES
)
892 else if( p_sys_id
->fmt
.i_cat
== SPU_ES
)
898 if (out_streams
.empty())
900 stopSoutChain( p_stream
);
901 access_out_live
.clear();
905 /* Ask to retry if we are not transcoding everything (because we can trust
907 p_intf
->setRetryOnFail(transcodingCanFallback());
912 void sout_stream_sys_t::setNextTranscodingState()
914 if (!(transcoding_state
& TRANSCODING_VIDEO
))
915 transcoding_state
|= TRANSCODING_VIDEO
;
916 else if (!(transcoding_state
& TRANSCODING_AUDIO
))
917 transcoding_state
= TRANSCODING_AUDIO
;
920 bool sout_stream_sys_t::transcodingCanFallback() const
922 return transcoding_state
!= (TRANSCODING_VIDEO
|TRANSCODING_AUDIO
);
925 static std::string
GetVencVPXOption( sout_stream_t
* /* p_stream */,
926 const video_format_t
* /* p_vid */,
927 int /* i_quality */ )
929 return "venc=vpx{quality-mode=1}";
932 static std::string
GetVencX264Option( sout_stream_t
* /* p_stream */,
933 const video_format_t
*p_vid
,
936 std::stringstream ssout
;
937 static const char video_x264_preset_veryfast
[] = "veryfast";
938 static const char video_x264_preset_ultrafast
[] = "ultrafast";
939 const char *psz_video_x264_preset
;
940 unsigned i_video_x264_crf_hd
, i_video_x264_crf_720p
;
944 case CONVERSION_QUALITY_HIGH
:
945 i_video_x264_crf_hd
= i_video_x264_crf_720p
= 21;
946 psz_video_x264_preset
= video_x264_preset_veryfast
;
948 case CONVERSION_QUALITY_MEDIUM
:
949 i_video_x264_crf_hd
= 23;
950 i_video_x264_crf_720p
= 21;
951 psz_video_x264_preset
= video_x264_preset_veryfast
;
953 case CONVERSION_QUALITY_LOW
:
954 i_video_x264_crf_hd
= i_video_x264_crf_720p
= 23;
955 psz_video_x264_preset
= video_x264_preset_veryfast
;
958 case CONVERSION_QUALITY_LOWCPU
:
959 i_video_x264_crf_hd
= i_video_x264_crf_720p
= 23;
960 psz_video_x264_preset
= video_x264_preset_ultrafast
;
964 const bool b_hdres
= p_vid
== NULL
|| p_vid
->i_height
== 0 || p_vid
->i_height
>= 800;
965 unsigned i_video_x264_crf
= b_hdres
? i_video_x264_crf_hd
: i_video_x264_crf_720p
;
967 ssout
<< "venc=x264{preset=" << psz_video_x264_preset
968 << ",crf=" << i_video_x264_crf
<< "}";
976 std::string (*get_opt
)( sout_stream_t
*, const video_format_t
*, int);
977 } venc_opt_list
[] = {
978 { .fcc
= VLC_CODEC_H264
, .get_opt
= GetVencX264Option
},
979 { .fcc
= VLC_CODEC_VP8
, .get_opt
= GetVencVPXOption
},
980 { .fcc
= VLC_CODEC_H264
, .get_opt
= NULL
},
984 sout_stream_sys_t::GetVencOption( sout_stream_t
*p_stream
, vlc_fourcc_t
*p_codec_video
,
985 const video_format_t
*p_vid
, int i_quality
)
987 for( size_t i
= (venc_opt_idx
== -1 ? 0 : venc_opt_idx
);
988 i
< ARRAY_SIZE(venc_opt_list
); ++i
)
990 std::stringstream ssout
, ssvenc
;
993 vlc_fourcc_to_char( venc_opt_list
[i
].fcc
, fourcc
);
995 ssvenc
<< fourcc
<< ',';
997 if( venc_opt_list
[i
].get_opt
!= NULL
)
998 ssvenc
<< venc_opt_list
[i
].get_opt( p_stream
, p_vid
, i_quality
) << ',';
1000 if( venc_opt_list
[i
].get_opt
== NULL
1001 || ( venc_opt_idx
!= -1 && (unsigned) venc_opt_idx
== i
) )
1004 *p_codec_video
= venc_opt_list
[i
].fcc
;
1005 return ssvenc
.str();
1008 /* Test if a module can encode with the specified options / fmt_video. */
1009 ssout
<< "transcode{" << ssvenc
.str() << "}:dummy";
1011 sout_stream_t
*p_sout_test
=
1012 sout_StreamChainNew( p_stream
->p_sout
, ssout
.str().c_str(), NULL
, NULL
);
1014 if( p_sout_test
!= NULL
)
1016 p_sout_test
->obj
.flags
|= OBJECT_FLAGS_QUIET
|OBJECT_FLAGS_NOINTERACT
;
1019 es_format_InitFromVideo( &fmt
, p_vid
);
1020 fmt
.i_codec
= fmt
.video
.i_chroma
= VLC_CODEC_I420
;
1021 fmt
.video
.i_frame_rate
= 30;
1022 fmt
.video
.i_frame_rate_base
= 1;
1024 sout_stream_id_sys_t
*id
= sout_StreamIdAdd( p_sout_test
, &fmt
);
1026 es_format_Clean( &fmt
);
1027 const bool success
= id
!= NULL
;
1030 sout_StreamIdDel( p_sout_test
, id
);
1031 sout_StreamChainDelete( p_sout_test
, NULL
);
1036 *p_codec_video
= venc_opt_list
[i
].fcc
;
1037 return ssvenc
.str();
1041 vlc_assert_unreachable();
1045 sout_stream_sys_t::GetVcodecOption( sout_stream_t
*p_stream
, vlc_fourcc_t
*p_codec_video
,
1046 const video_format_t
*p_vid
, int i_quality
)
1048 std::stringstream ssout
;
1049 static const char video_maxres_hd
[] = "maxwidth=1920,maxheight=1080";
1050 static const char video_maxres_720p
[] = "maxwidth=1280,maxheight=720";
1052 ssout
<< GetVencOption( p_stream
, p_codec_video
, p_vid
, i_quality
);
1054 switch ( i_quality
)
1056 case CONVERSION_QUALITY_HIGH
:
1057 case CONVERSION_QUALITY_MEDIUM
:
1058 ssout
<< video_maxres_hd
<< ',';
1061 ssout
<< video_maxres_720p
<< ',';
1065 || p_vid
->i_frame_rate
== 0 || p_vid
->i_frame_rate_base
== 0
1066 || ( p_vid
->i_frame_rate
/ p_vid
->i_frame_rate_base
) > 30 )
1068 /* Even force 24fps if the frame rate is unknown */
1069 msg_Warn( p_stream
, "lowering frame rate to 24fps" );
1073 msg_Dbg( p_stream
, "Converting video to %.4s", (const char*)p_codec_video
);
1079 sout_stream_sys_t::GetAcodecOption( sout_stream_t
*p_stream
, vlc_fourcc_t
*p_codec_audio
,
1080 const audio_format_t
*p_aud
, int i_quality
)
1082 std::stringstream ssout
;
1086 /* If we were already transcoding: force mp3 because maybe the CC may
1087 * have failed because of vorbis. */
1088 if( transcoding_state
& TRANSCODING_AUDIO
)
1092 switch ( i_quality
)
1094 case CONVERSION_QUALITY_HIGH
:
1095 case CONVERSION_QUALITY_MEDIUM
:
1096 b_audio_mp3
= false;
1105 && p_aud
->i_channels
> 2 && module_exists( "vorbis" ) )
1106 *p_codec_audio
= VLC_CODEC_VORBIS
;
1108 *p_codec_audio
= VLC_CODEC_MP3
;
1110 msg_Dbg( p_stream
, "Converting audio to %.4s", (const char*)p_codec_audio
);
1114 vlc_fourcc_to_char( *p_codec_audio
, fourcc
);
1116 ssout
<< fourcc
<< ',';
1118 /* XXX: higher vorbis qualities can cause glitches on some CC
1119 * devices (Chromecast 1 & 2) */
1120 if( *p_codec_audio
== VLC_CODEC_VORBIS
)
1121 ssout
<< "aenc=vorbis{quality=4},";
1125 bool sout_stream_sys_t::UpdateOutput( sout_stream_t
*p_stream
)
1127 assert( p_stream
->p_sys
== this );
1134 bool canRemux
= true;
1135 vlc_fourcc_t i_codec_video
= 0, i_codec_audio
= 0;
1136 const es_format_t
*p_original_audio
= NULL
;
1137 const es_format_t
*p_original_video
= NULL
;
1138 const es_format_t
*p_original_spu
= NULL
;
1139 bool b_out_streams_changed
= false;
1140 std::vector
<sout_stream_id_sys_t
*> new_streams
;
1142 for (std::vector
<sout_stream_id_sys_t
*>::iterator it
= streams
.begin(); it
!= streams
.end(); ++it
)
1144 const es_format_t
*p_es
= &(*it
)->fmt
;
1145 if (p_es
->i_cat
== AUDIO_ES
&& p_original_audio
== NULL
)
1147 if ( !canDecodeAudio( p_stream
, p_es
->i_codec
, &p_es
->audio
) )
1149 msg_Dbg( p_stream
, "can't remux audio track %d codec %4.4s", p_es
->i_id
, (const char*)&p_es
->i_codec
);
1152 else if (i_codec_audio
== 0)
1153 i_codec_audio
= p_es
->i_codec
;
1154 p_original_audio
= p_es
;
1155 new_streams
.push_back(*it
);
1157 else if (b_supports_video
)
1159 if (p_es
->i_cat
== VIDEO_ES
&& p_original_video
== NULL
)
1161 if (!canDecodeVideo( p_es
->i_codec
))
1163 msg_Dbg( p_stream
, "can't remux video track %d codec %4.4s",
1164 p_es
->i_id
, (const char*)&p_es
->i_codec
);
1167 else if (i_codec_video
== 0 && !p_original_spu
)
1168 i_codec_video
= p_es
->i_codec
;
1169 p_original_video
= p_es
;
1170 new_streams
.push_back(*it
);
1172 #ifdef CC_ENABLE_SPU
1173 else if (p_es
->i_cat
== SPU_ES
&& p_original_spu
== NULL
)
1175 msg_Dbg( p_stream
, "forcing video transcode because of subtitle '%4.4s'",
1176 (const char*)&p_es
->i_codec
);
1179 p_original_spu
= p_es
;
1180 new_streams
.push_back(*it
);
1189 bool b_found
= out_force_reload
;
1190 for (std::vector
<sout_stream_id_sys_t
*>::iterator out_it
= out_streams
.begin();
1191 out_it
!= out_streams
.end() && !b_found
; ++out_it
)
1197 b_out_streams_changed
= true;
1200 if (new_streams
.empty())
1202 p_intf
->requestPlayerStop();
1206 /* Don't restart sout and CC session if streams didn't change */
1207 if (!out_force_reload
&& new_streams
.size() == out_streams
.size() && !b_out_streams_changed
)
1210 out_force_reload
= false;
1212 std::stringstream ssout
;
1213 int new_transcoding_state
= TRANSCODING_NONE
;
1216 if ( !perf_warning_shown
&& i_codec_video
== 0 && p_original_video
1217 && var_InheritInteger( p_stream
, SOUT_CFG_PREFIX
"show-perf-warning" ) )
1219 int res
= vlc_dialog_wait_question( p_stream
,
1220 VLC_DIALOG_QUESTION_WARNING
,
1221 _("Cancel"), _("OK"), _("Ok, Don't warn me again"),
1222 _("Performance warning"),
1223 _("Casting this video requires conversion. "
1224 "This conversion can use all the available power and "
1225 "could quickly drain your battery." ) );
1228 perf_warning_shown
= true;
1230 config_PutInt(SOUT_CFG_PREFIX
"show-perf-warning", 0 );
1233 const int i_quality
= var_InheritInteger( p_stream
, SOUT_CFG_PREFIX
"conversion-quality" );
1235 /* TODO: provide audio samplerate and channels */
1236 ssout
<< "transcode{";
1237 if ( i_codec_audio
== 0 && p_original_audio
)
1239 ssout
<< GetAcodecOption( p_stream
, &i_codec_audio
,
1240 &p_original_audio
->audio
, i_quality
);
1241 new_transcoding_state
|= TRANSCODING_AUDIO
;
1243 if ( i_codec_video
== 0 && p_original_video
)
1245 ssout
<< GetVcodecOption( p_stream
, &i_codec_video
,
1246 &p_original_video
->video
, i_quality
);
1247 new_transcoding_state
|= TRANSCODING_VIDEO
;
1249 if ( p_original_spu
)
1250 ssout
<< "soverlay,";
1254 const bool is_webm
= ( i_codec_audio
== 0 || i_codec_audio
== VLC_CODEC_VORBIS
||
1255 i_codec_audio
== VLC_CODEC_OPUS
) &&
1256 ( i_codec_video
== 0 || i_codec_video
== VLC_CODEC_VP8
||
1257 i_codec_video
== VLC_CODEC_VP9
);
1259 if ( !p_original_video
)
1262 mime
= "audio/webm";
1264 mime
= "audio/x-matroska";
1269 mime
= "video/webm";
1271 mime
= "video/x-matroska";
1274 ssout
<< "chromecast-proxy:"
1275 << "std{mux=" << ( is_webm
? DEFAULT_MUXER_WEBM
: DEFAULT_MUXER
)
1276 << ",access=chromecast-http}";
1278 if ( !startSoutChain( p_stream
, new_streams
, ssout
.str(),
1279 new_transcoding_state
) )
1280 p_intf
->requestPlayerStop();
1284 sout_stream_id_sys_t
*sout_stream_sys_t::GetSubId( sout_stream_t
*p_stream
,
1285 sout_stream_id_sys_t
*id
,
1290 assert( p_stream
->p_sys
== this );
1292 if ( update
&& UpdateOutput( p_stream
) == false )
1295 for (i
= 0; i
< out_streams
.size(); ++i
)
1297 if ( id
== (sout_stream_id_sys_t
*) out_streams
[i
] )
1298 return out_streams
[i
]->p_sub_id
;
1304 bool sout_stream_sys_t::isFlushing( sout_stream_t
*p_stream
)
1308 /* Make sure that all out_streams are flushed when flushing. This avoids
1309 * too many sout/cc restart when a stream is sending data while one other
1315 for (size_t i
= 0; i
< out_streams
.size(); ++i
)
1317 if ( !out_streams
[i
]->flushed
)
1321 cc_flushing
= false;
1322 for (size_t i
= 0; i
< out_streams
.size(); ++i
)
1323 out_streams
[i
]->flushed
= false;
1328 static int Send(sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
,
1331 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
1332 vlc_mutex_locker
locker(&p_sys
->lock
);
1334 if( p_sys
->isFlushing( p_stream
) )
1336 block_Release( p_buffer
);
1340 sout_stream_id_sys_t
*next_id
= p_sys
->GetSubId( p_stream
, id
);
1341 if ( next_id
== NULL
)
1343 block_Release( p_buffer
);
1344 return VLC_EGENERIC
;
1347 int ret
= sout_StreamIdSend(p_sys
->p_out
, next_id
, p_buffer
);
1348 if (ret
!= VLC_SUCCESS
)
1349 DelInternal(p_stream
, id
, false);
1354 static void Flush( sout_stream_t
*p_stream
, sout_stream_id_sys_t
*id
)
1356 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
1357 vlc_mutex_locker
locker(&p_sys
->lock
);
1359 sout_stream_id_sys_t
*next_id
= p_sys
->GetSubId( p_stream
, id
, false );
1360 if ( next_id
== NULL
)
1362 next_id
->flushed
= true;
1364 if( !p_sys
->cc_flushing
)
1366 p_sys
->cc_flushing
= true;
1368 p_sys
->stopSoutChain( p_stream
);
1370 p_sys
->access_out_live
.stop();
1372 if (p_sys
->cc_has_input
)
1374 p_sys
->p_intf
->requestPlayerStop();
1375 p_sys
->cc_has_input
= false;
1377 p_sys
->out_force_reload
= p_sys
->es_changed
= true;
1381 static void on_input_event_cb(void *data
, enum cc_input_event event
, union cc_input_arg arg
)
1383 sout_stream_t
*p_stream
= reinterpret_cast<sout_stream_t
*>(data
);
1384 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
1386 vlc_mutex_locker
locker(&p_sys
->lock
);
1389 case CC_INPUT_EVENT_EOF
:
1390 /* In case of EOF: stop the sout chain in order to drain all
1391 * sout/demuxers/access. If EOF changes to false, reset es_changed
1392 * in order to reload the sout from next Send calls. */
1394 p_sys
->stopSoutChain( p_stream
);
1396 p_sys
->out_force_reload
= p_sys
->es_changed
= true;
1398 case CC_INPUT_EVENT_RETRY
:
1399 p_sys
->stopSoutChain( p_stream
);
1400 if( p_sys
->transcodingCanFallback() )
1402 p_sys
->setNextTranscodingState();
1403 msg_Warn(p_stream
, "Load failed detected. Switching to next "
1404 "configuration. Transcoding video%s",
1405 p_sys
->transcoding_state
& TRANSCODING_AUDIO
? "/audio" : "");
1406 p_sys
->out_force_reload
= p_sys
->es_changed
= true;
1412 /*****************************************************************************
1413 * Open: connect to the Chromecast and initialize the sout
1414 *****************************************************************************/
1415 static int Open(vlc_object_t
*p_this
)
1417 sout_stream_t
*p_stream
= reinterpret_cast<sout_stream_t
*>(p_this
);
1418 sout_stream_sys_t
*p_sys
= NULL
;
1419 intf_sys_t
*p_intf
= NULL
;
1420 char *psz_ip
= NULL
;
1421 sout_stream_t
*p_sout
= NULL
;
1422 httpd_host_t
*httpd_host
= NULL
;
1423 bool b_supports_video
= true;
1424 int i_local_server_port
;
1426 std::stringstream ss
;
1428 config_ChainParse(p_stream
, SOUT_CFG_PREFIX
, ppsz_sout_options
, p_stream
->p_cfg
);
1430 psz_ip
= var_GetNonEmptyString( p_stream
, SOUT_CFG_PREFIX
"ip");
1431 if ( psz_ip
== NULL
)
1433 msg_Err( p_this
, "missing Chromecast IP address" );
1437 i_device_port
= var_InheritInteger(p_stream
, SOUT_CFG_PREFIX
"port");
1438 i_local_server_port
= var_InheritInteger(p_stream
, SOUT_CFG_PREFIX
"http-port");
1440 var_Create(p_stream
, "http-port", VLC_VAR_INTEGER
);
1441 var_SetInteger(p_stream
, "http-port", i_local_server_port
);
1442 var_Create(p_stream
, "http-host", VLC_VAR_STRING
);
1443 var_SetString(p_stream
, "http-host", "");
1444 httpd_host
= vlc_http_HostNew(VLC_OBJECT(p_stream
));
1445 if (httpd_host
== NULL
)
1450 p_intf
= new intf_sys_t( p_this
, i_local_server_port
, psz_ip
, i_device_port
,
1453 catch (const std::runtime_error
& err
)
1455 msg_Err( p_this
, "cannot load the Chromecast controller (%s)", err
.what() );
1458 catch (const std::bad_alloc
& )
1464 /* check if we can open the proper sout */
1465 ss
<< "http{mux=" << DEFAULT_MUXER
<< "}";
1466 p_sout
= sout_StreamChainNew( p_stream
->p_sout
, ss
.str().c_str(), NULL
, NULL
);
1467 if (p_sout
== NULL
) {
1468 msg_Dbg(p_stream
, "could not create sout chain:%s", ss
.str().c_str());
1471 sout_StreamChainDelete( p_sout
, NULL
);
1473 b_supports_video
= var_GetBool(p_stream
, SOUT_CFG_PREFIX
"video");
1477 p_sys
= new sout_stream_sys_t( httpd_host
, p_intf
, b_supports_video
,
1478 i_local_server_port
);
1480 catch ( std::exception
& ex
)
1482 msg_Err( p_stream
, "Failed to instantiate sout_stream_sys_t: %s", ex
.what() );
1487 p_intf
->setOnInputEventCb(on_input_event_cb
, p_stream
);
1489 /* prevent sout-mux-caching since chromecast-proxy is already doing it */
1490 var_Create( p_stream
->p_sout
, "sout-mux-caching", VLC_VAR_INTEGER
);
1491 var_SetInteger( p_stream
->p_sout
, "sout-mux-caching", 0 );
1493 var_Create( p_stream
->p_sout
, SOUT_CFG_PREFIX
"sys", VLC_VAR_ADDRESS
);
1494 var_SetAddress( p_stream
->p_sout
, SOUT_CFG_PREFIX
"sys", p_sys
);
1496 var_Create( p_stream
->p_sout
, SOUT_CFG_PREFIX
"access-out-sys", VLC_VAR_ADDRESS
);
1498 // Set the sout callbacks.
1499 p_stream
->pf_add
= Add
;
1500 p_stream
->pf_del
= Del
;
1501 p_stream
->pf_send
= Send
;
1502 p_stream
->pf_flush
= Flush
;
1504 p_stream
->p_sys
= p_sys
;
1513 httpd_HostDelete(httpd_host
);
1516 return VLC_EGENERIC
;
1519 /*****************************************************************************
1520 * Close: destroy interface
1521 *****************************************************************************/
1522 static void Close(vlc_object_t
*p_this
)
1524 sout_stream_t
*p_stream
= reinterpret_cast<sout_stream_t
*>(p_this
);
1525 sout_stream_sys_t
*p_sys
= p_stream
->p_sys
;
1527 assert(p_sys
->out_streams
.empty() && p_sys
->streams
.empty());
1528 var_Destroy( p_stream
->p_sout
, SOUT_CFG_PREFIX
"sys" );
1529 var_Destroy( p_stream
->p_sout
, SOUT_CFG_PREFIX
"sout-mux-caching" );
1531 assert(p_sys
->streams
.empty() && p_sys
->out_streams
.empty());
1533 httpd_host_t
*httpd_host
= p_sys
->httpd_host
;
1534 delete p_sys
->p_intf
;
1536 /* Delete last since p_intf and p_sys depends on httpd_host */
1537 httpd_HostDelete(httpd_host
);