chromecast: always set vb value for vtenc
[vlc.git] / modules / stream_out / chromecast / cast.cpp
blob8830f57cad0bac745f0c647a6fda606e5ed5be75
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>
39 #include <vlc_httpd.h>
41 #include <cassert>
43 #define TRANSCODING_NONE 0x0
44 #define TRANSCODING_VIDEO 0x1
45 #define TRANSCODING_AUDIO 0x2
47 #if 0
48 /* TODO: works only with internal spu and transcoding/blending for now */
49 #define CC_ENABLE_SPU
50 #endif
52 struct sout_access_out_sys_t
54 sout_access_out_sys_t(httpd_host_t *httpd_host, intf_sys_t * const intf,
55 const char *psz_url);
56 ~sout_access_out_sys_t();
58 void clear();
59 void stop();
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);
64 void close();
67 private:
68 void clearUnlocked();
69 void initCopy();
70 void putCopy(block_t *p_block);
71 void restoreCopy();
73 intf_sys_t * const m_intf;
74 httpd_url_t *m_url;
75 httpd_client_t *m_client;
76 vlc_fifo_t *m_fifo;
77 block_t *m_header;
78 block_t *m_copy_chain;
79 block_t **m_copy_last;
80 size_t m_copy_size;
81 bool m_eof;
82 std::string m_mime;
85 typedef struct sout_stream_id_sys_t sout_stream_id_sys_t;
87 struct sout_stream_sys_t
89 sout_stream_sys_t(httpd_host_t *httpd_host, intf_sys_t * const intf, bool has_video, int port)
90 : httpd_host(httpd_host)
91 , access_out_live(httpd_host, intf, "/stream")
92 , p_out(NULL)
93 , p_intf(intf)
94 , b_supports_video(has_video)
95 , i_port(port)
96 , first_video_keyframe_pts( -1 )
97 , es_changed( true )
98 , cc_has_input( false )
99 , cc_reload( false )
100 , cc_flushing( false )
101 , has_video( false )
102 , out_force_reload( false )
103 , perf_warning_shown( false )
104 , transcoding_state( TRANSCODING_NONE )
105 , venc_opt_idx ( -1 )
106 , out_streams_added( 0 )
108 assert(p_intf != NULL);
109 vlc_mutex_init(&lock);
112 ~sout_stream_sys_t()
114 vlc_mutex_destroy(&lock);
117 bool canDecodeVideo( vlc_fourcc_t i_codec ) const;
118 bool canDecodeAudio( sout_stream_t* p_stream, vlc_fourcc_t i_codec,
119 const audio_format_t* p_fmt ) const;
120 bool startSoutChain(sout_stream_t* p_stream,
121 const std::vector<sout_stream_id_sys_t*> &new_streams,
122 const std::string &sout, int new_transcoding_state);
123 void stopSoutChain(sout_stream_t* p_stream);
124 sout_stream_id_sys_t *GetSubId( sout_stream_t*, sout_stream_id_sys_t*, bool update = true );
125 bool isFlushing( sout_stream_t* );
126 void setNextTranscodingState();
127 bool transcodingCanFallback() const;
129 httpd_host_t *httpd_host;
130 sout_access_out_sys_t access_out_live;
132 sout_stream_t *p_out;
133 std::string mime;
135 vlc_mutex_t lock; /* for input events cb */
137 intf_sys_t * const p_intf;
138 const bool b_supports_video;
139 const int i_port;
141 sout_stream_id_sys_t * video_proxy_id;
142 vlc_tick_t first_video_keyframe_pts;
144 bool es_changed;
145 bool cc_has_input;
146 bool cc_reload;
147 bool cc_flushing;
148 bool has_video;
149 bool out_force_reload;
150 bool perf_warning_shown;
151 int transcoding_state;
152 int venc_opt_idx;
153 std::vector<sout_stream_id_sys_t*> streams;
154 std::vector<sout_stream_id_sys_t*> out_streams;
155 unsigned int out_streams_added;
156 unsigned int spu_streams_count;
158 private:
159 std::string GetVencOption( sout_stream_t *, vlc_fourcc_t *,
160 const video_format_t *, int );
161 std::string GetAcodecOption( sout_stream_t *, vlc_fourcc_t *, const audio_format_t *, int );
162 std::string GetVcodecOption( sout_stream_t *, vlc_fourcc_t *, const video_format_t *, int );
163 bool UpdateOutput( sout_stream_t * );
166 struct sout_stream_id_sys_t
168 es_format_t fmt;
169 sout_stream_id_sys_t *p_sub_id;
170 bool flushed;
173 #define SOUT_CFG_PREFIX "sout-chromecast-"
175 static const char DEFAULT_MUXER[] = "avformat{mux=matroska,options={live=1}}";
176 static const char DEFAULT_MUXER_WEBM[] = "avformat{mux=webm,options={live=1}}";
179 /*****************************************************************************
180 * Local prototypes
181 *****************************************************************************/
182 static int Open(vlc_object_t *);
183 static void Close(vlc_object_t *);
184 static int ProxyOpen(vlc_object_t *);
185 static int AccessOpen(vlc_object_t *);
186 static void AccessClose(vlc_object_t *);
188 static const char *const ppsz_sout_options[] = {
189 "ip", "port", "http-port", "video", NULL
192 /*****************************************************************************
193 * Module descriptor
194 *****************************************************************************/
196 #define HTTP_PORT_TEXT N_("HTTP port")
197 #define HTTP_PORT_LONGTEXT N_("This sets the HTTP port of the local server " \
198 "used to stream the media to the Chromecast.")
199 #define PERF_TEXT N_( "Performance warning" )
200 #define PERF_LONGTEXT N_( "Display a performance warning when transcoding" )
201 #define AUDIO_PASSTHROUGH_TEXT N_( "Enable Audio passthrough" )
202 #define AUDIO_PASSTHROUGH_LONGTEXT N_( "Disable if your receiver does not support Dolby®." )
204 enum {
205 CONVERSION_QUALITY_HIGH = 0,
206 CONVERSION_QUALITY_MEDIUM = 1,
207 CONVERSION_QUALITY_LOW = 2,
208 CONVERSION_QUALITY_LOWCPU = 3,
211 #if defined (__ANDROID__) || defined (__arm__) || (defined (TARGET_OS_IPHONE) && TARGET_OS_IPHONE)
212 # define CONVERSION_QUALITY_DEFAULT CONVERSION_QUALITY_LOW
213 #else
214 # define CONVERSION_QUALITY_DEFAULT CONVERSION_QUALITY_MEDIUM
215 #endif
217 static const int conversion_quality_list[] = {
218 CONVERSION_QUALITY_HIGH,
219 CONVERSION_QUALITY_MEDIUM,
220 CONVERSION_QUALITY_LOW,
221 CONVERSION_QUALITY_LOWCPU,
223 static const char *const conversion_quality_list_text[] = {
224 N_( "High (high quality and high bandwidth)" ),
225 N_( "Medium (medium quality and medium bandwidth)" ),
226 N_( "Low (low quality and low bandwidth)" ),
227 N_( "Low CPU (low quality but high bandwidth)" ),
230 #define CONVERSION_QUALITY_TEXT N_( "Conversion quality" )
231 #define CONVERSION_QUALITY_LONGTEXT N_( "Change this option to increase conversion speed or quality." )
233 #define IP_ADDR_TEXT N_("IP Address")
234 #define IP_ADDR_LONGTEXT N_("IP Address of the Chromecast.")
235 #define PORT_TEXT N_("Chromecast port")
236 #define PORT_LONGTEXT N_("The port used to talk to the Chromecast.")
238 /* Fifo size after we tell the demux to pace */
239 #define HTTPD_BUFFER_PACE INT64_C(2 * 1024 * 1024) /* 2 MB */
240 /* Fifo size after we drop packets (should not happen) */
241 #define HTTPD_BUFFER_MAX INT64_C(32 * 1024 * 1024) /* 32 MB */
242 #define HTTPD_BUFFER_COPY_MAX INT64_C(10 * 1024 * 1024) /* 10 MB */
244 vlc_module_begin ()
246 set_shortname(N_("Chromecast"))
247 set_description(N_("Chromecast stream output"))
248 set_capability("sout stream", 0)
249 add_shortcut("chromecast")
250 set_category(CAT_SOUT)
251 set_subcategory(SUBCAT_SOUT_STREAM)
252 set_callbacks(Open, Close)
254 add_string(SOUT_CFG_PREFIX "ip", NULL, NULL, NULL, false)
255 change_private()
256 add_integer(SOUT_CFG_PREFIX "port", CHROMECAST_CONTROL_PORT, NULL, NULL, false)
257 change_private()
258 add_bool(SOUT_CFG_PREFIX "video", true, NULL, NULL, false)
259 change_private()
260 add_integer(SOUT_CFG_PREFIX "http-port", HTTP_PORT, HTTP_PORT_TEXT, HTTP_PORT_LONGTEXT, false)
261 add_obsolete_string(SOUT_CFG_PREFIX "mux")
262 add_obsolete_string(SOUT_CFG_PREFIX "mime")
263 add_integer(SOUT_CFG_PREFIX "show-perf-warning", 1, PERF_TEXT, PERF_LONGTEXT, true )
264 change_private()
265 add_bool(SOUT_CFG_PREFIX "audio-passthrough", false, AUDIO_PASSTHROUGH_TEXT, AUDIO_PASSTHROUGH_LONGTEXT, false )
266 add_integer(SOUT_CFG_PREFIX "conversion-quality", CONVERSION_QUALITY_DEFAULT,
267 CONVERSION_QUALITY_TEXT, CONVERSION_QUALITY_LONGTEXT, false );
268 change_integer_list(conversion_quality_list, conversion_quality_list_text)
270 add_submodule()
271 /* sout proxy that start the cc input when all streams are loaded */
272 add_shortcut("chromecast-proxy")
273 set_capability("sout stream", 0)
274 set_callbacks(ProxyOpen, NULL)
275 add_submodule()
276 set_subcategory(SUBCAT_SOUT_ACO)
277 add_shortcut("chromecast-http")
278 set_capability("sout access", 0)
279 set_callbacks(AccessOpen, AccessClose)
280 vlc_module_end ()
282 static void *ProxyAdd(sout_stream_t *p_stream, const es_format_t *p_fmt)
284 sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
285 sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( sout_StreamIdAdd(p_stream->p_next, p_fmt) );
286 if (id)
288 if (p_fmt->i_cat == VIDEO_ES)
289 p_sys->video_proxy_id = id;
290 p_sys->out_streams_added++;
292 return id;
295 static void ProxyDel(sout_stream_t *p_stream, void *_id)
297 sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
298 sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( _id );
299 p_sys->out_streams_added--;
300 if (id == p_sys->video_proxy_id)
301 p_sys->video_proxy_id = NULL;
302 return sout_StreamIdDel(p_stream->p_next, id);
305 static int ProxySend(sout_stream_t *p_stream, void *_id, block_t *p_buffer)
307 sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
308 sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( _id );
309 if (p_sys->cc_has_input
310 || p_sys->out_streams_added >= p_sys->out_streams.size() - p_sys->spu_streams_count)
312 if (p_sys->has_video)
314 // In case of video, the first block must be a keyframe
315 if (id == p_sys->video_proxy_id)
317 if (p_sys->first_video_keyframe_pts == -1
318 && p_buffer->i_flags & BLOCK_FLAG_TYPE_I)
319 p_sys->first_video_keyframe_pts = p_buffer->i_pts;
321 else // no keyframe for audio
322 p_buffer->i_flags &= ~BLOCK_FLAG_TYPE_I;
324 if (p_buffer->i_pts < p_sys->first_video_keyframe_pts
325 || p_sys->first_video_keyframe_pts == -1)
327 block_Release(p_buffer);
328 return VLC_SUCCESS;
332 vlc_tick_t pause_delay = p_sys->p_intf->getPauseDelay();
333 if( p_buffer->i_pts != VLC_TICK_INVALID )
334 p_buffer->i_pts -= pause_delay;
335 if( p_buffer->i_dts != VLC_TICK_INVALID )
336 p_buffer->i_dts -= pause_delay;
338 int ret = sout_StreamIdSend(p_stream->p_next, id, p_buffer);
339 if (ret == VLC_SUCCESS && !p_sys->cc_has_input)
341 /* Start the chromecast only when all streams are added into the
342 * last sout (the http one) */
343 p_sys->p_intf->setHasInput(p_sys->mime);
344 p_sys->cc_has_input = true;
346 return ret;
348 else
350 block_Release(p_buffer);
351 return VLC_SUCCESS;
355 static void ProxyFlush(sout_stream_t *p_stream, void *id)
357 sout_StreamFlush(p_stream->p_next, id);
360 static int ProxyOpen(vlc_object_t *p_this)
362 sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(p_this);
363 sout_stream_sys_t *p_sys = (sout_stream_sys_t *) var_InheritAddress(p_this, SOUT_CFG_PREFIX "sys");
364 if (p_sys == NULL || p_stream->p_next == NULL)
365 return VLC_EGENERIC;
367 p_stream->p_sys = (sout_stream_sys_t *) p_sys;
368 p_sys->out_streams_added = 0;
370 p_stream->pf_add = ProxyAdd;
371 p_stream->pf_del = ProxyDel;
372 p_stream->pf_send = ProxySend;
373 p_stream->pf_flush = ProxyFlush;
374 return VLC_SUCCESS;
377 static int httpd_url_cb(httpd_callback_sys_t *data, httpd_client_t *cl,
378 httpd_message_t *answer, const httpd_message_t *query)
380 sout_access_out_sys_t *p_sys = reinterpret_cast<sout_access_out_sys_t *>(data);
381 return p_sys->url_cb(cl, answer, query);
384 sout_access_out_sys_t::sout_access_out_sys_t(httpd_host_t *httpd_host,
385 intf_sys_t * const intf,
386 const char *psz_url)
387 : m_intf(intf)
388 , m_client(NULL)
389 , m_header(NULL)
390 , m_copy_chain(NULL)
391 , m_eof(true)
393 m_fifo = block_FifoNew();
394 if (!m_fifo)
395 throw std::runtime_error( "block_FifoNew failed" );
396 m_url = httpd_UrlNew(httpd_host, psz_url, NULL, NULL);
397 if (m_url == NULL)
399 block_FifoRelease(m_fifo);
400 throw std::runtime_error( "httpd_UrlNew failed" );
402 httpd_UrlCatch(m_url, HTTPD_MSG_GET, httpd_url_cb,
403 (httpd_callback_sys_t*)this);
404 initCopy();
407 sout_access_out_sys_t::~sout_access_out_sys_t()
409 httpd_UrlDelete(m_url);
410 block_FifoRelease(m_fifo);
413 void sout_access_out_sys_t::clearUnlocked()
415 block_ChainRelease(vlc_fifo_DequeueAllUnlocked(m_fifo));
416 if (m_header)
418 block_Release(m_header);
419 m_header = NULL;
421 m_eof = true;
422 initCopy();
425 void sout_access_out_sys_t::initCopy()
427 block_ChainRelease(m_copy_chain);
428 m_copy_chain = NULL;
429 m_copy_last = &m_copy_chain;
430 m_copy_size = 0;
433 void sout_access_out_sys_t::putCopy(block_t *p_block)
435 while (m_copy_size >= HTTPD_BUFFER_COPY_MAX)
437 assert(m_copy_chain);
438 block_t *copy = m_copy_chain;
439 m_copy_chain = copy->p_next;
440 m_copy_size -= copy->i_buffer;
441 block_Release(copy);
443 if (!m_copy_chain)
445 assert(m_copy_size == 0);
446 m_copy_last = &m_copy_chain;
448 block_ChainLastAppend(&m_copy_last, p_block);
449 m_copy_size += p_block->i_buffer;
452 void sout_access_out_sys_t::restoreCopy()
454 if (m_copy_chain)
456 fifo_put_back(m_copy_chain);
457 m_copy_chain = NULL;
458 initCopy();
462 void sout_access_out_sys_t::clear()
464 vlc_fifo_Lock(m_fifo);
465 clearUnlocked();
466 vlc_fifo_Unlock(m_fifo);
467 vlc_fifo_Signal(m_fifo);
470 void sout_access_out_sys_t::stop()
472 vlc_fifo_Lock(m_fifo);
473 clearUnlocked();
474 m_intf->setPacing(false);
475 m_client = NULL;
476 vlc_fifo_Unlock(m_fifo);
477 vlc_fifo_Signal(m_fifo);
480 void sout_access_out_sys_t::prepare(sout_stream_t *p_stream, const std::string &mime)
482 var_SetAddress(p_stream->p_sout, SOUT_CFG_PREFIX "access-out-sys", this);
484 vlc_fifo_Lock(m_fifo);
485 clearUnlocked();
486 m_intf->setPacing(false);
487 m_mime = mime;
488 m_eof = false;
489 vlc_fifo_Unlock(m_fifo);
492 void sout_access_out_sys_t::fifo_put_back(block_t *p_block)
494 block_t *p_fifo = vlc_fifo_DequeueAllUnlocked(m_fifo);
495 vlc_fifo_QueueUnlocked(m_fifo, p_block);
496 vlc_fifo_QueueUnlocked(m_fifo, p_fifo);
499 int sout_access_out_sys_t::url_cb(httpd_client_t *cl, httpd_message_t *answer,
500 const httpd_message_t *query)
502 if (!answer || !query || !cl)
503 return VLC_SUCCESS;
505 vlc_fifo_Lock(m_fifo);
507 if (!answer->i_body_offset)
509 /* When doing a lot a load requests, we can serve data to a client that
510 * will be closed (the close request is already sent). In that case, we
511 * should also serve data used by the old client to the new one. */
512 restoreCopy();
513 m_client = cl;
516 /* Send data per 512kB minimum */
517 size_t i_min_buffer = 524288;
518 while (m_client && vlc_fifo_GetBytes(m_fifo) < i_min_buffer && !m_eof)
519 vlc_fifo_Wait(m_fifo);
521 block_t *p_block = NULL;
522 if (m_client && vlc_fifo_GetBytes(m_fifo) > 0)
524 /* if less data is available, then we must be EOF */
525 if (vlc_fifo_GetBytes(m_fifo) < i_min_buffer)
527 assert(m_eof);
528 i_min_buffer = vlc_fifo_GetBytes(m_fifo);
530 block_t *p_first = vlc_fifo_DequeueUnlocked(m_fifo);
532 assert(p_first);
533 size_t i_total_size = p_first->i_buffer;
534 block_t *p_next = NULL, *p_cur = p_first;
536 while (i_total_size < i_min_buffer)
538 p_next = vlc_fifo_DequeueUnlocked(m_fifo);
539 assert(p_next);
540 i_total_size += p_next->i_buffer;
541 p_cur->p_next = p_next;
542 p_cur = p_cur->p_next;
544 assert(i_total_size >= i_min_buffer);
546 if (p_next != NULL)
548 p_block = block_Alloc(i_total_size);
549 if (p_block)
550 block_ChainExtract(p_first, p_block->p_buffer, p_block->i_buffer);
551 block_ChainRelease(p_first);
553 else
554 p_block = p_first;
556 if (vlc_fifo_GetBytes(m_fifo) < HTTPD_BUFFER_PACE)
557 m_intf->setPacing(false);
560 answer->i_proto = HTTPD_PROTO_HTTP;
561 answer->i_version= 0;
562 answer->i_type = HTTPD_MSG_ANSWER;
563 answer->i_status = 200;
565 if (p_block)
567 if (answer->i_body_offset == 0)
569 httpd_MsgAdd(answer, "Content-type", "%s", m_mime.c_str());
570 httpd_MsgAdd(answer, "Cache-Control", "no-cache");
571 httpd_MsgAdd(answer, "Connection", "close");
574 const bool send_header = answer->i_body_offset == 0 && m_header != NULL;
575 size_t i_answer_size = p_block->i_buffer;
576 if (send_header)
577 i_answer_size += m_header->i_buffer;
579 answer->p_body = (uint8_t *) malloc(i_answer_size);
580 if (answer->p_body)
582 answer->i_body = i_answer_size;
583 answer->i_body_offset += answer->i_body;
584 size_t i_block_offset = 0;
585 if (send_header)
587 memcpy(answer->p_body, m_header->p_buffer, m_header->i_buffer);
588 i_block_offset = m_header->i_buffer;
590 memcpy(&answer->p_body[i_block_offset], p_block->p_buffer, p_block->i_buffer);
593 putCopy(p_block);
595 if (!answer->i_body)
596 httpd_MsgAdd(answer, "Connection", "close");
598 vlc_fifo_Unlock(m_fifo);
599 return VLC_SUCCESS;
602 ssize_t sout_access_out_sys_t::write(sout_access_out_t *p_access, block_t *p_block)
604 size_t i_len = p_block->i_buffer;
606 vlc_fifo_Lock(m_fifo);
608 if (p_block->i_flags & BLOCK_FLAG_HEADER)
610 if (m_header)
611 block_Release(m_header);
612 m_header = p_block;
614 else
616 /* Drop buffer is the fifo is really full */
617 if (vlc_fifo_GetBytes(m_fifo) >= HTTPD_BUFFER_PACE)
619 /* XXX: Hackisk way to pace between the sout (controlled by the
620 * decoder thread) and the demux filter (controlled by the input
621 * thread). When the httpd FIFO reaches a specific size, we tell
622 * the demux filter to pace and wait a little before queing this
623 * block, but not too long since we don't want to block decoder
624 * thread controls. If the pacing fails (should not happen), we
625 * drop the first block in order to make room. The demux filter
626 * will be unpaced when the data is read from the httpd thread. */
628 m_intf->setPacing(true);
630 while (vlc_fifo_GetBytes(m_fifo) >= HTTPD_BUFFER_MAX)
632 block_t *p_drop = vlc_fifo_DequeueUnlocked(m_fifo);
633 msg_Warn(p_access, "httpd buffer full: dropping %zuB", p_drop->i_buffer);
634 block_Release(p_drop);
637 vlc_fifo_QueueUnlocked(m_fifo, p_block);
640 m_eof = false;
642 vlc_fifo_Unlock(m_fifo);
643 vlc_fifo_Signal(m_fifo);
645 return i_len;
648 void sout_access_out_sys_t::close()
650 vlc_fifo_Lock(m_fifo);
651 m_eof = true;
652 m_intf->setPacing(false);
653 vlc_fifo_Unlock(m_fifo);
654 vlc_fifo_Signal(m_fifo);
657 ssize_t AccessWrite(sout_access_out_t *p_access, block_t *p_block)
659 sout_access_out_sys_t *p_sys = reinterpret_cast<sout_access_out_sys_t *>( p_access->p_sys );
660 return p_sys->write(p_access, p_block);
663 static int AccessControl(sout_access_out_t *p_access, int i_query, va_list args)
665 (void) p_access;
667 switch (i_query)
669 case ACCESS_OUT_CONTROLS_PACE:
670 *va_arg(args, bool *) = true;
671 break;
672 default:
673 return VLC_EGENERIC;
675 return VLC_SUCCESS;
678 static int AccessOpen(vlc_object_t *p_this)
680 sout_access_out_t *p_access = (sout_access_out_t*)p_this;
682 sout_access_out_sys_t *p_sys = (sout_access_out_sys_t *)
683 var_InheritAddress(p_access, SOUT_CFG_PREFIX "access-out-sys");
684 if (p_sys == NULL)
685 return VLC_EGENERIC;
687 p_access->pf_write = AccessWrite;
688 p_access->pf_control = AccessControl;
689 p_access->p_sys = p_sys;
691 return VLC_SUCCESS;
694 static void AccessClose(vlc_object_t *p_this)
696 sout_access_out_t *p_access = (sout_access_out_t*)p_this;
697 sout_access_out_sys_t *p_sys = reinterpret_cast<sout_access_out_sys_t *>( p_access->p_sys );
699 p_sys->close();
702 /*****************************************************************************
703 * Sout callbacks
704 *****************************************************************************/
705 static void *Add(sout_stream_t *p_stream, const es_format_t *p_fmt)
707 sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
708 vlc_mutex_locker locker(&p_sys->lock);
710 if (!p_sys->b_supports_video)
712 if (p_fmt->i_cat != AUDIO_ES)
713 return NULL;
716 sout_stream_id_sys_t *p_sys_id = (sout_stream_id_sys_t *)malloc( sizeof(sout_stream_id_sys_t) );
717 if (p_sys_id != NULL)
719 es_format_Copy( &p_sys_id->fmt, p_fmt );
720 p_sys_id->p_sub_id = NULL;
721 p_sys_id->flushed = false;
723 p_sys->streams.push_back( p_sys_id );
724 p_sys->es_changed = true;
726 return p_sys_id;
730 static void DelInternal(sout_stream_t *p_stream, void *_id, bool reset_config)
732 sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
733 sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( _id );
735 for (std::vector<sout_stream_id_sys_t*>::iterator it = p_sys->streams.begin();
736 it != p_sys->streams.end(); )
738 sout_stream_id_sys_t *p_sys_id = *it;
739 if ( p_sys_id == id )
741 if ( p_sys_id->p_sub_id != NULL )
743 sout_StreamIdDel( p_sys->p_out, p_sys_id->p_sub_id );
744 for (std::vector<sout_stream_id_sys_t*>::iterator out_it = p_sys->out_streams.begin();
745 out_it != p_sys->out_streams.end(); )
747 if (*out_it == id)
749 p_sys->out_streams.erase(out_it);
750 p_sys->es_changed = reset_config;
751 p_sys->out_force_reload = reset_config;
752 if( p_sys_id->fmt.i_cat == VIDEO_ES )
753 p_sys->has_video = false;
754 else if( p_sys_id->fmt.i_cat == SPU_ES )
755 p_sys->spu_streams_count--;
756 break;
758 out_it++;
762 es_format_Clean( &p_sys_id->fmt );
763 free( p_sys_id );
764 p_sys->streams.erase( it );
765 break;
767 it++;
770 if ( p_sys->out_streams.empty() )
772 p_sys->stopSoutChain(p_stream);
773 p_sys->p_intf->requestPlayerStop();
774 p_sys->access_out_live.clear();
775 p_sys->transcoding_state = TRANSCODING_NONE;
779 static void Del(sout_stream_t *p_stream, void *_id)
781 sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
782 sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( _id );
784 vlc_mutex_locker locker(&p_sys->lock);
785 DelInternal(p_stream, id, true);
789 * Transcode steps:
790 * 0: Accept HEVC/VP9 & all supported audio formats
791 * 1: Transcode to h264 & accept all supported audio formats if the video codec
792 * was HEVC/VP9
793 * 2: Transcode to H264 & MP3
795 * Additionally:
796 * - Allow (E)AC3 passthrough depending on the audio-passthrough
797 * config value, except for the final step, where we just give up and transcode
798 * everything.
799 * - Disallow multichannel AAC
801 * Supported formats: https://developers.google.com/cast/docs/media
804 bool sout_stream_sys_t::canDecodeVideo( vlc_fourcc_t i_codec ) const
806 if( transcoding_state & TRANSCODING_VIDEO )
807 return false;
808 return i_codec == VLC_CODEC_H264 || i_codec == VLC_CODEC_HEVC
809 || i_codec == VLC_CODEC_VP8 || i_codec == VLC_CODEC_VP9;
812 bool sout_stream_sys_t::canDecodeAudio( sout_stream_t *p_stream,
813 vlc_fourcc_t i_codec,
814 const audio_format_t* p_fmt ) const
816 if( transcoding_state & TRANSCODING_AUDIO )
817 return false;
818 if ( i_codec == VLC_CODEC_A52 || i_codec == VLC_CODEC_EAC3 )
820 return var_InheritBool( p_stream, SOUT_CFG_PREFIX "audio-passthrough" );
822 if ( i_codec == VLC_FOURCC('h', 'a', 'a', 'c') ||
823 i_codec == VLC_FOURCC('l', 'a', 'a', 'c') ||
824 i_codec == VLC_FOURCC('s', 'a', 'a', 'c') ||
825 i_codec == VLC_CODEC_MP4A )
827 return p_fmt->i_channels <= 2;
829 return i_codec == VLC_CODEC_VORBIS || i_codec == VLC_CODEC_OPUS ||
830 i_codec == VLC_CODEC_MP3;
833 void sout_stream_sys_t::stopSoutChain(sout_stream_t *p_stream)
835 (void) p_stream;
837 if ( unlikely( p_out != NULL ) )
839 for ( size_t i = 0; i < out_streams.size(); i++ )
841 if ( out_streams[i]->p_sub_id != NULL )
843 sout_StreamIdDel( p_out, out_streams[i]->p_sub_id );
844 out_streams[i]->p_sub_id = NULL;
847 out_streams.clear();
848 sout_StreamChainDelete( p_out, NULL );
849 p_out = NULL;
853 bool sout_stream_sys_t::startSoutChain(sout_stream_t *p_stream,
854 const std::vector<sout_stream_id_sys_t*> &new_streams,
855 const std::string &sout, int new_transcoding_state)
857 stopSoutChain( p_stream );
859 msg_Dbg( p_stream, "Creating chain %s", sout.c_str() );
860 cc_has_input = false;
861 cc_reload = false;
862 first_video_keyframe_pts = -1;
863 video_proxy_id = NULL;
864 has_video = false;
865 out_streams = new_streams;
866 spu_streams_count = 0;
867 transcoding_state = new_transcoding_state;
869 access_out_live.prepare( p_stream, mime );
871 p_out = sout_StreamChainNew( p_stream->p_sout, sout.c_str(), NULL, NULL);
872 if (p_out == NULL) {
873 msg_Dbg(p_stream, "could not create sout chain:%s", sout.c_str());
874 out_streams.clear();
875 access_out_live.clear();
876 return false;
879 /* check the streams we can actually add */
880 for (std::vector<sout_stream_id_sys_t*>::iterator it = out_streams.begin();
881 it != out_streams.end(); )
883 sout_stream_id_sys_t *p_sys_id = *it;
884 p_sys_id->p_sub_id = reinterpret_cast<sout_stream_id_sys_t *>( sout_StreamIdAdd( p_out, &p_sys_id->fmt ) );
885 if ( p_sys_id->p_sub_id == NULL )
887 msg_Err( p_stream, "can't handle %4.4s stream", (char *)&p_sys_id->fmt.i_codec );
888 es_format_Clean( &p_sys_id->fmt );
889 it = out_streams.erase( it );
891 else
893 if( p_sys_id->fmt.i_cat == VIDEO_ES )
894 has_video = true;
895 else if( p_sys_id->fmt.i_cat == SPU_ES )
896 spu_streams_count++;
897 ++it;
901 if (out_streams.empty())
903 stopSoutChain( p_stream );
904 access_out_live.clear();
905 return false;
908 /* Ask to retry if we are not transcoding everything (because we can trust
909 * what we encode) */
910 p_intf->setRetryOnFail(transcodingCanFallback());
912 return true;
915 void sout_stream_sys_t::setNextTranscodingState()
917 if (!(transcoding_state & TRANSCODING_VIDEO))
918 transcoding_state |= TRANSCODING_VIDEO;
919 else if (!(transcoding_state & TRANSCODING_AUDIO))
920 transcoding_state = TRANSCODING_AUDIO;
923 bool sout_stream_sys_t::transcodingCanFallback() const
925 return transcoding_state != (TRANSCODING_VIDEO|TRANSCODING_AUDIO);
928 static std::string GetVencVPXOption( sout_stream_t * /* p_stream */,
929 const video_format_t * /* p_vid */,
930 int /* i_quality */ )
932 return "venc=vpx{quality-mode=1}";
935 static std::string GetVencQSVH264Option( sout_stream_t * /* p_stream */,
936 const video_format_t * /* p_vid */,
937 int i_quality )
939 std::stringstream ssout;
940 static const char video_target_usage_quality[] = "quality";
941 static const char video_target_usage_balanced[] = "balanced";
942 static const char video_target_usage_speed[] = "speed";
943 static const char video_bitrate_high[] = "vb=8000000";
944 static const char video_bitrate_low[] = "vb=3000000";
945 const char *psz_video_target_usage;
946 const char *psz_video_bitrate;
948 switch ( i_quality )
950 case CONVERSION_QUALITY_HIGH:
951 psz_video_target_usage = video_target_usage_quality;
952 psz_video_bitrate = video_bitrate_high;
953 break;
954 case CONVERSION_QUALITY_MEDIUM:
955 psz_video_target_usage = video_target_usage_balanced;
956 psz_video_bitrate = video_bitrate_high;
957 break;
958 case CONVERSION_QUALITY_LOW:
959 psz_video_target_usage = video_target_usage_balanced;
960 psz_video_bitrate = video_bitrate_low;
961 break;
962 default:
963 case CONVERSION_QUALITY_LOWCPU:
964 psz_video_target_usage = video_target_usage_speed;
965 psz_video_bitrate = video_bitrate_low;
966 break;
969 ssout << "venc=qsv{target-usage=" << psz_video_target_usage <<
970 "}," << psz_video_bitrate;
971 return ssout.str();
974 static std::string GetVencX264Option( sout_stream_t * /* p_stream */,
975 const video_format_t *p_vid,
976 int i_quality )
978 std::stringstream ssout;
979 static const char video_x264_preset_veryfast[] = "veryfast";
980 static const char video_x264_preset_ultrafast[] = "ultrafast";
981 const char *psz_video_x264_preset;
982 unsigned i_video_x264_crf_hd, i_video_x264_crf_720p;
984 switch ( i_quality )
986 case CONVERSION_QUALITY_HIGH:
987 i_video_x264_crf_hd = i_video_x264_crf_720p = 21;
988 psz_video_x264_preset = video_x264_preset_veryfast;
989 break;
990 case CONVERSION_QUALITY_MEDIUM:
991 i_video_x264_crf_hd = 23;
992 i_video_x264_crf_720p = 21;
993 psz_video_x264_preset = video_x264_preset_veryfast;
994 break;
995 case CONVERSION_QUALITY_LOW:
996 i_video_x264_crf_hd = i_video_x264_crf_720p = 23;
997 psz_video_x264_preset = video_x264_preset_veryfast;
998 break;
999 default:
1000 case CONVERSION_QUALITY_LOWCPU:
1001 i_video_x264_crf_hd = i_video_x264_crf_720p = 23;
1002 psz_video_x264_preset = video_x264_preset_ultrafast;
1003 break;
1006 const bool b_hdres = p_vid == NULL || p_vid->i_height == 0 || p_vid->i_height >= 800;
1007 unsigned i_video_x264_crf = b_hdres ? i_video_x264_crf_hd : i_video_x264_crf_720p;
1009 ssout << "venc=x264{preset=" << psz_video_x264_preset
1010 << ",crf=" << i_video_x264_crf << "}";
1011 return ssout.str();
1014 #ifdef __APPLE__
1015 static std::string GetVencAvcodecVTOption( sout_stream_t * /* p_stream */,
1016 const video_format_t * p_vid,
1017 int i_quality )
1019 std::stringstream ssout;
1020 ssout << "venc=avcodec{codec=h264_videotoolbox,options{realtime=1}}";
1021 switch( i_quality )
1023 /* Here, performances issues won't come from videotoolbox but from
1024 * some old chromecast devices */
1026 case CONVERSION_QUALITY_HIGH:
1027 break;
1028 case CONVERSION_QUALITY_MEDIUM:
1029 ssout << ",vb=8000000";
1030 break;
1031 case CONVERSION_QUALITY_LOW:
1032 case CONVERSION_QUALITY_LOWCPU:
1033 ssout << ",vb=3000000";
1034 break;
1037 return ssout.str();
1039 #endif
1041 static struct
1043 vlc_fourcc_t fcc;
1044 std::string (*get_opt)( sout_stream_t *, const video_format_t *, int);
1045 } venc_opt_list[] = {
1046 #ifdef __APPLE__
1047 { .fcc = VLC_CODEC_H264, .get_opt = GetVencAvcodecVTOption },
1048 #endif
1049 { .fcc = VLC_CODEC_H264, .get_opt = GetVencQSVH264Option },
1050 { .fcc = VLC_CODEC_H264, .get_opt = GetVencX264Option },
1051 { .fcc = VLC_CODEC_VP8, .get_opt = GetVencVPXOption },
1052 { .fcc = VLC_CODEC_H264, .get_opt = NULL },
1055 std::string
1056 sout_stream_sys_t::GetVencOption( sout_stream_t *p_stream, vlc_fourcc_t *p_codec_video,
1057 const video_format_t *p_vid, int i_quality )
1059 for( size_t i = (venc_opt_idx == -1 ? 0 : venc_opt_idx);
1060 i < ARRAY_SIZE(venc_opt_list); ++i )
1062 std::stringstream ssout, ssvenc;
1063 char fourcc[5];
1064 ssvenc << "vcodec=";
1065 vlc_fourcc_to_char( venc_opt_list[i].fcc, fourcc );
1066 fourcc[4] = '\0';
1067 ssvenc << fourcc << ',';
1069 if( venc_opt_list[i].get_opt != NULL )
1070 ssvenc << venc_opt_list[i].get_opt( p_stream, p_vid, i_quality ) << ',';
1072 if( venc_opt_list[i].get_opt == NULL
1073 || ( venc_opt_idx != -1 && (unsigned) venc_opt_idx == i) )
1075 venc_opt_idx = i;
1076 *p_codec_video = venc_opt_list[i].fcc;
1077 return ssvenc.str();
1080 /* Test if a module can encode with the specified options / fmt_video. */
1081 ssout << "transcode{" << ssvenc.str() << "}:dummy";
1083 sout_stream_t *p_sout_test =
1084 sout_StreamChainNew( p_stream->p_sout, ssout.str().c_str(), NULL, NULL );
1086 if( p_sout_test != NULL )
1088 p_sout_test->obj.flags |= OBJECT_FLAGS_QUIET|OBJECT_FLAGS_NOINTERACT;
1090 es_format_t fmt;
1091 es_format_InitFromVideo( &fmt, p_vid );
1092 fmt.i_codec = fmt.video.i_chroma = VLC_CODEC_I420;
1094 /* Test the maximum size/fps we will encode */
1095 fmt.video.i_visible_width = fmt.video.i_width = 1920;
1096 fmt.video.i_visible_height = fmt.video.i_height = 1080;
1097 fmt.video.i_frame_rate = 30;
1098 fmt.video.i_frame_rate_base = 1;
1100 void *id = sout_StreamIdAdd( p_sout_test, &fmt );
1102 es_format_Clean( &fmt );
1103 const bool success = id != NULL;
1105 if( id )
1106 sout_StreamIdDel( p_sout_test, id );
1107 sout_StreamChainDelete( p_sout_test, NULL );
1109 if( success )
1111 venc_opt_idx = i;
1112 *p_codec_video = venc_opt_list[i].fcc;
1113 return ssvenc.str();
1117 vlc_assert_unreachable();
1120 std::string
1121 sout_stream_sys_t::GetVcodecOption( sout_stream_t *p_stream, vlc_fourcc_t *p_codec_video,
1122 const video_format_t *p_vid, int i_quality )
1124 std::stringstream ssout;
1125 static const char video_maxres_hd[] = "maxwidth=1920,maxheight=1080";
1126 static const char video_maxres_720p[] = "maxwidth=1280,maxheight=720";
1128 ssout << GetVencOption( p_stream, p_codec_video, p_vid, i_quality );
1130 switch ( i_quality )
1132 case CONVERSION_QUALITY_HIGH:
1133 case CONVERSION_QUALITY_MEDIUM:
1134 ssout << video_maxres_hd << ',';
1135 break;
1136 default:
1137 ssout << video_maxres_720p << ',';
1140 if( p_vid == NULL
1141 || p_vid->i_frame_rate == 0 || p_vid->i_frame_rate_base == 0
1142 || ( p_vid->i_frame_rate / p_vid->i_frame_rate_base ) > 30 )
1144 /* Even force 24fps if the frame rate is unknown */
1145 msg_Warn( p_stream, "lowering frame rate to 24fps" );
1146 ssout << "fps=24,";
1149 msg_Dbg( p_stream, "Converting video to %.4s", (const char*)p_codec_video );
1151 return ssout.str();
1154 std::string
1155 sout_stream_sys_t::GetAcodecOption( sout_stream_t *p_stream, vlc_fourcc_t *p_codec_audio,
1156 const audio_format_t *p_aud, int i_quality )
1158 std::stringstream ssout;
1160 bool b_audio_mp3;
1162 /* If we were already transcoding: force mp3 because maybe the CC may
1163 * have failed because of vorbis. */
1164 if( transcoding_state & TRANSCODING_AUDIO )
1165 b_audio_mp3 = true;
1166 else
1168 switch ( i_quality )
1170 case CONVERSION_QUALITY_HIGH:
1171 case CONVERSION_QUALITY_MEDIUM:
1172 b_audio_mp3 = false;
1173 break;
1174 default:
1175 b_audio_mp3 = true;
1176 break;
1180 if ( !b_audio_mp3
1181 && p_aud->i_channels > 2 && module_exists( "vorbis" ) )
1182 *p_codec_audio = VLC_CODEC_VORBIS;
1183 else
1184 *p_codec_audio = VLC_CODEC_MP3;
1186 msg_Dbg( p_stream, "Converting audio to %.4s", (const char*)p_codec_audio );
1188 ssout << "acodec=";
1189 char fourcc[5];
1190 vlc_fourcc_to_char( *p_codec_audio, fourcc );
1191 fourcc[4] = '\0';
1192 ssout << fourcc << ',';
1194 /* XXX: higher vorbis qualities can cause glitches on some CC
1195 * devices (Chromecast 1 & 2) */
1196 if( *p_codec_audio == VLC_CODEC_VORBIS )
1197 ssout << "aenc=vorbis{quality=4},";
1198 return ssout.str();
1201 bool sout_stream_sys_t::UpdateOutput( sout_stream_t *p_stream )
1203 assert( p_stream->p_sys == this );
1205 if ( !es_changed )
1206 return true;
1208 es_changed = false;
1210 bool canRemux = true;
1211 vlc_fourcc_t i_codec_video = 0, i_codec_audio = 0;
1212 const es_format_t *p_original_audio = NULL;
1213 const es_format_t *p_original_video = NULL;
1214 const es_format_t *p_original_spu = NULL;
1215 bool b_out_streams_changed = false;
1216 std::vector<sout_stream_id_sys_t*> new_streams;
1218 for (std::vector<sout_stream_id_sys_t*>::iterator it = streams.begin(); it != streams.end(); ++it)
1220 const es_format_t *p_es = &(*it)->fmt;
1221 if (p_es->i_cat == AUDIO_ES && p_original_audio == NULL)
1223 if ( !canDecodeAudio( p_stream, p_es->i_codec, &p_es->audio ) )
1225 msg_Dbg( p_stream, "can't remux audio track %d codec %4.4s", p_es->i_id, (const char*)&p_es->i_codec );
1226 canRemux = false;
1228 else if (i_codec_audio == 0)
1229 i_codec_audio = p_es->i_codec;
1230 p_original_audio = p_es;
1231 new_streams.push_back(*it);
1233 else if (b_supports_video)
1235 if (p_es->i_cat == VIDEO_ES && p_original_video == NULL)
1237 if (!canDecodeVideo( p_es->i_codec ))
1239 msg_Dbg( p_stream, "can't remux video track %d codec %4.4s",
1240 p_es->i_id, (const char*)&p_es->i_codec );
1241 canRemux = false;
1243 else if (i_codec_video == 0 && !p_original_spu)
1244 i_codec_video = p_es->i_codec;
1245 p_original_video = p_es;
1246 new_streams.push_back(*it);
1248 #ifdef CC_ENABLE_SPU
1249 else if (p_es->i_cat == SPU_ES && p_original_spu == NULL)
1251 msg_Dbg( p_stream, "forcing video transcode because of subtitle '%4.4s'",
1252 (const char*)&p_es->i_codec );
1253 canRemux = false;
1254 i_codec_video = 0;
1255 p_original_spu = p_es;
1256 new_streams.push_back(*it);
1258 #endif
1259 else
1260 continue;
1262 else
1263 continue;
1265 bool b_found = out_force_reload;
1266 for (std::vector<sout_stream_id_sys_t*>::iterator out_it = out_streams.begin();
1267 out_it != out_streams.end() && !b_found; ++out_it)
1269 if (*out_it == *it)
1270 b_found = true;
1272 if (!b_found)
1273 b_out_streams_changed = true;
1276 if (new_streams.empty())
1278 p_intf->requestPlayerStop();
1279 return true;
1282 /* Don't restart sout and CC session if streams didn't change */
1283 if (!out_force_reload && new_streams.size() == out_streams.size() && !b_out_streams_changed)
1284 return true;
1286 out_force_reload = false;
1288 std::stringstream ssout;
1289 int new_transcoding_state = TRANSCODING_NONE;
1290 if ( !canRemux )
1292 if ( !perf_warning_shown && i_codec_video == 0 && p_original_video
1293 && var_InheritInteger( p_stream, SOUT_CFG_PREFIX "show-perf-warning" ) )
1295 int res = vlc_dialog_wait_question( p_stream,
1296 VLC_DIALOG_QUESTION_WARNING,
1297 _("Cancel"), _("OK"), _("Ok, Don't warn me again"),
1298 _("Performance warning"),
1299 _("Casting this video requires conversion. "
1300 "This conversion can use all the available power and "
1301 "could quickly drain your battery." ) );
1302 if ( res <= 0 )
1303 return false;
1304 perf_warning_shown = true;
1305 if ( res == 2 )
1306 config_PutInt(SOUT_CFG_PREFIX "show-perf-warning", 0 );
1309 const int i_quality = var_InheritInteger( p_stream, SOUT_CFG_PREFIX "conversion-quality" );
1311 /* TODO: provide audio samplerate and channels */
1312 ssout << "transcode{";
1313 if ( i_codec_audio == 0 && p_original_audio )
1315 ssout << GetAcodecOption( p_stream, &i_codec_audio,
1316 &p_original_audio->audio, i_quality );
1317 new_transcoding_state |= TRANSCODING_AUDIO;
1319 if ( i_codec_video == 0 && p_original_video )
1321 ssout << GetVcodecOption( p_stream, &i_codec_video,
1322 &p_original_video->video, i_quality );
1323 new_transcoding_state |= TRANSCODING_VIDEO;
1325 if ( p_original_spu )
1326 ssout << "soverlay,";
1327 ssout << "}:";
1330 const bool is_webm = ( i_codec_audio == 0 || i_codec_audio == VLC_CODEC_VORBIS ||
1331 i_codec_audio == VLC_CODEC_OPUS ) &&
1332 ( i_codec_video == 0 || i_codec_video == VLC_CODEC_VP8 ||
1333 i_codec_video == VLC_CODEC_VP9 );
1335 if ( !p_original_video )
1337 if( is_webm )
1338 mime = "audio/webm";
1339 else
1340 mime = "audio/x-matroska";
1342 else
1344 if ( is_webm )
1345 mime = "video/webm";
1346 else
1347 mime = "video/x-matroska";
1350 ssout << "chromecast-proxy:"
1351 << "std{mux=" << ( is_webm ? DEFAULT_MUXER_WEBM : DEFAULT_MUXER )
1352 << ",access=chromecast-http}";
1354 if ( !startSoutChain( p_stream, new_streams, ssout.str(),
1355 new_transcoding_state ) )
1356 p_intf->requestPlayerStop();
1357 return true;
1360 sout_stream_id_sys_t *sout_stream_sys_t::GetSubId( sout_stream_t *p_stream,
1361 sout_stream_id_sys_t *id,
1362 bool update )
1364 size_t i;
1366 assert( p_stream->p_sys == this );
1368 if ( update && UpdateOutput( p_stream ) == false )
1369 return NULL;
1371 for (i = 0; i < out_streams.size(); ++i)
1373 if ( id == (sout_stream_id_sys_t*) out_streams[i] )
1374 return out_streams[i]->p_sub_id;
1377 return NULL;
1380 bool sout_stream_sys_t::isFlushing( sout_stream_t *p_stream )
1382 (void) p_stream;
1384 /* Make sure that all out_streams are flushed when flushing. This avoids
1385 * too many sout/cc restart when a stream is sending data while one other
1386 * is flushing */
1388 if (!cc_flushing)
1389 return false;
1391 for (size_t i = 0; i < out_streams.size(); ++i)
1393 if ( !out_streams[i]->flushed )
1394 return true;
1397 cc_flushing = false;
1398 for (size_t i = 0; i < out_streams.size(); ++i)
1399 out_streams[i]->flushed = false;
1401 return false;
1404 static int Send(sout_stream_t *p_stream, void *_id, block_t *p_buffer)
1406 sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
1407 sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( _id );
1408 vlc_mutex_locker locker(&p_sys->lock);
1410 if( p_sys->isFlushing( p_stream ) )
1412 block_Release( p_buffer );
1413 return VLC_SUCCESS;
1416 sout_stream_id_sys_t *next_id = p_sys->GetSubId( p_stream, id );
1417 if ( next_id == NULL )
1419 block_Release( p_buffer );
1420 return VLC_EGENERIC;
1423 int ret = sout_StreamIdSend(p_sys->p_out, next_id, p_buffer);
1424 if (ret != VLC_SUCCESS)
1425 DelInternal(p_stream, id, false);
1427 return ret;
1430 static void Flush( sout_stream_t *p_stream, void *_id )
1432 sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
1433 sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( _id );
1434 vlc_mutex_locker locker(&p_sys->lock);
1436 sout_stream_id_sys_t *next_id = p_sys->GetSubId( p_stream, id, false );
1437 if ( next_id == NULL )
1438 return;
1439 next_id->flushed = true;
1441 if( !p_sys->cc_flushing )
1443 p_sys->cc_flushing = true;
1445 p_sys->stopSoutChain( p_stream );
1447 p_sys->access_out_live.stop();
1449 if (p_sys->cc_has_input)
1451 p_sys->p_intf->requestPlayerStop();
1452 p_sys->cc_has_input = false;
1454 p_sys->out_force_reload = p_sys->es_changed = true;
1458 static void on_input_event_cb(void *data, enum cc_input_event event, union cc_input_arg arg )
1460 sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(data);
1461 sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
1463 vlc_mutex_locker locker(&p_sys->lock);
1464 switch (event)
1466 case CC_INPUT_EVENT_EOF:
1467 /* In case of EOF: stop the sout chain in order to drain all
1468 * sout/demuxers/access. If EOF changes to false, reset es_changed
1469 * in order to reload the sout from next Send calls. */
1470 if( arg.eof )
1471 p_sys->stopSoutChain( p_stream );
1472 else
1473 p_sys->out_force_reload = p_sys->es_changed = true;
1474 break;
1475 case CC_INPUT_EVENT_RETRY:
1476 p_sys->stopSoutChain( p_stream );
1477 if( p_sys->transcodingCanFallback() )
1479 p_sys->setNextTranscodingState();
1480 msg_Warn(p_stream, "Load failed detected. Switching to next "
1481 "configuration. Transcoding video%s",
1482 p_sys->transcoding_state & TRANSCODING_AUDIO ? "/audio" : "");
1483 p_sys->out_force_reload = p_sys->es_changed = true;
1485 break;
1489 /*****************************************************************************
1490 * Open: connect to the Chromecast and initialize the sout
1491 *****************************************************************************/
1492 static int Open(vlc_object_t *p_this)
1494 sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(p_this);
1495 sout_stream_sys_t *p_sys = NULL;
1496 intf_sys_t *p_intf = NULL;
1497 char *psz_ip = NULL;
1498 sout_stream_t *p_sout = NULL;
1499 httpd_host_t *httpd_host = NULL;
1500 bool b_supports_video = true;
1501 int i_local_server_port;
1502 int i_device_port;
1503 std::stringstream ss;
1505 config_ChainParse(p_stream, SOUT_CFG_PREFIX, ppsz_sout_options, p_stream->p_cfg);
1507 psz_ip = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "ip");
1508 if ( psz_ip == NULL )
1510 msg_Err( p_this, "missing Chromecast IP address" );
1511 goto error;
1514 i_device_port = var_InheritInteger(p_stream, SOUT_CFG_PREFIX "port");
1515 i_local_server_port = var_InheritInteger(p_stream, SOUT_CFG_PREFIX "http-port");
1517 var_Create(p_stream, "http-port", VLC_VAR_INTEGER);
1518 var_SetInteger(p_stream, "http-port", i_local_server_port);
1519 var_Create(p_stream, "http-host", VLC_VAR_STRING);
1520 var_SetString(p_stream, "http-host", "");
1521 httpd_host = vlc_http_HostNew(VLC_OBJECT(p_stream));
1522 if (httpd_host == NULL)
1523 goto error;
1527 p_intf = new intf_sys_t( p_this, i_local_server_port, psz_ip, i_device_port,
1528 httpd_host );
1530 catch (const std::runtime_error& err )
1532 msg_Err( p_this, "cannot load the Chromecast controller (%s)", err.what() );
1533 goto error;
1535 catch (const std::bad_alloc& )
1537 p_intf = NULL;
1538 goto error;
1541 /* check if we can open the proper sout */
1542 ss << "http{mux=" << DEFAULT_MUXER << "}";
1543 p_sout = sout_StreamChainNew( p_stream->p_sout, ss.str().c_str(), NULL, NULL);
1544 if (p_sout == NULL) {
1545 msg_Dbg(p_stream, "could not create sout chain:%s", ss.str().c_str());
1546 goto error;
1548 sout_StreamChainDelete( p_sout, NULL );
1550 b_supports_video = var_GetBool(p_stream, SOUT_CFG_PREFIX "video");
1554 p_sys = new sout_stream_sys_t( httpd_host, p_intf, b_supports_video,
1555 i_local_server_port );
1557 catch ( std::exception& ex )
1559 msg_Err( p_stream, "Failed to instantiate sout_stream_sys_t: %s", ex.what() );
1560 p_sys = NULL;
1561 goto error;
1564 p_intf->setOnInputEventCb(on_input_event_cb, p_stream);
1566 /* prevent sout-mux-caching since chromecast-proxy is already doing it */
1567 var_Create( p_stream->p_sout, "sout-mux-caching", VLC_VAR_INTEGER );
1568 var_SetInteger( p_stream->p_sout, "sout-mux-caching", 0 );
1570 var_Create( p_stream->p_sout, SOUT_CFG_PREFIX "sys", VLC_VAR_ADDRESS );
1571 var_SetAddress( p_stream->p_sout, SOUT_CFG_PREFIX "sys", p_sys );
1573 var_Create( p_stream->p_sout, SOUT_CFG_PREFIX "access-out-sys", VLC_VAR_ADDRESS );
1575 // Set the sout callbacks.
1576 p_stream->pf_add = Add;
1577 p_stream->pf_del = Del;
1578 p_stream->pf_send = Send;
1579 p_stream->pf_flush = Flush;
1581 p_stream->p_sys = p_sys;
1583 free(psz_ip);
1585 return VLC_SUCCESS;
1587 error:
1588 delete p_intf;
1589 if (httpd_host)
1590 httpd_HostDelete(httpd_host);
1591 free(psz_ip);
1592 delete p_sys;
1593 return VLC_EGENERIC;
1596 /*****************************************************************************
1597 * Close: destroy interface
1598 *****************************************************************************/
1599 static void Close(vlc_object_t *p_this)
1601 sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(p_this);
1602 sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
1604 assert(p_sys->out_streams.empty() && p_sys->streams.empty());
1605 var_Destroy( p_stream->p_sout, SOUT_CFG_PREFIX "sys" );
1606 var_Destroy( p_stream->p_sout, SOUT_CFG_PREFIX "sout-mux-caching" );
1608 assert(p_sys->streams.empty() && p_sys->out_streams.empty());
1610 httpd_host_t *httpd_host = p_sys->httpd_host;
1611 delete p_sys->p_intf;
1612 delete p_sys;
1613 /* Delete last since p_intf and p_sys depends on httpd_host */
1614 httpd_HostDelete(httpd_host);