通过 MQDescriptorSync 实现 HIDL 大数据传递的最佳实践

以下内容来自 Audio HIDL 播放流程,经过了部分修改,但尚未经过测试。

HIDL

    struct WriteStatus {Result retval;union Reply {uint64_t written;  // WRITE command, amount of bytes written, >= 0.} reply;};prepareWriting(uint32_t frameSize, uint32_t framesCount)generates (Result retval, fmq_sync<uint8_t> dataMQ, fmq_sync<WriteStatus> statusMQ);

接收端

接收端需要创建 Data MessageQueue和 Status MessageQueue,并将它们通过回调传递给发送端。

typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
typedef MessageQueue<WriteStatus, kSynchronizedReadWrite> StatusMQ;
class WriteThread : public Thread {public:// WriteThread's lifespan never exceeds Device's lifespan.WriteThread(audio_hw_device_t* device, StreamOut::DataMQ* dataMQ,StreamOut::StatusMQ* statusMQ, EventFlag* efGroup): Thread(false /*canCallJava*/),mDevice(device),mDataMQ(dataMQ),mStatusMQ(statusMQ),mEfGroup(efGroup),mBuffer(nullptr) {}bool init() {mBuffer.reset(new (std::nothrow) uint8_t[mDataMQ->getQuantumCount()]);return mBuffer != nullptr;}virtual ~WriteThread() {}private:audio_hw_device_t* mDevice;StreamOut::DataMQ* mDataMQ;StreamOut::StatusMQ* mStatusMQ;EventFlag* mEfGroup;std::unique_ptr<uint8_t[]> mBuffer;IDevice::WriteStatus mStatus;bool threadLoop() override;void doWrite();
};void WriteThread::doWrite() {const size_t availToRead = mDataMQ->availableToRead();mStatus.retval = Result::OK;mStatus.reply.written = 0;if (mDataMQ->read(&mBuffer[0], availToRead)) {ssize_t writeResult = mDevice->virtualMicWrite(&mBuffer[0], availToRead);if (writeResult >= 0) {mStatus.reply.written = writeResult;} else {mStatus.retval = Device::analyzeStatus("virtualMicWrite", writeResult);}}
}bool WriteThread::threadLoop() {// This implementation doesn't return control back to the Thread until it// decides to stop,// as the Thread uses mutexes, and this can lead to priority inversion.while (true) {uint32_t efState = 0;mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY), &efState);if (!(efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY))) {continue;  // Nothing to do.}doWrite();if (!mStatusMQ->write(&mStatus)) {ALOGE("status message queue write failed");}mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));}return false;
}Return<void> Device::prepareForWriting(uint32_t frameSize, uint32_t framesCount,prepareForWriting_cb _hidl_cb) {status_t status;// Wrap the _hidl_cb to return an errorauto sendError = [&_hidl_cb] (Result result) {_hidl_cb(result, DataMQ::Descriptor(), StatusMQ::Descriptor()); };if (mDataMQ) {ALOGE("the client attempts to call prepareForWriting twice");sendError(Result::INVALID_STATE);return Void();}// Check frameSize and framesCountif (frameSize == 0 || framesCount == 0) {ALOGE("Null frameSize (%u) or framesCount (%u)", frameSize, framesCount);sendError(Result::INVALID_ARGUMENTS);return Void();}if (frameSize > Stream::MAX_BUFFER_SIZE / framesCount) {ALOGE("Buffer too big: %u*%u bytes > MAX_BUFFER_SIZE (%u)", frameSize, framesCount,Stream::MAX_BUFFER_SIZE);sendError(Result::INVALID_ARGUMENTS);return Void();}std::unique_ptr<DataMQ> tempDataMQ(new DataMQ(frameSize * framesCount, true /* EventFlag */));std::unique_ptr<StatusMQ> tempStatusMQ(new StatusMQ(1));if (!tempDataMQ->isValid() || !tempStatusMQ->isValid()) {ALOGE_IF(!tempDataMQ->isValid(), "data MQ is invalid");ALOGE_IF(!tempStatusMQ->isValid(), "status MQ is invalid");sendError(Result::INVALID_ARGUMENTS);return Void();}EventFlag* tempRawEfGroup{};status = EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &tempRawEfGroup);std::unique_ptr<EventFlag, void (*)(EventFlag*)> tempElfGroup(tempRawEfGroup, [](auto* ef) { EventFlag::deleteEventFlag(&ef); });if (status != OK || !tempElfGroup) {ALOGE("failed creating event flag for data MQ: %s", strerror(-status));sendError(Result::INVALID_ARGUMENTS);return Void();}// Create and launch the thread.auto tempWriteThread = sp<WriteThread>::make(mDevice, tempDataMQ.get(), tempStatusMQ.get(), tempElfGroup.get());if (!tempWriteThread->init()) {ALOGW("failed to start writer thread: %s", strerror(-status));sendError(Result::INVALID_ARGUMENTS);return Void();}status = tempWriteThread->run("writer", PRIORITY_URGENT_AUDIO);if (status != OK) {ALOGW("failed to start writer thread: %s", strerror(-status));sendError(Result::INVALID_ARGUMENTS);return Void();}mDataMQ = std::move(tempDataMQ);mStatusMQ = std::move(tempStatusMQ);mWriteThread = tempWriteThread;mEfGroup = tempElfGroup.release();_hidl_cb(Result::OK, *mDataMQ->getDesc(), *mStatusMQ->getDesc());return Void();
}

发送端

这是 BpBinder 的部分回调代码,返回的是 MQDescriptorSync 对象。

const ::android::hardware::MQDescriptorSync<uint8_t>* _hidl_out_dataMQ;
const ::android::hardware::MQDescriptorSync<::android::hardware::audio::V7_0::IStreamOut::WriteStatus>* _hidl_out_statusMQ;size_t _hidl__hidl_out_dataMQ_parent;
_hidl_err = _hidl_reply.readBuffer(sizeof(*_hidl_out_dataMQ), &_hidl__hidl_out_dataMQ_parent,  reinterpret_cast<const void **>(&_hidl_out_dataMQ));
if (_hidl_err != ::android::OK) { return; }_hidl_err = ::android::hardware::readEmbeddedFromParcel(const_cast<::android::hardware::MQDescriptorSync<uint8_t> &>(*_hidl_out_dataMQ), _hidl_reply, _hidl__hidl_out_dataMQ_parent, 0 /* parentOffset */);
if (_hidl_err != ::android::OK) { return; }size_t _hidl__hidl_out_statusMQ_parent;
_hidl_err = _hidl_reply.readBuffer(sizeof(*_hidl_out_statusMQ), &_hidl__hidl_out_statusMQ_parent,  reinterpret_cast<const void **>(&_hidl_out_statusMQ));
if (_hidl_err != ::android::OK) { return; }_hidl_err = ::android::hardware::readEmbeddedFromParcel(const_cast<::android::hardware::MQDescriptorSync<::android::hardware::audio::V7_0::IStreamOut::WriteStatus> &>(*_hidl_out_statusMQ), _hidl_reply, _hidl__hidl_out_statusMQ_parent, 0 /* parentOffset */);
if (_hidl_err != ::android::OK) { return; }
status_t DeviceHalHidl::virtualMicWrite(const void *buffer, size_t bytes, size_t *written) {// TIME_CHECK();  // TODO(b/243839867) reenable only when optimized.if (mDevice == 0) return NO_INIT;*written = 0;if (bytes == 0 && !mDataMQ) {// Can't determine the size for the MQ buffer. Wait for a non-empty write request.ALOGW("First call to async write with 0 bytes");return OK;}status_t status;if (!mDataMQ) {// In case if playback starts close to the end of a compressed track, the bytes// that need to be written is less than the actual buffer size. Need to use// full buffer size for the MQ since otherwise after seeking back to the middle// data will be truncated.if ((status = prepareForWriting(bufferSize)) != OK) {return status;}}status = callWriterThread("write", static_cast<const uint8_t*>(buffer), bytes,[&] (const WriteStatus& writeStatus) {*written = writeStatus.reply.written;// Diagnostics of the cause of b/35813113.ALOGE_IF(*written > bytes,"hal reports more bytes written than asked for: %lld > %lld",(long long)*written, (long long)bytes);});return status;
}status_t DeviceHalHidl::callWriterThread(const char* cmdName,const uint8_t* data, size_t dataSize, StreamOutHalHidl::WriterCallback callback) {if (data != nullptr) {size_t availableToWrite = mDataMQ->availableToWrite();if (dataSize > availableToWrite) {ALOGW("truncating write data from %lld to %lld due to insufficient data queue space",(long long)dataSize, (long long)availableToWrite);dataSize = availableToWrite;}if (!mDataMQ->write(data, dataSize)) {ALOGE("data message queue write failed for \"%s\"", cmdName);}}mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));// TODO: Remove manual event flag handling once blocking MQ is implemented. b/33815422uint32_t efState = 0;
retry:status_t ret = mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL), &efState);if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL)) {WriteStatus writeStatus;writeStatus.retval = Result::NOT_INITIALIZED;if (!mStatusMQ->read(&writeStatus)) {ALOGE("status message read failed for \"%s\"", cmdName);}if (writeStatus.retval == Result::OK) {ret = OK;callback(writeStatus);} else {ret = processReturn(cmdName, writeStatus.retval);}return ret;}if (ret == -EAGAIN || ret == -EINTR) {// Spurious wakeup. This normally retries no more than once.goto retry;}return ret;
}status_t DeviceHalHidl::prepareForWriting(size_t bufferSize) {std::unique_ptr<DataMQ> tempDataMQ;std::unique_ptr<StatusMQ> tempStatusMQ;Result retval;Return<void> ret = mDevice->prepareForWriting(1, bufferSize,[&](Result r, const DataMQ::Descriptor& dataMQ, const StatusMQ::Descriptor& statusMQ) {retval = r;if (retval == Result::OK) {tempDataMQ.reset(new DataMQ(dataMQ));tempStatusMQ.reset(new StatusMQ(statusMQ));if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) {EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup);}}});if (!ret.isOk() || retval != Result::OK) {return processReturn("prepareForWriting", ret, retval);}if (!tempDataMQ || !tempDataMQ->isValid() ||!tempStatusMQ || !tempStatusMQ->isValid() || !mEfGroup) {ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for writing");ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "Data message queue for writing is invalid");ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for writing");ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(),"Status message queue for writing is invalid");ALOGE_IF(!mEfGroup, "Event flag creation for writing failed");return NO_INIT;}mDataMQ = std::move(tempDataMQ);mStatusMQ = std::move(tempStatusMQ);return OK;
}

https://source.android.google.cn/docs/core/architecture/hidl/fmq?hl=zh-cn

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

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

相关文章

云栖3天,云原生+ AI 多场联动,新产品、新体验、新探索

云栖3天&#xff0c;云原生 AI 20场主题分享&#xff0c;三展互动&#xff0c;为开发者带来全新视听盛宴 2024.9.19-9.21 云栖大会 即将上演“云原生AI”的全球盛会 展现最新的云计算技术发展与 AI技术融合之下的 “新探索” 一起来云栖小镇 见证3天的云原生AI 前沿探索…

时间序列数据可视化

#时间序列可视化 #离散数据的时间序列可视化 import numpy as np import pandas as pdts pd.Series(np.random.randn(1000), indexpd.date_range(1/1/2000, periods1000)) ts ts.cumsum() ts.plot() #%% #连续数据的时间序列可视化 import matplotlib.pyplot as plt df pd.D…

Ubuntu下使用 python搭建服务实现从web端远程配置设备网口

1、通过文件配置Ubuntu设备网口 在Ubuntu工控机上&#xff0c;通过文件配置网口&#xff08;网络接口&#xff09;可以让网络配置在每次系统启动时自动生效。以下是常见的方法步骤&#xff1a; 1.1 使用 netplan 配置网口&#xff08;Ubuntu 18.04 及以上版本&#xff09; 编…

Vue学习记录之六(组件实战及BEM框架了解)

一、BEM BEM是一种前端开发中常用的命名约定&#xff0c;主要用于CSS和HTML的结构化和模块化。BEM是Block、Element、Modifier的缩写。 Block&#xff08;块&#xff09;&#xff1a;独立的功能性页面组件&#xff0c;可以是一个简单的按钮&#xff0c;一个复杂的导航条&…

【Python 数据分析学习】Matplotlib 的基础和应用

题目 1 Matplotlib 主要特性2 Matplotlib 基础知识2.1 导入模块2.2 图形构成2.2.1 图形&#xff08;Figure&#xff09;2.2.2 轴 &#xff08;Axes&#xff09;2.2.3 轴线&#xff08;axis&#xff09; 2.5 中文设置2.5.1 借助rcParams修改字体实现设置2.5.2 增加一个fontprope…

基于PHP+MySQL组合开发地方门户分类信息网站源码系统 带完整的安装代码包以及搭建部署教程

系统概述 随着互联网技术的飞速发展&#xff0c;地方门户分类信息网站逐渐成为城市生活不可或缺的一部分。它们涵盖了房产、招聘、二手交易、生活服务等多个领域&#xff0c;为当地居民提供了全方位的信息服务。为了满足这一市场需求&#xff0c;我们开发了这款基于PHPMySQL的…

uniapp监听滚动实现顶部透明度变化

效果如图&#xff1a; 实现思路&#xff1a; 1、使用onPageScroll监听页面滚动&#xff0c;改变导航条的透明度&#xff1b; 2、关于顶部图片的高度&#xff1a; 如果是小程序&#xff1a;使用getMenuButtonBoundingClientRect获取胶囊顶部距离和胶囊高度&#xff1b; 如果…

如何利用 Kafka,实时挖掘企业数据的价值?

首先&#xff0c;问读者老爷们一个简单的问题&#xff0c;如果你需要为你的数据选择一个同时具备高吞吐 、数据持久化、可扩展的数据传递系统&#xff0c;你会选择什么样的工具或架构呢&#xff1f; 答案非常显而易见&#xff0c;那就是 Kafka&#xff0c;不妨再次套用一个被反…

使用Java基于GeoTools读取Shapefile矢量数据属性信息-以某市POI数据为例

前言 在之前的博客中&#xff0c;我们讲过在GDAL中如何读取空间数据的属性和数据信息&#xff0c;也简单的讲过如何在GeoTools中读取Shapefile文件的属性信息和数据信息。对于空间矢量数据库&#xff0c;就像我们传统的二维数据库的表字段和表数据的关系&#xff0c;在研究表数…

14 vue3之内置组件trastion全系列

前置知识 Vue 提供了 transition 的封装组件&#xff0c;在下列情形中&#xff0c;可以给任何元素和组件添加进入/离开过渡: 条件渲染 (使用 v-if)条件展示 (使用 v-show)动态组件组件根节点 自定义 transition 过度效果&#xff0c;你需要对transition组件的name属性自定义。…

jupyter安装与使用——Ubuntu服务器

jupyter安装与使用——Ubuntu服务器 一、安装miniconda3/anaconda31. 下载miniconda32. 安装miniconda33. 切换到bin文件夹4. 输入pwd获取路径5. 打开用户环境编辑页面6. 重新加载用户环境变量7. 初始化conda8.验证是否安装成功9.conda配置 二、安装jupyter2.1 conda安装2.2 配…

国货之光|暴雨携信创新品亮相第八届丝博会

9月20日&#xff0c;第八届丝绸之路国际博览会暨中国东西部合作与投资贸易洽谈会&#xff08;以下简称“丝博会”&#xff09;在西安举行。 本届丝博会以“深化互联互通拓展经贸合作”为主题&#xff0c;会期为9月20日至24日&#xff0c;在西安国际会展中心设置国际交流展、省际…

研一奖学金计划2024/9/23有感

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、需要认真上课的1.应用数理统计&#xff08;开卷考试&#xff09;2.最优化方法&#xff08;开卷考试&#xff09;3.跨文化交际&#xff08;主题演讲20课堂讨…

[系统设计总结] - Proximity Service算法介绍

问题描述 Proximity Service广泛应用于各种地图相关的服务中比如外卖&#xff0c;大众点评&#xff0c;Uber打车&#xff0c;Google地图中&#xff0c;其中比较关键的是我们根据用户的位置来快速找到附近的餐厅&#xff0c;司机&#xff0c;外卖员也就是就近查询算法。 主流的…

小程序面板开发教程|开发照明 Matter 面板步骤(一)

一. 前置知识 前言 出于对 Matter 标准协议及第三方设备接入的可拓展性等方面考虑&#xff0c;照明 Matter 模型面板的功能点定义会与照明的 DP 模型有所不同&#xff0c;因此本文会着重介绍照明 Matter 面板的功能点定义及与 DP 模型的区别&#xff0c;以方便面板小程序开发…

Qt-QLabel 添加图片并设置 GIF 图动态效果

Qt-QLabel 添加图片并设置 GIF 图动态效果 一、添加图片资源并设置图片 选择标签&#xff0c;拖拉到界面上&#xff0c;然后选择器属性 picmap   选择设置&#xff0c;在这里添加图片资源   点击左边的加号符号按钮添加前缀&#xff0c;并设置前缀名&#xff0c;如果已经…

uniapp+renderJS+google map开发安卓版APP非小程序

背景需求 需要在uniapp中接入google地图,研究了一番,都没有找到合适的,现在说一下教程。 效果图 前期工作 这两点缺一不可,否则你啥也看不到。 1、电脑安装L-O-U梯 用于访问G-OO-G-LE的API或者创建google map key。 2、手机安装L-O-U梯 用于显示google地图。我就是手…

数据篇| 关于Selenium反爬杂谈

友情提示:本章节只做相关技术讨论, 爬虫触犯法律责任与作者无关。 LLM虽然如火如荼进行着, 但是没有数据支撑, 都是纸上谈兵, 人工智能的三辆马车:算法-数据-算力,缺一不可。之前写过关于LLM微调文章《微调入门篇:大模型微调的理论学习》、《微调实操一: 增量预训练(Pretrai…

USB 电缆中的信号线 DP、DM 的缩写由来

经常在一些芯片的规格书中看到 USB 的信号对是以 DP 和 DM 命名&#xff1a; 我在想&#xff0c;这些规格书是不是写错了&#xff0c;把 N 写成 M 了&#xff1f;DM 中的 M 到底是什么的缩写&#xff1f; 于是我找了一些资料&#xff0c;终于在《Universal Serial Bus Cables …

xilinx hbm ip运用

AXI-HBM是一个集成的IP核&#xff0c;该核提供高达16个AXI3从PORT的HBM接口&#xff0c;每个使用他自己的独立的时钟。HBM2 GEN存储器也支持&#xff0c;HBM相对传统DDR的方案&#xff0c;带宽得到极大的提高 特征 AXI3从端口存储器接口 -16个独立的256bit存储器接口 -可选的…