chromecast: replace the p_input hack by an other one
[vlc.git] / modules / stream_out / chromecast / chromecast_demux.cpp
blobe076a18f4d2879f0d90b804f79cbe2525ee482e6
1 /*****************************************************************************
2 * chromecast_demux.cpp: Chromecast demux filter module for vlc
3 *****************************************************************************
4 * Copyright © 2015-2016 VideoLAN
6 * Authors: Steve Lhomme <robux4@videolabs.io>
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU Lesser General Public License as published by
10 * the Free Software Foundation; either version 2.1 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program; if not, write to the Free Software Foundation,
20 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21 *****************************************************************************/
23 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_demux.h>
35 #include "chromecast_common.h"
37 #include <assert.h>
38 #include <new>
40 static void on_paused_changed_cb(void *data, bool paused);
42 struct demux_cc
44 demux_cc(demux_t * const demux, chromecast_common * const renderer)
45 :p_demux(demux)
46 ,p_renderer(renderer)
47 ,m_enabled( true )
48 ,m_pause_date( VLC_TICK_INVALID )
49 ,m_pause_delay( VLC_TICK_INVALID )
51 init();
54 void init()
56 resetDemuxEof();
58 vlc_meta_t *p_meta = vlc_meta_New();
59 if( likely(p_meta != NULL) )
61 input_item_t *p_item = p_demux->p_next->p_input ?
62 input_GetItem( p_demux->p_next->p_input ) : NULL;
63 if( p_item )
65 /* Favor Meta from the input item of the input_thread since
66 * it's pre-processed by the meta fetcher */
67 for( int i = 0; i < VLC_META_TYPE_COUNT; ++i )
69 char *psz_meta = input_item_GetMeta( p_item, (vlc_meta_type_t)i );
70 if( psz_meta )
72 vlc_meta_Set( p_meta, (vlc_meta_type_t)i, psz_meta );
73 free( psz_meta );
76 if( vlc_meta_Get( p_meta, vlc_meta_Title ) == NULL )
78 char *psz_meta = input_item_GetName( p_item );
79 if( psz_meta )
81 vlc_meta_Set( p_meta, vlc_meta_Title, psz_meta );
82 free( psz_meta );
85 p_renderer->pf_set_meta( p_renderer->p_opaque, p_meta );
87 else if (demux_Control( p_demux->p_next, DEMUX_GET_META, p_meta) == VLC_SUCCESS)
88 p_renderer->pf_set_meta( p_renderer->p_opaque, p_meta );
89 else
90 vlc_meta_Delete( p_meta );
93 if (demux_Control( p_demux->p_next, DEMUX_CAN_SEEK, &m_can_seek ) != VLC_SUCCESS)
94 m_can_seek = false;
95 if (demux_Control( p_demux->p_next, DEMUX_GET_LENGTH, &m_length ) != VLC_SUCCESS)
96 m_length = -1;
98 int i_current_title;
99 if( demux_Control( p_demux->p_next, DEMUX_GET_TITLE,
100 &i_current_title ) == VLC_SUCCESS )
102 input_title_t** pp_titles;
103 int i_nb_titles, i_title_offset, i_chapter_offset;
104 if( demux_Control( p_demux->p_next, DEMUX_GET_TITLE_INFO, &pp_titles,
105 &i_nb_titles, &i_title_offset,
106 &i_chapter_offset ) == VLC_SUCCESS )
108 int64_t i_longest_duration = 0;
109 int i_longest_title = 0;
110 bool b_is_interactive = false;
111 for( int i = 0 ; i < i_nb_titles; ++i )
113 if( pp_titles[i]->i_length > i_longest_duration )
115 i_longest_duration = pp_titles[i]->i_length;
116 i_longest_title = i;
118 if( i_current_title == i &&
119 pp_titles[i]->i_flags & INPUT_TITLE_INTERACTIVE )
121 b_is_interactive = true;
123 vlc_input_title_Delete( pp_titles[i] );
125 free( pp_titles );
127 if( b_is_interactive == true )
129 demux_Control( p_demux->p_next, DEMUX_SET_TITLE,
130 i_longest_title );
135 es_out_Control( p_demux->p_next->out, ES_OUT_RESET_PCR );
137 p_renderer->pf_set_demux_enabled(p_renderer->p_opaque, true,
138 on_paused_changed_cb, p_demux);
140 resetTimes();
143 void deinit()
145 assert(p_renderer);
146 p_renderer->pf_set_meta( p_renderer->p_opaque, NULL );
147 p_renderer->pf_set_demux_enabled(p_renderer->p_opaque, false, NULL, NULL);
150 void resetTimes()
152 m_start_time = m_last_time = -1;
153 m_start_pos = m_last_pos = -1.0f;
156 void initTimes()
158 if( demux_Control( p_demux->p_next, DEMUX_GET_TIME, &m_start_time ) != VLC_SUCCESS )
159 m_start_time = -1;
161 if( demux_Control( p_demux->p_next, DEMUX_GET_POSITION, &m_start_pos ) != VLC_SUCCESS )
162 m_start_pos = -1.0f;
164 m_last_time = m_start_time;
165 m_last_pos = m_start_pos;
168 ~demux_cc()
170 if( p_renderer )
171 deinit();
174 void resetDemuxEof()
176 m_demux_eof = false;
177 p_renderer->pf_send_input_event( p_renderer->p_opaque, CC_INPUT_EVENT_EOF,
178 cc_input_arg { false } );
181 void setPauseState(bool paused, vlc_tick_t delay)
183 p_renderer->pf_set_pause_state( p_renderer->p_opaque, paused, delay );
186 vlc_tick_t getCCTime()
188 vlc_tick_t system, delay;
189 if( es_out_ControlGetPcrSystem( p_demux->p_next->out, &system, &delay ) )
190 return VLC_TICK_INVALID;
192 vlc_tick_t cc_time = p_renderer->pf_get_time( p_renderer->p_opaque );
193 if( cc_time != VLC_TICK_INVALID )
194 return cc_time - system + m_pause_delay;
195 return VLC_TICK_INVALID;
198 vlc_tick_t getTime()
200 if( m_start_time < 0 )
201 return -1;
203 int64_t time = m_start_time;
204 vlc_tick_t cc_time = getCCTime();
206 if( cc_time != VLC_TICK_INVALID )
207 time += cc_time;
208 m_last_time = time;
209 return time;
212 double getPosition()
214 if( m_length >= 0 && m_start_pos >= 0 )
216 m_last_pos = ( getCCTime() / double( m_length ) ) + m_start_pos;
217 return m_last_pos;
219 else
220 return -1;
223 void seekBack( vlc_tick_t time, double pos )
225 es_out_Control( p_demux->p_next->out, ES_OUT_RESET_PCR );
227 if( m_can_seek )
229 int ret = VLC_EGENERIC;
230 if( time >= 0 )
231 ret = demux_Control( p_demux->p_next, DEMUX_SET_TIME, time, false );
233 if( ret != VLC_SUCCESS && pos >= 0 )
234 demux_Control( p_demux->p_next, DEMUX_SET_POSITION, pos, false );
238 int Demux()
240 if ( !m_enabled )
241 return demux_Demux( p_demux->p_next );
243 /* The CC sout is not pacing, so we pace here */
244 int pace = p_renderer->pf_pace( p_renderer->p_opaque );
245 switch (pace)
247 case CC_PACE_ERR:
248 return VLC_DEMUXER_EGENERIC;
249 case CC_PACE_ERR_RETRY:
251 /* Seek back to started position */
252 seekBack(m_start_time, m_start_pos);
254 resetDemuxEof();
255 p_renderer->pf_send_input_event( p_renderer->p_opaque,
256 CC_INPUT_EVENT_RETRY,
257 cc_input_arg{false} );
258 break;
260 case CC_PACE_OK_WAIT:
261 /* Yeld: return to let the input thread doing controls */
262 return VLC_DEMUXER_SUCCESS;
263 case CC_PACE_OK:
264 case CC_PACE_OK_ENDED:
265 break;
266 default:
267 vlc_assert_unreachable();
270 int ret = VLC_DEMUXER_SUCCESS;
271 if( !m_demux_eof )
273 ret = demux_Demux( p_demux->p_next );
274 if( ret != VLC_DEMUXER_EGENERIC
275 && ( m_start_time < 0 || m_start_pos < 0.0f ) )
276 initTimes();
277 if( ret == VLC_DEMUXER_EOF )
278 m_demux_eof = true;
281 if( m_demux_eof )
283 /* Signal EOF to the sout when the es_out is empty (so when the
284 * DecoderThread fifo are empty) */
285 bool b_empty;
286 es_out_Control( p_demux->p_next->out, ES_OUT_GET_EMPTY, &b_empty );
287 if( b_empty )
288 p_renderer->pf_send_input_event( p_renderer->p_opaque,
289 CC_INPUT_EVENT_EOF,
290 cc_input_arg{ true } );
292 /* Don't return EOF until the chromecast is not EOF. This allows
293 * this demux filter to have more controls over the sout. Indeed,
294 * we still can seek or change tracks when the input is EOF and we
295 * should continue to handle CC errors. */
296 ret = pace == CC_PACE_OK ? VLC_DEMUXER_SUCCESS : VLC_DEMUXER_EOF;
299 return ret;
302 int Control( demux_t *p_demux_filter, int i_query, va_list args )
304 if( !m_enabled && i_query != DEMUX_FILTER_ENABLE )
305 return demux_vaControl( p_demux_filter->p_next, i_query, args );
307 switch (i_query)
309 case DEMUX_GET_POSITION:
311 double pos = getPosition();
312 if( pos >= 0 )
314 *va_arg( args, double * ) = pos;
315 return VLC_SUCCESS;
317 return VLC_EGENERIC;
319 case DEMUX_GET_TIME:
321 vlc_tick_t time = getTime();
322 if( time >= 0 )
324 *va_arg(args, int64_t *) = time;
325 return VLC_SUCCESS;
327 return VLC_EGENERIC;
329 case DEMUX_GET_LENGTH:
331 int ret;
332 va_list ap;
334 va_copy( ap, args );
335 ret = demux_vaControl( p_demux_filter->p_next, i_query, args );
336 if( ret == VLC_SUCCESS )
337 m_length = *va_arg( ap, int64_t * );
338 va_end( ap );
339 return ret;
342 case DEMUX_CAN_SEEK:
344 int ret;
345 va_list ap;
347 va_copy( ap, args );
348 ret = demux_vaControl( p_demux_filter->p_next, i_query, args );
349 if( ret == VLC_SUCCESS )
350 m_can_seek = *va_arg( ap, bool* );
351 va_end( ap );
352 return ret;
355 case DEMUX_SET_POSITION:
357 m_pause_delay = m_pause_date = VLC_TICK_INVALID;
359 double pos = va_arg( args, double );
360 /* Force unprecise seek */
361 int ret = demux_Control( p_demux->p_next, DEMUX_SET_POSITION, pos, false );
362 if( ret != VLC_SUCCESS )
363 return ret;
365 resetTimes();
366 resetDemuxEof();
367 return VLC_SUCCESS;
369 case DEMUX_SET_TIME:
371 m_pause_delay = m_pause_date = VLC_TICK_INVALID;
373 vlc_tick_t time = va_arg( args, int64_t );
374 /* Force unprecise seek */
375 int ret = demux_Control( p_demux->p_next, DEMUX_SET_TIME, time, false );
376 if( ret != VLC_SUCCESS )
377 return ret;
379 resetTimes();
380 resetDemuxEof();
381 return VLC_SUCCESS;
383 case DEMUX_SET_PAUSE_STATE:
385 va_list ap;
387 va_copy( ap, args );
388 int paused = va_arg( ap, int );
389 va_end( ap );
391 if (paused)
393 if (m_pause_date == VLC_TICK_INVALID)
394 m_pause_date = vlc_tick_now();
396 else
398 if (m_pause_date != VLC_TICK_INVALID)
400 m_pause_delay += vlc_tick_now() - m_pause_date;
401 m_pause_date = VLC_TICK_INVALID;
405 setPauseState( paused != 0, m_pause_delay );
406 break;
408 case DEMUX_SET_ES:
409 /* Seek back to the last known pos when changing tracks. This will
410 * flush sout streams, make sout del/add called right away and
411 * clear CC buffers. */
412 seekBack(m_last_time, m_last_pos);
413 resetTimes();
414 resetDemuxEof();
415 break;
416 case DEMUX_FILTER_ENABLE:
417 p_renderer = static_cast<chromecast_common *>(
418 var_InheritAddress( p_demux, CC_SHARED_VAR_NAME ) );
419 assert(p_renderer != NULL);
420 m_enabled = true;
421 init();
422 return VLC_SUCCESS;
424 case DEMUX_FILTER_DISABLE:
426 deinit();
428 /* Seek back to last known position. Indeed we don't want to resume
429 * from the input position that can be more than 1 minutes forward
430 * (depending on the CC buffering policy). */
431 seekBack(m_last_time, m_last_pos);
433 m_enabled = false;
434 p_renderer = NULL;
436 return VLC_SUCCESS;
439 return demux_vaControl( p_demux_filter->p_next, i_query, args );
442 protected:
443 demux_t * const p_demux;
444 chromecast_common * p_renderer;
445 vlc_tick_t m_length;
446 bool m_can_seek;
447 bool m_enabled;
448 bool m_demux_eof;
449 double m_start_pos;
450 double m_last_pos;
451 vlc_tick_t m_start_time;
452 vlc_tick_t m_last_time;
453 vlc_tick_t m_pause_date;
454 vlc_tick_t m_pause_delay;
457 static void on_paused_changed_cb( void *data, bool paused )
459 demux_t *p_demux = reinterpret_cast<demux_t*>(data);
460 vlc_object_t *obj = p_demux->p_next->obj.parent;
462 /* XXX: Ugly: Notify the parent of the input_thread_t that the corks state
463 * changed */
464 while( obj != NULL )
466 /* Try to find the playlist or the mediaplayer that handle the corks
467 * state */
468 if( var_Type( obj, "corks" ) != 0 )
470 ( paused ? var_IncInteger : var_DecInteger )( obj, "corks" );
471 return;
473 obj = obj->obj.parent;
477 static int Demux( demux_t *p_demux_filter )
479 demux_cc *p_sys = reinterpret_cast<demux_cc*>(p_demux_filter->p_sys);
481 return p_sys->Demux();
484 static int Control( demux_t *p_demux_filter, int i_query, va_list args)
486 demux_cc *p_sys = reinterpret_cast<demux_cc*>(p_demux_filter->p_sys);
488 return p_sys->Control( p_demux_filter, i_query, args );
491 int Open(vlc_object_t *p_this)
493 demux_t *p_demux = reinterpret_cast<demux_t*>(p_this);
494 chromecast_common *p_renderer = static_cast<chromecast_common *>(
495 var_InheritAddress( p_demux, CC_SHARED_VAR_NAME ) );
496 if ( p_renderer == NULL )
498 msg_Warn( p_this, "using Chromecast demuxer with no sout" );
499 return VLC_ENOOBJ;
502 demux_cc *p_sys = new(std::nothrow) demux_cc( p_demux, p_renderer );
503 if (unlikely(p_sys == NULL))
504 return VLC_ENOMEM;
506 p_demux->p_sys = p_sys;
507 p_demux->pf_demux = Demux;
508 p_demux->pf_control = Control;
510 return VLC_SUCCESS;
513 void Close(vlc_object_t *p_this)
515 demux_t *p_demux = reinterpret_cast<demux_t*>(p_this);
516 demux_cc *p_sys = reinterpret_cast<demux_cc*>(p_demux->p_sys);
518 delete p_sys;
521 vlc_module_begin ()
522 set_shortname( "cc_demux" )
523 set_category( CAT_INPUT )
524 set_subcategory( SUBCAT_INPUT_DEMUX )
525 set_description( N_( "Chromecast demux wrapper" ) )
526 set_capability( "demux_filter", 0 )
527 add_shortcut( "cc_demux" )
528 set_callbacks( Open, Close )
529 vlc_module_end ()