最簡單的基於FFmpeg的移動端例子:Android HelloWorld

從本文開始打算記錄一系列FFmpeg在Android/IOS開發的示例程序。前面幾篇文章記錄FFmpeg安卓端開發的例子,後面幾篇文章記錄FFmpeg IOS端開發的例子。這些例子中FFmpeg相關的代碼源自於《FFmpeg示例合集》中的程序。本文記錄第一個程序:安卓平臺下基於FFmpeg的HelloWorld程序。該程序的源代碼源自於《最簡單的基於FFMPEG的Helloworld程序》。java

本文記錄第一個程序:安卓平臺下基於FFmpeg的HelloWorld程序。該程序的源代碼源自於《最簡單的基於FFMPEG的Helloworld程序》。linux

 

Android程序FFmpeg類庫使用說明

 

Android應用程序使用FFmpeg類庫的流程圖以下所示。android

上圖中的流程能夠分爲「編譯FFmpeg類庫」、「編寫Java端代碼」、「編寫C語言端代碼」三個步驟。ios

(1)編譯FFmpeg類庫

a) 下載安裝NDK

下載NDK以後直接解壓縮就可使用了。在Windows下使用的時候須要用到Cygwin。在這裏我本身使用Linux編譯類庫。git

b) 修改FFmpeg的configure

下載FFmpeg源代碼以後,首先須要對源代碼中的configure文件進行修改。因爲編譯出來的動態庫文件名的版本號在.so以後(例如「libavcodec.so.5.100.1」),而android平臺不能識別這樣文件名,因此須要修改這種文件名。在configure文件中找到下面幾行代碼:github

[plain] view plain copyapp

 

  1. SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'  
  2. LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'  
  3. SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'  
  4. SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR)$(SLIBNAME)'  

替換爲下面內容就能夠了:ide

[plain] view plain copy函數

 

  1. SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'  
  2. LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'  
  3. SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'  
  4. SLIB_INSTALL_LINKS='$(SLIBNAME)'  

c) 生成類庫

按照configure、make、make install的步驟就能夠獲得FFmpeg的頭文件和類庫文件了。其中configure的配置腳本在網上比較多。下面列舉幾個腳本。工具

FFmpeg類庫完整功能腳本
下面這個腳本能夠生成一套功能完整,體積比較大的類庫。

[plain] view plain copy

 

  1. cd ffmpeg  
  2.   
  3. make clean  
  4.   
  5. export NDK=/home/leixiaohua1020/cdtworkspace/android-ndk-r9d  
  6. export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt  
  7. export PLATFORM=$NDK/platforms/android-8/arch-arm  
  8. export PREFIX=../simplefflib  
  9. build_one(){  
  10.   ./configure --target-os=linux --prefix=$PREFIX \  
  11. --enable-cross-compile \  
  12. --enable-runtime-cpudetect \  
  13. --disable-asm \  
  14. --arch=arm \  
  15. --cc=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-gcc \  
  16. --cross-prefix=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi- \  
  17. --disable-stripping \  
  18. --nm=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-nm \  
  19. --sysroot=$PLATFORM \  
  20. --enable-gpl --enable-shared --disable-static --enable-small \  
  21. --disable-ffprobe --disable-ffplay --disable-ffmpeg --disable-ffserver --disable-debug \  
  22. --extra-cflags="-fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a"   
  23. }  
  24.   
  25. build_one  
  26.   
  27. make  
  28. make install  
  29.   
  30. cd ..  

 

該腳本中前面幾個變量「NDK」、「PREBUILT」、「PLATFORM」根據NDK路徑的不一樣須要作相應的修改。另外須要注意64位NDK和32位NDK中某些文件夾名稱也有一些區別:例如32位NDK中文件夾名稱爲「linux-x86」而64位NDK中文件夾名稱爲「linux-x86_64」。

將上述腳本拷貝至ffmpeg源代碼外面,成功執行以後,會將類庫和頭文件生成到腳本所在目錄下的「simplefflib」文件夾中。
 

FFmpeg類庫裁剪功能後包含libx264和libfaac支持腳本

下面這個腳本能夠生成一個裁剪功能後,包含libx264和libfaac支持的類庫。

