X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Faccess_filter%2Ftimeshift.c;h=b01cc1c649e54e8e7e1b661edec8e9a2e57a1ec9;hb=848489c3863b1b7ddbe542aa3e746063722b9280;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..b01cc1c649 --- 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,62 +19,105 @@ * * 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. *****************************************************************************/ /***************************************************************************** * Preamble *****************************************************************************/ -#include +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include #include -#include -#include +#include +#include +#include + #include +#ifdef WIN32 +# include /* _wgetcwd */ +#endif + /***************************************************************************** * Module descriptor *****************************************************************************/ 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 " \ + "that 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." ) +#define FORCE_TEXT N_("Force use of the timeshift module") +#define FORCE_LONGTEXT N_("Force use of the timeshift module even if the " \ + "access declares that it can control pace or pause." ) + vlc_module_begin(); - set_shortname( _("Timeshift") ); - set_description( _("Timeshift") ); + set_shortname( N_("Timeshift") ); + set_description( N_("Timeshift") ); set_category( CAT_INPUT ); set_subcategory( SUBCAT_INPUT_ACCESS_FILTER ); set_capability( "access_filter", 0 ); add_shortcut( "timeshift" ); set_callbacks( Open, Close ); + + add_integer( "timeshift-granularity", 50, NULL, GRANULARITY_TEXT, + GRANULARITY_LONGTEXT, true ); + add_directory( "timeshift-dir", 0, 0, DIR_TEXT, DIR_LONGTEXT, false ); + change_unsafe(); + add_bool( "timeshift-force", false, NULL, FORCE_TEXT, FORCE_LONGTEXT, + 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 + +typedef struct ts_entry_t +{ + FILE *file; + struct ts_entry_t *p_next; + +} ts_entry_t; struct access_sys_t { block_fifo_t *p_fifo; - vlc_bool_t b_opened; - - FILE *t1, *t2; + int i_files; + int i_file_size; + int i_write_size; - char *psz_tmp1; - char *psz_tmp2; + 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 *w; - int i_w; + char *psz_filename_base; + char *psz_filename; - FILE *r; + int64_t i_data; }; /***************************************************************************** @@ -84,66 +128,63 @@ static int Open( vlc_object_t *p_this ) access_t *p_access = (access_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 + bool b_bool; - /* Only work with not pace controled access */ - if( access2_Control( p_src, ACCESS_CAN_CONTROL_PACE, &b_bool ) || b_bool ) + var_Create( p_access, "timeshift-force", VLC_VAR_BOOL|VLC_VAR_DOINHERIT ); + if( var_GetBool( p_access, "timeshift-force" ) ) { - msg_Dbg( p_src, "ACCESS_CAN_CONTROL_PACE" ); - return VLC_EGENERIC; + msg_Dbg( p_access, "Forcing use of timeshift even if access can control pace or pause" ); } - /* Refuse access that can be paused */ - if( access2_Control( p_src, ACCESS_CAN_PAUSE, &b_bool ) || b_bool ) + else { - msg_Dbg( p_src, "ACCESS_CAN_PAUSE: timeshift useless" ); - return VLC_EGENERIC; + /* Only work with not pace controled access */ + if( access_Control( p_src, ACCESS_CAN_CONTROL_PACE, &b_bool ) || + b_bool ) + { + msg_Dbg( p_src, "ACCESS_CAN_CONTROL_PACE: timeshift useless" ); + return VLC_EGENERIC; + } + /* Refuse access that can be paused */ + if( access_Control( p_src, ACCESS_CAN_PAUSE, &b_bool ) || b_bool ) + { + msg_Dbg( p_src, "ACCESS_CAN_PAUSE: timeshift useless" ); + return VLC_EGENERIC; + } } /* */ 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; - -#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_fifo = block_FifoNew(); + p_sys->i_write_size = 0; + p_sys->i_files = 0; + p_sys->i_data = 0; + + 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 ) ) + VLC_THREAD_PRIORITY_LOW, false ) ) { msg_Err( p_access, "cannot spawn timeshift access thread" ); return VLC_EGENERIC; @@ -159,22 +200,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,72 +239,31 @@ 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; + access_t *p_src = p_access->p_source; + block_t *p_block = NULL; - if( p_access->b_die ) + /* Update info (we probably ought to be time caching that as well) */ + if( p_src->info.i_update & INPUT_UPDATE_META ) { - p_access->info.b_eof = VLC_TRUE; - return NULL; + p_src->info.i_update &= ~INPUT_UPDATE_META; + p_access->info.i_update |= INPUT_UPDATE_META; } - return block_FifoGet( p_sys->p_fifo ); -} + /* Get data from timeshift fifo */ + if( !p_access->info.b_eof ) + p_block = block_FifoGet( p_sys->p_fifo ); -/***************************************************************************** - * - *****************************************************************************/ -static int Control( access_t *p_access, int i_query, va_list args ) -{ - access_t *p_src = p_access->p_source; + if( p_block && !p_block->i_buffer ) /* Used to signal EOF */ + { block_Release( p_block ); p_block = 0; } - vlc_bool_t *pb_bool; - int *pi_int; - int64_t *pi_64; - - switch( i_query ) + if( p_block ) { - /* */ - case ACCESS_CAN_SEEK: - case ACCESS_CAN_FASTSEEK: - pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); - *pb_bool = VLC_FALSE; - break; - - case ACCESS_CAN_CONTROL_PACE: /* Not really true */ - case ACCESS_CAN_PAUSE: - pb_bool = (vlc_bool_t*)va_arg( args, vlc_bool_t* ); - *pb_bool = VLC_TRUE; - break; - - /* */ - case ACCESS_GET_MTU: - pi_int = (int*)va_arg( args, int * ); - *pi_int = 0; - break; - - 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; - - case ACCESS_GET_TITLE_INFO: - case ACCESS_SET_TITLE: - case ACCESS_SET_SEEKPOINT: - case ACCESS_GET_META: - return VLC_EGENERIC; - - case ACCESS_SET_PRIVATE_ID_STATE: - case ACCESS_GET_PRIVATE_ID_STATE: - case ACCESS_SET_PRIVATE_ID_CA: - return access2_vaControl( p_src, i_query, args ); - - default: - msg_Warn( p_access, "unimplemented query in control" ); - return VLC_EGENERIC; - + p_sys->i_data -= p_block->i_buffer; + return p_block; } - return VLC_SUCCESS; + + p_access->info.b_eof = p_src->info.b_eof; + return NULL; } /***************************************************************************** @@ -260,203 +273,330 @@ 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_loop = 0; + block_t *p_block; - while( !p_access->b_die ) + while( vlc_object_alive (p_access) ) { - block_t *p_block; - - /* Get a new 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; + 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 ) + 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; + block_Release( p_block ); + p_block = NULL; } } - /* Open dump files if we need them */ - if( p_sys->p_fifo->i_size >= TIMESHIFT_FIFO_MAX && !p_sys->b_opened ) + if( p_block == NULL ) { - msg_Dbg( p_access, "opening first temporary files (%s)", - p_sys->psz_tmp1 ); + if( p_src->info.b_eof ) break; + msleep( 10000 ); + continue; + } - p_sys->b_opened = VLC_TRUE; - p_sys->t1 = p_sys->t2 = NULL; - p_sys->w = p_sys->r = NULL; + p_sys->i_data += p_block->i_buffer; - 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) ); - } + /* Write block */ + if( !p_sys->p_write_list && !p_sys->p_read_list && + block_FifoSize( p_sys->p_fifo ) < TIMESHIFT_FIFO_MAX ) + { + /* If there isn't too much timeshifted data, + * write directly to FIFO */ + block_FifoPut( p_sys->p_fifo, p_block ); + continue; } - if( p_sys->w ) + WriteBlockToFile( p_access, p_block ); + block_Release( p_block ); + + /* Read from file to fill up the fifo */ + while( block_FifoSize( p_sys->p_fifo ) < TIMESHIFT_FIFO_MIN && + vlc_object_alive (p_access) ) { - int i_write; + p_block = ReadBlockFromFile( p_access ); + if( !p_block ) break; - /* Dump the block */ - i_write = fwrite( p_block->p_buffer, 1, p_block->i_buffer, - p_sys->w ); - block_Release( p_block ); + block_FifoPut( p_sys->p_fifo, p_block ); + } + } - if( i_write > 0 ) - p_sys->i_w += i_write; - else - msg_Warn( p_access, "cannot write data" ); + msg_Dbg( p_access, "timeshift: no more input 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 ); + while( vlc_object_alive (p_access) && + (p_sys->p_read_list || block_FifoSize( p_sys->p_fifo ) ) ) + { + /* Read from file to fill up the fifo */ + while( block_FifoSize( p_sys->p_fifo ) < TIMESHIFT_FIFO_MIN && + vlc_object_alive (p_access) && p_sys->p_read_list ) + { + p_block = ReadBlockFromFile( p_access ); + if( !p_block ) break; - p_sys->r = p_sys->w; - fseek( p_sys->r, 0, SEEK_SET ); + block_FifoPut( p_sys->p_fifo, p_block ); + } - p_sys->w = p_sys->t2; - p_sys->i_w = 0; - } + msleep( 100000 ); + } - 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 ) + msg_Dbg( p_access, "timeshift: EOF" ); + p_src->info.b_eof = true; + + /* Send dummy packet to avoid deadlock in Block() */ + block_FifoPut( p_sys->p_fifo, block_New( p_access, 0 ) ); +} + +/***************************************************************************** + * 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 ) { - block_FifoPut( p_sys->p_fifo, p_block ); + /* We just can't work with less than 2 buffer files */ + msg_Err( p_access, "cannot open temporary file '%s' (%m)", + p_sys->psz_filename ); + return VLC_EGENERIC; } - else + 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 ) { - /* We failed to opened files so trash new data */ - block_Release( p_block ); + msg_Warn( p_access, "no more space, overwritting old data" ); + NextFileRead( p_access ); + NextFileRead( p_access ); } -#if 0 - if( (i_loop % 400) == 0 ) - msg_Dbg( p_access, "timeshift: buff=%d", p_sys->p_fifo->i_size ); -#endif - i_loop++; + + /* Make sure we switch to next file in write list */ + p_sys->i_write_size = p_sys->i_file_size; } - msg_Warn( p_access, "timeshift: EOF" ); + 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; - /* Send dummy packet to avoid deadlock in TShiftBlock */ - for( i_loop = 0; i_loop < 2; i_loop++ ) + if( !p_sys->p_read_list && p_sys->p_write_list ) { - block_t *p_dummy = block_New( p_access, 128 ); + /* 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_dummy->i_flags |= BLOCK_FLAG_DISCONTINUITY; - memset( p_dummy->p_buffer, 0, p_dummy->i_buffer ); + 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; +} + +/***************************************************************************** + * + *****************************************************************************/ +static int Control( access_t *p_access, int i_query, va_list args ) +{ + bool *pb_bool; + int *pi_int; - block_FifoPut( p_sys->p_fifo, p_dummy ); + switch( i_query ) + { + case ACCESS_CAN_SEEK: + case ACCESS_CAN_FASTSEEK: + pb_bool = (bool*)va_arg( args, bool* ); + *pb_bool = true; + break; + + case ACCESS_CAN_CONTROL_PACE: /* Not really true */ + case ACCESS_CAN_PAUSE: + pb_bool = (bool*)va_arg( args, bool* ); + *pb_bool = true; + break; + + case ACCESS_GET_MTU: + pi_int = (int*)va_arg( args, int * ); + *pi_int = 0; + break; + + case ACCESS_SET_PAUSE_STATE: + break; + + /* Forward everything else to the source access */ + default: + return access_vaControl( p_access->p_source, i_query, args ); } + return VLC_SUCCESS; } +/***************************************************************************** + * GetTmpFilePath: + *****************************************************************************/ +#ifdef WIN32 +#define getpid() (int)GetCurrentProcessId() +#endif +static char *GetTmpFilePath( access_t *p_access ) +{ + char *psz_dir = var_GetNonEmptyString( p_access, "timeshift-dir" ); + char *psz_filename_base; + + if( psz_dir == NULL ) + { +#ifdef WIN32 + DWORD ret = GetTempPathW (0, NULL); + wchar_t wdir[ret + 3]; // can at least old "C:" + nul + const wchar_t *pwdir = wdir; + wchar_t *pwdir_free = NULL; + + if (GetTempPathW (ret + 1, wdir) == 0) + { + pwdir_free = pwdir = _wgetcwd (NULL, 0); + if (pwdir == NULL) + pwdir = L"C:"; + } + + psz_dir = FromWide (pwdir); + if (pwdir_free != NULL) + free (pwdir_free); + + /* remove trailing antislash if any */ + if (psz_dir[strlen (psz_dir) - 1] == '\\') + psz_dir[strlen (psz_dir) - 1] = '\0'; +#else + psz_dir = strdup( "/tmp" ); +#endif + } + + asprintf( &psz_filename_base, "%s/vlc-timeshift-%d-%d-", + psz_dir, getpid(), p_access->i_object_id ); + free( psz_dir ); + + return psz_filename_base; +}