最简单的基于FFmpeg的移动端例子:Android HelloWorld

=====================================================

最简单的基于FFmpeg的移动端例子系列文章列表:

最简单的基于FFmpeg的移动端例子:Android HelloWorld

最简单的基于FFmpeg的移动端例子:Android 视频解码器

最简单的基于FFmpeg的移动端例子:Android 视频解码器-单个库版

最简单的基于FFmpeg的移动端例子:Android 推流器

最简单的基于FFmpeg的移动端例子:Android 视频转码器

最简单的基于FFmpeg的移动端例子附件:Android 自带播放器

最简单的基于FFmpeg的移动端例子附件:SDL Android HelloWorld

最简单的基于FFmpeg的移动端例子:IOS HelloWorld

最简单的基于FFmpeg的移动端例子:IOS 视频解码器

最简单的基于FFmpeg的移动端例子:IOS 推流器

最简单的基于FFmpeg的移动端例子:IOS 视频转码器

最简单的基于FFmpeg的移动端例子附件:IOS自带播放器

最简单的基于FFmpeg的移动端例子:Windows Phone HelloWorld

=====================================================


从本文开始打算记录一系列FFmpeg在Android/IOS开发的示例程序。前面几篇文章记录FFmpeg安卓端开发的例子,后面几篇文章记录FFmpeg IOS端开发的例子。这些例子中FFmpeg相关的代码源自于《FFmpeg示例合集》中的程序。本文记录第一个程序:安卓平台下基于FFmpeg的HelloWorld程序。该程序的源代码源自于《最简单的基于FFMPEG的Helloworld程序》。



Android程序FFmpeg类库使用说明


Android应用程序使用FFmpeg类库的流程图如下所示。


上图中的流程可以分为“编译FFmpeg类库”、“编写Java端代码”、“编写C语言端代码”三个步骤。

(1)编译FFmpeg类库

a) 下载安装NDK

下载NDK之后直接解压缩就可以使用了。在Windows下使用的时候需要用到Cygwin。在这里我自己使用Linux编译类库。

b) 修改FFmpeg的configure
下载FFmpeg源代码之后,首先需要对源代码中的configure文件进行修改。由于编译出来的动态库文件名的版本号在.so之后(例如“libavcodec.so.5.100.1”),而android平台不能识别这样文件名,所以需要修改这种文件名。在configure文件中找到下面几行代码:

  
  
  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)'
替换为下面内容就可以了:

  
  
  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类库完整功能脚本
下面这个脚本可以生成一套功能完整,体积比较大的类库。

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

  
  
  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. build_one
  30. make
  31. make install
  32. cd ..

其中libfaac和libx264需要单独编译生成。它们编译过的类库位于“android-lib”文件夹中。libx264的编译脚本如下所示。

  
  
  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的编译脚本如下所示。

  
  
  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. ./configure \
  19. --without-mp4v2 \
  20. --host=arm-linux \
  21. -- enable-static \
  22. make
  23. make install
  24. cp -rf /usr/ local/include/faac.h ../android-lib/include
  25. cp -rf /usr/ local/include/faaccfg.h ../android-lib/include
  26. cp -rf /usr/ local/lib/libfaac.a ../android-lib/lib
  27. 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源代码如下所示。

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

从源代码可以看出,该Activity加载了名称为“libhello-jni.so”的类库(Java代码中不包含前面的“lib”和后面的“.so”),并声明了stringFromJNI()方法。
在这里,为了调用FFmpeg而经过修改后的Activity加载类库部分的源代码如下所示。

  
  
  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文件夹下,输入如下命令:
javah com.example.hellojni.HelloJni
  
  
就可以在当前目录下生成一个头文件“com_example_hellojni_HelloJni.h”,该头文件内容如下所示。

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

从源代码可以看出,JNI调用的C语言函数是有固定格式的,即:

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

对于HelloJni类中的stringFromJNI方法,其C语言版本的函数声明为:
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语言文件内容如下所示。

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

可以看出,Java_com_example_hellojni_HelloJni_stringFromJNI()根据宏定义判定了系统类型并且返回了一个字符串。在这里要注意,C语言中的char[]是不能直接对应为Java中的String类型的(即jstring)。char[]转换为String需要通过JNIEnv的NewStringUTF()函数。
为了调用FFmpeg而经过修改后的Java_com_example_hellojni_HelloJni_stringFromJNI()的源代码如下所示。

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

可以看出该函数调用了libavcodec的avcodec_configuration()方法,用于获取FFmpeg的配置信息。

c) 编写Android.mk
完成C语言程序的编写后,就可以开始编写Android的makefile文件Android.mk了。hello-jni例子中的Android.mk内容如下:

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

编译FFmpeg示例程序的时候由于用到了libavcodec等相关的库,所以将Android.mk文件修改如下:

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


  
  
  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. package com.leixiaohua1020.sffmpegandroidhelloworld;
  29. import android.os.Bundle;
  30. import android.app.Activity;
  31. import android.text.method.ScrollingMovementMethod;
  32. import android.util.Log;
  33. import android.view.Menu;
  34. import android.view.View;
  35. import android.view.View.OnClickListener;
  36. import android.widget.Button;
  37. import android.widget.TextView;
  38. public class MainActivity extends Activity {
  39. @Override
  40. protected void onCreate(Bundle savedInstanceState) {
  41. super.onCreate(savedInstanceState);
  42. setContentView(R.layout.activity_main);
  43. final TextView libinfoText = (TextView) findViewById(R.id.text_libinfo);
  44. libinfoText.setMovementMethod(ScrollingMovementMethod.getInstance());
  45. libinfoText.setText(configurationinfo());
  46. Button configurationButton = (Button) this.findViewById(R.id.button_configuration);
  47. Button urlprotocolButton = (Button) this.findViewById(R.id.button_urlprotocol);
  48. Button avformatButton = (Button) this.findViewById(R.id.button_avformat);
  49. Button avcodecButton = (Button) this.findViewById(R.id.button_avcodec);
  50. Button avfilterButton = (Button) this.findViewById(R.id.button_avfilter);
  51. urlprotocolButton.setOnClickListener( new OnClickListener() {
  52. public void onClick(View arg0){
  53. libinfoText.setText(urlprotocolinfo());
  54. }
  55. });
  56. avformatButton.setOnClickListener( new OnClickListener() {
  57. public void onClick(View arg0){
  58. libinfoText.setText(avformatinfo());
  59. }
  60. });
  61. avcodecButton.setOnClickListener( new OnClickListener() {
  62. public void onClick(View arg0){
  63. libinfoText.setText(avcodecinfo());
  64. }
  65. });
  66. avfilterButton.setOnClickListener( new OnClickListener() {
  67. public void onClick(View arg0){
  68. libinfoText.setText(avfilterinfo());
  69. }
  70. });
  71. configurationButton.setOnClickListener( new OnClickListener() {
  72. public void onClick(View arg0){
  73. libinfoText.setText(configurationinfo());
  74. }
  75. });
  76. }
  77. @Override
  78. public boolean onCreateOptionsMenu(Menu menu) {
  79. // Inflate the menu; this adds items to the action bar if it is present.
  80. getMenuInflater().inflate(R.menu.main, menu);
  81. return true;
  82. }
  83. //JNI
  84. public native String urlprotocolinfo();
  85. public native String avformatinfo();
  86. public native String avcodecinfo();
  87. public native String avfilterinfo();
  88. public native String configurationinfo();
  89. static{
  90. System.loadLibrary( "avutil-54");
  91. System.loadLibrary( "swresample-1");
  92. System.loadLibrary( "avcodec-56");
  93. System.loadLibrary( "avformat-56");
  94. System.loadLibrary( "swscale-3");
  95. System.loadLibrary( "postproc-53");
  96. System.loadLibrary( "avfilter-5");
  97. System.loadLibrary( "avdevice-56");
  98. System.loadLibrary( "sffhelloworld");
  99. }
  100. }