[plain] view plain copy

 

  1. export NDK=/home/leixiaohua1020/cdtworkspace/android-ndk-r9d  
  2. export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.6/prebuilt  
  3. export PLATFORM=$NDK/platforms/android-8/arch-arm  
  4. export PREFIX=../264fflib  
  5. build_one(){  
  6.   ./configure --target-os=linux --prefix=$PREFIX \  
  7. --enable-cross-compile \  
  8. --enable-runtime-cpudetect \  
  9. --disable-asm \  
  10. --arch=arm \  
  11. --cc=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-gcc \  
  12. --cross-prefix=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi- \  
  13. --disable-stripping \  
  14. --nm=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-nm \  
  15. --sysroot=$PLATFORM \  
  16. --enable-gpl --enable-shared --disable-static --enable-nonfree --enable-version3 --enable-small --disable-vda --disable-iconv \  
  17. --disable-encoders --enable-libx264 --enable-libfaac --enable-encoder=libx264 --enable-encoder=libfaac \  
  18. --disable-muxers --enable-muxer=mov --enable-muxer=ipod --enable-muxer=psp --enable-muxer=mp4 --enable-muxer=avi \  
  19. --disable-decoders --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=h264 --enable-decoder=mpeg4 \  
  20. --disable-demuxers --enable-demuxer=h264 --enable-demuxer=avi --enable-demuxer=mpc --enable-demuxer=mov \  
  21. --disable-parsers --enable-parser=aac --enable-parser=ac3 --enable-parser=h264 \  
  22. --disable-protocols --enable-protocol=file \  
  23. --disable-bsfs --enable-bsf=aac_adtstoasc --enable-bsf=h264_mp4toannexb \  
  24. --disable-indevs --enable-zlib \  
  25. --disable-outdevs --disable-ffprobe --disable-ffplay --disable-ffmpeg --disable-ffserver --disable-debug \  
  26. --extra-cflags="-I ../android-lib/include -fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a" \  
  27. --extra-ldflags="-L ../android-lib/lib"  
  28.   
  29. }  
  30.   
  31. build_one  
  32.   
  33. make  
  34. make install  
  35.   
  36. cd ..  


其中libfaac和libx264須要單獨編譯生成。它們編譯過的類庫位於「android-lib」文件夾中。libx264的編譯腳本以下所示。

[plain] view plain copy

 

  1. cd x264  
  2. export NDK=/home/leixiaohua1020/cdtworkspace/android-ndk-r9d  
  3. export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.6/prebuilt  
  4. export PLATFORM=$NDK/platforms/android-8/arch-arm  
  5. export PREFIX=../android-lib  
  6. ./configure --prefix=$PREFIX \  
  7. --enable-static \  
  8. --enable-pic \  
  9. --disable-asm \  
  10. --disable-cli \  
  11. --host=arm-linux \  
  12. --cross-prefix=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi- \  
  13. --sysroot=$PLATFORM  
  14. cd ..  


libfaac的編譯腳本以下所示。

[plain] view plain copy

 

  1. cd faac  
  2. export NDK=/home/leixiaohua1020/cdtworkspace/android-ndk-r9d  
  3. export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.6/prebuilt  
  4. export PLATFORM=$NDK/platforms/android-9/arch-arm  
  5. CFLAGS="-fpic -DANDROID -fpic -mthumb-interwork -ffunction-sections -funwind-tables -fstack-protector -fno-short-enums -D__ARM_ARCH_7__ -Wno-psabi -march=armv7 -mtune=xscale -msoft-float -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -DANDROID -Wa,--noexecstack -MMD -MP "  
  6. #CFLAGS="-fpic -DANDROID -fpic -mthumb-interwork -D__ARM_ARCH_7__ -Wno-psabi -march=armv7-a -mtune=xscale -msoft-float -mthumb -Os -fomit-frame-pointer -fno-strict-aliasing -finline-limit=64 -DANDROID -Wa, -MMD -MP "  
  7. CROSS_COMPILE=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-  
  8. export CPPFLAGS="$CFLAGS"  
  9. export CFLAGS="$CFLAGS"  
  10. export CXXFLAGS="$CFLAGS"  
  11. export CXX="${CROSS_COMPILE}g++ --sysroot=${PLATFORM}"  
  12. export LDFLAGS="$LDFLAGS"  
  13. export CC="${CROSS_COMPILE}gcc --sysroot=${PLATFORM}"  
  14. export NM="${CROSS_COMPILE}nm"  
  15. export STRIP="${CROSS_COMPILE}strip"  
  16. export RANLIB="${CROSS_COMPILE}ranlib"  
  17. export AR="${CROSS_COMPILE}ar"  
  18.   
  19. ./configure \  
  20. --without-mp4v2 \  
  21. --host=arm-linux \  
  22. --enable-static \  
  23.   
  24. make  
  25. make install  
  26.   
  27. cp -rf /usr/local/include/faac.h ../android-lib/include  
  28. cp -rf /usr/local/include/faaccfg.h ../android-lib/include  
  29. cp -rf /usr/local/lib/libfaac.a ../android-lib/lib  
  30.   
  31. cd ..  


FFmpeg編譯後生成的類庫文件包含下面幾個:

libavformat-56.so
libavcodec-56.so
libavfilter-5.so
libavdevice-56.so
libavutil-54.so
libpostproc-53.so
libswresample-1.so
libswscale-3.so

 

(2)編寫Java端代碼

使用Android IDE(例如Eclipse ADT)建立一個空的Android項目。也能夠直接使用NDK中的hello-jni例子,該項目位於「{NDK目錄}/samples/hello-jni」中。後文將會逐步改造hello-jni,使它支持FFmpeg類庫的調用。
修改Android項目中「src」文件夾下的Java源代碼,準備調用C語言函數。使用JNI調用C語言代碼有兩點須要作的步驟:

  • 聲明C語言函數對應的Java函數
  • 聲明要加載的類庫

