📄 stream.c
字号:
/***************************************************************************** * stream.c ***************************************************************************** * Copyright (C) 1999-2004 VideoLAN * $Id: stream.c 10660 2005-04-12 18:15:33Z gbazin $ * * 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., 59 Temple Place - Suite 330, Boston, MA 02111, USA. *****************************************************************************/#include <stdlib.h>#include <vlc/vlc.h>#include <vlc/input.h>#include "input_internal.h"/* TODO: * - tune the 2 methods * - compute cost for seek * - improve stream mode seeking with closest segments * - ... *//* Two methods: * - using pf_block * One linked list of data read * - using pf_read * More complex scheme using mutliple track to avoid seeking *//* 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;struct stream_sys_t{ access_t *p_access; vlc_bool_t b_block; /* Block method (1) or stream */ int64_t i_pos; /* Current reading offset */ /* Method 1: pf_block */ struct { int64_t i_start; /* Offset of block for p_first */ int 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; /* Peek temporary buffer */ int i_peek; uint8_t *p_peek; /* Stat for both method */ struct { vlc_bool_t 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 ? */ vlc_bool_t b_quick;};/* Method 1: */static int AStreamReadBlock( stream_t *s, void *p_read, int i_read );static int AStreamPeekBlock( stream_t *s, uint8_t **p_peek, 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, vlc_bool_t *pb_eof );/* Method 2 */static int AStreamReadStream( stream_t *s, void *p_read, int i_read );static int AStreamPeekStream( stream_t *s, uint8_t **pp_peek, 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, int i_read );/* 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 );/**************************************************************************** * stream_UrlNew: create a stream from a access ****************************************************************************/stream_t *__stream_UrlNew( vlc_object_t *p_parent, const char *psz_url ){ char *psz_access, *psz_demux, *psz_path, *psz_dup; access_t *p_access; stream_t *p_res; if( !psz_url ) return 0; psz_dup = strdup( psz_url ); MRLSplit( p_parent, psz_dup, &psz_access, &psz_demux, &psz_path ); /* Now try a real access */ p_access = access2_New( p_parent, psz_access, psz_demux, psz_path, 0 ); free( psz_dup ); 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, VLC_TRUE ) ) ) { access2_Delete( p_access ); return NULL; } p_res->pf_destroy = UStreamDestroy; return p_res;}stream_t *stream_AccessNew( access_t *p_access, vlc_bool_t b_quick ){ stream_t *s = vlc_object_create( p_access, VLC_OBJECT_STREAM ); stream_sys_t *p_sys; char *psz_list; if( !s ) return NULL; /* Attach it now, needed for b_die */ vlc_object_attach( s, p_access ); s->pf_block = NULL; 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 ) ); /* Common field */ p_sys->p_access = p_access; p_sys->b_block = p_access->pf_block ? VLC_TRUE : VLC_FALSE; p_sys->i_pos = p_access->info.i_pos; /* Stats */ access2_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) ); 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 ); TAB_APPEND( p_sys->i_list, p_sys->list, p_entry ); msg_Dbg( p_access, "adding file `%s', ("I64Fd" 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 = access2_New( p_access, p_access->psz_access, 0, psz_name, 0 ); if( !p_tmp ) { psz_name = psz_parser; if( psz_name ) psz_name++; continue; } msg_Dbg( p_access, "adding file `%s', ("I64Fd" bytes)", psz_name, p_tmp->info.i_size ); p_entry = malloc( sizeof(access_entry_t) ); 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 ); access2_Delete( p_tmp ); } psz_name = psz_parser; if( psz_name ) psz_name++; } } if( psz_list ) free( psz_list ); /* Peek */ p_sys->i_peek = 0; p_sys->p_peek = NULL; if( p_sys->b_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 { int i; s->pf_read = AStreamReadStream; s->pf_peek = AStreamPeekStream; /* Allocate/Setup our tracks */ p_sys->stream.i_offset = 0; p_sys->stream.i_tk = 0; p_sys->stream.p_buffer = malloc( STREAM_CACHE_SIZE ); p_sys->stream.i_used = 0; access2_Control( p_access, ACCESS_GET_MTU, &p_sys->stream.i_read_size ); if( p_sys->stream.i_read_size <= 0 ) p_sys->stream.i_read_size = STREAM_READ_ATONCE; else if( p_sys->stream.i_read_size <= 256 ) p_sys->stream.i_read_size = 256; for( i = 0; i < STREAM_CACHE_TRACK; i++ ) { p_sys->stream.tk[i].i_date = 0; p_sys->stream.tk[i].i_start = p_sys->i_pos; p_sys->stream.tk[i].i_end = p_sys->i_pos; p_sys->stream.tk[i].p_buffer= &p_sys->stream.p_buffer[i * STREAM_CACHE_TRACK_SIZE]; } /* Do the prebuffering */ AStreamPrebufferStream( s ); if( p_sys->stream.tk[p_sys->stream.i_tk].i_end <= 0 ) { msg_Err( s, "cannot pre fill buffer" ); goto error; } } return s;error: if( p_sys->b_block ) { /* Nothing yet */ } else { free( p_sys->stream.p_buffer ); } free( s->p_sys ); vlc_object_detach( s ); vlc_object_destroy( s ); return NULL;}/**************************************************************************** * AStreamDestroy: ****************************************************************************/static void AStreamDestroy( stream_t *s ){ stream_sys_t *p_sys = s->p_sys; vlc_object_detach( s ); if( p_sys->b_block ) block_ChainRelease( p_sys->block.p_first ); else free( p_sys->stream.p_buffer ); if( p_sys->p_peek ) free( p_sys->p_peek ); if( p_sys->p_list_access && p_sys->p_list_access != p_sys->p_access ) access2_Delete( p_sys->p_list_access ); while( p_sys->i_list-- ) { free( p_sys->list[p_sys->i_list]->psz_path ); free( p_sys->list[p_sys->i_list] ); if( !p_sys->i_list ) free( p_sys->list ); } free( s->p_sys ); vlc_object_destroy( s );}static void UStreamDestroy( stream_t *s ){ access_t *p_access = (access_t*)vlc_object_find( s, VLC_OBJECT_ACCESS, FIND_PARENT ); AStreamDestroy( s ); vlc_object_release( p_access ); access2_Delete( p_access );}/**************************************************************************** * stream_AccessReset: ****************************************************************************/void stream_AccessReset( stream_t *s ){ stream_sys_t *p_sys = s->p_sys; p_sys->i_pos = p_sys->p_access->info.i_pos; if( p_sys->b_block ) { block_ChainRelease( p_sys->block.p_first ); /* 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 ); } else { int i; /* Setup our tracks */ p_sys->stream.i_offset = 0; p_sys->stream.i_tk = 0; p_sys->stream.i_used = 0; for( i = 0; i < STREAM_CACHE_TRACK; i++ ) { p_sys->stream.tk[i].i_date = 0; p_sys->stream.tk[i].i_start = p_sys->i_pos; p_sys->stream.tk[i].i_end = p_sys->i_pos; } /* Do the prebuffering */ AStreamPrebufferStream( s ); }}/**************************************************************************** * stream_AccessUpdate: ****************************************************************************/void stream_AccessUpdate( stream_t *s ){ stream_sys_t *p_sys = s->p_sys; p_sys->i_pos = p_sys->p_access->info.i_pos; if( p_sys->i_list ) { int i; for( i = 0; i < p_sys->i_list_index; i++ ) { p_sys->i_pos += p_sys->list[i]->i_size; } }}/**************************************************************************** * AStreamControl: ****************************************************************************/static int AStreamControl( stream_t *s, int i_query, va_list args ){ stream_sys_t *p_sys = s->p_sys; access_t *p_access = p_sys->p_access; vlc_bool_t *p_bool; int64_t *pi_64, i_64; int i_int; switch( i_query ) { case STREAM_GET_SIZE: pi_64 = (int64_t*)va_arg( args, int64_t * ); if( s->p_sys->i_list ) { int i; *pi_64 = 0; for( i = 0; i < s->p_sys->i_list; i++ ) *pi_64 += s->p_sys->list[i]->i_size;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -