package org.jcodec.samples.splitter; import java.io.ByteArrayOutputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.Arrays; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * @author The JCodec project * */ public abstract class H264SplitterBase { private static final int NU_IDR = 5; private static final int NU_NON_IDR = 1; private static final int NU_PPS = 8; private static final int NU_SPS = 7; private OutputStream os; private int nIdr; private byte[] sps; private byte[] pps; private byte[] oldSps; private byte[] oldPps; private int[] next; private int sliceCount; private boolean prevIdr; static byte[] marker = new byte[4]; static { marker[0] = marker[1] = marker[2] = 0; marker[3] = 1; } protected void split(InputStream io) throws IOException { while (doNALUnit(io)) ; finishCurrentSlice(); } private boolean doNALUnit(InputStream io) throws IOException { if (!readMarker(io)) return false; int nalUnit = readRBSPByte(io); int nalUnitType = nalUnit & 0x1F; if (nalUnitType == NU_IDR) { if (sps != null && pps != null) { if (!prevIdr) { if (nIdr >= getMaxIdr()) { breakHere(); } nIdr++; } prevIdr = true; writeMarker(); os.write(nalUnit); copyRBSP(io, os); } else { skipRBSP(io); } } else { if (nalUnitType == NU_SPS) { oldSps = sps; sps = readRBSP(io); } else if (nalUnitType == NU_PPS) { oldPps = pps; pps = readRBSP(io); if (newSPSPPS()) { breakHere(); } } else if (nalUnitType == NU_NON_IDR) { if (sps != null && pps != null) { writeMarker(); os.write(nalUnit); copyRBSP(io, os); } else { skipRBSP(io); } } else { skipRBSP(io); } prevIdr = false; } return true; } private boolean newSPSPPS() { return !Arrays.equals(this.oldSps, sps) || !Arrays.equals(this.oldPps, pps); } private int readRBSPByte(InputStream io) throws IOException { if (next[0] == -1) throw new EOFException(); int ret = next[0]; next[0] = next[1]; next[1] = next[2]; next[2] = io.read(); return ret; } private boolean moreRBSPData() { return next[0] != -1 && !(next[0] == 0 && next[1] == 0 && (next[2] == 1 || next[2] == 0)); } private void copyRBSP(InputStream io, OutputStream os) throws IOException { while (moreRBSPData()) { os.write(readRBSPByte(io)); } } private void skipRBSP(InputStream io) throws IOException { while (moreRBSPData()) readRBSPByte(io); } private byte[] readRBSP(InputStream io) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); copyRBSP(io, baos); return baos.toByteArray(); } private void read3B(int[] buf, InputStream io) throws IOException { buf[0] = io.read(); buf[1] = io.read(); buf[2] = io.read(); } private boolean readMarker(InputStream io) throws IOException { if (next == null) { next = new int[3]; read3B(next, io); } if (next[0] == -1 || next[1] == -1 || next[2] == -1) return false; if ((next[0] | next[1] | next[2]) == 0) { int fourth = io.read(); if (fourth == -1) return false; } read3B(next, io); return true; } private void writeMarker() throws IOException { os.write(marker); } private void breakHere() throws IOException { finishCurrentSlice(); startNewSlice(); ++sliceCount; writeMarker(); os.write((2 << 5) | NU_SPS); os.write(sps); writeMarker(); os.write((2 << 5) | NU_PPS); os.write(pps); nIdr = 0; } protected void setOutputStream(OutputStream os) { this.os = os; } protected OutputStream getOutputStream() { return os; } protected int getSliceCount() { return sliceCount; } protected abstract void finishCurrentSlice() throws IOException; protected abstract void startNewSlice() throws IOException; protected abstract int getMaxIdr(); }