编译可执行命令的FFmpeg

上一篇讲到了使用FFmpeg生成视频封面图,其实也可以直接使用FFmpeg相关命令截取一帧的图像数据保存到本地,然后加载到ImageView上,有时候使用命令确实比写代码更加简单和使人轻松一点,所以这一篇是讲解如何导入FFmpeg相关源码 然后如何执行命令行工具的博客,但是其实这只是个Demo而已,因为有很多细节需要处理,推荐直接使用开源库。

导入源码

从FFmpeg源码中导入cmdutils.ccmdutils.hconfig.hffmpeg.cffmpeg.h

ffmpeg_filter.cffmpeg_hw.cffmpeg_opt.c这几个源码,一般存放在fftools目录下。config.h如果编译生成目录下没有,就可以直接使用ffmpeg根目录下的config.h

编写CmakeList

# 设置构建本机库文件所需的 CMake的最小版本
cmake_minimum_required(VERSION 3.4.1)#添加头文件的搜索路径
include_directories(src/main/cpp/include)#设置查找动态库位置
set(LINK_DIR ${CMAKE_SOURCE_DIR}/libs/${CMAKE_ANDROID_ARCH_ABI})
link_directories(${LINK_DIR})
#找到所有的so库,存放在全局变量SO_DIR中
file(GLOB SO_DIR ${LINK_DIR}/*.so)#找到所有的源文件,存放在全局变量中
#file(GLOB FFMPEG_DIR src/main/cpp/ffmpeg/*.c)
#message("FFMPEG_DIR == ${FFMPEG_DIR}")file(GLOB CPP_DIR src/main/cpp/*.cpp)
file(GLOB FFMPEG_DIR src/main/cpp/include/*.c)# 添加自己写的 C/C++源文件
add_library(utils #so名称SHARED #动态库${CPP_DIR}${FFMPEG_DIR})#  依赖 NDK中自带的log库
find_library(log-lib log)#  链接库
target_link_libraries(utils${SO_DIR}jnigraphics${log-lib})

我是将ffmpeg的源码和之前生成的ffmpeg头文件都放在了cpp/include目录下,这样在CmakeList中使用include_directories就可以直接找到所有的头文件,然后将ffmpeg的源码和自己写的工具类源码关联起来就行了。

修改FFmpeg的源码

修改ffmpeg.cmain方法名称为exe_cmd,并在ffmpeg.h头文件加上同样名称的方法声明。

//ffmpeg.c
int exe_cmd(int argc, char **argv) {...
}
//ffmpeg.h
int exe_cmd(int argc, char **argv);

原生命令行工具在执行完FFmpeg命令后都会退出程序,但是在Android里面可不能这样,所以我们要修改FFmpeg结束程序的函数。

修改cmdutils.ccmdutils.h,注释掉退出程序的代码,并且增加一个int的返回值。

//cmdutils.c
int exit_program(int ret)
{
//    if (program_exit)
//        program_exit(ret);//    exit(ret);return ret;
}//cmdutils.h
int exit_program(int ret);

并且在Android里面我们肯定是执行完一条命令,接着还会继续执行其他命令,所以我们需要重新初始化一些关键变量的值。

找到ffmpeg.c中的ffmpeg_cleanup函数,在末尾将一些关键变量重新初始化。

//ffmpeg.c
static void ffmpeg_cleanup(int ret) {...nb_filtergraphs = 0;nb_output_files = 0;nb_output_streams = 0;nb_input_files = 0;nb_input_streams = 0;
}

最后在main函数末尾调用ffmpeg_cleanup函数

int exe_cmd(int argc, char **argv) {...//    exit_program(received_nb_signals ? 255 : main_return_code);ffmpeg_cleanup(0);
}

增加FFmpeg日志输出

ffmpeg.c中找到log_callback_null的函数,添加如下代码,原代码块是空实现。

#include "android/log.h"
#define logDebug(...) __android_log_print(ANDROID_LOG_DEBUG,"MainActivity",__VA_ARGS__)static void log_callback_null(void *ptr, int level, const char *fmt, va_list vl) {static int print_prefix = 1;static int count;static char prev[1024];char line[1024];static int is_atty;av_log_format_line(ptr, level, fmt, vl, line, sizeof(line), &print_prefix);strcpy(prev, line);logDebug("ffmpeg log ----- %s", line);
}

main函数中调用log_callback_null函数

int exe_cmd(int argc, char **argv) {av_log_set_callback(log_callback_null);int i, ret;...
}

编写工具类方法

MainActivity中增加exeCmd(String[] cmd)方法

public static native int exeCmd(String[] cmd);

ffmpeg_utils.cpp增加jni方法

JNIEXPORT jint JNICALL
Java_demo_simple_example_1ffmpeg_MainActivity_exeCmd(JNIEnv *env, jclass clazz, jobjectArray cmd) {int argc = env->GetArrayLength(cmd);logDebug("argc == %d", argc);char *argv[argc];for (int i = 0; i < argc; ++i) {jstring str = (jstring) env->GetObjectArrayElement(cmd, i);argv[i] = (char *) env->GetStringUTFChars(str, JNI_FALSE);logDebug("%s ", argv[i]);}return exe_cmd(argc, argv);
//    return 1;
}

执行命令

private void exeCmd() {String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator+ "get_cover1.mp4";String outPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator+ "video.flv";File outFile = new File(outPath);if (outFile.exists()) {outFile.delete();}//裁剪个1s视频String cmd = "ffmpeg -ss 00:00:00 -t 00:00:10 -i " + path + " -vcodec copy -acodec copy " + outPath;String[] cmdArr = cmd.split(" ");int result = exeCmd(cmdArr);Log.d(TAG, "exe cmd result == " + result);}

查看日志输出

demo.simple.example_ffmpeg D/MainActivity: ffmpeg log ----- 
demo.simple.example_ffmpeg D/MainActivity: ffmpeg log -----   Total: 331 packets (1993540 bytes) demuxed
demo.simple.example_ffmpeg D/MainActivity: ffmpeg log ----- Output file #0 (/storage/emulated/0/video.flv):
demo.simple.example_ffmpeg D/MainActivity: ffmpeg log -----   Output stream #0:0 (video): 
demo.simple.example_ffmpeg D/MainActivity: ffmpeg log ----- 129 packets muxed (1822580 bytes); 
demo.simple.example_ffmpeg D/MainActivity: ffmpeg log ----- 
demo.simple.example_ffmpeg D/MainActivity: ffmpeg log -----   Output stream #0:1 (audio): 
demo.simple.example_ffmpeg D/MainActivity: ffmpeg log ----- 202 packets muxed (170960 bytes); 
demo.simple.example_ffmpeg D/MainActivity: ffmpeg log ----- 
demo.simple.example_ffmpeg D/MainActivity: ffmpeg log -----   Total: 331 packets (1993540 bytes) muxed
demo.simple.example_ffmpeg D/MainActivity: ffmpeg log ----- 0 frames successfully decoded, 0 decoding errors
demo.simple.example_ffmpeg D/MainActivity: ffmpeg log ----- [AVIOContext @ 0xda2ea480] Statistics: 2 seeks, 10 writeouts
demo.simple.example_ffmpeg D/MainActivity: ffmpeg log ----- [AVIOContext @ 0xda2ea3c0] Statistics: 2161643 bytes read, 1 seeks
demo.simple.example_ffmpeg D/MainActivity: ffmpeg log ----- 
demo.simple.example_ffmpeg D/MainActivity: exe cmd result == 0

执行命令的返回值==0,并且也看到确实文件已经生成出来了,我们adb pull把文件导出到桌面用ffprobeffplay看看。

ffprobe video.flv
ffprobe version 4.0.4 Copyright (c) 2007-2019 the FFmpeg developers
built with Apple LLVM version 10.0.0 (clang-1000.10.44.4)
configuration:
libavutil      56. 14.100 / 56. 14.100
libavcodec     58. 18.100 / 58. 18.100
libavformat    58. 12.100 / 58. 12.100
libavdevice    58.  3.100 / 58.  3.100
libavfilter     7. 16.100 /  7. 16.100
libswscale      5.  1.100 /  5.  1.100
libswresample   3.  1.100 /  3.  1.100
Input #0, flv, from 'video.flv':
Metadata:major_brand     : mp42minor_version   : 0compatible_brands: mp42mp41encoder         : Lavf58.45.100
Duration: 00:00:01.07, start: 0.033000, bitrate: 3130 kb/sStream #0:0: Video: h264 (Main), yuv420p(tv, bt709, progressive), 1080x1920, 3390 kb/s, 30 fps, 30 tbr, 1k tbn, 60 tbcStream #0:1: Audio: aac (LC), 48000 Hz, stereo, fltp, 317 kb/s

可以看到确实裁剪生成了一个1秒的视频,虽然后缀名我们用的.flv,但是其实我们是拷贝的视频编码,所以还是mp4的封装格式。

源码

使用已有的轮子

上面的例子并不是一个完善的工具类,比如缺少Native层的线程支持,出现错误就会直接闪退,缺少进度回调等,所以还是直接使用现成的轮子比较靠谱,只是我们需要知道轮子大概是怎么造出来的就行了。

这里我推荐使用mobile-ffmpeg这个开源库,1.8k的star足以证明其品质还行,直接导入编译好的aar就可以执行命令行工具链,而且可以自行编译链接很多有用的第三方library,比如x264libwebp 等。

动手能力强或有特殊需求的可以使用android.sh自行编译出FFmpeg头文件和动态库,以及Android工具链的aar。

比如说我现在只需要一个支持arm64-v8aapi16及以上的动态库,那么我就自己新建了一个shell脚本文件:

#!/bin/bashexport ANDROID_HOME="/Users/chenpeng/Library/Android/sdk/"
export ANDROID_NDK_ROOT="/Users/chenpeng/Desktop/work_space/ndk/android-ndk-r21b/"build() {./android.sh \--lts \--disable-arm-v7a \--disable-arm-v7a-neon \--disable-x86 \--disable-x86-64
}build

执行完这个shell后,就会在prebuilt目录下生产对应的头文件,动态库,以及aar文件,直接拿来用就可以了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/52033.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT3.5/4.0新手使用手册,国内中文版使用教程

引言 欢迎使用ChatGPT!无论你是刚开始接触AI聊天机器人,还是已经有了一些使用经验,这篇新手使用手册将帮助你快速上手,并且从ChatGPT中获得最优的体验。本文主要聚焦于提示词(Prompt)的使用教学&#xff0…

Spring 源码解读:实现Spring容器的初始化与刷新机制

引言 在Spring框架中,容器的初始化和刷新机制是其核心工作流程的重要部分,它负责加载Bean定义、创建Bean实例、进行依赖注入并管理整个Bean的生命周期。在实际开发中,ApplicationContext提供的refresh()方法扮演着关键角色,它帮助…

C++系列-STL容器之list

STL容器之list list容器的基本结构list容器的特点list容器的优点list容器的缺点 list容器的构造函数list容器的常用接口list赋值操作list大小及空否list访问list迭代器相关list增删查改push and popinsert其它 寄扬州韩绰判官 杜牧〔唐代〕 青山隐隐水迢迢,秋尽江南…

vant 动态查询下拉菜单(可用)

动态查询item项 <van-form submit"onSubmit" ref"formRef"><Title title"企业信息" title-line title-size"19" class"ml-[18px] mb-[18px]"></Title><van-cell-group inset class"py-[18px]&quo…

Python实战项目:天气数据爬取+数据可视化(完整代码)_python爬虫实战

一、选题的背景 随着人们对天气的关注逐渐增加&#xff0c;天气预报数据的获取与可视化成为了当今的热门话题&#xff0c;天气预报我们每天都会关注&#xff0c;天气情况会影响到我们日常的增减衣物、出行安排等。每天的气温、相对湿度、降水量以及风向风速是关注的焦点。通过…

Linux——网络(5)

一、sqlite3性能测试 1. 程序效率测试 时间相关接口&#xff1a; int gettimeofday(struct timeval*tv, struct timezone *tz); 功能&#xff1a;得到从1970年1月1日0时0分0秒到现在的秒数 精度到微妙 参数&#xff1a; tv&#xff1a;…

什么是视频缓存服务器,它有哪些作用?

视频缓存服务器通常拥有大容量的存储空间和高速的读写能力&#xff0c;它通过缓存(即临时存储)用户经常访问的视频内容&#xff0c;来优化内容的分发过程。这种服务器通常部署在网络中的关键位置&#xff0c;如靠近用户接入点的位置&#xff0c;以降低用户访问视频内容时的网络…

维信小程序禁止截屏/录屏

一、维信小程序禁止截屏/录屏 //录屏截屏,禁用wx.setVisualEffectOnCapture({visualEffect:hidden});wx.setVisualEffectOnCapture(Object object) 测试安卓手机&#xff1a; 用户截屏&#xff0c;被禁用 用户录屏&#xff0c;录制的是空白内容/黑色内容的视频。 二、微信小…

鸿蒙系统为什么能安装安卓的APP

鸿蒙系统能够安装安卓的APP&#xff0c;主要得益于其设计理念和技术实现上的几个关键点&#xff1a; 一、设计理念 鸿蒙系统的设计初衷并非完全取代安卓系统&#xff0c;而是与其共存&#xff0c;并建立一个更加广泛的软件生态圈。这一理念体现在鸿蒙系统对安卓应用的兼容性上…

浙大数据结构:01-复杂度2 Maximum Subsequence Sum

数据结构MOOC PTA习题 01-复杂度2 Maximum Subsequence Sum #include <iostream> using namespace std; const int M 100005; int a[M]; int main() {int k;cin >> k;int f 1;for (int i 0; i < k; i){cin >> a[i];if (a[i] > 0)//如果出现大于0则…

【HuggingFace Transformers】OpenAIGPTModel源码解析

OpenAIGPTModel源码解析 1. GPT 介绍2. OpenAIGPTModel类 源码解析 说到ChatGPT&#xff0c;大家可能都使用过吧。2022年&#xff0c;ChatGPT的推出引发了广泛的关注和讨论。这款对话生成模型不仅具备了强大的语言理解和生成能力&#xff0c;还能进行非常自然的对话&#xff0c…

1.初识ChatGPT:AI聊天机器人的革命(1/10)

引言 在当今的数字化世界中&#xff0c;人工智能&#xff08;AI&#xff09;正以其独特的方式重塑我们的生活和工作。其中&#xff0c;AI聊天机器人作为人机交互的前沿技术&#xff0c;已经成为企业与客户沟通、提供个性化服务的重要工具。这些机器人通过模拟人类的对话方式&a…

二、MMRotate旋转框目标检测训练DOTA数据集(模型推理与部署,保存推理结果为xml文件并构建镜像)

在上一篇文章中介绍了MMRotate的概述、安装和训练Dota数据集全流程,由于文章篇幅限制还剩下一部分模型的推理和部署环节没有写,为避免后续对这部分工作的遗忘,决定还是补充上这部分的笔记,仅作记录,如有不足之处还请指出! 一、模型推理测试 1.1单张图片推理测试 可以首…

DataSet和DataTable的关系

C#中的DataTable 在C#中&#xff0c;DataTable 是 System.Data 命名空间下的一个类&#xff0c;它是 DataSet 的一个组件&#xff0c;用于存储表格形式的数据。DataTable 可以独立于数据库使用&#xff0c;也可以与数据库表相关联&#xff0c;用于数据的读取、更新、插入和删除…

LeetCode 热题100-17 缺失的第一个正数

缺失的第一个正数 给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3 解释&#xff1a;范围 [1,…

OpenCV绘图函数(13)绘制多边形函数函数polylines()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 画几条多边形曲线 函数原型 void cv::polylines (InputOutputArray img,InputArrayOfArrays pts,bool isClosed,const Scalar & color…

【高等代数笔记】(18)N阶行列式

2. N阶行列式 2.12 行列式按k行&#xff08;列&#xff09;展开 【拉普拉斯定理】 n n n阶矩阵 A ( a i j ) \boldsymbol{A}(a_{ij}) A(aij​)&#xff0c;取定第 i 1 , i 2 , . . . , i k i_{1},i_{2},...,i_{k} i1​,i2​,...,ik​行&#xff08;其中 i 1 < i 2 < .…

将x减到零的最小操作数问题

欢迎跳转我的主页&#xff1a;羑悻的小杀马特-CSDN博客 目录 一题目简述&#xff1a; 二题目思路&#xff1a; 三解答代码&#xff1a; 一题目简述&#xff1a; leetcode题目链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 二题目思路&#xff1a; 首先这道题…

STM32(F103ZET6)第十九课:FreeRtos的移植和使用

目录 需求一、FreeRtos简介二、移植FreeRtos1.复制代码2.内存空间分配和内核相关接口3.FreeRtosConfig4.添加到工程中三、任务块操作1.任务四种状态2.创建任务过程 需求 1.将FreeRtos&#xff08;嵌入式实时操作系统&#xff09;移植到STM32中。 2.在该系统中实现任务的创建、…

git 更改分支名称

1. 本地分支重命名&#xff08;还未推送到远程&#xff09; 1、修改当前分支名称 git branch -m 新分支名称2、修改其他分支名称 git branch -m 旧分支名称 新分支名称2. 远程分支重命名&#xff08;已推送远程&#xff09; 1&#xff09;重新命名远程分支对应的本地分支 …