]> git.sesse.net Git - vlc/blob - src/input/es_out_timeshift.c
Added initial skeleton for es_out timeshift support.
[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
117     /* */
118     bool           b_delayed;
119
120     /* */
121     int            i_es;
122     es_out_id_t    **pp_es;
123
124     /* */
125     int            i_cmd;
126     ts_cmd_t       **pp_cmd;
127 };
128
129 static es_out_id_t *Add    ( es_out_t *, const es_format_t * );
130 static int          Send   ( es_out_t *, es_out_id_t *, block_t * );
131 static void         Del    ( es_out_t *, es_out_id_t * );
132 static int          Control( es_out_t *, int i_query, va_list );
133 static void         Destroy( es_out_t * );
134
135 static void CmdPush( es_out_t *, const ts_cmd_t * );
136 static int  CmdPop( es_out_t *p_out, ts_cmd_t *p_cmd );
137
138 static int  CmdInitAdd    ( ts_cmd_t *, es_out_id_t *, const es_format_t *, bool b_copy );
139 static void CmdInitSend   ( ts_cmd_t *, es_out_id_t *, block_t * );
140 static int  CmdInitDel    ( ts_cmd_t *, es_out_id_t * );
141 static int  CmdInitControl( ts_cmd_t *, int i_query, va_list, bool b_copy );
142
143 static void CmdExecuteAdd    ( es_out_t *, ts_cmd_t * );
144 static int  CmdExecuteSend   ( es_out_t *, ts_cmd_t * );
145 static void CmdExecuteDel    ( es_out_t *, ts_cmd_t * );
146 static int  CmdExecuteControl( es_out_t *, ts_cmd_t * );
147
148 static void CmdCleanAdd    ( ts_cmd_t * );
149 static void CmdCleanSend   ( ts_cmd_t * );
150 static void CmdCleanControl( ts_cmd_t *p_cmd );
151
152 static char *GetTmpPath( char *psz_path );
153 static FILE *GetTmpFile( const char *psz_path );
154
155 /*****************************************************************************
156  * input_EsOutTimeshiftNew:
157  *****************************************************************************/
158 es_out_t *input_EsOutTimeshiftNew( input_thread_t *p_input, es_out_t *p_next_out )
159 {
160     es_out_t *p_out = malloc( sizeof(*p_out) );
161     if( !p_out )
162         return NULL;
163
164     es_out_sys_t *p_sys = malloc( sizeof(*p_sys) );
165     if( !p_sys )
166     {
167         free( p_out );
168         return NULL;
169     }
170
171     /* */
172     p_out->pf_add     = Add;
173     p_out->pf_send    = Send;
174     p_out->pf_del     = Del;
175     p_out->pf_control = Control;
176     p_out->pf_destroy = Destroy;
177     p_out->p_sys      = p_sys;
178     p_out->b_sout     = p_input->p->p_sout != NULL;
179
180     /* */
181     p_sys->p_input = p_input;
182     p_sys->p_out = p_next_out;
183     vlc_mutex_init_recursive( &p_sys->lock );
184     p_sys->b_delayed = false;
185
186     TAB_INIT( p_sys->i_es, p_sys->pp_es );
187     TAB_INIT( p_sys->i_cmd, p_sys->pp_cmd );
188
189     /* TODO config
190      * timeshift-granularity
191      * timeshift-path
192      */
193     p_sys->i_tmp_size_max = 50 * 1024*1024;
194     p_sys->psz_tmp_path = GetTmpPath( NULL );
195
196     return p_out;
197 }
198
199 /*****************************************************************************
200  * Internal functions
201  *****************************************************************************/
202 static void Destroy( es_out_t *p_out )
203 {
204     es_out_sys_t *p_sys = p_out->p_sys;
205
206     while( p_sys->i_cmd > 0 )
207     {
208         ts_cmd_t cmd;
209
210         if( CmdPop( p_out, &cmd ) )
211             break;
212
213         switch( cmd.i_type )
214         {
215         case C_ADD:
216             CmdCleanAdd( &cmd );
217             break;
218         case C_SEND:
219             CmdCleanSend( &cmd );
220             break;
221         case C_CONTROL:
222             CmdCleanControl( &cmd );
223             break;
224         case C_DEL:
225             break;
226         default:
227             assert(0);
228             break;
229         }
230     }
231     TAB_CLEAN( p_sys->i_cmd, p_sys->pp_cmd  );
232
233     while( p_sys->i_es > 0 )
234         Del( p_out, p_sys->pp_es[0] );
235     TAB_CLEAN( p_sys->i_es, p_sys->pp_es  );
236
237     free( p_sys->psz_tmp_path );
238     vlc_mutex_destroy( &p_sys->lock );
239     free( p_sys );
240     free( p_out );
241 }
242
243 static es_out_id_t *Add( es_out_t *p_out, const es_format_t *p_fmt )
244 {
245     es_out_sys_t *p_sys = p_out->p_sys;
246     ts_cmd_t cmd;
247
248     es_out_id_t *p_es = malloc( sizeof( *p_es ) );
249     if( !p_es )
250         return NULL;
251
252     vlc_mutex_lock( &p_sys->lock );
253
254     if( CmdInitAdd( &cmd, p_es, p_fmt, p_sys->b_delayed ) )
255     {
256         vlc_mutex_unlock( &p_sys->lock );
257         free( p_es );
258         return NULL;
259     }
260
261     TAB_APPEND( p_sys->i_es, p_sys->pp_es, p_es );
262
263     if( p_sys->b_delayed )
264         CmdPush( p_out, &cmd );
265     else
266         CmdExecuteAdd( p_out, &cmd );
267
268     vlc_mutex_unlock( &p_sys->lock );
269
270     return p_es;
271 }
272 static int Send( es_out_t *p_out, es_out_id_t *p_es, block_t *p_block )
273 {
274     es_out_sys_t *p_sys = p_out->p_sys;
275     ts_cmd_t cmd;
276     int i_ret = VLC_SUCCESS;
277
278     vlc_mutex_lock( &p_sys->lock );
279
280     CmdInitSend( &cmd, p_es, p_block );
281     if( p_sys->b_delayed )
282         CmdPush( p_out, &cmd );
283     else
284         i_ret = CmdExecuteSend( p_out, &cmd) ;
285
286     vlc_mutex_unlock( &p_sys->lock );
287
288     return i_ret;
289 }
290 static void Del( es_out_t *p_out, es_out_id_t *p_es )
291 {
292     es_out_sys_t *p_sys = p_out->p_sys;
293     ts_cmd_t cmd;
294
295     vlc_mutex_lock( &p_sys->lock );
296
297     CmdInitDel( &cmd, p_es );
298     if( p_sys->b_delayed )
299         CmdPush( p_out, &cmd );
300     else
301         CmdExecuteDel( p_out, &cmd );
302
303     TAB_REMOVE( p_sys->i_es, p_sys->pp_es, p_es );
304
305     vlc_mutex_unlock( &p_sys->lock );
306 }
307 static int ControlLocked( es_out_t *p_out, int i_query, va_list args )
308 {
309     es_out_sys_t *p_sys = p_out->p_sys;
310
311     switch( i_query )
312     {
313     /* Invalid query for this es_out level */
314     case ES_OUT_SET_ES_BY_ID:
315     case ES_OUT_RESTART_ES_BY_ID:
316     case ES_OUT_SET_ES_DEFAULT_BY_ID:
317     case ES_OUT_SET_DELAY:
318     case ES_OUT_SET_RECORD_STATE:
319         assert(0);
320         return VLC_EGENERIC;
321
322     /* TODO ? or to remove ? */
323     case ES_OUT_GET_TS:
324         return VLC_EGENERIC;
325
326     /* Pass-through control */
327     case ES_OUT_SET_ACTIVE:
328     case ES_OUT_GET_ACTIVE:
329     case ES_OUT_SET_MODE:
330     case ES_OUT_GET_MODE:
331     case ES_OUT_SET_GROUP:
332     case ES_OUT_GET_GROUP:
333     case ES_OUT_SET_PCR:
334     case ES_OUT_SET_GROUP_PCR:
335     case ES_OUT_RESET_PCR:
336     case ES_OUT_SET_NEXT_DISPLAY_TIME:
337     case ES_OUT_SET_GROUP_META:
338     case ES_OUT_SET_GROUP_EPG:
339     case ES_OUT_DEL_GROUP:
340     case ES_OUT_SET_ES:
341     case ES_OUT_RESTART_ES:
342     case ES_OUT_SET_ES_DEFAULT:
343     case ES_OUT_SET_ES_STATE:
344     case ES_OUT_GET_ES_STATE:
345     case ES_OUT_SET_ES_FMT:
346     {
347         ts_cmd_t cmd;
348         if( CmdInitControl( &cmd, i_query, args, p_sys->b_delayed ) )
349             return VLC_EGENERIC;
350         if( p_sys->b_delayed )
351         {
352             CmdPush( p_out, &cmd );
353             return VLC_SUCCESS;
354         }
355         return CmdExecuteControl( p_out, &cmd );
356     }
357
358     /* Special control */
359     case ES_OUT_GET_WAKE_UP: /* TODO ? */
360     case ES_OUT_GET_BUFFERING:
361     case ES_OUT_GET_EMPTY:
362     case ES_OUT_SET_PAUSE_STATE:
363     case ES_OUT_SET_RATE:
364     case ES_OUT_SET_TIME:
365     case ES_OUT_SET_FRAME_NEXT:
366         if( p_sys->b_delayed )
367         {
368             /* TODO */
369         }
370         return es_out_vaControl( p_sys->p_out, i_query, args );
371
372     default:
373         msg_Err( p_sys->p_input, "Unknown es_out_Control query !" );
374         assert(0);
375         return VLC_EGENERIC;
376     }
377 }
378 static int Control( es_out_t *p_out, int i_query, va_list args )
379 {
380     es_out_sys_t *p_sys = p_out->p_sys;
381     int i_ret;
382
383     vlc_mutex_lock( &p_sys->lock );
384     i_ret = ControlLocked( p_out, i_query, args );
385     vlc_mutex_unlock( &p_sys->lock );
386
387     return i_ret;
388 }
389
390 /*****************************************************************************
391  *
392  *****************************************************************************/
393 static void CmdPush( es_out_t *p_out, const ts_cmd_t *p_cmd )
394 {
395     es_out_sys_t *p_sys = p_out->p_sys;
396     ts_cmd_t *p_dup = malloc( sizeof(*p_dup) );
397
398     if( p_dup )
399     {
400         *p_dup = *p_cmd;
401         TAB_APPEND( p_sys->i_cmd, p_sys->pp_cmd, p_dup );
402     }
403 }
404 static int CmdPop( es_out_t *p_out, ts_cmd_t *p_cmd )
405 {
406     es_out_sys_t *p_sys = p_out->p_sys;
407
408     if( p_sys->i_cmd <= 0 )
409         return VLC_EGENERIC;
410
411     *p_cmd = *p_sys->pp_cmd[0];
412
413     free( p_sys->pp_cmd[0] );
414     TAB_REMOVE( p_sys->i_cmd, p_sys->pp_cmd, p_sys->pp_cmd[0] );
415     return VLC_SUCCESS;
416 }
417
418 static int CmdInitAdd( ts_cmd_t *p_cmd, es_out_id_t *p_es, const es_format_t *p_fmt, bool b_copy )
419 {
420     p_cmd->i_type = C_ADD;
421     p_cmd->add.p_es = p_es;
422     if( b_copy )
423     {
424         p_cmd->add.p_fmt = malloc( sizeof(*p_fmt) );
425         if( !p_cmd->add.p_fmt )
426             return VLC_EGENERIC;
427         es_format_Copy( p_cmd->add.p_fmt, p_fmt );
428     }
429     else
430     {
431         p_cmd->add.p_fmt = (es_format_t*)p_fmt;
432     }
433     return VLC_SUCCESS;
434 }
435 static void CmdExecuteAdd( es_out_t *p_out, ts_cmd_t *p_cmd )
436 {
437     es_out_sys_t *p_sys = p_out->p_sys;
438
439     p_cmd->add.p_es->p_es = es_out_Add( p_sys->p_out, p_cmd->add.p_fmt );
440 }
441 static void CmdCleanAdd( ts_cmd_t *p_cmd )
442 {
443     es_format_Clean( p_cmd->add.p_fmt );
444     free( p_cmd->add.p_fmt );
445 }
446
447 static void CmdInitSend( ts_cmd_t *p_cmd, es_out_id_t *p_es, block_t *p_block )
448 {
449     p_cmd->i_type = C_SEND;
450     p_cmd->send.p_es = p_es;
451     p_cmd->send.p_block = p_block;
452 }
453 static int CmdExecuteSend( es_out_t *p_out, ts_cmd_t *p_cmd )
454 {
455     es_out_sys_t *p_sys = p_out->p_sys;
456     block_t *p_block = p_cmd->send.p_block;
457
458     p_cmd->send.p_block = NULL;
459
460     if( p_block )
461     {
462         if( p_cmd->send.p_es->p_es )
463             return es_out_Send( p_sys->p_out, p_cmd->send.p_es->p_es, p_block );
464         block_Release( p_block );
465     }
466     return VLC_EGENERIC;
467 }
468 static void CmdCleanSend( ts_cmd_t *p_cmd )
469 {
470     if( p_cmd->send.p_block )
471         block_Release( p_cmd->send.p_block );
472 }
473
474 static int CmdInitDel( ts_cmd_t *p_cmd, es_out_id_t *p_es )
475 {
476     p_cmd->i_type = C_DEL;
477     p_cmd->del.p_es = p_es;
478     return VLC_SUCCESS;
479 }
480 static void CmdExecuteDel( es_out_t *p_out, ts_cmd_t *p_cmd )
481 {
482     es_out_sys_t *p_sys = p_out->p_sys;
483
484     if( p_cmd->del.p_es->p_es )
485         es_out_Del( p_sys->p_out, p_cmd->del.p_es->p_es );
486     free( p_cmd->del.p_es );
487 }
488
489 static int CmdInitControl( ts_cmd_t *p_cmd, int i_query, va_list args, bool b_copy )
490 {
491     p_cmd->i_type = C_CONTROL;
492     p_cmd->control.i_query = i_query;
493
494     switch( i_query )
495     {
496     /* Pass-through control */
497     case ES_OUT_SET_ACTIVE:  /* arg1= bool                     */
498         p_cmd->control.b_bool = (bool)va_arg( args, int );
499         break;
500
501     case ES_OUT_GET_ACTIVE:  /* arg1= bool*                    */
502         p_cmd->control.pb_bool = (bool*)va_arg( args, bool * );
503         break;
504
505     case ES_OUT_SET_MODE:    /* arg1= int                            */
506     case ES_OUT_SET_GROUP:   /* arg1= int                            */
507     case ES_OUT_DEL_GROUP:   /* arg1=int i_group */
508         p_cmd->control.i_int = (int)va_arg( args, int );
509         break;
510
511     case ES_OUT_GET_MODE:    /* arg2= int*                           */
512     case ES_OUT_GET_GROUP:   /* arg1= int*                           */
513         p_cmd->control.pi_int = (int*)va_arg( args, int * );
514         break;
515
516     case ES_OUT_SET_PCR:                /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
517     case ES_OUT_SET_NEXT_DISPLAY_TIME:  /* arg1=int64_t i_pts(microsecond) */
518         p_cmd->control.i_i64 = (int64_t)va_arg( args, int64_t );
519         break;
520
521     case ES_OUT_SET_GROUP_PCR:          /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
522         p_cmd->control.i_int = (int)va_arg( args, int );
523         p_cmd->control.i_i64 = (int64_t)va_arg( args, int64_t );
524         break;
525
526     case ES_OUT_RESET_PCR:           /* no arg */
527         break;
528
529     case ES_OUT_SET_GROUP_META:  /* arg1=int i_group arg2=vlc_meta_t* */
530     {
531         p_cmd->control.i_int = (int)va_arg( args, int );
532         vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t * );
533
534         if( b_copy )
535         {
536             p_cmd->control.p_meta = vlc_meta_New();
537             if( !p_cmd->control.p_meta )
538                 return VLC_EGENERIC;
539             vlc_meta_Merge( p_cmd->control.p_meta, p_meta );
540         }
541         else
542         {
543             p_cmd->control.p_meta = p_meta;
544         }
545         break;
546     }
547
548     case ES_OUT_SET_GROUP_EPG:   /* arg1=int i_group arg2=vlc_epg_t* */
549     {
550         p_cmd->control.i_int = (int)va_arg( args, int );
551         vlc_epg_t *p_epg = (vlc_epg_t*)va_arg( args, vlc_epg_t * );
552
553         if( b_copy )
554         {
555             p_cmd->control.p_epg = vlc_epg_New( p_epg->psz_name );
556             if( !p_cmd->control.p_epg )
557                 return VLC_EGENERIC;
558             for( int i = 0; i < p_epg->i_event; i++ )
559             {
560                 vlc_epg_event_t *p_evt = p_epg->pp_event[i];
561
562                 vlc_epg_AddEvent( p_cmd->control.p_epg,
563                                   p_evt->i_start, p_evt->i_duration,
564                                   p_evt->psz_name,
565                                   p_evt->psz_short_description, p_evt->psz_description );
566             }
567             vlc_epg_SetCurrent( p_cmd->control.p_epg,
568                                 p_epg->p_current ? p_epg->p_current->i_start : -1 );
569         }
570         else
571         {
572             p_cmd->control.p_epg = p_epg;
573         }
574         break;
575     }
576
577     /* Modified control */
578     case ES_OUT_SET_ES:      /* arg1= es_out_id_t*                   */
579     case ES_OUT_RESTART_ES:  /* arg1= es_out_id_t*                   */
580     case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t*                */
581         p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
582         break;
583
584     case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool   */
585         p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
586         p_cmd->control.b_bool = (bool)va_arg( args, int );
587         break;
588
589     case ES_OUT_GET_ES_STATE:/* arg1= es_out_id_t* arg2=bool*  */
590         p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
591         p_cmd->control.pb_bool = (bool*)va_arg( args, bool * );
592         break;
593
594     case ES_OUT_SET_ES_FMT:     /* arg1= es_out_id_t* arg2=es_format_t* */
595     {
596         p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
597         es_format_t *p_fmt = (es_format_t*)va_arg( args, es_format_t * );
598
599         if( b_copy )
600         {
601             p_cmd->control.p_fmt = malloc( sizeof(*p_fmt) );
602             if( !p_cmd->control.p_fmt )
603                 return VLC_EGENERIC;
604             es_format_Copy( p_cmd->control.p_fmt, p_fmt );
605         }
606         else
607         {
608             p_cmd->control.p_fmt = p_fmt;
609         }
610         break;
611     }
612
613     default:
614         assert(0);
615         return VLC_EGENERIC;
616     }
617
618     return VLC_SUCCESS;
619 }
620 static int CmdExecuteControl( es_out_t *p_out, ts_cmd_t *p_cmd )
621 {
622     es_out_sys_t *p_sys = p_out->p_sys;
623     const int i_query = p_cmd->control.i_query;
624
625     switch( i_query )
626     {
627     /* Pass-through control */
628     case ES_OUT_SET_ACTIVE:  /* arg1= bool                     */
629         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.b_bool );
630
631     case ES_OUT_GET_ACTIVE:  /* arg1= bool*                    */
632         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.pb_bool );
633
634     case ES_OUT_SET_MODE:    /* arg1= int                            */
635     case ES_OUT_SET_GROUP:   /* arg1= int                            */
636     case ES_OUT_DEL_GROUP:   /* arg1=int i_group */
637         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int );
638
639     case ES_OUT_GET_MODE:    /* arg2= int*                           */
640     case ES_OUT_GET_GROUP:   /* arg1= int*                           */
641         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.pi_int );
642
643     case ES_OUT_SET_PCR:                /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
644     case ES_OUT_SET_NEXT_DISPLAY_TIME:  /* arg1=int64_t i_pts(microsecond) */
645         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_i64 );
646
647     case ES_OUT_SET_GROUP_PCR:          /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
648         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.i_i64 );
649
650     case ES_OUT_RESET_PCR:           /* no arg */
651         return es_out_Control( p_sys->p_out, i_query );
652
653     case ES_OUT_SET_GROUP_META:  /* arg1=int i_group arg2=vlc_meta_t* */
654         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.p_meta );
655
656     case ES_OUT_SET_GROUP_EPG:   /* arg1=int i_group arg2=vlc_epg_t* */
657         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.p_epg );
658
659     /* Modified control */
660     case ES_OUT_SET_ES:      /* arg1= es_out_id_t*                   */
661     case ES_OUT_RESTART_ES:  /* arg1= es_out_id_t*                   */
662     case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t*                */
663         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es );
664
665     case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool   */
666         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.b_bool );
667
668     case ES_OUT_GET_ES_STATE:/* arg1= es_out_id_t* arg2=bool*  */
669         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.pb_bool );
670
671     case ES_OUT_SET_ES_FMT:     /* arg1= es_out_id_t* arg2=es_format_t* */
672         return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.p_fmt );
673
674     default:
675         assert(0);
676         msg_Err( p_sys->p_input, "Unknown es_out_Control query in CmdExecuteControl!" );
677         return VLC_EGENERIC;
678     }
679 }
680 static void CmdCleanControl( ts_cmd_t *p_cmd )
681 {
682     if( p_cmd->control.p_meta )
683         vlc_meta_Delete( p_cmd->control.p_meta );
684     if( p_cmd->control.p_epg )
685         vlc_epg_Delete( p_cmd->control.p_epg );
686     if( p_cmd->control.p_fmt )
687     {
688         es_format_Clean( p_cmd->control.p_fmt );
689         free( p_cmd->control.p_fmt );
690     }
691 }
692
693
694 /*****************************************************************************
695  * GetTmpFile/Path:
696  *****************************************************************************/
697 static char *GetTmpPath( char *psz_path )
698 {
699     if( psz_path && *psz_path )
700     {
701         /* Make sure that the path exists and is a directory */
702         struct stat s;
703         const int i_ret = utf8_stat( psz_path, &s );
704
705         if( i_ret < 0 && !utf8_mkdir( psz_path, 0600 ) )
706             return psz_path;
707         else if( i_ret == 0 && ( s.st_mode & S_IFDIR ) )
708             return psz_path;
709     }
710     free( psz_path );
711
712     /* Create a suitable path */
713 #if defined (WIN32) && !defined (UNDER_CE)
714     const DWORD dwCount = GetTempPathW( 0, NULL );
715     wchar_t *psw_path = calloc( dwCount + 1, sizeof(wchar_t) );
716     if( psw_path )
717     {
718         if( GetTempPathW( dwCount + 1, psw_path ) <= 0 )
719         {
720             free( psw_path );
721
722             psw_path = _wgetcwd( NULL, 0 );
723         }
724     }
725
726     psz_path = NULL;
727     if( psw_path )
728     {
729         psz_path = FromWide( psw_path );
730         while( psz_path && *psz_path && psz_path[strlen( psz_path ) - 1] == '\\' )
731             psz_path[strlen( psz_path ) - 1] = '\0';
732
733         free( psw_path );
734     }
735
736     if( !psz_path || *psz_path == '\0' )
737     {
738         free( psz_path );
739         return strdup( "C:" );
740     }
741 #else
742     psz_path = strdup( "/tmp" );
743 #endif
744
745     return psz_path;
746 }
747
748 static FILE *GetTmpFile( const char *psz_path )
749 {
750     char *psz_name;
751     int fd;
752     FILE *f;
753
754     /* */
755     if( asprintf( &psz_name, "%s/vlc-timeshift.XXXXXX", psz_path ) < 0 )
756         return NULL;
757
758     /* */
759     fd = mkstemp( psz_name );
760     free( psz_name );
761
762     if( fd < 0 )
763         return NULL;
764
765     /* */
766     f = fdopen( fd, "rw+" );
767     if( !f )
768         close( fd );
769
770     return f;
771 }
772