在鸿蒙(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);
  1. 麦克风权限与长时任务:如果配置了采集麦克风音频数据,除了必须在 module.json5 中声明 ohos.permission.MICROPHONE 权限外,还必须在后台运行时申请长时任务(ohos.permission.KEEP_BACKGROUND_RUNNING),防止录屏被系统挂起。
  2. 状态机严格校验AVScreenCapture 具有严格的状态机机制。在调用 StartStop 等方法前,务必通过 on('stateChange') 监听当前状态,在错误的状态下执行操作会导致录屏异常中断。
  3. 通话中断保护:在录屏过程中如果发生系统通话或来电,录屏会被系统强制自动停止,并上报 OH_SCREEN_CAPTURE_STATE_STOPPED_BY_CALL 状态,开发者需在回调中做好业务状态的恢复与提示。
  4. 隐私合规提示:使用窗口级或应用级录屏时,系统 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;
  }
});
  1. 策略先行原则:在使用 Native C API 时,CaptureStrategy(如填充模式、Picker 弹窗策略)必须在 OH_AVScreenCapture_Init 之前通过 SetCaptureStrategy 挂载。录屏引擎启动后将不再接受策略变更。
  2. 沙箱路径与文件权限:录屏生成的 MP4 文件必须存放在应用沙箱目录(如 context.filesDir)下,且文件打开模式必须包含 fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE。直接传入外部存储路径会导致 init 报 401 参数错误。
  3. 隐私合规红线:虽然系统提供了禁用隐私弹窗的权限,但在面向 C 端消费者的应用中,强烈建议保留系统的 Picker 弹窗和录屏胶囊。静默录屏极易触发应用市场的安全合规审核拦截。
  4. 性能开销控制:录屏是极其消耗 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' });
  }
};
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