從本文開始打算記錄一系列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
- SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
- LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
- SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
- SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR)$(SLIBNAME)'
替換爲下面內容就能夠了:ide
[plain] view plain copy函數
- SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
- LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
- SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
- SLIB_INSTALL_LINKS='$(SLIBNAME)'
c) 生成類庫
按照configure、make、make install的步驟就能夠獲得FFmpeg的頭文件和類庫文件了。其中configure的配置腳本在網上比較多。下面列舉幾個腳本。工具
FFmpeg類庫完整功能腳本
下面這個腳本能夠生成一套功能完整,體積比較大的類庫。
[plain] view plain copy
- cd ffmpeg
-
- make clean
-
- export NDK=/home/leixiaohua1020/cdtworkspace/android-ndk-r9d
- export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt
- export PLATFORM=$NDK/platforms/android-8/arch-arm
- export PREFIX=../simplefflib
- build_one(){
- ./configure --target-os=linux --prefix=$PREFIX \
- --enable-cross-compile \
- --enable-runtime-cpudetect \
- --disable-asm \
- --arch=arm \
- --cc=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-gcc \
- --cross-prefix=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi- \
- --disable-stripping \
- --nm=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-nm \
- --sysroot=$PLATFORM \
- --enable-gpl --enable-shared --disable-static --enable-small \
- --disable-ffprobe --disable-ffplay --disable-ffmpeg --disable-ffserver --disable-debug \
- --extra-cflags="-fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a"
- }
-
- build_one
-
- make
- make install
-
- 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
- export NDK=/home/leixiaohua1020/cdtworkspace/android-ndk-r9d
- export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.6/prebuilt
- export PLATFORM=$NDK/platforms/android-8/arch-arm
- export PREFIX=../264fflib
- build_one(){
- ./configure --target-os=linux --prefix=$PREFIX \
- --enable-cross-compile \
- --enable-runtime-cpudetect \
- --disable-asm \
- --arch=arm \
- --cc=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-gcc \
- --cross-prefix=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi- \
- --disable-stripping \
- --nm=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-nm \
- --sysroot=$PLATFORM \
- --enable-gpl --enable-shared --disable-static --enable-nonfree --enable-version3 --enable-small --disable-vda --disable-iconv \
- --disable-encoders --enable-libx264 --enable-libfaac --enable-encoder=libx264 --enable-encoder=libfaac \
- --disable-muxers --enable-muxer=mov --enable-muxer=ipod --enable-muxer=psp --enable-muxer=mp4 --enable-muxer=avi \
- --disable-decoders --enable-decoder=aac --enable-decoder=aac_latm --enable-decoder=h264 --enable-decoder=mpeg4 \
- --disable-demuxers --enable-demuxer=h264 --enable-demuxer=avi --enable-demuxer=mpc --enable-demuxer=mov \
- --disable-parsers --enable-parser=aac --enable-parser=ac3 --enable-parser=h264 \
- --disable-protocols --enable-protocol=file \
- --disable-bsfs --enable-bsf=aac_adtstoasc --enable-bsf=h264_mp4toannexb \
- --disable-indevs --enable-zlib \
- --disable-outdevs --disable-ffprobe --disable-ffplay --disable-ffmpeg --disable-ffserver --disable-debug \
- --extra-cflags="-I ../android-lib/include -fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a" \
- --extra-ldflags="-L ../android-lib/lib"
-
- }
-
- build_one
-
- make
- make install
-
- cd ..
其中libfaac和libx264須要單獨編譯生成。它們編譯過的類庫位於「android-lib」文件夾中。libx264的編譯腳本以下所示。
[plain] view plain copy
- cd x264
- export NDK=/home/leixiaohua1020/cdtworkspace/android-ndk-r9d
- export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.6/prebuilt
- export PLATFORM=$NDK/platforms/android-8/arch-arm
- export PREFIX=../android-lib
- ./configure --prefix=$PREFIX \
- --enable-static \
- --enable-pic \
- --disable-asm \
- --disable-cli \
- --host=arm-linux \
- --cross-prefix=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi- \
- --sysroot=$PLATFORM
- cd ..
libfaac的編譯腳本以下所示。
[plain] view plain copy
- cd faac
- export NDK=/home/leixiaohua1020/cdtworkspace/android-ndk-r9d
- export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.6/prebuilt
- export PLATFORM=$NDK/platforms/android-9/arch-arm
- 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 "
- #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 "
- CROSS_COMPILE=$PREBUILT/linux-x86_64/bin/arm-linux-androideabi-
- export CPPFLAGS="$CFLAGS"
- export CFLAGS="$CFLAGS"
- export CXXFLAGS="$CFLAGS"
- export CXX="${CROSS_COMPILE}g++ --sysroot=${PLATFORM}"
- export LDFLAGS="$LDFLAGS"
- export CC="${CROSS_COMPILE}gcc --sysroot=${PLATFORM}"
- export NM="${CROSS_COMPILE}nm"
- export STRIP="${CROSS_COMPILE}strip"
- export RANLIB="${CROSS_COMPILE}ranlib"
- export AR="${CROSS_COMPILE}ar"
-
- ./configure \
- --without-mp4v2 \
- --host=arm-linux \
- --enable-static \
-
- make
- make install
-
- cp -rf /usr/local/include/faac.h ../android-lib/include
- cp -rf /usr/local/include/faaccfg.h ../android-lib/include
- cp -rf /usr/local/lib/libfaac.a ../android-lib/lib
-
- 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
- package com.example.hellojni;
-
- import android.app.Activity;
- import android.widget.TextView;
- import android.os.Bundle;
-
-
- public class HelloJni extends Activity
- {
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle savedInstanceState)
- {
- super.onCreate(savedInstanceState);
-
- /* Create a TextView and set its content.
- * the text is retrieved by calling a native
- * function.
- */
- TextView tv = new TextView(this);
- tv.setText( stringFromJNI() );
- setContentView(tv);
- }
-
- /* A native method that is implemented by the
- * 'hello-jni' native library, which is packaged
- * with this application.
- */
- public native String stringFromJNI();
-
- /* This is another native method declaration that is *not*
- * implemented by 'hello-jni'. This is simply to show that
- * you can declare as many native methods in your Java code
- * as you want, their implementation is searched in the
- * currently loaded native libraries only the first time
- * you call them.
- *
- * Trying to call this function will result in a
- * java.lang.UnsatisfiedLinkError exception !
- */
- public native String unimplementedStringFromJNI();
-
- /* this is used to load the 'hello-jni' library on application
- * startup. The library has already been unpacked into
- * /data/data/com.example.hellojni/lib/libhello-jni.so at
- * installation time by the package manager.
- */
- static {
- System.loadLibrary("hello-jni");
- }
- }
從源代碼能夠看出,該Activity加載了名稱爲「libhello-jni.so」的類庫(Java代碼中不包含前面的「lib」和後面的「.so」),並聲明瞭stringFromJNI()方法。
在這裏,爲了調用FFmpeg而通過修改後的Activity加載類庫部分的源代碼以下所示。
[java] view plain copy
- static {
- System.loadLibrary("avutil-54");
- System.loadLibrary("avcodec-56");
- System.loadLibrary("avformat-56");
- System.loadLibrary("avdevice-56");
- System.loadLibrary("swresample-1");
- System.loadLibrary("swscale-3");
- System.loadLibrary("postproc-53");
- System.loadLibrary("avfilter-5");
- System.loadLibrary("hello-jni");
- }
(3)編寫C語言端代碼
a) 獲取C語言的接口函數聲明
根據Java對於C語言接口的定義,生成相應的接口函數聲明。這一步須要用到JDK中的「javah」命令。例如對於hello-jni例子,首先切換到src文件夾下,輸入以下命令:
[plain] view plain copy
- javah com.example.hellojni.HelloJni
就能夠在當前目錄下生成一個頭文件「com_example_hellojni_HelloJni.h」,該頭文件內容以下所示。
[cpp] view plain copy
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class com_example_hellojni_HelloJni */
-
- #ifndef _Included_com_example_hellojni_HelloJni
- #define _Included_com_example_hellojni_HelloJni
- #ifdef __cplusplus
- extern "C" {
- #endif
- /*
- * Class: com_example_hellojni_HelloJni
- * Method: stringFromJNI
- * Signature: ()Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI
- (JNIEnv *, jobject);
-
- /*
- * Class: com_example_hellojni_HelloJni
- * Method: unimplementedStringFromJNI
- * Signature: ()Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_unimplementedStringFromJNI
- (JNIEnv *, jobject);
-
- #ifdef __cplusplus
- }
- #endif
- #endif
從源代碼能夠看出,JNI調用的C語言函數是有固定格式的,即:
Java_{包名}_{包名}…_{類名}(JNIEnv *,…)
對於HelloJni類中的stringFromJNI方法,其C語言版本的函數聲明爲:
[cpp] view plain copy
- 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
- #include <string.h>
- #include <jni.h>
-
- /* This is a trivial JNI example where we use a native method
- * to return a new VM String. See the corresponding Java source
- * file located at:
- *
- * apps/samples/hello-jni/project/src/com/example/hellojni/HelloJni.java
- */
- jstring
- Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
- jobject thiz )
- {
- #if defined(__arm__)
- #if defined(__ARM_ARCH_7A__)
- #if defined(__ARM_NEON__)
- #define ABI "armeabi-v7a/NEON"
- #else
- #define ABI "armeabi-v7a"
- #endif
- #else
- #define ABI "armeabi"
- #endif
- #elif defined(__i386__)
- #define ABI "x86"
- #elif defined(__mips__)
- #define ABI "mips"
- #else
- #define ABI "unknown"
- #endif
-
- return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI ".");
- }
能夠看出,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
- #include <string.h>
- #include <jni.h>
- #include "libavcodec/avcodec.h"
-
- jstring
- Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,
- jobject thiz )
- {
- char info[10000] = { 0 };
- sprintf(info, "%s\n", avcodec_configuration());
- return (*env)->NewStringUTF(env, info);
- }
能夠看出該函數調用了libavcodec的avcodec_configuration()方法,用於獲取FFmpeg的配置信息。
c) 編寫Android.mk
完成C語言程序的編寫後,就能夠開始編寫Android的makefile文件Android.mk了。hello-jni例子中的Android.mk內容以下:
[plain] view plain copy
- LOCAL_PATH := $(call my-dir)
-
- include $(CLEAR_VARS)
-
- LOCAL_MODULE := hello-jni
- LOCAL_SRC_FILES := hello-jni.c
-
- include $(BUILD_SHARED_LIBRARY)
編譯FFmpeg示例程序的時候因爲用到了libavcodec等相關的庫,因此將Android.mk文件修改以下:
[plain] view plain copy
- LOCAL_PATH := $(call my-dir)
-
- # FFmpeg library
- include $(CLEAR_VARS)
- LOCAL_MODULE := avcodec
- LOCAL_SRC_FILES := libavcodec-56.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := avdevice
- LOCAL_SRC_FILES := libavdevice-56.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := avfilter
- LOCAL_SRC_FILES := libavfilter-5.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := avformat
- LOCAL_SRC_FILES := libavformat-56.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := avutil
- LOCAL_SRC_FILES := libavutil-54.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := postproc
- LOCAL_SRC_FILES := libpostproc-53.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := swresample
- LOCAL_SRC_FILES := libswresample-1.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := swscale
- LOCAL_SRC_FILES := libswscale-3.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- # Program
- include $(CLEAR_VARS)
- LOCAL_MODULE := hello-jni
- LOCAL_SRC_FILES := hello-jni.c
- 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)
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
- /**
- * 最簡單的基於FFmpeg的Helloworld例子-安卓
- * Simplest FFmpeg Android Helloworld
- *
- * 雷霄驊 Lei Xiaohua
- * leixiaohua1020@126.com
- * 中國傳媒大學/數字電視技術
- * Communication University of China / Digital TV Technology
- * http://blog.csdn.net/leixiaohua1020
- *
- *
- * 本程序是移植FFmpeg到安卓平臺的最簡單程序。它能夠打印出FFmpeg類庫的下列信息:
- * Protocol: FFmpeg類庫支持的協議
- * AVFormat: FFmpeg類庫支持的封裝格式
- * AVCodec: FFmpeg類庫支持的編解碼器
- * AVFilter: FFmpeg類庫支持的濾鏡
- * Configure: FFmpeg類庫的配置信息
- *
- * This is the simplest program based on FFmpeg in Android. It can show following
- * informations about FFmpeg library:
- * Protocol: Protocols supported by FFmpeg.
- * AVFormat: Container format supported by FFmpeg.
- * AVCodec: Encoder/Decoder supported by FFmpeg.
- * AVFilter: Filters supported by FFmpeg.
- * Configure: configure information of FFmpeg.
- *
- */
-
-
- package com.leixiaohua1020.sffmpegandroidhelloworld;
-
-
- import android.os.Bundle;
- import android.app.Activity;
- import android.text.method.ScrollingMovementMethod;
- import android.util.Log;
- import android.view.Menu;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.TextView;
-
- public class MainActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
-
- final TextView libinfoText = (TextView) findViewById(R.id.text_libinfo);
- libinfoText.setMovementMethod(ScrollingMovementMethod.getInstance());
-
- libinfoText.setText(configurationinfo());
-
- Button configurationButton = (Button) this.findViewById(R.id.button_configuration);
- Button urlprotocolButton = (Button) this.findViewById(R.id.button_urlprotocol);
- Button avformatButton = (Button) this.findViewById(R.id.button_avformat);
- Button avcodecButton = (Button) this.findViewById(R.id.button_avcodec);
- Button avfilterButton = (Button) this.findViewById(R.id.button_avfilter);
-
- urlprotocolButton.setOnClickListener(new OnClickListener() {
- public void onClick(View arg0){
- libinfoText.setText(urlprotocolinfo());
- }
- });
-
- avformatButton.setOnClickListener(new OnClickListener() {
- public void onClick(View arg0){
- libinfoText.setText(avformatinfo());
- }
- });
-
- avcodecButton.setOnClickListener(new OnClickListener() {
- public void onClick(View arg0){
- libinfoText.setText(avcodecinfo());
- }
- });
-
- avfilterButton.setOnClickListener(new OnClickListener() {
- public void onClick(View arg0){
- libinfoText.setText(avfilterinfo());
- }
- });
-
- configurationButton.setOnClickListener(new OnClickListener() {
- public void onClick(View arg0){
- libinfoText.setText(configurationinfo());
- }
- });
-
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- // Inflate the menu; this adds items to the action bar if it is present.
- getMenuInflater().inflate(R.menu.main, menu);
- return true;
- }
-
- //JNI
- public native String urlprotocolinfo();
- public native String avformatinfo();
- public native String avcodecinfo();
- public native String avfilterinfo();
- public native String configurationinfo();
-
- static{
- System.loadLibrary("avutil-54");
- System.loadLibrary("swresample-1");
- System.loadLibrary("avcodec-56");
- System.loadLibrary("avformat-56");
- System.loadLibrary("swscale-3");
- System.loadLibrary("postproc-53");
- System.loadLibrary("avfilter-5");
- System.loadLibrary("avdevice-56");
- System.loadLibrary("sffhelloworld");
- }
-
- }
C語言端源代碼位於jni/simplest_ffmpeg_helloworld.c,以下所示。
[cpp] view plain copy
- /**
- * 最簡單的基於FFmpeg的Helloworld例子-安卓
- * Simplest FFmpeg Android Helloworld
- *
- * 雷霄驊 Lei Xiaohua
- * leixiaohua1020@126.com
- * 中國傳媒大學/數字電視技術
- * Communication University of China / Digital TV Technology
- * http://blog.csdn.net/leixiaohua1020
- *
- *
- * 本程序是移植FFmpeg到安卓平臺的最簡單程序。它能夠打印出FFmpeg類庫的下列信息:
- * Protocol: FFmpeg類庫支持的協議
- * AVFormat: FFmpeg類庫支持的封裝格式
- * AVCodec: FFmpeg類庫支持的編解碼器
- * AVFilter: FFmpeg類庫支持的濾鏡
- * Configure: FFmpeg類庫的配置信息
- *
- * This is the simplest program based on FFmpeg in Android. It can show following
- * informations about FFmpeg library:
- * Protocol: Protocols supported by FFmpeg.
- * AVFormat: Container format supported by FFmpeg.
- * AVCodec: Encoder/Decoder supported by FFmpeg.
- * AVFilter: Filters supported by FFmpeg.
- * Configure: configure information of FFmpeg.
- */
-
- #include <stdio.h>
-
- #include "libavcodec/avcodec.h"
- #include "libavformat/avformat.h"
- #include "libavfilter/avfilter.h"
-
- //Log
- #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
-
-
- //FIX
- struct URLProtocol;
- /**
- * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.urlprotocolinfo()
- * Protocol Support Information
- */
- JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_urlprotocolinfo(JNIEnv *env, jobject obj){
-
- char info[40000]={0};
- av_register_all();
-
- struct URLProtocol *pup = NULL;
- //Input
- struct URLProtocol **p_temp = &pup;
- avio_enum_protocols((void **)p_temp, 0);
- while ((*p_temp) != NULL){
- sprintf(info, "%s[In ][%10s]\n", info, avio_enum_protocols((void **)p_temp, 0));
- }
- pup = NULL;
- //Output
- avio_enum_protocols((void **)p_temp, 1);
- while ((*p_temp) != NULL){
- sprintf(info, "%s[Out][%10s]\n", info, avio_enum_protocols((void **)p_temp, 1));
- }
-
- //LOGE("%s", info);
- return (*env)->NewStringUTF(env, info);
- }
-
- /**
- * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.avformatinfo()
- * AVFormat Support Information
- */
- JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_avformatinfo(JNIEnv *env, jobject obj){
-
- char info[40000] = { 0 };
-
- av_register_all();
-
- AVInputFormat *if_temp = av_iformat_next(NULL);
- AVOutputFormat *of_temp = av_oformat_next(NULL);
- //Input
- while(if_temp!=NULL){
- sprintf(info, "%s[In ][%10s]\n", info, if_temp->name);
- if_temp=if_temp->next;
- }
- //Output
- while (of_temp != NULL){
- sprintf(info, "%s[Out][%10s]\n", info, of_temp->name);
- of_temp = of_temp->next;
- }
- //LOGE("%s", info);
- return (*env)->NewStringUTF(env, info);
- }
-
- /**
- * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.avcodecinfo()
- * AVCodec Support Information
- */
- JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_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->decode!=NULL){
- sprintf(info, "%s[Dec]", info);
- }
- else{
- sprintf(info, "%s[Enc]", info);
- }
- switch (c_temp->type){
- case AVMEDIA_TYPE_VIDEO:
- sprintf(info, "%s[Video]", info);
- break;
- case AVMEDIA_TYPE_AUDIO:
- sprintf(info, "%s[Audio]", info);
- break;
- default:
- sprintf(info, "%s[Other]", info);
- break;
- }
- sprintf(info, "%s[%10s]\n", info, c_temp->name);
-
-
- c_temp=c_temp->next;
- }
- //LOGE("%s", info);
-
- return (*env)->NewStringUTF(env, info);
- }
-
- /**
- * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.avfilterinfo()
- * AVFilter Support Information
- */
- JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_avfilterinfo(JNIEnv *env, jobject obj)
- {
- char info[40000] = { 0 };
- avfilter_register_all();
- AVFilter *f_temp = (AVFilter *)avfilter_next(NULL);
- while (f_temp != NULL){
- sprintf(info, "%s[%10s]\n", info, f_temp->name);
- }
- //LOGE("%s", info);
-
- return (*env)->NewStringUTF(env, info);
- }
-
- /**
- * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.urlprotocolinfo()
- * Protocol Support Information
- */
- JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_configurationinfo(JNIEnv *env, jobject obj)
- {
- char info[10000] = { 0 };
- av_register_all();
-
- sprintf(info, "%s\n", avcodec_configuration());
-
- //LOGE("%s", info);
- return (*env)->NewStringUTF(env, info);
- }
Android.mk文件位於jni/Android.mk,以下所示。
[plain] view plain copy
- # Android.mk for FFmpeg
- #
- # Lei Xiaohua 雷霄驊
- # leixiaohua1020@126.com
- # http://blog.csdn.net/leixiaohua1020
- #
-
- LOCAL_PATH := $(call my-dir)
-
- # FFmpeg library
- include $(CLEAR_VARS)
- LOCAL_MODULE := avcodec
- LOCAL_SRC_FILES := libavcodec-56.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := avdevice
- LOCAL_SRC_FILES := libavdevice-56.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := avfilter
- LOCAL_SRC_FILES := libavfilter-5.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := avformat
- LOCAL_SRC_FILES := libavformat-56.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := avutil
- LOCAL_SRC_FILES := libavutil-54.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := postproc
- LOCAL_SRC_FILES := libpostproc-53.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := swresample
- LOCAL_SRC_FILES := libswresample-1.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- include $(CLEAR_VARS)
- LOCAL_MODULE := swscale
- LOCAL_SRC_FILES := libswscale-3.so
- include $(PREBUILT_SHARED_LIBRARY)
-
- # Program
- include $(CLEAR_VARS)
- LOCAL_MODULE := sffhelloworld
- LOCAL_SRC_FILES :=simplest_ffmpeg_helloworld.c
- 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)
其它部分源代碼暫不詳細例舉。
運行結果
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平臺的最簡單程序