1
votes

Did anybody ever use a good open source Java lib that would allow NAL > H264 conversion ?

I've seen lots of libs, but most of them incomplete or very hard to use (no documentation etc.).

What I want is to pass the NAL payload and get the actual H.264 raw video. The next step would be putting this into a MP4 container.

2

2 Answers

2
votes

From the discussion in the comments I rephrase your question and split it into two:

I want to extract NAL units from H264 RTP packets in pure java and dump them into an Annex B H264 file.

How do I do that? Is there a library?

The payload of these packets is specified in RFC3984.

I don't know any Java library that performs this out of the box. But implementation is fairly simple as long as the packaging type is not interleaved:

RFC3984 allows three packaging type

  • Single NAL unit mode
  • Non-interleaved mode
  • Interleaved mode

as long as your packet source is sending in single NAL unit mode or non-interleaved mode you can extract the NAL units without any further processing from the packets and dump them to disk with 0x00,0x00,0x00,0x01 as divider between them.

If the packaging type is interleaved you need to put a lot more work into that since than you will have to look into the NALs and re-order them to be in decoding order.

I mux raw Annex B H264 files into an MP4 container in pure Java

You can you use my mp4parser project to generate a MP4 from the Annex B format. Have a look at the H264Example - that should give you an idea on how to use it.

0
votes

Here's what I found in JCodec ( http://jcodec.org ) samples:

public class AVCMP4Mux {
    private static AvcCBox avcC;

    public static void main(String[] args) throws Exception {
        if (args.length < 2) {
            System.out.println("Syntax: <in.264> <out.mp4>\n" + "\tWhere:\n"
                    + "\t-q\tLook for stream parameters only in the beginning of stream");
            return;
        }

        File in = new File(args[0]);
        File out = new File(args[1]);

        SeekableByteChannel file = writableFileChannel(out);
        MP4Muxer muxer = new MP4Muxer(file);
        CompressedTrack track = muxer.addTrackForCompressed(TrackType.VIDEO, 25);

        mux(track, in);

        muxer.writeHeader();

        file.close();
    }

    private static void mux(CompressedTrack track, File f) throws IOException {
        MappedH264ES es = new MappedH264ES(NIOUtils.map(f));

        ArrayList<ByteBuffer> spsList = new ArrayList<ByteBuffer>();
        ArrayList<ByteBuffer> ppsList = new ArrayList<ByteBuffer>();
        Packet frame = null;
        while ((frame = es.nextFrame()) != null) {
            ByteBuffer wrap = frame.getData().duplicate();
            H264Utils.encodeMOVPacket(wrap, spsList, ppsList);
            MP4Packet pkt = new MP4Packet(new Packet(frame, wrap), frame.getPts(), 0);
            System.out.println(pkt.getFrameNo());
            track.addFrame(pkt);
        }
        addSampleEntry(track, es.getSps(), es.getPps());
    }

    private static void addSampleEntry(CompressedTrack track, SeqParameterSet[] spss, PictureParameterSet[] ppss) {
        SeqParameterSet sps = spss[0];
        Size size = new Size((sps.pic_width_in_mbs_minus1 + 1) << 4, getPicHeightInMbs(sps) << 4);

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

        avcC = new AvcCBox(sps.profile_idc, 0, sps.level_idc, write(spss), write(ppss));
        se.add(avcC);
        track.addSampleEntry(se);
    }

    private static List<ByteBuffer> write(PictureParameterSet[] ppss) {
        List<ByteBuffer> result = new ArrayList<ByteBuffer>();
        for (PictureParameterSet pps : ppss) {
            ByteBuffer buf = ByteBuffer.allocate(1024);
            pps.write(buf);
            buf.flip();
            result.add(buf);
        }
        return result;
    }

    private static List<ByteBuffer> write(SeqParameterSet[] spss) {
        List<ByteBuffer> result = new ArrayList<ByteBuffer>();
        for (SeqParameterSet sps : spss) {
            ByteBuffer buf = ByteBuffer.allocate(1024);
            sps.write(buf);
            buf.flip();
            result.add(buf);
        }
        return result;
    }
}

I took it from here: https://github.com/jcodec/jcodec/blob/master/samples/main/java/org/jcodec/samples/mux/AVCMP4Mux.java