X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess_filter%2Ftimeshift.c;h=1345f4f28a84b99a0fe30538d61de56d6856e5ec;hb=07eee2b853949a787a2c2509e9027bc1a149e4a2;hp=c30b8ef9cf92eb77c1505351c5240b20dd1329ba;hpb=85b29bdc288a1573d43bd524908be5748a9b3640;p=vlc diff --git a/modules/access_filter/timeshift.c b/modules/access_filter/timeshift.c old mode 100755 new mode 100644 index c30b8ef9cf..1345f4f28a --- a/modules/access_filter/timeshift.c +++ b/modules/access_filter/timeshift.c @@ -1,10 +1,11 @@ /***************************************************************************** - * timeshift.c + * timeshift.c: access filter implementing timeshifting capabilities ***************************************************************************** - * Copyright (C) 2005 VideoLAN (Centrale Réseaux) and its contributors - * $Id: demux.c 7546 2004-04-29 13:53:29Z gbazin $ + * Copyright (C) 2005 the VideoLAN team + * $Id$ * - * Author: Laurent Aimar + * Authors: Laurent Aimar + * Gildas Bazin * * 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 @@ -18,7 +19,7 @@ * * 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -30,6 +31,8 @@ #include #include +#include "charset.h" + #include /***************************************************************************** @@ -38,6 +41,14 @@ static int Open ( vlc_object_t * ); static void Close( vlc_object_t * ); +#define GRANULARITY_TEXT N_("Timeshift granularity") +/// \bug [String] typo +#define GRANULARITY_LONGTEXT N_( "This is the size of the temporary files " \ + "tha will be used to store the timeshifted streams." ) +#define DIR_TEXT N_("Timeshift directory") +#define DIR_LONGTEXT N_( "Directory used to store the timeshift temporary " \ + "files." ) + vlc_module_begin(); set_shortname( _("Timeshift") ); set_description( _("Timeshift") ); @@ -46,34 +57,50 @@ vlc_module_begin(); set_capability( "access_filter", 0 ); add_shortcut( "timeshift" ); set_callbacks( Open, Close ); + + add_integer( "timeshift-granularity", 50, NULL, GRANULARITY_TEXT, + GRANULARITY_LONGTEXT, VLC_TRUE ); + add_directory( "timeshift-dir", 0, 0, DIR_TEXT, DIR_LONGTEXT, VLC_FALSE ); vlc_module_end(); /***************************************************************************** * Local prototypes *****************************************************************************/ +static int Seek( access_t *, int64_t ); static block_t *Block ( access_t *p_access ); static int Control( access_t *, int i_query, va_list args ); -static void Thread ( access_t * ); +static void Thread ( access_t *p_access ); +static int WriteBlockToFile( access_t *p_access, block_t *p_block ); +static block_t *ReadBlockFromFile( access_t *p_access ); +static char *GetTmpFilePath( access_t *p_access ); -#define TIMESHIFT_FIFO_MAX (4*1024*1024) +#define TIMESHIFT_FIFO_MAX (10*1024*1024) #define TIMESHIFT_FIFO_MIN (TIMESHIFT_FIFO_MAX/4) +#define TMP_FILE_MAX 256 -struct access_sys_t +typedef struct ts_entry_t { - block_fifo_t *p_fifo; + FILE *file; + struct ts_entry_t *p_next; - vlc_bool_t b_opened; +} ts_entry_t; - FILE *t1, *t2; +struct access_sys_t +{ + block_fifo_t *p_fifo; - char *psz_tmp1; - char *psz_tmp2; + int i_files; + int i_file_size; + int i_write_size; - FILE *w; - int i_w; + ts_entry_t *p_read_list; + ts_entry_t **pp_read_last; + ts_entry_t *p_write_list; + ts_entry_t **pp_write_last; - FILE *r; + char *psz_filename_base; + char *psz_filename; }; /***************************************************************************** @@ -85,10 +112,6 @@ static int Open( vlc_object_t *p_this ) access_t *p_src = p_access->p_source; access_sys_t *p_sys; vlc_bool_t b_bool; -#ifdef WIN32 - char buffer[4096]; - int i_size; -#endif /* Only work with not pace controled access */ if( access2_Control( p_src, ACCESS_CAN_CONTROL_PACE, &b_bool ) || b_bool ) @@ -106,41 +129,32 @@ static int Open( vlc_object_t *p_this ) /* */ p_access->pf_read = NULL; p_access->pf_block = Block; - p_access->pf_seek = NULL; + p_access->pf_seek = Seek; p_access->pf_control = Control; - p_access->info = p_src->info; p_access->p_sys = p_sys = malloc( sizeof( access_sys_t ) ); /* */ p_sys->p_fifo = block_FifoNew( p_access ); - p_sys->b_opened = VLC_FALSE; - p_sys->t1 = p_sys->t2 = NULL; - p_sys->w = p_sys->r = NULL; - p_sys->i_w = 0; + p_sys->i_write_size = 0; + p_sys->i_files = 0; -#ifdef WIN32 - i_size = GetTempPath( 4095, buffer ); - if( i_size <= 0 || i_size >= 4095 ) - { - if( getcwd( buffer, 4095 ) == NULL ) - strcpy( buffer, "c:" ); - } - /* remove last \\ if any */ - if( buffer[strlen(buffer)-1] == '\\' ) - buffer[strlen(buffer)-1] = '\0'; - - asprintf( &p_sys->psz_tmp1, "%s\\vlc-timeshift-%d-%d-1.dat", - buffer, GetCurrentProcessId(), p_access->i_object_id ); - asprintf( &p_sys->psz_tmp2, "%s\\vlc-timeshift-%d-%d-2.dat", - buffer, GetCurrentProcessId(), p_access->i_object_id ); -#else - asprintf( &p_sys->psz_tmp1, "/tmp/vlc-timeshift-%d-%d-1.dat", - getpid(), p_access->i_object_id ); - asprintf( &p_sys->psz_tmp2, "/tmp/vlc-timeshift-%d-%d-2.dat", - getpid(), p_access->i_object_id ); -#endif + p_sys->p_read_list = NULL; + p_sys->pp_read_last = &p_sys->p_read_list; + p_sys->p_write_list = NULL; + p_sys->pp_write_last = &p_sys->p_write_list; + + var_Create( p_access, "timeshift-dir", + VLC_VAR_DIRECTORY | VLC_VAR_DOINHERIT ); + var_Create( p_access, "timeshift-granularity", + VLC_VAR_INTEGER | VLC_VAR_DOINHERIT ); + p_sys->i_file_size = var_GetInteger( p_access, "timeshift-granularity" ); + if( p_sys->i_file_size < 1 ) p_sys->i_file_size = 1; + p_sys->i_file_size *= 1024 * 1024; /* In MBytes */ + + p_sys->psz_filename_base = GetTmpFilePath( p_access ); + p_sys->psz_filename = malloc( strlen( p_sys->psz_filename_base ) + 1000 ); if( vlc_thread_create( p_access, "timeshift thread", Thread, VLC_THREAD_PRIORITY_LOW, VLC_FALSE ) ) @@ -159,22 +173,35 @@ static void Close( vlc_object_t *p_this ) { access_t *p_access = (access_t*)p_this; access_sys_t *p_sys = p_access->p_sys; + ts_entry_t *p_entry; + int i; - /* */ msg_Dbg( p_access, "timeshift close called" ); vlc_thread_join( p_access ); - if( p_sys->b_opened ) + for( p_entry = p_sys->p_write_list; p_entry; ) { - if( p_sys->t1 ) fclose( p_sys->t1 ); - if( p_sys->t2 ) fclose( p_sys->t2 ); - unlink( p_sys->psz_tmp1 ); - unlink( p_sys->psz_tmp2 ); + ts_entry_t *p_next = p_entry->p_next; + fclose( p_entry->file ); + free( p_entry ); + p_entry = p_next; + } + for( p_entry = p_sys->p_read_list; p_entry; ) + { + ts_entry_t *p_next = p_entry->p_next; + fclose( p_entry->file ); + free( p_entry ); + p_entry = p_next; + } + for( i = 0; i < p_sys->i_files; i++ ) + { + sprintf( p_sys->psz_filename, "%s%i.dat", + p_sys->psz_filename_base, i ); + unlink( p_sys->psz_filename ); } - free( p_sys->psz_tmp1 ); - free( p_sys->psz_tmp2 ); - + free( p_sys->psz_filename ); + free( p_sys->psz_filename_base ); block_FifoRelease( p_sys->p_fifo ); free( p_sys ); } @@ -185,6 +212,7 @@ static void Close( vlc_object_t *p_this ) static block_t *Block( access_t *p_access ) { access_sys_t *p_sys = p_access->p_sys; + block_t *p_block; if( p_access->b_die ) { @@ -192,7 +220,255 @@ static block_t *Block( access_t *p_access ) return NULL; } - return block_FifoGet( p_sys->p_fifo ); + p_block = block_FifoGet( p_sys->p_fifo ); + //p_access->info.i_size -= p_block->i_buffer; + return p_block; +} + +/***************************************************************************** + * + *****************************************************************************/ +static void Thread( access_t *p_access ) +{ + access_sys_t *p_sys = p_access->p_sys; + access_t *p_src = p_access->p_source; + int i; + + while( !p_access->b_die ) + { + block_t *p_block; + + /* Get a new block from the source */ + if( p_src->pf_block ) + { + p_block = p_src->pf_block( p_src ); + + if( p_block == NULL ) + { + if( p_src->info.b_eof ) break; + msleep( 1000 ); + continue; + } + } + else + { + if( ( p_block = block_New( p_access, 2048 ) ) == NULL ) break; + + p_block->i_buffer = + p_src->pf_read( p_src, p_block->p_buffer, 2048 ); + + if( p_block->i_buffer < 0 ) + { + block_Release( p_block ); + if( p_block->i_buffer == 0 ) break; + msleep( 1000 ); + continue; + } + } + + /* Write block */ + if( !p_sys->p_write_list && !p_sys->p_read_list && + p_sys->p_fifo->i_size < TIMESHIFT_FIFO_MAX ) + { + /* If there isn't too much timeshifted data, + * write directly to FIFO */ + block_FifoPut( p_sys->p_fifo, p_block ); + + //p_access->info.i_size += p_block->i_buffer; + //p_access->info.i_update |= INPUT_UPDATE_SIZE; + + /* Nothing else to do */ + continue; + } + + WriteBlockToFile( p_access, p_block ); + block_Release( p_block ); + + /* Read from file to fill up the fifo */ + while( p_sys->p_fifo->i_size < TIMESHIFT_FIFO_MIN && + !p_access->b_die ) + { + p_block = ReadBlockFromFile( p_access ); + if( !p_block ) break; + block_FifoPut( p_sys->p_fifo, p_block ); + } + } + + msg_Dbg( p_access, "timeshift: EOF" ); + + /* Send dummy packet to avoid deadlock in TShiftBlock */ + for( i = 0; i < 2; i++ ) + { + block_t *p_dummy = block_New( p_access, 128 ); + p_dummy->i_flags |= BLOCK_FLAG_DISCONTINUITY; + memset( p_dummy->p_buffer, 0, p_dummy->i_buffer ); + block_FifoPut( p_sys->p_fifo, p_dummy ); + } +} + +/***************************************************************************** + * NextFileWrite: + *****************************************************************************/ +static void NextFileWrite( access_t *p_access ) +{ + access_sys_t *p_sys = p_access->p_sys; + ts_entry_t *p_next; + + if( !p_sys->p_write_list ) + { + p_sys->i_write_size = 0; + return; + } + + p_next = p_sys->p_write_list->p_next; + + /* Put written file in read list */ + if( p_sys->i_write_size < p_sys->i_file_size ) + ftruncate( fileno( p_sys->p_write_list->file ), p_sys->i_write_size ); + + fseek( p_sys->p_write_list->file, 0, SEEK_SET ); + *p_sys->pp_read_last = p_sys->p_write_list; + p_sys->pp_read_last = &p_sys->p_write_list->p_next; + p_sys->p_write_list->p_next = 0; + + /* Switch to next file to write */ + p_sys->p_write_list = p_next; + if( !p_sys->p_write_list ) p_sys->pp_write_last = &p_sys->p_write_list; + + p_sys->i_write_size = 0; +} + +/***************************************************************************** + * NextFileRead: + *****************************************************************************/ +static void NextFileRead( access_t *p_access ) +{ + access_sys_t *p_sys = p_access->p_sys; + ts_entry_t *p_next; + + if( !p_sys->p_read_list ) return; + + p_next = p_sys->p_read_list->p_next; + + /* Put read file in write list */ + fseek( p_sys->p_read_list->file, 0, SEEK_SET ); + *p_sys->pp_write_last = p_sys->p_read_list; + p_sys->pp_write_last = &p_sys->p_read_list->p_next; + p_sys->p_read_list->p_next = 0; + + /* Switch to next file to read */ + p_sys->p_read_list = p_next; + if( !p_sys->p_read_list ) p_sys->pp_read_last = &p_sys->p_read_list; +} + +/***************************************************************************** + * WriteBlockToFile: + *****************************************************************************/ +static int WriteBlockToFile( access_t *p_access, block_t *p_block ) +{ + access_sys_t *p_sys = p_access->p_sys; + int i_write, i_buffer; + + if( p_sys->i_write_size == p_sys->i_file_size ) NextFileWrite( p_access ); + + /* Open new file if necessary */ + if( !p_sys->p_write_list ) + { + FILE *file; + + sprintf( p_sys->psz_filename, "%s%i.dat", + p_sys->psz_filename_base, p_sys->i_files ); + file = utf8_fopen( p_sys->psz_filename, "w+b" ); + + if( !file && p_sys->i_files < 2 ) + { + /* We just can't work with less than 2 buffer files */ + msg_Err( p_access, "cannot open temporary file '%s' (%s)", + p_sys->psz_filename, strerror(errno) ); + return VLC_EGENERIC; + } + else if( !file ) return VLC_EGENERIC; + + p_sys->p_write_list = malloc( sizeof(ts_entry_t) ); + p_sys->p_write_list->p_next = 0; + p_sys->p_write_list->file = file; + p_sys->pp_write_last = &p_sys->p_write_list->p_next; + + p_sys->i_files++; + } + + /* Write to file */ + i_buffer = __MIN( p_block->i_buffer, + p_sys->i_file_size - p_sys->i_write_size ); + + i_write = fwrite( p_block->p_buffer, 1, i_buffer, + p_sys->p_write_list->file ); + + if( i_write > 0 ) p_sys->i_write_size += i_write; + + //p_access->info.i_size += i_write; + //p_access->info.i_update |= INPUT_UPDATE_SIZE; + + if( i_write < i_buffer ) + { + /* Looks like we're short of space */ + + if( !p_sys->p_write_list->p_next ) + { + msg_Warn( p_access, "no more space, overwritting old data" ); + NextFileRead( p_access ); + NextFileRead( p_access ); + } + + /* Make sure we switch to next file in write list */ + p_sys->i_write_size = p_sys->i_file_size; + } + + p_block->p_buffer += i_write; + p_block->i_buffer -= i_write; + + /* Check if we have some data left */ + if( p_block->i_buffer ) return WriteBlockToFile( p_access, p_block ); + + return VLC_SUCCESS; +} + +/***************************************************************************** + * ReadBlockFromFile: + *****************************************************************************/ +static block_t *ReadBlockFromFile( access_t *p_access ) +{ + access_sys_t *p_sys = p_access->p_sys; + block_t *p_block; + + if( !p_sys->p_read_list && p_sys->p_write_list ) + { + /* Force switching to next write file, that should + * give us something to read */ + NextFileWrite( p_access ); + } + + if( !p_sys->p_read_list ) return 0; + + p_block = block_New( p_access, 4096 ); + p_block->i_buffer = fread( p_block->p_buffer, 1, 4096, + p_sys->p_read_list->file ); + + if( p_block->i_buffer == 0 ) NextFileRead( p_access ); + + //p_access->info.i_size -= p_block->i_buffer; + //p_access->info.i_update |= INPUT_UPDATE_SIZE; + + return p_block; +} + +/***************************************************************************** + * Seek: seek to a specific location in a file + *****************************************************************************/ +static int Seek( access_t *p_access, int64_t i_pos ) +{ + //access_sys_t *p_sys = p_access->p_sys; + return VLC_SUCCESS; } /***************************************************************************** @@ -208,11 +484,10 @@ static int Control( access_t *p_access, int i_query, va_list args ) switch( i_query ) { - /* */ case ACCESS_CAN_SEEK: case ACCESS_CAN_FASTSEEK: pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); - *pb_bool = VLC_FALSE; + *pb_bool = VLC_TRUE; break; case ACCESS_CAN_CONTROL_PACE: /* Not really true */ @@ -221,7 +496,6 @@ static int Control( access_t *p_access, int i_query, va_list args ) *pb_bool = VLC_TRUE; break; - /* */ case ACCESS_GET_MTU: pi_int = (int*)va_arg( args, int * ); *pi_int = 0; @@ -230,7 +504,7 @@ static int Control( access_t *p_access, int i_query, va_list args ) case ACCESS_GET_PTS_DELAY: pi_64 = (int64_t*)va_arg( args, int64_t * ); return access2_Control( p_src, ACCESS_GET_PTS_DELAY, pi_64 ); - /* */ + case ACCESS_SET_PAUSE_STATE: return VLC_SUCCESS; @@ -254,209 +528,48 @@ static int Control( access_t *p_access, int i_query, va_list args ) } /***************************************************************************** - * + * GetTmpFilePath: *****************************************************************************/ -static void Thread( access_t *p_access ) +#ifdef WIN32 +#define getpid() GetCurrentProcessId() +#endif +static char *GetTmpFilePath( access_t *p_access ) { - access_sys_t *p_sys = p_access->p_sys; - access_t *p_src = p_access->p_source; - int i_loop = 0; + char *psz_dir = var_GetString( p_access, "timeshift-dir" ); + char *psz_filename_base; - while( !p_access->b_die ) + if( ( psz_dir != NULL ) && ( psz_dir[0] == '\0' ) ) { - block_t *p_block; - - /* Get a new block */ - if( p_src->pf_block ) - { - p_block = p_src->pf_block( p_src ); - - if( p_block == NULL ) - { - if( p_src->info.b_eof ) - break; - - msleep( 1000 ); - continue; - } - } - else - { - if( ( p_block = block_New( p_access, 2048 ) ) == NULL ) - break; + free( psz_dir ); + psz_dir = NULL; + } - p_block->i_buffer = p_src->pf_read( p_src, p_block->p_buffer, 2048); - if( p_block->i_buffer < 0 ) - { - block_Release( p_block ); - if( p_block->i_buffer == 0 ) - break; - msleep( 1000 ); - continue; - } - } + if( psz_dir == NULL ) + { +#ifdef WIN32 + char psz_local_dir[MAX_PATH]; + int i_size; - /* Open dump files if we need them */ - if( p_sys->p_fifo->i_size >= TIMESHIFT_FIFO_MAX && !p_sys->b_opened ) + i_size = GetTempPath( MAX_PATH, psz_local_dir ); + if( i_size <= 0 || i_size > MAX_PATH ) { - msg_Dbg( p_access, "opening first temporary files (%s)", - p_sys->psz_tmp1 ); - - p_sys->b_opened = VLC_TRUE; - p_sys->t1 = p_sys->t2 = NULL; - p_sys->w = p_sys->r = NULL; - - p_sys->t1 = fopen( p_sys->psz_tmp1, "w+b" ); - if( p_sys->t1 ) - { - msg_Dbg( p_access, "opening second temporary files (%s)", - p_sys->psz_tmp2 ); - - p_sys->t2 = fopen( p_sys->psz_tmp2, "w+b" ); - if( p_sys->t2 ) - { - p_sys->w = p_sys->t1; - p_sys->i_w = 0; - - msg_Dbg( p_access, "start writing into temporary file" ); - } - else - { - msg_Err( p_access, "cannot open temporary file '%s' (%s)", - p_sys->psz_tmp2, strerror(errno) ); - fclose( p_sys->t1 ); - p_sys->t1 = NULL; - } - } - else - { - msg_Err( p_access, "cannot open temporary file '%s' (%s)", - p_sys->psz_tmp1, strerror(errno) ); - } + if( !getcwd( psz_local_dir, MAX_PATH ) ) + strcpy( psz_local_dir, "C:" ); } - if( p_sys->w ) - { - int i_write; - - /* Dump the block */ - i_write = fwrite( p_block->p_buffer, 1, p_block->i_buffer, - p_sys->w ); - block_Release( p_block ); - - if( i_write > 0 ) - p_sys->i_w += i_write; - else - msg_Warn( p_access, "cannot write data" ); - - /* Start reading from a file if fifo isn't at 25% */ - if( p_sys->p_fifo->i_size < TIMESHIFT_FIFO_MIN && !p_sys->r ) - { - msg_Dbg( p_access, "start reading from temporary file (dumped=%d)", p_sys->i_w ); - - p_sys->r = p_sys->w; - fseek( p_sys->r, 0, SEEK_SET ); - - p_sys->w = p_sys->t2; - p_sys->i_w = 0; - } + psz_dir = FromLocaleDup( psz_local_dir ); - if( p_sys->r ) - { - while( p_sys->p_fifo->i_size < TIMESHIFT_FIFO_MIN ) - { - p_block = block_New( p_access, 4096 ); - p_block->i_buffer = fread( p_block->p_buffer, 1, 4096, - p_sys->r ); - - if( p_block->i_buffer > 0 ) - { - block_FifoPut( p_sys->p_fifo, p_block ); - } - else if( p_sys->i_w > 32*1024) - { - FILE *tmp; - block_Release( p_block ); - - msg_Dbg( p_access, "switching temporary files (dumped=%d)", p_sys->i_w ); - - /* Switch read/write */ - tmp = p_sys->r; - - p_sys->r = p_sys->w; - fseek( p_sys->r, 0, SEEK_SET ); - - p_sys->w = tmp; - fseek( p_sys->w, 0, SEEK_SET ); - ftruncate( fileno(p_sys->w), 0 ); - p_sys->i_w = 0; - } - else - { - msg_Dbg( p_access, "removing temporary files" ); - - /* We will remove the need of tmp files */ - if( p_sys->i_w > 0 ) - { - msg_Dbg( p_access, "loading temporary file" ); - fseek( p_sys->w, 0, SEEK_SET ); - for( ;; ) - { - p_block = block_New( p_access, 4096 ); - p_block->i_buffer = fread( p_block->p_buffer, - 1, 4096, - p_sys->w ); - if( p_block->i_buffer <= 0 ) - { - block_Release( p_block ); - break; - } - block_FifoPut( p_sys->p_fifo, p_block ); - } - } - - p_sys->b_opened = VLC_FALSE; - - fclose( p_sys->t1 ); - fclose( p_sys->t2 ); - - p_sys->t1 = p_sys->t2 = NULL; - p_sys->w = p_sys->r = NULL; - - unlink( p_sys->psz_tmp1 ); - unlink( p_sys->psz_tmp2 ); - break; - } - } - } - } - else if( p_sys->p_fifo->i_size < TIMESHIFT_FIFO_MAX ) - { - block_FifoPut( p_sys->p_fifo, p_block ); - } - else - { - /* We failed to opened files so trash new data */ - block_Release( p_block ); - } -#if 0 - if( (i_loop % 400) == 0 ) - msg_Dbg( p_access, "timeshift: buff=%d", p_sys->p_fifo->i_size ); + /* remove last \\ if any */ + if( psz_dir[strlen(psz_dir)-1] == '\\' ) + psz_dir[strlen(psz_dir)-1] = '\0'; +#else + psz_dir = strdup( "/tmp" ); #endif - i_loop++; } - msg_Warn( p_access, "timeshift: EOF" ); - - /* Send dummy packet to avoid deadlock in TShiftBlock */ - for( i_loop = 0; i_loop < 2; i_loop++ ) - { - block_t *p_dummy = block_New( p_access, 128 ); - - p_dummy->i_flags |= BLOCK_FLAG_DISCONTINUITY; - memset( p_dummy->p_buffer, 0, p_dummy->i_buffer ); + asprintf( &psz_filename_base, "%s/vlc-timeshift-%d-%d-", + psz_dir, getpid(), p_access->i_object_id ); + free( psz_dir ); - block_FifoPut( p_sys->p_fifo, p_dummy ); - } + return psz_filename_base; } -