## C++(CPP)在Android開發中的應用 ##
#### 使用純Java開發App的缺點 ###
- 在某些場合下,使用純Java開發Android應用程序不完美,比如:
- 有高性能算法,Java語言無法滿足
- 有跨平臺需求,希望將App移植到iOS(現在很多設計都是C語言和C++來寫底層,Android來寫界面)
- 已有代碼的重用
#### 為什么使用NDK ####
1. 代碼的保護。由于apk 的java 層代碼很容易被反編譯,而C/C++庫反編譯難度較大。
2. 可以方便地使用現存的開源庫。大部分現存的開源庫都是用C/C++代碼編寫的。
3. 提高程序的執行效率。將要求高性能的應用邏輯使用C 開發,從而提高應用程序的執行效率。
4. 便于移植。用C/C++寫得庫可以方便在其他的嵌入式平臺上再次使用。
### 引入NDK ####
- 早在Android 1.6(2009年)時,google就提供了NDK(native development kit),NDK包括了一套Android的交叉編譯環境和開發庫,利用它可以編寫C/C++程序,并編譯成Android環境下使用的動態庫,Java代碼通過Jni規范,調用C/C++寫的動態庫。
- NDK
- NDK是一系列工具的集合。
- 它提供了一系列的工具,幫助開發者快速開發C(或C++)的動態庫,并能自動將so和java應用一起打包成apk。這些工具對開發者的幫助是巨大的。它集成了交叉編譯器,并提供了相應的mk文件隔離CPU、平臺、ABI等差異,開發人員只需要簡單修改mk文件(指出“哪些文件需要編譯”、“編譯特性要求”等),就可以創建出so。它可以自動地將so和Java應用一起打包,極大地減輕了開發人員的打包工作。
- NDK 提供了一份穩定、功能有限的API 頭文件聲
- Google 明確聲明該API 是穩定的,在后續所有版本中都穩定支持當前發布的API。從該版本的NDK中看出,這些API 支持的功能非常有限,包含有:C 標準庫(libc)、標準數學庫(libm)、壓縮庫(libz)、Log 庫(liblog)。
- JNI
- JavaNative Interface (JNI)標準是java平臺的一部分,JNI是Java語言提供的Java和C/C++相互溝通的機制,Java可以通過JNI調用本地的C/C++代碼,本地的C/C++的代碼也可以通過JNI調用java代碼。JNI 是本地編程接口,Java和C/C++互相通過的接口。Java通過C/C++使用本地的代碼的一個關鍵性原因在于C/C++代碼的高效性。
- 總結
- NDK就是為我們生成了c/c++的動態鏈接庫而已,JNI只不過是java和c溝通的工具,兩者與Android沒有半毛錢關系,只因為安卓是java程序開發然后jni又能與c溝通,所以使“Java+C”的開發方式終于轉正。
- Android是JVM架設在Linux之上的架構。所以無論如何,在Linux OS層面,都應該可以跑C/C++程序。
- Android Native C就是使用C/C++程序直接跑到Linux OS層面上的程序。與其它平臺類似,只需要交叉編譯后。并得到Linux OS root權限,就可以直接跑起來了。
- 官方定義:Android NDK 是一套允許您使用原生代碼語言(例如 C 和 C++)實現部分應用的工具集。在開發某些類型應用時,這有助于您重復使用以這些語言編寫的代碼庫。
- 目前最新的Android Studio 2.2中,集成了C/C++開發環境,開發人員在使用C/C++更加簡單了。(以前,寫Android的jni庫時,需要用到Android.mk,Application.mk這些配置,來交叉編譯)
- Android.mk,負責配置如下內容:
- (1) 模塊名(LOCAL_MODULE)
- (2) 需要編譯的源文件(LOCAL_SRC_FILES)
- (3) 依賴的第三方庫(LOCAL_STATIC_LIBRARIES,LOCAL_SHARED_LIBRARIES)
- (4) 編譯/鏈接選項(LOCAL_LDLIBS、LOCAL_CFLAGS)
- Application.mk,負責配置如下內容:
- (1) 目標平臺的ABI類型(默認值:armeabi)(APP_ABI)
- 
- (2) Toolchains(默認值:GCC 4.8)
- (3) C++標準庫類型(默認值:system)(APP_STL)
- (4) release/debug模式(默認值:release)
- [NDK中文官方開發技術文檔 ](https://developer.android.google.cn/ndk/index.html)
- NDK下載地址
- [NDK 下載 ](https://developer.android.google.cn/ndk/downloads/index.html)
- [AndroidDevTools](http://www.androiddevtools.cn/)(隨時更新官網最新版本)
- [官方下載](https://developer.android.com/ndk/downloads/index.html)(需要翻墻,而且翻墻后網站的NDK版本都不是最新版本)
- 網友找到的一些版本----[NDK各個版本鏈接](http://blog.csdn.net/shuzfan/article/details/52690554)
- 也可以打開---AndroidDevTools網站的上各個大學的鏡像網站,找到Android,點擊repository。可以看到最新最全的各個版本(包括sdk、sdktools、platform、platform-tools、Android源碼等等官網上所持有的所有的資源),如下圖是打開的大連東軟信息學院鏡像服務器中的Android資源
- 注意:NDK的r10版本以上,就已經集成了Cygwin的UNIX虛擬機的功能,不需要安裝Cygwin的UNIX環境
- 
-
- 下載完成,解壓到自己認為合適的目錄,**注意:目錄路徑中不能有中文和空格**
###創建NDK項目(支持 C/C++ 的新項目)(project)
- 創建項目過程中,遇到問題,建議參考官網文章----[向您的項目添加 C 和 C++ 代碼](https://developer.android.com/studio/projects/add-native-code.html)(貌似需要翻墻,沒法翻墻的可以查看[在 Android Studio 2.2 中愉快地使用 C/C++ ](http://blog.csdn.net/wl9739/article/details/52607010)),而且這篇文章提到以下幾點
- Android Studio 用于構建原生庫的默認工具是 CMake。由于很多現有項目都使用構建工具包編譯其原生代碼,Android Studio 還支持 [ndk-build](https://developer.android.com/ndk/guides/ndk-build.html)。如果您想要將現有的 ndk-build 庫導入到您的 Android Studio 項目中,請參閱介紹如何配置 Gradle 以[關聯到您的原生庫](https://developer.android.com/studio/projects/add-native-code.html#link-gradle)的部分。不過,如果您在創建新的原生庫,則應使用 CMake。
- 下載 NDK 和構建工具
要為您的應用編譯和調試原生代碼,您需要以下組件:
- [Android 原生開發工具包 (NDK)](https://developer.android.com/ndk/index.html):這套工具集允許您為 Android 使用 C 和 C++ 代碼,并提供眾多平臺庫,讓您可以管理原生 Activity 和訪問物理設備組件,例如傳感器和觸摸輸入。
- [CMake](https://cmake.org/):一款外部構建工具,可與 Gradle 搭配使用來構建原生庫。如果您只計劃使用 ndk-build,則不需要此組件。[使用CMake變量](https://developer.android.com/ndk/guides/cmake.html#variables)
- [LLDB](http://lldb.llvm.org/):一種調試程序,Android Studio 使用它來[調試原生代碼](https://developer.android.com/studio/debug/index.html)
- 您可以使用 SDK 管理器安裝這些組件:
- 在打開的項目中,從菜單欄選擇 Tools > Android > SDK Manager。
- 點擊 SDK Tools 標簽。
- 選中 LLDB、CMake 和 NDK 旁的復選框,如圖所示。
- 
#### 創建NDK項目(支持 C/C++ 的新項目)(project)或者在一個已存在的項目中創建NDK module ####
- 創建一個NDK項目
- 創建支持原生代碼的項目與創建任何其他 Android Studio 項目類似,不過還需要額外幾個步驟:把 Include C++ support的勾打上

- 一直next,直到出一個左邊一大白色C++的方形框時,表示這是一個C++的項目,右邊選擇C++標準,可以選擇默認的Toolchain Default,同時可以選擇C++11,因為C++11有更多的新特性和功能。,一般選擇C++11。

- 點擊Finish后,進入工程目錄,如圖所示,除了java文件夾外多了一個cpp文件夾,cpp就是存放c和c++代碼的文件夾,如下圖所示

- 同時,下面為CMakeLists.txt和build.gradle中的內容
- CMakeLists.txt(注釋部分已刪除)
- 
- build.gradle中的內容
- 
- PS:如果在創建project時,沒有勾選,就只是一個簡單的AS的Android項目,這時如果想要創建C++項目,可以參考下面的在module中
- 如何在一個module中添加CPP組件,創建原生源文件
- 一般,我們的項目創建時并不是一個C++項目,而是一個Android普通的項目,如果需要用到NDK,JNI方面的,這時就需要添加CPP組件,原生源文件,之前都是用ndk-build命令來進行NDK開發,所以正如上面這篇文章(向您的項目添加 C 和 C++ 代碼)所述---->由于很多現有項目都使用構建工具包編譯其原生代碼,Android Studio 還支持 ndk-build(已非主流,過時)。如果您想要將現有的 ndk-build 庫導入到您的 Android Studio 項目中,請參閱介紹如何配置 Gradle 以關聯到您的原生庫的部分。不過,如果您在創建新的原生庫,則應使用 CMake。但是還是推薦使用CMake,因為在Android2.2以后的版本,進行JNI開發,更方便
- 同樣,我們需要認真閱讀[《向您的項目添加 C 和 C++ 代碼》](https://developer.android.com/studio/projects/add-native-code.html)的---->”向現有項目添加 C/C++ 代碼“這一小章節,注意CMake構建的腳本 CMakeLists.txt中的內容和build.gradle中的注意細節,一定要認真閱讀[《向您的項目添加 C 和 C++ 代碼》](https://developer.android.com/studio/projects/add-native-code.html),或者可以參考上面創建C++項目的build.gradle和CMakeLists.txt中的內容。
#### 添加 NDK API ####
- Android NDK 提供了一套實用的原生 API 和庫。通過將 [NDK 庫](https://developer.android.com/ndk/guides/stable_apis.html)包含到項目的 CMakeLists.txt 腳本文件中,您可以使用這些 API 中的任意一種。
- [NDK 庫(Android NDK 原生 API)](https://developer.android.com/ndk/guides/stable_apis.html)
- [概覽](https://developer.android.com/ndk/guides/stable_apis.html#purpose)
- [主要的原生 API 更新](https://developer.android.com/ndk/guides/stable_apis.html#mnu)([Android平臺版本對應的API等級](https://developer.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLevels))
- [Android API 級別 3](https://developer.android.com/ndk/guides/stable_apis.html#a3)
- [Android API 級別 4](https://developer.android.com/ndk/guides/stable_apis.html#a4)
- [Android API 級別 5](https://developer.android.com/ndk/guides/stable_apis.html#a5)
- [Android API 級別 8](https://developer.android.com/ndk/guides/stable_apis.html#a8)
- [Android API 級別 9](https://developer.android.com/ndk/guides/stable_apis.html#a9)
- 從 API 級別 9 開始,您可以使用原生代碼編寫整個 Android 應用,無需使用任何 Java。注:在原生代碼中編寫您的應用本身并不能讓您的應用在 VM 中運行。 此外,您的應用仍必須通過 JNI 訪問 Android 平臺的大部分功能
- [Android API 級別 14](https://developer.android.com/ndk/guides/stable_apis.html#a14)
- [Android API 級別 18 ](https://developer.android.com/ndk/guides/stable_apis.html#a18)
#### JNI規范 ####
- 在進行NDK開發時,閱讀官網的《[向您的項目添加 C 和 C++ 代碼](https://developer.android.com/studio/projects/add-native-code.html)》涉及到的[Android 的 JNI 提示](https://developer.android.com/training/articles/perf-jni.html),而在[Android 的 JNI 提示](https://developer.android.com/training/articles/perf-jni.html)中又涉及到了[JNI規范](http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html),來了解JNI如何工作,以及JNI的特點。
- 早已有大神將官方的文檔翻譯(可以說是摘抄)到自己的博客了
- [《Java 本地接口規范》](http://blog.csdn.net/zhanghw0917/article/details/7000377)
- [《Java 本地接口規范》- 簡介 ](http://blog.csdn.net/snowdream86/article/details/6886954)
- [已整理的目錄篇](http://116.196.111.149:8080/jni.htm)
#### APK分析儀 ####
- 如果您想要驗證 Gradle 是否已將原生庫打包到 APK 中,可以使用[APK分析器 ](https://developer.android.com/studio/build/apk-analyzer.html)
- 步驟
- 選擇 Build > Analyze APK。
- 從 app/build/outputs/apk/ 目錄中選擇 APK 并點擊 OK。
- 如下圖所示,您會在 APK 分析器窗口的 lib/<ABI>/ 下看到 編譯好的動態庫`libnative-lib.so`。
- 
- 提示:如果您想要試驗使用原生代碼的其他 Android應用,請點擊 File>New>Import Sample 并從Ndk列表中選擇示例項目。
#### 指定 ABI ####
- APP_ABI
- ABI全稱是:Application binary interface,即:應用程序二進制接口,它定義了一套規則,允許編譯好的二進制目標代碼在所有兼容該ABI的操作系統和硬件平臺中無需改動就能運行。(具體的定義請參考 [百度百科](https://baike.baidu.com/item/ABI/10912305#viewPageContent) 或者 維基百科 )
- 由上述定義可以判斷,ABI定義了規則,而具體的實現則是由編譯器、CPU、操作系統共同來完成的。不同的CPU芯片(如:ARM、Intel x86、MIPS)支持不同的ABI架構,常見的ABI類型包括:armeabi,armeabi-v7a,x86,x86_64,mips,mips64,arm64-v8a等。
- 這就是為什么我們編譯出來的可以運行于Windows的二進制程序不能運行于Mac OS/Linux/Android平臺了,因為CPU芯片和操作系統均不相同,支持的ABI類型也不一樣,因此無法識別對方的二進制程序。
- 而我們所說的“**交叉編譯**(在一個平臺上去編譯另一個平臺上可以執行的本地代碼)”的核心原理也跟這些密切相關,交叉編譯,就是使用交叉編譯工具,在一個平臺上編譯生成另一個平臺上的二進制可執行程序,為什么可以做到?因為交叉編譯工具實現了另一個平臺所定義的ABI規則。我們在Windows/Linux平臺使用Android NDK交叉編譯工具來編譯出Android平臺的庫也是這個道理。
- 支持的 ABI(每個 ABI 支持一個或多個指令集。下圖提供每個 ABI 支持的指令集概覽。)
- 
- 默認情況下,Gradle 會針對 [NDK 支持的 ABI](https://developer.android.com/ndk/guides/abis.html#sa) 將您的原生庫構建到單獨的 .so 文件中,并將其全部打包到您的 APK 中。如果您希望 Gradle 僅構建和打包原生庫的特定 ABI 配置,您可以在模塊級 build.gradle 文件中defaultConfig節點內使用 ndk.abiFilters 標志指定這些配置,如下所示:

- 在大多數情況下,您只需要在 ndk {} 塊中指定 abiFilters(如上所示),因為它會指示 Gradle 構建和打包原生庫的這些版本。不過,如果您希望控制 Gradle 應當構建的配置,并獨立于您希望其打包到 APK 中的配置,請在 defaultConfig.externalNativeBuild.cmake {} 塊(或 defaultConfig.externalNativeBuild.ndkBuild {} 塊中)配置另一個 abiFilters 標志。Gradle 會構建這些 ABI 配置,不過僅會打包您在 defaultConfig.ndk{} 塊中指定的配置。
- 為了進一步降低 APK 的大小,請考慮[配置 ABI APK 拆分](https://developer.android.com/studio/build/configure-apk-splits.html#configure-abi-split),而不是創建一個包含原生庫所有版本的大型 APK,Gradle 會為您想要支持的每個 ABI 創建單獨的 APK,并且僅打包每個 ABI 需要的文件。如果您配置 ABI 拆分,但沒有像上面的代碼示例一樣指定 abiFilters 標志,Gradle 會構建原生庫的所有受支持 ABI 版本,不過僅會打包您在 ABI 拆分配置中指定的版本。為了避免構建您不想要的原生庫版本,請為 abiFilters 標志和 ABI 拆分配置提供相同的 ABI 列表。
#### 從 ndkCompile 遷移 ####
- 如果您使用已過時的ndkCompile ,你應該遷移---使用 CMake 或 ndk-build 生成。因為ndkCompile生成的中間文件Android.mk 給你,遷移到ndk-build 可能是一個簡單的選擇。
- 從ndkCompile遷移到ndk-build,進行如下操作:
1. 使用ndkCompile,至少一次,通過Build > Make Project, 來編譯項目,這將為您生成Android.mk文件。
2. 通過 `project-root/module-root/build/intermediates/ndk/debug/Android.mk. `到找到的自動生成的Android.mk 文件。
3. 遷移Android.mk 文件到其他的目錄,比如和module下的build.gradle同一等級的目錄,這將確保 clean時,Gradle 并不會刪除該腳本文件,
4. 打開Android.mk文件并編輯腳本中的任何路徑,這樣,他們是相對于當前腳本文件的位置。
5. [Link Gradle to the Android.mk file. ](https://developer.android.com/studio/projects/add-native-code.html#link-gradle)
6. 禁用ndkCompile 通過打開build.properties文件并刪除以下行:`android.useDeprecatedNdk = true`
7. 通過單擊工具欄中的Sync Project ,應用這些改變。
#### 需要注意的幾點 ####
- CMakeLists.txt腳本文件,`Android.mk`,`Application.mk`,這2個文件全是通過CMakeLists.txt來寫;如果項目目錄的CPP文件夾下有N個原生源文件,那么CMakeLists.txt中的add_library中就要對應有N個路徑,類似于native-lib.cpp ,對應的add_library就對應的`src/main/cpp/native-lib.cpp`,如果CPP有test.cpp,則add_library對應有`src/main/cpp/test.cpp`;這就是編譯的配置,而且相互之間空格隔開;
- 這配置和module目錄下的build.gradle的`android--->externalNativeBuild--->cmake--->path "CMakeLists.txt",`編譯build時,通過這個路徑path,找到CMakeLists.txt里面的構建命令,這是相互對應的。CMake是Unix自動化固件,跨平臺
- find_library是鏈接靜態庫,如果有其他的.so庫,就寫在find_library節點內。
- 在使用Cmake插件之前,是在module目錄下的build.gradle中配置NDK選項,以后就不需要在module目錄下的build.gradle中NDK,因為這些操作,都在CMakeLists.txt中配置好了
#### 具體的創建工程 ####
- JNI知識總結
- cpp中原生源文件(.c、.cpp)方法名的命名規則
- 這里的命名規則只適用于跟java 文件中的native 方法相對應C 語言函數,而C 語言中的其他函數命名只要符合C 語言規則就行。java 文件中的native 方法有N個,對應的源文件中就有N個函數,來實現native方法。
- C++調用Java
#### Java和C++字符串轉換 ####
- JNI 基本類型和本地等效類型的對應表格如下:

- 引用類型,JNI 還包含了很對對應于不同Java 對象的引用類型,JNI 引用類型的組織層次如下圖

- Java中任何一種類型,在Jni中都有一種對應的類型,比如:String--->jstring,但是Java中String是雙字節的,在C++中不是雙字節的,這就涉及到一個字符串轉換,編碼的轉換
- string轉換為jstring
~~~
jstring c2j(JNIEnv* env, string cstr)
{
return env->NewStringUTF(cstr.c_str());
}
~~~
- jstring轉換為string
~~~
string j2c(JNIEnv* env, jstring jstr)
{
string ret;
jclass stringClass = env->FindClass("java/lang/String");
jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B");
// 把參數用到的字符串轉化成java的字符
jstring arg = c2j(env, "utf-8");
jbyteArray jbytes = (jbyteArray)env->CallObjectMethod(jstr, getBytes, arg);
// 從jbytes中,提取UTF8格式的內容
jsize byteLen = env->GetArrayLength(jbytes);
jbyte* JBuffer = env->GetByteArrayElements(jbytes, JNI_FALSE);
// 將內容拷貝到C++內存中
if(byteLen > 0)
{
char* buf = (char*)JBuffer;
std::copy(buf, buf+byteLen, back_inserter(ret));
}
// 釋放
env->ReleaseByteArrayElements(jbytes, JBuffer, 0);
return ret;
}
~~~
- jstring轉換成c語言的char* 類型

#### javah和javap
- javah
- 開發時,Java代碼中native聲明了本地方法,由于本地方法沒有方法體,需要到cpp文件中完善對應的本地方法,但是cpp中原生源文件(.c、.cpp)對應本地方法的方法名的命名很麻煩,如果一個一個來寫,可能會出錯,這時,可以使用javah來生成一個頭文件,該頭文件中直接寫好了Java代碼的本地方法對應的cpp中方法命名,可以直接復制到cpp文件中,這樣便捷高效。
- javah用于生成native接口定義,比如`javah com.wsc.wangsc.ndktest.Jni `
- 
- 
- jni類中native方法和.h頭文件中對應的方法如下圖紅色方框
- 
- 
- 這樣可以直接復制.h頭文件中方法命名到cpp文件中
- javap
- javap作用
1. 反編譯
2. 生成函數簽名
- javap用于生成java函數的簽名,比如 `javap -s Jni`
- 前言
- JNI基礎知識
- C語言知識點總結
- ①基本語法
- ②數據類型
- 枚舉類型
- 自定義類型(類型定義)
- ③格式化輸入輸出
- printf函數
- scanf函數
- 編程規范
- ④變量和常量
- 局部變量和外部變量
- ⑤類型轉換
- ⑥運算符
- ⑦結構語句
- 1、分支結構(選擇語句)
- 2、循環結構
- 退出循環
- break語句
- continue語句
- goto語句
- ⑧函數
- 函數的定義和調用
- 參數
- 函數的返回值
- 遞歸函數
- 零起點學通C語言摘要
- 內部函數和外部函數
- 變量存儲類別
- ⑨數組
- 指針
- 結構體
- 聯合體(共用體)
- 預處理器
- 預處理器的工作原理
- 預處理指令
- 宏定義
- 簡單的宏
- 帶參數的宏
- 預定義宏
- 文件包含
- 條件編譯
- 內存中的數據
- C語言讀文件和寫文件
- JNI知識點總結
- 前情回顧
- JNI規范
- jni開發
- jni開發中常見的錯誤
- JNI實戰演練
- C++(CPP)在Android開發中的應用
- 掘金網友總結的音視頻開發知識
- 音視頻學習一、C 語言入門
- 1.程序結構
- 2. 基本語法
- 3. 數據類型
- 4. 變量
- 5. 常量
- 6. 存儲類型關鍵字
- 7. 運算符
- 8. 判斷
- 9. 循環
- 10. 函數
- 11. 作用域規則
- 12. 數組
- 13. 枚舉
- 14. 指針
- 15. 函數指針與回調函數
- 16. 字符串
- 17. 結構體
- 18. 共用體
- 19. typedef
- 20. 輸入 & 輸出
- 21.文件讀寫
- 22. 預處理器
- 23.頭文件
- 24. 強制類型轉換
- 25. 錯誤處理
- 26. 遞歸
- 27. 可變參數
- 28. 內存管理
- 29. 命令行參數
- 總結
- 音視頻學習二 、C++ 語言入門
- 1. 基本語法
- 2. C++ 關鍵字
- 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. 多線程
- 總結
- 音視頻學習 (三) JNI 從入門到掌握
- 音視頻學習 (四) 交叉編譯動態庫、靜態庫的入門學習
- 音視頻學習 (五) Shell 腳本入門
- 音視頻學習 (六) 一鍵編譯 32/64 位 FFmpeg 4.2.2
- 音視頻學習 (七) 掌握音頻基礎知識并使用 AudioTrack、OpenSL ES 渲染 PCM 數據
- 音視頻學習 (八) 掌握視頻基礎知識并使用 OpenGL ES 2.0 渲染 YUV 數據
- 音視頻學習 (九) 從 0 ~ 1 開發一款 Android 端播放器(支持多協議網絡拉流/本地文件)
- 音視頻學習 (十) 基于 Nginx 搭建(rtmp、http)直播服務器
- 音視頻學習 (十一) Android 端實現 rtmp 推流
- 音視頻學習 (十二) 基于 FFmpeg + OpenSLES 實現音頻萬能播放器
- 音視頻學習 (十三) Android 中通過 FFmpeg 命令對音視頻編輯處理(已開源)