Closed axitasavani closed 5 years ago
@axitasavani The way you are using a LottieDrawable to draw to a canvas seems reasonable to me. Unfortunately, I can't provide generalized support here but drawing Lottie to a canvas should work the same as any other view.
Also, duplicate of https://github.com/airbnb/lottie-android/issues/731
@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
Thank you. I'll apply this solution to save my Animation into Video. @rkcoders
@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
Hi @rkcoders can you reply me what is a ffmpeg command for making video from all frame images?
@rkcoders This works only when the animation is shown on the screen, right? Do you know how to simulate that as for me it's static images still
I didn't get Bitmap Image
With images not working
we can use in flutter
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 } } } }
video save but open video to show can't play video.
is that another way to save Lottie animation in android?
i have sued the below code but video proper not save.