chromecast: refactor out encoder option functions
[vlc.git] / modules / stream_out / chromecast / cast.cpp
1 /*****************************************************************************
2  * cast.cpp: Chromecast sout module for vlc
3  *****************************************************************************
4  * Copyright © 2014-2015 VideoLAN
5  *
6  * Authors: Adrien Maglo <magsoft@videolan.org>
7  *          Jean-Baptiste Kempf <jb@videolan.org>
8  *          Steve Lhomme <robux4@videolabs.io>
9  *
10  * This program is free software; you can redistribute it and/or modify it
11  * under the terms of the GNU Lesser General Public License as published by
12  * the Free Software Foundation; either version 2.1 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #include "../renderer_common.hpp"
34 #include "chromecast.h"
35 #include <vlc_dialog.h>
36
37 #include <vlc_sout.h>
38 #include <vlc_block.h>
39 #include <vlc_modules.h>
40 #include <vlc_httpd.h>
41
42 #include <cassert>
43
44 #define TRANSCODING_NONE 0x0
45 #define TRANSCODING_VIDEO 0x1
46 #define TRANSCODING_AUDIO 0x2
47
48 #if 0
49 /* TODO: works only with internal spu and transcoding/blending for now */
50 #define CC_ENABLE_SPU
51 #endif
52
53 namespace {
54
55 struct sout_access_out_sys_t
56 {
57     sout_access_out_sys_t(httpd_host_t *httpd_host, intf_sys_t * const intf);
58     ~sout_access_out_sys_t();
59
60     void clear();
61     void stop();
62     void prepare(sout_stream_t *p_stream, const std::string &mime);
63     int url_cb(httpd_client_t *cl, httpd_message_t *answer, const httpd_message_t *query);
64     void fifo_put_back(block_t *);
65     ssize_t write(sout_access_out_t *p_access, block_t *p_block);
66     void close();
67
68
69 private:
70     void clearUnlocked();
71     void initCopy();
72     void putCopy(block_t *p_block);
73     void restoreCopy();
74
75     intf_sys_t * const m_intf;
76     httpd_url_t       *m_url;
77     httpd_client_t    *m_client;
78     vlc_fifo_t        *m_fifo;
79     block_t           *m_header;
80     block_t           *m_copy_chain;
81     block_t           **m_copy_last;
82     size_t             m_copy_size;
83     bool               m_eof;
84     std::string        m_mime;
85 };
86
87 typedef struct sout_stream_id_sys_t sout_stream_id_sys_t;
88
89 struct sout_stream_sys_t
90 {
91     sout_stream_sys_t(httpd_host_t *httpd_host, intf_sys_t * const intf, bool has_video, int port)
92         : httpd_host(httpd_host)
93         , access_out_live(httpd_host, intf)
94         , p_out(NULL)
95         , p_intf(intf)
96         , b_supports_video(has_video)
97         , i_port(port)
98         , first_video_keyframe_pts( -1 )
99         , es_changed( true )
100         , cc_has_input( false )
101         , cc_reload( false )
102         , cc_flushing( false )
103         , has_video( false )
104         , out_force_reload( false )
105         , perf_warning_shown( false )
106         , transcoding_state( TRANSCODING_NONE )
107         , venc_opt_idx ( -1 )
108         , out_streams_added( 0 )
109     {
110         assert(p_intf != NULL);
111         vlc_mutex_init(&lock);
112     }
113
114     ~sout_stream_sys_t()
115     {
116         vlc_mutex_destroy(&lock);
117     }
118
119     bool canDecodeVideo( vlc_fourcc_t i_codec ) const;
120     bool canDecodeAudio( sout_stream_t* p_stream, vlc_fourcc_t i_codec,
121                          const audio_format_t* p_fmt ) const;
122     bool startSoutChain(sout_stream_t* p_stream,
123                         const std::vector<sout_stream_id_sys_t*> &new_streams,
124                         const std::string &sout, int new_transcoding_state);
125     void stopSoutChain(sout_stream_t* p_stream);
126     sout_stream_id_sys_t *GetSubId( sout_stream_t*, sout_stream_id_sys_t*, bool update = true );
127     bool isFlushing( sout_stream_t* );
128     void setNextTranscodingState();
129     bool transcodingCanFallback() const;
130
131     httpd_host_t      *httpd_host;
132     sout_access_out_sys_t access_out_live;
133
134     sout_stream_t     *p_out;
135     std::string        mime;
136
137     vlc_mutex_t        lock; /* for input events cb */
138
139     intf_sys_t * const p_intf;
140     const bool b_supports_video;
141     const int i_port;
142
143     sout_stream_id_sys_t *             video_proxy_id;
144     vlc_tick_t                         first_video_keyframe_pts;
145
146     bool                               es_changed;
147     bool                               cc_has_input;
148     bool                               cc_reload;
149     bool                               cc_flushing;
150     bool                               has_video;
151     bool                               out_force_reload;
152     bool                               perf_warning_shown;
153     int                                transcoding_state;
154     int                                venc_opt_idx;
155     std::vector<sout_stream_id_sys_t*> streams;
156     std::vector<sout_stream_id_sys_t*> out_streams;
157     unsigned int                       out_streams_added;
158     unsigned int                       spu_streams_count;
159
160 private:
161     std::string GetAcodecOption( sout_stream_t *, vlc_fourcc_t *, const audio_format_t *, int );
162     bool UpdateOutput( sout_stream_t * );
163 };
164
165 struct sout_stream_id_sys_t
166 {
167     es_format_t           fmt;
168     sout_stream_id_sys_t  *p_sub_id;
169     bool                  flushed;
170 };
171
172 } // namespace
173
174 #define SOUT_CFG_PREFIX "sout-chromecast-"
175
176 static const char DEFAULT_MUXER[] = "avformat{mux=matroska,options={live=1}}";
177 static const char DEFAULT_MUXER_WEBM[] = "avformat{mux=webm,options={live=1}}";
178
179
180 /*****************************************************************************
181  * Local prototypes
182  *****************************************************************************/
183 static int Open(vlc_object_t *);
184 static void Close(vlc_object_t *);
185 static int ProxyOpen(vlc_object_t *);
186 static int AccessOpen(vlc_object_t *);
187 static void AccessClose(vlc_object_t *);
188
189 static const char *const ppsz_sout_options[] = {
190     "ip", "port",  "http-port", "video", NULL
191 };
192
193 /*****************************************************************************
194  * Module descriptor
195  *****************************************************************************/
196
197 #define HTTP_PORT_TEXT N_("HTTP port")
198 #define HTTP_PORT_LONGTEXT N_("This sets the HTTP port of the local server " \
199                               "used to stream the media to the Chromecast.")
200
201 #define IP_ADDR_TEXT N_("IP Address")
202 #define IP_ADDR_LONGTEXT N_("IP Address of the Chromecast.")
203 #define PORT_TEXT N_("Chromecast port")
204 #define PORT_LONGTEXT N_("The port used to talk to the Chromecast.")
205
206 /* Fifo size after we tell the demux to pace */
207 #define HTTPD_BUFFER_PACE INT64_C(2 * 1024 * 1024) /* 2 MB */
208 /* Fifo size after we drop packets (should not happen) */
209 #define HTTPD_BUFFER_MAX INT64_C(32 * 1024 * 1024) /* 32 MB */
210 #define HTTPD_BUFFER_COPY_MAX INT64_C(10 * 1024 * 1024) /* 10 MB */
211
212 vlc_module_begin ()
213
214     set_shortname(N_("Chromecast"))
215     set_description(N_("Chromecast stream output"))
216     set_capability("sout stream", 0)
217     add_shortcut("chromecast")
218     set_category(CAT_SOUT)
219     set_subcategory(SUBCAT_SOUT_STREAM)
220     set_callbacks(Open, Close)
221
222     add_string(SOUT_CFG_PREFIX "ip", NULL, NULL, NULL, false)
223         change_private()
224     add_integer(SOUT_CFG_PREFIX "port", CHROMECAST_CONTROL_PORT, NULL, NULL, false)
225         change_private()
226     add_bool(SOUT_CFG_PREFIX "video", true, NULL, NULL, false)
227         change_private()
228     add_integer(SOUT_CFG_PREFIX "http-port", HTTP_PORT, HTTP_PORT_TEXT, HTTP_PORT_LONGTEXT, false)
229     add_obsolete_string(SOUT_CFG_PREFIX "mux")
230     add_obsolete_string(SOUT_CFG_PREFIX "mime")
231     add_renderer_opts(SOUT_CFG_PREFIX)
232
233     add_submodule()
234         /* sout proxy that start the cc input when all streams are loaded */
235         add_shortcut("chromecast-proxy")
236         set_capability("sout stream", 0)
237         set_callbacks(ProxyOpen, NULL)
238     add_submodule()
239         set_subcategory(SUBCAT_SOUT_ACO)
240         add_shortcut("chromecast-http")
241         set_capability("sout access", 0)
242         set_callbacks(AccessOpen, AccessClose)
243 vlc_module_end ()
244
245 static void *ProxyAdd(sout_stream_t *p_stream, const es_format_t *p_fmt)
246 {
247     sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
248     sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( sout_StreamIdAdd(p_stream->p_next, p_fmt) );
249     if (id)
250     {
251         if (p_fmt->i_cat == VIDEO_ES)
252             p_sys->video_proxy_id = id;
253         p_sys->out_streams_added++;
254     }
255     return id;
256 }
257
258 static void ProxyDel(sout_stream_t *p_stream, void *_id)
259 {
260     sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
261     sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( _id );
262     p_sys->out_streams_added--;
263     if (id == p_sys->video_proxy_id)
264         p_sys->video_proxy_id = NULL;
265     return sout_StreamIdDel(p_stream->p_next, id);
266 }
267
268 static int ProxySend(sout_stream_t *p_stream, void *_id, block_t *p_buffer)
269 {
270     sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
271     sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( _id );
272     if (p_sys->cc_has_input
273      || p_sys->out_streams_added >= p_sys->out_streams.size() - p_sys->spu_streams_count)
274     {
275         if (p_sys->has_video)
276         {
277             // In case of video, the first block must be a keyframe
278             if (id == p_sys->video_proxy_id)
279             {
280                 if (p_sys->first_video_keyframe_pts == -1
281                  && p_buffer->i_flags & BLOCK_FLAG_TYPE_I)
282                     p_sys->first_video_keyframe_pts = p_buffer->i_pts;
283             }
284             else // no keyframe for audio
285                 p_buffer->i_flags &= ~BLOCK_FLAG_TYPE_I;
286
287             if (p_buffer->i_pts < p_sys->first_video_keyframe_pts
288              || p_sys->first_video_keyframe_pts == -1)
289             {
290                 block_ChainRelease(p_buffer);
291                 return VLC_SUCCESS;
292             }
293         }
294
295         vlc_tick_t pause_delay = p_sys->p_intf->getPauseDelay();
296         if( p_buffer->i_pts != VLC_TICK_INVALID )
297             p_buffer->i_pts -= pause_delay;
298         if( p_buffer->i_dts != VLC_TICK_INVALID )
299             p_buffer->i_dts -= pause_delay;
300
301         int ret = sout_StreamIdSend(p_stream->p_next, id, p_buffer);
302         if (ret == VLC_SUCCESS && !p_sys->cc_has_input)
303         {
304             /* Start the chromecast only when all streams are added into the
305              * last sout (the http one) */
306             p_sys->p_intf->setHasInput(p_sys->mime);
307             p_sys->cc_has_input = true;
308         }
309         return ret;
310     }
311     else
312     {
313         block_ChainRelease(p_buffer);
314         return VLC_SUCCESS;
315     }
316 }
317
318 static void ProxyFlush(sout_stream_t *p_stream, void *id)
319 {
320     sout_StreamFlush(p_stream->p_next, id);
321 }
322
323 static int ProxyOpen(vlc_object_t *p_this)
324 {
325     sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(p_this);
326     sout_stream_sys_t *p_sys = (sout_stream_sys_t *) var_InheritAddress(p_this, SOUT_CFG_PREFIX "sys");
327     if (p_sys == NULL || p_stream->p_next == NULL)
328         return VLC_EGENERIC;
329
330     p_stream->p_sys = (sout_stream_sys_t *) p_sys;
331     p_sys->out_streams_added = 0;
332
333     p_stream->pf_add     = ProxyAdd;
334     p_stream->pf_del     = ProxyDel;
335     p_stream->pf_send    = ProxySend;
336     p_stream->pf_flush   = ProxyFlush;
337     return VLC_SUCCESS;
338 }
339
340 static int httpd_url_cb(httpd_callback_sys_t *data, httpd_client_t *cl,
341                         httpd_message_t *answer, const httpd_message_t *query)
342 {
343     sout_access_out_sys_t *p_sys = reinterpret_cast<sout_access_out_sys_t *>(data);
344     return p_sys->url_cb(cl, answer, query);
345 }
346
347 sout_access_out_sys_t::sout_access_out_sys_t(httpd_host_t *httpd_host,
348                                              intf_sys_t * const intf)
349     : m_intf(intf)
350     , m_client(NULL)
351     , m_header(NULL)
352     , m_copy_chain(NULL)
353     , m_eof(true)
354 {
355     m_fifo = block_FifoNew();
356     if (!m_fifo)
357         throw std::runtime_error( "block_FifoNew failed" );
358     m_url = httpd_UrlNew(httpd_host, intf->getHttpStreamPath().c_str(), NULL, NULL);
359     if (m_url == NULL)
360     {
361         block_FifoRelease(m_fifo);
362         throw std::runtime_error( "httpd_UrlNew failed" );
363     }
364     httpd_UrlCatch(m_url, HTTPD_MSG_GET, httpd_url_cb,
365                    (httpd_callback_sys_t*)this);
366     initCopy();
367 }
368
369 sout_access_out_sys_t::~sout_access_out_sys_t()
370 {
371     httpd_UrlDelete(m_url);
372     block_FifoRelease(m_fifo);
373 }
374
375 void sout_access_out_sys_t::clearUnlocked()
376 {
377     block_ChainRelease(vlc_fifo_DequeueAllUnlocked(m_fifo));
378     if (m_header)
379     {
380         block_Release(m_header);
381         m_header = NULL;
382     }
383     m_eof = true;
384     initCopy();
385 }
386
387 void sout_access_out_sys_t::initCopy()
388 {
389     block_ChainRelease(m_copy_chain);
390     m_copy_chain = NULL;
391     m_copy_last = &m_copy_chain;
392     m_copy_size = 0;
393 }
394
395 void sout_access_out_sys_t::putCopy(block_t *p_block)
396 {
397     while (m_copy_size >= HTTPD_BUFFER_COPY_MAX)
398     {
399         assert(m_copy_chain);
400         block_t *copy = m_copy_chain;
401         m_copy_chain = copy->p_next;
402         m_copy_size -= copy->i_buffer;
403         block_Release(copy);
404     }
405     if (!m_copy_chain)
406     {
407         assert(m_copy_size == 0);
408         m_copy_last = &m_copy_chain;
409     }
410     block_ChainLastAppend(&m_copy_last, p_block);
411     m_copy_size += p_block->i_buffer;
412 }
413
414 void sout_access_out_sys_t::restoreCopy()
415 {
416     if (m_copy_chain)
417     {
418         fifo_put_back(m_copy_chain);
419         m_copy_chain = NULL;
420         initCopy();
421     }
422 }
423
424 void sout_access_out_sys_t::clear()
425 {
426     vlc_fifo_Lock(m_fifo);
427     clearUnlocked();
428     vlc_fifo_Unlock(m_fifo);
429     vlc_fifo_Signal(m_fifo);
430 }
431
432 void sout_access_out_sys_t::stop()
433 {
434     vlc_fifo_Lock(m_fifo);
435     clearUnlocked();
436     m_intf->setPacing(false);
437     m_client = NULL;
438     vlc_fifo_Unlock(m_fifo);
439     vlc_fifo_Signal(m_fifo);
440 }
441
442 void sout_access_out_sys_t::prepare(sout_stream_t *p_stream, const std::string &mime)
443 {
444     var_SetAddress(p_stream->p_sout, SOUT_CFG_PREFIX "access-out-sys", this);
445
446     vlc_fifo_Lock(m_fifo);
447     clearUnlocked();
448     m_intf->setPacing(false);
449     m_mime = mime;
450     m_eof = false;
451     vlc_fifo_Unlock(m_fifo);
452 }
453
454 void sout_access_out_sys_t::fifo_put_back(block_t *p_block)
455 {
456     block_t *p_fifo = vlc_fifo_DequeueAllUnlocked(m_fifo);
457     vlc_fifo_QueueUnlocked(m_fifo, p_block);
458     vlc_fifo_QueueUnlocked(m_fifo, p_fifo);
459 }
460
461 int sout_access_out_sys_t::url_cb(httpd_client_t *cl, httpd_message_t *answer,
462                                   const httpd_message_t *query)
463 {
464     if (!answer || !query || !cl)
465         return VLC_SUCCESS;
466
467     vlc_fifo_Lock(m_fifo);
468
469     if (!answer->i_body_offset)
470     {
471         /* When doing a lot a load requests, we can serve data to a client that
472          * will be closed (the close request is already sent). In that case, we
473          * should also serve data used by the old client to the new one. */
474         restoreCopy();
475         m_client = cl;
476     }
477
478     /* Send data per 512kB minimum */
479     size_t i_min_buffer = 524288;
480     while (m_client && vlc_fifo_GetBytes(m_fifo) < i_min_buffer && !m_eof)
481         vlc_fifo_Wait(m_fifo);
482
483     block_t *p_block = NULL;
484     if (m_client && vlc_fifo_GetBytes(m_fifo) > 0)
485     {
486         /* if less data is available, then we must be EOF */
487         if (vlc_fifo_GetBytes(m_fifo) < i_min_buffer)
488         {
489             assert(m_eof);
490             i_min_buffer = vlc_fifo_GetBytes(m_fifo);
491         }
492         block_t *p_first = vlc_fifo_DequeueUnlocked(m_fifo);
493
494         assert(p_first);
495         size_t i_total_size = p_first->i_buffer;
496         block_t *p_next = NULL, *p_cur = p_first;
497
498         while (i_total_size < i_min_buffer)
499         {
500             p_next = vlc_fifo_DequeueUnlocked(m_fifo);
501             assert(p_next);
502             i_total_size += p_next->i_buffer;
503             p_cur->p_next = p_next;
504             p_cur = p_cur->p_next;
505         }
506         assert(i_total_size >= i_min_buffer);
507
508         if (p_next != NULL)
509         {
510             p_block = block_Alloc(i_total_size);
511             if (p_block)
512                 block_ChainExtract(p_first, p_block->p_buffer, p_block->i_buffer);
513             block_ChainRelease(p_first);
514         }
515         else
516             p_block = p_first;
517
518         if (vlc_fifo_GetBytes(m_fifo) < HTTPD_BUFFER_PACE)
519             m_intf->setPacing(false);
520     }
521
522     answer->i_proto  = HTTPD_PROTO_HTTP;
523     answer->i_version= 0;
524     answer->i_type   = HTTPD_MSG_ANSWER;
525     answer->i_status = 200;
526
527     if (p_block)
528     {
529         if (answer->i_body_offset == 0)
530         {
531             httpd_MsgAdd(answer, "Content-type", "%s", m_mime.c_str());
532             httpd_MsgAdd(answer, "Cache-Control", "no-cache");
533             httpd_MsgAdd(answer, "Connection", "close");
534         }
535
536         const bool send_header = answer->i_body_offset == 0 && m_header != NULL;
537         size_t i_answer_size = p_block->i_buffer;
538         if (send_header)
539             i_answer_size += m_header->i_buffer;
540
541         answer->p_body = (uint8_t *) malloc(i_answer_size);
542         if (answer->p_body)
543         {
544             answer->i_body = i_answer_size;
545             answer->i_body_offset += answer->i_body;
546             size_t i_block_offset = 0;
547             if (send_header)
548             {
549                 memcpy(answer->p_body, m_header->p_buffer, m_header->i_buffer);
550                 i_block_offset = m_header->i_buffer;
551             }
552             memcpy(&answer->p_body[i_block_offset], p_block->p_buffer, p_block->i_buffer);
553         }
554
555         putCopy(p_block);
556     }
557     if (!answer->i_body)
558         httpd_MsgAdd(answer, "Connection", "close");
559
560     vlc_fifo_Unlock(m_fifo);
561     return VLC_SUCCESS;
562 }
563
564 ssize_t sout_access_out_sys_t::write(sout_access_out_t *p_access, block_t *p_block)
565 {
566     size_t i_len = p_block->i_buffer;
567
568     vlc_fifo_Lock(m_fifo);
569
570     if (p_block->i_flags & BLOCK_FLAG_HEADER)
571     {
572         if (m_header)
573             block_Release(m_header);
574         m_header = p_block;
575     }
576     else
577     {
578         /* Drop buffer is the fifo is really full */
579         if (vlc_fifo_GetBytes(m_fifo) >= HTTPD_BUFFER_PACE)
580         {
581             /* XXX: Hackisk way to pace between the sout (controlled by the
582              * decoder thread) and the demux filter (controlled by the input
583              * thread). When the httpd FIFO reaches a specific size, we tell
584              * the demux filter to pace and wait a little before queing this
585              * block, but not too long since we don't want to block decoder
586              * thread controls. If the pacing fails (should not happen), we
587              * drop the first block in order to make room. The demux filter
588              * will be unpaced when the data is read from the httpd thread. */
589
590             m_intf->setPacing(true);
591
592             while (vlc_fifo_GetBytes(m_fifo) >= HTTPD_BUFFER_MAX)
593             {
594                 block_t *p_drop = vlc_fifo_DequeueUnlocked(m_fifo);
595                 msg_Warn(p_access, "httpd buffer full: dropping %zuB", p_drop->i_buffer);
596                 block_Release(p_drop);
597             }
598         }
599         vlc_fifo_QueueUnlocked(m_fifo, p_block);
600     }
601
602     m_eof = false;
603
604     vlc_fifo_Unlock(m_fifo);
605     vlc_fifo_Signal(m_fifo);
606
607     return i_len;
608 }
609
610 void sout_access_out_sys_t::close()
611 {
612     vlc_fifo_Lock(m_fifo);
613     m_eof = true;
614     m_intf->setPacing(false);
615     vlc_fifo_Unlock(m_fifo);
616     vlc_fifo_Signal(m_fifo);
617 }
618
619 ssize_t AccessWrite(sout_access_out_t *p_access, block_t *p_block)
620 {
621     sout_access_out_sys_t *p_sys = reinterpret_cast<sout_access_out_sys_t *>( p_access->p_sys );
622     return p_sys->write(p_access, p_block);
623 }
624
625 static int AccessControl(sout_access_out_t *p_access, int i_query, va_list args)
626 {
627     (void) p_access;
628
629     switch (i_query)
630     {
631         case ACCESS_OUT_CONTROLS_PACE:
632             *va_arg(args, bool *) = true;
633             break;
634         default:
635             return VLC_EGENERIC;
636     }
637     return VLC_SUCCESS;
638 }
639
640 static int AccessOpen(vlc_object_t *p_this)
641 {
642     sout_access_out_t *p_access = (sout_access_out_t*)p_this;
643
644     sout_access_out_sys_t *p_sys = (sout_access_out_sys_t *)
645         var_InheritAddress(p_access, SOUT_CFG_PREFIX "access-out-sys");
646     if (p_sys == NULL)
647         return VLC_EGENERIC;
648
649     p_access->pf_write       = AccessWrite;
650     p_access->pf_control     = AccessControl;
651     p_access->p_sys          = p_sys;
652
653     return VLC_SUCCESS;
654 }
655
656 static void AccessClose(vlc_object_t *p_this)
657 {
658     sout_access_out_t *p_access = (sout_access_out_t*)p_this;
659     sout_access_out_sys_t *p_sys = reinterpret_cast<sout_access_out_sys_t *>( p_access->p_sys );
660
661     p_sys->close();
662 }
663
664 /*****************************************************************************
665  * Sout callbacks
666  *****************************************************************************/
667 static void *Add(sout_stream_t *p_stream, const es_format_t *p_fmt)
668 {
669     sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
670     vlc_mutex_locker locker(&p_sys->lock);
671
672     if (!p_sys->b_supports_video)
673     {
674         if (p_fmt->i_cat != AUDIO_ES)
675             return NULL;
676     }
677
678     sout_stream_id_sys_t *p_sys_id = (sout_stream_id_sys_t *)malloc( sizeof(sout_stream_id_sys_t) );
679     if (p_sys_id != NULL)
680     {
681         es_format_Copy( &p_sys_id->fmt, p_fmt );
682         p_sys_id->p_sub_id = NULL;
683         p_sys_id->flushed = false;
684
685         p_sys->streams.push_back( p_sys_id );
686         p_sys->es_changed = true;
687     }
688     return p_sys_id;
689 }
690
691
692 static void DelInternal(sout_stream_t *p_stream, void *_id, bool reset_config)
693 {
694     sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
695     sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( _id );
696
697     for (std::vector<sout_stream_id_sys_t*>::iterator it = p_sys->streams.begin();
698          it != p_sys->streams.end(); )
699     {
700         sout_stream_id_sys_t *p_sys_id = *it;
701         if ( p_sys_id == id )
702         {
703             if ( p_sys_id->p_sub_id != NULL )
704             {
705                 sout_StreamIdDel( p_sys->p_out, p_sys_id->p_sub_id );
706                 for (std::vector<sout_stream_id_sys_t*>::iterator out_it = p_sys->out_streams.begin();
707                      out_it != p_sys->out_streams.end(); )
708                 {
709                     if (*out_it == id)
710                     {
711                         p_sys->out_streams.erase(out_it);
712                         p_sys->es_changed = reset_config;
713                         p_sys->out_force_reload = reset_config;
714                         if( p_sys_id->fmt.i_cat == VIDEO_ES )
715                             p_sys->has_video = false;
716                         else if( p_sys_id->fmt.i_cat == SPU_ES )
717                             p_sys->spu_streams_count--;
718                         break;
719                     }
720                     out_it++;
721                 }
722             }
723
724             es_format_Clean( &p_sys_id->fmt );
725             free( p_sys_id );
726             p_sys->streams.erase( it );
727             break;
728         }
729         it++;
730     }
731
732     if ( p_sys->out_streams.empty() )
733     {
734         p_sys->stopSoutChain(p_stream);
735         p_sys->p_intf->requestPlayerStop();
736         p_sys->access_out_live.clear();
737         p_sys->transcoding_state = TRANSCODING_NONE;
738     }
739 }
740
741 static void Del(sout_stream_t *p_stream, void *_id)
742 {
743     sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
744     sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( _id );
745
746     vlc_mutex_locker locker(&p_sys->lock);
747     DelInternal(p_stream, id, true);
748 }
749
750 /**
751  * Transcode steps:
752  * 0: Accept HEVC/VP9 & all supported audio formats
753  * 1: Transcode to h264 & accept all supported audio formats if the video codec
754  *    was HEVC/VP9
755  * 2: Transcode to H264 & MP3
756  *
757  * Additionally:
758  * - Allow (E)AC3 passthrough depending on the audio-passthrough
759  *   config value, except for the final step, where we just give up and transcode
760  *   everything.
761  * - Disallow multichannel AAC
762  *
763  * Supported formats: https://developers.google.com/cast/docs/media
764  */
765
766 bool sout_stream_sys_t::canDecodeVideo( vlc_fourcc_t i_codec ) const
767 {
768     if( transcoding_state & TRANSCODING_VIDEO )
769         return false;
770     return i_codec == VLC_CODEC_H264 || i_codec == VLC_CODEC_HEVC
771         || i_codec == VLC_CODEC_VP8 || i_codec == VLC_CODEC_VP9;
772 }
773
774 bool sout_stream_sys_t::canDecodeAudio( sout_stream_t *p_stream,
775                                         vlc_fourcc_t i_codec,
776                                         const audio_format_t* p_fmt ) const
777 {
778     if( transcoding_state & TRANSCODING_AUDIO )
779         return false;
780     if ( i_codec == VLC_CODEC_A52 || i_codec == VLC_CODEC_EAC3 )
781     {
782         return var_InheritBool( p_stream, SOUT_CFG_PREFIX "audio-passthrough" );
783     }
784     if ( i_codec == VLC_FOURCC('h', 'a', 'a', 'c') ||
785             i_codec == VLC_FOURCC('l', 'a', 'a', 'c') ||
786             i_codec == VLC_FOURCC('s', 'a', 'a', 'c') ||
787             i_codec == VLC_CODEC_MP4A )
788     {
789         return p_fmt->i_channels <= 2;
790     }
791     return i_codec == VLC_CODEC_VORBIS || i_codec == VLC_CODEC_OPUS ||
792            i_codec == VLC_CODEC_MP3;
793 }
794
795 void sout_stream_sys_t::stopSoutChain(sout_stream_t *p_stream)
796 {
797     (void) p_stream;
798
799     if ( unlikely( p_out != NULL ) )
800     {
801         for ( size_t i = 0; i < out_streams.size(); i++ )
802         {
803             if ( out_streams[i]->p_sub_id != NULL )
804             {
805                 sout_StreamIdDel( p_out, out_streams[i]->p_sub_id );
806                 out_streams[i]->p_sub_id = NULL;
807             }
808         }
809         out_streams.clear();
810         sout_StreamChainDelete( p_out, NULL );
811         p_out = NULL;
812     }
813 }
814
815 bool sout_stream_sys_t::startSoutChain(sout_stream_t *p_stream,
816                                        const std::vector<sout_stream_id_sys_t*> &new_streams,
817                                        const std::string &sout, int new_transcoding_state)
818 {
819     stopSoutChain( p_stream );
820
821     msg_Dbg( p_stream, "Creating chain %s", sout.c_str() );
822     cc_has_input = false;
823     cc_reload = false;
824     first_video_keyframe_pts = -1;
825     video_proxy_id = NULL;
826     has_video = false;
827     out_streams = new_streams;
828     spu_streams_count = 0;
829     transcoding_state = new_transcoding_state;
830
831     access_out_live.prepare( p_stream, mime );
832
833     p_out = sout_StreamChainNew( p_stream->p_sout, sout.c_str(), NULL, NULL);
834     if (p_out == NULL) {
835         msg_Dbg(p_stream, "could not create sout chain:%s", sout.c_str());
836         out_streams.clear();
837         access_out_live.clear();
838         return false;
839     }
840
841     /* check the streams we can actually add */
842     for (std::vector<sout_stream_id_sys_t*>::iterator it = out_streams.begin();
843          it != out_streams.end(); )
844     {
845         sout_stream_id_sys_t *p_sys_id = *it;
846         p_sys_id->p_sub_id = reinterpret_cast<sout_stream_id_sys_t *>( sout_StreamIdAdd( p_out, &p_sys_id->fmt ) );
847         if ( p_sys_id->p_sub_id == NULL )
848         {
849             msg_Err( p_stream, "can't handle %4.4s stream", (char *)&p_sys_id->fmt.i_codec );
850             es_format_Clean( &p_sys_id->fmt );
851             it = out_streams.erase( it );
852         }
853         else
854         {
855             if( p_sys_id->fmt.i_cat == VIDEO_ES )
856                 has_video = true;
857             else if( p_sys_id->fmt.i_cat == SPU_ES )
858                 spu_streams_count++;
859             ++it;
860         }
861     }
862
863     if (out_streams.empty())
864     {
865         stopSoutChain( p_stream );
866         access_out_live.clear();
867         return false;
868     }
869
870     /* Ask to retry if we are not transcoding everything (because we can trust
871      * what we encode) */
872     p_intf->setRetryOnFail(transcodingCanFallback());
873
874     return true;
875 }
876
877 void sout_stream_sys_t::setNextTranscodingState()
878 {
879     if (!(transcoding_state & TRANSCODING_VIDEO))
880         transcoding_state |= TRANSCODING_VIDEO;
881     else if (!(transcoding_state & TRANSCODING_AUDIO))
882         transcoding_state = TRANSCODING_AUDIO;
883 }
884
885 bool sout_stream_sys_t::transcodingCanFallback() const
886 {
887     return transcoding_state != (TRANSCODING_VIDEO|TRANSCODING_AUDIO);
888 }
889
890 std::string
891 sout_stream_sys_t::GetAcodecOption( sout_stream_t *p_stream, vlc_fourcc_t *p_codec_audio,
892                                     const audio_format_t *p_aud, int i_quality )
893 {
894     std::stringstream ssout;
895
896     bool b_audio_mp3;
897
898     /* If we were already transcoding: force mp3 because maybe the CC may
899      * have failed because of vorbis. */
900     if( transcoding_state & TRANSCODING_AUDIO )
901         b_audio_mp3 = true;
902     else
903     {
904         switch ( i_quality )
905         {
906             case CONVERSION_QUALITY_HIGH:
907             case CONVERSION_QUALITY_MEDIUM:
908                 b_audio_mp3 = false;
909                 break;
910             default:
911                 b_audio_mp3 = true;
912                 break;
913         }
914     }
915
916     if ( !b_audio_mp3
917       && p_aud->i_channels > 2 && module_exists( "vorbis" ) )
918         *p_codec_audio = VLC_CODEC_VORBIS;
919     else
920         *p_codec_audio = VLC_CODEC_MP3;
921
922     msg_Dbg( p_stream, "Converting audio to %.4s", (const char*)p_codec_audio );
923
924     ssout << "acodec=";
925     char fourcc[5];
926     vlc_fourcc_to_char( *p_codec_audio, fourcc );
927     fourcc[4] = '\0';
928     ssout << fourcc << ',';
929
930     /* XXX: higher vorbis qualities can cause glitches on some CC
931      * devices (Chromecast 1 & 2) */
932     if( *p_codec_audio == VLC_CODEC_VORBIS )
933         ssout << "aenc=vorbis{quality=4},";
934     return ssout.str();
935 }
936
937 bool sout_stream_sys_t::UpdateOutput( sout_stream_t *p_stream )
938 {
939     assert( p_stream->p_sys == this );
940
941     if ( !es_changed )
942         return true;
943
944     es_changed = false;
945
946     bool canRemux = true;
947     vlc_fourcc_t i_codec_video = 0, i_codec_audio = 0;
948     const es_format_t *p_original_audio = NULL;
949     const es_format_t *p_original_video = NULL;
950     const es_format_t *p_original_spu = NULL;
951     bool b_out_streams_changed = false;
952     std::vector<sout_stream_id_sys_t*> new_streams;
953
954     for (std::vector<sout_stream_id_sys_t*>::iterator it = streams.begin(); it != streams.end(); ++it)
955     {
956         const es_format_t *p_es = &(*it)->fmt;
957         if (p_es->i_cat == AUDIO_ES && p_original_audio == NULL)
958         {
959             if ( !canDecodeAudio( p_stream, p_es->i_codec, &p_es->audio ) )
960             {
961                 msg_Dbg( p_stream, "can't remux audio track %d codec %4.4s", p_es->i_id, (const char*)&p_es->i_codec );
962                 canRemux = false;
963             }
964             else if (i_codec_audio == 0)
965                 i_codec_audio = p_es->i_codec;
966             p_original_audio = p_es;
967             new_streams.push_back(*it);
968         }
969         else if (b_supports_video)
970         {
971             if (p_es->i_cat == VIDEO_ES && p_original_video == NULL)
972             {
973                 if (!canDecodeVideo( p_es->i_codec ))
974                 {
975                     msg_Dbg( p_stream, "can't remux video track %d codec %4.4s",
976                              p_es->i_id, (const char*)&p_es->i_codec );
977                     canRemux = false;
978                 }
979                 else if (i_codec_video == 0 && !p_original_spu)
980                     i_codec_video = p_es->i_codec;
981                 p_original_video = p_es;
982                 new_streams.push_back(*it);
983             }
984 #ifdef CC_ENABLE_SPU
985             else if (p_es->i_cat == SPU_ES && p_original_spu == NULL)
986             {
987                 msg_Dbg( p_stream, "forcing video transcode because of subtitle '%4.4s'",
988                          (const char*)&p_es->i_codec );
989                 canRemux = false;
990                 i_codec_video = 0;
991                 p_original_spu = p_es;
992                 new_streams.push_back(*it);
993             }
994 #endif
995             else
996                 continue;
997         }
998         else
999             continue;
1000
1001         bool b_found = out_force_reload;
1002         for (std::vector<sout_stream_id_sys_t*>::iterator out_it = out_streams.begin();
1003              out_it != out_streams.end() && !b_found; ++out_it)
1004         {
1005             if (*out_it == *it)
1006                 b_found = true;
1007         }
1008         if (!b_found)
1009             b_out_streams_changed = true;
1010     }
1011
1012     if (new_streams.empty())
1013     {
1014         p_intf->requestPlayerStop();
1015         return true;
1016     }
1017
1018     /* Don't restart sout and CC session if streams didn't change */
1019     if (!out_force_reload && new_streams.size() == out_streams.size() && !b_out_streams_changed)
1020         return true;
1021
1022     out_force_reload = false;
1023
1024     std::stringstream ssout;
1025     int new_transcoding_state = TRANSCODING_NONE;
1026     if ( !canRemux )
1027     {
1028         if ( !perf_warning_shown && i_codec_video == 0 && p_original_video
1029           && var_InheritInteger( p_stream, RENDERER_CFG_PREFIX "show-perf-warning" ) )
1030         {
1031             int res = vlc_dialog_wait_question( p_stream,
1032                           VLC_DIALOG_QUESTION_WARNING,
1033                          _("Cancel"), _("OK"), _("Ok, Don't warn me again"),
1034                          _("Performance warning"),
1035                          _("Casting this video requires conversion. "
1036                            "This conversion can use all the available power and "
1037                            "could quickly drain your battery." ) );
1038             if ( res <= 0 )
1039                  return false;
1040             perf_warning_shown = true;
1041             if ( res == 2 )
1042                 config_PutInt(RENDERER_CFG_PREFIX "show-perf-warning", 0 );
1043         }
1044
1045         const int i_quality = var_InheritInteger( p_stream, SOUT_CFG_PREFIX "conversion-quality" );
1046
1047         /* TODO: provide audio samplerate and channels */
1048         ssout << "transcode{";
1049         if ( i_codec_audio == 0 && p_original_audio )
1050         {
1051             ssout << GetAcodecOption( p_stream, &i_codec_audio,
1052                                       &p_original_audio->audio, i_quality );
1053             new_transcoding_state |= TRANSCODING_AUDIO;
1054         }
1055         if ( i_codec_video == 0 && p_original_video )
1056         {
1057             try {
1058                 ssout << vlc_sout_renderer_GetVcodecOption( p_stream,
1059                                         { VLC_CODEC_H264, VLC_CODEC_VP8 },
1060                                         &p_original_video->video, i_quality );
1061                 new_transcoding_state |= TRANSCODING_VIDEO;
1062             } catch(const std::exception& e) {
1063                 return false;
1064             }
1065         }
1066         if ( p_original_spu )
1067             ssout << "soverlay,";
1068         ssout << "}:";
1069     }
1070
1071     const bool is_webm = ( i_codec_audio == 0 || i_codec_audio == VLC_CODEC_VORBIS ||
1072                            i_codec_audio == VLC_CODEC_OPUS ) &&
1073                          ( i_codec_video == 0 || i_codec_video == VLC_CODEC_VP8 ||
1074                            i_codec_video == VLC_CODEC_VP9 );
1075
1076     if ( !p_original_video )
1077     {
1078         if( is_webm )
1079             mime = "audio/webm";
1080         else
1081             mime = "audio/x-matroska";
1082     }
1083     else
1084     {
1085         if ( is_webm )
1086             mime = "video/webm";
1087         else
1088             mime = "video/x-matroska";
1089     }
1090
1091     ssout << "chromecast-proxy:"
1092           << "std{mux=" << ( is_webm ? DEFAULT_MUXER_WEBM : DEFAULT_MUXER )
1093           << ",access=chromecast-http}";
1094
1095     if ( !startSoutChain( p_stream, new_streams, ssout.str(),
1096                           new_transcoding_state ) )
1097         p_intf->requestPlayerStop();
1098     return true;
1099 }
1100
1101 sout_stream_id_sys_t *sout_stream_sys_t::GetSubId( sout_stream_t *p_stream,
1102                                                    sout_stream_id_sys_t *id,
1103                                                    bool update )
1104 {
1105     size_t i;
1106
1107     assert( p_stream->p_sys == this );
1108
1109     if ( update && UpdateOutput( p_stream ) == false )
1110         return NULL;
1111
1112     for (i = 0; i < out_streams.size(); ++i)
1113     {
1114         if ( id == (sout_stream_id_sys_t*) out_streams[i] )
1115             return out_streams[i]->p_sub_id;
1116     }
1117
1118     return NULL;
1119 }
1120
1121 bool sout_stream_sys_t::isFlushing( sout_stream_t *p_stream )
1122 {
1123     (void) p_stream;
1124
1125     /* Make sure that all out_streams are flushed when flushing. This avoids
1126      * too many sout/cc restart when a stream is sending data while one other
1127      * is flushing */
1128
1129     if (!cc_flushing)
1130         return false;
1131
1132     for (size_t i = 0; i < out_streams.size(); ++i)
1133     {
1134         if ( !out_streams[i]->flushed )
1135             return true;
1136     }
1137
1138     cc_flushing = false;
1139     for (size_t i = 0; i < out_streams.size(); ++i)
1140         out_streams[i]->flushed = false;
1141
1142     return false;
1143 }
1144
1145 static int Send(sout_stream_t *p_stream, void *_id, block_t *p_buffer)
1146 {
1147     sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
1148     sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( _id );
1149     vlc_mutex_locker locker(&p_sys->lock);
1150
1151     if( p_sys->isFlushing( p_stream ) )
1152     {
1153         block_ChainRelease( p_buffer );
1154         return VLC_SUCCESS;
1155     }
1156
1157     sout_stream_id_sys_t *next_id = p_sys->GetSubId( p_stream, id );
1158     if ( next_id == NULL )
1159     {
1160         block_ChainRelease( p_buffer );
1161         return VLC_EGENERIC;
1162     }
1163
1164     int ret = sout_StreamIdSend(p_sys->p_out, next_id, p_buffer);
1165     if (ret != VLC_SUCCESS)
1166         DelInternal(p_stream, id, false);
1167
1168     return ret;
1169 }
1170
1171 static void Flush( sout_stream_t *p_stream, void *_id )
1172 {
1173     sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
1174     sout_stream_id_sys_t *id = reinterpret_cast<sout_stream_id_sys_t *>( _id );
1175     vlc_mutex_locker locker(&p_sys->lock);
1176
1177     sout_stream_id_sys_t *next_id = p_sys->GetSubId( p_stream, id, false );
1178     if ( next_id == NULL )
1179         return;
1180     next_id->flushed = true;
1181
1182     if( !p_sys->cc_flushing )
1183     {
1184         p_sys->cc_flushing = true;
1185
1186         p_sys->stopSoutChain( p_stream );
1187
1188         p_sys->access_out_live.stop();
1189
1190         if (p_sys->cc_has_input)
1191         {
1192             p_sys->p_intf->requestPlayerStop();
1193             p_sys->cc_has_input = false;
1194         }
1195         p_sys->out_force_reload = p_sys->es_changed = true;
1196     }
1197 }
1198
1199 static void on_input_event_cb(void *data, enum cc_input_event event, union cc_input_arg arg )
1200 {
1201     sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(data);
1202     sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
1203
1204     vlc_mutex_locker locker(&p_sys->lock);
1205     switch (event)
1206     {
1207         case CC_INPUT_EVENT_EOF:
1208             /* In case of EOF: stop the sout chain in order to drain all
1209              * sout/demuxers/access. If EOF changes to false, reset es_changed
1210              * in order to reload the sout from next Send calls. */
1211             if( arg.eof )
1212                 p_sys->stopSoutChain( p_stream );
1213             else
1214                 p_sys->out_force_reload = p_sys->es_changed = true;
1215             break;
1216         case CC_INPUT_EVENT_RETRY:
1217             p_sys->stopSoutChain( p_stream );
1218             if( p_sys->transcodingCanFallback() )
1219             {
1220                 p_sys->setNextTranscodingState();
1221                 msg_Warn(p_stream, "Load failed detected. Switching to next "
1222                          "configuration. Transcoding video%s",
1223                          p_sys->transcoding_state & TRANSCODING_AUDIO ? "/audio" : "");
1224                 p_sys->out_force_reload = p_sys->es_changed = true;
1225             }
1226             break;
1227     }
1228 }
1229
1230 /*****************************************************************************
1231  * Open: connect to the Chromecast and initialize the sout
1232  *****************************************************************************/
1233 static int Open(vlc_object_t *p_this)
1234 {
1235     sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(p_this);
1236     sout_stream_sys_t *p_sys = NULL;
1237     intf_sys_t *p_intf = NULL;
1238     char *psz_ip = NULL;
1239     httpd_host_t *httpd_host = NULL;
1240     bool b_supports_video = true;
1241     int i_local_server_port;
1242     int i_device_port;
1243     std::stringstream ss;
1244
1245     config_ChainParse(p_stream, SOUT_CFG_PREFIX, ppsz_sout_options, p_stream->p_cfg);
1246
1247     psz_ip = var_GetNonEmptyString( p_stream, SOUT_CFG_PREFIX "ip");
1248     if ( psz_ip == NULL )
1249     {
1250         msg_Err( p_this, "missing Chromecast IP address" );
1251         goto error;
1252     }
1253
1254     i_device_port = var_InheritInteger(p_stream, SOUT_CFG_PREFIX "port");
1255     i_local_server_port = var_InheritInteger(p_stream, SOUT_CFG_PREFIX "http-port");
1256
1257     var_Create(p_stream, "http-port", VLC_VAR_INTEGER);
1258     var_SetInteger(p_stream, "http-port", i_local_server_port);
1259     var_Create(p_stream, "http-host", VLC_VAR_STRING);
1260     var_SetString(p_stream, "http-host", "");
1261     httpd_host = vlc_http_HostNew(VLC_OBJECT(p_stream));
1262     if (httpd_host == NULL)
1263         goto error;
1264
1265     try
1266     {
1267         p_intf = new intf_sys_t( p_this, i_local_server_port, psz_ip, i_device_port,
1268                                  httpd_host );
1269     }
1270     catch (const std::runtime_error& err )
1271     {
1272         msg_Err( p_this, "cannot load the Chromecast controller (%s)", err.what() );
1273         goto error;
1274     }
1275     catch (const std::bad_alloc& )
1276     {
1277         p_intf = NULL;
1278         goto error;
1279     }
1280
1281     b_supports_video = var_GetBool(p_stream, SOUT_CFG_PREFIX "video");
1282
1283     try
1284     {
1285         p_sys = new sout_stream_sys_t( httpd_host, p_intf, b_supports_video,
1286                                                     i_local_server_port );
1287     }
1288     catch ( std::exception& ex )
1289     {
1290         msg_Err( p_stream, "Failed to instantiate sout_stream_sys_t: %s", ex.what() );
1291         p_sys = NULL;
1292         goto error;
1293     }
1294
1295     p_intf->setOnInputEventCb(on_input_event_cb, p_stream);
1296
1297     /* prevent sout-mux-caching since chromecast-proxy is already doing it */
1298     var_Create( p_stream->p_sout, "sout-mux-caching", VLC_VAR_INTEGER );
1299     var_SetInteger( p_stream->p_sout, "sout-mux-caching", 0 );
1300
1301     var_Create( p_stream->p_sout, SOUT_CFG_PREFIX "sys", VLC_VAR_ADDRESS );
1302     var_SetAddress( p_stream->p_sout, SOUT_CFG_PREFIX "sys", p_sys );
1303
1304     var_Create( p_stream->p_sout, SOUT_CFG_PREFIX "access-out-sys", VLC_VAR_ADDRESS );
1305
1306     // Set the sout callbacks.
1307     p_stream->pf_add     = Add;
1308     p_stream->pf_del     = Del;
1309     p_stream->pf_send    = Send;
1310     p_stream->pf_flush   = Flush;
1311
1312     p_stream->p_sys = p_sys;
1313
1314     free(psz_ip);
1315
1316     return VLC_SUCCESS;
1317
1318 error:
1319     delete p_intf;
1320     if (httpd_host)
1321         httpd_HostDelete(httpd_host);
1322     free(psz_ip);
1323     delete p_sys;
1324     return VLC_EGENERIC;
1325 }
1326
1327 /*****************************************************************************
1328  * Close: destroy interface
1329  *****************************************************************************/
1330 static void Close(vlc_object_t *p_this)
1331 {
1332     sout_stream_t *p_stream = reinterpret_cast<sout_stream_t*>(p_this);
1333     sout_stream_sys_t *p_sys = reinterpret_cast<sout_stream_sys_t *>( p_stream->p_sys );
1334
1335     assert(p_sys->out_streams.empty() && p_sys->streams.empty());
1336     var_Destroy( p_stream->p_sout, SOUT_CFG_PREFIX "sys" );
1337     var_Destroy( p_stream->p_sout, SOUT_CFG_PREFIX "sout-mux-caching" );
1338
1339     assert(p_sys->streams.empty() && p_sys->out_streams.empty());
1340
1341     httpd_host_t *httpd_host = p_sys->httpd_host;
1342     delete p_sys->p_intf;
1343     delete p_sys;
1344     /* Delete last since p_intf and p_sys depends on httpd_host */
1345     httpd_HostDelete(httpd_host);
1346 }
1347