ogg_stream_state os; /* logical stream of packets */
es_format_t fmt;
+ es_format_t fmt_old; /* format of old ES is reused */
es_out_id_t *p_es;
double f_rate;
int i_streams; /* number of logical bitstreams */
logical_stream_t **pp_stream; /* pointer to an array of logical streams */
+ logical_stream_t *p_old_stream; /* pointer to a old logical stream to avoid recreating it */
+
/* program clock reference (in units of 90kHz) derived from the pcr of
* the sub-streams */
mtime_t i_pcr;
};
/* OggDS headers for the new header format (used in ogm files) */
-typedef struct stream_header_video
+typedef struct
{
ogg_int32_t width;
ogg_int32_t height;
-} stream_header_video;
+} stream_header_video_t;
-typedef struct stream_header_audio
+typedef struct
{
ogg_int16_t channels;
+ ogg_int16_t padding;
ogg_int16_t blockalign;
ogg_int32_t avgbytespersec;
-} stream_header_audio;
+} stream_header_audio_t;
-typedef struct stream_header
+typedef struct
{
char streamtype[8];
char subtype[4];
ogg_int32_t buffersize;
ogg_int16_t bits_per_sample;
+ ogg_int16_t padding;
union
{
/* Video specific */
- stream_header_video video;
+ stream_header_video_t video;
/* Audio specific */
- stream_header_audio audio;
+ stream_header_audio_t audio;
} sh;
-} stream_header;
+} stream_header_t;
#define OGG_BLOCK_SIZE 4096
static int Ogg_FindLogicalStreams( demux_t *p_demux );
static void Ogg_EndOfStream( demux_t *p_demux );
+/* */
+static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_stream );
+static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t *p_stream );
+
/* Logical bitstream headers */
static void Ogg_ReadTheoraHeader( logical_stream_t *, ogg_packet * );
static void Ogg_ReadVorbisHeader( logical_stream_t *, ogg_packet * );
static void Ogg_ReadKateHeader( logical_stream_t *, ogg_packet * );
static void Ogg_ReadFlacHeader( demux_t *, logical_stream_t *, ogg_packet * );
static void Ogg_ReadAnnodexHeader( vlc_object_t *, logical_stream_t *, ogg_packet * );
+static void Ogg_ReadDiracHeader( logical_stream_t *, ogg_packet * );
/*****************************************************************************
* Open: initializes ogg demux structures
p_demux->pf_demux = Demux;
p_demux->pf_control = Control;
p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
+ if( !p_sys )
+ return VLC_ENOMEM;
memset( p_sys, 0, sizeof( demux_sys_t ) );
p_sys->i_bitrate = 0;
p_sys->pp_stream = NULL;
+ p_sys->p_old_stream = NULL;
/* Begnning of stream, tell the demux to look for elementary streams. */
p_sys->i_eos = 0;
Ogg_EndOfStream( p_demux );
+ if( p_sys->p_old_stream )
+ Ogg_LogicalStreamDelete( p_demux, p_sys->p_old_stream );
+
free( p_sys );
}
if( p_sys->i_eos )
{
msg_Dbg( p_demux, "end of a group of logical streams" );
+ /* We keep the ES to try reusing it in Ogg_BeginningOfStream
+ * only 1 ES is supported (common case for ogg web radio) */
+ if( p_sys->i_streams == 1 )
+ {
+ p_sys->p_old_stream = p_sys->pp_stream[0];
+ TAB_CLEAN( p_sys->i_streams, p_sys->pp_stream );
+ }
Ogg_EndOfStream( p_demux );
}
p_sys->i_eos = 0;
- if( Ogg_BeginningOfStream( p_demux ) != VLC_SUCCESS ) return 0;
+ if( Ogg_BeginningOfStream( p_demux ) != VLC_SUCCESS )
+ return 0;
msg_Dbg( p_demux, "beginning of a group of logical streams" );
es_out_Control( p_demux->out, ES_OUT_RESET_PCR );
es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pcr );
}
-
return 1;
}
p_stream->i_pcr = ( iframe + pframe ) * INT64_C(1000000)
/ p_stream->f_rate;
}
+ else if( p_stream->fmt.i_codec == VLC_FOURCC( 'd','r','a','c' ) )
+ {
+ ogg_int64_t i_dts = p_oggpacket->granulepos >> 31;
+ /* NB, OggDirac granulepos values are in units of 2*picturerate */
+ p_stream->i_pcr = (i_dts/2) * INT64_C(1000000) / p_stream->f_rate;
+ }
else
{
p_stream->i_pcr = p_oggpacket->granulepos * INT64_C(1000000)
if( p_stream->b_force_backup )
{
- uint8_t *p_extra;
+ uint8_t *p_sav;
bool b_store_size = true;
bool b_store_num_headers = false;
/* Backup the ogg packet (likely an header packet) */
p_stream->p_headers =
- realloc( p_stream->p_headers, p_stream->i_headers +
+ realloc( p_sav = p_stream->p_headers, p_stream->i_headers +
p_oggpacket->bytes + (b_store_size ? 2 : 0) + (b_store_num_headers ? 1 : 0) );
- p_extra = p_stream->p_headers + p_stream->i_headers;
- if( b_store_num_headers )
- {
- /* Kate streams store the number of headers in the first header,
- so we can't just test for 3 as Vorbis/Theora */
- *(p_extra++) = p_stream->i_kate_num_headers;
- }
- if( b_store_size )
+ if( p_stream->p_headers )
{
- *(p_extra++) = p_oggpacket->bytes >> 8;
- *(p_extra++) = p_oggpacket->bytes & 0xFF;
- }
- memcpy( p_extra, p_oggpacket->packet, p_oggpacket->bytes );
- p_stream->i_headers += p_oggpacket->bytes + (b_store_size ? 2 : 0) + (b_store_num_headers ? 1 : 0);
+ uint8_t *p_extra = p_stream->p_headers + p_stream->i_headers;
+
+ if( b_store_num_headers )
+ {
+ /* Kate streams store the number of headers in the first header,
+ so we can't just test for 3 as Vorbis/Theora */
+ *(p_extra++) = p_stream->i_kate_num_headers;
+ }
+ if( b_store_size )
+ {
+ *(p_extra++) = p_oggpacket->bytes >> 8;
+ *(p_extra++) = p_oggpacket->bytes & 0xFF;
+ }
+ memcpy( p_extra, p_oggpacket->packet, p_oggpacket->bytes );
+ p_stream->i_headers += p_oggpacket->bytes + (b_store_size ? 2 : 0) + (b_store_num_headers ? 1 : 0);
- if( !p_stream->b_force_backup )
+ if( !p_stream->b_force_backup )
+ {
+ /* Last header received, commit changes */
+ free( p_stream->fmt.p_extra );
+
+ p_stream->fmt.i_extra = p_stream->i_headers;
+ p_stream->fmt.p_extra =
+ realloc( p_stream->fmt.p_extra, p_stream->i_headers );
+ if( p_stream->fmt.p_extra )
+ memcpy( p_stream->fmt.p_extra, p_stream->p_headers,
+ p_stream->i_headers );
+ else
+ p_stream->fmt.i_extra = 0;
+
+ if( Ogg_LogicalStreamResetEsFormat( p_demux, p_stream ) )
+ es_out_Control( p_demux->out, ES_OUT_SET_FMT,
+ p_stream->p_es, &p_stream->fmt );
+ }
+ }
+ else
{
- /* Last header received, commit changes */
- p_stream->fmt.i_extra = p_stream->i_headers;
- p_stream->fmt.p_extra =
- realloc( p_stream->fmt.p_extra, p_stream->i_headers );
- memcpy( p_stream->fmt.p_extra, p_stream->p_headers,
- p_stream->i_headers );
- es_out_Control( p_demux->out, ES_OUT_SET_FMT,
- p_stream->p_es, &p_stream->fmt );
+ p_stream->p_headers = p_sav;
}
b_selected = false; /* Discard the header packet */
}
else if( p_stream->fmt.i_codec == VLC_FOURCC( 't','h','e','o' ) )
p_block->i_dts = p_block->i_pts = i_pts;
+ else if( p_stream->fmt.i_codec == VLC_FOURCC( 'd','r','a','c' ) )
+ {
+ ogg_int64_t dts = p_oggpacket->granulepos >> 31;
+ ogg_int64_t delay = (p_oggpacket->granulepos >> 9) & 0x1fff;
+
+ uint64_t u_pnum = dts + delay;
+
+ p_block->i_dts = p_stream->i_pcr;
+ p_block->i_pts = 0;
+ /* NB, OggDirac granulepos values are in units of 2*picturerate */
+ if( -1 != p_oggpacket->granulepos )
+ p_block->i_pts = u_pnum * INT64_C(1000000) / p_stream->f_rate / 2;
+ }
else
{
p_block->i_dts = i_pts;
p_stream->fmt.i_codec != VLC_FOURCC( 't','a','r','k' ) &&
p_stream->fmt.i_codec != VLC_FOURCC( 't','h','e','o' ) &&
p_stream->fmt.i_codec != VLC_FOURCC( 'c','m','m','l' ) &&
+ p_stream->fmt.i_codec != VLC_FOURCC( 'd','r','a','c' ) &&
p_stream->fmt.i_codec != VLC_FOURCC( 'k','a','t','e' ) )
{
/* We remove the header from the packet */
ogg_page oggpage;
int i_stream;
-#define p_stream p_ogg->pp_stream[p_ogg->i_streams - 1]
-
while( Ogg_ReadPage( p_demux, &oggpage ) == VLC_SUCCESS )
{
if( ogg_page_bos( &oggpage ) )
* We found the beginning of our first logical stream. */
while( ogg_page_bos( &oggpage ) )
{
- p_ogg->i_streams++;
- p_ogg->pp_stream =
- realloc( p_ogg->pp_stream, p_ogg->i_streams *
- sizeof(logical_stream_t *) );
+ logical_stream_t **pp_sav = p_ogg->pp_stream;
+ logical_stream_t *p_stream;
p_stream = malloc( sizeof(logical_stream_t) );
+ if( !p_stream )
+ return VLC_ENOMEM;
+
+ TAB_APPEND( p_ogg->i_streams, p_ogg->pp_stream, p_stream );
+
memset( p_stream, 0, sizeof(logical_stream_t) );
p_stream->p_headers = 0;
p_stream->secondary_header_packets = 0;
es_format_Init( &p_stream->fmt, 0, 0 );
+ es_format_Init( &p_stream->fmt_old, 0, 0 );
/* Setup the logical stream */
p_stream->i_serial_no = ogg_page_serialno( &oggpage );
"found theora header, bitrate: %i, rate: %f",
p_stream->fmt.i_bitrate, p_stream->f_rate );
}
+ /* Check for Dirac header */
+ else if( oggpacket.bytes >= 5 &&
+ ! memcmp( oggpacket.packet, "BBCD\x00", 5 ) )
+ {
+ Ogg_ReadDiracHeader( p_stream, &oggpacket );
+ msg_Dbg( p_demux, "found dirac header" );
+ }
/* Check for Tarkin header */
else if( oggpacket.bytes >= 7 &&
! memcmp( &oggpacket.packet[1], "tarkin", 6 ) )
&oggpacket );
}
/* Check for Kate header */
- else if( oggpacket.bytes >= 9 &&
- ! memcmp( &oggpacket.packet[1], "kate\0\0\0\0", 8 ) )
+ else if( oggpacket.bytes >= 8 &&
+ ! memcmp( &oggpacket.packet[1], "kate\0\0\0", 7 ) )
{
Ogg_ReadKateHeader( p_stream, &oggpacket );
msg_Dbg( p_demux, "found kate header" );
p_stream->fmt.i_cat = AUDIO_ES;
i_extra_size = GetWLE((oggpacket.packet+140));
- if( i_extra_size )
+ if( i_extra_size > 0 && i_extra_size < oggpacket.bytes - 142 )
{
p_stream->fmt.i_extra = i_extra_size;
p_stream->fmt.p_extra = malloc( i_extra_size );
- memcpy( p_stream->fmt.p_extra,
- oggpacket.packet + 142, i_extra_size );
+ if( p_stream->fmt.p_extra )
+ memcpy( p_stream->fmt.p_extra,
+ oggpacket.packet + 142, i_extra_size );
+ else
+ p_stream->fmt.i_extra = 0;
}
i_format_tag = GetWLE((oggpacket.packet+124));
p_ogg->i_streams--;
}
}
- else if( (*oggpacket.packet & PACKET_TYPE_BITS )
- == PACKET_TYPE_HEADER &&
- oggpacket.bytes >= (int)sizeof(stream_header)+1 )
+ else if( (*oggpacket.packet & PACKET_TYPE_BITS ) == PACKET_TYPE_HEADER &&
+ oggpacket.bytes >= 56+1 )
{
- stream_header *st = (stream_header *)(oggpacket.packet+1);
+ stream_header_t tmp;
+ stream_header_t *st = &tmp;
+
+ memcpy( st->streamtype, &oggpacket.packet[1+0], 8 );
+ memcpy( st->subtype, &oggpacket.packet[1+8], 4 );
+ st->size = GetDWLE( &oggpacket.packet[1+12] );
+ st->time_unit = GetQWLE( &oggpacket.packet[1+16] );
+ st->samples_per_unit = GetQWLE( &oggpacket.packet[1+24] );
+ st->default_len = GetDWLE( &oggpacket.packet[1+32] );
+ st->buffersize = GetDWLE( &oggpacket.packet[1+36] );
+ st->bits_per_sample = GetWLE( &oggpacket.packet[1+40] ); // (padding 2)
/* Check for video header (new format) */
if( !strncmp( st->streamtype, "video", 5 ) )
{
+ st->sh.video.width = GetDWLE( &oggpacket.packet[1+44] );
+ st->sh.video.height = GetDWLE( &oggpacket.packet[1+48] );
+
p_stream->fmt.i_cat = VIDEO_ES;
/* We need to get rid of the header packet */
(char *)&p_stream->fmt.i_codec );
p_stream->fmt.video.i_frame_rate = 10000000;
- p_stream->fmt.video.i_frame_rate_base =
- GetQWLE(&st->time_unit);
- p_stream->f_rate = 10000000.0 /
- GetQWLE(&st->time_unit);
- p_stream->fmt.video.i_bits_per_pixel =
- GetWLE(&st->bits_per_sample);
- p_stream->fmt.video.i_width =
- GetDWLE(&st->sh.video.width);
- p_stream->fmt.video.i_height =
- GetDWLE(&st->sh.video.height);
+ p_stream->fmt.video.i_frame_rate_base = st->time_unit;
+ if( st->time_unit <= 0 )
+ st->time_unit = 400000;
+ p_stream->f_rate = 10000000.0 / st->time_unit;
+ p_stream->fmt.video.i_bits_per_pixel = st->bits_per_sample;
+ p_stream->fmt.video.i_width = st->sh.video.width;
+ p_stream->fmt.video.i_height = st->sh.video.height;
msg_Dbg( p_demux,
"fps: %f, width:%i; height:%i, bitcount:%i",
else if( !strncmp( st->streamtype, "audio", 5 ) )
{
char p_buffer[5];
+ unsigned int i_extra_size;
int i_format_tag;
+ st->sh.audio.channels = GetWLE( &oggpacket.packet[1+44] );
+ st->sh.audio.blockalign = GetWLE( &oggpacket.packet[1+48] );
+ st->sh.audio.avgbytespersec = GetDWLE( &oggpacket.packet[1+52] );
+
p_stream->fmt.i_cat = AUDIO_ES;
/* We need to get rid of the header packet */
ogg_stream_packetout( &p_stream->os, &oggpacket );
- p_stream->fmt.i_extra = GetQWLE(&st->size) -
- sizeof(stream_header);
- if( p_stream->fmt.i_extra )
+ i_extra_size = st->size - 56;
+
+ if( i_extra_size > 0 &&
+ i_extra_size < oggpacket.bytes - 1 - 56 )
{
- p_stream->fmt.p_extra =
- malloc( p_stream->fmt.i_extra );
- memcpy( p_stream->fmt.p_extra, st + 1,
- p_stream->fmt.i_extra );
+ p_stream->fmt.i_extra = i_extra_size;
+ p_stream->fmt.p_extra = malloc( p_stream->fmt.i_extra );
+ if( p_stream->fmt.p_extra )
+ memcpy( p_stream->fmt.p_extra, st + 1,
+ p_stream->fmt.i_extra );
+ else
+ p_stream->fmt.i_extra = 0;
}
memcpy( p_buffer, st->subtype, 4 );
p_buffer[4] = '\0';
i_format_tag = strtol(p_buffer,NULL,16);
- p_stream->fmt.audio.i_channels =
- GetWLE(&st->sh.audio.channels);
- p_stream->f_rate = p_stream->fmt.audio.i_rate =
- GetQWLE(&st->samples_per_unit);
- p_stream->fmt.i_bitrate =
- GetDWLE(&st->sh.audio.avgbytespersec) * 8;
- p_stream->fmt.audio.i_blockalign =
- GetWLE(&st->sh.audio.blockalign);
- p_stream->fmt.audio.i_bitspersample =
- GetWLE(&st->bits_per_sample);
+ p_stream->fmt.audio.i_channels = st->sh.audio.channels;
+ if( st->time_unit <= 0 )
+ st->time_unit = 10000000;
+ p_stream->f_rate = p_stream->fmt.audio.i_rate = st->samples_per_unit * 10000000 / st->time_unit;
+ p_stream->fmt.i_bitrate = st->sh.audio.avgbytespersec * 8;
+ p_stream->fmt.audio.i_blockalign = st->sh.audio.blockalign;
+ p_stream->fmt.audio.i_bitspersample = st->bits_per_sample;
wf_tag_to_fourcc( i_format_tag,
&p_stream->fmt.i_codec, 0 );
msg_Dbg( p_demux, "found text subtitles header" );
p_stream->fmt.i_cat = SPU_ES;
p_stream->fmt.i_codec = VLC_FOURCC('s','u','b','t');
- p_stream->f_rate = 1000; /* granulepos is in milisec */
+ p_stream->f_rate = 1000; /* granulepos is in millisec */
}
else
{
return VLC_SUCCESS;
}
}
-#undef p_stream
return VLC_EGENERIC;
}
static int Ogg_BeginningOfStream( demux_t *p_demux )
{
demux_sys_t *p_ogg = p_demux->p_sys ;
+ logical_stream_t *p_old_stream = p_ogg->p_old_stream;
int i_stream;
/* Find the logical streams embedded in the physical stream and
for( i_stream = 0 ; i_stream < p_ogg->i_streams; i_stream++ )
{
-#define p_stream p_ogg->pp_stream[i_stream]
- p_stream->p_es = es_out_Add( p_demux->out, &p_stream->fmt );
+ logical_stream_t *p_stream = p_ogg->pp_stream[i_stream];
+
+ p_stream->p_es = NULL;
+
+ /* Try first to reuse an old ES */
+ if( p_old_stream &&
+ p_old_stream->fmt.i_cat == p_stream->fmt.i_cat &&
+ p_old_stream->fmt.i_codec == p_stream->fmt.i_codec )
+ {
+ msg_Dbg( p_demux, "will reuse old stream to avoid glitch" );
+
+ p_stream->p_es = p_old_stream->p_es;
+ es_format_Copy( &p_stream->fmt_old, &p_old_stream->fmt );
+
+ p_old_stream->p_es = NULL;
+ p_old_stream = NULL;
+ }
+
+ if( !p_stream->p_es )
+ p_stream->p_es = es_out_Add( p_demux->out, &p_stream->fmt );
// TODO: something to do here ?
if( p_stream->fmt.i_codec == VLC_FOURCC('c','m','m','l') )
p_stream->i_pcr = p_stream->i_previous_pcr =
p_stream->i_interpolated_pcr = -1;
p_stream->b_reinit = 0;
-#undef p_stream
}
+ if( p_ogg->p_old_stream )
+ {
+ if( p_ogg->p_old_stream->p_es )
+ msg_Dbg( p_demux, "old stream not reused" );
+ Ogg_LogicalStreamDelete( p_demux, p_ogg->p_old_stream );
+ p_ogg->p_old_stream = NULL;
+ }
return VLC_SUCCESS;
}
demux_sys_t *p_ogg = p_demux->p_sys ;
int i_stream;
-#define p_stream p_ogg->pp_stream[i_stream]
for( i_stream = 0 ; i_stream < p_ogg->i_streams; i_stream++ )
+ Ogg_LogicalStreamDelete( p_demux, p_ogg->pp_stream[i_stream] );
+ free( p_ogg->pp_stream );
+
+ /* Reinit p_ogg */
+ p_ogg->i_bitrate = 0;
+ p_ogg->i_streams = 0;
+ p_ogg->pp_stream = NULL;
+}
+
+/**
+ * This function delete and release all data associated to a logical_stream_t
+ */
+static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_stream )
+{
+ if( p_stream->p_es )
+ es_out_Del( p_demux->out, p_stream->p_es );
+
+ ogg_stream_clear( &p_stream->os );
+ free( p_stream->p_headers );
+
+ es_format_Clean( &p_stream->fmt_old );
+ es_format_Clean( &p_stream->fmt );
+
+ free( p_stream );
+}
+/**
+ * This function check if a we need to reset a decoder in case we are
+ * reusing an old ES
+ */
+static bool Ogg_IsVorbisFormatCompatible( const es_format_t *p_new, const es_format_t *p_old )
+{
+ int i_new = 0;
+ int i_old = 0;
+ int i;
+
+ for( i = 0; i < 3; i++ )
{
- if( p_stream->p_es )
- es_out_Del( p_demux->out, p_stream->p_es );
+ const uint8_t *p_new_extra = ( const uint8_t*)p_new->p_extra + i_new;
+ const uint8_t *p_old_extra = ( const uint8_t*)p_old->p_extra + i_old;
- p_ogg->i_bitrate -= p_stream->fmt.i_bitrate;
+ if( p_new->i_extra < i_new+2 || p_old->i_extra < i_old+2 )
+ return false;
- ogg_stream_clear( &p_ogg->pp_stream[i_stream]->os );
- free( p_ogg->pp_stream[i_stream]->p_headers );
+ const int i_new_size = GetWBE( &p_new_extra[0] );
+ const int i_old_size = GetWBE( &p_old_extra[0] );
- es_format_Clean( &p_stream->fmt );
+ if( i != 1 ) /* Ignore vorbis comment */
+ {
+ if( i_new_size != i_old_size )
+ return false;
+ if( memcmp( &p_new_extra[2], &p_old_extra[2], i_new_size ) )
+ return false;
+ }
- free( p_ogg->pp_stream[i_stream] );
+ i_new += 2 + i_new_size;
+ i_old += 2 + i_old_size;
}
-#undef p_stream
+ return true;
+}
+static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t *p_stream )
+{
+ bool b_compatible;
+ if( !p_stream->fmt_old.i_cat || !p_stream->fmt_old.i_codec )
+ return true;
- /* Reinit p_ogg */
- free( p_ogg->pp_stream );
- p_ogg->pp_stream = NULL;
- p_ogg->i_streams = 0;
+ /* Only vorbis is supported */
+ if( p_stream->fmt.i_codec == VLC_FOURCC( 'v','o','r','b' ) )
+ b_compatible = Ogg_IsVorbisFormatCompatible( &p_stream->fmt, &p_stream->fmt_old );
+
+ if( !b_compatible )
+ msg_Warn( p_demux, "cannot reuse old stream, resetting the decoder" );
+
+ return !b_compatible;
}
static void Ogg_ReadTheoraHeader( logical_stream_t *p_stream,
}
}
}
+
+static uint32_t dirac_uint( bs_t *p_bs )
+{
+ uint32_t count = 0, value = 0;
+ while( !bs_read ( p_bs, 1 ) ) {
+ count++;
+ value <<= 1;
+ value |= bs_read ( p_bs, 1 );
+ }
+
+ return (1<<count) - 1 + value;
+}
+
+static int dirac_bool( bs_t *p_bs )
+{
+ return bs_read ( p_bs, 1 );
+}
+
+static void Ogg_ReadDiracHeader( logical_stream_t *p_stream,
+ ogg_packet *p_oggpacket )
+{
+ bs_t bs;
+
+ p_stream->fmt.i_cat = VIDEO_ES;
+ p_stream->fmt.i_codec = VLC_FOURCC( 'd','r','a','c' );
+ p_stream->i_granule_shift = 32;
+
+ /* Backing up stream headers is not required -- seqhdrs are repeated
+ * thoughout the stream at suitable decoding start points */
+ p_stream->b_force_backup = 0;
+
+ /* read in useful bits from sequence header */
+ bs_init( &bs, p_oggpacket->packet, p_oggpacket->bytes );
+ bs_skip( &bs, 13*8); /* parse_info_header */
+ dirac_uint( &bs ); /* major_version */
+ dirac_uint( &bs ); /* minor_version */
+ dirac_uint( &bs ); /* profile */
+ dirac_uint( &bs ); /* level */
+
+ uint32_t u_video_format = dirac_uint( &bs ); /* index */
+
+ if (dirac_bool( &bs )) {
+ dirac_uint( &bs ); /* frame_width */
+ dirac_uint( &bs ); /* frame_height */
+ }
+
+ if (dirac_bool( &bs )) {
+ dirac_uint( &bs ); /* chroma_format */
+ }
+ if (dirac_bool( &bs )) {
+ if (dirac_bool( &bs )) { /* interlaced */
+ dirac_bool( &bs ); /* top_field_first */
+ }
+ }
+
+ static const struct {
+ uint32_t u_n /* numerator */, u_d /* denominator */;
+ } dirac_frate_tbl[] = { /* table 10.3 */
+ {1,1}, /* this first value is never used */
+ {24000,1001}, {24,1}, {25,1}, {30000,1001}, {30,1},
+ {50,1}, {60000,1001}, {60,1}, {15000,1001}, {25,2},
+ };
+
+ static const uint32_t dirac_vidfmt_frate[] = { /* table C.1 */
+ 1, 9, 10, 9, 10, 9, 10, 4, 3, 7, 6, 4, 3, 7, 6, 2, 2, 7, 6, 7, 6,
+ };
+
+ uint32_t u_n = dirac_frate_tbl[dirac_vidfmt_frate[u_video_format]].u_n;
+ uint32_t u_d = dirac_frate_tbl[dirac_vidfmt_frate[u_video_format]].u_d;
+ if (dirac_bool( &bs )) {
+ uint32_t frame_rate_index = dirac_uint( &bs );
+ u_n = dirac_frate_tbl[frame_rate_index].u_n;
+ u_d = dirac_frate_tbl[frame_rate_index].u_d;
+ if (frame_rate_index == 0) {
+ u_n = dirac_uint( &bs ); /* frame_rate_numerator */
+ u_d = dirac_uint( &bs ); /* frame_rate_denominator */
+ }
+ }
+ p_stream->f_rate = (float) u_n / u_d;
+}