]> git.sesse.net Git - vlc/blob - src/input/es_out_timeshift.c
Cosmetic.
[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_ via _DOT_ ecp _DOT_ fr>
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 <errno.h>
34 #include <assert.h>
35 #if defined (WIN32) && !defined (UNDER_CE)
36 #  include <direct.h>
37 #endif
38 #ifdef HAVE_SYS_STAT_H
39 #   include <sys/stat.h>
40 #endif
41
42 #include <vlc_common.h>
43 #include <vlc_charset.h>
44
45 #include <vlc_input.h>
46 #include <vlc_es_out.h>
47 #include <vlc_block.h>
48 #include "input_internal.h"
49 #include "es_out.h"
50 #include "es_out_timeshift.h"
51
52 /*****************************************************************************
53  * Local prototypes
54  *****************************************************************************/
55
56 enum
57 {
58     C_ADD,
59     C_SEND,
60     C_DEL,
61     C_CONTROL,
62 };
63
64 typedef struct
65 {
66     int i_type;
67     union
68     {
69         struct
70         {
71             es_out_id_t *p_es;
72             es_format_t *p_fmt;
73         } add;
74         struct
75         {
76             es_out_id_t *p_es;
77         } del;
78         struct
79         {
80             es_out_id_t *p_es;
81             block_t *p_block;
82         } send;
83         struct
84         {
85             int  i_query;
86
87             bool b_bool;
88             bool *pb_bool;
89             int  i_int;
90             int  *pi_int;
91             int64_t i_i64;
92             vlc_meta_t *p_meta;
93             vlc_epg_t *p_epg;
94             es_out_id_t *p_es;
95             es_format_t *p_fmt;
96         } control;
97     };
98 } ts_cmd_t;
99
100 struct es_out_id_t
101 {
102     es_out_id_t *p_es;
103 };
104
105 struct es_out_sys_t
106 {
107     input_thread_t *p_input;
108         es_out_t       *p_out;
109
110     /* Configuration */
111     int64_t        i_tmp_size_max;    /* Maximal temporary file size in byte */
112     char           *psz_tmp_path;     /* Path for temporary files */
113
114     /* Lock for all following fields */
115     vlc_mutex_t    lock;
116     vlc_cond_t     wait;
117
118     /* */
119     bool           b_delayed;
120     vlc_object_t   *p_thread;
121
122     /* */
123     int            i_es;
124     es_out_id_t    **pp_es;
125
126     /* */
127     int            i_cmd;
128     ts_cmd_t       **pp_cmd;
129 };
130
131 static es_out_id_t *Add    ( es_out_t *, const es_format_t * );
132 static int          Send   ( es_out_t *, es_out_id_t *, block_t * );
133 static void         Del    ( es_out_t *, es_out_id_t * );
134 static int          Control( es_out_t *, int i_query, va_list );
135 static void         Destroy( es_out_t * );
136
137 static int          TsStart( es_out_t * );
138 static void         TsStop( es_out_t * );
139 static void         *TsRun( vlc_object_t * );
140
141 static void CmdPush( es_out_t *, const ts_cmd_t * );
142 static int  CmdPop( es_out_t *p_out, ts_cmd_t *p_cmd );
143
144 static int  CmdInitAdd    ( ts_cmd_t *, es_out_id_t *, const es_format_t *, bool b_copy );
145 static void CmdInitSend   ( ts_cmd_t *, es_out_id_t *, block_t * );
146 static int  CmdInitDel    ( ts_cmd_t *, es_out_id_t * );
147 static int  CmdInitControl( ts_cmd_t *, int i_query, va_list, bool b_copy );
148
149 static void CmdExecuteAdd    ( es_out_t *, ts_cmd_t * );
150 static int  CmdExecuteSend   ( es_out_t *, ts_cmd_t * );
151 static void CmdExecuteDel    ( es_out_t *, ts_cmd_t * );
152 static int  CmdExecuteControl( es_out_t *, ts_cmd_t * );
153
154 static void CmdCleanAdd    ( ts_cmd_t * );
155 static void CmdCleanSend   ( ts_cmd_t * );
156 static void CmdCleanControl( ts_cmd_t *p_cmd );
157
158 static char *GetTmpPath( char *psz_path );
159 static FILE *GetTmpFile( const char *psz_path );
160
161 /*****************************************************************************
162  * input_EsOutTimeshiftNew:
163  *****************************************************************************/
164 es_out_t *input_EsOutTimeshiftNew( input_thread_t *p_input, es_out_t *p_next_out )
165 {
166     es_out_t *p_out = malloc( sizeof(*p_out) );
167     if( !p_out )
168         return NULL;
169
170     es_out_sys_t *p_sys = malloc( sizeof(*p_sys) );
171     if( !p_sys )
172     {
173         free( p_out );
174         return NULL;
175     }
176
177     /* */
178     p_out->pf_add     = Add;
179     p_out->pf_send    = Send;
180     p_out->pf_del     = Del;
181     p_out->pf_control = Control;
182     p_out->pf_destroy = Destroy;
183     p_out->p_sys      = p_sys;
184     p_out->b_sout     = p_input->p->p_sout != NULL;
185
186     /* */
187     p_sys->p_input = p_input;
188     p_sys->p_out = p_next_out;
189     vlc_mutex_init_recursive( &p_sys->lock );
190     vlc_cond_init( &p_sys->wait );
191
192     p_sys->b_delayed = false;
193     p_sys->p_thread = NULL;
194
195     TAB_INIT( p_sys->i_es, p_sys->pp_es );
196     TAB_INIT( p_sys->i_cmd, p_sys->pp_cmd );
197
198     /* TODO config
199      * timeshift-granularity
200      * timeshift-path
201      */
202     p_sys->i_tmp_size_max = 50 * 1024*1024;
203     p_sys->psz_tmp_path = GetTmpPath( NULL );
204
205     return p_out;
206 }
207
208 /*****************************************************************************
209  * Internal functions
210  *****************************************************************************/
211 static void Destroy( es_out_t *p_out )
212 {
213     es_out_sys_t *p_sys = p_out->p_sys;
214
215     if( p_sys->b_delayed )
216         TsStop( p_out );
217
218     for( ;; )
219     {
220         ts_cmd_t cmd;
221
222         if( CmdPop( p_out, &cmd ) )
223             break;
224
225         switch( cmd.i_type )
226         {
227         case C_ADD:
228             CmdCleanAdd( &cmd );
229             break;
230         case C_SEND:
231             CmdCleanSend( &cmd );
232             break;
233         case C_CONTROL:
234             CmdCleanControl( &cmd );
235             break;
236         case C_DEL:
237             break;
238         default:
239             assert(0);
240             break;
241         }
242     }
243     TAB_CLEAN( p_sys->i_cmd, p_sys->pp_cmd  );
244
245     while( p_sys->i_es > 0 )
246         Del( p_out, p_sys->pp_es[0] );
247     TAB_CLEAN( p_sys->i_es, p_sys->pp_es  );
248
249     free( p_sys->psz_tmp_path );
250     vlc_cond_destroy( &p_sys->wait );
251     vlc_mutex_destroy( &p_sys->lock );
252     free( p_sys );
253     free( p_out );
254 }
255
256 static es_out_id_t *Add( es_out_t *p_out, const es_format_t *p_fmt )
257 {
258     es_out_sys_t *p_sys = p_out->p_sys;
259     ts_cmd_t cmd;
260
261     es_out_id_t *p_es = malloc( sizeof( *p_es ) );
262     if( !p_es )
263         return NULL;
264
265     vlc_mutex_lock( &p_sys->lock );
266
267     if( CmdInitAdd( &cmd, p_es, p_fmt, p_sys->b_delayed ) )
268     {
269         vlc_mutex_unlock( &p_sys->lock );
270         free( p_es );
271         return NULL;
272     }
273
274     TAB_APPEND( p_sys->i_es, p_sys->pp_es, p_es );
275
276     if( p_sys->b_delayed )
277         CmdPush( p_out, &cmd );
278     else
279         CmdExecuteAdd( p_out, &cmd );
280
281     vlc_mutex_unlock( &p_sys->lock );
282
283     return p_es;
284 }
285 static int Send( es_out_t *p_out, es_out_id_t *p_es, block_t *p_block )
286 {
287     es_out_sys_t *p_sys = p_out->p_sys;
288     ts_cmd_t cmd;
289     int i_ret = VLC_SUCCESS;
290
291     vlc_mutex_lock( &p_sys->lock );
292
293     CmdInitSend( &cmd, p_es, p_block );
294     if( p_sys->b_delayed )
295         CmdPush( p_out, &cmd );
296     else
297         i_ret = CmdExecuteSend( p_out, &cmd) ;
298
299     vlc_mutex_unlock( &p_sys->lock );
300
301     return i_ret;
302 }
303 static void Del( es_out_t *p_out, es_out_id_t *p_es )
304 {
305     es_out_sys_t *p_sys = p_out->p_sys;
306     ts_cmd_t cmd;
307
308     vlc_mutex_lock( &p_sys->lock );
309
310     CmdInitDel( &cmd, p_es );
311     if( p_sys->b_delayed )
312         CmdPush( p_out, &cmd );
313     else
314         CmdExecuteDel( p_out, &cmd );
315
316     TAB_REMOVE( p_sys->i_es, p_sys->pp_es, p_es );
317
318     vlc_mutex_unlock( &p_sys->lock );
319 }
320
321 static int ControlLockedGetEmpty( es_out_t *p_out, bool *pb_empty )
322 {
323     es_out_sys_t *p_sys = p_out->p_sys;
324
325     if( p_sys->b_delayed && p_sys->i_cmd > 0 )
326         *pb_empty = false;
327     else
328         *pb_empty = es_out_GetEmpty( p_sys->p_out );
329
330     return VLC_SUCCESS;
331 }
332 static int ControlLockedGetWakeup( es_out_t *p_out, mtime_t *pi_wakeup )
333 {
334     es_out_sys_t *p_sys = p_out->p_sys;
335
336     if( p_sys->b_delayed )
337     {
338         assert( !p_sys->p_input->b_can_pace_control );
339         *pi_wakeup = 0;
340     }
341     else
342     {
343         *pi_wakeup = es_out_GetWakeup( p_sys->p_out );
344     }
345
346     return VLC_SUCCESS;
347 }
348 static int ControlLockedGetBuffering( es_out_t *p_out, bool *pb_buffering )
349 {
350     es_out_sys_t *p_sys = p_out->p_sys;
351
352     if( p_sys->b_delayed )
353         *pb_buffering = true;
354     else
355         *pb_buffering = es_out_GetBuffering( p_sys->p_out );
356
357     return VLC_SUCCESS;
358 }
359 static int ControlLockedSetPauseState( es_out_t *p_out, bool b_source_paused, bool b_paused, mtime_t i_date )
360 {
361     es_out_sys_t *p_sys = p_out->p_sys;
362
363     if( !p_sys->b_delayed &&
364         !b_source_paused == !b_paused )
365     {
366         return es_out_SetPauseState( p_sys->p_out, b_source_paused, b_paused, i_date );
367     }
368
369     if( p_sys->p_input->b_can_pace_control )
370     {
371         /* XXX we may do it BUT it would be better to finish the clock clean up+improvments
372          * and so be able to advertize correctly pace control property in access
373          * module */
374         msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have space control" );
375         return VLC_EGENERIC;
376     }
377
378     if( b_paused )
379     {
380         assert( !b_source_paused );
381
382         if( !p_sys->b_delayed )
383             TsStart( p_out );
384
385         return es_out_SetPauseState( p_sys->p_out, true, true, i_date );
386     }
387     else
388     {
389         /* TODO store pause state to know if stoping b_delayed is possible ? */
390         return es_out_SetPauseState( p_sys->p_out, false, false, i_date );
391     }
392 }
393 static int ControlLockedSetRate( es_out_t *p_out, int i_src_rate, int i_rate )
394 {
395     es_out_sys_t *p_sys = p_out->p_sys;
396
397     if( !p_sys->b_delayed &&
398         i_src_rate == i_rate )
399     {
400         return es_out_SetRate( p_sys->p_out, i_src_rate, i_rate );
401     }
402     /* TODO */
403     msg_Err( p_sys->p_input, "EsOutTimeshift does not yet support rate change" );
404     return VLC_EGENERIC;
405 }
406 static int ControlLockedSetTime( es_out_t *p_out, mtime_t i_date )
407 {
408     es_out_sys_t *p_sys = p_out->p_sys;
409
410     if( !p_sys->b_delayed )
411         return es_out_SetTime( p_sys->p_out, i_date );
412
413     /* TODO */
414     msg_Err( p_sys->p_input, "EsOutTimeshift does not yet support time change" );
415     return VLC_EGENERIC;
416 }
417 static int ControlLockedSetFrameNext( es_out_t *p_out )
418 {
419     es_out_sys_t *p_sys = p_out->p_sys;
420
421     if( !p_sys->b_delayed )
422         return es_out_SetFrameNext( p_sys->p_out );
423
424     /* TODO */
425     msg_Err( p_sys->p_input, "EsOutTimeshift does not yet support frame next" );
426     return VLC_EGENERIC;
427 }
428
429 static int ControlLocked( es_out_t *p_out, int i_query, va_list args )
430 {
431     es_out_sys_t *p_sys = p_out->p_sys;
432
433     switch( i_query )
434     {
435     /* Invalid query for this es_out level */
436     case ES_OUT_SET_ES_BY_ID:
437     case ES_OUT_RESTART_ES_BY_ID:
438     case ES_OUT_SET_ES_DEFAULT_BY_ID:
439     case ES_OUT_SET_DELAY:
440     case ES_OUT_SET_RECORD_STATE:
441         assert(0);
442         return VLC_EGENERIC;
443
444     /* TODO ? or to remove ? */
445     case ES_OUT_GET_TS:
446         return VLC_EGENERIC;
447
448     /* Pass-through control */
449     case ES_OUT_SET_ACTIVE:
450     case ES_OUT_GET_ACTIVE:
451     case ES_OUT_SET_MODE:
452     case ES_OUT_GET_MODE:
453     case ES_OUT_SET_GROUP:
454     case ES_OUT_GET_GROUP:
455     case ES_OUT_SET_PCR:
456     case ES_OUT_SET_GROUP_PCR:
457     case ES_OUT_RESET_PCR:
458     case ES_OUT_SET_NEXT_DISPLAY_TIME:
459     case ES_OUT_SET_GROUP_META:
460     case ES_OUT_SET_GROUP_EPG:
461     case ES_OUT_DEL_GROUP:
462     case ES_OUT_SET_ES:
463     case ES_OUT_RESTART_ES:
464     case ES_OUT_SET_ES_DEFAULT:
465     case ES_OUT_SET_ES_STATE:
466     case ES_OUT_GET_ES_STATE:
467     case ES_OUT_SET_ES_FMT:
468     {
469         ts_cmd_t cmd;
470         if( CmdInitControl( &cmd, i_query, args, p_sys->b_delayed ) )
471             return VLC_EGENERIC;
472         if( p_sys->b_delayed )
473         {
474             CmdPush( p_out, &cmd );
475             return VLC_SUCCESS;
476         }
477         return CmdExecuteControl( p_out, &cmd );
478     }
479
480     /* Special control */
481     case ES_OUT_GET_EMPTY:
482     {
483         bool *pb_empty = (bool*)va_arg( args, bool* );
484         return ControlLockedGetEmpty( p_out, pb_empty );
485     }
486     case ES_OUT_GET_WAKE_UP: /* TODO ? */
487     {
488         mtime_t *pi_wakeup = (mtime_t*)va_arg( args, mtime_t* );
489         return ControlLockedGetWakeup( p_out, pi_wakeup );
490     }
491     case ES_OUT_GET_BUFFERING:
492     {
493         bool *pb_buffering = (bool *)va_arg( args, bool* );
494         return ControlLockedGetBuffering( p_out, pb_buffering );
495     }
496     case ES_OUT_SET_PAUSE_STATE:
497     {
498         const bool b_source_paused = (bool)va_arg( args, int );
499         const bool b_paused = (bool)va_arg( args, int );
500         const mtime_t i_date = (mtime_t) va_arg( args, mtime_t );
501
502         return ControlLockedSetPauseState( p_out, b_source_paused, b_paused, i_date );
503     }
504     case ES_OUT_SET_RATE:
505     {
506         const int i_src_rate = (int)va_arg( args, int );
507         const int i_rate = (int)va_arg( args, int );
508
509         return ControlLockedSetRate( p_out, i_src_rate, i_rate );
510     }
511     case ES_OUT_SET_TIME:
512     {
513         const mtime_t i_date = (mtime_t)va_arg( args, mtime_t );
514
515         return ControlLockedSetTime( p_out, i_date );
516     }
517     case ES_OUT_SET_FRAME_NEXT:
518     {
519         return ControlLockedSetFrameNext( p_out );
520     }
521
522     default:
523         msg_Err( p_sys->p_input, "Unknown es_out_Control query !" );
524         assert(0);
525         return VLC_EGENERIC;
526     }
527 }
528 static int Control( es_out_t *p_out, int i_query, va_list args )
529 {
530     es_out_sys_t *p_sys = p_out->p_sys;
531     int i_ret;
532
533     vlc_mutex_lock( &p_sys->lock );
534     i_ret = ControlLocked( p_out, i_query, args );
535     vlc_mutex_unlock( &p_sys->lock );
536
537     return i_ret;
538 }
539
540 /*****************************************************************************
541  *
542  *****************************************************************************/
543 static int TsStart( es_out_t *p_out )
544 {
545     es_out_sys_t *p_sys = p_out->p_sys;
546
547     assert( !p_sys->b_delayed );
548
549     p_sys->p_thread = vlc_custom_create( p_sys->p_input, sizeof(vlc_object_t),
550                                          VLC_OBJECT_GENERIC, "es out timeshift" );
551     if( !p_sys->p_thread )
552         return VLC_EGENERIC;
553
554     p_sys->b_delayed = true;
555     p_sys->p_thread->p_private = p_out;
556     if( vlc_thread_create( p_sys->p_thread, "es out timeshift",
557                            TsRun, VLC_THREAD_PRIORITY_INPUT, false ) )
558     {
559         msg_Err( p_sys->p_input, "cannot create input thread" );
560         vlc_object_release( p_sys->p_thread );
561
562         p_sys->b_delayed = false;
563         return VLC_EGENERIC;
564     }
565
566     return VLC_SUCCESS;
567 }
568 static void TsStop( es_out_t *p_out )
569 {
570     es_out_sys_t *p_sys = p_out->p_sys;
571
572     assert( p_sys->b_delayed );
573
574     vlc_object_kill( p_sys->p_thread );
575     vlc_thread_join( p_sys->p_thread );
576
577     p_sys->b_delayed = false;
578 }
579 static void *TsRun( vlc_object_t *p_thread )
580 {
581     es_out_t *p_out = p_thread->p_private;
582     es_out_sys_t *p_sys = p_out->p_sys;
583
584     for( ;; )
585     {
586         ts_cmd_t cmd;
587
588         vlc_mutex_lock( &p_sys->lock );
589         mutex_cleanup_push( &p_sys->lock );
590
591         while( CmdPop( p_out, &cmd ) )
592             vlc_cond_wait( &p_sys->wait, &p_sys->lock );
593
594         /* TODO we MUST regulate the speed of this thread, to the same speed
595          * than the reading */
596
597         const int canc = vlc_savecancel();
598         switch( cmd.i_type )
599         {
600         case C_ADD:
601             CmdExecuteAdd( p_out, &cmd );
602             CmdCleanAdd( &cmd );
603             break;
604         case C_SEND:
605             CmdExecuteSend( p_out, &cmd );
606             CmdCleanSend( &cmd );
607             break;
608         case C_CONTROL:
609             CmdExecuteControl( p_out, &cmd );
610             CmdCleanControl( &cmd );
611             break;
612         case C_DEL:
613             CmdExecuteDel( p_out, &cmd );
614             break;
615         default:
616             assert(0);
617             break;
618         }
619         vlc_restorecancel( canc );
620
621         vlc_cleanup_pop();
622         vlc_mutex_unlock( &p_sys->lock );
623     }
624
625     return NULL;
626 }
627
628 /*****************************************************************************
629  *
630  *****************************************************************************/
631 static void CmdPush( es_out_t *p_out, const ts_cmd_t *p_cmd )
632 {
633     es_out_sys_t *p_sys = p_out->p_sys;
634     ts_cmd_t *p_dup = malloc( sizeof(*p_dup) );
635
636     if( p_dup )
637     {
638         *p_dup = *p_cmd;
639         TAB_APPEND( p_sys->i_cmd, p_sys->pp_cmd, p_dup );
640
641         vlc_cond_signal( &p_sys->wait );
642     }
643 }
644 static int CmdPop( es_out_t *p_out, ts_cmd_t *p_cmd )
645 {
646     es_out_sys_t *p_sys = p_out->p_sys;
647
648     if( p_sys->i_cmd <= 0 )
649         return VLC_EGENERIC;
650
651     *p_cmd = *p_sys->pp_cmd[0];
652
653     free( p_sys->pp_cmd[0] );
654     TAB_REMOVE( p_sys->i_cmd, p_sys->pp_cmd, p_sys->pp_cmd[0] );
655     return VLC_SUCCESS;
656 }
657
658 static int CmdInitAdd( ts_cmd_t *p_cmd, es_out_id_t *p_es, const es_format_t *p_fmt, bool b_copy )
659 {
660     p_cmd->i_type = C_ADD;
661     p_cmd->add.p_es = p_es;
662     if( b_copy )
663     {
664         p_cmd->add.p_fmt = malloc( sizeof(*p_fmt) );
665         if( !p_cmd->add.p_fmt )
666             return VLC_EGENERIC;
667         es_format_Copy( p_cmd->add.p_fmt, p_fmt );
668     }
669     else
670     {
671         p_cmd->add.p_fmt = (es_format_t*)p_fmt;
672     }
673     return VLC_SUCCESS;
674 }
675 static void CmdExecuteAdd( es_out_t *p_out, ts_cmd_t *p_cmd )
676 {
677     es_out_sys_t *p_sys = p_out->p_sys;
678
679     p_cmd->add.p_es->p_es = es_out_Add( p_sys->p_out, p_cmd->add.p_fmt );
680 }
681 static void CmdCleanAdd( ts_cmd_t *p_cmd )
682 {
683     es_format_Clean( p_cmd->add.p_fmt );
684     free( p_cmd->add.p_fmt );
685 }
686
687 static void CmdInitSend( ts_cmd_t *p_cmd, es_out_id_t *p_es, block_t *p_block )
688 {
689     p_cmd->i_type = C_SEND;
690     p_cmd->send.p_es = p_es;
691     p_cmd->send.p_block = p_block;
692 }
693 static int CmdExecuteSend( es_out_t *p_out, ts_cmd_t *p_cmd )
694 {
695     es_out_sys_t *p_sys = p_out->p_sys;
696     block_t *p_block = p_cmd->send.p_block;
697
698     p_cmd->send.p_block = NULL;
699
700     if( p_block )
701     {
702         if( p_cmd->send.p_es->p_es )
703             return es_out_Send( p_sys->p_out, p_cmd->send.p_es->p_es, p_block );
704         block_Release( p_block );
705     }
706     return VLC_EGENERIC;
707 }
708 static void CmdCleanSend( ts_cmd_t *p_cmd )
709 {
710     if( p_cmd->send.p_block )
711         block_Release( p_cmd->send.p_block );
712 }
713
714 static int CmdInitDel( ts_cmd_t *p_cmd, es_out_id_t *p_es )
715 {
716     p_cmd->i_type = C_DEL;
717     p_cmd->del.p_es = p_es;
718     return VLC_SUCCESS;
719 }
720 static void CmdExecuteDel( es_out_t *p_out, ts_cmd_t *p_cmd )
721 {
722     es_out_sys_t *p_sys = p_out->p_sys;
723
724     if( p_cmd->del.p_es->p_es )
725         es_out_Del( p_sys->p_out, p_cmd->del.p_es->p_es );
726     free( p_cmd->del.p_es );
727 }
728
729 static int CmdInitControl( ts_cmd_t *p_cmd, int i_query, va_list args, bool b_copy )
730 {
731     p_cmd->i_type = C_CONTROL;
732     p_cmd->control.i_query = i_query;
733     p_cmd->control.p_meta  = NULL;
734     p_cmd->control.p_epg = NULL;
735     p_cmd->control.p_fmt = NULL;
736
737     switch( i_query )
738     {
739     /* Pass-through control */
740     case ES_OUT_SET_ACTIVE:  /* arg1= bool                     */
741         p_cmd->control.b_bool = (bool)va_arg( args, int );
742         break;
743
744     case ES_OUT_GET_ACTIVE:  /* arg1= bool*                    */
745         p_cmd->control.pb_bool = (bool*)va_arg( args, bool * );
746         break;
747
748     case ES_OUT_SET_MODE:    /* arg1= int                            */
749     case ES_OUT_SET_GROUP:   /* arg1= int                            */
750     case ES_OUT_DEL_GROUP:   /* arg1=int i_group */
751         p_cmd->control.i_int = (int)va_arg( args, int );
752         break;
753
754     case ES_OUT_GET_MODE:    /* arg2= int*                           */
755     case ES_OUT_GET_GROUP:   /* arg1= int*                           */
756         p_cmd->control.pi_int = (int*)va_arg( args, int * );
757         break;
758
759     case ES_OUT_SET_PCR:                /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
760     case ES_OUT_SET_NEXT_DISPLAY_TIME:  /* arg1=int64_t i_pts(microsecond) */
761         p_cmd->control.i_i64 = (int64_t)va_arg( args, int64_t );
762         break;
763
764     case ES_OUT_SET_GROUP_PCR:          /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
765         p_cmd->control.i_int = (int)va_arg( args, int );
766         p_cmd->control.i_i64 = (int64_t)va_arg( args, int64_t );
767         break;
768
769     case ES_OUT_RESET_PCR:           /* no arg */
770         break;
771
772     case ES_OUT_SET_GROUP_META:  /* arg1=int i_group arg2=vlc_meta_t* */
773     {
774         p_cmd->control.i_int = (int)va_arg( args, int );
775         vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t * );
776
777         if( b_copy )
778         {
779             p_cmd->control.p_meta = vlc_meta_New();
780             if( !p_cmd->control.p_meta )
781                 return VLC_EGENERIC;
782             vlc_meta_Merge( p_cmd->control.p_meta, p_meta );
783         }
784         else
785         {
786             p_cmd->control.p_meta = p_meta;
787         }
788         break;
789     }
790
791     case ES_OUT_SET_GROUP_EPG:   /* arg1=int i_group arg2=vlc_epg_t* */
792     {
793         p_cmd->control.i_int = (int)va_arg( args, int );
794         vlc_epg_t *p_epg = (vlc_epg_t*)va_arg( args, vlc_epg_t * );
795
796         if( b_copy )
797         {
798             p_cmd->control.p_epg = vlc_epg_New( p_epg->psz_name );
799             if( !p_cmd->control.p_epg )
800                 return VLC_EGENERIC;
801             for( int i = 0; i < p_epg->i_event; i++ )
802             {
803                 vlc_epg_event_t *p_evt = p_epg->pp_event[i];
804
805                 vlc_epg_AddEvent( p_cmd->control.p_epg,
806                                   p_evt->i_start, p_evt->i_duration,
807                                   p_evt->psz_name,
808                                   p_evt->psz_short_description, p_evt->psz_description );
809             }
810             vlc_epg_SetCurrent( p_cmd->control.p_epg,
811                                 p_epg->p_current ? p_epg->p_current->i_start : -1 );
812         }
813         else
814         {
815             p_cmd->control.p_epg = p_epg;
816         }
817         break;
818     }
819
820     /* Modified control */
821     case ES_OUT_SET_ES:      /* arg1= es_out_id_t*                   */
822     case ES_OUT_RESTART_ES:  /* arg1= es_out_id_t*                   */
823     case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t*                */
824         p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
825         break;
826
827     case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool   */
828         p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
829         p_cmd->control.b_bool = (bool)va_arg( args, int );
830         break;
831
832     case ES_OUT_GET_ES_STATE:/* arg1= es_out_id_t* arg2=bool*  */
833         p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
834         p_cmd->control.pb_bool = (bool*)va_arg( args, bool * );
835         break;
836
837     case ES_OUT_SET_ES_FMT:     /* arg1= es_out_id_t* arg2=es_format_t* */
838     {
839         p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
840         es_format_t *p_fmt = (es_format_t*)va_arg( args, es_format_t * );
841
842         if( b_copy )
843         {
844             p_cmd->control.p_fmt = malloc( sizeof(*p_fmt) );
845             if( !p_cmd->control.p_fmt )
846                 return VLC_EGENERIC;
847             es_format_Copy( p_cmd->control.p_fmt, p_fmt );
848         }
849         else
850         {
851             p_cmd->control.p_fmt = p_fmt;
852         }
853         break;
854     }
855
856     default:
857         assert(0);
858         return VLC_EGENERIC;
859     }
860
861     return VLC_SUCCESS;
862 }
863 static int CmdExecuteControl( es_out_t *p_out, ts_cmd_t *p_cmd )
864 {
865     es_out_sys_t *p_sys = p_out->p_sys;
866     const int i_query = p_cmd->control.i_query;
867
868     switch( i_query )
869     {
870     /* Pass-through control */
871     case ES_OUT_SET_ACTIVE:  /* arg1= bool                     */
872         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.b_bool );
873
874     case ES_OUT_GET_ACTIVE:  /* arg1= bool*                    */
875         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.pb_bool );
876
877     case ES_OUT_SET_MODE:    /* arg1= int                            */
878     case ES_OUT_SET_GROUP:   /* arg1= int                            */
879     case ES_OUT_DEL_GROUP:   /* arg1=int i_group */
880         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int );
881
882     case ES_OUT_GET_MODE:    /* arg2= int*                           */
883     case ES_OUT_GET_GROUP:   /* arg1= int*                           */
884         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.pi_int );
885
886     case ES_OUT_SET_PCR:                /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
887     case ES_OUT_SET_NEXT_DISPLAY_TIME:  /* arg1=int64_t i_pts(microsecond) */
888         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_i64 );
889
890     case ES_OUT_SET_GROUP_PCR:          /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
891         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.i_i64 );
892
893     case ES_OUT_RESET_PCR:           /* no arg */
894         return es_out_Control( p_sys->p_out, i_query );
895
896     case ES_OUT_SET_GROUP_META:  /* arg1=int i_group arg2=vlc_meta_t* */
897         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.p_meta );
898
899     case ES_OUT_SET_GROUP_EPG:   /* arg1=int i_group arg2=vlc_epg_t* */
900         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.p_epg );
901
902     /* Modified control */
903     case ES_OUT_SET_ES:      /* arg1= es_out_id_t*                   */
904     case ES_OUT_RESTART_ES:  /* arg1= es_out_id_t*                   */
905     case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t*                */
906         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es );
907
908     case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool   */
909         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.b_bool );
910
911     case ES_OUT_GET_ES_STATE:/* arg1= es_out_id_t* arg2=bool*  */
912         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.pb_bool );
913
914     case ES_OUT_SET_ES_FMT:     /* arg1= es_out_id_t* arg2=es_format_t* */
915         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.p_fmt );
916
917     default:
918         assert(0);
919         msg_Err( p_sys->p_input, "Unknown es_out_Control query in CmdExecuteControl!" );
920         return VLC_EGENERIC;
921     }
922 }
923 static void CmdCleanControl( ts_cmd_t *p_cmd )
924 {
925     if( p_cmd->control.p_meta )
926         vlc_meta_Delete( p_cmd->control.p_meta );
927     if( p_cmd->control.p_epg )
928         vlc_epg_Delete( p_cmd->control.p_epg );
929     if( p_cmd->control.p_fmt )
930     {
931         es_format_Clean( p_cmd->control.p_fmt );
932         free( p_cmd->control.p_fmt );
933     }
934 }
935
936
937 /*****************************************************************************
938  * GetTmpFile/Path:
939  *****************************************************************************/
940 static char *GetTmpPath( char *psz_path )
941 {
942     if( psz_path && *psz_path )
943     {
944         /* Make sure that the path exists and is a directory */
945         struct stat s;
946         const int i_ret = utf8_stat( psz_path, &s );
947
948         if( i_ret < 0 && !utf8_mkdir( psz_path, 0600 ) )
949             return psz_path;
950         else if( i_ret == 0 && ( s.st_mode & S_IFDIR ) )
951             return psz_path;
952     }
953     free( psz_path );
954
955     /* Create a suitable path */
956 #if defined (WIN32) && !defined (UNDER_CE)
957     const DWORD dwCount = GetTempPathW( 0, NULL );
958     wchar_t *psw_path = calloc( dwCount + 1, sizeof(wchar_t) );
959     if( psw_path )
960     {
961         if( GetTempPathW( dwCount + 1, psw_path ) <= 0 )
962         {
963             free( psw_path );
964
965             psw_path = _wgetcwd( NULL, 0 );
966         }
967     }
968
969     psz_path = NULL;
970     if( psw_path )
971     {
972         psz_path = FromWide( psw_path );
973         while( psz_path && *psz_path && psz_path[strlen( psz_path ) - 1] == '\\' )
974             psz_path[strlen( psz_path ) - 1] = '\0';
975
976         free( psw_path );
977     }
978
979     if( !psz_path || *psz_path == '\0' )
980     {
981         free( psz_path );
982         return strdup( "C:" );
983     }
984 #else
985     psz_path = strdup( "/tmp" );
986 #endif
987
988     return psz_path;
989 }
990
991 static FILE *GetTmpFile( const char *psz_path )
992 {
993     char *psz_name;
994     int fd;
995     FILE *f;
996
997     /* */
998     if( asprintf( &psz_name, "%s/vlc-timeshift.XXXXXX", psz_path ) < 0 )
999         return NULL;
1000
1001     /* */
1002     fd = mkstemp( psz_name );
1003     free( psz_name );
1004
1005     if( fd < 0 )
1006         return NULL;
1007
1008     /* */
1009     f = fdopen( fd, "rw+" );
1010     if( !f )
1011         close( fd );
1012
1013     return f;
1014 }
1015