文档章节

UBUNTU14.04LTS64位下为Android平台编译FFmpeg2.5

zhangyujsj
 zhangyujsj
发布于 2015/05/08 14:21
字数 1601
阅读 60
收藏 0

最近要为论文搭建实验环境,需要将FFmpeg移植到Android平台。参考很多前辈的技术文章,研究如下。另外,Android下的多媒体技术非常有意思,要学的东西很多,编译个小库只是很简单的一件事,麻烦的是如何在这个基础上去做应用。JNI如何封装,上层应用又如何跟底下的库协同工作,如何架构,这才是有价值的技术,会吗?

交叉编译主机:UBUNTU14.04LTS 64位

Android NDK:android-ndk-r9d-linux-x86_64.tar.bz2

Android SDK:adt-bundle-linux-x86_64-20140702.zip

1 下载并安装Android NDK和SDK && 下载FFmpeg库源码(FFmpeg2.5)

这个过程没有什么技术含量

2 用NDK-r9d编译FFmpeg

这类开源库的编译或者交叉编译无非就是三步:1 configure;2 make; make install

2.1 configure

如果直接按照未修改的配置进行编译,结果编译出来的so文件类似libavcodec.so.55.39.101,版本号位于so之后,Android上似乎无法加载。因此需要按如下修改:

将该文件中的如下四行:

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)'

替换为:

SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'

LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'

SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'

SLIB_INSTALL_LINKS='$(SLIBNAME)'

在此之后,可以开始配置,编写一个配置的shell脚本,内容如下:

#!/bin/bash
NDK=/home/abc/tools/android_ndk/android-ndk-r9d/
SYSROOT=$NDK/platforms/android-16/arch-arm/
TOOLCHAIN=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt/linux-x86_64

function build_one
{
  ./configure \
    --prefix=$PREFIX \
    --enable-shared \
    --disable-static \
    --disable-doc \
    --disable-ffserver \
    --enable-cross-compile \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --target-os=linux \
    --arch=arm \
    --sysroot=$SYSROOT \
    --extra-cflags="-Os -fpic $ADDI_CFLAGS" \
    --extra-ldflags="$ADDI_LDFLAGS" \
    $ADDITIONAL_CONFIGURE_FLAG
}
CPU=arm
PREFIX=$(pwd)/android/$CPU
ADDI_CFLAGS="-marm"
build_one 

2.2 make ;make install

make的时候可以多线程make,我的I7-4700是四核八线程的,用-j9。

LD    libavutil/libavutil-54.so
LD    libswscale/libswscale-3.so
LD    libswresample/libswresample-1.so
LD    libavcodec/libavcodec-56.so
LD    libavformat/libavformat-56.so
LD    libavfilter/libavfilter-5.so
LD    libavdevice/libavdevice-56.so

在android/arm/目录下就有交叉编译好的include和lib目录,这些是所需的。抽取include目录下所有和lib目录里如下所示的文件:

-rwxr-xr-x 1 abc abc 8.5M  5月  6 15:18 libavcodec-56.so
-rwxr-xr-x 1 abc abc  58K  5月  6 15:18 libavdevice-56.so
-rwxr-xr-x 1 abc abc 790K  5月  6 15:18 libavfilter-5.so
-rwxr-xr-x 1 abc abc 1.6M  5月  6 15:18 libavformat-56.so
-rwxr-xr-x 1 abc abc 326K  5月  6 15:18 libavutil-54.so
-rwxr-xr-x 1 abc abc  82K  5月  6 15:18 libswresample-1.so
-rwxr-xr-x 1 abc abc 334K  5月  6 15:18 libswscale-3.so

至此,FFmpeg的编译就已经完成了,接下来,主要是将其应用在安桌应用程序里,一方面研究如何在Android应用中调用FFmpeg库来工作,另一方面,确认FFmpeg库是否能正常工作。

3 创建Android应用程序工程

JNI项目的常规流程就是4步,先了解这四步:

A 创建一个类,其中包含要实现的native成员方法

B 用javah -jni A步定义的类生成的class名,生成头文件,用C/C++实现生成的头文件中的方法

C 编写Android.mk和Application.mk文件并处理好要用到的头文件和链接库文件

D ndk-build编译

3.1 创建Android应用程序工程,并创建用于封装解码的FFmpegNative类

