From: Laurent Aimar Date: Tue, 18 Nov 2003 20:15:38 +0000 (+0000) Subject: * include/vlc_bits.h: bit stream reader/writer. X-Git-Tag: 0.7.0~484 X-Git-Url: https://git.sesse.net/?a=commitdiff_plain;h=355794923679b416110e848bbc875a4a546980f2;p=vlc * include/vlc_bits.h: bit stream reader/writer. * mpeg4video.c: rework of the mpeg4 video packetizer. (not well tested) --- diff --git a/Makefile.am b/Makefile.am index 166cfa5353..e1d04cf96f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -90,6 +90,7 @@ HEADERS_include = \ include/variables.h \ include/video_output.h \ include/vlc_block.h \ + include/vlc_bits.h \ include/vlc_block_helper.h \ include/vlc_codec.h \ include/vlc_common.h \ diff --git a/include/vlc_bits.h b/include/vlc_bits.h new file mode 100644 index 0000000000..87716e0fb0 --- /dev/null +++ b/include/vlc_bits.h @@ -0,0 +1,185 @@ +/***************************************************************************** + * bits.h : + ***************************************************************************** + * Copyright (C) 2003 VideoLAN + * $Id: vlc_bits.h,v 1.1 2003/11/18 20:15:38 fenrir Exp $ + * + * Authors: Laurent Aimar + * + * 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. + *****************************************************************************/ + +#ifndef _VLC_BITS_H +#define _VLC_BITS_H 1 + +typedef struct bs_s +{ + uint8_t *p_start; + uint8_t *p; + uint8_t *p_end; + + int i_left; /* i_count number of available bits */ +} bs_t; + +static inline void bs_init( bs_t *s, void *p_data, int i_data ) +{ + s->p_start = p_data; + s->p = p_data; + s->p_end = s->p + i_data; + s->i_left = 8; +} +static inline int bs_pos( bs_t *s ) +{ + return( 8 * ( s->p - s->p_start ) + 8 - s->i_left ); +} +static inline int bs_eof( bs_t *s ) +{ + return( s->p >= s->p_end ? 1: 0 ); +} +static inline uint32_t bs_read( bs_t *s, int i_count ) +{ + static uint32_t i_mask[33] = + { 0x00, + 0x01, 0x03, 0x07, 0x0f, + 0x1f, 0x3f, 0x7f, 0xff, + 0x1ff, 0x3ff, 0x7ff, 0xfff, + 0x1fff, 0x3fff, 0x7fff, 0xffff, + 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff, + 0x1fffff, 0x3fffff, 0x7fffff, 0xffffff, + 0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff, + 0x1fffffff,0x3fffffff,0x7fffffff,0xffffffff}; + int i_shr; + uint32_t i_result = 0; + + while( i_count > 0 ) + { + if( s->p >= s->p_end ) + { + break; + } + + if( ( i_shr = s->i_left - i_count ) >= 0 ) + { + /* more in the buffer than requested */ + i_result |= ( *s->p >> i_shr )&i_mask[i_count]; + s->i_left -= i_count; + if( s->i_left == 0 ) + { + s->p++; + s->i_left = 8; + } + return( i_result ); + } + else + { + /* less in the buffer than requested */ + i_result |= (*s->p&i_mask[s->i_left]) << -i_shr; + i_count -= s->i_left; + s->p++; + s->i_left = 8; + } + } + + return( i_result ); +} + +static inline uint32_t bs_read1( bs_t *s ) +{ + if( s->p < s->p_end ) + { + unsigned int i_result; + + s->i_left--; + i_result = ( *s->p >> s->i_left )&0x01; + if( s->i_left == 0 ) + { + s->p++; + s->i_left = 8; + } + return i_result; + } + + return 0; +} + +static inline uint32_t bs_show( bs_t *s, int i_count ) +{ + bs_t s_tmp = *s; + return bs_read( &s_tmp, i_count ); +} + +static inline void bs_skip( bs_t *s, int i_count ) +{ + s->i_left -= i_count; + + while( s->i_left <= 0 ) + { + s->p++; + s->i_left += 8; + } +} + +static inline void bs_write( bs_t *s, int i_count, uint32_t i_bits ) +{ + while( i_count > 0 ) + { + if( s->p >= s->p_end ) + { + break; + } + + i_count--; + + if( ( i_bits >> i_count )&0x01 ) + { + *s->p |= 1 << ( s->i_left - 1 ); + } + else + { + *s->p &= ~( 1 << ( s->i_left - 1 ) ); + } + s->i_left--; + if( s->i_left == 0 ) + { + s->p++; + s->i_left = 8; + } + } +} + +static inline void bs_align( bs_t *s ) +{ + if( s->i_left != 8 ) + { + s->i_left = 8; + s->p++; + } +} +static inline void bs_align_0( bs_t *s ) +{ + if( s->i_left != 8 ) + { + bs_write( s, s->i_left, 0 ); + } +} +static inline void bs_align_1( bs_t *s ) +{ + while( s->i_left != 8 ) + { + bs_write( s, 1, 1 ); + } +} + +#endif diff --git a/modules/packetizer/mpeg4video.c b/modules/packetizer/mpeg4video.c index 28b9d8620c..e2eda071bd 100644 --- a/modules/packetizer/mpeg4video.c +++ b/modules/packetizer/mpeg4video.c @@ -2,7 +2,7 @@ * mpeg4video.c: mpeg 4 video packetizer ***************************************************************************** * Copyright (C) 2001, 2002 VideoLAN - * $Id: mpeg4video.c,v 1.15 2003/11/17 18:48:08 gbazin Exp $ + * $Id: mpeg4video.c,v 1.16 2003/11/18 20:15:38 fenrir Exp $ * * Authors: Laurent Aimar * Eric Petit @@ -26,36 +26,49 @@ /***************************************************************************** * Preamble *****************************************************************************/ +#include /* malloc(), free() */ + #include #include -#include #include -#include /* malloc(), free() */ -#include /* strdup() */ - -#include "codecs.h" +#include "vlc_bits.h" /***************************************************************************** - * decoder_sys_t : decoder descriptor + * Module descriptor *****************************************************************************/ +static int Open ( vlc_object_t * ); +static void Close( vlc_object_t * ); + +vlc_module_begin(); + set_description( _("MPEG4 Video packetizer") ); + set_capability( "packetizer", 50 ); + set_callbacks( Open, Close ); +vlc_module_end(); + + +/**************************************************************************** + * Local prototypes + ****************************************************************************/ +static block_t *Packetize( decoder_t *, block_t ** ); + struct decoder_sys_t { /* * Common properties */ mtime_t i_pts; -}; + mtime_t i_dts; -/**************************************************************************** - * Local prototypes - ****************************************************************************/ -static int OpenPacketizer ( vlc_object_t * ); -static void ClosePacketizer( vlc_object_t * ); -static block_t *PacketizeBlock( decoder_t *, block_t ** ); + vlc_bool_t b_vop; + int i_buffer; + int i_buffer_size; + uint8_t *p_buffer; +}; -static int m4v_FindVol( decoder_t *p_dec, block_t *p_block ); +static int m4v_FindStartCode( uint8_t **pp_start, uint8_t *p_end ); +static int m4v_VOLParse( es_format_t *fmt, uint8_t *p_vol, int i_vol ); #define VIDEO_OBJECT_MASK 0x01f #define VIDEO_OBJECT_LAYER_MASK 0x00f @@ -78,23 +91,14 @@ static int m4v_FindVol( decoder_t *p_dec, block_t *p_block ); #define TEXTURE_SNR_LAYER_START_CODE 0x1c0 /***************************************************************************** - * Module descriptor - *****************************************************************************/ -vlc_module_begin(); - set_description( _("MPEG4 Video packetizer") ); - set_capability( "packetizer", 50 ); - set_callbacks( OpenPacketizer, ClosePacketizer ); -vlc_module_end(); - -/***************************************************************************** - * OpenPacketizer: probe the packetizer and return score + * Open: probe the packetizer and return score *****************************************************************************/ -static int OpenPacketizer( vlc_object_t *p_this ) +static int Open( vlc_object_t *p_this ) { - decoder_t *p_dec = (decoder_t*)p_this; + decoder_t *p_dec = (decoder_t*)p_this; decoder_sys_t *p_sys; - switch( p_dec->p_fifo->i_fourcc ) + switch( p_dec->fmt_in.i_codec ) { case VLC_FOURCC( 'm', '4', 's', '2'): case VLC_FOURCC( 'M', '4', 'S', '2'): @@ -116,12 +120,16 @@ static int OpenPacketizer( vlc_object_t *p_this ) } /* Allocate the memory needed to store the decoder's structure */ - if( ( p_dec->p_sys = p_sys = - (decoder_sys_t *)malloc(sizeof(decoder_sys_t)) ) == NULL ) + if( ( p_dec->p_sys = p_sys = malloc( sizeof(decoder_sys_t) ) ) == NULL ) { msg_Err( p_dec, "out of memory" ); return VLC_EGENERIC; } + p_sys->i_pts = 0; + p_sys->b_vop = VLC_FALSE; + p_sys->i_buffer = 0; + p_sys->i_buffer_size = 10000; + p_sys->p_buffer = malloc( p_sys->i_buffer_size ); /* Setup properties */ p_dec->fmt_out = p_dec->fmt_in; @@ -136,6 +144,8 @@ static int OpenPacketizer( 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->fmt_out, + p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra ); } else { @@ -145,135 +155,292 @@ static int OpenPacketizer( vlc_object_t *p_this ) } /* Set callback */ - p_dec->pf_packetize = PacketizeBlock; + p_dec->pf_packetize = Packetize; return VLC_SUCCESS; } +/***************************************************************************** + * Close: clean up the packetizer + *****************************************************************************/ +static void Close( vlc_object_t *p_this ) +{ + decoder_t *p_dec = (decoder_t*)p_this; + + free( p_dec->p_sys ); +} + /**************************************************************************** - * PacketizeBlock: the whole thing + * Packetize: the whole thing ****************************************************************************/ -static block_t *PacketizeBlock( decoder_t *p_dec, block_t **pp_block ) +static block_t *Packetize( decoder_t *p_dec, block_t **pp_block ) { + decoder_sys_t *p_sys = p_dec->p_sys; + + 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( !p_dec->fmt_out.i_extra ) + /* Append data */ + if( p_sys->i_buffer + p_block->i_buffer > p_sys->i_buffer_size ) { - m4v_FindVol( p_dec, p_block ); + p_sys->i_buffer_size += p_block->i_buffer + 1024; + p_sys->p_buffer = realloc( p_sys->p_buffer, p_sys->i_buffer_size ); } + 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; - /* Drop blocks until we have a VOL */ - if( !p_dec->fmt_out.i_extra ) + if( p_sys->i_buffer > 10*1000000 ) { - block_Release( p_block ); - return NULL; + msg_Err( p_dec, "mmh reseting context" ); + p_sys->i_buffer = 0; } - /* TODO: Date management */ - p_block->i_length = 1000000 / 25; + /* 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( ;; ) + { + if( m4v_FindStartCode( &p_start, &p_sys->p_buffer[p_sys->i_buffer] ) ) + { + 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 ) + { + /* Copy the complete VOL */ + p_dec->fmt_out.i_extra = p_start - p_vol; + p_dec->fmt_out.p_extra = malloc( 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->fmt_out, + p_dec->fmt_out.p_extra, p_dec->fmt_out.i_extra ); - *pp_block = NULL; - return p_block; + 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 ) + { + memmove( p_sys->p_buffer, &p_sys->p_buffer[i_out], + p_sys->i_buffer - i_out ); + } + p_sys->i_buffer -= i_out; + p_start -= i_out; + + /* FIXME do proper dts/pts */ + p_out->i_pts = p_sys->i_pts; + p_out->i_dts = p_sys->i_dts; + /* FIXME doesn't work when there is multiple VOP in one block */ + if( p_block->i_dts > p_sys->i_dts ) + { + p_out->i_length = p_block->i_dts - p_sys->i_dts; + } + + 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 ); + } + +#if 0 + fprintf( stderr, "pts=%lld dts=%lld length=%lldms\n", + p_out->i_pts, p_out->i_dts, + p_out->i_length / 1000 ); +#endif + p_sys->b_vop = VLC_FALSE; + } + + if( p_start[3] >= 0x20 && p_start[3] <= 0x2f ) + { + /* Start of the VOL */ + p_vol = p_start; + } + else if( p_start[3] == 0xb6 ) + { + p_sys->b_vop = VLC_TRUE; + p_sys->i_pts = p_block->i_pts; + p_sys->i_dts = p_block->i_dts; + } + p_start += 4; /* Next */ + } } /**************************************************************************** * m4v_FindStartCode ****************************************************************************/ -static int m4v_FindStartCode( uint8_t **pp_data, uint8_t *p_end ) +static int m4v_FindStartCode( uint8_t **pp_start, uint8_t *p_end ) { - for( ; *pp_data < p_end - 4; (*pp_data)++ ) + uint8_t *p = *pp_start; + + for( p = *pp_start; p < p_end - 4; p++ ) { - if( (*pp_data)[0] == 0 && (*pp_data)[1] == 0 && (*pp_data)[2] == 1 ) + if( p[0] == 0 && p[1] == 0 && p[2] == 1 ) { - return 0; + *pp_start = p; + return VLC_SUCCESS; } } - return -1; + + *pp_start = p_end; + return VLC_EGENERIC; } -static int m4v_FindVol( decoder_t *p_dec, block_t *p_block ) + +/* look at ffmpeg av_log2 ;) */ +static int vlc_log2( unsigned int v ) { - uint8_t *p_vol_begin, *p_vol_end, *p_end; + 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 + }; - /* search if p_block contains with a vol */ - p_vol_begin = p_block->p_buffer; - p_vol_end = NULL; - p_end = p_block->p_buffer + p_block->i_buffer; + 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; +} + +/* m4v_VOLParse: + * TODO: + * - support aspect ratio + */ +static int m4v_VOLParse( es_format_t *fmt, uint8_t *p_vol, int i_vol ) +{ + bs_t s; + int i_vo_type; + int i_vo_ver_id; + int i_ar; + int i_shape; + int i_time_increment_resolution; for( ;; ) { - if( m4v_FindStartCode( &p_vol_begin, p_end ) ) + if( p_vol[0] == 0x00 && p_vol[1] == 0x00 && + p_vol[2] == 0x01 && + p_vol[3] >= 0x20 && p_vol[3] <= 0x2f ) { break; } - - msg_Dbg( p_dec, "starcode 0x%2.2x%2.2x%2.2x%2.2x", - p_vol_begin[0], p_vol_begin[1], - p_vol_begin[2], p_vol_begin[3] ); - - if( ( p_vol_begin[3] & ~VIDEO_OBJECT_MASK ) == - ( VIDEO_OBJECT_START_CODE&0xff ) ) + p_vol++; + i_vol--; + if( i_vol <= 4 ) { - p_vol_end = p_vol_begin + 4; - if( m4v_FindStartCode( &p_vol_end, p_end ) ) - { - p_vol_begin++; - continue; - } - if( ( p_vol_end[3] & ~VIDEO_OBJECT_LAYER_MASK ) == - ( VIDEO_OBJECT_LAYER_START_CODE&0xff ) ) - { - p_vol_end += 4; - if( m4v_FindStartCode( &p_vol_end, p_end ) ) - { - p_vol_end = p_end; - } - } - else - { - p_vol_begin++; - continue; - } + return VLC_EGENERIC; } - else if( ( p_vol_begin[3] & ~VIDEO_OBJECT_LAYER_MASK ) == - ( VIDEO_OBJECT_LAYER_START_CODE&0xff) ) + } + + /* parse the vol */ + bs_init( &s, &p_vol[4], i_vol - 4 ); + + bs_skip( &s, 1 ); /* random access */ + i_vo_type = bs_read( &s, 8 ); + if( bs_read1( &s ) ) + { + i_vo_ver_id = bs_read( &s, 4 ); + bs_skip( &s, 3 ); + } + else + { + i_vo_ver_id = 1; + } + i_ar = bs_read( &s, 4 ); + if( i_ar == 0xf ) + { + int i_ar_width = bs_read( &s, 8 ); + int i_ar_height= bs_read( &s, 8 ); + } + if( bs_read1( &s ) ) + { + /* vol control parameter */ + int i_chroma_format = bs_read( &s, 2 ); + int i_low_delay = bs_read1( &s ); + + if( bs_read1( &s ) ) { - p_vol_end = p_vol_begin + 4; - if( m4v_FindStartCode( &p_vol_end, p_end ) ) - { - p_vol_end = p_end; - } + bs_skip( &s, 16 ); + bs_skip( &s, 16 ); + bs_skip( &s, 16 ); + bs_skip( &s, 3 ); + bs_skip( &s, 11 ); + bs_skip( &s, 1 ); + bs_skip( &s, 16 ); } + } + /* shape 0->RECT, 1->BIN, 2->BIN_ONLY, 3->GRAY */ + i_shape = bs_read( &s, 2 ); + if( i_shape == 3 && i_vo_ver_id != 1 ) + { + bs_skip( &s, 4 ); + } - if( p_vol_end != NULL && p_vol_begin < p_vol_end ) - { - p_dec->fmt_out.i_extra = p_vol_end - p_vol_begin; - msg_Dbg( p_dec, "Found VOL" ); + if( !bs_read1( &s ) ) + { + /* marker */ + return VLC_EGENERIC; + } + i_time_increment_resolution = bs_read( &s, 16 ); + if( !bs_read1( &s ) ) + { + /* marker */ + return VLC_EGENERIC; + } - p_dec->fmt_out.p_extra = malloc( p_dec->fmt_out.i_extra ); - memcpy( p_dec->fmt_out.p_extra, p_vol_begin, - p_dec->fmt_out.i_extra ); - return VLC_SUCCESS; - } - else + if( bs_read1( &s ) ) + { + int i_time_increment_bits = vlc_log2( i_time_increment_resolution - 1 ) + 1; + if( i_time_increment_bits < 1 ) { - p_vol_begin++; + i_time_increment_bits = 1; } + bs_skip( &s, i_time_increment_bits ); + } + if( i_shape == 0 ) + { + bs_skip( &s, 1 ); + fmt->video.i_width = bs_read( &s, 13 ); + bs_skip( &s, 1 ); + fmt->video.i_height= bs_read( &s, 13 ); + bs_skip( &s, 1 ); } - return VLC_SUCCESS; } -/***************************************************************************** - * ClosePacketizer: clean up the packetizer - *****************************************************************************/ -static void ClosePacketizer( vlc_object_t *p_this ) -{ - decoder_t *p_dec = (decoder_t*)p_this; - free( p_dec->p_sys ); -} +