1.首先我们需要一个已经编译好的libffmpeg.so文件。(怎么编译是个大坑,可以参考windows环境下编译android中使用的FFmpeg,也可以用网上下载的现成的,本文相关的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<String[],Integer,Integer>(){
@Override
protected void onPreExecute() {
if(kitIntenrface != null){
kitIntenrface.onStart();
}
}
@Override
protected Integer doInBackground(String[]... params) {
return run(params[]);
}
@Override
protected void onProgressUpdate(Integer... values) {
if(kitIntenrface != null){
kitIntenrface.onProgress(values[]);
}
}
@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 <stdio.h>
#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 = ; i < argc; i++) {
jstring js = (jstring) (*env)->GetObjectArrayElement(env, commands, i);
argv[i] = (char*) (*env)->GetStringUTFChars(env, js, );
LOGD("Kit argv %s\n", argv[i]);
}
return run(argc, argv);
}

9.编辑Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeg
LOCAL_SRC_FILES := libffmpeg.so
include $(PREBUILT_SHARED_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := ffmpeginvoke
LOCAL_SRC_FILES := codepig_ffmpegcldemo_FFmpegKit.c ffmpeg.c ffmpeg_opt.c cmdutils.c ffmpeg_filter.c
LOCAL_C_INCLUDES := F:/demo/ffmpeg-3.0
LOCAL_LDLIBS := -llog -lz -ldl
LOCAL_SHARED_LIBRARIES := ffmpeg
include $(BUILD_SHARED_LIBRARY)

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

10.编辑Application.mk 文件

APP_ABI := armeabi-v7a
APP_PLATFORM := android-

其中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[];
commands[] = "ffmpeg";
commands[] = "-i";
commands[] = videoUrl;
commands[] = "-i";
commands[] = imageUrl;
commands[] = "-filter_complex";
commands[] = "overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2";
commands[] = "-codec:a";
commands[] = "copy";
commands[] = 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[];
commands[] = "ffmpeg";
commands[] = "-i";
commands[] = videoUrl;
commands[] = "-i";
commands[] = musicUrl;
commands[] = 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[];
commands[] = "ffmpeg";
commands[] = "-i";
commands[] = videoUrl;
commands[] = "-i";
commands[] = imageUrl;
commands[] = "-filter_complex";
commands[] = "overlay=(main_w-overlay_w)/2:(main_h-overlay_h)/2";
commands[] = "-i";
commands[] = musicUrl;
commands[] = "-y";
commands[] = 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;

头尾不变,中间的参数顺序无所谓。但是一个操作的参数必须写一起。
参数是以 -参数名+参数值 这样的成对形式赋值。


参考:
ffmpeg参数解释
FFmpeg常用命令大全,并简单封装
Android最简单的基于FFmpeg的例子(四)---以命令行的形式来使用ffmpeg
本文github项目:
ffmpegCLDemo

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

在Android中使用FFmpeg(android studio环境)的更多相关文章

  1. Android中集成ffmpeg(一):编译ffmpeg

    方案选择 Android中集成ffmpeg的codec功能无非两种方式: JNI直接调用,主要用于App开发(无权限修改系统底层),如EXOPlayer,JPlayer等. 集成ffmpeg到OMX, ...

  2. Matrix: android 中的Matrix (android.graphics.Matrix) (转)

    本篇博客主要讲解一下如何处理对一个Bitmap对象进行处理,包括:缩放.旋转.位移.倾斜等.在最后将以一个简单的Demo来演示图片特效的变换. 1. Matrix概述 对于一个图片变换的处理,需要Ma ...

  3. Android中开发工具Android Studio修改created用户(windows环境)

    最近经常有朋友反馈说我的安卓项目中,在一些类中会出现Created by panchengjia on 2016/12/30的字样,是如何自动实现的(默认一般为Administrator),如下图: ...

  4. Android中Activity的android:windowSoftInputMode属性

    转载 https://blog.csdn.net/qiutiandepaomo/article/details/84028558 windowSoftInputMode属性主要是用来设置窗口软键盘的交 ...

  5. Android中矢量动画

    Android中矢量动画 Android中用<path> 标签来创建SVG,就好比控制着一支画笔,从一点到一点,动一条线. <path> 标签 支持一下属性 M = (Mx, ...

  6. Android中的HTTP通信

    前言:近期在慕课网学习了慕课网课程Android中的HTTP通信,就自己总结了一下,其中参考了不少博文,感谢大家的分享. 文章内容包括:1.HTTP简介2.HTTP/1.0和HTTP/1.1之间的区别 ...

  7. Android 中 Handler 引起的内存泄露

    在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用.其实这可能导致内存泄露,代码中哪里可能导致内存泄露,又是如何导致内存泄露的呢?那我们就慢慢分析一下.http://w ...

  8. android中数据存储

    android中数据存储     Android 中存储数据的方式有五种:SQLite数据库.文件存储.内容提供者.网络.SharedPreferences(Key----value)五种存储方式. ...

  9. Android中的Audio播放:控制Audio输出通道切换

    Audio 输出通道有很多,Speaker.headset.bluetooth A2DP等.通话或播放音乐等使用Audio输出过程中,可能发生Audio输出通道的切换.比如,插入有线耳机播放音乐时,声 ...

随机推荐

  1. python基础一 ------如何获取多个字典相同的键

    需求: 足球赛第一场进去统计  {"A":3,"B":2,"C":1}足球赛第二场进去统计  {"A":3," ...

  2. SS+FinalSpeed终极教程[转]

    亲测,晚高峰看YOUTUBE的720P完全不卡. 推荐使用virwire buget VPS,性价比较高,128M内存,每月500G超大流量:http://www.cnblogs.com/tuzhuk ...

  3. Charles通用破解激活办法

    Charles通用破解激活办法 适用于Charles任意版本的注册码,谁还会想要使用破解版呢. 目前最新版Charles 4.2.8. Registered Name: https://zhile.i ...

  4. 通过Redis、Memcache的 incr 原子操作防刷机制的使用差别

    我的版本如下: Redis:3.2.100 Memcache:1.4.4 最近在处理服务器压力测试的时候,想到一个方案,在一定时间段内限制用户访问次数.具体的实现就是通过redis的院子操作incre ...

  5. (转自知乎)Unicode编码

    很多人都把Unicode编码挂在嘴边,其实咱们现实生活中遇到的编码基本都是Unicode的 因为Unicode兼容了大多数老版本的编码规范例如 ASCII Unicode编码定义了这个世界上几乎所有字 ...

  6. WC、KOI2017小随想..

    自从WC之后就一直想说些什么.. 嗯比较零碎.. Part A WC,我通过我的双眼看到了我想要的 嗯,就是在上面道出自己的想法.自己的思想 也是自己在十二月的迷茫后看到的曙光吧.. 感动.激动.难以 ...

  7. Java 接口 Closeable

    该接口位于java.io包下,声明如下:public interface Closeable extends AutoCloseable.关闭流并释放与该流关联的所有系统资源.如果已经关闭该流,则调用 ...

  8. 多重背包--java

    多重背包 有N种物品和一个容量为V的背包.第i种物品最多有n[i]件可用,每件费用是c[i],价值 是w[i].求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大母函数的思想也 ...

  9. pygame-KidsCanCode系列jumpy-part16-enemy敌人

    接上回继续,这次我们要给游戏加点难度,增加几个随机出现的敌人,玩家碰到敌人后Game Over. 最终效果如下,头上顶个"电风扇"的家伙,就是敌人. 一.先定义敌人类 # 敌人类 ...

  10. Win 10 System Restore Fail 0x80070091

    Question: Below about says it all.  I tried SysRes from two points, both with same failure. System R ...