須要注意,C語言函數的聲明要加上「native」關鍵字;加載類庫的時候須要使用「System.loadLibrary()」方法。例如hello-jni例子中的Activity源代碼以下所示。

[java] view plain copy

 

  1. package com.example.hellojni;  
  2.   
  3. import android.app.Activity;  
  4. import android.widget.TextView;  
  5. import android.os.Bundle;  
  6.   
  7.   
  8. public class HelloJni extends Activity  
  9. {  
  10.     /** Called when the activity is first created. */  
  11.     @Override  
  12.     public void onCreate(Bundle savedInstanceState)  
  13.     {  
  14.         super.onCreate(savedInstanceState);  
  15.   
  16.         /* Create a TextView and set its content. 
  17.          * the text is retrieved by calling a native 
  18.          * function. 
  19.          */  
  20.         TextView  tv = new TextView(this);  
  21.         tv.setText( stringFromJNI() );  
  22.         setContentView(tv);  
  23.     }  
  24.   
  25.     /* A native method that is implemented by the 
  26.      * 'hello-jni' native library, which is packaged 
  27.      * with this application. 
  28.      */  
  29.     public native String  stringFromJNI();  
  30.   
  31.     /* This is another native method declaration that is *not* 
  32.      * implemented by 'hello-jni'. This is simply to show that 
  33.      * you can declare as many native methods in your Java code 
  34.      * as you want, their implementation is searched in the 
  35.      * currently loaded native libraries only the first time 
  36.      * you call them. 
  37.      * 
  38.      * Trying to call this function will result in a 
  39.      * java.lang.UnsatisfiedLinkError exception ! 
  40.      */  
  41.     public native String  unimplementedStringFromJNI();  
  42.   
  43.     /* this is used to load the 'hello-jni' library on application 
  44.      * startup. The library has already been unpacked into 
  45.      * /data/data/com.example.hellojni/lib/libhello-jni.so at 
  46.      * installation time by the package manager. 
  47.      */  
  48.     static {  
  49.         System.loadLibrary("hello-jni");  
  50.     }  
  51. }  


從源代碼能夠看出,該Activity加載了名稱爲「libhello-jni.so」的類庫(Java代碼中不包含前面的「lib」和後面的「.so」),並聲明瞭stringFromJNI()方法。
在這裏,爲了調用FFmpeg而通過修改後的Activity加載類庫部分的源代碼以下所示。

[java] view plain copy

 

  1. static {  
  2.  System.loadLibrary("avutil-54");  
  3.     System.loadLibrary("avcodec-56");  
  4.     System.loadLibrary("avformat-56");  
  5.     System.loadLibrary("avdevice-56");  
  6.     System.loadLibrary("swresample-1");  
  7.     System.loadLibrary("swscale-3");  
  8.     System.loadLibrary("postproc-53");  
  9.     System.loadLibrary("avfilter-5");  
  10.  System.loadLibrary("hello-jni");  
  11. }  

 

(3)編寫C語言端代碼

a) 獲取C語言的接口函數聲明

根據Java對於C語言接口的定義,生成相應的接口函數聲明。這一步須要用到JDK中的「javah」命令。例如對於hello-jni例子,首先切換到src文件夾下,輸入以下命令:

[plain] view plain copy

 

  1. javah com.example.hellojni.HelloJni  

就能夠在當前目錄下生成一個頭文件「com_example_hellojni_HelloJni.h」,該頭文件內容以下所示。

[cpp] view plain copy

 

  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class com_example_hellojni_HelloJni */  
  4.   
  5. #ifndef _Included_com_example_hellojni_HelloJni  
  6. #define _Included_com_example_hellojni_HelloJni  
  7. #ifdef __cplusplus  
  8. extern "C" {  
  9. #endif  
  10. /* 
  11.  * Class:     com_example_hellojni_HelloJni 
  12.  * Method:    stringFromJNI 
  13.  * Signature: ()Ljava/lang/String; 
  14.  */  
  15. JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI  
  16.   (JNIEnv *, jobject);  
  17.   
  18. /* 
  19.  * Class:     com_example_hellojni_HelloJni 
  20.  * Method:    unimplementedStringFromJNI 
  21.  * Signature: ()Ljava/lang/String; 
  22.  */  
  23. JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_unimplementedStringFromJNI  
  24.   (JNIEnv *, jobject);  
  25.   
  26. #ifdef __cplusplus  
  27. }  
  28. #endif  
  29. #endif  

 

從源代碼能夠看出,JNI調用的C語言函數是有固定格式的,即:

Java_{包名}_{包名}…_{類名}(JNIEnv *,…)

對於HelloJni類中的stringFromJNI方法,其C語言版本的函數聲明爲:

[cpp] view plain copy

 

  1. JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI (JNIEnv *, jobject)  

PS:這個頭文件只是一個參考,對於JNI來講並非必須的。也能夠根據命名規則直接編寫C語言函數。
 

b) 編寫C語言接口函數代碼

在Android項目根目錄下新建「jni」文件夾,用於存儲C語言源代碼以及相關的開發資源。將編譯生成的FFmpeg的類庫(.so文件)和頭文件(.h文件)拷貝到這個目錄下,而後新建一個C語言文件,就能夠開始編寫相應的邏輯了。此時jni文件夾目錄結構以下圖所示。

 

