
org.mp4parser.muxer.tracks.AbstractH26XTrack Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of muxer Show documentation
Show all versions of muxer Show documentation
This package has a focus on file operation. It can read A/V data from Random Access Datasources
package org.mp4parser.muxer.tracks;
import org.mp4parser.boxes.iso14496.part12.CompositionTimeToSample;
import org.mp4parser.boxes.iso14496.part12.SampleDependencyTypeBox;
import org.mp4parser.boxes.sampleentry.SampleEntry;
import org.mp4parser.muxer.*;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
/**
* Bundles common functionality and parsing patterns of NAL based formats such as H264(AVC) and H265 (HEVC).
*/
public abstract class AbstractH26XTrack extends AbstractTrack {
public static int BUFFER = 65535 << 10;
protected long[] decodingTimes;
protected List ctts = new ArrayList();
protected List sdtp = new ArrayList();
protected List stss = new ArrayList();
protected TrackMetaData trackMetaData = new TrackMetaData();
boolean tripleZeroIsEndOfSequence = true;
private DataSource dataSource;
public AbstractH26XTrack(DataSource dataSource, boolean tripleZeroIsEndOfSequence) {
super(dataSource.toString());
this.dataSource = dataSource;
this.tripleZeroIsEndOfSequence = tripleZeroIsEndOfSequence;
}
public AbstractH26XTrack(DataSource dataSource) {
this(dataSource, true);
}
protected static InputStream cleanBuffer(InputStream is) {
return new CleanInputStream(is);
}
protected static byte[] toArray(ByteBuffer buf) {
buf = buf.duplicate();
byte[] b = new byte[buf.remaining()];
buf.get(b, 0, b.length);
return b;
}
public TrackMetaData getTrackMetaData() {
return trackMetaData;
}
protected ByteBuffer findNextNal(LookAhead la) throws IOException {
try {
while (!la.nextThreeEquals001()) {
la.discardByte();
}
la.discardNext3AndMarkStart();
while (!la.nextThreeEquals000or001orEof(tripleZeroIsEndOfSequence)) {
la.discardByte();
}
return la.getNal();
} catch (EOFException e) {
return null;
}
}
abstract protected SampleEntry getCurrentSampleEntry();
/**
* Builds an MP4 sample from a list of NALs. Each NAL will be preceded by its
* 4 byte (unit32) length.
*
* @param nals a list of NALs that form the sample
* @return sample as it appears in the MP4 file
*/
protected Sample createSampleObject(List extends ByteBuffer> nals) {
byte[] sizeInfo = new byte[nals.size() * 4];
ByteBuffer sizeBuf = ByteBuffer.wrap(sizeInfo);
for (ByteBuffer b : nals) {
sizeBuf.putInt(b.remaining());
}
ByteBuffer[] data = new ByteBuffer[nals.size() * 2];
for (int i = 0; i < nals.size(); i++) {
data[2 * i] = ByteBuffer.wrap(sizeInfo, i * 4, 4);
data[2 * i + 1] = nals.get(i);
}
return new SampleImpl(data, getCurrentSampleEntry());
}
public long[] getSampleDurations() {
return decodingTimes;
}
public List getCompositionTimeEntries() {
return ctts;
}
public long[] getSyncSamples() {
long[] returns = new long[stss.size()];
for (int i = 0; i < stss.size(); i++) {
returns[i] = stss.get(i);
}
return returns;
}
public List getSampleDependencies() {
return sdtp;
}
public void close() throws IOException {
dataSource.close();
}
public static class LookAhead {
long bufferStartPos = 0;
int inBufferPos = 0;
DataSource dataSource;
ByteBuffer buffer;
long start;
public LookAhead(DataSource dataSource) throws IOException {
this.dataSource = dataSource;
fillBuffer();
}
public void fillBuffer() throws IOException {
buffer = dataSource.map(bufferStartPos, Math.min(dataSource.size() - bufferStartPos, BUFFER));
}
public boolean nextThreeEquals001() throws IOException {
if (buffer.limit() - inBufferPos >= 3) {
return (buffer.get(inBufferPos) == 0 &&
buffer.get(inBufferPos + 1) == 0 &&
buffer.get(inBufferPos + 2) == 1);
}
if (bufferStartPos + inBufferPos + 3 >= dataSource.size()) {
throw new EOFException();
}
return false;
}
public boolean nextThreeEquals000or001orEof(boolean tripleZeroIsEndOfSequence) throws IOException {
if (buffer.limit() - inBufferPos >= 3) {
return ((buffer.get(inBufferPos) == 0 &&
buffer.get(inBufferPos + 1) == 0 &&
((buffer.get(inBufferPos + 2) == 0 && tripleZeroIsEndOfSequence) || buffer.get(inBufferPos + 2) == 1)));
} else {
if (bufferStartPos + inBufferPos + 3 > dataSource.size()) {
return bufferStartPos + inBufferPos == dataSource.size();
} else {
bufferStartPos = start;
inBufferPos = 0;
fillBuffer();
return nextThreeEquals000or001orEof(tripleZeroIsEndOfSequence);
}
}
}
public void discardByte() {
inBufferPos++;
}
public void discardNext3AndMarkStart() {
inBufferPos += 3;
start = bufferStartPos + inBufferPos;
}
public ByteBuffer getNal() {
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
// and that most likely cannot happen with correct inputs
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy