屏幕录制:调用系统录屏能力录制桌面内容(92)
在鸿蒙(HarmonyOS)应用开发中,调用系统录屏能力主要依赖于 Media Kit 中的 AVScreenCaptureRecorder 模块。该模块支持 ArkTS 和 Native (C/C++) 两种开发语言,能够完成从全屏录制、窗口级录制到获取原始音视频码流等多种复杂场景。
一、 ArkTS 基础录屏:初始化配置与文件写入
使用 ArkTS 进行录屏时,核心流程包括创建 AVScreenCaptureRecorder 实例、配置音视频参数(如指定文件描述符 fd)、以及控制录屏的开始与停止。
核心代码示例:
import media from '@ohos.multimedia.media';
import fs from '@ohos.file.fs';
import { common } from '@kit.AbilityKit';
private avScreenCaptureRecorder: media.AVScreenCaptureRecorder | undefined = undefined;
async function startScreenRecord(context: common.UIAbilityContext) {
// 1. 创建录屏实例
this.avScreenCaptureRecorder = await media.createAVScreenCaptureRecorder();
// 2. 准备录制文件并配置参数
let pathDir = context.filesDir;
let filesUri = pathDir + '/Screen_' + new Date().getTime() + '.mp4';
let curFile = fs.openSync(filesUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
let avCaptureConfig: media.AVScreenCaptureRecordConfig = {
fd: curFile.fd, // 必须传入具有写权限的文件描述符
// frameWidth: 720, // 可选:自定义视频宽度
// frameHeight: 1280 // 可选:自定义视频高度
};
// 3. 初始化并开始录制
await this.avScreenCaptureRecorder.init(avCaptureConfig);
await this.avScreenCaptureRecorder.startRecording();
}
async function stopScreenRecord() {
// 4. 停止录制并释放资源
await this.avScreenCaptureRecorder?.stopRecording();
await this.avScreenCaptureRecorder?.release();
}
二、 高阶控制:Picker 模式与麦克风权限
在 HarmonyOS 6.1.0 及以上版本中,系统增强了录屏的隐私保护与多屏适配能力。开发者可以通过 setPickerMode 调起系统级的选择器(Picker),让用户自主选择录制全屏、特定应用或特定窗口。同时,若需录入麦克风声音,必须动态申请权限。
核心代码示例:
import { abilityAccessCtrl } from '@kit.AbilityKit';
// 1. 设置 Picker 显示模式(例如仅显示窗口选择)
if (this.avScreenCaptureRecorder) {
await this.avScreenCaptureRecorder.setPickerMode(media.PickerMode.WINDOW_ONLY);
// 录屏过程中可动态更新录制源
await this.avScreenCaptureRecorder.presentPicker();
}
// 2. 申请麦克风权限(录屏带解说必备)
async function requestMicPermission() {
const atManager = abilityAccessCtrl.createAtManager();
try {
let result = await atManager.requestPermissionsFromUser(getContext(), ["ohos.permission.MICROPHONE"]);
if (result.authResults[0] === 0) {
console.info('麦克风权限授权成功');
}
} catch (err) {
console.error('申请麦克风权限失败:', err);
}
}
三、 Native 层进阶:获取原始音视频码流
对于直播推流、远程桌面共享等需要实时处理视频帧的场景,可以使用 C/C++ 调用 AVScreenCapture 的 NDK 接口。该方式不直接生成 MP4 文件,而是通过回调将原始的音视频 Buffer 抛给业务层处理。
核心代码示例(C++):
#include <multimedia/player_framework/native_avscreen_capture.h>
// 1. 创建并配置录屏实例
OH_AVScreenCapture* capture = OH_AVScreenCapture_Create();
OH_AVScreenCaptureConfig config = {
.captureMode = OH_CAPTURE_HOME_SCREEN,
.dataType = OH_ORIGINAL_STREAM, // 指定获取原始码流
// ...配置音视频采集参数
};
OH_AVScreenCapture_Init(capture, config);
// 2. 设置数据回调,接收原始 Buffer
void OnBufferAvailable(OH_AVScreenCapture *capture, OH_AVBuffer *buffer,
OH_AVScreenCaptureBufferType bufferType, int64_t timestamp, void *userData) {
// 在此处获取音视频原始码流数据,用于推流或实时渲染
}
OH_AVScreenCapture_SetDataCallback(capture, OnBufferAvailable, nullptr);
// 3. 开始与停止采集
OH_AVScreenCapture_StartScreenCapture(capture);
OH_AVScreenCapture_StopScreenCapture(capture);
// 4. 释放资源
OH_AVScreenCapture_Release(capture);
- 麦克风权限与长时任务:如果配置了采集麦克风音频数据,除了必须在
module.json5中声明ohos.permission.MICROPHONE权限外,还必须在后台运行时申请长时任务(ohos.permission.KEEP_BACKGROUND_RUNNING),防止录屏被系统挂起。 - 状态机严格校验:
AVScreenCapture具有严格的状态机机制。在调用Start、Stop等方法前,务必通过on('stateChange')监听当前状态,在错误的状态下执行操作会导致录屏异常中断。 - 通话中断保护:在录屏过程中如果发生系统通话或来电,录屏会被系统强制自动停止,并上报
OH_SCREEN_CAPTURE_STATE_STOPPED_BY_CALL状态,开发者需在回调中做好业务状态的恢复与提示。 - 隐私合规提示:使用窗口级或应用级录屏时,系统 Picker 会向用户展示“隐私保护”警告。开发者应确保录屏功能仅在用户明确授权和知情的情况下开启,避免后台静默录制敏感信息。
四、 视觉进阶:精细化控制图像填充模式(FillMode)
在录屏时,捕获源的宽高比往往与目标输出分辨率不一致(例如手机全屏录制输出为 1080p)。鸿蒙提供了 OH_AVScreenCapture_CaptureStrategy 策略对象,允许开发者灵活配置画面的填充模式,避免自行处理裁剪或黑边。
核心代码示例(C++):
// 1. 创建录屏实例与策略对象
OH_AVScreenCapture* capture = OH_AVScreenCapture_Create();
OH_AVScreenCapture_CaptureStrategy* strategy = OH_AVScreenCapture_CreateCaptureStrategy();
// 2. 设置填充模式:
// OH_FILL_MODE_CROP: 裁剪边缘以填满画面(适合游戏、沉浸式视频)
// OH_FILL_MODE_FIT: 保持比例留黑边(适合代码教学、文档演示)
OH_AVScreenCapture_StrategyForFillMode(strategy, OH_FILL_MODE_FIT);
// 3. 将策略关联到录屏实例(必须在 Init 之前执行)
OH_AVScreenCapture_SetCaptureStrategy(capture, strategy);
OH_AVScreenCapture_Init(capture, config);
// 4. 资源释放(录屏结束后先释放策略,再释放实例)
OH_AVScreenCapture_DestroyCaptureStrategy(strategy);
OH_AVScreenCapture_Release(capture);
五、 PC端特化:后台保活与隐私弹窗静默
在鸿蒙 PC/2in1 设备上,录屏应用常面临屏幕熄灭导致录制中断,或频繁弹出系统隐私警告影响体验的问题。通过申请特定的系统级权限,可实现无感录制与后台保活。
配置与代码示例:
// module.json5 权限声明
"requestPermissions": [
{ "name": "ohos.permission.TIMEOUT_SCREENOFF_DISABLE_LOCK" }, // 息屏不锁屏保活
{ "name": "ohos.permission.CUSTOM_SCREEN_RECORDING" } // 禁用系统隐私警告弹窗
]
注:配置 CUSTOM_SCREEN_RECORDING 后,应用录屏时将不再弹出“正在录制屏幕”的系统级隐私警告,适用于企业内网会议或受信任的监控场景。
六、 跨平台架构:Flutter 鸿蒙录屏能力的桥接
对于使用 Flutter 构建的鸿蒙应用,Dart 层无法直接调用 AVScreenCaptureRecorder。必须通过自定义插件(Plugin)结合 Platform Channel 机制,在 ArkTS 层完成文件创建、权限申请和录屏控制,再将状态回传给 Dart 层。
核心代码示例(ArkTS 侧):
// 处理来自 Flutter (Dart) 的 MethodChannel 调用
private onMethodCall(call: MethodCall): void {
switch (call.method) {
case 'startRecord':
// 调用鸿蒙原生 API 启动录屏
this.startScreenRecord(getContext(this) as common.UIAbilityContext);
this.channel.invokeMethodSuccess(call.callbackId, true);
break;
case 'stopRecord':
this.stopScreenRecord();
this.channel.invokeMethodSuccess(call.callbackId, true);
break;
default:
this.channel.invokeMethodError(call.callbackId, 'NOT_IMPLEMENTED', 'Method not implemented');
}
}
七、 稳定性保障:系统级异常中断的优雅降级
录屏过程中极易受到系统级事件的干扰(如来电、用户账号切换、麦克风被抢占)。开发者必须完善状态机监听,做好业务状态的恢复与用户提示。
核心代码示例(ArkTS):
this.screenCapture.on('stateChange', async (infoType: media.AVScreenCaptureStateCode) => {
switch (infoType) {
case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_STOPPED_BY_CALL:
// 录屏因通话中断:自动释放资源并提示用户
await this.screenCapture?.release();
this.showUserToast('录屏已因系统通话自动暂停');
break;
case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_MIC_UNAVAILABLE:
// 麦克风被其他应用抢占:仅停止音频采集,视频继续或暂停
console.warn('麦克风不可用,请检查其他音频应用');
break;
case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_STOPPED_BY_USER_SWITCHES:
// 用户切换账号:强制停止并清理沙箱临时文件
await this.screenCapture?.release();
this.cleanTempRecordFiles();
break;
}
});
- 策略先行原则:在使用 Native C API 时,
CaptureStrategy(如填充模式、Picker 弹窗策略)必须在OH_AVScreenCapture_Init之前通过SetCaptureStrategy挂载。录屏引擎启动后将不再接受策略变更。 - 沙箱路径与文件权限:录屏生成的 MP4 文件必须存放在应用沙箱目录(如
context.filesDir)下,且文件打开模式必须包含fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE。直接传入外部存储路径会导致init报 401 参数错误。 - 隐私合规红线:虽然系统提供了禁用隐私弹窗的权限,但在面向 C 端消费者的应用中,强烈建议保留系统的 Picker 弹窗和录屏胶囊。静默录屏极易触发应用市场的安全合规审核拦截。
- 性能开销控制:录屏是极其消耗 CPU/GPU 和 I/O 的操作。在 ArkTS 层避免在录屏期间进行高频的 UI 刷新或复杂的 JSON 序列化,建议将视频帧的后处理(如加水印、裁剪)交由 Native 层或独立的 Worker 线程处理。
1、 策略先行原则:Native C API 的严格时序控制
在使用 C/C++ 进行录屏开发时,CaptureStrategy 必须在 Init 之前完成挂载。以下代码展示了如何正确地将“填充模式”和“Picker 弹窗策略”封装并绑定到录屏实例中。
核心代码示例(C++):
#include <multimedia/player_framework/native_avscreen_capture.h>
int32_t ConfigureCaptureStrategy(OH_AVScreenCapture *capture) {
if (capture == NULL) return -1;
// 1. 创建策略对象
OH_AVScreenCapture_CaptureStrategy *strategy = OH_AVScreenCapture_CreateCaptureStrategy();
if (strategy == NULL) return -1;
// 2. 设置填充模式(例如:保持比例留黑边,适合文档演示)
OH_AVScreenCapture_StrategyForFillMode(strategy, OH_SCREEN_CAPTURE_FILLMODE_LETTERBOX);
// 3. 设置 Picker 弹窗策略(true: 录屏启动时弹出系统选择器)
OH_AVScreenCapture_StrategyForPickerPopUp(strategy, true);
// 4. 将策略挂载到录屏实例(必须在 Init 之前执行)
OH_AVSCREEN_CAPTURE_ErrCode ret = OH_AVScreenCapture_SetCaptureStrategy(capture, strategy);
// 注意:策略对象在 Set 之后,其生命周期由 capture 管理,无需手动释放
return ret;
}
2、 沙箱路径与文件权限:规避 401 参数错误
录屏初始化时,如果文件路径越界或权限不足,系统会直接抛出 401 错误。必须严格使用应用沙箱路径,并使用正确的 OpenMode 组合。
核心代码示例(ArkTS):
import { common } from '@kit.AbilityKit';
import { fileIo as fs } from '@kit.CoreFileKit';
import media from '@ohos.multimedia.media';
async function initScreenCapture(context: common.UIAbilityContext) {
// 1. 严格使用应用沙箱目录 (context.filesDir)
let sandboxPath: string = context.filesDir + '/Screen_' + new Date().getTime() + '.mp4';
// 2. 创建文件并赋予 读写 + 创建 权限
let curFile = fs.openSync(sandboxPath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);
// 3. 构建配置对象(fd 为必填项,其他参数可选)
let avCaptureConfig: media.AVScreenCaptureRecordConfig = {
fd: curFile.fd,
// 若需自定义输出分辨率,可在此指定
// frameWidth: 1920,
// frameHeight: 1080
};
let recorder = await media.createAVScreenCaptureRecorder();
await recorder.init(avCaptureConfig); // 此时不会再报 401 错误
}
3、 隐私合规红线:动态控制 Picker 与弹窗
为了兼顾合规与用户体验,建议在代码中将 Picker 弹窗策略与业务场景解耦,通过动态配置策略对象来控制是否弹出系统级选择器。
核心代码示例(C++):
// 场景A:面向C端用户,必须弹出 Picker 让用户知情并选择录制目标
void enablePrivacyCompliance(OH_AVScreenCapture *capture) {
OH_AVScreenCapture_CaptureStrategy *strategy = OH_AVScreenCapture_CreateCaptureStrategy();
OH_AVScreenCapture_StrategyForPickerPopUp(strategy, true); // 显式开启弹窗
OH_AVScreenCapture_SetCaptureStrategy(capture, strategy);
}
// 场景B:企业内部受信任设备(需提前申请 CUSTOM_SCREEN_RECORDING 权限)
void disablePickerForEnterprise(OH_AVScreenCapture *capture) {
OH_AVScreenCapture_CaptureStrategy *strategy = OH_AVScreenCapture_CreateCaptureStrategy();
OH_AVScreenCapture_StrategyForPickerPopUp(strategy, false); // 静默启动
OH_AVScreenCapture_SetCaptureStrategy(capture, strategy);
}
4、 性能开销控制:Worker 线程隔离与后处理
录屏期间,主线程的 CPU 和 I/O 资源极其紧张。视频帧的后处理(如加水印、裁剪、JSON 序列化)必须剥离到独立的 Worker 线程中执行。
核心代码示例(ArkTS):
// 1. 主线程仅负责录屏控制和轻量级状态更新
@Entry
@Component
struct ScreenRecordPage {
private worker: worker.ThreadWorker | null = null;
aboutToAppear() {
// 2. 初始化 Worker 线程,专门处理耗时的视频帧后处理
this.worker = new worker.ThreadWorker('workers/VideoPostProcess.ets');
// 监听 Worker 处理完成后的结果,仅更新 UI 状态
this.worker.onmessage = (e: MessageEvents) => {
if (e.data.type === 'watermark_added') {
// 安全地更新 UI,不会导致录屏掉帧
this.updateUIStatus('水印添加成功');
}
};
}
// 3. 录屏结束后的异步后处理
async onRecordingStopped(filePath: string) {
// 将耗时操作抛给 Worker,绝不阻塞主线程
this.worker?.postMessage({
action: 'add_watermark',
videoPath: filePath
});
}
}
// workers/VideoPostProcess.ets (Worker 线程代码)
import { worker, MessageEvents } from '@kit.ArkTS';
const workerPort = worker.workerPort;
workerPort.onmessage = async (e: MessageEvents) => {
if (e.data.action === 'add_watermark') {
// 在独立线程中执行耗时的视频帧处理/JSON序列化
// await processVideoFrames(e.data.videoPath);
// 处理完成后通知主线程
workerPort.postMessage({ type: 'watermark_added' });
}
};更多推荐



所有评论(0)