Android最简单的基于FFmpeg的例子(二)---测试编译好的SO库

FFmpeg

FFmpeg使用第二步,测试编译好的SO库。

使用AndroidStudio创建测试工程

创建工程,并新建 jni 目录,如图:

create_test_project

界面实现

这是一个简单的例子,所以只有一个Activity,Activity里有四个按钮和一个TextView(ScrollView包裹),直接上代码吧:

MainActivity.java

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
      
      
package com.ihubin.ffmpegstudy;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void format(View view){
}
public void codec(View view){
}
public void filter(View view){
}
public void config(View view){
}
}

activity_main.xml

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
      
      
<?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>

将编译好的FFmpeg放入工程

在jni目录新建目录include存放头文件,如图:

create_header_path

将生成的so库放入jni目录,新建Android.mk\Application.mk\ffmpegdemo.c三个空文件备用,如图:

copy_so_to_jni

准备调用C语言函数。使用JNI调用C语言代码有两点需要做的步骤:

· 声明C语言函数对应的Java函数

· 声明要加载的类库

需要注意,C语言函数的声明要加上“native”关键字;加载类库的时候需要使用“System.loadLibrary()”方法。

所以改写MainActivity.java为:

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
      
      
package com.ihubin.ffmpegstudy;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
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( "ffmpegdemo");
}
//JNI
public native String avformatinfo();
public native String avcodecinfo();
public native String avfilterinfo();
public native String configurationinfo();
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.textview);
}
public void format(View view){
textView.setText(avformatinfo());
}
public void codec(View view){
textView.setText(avcodecinfo());
}
public void filter(View view){
textView.setText(avfilterinfo());
}
public void config(View view){
textView.setText(configurationinfo());
}
}

编写C语言端代码

获取C语言的接口函数声明

根据Java对于C语言接口的定义,生成相应的接口函数声明。这一步需要用到JDK中的“javah”命令。命令行切换到src/main/java文件夹下,输入如下命令:

      
      
1
      
      
javah com.ihubin.ffmpegstudy.MainActivity

在src/main/java目录下会生成头文件,com_ihubin_ffmpegstudy_MainActivity.h,如图:

generate_header_file

将com_ihubin_ffmpegstudy_MainActivity.h移动到jni目录,如图:

move_headerfile_to_jni

在ffmpegdemo.c中实现生成的头文件中的方法

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
      
      
#include <stdio.h>
#include "com_ihubin_ffmpegstudy_MainActivity.h"
#include "include/libavformat/avformat.h"
#include "include/libavcodec/avcodec.h"
#include "include/libavutil/avutil.h"
#include "include/libavfilter/avfilter.h"
//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
/**
* com.ihubin.ffmpegstudy.MainActivity.avformatinfo()
* AVFormat Support Information
*/
JNIEXPORT jstring JNICALL Java_com_ihubin_ffmpegstudy_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);
}
/**
* com.ihubin.ffmpegstudy.MainActivity.avcodecinfo()
* AVCodec Support Information
*/
JNIEXPORT jstring JNICALL Java_com_ihubin_ffmpegstudy_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);
}
/**
* com.ihubin.ffmpegstudy.MainActivity.avfilterinfo()
* AVFilter Support Information
*/
JNIEXPORT jstring JNICALL Java_com_ihubin_ffmpegstudy_MainActivity_avfilterinfo(JNIEnv *env, jobject obj)
{
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);
}
/**
* com.ihubin.ffmpegstudy.MainActivity.urlprotocolinfo()
* Protocol Support Information
*/
JNIEXPORT jstring JNICALL Java_com_ihubin_ffmpegstudy_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);
}

编写Android.mk

Android.mk

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
      
      
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 := ffmpegdemo
LOCAL_SRC_FILES := ffmpegdemo.c
LOCAL_C_INCLUDES += $(LOCAL_PATH)/include
LOCAL_LDLIBS := -llog -lz
LOCAL_SHARED_LIBRARIES := avcodec avfilter avformat avutil swresample swscale
include $(BUILD_SHARED_LIBRARY)

编写Application.mk(可选)

Application.mk中的APP_ABI设定了编译后库文件支持的指令集,默认使用“armeabi”.

APP_ABI取值为“all”表示支持所有的指令集.

Application.mk

      
      
1
2
      
      
APP_ABI := armeabi-v7a
APP_MODULES := libffmpegdemo

运行ndk-build

编写完Android的Makefile文件之后,就可以运行ndk-build编译生成可以通过JNI调用的类库了。

ndk-build本身是一个脚本,位于NDK根目录下。切换到Android程序src/main/jni目录中,直接执行该脚本就可以了。

△ 需要先配置NDK环境变量!!需要先配置NDK环境变量!!需要先配置NDK环境变量!!

运行完就可以看到生成的SO文件了,如图:

compile_ffmpegdemo_jni_complete

生成是SO文件在 src/main/libs/armeabi-v7a中:

generate_so_file_in_libs

这时就可以运行程序查看效果了~~Well Done !

PS | 补充

直接运行会报错:

test_ffmpeg_demo_error1

解决方法:

修改build.gradle文件:

      
      
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
      
      
apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.2"
defaultConfig {
applicationId "com.ihubin.ffmpeg_study"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
// ↓↓↓↓ 加上这几行 ↓↓↓↓
sourceSets.main{
jniLibs.srcDir 'src/main/libs'
jni.srcDirs=[]
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile( 'proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: [ '*.jar'])
compile 'com.android.support:appcompat-v7:23.2.1'
}

查看效果

demo_test_ffmpeg

源码

源码地址在这里 

Module:FFmpeg-Test-SO

Demo APK下载地址:https://www.pgyer.com/FFmpegTestSO

也可以扫码下载体验:

app-qrcode-FFmpegTestSO.png


来源地址:

http://www.ihubin.com/blog/android-ffmpeg-demo-2/


参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值