查看: 342|回复: 3

OBS鸿蒙移植AVScreenCapture采屏零帧四层排障

[复制链接]
发表于 1 小时前 | 显示全部楼层 |阅读模式
背景:为什么 OBS 鸿蒙移植选择 AVScreenCapture 的 C 接口

OBS Studio 鸿蒙迁移系列进行到第五篇,图形后端已经立起来,下一块拼图是“画面从哪来”——屏幕采集。鸿蒙官方给录屏留了三条路,文档《录屏转码流(C/C++)》里自带对比。OBS 需要的是原始帧直接进渲染树、自己合成、自己编码,因此只有 NDK 的 AVScreenCapture C 接口满足要求,文档对该路径的适用描述里明确写着“多源合成及逐帧处理”。代价文档也写得很清楚:开发成本高,线程和内存都要自己管。实测下来还得补一条:错误码语义与文档存在出入。

本文按实际踩坑顺序,把配置合法性、设备形态交互、buffer 映射语义、像素语义这四层问题逐一拆开,并给出可复用的排查手法。

一、第一层:OPERATE_NOT_PERMIT 不等于权限问题

接入第一步就翻车:
  1. OH_AVScreenCapture_Init → err = 2 (AV_SCREEN_CAPTURE_ERR_OPERATE_NOT_PERMIT)
复制代码

字面意思是“操作不允许”,第一反应自然是权限。于是开始逐项核对:CUSTOM_SCREEN_RECORDING 在不在调试 profile 的 allowed-acls 里?在。弹窗授权点了没有?点了。系统录屏开关?正常。权限方向查了一小时,一无所获。

真相藏在 NDK 文档的角落里:音频通道只有在 audioSampleRate 和 channelCount 都为 0 时才会被忽略。而代码当时是这样写的:
  1. config.audioSampleRate = 48000;   // 原本想采音频
  2. config.channelCount = 2;            // 双声道
  3. config.audioSource = OH_SOURCE_INVALID; // 但音源无效
复制代码

只采视频的场景本不该碰音频字段,可一旦写了非零的音频参数,NDK 就会按“你要采音频”去校验配置自洽性:音源无效、配置矛盾,Init 直接失败,而它返回的错误码是“操作不允许”。

修法只有一行:视频-only 场景把 audioSampleRate 和 channelCount 都置 0,display 与 window 两个采集源要同样处理。这条经验值得所有鸿蒙 NDK 开发者记下来——OPERATE_NOT_PERMIT 类错误码经常“指权限不报权限”,第一排查方向应是配置合法性,而不是权限。

二、第二层:Init、Start、started 都正常,就是一帧不来

配置修好后,Init 成功、Start 成功、状态回调也报了 started,然后视频 buffer 回调一次都不触发,零帧。一度怀疑授权链路、线程模型、回调注册 API 用法。直到跑了一次 uitest dumpLayout 把屏幕控件树 dump 下来,才发现 OBS 窗口旁边藏着一个“选择共享内容”的 Picker 弹窗(有一部分被主窗口挡住),上面有个“开始共享”按钮。点掉它,帧立刻以 30fps 涌进来。

根因是这台 MatePad Edge 跑在 HarmonyOS PC/2in1 模式。该形态下 OH_CAPTURE_SPECIFIED_SCREEN 启动后,系统会弹出内容选择器让用户确认共享哪块内容,用户确认后才真正开始出帧。这是手机形态没有、平板默认形态也没有、文档几乎没提的交互——桌面形态的录屏语义向 Windows/macOS 看齐,共享前要求用户显式确认。设计本身合理,但对开发者是个隐藏状态机:Init/Start 全部成功,应用却在等一个可能被挡住的弹窗。

产品化含义很明确:这个 Picker 是必须向用户展示的交互流程,不是可以绕过的调试障碍。官方 FAQ 里还有一条相关的形态约束——通话/会议中未设置保持录屏策略时启动录屏,同样报 OPERATE_NOT_PERMIT,错误码复用再次出现。同一套 NDK API 在手机、平板、PC 三种形态下的交互模型并不一致,形态差异是文档最薄的地方,只能靠 dumpLayout 这类现场取证来补。

三、第三层:帧来了,像素全零

回调终于触发,拷贝出来的像素却全是零。首帧诊断打印如下:
  1. attr.size = 8294400  // 1920×1080×4,buffer 完整
  2. addr != NULL,但 tl=(0,0,0)、centre=(0,0,0)
复制代码

buffer 是完整的,地址也有效,读出来却是零,查下去是映射语义问题:OH_NativeBuffer_MapAndGetConfig 对 GPU-usage 的 buffer 返回空视图。采集服务给的帧 buffer 带 GPU 用途标记,NativeBuffer 的 CPU 映射路径对它不适用。

正解就躺在官方样例代码里:改用 OH_AVBuffer_GetAddr(buffer) 直接取址。换掉之后,中心像素采样变成 (36,38,43)——正是 OBS 深色窗口的背景色。采到的确实是真实屏幕,整条采集链路这时才算真正打通。教训是同一块 buffer 在 NativeBuffer 和 AVBuffer 两个入口的映射语义不同,拿不准时以官方样例代码路径为准,别按 API 名字直觉选型。

四、第四层:alpha 未定义,预乘混合下全透明

画面进来了,预览却像蒙了一层黑纱。查 libobs 的源混合模式是 (GL_ONE, GL_INVSRCCALPHA),即预乘 alpha;而采集服务给的 RGBA buffer 里 alpha 字节并不可靠,经常是 0。alpha=0 在预乘混合下就是全透明。修法是在拷贝时强制 alpha=0xFF:采集服务不承诺 alpha 语义,消费方就自己补齐语义。

