package org.jcodec.samples.mashup; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import org.jcodec.codecs.h264.H264Utils; import org.jcodec.codecs.h264.decode.SliceHeaderReader; import org.jcodec.codecs.h264.io.model.NALUnit; import org.jcodec.codecs.h264.io.model.NALUnitType; import org.jcodec.codecs.h264.io.model.PictureParameterSet; import org.jcodec.codecs.h264.io.model.SeqParameterSet; import org.jcodec.codecs.h264.io.model.SliceHeader; import org.jcodec.codecs.h264.io.write.NALUnitWriter; import org.jcodec.codecs.h264.io.write.SliceHeaderWriter; import org.jcodec.common.io.BitReader; import org.jcodec.common.io.BitWriter; import org.jcodec.common.io.IOUtils; import org.jcodec.common.io.NIOUtils; import org.junit.Assert; /** * This class is part of JCodec ( www.jcodec.org ) This software is distributed * under FreeBSD License * * @author The JCodec project * */ public class H264Mashup { private SeqParameterSet sps; private PictureParameterSet pps; private int lastSPS; private int lastPPS; public static void main(String[] args) throws Exception { if (args.length < 3) { System.out.println("Syntax: <in 1> <in 2> <out>"); System.exit(-1); } new H264Mashup().mashup(new File(args[0]), new File(args[1]), new File(args[2])); } public void mashup(File if1, File if2, File of) throws IOException { { MappedByteBuffer map = NIOUtils.mapFile(if1); FileOutputStream os = null; try { os = new FileOutputStream(of); NALUnitWriter out = new NALUnitWriter(os.getChannel()); justCopy(out, map); } finally { IOUtils.closeQuietly(os); } } { FileOutputStream os = null; try { MappedByteBuffer map = NIOUtils.mapFile(if1); os = new FileOutputStream(of, true); NALUnitWriter out = new NALUnitWriter(os.getChannel()); copyModify(out, map); } finally { IOUtils.closeQuietly(os); } } } private void justCopy(NALUnitWriter out, ByteBuffer buf) throws IOException { ByteBuffer nus; while ((nus = H264Utils.nextNALUnit(buf)) != null) { NALUnit nu = NALUnit.read(nus); if (nu.type == NALUnitType.SPS) { out.writeUnit(nu, nus.duplicate()); sps = SeqParameterSet.read(nus); if (sps.seq_parameter_set_id > lastSPS) lastSPS = sps.seq_parameter_set_id; } else if (nu.type == NALUnitType.PPS) { out.writeUnit(nu, nus.duplicate()); pps = PictureParameterSet.read(nus); if (pps.pic_parameter_set_id > lastPPS) lastPPS = pps.pic_parameter_set_id; } else { out.writeUnit(nu, nus); } } } private void copyModify(NALUnitWriter out, ByteBuffer buf) throws IOException { SliceHeaderReader reader = null; SliceHeaderWriter writer = null; ByteBuffer nus; while ((nus = H264Utils.nextNALUnit(buf)) != null) { NALUnit nu = NALUnit.read(nus); if (nu.type == NALUnitType.SPS) { out.writeUnit(nu, nus.duplicate()); sps = SeqParameterSet.read(nus); sps.seq_parameter_set_id = ++lastSPS; System.out.println("SPS"); } else if (nu.type == NALUnitType.PPS) { out.writeUnit(nu, nus.duplicate()); pps = PictureParameterSet.read(nus); pps.seq_parameter_set_id = lastSPS; pps.pic_parameter_set_id = ++lastPPS; reader = new SliceHeaderReader(); writer = new SliceHeaderWriter(); System.out.println("PPS"); } else if (nu.type == NALUnitType.IDR_SLICE || nu.type == NALUnitType.NON_IDR_SLICE) { ByteBuffer res = ByteBuffer.allocate(nus.remaining() + 10); BitReader r = BitReader.createBitReader(nus); SliceHeader header = reader.readPart1(r); reader.readPart2(header, nu, sps, pps, r); header.pic_parameter_set_id = lastPPS; BitWriter w = new BitWriter(res); writer.write(header, nu.type == NALUnitType.IDR_SLICE, nu.nal_ref_idc, w); if (pps.entropy_coding_mode_flag) { copyCABAC(w, r); } else { copyCAVLC(w, r); } res.flip(); out.writeUnit(nu, res); } else { out.writeUnit(nu, nus); } } } private void copyCAVLC(BitWriter w, BitReader r) { int rem = 8 - r.curBit(); int l = r.readNBit(rem); w.writeNBit(l, rem); int b = r.readNBit(8), next; while ((next = r.readNBit(8)) != -1) { w.writeNBit(b, 8); b = next; } int len = 7; while ((b & 0x1) == 0) { b >>= 1; len--; } w.writeNBit(b, len); w.write1Bit(1); w.flush(); } private void copyCABAC(BitWriter w, BitReader r) { long bp = r.curBit(); long rem = r.readNBit(8 - (int) bp); Assert.assertEquals(rem, (1 << (8 - bp)) - 1); if (w.curBit() != 0) w.writeNBit(0xff, 8 - w.curBit()); int b; while ((b = r.readNBit(8)) != -1) w.writeNBit(b, 8); } }