项目背景
最近因公司业务需要,需要写一个视频播放器,目前还属于前期调研阶段,综合看了Google
的ExoPlayer
和Bilibili
的IJKplayer
,决定还是基于FFmpeg
来写一个轻量级的播放器,目前稍微有了一点进展,现在记录一下,其他人如果有需要也可以参考一下
-
FFmpeg
下载和编译
下载地址:download url
没翻墙的同学下载可能比较慢,我上传了一份到我的百度网盘,链接为:
链接: https://pan.baidu.com/s/1I_IwyRsD8lAUy4TxxOkkcQ 密码: 19uq
下载好了以后记得把版本切换到n3.6.6
,最新版本在mac上编译会报下面这个错误
/bin/sh: ranlib/usr/local/lib/libavdevice.a: No such file or directory
- 修改配置文件
大概在3305行左右,修改成如下配置
#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)'
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
- 添加
Android
编译脚本
新建一个文件名为build_android.sh
的文件,然后用文本编辑器打开,添加如下内容
#!/bin/bash
export NDK_HOME=/Users/zhangsan/Documents/android-ndk-r10e
export PLATFORM_VERSION=android-9
function build
{
echo "start build ffmpeg for $ARCH"
./configure --target-os=linux \
--prefix=$PREFIX --arch=$ARCH \
--disable-doc \
--enable-shared \
--disable-static \
--disable-yasm \
--disable-asm \
--disable-symver \
--enable-gpl \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--cross-prefix=$CROSS_COMPILE \
--enable-cross-compile \
--sysroot=$SYSROOT \
--enable-small \
--extra-cflags="-Os -fpic $ADDI_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS" \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make
make install
echo "build ffmpeg for $ARCH finished"
}
#arm
ARCH=arm
CPU=arm
PREFIX=$(pwd)/android/$ARCH
TOOLCHAIN=$NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64
CROSS_COMPILE=$TOOLCHAIN/bin/arm-linux-androideabi-
ADDI_CFLAGS="-marm"
SYSROOT=$NDK_HOME/platforms/$PLATFORM_VERSION/arch-$ARCH/
build
#x86
ARCH=x86
CPU=x86
PREFIX=$(pwd)/android/$ARCH
TOOLCHAIN=$NDK_HOME/toolchains/x86-4.9/prebuilt/darwin-x86_64
CROSS_COMPILE=$TOOLCHAIN/bin/i686-linux-android-
ADDI_CFLAGS="-march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32"
SYSROOT=$NDK_HOME/platforms/$PLATFORM_VERSION/arch-$ARCH/
build
其中需要注意将文件上方NDK_HOME
的路径换成自己的路径
然后记得给这个脚本添加执行权限,命令可参考
chmod 777 ./build_android.sh
- 执行编译命令
在终端里执行
./build_android.sh
开始编译,如果编译顺利,最后你应该会看到下面这句话
build ffmpeg for x86 finished
然后在当前项目下面应该会看到android
文件夹,下面有两种架构对应的不同文件夹arm
, x86
新建一个支持C++开发的Android工程
工程准备就绪后,在app/libs
文件下新建x86
,arm-v7a
文件夹,然后将上面编译生成的文件夹下的
libavcodec-57.so
libavdevice-57.so
libavfilter-6.so
libavformat-57.so
libavutil-55.so
libpostproc-54.so
libswresample-2.so
libswscale-4.so
拷贝到对应路径下(arm
拷贝到arm-v7a
下),头文件随便拷贝一个就行
然后将app/build.gradle
添加如下配置
defaultConfig {
applicationId "com.test.meida"
minSdkVersion 23
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
arguments "-DANDROID_APP_PLATFORM=android-16", "-DANDROID_STL=c++_static"
cppFlags ""
abiFilters "armeabi-v7a", "x86"
}
}
//
// ndk{
// abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
// }
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
jniDebuggable true
ndk {
debuggable true
}
}
}
externalNativeBuild {
// ndkBuild {
// path file("src\\main\\jni\\Android.mk")
// }
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
lintOptions {
abortOnError false
}
再修改相应cpp
文件夹下的CMakeLists.txt
为如下
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
#include_directories(${CMAKE_SOURCE_DIR}/libsdl/)
#add_subdirectory(${CMAKE_SOURCE_DIR}/libsdl)
# 定义变量
set(distribution_DIR /Users/Zhangsan/Documents/Work/MaMedia/app/libs)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
myplayer
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp
)
add_library( avcodec-57
SHARED
IMPORTED )
set_target_properties( avcodec-57
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libavcodec-57.so )
add_library( avdevice-57
SHARED
IMPORTED )
set_target_properties( avdevice-57
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libavdevice-57.so )
add_library( avfilter-6
SHARED
IMPORTED )
set_target_properties( avfilter-6
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libavfilter-6.so )
add_library( avformat-57
SHARED
IMPORTED )
set_target_properties( avformat-57
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libavformat-57.so )
add_library( avutil-55
SHARED
IMPORTED )
set_target_properties( avutil-55
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libavutil-55.so )
add_library( postproc-54
SHARED
IMPORTED )
set_target_properties( postproc-54
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libpostproc-54.so )
add_library( swresample-2
SHARED
IMPORTED )
set_target_properties( swresample-2
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libswresample-2.so )
add_library( swscale-4
SHARED
IMPORTED )
set_target_properties( swscale-4
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/${ANDROID_ABI}/libswscale-4.so )
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
find_library(
android-lib
android)
include_directories(${distribution_DIR}/../src/main/cpp/)
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
myplayer
avcodec-57
avdevice-57
avfilter-6
avformat-57
avutil-55
postproc-54
swresample-2
swscale-4
# Links the target library to the log library
# included in the NDK.
${android-lib}
${log-lib})
- 编写测试代码
在native-lib.cpp
文件中添加如下代码
#define JNI_CLASS_PLAYER "com/test/media/MediaPlayer"
static jstring
MediaPlayer_getInfo(JNIEnv *env, jobject thiz) {
char info[40000] = {0};
avfilter_register_all();
AVFilter *f_temp = (AVFilter *) avfilter_next(NULL);
while (f_temp != NULL) {
sprintf(info, "%s%s\n", info, f_temp->name);
f_temp = f_temp->next;
}
return env->NewStringUTF(info);
}
static JNINativeMethod g_methods[] = {
{"_getInfo", "()Ljava/lang/String;", (void *)(MediaPlayer_getInfo)}
};
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv* env = NULL;
jint result = -1;
jclass clazz = NULL;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed\n");
goto fail;
}
clazz = env->FindClass(JNI_CLASS_PLAYER);
if (clazz) {
env->RegisterNatives(clazz, g_methods, NELEM(g_methods));
} else {
LOGE("ERROR: %s is Not Found", JNI_CLASS_PLAYER);
goto fail;
}
result = JNI_VERSION_1_4;
fail:
return result;
}
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved)
{
LOGE("JNI_OnUnload");
}
上层对应的java
代码如下:
public class MediaPlayer {
static {
System.loadLibrary("myplayer");
}
public native String _getInfo();
在MainActivity
中调用此方法
tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
MediaPlayer mediaPlayer = new MediaPlayer();
Log.d("MainActivity", "getInfo:" + mediaPlayer._getInfo());
}
});
如果在logcat
中打印如下信息
2020-11-22 12:37:58.249 4416-4416/com.test.meida D/MainActivity: getInfo:abench
acompressor
acrossfade
acrusher
adelay
aecho
aemphasis
aeval
afade
afftfilt
...
那么恭喜你,成功了!
网友评论