vlc | branch: master | Francois Cartegnie <[email protected]> | Thu May 24 18:40:26 2018 +0200| [bc9f6bb1ce26c07fded6b1065cff4ac194b68f28] | committer: Francois Cartegnie
demux: ogg: rework and simplify Was no longer understandable mess > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=bc9f6bb1ce26c07fded6b1065cff4ac194b68f28 --- modules/demux/Makefile.am | 6 +- modules/demux/ogg.c | 447 ++++++++++++++++++++++---------------------- modules/demux/ogg.h | 17 +- modules/demux/ogg_granule.c | 187 ++++++++++++++++++ modules/demux/ogg_granule.h | 27 +++ modules/demux/oggseek.c | 149 ++------------- modules/demux/oggseek.h | 14 +- 7 files changed, 467 insertions(+), 380 deletions(-) diff --git a/modules/demux/Makefile.am b/modules/demux/Makefile.am index 6f799e84c4..28e32f29d6 100644 --- a/modules/demux/Makefile.am +++ b/modules/demux/Makefile.am @@ -10,8 +10,10 @@ libflacsys_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) libflacsys_plugin_la_LIBADD = libxiph_metadata.la demux_LTLIBRARIES += libflacsys_plugin.la -libogg_plugin_la_SOURCES = demux/ogg.c demux/ogg.h demux/oggseek.c demux/oggseek.h \ - demux/xiph.h demux/opus.h +libogg_plugin_la_SOURCES = demux/ogg.c demux/ogg.h \ + demux/oggseek.c demux/oggseek.h \ + demux/ogg_granule.c demux/ogg_granule.h \ + demux/xiph.h demux/opus.h libogg_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) $(LIBVORBIS_CFLAGS) $(OGG_CFLAGS) libogg_plugin_la_LDFLAGS = $(AM_LDFLAGS) -rpath '$(demuxdir)' libogg_plugin_la_LIBADD = $(LIBVORBIS_LIBS) $(OGG_LIBS) libxiph_metadata.la diff --git a/modules/demux/ogg.c b/modules/demux/ogg.c index eeaf09acaa..4d52b3e194 100644 --- a/modules/demux/ogg.c +++ b/modules/demux/ogg.c @@ -29,6 +29,10 @@ # include "config.h" #endif +#ifdef HAVE_LIBVORBIS + #include <vorbis/codec.h> +#endif + #include <vlc_common.h> #include <vlc_plugin.h> #include <vlc_access.h> @@ -44,6 +48,7 @@ #include "xiph_metadata.h" #include "ogg.h" #include "oggseek.h" +#include "ogg_granule.h" #include "opus.h" /***************************************************************************** @@ -121,7 +126,6 @@ static int Control( demux_t *, int, va_list ); /* Bitstream manipulation */ static int Ogg_ReadPage ( demux_t *, ogg_page * ); -static void Ogg_UpdatePCR ( demux_t *, logical_stream_t *, ogg_packet * ); static void Ogg_DecodePacket ( demux_t *, logical_stream_t *, ogg_packet * ); static unsigned Ogg_OpusPacketDuration( ogg_packet * ); static void Ogg_SendOrQueueBlocks( demux_t *, logical_stream_t *, block_t * ); @@ -580,8 +584,11 @@ static int Demux( demux_t * p_demux ) if ( p_stream->prepcr.pp_blocks ) { - mtime_t pagestamp = Oggseek_GranuleToAbsTimestamp( p_stream, ogg_page_granulepos( &p_sys->current_page ), false ); - p_stream->i_previous_pcr = pagestamp; + mtime_t i_end = Ogg_GranuleToTime( p_stream, + ogg_page_granulepos( &p_sys->current_page ), + false, false ); + mtime_t i_end_backup = i_end; + #ifdef HAVE_LIBVORBIS int i_prev_blocksize = 0; #endif @@ -624,9 +631,8 @@ static int Demux( demux_t * p_demux ) // PASS 2 bool b_fixed = false; - date_t d; - date_Init( &d, p_stream->f_rate, 1 ); - date_Set( &d, p_sys->i_nzpcr_offset + pagestamp - VLC_TS_0 ); + date_t d = p_stream->dts; + date_Set( &d, p_sys->i_nzpcr_offset + i_end - VLC_TS_0 ); for( int i=p_stream->prepcr.i_used - 1; i>=0; i-- ) { block_t *p_block = p_stream->prepcr.pp_blocks[i]; @@ -635,7 +641,7 @@ static int Demux( demux_t * p_demux ) case VLC_CODEC_SPEEX: case VLC_CODEC_OPUS: case VLC_CODEC_VORBIS: - if( pagestamp != VLC_TS_INVALID ) + if( i_end != VLC_TS_INVALID ) { date_Decrement( &d, p_block->i_nb_samples ); p_block->i_pts = date_Get( &d ) + VLC_TS_0; @@ -651,7 +657,7 @@ static int Demux( demux_t * p_demux ) default: if ( p_stream->fmt.i_cat == VIDEO_ES ) { - if( pagestamp != VLC_TS_INVALID ) + if( i_end != VLC_TS_INVALID ) { date_Decrement( &d, 1 ); p_block->i_pts = date_Get( &d ) + VLC_TS_0; @@ -663,26 +669,22 @@ static int Demux( demux_t * p_demux ) if ( b_fixed ) { - pagestamp = p_stream->i_previous_pcr; /* as set above */ - p_stream->i_pcr = pagestamp; - p_stream->i_pcr += p_sys->i_nzpcr_offset; - p_stream->i_previous_granulepos = ogg_page_granulepos( &p_sys->current_page ); + i_end = i_end_backup; /* as set above */ + if( i_end != VLC_TS_INVALID ) + p_stream->i_pcr = i_end + p_sys->i_nzpcr_offset; } FREENULL(p_stream->prepcr.pp_blocks); p_stream->prepcr.i_used = 0; Ogg_SendOrQueueBlocks( p_demux, p_stream, NULL ); - } - mtime_t i_pagestamp = Oggseek_GranuleToAbsTimestamp( p_stream, - ogg_page_granulepos( &p_sys->current_page ), false ); - if ( i_pagestamp != VLC_TS_INVALID ) - { - p_stream->i_pcr = i_pagestamp; - p_stream->i_pcr += p_sys->i_nzpcr_offset; - } + mtime_t i_pcr = Ogg_GranuleToTime( p_stream, + ogg_page_granulepos( &p_sys->current_page ), + !p_stream->b_contiguous, false ); + if ( i_pcr != VLC_TS_INVALID ) + p_stream->i_pcr = p_sys->i_nzpcr_offset + i_pcr; if( !p_sys->b_page_waiting ) break; @@ -727,8 +729,7 @@ static void Ogg_ResetStream( logical_stream_t *p_stream ) /* 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; + date_Set( &p_stream->dts, VLC_TS_INVALID ); ogg_stream_reset( &p_stream->os ); FREENULL( p_stream->prepcr.pp_blocks ); p_stream->prepcr.i_size = 0; @@ -1026,133 +1027,69 @@ static int Ogg_ReadPage( demux_t *p_demux, ogg_page *p_oggpage ) return VLC_SUCCESS; } -/**************************************************************************** - * Ogg_UpdatePCR: update the PCR (90kHz program clock reference) for the - * current stream. - ****************************************************************************/ -static void Ogg_UpdatePCR( demux_t *p_demux, logical_stream_t *p_stream, - ogg_packet *p_oggpacket ) +static void Ogg_SetNextFrame( 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; + VLC_UNUSED(p_demux); + ogg_int64_t i_granule = p_oggpacket->granulepos; - /* Convert the granulepos into a pcr */ - if ( p_oggpacket->granulepos == 0 ) + if( Ogg_GranuleIsValid( p_stream, i_granule ) ) { - /* We're in headers, and we haven't parsed 1st data packet yet */ -// p_stream->i_pcr = VLC_TS_UNKNOWN; - if( p_stream->b_oggds && p_oggpacket->bytes > 0 && - (p_oggpacket->packet[0] & PACKET_TYPE_HEADER) == 0 ) + mtime_t i_endtime = Ogg_GranuleToTime( p_stream, i_granule, false, false ); + assert( !p_stream->b_contiguous || i_endtime != VLC_TS_INVALID ); + if( i_endtime != VLC_TS_INVALID ) { - p_stream->i_pcr = VLC_TS_0 + p_ogg->i_nzpcr_offset; + date_Set( &p_stream->dts, i_endtime ); + return; } } - else if( p_oggpacket->granulepos > 0 ) + + /* Do Interpolation if can't compute directly from granule */ + if( date_Get( &p_stream->dts ) != VLC_TS_INVALID ) { - if( p_stream->fmt.i_codec == VLC_CODEC_THEORA || - 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->fmt.i_codec == VLC_CODEC_OGGSPOTS || - (p_stream->b_oggds && p_stream->fmt.i_cat == VIDEO_ES) ) + if( p_stream->fmt.i_cat == VIDEO_ES ) { - p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream, - p_oggpacket->granulepos, true ); - p_stream->i_pcr += p_ogg->i_nzpcr_offset; + date_Increment( &p_stream->dts, 1 ); } - else if ( p_stream->i_previous_granulepos > 0 ) + else if( p_stream->fmt.i_cat == AUDIO_ES ) { - ogg_int64_t sample = p_stream->i_previous_granulepos; - - if( p_stream->fmt.i_codec == VLC_CODEC_OPUS && p_oggpacket->e_o_s ) - p_stream->i_end_trim = p_oggpacket->granulepos - sample; - - sample -= p_stream->i_pre_skip; - - 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_oggpacket->granulepos == -1 ) - { - unsigned i_duration; - /* no granulepos available, try to interpolate the pcr. - * If we can't then don't touch the old value. */ - if( p_stream->b_oggds && p_stream->fmt.i_cat == VIDEO_ES ) - { - if( p_stream->i_previous_granulepos > 0 ) - { - p_stream->i_pcr = Oggseek_GranuleToAbsTimestamp( p_stream, ++p_stream->i_previous_granulepos, false ); - p_stream->i_pcr += p_ogg->i_nzpcr_offset; - } - /* First frame in ogm can be -1 (0 0 -1 2 3 -1 5 ...) */ - else if( p_stream->i_previous_granulepos == 0 ) - { - p_stream->i_pcr = VLC_TS_0 + p_ogg->i_nzpcr_offset; - } - else + int64_t i_samples = 0; + switch( p_stream->fmt.i_codec ) { - p_stream->i_pcr += (CLOCK_FREQ / p_stream->f_rate); - } - } + case VLC_CODEC_OPUS: + i_samples = Ogg_OpusPacketDuration( p_oggpacket ); + break; + case VLC_CODEC_SPEEX: + i_samples = p_stream->special.speex.i_framesize * + p_stream->special.speex.i_framesperpacket; + break; #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; - } + case VLC_CODEC_VORBIS: + if( p_stream->special.vorbis.p_info && + VORBIS_HEADERS_VALID(p_stream) ) + { + long i_blocksize = vorbis_packet_blocksize( + p_stream->special.vorbis.p_info, p_oggpacket ); + /* duration in samples per channel */ + if ( p_stream->special.vorbis.i_prev_blocksize ) + i_samples = ( i_blocksize + p_stream->special.vorbis.i_prev_blocksize ) / 4; + else + i_samples = i_blocksize / 2; + p_stream->special.vorbis.i_prev_blocksize = i_blocksize; + } + break; #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 = 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; - sample -= p_stream->i_pre_skip; - - 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_cat == VIDEO_ES && p_stream->i_pcr > VLC_TS_UNKNOWN ) - { - p_stream->i_pcr += (CLOCK_FREQ / p_stream->f_rate); - } - else if( p_stream->fmt.i_bitrate && p_stream->i_pcr > VLC_TS_UNKNOWN ) - { - p_stream->i_pcr += ( CLOCK_FREQ * p_oggpacket->bytes / - p_stream->fmt.i_bitrate / 8 ); + default: + if( p_stream->fmt.i_bitrate ) + { + i_samples = 8 * p_oggpacket->bytes * p_stream->dts.i_divider_num; + i_samples /= p_stream->fmt.i_bitrate / p_stream->dts.i_divider_den; + } + break; + } + date_Increment( &p_stream->dts, i_samples ); } } - - p_stream->i_previous_granulepos = p_oggpacket->granulepos; } static void Ogg_SendOrQueueBlocks( demux_t *p_demux, logical_stream_t *p_stream, @@ -1223,6 +1160,17 @@ static void Ogg_SendOrQueueBlocks( demux_t *p_demux, logical_stream_t *p_stream, } } +static bool Ogg_IsHeaderPacket( const logical_stream_t *p_stream, + const ogg_packet *p_oggpacket ) +{ + if ( p_stream->b_oggds ) + { + return p_oggpacket->bytes > 0 && + (p_oggpacket->packet[0] & PACKET_TYPE_HEADER); + } + else return (p_oggpacket->granulepos == 0); +} + /**************************************************************************** * Ogg_DecodePacket: Decode an Ogg packet. ****************************************************************************/ @@ -1398,8 +1346,16 @@ static void Ogg_DecodePacket( demux_t *p_demux, p_stream->b_initializing = false; } - /* Convert the granulepos into the next pcr */ - Ogg_UpdatePCR( p_demux, p_stream, p_oggpacket ); + mtime_t i_dts = Ogg_GranuleToTime( p_stream, p_oggpacket->granulepos, true, false ); + mtime_t i_expected_dts = date_Get( &p_stream->dts ); /* Interpolated or previous end time */ + if( i_dts == VLC_TS_INVALID ) + i_dts = i_expected_dts; + else + date_Set( &p_stream->dts, i_dts ); + + /* Write end granule as next start, or do interpolation */ + if( !Ogg_IsHeaderPacket( p_stream, p_oggpacket ) ) + Ogg_SetNextFrame( p_demux, p_stream, p_oggpacket ); if( !b_selected ) { @@ -1408,14 +1364,30 @@ static void Ogg_DecodePacket( demux_t *p_demux, return; } - if( !( p_block = block_Alloc( p_oggpacket->bytes ) ) ) return; + if( !( p_block = block_Alloc( 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); ) + /* Set effective timestamp */ + if( i_dts != VLC_TS_INVALID ) + p_block->i_dts = p_sys->i_nzpcr_offset + i_dts; - if( p_stream->fmt.i_codec == VLC_CODEC_OPUS ) + /* Vorbis and Opus can trim the end of a stream using granule positions. */ + if( p_oggpacket->e_o_s ) + { + mtime_t i_endtime = Ogg_GranuleToTime( p_stream, p_oggpacket->granulepos, false, false ); + if( i_endtime != VLC_TS_INVALID && i_expected_dts != VLC_TS_INVALID ) + { + p_block->i_length = i_endtime - i_expected_dts; + p_block->i_flags |= BLOCK_FLAG_END_OF_SEQUENCE; + } + } + + if( p_stream->fmt.i_codec == VLC_CODEC_OPUS ) /* also required for trimming */ p_block->i_nb_samples = Ogg_OpusPacketDuration( p_oggpacket ); + DemuxDebug( msg_Dbg(p_demux, "block set from granule %"PRId64" to pts/pcr %"PRId64" skip %d", + p_oggpacket->granulepos, p_block->i_dts, p_stream->i_skip_frames); ) + /* may need to preroll after a seek or in case of preskip */ if ( p_stream->i_skip_frames > 0 ) { @@ -1450,40 +1422,22 @@ static void Ogg_DecodePacket( demux_t *p_demux, if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC ) { - p_block->i_dts = Oggseek_GranuleToAbsTimestamp( p_stream, p_oggpacket->granulepos, false ); - p_block->i_pts = Oggseek_GranuleToAbsTimestamp( p_stream, p_oggpacket->granulepos, true ); - /* 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_oggpacket->granulepos > 0 ) + p_block->i_pts = Ogg_GranuleToTime( p_stream, p_oggpacket->granulepos, true, true ); } else if( p_stream->fmt.i_codec == VLC_CODEC_THEORA ) { - p_block->i_pts = - p_block->i_dts = p_stream->i_pcr; - } - else - { - p_block->i_pts = VLC_TS_INVALID; - p_block->i_dts = p_stream->i_pcr; + p_block->i_pts = p_block->i_dts; } } else if( p_stream->fmt.i_cat == AUDIO_ES ) { - /* Blatant abuse of the i_length field. */ - if( p_stream->i_end_trim > 0 ) - { - p_block->i_length = p_stream->i_end_trim * CLOCK_FREQ / p_stream->f_rate; - p_block->i_flags |= BLOCK_FLAG_END_OF_SEQUENCE; - } - p_block->i_pts = p_block->i_dts = p_stream->i_pcr; + p_block->i_pts = p_block->i_dts; } else if( p_stream->fmt.i_cat == SPU_ES ) { p_block->i_length = 0; - p_block->i_pts = p_block->i_dts = p_stream->i_pcr; + p_block->i_pts = p_block->i_dts; } if( p_stream->fmt.i_codec != VLC_CODEC_VORBIS && @@ -1567,7 +1521,7 @@ static unsigned Ogg_OpusPacketDuration( ogg_packet *p_oggpacket ) ****************************************************************************/ static int Ogg_FindLogicalStreams( demux_t *p_demux ) { - demux_sys_t *p_ogg = p_demux->p_sys ; + demux_sys_t *p_ogg = p_demux->p_sys; ogg_packet oggpacket; p_ogg->i_total_length = stream_Size ( p_demux->s ); @@ -1593,6 +1547,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) es_format_Init( &p_stream->fmt, UNKNOWN_ES, 0 ); es_format_Init( &p_stream->fmt_old, UNKNOWN_ES, 0 ); p_stream->b_initializing = true; + p_stream->b_contiguous = true; /* default */ /* Setup the logical stream */ p_stream->i_serial_no = ogg_page_serialno( &p_ogg->current_page ); @@ -1633,9 +1588,10 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) es_format_Change( &p_stream->fmt, AUDIO_ES, VLC_CODEC_SPEEX ); if ( Ogg_ReadSpeexHeader( p_stream, &oggpacket ) ) msg_Dbg( p_demux, "found speex header, channels: %i, " - "rate: %i, bitrate: %i, frames: %i group %i", + "rate: %"PRIu32"/%"PRIu32", bitrate: %i, frames: %i group %i", p_stream->fmt.audio.i_channels, - (int)p_stream->f_rate, p_stream->fmt.i_bitrate, + p_stream->dts.i_divider_num, p_stream->dts.i_divider_den, + p_stream->fmt.i_bitrate, p_stream->special.speex.i_framesize, p_stream->special.speex.i_framesperpacket ); else @@ -1707,8 +1663,9 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) es_format_Change( &p_stream->fmt, VIDEO_ES, VLC_CODEC_THEORA ); 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 ); + "found theora header, bitrate: %i, rate: %"PRIu32"/%"PRIu32, + p_stream->fmt.i_bitrate, + p_stream->dts.i_divider_num, p_stream->dts.i_divider_den ); else { msg_Dbg( p_demux, "found invalid Theora header" ); @@ -1724,8 +1681,9 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) es_format_Change( &p_stream->fmt, VIDEO_ES, VLC_CODEC_DAALA ); 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 ); + "found daala header, bitrate: %i, rate: %"PRIu32"/%"PRIu32, + p_stream->fmt.i_bitrate, + p_stream->dts.i_divider_num, p_stream->dts.i_divider_den ); else { msg_Dbg( p_demux, "found invalid Daala header" ); @@ -1758,8 +1716,8 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) es_format_Change( &p_stream->fmt, VIDEO_ES, VLC_CODEC_VP8 ); 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, + "fps: %"PRIu32"/%"PRIu32", width:%i; height:%i", + p_stream->dts.i_divider_num, p_stream->dts.i_divider_den, p_stream->fmt.video.i_width, p_stream->fmt.video.i_height ); else @@ -1807,6 +1765,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) { /* Old header type */ p_stream->b_oggds = true; + p_stream->b_contiguous = false; /* Check for video header (old format) */ if( GetDWLE((oggpacket.packet+96)) == 0x05589f80 && oggpacket.bytes >= 184 ) @@ -1819,13 +1778,12 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) msg_Dbg( p_demux, "found video header of type: %.4s", (char *)&p_stream->fmt.i_codec ); - 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 / - p_stream->fmt.video.i_frame_rate_base; + unsigned num = OGGDS_RESOLUTION; + unsigned den = GetQWLE(oggpacket.packet+164); + vlc_ureduce( &num, &den, num, den > 0 ? den : 1, OGGDS_RESOLUTION ); + p_stream->fmt.video.i_frame_rate = num; + p_stream->fmt.video.i_frame_rate_base = den; + date_Init( &p_stream->dts, num, den ); p_stream->fmt.video.i_bits_per_pixel = GetWLE((oggpacket.packet+182)); if( !p_stream->fmt.video.i_bits_per_pixel ) @@ -1841,12 +1799,20 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) p_stream->fmt.video.i_height; msg_Dbg( p_demux, - "fps: %f, width:%i; height:%i, bitcount:%i", - p_stream->f_rate, + "fps: %u/%u, width:%i; height:%i, bitcount:%i", + p_stream->fmt.video.i_frame_rate, + p_stream->fmt.video.i_frame_rate_base, p_stream->fmt.video.i_width, p_stream->fmt.video.i_height, p_stream->fmt.video.i_bits_per_pixel); + if ( !p_stream->fmt.video.i_frame_rate || + !p_stream->fmt.video.i_frame_rate_base ) + { + Ogg_LogicalStreamDelete( p_demux, p_stream ); + p_stream = NULL; + p_ogg->i_streams--; + } } /* Check for audio header (old format) */ else if( GetDWLE((oggpacket.packet+96)) == 0x05589F81 ) @@ -1872,7 +1838,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) 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 = + p_stream->fmt.audio.i_rate = GetDWLE((oggpacket.packet+128)); p_stream->fmt.i_bitrate = GetDWLE((oggpacket.packet+132)) * 8; @@ -1881,6 +1847,8 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) p_stream->fmt.audio.i_bitspersample = GetWLE((oggpacket.packet+138)); + date_Init( &p_stream->dts, p_stream->fmt.audio.i_rate, 1 ); + wf_tag_to_fourcc( i_format_tag, &p_stream->fmt.i_codec, 0 ); @@ -1900,7 +1868,8 @@ 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 ) + + if ( p_stream->fmt.audio.i_rate == 0 ) { msg_Dbg( p_demux, "invalid oggds audio header" ); Ogg_LogicalStreamDelete( p_demux, p_stream ); @@ -1924,6 +1893,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) stream_header_t *st = &tmp; p_stream->b_oggds = true; + p_stream->b_contiguous = false; memcpy( st->streamtype, &oggpacket.packet[1+0], 8 ); memcpy( st->subtype, &oggpacket.packet[1+8], 4 ); @@ -1952,11 +1922,14 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) msg_Dbg( p_demux, "found video header of type: %.4s", (char *)&p_stream->fmt.i_codec ); - p_stream->fmt.video.i_frame_rate = 10000000; - p_stream->fmt.video.i_frame_rate_base = st->time_unit; if( st->time_unit <= 0 ) st->time_unit = 400000; - p_stream->f_rate = 10000000.0 / st->time_unit; + unsigned num,den; + vlc_ureduce( &num, &den, OGGDS_RESOLUTION, st->time_unit, + OGGDS_RESOLUTION ); + date_Init( &p_stream->dts, num, den ); + p_stream->fmt.video.i_frame_rate = num; + p_stream->fmt.video.i_frame_rate_base = den; p_stream->fmt.video.i_bits_per_pixel = st->bits_per_sample; p_stream->fmt.video.i_width = st->sh.video.width; p_stream->fmt.video.i_height = st->sh.video.height; @@ -1966,8 +1939,9 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) p_stream->fmt.video.i_height; msg_Dbg( p_demux, - "fps: %f, width:%i; height:%i, bitcount:%i", - p_stream->f_rate, + "fps: %u/%u, width:%i; height:%i, bitcount:%i", + p_stream->fmt.video.i_frame_rate, + p_stream->fmt.video.i_frame_rate_base, p_stream->fmt.video.i_width, p_stream->fmt.video.i_height, p_stream->fmt.video.i_bits_per_pixel ); @@ -2008,9 +1982,13 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) 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; + unsigned num,den; + vlc_ureduce( &num, &den, + st->samples_per_unit * OGGDS_RESOLUTION, + st->time_unit > 0 ? st->time_unit : OGGDS_RESOLUTION, + OGGDS_RESOLUTION ); + date_Init( &p_stream->dts, num, den ); + p_stream->fmt.audio.i_rate = num / den; p_stream->fmt.i_bitrate = st->sh.audio.avgbytespersec * 8; p_stream->fmt.audio.i_blockalign = st->sh.audio.blockalign; p_stream->fmt.audio.i_bitspersample = st->bits_per_sample; @@ -2034,7 +2012,7 @@ 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 ) + if ( p_stream->fmt.audio.i_rate == 0 ) { msg_Dbg( p_demux, "invalid oggds audio header" ); Ogg_LogicalStreamDelete( p_demux, p_stream ); @@ -2050,7 +2028,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) msg_Dbg( p_demux, "found text subtitle header" ); es_format_Change( &p_stream->fmt, SPU_ES, VLC_CODEC_SUBT ); - p_stream->f_rate = 1000; /* granulepos is in millisec */ + date_Init( &p_stream->dts, 1000, 1 ); /* granulepos is in millisec */ } else { @@ -2075,8 +2053,9 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) { if ( Ogg_ReadOggSpotsHeader( p_stream, &oggpacket ) ) msg_Dbg( p_demux, - "found OggSpots header, time resolution: %f", - p_stream->f_rate ); + "found OggSpots header, time resolution: %u/%u", + p_stream->fmt.video.i_frame_rate, + p_stream->fmt.video.i_frame_rate_base ); else { msg_Err( p_demux, "found invalid OggSpots header" ); @@ -2219,8 +2198,7 @@ static int Ogg_BeginningOfStream( demux_t *p_demux ) else p_ogg->i_bitrate += p_stream->fmt.i_bitrate; - p_stream->i_pcr = p_stream->i_previous_pcr = VLC_TS_UNKNOWN; - p_stream->i_previous_granulepos = -1; + p_stream->i_pcr = VLC_TS_UNKNOWN; p_stream->b_reinit = false; } @@ -2644,8 +2622,9 @@ static bool 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_fps_denominator || !i_fps_numerator ) + return false; + date_Init( &p_stream->dts, i_fps_numerator, i_fps_denominator ); if ( i_version >= 3002001 ) { @@ -2708,8 +2687,9 @@ static bool Ogg_ReadDaalaHeader( logical_stream_t *p_stream, i_version = i_major * 1000000 + i_minor * 1000 + i_subminor; VLC_UNUSED(i_version); 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; + if ( !i_timebase_numerator || !i_timebase_denominator ) + return false; + date_Init( &p_stream->dts, i_timebase_numerator, i_timebase_denominator ); return true; } @@ -2729,12 +2709,14 @@ static bool Ogg_ReadVorbisHeader( logical_stream_t *p_stream, 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 ); + p_stream->fmt.audio.i_rate = oggpack_read( &opb, 32 ); + if( p_stream->fmt.audio.i_rate == 0 ) + return false; + date_Init( &p_stream->dts, p_stream->fmt.audio.i_rate, 1 ); + oggpack_adv( &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 @@ -2788,8 +2770,10 @@ static bool Ogg_ReadSpeexHeader( logical_stream_t *p_stream, oggpack_adv( &opb, 224 ); 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; + p_stream->fmt.audio.i_rate = oggpack_read( &opb, 32 ); + if ( !p_stream->fmt.audio.i_rate ) + return false; + date_Init( &p_stream->dts, p_stream->fmt.audio.i_rate, 1 ); oggpack_adv( &opb, 32 ); /* mode */ oggpack_adv( &opb, 32 ); /* mode_bitstream_version */ p_stream->fmt.audio.i_channels = oggpack_read( &opb, 32 ); @@ -2816,7 +2800,8 @@ static void Ogg_ReadOpusHeader( logical_stream_t *p_stream, /* 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.audio.i_rate = 48000; + date_Init( &p_stream->dts, p_stream->fmt.audio.i_rate, 1 ); p_stream->fmt.i_bitrate = 0; /* Cheat and get additional info ;) */ @@ -2852,13 +2837,15 @@ static bool Ogg_ReadFlacStreamInfo( demux_t *p_demux, logical_stream_t *p_stream 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_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; + msg_Dbg( p_demux, "FLAC header, channels: %"PRIu8", rate: %u", + p_stream->fmt.audio.i_channels, p_stream->fmt.audio.i_rate ); + if ( p_stream->fmt.audio.i_rate == 0 ) + return false; + date_Init( &p_stream->dts, p_stream->fmt.audio.i_rate, 1 ); } else { @@ -2894,8 +2881,9 @@ static bool Ogg_ReadKateHeader( logical_stream_t *p_stream, 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; + if ( !gnum || !gden ) + return false; + date_Init( &p_stream->dts, gnum, gden ); p_stream->fmt.psz_language = malloc(16); if( p_stream->fmt.psz_language ) @@ -2952,10 +2940,10 @@ static bool Ogg_ReadVP8Header( demux_t *p_demux, logical_stream_t *p_stream, 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; + if ( !p_stream->fmt.video.i_frame_rate || !p_stream->fmt.video.i_frame_rate_base ) + return false; + date_Init( &p_stream->dts, p_stream->fmt.video.i_frame_rate, + p_stream->fmt.video.i_frame_rate_base ); return true; /* METADATA */ case 0x02: @@ -3095,8 +3083,8 @@ static void Ogg_ReadAnnodexHeader( demux_t *p_demux, granule_rate_numerator, granule_rate_denominator, p_stream->i_secondary_header_packets, content_type_string ); - p_stream->f_rate = (float) granule_rate_numerator / - (float) granule_rate_denominator; + if( granule_rate_numerator && granule_rate_denominator ) + date_Init( &p_stream->dts, granule_rate_numerator, granule_rate_denominator ); /* What type of file do we have? * strcmp is safe to use here because we've extracted @@ -3425,8 +3413,17 @@ static bool Ogg_ReadDiracHeader( logical_stream_t *p_stream, u_d = dirac_uint( &bs ); /* frame_rate_denominator */ } } - p_stream->f_rate = (float) u_n / u_d; - if ( p_stream->f_rate == 0 ) return false; + + if( !u_n || !u_d ) + return false; + + /* + * NB, OggDirac granulepos values are in units of 2*picturerate + * When picture_coding_mode = 0 (progressive), + * pt increments by two for each picture in display order. + * When picture_coding_mode = 1 (interlace), + * pt increments by one for each field in display order. */ + date_Init( &p_stream->dts, 2 * u_n, u_d ); return true; } @@ -3477,17 +3474,17 @@ static bool Ogg_ReadOggSpotsHeader( logical_stream_t *p_stream, i_granulerate_denominator = 1; } - p_stream->f_rate = ((double)i_granulerate_numerator) / i_granulerate_denominator; - if ( p_stream->f_rate == 0 ) - { + if ( !i_granulerate_numerator || !i_granulerate_denominator ) return false; - } /* Normalize granulerate */ vlc_ureduce(&p_stream->fmt.video.i_frame_rate, &p_stream->fmt.video.i_frame_rate_base, i_granulerate_numerator, i_granulerate_denominator, 0); + date_Init( &p_stream->dts, p_stream->fmt.video.i_frame_rate, + p_stream->fmt.video.i_frame_rate_base ); + p_stream->i_granule_shift = p_oggpacket->packet[28]; return true; diff --git a/modules/demux/ogg.h b/modules/demux/ogg.h index 13c1c6d4d6..1914762e41 100644 --- a/modules/demux/ogg.h +++ b/modules/demux/ogg.h @@ -22,14 +22,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#ifdef HAVE_LIBVORBIS - #include <vorbis/codec.h> -#endif - /***************************************************************************** * Definitions of structures and functions used by this plugin *****************************************************************************/ @@ -48,6 +40,8 @@ #define PACKET_LEN_BITS2 0x02 #define PACKET_IS_SYNCPOINT 0x08 +#define OGGDS_RESOLUTION 10000000 + typedef struct oggseek_index_entry demux_index_entry_t; typedef struct ogg_skeleton_t ogg_skeleton_t; @@ -64,7 +58,8 @@ typedef struct logical_stream_s es_format_t fmt; es_format_t fmt_old; /* format of old ES is reused */ es_out_id_t *p_es; - double f_rate; + date_t dts; + bool b_contiguous; /* Granule is end of packet */ int i_serial_no; @@ -76,13 +71,11 @@ typedef struct logical_stream_s int32_t i_extra_headers_packets; void *p_headers; int i_headers; - ogg_int64_t i_previous_granulepos; ogg_int64_t i_granulepos_offset;/* first granule offset */ /* program clock reference (in units of 90kHz) derived from the previous * granulepos */ mtime_t i_pcr; - mtime_t i_previous_pcr; /* Misc */ bool b_initializing; @@ -93,8 +86,6 @@ typedef struct logical_stream_s /* Opus has a starting offset in the headers. */ int i_pre_skip; - /* Vorbis and Opus can trim the end of a stream using granule positions. */ - int i_end_trim; /* number of samples to keep */ /* offset of first keyframe for theora; can be 0 or 1 depending on version number */ int8_t i_keyframe_offset; diff --git a/modules/demux/ogg_granule.c b/modules/demux/ogg_granule.c new file mode 100644 index 0000000000..524533dd87 --- /dev/null +++ b/modules/demux/ogg_granule.c @@ -0,0 +1,187 @@ +/***************************************************************************** + * ogg_granule.c : ogg granule functions + ***************************************************************************** + * Copyright (C) 2008 - 2018 VideoLAN Authors and VideoLabs + * + * 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 Lesser General Public License for more details. + * + * 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. + *****************************************************************************/ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_LIBVORBIS + #include <vorbis/codec.h> +#endif + +#include <ogg/ogg.h> + +#include <vlc_common.h> +#include <vlc_codecs.h> +#include <vlc_es.h> + +#include "ogg.h" +#include "ogg_granule.h" + +/* Theora spec 7.1 */ +#define THEORA_FTYPE_NOTDATA 0x80 +#define THEORA_FTYPE_INTERFRAME 0x40 + +/* Checks if current packet matches codec keyframe */ +bool Ogg_IsKeyFrame( const logical_stream_t *p_stream, const ogg_packet *p_packet ) +{ + if ( p_stream->b_oggds ) + { + return ( p_packet->bytes > 0 && p_packet->packet[0] & PACKET_IS_SYNCPOINT ); + } + else switch ( p_stream->fmt.i_codec ) + { + case VLC_CODEC_THEORA: + case VLC_CODEC_DAALA: /* Same convention used in daala */ + if ( p_packet->bytes <= 0 || p_packet->packet[0] & THEORA_FTYPE_NOTDATA ) + return false; + else + return !( p_packet->packet[0] & THEORA_FTYPE_INTERFRAME ); + case VLC_CODEC_VP8: + return ( ( ( p_packet->granulepos >> 3 ) & 0x07FFFFFF ) == 0 ); + case VLC_CODEC_DIRAC: + return ( p_packet->granulepos & 0xFF8000FF ); + default: + return true; + } +} + +int64_t Ogg_GetKeyframeGranule( const logical_stream_t *p_stream, int64_t i_granule ) +{ + if ( p_stream->b_oggds ) + { + return -1; /* We have no way to know */ + } + else switch( p_stream->fmt.i_codec ) + { + case VLC_CODEC_THEORA: + case VLC_CODEC_DAALA: + return ( i_granule >> p_stream->i_granule_shift ) << p_stream->i_granule_shift; + case VLC_CODEC_DIRAC: + return ( i_granule >> 31 ) << 31; + default: + /* No change, that's keyframe */ + return i_granule; + } +} + +static int64_t Ogg_GranuleToSampleDelta( const logical_stream_t *p_stream, int64_t i_granule ) +{ + if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC ) + return (i_granule >> 9) & 0x1fff; + else + return -1; +} + +static int64_t Ogg_GranuleToSample( const logical_stream_t *p_stream, int64_t i_granule ) +{ + switch( p_stream->fmt.i_codec ) + { + case VLC_CODEC_THEORA: + case VLC_CODEC_DAALA: + case VLC_CODEC_KATE: + { + ogg_int64_t iframe = i_granule >> p_stream->i_granule_shift; + ogg_int64_t pframe = i_granule - ( iframe << p_stream->i_granule_shift ); + return iframe + pframe; + } + case VLC_CODEC_VP8: + case VLC_CODEC_OGGSPOTS: + return i_granule >> p_stream->i_granule_shift; + case VLC_CODEC_DIRAC: + return (i_granule >> 31); + case VLC_CODEC_OPUS: + case VLC_CODEC_VORBIS: + case VLC_CODEC_SPEEX: + case VLC_CODEC_FLAC: + return i_granule/* - p_stream->i_pre_skip*/; + default: + return i_granule; + } +} + +static int64_t Ogg_ShiftPacketSample( const logical_stream_t *p_stream, + int64_t i_sample, bool b_start ) +{ + /* /!\ Packet Granule as sample value ! */ + + /* granule always point to end time of packet + Except with OggDS where it is reversed */ + int64_t i_endtostartoffset = 0; /* in interval # */ + if( p_stream->b_oggds ) + i_endtostartoffset = (b_start ? 0 : 1); + else + i_endtostartoffset = (b_start ? -1 : 0); + + if( p_stream->fmt.i_cat == VIDEO_ES ) + { + if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC ) /* points to start */ + i_sample += (p_stream->special.dirac.b_interlaced ? 1 : 2) * (i_endtostartoffset + 1); + else + i_sample += i_endtostartoffset * 1; + } + else if( p_stream->fmt.i_cat == AUDIO_ES ) + { + if( p_stream->fmt.i_codec == VLC_CODEC_SPEEX ) + { + i_sample += i_endtostartoffset * + p_stream->special.speex.i_framesize * + p_stream->special.speex.i_framesperpacket; + } + else /* we can't tell */ + { + if( i_endtostartoffset != 0 ) + return -1; + } + } + return i_sample; +} + +mtime_t Ogg_SampleToTime( const logical_stream_t *p_stream, int64_t i_sample, bool b_start ) +{ + i_sample = Ogg_ShiftPacketSample( p_stream, i_sample, b_start ); + if( i_sample < 0 ) + return VLC_TS_INVALID; + + date_t d = p_stream->dts; + date_Set(&d, VLC_TS_0); + return date_Increment( &d, i_sample ); +} + +bool Ogg_GranuleIsValid( const logical_stream_t *p_stream, int64_t i_granule ) +{ + /* First frame in ogm is 0 (0[header] 0[frame] -1 2 3 -1 5 ...) */ + return !( i_granule < 1 - !!p_stream->b_oggds ); +} + +mtime_t Ogg_GranuleToTime( const logical_stream_t *p_stream, int64_t i_granule, + bool b_start, bool b_pts ) +{ + if( !Ogg_GranuleIsValid( p_stream, i_granule ) ) + return VLC_TS_INVALID; + + int64_t i_sample = Ogg_GranuleToSample( p_stream, i_granule ); + if( b_pts ) + { + int64_t i_delta = Ogg_GranuleToSampleDelta( p_stream, i_granule ); + if( i_delta != -1 ) + i_sample += i_delta; + } + return Ogg_SampleToTime( p_stream, i_sample, b_start ); +} diff --git a/modules/demux/ogg_granule.h b/modules/demux/ogg_granule.h new file mode 100644 index 0000000000..2bdf409e29 --- /dev/null +++ b/modules/demux/ogg_granule.h @@ -0,0 +1,27 @@ +/***************************************************************************** + * ogg_granule.h : ogg granule functions + ***************************************************************************** + * Copyright (C) 2008 - 2018 VideoLAN Authors and VideoLabs + * + * 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 Lesser General Public License for more details. + * + * 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. + *****************************************************************************/ + +int64_t Ogg_GetKeyframeGranule ( const logical_stream_t *, int64_t i_granule ); +bool Ogg_IsKeyFrame ( const logical_stream_t *, const ogg_packet * ); +mtime_t Ogg_GranuleToTime( const logical_stream_t *, int64_t i_granule, + bool b_packetstart, bool b_pts ); +mtime_t Ogg_SampleToTime( const logical_stream_t *, int64_t i_sample, + bool b_packetstart ); +bool Ogg_GranuleIsValid( const logical_stream_t *, int64_t i_granule ); diff --git a/modules/demux/oggseek.c b/modules/demux/oggseek.c index ff4c5f7f7b..3e774c79ea 100644 --- a/modules/demux/oggseek.c +++ b/modules/demux/oggseek.c @@ -30,6 +30,10 @@ # include "config.h" #endif +#ifdef HAVE_LIBVORBIS + #include <vorbis/codec.h> +#endif + #include <vlc_common.h> #include <vlc_demux.h> @@ -40,10 +44,7 @@ #include "ogg.h" #include "oggseek.h" - -/* Theora spec 7.1 */ -#define THEORA_FTYPE_NOTDATA 0x80 -#define THEORA_FTYPE_INTERFRAME 0x40 +#include "ogg_granule.h" #define SEGMENT_NOT_FOUND -1 @@ -55,14 +56,6 @@ typedef struct packetStartCoordinates int64_t i_skip; } packetStartCoordinates; -//#define OGG_SEEK_DEBUG 1 -#ifdef OGG_SEEK_DEBUG - #define OggDebug(code) code - #define OggNoDebug(code) -#else - #define OggDebug(code) - #define OggNoDebug(code) code -#endif /************************************************************ * index entries *************************************************************/ @@ -289,7 +282,8 @@ void Oggseek_ProbeEnd( demux_t *p_demux ) if ( p_sys->pp_stream[i]->i_serial_no != ogg_page_serialno( &page ) ) continue; - i_length = Oggseek_GranuleToAbsTimestamp( p_sys->pp_stream[i], i_granule, false ); + i_length = Ogg_GranuleToTime( p_sys->pp_stream[i], i_granule, + !p_sys->pp_stream[i]->b_contiguous, false ); if( i_length > VLC_TS_INVALID ) p_sys->i_length = __MAX( p_sys->i_length, (i_length - VLC_TS_0) / 1000000 ); break; @@ -441,49 +435,6 @@ static int64_t find_first_page_granule( demux_t *p_demux, } } -/* Checks if current packet matches codec keyframe */ -bool Ogg_IsKeyFrame( logical_stream_t *p_stream, ogg_packet *p_packet ) -{ - if ( p_stream->b_oggds ) - { - return ( p_packet->bytes > 0 && p_packet->packet[0] & PACKET_IS_SYNCPOINT ); - } - else switch ( p_stream->fmt.i_codec ) - { - case VLC_CODEC_THEORA: - case VLC_CODEC_DAALA: /* Same convention used in daala */ - if ( p_packet->bytes <= 0 || p_packet->packet[0] & THEORA_FTYPE_NOTDATA ) - return false; - else - return !( p_packet->packet[0] & THEORA_FTYPE_INTERFRAME ); - case VLC_CODEC_VP8: - return ( ( ( p_packet->granulepos >> 3 ) & 0x07FFFFFF ) == 0 ); - case VLC_CODEC_DIRAC: - return ( p_packet->granulepos & 0xFF8000FF ); - default: - return true; - } -} - -int64_t Ogg_GetKeyframeGranule( logical_stream_t *p_stream, int64_t i_granule ) -{ - if ( p_stream->b_oggds ) - { - return -1; /* We have no way to know */ - } - else if( p_stream->fmt.i_codec == VLC_CODEC_THEORA || - p_stream->fmt.i_codec == VLC_CODEC_DAALA ) - { - return ( i_granule >> p_stream->i_granule_shift ) << p_stream->i_granule_shift; - } - else if( p_stream->fmt.i_codec == VLC_CODEC_DIRAC ) - { - return ( i_granule >> 31 ) << 31; - } - /* No change, that's keyframe or it can't be shifted out (oggds) */ - return i_granule; -} - static bool OggSeekToPacket( demux_t *p_demux, logical_stream_t *p_stream, int64_t i_granulepos, packetStartCoordinates *p_lastpacketcoords, bool b_exact ) @@ -674,82 +625,6 @@ restart: return i_result; } -/* Dont use b_presentation with frames granules ! */ -mtime_t Oggseek_GranuleToAbsTimestamp( logical_stream_t *p_stream, - int64_t i_granule, bool b_presentation ) -{ - mtime_t i_timestamp = VLC_TS_INVALID; - if ( i_granule < 1 - !!p_stream->b_oggds ) - return VLC_TS_INVALID; - - if ( p_stream->b_oggds ) - { - i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate; - } - else switch( p_stream->fmt.i_codec ) - { - case VLC_CODEC_THEORA: - case VLC_CODEC_DAALA: - case VLC_CODEC_KATE: - { - ogg_int64_t iframe = i_granule >> p_stream->i_granule_shift; - ogg_int64_t pframe = i_granule - ( iframe << p_stream->i_granule_shift ); - /* See Theora A.2.3 */ - if ( b_presentation ) pframe -= p_stream->i_keyframe_offset; - i_timestamp = ( iframe + pframe ) * CLOCK_FREQ / p_stream->f_rate; - break; - } - case VLC_CODEC_VP8: - { - ogg_int64_t frame = i_granule >> p_stream->i_granule_shift; - if ( b_presentation ) frame--; - i_timestamp = frame * CLOCK_FREQ / p_stream->f_rate; - break; - } - case VLC_CODEC_DIRAC: - { - ogg_int64_t i_dts = i_granule >> 31; - ogg_int64_t delay = (i_granule >> 9) & 0x1fff; - /* NB, OggDirac granulepos values are in units of 2*picturerate */ - double f_rate = p_stream->f_rate; - if ( !p_stream->special.dirac.b_interlaced ) f_rate *= 2; - if ( b_presentation ) i_dts += delay; - i_timestamp = i_dts * CLOCK_FREQ / f_rate; - break; - } - case VLC_CODEC_OPUS: - { - if ( b_presentation ) return VLC_TS_INVALID; - i_timestamp = ( i_granule - p_stream->i_pre_skip ) * CLOCK_FREQ / 48000; - break; - } - case VLC_CODEC_VORBIS: - case VLC_CODEC_FLAC: - { - if ( b_presentation ) return VLC_TS_INVALID; - i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate; - break; - } - case VLC_CODEC_SPEEX: - { - if ( b_presentation ) - i_granule -= p_stream->special.speex.i_framesize * - p_stream->special.speex.i_framesperpacket; - i_timestamp = i_granule * CLOCK_FREQ / p_stream->f_rate; - break; - } - case VLC_CODEC_OGGSPOTS: - { - if ( b_presentation ) return VLC_TS_INVALID; - i_timestamp = ( i_granule >> p_stream->i_granule_shift ) - * CLOCK_FREQ / p_stream->f_rate; - break; - } - } - - return i_timestamp != VLC_TS_INVALID ? i_timestamp + VLC_TS_0 : VLC_TS_INVALID; -} - /* returns pos */ static int64_t OggBisectSearchByTime( demux_t *p_demux, logical_stream_t *p_stream, mtime_t i_targettime, int64_t i_pos_lower, int64_t i_pos_upper) @@ -803,14 +678,18 @@ static int64_t OggBisectSearchByTime( demux_t *p_demux, logical_stream_t *p_stre p_stream, ¤t.i_granule ); - current.i_timestamp = Oggseek_GranuleToAbsTimestamp( p_stream, - current.i_granule, false ); + current.i_timestamp = Ogg_GranuleToTime( p_stream, current.i_granule, + !p_stream->b_contiguous, false ); if ( current.i_timestamp == VLC_TS_INVALID && current.i_granule > 0 ) { msg_Err( p_demux, "Unmatched granule. New codec ?" ); return -1; } + else if ( current.i_timestamp < 0 ) /* due to preskip with some codecs */ + { + current.i_timestamp = 0; + } if ( current.i_pos != -1 && current.i_granule != -1 ) { @@ -874,7 +753,7 @@ static int64_t OggBisectSearchByTime( demux_t *p_demux, logical_stream_t *p_stre i_keyframegranule >> p_stream->i_granule_shift, bestlower.i_granule, i_pos_upper, - Oggseek_GranuleToAbsTimestamp( p_stream, i_keyframegranule, false ) ) ); + Ogg_GranuleToTime( p_stream, i_keyframegranule, !p_stream->b_contiguous, false ) ) ); OggDebug( msg_Dbg( p_demux, "Seeking back to %"PRId64, __MAX ( bestlower.i_pos - OGGSEEK_BYTES_TO_READ, p_stream->i_data_start ) ) ); diff --git a/modules/demux/oggseek.h b/modules/demux/oggseek.h index 4225a13472..93755555ee 100644 --- a/modules/demux/oggseek.h +++ b/modules/demux/oggseek.h @@ -26,6 +26,15 @@ * Preamble *****************************************************************************/ +//#define OGG_SEEK_DEBUG 1 +#ifdef OGG_SEEK_DEBUG + #define OggDebug(code) code + #define OggNoDebug(code) +#else + #define OggDebug(code) + #define OggNoDebug(code) code +#endif + #define PAGE_HEADER_BYTES 27 #define OGGSEEK_BYTES_TO_READ 8500 @@ -49,11 +58,6 @@ struct oggseek_index_entry int64_t i_pagepos_end; }; -int64_t Ogg_GetKeyframeGranule ( logical_stream_t *p_stream, int64_t i_granule ); -bool Ogg_IsKeyFrame ( logical_stream_t *, ogg_packet * ); - -mtime_t Oggseek_GranuleToAbsTimestamp ( logical_stream_t *p_stream, int64_t i_granule, - bool b_presentation ); int Oggseek_BlindSeektoAbsoluteTime ( demux_t *, logical_stream_t *, mtime_t, bool ); int Oggseek_BlindSeektoPosition ( demux_t *, logical_stream_t *, double f, bool ); int Oggseek_SeektoAbsolutetime ( demux_t *, logical_stream_t *, mtime_t ); _______________________________________________ vlc-commits mailing list [email protected] https://mailman.videolan.org/listinfo/vlc-commits
