博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在Android中使用FFmpeg(android studio环境)
阅读量:7068 次
发布时间:2019-06-28

本文共 8616 字,大约阅读时间需要 28 分钟。

1.首先我们需要一个已经编译好的libffmpeg.so文件。(怎么编译是个大坑,可以参考,也可以用网上下载的现成的,本文相关的github项目里也有。),当然也要下载好ffmpeg的源码,一会要用到。

2.打开你的Android工程,在 src/main/ 目录下新建 jni 目录。并将libffmpeg.so文件丢进去。

3.创建FFmpegKit.java。写入如下代码

package codepig.ffmpegcldemo;import android.os.AsyncTask;public class FFmpegKit {    public interface KitInterface{        void onStart();        void onProgress(int progress);        void onEnd(int result);    }    static{        System.loadLibrary("ffmpeg");        System.loadLibrary("ffmpeginvoke");    }    public static int execute(String[] commands){        return run(commands);    }    public static void execute(String[] commands, final KitInterface kitIntenrface){        new AsyncTask
(){ @Override protected void onPreExecute() { if(kitIntenrface != null){ kitIntenrface.onStart(); } } @Override protected Integer doInBackground(String[]... params) { return run(params[0]); } @Override protected void onProgressUpdate(Integer... values) { if(kitIntenrface != null){ kitIntenrface.onProgress(values[0]); } } @Override protected void onPostExecute(Integer integer) { if(kitIntenrface != null){ kitIntenrface.onEnd(integer); } } }.execute(commands); } public native static int run(String[] commands);}

 

这个是用来调用ffmpeg可执行文件的。

4.在终端中切到src/main/java文件夹下,输入:

javah codepig.ffmpegcldemo.FFmpegKit

 

(这里注意你自己的文件的实际位置)

然后就会在该目录生成 codepig_ffmpegecldemo_FFmpegKit.h 文件,将这个文件移动到 jni 目录。

5.复制FFmpeg源码文件 ffmpeg.h, ffmpeg.c, ffmpeg_opt.c, ffmpeg_filter.c,cmdutils.c, cmdutils.h, cmdutils_common_opts.h 到jni目录下。

在 jni 目录新建文件 Android.mk Application.mk codepig_ffmpegcldemo_FFmpegKit.c。

6.编辑ffmpeg.c,把

int main(int argc, char **argv)

 

改名为

int run(int argc, char **argv)

 

编辑ffmpeg.h, 在文件末尾添加函数申明:

int run(int argc, char **argv)

 

7.编辑cmdutils.c中的exit_program函数,删掉函数中原来的内容, 添加 return ret;并修改函数的返回类型为int。

长这样:

int exit_program(int ret){    return ret;}

 

编辑cmdutils.h中exit_program的申明,把返回类型修改为int。

长这样:

int exit_program(int ret);

 

8.在 codepig_ffmpegcldemo_FFmpegKit.c 中实现 codepig_ffmpegcldemo_FFmpegKit.h 中的方法。

#include 
#include "codepig_ffmpegcldemo_FFmpegKit.h"#include "ffmpeg.h"#include "logjam.h"JNIEXPORT jint JNICALL Java_codepig_ffmpegcldemo_FFmpegKit_run(JNIEnv *env, jclass obj, jobjectArray commands){ //FFmpeg av_log() callback int argc = (*env)->GetArrayLength(env, commands); char *argv[argc]; LOGD("Kit argc %d\n", argc); int i; for (i = 0; i < argc; i++) { jstring js = (jstring) (*env)->GetObjectArrayElement(env, commands, i); argv[i] = (char*) (*env)->GetStringUTFChars(env, js, 0); LOGD("Kit argv %s\n", argv[i]); } return run(argc, argv);}

 

9.编辑Android.mk

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := ffmpegLOCAL_SRC_FILES := libffmpeg.soinclude $(PREBUILT_SHARED_LIBRARY)include $(CLEAR_VARS)LOCAL_MODULE := ffmpeginvokeLOCAL_SRC_FILES := codepig_ffmpegcldemo_FFmpegKit.c ffmpeg.c ffmpeg_opt.c cmdutils.c ffmpeg_filter.cLOCAL_C_INCLUDES := F:/demo/ffmpeg-3.0LOCAL_LDLIBS := -llog -lz -ldlLOCAL_SHARED_LIBRARIES := ffmpeginclude $(BUILD_SHARED_LIBRARY)

 

其中LOCAL_C_INCLUDES的值为ffmpeg源码文件夹地址

10.编辑Application.mk 文件

APP_ABI := armeabi-v7aAPP_PLATFORM := android-17

 

其中APP_ABI的值是支持的cpu类型。要支持多种cpu的话,可以把类型写一起用空格隔开,比如

APP_ABI := armeabi-v7a x86

 

11.在终端中定位到jni目录,执行ndk -build

成功后就会在libs文件夹生成相应的libffmpeg.so和libffmpeginvoke.so文件。这些so文件就是最终我们用来调用的FFmpeg可执行文件。
如果出现如下错误提示

Android NDK: Could not find application project directory !Android NDK: Please define the NDK_PROJECT_PATH variable to point to it.

 

则在Application.mk文件中添加

APP_BUILD_SCRIPT := Android.mk

 

如果出现如下提示

Android NDK: Your Android application project path contains spaces: 'F:/qzd android/Android/workspace/'Android NDK: The Android NDK build cannot work here. Please move your project to a different location.

 

