X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Fogg.c;h=8326b4e0ef35fdbbf59332ff1730990f2c4bd894;hb=38de02fe5c4102351436c7f058db297486f5b03f;hp=65f4a44eff7360dc11d36776a17edb2566079e71;hpb=6a41b030f5b7fcbe5ad7249c374172c0fdc29add;p=vlc diff --git a/modules/demux/ogg.c b/modules/demux/ogg.c index 65f4a44eff..8326b4e0ef 100644 --- a/modules/demux/ogg.c +++ b/modules/demux/ogg.c @@ -1,25 +1,25 @@ /***************************************************************************** * ogg.c : ogg stream demux module for vlc ***************************************************************************** - * Copyright (C) 2001-2007 the VideoLAN team + * Copyright (C) 2001-2007 VLC authors and VideoLAN * $Id$ * * Authors: Gildas Bazin * Andre Pang (Annodex support) * - * 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 + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 2.1 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. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser 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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ /***************************************************************************** @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -40,10 +41,10 @@ #include #include #include "xiph.h" -#include "vorbis.h" -#include "kate_categories.h" +#include "xiph_metadata.h" #include "ogg.h" #include "oggseek.h" +#include "opus.h" /***************************************************************************** * Module descriptor @@ -105,13 +106,12 @@ typedef struct } sh; } stream_header_t; - -/* Some defines from OggDS */ -#define PACKET_TYPE_HEADER 0x01 -#define PACKET_TYPE_BITS 0x07 -#define PACKET_LEN_BITS01 0xc0 -#define PACKET_LEN_BITS2 0x02 -#define PACKET_IS_SYNCPOINT 0x08 +#define VORBIS_HEADER_IDENTIFICATION 1 +#define VORBIS_HEADER_COMMENT 2 +#define VORBIS_HEADER_SETUP 3 +#define VORBIS_HEADER_TO_FLAG(i) (1 << (i - 1)) +#define VORBIS_HEADERS_VALID(p_stream) \ + ((p_stream->special.vorbis.i_headers_flags & 0x07) == 0x07) // 0b111 /***************************************************************************** * Local prototypes @@ -121,9 +121,12 @@ static int Control( demux_t *, int, va_list ); /* Bitstream manipulation */ static int Ogg_ReadPage ( demux_t *, ogg_page * ); -static void Ogg_UpdatePCR ( logical_stream_t *, ogg_packet * ); +static void Ogg_UpdatePCR ( demux_t *, logical_stream_t *, ogg_packet * ); static void Ogg_DecodePacket ( demux_t *, logical_stream_t *, ogg_packet * ); +static int Ogg_OpusPacketDuration( ogg_packet * ); +static void Ogg_SendOrQueueBlocks( demux_t *, logical_stream_t *, block_t * ); +static void Ogg_CreateES( demux_t *p_demux ); static int Ogg_BeginningOfStream( demux_t *p_demux ); static int Ogg_FindLogicalStreams( demux_t *p_demux ); static void Ogg_EndOfStream( demux_t *p_demux ); @@ -131,18 +134,67 @@ 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 ); +static void Ogg_ResetStream( logical_stream_t *p_stream ); /* */ -static void Ogg_ExtractMeta( demux_t *p_demux, vlc_fourcc_t i_codec, const uint8_t *p_headers, int i_headers ); +static void Ogg_ExtractMeta( demux_t *p_demux, es_format_t *p_fmt, const uint8_t *p_headers, int i_headers ); /* Logical bitstream headers */ -static void Ogg_ReadTheoraHeader( logical_stream_t *, ogg_packet * ); -static void Ogg_ReadVorbisHeader( logical_stream_t *, ogg_packet * ); -static void Ogg_ReadSpeexHeader( 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 bool Ogg_ReadDaalaHeader( logical_stream_t *, ogg_packet * ); +static bool Ogg_ReadTheoraHeader( logical_stream_t *, ogg_packet * ); +static bool Ogg_ReadVorbisHeader( logical_stream_t *, ogg_packet * ); +static bool Ogg_ReadSpeexHeader( logical_stream_t *, ogg_packet * ); +static void Ogg_ReadOpusHeader( logical_stream_t *, ogg_packet * ); +static bool Ogg_ReadKateHeader( logical_stream_t *, ogg_packet * ); +static bool Ogg_ReadFlacHeader( demux_t *, logical_stream_t *, ogg_packet * ); +static void Ogg_ReadAnnodexHeader( demux_t *, logical_stream_t *, ogg_packet * ); static bool Ogg_ReadDiracHeader( logical_stream_t *, ogg_packet * ); +static bool Ogg_ReadVP8Header( demux_t *, logical_stream_t *, ogg_packet * ); +static void Ogg_ReadSkeletonHeader( demux_t *, logical_stream_t *, ogg_packet * ); + +/* Skeleton */ +static void Ogg_ReadSkeletonBones( demux_t *, ogg_packet * ); +static void Ogg_ReadSkeletonIndex( demux_t *, ogg_packet * ); +static void Ogg_FreeSkeleton( ogg_skeleton_t * ); +static void Ogg_ApplySkeleton( logical_stream_t * ); + +/* Special decoding */ +static void Ogg_CleanSpecificData( logical_stream_t * ); +#ifdef HAVE_LIBVORBIS +static void Ogg_DecodeVorbisHeader( logical_stream_t *, ogg_packet *, int ); +#endif + +static void fill_channels_info(audio_format_t *audio) +{ + static const int pi_channels_map[9] = + { + 0, + AOUT_CHAN_CENTER, + AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT, + AOUT_CHAN_CENTER | AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT, + AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_REARLEFT + | AOUT_CHAN_REARRIGHT, + AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT, + AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER + | AOUT_CHAN_REARLEFT | AOUT_CHAN_REARRIGHT | AOUT_CHAN_LFE, + AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER + | AOUT_CHAN_REARCENTER | AOUT_CHAN_MIDDLELEFT + | AOUT_CHAN_MIDDLERIGHT | AOUT_CHAN_LFE, + AOUT_CHAN_LEFT | AOUT_CHAN_RIGHT | AOUT_CHAN_CENTER | AOUT_CHAN_REARLEFT + | AOUT_CHAN_REARRIGHT | AOUT_CHAN_MIDDLELEFT | AOUT_CHAN_MIDDLERIGHT + | AOUT_CHAN_LFE, + }; + + unsigned chans = audio->i_channels; + if (chans < sizeof(pi_channels_map) / sizeof(pi_channels_map[0])) + audio->i_physical_channels = + audio->i_original_channels = pi_channels_map[chans]; +} + +/* Special TS value: don't send or derive any pts/pcr from it. + Represents TS state prior first known valid timestamp */ +#define VLC_TS_UNKNOWN (VLC_TS_INVALID - 1) /***************************************************************************** * Open: initializes ogg demux structures @@ -153,36 +205,48 @@ static int Open( vlc_object_t * p_this ) demux_sys_t *p_sys; const uint8_t *p_peek; - /* Check if we are dealing with an ogg stream */ if( stream_Peek( p_demux->s, &p_peek, 4 ) < 4 ) return VLC_EGENERIC; if( !p_demux->b_force && memcmp( p_peek, "OggS", 4 ) ) { - return VLC_EGENERIC; + char *psz_mime = stream_ContentType( p_demux->s ); + if( !psz_mime ) + { + return VLC_EGENERIC; + } + else if ( strcmp( psz_mime, "application/ogg" ) && + strcmp( psz_mime, "video/ogg" ) && + strcmp( psz_mime, "audio/ogg" ) ) + { + free( psz_mime ); + return VLC_EGENERIC; + } + free( psz_mime ); } - /* Set exported functions */ - p_demux->pf_demux = Demux; - p_demux->pf_control = Control; - p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) ); + /* */ + p_demux->p_sys = p_sys = calloc( 1, 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; + p_sys->i_length = -1; + p_sys->b_preparsing_done = false; - /* Begnning of stream, tell the demux to look for elementary streams. */ - p_sys->i_bos = 0; - p_sys->i_eos = 0; + stream_Control( p_demux->s, ACCESS_GET_PTS_DELAY, & p_sys->i_access_delay ); + + /* Set exported functions */ + p_demux->pf_demux = Demux; + p_demux->pf_control = Control; /* Initialize the Ogg physical bitstream parser */ ogg_sync_init( &p_sys->oy ); - p_sys->b_page_waiting = false; /* */ - p_sys->p_meta = NULL; + TAB_INIT( p_sys->i_seekpoints, p_sys->pp_seekpoints ); + + + while ( !p_sys->b_preparsing_done && p_demux->pf_demux( p_demux ) > 0 ) + {} return VLC_SUCCESS; } @@ -217,11 +281,18 @@ static int Demux( demux_t * p_demux ) ogg_packet oggpacket; int i_stream; bool b_skipping = false; + bool b_canseek; + int i_active_streams = p_sys->i_streams; + for ( int i=0; i < p_sys->i_streams; i++ ) + { + if ( p_sys->pp_stream[i]->b_finished ) + i_active_streams--; + } - if( p_sys->i_eos == p_sys->i_streams ) + if ( i_active_streams == 0 ) { - if( p_sys->i_eos ) + if ( p_sys->i_streams ) /* All finished */ { msg_Dbg( p_demux, "end of a group of logical streams" ); /* We keep the ES to try reusing it in Ogg_BeginningOfStream @@ -232,14 +303,31 @@ static int Demux( demux_t * p_demux ) TAB_CLEAN( p_sys->i_streams, p_sys->pp_stream ); } Ogg_EndOfStream( p_demux ); + p_sys->b_chained_boundary = true; } - p_sys->i_eos = 0; if( Ogg_BeginningOfStream( p_demux ) != VLC_SUCCESS ) - return 0; + return VLC_DEMUXER_EOF; msg_Dbg( p_demux, "beginning of a group of logical streams" ); - es_out_Control( p_demux->out, ES_OUT_RESET_PCR ); + + if ( !p_sys->b_chained_boundary ) + { + /* Find the real duration */ + stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_canseek ); + if ( b_canseek ) + Oggseek_ProbeEnd( p_demux ); + } + else + { + p_sys->b_chained_boundary = false; + } + } + + if ( p_sys->b_preparsing_done && !p_sys->b_es_created ) + { + Ogg_CreateES( p_demux ); + p_sys->b_es_created = true; } /* @@ -262,13 +350,42 @@ static int Demux( demux_t * p_demux ) * Demux an ogg page from the stream */ if( Ogg_ReadPage( p_demux, &p_sys->current_page ) != VLC_SUCCESS ) - return 0; /* EOF */ - + return VLC_DEMUXER_EOF; /* EOF */ /* Test for End of Stream */ if( ogg_page_eos( &p_sys->current_page ) ) - p_sys->i_eos++; + { + /* If we delayed restarting encoders/SET_ES_FMT for more + * skeleton provided configuration */ + if ( p_sys->p_skelstream ) + { + if ( p_sys->p_skelstream->i_serial_no == ogg_page_serialno(&p_sys->current_page) ) + { + msg_Dbg( p_demux, "End of Skeleton" ); + p_sys->b_preparsing_done = true; + for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ ) + { + logical_stream_t *p_stream = p_sys->pp_stream[i_stream]; + Ogg_ApplySkeleton( p_stream ); + } + } + } + + for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ ) + { + if ( p_sys->pp_stream[i_stream]->i_serial_no == ogg_page_serialno( &p_sys->current_page ) ) + { + p_sys->pp_stream[i_stream]->b_finished = true; + break; + } + } + } } + b_skipping = false; + for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ ) + { + b_skipping |= p_sys->pp_stream[i_stream]->i_skip_frames; + } for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ ) { @@ -281,23 +398,70 @@ static int Demux( demux_t * p_demux ) ogg_page_serialno( &p_sys->current_page ) != p_stream->os.serialno ) { msg_Err( p_demux, "Broken Ogg stream (serialno) mismatch" ); + Ogg_ResetStream( p_stream ); + p_sys->i_nzpcr_offset = (p_sys->i_pcr >= VLC_TS_INVALID) ? + p_sys->i_pcr - VLC_TS_0 : 0; ogg_stream_reset_serialno( &p_stream->os, ogg_page_serialno( &p_sys->current_page ) ); - - p_stream->b_reinit = true; - p_stream->i_pcr = -1; - p_stream->i_interpolated_pcr = -1; - es_out_Control( p_demux->out, ES_OUT_RESET_PCR ); } + /* Does fail if serialno differs */ if( ogg_stream_pagein( &p_stream->os, &p_sys->current_page ) != 0 ) { continue; } + } + + /* clear the finished flag if pages after eos (ex: after a seek) */ + if ( ! ogg_page_eos( &p_sys->current_page ) && p_sys->p_skelstream != p_stream ) + p_stream->b_finished = false; + + DemuxDebug( + if ( p_stream->fmt.i_cat == VIDEO_ES ) + msg_Dbg(p_demux, "DEMUX READ pageno %ld g%"PRId64" (%d packets) cont %d %ld bytes", + ogg_page_pageno( &p_sys->current_page ), + ogg_page_granulepos( &p_sys->current_page ), + ogg_page_packets( &p_sys->current_page ), + ogg_page_continued(&p_sys->current_page), + p_sys->current_page.body_len ) + ); + const int i_page_packets = ogg_page_packets( &p_sys->current_page ); + bool b_doprepcr = false; + + if ( p_stream->i_pcr < VLC_TS_0 && ogg_page_granulepos( &p_sys->current_page ) > 0 ) + { + // PASS 0 + if ( p_stream->fmt.i_codec == VLC_CODEC_OPUS || + p_stream->fmt.i_codec == VLC_CODEC_VORBIS || + p_stream->fmt.i_codec == VLC_CODEC_SPEEX || + p_stream->fmt.i_cat == VIDEO_ES ) + { + assert( p_stream->prepcr.pp_blocks == NULL ); + b_doprepcr = true; + } } + int i_real_page_packets = 0; while( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 ) { + i_real_page_packets++; + int i_max_packets = __MAX(i_page_packets, i_real_page_packets); + if ( b_doprepcr && p_stream->prepcr.i_size < i_max_packets ) + { + /* always double alloc for performance */ + i_max_packets = __MAX( i_max_packets << 1, 255 ); + /* alloc or realloc */ + block_t **pp_realloc = realloc( p_stream->prepcr.pp_blocks, + sizeof(block_t *) * i_max_packets ); + if ( !pp_realloc ) + { + /* drop it then */ + continue; + } + p_stream->prepcr.i_size = i_max_packets; + p_stream->prepcr.pp_blocks = pp_realloc; + } + /* Read info from any secondary header packets, if there are any */ if( p_stream->i_secondary_header_packets > 0 ) { @@ -308,6 +472,13 @@ static int Demux( demux_t * p_demux ) Ogg_ReadTheoraHeader( p_stream, &oggpacket ); p_stream->i_secondary_header_packets = 0; } + else if( p_stream->fmt.i_codec == VLC_CODEC_DAALA && + oggpacket.bytes >= 6 && + ! memcmp( oggpacket.packet, "\x80""daala", 6 ) ) + { + Ogg_ReadDaalaHeader( p_stream, &oggpacket ); + p_stream->i_secondary_header_packets = 0; + } else if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS && oggpacket.bytes >= 7 && ! memcmp( oggpacket.packet, "\x01vorbis", 7 ) ) @@ -322,49 +493,120 @@ static int Demux( demux_t * p_demux ) /* update start of data pointer */ p_stream->i_data_start = stream_Tell( p_demux->s ); - } /* If any streams have i_skip_frames, only decode (pre-roll) - * for those streams */ - if ( b_skipping && p_stream->i_skip_frames == 0 ) continue; - + * for those streams, but don't skip headers */ + if ( b_skipping && p_stream->i_skip_frames == 0 + && p_stream->i_secondary_header_packets ) continue; if( p_stream->b_reinit ) { - /* If synchro is re-initialized we need to drop all the packets - * until we find a new dated one. */ - Ogg_UpdatePCR( p_stream, &oggpacket ); - - if( p_stream->i_pcr >= 0 ) + p_stream->b_reinit = false; + if( p_stream->fmt.i_codec == VLC_CODEC_OPUS ) { - p_stream->b_reinit = false; + p_stream->i_skip_frames = p_stream->i_pre_skip; } - else + } + + Ogg_DecodePacket( p_demux, p_stream, &oggpacket ); + } + + if ( p_stream->prepcr.pp_blocks ) + { + int64_t pagestamp = Oggseek_GranuleToAbsTimestamp( p_stream, ogg_page_granulepos( &p_sys->current_page ), false ); + p_stream->i_previous_pcr = pagestamp; +#ifdef HAVE_LIBVORBIS + int i_prev_blocksize = 0; +#endif + // PASS 1 + for( int i=0; iprepcr.i_used; i++ ) + { + block_t *p_block = p_stream->prepcr.pp_blocks[i]; + ogg_packet dumb_packet; + dumb_packet.bytes = p_block->i_buffer; + dumb_packet.packet = p_block->p_buffer; + + switch( p_stream->fmt.i_codec ) { - p_stream->i_interpolated_pcr = -1; - continue; + case VLC_CODEC_SPEEX: + p_block->i_nb_samples = p_stream->special.speex.i_framesize * + p_stream->special.speex.i_framesperpacket; + break; + case VLC_CODEC_OPUS: + p_block->i_nb_samples = Ogg_OpusPacketDuration( &dumb_packet ); + break; +#ifdef HAVE_LIBVORBIS + case VLC_CODEC_VORBIS: + { + if( !VORBIS_HEADERS_VALID(p_stream) ) + { + msg_Err( p_demux, "missing vorbis headers, can't compute block size" ); + break; + } + long i_blocksize = vorbis_packet_blocksize( + p_stream->special.vorbis.p_info, &dumb_packet ); + if ( i_prev_blocksize ) + p_block->i_nb_samples = ( i_blocksize + i_prev_blocksize ) / 4; + else + p_block->i_nb_samples = i_blocksize / 2; + i_prev_blocksize = i_blocksize; + } +#endif } + } - /* An Ogg/vorbis packet contains an end date granulepos */ - if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS || - p_stream->fmt.i_codec == VLC_CODEC_SPEEX || - p_stream->fmt.i_codec == VLC_CODEC_FLAC ) + // PASS 2 + bool b_fixed = false; + for( int i=p_stream->prepcr.i_used - 1; i>=0; i-- ) + { + block_t *p_block = p_stream->prepcr.pp_blocks[i]; + switch( p_stream->fmt.i_codec ) { - if( ogg_stream_packetout( &p_stream->os, &oggpacket ) > 0 ) + case VLC_CODEC_SPEEX: + case VLC_CODEC_OPUS: + case VLC_CODEC_VORBIS: + pagestamp -= CLOCK_FREQ * p_block->i_nb_samples / p_stream->f_rate; + if ( pagestamp < 0 ) { - Ogg_DecodePacket( p_demux, p_stream, &oggpacket ); + p_block->i_pts = VLC_TS_INVALID; + p_block->i_flags |= BLOCK_FLAG_PREROLL; } else + p_block->i_pts = VLC_TS_0 + p_sys->i_nzpcr_offset + pagestamp; + b_fixed = true; + break; + default: + if ( p_stream->fmt.i_cat == VIDEO_ES ) { - es_out_Control( p_demux->out, ES_OUT_SET_PCR, - VLC_TS_0 + p_stream->i_pcr ); + pagestamp = pagestamp - ( CLOCK_FREQ / p_stream->f_rate ); + p_block->i_pts = p_sys->i_nzpcr_offset + pagestamp; + b_fixed = true; } - continue; } } - Ogg_DecodePacket( p_demux, p_stream, &oggpacket ); + if ( b_fixed ) + { + if ( pagestamp < 0 ) pagestamp = 0; + p_stream->i_pcr = VLC_TS_0 + pagestamp; + p_stream->i_pcr += p_sys->i_nzpcr_offset; + p_stream->i_previous_granulepos = ogg_page_granulepos( &p_sys->current_page ); + } + + FREENULL(p_stream->prepcr.pp_blocks); + p_stream->prepcr.i_used = 0; + + Ogg_SendOrQueueBlocks( p_demux, p_stream, NULL ); + + } + + int64_t i_pagestamp = Oggseek_GranuleToAbsTimestamp( p_stream, + ogg_page_granulepos( &p_sys->current_page ), false ); + if ( i_pagestamp > -1 ) + { + p_stream->i_pcr = VLC_TS_0 + i_pagestamp; + p_stream->i_pcr += p_sys->i_nzpcr_offset; } if( !p_sys->b_page_waiting ) @@ -374,24 +616,119 @@ static int Demux( demux_t * p_demux ) /* if a page was waiting, it's now processed */ p_sys->b_page_waiting = false; - p_sys->i_pcr = -1; + if ( p_sys->p_skelstream && !p_sys->p_skelstream->b_finished ) + p_sys->b_preparsing_done = false; + else + p_sys->b_preparsing_done = true; + + /* We will consider the lowest PCR among tracks, because the audio core badly + * handles PCR rewind (mute) + */ + mtime_t i_pcr_candidate = VLC_TS_INVALID; for( i_stream = 0; i_stream < p_sys->i_streams; i_stream++ ) { logical_stream_t *p_stream = p_sys->pp_stream[i_stream]; + if ( p_sys->b_preparsing_done && p_stream->b_initializing ) + { + /* We have 1 or more streams needing more than 1 page for preparsing */ + p_sys->b_preparsing_done = false; + } + if( p_stream->fmt.i_cat == SPU_ES ) continue; - if( p_stream->i_interpolated_pcr < 0 ) + if( p_stream->i_pcr < VLC_TS_0 ) continue; + if ( p_stream->b_finished || p_stream->b_initializing ) + continue; + if ( p_stream->p_preparse_block ) + continue; + if( i_pcr_candidate < VLC_TS_0 + || p_stream->i_pcr <= i_pcr_candidate ) + { + i_pcr_candidate = p_stream->i_pcr; + } + } + + if ( i_pcr_candidate > VLC_TS_INVALID && p_sys->i_pcr != i_pcr_candidate ) + { + if ( p_sys->i_streams == 1 && p_sys->i_access_delay ) + { + int64_t i_pcr_jitter = i_pcr_candidate - p_sys->i_pcr; + if ( i_pcr_jitter > p_sys->i_pcr_jitter ) + { + p_sys->i_pcr_jitter = i_pcr_jitter; + if ( p_sys->i_access_delay < i_pcr_jitter ) + msg_Warn( p_demux, "Consider increasing access caching variable from %"PRId64" to >%"PRId64, + p_sys->i_access_delay / 1000, i_pcr_jitter / 1000 ); + } + } + + if( ! b_skipping && p_sys->b_preparsing_done ) + { + p_sys->i_pcr = i_pcr_candidate; + es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_sys->i_pcr ); + } + } - if( p_sys->i_pcr < 0 || p_stream->i_interpolated_pcr < p_sys->i_pcr ) - p_sys->i_pcr = p_stream->i_interpolated_pcr; + return VLC_DEMUXER_SUCCESS; +} + +static void Ogg_ResetStream( logical_stream_t *p_stream ) +{ +#ifdef HAVE_LIBVORBIS + if ( p_stream->fmt.i_codec == VLC_CODEC_VORBIS ) + { + p_stream->special.vorbis.i_prev_blocksize = 0; } +#endif + /* we'll trash all the data until we find the next pcr */ + p_stream->b_reinit = true; + p_stream->i_pcr = VLC_TS_UNKNOWN; + p_stream->i_previous_granulepos = -1; + p_stream->i_previous_pcr = VLC_TS_UNKNOWN; + ogg_stream_reset( &p_stream->os ); + FREENULL( p_stream->prepcr.pp_blocks ); + p_stream->prepcr.i_size = 0; + p_stream->prepcr.i_used = 0; +} - if( p_sys->i_pcr >= 0 && ! b_skipping ) - es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_sys->i_pcr ); +static void Ogg_ResetStreamsHelper( demux_sys_t *p_sys ) +{ + for( int i = 0; i < p_sys->i_streams; i++ ) + Ogg_ResetStream( p_sys->pp_stream[i] ); - return 1; + ogg_sync_reset( &p_sys->oy ); + p_sys->i_pcr = VLC_TS_UNKNOWN; +} + +static logical_stream_t * Ogg_GetSelectedStream( demux_t *p_demux ) +{ + demux_sys_t *p_sys = p_demux->p_sys; + logical_stream_t *p_stream = NULL; + for( int i=0; ii_streams; i++ ) + { + logical_stream_t *p_candidate = p_sys->pp_stream[i]; + if ( !p_candidate->p_es ) continue; + + bool b_selected = false; + es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, + p_candidate->p_es, &b_selected ); + if ( !b_selected ) continue; + + if ( !p_stream && p_candidate->fmt.i_cat == AUDIO_ES ) + { + p_stream = p_candidate; + continue; /* Try to find video anyway */ + } + + if ( p_candidate->fmt.i_cat == VIDEO_ES ) + { + p_stream = p_candidate; + break; + } + } + return p_stream; } /***************************************************************************** @@ -401,9 +738,9 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) { demux_sys_t *p_sys = p_demux->p_sys; vlc_meta_t *p_meta; - int64_t *pi64; - bool *pb_bool; - int i; + int64_t *pi64, i64; + double *pf, f; + bool *pb_bool, b; switch( i_query ) { @@ -424,7 +761,23 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) return VLC_SUCCESS; case DEMUX_SET_TIME: - return VLC_EGENERIC; + i64 = (int64_t)va_arg( args, int64_t ); + logical_stream_t *p_stream = Ogg_GetSelectedStream( p_demux ); + if ( !p_stream ) + { + msg_Err( p_demux, "No selected seekable stream found" ); + return VLC_EGENERIC; + } + stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b ); + if ( Oggseek_BlindSeektoAbsoluteTime( p_demux, p_stream, i64, b ) ) + { + Ogg_ResetStreamsHelper( p_sys ); + es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, + VLC_TS_0 + i64 ); + return VLC_SUCCESS; + } + else + return VLC_EGENERIC; case DEMUX_GET_ATTACHMENTS: { @@ -436,34 +789,138 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) return VLC_EGENERIC; *pi_int = p_sys->i_attachments; - *ppp_attach = xmalloc( sizeof(input_attachment_t**) * p_sys->i_attachments ); + *ppp_attach = xmalloc( sizeof(input_attachment_t*) * p_sys->i_attachments ); for( int i = 0; i < p_sys->i_attachments; i++ ) (*ppp_attach)[i] = vlc_input_attachment_Duplicate( p_sys->attachments[i] ); return VLC_SUCCESS; } + case DEMUX_GET_POSITION: + pf = (double*)va_arg( args, double * ); + if( p_sys->i_length > 0 ) + { + *pf = (double) p_sys->i_pcr / + (double) ( p_sys->i_length * (mtime_t)1000000 ); + } + else if( stream_Size( p_demux->s ) > 0 ) + { + i64 = stream_Tell( p_demux->s ); + *pf = (double) i64 / stream_Size( p_demux->s ); + } + else *pf = 0.0; + return VLC_SUCCESS; + case DEMUX_SET_POSITION: /* forbid seeking if we haven't initialized all logical bitstreams yet; if we allowed, some headers would not get backed up and decoder init would fail, making that logical stream unusable */ - if( p_sys->i_bos > 0 ) + for ( int i=0; i< p_sys->i_streams; i++ ) + { + if ( p_sys->pp_stream[i]->b_initializing ) + return VLC_EGENERIC; + } + + p_stream = Ogg_GetSelectedStream( p_demux ); + if ( !p_stream ) + { + msg_Err( p_demux, "No selected seekable stream found" ); + return VLC_EGENERIC; + } + + stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b ); + + f = (double)va_arg( args, double ); + if ( p_sys->i_length <= 0 || !b /* || ! ACCESS_CAN_FASTSEEK */ ) + { + Ogg_ResetStreamsHelper( p_sys ); + Oggseek_BlindSeektoPosition( p_demux, p_stream, f, b ); + return VLC_SUCCESS; + } + + assert( p_sys->i_length > 0 ); + i64 = CLOCK_FREQ * p_sys->i_length * f; + Ogg_ResetStreamsHelper( p_sys ); + if ( Oggseek_SeektoAbsolutetime( p_demux, p_stream, i64 ) >= 0 ) + { + es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, + VLC_TS_0 + i64 ); + return VLC_SUCCESS; + } + + return VLC_EGENERIC; + + case DEMUX_GET_LENGTH: + if ( p_sys->i_length < 0 ) + return demux_vaControlHelper( p_demux->s, 0, -1, p_sys->i_bitrate, + 1, i_query, args ); + pi64 = (int64_t*)va_arg( args, int64_t * ); + *pi64 = p_sys->i_length * 1000000; + return VLC_SUCCESS; + + case DEMUX_GET_TITLE_INFO: + { + input_title_t ***ppp_title = (input_title_t***)va_arg( args, input_title_t*** ); + int *pi_int = (int*)va_arg( args, int* ); + int *pi_title_offset = (int*)va_arg( args, int* ); + int *pi_seekpoint_offset = (int*)va_arg( args, int* ); + + if( p_sys->i_seekpoints > 0 ) { + *pi_int = 1; + *ppp_title = malloc( sizeof( input_title_t* ) ); + input_title_t *p_title = (*ppp_title)[0] = vlc_input_title_New(); + for( int i = 0; i < p_sys->i_seekpoints; i++ ) + { + seekpoint_t *p_seekpoint_copy = vlc_seekpoint_Duplicate( p_sys->pp_seekpoints[i] ); + if ( likely( p_seekpoint_copy ) ) + TAB_APPEND( p_title->i_seekpoint, p_title->seekpoint, p_seekpoint_copy ); + } + *pi_title_offset = 0; + *pi_seekpoint_offset = 0; + } + return VLC_SUCCESS; + } + case DEMUX_SET_TITLE: + { + const int i_title = (int)va_arg( args, int ); + if( i_title > 1 ) + return VLC_EGENERIC; + return VLC_SUCCESS; + } + case DEMUX_SET_SEEKPOINT: + { + const int i_seekpoint = (int)va_arg( args, int ); + if( i_seekpoint > p_sys->i_seekpoints ) return VLC_EGENERIC; + + for ( int i=0; i< p_sys->i_streams; i++ ) + { + if ( p_sys->pp_stream[i]->b_initializing ) + return VLC_EGENERIC; } - for( i = 0; i < p_sys->i_streams; i++ ) + i64 = p_sys->pp_seekpoints[i_seekpoint]->i_time_offset; + + p_stream = Ogg_GetSelectedStream( p_demux ); + if ( !p_stream ) { - logical_stream_t *p_stream = p_sys->pp_stream[i]; + msg_Err( p_demux, "No selected seekable stream found" ); + return VLC_EGENERIC; + } - /* we'll trash all the data until we find the next pcr */ - p_stream->b_reinit = true; - p_stream->i_pcr = -1; - p_stream->i_interpolated_pcr = -1; - ogg_stream_reset( &p_stream->os ); + stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b ); + if ( Oggseek_BlindSeektoAbsoluteTime( p_demux, p_stream, i64, b ) ) + { + Ogg_ResetStreamsHelper( p_sys ); + es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, + VLC_TS_0 + i64 ); + p_demux->info.i_update |= INPUT_UPDATE_SEEKPOINT; + p_demux->info.i_seekpoint = i_seekpoint; + return VLC_SUCCESS; } - ogg_sync_reset( &p_sys->oy ); - /* XXX The break/return is missing on purpose as - * demux_vaControlHelper will do the last part of the job */ + else + return VLC_EGENERIC; + } default: return demux_vaControlHelper( p_demux->s, 0, -1, p_sys->i_bitrate, @@ -501,51 +958,173 @@ static int Ogg_ReadPage( demux_t *p_demux, ogg_page *p_oggpage ) * Ogg_UpdatePCR: update the PCR (90kHz program clock reference) for the * current stream. ****************************************************************************/ -static void Ogg_UpdatePCR( logical_stream_t *p_stream, +static void Ogg_UpdatePCR( demux_t *p_demux, logical_stream_t *p_stream, ogg_packet *p_oggpacket ) { + demux_sys_t *p_ogg = p_demux->p_sys; + p_stream->i_end_trim = 0; + /* Convert the granulepos into a pcr */ - if( p_oggpacket->granulepos >= 0 ) + if ( p_oggpacket->granulepos == 0 ) + { + /* We're in headers, and we haven't parsed 1st data packet yet */ +// p_stream->i_pcr = VLC_TS_UNKNOWN; + } + else if( p_oggpacket->granulepos > 0 ) { if( p_stream->fmt.i_codec == VLC_CODEC_THEORA || - p_stream->fmt.i_codec == VLC_CODEC_KATE ) + p_stream->fmt.i_codec == VLC_CODEC_DAALA || + p_stream->fmt.i_codec == VLC_CODEC_KATE || + p_stream->fmt.i_codec == VLC_CODEC_VP8 || + p_stream->fmt.i_codec == VLC_CODEC_DIRAC || + p_stream->fmt.i_codec == VLC_CODEC_SPEEX || + (p_stream->b_oggds && p_stream->fmt.i_cat == VIDEO_ES) ) { - ogg_int64_t iframe = p_oggpacket->granulepos >> - p_stream->i_granule_shift; - ogg_int64_t pframe = p_oggpacket->granulepos - - ( iframe << p_stream->i_granule_shift ); + p_stream->i_pcr = VLC_TS_0 + Oggseek_GranuleToAbsTimestamp( p_stream, + p_oggpacket->granulepos, true ); + p_stream->i_pcr += p_ogg->i_nzpcr_offset; + } + else if ( p_stream->i_previous_granulepos > 0 ) + { + ogg_int64_t sample = p_stream->i_previous_granulepos; - p_stream->i_pcr = ( iframe + pframe - p_stream->i_keyframe_offset ) - * INT64_C(1000000) / p_stream->f_rate; + if( p_stream->fmt.i_codec == VLC_CODEC_OPUS && p_oggpacket->e_o_s ) + { + int duration = Ogg_OpusPacketDuration( p_oggpacket ); + if( duration > 0 ) + { + ogg_int64_t end_sample = p_oggpacket->granulepos; + if( end_sample < ( sample + duration ) ) + p_stream->i_end_trim = sample + duration - end_sample; + } + } + + if (sample >= p_stream->i_pre_skip) + sample -= p_stream->i_pre_skip; + else + sample = 0; + + p_stream->i_pcr = VLC_TS_0 + sample * CLOCK_FREQ / p_stream->f_rate; + p_stream->i_pcr += p_ogg->i_nzpcr_offset; } - else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC ) + + } + else if ( p_oggpacket->granulepos == -1 ) + { + int i_duration; + /* no granulepos available, try to interpolate the pcr. + * If we can't then don't touch the old value. */ + if( p_stream->fmt.i_cat == VIDEO_ES && p_stream->i_pcr > VLC_TS_INVALID ) { - 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; + p_stream->i_pcr += (CLOCK_FREQ / p_stream->f_rate); } - else +#ifdef HAVE_LIBVORBIS + else if ( p_stream->fmt.i_codec == VLC_CODEC_VORBIS && + p_stream->special.vorbis.p_info && + VORBIS_HEADERS_VALID(p_stream) && + p_stream->i_previous_granulepos > 0 ) + { + long i_blocksize = vorbis_packet_blocksize( + p_stream->special.vorbis.p_info, p_oggpacket ); + if ( p_stream->special.vorbis.i_prev_blocksize ) + i_duration = ( i_blocksize + p_stream->special.vorbis.i_prev_blocksize ) / 4; + else + i_duration = i_blocksize / 2; + p_stream->special.vorbis.i_prev_blocksize = i_blocksize; + /* duration in samples per channel */ + p_oggpacket->granulepos = p_stream->i_previous_granulepos + i_duration; + p_stream->i_pcr = p_stream->i_previous_granulepos * + CLOCK_FREQ / p_stream->special.vorbis.p_info->rate; + p_stream->i_pcr += p_ogg->i_nzpcr_offset; + } +#endif + else if ( p_stream->fmt.i_codec == VLC_CODEC_SPEEX && + p_stream->i_previous_granulepos > 0 ) + { + i_duration = p_stream->special.speex.i_framesize * + p_stream->special.speex.i_framesperpacket; + p_oggpacket->granulepos = p_stream->i_previous_granulepos + i_duration; + p_stream->i_pcr = VLC_TS_0 + Oggseek_GranuleToAbsTimestamp( p_stream, + p_stream->i_previous_granulepos, false ); + p_stream->i_pcr += p_ogg->i_nzpcr_offset; + } + else if( p_stream->fmt.i_codec == VLC_CODEC_OPUS && + p_stream->i_previous_granulepos > 0 && + ( i_duration = + Ogg_OpusPacketDuration( p_oggpacket ) ) > 0 ) + { + ogg_int64_t sample; + p_oggpacket->granulepos = p_stream->i_previous_granulepos + i_duration; + sample = p_stream->i_previous_granulepos; + if (sample >= p_stream->i_pre_skip) + sample -= p_stream->i_pre_skip; + else + sample = 0; + + p_stream->i_pcr = VLC_TS_0 + sample * CLOCK_FREQ / p_stream->f_rate; + p_stream->i_pcr += p_ogg->i_nzpcr_offset; + } + else if( p_stream->fmt.i_bitrate && p_stream->i_pcr > VLC_TS_UNKNOWN ) { - p_stream->i_pcr = p_oggpacket->granulepos * INT64_C(1000000) - / p_stream->f_rate; + p_stream->i_pcr += ( CLOCK_FREQ * p_oggpacket->bytes / + p_stream->fmt.i_bitrate / 8 ); } + } - p_stream->i_pcr += 1; - p_stream->i_interpolated_pcr = p_stream->i_pcr; + p_stream->i_previous_granulepos = p_oggpacket->granulepos; +} + +static void Ogg_SendOrQueueBlocks( demux_t *p_demux, logical_stream_t *p_stream, + block_t *p_block ) +{ + demux_sys_t *p_ogg = p_demux->p_sys; + if ( !p_stream->p_es || p_stream->prepcr.pp_blocks || p_stream->i_pcr == VLC_TS_UNKNOWN ) + { + if ( !p_block ) return; + if ( p_stream->prepcr.pp_blocks ) + { + assert( p_stream->prepcr.i_size ); + p_stream->prepcr.pp_blocks[p_stream->prepcr.i_used++] = p_block; + } + DemuxDebug( msg_Dbg( p_demux, "block prepcr append > pts %"PRId64" spcr %"PRId64" pcr %"PRId64, + p_block->i_pts, p_stream->i_pcr, p_ogg->i_pcr ); ) + block_ChainAppend( & p_stream->p_preparse_block, p_block ); } else { - p_stream->i_pcr = -1; + /* Because ES creation is delayed for preparsing */ + mtime_t i_firstpts = VLC_TS_UNKNOWN; + if ( p_stream->p_preparse_block ) + { + block_t *temp = p_stream->p_preparse_block; + while ( temp ) + { + if ( temp && i_firstpts < VLC_TS_0 ) + i_firstpts = temp->i_pts; - /* no granulepos available, try to interpolate the pcr. - * If we can't then don't touch the old value. */ - if( p_stream->fmt.i_cat == VIDEO_ES ) - /* 1 frame per packet */ - p_stream->i_interpolated_pcr += (INT64_C(1000000) / p_stream->f_rate); - else if( p_stream->fmt.i_bitrate ) - p_stream->i_interpolated_pcr += - ( p_oggpacket->bytes * INT64_C(1000000) / - p_stream->fmt.i_bitrate / 8 ); + block_t *tosend = temp; + temp = temp->p_next; + tosend->p_next = NULL; + + DemuxDebug( msg_Dbg( p_demux, "block sent from preparse > pts %"PRId64" spcr %"PRId64" pcr %"PRId64, + tosend->i_pts, p_stream->i_pcr, p_ogg->i_pcr ); ) + es_out_Send( p_demux->out, p_stream->p_es, tosend ); + + if ( p_ogg->i_pcr < VLC_TS_0 && i_firstpts > VLC_TS_INVALID ) + { + p_ogg->i_pcr = i_firstpts; + es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_ogg->i_pcr ); + } + } + p_stream->p_preparse_block = NULL; + } + + if ( p_block ) + { + DemuxDebug( msg_Dbg( p_demux, "block sent directly > pts %"PRId64" spcr %"PRId64" pcr %"PRId64, + p_block->i_pts, p_stream->i_pcr, p_ogg->i_pcr ) ); + es_out_Send( p_demux->out, p_stream->p_es, p_block ); + } } } @@ -559,15 +1138,6 @@ static void Ogg_DecodePacket( demux_t *p_demux, block_t *p_block; bool b_selected; int i_header_len = 0; - mtime_t i_pts = -1, i_interpolated_pts; - demux_sys_t *p_ogg = p_demux->p_sys; - - /* Sanity check */ - if( !p_oggpacket->bytes ) - { - msg_Dbg( p_demux, "discarding 0 sized packet" ); - return; - } if( p_oggpacket->bytes >= 7 && ! memcmp ( p_oggpacket->packet, "Annodex", 7 ) ) @@ -581,13 +1151,35 @@ static void Ogg_DecodePacket( demux_t *p_demux, /* it's an AnxData packet -- skip it (do nothing) */ return; } + else if( p_oggpacket->bytes >= 8 && + ! memcmp ( p_oggpacket->packet, "fisbone", 8 ) ) + { + Ogg_ReadSkeletonBones( p_demux, p_oggpacket ); + return; + } + else if( p_oggpacket->bytes >= 6 && + ! memcmp ( p_oggpacket->packet, "index", 6 ) ) + { + Ogg_ReadSkeletonIndex( p_demux, p_oggpacket ); + return; + } + else if( p_stream->fmt.i_codec == VLC_CODEC_VP8 && + p_oggpacket->bytes >= 7 && + !memcmp( p_oggpacket->packet, "OVP80\x02\x20", 7 ) ) + { + Ogg_ReadVP8Header( p_demux, p_stream, p_oggpacket ); + return; + } - if( p_stream->fmt.i_codec == VLC_CODEC_SUBT && + if( p_stream->fmt.i_codec == VLC_CODEC_SUBT && p_oggpacket->bytes > 0 && p_oggpacket->packet[0] & PACKET_TYPE_BITS ) return; /* Check the ES is selected */ - es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, - p_stream->p_es, &b_selected ); + if ( !p_stream->p_es ) + b_selected = true; + else + es_out_Control( p_demux->out, ES_OUT_GET_ES_STATE, + p_stream->p_es, &b_selected ); if( p_stream->b_force_backup ) { @@ -596,9 +1188,31 @@ static void Ogg_DecodePacket( demux_t *p_demux, switch( p_stream->fmt.i_codec ) { case VLC_CODEC_VORBIS: - case VLC_CODEC_SPEEX: +#ifdef HAVE_LIBVORBIS + Ogg_DecodeVorbisHeader( p_stream, p_oggpacket, p_stream->i_packets_backup ); +#endif + //ft case VLC_CODEC_THEORA: - if( p_stream->i_packets_backup == 3 ) p_stream->b_force_backup = 0; + if( p_stream->i_packets_backup == 3 ) + p_stream->b_force_backup = false; + b_xiph = true; + break; + + case VLC_CODEC_DAALA: + if( p_stream->i_packets_backup == 3 ) + p_stream->b_force_backup = false; + b_xiph = true; + break; + + case VLC_CODEC_SPEEX: + if( p_stream->i_packets_backup == 2 + p_stream->i_extra_headers_packets ) + p_stream->b_force_backup = false; + b_xiph = true; + break; + + case VLC_CODEC_OPUS: + if( p_stream->i_packets_backup == 2 ) + p_stream->b_force_backup = false; b_xiph = true; break; @@ -606,11 +1220,11 @@ static void Ogg_DecodePacket( demux_t *p_demux, if( !p_stream->fmt.audio.i_rate && p_stream->i_packets_backup == 2 ) { Ogg_ReadFlacHeader( p_demux, p_stream, p_oggpacket ); - p_stream->b_force_backup = 0; + p_stream->b_force_backup = false; } else if( p_stream->fmt.audio.i_rate ) { - p_stream->b_force_backup = 0; + p_stream->b_force_backup = false; if( p_oggpacket->bytes >= 9 ) { p_oggpacket->packet += 9; @@ -621,12 +1235,13 @@ static void Ogg_DecodePacket( demux_t *p_demux, break; case VLC_CODEC_KATE: - if( p_stream->i_packets_backup == p_stream->i_kate_num_headers ) p_stream->b_force_backup = 0; + if( p_stream->i_packets_backup == p_stream->special.kate.i_num_headers ) + p_stream->b_force_backup = false; b_xiph = true; break; default: - p_stream->b_force_backup = 0; + p_stream->b_force_backup = false; b_xiph = false; break; } @@ -671,80 +1286,24 @@ static void Ogg_DecodePacket( demux_t *p_demux, else p_stream->fmt.i_extra = 0; - if( Ogg_LogicalStreamResetEsFormat( p_demux, p_stream ) ) - es_out_Control( p_demux->out, ES_OUT_SET_ES_FMT, - p_stream->p_es, &p_stream->fmt ); - if( p_stream->i_headers > 0 ) - Ogg_ExtractMeta( p_demux, p_stream->fmt.i_codec, + Ogg_ExtractMeta( p_demux, & p_stream->fmt, p_stream->p_headers, p_stream->i_headers ); /* we're not at BOS anymore for this logical stream */ - p_ogg->i_bos--; + p_stream->b_initializing = false; } } b_selected = false; /* Discard the header packet */ } - - /* Convert the pcr into a pts */ - if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS || - p_stream->fmt.i_codec == VLC_CODEC_SPEEX || - p_stream->fmt.i_codec == VLC_CODEC_FLAC ) + else { - if( p_stream->i_pcr >= 0 ) - { - /* This is for streams where the granulepos of the header packets - * doesn't match these of the data packets (eg. ogg web radios). */ - if( p_stream->i_previous_pcr == 0 && - p_stream->i_pcr > 3 * DEFAULT_PTS_DELAY ) - { - es_out_Control( p_demux->out, ES_OUT_RESET_PCR ); - - /* Call the pace control */ - es_out_Control( p_demux->out, ES_OUT_SET_PCR, - VLC_TS_0 + p_stream->i_pcr ); - } - - p_stream->i_previous_pcr = p_stream->i_pcr; - - /* The granulepos is the end date of the sample */ - i_pts = p_stream->i_pcr; - } + p_stream->b_initializing = false; } /* Convert the granulepos into the next pcr */ - i_interpolated_pts = p_stream->i_interpolated_pcr; - Ogg_UpdatePCR( p_stream, p_oggpacket ); - - /* SPU streams are typically discontinuous, do not mind large gaps */ - if( p_stream->fmt.i_cat != SPU_ES ) - { - if( p_stream->i_pcr >= 0 ) - { - /* This is for streams where the granulepos of the header packets - * doesn't match these of the data packets (eg. ogg web radios). */ - if( p_stream->i_previous_pcr == 0 && - p_stream->i_pcr > 3 * DEFAULT_PTS_DELAY ) - { - es_out_Control( p_demux->out, ES_OUT_RESET_PCR ); - - /* Call the pace control */ - es_out_Control( p_demux->out, ES_OUT_SET_PCR, VLC_TS_0 + p_stream->i_pcr ); - } - } - } - - if( p_stream->fmt.i_codec != VLC_CODEC_VORBIS && - p_stream->fmt.i_codec != VLC_CODEC_SPEEX && - p_stream->fmt.i_codec != VLC_CODEC_FLAC && - p_stream->i_pcr >= 0 ) - { - p_stream->i_previous_pcr = p_stream->i_pcr; - - /* The granulepos is the start date of the sample */ - i_pts = p_stream->i_pcr; - } + Ogg_UpdatePCR( p_demux, p_stream, p_oggpacket ); if( !b_selected ) { @@ -753,74 +1312,109 @@ static void Ogg_DecodePacket( demux_t *p_demux, return; } - if( p_oggpacket->bytes <= 0 ) - return; + if( !( p_block = block_Alloc( p_oggpacket->bytes ) ) ) return; + p_block->i_pts = p_stream->i_pcr; - if( !( p_block = block_New( p_demux, p_oggpacket->bytes ) ) ) return; + DemuxDebug( msg_Dbg(p_demux, "block set from granule %"PRId64" to pts/pcr %"PRId64" skip %d", + p_oggpacket->granulepos, p_stream->i_pcr, p_stream->i_skip_frames); ) + if( p_stream->fmt.i_codec == VLC_CODEC_OPUS ) + p_block->i_nb_samples = Ogg_OpusPacketDuration( p_oggpacket ); - /* may need to preroll video frames after a seek */ + /* may need to preroll after a seek or in case of preskip */ if ( p_stream->i_skip_frames > 0 ) { - p_block->i_flags |= BLOCK_FLAG_PREROLL; - p_stream->i_skip_frames--; + if( p_stream->fmt.i_codec == VLC_CODEC_OPUS ) + { + if( p_stream->i_skip_frames >= p_block->i_nb_samples ) + { + p_block->i_flags |= BLOCK_FLAG_PREROLL; + p_stream->i_skip_frames -= p_block->i_nb_samples; + p_block->i_nb_samples = 0; + } + else + { + p_block->i_nb_samples -= p_stream->i_skip_frames; + p_stream->i_skip_frames = 0; + } + } + else + { + p_block->i_flags |= BLOCK_FLAG_PREROLL; + p_stream->i_skip_frames--; + } } - - /* Normalize PTS */ - if( i_pts == 0 ) i_pts = VLC_TS_0; - else if( i_pts == -1 && i_interpolated_pts == 0 ) i_pts = VLC_TS_0; - else if( i_pts == -1 ) i_pts = VLC_TS_INVALID; - - if( p_stream->fmt.i_cat == AUDIO_ES ) - p_block->i_dts = p_block->i_pts = i_pts; - else if( p_stream->fmt.i_cat == SPU_ES ) + /* Conditional block fixes */ + if ( p_stream->fmt.i_cat == VIDEO_ES && + Ogg_IsKeyFrame( p_stream, p_oggpacket ) ) { - p_block->i_dts = p_block->i_pts = i_pts; - p_block->i_length = 0; + p_block->i_flags |= BLOCK_FLAG_TYPE_I; } - else if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ) + else if( p_stream->fmt.i_cat == AUDIO_ES ) { - p_block->i_dts = p_block->i_pts = i_pts; - if( (p_oggpacket->granulepos & ((1<i_granule_shift)-1)) == 0 ) + if ( p_stream->fmt.i_codec == VLC_CODEC_FLAC && + p_stream->p_es && 0 >= p_oggpacket->granulepos && + p_stream->fmt.b_packetized ) { - p_block->i_flags |= BLOCK_FLAG_TYPE_I; + /* Handle OggFlac spec violation (multiple frame/packet + * by turning on packetizer */ + msg_Warn( p_demux, "Invalid FLAC in ogg detected. Restarting ES with packetizer." ); + p_stream->fmt.b_packetized = false; + es_out_Del( p_demux->out, p_stream->p_es ); + p_stream->p_es = es_out_Add( p_demux->out, &p_stream->fmt ); } + + /* Blatant abuse of the i_length field. */ + p_block->i_length = p_stream->i_end_trim; } - else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC ) + else if( p_stream->fmt.i_cat == SPU_ES ) { - 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 = VLC_TS_INVALID; - /* NB, OggDirac granulepos values are in units of 2*picturerate */ - - /* granulepos for dirac is possibly broken, this value should be ignored */ - if( -1 != p_oggpacket->granulepos ) - p_block->i_pts = u_pnum * INT64_C(1000000) / p_stream->f_rate / 2; + p_block->i_length = 0; } - else + else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC ) { - p_block->i_dts = i_pts; - p_block->i_pts = VLC_TS_INVALID; + ogg_int64_t nzdts = Oggseek_GranuleToAbsTimestamp( p_stream, p_oggpacket->granulepos, false ); + ogg_int64_t nzpts = Oggseek_GranuleToAbsTimestamp( p_stream, p_oggpacket->granulepos, true ); + p_block->i_dts = ( nzdts > VLC_TS_INVALID ) ? VLC_TS_0 + nzdts : nzdts; + p_block->i_pts = ( nzpts > VLC_TS_INVALID ) ? VLC_TS_0 + nzpts : nzpts; + /* granulepos for dirac is possibly broken, this value should be ignored */ + if( 0 >= p_oggpacket->granulepos ) + { + p_block->i_pts = VLC_TS_INVALID; + p_block->i_dts = p_stream->i_pcr; + } } if( p_stream->fmt.i_codec != VLC_CODEC_VORBIS && p_stream->fmt.i_codec != VLC_CODEC_SPEEX && + p_stream->fmt.i_codec != VLC_CODEC_OPUS && + p_stream->fmt.i_codec != VLC_CODEC_VP8 && p_stream->fmt.i_codec != VLC_CODEC_FLAC && p_stream->fmt.i_codec != VLC_CODEC_TARKIN && p_stream->fmt.i_codec != VLC_CODEC_THEORA && + p_stream->fmt.i_codec != VLC_CODEC_DAALA && p_stream->fmt.i_codec != VLC_CODEC_CMML && p_stream->fmt.i_codec != VLC_CODEC_DIRAC && p_stream->fmt.i_codec != VLC_CODEC_KATE ) { + if( p_oggpacket->bytes <= 0 ) + { + msg_Dbg( p_demux, "discarding 0 sized packet" ); + block_Release( p_block ); + return; + } /* We remove the header from the packet */ i_header_len = (*p_oggpacket->packet & PACKET_LEN_BITS01) >> 6; i_header_len |= (*p_oggpacket->packet & PACKET_LEN_BITS2) << 1; + if( i_header_len >= p_oggpacket->bytes ) + { + msg_Dbg( p_demux, "discarding invalid packet" ); + block_Release( p_block ); + return; + } + if( p_stream->fmt.i_codec == VLC_CODEC_SUBT) { /* But with subtitles we need to retrieve the duration first */ @@ -851,6 +1445,7 @@ static void Ogg_DecodePacket( demux_t *p_demux, p_block->i_buffer = 0; } + if( p_stream->fmt.i_codec == VLC_CODEC_TARKIN ) { /* FIXME: the biggest hack I've ever done */ @@ -862,7 +1457,12 @@ static void Ogg_DecodePacket( demux_t *p_demux, memcpy( p_block->p_buffer, p_oggpacket->packet + i_header_len, p_oggpacket->bytes - i_header_len ); - es_out_Send( p_demux->out, p_stream->p_es, p_block ); + Ogg_SendOrQueueBlocks( p_demux, p_stream, p_block ); +} + +static int Ogg_OpusPacketDuration( ogg_packet *p_oggpacket ) +{ + return opus_frame_duration(p_oggpacket->packet, p_oggpacket->bytes); } /**************************************************************************** @@ -879,7 +1479,6 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) { demux_sys_t *p_ogg = p_demux->p_sys ; ogg_packet oggpacket; - int i_stream; p_ogg->i_total_length = stream_Size ( p_demux->s ); msg_Dbg( p_demux, "File length is %"PRId64" bytes", p_ogg->i_total_length ); @@ -895,25 +1494,15 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) * We found the beginning of our first logical stream. */ while( ogg_page_bos( &p_ogg->current_page ) ) { - logical_stream_t *p_stream; - - p_stream = malloc( sizeof(logical_stream_t) ); - if( !p_stream ) + logical_stream_t *p_stream = calloc( 1, sizeof(logical_stream_t) ); + if( unlikely( !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->i_secondary_header_packets = 0; - - p_stream->i_keyframe_offset = 0; - p_stream->i_skip_frames = 0; - - p_stream->i_data_start = 0; - es_format_Init( &p_stream->fmt, 0, 0 ); es_format_Init( &p_stream->fmt_old, 0, 0 ); + p_stream->b_initializing = true; /* Setup the logical stream */ p_stream->i_serial_no = ogg_page_serialno( &p_ogg->current_page ); @@ -936,18 +1525,45 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) if( oggpacket.bytes >= 7 && ! memcmp( oggpacket.packet, "\x01vorbis", 7 ) ) { - Ogg_ReadVorbisHeader( p_stream, &oggpacket ); - msg_Dbg( p_demux, "found vorbis header" ); + if ( Ogg_ReadVorbisHeader( p_stream, &oggpacket ) ) + msg_Dbg( p_demux, "found vorbis header" ); + else + { + msg_Dbg( p_demux, "found invalid vorbis header" ); + Ogg_LogicalStreamDelete( p_demux, p_stream ); + p_stream = NULL; + p_ogg->i_streams--; + } } /* Check for Speex header */ else if( oggpacket.bytes >= 5 && ! memcmp( oggpacket.packet, "Speex", 5 ) ) { - Ogg_ReadSpeexHeader( p_stream, &oggpacket ); - msg_Dbg( p_demux, "found speex header, channels: %i, " - "rate: %i, bitrate: %i", + if ( Ogg_ReadSpeexHeader( p_stream, &oggpacket ) ) + msg_Dbg( p_demux, "found speex header, channels: %i, " + "rate: %i, bitrate: %i, frames: %i group %i", + p_stream->fmt.audio.i_channels, + (int)p_stream->f_rate, p_stream->fmt.i_bitrate, + p_stream->special.speex.i_framesize, + p_stream->special.speex.i_framesperpacket ); + else + { + msg_Dbg( p_demux, "found invalid Speex header" ); + Ogg_LogicalStreamDelete( p_demux, p_stream ); + p_stream = NULL; + p_ogg->i_streams--; + } + } + /* Check for Opus header */ + else if( oggpacket.bytes >= 8 && + ! memcmp( oggpacket.packet, "OpusHead", 8 ) ) + { + Ogg_ReadOpusHeader( p_stream, &oggpacket ); + msg_Dbg( p_demux, "found opus header, channels: %i, " + "pre-skip: %i", p_stream->fmt.audio.i_channels, - (int)p_stream->f_rate, p_stream->fmt.i_bitrate ); + (int)p_stream->i_pre_skip); + p_stream->i_skip_frames = p_stream->i_pre_skip; } /* Check for Flac header (< version 1.1.1) */ else if( oggpacket.bytes >= 4 && @@ -958,8 +1574,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) /* Grrrr!!!! Did they really have to put all the * important info in the second header packet!!! * (STREAMINFO metadata is in the following packet) */ - p_stream->b_force_backup = 1; - + p_stream->b_force_backup = true; p_stream->fmt.i_cat = AUDIO_ES; p_stream->fmt.i_codec = VLC_CODEC_FLAC; } @@ -975,22 +1590,50 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) oggpacket.packet[5], oggpacket.packet[6], i_packets ); - p_stream->b_force_backup = 1; + p_stream->b_force_backup = true; p_stream->fmt.i_cat = AUDIO_ES; p_stream->fmt.i_codec = VLC_CODEC_FLAC; oggpacket.packet += 13; oggpacket.bytes -= 13; - Ogg_ReadFlacHeader( p_demux, p_stream, &oggpacket ); + if ( !Ogg_ReadFlacHeader( p_demux, p_stream, &oggpacket ) ) + { + msg_Dbg( p_demux, "found invalid Flac header" ); + Ogg_LogicalStreamDelete( p_demux, p_stream ); + p_stream = NULL; + p_ogg->i_streams--; + } } /* Check for Theora header */ else if( oggpacket.bytes >= 7 && ! memcmp( oggpacket.packet, "\x80theora", 7 ) ) { - Ogg_ReadTheoraHeader( p_stream, &oggpacket ); - - msg_Dbg( p_demux, - "found theora header, bitrate: %i, rate: %f", - p_stream->fmt.i_bitrate, p_stream->f_rate ); + if ( Ogg_ReadTheoraHeader( p_stream, &oggpacket ) ) + msg_Dbg( p_demux, + "found theora header, bitrate: %i, rate: %f", + p_stream->fmt.i_bitrate, p_stream->f_rate ); + else + { + msg_Dbg( p_demux, "found invalid Theora header" ); + Ogg_LogicalStreamDelete( p_demux, p_stream ); + p_stream = NULL; + p_ogg->i_streams--; + } + } + /* Check for Daala header */ + else if( oggpacket.bytes >= 6 && + ! memcmp( oggpacket.packet, "\x80""daala", 6 ) ) + { + if ( Ogg_ReadDaalaHeader( p_stream, &oggpacket ) ) + msg_Dbg( p_demux, + "found daala header, bitrate: %i, rate: %f", + p_stream->fmt.i_bitrate, p_stream->f_rate ); + else + { + msg_Dbg( p_demux, "found invalid Daala header" ); + Ogg_LogicalStreamDelete( p_demux, p_stream ); + p_stream = NULL; + p_ogg->i_streams--; + } } /* Check for Dirac header */ else if( ( oggpacket.bytes >= 5 && @@ -1003,7 +1646,8 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) else { msg_Warn( p_demux, "found dirac header isn't decodable" ); - free( p_stream ); + Ogg_LogicalStreamDelete( p_demux, p_stream ); + p_stream = NULL; p_ogg->i_streams--; } } @@ -1027,36 +1671,60 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) "found tarkin header, bitrate: %i, rate: %f", p_stream->fmt.i_bitrate, p_stream->f_rate ); } + /* Check for VP8 header */ + else if( oggpacket.bytes >= 26 && + ! memcmp( oggpacket.packet, "OVP80", 5 ) ) + { + if ( Ogg_ReadVP8Header( p_demux, p_stream, &oggpacket ) ) + msg_Dbg( p_demux, "found VP8 header " + "fps: %f, width:%i; height:%i", + p_stream->f_rate, + p_stream->fmt.video.i_width, + p_stream->fmt.video.i_height ); + else + { + msg_Dbg( p_demux, "invalid VP8 header found"); + Ogg_LogicalStreamDelete( p_demux, p_stream ); + p_stream = NULL; + p_ogg->i_streams--; + } + } /* Check for Annodex header */ else if( oggpacket.bytes >= 7 && ! memcmp( oggpacket.packet, "Annodex", 7 ) ) { - Ogg_ReadAnnodexHeader( VLC_OBJECT(p_demux), p_stream, - &oggpacket ); + Ogg_ReadAnnodexHeader( p_demux, p_stream, &oggpacket ); /* kill annodex track */ - free( p_stream ); + FREENULL( p_stream ); p_ogg->i_streams--; } /* Check for Annodex header */ else if( oggpacket.bytes >= 7 && ! memcmp( oggpacket.packet, "AnxData", 7 ) ) { - Ogg_ReadAnnodexHeader( VLC_OBJECT(p_demux), p_stream, - &oggpacket ); + Ogg_ReadAnnodexHeader( p_demux, p_stream, &oggpacket ); } /* Check for Kate header */ 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" ); + if ( Ogg_ReadKateHeader( p_stream, &oggpacket ) ) + msg_Dbg( p_demux, "found kate header" ); + else + { + msg_Dbg( p_demux, "invalid kate header found"); + Ogg_LogicalStreamDelete( p_demux, p_stream ); + p_stream = NULL; + p_ogg->i_streams--; + } } + /* Check for OggDS */ else if( oggpacket.bytes >= 142 && !memcmp( &oggpacket.packet[1], "Direct Show Samples embedded in Ogg", 35 )) { /* Old header type */ - + p_stream->b_oggds = true; /* Check for video header (old format) */ if( GetDWLE((oggpacket.packet+96)) == 0x05589f80 && oggpacket.bytes >= 184 ) @@ -1073,8 +1741,10 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) p_stream->fmt.video.i_frame_rate = 10000000; p_stream->fmt.video.i_frame_rate_base = GetQWLE((oggpacket.packet+164)); + p_stream->fmt.video.i_frame_rate_base = + __MAX( p_stream->fmt.video.i_frame_rate_base, 1 ); p_stream->f_rate = 10000000.0 / - GetQWLE((oggpacket.packet+164)); + p_stream->fmt.video.i_frame_rate_base; p_stream->fmt.video.i_bits_per_pixel = GetWLE((oggpacket.packet+182)); if( !p_stream->fmt.video.i_bits_per_pixel ) @@ -1116,6 +1786,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) i_format_tag = GetWLE((oggpacket.packet+124)); p_stream->fmt.audio.i_channels = GetWLE((oggpacket.packet+126)); + fill_channels_info(&p_stream->fmt.audio); p_stream->f_rate = p_stream->fmt.audio.i_rate = GetDWLE((oggpacket.packet+128)); p_stream->fmt.i_bitrate = @@ -1145,22 +1816,31 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) p_stream->fmt.audio.i_rate, p_stream->fmt.audio.i_bitspersample, p_stream->fmt.i_bitrate / 1024 ); - + if ( p_stream->f_rate == 0 ) + { + msg_Dbg( p_demux, "invalid oggds audio header" ); + Ogg_LogicalStreamDelete( p_demux, p_stream ); + p_stream = NULL; + p_ogg->i_streams--; + } } else { msg_Dbg( p_demux, "stream %d has an old header " "but is of an unknown type", p_ogg->i_streams-1 ); - free( p_stream ); + FREENULL( p_stream ); p_ogg->i_streams--; } } - else if( (*oggpacket.packet & PACKET_TYPE_BITS ) == PACKET_TYPE_HEADER && - oggpacket.bytes >= 44+1 ) + /* Check for OggDS */ + else if( oggpacket.bytes >= 44+1 && + (*oggpacket.packet & PACKET_TYPE_BITS ) == PACKET_TYPE_HEADER ) { stream_header_t tmp; stream_header_t *st = &tmp; + p_stream->b_oggds = true; + memcpy( st->streamtype, &oggpacket.packet[1+0], 8 ); memcpy( st->subtype, &oggpacket.packet[1+8], 4 ); st->size = GetDWLE( &oggpacket.packet[1+12] ); @@ -1229,7 +1909,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) 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, + memcpy( p_stream->fmt.p_extra, oggpacket.packet + 57, p_stream->fmt.i_extra ); else p_stream->fmt.i_extra = 0; @@ -1239,6 +1919,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) p_buffer[4] = '\0'; i_format_tag = strtol(p_buffer,NULL,16); p_stream->fmt.audio.i_channels = st->sh.audio.channels; + fill_channels_info(&p_stream->fmt.audio); 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; @@ -1266,6 +1947,13 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) p_stream->fmt.audio.i_rate, p_stream->fmt.audio.i_bitspersample, p_stream->fmt.i_bitrate / 1024 ); + if ( p_stream->f_rate == 0 ) + { + msg_Dbg( p_demux, "invalid oggds audio header" ); + Ogg_LogicalStreamDelete( p_demux, p_stream ); + p_stream = NULL; + p_ogg->i_streams--; + } } /* Check for text (subtitles) header */ else if( !strncmp(st->streamtype, "text", 4) ) @@ -1273,7 +1961,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) /* We need to get rid of the header packet */ ogg_stream_packetout( &p_stream->os, &oggpacket ); - msg_Dbg( p_demux, "found text subtitles header" ); + msg_Dbg( p_demux, "found text subtitle header" ); p_stream->fmt.i_cat = SPU_ES; p_stream->fmt.i_codec = VLC_CODEC_SUBT; p_stream->f_rate = 1000; /* granulepos is in millisec */ @@ -1282,45 +1970,39 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) { msg_Dbg( p_demux, "stream %d has a header marker " "but is of an unknown type", p_ogg->i_streams-1 ); - free( p_stream ); + FREENULL( p_stream ); p_ogg->i_streams--; } } - else if( oggpacket.bytes >= 7 && - ! memcmp( oggpacket.packet, "fishead", 7 ) ) + else if( oggpacket.bytes >= 8 && + ! memcmp( oggpacket.packet, "fishead\0", 8 ) ) { /* Skeleton */ msg_Dbg( p_demux, "stream %d is a skeleton", p_ogg->i_streams-1 ); - /* FIXME: https://trac.videolan.org/vlc/ticket/1412 */ + Ogg_ReadSkeletonHeader( p_demux, p_stream, &oggpacket ); } else { msg_Dbg( p_demux, "stream %d is of unknown type", p_ogg->i_streams-1 ); - free( p_stream ); + FREENULL( p_stream ); p_ogg->i_streams--; } + /* we'll need to get all headers */ + if ( p_stream ) + p_stream->b_initializing &= p_stream->b_force_backup; + if( Ogg_ReadPage( p_demux, &p_ogg->current_page ) != VLC_SUCCESS ) return VLC_EGENERIC; } - /* we'll need to get all headers for all of those streams - that we have to backup headers for */ - p_ogg->i_bos = 0; - for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ ) - { - if( p_ogg->pp_stream[i_stream]->b_force_backup ) - p_ogg->i_bos++; - } - - /* This is the first data page, which means we are now finished * with the initial pages. We just need to store it in the relevant * bitstream. */ - for( i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ ) + for( int i_stream = 0; i_stream < p_ogg->i_streams; i_stream++ ) { if( ogg_stream_pagein( &p_ogg->pp_stream[i_stream]->os, &p_ogg->current_page ) == 0 ) @@ -1337,6 +2019,72 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) return VLC_EGENERIC; } +/**************************************************************************** + * Ogg_CreateES: Creates all Elementary streams once headers are parsed + ****************************************************************************/ +static void Ogg_CreateES( 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; + + for( i_stream = 0 ; i_stream < p_ogg->i_streams; i_stream++ ) + { + logical_stream_t *p_stream = p_ogg->pp_stream[i_stream]; + + if ( p_stream->p_es == NULL && !p_stream->b_finished ) + { + /* Better be safe than sorry when possible with ogm */ + if( p_stream->fmt.i_codec == VLC_CODEC_MPGA || + p_stream->fmt.i_codec == VLC_CODEC_A52 ) + p_stream->fmt.b_packetized = false; + + /* 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; + p_stream->b_finished = false; + p_stream->b_reinit = false; + p_stream->b_initializing = false; + p_stream->i_pre_skip = 0; + bool b_resetdecoder = Ogg_LogicalStreamResetEsFormat( p_demux, p_stream ); + es_format_Copy( &p_stream->fmt_old, &p_old_stream->fmt ); + + p_old_stream->p_es = NULL; + p_old_stream = NULL; + if ( b_resetdecoder ) + { + es_out_Control( p_demux->out, ES_OUT_SET_ES_FMT, + p_stream->p_es, &p_stream->fmt ); + } + } + else + { + 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_CODEC_CMML ) + { + /* Set the CMML stream active */ + es_out_Control( p_demux->out, ES_OUT_SET_ES, p_stream->p_es ); + } + } + } + + 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; + } +} + /**************************************************************************** * Ogg_BeginningOfStream: Look for Beginning of Stream ogg pages and add * Elementary streams. @@ -1344,7 +2092,6 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) 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 @@ -1366,53 +2113,18 @@ static int Ogg_BeginningOfStream( demux_t *p_demux ) /* initialise kframe index */ p_stream->idx=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 ) - { - /* Better be safe than sorry when possible with ogm */ - if( p_stream->fmt.i_codec == VLC_CODEC_MPGA || - p_stream->fmt.i_codec == VLC_CODEC_A52 ) - p_stream->fmt.b_packetized = false; - - 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_CODEC_CMML ) - { - /* Set the CMML stream active */ - es_out_Control( p_demux->out, ES_OUT_SET_ES, p_stream->p_es ); - } - - p_ogg->i_bitrate += p_stream->fmt.i_bitrate; + if ( p_stream->fmt.i_bitrate == 0 && + ( p_stream->fmt.i_cat == VIDEO_ES || + p_stream->fmt.i_cat == AUDIO_ES ) ) + p_ogg->b_partial_bitrate = true; + else + p_ogg->i_bitrate += p_stream->fmt.i_bitrate; - p_stream->i_pcr = p_stream->i_previous_pcr = - p_stream->i_interpolated_pcr = -1; + p_stream->i_pcr = p_stream->i_previous_pcr = VLC_TS_UNKNOWN; + p_stream->i_previous_granulepos = -1; p_stream->b_reinit = false; } - 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; - } - - /* get total frame count for video stream; we will need this for seeking */ p_ogg->i_total_frames = 0; @@ -1435,11 +2147,39 @@ static void Ogg_EndOfStream( demux_t *p_demux ) p_ogg->i_bitrate = 0; p_ogg->i_streams = 0; p_ogg->pp_stream = NULL; + p_ogg->skeleton.major = 0; + p_ogg->skeleton.minor = 0; + p_ogg->b_preparsing_done = false; + p_ogg->b_es_created = false; + p_ogg->i_nzpcr_offset = (p_ogg->i_pcr >= VLC_TS_INVALID) ? + p_ogg->i_pcr - VLC_TS_0 : 0; /* */ if( p_ogg->p_meta ) vlc_meta_Delete( p_ogg->p_meta ); p_ogg->p_meta = NULL; + + for ( int i=0; i < p_ogg->i_seekpoints; i++ ) + { + if ( p_ogg->pp_seekpoints[i] ) + vlc_seekpoint_Delete( p_ogg->pp_seekpoints[i] ); + } + TAB_CLEAN( p_ogg->i_seekpoints, p_ogg->pp_seekpoints ); + p_ogg->i_seekpoints = 0; +} + +static void Ogg_CleanSpecificData( logical_stream_t *p_stream ) +{ +#ifdef HAVE_LIBVORBIS + if ( p_stream->fmt.i_codec == VLC_CODEC_VORBIS ) + { + FREENULL( p_stream->special.vorbis.p_info ); + FREENULL( p_stream->special.vorbis.p_comment ); + p_stream->special.vorbis.i_headers_flags = 0; + } +#else + VLC_UNUSED( p_stream ); +#endif } /** @@ -1453,6 +2193,8 @@ static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_strea ogg_stream_clear( &p_stream->os ); free( p_stream->p_headers ); + Ogg_CleanSpecificData( p_stream ); + es_format_Clean( &p_stream->fmt_old ); es_format_Clean( &p_stream->fmt ); @@ -1461,6 +2203,19 @@ static void Ogg_LogicalStreamDelete( demux_t *p_demux, logical_stream_t *p_strea oggseek_index_entries_free( p_stream->idx ); } + Ogg_FreeSkeleton( p_stream->p_skel ); + p_stream->p_skel = NULL; + if ( p_demux->p_sys->p_skelstream == p_stream ) + p_demux->p_sys->p_skelstream = NULL; + + /* Shouldn't happen */ + if ( unlikely( p_stream->p_preparse_block ) ) + { + block_ChainRelease( p_stream->p_preparse_block ); + p_stream->p_preparse_block = NULL; + } + free( p_stream->prepcr.pp_blocks ); + free( p_stream ); } /** @@ -1492,71 +2247,202 @@ static bool Ogg_IsVorbisFormatCompatible( const es_format_t *p_new, const es_for b_match = false; } - for( unsigned i = 0; i < i_new_count; i++ ) - free( pp_new_data[i] ); - for( unsigned i = 0; i < i_old_count; i++ ) - free( pp_old_data[i] ); return b_match; } + +static bool Ogg_IsOpusFormatCompatible( const es_format_t *p_new, + const es_format_t *p_old ) +{ + unsigned pi_new_size[XIPH_MAX_HEADER_COUNT]; + void *pp_new_data[XIPH_MAX_HEADER_COUNT]; + unsigned i_new_count; + if( xiph_SplitHeaders(pi_new_size, pp_new_data, &i_new_count, p_new->i_extra, p_new->p_extra ) ) + i_new_count = 0; + unsigned pi_old_size[XIPH_MAX_HEADER_COUNT]; + void *pp_old_data[XIPH_MAX_HEADER_COUNT]; + unsigned i_old_count; + if( xiph_SplitHeaders(pi_old_size, pp_old_data, &i_old_count, p_old->i_extra, p_old->p_extra ) ) + i_old_count = 0; + bool b_match = false; + if( i_new_count == i_old_count && i_new_count > 0 ) + { + static const unsigned char default_map[2] = { 0, 1 }; + unsigned char *p_old_head; + unsigned char *p_new_head; + const unsigned char *p_old_map; + const unsigned char *p_new_map; + int i_old_channel_count; + int i_new_channel_count; + int i_old_stream_count; + int i_new_stream_count; + int i_old_coupled_count; + int i_new_coupled_count; + p_old_head = (unsigned char *)pp_old_data[0]; + i_old_channel_count = i_old_stream_count = i_old_coupled_count = 0; + p_old_map = default_map; + if( pi_old_size[0] >= 19 && p_old_head[8] <= 15 ) + { + i_old_channel_count = p_old_head[9]; + switch( p_old_head[18] ) + { + case 0: + i_old_stream_count = 1; + i_old_coupled_count = i_old_channel_count - 1; + break; + case 1: + if( pi_old_size[0] >= 21U + i_old_channel_count ) + { + i_old_stream_count = p_old_head[19]; + i_old_coupled_count = p_old_head[20]; + p_old_map = p_old_head + 21; + } + break; + } + } + p_new_head = (unsigned char *)pp_new_data[0]; + i_new_channel_count = i_new_stream_count = i_new_coupled_count = 0; + p_new_map = default_map; + if( pi_new_size[0] >= 19 && p_new_head[8] <= 15 ) + { + i_new_channel_count = p_new_head[9]; + switch( p_new_head[18] ) + { + case 0: + i_new_stream_count = 1; + i_new_coupled_count = i_new_channel_count - 1; + break; + case 1: + if( pi_new_size[0] >= 21U + i_new_channel_count ) + { + i_new_stream_count = p_new_head[19]; + i_new_coupled_count = p_new_head[20]; + p_new_map = p_new_head+21; + } + break; + } + } + b_match = i_old_channel_count == i_new_channel_count && + i_old_stream_count == i_new_stream_count && + i_old_coupled_count == i_new_coupled_count && + memcmp(p_old_map, p_new_map, + i_new_channel_count*sizeof(*p_new_map)) == 0; + } + + return b_match; +} + static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t *p_stream ) { bool b_compatible = false; if( !p_stream->fmt_old.i_cat || !p_stream->fmt_old.i_codec ) return true; - /* Only vorbis is supported */ + /* Only Vorbis and Opus are supported. */ if( p_stream->fmt.i_codec == VLC_CODEC_VORBIS ) b_compatible = Ogg_IsVorbisFormatCompatible( &p_stream->fmt, &p_stream->fmt_old ); + else if( p_stream->fmt.i_codec == VLC_CODEC_OPUS ) + b_compatible = Ogg_IsOpusFormatCompatible( &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_ExtractXiphMeta( demux_t *p_demux, const void *p_headers, unsigned i_headers, unsigned i_skip ) + +static void Ogg_ExtractComments( demux_t *p_demux, es_format_t *p_fmt, + const void *p_headers, unsigned i_headers ) { demux_sys_t *p_ogg = p_demux->p_sys; + int i_cover_score = 0; + int i_cover_idx = 0; + float pf_replay_gain[AUDIO_REPLAY_GAIN_MAX]; + float pf_replay_peak[AUDIO_REPLAY_GAIN_MAX]; + for(int i=0; i< AUDIO_REPLAY_GAIN_MAX; i++ ) + { + pf_replay_gain[i] = 0; + pf_replay_peak[i] = 0; + } + vorbis_ParseComment( p_fmt, &p_ogg->p_meta, p_headers, i_headers, + &p_ogg->i_attachments, &p_ogg->attachments, + &i_cover_score, &i_cover_idx, + &p_ogg->i_seekpoints, &p_ogg->pp_seekpoints, + &pf_replay_gain, &pf_replay_peak ); + if( p_ogg->p_meta != NULL && i_cover_idx < p_ogg->i_attachments ) + { + char psz_url[128]; + snprintf( psz_url, sizeof(psz_url), "attachment://%s", + p_ogg->attachments[i_cover_idx]->psz_name ); + vlc_meta_Set( p_ogg->p_meta, vlc_meta_ArtworkURL, psz_url ); + } + for ( int i=0; iaudio_replay_gain.pb_gain[i] = true; + p_fmt->audio_replay_gain.pf_gain[i] = pf_replay_gain[i]; + msg_Dbg( p_demux, "setting replay gain %d to %f", i, pf_replay_gain[i] ); + } + if ( pf_replay_peak[i] != 0 ) + { + p_fmt->audio_replay_gain.pb_peak[i] = true; + p_fmt->audio_replay_gain.pf_peak[i] = pf_replay_peak[i]; + msg_Dbg( p_demux, "setting replay peak %d to %f", i, pf_replay_gain[i] ); + } + } + + if( p_ogg->i_seekpoints > 1 ) + { + p_demux->info.i_update |= INPUT_UPDATE_TITLE_LIST; + } +} + +static void Ogg_ExtractXiphMeta( demux_t *p_demux, es_format_t *p_fmt, + const void *p_headers, unsigned i_headers, unsigned i_skip ) +{ unsigned pi_size[XIPH_MAX_HEADER_COUNT]; void *pp_data[XIPH_MAX_HEADER_COUNT]; unsigned i_count; + if( xiph_SplitHeaders( pi_size, pp_data, &i_count, i_headers, p_headers ) ) return; - /* TODO how to handle multiple comments properly ? */ if( i_count >= 2 && pi_size[1] > i_skip ) - vorbis_ParseComment( &p_ogg->p_meta, (uint8_t*)pp_data[1] + i_skip, pi_size[1] - i_skip, - &p_ogg->i_attachments, &p_ogg->attachments ); - - for( unsigned i = 0; i < i_count; i++ ) - free( pp_data[i] ); + { + Ogg_ExtractComments( p_demux, p_fmt, (uint8_t*)pp_data[1] + i_skip, pi_size[1] - i_skip ); + } } -static void Ogg_ExtractMeta( demux_t *p_demux, vlc_fourcc_t i_codec, const uint8_t *p_headers, int i_headers ) + +static void Ogg_ExtractMeta( demux_t *p_demux, es_format_t *p_fmt, const uint8_t *p_headers, int i_headers ) { demux_sys_t *p_ogg = p_demux->p_sys; - switch( i_codec ) + switch( p_fmt->i_codec ) { /* 3 headers with the 2° one being the comments */ case VLC_CODEC_VORBIS: - Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 1+6 ); - break; case VLC_CODEC_THEORA: - Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 1+6 ); + case VLC_CODEC_DAALA: + Ogg_ExtractXiphMeta( p_demux, p_fmt, p_headers, i_headers, 1+6 ); + break; + case VLC_CODEC_OPUS: + Ogg_ExtractXiphMeta( p_demux, p_fmt, p_headers, i_headers, 8 ); break; case VLC_CODEC_SPEEX: - Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 0 ); + Ogg_ExtractXiphMeta( p_demux, p_fmt, p_headers, i_headers, 0 ); + break; + case VLC_CODEC_VP8: + Ogg_ExtractComments( p_demux, p_fmt, p_headers, i_headers ); break; - /* N headers with the 2° one being the comments */ case VLC_CODEC_KATE: /* 1 byte for header type, 7 bytes for magic, 1 reserved zero byte */ - Ogg_ExtractXiphMeta( p_demux, p_headers, i_headers, 1+7+1 ); + Ogg_ExtractXiphMeta( p_demux, p_fmt, p_headers, i_headers, 1+7+1 ); break; /* TODO */ case VLC_CODEC_FLAC: - msg_Warn( p_demux, "Ogg_ExtractMeta does not support %4.4s", (const char*)&i_codec ); + msg_Warn( p_demux, "Ogg_ExtractMeta does not support %4.4s", (const char*)&p_fmt->i_codec ); break; /* No meta data */ @@ -1569,12 +2455,12 @@ static void Ogg_ExtractMeta( demux_t *p_demux, vlc_fourcc_t i_codec, const uint8 p_demux->info.i_update |= INPUT_UPDATE_META; } -static void Ogg_ReadTheoraHeader( logical_stream_t *p_stream, +static bool Ogg_ReadTheoraHeader( logical_stream_t *p_stream, ogg_packet *p_oggpacket ) { bs_t bitstream; - int i_fps_numerator; - int i_fps_denominator; + unsigned int i_fps_numerator; + unsigned int i_fps_denominator; int i_keyframe_frequency_force; int i_major; int i_minor; @@ -1587,7 +2473,7 @@ static void Ogg_ReadTheoraHeader( logical_stream_t *p_stream, /* Signal that we want to keep a backup of the theora * stream headers. They will be used when switching between * audio streams. */ - p_stream->b_force_backup = 1; + p_stream->b_force_backup = true; /* Cheat and get additionnal info ;) */ bs_init( &bitstream, p_oggpacket->packet, p_oggpacket->bytes ); @@ -1606,6 +2492,7 @@ static void Ogg_ReadTheoraHeader( logical_stream_t *p_stream, i_fps_numerator = bs_read( &bitstream, 32 ); i_fps_denominator = bs_read( &bitstream, 32 ); + i_fps_denominator = __MAX( i_fps_denominator, 1 ); bs_read( &bitstream, 24 ); /* aspect_numerator */ bs_read( &bitstream, 24 ); /* aspect_denominator */ @@ -1629,16 +2516,79 @@ static void Ogg_ReadTheoraHeader( logical_stream_t *p_stream, i_version = i_major * 1000000 + i_minor * 1000 + i_subminor; p_stream->i_keyframe_offset = 0; + p_stream->f_rate = ((double)i_fps_numerator) / i_fps_denominator; + if ( p_stream->f_rate == 0 ) return false; if ( i_version >= 3002001 ) { p_stream->i_keyframe_offset = 1; } + return true; +} + +static bool Ogg_ReadDaalaHeader( logical_stream_t *p_stream, + ogg_packet *p_oggpacket ) +{ + oggpack_buffer opb; + uint32_t i_timebase_numerator; + uint32_t i_timebase_denominator; + int i_keyframe_frequency_force; + uint8_t i_major; + uint8_t i_minor; + uint8_t i_subminor; + int i_version; + + p_stream->fmt.i_cat = VIDEO_ES; + p_stream->fmt.i_codec = VLC_CODEC_DAALA; + + /* Signal that we want to keep a backup of the daala + * stream headers. They will be used when switching between + * audio streams. */ + p_stream->b_force_backup = true; + + /* Cheat and get additionnal info ;) */ + oggpack_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes ); + oggpack_adv( &opb, 48 ); + + i_major = oggpack_read( &opb, 8 ); /* major version num */ + i_minor = oggpack_read( &opb, 8 ); /* minor version num */ + i_subminor = oggpack_read( &opb, 8 ); /* subminor version num */ + + oggpack_adv( &opb, 32 ); /* width */ + oggpack_adv( &opb, 32 ); /* height */ + + oggpack_adv( &opb, 32 ); /* aspect numerator */ + oggpack_adv( &opb, 32 ); /* aspect denominator */ + i_timebase_numerator = oggpack_read( &opb, 32 ); + + i_timebase_denominator = oggpack_read( &opb, 32 ); + i_timebase_denominator = __MAX( i_timebase_denominator, 1 ); + + p_stream->fmt.video.i_frame_rate = i_timebase_numerator; + p_stream->fmt.video.i_frame_rate_base = i_timebase_denominator; + + oggpack_adv( &opb, 32 ); /* frame duration */ + + i_keyframe_frequency_force = 1 << oggpack_read( &opb, 8 ); + + /* granule_shift = i_log( frequency_force -1 ) */ + p_stream->i_granule_shift = 0; + i_keyframe_frequency_force--; + while( i_keyframe_frequency_force ) + { + p_stream->i_granule_shift++; + i_keyframe_frequency_force >>= 1; + } + + i_version = i_major * 1000000 + i_minor * 1000 + i_subminor; + p_stream->i_keyframe_offset = 0; + p_stream->f_rate = ((double)i_timebase_numerator) / i_timebase_denominator; + if ( p_stream->f_rate == 0 ) return false; - p_stream->f_rate = ((float)i_fps_numerator) / i_fps_denominator; + return true; } -static void Ogg_ReadVorbisHeader( logical_stream_t *p_stream, +static bool Ogg_ReadVorbisHeader( logical_stream_t *p_stream, ogg_packet *p_oggpacket ) { oggpack_buffer opb; @@ -1649,19 +2599,58 @@ static void Ogg_ReadVorbisHeader( logical_stream_t *p_stream, /* Signal that we want to keep a backup of the vorbis * stream headers. They will be used when switching between * audio streams. */ - p_stream->b_force_backup = 1; + p_stream->b_force_backup = true; /* Cheat and get additionnal info ;) */ oggpack_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes); oggpack_adv( &opb, 88 ); p_stream->fmt.audio.i_channels = oggpack_read( &opb, 8 ); + fill_channels_info(&p_stream->fmt.audio); p_stream->f_rate = p_stream->fmt.audio.i_rate = oggpack_read( &opb, 32 ); oggpack_adv( &opb, 32 ); - p_stream->fmt.i_bitrate = oggpack_read( &opb, 32 ); + p_stream->fmt.i_bitrate = oggpack_read( &opb, 32 ); /* is signed 32 */ + if( p_stream->fmt.i_bitrate > INT32_MAX ) p_stream->fmt.i_bitrate = 0; + if ( p_stream->f_rate == 0 ) return false; + return true; +} +#ifdef HAVE_LIBVORBIS +static void Ogg_DecodeVorbisHeader( logical_stream_t *p_stream, + ogg_packet *p_oggpacket, int i_number ) +{ + switch( i_number ) + { + case VORBIS_HEADER_IDENTIFICATION: + p_stream->special.vorbis.p_info = calloc( 1, sizeof(vorbis_info) ); + p_stream->special.vorbis.p_comment = malloc( sizeof(vorbis_comment) ); + if ( !p_stream->special.vorbis.p_info || !p_stream->special.vorbis.p_comment ) + { + FREENULL( p_stream->special.vorbis.p_info ); + FREENULL( p_stream->special.vorbis.p_comment ); + break; + } + vorbis_info_init( p_stream->special.vorbis.p_info ); + vorbis_comment_init( p_stream->special.vorbis.p_comment ); + // ft + + case VORBIS_HEADER_COMMENT: + case VORBIS_HEADER_SETUP: + if ( !p_stream->special.vorbis.p_info || + vorbis_synthesis_headerin( + p_stream->special.vorbis.p_info, + p_stream->special.vorbis.p_comment, p_oggpacket ) ) + break; + + p_stream->special.vorbis.i_headers_flags |= VORBIS_HEADER_TO_FLAG(i_number); + // ft + + default: + break; + } } +#endif -static void Ogg_ReadSpeexHeader( logical_stream_t *p_stream, +static bool Ogg_ReadSpeexHeader( logical_stream_t *p_stream, ogg_packet *p_oggpacket ) { oggpack_buffer opb; @@ -1672,7 +2661,7 @@ static void Ogg_ReadSpeexHeader( logical_stream_t *p_stream, /* Signal that we want to keep a backup of the speex * stream headers. They will be used when switching between * audio streams. */ - p_stream->b_force_backup = 1; + p_stream->b_force_backup = true; /* Cheat and get additionnal info ;) */ oggpack_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes); @@ -1680,13 +2669,55 @@ static void Ogg_ReadSpeexHeader( logical_stream_t *p_stream, oggpack_adv( &opb, 32 ); /* speex_version_id */ oggpack_adv( &opb, 32 ); /* header_size */ p_stream->f_rate = p_stream->fmt.audio.i_rate = oggpack_read( &opb, 32 ); + if ( p_stream->f_rate == 0 ) return false; oggpack_adv( &opb, 32 ); /* mode */ oggpack_adv( &opb, 32 ); /* mode_bitstream_version */ p_stream->fmt.audio.i_channels = oggpack_read( &opb, 32 ); + fill_channels_info(&p_stream->fmt.audio); p_stream->fmt.i_bitrate = oggpack_read( &opb, 32 ); + p_stream->special.speex.i_framesize = + oggpack_read( &opb, 32 ); /* frame_size */ + oggpack_adv( &opb, 32 ); /* vbr */ + p_stream->special.speex.i_framesperpacket = + oggpack_read( &opb, 32 ); /* frames_per_packet */ + p_stream->i_extra_headers_packets = oggpack_read( &opb, 32 ); /* extra_headers */ + return true; +} + +static void Ogg_ReadOpusHeader( logical_stream_t *p_stream, + ogg_packet *p_oggpacket ) +{ + oggpack_buffer opb; + + p_stream->fmt.i_cat = AUDIO_ES; + p_stream->fmt.i_codec = VLC_CODEC_OPUS; + + /* Signal that we want to keep a backup of the opus + * stream headers. They will be used when switching between + * audio streams. */ + p_stream->b_force_backup = true; + + /* All OggOpus streams are timestamped at 48kHz and + * can be played at 48kHz. */ + p_stream->f_rate = p_stream->fmt.audio.i_rate = 48000; + p_stream->fmt.i_bitrate = 0; + + /* Cheat and get additional info ;) */ + oggpack_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes); + oggpack_adv( &opb, 64 ); + oggpack_adv( &opb, 8 ); /* version_id */ + p_stream->fmt.audio.i_channels = oggpack_read( &opb, 8 ); + fill_channels_info(&p_stream->fmt.audio); + p_stream->i_pre_skip = oggpack_read( &opb, 16 ); + /* For Opus, trash the first 80 ms of decoded output as + well, to avoid blowing out speakers if we get unlucky. + Opus predicts content from prior frames, which can go + badly if we seek right where the stream goes from very + quiet to very loud. It will converge after a bit. */ + p_stream->i_pre_skip = __MAX( 80*48, p_stream->i_pre_skip ); } -static void Ogg_ReadFlacHeader( demux_t *p_demux, logical_stream_t *p_stream, +static bool Ogg_ReadFlacHeader( demux_t *p_demux, logical_stream_t *p_stream, ogg_packet *p_oggpacket ) { /* Parse the STREAMINFO metadata */ @@ -1695,38 +2726,39 @@ static void Ogg_ReadFlacHeader( demux_t *p_demux, logical_stream_t *p_stream, bs_init( &s, p_oggpacket->packet, p_oggpacket->bytes ); bs_read( &s, 1 ); - if( bs_read( &s, 7 ) == 0 ) + if( p_oggpacket->bytes > 0 && bs_read( &s, 7 ) != 0 ) { - if( bs_read( &s, 24 ) >= 34 /*size STREAMINFO*/ ) - { - bs_skip( &s, 80 ); - p_stream->f_rate = p_stream->fmt.audio.i_rate = bs_read( &s, 20 ); - p_stream->fmt.audio.i_channels = bs_read( &s, 3 ) + 1; - - msg_Dbg( p_demux, "FLAC header, channels: %i, rate: %i", - p_stream->fmt.audio.i_channels, (int)p_stream->f_rate ); - } - else - { - msg_Dbg( p_demux, "FLAC STREAMINFO metadata too short" ); - } + msg_Dbg( p_demux, "Invalid FLAC STREAMINFO metadata" ); + return false; + } - /* Fake this as the last metadata block */ - *((uint8_t*)p_oggpacket->packet) |= 0x80; + if( bs_read( &s, 24 ) >= 34 /*size STREAMINFO*/ ) + { + bs_skip( &s, 80 ); + p_stream->f_rate = p_stream->fmt.audio.i_rate = bs_read( &s, 20 ); + p_stream->fmt.audio.i_channels = bs_read( &s, 3 ) + 1; + fill_channels_info(&p_stream->fmt.audio); + + msg_Dbg( p_demux, "FLAC header, channels: %i, rate: %i", + p_stream->fmt.audio.i_channels, (int)p_stream->f_rate ); + if ( p_stream->f_rate == 0 ) return false; } else { - /* This ain't a STREAMINFO metadata */ - msg_Dbg( p_demux, "Invalid FLAC STREAMINFO metadata" ); + msg_Dbg( p_demux, "FLAC STREAMINFO metadata too short" ); } + + /* Fake this as the last metadata block */ + *((uint8_t*)p_oggpacket->packet) |= 0x80; + return true; } -static void Ogg_ReadKateHeader( logical_stream_t *p_stream, +static bool Ogg_ReadKateHeader( logical_stream_t *p_stream, ogg_packet *p_oggpacket ) { oggpack_buffer opb; - int32_t gnum; - int32_t gden; + uint32_t gnum; + uint32_t gden; int n; char *psz_desc; @@ -1736,18 +2768,20 @@ static void Ogg_ReadKateHeader( logical_stream_t *p_stream, /* Signal that we want to keep a backup of the kate * stream headers. They will be used when switching between * kate streams. */ - p_stream->b_force_backup = 1; + p_stream->b_force_backup = true; /* Cheat and get additionnal info ;) */ oggpack_readinit( &opb, p_oggpacket->packet, p_oggpacket->bytes); oggpack_adv( &opb, 11*8 ); /* packet type, kate magic, version */ - p_stream->i_kate_num_headers = oggpack_read( &opb, 8 ); + p_stream->special.kate.i_num_headers = oggpack_read( &opb, 8 ); oggpack_adv( &opb, 3*8 ); p_stream->i_granule_shift = oggpack_read( &opb, 8 ); oggpack_adv( &opb, 8*8 ); /* reserved */ gnum = oggpack_read( &opb, 32 ); gden = oggpack_read( &opb, 32 ); + gden = __MAX( gden, 1 ); p_stream->f_rate = (double)gnum/gden; + if ( p_stream->f_rate == 0 ) return false; p_stream->fmt.psz_language = malloc(16); if( p_stream->fmt.psz_language ) @@ -1781,9 +2815,128 @@ static void Ogg_ReadKateHeader( logical_stream_t *p_stream, for( n = 0; n < 16; n++ ) oggpack_read(&opb,8); } + + return true; +} + +static bool Ogg_ReadVP8Header( demux_t *p_demux, logical_stream_t *p_stream, + ogg_packet *p_oggpacket ) +{ + switch( p_oggpacket->packet[5] ) + { + /* STREAMINFO */ + case 0x01: + /* Mapping version */ + if ( p_oggpacket->packet[6] != 0x01 || p_oggpacket->packet[7] != 0x00 ) + return false; + p_stream->fmt.i_cat = VIDEO_ES; + p_stream->fmt.i_codec = VLC_CODEC_VP8; + p_stream->i_granule_shift = 32; + p_stream->fmt.video.i_width = GetWBE( &p_oggpacket->packet[8] ); + p_stream->fmt.video.i_height = GetWBE( &p_oggpacket->packet[10] ); + p_stream->fmt.video.i_sar_num = GetDWBE( &p_oggpacket->packet[12 - 1] ) & 0x0FFF; + p_stream->fmt.video.i_sar_den = GetDWBE( &p_oggpacket->packet[15 - 1] ) & 0x0FFF; + p_stream->fmt.video.i_frame_rate = GetDWBE( &p_oggpacket->packet[18] ); + p_stream->fmt.video.i_frame_rate_base = GetDWBE( &p_oggpacket->packet[22] ); + p_stream->fmt.video.i_frame_rate_base = + __MAX( p_stream->fmt.video.i_frame_rate_base, 1 ); + p_stream->f_rate = (double) p_stream->fmt.video.i_frame_rate / p_stream->fmt.video.i_frame_rate_base; + if ( p_stream->f_rate == 0 ) return false; + return true; + /* METADATA */ + case 0x02: + Ogg_ExtractMeta( p_demux, & p_stream->fmt, + p_oggpacket->packet + 7, p_oggpacket->bytes - 7 ); + return true; + default: + return false; + } } -static void Ogg_ReadAnnodexHeader( vlc_object_t *p_this, +static void Ogg_ApplyContentType( logical_stream_t *p_stream, const char* psz_value, + bool *b_force_backup, bool *b_packet_out ) +{ + if( !strncmp(psz_value, "audio/x-wav", 11) ) + { + /* n.b. WAVs are unsupported right now */ + p_stream->fmt.i_cat = UNKNOWN_ES; + free( p_stream->fmt.psz_description ); + p_stream->fmt.psz_description = strdup("WAV Audio (Unsupported)"); + } + else if( !strncmp(psz_value, "audio/x-vorbis", 14) || + !strncmp(psz_value, "audio/vorbis", 12) ) + { + p_stream->fmt.i_cat = AUDIO_ES; + p_stream->fmt.i_codec = VLC_CODEC_VORBIS; + + *b_force_backup = true; + } + else if( !strncmp(psz_value, "audio/x-speex", 13) || + !strncmp(psz_value, "audio/speex", 11) ) + { + p_stream->fmt.i_cat = AUDIO_ES; + p_stream->fmt.i_codec = VLC_CODEC_SPEEX; + + *b_force_backup = true; + } + else if( !strncmp(psz_value, "audio/flac", 10) ) + { + p_stream->fmt.i_cat = AUDIO_ES; + p_stream->fmt.i_codec = VLC_CODEC_FLAC; + + *b_force_backup = true; + } + else if( !strncmp(psz_value, "video/x-theora", 14) || + !strncmp(psz_value, "video/theora", 12) ) + { + p_stream->fmt.i_cat = VIDEO_ES; + p_stream->fmt.i_codec = VLC_CODEC_THEORA; + + *b_force_backup = true; + } + else if( !strncmp(psz_value, "video/x-daala", 13) || + !strncmp(psz_value, "video/daala", 11) ) + { + p_stream->fmt.i_cat = VIDEO_ES; + p_stream->fmt.i_codec = VLC_CODEC_DAALA; + + *b_force_backup = true; + } + else if( !strncmp(psz_value, "video/x-xvid", 12) ) + { + p_stream->fmt.i_cat = VIDEO_ES; + p_stream->fmt.i_codec = VLC_FOURCC( 'x','v','i','d' ); + + *b_force_backup = true; + } + else if( !strncmp(psz_value, "video/mpeg", 10) ) + { + /* n.b. MPEG streams are unsupported right now */ + p_stream->fmt.i_cat = VIDEO_ES; + p_stream->fmt.i_codec = VLC_CODEC_MPGV; + } + else if( !strncmp(psz_value, "text/x-cmml", 11) || + !strncmp(psz_value, "text/cmml", 9) ) + { + p_stream->fmt.i_cat = SPU_ES; + p_stream->fmt.i_codec = VLC_CODEC_CMML; + *b_packet_out = true; + } + else if( !strncmp(psz_value, "application/kate", 16) ) + { + /* ??? */ + p_stream->fmt.i_cat = UNKNOWN_ES; + free( p_stream->fmt.psz_description ); + p_stream->fmt.psz_description = strdup("OGG Kate Overlay (Unsupported)"); + } + else if( !strncmp(psz_value, "video/x-vp8", 11) ) + { + p_stream->fmt.i_cat = VIDEO_ES; + p_stream->fmt.i_codec = VLC_CODEC_VP8; + } +} + +static void Ogg_ReadAnnodexHeader( demux_t *p_demux, logical_stream_t *p_stream, ogg_packet *p_oggpacket ) { @@ -1805,6 +2958,11 @@ static void Ogg_ReadAnnodexHeader( vlc_object_t *p_this, minor_version = oggpack_read( &opb, 2*8 ); /* minor version */ timebase_numerator = GetQWLE( &p_oggpacket->packet[16] ); timebase_denominator = GetQWLE( &p_oggpacket->packet[24] ); + + msg_Dbg( p_demux, "Annodex info: version %"PRIu16".%"PRIu16" " + "Timebase %"PRId64" / %"PRId64, + major_version, minor_version, + timebase_numerator, timebase_denominator ); } else if( p_oggpacket->bytes >= 42 && !memcmp( p_oggpacket->packet, "AnxData", 7 ) ) @@ -1832,7 +2990,7 @@ static void Ogg_ReadAnnodexHeader( vlc_object_t *p_this, content_type_string ); } - msg_Dbg( p_this, "AnxData packet info: %"PRId64" / %"PRId64", %d, ``%s''", + msg_Dbg( p_demux, "AnxData packet info: %"PRId64" / %"PRId64", %d, ``%s''", granule_rate_numerator, granule_rate_denominator, p_stream->i_secondary_header_packets, content_type_string ); @@ -1842,52 +3000,226 @@ static void Ogg_ReadAnnodexHeader( vlc_object_t *p_this, /* What type of file do we have? * strcmp is safe to use here because we've extracted * content_type_string from the stream manually */ - if( !strncmp(content_type_string, "audio/x-wav", 11) ) + bool b_dopacketout = false; + Ogg_ApplyContentType( p_stream, content_type_string, + &p_stream->b_force_backup, &b_dopacketout ); + if ( b_dopacketout ) ogg_stream_packetout( &p_stream->os, p_oggpacket ); + } +} + +static void Ogg_ReadSkeletonHeader( demux_t *p_demux, logical_stream_t *p_stream, + ogg_packet *p_oggpacket ) +{ + p_demux->p_sys->p_skelstream = p_stream; + /* There can be only 1 skeleton for streams */ + p_demux->p_sys->skeleton.major = GetWLE( &p_oggpacket->packet[8] ); + p_demux->p_sys->skeleton.minor = GetWLE( &p_oggpacket->packet[10] ); + if ( asprintf( & p_stream->fmt.psz_description, + "OGG Skeleton version %" PRIu16 ".%" PRIu16, + p_demux->p_sys->skeleton.major, + p_demux->p_sys->skeleton.minor ) < 0 ) + p_stream->fmt.psz_description = NULL; +} + +static void Ogg_ReadSkeletonBones( demux_t *p_demux, ogg_packet *p_oggpacket ) +{ + if ( p_demux->p_sys->skeleton.major < 3 || p_oggpacket->bytes < 52 ) return; + + /* Find the matching stream for this skeleton data */ + ogg_int32_t i_serialno = GetDWLE( &p_oggpacket->packet[12] ); + logical_stream_t *p_target_stream = NULL; + for ( int i=0; i< p_demux->p_sys->i_streams; i++ ) + { + if ( p_demux->p_sys->pp_stream[i]->i_serial_no == i_serialno ) { - /* n.b. WAVs are unsupported right now */ - p_stream->fmt.i_cat = UNKNOWN_ES; + p_target_stream = p_demux->p_sys->pp_stream[i]; + break; } - else if( !strncmp(content_type_string, "audio/x-vorbis", 14) ) - { - p_stream->fmt.i_cat = AUDIO_ES; - p_stream->fmt.i_codec = VLC_CODEC_VORBIS; + } + if ( !p_target_stream ) return; - p_stream->b_force_backup = 1; - } - else if( !strncmp(content_type_string, "audio/x-speex", 13) ) - { - p_stream->fmt.i_cat = AUDIO_ES; - p_stream->fmt.i_codec = VLC_CODEC_SPEEX; + ogg_skeleton_t *p_skel = p_target_stream->p_skel; + if ( !p_skel ) + { + p_skel = malloc( sizeof( ogg_skeleton_t ) ); + if ( !p_skel ) return; + TAB_INIT( p_skel->i_messages, p_skel->ppsz_messages ); + p_skel->p_index = NULL; + p_target_stream->p_skel = p_skel; + } - p_stream->b_force_backup = 1; - } - else if( !strncmp(content_type_string, "video/x-theora", 14) ) + const unsigned char *p_messages = p_oggpacket->packet + 8 + GetDWLE( &p_oggpacket->packet[8] ); + const unsigned char *p_boundary = p_oggpacket->packet + p_oggpacket->bytes; + const unsigned char *p = p_messages; + while ( p <= p_boundary - 1 && p > p_oggpacket->packet ) + { + if ( *p == 0x0D && *(p+1) == 0x0A ) { - p_stream->fmt.i_cat = VIDEO_ES; - p_stream->fmt.i_codec = VLC_CODEC_THEORA; - - p_stream->b_force_backup = 1; + char *psz_message = strndup( (const char *) p_messages, + p - p_messages ); + if ( psz_message ) + { + msg_Dbg( p_demux, "stream %" PRId32 " [%s]", i_serialno, psz_message ); + TAB_APPEND( p_skel->i_messages, p_skel->ppsz_messages, psz_message ); + } + if ( p < p_boundary - 1 ) p_messages = p + 2; } - else if( !strncmp(content_type_string, "video/x-xvid", 12) ) + p++; + } + +} + +/* Unpacks the 7bit variable encoding used in skeleton indexes */ +unsigned const char * Read7BitsVariableLE( unsigned const char *p_begin, + unsigned const char *p_end, + uint64_t *pi_value ) +{ + int i_shift = 0; + int64_t i_read = 0; + *pi_value = 0; + + while ( p_begin < p_end ) + { + i_read = *p_begin & 0x7F; /* High bit is start of integer */ + *pi_value = *pi_value | ( i_read << i_shift ); + i_shift += 7; + if ( (*p_begin++ & 0x80) == 0x80 ) break; /* see prev */ + } + + *pi_value = GetQWLE( pi_value ); + return p_begin; +} + +static void Ogg_ReadSkeletonIndex( demux_t *p_demux, ogg_packet *p_oggpacket ) +{ + if ( p_demux->p_sys->skeleton.major < 4 + || p_oggpacket->bytes < 44 /* Need at least 1 index value (42+1+1) */ + ) return; + + /* Find the matching stream for this skeleton data */ + int32_t i_serialno = GetDWLE( &p_oggpacket->packet[6] ); + logical_stream_t *p_stream = NULL; + for ( int i=0; i< p_demux->p_sys->i_streams; i++ ) + { + if ( p_demux->p_sys->pp_stream[i]->i_serial_no == i_serialno ) { - p_stream->fmt.i_cat = VIDEO_ES; - p_stream->fmt.i_codec = VLC_FOURCC( 'x','v','i','d' ); + p_stream = p_demux->p_sys->pp_stream[i]; + break; + } + } + if ( !p_stream || !p_stream->p_skel ) return; + uint64_t i_keypoints = GetQWLE( &p_oggpacket->packet[10] ); + msg_Dbg( p_demux, "%" PRIi64 " index data for %" PRIi32, i_keypoints, i_serialno ); + if ( !i_keypoints ) return; + + p_stream->p_skel->i_indexstampden = GetQWLE( &p_oggpacket->packet[18] ); + p_stream->p_skel->i_indexfirstnum = GetQWLE( &p_oggpacket->packet[24] ); + p_stream->p_skel->i_indexlastnum = GetQWLE( &p_oggpacket->packet[32] ); + unsigned const char *p_fwdbyte = &p_oggpacket->packet[42]; + unsigned const char *p_boundary = p_oggpacket->packet + p_oggpacket->bytes; + uint64_t i_offset = 0; + uint64_t i_time = 0; + uint64_t i_keypoints_found = 0; + + while( p_fwdbyte < p_boundary && i_keypoints_found < i_keypoints ) + { + uint64_t i_val; + p_fwdbyte = Read7BitsVariableLE( p_fwdbyte, p_boundary, &i_val ); + i_offset += i_val; + p_fwdbyte = Read7BitsVariableLE( p_fwdbyte, p_boundary, &i_val ); + i_time += i_val * p_stream->p_skel->i_indexstampden; + i_keypoints_found++; + } + + if ( i_keypoints_found != i_keypoints ) + { + msg_Warn( p_demux, "Invalid Index: missing entries" ); + return; + } + + p_stream->p_skel->p_index = malloc( p_oggpacket->bytes - 42 ); + if ( !p_stream->p_skel->p_index ) return; + memcpy( p_stream->p_skel->p_index, &p_oggpacket->packet[42], + p_oggpacket->bytes - 42 ); + p_stream->p_skel->i_index = i_keypoints_found; + p_stream->p_skel->i_index_size = p_oggpacket->bytes - 42; +} + +static void Ogg_FreeSkeleton( ogg_skeleton_t *p_skel ) +{ + if ( !p_skel ) return; + for ( int i=0; i< p_skel->i_messages; i++ ) + free( p_skel->ppsz_messages[i] ); + TAB_CLEAN( p_skel->i_messages, p_skel->ppsz_messages ); + free( p_skel->p_index ); + free( p_skel ); +} - p_stream->b_force_backup = 1; +static void Ogg_ApplySkeleton( logical_stream_t *p_stream ) +{ + if ( !p_stream->p_skel ) return; + for ( int i=0; i< p_stream->p_skel->i_messages; i++ ) + { + const char *psz_message = p_stream->p_skel->ppsz_messages[i]; + if ( ! strncmp( "Name: ", psz_message, 6 ) ) + { + free( p_stream->fmt.psz_description ); + p_stream->fmt.psz_description = strdup( psz_message + 6 ); } - else if( !strncmp(content_type_string, "video/mpeg", 10) ) + else if ( ! strncmp("Content-Type: ", psz_message, 14 ) ) { - /* n.b. MPEG streams are unsupported right now */ - p_stream->fmt.i_cat = VIDEO_ES; - p_stream->fmt.i_codec = VLC_CODEC_MPGV; + bool b_foo; + Ogg_ApplyContentType( p_stream, psz_message + 14, &b_foo, &b_foo ); } - else if( !strncmp(content_type_string, "text/x-cmml", 11) ) + } +} + +/* Return true if there's a skeleton exact match */ +bool Ogg_GetBoundsUsingSkeletonIndex( logical_stream_t *p_stream, int64_t i_time, + int64_t *pi_lower, int64_t *pi_upper ) +{ + if ( !p_stream || !p_stream->p_skel || !p_stream->p_skel->p_index ) + return false; + + /* Validate range */ + if ( i_time < p_stream->p_skel->i_indexfirstnum + * p_stream->p_skel->i_indexstampden || + i_time > p_stream->p_skel->i_indexlastnum + * p_stream->p_skel->i_indexstampden ) return false; + + /* Then Lookup its index */ + unsigned const char *p_fwdbyte = p_stream->p_skel->p_index; + struct + { + int64_t i_pos; + int64_t i_time; + } current = { 0, 0 }, prev = { -1, -1 }; + + uint64_t i_keypoints_found = 0; + + while( p_fwdbyte < p_fwdbyte + p_stream->p_skel->i_index_size + && i_keypoints_found < p_stream->p_skel->i_index ) + { + uint64_t i_val; + p_fwdbyte = Read7BitsVariableLE( p_fwdbyte, + p_fwdbyte + p_stream->p_skel->i_index_size, &i_val ); + current.i_pos += i_val; + p_fwdbyte = Read7BitsVariableLE( p_fwdbyte, + p_fwdbyte + p_stream->p_skel->i_index_size, &i_val ); + current.i_time += i_val * p_stream->p_skel->i_indexstampden; + if ( current.i_pos < 0 || current.i_time < 0 ) break; + + i_keypoints_found++; + + if ( i_time <= current.i_time ) { - ogg_stream_packetout( &p_stream->os, p_oggpacket ); - p_stream->fmt.i_cat = SPU_ES; - p_stream->fmt.i_codec = VLC_CODEC_CMML; + *pi_lower = prev.i_pos; + *pi_upper = current.i_pos; + return ( i_time == current.i_time ); } + prev = current; } + return false; } static uint32_t dirac_uint( bs_t *p_bs ) @@ -1932,7 +3264,7 @@ static bool Ogg_ReadDiracHeader( logical_stream_t *p_stream, /* Backing up stream headers is not required -- seqhdrs are repeated * thoughout the stream at suitable decoding start points */ - p_stream->b_force_backup = 0; + p_stream->b_force_backup = false; /* read in useful bits from sequence header */ bs_init( &bs, p_oggpacket->packet, p_oggpacket->bytes ); @@ -1962,11 +3294,14 @@ static bool Ogg_ReadDiracHeader( logical_stream_t *p_stream, if( dirac_bool( &bs ) ) { - dirac_uint( &bs ); /* scan_format */ + p_stream->special.dirac.b_interlaced = dirac_uint( &bs ); /* scan_format */ } + else + p_stream->special.dirac.b_interlaced = false; uint32_t u_n = p_dirac_frate_tbl[pu_dirac_vidfmt_frate[u_video_format]].u_n; uint32_t u_d = p_dirac_frate_tbl[pu_dirac_vidfmt_frate[u_video_format]].u_d; + u_d = __MAX( u_d, 1 ); if( dirac_bool( &bs ) ) { uint32_t u_frame_rate_index = dirac_uint( &bs ); @@ -1984,6 +3319,7 @@ static bool Ogg_ReadDiracHeader( logical_stream_t *p_stream, } } p_stream->f_rate = (float) u_n / u_d; + if ( p_stream->f_rate == 0 ) return false; /* probably is an ogg dirac es */ p_stream->fmt.i_cat = VIDEO_ES;