android studio 移植 ffmpeg
android进行ffmpeg的移植操作,,首先准备工作是编译ffmpeg的源码生成静态库。这里在linux环境下编译,下载以后解压,然后准备ndk就行了。需要修改地方有SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'SLIB_IN
android进行ffmpeg的移植操作,,首先准备工作是编译ffmpeg的源码生成静态库。这里在linux环境下编译,下载以后解压,然后准备ndk就行了。
ffmpeg 下载地址https://ffmpeg.org/download.html
下载FFmpeg源代码之后,首先需要对源代码中的configure文件进行修改。。在configure文件中找到下面几行代码:
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)'
这个是因为编译完版本号格式的问题,之前的是studio不能识别的。然后就是建立编译的脚本,编译即可。脚本如下
cd ffmpeg-3.1.5
make clean
export NDK=/home/spc/android-ndk-r10b
export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt
export PLATFORM=$NDK/platforms/android-8/arch-arm
export PREFIX=../lib
build_one(){
./configure --target-os=linux --prefix=$PREFIX \
--enable-cross-compile \
--enable-runtime-cpudetect \
--disable-asm \
--arch=arm \
--cc=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-gcc \
--cross-prefix=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi- \
--disable-stripping \
--nm=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-nm \
--sysroot=$PLATFORM \
--enable-gpl --enable-shared --disable-static --enable-small \
--disable-ffprobe --disable-ffplay --disable-ffmpeg --disable-ffserver --disable-debug \
--extra-cflags="-fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a"
}
build_one
make
make install
cd ..
需要注意的是ndk的目录替换成自己的,检查下目录是否存在就行。
在源码外层文件夹建立脚本文件,然后简单暴力点就chmod 777 直接赋予可写可执行就行。然后./ 执行脚本。就可以编译。
完成编译以后,在源码外层lib文件下就是编译so 准备工作到此完成
下面正式开始
新建一个工程,在gradle的defaultConfig里面 配置上
sourceSets.main {
jni.srcDirs = []
jniLibs.srcDir 'src/main/libs'
}
这样,指定磨人的jni目录为空,在main目录下新建一个jni文件夹,这就是ndk编译的目录。
首先先写几个native方法, 获取下ffmpeg的信息,
public class NativeMethods {
native String helloWorld();
public native String urlprotocolinfo();
public native String avformatinfo();
public native String avcodecinfo();
public native String avfilterinfo();
}
然后javah 生成签名。在main下面的java目录执行 javah
javah -d ../jni com.example.xxx.NativeMethods
生成头文件自己在jni目录新建一个c文件,实现刚才头文件里面的方法。
c代码如下
#include "com_example_spc_mmfpeghello_NativeMethods.h"
#include <android/log.h>
#include <string.h>
#include <jni.h>
#include <stdio.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"
#define TAG "myDemo-jni" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型
JNIEXPORT jstring JNICALL Java_com_example_spc_mmfpeghello_NativeMethods_helloWorld (JNIEnv *env, jobject obj)
{
char info[10000] = {0};
sprintf(info, "%s\n", avcodec_configuration());
return (*env)->NewStringUTF(env, info);
}
JNIEXPORT jstring JNICALL Java_com_example_spc_mmfpeghello_NativeMethods_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);
}
JNIEXPORT jstring JNICALL Java_com_example_spc_mmfpeghello_NativeMethods_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);
}
JNIEXPORT jstring JNICALL Java_com_example_spc_mmfpeghello_NativeMethods_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);
}
JNIEXPORT jstring JNICALL Java_com_example_spc_mmfpeghello_NativeMethods_avfilterinfo
(JNIEnv *env, jobject obj)
{
char info[40000] = { 0 };
avfilter_register_all();
AVFilter *f_temp = (AVFilter *)avfilter_next(NULL);
while (f_temp != NULL){
sprintf(info, "%s[%10s]\n", info, f_temp->name);
f_temp = f_temp->next;
}
return (*env)->NewStringUTF(env, info);
}
这里是参照的雷神的文章,写了几个测试的方法。
最后就是调用了。
public class MainActivity extends AppCompatActivity {
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("hello_jni");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void click1(View v) {
Toast.makeText(this, new NativeMethods().helloWorld(), Toast.LENGTH_SHORT).show();
}
public void click2(View v) {
Toast.makeText(this, new NativeMethods().urlprotocolinfo(), Toast.LENGTH_SHORT).show();
}
public void click3(View v) {
Toast.makeText(this, new NativeMethods().avformatinfo(), Toast.LENGTH_SHORT).show();
}
public void click4(View v) {
Toast.makeText(this, new NativeMethods().avcodecinfo(), Toast.LENGTH_SHORT).show();
}
public void click5(View v) {
Toast.makeText(this, new NativeMethods().avfilterinfo(), Toast.LENGTH_SHORT).show();
}
}
然后是生成so 。自己写Android.mk文件,放在jni文件夹里面。
# FFmpeg library的
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := avcodec
LOCAL_SRC_FILES := libavcodec-57.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := avdevice
LOCAL_SRC_FILES := libavdevice-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 := postproc
LOCAL_SRC_FILES := libpostproc-54.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)
#自己的so文件
include $(CLEAR_VARS)
LOCAL_MODULE := hello_jni
LOCAL_SRC_FILES := hello_jni.c
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_LDLIBS := -llog -lz
LOCAL_SHARED_LIBRARIES := avcodec avdevice avfilter avformat avutil postproc swresample swscale
include $(BUILD_SHARED_LIBRARY)
到jni目录执行ndk-build命令即可。在生成so过程中可能出现错误,
Unable to recognise the format of the input file x86_64/libavcodec-57.so
make: * libavcodec-57.so] Error 1
make: * Deleting file libavcodec-57.so’
我是参考http://blog.csdn.net/darwinlong/article/details/48178131
由于我是在linux虚拟下编译的ffmpeg,x86 x86-64版本架构不匹配。这里就先跳过了,不生成x86架构的so了,做法是新建立application.mk指定只编译 armeabi。 Application.mk 也是在jni目录下。
Application.mk
APP_ABI := armeabi armeabi-v7a
Application.mk 仅此一行就可以了。
然后在将 在linux环境编译的so 和头文件复制到jni目录,即可。
最后执行ndk-build就会自动生成so 。
在手机上实验就行了, 模拟器因为没有生成x86的so,所以说是不行了。
更多推荐
所有评论(0)