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

openharmony 使用uvc库获取摄像头数据使用nativewindow显示

界面代码:

XComponent({ id: 'xcomponentId', type: 'texture', libraryname: 'entry' }).width(800).height(500)

Natvie代码:

1、头文件

//NativeWindow
#include <ace/xcomponent/native_interface_xcomponent.h>
#include <cstdint>
#include <native_window/external_window.h>
#include <sys/mman.h>//UVC
#include "libuvc.h"
#include "stdio.h"
#include "string.h"

2、关键变量

OHNativeWindow* nativeWindow = nullptr;
BufferHandle* bufferHandle = nullptr;
// 初始化 OH_NativeXComponent_Callback
OH_NativeXComponent_Callback callback;
void* mappedAddr = nullptr;
int winwidth = 640;
int winheigh = 480;
OHNativeWindowBuffer* buffer = nullptr;
int fenceFd;
// 设置刷新区域,如果Region中的Rect为nullptr,或者rectNumber为0,则认为OHNativeWindowBuffer全部有内容更改。
Region region{nullptr, 0};

3、注册回调函数,通过回调函数获取nativewindow,可以在

static napi_value Init(napi_env env, napi_value exports)中调用
// 定义回调函数
void OnSurfaceCreatedCB(OH_NativeXComponent* component, void* window)
{// 可获取 OHNativeWindow 实例nativeWindow = static_cast<OHNativeWindow*>(window);OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","surface create get nativewindow");// ...
}
void OnSurfaceChangedCB(OH_NativeXComponent* component, void* window)
{// 可获取 OHNativeWindow 实例nativeWindow = static_cast<OHNativeWindow*>(window);// ...
}
void OnSurfaceDestroyedCB(OH_NativeXComponent* component, void* window)
{// 可获取 OHNativeWindow 实例nativeWindow = static_cast<OHNativeWindow*>(window);// ...
}
void DispatchTouchEventCB(OH_NativeXComponent* component, void* window)
{// 可获取 OHNativeWindow 实例nativeWindow = static_cast<OHNativeWindow*>(window);OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","DispatchTouchEventCB get nativewindow");// ...
}static napi_value nativewin_init(napi_env env,  napi_value exports)
{
#if NATIVE_WINDOW_napi_value exportInstance = nullptr;// 用来解析出被wrap了NativeXComponent指针的属性napi_get_named_property(env, exports, OH_NATIVE_XCOMPONENT_OBJ, &exportInstance);OH_NativeXComponent *nativeXComponent = nullptr;// 通过napi_unwrap接口,解析出NativeXComponent的实例指针napi_unwrap(env, exportInstance, reinterpret_cast<void**>(&nativeXComponent));// 获取XComponentIdchar idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;OH_NativeXComponent_GetXComponentId(nativeXComponent, idStr, &idSize);callback.OnSurfaceCreated = OnSurfaceCreatedCB;callback.OnSurfaceChanged = OnSurfaceChangedCB;callback.OnSurfaceDestroyed = OnSurfaceDestroyedCB;callback.DispatchTouchEvent = DispatchTouchEventCB;// 注册回调函数OH_NativeXComponent_RegisterCallback(nativeXComponent, &callback);return nullptr;#endif
}

4、设置nativewindow窗口


