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_input.h>
47 #include <vlc_es_out.h>
48 #include <vlc_block.h>
49 #include "input_internal.h"
52 /*****************************************************************************
54 *****************************************************************************/
56 /* XXX attribute_packed is (and MUST be) used ONLY to reduce memory usage */
57 #ifdef HAVE_ATTRIBUTE_PACKED
58 # define attribute_packed __attribute__((__packed__))
60 # define attribute_packed
71 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
117 vlc_epg_event_t
*p_evt
;
136 /* FIXME Really too big (double make the whole thing too big) */
143 vlc_tick_t i_pts_delay
;
144 vlc_tick_t i_pts_jitter
;
150 typedef struct attribute_packed
159 ts_cmd_control_t control
;
163 typedef struct ts_storage_t ts_storage_t
;
166 ts_storage_t
*p_next
;
170 char *psz_file
; /* Filename */
172 size_t i_file_max
; /* Max size in bytes */
173 int64_t i_file_size
;/* Current size in bytes */
174 FILE *p_filew
; /* FILE handle for data writing */
175 FILE *p_filer
; /* FILE handle for data reading */
187 input_thread_t
*p_input
;
189 int64_t i_tmp_size_max
;
190 const char *psz_tmp_path
;
192 /* Lock for all following fields */
198 vlc_tick_t i_pause_date
;
203 vlc_tick_t i_rate_date
;
204 vlc_tick_t i_rate_delay
;
207 vlc_tick_t i_buffering_delay
;
210 ts_storage_t
*p_storage_r
;
211 ts_storage_t
*p_storage_w
;
213 vlc_tick_t i_cmd_delay
;
224 input_thread_t
*p_input
;
228 int64_t i_tmp_size_max
; /* Maximal temporary file size in byte */
229 char *psz_tmp_path
; /* Path for temporary files */
231 /* Lock for all following fields */
240 bool b_input_paused_source
;
242 float input_rate_source
;
251 static void Del ( es_out_t
*, es_out_id_t
* );
253 static int TsStart( es_out_t
* );
254 static void TsAutoStop( es_out_t
* );
256 static void TsStop( ts_thread_t
* );
257 static void TsPushCmd( ts_thread_t
*, ts_cmd_t
* );
258 static int TsPopCmdLocked( ts_thread_t
*, ts_cmd_t
*, bool b_flush
);
259 static bool TsHasCmd( ts_thread_t
* );
260 static bool TsIsUnused( ts_thread_t
* );
261 static int TsChangePause( ts_thread_t
*, bool b_source_paused
, bool b_paused
, vlc_tick_t i_date
);
262 static int TsChangeRate( ts_thread_t
*, float src_rate
, float rate
);
264 static void *TsRun( void * );
266 static ts_storage_t
*TsStorageNew( const char *psz_path
, int64_t i_tmp_size_max
);
267 static void TsStorageDelete( ts_storage_t
* );
268 static void TsStoragePack( ts_storage_t
*p_storage
);
269 static bool TsStorageIsFull( ts_storage_t
*, const ts_cmd_t
*p_cmd
);
270 static bool TsStorageIsEmpty( ts_storage_t
* );
271 static void TsStoragePushCmd( ts_storage_t
*, const ts_cmd_t
*p_cmd
, bool b_flush
);
272 static void TsStoragePopCmd( ts_storage_t
*p_storage
, ts_cmd_t
*p_cmd
, bool b_flush
);
274 static void CmdClean( ts_cmd_t
* );
275 static void cmd_cleanup_routine( void *p
) { CmdClean( p
); }
277 static int CmdInitAdd ( ts_cmd_t
*, es_out_id_t
*, const es_format_t
*, bool b_copy
);
278 static void CmdInitSend ( ts_cmd_t
*, es_out_id_t
*, block_t
* );
279 static int CmdInitDel ( ts_cmd_t
*, es_out_id_t
* );
280 static int CmdInitControl( ts_cmd_t
*, int i_query
, va_list, bool b_copy
);
283 static void CmdCleanAdd ( ts_cmd_t
* );
284 static void CmdCleanSend ( ts_cmd_t
* );
285 static void CmdCleanControl( ts_cmd_t
*p_cmd
);
287 /* XXX these functions will take the destination es_out_t */
288 static void CmdExecuteAdd ( es_out_t
*, ts_cmd_t
* );
289 static int CmdExecuteSend ( es_out_t
*, ts_cmd_t
* );
290 static void CmdExecuteDel ( es_out_t
*, ts_cmd_t
* );
291 static int CmdExecuteControl( es_out_t
*, ts_cmd_t
* );
294 static int GetTmpFile( char **ppsz_file
, const char *psz_path
);
296 static const struct es_out_callbacks es_out_timeshift_cbs
;
298 /*****************************************************************************
299 * input_EsOutTimeshiftNew:
300 *****************************************************************************/
301 es_out_t
*input_EsOutTimeshiftNew( input_thread_t
*p_input
, es_out_t
*p_next_out
, float rate
)
303 es_out_sys_t
*p_sys
= malloc( sizeof(*p_sys
) );
307 p_sys
->out
.cbs
= &es_out_timeshift_cbs
;
310 p_sys
->b_input_paused
= false;
311 p_sys
->b_input_paused_source
= false;
312 p_sys
->p_input
= p_input
;
313 p_sys
->input_rate
= rate
;
314 p_sys
->input_rate_source
= rate
;
316 p_sys
->p_out
= p_next_out
;
317 vlc_mutex_init_recursive( &p_sys
->lock
);
319 p_sys
->b_delayed
= false;
322 TAB_INIT( p_sys
->i_es
, p_sys
->pp_es
);
325 const int i_tmp_size_max
= var_CreateGetInteger( p_input
, "input-timeshift-granularity" );
326 if( i_tmp_size_max
< 0 )
327 p_sys
->i_tmp_size_max
= 50*1024*1024;
329 p_sys
->i_tmp_size_max
= __MAX( i_tmp_size_max
, 1*1024*1024 );
330 msg_Dbg( p_input
, "using timeshift granularity of %d MiB",
331 (int)p_sys
->i_tmp_size_max
/(1024*1024) );
333 p_sys
->psz_tmp_path
= var_InheritString( p_input
, "input-timeshift-path" );
334 #if defined (_WIN32) && !VLC_WINSTORE_APP
335 if( p_sys
->psz_tmp_path
== NULL
)
337 const DWORD count
= GetTempPath( 0, NULL
);
340 TCHAR
*path
= vlc_alloc( count
+ 1, sizeof(TCHAR
) );
343 DWORD ret
= GetTempPath( count
+ 1, path
);
344 if( ret
!= 0 && ret
<= count
)
345 p_sys
->psz_tmp_path
= FromT( path
);
350 if( p_sys
->psz_tmp_path
== NULL
)
352 wchar_t *wpath
= _wgetcwd( NULL
, 0 );
355 p_sys
->psz_tmp_path
= FromWide( wpath
);
359 if( p_sys
->psz_tmp_path
== NULL
)
360 p_sys
->psz_tmp_path
= strdup( "C:" );
362 if( p_sys
->psz_tmp_path
!= NULL
)
364 size_t len
= strlen( p_sys
->psz_tmp_path
);
366 while( len
> 0 && p_sys
->psz_tmp_path
[len
- 1] == DIR_SEP_CHAR
)
369 p_sys
->psz_tmp_path
[len
] = '\0';
372 if( p_sys
->psz_tmp_path
!= NULL
)
373 msg_Dbg( p_input
, "using timeshift path: %s", p_sys
->psz_tmp_path
);
375 msg_Dbg( p_input
, "using default timeshift path" );
378 #define S(t) msg_Err( p_input, "SIZEOF("#t")=%d", sizeof(t) )
390 /*****************************************************************************
392 *****************************************************************************/
393 static void Destroy( es_out_t
*p_out
)
395 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
397 if( p_sys
->b_delayed
)
399 TsStop( p_sys
->p_ts
);
400 p_sys
->b_delayed
= false;
403 while( p_sys
->i_es
> 0 )
404 Del( p_out
, p_sys
->pp_es
[0] );
405 TAB_CLEAN( p_sys
->i_es
, p_sys
->pp_es
);
407 free( p_sys
->psz_tmp_path
);
408 vlc_mutex_destroy( &p_sys
->lock
);
412 static es_out_id_t
*Add( es_out_t
*p_out
, const es_format_t
*p_fmt
)
414 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
417 es_out_id_t
*p_es
= malloc( sizeof( *p_es
) );
421 vlc_mutex_lock( &p_sys
->lock
);
425 if( CmdInitAdd( &cmd
, p_es
, p_fmt
, p_sys
->b_delayed
) )
427 vlc_mutex_unlock( &p_sys
->lock
);
432 TAB_APPEND( p_sys
->i_es
, p_sys
->pp_es
, p_es
);
434 if( p_sys
->b_delayed
)
435 TsPushCmd( p_sys
->p_ts
, &cmd
);
437 CmdExecuteAdd( p_sys
->p_out
, &cmd
);
439 vlc_mutex_unlock( &p_sys
->lock
);
443 static int Send( es_out_t
*p_out
, es_out_id_t
*p_es
, block_t
*p_block
)
445 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
447 int i_ret
= VLC_SUCCESS
;
449 vlc_mutex_lock( &p_sys
->lock
);
453 CmdInitSend( &cmd
, p_es
, p_block
);
454 if( p_sys
->b_delayed
)
455 TsPushCmd( p_sys
->p_ts
, &cmd
);
457 i_ret
= CmdExecuteSend( p_sys
->p_out
, &cmd
) ;
459 vlc_mutex_unlock( &p_sys
->lock
);
463 static void Del( es_out_t
*p_out
, es_out_id_t
*p_es
)
465 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
468 vlc_mutex_lock( &p_sys
->lock
);
472 CmdInitDel( &cmd
, p_es
);
473 if( p_sys
->b_delayed
)
474 TsPushCmd( p_sys
->p_ts
, &cmd
);
476 CmdExecuteDel( p_sys
->p_out
, &cmd
);
478 TAB_REMOVE( p_sys
->i_es
, p_sys
->pp_es
, p_es
);
480 vlc_mutex_unlock( &p_sys
->lock
);
483 static int ControlLockedGetEmpty( es_out_t
*p_out
, bool *pb_empty
)
485 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
487 if( p_sys
->b_delayed
&& TsHasCmd( p_sys
->p_ts
) )
490 *pb_empty
= es_out_GetEmpty( p_sys
->p_out
);
494 static int ControlLockedGetWakeup( es_out_t
*p_out
, vlc_tick_t
*pi_wakeup
)
496 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
498 if( p_sys
->b_delayed
)
500 assert( !input_priv(p_sys
->p_input
)->b_can_pace_control
);
505 *pi_wakeup
= es_out_GetWakeup( p_sys
->p_out
);
510 static int ControlLockedGetBuffering( es_out_t
*p_out
, bool *pb_buffering
)
512 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
514 if( p_sys
->b_delayed
)
515 *pb_buffering
= true;
517 *pb_buffering
= es_out_GetBuffering( p_sys
->p_out
);
521 static int ControlLockedSetPauseState( es_out_t
*p_out
, bool b_source_paused
, bool b_paused
, vlc_tick_t i_date
)
523 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
526 if( !p_sys
->b_delayed
&& !b_source_paused
== !b_paused
)
528 i_ret
= es_out_SetPauseState( p_sys
->p_out
, b_source_paused
, b_paused
, i_date
);
532 i_ret
= VLC_EGENERIC
;
533 if( !input_priv(p_sys
->p_input
)->b_can_pace_control
)
535 if( !p_sys
->b_delayed
)
537 if( p_sys
->b_delayed
)
538 i_ret
= TsChangePause( p_sys
->p_ts
, b_source_paused
, b_paused
, i_date
);
542 /* XXX we may do it BUT it would be better to finish the clock clean up+improvements
543 * and so be able to advertize correctly pace control property in access
545 msg_Err( p_sys
->p_input
, "EsOutTimeshift does not work with streams that have pace control" );
551 p_sys
->b_input_paused_source
= b_source_paused
;
552 p_sys
->b_input_paused
= b_paused
;
556 static int ControlLockedSetRate( es_out_t
*p_out
, float src_rate
, float rate
)
558 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
561 if( !p_sys
->b_delayed
&& src_rate
== rate
)
563 i_ret
= es_out_SetRate( p_sys
->p_out
, src_rate
, rate
);
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
= TsChangeRate( p_sys
->p_ts
, src_rate
, rate
);
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" );
587 p_sys
->input_rate_source
= src_rate
;
588 p_sys
->input_rate
= rate
;
592 static int ControlLockedSetFrameNext( es_out_t
*p_out
)
594 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
596 return es_out_SetFrameNext( p_sys
->p_out
);
599 static int ControlLocked( es_out_t
*p_out
, int i_query
, va_list args
)
601 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
605 /* Pass-through control */
606 case ES_OUT_SET_MODE
:
607 case ES_OUT_SET_GROUP
:
609 case ES_OUT_SET_GROUP_PCR
:
610 case ES_OUT_RESET_PCR
:
611 case ES_OUT_SET_NEXT_DISPLAY_TIME
:
612 case ES_OUT_SET_GROUP_META
:
613 case ES_OUT_SET_GROUP_EPG
:
614 case ES_OUT_SET_GROUP_EPG_EVENT
:
615 case ES_OUT_SET_EPG_TIME
:
616 case ES_OUT_SET_ES_SCRAMBLED_STATE
:
617 case ES_OUT_DEL_GROUP
:
618 case ES_OUT_SET_META
:
620 case ES_OUT_UNSET_ES
:
621 case ES_OUT_RESTART_ES
:
622 case ES_OUT_SET_ES_DEFAULT
:
623 case ES_OUT_SET_ES_STATE
:
624 case ES_OUT_SET_ES_CAT_POLICY
:
625 case ES_OUT_SET_ES_FMT
:
626 case ES_OUT_SET_TIMES
:
627 case ES_OUT_SET_JITTER
:
631 if( CmdInitControl( &cmd
, i_query
, args
, p_sys
->b_delayed
) )
633 if( p_sys
->b_delayed
)
635 TsPushCmd( p_sys
->p_ts
, &cmd
);
638 return CmdExecuteControl( p_sys
->p_out
, &cmd
);
641 /* Special control when delayed */
642 case ES_OUT_GET_ES_STATE
:
644 es_out_id_t
*p_es
= va_arg( args
, es_out_id_t
* );
645 bool *pb_enabled
= va_arg( args
, bool* );
647 if( p_sys
->b_delayed
)
652 return es_out_Control( p_sys
->p_out
, ES_OUT_GET_ES_STATE
, p_es
->p_es
, pb_enabled
);
654 case ES_OUT_VOUT_SET_MOUSE_EVENT
:
656 es_out_id_t
*p_es
= va_arg( args
, es_out_id_t
* );
657 vlc_mouse_event cb
= va_arg( args
, vlc_mouse_event
);
658 void *user_data
= va_arg( args
, void * );
659 return es_out_Control( p_sys
->p_out
, ES_OUT_VOUT_SET_MOUSE_EVENT
,
660 p_es
->p_es
, cb
, user_data
);
662 case ES_OUT_VOUT_ADD_OVERLAY
:
664 es_out_id_t
*p_es
= va_arg( args
, es_out_id_t
* );
665 subpicture_t
*sub
= va_arg( args
, subpicture_t
* );
666 int *channel
= va_arg( args
, int * );
667 return es_out_Control( p_sys
->p_out
, ES_OUT_VOUT_ADD_OVERLAY
,
668 p_es
->p_es
, sub
, channel
);
670 case ES_OUT_VOUT_FLUSH_OVERLAY
:
672 es_out_id_t
*p_es
= va_arg( args
, es_out_id_t
* );
673 int channel
= va_arg( args
, int );
674 return es_out_Control( p_sys
->p_out
, ES_OUT_VOUT_FLUSH_OVERLAY
,
675 p_es
->p_es
, channel
);
677 case ES_OUT_SPU_SET_HIGHLIGHT
:
679 es_out_id_t
*p_es
= va_arg( args
, es_out_id_t
* );
680 const vlc_spu_highlight_t
*p_hl
= va_arg( args
, const vlc_spu_highlight_t
* );
681 return es_out_Control( p_sys
->p_out
, ES_OUT_SPU_SET_HIGHLIGHT
,
684 /* Special internal input control */
685 case ES_OUT_GET_EMPTY
:
687 bool *pb_empty
= va_arg( args
, bool* );
688 return ControlLockedGetEmpty( p_out
, pb_empty
);
690 case ES_OUT_GET_WAKE_UP
: /* TODO ? */
692 vlc_tick_t
*pi_wakeup
= va_arg( args
, vlc_tick_t
* );
693 return ControlLockedGetWakeup( p_out
, pi_wakeup
);
695 case ES_OUT_GET_BUFFERING
:
697 bool *pb_buffering
= va_arg( args
, bool* );
698 return ControlLockedGetBuffering( p_out
, pb_buffering
);
700 case ES_OUT_SET_PAUSE_STATE
:
702 const bool b_source_paused
= (bool)va_arg( args
, int );
703 const bool b_paused
= (bool)va_arg( args
, int );
704 const vlc_tick_t i_date
= va_arg( args
, vlc_tick_t
);
706 return ControlLockedSetPauseState( p_out
, b_source_paused
, b_paused
, i_date
);
708 case ES_OUT_SET_RATE
:
710 const float src_rate
= va_arg( args
, double );
711 const float rate
= va_arg( args
, double );
713 return ControlLockedSetRate( p_out
, src_rate
, rate
);
715 case ES_OUT_SET_FRAME_NEXT
:
717 return ControlLockedSetFrameNext( p_out
);
720 case ES_OUT_GET_PCR_SYSTEM
:
721 if( p_sys
->b_delayed
)
724 case ES_OUT_GET_GROUP_FORCED
:
725 case ES_OUT_POST_SUBNODE
:
726 return es_out_vaControl( p_sys
->p_out
, i_query
, args
);
728 case ES_OUT_MODIFY_PCR_SYSTEM
:
730 const bool b_absolute
= va_arg( args
, int );
731 const vlc_tick_t i_system
= va_arg( args
, vlc_tick_t
);
733 if( b_absolute
&& p_sys
->b_delayed
)
736 return es_out_ControlModifyPcrSystem( p_sys
->p_out
, b_absolute
, i_system
);
739 /* Invalid queries for this es_out level */
740 case ES_OUT_SET_ES_BY_ID
:
741 case ES_OUT_RESTART_ES_BY_ID
:
742 case ES_OUT_SET_ES_DEFAULT_BY_ID
:
743 case ES_OUT_GET_ES_OBJECTS_BY_ID
:
744 case ES_OUT_STOP_ALL_ES
:
745 case ES_OUT_START_ALL_ES
:
746 case ES_OUT_SET_DELAY
:
747 case ES_OUT_SET_RECORD_STATE
:
748 case ES_OUT_SET_VBI_PAGE
:
749 case ES_OUT_SET_VBI_TRANSPARENCY
:
751 vlc_assert_unreachable();
755 static int Control( es_out_t
*p_out
, int i_query
, va_list args
)
757 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
760 vlc_mutex_lock( &p_sys
->lock
);
764 i_ret
= ControlLocked( p_out
, i_query
, args
);
766 vlc_mutex_unlock( &p_sys
->lock
);
771 static const struct es_out_callbacks es_out_timeshift_cbs
=
780 /*****************************************************************************
782 *****************************************************************************/
783 static void TsDestroy( ts_thread_t
*p_ts
)
785 vlc_cond_destroy( &p_ts
->wait
);
786 vlc_mutex_destroy( &p_ts
->lock
);
789 static int TsStart( es_out_t
*p_out
)
791 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
794 assert( !p_sys
->b_delayed
);
796 p_sys
->p_ts
= p_ts
= calloc(1, sizeof(*p_ts
));
800 p_ts
->i_tmp_size_max
= p_sys
->i_tmp_size_max
;
801 p_ts
->psz_tmp_path
= p_sys
->psz_tmp_path
;
802 p_ts
->p_input
= p_sys
->p_input
;
803 p_ts
->p_out
= p_sys
->p_out
;
804 vlc_mutex_init( &p_ts
->lock
);
805 vlc_cond_init( &p_ts
->wait
);
806 p_ts
->b_paused
= p_sys
->b_input_paused
&& !p_sys
->b_input_paused_source
;
807 p_ts
->i_pause_date
= p_ts
->b_paused
? vlc_tick_now() : -1;
808 p_ts
->rate_source
= p_sys
->input_rate_source
;
809 p_ts
->rate
= p_sys
->input_rate
;
810 p_ts
->i_rate_date
= -1;
811 p_ts
->i_rate_delay
= 0;
812 p_ts
->i_buffering_delay
= 0;
813 p_ts
->i_cmd_delay
= 0;
814 p_ts
->p_storage_r
= NULL
;
815 p_ts
->p_storage_w
= NULL
;
817 p_sys
->b_delayed
= true;
818 if( vlc_clone( &p_ts
->thread
, TsRun
, p_ts
, VLC_THREAD_PRIORITY_INPUT
) )
820 msg_Err( p_sys
->p_input
, "cannot create timeshift thread" );
824 p_sys
->b_delayed
= false;
830 static void TsAutoStop( es_out_t
*p_out
)
832 es_out_sys_t
*p_sys
= container_of(p_out
, es_out_sys_t
, out
);
834 if( !p_sys
->b_delayed
|| !TsIsUnused( p_sys
->p_ts
) )
837 msg_Warn( p_sys
->p_input
, "es out timeshift: auto stop" );
838 TsStop( p_sys
->p_ts
);
840 p_sys
->b_delayed
= false;
842 static void TsStop( ts_thread_t
*p_ts
)
844 vlc_cancel( p_ts
->thread
);
845 vlc_join( p_ts
->thread
, NULL
);
847 vlc_mutex_lock( &p_ts
->lock
);
852 if( TsPopCmdLocked( p_ts
, &cmd
, true ) )
857 assert( !p_ts
->p_storage_r
|| !p_ts
->p_storage_r
->p_next
);
858 if( p_ts
->p_storage_r
)
859 TsStorageDelete( p_ts
->p_storage_r
);
860 vlc_mutex_unlock( &p_ts
->lock
);
864 static void TsPushCmd( ts_thread_t
*p_ts
, ts_cmd_t
*p_cmd
)
866 vlc_mutex_lock( &p_ts
->lock
);
868 if( !p_ts
->p_storage_w
|| TsStorageIsFull( p_ts
->p_storage_w
, p_cmd
) )
870 ts_storage_t
*p_storage
= TsStorageNew( p_ts
->psz_tmp_path
, p_ts
->i_tmp_size_max
);
875 vlc_mutex_unlock( &p_ts
->lock
);
876 /* TODO warn the user (but only once) */
880 if( !p_ts
->p_storage_w
)
882 p_ts
->p_storage_r
= p_ts
->p_storage_w
= p_storage
;
886 TsStoragePack( p_ts
->p_storage_w
);
887 p_ts
->p_storage_w
->p_next
= p_storage
;
888 p_ts
->p_storage_w
= p_storage
;
892 /* TODO return error and warn the user (but only once) */
893 TsStoragePushCmd( p_ts
->p_storage_w
, p_cmd
, p_ts
->p_storage_r
== p_ts
->p_storage_w
);
895 vlc_cond_signal( &p_ts
->wait
);
897 vlc_mutex_unlock( &p_ts
->lock
);
899 static int TsPopCmdLocked( ts_thread_t
*p_ts
, ts_cmd_t
*p_cmd
, bool b_flush
)
901 vlc_mutex_assert( &p_ts
->lock
);
903 if( TsStorageIsEmpty( p_ts
->p_storage_r
) )
906 TsStoragePopCmd( p_ts
->p_storage_r
, p_cmd
, b_flush
);
908 while( TsStorageIsEmpty( p_ts
->p_storage_r
) )
910 ts_storage_t
*p_next
= p_ts
->p_storage_r
->p_next
;
914 TsStorageDelete( p_ts
->p_storage_r
);
915 p_ts
->p_storage_r
= p_next
;
920 static bool TsHasCmd( ts_thread_t
*p_ts
)
924 vlc_mutex_lock( &p_ts
->lock
);
925 b_cmd
= !TsStorageIsEmpty( p_ts
->p_storage_r
);
926 vlc_mutex_unlock( &p_ts
->lock
);
930 static bool TsIsUnused( ts_thread_t
*p_ts
)
934 vlc_mutex_lock( &p_ts
->lock
);
935 b_unused
= !p_ts
->b_paused
&&
936 p_ts
->rate
== p_ts
->rate_source
&&
937 TsStorageIsEmpty( p_ts
->p_storage_r
);
938 vlc_mutex_unlock( &p_ts
->lock
);
942 static int TsChangePause( ts_thread_t
*p_ts
, bool b_source_paused
, bool b_paused
, vlc_tick_t i_date
)
944 vlc_mutex_lock( &p_ts
->lock
);
949 assert( !b_source_paused
);
950 i_ret
= es_out_SetPauseState( p_ts
->p_out
, true, true, i_date
);
954 i_ret
= es_out_SetPauseState( p_ts
->p_out
, false, false, i_date
);
961 assert( p_ts
->i_pause_date
> 0 );
963 p_ts
->i_cmd_delay
+= i_date
- p_ts
->i_pause_date
;
966 p_ts
->b_paused
= b_paused
;
967 p_ts
->i_pause_date
= i_date
;
969 vlc_cond_signal( &p_ts
->wait
);
971 vlc_mutex_unlock( &p_ts
->lock
);
974 static int TsChangeRate( ts_thread_t
*p_ts
, float src_rate
, float rate
)
978 vlc_mutex_lock( &p_ts
->lock
);
979 p_ts
->i_cmd_delay
+= p_ts
->i_rate_delay
;
981 p_ts
->i_rate_date
= -1;
982 p_ts
->i_rate_delay
= 0;
984 p_ts
->rate_source
= src_rate
;
986 i_ret
= es_out_SetRate( p_ts
->p_out
, rate
, rate
);
987 vlc_mutex_unlock( &p_ts
->lock
);
992 static void *TsRun( void *p_data
)
994 ts_thread_t
*p_ts
= p_data
;
995 vlc_tick_t i_buffering_date
= -1;
1000 vlc_tick_t i_deadline
;
1003 /* Pop a command to execute */
1004 vlc_mutex_lock( &p_ts
->lock
);
1005 mutex_cleanup_push( &p_ts
->lock
);
1009 const int canc
= vlc_savecancel();
1010 b_buffering
= es_out_GetBuffering( p_ts
->p_out
);
1012 if( ( !p_ts
->b_paused
|| b_buffering
) && !TsPopCmdLocked( p_ts
, &cmd
, false ) )
1014 vlc_restorecancel( canc
);
1017 vlc_restorecancel( canc
);
1019 vlc_cond_wait( &p_ts
->wait
, &p_ts
->lock
);
1022 if( b_buffering
&& i_buffering_date
< 0 )
1024 i_buffering_date
= cmd
.i_date
;
1026 else if( i_buffering_date
> 0 )
1028 p_ts
->i_buffering_delay
+= i_buffering_date
- cmd
.i_date
; /* It is < 0 */
1030 i_buffering_date
= cmd
.i_date
;
1032 i_buffering_date
= -1;
1035 if( p_ts
->i_rate_date
< 0 )
1036 p_ts
->i_rate_date
= cmd
.i_date
;
1038 p_ts
->i_rate_delay
= 0;
1039 if( p_ts
->rate_source
!= p_ts
->rate
)
1041 const vlc_tick_t i_duration
= cmd
.i_date
- p_ts
->i_rate_date
;
1042 p_ts
->i_rate_delay
= i_duration
* p_ts
->rate_source
/ p_ts
->rate
- i_duration
;
1044 if( p_ts
->i_cmd_delay
+ p_ts
->i_rate_delay
+ p_ts
->i_buffering_delay
< 0 && p_ts
->rate
!= p_ts
->rate_source
)
1046 const int canc
= vlc_savecancel();
1048 /* Auto reset to rate 1.0 */
1049 msg_Warn( p_ts
->p_input
, "es out timeshift: auto reset rate to %f", p_ts
->rate_source
);
1051 p_ts
->i_cmd_delay
= 0;
1052 p_ts
->i_buffering_delay
= 0;
1054 p_ts
->i_rate_delay
= 0;
1055 p_ts
->i_rate_date
= -1;
1056 p_ts
->rate
= p_ts
->rate_source
;
1058 if( !es_out_SetRate( p_ts
->p_out
, p_ts
->rate_source
, p_ts
->rate
) )
1060 vlc_value_t val
= { .f_float
= p_ts
->rate
};
1062 * FIXME it is perfectly safe BUT it is ugly as it may hide a
1063 * rate change requested by user */
1064 input_ControlPushHelper( p_ts
->p_input
, INPUT_CONTROL_SET_RATE
, &val
);
1067 vlc_restorecancel( canc
);
1069 i_deadline
= cmd
.i_date
+ p_ts
->i_cmd_delay
+ p_ts
->i_rate_delay
+ p_ts
->i_buffering_delay
;
1072 vlc_mutex_unlock( &p_ts
->lock
);
1074 /* Regulate the speed of command processing to the same one than
1076 vlc_cleanup_push( cmd_cleanup_routine
, &cmd
);
1078 vlc_tick_wait( i_deadline
);
1082 /* Execute the command */
1083 const int canc
= vlc_savecancel();
1084 switch( cmd
.i_type
)
1087 CmdExecuteAdd( p_ts
->p_out
, &cmd
);
1088 CmdCleanAdd( &cmd
);
1091 CmdExecuteSend( p_ts
->p_out
, &cmd
);
1092 CmdCleanSend( &cmd
);
1095 CmdExecuteControl( p_ts
->p_out
, &cmd
);
1096 CmdCleanControl( &cmd
);
1099 CmdExecuteDel( p_ts
->p_out
, &cmd
);
1102 vlc_assert_unreachable();
1105 vlc_restorecancel( canc
);
1111 /*****************************************************************************
1113 *****************************************************************************/
1114 static ts_storage_t
*TsStorageNew( const char *psz_tmp_path
, int64_t i_tmp_size_max
)
1116 ts_storage_t
*p_storage
= malloc( sizeof (*p_storage
) );
1117 if( unlikely(p_storage
== NULL
) )
1121 int fd
= GetTmpFile( &psz_file
, psz_tmp_path
);
1128 p_storage
->p_filew
= fdopen( fd
, "w+b" );
1129 if( p_storage
->p_filew
== NULL
)
1132 vlc_unlink( psz_file
);
1136 p_storage
->p_filer
= vlc_fopen( psz_file
, "rb" );
1137 if( p_storage
->p_filer
== NULL
)
1139 fclose( p_storage
->p_filew
);
1140 vlc_unlink( psz_file
);
1145 vlc_unlink( psz_file
);
1148 p_storage
->psz_file
= psz_file
;
1150 p_storage
->p_next
= NULL
;
1153 p_storage
->i_file_max
= i_tmp_size_max
;
1154 p_storage
->i_file_size
= 0;
1157 p_storage
->i_cmd_w
= 0;
1158 p_storage
->i_cmd_r
= 0;
1159 p_storage
->i_cmd_max
= 30000;
1160 p_storage
->p_cmd
= vlc_alloc( p_storage
->i_cmd_max
, sizeof(*p_storage
->p_cmd
) );
1161 //fprintf( stderr, "\nSTORAGE name=%s size=%d KiB\n", p_storage->psz_file, p_storage->i_cmd_max * sizeof(*p_storage->p_cmd) /1024 );
1163 if( !p_storage
->p_cmd
)
1165 TsStorageDelete( p_storage
);
1175 static void TsStorageDelete( ts_storage_t
*p_storage
)
1177 while( p_storage
->i_cmd_r
< p_storage
->i_cmd_w
)
1181 TsStoragePopCmd( p_storage
, &cmd
, true );
1185 free( p_storage
->p_cmd
);
1187 fclose( p_storage
->p_filer
);
1188 fclose( p_storage
->p_filew
);
1190 vlc_unlink( p_storage
->psz_file
);
1191 free( p_storage
->psz_file
);
1196 static void TsStoragePack( ts_storage_t
*p_storage
)
1198 /* Try to release a bit of memory */
1199 if( p_storage
->i_cmd_w
>= p_storage
->i_cmd_max
)
1202 p_storage
->i_cmd_max
= __MAX( p_storage
->i_cmd_w
, 1 );
1204 ts_cmd_t
*p_new
= realloc( p_storage
->p_cmd
, p_storage
->i_cmd_max
* sizeof(*p_storage
->p_cmd
) );
1206 p_storage
->p_cmd
= p_new
;
1208 static bool TsStorageIsFull( ts_storage_t
*p_storage
, const ts_cmd_t
*p_cmd
)
1210 if( p_cmd
&& p_cmd
->i_type
== C_SEND
&& p_storage
->i_cmd_w
> 0 )
1212 size_t i_size
= sizeof(*p_cmd
->u
.send
.p_block
) + p_cmd
->u
.send
.p_block
->i_buffer
;
1214 if( p_storage
->i_file_size
+ i_size
>= p_storage
->i_file_max
)
1217 return p_storage
->i_cmd_w
>= p_storage
->i_cmd_max
;
1219 static bool TsStorageIsEmpty( ts_storage_t
*p_storage
)
1221 return !p_storage
|| p_storage
->i_cmd_r
>= p_storage
->i_cmd_w
;
1223 static void TsStoragePushCmd( ts_storage_t
*p_storage
, const ts_cmd_t
*p_cmd
, bool b_flush
)
1225 ts_cmd_t cmd
= *p_cmd
;
1227 assert( !TsStorageIsFull( p_storage
, p_cmd
) );
1229 if( cmd
.i_type
== C_SEND
)
1231 block_t
*p_block
= cmd
.u
.send
.p_block
;
1233 cmd
.u
.send
.p_block
= NULL
;
1234 cmd
.u
.send
.i_offset
= ftell( p_storage
->p_filew
);
1236 if( fwrite( p_block
, sizeof(*p_block
), 1, p_storage
->p_filew
) != 1 )
1238 block_Release( p_block
);
1241 p_storage
->i_file_size
+= sizeof(*p_block
);
1242 if( p_block
->i_buffer
> 0 )
1244 if( fwrite( p_block
->p_buffer
, p_block
->i_buffer
, 1, p_storage
->p_filew
) != 1 )
1246 block_Release( p_block
);
1250 p_storage
->i_file_size
+= p_block
->i_buffer
;
1251 block_Release( p_block
);
1254 fflush( p_storage
->p_filew
);
1256 p_storage
->p_cmd
[p_storage
->i_cmd_w
++] = cmd
;
1258 static void TsStoragePopCmd( ts_storage_t
*p_storage
, ts_cmd_t
*p_cmd
, bool b_flush
)
1260 assert( !TsStorageIsEmpty( p_storage
) );
1262 *p_cmd
= p_storage
->p_cmd
[p_storage
->i_cmd_r
++];
1263 if( p_cmd
->i_type
== C_SEND
)
1268 !fseek( p_storage
->p_filer
, p_cmd
->u
.send
.i_offset
, SEEK_SET
) &&
1269 fread( &block
, sizeof(block
), 1, p_storage
->p_filer
) == 1 )
1271 block_t
*p_block
= block_Alloc( block
.i_buffer
);
1274 p_block
->i_dts
= block
.i_dts
;
1275 p_block
->i_pts
= block
.i_pts
;
1276 p_block
->i_flags
= block
.i_flags
;
1277 p_block
->i_length
= block
.i_length
;
1278 p_block
->i_nb_samples
= block
.i_nb_samples
;
1279 p_block
->i_buffer
= fread( p_block
->p_buffer
, 1, block
.i_buffer
, p_storage
->p_filer
);
1281 p_cmd
->u
.send
.p_block
= p_block
;
1285 //perror( "TsStoragePopCmd" );
1286 p_cmd
->u
.send
.p_block
= block_Alloc( 1 );
1291 /*****************************************************************************
1293 *****************************************************************************/
1294 static void CmdClean( ts_cmd_t
*p_cmd
)
1296 switch( p_cmd
->i_type
)
1299 CmdCleanAdd( p_cmd
);
1302 CmdCleanSend( p_cmd
);
1305 CmdCleanControl( p_cmd
);
1310 vlc_assert_unreachable();
1315 static int CmdInitAdd( ts_cmd_t
*p_cmd
, es_out_id_t
*p_es
, const es_format_t
*p_fmt
, bool b_copy
)
1317 p_cmd
->i_type
= C_ADD
;
1318 p_cmd
->i_date
= vlc_tick_now();
1319 p_cmd
->u
.add
.p_es
= p_es
;
1322 p_cmd
->u
.add
.p_fmt
= malloc( sizeof(*p_fmt
) );
1323 if( !p_cmd
->u
.add
.p_fmt
)
1324 return VLC_EGENERIC
;
1325 es_format_Copy( p_cmd
->u
.add
.p_fmt
, p_fmt
);
1329 p_cmd
->u
.add
.p_fmt
= (es_format_t
*)p_fmt
;
1333 static void CmdExecuteAdd( es_out_t
*p_out
, ts_cmd_t
*p_cmd
)
1335 p_cmd
->u
.add
.p_es
->p_es
= es_out_Add( p_out
, p_cmd
->u
.add
.p_fmt
);
1337 static void CmdCleanAdd( ts_cmd_t
*p_cmd
)
1339 es_format_Clean( p_cmd
->u
.add
.p_fmt
);
1340 free( p_cmd
->u
.add
.p_fmt
);
1343 static void CmdInitSend( ts_cmd_t
*p_cmd
, es_out_id_t
*p_es
, block_t
*p_block
)
1345 p_cmd
->i_type
= C_SEND
;
1346 p_cmd
->i_date
= vlc_tick_now();
1347 p_cmd
->u
.send
.p_es
= p_es
;
1348 p_cmd
->u
.send
.p_block
= p_block
;
1350 static int CmdExecuteSend( es_out_t
*p_out
, ts_cmd_t
*p_cmd
)
1352 block_t
*p_block
= p_cmd
->u
.send
.p_block
;
1354 p_cmd
->u
.send
.p_block
= NULL
;
1358 if( p_cmd
->u
.send
.p_es
->p_es
)
1359 return es_out_Send( p_out
, p_cmd
->u
.send
.p_es
->p_es
, p_block
);
1360 block_Release( p_block
);
1362 return VLC_EGENERIC
;
1364 static void CmdCleanSend( ts_cmd_t
*p_cmd
)
1366 if( p_cmd
->u
.send
.p_block
)
1367 block_Release( p_cmd
->u
.send
.p_block
);
1370 static int CmdInitDel( ts_cmd_t
*p_cmd
, es_out_id_t
*p_es
)
1372 p_cmd
->i_type
= C_DEL
;
1373 p_cmd
->i_date
= vlc_tick_now();
1374 p_cmd
->u
.del
.p_es
= p_es
;
1377 static void CmdExecuteDel( es_out_t
*p_out
, ts_cmd_t
*p_cmd
)
1379 if( p_cmd
->u
.del
.p_es
->p_es
)
1380 es_out_Del( p_out
, p_cmd
->u
.del
.p_es
->p_es
);
1381 free( p_cmd
->u
.del
.p_es
);
1384 static int CmdInitControl( ts_cmd_t
*p_cmd
, int i_query
, va_list args
, bool b_copy
)
1386 p_cmd
->i_type
= C_CONTROL
;
1387 p_cmd
->i_date
= vlc_tick_now();
1388 p_cmd
->u
.control
.i_query
= i_query
;
1392 /* Pass-through control */
1393 case ES_OUT_SET_MODE
: /* arg1= int */
1394 case ES_OUT_SET_GROUP
: /* arg1= int */
1395 case ES_OUT_DEL_GROUP
: /* arg1=int i_group */
1396 p_cmd
->u
.control
.u
.i_int
= va_arg( args
, int );
1399 case ES_OUT_SET_PCR
: /* arg1=vlc_tick_t i_pcr(microsecond!) (using default group 0)*/
1400 case ES_OUT_SET_NEXT_DISPLAY_TIME
: /* arg1=int64_t i_pts(microsecond) */
1401 p_cmd
->u
.control
.u
.i_i64
= va_arg( args
, int64_t );
1404 case ES_OUT_SET_GROUP_PCR
: /* arg1= int i_group, arg2=vlc_tick_t i_pcr(microsecond!)*/
1405 p_cmd
->u
.control
.u
.int_i64
.i_int
= va_arg( args
, int );
1406 p_cmd
->u
.control
.u
.int_i64
.i_i64
= va_arg( args
, vlc_tick_t
);
1409 case ES_OUT_SET_ES_SCRAMBLED_STATE
:
1410 p_cmd
->u
.control
.u
.es_bool
.p_es
= va_arg( args
, es_out_id_t
* );
1411 p_cmd
->u
.control
.u
.es_bool
.b_bool
= (bool)va_arg( args
, int );
1414 case ES_OUT_RESET_PCR
: /* no arg */
1415 case ES_OUT_SET_EOS
:
1418 case ES_OUT_SET_META
: /* arg1=const vlc_meta_t* */
1419 case ES_OUT_SET_GROUP_META
: /* arg1=int i_group arg2=const vlc_meta_t* */
1421 if( i_query
== ES_OUT_SET_GROUP_META
)
1422 p_cmd
->u
.control
.u
.int_meta
.i_int
= va_arg( args
, int );
1423 const vlc_meta_t
*p_meta
= va_arg( args
, const vlc_meta_t
* );
1427 p_cmd
->u
.control
.u
.int_meta
.p_meta
= vlc_meta_New();
1428 if( !p_cmd
->u
.control
.u
.int_meta
.p_meta
)
1429 return VLC_EGENERIC
;
1430 vlc_meta_Merge( p_cmd
->u
.control
.u
.int_meta
.p_meta
, p_meta
);
1434 /* The cast is only needed to avoid warning */
1435 p_cmd
->u
.control
.u
.int_meta
.p_meta
= (vlc_meta_t
*)p_meta
;
1440 case ES_OUT_SET_GROUP_EPG
: /* arg1=int i_group arg2=const vlc_epg_t* */
1442 p_cmd
->u
.control
.u
.int_epg
.i_int
= va_arg( args
, int );
1443 const vlc_epg_t
*p_epg
= va_arg( args
, const vlc_epg_t
* );
1447 p_cmd
->u
.control
.u
.int_epg
.p_epg
= vlc_epg_Duplicate( p_epg
);
1448 if( !p_cmd
->u
.control
.u
.int_epg
.p_epg
)
1449 return VLC_EGENERIC
;
1453 /* The cast is only needed to avoid warning */
1454 p_cmd
->u
.control
.u
.int_epg
.p_epg
= (vlc_epg_t
*)p_epg
;
1458 case ES_OUT_SET_GROUP_EPG_EVENT
: /* arg1=int i_group arg2=const vlc_epg_event_t* */
1460 p_cmd
->u
.control
.u
.int_epg_evt
.i_int
= va_arg( args
, int );
1461 const vlc_epg_event_t
*p_evt
= va_arg( args
, const vlc_epg_event_t
* );
1465 p_cmd
->u
.control
.u
.int_epg_evt
.p_evt
= vlc_epg_event_Duplicate( p_evt
);
1466 if( !p_cmd
->u
.control
.u
.int_epg_evt
.p_evt
)
1467 return VLC_EGENERIC
;
1471 /* The cast is only needed to avoid warning */
1472 p_cmd
->u
.control
.u
.int_epg_evt
.p_evt
= (vlc_epg_event_t
*)p_evt
;
1476 case ES_OUT_SET_EPG_TIME
: /* arg1=int64_t (seconds) */
1477 p_cmd
->u
.control
.u
.i_i64
= va_arg( args
, int64_t );
1480 /* Modified control */
1481 case ES_OUT_SET_ES
: /* arg1= es_out_id_t* */
1482 case ES_OUT_UNSET_ES
: /* arg1= es_out_id_t* */
1483 case ES_OUT_RESTART_ES
: /* arg1= es_out_id_t* */
1484 case ES_OUT_SET_ES_DEFAULT
: /* arg1= es_out_id_t* */
1485 p_cmd
->u
.control
.u
.p_es
= va_arg( args
, es_out_id_t
* );
1488 case ES_OUT_SET_ES_CAT_POLICY
:
1489 p_cmd
->u
.control
.u
.es_policy
.i_cat
= va_arg( args
, int );
1490 p_cmd
->u
.control
.u
.es_policy
.i_policy
= va_arg( args
, int );
1493 case ES_OUT_SET_ES_STATE
:/* arg1= es_out_id_t* arg2=bool */
1494 p_cmd
->u
.control
.u
.es_bool
.p_es
= va_arg( args
, es_out_id_t
* );
1495 p_cmd
->u
.control
.u
.es_bool
.b_bool
= (bool)va_arg( args
, int );
1498 case ES_OUT_SET_ES_FMT
: /* arg1= es_out_id_t* arg2=es_format_t* */
1500 p_cmd
->u
.control
.u
.es_fmt
.p_es
= va_arg( args
, es_out_id_t
* );
1501 es_format_t
*p_fmt
= va_arg( args
, es_format_t
* );
1505 p_cmd
->u
.control
.u
.es_fmt
.p_fmt
= malloc( sizeof(*p_fmt
) );
1506 if( !p_cmd
->u
.control
.u
.es_fmt
.p_fmt
)
1507 return VLC_EGENERIC
;
1508 es_format_Copy( p_cmd
->u
.control
.u
.es_fmt
.p_fmt
, p_fmt
);
1512 p_cmd
->u
.control
.u
.es_fmt
.p_fmt
= p_fmt
;
1516 case ES_OUT_SET_TIMES
:
1518 double f_position
= va_arg( args
, double );
1519 vlc_tick_t i_time
= va_arg( args
, vlc_tick_t
);
1520 vlc_tick_t i_length
= va_arg( args
, vlc_tick_t
);
1522 p_cmd
->u
.control
.u
.times
.f_position
= f_position
;
1523 p_cmd
->u
.control
.u
.times
.i_time
= i_time
;
1524 p_cmd
->u
.control
.u
.times
.i_length
= i_length
;
1527 case ES_OUT_SET_JITTER
:
1529 vlc_tick_t i_pts_delay
= va_arg( args
, vlc_tick_t
);
1530 vlc_tick_t i_pts_jitter
= va_arg( args
, vlc_tick_t
);
1531 int i_cr_average
= va_arg( args
, int );
1533 p_cmd
->u
.control
.u
.jitter
.i_pts_delay
= i_pts_delay
;
1534 p_cmd
->u
.control
.u
.jitter
.i_pts_jitter
= i_pts_jitter
;
1535 p_cmd
->u
.control
.u
.jitter
.i_cr_average
= i_cr_average
;
1540 vlc_assert_unreachable();
1541 return VLC_EGENERIC
;
1546 static int CmdExecuteControl( es_out_t
*p_out
, ts_cmd_t
*p_cmd
)
1548 const int i_query
= p_cmd
->u
.control
.i_query
;
1552 /* Pass-through control */
1553 case ES_OUT_SET_MODE
: /* arg1= int */
1554 case ES_OUT_SET_GROUP
: /* arg1= int */
1555 case ES_OUT_DEL_GROUP
: /* arg1=int i_group */
1556 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.i_int
);
1558 case ES_OUT_SET_PCR
: /* arg1=vlc_tick_t i_pcr(microsecond!) (using default group 0)*/
1559 case ES_OUT_SET_NEXT_DISPLAY_TIME
: /* arg1=int64_t i_pts(microsecond) */
1560 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.i_i64
);
1562 case ES_OUT_SET_GROUP_PCR
: /* arg1= int i_group, arg2=vlc_tick_t i_pcr(microsecond!)*/
1563 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.int_i64
.i_int
,
1564 p_cmd
->u
.control
.u
.int_i64
.i_i64
);
1566 case ES_OUT_RESET_PCR
: /* no arg */
1567 case ES_OUT_SET_EOS
:
1568 return es_out_Control( p_out
, i_query
);
1570 case ES_OUT_SET_GROUP_META
: /* arg1=int i_group arg2=const vlc_meta_t* */
1571 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.int_meta
.i_int
,
1572 p_cmd
->u
.control
.u
.int_meta
.p_meta
);
1574 case ES_OUT_SET_GROUP_EPG
: /* arg1=int i_group arg2=const vlc_epg_t* */
1575 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.int_epg
.i_int
,
1576 p_cmd
->u
.control
.u
.int_epg
.p_epg
);
1578 case ES_OUT_SET_GROUP_EPG_EVENT
: /* arg1=int i_group arg2=const vlc_epg_event_t* */
1579 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.int_epg_evt
.i_int
,
1580 p_cmd
->u
.control
.u
.int_epg_evt
.p_evt
);
1582 case ES_OUT_SET_EPG_TIME
: /* arg1=int64_t */
1583 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.i_i64
);
1585 case ES_OUT_SET_ES_SCRAMBLED_STATE
: /* arg1=int es_out_id_t* arg2=bool */
1586 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.es_bool
.p_es
->p_es
,
1587 p_cmd
->u
.control
.u
.es_bool
.b_bool
);
1589 case ES_OUT_SET_META
: /* arg1=const vlc_meta_t* */
1590 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.int_meta
.p_meta
);
1592 /* Modified control */
1593 case ES_OUT_SET_ES
: /* arg1= es_out_id_t* */
1594 case ES_OUT_UNSET_ES
: /* arg1= es_out_id_t* */
1595 case ES_OUT_RESTART_ES
: /* arg1= es_out_id_t* */
1596 case ES_OUT_SET_ES_DEFAULT
: /* arg1= es_out_id_t* */
1597 return es_out_Control( p_out
, i_query
, !p_cmd
->u
.control
.u
.p_es
? NULL
:
1598 p_cmd
->u
.control
.u
.p_es
->p_es
);
1600 case ES_OUT_SET_ES_STATE
:/* arg1= es_out_id_t* arg2=bool */
1601 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.es_bool
.p_es
->p_es
,
1602 p_cmd
->u
.control
.u
.es_bool
.b_bool
);
1604 case ES_OUT_SET_ES_CAT_POLICY
:
1605 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.es_policy
.i_cat
,
1606 p_cmd
->u
.control
.u
.es_policy
.i_policy
);
1608 case ES_OUT_SET_ES_FMT
: /* arg1= es_out_id_t* arg2=es_format_t* */
1609 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.es_fmt
.p_es
->p_es
,
1610 p_cmd
->u
.control
.u
.es_fmt
.p_fmt
);
1612 case ES_OUT_SET_TIMES
:
1613 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.times
.f_position
,
1614 p_cmd
->u
.control
.u
.times
.i_time
,
1615 p_cmd
->u
.control
.u
.times
.i_length
);
1616 case ES_OUT_SET_JITTER
:
1617 return es_out_Control( p_out
, i_query
, p_cmd
->u
.control
.u
.jitter
.i_pts_delay
,
1618 p_cmd
->u
.control
.u
.jitter
.i_pts_jitter
,
1619 p_cmd
->u
.control
.u
.jitter
.i_cr_average
);
1622 vlc_assert_unreachable();
1623 return VLC_EGENERIC
;
1626 static void CmdCleanControl( ts_cmd_t
*p_cmd
)
1628 switch( p_cmd
->u
.control
.i_query
)
1630 case ES_OUT_SET_GROUP_META
:
1631 case ES_OUT_SET_META
:
1632 if( p_cmd
->u
.control
.u
.int_meta
.p_meta
)
1633 vlc_meta_Delete( p_cmd
->u
.control
.u
.int_meta
.p_meta
);
1635 case ES_OUT_SET_GROUP_EPG
:
1636 if( p_cmd
->u
.control
.u
.int_epg
.p_epg
)
1637 vlc_epg_Delete( p_cmd
->u
.control
.u
.int_epg
.p_epg
);
1639 case ES_OUT_SET_GROUP_EPG_EVENT
:
1640 if( p_cmd
->u
.control
.u
.int_epg_evt
.p_evt
)
1641 vlc_epg_event_Delete( p_cmd
->u
.control
.u
.int_epg_evt
.p_evt
);
1643 case ES_OUT_SET_ES_FMT
:
1644 if( p_cmd
->u
.control
.u
.es_fmt
.p_fmt
)
1646 es_format_Clean( p_cmd
->u
.control
.u
.es_fmt
.p_fmt
);
1647 free( p_cmd
->u
.control
.u
.es_fmt
.p_fmt
);
1653 static int GetTmpFile( char **filename
, const char *dirname
)
1656 && asprintf( filename
, "%s"DIR_SEP PACKAGE_NAME
"-timeshift.XXXXXX",
1659 vlc_mkdir( dirname
, 0700 );
1661 int fd
= vlc_mkstemp( *filename
);
1668 *filename
= strdup( DIR_SEP
"tmp"DIR_SEP PACKAGE_NAME
"-timeshift.XXXXXX" );
1669 if( unlikely(*filename
== NULL
) )
1672 int fd
= vlc_mkstemp( *filename
);