input: change rate argument
[vlc.git] / src / input / es_out_timeshift.c
blobee51662b3fc43ce39c10aa3caa349bf24729242b
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_input.h>
47 #include <vlc_es_out.h>
48 #include <vlc_block.h>
49 #include "input_internal.h"
50 #include "es_out.h"
52 /*****************************************************************************
53 * Local prototypes
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__))
59 #else
60 # define attribute_packed
61 #endif
63 enum
65 C_ADD,
66 C_SEND,
67 C_DEL,
68 C_CONTROL,
71 typedef struct attribute_packed
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 int i_query;
93 union
95 bool b_bool;
96 int i_int;
97 int64_t i_i64;
98 es_out_id_t *p_es;
99 struct
101 int i_int;
102 int64_t i_i64;
103 } int_i64;
104 struct
106 int i_int;
107 vlc_meta_t *p_meta;
108 } int_meta;
109 struct
111 int i_int;
112 vlc_epg_t *p_epg;
113 } int_epg;
114 struct
116 int i_int;
117 vlc_epg_event_t *p_evt;
118 } int_epg_evt;
119 struct
121 es_out_id_t *p_es;
122 bool b_bool;
123 } es_bool;
124 struct
126 es_out_id_t *p_es;
127 es_format_t *p_fmt;
128 } es_fmt;
129 struct
131 int i_cat;
132 int i_policy;
133 } es_policy;
134 struct
136 /* FIXME Really too big (double make the whole thing too big) */
137 double f_position;
138 vlc_tick_t i_time;
139 vlc_tick_t i_length;
140 } times;
141 struct
143 vlc_tick_t i_pts_delay;
144 vlc_tick_t i_pts_jitter;
145 int i_cr_average;
146 } jitter;
147 } u;
148 } ts_cmd_control_t;
150 typedef struct attribute_packed
152 int8_t i_type;
153 vlc_tick_t i_date;
154 union
156 ts_cmd_add_t add;
157 ts_cmd_del_t del;
158 ts_cmd_send_t send;
159 ts_cmd_control_t control;
160 } u;
161 } ts_cmd_t;
163 typedef struct ts_storage_t ts_storage_t;
164 struct ts_storage_t
166 ts_storage_t *p_next;
168 /* */
169 #ifdef _WIN32
170 char *psz_file; /* Filename */
171 #endif
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 */
177 /* */
178 int i_cmd_r;
179 int i_cmd_w;
180 int i_cmd_max;
181 ts_cmd_t *p_cmd;
184 typedef struct
186 vlc_thread_t thread;
187 input_thread_t *p_input;
188 es_out_t *p_out;
189 int64_t i_tmp_size_max;
190 const char *psz_tmp_path;
192 /* Lock for all following fields */
193 vlc_mutex_t lock;
194 vlc_cond_t wait;
196 /* */
197 bool b_paused;
198 vlc_tick_t i_pause_date;
200 /* */
201 float rate;
202 float rate_source;
203 vlc_tick_t i_rate_date;
204 vlc_tick_t i_rate_delay;
206 /* */
207 vlc_tick_t i_buffering_delay;
209 /* */
210 ts_storage_t *p_storage_r;
211 ts_storage_t *p_storage_w;
213 vlc_tick_t i_cmd_delay;
215 } ts_thread_t;
217 struct es_out_id_t
219 es_out_id_t *p_es;
222 typedef struct
224 input_thread_t *p_input;
225 es_out_t *p_out;
227 /* Configuration */
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 */
232 vlc_mutex_t lock;
234 /* */
235 bool b_delayed;
236 ts_thread_t *p_ts;
238 /* */
239 bool b_input_paused;
240 bool b_input_paused_source;
241 float input_rate;
242 float input_rate_source;
244 /* */
245 int i_es;
246 es_out_id_t **pp_es;
248 es_out_t out;
249 } es_out_sys_t;
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 );
282 /* */
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 * );
293 /* File helpers */
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) );
304 if( !p_sys )
305 return NULL;
307 p_sys->out.cbs = &es_out_timeshift_cbs;
309 /* */
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;
320 p_sys->p_ts = NULL;
322 TAB_INIT( p_sys->i_es, p_sys->pp_es );
324 /* */
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;
328 else
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 );
338 if( count > 0 )
340 TCHAR *path = vlc_alloc( count + 1, sizeof(TCHAR) );
341 if( path != NULL )
343 DWORD ret = GetTempPath( count + 1, path );
344 if( ret != 0 && ret <= count )
345 p_sys->psz_tmp_path = FromT( path );
346 free( path );
350 if( p_sys->psz_tmp_path == NULL )
352 wchar_t *wpath = _wgetcwd( NULL, 0 );
353 if( wpath != NULL )
355 p_sys->psz_tmp_path = FromWide( wpath );
356 free( 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 )
367 len--;
369 p_sys->psz_tmp_path[len] = '\0';
371 #endif
372 if( p_sys->psz_tmp_path != NULL )
373 msg_Dbg( p_input, "using timeshift path: %s", p_sys->psz_tmp_path );
374 else
375 msg_Dbg( p_input, "using default timeshift path" );
377 #if 0
378 #define S(t) msg_Err( p_input, "SIZEOF("#t")=%d", sizeof(t) )
379 S(ts_cmd_t);
380 S(ts_cmd_control_t);
381 S(ts_cmd_send_t);
382 S(ts_cmd_del_t);
383 S(ts_cmd_add_t);
384 #undef S
385 #endif
387 return &p_sys->out;
390 /*****************************************************************************
391 * Internal functions
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 );
409 free( p_sys );
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);
415 ts_cmd_t cmd;
417 es_out_id_t *p_es = malloc( sizeof( *p_es ) );
418 if( !p_es )
419 return NULL;
421 vlc_mutex_lock( &p_sys->lock );
423 TsAutoStop( p_out );
425 if( CmdInitAdd( &cmd, p_es, p_fmt, p_sys->b_delayed ) )
427 vlc_mutex_unlock( &p_sys->lock );
428 free( p_es );
429 return NULL;
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 );
436 else
437 CmdExecuteAdd( p_sys->p_out, &cmd );
439 vlc_mutex_unlock( &p_sys->lock );
441 return p_es;
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);
446 ts_cmd_t cmd;
447 int i_ret = VLC_SUCCESS;
449 vlc_mutex_lock( &p_sys->lock );
451 TsAutoStop( p_out );
453 CmdInitSend( &cmd, p_es, p_block );
454 if( p_sys->b_delayed )
455 TsPushCmd( p_sys->p_ts, &cmd );
456 else
457 i_ret = CmdExecuteSend( p_sys->p_out, &cmd) ;
459 vlc_mutex_unlock( &p_sys->lock );
461 return i_ret;
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);
466 ts_cmd_t cmd;
468 vlc_mutex_lock( &p_sys->lock );
470 TsAutoStop( p_out );
472 CmdInitDel( &cmd, p_es );
473 if( p_sys->b_delayed )
474 TsPushCmd( p_sys->p_ts, &cmd );
475 else
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 ) )
488 *pb_empty = false;
489 else
490 *pb_empty = es_out_GetEmpty( p_sys->p_out );
492 return VLC_SUCCESS;
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 );
501 *pi_wakeup = 0;
503 else
505 *pi_wakeup = es_out_GetWakeup( p_sys->p_out );
508 return VLC_SUCCESS;
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;
516 else
517 *pb_buffering = es_out_GetBuffering( p_sys->p_out );
519 return VLC_SUCCESS;
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);
524 int i_ret;
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 );
530 else
532 i_ret = VLC_EGENERIC;
533 if( !input_priv(p_sys->p_input)->b_can_pace_control )
535 if( !p_sys->b_delayed )
536 TsStart( p_out );
537 if( p_sys->b_delayed )
538 i_ret = TsChangePause( p_sys->p_ts, b_source_paused, b_paused, i_date );
540 else
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
544 * module */
545 msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have pace control" );
549 if( !i_ret )
551 p_sys->b_input_paused_source = b_source_paused;
552 p_sys->b_input_paused = b_paused;
554 return i_ret;
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);
559 int i_ret;
561 if( !p_sys->b_delayed && src_rate == rate )
563 i_ret = es_out_SetRate( p_sys->p_out, src_rate, rate );
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 = TsChangeRate( p_sys->p_ts, src_rate, rate );
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" );
585 if( !i_ret )
587 p_sys->input_rate_source = src_rate;
588 p_sys->input_rate = rate;
590 return i_ret;
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);
603 switch( i_query )
605 /* Pass-through control */
606 case ES_OUT_SET_MODE:
607 case ES_OUT_SET_GROUP:
608 case ES_OUT_SET_PCR:
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:
619 case ES_OUT_SET_ES:
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:
628 case ES_OUT_SET_EOS:
630 ts_cmd_t cmd;
631 if( CmdInitControl( &cmd, i_query, args, p_sys->b_delayed ) )
632 return VLC_EGENERIC;
633 if( p_sys->b_delayed )
635 TsPushCmd( p_sys->p_ts, &cmd );
636 return VLC_SUCCESS;
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 )
649 *pb_enabled = true;
650 return VLC_SUCCESS;
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,
682 p_es->p_es, p_hl );
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 )
722 return VLC_EGENERIC;
723 /* fall through */
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 )
734 return VLC_EGENERIC;
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:
750 default:
751 vlc_assert_unreachable();
752 return VLC_EGENERIC;
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);
758 int i_ret;
760 vlc_mutex_lock( &p_sys->lock );
762 TsAutoStop( p_out );
764 i_ret = ControlLocked( p_out, i_query, args );
766 vlc_mutex_unlock( &p_sys->lock );
768 return i_ret;
771 static const struct es_out_callbacks es_out_timeshift_cbs =
773 .add = Add,
774 .send = Send,
775 .del = Del,
776 .control = Control,
777 .destroy = Destroy,
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 );
787 free( p_ts );
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);
792 ts_thread_t *p_ts;
794 assert( !p_sys->b_delayed );
796 p_sys->p_ts = p_ts = calloc(1, sizeof(*p_ts));
797 if( !p_ts )
798 return VLC_EGENERIC;
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" );
822 TsDestroy( p_ts );
824 p_sys->b_delayed = false;
825 return VLC_EGENERIC;
828 return VLC_SUCCESS;
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 ) )
835 return;
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 );
848 for( ;; )
850 ts_cmd_t cmd;
852 if( TsPopCmdLocked( p_ts, &cmd, true ) )
853 break;
855 CmdClean( &cmd );
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 );
862 TsDestroy( p_ts );
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 );
872 if( !p_storage )
874 CmdClean( p_cmd );
875 vlc_mutex_unlock( &p_ts->lock );
876 /* TODO warn the user (but only once) */
877 return;
880 if( !p_ts->p_storage_w )
882 p_ts->p_storage_r = p_ts->p_storage_w = p_storage;
884 else
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 ) )
904 return VLC_EGENERIC;
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;
911 if( !p_next )
912 break;
914 TsStorageDelete( p_ts->p_storage_r );
915 p_ts->p_storage_r = p_next;
918 return VLC_SUCCESS;
920 static bool TsHasCmd( ts_thread_t *p_ts )
922 bool b_cmd;
924 vlc_mutex_lock( &p_ts->lock );
925 b_cmd = !TsStorageIsEmpty( p_ts->p_storage_r );
926 vlc_mutex_unlock( &p_ts->lock );
928 return b_cmd;
930 static bool TsIsUnused( ts_thread_t *p_ts )
932 bool b_unused;
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 );
940 return b_unused;
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 );
946 int i_ret;
947 if( b_paused )
949 assert( !b_source_paused );
950 i_ret = es_out_SetPauseState( p_ts->p_out, true, true, i_date );
952 else
954 i_ret = es_out_SetPauseState( p_ts->p_out, false, false, i_date );
957 if( !i_ret )
959 if( !b_paused )
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 );
972 return i_ret;
974 static int TsChangeRate( ts_thread_t *p_ts, float src_rate, float rate )
976 int i_ret;
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;
983 p_ts->rate = rate;
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 );
989 return i_ret;
992 static void *TsRun( void *p_data )
994 ts_thread_t *p_ts = p_data;
995 vlc_tick_t i_buffering_date = -1;
997 for( ;; )
999 ts_cmd_t cmd;
1000 vlc_tick_t i_deadline;
1001 bool b_buffering;
1003 /* Pop a command to execute */
1004 vlc_mutex_lock( &p_ts->lock );
1005 mutex_cleanup_push( &p_ts->lock );
1007 for( ;; )
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 );
1015 break;
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 */
1029 if( b_buffering )
1030 i_buffering_date = cmd.i_date;
1031 else
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 };
1061 /* Warn back input
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;
1071 vlc_cleanup_pop();
1072 vlc_mutex_unlock( &p_ts->lock );
1074 /* Regulate the speed of command processing to the same one than
1075 * reading */
1076 vlc_cleanup_push( cmd_cleanup_routine, &cmd );
1078 vlc_tick_wait( i_deadline );
1080 vlc_cleanup_pop();
1082 /* Execute the command */
1083 const int canc = vlc_savecancel();
1084 switch( cmd.i_type )
1086 case C_ADD:
1087 CmdExecuteAdd( p_ts->p_out, &cmd );
1088 CmdCleanAdd( &cmd );
1089 break;
1090 case C_SEND:
1091 CmdExecuteSend( p_ts->p_out, &cmd );
1092 CmdCleanSend( &cmd );
1093 break;
1094 case C_CONTROL:
1095 CmdExecuteControl( p_ts->p_out, &cmd );
1096 CmdCleanControl( &cmd );
1097 break;
1098 case C_DEL:
1099 CmdExecuteDel( p_ts->p_out, &cmd );
1100 break;
1101 default:
1102 vlc_assert_unreachable();
1103 break;
1105 vlc_restorecancel( canc );
1108 return NULL;
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) )
1118 return NULL;
1120 char *psz_file;
1121 int fd = GetTmpFile( &psz_file, psz_tmp_path );
1122 if( fd == -1 )
1124 free( p_storage );
1125 return NULL;
1128 p_storage->p_filew = fdopen( fd, "w+b" );
1129 if( p_storage->p_filew == NULL )
1131 vlc_close( fd );
1132 vlc_unlink( psz_file );
1133 goto error;
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 );
1141 goto error;
1144 #ifndef _WIN32
1145 vlc_unlink( psz_file );
1146 free( psz_file );
1147 #else
1148 p_storage->psz_file = psz_file;
1149 #endif
1150 p_storage->p_next = NULL;
1152 /* */
1153 p_storage->i_file_max = i_tmp_size_max;
1154 p_storage->i_file_size = 0;
1156 /* */
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 );
1166 return NULL;
1168 return p_storage;
1169 error:
1170 free( psz_file );
1171 free( p_storage );
1172 return NULL;
1175 static void TsStorageDelete( ts_storage_t *p_storage )
1177 while( p_storage->i_cmd_r < p_storage->i_cmd_w )
1179 ts_cmd_t cmd;
1181 TsStoragePopCmd( p_storage, &cmd, true );
1183 CmdClean( &cmd );
1185 free( p_storage->p_cmd );
1187 fclose( p_storage->p_filer );
1188 fclose( p_storage->p_filew );
1189 #ifdef _WIN32
1190 vlc_unlink( p_storage->psz_file );
1191 free( p_storage->psz_file );
1192 #endif
1193 free( p_storage );
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 )
1200 return;
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) );
1205 if( p_new )
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 )
1215 return true;
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 );
1239 return;
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 );
1247 return;
1250 p_storage->i_file_size += p_block->i_buffer;
1251 block_Release( p_block );
1253 if( b_flush )
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 )
1265 block_t block;
1267 if( !b_flush &&
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 );
1272 if( p_block )
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;
1283 else
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 )
1298 case C_ADD:
1299 CmdCleanAdd( p_cmd );
1300 break;
1301 case C_SEND:
1302 CmdCleanSend( p_cmd );
1303 break;
1304 case C_CONTROL:
1305 CmdCleanControl( p_cmd );
1306 break;
1307 case C_DEL:
1308 break;
1309 default:
1310 vlc_assert_unreachable();
1311 break;
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;
1320 if( b_copy )
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 );
1327 else
1329 p_cmd->u.add.p_fmt = (es_format_t*)p_fmt;
1331 return VLC_SUCCESS;
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;
1356 if( p_block )
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;
1375 return VLC_SUCCESS;
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;
1390 switch( 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 );
1397 break;
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 );
1402 break;
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 );
1407 break;
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 );
1412 break;
1414 case ES_OUT_RESET_PCR: /* no arg */
1415 case ES_OUT_SET_EOS:
1416 break;
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 * );
1425 if( b_copy )
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 );
1432 else
1434 /* The cast is only needed to avoid warning */
1435 p_cmd->u.control.u.int_meta.p_meta = (vlc_meta_t*)p_meta;
1437 break;
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 * );
1445 if( b_copy )
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;
1451 else
1453 /* The cast is only needed to avoid warning */
1454 p_cmd->u.control.u.int_epg.p_epg = (vlc_epg_t*)p_epg;
1456 break;
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 * );
1463 if( b_copy )
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;
1469 else
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;
1474 break;
1476 case ES_OUT_SET_EPG_TIME: /* arg1=int64_t (seconds) */
1477 p_cmd->u.control.u.i_i64 = va_arg( args, int64_t );
1478 break;
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 * );
1486 break;
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 );
1491 break;
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 );
1496 break;
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 * );
1503 if( b_copy )
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 );
1510 else
1512 p_cmd->u.control.u.es_fmt.p_fmt = p_fmt;
1514 break;
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;
1525 break;
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;
1536 break;
1539 default:
1540 vlc_assert_unreachable();
1541 return VLC_EGENERIC;
1544 return VLC_SUCCESS;
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;
1550 switch( 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 );
1621 default:
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 );
1634 break;
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 );
1638 break;
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 );
1642 break;
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 );
1649 break;
1653 static int GetTmpFile( char **filename, const char *dirname )
1655 if( dirname != NULL
1656 && asprintf( filename, "%s"DIR_SEP PACKAGE_NAME"-timeshift.XXXXXX",
1657 dirname ) >= 0 )
1659 vlc_mkdir( dirname, 0700 );
1661 int fd = vlc_mkstemp( *filename );
1662 if( fd != -1 )
1663 return fd;
1665 free( *filename );
1668 *filename = strdup( DIR_SEP"tmp"DIR_SEP PACKAGE_NAME"-timeshift.XXXXXX" );
1669 if( unlikely(*filename == NULL) )
1670 return -1;
1672 int fd = vlc_mkstemp( *filename );
1673 if( fd != -1 )
1674 return fd;
1676 free( *filename );
1677 return -1;