void SetNativeWindow(OHNativeWindow* nativeWindow, uint64_t width,  uint64_t height)
{if(nativeWindow!=nullptr){// 设置 OHNativeWindowBuffer 的宽高int32_t code = SET_BUFFER_GEOMETRY;// 这里的nativeWindow是从上一步骤中的回调函数中获得的winwidth = width;winheigh = height;int32_t bufferHeight = static_cast<int32_t>(height );int32_t bufferWidth = static_cast<int32_t>(width );int32_t ret = OH_NativeWindow_NativeWindowHandleOpt(nativeWindow, code, bufferWidth, bufferHeight);if(ret<0){OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","SetNativeWindow fail %{public}d",ret);} else {OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","SetNativeWindow  ok");}} else {OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","get NativeWindow fail");}
}

5、显示一帧数据  可以放到uvc的回调函数中

void draw_init(OHNativeWindow* nativeWindow,BufferHandle* bufferHandle,int32_t *bgrdata)
{if(nativeWindow!=nullptr){//OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","draw_init");// 通过 OH_NativeWindow_NativeWindowRequestBuffer 获取 OHNativeWindowBuffer 实例OH_NativeWindow_NativeWindowRequestBuffer(nativeWindow, &buffer, &fenceFd);if(buffer == nullptr){OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","RequestBuffer fail");return;}else{//OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","RequestBuffer ok");}// 通过 OH_NativeWindow_GetBufferHandleFromNative 获取 buffer 的 handlebufferHandle = OH_NativeWindow_GetBufferHandleFromNative(buffer);if(bufferHandle == nullptr){OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","GetBufferHandle fail");return;}else{//OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","GetBufferHandle ok");}// 使用内存映射函数mmap将bufferHandle对应的共享内存映射到用户空间,可以通过映射出来的虚拟地址向bufferHandle中写入图像数据// bufferHandle->virAddr是bufferHandle在共享内存中的起始地址,bufferHandle->size是bufferHandle在共享内存中的内存占用大小mappedAddr = mmap(bufferHandle->virAddr, bufferHandle->size, PROT_READ | PROT_WRITE, MAP_SHARED, bufferHandle->fd, 0);if (mappedAddr == MAP_FAILED) {// mmap failedOH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","mmap failed");return;} else {//OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","mmap ok");}static uint32_t value = 0xFF;int32_t index = 0;uint32_t *pixel = static_cast<uint32_t *>(mappedAddr); // 使用mmap获取到的地址来访问内存for (uint32_t x = 0; x < winwidth ; x++) {for (uint32_t y = 0;  y < winheigh; y++) {*pixel++ = bgrdata[index++];}}// 设置刷新区域,如果Region中的Rect为nullptr,或者rectNumber为0,则认为OHNativeWindowBuffer全部有内容更改。// 通过OH_NativeWindow_NativeWindowFlushBuffer 提交给消费者使用,例如:显示在屏幕上。OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow, buffer, fenceFd, region);//OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow, buffer, fenceFd, region);// 内存使用完记得去掉内存映射int result = munmap(mappedAddr, bufferHandle->size);if (result == -1) {// munmap failed}//OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","mmap write ok");} else {OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","draw_init fail");}}

6、uvc设备打开及注册回到函数

bool s_cbSignal=false;
void uvc_callback(uvc_frame_t *frame, void *ptr);void uvc_start()
{uvc_context_t *m_ctx = nullptr;uvc_device_t *m_dev = nullptr;/*!< uvc设备结构*/uvc_device_handle_t *m_devh = nullptr;/*!< 打开uvc设备的句柄*/int fps = 25;uvc_frame_format frame_format = UVC_FRAME_FORMAT_MJPEG;uvc_error_t res = uvc_init(&m_ctx, nullptr);int deviceWidth = 640;int deviceHeight = 480;if (res < 0){OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc init fail");} else {OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc init ok");}int VID=0xxxx,PID=0xxxx;res = uvc_find_device( m_ctx, &m_dev, VID, PID, nullptr);if (res < 0){OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc find fail");} else {OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc find ok");res = uvc_open(m_dev, &m_devh);if (res < 0){OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc open fail%{public}d",res);} else {OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc open ok");uvc_stream_ctrl_t ctrl;res = uvc_get_stream_ctrl_format_size( m_devh, &ctrl, frame_format, deviceWidth, deviceHeight, fps);uvc_print_stream_ctrl(&ctrl, stderr);if (res < 0){OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc get stream mode fail");} else {OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc get stream mode ok");void* user_ptr = nullptr;res = uvc_start_streaming(m_devh, &ctrl, uvc_callback, user_ptr, 0);if(res < 0){OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc_start_streaming error");} else {OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc_start_streaming ok");}struct timeval tpstart,tpend;float timeuse;gettimeofday(&tpstart,nullptr);while(!s_cbSignal){gettimeofday(&tpend,nullptr);timeuse=(1000000*(tpend.tv_sec-tpstart.tv_sec) + tpend.tv_usec-tpstart.tv_usec)/1000000.0;if(timeuse > 2){//printf("等待回调超时\n");return ;}}return ;}}}
}void uvc_callback(uvc_frame_t *frame, void *ptr)
{//OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc getting stream");s_cbSignal = true;
#if 1static int g_current_bufNum = 0;if(g_current_bufNum >= 3){g_current_bufNum = 0;}(void)ptr;uvc_frame_t *bgr;uvc_error_t ret = UVC_ERROR_OTHER;bgr = uvc_allocate_frame(frame->width * frame->height * 3);if (!bgr){OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc allocate error");return;}//OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc_allocate_frame");try {ret = uvc_any2rgb(frame, bgr);//RGBif (ret){uvc_perror(ret, "uvc_any2rgb(face)");uvc_free_frame(bgr);OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc any2rgb");return;}//OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc_any2rgb");} catch (...){uvc_perror(ret, "uvc_any2rgb(face) catch");uvc_free_frame(bgr);OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc any2rgb catch");return;}//"frame width=%{public}d height=%{public}d format=%{public}d size=%{public}d",//bgr->width,bgr->height,bgr->frame_format,bgr->data_bytes);static uint32_t value = 0xFF;int32_t index = 0;int32_t indexrgb = 0;for (uint32_t x = 0; x < winwidth ; x++) {for (uint32_t y = 0;  y < winheigh; y++) {value = *((uint8_t *)(bgr->data)+indexrgb++); value+=(*((uint8_t *)(bgr->data)+indexrgb++)<<8);value+=(*((uint8_t *)(bgr->data)+indexrgb++)<<16);bmpdata[index++] = value;}}draw_init(nativeWindow,bufferHandle,bmpdata);//memcpy(bmpdata,bgr->data,bgr->data_bytes);uvc_free_frame(bgr);//OH_LOG_Print(LOG_APP,LOG_INFO, LOG_PRINT_DOMAIN,"Init","uvc_free_frame");
#endif
}

需要依赖的库

target_link_libraries(entry PUBLIC libace_napi.z.so  libhilog_ndk.z.so )
target_link_libraries(entry PUBLIC libace_ndk.z.so libuvc.so)
target_link_libraries(entry PUBLIC libnative_buffer.so libnative_window.so)

相关文章:

openharmony 使用uvc库获取摄像头数据使用nativewindow显示

界面代码&#xff1a; XComponent({ id: xcomponentId, type: texture, libraryname: entry }).width(800).height(500) Natvie代码&#xff1a; 1、头文件 //NativeWindow #include <ace/xcomponent/native_interface_xcomponent.h> #include <cstdint> #incl…...

SQL Server 实战 - 多种连接

目录 背景 一、多种连接 1. 复合连接条件 2. 跨数据库连接 3. 隐连接 4. 自连接 5. 多表外连接 6. UNION ALL 二、一个对比例子 背景 本专栏文章以 SAP 实施顾问在实施项目中需要掌握的 sql 语句为偏向进行选题&#xff1a; 用例&#xff1a;SAP B1 的数据库工具&am…...

【手术显微镜】市场高度集中,由于高端手术显微镜的制造技术主要掌握于欧美企业

摘要 HengCe (恒策咨询&#xff09;是全球知名的大型咨询机构&#xff0c;长期专注于各行业细分市场的调研。行业层面&#xff0c;重点关注可能存在“卡脖子”的高科技细分领域。企业层面&#xff0c;重点关注在国际和国内市场在规模和技术等层面具有代表性的企业&#xff0c;…...

IDEA 2024 配置Maven

Step 1:确定下载Apache Maven版本 在IDEA 2024中&#xff0c;随便新建一个Maven项目&#xff1b; 在File下拉菜单栏中&#xff0c;找到Setings&#xff1b; 在Build&#xff0c;Execution&#xff0c;Deployment中找到Maven 确定下载的Apache Maven版本应略低于或等于IDEA绑…...

Admin.NET框架使用宝塔面板部署步骤

文章目录 Admin.NET框架使用宝塔面板部署步骤&#x1f381;框架介绍部署步骤1.Centos7 部署宝塔面板2.部署Admin.NET后端3.部署前端Web4.访问前端页面 Admin.NET框架使用宝塔面板部署步骤 &#x1f381;框架介绍 Admin.NET 是基于 .NET6 (Furion/SqlSugar) 实现的通用权限开发…...

Flutter中的Future和Stream

在 Flutter 中&#xff0c;Future 和 Stream 都是用于处理异步操作的类&#xff0c;它们都基于 Dart 的异步编程模型&#xff0c;但是它们的使用场景和工作方式有所不同。以下是它们的区别以及各自适用的场景。 目录 一、Future1、基本使用2、异常处理1. catchError2. onError…...

107.【C语言】数据结构之二叉树求总节点和第K层节点的个数

目录 1.求二叉树总的节点的个数 1.容易想到的方法 代码 缺陷 思考:能否在TreeSize函数内定义静态变量解决size的问题呢? 其他写法 运行结果 2.最好的方法:分而治之 代码 运行结果 2.求二叉树第K层节点的个数 错误代码 运行结果 修正 运行结果 其他写法 1.求二…...

spring boot支持那些开发工具?

Spring Boot 支持多种开发工具&#xff0c;以帮助开发者更高效地进行应用开发。以下是小编给大家分享几种常用的开发工具及其特点&#xff1a; IntelliJ IDEA&#xff1a; IntelliJ IDEA 是一款非常流行的 Java IDE&#xff0c;它提供了对 Spring Boot 的全面支持&#xff0c;…...

Go-MediatR:Go语言中的中介者模式

在Go语言中&#xff0c;确实存在一个与C#中的MediatR类似的组件包&#xff0c;名为Go-MediatR。 Go-MediatR是一个受.NET中MediatR库启发的Go语言实现&#xff0c;它专注于通过中介者模式简化命令查询责任分离&#xff08;CQRS&#xff09;模式的处理和在事件驱动架构中的应用…...

5.11【机器学习】

先是对图像进行划分 划分完后&#xff0c; 顺序读取文件夹&#xff0c;在文件夹里顺序读取图片&#xff0c; 卷积层又称为滤波器&#xff0c;通道是说滤波器的个数&#xff0c;黑白通道数为1&#xff0c;RGB通道个数为3 在输入层&#xff0c;对于输入层而言&#xff0c;滤波…...

在 CentOS 上安装 Docker:构建容器化环境全攻略

一、引言 在当今的软件开发与运维领域&#xff0c;Docker 无疑是一颗璀璨的明星。它以轻量级虚拟化的卓越特性&#xff0c;为应用程序的打包、分发和管理开辟了崭新的高效便捷之路。无论是开发环境的快速搭建&#xff0c;还是生产环境的稳定部署&#xff0c;Docker 都展现出了…...

Python练习(2)

重复元素判定续。利用集合的无重复性来编写一个程序如果有一个元素出现了不止一次则返回true但不要改变原来列表的值&#xff1a; 一&#xff1a; def has_duplicates(lst): # 使用集合来存储已经见过的元素 seen set() for item in lst: if item in seen: # 如果元素已经在…...

如何实现一套键盘鼠标控制两台计算机(罗技Options+ Flow功能快速实现演示)

需求背景 之前我写过一篇文章如何实现一套键盘鼠标控制两台计算机&#xff08;Mouse Without Borders快速上手教程&#xff09;_一套键鼠控制两台电脑-CSDN博客 当我们在局域网内有两台计算机&#xff0c;想使用一套键鼠操控时&#xff0c;可以安装Mouse Without Borders软件…...

现代应用程序中基于 Cell 架构的安全防护之道

在飞速发展的软件开发领域&#xff0c;基于 Cell 的架构日益流行起来。其概念源自船舶舱壁的设计准则&#xff0c;即单独的水密舱室能允许故障孤立存在。通过将这个概念应用于软件&#xff0c;我们创建了一个架构&#xff0c;将应用程序划分为离散的、可管理的组件&#xff0c;…...

【导航查询】.NET开源 ORM 框架 SqlSugar 系列

.NET开源 ORM 框架 SqlSugar 系列 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列【Code First】.NET开源 ORM 框架 SqlSugar 系列【数据事务…...

【基础分析】——Qt 信号和槽的机制 优点

QT信号和槽机制的优点包括&#xff1a; 1、类型安全&#xff1a; 信号和槽的签名必须是等同的&#xff0c;即信号的参数类型和参数个数必须与接收该信号的槽的参数类型和参数个数相同。 2、松散耦合&#xff1a; 信号和槽机制减弱了Qt对象的耦合度。激发信号的Qt对象无须知道…...

Vue3学习宝典

1.ref函数调用的方式生成响应式数据&#xff0c;可以传复杂和简单数据类型 <script setup> // reactive接收一个对象类型的数据 import { reactive } from vue;// ref用函数调用的方式生成响应式数据&#xff0c;可以传复杂和简单数据类型 import { ref } from vue // 简…...

leecode96.不同的二叉搜索树

在画的过程中发现规律&#xff0c;每次选择不同的节点作为根节点&#xff0c;左右两边的节点再排列组合一下就能求出总数 class Solution { public:int numTrees(int n) {vector<int> dp(n1,0);dp[0]1;for(int i1;i<n;i)for(int j0;j<i;j)dp[i]dp[i-j-1]*dp[j];ret…...

树莓派基本配置-基础配置配置

树莓派基本配置 文章目录 树莓派基本配置前言硬件准备树莓派刷机串口方式登录树莓派接入网络ssh方式登录树莓派更换国内源xrdp界面登录树莓派远程文件传输FileZilla 前言 树莓派是一款功能强大且价格实惠的小型计算机&#xff0c;非常适合作为学习编程、物联网项目、家庭自动化…...

手机卡限速丨中国移动5G变3G,网速500kb

以下猜测错误&#xff0c;又有新的猜测&#xff1a;河南移动的卡出省限速。可能是因为流量结算。 “2024年7月1日起&#xff0c;中国移动集团内部将开启跨省流量结算” 在深圳四五年了&#xff0c;之前没有过&#xff0c;就从上个月开始。11月底解除限速&#xff0c;12月刚开…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql

智慧工地管理云平台系统&#xff0c;智慧工地全套源码&#xff0c;java版智慧工地源码&#xff0c;支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求&#xff0c;提供“平台网络终端”的整体解决方案&#xff0c;提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

【入坑系列】TiDB 强制索引在不同库下不生效问题

文章目录 背景SQL 优化情况线上SQL运行情况分析怀疑1:执行计划绑定问题?尝试:SHOW WARNINGS 查看警告探索 TiDB 的 USE_INDEX 写法Hint 不生效问题排查解决参考背景 项目中使用 TiDB 数据库,并对 SQL 进行优化了,添加了强制索引。 UAT 环境已经生效,但 PROD 环境强制索…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存

文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...