Home Source code Downloads Documentation

Muxing h264 (avc) into mp4

This sample shows how to mux coded frames from H264 ( MPEG 4 AVC ) elementary stream into mp4 ( ISO base media format ) file.

Source code

Compiling and running

git clone git://github.com/jcodec/jcodec.git
cd jcodec
mvn -Dmaven.test.skip=true clean install
cd samples
mvn -Dmaven.test.skip=true clean assembly:assembly

java -cp target/jcodec-samples-<version>-uberjar.jar org.jcodec.samples.mux.AVCMP4Mux <in mov> <in mov> <out mov>

Implementation details

When muxing h264 into MP4 container frame NAL units ( IDR and non IDR ) go into packets of a video track. Stream parameters ( SPSs, PPSs ) are stored in the video sample entry of the video track.

Code explanation

Create 'bare' track for video without sample entry. Sample entry will be added after the input file is scanned and all SPSs/PPSs are collected. Mux NAL units into track collecting all SPSs and PPSs that we come across. Create video sample entry, add 'avcC' sub-box, add video sample entry to video track. Save header of the movie.

CompressedTrack track = muxer.addTrackForCompressed(TrackType.VIDEO, 25);

mux(track, in, spsList, ppsList);

Size size = new Size((spsList.get(0).pic_width_in_mbs_minus1 + 1) << 4,
        (spsList.get(0).pic_height_in_map_units_minus1 + 1) << 4);

SampleEntry se = MP4Muxer.videoSampleEntry("avc1", size, "JCodec");

se.add(new AvcCBox(spsList, ppsList));
track.addSampleEntry(se);

muxer.writeHeader();

This code forms correct MP4 packet from a codec h264 frame. MP4 packet should contain a total length of coded frame as the first 4 bytes of the payload ( big endian 32 bit integer ).

ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos);
byte[] data = IOUtils.toByteArray(nextNALUnit);
out.writeInt(data.length + 1);
nu.write(out);
out.write(data);
return baos.toByteArray();

This code runs through all the NAL units of h264 elementary stream, selects frames and addes them to container. All the SPSs and PPSs specifically are collected and later will be added to 'avcC' box of video sample entry.

do {
    nextNALUnit = in.nextNALUnit();
    if (nextNALUnit == null)
        continue;
    NALUnit nu = NALUnit.read(nextNALUnit);
    if (nu.type == NALUnitType.IDR_SLICE || nu.type == NALUnitType.NON_IDR_SLICE) {

        track.addFrame(formPacket(nu, nextNALUnit), 1, 1, nu.type == NALUnitType.IDR_SLICE);
    } else if (nu.type == NALUnitType.SPS) {
        spsList.add(SeqParameterSet.read(nextNALUnit));
    } else if (nu.type == NALUnitType.PPS) {
        ppsList.add(PictureParameterSet.read(nextNALUnit));
    } else {
        nextNALUnit.skip(Integer.MAX_VALUE);
    }
} while (nextNALUnit != null);
Guides
H264