那大约是因为项目所在文件夹名称有空格。改名就好了(感觉好弱鸡)

12.在build.gradle文件中修改下库文件地址的指向

android {    sourceSets {        main {            jniLibs.srcDirs = ['src/main/libs']            jni.srcDirs=[]        }    }}

 

现在终于可以在android中使用ffmpeg库了。

13.举个栗子

(以下例子里的videoUrl是原始视频文件地址,imageUrl是水印图片地址,musicUrl是音频mp3地址,outputUrl是最终输出视频地址。)

(1) 给视频添加图片水印:

Runnable compoundRun=new Runnable() {            @Override            public void run() {                String[] commands = new String[10];                commands[0] = "ffmpeg";                commands[1] = "-i";                commands[2] = videoUrl;                commands[3] = "-i";                commands[4] = imageUrl;                commands[5] = "-filter_complex";                commands[6] = "overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2";                commands[7] = "-codec:a";                commands[8] = "copy";                commands[9] = outputUrl;                FFmpegKit.execute(commands, new FFmpegKit.KitInterface() {                    @Override                    public void onStart() {                        Log.d("FFmpegLog LOGCAT","FFmpeg 命令行开始执行了...");                    }                    @Override                    public void onProgress(int progress) {                        Log.d("FFmpegLog LOGCAT","done com"+"FFmpeg 命令行执行进度..."+progress);                    }                    @Override                    public void onEnd(int result) {                        Log.d("FFmpegLog LOGCAT","FFmpeg 命令行执行完成...");                    }                });            }        };        ThreadPoolUtils.execute(compoundRun);

 

(2) 合成音频视频

Runnable compoundRun=new Runnable() {            @Override            public void run() {                String[] commands = new String[6];                commands[0] = "ffmpeg";                commands[1] = "-i";                commands[2] = videoUrl;                commands[3] = "-i";                commands[4] = musicUrl;                commands[5] = outputUrl;                FFmpegKit.execute(commands, new FFmpegKit.KitInterface() {                    @Override                    public void onStart() {                        Log.d("FFmpegLog LOGCAT","FFmpeg 命令行开始执行了...");                    }                    @Override                    public void onProgress(int progress) {                        Log.d("FFmpegLog LOGCAT","done com"+"FFmpeg 命令行执行进度..."+progress);                    }                    @Override                    public void onEnd(int result) {                        Log.d("FFmpegLog LOGCAT","FFmpeg 命令行执行完成...");//                        getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);//                        Message msg = new Message();//                        msg.what = 1;//                        mHandler.sendMessage(msg);                    }                });            }        };        ThreadPoolUtils.execute(compoundRun);

 

(3)把这两个命令写一起

Runnable compoundRun=new Runnable() {            @Override            public void run() {                String[] commands = new String[11];                commands[0] = "ffmpeg";                commands[1] = "-i";                commands[2] = videoUrl;                commands[3] = "-i";                commands[4] = imageUrl;                commands[5] = "-filter_complex";                commands[6] = "overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2";                commands[7] = "-i";                commands[8] = musicUrl;                commands[9] = "-y";                commands[10] = outputUrl;                FFmpegKit.execute(commands, new FFmpegKit.KitInterface() {                    @Override                    public void onStart() {                        Log.d("FFmpegLog LOGCAT","FFmpeg 命令行开始执行了...");                    }                    @Override                    public void onProgress(int progress) {                        Log.d("FFmpegLog LOGCAT","done com"+"FFmpeg 命令行执行进度..."+progress);                    }                    @Override                    public void onEnd(int result) {                        Log.d("FFmpegLog LOGCAT","FFmpeg 命令行执行完成...");//                        getWindow().setFlags(0, WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);                    }                });            }        };        ThreadPoolUtils.execute(compoundRun);

 

附:ffmpeg命令格式简要说明

基本形式:

ffmpeg -i inputFile -参数名 参数值 …… outputFile;

头尾不变,中间的参数顺序无所谓。但是一个操作的参数必须写一起。

参数是以 -参数名+参数值 这样的成对形式赋值。


参考:

本文github项目:

作者链接:https://www.jianshu.com/p/e0c32c8b0ebc

转载于:https://www.cnblogs.com/Sharley/p/10438660.html

你可能感兴趣的文章
TCP/IP中的四元组、五元组、七元组
查看>>
用代码告诉你“问世间情为何物,直教人生死相许”
查看>>
(PHP)设置修改 Apache 文件根目录 (Document Root)(转帖)
查看>>
使用sqlite保存数据返回主键
查看>>
js循环生成多个easyui datagrid数据网格时,初始化表格
查看>>
Python编程笔记(第三篇)【补充】三元运算、文件处理、检测文件编码、递归、斐波那契数列、名称空间、作用域、生成器...
查看>>
获取用户信息
查看>>
洛谷P3952 时间复杂度
查看>>
Leetcode | Parentheses 相关
查看>>
Ajax分页问题
查看>>
如何禁止内部viewPager滑动
查看>>
简单的转义字符
查看>>
RabbitMQ入门-Topic模式
查看>>
poj 2777 Count Color(线段树区间更新)
查看>>
Java数据结构与算法(5) - ch05链表(LinkList)
查看>>
CLR Via CSharp读书笔记(21):自动内存管理(垃圾回收)
查看>>
刚刚接触python的感想
查看>>
modelsim使用常见问题及解决办法集锦 ②
查看>>
常用的第三方库
查看>>
java 操作elasticsearch之搭建测试项目环境
查看>>