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

org.chenliang.oggus.opus.IdHeader Maven / Gradle / Ivy

package org.chenliang.oggus.opus;

import com.google.common.io.LittleEndianDataInputStream;
import com.google.common.io.LittleEndianDataOutputStream;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;

import static java.lang.String.format;

/**
 * The Identification Header packet of a Ogg Opus stream. It has following structure:
 *
 * 
 *  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
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |      'O'      |      'p'      |      'u'      |      's'      |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |      'H'      |      'e'      |      'a'      |      'd'      |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |  Version = 1  | Channel Count |           Pre-skip            |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |                     Input Sample Rate (Hz)                    |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * |   Output Gain (Q7.8 in dB)    | Mapping Family|               |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+               :
 * |                                                               |
 * :               Optional Channel Mapping Table...               :
 * |                                                               |
 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 * 
*/ public class IdHeader { public static final byte[] MAGIC_SIGNATURE = {'O', 'p', 'u', 's', 'H', 'e', 'a', 'd'}; private int majorVersion; private int minorVersion; private int channelCount; private int preSkip; private double outputGain; private int coupledCount; private long inputSampleRate; private int channelMappingFamily; private int streamCount; private int[] channelMapping; private IdHeader() { } /** * Parse {@code IdHeader} from binary data, the data must start with 'OpusHead'. * * @param data the binary data of ID Header * @return IdHeader */ public static IdHeader from(byte[] data) { IdHeader idHeader = new IdHeader(); LittleEndianDataInputStream in = new LittleEndianDataInputStream(new ByteArrayInputStream(data)); try { if (!Arrays.equals(in.readNBytes(8), MAGIC_SIGNATURE)) { throw new InvalidOpusException("Id Header packet doesn't start with 'OpusHead'"); } byte version = in.readByte(); idHeader.majorVersion = version >> 4; idHeader.minorVersion = version & 0x0F; idHeader.channelCount = in.readUnsignedByte(); if (idHeader.channelCount < 1) { throw new InvalidOpusException("Invalid channel count: " + idHeader.channelCount); } idHeader.preSkip = in.readUnsignedShort(); idHeader.inputSampleRate = Integer.toUnsignedLong(in.readInt()); idHeader.outputGain = in.readUnsignedShort() / 256.0; idHeader.channelMappingFamily = in.readUnsignedByte(); if (idHeader.channelMappingFamily == 0) { if (idHeader.channelCount > 2) { throw new InvalidOpusException(format("Invalid channel count: %d, for channel mapping family 0", idHeader.channelCount)); } idHeader.streamCount = 1; idHeader.coupledCount = idHeader.channelCount - idHeader.streamCount; idHeader.channelMapping = idHeader.channelCount == 1 ? new int[]{0} : new int[]{0, 1}; } else { if (idHeader.channelMappingFamily == 1 && idHeader.channelCount > 8) { throw new InvalidOpusException(format("Invalid channel count: %d, for channel mapping family 1", idHeader.channelCount)); } idHeader.streamCount = in.readUnsignedByte(); idHeader.coupledCount = in.readUnsignedByte(); idHeader.channelMapping = new int[idHeader.channelCount]; for (int i = 0; i < idHeader.channelCount; i++) { idHeader.channelMapping[i] = in.readUnsignedByte(); } } return idHeader; } catch (IOException e) { throw new InvalidOpusException("Id Header data is corrupted"); } } /** * Create an empty Id Header. * * @return IdHeader */ public static IdHeader emptyHeader() { return new IdHeader(); } /** * Dump this IdHeader to binary. * * @return the binary bytes representation of this IdHeader */ public byte[] dump() { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); LittleEndianDataOutputStream out = new LittleEndianDataOutputStream(byteArrayOutputStream); try { out.write(MAGIC_SIGNATURE); out.writeByte(majorVersion << 4 | minorVersion); out.writeByte(channelCount); out.writeShort(preSkip); out.writeInt((int) inputSampleRate); out.writeShort((int) (outputGain * 256)); out.writeByte(channelMappingFamily); if (channelMappingFamily > 0) { out.writeByte(streamCount); out.writeByte(coupledCount); for (int i = 0; i < channelCount; i++) { out.writeByte(channelMapping[i]); } } } catch (IOException e) { throw new RuntimeException("IdHeader dump to byte array error", e); } return byteArrayOutputStream.toByteArray(); } public int getMajorVersion() { return majorVersion; } public void setMajorVersion(int majorVersion) { this.majorVersion = majorVersion; } public int getMinorVersion() { return minorVersion; } public void setMinorVersion(int minorVersion) { this.minorVersion = minorVersion; } public int getChannelCount() { return channelCount; } public void setChannelCount(int channelCount) { this.channelCount = channelCount; } public int getPreSkip() { return preSkip; } public void setPreSkip(int preSkip) { this.preSkip = preSkip; } public double getOutputGain() { return outputGain; } public void setOutputGain(double outputGain) { this.outputGain = outputGain; } public int getCoupledCount() { return coupledCount; } public void setCoupledCount(int coupledCount) { this.coupledCount = coupledCount; } public long getInputSampleRate() { return inputSampleRate; } public void setInputSampleRate(long inputSampleRate) { this.inputSampleRate = inputSampleRate; } public int getChannelMappingFamily() { return channelMappingFamily; } public void setChannelMappingFamily(int channelMappingFamily) { this.channelMappingFamily = channelMappingFamily; } public int getStreamCount() { return streamCount; } public void setStreamCount(int streamCount) { this.streamCount = streamCount; } public int[] getChannelMapping() { return channelMapping; } public void setChannelMapping(int[] channelMapping) { this.channelMapping = channelMapping; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy