001package net.bramp.ffmpeg.nut; 002 003import com.google.common.base.MoreObjects; 004import java.io.IOException; 005import java.util.ArrayList; 006import java.util.List; 007import org.apache.commons.lang3.math.Fraction; 008 009public class MainHeaderPacket extends Packet { 010 011 public static final int BROADCAST_MODE = 0; 012 013 long version; 014 long minorVersion; 015 int streamCount; 016 long maxDistance; 017 Fraction[] timeBase; 018 long flags; 019 020 final List<FrameCode> frameCodes = new ArrayList<>(); 021 final List<byte[]> elision = new ArrayList<>(); 022 023 public MainHeaderPacket() {} 024 025 @Override 026 protected void readBody(NutDataInputStream in) throws IOException { 027 frameCodes.clear(); 028 029 version = in.readVarLong(); 030 if (version > 3) { 031 minorVersion = in.readVarLong(); 032 } 033 034 streamCount = in.readVarInt(); 035 if (streamCount >= 250) { 036 throw new IOException("Illegal stream count " + streamCount + " must be < 250"); 037 } 038 039 maxDistance = in.readVarLong(); 040 if (maxDistance > 65536) { 041 maxDistance = 65536; 042 } 043 044 int time_base_count = in.readVarInt(); 045 timeBase = new Fraction[time_base_count]; 046 for (int i = 0; i < time_base_count; i++) { 047 int time_base_num = (int) in.readVarLong(); 048 int time_base_denom = (int) in.readVarLong(); 049 timeBase[i] = Fraction.getFraction(time_base_num, time_base_denom); 050 } 051 052 long pts = 0; 053 int mul = 1; 054 int stream_id = 0; 055 int header_idx = 0; 056 057 long match = 1L - (1L << 62); 058 int size; 059 int reserved; 060 long count; 061 062 for (int i = 0; i < 256; ) { 063 long flags = in.readVarLong(); 064 long fields = in.readVarLong(); 065 if (fields > 0) { 066 pts = in.readSignedVarInt(); 067 } 068 if (fields > 1) { 069 mul = in.readVarInt(); 070 if (mul >= 16384) { 071 throw new IOException("Illegal mul value " + mul + " must be < 16384"); 072 } 073 } 074 if (fields > 2) { 075 stream_id = in.readVarInt(); 076 if (stream_id >= streamCount) { 077 throw new IOException( 078 "Illegal stream id value " + stream_id + " must be < " + streamCount); 079 } 080 } 081 if (fields > 3) { 082 size = in.readVarInt(); 083 } else { 084 size = 0; 085 } 086 if (fields > 4) { 087 reserved = in.readVarInt(); 088 if (reserved >= 256) { 089 throw new IOException("Illegal reserved frame count " + reserved + " must be < 256"); 090 } 091 } else { 092 reserved = 0; 093 } 094 if (fields > 5) { 095 count = in.readVarLong(); 096 } else { 097 count = mul - size; 098 } 099 if (fields > 6) { 100 match = in.readSignedVarInt(); 101 } 102 if (fields > 7) { 103 header_idx = in.readVarInt(); 104 } 105 for (int j = 8; j < fields; j++) { 106 in.readVarLong(); // Throw away 107 } 108 109 if (stream_id >= streamCount) { 110 throw new IOException( 111 String.format("Invalid stream value %d, must be < %d", stream_id, streamCount)); 112 } 113 114 if (count <= 0 || (count > 256 - i - (i <= 'N' ? 1 : 0))) { 115 throw new IOException( 116 String.format( 117 "Invalid count value %d, must be > 0 && < %d", 118 count, 256 - i - (i <= 'N' ? 1 : 0))); 119 } 120 121 for (int j = 0; j < count && i < 256; j++, i++) { 122 FrameCode fc = new FrameCode(); 123 frameCodes.add(fc); 124 125 // Skip 'N' because that is an illegal frame code 126 if (i == 'N') { 127 fc.flags = Frame.FLAG_INVALID; 128 j--; 129 continue; 130 } 131 132 fc.flags = flags; 133 fc.streamId = stream_id; 134 fc.dataSizeMul = mul; 135 fc.dataSizeLsb = size + j; 136 fc.ptsDelta = pts; 137 fc.reservedCount = reserved; 138 fc.matchTimeDelta = match; 139 fc.headerIdx = header_idx; 140 141 if (fc.dataSizeLsb >= 16384) { 142 throw new IOException("Illegal dataSizeLsb value " + fc.dataSizeLsb + " must be < 16384"); 143 } 144 } 145 } 146 147 int remain = 1024; 148 if (in.offset() < (header.end - 4)) { 149 int header_count = in.readVarInt(); 150 if (header_count >= 128) { 151 throw new IOException("Invalid header_count value " + header_count + " must be < 128"); 152 } 153 154 elision.clear(); 155 elision.add(new byte[0]); // First elision is always empty 156 for (int i = 1; i < header_count; i++) { 157 byte[] e = in.readVarArray(); 158 if (e.length == 0 || e.length >= 256) { 159 throw new IOException("Invalid elision length " + e.length + " must be > 0 and < 256"); 160 } 161 if (e.length > remain) { 162 throw new IOException( 163 "Invalid elision length value " + e.length + " must be <= " + remain); 164 } 165 remain -= e.length; 166 elision.add(e); 167 } 168 } 169 170 if (version > 3 && (in.offset() < (header.end - 4))) { 171 flags = in.readVarLong(); 172 } 173 } 174 175 @Override 176 public String toString() { 177 return MoreObjects.toStringHelper(this) 178 .add("header", header) 179 .add("version", version) 180 .add("minorVersion", minorVersion) 181 .add("streamCount", streamCount) 182 .add("maxDistance", maxDistance) 183 .add("timeBase", timeBase) 184 .add("flags", flags) 185 .add("frameCodes", frameCodes.size()) 186 .add("elision", elision) 187 .add("footer", footer) 188 .toString(); 189 } 190}