Added initial skeleton for es_out timeshift support.
authorLaurent Aimar <fenrir@videolan.org>
Tue, 11 Nov 2008 20:26:46 +0000 (21:26 +0100)
committerLaurent Aimar <fenrir@videolan.org>
Mon, 17 Nov 2008 19:03:27 +0000 (20:03 +0100)
It is not yet functionnal.

src/Makefile.am
src/input/es_out.c
src/input/es_out_timeshift.c [new file with mode: 0644]
src/input/es_out_timeshift.h [new file with mode: 0644]
src/input/input.c

index b16d6e7..b2de04b 100644 (file)
@@ -309,6 +309,7 @@ SOURCES_libvlc_common = \
        input/decoder_synchro.c \
        input/demux.c \
        input/es_out.c \
+       input/es_out_timeshift.c \
        input/input.c \
        input/meta.c \
        input/access.h \
@@ -316,6 +317,7 @@ SOURCES_libvlc_common = \
        input/decoder.h \
        input/demux.h \
        input/es_out.h \
+       input/es_out_timeshift.h \
        input/stream.h \
        input/input_internal.h \
        input/vlm_internal.h \
index 67332cb..bdc49fe 100644 (file)
@@ -2210,7 +2210,7 @@ static int EsOutControlLocked( es_out_t *out, int i_query, va_list args )
             return VLC_EGENERIC;
         }
 
-        case ES_OUT_SET_FMT:
+        case ES_OUT_SET_ES_FMT:
         {
             /* This ain't pretty but is need by some demuxers (eg. Ogg )
              * to update the p_extra data */
diff --git a/src/input/es_out_timeshift.c b/src/input/es_out_timeshift.c
new file mode 100644 (file)
index 0000000..3dc5add
--- /dev/null
@@ -0,0 +1,772 @@
+/*****************************************************************************
+ * es_out_timeshift.c: Es Out timeshift.
+ *****************************************************************************
+ * Copyright (C) 2008 Laurent Aimar
+ * $Id$
+ *
+ * Authors: Laurent Aimar < fenrir _AT_ via _DOT_ ecp _DOT_ fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <assert.h>
+#if defined (WIN32) && !defined (UNDER_CE)
+#  include <direct.h>
+#endif
+#ifdef HAVE_SYS_STAT_H
+#   include <sys/stat.h>
+#endif
+
+#include <vlc_common.h>
+#include <vlc_charset.h>
+
+#include <vlc_input.h>
+#include <vlc_es_out.h>
+#include <vlc_block.h>
+#include "input_internal.h"
+#include "es_out.h"
+#include "es_out_timeshift.h"
+
+/*****************************************************************************
+ * Local prototypes
+ *****************************************************************************/
+
+enum
+{
+    C_ADD,
+    C_SEND,
+    C_DEL,
+    C_CONTROL,
+};
+
+typedef struct
+{
+    int i_type;
+    union
+    {
+        struct
+        {
+            es_out_id_t *p_es;
+            es_format_t *p_fmt;
+        } add;
+        struct
+        {
+            es_out_id_t *p_es;
+        } del;
+        struct
+        {
+            es_out_id_t *p_es;
+            block_t *p_block;
+        } send;
+        struct
+        {
+            int  i_query;
+
+            bool b_bool;
+            bool *pb_bool;
+            int  i_int;
+            int  *pi_int;
+            int64_t i_i64;
+            vlc_meta_t *p_meta;
+            vlc_epg_t *p_epg;
+            es_out_id_t *p_es;
+            es_format_t *p_fmt;
+        } control;
+    };
+} ts_cmd_t;
+
+struct es_out_id_t
+{
+    es_out_id_t *p_es;
+};
+
+struct es_out_sys_t
+{
+    input_thread_t *p_input;
+       es_out_t       *p_out;
+
+    /* Configuration */
+    int64_t        i_tmp_size_max;    /* Maximal temporary file size in byte */
+    char           *psz_tmp_path;     /* Path for temporary files */
+
+    /* Lock for all following fields */
+    vlc_mutex_t    lock;
+
+    /* */
+    bool           b_delayed;
+
+    /* */
+    int            i_es;
+    es_out_id_t    **pp_es;
+
+    /* */
+    int            i_cmd;
+    ts_cmd_t       **pp_cmd;
+};
+
+static es_out_id_t *Add    ( es_out_t *, const es_format_t * );
+static int          Send   ( es_out_t *, es_out_id_t *, block_t * );
+static void         Del    ( es_out_t *, es_out_id_t * );
+static int          Control( es_out_t *, int i_query, va_list );
+static void         Destroy( es_out_t * );
+
+static void CmdPush( es_out_t *, const ts_cmd_t * );
+static int  CmdPop( es_out_t *p_out, ts_cmd_t *p_cmd );
+
+static int  CmdInitAdd    ( ts_cmd_t *, es_out_id_t *, const es_format_t *, bool b_copy );
+static void CmdInitSend   ( ts_cmd_t *, es_out_id_t *, block_t * );
+static int  CmdInitDel    ( ts_cmd_t *, es_out_id_t * );
+static int  CmdInitControl( ts_cmd_t *, int i_query, va_list, bool b_copy );
+
+static void CmdExecuteAdd    ( es_out_t *, ts_cmd_t * );
+static int  CmdExecuteSend   ( es_out_t *, ts_cmd_t * );
+static void CmdExecuteDel    ( es_out_t *, ts_cmd_t * );
+static int  CmdExecuteControl( es_out_t *, ts_cmd_t * );
+
+static void CmdCleanAdd    ( ts_cmd_t * );
+static void CmdCleanSend   ( ts_cmd_t * );
+static void CmdCleanControl( ts_cmd_t *p_cmd );
+
+static char *GetTmpPath( char *psz_path );
+static FILE *GetTmpFile( const char *psz_path );
+
+/*****************************************************************************
+ * input_EsOutTimeshiftNew:
+ *****************************************************************************/
+es_out_t *input_EsOutTimeshiftNew( input_thread_t *p_input, es_out_t *p_next_out )
+{
+    es_out_t *p_out = malloc( sizeof(*p_out) );
+    if( !p_out )
+        return NULL;
+
+    es_out_sys_t *p_sys = malloc( sizeof(*p_sys) );
+    if( !p_sys )
+    {
+        free( p_out );
+        return NULL;
+    }
+
+    /* */
+    p_out->pf_add     = Add;
+    p_out->pf_send    = Send;
+    p_out->pf_del     = Del;
+    p_out->pf_control = Control;
+    p_out->pf_destroy = Destroy;
+    p_out->p_sys      = p_sys;
+    p_out->b_sout     = p_input->p->p_sout != NULL;
+
+    /* */
+    p_sys->p_input = p_input;
+    p_sys->p_out = p_next_out;
+    vlc_mutex_init_recursive( &p_sys->lock );
+    p_sys->b_delayed = false;
+
+    TAB_INIT( p_sys->i_es, p_sys->pp_es );
+    TAB_INIT( p_sys->i_cmd, p_sys->pp_cmd );
+
+    /* TODO config
+     * timeshift-granularity
+     * timeshift-path
+     */
+    p_sys->i_tmp_size_max = 50 * 1024*1024;
+    p_sys->psz_tmp_path = GetTmpPath( NULL );
+
+    return p_out;
+}
+
+/*****************************************************************************
+ * Internal functions
+ *****************************************************************************/
+static void Destroy( es_out_t *p_out )
+{
+    es_out_sys_t *p_sys = p_out->p_sys;
+
+    while( p_sys->i_cmd > 0 )
+    {
+        ts_cmd_t cmd;
+
+        if( CmdPop( p_out, &cmd ) )
+            break;
+
+        switch( cmd.i_type )
+        {
+        case C_ADD:
+            CmdCleanAdd( &cmd );
+            break;
+        case C_SEND:
+            CmdCleanSend( &cmd );
+            break;
+        case C_CONTROL:
+            CmdCleanControl( &cmd );
+            break;
+        case C_DEL:
+            break;
+        default:
+            assert(0);
+            break;
+        }
+    }
+    TAB_CLEAN( p_sys->i_cmd, p_sys->pp_cmd  );
+
+    while( p_sys->i_es > 0 )
+        Del( p_out, p_sys->pp_es[0] );
+    TAB_CLEAN( p_sys->i_es, p_sys->pp_es  );
+
+    free( p_sys->psz_tmp_path );
+    vlc_mutex_destroy( &p_sys->lock );
+    free( p_sys );
+    free( p_out );
+}
+
+static es_out_id_t *Add( es_out_t *p_out, const es_format_t *p_fmt )
+{
+    es_out_sys_t *p_sys = p_out->p_sys;
+    ts_cmd_t cmd;
+
+    es_out_id_t *p_es = malloc( sizeof( *p_es ) );
+    if( !p_es )
+        return NULL;
+
+    vlc_mutex_lock( &p_sys->lock );
+
+    if( CmdInitAdd( &cmd, p_es, p_fmt, p_sys->b_delayed ) )
+    {
+        vlc_mutex_unlock( &p_sys->lock );
+        free( p_es );
+        return NULL;
+    }
+
+    TAB_APPEND( p_sys->i_es, p_sys->pp_es, p_es );
+
+    if( p_sys->b_delayed )
+        CmdPush( p_out, &cmd );
+    else
+        CmdExecuteAdd( p_out, &cmd );
+
+    vlc_mutex_unlock( &p_sys->lock );
+
+    return p_es;
+}
+static int Send( es_out_t *p_out, es_out_id_t *p_es, block_t *p_block )
+{
+    es_out_sys_t *p_sys = p_out->p_sys;
+    ts_cmd_t cmd;
+    int i_ret = VLC_SUCCESS;
+
+    vlc_mutex_lock( &p_sys->lock );
+
+    CmdInitSend( &cmd, p_es, p_block );
+    if( p_sys->b_delayed )
+        CmdPush( p_out, &cmd );
+    else
+        i_ret = CmdExecuteSend( p_out, &cmd) ;
+
+    vlc_mutex_unlock( &p_sys->lock );
+
+    return i_ret;
+}
+static void Del( es_out_t *p_out, es_out_id_t *p_es )
+{
+    es_out_sys_t *p_sys = p_out->p_sys;
+    ts_cmd_t cmd;
+
+    vlc_mutex_lock( &p_sys->lock );
+
+    CmdInitDel( &cmd, p_es );
+    if( p_sys->b_delayed )
+        CmdPush( p_out, &cmd );
+    else
+        CmdExecuteDel( p_out, &cmd );
+
+    TAB_REMOVE( p_sys->i_es, p_sys->pp_es, p_es );
+
+    vlc_mutex_unlock( &p_sys->lock );
+}
+static int ControlLocked( es_out_t *p_out, int i_query, va_list args )
+{
+    es_out_sys_t *p_sys = p_out->p_sys;
+
+    switch( i_query )
+    {
+    /* Invalid query for this es_out level */
+    case ES_OUT_SET_ES_BY_ID:
+    case ES_OUT_RESTART_ES_BY_ID:
+    case ES_OUT_SET_ES_DEFAULT_BY_ID:
+    case ES_OUT_SET_DELAY:
+    case ES_OUT_SET_RECORD_STATE:
+        assert(0);
+        return VLC_EGENERIC;
+
+    /* TODO ? or to remove ? */
+    case ES_OUT_GET_TS:
+        return VLC_EGENERIC;
+
+    /* Pass-through control */
+    case ES_OUT_SET_ACTIVE:
+    case ES_OUT_GET_ACTIVE:
+    case ES_OUT_SET_MODE:
+    case ES_OUT_GET_MODE:
+    case ES_OUT_SET_GROUP:
+    case ES_OUT_GET_GROUP:
+    case ES_OUT_SET_PCR:
+    case ES_OUT_SET_GROUP_PCR:
+    case ES_OUT_RESET_PCR:
+    case ES_OUT_SET_NEXT_DISPLAY_TIME:
+    case ES_OUT_SET_GROUP_META:
+    case ES_OUT_SET_GROUP_EPG:
+    case ES_OUT_DEL_GROUP:
+    case ES_OUT_SET_ES:
+    case ES_OUT_RESTART_ES:
+    case ES_OUT_SET_ES_DEFAULT:
+    case ES_OUT_SET_ES_STATE:
+    case ES_OUT_GET_ES_STATE:
+    case ES_OUT_SET_ES_FMT:
+    {
+        ts_cmd_t cmd;
+        if( CmdInitControl( &cmd, i_query, args, p_sys->b_delayed ) )
+            return VLC_EGENERIC;
+        if( p_sys->b_delayed )
+        {
+            CmdPush( p_out, &cmd );
+            return VLC_SUCCESS;
+        }
+        return CmdExecuteControl( p_out, &cmd );
+    }
+
+    /* Special control */
+    case ES_OUT_GET_WAKE_UP: /* TODO ? */
+    case ES_OUT_GET_BUFFERING:
+    case ES_OUT_GET_EMPTY:
+    case ES_OUT_SET_PAUSE_STATE:
+    case ES_OUT_SET_RATE:
+    case ES_OUT_SET_TIME:
+    case ES_OUT_SET_FRAME_NEXT:
+        if( p_sys->b_delayed )
+        {
+            /* TODO */
+        }
+        return es_out_vaControl( p_sys->p_out, i_query, args );
+
+    default:
+        msg_Err( p_sys->p_input, "Unknown es_out_Control query !" );
+        assert(0);
+        return VLC_EGENERIC;
+    }
+}
+static int Control( es_out_t *p_out, int i_query, va_list args )
+{
+    es_out_sys_t *p_sys = p_out->p_sys;
+    int i_ret;
+
+    vlc_mutex_lock( &p_sys->lock );
+    i_ret = ControlLocked( p_out, i_query, args );
+    vlc_mutex_unlock( &p_sys->lock );
+
+    return i_ret;
+}
+
+/*****************************************************************************
+ *
+ *****************************************************************************/
+static void CmdPush( es_out_t *p_out, const ts_cmd_t *p_cmd )
+{
+    es_out_sys_t *p_sys = p_out->p_sys;
+    ts_cmd_t *p_dup = malloc( sizeof(*p_dup) );
+
+    if( p_dup )
+    {
+        *p_dup = *p_cmd;
+        TAB_APPEND( p_sys->i_cmd, p_sys->pp_cmd, p_dup );
+    }
+}
+static int CmdPop( es_out_t *p_out, ts_cmd_t *p_cmd )
+{
+    es_out_sys_t *p_sys = p_out->p_sys;
+
+    if( p_sys->i_cmd <= 0 )
+        return VLC_EGENERIC;
+
+    *p_cmd = *p_sys->pp_cmd[0];
+
+    free( p_sys->pp_cmd[0] );
+    TAB_REMOVE( p_sys->i_cmd, p_sys->pp_cmd, p_sys->pp_cmd[0] );
+    return VLC_SUCCESS;
+}
+
+static int CmdInitAdd( ts_cmd_t *p_cmd, es_out_id_t *p_es, const es_format_t *p_fmt, bool b_copy )
+{
+    p_cmd->i_type = C_ADD;
+    p_cmd->add.p_es = p_es;
+    if( b_copy )
+    {
+        p_cmd->add.p_fmt = malloc( sizeof(*p_fmt) );
+        if( !p_cmd->add.p_fmt )
+            return VLC_EGENERIC;
+        es_format_Copy( p_cmd->add.p_fmt, p_fmt );
+    }
+    else
+    {
+        p_cmd->add.p_fmt = (es_format_t*)p_fmt;
+    }
+    return VLC_SUCCESS;
+}
+static void CmdExecuteAdd( es_out_t *p_out, ts_cmd_t *p_cmd )
+{
+    es_out_sys_t *p_sys = p_out->p_sys;
+
+    p_cmd->add.p_es->p_es = es_out_Add( p_sys->p_out, p_cmd->add.p_fmt );
+}
+static void CmdCleanAdd( ts_cmd_t *p_cmd )
+{
+    es_format_Clean( p_cmd->add.p_fmt );
+    free( p_cmd->add.p_fmt );
+}
+
+static void CmdInitSend( ts_cmd_t *p_cmd, es_out_id_t *p_es, block_t *p_block )
+{
+    p_cmd->i_type = C_SEND;
+    p_cmd->send.p_es = p_es;
+    p_cmd->send.p_block = p_block;
+}
+static int CmdExecuteSend( es_out_t *p_out, ts_cmd_t *p_cmd )
+{
+    es_out_sys_t *p_sys = p_out->p_sys;
+    block_t *p_block = p_cmd->send.p_block;
+
+    p_cmd->send.p_block = NULL;
+
+    if( p_block )
+    {
+        if( p_cmd->send.p_es->p_es )
+            return es_out_Send( p_sys->p_out, p_cmd->send.p_es->p_es, p_block );
+        block_Release( p_block );
+    }
+    return VLC_EGENERIC;
+}
+static void CmdCleanSend( ts_cmd_t *p_cmd )
+{
+    if( p_cmd->send.p_block )
+        block_Release( p_cmd->send.p_block );
+}
+
+static int CmdInitDel( ts_cmd_t *p_cmd, es_out_id_t *p_es )
+{
+    p_cmd->i_type = C_DEL;
+    p_cmd->del.p_es = p_es;
+    return VLC_SUCCESS;
+}
+static void CmdExecuteDel( es_out_t *p_out, ts_cmd_t *p_cmd )
+{
+    es_out_sys_t *p_sys = p_out->p_sys;
+
+    if( p_cmd->del.p_es->p_es )
+        es_out_Del( p_sys->p_out, p_cmd->del.p_es->p_es );
+    free( p_cmd->del.p_es );
+}
+
+static int CmdInitControl( ts_cmd_t *p_cmd, int i_query, va_list args, bool b_copy )
+{
+    p_cmd->i_type = C_CONTROL;
+    p_cmd->control.i_query = i_query;
+
+    switch( i_query )
+    {
+    /* Pass-through control */
+    case ES_OUT_SET_ACTIVE:  /* arg1= bool                     */
+        p_cmd->control.b_bool = (bool)va_arg( args, int );
+        break;
+
+    case ES_OUT_GET_ACTIVE:  /* arg1= bool*                    */
+        p_cmd->control.pb_bool = (bool*)va_arg( args, bool * );
+        break;
+
+    case ES_OUT_SET_MODE:    /* arg1= int                            */
+    case ES_OUT_SET_GROUP:   /* arg1= int                            */
+    case ES_OUT_DEL_GROUP:   /* arg1=int i_group */
+        p_cmd->control.i_int = (int)va_arg( args, int );
+        break;
+
+    case ES_OUT_GET_MODE:    /* arg2= int*                           */
+    case ES_OUT_GET_GROUP:   /* arg1= int*                           */
+        p_cmd->control.pi_int = (int*)va_arg( args, int * );
+        break;
+
+    case ES_OUT_SET_PCR:                /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
+    case ES_OUT_SET_NEXT_DISPLAY_TIME:  /* arg1=int64_t i_pts(microsecond) */
+        p_cmd->control.i_i64 = (int64_t)va_arg( args, int64_t );
+        break;
+
+    case ES_OUT_SET_GROUP_PCR:          /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
+        p_cmd->control.i_int = (int)va_arg( args, int );
+        p_cmd->control.i_i64 = (int64_t)va_arg( args, int64_t );
+        break;
+
+    case ES_OUT_RESET_PCR:           /* no arg */
+        break;
+
+    case ES_OUT_SET_GROUP_META:  /* arg1=int i_group arg2=vlc_meta_t* */
+    {
+        p_cmd->control.i_int = (int)va_arg( args, int );
+        vlc_meta_t *p_meta = (vlc_meta_t*)va_arg( args, vlc_meta_t * );
+
+        if( b_copy )
+        {
+            p_cmd->control.p_meta = vlc_meta_New();
+            if( !p_cmd->control.p_meta )
+                return VLC_EGENERIC;
+            vlc_meta_Merge( p_cmd->control.p_meta, p_meta );
+        }
+        else
+        {
+            p_cmd->control.p_meta = p_meta;
+        }
+        break;
+    }
+
+    case ES_OUT_SET_GROUP_EPG:   /* arg1=int i_group arg2=vlc_epg_t* */
+    {
+        p_cmd->control.i_int = (int)va_arg( args, int );
+        vlc_epg_t *p_epg = (vlc_epg_t*)va_arg( args, vlc_epg_t * );
+
+        if( b_copy )
+        {
+            p_cmd->control.p_epg = vlc_epg_New( p_epg->psz_name );
+            if( !p_cmd->control.p_epg )
+                return VLC_EGENERIC;
+            for( int i = 0; i < p_epg->i_event; i++ )
+            {
+                vlc_epg_event_t *p_evt = p_epg->pp_event[i];
+
+                vlc_epg_AddEvent( p_cmd->control.p_epg,
+                                  p_evt->i_start, p_evt->i_duration,
+                                  p_evt->psz_name,
+                                  p_evt->psz_short_description, p_evt->psz_description );
+            }
+            vlc_epg_SetCurrent( p_cmd->control.p_epg,
+                                p_epg->p_current ? p_epg->p_current->i_start : -1 );
+        }
+        else
+        {
+            p_cmd->control.p_epg = p_epg;
+        }
+        break;
+    }
+
+    /* Modified control */
+    case ES_OUT_SET_ES:      /* arg1= es_out_id_t*                   */
+    case ES_OUT_RESTART_ES:  /* arg1= es_out_id_t*                   */
+    case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t*                */
+        p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
+        break;
+
+    case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool   */
+        p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
+        p_cmd->control.b_bool = (bool)va_arg( args, int );
+        break;
+
+    case ES_OUT_GET_ES_STATE:/* arg1= es_out_id_t* arg2=bool*  */
+        p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
+        p_cmd->control.pb_bool = (bool*)va_arg( args, bool * );
+        break;
+
+    case ES_OUT_SET_ES_FMT:     /* arg1= es_out_id_t* arg2=es_format_t* */
+    {
+        p_cmd->control.p_es = (es_out_id_t*)va_arg( args, es_out_id_t * );
+        es_format_t *p_fmt = (es_format_t*)va_arg( args, es_format_t * );
+
+        if( b_copy )
+        {
+            p_cmd->control.p_fmt = malloc( sizeof(*p_fmt) );
+            if( !p_cmd->control.p_fmt )
+                return VLC_EGENERIC;
+            es_format_Copy( p_cmd->control.p_fmt, p_fmt );
+        }
+        else
+        {
+            p_cmd->control.p_fmt = p_fmt;
+        }
+        break;
+    }
+
+    default:
+        assert(0);
+        return VLC_EGENERIC;
+    }
+
+    return VLC_SUCCESS;
+}
+static int CmdExecuteControl( es_out_t *p_out, ts_cmd_t *p_cmd )
+{
+    es_out_sys_t *p_sys = p_out->p_sys;
+    const int i_query = p_cmd->control.i_query;
+
+    switch( i_query )
+    {
+    /* Pass-through control */
+    case ES_OUT_SET_ACTIVE:  /* arg1= bool                     */
+        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.b_bool );
+
+    case ES_OUT_GET_ACTIVE:  /* arg1= bool*                    */
+        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.pb_bool );
+
+    case ES_OUT_SET_MODE:    /* arg1= int                            */
+    case ES_OUT_SET_GROUP:   /* arg1= int                            */
+    case ES_OUT_DEL_GROUP:   /* arg1=int i_group */
+        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int );
+
+    case ES_OUT_GET_MODE:    /* arg2= int*                           */
+    case ES_OUT_GET_GROUP:   /* arg1= int*                           */
+        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.pi_int );
+
+    case ES_OUT_SET_PCR:                /* arg1=int64_t i_pcr(microsecond!) (using default group 0)*/
+    case ES_OUT_SET_NEXT_DISPLAY_TIME:  /* arg1=int64_t i_pts(microsecond) */
+        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_i64 );
+
+    case ES_OUT_SET_GROUP_PCR:          /* arg1= int i_group, arg2=int64_t i_pcr(microsecond!)*/
+        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.i_i64 );
+
+    case ES_OUT_RESET_PCR:           /* no arg */
+        return es_out_Control( p_sys->p_out, i_query );
+
+    case ES_OUT_SET_GROUP_META:  /* arg1=int i_group arg2=vlc_meta_t* */
+        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.p_meta );
+
+    case ES_OUT_SET_GROUP_EPG:   /* arg1=int i_group arg2=vlc_epg_t* */
+        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.i_int, p_cmd->control.p_epg );
+
+    /* Modified control */
+    case ES_OUT_SET_ES:      /* arg1= es_out_id_t*                   */
+    case ES_OUT_RESTART_ES:  /* arg1= es_out_id_t*                   */
+    case ES_OUT_SET_ES_DEFAULT: /* arg1= es_out_id_t*                */
+        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es );
+
+    case ES_OUT_SET_ES_STATE:/* arg1= es_out_id_t* arg2=bool   */
+        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.b_bool );
+
+    case ES_OUT_GET_ES_STATE:/* arg1= es_out_id_t* arg2=bool*  */
+        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.pb_bool );
+
+    case ES_OUT_SET_ES_FMT:     /* arg1= es_out_id_t* arg2=es_format_t* */
+        return es_out_Control( p_sys->p_out, i_query, p_cmd->control.p_es->p_es, p_cmd->control.p_fmt );
+
+    default:
+        assert(0);
+        msg_Err( p_sys->p_input, "Unknown es_out_Control query in CmdExecuteControl!" );
+        return VLC_EGENERIC;
+    }
+}
+static void CmdCleanControl( ts_cmd_t *p_cmd )
+{
+    if( p_cmd->control.p_meta )
+        vlc_meta_Delete( p_cmd->control.p_meta );
+    if( p_cmd->control.p_epg )
+        vlc_epg_Delete( p_cmd->control.p_epg );
+    if( p_cmd->control.p_fmt )
+    {
+        es_format_Clean( p_cmd->control.p_fmt );
+        free( p_cmd->control.p_fmt );
+    }
+}
+
+
+/*****************************************************************************
+ * GetTmpFile/Path:
+ *****************************************************************************/
+static char *GetTmpPath( char *psz_path )
+{
+    if( psz_path && *psz_path )
+    {
+        /* Make sure that the path exists and is a directory */
+        struct stat s;
+        const int i_ret = utf8_stat( psz_path, &s );
+
+        if( i_ret < 0 && !utf8_mkdir( psz_path, 0600 ) )
+            return psz_path;
+        else if( i_ret == 0 && ( s.st_mode & S_IFDIR ) )
+            return psz_path;
+    }
+    free( psz_path );
+
+    /* Create a suitable path */
+#if defined (WIN32) && !defined (UNDER_CE)
+    const DWORD dwCount = GetTempPathW( 0, NULL );
+    wchar_t *psw_path = calloc( dwCount + 1, sizeof(wchar_t) );
+    if( psw_path )
+    {
+        if( GetTempPathW( dwCount + 1, psw_path ) <= 0 )
+        {
+            free( psw_path );
+
+            psw_path = _wgetcwd( NULL, 0 );
+        }
+    }
+
+    psz_path = NULL;
+    if( psw_path )
+    {
+        psz_path = FromWide( psw_path );
+        while( psz_path && *psz_path && psz_path[strlen( psz_path ) - 1] == '\\' )
+            psz_path[strlen( psz_path ) - 1] = '\0';
+
+        free( psw_path );
+    }
+
+    if( !psz_path || *psz_path == '\0' )
+    {
+        free( psz_path );
+        return strdup( "C:" );
+    }
+#else
+    psz_path = strdup( "/tmp" );
+#endif
+
+    return psz_path;
+}
+
+static FILE *GetTmpFile( const char *psz_path )
+{
+    char *psz_name;
+    int fd;
+    FILE *f;
+
+    /* */
+    if( asprintf( &psz_name, "%s/vlc-timeshift.XXXXXX", psz_path ) < 0 )
+        return NULL;
+
+    /* */
+    fd = mkstemp( psz_name );
+    free( psz_name );
+
+    if( fd < 0 )
+        return NULL;
+
+    /* */
+    f = fdopen( fd, "rw+" );
+    if( !f )
+        close( fd );
+
+    return f;
+}
+
diff --git a/src/input/es_out_timeshift.h b/src/input/es_out_timeshift.h
new file mode 100644 (file)
index 0000000..4370963
--- /dev/null
@@ -0,0 +1,36 @@
+/*****************************************************************************
+ * es_out_timeshift.h: Es Out timeshift.
+ *****************************************************************************
+ * Copyright (C) 2008 Laurent Aimar
+ * $Id$
+ *
+ * Authors: Laurent Aimar < fenrir _AT_ via _DOT_ ecp _DOT_ fr>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#if defined(__PLUGIN__) || defined(__BUILTIN__) || !defined(__LIBVLC__)
+# error This header file can only be included from LibVLC.
+#endif
+
+#ifndef _INPUT_ES_OUT_TIMESHIFT_H
+#define _INPUT_ES_OUT_TIMESHIFT_H 1
+
+#include <vlc_common.h>
+
+
+es_out_t *input_EsOutTimeshiftNew( input_thread_t *, es_out_t * );
+
+#endif
index 8864c2e..67ca630 100644 (file)
@@ -37,6 +37,7 @@
 
 #include "input_internal.h"
 #include "es_out.h"
+#include "es_out_timeshift.h"
 #include "access.h"
 #include "demux.h"
 #include "stream.h"
@@ -200,8 +201,8 @@ static input_thread_t *Create( vlc_object_t *p_parent, input_item_t *p_item,
     p_input->p->b_recording = false;
     TAB_INIT( p_input->p->i_bookmark, p_input->p->bookmark );
     TAB_INIT( p_input->p->i_attachment, p_input->p->attachment );
-    p_input->p->p_es_out_display =
-    p_input->p->p_es_out         = NULL;
+    p_input->p->p_es_out_display = NULL;
+    p_input->p->p_es_out = NULL;
     p_input->p->p_sout  = NULL;
     p_input->p->b_out_pace_control = false;
     p_input->i_pts_delay = 0;
@@ -1183,8 +1184,8 @@ static int Init( input_thread_t * p_input )
 #endif
 
     /* Create es out */