public class FFmpegNative {
	static {
		System.loadLibrary("avutil-54"); // 1
		System.loadLibrary("swresample-1"); // 2
		System.loadLibrary("avcodec-56"); // 3
		System.loadLibrary("avformat-56"); // 4
		System.loadLibrary("swscale-3"); // 5
		System.loadLibrary("avfilter-5"); // 6
		System.loadLibrary("avdevice-56"); // 7
	}
	public native int avcodec_find_decoder(int codecID);
}
其中定义了native成员方法,在eclipse中保存会自动编译为class文件。

3.2 javah -jni

在安桌工程的根目录下创建一个jni目录,用来放jni所有相关的文件。

将2中的lib文件和include目录下所有头文件放到jni下。再到android工程目录的bin/class目录下,执行如下命令:

javah -jni -d ../../jni/ com.example.ffmpeg4android.FFmpegNative
即可生成头文件到jni目录,内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_ffmpeg4android_FFmpegNative */

#ifndef _Included_com_example_ffmpeg4android_FFmpegNative
#define _Included_com_example_ffmpeg4android_FFmpegNative
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_ffmpeg4android_FFmpegNative
 * Method:    avcodec_find_decoder
 * Signature: (I)I
 */
JNIEXPORT jint JNICALL Java_com_example_ffmpeg4android_FFmpegNative_avcodec_1find_1decoder
  (JNIEnv *, jobject, jint);

#ifdef __cplusplus
}
#endif
#endif
实现其中的native方法,代码如下,保存在com_example_ffmpeg4android_FFmpegNative.c文件中:
#include <math.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
#include <libavformat/avformat.h>  
#include <libswscale/swscale.h>
#include <jni.h>
#include "com_example_ffmpeg4android_FFmpegNative.h"

jint JNICALL Java_com_example_ffmpeg4android_FFmpegNative_avcodec_1find_1decoder(JNIEnv *env, jobject obj, jint codecID) 
{
  AVCodec*codec = NULL;  

  /*register all formats and codecs */  
  av_register_all();  

  codec= avcodec_find_decoder(codecID);   // avcodec_find_decoder是根据codec的ID来找AVCodec的

  if(codec != NULL)  
  {   
    return 0;  
  }   
  else  
  {   
    return -1;  
  }   
}



3.3 Android.mk和Application.mk文件及其相关头和库文件

在工程的jni目录下的Android.mk文件内容如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE :=avcodec-56
LOCAL_SRC_FILES :=libavcodec-56.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=avdevice-56
LOCAL_SRC_FILES :=libavdevice-56.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=avfilter-5
LOCAL_SRC_FILES :=libavfilter-5.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=avformat-56
LOCAL_SRC_FILES :=libavformat-56.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=  avutil-54
LOCAL_SRC_FILES :=libavutil-54.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE :=  avswresample-1
LOCAL_SRC_FILES :=libswresample-1.so
include $(PREBUILT_SHARED_LIBRARY)

LOCAL_MODULE :=  swscale-3
LOCAL_SRC_FILES :=libswscale-3.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE :=ffmpeg_codec
LOCAL_SRC_FILES :=com_example_ffmpeg4android_FFmpegNative.c

LOCAL_LDLIBS := -llog -ljnigraphics -lz -landroid
LOCAL_SHARED_LIBRARIES:= avcodec-56 avdevice-56 avfilter-5 avformat-56 avutil-54

include $(BUILD_SHARED_LIBRARY)
其中的Application.mk文件内容如下:
APP_ABI:=armeabi
APP_PLATFORM:=android-16



其中的APP_ABI是指定为那种架构的CPU生成,APP_PLATFORM则是指定SDK版本,为Android4.1生成,对应的是API16,所以是android-16.

3.4 ndk-build编译

abc@k610c:~/and_workspace/FFmpeg4Android/jni$ ndk-build
[armeabi] Install        : libavcodec-56.so => libs/armeabi/libavcodec-56.so
[armeabi] Install        : libavdevice-56.so => libs/armeabi/libavdevice-56.so
[armeabi] Install        : libavfilter-5.so => libs/armeabi/libavfilter-5.so
[armeabi] Install        : libavformat-56.so => libs/armeabi/libavformat-56.so
[armeabi] Install        : libswresample-1.so => libs/armeabi/libswresample-1.so
[armeabi] Install        : libavutil-54.so => libs/armeabi/libavutil-54.so
[armeabi] Compile thumb  : ffmpeg_codec <= com_example_ffmpeg4android_FFmpegNative.c
[armeabi] SharedLibrary  : libffmpeg_codec.so
[armeabi] Install        : libffmpeg_codec.so => libs/armeabi/libffmpeg_codec.so
[armeabi] Prebuilt       : libswscale-3.so <= jni/prebuilt/
[armeabi] Install        : libswscale-3.so => libs/armeabi/libswscale-3.so

