Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to encode images into a video file in Java through programming?

I am trying to encode some images of same resolution into a video file using, For that I have tried:

jCodec

  • jcodec..example description

    But it is very time consuming and not a proper tool to encode large number of images and it creates a quick time extension.

FFMPEG

  • FFMPEG..example description

    But ffmpeg only able to create video from image files. Images need to be create on physical system.

I have heard Xuggler that its APIs can be used in java program to create video file but as its site seems broken. I am unable to try it.

Does anybody know how to encode images in java format into a video file Please help!

THanks in Advance !

like image 208
ANUJ SINGH Avatar asked Jun 22 '16 09:06

ANUJ SINGH


2 Answers

Xuggler is deprecated, use Humble-Video instead. It already comes with some demo projects, including how to take screenshots and convert it to a video file: RecordAndEncodeVideo.java

/*******************************************************************************
 * Copyright (c) 2014, Art Clarke.  All rights reserved.
 * <p>
 * This file is part of Humble-Video.
 * <p>
 * Humble-Video is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * <p>
 * Humble-Video is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 * <p>
 * You should have received a copy of the GNU Affero General Public License
 * along with Humble-Video.  If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************/
package io.humble.video.demos;

import io.humble.video.*;
import io.humble.video.awt.MediaPictureConverter;
import io.humble.video.awt.MediaPictureConverterFactory;
import org.apache.commons.cli.*;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;

/**
 * Records the contents of your computer screen to a media file for the passed in duration.
 * This is meant as a demonstration program to teach the use of the Humble API.
 * <p>
 * Concepts introduced:
 * </p>
 * <ul>
 * <li>Muxer: A {@link Muxer} object is a container you can write media data to.</li>
 * <li>Encoders: An {@link Encoder} object lets you convert {@link MediaAudio} or {@link MediaPicture} objects into {@link MediaPacket} objects
 * so they can be written to {@link Muxer} objects.</li>
 * </ul>
 *
 * <p>
 * To run from maven, do:
 * </p>
 * <pre>
 * mvn install exec:java -Dexec.mainClass="io.humble.video.demos.RecordAndEncodeVideo" -Dexec.args="filename.mp4"
 * </pre>
 *
 * @author aclarke
 *
 */
public class RecordAndEncodeVideo
{
    /**
     * Records the screen
     */
    private static void recordScreen (String filename, String formatname, String codecname, int duration, int snapsPerSecond) throws AWTException, InterruptedException, IOException
    {
        /**
         * Set up the AWT infrastructure to take screenshots of the desktop.
         */
        final Robot robot = new Robot();
        final Toolkit toolkit = Toolkit.getDefaultToolkit();
        final Rectangle screenbounds = new Rectangle(toolkit.getScreenSize());

        final Rational framerate = Rational.make(1, snapsPerSecond);

        /** First we create a muxer using the passed in filename and formatname if given. */
        final Muxer muxer = Muxer.make(filename, null, formatname);

        /** Now, we need to decide what type of codec to use to encode video. Muxers
         * have limited sets of codecs they can use. We're going to pick the first one that
         * works, or if the user supplied a codec name, we're going to force-fit that
         * in instead.
         */
        final MuxerFormat format = muxer.getFormat();
        final Codec codec;
        if (codecname != null)
        {
            codec = Codec.findEncodingCodecByName(codecname);
        }
        else
        {
            codec = Codec.findEncodingCodec(format.getDefaultVideoCodecId());
        }

        /**
         * Now that we know what codec, we need to create an encoder
         */
        Encoder encoder = Encoder.make(codec);

        /**
         * Video encoders need to know at a minimum:
         *   width
         *   height
         *   pixel format
         * Some also need to know frame-rate (older codecs that had a fixed rate at which video files could
         * be written needed this). There are many other options you can set on an encoder, but we're
         * going to keep it simpler here.
         */
        encoder.setWidth(screenbounds.width);
        encoder.setHeight(screenbounds.height);
        // We are going to use 420P as the format because that's what most video formats these days use
        final PixelFormat.Type pixelformat = PixelFormat.Type.PIX_FMT_YUV420P;
        encoder.setPixelFormat(pixelformat);
        encoder.setTimeBase(framerate);

        /** An annoynace of some formats is that they need global (rather than per-stream) headers,
         * and in that case you have to tell the encoder. And since Encoders are decoupled from
         * Muxers, there is no easy way to know this beyond
         */
        if (format.getFlag(MuxerFormat.Flag.GLOBAL_HEADER))
        {
            encoder.setFlag(Encoder.Flag.FLAG_GLOBAL_HEADER, true);
        }

        /** Open the encoder. */
        encoder.open(null, null);


        /** Add this stream to the muxer. */
        muxer.addNewStream(encoder);

        /** And open the muxer for business. */
        muxer.open(null, null);

        /** Next, we need to make sure we have the right MediaPicture format objects
         * to encode data with. Java (and most on-screen graphics programs) use some
         * variant of Red-Green-Blue image encoding (a.k.a. RGB or BGR). Most video
         * codecs use some variant of YCrCb formatting. So we're going to have to
         * convert. To do that, we'll introduce a MediaPictureConverter object later. object.
         */
        MediaPictureConverter converter = null;
        final MediaPicture picture = MediaPicture.make(encoder.getWidth(), encoder.getHeight(), pixelformat);
        picture.setTimeBase(framerate);

        /** Now begin our main loop of taking screen snaps.
         * We're going to encode and then write out any resulting packets. */
        final MediaPacket packet = MediaPacket.make();
        for (int i = 0; i < duration / framerate.getDouble(); i++)
        {
            /** Make the screen capture && convert image to TYPE_3BYTE_BGR */
            final BufferedImage screen = convertToType(robot.createScreenCapture(screenbounds), BufferedImage.TYPE_3BYTE_BGR);

            /** This is LIKELY not in YUV420P format, so we're going to convert it using some handy utilities. */
            if (converter == null)
            {
                converter = MediaPictureConverterFactory.createConverter(screen, picture);
            }
            converter.toPicture(picture, screen, i);

            do
            {
                encoder.encode(packet, picture);
                if (packet.isComplete())
                {
                    muxer.write(packet, false);
                }
            } while (packet.isComplete());

            /** now we'll sleep until it's time to take the next snapshot. */
            Thread.sleep((long) (1000 * framerate.getDouble()));
        }

        /** Encoders, like decoders, sometimes cache pictures so it can do the right key-frame optimizations.
         * So, they need to be flushed as well. As with the decoders, the convention is to pass in a null
         * input until the output is not complete.
         */
        do
        {
            encoder.encode(packet, null);
            if (packet.isComplete())
            {
                muxer.write(packet, false);
            }
        } while (packet.isComplete());

        /** Finally, let's clean up after ourselves. */
        muxer.close();
    }