-    p_input->p->p_es_out         =
     p_input->p->p_es_out_display = input_EsOutNew( p_input, p_input->p->i_rate );
+    p_input->p->p_es_out         = input_EsOutTimeshiftNew( p_input, p_input->p->p_es_out_display );
     es_out_Control( p_input->p->p_es_out, ES_OUT_SET_ACTIVE, false );
     es_out_Control( p_input->p->p_es_out, ES_OUT_SET_MODE, ES_OUT_MODE_NONE );
 
@@ -1264,12 +1265,10 @@ static int Init( input_thread_t * p_input )
 error:
     input_ChangeState( p_input, ERROR_S );
 
-    if( p_input->p->p_es_out_display )
-    {
-        //TODO
-    }
     if( p_input->p->p_es_out )
         es_out_Delete( p_input->p->p_es_out );
+    if( p_input->p->p_es_out_display )
+        es_out_Delete( p_input->p->p_es_out_display );
 #ifdef ENABLE_SOUT
     if( p_input->p->p_sout )
     {
@@ -1357,12 +1356,10 @@ static void End( input_thread_t * p_input )
     free( p_input->p->slave );
 
     /* Unload all modules */
-    if( p_input->p->p_es_out_display )
-    {
-        //TODO
-    }
     if( p_input->p->p_es_out )
         es_out_Delete( p_input->p->p_es_out );
+    if( p_input->p->p_es_out_display )
+        es_out_Delete( p_input->p->p_es_out_display );
 
     if( !p_input->b_preparsing )
     {