package org.mp4parser.streaming.input.h264; import org.mp4parser.streaming.extensions.TrackIdTrackExtension; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.concurrent.Callable; /** * Reads H264 data from an Annex B InputStream. */ public class H264AnnexBTrack extends H264NalConsumingTrack implements Callable<Void> { private InputStream inputStream; public H264AnnexBTrack(InputStream inputStream) throws IOException { assert inputStream != null; this.inputStream = new BufferedInputStream(inputStream); } public Void call() throws IOException, InterruptedException { byte[] nal; NalStreamTokenizer st = new NalStreamTokenizer(inputStream); while ((nal = st.getNext()) != null) { //System.err.println("NAL before consume"); consumeNal(ByteBuffer.wrap(nal)); //System.err.println("NAL after consume"); } pushSample(createSample(buffered, fvnd.sliceHeader, sliceNalUnitHeader), true, true); return null; } @Override public String toString() { TrackIdTrackExtension trackIdTrackExtension = this.getTrackExtension(TrackIdTrackExtension.class); if (trackIdTrackExtension != null) { return "H264AnnexBTrack{trackId=" + trackIdTrackExtension.getTrackId() + "}"; } else { return "H264AnnexBTrack{}"; } } public static class NalStreamTokenizer { private static Logger LOG = LoggerFactory.getLogger(NalStreamTokenizer.class.getName()); MyByteArrayOutputStream next = new MyByteArrayOutputStream(); int pattern = 0; private InputStream inputStream; public NalStreamTokenizer(InputStream inputStream) { this.inputStream = inputStream; } public byte[] getNext() throws IOException { //System.err.println("getNext() called"); if (LOG.isDebugEnabled()) { LOG.debug("getNext() called"); } int c; while ((c = inputStream.read()) != -1) { if (!(pattern == 2 && c == 3)) { next.write(c); if (pattern == 0 && c == 0) { pattern = 1; } else if (pattern == 1 && c == 0) { pattern = 2; } else if (pattern == 2 && c == 0) { byte[] s = next.toByteArrayLess3(); next.reset(); if (s != null) { return s; } } else if (pattern == 2 && c == 1) { byte[] s = next.toByteArrayLess3(); next.reset(); pattern = 0; if (s != null) { return s; } } else if (pattern != 0) { pattern = 0; } } else { pattern = 0; } } byte[] s = next.toByteArray(); next.reset(); if (s.length > 0) { return s; } else { return null; } } } static class MyByteArrayOutputStream extends ByteArrayOutputStream { public byte[] toByteArrayLess3() { if (count > 3) { return Arrays.copyOf(buf, count - 3 > 0 ? count - 3 : 0); } else { return null; } } } }