X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=modules%2Fdemux%2Fogg.c;h=b5750c3d1f536686101685945efadcc013901118;hb=6d5336200143e6d1ad70ef653c72265d25f67640;hp=18b43dedea30df8dedba8787bbeac0548d500174;hpb=9c7198eaeda34f791fd007287e4089099ed70e22;p=vlc diff --git a/modules/demux/ogg.c b/modules/demux/ogg.c index 18b43dedea..b5750c3d1f 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. *****************************************************************************/ /***************************************************************************** @@ -40,8 +40,7 @@ #include #include #include "xiph.h" -#include "vorbis.h" -#include "kate_categories.h" +#include "xiph_metadata.h" #include "ogg.h" #include "oggseek.h" @@ -135,12 +134,13 @@ static bool Ogg_LogicalStreamResetEsFormat( demux_t *p_demux, logical_stream_t * /* */ static void Ogg_ExtractMeta( demux_t *p_demux, vlc_fourcc_t i_codec, const uint8_t *p_headers, int i_headers ); +static int64_t Ogg_GetLastPacket( demux_t *p_demux, logical_stream_t *p_stream, double f_rate ); /* Logical bitstream headers */ static void Ogg_ReadTheoraHeader( demux_t *, logical_stream_t *, ogg_packet * ); -static void Ogg_ReadVorbisHeader( logical_stream_t *, ogg_packet * ); +static void Ogg_ReadVorbisHeader( demux_t *, logical_stream_t *, ogg_packet * ); static void Ogg_ReadSpeexHeader( logical_stream_t *, ogg_packet * ); -static void Ogg_ReadOpusHeader( logical_stream_t *, ogg_packet * ); +static void Ogg_ReadOpusHeader( demux_t *, 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( demux_t *, logical_stream_t *, ogg_packet * ); @@ -155,7 +155,6 @@ 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 ) ) @@ -163,30 +162,22 @@ static int Open( vlc_object_t * p_this ) return VLC_EGENERIC; } - /* 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; - - /* Begnning of stream, tell the demux to look for elementary streams. */ - p_sys->i_bos = 0; - p_sys->i_eos = 0; - p_sys->i_length = -1; + /* 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 ); return VLC_SUCCESS; } @@ -207,6 +198,8 @@ static void Close( vlc_object_t *p_this ) if( p_sys->p_old_stream ) Ogg_LogicalStreamDelete( p_demux, p_sys->p_old_stream ); + TAB_CLEAN( p_sys->i_seekpoints, p_sys->pp_seekpoints ); + free( p_sys ); } @@ -317,7 +310,7 @@ static int Demux( demux_t * p_demux ) oggpacket.bytes >= 7 && ! memcmp( oggpacket.packet, "\x01vorbis", 7 ) ) { - Ogg_ReadVorbisHeader( p_stream, &oggpacket ); + Ogg_ReadVorbisHeader( p_demux, p_stream, &oggpacket ); p_stream->i_secondary_header_packets = 0; } else if( p_stream->fmt.i_codec == VLC_CODEC_CMML ) @@ -427,6 +420,22 @@ static int Demux( demux_t * p_demux ) return 1; } +static void Ogg_ResetStreamHelper( demux_sys_t *p_sys ) +{ + for( int i = 0; i < p_sys->i_streams; i++ ) + { + logical_stream_t *p_stream = p_sys->pp_stream[i]; + + /* 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; + p_stream->i_previous_granulepos = -1; + ogg_stream_reset( &p_stream->os ); + } + ogg_sync_reset( &p_sys->oy ); +} + /***************************************************************************** * Control: *****************************************************************************/ @@ -436,7 +445,6 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) vlc_meta_t *p_meta; int64_t *pi64; bool *pb_bool; - int i; switch( i_query ) { @@ -484,18 +492,7 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) return VLC_EGENERIC; } - for( i = 0; i < p_sys->i_streams; i++ ) - { - logical_stream_t *p_stream = p_sys->pp_stream[i]; - - /* 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; - p_stream->i_previous_granulepos = -1; - ogg_stream_reset( &p_stream->os ); - } - ogg_sync_reset( &p_sys->oy ); + Ogg_ResetStreamHelper( p_sys ); return demux_vaControlHelper( p_demux->s, 0, -1, p_sys->i_bitrate, 1, i_query, args ); case DEMUX_GET_LENGTH: @@ -506,6 +503,50 @@ static int Control( demux_t *p_demux, int i_query, va_list args ) *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++ ) + { + TAB_APPEND( p_title->i_seekpoint, p_title->seekpoint, p_sys->pp_seekpoints[i] ); + } + *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; + if( p_sys->i_bos > 0 ) + { + return VLC_EGENERIC; + } + + Ogg_ResetStreamHelper( p_sys ); + int64_t i_block = p_sys->pp_seekpoints[i_seekpoint]->i_time_offset * p_sys->i_bitrate / INT64_C(8000000); + if( stream_Seek( p_demux->s, i_block ) ) + return VLC_EGENERIC; + return VLC_SUCCESS; + } default: return demux_vaControlHelper( p_demux->s, 0, -1, p_sys->i_bitrate, @@ -673,12 +714,14 @@ static void Ogg_DecodePacket( demux_t *p_demux, case VLC_CODEC_VORBIS: case VLC_CODEC_SPEEX: 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_OPUS: - if( p_stream->i_packets_backup == 2 ) p_stream->b_force_backup = 0; + if( p_stream->i_packets_backup == 2 ) + p_stream->b_force_backup = false; b_xiph = true; break; @@ -686,11 +729,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; @@ -701,12 +744,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->i_kate_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; } @@ -833,7 +877,7 @@ static void Ogg_DecodePacket( demux_t *p_demux, return; } - if( !( p_block = block_New( p_demux, p_oggpacket->bytes ) ) ) return; + if( !( p_block = block_Alloc( p_oggpacket->bytes ) ) ) return; /* may need to preroll after a seek */ @@ -1096,7 +1140,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) if( oggpacket.bytes >= 7 && ! memcmp( oggpacket.packet, "\x01vorbis", 7 ) ) { - Ogg_ReadVorbisHeader( p_stream, &oggpacket ); + Ogg_ReadVorbisHeader( p_demux, p_stream, &oggpacket ); msg_Dbg( p_demux, "found vorbis header" ); } /* Check for Speex header */ @@ -1113,7 +1157,7 @@ static int Ogg_FindLogicalStreams( demux_t *p_demux ) else if( oggpacket.bytes >= 8 && ! memcmp( oggpacket.packet, "OpusHead", 8 ) ) { - Ogg_ReadOpusHeader( p_stream, &oggpacket ); + Ogg_ReadOpusHeader( p_demux, p_stream, &oggpacket ); msg_Dbg( p_demux, "found opus header, channels: %i, " "pre-skip: %i", p_stream->fmt.audio.i_channels, @@ -1129,7 +1173,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; @@ -1146,7 +1190,7 @@ 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; @@ -1442,7 +1486,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 */ @@ -1781,8 +1825,26 @@ static void Ogg_ExtractXiphMeta( demux_t *p_demux, const void *p_headers, unsign /* TODO how to handle multiple comments properly ? */ if( i_count >= 2 && pi_size[1] > i_skip ) + { + int i_cover_score = 0; + int i_cover_idx = 0; 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 ); + &p_ogg->i_attachments, &p_ogg->attachments, + &i_cover_score, &i_cover_idx, + &p_ogg->i_seekpoints, &p_ogg->pp_seekpoints ); + 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 ); + } + } + + if( p_ogg->i_seekpoints > 1 ) + { + p_demux->info.i_update |= INPUT_UPDATE_TITLE_LIST; + } for( unsigned i = 0; i < i_count; i++ ) free( pp_data[i] ); @@ -1826,6 +1888,13 @@ static void Ogg_ExtractMeta( demux_t *p_demux, vlc_fourcc_t i_codec, const uint8 p_demux->info.i_update |= INPUT_UPDATE_META; } +static int64_t Ogg_GetLastPacket( demux_t *p_demux, logical_stream_t *p_stream, + double f_rate ) +{ + int64_t last_packet = oggseek_get_last_frame( p_demux, p_stream ); + return ( last_packet >= 0 ) ? last_packet / f_rate : -1; +} + static void Ogg_ReadTheoraHeader( demux_t *p_demux, logical_stream_t *p_stream, ogg_packet *p_oggpacket ) { @@ -1844,7 +1913,7 @@ static void Ogg_ReadTheoraHeader( demux_t *p_demux, 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 ); @@ -1886,6 +1955,7 @@ static void Ogg_ReadTheoraHeader( demux_t *p_demux, 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 = ((float)i_fps_numerator) / i_fps_denominator; if ( i_version >= 3002001 ) { @@ -1893,24 +1963,14 @@ static void Ogg_ReadTheoraHeader( demux_t *p_demux, logical_stream_t *p_stream, } if ( p_demux->p_sys->i_length < 0 ) { - int64_t last_frame = oggseek_get_last_frame( p_demux, p_stream ); - /* - * Since there's quite a good chance that ogg_stream_packetout was called, - * the given p_oggpacket may point to invalid data. Fill it with some valid ones - */ - ogg_stream_packetpeek( &p_stream->os, p_oggpacket ); - - if ( last_frame >= 0 ) - { - p_demux->p_sys->i_length = last_frame / ((float)i_fps_numerator / - (float)i_fps_denominator); - } + int64_t last_packet = Ogg_GetLastPacket( p_demux, p_stream, p_stream->f_rate ); + if ( last_packet >= 0 ) + p_demux->p_sys->i_length = last_packet; } - p_stream->f_rate = ((float)i_fps_numerator) / i_fps_denominator; } -static void Ogg_ReadVorbisHeader( logical_stream_t *p_stream, +static void Ogg_ReadVorbisHeader( demux_t *p_demux, logical_stream_t *p_stream, ogg_packet *p_oggpacket ) { oggpack_buffer opb; @@ -1921,7 +1981,7 @@ 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); @@ -1931,6 +1991,13 @@ static void Ogg_ReadVorbisHeader( logical_stream_t *p_stream, oggpack_read( &opb, 32 ); oggpack_adv( &opb, 32 ); p_stream->fmt.i_bitrate = oggpack_read( &opb, 32 ); + + if ( p_demux->p_sys->i_length < 0 ) + { + int64_t last_packet = Ogg_GetLastPacket( p_demux, p_stream, p_stream->f_rate ); + if ( last_packet >= 0 ) + p_demux->p_sys->i_length = last_packet; + } } static void Ogg_ReadSpeexHeader( logical_stream_t *p_stream, @@ -1944,7 +2011,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); @@ -1958,7 +2025,8 @@ static void Ogg_ReadSpeexHeader( logical_stream_t *p_stream, p_stream->fmt.i_bitrate = oggpack_read( &opb, 32 ); } -static void Ogg_ReadOpusHeader( logical_stream_t *p_stream, +static void Ogg_ReadOpusHeader( demux_t *p_demux, + logical_stream_t *p_stream, ogg_packet *p_oggpacket ) { oggpack_buffer opb; @@ -1969,7 +2037,7 @@ static void Ogg_ReadOpusHeader( logical_stream_t *p_stream, /* 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 = 1; + p_stream->b_force_backup = true; /* All OggOpus streams are timestamped at 48kHz and * can be played at 48kHz. */ @@ -1982,6 +2050,13 @@ static void Ogg_ReadOpusHeader( logical_stream_t *p_stream, oggpack_adv( &opb, 8 ); /* version_id */ p_stream->fmt.audio.i_channels = oggpack_read( &opb, 8 ); p_stream->i_pre_skip = oggpack_read( &opb, 16 ); + + if ( p_demux->p_sys->i_length < 0 ) + { + int64_t last_packet = Ogg_GetLastPacket( p_demux, p_stream, p_stream->f_rate ); + if ( last_packet >= 0 ) + p_demux->p_sys->i_length = last_packet; + } } static void Ogg_ReadFlacHeader( demux_t *p_demux, logical_stream_t *p_stream, @@ -2034,7 +2109,7 @@ 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); @@ -2103,6 +2178,11 @@ static void Ogg_ReadAnnodexHeader( demux_t *p_demux, 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 ) ) @@ -2150,28 +2230,28 @@ static void Ogg_ReadAnnodexHeader( demux_t *p_demux, p_stream->fmt.i_cat = AUDIO_ES; p_stream->fmt.i_codec = VLC_CODEC_VORBIS; - p_stream->b_force_backup = 1; + p_stream->b_force_backup = true; } 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; - p_stream->b_force_backup = 1; + p_stream->b_force_backup = true; } else if( !strncmp(content_type_string, "video/x-theora", 14) ) { p_stream->fmt.i_cat = VIDEO_ES; p_stream->fmt.i_codec = VLC_CODEC_THEORA; - p_stream->b_force_backup = 1; + p_stream->b_force_backup = true; } else if( !strncmp(content_type_string, "video/x-xvid", 12) ) { p_stream->fmt.i_cat = VIDEO_ES; p_stream->fmt.i_codec = VLC_FOURCC( 'x','v','i','d' ); - p_stream->b_force_backup = 1; + p_stream->b_force_backup = true; } else if( !strncmp(content_type_string, "video/mpeg", 10) ) { @@ -2230,7 +2310,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 );