安卓部署ffmpeg全平台so并实现命令行调用
安卓 FFmpeg系列
第一章 Ubuntu生成ffmpeg安卓全平台so
 第二章 Windows生成ffmpeg安卓全平台so
 第三章 生成支持x264的ffmpeg安卓全平台so
 第四章 部署ffmpeg安卓全平台so并使用(本章)
文章目录
- 安卓 FFmpeg系列
 - 前言
 - 一、添加so
 - 1、拷贝ffmpeg到项目
 - 2、build.gradle指定so目录
 
- 二、调用命令行
 - 1、新建CMakeLists链接ffmpeg的so
 - 2、封装命令行方法
 - (1)、导入main符号
 - (2)、将字符串解析为argc、argv
 - (3)、注册log回调输出安卓日志
 - (4)、阻止exit退出
 - 完整代码
 
- 3、dart ffi调用
 - 4、java jni调用
 
- 三、完整代码
 - 四、使用示例
 - 1、flutter调用命令行rtsp拉流
 
- 总结
 - 附录
 - 1、dart将字符串解析为argc、argv
 
前言
前面的章节实现了ffmpeg全平台so的生成,接下来的步骤就是部署以及使用了,部署so还是比较简单的,用gradle和cmake都可以部署,部署好了就可以直接使用了,如果需要c++进行封装一层,则需要链接,对cmake熟悉的话链接也是比较简单的。
一、添加so
1、拷贝ffmpeg到项目
ffmpeg的生成方法可以参考前面三章,或者使用第二章 生成好的包。将ffmpeg生成好的包拷贝到如下目录。
 
并将so放到对应abi的目录中。
 
2、build.gradle指定so目录
指定的目录为abi名称的上一级。
 
sourceSets {main {jniLibs.srcDirs = ['src/main/cpp/jniLibs/ffmpeg4.3.6/24']       }}
 
如果是jni或者flutter的ffi直接调用ffmpeg的符号,则到这一步就结束了。
 通过jni或ffi直接调用命令行:可以使用第三章 生成的包,里面有个libffmpeg.so,包含了ffmpeg的main符号,可以通过ffi直接调用。但需要解决2个问题:1、字符串解析为argc、argv。(dart.可参考附录)2、中断ffmpeg的exit操作。
 否则继续往下
二、调用命令行
此步骤依赖第三章 生成的包:libffmpeg.so,是ffmpeg可执行程序,笔者将其生成了so。
1、新建CMakeLists链接ffmpeg的so
在项目中新建一个CMakeLists.txt,用于生成c++代码。
 
 在CMakeLists.txt中填入以下内容,会链接ffmpeg的所有so,以及包含头文件。
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
#拼接ffmpeg目录
set(PREFIX "${CMAKE_SOURCE_DIR}/jniLibs/ffmpeg4.3.6/24/${ANDROID_ABI}")
#包含ffmpeg头文件目录
include_directories(${PREFIX}/include)
#添加链接目录
link_directories(${PREFIX})
# 搜索ffmpeg目录下的所有.so文件
file(GLOB SO_FILES  "${PREFIX}/*.so" )  
# 获取目录下所有库名
foreach(SO_FILE IN LISTS SO_FILES)# 获取不带路径的文件名get_filename_component(LIB_NAME ${SO_FILE} NAME)list(APPEND FFMPEG_LIBRARIES "${LIB_NAME}")
endforeach()find_library( log-liblog )add_library( ffmpeg_v4_native-libSHAREDnative-lib.cpp
)target_link_libraries(ffmpeg_v4_native-lib#ffmpeg链接到native-lib${FFMPEG_LIBRARIES}${log-lib}android
)
 
在build.gradle中关联CmakeLists.txt
 
externalNativeBuild {cmake {path "src/main/cpp/CMakeLists.txt"}
}
 
2、封装命令行方法
此步骤依赖第三章 生成的包:libffmpeg.so,是ffmpeg可执行程序,笔者将其生成了so。
 新建一个cpp文件用于实现ffmpeg命令行的调用。
 
(1)、导入main符号
// 导入ffmpeg的main符号,直接jni或者ffi调用也行,但是需要解决一个问题:ffmpeg内部会调用exit退出进程,在安卓会导致Activity退出。
//目前本文件的解决方案是在exit过程中抛出c++异常并捕获,中断后续退出操作。
extern "C" int main(int argc, char **argv);
 