五、顺带打通的两件事:系统内录与受限权限

系统音频内录方面,AVScreenCapture 支持 OH_SOURCE_PLAYBACK(PlaybackCapture)采集系统播放声。官方文档标注该 syscap 支持 Phone/Tablet/TV,PC/2in1 需要运行时探测;在 API 26 上实测探测为 true,成片带 aac 192kbps stereo 音轨,录屏文件用 ffprobe 检查音视频两条流俱在。桥接层把探测结果如实透给 UI(desktop loopback probe: syscap=yes native=yes),探测到什么就说什么,绝不假设可用。

权限方面,CUSTOM_SCREEN_RECORDING 属于 system_basic 级受限权限:调试 profile 里靠 ACL 白名单可以先跑通,正式上架需要走 AGC 的受限权限申请,申请材料与常见驳回原因需要在工程文档中单独整理。

六、采集链路的四层排障顺序

把上面的过程压缩成一份可复用的排查清单:

第一层看配置自洽:遇到 OPERATE_NOT_PERMIT,先检查配置字段是否合法,而不是先查权限;

第二层看形态交互:Init/Start 成功但零帧,先 dumpLayout 找到系统弹出的共享 Picker;

第三层看 buffer 语义:GPU 用途的 buffer 用 OH_AVBuffer_GetAddr 取址;

第四层看像素语义:alpha 未定义就补 0xFF,再排查其他环节。

链路打通后,在来源面板点“捕获屏幕”,走完 PC/2in1 的共享 Picker 确认,屏幕源即可挂上,预览里出现真实桌面。本文验证环境:HUAWEI MatePad Edge(PC/2in1 模式),HarmonyOS 7.0.0.109,API 26;OBS Studio 32.2.2,fork 分支 qingkouwei/obs-studio@harmonyos-port。
回复

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: OBS鸿蒙移植AVScreenCapture采屏零帧四层排障

这篇排障记录很有参考价值,尤其是把 OPERATE_NOT_PERMIT 先从配置自洽性入手排查这一点,很容易被错误码字面意思带偏。PC/2in1 形态下 Picker 弹窗导致零帧、NativeBuffer 与 AVBuffer 映射语义不同、以及 RGBA 的 alpha 需要消费方补成 0xFF,这几个坑都很具体,比单看接口文档更有现场感。 四层排查顺序总结得也很实用,建议后续把形态差异、系统内录的运行时探测、以及受限权限申请材料分别沉淀到工程文档里,团队里其他人复现时能少走不少弯路。感谢分享,期待看到采集链路稳定后的后续进展。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: OBS鸿蒙移植AVScreenCapture采屏零帧四层排障

看完这篇很有收获,四层排障的顺序感很强。最意外的是 OPERATE_NOT_PERMIT 居然要先查配置自洽,尤其视频-only 场景下 audioSampleRate 和 channelCount 必须置 0 这点,很容易被错误码带偏。PC/2in1 下 Picker 弹窗导致零帧也很隐蔽,用 dumpLayout 现场取证这个手法很实用。像素全零改用 OH_AVBuffer_GetAddr,以及拷贝时强制 alpha=0xFF 补齐语义,都是能直接复用的经验。感觉这套清单比按 API 文档逐项猜更靠谱,也提醒同形态差异和 buffer 映射语义不能想当然。感谢把踩坑过程写得这么细,后面如果整理受限权限申请和系统内录探测的工程文档,应该也很有价值。
回复 支持 反对

使用道具 举报

发表于 1 小时前 | 显示全部楼层

Re: OBS鸿蒙移植AVScreenCapture采屏零帧四层排障

这篇排障记录很有价值,尤其是把“错误码字面意思会误导”这件事讲透了。OPERATE_NOT_PERMIT 第一反应查权限,结果根因是音频参数没置零,这个坑对后续接 AVScreenCapture 的人应该很有提醒作用。四层顺序也总结得清楚:配置自洽、形态交互、buffer 映射、像素语义,基本覆盖了从 Init 到出帧再到能看的主干路径。 比较受启发的是 PC/2in1 下那个“选择共享内容”的 Picker,Init/Start 都成功但零帧,确实很容易往线程和回调上查,用 dumpLayout 现场取证这个手法很实用。另外 NativeBuffer 和 AVBuffer 映射语义不同、alpha 不承诺语义就由消费方补齐,这两条也很像工程里会反复踩的细节。系统音频探测结果如实透给 UI 这点也挺稳,避免把“可能可用”当成“一定可用”。 如果后续整理工程文档,感觉可以把这几项直接固化成检查清单:视频-only 时音频字段置零、PC/2in1 形态的 Picker 引导、采集 buffer 走 AVBuffer_GetAddr、拷贝时强制 alpha=0xFF、PlaybackCapture 的运行时探测结果透传,以及受限权限申请材料单独归档。感谢分享,收藏了。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

指导单位

江苏省公安厅

江苏省通信管理局

浙江省台州刑侦支队

DEFCON GROUP 86025

Hacking Group 021A

旗下站点

态势感知中心

应急响应中心

红盟安全

联系我们

官方QQ群:112851260

官方邮箱:security#ihonker.org(#改成@)

官方核心成员

关注微信公众号

Archiver|手机版|小黑屋| ( 沪ICP备2021026908号 )

GMT+8, 2026-11-2 01:19 , Processed in 0.038418 second(s), 28 queries , Gzip On.

Powered by ihonker.com

Copyright © 2015-现在.

  • 返回顶部