當前位置:
首頁 > 最新 > Android編譯調用FFmpeg API,自己寫方法,編譯so庫

Android編譯調用FFmpeg API,自己寫方法,編譯so庫

作者:譚東

時間:2017年9月19日

環境:Windows 8.1專業版

NDK版本:android-ndk-r14b

FFmpeg版本:FFmpeg 3.0.2 「Einstein」

我這裡使用的Android Studio為2.3.3版本。

大名鼎鼎的FFmpeg可以說全球出名,基本上視頻處理都是它,但是使用FFmpeg的技術門檻和難度也相對要求高一些。

那麼我就先給大家簡單引用別人的描述簡單介紹下FFmpeg吧:

FFmpeg是一套可以用來記錄、轉換數字音頻、視頻,並能將其轉化為流的開源計算機程序。採用LGPL或GPL許可證。它提供了錄製、轉換以及流化音視頻的完整解決方案。它包含了非常先進的音頻/視頻編解碼庫libavcodec,為了保證高可移植性和編解碼質量,libavcodec里很多code都是從頭開發的。多媒體視頻處理工具FFmpeg有非常強大的功能包括視頻採集功能、視頻格式轉換、視頻抓圖、給視頻加水印等。

FFmpeg被許多開源項目採用,比如ffmpeg2theora,VLC, MPlayer, HandBrake, Blender, Google Chrome等。還有DirectShow/VFW的ffdshow(external project)和QuickTime的Perian (external project)也採用了FFmpeg。

由於FFmpeg是在LGPL/GPL協議下發布的(如果使用了其中一些使用GPL協議發布的模塊則必須使用GPL協議),任何人都可以自由使用,但必須嚴格遵守LGPL/GPL協議。有很多播放軟體都使用了FFmpeg的代碼,但它們並沒有遵守LGPL/GPL協議,沒有公開任何源代碼。我們應該對這種侵權行為表示恥辱。

2009年加入FFmpeg恥辱黑名單的播放軟體:暴風影音、QQ影音、KMP、GOM Player、PotPlayer(2010)都在其列。

2009年2月,韓國名軟KMPlayer被FFmpeg開源項目發現使用了它們的代碼和二進位文件,但是沒有按照規定/慣例開放相應說明/源碼。因此被人舉報,進入了FFmpeg官網上的恥辱黑名單。

2009年11月,網友roo_zhou向FFmpeg舉報,指出QQ影音的credit只給出了修改的FFmpeg源碼下載,聲稱是LGPL許可證。但實際是修改過的ffdshow,採用的是GPL許可證,之後QQ影音被正式加入到FFmpeg恥辱名單之列。

Libav項目啟動之後,FFmpeg官方版本也仍然在一直維護中。FFmpeg與libav屬於獨立的兩個項目。

-------------------------------------------------------------------------------------------------------------------------

就先介紹到這裡。

FFmpeg大致有兩種使用方式,第一種是命令行,這個最簡單也最直接。不過這個比較適合Linux下,移動平台和Windows下還是需要單獨編譯源碼,自己寫C方法調用FFmpeg的API,然後編譯成庫,例如Android平台要編譯成so庫。這樣難度和門檻就加大了,你要讀懂和了解FFmpeg的C方法,C源碼和API文檔。其實很多用法可以看下官方英文文檔,官方也有很多參考資料。

好了。接下來給大家講解下編譯和調用FFmpeg的C的API。

首先肯定是先要去FFmpeg官網下載最新的源碼,然後在Linux下進行編譯了。Android平台需要編譯so庫。

官網源碼下載地址:http://ffmpeg.org/download.html

我之前寫過一篇文章,講解如何編譯FFmpeg的。

《Ubuntu下編譯Android版本的ffmepg so庫及源碼》以及《Ubuntu編譯調用FFmpeg so庫Api方法例子》具體如何編譯我就不說了,編譯好後,進行下面的步驟。

1、Android Studio新建支持C++的項目。勾選Include C++ support。

2、新建好的項目大致結構這樣。主要關注下圖裡紅色的幾個部分。

3.、接下來在項目的src下的main目錄新建jniLibs目錄,再新建對應的架構平台的文件夾名,然後把so庫放進去。我這裡只編譯了armeabi平台的so庫,所以結構是下圖這樣的。

4、把FFmpeg編譯後的源碼.h頭文件拷貝到項目的libs目錄里。

5、可以大致看下裡面的,h里的方法和定義。

6、接下來就是編寫配置我們的CMakeLists.txt了。下面是我寫好的配置,大家可以參考。

# Sets the minimum version of CMake required to build the native

# library. You should either keep the default value or only pass a

# value of 3.4.0 or lower.

cmake_minimum_required(VERSION 3.4.1)

find_library( # Sets the name of the path variable.

log-lib

# Specifies the name of the NDK library that

# you want CMake to locate.

log )

set(distribution_DIR $/../../../../libs)

add_library( avutil-55

SHARED

IMPORTED )

set_target_properties( avutil-55

PROPERTIES IMPORTED_LOCATION

../../../../src/main/jniLibs/armeabi/libavutil-55.so )

add_library( swresample-2

SHARED

IMPORTED )

set_target_properties( swresample-2

PROPERTIES IMPORTED_LOCATION

../../../../src/main/jniLibs/armeabi/libswresample-2.so )

