Harmony OS 如何实现 C++ NATIVE YUV420(其他数据格式如BGRA等)自渲染

在HarmonyOS下自渲染视频数据

在本文中,我们将介绍如何在HarmonyOS下自渲染视频数据。我们将实现包括创建本地窗口、设置缓冲区选项、请求缓冲区、处理视频帧数据以及刷新缓冲区等步骤。

环境准备

在开始之前,请确保您已经安装了HarmonyOS的开发环境,并且能够编译和运行C++代码。

注意⚠️

以下方案中用到的API,都不是线程安全的!!!我这里是通过其他手段有保护的,请自行上锁~

创建本地窗口

首先,我们需要根据surfaceId创建一个本地窗口。如何拿这个surfaceId,有两种方案:

  • 一种是原生ArkTs UI开发从XComponent拿;
  • 一种是跨平台框架比如 flutter通过TextureRegistry注册一个Texture拿,

NativeWindowRender 类的构造函数中实现了这一功能:

NativeWindowRender::NativeWindowRender(const RenderConfig &config) : render_config_(config) {int ret = OH_NativeWindow_CreateNativeWindowFromSurfaceId(config.surfaceId, &window_);if (window_ == nullptr || ret != 0) {LOG_ERROR("create native window failed: {}", ret);return;}
}

在这里,我们使用

OH_NativeWindow_CreateNativeWindowFromSurfaceId

函数从surfaceId创建一个本地窗口。如果创建失败,会记录错误日志。

设置缓冲区选项

创建本地窗口后,我们需要设置一些缓冲区选项,例如缓冲区使用情况、交换间隔、请求超时、颜色范围和变换等可以参考 NativeWindowOperation,我暂时只设置了这些,看看渲染效果和性能

bool NativeWindowRender::UpdateNativeBufferOptionsByVideoFrame(const VideoFrame *frame) {
#if 0// get current buffer geometry, if different, reset buffer geometryint32_t stride = 0;int32_t height = 0;// the fucking order of get is h, w, however, the fucking order of set is w, hint ret = OH_NativeWindow_NativeWindowHandleOpt(window_, GET_BUFFER_GEOMETRY, &height, &stride);if (ret != 0) {LOG_ERROR("get buffer geometry failed: {}", ret);return false;}// set buffer geometry if differentif (stride != frame->yStride || height != frame->height) {ret = OH_NativeWindow_NativeWindowHandleOpt(window_, SET_BUFFER_GEOMETRY, frame->yStride, frame->height);if (ret != 0) {LOG_ERROR("set buffer geometry failed: {}", ret);return false;}}
#elseint ret = OH_NativeWindow_NativeWindowHandleOpt(window_, SET_BUFFER_GEOMETRY, frame->yStride, frame->height);if (ret != 0) {LOG_ERROR("set buffer geometry failed: {}", ret);return false;}
#endif// set buffer formatret = OH_NativeWindow_NativeWindowHandleOpt(window_, SET_FORMAT, NATIVEBUFFER_PIXEL_FMT_YCBCR_420_P);if (ret != 0) {LOG_ERROR("set buffer format failed: {}", ret);return false;}// set buffer strideret = OH_NativeWindow_NativeWindowHandleOpt(window_, SET_STRIDE, 4);if (ret != 0) {LOG_ERROR("set buffer stride failed: {}", ret);return false;}// set native source type to OH_SURFACE_SOURCE_VIDEOret = OH_NativeWindow_NativeWindowHandleOpt(window_, SET_SOURCE_TYPE, OH_SURFACE_SOURCE_VIDEO);if (ret != 0) {LOG_ERROR("set source type failed: {}", ret);return false;}// set app framework typeret = OH_NativeWindow_NativeWindowHandleOpt(window_, SET_APP_FRAMEWORK_TYPE, "unknown");if (ret != 0) {LOG_ERROR("set app framework type failed: {}", ret);return false;}return true;
}

请求缓冲区并处理视频帧数据

在接收到视频帧数据时,我们需要请求缓冲区并将视频帧数据写入缓冲区, 坑点来了,UpdateNativeBufferOptionsByVideoFrame 你必须每次request前都要调用。。。。我反正没找到文档哪里有写,又或者我理解能力有问题,坑了一两个小时,最后lldb debug,发现如果只在构造函数中设置的话,只有第一次request出来的BufferHandle中的width height stride format等参数是符合预期的,后面就不对了,结果就只能画出来第一张图,后面没有任何报错但是就是不出图,所以切记切记, 每次都要调用 (吗?请帮忙指正)

