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庫。
調用編譯就先給大家講解到這裡,請繼續關注。
(版權所有,尊重版權)
TAG:譚東jay |