一个GIF生成器的实现(一)

前言

一直在做饭否的客户端,但是根据大家反映,始终都没有iOS端的好用,特别是有一个小功能——GIF动画生成。猫饭、泡饭等很多都可以轻而易举生成GIF进行分享,但是Android端却从来没有。

稍微做了一些搜索,发现iOS端有一个叫做NSGIF的库,可以很容易的生成GIF;而Android端却没有类似的库,直到我自己打算写之后,才发现是Android的锅:毕竟分化太严重,Android不能对硬件底层有太强的控制,导致视频解码部分,额,几乎不能用。

难点

GIF原理很简单,生成也并不难,重点就在于视频的截取。

从网上能够找到的若干篇文章,都是使用 Android API MediaMetadataRetriever.getFrameAtTime 打开视频,并截取图像,最终拼接成GIF。

但是!目前不知道是Android本身的问题还是各手机(ROM)厂商的缘故,MediaMetadataRetriever 的实现均有严重的问题,在我的测试中,仅有刷了LineageOS的小米5该接口是正常的,其他手机都出现只能截取到固定的(相同的)关键帧的问题。

流程

  1. 截取图片
  2. 生成色板
  3. 写入GIF

准备FFmpeg

根据上面所说,我准备上牛刀。从FFmpeg官网下载回来最新版本的源代码,或者直接

git clone https://git.ffmpeg.org/ffmpeg.git ffmpeg

也可以,不过记得checkout到正确的版本(都是泪

为 Android 编译

FFmpeg 号称十万编译选项,要正确的编译确实不太容易,首先需要准备以下工具(Mac为例)

  • XCode
  • NDK
  • Homebrew

Xcode首先最好是最新版,因为这里只使用命令行;NDK推荐下载独立版而不是从 Android Studio 的 SDK Manager 中下载。FFmpeg交叉编译文章很多,这里只简单提一下过程。

安装依赖项

brew install automake fdk-aac git lame libass libtool libvorbis libvpx \
opus sdl shtool texi2html theora wget x264 x265 xvid nasm

修改 configure

FFmpeg根目录下的 configure 中

SLIBNAME_WITH_VERSION='$(SLIBNAME).$(LIBVERSION)'
SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'

修改为 Android 中习惯命名方式

SLIBNAME_WITH_VERSION='$(SLIBNAME).$(LIBVERSION)'
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'

编(chao)写编译脚本

有若干Github项目可以直接查看

编译之前脚本主要修改几个部分

  • NDK地址,修改为你本地的NDK位置
  • prefix,最终生成库的位置
  • sysroot,主要是其中Android的版本,建议用16或19
  • 其他编译选项,FFmpeg编译选项太多,要么使用脚本中的,要么使用默认,或者对FFmpeg足够熟悉,选择自己需要的内容编译

编译

这个过程建议你把脚本中的make改成make -j8,让编译系统多进程去编译,特别是如果你一次性比编译多个平台,你可以出去吃个饭,睡一觉什么的…

差不多一个小时之后,就可以看到下面这幅可爱的场面了。

可爱的画面

(未完待续……)