编译环境
操作系统:macOS 10.15.5
NDK:22.1.7171670
ffmpeg:4.4 “Rao”(截止 20210609)
cmake: 3.20.3
gcc:
1
2
3
4
5
6
7
8
9Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include/c++/4.2.1
Apple clang version 12.0.0 (clang-1200.0.32.29)
Target: x86_64-apple-darwin19.5.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin
下载
从官网下载源码压缩包。截止本文章发布时,最新版为 4.4 "Rao"
该版本发布于 2021-04-08。
1 | wget https://www.ffmpeg.org/releases/ffmpeg-4.4.tar.bz2 |
若要编译 x86 架构,需要下载如下工具:
1 | brew install yasm |
编译
编写编译脚本
解压缩上面的文件,进入下载的文件夹,并创建编译脚本:
1 | cd ffmpeg-4.4 |
内容如下:
1 | !/bin/bash |
说明:
- 设置
cflags
的技巧,可以参考我的这篇文章。 - 添加
--disable-x86asm
和--disable-inline-asm
可以解决如下问题:./libavutil/x86/timer.h:39:24: error: invalid output constraint '=a' in asm
- 添加
--disable-programs
的话,则不会生成可执行文件。(即,不会生成bin
文件夹) - 还可以添加如下参数,从而不生成对应的文件:
- –disable-ffmpeg
- –disable-ffprobe
- –disable-ffplay
开始编译
添加执行权限:
1 | chmod +x build_android.sh |
执行编译脚本:
1 | ./build_android.sh |
查看编译后的文件
1 | cd prebuilt |
验证编译结果
以 arm64-v8a
为例,将编译生成的所有文件拷贝到手机 /data/local/tmp/ffmpeg
目录下(若目录不存在,请先自行创建):
1 | adb push arm64-v8a/* /data/local/tmp/ffmpeg |
使用 adb shell
进入手机 shell
模式,并进入 bin
中文件夹:
1 | adb shell |
此时,若直接运行 ./ffmpeg
会报如下错:
1 | CANNOT LINK EXECUTABLE "./ffmpeg": library "libavdevice.so" not found |
因为我们刚刚编译的是动态库,所以要想运行 ffmpeg
的话,需要添加必要设置:
1 | export LD_LIBRARY_PATH=/data/local/tmp/ffmpeg/lib |
之后再运行 ./ffmpeg
,确认是否仅编译了 adpcm_ima_qt
解码器:
1 | ./ffmpeg -decoders |
直接编译在 Android
上可执行的 ffmpeg
刚刚编译得到的 ffmpeg
命令并不能直接运行,那么如果需要编译能直接运行的 ffmpeg
命令的话,需要对脚本做如下修改:
- 删除
--disable-programs
- 将
--disable-static
修改为--enable-static
- 将
--enable-shared
修改为--disable-shared
之后再重新执行编译脚本的话,其生成的 bin
文件夹下的 ffmpeg
命令就可以直接在 Android 运行了。这里就不再演示了。
动态库生成的 ffmpeg
大小为 235K。静态库生成的可执行的 ffmpeg
大小为 1.2M。
生成 Android
可用的 so
文件
编写 Application.mk
文件
在 Android 项目 app module 的 src/main/jni
目录下创建 Application.mk
文件:
1 | APP_STL := c++_shared |
编写 Android.mk
文件
在 Android 项目 app module 的 src/main/jni
目录下创建 Android.mk
文件:
1 | LOCAL_PATH := $(call my-dir) |
编写 cpp
文件
在 Android 项目 app module 的 src/main/jni
目录下创建 libadpcm_ima_qt_jni.cpp
文件:
1 |
|
使用 NDK 编译生成 libadpcm-ima-qt.so
文件
在 jni
目录下执行 ndk-build
,生成所需的 libadpcm-ima-qt.so
文件:
1 | ndk-build |
生成的 so
位于如下目录:src/main/libs/
以 arm64-v8a
为例,将生成的 src/main/libs/arm64-v8a
拷贝到自己的项目里,就可以使用 libadpcm-ima-qt.so
了。
好了所有的编译工作,到这里终于大功告成!!!
编写 JNI 文件
在 com.leovp.ffmpeg.audio.adpcm
包下,创建 AdpcmImaQtDecoder
类:
1 | package com.leovp.ffmpeg.audio.adpcm |
接下来大家就可以随意玩耍了!!!
注意事项
这里强调下:解码时,每次传入的 ADPCM-IMA-QT 裸流的大小必须是 34bytes×声道数。也就是单声道每次解码 34 个字节,双声道每次解码 68 个字节。
此外需要特别说明的是,ADPCM IMA QT 解码时的音频采样格式必须是 **AV_SAMPLE_FMT_S16P**。下图来自 ffmpeg 官网源码。
还有一点需要特殊注意,我当时就在这个地方卡了挺长时间,在获取每个声道的数据长度时,只需要获取 frame->linesize[0] 的长度即可,这是因为每个声道数据长度是一样的。
我当时认为 frame->linesize[0] 应该为左声道的数据长度,frame->linesize[1] 为右声道的数据长度。可实际情况是通过 frame->linesize[1] 获取的大小竟然为 0
。后来仔细看了下源码才明白,原来源码里有说明:对于音频而言,可能只有 frame->linesize[0] 有值。并且若是 planar 音频
的话(ADPCM IMA QT 就是这种情况),每个声道的数据长度是一样的。
最后还有一个小的提醒:Android 播放的 PCM 数据,左右声道数据是交错存储的:
源码
需要源码的请移至我的Github。
ffmpeg 各库功能说明
- libavutil is a library containing functions for simplifying programming, including random number generators, data structures, mathematics routines, core multimedia utilities, and much more.
- libavcodec is a library containing decoders and encoders for audio/video codecs.
- libavformat is a library containing demuxers and muxers for multimedia container formats.
- libavdevice is a library containing input and output devices for grabbing from and rendering to many common multimedia input/output software frameworks, including Video4Linux, Video4Linux2, VfW, and ALSA.
- libavfilter is a library containing media filters.
- libswscale is a library performing highly optimized image scaling and color space/pixel format conversion operations.
- libswresample is a library performing highly optimized audio resampling, rematrixing and sample format conversion operations.
参考文献
- https://habr.com/ru/post/473134/
- https://vinsol.com/blog/2014/07/30/cross-compiling-ffmpeg-with-x264-for-android/
- https://github.com/tanersener/mobile-ffmpeg/issues/30
- https://blog.csdn.net/shaosunrise/article/details/79833592
- https://zhuanlan.zhihu.com/p/46225885
- https://juejin.cn/post/6844903905092960270#heading-5
- https://www.jianshu.com/p/aba734d5b5cd
- https://blog.csdn.net/ck_19900710/article/details/52610551
- https://blog.csdn.net/u010925331/article/details/50992505
- https://blog.csdn.net/weiwei9363/article/details/108565695
- https://blog.csdn.net/yikunbai5708/article/details/102995104
- https://blog.csdn.net/bobcat_kay/article/details/80889398
- https://github.com/WritingMinds/ffmpeg-android/blob/master/ffmpeg_build.sh
- https://www.huaweicloud.com/articles/45db62415c5a415959fc5767444cf845.html