网站制作怎么学,产品设计招聘网站,做网站前端的软件,商城网站开发 多少钱鸿蒙 next 即将发布#xff0c;让我们先喊3遍 遥遥领先~ 遥遥领先~ 遥遥领先~
作为一门新的系统#xff0c;本人也是刚入门学习中#xff0c;如果对于一些理解有问题的#xff0c;欢迎即使指出哈
首先这里要讲一下#xff0c;在鸿蒙 next 中#xff0c;要实现摄像头预览…鸿蒙 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 recorderstruct 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_castAsyncCallbackInfo *(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_castAsyncCallbackInfo *(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_guardstd::mutex lock(mutex_);sampleInfo_ sampleInfo;videoEncoder_ std::make_uniqueVideoEncoder();muxer_ std::make_uniqueMuxer();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: Arraycamera.CameraDevice getCameraDevices(cameraManager)
let profiles: camera.CameraOutputCapability cameraManager.getSupportedOutputCapability(camerasDevices[0],camera.SceneMode.NORMAL_VIDEO)// 获取预览流profilelet previewProfiles: Arraycamera.Profile profiles.previewProfiles// 获取录像流profilelet videoProfiles: Arraycamera.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未完待续~