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