All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.jarnbjo.jmf.OggJmfPage Maven / Gradle / Ivy

/*
 * $ProjectName$
 * $ProjectRevision$
 * -----------------------------------------------------------
 * $Id: OggJmfPage.java,v 1.2 2003/03/31 00:23:18 jarnbjo Exp $
 * -----------------------------------------------------------
 *
 * $Author: jarnbjo $
 *
 * Description:
 *
 * Copyright 2002-2003 Tor-Einar Jarnbjo
 * -----------------------------------------------------------
 *
 * Change History
 * -----------------------------------------------------------
 * $Log: OggJmfPage.java,v $
 * Revision 1.2  2003/03/31 00:23:18  jarnbjo
 * no message
 *
 * Revision 1.1  2003/03/03 22:06:12  jarnbjo
 * no message
 */
package de.jarnbjo.jmf;

import de.jarnbjo.ogg.EndOfOggStreamException;
import de.jarnbjo.ogg.OggFormatException;
import de.jarnbjo.ogg.OggPage;
import de.jarnbjo.util.io.BitInputStream;
import de.jarnbjo.util.io.ByteArrayBitInputStream;
import java.io.DataInput;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import javax.media.protocol.PullSourceStream;

/**
 * 

* An instance of this class represents an ogg page read from an ogg file or * network stream. It has no public constructor, but instances can be created by * the {@code create} methods, supplying a JMF stream or a * {@code RandomAccessFile} which is positioned at the beginning of an Ogg * page.

* *

* Furthermore, the class provides methods for accessing the raw page data, as * well as data attributes like segmenting information, sequence number, stream * serial number, checksum and whether this page is the beginning or end of a * logical bitstream (BOS, EOS) and if the page data starts with a continued * packet or a fresh data packet.

*/ public final class OggJmfPage extends OggPage { final private int version; final private boolean continued, bos, eos; final private long absoluteGranulePosition; final private int streamSerialNumber, pageSequenceNumber, pageCheckSum; final private int[] segmentOffsets; final private int[] segmentLengths; final private int totalLength; final private byte[] header, segmentTable, data; private OggJmfPage( int version, boolean continued, boolean bos, boolean eos, long absoluteGranulePosition, int streamSerialNumber, int pageSequenceNumber, int pageCheckSum, int[] segmentOffsets, int[] segmentLengths, int totalLength, byte[] header, byte[] segmentTable, byte[] data) { this.version = version; this.continued = continued; this.bos = bos; this.eos = eos; this.absoluteGranulePosition = absoluteGranulePosition; this.streamSerialNumber = streamSerialNumber; this.pageSequenceNumber = pageSequenceNumber; this.pageCheckSum = pageCheckSum; this.segmentOffsets = segmentOffsets; this.segmentLengths = segmentLengths; this.totalLength = totalLength; this.header = header; this.segmentTable = segmentTable; this.data = data; } /** * This method is equivalent to {@code create(source, false)} * * @param source the byte channel from which the ogg page is generated * @return an ogg page created by reading data from the specified channel, * starting at the current position * @throws OggFormatException if the data read from the specified channel is * not matching the specification for an ogg page * @throws EndOfOggStreamException if it is not possible to read an entire * ogg page from the specified channel * @throws IOException if some other IO error is detected when reading from * the channel * * @see #create(javax.media.protocol.PullSourceStream, boolean) */ public static OggJmfPage create(PullSourceStream source) throws IOException, EndOfOggStreamException, OggFormatException { return create(source, false); } public static OggJmfPage create(PullSourceStream source, boolean skipData) throws IOException { return create((Object) source, skipData); } private static OggJmfPage create(Object source, boolean skipData) throws IOException { try { byte[] header = new byte[27]; if (source instanceof RandomAccessFile) { ((DataInput) source).readFully(header); } else if (source instanceof PullSourceStream) { readFully((PullSourceStream) source, header); } else if (source instanceof InputStream) { readFully((InputStream) source, header); } BitInputStream bdSource = new ByteArrayBitInputStream(header); int capture = bdSource.getInt(32); if (capture != 0x5367674f) { //throw new FormatException( //"Ogg page does not start with 'OggS' (0x4f676753)"); /* ** This condition is IMHO an error, but older Ogg files often ** contain pages with a different capture than OggS. I am not ** sure how to manage these pages, but the decoder seems to ** work properly, if the incorrect capture is simply ignored. */ String cs = Integer.toHexString(capture); while (cs.length() < 8) { cs = "0" + cs; } cs = cs.substring(6, 8) + cs.substring(4, 6) + cs.substring(2, 4) + cs.substring(0, 2); char c1 = (char) (Integer.valueOf( cs.substring(0, 2), 16).intValue()); char c2 = (char) (Integer.valueOf( cs.substring(2, 4), 16).intValue()); char c3 = (char) (Integer.valueOf( cs.substring(4, 6), 16).intValue()); char c4 = (char) (Integer.valueOf( cs.substring(6, 8), 16).intValue()); System.out.println("Ogg packet header is 0x" + cs + " (" + c1 + c2 + c3 + c4 + "), should be 0x4f676753 (OggS)"); } int version = bdSource.getInt(8); byte tmp = (byte) bdSource.getInt(8); boolean bf1 = (tmp & 1) != 0; boolean bos = (tmp & 2) != 0; boolean eos = (tmp & 4) != 0; long absoluteGranulePosition = bdSource.getLong(64); int streamSerialNumber = bdSource.getInt(32); int pageSequenceNumber = bdSource.getInt(32); int pageCheckSum = bdSource.getInt(32); int pageSegments = bdSource.getInt(8); //System.out.println("OggPage: "+streamSerialNumber+ //" / "+absoluteGranulePosition+" / "+pageSequenceNumber); int[] segmentOffsets = new int[pageSegments]; int[] segmentLengths = new int[pageSegments]; int totalLength = 0; byte[] segmentTable = new byte[pageSegments]; byte[] tmpBuf = new byte[1]; for (int i = 0; i < pageSegments; i++) { int l = 0; if (source instanceof RandomAccessFile) { l = ((int) ((DataInput) source).readByte() & 0xff); } else if (source instanceof PullSourceStream) { ((PullSourceStream) source).read(tmpBuf, 0, 1); l = (int) tmpBuf[0] & 0xff; } else if (source instanceof InputStream) { l = ((InputStream) source).read(); } segmentTable[i] = (byte) l; segmentLengths[i] = l; segmentOffsets[i] = totalLength; totalLength += l; } byte[] data = null; if (!skipData) { data = new byte[totalLength]; if (source instanceof RandomAccessFile) { ((DataInput) source).readFully(data); } else if (source instanceof PullSourceStream) { readFully((PullSourceStream) source, data); } else if (source instanceof InputStream) { readFully((InputStream) source, data); } } return new OggJmfPage(version, bf1, bos, eos, absoluteGranulePosition, streamSerialNumber, pageSequenceNumber, pageCheckSum, segmentOffsets, segmentLengths, totalLength, header, segmentTable, data); } catch (EOFException e) { throw new EndOfOggStreamException(); } } private static void readFully(PullSourceStream source, byte[] buffer) throws IOException { int total = 0; while (total < buffer.length) { int read = source.read(buffer, total, buffer.length - total); if (read == -1) { throw new EndOfOggStreamException(); } total += read; } } private static void readFully(InputStream source, byte[] buffer) throws IOException { int total = 0; while (total < buffer.length) { int read = source.read(buffer, total, buffer.length - total); if (read == -1) { throw new EndOfOggStreamException(); } total += read; } } /** * Returns the absolute granule position of the last complete packet * contained in this Ogg page, or -1 if the page contains a single packet, * which is not completed on this page. For pages containing Vorbis data, * this value is the sample index within the Vorbis stream. The Vorbis * stream does not necessarily start with sample index 0. * * @return the absolute granule position of the last packet completed on * this page */ @Override public long getAbsoluteGranulePosition() { return absoluteGranulePosition; } /** * Returns the stream serial number of this ogg page. * * @return this page's serial number */ @Override public int getStreamSerialNumber() { return streamSerialNumber; } /** * Return the sequence number of this ogg page. * * @return this page's sequence number */ @Override public int getPageSequenceNumber() { return pageSequenceNumber; } /** * Return the check sum of this ogg page. * * @return this page's check sum */ @Override public int getPageCheckSum() { return pageCheckSum; } /** * @return the total number of bytes in the page data */ @Override public int getTotalLength() { return totalLength; } /** * @return a ByteBuffer containing the page data */ @Override public byte[] getData() { return data; } @Override public byte[] getHeader() { return header; } @Override public byte[] getSegmentTable() { return segmentTable; } @Override public int[] getSegmentOffsets() { return segmentOffsets; } @Override public int[] getSegmentLengths() { return segmentLengths; } /** * @return {@code true} if this page begins with a continued packet */ @Override public boolean isContinued() { return continued; } /** * @return {@code true} if this page begins with a fresh packet */ @Override public boolean isFresh() { return !continued; } /** * @return {@code true} if this page is the beginning of a logical stream */ @Override public boolean isBos() { return bos; } /** * @return {@code true} if this page is the end of a logical stream */ @Override public boolean isEos() { return eos; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy