Android Java调用ffmpeg命令
0. 前言
ffmpeg命令很强大,但是在Android工程中无法执行可执行文件ffmpeg,即无法使用ffmpeg。
本文介绍把ffmpeg改造成库文件,然后通过JNI调用它,即可实现在Java中使用ffmpeg命令。
PS:
本工程依赖于前文Android 编译FFmpeg x264。
1. ffmpeg
1.1 main to run
(1)ffmpeg.h
进入ffmpeg源代码,修改ffmpeg.h,在文件中添加一下代码:
#ifdef FFMPEG_RUN_LIB
int run(int argc, char** argv);
#endif
- 1
- 2
- 3
- 1
- 2
- 3
(2)ffmpeg.c
修改ffmpeg.c
把
int main(int argc, char** argv)
- 1
- 1
替换为
#ifdef FFMPEG_RUN_LIB
int run(int argc, char **argv)
#else
int main(int argc, char** argv)
#endif
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
1.2 ffmpeg cleanup
ffmpeg 在清理阶段虽然把各个变量释放掉了,但是并没有将其置为null,会出现问题。
修改ffmpeg_cleanup函数,具体修改方法就是当调用av_freep函数后,在把变量设置为NULL。部分代码如下:
2. 修复直接退出进程
如果只完成上面修改的话,在执行ffmpeg命令会直接结束,因为原始工程作为一个进程,运行结束会进行垃圾回收,以及结束进程。
解决方法:
找到cmdutils文件,用longjmp替换exit方法
具体操作如下:
(1)修改cmdutils.c
在文件include代码块底部添加一下代码:
#ifdef FFMPEG_RUN_LIB
#include <setjmp.h>
extern jmp_buf jmp_exit;
#endif
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
找到并修改exit_program函数:
void exit_program(int ret)
{if (program_exit)program_exit(ret);#ifdef FFMPEG_RUN_LIBav_log(NULL, AV_LOG_INFO, "exit_program code : %d\n", ret);longjmp(jmp_exit, ret);
#elseexit(ret);
#endif
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
(2)修改ffmpeg.c
把
exit()
- 1
- 1
函数替换为
exit_program()
- 1
- 1
(3) 添加ffmpeg_cmd文件
ffmpeg_cmd.h
//
// Created by Taylor Guo on 16/6/24.
//#ifndef FFMPEG_BUILD_LIB_FFMPEG_MAIN_H
#define FFMPEG_BUILD_LIB_FFMPEG_MAIN_Hint run_cmd(int argc, char** argv);#endif //FFMPEG_BUILD_LIB_FFMPEG_MAIN_H
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
ffmpeg_cmd.c
//
// Created by Taylor Guo on 16/6/24.
//#include <setjmp.h>#include "ffmpeg_cmd.h"
#include "ffmpeg.h"
#include "cmdutils.h"jmp_buf jmp_exit;int run_cmd(int argc, char** argv)
{int res = 0;if(res = setjmp(jmp_exit)){return res;}res = run(argc, argv);return res;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
3. JNI接口
3.1 JAVA
(1)加载库
(2)函数接口
public class FFmpegCmd {public static final String TAG = "FFmpegUtils";public static final int R_SUCCESS = 0;public static final int R_FAILED = -1;private static final String STR_DEBUG_PARAM = "-d";public static boolean mEnableDebug = false;static {System.loadLibrary("ffmpeg");System.loadLibrary("ffmpeg_cmd");}public native static int run(String[] cmd);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
3.2 Native
(1)ffmpeg_cmd_wrapper.h
//
// Created by Taylor Guo on 16/6/24.
//#ifndef FFMPEG_BUILD_LIB_FFMPEG_MAIN_H
#define FFMPEG_BUILD_LIB_FFMPEG_MAIN_H#include"jni.h"#ifdef __cplusplus
extern "C" {
#endif/** Class: org_wi_androidffmpeg_FFmpegCmd* Method: run* Signature: ([Ljava/lang/String;)I*/JNIEXPORT jint
JNICALL Java_org_wi_androidffmpeg_FFmpegCmd_run(JNIEnv *env, jclass obj, jobjectArray commands);#ifdef __cplusplus
}
#endif#endif //FFMPEG_BUILD_LIB_FFMPEG_MAIN_H
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
(2)ffmpeg_cmd_wrapper.c
//
// Created by Taylor Guo on 16/6/24.
//#include "ffmpeg_cmd.h"
#include "ffmpeg_cmd_wrapper.h"
#include "jni.h"#ifdef __cplusplus
extern "C" {
#endifJNIEXPORT jint
JNICALL Java_org_wi_androidffmpeg_FFmpegCmd_run(JNIEnv *env, jclass obj, jobjectArray commands)
{int argc = (*env)->GetArrayLength(env, commands);char *argv[argc];jstring jstr[argc];int i = 0;;for (i = 0; i < argc; i++){jstr[i] = (jstring)(*env)->GetObjectArrayElement(env, commands, i);argv[i] = (char *) (*env)->GetStringUTFChars(env, jstr[i], 0);//CGE_LOG_INFO("argv[%d] : %s", i, argv[i]);}int status = run_cmd(argc, argv);for (i = 0; i < argc; ++i){(*env)->ReleaseStringUTFChars(env, jstr[i], argv[i]);}return status;
}
#ifdef __cplusplus
}
#endif
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
5. 编译
修改Android.mk文件,在文件末尾添加:
###############################
#libffmpeg_main
###############################
include $(CLEAR_VARS)FFMPEG_ROOT=../ffmpeg
LOCAL_C_INCLUDES := $(FFMPEG_ROOT) \LOCAL_MODULE := ffmpeg_cmd
LOCAL_SRC_FILES := \ffmpeg_cmd.c \ffmpeg_cmd_wrapper.c \$(FFMPEG_ROOT)/cmdutils.c \$(FFMPEG_ROOT)/ffmpeg.c \$(FFMPEG_ROOT)/ffmpeg_opt.c \$(FFMPEG_ROOT)/ffmpeg_filter.cLOCAL_LDLIBS := -llog -lz -ldl
LOCAL_SHARED_LIBRARIES := libffmpegLOCAL_CFLAGS := -march=armv7-a -mfloat-abi=softfp -mfpu=neon -O3 -ffast-math -funroll-loops -DFFMPEG_RUN_LIB -DLOG_TAG=\"FFMPEG\"include $(BUILD_SHARED_LIBRARY)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
6. ffmpeg命令测试代码
(1)这里写一个简单的程序用于测试ffmpeg命令,具体功能是:
通过调用ffmpeg命令实现音视频混合,ffmpeg对应命令为:
ffmpeg -i test.mp4 -i test.mp3 -vcodec copy -acodec aac -map 0:v:0 -map 1:a:0 -shortest mix_test.mp4
- 1
- 1
程序如下:
/*** Muxing video stream and audio stream.* This interface is quite complex which is only for adding audio effect.** @param srcVideoName Input video file name.* @param fvVolume Input video volume, should not be negative, default is 1.0f.* @param srcAudioName Input audio file name.* @param faVolume Input audio volume, should not be negative, default is 1.0f.* @param desVideoName Output video file name.* @param callback Completion callback.** @return Negative : Failed* else : Success.*/public static int mixAV(final String srcVideoName, final float fvVolume, final String srcAudioName, final float faVolume,final String desVideoName, final OnCompletionListener callback) {if (srcAudioName == null || srcAudioName.length() <= 0|| srcVideoName == null || srcVideoName.length() <= 0|| desVideoName == null || desVideoName.length() <= 0) {return R_FAILED;}Runnable runnable = new Runnable() {@Overridepublic void run() {ArrayList<String> cmds = new ArrayList<String>();cmds.add("ffmpeg");cmds.add("-i");cmds.add(srcVideoName);cmds.add("-i");cmds.add(srcAudioName);//Copy Video Streamcmds.add("-c:v");cmds.add("copy");cmds.add("-map");cmds.add("0:v:0");//Deal With Audio Streamcmds.add("-strict");cmds.add("-2");if (fvVolume <= 0.001f) {//Replace audio streamcmds.add("-c:a");cmds.add("aac");cmds.add("-map");cmds.add("1:a:0");cmds.add("-shortest");if (faVolume < 0.99 || faVolume > 1.01) {cmds.add("-vol");cmds.add(String.valueOf((int) (faVolume * 100)));}} else if (fvVolume > 0.001f && faVolume > 0.001f){//Merge audio streamscmds.add("-filter_complex");cmds.add(String.format("[0:a]aformat=sample_fmts=fltp:sample_rates=48000:channel_layouts=stereo,volume=%f[a0]; " +"[1:a]aformat=sample_fmts=fltp:sample_rates=48000:channel_layouts=stereo,volume=%f[a1];" +"[a0][a1]amix=inputs=2:duration=first[aout]", fvVolume, faVolume));cmds.add("-map");cmds.add("[aout]");} else {Log.w(TAG, String.format(Locale.getDefault(), "Illigal volume : SrcVideo = %.2f, SrcAudio = %.2f",fvVolume, faVolume));if (callback != null) {callback.onCompletion(R_FAILED);}return;}cmds.add("-f");cmds.add("mp4");cmds.add("-y");cmds.add("-movflags");cmds.add("faststart");cmds.add(desVideoName);if (mEnableDebug) {cmds.add(STR_DEBUG_PARAM);}String[] commands = cmds.toArray(new String[cmds.size()]);int result = FFmpegCmd.run(commands);if (callback != null) {callback.onCompletion(result);}}};new Thread(runnable).start();return R_SUCCESS;}public interface OnCompletionListener {void onCompletion(int result);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
(2)Activity:
package org.wi.androidffmpeg;import android.os.Bundle;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;import java.io.File;public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}public void run(View view) {Log.d("MainActivity", "MIX AV...");FFmpegCmd.setEnableDebug(true);String folder = Environment.getExternalStorageDirectory().getPath();if (folder == null || folder.length() <=0) {return;}folder += "/libCGE";FFmpegCmd.mixAV(folder + "/MediaResource/test.mp4", 1.0f,folder + "/MediaResource/test.mp3",0.7f, folder + "/new_mix.mp4", new FFmpegCmd.OnCompletionListener() {@Overridepublic void onCompletion(int result) {Log.d("MainActivity", "MIX AV Finish : " + result);}});}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
(3)运行结果:
7. 项目源码
AndroidFFmpeg
Android Java调用ffmpeg命令相关推荐
- java命令截图,GitHub - xuege-cn/fmj: FMJ (FFMpeg for Java)。通过Java调用FFMpeg命令的方式来对音视频进行处理(获取信息、截图等等)。...
项目介绍 FMJ (FFMpeg for Java) 通过Java调用FFMpeg命令的方式来对音视频进行处理(获取信息.截图等等). FFMpeg安装 建议使用 ffmpeg-2.6.1 版本 Li ...
- 使用java调用ffmpeg命令将视频分屏并音视频同时播放(二分屏)
实现想法 使用ffmpeg命令中的filter_complex(滤镜)参数来进行展示 引入javacv版本 <dependency><groupId>org.bytedeco& ...
- 在Android项目中调用FFmpeg命令
概述 FFmpeg,命令行非常强大.在PC机上,调用一行FFmpeg命令,就可以对视频文件进行剪切.合并.转码等功能.本文将介绍如何在Android中调用FFmpeg命令行. 编译 前面已经有两篇文章 ...
- 流媒体方案,技术开源项目,包含Java调用FFMpeg(FFCH4J),推拉流服务器,nginx-http-flv-module,ckplayer,Flv.js,EasyPlayer.js集成
文章目录 streaming_media 介绍 ckplayer拉流播放 软件介绍 功能介绍 软件架构 安装教程 使用说明 EasyPlayer.js拉流播放 简介 功能说明 集成使用示例 普通集成 ...
- java调用FFmpeg实现视屏压缩
前言 最近做人脸识别,对于过大的视屏(10M)以上,识别失败的几率比较大.找了下相关的视屏压缩资料.首先使用的是jave进行视屏压缩,但是踩坑失败,jave中有转码的过程,虽然压缩成功,各种压缩参数也 ...
- java rtmp_搭建rtmp直播流服务之2:使用java实现ffmpeg命令接口化调用(用java执行ffmpeg命令)...
一.环境搭建 1.安装ffmpeg 下载对应系统的ffmpeg安装包,个人采用windows平台进行开发,所以安装了windows版本(各平台ffmpeg命令都是一样的,无须纠结) 2.ffmpeg的 ...
- android java调用_关于Android中Java调用外部命令的三种方式
此所谓三种方式,只是个人认为.本人还是菜鸟初涉,所以有所错误,请指正. 个人认为,Java调用外部命令.无非三种情况: 一.是只执行命令,不考虑返回值. 二.是执行命令的同时,还需要得到返回值. 三. ...
- Android录屏并利用FFmpeg转换成gif(三) 在Android中使用ffmpeg命令
Android录屏并利用FFmpeg转换成gif(三) 写博客时经常会希望用一段动画来演示app的行为,目前大多数的做法是在电脑上开模拟器,然后用gif录制软件录制模拟器屏幕,对于非开发人员来讲这种方 ...
- mysql+视频文件转成流_详解java调用ffmpeg转换视频格式为flv
详解java调用ffmpeg转换视频格式为flv 注意:下面的程序是在Linux下运行的,如果在windows下rmvb转换成avi会出现问题,想成功需要下载下个drv43260.dll东西放到C:W ...
最新文章
- SwfUpload及imgareaselect使用方法
- Dancing Stars on Me HDU - 5533
- python编程制作接金币游戏_pygame学习笔记(6):完成一个简单的游戏
- sharedPreferences的用法
- 如何在三个月内获得三年的工作经验
- WDS部署服务所用的PXE引导文件
- 来自于51CTO的经典学习资料汇总
- centos7.x表安装iptables防火墙
- MS17010漏洞利用总结
- js下载视频(mp4格式)
- 破解有道翻译反爬虫机制
- intellij idea 插件 开发 新加的mainmenu不显示
- 搜狗二季度财报解读:有扎实的现在,也有性感的未来
- 【Excel】Excel读取数据时,提示“服务器连接异常”
- 微信游戏奇迹暖暖选取服务器失败,奇迹暖暖微信怎么登录失败
- X280 工业4g路由器
- 计算机视觉(视频追踪检测分类、监控追踪)常用测试数据集
- win7 如何锁定计算机,Win7系统如何锁定计算机
- 全息投影马戏团,好有爱的科技呀~
- 5、TWS API的故障排除和支持