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

2024-08-22 00:20

本文主要是介绍鸿蒙 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 实现摄像头视频预览编码(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1094701

相关文章

使用Python实现IP地址和端口状态检测与监控

《使用Python实现IP地址和端口状态检测与监控》在网络运维和服务器管理中,IP地址和端口的可用性监控是保障业务连续性的基础需求,本文将带你用Python从零打造一个高可用IP监控系统,感兴趣的小伙... 目录概述:为什么需要IP监控系统使用步骤说明1. 环境准备2. 系统部署3. 核心功能配置系统效果展

解决IDEA报错:编码GBK的不可映射字符问题

《解决IDEA报错:编码GBK的不可映射字符问题》:本文主要介绍解决IDEA报错:编码GBK的不可映射字符问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录IDEA报错:编码GBK的不可映射字符终端软件问题描述原因分析解决方案方法1:将命令改为方法2:右下jav

Python实现微信自动锁定工具

《Python实现微信自动锁定工具》在数字化办公时代,微信已成为职场沟通的重要工具,但临时离开时忘记锁屏可能导致敏感信息泄露,下面我们就来看看如何使用Python打造一个微信自动锁定工具吧... 目录引言:当微信隐私遇到自动化守护效果展示核心功能全景图技术亮点深度解析1. 无操作检测引擎2. 微信路径智能获

Python中pywin32 常用窗口操作的实现

《Python中pywin32常用窗口操作的实现》本文主要介绍了Python中pywin32常用窗口操作的实现,pywin32主要的作用是供Python开发者快速调用WindowsAPI的一个... 目录获取窗口句柄获取最前端窗口句柄获取指定坐标处的窗口根据窗口的完整标题匹配获取句柄根据窗口的类别匹配获取句

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

Python位移操作和位运算的实现示例

《Python位移操作和位运算的实现示例》本文主要介绍了Python位移操作和位运算的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录1. 位移操作1.1 左移操作 (<<)1.2 右移操作 (>>)注意事项:2. 位运算2.1

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll

Spring Security自定义身份认证的实现方法

《SpringSecurity自定义身份认证的实现方法》:本文主要介绍SpringSecurity自定义身份认证的实现方法,下面对SpringSecurity的这三种自定义身份认证进行详细讲解,... 目录1.内存身份认证(1)创建配置类(2)验证内存身份认证2.JDBC身份认证(1)数据准备 (2)配置依