    @SuppressWarnings("static-access")
    public static void main (String[] args) throws InterruptedException, IOException, AWTException
    {
        final Options options = new Options();
        options.addOption("h", "help", false, "displays help");
        options.addOption("v", "version", false, "version of this library");
        options.addOption(OptionBuilder.withArgName("format").withLongOpt("format").hasArg().
                withDescription("muxer format to use. If unspecified, we will guess from filename").create("f"));
        options.addOption(OptionBuilder.withArgName("codec")
                .withLongOpt("codec")
                .hasArg()
                .withDescription("codec to use when encoding video; If unspecified, we will guess from format")
                .create("c"));
        options.addOption(OptionBuilder.withArgName("duration")
                .withLongOpt("duration")
                .hasArg()
                .withDescription("number of seconds of screenshot to record; defaults to 10.")
                .create("d"));
        options.addOption(OptionBuilder.withArgName("snaps per second")
                .withLongOpt("snaps")
                .hasArg()
                .withDescription("number of pictures to take per second (i.e. the frame rate); defaults to 5")
                .create("s"));

        final CommandLineParser parser = new org.apache.commons.cli.BasicParser();
        try
        {
            final CommandLine cmd = parser.parse(options, args);
            final String[] parsedArgs = cmd.getArgs();
            if (cmd.hasOption("version"))
            {
                // let's find what version of the library we're running
                final String version = io.humble.video_native.Version.getVersionInfo();
                System.out.println("Humble Version: " + version);
            }
            else if (cmd.hasOption("help") || parsedArgs.length != 1)
            {
                final HelpFormatter formatter = new HelpFormatter();
                formatter.printHelp(RecordAndEncodeVideo.class.getCanonicalName() + " <filename>", options);
            }
            else
            {
                /**
                 * Read in some option values and their defaults.
                 */
                final int duration = Integer.parseInt(cmd.getOptionValue("duration", "10"));
                if (duration <= 0)
                {
                    throw new IllegalArgumentException("duration must be > 0");
                }
                final int snaps = Integer.parseInt(cmd.getOptionValue("snaps", "5"));
                if (snaps <= 0)
                {
                    throw new IllegalArgumentException("snaps must be > 0");
                }
                final String codecname = cmd.getOptionValue("codec");
                final String formatname = cmd.getOptionValue("format");
                final String filename = cmd.getArgs()[0];

                recordScreen(filename, formatname, codecname, duration, snaps);
            }
        } catch (ParseException e)
        {
            System.err.println("Exception parsing command line: " + e.getLocalizedMessage());
        }
    }