void NativeWindowRender::OnVideoFrameReceived(const void *videoFrame, const VideoFrameConfig &config, bool resize) {const VideoFrame *frame = reinterpret_cast<const VideoFrame *>(videoFrame);// must call this every time, coz every time you request a buffer, the properties of BufferHandle may change to// default value.if (!UpdateNativeBufferOptionsByVideoFrame(frame)) {return;}// request buffer from native windowOHNativeWindowBuffer *buffer = nullptr;int fence_fd = -1;int ret = OH_NativeWindow_NativeWindowRequestBuffer(window_, &buffer, &fence_fd);if (ret != 0 || buffer == nullptr) {LOG_ERROR("request buffer failed: {}", ret);return;}// get buffer handle from native bufferBufferHandle *handle = OH_NativeWindow_GetBufferHandleFromNative(buffer);// mmap buffer handle to write datavoid *data = mmap(handle->virAddr, handle->size, PROT_READ | PROT_WRITE, MAP_SHARED, handle->fd, 0);if (data == MAP_FAILED) {LOG_ERROR("mmap buffer failed");return;}// wait for fence fd to be signaleduint32_t timeout = 3000;if (fence_fd != -1) {struct pollfd fds = {.fd = fence_fd, .events = POLLIN};do {ret = poll(&fds, 1, timeout);} while (ret == -1 && (errno == EINTR || errno == EAGAIN));close(fence_fd);}// copy yuv420 data to bufferuint8_t *y = (uint8_t *)data;uint8_t *u = y + frame->yStride * frame->height;uint8_t *v = u + frame->uStride * frame->height / 2;memcpy(y, frame->yBuffer, frame->yStride * frame->height);memcpy(u, frame->uBuffer, frame->uStride * frame->height / 2);memcpy(v, frame->vBuffer, frame->vStride * frame->height / 2);// flush bufferRegion region{.rects = nullptr, .rectNumber = 0};int acquire_fence_fd = -1;ret = OH_NativeWindow_NativeWindowFlushBuffer(window_, buffer, acquire_fence_fd, region);if (ret != 0) {LOG_ERROR("flush buffer failed: {}", ret);}// unmap buffer handleret = munmap(data, handle->size);if (ret != 0) {LOG_ERROR("munmap buffer failed: {}", ret);}
}

在这里,我们首先调用 UpdateNativeBufferOptionsByVideoFrame 函数更新缓冲区选项,然后请求缓冲区并将视频帧数据写入缓冲区。最后,我们刷新缓冲区并解除映射。

结论

通过以上步骤,我们可以在HarmonyOS下实现自渲染视频数据。希望本文对您有所帮助。如果您有任何问题或建议,请随时与我联系。

PS

另外一种方案,是拿到NativeWindow后,用opengl自己画,但是opengl这个鬼你也知道的,一个context要一个线程,我要是十几路,几十路视频渲染,就得几十个线程?这在移动平台谁能忍?共享context的话,我自信我这种new opengler不能保证不出错。。。可以参考官方文档 自定义渲染 (XComponent) 。所以,这种方案交给鸿蒙UI框架说着说系统自己画的,叫懒人方案?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/884958.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【大数据学习 | kafka高级部分】kafka的快速读写

1. 追加写 根据以上的部分我们发现存储的方式比较有规划是对于后续查询非常便捷的&#xff0c;但是这样存储是不是会更加消耗存储性能呢&#xff1f; 其实kafka的数据存储是追加形式的&#xff0c;也就是数据在存储到文件中的时候是以追加方式拼接到文件末尾的&#xff0c;这…

【超级详细】基于Zynq FPGA对雷龙SD NAND的测试

目录 一、SD NAND特征1.1 SD卡简介1.2 SD卡Block图 二、SD卡样片三、Zynq测试平台搭建3.1 测试流程3.2 SOC搭建 一、SD NAND特征 1.1 SD卡简介 雷龙的SD NAND有很多型号&#xff0c;在测试中使用的是CSNP4GCR01-AMW与CSNP32GCR01-AOW。芯片是基于NAND FLASH和 SD控制器实现的…

[357]基于springboot的中小型制造企业质量管理系统

摘 要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自古…

数据结构:跳表实现(C++)

个人主页 &#xff1a; 个人主页 个人专栏 &#xff1a; 《数据结构》 《C语言》《C》《Linux》《网络》 《redis学习笔记》 文章目录 前言跳表跳表的优化思路skiplist&#xff0c;平衡搜索树&#xff0c;哈希表的对比 实现思路SkiplistNodesearch 搜索add 增加earse 删除 整体…

Rancher的安装

1. 概览 1.1 用户界面优势 Rancher 提供了一个直观的图形用户界面&#xff08;GUI&#xff09;。对于不熟悉 Kubernetes 复杂的命令行操作&#xff08;如使用kubectl&#xff09;的用户来说&#xff0c;通过 Rancher 的界面可以方便地进行资源管理。例如&#xff0c;用户可以在…

文件上传和下载

目录 一、准备工作 二、文件上传 三、文件下载 一、准备工作 如果想使用Spring的文件上传功能&#xff0c;则需要再上下文中配置MultipartResolver前端表单要求&#xff1a;为了能上传文件&#xff0c;必须将表单的method设置为post&#xff0c;并将enctype设置为multipart…

Docker 镜像拉不动?自建 Docker Hub 加速站 解决镜像拉取失败

本文首发于只抄博客&#xff0c;欢迎点击原文链接了解更多内容。 前言 众所周知&#xff0c;6 月份的时候&#xff0c;Docker Hub 的镜像就已经无法正常拉取&#xff0c;那会随手用 Nginx 反代了一下 Docker Hub&#xff0c;建了个自用的镜像站&#xff0c;一直用到了 9 月份&…

真·香!深度体验 zCloud 数据库云管平台 -- DBA日常管理篇

点击蓝字 关注我们 zCloud 作为一款业界领先的数据库云管平台&#xff0c;通过云化自治的部署能力、智能巡检和诊断能力、知识即代码的沉淀能力&#xff0c;为DBA的日常管理工作带来了革新式的简化与优化。经过一周的深度体验&#xff0c;今天笔者与您深入探讨 zCloud 在数据库…

Qt的程序如何打包详细教学

生成Release版的程序 在打包Qt程序时&#xff0c;我们需要将发布程序需要切换为Release版本&#xff08;Debug为调试版本&#xff09;&#xff0c;编译器会对生成的Release版可执行程序进行优化&#xff0c;使生成的可执行程序会更小。 debug版本 debug版本是一种开发过程中的…

适配器模式:类适配器与对象适配器

适配器模式是一种结构性设计模式&#xff0c;旨在将一个接口转换成客户端所期望的另一种接口。它通常用于解决由于接口不兼容而导致的类之间的通信问题。适配器模式主要有两种实现方式&#xff1a;类适配器和对象适配器。下面&#xff0c;我们将详细探讨这两种方式的优缺点及适…

语音识别:docker部署FunASR以及springboot集成funasr

内容摘选自: https://github.com/modelscope/FunASR/blob/main/runtime/docs/SDK_advanced_guide_offline_zh.md FunASR FunASR是一个基础语音识别工具包&#xff0c;提供多种功能&#xff0c;包括语音识别&#xff08;ASR&#xff09;、语音端点检测&#xff08;VAD&#xf…

oracle-函数-NULLIF (expr1, expr2)的妙用

【语法】NULLIF (expr1, expr2) 【功能】expr1和expr2相等返回NULL&#xff0c;不相等返回expr1经典的使用场景&#xff1a; 1. 数据清洗与转换 在数据清洗过程中&#xff0c;NULLIF 函数可以用于将某些特定值&#xff08;通常是无效或不需要的值&#xff09;替换为 NULL&…

【LLM】Agentic Workflow的四种常见思路

note Reflection 和 Tool Use 属于比较经典且相对已经广泛使用的方式&#xff0c;Planning 和 Multi-agent 属于比较新颖比较有前景的方式。 文章目录 note一、四种设计模式1. Reflection2. Tool use3. Planning4. Multi-agent collaboration 二、相关代码实践 一、四种设计模…

Python数据可视化seaborn

产品经理在做数据分析时可能需要通过可视化来分析。seaborn官网 1. relplot 散点图 https://seaborn.pydata.org/examples/scatterplot_sizes.html import pandas as pd import seaborn as sns df pd.DataFrame({x: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],y: [8, 6, 7, 8, 4, 6,…

基于ssm的个人健康管理系统

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

CSS3新增渐变(线性渐变、径向渐变、重复渐变)

1.线性渐变 代码&#xff1a; 效果图&#xff1a; 使文字填充背景颜色&#xff1a; 效果图&#xff1a; 2.径向渐变 代码&#xff1a; 效果图&#xff1a; 代码图&#xff1a; 效果图&#xff1a; 3.重复渐变 代码&#xff1a; 效果图&#xff1a;

[mysql]mysql的DML数据操作语言增删改,以及新特性计算列,阿里巴巴开发手册mysql相关

1DML数据操作语言,增加删除改数据 插入数据INSERT 插入添加数据,两种方法 方式1:VALUES添加数据 #准备工作 USE atguigudb; CREATE TABLE IF NOT EXISTS emp1( id INT, name VARCHAR(15), hire_data DATE, salary DOUBLE(10,2)); SELECT * FROM emp1 INSERT INTO em…

自由学习记录(19)

unity核心也算是看完了吧&#xff0c;但觉得的确是少了点东西&#xff0c;之后再看mvc框架&#xff0c;和网络开发&#xff0c;&#xff0c;感觉有必要想想主次顺序了&#xff0c;毕竟在明年的3月之前尽量让自己更有贴合需求的能力 先了解一些相关概念&#xff0c;不用看懂&am…

vue计算属性

概念&#xff1a;基于现有的数据&#xff0c;计算出来新属性。并依赖数据的变化&#xff0c;自动重新计算 使用场景&#xff1a; 语法&#xff1a;声明在computed配置项中&#xff0c;一个计算属性对应一个函数&#xff0c;使用起来和普通属性一样使用{{计算属性名}} 代码&…

springboot2.x使用SSE方式代理或者转发其他流式接口

文章目录 1.需求描述2.代码2.1.示例controller2.2.示例service2.3.示例impl 3.测试 1.需求描述 使用SSE的方式主要还是要跟前端建立一个EventSource的链接&#xff0c;有了这个连接&#xff0c;然后往通道里写入数据流&#xff0c;前端自然会拿到流式数据&#xff0c;写啥拿啥…