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 /*****************************************************************************
25 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_demux.h>
35 #include "chromecast_common.h"
40 static void on_paused_changed_cb(void *data
, bool paused
);
44 demux_cc(demux_t
* const demux
, chromecast_common
* const renderer
)
48 ,m_pause_date( VLC_TICK_INVALID
)
49 ,m_pause_delay( VLC_TICK_INVALID
)
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
;
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
);
72 vlc_meta_Set( p_meta
, (vlc_meta_type_t
)i
, psz_meta
);
76 if( vlc_meta_Get( p_meta
, vlc_meta_Title
) == NULL
)
78 char *psz_meta
= input_item_GetName( p_item
);
81 vlc_meta_Set( p_meta
, vlc_meta_Title
, 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
);
90 vlc_meta_Delete( p_meta
);
93 if (demux_Control( p_demux
->p_next
, DEMUX_CAN_SEEK
, &m_can_seek
) != VLC_SUCCESS
)
95 if (demux_Control( p_demux
->p_next
, DEMUX_GET_LENGTH
, &m_length
) != VLC_SUCCESS
)
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
;
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
] );
127 if( b_is_interactive
== true )
129 demux_Control( p_demux
->p_next
, DEMUX_SET_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
);
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
);
152 m_start_time
= m_last_time
= -1;
153 m_start_pos
= m_last_pos
= -1.0f
;
158 if( demux_Control( p_demux
->p_next
, DEMUX_GET_TIME
, &m_start_time
) != VLC_SUCCESS
)
161 if( demux_Control( p_demux
->p_next
, DEMUX_GET_POSITION
, &m_start_pos
) != VLC_SUCCESS
)
164 m_last_time
= m_start_time
;
165 m_last_pos
= m_start_pos
;
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
;
200 if( m_start_time
< 0 )
203 int64_t time
= m_start_time
;
204 vlc_tick_t cc_time
= getCCTime();
206 if( cc_time
!= VLC_TICK_INVALID
)
214 if( m_length
>= 0 && m_start_pos
>= 0 )
216 m_last_pos
= ( getCCTime() / double( m_length
) ) + m_start_pos
;
223 void seekBack( vlc_tick_t time
, double pos
)
225 es_out_Control( p_demux
->p_next
->out
, ES_OUT_RESET_PCR
);
229 int ret
= VLC_EGENERIC
;
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 );
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
);
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
);
255 p_renderer
->pf_send_input_event( p_renderer
->p_opaque
,
256 CC_INPUT_EVENT_RETRY
,
257 cc_input_arg
{false} );
260 case CC_PACE_OK_WAIT
:
261 /* Yeld: return to let the input thread doing controls */
262 return VLC_DEMUXER_SUCCESS
;
264 case CC_PACE_OK_ENDED
:
267 vlc_assert_unreachable();
270 int ret
= VLC_DEMUXER_SUCCESS
;
273 ret
= demux_Demux( p_demux
->p_next
);
274 if( ret
!= VLC_DEMUXER_EGENERIC
275 && ( m_start_time
< 0 || m_start_pos
< 0.0f
) )
277 if( ret
== VLC_DEMUXER_EOF
)
283 /* Signal EOF to the sout when the es_out is empty (so when the
284 * DecoderThread fifo are empty) */
286 es_out_Control( p_demux
->p_next
->out
, ES_OUT_GET_EMPTY
, &b_empty
);
288 p_renderer
->pf_send_input_event( p_renderer
->p_opaque
,
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
;
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
);
309 case DEMUX_GET_POSITION
:
311 double pos
= getPosition();
314 *va_arg( args
, double * ) = pos
;
321 vlc_tick_t time
= getTime();
324 *va_arg(args
, int64_t *) = time
;
329 case DEMUX_GET_LENGTH
:
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 * );
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* );
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
)
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
)
383 case DEMUX_SET_PAUSE_STATE
:
388 int paused
= va_arg( ap
, int );
393 if (m_pause_date
== VLC_TICK_INVALID
)
394 m_pause_date
= vlc_tick_now();
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
);
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
);
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
);
424 case DEMUX_FILTER_DISABLE
:
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
);
439 return demux_vaControl( p_demux_filter
->p_next
, i_query
, args
);
443 demux_t
* const p_demux
;
444 chromecast_common
* p_renderer
;
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
466 /* Try to find the playlist or the mediaplayer that handle the corks
468 if( var_Type( obj
, "corks" ) != 0 )
470 ( paused
? var_IncInteger
: var_DecInteger
)( obj
, "corks" );
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" );
502 demux_cc
*p_sys
= new(std::nothrow
) demux_cc( p_demux
, p_renderer
);
503 if (unlikely(p_sys
== NULL
))
506 p_demux
->p_sys
= p_sys
;
507 p_demux
->pf_demux
= Demux
;
508 p_demux
->pf_control
= Control
;
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
);
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
)