001package net.bramp.ffmpeg.nut;
002
003import com.google.common.base.MoreObjects;
004import java.io.IOException;
005import java.nio.charset.StandardCharsets;
006import org.apache.commons.lang3.math.Fraction;
007
008public class StreamHeaderPacket extends Packet {
009
010  public static final int VIDEO = 0;
011  public static final int AUDIO = 1;
012  public static final int SUBTITLE = 2;
013  public static final int USER_DATA = 3;
014
015  int id;
016  long type; // One of VIDEO/AUDIO/SUBTITLE/USER_DATA // TODO Convert to enum.
017  byte[] fourcc;
018  int timeBaseId;
019  int msbPtsShift;
020  int maxPtsDistance;
021  long decodeDelay;
022  long flags;
023  byte[] codecSpecificData;
024
025  // If video
026  int width;
027  int height;
028  int sampleWidth;
029  int sampleHeight;
030  long colorspaceType;
031
032  // If audio
033  Fraction sampleRate = Fraction.ZERO;
034  int channels;
035
036  protected static String fourccToString(byte[] fourcc) {
037    return new String(fourcc, StandardCharsets.ISO_8859_1);
038  }
039
040  @Override
041  protected void readBody(NutDataInputStream in) throws IOException {
042
043    id = in.readVarInt();
044    type = in.readVarLong();
045    fourcc = in.readVarArray();
046
047    if (fourcc.length != 2 && fourcc.length != 4) {
048      // TODO In future fourcc could be a different size, but for sanity checking lets leave this
049      // check in.
050      throw new IOException("Unexpected fourcc length: " + fourcc.length);
051    }
052
053    timeBaseId = in.readVarInt();
054    msbPtsShift = in.readVarInt();
055    if (msbPtsShift >= 16) {
056      throw new IOException("invalid msbPtsShift " + msbPtsShift + " want < 16");
057    }
058    maxPtsDistance = in.readVarInt();
059    decodeDelay = in.readVarLong();
060    flags = in.readVarLong();
061    codecSpecificData = in.readVarArray();
062
063    if (type == VIDEO) {
064      width = in.readVarInt();
065      height = in.readVarInt();
066
067      if (width == 0 || height == 0) {
068        throw new IOException("invalid video dimensions " + width + "x" + height);
069      }
070
071      sampleWidth = in.readVarInt();
072      sampleHeight = in.readVarInt();
073
074      // Both MUST be 0 if unknown otherwise both MUST be nonzero.
075      if ((sampleWidth == 0 || sampleHeight == 0) && sampleWidth != sampleHeight) {
076        throw new IOException(
077            "invalid video sample dimensions " + sampleWidth + "x" + sampleHeight);
078      }
079
080      colorspaceType = in.readVarLong();
081
082    } else if (type == AUDIO) {
083      int samplerateNum = in.readVarInt();
084      int samplerateDenom = in.readVarInt();
085      sampleRate = Fraction.getFraction(samplerateNum, samplerateDenom);
086      channels = in.readVarInt();
087    }
088  }
089
090  @Override
091  public String toString() {
092    return MoreObjects.toStringHelper(this)
093        .add("header", header)
094        .add("id", id)
095        .add("type", type)
096        .add("fourcc", fourccToString(fourcc))
097        .add("timeBaseId", timeBaseId)
098        .add("msbPtsShift", msbPtsShift)
099        .add("maxPtsDistance", maxPtsDistance)
100        .add("decodeDelay", decodeDelay)
101        .add("flags", flags)
102        .add("codecSpecificData", codecSpecificData)
103        .add("width", width)
104        .add("height", height)
105        .add("sampleWidth", sampleWidth)
106        .add("sampleHeight", sampleHeight)
107        .add("colorspaceType", colorspaceType)
108        .add("sampleRate", sampleRate)
109        .add("channels", channels)
110        .add("footer", footer)
111        .toString();
112  }
113}