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

org.chenliang.oggus.ogg.OggPage Maven / Gradle / Ivy

Go to download

Oggus is a Java library for reading and writing Ogg and Opus stream. Opus packet structure is supported.

There is a newer version: 1.2.0
Show newest version
package org.chenliang.oggus.ogg;

import com.google.common.io.LittleEndianDataOutputStream;
import com.google.common.primitives.Bytes;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

/**
 * Ogg page that defined by RFC3533.
 *
 * 

Following is the field layout of an Ogg page header:

*
 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1| Byte
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * | capture_pattern: Magic number for page start "OggS"           | 0-3
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * | version       | header_type   | granule_position              | 4-7
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                                                               | 8-11
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                               | bitstream_serial_number       | 12-15
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                               | page_sequence_number          | 16-19
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                               | CRC_checksum                  | 20-23
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                               |page_segments  | segment_table | 24-27
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * | ...                                                           | 28-
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * 
* *

Create a new OggPage object with static method: {@link OggPage#empty()}. Call set...() methods to set fields of * Ogg page. */ public class OggPage { public static final byte[] CAPTURE_PATTERN = {'O', 'g', 'g', 'S'}; public static final int MAX_LACE_VALUE = 255; private int version = 0; private int flag = 0x00; private long granulePosition; private long serialNum; private long seqNum; private int checkSum; private byte[] laceValues = new byte[0]; private final List dataPackets = new LinkedList<>(); private OggPage() { } /** * @return a new {@code OggPage} object with fields unset */ public static OggPage empty() { return new OggPage(); } /** * Get the Ogg page spec version, currently only version 0 is supported. * * @return version number */ public int getVersion() { return version; } /** * Set the flag byte. The flags should also be individually set with methods: {@link OggPage#setContinued()}, * {@link OggPage#setBOS()}, {@link OggPage#setBOS()}. * * @param flag the flag byte value, only last three bits is used. */ public void setFlag(int flag) { this.flag = flag & 0x07; } /** * Check whether the "continued" bit of flag byte is set. * * @return true if this Ogg is continued with last Ogg page. */ public boolean isContinued() { return (this.flag & 0x01) != 0; } /** * Set the "continued" bit of flag byte. */ public void setContinued() { flag = flag | 0x01; } /** * Check whether the "BOS" bit of flag byte is set. * * @return true if this page is the begging of a logical ogg stream. */ public boolean isBOS() { return (this.flag & 0x02) != 0; } /** * Set the "BOS" bit of flag byte. */ public void setBOS() { flag = flag | 0x02; } /** * Check whether the "EOS" bit of flag byte is set. * * @return true if this page is the end of a logical ogg stream. */ public boolean isEOS() { return (this.flag & 0x04) != 0; } /** * Set the "EOS" bit of flag byte. */ public void setEOS() { flag = flag | 0x04; } public long getGranulePosition() { return granulePosition; } public void setGranulePosition(long granulePosition) { this.granulePosition = granulePosition; } public long getSerialNum() { return serialNum; } public void setSerialNum(long serialNum) { this.serialNum = serialNum; } public long getSeqNum() { return seqNum; } public void setSeqNum(long seqNum) { this.seqNum = seqNum; } public int getCheckSum() { return checkSum; } public void setCheckSum(int checkSum) { this.checkSum = checkSum; } public int getSegCount() { return laceValues != null ? laceValues.length : 0; } public byte[] getLaceValues() { return laceValues; } /** * Check whether the last page is completed in this page. * * @return true if the last data packet is completed in this page. */ public boolean isCompleted() { return Byte.toUnsignedInt(laceValues[getSegCount() - 1]) < MAX_LACE_VALUE; } /** * Add a data packet to this page. The lace values of this data packet are automatically generated. * * @param data the binary byte array of a data packet. */ public void addDataPacket(byte[] data) { laceValues = Bytes.concat(laceValues, lenToLaceValues(data.length, false)); dataPackets.add(data); } /** * Add a partial data packet to this page. The lace values of this data packet are automatically generated. * *

Partial data packet means the remaining parts are stored in following Ogg pages. The length of partial * data packet must be a multiple of 255

* * @param data the binary byte array of a data packet. */ public void addPartialDataPacket(byte[] data) { if (data.length % 255 != 0) { throw new InvalidOggException("Not a partial data packet"); } laceValues = Bytes.concat(laceValues, lenToLaceValues(data.length, true)); dataPackets.add(data); } public List getDataPackets() { return dataPackets; } /** * Dump the Ogg page to binary. This method could be used to create a binary Ogg stream. * * @return the dumped binary byte array */ public byte[] dump() { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); LittleEndianDataOutputStream out = new LittleEndianDataOutputStream(byteArrayOutputStream); try { out.write(CAPTURE_PATTERN); out.write(version); out.write(flag); out.writeLong(granulePosition); out.writeInt((int) serialNum); out.writeInt((int) seqNum); out.writeInt(checkSum); out.write(getSegCount()); out.write(laceValues); for (byte[] dataPacket : dataPackets) { out.write(dataPacket); } } catch (IOException e) { throw new RuntimeException("OggPage dump to byte array error", e); } return byteArrayOutputStream.toByteArray(); } private byte[] lenToLaceValues(int len, boolean isPartial) { int countOf255 = len / 255; if (isPartial) { byte[] laceValues = new byte[countOf255]; Arrays.fill(laceValues, (byte) 255); return laceValues; } else { int lastValue = len % 255; byte[] laceValues = new byte[countOf255 + 1]; Arrays.fill(laceValues, 0, countOf255, (byte) 255); laceValues[countOf255] = (byte) lastValue; return laceValues; } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy