使用xuggler将BufferedImages与视频结合使用

时间:2014-10-12 01:24:42

标签: java xuggler

我尝试使用xuggler从BufferedImages列表制作视频。我遵循了this教程,代码完美无缺。

但是,当我将示例中的工作代码合并到我的代码中时,我得到一个RuntimeException:

  

java.lang.RuntimeException:写入数据包失败:com.xuggle.xuggler.IPacket@46575264 [complete:true; dts:67915; pts:67915; size:24201; key:false; flags:0; stream index :0;持续时间:1;位置:-1;时基:1/65535;]       在com.xuggle.mediatool.MediaWriter.writePacket(MediaWriter.java:1215)       在com.xuggle.mediatool.MediaWriter.encodeVideo(MediaWriter.java:767)       在com.xuggle.mediatool.MediaWriter.encodeVideo(MediaWriter.java:810)       在s3.S3CompositeUtil.generateVideo(S3CompositeUtil.java:247)       在s3.S3CompositeUtil.main(S3CompositeUtil.java:349)

Xuggler文档提到: 调用者必须确保IMediaData.getTimeStamp()(如果指定)始终单调递增或将引发RuntimeException。

但我不认为我在制作视频时所处的时间在任何时候都会减少。

    private void generateVideo() {
    System.out.println("Creating video...");
    String outputMoviePath = "";
    IMediaWriter writer = null;
    if (allLabels) {
        outputMoviePath = TEMP_DIR_PATH + "/" + clipName + "/" + clipName + "_" + "all_labels" + ".avi";
    }
    else {
        outputMoviePath = TEMP_DIR_PATH + "/" + clipName + "/" + clipName + "_" + labels[desiredLabelIdx] + ".avi";
    }
    writer = ToolFactory.makeWriter(outputMoviePath);
    // add one video stream, with position 0 and ID 0, and a frame rate of OUTPUT_MOVIE_FPS
    writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_MPEG4, vidWidth, vidHeight);
    long startTime = System.nanoTime();
    for (int idxF = 0; idxF < numFrames; idxF++) {
        BufferedImage currFrameImg = convertToType(videoBIHolder.get(idxF), BufferedImage.TYPE_3BYTE_BGR);
//          BufferedImage currFrameImg = videoBIHolder.get(idxF);
        System.out.println("currFrameImg type is " + currFrameImg.getType()); // debug
        // encode video to stream at 0
        long frameTimeInVideo = System.nanoTime() - startTime;
        System.out.println("Time: " + frameTimeInVideo);
        writer.encodeVideo(0, currFrameImg, frameTimeInVideo, TimeUnit.NANOSECONDS);
        // sleep for millisec. amount of time of the frame rate
        try {
            Thread.sleep((long) (1000/OUTPUT_MOVIE_FPS)); //OUTPUT_MOVIE_FPS
        }
        catch (InterruptedException e) {
            System.err.println();
        }
    }

(S3CompositeUtil.java:247)是

            writer.encodeVideo(0, currFrameImg, frameTimeInVideo, TimeUnit.NANOSECONDS);

我需要OUTPUT_MOVIE_FPS为1,但奇怪的是,当它是5时,视频创建时没有例外,当它是2时,&#34;视频&#34;只是第一帧(没有其他帧),当它是1时,我得到了这个例外。

有什么想法?我非常感谢能得到的所有帮助!

1 个答案:

答案 0 :(得分:0)

我在我的一个多媒体课程中教授Xuggler,我的一些学生在创建定格动画或制作图片幻灯片的视频时报告了类似的问题。

我们至少创造了2个甚至10个FPS。有趣的是,您很可能不会使用更多的内存来存储视频。 MPG4编码器足够聪明,只能将预测帧插入到视频文件中,这些帧基本上是空的,因为在插入全新帧之前帧中没有差异。

这种方法的唯一缺点是会导致更多的编码开销。显然,在视频流中插入任何帧都会导致CPU负载,即使最终解码器决定只能插入一个空的预测帧。

编辑:事实证明我们实际上是以25 fps的速率在视频中插入帧。具有上述所有缺点。然而,出于好奇,我现在进行了帧速率介于10 fps和25 fps之间的测试,结果发现文件大小没有显着变化。有趣的是10 fps测试文件是最大的。

静止图像的5秒全高清视频在450kb到580kb的范围内。所以我建议你只是试验一下帧速率,然后选择ffmpeg / Xuggler可以处理H264的最低值(实际上这应该是在文档的某处,但我现在找不到它。)

为了进行测试,我使用了以下代码,它再次插入相同的BufferedImage。

    final IMediaWriter writer = ToolFactory.makeWriter(outputFile);
    writer.addVideoStream(0, 0, ICodec.ID.CODEC_ID_H264, img.getSize().width, screenshot.getSize().height);
    long startTime = System.nanoTime();
    for (int i=0; i<seconds*25; i++) {
        BufferedImage bgrScreen = Tools.convertToType(img, BufferedImage.TYPE_3BYTE_BGR);
        writer.encodeVideo(0, bgrScreen, System.nanoTime() - startTime, TimeUnit.NANOSECONDS);
        try {
            Thread.sleep((long) (1000 / 25));
        } catch (InterruptedException e) {
            // ignore
        }
    }
    writer.close();

Pro-Tip :在现实世界的应用程序中,我们使用一个线程来提供图像帧和一个线程来执行编码(writer.encodeVideo),这样我们就可以确保我们确实帧速率为25 fps。在你的例子中(以及我的测试代码中),添加了Xuggler / ffmpeg的编码时间。