C語言文件用於實現上文中經過「javah」命令生成的頭文件的函數。對於hello-jni例子,其C語言文件內容以下所示。

[cpp] view plain copy

 

  1. #include <string.h>  
  2. #include <jni.h>  
  3.   
  4. /* This is a trivial JNI example where we use a native method 
  5.  * to return a new VM String. See the corresponding Java source 
  6.  * file located at: 
  7.  * 
  8.  *   apps/samples/hello-jni/project/src/com/example/hellojni/HelloJni.java 
  9.  */  
  10. jstring  
  11. Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,  
  12.                                                   jobject thiz )  
  13. {  
  14. #if defined(__arm__)  
  15.   #if defined(__ARM_ARCH_7A__)  
  16.     #if defined(__ARM_NEON__)  
  17.       #define ABI "armeabi-v7a/NEON"  
  18.     #else  
  19.       #define ABI "armeabi-v7a"  
  20.     #endif  
  21.   #else  
  22.    #define ABI "armeabi"  
  23.   #endif  
  24. #elif defined(__i386__)  
  25.    #define ABI "x86"  
  26. #elif defined(__mips__)  
  27.    #define ABI "mips"  
  28. #else  
  29.    #define ABI "unknown"  
  30. #endif  
  31.   
  32.     return (*env)->NewStringUTF(env, "Hello from JNI !  Compiled with ABI " ABI ".");  
  33. }  


能夠看出,Java_com_example_hellojni_HelloJni_stringFromJNI()根據宏定義斷定了系統類型而且返回了一個字符串。在這裏要注意,C語言中的char[]是不能直接對應爲Java中的String類型的(即jstring)。char[]轉換爲String須要經過JNIEnv的NewStringUTF()函數。
爲了調用FFmpeg而通過修改後的Java_com_example_hellojni_HelloJni_stringFromJNI()的源代碼以下所示。

[cpp] view plain copy

 

  1. #include <string.h>  
  2. #include <jni.h>  
  3. #include "libavcodec/avcodec.h"  
  4.   
  5. jstring  
  6. Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,  
  7.                                                   jobject thiz )  
  8. {  
  9.     char info[10000] = { 0 };  
  10.     sprintf(info, "%s\n", avcodec_configuration());  
  11.     return (*env)->NewStringUTF(env, info);  
  12. }  


能夠看出該函數調用了libavcodec的avcodec_configuration()方法,用於獲取FFmpeg的配置信息。
 

c) 編寫Android.mk

完成C語言程序的編寫後,就能夠開始編寫Android的makefile文件Android.mk了。hello-jni例子中的Android.mk內容以下:

[plain] view plain copy

 

  1. LOCAL_PATH := $(call my-dir)  
  2.   
  3. include $(CLEAR_VARS)  
  4.   
  5. LOCAL_MODULE    := hello-jni  
  6. LOCAL_SRC_FILES := hello-jni.c  
  7.   
  8. include $(BUILD_SHARED_LIBRARY)  


編譯FFmpeg示例程序的時候因爲用到了libavcodec等相關的庫,因此將Android.mk文件修改以下:

[plain] view plain copy

 

  1. LOCAL_PATH := $(call my-dir)  
  2.   
  3. # FFmpeg library  
  4. include $(CLEAR_VARS)  
  5. LOCAL_MODULE := avcodec  
  6. LOCAL_SRC_FILES := libavcodec-56.so  
  7. include $(PREBUILT_SHARED_LIBRARY)  
  8.   
  9. include $(CLEAR_VARS)  
  10. LOCAL_MODULE := avdevice  
  11. LOCAL_SRC_FILES := libavdevice-56.so  
  12. include $(PREBUILT_SHARED_LIBRARY)  
  13.   
  14. include $(CLEAR_VARS)  
  15. LOCAL_MODULE := avfilter  
  16. LOCAL_SRC_FILES := libavfilter-5.so  
  17. include $(PREBUILT_SHARED_LIBRARY)  
  18.   
  19. include $(CLEAR_VARS)  
  20. LOCAL_MODULE := avformat  
  21. LOCAL_SRC_FILES := libavformat-56.so  
  22. include $(PREBUILT_SHARED_LIBRARY)  
  23.   
  24. include $(CLEAR_VARS)  
  25. LOCAL_MODULE := avutil  
  26. LOCAL_SRC_FILES := libavutil-54.so  
  27. include $(PREBUILT_SHARED_LIBRARY)  
  28.   
  29. include $(CLEAR_VARS)  
  30. LOCAL_MODULE := postproc  
  31. LOCAL_SRC_FILES := libpostproc-53.so  
  32. include $(PREBUILT_SHARED_LIBRARY)  
  33.   
  34. include $(CLEAR_VARS)  
  35. LOCAL_MODULE := swresample  
  36. LOCAL_SRC_FILES := libswresample-1.so  
  37. include $(PREBUILT_SHARED_LIBRARY)  
  38.   
  39. include $(CLEAR_VARS)  
  40. LOCAL_MODULE := swscale  
  41. LOCAL_SRC_FILES := libswscale-3.so  
  42. include $(PREBUILT_SHARED_LIBRARY)  
  43.   
  44. # Program  
  45. include $(CLEAR_VARS)  
  46. LOCAL_MODULE := hello-jni  
  47. LOCAL_SRC_FILES := hello-jni.c  
  48. LOCAL_C_INCLUDES += $(LOCAL_PATH)/include  
  49. LOCAL_LDLIBS := -llog -lz  
  50. LOCAL_SHARED_LIBRARIES := avcodec avdevice avfilter avformat avutil postproc swresample swscale  
  51. include $(BUILD_SHARED_LIBRARY)  

 

