Android make animated video from list of images

后端 未结 2 930
天涯浪人
天涯浪人 2020-11-28 08:14

I want to make animated video from list of images by applying transition animation between two images. I found many similar questions on SO like,

Android Screen capt

相关标签:
2条回答
  • 2020-11-28 08:52

    Android do not support for AWT's BufferedBitmap nor AWTUtil, that is for Java SE. Currently the solution with SequenceEncoder has been integrated into jcodec's Android version. You can use it from package org.jcodec.api.SequenceEncoder.

    Here is the solution for generating MP4 file from series of Bitmaps using jcodec:

    try {
        File file = this.GetSDPathToFile("", "output.mp4");
        SequenceEncoder encoder = new SequenceEncoder(file);
    
        // only 5 frames in total
        for (int i = 1; i <= 5; i++) {
            // getting bitmap from drawable path
            int bitmapResId = this.getResources().getIdentifier("image" + i, "drawable", this.getPackageName());
            Bitmap bitmap = this.getBitmapFromResources(this.getResources(), bitmapResId);
            encoder.encodeNativeFrame(this.fromBitmap(bitmap));
        }
        encoder.finish();
    } catch (IOException e) {
        e.printStackTrace();
    }
    
    // get full SD path
    File GetSDPathToFile(String filePatho, String fileName) {
        File extBaseDir = Environment.getExternalStorageDirectory();
        if (filePatho == null || filePatho.length() == 0 || filePatho.charAt(0) != '/')
            filePatho = "/" + filePatho;
        makeDirectory(filePatho);
        File file = new File(extBaseDir.getAbsoluteFile() + filePatho);
        return new File(file.getAbsolutePath() + "/" + fileName);// file;
    }
    
    // convert from Bitmap to Picture (jcodec native structure)
    public Picture fromBitmap(Bitmap src) {
        Picture dst = Picture.create((int)src.getWidth(), (int)src.getHeight(), ColorSpace.RGB);
        fromBitmap(src, dst);
        return dst;
    }
    
    public void fromBitmap(Bitmap src, Picture dst) {
        int[] dstData = dst.getPlaneData(0);
        int[] packed = new int[src.getWidth() * src.getHeight()];
    
        src.getPixels(packed, 0, src.getWidth(), 0, 0, src.getWidth(), src.getHeight());
    
        for (int i = 0, srcOff = 0, dstOff = 0; i < src.getHeight(); i++) {
            for (int j = 0; j < src.getWidth(); j++, srcOff++, dstOff += 3) {
                int rgb = packed[srcOff];
                dstData[dstOff]     = (rgb >> 16) & 0xff;
                dstData[dstOff + 1] = (rgb >> 8) & 0xff;
                dstData[dstOff + 2] = rgb & 0xff;
            }
        }
    }
    

    In case you need to change the fps, you may customize the SequenceEncoder.

    0 讨论(0)
  • 2020-11-28 09:02

    You can use a pure java solution called JCodec ( http://jcodec.org ). Here's a CORRECTED simple class that does it using JCodec low-level API:

    public class SequenceEncoder {
        private SeekableByteChannel ch;
        private Picture toEncode;
        private RgbToYuv420 transform;
        private H264Encoder encoder;
        private ArrayList<ByteBuffer> spsList;
        private ArrayList<ByteBuffer> ppsList;
        private CompressedTrack outTrack;
        private ByteBuffer _out;
        private int frameNo;
        private MP4Muxer muxer;
    
        public SequenceEncoder(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<ByteBuffer>();
            ppsList = new ArrayList<ByteBuffer>();
    
        }
    
        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 {
            SequenceEncoder encoder = new SequenceEncoder(new File("video.mp4"));
            for (int i = 1; i < 100; i++) {
                BufferedImage bi = ImageIO.read(new File(String.format("folder/img%08d.png", i)));
                encoder.encodeImage(bi);
            }
            encoder.finish();
        }
    }
    
    0 讨论(0)
提交回复
热议问题