目的
编写 bash 脚本,实现一行命令得到 Android 手机录制屏幕 gif 动图文件.
博主使用 ubuntu 系统,shell 为 bash. 这个脚本也可以用在 mac 系统上.
听说 windows 系统出了 ubuntu on windows,不知道能不能使用这个脚本.
原理
adb shell screenrecord
Android 4.4 版本后系统内预置了一个 screenrecord 命令,可以用来将屏幕录制为 MP4 格式. 具体命令格式可以通过 --help 参数查看:
$ adb shell screenrecord --help Usage: screenrecord [options] <filename> Android screenrecord v1.2. Records the device's display to a .mp4 file. Options: --size WIDTHxHEIGHT Set the video size,e.g. "1280x720". Default is the device's main display resolution (if supported),1280x720 if not. For best results,use a size supported by the AVC encoder. --bit-rate RATE Set the video bit rate,in bits per second. Value may be specified as bits or megabits,e.g. '4000000' is equivalent to '4M'. Default 4Mbps. --bugreport Add additional information,such as a timestamp overlay,that is helpful in videos captured to illustrate bugs. --time-limit TIME Set the maximum recording time,in seconds. Default / maximum is 180. --verbose Display interesting information on stdout. --help Show this message. Recording continues until Ctrl-C is hit or the time limit is reached.
举例:
adb shell screenrecord --size "360x640" --bit-rate 2000000 /sdcard/android_screenrecord_test.mp4
上面的命令将把所连接的手机屏幕录制为 宽高 360x640,比特率 2M 的视频,保存为手机 sd 卡根目录下的 android_screenrecord_test.mp4 文件.
该命令会持续录制,直到用 ctrl-c 终止命令,那么录制也就结束了.
也可以用 --time-limit TIME 参数来预先指定录制时长,到时间会自动结束. 默认的最大时长为 3 分钟.
ffmpeg
ffmpeg -i video.mp4 -r 5 'frames/frame-%03d.jpg'
其中: -r 5
代表抽取的帧率,即每秒视频抽取 5 帧出来.
convert
使用 imagemagick 包中的 convert 命令将一系列图片组合为 gif 动图格式:
convert -delay 20 -loop 0 *.jpg myimage.gif
其中: -delay 20
代表所生成的 gif 动图每帧之间的时间间隔,即每 0.2 秒显示下一帧.
如果系统内还没有 convert 命令,可以用如下命令安装:
sudo apt-get install imagemagick
博主使用 ubuntu 16.10,这个命令是预置在系统里的,不需要安装.
ffmpeg 及 convert 参数设置
上面两个命令中,ffmpeg -r 5
和 convert -delay 20
这两个参数值,分别是 视频抽帧间隔 和 gif每帧间隔,假设其分别为 video_fps 和 gif_delay,那么这两个参数在设置时必须满足如下条件:video_fps * gif_delay = 100
如果乘积小于 100,则生成的 gif 会比原本的播放速度快;
如果乘积大于 100,则生成的 gif 会比原本的播放速度慢.
至于原因,结合上面对这两个参数的介绍,思考一下就明白了.
捕获录制结束事件
上面三个命令分开调用,实现录屏为 gif 已经相当简单了.
如果要将三条命令写在一个脚本里,在一个过程中完成功能,第一个要解决的是如何捕获录制结束事件,即 ctrl-c 命令.
在 bash 中可以通过下面脚本实现:
# catch ctrl-c signal CTRL_C() { # ctrl-c hit,do something } trap CTRL_C SIGINT
有了这个方法获取录制结束事件,再往下就简单了.
这里遇到一个坑,就是如果捕获 ctrl-c 后直接开始转换 gif 的操作,会失败. 试过几次后,发现是 ctrl-c 后其实 Android 的 screenrecord 命令并没有处理完,这时候的视频还不可用. 解决的办法简单粗暴,让脚本原地 sleep 2秒,再去操作所生成的 MP4 文件就可用了.
最终脚本
也不知道该写些啥了,直接贴出完整脚本吧:
#!/bin/bash # author : liuxu-0703@163.com #======================================================== # define param group here QUALITY_1=("360x640" 1000000 4 25) QUALITY_2=("360x640" 1000000 5 20) QUALITY_3=("360x640" 1000000 10 10) QUALITY_4=("720x1280" 1000000 4 25) QUALITY_5=("720x1280" 1000000 5 20) #======================================================== QUALITY=(${QUALITY_2[@]}) RESOLUTION=${QUALITY[0]} BIT_RATE=${QUALITY[1]} GIF_FPS=${QUALITY[2]} GIF_DELAY=${QUALITY[3]} # GIF_FPS and GIF_DELAY must meet the following condition: # GIF_FPS * GIF_DELAY = 100 OUTPUT_FILE_NAME=android_screen_record.$(date +%m%d%H%M%S).gif OUTPUT_FILE_DIR=$(pwd) OUTPUT_VIDEO_NAME=screenrecord_$(date +%m%d%H%M%S).mp4 OUTPUT_VIDEO_DEVICE_DIR=/sdcard TMP_DIR=/tmp/android_screen_to_gif_$(date +%m%d%H%M%S) RECORDING=true # you may use adb by absolute file path. if so,specify it here ADB="adb" #======================================================== # catch ctrl-c signal CTRL_C() { if $RECORDING; then echo "stop recording. start convert..." RECORDING=false else # ctrl-c hit but not for stop recording,just exit. exit $? fi # adb screenrecord may still deal with mp4 file creating,# just wait for it a little while. sleep 2s adb pull $OUTPUT_VIDEO_DEVICE_DIR/$OUTPUT_VIDEO_NAME $TMP_DIR if [ -f $TMP_DIR/$OUTPUT_VIDEO_NAME ]; then # remove video on phone adb shell rm $OUTPUT_VIDEO_DEVICE_DIR/$OUTPUT_VIDEO_NAME echo "converting file: $TMP_DIR/$OUTPUT_VIDEO_NAME" MP4ToGIF $TMP_DIR/$OUTPUT_VIDEO_NAME else echo "* create screen record mp4 fail" exit 2 fi } trap CTRL_C SIGINT # catch script exit event CLEAR_WORK() { if [ -e $TMP_DIR ]; then # since the tmp files have been put into /tmp/ dir,they will get # removed on system reboot. thus we are in no hurry to remove them now. # un-commit this line if you want to remove tmp files immediately after script run #rm $TMP_DIR echo fi } trap "CLEAR_WORK" EXIT #======================================================== function MP4ToGIF() { echo "*** extract frames ***" mkdir $TMP_DIR/frames ffmpeg -i $1 -r $GIF_FPS "$TMP_DIR/frames/frame-%03d.jpg" echo "*** convert frames to gif ***" convert -delay $GIF_DELAY -loop 0 "$TMP_DIR/frames/*.jpg" $OUTPUT_FILE_DIR/$OUTPUT_FILE_NAME } #======================================================== if [ ! -d "$OUTPUT_FILE_DIR" ]; then echo "* output dir not exists: $OUTPUT_FILE_DIR" exit 1 fi if [ ! -e $TMP_DIR ]; then mkdir $TMP_DIR fi if [ ! -e $TMP_DIR ]; then echo "* tmp dir not exists: $TMP_DIR" exit 1 fi echo "params: $RESOLUTION,$BIT_RATE,$GIF_FPS,$GIF_DELAY" adb shell screenrecord --verbose --size $RESOLUTION --bit-rate $BIT_RATE $OUTPUT_VIDEO_DEVICE_DIR/$OUTPUT_VIDEO_NAME
开头定义的那几个数组,是为了便于测试. 最终生成的 gif 文件也直接保存在了当前目录下.
主要是因为加上参数化,代码会多很多,就不容易找到主要功能了.
带参数化,带简易帮助文档的完整脚本,可以在下面链接中找到:
使用
使用前提:
- adb 可以正常连接到手机,就是不能有 offline 之类的问题. (这里安利另一篇文章: 解决 ubuntu adb 设备识别问题)
- 手机必须是 4.4 以上系统.
脚本本身就是为了使用简单写的. 只需在命令行直接执行脚本,即可开始手机屏幕录制. 因为是测试,选在 /tmp 目录操作.
$ cd /tmp/ $ android_screen2gif.sh params: 360x640,1000000,5,20 Main display is 720x1280 @60.00fps (orientation=0) Configuring recorder for 360x640 video/avc at 1.00Mbps Content area is 360x640 at offset x=0 y=0
这时录制正在进行,命令行挂起. 等你认为录制可以结束了,按下 ctrl-c,录制结束,开始转换 gif 的步骤. 一般需要3到4秒,录屏的 gif 就在当前目录生成了.
^Cstop recording. start convert... [100%] /sdcard/screenrecord_0407090859.mp4 converting file: /tmp/android_screen_to_gif_0407090859/screenrecord_0407090859.mp4 *** extract frames *** ffmpeg version 3.0.7-0ubuntu0.16.10.1 Copyright (c) 2000-2017 the FFmpeg developers ...... Input #0,mov,mp4,m4a,3gp,3g2,mj2,from '/tmp/android_screen_to_gif_0407090859/screenrecord_0407090859.mp4': ...... Output #0,image2,to '/tmp/android_screen_to_gif_0407090859/frames/frame-%03d.jpg': ...... *** convert frames to gif *** result gif file: /tmp/android_screen_record.0407090859.gif
这一步输出较多,用 ......
代替了部分输出. 最后那行就是最终生成的 gif 录屏文件了.
参数建议
根据自测 及 最终 gif 生成的过程,可以得到如下结论:
视频解析度参数:
宽高越大越清晰,最终生成的 gif 文件越大. 这个是很明显的.
最好按手机竖屏显示的方式,将解析度设置为 9:16 的比例. 如果设置为其他比例,比如 480:480,则录制出的视频会有很宽的黑边.
视频比特率参数:
先说结论: 比特率设置基本不会影响最终生成的 gif 文件质量.
因为最终 gif 文件来自视频抽帧,视频比特率的大小对于比如每秒抽取10帧这样的需求,对抽取出的图片清晰度影响不大. 因此为了中间文件更小,建议把这个参数设低即可.
抽帧帧率 及 每帧间隔:
这两个参数对最终 gif 质量影响很大. 其中:
抽帧帧率越大,最终 gif 质量越高;
每帧间隔越小,最终 gif 质量越高.
如前述,这两个参数必须成对设置. 如果这两个参数都取整数的话,基本上可以设置的就下面这几对:
2,50 4,25 5,20 10,10
其中:10,10
这组,设置每秒10帧,gif 每帧间隔 0.1 秒,这样5秒的gif文件就要大概 10M 大小了.2,50
这组,设置每秒2帧,gif 每帧间隔 0.5 秒,最终gif文件卡顿就很严重了.
因此建议选取 5,20
这组参数. 如果对gif质量要求很高,可以试试 10,10
这组参数.
综上,小伙伴们可以自己尝试下设置不同的参数组合,试几次就能体会该怎么设置了.
参考:
create gif from mp4 via command line
@L_403_4@
脚本获取地址(github):
android_screen2gif.sh