3.5 完善工程并验证

此时,已经生成了libffmpeg_codec.so这个文件,这个库是编写的jni代码生成的库,所以也要加载到应用。修改FFmpegNative类定义如下:

public class FFmpegNative {
	static {
		System.loadLibrary("avutil-54"); // 1
		System.loadLibrary("swresample-1"); // 2
		System.loadLibrary("avcodec-56"); // 3
		System.loadLibrary("avformat-56"); // 4
		System.loadLibrary("swscale-3"); // 5
		System.loadLibrary("avfilter-5"); // 6
		System.loadLibrary("avdevice-56"); // 7
		System.loadLibrary("ffmpeg_codec"); // 8
		/*特别注意,在ffmpeg2.5中这8个库的顺序是不能错的,因为,加库的时候库有先后依赖关系*/
	}
	public native int avcodec_find_decoder(int codecID);
}



在Activity里面创建一个TextView控件,然后根据调用native方法的返回值结果来显示。

TextView tv = (TextView)this.findViewById(R.id.textview_hello);
		
FFmpegNative ffmpeg = new FFmpegNative();
int codecId = 28; //H.264
int res = ffmpeg.avcodec_find_decoder(codecId);
if(res == 0) {
    tv.setText("Success");
} else {
    tv.setText("Failed");
}

结果显示Success


© 著作权归作者所有

zhangyujsj
粉丝 24
博文 358
码字总数 224241
作品 0
广州
私信 提问
Ubuntu12.04系统编译Android4.4源码

系统环境 系统:Ubuntu12.04 64位系统。 源码:Android4.4源码。 选择Ubuntu12.04,是因为在14.04上没能折腾成功,在上面兼容32位程序的配置有点烦。12.04必须要用64位的,编译Android4.4时需...

stillcook
2014/10/14
0
4
[Tools] [cmake] 调试笔记 --- 64位平台下的cmake配置

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/kris_fei/article/details/84644996 背景: 64位平台需要使用armv8版本编译so。 调试: 从armv7版本配置上修改...

KrisFei
2018/11/30
0
0
最简单的基于FFmpeg的移动端例子:Android HelloWorld

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/leixiaohua1020/article/details/47008825 ===================================================== 最简单的...

雷霄骅
2015/07/23
0
0
【几维安全】lua脚本加密,lua代码加密使用说明

概述 xLua加密工具主要是一款Lua安全编译器,配置好xLua环境后,用几维提供的libluajit.a替换对应的默认文件,来实现对Lua文件的加密,加密后的Lua文件只能由几维提供的lua bytecode解释器解...

工作的事
04/17
0
0
Android客户端.So 精简策略

1. 理论基础 首先介绍一下 目前所有手机上 的指令集 Android系统目前支持以下七种不同的CPU架构,每一种都关联着一个相应的ABI。而架构上对应的指令集是以下几种 Intel 64 指令集在 x86基础上...

ccj659
2017/10/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

jenkins定时构建时间设置

举几个例子: 每隔5分钟构建一次 H/5 * * * * 每两小时构建一次 H H/2 * * * 每天中午12点定时构建一次 H 12 * * * 每天下午18点定时构建一次 H 18 * * * 在每个小时的前半个小时内的每10分钟...

shzwork
17分钟前
0
0
Myeclipse 问题记录

1. 创建maven聚合(pom) 工程 ,子项目会带上 maven app这些文字,删掉后会导致工程文件在working set里消失,解决办法:右键子项目 import as project 解决问题。...

无敌小学僧
45分钟前
1
0
《Chez Scheme初探》定义变量、递归、测试性能、并列代码编写

普通fib函数 (define (fact n) (if (= n 1) 1 (* n (fact (- n 1)) ) )) 尾递归fib函数 (define (fact-tail n) (fact-rec n n))(defi......

flash胜龙
45分钟前
1
0
任正非对华为热点问题的回应亮了,终于知道华为为什么能扛过这一次的冲击!

任正非对华为热点问题的回应亮了,终于知道华为为什么能扛过这一次的冲击! 如果你是华为的老板,看到一条传遍网络的“美国封锁华为”、“华为禁令”的消息,你会怎么办? 昨天上午,华为创始...

forespider
今天
2
0
Java HTTP 组件库选型看这篇就够了

最近项目需要使用 Java 重度调用 HTTP API 接口,于是想着封装一个团队公用的 HTTP client lib. 这个库需要支持以下特性: 连接池管理,包括连接创建和超时、空闲连接数控制、每个 host 的连...

Java面经
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部