video_filter: erase: use C99 loop declarations
[vlc.git] / src / input / es_out_timeshift.c
blob33a965e76ecf9c6e67c3fe8323cc43a78ef1af8d
1 /*****************************************************************************
2 * es_out_timeshift.c: Es Out timeshift.
3 *****************************************************************************
4 * Copyright (C) 2008 Laurent Aimar
5 * $Id$
7 * Authors: Laurent Aimar < fenrir _AT_ videolan _DOT_ org>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <assert.h>
34 #include <errno.h>
35 #if defined (_WIN32)
36 # include <direct.h>
37 #endif
38 #include <sys/stat.h>
39 #include <unistd.h>
41 #include <vlc_common.h>
42 #include <vlc_fs.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"
51 #include "es_out_timeshift.h"
53 /*****************************************************************************
54 * Local prototypes
55 *****************************************************************************/
57 /* XXX attribute_packed is (and MUST be) used ONLY to reduce memory usage */
58 #ifdef HAVE_ATTRIBUTE_PACKED
59 # define attribute_packed __attribute__((__packed__))
60 #else
61 # define attribute_packed
62 #endif
64 enum
66 C_ADD,
67 C_SEND,
68 C_DEL,
69 C_CONTROL,
72 typedef struct attribute_packed
74 es_out_id_t *p_es;
75 es_format_t *p_fmt;
76 } ts_cmd_add_t;
78 typedef struct attribute_packed
80 es_out_id_t *p_es;
81 } ts_cmd_del_t;
83 typedef struct attribute_packed
85 es_out_id_t *p_es;
86 block_t *p_block;
87 int i_offset; /* We do not use file > INT_MAX */
88 } ts_cmd_send_t;
90 typedef struct attribute_packed
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 es_out_id_t *p_es;
118 bool b_bool;
119 } es_bool;
120 struct
122 es_out_id_t *p_es;
123 es_format_t *p_fmt;
124 } es_fmt;
125 struct
127 /* FIXME Really too big (double make the whole thing too big) */
128 double f_position;
129 mtime_t i_time;
130 mtime_t i_length;
131 } times;
132 struct
134 mtime_t i_pts_delay;
135 mtime_t i_pts_jitter;
136 int i_cr_average;
137 } jitter;
138 } u;
139 } ts_cmd_control_t;
141 typedef struct attribute_packed
143 int8_t i_type;
144 mtime_t i_date;
145 union
147 ts_cmd_add_t add;
148 ts_cmd_del_t del;
149 ts_cmd_send_t send;
150 ts_cmd_control_t control;
151 } u;
152 } ts_cmd_t;
154 typedef struct ts_storage_t ts_storage_t;
155 struct ts_storage_t
157 ts_storage_t *p_next;
159 /* */
160 #ifdef _WIN32
161 char *psz_file; /* Filename */
162 #endif
163 size_t i_file_max; /* Max size in bytes */
164 int64_t i_file_size;/* Current size in bytes */
165 FILE *p_filew; /* FILE handle for data writing */
166 FILE *p_filer; /* FILE handle for data reading */
168 /* */
169 int i_cmd_r;
170 int i_cmd_w;
171 int i_cmd_max;
172 ts_cmd_t *p_cmd;
175 typedef struct
177 vlc_thread_t thread;
178 input_thread_t *p_input;
179 es_out_t *p_out;
180 int64_t i_tmp_size_max;
181 const char *psz_tmp_path;
183 /* Lock for all following fields */
184 vlc_mutex_t lock;
185 vlc_cond_t wait;
187 /* */
188 bool b_paused;
189 mtime_t i_pause_date;
191 /* */
192 int i_rate;
193 int i_rate_source;
194 mtime_t i_rate_date;
195 mtime_t i_rate_delay;
197 /* */
198 mtime_t i_buffering_delay;
200 /* */
201 ts_storage_t *p_storage_r;
202 ts_storage_t *p_storage_w;
204 mtime_t i_cmd_delay;
206 } ts_thread_t;
208 struct es_out_id_t
210 es_out_id_t *p_es;
213 struct es_out_sys_t
215 input_thread_t *p_input;
216 es_out_t *p_out;
218 /* Configuration */
219 int64_t i_tmp_size_max; /* Maximal temporary file size in byte */
220 char *psz_tmp_path; /* Path for temporary files */
222 /* Lock for all following fields */
223 vlc_mutex_t lock;
225 /* */
226 bool b_delayed;
227 ts_thread_t *p_ts;
229 /* */
230 bool b_input_paused;
231 bool b_input_paused_source;
232 int i_input_rate;
233 int i_input_rate_source;
235 /* */
236 int i_es;
237 es_out_id_t **pp_es;
240 static es_out_id_t *Add ( es_out_t *, const es_format_t * );
241 static int Send ( es_out_t *, es_out_id_t *, block_t * );
242 static void Del ( es_out_t *, es_out_id_t * );
243 static int Control( es_out_t *, int i_query, va_list );
244 static void Destroy( es_out_t * );
246 static int TsStart( es_out_t * );
247 static void TsAutoStop( es_out_t * );
249 static void TsStop( ts_thread_t * );
250 static void TsPushCmd( ts_thread_t *, ts_cmd_t * );
251 static int TsPopCmdLocked( ts_thread_t *, ts_cmd_t *, bool b_flush );
252 static bool TsHasCmd( ts_thread_t * );
253 static bool TsIsUnused( ts_thread_t * );
254 static int TsChangePause( ts_thread_t *, bool b_source_paused, bool b_paused, mtime_t i_date );
255 static int TsChangeRate( ts_thread_t *, int i_src_rate, int i_rate );
257 static void *TsRun( void * );
259 static ts_storage_t *TsStorageNew( const char *psz_path, int64_t i_tmp_size_max );
260 static void TsStorageDelete( ts_storage_t * );
261 static void TsStoragePack( ts_storage_t *p_storage );
262 static bool TsStorageIsFull( ts_storage_t *, const ts_cmd_t *p_cmd );
263 static bool TsStorageIsEmpty( ts_storage_t * );
264 static void TsStoragePushCmd( ts_storage_t *, const ts_cmd_t *p_cmd, bool b_flush );
265 static void TsStoragePopCmd( ts_storage_t *p_storage, ts_cmd_t *p_cmd, bool b_flush );
267 static void CmdClean( ts_cmd_t * );
268 static void cmd_cleanup_routine( void *p ) { CmdClean( p ); }
270 static int CmdInitAdd ( ts_cmd_t *, es_out_id_t *, const es_format_t *, bool b_copy );
271 static void CmdInitSend ( ts_cmd_t *, es_out_id_t *, block_t * );
272 static int CmdInitDel ( ts_cmd_t *, es_out_id_t * );
273 static int CmdInitControl( ts_cmd_t *, int i_query, va_list, bool b_copy );
275 /* */
276 static void CmdCleanAdd ( ts_cmd_t * );
277 static void CmdCleanSend ( ts_cmd_t * );
278 static void CmdCleanControl( ts_cmd_t *p_cmd );
280 /* XXX these functions will take the destination es_out_t */
281 static void CmdExecuteAdd ( es_out_t *, ts_cmd_t * );
282 static int CmdExecuteSend ( es_out_t *, ts_cmd_t * );
283 static void CmdExecuteDel ( es_out_t *, ts_cmd_t * );
284 static int CmdExecuteControl( es_out_t *, ts_cmd_t * );
286 /* File helpers */
287 static int GetTmpFile( char **ppsz_file, const char *psz_path );
289 /*****************************************************************************
290 * input_EsOutTimeshiftNew:
291 *****************************************************************************/
292 es_out_t *input_EsOutTimeshiftNew( input_thread_t *p_input, es_out_t *p_next_out, int i_rate )
294 es_out_t *p_out = malloc( sizeof(*p_out) );
295 if( !p_out )
296 return NULL;
298 es_out_sys_t *p_sys = malloc( sizeof(*p_sys) );
299 if( !p_sys )
301 free( p_out );
302 return NULL;
305 /* */
306 p_out->pf_add = Add;
307 p_out->pf_send = Send;
308 p_out->pf_del = Del;
309 p_out->pf_control = Control;
310 p_out->pf_destroy = Destroy;
311 p_out->p_sys = p_sys;
313 /* */
314 p_sys->b_input_paused = false;
315 p_sys->b_input_paused_source = false;
316 p_sys->p_input = p_input;
317 p_sys->i_input_rate = i_rate;
318 p_sys->i_input_rate_source = i_rate;
320 p_sys->p_out = p_next_out;
321 vlc_mutex_init_recursive( &p_sys->lock );
323 p_sys->b_delayed = false;
324 p_sys->p_ts = NULL;
326 TAB_INIT( p_sys->i_es, p_sys->pp_es );
328 /* */
329 const int i_tmp_size_max = var_CreateGetInteger( p_input, "input-timeshift-granularity" );
330 if( i_tmp_size_max < 0 )
331 p_sys->i_tmp_size_max = 50*1024*1024;
332 else
333 p_sys->i_tmp_size_max = __MAX( i_tmp_size_max, 1*1024*1024 );
334 msg_Dbg( p_input, "using timeshift granularity of %d MiB",
335 (int)p_sys->i_tmp_size_max/(1024*1024) );
337 p_sys->psz_tmp_path = var_InheritString( p_input, "input-timeshift-path" );
338 #if defined (_WIN32) && !VLC_WINSTORE_APP
339 if( p_sys->psz_tmp_path == NULL )
341 const DWORD count = GetTempPath( 0, NULL );
342 if( count > 0 )
344 TCHAR *path = malloc( (count + 1) * sizeof(TCHAR) );
345 if( path != NULL )
347 DWORD ret = GetTempPath( count + 1, path );
348 if( ret != 0 && ret <= count )
349 p_sys->psz_tmp_path = FromT( path );
350 free( path );
354 if( p_sys->psz_tmp_path == NULL )
356 wchar_t *wpath = _wgetcwd( NULL, 0 );
357 if( wpath != NULL )
359 p_sys->psz_tmp_path = FromWide( wpath );
360 free( wpath );
363 if( p_sys->psz_tmp_path == NULL )
364 p_sys->psz_tmp_path = strdup( "C:" );
366 if( p_sys->psz_tmp_path != NULL )
368 size_t len = strlen( p_sys->psz_tmp_path );
370 while( len > 0 && p_sys->psz_tmp_path[len - 1] == DIR_SEP_CHAR )
371 len--;
373 p_sys->psz_tmp_path[len] = '\0';
375 #endif
376 if( p_sys->psz_tmp_path != NULL )
377 msg_Dbg( p_input, "using timeshift path: %s", p_sys->psz_tmp_path );
378 else
379 msg_Dbg( p_input, "using default timeshift path" );
381 #if 0
382 #define S(t) msg_Err( p_input, "SIZEOF("#t")=%d", sizeof(t) )
383 S(ts_cmd_t);
384 S(ts_cmd_control_t);
385 S(ts_cmd_send_t);
386 S(ts_cmd_del_t);
387 S(ts_cmd_add_t);
388 #undef S
389 #endif
391 return p_out;
394 /*****************************************************************************
395 * Internal functions
396 *****************************************************************************/
397 static void Destroy( es_out_t *p_out )
399 es_out_sys_t *p_sys = p_out->p_sys;
401 if( p_sys->b_delayed )
403 TsStop( p_sys->p_ts );
404 p_sys->b_delayed = false;
407 while( p_sys->i_es > 0 )
408 Del( p_out, p_sys->pp_es[0] );
409 TAB_CLEAN( p_sys->i_es, p_sys->pp_es );
411 free( p_sys->psz_tmp_path );
412 vlc_mutex_destroy( &p_sys->lock );
413 free( p_sys );
414 free( p_out );
417 static es_out_id_t *Add( es_out_t *p_out, const es_format_t *p_fmt )
419 es_out_sys_t *p_sys = p_out->p_sys;
420 ts_cmd_t cmd;
422 es_out_id_t *p_es = malloc( sizeof( *p_es ) );
423 if( !p_es )
424 return NULL;
426 vlc_mutex_lock( &p_sys->lock );
428 TsAutoStop( p_out );
430 if( CmdInitAdd( &cmd, p_es, p_fmt, p_sys->b_delayed ) )
432 vlc_mutex_unlock( &p_sys->lock );
433 free( p_es );
434 return NULL;
437 TAB_APPEND( p_sys->i_es, p_sys->pp_es, p_es );
439 if( p_sys->b_delayed )
440 TsPushCmd( p_sys->p_ts, &cmd );
441 else
442 CmdExecuteAdd( p_sys->p_out, &cmd );
444 vlc_mutex_unlock( &p_sys->lock );
446 return p_es;
448 static int Send( es_out_t *p_out, es_out_id_t *p_es, block_t *p_block )
450 es_out_sys_t *p_sys = p_out->p_sys;
451 ts_cmd_t cmd;
452 int i_ret = VLC_SUCCESS;
454 vlc_mutex_lock( &p_sys->lock );
456 TsAutoStop( p_out );
458 CmdInitSend( &cmd, p_es, p_block );
459 if( p_sys->b_delayed )
460 TsPushCmd( p_sys->p_ts, &cmd );
461 else
462 i_ret = CmdExecuteSend( p_sys->p_out, &cmd) ;
464 vlc_mutex_unlock( &p_sys->lock );
466 return i_ret;
468 static void Del( es_out_t *p_out, es_out_id_t *p_es )
470 es_out_sys_t *p_sys = p_out->p_sys;
471 ts_cmd_t cmd;
473 vlc_mutex_lock( &p_sys->lock );
475 TsAutoStop( p_out );
477 CmdInitDel( &cmd, p_es );
478 if( p_sys->b_delayed )
479 TsPushCmd( p_sys->p_ts, &cmd );
480 else
481 CmdExecuteDel( p_sys->p_out, &cmd );
483 TAB_REMOVE( p_sys->i_es, p_sys->pp_es, p_es );
485 vlc_mutex_unlock( &p_sys->lock );
488 static int ControlLockedGetEmpty( es_out_t *p_out, bool *pb_empty )
490 es_out_sys_t *p_sys = p_out->p_sys;
492 if( p_sys->b_delayed && TsHasCmd( p_sys->p_ts ) )
493 *pb_empty = false;
494 else
495 *pb_empty = es_out_GetEmpty( p_sys->p_out );
497 return VLC_SUCCESS;
499 static int ControlLockedGetWakeup( es_out_t *p_out, mtime_t *pi_wakeup )
501 es_out_sys_t *p_sys = p_out->p_sys;
503 if( p_sys->b_delayed )
505 assert( !p_sys->p_input->p->b_can_pace_control );
506 *pi_wakeup = 0;
508 else
510 *pi_wakeup = es_out_GetWakeup( p_sys->p_out );
513 return VLC_SUCCESS;
515 static int ControlLockedGetBuffering( es_out_t *p_out, bool *pb_buffering )
517 es_out_sys_t *p_sys = p_out->p_sys;
519 if( p_sys->b_delayed )
520 *pb_buffering = true;
521 else
522 *pb_buffering = es_out_GetBuffering( p_sys->p_out );
524 return VLC_SUCCESS;
526 static int ControlLockedSetPauseState( es_out_t *p_out, bool b_source_paused, bool b_paused, mtime_t i_date )
528 es_out_sys_t *p_sys = p_out->p_sys;
529 int i_ret;
531 if( !p_sys->b_delayed && !b_source_paused == !b_paused )
533 i_ret = es_out_SetPauseState( p_sys->p_out, b_source_paused, b_paused, i_date );
535 else
537 i_ret = VLC_EGENERIC;
538 if( !p_sys->p_input->p->b_can_pace_control )
540 if( !p_sys->b_delayed )
541 TsStart( p_out );
542 if( p_sys->b_delayed )
543 i_ret = TsChangePause( p_sys->p_ts, b_source_paused, b_paused, i_date );
545 else
547 /* XXX we may do it BUT it would be better to finish the clock clean up+improvments
548 * and so be able to advertize correctly pace control property in access
549 * module */
550 msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have pace control" );
554 if( !i_ret )
556 p_sys->b_input_paused_source = b_source_paused;
557 p_sys->b_input_paused = b_paused;
559 return i_ret;
561 static int ControlLockedSetRate( es_out_t *p_out, int i_src_rate, int i_rate )
563 es_out_sys_t *p_sys = p_out->p_sys;
564 int i_ret;
566 if( !p_sys->b_delayed && i_src_rate == i_rate )
568 i_ret = es_out_SetRate( p_sys->p_out, i_src_rate, i_rate );
570 else
572 i_ret = VLC_EGENERIC;
573 if( !p_sys->p_input->p->b_can_pace_control )
575 if( !p_sys->b_delayed )
576 TsStart( p_out );
577 if( p_sys->b_delayed )
578 i_ret = TsChangeRate( p_sys->p_ts, i_src_rate, i_rate );
580 else
582 /* XXX we may do it BUT it would be better to finish the clock clean up+improvments
583 * and so be able to advertize correctly pace control property in access
584 * module */
585 msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have pace control" );
590 if( !i_ret )
592 p_sys->i_input_rate_source = i_src_rate;
593 p_sys->i_input_rate = i_rate;
595 return i_ret;
597 static int ControlLockedSetTime( es_out_t *p_out, mtime_t i_date )
599 es_out_sys_t *p_sys = p_out->p_sys;
601 if( !p_sys->b_delayed )
602 return es_out_SetTime( p_sys->p_out, i_date );
604 /* TODO */
605 msg_Err( p_sys->p_input, "EsOutTimeshift does not yet support time change" );
606 return VLC_EGENERIC;
608 static int ControlLockedSetFrameNext( es_out_t *p_out )
610 es_out_sys_t *p_sys = p_out->p_sys;
612 return es_out_SetFrameNext( p_sys->p_out );
615 static int ControlLocked( es_out_t *p_out, int i_query, va_list args )
617 es_out_sys_t *p_sys = p_out->p_sys;
619 switch( i_query )
621 /* Pass-through control */
622 case ES_OUT_SET_MODE:
623 case ES_OUT_SET_GROUP:
624 case ES_OUT_SET_PCR:
625 case ES_OUT_SET_GROUP_PCR:
626 case ES_OUT_RESET_PCR:
627 case ES_OUT_SET_NEXT_DISPLAY_TIME:
628 case ES_OUT_SET_GROUP_META:
629 case ES_OUT_SET_GROUP_EPG:
630 case ES_OUT_SET_ES_SCRAMBLED_STATE:
631 case ES_OUT_DEL_GROUP:
632 case ES_OUT_SET_META:
633 case ES_OUT_SET_ES:
634 case ES_OUT_RESTART_ES:
635 case ES_OUT_SET_ES_DEFAULT:
636 case ES_OUT_SET_ES_STATE:
637 case ES_OUT_SET_ES_FMT:
638 case ES_OUT_SET_TIMES:
639 case ES_OUT_SET_JITTER:
640 case ES_OUT_SET_EOS:
642 ts_cmd_t cmd;
643 if( CmdInitControl( &cmd, i_query, args, p_sys->b_delayed ) )
644 return VLC_EGENERIC;
645 if( p_sys->b_delayed )
647 TsPushCmd( p_sys->p_ts, &cmd );
648 return VLC_SUCCESS;
650 return CmdExecuteControl( p_sys->p_out, &cmd );
653 /* Special control when delayed */
654 case ES_OUT_GET_ES_STATE:
656 es_out_id_t *p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
657 bool *pb_enabled = (bool*)va_arg( args, bool* );
659 if( p_sys->b_delayed )
661 *pb_enabled = true;
662 return VLC_SUCCESS;
664 return es_out_Control( p_sys->p_out, ES_OUT_GET_ES_STATE, p_es->p_es, pb_enabled );
666 /* Special internal input control */
667 case ES_OUT_GET_EMPTY:
669 bool *pb_empty = (bool*)va_arg( args, bool* );
670 return ControlLockedGetEmpty( p_out, pb_empty );
672 case ES_OUT_GET_WAKE_UP: /* TODO ? */
674 mtime_t *pi_wakeup = (mtime_t*)va_arg( args, mtime_t* );
675 return ControlLockedGetWakeup( p_out, pi_wakeup );
677 case ES_OUT_GET_BUFFERING:
679 bool *pb_buffering = (bool *)va_arg( args, bool* );
680 return ControlLockedGetBuffering( p_out, pb_buffering );
682 case ES_OUT_SET_PAUSE_STATE:
684 const bool b_source_paused = (bool)va_arg( args, int );
685 const bool b_paused = (bool)va_arg( args, int );
686 const mtime_t i_date = (mtime_t) va_arg( args, mtime_t );
688 return ControlLockedSetPauseState( p_out, b_source_paused, b_paused, i_date );
690 case ES_OUT_SET_RATE:
692 const int i_src_rate = (int)va_arg( args, int );
693 const int i_rate = (int)va_arg( args, int );
695 return ControlLockedSetRate( p_out, i_src_rate, i_rate );
697 case ES_OUT_SET_TIME:
699 const mtime_t i_date = (mtime_t)va_arg( args, mtime_t );
701 return ControlLockedSetTime( p_out, i_date );
703 case ES_OUT_SET_FRAME_NEXT:
705 return ControlLockedSetFrameNext( p_out );
707 case ES_OUT_GET_PCR_SYSTEM:
709 if( p_sys->b_delayed )
710 return VLC_EGENERIC;
712 mtime_t *pi_system = (mtime_t*)va_arg( args, mtime_t * );
713 mtime_t *pi_delay = (mtime_t*)va_arg( args, mtime_t * );
714 return es_out_ControlGetPcrSystem( p_sys->p_out, pi_system, pi_delay );
716 case ES_OUT_MODIFY_PCR_SYSTEM:
718 const bool b_absolute = va_arg( args, int );
719 const mtime_t i_system = va_arg( args, mtime_t );
721 if( b_absolute && p_sys->b_delayed )
722 return VLC_EGENERIC;
724 return es_out_ControlModifyPcrSystem( p_sys->p_out, b_absolute, i_system );
726 case ES_OUT_GET_GROUP_FORCED:
728 int *pi_group = va_arg( args, int * );
729 return es_out_Control( p_sys->p_out, ES_OUT_GET_GROUP_FORCED, pi_group );
732 default:
733 msg_Err( p_sys->p_input, "Unknown es_out_Control query !" );
734 // ft
735 /* Invalid queries for this es_out level */
736 case ES_OUT_SET_ES_BY_ID:
737 case ES_OUT_RESTART_ES_BY_ID:
738 case ES_OUT_SET_ES_DEFAULT_BY_ID:
739 case ES_OUT_GET_ES_OBJECTS_BY_ID:
740 case ES_OUT_SET_DELAY:
741 case ES_OUT_SET_RECORD_STATE:
742 vlc_assert_unreachable();
743 return VLC_EGENERIC;
746 static int Control( es_out_t *p_out, int i_query, va_list args )
748 es_out_sys_t *p_sys = p_out->p_sys;
749 int i_ret;
751 vlc_mutex_lock( &p_sys->lock );
753 TsAutoStop( p_out );
755 i_ret = ControlLocked( p_out, i_query, args );
757 vlc_mutex_unlock( &p_sys->lock );
759 return i_ret;
762 /*****************************************************************************
764 *****************************************************************************/
765 static void TsDestroy( ts_thread_t *p_ts )
767 vlc_cond_destroy( &p_ts->wait );
768 vlc_mutex_destroy( &p_ts->lock );
769 free( p_ts );
771 static int TsStart( es_out_t *p_out )
773 es_out_sys_t *p_sys = p_out->p_sys;
774 ts_thread_t *p_ts;
776 assert( !p_sys->b_delayed );
778 p_sys->p_ts = p_ts = calloc(1, sizeof(*p_ts));
779 if( !p_ts )
780 return VLC_EGENERIC;
782 p_ts->i_tmp_size_max = p_sys->i_tmp_size_max;
783 p_ts->psz_tmp_path = p_sys->psz_tmp_path;
784 p_ts->p_input = p_sys->p_input;
785 p_ts->p_out = p_sys->p_out;
786 vlc_mutex_init( &p_ts->lock );
787 vlc_cond_init( &p_ts->wait );
788 p_ts->b_paused = p_sys->b_input_paused && !p_sys->b_input_paused_source;
789 p_ts->i_pause_date = p_ts->b_paused ? mdate() : -1;
790 p_ts->i_rate_source = p_sys->i_input_rate_source;
791 p_ts->i_rate = p_sys->i_input_rate;
792 p_ts->i_rate_date = -1;
793 p_ts->i_rate_delay = 0;
794 p_ts->i_buffering_delay = 0;
795 p_ts->i_cmd_delay = 0;
796 p_ts->p_storage_r = NULL;
797 p_ts->p_storage_w = NULL;
799 p_sys->b_delayed = true;
800 if( vlc_clone( &p_ts->thread, TsRun, p_ts, VLC_THREAD_PRIORITY_INPUT ) )
802 msg_Err( p_sys->p_input, "cannot create timeshift thread" );
804 TsDestroy( p_ts );
806 p_sys->b_delayed = false;
807 return VLC_EGENERIC;
810 return VLC_SUCCESS;
812 static void TsAutoStop( es_out_t *p_out )
814 es_out_sys_t *p_sys = p_out->p_sys;
816 if( !p_sys->b_delayed || !TsIsUnused( p_sys->p_ts ) )
817 return;
819 msg_Warn( p_sys->p_input, "es out timeshift: auto stop" );
820 TsStop( p_sys->p_ts );
822 p_sys->b_delayed = false;
824 static void TsStop( ts_thread_t *p_ts )
826 vlc_cancel( p_ts->thread );
827 vlc_join( p_ts->thread, NULL );
829 vlc_mutex_lock( &p_ts->lock );
830 for( ;; )
832 ts_cmd_t cmd;
834 if( TsPopCmdLocked( p_ts, &cmd, true ) )
835 break;
837 CmdClean( &cmd );
839 assert( !p_ts->p_storage_r || !p_ts->p_storage_r->p_next );
840 if( p_ts->p_storage_r )
841 TsStorageDelete( p_ts->p_storage_r );
842 vlc_mutex_unlock( &p_ts->lock );
844 TsDestroy( p_ts );
846 static void TsPushCmd( ts_thread_t *p_ts, ts_cmd_t *p_cmd )
848 vlc_mutex_lock( &p_ts->lock );
850 if( !p_ts->p_storage_w || TsStorageIsFull( p_ts->p_storage_w, p_cmd ) )
852 ts_storage_t *p_storage = TsStorageNew( p_ts->psz_tmp_path, p_ts->i_tmp_size_max );
854 if( !p_storage )
856 CmdClean( p_cmd );
857 vlc_mutex_unlock( &p_ts->lock );
858 /* TODO warn the user (but only once) */
859 return;
862 if( !p_ts->p_storage_w )
864 p_ts->p_storage_r = p_ts->p_storage_w = p_storage;
866 else
868 TsStoragePack( p_ts->p_storage_w );
869 p_ts->p_storage_w->p_next = p_storage;
870 p_ts->p_storage_w = p_storage;
874 /* TODO return error and warn the user (but only once) */
875 TsStoragePushCmd( p_ts->p_storage_w, p_cmd, p_ts->p_storage_r == p_ts->p_storage_w );
877 vlc_cond_signal( &p_ts->wait );
879 vlc_mutex_unlock( &p_ts->lock );
881 static int TsPopCmdLocked( ts_thread_t *p_ts, ts_cmd_t *p_cmd, bool b_flush )
883 vlc_assert_locked( &p_ts->lock );
885 if( TsStorageIsEmpty( p_ts->p_storage_r ) )
886 return VLC_EGENERIC;
888 TsStoragePopCmd( p_ts->p_storage_r, p_cmd, b_flush );
890 while( p_ts->p_storage_r && TsStorageIsEmpty( p_ts->p_storage_r ) )
892 ts_storage_t *p_next = p_ts->p_storage_r->p_next;
893 if( !p_next )
894 break;
896 TsStorageDelete( p_ts->p_storage_r );
897 p_ts->p_storage_r = p_next;
900 return VLC_SUCCESS;
902 static bool TsHasCmd( ts_thread_t *p_ts )
904 bool b_cmd;
906 vlc_mutex_lock( &p_ts->lock );
907 b_cmd = TsStorageIsEmpty( p_ts->p_storage_r );
908 vlc_mutex_unlock( &p_ts->lock );
910 return b_cmd;
912 static bool TsIsUnused( ts_thread_t *p_ts )
914 bool b_unused;
916 vlc_mutex_lock( &p_ts->lock );
917 b_unused = !p_ts->b_paused &&
918 p_ts->i_rate == p_ts->i_rate_source &&
919 TsStorageIsEmpty( p_ts->p_storage_r );
920 vlc_mutex_unlock( &p_ts->lock );
922 return b_unused;
924 static int TsChangePause( ts_thread_t *p_ts, bool b_source_paused, bool b_paused, mtime_t i_date )
926 vlc_mutex_lock( &p_ts->lock );
928 int i_ret;
929 if( b_paused )
931 assert( !b_source_paused );
932 i_ret = es_out_SetPauseState( p_ts->p_out, true, true, i_date );
934 else
936 i_ret = es_out_SetPauseState( p_ts->p_out, false, false, i_date );
939 if( !i_ret )
941 if( !b_paused )
943 assert( p_ts->i_pause_date > 0 );
945 p_ts->i_cmd_delay += i_date - p_ts->i_pause_date;
948 p_ts->b_paused = b_paused;
949 p_ts->i_pause_date = i_date;
951 vlc_cond_signal( &p_ts->wait );
953 vlc_mutex_unlock( &p_ts->lock );
954 return i_ret;
956 static int TsChangeRate( ts_thread_t *p_ts, int i_src_rate, int i_rate )
958 int i_ret;
960 vlc_mutex_lock( &p_ts->lock );
961 p_ts->i_cmd_delay += p_ts->i_rate_delay;
963 p_ts->i_rate_date = -1;
964 p_ts->i_rate_delay = 0;
965 p_ts->i_rate = i_rate;
966 p_ts->i_rate_source = i_src_rate;
968 i_ret = es_out_SetRate( p_ts->p_out, i_rate, i_rate );
969 vlc_mutex_unlock( &p_ts->lock );
971 return i_ret;
974 static void *TsRun( void *p_data )
976 ts_thread_t *p_ts = p_data;
977 mtime_t i_buffering_date = -1;
979 for( ;; )
981 ts_cmd_t cmd;
982 mtime_t i_deadline;
983 bool b_buffering;
985 /* Pop a command to execute */
986 vlc_mutex_lock( &p_ts->lock );
987 mutex_cleanup_push( &p_ts->lock );
989 for( ;; )
991 const int canc = vlc_savecancel();
992 b_buffering = es_out_GetBuffering( p_ts->p_out );
994 if( ( !p_ts->b_paused || b_buffering ) && !TsPopCmdLocked( p_ts, &cmd, false ) )
996 vlc_restorecancel( canc );
997 break;
999 vlc_restorecancel( canc );
1001 vlc_cond_wait( &p_ts->wait, &p_ts->lock );
1004 if( b_buffering && i_buffering_date < 0 )
1006 i_buffering_date = cmd.i_date;
1008 else if( i_buffering_date > 0 )
1010 p_ts->i_buffering_delay += i_buffering_date - cmd.i_date; /* It is < 0 */
1011 if( b_buffering )
1012 i_buffering_date = cmd.i_date;
1013 else
1014 i_buffering_date = -1;
1017 if( p_ts->i_rate_date < 0 )
1018 p_ts->i_rate_date = cmd.i_date;
1020 p_ts->i_rate_delay = 0;
1021 if( p_ts->i_rate_source != p_ts->i_rate )
1023 const mtime_t i_duration = cmd.i_date - p_ts->i_rate_date;
1024 p_ts->i_rate_delay = i_duration * p_ts->i_rate / p_ts->i_rate_source - i_duration;
1026 if( p_ts->i_cmd_delay + p_ts->i_rate_delay + p_ts->i_buffering_delay < 0 && p_ts->i_rate != p_ts->i_rate_source )
1028 const int canc = vlc_savecancel();
1030 /* Auto reset to rate 1.0 */
1031 msg_Warn( p_ts->p_input, "es out timeshift: auto reset rate to %d", p_ts->i_rate_source );
1033 p_ts->i_cmd_delay = 0;
1034 p_ts->i_buffering_delay = 0;
1036 p_ts->i_rate_delay = 0;
1037 p_ts->i_rate_date = -1;
1038 p_ts->i_rate = p_ts->i_rate_source;
1040 if( !es_out_SetRate( p_ts->p_out, p_ts->i_rate_source, p_ts->i_rate ) )
1042 vlc_value_t val = { .i_int = p_ts->i_rate };
1043 /* Warn back input
1044 * FIXME it is perfectly safe BUT it is ugly as it may hide a
1045 * rate change requested by user */
1046 input_ControlPush( p_ts->p_input, INPUT_CONTROL_SET_RATE, &val );
1049 vlc_restorecancel( canc );
1051 i_deadline = cmd.i_date + p_ts->i_cmd_delay + p_ts->i_rate_delay + p_ts->i_buffering_delay;
1053 vlc_cleanup_pop();
1054 vlc_mutex_unlock( &p_ts->lock );
1056 /* Regulate the speed of command processing to the same one than
1057 * reading */
1058 vlc_cleanup_push( cmd_cleanup_routine, &cmd );
1060 mwait( i_deadline );
1062 vlc_cleanup_pop();
1064 /* Execute the command */
1065 const int canc = vlc_savecancel();
1066 switch( cmd.i_type )
1068 case C_ADD:
1069 CmdExecuteAdd( p_ts->p_out, &cmd );
1070 CmdCleanAdd( &cmd );
1071 break;
1072 case C_SEND:
1073 CmdExecuteSend( p_ts->p_out, &cmd );
1074 CmdCleanSend( &cmd );
1075 break;
1076 case C_CONTROL:
1077 CmdExecuteControl( p_ts->p_out, &cmd );
1078 CmdCleanControl( &cmd );
1079 break;
1080 case C_DEL:
1081 CmdExecuteDel( p_ts->p_out, &cmd );
1082 break;
1083 default:
1084 vlc_assert_unreachable();
1085 break;
1087 vlc_restorecancel( canc );
1090 return NULL;
1093 /*****************************************************************************
1095 *****************************************************************************/
1096 static ts_storage_t *TsStorageNew( const char *psz_tmp_path, int64_t i_tmp_size_max )
1098 ts_storage_t *p_storage = malloc( sizeof (*p_storage) );
1099 if( unlikely(p_storage == NULL) )
1100 return NULL;
1102 char *psz_file;
1103 int fd = GetTmpFile( &psz_file, psz_tmp_path );
1104 if( fd == -1 )
1106 free( p_storage );
1107 return NULL;
1110 p_storage->p_filew = fdopen( fd, "w+b" );
1111 if( p_storage->p_filew == NULL )
1113 close( fd );
1114 vlc_unlink( psz_file );
1115 goto error;
1118 p_storage->p_filer = vlc_fopen( psz_file, "rb" );
1119 if( p_storage->p_filer == NULL )
1121 fclose( p_storage->p_filew );
1122 vlc_unlink( psz_file );
1123 goto error;
1126 #ifndef _WIN32
1127 vlc_unlink( psz_file );
1128 free( psz_file );
1129 #else
1130 p_storage->psz_file = psz_file;
1131 #endif
1132 p_storage->p_next = NULL;
1134 /* */
1135 p_storage->i_file_max = i_tmp_size_max;
1136 p_storage->i_file_size = 0;
1138 /* */
1139 p_storage->i_cmd_w = 0;
1140 p_storage->i_cmd_r = 0;
1141 p_storage->i_cmd_max = 30000;
1142 p_storage->p_cmd = malloc( p_storage->i_cmd_max * sizeof(*p_storage->p_cmd) );
1143 //fprintf( stderr, "\nSTORAGE name=%s size=%d KiB\n", p_storage->psz_file, p_storage->i_cmd_max * sizeof(*p_storage->p_cmd) /1024 );
1145 if( !p_storage->p_cmd )
1147 TsStorageDelete( p_storage );
1148 return NULL;
1150 return p_storage;
1151 error:
1152 free( psz_file );
1153 free( p_storage );
1154 return NULL;
1157 static void TsStorageDelete( ts_storage_t *p_storage )
1159 while( p_storage->i_cmd_r < p_storage->i_cmd_w )
1161 ts_cmd_t cmd;
1163 TsStoragePopCmd( p_storage, &cmd, true );
1165 CmdClean( &cmd );
1167 free( p_storage->p_cmd );
1169 fclose( p_storage->p_filer );
1170 fclose( p_storage->p_filew );
1171 #ifdef _WIN32
1172 vlc_unlink( p_storage->psz_file );
1173 free( p_storage->psz_file );
1174 #endif
1175 free( p_storage );
1178 static void TsStoragePack( ts_storage_t *p_storage )
1180 /* Try to release a bit of memory */
1181 if( p_storage->i_cmd_w >= p_storage->i_cmd_max )
1182 return;
1184 p_storage->i_cmd_max = __MAX( p_storage->i_cmd_w, 1 );
1186 ts_cmd_t *p_new = realloc( p_storage->p_cmd, p_storage->i_cmd_max * sizeof(*p_storage->p_cmd) );
1187 if( p_new )
1188 p_storage->p_cmd = p_new;
1190 static bool TsStorageIsFull( ts_storage_t *p_storage, const ts_cmd_t *p_cmd )
1192 if( p_cmd && p_cmd->i_type == C_SEND && p_storage->i_cmd_w > 0 )
1194 size_t i_size = sizeof(*p_cmd->u.send.p_block) + p_cmd->u.send.p_block->i_buffer;
1196 if( p_storage->i_file_size + i_size >= p_storage->i_file_max )
1197 return true;
1199 return p_storage->i_cmd_w >= p_storage->i_cmd_max;
1201 static bool TsStorageIsEmpty( ts_storage_t *p_storage )
1203 return !p_storage || p_storage->i_cmd_r >= p_storage->i_cmd_w;
1205 static void TsStoragePushCmd( ts_storage_t *p_storage, const ts_cmd_t *p_cmd, bool b_flush )
1207 ts_cmd_t cmd = *p_cmd;
1209 assert( !TsStorageIsFull( p_storage, p_cmd ) );
1211 if( cmd.i_type == C_SEND )
1213 block_t *p_block = cmd.u.send.p_block;
1215 cmd.u.send.p_block = NULL;
1216 cmd.u.send.i_offset = ftell( p_storage->p_filew );
1218 if( fwrite( p_block, sizeof(*p_block), 1, p_storage->p_filew ) != 1 )
1220 block_Release( p_block );
1221 return;
1223 p_storage->i_file_size += sizeof(*p_block);
1224 if( p_block->i_buffer > 0 )
1226 if( fwrite( p_block->p_buffer, p_block->i_buffer, 1, p_storage->p_filew ) != 1 )
1228 block_Release( p_block );
1229 return;
1232 p_storage->i_file_size += p_block->i_buffer;
1233 block_Release( p_block );
1235 if( b_flush )
1236 fflush( p_storage->p_filew );
1238 p_storage->p_cmd[p_storage->i_cmd_w++] = cmd;
1240 static void TsStoragePopCmd( ts_storage_t *p_storage, ts_cmd_t *p_cmd, bool b_flush )
1242 assert( !TsStorageIsEmpty( p_storage ) );
1244 *p_cmd = p_storage->p_cmd[p_storage->i_cmd_r++];
1245 if( p_cmd->i_type == C_SEND )
1247 block_t block;
1249 if( !b_flush &&
1250 !fseek( p_storage->p_filer, p_cmd->u.send.i_offset, SEEK_SET ) &&
1251 fread( &block, sizeof(block), 1, p_storage->p_filer ) == 1 )
1253 block_t *p_block = block_Alloc( block.i_buffer );
1254 if( p_block )
1256 p_block->i_dts = block.i_dts;
1257 p_block->i_pts = block.i_pts;
1258 p_block->i_flags = block.i_flags;
1259 p_block->i_length = block.i_length;
1260 p_block->i_nb_samples = block.i_nb_samples;
1261 p_block->i_buffer = fread( p_block->p_buffer, 1, block.i_buffer, p_storage->p_filer );
1263 p_cmd->u.send.p_block = p_block;
1265 else
1267 //perror( "TsStoragePopCmd" );
1268 p_cmd->u.send.p_block = block_Alloc( 1 );
1273 /*****************************************************************************
1275 *****************************************************************************/
1276 static void CmdClean( ts_cmd_t *p_cmd )
1278 switch( p_cmd->i_type )
1280 case C_ADD:
1281 CmdCleanAdd( p_cmd );
1282 break;
1283 case C_SEND:
1284 CmdCleanSend( p_cmd );
1285 break;
1286 case C_CONTROL:
1287 CmdCleanControl( p_cmd );
1288 break;
1289 case C_DEL:
1290 break;
1291 default:
1292 vlc_assert_unreachable();
1293 break;
1297 static int CmdInitAdd( ts_cmd_t *p_cmd, es_out_id_t *p_es, const es_format_t *p_fmt, bool b_copy )
1299 p_cmd->i_type = C_ADD;
1300 p_cmd->i_date = mdate();
1301 p_cmd->u.add.p_es = p_es;
1302 if( b_copy )
1304 p_cmd->u.add.p_fmt = malloc( sizeof(*p_fmt) );
1305 if( !p_cmd->u.add.p_fmt )
1306 return VLC_EGENERIC;
1307 es_format_Copy( p_cmd->u.add.p_fmt, p_fmt );
1309 else
1311 p_cmd->u.add.p_fmt = (es_format_t*)p_fmt;
1313 return VLC_SUCCESS;
1315 static void CmdExecuteAdd( es_out_t *p_out, ts_cmd_t *p_cmd )
1317 p_cmd->u.add.p_es->p_es = es_out_Add( p_out, p_cmd->u.add.p_fmt );
1319 static void CmdCleanAdd( ts_cmd_t *p_cmd )
1321 es_format_Clean( p_cmd->u.add.p_fmt );
1322 free( p_cmd->u.add.p_fmt );
1325 static void CmdInitSend( ts_cmd_t *p_cmd, es_out_id_t *p_es, block_t *p_block )
1327 p_cmd->i_type = C_SEND;
1328 p_cmd->i_date = mdate();
1329 p_cmd->u.send.p_es = p_es;
1330 p_cmd->u.send.p_block = p_block;
1332 static int CmdExecuteSend( es_out_t *p_out, ts_cmd_t *p_cmd )
1334 block_t *p_block = p_cmd->u.send.p_block;
1336 p_cmd->u.send.p_block = NULL;
1338 if( p_block )
1340 if( p_cmd->u.send.p_es->p_es )
1341 return es_out_Send( p_out, p_cmd->u.send.p_es->p_es, p_block );
1342 block_Release( p_block );
1344 return VLC_EGENERIC;
1346 static void CmdCleanSend( ts_cmd_t *p_cmd )
1348 if( p_cmd->u.send.p_block )
1349 block_Release( p_cmd->u.send.p_block );
1352 static int CmdInitDel( ts_cmd_t *p_cmd, es_out_id_t *p_es )
1354 p_cmd->i_type = C_DEL;
1355 p_cmd->i_date = mdate();
1356 p_cmd->u.del.p_es = p_es;
1357 return VLC_SUCCESS;
1359 static void CmdExecuteDel( es_out_t *p_out, ts_cmd_t *p_cmd )
1361 if( p_cmd->u.del.p_es->p_es )
1362 es_out_Del( p_out, p_cmd->u.del.p_es->p_es );
1363 free( p_cmd->u.del.p_es );
1366 static int CmdInitControl( ts_cmd_t *p_cmd, int i_query, va_list args, bool b_copy )
1368 p_cmd->i_type = C_CONTROL;
1369 p_cmd->i_date = mdate();
1370 p_cmd->u.control.i_query = i_query;
1372 switch( i_query )
1374 /* Pass-through control */
1375 case ES_OUT_SET_MODE: /* arg1= int */
1376 case ES_OUT_SET_GROUP: /* arg1= int */
1377 case ES_OUT_DEL_GROUP: /* arg1=int i_group */
1378 p_cmd->u.control.u.i_int = (int)va_arg( args, int );
1379 break;
1381 case ES_OUT_SET_PCR: /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
1382 case ES_OUT_SET_NEXT_DISPLAY_TIME: /* arg1=int64_t i_pts(microsecond) */
1383 p_cmd->u.control.u.i_i64 = (int64_t)va_arg( args, int64_t );
1384 break;
1386 case ES_OUT_SET_GROUP_PCR: /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
1387 p_cmd->u.control.u.int_i64.i_int = (int)va_arg( args, int );
1388 p_cmd->u.control.u.int_i64.i_i64 = (int64_t)va_arg( args, int64_t );
1389 break;
1391 case ES_OUT_SET_ES_SCRAMBLED_STATE:
1392 p_cmd->u.control.u.es_bool.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
1393 p_cmd->u.control.u.es_bool.b_bool = (bool)va_arg( args, int );
1394 break;
1396 case ES_OUT_RESET_PCR: /* no arg */
1397 case ES_OUT_SET_EOS:
1398 break;
1400 case ES_OUT_SET_META: /* arg1=const vlc_meta_t* */
1401 case ES_OUT_SET_GROUP_META: /* arg1=int i_group arg2=const vlc_meta_t* */
1403 if( i_query == ES_OUT_SET_GROUP_META )
1404 p_cmd->u.control.u.int_meta.i_int = (int)va_arg( args, int );
1405 const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * );
1407 if( b_copy )
1409 p_cmd->u.control.u.int_meta.p_meta = vlc_meta_New();
1410 if( !p_cmd->u.control.u.int_meta.p_meta )
1411 return VLC_EGENERIC;
1412 vlc_meta_Merge( p_cmd->u.control.u.int_meta.p_meta, p_meta );
1414 else
1416 /* The cast is only needed to avoid warning */
1417 p_cmd->u.control.u.int_meta.p_meta = (vlc_meta_t*)p_meta;
1419 break;
1422 case ES_OUT_SET_GROUP_EPG: /* arg1=int i_group arg2=const vlc_epg_t* */
1424 p_cmd->u.control.u.int_epg.i_int = (int)va_arg( args, int );
1425 const vlc_epg_t *p_epg = va_arg( args, const vlc_epg_t * );
1427 if( b_copy )
1429 p_cmd->u.control.u.int_epg.p_epg = vlc_epg_New( p_epg->psz_name );
1430 if( !p_cmd->u.control.u.int_epg.p_epg )
1431 return VLC_EGENERIC;
1432 for( int i = 0; i < p_epg->i_event; i++ )
1434 vlc_epg_event_t *p_evt = p_epg->pp_event[i];
1436 vlc_epg_AddEvent( p_cmd->u.control.u.int_epg.p_epg,
1437 p_evt->i_start, p_evt->i_duration,
1438 p_evt->psz_name,
1439 p_evt->psz_short_description,
1440 p_evt->psz_description, 0 );
1442 vlc_epg_SetCurrent( p_cmd->u.control.u.int_epg.p_epg,
1443 p_epg->p_current ? p_epg->p_current->i_start : -1 );
1445 else
1447 /* The cast is only needed to avoid warning */
1448 p_cmd->u.control.u.int_epg.p_epg = (vlc_epg_t*)p_epg;
1450 break;
1453 /* Modified control */
1454 case ES_OUT_SET_ES: /* arg1= es_out_id_t* */
1455 case ES_OUT_RESTART_ES: /* arg1= es_out_id_t* */
1456 case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t* */
1457 p_cmd->u.control.u.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
1458 break;
1460 case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool */
1461 p_cmd->u.control.u.es_bool.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
1462 p_cmd->u.control.u.es_bool.b_bool = (bool)va_arg( args, int );
1463 break;
1465 case ES_OUT_SET_ES_FMT: /* arg1= es_out_id_t* arg2=es_format_t* */
1467 p_cmd->u.control.u.es_fmt.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
1468 es_format_t *p_fmt = (es_format_t*)va_arg( args, es_format_t * );
1470 if( b_copy )
1472 p_cmd->u.control.u.es_fmt.p_fmt = malloc( sizeof(*p_fmt) );
1473 if( !p_cmd->u.control.u.es_fmt.p_fmt )
1474 return VLC_EGENERIC;
1475 es_format_Copy( p_cmd->u.control.u.es_fmt.p_fmt, p_fmt );
1477 else
1479 p_cmd->u.control.u.es_fmt.p_fmt = p_fmt;
1481 break;
1483 case ES_OUT_SET_TIMES:
1485 double f_position = (double)va_arg( args, double );
1486 mtime_t i_time = (mtime_t)va_arg( args, mtime_t );
1487 mtime_t i_length = (mtime_t)va_arg( args, mtime_t );
1489 p_cmd->u.control.u.times.f_position = f_position;
1490 p_cmd->u.control.u.times.i_time = i_time;
1491 p_cmd->u.control.u.times.i_length = i_length;
1492 break;
1494 case ES_OUT_SET_JITTER:
1496 mtime_t i_pts_delay = (mtime_t)va_arg( args, mtime_t );
1497 mtime_t i_pts_jitter = (mtime_t)va_arg( args, mtime_t );
1498 int i_cr_average = (int)va_arg( args, int );
1500 p_cmd->u.control.u.jitter.i_pts_delay = i_pts_delay;
1501 p_cmd->u.control.u.jitter.i_pts_jitter = i_pts_jitter;
1502 p_cmd->u.control.u.jitter.i_cr_average = i_cr_average;
1503 break;
1506 default:
1507 vlc_assert_unreachable();
1508 return VLC_EGENERIC;
1511 return VLC_SUCCESS;
1513 static int CmdExecuteControl( es_out_t *p_out, ts_cmd_t *p_cmd )
1515 const int i_query = p_cmd->u.control.i_query;
1517 switch( i_query )
1519 /* Pass-through control */
1520 case ES_OUT_SET_MODE: /* arg1= int */
1521 case ES_OUT_SET_GROUP: /* arg1= int */
1522 case ES_OUT_DEL_GROUP: /* arg1=int i_group */
1523 return es_out_Control( p_out, i_query, p_cmd->u.control.u.i_int );
1525 case ES_OUT_SET_PCR: /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
1526 case ES_OUT_SET_NEXT_DISPLAY_TIME: /* arg1=int64_t i_pts(microsecond) */
1527 return es_out_Control( p_out, i_query, p_cmd->u.control.u.i_i64 );
1529 case ES_OUT_SET_GROUP_PCR: /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
1530 return es_out_Control( p_out, i_query, p_cmd->u.control.u.int_i64.i_int,
1531 p_cmd->u.control.u.int_i64.i_i64 );
1533 case ES_OUT_RESET_PCR: /* no arg */
1534 case ES_OUT_SET_EOS:
1535 return es_out_Control( p_out, i_query );
1537 case ES_OUT_SET_GROUP_META: /* arg1=int i_group arg2=const vlc_meta_t* */
1538 return es_out_Control( p_out, i_query, p_cmd->u.control.u.int_meta.i_int,
1539 p_cmd->u.control.u.int_meta.p_meta );
1541 case ES_OUT_SET_GROUP_EPG: /* arg1=int i_group arg2=const vlc_epg_t* */
1542 return es_out_Control( p_out, i_query, p_cmd->u.control.u.int_epg.i_int,
1543 p_cmd->u.control.u.int_epg.p_epg );
1545 case ES_OUT_SET_ES_SCRAMBLED_STATE: /* arg1=int es_out_id_t* arg2=bool */
1546 return es_out_Control( p_out, i_query, p_cmd->u.control.u.es_bool.p_es->p_es,
1547 p_cmd->u.control.u.es_bool.b_bool );
1549 case ES_OUT_SET_META: /* arg1=const vlc_meta_t* */
1550 return es_out_Control( p_out, i_query, p_cmd->u.control.u.int_meta.p_meta );
1552 /* Modified control */
1553 case ES_OUT_SET_ES: /* arg1= es_out_id_t* */
1554 case ES_OUT_RESTART_ES: /* arg1= es_out_id_t* */
1555 case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t* */
1556 return es_out_Control( p_out, i_query, p_cmd->u.control.u.p_es->p_es );
1558 case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool */
1559 return es_out_Control( p_out, i_query, p_cmd->u.control.u.es_bool.p_es->p_es,
1560 p_cmd->u.control.u.es_bool.b_bool );
1562 case ES_OUT_SET_ES_FMT: /* arg1= es_out_id_t* arg2=es_format_t* */
1563 return es_out_Control( p_out, i_query, p_cmd->u.control.u.es_fmt.p_es->p_es,
1564 p_cmd->u.control.u.es_fmt.p_fmt );
1566 case ES_OUT_SET_TIMES:
1567 return es_out_Control( p_out, i_query, p_cmd->u.control.u.times.f_position,
1568 p_cmd->u.control.u.times.i_time,
1569 p_cmd->u.control.u.times.i_length );
1570 case ES_OUT_SET_JITTER:
1571 return es_out_Control( p_out, i_query, p_cmd->u.control.u.jitter.i_pts_delay,
1572 p_cmd->u.control.u.jitter.i_pts_jitter,
1573 p_cmd->u.control.u.jitter.i_cr_average );
1575 default:
1576 vlc_assert_unreachable();
1577 return VLC_EGENERIC;
1580 static void CmdCleanControl( ts_cmd_t *p_cmd )
1582 switch( p_cmd->u.control.i_query )
1584 case ES_OUT_SET_GROUP_META:
1585 case ES_OUT_SET_META:
1586 if( p_cmd->u.control.u.int_meta.p_meta )
1587 vlc_meta_Delete( p_cmd->u.control.u.int_meta.p_meta );
1588 break;
1589 case ES_OUT_SET_GROUP_EPG:
1590 if( p_cmd->u.control.u.int_epg.p_epg )
1591 vlc_epg_Delete( p_cmd->u.control.u.int_epg.p_epg );
1592 break;
1593 case ES_OUT_SET_ES_FMT:
1594 if( p_cmd->u.control.u.es_fmt.p_fmt )
1596 es_format_Clean( p_cmd->u.control.u.es_fmt.p_fmt );
1597 free( p_cmd->u.control.u.es_fmt.p_fmt );
1599 // ft
1600 default:
1601 break;
1605 static int GetTmpFile( char **filename, const char *dirname )
1607 if( dirname != NULL
1608 && asprintf( filename, "%s"DIR_SEP PACKAGE_NAME"-timeshift.XXXXXX",
1609 dirname ) >= 0 )
1611 vlc_mkdir( dirname, 0700 );
1613 int fd = vlc_mkstemp( *filename );
1614 if( fd != -1 )
1615 return fd;
1617 free( *filename );
1620 *filename = strdup( DIR_SEP"tmp"DIR_SEP PACKAGE_NAME"-timeshift.XXXXXX" );
1621 if( unlikely(*filename == NULL) )
1622 return -1;
1624 int fd = vlc_mkstemp( *filename );
1625 if( fd != -1 )
1626 return fd;
1628 free( *filename );
1629 return -1;