📄 stream.c
字号:
/***************************************************************************** * stream.c ***************************************************************************** * Copyright (C) 1999-2004 the VideoLAN team * $Id: 1e442b407cc2630e7012b49cf672806a52054be9 $ * * Authors: Laurent Aimar <fenrir@via.ecp.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. *****************************************************************************/#ifdef HAVE_CONFIG_H# include "config.h"#endif#include <vlc_common.h>#include <assert.h>#include "input_internal.h"#undef STREAM_DEBUG/* TODO: * - tune the 2 methods (block/stream) * - compute cost for seek * - improve stream mode seeking with closest segments * - ... * - Maybe remove (block/stream) in favour of immediate *//* Two methods: * - using pf_block * One linked list of data read * - using pf_read * More complex scheme using mutliple track to avoid seeking * - using directly the access (only indirection for peeking). * This method is known to introduce much less latency. * It should probably defaulted (instead of the stream method (2)). *//* How many tracks we have, currently only used for stream mode */#ifdef OPTIMIZE_MEMORY# define STREAM_CACHE_TRACK 1 /* Max size of our cache 128Ko per track */# define STREAM_CACHE_SIZE (STREAM_CACHE_TRACK*1024*128)#else# define STREAM_CACHE_TRACK 3 /* Max size of our cache 4Mo per track */# define STREAM_CACHE_SIZE (4*STREAM_CACHE_TRACK*1024*1024)#endif/* How many data we try to prebuffer */#define STREAM_CACHE_PREBUFFER_SIZE (32767)/* Maximum time we take to pre-buffer */#define STREAM_CACHE_PREBUFFER_LENGTH (100*1000)/* Method1: Simple, for pf_block. * We get blocks and put them in the linked list. * We release blocks once the total size is bigger than CACHE_BLOCK_SIZE */#define STREAM_DATA_WAIT 40000 /* Time between before a pf_block retry *//* Method2: A bit more complex, for pf_read * - We use ring buffers, only one if unseekable, all if seekable * - Upon seek date current ring, then search if one ring match the pos, * yes: switch to it, seek the access to match the end of the ring * no: search the ring with i_end the closer to i_pos, * if close enough, read data and use this ring * else use the oldest ring, seek and use it. * * TODO: - with access non seekable: use all space available for only one ring, but * we have to support seekable/non-seekable switch on the fly. * - compute a good value for i_read_size * - ? */#define STREAM_READ_ATONCE 32767#define STREAM_CACHE_TRACK_SIZE (STREAM_CACHE_SIZE/STREAM_CACHE_TRACK)typedef struct{ int64_t i_date; int64_t i_start; int64_t i_end; uint8_t *p_buffer;} stream_track_t;typedef struct{ char *psz_path; int64_t i_size;} access_entry_t;typedef enum stream_read_method_t{ Immediate, Block, Stream} stream_read_method_t;struct stream_sys_t{ access_t *p_access; stream_read_method_t method; /* method to use */ int64_t i_pos; /* Current reading offset */ /* Method 1: pf_block */ struct { int64_t i_start; /* Offset of block for p_first */ int64_t i_offset; /* Offset for data in p_current */ block_t *p_current; /* Current block */ int i_size; /* Total amount of data in the list */ block_t *p_first; block_t **pp_last; } block; /* Method 2: for pf_read */ struct { int i_offset; /* Buffer offset in the current track */ int i_tk; /* Current track */ stream_track_t tk[STREAM_CACHE_TRACK]; /* Global buffer */ uint8_t *p_buffer; /* */ int i_used; /* Used since last read */ int i_read_size; } stream; /* Method 3: for pf_read */ struct { int64_t i_end; uint8_t *p_buffer; } immediate; /* Peek temporary buffer */ unsigned int i_peek; uint8_t *p_peek; /* Stat for both method */ struct { bool b_fastseek; /* From access */ /* Stat about reading data */ int64_t i_read_count; int64_t i_bytes; int64_t i_read_time; /* Stat about seek */ int i_seek_count; int64_t i_seek_time; } stat; /* Streams list */ int i_list; access_entry_t **list; int i_list_index; access_t *p_list_access; /* Preparse mode ? */ bool b_quick;};/* Method 1: */static int AStreamReadBlock( stream_t *s, void *p_read, unsigned int i_read );static int AStreamPeekBlock( stream_t *s, const uint8_t **p_peek, unsigned int i_read );static int AStreamSeekBlock( stream_t *s, int64_t i_pos );static void AStreamPrebufferBlock( stream_t *s );static block_t *AReadBlock( stream_t *s, bool *pb_eof );/* Method 2 */static int AStreamReadStream( stream_t *s, void *p_read, unsigned int i_read );static int AStreamPeekStream( stream_t *s, const uint8_t **pp_peek, unsigned int i_read );static int AStreamSeekStream( stream_t *s, int64_t i_pos );static void AStreamPrebufferStream( stream_t *s );static int AReadStream( stream_t *s, void *p_read, unsigned int i_read );/* Method 3 */static int AStreamReadImmediate( stream_t *s, void *p_read, unsigned int i_read );static int AStreamPeekImmediate( stream_t *s, const uint8_t **pp_peek, unsigned int i_read );static int AStreamSeekImmediate( stream_t *s, int64_t i_pos );/* Common */static int AStreamControl( stream_t *s, int i_query, va_list );static void AStreamDestroy( stream_t *s );static void UStreamDestroy( stream_t *s );static int ASeek( stream_t *s, int64_t i_pos );/**************************************************************************** * Method 3 helpers: ****************************************************************************/static inline int64_t stream_buffered_size( stream_t *s ){ return s->p_sys->immediate.i_end;}static inline void stream_buffer_empty( stream_t *s, int length ){ length = __MAX( stream_buffered_size( s ), length ); if( length ) { memmove( s->p_sys->immediate.p_buffer, s->p_sys->immediate.p_buffer + length, stream_buffered_size( s ) - length ); } s->p_sys->immediate.i_end -= length;}static inline void stream_buffer_fill( stream_t *s, int length ){ s->p_sys->immediate.i_end += length;}static inline uint8_t * stream_buffer( stream_t *s ){ return s->p_sys->immediate.p_buffer;}/**************************************************************************** * stream_UrlNew: create a stream from a access ****************************************************************************/stream_t *__stream_UrlNew( vlc_object_t *p_parent, const char *psz_url ){ const char *psz_access, *psz_demux; char *psz_path; access_t *p_access; stream_t *p_res; if( !psz_url ) return NULL; char psz_dup[strlen( psz_url ) + 1]; strcpy( psz_dup, psz_url ); input_SplitMRL( &psz_access, &psz_demux, &psz_path, psz_dup ); /* Now try a real access */ p_access = access_New( p_parent, psz_access, psz_demux, psz_path ); if( p_access == NULL ) { msg_Err( p_parent, "no suitable access module for `%s'", psz_url ); return NULL; } if( !( p_res = stream_AccessNew( p_access, true ) ) ) { access_Delete( p_access ); return NULL; } p_res->pf_destroy = UStreamDestroy; return p_res;}stream_t *stream_AccessNew( access_t *p_access, bool b_quick ){ stream_t *s = vlc_stream_create( VLC_OBJECT(p_access) ); stream_sys_t *p_sys; char *psz_list = NULL; if( !s ) return NULL; /* Attach it now, needed for b_die */ vlc_object_attach( s, p_access ); s->pf_read = NULL; /* Set up later */ s->pf_peek = NULL; s->pf_control = AStreamControl; s->pf_destroy = AStreamDestroy; s->p_sys = p_sys = malloc( sizeof( stream_sys_t ) ); if( p_sys == NULL ) goto error; /* UTF16 and UTF32 text file conversion */ s->i_char_width = 1; s->b_little_endian = false; s->conv = (vlc_iconv_t)(-1); /* Common field */ p_sys->p_access = p_access; if( p_access->pf_block ) p_sys->method = Block; else if (var_CreateGetBool( s, "use-stream-immediate")) p_sys->method = Immediate; else p_sys->method = Stream; p_sys->i_pos = p_access->info.i_pos; /* Stats */ access_Control( p_access, ACCESS_CAN_FASTSEEK, &p_sys->stat.b_fastseek ); p_sys->stat.i_bytes = 0; p_sys->stat.i_read_time = 0; p_sys->stat.i_read_count = 0; p_sys->stat.i_seek_count = 0; p_sys->stat.i_seek_time = 0; p_sys->i_list = 0; p_sys->list = 0; p_sys->i_list_index = 0; p_sys->p_list_access = 0; p_sys->b_quick = b_quick; /* Get the additional list of inputs if any (for concatenation) */ if( (psz_list = var_CreateGetString( s, "input-list" )) && *psz_list ) { access_entry_t *p_entry = malloc( sizeof(access_entry_t) ); if( p_entry == NULL ) goto error; char *psz_name, *psz_parser = psz_name = psz_list; p_sys->p_list_access = p_access; p_entry->i_size = p_access->info.i_size; p_entry->psz_path = strdup( p_access->psz_path ); if( p_entry->psz_path == NULL ) { free( p_entry ); goto error; } TAB_APPEND( p_sys->i_list, p_sys->list, p_entry ); msg_Dbg( p_access, "adding file `%s', (%"PRId64" bytes)", p_entry->psz_path, p_access->info.i_size ); while( psz_name && *psz_name ) { psz_parser = strchr( psz_name, ',' ); if( psz_parser ) *psz_parser = 0; psz_name = strdup( psz_name ); if( psz_name ) { access_t *p_tmp = access_New( p_access, p_access->psz_access, "", psz_name ); if( !p_tmp ) { psz_name = psz_parser; if( psz_name ) psz_name++; continue; } msg_Dbg( p_access, "adding file `%s', (%"PRId64" bytes)", psz_name, p_tmp->info.i_size ); p_entry = malloc( sizeof(access_entry_t) ); if( p_entry == NULL ) goto error; p_entry->i_size = p_tmp->info.i_size; p_entry->psz_path = psz_name; TAB_APPEND( p_sys->i_list, p_sys->list, p_entry ); access_Delete( p_tmp ); } psz_name = psz_parser; if( psz_name ) psz_name++; } } FREENULL( psz_list ); /* Peek */ p_sys->i_peek = 0; p_sys->p_peek = NULL; if( p_sys->method == Block ) { msg_Dbg( s, "Using AStream*Block" ); s->pf_read = AStreamReadBlock; s->pf_peek = AStreamPeekBlock; /* Init all fields of p_sys->block */ p_sys->block.i_start = p_sys->i_pos; p_sys->block.i_offset = 0; p_sys->block.p_current = NULL; p_sys->block.i_size = 0; p_sys->block.p_first = NULL; p_sys->block.pp_last = &p_sys->block.p_first; /* Do the prebuffering */ AStreamPrebufferBlock( s ); if( p_sys->block.i_size <= 0 ) { msg_Err( s, "cannot pre fill buffer" ); goto error; } } else if (p_sys->method == Immediate) { msg_Dbg( s, "Using AStream*Immediate" ); s->pf_read = AStreamReadImmediate; s->pf_peek = AStreamPeekImmediate; /* Allocate/Setup our tracks (useful to peek)*/ p_sys->immediate.i_end = 0; p_sys->immediate.p_buffer = malloc( STREAM_CACHE_SIZE );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -