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

鸿蒙 next 实现摄像头视频预览编码(一)

鸿蒙 next 即将发布,让我们先喊3遍 遥遥领先~ 遥遥领先~ 遥遥领先~

作为一门新的系统,本人也是刚入门学习中,如果对于一些理解有问题的,欢迎即使指出哈

首先这里要讲一下,在鸿蒙 next 中,要实现摄像头预览&编码有两种方式。第一种,通过摄像头的预览流&录制流来实现,其中预览很简单,直接使用 xcomponent 即可,对于编码,则可以通过创建编码器获取到的 surfaceid 传递给录制流即可。第二种是通过 nativeimage 类似于 android 的 surfacetexture 然后将纹理通过 opengl 绘制到预览 surface 和编码 surface 上去,这边文章主要将第一种简单的方式,步骤大致如下:

第一步,创建 xcomponaent,代码如下:

 XComponent({id: '',type: XComponentType.SURFACE,libraryname: '',controller: this.XcomponentController}).onLoad(() => {this.XcomponentController.setXComponentSurfaceSize({surfaceWidth: this.cameraWidth, surfaceHeight: this.cameraHeight})this.XcomponentSurfaceId = this.XcomponentController.getXComponentSurfaceId()})

创建 xcomponeant 的关键是获取 surfaceid,这个后面会用来传给摄像头预览流用的。

第二步,获取编码器的 surfaceid,由于目前鸿蒙没有为编码器这块提供 arkts 接口,所以需要用到 napi 作为中间桥接,通过 arkts 来调用 c++ 代码,大致代码如下:

arkts 部分:

import recorder from 'librecorder.so'
recorder.initNative()

librecorder.so 为工程中 c++ 的部分,具体可以参考项目模板中关于 c++ 的示例

napi 部分:

#include "RecorderNative.h"
#include <bits/alltypes.h>#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN 0xFF00
#define LOG_TAG "recorder"struct AsyncCallbackInfo {napi_env env;napi_async_work asyncWork;napi_deferred deferred;int32_t resultCode = 0;std::string surfaceId = "";SampleInfo sampleInfo;
};void DealCallBack(napi_env env, void *data)
{AsyncCallbackInfo *asyncCallbackInfo = static_cast<AsyncCallbackInfo *>(data);napi_value code;napi_create_int32(env, asyncCallbackInfo->resultCode, &code);napi_value surfaceId;napi_create_string_utf8(env, asyncCallbackInfo->surfaceId.data(), NAPI_AUTO_LENGTH, &surfaceId);napi_value obj;napi_create_object(env, &obj);napi_set_named_property(env, obj, "code", code);napi_set_named_property(env, obj, "surfaceId", surfaceId);napi_resolve_deferred(asyncCallbackInfo->env, asyncCallbackInfo->deferred, obj);napi_delete_async_work(env, asyncCallbackInfo->asyncWork);delete asyncCallbackInfo;
}void SetCallBackResult(AsyncCallbackInfo *asyncCallbackInfo, int32_t code)
{asyncCallbackInfo->resultCode = code;
}void SurfaceIdCallBack(AsyncCallbackInfo *asyncCallbackInfo, std::string surfaceId)
{asyncCallbackInfo->surfaceId = surfaceId;
}void NativeInit(napi_env env, void *data)
{AsyncCallbackInfo *asyncCallbackInfo = static_cast<AsyncCallbackInfo *>(data);int32_t ret = Recorder::GetInstance().Init(asyncCallbackInfo->sampleInfo);if (ret != AVCODEC_SAMPLE_ERR_OK) {SetCallBackResult(asyncCallbackInfo, -1);}uint64_t id = 0;ret = OH_NativeWindow_GetSurfaceId(asyncCallbackInfo->sampleInfo.window, &id);if (ret != AVCODEC_SAMPLE_ERR_OK) {SetCallBackResult(asyncCallbackInfo, -1);}asyncCallbackInfo->surfaceId = std::to_string(id);SurfaceIdCallBack(asyncCallbackInfo, asyncCallbackInfo->surfaceId);
}napi_value RecorderNative::Init(napi_env env, napi_callback_info info)
{SampleInfo sampleInfo;napi_value promise;napi_deferred deferred;napi_create_promise(env, &deferred, &promise);AsyncCallbackInfo *asyncCallbackInfo = new AsyncCallbackInfo();asyncCallbackInfo->env = env;asyncCallbackInfo->asyncWork = nullptr;asyncCallbackInfo->deferred = deferred;asyncCallbackInfo->resultCode = -1;asyncCallbackInfo->sampleInfo = sampleInfo;napi_value resourceName;napi_create_string_latin1(env, "recorder", NAPI_AUTO_LENGTH, &resourceName);napi_create_async_work(env, nullptr, resourceName, [](napi_env env, void *data) { NativeInit(env, data); },[](napi_env env, napi_status status, void *data) { DealCallBack(env, data); }, (void *)asyncCallbackInfo,&asyncCallbackInfo->asyncWork);napi_queue_async_work(env, asyncCallbackInfo->asyncWork);return promise;
}EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{napi_property_descriptor classProp[] = {{"initNative", nullptr, RecorderNative::Init, nullptr, nullptr, nullptr, napi_default, nullptr}};return exports;
}
EXTERN_C_ENDstatic napi_module RecorderModule = {.nm_version = 1,.nm_flags = 0,.nm_filename = nullptr,.nm_register_func = Init,.nm_modname = "recorder",.nm_priv = ((void *)0),.reserved = {0},
};extern "C" __attribute__((constructor)) void RegisterRecorderModule(void) { napi_module_register(&RecorderModule); }

鸿蒙这边的 napi 其实是参考的 nodejs 的,语法基本一致,这里的大致逻辑就是调用 Recorder::GetInstance().Init() 获取编码器的 surfaceid 然后通过 ts 的 promise 传递给前端

c++ 编码器部分:

int32_t Recorder::Init(SampleInfo &sampleInfo)
{std::lock_guard<std::mutex> lock(mutex_);sampleInfo_ = sampleInfo;videoEncoder_ = std::make_unique<VideoEncoder>();muxer_ = std::make_unique<Muxer>();videoEncoder_->Create(sampleInfo_.videoCodecMime);ret = muxer_->Create(sampleInfo_.outputFd);encContext_ = new CodecUserData;videoEncoder_->Config(sampleInfo_, encContext_);muxer_->Config(sampleInfo_);sampleInfo.window = sampleInfo_.window;releaseThread_ = nullptr;return AVCODEC_SAMPLE_ERR_OK;
}

其中核心的在于 videoEncoder_->Config(),这一步会将 nativewindow 赋值给 sampleInfo 结构体,然后就可以获取到nativewindow 的 surfaceid了

代码如下:

int32_t VideoEncoder::Config(SampleInfo &sampleInfo, CodecUserData *codecUserData)
{Configure(sampleInfo);OH_VideoEncoder_GetSurface(encoder_, &sampleInfo.window);SetCallback(codecUserData);OH_VideoEncoder_Prepare(encoder_);return AVCODEC_SAMPLE_ERR_OK;
}

到此为止,xcomponents 的 surfaceid 和编码器的 surtfaceid 都获取到了,接着就是在 arkts 层创建摄像头,并设置预览&编码输出了,这块比较简单,照着文档来就行,代码如下:

let cameraManager = camera.getCameraManager(globalThis.context)
let camerasDevices: Array<camera.CameraDevice> = getCameraDevices(cameraManager)
let profiles: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(camerasDevices[0],camera.SceneMode.NORMAL_VIDEO)// 获取预览流profilelet previewProfiles: Array<camera.Profile> = profiles.previewProfiles// 获取录像流profilelet videoProfiles: Array<camera.VideoProfile> = profiles.videoProfiles// Xcomponent预览流let XComponentPreviewProfile: camera.Profile = previewProfiles[0]// 创建 编码器 输出对象encoderVideoOutput = cameraManager.createVideoOutput(videoProfile, encoderSurfaceId)// 创建 预览流 输出对象XcomponentPreviewOutput = cameraManager.createPreviewOutput(XComponentPreviewProfile, this.XcomponentSurfaceId)// 创建cameraInput对象cameraInput = cameraManager.createCameraInput(camerasDevices[0])// 打开相机await cameraInput.open()// 会话流程videoSession = cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession// 开始配置会话videoSession.beginConfig()// 把CameraInput加入到会话videoSession.addInput(cameraInput)// 把 Xcomponent 预览流加入到会话videoSession.addOutput(XcomponentPreviewOutput)// 把编码器录像流加入到会话videoSession.addOutput(encoderVideoOutput)// 提交配置信息await videoSession.commitConfig()// 会话开始await videoSession.start()

至此,关于预览&编码的大致流程就是这样了,整体流程其实还是很简单的,核心就是获取两个 surfaceid,然后传入到摄像头录制&预览流中即可。这里就大致讲一下思路,相信做安卓或者前端的同学都能看明白。不过这种模式的一个缺点在于无法做一些深层次的操作,例如水印、美白、瘦脸等,优点在于代码量比较少。第二篇要将的是关于如何通过 opengl 来绘制预览 & 编码 surface,未完待续~

 

 

相关文章:

鸿蒙 next 实现摄像头视频预览编码(一)

鸿蒙 next 即将发布&#xff0c;让我们先喊3遍 遥遥领先~ 遥遥领先~ 遥遥领先~ 作为一门新的系统&#xff0c;本人也是刚入门学习中&#xff0c;如果对于一些理解有问题的&#xff0c;欢迎即使指出哈 首先这里要讲一下&#xff0c;在鸿蒙 next 中&#xff0c;要实现摄像头预览…...

YOLO-V3

一、概述 最大的改进就是网络结构&#xff0c;使其更适合小目标检测特征做的更细致&#xff0c;融入多持续特征图信息来预测不同规格物体先验框更丰富了&#xff0c;3种scale&#xff0c;每种3个规格&#xff0c;一共9种softmax改进&#xff0c;预测多标签任务 先验框&#xf…...

golang提案,内置 Go 错误检查函数

先来狠狠吐个槽 要吐槽 Go1 的 error &#xff0c;那咱得先整明白大家为啥都猛喷它的错误处理做得不咋地。在 Go 语言里头&#xff0c;error 本质上其实就是个 Error 的接口&#xff1a; type error interface {Error() string }实际的应用场景如下&#xff1a; func main()…...

零售业务产品系统应用架构设计(三)

智慧物业依据《住房和城乡建设部等部门关于推动物业服务企业加快发展线上线下生活服务的意见建房〔2020〕99号》,推动物业管理公司广泛运用5G、互联网、物联网、云计算、大数据、区块链和人工智能等技术,建设智慧物业管理服务平台,对接城市信息模型(CIM)和城市运行管理服务…...

【GD32】从零开始学GD32单片机 | PMU电源管理单元+深度睡眠和待机例程(GD32F470ZGT6)

1. 简介 PMU电源管理单元通俗讲就是用来管理MCU的电源域的&#xff0c;它主要有两个功能——电压监测和低功耗管理。在GD32中一共有3个电源域——VDD/VDDA域、1.2V域和备份域。 VDD/VDDA域主要供PMU控制器、ADC、DAC等外设使用&#xff1b;1.2V域就是大部分外设都会使用的电源域…...

公司员工电脑桌面太乱如何解决?桌面管理软件一招解决!

“工欲善其事&#xff0c;必先利其器。” 在数字化管理的时代背景下&#xff0c;选择合适的桌面管理软件就如同为企业网络管理装上了一双慧眼。 员工的电脑桌面往往因为长时间的使用而变得杂乱无章&#xff0c;这不仅影响了工作效率&#xff0c;还可能给企业信息安全带来隐患。…...

leetcode:2119. 反转两次的数字(python3解法)

难度&#xff1a;简单 反转 一个整数意味着倒置它的所有位。 例如&#xff0c;反转 2021 得到 1202 。反转 12300 得到 321 &#xff0c;不保留前导零 。 给你一个整数 num &#xff0c;反转 num 得到 reversed1 &#xff0c;接着反转 reversed1 得到 reversed2 。如果 reverse…...

5.vue中axios封装工程化

vue工程化中axios封装 视频演示地址&#xff1a;https://www.bilibili.com/video/BV121egeQEHg/?vd_source0f4eae2845bd3b24b877e4586ffda69a 通常我们封装需要封装request.js基础的发送请求工具类&#xff0c;再根据业务封装service类&#xff0c;service类是具体业务的接口…...

实验六:动态数码管实验

实验结果图,从右到左0-7,从左到右7-0,来回滚动。 硬件接线图: 具体看图,不说了,前面讲过,自己查资料就可以,资料得慢慢查,熟练就好了,不浪费时间和版面了 main.c代码 #include<reg52.h>typedef unsigned int u16; typedef unsigned char u8;#define SMG P0 …...

《Cloud Native Data Center Networking》(云原生数据中心网络设计)读书笔记 -- 05网络虚拟化

本章帮助网络工程师或架构师回答如下问题&#xff1a; 什么是网络虚拟化?网络虚拟化有哪些用途?网络虚拟化领域内有哪些不同的技术方向?网络虚拟化的控制面有哪些选择?当使用 VXLAN 时如何进行桥接和路由&#xff1f; 什么是网络虚拟化&#xff1f; 网络虚拟化可以让网络…...

奥威BI数据可视化展示:如何充分发挥数据价值

奥威BI数据可视化展示&#xff1a;如何充分发挥数据价值 在大数据时代&#xff0c;数据已成为企业最宝贵的资产之一。然而&#xff0c;仅仅拥有海量数据并不足以带来竞争优势&#xff0c;关键在于如何有效地挖掘、分析和展示这些数据&#xff0c;从而转化为有价值的洞察和决策…...

jenkins工具配置

上一篇&#xff08;https://blog.csdn.net/abc666_666/article/details/141207741&#xff09;文章我们介绍了基于docker安装jenkins的过程&#xff0c;本文将介绍如何配置jenkins的相关全局工具如maven、 jdk以及git等 配置的页面如下&#xff1a; 打开后的页面如下&#xff…...

VAuditDemo文件漏洞

目录 VAuditDemo文件漏洞 一、首页文件包含漏洞 包含图片马 利用伪协议phar:// 构造shell.inc被压缩为shell.zip&#xff0c;然后更改shell.zip 为 shell.jpg上传 二、任意文件读取漏洞 avatar.php updateAvatar.php logCheck.php 任意文件读取漏洞利用 VAuditDemo文件…...

[Meachines] [Medium] poison LFI+日志投毒+VNC权限提升

信息收集 IP AddressOpening Ports10.10.10.84TCP:22,80 $ nmap -p- 10.10.10.84 --min-rate 1000 -sC -sV 22/tcp open ssh OpenSSH 7.2 (FreeBSD 20161230; protocol 2.0) | ssh-hostkey: | 2048 e3:3b:7d:3c:8f:4b:8c:f9:cd:7f:d2:3a:ce:2d:ff:bb (RSA) | 256 …...

EtherCAT运动控制器上位机开发之Python+Qt(三):PDO配置与SDO读写

ZMC408CE控制器硬件介绍 ZMC408CE是正运动推出的一款多轴高性能EtherCAT总线运动控制器&#xff0c;具有EtherCAT、EtherNET、RS232、CAN和U盘等通讯接口&#xff0c;ZMC系列运动控制器可应用于各种需要脱机或联机运行的场合。 ZMC408CE支持8轴运动控制&#xff0c;最多可扩展…...

MyBatis源码系列1(使用JDBC查询数据)