    /**
     * Convert a {@link BufferedImage} of any type, to {@link BufferedImage} of a
     * specified type. If the source image is the same type as the target type,
     * then original image is returned, otherwise new image of the correct type is
     * created and the content of the source image is copied into the new image.
     *
     * @param sourceImage
     *          the image to be converted
     * @param targetType
     *          the desired BufferedImage type
     *
     * @return a BufferedImage of the specifed target type.
     *
     * @see BufferedImage
     */
    public static BufferedImage convertToType (BufferedImage sourceImage, int targetType)
    {
        BufferedImage image;

        // if the source image is already the target type, return the source image

        if (sourceImage.getType() == targetType)
        {
            image = sourceImage;
        }

        // otherwise create a new image of the target type and draw the new
        // image

        else
        {
            image = new BufferedImage(sourceImage.getWidth(), sourceImage.getHeight(), targetType);
            image.getGraphics().drawImage(sourceImage, 0, 0, null);
        }

        return image;
    }
}

Check other demos too : humble-video-demos

I am using it for real time using on a webapp.

If you will gonna stream this in real time you will need a RTSP server. You can either use big frameworks like Red 5 Server, Wowza Streaming Engine or you can built your own server using Netty which has a built in RTSP codec since version 3.2.

like image 63
Onur Avatar answered Nov 02 '22 22:11

Onur


Using command line, there are various ways to convert image to video. You can use those command in java for saving. You can get those commands from the following link:

  1. Using ffmpeg to convert a set of images into a video
  2. Create a video slideshow from images

I am sharing a code snippet to solve the issue:

code to save png image from HTML5 canvas

Base64 decoder = new Base64();
byte[] pic = decoder.decodeBase64(request.getParameter("pic"));
String frameCount = request.getParameter("frame");
InputStream in = new ByteArrayInputStream(pic);
BufferedImage bImageFromConvert = ImageIO.read(in);
String outdir = "output\\"+frameCount;
//Random rand = new Random();
File file = new File(outdir);
if(file.isFile()){
    if(file.delete()){
        File writefile = new File(outdir);
        ImageIO.write(bImageFromConvert, "png", file);
    }
}

Code for creating image from video

String filePath = "D:\\temp\\some.mpg";
String outdir = "output";
File file = new File(outdir);
file.mkdirs();
Map<String, String> m = System.getenv();

/*
 * String command[] =
 * {"D:\\ffmpeg-win32-static\\bin\\ffmpeg","-i",filePath
 * ,"-r 30","-f","image2",outdir,"\\user%03d.jpg"};
 * 
 * ProcessBuilder pb = new ProcessBuilder(command); pb.start();
 */
String commands = "D:\\ffmpeg-win32-static\\bin\\ffmpeg -i " + filePath
        + " -r 30  -f image2 " + outdir + "\\image%5d.png";
Process p = Runtime.getRuntime().exec(commands);

code for creating video from image

String filePath = "output";
File fileP = new File(filePath);
String commands = "D:\\ffmpeg-win32-static\\bin\\ffmpeg -f image2 -i "
        + fileP + "\\image%5d.png " + fileP + "\\video.mp4";
System.out.println(commands);
Runtime.getRuntime().exec(commands);
System.out.println(fileP.getAbsolutePath());

Credit goes to @yashprit


Another approach for Android developers:

  1. Create a temporary folder inside the Android.
  2. Copy your images in the new folder
  3. First, rename your pictures to follow a numerical sequence. For example, img1.jpg, img2.jpg, img3.jpg,... Then you may run:
  4. Run this program programmetcally ffmpeg -f image2 -i img%d.jpg /tmp/a.mpg To run this programmatically,

Use the following code:

void convertImg_to_vid()
{
    Process chperm;
    try {
        chperm=Runtime.getRuntime().exec("su");
          DataOutputStream os = 
              new DataOutputStream(chperm.getOutputStream());

              os.writeBytes("ffmpeg -f image2 -i img%d.jpg /tmp/a.mpg\n");
              os.flush();

              chperm.waitFor();

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

Resource Link:

  1. Create a Video file from images using ffmpeg
like image 30
SkyWalker Avatar answered Nov 02 '22 23:11

SkyWalker