1 /*****************************************************************************
2 * es_out_timeshift.c: Es Out timeshift.
3 *****************************************************************************
4 * Copyright (C) 2008 Laurent Aimar
6 * Authors: Laurent Aimar < fenrir _AT_ videolan _DOT_ org>
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 *****************************************************************************/
40 #include <vlc_common.h>
42 #include <vlc_mouse.h>
44 # include <vlc_charset.h>
46 #include <vlc_es_out.h>
47 #include <vlc_block.h>
48 #include "input_internal.h"
51 /*****************************************************************************
53 *****************************************************************************/
55 /* XXX attribute_packed is (and MUST be) used ONLY to reduce memory usage */
56 #ifdef HAVE_ATTRIBUTE_PACKED
57 # define attribute_packed __attribute__((__packed__))
59 # define attribute_packed
70 typedef struct attribute_packed
77 typedef struct attribute_packed
82 typedef struct attribute_packed
86 int i_offset
; /* We do not use file > INT_MAX */
89 typedef struct attribute_packed
118 vlc_epg_event_t
*p_evt
;
138 typedef struct attribute_packed
147 vlc_tick_t i_pts_delay
;
148 vlc_tick_t i_pts_jitter
;
155 vlc_tick_t i_normal_time
;
159 } ts_cmd_privcontrol_t
;
161 typedef struct attribute_packed
170 ts_cmd_control_t control
;
171 ts_cmd_privcontrol_t privcontrol
;
175 typedef struct ts_storage_t ts_storage_t
;
178 ts_storage_t
*p_next
;
182 char *psz_file
; /* Filename */
184 size_t i_file_max
; /* Max size in bytes */
185 int64_t i_file_size
;/* Current size in bytes */
186 FILE *p_filew
; /* FILE handle for data writing */
187 FILE *p_filer
; /* FILE handle for data reading */
199 input_thread_t
*p_input
;
201 int64_t i_tmp_size_max
;
202 const char *psz_tmp_path
;
204 /* Lock for all following fields */
210 vlc_tick_t i_pause_date
;
215 vlc_tick_t i_rate_date
;
216 vlc_tick_t i_rate_delay
;
219 vlc_tick_t i_buffering_delay
;
222 ts_storage_t
*p_storage_r
;
223 ts_storage_t
*p_storage_w
;
225 vlc_tick_t i_cmd_delay
;
236 input_thread_t
*p_input
;
240 int64_t i_tmp_size_max
; /* Maximal temporary file size in byte */
241 char *psz_tmp_path
; /* Path for temporary files */
243 /* Lock for all following fields */
252 bool b_input_paused_source
;
254 float input_rate_source
;
263 static void Del ( es_out_t
*, es_out_id_t
* );
265 static int TsStart( es_out_t
* );
266 static void TsAutoStop( es_out_t
* );
268 static void TsStop( ts_thread_t
* );
269 static void TsPushCmd( ts_thread_t
*, ts_cmd_t
* );
270 static int TsPopCmdLocked( ts_thread_t
*, ts_cmd_t
*, bool b_flush
);
271 static bool TsHasCmd( ts_thread_t
* );
272 static bool TsIsUnused( ts_thread_t
* );
273 static int TsChangePause( ts_thread_t
*, bool b_source_paused
, bool b_paused
, vlc_tick_t i_date
);
274 static int TsChangeRate( ts_thread_t
*, float src_rate
, float rate
);
276 static void *TsRun( void * );
278 static ts_storage_t
*TsStorageNew( const char *psz_path
, int64_t i_tmp_size_max
);
279 static void TsStorageDelete( ts_storage_t
* );
280 static void TsStoragePack( ts_storage_t
*p_storage
);
281 static bool TsStorageIsFull( ts_storage_t
*, const ts_cmd_t
*p_cmd
);
282 static bool TsStorageIsEmpty( ts_storage_t
* );
283 static void TsStoragePushCmd( ts_storage_t
*, const ts_cmd_t
*p_cmd
, bool b_flush
);
284 static void TsStoragePopCmd( ts_storage_t
*p_storage
, ts_cmd_t
*p_cmd
, bool b_flush
);
286 static void CmdClean( ts_cmd_t
* );
287 static void cmd_cleanup_routine( void *p
) { CmdClean( p
); }
289 static int CmdInitAdd ( ts_cmd_t
*, input_source_t
*, es_out_id_t
*, const es_format_t
*, bool b_copy
);
290 static void CmdInitSend ( ts_cmd_t
*, es_out_id_t
*, block_t
* );
291 static int CmdInitDel ( ts_cmd_t
*, es_out_id_t
* );
292 static int CmdInitControl( ts_cmd_t
*, input_source_t
*, int i_query
, va_list, bool b_copy
);
293 static int CmdInitPrivControl( ts_cmd_t
*, int i_query
, va_list, bool b_copy
);
296 static void CmdCleanAdd ( ts_cmd_t
* );
297 static void CmdCleanSend ( ts_cmd_t
* );
298 static void CmdCleanControl( ts_cmd_t
*p_cmd
);
300 /* XXX these functions will take the destination es_out_t */
301 static void CmdExecuteAdd ( es_out_t
*, ts_cmd_t
* );
302 static int CmdExecuteSend ( es_out_t
*, ts_cmd_t
* );
303 static void CmdExecuteDel ( es_out_t
*, ts_cmd_t
* );
304 static int CmdExecuteControl( es_out_t
*, ts_cmd_t
* );
305 static int CmdExecutePrivControl( es_out_t
*, ts_cmd_t
* );
308 static int GetTmpFile( char **ppsz_file
, const char *psz_path
);
310 static const struct es_out_callbacks es_out_timeshift_cbs
;
312 /*****************************************************************************
313 * input_EsOutTimeshiftNew:
314 *****************************************************************************/
315 es_out_t
*input_EsOutTimeshiftNew( input_thread_t
*p_input
, es_out_t
*p_next_out
, float rate
)
317 es_out_sys_t
*p_sys
= malloc( sizeof(*p_sys
) );
321 p_sys
->out
.cbs
= &es_out_timeshift_cbs
;
324 p_sys
->b_input_paused
= false;
325 p_sys
->b_input_paused_source
= false;
326 p_sys
->p_input
= p_input
;
327 p_sys
->input_rate
= rate
;
328 p_sys
->input_rate_source
= rate
;
330 p_sys
->p_out
= p_next_out
;
331 vlc_mutex_init_recursive( &p_sys
->lock
);
333 p_sys
->b_delayed
= false;
336 TAB_INIT( p_sys
->i_es
, p_sys
->pp_es
);
339 const int i_tmp_size_max
= var_CreateGetInteger( p_input
, "input-timeshift-granularity" );
340 if( i_tmp_size_max
< 0 )
341 p_sys
->i_tmp_size_max
= 50*1024*1024;
343 p_sys
->i_tmp_size_max
= __MAX( i_tmp_size_max
, 1*1024*1024 );
344 msg_Dbg( p_input
, "using timeshift granularity of %d MiB",
345 (int)p_sys
->i_tmp_size_max
/(1024*1024) );
347 p_sys
->psz_tmp_path
= var_InheritString( p_input
, "input-timeshift-path" );
348 #if defined (_WIN32) && !VLC_WINSTORE_APP
349 if( p_sys
->psz_tmp_path
== NULL
)
351 const DWORD count
= GetTempPath( 0, NULL
);
354 WCHAR
*path
= vlc_alloc( count
+ 1, sizeof(WCHAR
) );
357 DWORD ret
= GetTempPath( count
+ 1, path
);
358 if( ret
!= 0 && ret
<= count
)
359 p_sys
->psz_tmp_path
= FromWide( path
);
364 if( p_sys
->psz_tmp_path
== NULL
)
366 wchar_t *wpath
= _wgetcwd( NULL
, 0 );
369 p_sys
->psz_tmp_path
= FromWide( wpath
);
373 if( p_sys
->psz_tmp_path
== NULL
)
374 p_sys
->psz_tmp_path
= strdup( "C:" );
376 if( p_sys
->psz_tmp_path
!= NULL
)
378 size_t len
= strlen( p_sys
->psz_tmp_path
);
380 while( len
> 0 && p_sys
->psz_tmp_path
[len
- 1] == DIR_SEP_CHAR
)
383 p_sys
->psz_tmp_path
[len
] = '\0';
386 if( p_sys
->psz_tmp_path
!= NULL
)
387 msg_Dbg( p_input
, "using timeshift path: %s", p_sys
->psz_tmp_path
);
389 msg_Dbg( p_input
, "using default timeshift path" );
392 #define S(t) msg_Err( p_input, "SIZEOF("#t")=%d", sizeof(t) )
404 /*****************************************************************************
406 *****************************************************************************/
407 static void Destroy( es_out_t
*p_out
)
409 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
411 if( p_sys
->b_delayed
)
413 TsStop( p_sys
->p_ts
);
414 p_sys
->b_delayed
= false;
417 while( p_sys
->i_es
> 0 )
418 Del( p_out
, p_sys
->pp_es
[0] );
419 TAB_CLEAN( p_sys
->i_es
, p_sys
->pp_es
);
421 free( p_sys
->psz_tmp_path
);
425 static es_out_id_t
*Add( es_out_t
*p_out
, input_source_t
*in
, const es_format_t
*p_fmt
)
427 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
430 es_out_id_t
*p_es
= malloc( sizeof( *p_es
) );
434 vlc_mutex_lock( &p_sys
->lock
);
438 if( CmdInitAdd( &cmd
, in
, p_es
, p_fmt
, p_sys
->b_delayed
) )
440 vlc_mutex_unlock( &p_sys
->lock
);
445 TAB_APPEND( p_sys
->i_es
, p_sys
->pp_es
, p_es
);
447 if( p_sys
->b_delayed
)
448 TsPushCmd( p_sys
->p_ts
, &cmd
);
450 CmdExecuteAdd( p_sys
->p_out
, &cmd
);
452 vlc_mutex_unlock( &p_sys
->lock
);
456 static int Send( es_out_t
*p_out
, es_out_id_t
*p_es
, block_t
*p_block
)
458 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
460 int i_ret
= VLC_SUCCESS
;
462 vlc_mutex_lock( &p_sys
->lock
);
466 CmdInitSend( &cmd
, p_es
, p_block
);
467 if( p_sys
->b_delayed
)
468 TsPushCmd( p_sys
->p_ts
, &cmd
);
470 i_ret
= CmdExecuteSend( p_sys
->p_out
, &cmd
) ;
472 vlc_mutex_unlock( &p_sys
->lock
);
476 static void Del( es_out_t
*p_out
, es_out_id_t
*p_es
)
478 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
481 vlc_mutex_lock( &p_sys
->lock
);
485 CmdInitDel( &cmd
, p_es
);
486 if( p_sys
->b_delayed
)
487 TsPushCmd( p_sys
->p_ts
, &cmd
);
489 CmdExecuteDel( p_sys
->p_out
, &cmd
);
491 TAB_REMOVE( p_sys
->i_es
, p_sys
->pp_es
, p_es
);
493 vlc_mutex_unlock( &p_sys
->lock
);
496 static inline int es_out_in_vaControl( es_out_t
*p_out
, input_source_t
*in
,
497 int i_query
, va_list args
)
499 return p_out
->cbs
->control( p_out
, in
, i_query
, args
);
502 static inline int es_out_in_Control( es_out_t
*p_out
, input_source_t
*in
,
508 va_start( args
, i_query
);
509 i_result
= es_out_in_vaControl( p_out
, in
, i_query
, args
);
514 static int ControlLockedGetEmpty( es_out_t
*p_out
, input_source_t
*in
,
517 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
519 if( p_sys
->b_delayed
&& TsHasCmd( p_sys
->p_ts
) )
523 int ret
= es_out_in_Control( p_sys
->p_out
, in
, ES_OUT_GET_EMPTY
, pb_empty
);
524 assert( ret
== VLC_SUCCESS
);
529 static int ControlLockedGetWakeup( es_out_t
*p_out
, vlc_tick_t
*pi_wakeup
)
531 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
533 if( p_sys
->b_delayed
)
535 assert( !input_priv(p_sys
->p_input
)->b_can_pace_control
);
540 *pi_wakeup
= es_out_GetWakeup( p_sys
->p_out
);
545 static int ControlLockedGetBuffering( es_out_t
*p_out
, bool *pb_buffering
)
547 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
549 if( p_sys
->b_delayed
)
550 *pb_buffering
= true;
552 *pb_buffering
= es_out_GetBuffering( p_sys
->p_out
);
556 static int ControlLockedSetPauseState( es_out_t
*p_out
, bool b_source_paused
, bool b_paused
, vlc_tick_t i_date
)
558 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
561 if( !p_sys
->b_delayed
&& !b_source_paused
== !b_paused
)
563 i_ret
= es_out_SetPauseState( p_sys
->p_out
, b_source_paused
, b_paused
, i_date
);
567 i_ret
= VLC_EGENERIC
;
568 if( !input_priv(p_sys
->p_input
)->b_can_pace_control
)
570 if( !p_sys
->b_delayed
)
572 if( p_sys
->b_delayed
)
573 i_ret
= TsChangePause( p_sys
->p_ts
, b_source_paused
, b_paused
, i_date
);
577 /* XXX we may do it BUT it would be better to finish the clock clean up+improvements
578 * and so be able to advertize correctly pace control property in access
580 msg_Err( p_sys
->p_input
, "EsOutTimeshift does not work with streams that have pace control" );
586 p_sys
->b_input_paused_source
= b_source_paused
;
587 p_sys
->b_input_paused
= b_paused
;
591 static int ControlLockedSetRate( es_out_t
*p_out
, float src_rate
, float rate
)
593 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
596 if( !p_sys
->b_delayed
&& src_rate
== rate
)
598 i_ret
= es_out_SetRate( p_sys
->p_out
, src_rate
, rate
);
602 i_ret
= VLC_EGENERIC
;
603 if( !input_priv(p_sys
->p_input
)->b_can_pace_control
)
605 if( !p_sys
->b_delayed
)
607 if( p_sys
->b_delayed
)
608 i_ret
= TsChangeRate( p_sys
->p_ts
, src_rate
, rate
);
612 /* XXX we may do it BUT it would be better to finish the clock clean up+improvements
613 * and so be able to advertize correctly pace control property in access
615 msg_Err( p_sys
->p_input
, "EsOutTimeshift does not work with streams that have pace control" );
622 p_sys
->input_rate_source
= src_rate
;
623 p_sys
->input_rate
= rate
;
627 static int ControlLockedSetFrameNext( es_out_t
*p_out
)
629 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
631 return es_out_SetFrameNext( p_sys
->p_out
);
634 static int ControlLocked( es_out_t
*p_out
, input_source_t
*in
, int i_query
,
637 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
641 /* Pass-through control */
642 case ES_OUT_SET_GROUP
:
644 case ES_OUT_SET_GROUP_PCR
:
645 case ES_OUT_RESET_PCR
:
646 case ES_OUT_SET_NEXT_DISPLAY_TIME
:
647 case ES_OUT_SET_GROUP_META
:
648 case ES_OUT_SET_GROUP_EPG
:
649 case ES_OUT_SET_GROUP_EPG_EVENT
:
650 case ES_OUT_SET_EPG_TIME
:
651 case ES_OUT_SET_ES_SCRAMBLED_STATE
:
652 case ES_OUT_DEL_GROUP
:
653 case ES_OUT_SET_META
:
655 case ES_OUT_UNSET_ES
:
656 case ES_OUT_RESTART_ES
:
657 case ES_OUT_SET_ES_DEFAULT
:
658 case ES_OUT_SET_ES_STATE
:
659 case ES_OUT_SET_ES_CAT_POLICY
:
660 case ES_OUT_SET_ES_FMT
:
663 if( CmdInitControl( &cmd
, in
, i_query
, args
, p_sys
->b_delayed
) )
665 if( p_sys
->b_delayed
)
667 TsPushCmd( p_sys
->p_ts
, &cmd
);
670 return CmdExecuteControl( p_sys
->p_out
, &cmd
);
673 /* Special control when delayed */
674 case ES_OUT_GET_ES_STATE
:
676 es_out_id_t
*p_es
= va_arg( args
, es_out_id_t
* );
677 bool *pb_enabled
= va_arg( args
, bool* );
679 if( p_sys
->b_delayed
)
684 return es_out_in_Control( p_sys
->p_out
, in
, ES_OUT_GET_ES_STATE
,
685 p_es
->p_es
, pb_enabled
);
687 case ES_OUT_VOUT_SET_MOUSE_EVENT
:
689 es_out_id_t
*p_es
= va_arg( args
, es_out_id_t
* );
690 vlc_mouse_event cb
= va_arg( args
, vlc_mouse_event
);
691 void *user_data
= va_arg( args
, void * );
692 return es_out_in_Control( p_sys
->p_out
, in
, ES_OUT_VOUT_SET_MOUSE_EVENT
,
693 p_es
->p_es
, cb
, user_data
);
695 case ES_OUT_VOUT_ADD_OVERLAY
:
697 es_out_id_t
*p_es
= va_arg( args
, es_out_id_t
* );
698 subpicture_t
*sub
= va_arg( args
, subpicture_t
* );
699 size_t *channel
= va_arg( args
, size_t * );
700 return es_out_in_Control( p_sys
->p_out
, in
, ES_OUT_VOUT_ADD_OVERLAY
,
701 p_es
->p_es
, sub
, channel
);
703 case ES_OUT_VOUT_DEL_OVERLAY
:
705 es_out_id_t
*p_es
= va_arg( args
, es_out_id_t
* );
706 size_t channel
= va_arg( args
, size_t );
707 return es_out_in_Control( p_sys
->p_out
, in
, ES_OUT_VOUT_DEL_OVERLAY
,
708 p_es
->p_es
, channel
);
710 case ES_OUT_SPU_SET_HIGHLIGHT
:
712 es_out_id_t
*p_es
= va_arg( args
, es_out_id_t
* );
713 const vlc_spu_highlight_t
*p_hl
= va_arg( args
, const vlc_spu_highlight_t
* );
714 return es_out_in_Control( p_sys
->p_out
, in
, ES_OUT_SPU_SET_HIGHLIGHT
,
717 /* Special internal input control */
718 case ES_OUT_GET_EMPTY
:
720 bool *pb_empty
= va_arg( args
, bool* );
721 return ControlLockedGetEmpty( p_out
, in
, pb_empty
);
724 case ES_OUT_GET_PCR_SYSTEM
:
725 if( p_sys
->b_delayed
)
728 case ES_OUT_POST_SUBNODE
:
729 return es_out_in_vaControl( p_sys
->p_out
, in
, i_query
, args
);
731 case ES_OUT_MODIFY_PCR_SYSTEM
:
733 const bool b_absolute
= va_arg( args
, int );
734 const vlc_tick_t i_system
= va_arg( args
, vlc_tick_t
);
736 if( b_absolute
&& p_sys
->b_delayed
)
739 return es_out_in_Control( p_sys
->p_out
, in
, i_query
, b_absolute
,
744 vlc_assert_unreachable();
749 static int Control( es_out_t
*p_out
, input_source_t
*in
, int i_query
, va_list args
)
751 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
754 vlc_mutex_lock( &p_sys
->lock
);
758 i_ret
= ControlLocked( p_out
, in
, i_query
, args
);
760 vlc_mutex_unlock( &p_sys
->lock
);
765 static int PrivControlLocked( es_out_t
*p_out
, int i_query
, va_list args
)
767 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
771 /* Pass-through control */
772 case ES_OUT_PRIV_SET_MODE
:
773 case ES_OUT_PRIV_SET_TIMES
:
774 case ES_OUT_PRIV_SET_JITTER
:
775 case ES_OUT_PRIV_SET_EOS
:
778 if( CmdInitPrivControl( &cmd
, i_query
, args
, p_sys
->b_delayed
) )
780 if( p_sys
->b_delayed
)
782 TsPushCmd( p_sys
->p_ts
, &cmd
);
785 return CmdExecutePrivControl( p_sys
->p_out
, &cmd
);
787 case ES_OUT_PRIV_GET_WAKE_UP
: /* TODO ? */
789 vlc_tick_t
*pi_wakeup
= va_arg( args
, vlc_tick_t
* );
790 return ControlLockedGetWakeup( p_out
, pi_wakeup
);
792 case ES_OUT_PRIV_GET_BUFFERING
:
794 bool *pb_buffering
= va_arg( args
, bool* );
795 return ControlLockedGetBuffering( p_out
, pb_buffering
);
797 case ES_OUT_PRIV_SET_PAUSE_STATE
:
799 const bool b_source_paused
= (bool)va_arg( args
, int );
800 const bool b_paused
= (bool)va_arg( args
, int );
801 const vlc_tick_t i_date
= va_arg( args
, vlc_tick_t
);
803 return ControlLockedSetPauseState( p_out
, b_source_paused
, b_paused
, i_date
);
805 case ES_OUT_PRIV_SET_RATE
:
807 const float src_rate
= va_arg( args
, double );
808 const float rate
= va_arg( args
, double );
810 return ControlLockedSetRate( p_out
, src_rate
, rate
);
812 case ES_OUT_PRIV_SET_FRAME_NEXT
:
814 return ControlLockedSetFrameNext( p_out
);
816 case ES_OUT_PRIV_GET_GROUP_FORCED
:
817 return es_out_vaPrivControl( p_sys
->p_out
, i_query
, args
);
818 /* Invalid queries for this es_out level */
819 case ES_OUT_PRIV_SET_ES
:
820 case ES_OUT_PRIV_UNSET_ES
:
821 case ES_OUT_PRIV_RESTART_ES
:
822 case ES_OUT_PRIV_SET_ES_LIST
:
823 case ES_OUT_PRIV_SET_ES_BY_ID
:
824 case ES_OUT_PRIV_RESTART_ES_BY_ID
:
825 case ES_OUT_PRIV_SET_ES_DEFAULT_BY_ID
:
826 case ES_OUT_PRIV_STOP_ALL_ES
:
827 case ES_OUT_PRIV_START_ALL_ES
:
828 case ES_OUT_PRIV_SET_ES_DELAY
:
829 case ES_OUT_PRIV_SET_DELAY
:
830 case ES_OUT_PRIV_SET_RECORD_STATE
:
831 case ES_OUT_PRIV_SET_VBI_PAGE
:
832 case ES_OUT_PRIV_SET_VBI_TRANSPARENCY
:
833 default: vlc_assert_unreachable();
837 static int PrivControl( es_out_t
*p_out
, int i_query
, va_list args
)
839 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
842 vlc_mutex_lock( &p_sys
->lock
);
846 i_ret
= PrivControlLocked( p_out
, i_query
, args
);
848 vlc_mutex_unlock( &p_sys
->lock
);
853 static const struct es_out_callbacks es_out_timeshift_cbs
=
860 .priv_control
= PrivControl
,
863 /*****************************************************************************
865 *****************************************************************************/
866 static void TsDestroy( ts_thread_t
*p_ts
)
870 static int TsStart( es_out_t
*p_out
)
872 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
875 assert( !p_sys
->b_delayed
);
877 p_sys
->p_ts
= p_ts
= calloc(1, sizeof(*p_ts
));
881 p_ts
->i_tmp_size_max
= p_sys
->i_tmp_size_max
;
882 p_ts
->psz_tmp_path
= p_sys
->psz_tmp_path
;
883 p_ts
->p_input
= p_sys
->p_input
;
884 p_ts
->p_out
= p_sys
->p_out
;
885 vlc_mutex_init( &p_ts
->lock
);
886 vlc_cond_init( &p_ts
->wait
);
887 p_ts
->b_paused
= p_sys
->b_input_paused
&& !p_sys
->b_input_paused_source
;
888 p_ts
->i_pause_date
= p_ts
->b_paused
? vlc_tick_now() : -1;
889 p_ts
->rate_source
= p_sys
->input_rate_source
;
890 p_ts
->rate
= p_sys
->input_rate
;
891 p_ts
->i_rate_date
= -1;
892 p_ts
->i_rate_delay
= 0;
893 p_ts
->i_buffering_delay
= 0;
894 p_ts
->i_cmd_delay
= 0;
895 p_ts
->p_storage_r
= NULL
;
896 p_ts
->p_storage_w
= NULL
;
898 p_sys
->b_delayed
= true;
899 if( vlc_clone( &p_ts
->thread
, TsRun
, p_ts
, VLC_THREAD_PRIORITY_INPUT
) )
901 msg_Err( p_sys
->p_input
, "cannot create timeshift thread" );
905 p_sys
->b_delayed
= false;
911 static void TsAutoStop( es_out_t
*p_out
)
913 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
915 if( !p_sys
->b_delayed
|| !TsIsUnused( p_sys
->p_ts
) )
918 msg_Warn( p_sys
->p_input
, "es out timeshift: auto stop" );
919 TsStop( p_sys
->p_ts
);
921 p_sys
->b_delayed
= false;
923 static void TsStop( ts_thread_t
*p_ts
)
925 vlc_cancel( p_ts
->thread
);
926 vlc_join( p_ts
->thread
, NULL
);
928 vlc_mutex_lock( &p_ts
->lock
);
933 if( TsPopCmdLocked( p_ts
, &cmd
, true ) )
938 assert( !p_ts
->p_storage_r
|| !p_ts
->p_storage_r
->p_next
);
939 if( p_ts
->p_storage_r
)
940 TsStorageDelete( p_ts
->p_storage_r
);
941 vlc_mutex_unlock( &p_ts
->lock
);
945 static void TsPushCmd( ts_thread_t
*p_ts
, ts_cmd_t
*p_cmd
)
947 vlc_mutex_lock( &p_ts
->lock
);
949 if( !p_ts
->p_storage_w
|| TsStorageIsFull( p_ts
->p_storage_w
, p_cmd
) )
951 ts_storage_t
*p_storage
= TsStorageNew( p_ts
->psz_tmp_path
, p_ts
->i_tmp_size_max
);
956 vlc_mutex_unlock( &p_ts
->lock
);
957 /* TODO warn the user (but only once) */
961 if( !p_ts
->p_storage_w
)
963 p_ts
->p_storage_r
= p_ts
->p_storage_w
= p_storage
;
967 TsStoragePack( p_ts
->p_storage_w
);
968 p_ts
->p_storage_w
->p_next
= p_storage
;
969 p_ts
->p_storage_w
= p_storage
;
973 /* TODO return error and warn the user (but only once) */
974 TsStoragePushCmd( p_ts
->p_storage_w
, p_cmd
, p_ts
->p_storage_r
== p_ts
->p_storage_w
);
976 vlc_cond_signal( &p_ts
->wait
);
978 vlc_mutex_unlock( &p_ts
->lock
);
980 static int TsPopCmdLocked( ts_thread_t
*p_ts
, ts_cmd_t
*p_cmd
, bool b_flush
)
982 vlc_mutex_assert( &p_ts
->lock
);
984 if( TsStorageIsEmpty( p_ts
->p_storage_r
) )
987 TsStoragePopCmd( p_ts
->p_storage_r
, p_cmd
, b_flush
);
989 while( TsStorageIsEmpty( p_ts
->p_storage_r
) )
991 ts_storage_t
*p_next
= p_ts
->p_storage_r
->p_next
;
995 TsStorageDelete( p_ts
->p_storage_r
);
996 p_ts
->p_storage_r
= p_next
;
1001 static bool TsHasCmd( ts_thread_t
*p_ts
)
1005 vlc_mutex_lock( &p_ts
->lock
);
1006 b_cmd
= !TsStorageIsEmpty( p_ts
->p_storage_r
);
1007 vlc_mutex_unlock( &p_ts
->lock
);
1011 static bool TsIsUnused( ts_thread_t
*p_ts
)
1015 vlc_mutex_lock( &p_ts
->lock
);
1016 b_unused
= !p_ts
->b_paused
&&
1017 p_ts
->rate
== p_ts
->rate_source
&&
1018 TsStorageIsEmpty( p_ts
->p_storage_r
);
1019 vlc_mutex_unlock( &p_ts
->lock
);
1023 static int TsChangePause( ts_thread_t
*p_ts
, bool b_source_paused
, bool b_paused
, vlc_tick_t i_date
)
1025 vlc_mutex_lock( &p_ts
->lock
);
1030 assert( !b_source_paused
);
1031 i_ret
= es_out_SetPauseState( p_ts
->p_out
, true, true, i_date
);
1035 i_ret
= es_out_SetPauseState( p_ts
->p_out
, false, false, i_date
);
1042 assert( p_ts
->i_pause_date
> 0 );
1044 p_ts
->i_cmd_delay
+= i_date
- p_ts
->i_pause_date
;
1047 p_ts
->b_paused
= b_paused
;
1048 p_ts
->i_pause_date
= i_date
;
1050 vlc_cond_signal( &p_ts
->wait
);
1052 vlc_mutex_unlock( &p_ts
->lock
);
1055 static int TsChangeRate( ts_thread_t
*p_ts
, float src_rate
, float rate
)
1059 vlc_mutex_lock( &p_ts
->lock
);
1060 p_ts
->i_cmd_delay
+= p_ts
->i_rate_delay
;
1062 p_ts
->i_rate_date
= -1;
1063 p_ts
->i_rate_delay
= 0;
1065 p_ts
->rate_source
= src_rate
;
1067 i_ret
= es_out_SetRate( p_ts
->p_out
, rate
, rate
);
1068 vlc_mutex_unlock( &p_ts
->lock
);
1073 static void *TsRun( void *p_data
)
1075 ts_thread_t
*p_ts
= p_data
;
1076 vlc_tick_t i_buffering_date
= -1;
1081 vlc_tick_t i_deadline
;
1084 /* Pop a command to execute */
1085 vlc_mutex_lock( &p_ts
->lock
);
1086 mutex_cleanup_push( &p_ts
->lock
);
1090 const int canc
= vlc_savecancel();
1091 b_buffering
= es_out_GetBuffering( p_ts
->p_out
);
1093 if( ( !p_ts
->b_paused
|| b_buffering
) && !TsPopCmdLocked( p_ts
, &cmd
, false ) )
1095 vlc_restorecancel( canc
);
1098 vlc_restorecancel( canc
);
1100 vlc_cond_wait( &p_ts
->wait
, &p_ts
->lock
);
1103 if( b_buffering
&& i_buffering_date
< 0 )
1105 i_buffering_date
= cmd
.i_date
;
1107 else if( i_buffering_date
> 0 )
1109 p_ts
->i_buffering_delay
+= i_buffering_date
- cmd
.i_date
; /* It is < 0 */
1111 i_buffering_date
= cmd
.i_date
;
1113 i_buffering_date
= -1;
1116 if( p_ts
->i_rate_date
< 0 )
1117 p_ts
->i_rate_date
= cmd
.i_date
;
1119 p_ts
->i_rate_delay
= 0;
1120 if( p_ts
->rate_source
!= p_ts
->rate
)
1122 const vlc_tick_t i_duration
= cmd
.i_date
- p_ts
->i_rate_date
;
1123 p_ts
->i_rate_delay
= i_duration
* p_ts
->rate_source
/ p_ts
->rate
- i_duration
;
1125 if( p_ts
->i_cmd_delay
+ p_ts
->i_rate_delay
+ p_ts
->i_buffering_delay
< 0 && p_ts
->rate
!= p_ts
->rate_source
)
1127 const int canc
= vlc_savecancel();
1129 /* Auto reset to rate 1.0 */
1130 msg_Warn( p_ts
->p_input
, "es out timeshift: auto reset rate to %f", p_ts
->rate_source
);
1132 p_ts
->i_cmd_delay
= 0;
1133 p_ts
->i_buffering_delay
= 0;
1135 p_ts
->i_rate_delay
= 0;
1136 p_ts
->i_rate_date
= -1;
1137 p_ts
->rate
= p_ts
->rate_source
;
1139 if( !es_out_SetRate( p_ts
->p_out
, p_ts
->rate_source
, p_ts
->rate
) )
1141 vlc_value_t val
= { .f_float
= p_ts
->rate
};
1143 * FIXME it is perfectly safe BUT it is ugly as it may hide a
1144 * rate change requested by user */
1145 input_ControlPushHelper( p_ts
->p_input
, INPUT_CONTROL_SET_RATE
, &val
);
1148 vlc_restorecancel( canc
);
1150 i_deadline
= cmd
.i_date
+ p_ts
->i_cmd_delay
+ p_ts
->i_rate_delay
+ p_ts
->i_buffering_delay
;
1153 vlc_mutex_unlock( &p_ts
->lock
);
1155 /* Regulate the speed of command processing to the same one than
1157 vlc_cleanup_push( cmd_cleanup_routine
, &cmd
);
1159 vlc_tick_wait( i_deadline
);
1163 /* Execute the command */
1164 const int canc
= vlc_savecancel();
1165 switch( cmd
.i_type
)
1168 CmdExecuteAdd( p_ts
->p_out
, &cmd
);
1169 CmdCleanAdd( &cmd
);
1172 CmdExecuteSend( p_ts
->p_out
, &cmd
);
1173 CmdCleanSend( &cmd
);
1176 CmdExecuteControl( p_ts
->p_out
, &cmd
);
1177 CmdCleanControl( &cmd
);
1180 CmdExecuteDel( p_ts
->p_out
, &cmd
);
1183 vlc_assert_unreachable();
1186 vlc_restorecancel( canc
);
1192 /*****************************************************************************
1194 *****************************************************************************/
1195 static ts_storage_t
*TsStorageNew( const char *psz_tmp_path
, int64_t i_tmp_size_max
)
1197 ts_storage_t
*p_storage
= malloc( sizeof (*p_storage
) );
1198 if( unlikely(p_storage
== NULL
) )
1202 int fd
= GetTmpFile( &psz_file
, psz_tmp_path
);
1209 p_storage
->p_filew
= fdopen( fd
, "w+b" );
1210 if( p_storage
->p_filew
== NULL
)
1213 vlc_unlink( psz_file
);
1217 p_storage
->p_filer
= vlc_fopen( psz_file
, "rb" );
1218 if( p_storage
->p_filer
== NULL
)
1220 fclose( p_storage
->p_filew
);
1221 vlc_unlink( psz_file
);
1226 vlc_unlink( psz_file
);
1229 p_storage
->psz_file
= psz_file
;
1231 p_storage
->p_next
= NULL
;
1234 p_storage
->i_file_max
= i_tmp_size_max
;
1235 p_storage
->i_file_size
= 0;
1238 p_storage
->i_cmd_w
= 0;
1239 p_storage
->i_cmd_r
= 0;
1240 p_storage
->i_cmd_max
= 30000;
1241 p_storage
->p_cmd
= vlc_alloc( p_storage
->i_cmd_max
, sizeof(*p_storage
->p_cmd
) );
1242 //fprintf( stderr, "\nSTORAGE name=%s size=%d KiB\n", p_storage->psz_file, p_storage->i_cmd_max * sizeof(*p_storage->p_cmd) /1024 );
1244 if( !p_storage
->p_cmd
)
1246 TsStorageDelete( p_storage
);
1256 static void TsStorageDelete( ts_storage_t
*p_storage
)
1258 while( p_storage
->i_cmd_r
< p_storage
->i_cmd_w
)
1262 TsStoragePopCmd( p_storage
, &cmd
, true );
1266 free( p_storage
->p_cmd
);
1268 fclose( p_storage
->p_filer
);
1269 fclose( p_storage
->p_filew
);
1271 vlc_unlink( p_storage
->psz_file
);
1272 free( p_storage
->psz_file
);
1277 static void TsStoragePack( ts_storage_t
*p_storage
)
1279 /* Try to release a bit of memory */
1280 if( p_storage
->i_cmd_w
>= p_storage
->i_cmd_max
)
1283 p_storage
->i_cmd_max
= __MAX( p_storage
->i_cmd_w
, 1 );
1285 ts_cmd_t
*p_new
= realloc( p_storage
->p_cmd
, p_storage
->i_cmd_max
* sizeof(*p_storage
->p_cmd
) );
1287 p_storage
->p_cmd
= p_new
;
1289 static bool TsStorageIsFull( ts_storage_t
*p_storage
, const ts_cmd_t
*p_cmd
)
1291 if( p_cmd
&& p_cmd
->i_type
== C_SEND
&& p_storage
->i_cmd_w
> 0 )
1293 size_t i_size
= sizeof(*p_cmd
->u
.send
.p_block
) + p_cmd
->u
.send
.p_block
->i_buffer
;
1295 if( p_storage
->i_file_size
+ i_size
>= p_storage
->i_file_max
)
1298 return p_storage
->i_cmd_w
>= p_storage
->i_cmd_max
;
1300 static bool TsStorageIsEmpty( ts_storage_t
*p_storage
)
1302 return !p_storage
|| p_storage
->i_cmd_r
>= p_storage
->i_cmd_w
;
1304 static void TsStoragePushCmd( ts_storage_t
*p_storage
, const ts_cmd_t
*p_cmd
, bool b_flush
)
1306 ts_cmd_t cmd
= *p_cmd
;
1308 assert( !TsStorageIsFull( p_storage
, p_cmd
) );
1310 if( cmd
.i_type
== C_SEND
)
1312 block_t
*p_block
= cmd
.u
.send
.p_block
;
1314 cmd
.u
.send
.p_block
= NULL
;
1315 cmd
.u
.send
.i_offset
= ftell( p_storage
->p_filew
);
1317 if( fwrite( p_block
, sizeof(*p_block
), 1, p_storage
->p_filew
) != 1 )
1319 block_Release( p_block
);
1322 p_storage
->i_file_size
+= sizeof(*p_block
);
1323 if( p_block
->i_buffer
> 0 )
1325 if( fwrite( p_block
->p_buffer
, p_block
->i_buffer
, 1, p_storage
->p_filew
) != 1 )
1327 block_Release( p_block
);
1331 p_storage
->i_file_size
+= p_block
->i_buffer
;
1332 block_Release( p_block
);
1335 fflush( p_storage
->p_filew
);
1337 p_storage
->p_cmd
[p_storage
->i_cmd_w
++] = cmd
;
1339 static void TsStoragePopCmd( ts_storage_t
*p_storage
, ts_cmd_t
*p_cmd
, bool b_flush
)
1341 assert( !TsStorageIsEmpty( p_storage
) );
1343 *p_cmd
= p_storage
->p_cmd
[p_storage
->i_cmd_r
++];
1344 if( p_cmd
->i_type
== C_SEND
)
1349 !fseek( p_storage
->p_filer
, p_cmd
->u
.send
.i_offset
, SEEK_SET
) &&
1350 fread( &block
, sizeof(block
), 1, p_storage
->p_filer
) == 1 )
1352 block_t
*p_block
= block_Alloc( block
.i_buffer
);
1355 p_block
->i_dts
= block
.i_dts
;
1356 p_block
->i_pts
= block
.i_pts
;
1357 p_block
->i_flags
= block
.i_flags
;
1358 p_block
->i_length
= block
.i_length
;
1359 p_block
->i_nb_samples
= block
.i_nb_samples
;
1360 p_block
->i_buffer
= fread( p_block
->p_buffer
, 1, block
.i_buffer
, p_storage
->p_filer
);
1362 p_cmd
->u
.send
.p_block
= p_block
;
1366 //perror( "TsStoragePopCmd" );
1367 p_cmd
->u
.send
.p_block
= block_Alloc( 1 );
1372 /*****************************************************************************
1374 *****************************************************************************/
1375 static void CmdClean( ts_cmd_t
*p_cmd
)
1377 switch( p_cmd
->i_type
)
1380 CmdCleanAdd( p_cmd
);
1383 CmdCleanSend( p_cmd
);
1386 CmdCleanControl( p_cmd
);
1391 vlc_assert_unreachable();
1396 static int CmdInitAdd( ts_cmd_t
*p_cmd
, input_source_t
*in
, es_out_id_t
*p_es
,
1397 const es_format_t
*p_fmt
, bool b_copy
)
1399 p_cmd
->i_type
= C_ADD
;
1400 p_cmd
->i_date
= vlc_tick_now();
1401 p_cmd
->u
.add
.p_es
= p_es
;
1404 p_cmd
->u
.add
.p_fmt
= malloc( sizeof(*p_fmt
) );
1405 if( !p_cmd
->u
.add
.p_fmt
)
1406 return VLC_EGENERIC
;
1407 es_format_Copy( p_cmd
->u
.add
.p_fmt
, p_fmt
);
1408 p_cmd
->u
.add
.in
= in
? input_source_Hold( in
) : NULL
;
1412 p_cmd
->u
.add
.p_fmt
= (es_format_t
*)p_fmt
;
1413 p_cmd
->u
.add
.in
= in
;
1417 static void CmdExecuteAdd( es_out_t
*p_out
, ts_cmd_t
*p_cmd
)
1419 p_cmd
->u
.add
.p_es
->p_es
= p_out
->cbs
->add( p_out
, p_cmd
->u
.add
.in
,
1420 p_cmd
->u
.add
.p_fmt
);
1422 static void CmdCleanAdd( ts_cmd_t
*p_cmd
)
1424 es_format_Clean( p_cmd
->u
.add
.p_fmt
);
1425 if( p_cmd
->u
.add
.in
)
1426 input_source_Release( p_cmd
->u
.add
.in
);
1427 free( p_cmd
->u
.add
.p_fmt
);
1430 static void CmdInitSend( ts_cmd_t
*p_cmd
, es_out_id_t
*p_es
, block_t
*p_block
)
1432 p_cmd
->i_type
= C_SEND
;
1433 p_cmd
->i_date
= vlc_tick_now();
1434 p_cmd
->u
.send
.p_es
= p_es
;
1435 p_cmd
->u
.send
.p_block
= p_block
;
1437 static int CmdExecuteSend( es_out_t
*p_out
, ts_cmd_t
*p_cmd
)
1439 block_t
*p_block
= p_cmd
->u
.send
.p_block
;
1441 p_cmd
->u
.send
.p_block
= NULL
;
1445 if( p_cmd
->u
.send
.p_es
->p_es
)
1446 return es_out_Send( p_out
, p_cmd
->u
.send
.p_es
->p_es
, p_block
);
1447 block_Release( p_block
);
1449 return VLC_EGENERIC
;
1451 static void CmdCleanSend( ts_cmd_t
*p_cmd
)
1453 if( p_cmd
->u
.send
.p_block
)
1454 block_Release( p_cmd
->u
.send
.p_block
);
1457 static int CmdInitDel( ts_cmd_t
*p_cmd
, es_out_id_t
*p_es
)
1459 p_cmd
->i_type
= C_DEL
;
1460 p_cmd
->i_date
= vlc_tick_now();
1461 p_cmd
->u
.del
.p_es
= p_es
;
1464 static void CmdExecuteDel( es_out_t
*p_out
, ts_cmd_t
*p_cmd
)
1466 if( p_cmd
->u
.del
.p_es
->p_es
)
1467 es_out_Del( p_out
, p_cmd
->u
.del
.p_es
->p_es
);
1468 free( p_cmd
->u
.del
.p_es
);
1471 static int CmdInitControl( ts_cmd_t
*p_cmd
, input_source_t
*in
,
1472 int i_query
, va_list args
, bool b_copy
)
1474 p_cmd
->i_type
= C_CONTROL
;
1475 p_cmd
->i_date
= vlc_tick_now();
1476 p_cmd
->u
.control
.i_query
= i_query
;
1479 p_cmd
->u
.control
.in
= in
? input_source_Hold( in
) : NULL
;
1481 p_cmd
->u
.control
.in
= in
;
1485 /* Pass-through control */
1486 case ES_OUT_SET_GROUP
: /* arg1= int */
1487 case ES_OUT_DEL_GROUP
: /* arg1=int i_group */
1488 p_cmd
->u
.control
.u
.i_int
= va_arg( args
, int );
1491 case ES_OUT_SET_PCR
: /* arg1=vlc_tick_t i_pcr(microsecond!) (using default group 0)*/
1492 case ES_OUT_SET_NEXT_DISPLAY_TIME
: /* arg1=int64_t i_pts(microsecond) */
1493 p_cmd
->u
.control
.u
.i_i64
= va_arg( args
, int64_t );
1496 case ES_OUT_SET_GROUP_PCR
: /* arg1= int i_group, arg2=vlc_tick_t i_pcr(microsecond!)*/
1497 p_cmd
->u
.control
.u
.int_i64
.i_int
= va_arg( args
, int );
1498 p_cmd
->u
.control
.u
.int_i64
.i_i64
= va_arg( args
, vlc_tick_t
);
1501 case ES_OUT_SET_ES_SCRAMBLED_STATE
:
1502 p_cmd
->u
.control
.u
.es_bool
.p_es
= va_arg( args
, es_out_id_t
* );
1503 p_cmd
->u
.control
.u
.es_bool
.b_bool
= (bool)va_arg( args
, int );
1506 case ES_OUT_RESET_PCR
: /* no arg */
1509 case ES_OUT_SET_META
: /* arg1=const vlc_meta_t* */
1510 case ES_OUT_SET_GROUP_META
: /* arg1=int i_group arg2=const vlc_meta_t* */
1512 if( i_query
== ES_OUT_SET_GROUP_META
)
1513 p_cmd
->u
.control
.u
.int_meta
.i_int
= va_arg( args
, int );
1514 const vlc_meta_t
*p_meta
= va_arg( args
, const vlc_meta_t
* );
1518 p_cmd
->u
.control
.u
.int_meta
.p_meta
= vlc_meta_New();
1519 if( !p_cmd
->u
.control
.u
.int_meta
.p_meta
)
1520 return VLC_EGENERIC
;
1521 vlc_meta_Merge( p_cmd
->u
.control
.u
.int_meta
.p_meta
, p_meta
);
1525 /* The cast is only needed to avoid warning */
1526 p_cmd
->u
.control
.u
.int_meta
.p_meta
= (vlc_meta_t
*)p_meta
;
1531 case ES_OUT_SET_GROUP_EPG
: /* arg1=int i_group arg2=const vlc_epg_t* */
1533 p_cmd
->u
.control
.u
.int_epg
.i_int
= va_arg( args
, int );
1534 const vlc_epg_t
*p_epg
= va_arg( args
, const vlc_epg_t
* );
1538 p_cmd
->u
.control
.u
.int_epg
.p_epg
= vlc_epg_Duplicate( p_epg
);
1539 if( !p_cmd
->u
.control
.u
.int_epg
.p_epg
)
1540 return VLC_EGENERIC
;
1544 /* The cast is only needed to avoid warning */
1545 p_cmd
->u
.control
.u
.int_epg
.p_epg
= (vlc_epg_t
*)p_epg
;
1549 case ES_OUT_SET_GROUP_EPG_EVENT
: /* arg1=int i_group arg2=const vlc_epg_event_t* */
1551 p_cmd
->u
.control
.u
.int_epg_evt
.i_int
= va_arg( args
, int );
1552 const vlc_epg_event_t
*p_evt
= va_arg( args
, const vlc_epg_event_t
* );
1556 p_cmd
->u
.control
.u
.int_epg_evt
.p_evt
= vlc_epg_event_Duplicate( p_evt
);
1557 if( !p_cmd
->u
.control
.u
.int_epg_evt
.p_evt
)
1558 return VLC_EGENERIC
;
1562 /* The cast is only needed to avoid warning */
1563 p_cmd
->u
.control
.u
.int_epg_evt
.p_evt
= (vlc_epg_event_t
*)p_evt
;
1567 case ES_OUT_SET_EPG_TIME
: /* arg1=int64_t (seconds) */
1568 p_cmd
->u
.control
.u
.i_i64
= va_arg( args
, int64_t );
1571 /* Modified control */
1572 case ES_OUT_SET_ES
: /* arg1= es_out_id_t* */
1573 case ES_OUT_UNSET_ES
: /* arg1= es_out_id_t* */
1574 case ES_OUT_RESTART_ES
: /* arg1= es_out_id_t* */
1575 case ES_OUT_SET_ES_DEFAULT
: /* arg1= es_out_id_t* */
1576 p_cmd
->u
.control
.u
.p_es
= va_arg( args
, es_out_id_t
* );
1579 case ES_OUT_SET_ES_CAT_POLICY
:
1580 p_cmd
->u
.control
.u
.es_policy
.i_cat
= va_arg( args
, int );
1581 p_cmd
->u
.control
.u
.es_policy
.i_policy
= va_arg( args
, int );
1584 case ES_OUT_SET_ES_STATE
:/* arg1= es_out_id_t* arg2=bool */
1585 p_cmd
->u
.control
.u
.es_bool
.p_es
= va_arg( args
, es_out_id_t
* );
1586 p_cmd
->u
.control
.u
.es_bool
.b_bool
= (bool)va_arg( args
, int );
1589 case ES_OUT_SET_ES_FMT
: /* arg1= es_out_id_t* arg2=es_format_t* */
1591 p_cmd
->u
.control
.u
.es_fmt
.p_es
= va_arg( args
, es_out_id_t
* );
1592 es_format_t
*p_fmt
= va_arg( args
, es_format_t
* );
1596 p_cmd
->u
.control
.u
.es_fmt
.p_fmt
= malloc( sizeof(*p_fmt
) );
1597 if( !p_cmd
->u
.control
.u
.es_fmt
.p_fmt
)
1598 return VLC_EGENERIC
;
1599 es_format_Copy( p_cmd
->u
.control
.u
.es_fmt
.p_fmt
, p_fmt
);
1603 p_cmd
->u
.control
.u
.es_fmt
.p_fmt
= p_fmt
;
1608 vlc_assert_unreachable();
1609 return VLC_EGENERIC
;
1614 static int CmdExecuteControl( es_out_t
*p_out
, ts_cmd_t
*p_cmd
)
1616 const int i_query
= p_cmd
->u
.control
.i_query
;
1617 input_source_t
*in
= p_cmd
->u
.control
.in
;
1621 /* Pass-through control */
1622 case ES_OUT_SET_GROUP
: /* arg1= int */
1623 case ES_OUT_DEL_GROUP
: /* arg1=int i_group */
1624 return es_out_in_Control( p_out
, in
, i_query
, p_cmd
->u
.control
.u
.i_int
);
1626 case ES_OUT_SET_PCR
: /* arg1=vlc_tick_t i_pcr(microsecond!) (using default group 0)*/
1627 case ES_OUT_SET_NEXT_DISPLAY_TIME
: /* arg1=int64_t i_pts(microsecond) */
1628 return es_out_in_Control( p_out
, in
, i_query
, p_cmd
->u
.control
.u
.i_i64
);
1630 case ES_OUT_SET_GROUP_PCR
: /* arg1= int i_group, arg2=vlc_tick_t i_pcr(microsecond!)*/
1631 return es_out_in_Control( p_out
, in
, i_query
, p_cmd
->u
.control
.u
.int_i64
.i_int
,
1632 p_cmd
->u
.control
.u
.int_i64
.i_i64
);
1634 case ES_OUT_RESET_PCR
: /* no arg */
1635 return es_out_in_Control( p_out
, in
, i_query
);
1637 case ES_OUT_SET_GROUP_META
: /* arg1=int i_group arg2=const vlc_meta_t* */
1638 return es_out_in_Control( p_out
, in
, i_query
, p_cmd
->u
.control
.u
.int_meta
.i_int
,
1639 p_cmd
->u
.control
.u
.int_meta
.p_meta
);
1641 case ES_OUT_SET_GROUP_EPG
: /* arg1=int i_group arg2=const vlc_epg_t* */
1642 return es_out_in_Control( p_out
, in
, i_query
, p_cmd
->u
.control
.u
.int_epg
.i_int
,
1643 p_cmd
->u
.control
.u
.int_epg
.p_epg
);
1645 case ES_OUT_SET_GROUP_EPG_EVENT
: /* arg1=int i_group arg2=const vlc_epg_event_t* */
1646 return es_out_in_Control( p_out
, in
, i_query
, p_cmd
->u
.control
.u
.int_epg_evt
.i_int
,
1647 p_cmd
->u
.control
.u
.int_epg_evt
.p_evt
);
1649 case ES_OUT_SET_EPG_TIME
: /* arg1=int64_t */
1650 return es_out_in_Control( p_out
, in
, i_query
, p_cmd
->u
.control
.u
.i_i64
);
1652 case ES_OUT_SET_ES_SCRAMBLED_STATE
: /* arg1=int es_out_id_t* arg2=bool */
1653 return es_out_in_Control( p_out
, in
, i_query
, p_cmd
->u
.control
.u
.es_bool
.p_es
->p_es
,
1654 p_cmd
->u
.control
.u
.es_bool
.b_bool
);
1656 case ES_OUT_SET_META
: /* arg1=const vlc_meta_t* */
1657 return es_out_in_Control( p_out
, in
, i_query
, p_cmd
->u
.control
.u
.int_meta
.p_meta
);
1659 /* Modified control */
1660 case ES_OUT_SET_ES
: /* arg1= es_out_id_t* */
1661 case ES_OUT_UNSET_ES
: /* arg1= es_out_id_t* */
1662 case ES_OUT_RESTART_ES
: /* arg1= es_out_id_t* */
1663 case ES_OUT_SET_ES_DEFAULT
: /* arg1= es_out_id_t* */
1664 return es_out_in_Control( p_out
, in
, i_query
, !p_cmd
->u
.control
.u
.p_es
? NULL
:
1665 p_cmd
->u
.control
.u
.p_es
->p_es
);
1667 case ES_OUT_SET_ES_STATE
:/* arg1= es_out_id_t* arg2=bool */
1668 return es_out_in_Control( p_out
, in
, i_query
, p_cmd
->u
.control
.u
.es_bool
.p_es
->p_es
,
1669 p_cmd
->u
.control
.u
.es_bool
.b_bool
);
1671 case ES_OUT_SET_ES_CAT_POLICY
:
1672 return es_out_in_Control( p_out
, in
, i_query
, p_cmd
->u
.control
.u
.es_policy
.i_cat
,
1673 p_cmd
->u
.control
.u
.es_policy
.i_policy
);
1675 case ES_OUT_SET_ES_FMT
: /* arg1= es_out_id_t* arg2=es_format_t* */
1676 return es_out_in_Control( p_out
, in
, i_query
, p_cmd
->u
.control
.u
.es_fmt
.p_es
->p_es
,
1677 p_cmd
->u
.control
.u
.es_fmt
.p_fmt
);
1680 vlc_assert_unreachable();
1681 return VLC_EGENERIC
;
1684 static void CmdCleanControl( ts_cmd_t
*p_cmd
)
1686 if( p_cmd
->u
.control
.in
)
1687 input_source_Release( p_cmd
->u
.control
.in
);
1689 switch( p_cmd
->u
.control
.i_query
)
1691 case ES_OUT_SET_GROUP_META
:
1692 case ES_OUT_SET_META
:
1693 if( p_cmd
->u
.control
.u
.int_meta
.p_meta
)
1694 vlc_meta_Delete( p_cmd
->u
.control
.u
.int_meta
.p_meta
);
1696 case ES_OUT_SET_GROUP_EPG
:
1697 if( p_cmd
->u
.control
.u
.int_epg
.p_epg
)
1698 vlc_epg_Delete( p_cmd
->u
.control
.u
.int_epg
.p_epg
);
1700 case ES_OUT_SET_GROUP_EPG_EVENT
:
1701 if( p_cmd
->u
.control
.u
.int_epg_evt
.p_evt
)
1702 vlc_epg_event_Delete( p_cmd
->u
.control
.u
.int_epg_evt
.p_evt
);
1704 case ES_OUT_SET_ES_FMT
:
1705 if( p_cmd
->u
.control
.u
.es_fmt
.p_fmt
)
1707 es_format_Clean( p_cmd
->u
.control
.u
.es_fmt
.p_fmt
);
1708 free( p_cmd
->u
.control
.u
.es_fmt
.p_fmt
);
1714 static int CmdInitPrivControl( ts_cmd_t
*p_cmd
, int i_query
, va_list args
, bool b_copy
)
1716 VLC_UNUSED( b_copy
);
1717 p_cmd
->i_type
= C_CONTROL
;
1718 p_cmd
->i_date
= vlc_tick_now();
1719 p_cmd
->u
.privcontrol
.i_query
= i_query
;
1723 /* Pass-through control */
1724 case ES_OUT_PRIV_SET_MODE
: /* arg1= int */
1725 p_cmd
->u
.privcontrol
.u
.i_int
= va_arg( args
, int );
1727 case ES_OUT_PRIV_SET_JITTER
:
1729 vlc_tick_t i_pts_delay
= va_arg( args
, vlc_tick_t
);
1730 vlc_tick_t i_pts_jitter
= va_arg( args
, vlc_tick_t
);
1731 int i_cr_average
= va_arg( args
, int );
1733 p_cmd
->u
.privcontrol
.u
.jitter
.i_pts_delay
= i_pts_delay
;
1734 p_cmd
->u
.privcontrol
.u
.jitter
.i_pts_jitter
= i_pts_jitter
;
1735 p_cmd
->u
.privcontrol
.u
.jitter
.i_cr_average
= i_cr_average
;
1738 case ES_OUT_PRIV_SET_TIMES
:
1740 double f_position
= va_arg( args
, double );
1741 vlc_tick_t i_time
= va_arg( args
, vlc_tick_t
);
1742 vlc_tick_t i_normal_time
= va_arg( args
, vlc_tick_t
);
1743 vlc_tick_t i_length
= va_arg( args
, vlc_tick_t
);
1745 p_cmd
->u
.privcontrol
.u
.times
.f_position
= f_position
;
1746 p_cmd
->u
.privcontrol
.u
.times
.i_time
= i_time
;
1747 p_cmd
->u
.privcontrol
.u
.times
.i_normal_time
= i_normal_time
;
1748 p_cmd
->u
.privcontrol
.u
.times
.i_length
= i_length
;
1751 case ES_OUT_PRIV_SET_EOS
: /* no arg */
1753 default: vlc_assert_unreachable();
1759 static int CmdExecutePrivControl( es_out_t
*p_out
, ts_cmd_t
*p_cmd
)
1761 const int i_query
= p_cmd
->u
.privcontrol
.i_query
;
1765 /* Pass-through control */
1766 case ES_OUT_PRIV_SET_MODE
: /* arg1= int */
1767 return es_out_PrivControl( p_out
, i_query
, p_cmd
->u
.privcontrol
.u
.i_int
);
1768 case ES_OUT_PRIV_SET_JITTER
:
1769 return es_out_PrivControl( p_out
, i_query
, p_cmd
->u
.privcontrol
.u
.jitter
.i_pts_delay
,
1770 p_cmd
->u
.privcontrol
.u
.jitter
.i_pts_jitter
,
1771 p_cmd
->u
.privcontrol
.u
.jitter
.i_cr_average
);
1772 case ES_OUT_PRIV_SET_TIMES
:
1773 return es_out_PrivControl( p_out
, i_query
,
1774 p_cmd
->u
.privcontrol
.u
.times
.f_position
,
1775 p_cmd
->u
.privcontrol
.u
.times
.i_time
,
1776 p_cmd
->u
.privcontrol
.u
.times
.i_normal_time
,
1777 p_cmd
->u
.privcontrol
.u
.times
.i_length
);
1778 case ES_OUT_PRIV_SET_EOS
: /* no arg */
1779 return es_out_PrivControl( p_out
, i_query
);
1780 default: vlc_assert_unreachable();
1784 static int GetTmpFile( char **filename
, const char *dirname
)
1787 && asprintf( filename
, "%s"DIR_SEP PACKAGE_NAME
"-timeshift.XXXXXX",
1790 vlc_mkdir( dirname
, 0700 );
1792 int fd
= vlc_mkstemp( *filename
);
1799 *filename
= strdup( DIR_SEP
"tmp"DIR_SEP PACKAGE_NAME
"-timeshift.XXXXXX" );
1800 if( unlikely(*filename
== NULL
) )
1803 int fd
= vlc_mkstemp( *filename
);