当前位置: 首页 > news >正文

学做川菜网站平台软件定制开发

学做川菜网站,平台软件定制开发,wordpress 公众号采集器,dedecms做网站注意事项安卓 FFmpeg系列 第一章 Ubuntu生成ffmpeg安卓全平台so 第二章 Windows生成ffmpeg安卓全平台so 第三章 生成支持x264的ffmpeg安卓全平台so 第四章 部署ffmpeg安卓全平台so并使用(本章) 文章目录 安卓 FFmpeg系列前言一、添加so1、拷贝ffmpeg到项目2、bu…

安卓 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();}
http://www.15wanjia.com/news/44057.html

相关文章:

  • 郑州 手机网站国外媒体报道
  • 推进纪委网站建设寻找客户资源的网站
  • 网站开发小程序开发公司营销方式和手段
  • 好玩的网页游戏排行榜电脑桂林seo排名
  • 杭州做档口批发的网站免费建网站知乎
  • 怎么低成本做网站天门网站建设
  • 如何找人做网站竞价关键词排名软件
  • 网站网域名查询竞猜世界杯
  • 网站建设的要求有哪些上海关键词排名优化公司
  • 西安营销型网站制作平台接广告在哪里接的
  • 开发一个网站一般需要多少钱域名备案查询
  • 灵犀科技 高端网站建设游戏推广员拉人技巧
  • 沈阳网站建设找世纪兴网络推广seo怎么做
  • 捷信做单官方网站360站长平台
  • 商业设计包括什么百度seo最成功的优化
  • 能赚钱的网站怎么做网络营销学什么内容
  • 做网站分销违法吗网络优化大师手机版
  • 巩义做网站汉狮公司石首seo排名
  • wordpress站点实例深圳做网站的
  • 国内手机网站建设哪里有整站优化
  • 网站维护与优化教程今天重大国际新闻
  • dede 手机网站网页优化公司
  • 开发员给我用织梦做的网站网站技术外包公司
  • 辽宁购物网站制作个人怎么做互联网推广平台
  • 安徽哪家公司做网站比较好免费网页制作模板
  • 电商网站建设开发公司网站制作报价
  • 慧聪网登录关键词推广优化
  • 网站建设总结体会外国人b站
  • 天津市建设 银行网站网络销售的工作内容
  • 做品牌断码的网站seo搜索引擎实战详解