
HarmonyOS 视频录制暂停与恢复功能实现:让用户录制体验更灵活
在移动应用开发中,视频录制功能的灵活性直接影响用户体验。比如用户录制 Vlog 时需要暂停调整镜头,或是录制产品演示视频时需暂停补充讲解,此时 “单次录制支持暂停 / 恢复” 就成了关键需求 —— 它能避免用户因中断录制生成多个视频文件,还能减少后期拼接的繁琐操作。在 HarmonyOS 系统中,借助 AVRecorder 多媒体接口,我们可以轻松实现这一功能,让视频录制过程更符合用户直觉。下面正文开始
一、核心技术基础:认识 AVRecorder 接口
要实现视频录制的暂停与恢复,首先需要了解 HarmonyOS 提供的 AVRecorder 类 —— 它是系统级多媒体录制接口,封装了视频源(如相机)、音频源(如麦克风)的采集、编码与文件输出逻辑,且从 API 9 开始原生支持 pause()(暂停)和 resume()(恢复)方法,这为功能实现提供了底层支持。
1.1 AVRecorder 的核心能力
- 多源适配:支持从相机(VIDEO_SOURCE_CAMERA)获取视频流,从麦克风(AUDIO_SOURCE_MIC)获取音频流,满足音视频同步录制需求;
- 灵活控制:提供 prepare()(准备)、start()(开始)、pause()(暂停)、resume()(恢复)、stop()(停止)、release()(释放)等方法,覆盖录制全生命周期;
- 文件完整性保障:暂停 / 恢复过程中,系统会自动处理音视频帧的拼接逻辑,最终生成单个连续的视频文件,无需开发者手动合并片段。
1.2 关键状态流转
AVRecorder 的状态管理是功能稳定的核心,必须严格遵循以下流转顺序,避免跨状态调用导致异常:
idle(初始) → prepared(准备完成) → recording(录制中) → paused(暂停) → recording(恢复录制) → stopped(停止) → released(资源释放)
例如:未调用 start() 直接调用 pause(),或在 paused 状态下调用 start(),都会触发 BusinessError 异常,因此需在代码中做好状态判断。
二、完整实现流程:从权限配置到功能落地
下面以 ArkTS 声明式开发为例,完整实现 “视频录制 + 暂停 / 恢复 + 文件保存” 功能,涵盖权限申请、录制器初始化、控制逻辑与 UI 交互。
2.1 第一步:配置必要权限
视频录制需访问相机、麦克风与媒体存储,需在 module.json5 的 requestPermissions 中声明以下权限(HarmonyOS 权限管理遵循 “动态申请 + 用户授权” 原则):
{
"module": {
"requestPermissions": [
{ "name": "ohos.permission.CAMERA", "reason": "需要访问相机进行视频采集", "usedScene": { "ability": ["com.example.videorecorder.MainAbility"], "when": "always" } },
{ "name": "ohos.permission.MICROPHONE", "reason": "需要访问麦克风进行音频采集", "usedScene": { "ability": ["com.example.videorecorder.MainAbility"], "when": "always" } },
{ "name": "ohos.permission.WRITE_MEDIA", "reason": "需要写入媒体文件保存录制视频", "usedScene": { "ability": ["com.example.videorecorder.MainAbility"], "when": "always" } },
{ "name": "ohos.permission.READ_MEDIA", "reason": "需要读取媒体库验证文件保存状态", "usedScene": { "ability": ["com.example.videorecorder.MainAbility"], "when": "always" } }
]
}
}
2.2 第二步:初始化录制器与配置参数
录制器初始化需完成两件核心事:一是获取合法的视频输出路径(通过 mediaLibrary 创建媒体资源),二是配置录制参数(如分辨率、编码格式、比特率等)。
import avRecorder from '@ohos.multimedia.avRecorder';
import mediaLibrary from '@ohos.multimedia.mediaLibrary';
import { BusinessError } from '@ohos.base';
import abilityAccessCtrl from '@ohos.abilityAccessCtrl';
@Component
struct VideoRecorderPage {
// 录制器实例
private recorder: avRecorder.Recorder | null = null;
// 录制状态管理
@State isRecording: boolean = false;
@State isPaused: boolean = false;
// 视频输出路径
private outputUrl: string = '';
// 权限申请结果标记
@State hasPermission: boolean = false;
// 页面加载时检查并申请权限
aboutToAppear() {
this.checkAndRequestPermissions();
}
// 检查并申请必要权限
private async checkAndRequestPermissions() {
const permissions = [
'ohos.permission.CAMERA',
'ohos.permission.MICROPHONE',
'ohos.permission.WRITE_MEDIA',
'ohos.permission.READ_MEDIA'
];
const atManager = abilityAccessCtrl.createAtManager();
const tokenId = abilityAccessCtrl.getTokenId();
try {
// 检查权限状态
const statusList = await atManager.checkPermissions(tokenId, permissions);
const needRequest: string[] = [];
statusList.forEach((status, index) => {
if (status !== abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {
needRequest.push(permissions[index]);
}
});
// 申请未授予的权限
if (needRequest.length > 0) {
await atManager.requestPermissionsFromUser(tokenId, needRequest);
}
// 再次检查权限,确认全部授予
const finalStatus = await atManager.checkPermissions(tokenId, permissions);
this.hasPermission = finalStatus.every(status => status === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED);
if (!this.hasPermission) {
promptAction.showToast({ message: '缺少必要权限,无法启动录制' });
}
} catch (err) {
console.error('权限检查/申请失败:', (err as BusinessError).message);
}
}
// 初始化录制器配置
private async initRecorder() {
if (!this.hasPermission) return;
try {
// 1. 获取媒体库实例,创建视频文件(避免覆盖已有文件,用时间戳命名)
const mediaLib = mediaLibrary.getMediaLibrary();
const fileName = `Video_${Date.now()}.mp4`;
const assetUri = await mediaLib.createAsset(mediaLibrary.MediaType.VIDEO, fileName);
this.outputUrl = assetUri;
// 2. 配置录制参数(根据需求调整分辨率、比特率等)
const recorderConfig: avRecorder.RecorderConfig = {
audioSourceType: avRecorder.AudioSourceType.AUDIO_SOURCE_MIC, // 音频源:麦克风
videoSourceType: avRecorder.VideoSourceType.VIDEO_SOURCE_CAMERA, // 视频源:相机
outputUrl: this.outputUrl, // 输出路径
videoEncoder: 'video/avc', // 视频编码:H.264(兼容性好)
audioEncoder: 'audio/mp4a-latm', // 音频编码:AAC
videoSize: { width: 1920, height: 1080 }, // 分辨率:1080P
bitrate: 10 * 1024 * 1024, // 比特率:10Mbps(平衡画质与文件大小)
frameRate: 30 // 帧率:30fps(流畅度适中)
};
// 3. 创建录制器实例并准备
this.recorder = await avRecorder.createRecorder(recorderConfig);
await this.recorder.prepare(); // 进入 prepared 状态
console.log('录制器初始化完成,输出路径:', this.outputUrl);
} catch (err) {
console.error('录制器初始化失败:', (err as BusinessError).message);
promptAction.showToast({ message: '录制初始化失败,请重试' });
}
}
}
2.3 第三步:实现录制控制逻辑(开始 / 暂停 / 恢复 / 停止)
基于 AVRecorder 的状态流转,实现核心控制方法,确保每个操作仅在合法状态下执行:
// 开始录制
private async startRecording() {
if (!this.hasPermission) {
promptAction.showToast({ message: '缺少权限,无法开始录制' });
return;
}
try {
// 若录制器未初始化,先执行初始化
if (!this.recorder) {
await this.initRecorder();
}
// 仅在未录制状态下执行 start()
if (this.recorder && !this.isRecording) {
await this.recorder.start();
this.isRecording = true;
this.isPaused = false;
promptAction.showToast({ message: '开始录制' });
}
} catch (err) {
console.error('开始录制失败:', (err as BusinessError).message);
promptAction.showToast({ message: '录制启动失败' });
}
}
// 暂停录制
private async pauseRecording() {
try {
// 仅在“录制中且未暂停”状态下执行 pause()
if (this.recorder && this.isRecording && !this.isPaused) {
await this.recorder.pause();
this.isPaused = true;
promptAction.showToast({ message: '已暂停录制' });
}
} catch (err) {
console.error('暂停录制失败:', (err as BusinessError).message);
promptAction.showToast({ message: '暂停失败,请重试' });
}
}
// 恢复录制
private async resumeRecording() {
try {
// 仅在“录制中且已暂停”状态下执行 resume()
if (this.recorder && this.isRecording && this.isPaused) {
await this.recorder.resume();
this.isPaused = false;
promptAction.showToast({ message: '恢复录制' });
}
} catch (err) {
console.error('恢复录制失败:', (err as BusinessError).message);
promptAction.showToast({ message: '恢复失败,请重试' });
}
}
// 停止录制并释放资源
private async stopRecording() {
try {
// 仅在录制状态下执行 stop()
if (this.recorder && this.isRecording) {
await this.recorder.stop(); // 停止录制,生成完整文件
promptAction.showToast({ message: `录制完成,文件已保存` });
console.log('录制文件路径:', this.outputUrl);
// 释放资源(避免内存泄漏)
await this.recorder.release();
this.recorder = null;
this.isRecording = false;
this.isPaused = false;
}
} catch (err) {
console.error('停止录制失败:', (err as BusinessError).message);
promptAction.showToast({ message: '录制停止失败,文件可能损坏' });
}
}
2.4 第四步:设计 UI 交互与状态反馈
为让用户清晰感知录制状态,设计简洁的 UI 界面,包含 “开始 / 暂停 / 恢复 / 停止” 按钮,并根据状态禁用无效操作:
build() {
Column({ space: 30 }) {
// 标题
Text('灵活视频录制')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.textColor('#18181B')
// 录制状态提示
Text(this.getRecordingStatusText())
.fontSize(16)
.textColor(this.isRecording ? '#DC2626' : '#4B5563')
// 录制控制按钮组
Row({ space: 20 }) {
Button('开始')
.width(120)
.height(45)
.fontSize(16)
.backgroundColor('#2563EB')
.onClick(() => this.startRecording())
.enabled(!this.isRecording) // 录制中时禁用“开始”
Button('暂停')
.width(120)
.height(45)
.fontSize(16)
.backgroundColor('#F59E0B')
.onClick(() => this.pauseRecording())
.enabled(this.isRecording && !this.isPaused) // 仅录制中且未暂停时可用
Button('恢复')
.width(120)
.height(45)
.fontSize(16)
.backgroundColor('#10B981')
.onClick(() => this.resumeRecording())
.enabled(this.isRecording && this.isPaused) // 仅录制中且已暂停时可用
Button('停止')
.width(120)
.height(45)
.fontSize(16)
.backgroundColor('#EF4444')
.onClick(() => this.stopRecording())
.enabled(this.isRecording) // 仅录制中时可用
}
}
.width('100%')
.height('100%')
.padding(40)
.backgroundColor('#F9FAFB')
}
// 获取录制状态文本
private getRecordingStatusText(): string {
if (!this.isRecording) {
return '未开始录制,点击“开始”启动';
} else if (this.isPaused) {
return '已暂停录制,点击“恢复”继续或“停止”结束';
} else {
return '正在录制中,点击“暂停”中断或“停止”结束';
}
}
三、优化点:提升功能稳定性与用户体验
完成基础功能后,还需关注以下优化点,避免常见问题并提升用户体验:
3.1 异常处理增强
除了核心流程的 try-catch,还需处理特殊场景:
- 录制中断恢复:若录制中出现临时错误(如相机被占用),可在 onError 回调中释放资源,提示用户重试;
- 文件验证:停止录制后,通过 mediaLibrary 检查文件是否存在且大小合理(避免生成 0KB 无效文件)。
3.2 低版本兼容性
AVRecorder 的 pause() 和 resume() 方法从 API 9 开始支持,若需适配 API 8 及以下版本,需通过 canIUse 做兼容性判断:
// 检查是否支持暂停/恢复方法
private isPauseResumeSupported(): boolean {
return typeof avRecorder.Recorder.prototype.pause === 'function'
&& typeof avRecorder.Recorder.prototype.resume === 'function';
}
// 在初始化时判断
private async initRecorder() {
if (!this.isPauseResumeSupported()) {
promptAction.showToast({ message: '当前系统版本不支持暂停/恢复录制' });
return;
}
// 后续初始化逻辑...
}
3.3 性能优化
- 资源及时释放:录制停止后必须调用 release() 释放相机、麦克风等硬件资源,避免占用导致其他应用无法使用;
- 参数动态调整:根据设备性能动态调整录制参数(如低端设备降低分辨率至 720P),避免卡顿。
四、总结
最后简单总结一下,通过 HarmonyOS 的 AVRecorder 接口,我们无需手动处理音视频帧拼接,即可轻松实现视频录制的暂停与恢复功能,最终生成单个完整视频文件。当然关键在于严格遵循录制器的状态流转,做好权限管理与异常处理,并结合用户体验设计合理的交互反馈。我们后续还可扩展功能,如添加录制时长显示、视频预览等,进一步提升应用的竞争力。希望文章可以帮助到大家,谢谢!!!
