es_out: propagate the input_source_t
[vlc.git] / src / input / es_out_timeshift.c
blob65c1526ee08c454c1f54263e04ec331e459af9ce
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 /*****************************************************************************
24 * Preamble
25 *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
30 #include <stdlib.h>
31 #include <stdio.h>
32 #include <assert.h>
33 #include <errno.h>
34 #if defined (_WIN32)
35 # include <direct.h>
36 #endif
37 #include <sys/stat.h>
38 #include <unistd.h>
40 #include <vlc_common.h>
41 #include <vlc_fs.h>
42 #include <vlc_mouse.h>
43 #ifdef _WIN32
44 # include <vlc_charset.h>
45 #endif
46 #include <vlc_es_out.h>
47 #include <vlc_block.h>
48 #include "input_internal.h"
49 #include "es_out.h"
51 /*****************************************************************************
52 * Local prototypes
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__))
58 #else
59 # define attribute_packed
60 #endif
62 enum
64 C_ADD,
65 C_SEND,
66 C_DEL,
67 C_CONTROL,
70 typedef struct attribute_packed
72 input_source_t *in;
73 es_out_id_t *p_es;
74 es_format_t *p_fmt;
75 } ts_cmd_add_t;
77 typedef struct attribute_packed
79 es_out_id_t *p_es;
80 } ts_cmd_del_t;
82 typedef struct attribute_packed
84 es_out_id_t *p_es;
85 block_t *p_block;
86 int i_offset; /* We do not use file > INT_MAX */
87 } ts_cmd_send_t;
89 typedef struct attribute_packed
91 input_source_t *in;
92 int i_query;
94 union
96 bool b_bool;
97 int i_int;
98 int64_t i_i64;
99 es_out_id_t *p_es;
100 struct
102 int i_int;
103 int64_t i_i64;
104 } int_i64;
105 struct
107 int i_int;
108 vlc_meta_t *p_meta;
109 } int_meta;
110 struct
112 int i_int;
113 vlc_epg_t *p_epg;
114 } int_epg;
115 struct
117 int i_int;
118 vlc_epg_event_t *p_evt;
119 } int_epg_evt;
120 struct
122 es_out_id_t *p_es;
123 bool b_bool;
124 } es_bool;
125 struct
127 es_out_id_t *p_es;
128 es_format_t *p_fmt;
129 } es_fmt;
130 struct
132 int i_cat;
133 int i_policy;
134 } es_policy;
135 } u;
136 } ts_cmd_control_t;
138 typedef struct attribute_packed
140 int i_query;
142 union
144 int i_int;
145 struct
147 vlc_tick_t i_pts_delay;
148 vlc_tick_t i_pts_jitter;
149 int i_cr_average;
150 } jitter;
151 struct
153 double f_position;
154 vlc_tick_t i_time;
155 vlc_tick_t i_normal_time;
156 vlc_tick_t i_length;
157 } times;
158 } u;
159 } ts_cmd_privcontrol_t;
161 typedef struct attribute_packed
163 int8_t i_type;
164 vlc_tick_t i_date;
165 union
167 ts_cmd_add_t add;
168 ts_cmd_del_t del;
169 ts_cmd_send_t send;
170 ts_cmd_control_t control;
171 ts_cmd_privcontrol_t privcontrol;
172 } u;
173 } ts_cmd_t;
175 typedef struct ts_storage_t ts_storage_t;
176 struct ts_storage_t
178 ts_storage_t *p_next;
180 /* */
181 #ifdef _WIN32
182 char *psz_file; /* Filename */
183 #endif
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 */
189 /* */
190 int i_cmd_r;
191 int i_cmd_w;
192 int i_cmd_max;
193 ts_cmd_t *p_cmd;
196 typedef struct
198 vlc_thread_t thread;
199 input_thread_t *p_input;
200 es_out_t *p_out;
201 int64_t i_tmp_size_max;
202 const char *psz_tmp_path;
204 /* Lock for all following fields */
205 vlc_mutex_t lock;
206 vlc_cond_t wait;
208 /* */
209 bool b_paused;
210 vlc_tick_t i_pause_date;
212 /* */
213 float rate;
214 float rate_source;
215 vlc_tick_t i_rate_date;
216 vlc_tick_t i_rate_delay;
218 /* */
219 vlc_tick_t i_buffering_delay;
221 /* */
222 ts_storage_t *p_storage_r;
223 ts_storage_t *p_storage_w;
225 vlc_tick_t i_cmd_delay;
227 } ts_thread_t;
229 struct es_out_id_t
231 es_out_id_t *p_es;
234 typedef struct
236 input_thread_t *p_input;
237 es_out_t *p_out;
239 /* Configuration */
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 */
244 vlc_mutex_t lock;
246 /* */
247 bool b_delayed;
248 ts_thread_t *p_ts;
250 /* */
251 bool b_input_paused;
252 bool b_input_paused_source;
253 float input_rate;
254 float input_rate_source;
256 /* */
257 int i_es;
258 es_out_id_t **pp_es;
260 es_out_t out;
261 } es_out_sys_t;
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 );
295 /* */
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 * );
307 /* File helpers */
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) );
318 if( !p_sys )
319 return NULL;
321 p_sys->out.cbs = &es_out_timeshift_cbs;
323 /* */
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;
334 p_sys->p_ts = NULL;
336 TAB_INIT( p_sys->i_es, p_sys->pp_es );
338 /* */
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;
342 else
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 );
352 if( count > 0 )
354 WCHAR *path = vlc_alloc( count + 1, sizeof(WCHAR) );
355 if( path != NULL )
357 DWORD ret = GetTempPath( count + 1, path );
358 if( ret != 0 && ret <= count )
359 p_sys->psz_tmp_path = FromWide( path );
360 free( path );
364 if( p_sys->psz_tmp_path == NULL )
366 wchar_t *wpath = _wgetcwd( NULL, 0 );
367 if( wpath != NULL )
369 p_sys->psz_tmp_path = FromWide( wpath );
370 free( 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 )
381 len--;
383 p_sys->psz_tmp_path[len] = '\0';
385 #endif
386 if( p_sys->psz_tmp_path != NULL )
387 msg_Dbg( p_input, "using timeshift path: %s", p_sys->psz_tmp_path );
388 else
389 msg_Dbg( p_input, "using default timeshift path" );
391 #if 0
392 #define S(t) msg_Err( p_input, "SIZEOF("#t")=%d", sizeof(t) )
393 S(ts_cmd_t);
394 S(ts_cmd_control_t);
395 S(ts_cmd_send_t);
396 S(ts_cmd_del_t);
397 S(ts_cmd_add_t);
398 #undef S
399 #endif
401 return &p_sys->out;
404 /*****************************************************************************
405 * Internal functions
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 );
422 free( p_sys );
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);
428 ts_cmd_t cmd;
430 es_out_id_t *p_es = malloc( sizeof( *p_es ) );
431 if( !p_es )
432 return NULL;
434 vlc_mutex_lock( &p_sys->lock );
436 TsAutoStop( p_out );
438 if( CmdInitAdd( &cmd, in, p_es, p_fmt, p_sys->b_delayed ) )
440 vlc_mutex_unlock( &p_sys->lock );
441 free( p_es );
442 return NULL;
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 );
449 else
450 CmdExecuteAdd( p_sys->p_out, &cmd );
452 vlc_mutex_unlock( &p_sys->lock );
454 return p_es;
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);
459 ts_cmd_t cmd;
460 int i_ret = VLC_SUCCESS;
462 vlc_mutex_lock( &p_sys->lock );
464 TsAutoStop( p_out );
466 CmdInitSend( &cmd, p_es, p_block );
467 if( p_sys->b_delayed )
468 TsPushCmd( p_sys->p_ts, &cmd );
469 else
470 i_ret = CmdExecuteSend( p_sys->p_out, &cmd) ;
472 vlc_mutex_unlock( &p_sys->lock );
474 return i_ret;
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);
479 ts_cmd_t cmd;
481 vlc_mutex_lock( &p_sys->lock );
483 TsAutoStop( p_out );
485 CmdInitDel( &cmd, p_es );
486 if( p_sys->b_delayed )
487 TsPushCmd( p_sys->p_ts, &cmd );
488 else
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,
503 int i_query, ... )
505 va_list args;
506 int i_result;
508 va_start( args, i_query );
509 i_result = es_out_in_vaControl( p_out, in, i_query, args );
510 va_end( args );
511 return i_result;
514 static int ControlLockedGetEmpty( es_out_t *p_out, input_source_t *in,
515 bool *pb_empty )
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 ) )
520 *pb_empty = false;
521 else
523 int ret = es_out_in_Control( p_sys->p_out, in, ES_OUT_GET_EMPTY, pb_empty );
524 assert( ret == VLC_SUCCESS );
527 return 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 );
536 *pi_wakeup = 0;
538 else
540 *pi_wakeup = es_out_GetWakeup( p_sys->p_out );
543 return VLC_SUCCESS;
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;
551 else
552 *pb_buffering = es_out_GetBuffering( p_sys->p_out );
554 return VLC_SUCCESS;
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);
559 int i_ret;
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 );
565 else
567 i_ret = VLC_EGENERIC;
568 if( !input_priv(p_sys->p_input)->b_can_pace_control )
570 if( !p_sys->b_delayed )
571 TsStart( p_out );
572 if( p_sys->b_delayed )
573 i_ret = TsChangePause( p_sys->p_ts, b_source_paused, b_paused, i_date );
575 else
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
579 * module */
580 msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have pace control" );
584 if( !i_ret )
586 p_sys->b_input_paused_source = b_source_paused;
587 p_sys->b_input_paused = b_paused;
589 return i_ret;
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);
594 int i_ret;
596 if( !p_sys->b_delayed && src_rate == rate )
598 i_ret = es_out_SetRate( p_sys->p_out, src_rate, rate );
600 else
602 i_ret = VLC_EGENERIC;
603 if( !input_priv(p_sys->p_input)->b_can_pace_control )
605 if( !p_sys->b_delayed )
606 TsStart( p_out );
607 if( p_sys->b_delayed )
608 i_ret = TsChangeRate( p_sys->p_ts, src_rate, rate );
610 else
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
614 * module */
615 msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have pace control" );
620 if( !i_ret )
622 p_sys->input_rate_source = src_rate;
623 p_sys->input_rate = rate;
625 return i_ret;
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,
635 va_list args )
637 es_out_sys_t *p_sys = container_of(p_out, es_out_sys_t, out);
639 switch( i_query )
641 /* Pass-through control */
642 case ES_OUT_SET_GROUP:
643 case ES_OUT_SET_PCR:
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:
654 case ES_OUT_SET_ES:
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:
662 ts_cmd_t cmd;
663 if( CmdInitControl( &cmd, in, i_query, args, p_sys->b_delayed ) )
664 return VLC_EGENERIC;
665 if( p_sys->b_delayed )
667 TsPushCmd( p_sys->p_ts, &cmd );
668 return VLC_SUCCESS;
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 )
681 *pb_enabled = true;
682 return VLC_SUCCESS;
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,
715 p_es->p_es, p_hl );
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 )
726 return VLC_EGENERIC;
727 /* fall through */
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 )
737 return VLC_EGENERIC;
739 return es_out_in_Control( p_sys->p_out, in, i_query, b_absolute,
740 i_system );
743 default:
744 vlc_assert_unreachable();
745 return VLC_EGENERIC;
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);
752 int i_ret;
754 vlc_mutex_lock( &p_sys->lock );
756 TsAutoStop( p_out );
758 i_ret = ControlLocked( p_out, in, i_query, args );
760 vlc_mutex_unlock( &p_sys->lock );
762 return i_ret;
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);
769 switch( i_query )
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:
777 ts_cmd_t cmd;
778 if( CmdInitPrivControl( &cmd, i_query, args, p_sys->b_delayed ) )
779 return VLC_EGENERIC;
780 if( p_sys->b_delayed )
782 TsPushCmd( p_sys->p_ts, &cmd );
783 return VLC_SUCCESS;
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);
840 int i_ret;
842 vlc_mutex_lock( &p_sys->lock );
844 TsAutoStop( p_out );
846 i_ret = PrivControlLocked( p_out, i_query, args );
848 vlc_mutex_unlock( &p_sys->lock );
850 return i_ret;
853 static const struct es_out_callbacks es_out_timeshift_cbs =
855 .add = Add,
856 .send = Send,
857 .del = Del,
858 .control = Control,
859 .destroy = Destroy,
860 .priv_control = PrivControl,
863 /*****************************************************************************
865 *****************************************************************************/
866 static void TsDestroy( ts_thread_t *p_ts )
868 free( 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);
873 ts_thread_t *p_ts;
875 assert( !p_sys->b_delayed );
877 p_sys->p_ts = p_ts = calloc(1, sizeof(*p_ts));
878 if( !p_ts )
879 return VLC_EGENERIC;
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" );
903 TsDestroy( p_ts );
905 p_sys->b_delayed = false;
906 return VLC_EGENERIC;
909 return VLC_SUCCESS;
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 ) )
916 return;
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 );
929 for( ;; )
931 ts_cmd_t cmd;
933 if( TsPopCmdLocked( p_ts, &cmd, true ) )
934 break;
936 CmdClean( &cmd );
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 );
943 TsDestroy( p_ts );
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 );
953 if( !p_storage )
955 CmdClean( p_cmd );
956 vlc_mutex_unlock( &p_ts->lock );
957 /* TODO warn the user (but only once) */
958 return;
961 if( !p_ts->p_storage_w )
963 p_ts->p_storage_r = p_ts->p_storage_w = p_storage;
965 else
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 ) )
985 return VLC_EGENERIC;
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;
992 if( !p_next )
993 break;
995 TsStorageDelete( p_ts->p_storage_r );
996 p_ts->p_storage_r = p_next;
999 return VLC_SUCCESS;
1001 static bool TsHasCmd( ts_thread_t *p_ts )
1003 bool b_cmd;
1005 vlc_mutex_lock( &p_ts->lock );
1006 b_cmd = !TsStorageIsEmpty( p_ts->p_storage_r );
1007 vlc_mutex_unlock( &p_ts->lock );
1009 return b_cmd;
1011 static bool TsIsUnused( ts_thread_t *p_ts )
1013 bool b_unused;
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 );
1021 return b_unused;
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 );
1027 int i_ret;
1028 if( b_paused )
1030 assert( !b_source_paused );
1031 i_ret = es_out_SetPauseState( p_ts->p_out, true, true, i_date );
1033 else
1035 i_ret = es_out_SetPauseState( p_ts->p_out, false, false, i_date );
1038 if( !i_ret )
1040 if( !b_paused )
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 );
1053 return i_ret;
1055 static int TsChangeRate( ts_thread_t *p_ts, float src_rate, float rate )
1057 int i_ret;
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;
1064 p_ts->rate = rate;
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 );
1070 return i_ret;
1073 static void *TsRun( void *p_data )
1075 ts_thread_t *p_ts = p_data;
1076 vlc_tick_t i_buffering_date = -1;
1078 for( ;; )
1080 ts_cmd_t cmd;
1081 vlc_tick_t i_deadline;
1082 bool b_buffering;
1084 /* Pop a command to execute */
1085 vlc_mutex_lock( &p_ts->lock );
1086 mutex_cleanup_push( &p_ts->lock );
1088 for( ;; )
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 );
1096 break;
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 */
1110 if( b_buffering )
1111 i_buffering_date = cmd.i_date;
1112 else
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 };
1142 /* Warn back input
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;
1152 vlc_cleanup_pop();
1153 vlc_mutex_unlock( &p_ts->lock );
1155 /* Regulate the speed of command processing to the same one than
1156 * reading */
1157 vlc_cleanup_push( cmd_cleanup_routine, &cmd );
1159 vlc_tick_wait( i_deadline );
1161 vlc_cleanup_pop();
1163 /* Execute the command */
1164 const int canc = vlc_savecancel();
1165 switch( cmd.i_type )
1167 case C_ADD:
1168 CmdExecuteAdd( p_ts->p_out, &cmd );
1169 CmdCleanAdd( &cmd );
1170 break;
1171 case C_SEND:
1172 CmdExecuteSend( p_ts->p_out, &cmd );
1173 CmdCleanSend( &cmd );
1174 break;
1175 case C_CONTROL:
1176 CmdExecuteControl( p_ts->p_out, &cmd );
1177 CmdCleanControl( &cmd );
1178 break;
1179 case C_DEL:
1180 CmdExecuteDel( p_ts->p_out, &cmd );
1181 break;
1182 default:
1183 vlc_assert_unreachable();
1184 break;
1186 vlc_restorecancel( canc );
1189 return NULL;
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) )
1199 return NULL;
1201 char *psz_file;
1202 int fd = GetTmpFile( &psz_file, psz_tmp_path );
1203 if( fd == -1 )
1205 free( p_storage );
1206 return NULL;
1209 p_storage->p_filew = fdopen( fd, "w+b" );
1210 if( p_storage->p_filew == NULL )
1212 vlc_close( fd );
1213 vlc_unlink( psz_file );
1214 goto error;
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 );
1222 goto error;
1225 #ifndef _WIN32
1226 vlc_unlink( psz_file );
1227 free( psz_file );
1228 #else
1229 p_storage->psz_file = psz_file;
1230 #endif
1231 p_storage->p_next = NULL;
1233 /* */
1234 p_storage->i_file_max = i_tmp_size_max;
1235 p_storage->i_file_size = 0;
1237 /* */
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 );
1247 return NULL;
1249 return p_storage;
1250 error:
1251 free( psz_file );
1252 free( p_storage );
1253 return NULL;
1256 static void TsStorageDelete( ts_storage_t *p_storage )
1258 while( p_storage->i_cmd_r < p_storage->i_cmd_w )
1260 ts_cmd_t cmd;
1262 TsStoragePopCmd( p_storage, &cmd, true );
1264 CmdClean( &cmd );
1266 free( p_storage->p_cmd );
1268 fclose( p_storage->p_filer );
1269 fclose( p_storage->p_filew );
1270 #ifdef _WIN32
1271 vlc_unlink( p_storage->psz_file );
1272 free( p_storage->psz_file );
1273 #endif
1274 free( p_storage );
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 )
1281 return;
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) );
1286 if( p_new )
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 )
1296 return true;
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 );
1320 return;
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 );
1328 return;
1331 p_storage->i_file_size += p_block->i_buffer;
1332 block_Release( p_block );
1334 if( b_flush )
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 )
1346 block_t block;
1348 if( !b_flush &&
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 );
1353 if( p_block )
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;
1364 else
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 )
1379 case C_ADD:
1380 CmdCleanAdd( p_cmd );
1381 break;
1382 case C_SEND:
1383 CmdCleanSend( p_cmd );
1384 break;
1385 case C_CONTROL:
1386 CmdCleanControl( p_cmd );
1387 break;
1388 case C_DEL:
1389 break;
1390 default:
1391 vlc_assert_unreachable();
1392 break;
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;
1402 if( b_copy )
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;
1410 else
1412 p_cmd->u.add.p_fmt = (es_format_t*)p_fmt;
1413 p_cmd->u.add.in = in;
1415 return VLC_SUCCESS;
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;
1443 if( p_block )
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;
1462 return VLC_SUCCESS;
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;
1478 if( b_copy )
1479 p_cmd->u.control.in = in ? input_source_Hold( in ) : NULL;
1480 else
1481 p_cmd->u.control.in = in;
1483 switch( i_query )
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 );
1489 break;
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 );
1494 break;
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 );
1499 break;
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 );
1504 break;
1506 case ES_OUT_RESET_PCR: /* no arg */
1507 break;
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 * );
1516 if( b_copy )
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 );
1523 else
1525 /* The cast is only needed to avoid warning */
1526 p_cmd->u.control.u.int_meta.p_meta = (vlc_meta_t*)p_meta;
1528 break;
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 * );
1536 if( b_copy )
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;
1542 else
1544 /* The cast is only needed to avoid warning */
1545 p_cmd->u.control.u.int_epg.p_epg = (vlc_epg_t*)p_epg;
1547 break;
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 * );
1554 if( b_copy )
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;
1560 else
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;
1565 break;
1567 case ES_OUT_SET_EPG_TIME: /* arg1=int64_t (seconds) */
1568 p_cmd->u.control.u.i_i64 = va_arg( args, int64_t );
1569 break;
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 * );
1577 break;
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 );
1582 break;
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 );
1587 break;
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 * );
1594 if( b_copy )
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 );
1601 else
1603 p_cmd->u.control.u.es_fmt.p_fmt = p_fmt;
1605 break;
1607 default:
1608 vlc_assert_unreachable();
1609 return VLC_EGENERIC;
1612 return VLC_SUCCESS;
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;
1619 switch( i_query )
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 );
1679 default:
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 );
1695 break;
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 );
1699 break;
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 );
1703 break;
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 );
1710 break;
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;
1721 switch( 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 );
1726 break;
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;
1736 break;
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;
1749 break;
1751 case ES_OUT_PRIV_SET_EOS: /* no arg */
1752 break;
1753 default: vlc_assert_unreachable();
1756 return VLC_SUCCESS;
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;
1763 switch( 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 )
1786 if( dirname != NULL
1787 && asprintf( filename, "%s"DIR_SEP PACKAGE_NAME"-timeshift.XXXXXX",
1788 dirname ) >= 0 )
1790 vlc_mkdir( dirname, 0700 );
1792 int fd = vlc_mkstemp( *filename );
1793 if( fd != -1 )
1794 return fd;
1796 free( *filename );
1799 *filename = strdup( DIR_SEP"tmp"DIR_SEP PACKAGE_NAME"-timeshift.XXXXXX" );
1800 if( unlikely(*filename == NULL) )
1801 return -1;
1803 int fd = vlc_mkstemp( *filename );
1804 if( fd != -1 )
1805 return fd;
1807 free( *filename );
1808 return -1;