/*
* Java port of ogg demultiplexer.
* Copyright (c) 2004 Jonathan Hueber.
*
* License conditions are the same as OggVorbis. See README.
* 1a39e335700bec46ae31a38e2156a898
*/
/********************************************************************
* *
* THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
* USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
* GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
* IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
* *
* THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002 *
* by the XIPHOPHORUS Company http://www.xiph.org/ *
* *
********************************************************************/
package net.sourceforge.jffmpeg.demux.ogg;
import javax.media.Track;
import javax.media.Time;
import javax.media.Format;
import javax.media.Buffer;
import javax.media.TrackListener;
import javax.media.format.AudioFormat;
import java.io.IOException;
import java.io.InputStream;
import javax.media.Time;
/**
* This class handles audio data read from a VOB file
*/
public class AudioTrack implements Track {
private long sampleRate = 44100;
private long sampleDuration = Time.ONE_SECOND / 44100;
/** Data source */
private OggDemux demux;
private int serial;
/** Creates a new instance of AudioTrack */
public AudioTrack( OggDemux demux, int serial ) {
this.demux = demux;
this.serial = serial;
}
public Time getDuration() {
return demux.getDuration();
}
/**
* Return Audio format
*/
public Format getFormat() {
/* We need to parse the three OGG headers for the Rate */
if (headersRequired > 0 ) {
while (headersRequired > 0 ) {
readFrame( new Buffer() );
}
demux.seekPacket( 0, serial );
}
return new AudioFormat("vorbis", sampleRate,16,2,0,1);
}
public void setEnabled( boolean enabled ) {
}
public boolean isEnabled() {
return true;
}
public Time mapFrameToTime( int frame ) {
return new Time( frame * sampleDuration );
}
public int mapTimeToFrame(javax.media.Time time) {
return (int)(time.getNanoseconds() / sampleDuration);
}
/**
* Return a buffer containing audio data
*/
public void readFrame(Buffer buffer) {
try {
demux.readFrame( buffer, serial, sampleDuration );
if ( headersRequired > 0 ) process( buffer );
} catch( IOException e ) {
e.printStackTrace();
}
}
public Time getStartTime() {
return new Time( 0 );
}
public void setTrackListener(TrackListener trackListener) {
}
/* Header parsing (copy from VorbisDecoder)
* I have decided against passing the headers to the
* codec as an object to simplify compatability with
* other vorbis implementations.
*
* Unfortuantly this means a bit of logic duplication.
*/
private int headersRequired = 3;
private static final int HEADER_INFO = 1;
private static final int HEADER_COMMENT = 3;
private static final int HEADER_BOOKS = 5;
private byte[] packetBuffer = new byte[ 0 ];
private int packetBufferLength = 0;
private void process( Buffer input ) {
try {
byte[] data = (byte[])input.getData(); //in.getLength
int dataLength = input.getLength();
/* Parse segments */
int numberOfSegments = data[ 26 ] & 0xff;
int segmentNumber = 0;
int dataPointer = 27 + numberOfSegments;
while ( segmentNumber < numberOfSegments ) {
int length = 0;
do {
length += data[ 27 + segmentNumber ] & 0xff;
segmentNumber++;
} while( data[ 27 + (segmentNumber - 1) ] == -1
&& segmentNumber < numberOfSegments );
/* Copy data to spare buffer */
if ( packetBuffer.length < length + packetBufferLength ) {
byte[] t = packetBuffer;
packetBuffer = new byte[ length + packetBufferLength ];
System.arraycopy(t, 0, packetBuffer,0, packetBufferLength);
}
System.arraycopy( data, dataPointer,
packetBuffer, packetBufferLength, length );
packetBufferLength += length;
/* Is this packet going to wrap to next packet ? */
if ( data[ 27 + (segmentNumber - 1) ] == -1 ) {
continue;
}
decodeHeader( packetBuffer, 0, packetBufferLength );
dataPointer += length;
packetBufferLength = 0;
}
} catch ( Exception e ) {
} catch( Error e ) {
}
}
private void decodeHeader( byte[] data, int offset, int length ) {
int packType = data[ offset ];
if ( data[ offset + 1 ] != 'v'
|| data[ offset + 2 ] != 'o'
|| data[ offset + 3 ] != 'r'
|| data[ offset + 4 ] != 'b'
|| data[ offset + 5 ] != 'i'
|| data[ offset + 6 ] != 's' ) {
return;
}
offset += 7;
length -= 7;
switch (packType) {
case HEADER_INFO: {
vorbis_unpack_info( data, offset, length );
break;
}
case HEADER_COMMENT: {
// vorbis_unpack_comment( data, offset, length );
break;
}
case HEADER_BOOKS: {
// vorbis_unpack_books( data, offset, length);
break;
}
default: {
throw new Error( "Invalid header ID: " + packType );
}
}
headersRequired--;
}
private void vorbis_unpack_info( byte[] data, int offset, int length ) {
int version = readInt( data, offset );
int channels = data[ offset + 4 ] & 0xff;
int rate = readInt( data, offset + 5 );
int bitrate_upper = readInt( data, offset + 9 );
int bitrate_nominal = readInt( data, offset + 13 );
int bitrate_lower = readInt( data, offset + 17 );
sampleRate = rate;
sampleDuration = Time.ONE_SECOND / rate;
// System.out.println( "Extracted Rate: " + rate );
}
/* Read an OGG int */
private int readInt( byte[] buffer, int offset ) {
int ret = 0;
ret = (ret << 8 ) | (buffer[ offset + 3 ] & 0xff);
ret = (ret << 8 ) | (buffer[ offset + 2 ] & 0xff);
ret = (ret << 8 ) | (buffer[ offset + 1 ] & 0xff);
ret = (ret << 8 ) | (buffer[ offset + 0 ] & 0xff);
return ret;
}
}