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}