Commit 7e676a02 authored by Laurent Aimar's avatar Laurent Aimar

Added initial skeleton for es_out timeshift support.

It is not yet functionnal.
parent cdf95dd2
......@@ -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 \
......
......@@ -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 */
......
/*****************************************************************************
* 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,