Record and play h.264 video in memory using Jcodec

1.1k views Asked by At

I am working on a project that requires h.264 video to be written to memory and then read from memory. In other words, using a byte array rather than a File. The only java encoder library that I can find is JCodec.
      I am using version 0.2.0 of JCodec's SequenceEncoder8Bit and FrameGrab8Bit classes to read and write to a byte array. The error that I am currently getting is

    java.io.IOException: Could not find movie meta information box
    at org.jcodec.containers.mp4.demuxer.MP4Demuxer.findMovieBox(MP4Demuxer.java:56)
    at org.jcodec.containers.mp4.demuxer.MP4Demuxer.<init>(MP4Demuxer.java:46)
    at ....

My code example is as follows:

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Arrays;

import org.jcodec.api.FrameGrab8Bit;
import org.jcodec.api.JCodecException;
import org.jcodec.api.SequenceEncoder8Bit;
import org.jcodec.common.io.ByteBufferSeekableByteChannel;
import org.jcodec.common.io.SeekableByteChannel;
import org.jcodec.common.model.Picture8Bit;
import org.jcodec.common.model.Rational;
import org.jcodec.scale.AWTUtil;
import org.opencv.core.Mat;

public class Sequence8BitTest {

private static boolean _isDone = false;

public static void main(String[] args) {
    byte[] data = writeVideo();
    readVideo(data);
}

private static void encodeVideo(SequenceEncoder8Bit encoder, ByteBuffer buffer, int videoSize){
    try{//Close Encoder
        encoder.finish();
        buffer = ByteBuffer.wrap(Arrays.copyOf(buffer.array(), videoSize));
    }catch(IOException e){
        e.printStackTrace();
    }
}

private static BufferedImage matToBufferedImage(Mat in) throws IOException {
    byte[] data = new byte[in.width() * in.height() * (int)in.elemSize()];
    int type;
    BufferedImage out;

    in.get(0, 0, data);

    if(in.channels() == 1){
        type = BufferedImage.TYPE_BYTE_GRAY;
    }
    else{
        type = BufferedImage.TYPE_3BYTE_BGR;
    }

    out = new BufferedImage(in.width(), in.height(), type);
    out.getRaster().setDataElements(0, 0, in.width(), in.height(), data);

    return out;
}

private static BufferedImage readImage(FrameGrab8Bit grabber) {
    Picture8Bit pic = null;

    try {
        pic = grabber.getNativeFrame();
    } catch (IOException e) {
        e.printStackTrace();
        return null;
    }

    return AWTUtil.toBufferedImage8Bit(pic);

}

private static void readVideo(byte[] data){
    FrameGrab8Bit grabber = null;
    BufferedImage img;
    ByteBuffer buffer;
    SeekableByteChannel channel;

    buffer = ByteBuffer.wrap(data);
    channel = new ByteBufferSeekableByteChannel(buffer);

    try {
        grabber = FrameGrab8Bit.createFrameGrab8Bit(channel);

        while((img = readImage(grabber)) != null){
            //display Image
        }

        channel.close();

    } catch (IOException | JCodecException e) {
        e.printStackTrace();
        System.exit(-1);
    }
}

private static void writeImage(Mat mat, SequenceEncoder8Bit encoder){
    try{//Mat -> BufferedImage -> Picture8Bit
        BufferedImage img = matToBufferedImage(mat);
        Picture8Bit pic = AWTUtil.fromBufferedImageRGB8Bit(img);//this is the 
        encoder.encodeNativeFrame(pic);                        //only constructor that won't crash
    }catch(Exception e){
        e.printStackTrace();
    }
}

public static byte[] writeVideo(){
    int fps = 25;
    int videoSize = 300_000;//allocated for buffer --> think max size
    ByteBuffer buffer = ByteBuffer.allocate(videoSize);
    Mat mat = new Mat();
    SeekableByteChannel channel = new ByteBufferSeekableByteChannel(buffer);
    SequenceEncoder8Bit enc = null;

    try {// new SequenceEncoder..
        enc = new SequenceEncoder8Bit(channel, new Rational(fps, 1));
        enc.getEncoder().setKeyInterval(fps);

        while(!_isDone){//Here, grabbing mat frames from camera using JavaCV
            writeImage(mat, enc);
        }   
        encodeVideo(enc, buffer, (int)channel.position());
    } catch (IOException e1) {
        e1.printStackTrace();
    }

    return buffer.array();
}   

}

I looked into the code base and the call to SequenceEncoder8Bit.finish() does write a header to the buffer. Not sure why I can't get the metadata. Anyone out their familiar with this stuff?

0

There are 0 answers