JCodec main repo

Related tags

Media jcodec
Overview

jcodec - a pure java implementation of video/audio codecs.

About

JCodec is a library implementing a set of popular video and audio codecs. Currently JCodec supports:

  • Video codecs

    • H.264 main profile decoder;
    • H.264 baseline profile encoder;
    • VP8 decoder (I frames only);
    • VP8 encoder (I frames only);
    • MPEG 1/2 decoder ( I/P/B frames, interlace );
    • Apple ProRes decoder/encoder;
    • JPEG decoder;
    • PNG decoder/encoder.
    • DivX/Xvid
  • Audio codecs

    • SMPTE 302M decoder;
    • AAC decoder (JAAD)
    • RAW PCM.
  • Formats:

    • MP4 ( MOV ) demuxer / muxer;
    • MKV ( Matroska ) demuxer / muxer;
    • MPEG PS demuxer;
    • MPEG TS demuxer;
    • WAV demuxer/muxer;
    • MPEG Audio (MP3) demuxer;
    • ADTS demuxer.
    • DPX parser

JCodec is free software distributed under FreeBSD License.

Future development

Those are just some of the things JCodec dev team is planning to work on:

  • Video
    • Improve H.264 encoder: CABAC, rate control;
    • Performance optimize H.264 decoder.
  • Audio
    • MP3 decoder;
    • AAC encoder.

Getting started

JCodec can be used in both standard Java and Android. It contains platform-agnostic java classes. To use the latest version of JCodec add the maven dependency as below:

<dependency>
    <groupId>org.jcodec</groupId>
    <artifactId>jcodec</artifactId>
    <version>0.2.5</version>
</dependency>

OR gradle dependency as below:

dependencies {
    ...
    implementation 'org.jcodec:jcodec:0.2.5'
    ...
}

Additionally if you want to use JCodec with AWT images (BufferedImage) add this maven dependency:

<dependency>
    <groupId>org.jcodec</groupId>
    <artifactId>jcodec</artifactId>
    <version>0.2.5</version>
</dependency>
<dependency>
    <groupId>org.jcodec</groupId>
    <artifactId>jcodec-javase</artifactId>
    <version>0.2.5</version>
</dependency>

OR if you want to use JCodec with Android images (Bitmap) add this gradle dependency:

android {
    configurations.all {
        resolutionStrategy.force 'com.google.code.findbugs:jsr305:3.0.2'
    }
}
dependencies {
    ...
    implementation 'org.jcodec:jcodec:0.2.5'
    implementation 'org.jcodec:jcodec-android:0.2.5'
    ...
}

For the latest and greatest (the 0.2.6-SNAPSHOT) clone this Git project and build locally like so:

git clone https://github.com/jcodec/jcodec.git
cd jcodec
mvn clean install
(cd javase; mvn clean install)
(cd android; mvn clean install)

If you JUST need the jars, download them from here:

There is virtually no documentation right now but the plan is to catch up on this so stay tuned. stackoverflow.com contains quite a bit information at this point.

Also check the 'samples' subfolder. It's a maven project, and it contains some code samples for the popular use-cases:

Performance / quality considerations

Because JCodec is a pure Java implementation please adjust your performance expectations accordingly. We usually make the best effort to write efficient code but despite this the decoding will typically be an order of magnitude slower than the native implementations (such as FFMpeg). We are currently looking into implementing performance-critical parts in OpenCL (or RenderScript for Android) but the ETA is unknown.

Expect the encoded quality/bitrate for h.264 (AVC) to be so much worse compared to the well known native encoders (such as x264). This is because very little work has been put so far into developing the encoder and also because encoders usually trade speed for quality, speed is something we don't have in Java, hence the quality. Again we may potentially fix that in the future by introducing OpenCL (RenderScript) code but at this point it's an unknown.

That said the decode quality should be at the industry level. This is because the decoding process is usually specified by the standard and the correct decoder implementations are expected to produce bit-exact outputs.

Sample code

Getting a single frame from a movie ( supports only AVC, H.264 in MP4, ISO BMF, Quicktime container ):

int frameNumber = 42;
Picture picture = FrameGrab.getFrameFromFile(new File("video.mp4"), frameNumber);

//for JDK (jcodec-javase)
BufferedImage bufferedImage = AWTUtil.toBufferedImage(picture);
ImageIO.write(bufferedImage, "png", new File("frame42.png"));

//for Android (jcodec-android)
Bitmap bitmap = AndroidUtil.toBitmap(picture);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream("frame42.png")); 

Get all frames from a video file

File file = new File("video.mp4");
FrameGrab grab = FrameGrab.createFrameGrab(NIOUtils.readableChannel(file));
Picture picture;
while (null != (picture = grab.getNativeFrame())) {
    System.out.println(picture.getWidth() + "x" + picture.getHeight() + " " + picture.getColor());
}

Getting a sequence of frames from a movie ( supports only AVC, H.264 in MP4, ISO BMF, Quicktime container ):

double startSec = 51.632;
int frameCount = 10;
File file = new File("video.mp4");

FrameGrab grab = FrameGrab.createFrameGrab(NIOUtils.readableChannel(file));
grab.seekToSecondPrecise(startSec);

for (int i=0;i<frameCount;i++) {
    Picture picture = grab.getNativeFrame();
    System.out.println(picture.getWidth() + "x" + picture.getHeight() + " " + picture.getColor());
    //for JDK (jcodec-javase)
    BufferedImage bufferedImage = AWTUtil.toBufferedImage(picture);
    ImageIO.write(bufferedImage, "png", new File("frame"+i+".png"));

    //for Android (jcodec-android)
    Bitmap bitmap = AndroidUtil.toBitmap(picture);
    bitmap.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream("frame"+i+".png")); 
}

Making a video with a set of images from memory:

SeekableByteChannel out = null;
try {
    out = NIOUtils.writableFileChannel("/tmp/output.mp4");
    // for Android use: AndroidSequenceEncoder
    AWTSequenceEncoder encoder = new AWTSequenceEncoder(out, Rational.R(25, 1));
    for (...) {
        // Generate the image, for Android use Bitmap
        BufferedImage image = ...;
        // Encode the image
        encoder.encodeImage(image);
    }
    // Finalize the encoding, i.e. clear the buffers, write the header, etc.
    encoder.finish();
} finally {
    NIOUtils.closeQuietly(out);
}

H264 video from png images

File output = new File("test.mp4");
SequenceEncoder enc = SequenceEncoder.createWithFps(NIOUtils.writableChannel(output), new Rational(1, 1));
String[] files = {"frame0.png", "frame1.png", "frame2.png"};
for (String file : files) {
   enc.encodeNativeFrame(AWTUtil.decodePNG(new File(file), ColorSpace.RGB));
}
enc.finish();

Read Tape Timecode from MXF file

TapeTimecode timecode = MXFDemuxer.readTapeTimecode(new File("myfile.mxf"));

Parse DPX metadata

DPXMetadata dpx = DPXReader.readFile(firstDpx).parseMetadata();
System.out.println(dpx.getTimecodeString());

Parse Apple GPS metadata from MOV or MP4 file

MovieBox moov = MP4Util.parseMovie(new File("gps1.mp4"));
UdtaBox udta = NodeBox.findFirst(moov, UdtaBox.class, "udta");
String latlng = udta.latlng();
assertEquals("-35.2840+149.1215/", latlng);

OR

MovieBox moov = MP4Util.parseMovie(new File("gps2.MOV"));
MetaBox meta = NodeBox.findFirst(moov, MetaBox.class, "meta");
String latlng1 = meta.getKeyedMeta().get("com.apple.quicktime.location.ISO6709").getString();
assertEquals("-35.2844+149.1214+573.764/", latlng1);
String latlng2 = meta.getItunesMeta().get(1).getString();
assertEquals("-35.2844+149.1214+573.764/", latlng2);

Extract subtitles from MKV file

MKVDemuxer demuxer = new MKVDemuxer(new AutoFileChannelWrapper(new File("subs.mkv")));
DemuxerTrack track = demuxer.getSubtitleTracks().get(0);
Packet packet;
while (null != (packet = track.nextFrame())) {
    String text = takeString(packet.getData());
    System.out.println("time: " + packet.pts + " text: " + text);
}

MP4/M4A/MOV metadata versions

Some editors (e.g. Final Cut Pro 7) employ a clever hack to support multiple versions of metadata for mp4 files. The trick is to append a new version of moov atom to end of file and set previous version atom name to free. Jcodec supports this hack via org.jcodec.movtool.MoovVersions

See MoovVersionsTest.java for complete usage example.

To list available versions use

MoovVersions.listMoovVersionAtoms(new File("my.mp4"))

To add a version

moov = MP4Util.parseMovie(new File("my.mp4"))
//add your modifications to moov here
MoovVersions.addVersion(new File("my.mp4"), moov)

To rollback (undo) to previous version

MoovVersions.undo(new File("my.mp4"))

To rollback to specific version

versions = MoovVersions.listMoovVersionAtoms(new File("my.mp4"))
//pick your version
version = versions.get(Math.random()*versions.size())
MoovVersions.rollback(new File("my.mp4"), version)

Contact

Feel free to communicate any questions or concerns to us. Dev team email: [email protected]

