/*****************************************************************************
* 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 <gbazin@netcourrier.com>
* Andre Pang <Andre.Pang@csiro.au> (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.
*****************************************************************************/
/*****************************************************************************
#include <vlc_codecs.h>
#include <vlc_bits.h>
#include "xiph.h"
-#include "vorbis.h"
-#include "kate_categories.h"
+#include "xiph_metadata.h"
#include "ogg.h"
#include "oggseek.h"
/* */
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 * );
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;
}
- /* 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;
}
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 );
}
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 )
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:
*****************************************************************************/
vlc_meta_t *p_meta;
int64_t *pi64;
bool *pb_bool;
- int i;
switch( i_query )
{
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:
*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,
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 ) )
{
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 */
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;
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;
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;
}
return;
}
- if( p_oggpacket->bytes <= 0 )
- 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 */
/* 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;
+ if( i_pts == VLC_TS_INVALID ) i_pts = VLC_TS_0;
+ else if( i_pts == -1 && i_interpolated_pts == VLC_TS_INVALID )
+ i_pts = VLC_TS_0;
+ else if( i_pts == -1 && p_stream->fmt.i_cat == VIDEO_ES )
+ i_pts = i_interpolated_pts;
else if( i_pts == -1 ) i_pts = VLC_TS_INVALID;
if( p_stream->fmt.i_cat == AUDIO_ES )
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( 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 */
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,
/* 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;
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;
p_ogg->i_streams--;
}
}
- else if( (*oggpacket.packet & PACKET_TYPE_BITS ) == PACKET_TYPE_HEADER &&
- oggpacket.bytes >= 44+1 )
+ else if( oggpacket.bytes >= 44+1 &&
+ (*oggpacket.packet & PACKET_TYPE_BITS ) == PACKET_TYPE_HEADER )
{
stream_header_t tmp;
stream_header_t *st = &tmp;
/* 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 */
/* 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] );
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 )
{
/* 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 );
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 )
{
}
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;
/* 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_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,
/* 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);
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;
/* 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. */
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,
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*/ )
{
/* 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);
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 ) )
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) )
{
/* 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 );