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 *****************************************************************************/
105 ts_cmd_control_t control;
116 input_thread_t *p_input;
120 int64_t i_tmp_size_max; /* Maximal temporary file size in byte */
121 char *psz_tmp_path; /* Path for temporary files */
123 /* Lock for all following fields */
129 vlc_object_t *p_thread;
132 mtime_t i_pause_date;
137 mtime_t i_rate_delay;
149 static es_out_id_t *Add ( es_out_t *, const es_format_t * );
150 static int Send ( es_out_t *, es_out_id_t *, block_t * );
151 static void Del ( es_out_t *, es_out_id_t * );
152 static int Control( es_out_t *, int i_query, va_list );
153 static void Destroy( es_out_t * );
155 static int TsStart( es_out_t * );
156 static void TsStop( es_out_t * );
157 static void *TsRun( vlc_object_t * );
159 static void CmdPush( es_out_t *, const ts_cmd_t * );
160 static int CmdPop( es_out_t *, ts_cmd_t * );
161 static void CmdClean( ts_cmd_t * );
162 static void cmd_cleanup_routine( void *p ) { CmdClean( p ); }
164 static int CmdInitAdd ( ts_cmd_t *, es_out_id_t *, const es_format_t *, bool b_copy );
165 static void CmdInitSend ( ts_cmd_t *, es_out_id_t *, block_t * );
166 static int CmdInitDel ( ts_cmd_t *, es_out_id_t * );
167 static int CmdInitControl( ts_cmd_t *, int i_query, va_list, bool b_copy );
169 static void CmdExecuteAdd ( es_out_t *, ts_cmd_t * );
170 static int CmdExecuteSend ( es_out_t *, ts_cmd_t * );
171 static void CmdExecuteDel ( es_out_t *, ts_cmd_t * );
172 static int CmdExecuteControl( es_out_t *, ts_cmd_t * );
174 static void CmdCleanAdd ( ts_cmd_t * );
175 static void CmdCleanSend ( ts_cmd_t * );
176 static void CmdCleanControl( ts_cmd_t *p_cmd );
178 static char *GetTmpPath( char *psz_path );
179 static FILE *GetTmpFile( const char *psz_path );
181 /*****************************************************************************
182 * input_EsOutTimeshiftNew:
183 *****************************************************************************/
184 es_out_t *input_EsOutTimeshiftNew( input_thread_t *p_input, es_out_t *p_next_out, int i_rate )
186 es_out_t *p_out = malloc( sizeof(*p_out) );
190 es_out_sys_t *p_sys = malloc( sizeof(*p_sys) );
199 p_out->pf_send = Send;
201 p_out->pf_control = Control;
202 p_out->pf_destroy = Destroy;
203 p_out->p_sys = p_sys;
204 p_out->b_sout = p_input->p->p_sout != NULL;
207 p_sys->p_input = p_input;
208 p_sys->p_out = p_next_out;
209 vlc_mutex_init_recursive( &p_sys->lock );
210 vlc_cond_init( &p_sys->wait );
212 p_sys->b_delayed = false;
213 p_sys->p_thread = NULL;
214 p_sys->b_paused = false;
215 p_sys->i_pause_date = -1;
216 p_sys->i_rate_source =
217 p_sys->i_rate = i_rate;
218 p_sys->i_rate_date = -1;
219 p_sys->i_rate_delay = 0;
220 p_sys->i_cmd_delay = 0;
222 TAB_INIT( p_sys->i_es, p_sys->pp_es );
223 TAB_INIT( p_sys->i_cmd, p_sys->pp_cmd );
226 * timeshift-granularity
229 p_sys->i_tmp_size_max = 50 * 1024*1024;
230 p_sys->psz_tmp_path = GetTmpPath( NULL );
235 /*****************************************************************************
237 *****************************************************************************/
238 static void Destroy( es_out_t *p_out )
240 es_out_sys_t *p_sys = p_out->p_sys;
242 if( p_sys->b_delayed )
245 TAB_CLEAN( p_sys->i_cmd, p_sys->pp_cmd );
247 while( p_sys->i_es > 0 )
248 Del( p_out, p_sys->pp_es[0] );
249 TAB_CLEAN( p_sys->i_es, p_sys->pp_es );
251 free( p_sys->psz_tmp_path );
252 vlc_cond_destroy( &p_sys->wait );
253 vlc_mutex_destroy( &p_sys->lock );
258 static es_out_id_t *Add( es_out_t *p_out, const es_format_t *p_fmt )
260 es_out_sys_t *p_sys = p_out->p_sys;
263 es_out_id_t *p_es = malloc( sizeof( *p_es ) );
267 vlc_mutex_lock( &p_sys->lock );
269 if( CmdInitAdd( &cmd, p_es, p_fmt, p_sys->b_delayed ) )
271 vlc_mutex_unlock( &p_sys->lock );
276 TAB_APPEND( p_sys->i_es, p_sys->pp_es, p_es );
278 if( p_sys->b_delayed )
279 CmdPush( p_out, &cmd );
281 CmdExecuteAdd( p_out, &cmd );
283 vlc_mutex_unlock( &p_sys->lock );
287 static int Send( es_out_t *p_out, es_out_id_t *p_es, block_t *p_block )
289 es_out_sys_t *p_sys = p_out->p_sys;
291 int i_ret = VLC_SUCCESS;
293 vlc_mutex_lock( &p_sys->lock );
295 CmdInitSend( &cmd, p_es, p_block );
296 if( p_sys->b_delayed )
297 CmdPush( p_out, &cmd );
299 i_ret = CmdExecuteSend( p_out, &cmd) ;
301 vlc_mutex_unlock( &p_sys->lock );
305 static void Del( es_out_t *p_out, es_out_id_t *p_es )
307 es_out_sys_t *p_sys = p_out->p_sys;
310 vlc_mutex_lock( &p_sys->lock );
312 CmdInitDel( &cmd, p_es );
313 if( p_sys->b_delayed )
314 CmdPush( p_out, &cmd );
316 CmdExecuteDel( p_out, &cmd );
318 TAB_REMOVE( p_sys->i_es, p_sys->pp_es, p_es );
320 vlc_mutex_unlock( &p_sys->lock );
323 static int ControlLockedGetEmpty( es_out_t *p_out, bool *pb_empty )
325 es_out_sys_t *p_sys = p_out->p_sys;
327 if( p_sys->b_delayed && p_sys->i_cmd > 0 )
330 *pb_empty = es_out_GetEmpty( p_sys->p_out );
334 static int ControlLockedGetWakeup( es_out_t *p_out, mtime_t *pi_wakeup )
336 es_out_sys_t *p_sys = p_out->p_sys;
338 if( p_sys->b_delayed )
340 assert( !p_sys->p_input->b_can_pace_control );
345 *pi_wakeup = es_out_GetWakeup( p_sys->p_out );
350 static int ControlLockedGetBuffering( es_out_t *p_out, bool *pb_buffering )
352 es_out_sys_t *p_sys = p_out->p_sys;
354 if( p_sys->b_delayed )
355 *pb_buffering = true;
357 *pb_buffering = es_out_GetBuffering( p_sys->p_out );
361 static int ControlLockedSetPauseState( es_out_t *p_out, bool b_source_paused, bool b_paused, mtime_t i_date )
363 es_out_sys_t *p_sys = p_out->p_sys;
365 if( !p_sys->b_delayed &&
366 !b_source_paused == !b_paused )
368 return es_out_SetPauseState( p_sys->p_out, b_source_paused, b_paused, i_date );
370 if( p_sys->p_input->b_can_pace_control )
372 /* XXX we may do it BUT it would be better to finish the clock clean up+improvments
373 * and so be able to advertize correctly pace control property in access
375 msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have space control" );
382 assert( !b_source_paused );
384 if( !p_sys->b_delayed )
387 i_ret = es_out_SetPauseState( p_sys->p_out, true, true, i_date );
391 i_ret = es_out_SetPauseState( p_sys->p_out, false, false, i_date );
398 assert( p_sys->i_pause_date > 0 );
400 p_sys->i_cmd_delay += i_date - p_sys->i_pause_date;
403 p_sys->b_paused = b_paused;
404 p_sys->i_pause_date = i_date;
406 vlc_cond_signal( &p_sys->wait );
410 static int ControlLockedSetRate( es_out_t *p_out, int i_src_rate, int i_rate )
412 es_out_sys_t *p_sys = p_out->p_sys;
414 if( !p_sys->b_delayed &&
415 i_src_rate == i_rate )
417 return es_out_SetRate( p_sys->p_out, i_src_rate, i_rate );
419 if( p_sys->p_input->b_can_pace_control )
421 /* XXX we may do it BUT it would be better to finish the clock clean up+improvments
422 * and so be able to advertize correctly pace control property in access
424 msg_Err( p_sys->p_input, "EsOutTimeshift does not work with streams that have space control" );
428 p_sys->i_cmd_delay += p_sys->i_rate_delay;
430 p_sys->i_rate_date = -1;
431 p_sys->i_rate_delay = 0;
432 p_sys->i_rate = i_rate;
433 p_sys->i_rate_source = i_src_rate;
435 if( !p_sys->b_delayed )
438 return es_out_SetRate( p_sys->p_out, i_rate, i_rate );
440 static int ControlLockedSetTime( es_out_t *p_out, mtime_t i_date )
442 es_out_sys_t *p_sys = p_out->p_sys;
444 if( !p_sys->b_delayed )
445 return es_out_SetTime( p_sys->p_out, i_date );
448 msg_Err( p_sys->p_input, "EsOutTimeshift does not yet support time change" );
451 static int ControlLockedSetFrameNext( es_out_t *p_out )
453 es_out_sys_t *p_sys = p_out->p_sys;
455 if( !p_sys->b_delayed )
456 return es_out_SetFrameNext( p_sys->p_out );
459 msg_Err( p_sys->p_input, "EsOutTimeshift does not yet support frame next" );
463 static int ControlLocked( es_out_t *p_out, int i_query, va_list args )
465 es_out_sys_t *p_sys = p_out->p_sys;
469 /* Invalid query for this es_out level */
470 case ES_OUT_SET_ES_BY_ID:
471 case ES_OUT_RESTART_ES_BY_ID:
472 case ES_OUT_SET_ES_DEFAULT_BY_ID:
473 case ES_OUT_SET_DELAY:
474 case ES_OUT_SET_RECORD_STATE:
478 /* TODO ? or to remove ? */
482 /* Pass-through control */
483 case ES_OUT_SET_ACTIVE:
484 case ES_OUT_GET_ACTIVE:
485 case ES_OUT_SET_MODE:
486 case ES_OUT_GET_MODE:
487 case ES_OUT_SET_GROUP:
488 case ES_OUT_GET_GROUP:
490 case ES_OUT_SET_GROUP_PCR:
491 case ES_OUT_RESET_PCR:
492 case ES_OUT_SET_NEXT_DISPLAY_TIME:
493 case ES_OUT_SET_GROUP_META:
494 case ES_OUT_SET_GROUP_EPG:
495 case ES_OUT_DEL_GROUP:
497 case ES_OUT_RESTART_ES:
498 case ES_OUT_SET_ES_DEFAULT:
499 case ES_OUT_SET_ES_STATE:
500 case ES_OUT_GET_ES_STATE:
501 case ES_OUT_SET_ES_FMT:
504 if( CmdInitControl( &cmd, i_query, args, p_sys->b_delayed ) )
506 if( p_sys->b_delayed )
508 CmdPush( p_out, &cmd );
511 return CmdExecuteControl( p_out, &cmd );
514 /* Special control */
515 case ES_OUT_GET_EMPTY:
517 bool *pb_empty = (bool*)va_arg( args, bool* );
518 return ControlLockedGetEmpty( p_out, pb_empty );
520 case ES_OUT_GET_WAKE_UP: /* TODO ? */
522 mtime_t *pi_wakeup = (mtime_t*)va_arg( args, mtime_t* );
523 return ControlLockedGetWakeup( p_out, pi_wakeup );
525 case ES_OUT_GET_BUFFERING:
527 bool *pb_buffering = (bool *)va_arg( args, bool* );
528 return ControlLockedGetBuffering( p_out, pb_buffering );
530 case ES_OUT_SET_PAUSE_STATE:
532 const bool b_source_paused = (bool)va_arg( args, int );
533 const bool b_paused = (bool)va_arg( args, int );
534 const mtime_t i_date = (mtime_t) va_arg( args, mtime_t );
536 return ControlLockedSetPauseState( p_out, b_source_paused, b_paused, i_date );
538 case ES_OUT_SET_RATE:
540 const int i_src_rate = (int)va_arg( args, int );
541 const int i_rate = (int)va_arg( args, int );
543 return ControlLockedSetRate( p_out, i_src_rate, i_rate );
545 case ES_OUT_SET_TIME:
547 const mtime_t i_date = (mtime_t)va_arg( args, mtime_t );
549 return ControlLockedSetTime( p_out, i_date );
551 case ES_OUT_SET_FRAME_NEXT:
553 return ControlLockedSetFrameNext( p_out );
557 msg_Err( p_sys->p_input, "Unknown es_out_Control query !" );
562 static int Control( es_out_t *p_out, int i_query, va_list args )
564 es_out_sys_t *p_sys = p_out->p_sys;
567 vlc_mutex_lock( &p_sys->lock );
568 i_ret = ControlLocked( p_out, i_query, args );
569 vlc_mutex_unlock( &p_sys->lock );
574 /*****************************************************************************
576 *****************************************************************************/
577 static int TsStart( es_out_t *p_out )
579 es_out_sys_t *p_sys = p_out->p_sys;
581 assert( !p_sys->b_delayed );
583 p_sys->p_thread = vlc_custom_create( p_sys->p_input, sizeof(vlc_object_t),
584 VLC_OBJECT_GENERIC, "es out timeshift" );
585 if( !p_sys->p_thread )
588 p_sys->b_delayed = true;
589 p_sys->p_thread->p_private = p_out;
590 if( vlc_thread_create( p_sys->p_thread, "es out timeshift",
591 TsRun, VLC_THREAD_PRIORITY_INPUT, false ) )
593 msg_Err( p_sys->p_input, "cannot create input thread" );
594 vlc_object_release( p_sys->p_thread );
596 p_sys->b_delayed = false;
602 static void TsStop( es_out_t *p_out )
604 es_out_sys_t *p_sys = p_out->p_sys;
606 assert( p_sys->b_delayed );
608 vlc_object_kill( p_sys->p_thread );
609 vlc_thread_join( p_sys->p_thread );
615 if( CmdPop( p_out, &cmd ) )
621 p_sys->b_delayed = false;
623 static void *TsRun( vlc_object_t *p_thread )
625 es_out_t *p_out = p_thread->p_private;
626 es_out_sys_t *p_sys = p_out->p_sys;
633 /* Pop a command to execute */
634 vlc_mutex_lock( &p_sys->lock );
635 mutex_cleanup_push( &p_sys->lock );
637 while( p_sys->b_paused || CmdPop( p_out, &cmd ) )
638 vlc_cond_wait( &p_sys->wait, &p_sys->lock );
640 if( p_sys->i_rate_date < 0 )
641 p_sys->i_rate_date = cmd.i_date;
643 p_sys->i_rate_delay = 0;
644 if( p_sys->i_rate_source != p_sys->i_rate )
646 const mtime_t i_duration = cmd.i_date - p_sys->i_rate_date;
647 p_sys->i_rate_delay = i_duration * p_sys->i_rate / p_sys->i_rate_source - i_duration;
649 if( p_sys->i_cmd_delay + p_sys->i_rate_delay < 0 )
651 /* Auto reset to rate 1.0 */
652 msg_Warn( p_sys->p_input, "es out timeshift: auto reset rate to %d", p_sys->i_rate_source );
654 p_sys->i_cmd_delay = 0;
656 p_sys->i_rate_date = -1;
657 p_sys->i_rate_delay = 0;
658 p_sys->i_rate = p_sys->i_rate_source;
660 if( !es_out_SetRate( p_sys->p_out, p_sys->i_rate_source, p_sys->i_rate ) )
662 vlc_value_t val = { .i_int = p_sys->i_rate };
664 * FIXME it is perfectly safe BUT it is ugly as it may hide a
665 * rate change requested by user */
666 input_ControlPush( p_sys->p_input, INPUT_CONTROL_SET_RATE, &val );
669 i_deadline = cmd.i_date + p_sys->i_cmd_delay + p_sys->i_rate_delay;
673 /* Regulate the speed of command processing to the same one than
675 //fprintf( stderr, "d=%d - %d\n", (int)(p_sys->i_cmd_delay/1000), (int)( (mdate() - cmd.i_date - p_sys->i_cmd_delay)/1000) );
676 vlc_cleanup_push( cmd_cleanup_routine, &cmd );
682 /* Execute the command */
683 const int canc = vlc_savecancel();
685 vlc_mutex_lock( &p_sys->lock );
689 CmdExecuteAdd( p_out, &cmd );
693 CmdExecuteSend( p_out, &cmd );
694 CmdCleanSend( &cmd );
697 CmdExecuteControl( p_out, &cmd );
698 CmdCleanControl( &cmd );
701 CmdExecuteDel( p_out, &cmd );
707 vlc_mutex_unlock( &p_sys->lock );
709 vlc_restorecancel( canc );
715 /*****************************************************************************
717 *****************************************************************************/
718 static void CmdPush( es_out_t *p_out, const ts_cmd_t *p_cmd )
720 es_out_sys_t *p_sys = p_out->p_sys;
721 ts_cmd_t *p_dup = malloc( sizeof(*p_dup) );
726 TAB_APPEND( p_sys->i_cmd, p_sys->pp_cmd, p_dup );
728 vlc_cond_signal( &p_sys->wait );
731 static int CmdPop( es_out_t *p_out, ts_cmd_t *p_cmd )
733 es_out_sys_t *p_sys = p_out->p_sys;
735 if( p_sys->i_cmd <= 0 )
738 *p_cmd = *p_sys->pp_cmd[0];
740 free( p_sys->pp_cmd[0] );
741 TAB_REMOVE( p_sys->i_cmd, p_sys->pp_cmd, p_sys->pp_cmd[0] );
745 static void CmdClean( ts_cmd_t *p_cmd )
747 switch( p_cmd->i_type )
750 CmdCleanAdd( p_cmd );
753 CmdCleanSend( p_cmd );
756 CmdCleanControl( p_cmd );
766 static int CmdInitAdd( ts_cmd_t *p_cmd, es_out_id_t *p_es, const es_format_t *p_fmt, bool b_copy )
768 p_cmd->i_type = C_ADD;
769 p_cmd->i_date = mdate();
770 p_cmd->add.p_es = p_es;
773 p_cmd->add.p_fmt = malloc( sizeof(*p_fmt) );
774 if( !p_cmd->add.p_fmt )
776 es_format_Copy( p_cmd->add.p_fmt, p_fmt );
780 p_cmd->add.p_fmt = (es_format_t*)p_fmt;
784 static void CmdExecuteAdd( es_out_t *p_out, ts_cmd_t *p_cmd )
786 es_out_sys_t *p_sys = p_out->p_sys;
788 p_cmd->add.p_es->p_es = es_out_Add( p_sys->p_out, p_cmd->add.p_fmt );
790 static void CmdCleanAdd( ts_cmd_t *p_cmd )
792 es_format_Clean( p_cmd->add.p_fmt );
793 free( p_cmd->add.p_fmt );
796 static void CmdInitSend( ts_cmd_t *p_cmd, es_out_id_t *p_es, block_t *p_block )
798 p_cmd->i_type = C_SEND;
799 p_cmd->i_date = mdate();
800 p_cmd->send.p_es = p_es;
801 p_cmd->send.p_block = p_block;
803 static int CmdExecuteSend( es_out_t *p_out, ts_cmd_t *p_cmd )
805 es_out_sys_t *p_sys = p_out->p_sys;
806 block_t *p_block = p_cmd->send.p_block;
808 p_cmd->send.p_block = NULL;
812 if( p_cmd->send.p_es->p_es )
813 return es_out_Send( p_sys->p_out, p_cmd->send.p_es->p_es, p_block );
814 block_Release( p_block );
818 static void CmdCleanSend( ts_cmd_t *p_cmd )
820 if( p_cmd->send.p_block )
821 block_Release( p_cmd->send.p_block );
824 static int CmdInitDel( ts_cmd_t *p_cmd, es_out_id_t *p_es )
826 p_cmd->i_type = C_DEL;
827 p_cmd->i_date = mdate();
828 p_cmd->del.p_es = p_es;
831 static void CmdExecuteDel( es_out_t *p_out, ts_cmd_t *p_cmd )
833 es_out_sys_t *p_sys = p_out->p_sys;
835 if( p_cmd->del.p_es->p_es )
836 es_out_Del( p_sys->p_out, p_cmd->del.p_es->p_es );
837 free( p_cmd->del.p_es );
840 static int CmdInitControl( ts_cmd_t *p_cmd, int i_query, va_list args, bool b_copy )
842 p_cmd->i_type = C_CONTROL;
843 p_cmd->i_date = mdate();
844 p_cmd->control.i_query = i_query;
845 p_cmd->control.p_meta = NULL;
846 p_cmd->control.p_epg = NULL;
847 p_cmd->control.p_fmt = NULL;
851 /* Pass-through control */
852 case ES_OUT_SET_ACTIVE: /* arg1= bool */
853 p_cmd->control.b_bool = (bool)va_arg( args, int );
856 case ES_OUT_GET_ACTIVE: /* arg1= bool* */
857 p_cmd->control.pb_bool = (bool*)va_arg( args, bool * );
860 case ES_OUT_SET_MODE: /* arg1= int */
861 case ES_OUT_SET_GROUP: /* arg1= int */
862 case ES_OUT_DEL_GROUP: /* arg1=int i_group */
863 p_cmd->control.i_int = (int)va_arg( args, int );
866 case ES_OUT_GET_MODE: /* arg2= int* */
867 case ES_OUT_GET_GROUP: /* arg1= int* */
868 p_cmd->control.pi_int = (int*)va_arg( args, int * );
871 case ES_OUT_SET_PCR: /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
872 case ES_OUT_SET_NEXT_DISPLAY_TIME: /* arg1=int64_t i_pts(microsecond) */
873 p_cmd->control.i_i64 = (int64_t)va_arg( args, int64_t );
876 case ES_OUT_SET_GROUP_PCR: /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
877 p_cmd->control.i_int = (int)va_arg( args, int );
878 p_cmd->control.i_i64 = (int64_t)va_arg( args, int64_t );
881 case ES_OUT_RESET_PCR: /* no arg */
884 case ES_OUT_SET_GROUP_META: /* arg1=int i_group arg2=vlc_meta_t* */
886 p_cmd->control.i_int = (int)va_arg( args, int );
887 vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t * );
891 p_cmd->control.p_meta = vlc_meta_New();
892 if( !p_cmd->control.p_meta )
894 vlc_meta_Merge( p_cmd->control.p_meta, p_meta );
898 p_cmd->control.p_meta = p_meta;
903 case ES_OUT_SET_GROUP_EPG: /* arg1=int i_group arg2=vlc_epg_t* */
905 p_cmd->control.i_int = (int)va_arg( args, int );
906 vlc_epg_t *p_epg = (vlc_epg_t*)va_arg( args, vlc_epg_t * );
910 p_cmd->control.p_epg = vlc_epg_New( p_epg->psz_name );
911 if( !p_cmd->control.p_epg )
913 for( int i = 0; i < p_epg->i_event; i++ )
915 vlc_epg_event_t *p_evt = p_epg->pp_event[i];
917 vlc_epg_AddEvent( p_cmd->control.p_epg,
918 p_evt->i_start, p_evt->i_duration,
920 p_evt->psz_short_description, p_evt->psz_description );
922 vlc_epg_SetCurrent( p_cmd->control.p_epg,
923 p_epg->p_current ? p_epg->p_current->i_start : -1 );
927 p_cmd->control.p_epg = p_epg;
932 /* Modified control */
933 case ES_OUT_SET_ES: /* arg1= es_out_id_t* */
934 case ES_OUT_RESTART_ES: /* arg1= es_out_id_t* */
935 case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t* */
936 p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
939 case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool */
940 p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
941 p_cmd->control.b_bool = (bool)va_arg( args, int );
944 case ES_OUT_GET_ES_STATE:/* arg1= es_out_id_t* arg2=bool* */
945 p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
946 p_cmd->control.pb_bool = (bool*)va_arg( args, bool * );
949 case ES_OUT_SET_ES_FMT: /* arg1= es_out_id_t* arg2=es_format_t* */
951 p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
952 es_format_t *p_fmt = (es_format_t*)va_arg( args, es_format_t * );
956 p_cmd->control.p_fmt = malloc( sizeof(*p_fmt) );
957 if( !p_cmd->control.p_fmt )
959 es_format_Copy( p_cmd->control.p_fmt, p_fmt );
963 p_cmd->control.p_fmt = p_fmt;
975 static int CmdExecuteControl( es_out_t *p_out, ts_cmd_t *p_cmd )
977 es_out_sys_t *p_sys = p_out->p_sys;
978 const int i_query = p_cmd->control.i_query;
982 /* Pass-through control */
983 case ES_OUT_SET_ACTIVE: /* arg1= bool */
984 return es_out_Control( p_sys->p_out, i_query, p_cmd->control.b_bool );
986 case ES_OUT_GET_ACTIVE: /* arg1= bool* */
987 return es_out_Control( p_sys->p_out, i_query, p_cmd->control.pb_bool );
989 case ES_OUT_SET_MODE: /* arg1= int */
990 case ES_OUT_SET_GROUP: /* arg1= int */
991 case ES_OUT_DEL_GROUP: /* arg1=int i_group */
992 return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int );
994 case ES_OUT_GET_MODE: /* arg2= int* */
995 case ES_OUT_GET_GROUP: /* arg1= int* */
996 return es_out_Control( p_sys->p_out, i_query, p_cmd->control.pi_int );
998 case ES_OUT_SET_PCR: /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
999 case ES_OUT_SET_NEXT_DISPLAY_TIME: /* arg1=int64_t i_pts(microsecond) */
1000 return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_i64 );
1002 case ES_OUT_SET_GROUP_PCR: /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
1003 return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.i_i64 );
1005 case ES_OUT_RESET_PCR: /* no arg */
1006 return es_out_Control( p_sys->p_out, i_query );
1008 case ES_OUT_SET_GROUP_META: /* arg1=int i_group arg2=vlc_meta_t* */
1009 return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.p_meta );
1011 case ES_OUT_SET_GROUP_EPG: /* arg1=int i_group arg2=vlc_epg_t* */
1012 return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.p_epg );
1014 /* Modified control */
1015 case ES_OUT_SET_ES: /* arg1= es_out_id_t* */
1016 case ES_OUT_RESTART_ES: /* arg1= es_out_id_t* */
1017 case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t* */
1018 return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es );
1020 case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool */
1021 return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.b_bool );
1023 case ES_OUT_GET_ES_STATE:/* arg1= es_out_id_t* arg2=bool* */
1024 return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.pb_bool );
1026 case ES_OUT_SET_ES_FMT: /* arg1= es_out_id_t* arg2=es_format_t* */
1027 return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.p_fmt );
1031 msg_Err( p_sys->p_input, "Unknown es_out_Control query in CmdExecuteControl!" );
1032 return VLC_EGENERIC;
1035 static void CmdCleanControl( ts_cmd_t *p_cmd )
1037 if( p_cmd->control.p_meta )
1038 vlc_meta_Delete( p_cmd->control.p_meta );
1039 if( p_cmd->control.p_epg )
1040 vlc_epg_Delete( p_cmd->control.p_epg );
1041 if( p_cmd->control.p_fmt )
1043 es_format_Clean( p_cmd->control.p_fmt );
1044 free( p_cmd->control.p_fmt );
1049 /*****************************************************************************
1051 *****************************************************************************/
1052 static char *GetTmpPath( char *psz_path )
1054 if( psz_path && *psz_path )
1056 /* Make sure that the path exists and is a directory */
1058 const int i_ret = utf8_stat( psz_path, &s );
1060 if( i_ret < 0 && !utf8_mkdir( psz_path, 0600 ) )
1062 else if( i_ret == 0 && ( s.st_mode & S_IFDIR ) )
1067 /* Create a suitable path */
1068 #if defined (WIN32) && !defined (UNDER_CE)
1069 const DWORD dwCount = GetTempPathW( 0, NULL );
1070 wchar_t *psw_path = calloc( dwCount + 1, sizeof(wchar_t) );
1073 if( GetTempPathW( dwCount + 1, psw_path ) <= 0 )
1077 psw_path = _wgetcwd( NULL, 0 );
1084 psz_path = FromWide( psw_path );
1085 while( psz_path && *psz_path && psz_path[strlen( psz_path ) - 1] == '\\' )
1086 psz_path[strlen( psz_path ) - 1] = '\0';
1091 if( !psz_path || *psz_path == '\0' )
1094 return strdup( "C:" );
1097 psz_path = strdup( "/tmp" );
1103 static FILE *GetTmpFile( const char *psz_path )
1110 if( asprintf( &psz_name, "%s/vlc-timeshift.XXXXXX", psz_path ) < 0 )
1114 fd = mkstemp( psz_name );
1121 f = fdopen( fd, "rw+" );