C语言端源代码位于jni/simplest_ffmpeg_helloworld.c,如下所示。

  
  
  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. #include <stdio.h>
  28. #include "libavcodec/avcodec.h"
  29. #include "libavformat/avformat.h"
  30. #include "libavfilter/avfilter.h"
  31. //Log
  32. #ifdef ANDROID
  33. #include <jni.h>
  34. #include <android/log.h>
  35. #define LOGE(format, ...) __android_log_print(ANDROID_LOG_ERROR, "(>_<)", format, ##__VA_ARGS__)
  36. #else
  37. #define LOGE(format, ...) printf("(>_<) " format "\n", ##__VA_ARGS__)
  38. #endif
  39. //FIX
  40. struct URLProtocol;
  41. /**
  42. * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.urlprotocolinfo()
  43. * Protocol Support Information
  44. */
  45. JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_urlprotocolinfo(JNIEnv *env, jobject obj){
  46. char info[ 40000]={ 0};
  47. av_register_all();
  48. struct URLProtocol *pup = NULL;
  49. //Input
  50. struct URLProtocol **p_temp = &pup;
  51. avio_enum_protocols(( void **)p_temp, 0);
  52. while ((*p_temp) != NULL){
  53. sprintf(info, "%s[In ][%10s]\n", info, avio_enum_protocols(( void **)p_temp, 0));
  54. }
  55. pup = NULL;
  56. //Output
  57. avio_enum_protocols(( void **)p_temp, 1);
  58. while ((*p_temp) != NULL){
  59. sprintf(info, "%s[Out][%10s]\n", info, avio_enum_protocols(( void **)p_temp, 1));
  60. }
  61. //LOGE("%s", info);
  62. return (*env)->NewStringUTF(env, info);
  63. }
  64. /**
  65. * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.avformatinfo()
  66. * AVFormat Support Information
  67. */
  68. JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_avformatinfo(JNIEnv *env, jobject obj){
  69. char info[ 40000] = { 0 };
  70. av_register_all();
  71. AVInputFormat *if_temp = av_iformat_next( NULL);
  72. AVOutputFormat *of_temp = av_oformat_next( NULL);
  73. //Input
  74. while(if_temp!= NULL){
  75. sprintf(info, "%s[In ][%10s]\n", info, if_temp->name);
  76. if_temp=if_temp->next;
  77. }
  78. //Output
  79. while (of_temp != NULL){
  80. sprintf(info, "%s[Out][%10s]\n", info, of_temp->name);
  81. of_temp = of_temp->next;
  82. }
  83. //LOGE("%s", info);
  84. return (*env)->NewStringUTF(env, info);
  85. }
  86. /**
  87. * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.avcodecinfo()
  88. * AVCodec Support Information
  89. */
  90. JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_avcodecinfo(JNIEnv *env, jobject obj)
  91. {
  92. char info[ 40000] = { 0 };
  93. av_register_all();
  94. AVCodec *c_temp = av_codec_next( NULL);
  95. while(c_temp!= NULL){
  96. if (c_temp->decode!= NULL){
  97. sprintf(info, "%s[Dec]", info);
  98. }
  99. else{
  100. sprintf(info, "%s[Enc]", info);
  101. }
  102. switch (c_temp->type){
  103. case AVMEDIA_TYPE_VIDEO:
  104. sprintf(info, "%s[Video]", info);
  105. break;
  106. case AVMEDIA_TYPE_AUDIO:
  107. sprintf(info, "%s[Audio]", info);
  108. break;
  109. default:
  110. sprintf(info, "%s[Other]", info);
  111. break;
  112. }
  113. sprintf(info, "%s[%10s]\n", info, c_temp->name);
  114. c_temp=c_temp->next;
  115. }
  116. //LOGE("%s", info);
  117. return (*env)->NewStringUTF(env, info);
  118. }
  119. /**
  120. * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.avfilterinfo()
  121. * AVFilter Support Information
  122. */
  123. JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_avfilterinfo(JNIEnv *env, jobject obj)
  124. {
  125. char info[ 40000] = { 0 };
  126. avfilter_register_all();
  127. AVFilter *f_temp = (AVFilter *)avfilter_next( NULL);
  128. while (f_temp != NULL){
  129. sprintf(info, "%s[%10s]\n", info, f_temp->name);
  130. }
  131. //LOGE("%s", info);
  132. return (*env)->NewStringUTF(env, info);
  133. }
  134. /**
  135. * com.leixiaohua1020.sffmpegandroidhelloworld.MainActivity.urlprotocolinfo()
  136. * Protocol Support Information
  137. */
  138. JNIEXPORT jstring Java_com_leixiaohua1020_sffmpegandroidhelloworld_MainActivity_configurationinfo(JNIEnv *env, jobject obj)
  139. {
  140. char info[ 10000] = { 0 };
  141. av_register_all();
  142. sprintf(info, "%s\n", avcodec_configuration());
  143. //LOGE("%s", info);
  144. return (*env)->NewStringUTF(env, info);
  145. }

Android.mk文件位于jni/Android.mk,如下所示。

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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要下载一个基于FFmpeg SDL的最简单的视频播放器,可以按照以下步骤进行: 1. 首先,需要下载和安装FFmpeg软件包。FFmpeg是一个开源的跨平台多媒体框架,可以用于处理音频和视频文件。可以上FFmpeg官网(https://www.ffmpeg.org/)找到相应的下载链接,并根据操作系统选择正确的版本进行下载和安装。 2. 下载SDL库。SDL是一个跨平台的开发库,可以用于创建多媒体应用程序。可以在SDL官网(https://www.libsdl.org/)上找到相应的下载链接,并选择适合自己操作系统的版本进行下载和安装。 3. 使用编程语言(如C/C++)编写一个基于FFmpeg和SDL的视频播放器。可以使用任何喜欢的集成开发环境(IDE),如Visual Studio、Dev-C++等。根据自己的需求,可以封装FFmpeg和SDL的相关函数,以方便播放视频文件。 4. 在编程中,需要包含FFmpeg和SDL所需的头文件,并链接FFmpeg和SDL的库文件。可以在编译选项中添加"-lffmpeg"和"-lsdl"等参数。 5. 编写代码来打开视频文件,读取视频流,将每一帧解码和渲染到屏幕上并进行播放。可以使用FFmpeg提供的函数来进行解码和渲染,使用SDL提供的函数来显示图像并进行窗口管理。 6. 编译和运行程序,即可实现最简单的基于FFmpeg SDL的视频播放器。可以通过命令行输入视频文件的路径进行播放。 需要注意的是,基于FFmpeg SDL的视频播放器可以根据个人需求来进行功能的扩展,如添加播放控制(播放、暂停、停止等)、全屏显示、音量调节等功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值