本文记录如何在Ubuntu 14.04下编译FFmpeg源码以及在Android Studio中调用FFmpeg动态库。
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)'
编写ffmpeg_android.sh 脚本,这个脚本用于生成FFmpeg头文件及类库文件。脚本参考这里。网上相关的配置脚本比较多,这个脚本可以比较方便生成各个cpu架构的库文件。
在FFmpeg目录下创建 ffmpeg_android.sh:
#!/bin/bash
# Don't forget to install yasm.
#set -e
# Set your own NDK here
NDK=/home/xiangyi/FFmpeg/android-ndk-r14b
ARM_PLATFORM=$NDK/platforms/android-23/arch-arm/
ARM_PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
ARM64_PLATFORM=$NDK/platforms/android-23/arch-arm64/
ARM64_PREBUILT=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64
X86_PLATFORM=$NDK/platforms/android-23/arch-x86/
X86_PREBUILT=$NDK/toolchains/x86-4.9/prebuilt/linux-x86_64
X86_64_PLATFORM=$NDK/platforms/android-23/arch-x86_64/
X86_64_PREBUILT=$NDK/toolchains/x86_64-4.9/prebuilt/linux-x86_64
MIPS_PLATFORM=$NDK/platforms/android-23/arch-mips/
MIPS_PREBUILT=$NDK/toolchains/mipsel-linux-android-4.9/prebuilt/linux-x86_64
BUILD_DIR=`pwd`/ffmpeg-android
FFMPEG_VERSION="3.3.8"
#if [ ! -e "ffmpeg-${FFMPEG_VERSION}.tar.bz2" ]; then
# echo "Downloading ffmpeg-${FFMPEG_VERSION}.tar.bz2"
# curl -LO http://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2
#else
# echo "Using ffmpeg-${FFMPEG_VERSION}.tar.bz2"
#fi
#tar -xvf ffmpeg-${FFMPEG_VERSION}.tar.bz2
function build_one
{
if [ $ARCH == "arm" ]
then
PLATFORM=$ARM_PLATFORM
PREBUILT=$ARM_PREBUILT
HOST=arm-linux-androideabi
elif [ $ARCH == "arm64" ]
then
PLATFORM=$ARM64_PLATFORM
PREBUILT=$ARM64_PREBUILT
HOST=aarch64-linux-android
elif [ $ARCH == "mips" ]
then
PLATFORM=$MIPS_PLATFORM
PREBUILT=$MIPS_PREBUILT
HOST=mipsel-linux-android
elif [ $ARCH == "x86_64" ]
then
PLATFORM=$X86_64_PLATFORM
PREBUILT=$X86_64_PREBUILT
HOST=x86_64-linux-android
else
PLATFORM=$X86_PLATFORM
PREBUILT=$X86_PREBUILT
HOST=i686-linux-android
fi
pushd ffmpeg-$FFMPEG_VERSION
./configure --target-os=linux \
--incdir=$BUILD_DIR/include \
--libdir=$BUILD_DIR/lib/$CPU \
--enable-cross-compile \
--arch=$ARCH \
--cc=$PREBUILT/bin/$HOST-gcc \
--cross-prefix=$PREBUILT/bin/$HOST- \
--nm=$PREBUILT/bin/$HOST-nm \
--sysroot=$PLATFORM \
--extra-cflags="-I$BUILD_DIR/include -fPIC -DANDROID -Wfatal-errors -Wno-deprecated $OPTIMIZE_CFLAGS" \
--enable-gpl \
--enable-small \
--extra-ldflags="-L$BUILD_DIR/lib/$CPU" \
--enable-shared \
--disable-doc \
--disable-programs \
--disable-stripping \
--disable-symver \
--disable-static \
--pkg-config=/usr/local/bin/pkg-config \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make -j80
make install
$PREBUILT/bin/$HOST-ar d libavcodec/libavcodec.a inverse.o
$PREBUILT/bin/$HOST-ld -rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -soname libffmpeg.so -shared -nostdlib -z noexecstack -Bsymbolic --whole-archive --no-undefined -o libffmpeg.so libavcodec/libavcodec.a libavformat/libavformat.a libavutil/libavutil.a libswscale/libswscale.a -lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker $PREBUILT/lib/gcc/$HOST/4.9.x/libgcc.a
popd
}
#arm v5te
#CPU=armv5te
#ARCH=arm
#OPTIMIZE_CFLAGS="-marm -march=$CPU"
#PREFIX=$BUILD_DIR/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one
#arm v6
#CPU=armv6
#ARCH=arm
#OPTIMIZE_CFLAGS="-marm -march=$CPU"
#PREFIX=`pwd`/ffmpeg-android/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one
#arm v7vfpv3
#CPU=armv7-a
#ARCH=arm
#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfpv3-d16 -marm -march=$CPU -D__thumb__ -mthumb"
#PREFIX=$BUILD_DIR/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one
#arm v7vfp
#CPU=armv7-a
#ARCH=arm
#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU "
#PREFIX=`pwd`/ffmpeg-android/$CPU-vfp
#ADDITIONAL_CONFIGURE_FLAG=
#build_one
#arm v7n
#CPU=armv7-a
#ARCH=arm
#OPTIMIZE_CFLAGS="-mfloat-abi=softfp -mfpu=neon -marm -march=$CPU -mtune=cortex-a8"
#PREFIX=$BUILD_DIR/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one
#arm v6+vfp
#CPU=armv6
#ARCH=arm
#OPTIMIZE_CFLAGS="-DCMP_HAVE_VFP -mfloat-abi=softfp -mfpu=vfp -marm -march=$CPU"
#PREFIX=`pwd`/ffmpeg-android/${CPU}_vfp
#ADDITIONAL_CONFIGURE_FLAG=
#build_one
#arm64-v8a
#CPU=arm64-v8a
#ARCH=arm64
#OPTIMIZE_CFLAGS=
#PREFIX=$BUILD_DIR/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one
#x86_64
#CPU=x86_64
#ARCH=x86_64
#OPTIMIZE_CFLAGS="-fomit-frame-pointer"
#PREFIX=$BUILD_DIR/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one
#x86
#CPU=i686
#ARCH=i686
#OPTIMIZE_CFLAGS="-fomit-frame-pointer"
#PREFIX=$BUILD_DIR/$CPU
#ADDITIONAL_CONFIGURE_FLAG=
#build_one
上面代码中,打开arm64-v8a、x86等下的注释即可编译不同cpu架构的库文件了。执行./ffmpeg_android.sh,成功执行后会生成ffmpeg-android 目录:
其中include目录存放的是头文件:
lib目录以cpu架构类型存放库文件:
#include
#include
#include
extern "C" {
//编码
#include "libavcodec/avcodec.h"
//封装格式处理
#include "libavformat/avformat.h"
//像素处理
#include "libswscale/swscale.h"
}
#define FFLOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"ffmpeg",FORMAT,##__VA_ARGS__);
#define FFLOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"ffmpeg",FORMAT,##__VA_ARGS__);
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_cdrc_helloffmpeg_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
#if defined(__arm__)
#if defined(__ARM_ARCH_7A__)
#if defined(__ARM_NEON__)
#define ABI "armeabi-v7a/NEON"
#else
#define ABI "armeabi-v7a"
#endif
#else
#define ABI "armeabi"
#endif
#elif defined(__i386__)
#define ABI "x86"
#elif defined(__mips__)
#define ABI "mips"
#elif defined(__aarch64__)
#define ABI "aarch64"
#else
#define ABI "unknown"
#endif
return env->NewStringUTF("Hello from JNI ! Compiled with ABI " ABI ".");
}
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_cdrc_helloffmpeg_MainActivity_InitFFMPEG(
JNIEnv *env,
jobject /* this */) {
av_register_all();
std::string hello = "av_register_all OK";
return env->NewStringUTF(hello.c_str());
}
//
// Created by CDRC on 2018/8/21.
//
#include
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
//Log
#ifdef ANDROID
#include
#include
#define LOGE(format, ...) __android_log_print(ANDROID_LOG_ERROR, "(>_<)", format, ##__VA_ARGS__)
#else
#define LOGE(format, ...) printf("(>_<) " format "\n", ##__VA_ARGS__)
#endif
//FIX
struct URLProtocol;
/**
* com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.urlprotocolinfo()
* Protocol Support Information
*/
JNIEXPORT jstring Java_com_cdrc_helloffmpeg_MainActivity_urlprotocolinfo(JNIEnv *env, jobject obj){
char info[40000]={0};
av_register_all();
struct URLProtocol *pup = NULL;
//Input
struct URLProtocol **p_temp = &pup;
avio_enum_protocols((void **)p_temp, 0);
while ((*p_temp) != NULL){
sprintf(info, "%s[In ][%10s]\n", info, avio_enum_protocols((void **)p_temp, 0));
}
pup = NULL;
//Output
avio_enum_protocols((void **)p_temp, 1);
while ((*p_temp) != NULL){
sprintf(info, "%s[Out][%10s]\n", info, avio_enum_protocols((void **)p_temp, 1));
}
//LOGE("%s", info);
return (*env)->NewStringUTF(env, info);
}
/**
* com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.avformatinfo()
* AVFormat Support Information
*/
JNIEXPORT jstring Java_com_cdrc_helloffmpeg_MainActivity_avformatinfo(JNIEnv *env, jobject obj){
char info[40000] = { 0 };
av_register_all();
AVInputFormat *if_temp = av_iformat_next(NULL);
AVOutputFormat *of_temp = av_oformat_next(NULL);
//Input
while(if_temp!=NULL){
sprintf(info, "%s[In ][%10s]\n", info, if_temp->name);
if_temp=if_temp->next;
}
//Output
while (of_temp != NULL){
sprintf(info, "%s[Out][%10s]\n", info, of_temp->name);
of_temp = of_temp->next;
}
//LOGE("%s", info);
return (*env)->NewStringUTF(env, info);
}
package com.cdrc.helloffmpeg;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
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("postproc-54");
System.loadLibrary("avfilter-6");
System.loadLibrary("native-lib");
}
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Example of a call to a native method
tv = (TextView) findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
public void InitFFmpeg(View v){
tv.setText(InitFFMPEG());
}
public void protocol(View v){
tv.setText(urlprotocolinfo());
}
public void format(View v){
tv.setText(avformatinfo());
}
/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
public native String InitFFMPEG();
public native String urlprotocolinfo();
public native String avformatinfo();
}
stringFromJNI和InitFFMPEG是在native-lib.cpp中实现,urlprotocolinfo和avformatinfo是在simplest_ffmpeg_helloworld.c中实现。
6. 修改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)
# 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.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp
src/main/cpp/simplest_ffmpeg_helloworld.c
)
# 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 )
# 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.
set(distribution_DIR ../../../../libs)
add_library( avcodec-57
SHARED
IMPORTED)
set_target_properties( avcodec-57
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/arm64-v8a/libavcodec-57.so)
add_library( avdevice-57
SHARED
IMPORTED)
set_target_properties( avdevice-57
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/arm64-v8a/libavdevice-57.so)
add_library( avfilter-6
SHARED
IMPORTED)
set_target_properties( avfilter-6
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/arm64-v8a/libavfilter-6.so)
add_library( avformat-57
SHARED
IMPORTED)
set_target_properties( avformat-57
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/arm64-v8a/libavformat-57.so)
add_library( avutil-55
SHARED
IMPORTED)
set_target_properties( avutil-55
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/arm64-v8a/libavutil-55.so)
add_library( postproc-54
SHARED
IMPORTED)
set_target_properties( postproc-54
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/arm64-v8a/libpostproc-54.so)
add_library( swresample-2
SHARED
IMPORTED)
set_target_properties( swresample-2
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/arm64-v8a/libswresample-2.so)
add_library( swscale-4
SHARED
IMPORTED)
set_target_properties( swscale-4
PROPERTIES IMPORTED_LOCATION
${distribution_DIR}/arm64-v8a/libswscale-4.so)
include_directories(libs/include)
target_link_libraries( # Specifies the target library.
native-lib
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.
${log-lib} )