(2)、将字符串解析为argc、argv
参考C++ 将字符串解析为argc、argv
(3)、注册log回调输出安卓日志
输出安卓日志方便调试。
//  自定义的日志回调函数,输入安卓日志
av_log_set_callback([](void *avcl, int level, const char *fmt, va_list vl){if (level >= 0)level &= 0xff;if (level > av_log_get_level())return;AVBPrint part;av_bprint_init(&part, 0, 65536);av_vbprintf(&part, fmt, vl);//打印安卓日志标签为ffmpeg__android_log_print(ANDROID_LOG_INFO, "ffmpeg", "%s", part.str);av_bprint_finalize(&part, NULL); });
 
(4)、阻止exit退出
ffmpeg中有大量异常流程会调用exit,会导致整个进程退出,所以需要阻止这种情况,我们可以注册atexit并抛出异常,中断exit操作。
atexit([](){ throw 0; });
 
try catch中捕获异常,避免程序终止。
try
{// 调用ffmpeg的mainreturn main(argv.size(), argv.data());
}
catch (...)
{
//main里面调用了exit会走到这里。
}
 
完整代码
在native-lib.cpp中加入如下代码
#include <jni.h>
#include <dlfcn.h>
#include <android/log.h>
#include <mutex>
#include <iostream>
#include <sstream>
#include <vector>
#include <string>
extern "C"
{
#include <libavutil/log.h>
#include <libavutil/bprint.h>
}
// 导入ffmpeg的main符号,直接jni或者ffi调用也行,但是需要解决一个问题:ffmpeg内部会调用exit退出进程,在安卓会导致Activity退出。
//目前本文件的解决方案是在exit过程中抛出c++异常并捕获,中断后续退出操作。
extern "C" int main(int argc, char **argv);
static std::vector<std::string> split(const std::string &str, char delim);
static std::vector<std::string> parseArgv(const std::string &str);
static bool _isFFmpegInit = false;
static std::mutex _mtx;
// 调用ffmpeg命令行,参数是命令行,例如:ffmpeg --version
extern "C" int ffmpeg_exec(char *shell)
{if (!_isFFmpegInit){std::unique_lock<std::mutex> lck(_mtx);if (!_isFFmpegInit){//  自定义的日志回调函数av_log_set_callback([](void *avcl, int level, const char *fmt, va_list vl){if (level >= 0)level &= 0xff;if (level > av_log_get_level())return;AVBPrint part;av_bprint_init(&part, 0, 65536);av_vbprintf(&part, fmt, vl);__android_log_print(ANDROID_LOG_INFO, "ffmpeg", "%s", part.str);av_bprint_finalize(&part, NULL); });// 注册退出回调,在回调触发异常,阻止ffmpeg调用exit进程退出。atexit([](){ throw 0; });_isFFmpegInit = true;}}try{// 解析命令行auto strArgv = parseArgv(shell);std::vector<char *> argv;for (int i = 0; i < strArgv.size(); i++)argv.push_back((char *)strArgv[i].c_str());// 调用ffmpeg的mainreturn main(argv.size(), argv.data());}catch (...){}return -1;
}static std::vector<std::string> split(const std::string &str, char delim)
{std::vector<std::string> tokens;std::istringstream iss(str);std::string token;while (std::getline(iss, token, delim))if (!token.empty())tokens.push_back(token);return tokens;
}
static std::vector<std::string> parseArgv(const std::string &str)
{std::vector<std::string> args;int n = 0;for (auto i : split(str, '"')){if (n++ % 2 == 0)for (auto j : split(i, ' '))args.push_back(j);elseargs.push_back(i);}return args;
}
 
3、dart ffi调用
导入方法,上一步生成的库名称是libffmpeg_v4_native-lib.so,里面提供的符号是ffmpeg_exec。
import 'package:ffi/ffi.dart';
import 'dart:ffi';
final int Function(Pointer<Utf8> shell) _ffmpeg_exec =DynamicLibrary.open("libffmpeg_v4_native-lib.so").lookup<NativeFunction<Int32 Function(Pointer<Utf8>)>>('ffmpeg_exec').asFunction(isLeaf: true);
 
封装成dart方法
//执行ffmpeg命令行。
int ffmpegExec(String s) {final cmd = s.toNativeUtf8();final ret = _ffmpeg_exec(cmd);malloc.free(cmd);return ret;
}
 
4、java jni调用
查找android使用jna调用so的方法。按上述步骤生成的so名称为libffmpeg_v4_native-lib.so,符号对应java为int ffmpeg_exec(String s)
三、完整代码
flutter示例项目,已加入第二章生成好的包里。
四、使用示例
1、flutter调用命令行rtsp拉流
作为测试命令,-f以及输出为空
Future<void> main() async {ffmpegExec("ffmpeg -rtsp_transport tcp -i  rtsp://rtspstream:a4388c5a3f8c06031368479b29087a09@zephyr.rtsp.stream/movie  -vcodec  copy  -f  null  _  ");runApp(const MyApp());
}
 
效果预览

总结
以上就是今天讲述的内容,ffmpeg的so部署还是比较容易的,但是命令行的调用会麻烦一些,尤其是要解决ffmpeg退出问题,在c++中比较好解决,java理论上也比较好实现,如果在dart中则会比较麻烦,因为dart的方法通常不能跨线程调用,多线程的情况会出问题。
附录
1、dart将字符串解析为argc、argv
//将字符串解析为argv
List<String> _stringToArgv(String input) {// 使用正则表达式分割字符串// 这里使用了简单的空白字符分割,但是不会处理引号内的空格var parts = input.split(RegExp(r'\s+'));// 处理引号内的文本var argv = <String>[];for (var part in parts) {if (part.startsWith('"') && part.endsWith('"')) {// 去除引号argv.add(part.substring(1, part.length - 1));} else if (part.isNotEmpty) {argv.add(part);}}return argv;
}
 
将字符串数组,转为native type的argc、argv。
 下列代码资源释放略
final args = _stringToArgv(s);
final argc=args.length;
final argv =calloc.allocate<Pointer<Char>>(sizeOf<Pointer>() * (args.length));for (int i = 0; i < args.length; i++) {argv[i] = args[i].toNativeUtf8().cast();}
相关文章:
安卓部署ffmpeg全平台so并实现命令行调用
安卓 FFmpeg系列 第一章 Ubuntu生成ffmpeg安卓全平台so 第二章 Windows生成ffmpeg安卓全平台so 第三章 生成支持x264的ffmpeg安卓全平台so 第四章 部署ffmpeg安卓全平台so并使用(本章) 文章目录 安卓 FFmpeg系列前言一、添加so1、拷贝ffmpeg到项目2、bu…...
Go语言中MD5盐值加密解决用户密码问题
1. 用户密码存储的挑战 在Web应用开发中,用户密码的安全存储是一个核心问题。明文存储用户密码是极其危险的,因为一旦数据库被泄露,攻击者就可以直接获取用户的密码。为了保护用户密码,我们需要采取加密措施。 2. MD5算法简介 …...
flutter开发实战-本地SQLite数据存储
flutter开发实战-本地SQLite数据库存储 正在编写一个需要持久化且查询大量本地设备数据的 app,可考虑采用数据库。相比于其他本地持久化方案来说,数据库能够提供更为迅速的插入、更新、查询功能。这里需要用到sqflite package 来使用 SQLite 数据库 预…...
【路由組件】
完成Vue Router 安装后,就可以使用路由了,路由的基本使用步骤,首先定义路由组件,以便使用Vue Router控制路由组件展示与 切换,接着定义路由链接和路由视图,以便告知路由组件渲染到哪个位置,然后…...
【C++风云录】数字逻辑设计优化:电子设计自动化与集成电路
集成电路设计:打开知识的大门 前言 本文将详细介绍关于数字芯片设计,电子设计格式解析,集成电路设计工具,硬件描述语言分析,电路验证以及电路优化六个主题的深入研究与实践。每一部分都包含了主题的概述,…...
Flask Response 对象
文章目录 创建 Response 对象设置响应内容设置响应状态码设置响应头完整的示例拓展设置响应的 cookie重定向响应发送文件作为响应 总结 Flask 是一个 Python Web 框架,用于快速开发 Web 应用程序。在 Flask 中,我们使用 Response 对象来构建 HTTP 响应。…...
算法001:移动零
力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/move-zeroes/ 使用 双指针 来解题: 此处的双指针,…...
基于springboot+vue+Mysql的网上书城管理系统
开发语言:Java框架:springbootJDK版本:JDK1.8服务器:tomcat7数据库:mysql 5.7(一定要5.7版本)数据库工具:Navicat11开发软件:eclipse/myeclipse/ideaMaven包:…...
python实现绘制烟花代码
在Python中,我们可以使用多个库来绘制烟花效果,例如turtle库用于简单的绘图,或者更复杂的库如pygame或matplotlib结合动画。但是,由于turtle库是Python自带的,我们可以使用它来绘制一个简单的烟花效果。 下面是一个使…...
Python小白的机器学习入门指南
Python小白的机器学习入门指南 大家好!今天我们来聊一聊如何使用Python进行机器学习。本文将为大家介绍一些基本的Python命令,并结合一个简单的数据集进行实例讲解,希望能帮助你快速入门机器学习。 数据集介绍 我们将使用一个简单的鸢尾花数…...
学校上课,是耽误我学习了。。
>>上一篇(文科生在三本院校,读计算机专业) 2015年9月,我入学了。 我期待的大学生活是多姿多彩的,我会参加各种社团,参与各种有意思的活动。 但我是个社恐,有过尝试,但还是难…...
OpenFeign高级用法:缓存、QueryMap、MatrixVariable、CollectionFormat优雅地远程调用
码到三十五 : 个人主页 微服务架构中,服务之间的通信变得尤为关键。OpenFeign,一个声明式的Web服务客户端,使得REST API的调用变得更加简单和优雅。OpenFeign集成了Ribbon和Hystrix,具有负载均衡和容错的能力ÿ…...
python基础之函数
目录 1.函数相关术语 2.函数类型分类 3.栈 4.位置参数和关键字参数 5.默认参数 6.局部变量和全局变量 7.返回多个值 8.怀孕函数 9.匿名函数 10.可传递任意个数实参的函数 11.函数地址与函数接口 12.内置函数修改与函数包装 1.函数相关术语 函数的基本概念有函数头…...
深入理解C#中的IO操作 - FileStream流详解与示例
文章目录 一、FileStream类的介绍二、文件读取和写入2.1 文件读取(FileStream.Read)2.2 文件写入(FileStream.Write) 三、文件复制、移动和目录操作3.1 文件复制(FileStream.Copy)3.2 文件移动(…...
信息泄露--注意点点
目录 明确目标: 信息泄露: 版本软件 敏感文件 配置错误 url基于文件: url基于路由: 状态码: http头信息泄露 报错信息泄露 页面信息泄露 robots.txt敏感信息泄露 .get文件泄露 --判断: 搜索引擎收录泄露 BP: 爆破: 明确目标: 失能 读取 写入 执行 信息泄…...
位运算符
自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 位运算符是把数字看作二进制数来进行计算的,因此,需要先将要执行运算的数据转换为二进制,然后才能进行执行运算。…...
云上聚智——移动云云服务器进行后端的搭建及部署
什么是移动云 移动云是指将移动设备和云计算技术相结合,为移动应用提供强大的计算和存储能力的服务模式。传统的移动应用通常在本地设备上进行计算和存储,而移动云将这些任务转移到云端进行处理。通过移动云,移动设备可以利用云端的高性能计算…...
C语言程序的编译
目录 一、预处理(预编译) 二、编译 三、汇编 四,链接 在前面讲到了宏的定义,那么宏在编译时候是如何发生替换的?接下来做一下详细的介绍C语言程序的编译过程主要包括以下几个步骤:预处理、编译、汇编和…...
滴滴三面 | Go后端研发
狠狠的被鞭打了快两个小时… 注意我写的题解不一定是对的,如果你认为有其他答案欢迎评论区留言 bg:23届 211本 社招 1. 自我介绍 2. 讲一个项目的点,因为用到了中间件平台的数据同步,于是开始鞭打数据同步。。 3. 如果同步的时候…...
深度学习之基于Yolov3的行人重识别
欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介 二、功能三、系统四. 总结 一项目简介 一、项目背景 行人重识别(Person Re-Identification,简称ReID)是计算机视觉领域…...
谷歌浏览器插件
项目中有时候会用到插件 sync-cookie-extension1.0.0:开发环境同步测试 cookie 至 localhost,便于本地请求服务携带 cookie 参考地址:https://juejin.cn/post/7139354571712757767 里面有源码下载下来,加在到扩展即可使用FeHelp…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
Fabric V2.5 通用溯源系统——增加图片上传与下载功能
fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...
网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
《C++ 模板》
目录 函数模板 类模板 非类型模板参数 模板特化 函数模板特化 类模板的特化 模板,就像一个模具,里面可以将不同类型的材料做成一个形状,其分为函数模板和类模板。 函数模板 函数模板可以简化函数重载的代码。格式:templa…...
