如何使用JCodec库从Java中的图像创建mp4视频?

 0Hey0ne 发布于 2023-01-06 15:55

我正在研究如何用Java中的图像创建mp4视频.经过几天的研究,我知道JCodec可以做到(http://jcodec.org/).这是我在Android上发现的演示,从图像列表中制作动画视频(我只更改了输入和输出链接):

private SeekableByteChannel ch;
private Picture toEncode;
private RgbToYuv420 transform;
private H264Encoder encoder;
private ArrayList spsList;
private ArrayList ppsList;
private CompressedTrack outTrack;
private ByteBuffer _out;
private int frameNo;
private MP4Muxer muxer;

public SequenceImagesEncoder(File out) throws IOException {
    this.ch = NIOUtils.writableFileChannel(out);

    // Transform to convert between RGB and YUV
    transform = new RgbToYuv420(0, 0);

    // Muxer that will store the encoded frames
    muxer = new MP4Muxer(ch, Brand.MP4);

    // Add video track to muxer
    outTrack = muxer.addTrackForCompressed(TrackType.VIDEO, 25);

    // Allocate a buffer big enough to hold output frames
    _out = ByteBuffer.allocate(1920 * 1080 * 6);

    // Create an instance of encoder
    encoder = new H264Encoder();

    // Encoder extra data ( SPS, PPS ) to be stored in a special place of
    // MP4
    spsList = new ArrayList();
    ppsList = new ArrayList();

}

public void encodeImage(BufferedImage bi) throws IOException {
    if (toEncode == null) {
        toEncode = Picture.create(bi.getWidth(), bi.getHeight(), ColorSpace.YUV420);
    }

    // Perform conversion
    for (int i = 0; i < 3; i++)
        Arrays.fill(toEncode.getData()[i], 0);
    transform.transform(AWTUtil.fromBufferedImage(bi), toEncode);

    // Encode image into H.264 frame, the result is stored in '_out' buffer
    _out.clear();
    ByteBuffer result = encoder.encodeFrame(_out, toEncode);

    // Based on the frame above form correct MP4 packet
    spsList.clear();
    ppsList.clear();
    H264Utils.encodeMOVPacket(result, spsList, ppsList);

    // Add packet to video track
    outTrack.addFrame(new MP4Packet(result, frameNo, 25, 1, frameNo, true, null, frameNo, 0));

    frameNo++;
}

public void finish() throws IOException {
    // Push saved SPS/PPS to a special storage in MP4
    outTrack.addSampleEntry(H264Utils.createMOVSampleEntry(spsList, ppsList));

    // Write MP4 header and finalize recording
    muxer.writeHeader();
    NIOUtils.closeQuietly(ch);
}

public static void main(String[] args) throws IOException {
    SequenceImagesEncoder encoder = new SequenceImagesEncoder(new File("D:/workspace/JCodecMakeMP4/out.mp4"));
    for (int i = 1; i < 100; i++) {
        BufferedImage bi = ImageIO.read(new File(String.format("D:/workspace/JCodecMakeMP4/bin/frame" + i + ".jpeg", i)));
        encoder.encodeImage(bi);
    }
    encoder.finish();
}

当我使用jcodec-0.1.0.jar时,类NIOUtils没有函数:writableFileChannel(文件文件).

当我使用jcodec-0.1.3.jar时,一切似乎都没问题,但我调试代码,当我移动到行时它导致"Source Not Found":muxer = new MP4Muxer(ch,Brand.MP4);

有谁知道如何修理它.

先感谢您!

1 个回答
  • 这只是分享我的经验.我JpegImagesToMovie用来解决你的问题.

    有关更多参考JpegImagesToMovie.

    示例程序

    public static void makeVideo(String fileName) throws MalformedURLException {
        Vector<String> imgLst = get images path list.
    
        JpegImagesToMovie imageToMovie = new JpegImagesToMovie();
        MediaLocator oml;
        if ((oml = imageToMovie.createMediaLocator(fileName)) == null) {
            System.err.println("Cannot build media locator from: " + fileName);
            System.exit(0);
        }
        int interval = 50;
        imageToMovie.doIt(screenWidth, screenHeight, (1000 / interval), imgLst, oml);
    
    }
    

    JpegImagesToMovie.java

    /*
     * @(#)JpegImagesToMovie.java   1.3 01/03/13
     *
     * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
     *
     * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
     * modify and redistribute this software in source and binary code form,
     * provided that i) this copyright notice and license appear on all copies of
     * the software; and ii) Licensee does not utilize the software in a manner
     * which is disparaging to Sun.
     *
     * This software is provided "AS IS," without a warranty of any kind. ALL
     * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
     * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
     * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
     * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
     * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
     * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
     * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
     * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
     * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
     * POSSIBILITY OF SUCH DAMAGES.
     *
     * This software is not designed or intended for use in on-line control of
     * aircraft, air traffic, aircraft navigation or aircraft communications; or in
     * the design, construction, operation or maintenance of any nuclear
     * facility. Licensee represents and warrants that it will not use or
     * redistribute the Software for such purposes.
     */
    
    import java.awt.Dimension;
    import java.io.File;
    import java.io.IOException;
    import java.io.RandomAccessFile;
    import java.net.MalformedURLException;
    import java.util.Vector;
    
    import javax.media.Buffer;
    import javax.media.ConfigureCompleteEvent;
    import javax.media.ControllerEvent;
    import javax.media.ControllerListener;
    import javax.media.DataSink;
    import javax.media.EndOfMediaEvent;
    import javax.media.Format;
    import javax.media.Manager;
    import javax.media.MediaLocator;
    import javax.media.PrefetchCompleteEvent;
    import javax.media.Processor;
    import javax.media.RealizeCompleteEvent;
    import javax.media.ResourceUnavailableEvent;
    import javax.media.Time;
    import javax.media.control.TrackControl;
    import javax.media.datasink.DataSinkErrorEvent;
    import javax.media.datasink.DataSinkEvent;
    import javax.media.datasink.DataSinkListener;
    import javax.media.datasink.EndOfStreamEvent;
    import javax.media.format.VideoFormat;
    import javax.media.protocol.ContentDescriptor;
    import javax.media.protocol.DataSource;
    import javax.media.protocol.FileTypeDescriptor;
    import javax.media.protocol.PullBufferDataSource;
    import javax.media.protocol.PullBufferStream;
    
    /**
     * This program takes a list of JPEG image files and convert them into a
     * QuickTime movie.
     */
    public class JpegImagesToMovie implements ControllerListener, DataSinkListener {
    
        public boolean doIt(int width, int height, int frameRate, Vector inFiles,
                MediaLocator outML) throws MalformedURLException {
            ImageDataSource ids = new ImageDataSource(width, height, frameRate,
                    inFiles);
    
            Processor p;
    
            try {
                //System.err
                //      .println("- create processor for the image datasource ...");
                p = Manager.createProcessor(ids);
            } catch (Exception e) {
                System.err
                        .println("Yikes!  Cannot create a processor from the data source.");
                return false;
            }
    
            p.addControllerListener(this);
    
            // Put the Processor into configured state so we can set
            // some processing options on the processor.
            p.configure();
            if (!waitForState(p, p.Configured)) {
                System.err.println("Failed to configure the processor.");
                return false;
            }
    
            // Set the output content descriptor to QuickTime.
            p.setContentDescriptor(new ContentDescriptor(
                    FileTypeDescriptor.QUICKTIME));
    
            // Query for the processor for supported formats.
            // Then set it on the processor.
            TrackControl tcs[] = p.getTrackControls();
            Format f[] = tcs[0].getSupportedFormats();
            if (f == null || f.length <= 0) {
                System.err.println("The mux does not support the input format: "
                        + tcs[0].getFormat());
                return false;
            }
    
            tcs[0].setFormat(f[0]);
    
            //System.err.println("Setting the track format to: " + f[0]);
    
            // We are done with programming the processor. Let's just
            // realize it.
            p.realize();
            if (!waitForState(p, p.Realized)) {
                System.err.println("Failed to realize the processor.");
                return false;
            }
    
            // Now, we'll need to create a DataSink.
            DataSink dsink;
            if ((dsink = createDataSink(p, outML)) == null) {
                System.err
                        .println("Failed to create a DataSink for the given output MediaLocator: "
                                + outML);
                return false;
            }
    
            dsink.addDataSinkListener(this);
            fileDone = false;
    
            System.out.println("Generating the video : "+outML.getURL().toString());
    
            // OK, we can now start the actual transcoding.
            try {
                p.start();
                dsink.start();
            } catch (IOException e) {
                System.err.println("IO error during processing");
                return false;
            }
    
            // Wait for EndOfStream event.
            waitForFileDone();
    
            // Cleanup.
            try {
                dsink.close();
            } catch (Exception e) {
            }
            p.removeControllerListener(this);
    
            System.out.println("Video creation completed!!!!!");
            return true;
        }
    
        /**
         * Create the DataSink.
         */
        DataSink createDataSink(Processor p, MediaLocator outML) {
    
            DataSource ds;
    
            if ((ds = p.getDataOutput()) == null) {
                System.err
                        .println("Something is really wrong: the processor does not have an output DataSource");
                return null;
            }
    
            DataSink dsink;
    
            try {
                //System.err.println("- create DataSink for: " + outML);
                dsink = Manager.createDataSink(ds, outML);
                dsink.open();
            } catch (Exception e) {
                System.err.println("Cannot create the DataSink: " + e);
                return null;
            }
    
            return dsink;
        }
    
        Object waitSync = new Object();
        boolean stateTransitionOK = true;
    
        /**
         * Block until the processor has transitioned to the given state. Return
         * false if the transition failed.
         */
        boolean waitForState(Processor p, int state) {
            synchronized (waitSync) {
                try {
                    while (p.getState() < state && stateTransitionOK)
                        waitSync.wait();
                } catch (Exception e) {
                }
            }
            return stateTransitionOK;
        }
    
        /**
         * Controller Listener.
         */
        public void controllerUpdate(ControllerEvent evt) {
    
            if (evt instanceof ConfigureCompleteEvent
                    || evt instanceof RealizeCompleteEvent
                    || evt instanceof PrefetchCompleteEvent) {
                synchronized (waitSync) {
                    stateTransitionOK = true;
                    waitSync.notifyAll();
                }
            } else if (evt instanceof ResourceUnavailableEvent) {
                synchronized (waitSync) {
                    stateTransitionOK = false;
                    waitSync.notifyAll();
                }
            } else if (evt instanceof EndOfMediaEvent) {
                evt.getSourceController().stop();
                evt.getSourceController().close();
            }
        }
    
        Object waitFileSync = new Object();
        boolean fileDone = false;
        boolean fileSuccess = true;
    
        /**
         * Block until file writing is done.
         */
        boolean waitForFileDone() {
            synchronized (waitFileSync) {
                try {
                    while (!fileDone)
                        waitFileSync.wait();
                } catch (Exception e) {
                }
            }
            return fileSuccess;
        }
    
        /**
         * Event handler for the file writer.
         */
        public void dataSinkUpdate(DataSinkEvent evt) {
    
            if (evt instanceof EndOfStreamEvent) {
                synchronized (waitFileSync) {
                    fileDone = true;
                    waitFileSync.notifyAll();
                }
            } else if (evt instanceof DataSinkErrorEvent) {
                synchronized (waitFileSync) {
                    fileDone = true;
                    fileSuccess = false;
                    waitFileSync.notifyAll();
                }
            }
        }
    
        /*public static void main(String args[]) {
    
            if (args.length == 0)
                prUsage();
    
            // Parse the arguments.
            int i = 0;
            int width = -1, height = -1, frameRate = 1;
            Vector inputFiles = new Vector();
            String outputURL = null;
    
            while (i < args.length) {
    
                if (args[i].equals("-w")) {
                    i++;
                    if (i >= args.length)
                        prUsage();
                    width = new Integer(args[i]).intValue();
                } else if (args[i].equals("-h")) {
                    i++;
                    if (i >= args.length)
                        prUsage();
                    height = new Integer(args[i]).intValue();
                } else if (args[i].equals("-f")) {
                    i++;
                    if (i >= args.length)
                        prUsage();
                    frameRate = new Integer(args[i]).intValue();
                } else if (args[i].equals("-o")) {
                    i++;
                    if (i >= args.length)
                        prUsage();
                    outputURL = args[i];
                } else {
                    inputFiles.addElement(args[i]);
                }
                i++;
            }
    
            if (outputURL == null || inputFiles.size() == 0)
                prUsage();
    
            // Check for output file extension.
            if (!outputURL.endsWith(".mov") && !outputURL.endsWith(".MOV")) {
                System.err
                        .println("The output file extension should end with a .mov extension");
                prUsage();
            }
    
            if (width < 0 || height < 0) {
                System.err.println("Please specify the correct image size.");
                prUsage();
            }
    
            // Check the frame rate.
            if (frameRate < 1)
                frameRate = 1;
    
            // Generate the output media locators.
            MediaLocator oml;
    
            if ((oml = createMediaLocator(outputURL)) == null) {
                System.err.println("Cannot build media locator from: " + outputURL);
                System.exit(0);
            }
    
            JpegImagesToMovie imageToMovie = new JpegImagesToMovie();
            imageToMovie.doIt(width, height, frameRate, inputFiles, oml);
    
            System.exit(0);
        }*/
    
        static void prUsage() {
            System.err
                    .println("Usage: java JpegImagesToMovie -w <width> -h <height> -f <frame rate> -o <output URL> <input JPEG file 1> <input JPEG file 2> ...");
            System.exit(-1);
        }
    
        /**
         * Create a media locator from the given string.
         */
        static MediaLocator createMediaLocator(String url) {
    
            MediaLocator ml;
    
            if (url.indexOf(":") > 0 && (ml = new MediaLocator(url)) != null)
                return ml;
    
            if (url.startsWith(File.separator)) {
                if ((ml = new MediaLocator("file:" + url)) != null)
                    return ml;
            } else {
                String file = "file:" + System.getProperty("user.dir")
                        + File.separator + url;
                if ((ml = new MediaLocator(file)) != null)
                    return ml;
            }
    
            return null;
        }
    
        // /////////////////////////////////////////////
        //
        // Inner classes.
        // /////////////////////////////////////////////
    
        /**
         * A DataSource to read from a list of JPEG image files and turn that into a
         * stream of JMF buffers. The DataSource is not seekable or positionable.
         */
        class ImageDataSource extends PullBufferDataSource {
    
            ImageSourceStream streams[];
    
            ImageDataSource(int width, int height, int frameRate, Vector images) {
                streams = new ImageSourceStream[1];
                streams[0] = new ImageSourceStream(width, height, frameRate, images);
            }
    
            public void setLocator(MediaLocator source) {
            }
    
            public MediaLocator getLocator() {
                return null;
            }
    
            /**
             * Content type is of RAW since we are sending buffers of video frames
             * without a container format.
             */
            public String getContentType() {
                return ContentDescriptor.RAW;
            }
    
            public void connect() {
            }
    
            public void disconnect() {
            }
    
            public void start() {
            }
    
            public void stop() {
            }
    
            /**
             * Return the ImageSourceStreams.
             */
            public PullBufferStream[] getStreams() {
                return streams;
            }
    
            /**
             * We could have derived the duration from the number of frames and
             * frame rate. But for the purpose of this program, it's not necessary.
             */
            public Time getDuration() {
                return DURATION_UNKNOWN;
            }
    
            public Object[] getControls() {
                return new Object[0];
            }
    
            public Object getControl(String type) {
                return null;
            }
        }
    
        /**
         * The source stream to go along with ImageDataSource.
         */
        class ImageSourceStream implements PullBufferStream {
    
            Vector images;
            int width, height;
            VideoFormat format;
    
            int nextImage = 0; // index of the next image to be read.
            boolean ended = false;
    
            public ImageSourceStream(int width, int height, int frameRate,
                    Vector images) {
                this.width = width;
                this.height = height;
                this.images = images;
    
                format = new VideoFormat(VideoFormat.JPEG, new Dimension(width,
                        height), Format.NOT_SPECIFIED, Format.byteArray,
                        (float) frameRate);
            }
    
            /**
             * We should never need to block assuming data are read from files.
             */
            public boolean willReadBlock() {
                return false;
            }
    
            /**
             * This is called from the Processor to read a frame worth of video
             * data.
             */
            public void read(Buffer buf) throws IOException {
    
                // Check if we've finished all the frames.
                if (nextImage >= images.size()) {
                    // We are done. Set EndOfMedia.
                    //System.err.println("Done reading all images.");
                    buf.setEOM(true);
                    buf.setOffset(0);
                    buf.setLength(0);
                    ended = true;
                    return;
                }
    
                String imageFile = (String) images.elementAt(nextImage);
                nextImage++;
    
                //System.err.println("  - reading image file: " + imageFile);
    
                // Open a random access file for the next image.
                RandomAccessFile raFile;
                raFile = new RandomAccessFile(imageFile, "r");
    
                byte data[] = null;
    
                // Check the input buffer type & size.
    
                if (buf.getData() instanceof byte[])
                    data = (byte[]) buf.getData();
    
                // Check to see the given buffer is big enough for the frame.
                if (data == null || data.length < raFile.length()) {
                    data = new byte[(int) raFile.length()];
                    buf.setData(data);
                }
    
                // Read the entire JPEG image from the file.
                raFile.readFully(data, 0, (int) raFile.length());
    
                //System.err.println("    read " + raFile.length() + " bytes.");
    
                buf.setOffset(0);
                buf.setLength((int) raFile.length());
                buf.setFormat(format);
                buf.setFlags(buf.getFlags() | buf.FLAG_KEY_FRAME);
    
                // Close the random access file.
                raFile.close();
            }
    
            /**
             * Return the format of each video frame. That will be JPEG.
             */
            public Format getFormat() {
                return format;
            }
    
            public ContentDescriptor getContentDescriptor() {
                return new ContentDescriptor(ContentDescriptor.RAW);
            }
    
            public long getContentLength() {
                return 0;
            }
    
            public boolean endOfStream() {
                return ended;
            }
    
            public Object[] getControls() {
                return new Object[0];
            }
    
            public Object getControl(String type) {
                return null;
            }
        }
    }
    

    2023-01-06 15:57 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有