当前位置: 首页 > 工具软件 > FFMPEG-Java > 使用案例 >

ffmpeg-java-android视频处理

商辰钊
2023-12-01

java项目中需要引入ffmpeg,主要用于处理视频转码和视频加水印。

ffmpeg是一个主流的音视频处理库,C语言编写。官网地址:http://ffmpeg.org/ 也有中文版地址。

网上关于ffmpeg java引入的帖子大多是先有ffmpeg可执行工具,然后再java模拟os命令执行。

而我们项目会在客户方的服务器上部署,当时看到这类帖子第一个想法是,客户方服务器是集群的,总不能让客户方每台服务器都装一个ffmpeg工具,那太麻烦客户了。然后就走进了另一个思维定式的坑。。。

原谅我是android刚转后端开发不久,当时就想着不如用android交叉编译出想要的jni中间层,然后放到java工程下用就好了,然后就各种尝试。有的帖子还是写得不错的,在此贴出来供需要android交叉编译的人参考。

 

以下将描述mac环境下android的ffmpeg动态库开发:

假设已具备android ndk基础,能看懂并改写简单c代码的基础。

step 1:mac环境编译ffmpeg的so库,推荐选择mac或linux操作系统做这一步,因为windows下做要解决的问题比较多。

ffmpeg这里有个牛人,英年早逝的雷霄骅csdn博客,但因为停更好几年了用的库都比较老,拿来做实战教程不太合适,但作为ffmpeg学习栈还是相当好的。从这位牛人的生平也能看出,年(cheng)轻(xu)人(yuan)还是不要太拼,还是要爱惜自己的生命。。。

ffmpeg编译趟过的坑大部分都在这篇里:http://blog.sina.com.cn/s/blog_61bc01360102w815.html。坑还是相当多的,谁编谁知道。

参考好多编译脚本,这个作者写的最好最全面:https://github.com/coopsrc/FFPlayerDemo/blob/master/build_ffmpeg.sh。支持

armeabi、armeabi-v7a、arm64-v8a、x86、x86_64等市面上能见着的所有android cpu,且连一个demo作者都在持续更新,看着感觉像是企业级在使用的,相当靠谱了。

当时就是想着我们服务器也是x86_64位的,那既然不会写makefile也不会用ide编动态库,不如借助android ndk工具打一个x86_64吧。cry~,事后咨询了一个linux行家,才知道,linux主要指令用的libc.so,而android定制了用的bionic.so,所以走了好大一个弯。

不如我把我自己的android_build.sh也分享一下吧,骗骗csdn积分。

其实就在build_ffmpeg.sh基础上改写了一下。

step 2:编写android下的jni中间层,坑了我好几天的一个步骤。

大体教程可以参考这个作者写的几篇:https://juejin.im/post/5caeb0a76fb9a0688a67fef9。2019年的,新鲜好用。

用的demo几乎是这个作者的demo基础上改的:https://blog.csdn.net/u014418171/article/details/53337759

这里面的坑也是巨多。

坑1:step1做完后会在ffmpeg编译目录下自动生成一个config.h,这个是编译过程产生的宏定义,每个人的编译需求都不同,需要把自己的文件替换这个demo的 config.h。不然会报各种error:user of undeclared identifer '***'变量找不到。

例如我用上述android_build.sh编译生成的config.h大概会长如下样子