d) 編寫Application.mk(可選)

Application.mk中的APP_ABI設定了編譯後庫文件支持的指令集,默認使用「armeabi」。在hello-jni例子中,APP_ABI取值爲「all」。因爲咱們編譯的FFmpeg並不在像x86這樣的平臺下運行,因此不須要「all」,把它修改成「armeabi」或者刪除就能夠了(對於hello-jni這個例子,不作這一步的話會在編譯x86平臺類庫的時候報錯,但並不影響後面的測試運行)。

e) 運行ndk-build

編寫完Android的Makefile文件以後,就能夠運行ndk-build編譯生成能夠經過JNI調用的類庫了。ndk-build自己是一個腳本,位於NDK根目錄下。切換到Android程序目錄中,直接執行該腳本就能夠了。
ndk-build成功後,會在根目錄下的「libs/armeabi」目錄中生成相關的庫文件。hello-jni例子中,會生成如下庫文件:

libavformat-56.so
libavcodec-56.so
libavfilter-5.so
libavdevice-56.so
libavutil-54.so
libpostproc-53.so
libswresample-1.so
libswscale-3.so
libhello-jni.so

接下來就能夠在Android手機或者虛擬機上對整個Android工程進行測試了。

f) 程序運行結果

程序最終的運行結果截圖以下所示。

 

從圖中能夠看出,程序中打印出了FFmpeg的配置信息。

 

FFmpeg Helloworld

本文記錄的FFmpeg Helloworld程序C語言的源代碼來自於《最簡單的基於FFMPEG的Helloworld程序》。改程序會輸出FFmpeg類庫下列信息:

Protocol: FFmpeg類庫支持的協議
AVFormat: FFmpeg類庫支持的封裝格式
AVCodec: FFmpeg類庫支持的編解碼器
AVFilter: FFmpeg類庫支持的濾鏡
Configure: FFmpeg類庫的配置信息

 

源代碼

項目的目錄結構如圖所示。Java源代碼位於src目錄,而C代碼位於jni目錄。

Android程序Java端代碼位於src\com\leixiaohua1020\sffmpegandroidhelloworld\MainActivity.java,以下所示。

 

[java] view plain copy

 

  1. /** 
  2.  * 最簡單的基於FFmpeg的Helloworld例子-安卓 
  3.  * Simplest FFmpeg Android Helloworld 
  4.  *  
  5.  * 雷霄驊 Lei Xiaohua 
  6.  * leixiaohua1020@126.com 
  7.  * 中國傳媒大學/數字電視技術 
  8.  * Communication University of China / Digital TV Technology 
  9.  * http://blog.csdn.net/leixiaohua1020 
  10.  *  
  11.  *  
  12.  * 本程序是移植FFmpeg到安卓平臺的最簡單程序。它能夠打印出FFmpeg類庫的下列信息: 
  13.  *  Protocol:  FFmpeg類庫支持的協議 
  14.  *  AVFormat:  FFmpeg類庫支持的封裝格式 
  15.  *  AVCodec:   FFmpeg類庫支持的編解碼器 
  16.  *  AVFilter:  FFmpeg類庫支持的濾鏡 
  17.  *  Configure: FFmpeg類庫的配置信息 
  18.  *  
  19.  * This is the simplest program based on FFmpeg in Android. It can show following  
  20.  * informations about FFmpeg library: 
  21.  *  Protocol:  Protocols supported by FFmpeg. 
  22.  *  AVFormat:  Container format supported by FFmpeg. 
  23.  *  AVCodec:   Encoder/Decoder supported by FFmpeg. 
  24.  *  AVFilter:  Filters supported by FFmpeg. 
  25.  *  Configure: configure information of FFmpeg. 
  26.  *  
  27.  */  
  28.   
  29.   
  30. package com.leixiaohua1020.sffmpegandroidhelloworld;  
  31.   
  32.   
  33. import android.os.Bundle;  
  34. import android.app.Activity;  
  35. import android.text.method.ScrollingMovementMethod;  
  36. import android.util.Log;  
  37. import android.view.Menu;  
  38. import android.view.View;  
  39. import android.view.View.OnClickListener;  
  40. import android.widget.Button;  
  41. import android.widget.TextView;  
  42.   
  43. public class MainActivity extends Activity {  
  44.   
  45.     @Override  
  46.     protected void onCreate(Bundle savedInstanceState) {  
  47.         super.onCreate(savedInstanceState);  
  48.         setContentView(R.layout.activity_main);  
  49.           
  50.         final TextView libinfoText = (TextView) findViewById(R.id.text_libinfo);  
  51.         libinfoText.setMovementMethod(ScrollingMovementMethod.getInstance());   
  52.           
  53.         libinfoText.setText(configurationinfo());  
  54.           
  55.         Button configurationButton = (Button) this.findViewById(R.id.button_configuration);  
  56.         Button urlprotocolButton = (Button) this.findViewById(R.id.button_urlprotocol);  
  57.         Button avformatButton = (Button) this.findViewById(R.id.button_avformat);  
  58.         Button avcodecButton = (Button) this.findViewById(R.id.button_avcodec);  
  59.         Button avfilterButton = (Button) this.findViewById(R.id.button_avfilter);  
  60.           
  61.         urlprotocolButton.setOnClickListener(new OnClickListener() {  
  62.             public void onClick(View arg0){  
  63.                 libinfoText.setText(urlprotocolinfo());  
  64.             }  
  65.         });  
  66.           
  67.         avformatButton.setOnClickListener(new OnClickListener() {  
  68.             public void onClick(View arg0){  
  69.                 libinfoText.setText(avformatinfo());  
  70.             }  
  71.         });  
  72.           
  73.         avcodecButton.setOnClickListener(new OnClickListener() {  
  74.             public void onClick(View arg0){  
  75.                 libinfoText.setText(avcodecinfo());  
  76.             }  
  77.         });  
  78.           
  79.         avfilterButton.setOnClickListener(new OnClickListener() {  
  80.             public void onClick(View arg0){  
  81.                 libinfoText.setText(avfilterinfo());  
  82.             }  
  83.         });  
  84.           
  85.         configurationButton.setOnClickListener(new OnClickListener() {  
  86.             public void onClick(View arg0){  
  87.                 libinfoText.setText(configurationinfo());  
  88.             }  
  89.         });  
  90.           
  91.     }  
  92.   
  93.     @Override  
  94.     public boolean onCreateOptionsMenu(Menu menu) {  
  95.         // Inflate the menu; this adds items to the action bar if it is present.  
  96.         getMenuInflater().inflate(R.menu.main, menu);  
  97.         return true;  
  98.     }  
  99.       
  100.     //JNI  
  101.     public native String urlprotocolinfo();  
  102.     public native String avformatinfo();  
  103.     public native String avcodecinfo();  
  104.     public native String avfilterinfo();  
  105.     public native String configurationinfo();  
  106.       
  107.     static{  
  108.         System.loadLibrary("avutil-54");  
  109.         System.loadLibrary("swresample-1");  
  110.         System.loadLibrary("avcodec-56");  
  111.         System.loadLibrary("avformat-56");  
  112.         System.loadLibrary("swscale-3");  
  113.         System.loadLibrary("postproc-53");  
  114.         System.loadLibrary("avfilter-5");  
  115.         System.loadLibrary("avdevice-56");  
  116.         System.loadLibrary("sffhelloworld");  
  117.     }  
  118.       
  119. }  

 

 

C語言端源代碼位於jni/simplest_ffmpeg_helloworld.c,以下所示。

[cpp] view plain copy

 

  1. /** 
  2.  * 最簡單的基於FFmpeg的Helloworld例子-安卓 
  3.  * Simplest FFmpeg Android Helloworld 
  4.  * 
  5.  * 雷霄驊 Lei Xiaohua 
  6.  * leixiaohua1020@126.com 
  7.  * 中國傳媒大學/數字電視技術 
  8.  * Communication University of China / Digital TV Technology 
  9.  * http://blog.csdn.net/leixiaohua1020 
  10.  * 
  11.  * 
  12.  * 本程序是移植FFmpeg到安卓平臺的最簡單程序。它能夠打印出FFmpeg類庫的下列信息: 
  13.  * Protocol:  FFmpeg類庫支持的協議 
  14.  * AVFormat:  FFmpeg類庫支持的封裝格式 
  15.  * AVCodec:   FFmpeg類庫支持的編解碼器 
  16.  * AVFilter:  FFmpeg類庫支持的濾鏡 
  17.  * Configure: FFmpeg類庫的配置信息 
  18.  * 
  19.  * This is the simplest program based on FFmpeg in Android. It can show following 
  20.  * informations about FFmpeg library: 
  21.  * Protocol:  Protocols supported by FFmpeg. 
  22.  * AVFormat:  Container format supported by FFmpeg. 
  23.  * AVCodec:   Encoder/Decoder supported by FFmpeg. 
  24.  * AVFilter:  Filters supported by FFmpeg. 
  25.  * Configure: configure information of FFmpeg. 
  26.  */  
  27.   
  28. #include <stdio.h>  
  29.    
  30. #include "libavcodec/avcodec.h"  
  31. #include "libavformat/avformat.h"  
  32. #include "libavfilter/avfilter.h"  
  33.   
  34. //Log  
  35. #ifdef ANDROID  
  36. #include <jni.h>  
  37. #include <android/log.h>  
  38. #define LOGE(format, ...)  __android_log_print(ANDROID_LOG_ERROR, "(>_<)", format, ##__VA_ARGS__)  
  39. #else  
  40. #define LOGE(format, ...)  printf("(>_<) " format "\n", ##__VA_ARGS__)  
  41. #endif  
  42.   
  43.   
  44. //FIX  
  45. struct URLProtocol;  
  46. /** 
  47.  * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.urlprotocolinfo() 
  48.  * Protocol Support Information 
  49.  */  
  50. JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_urlprotocolinfo(JNIEnv *env, jobject obj){  
  51.       
  52.     char info[40000]={0};  
  53.     av_register_all();  
  54.   
  55.     struct URLProtocol *pup = NULL;  
  56.     //Input  
  57.     struct URLProtocol **p_temp = &pup;  
  58.     avio_enum_protocols((void **)p_temp, 0);  
  59.     while ((*p_temp) != NULL){  
  60.         sprintf(info, "%s[In ][%10s]\n", info, avio_enum_protocols((void **)p_temp, 0));  
  61.     }  
  62.     pup = NULL;  
  63.     //Output  
  64.     avio_enum_protocols((void **)p_temp, 1);  
  65.     while ((*p_temp) != NULL){  
  66.         sprintf(info, "%s[Out][%10s]\n", info, avio_enum_protocols((void **)p_temp, 1));  
  67.     }  
  68.   
  69.     //LOGE("%s", info);  
  70.     return (*env)->NewStringUTF(env, info);  
  71. }  
  72.   
  73. /** 
  74.  * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.avformatinfo() 
  75.  * AVFormat Support Information 
  76.  */  
  77. JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_avformatinfo(JNIEnv *env, jobject obj){  
  78.   
  79.     char info[40000] = { 0 };  
  80.   
  81.     av_register_all();  
  82.   
  83.     AVInputFormat *if_temp = av_iformat_next(NULL);  
  84.     AVOutputFormat *of_temp = av_oformat_next(NULL);  
  85.     //Input  
  86.     while(if_temp!=NULL){  
  87.         sprintf(info, "%s[In ][%10s]\n", info, if_temp->name);  
  88.         if_temp=if_temp->next;  
  89.     }  
  90.     //Output  
  91.     while (of_temp != NULL){  
  92.         sprintf(info, "%s[Out][%10s]\n", info, of_temp->name);  
  93.         of_temp = of_temp->next;  
  94.     }  
  95.     //LOGE("%s", info);  
  96.     return (*env)->NewStringUTF(env, info);  
  97. }  
  98.   
  99. /** 
  100.  * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.avcodecinfo() 
  101.  * AVCodec Support Information 
  102.  */  
  103. JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_avcodecinfo(JNIEnv *env, jobject obj)  
  104. {  
  105.     char info[40000] = { 0 };  
  106.   
  107.     av_register_all();  
  108.   
  109.     AVCodec *c_temp = av_codec_next(NULL);  
  110.   
  111.     while(c_temp!=NULL){  
  112.         if (c_temp->decode!=NULL){  
  113.             sprintf(info, "%s[Dec]", info);  
  114.         }  
  115.         else{  
  116.             sprintf(info, "%s[Enc]", info);  
  117.         }  
  118.         switch (c_temp->type){  
  119.         case AVMEDIA_TYPE_VIDEO:  
  120.             sprintf(info, "%s[Video]", info);  
  121.             break;  
  122.         case AVMEDIA_TYPE_AUDIO:  
  123.             sprintf(info, "%s[Audio]", info);  
  124.             break;  
  125.         default:  
  126.             sprintf(info, "%s[Other]", info);  
  127.             break;  
  128.         }  
  129.         sprintf(info, "%s[%10s]\n", info, c_temp->name);  
  130.   
  131.           
  132.         c_temp=c_temp->next;  
  133.     }  
  134.     //LOGE("%s", info);  
  135.   
  136.     return (*env)->NewStringUTF(env, info);  
  137. }  
  138.   
  139. /** 
  140.  * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.avfilterinfo() 
  141.  * AVFilter Support Information 
  142.  */  
  143. JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_avfilterinfo(JNIEnv *env, jobject obj)  
  144. {  
  145.     char info[40000] = { 0 };  
  146.     avfilter_register_all();  
  147.     AVFilter *f_temp = (AVFilter *)avfilter_next(NULL);  
  148.     while (f_temp != NULL){  
  149.         sprintf(info, "%s[%10s]\n", info, f_temp->name);  
  150.     }  
  151.     //LOGE("%s", info);  
  152.   
  153.     return (*env)->NewStringUTF(env, info);  
  154. }  
  155.   
  156. /** 
  157.  * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.urlprotocolinfo() 
  158.  * Protocol Support Information 
  159.  */  
  160. JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_configurationinfo(JNIEnv *env, jobject obj)  
  161. {  
  162.     char info[10000] = { 0 };  
  163.     av_register_all();  
  164.   
  165.     sprintf(info, "%s\n", avcodec_configuration());  
  166.   
  167.     //LOGE("%s", info);  
  168.     return (*env)->NewStringUTF(env, info);  
  169. }  


