1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > Android录屏并利用FFmpeg转换成gif(四) 将mp4文件转换成gif文件

Android录屏并利用FFmpeg转换成gif(四) 将mp4文件转换成gif文件

时间:2023-05-01 17:38:35

相关推荐

Android录屏并利用FFmpeg转换成gif(四) 将mp4文件转换成gif文件

Android录屏并利用FFmpeg转换成gif(四)

写博客时经常会希望用一段动画来演示app的行为,目前大多数的做法是在电脑上开模拟器,然后用gif录制软件录制模拟器屏幕,对于非开发人员来讲这种方式还是比较困难的。本来我以为应该也有能直接在手机上录屏并生成gif文件这样的app,下载一个这样的APP来录gif要方便得多。结果发现目前几乎没有此类APP,我就想能不能自己写一个,然后查了查资料,感觉应该能做出来,于是就撸起袖子干起来了。总的来讲要实现这个功能可以分成两个部分(当然,如果有更好的实现方式欢迎大家提出来,谢谢!):

录屏,生成mp4文件利用ffmpeg开源软件将mp4转换成gif

第一点比较容易实现,已有现成的开源代码供参考。难点在第二点,涉及到NDK开发相关的知识,及FFmpeg的集成,这方面知识我之前从未接触过,还是比较有挑战性的。

功能虽然很简单,但要讲解起来感觉还是要费点篇幅的,所以我分成了4篇文章来介绍,分别是:

Android录屏并利用FFmpeg转换成gif(一) 录屏,讲讲怎样录屏生成mp4文件

Android录屏并利用FFmpeg转换成gif(二) 交叉编译FFmpeg源码,说说如何根据我们的需求裁剪FFmepg并编译出可在android下运行的so包

Android录屏并利用FFmpeg转换成gif(三) 在Android中使用ffmpeg命令,说说如何在Android中使用ffmpeg命令,简化C代码的编写难度

Android录屏并利用FFmpeg转换成gif(四) 将mp4文件转换成gif文件,将2、3两步生成的so文件集成到android工程中,实现将mp4文件转换成gif文件,完成最终的工程。

概述

这是最后一篇了,通过前面三篇的工作,可以说这个工程的核心技术部分的功能已经基本完成了,就差最后一个组合的环节了,所以这最后一篇就是介绍一下如何将前面的工作成果串联起来,形成一个完整的工程。

先说说整个工程的业务流程,非常简单,如下图

开始录屏后,在通知栏显示一个通知,点击这个通知可以停止录屏,然后开始转码,转码结束后打开文件浏览器显示gif文件所在位置。

一、通知栏

通知栏的目的是提供一个停止录屏的操作界面,由于录屏开始后,主界面就销毁了,录屏是在子线程中执行的,当要停止录屏的时候就没有界面给用户去停止了,于是就想到利用通知栏来提供一个入口,通知栏既不会占用屏幕也符合用户的操作习惯,所以就选择了这种方式。下面是发送通知的代码:

private void showNotification() {NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);NotificationCompat.Builder builder = new NotificationCompat.Builder(this);Intent intent = new Intent();intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setClass(this, WaitingActivity.class);PendingIntent pendingIntent = PendingIntent.getActivity(this, 99, intent, PendingIntent.FLAG_ONE_SHOT);Notification notification = builder.setContentTitle("正在录屏...").setContentText("点击可结束录屏").setWhen(System.currentTimeMillis()).setSmallIcon(R.mipmap.ic_launcher).setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)).setContentIntent(pendingIntent).build();notification.flags = Notification.FLAG_AUTO_CANCEL;manager.notify(1, notification);}

这个通知栏有两个功能,

一是点击了该通知就跳到一个Activity,在该Actitivity里会结束录屏,并执行将mp4转换成gif的命令。

二是点击了之后这条消息就消失了,不会在通知栏中看到了。

二、FFmpeg命令

在第三篇中已经生成了可调用FFmpeg命令的so包了,现在来看看怎么在代码中调用FFmpeg命令,先要加载so包,

static {System.loadLibrary("avutil-55");System.loadLibrary("avcodec-57");System.loadLibrary("avformat-57");System.loadLibrary("avdevice-57");System.loadLibrary("swresample-2");System.loadLibrary("swscale-4");System.loadLibrary("avfilter-6");System.loadLibrary("ffmpeg");}

这些so包放在jniLibs目录下

将mp4转换成gif可以用一行FFmpeg命令就可以完成,比如:

ffmpeg -i test.mp4 test.gif

不过这样转出来的gif质量很差,会有很多网格,经过一番google,我采用了这个方法 http://blog.pkh.me/p/21-high-quality-gif-with-ffmpeg.html ,转出来的gif质量明显比第一种方式好了很多。这种方式使用了两行命令。

#!/bin/shpalette="/tmp/palette.png"filters="fps=15,scale=320:-1:flags=lanczos"ffmpeg -v warning -i $1 -vf "$filters,palettegen" -y $paletteffmpeg -v warning -i $1 -i $palette -lavfi "$filters [x]; [x][1:v] paletteuse" -y $2

注意:上述两种方式转换出来的gif体积都特别大,需要调整一些参数如帧率,gif的高宽等,gif体积才会小一些,但调整后的结果我也不是很满意,转换出来的gif跟原来的mp4文件差不多大小,不知道还有没有别的什么办法来压缩一下。

由于public native int exctFFmpeg(String[] commands)方法是用数组形式传递的FFmpeg命令,因此要将每行命令拆成一个数组,即命令行中的每个关键字或参数都作为数组的一个元素。比如将命令行:

ffmpeg -v warning -i "inputfile.mp4" -vf "fps=5,scale=320:-1:flags=lanczos,palettegen" -y "palette.gpeg"

拆分成:

String[] c = new String[9];c[0] = "ffmpeg";c[1] = "-v";c[2] = "warning";c[3] = "-i";c[4] = "inputfile.mp4";c[5] = "-vf";c[6] = "fps=5,scale=320:-1:flags=lanczos,palettegen";c[7] = "-y";c[8] = "palette.gpeg";

然后调用:

exctFFmpeg(c);

这样就完成了在android中调用FFmpeg命令了。

三、打开gif所在文件夹

当转换完成后,就打开文件管理器,打开gif文件所在的文件夹,供用户享用转换的成果。

/*** 打开gif文件所在的文件夹,要求安装ES等文件浏览器,系统自带文件管理器无法导航到指定文件夹*/private void openFolder() {String path = Controller.getInstance().getFileSavedDirectory() + File.separator;Uri selectedUri = Uri.parse(path);Intent intent = new Intent(Intent.ACTION_VIEW);intent.setDataAndType(selectedUri, "resource/folder");if (intent.resolveActivityInfo(getPackageManager(), 0) != null) {// 打开ES等导航到gif文件所在的文件夹startActivity(intent);} else {// 打开系统自带文件管理器手动进入gif文件所在的文件夹intent.setAction(Intent.ACTION_GET_CONTENT);intent.setDataAndType(selectedUri, "*/*");startActivity(intent);}}

由于没有找到一个能够调用系统自带的文件管理器打开指定的文件夹的方法,只能打开文件管理器后由用户自己手动去打开特定的文件夹。

这样的话,整个工程的实现过程就介绍完了,有什么问题可以一起来讨论。

###最后,上源码:

/MingHuang1024/GifRecorder

由于水平有限,如果文中存在错误之处,请大家批评指正,欢迎大家一起来分享、探讨!

博客:/MingHuang

GitHub:/MingHuang1024

Email:minghuang1024@

微信:724360018

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。