使用原生jdbc进行查询数据步骤. 1、加载驱动2、获取数据库连接3、创建Statement对象4、占位符赋值5、执行脚本6、解析结果7、关闭资源 代码示例 public class T0 {public static void main(String[] args) throws Exception {String sql "SELECT id,name FROM goods WH…...

【微服务】Nacos配置中心和客户端数据同步模式

一、Nacos概述 Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。它提供了一组简单易用的特性集&#xff0c;帮助用户快速实现动态服务发现、服务配置、服务元数据及流量管理。 二、数据同步模式 1. 实时同步 Push模式&#xff1a;在服务端的配置信…...

WebRTC音视频开发读书笔记(六)

数据通道不仅可以发送文本消息, 还可以发送图片、二进制文件,将其类型binaryType属性设置成arraybuffer类型即可. 九\、文件传输 1、文件传输流程 &#xff08;1&#xff09;使用表单file打开本地文件 &#xff08;2&#xff09;使用FileReader读取文件的二进制数据 &#…...

高级列表组件ReList

高级列表组件ReList 组件实现基于 Vue3 Element Plus Typescript&#xff0c;同时引用 vueUse lodash-es tailwindCss (不影响功能&#xff0c;可忽略) 主要基于JSX风格实现高度动态的列表渲染组件&#xff0c;可以通过信息配置Metas配置控制信息项展示&#xff0c;同时支持…...

Vxe UI vue vxe-table 实现表格数据分组功能,根据字段数据分组

Vxe UI vue vxe-table 实现表格数据分组功能&#xff0c;根据字段数据分组 实现数据分组功能 基于树结构功能就可以直接实现数据分组功能&#xff0c;代码如下&#xff1a; <template><div><vxe-button status"primary" click"listToGroup()&…...

oracle创建账户

1、查看表空间 SELECT tablespace_name FROM user_tablespaces;2、创建用户 CREATE USER FLINKCDC2 IDENTIFIED BY "123456";也可以使用指定表空间的方式 CREATE USER FLINKCDC2 IDENTIFIED BY "123456" DEFAULT TABLESPACE LOGMINER_TBS QUOTA UNLIMIT…...

2024新型数字政府综合解决方案(五)

新型数字政府综合解决方案通过集成人工智能、大数据、区块链和云计算技术&#xff0c;打造了一个智能化、透明化和高效的政务服务平台&#xff0c;旨在提升政府服务的响应速度、处理效率和数据安全性。该方案实现了跨部门的数据共享与实时更新&#xff0c;通过智能化的流程自动…...

datawind可视化查询-其他函数

飞书文档学习链接:https://www.volcengine.com/docs/4726/47275 1. 用户名函数 用户名函数并非 ClickHouse 官方函数,而是与项目用户信息相结合,用于返回当前使用用户的指定信息的函数。 USERNAME()可返回当前用户的用户名,如下所示。该函数也可与其他函数组合使用 2. J…...

数据库MySQL之事务、索引

目录 1.概述 2.事务 3.索引 3.1索引结构 3.2操作语法 1.概述 场景&#xff1a;假如我们需要解散教学部&#xff0c;那么该部门下的所有员工都需要删除。如果教学部成功删除了&#xff0c;但员工出于某些原因(比如SQL语句写错了等)并没有删除&#xff0c;此时就会出现数据…...

AI学习记录 - transformers的decoder和encoder中的自注意力矩阵和掩码矩阵的数据处理

掩码掩码&#xff0c;指的是掩盖住后面的词汇的词向量对我当前词汇造成影响。把PAD字符设置成负无穷大&#xff0c;概念上不叫掩码&#xff0c;只是计算方式和掩码一样。 怎么生成掩码&#xff0c;在非掩码注意力矩阵中&#xff0c;把PAD词向量每个维度设置成负无穷大&#xf…...

【Solidity】代币

ERC20 ERC-20 全称 “Ethereum Request for Comment 20”&#xff0c;是一种标准接口&#xff0c;用于实现代币合约。ERC20 标准定义了一组函数和事件&#xff0c;使得代币可以在不同的应用和平台之间互操作。 ERC20 标准接口定义了一组必须实现的函数和事件&#xff1a; in…...

5 - Linux YUM仓库及NFS共享服务

目录 一、YUM概述 1.YUM简介 2.软件仓库的提供方式 3.RPM软件包的来源 4. yum 命令的运用 二、搭建ftp YUM仓库 三、NFS共享服务 1.NFS简述 2.模拟NFS 一、YUM概述 1.YUM简介 YUM&#xff08;Yellow dog Updater Modified&#xff09;是一个专门为了解决包的依赖关系…...

上传文件,文件类型限制语法,各种媒体视频文件的Content-Type

各种媒体视频文件的Content-Type “application/x-apple-diskimage”: “DMG”, “application/epubzip”: “EPUB”, “application/java-archive”: “JAR”, “video/x-matroska”: “MKV”, “text/html”: “HTML|HTM”, “text/css”: “CSS”, “text/javascript…...

类和对象(下)(2)

类和对象&#xff08;下&#xff09;(2) static成员 • ⽤static修饰的成员变量&#xff0c;称之为静态成员变量&#xff0c;静态成员变量⼀定要在类外进⾏初始化。 • 静态成员变量为当前类的所有对象所共享&#xff0c;不属于某个具体的对象&#xff0c;不存在对象中&#…...

软件测试 - 自动化测试(概念)(Java)(自动化测试分类、web自动化测试、驱动、selenium自动化测试工具的安装)

一、自动化的概念 ⾃动洒⽔机&#xff0c;主要通上⽔就可以⾃动化洒⽔并且可以⾃动的旋转。 ⾃动洗⼿液&#xff0c;免去了⼿动挤压可以⾃动感应出洗⼿液 超市⾃动闸⻔&#xff0c;不需要⼿动的开⻔关⻔ ⽣活中的⾃动化案例有效的减少了⼈⼒的消耗&#xff0c;同时也提⾼了⽣…...

网站建设建设公司/网页设计制作网站

晕啊&#xff0c;今天不顺啊&#xff0c;白天毕设因为一个小问题耽误了很长时间&#xff0c;刚写了一半了突然浏览器又卡死&#xff0c;打开后又待重新写。。。。。。 1 创建项目 这个不多说&#xff0c;新建个Web project都会吧。 2 导入Extjs文件 在WebRoot下新建ext文件夹&a…...

深圳石岩建网站/优化大师手机版下载

今日头条自媒体运营推广视频教程学习资料短视频运营从零到精通今日头条趣东方头条凤凰新浪看点网易企鹅UC大鱼一点资讯自媒体快传视频处理软件今日头条推广视频教程自媒体推广短视频教程今日头条引流小白入门视频解析下载支持今日头条快手抖音火山映客陌陌西瓜美拍微博等快手今…...

江苏连云港做网站/在线代理浏览网址

系统盘里带着那个啊具体步骤。。1运行GHOST,进入GHOST主页面 2在主页面单击OK按钮后依次执行Local\Partition\To lmage,命令 3接下来选择硬盘.用户装了几个硬盘,则此处就会出现几个选项. 4接下来,选择硬盘中的分区.选择需要的分区后,单击OK进入下一个步骤 5选择存放的地址和文件…...

草莓网是b2b吗/搜索引擎优化怎么做的

考虑这个y(x)函数&#xff1a;我们可以在文件中生成这些分散的点&#xff1a;dataset_1D.dat&#xff1a;# x y0 01 12 03 -94 -32以下是这些点的一维插值代码&#xff1a;加载这些分散的点创建x_mesh执行1D插值代码&#xff1a;^{pr2}$图中显示了以下内容&#xff1a;现在&…...

高唐做网站建设的公司/网站运营需要多少钱

最近也在思考这个问题,网上看了一些资料,得出的总结:分类表 < 商品表 < SKU表(库存表)分类表 < 属性名 < 属性值商品表 < 商品和属性关系表 > 属性名|属性值分类表:(商品分类编号, 分类名称, 父分类编号)(1, 男装, 0)(2, 裤子, 1)(3, 外套, 1)(4, 内裤, 1)(5…...

利鑫做彩票网站/网站优化企业排名

主流的浏览器IE、火狐、Safari和Chrome等&#xff0c;都具有某种向用户报告JavaScript错误的机制。默认情况下&#xff0c;浏览器都会隐藏这些错误&#xff0c;因为除了开发者没有人会关心这些错误信息。 在Chrome中也会隐藏JavaScript错误、快捷打开JavaScript错误控制台的方…...