Android.mk文件位於jni/Android.mk,以下所示。

[plain] view plain copy

 

  1. # Android.mk for FFmpeg  
  2. #  
  3. # Lei Xiaohua 雷霄驊  
  4. # leixiaohua1020@126.com  
  5. # http://blog.csdn.net/leixiaohua1020  
  6. #   
  7.   
  8. LOCAL_PATH := $(call my-dir)  
  9.   
  10. # FFmpeg library  
  11. include $(CLEAR_VARS)  
  12. LOCAL_MODULE := avcodec  
  13. LOCAL_SRC_FILES := libavcodec-56.so  
  14. include $(PREBUILT_SHARED_LIBRARY)  
  15.   
  16. include $(CLEAR_VARS)  
  17. LOCAL_MODULE := avdevice  
  18. LOCAL_SRC_FILES := libavdevice-56.so  
  19. include $(PREBUILT_SHARED_LIBRARY)  
  20.   
  21. include $(CLEAR_VARS)  
  22. LOCAL_MODULE := avfilter  
  23. LOCAL_SRC_FILES := libavfilter-5.so  
  24. include $(PREBUILT_SHARED_LIBRARY)  
  25.   
  26. include $(CLEAR_VARS)  
  27. LOCAL_MODULE := avformat  
  28. LOCAL_SRC_FILES := libavformat-56.so  
  29. include $(PREBUILT_SHARED_LIBRARY)  
  30.   
  31. include $(CLEAR_VARS)  
  32. LOCAL_MODULE := avutil  
  33. LOCAL_SRC_FILES := libavutil-54.so  
  34. include $(PREBUILT_SHARED_LIBRARY)  
  35.   
  36. include $(CLEAR_VARS)  
  37. LOCAL_MODULE := postproc  
  38. LOCAL_SRC_FILES := libpostproc-53.so  
  39. include $(PREBUILT_SHARED_LIBRARY)  
  40.   
  41. include $(CLEAR_VARS)  
  42. LOCAL_MODULE := swresample  
  43. LOCAL_SRC_FILES := libswresample-1.so  
  44. include $(PREBUILT_SHARED_LIBRARY)  
  45.   
  46. include $(CLEAR_VARS)  
  47. LOCAL_MODULE := swscale  
  48. LOCAL_SRC_FILES := libswscale-3.so  
  49. include $(PREBUILT_SHARED_LIBRARY)  
  50.   
  51. # Program  
  52. include $(CLEAR_VARS)  
  53. LOCAL_MODULE := sffhelloworld  
  54. LOCAL_SRC_FILES :=simplest_ffmpeg_helloworld.c  
  55. LOCAL_C_INCLUDES += $(LOCAL_PATH)/include  
  56. LOCAL_LDLIBS := -llog -lz  
  57. LOCAL_SHARED_LIBRARIES := avcodec avdevice avfilter avformat avutil postproc swresample swscale  
  58. include $(BUILD_SHARED_LIBRARY)  


其它部分源代碼暫不詳細例舉。
 

運行結果

App在手機上運行後的結果以下圖所示。

 

單擊不一樣的按鈕,能夠查看FFmpeg類庫相關的信息。

 

下載

simplest ffmpeg mobile

項目主頁

Github:https://github.com/leixiaohua1020/simplest_ffmpeg_mobile

開源中國:https://git.oschina.net/leixiaohua1020/simplest_ffmpeg_mobile

SourceForge:https://sourceforge.net/projects/simplestffmpegmobile/

 

CSDN工程下載地址:http://download.csdn.net/detail/leixiaohua1020/8924391

 

 

本解決方案包含了使用FFmpeg在移動端處理多媒體的各類例子:

[Android]
simplest_android_player: 基於安卓接口的視頻播放器
simplest_ffmpeg_android_helloworld: 安卓平臺下基於FFmpeg的HelloWorld程序
simplest_ffmpeg_android_decoder: 安卓平臺下最簡單的基於FFmpeg的視頻解碼器
simplest_ffmpeg_android_decoder_onelib: 安卓平臺下最簡單的基於FFmpeg的視頻解碼器-單庫版
simplest_ffmpeg_android_streamer: 安卓平臺下最簡單的基於FFmpeg的推流器
simplest_ffmpeg_android_transcoder: 安卓平臺下移植的FFmpeg命令行工具
simplest_sdl_android_helloworld: 移植SDL到安卓平臺的最簡單程序
[IOS]
simplest_ios_player: 基於IOS接口的視頻播放器
simplest_ffmpeg_ios_helloworld: IOS平臺下基於FFmpeg的HelloWorld程序
simplest_ffmpeg_ios_decoder: IOS平臺下最簡單的基於FFmpeg的視頻解碼器
simplest_ffmpeg_ios_streamer: IOS平臺下最簡單的基於FFmpeg的推流器
simplest_ffmpeg_ios_transcoder: IOS平臺下移植的ffmpeg.c命令行工具
simplest_sdl_ios_helloworld: 移植SDL到IOS平臺的最簡單程序
相關文章
相關標籤/搜索