#ifndef FFMPEG_CONFIG_H
#define FFMPEG_CONFIG_H
#define FFMPEG_CONFIGURATION "--prefix=android-build --libdir=android-build/libs/x86_64 --incdir=android-build/includes/x86_64 --pkgconfigdir=android-build/pkgconfig/x86_64 --arch=x86_64 --cpu=x86_64 --cross-prefix=/Users/zqyr/Downloads/android-ndk-r13b/toolchains/x86_64-4.9/prebuilt/darwin-x86_64/bin/x86_64-linux-android- --sysroot=/Users/zqyr/Downloads/android-ndk-r13b/platforms/android-21/arch-x86_64/ --extra-ldexeflags=-pie --target-os=linux --disable-static --enable-shared --enable-small --disable-programs --disable-ffmpeg --disable-ffplay --disable-ffprobe --disable-doc --disable-symver --disable-asm --enable-decoder=vorbis --enable-decoder=opus --enable-decoder=flac"
#define FFMPEG_LICENSE "LGPL version 2.1 or later"
#define CONFIG_THIS_YEAR 2019
#define FFMPEG_DATADIR "android-build/share/ffmpeg"
#define AVCONV_DATADIR "android-build/share/ffmpeg"
#define CC_IDENT "gcc 4.9.x (GCC) 20150123 (prerelease)"
#define av_restrict restrict
#define EXTERN_PREFIX ""
#define EXTERN_ASM 
#define BUILDSUF ""
#define SLIBSUF ".so"
#define HAVE_MMX2 HAVE_MMXEXT
#define SWS_MAX_FILTER_SIZE 256
#define ARCH_AARCH64 0
#define ARCH_ALPHA 0

...

坑2:如果按照这个demohttps://blog.csdn.net/u014418171/article/details/53337759,其实工程里的include头文件不全,没啥用,还不如用官网下的ffmpeg原文件include。android.mk如下

LOCAL_C_INCLUDES := /Users/***/***/ffmpeg-4.0.4

c语言的include和java的import概念差别挺大的,但是只要加对了系统及本地库引用路径,相对地址和绝对地址还是一样一样滴。

这里也是困扰我比较久的地方。

坑3:按照这个demohttps://blog.csdn.net/u014418171/article/details/53337759教程,需要把你当时用的官网ffmpeg版本下的几个tools工具.h.c都copy过来,然后ffmpeg-4.0.4新增了ffmpeg_hw.c,android.mk ffmpeg中间层ffmpeg_hw.c也要一起打包进来才行,不然会报error:undefined reference to 'hw_device_setup_for_decode'等各种hw相关的错误,主要ffmpeg.c有调用ffmpeg_hw.c的东西。

总之这一步的坑着实很多,当我成功编译出android中间层的时候有一种想哭的冲动。。。没有好的建议,总之就踩出问题然后根据error打地鼠一般逐步解决吧。

 

说了这么多,然而这篇帖子主要不是想表达android下的ffmpeg开发,而是想表达java项目下的ffmpeg引入。

所以以上写的东西对java项目ffmeg的开发没有任何帮助。。。

最终我采取了在x86_64 linux虚拟机上编译ffmpeg可执行包,编译选项为:

./configure --prefix=/usr/local/ffmpeg_text --disable-yasm --enable-small --enable-libfreetype --disable-ffplay --disable-ffprobe --disable-doc --disable-symver --disable-asm

找到bin下面的可执行文件ffmpeg,打出来也不大,十几M的样子。

然后把可执行包打包进java项目,server init时放在服务器某处,然后再java模拟os命令执行。

public static void main(String args[]) {
		String command = "ffmpeg -i /home/parallels/Documents/vedio.mp4 -vf \"drawtext=fontfile=/home/parallels/Documents/FreeMono.ttf: text=‘2019-08-11’:x=100:y=10:fontsize=24:fontcolor=black:shadowy=2\" -b:v 3000k /home/parallels/Documents/vedio_text.mp4";
		System.out.println("command = " + command);
		try {
			Runtime rt = Runtime.getRuntime();

			Process proc = rt.exec(new String[] { "sh", "-c", command });
			InputStream stderr = proc.getErrorStream(); // proc.getInputStream();
														// //proc.getErrorStream();
			InputStreamReader isr = new InputStreamReader(stderr);
			BufferedReader br = new BufferedReader(isr);
			String line = null;
			while ((line = br.readLine()) != null)
				System.out.println(line);
			int exitVal = proc.waitFor();
			System.out.println("Process exitValue: " + exitVal);
		} catch (Throwable t) {
			t.printStackTrace();
		}

	}

然后就搞定了。。。

迂回了一大圈最后又绕回了原点,其实我是要被自己蠢哭的。

 类似资料: