首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 25-03-08 01:22
  • 2421
  • 11327
blog.csdn.net

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

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

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

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

  1. XComponent({
  2. id: '',
  3. type: XComponentType.SURFACE,
  4. libraryname: '',
  5. controller: this.XcomponentController
  6. })
  7. .onLoad(() => {
  8. this.XcomponentController.setXComponentSurfaceSize({
  9. surfaceWidth: this.cameraWidth, surfaceHeight: this.cameraHeight
  10. })
  11. this.XcomponentSurfaceId = this.XcomponentController.getXComponentSurfaceId()
  12. })

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

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

arkts 部分:

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

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

napi 部分:

  1. #include "RecorderNative.h"
  2. #include
  3. #undef LOG_DOMAIN
  4. #undef LOG_TAG
  5. #define LOG_DOMAIN 0xFF00
  6. #define LOG_TAG "recorder"
  7. struct AsyncCallbackInfo {
  8. napi_env env;
  9. napi_async_work asyncWork;
  10. napi_deferred deferred;
  11. int32_t resultCode = 0;
  12. std::string surfaceId = "";
  13. SampleInfo sampleInfo;
  14. };
  15. void DealCallBack(napi_env env, void *data)
  16. {
  17. AsyncCallbackInfo *asyncCallbackInfo = static_cast(data);
  18. napi_value code;
  19. napi_create_int32(env, asyncCallbackInfo->resultCode, &code);
  20. napi_value surfaceId;
  21. napi_create_string_utf8(env, asyncCallbackInfo->surfaceId.data(), NAPI_AUTO_LENGTH, &surfaceId);
  22. napi_value obj;
  23. napi_create_object(env, &obj);
  24. napi_set_named_property(env, obj, "code", code);
  25. napi_set_named_property(env, obj, "surfaceId", surfaceId);
  26. napi_resolve_deferred(asyncCallbackInfo->env, asyncCallbackInfo->deferred, obj);
  27. napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
  28. delete asyncCallbackInfo;
  29. }
  30. void SetCallBackResult(AsyncCallbackInfo *asyncCallbackInfo, int32_t code)
  31. {
  32. asyncCallbackInfo->resultCode = code;
  33. }
  34. void SurfaceIdCallBack(AsyncCallbackInfo *asyncCallbackInfo, std::string surfaceId)
  35. {
  36. asyncCallbackInfo->surfaceId = surfaceId;
  37. }
  38. void NativeInit(napi_env env, void *data)
  39. {
  40. AsyncCallbackInfo *asyncCallbackInfo = static_cast(data);
  41. int32_t ret = Recorder::GetInstance().Init(asyncCallbackInfo->sampleInfo);
  42. if (ret != AVCODEC_SAMPLE_ERR_OK) {
  43. SetCallBackResult(asyncCallbackInfo, -1);
  44. }
  45. uint64_t id = 0;
  46. ret = OH_NativeWindow_GetSurfaceId(asyncCallbackInfo->sampleInfo.window, &id);
  47. if (ret != AVCODEC_SAMPLE_ERR_OK) {
  48. SetCallBackResult(asyncCallbackInfo, -1);
  49. }
  50. asyncCallbackInfo->surfaceId = std::to_string(id);
  51. SurfaceIdCallBack(asyncCallbackInfo, asyncCallbackInfo->surfaceId);
  52. }
  53. napi_value RecorderNative::Init(napi_env env, napi_callback_info info)
  54. {
  55. SampleInfo sampleInfo;
  56. napi_value promise;
  57. napi_deferred deferred;
  58. napi_create_promise(env, &deferred, &promise);
  59. AsyncCallbackInfo *asyncCallbackInfo = new AsyncCallbackInfo();
  60. asyncCallbackInfo->env = env;
  61. asyncCallbackInfo->asyncWork = nullptr;
  62. asyncCallbackInfo->deferred = deferred;
  63. asyncCallbackInfo->resultCode = -1;
  64. asyncCallbackInfo->sampleInfo = sampleInfo;
  65. napi_value resourceName;
  66. napi_create_string_latin1(env, "recorder", NAPI_AUTO_LENGTH, &resourceName);
  67. napi_create_async_work(
  68. env, nullptr, resourceName, [](napi_env env, void *data) { NativeInit(env, data); },
  69. [](napi_env env, napi_status status, void *data) { DealCallBack(env, data); }, (void *)asyncCallbackInfo,
  70. &asyncCallbackInfo->asyncWork);
  71. napi_queue_async_work(env, asyncCallbackInfo->asyncWork);
  72. return promise;
  73. }
  74. EXTERN_C_START
  75. static napi_value Init(napi_env env, napi_value exports)
  76. {
  77. napi_property_descriptor classProp[] = {
  78. {"initNative", nullptr, RecorderNative::Init, nullptr, nullptr, nullptr, napi_default, nullptr}
  79. };
  80. return exports;
  81. }
  82. EXTERN_C_END
  83. static napi_module RecorderModule = {
  84. .nm_version = 1,
  85. .nm_flags = 0,
  86. .nm_filename = nullptr,
  87. .nm_register_func = Init,
  88. .nm_modname = "recorder",
  89. .nm_priv = ((void *)0),
  90. .reserved = {0},
  91. };
  92. extern "C" __attribute__((constructor)) void RegisterRecorderModule(void) { napi_module_register(&RecorderModule); }

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

c++ 编码器部分:

  1. int32_t Recorder::Init(SampleInfo &sampleInfo)
  2. {
  3. std::lock_guard lock(mutex_);
  4. sampleInfo_ = sampleInfo;
  5. videoEncoder_ = std::make_unique();
  6. muxer_ = std::make_unique();
  7. videoEncoder_->Create(sampleInfo_.videoCodecMime);
  8. ret = muxer_->Create(sampleInfo_.outputFd);
  9. encContext_ = new CodecUserData;
  10. videoEncoder_->Config(sampleInfo_, encContext_);
  11. muxer_->Config(sampleInfo_);
  12. sampleInfo.window = sampleInfo_.window;
  13. releaseThread_ = nullptr;
  14. return AVCODEC_SAMPLE_ERR_OK;
  15. }

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

代码如下:

  1. int32_t VideoEncoder::Config(SampleInfo &sampleInfo, CodecUserData *codecUserData)
  2. {
  3. Configure(sampleInfo);
  4. OH_VideoEncoder_GetSurface(encoder_, &sampleInfo.window);
  5. SetCallback(codecUserData);
  6. OH_VideoEncoder_Prepare(encoder_);
  7. return AVCODEC_SAMPLE_ERR_OK;
  8. }

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

  1. let cameraManager = camera.getCameraManager(globalThis.context)
  2. let camerasDevices: ArrayCameraDevice> = getCameraDevices(cameraManager)
  3. let profiles: camera.CameraOutputCapability = cameraManager.getSupportedOutputCapability(camerasDevices[0],
  4. camera.SceneMode.NORMAL_VIDEO)
  5. // 获取预览流profile
  6. let previewProfiles: ArrayProfile> = profiles.previewProfiles
  7. // 获取录像流profile
  8. let videoProfiles: ArrayVideoProfile> = profiles.videoProfiles
  9. // Xcomponent预览流
  10. let XComponentPreviewProfile: camera.Profile = previewProfiles[0]
  11. // 创建 编码器 输出对象
  12. encoderVideoOutput = cameraManager.createVideoOutput(videoProfile, encoderSurfaceId)
  13. // 创建 预览流 输出对象
  14. XcomponentPreviewOutput = cameraManager.createPreviewOutput(XComponentPreviewProfile, this.XcomponentSurfaceId)
  15. // 创建cameraInput对象
  16. cameraInput = cameraManager.createCameraInput(camerasDevices[0])
  17. // 打开相机
  18. await cameraInput.open()
  19. // 会话流程
  20. videoSession = cameraManager.createSession(camera.SceneMode.NORMAL_VIDEO) as camera.VideoSession
  21. // 开始配置会话
  22. videoSession.beginConfig()
  23. // 把CameraInput加入到会话
  24. videoSession.addInput(cameraInput)
  25. // 把 Xcomponent 预览流加入到会话
  26. videoSession.addOutput(XcomponentPreviewOutput)
  27. // 把编码器录像流加入到会话
  28. videoSession.addOutput(encoderVideoOutput)
  29. // 提交配置信息
  30. await videoSession.commitConfig()
  31. // 会话开始
  32. await videoSession.start()

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

 

 

注:本文转载自blog.csdn.net的yydounai的文章"https://blog.csdn.net/weixin_38189842/article/details/141402357"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

135
HarmonyOS
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top