vlc的stream是介于demux和access的中间层,demux通过它间接访问access。stream把access抽象成data stream以供demux访问,而不管具体的access是文件、网络还是设备。
我研究的是Method 1(STREAM_METHOD_BLOCK
),要搞清楚vlc的stream花了不少的时间,关键是要把它作为stream来看,允许read、peek和seek等,而不能简单的看成读写缓冲。思路上正确了,再搞清楚stream_sys_t几个标识stream的关键变量,再看代码就豁然开朗了,废话不多少,直接上代码:
struct stream_sys_t
{
access_t *p_access;
stream_read_method_t method; /* method to use */
uint64_t i_pos; /* Current reading offset */ //初始化的时候设置为access的值,后续ReadBlock/SeekBlock的时候被更新。
/* Method 1: pf_block */
struct
{
uint64_t i_start; /* Offset of block for p_first */ //初始化的时候设置为i_pos的值,后续RefillBlock回收内存的时候更新。
uint64_t i_offset; /* Offset for data in p_current */
block_t *p_current; /* Current block */ //初始化的时候设置为p_first的值,后续ReadBlock/SeekBlock的时候被更新。
uint64_t i_size; /* Total amount of data in the list */ //AReadBlock后增加,RefillBlock回收内存的时候减小。
block_t *p_first; //在第一次读数据/复位后被初始化为第一个读取的block,后面RefillBlock回收内存的时候被更新。
block_t **pp_last; //初始化的时候指向p_first指针,后面指向b->p_next指针。
} block;
stream_t *stream_AccessNew( access_t *p_access, char **ppsz_list )
{
.
if( p_sys->method == STREAM_METHOD_BLOCK )
{
msg_Dbg( s, "Using block method for AStream*" );
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; //注意,pp_last初始化指向p_first,后面通过修改pp_last指向的值间接修改p_first。
/* Do the prebuffering */
AStreamPrebufferBlock( s );
if( p_sys->block.i_size <= 0 )
{
msg_Err( s, "cannot pre fill buffer" );
goto error;
}
}
static void AStreamPrebufferBlock( stream_t *s )
{
.
/* Fetch a block */ //通过access读取数据
if( ( b = AReadBlock( s, &b_eof ) ) == NULL )
{
if( b_eof )
break;
continue;
}
while( b ) //把数据添加到block list
{
/* Append the block */
p_sys->block.i_size += b->i_buffer;
*p_sys->block.pp_last = b; //注意,添加第一个block的时候,通过修改pp_last指向的值,间接修改了p_first。
p_sys->block.pp_last = &b->p_next;
p_sys->stat.i_read_count++;
b = b->p_next;
}
if( i_first == 0 )
{
i_first = mdate();
msg_Dbg( s, "received first data after %d ms",
(int)((i_first-i_start)/1000) );
}
}
p_sys->block.p_current = p_sys->block.p_first; //设置p_current初始值为p_first(第一个读取的block)
}
static int AStreamReadBlock( stream_t *s, void *p_read, unsigned int i_read )
{
.
while( i_data < i_read ) //循环读取需要的字节(一个TS包)
{
int i_current =
p_sys->block.p_current->i_buffer - p_sys->block.i_offset; //当前buffer剩余数据多少
unsigned int i_copy = VLC_CLIP( (unsigned int)i_current, 0, i_read - i_data);
/* Copy data */
if( p_data )
{
memcpy( p_data,
&p_sys->block.p_current->p_buffer[p_sys->block.i_offset],
i_copy );
p_data += i_copy; //先拷贝当前buffer的数据
}
i_data += i_copy;
p_sys->block.i_offset += i_copy;
if( p_sys->block.i_offset >= p_sys->block.p_current->i_buffer ) //当前buffer的数据已经读取完毕
{
/* Current block is now empty, switch to next */
if( p_sys->block.p_current )
{
p_sys->block.i_offset = 0;
p_sys->block.p_current = p_sys->block.p_current->p_next; //切换到下一个buffer
}
/*Get a new block if needed */
if( !p_sys->block.p_current && AStreamRefillBlock( s ) ) //如果所有的buffer都空了,读取新的数据进buffer。
{
break;
}
}
}
p_sys->i_pos += i_data;
return i_data;
}
static int AStreamPeekBlock( stream_t *s, const uint8_t **pp_peek, unsigned int i_read )
{
.
/* We need to create a local copy */
if( p_sys->i_peek < i_read ) //目前需要读取的数据大小>之前创建的临时buffer的大小
{
p_sys->p_peek = realloc_or_free( p_sys->p_peek, i_read ); //扩大临时buffer的大小
if( !p_sys->p_peek )
{
p_sys->i_peek = 0;
return 0;
}
p_sys->i_peek = i_read;
}
/* Fill enough data */
while( p_sys->block.i_size - (p_sys->i_pos - p_sys->block.i_start)
< i_read ) //等待buffer有足够的数据供读取
{
block_t **pp_last = p_sys->block.pp_last;
if( AStreamRefillBlock( s ) ) break;
/* Our buffer are probably filled enough, don't try anymore */
if( pp_last == p_sys->block.pp_last ) break;
}
/* Copy what we have */
b = p_sys->block.p_current;
i_offset = p_sys->block.i_offset;
p_data = p_sys->p_peek;
while( b && i_data < i_read ) //拷贝数据到临时buffer
{
unsigned int i_current = __MAX(b->i_buffer - i_offset,0);
int i_copy = __MIN( i_current, i_read - i_data );
memcpy( p_data, &b->p_buffer[i_offset], i_copy );
i_data += i_copy;
p_data += i_copy;
i_offset += i_copy;
if( i_offset >= b->i_buffer )
{
i_offset = 0;
b = b->p_next;
}
}
*pp_peek = p_sys->p_peek;
return i_data;
}
static int AStreamSeekBlock( stream_t *s, uint64_t i_pos )
{
.
/* We already have thoses data, just update p_current/i_offset */
if( i_offset >= 0 && (uint64_t)i_offset < p_sys->block.i_size )
{
block_t *b = p_sys->block.p_first;
int i_current = 0;
while( i_current + b->i_buffer < (uint64_t)i_offset ) //查找i_pos对应的buffer
{
i_current += b->i_buffer;
b = b->p_next;
}
p_sys->block.p_current = b; //根据i_pos,设置p_current和i_offset
p_sys->block.i_offset = i_offset - i_current;
p_sys->i_pos = i_pos; //保存i_pos
return VLC_SUCCESS;
}
/* We may need to seek or to read data */
if( i_offset < 0 )
{
bool b_aseek;
access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
if( !b_aseek )
{
msg_Err( s, "backward seeking impossible (access not seekable)" );
return VLC_EGENERIC;
}
b_seek = true;
}
else //需要读取新的数据到buffer
{
bool b_aseek, b_aseekfast;
access_Control( p_access, ACCESS_CAN_SEEK, &b_aseek );
access_Control( p_access, ACCESS_CAN_FASTSEEK, &b_aseekfast );
if( !b_aseek )
{
b_seek = false;
msg_Warn( s, "%"PRId64" bytes need to be skipped "
"(access non seekable)",
i_offset - p_sys->block.i_size );
}
else
{
int64_t i_skip = i_offset - p_sys->block.i_size;
/* Avg bytes per packets */
int i_avg = p_sys->stat.i_bytes / p_sys->stat.i_read_count;
/* TODO compute a seek cost instead of fixed threshold */
int i_th = b_aseekfast ? 1 : 5;
if( i_skip <= i_th * i_avg &&
i_skip < STREAM_CACHE_SIZE )
b_seek = false;
else
b_seek = true;
msg_Dbg( s, "b_seek=%d th*avg=%d skip=%"PRId64,
b_seek, i_th*i_avg, i_skip );
}
}
static int AStreamRefillBlock( stream_t *s )
{
stream_sys_t *p_sys = s->p_sys;
block_t *b;
/* Release data */
while( p_sys->block.i_size >= STREAM_CACHE_SIZE &&
p_sys->block.p_first != p_sys->block.p_current ) //内存回收(数据已经被读出,或被跳过),并更新p_first、i_start、i_size
{
block_t *b = p_sys->block.p_first;
p_sys->block.i_start += b->i_buffer;
p_sys->block.i_size -= b->i_buffer;
p_sys->block.p_first = b->p_next;
block_Release( b );
}
if( p_sys->block.i_size >= STREAM_CACHE_SIZE &&
p_sys->block.p_current == p_sys->block.p_first &&
p_sys->block.p_current->p_next ) /* At least 2 packets */ //有足够的数据,且不需要内存回收,直接返回。
{
/* Enough data, don't read more */
return VLC_SUCCESS;
}
/* Now read a new block */
const int64_t i_start = mdate();
for( ;; )
{
bool b_eof;
if( !vlc_object_alive(s) )
return VLC_EGENERIC;
/* Fetch a block */
if( ( b = AReadBlock( s, &b_eof ) ) ) //通过access读取数据
break;
if( b_eof )
return VLC_EGENERIC;
}
p_sys->stat.i_read_time += mdate() - i_start;
while( b )
{
/* Append the block */ //添加到buffer list
p_sys->block.i_size += b->i_buffer;
*p_sys->block.pp_last = b;
p_sys->block.pp_last = &b->p_next;
/* Fix p_current */
if( p_sys->block.p_current == NULL )
p_sys->block.p_current = b;
/* Update stat */
p_sys->stat.i_bytes += b->i_buffer;
p_sys->stat.i_read_count++;
b = b->p_next;
}
return VLC_SUCCESS;
}
posted on 2015-11-11 16:25
lfc 阅读(442)
评论(0) 编辑 收藏 引用