question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How to save Lottie Animation as Video (.mp4) and GIF (.gif) in Android ?

See original GitHub issue

is that another way to save Lottie animation in android?

i have sued the below code but video proper not save.


 private static final String MIME_TYPE = "video/avc";
    private static final int WIDTH = 640;
    private static final int HEIGHT = 640;
    private static final int BIT_RATE = 40000;
    private static final int FRAMES_PER_SECOND = 1;
    private static final int IFRAME_INTERVAL = 5;

    private static final int NUM_FRAMES = 8;

    // "live" state during recording
    private MediaCodec.BufferInfo mBufferInfo;
    private MediaCodec mEncoder;
    private MediaMuxer mMuxer;
    private Surface mInputSurface;
    private int mTrackIndex;
    private boolean mMuxerStarted;
    private long mFakePts;
    LottieDrawable drawable = new LottieDrawable();

try {
            LottieTask<LottieComposition> composition = LottieCompositionFactory
                    .fromAsset(this, "text.json")
                    .addListener(new LottieListener<LottieComposition>() {
                        @Override
                        public void onResult(LottieComposition result) {
                            drawable.setComposition(result);
                        }
                    });
        } catch (Exception e) {
            Log.e(TAG, "onCreate: ", e);
        }


// Be VERY BAD and do the whole thing during onCreate().
                Log.i(TAG, "Generating movie...");
                String str = FileUtils.getSaveVideoDirPath();
                Log.i(TAG, "onClick: " + str);

                if (new File(str).exists()) {
                    Log.i(TAG, "onClick: EXISTS");
                    try {
                        File file = new File(str, "Demo_123.mp4");
                        Log.i(TAG, "onClick: " + file.getAbsolutePath());
                        generateMovie(file);
                        textUser.setText("Success");
                        Log.i(TAG, "Movie generation complete");
                    } catch (Exception ex) {
                        Log.e(TAG, "Movie generation FAILED", ex);
                        textUser.setText("Failed");
                    }
                }

private void generateMovie(File outputFile) {
        try {
            prepareEncoder(outputFile);

            for (int i = 0; i < drawable.getMaxFrame(); i++) {
                drainEncoder(false);
                Log.i(TAG, "generateMovie: " + i);
                drawable.setFrame(i);
                generateFrame(drawable);

            }

            drainEncoder(true);
        } catch (IOException ioe) {
            throw new RuntimeException(ioe);
        } finally {
            releaseEncoder();
        }
    }

    public void generateFrame(Drawable lottieDrawable) {
        drainEncoder(false);
        final Canvas canvas = mInputSurface.lockCanvas(null);
        try {
            lottieDrawable.draw(canvas);
        } finally {
            mInputSurface.unlockCanvasAndPost(canvas);
        }
    }

    /**
     * Prepares the video encoder, muxer, and an input surface.
     */
    private void prepareEncoder(File outputFile) throws IOException {
        mBufferInfo = new BufferInfo();

        MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, WIDTH, HEIGHT);

        // Set some properties.  Failing to specify some of these can cause the MediaCodec
        // configure() call to throw an unhelpful exception.
        format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
                CodecCapabilities.COLOR_FormatSurface);
        format.setInteger(MediaFormat.KEY_BIT_RATE, BIT_RATE);
        format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAMES_PER_SECOND);
        format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
        if (VERBOSE) Log.d(TAG, "format: " + format);

        // Create a MediaCodec encoder, and configure it with our format.  Get a Surface
        // we can use for input and wrap it with a class that handles the EGL work.
        mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
        mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
            mInputSurface = mEncoder.createInputSurface();
        }
        mEncoder.start();

        // Create a MediaMuxer.  We can't add the video track and start() the muxer here,
        // because our MediaFormat doesn't have the Magic Goodies.  These can only be
        // obtained from the encoder after it has started processing data.
        //
        // We're not actually interested in multiplexing audio.  We just want to convert
        // the raw H.264 elementary stream we get from MediaCodec into a .mp4 file.
        if (VERBOSE) Log.d(TAG, "output will go to " + outputFile);
        if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
            mMuxer = new MediaMuxer(outputFile.toString(),
                    OutputFormat.MUXER_OUTPUT_MPEG_4);
        }

        mTrackIndex = -1;
        mMuxerStarted = false;
    }

    /**
     * Releases encoder resources.  May be called after partial / failed initialization.
     */
    private void releaseEncoder() {
        if (VERBOSE) Log.d(TAG, "releasing encoder objects");
        if (mEncoder != null) {
            mEncoder.stop();
            mEncoder.release();
            mEncoder = null;
        }
        if (mInputSurface != null) {
            mInputSurface.release();
            mInputSurface = null;
        }
        if (mMuxer != null) {
            if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
                mMuxer.stop();
                mMuxer.release();
            }
            mMuxer = null;
        }
    }

    /**
     * Extracts all pending data from the encoder.
     * <p>
     * If endOfStream is not set, this returns when there is no more data to drain.  If it
     * is set, we send EOS to the encoder, and then iterate until we see EOS on the output.
     * Calling this with endOfStream set should be done once, right before stopping the muxer.
     */
    private void drainEncoder(boolean endOfStream) {
        final int TIMEOUT_USEC = 10000;
        if (VERBOSE) Log.d(TAG, "drainEncoder(" + endOfStream + ")");

        if (endOfStream) {
            if (VERBOSE) Log.d(TAG, "sending EOS to encoder");
            if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
                mEncoder.signalEndOfInputStream();
            }
        }

        ByteBuffer[] encoderOutputBuffers = mEncoder.getOutputBuffers();
        while (true) {
            int encoderStatus = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_USEC);
            if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) {
                // no output available yet
                if (!endOfStream) {
                    break;      // out of while
                } else {
                    if (VERBOSE) Log.d(TAG, "no output available, spinning to await EOS");
                }
            } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                // not expected for an encoder
                encoderOutputBuffers = mEncoder.getOutputBuffers();
            } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                // should happen before receiving buffers, and should only happen once
                if (mMuxerStarted) {
                    throw new RuntimeException("format changed twice");
                }
                MediaFormat newFormat = mEncoder.getOutputFormat();
                Log.d(TAG, "encoder output format changed: " + newFormat);

                // now that we have the Magic Goodies, start the muxer
                if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
                    mTrackIndex = mMuxer.addTrack(newFormat);
                    mMuxer.start();
                }

                mMuxerStarted = true;
            } else if (encoderStatus < 0) {
                Log.w(TAG, "unexpected result from encoder.dequeueOutputBuffer: " +
                        encoderStatus);
                // let's ignore it
            } else {
                ByteBuffer encodedData = encoderOutputBuffers[encoderStatus];
                if (encodedData == null) {
                    throw new RuntimeException("encoderOutputBuffer " + encoderStatus +
                            " was null");
                }

                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                    // The codec config data was pulled out and fed to the muxer when we got
                    // the INFO_OUTPUT_FORMAT_CHANGED status.  Ignore it.
                    if (VERBOSE) Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");
                    mBufferInfo.size = 0;
                }

                if (mBufferInfo.size != 0) {
                    if (!mMuxerStarted) {
                        throw new RuntimeException("muxer hasn't started");
                    }

                    // adjust the ByteBuffer values to match BufferInfo
                    encodedData.position(mBufferInfo.offset);
                    encodedData.limit(mBufferInfo.offset + mBufferInfo.size);
                    mBufferInfo.presentationTimeUs = mFakePts;
                    mFakePts += 1000000L / FRAMES_PER_SECOND;

                    if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR2) {
                        mMuxer.writeSampleData(mTrackIndex, encodedData, mBufferInfo);
                    }
                    if (VERBOSE) Log.d(TAG, "sent " + mBufferInfo.size + " bytes to muxer");
                }

                mEncoder.releaseOutputBuffer(encoderStatus, false);

                if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                    if (!endOfStream) {
                        Log.w(TAG, "reached end of stream unexpectedly");
                    } else {
                        if (VERBOSE) Log.d(TAG, "end of stream reached");
                    }
                    break;      // out of while
                }
            }
        }
    }

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:1
  • Comments:9