add_library( avcodec-57

SHARED

IMPORTED )

set_target_properties( avcodec-57

PROPERTIES IMPORTED_LOCATION

../../../../src/main/jniLibs/armeabi/libavcodec-57.so )

add_library( avfilter-6

SHARED

IMPORTED)

set_target_properties( avfilter-6

PROPERTIES IMPORTED_LOCATION

../../../../src/main/jniLibs/armeabi/libavfilter-6.so )

add_library( swscale-4

SHARED

IMPORTED)

set_target_properties( swscale-4

PROPERTIES IMPORTED_LOCATION

../../../../src/main/jniLibs/armeabi/libswscale-4.so )

add_library( avdevice-57

SHARED

IMPORTED)

set_target_properties( avdevice-57

PROPERTIES IMPORTED_LOCATION

../../../../src/main/jniLibs/armeabi/libavdevice-57.so )

add_library( avformat-57

SHARED

IMPORTED)

set_target_properties( avformat-57

PROPERTIES IMPORTED_LOCATION

../../../../src/main/jniLibs/armeabi/libavformat-57.so )

set(CMAKE_CXX_FLAGS "$ -std=gnu++11")

add_library( native-lib

SHARED

src/main/cpp/native-lib.cpp )

include_directories(libs/include)

#target_include_directories(native-lib PRIVATE libs/include)

target_link_libraries( native-lib swresample-2 avcodec-57 avfilter-6 swscale-4 avdevice-57 avformat-57

$ )

7、接下來我們看下默認生成的cpp文件夾里的native-lib.cpp的裡面的C方法例子規範。

#include

#include

extern "C"

JNIEXPORT jstring JNICALL

Java_com_tandong_testffmpeg_MainActivity_stringFromJNI(

JNIEnv *env,

jobject /* this */) {

std::string hello = "Hello from C++";

return env->NewStringUTF(hello.c_str());

}

很簡單對不對。我們可以按照這個規範寫我們的其他測試和調用FFmpeg的API的C++方法了。

#include

#include

extern "C" {

#include

#include

#include

JNIEXPORT jstring JNICALL

Java_com_tandong_testffmpeg_MainActivity_stringFromJNI(

JNIEnv *env,

jobject /* this */) {

std::string hello = "Hello from C++";

return env->NewStringUTF(hello.c_str());

}

JNIEXPORT jstring JNICALL

Java_com_tandong_testffmpeg_MainActivity_stringFromJNI2(

JNIEnv *env,

jobject /* this */) {

return env->NewStringUTF("測試");

}

/**

* 獲取FFmpeg支持的協議格式

*/

JNIEXPORT jstring JNICALL

Java_com_tandong_testffmpeg_MainActivity_getUrlProtocolInfo(

JNIEnv *env, jobject) {

char info[40000] = ;

av_register_all();

struct URLProtocol *pup = NULL;

struct URLProtocol **p_temp = &pup;

avio_enum_protocols((void **) p_temp, 0);

while ((*p_temp) != NULL) {

sprintf(info, "%sInput: %s
", info, avio_enum_protocols((void **) p_temp, 0));

}

pup = NULL;

avio_enum_protocols((void **) p_temp, 1);

while ((*p_temp) != NULL) {

sprintf(info, "%sInput: %s
", info, avio_enum_protocols((void **) p_temp, 1));

}

return env->NewStringUTF(info);

}

/**

* 獲取avcodec的庫的版本號

*/

JNIEXPORT jstring JNICALL

Java_com_tandong_testffmpeg_MainActivity_getAvCodecVersion(

JNIEnv *env, jobject) {

char version[50];

avcodec_register_all();

sprintf(version, "%d", avcodec_version());

return env->NewStringUTF(version);

}

/**

* 獲取avformat的庫的協議

*/

JNIEXPORT jstring JNICALL

Java_com_tandong_testffmpeg_MainActivity_getAvFormatLicense(

JNIEnv *env, jobject) {

std::string license = avformat_license();

return env->NewStringUTF(license.c_str());

}

}

這些是我寫的幾個調用API的方法,這些都是很簡單的,後面有很多複雜的功能,寫法就會更加的複雜,難度也會更大。

我們可以根據它的.h頭文件或者官方源碼文檔,查看這個方法傳入的參數含義和類型以及返回的類型及作用。

下面是官方文檔查看及源碼。

8、緊接著,我們編寫我們的JAVA來調用JNI的方法。

import android.support.v7.app.AppCompatActivity;

import android.os.Bundle;

import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

// Example of a call to a native method

TextView tv = (TextView) findViewById(R.id.sample_text);

tv.setText(getUrlProtocolInfo());

}

/**

* A native method that is implemented by the native-lib native library,

* which is packaged with this application.

*/

public native String stringFromJNI();

public native String stringFromJNI2();

public native String getUrlProtocolInfo();

public native String getAvCodecVersion();

public native String getAvFormatLicense();

// Used to load the native-lib library on application startup.

static {

System.loadLibrary("native-lib");

}

}

這個就很簡單了,就不給大家解釋了。

9、最後就是編譯打包運行了,編譯運行後,就會生成我們的so庫。

調用編譯就先給大家講解到這裡,請繼續關注。

(版權所有,尊重版權)

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 譚東jay 的精彩文章:

TAG:譚東jay |