Monday, November 28, 2016

Android 使用FFMPEG編出的.so檔 建立第一個專案

建立好NDK環境後

移植到Android平台

接下來寫在Android studio寫一個示例,調用ffmpeg中方法
建一個工程:在src/main下建一個jni目錄

將lib文件夾中的pkgconfig目錄刪除只保留so文件,然後將include和lib兩個目錄一起copy到你的 jni資料夾下去編譯,下圖因為我已經編譯完成會有其他檔案


建置好支援JNI的Android專案後,先別急著開始寫C/C++程式,應該要先規劃好Java程式中哪些地方需要使用到由C/C++程式語言實作的方法或是函數,這些方法都需要先使用native修飾子來進行宣告,並且使用System類別下的loadLibrary方法來讀入指定名稱的.so函式庫。用以下程式為例:

MainActivity.java


import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView infoText = (TextView)findViewById(R.id.text_libinfo);
infoText.setMovementMethod(ScrollingMovementMethod.getInstance());
Button button = (Button)this.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener(){

@Override
public void onClick(View view) {
infoText.setText(avcodecinfo());
}
});

}
//JNI
public native String avcodecinfo();
static{
System.loadLibrary("avutil-55");
System.loadLibrary("swresample-2");
System.loadLibrary("avcodec-57");
System.loadLibrary("avformat-57");
System.loadLibrary("swscale-4");
System.loadLibrary("avfilter-6");
System.loadLibrary("sffhelloworld");

}
}

.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.cheng.myapplication.MainActivity">


<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
android:id="@+id/textView" />

<TextView
android:text="TextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView"
android:layout_alignParentStart="true"
android:layout_marginTop="122dp"
android:id="@+id/text_libinfo" />

<Button
android:text="Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView"
android:layout_alignParentStart="true"
android:layout_marginTop="13dp"
android:id="@+id/button" />
</RelativeLayout>

接著再使用JDK提供的「javah」工具,來將指定類別轉換成JNI使用的標頭檔
MainActivity.java按右鍵 -> External Tools -> Javah
執行完畢後,在jni ([c])目錄中會出現 標頭檔.h
將使用javah產生出來的.h檔案移動到「jni」目錄下,然後在別的.c或是.cpp檔案中include,並實作出其中宣告的函數

simplest_ffmpeg_helloworld.c


//
// Created by cheng on 2016/11/29.
//
#include "com_example_cheng_myapplication_MainActivity.h"
#include <stdio.h>
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavfilter/avfilter.h"

#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 Java_com_example_cheng_myapplication_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 != NULL){
sprintf(info, "%s[Dec]\n", info);
}else{
sprintf(info, "%s[Enc]\n", info);
}
switch(c_temp->type){
case AVMEDIA_TYPE_VIDEO:
sprintf(info, "%s[Video]\n", info);
break;
case AVMEDIA_TYPE_AUDIO:
sprintf(info, "%s[Audio]\n", info);
break;
default:
sprintf(info, "%s[Other]\n", info);
break;
}
sprintf(info, "%s[%10s]\n", info,c_temp->name);
c_temp = c_temp->next;
}

return (*env).NewStringUTF(info);
}
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 := libsffhelloworld
LOCAL_SRC_FILES := simplest_ffmpeg_helloworld.cpp
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)
LOCAL_MODULE = "xxxx"就是System.loadLibrary的名子

預設的情況下,NDK只會編譯出armeabi架構的.so檔案,可以用在任何ARM架構的Android裝置上。如果要針對ARMv7架構優化,或是使.so檔能在x86架構或是MIPS架構上執行的話,可以在「jni」目錄下,增加「Application.mk」檔案,並且設定「APP_ABI」參數的值。
例如要編譯出ARM與x86的.so檔案,可以這樣設定:
Application.mk
APP_ABI := armeabi
開始編譯目標so,在Studio中的Terminal面板中:執行ndk-build (在此之前記得要在local.properties下配置ndk目錄)


之後編譯有問題

建立build.gradle(app)
    defaultConfig {
applicationId "com.example.cheng.myapplication"
minSdkVersion 19
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
ndk{
moduleName "libsffhelloworld"
}

sourceSets.main{
jni.srcDirs=[]
jniLibs.srcDir "src/main/jniLibs"
}
}
在gradle.properties
android.useDeprecatedNdk=true

在 libavutil\common.h 加入

#define __STDC_CONSTANT_MACROS
#define inline __inline

#ifndef INT64_C
#define INT64_C(c) (c ## LL)
#define UINT64_C(c) (c ## ULL)
#endif

接這又出現錯誤= =

要再.h檔加入
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#endif


再編譯一次成功的話就燒入吧



Reference:

http://blog.csdn.net/hejjunlin/article/details/52661331
https://magiclen.org/android-jni/
http://jojosula001.pixnet.net/blog/post/173475381-%5Bwindows%5D-using-ffmpeg-in-visual-c%2B%2B
https://github.com/dxjia/ffmpeg-compile-shared-library-for-android
http://blog.csdn.net/leixiaohua1020/article/details/47008825

No comments:

Post a Comment