Commit a1bf965d authored by Gildas Bazin's avatar Gildas Bazin

* modules/packetizer/mpeg4video.c: rewrite using the block helper facility....

* modules/packetizer/mpeg4video.c: rewrite using the block helper facility. Fixes a few issues with the previous packetizer and is simpler.
parent 31a2d53b
/*****************************************************************************
* mpeg4video.c: mpeg 4 video packetizer
*****************************************************************************
* Copyright (C) 2001, 2002, 2006 the VideoLAN team
* Copyright (C) 2001-2006 the VideoLAN team
* $Id$
*
* Authors: Laurent Aimar <fenrir@via.ecp.fr>
* Authors: Gildas Bazin <gbazin@videolan.org>
* Laurent Aimar <fenrir@via.ecp.fr>
* Eric Petit <titer@videolan.org>
* Gildas Bazin <gbazin@videolan.org>
*
* 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
......@@ -34,6 +34,7 @@
#include <vlc/input.h> /* hmmm, just for INPUT_RATE_DEFAULT */
#include "vlc_bits.h"
#include "vlc_block_helper.h"
/*****************************************************************************
* Module descriptor
......@@ -49,7 +50,6 @@ vlc_module_begin();
set_callbacks( Open, Close );
vlc_module_end();
/****************************************************************************
* Local prototypes
****************************************************************************/
......@@ -57,6 +57,14 @@ static block_t *Packetize( decoder_t *, block_t ** );
struct decoder_sys_t
{
/*
* Input properties
*/
block_bytestream_t bytestream;
int i_state;
int i_offset;
uint8_t p_startcode[3];
/*
* Common properties
*/
......@@ -68,10 +76,6 @@ struct decoder_sys_t
mtime_t i_last_time;
mtime_t i_last_timeincr;
vlc_bool_t b_vop;
int i_buffer;
int i_buffer_size;
uint8_t *p_buffer;
unsigned int i_flags;
int i_fps_num;
......@@ -80,10 +84,20 @@ struct decoder_sys_t
int i_last_incr_diff;
vlc_bool_t b_frame;
/* Current frame being built */
block_t *p_frame;
block_t **pp_last;
};
static int m4v_FindStartCode( uint8_t **, uint8_t * );
static int m4v_VOLParse( decoder_t *, es_format_t *, uint8_t *, int );
enum {
STATE_NOSYNC,
STATE_NEXT_SYNC
};
static block_t *ParseMPEGBlock( decoder_t *, block_t * );
static int ParseVOL( decoder_t *, es_format_t *, uint8_t *, int );
static int ParseVOP( decoder_t *, block_t * );
static int vlc_log2( unsigned int );
#define VIDEO_OBJECT_MASK 0x01f
......@@ -147,6 +161,16 @@ static int Open( vlc_object_t *p_this )
}
memset( p_sys, 0, sizeof(decoder_sys_t) );
/* Misc init */
p_sys->i_state = STATE_NOSYNC;
p_sys->bytestream = block_BytestreamInit( p_dec );
p_sys->p_startcode[0] = 0;
p_sys->p_startcode[1] = 0;
p_sys->p_startcode[2] = 1;
p_sys->i_offset = 0;
p_sys->p_frame = NULL;
p_sys->pp_last = &p_sys->p_frame;
/* Setup properties */
es_format_Copy( &p_dec->fmt_out, &p_dec->fmt_in );
p_dec->fmt_out.i_codec = VLC_FOURCC( 'm', 'p', '4', 'v' );
......@@ -160,8 +184,8 @@ static int Open( vlc_object_t *p_this )
p_dec->fmt_in.i_extra );
msg_Dbg( p_dec, "opening with vol size: %d", p_dec->fmt_in.i_extra );
m4v_VOLParse( p_dec, &p_dec->fmt_out,
p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra );
ParseVOL( p_dec, &p_dec->fmt_out,
p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra );
}
else
{
......@@ -183,7 +207,8 @@ static void Close( vlc_object_t *p_this )
{
decoder_t *p_dec = (decoder_t*)p_this;
if( p_dec->p_sys->p_buffer ) free( p_dec->p_sys->p_buffer );
block_BytestreamRelease( &p_dec->p_sys->bytestream );
if( p_dec->p_sys->p_frame ) block_ChainRelease( p_dec->p_sys->p_frame );
free( p_dec->p_sys );
}
......@@ -193,307 +218,191 @@ static void Close( vlc_object_t *p_this )
static block_t *Packetize( decoder_t *p_dec, block_t **pp_block )
{
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_pic;
mtime_t i_pts, i_dts;
block_t *p_chain_out = NULL;
block_t *p_block;
uint8_t *p_vol = NULL;
uint8_t *p_start;
if( !pp_block || !*pp_block ) return NULL;
p_block = *pp_block;
if( pp_block == NULL || *pp_block == NULL ) return NULL;
/* Append data */
if( p_sys->i_buffer + p_block->i_buffer > p_sys->i_buffer_size )
if( (*pp_block)->i_flags & BLOCK_FLAG_DISCONTINUITY )
{
p_sys->i_buffer_size += p_block->i_buffer + 1024;
p_sys->p_buffer = realloc( p_sys->p_buffer, p_sys->i_buffer_size );
p_sys->i_state = STATE_NOSYNC;
if( p_sys->p_frame ) block_ChainRelease( p_sys->p_frame );
p_sys->p_frame = NULL;
p_sys->pp_last = &p_sys->p_frame;
block_Release( *pp_block );
return NULL;
}
memcpy( &p_sys->p_buffer[p_sys->i_buffer], p_block->p_buffer,
p_block->i_buffer );
p_sys->i_buffer += p_block->i_buffer;
if( p_sys->i_buffer > 10*1000000 )
{
msg_Warn( p_dec, "reseting context" );
p_sys->i_buffer = 0;
}
block_BytestreamPush( &p_sys->bytestream, *pp_block );
/* Search vop */
p_start = &p_sys->p_buffer[p_sys->i_buffer - p_block->i_buffer - 4];
if( p_start < p_sys->p_buffer )
{
p_start = p_sys->p_buffer;
}
for( ;; )
while( 1 )
{
if( m4v_FindStartCode( &p_start, &p_sys->p_buffer[p_sys->i_buffer] ) )
switch( p_sys->i_state )
{
block_Release( p_block );
*pp_block = NULL;
return p_chain_out;
}
/* fprintf( stderr, "start code=0x1%2.2x\n", p_start[3] ); */
if( p_vol )
{
if( !p_dec->fmt_out.i_extra )
case STATE_NOSYNC:
if( block_FindStartcodeFromOffset( &p_sys->bytestream,
&p_sys->i_offset, p_sys->p_startcode, 3 ) == VLC_SUCCESS )
{
/* Copy the complete VOL */
p_dec->fmt_out.i_extra = p_start - p_vol;
p_dec->fmt_out.p_extra =
realloc( p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra );
memcpy( p_dec->fmt_out.p_extra, p_vol,
p_dec->fmt_out.i_extra );
m4v_VOLParse( p_dec, &p_dec->fmt_out,
p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra );
p_sys->i_state = STATE_NEXT_SYNC;
}
/* Remove VOL from the original stream */
memmove( p_vol, p_start,
p_sys->i_buffer - (p_start - p_sys->p_buffer) );
p_sys->i_buffer -= p_dec->fmt_out.i_extra;
p_start = p_vol;
p_vol = NULL;
}
if( p_sys->b_vop )
{
/* Output the complete VOP we have */
int i_out = p_start - p_sys->p_buffer;
block_t *p_out = block_New( p_dec, i_out );
/* extract data */
memcpy( p_out->p_buffer, p_sys->p_buffer, i_out );
if( i_out < p_sys->i_buffer )
if( p_sys->i_offset )
{
memmove( p_sys->p_buffer, &p_sys->p_buffer[i_out],
p_sys->i_buffer - i_out );
block_SkipBytes( &p_sys->bytestream, p_sys->i_offset );
p_sys->i_offset = 0;
block_BytestreamFlush( &p_sys->bytestream );
}
p_sys->i_buffer -= i_out;
p_start -= i_out;
p_out->i_flags = p_sys->i_flags;
p_out->i_pts = p_sys->i_interpolated_pts;
p_out->i_dts = p_sys->i_interpolated_dts;
/* FIXME doesn't work when there is multiple VOP in one block */
if( p_block->i_dts > p_sys->i_interpolated_dts )
if( p_sys->i_state != STATE_NEXT_SYNC )
{
p_out->i_length = p_block->i_dts - p_sys->i_interpolated_dts;
/* Need more data */
return NULL;
}
if( p_dec->fmt_out.i_extra > 0 )
{
block_ChainAppend( &p_chain_out, p_out );
}
else
{
msg_Warn( p_dec, "waiting for VOL" );
block_Release( p_out );
}
p_sys->i_offset = 1; /* To find next startcode */
p_sys->b_vop = VLC_FALSE;
}
case STATE_NEXT_SYNC:
/* TODO: If p_block == NULL, flush the buffer without checking the
* next sync word */
if( p_start[3] >= 0x20 && p_start[3] <= 0x2f )
{
/* Start of the VOL */
p_vol = p_start;
}
else if( p_start[3] == 0xb3 )
{
/* GOP header */
}
else if( p_start[3] == 0xb6 )
{
/* Parse the VOP */
bs_t s;
int i_modulo_time_base = 0;
int i_time_increment_bits;
int64_t i_time_increment, i_time_ref;
/* FIXME: we don't actually check we received enough data to read
* the VOP time increment. */
bs_init( &s, &p_start[4],
p_sys->i_buffer - (p_start - p_sys->p_buffer) - 4 );
switch( bs_read( &s, 2 ) )
/* Find the next startcode */
if( block_FindStartcodeFromOffset( &p_sys->bytestream,
&p_sys->i_offset, p_sys->p_startcode, 3 ) != VLC_SUCCESS )
{
case 0:
p_sys->i_flags = BLOCK_FLAG_TYPE_I;
break;
case 1:
p_sys->i_flags = BLOCK_FLAG_TYPE_P;
break;
case 2:
p_sys->i_flags = BLOCK_FLAG_TYPE_B;
p_sys->b_frame = VLC_TRUE;
break;
case 3: /* gni ? */
p_sys->i_flags = BLOCK_FLAG_TYPE_PB;
break;
/* Need more data */
return NULL;
}
while( bs_read( &s, 1 ) ) i_modulo_time_base++;
if( !bs_read1( &s ) ) continue; /* Marker */
/* Get the new fragment and set the pts/dts */
p_pic = block_New( p_dec, p_sys->i_offset );
block_BytestreamFlush( &p_sys->bytestream );
p_pic->i_pts = i_pts = p_sys->bytestream.p_block->i_pts;
p_pic->i_dts = i_dts = p_sys->bytestream.p_block->i_dts;
p_pic->i_rate = p_sys->bytestream.p_block->i_rate;
block_GetBytes( &p_sys->bytestream, p_pic->p_buffer,
p_pic->i_buffer );
/* VOP time increment */
i_time_increment_bits = vlc_log2(p_dec->p_sys->i_fps_num - 1) + 1;
if( i_time_increment_bits < 1 ) i_time_increment_bits = 1;
i_time_increment = bs_read( &s, i_time_increment_bits );
p_sys->i_offset = 0;
/* Interpolate PTS/DTS */
if( !(p_sys->i_flags & BLOCK_FLAG_TYPE_B) )
/* Get picture if any */
if( !( p_pic = ParseMPEGBlock( p_dec, p_pic ) ) )
{
p_sys->i_last_time_ref = p_sys->i_time_ref;
p_sys->i_time_ref +=
(i_modulo_time_base * p_dec->p_sys->i_fps_num);
i_time_ref = p_sys->i_time_ref;
p_sys->i_state = STATE_NOSYNC;
break;
}
else
/* don't reuse the same timestamps several times */
if( i_pts == p_sys->bytestream.p_block->i_pts &&
i_dts == p_sys->bytestream.p_block->i_dts )
{
i_time_ref = p_sys->i_last_time_ref +
(i_modulo_time_base * p_dec->p_sys->i_fps_num);
p_sys->bytestream.p_block->i_pts = 0;
p_sys->bytestream.p_block->i_dts = 0;
}
if( p_dec->p_sys->i_fps_num < 5 && /* Work-around buggy streams */
p_dec->fmt_in.video.i_frame_rate > 0 &&
p_dec->fmt_in.video.i_frame_rate_base > 0 )
/* We've just started the stream, wait for the first PTS.
* We discard here so we can still get the sequence header. */
if( p_sys->i_interpolated_pts <= 0 &&
p_sys->i_interpolated_dts <= 0 )
{
p_sys->i_interpolated_pts += I64C(1000000) *
p_dec->fmt_in.video.i_frame_rate_base *
p_block->i_rate / INPUT_RATE_DEFAULT /
p_dec->fmt_in.video.i_frame_rate;
msg_Dbg( p_dec, "need a starting pts/dts" );
p_sys->i_state = STATE_NOSYNC;
block_Release( p_pic );
break;
}
else if( p_dec->p_sys->i_fps_num )
p_sys->i_interpolated_pts +=
( I64C(1000000) * (i_time_ref + i_time_increment -
p_sys->i_last_time - p_sys->i_last_timeincr) *
p_block->i_rate / INPUT_RATE_DEFAULT /
p_dec->p_sys->i_fps_num );
p_sys->i_last_time = i_time_ref;
p_sys->i_last_timeincr = i_time_increment;
/* Correct interpolated dts when we receive a new pts/dts */
if( p_block->i_pts > 0 )
p_sys->i_interpolated_pts = p_block->i_pts;
if( p_block->i_dts > 0 )
p_sys->i_interpolated_dts = p_block->i_dts;
if( (p_sys->i_flags & BLOCK_FLAG_TYPE_B) || !p_sys->b_frame )
{
/* Trivial case (DTS == PTS) */
p_sys->i_interpolated_dts = p_sys->i_interpolated_pts;
if( p_block->i_pts > 0 )
p_sys->i_interpolated_dts = p_block->i_pts;
if( p_block->i_dts > 0 )
p_sys->i_interpolated_dts = p_block->i_dts;
p_sys->i_interpolated_pts = p_sys->i_interpolated_dts;
}
else
{
if( p_sys->i_last_ref_pts > 0 )
p_sys->i_interpolated_dts = p_sys->i_last_ref_pts;
/* When starting the stream we can have the first frame with
* a null DTS (i_interpolated_pts is initialized to 0) */
if( !p_pic->i_dts ) p_pic->i_dts = p_pic->i_pts;
p_sys->i_last_ref_pts = p_sys->i_interpolated_pts;
}
/* So p_block doesn't get re-added several times */
*pp_block = block_BytestreamPop( &p_sys->bytestream );
p_sys->b_vop = VLC_TRUE;
p_sys->i_state = STATE_NOSYNC;
/* Don't re-use the same PTS/DTS twice */
p_block->i_pts = p_block->i_dts = 0;
return p_pic;
}
p_start += 4; /* Next */
}
}
/****************************************************************************
* m4v_FindStartCode
****************************************************************************/
static int m4v_FindStartCode( uint8_t **pp_start, uint8_t *p_end )
/*****************************************************************************
* ParseMPEGBlock: Re-assemble fragments into a block containing a picture
*****************************************************************************/
static block_t *ParseMPEGBlock( decoder_t *p_dec, block_t *p_frag )
{
uint8_t *p = *pp_start;
decoder_sys_t *p_sys = p_dec->p_sys;
block_t *p_pic = NULL;
/* We wait for 4+1 bytes */
for( p = *pp_start; p < p_end - 5; p++ )
if( p_frag->p_buffer[3] == 0xB0 || p_frag->p_buffer[3] == 0xB1 )
{
if( p[0] == 0 && p[1] == 0 && p[2] == 1 )
{
*pp_start = p;
return VLC_SUCCESS;
}
/* Remove VOS start/end code from the original stream */
block_Release( p_frag );
return NULL;
}
*pp_start = p_end;
return VLC_EGENERIC;
}
/* look at ffmpeg av_log2 ;) */
static int vlc_log2( unsigned int v )
{
int n = 0;
static const int vlc_log2_table[16] =
if( p_frag->p_buffer[3] >= 0x20 && p_frag->p_buffer[3] <= 0x2f )
{
0,0,1,1,2,2,2,2, 3,3,3,3,3,3,3,3
};
if( v&0xffff0000 )
{
v >>= 16;
n += 16;
/* Copy the complete VOL */
p_dec->fmt_out.i_extra = p_frag->i_buffer;
p_dec->fmt_out.p_extra =
realloc( p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra );
memcpy( p_dec->fmt_out.p_extra, p_frag->p_buffer, p_frag->i_buffer );
ParseVOL( p_dec, &p_dec->fmt_out,
p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra );
/* Remove from the original stream */
block_Release( p_frag );
return NULL;
}
if( v&0xff00 )
else
{
v >>= 8;
n += 8;
if( !p_dec->fmt_out.i_extra )
{
msg_Warn( p_dec, "waiting for VOL" );
block_Release( p_frag );
return NULL;
}
/* Append the block */
block_ChainLastAppend( &p_sys->pp_last, p_frag );
}
if( v&0xf0 )
if( p_frag->p_buffer[3] == 0xb6 &&
ParseVOP( p_dec, p_frag ) == VLC_SUCCESS )
{
v >>= 4;
n += 4;
/* We are dealing with a VOP */
p_pic = block_ChainGather( p_sys->p_frame );
p_pic->i_pts = p_sys->i_interpolated_pts;
p_pic->i_dts = p_sys->i_interpolated_dts;
/* Reset context */
p_sys->p_frame = NULL;
p_sys->pp_last = &p_sys->p_frame;
}
n += vlc_log2_table[v];
return n;
return p_pic;
}
/* m4v_VOLParse:
/* ParseVOL:
* TODO:
* - support aspect ratio
*/
static int m4v_VOLParse( decoder_t *p_dec, es_format_t *fmt,
uint8_t *p_vol, int i_vol )
static int ParseVOL( decoder_t *p_dec, es_format_t *fmt,
uint8_t *p_vol, int i_vol )
{
decoder_sys_t *p_sys = p_dec->p_sys;
int i_vo_type, i_vo_ver_id, i_ar, i_shape;
bs_t s;
int i_vo_type;
int i_vo_ver_id;
int i_ar;
int i_shape;
for( ;; )
{
if( p_vol[0] == 0x00 && p_vol[1] == 0x00 &&
p_vol[2] == 0x01 &&
p_vol[3] >= 0x20 && p_vol[3] <= 0x2f )
{
break;
}
p_vol++;
i_vol--;
if( i_vol <= 4 )
{
return VLC_EGENERIC;
}
if( p_vol[0] == 0x00 && p_vol[1] == 0x00 && p_vol[2] == 0x01 &&
p_vol[3] >= 0x20 && p_vol[3] <= 0x2f ) break;
p_vol++; i_vol--;
if( i_vol <= 4 ) return VLC_EGENERIC;
}
/* parse the vol */
bs_init( &s, &p_vol[4], i_vol - 4 );
bs_skip( &s, 1 ); /* random access */
......@@ -544,19 +453,18 @@ static int m4v_VOLParse( decoder_t *p_dec, es_format_t *fmt,
if( !bs_read1( &s ) ) return VLC_EGENERIC; /* Marker */
p_dec->p_sys->i_fps_num = bs_read( &s, 16 ); /* Time increment resolution*/
if( !p_dec->p_sys->i_fps_num ) p_dec->p_sys->i_fps_num = 1;
p_sys->i_fps_num = bs_read( &s, 16 ); /* Time increment resolution*/
if( !p_sys->i_fps_num ) p_sys->i_fps_num = 1;
if( !bs_read1( &s ) ) return VLC_EGENERIC; /* Marker */
if( bs_read1( &s ) )
{
int i_time_increment_bits =
vlc_log2( p_dec->p_sys->i_fps_num - 1 ) + 1;
int i_time_increment_bits = vlc_log2( p_sys->i_fps_num - 1 ) + 1;
if( i_time_increment_bits < 1 ) i_time_increment_bits = 1;
p_dec->p_sys->i_fps_den = bs_read( &s, i_time_increment_bits );
p_sys->i_fps_den = bs_read( &s, i_time_increment_bits );
}
if( i_shape == 0 )
{
......@@ -566,5 +474,138 @@ static int m4v_VOLParse( decoder_t *p_dec, es_format_t *fmt,
fmt->video.i_height= bs_read( &s, 13 );
bs_skip( &s, 1 );
}
return VLC_SUCCESS;
}
static int ParseVOP( decoder_t *p_dec, block_t *p_vop )
{
decoder_sys_t *p_sys = p_dec->p_sys;
int64_t i_time_increment, i_time_ref;
int i_modulo_time_base = 0, i_time_increment_bits;
bs_t s;
bs_init( &s, &p_vop->p_buffer[4], p_vop->i_buffer - 4 );
switch( bs_read( &s, 2 ) )
{
case 0:
p_sys->i_flags = BLOCK_FLAG_TYPE_I;
break;
case 1:
p_sys->i_flags = BLOCK_FLAG_TYPE_P;
break;
case 2:
p_sys->i_flags = BLOCK_FLAG_TYPE_B;
p_sys->b_frame = VLC_TRUE;
break;
case 3: /* gni ? */
p_sys->i_flags = BLOCK_FLAG_TYPE_PB;
break;
}
while( bs_read( &s, 1 ) ) i_modulo_time_base++;
if( !bs_read1( &s ) ) return VLC_EGENERIC; /* Marker */
/* VOP time increment */
i_time_increment_bits = vlc_log2(p_dec->p_sys->i_fps_num - 1) + 1;
if( i_time_increment_bits < 1 ) i_time_increment_bits = 1;
i_time_increment = bs_read( &s, i_time_increment_bits );
/* Interpolate PTS/DTS */
if( !(p_sys->i_flags & BLOCK_FLAG_TYPE_B) )
{
p_sys->i_last_time_ref = p_sys->i_time_ref;
p_sys->i_time_ref +=
(i_modulo_time_base * p_dec->p_sys->i_fps_num);
i_time_ref = p_sys->i_time_ref;
}
else
{
i_time_ref = p_sys->i_last_time_ref +
(i_modulo_time_base * p_dec->p_sys->i_fps_num);
}
#if 0
msg_Err( p_dec, "interp pts/dts (%lli,%lli), pts/dts (%lli,%lli)",
p_sys->i_interpolated_pts, p_sys->i_interpolated_dts,
p_vop->i_pts, p_vop->i_dts );
#endif
if( p_dec->p_sys->i_fps_num < 5 && /* Work-around buggy streams */
p_dec->fmt_in.video.i_frame_rate > 0 &&
p_dec->fmt_in.video.i_frame_rate_base > 0 )
{
p_sys->i_interpolated_pts += I64C(1000000) *
p_dec->fmt_in.video.i_frame_rate_base *
p_vop->i_rate / INPUT_RATE_DEFAULT /
p_dec->fmt_in.video.i_frame_rate;
}
else if( p_dec->p_sys->i_fps_num )
p_sys->i_interpolated_pts +=
( I64C(1000000) * (i_time_ref + i_time_increment -
p_sys->i_last_time - p_sys->i_last_timeincr) *
p_vop->i_rate / INPUT_RATE_DEFAULT /
p_dec->p_sys->i_fps_num );
p_sys->i_last_time = i_time_ref;
p_sys->i_last_timeincr = i_time_increment;
/* Correct interpolated dts when we receive a new pts/dts */
if( p_vop->i_pts > 0 )
p_sys->i_interpolated_pts = p_vop->i_pts;
if( p_vop->i_dts > 0 )
p_sys->i_interpolated_dts = p_vop->i_dts;
if( (p_sys->i_flags & BLOCK_FLAG_TYPE_B) || !p_sys->b_frame )
{
/* Trivial case (DTS == PTS) */
p_sys->i_interpolated_dts = p_sys->i_interpolated_pts;
if( p_vop->i_pts > 0 )
p_sys->i_interpolated_dts = p_vop->i_pts;
if( p_vop->i_dts > 0 )
p_sys->i_interpolated_dts = p_vop->i_dts;
p_sys->i_interpolated_pts = p_sys->i_interpolated_dts;
}
else
{
if( p_sys->i_last_ref_pts > 0 )
p_sys->i_interpolated_dts = p_sys->i_last_ref_pts;
p_sys->i_last_ref_pts = p_sys->i_interpolated_pts;
}
return VLC_SUCCESS;
}
/* look at ffmpeg av_log2 ;) */
static int vlc_log2( unsigned int v )
{
int n = 0;
static const int vlc_log2_table[16] =
{
0,0,1,1,2,2,2,2, 3,3,3,3,3,3,3,3
};
if( v&0xffff0000 )
{
v >>= 16;
n += 16;
}
if( v&0xff00 )
{
v >>= 8;
n += 8;
}
if( v&0xf0 )
{
v >>= 4;
n += 4;
}
n += vlc_log2_table[v];
return n;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment