com.googlecode.mp4parser.authoring.tracks.DTSTrackImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of isoparser Show documentation
Show all versions of isoparser Show documentation
A generic parser and writer for all ISO 14496 based files (MP4, Quicktime, DCF, PDCF, ...)
The newest version!
package com.googlecode.mp4parser.authoring.tracks;
import com.coremedia.iso.boxes.*;
import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
import com.googlecode.mp4parser.DataSource;
import com.googlecode.mp4parser.authoring.AbstractTrack;
import com.googlecode.mp4parser.authoring.Sample;
import com.googlecode.mp4parser.authoring.TrackMetaData;
import com.googlecode.mp4parser.boxes.DTSSpecificBox;
import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.BitReaderBuffer;
import java.io.EOFException;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.channels.WritableByteChannel;
import java.util.*;
public class DTSTrackImpl extends AbstractTrack {
TrackMetaData trackMetaData = new TrackMetaData();
SampleDescriptionBox sampleDescriptionBox;
int samplerate;
int bitrate;
int frameSize = 0;
int sampleSize;
int samplesPerFrame;
int channelCount;
private long[] sampleDurations;
private int dataOffset = 0;
DTSSpecificBox ddts = new DTSSpecificBox();
private DataSource dataSource;
private List samples;
// Info from the headers
boolean isVBR = false;
boolean coreSubStreamPresent = false;
boolean extensionSubStreamPresent = false;
int numExtSubStreams = 0;
int coreMaxSampleRate = 0;
int coreBitRate = 0;
int coreChannelMask = 0;
int coreFramePayloadInBytes = 0;
int extAvgBitrate = 0;
int extPeakBitrate = 0;
int extSmoothBuffSize = 0;
int extFramePayloadInBytes = 0;
int maxSampleRate = 0;
int lbrCodingPresent = 0;
int numFramesTotal = 0;
int samplesPerFrameAtMaxFs = 0;
int numSamplesOrigAudioAtMaxFs = 0;
int channelMask = 0;
int codecDelayAtMaxFs = 0;
int bcCoreMaxSampleRate = 0;
int bcCoreBitRate = 0;
int bcCoreChannelMask = 0;
int lsbTrimPercent = 0;
String type = "none";
private String lang = "eng";
public DTSTrackImpl(DataSource dataSource, String lang) throws IOException {
super(dataSource.toString());
this.lang = lang;
this.dataSource = dataSource;
parse();
}
public DTSTrackImpl(DataSource dataSource) throws IOException {
super(dataSource.toString());
this.dataSource = dataSource;
parse();
}
public void close() throws IOException {
dataSource.close();
}
private void parse() throws IOException {
if (!readVariables()) {
throw new IOException();
}
sampleDescriptionBox = new SampleDescriptionBox();
AudioSampleEntry audioSampleEntry = new AudioSampleEntry(type);
audioSampleEntry.setChannelCount(channelCount);
audioSampleEntry.setSampleRate(samplerate);
audioSampleEntry.setDataReferenceIndex(1);
audioSampleEntry.setSampleSize(16);
audioSampleEntry.addBox(ddts);
sampleDescriptionBox.addBox(audioSampleEntry);
trackMetaData.setCreationTime(new Date());
trackMetaData.setModificationTime(new Date());
trackMetaData.setLanguage(lang);
trackMetaData.setTimescale(samplerate); // Audio tracks always use samplerate as timescale
}
public List getSamples() {
return samples;
}
public SampleDescriptionBox getSampleDescriptionBox() {
return sampleDescriptionBox;
}
public long[] getSampleDurations() {
return sampleDurations;
}
public List getCompositionTimeEntries() {
return null;
}
public long[] getSyncSamples() {
return null;
}
public List getSampleDependencies() {
return null;
}
public TrackMetaData getTrackMetaData() {
return trackMetaData;
}
public String getHandler() {
return "soun";
}
private void parseDtshdhdr(int size, ByteBuffer bb) {
int hdrVersion = bb.getInt();
int timeCodeHigh = bb.get();
int timeCode = bb.getInt();
int timeCodeFrameRate = bb.get();
int bitwStreamMetadata = bb.getShort();
int numAudioPresentations = bb.get();
numExtSubStreams = bb.get();
if ((bitwStreamMetadata & 1) == 1) {
isVBR = true;
}
if ((bitwStreamMetadata & 8) == 8) {
coreSubStreamPresent = true;
}
if ((bitwStreamMetadata & 0x10) == 0x10) {
extensionSubStreamPresent = true;
numExtSubStreams++;
} else {
numExtSubStreams = 0;
}
int i = 14;
while (i < size) {
bb.get();
i++;
}
}
private boolean parseCoressmd(int size, ByteBuffer bb) {
int cmsr_1 = bb.get();
int cmsr_2 = bb.getShort();
coreMaxSampleRate = (cmsr_1 << 16) | (cmsr_2 & 0xffff);
coreBitRate = bb.getShort();
coreChannelMask = bb.getShort();
coreFramePayloadInBytes = bb.getInt();
int i = 11;
while (i < size) {
bb.get();
i++;
}
return true;
}
private boolean parseAuprhdr(int size, ByteBuffer bb) {
int audioPresIndex = bb.get(); // Audio_Pres_Index
int bitwAupresData = bb.getShort(); // Bitw_Aupres_Metadata
int a = bb.get();
int b = bb.getShort();
maxSampleRate = (a << 16) | (b & 0xffff);
numFramesTotal = bb.getInt();
samplesPerFrameAtMaxFs = bb.getShort();
a = bb.get();
b = bb.getInt();
numSamplesOrigAudioAtMaxFs = (a << 32) | (b & 0xffff);
channelMask = bb.getShort();
codecDelayAtMaxFs = bb.getShort();
int c = 21;
if ((bitwAupresData & 3) == 3) {
a = bb.get();
b = bb.getShort();
bcCoreMaxSampleRate = (a << 16) | (b & 0xffff);
bcCoreBitRate = bb.getShort();
bcCoreChannelMask = bb.getShort();
c += 7;
}
if ((bitwAupresData & 0x4) > 0) {
lsbTrimPercent = bb.get();
c++;
}
if ((bitwAupresData & 0x8) > 0) {
lbrCodingPresent = 1;
}
while (c < size) {
bb.get();
c++;
}
return true;
}
/**
* EXTSS_MD
*/
private boolean parseExtssmd(int size, ByteBuffer bb) {
int a = bb.get();
int b = bb.getShort();
extAvgBitrate = (a << 16) | (b & 0xffff);
int i = 3;
if (isVBR) {
a = bb.get();
b = bb.getShort();
extPeakBitrate = (a << 16) | (b & 0xffff);
extSmoothBuffSize = bb.getShort();
i += 5;
} else {
extFramePayloadInBytes = bb.getInt();
i += 4;
}
while (i < size) {
bb.get();
i++;
}
return true;
}
private boolean readVariables() throws IOException {
ByteBuffer bb = dataSource.map(0, 25000);
int testHeader1 = bb.getInt();
int testHeader2 = bb.getInt();
if (testHeader1 != 0x44545348 || (testHeader2 != 0x44484452)) { // ASCII: DTSHDHDR
throw new IOException("data does not start with 'DTSHDHDR' as required for a DTS-HD file");
}
while ((testHeader1 != 0x5354524d || testHeader2 != 0x44415441) && bb.remaining() > 100) { // ASCII: STRMDATA
int size = (int) bb.getLong();
if (testHeader1 == 0x44545348 && testHeader2 == 0x44484452) { // ASCII: DTSHDHDR
parseDtshdhdr(size, bb);
} else if (testHeader1 == 0x434f5245 && testHeader2 == 0x53534d44) { // ASCII: CORESSMD
if (!parseCoressmd(size, bb)) {
return false;
}
} else if (testHeader1 == 0x41555052 && testHeader2 == 0x2d484452) { // ASCII: AUPR-HDR
if (!parseAuprhdr(size, bb)) {
return false;
}
} else if (testHeader1 == 0x45585453 && testHeader2 == 0x535f4d44) { // ASCII: EXTSS_MD
if (!parseExtssmd(size, bb)) {
return false;
}
} else {
for (int i = 0; i < size; i++) {
byte b = bb.get();
}
}
testHeader1 = bb.getInt();
testHeader2 = bb.getInt();
}
long dataSize = bb.getLong();
dataOffset = bb.position();
int amode = -1;
int extAudioId = 0;
int extAudio = 0;
int corePresent = -1;
int extPresent = -1;
int extXch = 0;
int extXXch = 0;
int extX96k = 0;
int extXbr = 0;
int extLbr = 0;
int extXll = 0;
int extCore = 0;
boolean done = false;
while (!done) {
int offset = bb.position();
int sync = bb.getInt();
if (sync == 0x7ffe8001) { // DTS_SYNCWORD_CORE
if (corePresent == 1) {
done = true;
} else {
corePresent = 1;
BitReaderBuffer brb = new BitReaderBuffer(bb);
int ftype = brb.readBits(1);
int shrt = brb.readBits(5);
int cpf = brb.readBits(1); // Should always be 0 - CRC is not present
if (ftype != 1 || shrt != 31 || cpf != 0)
{ // Termination frames - first frame should not be the last...
return false;
}
int nblks = brb.readBits(7);
samplesPerFrame = 32 * (nblks + 1);
int fsize = brb.readBits(14);
frameSize += fsize + 1;
amode = brb.readBits(6); // Calc channel layout from this
int sfreq = brb.readBits(4);
samplerate = getSampleRate(sfreq);
int rate = brb.readBits(5);
bitrate = getBitRate(rate);
int fixedBit = brb.readBits(1);
if (fixedBit != 0)
{
return false;
}
int dynf = brb.readBits(1);
int timef = brb.readBits(1);
int auxf = brb.readBits(1);
int hdcd = brb.readBits(1);
extAudioId = brb.readBits(3);
extAudio = brb.readBits(1);
int aspf = brb.readBits(1);
int lff = brb.readBits(2);
int hflag = brb.readBits(1);
int hcrc = 0;
if (cpf == 1)
{ // Present only if CPF=1.
hcrc = brb.readBits(16);
}
int filts = brb.readBits(1);
int vernum = brb.readBits(4);
int chist = brb.readBits(2);
int pcmr = brb.readBits(3);
switch (pcmr)
{
case 0:
case 1:
sampleSize = 16;
break;
case 2:
case 3:
sampleSize = 20;
break;
case 5:
case 6:
sampleSize = 24;
break;
default:
return false;
}
int sumf = brb.readBits(1);
int sums = brb.readBits(1);
int dialnorm = 0;
int dng = 0;
switch (vernum)
{
case 6:
dialnorm = brb.readBits(4);
dng = -(16 + dialnorm);
break;
case 7:
dialnorm = brb.readBits(4);
dng = -dialnorm;
break;
default:
brb.readBits(4);
break;
}
bb.position(offset + fsize + 1);
}
} else if (sync == 0x64582025) { // DTS_SYNCWORD_SUBSTREAM
if (corePresent == -1) {
corePresent = 0;
samplesPerFrame = samplesPerFrameAtMaxFs;
}
extPresent = 1;
BitReaderBuffer brb = new BitReaderBuffer(bb);
int userDefinedBits = brb.readBits(8);
int nExtSSIndex = brb.readBits(2);
int headerSizeType = brb.readBits(1);
int nuBits4Header = 12;
int nuBits4ExSSFsize = 20;
if (headerSizeType == 0) {
nuBits4Header = 8;
nuBits4ExSSFsize = 16;
}
int nuExtSSHeaderSize = brb.readBits(nuBits4Header) + 1;
int nuExtSSFsize = brb.readBits(nuBits4ExSSFsize) + 1;
bb.position(offset + nuExtSSHeaderSize);
int extSync = bb.getInt();
if (extSync == 0x5a5a5a5a) {
if (extXch == 1) {
done = true;
}
extXch = 1;
} else if (extSync == 0x47004a03) {
if (extXXch == 1) {
done = true;
}
extXXch = 1;
} else if (extSync == 0x1d95f262) {
if (extX96k == 1) {
done = true;
}
extX96k = 1;
} else if (extSync == 0x655e315e) {
if (extXbr == 1) {
done = true;
}
extXbr = 1;
} else if (extSync == 0x0a801921) {
if (extLbr == 1) {
done = true;
}
extLbr = 1;
} else if (extSync == 0x41a29547) {
if (extXll == 1) {
done = true;
}
extXll = 1;
} else if (extSync == 0x02b09261) {
if (extCore == 1) {
done = true;
}
extCore = 1;
}
if (!done) {
frameSize += nuExtSSFsize;
}
bb.position(offset + nuExtSSFsize);
} else {
throw new IOException("No DTS_SYNCWORD_* found at " + bb.position());
}
}
int fd = -1;
switch (samplesPerFrame)
{
case 512:
fd = 0;
break;
case 1024:
fd = 1;
break;
case 2048:
fd = 2;
break;
case 4096:
fd = 3;
break;
}
if (fd == -1)
{
return false;
}
int coreLayout = 31;
switch (amode)
{
case 0:
case 2:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
coreLayout = amode;
}
int streamContruction = 0;
if (corePresent == 0) {
if (extXll == 1) {
if (extCore == 0) {
streamContruction = 17;
type = "dtsl";
} else {
streamContruction = 21;
type = "dtsh";
}
} else if (extLbr == 1) {
streamContruction = 18;
type = "dtse";
} else if (extCore == 1) {
type = "dtsh";
if (extXXch == 0 && extXll == 0) {
streamContruction = 19;
} else if (extXXch == 1 && extXll == 0) {
streamContruction = 20;
} else if (extXXch == 0 && extXll == 1) {
streamContruction = 21;
}
}
samplerate = maxSampleRate;
sampleSize = 24; // Is this always true?
} else {
if (extPresent < 1) {
if (extAudio > 0) {
switch (extAudioId) {
case 0:
streamContruction = 2;
type = "dtsc";
break;
case 2:
streamContruction = 4;
type = "dtsc";
break;
case 6:
streamContruction = 3;
type = "dtsh";
break;
default:
streamContruction = 0;
type = "dtsh";
break;
}
} else {
streamContruction = 1;
type = "dtsc";
}
} else {
type = "dtsh";
if (extAudio == 0) {
if ((extCore == 0) && (extXXch == 1) && (extX96k == 0) && (extXbr == 0) && (extXll == 0) && (extLbr == 0)) {
streamContruction = 5;
} else if (extCore == 0 && extXXch == 0 && extX96k == 0 && extXbr == 1 && extXll == 0 && extLbr == 0) {
streamContruction = 6;
} else if (extCore == 0 && extXXch == 1 && extX96k == 0 && extXbr == 1 && extXll == 0 && extLbr == 0) {
streamContruction = 9;
} else if (extCore == 0 && extXXch == 0 && extX96k == 1 && extXbr == 0 && extXll == 0 && extLbr == 0) {
streamContruction = 10;
} else if (extCore == 0 && extXXch == 1 && extX96k == 1 && extXbr == 0 && extXll == 0 && extLbr == 0) {
streamContruction = 13;
} else if (extCore == 0 && extXXch == 0 && extX96k == 0 && extXbr == 0 && extXll == 1 && extLbr == 0) {
streamContruction = 14;
}
} else {
if ((extAudioId == 0) && (extCore == 0) && (extXXch == 0) && (extX96k == 0) && (extXbr == 1) && (extXll == 0) && (extLbr == 0)) {
streamContruction = 7;
} else if ((extAudioId == 6) && (extCore == 0) && (extXXch == 0) && (extX96k == 0) && (extXbr == 1) && (extXll == 0) && (extLbr == 0)) {
streamContruction = 8;
} else if ((extAudioId == 0) && (extCore == 0) && (extXXch == 0) && (extX96k == 1) && (extXbr == 0) && (extXll == 0) && (extLbr == 0)) {
streamContruction = 11;
} else if ((extAudioId == 6) && (extCore == 0) && (extXXch == 0) && (extX96k == 1) && (extXbr == 0) && (extXll == 0) && (extLbr == 0)) {
streamContruction = 12;
} else if ((extAudioId == 0) && (extCore == 0) && (extXXch == 0) && (extX96k == 0) && (extXbr == 0) && (extXll == 1) && (extLbr == 0)) {
streamContruction = 15;
} else if ((extAudioId == 2) && (extCore == 0) && (extXXch == 0) && (extX96k == 0) && (extXbr == 0) && (extXll == 1) && (extLbr == 0)) {
streamContruction = 16;
}
}
}
}
ddts.setDTSSamplingFrequency(maxSampleRate);
if (isVBR) {
ddts.setMaxBitRate(1000 * (coreBitRate + extPeakBitrate));
} else {
ddts.setMaxBitRate(1000 * (coreBitRate + extAvgBitrate));
}
ddts.setAvgBitRate(1000 * (coreBitRate + extAvgBitrate));
ddts.setPcmSampleDepth(sampleSize);
ddts.setFrameDuration(fd);
ddts.setStreamConstruction(streamContruction); // We still need to look at this...
if ((coreChannelMask & 0x8) > 0 || (coreChannelMask & 0x1000) > 0) {
ddts.setCoreLFEPresent(1);
} else {
ddts.setCoreLFEPresent(0);
}
ddts.setCoreLayout(coreLayout);
ddts.setCoreSize(coreFramePayloadInBytes);
ddts.setStereoDownmix(0);
ddts.setRepresentationType(4); // This was set in the other files?
ddts.setChannelLayout(channelMask);
if (coreMaxSampleRate > 0 && extAvgBitrate > 0) {
ddts.setMultiAssetFlag(1);
} else {
ddts.setMultiAssetFlag(0);
}
ddts.setLBRDurationMod(lbrCodingPresent);
ddts.setReservedBoxPresent(0);
channelCount = 0;
for (int bit = 0; bit < 16; bit++) {
if (((channelMask >> bit) & 1) == 1) {
switch (bit) {
case 0:
case 3:
case 4:
case 7:
case 8:
case 12:
case 14:
channelCount++;
break;
default:
channelCount += 2;
break;
}
}
}
samples = generateSamples(dataSource, dataOffset, dataSize, corePresent);
sampleDurations = new long[samples.size()];
Arrays.fill(sampleDurations, samplesPerFrame );
return true;
}
private List generateSamples(DataSource dataSource, int dataOffset, long dataSize, int corePresent) throws IOException {
LookAhead la = new LookAhead(dataSource, dataOffset, dataSize , corePresent);
ByteBuffer sample;
List mySamples = new ArrayList();
while ((sample = la.findNextStart()) != null) {
final ByteBuffer finalSample = sample;
mySamples.add(new Sample() {
public void writeTo(WritableByteChannel channel) throws IOException {
channel.write((ByteBuffer) finalSample.rewind());
}
public long getSize() {
return finalSample.rewind().remaining();
}
public ByteBuffer asByteBuffer() {
return finalSample;
}
});
//System.err.println(finalSample.remaining());
}
System.err.println("all samples found");
return mySamples;
}
private int getBitRate(int rate) throws IOException {
int bitrate;
switch (rate)
{
case 0:
bitrate = 32;
break;
case 1:
bitrate = 56;
break;
case 2:
bitrate = 64;
break;
case 3:
bitrate = 96;
break;
case 4:
bitrate = 112;
break;
case 5:
bitrate = 128;
break;
case 6:
bitrate = 192;
break;
case 7:
bitrate = 224;
break;
case 8:
bitrate = 256;
break;
case 9:
bitrate = 320;
break;
case 10:
bitrate = 384;
break;
case 11:
bitrate = 448;
break;
case 12:
bitrate = 512;
break;
case 13:
bitrate = 576;
break;
case 14:
bitrate = 640;
break;
case 15:
bitrate = 768;
break;
case 16:
bitrate = 960;
break;
case 17:
bitrate = 1024;
break;
case 18:
bitrate = 1152;
break;
case 19:
bitrate = 1280;
break;
case 20:
bitrate = 1344;
break;
case 21:
bitrate = 1408;
break;
case 22:
bitrate = 1411;
break;
case 23:
bitrate = 1472;
break;
case 24:
bitrate = 1536;
break;
case 25:
bitrate = -1; // Open, some other bitrate....
break;
default:
throw new IOException("Unknown bitrate value");
}
return bitrate;
}
private int getSampleRate(int sfreq) throws IOException {
int samplerate;
switch (sfreq)
{
case 1:
samplerate = 8000;
break;
case 2:
samplerate = 16000;
break;
case 3:
samplerate = 32000;
break;
case 6:
samplerate = 11025;
break;
case 7:
samplerate = 22050;
break;
case 8:
samplerate = 44100;
break;
case 11:
samplerate = 12000;
break;
case 12:
samplerate = 24000;
break;
case 13:
samplerate = 48000;
break;
default: // No other base samplerates allowed
throw new IOException("Unknown Sample Rate");
}
return samplerate;
}
private static final int BUFFER = 1024 * 1024 * 64;
class LookAhead {
private final int corePresent;
long bufferStartPos;
int inBufferPos = 0;
DataSource dataSource;
long dataEnd;
ByteBuffer buffer;
long start;
LookAhead(DataSource dataSource, long bufferStartPos, long dataSize, int corePresent) throws IOException {
this.dataSource = dataSource;
this.bufferStartPos = bufferStartPos;
this.dataEnd = dataSize + bufferStartPos;
this.corePresent = corePresent;
fillBuffer();
}
public ByteBuffer findNextStart() throws IOException {
try {
// If core DTS stream is present then sync word is 0x7FFE8001
// otherwise 0x64582025
while (corePresent==1?!this.nextFourEquals0x7FFE8001():!nextFourEquals0x64582025()) {
this.discardByte();
}
this.discardNext4AndMarkStart();
while (corePresent==1?!this.nextFourEquals0x7FFE8001orEof():!nextFourEquals0x64582025orEof()) {
this.discardQWord();
}
return this.getSample();
} catch (EOFException e) {
return null;
}
}
private void fillBuffer() throws IOException {
System.err.println("Fill Buffer");
buffer = dataSource.map(bufferStartPos, Math.min(dataEnd - bufferStartPos, BUFFER));
}
private boolean nextFourEquals0x64582025() throws IOException {
return nextFourEquals((byte) 100, (byte) 88, (byte) 32, (byte) 37);
}
private boolean nextFourEquals0x7FFE8001() throws IOException {
return nextFourEquals((byte) 127, (byte) -2, (byte) -128, (byte) 1);
}
private boolean nextFourEquals(byte a, byte b, byte c, byte d) throws IOException {
if (buffer.limit() - inBufferPos >= 4) {
return ((buffer.get(inBufferPos) == a &&
buffer.get(inBufferPos + 1) == b &&
buffer.get(inBufferPos + 2) == c &&
(buffer.get(inBufferPos + 3) == d)));
}
if (bufferStartPos + inBufferPos + 4 >= dataSource.size()) {
throw new EOFException();
}
return false;
}
private boolean nextFourEquals0x64582025orEof() throws IOException {
return nextFourEqualsOrEof((byte) 100, (byte) 88, (byte) 32, (byte) 37);
}
private boolean nextFourEquals0x7FFE8001orEof() throws IOException {
return nextFourEqualsOrEof((byte) 127, (byte) -2, (byte) -128, (byte) 1);
}
private boolean nextFourEqualsOrEof(byte a, byte b, byte c, byte d) throws IOException {
if (buffer.limit() - inBufferPos >= 4) {
if (((bufferStartPos + inBufferPos) % (1024 * 1024)) == 0) {
System.err.println("" + ((bufferStartPos + inBufferPos) / 1024 / 1024));
}
return ((buffer.get(inBufferPos) == a /*0x7F */&&
buffer.get(inBufferPos + 1) == b/*0xfe*/ &&
buffer.get(inBufferPos + 2) == c /*0x80*/ &&
(buffer.get(inBufferPos + 3) == d)));
} else {
if (bufferStartPos + inBufferPos + 4 > dataEnd) {
return bufferStartPos + inBufferPos == dataEnd;
} else {
bufferStartPos = start;
inBufferPos = 0;
fillBuffer();
return nextFourEquals0x7FFE8001();
}
}
}
private void discardByte() {
inBufferPos += 1;
}
private void discardQWord() {
inBufferPos += 4;
}
private void discardNext4AndMarkStart() {
start = bufferStartPos + inBufferPos;
inBufferPos += 4;
}
private ByteBuffer getSample() {
if (start >= bufferStartPos) {
buffer.position((int) (start - bufferStartPos));
Buffer sample = buffer.slice();
sample.limit((int) (inBufferPos - (start - bufferStartPos)));
return (ByteBuffer) sample;
} else {
throw new RuntimeException("damn! NAL exceeds buffer");
// this can only happen if NAL is bigger than the buffer
}
}
}
}