Comments
  • Images to mp4

    Images to mp4

    Hope its ok to post this here, but didn't know where else.

    I got it kind of working but, i can't get the correct delays working. I basically want a gif like animation of pictures. So when i use this line to add frame i don't quite understand the parameters:

        int timescale = 25;
    FramesMP4MuxerTrack outTrack = muxer.addTrackForCompressed(TrackType.VIDEO, timescale);
    
    // Create H.264 encoder
    
    H264Encoder encoder = new H264Encoder(); // not we could use a
    // rate
    // control in the
    // constructor
    
    // Allocate a buffer that would hold an encoded frame
    ByteBuffer _out = ByteBuffer.allocate(height * width * 6); // Not sur
    // about
    // RGB
    
    // Allocate storage for SPS/PPS, they need to be stored
    // separately
    // in a special place of MP4 file
    ArrayList<ByteBuffer> spsList = new ArrayList<ByteBuffer>();
    ArrayList<ByteBuffer> ppsList = new ArrayList<ByteBuffer>();
    
    
    
    
    options.inSampleSize = imageWidth / 320;
    for (int i = 0; i < images.size(); i++) {
        Bitmap bitmap = BitmapFactory.decodeFile(images.get(i),
        options);
        Picture img = fromBitmap(bitmap);
    
        Picture yuv = Picture.create(img.getWidth(), img.getHeight(), ColorSpace.YUV420);
        transform.transform(img, yuv);
    
        img = null;
        _out.clear();
        ByteBuffer result = encoder.encodeFrame(_out, yuv); //toEncode
    
        // Based on the frame above form correct MP4 packet
        spsList.clear();
        ppsList.clear();
        H264Utils.encodeMOVPacket(result, spsList, ppsList);
    
        outTrack.addFrame(new MP4Packet(result, i, timescale, 1, speed, true, null, i, 0));
    
        result = null;
    
    }
    outTrack.addSampleEntry(H264Utils.createMOVSampleEntry(spsList, ppsList));
    muxer.writeHeader();
    

    Can someone explain those parameters, especially the one at this line:

       outTrack.addFrame(new MP4Packet(result, i, timescale, 1, speed, true, null, i, 0));
    

    I just want each frame to have a certain duration/delay. Can anyone help?

    opened by tobibo 19
  • Tutorial / Documentation

    Tutorial / Documentation

    Hi,

    I'm looking for some ways to encode/decode .H264 file in java. After googling, I find out your project and very interested in it. Therefore, I couldn't figure out how to get started with it.

    So, can you please give me some documentations/tutorials or references ?

    Thank you.

    opened by nxhoaf 15
  • Android : MP4Muxer writeHeader(); outOfMemory

    Android : MP4Muxer writeHeader(); outOfMemory

    Hi,

    I'm trying to create a movie from some images in an Android project. I succeed, but with low memory device, memory dedicated is too small for writeHeader() on the MP4Muxer.

    I checked the memory usage, my app is between 6MB and 11MB while adding all images to the outTrack (CompressedTrack) and then when i do writeHeader(), it goes to 26MB.

    So, i doubt about it but is there another a way to do it using less memory? Or maybe with less memory leak?

    I have already recycle all Android Bitmap and Jcodec Picture used after adding them to the outTrack.

    And also, is it normal than produced movies doesn't works on VLC or Windows Movie Player or maybe i did something wrong?

    Mathias.

    try {
            RgbToYuv420 transform = new RgbToYuv420(0, 0);
    
            this.ch = NIOUtils.writableFileChannel(new File(pathMovie+"out.mp4"));
            final MP4Muxer muxer = new MP4Muxer(ch, Brand.MP4);
    
            // Add a video track
            CompressedTrack outTrack = muxer.addTrackForCompressed(TrackType.VIDEO, 25);
    
            // Create H.264 encoder
            H264Encoder encoder = new H264Encoder(); // not we could use a rate control in the constructor
    
            // Allocate a buffer that would hold an encoded frame
            ByteBuffer _out = ByteBuffer.allocate(640 * 480 * 6); //Not sur about RGB
    
            // Allocate storage for SPS/PPS, they need to be stored separately in a special place of MP4 file
            ArrayList<ByteBuffer> spsList = new ArrayList<ByteBuffer>();
            ArrayList<ByteBuffer> ppsList = new ArrayList<ByteBuffer>();
    
            final File directory = new File(pathImage);
            final int numberOfimage = 10;
    
            num = 0;
            for (File file : directory.listFiles()) {
                if (file.isFile()) {
    
                    Bitmap bitmap = BitmapFactory.decodeFile(file.getPath());
                    Picture rgb = fromBitmap(bitmap);
                    bitmap.recycle();
    
                    Picture yuv = Picture.create(rgb.getWidth(), rgb.getHeight(), ColorSpace.YUV420);
                    transform.transform(rgb, yuv);
    
                    rgb = null;
    
                    ByteBuffer result = encoder.encodeFrame(_out, yuv); //toEncode
    
                    yuv = null;
    
                    H264Utils.encodeMOVPacket(result, spsList, ppsList);
                    outTrack.addFrame(new MP4Packet(result, 0, 25, numberOfimage, num, true, null, 0, num));
                    outTrack.addSampleEntry(H264Utils.createMOVSampleEntry(spsList, ppsList));
    
                    result = null;
                    System.gc();
    
                    num++;
                }
            }
    
            // Write MP4 header and finalize recording
            muxer.writeHeader();
    
        } catch (Exception e) {
            Log.d("writer","Bonjour => "+e.toString());
        }
    
    opened by Math42 14
  • Matrix Structure is destroyed when add metadata

    Matrix Structure is destroyed when add metadata

    Hello

    I typed the below command to insert metadata into a captured video by Android phone. ./metaedit -sk metadata=foobar input_video.mp4

    I found out that the metadata was inserted but there is a problem with rotation for the video. Original file is portrait but converted one is landscape.

    I checked the video information by exiftool and it seems that the matrix structure may be destroyed. I've attached the videos and information texts as zip file.

    Could you please check it out? I would like to know how to avoid this problem. Thanks

    [ metadata.zip includes]

    • input_video.mp: original video file
    • .input_video.mp: converted video file
    • before_info.txt: the information of original video file by exiftool
    • after_info.txt: the information of converted video file by exiftool

    metadata.zip

    opened by ShinjiYoshimori 12
  • Crash on H264Encoder.encodeFrame

    Crash on H264Encoder.encodeFrame

    If I try to encode some images (jpg) to a movie on android, I get the following errors:

    Code:

    Picture rgb = fromBitmap(BitmapFactory.decodeFile(next.getAbsolutePath()));
    Picture yuv = Picture.create(rgb.getWidth(), rgb.getHeight(), ColorSpace.YUV420);
    
    transform.transform(rgb, yuv);
    
    ByteBuffer tempBuffer = ByteBuffer.allocate(yuv.getWidth() * yuv.getHeight() * 3);
    ByteBuffer writeBuffer = encoder.encodeFrame(tempBuffer, yuv); // <-- Line 100
    

    Errors:

    05-14 18:41:07.676: E/dalvikvm(6795): Could not find class 'gnu.trove.map.hash.TIntIntHashMap', referenced from method org.jcodec.common.io.VLCBuilder.<init>
    05-14 18:41:07.676: W/dalvikvm(6795): VFY: unable to resolve new-instance 518 (Lgnu/trove/map/hash/TIntIntHashMap;) in Lorg/jcodec/common/io/VLCBuilder;
    05-14 18:41:07.686: D/dalvikvm(6795): VFY: replacing opcode 0x22 at 0x0003
    05-14 18:41:07.686: E/dalvikvm(6795): Could not find class 'gnu.trove.map.hash.TIntIntHashMap', referenced from method org.jcodec.common.io.VLCBuilder.<init>
    05-14 18:41:07.686: W/dalvikvm(6795): VFY: unable to resolve new-instance 518 (Lgnu/trove/map/hash/TIntIntHashMap;) in Lorg/jcodec/common/io/VLCBuilder;
    05-14 18:41:07.686: D/dalvikvm(6795): VFY: replacing opcode 0x22 at 0x0003
    05-14 18:41:07.686: W/dalvikvm(6795): VFY: unable to find class referenced in signature (Lgnu/trove/map/hash/TIntIntHashMap;)
    05-14 18:41:07.686: W/dalvikvm(6795): VFY: unable to find class referenced in signature (Lgnu/trove/map/hash/TIntIntHashMap;)
    05-14 18:41:07.686: I/dalvikvm(6795): Could not find method gnu.trove.list.array.TIntArrayList.toArray, referenced from method org.jcodec.common.io.VLCBuilder.getVLC
    05-14 18:41:07.686: W/dalvikvm(6795): VFY: unable to resolve virtual method 3525: Lgnu/trove/list/array/TIntArrayList;.toArray ()[I
    05-14 18:41:07.686: D/dalvikvm(6795): VFY: replacing opcode 0x6e at 0x0004
    05-14 18:41:07.686: I/dalvikvm(6795): Could not find method gnu.trove.list.array.TIntArrayList.add, referenced from method org.jcodec.common.io.VLCBuilder.set
    05-14 18:41:07.686: W/dalvikvm(6795): VFY: unable to resolve virtual method 3520: Lgnu/trove/list/array/TIntArrayList;.add (I)Z
    05-14 18:41:07.686: D/dalvikvm(6795): VFY: replacing opcode 0x6e at 0x0006
    05-14 18:41:07.686: D/dalvikvm(6795): DexOpt: unable to opt direct call 0x0dcc at 0x05 in Lorg/jcodec/common/io/VLCBuilder;.<init>
    05-14 18:41:07.686: D/dalvikvm(6795): DexOpt: unable to opt direct call 0x0dcc at 0x0c in Lorg/jcodec/common/io/VLCBuilder;.<init>
    05-14 18:41:07.686: D/dalvikvm(6795): DexOpt: unable to opt direct call 0x0dbf at 0x13 in Lorg/jcodec/common/io/VLCBuilder;.<init>
    05-14 18:41:07.686: D/dalvikvm(6795): DexOpt: unable to opt direct call 0x0dbf at 0x1a in Lorg/jcodec/common/io/VLCBuilder;.<init>
    05-14 18:41:07.686: D/dalvikvm(6795): DexOpt: unable to opt direct call 0x0dcc at 0x05 in Lorg/jcodec/common/io/VLCBuilder;.<init>
    05-14 18:41:07.686: D/dalvikvm(6795): DexOpt: unable to opt direct call 0x0dcc at 0x0c in Lorg/jcodec/common/io/VLCBuilder;.<init>
    05-14 18:41:07.686: D/dalvikvm(6795): DexOpt: unable to opt direct call 0x0dbf at 0x13 in Lorg/jcodec/common/io/VLCBuilder;.<init>
    05-14 18:41:07.686: D/dalvikvm(6795): DexOpt: unable to opt direct call 0x0dbf at 0x1a in Lorg/jcodec/common/io/VLCBuilder;.<init>
    05-14 18:41:07.686: W/dalvikvm(6795): Exception Ljava/lang/NoClassDefFoundError; thrown while initializing Lorg/jcodec/codecs/h264/H264Const;
    05-14 18:41:07.686: W/dalvikvm(6795): threadid=13: thread exiting with uncaught exception (group=0x410b72a0)
    05-14 18:41:07.696: E/AndroidRuntime(6795): FATAL EXCEPTION: Thread-4325
    05-14 18:41:07.696: E/AndroidRuntime(6795): java.lang.ExceptionInInitializerError
    05-14 18:41:07.696: E/AndroidRuntime(6795):     at org.jcodec.codecs.h264.io.CAVLC.codeTableChromaDC(CAVLC.java:190)
    05-14 18:41:07.696: E/AndroidRuntime(6795):     at org.jcodec.codecs.h264.io.CAVLC.<init>(CAVLC.java:40)
    05-14 18:41:07.696: E/AndroidRuntime(6795):     at org.jcodec.codecs.h264.H264Encoder.encodeSlice(H264Encoder.java:118)
    05-14 18:41:07.696: E/AndroidRuntime(6795):     at org.jcodec.codecs.h264.H264Encoder.encodeFrame(H264Encoder.java:75)
    05-14 18:41:07.696: E/AndroidRuntime(6795):     at de.recode.gopix.ConverterActivity$ConverterThread.run(ConverterActivity.java:100)
    05-14 18:41:07.696: E/AndroidRuntime(6795): Caused by: java.lang.NoClassDefFoundError: gnu.trove.map.hash.TIntIntHashMap
    05-14 18:41:07.696: E/AndroidRuntime(6795):     at org.jcodec.common.io.VLCBuilder.<init>(VLCBuilder.java:17)
    05-14 18:41:07.696: E/AndroidRuntime(6795):     at org.jcodec.codecs.h264.H264Const.<clinit>(H264Const.java:22)
    05-14 18:41:07.696: E/AndroidRuntime(6795):     ... 5 more
    

    Why is this happening?

    opened by kelunik 10
  • FrameGrab Can't detect format of MP4 after being resized with ffmpeg

    FrameGrab Can't detect format of MP4 after being resized with ffmpeg

    I'm creating a program that opens an MP4 file and returns the next frame as a BufferedImage upon request (Pretty much viewing an MP4 frame by frame).

    My code is working fine on my original test MP4 but if I resize that MP4 using ffmpeg i get a

    org.jcodec.api.UnsupportedFormatException: Could not detect the format of the input video.
    	at org.jcodec.api.FrameGrab.createFrameGrab(FrameGrab.java:55)
    

    Resize Command:

    ffmpeg -i input.mp4 -s 1280x720 output.mp4
    

    Code:

    final FileChannelWrapper wrapper = NIOUtils.readableChannel(_FILE_);
    final FrameGrab grab = FrameGrab.createFrameGrab(wrapper);
    

    I'm pretty new to jcodec and media encoding/decoding in general so I'm not sure if this is a problem with my resize command, my code, or the library.

    opened by TheStonedTurtle 8
  • Support long term reference in the h264 video decoder

    Support long term reference in the h264 video decoder

    I am getting the error of long term full stack trace

    ava.lang.RuntimeException: long term at org.jcodec.codecs.h264.decode.RefListManager.reorder(RefListManager.java:156) at org.jcodec.codecs.h264.decode.RefListManager.buildRefListP(RefListManager.java:79) at org.jcodec.codecs.h264.decode.RefListManager.getRefList(RefListManager.java:42) at org.jcodec.codecs.h264.decode.SliceDecoder.decodeFromReader(SliceDecoder.java:71) at org.jcodec.codecs.h264.H264Decoder$FrameDecoder.decodeFrame(H264Decoder.java:152) at org.jcodec.codecs.h264.H264Decoder.decodeFrameFromNals(H264Decoder.java:103)

    private void reorder(Picture[] result, int list) { if (sh.refPicReordering[list] == null) return; int predict = sh.frame_num; int maxFrames = 1 << (sh.sps.log2_max_frame_num_minus4 + 4);

        for (int ind = 0; ind < sh.refPicReordering[list][0].length; ind++) {
            switch (sh.refPicReordering[list][0][ind]) {
            case 0:
                predict = wrap(predict - sh.refPicReordering[list][1][ind] - 1, maxFrames);
                break;
            case 1:
                predict = wrap(predict + sh.refPicReordering[list][1][ind] + 1, maxFrames);
                break;
            case 2:
                throw new RuntimeException("long term");
            }
            for (int i = numRef[list] - 1; i > ind; i--)
                result[i] = result[i - 1];
            result[ind] = sRefs[predict];
            for (int i = ind + 1, j = i; i < numRef[list] && result[i] != null; i++) {
                if (result[i] != sRefs[predict])
                    result[j++] = result[i];
            }
        }
    }
    

    The above error occurs when I am using rtsp link. However, in video, the code works fine. Any help would be appreciated.

    enhancement 
    opened by dhrumil-softvan 8
  • Add sound to video

    Add sound to video

    Hi, please advise how I can add sound to a video? I want to encode from a raw PCM stream. Here is my (Stan's) demo program that makes the video without sound.

    OK, I heard AAC encoding is not supported in Java? Can I encode to something else (ogg maybe)?

    opened by stefan-reich 8
  • Odd artifacts in some exported MP4s?

    Odd artifacts in some exported MP4s?

    I have an app that allows users to create animations, it uses jcodec to export the animations as MP4.

    Many users upload their animations to Youtube, and they look fine. Others have animations that have odd artifacts like this: https://www.youtube.com/watch?v=eycOo2pfBYc

    It seems to only happen with colored backgrounds, but not exclusively - and not all the time. I've found out in one case that a user exported their MP4 at a 640x360 resolution on a Galaxy S4 phone and they experienced the same artifacts.

    I'm not sure if there's something I can do to prevent this? My actual jcodec code is taken from SequenceEncoder.java.


    Here's how I set things up:

    // Muxer that will store the encoded frames.
    _mp4Muxer = mp4Muxer;
    
    // Add a video track to the muxer.
    _fps = fps;
    _outTrack = _mp4Muxer.addTrack(TrackType.VIDEO, _fps);
    
    // Allocate a buffer big enough to hold output frames.
    _encodedByteBuffer = ByteBuffer.allocate(exportWidth * exportHeight * 3);
    
    // Create an instance of an encoder.
    _encoder = new H264Encoder(new H264FixedRateControl(512)); // Was 256, apparently 512 is much better quality.
    
    // Transform to convert between RGB and YUV.
    _transform = ColorUtil.getTransform8Bit(ColorSpace.RGB, _encoder.getSupportedColorSpaces()[0]);
    
    // Extra data of encoder (SPS, PPS) to be stored in a special place of MP4.
    _spsList = new ArrayList<ByteBuffer>();
    _ppsList = new ArrayList<ByteBuffer>();
    
    // Picture that will hold pixels and be encoded.
    _pictureRGB = Picture8Bit.create(exportWidth, exportHeight, ColorSpace.RGB);
    _pictureToEncode = Picture8Bit.create(exportWidth, exportHeight, _encoder.getSupportedColorSpaces()[0]);
    
    // Start this Thread.
    _lockObject = new Object();
    start();
    

    And here's the encoding part, it's inside the run() function of a Thread and receives new rgba pixels constantly until encoding is done:

    @Override
    public void run()
    {
        // Runs until dispose() is called.
        while (_isAlive == true)
        {
            // Don't do any of this if finish() or cancel() was already called.
            if (_isFinishedEncoding == false && _isCancelled == false)
            {
                synchronized (_lockObject)
                {
                    // If _rgbaPixels isn't null, encode the pixels.    
                    if (_rgbaPixels != null)
                    {
                        // Put pixels into an RGB picture.
                        byte[] dstData = _pictureRGB.getPlaneData(0);
    
                        int offset = 0;
                        for (int k = 0, length = dstData.length; k < length; k += 3)
                        {
                            dstData[k] = (byte)(_rgbaPixels[offset++] - 128);
                            dstData[k + 1] = (byte)(_rgbaPixels[offset++] - 128);
                            dstData[k + 2] = (byte)(_rgbaPixels[offset++] - 128);
                            offset++;
                        }                           
    
                        // Perform conversion.
                        _transform.transform(_pictureRGB, _pictureToEncode);
    
                        // Encode image into H.264 frame, the result is stored in _encodedByteBuffer.
                        _encodedByteBuffer.clear();
                        ByteBuffer result = _encoder.encodeFrame(_pictureToEncode, _encodedByteBuffer);
    
                        // Based on the frame above, form the correct MP4 packet.
                        _spsList.clear();
                        _ppsList.clear();
                        H264Utils.wipePS(_encodedByteBuffer, _spsList, _ppsList);
                        H264Utils.encodeMOVPacket(result);
    
                        // We presume there will be only one SPS/PPS pair for now
                        if (_sps == null && _spsList.size() != 0)
                            _sps = _spsList.get(0);
                        if (_pps == null && _ppsList.size() != 0)
                            _pps = _ppsList.get(0);
    
                        try
                        {
                            _outTrack.addFrame(new MP4Packet(result, _encodedFrameIndex, _fps, 1, _encodedFrameIndex, false, null, _encodedFrameIndex, 0));
                        }
                        catch (IOException e)
                        {}
    
                        _encodedFrameIndex++;
    
                        // Prepare for more pixels.
                        _rgbaPixels = null;
                        _isReadyForMorePixels = true;
                        _isProcessingPixels = false;
                    }
                }
            }
        }
    
        // Can only be reached after dispose() is called.
        _transform = null;
        _outTrack = null;
        _encoder = null;
        _mp4Muxer = null;
        _pictureRGB = null;
        _pictureToEncode = null;
        _encodedByteBuffer = null;
        _spsList = null;
        _ppsList = null;
        _rgbaPixels = null;
        _lockObject = null;
    }
    

    Wondering if this is something I can fix on my end, or perhaps an issue within the jcodec library?

    opened by rdamiano 7
  • Does jcodec properly write a moov atom for MP4 files?

    Does jcodec properly write a moov atom for MP4 files?

    I have an MP4 file that I'm encoding through the same code from SequenceEncoder.java (including the call to MP4Muxer.writeHeader() at the end).

    This file does successfully play in VLC and the default Android video player. However, upon trying to open the file in FFmpeg for some further modifications, it crashes with an error message of:

    Invalid data found when processing input moov atom not found

    Is this something that can be modified in code?

    Unrelated: If I understand correctly, jcodec uses the "sequence paramater set extension" for its NAL type. I was wondering if this was also something I could change via code? I'm running the MP4 generated via jcodec through another library that doesn't support this NAL type.

    opened by rdamiano 7
  • jcodec-javase not on Maven Central

    jcodec-javase not on Maven Central

    The main jcodec artifact for version 0.1.9 can be found on Maven Central, but not jcodec-javase

    Repo contents: http://repo1.maven.org/maven2/org/jcodec/

    I believe both artifacts should be available on Maven Central.

    opened by mikera 7
  • Latest Git Tag not pushed

    Latest Git Tag not pushed

    It seems like version 0.2.5 is without a git tag: https://github.com/jcodec/jcodec/releases

    Would it be possible to push one? I'm legally required to list license files with version pinned links

    opened by vanniktech 0
  • SnapdragonGallery  NoSuchMethodException

    SnapdragonGallery NoSuchMethodException

    When I use Snapdragon Gallery to read my MP4 file, an exception occurs: "java.lang.RuntimeException: java.lang.NoSuchMethodException: org.jcodec.containers.mp4.boxes.FileTypeBox.< init> [class org.jcodec.containers.mp4.boxes.Header]"

    This problem does not occur in my own apk and SnapdragonCamera;

    The error code is this sentence: "MetadataEditor metadataEditor = MetadataEditor.createFrom(file);"

    opened by Ace122 0
  • H264Decoder's ThreadPoolExecutor is never closed / threads are never killed

    H264Decoder's ThreadPoolExecutor is never closed / threads are never killed

    I am using jcodec in one of my research projects which consists in running your test cases multiple times in a single VM.

    During my experimentation, my Linux system started throwing an exception due to overload of native threads (java.lang.OutOfMemoryError : unable to create new native Thread). Upon further inspection, I found out that one specific test case created multiple threads and those were never killed, even after all task were completed and no more tasks were submitted. The test case is: org.jcodec.api.Issue180Test#testFrameGrabDoesNotThrowException().

    Apparently, this piece of code submits a few tasks to a thread pool, but such thread pool is never shutdown. This means that the thread pool will keep the threads (even when not used anymore) active forever (i.e., until the JVM stops).

    I see that this is not a common scenario (a user executing your test cases), but there might be occasions in which someone might instantiate the decoder to decode a given video, perform the task, and then their JVM will hold such resources active thereafter.

    A way to avoid such a thing is to use the ThreadPoolExecutor's method shutdown() to await the current tasks to finish and deactivate the threads, consequently releasing the resources.

    I am bringing this up because maybe someone might face the same issue that I am facing right now. I am not sure I can call this a bug, but rather a performance issue depending on how you use the API. By the way, my solution was to deactivate the test case for now.

    opened by GiovaniGuizzo 0
  • MKVDemuxer.AudioTrack.nextFrame() returns null prematurely

    MKVDemuxer.AudioTrack.nextFrame() returns null prematurely

    https://github.com/jcodec/jcodec/blob/784d4be2f49c2897d1aa472447a0b389ee046a44/src/main/java/org/jcodec/containers/mkv/demuxer/MKVDemuxer.java#L493

    I'm working with an EAC-3 stream and it seems there are 8 packets in each block. I couldn't access all of the packets until I changed the above condition to:

    if (frameIdx >= framesCount || blockIdx >= blocks.size())

    opened by Intrepd 0
  • Bump protobuf-java from 3.11.0 to 3.16.3 in /samples

    Bump protobuf-java from 3.11.0 to 3.16.3 in /samples

    Bumps protobuf-java from 3.11.0 to 3.16.3.

    Release notes

    Sourced from protobuf-java's releases.

    Protobuf Release v3.16.3

    Java

    • Refactoring java full runtime to reuse sub-message builders and prepare to migrate parsing logic from parse constructor to builder.
    • Move proto wireformat parsing functionality from the private "parsing constructor" to the Builder class.
    • Change the Lite runtime to prefer merging from the wireformat into mutable messages rather than building up a new immutable object before merging. This way results in fewer allocations and copy operations.
    • Make message-type extensions merge from wire-format instead of building up instances and merging afterwards. This has much better performance.
    • Fix TextFormat parser to build up recurring (but supposedly not repeated) sub-messages directly from text rather than building a new sub-message and merging the fully formed message into the existing field.
    • This release addresses a Security Advisory for Java users

    Protocol Buffers v3.16.1

    Java

    • Improve performance characteristics of UnknownFieldSet parsing (#9371)

    Protocol Buffers v3.16.0

    C++

    • Fix compiler warnings issue found in conformance_test_runner #8189 (#8190)
    • Fix MinGW-w64 build issues. (#8286)
    • [Protoc] C++ Resolved an issue where NO_DESTROY and CONSTINIT are in incorrect order (#8296)
    • Fix PROTOBUF_CONSTINIT macro redefinition (#8323)
    • Delete StringPiecePod (#8353)
    • Fix gcc error: comparison of unsigned expression in '>= 0' is always … (#8309)
    • Fix cmake install on iOS (#8301)
    • Create a CMake option to control whether or not RTTI is enabled (#8347)
    • Fix endian.h location on FreeBSD (#8351)
    • Refactor util::Status (#8354)
    • Make util::Status more similar to absl::Status (#8405)
    • Fix -Wsuggest-destructor-override for generated C++ proto classes. (#8408)
    • Refactor StatusOr and StringPiece (#8406)
    • Refactor uint128 (#8416)
    • The ::pb namespace is no longer exposed due to conflicts.
    • Allow MessageDifferencer::TreatAsSet() (and friends) to override previous calls instead of crashing.
    • Reduce the size of generated proto headers for protos with string or bytes fields.
    • Move arena() operation on uncommon path to out-of-line routine
    • For iterator-pair function parameter types, take both iterators by value.
    • Code-space savings and perhaps some modest performance improvements in RepeatedPtrField.
    • Eliminate nullptr check from every tag parse.
    • Remove unused _$name$cached_byte_size fields.
    • Serialize extension ranges together when not broken by a proto field in the middle.
    • Do out-of-line allocation and deallocation of string object in ArenaString.

    ... (truncated)

    Commits
    • b8c2488 Updating version.json and repo version numbers to: 16.3
    • 42e47e5 Refactoring Java parsing (3.16.x) (#10668)
    • 98884a8 Merge pull request #10556 from deannagarcia/3.16.x
    • 450b648 Cherrypick ruby fixes for monterey
    • b17bb39 Merge pull request #10548 from protocolbuffers/3.16.x-202209131829
    • c18f5e7 Updating changelog
    • 6f4e817 Updating version.json and repo version numbers to: 16.2
    • a7d4e94 Merge pull request #10547 from deannagarcia/3.16.x
    • 55815e4 Apply patch
    • 152d7bf Update version.json with "lts": true (#10535)
    • Additional commits viewable in compare view

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    • @dependabot use these labels will set the current labels as the default for future PRs for this repo and language
    • @dependabot use these reviewers will set the current reviewers as the default for future PRs for this repo and language
    • @dependabot use these assignees will set the current assignees as the default for future PRs for this repo and language
    • @dependabot use this milestone will set the current milestone as the default for future PRs for this repo and language

    You can disable automated security fix PRs for this repo from the Security Alerts page.

    dependencies 
    opened by dependabot[bot] 0
  • SequenceEncoder is getting error in it

    SequenceEncoder is getting error in it

    'SequenceEncoder(org.jcodec.common.io.SeekableByteChannel, org.jcodec.common.model.Rational, org.jcodec.common.Format, org.jcodec.common.Codec, org.jcodec.common.Codec)' in 'org.jcodec.api.SequenceEncoder' cannot be applied to '(java.io.File)' error getting in this line SequenceEncoder encoder = new SequenceEncoder(new File(dir, "video.mp4")); please help to get out out of this and other error is encoder.encodeImage(frame);

    Cannot resolve method 'encodeImage' in SequenceEncoder

    method is missing how can i fix it could you please help me sir

    opened by Jagan3534 0
Releases(v0.2.3)
Owner
null
Button which is visible while user holds it. Main use case is controlling audio recording state (like in Telegram, Viber, VK).

HoldingButton Button which is visible while user holds it. Main use case is controlling audio recording state (like in Telegram, Viber, VK). Getting s

Artem Hluhovskyi 594 Jan 1, 2023
Maxibon kata for Kotlin Developers. The main goal is to practice property based testing.

Kata Maxibon for Kotlin. We are here to practice property based testing. We are going to use KotlinTest to write our tests. We are going to practice p

Karumi 44 Oct 3, 2022
KataContacts written in Kotlin. The main goal is to practice Clean Architecture Development

KataContacts written in Kotlin We are here to practice Clean Architecture Development. Clean Architecture is a way of structuring code. We are going t

Karumi 48 Oct 3, 2022
TODO API Client Kata for Kotlin Developers. The main goal is to practice integration testing using MockWebServer

KataTODOApiClient for Kotlin We are here to practice integration testsing using HTTP stubbing. We are going to use MockWebServer to simulate a HTTP se

Karumi 61 Nov 20, 2022
Screenshot Kata for Android Developers with Kotlin. The main goal is to practice UI Screenshot Testing.

KataScreenshot in Kotlin We are here to practice UI testing using screenshot tests for Android. We are going to use Espresso to interact with the Appl

Karumi 76 Nov 20, 2022
Super Heroes Kata for Android Developers in Kotlin. The main goal is to practice UI Testing.

KataSuperHeroes in Kotlin We are here to practice UI Testing. We are going to use Espresso to interact with the Application UI. We are going to use Ko

Karumi 86 Nov 20, 2022
Maxibon kata for Kotlin Developers. The main goal is to practice property based testing.

Kata Maxibon for Kotlin. We are here to practice property based testing. We are going to use KotlinTest to write our tests. We are going to practice p

Karumi 44 Oct 3, 2022
KataContacts written in Kotlin. The main goal is to practice Clean Architecture Development

KataContacts written in Kotlin We are here to practice Clean Architecture Development. Clean Architecture is a way of structuring code. We are going t

Karumi 48 Oct 3, 2022
TODO API Client Kata for Kotlin Developers. The main goal is to practice integration testing using MockWebServer

KataTODOApiClient for Kotlin We are here to practice integration testsing using HTTP stubbing. We are going to use MockWebServer to simulate a HTTP se

Karumi 61 Nov 20, 2022
Screenshot Kata for Android Developers with Kotlin. The main goal is to practice UI Screenshot Testing.

KataScreenshot in Kotlin We are here to practice UI testing using screenshot tests for Android. We are going to use Espresso to interact with the Appl

Karumi 76 Nov 20, 2022
Super Heroes Kata for Android Developers in Kotlin. The main goal is to practice UI Testing.

KataSuperHeroes in Kotlin We are here to practice UI Testing. We are going to use Espresso to interact with the Application UI. We are going to use Ko

Karumi 86 Nov 20, 2022
GoolgePlusLayout is a custom layout that plays animation on the children views while scrolling as the layout in the Google Plus (android) main page

Google Plus Layout Google Plus Layout is a custom layout that support playing animation on child view(s) in a serialize manner like the the main

Ahmed Nammari 224 Nov 25, 2022
Integration Testing Kotlin Multiplatform Kata for Kotlin Developers. The main goal is to practice integration testing using Ktor and Ktor Client Mock

This kata is a Kotlin multiplatform version of the kata KataTODOApiClientKotlin of Karumi. We are here to practice integration testing using HTTP stub

Jorge Sánchez Fernández 29 Oct 3, 2022
This is a simple application through which I connected Students, Courses and Teachers in an a beautiful way. The main purpose of this project is the connection between the Objects.

UniversityApp This is a simple application through which I connected Students, Courses and Teachers in an a beautiful way. The main purpose of this pr

Anas Khalil 3 Aug 21, 2021
App consist of 4 main fragments accessable from bottom navigation

Bug demo Demo App consist of 4 main fragments accessable from bottom navigation, each fragment is a tablayout hosting 2 more child fragment, child fra

Babish 0 Nov 21, 2021
The home of the amigo-platform which serves as the main service for the amigo multimedia platform

amigo-platform This is the home of the amigo-platform which serves as the main service for the amigo multimedia platform. Authentication with JWT Toke

null 1 Nov 22, 2021
Repositório para criar layouts e chamar na activity main e melhorar um dos pontos fracos meu (layout).

Repositório para criar layouts e chamar na activity main e melhorar um dos pontos fracos meu (layout). Não se preocupe com os tipos malucos de layouts

Murillo Alves da Silva 1 Dec 14, 2021
Main goal of this project is to find the best route from one country to another

Route-service Main goal of this project is to find the best route from one country to another. Data is presented as json format. I've implemented A* p

Teyyihan Aksu 4 Aug 2, 2022
Cryptac is a mobile application that allows you to track the main important information about your favorite cryptos

Cryptac is a mobile application that allows you to track the main important information about your favorite cryptos.

null 1 Jan 21, 2022
This app works as a simple replacement for SpongeAuth for those who want to use Discourse as the main SSO auth provider.

PowerNukkit Ore to Discourse Auth Gateway This app works as a simple replacement for SpongeAuth for those who want to use Discourse as the main SSO au

null 1 Apr 9, 2022