1 /*****************************************************************************
2 * es_out_timeshift.c: Es Out timeshift.
3 *****************************************************************************
4 * Copyright (C) 2008 Laurent Aimar
7 * Authors: Laurent Aimar < fenrir _AT_ via _DOT_ ecp _DOT_ fr>
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.
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.
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 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
35 #if defined (WIN32) && !defined (UNDER_CE)
38 #ifdef HAVE_SYS_STAT_H
39 # include <sys/stat.h>
42 #include <vlc_common.h>
43 #include <vlc_charset.h>
45 #include <vlc_input.h>
46 #include <vlc_es_out.h>
47 #include <vlc_block.h>
48 #include "input_internal.h"
50 #include "es_out_timeshift.h"
52 /*****************************************************************************
54 *****************************************************************************/
107 input_thread_t *p_input;
111 int64_t i_tmp_size_max; /* Maximal temporary file size in byte */
112 char *psz_tmp_path; /* Path for temporary files */
114 /* Lock for all following fields */
120 vlc_object_t *p_thread;
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 * );
137 static int TsStart( es_out_t * );
138 static void TsStop( es_out_t * );
139 static void *TsRun( vlc_object_t * );
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 );
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 );
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 * );
154 static void CmdCleanAdd ( ts_cmd_t * );
155 static void CmdCleanSend ( ts_cmd_t * );
156 static void CmdCleanControl( ts_cmd_t *p_cmd );
158 static char *GetTmpPath( char *psz_path );
159 static FILE *GetTmpFile( const char *psz_path );
161 /*****************************************************************************
162 * input_EsOutTimeshiftNew:
163 *****************************************************************************/
164 es_out_t *input_EsOutTimeshiftNew( input_thread_t *p_input, es_out_t *p_next_out )
166 es_out_t *p_out = malloc( sizeof(*p_out) );
170 es_out_sys_t *p_sys = malloc( sizeof(*p_sys) );
179 p_out->pf_send = Send;
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;
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 );
192 p_sys->b_delayed = false;
193 p_sys->p_thread = NULL;
195 TAB_INIT( p_sys->i_es, p_sys->pp_es );
196 TAB_INIT( p_sys->i_cmd, p_sys->pp_cmd );
199 * timeshift-granularity
202 p_sys->i_tmp_size_max = 50 * 1024*1024;
203 p_sys->psz_tmp_path = GetTmpPath( NULL );
208 /*****************************************************************************
210 *****************************************************************************/
211 static void Destroy( es_out_t *p_out )
213 es_out_sys_t *p_sys = p_out->p_sys;
215 if( p_sys->b_delayed )
222 if( CmdPop( p_out, &cmd ) )
231 CmdCleanSend( &cmd );
234 CmdCleanControl( &cmd );
243 TAB_CLEAN( p_sys->i_cmd, p_sys->pp_cmd );
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 );
249 free( p_sys->psz_tmp_path );
250 vlc_cond_destroy( &p_sys->wait );
251 vlc_mutex_destroy( &p_sys->lock );
256 static es_out_id_t *Add( es_out_t *p_out, const es_format_t *p_fmt )
258 es_out_sys_t *p_sys = p_out->p_sys;
261 es_out_id_t *p_es = malloc( sizeof( *p_es ) );
265 vlc_mutex_lock( &p_sys->lock );
267 if( CmdInitAdd( &cmd, p_es, p_fmt, p_sys->b_delayed ) )
269 vlc_mutex_unlock( &p_sys->lock );
274 TAB_APPEND( p_sys->i_es, p_sys->pp_es, p_es );
276 if( p_sys->b_delayed )
277 CmdPush( p_out, &cmd );
279 CmdExecuteAdd( p_out, &cmd );
281 vlc_mutex_unlock( &p_sys->lock );
285 static int Send( es_out_t *p_out, es_out_id_t *p_es, block_t *p_block )
287 es_out_sys_t *p_sys = p_out->p_sys;
289 int i_ret = VLC_SUCCESS;
291 vlc_mutex_lock( &p_sys->lock );
293 CmdInitSend( &cmd, p_es, p_block );
294 if( p_sys->b_delayed )
295 CmdPush( p_out, &cmd );
297 i_ret = CmdExecuteSend( p_out, &cmd) ;
299 vlc_mutex_unlock( &p_sys->lock );
303 static void Del( es_out_t *p_out, es_out_id_t *p_es )
305 es_out_sys_t *p_sys = p_out->p_sys;
308 vlc_mutex_lock( &p_sys->lock );
310 CmdInitDel( &cmd, p_es );
311 if( p_sys->b_delayed )
312 CmdPush( p_out, &cmd );
314 CmdExecuteDel( p_out, &cmd );
316 TAB_REMOVE( p_sys->i_es, p_sys->pp_es, p_es );
318 vlc_mutex_unlock( &p_sys->lock );
321 static int ControlLockedGetEmpty( es_out_t *p_out, bool *pb_empty )
323 es_out_sys_t *p_sys = p_out->p_sys;
325 if( p_sys->b_delayed && p_sys->i_cmd > 0 )
328 *pb_empty = es_out_GetEmpty( p_sys->p_out );
332 static int ControlLockedGetWakeup( es_out_t *p_out, mtime_t *pi_wakeup )
334 es_out_sys_t *p_sys = p_out->p_sys;
336 if( p_sys->b_delayed )
338 assert( !p_sys->p_input->b_can_pace_control );
343 *pi_wakeup = es_out_GetWakeup( p_sys->p_out );
348 static int ControlLockedGetBuffering( es_out_t *p_out, bool *pb_buffering )
350 es_out_sys_t *p_sys = p_out->p_sys;
352 if( p_sys->b_delayed )
353 *pb_buffering = true;
355 *pb_buffering = es_out_GetBuffering( p_sys->p_out );
359 static int ControlLockedSetPauseState( es_out_t *p_out, bool b_source_paused, bool b_paused, mtime_t i_date )
361 es_out_sys_t *p_sys = p_out->p_sys;
363 if( !p_sys->b_delayed &&
364 !b_source_paused == !b_paused )
366 return es_out_SetPauseState( p_sys->p_out, b_source_paused, b_paused, i_date );
369 if( p_sys->p_input->b_can_pace_control )
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
374 msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have space control" );
380 assert( !b_source_paused );
382 if( !p_sys->b_delayed )
385 return es_out_SetPauseState( p_sys->p_out, true, true, i_date );
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 );
393 static int ControlLockedSetRate( es_out_t *p_out, int i_src_rate, int i_rate )
395 es_out_sys_t *p_sys = p_out->p_sys;
397 if( !p_sys->b_delayed &&
398 i_src_rate == i_rate )
400 return es_out_SetRate( p_sys->p_out, i_src_rate, i_rate );
403 msg_Err( p_sys->p_input, "EsOutTimeshift does not yet support rate change" );
406 static int ControlLockedSetTime( es_out_t *p_out, mtime_t i_date )
408 es_out_sys_t *p_sys = p_out->p_sys;
410 if( !p_sys->b_delayed )
411 return es_out_SetTime( p_sys->p_out, i_date );
414 msg_Err( p_sys->p_input, "EsOutTimeshift does not yet support time change" );
417 static int ControlLockedSetFrameNext( es_out_t *p_out )
419 es_out_sys_t *p_sys = p_out->p_sys;
421 if( !p_sys->b_delayed )
422 return es_out_SetFrameNext( p_sys->p_out );
425 msg_Err( p_sys->p_input, "EsOutTimeshift does not yet support frame next" );
429 static int ControlLocked( es_out_t *p_out, int i_query, va_list args )
431 es_out_sys_t *p_sys = p_out->p_sys;
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:
444 /* TODO ? or to remove ? */
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:
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:
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:
470 if( CmdInitControl( &cmd, i_query, args, p_sys->b_delayed ) )
472 if( p_sys->b_delayed )
474 CmdPush( p_out, &cmd );
477 return CmdExecuteControl( p_out, &cmd );
480 /* Special control */
481 case ES_OUT_GET_EMPTY:
483 bool *pb_empty = (bool*)va_arg( args, bool* );
484 return ControlLockedGetEmpty( p_out, pb_empty );
486 case ES_OUT_GET_WAKE_UP: /* TODO ? */
488 mtime_t *pi_wakeup = (mtime_t*)va_arg( args, mtime_t* );
489 return ControlLockedGetWakeup( p_out, pi_wakeup );
491 case ES_OUT_GET_BUFFERING:
493 bool *pb_buffering = (bool *)va_arg( args, bool* );
494 return ControlLockedGetBuffering( p_out, pb_buffering );
496 case ES_OUT_SET_PAUSE_STATE:
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 );
502 return ControlLockedSetPauseState( p_out, b_source_paused, b_paused, i_date );
504 case ES_OUT_SET_RATE:
506 const int i_src_rate = (int)va_arg( args, int );
507 const int i_rate = (int)va_arg( args, int );
509 return ControlLockedSetRate( p_out, i_src_rate, i_rate );
511 case ES_OUT_SET_TIME:
513 const mtime_t i_date = (mtime_t)va_arg( args, mtime_t );
515 return ControlLockedSetTime( p_out, i_date );
517 case ES_OUT_SET_FRAME_NEXT:
519 return ControlLockedSetFrameNext( p_out );
523 msg_Err( p_sys->p_input, "Unknown es_out_Control query !" );
528 static int Control( es_out_t *p_out, int i_query, va_list args )
530 es_out_sys_t *p_sys = p_out->p_sys;
533 vlc_mutex_lock( &p_sys->lock );
534 i_ret = ControlLocked( p_out, i_query, args );
535 vlc_mutex_unlock( &p_sys->lock );
540 /*****************************************************************************
542 *****************************************************************************/
543 static int TsStart( es_out_t *p_out )
545 es_out_sys_t *p_sys = p_out->p_sys;
547 assert( !p_sys->b_delayed );
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 )
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 ) )
559 msg_Err( p_sys->p_input, "cannot create input thread" );
560 vlc_object_release( p_sys->p_thread );
562 p_sys->b_delayed = false;
568 static void TsStop( es_out_t *p_out )
570 es_out_sys_t *p_sys = p_out->p_sys;
572 assert( p_sys->b_delayed );
574 vlc_object_kill( p_sys->p_thread );
575 vlc_thread_join( p_sys->p_thread );
577 p_sys->b_delayed = false;
579 static void *TsRun( vlc_object_t *p_thread )
581 es_out_t *p_out = p_thread->p_private;
582 es_out_sys_t *p_sys = p_out->p_sys;
588 vlc_mutex_lock( &p_sys->lock );
589 mutex_cleanup_push( &p_sys->lock );
591 while( CmdPop( p_out, &cmd ) )
592 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
594 /* TODO we MUST regulate the speed of this thread, to the same speed
595 * than the reading */
597 const int canc = vlc_savecancel();
601 CmdExecuteAdd( p_out, &cmd );
605 CmdExecuteSend( p_out, &cmd );
606 CmdCleanSend( &cmd );
609 CmdExecuteControl( p_out, &cmd );
610 CmdCleanControl( &cmd );
613 CmdExecuteDel( p_out, &cmd );
619 vlc_restorecancel( canc );
622 vlc_mutex_unlock( &p_sys->lock );
628 /*****************************************************************************
630 *****************************************************************************/
631 static void CmdPush( es_out_t *p_out, const ts_cmd_t *p_cmd )
633 es_out_sys_t *p_sys = p_out->p_sys;
634 ts_cmd_t *p_dup = malloc( sizeof(*p_dup) );
639 TAB_APPEND( p_sys->i_cmd, p_sys->pp_cmd, p_dup );
641 vlc_cond_signal( &p_sys->wait );
644 static int CmdPop( es_out_t *p_out, ts_cmd_t *p_cmd )
646 es_out_sys_t *p_sys = p_out->p_sys;
648 if( p_sys->i_cmd <= 0 )
651 *p_cmd = *p_sys->pp_cmd[0];
653 free( p_sys->pp_cmd[0] );
654 TAB_REMOVE( p_sys->i_cmd, p_sys->pp_cmd, p_sys->pp_cmd[0] );
658 static int CmdInitAdd( ts_cmd_t *p_cmd, es_out_id_t *p_es, const es_format_t *p_fmt, bool b_copy )
660 p_cmd->i_type = C_ADD;
661 p_cmd->add.p_es = p_es;
664 p_cmd->add.p_fmt = malloc( sizeof(*p_fmt) );
665 if( !p_cmd->add.p_fmt )
667 es_format_Copy( p_cmd->add.p_fmt, p_fmt );
671 p_cmd->add.p_fmt = (es_format_t*)p_fmt;
675 static void CmdExecuteAdd( es_out_t *p_out, ts_cmd_t *p_cmd )
677 es_out_sys_t *p_sys = p_out->p_sys;
679 p_cmd->add.p_es->p_es = es_out_Add( p_sys->p_out, p_cmd->add.p_fmt );
681 static void CmdCleanAdd( ts_cmd_t *p_cmd )
683 es_format_Clean( p_cmd->add.p_fmt );
684 free( p_cmd->add.p_fmt );
687 static void CmdInitSend( ts_cmd_t *p_cmd, es_out_id_t *p_es, block_t *p_block )
689 p_cmd->i_type = C_SEND;
690 p_cmd->send.p_es = p_es;
691 p_cmd->send.p_block = p_block;
693 static int CmdExecuteSend( es_out_t *p_out, ts_cmd_t *p_cmd )
695 es_out_sys_t *p_sys = p_out->p_sys;
696 block_t *p_block = p_cmd->send.p_block;
698 p_cmd->send.p_block = NULL;
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 );
708 static void CmdCleanSend( ts_cmd_t *p_cmd )
710 if( p_cmd->send.p_block )
711 block_Release( p_cmd->send.p_block );
714 static int CmdInitDel( ts_cmd_t *p_cmd, es_out_id_t *p_es )
716 p_cmd->i_type = C_DEL;
717 p_cmd->del.p_es = p_es;
720 static void CmdExecuteDel( es_out_t *p_out, ts_cmd_t *p_cmd )
722 es_out_sys_t *p_sys = p_out->p_sys;
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 );
729 static int CmdInitControl( ts_cmd_t *p_cmd, int i_query, va_list args, bool b_copy )
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;
739 /* Pass-through control */
740 case ES_OUT_SET_ACTIVE: /* arg1= bool */
741 p_cmd->control.b_bool = (bool)va_arg( args, int );
744 case ES_OUT_GET_ACTIVE: /* arg1= bool* */
745 p_cmd->control.pb_bool = (bool*)va_arg( args, bool * );
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 );
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 * );
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 );
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 );
769 case ES_OUT_RESET_PCR: /* no arg */
772 case ES_OUT_SET_GROUP_META: /* arg1=int i_group arg2=vlc_meta_t* */
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 * );
779 p_cmd->control.p_meta = vlc_meta_New();
780 if( !p_cmd->control.p_meta )
782 vlc_meta_Merge( p_cmd->control.p_meta, p_meta );
786 p_cmd->control.p_meta = p_meta;
791 case ES_OUT_SET_GROUP_EPG: /* arg1=int i_group arg2=vlc_epg_t* */
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 * );
798 p_cmd->control.p_epg = vlc_epg_New( p_epg->psz_name );
799 if( !p_cmd->control.p_epg )
801 for( int i = 0; i < p_epg->i_event; i++ )
803 vlc_epg_event_t *p_evt = p_epg->pp_event[i];
805 vlc_epg_AddEvent( p_cmd->control.p_epg,
806 p_evt->i_start, p_evt->i_duration,
808 p_evt->psz_short_description, p_evt->psz_description );
810 vlc_epg_SetCurrent( p_cmd->control.p_epg,
811 p_epg->p_current ? p_epg->p_current->i_start : -1 );
815 p_cmd->control.p_epg = p_epg;
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 * );
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 );
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 * );
837 case ES_OUT_SET_ES_FMT: /* arg1= es_out_id_t* arg2=es_format_t* */
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 * );
844 p_cmd->control.p_fmt = malloc( sizeof(*p_fmt) );
845 if( !p_cmd->control.p_fmt )
847 es_format_Copy( p_cmd->control.p_fmt, p_fmt );
851 p_cmd->control.p_fmt = p_fmt;
863 static int CmdExecuteControl( es_out_t *p_out, ts_cmd_t *p_cmd )
865 es_out_sys_t *p_sys = p_out->p_sys;
866 const int i_query = p_cmd->control.i_query;
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 );
874 case ES_OUT_GET_ACTIVE: /* arg1= bool* */
875 return es_out_Control( p_sys->p_out, i_query, p_cmd->control.pb_bool );
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 );
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 );
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 );
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 );
893 case ES_OUT_RESET_PCR: /* no arg */
894 return es_out_Control( p_sys->p_out, i_query );
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 );
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 );
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 );
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 );
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 );
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 );
919 msg_Err( p_sys->p_input, "Unknown es_out_Control query in CmdExecuteControl!" );
923 static void CmdCleanControl( ts_cmd_t *p_cmd )
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 )
931 es_format_Clean( p_cmd->control.p_fmt );
932 free( p_cmd->control.p_fmt );
937 /*****************************************************************************
939 *****************************************************************************/
940 static char *GetTmpPath( char *psz_path )
942 if( psz_path && *psz_path )
944 /* Make sure that the path exists and is a directory */
946 const int i_ret = utf8_stat( psz_path, &s );
948 if( i_ret < 0 && !utf8_mkdir( psz_path, 0600 ) )
950 else if( i_ret == 0 && ( s.st_mode & S_IFDIR ) )
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) );
961 if( GetTempPathW( dwCount + 1, psw_path ) <= 0 )
965 psw_path = _wgetcwd( NULL, 0 );
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';
979 if( !psz_path || *psz_path == '\0' )
982 return strdup( "C:" );
985 psz_path = strdup( "/tmp" );
991 static FILE *GetTmpFile( const char *psz_path )
998 if( asprintf( &psz_name, "%s/vlc-timeshift.XXXXXX", psz_path ) < 0 )
1002 fd = mkstemp( psz_name );
1009 f = fdopen( fd, "rw+" );