ffmpeg提供了强大的多媒体处理功能,并且由于跨平台,所以也可以使用在android 平台.很多第三方平台的后台程序也使用了这个库,如七牛,趣拍等.
下面我要记录在android studio中使用它的步骤:
一.配置ndk,具体请参考博文http://blog.youkuaiyun.com/jishoujiang/article/details/52705425;
二.下载ffmpeg源码,去官网http://ffmpeg.org/,现在是3.1.3版本;
三.编译该源码,以ubutu系统为例:
(1)修改配置文件:
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)'
(2)输入脚本
我是使用了别人的脚本,网上有很多,在源码根目录,即ffmpeg-3.1.3下新建build_android.sh,输入:
#!/bin/bash
NDK=/usr/android-sdk-linux/ndk-bundle
SYSROOT=$NDK/platforms/android-9/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
function build_one
{
./configure \
--prefix=$PREFIX \
--enable-shared \
--disable-static \
--disable-doc \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-avdevice \
--disable-doc \
--disable-symver \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--target-os=linux \
--arch=arm \
--enable-cross-compile \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $ADDI_CFLAGS" \
--extra-ldflags="$ADDI_LDFLAGS" \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make
make install
}
CPU=arm
PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-marm"
build_one
注意2~4行的ndk路径改成你自己的ndk路径
(3)执行脚本
打开终端,进入ffmpeg-3.1.3目录,执行sudo chmod + xxx build_android.sh;
再执行sudo ./build_android.sh,即开始执行脚本.大概10分钟左右吧,执行完毕,在ffmpeg-3.1.3目录下会生成一个android 的文件夹.里面有一个arm文件夹,再里面是include和lib文件夹,使用chmod + xxx -R命令,对android文件夹添加权限
编译完毕!
四.编译class,新建MainActivity.class ,
新增代码:
static {
System.loadLibrary("avcodec-57");
System.loadLibrary("avfilter-6");
System.loadLibrary("avformat-57");
System.loadLibrary("avutil-55");
System.loadLibrary("swresample-2");
System.loadLibrary("swscale-4");
System.loadLibrary("test"); //即Android.mk文件#Program下的LOCAL_MODULE
}
public native String urlprotocolinfo();
public native String avformatinfo();
public native String avcodecinfo();
public native String avfilterinfo();
public native String configurationinfo();
执行javah -d ../jni com.example.test.MainActivity,自动在java下生成jni目录 和com_example_test_MainActivity.h文件,jni下新建com_example_test_MainActivity.c,输入:
#include "com_example_test_MainActivity.h"
#include "include/libavformat/avformat.h"
#include "include/libavfilter/avfilter.h"
#include <stdio.h>
#include <android/log.h>
#define TAG "JNI_TAG"
//为了方便调用,将输出宏定义
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
struct URLProtocol;
/*
* Class: com_example_test_MainActivity
* Method: urlprotocolinfo
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_test_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);
LOGD("%s",info);
return (*env)->NewStringUTF(env, info);
}
/*
* Class: com_example_test_MainActivity
* Method: avformatinfo
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_test_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);
}
/*
* Class: com_example_test_MainActivity
* Method: avcodecinfo
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_test_MainActivity_avcodecinfo
(JNIEnv *env, jobject obj)
{
char info[40000] = { 0 };
av_register_all();
AVCodec *c_temp = av_codec_next(NULL);
while(c_temp!=NULL){
if (c_temp->decode!=NULL){
sprintf(info, "%s[Dec]", info);
}
else{
sprintf(info, "%s[Enc]", info);
}
switch (c_temp->type){
case AVMEDIA_TYPE_VIDEO:
sprintf(info, "%s[Video]", info);
break;
case AVMEDIA_TYPE_AUDIO:
sprintf(info, "%s[Audio]", info);
break;
default:
sprintf(info, "%s[Other]", info);
break;
}
sprintf(info, "%s[%10s]\n", info, c_temp->name);
c_temp=c_temp->next;
}
//LOGE("%s", info);
return (*env)->NewStringUTF(env, info);
}
/*
* Class: com_example_test_MainActivity
* Method: avfilterinfo
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_test_MainActivity_avfilterinfo
(JNIEnv *env, jobject obj)
{
char info[40000] = { 0 };
av_register_all();
AVFilter *f_temp = (AVFilter *)avfilter_next(NULL);
while (f_temp != NULL){
sprintf(info, "%s[%10s]\n", info, f_temp->name);
}
//LOGE("%s", info);
return (*env)->NewStringUTF(env, info);
}
/*
* Class: com_example_test_MainActivity
* Method: configurationinfo
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_test_MainActivity_configurationinfo
(JNIEnv *env, jobject obj)
{
char info[10000] = { 0 };
av_register_all();
sprintf(info, "%s\n", avcodec_configuration());
//LOGE("%s", info);
return (*env)->NewStringUTF(env, info);
}
五.生成so文件
复制include文件夹和ib下的so文件到生成的main/java/jni目录中,在jni中新建android.mk文件
输入:
LOCAL_PATH := $(call my-dir)
# FFmpeg library
include $(CLEAR_VARS)
LOCAL_MODULE := avcodec
LOCAL_SRC_FILES := libavcodec-57.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avfilter
LOCAL_SRC_FILES := libavfilter-6.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avformat
LOCAL_SRC_FILES := libavformat-57.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avutil
LOCAL_SRC_FILES := libavutil-55.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swresample
LOCAL_SRC_FILES := libswresample-2.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := swscale
LOCAL_SRC_FILES := libswscale-4.so
include $(PREBUILT_SHARED_LIBRARY)
# Program
include $(CLEAR_VARS)
LOCAL_MODULE := test
LOCAL_SRC_FILES :=com_example_test_MainActivity.c
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include #头文件搜索路径
LOCAL_LDLIBS :=-llog -lz #附加的链接器选项
LOCAL_SHARED_LIBRARIES := avcodec avfilter avformat avutil swresample swscale
include $(BUILD_SHARED_LIBRARY)
打开android studio终端,进入java目录,执行ndk-build APP_ABI=”armeabi” ,会在main下生成libs和obj两个包,在main下新建jniLibs包,将libs下的armeabi包剪切到jniLibs下,改名为armeabi-v7a,build.gradle新增:
sourceSets{
main{
jni.srcDirs=['jniLibs']
}
}
5.验证效果:
MainActivity新增代码:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String s1 = urlprotocolinfo();
String s2 = avformatinfo();
String s3 = avcodecinfo();
String s4 = avfilterinfo();
String s5 = configurationinfo();
Log.e("jni",s1);
Log.e("jni",s2);
Log.e("jni",s3);
Log.e("jni",s4);
Log.e("jni",s5);
}
正常打印了五次日志,说明配置成功
ps:如果出现include中的.h文件出现#include “libavutil/version.h”标红的问题,只要改成#include “../libavutil/version.h”就可以了