上篇文章介绍了怎样用虚拟机把ffmpeg编译成android可用的动态库,这次把动态库引入eclipse android工程,并调用ffmpeg类库
注意,在网页上粘贴的代码,很多符号会变成中文的,导致很难发现的错误...
如果没有so库的朋友,可以参考我的上一篇博客,之中也有编译好的下载地址,ndk版本是r13,ffmpeg版本是3.1.3,abi是armeabi-v7a,在华为4x上运行通过
下面开始:
1.so库和头文件复制到安卓工程jni目录下,并创建Android.mk,Application.mk,HelloJni.c文件,
Android.mk:指明so库如何引入,编译那些c文件,因为ffmpeg是c写的,所以写c++调用可能会有问题,这个文件描述如何编译动态库
Application.mk:可以指明编译时生成那些目标cpu架构的so,我的华为4x是armeabi-v7a
HelloJni.c:实现java的native方法
JniUtils.java
package com.example.ffmpegtest001;
public class JniUtils {
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("hello-jni");
}
public native String stringFromJNI();
public native String avformatinfo();
public native String avcodecinfo();
public native String avfilterinfo();
public native String configurationinfo();
}
MainActivity.javapackage com.example.ffmpegtest001;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends ActionBarActivity {
private TextView textView;
JniUtils jniUtils;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
jniUtils = new JniUtils();
textView = (TextView) findViewById(R.id.textview);
}
public void format(View view) {
textView.setText(jniUtils.avformatinfo());
}
public void codec(View view) {
textView.setText(jniUtils.avcodecinfo());
}
public void filter(View view) {
textView.setText(jniUtils.avfilterinfo());
}
public void config(View view) {
textView.setText(jniUtils.configurationinfo());
}
}
Android.mk
LOCAL_PATH := $(call my-dir)
#ffmpeg
include $(CLEAR_VARS)
LOCAL_MODULE:= avcodec
LOCAL_SRC_FILES:= libavcodec-57.so
LOCAL_MODULE_FILENAME:=libavcodec-57
include $(PREBUILT_SHARED_LIBRARY) ##指明此模块需要预编译
include $(CLEAR_VARS)
LOCAL_MODULE:= avfilter
LOCAL_SRC_FILES:= libavfilter-6.so
LOCAL_MODULE_FILENAME:=libavfilter-6
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:= avformat
LOCAL_SRC_FILES:= libavformat-57.so
LOCAL_MODULE_FILENAME:=libavformat-57
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:= avutil
LOCAL_SRC_FILES:= libavutil-55.so
LOCAL_MODULE_FILENAME:=libavutil-55
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:= swresample
LOCAL_SRC_FILES:= libswresample-2.so
LOCAL_MODULE_FILENAME:=libswresample-2
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE:= swscale
LOCAL_SRC_FILES:= libswscale-4.so
LOCAL_MODULE_FILENAME:=libswscale-4
include $(PREBUILT_SHARED_LIBRARY)
#end
#my
include $(CLEAR_VARS)
#指明自己模块的名称
LOCAL_MODULE := hello-jni
#指明自己模块需要的源代码
LOCAL_SRC_FILES := HelloJni.c
#指明.so库的头文件路径
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
#指明需要使用的ndk自带模块
LOCAL_LDLIBS := -llog -lz -landroid
#指明自己模块编译时需要依赖的模块
LOCAL_SHARED_LIBRARIES := avcodec avfilter avformat avutil swresample swscale
#指明自己的模块编译为动态库
include $(BUILD_SHARED_LIBRARY)
Application.mk#指明编译时生成armeabi-v7a架构的so文件
APP_ABI := armeabi-v7a
HelloJni.c
#include <string.h>
//#include "com_example_ffmpegtest001_JniUtils.h"
#include <stdio.h>
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include "include/libavutil/avutil.h"
#include "include/libavfilter/avfilter.h"
//http://www.pps.tv/w_19rt0grxmx.html 很好的视频
//Log
#ifdef ANDROID
#include <jni.h>
#include <android/log.h>
#define LOGE(format, ...) __android_log_print(ANDROID_LOG_ERROR, "(>_<)", format, ##__VA_ARGS__)
#else
#define LOGE(format, ...) printf("(>_<) " format "\n", ##__VA_ARGS__)
#endif
JNIEXPORT jstring JNICALL Java_com_example_ffmpegtest001_JniUtils_stringFromJNI(
JNIEnv* env, jobject thiz) {
//return env->NewStringUTF("Hello from JNI ! Compiled with ABI ");
return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI ");
}
JNIEXPORT jstring JNICALL Java_com_example_ffmpegtest001_JniUtils_avformatinfo(
JNIEnv* env, jobject thiz) {
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(info);
return (*env)->NewStringUTF(env, info);
}
JNIEXPORT jstring JNICALL Java_com_example_ffmpegtest001_JniUtils_avcodecinfo(
JNIEnv* env, jobject thiz) {
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);
// return env->NewStringUTF(info);
}
JNIEXPORT jstring JNICALL Java_com_example_ffmpegtest001_JniUtils_avfilterinfo(
JNIEnv* env, jobject thiz) {
char info[40000] = { 0 };
avfilter_register_all();
AVFilter *f_temp = (AVFilter *) avfilter_next(NULL);
int i = 0;
while (f_temp != NULL) {
sprintf(info, "%s[%10s]\n", info, f_temp->name);
f_temp = f_temp->next;
}
return (*env)->NewStringUTF(env, info);
// return env->NewStringUTF( info);
}
JNIEXPORT jstring JNICALL Java_com_example_ffmpegtest001_JniUtils_configurationinfo(
JNIEnv* env, jobject thiz) {
char info[10000] = { 0 };
av_register_all();
sprintf(info, "%s\n", avcodec_configuration());
//LOGE("%s", info);
//return env->NewStringUTF(info);
return (*env)->NewStringUTF(env, info);
}
其中每个c方法对应一个java的native方法
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.ihubin.ffmpegstudy.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:onClick="format"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Format" />
<Button
android:onClick="codec"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Codec" />
<Button
android:onClick="filter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Filter" />
<Button
android:onClick="config"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Config" />
</LinearLayout>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<TextView
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</ScrollView>
</LinearLayout>
我的华为4x的运行截图