github_iconTop GitHub Comments

3reactions
rkcoderscommented, Jul 1, 2019

@axitasavani @gpeal hello i found solution of lottie animation convert to mp4 step 1: load animation in LottieAnimationView Step 2 : get duration of LottieAnimationView after set resorce Example: LottieAnimationView.getDuration(); step 3: LottieAnimationView.setDrawingCacheEnabled(true); step 4: use value animator and put duration of animation

Example: i = 0; ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f).setDuration(duration); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { Log.e(“progress”, “progress”); animationView.setProgress((Float) animation.getAnimatedValue()); //here you can get bitmap of every time; Bitmap bitmap = animationView.getDrawingCache(); StaticUtils.saveBitmap(MainActivity.this, bitmap, i); i++; } }); animator.start(); animator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) {

        }

        @Override
        public void onAnimationEnd(Animator animation) {
            new LongOperation().execute();
        }

        @Override
        public void onAnimationCancel(Animator animation) {

        }

        @Override
        public void onAnimationRepeat(Animator animation) {

        }
    });

step 5:you can see in above example onAnimationUpdate method using getDrawingCache method get bitmap and save in one folder

step 6:now you have multiple images there you can just make video from image using ffmpeg and generate mp4 file

thank you

0reactions
KevinShingalacommented, Jun 15, 2022

we can use in flutter

Read more comments on GitHub >

github_iconTop Results From Across the Web

How to save/export a lottie animation as a video file
Here's a list of what I've tried: How to save Lottie Animation as Video (.mp4) and GIF (.gif) in Android? I tried with...
Read more >
How to generate videos using Lottie in Android - Medium
This class is quite simple, and gets to be something like: First of all we should scale manually our Lottie animation so it...
Read more >
How to add Lottie Animation in an Android app - GeeksforGeeks
Approach: Step 1: Add this dependency into the App level gradle module of the project and then sync the gradle with the project....
Read more >
Convert Lottie to GIF - LottieFiles
Convert your Lottie Animations to GIF ... LottieFiles is by Design Barn Inc. ... We care about your data, and we'd love to...
Read more >
Videos/GIF/Lottie - Anima Documentation
How To Embed Videos / GIFs / Lottie · Select the layer that will become the video, GIF, Lottie animation · Under the...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found