在Android中使用FFmpeg(android studio环境)
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环境)的更多相关文章
- Android中集成ffmpeg(一):编译ffmpeg
方案选择 Android中集成ffmpeg的codec功能无非两种方式: JNI直接调用,主要用于App开发(无权限修改系统底层),如EXOPlayer,JPlayer等. 集成ffmpeg到OMX, ...
- Matrix: android 中的Matrix (android.graphics.Matrix) (转)
本篇博客主要讲解一下如何处理对一个Bitmap对象进行处理,包括:缩放.旋转.位移.倾斜等.在最后将以一个简单的Demo来演示图片特效的变换. 1. Matrix概述 对于一个图片变换的处理,需要Ma ...
- Android中开发工具Android Studio修改created用户(windows环境)
最近经常有朋友反馈说我的安卓项目中,在一些类中会出现Created by panchengjia on 2016/12/30的字样,是如何自动实现的(默认一般为Administrator),如下图: ...
- Android中Activity的android:windowSoftInputMode属性
转载 https://blog.csdn.net/qiutiandepaomo/article/details/84028558 windowSoftInputMode属性主要是用来设置窗口软键盘的交 ...
- Android中矢量动画
Android中矢量动画 Android中用<path> 标签来创建SVG,就好比控制着一支画笔,从一点到一点,动一条线. <path> 标签 支持一下属性 M = (Mx, ...
- Android中的HTTP通信
前言:近期在慕课网学习了慕课网课程Android中的HTTP通信,就自己总结了一下,其中参考了不少博文,感谢大家的分享. 文章内容包括:1.HTTP简介2.HTTP/1.0和HTTP/1.1之间的区别 ...
- Android 中 Handler 引起的内存泄露
在Android常用编程中,Handler在进行异步操作并处理返回结果时经常被使用.其实这可能导致内存泄露,代码中哪里可能导致内存泄露,又是如何导致内存泄露的呢?那我们就慢慢分析一下.http://w ...
- android中数据存储
android中数据存储 Android 中存储数据的方式有五种:SQLite数据库.文件存储.内容提供者.网络.SharedPreferences(Key----value)五种存储方式. ...
- Android中的Audio播放:控制Audio输出通道切换
Audio 输出通道有很多,Speaker.headset.bluetooth A2DP等.通话或播放音乐等使用Audio输出过程中,可能发生Audio输出通道的切换.比如,插入有线耳机播放音乐时,声 ...
随机推荐
- 潭州课堂25班:Ph201805201 django框架 第三课 模板路径,变量,过滤器,静态文件的引用 (课堂笔记)
第二种方法 首先 'APP_DIRS': True, 将 app 的名字在 INSTALLED_APPS 进行注册 模板变量 传的各种数据类型,和取值 过渡器: 静态文件: 在项目文件目录 中创建 ...
- [PA2015]Rozstaw szyn
[PA2015]Rozstaw szyn 题目大意: 一棵\(n(n\le5\times10^5)\)个点的树,其中有\(m\)个结点是叶子结点.叶子结点权值已知,你可以自己决定其余结点的权值,定义整 ...
- python系统编程(三)
multiprocessing 如果你打算编写多进程的服务程序,Unix/Linux无疑是正确的选择.由于Windows没有fork调用,难道在Windows上无法用Python编写多进程的程序? 由 ...
- C++学习笔记55:流类库与输入输出
I/O流的概念及流类库的结构 程序与外界环境的信息交换 当程序与外界环境进行信息交换时,存在着两个对象:程序中的对象,文件对象. 流是一种抽象,负责在数据的生产者和数据的消费者之间建立联系,并管理数据 ...
- 自己遇到过的出现java.lang.StackOverflowError的原因
public static JSONArray geth24Weather(String result) {//获取当天24小时以及第二天的天气结果对象 JSONObject fromO ...
- TypeScript语法学习--基本类型
查看官方文档手册:链接:https://www.tslang.cn/docs/home.html (一)Boolean 最基本的数据类型就是简单的true/false值 The most basic ...
- unity 调整摄像机视角完整脚本
脚本作为组件挂在摄像机上即可,调用接口开关IsControlMove,控制是否启用: using System.Collections; using System.Collections.Generi ...
- /etc/security/limits.conf 文件说明
/etc/security/limits.conf 是 Linux 资源使用配置文件,用来限制用户对系统资源的使用 语法:<domain> <type> <item& ...
- 论文泛读 A Novel Ensemble Learning-based Approach for Click Fraud Detection in Mobile Advertising [1/10]
title:新的基于集成学习的移动广告作弊检测 导语:基于buzzcity数据集,我们提出了对点击欺诈检测是基于一组来自现有属性的新功能的一种新方法.根据所得到的精度.召回率和AUC对所提出的模型进行 ...
- 团队——League of Programers简介
团队名称 League of Programers 团队成员简介 武璧泽:编程能力较强,善于程序思路设计: 邹兰兰:擅长代码的分析.编写与调试: 倪彤炜:擅长解决代码的修正与编写,善于调节团队关系: ...