]> git.sesse.net Git - vlc/blob - src/input/es_out_timeshift.c
891bba1495edf1742aa2ab0f55824104ae603fa9
[vlc] / src / input / es_out_timeshift.c
1 /*****************************************************************************
2  * es_out_timeshift.c: Es Out timeshift.
3  *****************************************************************************
4  * Copyright (C) 2008 Laurent Aimar
5  * $Id$
6  *
7  * Authors: Laurent Aimar < fenrir _AT_ videolan _DOT_ org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
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 General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
30
31 #include <stdlib.h>
32 #include <stdio.h>
33 #include <assert.h>
34 #if defined (WIN32) && !defined (UNDER_CE)
35 #  include <direct.h>
36 #endif
37 #ifdef HAVE_SYS_STAT_H
38 #   include <sys/stat.h>
39 #endif
40 #ifdef HAVE_UNISTD_H
41 # include <unistd.h>
42 #endif
43
44 #include <vlc_common.h>
45 #include <vlc_fs.h>
46 #ifdef WIN32
47 #  include <vlc_charset.h>
48 #endif
49 #include <vlc_input.h>
50 #include <vlc_es_out.h>
51 #include <vlc_block.h>
52 #include "input_internal.h"
53 #include "es_out.h"
54 #include "es_out_timeshift.h"
55
56 /*****************************************************************************
57  * Local prototypes
58  *****************************************************************************/
59
60 /* XXX attribute_packed is (and MUST be) used ONLY to reduce memory usage */
61 #ifdef HAVE_ATTRIBUTE_PACKED
62 #   define attribute_packed __attribute__((__packed__))
63 #else
64 #   define attribute_packed
65 #endif
66
67 enum
68 {
69     C_ADD,
70     C_SEND,
71     C_DEL,
72     C_CONTROL,
73 };
74
75 typedef struct attribute_packed
76 {
77     es_out_id_t *p_es;
78     es_format_t *p_fmt;
79 } ts_cmd_add_t;
80
81 typedef struct attribute_packed
82 {
83     es_out_id_t *p_es;
84 } ts_cmd_del_t;
85
86 typedef struct attribute_packed
87 {
88     es_out_id_t *p_es;
89     block_t *p_block;
90     int     i_offset;  /* We do not use file > INT_MAX */
91 } ts_cmd_send_t;
92
93 typedef struct attribute_packed
94 {
95     int  i_query;
96
97     union
98     {
99         bool b_bool;
100         int  i_int;
101         int64_t i_i64;
102         es_out_id_t *p_es;
103         struct
104         {
105             int     i_int;
106             int64_t i_i64;
107         } int_i64;
108         struct
109         {
110             int        i_int;
111             vlc_meta_t *p_meta;
112         } int_meta;
113         struct
114         {
115             int       i_int;
116             vlc_epg_t *p_epg;
117         } int_epg;
118         struct
119         {
120             es_out_id_t *p_es;
121             bool        b_bool;
122         } es_bool;
123         struct
124         {
125             es_out_id_t *p_es;
126             es_format_t *p_fmt;
127         } es_fmt;
128         struct
129         {
130             /* FIXME Really too big (double make the whole thing too big) */
131             double  f_position;
132             mtime_t i_time;
133             mtime_t i_length;
134         } times;
135         struct
136         {
137             mtime_t i_pts_delay;
138             mtime_t i_pts_jitter;
139             int     i_cr_average;
140         } jitter;
141     } u;
142 } ts_cmd_control_t;
143
144 typedef struct attribute_packed
145 {
146     int8_t  i_type;
147     mtime_t i_date;
148     union
149     {
150         ts_cmd_add_t     add;
151         ts_cmd_del_t     del;
152         ts_cmd_send_t    send;
153         ts_cmd_control_t control;
154     } u;
155 } ts_cmd_t;
156
157 typedef struct ts_storage_t ts_storage_t;
158 struct ts_storage_t
159 {
160     ts_storage_t *p_next;
161
162     /* */
163     char    *psz_file;  /* Filename */
164     size_t  i_file_max; /* Max size in bytes */
165     int64_t i_file_size;/* Current size in bytes */
166     FILE    *p_filew;   /* FILE handle for data writing */
167     FILE    *p_filer;   /* FILE handle for data reading */
168
169     /* */
170     int      i_cmd_r;
171     int      i_cmd_w;
172     int      i_cmd_max;
173     ts_cmd_t *p_cmd;
174 };
175
176 typedef struct
177 {
178     vlc_thread_t   thread;
179     input_thread_t *p_input;
180     es_out_t       *p_out;
181     int64_t        i_tmp_size_max;
182     const char     *psz_tmp_path;
183
184     /* Lock for all following fields */
185     vlc_mutex_t    lock;
186     vlc_cond_t     wait;
187
188     /* */
189     bool           b_paused;
190     mtime_t        i_pause_date;
191
192     /* */
193     int            i_rate;
194     int            i_rate_source;
195     mtime_t        i_rate_date;
196     mtime_t        i_rate_delay;
197
198     /* */
199     mtime_t        i_buffering_delay;
200
201     /* */
202     ts_storage_t   *p_storage_r;
203     ts_storage_t   *p_storage_w;
204
205     mtime_t        i_cmd_delay;
206
207 } ts_thread_t;
208
209 struct es_out_id_t
210 {
211     es_out_id_t *p_es;
212 };
213
214 struct es_out_sys_t
215 {
216     input_thread_t *p_input;
217         es_out_t       *p_out;
218
219     /* Configuration */
220     int64_t        i_tmp_size_max;    /* Maximal temporary file size in byte */
221     char           *psz_tmp_path;     /* Path for temporary files */
222
223     /* Lock for all following fields */
224     vlc_mutex_t    lock;
225
226     /* */
227     bool           b_delayed;
228     ts_thread_t   *p_ts;
229
230     /* */
231     bool           b_input_paused;
232     bool           b_input_paused_source;
233     int            i_input_rate;
234     int            i_input_rate_source;
235
236     /* */
237     int            i_es;
238     es_out_id_t    **pp_es;
239 };
240
241 static es_out_id_t *Add    ( es_out_t *, const es_format_t * );
242 static int          Send   ( es_out_t *, es_out_id_t *, block_t * );
243 static void         Del    ( es_out_t *, es_out_id_t * );
244 static int          Control( es_out_t *, int i_query, va_list );
245 static void         Destroy( es_out_t * );
246
247 static int          TsStart( es_out_t * );
248 static void         TsAutoStop( es_out_t * );
249
250 static void         TsStop( ts_thread_t * );
251 static void         TsPushCmd( ts_thread_t *, ts_cmd_t * );
252 static int          TsPopCmdLocked( ts_thread_t *, ts_cmd_t *, bool b_flush );
253 static bool         TsHasCmd( ts_thread_t * );
254 static bool         TsIsUnused( ts_thread_t * );
255 static int          TsChangePause( ts_thread_t *, bool b_source_paused, bool b_paused, mtime_t i_date );
256 static int          TsChangeRate( ts_thread_t *, int i_src_rate, int i_rate );
257
258 static void         *TsRun( void * );
259
260 static ts_storage_t *TsStorageNew( const char *psz_path, int64_t i_tmp_size_max );
261 static void         TsStorageDelete( ts_storage_t * );
262 static void         TsStoragePack( ts_storage_t *p_storage );
263 static bool         TsStorageIsFull( ts_storage_t *, const ts_cmd_t *p_cmd );
264 static bool         TsStorageIsEmpty( ts_storage_t * );
265 static void         TsStoragePushCmd( ts_storage_t *, const ts_cmd_t *p_cmd, bool b_flush );
266 static void         TsStoragePopCmd( ts_storage_t *p_storage, ts_cmd_t *p_cmd, bool b_flush );
267
268 static void CmdClean( ts_cmd_t * );
269 static void cmd_cleanup_routine( void *p ) { CmdClean( p ); }
270
271 static int  CmdInitAdd    ( ts_cmd_t *, es_out_id_t *, const es_format_t *, bool b_copy );
272 static void CmdInitSend   ( ts_cmd_t *, es_out_id_t *, block_t * );
273 static int  CmdInitDel    ( ts_cmd_t *, es_out_id_t * );
274 static int  CmdInitControl( ts_cmd_t *, int i_query, va_list, bool b_copy );
275
276 /* */
277 static void CmdCleanAdd    ( ts_cmd_t * );
278 static void CmdCleanSend   ( ts_cmd_t * );
279 static void CmdCleanControl( ts_cmd_t *p_cmd );
280
281 /* XXX these functions will take the destination es_out_t */
282 static void CmdExecuteAdd    ( es_out_t *, ts_cmd_t * );
283 static int  CmdExecuteSend   ( es_out_t *, ts_cmd_t * );
284 static void CmdExecuteDel    ( es_out_t *, ts_cmd_t * );
285 static int  CmdExecuteControl( es_out_t *, ts_cmd_t * );
286
287 /* File helpers */
288 static char *GetTmpPath( char *psz_path );
289 static FILE *GetTmpFile( char **ppsz_file, const char *psz_path );
290
291 /*****************************************************************************
292  * input_EsOutTimeshiftNew:
293  *****************************************************************************/
294 es_out_t *input_EsOutTimeshiftNew( input_thread_t *p_input, es_out_t *p_next_out, int i_rate )
295 {
296     es_out_t *p_out = malloc( sizeof(*p_out) );
297     if( !p_out )
298         return NULL;
299
300     es_out_sys_t *p_sys = malloc( sizeof(*p_sys) );
301     if( !p_sys )
302     {
303         free( p_out );
304         return NULL;
305     }
306
307     /* */
308     p_out->pf_add     = Add;
309     p_out->pf_send    = Send;
310     p_out->pf_del     = Del;
311     p_out->pf_control = Control;
312     p_out->pf_destroy = Destroy;
313     p_out->p_sys      = p_sys;
314
315     /* */
316     p_sys->b_input_paused = false;
317     p_sys->b_input_paused_source = false;
318     p_sys->p_input = p_input;
319     p_sys->i_input_rate = i_rate;
320     p_sys->i_input_rate_source = i_rate;
321
322     p_sys->p_out = p_next_out;
323     vlc_mutex_init_recursive( &p_sys->lock );
324
325     p_sys->b_delayed = false;
326     p_sys->p_ts = NULL;
327
328     TAB_INIT( p_sys->i_es, p_sys->pp_es );
329
330     /* */
331     const int i_tmp_size_max = var_CreateGetInteger( p_input, "input-timeshift-granularity" );
332     if( i_tmp_size_max < 0 )
333         p_sys->i_tmp_size_max = 50*1024*1024;
334     else
335         p_sys->i_tmp_size_max = __MAX( i_tmp_size_max, 1*1024*1024 );
336
337     char *psz_tmp_path = var_CreateGetNonEmptyString( p_input, "input-timeshift-path" );
338     p_sys->psz_tmp_path = GetTmpPath( psz_tmp_path );
339
340     msg_Dbg( p_input, "using timeshift granularity of %d MiB, in path '%s'",
341              (int)p_sys->i_tmp_size_max/(1024*1024), p_sys->psz_tmp_path );
342
343 #if 0
344 #define S(t) msg_Err( p_input, "SIZEOF("#t")=%d", sizeof(t) )
345     S(ts_cmd_t);
346     S(ts_cmd_control_t);
347     S(ts_cmd_send_t);
348     S(ts_cmd_del_t);
349     S(ts_cmd_add_t);
350 #undef S
351 #endif
352
353     return p_out;
354 }
355
356 /*****************************************************************************
357  * Internal functions
358  *****************************************************************************/
359 static void Destroy( es_out_t *p_out )
360 {
361     es_out_sys_t *p_sys = p_out->p_sys;
362
363     if( p_sys->b_delayed )
364     {
365         TsStop( p_sys->p_ts );
366         p_sys->b_delayed = false;
367     }
368
369     while( p_sys->i_es > 0 )
370         Del( p_out, p_sys->pp_es[0] );
371     TAB_CLEAN( p_sys->i_es, p_sys->pp_es  );
372
373     free( p_sys->psz_tmp_path );
374     vlc_mutex_destroy( &p_sys->lock );
375     free( p_sys );
376     free( p_out );
377 }
378
379 static es_out_id_t *Add( es_out_t *p_out, const es_format_t *p_fmt )
380 {
381     es_out_sys_t *p_sys = p_out->p_sys;
382     ts_cmd_t cmd;
383
384     es_out_id_t *p_es = malloc( sizeof( *p_es ) );
385     if( !p_es )
386         return NULL;
387
388     vlc_mutex_lock( &p_sys->lock );
389
390     TsAutoStop( p_out );
391
392     if( CmdInitAdd( &cmd, p_es, p_fmt, p_sys->b_delayed ) )
393     {
394         vlc_mutex_unlock( &p_sys->lock );
395         free( p_es );
396         return NULL;
397     }
398
399     TAB_APPEND( p_sys->i_es, p_sys->pp_es, p_es );
400
401     if( p_sys->b_delayed )
402         TsPushCmd( p_sys->p_ts, &cmd );
403     else
404         CmdExecuteAdd( p_sys->p_out, &cmd );
405
406     vlc_mutex_unlock( &p_sys->lock );
407
408     return p_es;
409 }
410 static int Send( es_out_t *p_out, es_out_id_t *p_es, block_t *p_block )
411 {
412     es_out_sys_t *p_sys = p_out->p_sys;
413     ts_cmd_t cmd;
414     int i_ret = VLC_SUCCESS;
415
416     vlc_mutex_lock( &p_sys->lock );
417
418     TsAutoStop( p_out );
419
420     CmdInitSend( &cmd, p_es, p_block );
421     if( p_sys->b_delayed )
422         TsPushCmd( p_sys->p_ts, &cmd );
423     else
424         i_ret = CmdExecuteSend( p_sys->p_out, &cmd) ;
425
426     vlc_mutex_unlock( &p_sys->lock );
427
428     return i_ret;
429 }
430 static void Del( es_out_t *p_out, es_out_id_t *p_es )
431 {
432     es_out_sys_t *p_sys = p_out->p_sys;
433     ts_cmd_t cmd;
434
435     vlc_mutex_lock( &p_sys->lock );
436
437     TsAutoStop( p_out );
438
439     CmdInitDel( &cmd, p_es );
440     if( p_sys->b_delayed )
441         TsPushCmd( p_sys->p_ts, &cmd );
442     else
443         CmdExecuteDel( p_sys->p_out, &cmd );
444
445     TAB_REMOVE( p_sys->i_es, p_sys->pp_es, p_es );
446
447     vlc_mutex_unlock( &p_sys->lock );
448 }
449
450 static int ControlLockedGetEmpty( es_out_t *p_out, bool *pb_empty )
451 {
452     es_out_sys_t *p_sys = p_out->p_sys;
453
454     if( p_sys->b_delayed && TsHasCmd( p_sys->p_ts ) )
455         *pb_empty = false;
456     else
457         *pb_empty = es_out_GetEmpty( p_sys->p_out );
458
459     return VLC_SUCCESS;
460 }
461 static int ControlLockedGetWakeup( es_out_t *p_out, mtime_t *pi_wakeup )
462 {
463     es_out_sys_t *p_sys = p_out->p_sys;
464
465     if( p_sys->b_delayed )
466     {
467         assert( !p_sys->p_input->p->b_can_pace_control );
468         *pi_wakeup = 0;
469     }
470     else
471     {
472         *pi_wakeup = es_out_GetWakeup( p_sys->p_out );
473     }
474
475     return VLC_SUCCESS;
476 }
477 static int ControlLockedGetBuffering( es_out_t *p_out, bool *pb_buffering )
478 {
479     es_out_sys_t *p_sys = p_out->p_sys;
480
481     if( p_sys->b_delayed )
482         *pb_buffering = true;
483     else
484         *pb_buffering = es_out_GetBuffering( p_sys->p_out );
485
486     return VLC_SUCCESS;
487 }
488 static int ControlLockedSetPauseState( es_out_t *p_out, bool b_source_paused, bool b_paused, mtime_t i_date )
489 {
490     es_out_sys_t *p_sys = p_out->p_sys;
491     int i_ret;
492
493     if( !p_sys->b_delayed && !b_source_paused == !b_paused )
494     {
495         i_ret = es_out_SetPauseState( p_sys->p_out, b_source_paused, b_paused, i_date );
496     }
497     else
498     {
499         i_ret = VLC_EGENERIC;
500         if( !p_sys->p_input->p->b_can_pace_control )
501         {
502             if( !p_sys->b_delayed )
503                 TsStart( p_out );
504             if( p_sys->b_delayed )
505                 i_ret = TsChangePause( p_sys->p_ts, b_source_paused, b_paused, i_date );
506         }
507         else
508         {
509             /* XXX we may do it BUT it would be better to finish the clock clean up+improvments
510              * and so be able to advertize correctly pace control property in access
511              * module */
512             msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have pace control" );
513         }
514     }
515
516     if( !i_ret )
517     {
518         p_sys->b_input_paused_source = b_source_paused;
519         p_sys->b_input_paused = b_paused;
520     }
521     return i_ret;
522 }
523 static int ControlLockedSetRate( es_out_t *p_out, int i_src_rate, int i_rate )
524 {
525     es_out_sys_t *p_sys = p_out->p_sys;
526     int i_ret;
527
528     if( !p_sys->b_delayed && i_src_rate == i_rate )
529     {
530         i_ret = es_out_SetRate( p_sys->p_out, i_src_rate, i_rate );
531     }
532     else
533     {
534         i_ret = VLC_EGENERIC;
535         if( !p_sys->p_input->p->b_can_pace_control )
536         {
537             if( !p_sys->b_delayed )
538                 TsStart( p_out );
539             if( p_sys->b_delayed )
540                 i_ret = TsChangeRate( p_sys->p_ts, i_src_rate, i_rate );
541         }
542         else
543         {
544             /* XXX we may do it BUT it would be better to finish the clock clean up+improvments
545              * and so be able to advertize correctly pace control property in access
546              * module */
547             msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have pace control" );
548         }
549
550     }
551
552     if( !i_ret )
553     {
554         p_sys->i_input_rate_source = i_src_rate;
555         p_sys->i_input_rate = i_rate;
556     }
557     return i_ret;
558 }
559 static int ControlLockedSetTime( es_out_t *p_out, mtime_t i_date )
560 {
561     es_out_sys_t *p_sys = p_out->p_sys;
562
563     if( !p_sys->b_delayed )
564         return es_out_SetTime( p_sys->p_out, i_date );
565
566     /* TODO */
567     msg_Err( p_sys->p_input, "EsOutTimeshift does not yet support time change" );
568     return VLC_EGENERIC;
569 }
570 static int ControlLockedSetFrameNext( es_out_t *p_out )
571 {
572     es_out_sys_t *p_sys = p_out->p_sys;
573
574     return es_out_SetFrameNext( p_sys->p_out );
575 }
576
577 static int ControlLocked( es_out_t *p_out, int i_query, va_list args )
578 {
579     es_out_sys_t *p_sys = p_out->p_sys;
580
581     switch( i_query )
582     {
583     /* Invalid query for this es_out level */
584     case ES_OUT_SET_ES_BY_ID:
585     case ES_OUT_RESTART_ES_BY_ID:
586     case ES_OUT_SET_ES_DEFAULT_BY_ID:
587     case ES_OUT_GET_ES_OBJECTS_BY_ID:
588     case ES_OUT_SET_DELAY:
589     case ES_OUT_SET_RECORD_STATE:
590         assert(0);
591         return VLC_EGENERIC;
592
593     /* Pass-through control */
594     case ES_OUT_SET_MODE:
595     case ES_OUT_SET_GROUP:
596     case ES_OUT_SET_PCR:
597     case ES_OUT_SET_GROUP_PCR:
598     case ES_OUT_RESET_PCR:
599     case ES_OUT_SET_NEXT_DISPLAY_TIME:
600     case ES_OUT_SET_GROUP_META:
601     case ES_OUT_SET_GROUP_EPG:
602     case ES_OUT_SET_ES_SCRAMBLED_STATE:
603     case ES_OUT_DEL_GROUP:
604     case ES_OUT_SET_META:
605     case ES_OUT_SET_ES:
606     case ES_OUT_RESTART_ES:
607     case ES_OUT_SET_ES_DEFAULT:
608     case ES_OUT_SET_ES_STATE:
609     case ES_OUT_SET_ES_FMT:
610     case ES_OUT_SET_TIMES:
611     case ES_OUT_SET_JITTER:
612     case ES_OUT_SET_EOS:
613     {
614         ts_cmd_t cmd;
615         if( CmdInitControl( &cmd, i_query, args, p_sys->b_delayed ) )
616             return VLC_EGENERIC;
617         if( p_sys->b_delayed )
618         {
619             TsPushCmd( p_sys->p_ts, &cmd );
620             return VLC_SUCCESS;
621         }
622         return CmdExecuteControl( p_sys->p_out, &cmd );
623     }
624
625     /* Special control when delayed */
626     case ES_OUT_GET_ES_STATE:
627     {
628         es_out_id_t *p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
629         bool *pb_enabled = (bool*)va_arg( args, bool* );
630
631         if( p_sys->b_delayed )
632         {
633             *pb_enabled = true;
634             return VLC_SUCCESS;
635         }
636         return es_out_Control( p_sys->p_out, ES_OUT_GET_ES_STATE, p_es->p_es, pb_enabled );
637     }
638     /* Special internal input control */
639     case ES_OUT_GET_EMPTY:
640     {
641         bool *pb_empty = (bool*)va_arg( args, bool* );
642         return ControlLockedGetEmpty( p_out, pb_empty );
643     }
644     case ES_OUT_GET_WAKE_UP: /* TODO ? */
645     {
646         mtime_t *pi_wakeup = (mtime_t*)va_arg( args, mtime_t* );
647         return ControlLockedGetWakeup( p_out, pi_wakeup );
648     }
649     case ES_OUT_GET_BUFFERING:
650     {
651         bool *pb_buffering = (bool *)va_arg( args, bool* );
652         return ControlLockedGetBuffering( p_out, pb_buffering );
653     }
654     case ES_OUT_SET_PAUSE_STATE:
655     {
656         const bool b_source_paused = (bool)va_arg( args, int );
657         const bool b_paused = (bool)va_arg( args, int );
658         const mtime_t i_date = (mtime_t) va_arg( args, mtime_t );
659
660         return ControlLockedSetPauseState( p_out, b_source_paused, b_paused, i_date );
661     }
662     case ES_OUT_SET_RATE:
663     {
664         const int i_src_rate = (int)va_arg( args, int );
665         const int i_rate = (int)va_arg( args, int );
666
667         return ControlLockedSetRate( p_out, i_src_rate, i_rate );
668     }
669     case ES_OUT_SET_TIME:
670     {
671         const mtime_t i_date = (mtime_t)va_arg( args, mtime_t );
672
673         return ControlLockedSetTime( p_out, i_date );
674     }
675     case ES_OUT_SET_FRAME_NEXT:
676     {
677         return ControlLockedSetFrameNext( p_out );
678     }
679     case ES_OUT_GET_PCR_SYSTEM:
680     {
681         if( p_sys->b_delayed )
682             return VLC_EGENERIC;
683
684         mtime_t *pi_system = (mtime_t*)va_arg( args, mtime_t * );
685         mtime_t *pi_delay  = (mtime_t*)va_arg( args, mtime_t * );
686         return es_out_ControlGetPcrSystem( p_sys->p_out, pi_system, pi_delay );
687     }
688     case ES_OUT_MODIFY_PCR_SYSTEM:
689     {
690         const bool    b_absolute = va_arg( args, int );
691         const mtime_t i_system   = va_arg( args, mtime_t );
692
693         if( b_absolute && p_sys->b_delayed )
694             return VLC_EGENERIC;
695
696         return es_out_ControlModifyPcrSystem( p_sys->p_out, b_absolute, i_system );
697     }
698     case ES_OUT_GET_GROUP_FORCED:
699     {
700         int *pi_group = va_arg( args, int * );
701         return es_out_Control( p_sys->p_out, ES_OUT_GET_GROUP_FORCED, pi_group );
702     }
703
704
705     default:
706         msg_Err( p_sys->p_input, "Unknown es_out_Control query !" );
707         assert(0);
708         return VLC_EGENERIC;
709     }
710 }
711 static int Control( es_out_t *p_out, int i_query, va_list args )
712 {
713     es_out_sys_t *p_sys = p_out->p_sys;
714     int i_ret;
715
716     vlc_mutex_lock( &p_sys->lock );
717
718     TsAutoStop( p_out );
719
720     i_ret = ControlLocked( p_out, i_query, args );
721
722     vlc_mutex_unlock( &p_sys->lock );
723
724     return i_ret;
725 }
726
727 /*****************************************************************************
728  *
729  *****************************************************************************/
730 static void TsDestroy( ts_thread_t *p_ts )
731 {
732     vlc_cond_destroy( &p_ts->wait );
733     vlc_mutex_destroy( &p_ts->lock );
734     free( p_ts );
735 }
736 static int TsStart( es_out_t *p_out )
737 {
738     es_out_sys_t *p_sys = p_out->p_sys;
739     ts_thread_t *p_ts;
740
741     assert( !p_sys->b_delayed );
742
743     p_sys->p_ts = p_ts = calloc(1, sizeof(*p_ts));
744     if( !p_ts )
745         return VLC_EGENERIC;
746
747     p_ts->i_tmp_size_max = p_sys->i_tmp_size_max;
748     p_ts->psz_tmp_path = p_sys->psz_tmp_path;
749     p_ts->p_input = p_sys->p_input;
750     p_ts->p_out = p_sys->p_out;
751     vlc_mutex_init( &p_ts->lock );
752     vlc_cond_init( &p_ts->wait );
753     p_ts->b_paused = p_sys->b_input_paused && !p_sys->b_input_paused_source;
754     p_ts->i_pause_date = p_ts->b_paused ? mdate() : -1;
755     p_ts->i_rate_source = p_sys->i_input_rate_source;
756     p_ts->i_rate        = p_sys->i_input_rate;
757     p_ts->i_rate_date = -1;
758     p_ts->i_rate_delay = 0;
759     p_ts->i_buffering_delay = 0;
760     p_ts->i_cmd_delay = 0;
761     p_ts->p_storage_r = NULL;
762     p_ts->p_storage_w = NULL;
763
764     p_sys->b_delayed = true;
765     if( vlc_clone( &p_ts->thread, TsRun, p_ts, VLC_THREAD_PRIORITY_INPUT ) )
766     {
767         msg_Err( p_sys->p_input, "cannot create timeshift thread" );
768
769         TsDestroy( p_ts );
770
771         p_sys->b_delayed = false;
772         return VLC_EGENERIC;
773     }
774
775     return VLC_SUCCESS;
776 }
777 static void TsAutoStop( es_out_t *p_out )
778 {
779     es_out_sys_t *p_sys = p_out->p_sys;
780
781     if( !p_sys->b_delayed || !TsIsUnused( p_sys->p_ts ) )
782         return;
783
784     msg_Warn( p_sys->p_input, "es out timeshift: auto stop" );
785     TsStop( p_sys->p_ts );
786
787     p_sys->b_delayed = false;
788 }
789 static void TsStop( ts_thread_t *p_ts )
790 {
791     vlc_cancel( p_ts->thread );
792     vlc_join( p_ts->thread, NULL );
793
794     vlc_mutex_lock( &p_ts->lock );
795     for( ;; )
796     {
797         ts_cmd_t cmd;
798
799         if( TsPopCmdLocked( p_ts, &cmd, true ) )
800             break;
801
802         CmdClean( &cmd );
803     }
804     assert( !p_ts->p_storage_r || !p_ts->p_storage_r->p_next );
805     if( p_ts->p_storage_r )
806         TsStorageDelete( p_ts->p_storage_r );
807     vlc_mutex_unlock( &p_ts->lock );
808
809     TsDestroy( p_ts );
810 }
811 static void TsPushCmd( ts_thread_t *p_ts, ts_cmd_t *p_cmd )
812 {
813     vlc_mutex_lock( &p_ts->lock );
814
815     if( !p_ts->p_storage_w || TsStorageIsFull( p_ts->p_storage_w, p_cmd ) )
816     {
817         ts_storage_t *p_storage = TsStorageNew( p_ts->psz_tmp_path, p_ts->i_tmp_size_max );
818
819         if( !p_storage )
820         {
821             CmdClean( p_cmd );
822             vlc_mutex_unlock( &p_ts->lock );
823             /* TODO warn the user (but only once) */
824             return;
825         }
826
827         if( !p_ts->p_storage_w )
828         {
829             p_ts->p_storage_r = p_ts->p_storage_w = p_storage;
830         }
831         else
832         {
833             TsStoragePack( p_ts->p_storage_w );
834             p_ts->p_storage_w->p_next = p_storage;
835             p_ts->p_storage_w = p_storage;
836         }
837     }
838
839     /* TODO return error and warn the user (but only once) */
840     TsStoragePushCmd( p_ts->p_storage_w, p_cmd, p_ts->p_storage_r == p_ts->p_storage_w );
841
842     vlc_cond_signal( &p_ts->wait );
843
844     vlc_mutex_unlock( &p_ts->lock );
845 }
846 static int TsPopCmdLocked( ts_thread_t *p_ts, ts_cmd_t *p_cmd, bool b_flush )
847 {
848     vlc_assert_locked( &p_ts->lock );
849
850     if( TsStorageIsEmpty( p_ts->p_storage_r ) )
851         return VLC_EGENERIC;
852
853     TsStoragePopCmd( p_ts->p_storage_r, p_cmd, b_flush );
854
855     while( p_ts->p_storage_r && TsStorageIsEmpty( p_ts->p_storage_r ) )
856     {
857         ts_storage_t *p_next = p_ts->p_storage_r->p_next;
858         if( !p_next )
859             break;
860
861         TsStorageDelete( p_ts->p_storage_r );
862         p_ts->p_storage_r = p_next;
863     }
864
865     return VLC_SUCCESS;
866 }
867 static bool TsHasCmd( ts_thread_t *p_ts )
868 {
869     bool b_cmd;
870
871     vlc_mutex_lock( &p_ts->lock );
872     b_cmd =  TsStorageIsEmpty( p_ts->p_storage_r );
873     vlc_mutex_unlock( &p_ts->lock );
874
875     return b_cmd;
876 }
877 static bool TsIsUnused( ts_thread_t *p_ts )
878 {
879     bool b_unused;
880
881     vlc_mutex_lock( &p_ts->lock );
882     b_unused = !p_ts->b_paused &&
883                p_ts->i_rate == p_ts->i_rate_source &&
884                TsStorageIsEmpty( p_ts->p_storage_r );
885     vlc_mutex_unlock( &p_ts->lock );
886
887     return b_unused;
888 }
889 static int TsChangePause( ts_thread_t *p_ts, bool b_source_paused, bool b_paused, mtime_t i_date )
890 {
891     vlc_mutex_lock( &p_ts->lock );
892
893     int i_ret;
894     if( b_paused )
895     {
896         assert( !b_source_paused );
897         i_ret = es_out_SetPauseState( p_ts->p_out, true, true, i_date );
898     }
899     else
900     {
901         i_ret = es_out_SetPauseState( p_ts->p_out, false, false, i_date );
902     }
903
904     if( !i_ret )
905     {
906         if( !b_paused )
907         {
908             assert( p_ts->i_pause_date > 0 );
909
910             p_ts->i_cmd_delay += i_date - p_ts->i_pause_date;
911         }
912
913         p_ts->b_paused = b_paused;
914         p_ts->i_pause_date = i_date;
915
916         vlc_cond_signal( &p_ts->wait );
917     }
918     vlc_mutex_unlock( &p_ts->lock );
919     return i_ret;
920 }
921 static int TsChangeRate( ts_thread_t *p_ts, int i_src_rate, int i_rate )
922 {
923     int i_ret;
924
925     vlc_mutex_lock( &p_ts->lock );
926     p_ts->i_cmd_delay += p_ts->i_rate_delay;
927
928     p_ts->i_rate_date = -1;
929     p_ts->i_rate_delay = 0;
930     p_ts->i_rate = i_rate;
931     p_ts->i_rate_source = i_src_rate;
932
933     i_ret = es_out_SetRate( p_ts->p_out, i_rate, i_rate );
934     vlc_mutex_unlock( &p_ts->lock );
935
936     return i_ret;
937 }
938
939 static void *TsRun( void *p_data )
940 {
941     ts_thread_t *p_ts = p_data;
942     mtime_t i_buffering_date = -1;
943
944     for( ;; )
945     {
946         ts_cmd_t cmd;
947         mtime_t  i_deadline;
948         bool b_buffering;
949
950         /* Pop a command to execute */
951         vlc_mutex_lock( &p_ts->lock );
952         mutex_cleanup_push( &p_ts->lock );
953
954         for( ;; )
955         {
956             const int canc = vlc_savecancel();
957             b_buffering = es_out_GetBuffering( p_ts->p_out );
958
959             if( ( !p_ts->b_paused || b_buffering ) && !TsPopCmdLocked( p_ts, &cmd, false ) )
960             {
961                 vlc_restorecancel( canc );
962                 break;
963             }
964             vlc_restorecancel( canc );
965
966             vlc_cond_wait( &p_ts->wait, &p_ts->lock );
967         }
968
969         if( b_buffering && i_buffering_date < 0 )
970         {
971             i_buffering_date = cmd.i_date;
972         }
973         else if( i_buffering_date > 0 )
974         {
975             p_ts->i_buffering_delay += i_buffering_date - cmd.i_date; /* It is < 0 */
976             if( b_buffering )
977                 i_buffering_date = cmd.i_date;
978             else
979                 i_buffering_date = -1;
980         }
981
982         if( p_ts->i_rate_date < 0 )
983             p_ts->i_rate_date = cmd.i_date;
984
985         p_ts->i_rate_delay = 0;
986         if( p_ts->i_rate_source != p_ts->i_rate )
987         {
988             const mtime_t i_duration = cmd.i_date - p_ts->i_rate_date;
989             p_ts->i_rate_delay = i_duration * p_ts->i_rate / p_ts->i_rate_source - i_duration;
990         }
991         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 )
992         {
993             const int canc = vlc_savecancel();
994
995             /* Auto reset to rate 1.0 */
996             msg_Warn( p_ts->p_input, "es out timeshift: auto reset rate to %d", p_ts->i_rate_source );
997
998             p_ts->i_cmd_delay = 0;
999             p_ts->i_buffering_delay = 0;
1000
1001             p_ts->i_rate_delay = 0;
1002             p_ts->i_rate_date = -1;
1003             p_ts->i_rate = p_ts->i_rate_source;
1004
1005             if( !es_out_SetRate( p_ts->p_out, p_ts->i_rate_source, p_ts->i_rate ) )
1006             {
1007                 vlc_value_t val = { .i_int = p_ts->i_rate };
1008                 /* Warn back input
1009                  * FIXME it is perfectly safe BUT it is ugly as it may hide a
1010                  * rate change requested by user */
1011                 input_ControlPush( p_ts->p_input, INPUT_CONTROL_SET_RATE, &val );
1012             }
1013
1014             vlc_restorecancel( canc );
1015         }
1016         i_deadline = cmd.i_date + p_ts->i_cmd_delay + p_ts->i_rate_delay + p_ts->i_buffering_delay;
1017
1018         vlc_cleanup_run();
1019
1020         /* Regulate the speed of command processing to the same one than
1021          * reading  */
1022         vlc_cleanup_push( cmd_cleanup_routine, &cmd );
1023
1024         mwait( i_deadline );
1025
1026         vlc_cleanup_pop();
1027
1028         /* Execute the command  */
1029         const int canc = vlc_savecancel();
1030         switch( cmd.i_type )
1031         {
1032         case C_ADD:
1033             CmdExecuteAdd( p_ts->p_out, &cmd );
1034             CmdCleanAdd( &cmd );
1035             break;
1036         case C_SEND:
1037             CmdExecuteSend( p_ts->p_out, &cmd );
1038             CmdCleanSend( &cmd );
1039             break;
1040         case C_CONTROL:
1041             CmdExecuteControl( p_ts->p_out, &cmd );
1042             CmdCleanControl( &cmd );
1043             break;
1044         case C_DEL:
1045             CmdExecuteDel( p_ts->p_out, &cmd );
1046             break;
1047         default:
1048             assert(0);
1049             break;
1050         }
1051         vlc_restorecancel( canc );
1052     }
1053
1054     return NULL;
1055 }
1056
1057 /*****************************************************************************
1058  *
1059  *****************************************************************************/
1060 static ts_storage_t *TsStorageNew( const char *psz_tmp_path, int64_t i_tmp_size_max )
1061 {
1062     ts_storage_t *p_storage = calloc( 1, sizeof(ts_storage_t) );
1063     if( !p_storage )
1064         return NULL;
1065
1066     /* */
1067     p_storage->p_next = NULL;
1068
1069     /* */
1070     p_storage->i_file_max = i_tmp_size_max;
1071     p_storage->i_file_size = 0;
1072     p_storage->p_filew = GetTmpFile( &p_storage->psz_file, psz_tmp_path );
1073     if( p_storage->psz_file )
1074         p_storage->p_filer = vlc_fopen( p_storage->psz_file, "rb" );
1075
1076     /* */
1077     p_storage->i_cmd_w = 0;
1078     p_storage->i_cmd_r = 0;
1079     p_storage->i_cmd_max = 30000;
1080     p_storage->p_cmd = malloc( p_storage->i_cmd_max * sizeof(*p_storage->p_cmd) );
1081     //fprintf( stderr, "\nSTORAGE name=%s size=%d KiB\n", p_storage->psz_file, p_storage->i_cmd_max * sizeof(*p_storage->p_cmd) /1024 );
1082
1083     if( !p_storage->p_cmd || !p_storage->p_filew || !p_storage->p_filer )
1084     {
1085         TsStorageDelete( p_storage );
1086         return NULL;
1087     }
1088     return p_storage;
1089 }
1090 static void TsStorageDelete( ts_storage_t *p_storage )
1091 {
1092     while( p_storage->i_cmd_r < p_storage->i_cmd_w )
1093     {
1094         ts_cmd_t cmd;
1095
1096         TsStoragePopCmd( p_storage, &cmd, true );
1097
1098         CmdClean( &cmd );
1099     }
1100     free( p_storage->p_cmd );
1101
1102     if( p_storage->p_filer )
1103         fclose( p_storage->p_filer );
1104     if( p_storage->p_filew )
1105         fclose( p_storage->p_filew );
1106
1107     if( p_storage->psz_file )
1108     {
1109         vlc_unlink( p_storage->psz_file );
1110         free( p_storage->psz_file );
1111     }
1112
1113     free( p_storage );
1114 }
1115 static void TsStoragePack( ts_storage_t *p_storage )
1116 {
1117     /* Try to release a bit of memory */
1118     if( p_storage->i_cmd_w >= p_storage->i_cmd_max )
1119         return;
1120
1121     p_storage->i_cmd_max = __MAX( p_storage->i_cmd_w, 1 );
1122
1123     ts_cmd_t *p_new = realloc( p_storage->p_cmd, p_storage->i_cmd_max * sizeof(*p_storage->p_cmd) );
1124     if( p_new )
1125         p_storage->p_cmd = p_new;
1126 }
1127 static bool TsStorageIsFull( ts_storage_t *p_storage, const ts_cmd_t *p_cmd )
1128 {
1129     if( p_cmd && p_cmd->i_type == C_SEND && p_storage->i_cmd_w > 0 )
1130     {
1131         size_t i_size = sizeof(*p_cmd->u.send.p_block) + p_cmd->u.send.p_block->i_buffer;
1132
1133         if( p_storage->i_file_size + i_size >= p_storage->i_file_max )
1134             return true;
1135     }
1136     return p_storage->i_cmd_w >= p_storage->i_cmd_max;
1137 }
1138 static bool TsStorageIsEmpty( ts_storage_t *p_storage )
1139 {
1140     return !p_storage || p_storage->i_cmd_r >= p_storage->i_cmd_w;
1141 }
1142 static void TsStoragePushCmd( ts_storage_t *p_storage, const ts_cmd_t *p_cmd, bool b_flush )
1143 {
1144     ts_cmd_t cmd = *p_cmd;
1145
1146     assert( !TsStorageIsFull( p_storage, p_cmd ) );
1147
1148     if( cmd.i_type == C_SEND )
1149     {
1150         block_t *p_block = cmd.u.send.p_block;
1151
1152         cmd.u.send.p_block = NULL;
1153         cmd.u.send.i_offset = ftell( p_storage->p_filew );
1154
1155         if( fwrite( p_block, sizeof(*p_block), 1, p_storage->p_filew ) != 1 )
1156         {
1157             block_Release( p_block );
1158             return;
1159         }
1160         p_storage->i_file_size += sizeof(*p_block);
1161         if( p_block->i_buffer > 0 )
1162         {
1163             if( fwrite( p_block->p_buffer, p_block->i_buffer, 1, p_storage->p_filew ) != 1 )
1164             {
1165                 block_Release( p_block );
1166                 return;
1167             }
1168         }
1169         p_storage->i_file_size += p_block->i_buffer;
1170         block_Release( p_block );
1171
1172         if( b_flush )
1173             fflush( p_storage->p_filew );
1174     }
1175     p_storage->p_cmd[p_storage->i_cmd_w++] = cmd;
1176 }
1177 static void TsStoragePopCmd( ts_storage_t *p_storage, ts_cmd_t *p_cmd, bool b_flush )
1178 {
1179     assert( !TsStorageIsEmpty( p_storage ) );
1180
1181     *p_cmd = p_storage->p_cmd[p_storage->i_cmd_r++];
1182     if( p_cmd->i_type == C_SEND )
1183     {
1184         block_t block;
1185
1186         if( !b_flush &&
1187             !fseek( p_storage->p_filer, p_cmd->u.send.i_offset, SEEK_SET ) &&
1188             fread( &block, sizeof(block), 1, p_storage->p_filer ) == 1 )
1189         {
1190             block_t *p_block = block_Alloc( block.i_buffer );
1191             if( p_block )
1192             {
1193                 p_block->i_dts      = block.i_dts;
1194                 p_block->i_pts      = block.i_pts;
1195                 p_block->i_flags    = block.i_flags;
1196                 p_block->i_length   = block.i_length;
1197                 p_block->i_nb_samples = block.i_nb_samples;
1198                 p_block->i_buffer = fread( p_block->p_buffer, 1, block.i_buffer, p_storage->p_filer );
1199             }
1200             p_cmd->u.send.p_block = p_block;
1201         }
1202         else
1203         {
1204             //fprintf( stderr, "TsStoragePopCmd: %m\n" );
1205             p_cmd->u.send.p_block = block_Alloc( 1 );
1206         }
1207     }
1208 }
1209
1210 /*****************************************************************************
1211  *
1212  *****************************************************************************/
1213 static void CmdClean( ts_cmd_t *p_cmd )
1214 {
1215     switch( p_cmd->i_type )
1216     {
1217     case C_ADD:
1218         CmdCleanAdd( p_cmd );
1219         break;
1220     case C_SEND:
1221         CmdCleanSend( p_cmd );
1222         break;
1223     case C_CONTROL:
1224         CmdCleanControl( p_cmd );
1225         break;
1226     case C_DEL:
1227         break;
1228     default:
1229         assert(0);
1230         break;
1231     }
1232 }
1233
1234 static int CmdInitAdd( ts_cmd_t *p_cmd, es_out_id_t *p_es, const es_format_t *p_fmt, bool b_copy )
1235 {
1236     p_cmd->i_type = C_ADD;
1237     p_cmd->i_date = mdate();
1238     p_cmd->u.add.p_es = p_es;
1239     if( b_copy )
1240     {
1241         p_cmd->u.add.p_fmt = malloc( sizeof(*p_fmt) );
1242         if( !p_cmd->u.add.p_fmt )
1243             return VLC_EGENERIC;
1244         es_format_Copy( p_cmd->u.add.p_fmt, p_fmt );
1245     }
1246     else
1247     {
1248         p_cmd->u.add.p_fmt = (es_format_t*)p_fmt;
1249     }
1250     return VLC_SUCCESS;
1251 }
1252 static void CmdExecuteAdd( es_out_t *p_out, ts_cmd_t *p_cmd )
1253 {
1254     p_cmd->u.add.p_es->p_es = es_out_Add( p_out, p_cmd->u.add.p_fmt );
1255 }
1256 static void CmdCleanAdd( ts_cmd_t *p_cmd )
1257 {
1258     es_format_Clean( p_cmd->u.add.p_fmt );
1259     free( p_cmd->u.add.p_fmt );
1260 }
1261
1262 static void CmdInitSend( ts_cmd_t *p_cmd, es_out_id_t *p_es, block_t *p_block )
1263 {
1264     p_cmd->i_type = C_SEND;
1265     p_cmd->i_date = mdate();
1266     p_cmd->u.send.p_es = p_es;
1267     p_cmd->u.send.p_block = p_block;
1268 }
1269 static int CmdExecuteSend( es_out_t *p_out, ts_cmd_t *p_cmd )
1270 {
1271     block_t *p_block = p_cmd->u.send.p_block;
1272
1273     p_cmd->u.send.p_block = NULL;
1274
1275     if( p_block )
1276     {
1277         if( p_cmd->u.send.p_es->p_es )
1278             return es_out_Send( p_out, p_cmd->u.send.p_es->p_es, p_block );
1279         block_Release( p_block );
1280     }
1281     return VLC_EGENERIC;
1282 }
1283 static void CmdCleanSend( ts_cmd_t *p_cmd )
1284 {
1285     if( p_cmd->u.send.p_block )
1286         block_Release( p_cmd->u.send.p_block );
1287 }
1288
1289 static int CmdInitDel( ts_cmd_t *p_cmd, es_out_id_t *p_es )
1290 {
1291     p_cmd->i_type = C_DEL;
1292     p_cmd->i_date = mdate();
1293     p_cmd->u.del.p_es = p_es;
1294     return VLC_SUCCESS;
1295 }
1296 static void CmdExecuteDel( es_out_t *p_out, ts_cmd_t *p_cmd )
1297 {
1298     if( p_cmd->u.del.p_es->p_es )
1299         es_out_Del( p_out, p_cmd->u.del.p_es->p_es );
1300     free( p_cmd->u.del.p_es );
1301 }
1302
1303 static int CmdInitControl( ts_cmd_t *p_cmd, int i_query, va_list args, bool b_copy )
1304 {
1305     p_cmd->i_type = C_CONTROL;
1306     p_cmd->i_date = mdate();
1307     p_cmd->u.control.i_query = i_query;
1308
1309     switch( i_query )
1310     {
1311     /* Pass-through control */
1312     case ES_OUT_SET_MODE:    /* arg1= int                            */
1313     case ES_OUT_SET_GROUP:   /* arg1= int                            */
1314     case ES_OUT_DEL_GROUP:   /* arg1=int i_group */
1315         p_cmd->u.control.u.i_int = (int)va_arg( args, int );
1316         break;
1317
1318     case ES_OUT_SET_PCR:                /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
1319     case ES_OUT_SET_NEXT_DISPLAY_TIME:  /* arg1=int64_t i_pts(microsecond) */
1320         p_cmd->u.control.u.i_i64 = (int64_t)va_arg( args, int64_t );
1321         break;
1322
1323     case ES_OUT_SET_GROUP_PCR:          /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
1324         p_cmd->u.control.u.int_i64.i_int = (int)va_arg( args, int );
1325         p_cmd->u.control.u.int_i64.i_i64 = (int64_t)va_arg( args, int64_t );
1326         break;
1327
1328     case ES_OUT_SET_ES_SCRAMBLED_STATE:
1329         p_cmd->u.control.u.es_bool.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
1330         p_cmd->u.control.u.es_bool.b_bool = (bool)va_arg( args, int );
1331         break;
1332
1333     case ES_OUT_RESET_PCR:           /* no arg */
1334     case ES_OUT_SET_EOS:
1335         break;
1336
1337     case ES_OUT_SET_META:        /* arg1=const vlc_meta_t* */
1338     case ES_OUT_SET_GROUP_META:  /* arg1=int i_group arg2=const vlc_meta_t* */
1339     {
1340         if( i_query == ES_OUT_SET_GROUP_META )
1341             p_cmd->u.control.u.int_meta.i_int = (int)va_arg( args, int );
1342         const vlc_meta_t *p_meta = va_arg( args, const vlc_meta_t * );
1343
1344         if( b_copy )
1345         {
1346             p_cmd->u.control.u.int_meta.p_meta = vlc_meta_New();
1347             if( !p_cmd->u.control.u.int_meta.p_meta )
1348                 return VLC_EGENERIC;
1349             vlc_meta_Merge( p_cmd->u.control.u.int_meta.p_meta, p_meta );
1350         }
1351         else
1352         {
1353             /* The cast is only needed to avoid warning */
1354             p_cmd->u.control.u.int_meta.p_meta = (vlc_meta_t*)p_meta;
1355         }
1356         break;
1357     }
1358
1359     case ES_OUT_SET_GROUP_EPG:   /* arg1=int i_group arg2=const vlc_epg_t* */
1360     {
1361         p_cmd->u.control.u.int_epg.i_int = (int)va_arg( args, int );
1362         const vlc_epg_t *p_epg = va_arg( args, const vlc_epg_t * );
1363
1364         if( b_copy )
1365         {
1366             p_cmd->u.control.u.int_epg.p_epg = vlc_epg_New( p_epg->psz_name );
1367             if( !p_cmd->u.control.u.int_epg.p_epg )
1368                 return VLC_EGENERIC;
1369             for( int i = 0; i < p_epg->i_event; i++ )
1370             {
1371                 vlc_epg_event_t *p_evt = p_epg->pp_event[i];
1372
1373                 vlc_epg_AddEvent( p_cmd->u.control.u.int_epg.p_epg,
1374                                   p_evt->i_start, p_evt->i_duration,
1375                                   p_evt->psz_name,
1376                                   p_evt->psz_short_description, p_evt->psz_description );
1377             }
1378             vlc_epg_SetCurrent( p_cmd->u.control.u.int_epg.p_epg,
1379                                 p_epg->p_current ? p_epg->p_current->i_start : -1 );
1380         }
1381         else
1382         {
1383             /* The cast is only needed to avoid warning */
1384             p_cmd->u.control.u.int_epg.p_epg = (vlc_epg_t*)p_epg;
1385         }
1386         break;
1387     }
1388
1389     /* Modified control */
1390     case ES_OUT_SET_ES:      /* arg1= es_out_id_t*                   */
1391     case ES_OUT_RESTART_ES:  /* arg1= es_out_id_t*                   */
1392     case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t*                */
1393         p_cmd->u.control.u.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
1394         break;
1395
1396     case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool   */
1397         p_cmd->u.control.u.es_bool.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
1398         p_cmd->u.control.u.es_bool.b_bool = (bool)va_arg( args, int );
1399         break;
1400
1401     case ES_OUT_SET_ES_FMT:     /* arg1= es_out_id_t* arg2=es_format_t* */
1402     {
1403         p_cmd->u.control.u.es_fmt.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
1404         es_format_t *p_fmt = (es_format_t*)va_arg( args, es_format_t * );
1405
1406         if( b_copy )
1407         {
1408             p_cmd->u.control.u.es_fmt.p_fmt = malloc( sizeof(*p_fmt) );
1409             if( !p_cmd->u.control.u.es_fmt.p_fmt )
1410                 return VLC_EGENERIC;
1411             es_format_Copy( p_cmd->u.control.u.es_fmt.p_fmt, p_fmt );
1412         }
1413         else
1414         {
1415             p_cmd->u.control.u.es_fmt.p_fmt = p_fmt;
1416         }
1417         break;
1418     }
1419     case ES_OUT_SET_TIMES:
1420     {
1421         double f_position = (double)va_arg( args, double );
1422         mtime_t i_time = (mtime_t)va_arg( args, mtime_t );
1423         mtime_t i_length = (mtime_t)va_arg( args, mtime_t );
1424
1425         p_cmd->u.control.u.times.f_position = f_position;
1426         p_cmd->u.control.u.times.i_time = i_time;
1427         p_cmd->u.control.u.times.i_length = i_length;
1428         break;
1429     }
1430     case ES_OUT_SET_JITTER:
1431     {
1432         mtime_t i_pts_delay = (mtime_t)va_arg( args, mtime_t );
1433         mtime_t i_pts_jitter = (mtime_t)va_arg( args, mtime_t );
1434         int     i_cr_average = (int)va_arg( args, int );
1435
1436         p_cmd->u.control.u.jitter.i_pts_delay = i_pts_delay;
1437         p_cmd->u.control.u.jitter.i_pts_jitter = i_pts_jitter;
1438         p_cmd->u.control.u.jitter.i_cr_average = i_cr_average;
1439         break;
1440     }
1441
1442     default:
1443         assert(0);
1444         return VLC_EGENERIC;
1445     }
1446
1447     return VLC_SUCCESS;
1448 }
1449 static int CmdExecuteControl( es_out_t *p_out, ts_cmd_t *p_cmd )
1450 {
1451     const int i_query = p_cmd->u.control.i_query;
1452
1453     switch( i_query )
1454     {
1455     /* Pass-through control */
1456     case ES_OUT_SET_MODE:    /* arg1= int                            */
1457     case ES_OUT_SET_GROUP:   /* arg1= int                            */
1458     case ES_OUT_DEL_GROUP:   /* arg1=int i_group */
1459         return es_out_Control( p_out, i_query, p_cmd->u.control.u.i_int );
1460
1461     case ES_OUT_SET_PCR:                /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
1462     case ES_OUT_SET_NEXT_DISPLAY_TIME:  /* arg1=int64_t i_pts(microsecond) */
1463         return es_out_Control( p_out, i_query, p_cmd->u.control.u.i_i64 );
1464
1465     case ES_OUT_SET_GROUP_PCR:          /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
1466         return es_out_Control( p_out, i_query, p_cmd->u.control.u.int_i64.i_int,
1467                                                p_cmd->u.control.u.int_i64.i_i64 );
1468
1469     case ES_OUT_RESET_PCR:           /* no arg */
1470     case ES_OUT_SET_EOS:
1471         return es_out_Control( p_out, i_query );
1472
1473     case ES_OUT_SET_GROUP_META:  /* arg1=int i_group arg2=const vlc_meta_t* */
1474         return es_out_Control( p_out, i_query, p_cmd->u.control.u.int_meta.i_int,
1475                                                p_cmd->u.control.u.int_meta.p_meta );
1476
1477     case ES_OUT_SET_GROUP_EPG:   /* arg1=int i_group arg2=const vlc_epg_t* */
1478         return es_out_Control( p_out, i_query, p_cmd->u.control.u.int_epg.i_int,
1479                                                p_cmd->u.control.u.int_epg.p_epg );
1480
1481     case ES_OUT_SET_ES_SCRAMBLED_STATE: /* arg1=int es_out_id_t* arg2=bool */
1482         return es_out_Control( p_out, i_query, p_cmd->u.control.u.es_bool.p_es->p_es,
1483                                                p_cmd->u.control.u.es_bool.b_bool );
1484
1485     case ES_OUT_SET_META:  /* arg1=const vlc_meta_t* */
1486         return es_out_Control( p_out, i_query, p_cmd->u.control.u.int_meta.p_meta );
1487
1488     /* Modified control */
1489     case ES_OUT_SET_ES:      /* arg1= es_out_id_t*                   */
1490     case ES_OUT_RESTART_ES:  /* arg1= es_out_id_t*                   */
1491     case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t*                */
1492         return es_out_Control( p_out, i_query, p_cmd->u.control.u.p_es->p_es );
1493
1494     case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool   */
1495         return es_out_Control( p_out, i_query, p_cmd->u.control.u.es_bool.p_es->p_es,
1496                                                p_cmd->u.control.u.es_bool.b_bool );
1497
1498     case ES_OUT_SET_ES_FMT:     /* arg1= es_out_id_t* arg2=es_format_t* */
1499         return es_out_Control( p_out, i_query, p_cmd->u.control.u.es_fmt.p_es->p_es,
1500                                                p_cmd->u.control.u.es_fmt.p_fmt );
1501
1502     case ES_OUT_SET_TIMES:
1503         return es_out_Control( p_out, i_query, p_cmd->u.control.u.times.f_position,
1504                                                p_cmd->u.control.u.times.i_time,
1505                                                p_cmd->u.control.u.times.i_length );
1506     case ES_OUT_SET_JITTER:
1507         return es_out_Control( p_out, i_query, p_cmd->u.control.u.jitter.i_pts_delay,
1508                                                p_cmd->u.control.u.jitter.i_pts_jitter,
1509                                                p_cmd->u.control.u.jitter.i_cr_average );
1510
1511     default:
1512         assert(0);
1513         return VLC_EGENERIC;
1514     }
1515 }
1516 static void CmdCleanControl( ts_cmd_t *p_cmd )
1517 {
1518     if( ( p_cmd->u.control.i_query == ES_OUT_SET_GROUP_META ||
1519           p_cmd->u.control.i_query == ES_OUT_SET_META ) &&
1520         p_cmd->u.control.u.int_meta.p_meta )
1521     {
1522         vlc_meta_Delete( p_cmd->u.control.u.int_meta.p_meta );
1523     }
1524     else if( p_cmd->u.control.i_query == ES_OUT_SET_GROUP_EPG &&
1525              p_cmd->u.control.u.int_epg.p_epg )
1526     {
1527         vlc_epg_Delete( p_cmd->u.control.u.int_epg.p_epg );
1528     }
1529     else if( p_cmd->u.control.i_query == ES_OUT_SET_ES_FMT &&
1530              p_cmd->u.control.u.es_fmt.p_fmt )
1531     {
1532         es_format_Clean( p_cmd->u.control.u.es_fmt.p_fmt );
1533         free( p_cmd->u.control.u.es_fmt.p_fmt );
1534     }
1535 }
1536
1537
1538 /*****************************************************************************
1539  * GetTmpFile/Path:
1540  *****************************************************************************/
1541 static char *GetTmpPath( char *psz_path )
1542 {
1543     if( psz_path && *psz_path )
1544     {
1545         /* Make sure that the path exists and is a directory */
1546         struct stat s;
1547         const int i_ret = vlc_stat( psz_path, &s );
1548
1549         if( i_ret < 0 && !vlc_mkdir( psz_path, 0600 ) )
1550             return psz_path;
1551         else if( i_ret == 0 && ( s.st_mode & S_IFDIR ) )
1552             return psz_path;
1553     }
1554     free( psz_path );
1555
1556     /* Create a suitable path */
1557 #if defined (WIN32) && !defined (UNDER_CE)
1558     const DWORD dwCount = GetTempPathW( 0, NULL );
1559     wchar_t *psw_path = calloc( dwCount + 1, sizeof(wchar_t) );
1560     if( psw_path )
1561     {
1562         if( GetTempPathW( dwCount + 1, psw_path ) <= 0 )
1563         {
1564             free( psw_path );
1565
1566             psw_path = _wgetcwd( NULL, 0 );
1567         }
1568     }
1569
1570     psz_path = NULL;
1571     if( psw_path )
1572     {
1573         psz_path = FromWide( psw_path );
1574         while( psz_path && *psz_path && psz_path[strlen( psz_path ) - 1] == '\\' )
1575             psz_path[strlen( psz_path ) - 1] = '\0';
1576
1577         free( psw_path );
1578     }
1579
1580     if( !psz_path || *psz_path == '\0' )
1581     {
1582         free( psz_path );
1583         return strdup( "C:" );
1584     }
1585 #else
1586     psz_path = strdup( DIR_SEP"tmp" );
1587 #endif
1588
1589     return psz_path;
1590 }
1591
1592 static FILE *GetTmpFile( char **ppsz_file, const char *psz_path )
1593 {
1594     char *psz_name;
1595     int fd;
1596     FILE *f;
1597
1598     /* */
1599     *ppsz_file = NULL;
1600     if( asprintf( &psz_name, "%s/vlc-timeshift.XXXXXX", psz_path ) < 0 )
1601         return NULL;
1602
1603     /* */
1604     fd = vlc_mkstemp( psz_name );
1605     *ppsz_file = psz_name;
1606
1607     if( fd < 0 )
1608         return NULL;
1609
1610     /* */
1611     f = fdopen( fd, "w+b" );
1612     if( !f )
1613         close( fd );
1614
1615     return f;
1616 }
1617