药品网站如何建设,网站做淘宝客排名会掉吗,适合前端新手做的网站,网站编辑招聘信息【HarmonyOS】鸿蒙应用实现屏幕录制详解和源码
一、前言
官方文档关于屏幕录制的API和示例介绍获取简单和突兀。使用起来会让上手程度变高。所以特意开篇文章#xff0c;讲解屏幕录制的使用。官方文档参见#xff1a;使用AVScreenCaptureRecorder录屏写文件(ArkTS)
二、方…【HarmonyOS】鸿蒙应用实现屏幕录制详解和源码
一、前言
官方文档关于屏幕录制的API和示例介绍获取简单和突兀。使用起来会让上手程度变高。所以特意开篇文章讲解屏幕录制的使用。官方文档参见使用AVScreenCaptureRecorder录屏写文件(ArkTS)
二、方案思路
鸿蒙应用关于录制屏幕官方提供了AVScreenCaptureRecorder进行屏幕录制的调用。分为以下几个步骤 1.创建该对象
import media from ohos.multimedia.media;private avScreenCaptureRecorder: media.AVScreenCaptureRecorder | undefined undefined;this.avScreenCaptureRecorder await media.createAVScreenCaptureRecorder();2.进行属性配置初始化 这里尤其要注意config配置属性对象的作用范围在官方示例中一般不喜欢创建成局部对象而是全局对象。但是fd又是异步获取就容器造成fd拿到后并没有赋值给config中导致init函数初始化一直报错401参数错误。
如果像官方示例列为全局对象那fd的file对象也需要创建为全局对象看起来就很恶心。所以我这里改成局部对象。
【官方DEMO关于fd的出处并没有写全春秋笔法过多。所以我经常吐槽说官方文档基本上属于你会了才能看懂了。。】 let context getContext(this) as common.UIAbilityContext; // 获取设备A的UIAbilityContext信息// 沙箱路径let pathDir: string context.filesDir; // /data/storage/el2/base/haps/entry/files// 视频文件名字和路径let filesUri: string pathDir /Screen_ new Date().getTime() .mp4;// 缓存Uri用于保存媒体库使用this.targetFileUri filesUri;// 创建文件赋予写权限let curFile fs.openSync(filesUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);let avCaptureConfig: media.AVScreenCaptureRecordConfig {// 文件需要先有调用者创建赋予写权限将文件fd传给此参数fd: curFile.fd,// 除了fd其他参数都是可选可以不设置。默认宽高就是手机时机宽高。// frameWidth: 768,// frameHeight: 1280,}await this.avScreenCaptureRecorder?.init(avCaptureConfig);此时录屏文件是保存在我们创建的沙箱路径中的。所以并不需要官方文档中提到的读写权限。
3.然后调用开始录屏或者结束录屏。 await this.avScreenCaptureRecorder.startRecording()await this.avScreenCaptureRecorder.stopRecording()4.选配-录音权限的配置和申请 如果没有配置和申请录音权限。默认录屏是没有麦克风的声音。反之录屏时你说话就能录入到视频中。 /*** 申请麦克风权限*/private questMicPermissions(){const atManager: abilityAccessCtrl.AtManager abilityAccessCtrl.createAtManager();try {atManager.requestPermissionsFromUser(getContext(), [ohos.permission.MICROPHONE]).then((data) {if (data.authResults[0] 0) {} else {console.log(this.TAG, user rejected)}}).catch((err: BusinessError) {console.log(this.TAG, BusinessError err: JSON.stringify(err))})} catch (err) {console.log(this.TAG, catch err: JSON.stringify(err))}}5.选配-将沙箱路径下的录屏保存到相册中 保存到媒体库中有很多种方式。我此处举例使用的是saveButton的形式进行保存函数的调用。
直接调用以下保存函数是不会生效。在鸿蒙中一定需要用户知情同意才能将沙箱的资源保存到媒体库中。 /*** 保存视频到媒体库*/private saveVideo() {let titleStr Screen_ new Date().getTime()let context getContext(this);let phAccessHelper photoAccessHelper.getPhotoAccessHelper(context);let photoType: photoAccessHelper.PhotoType photoAccessHelper.PhotoType.VIDEO;let extension:string mp4;let options: photoAccessHelper.CreateOptions {title:titleStr}phAccessHelper.createAsset(photoType, extension, options).then(async (uriDes:string){try {let file_uri fs.openSync(this.targetFileUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);let file fs.openSync(uriDes, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);fs.copyFileSync(file_uri.fd, file.fd);fs.closeSync(file.fd);fs.closeSync(file_uri.fd);promptAction.showToast({message: 已保存至相册,duration: 3000});}catch (err) {console.error(error is JSON.stringify(err))}}).catch((err:Error){console.error(error is JSON.stringify(err))});}SaveButton隐私窗口的豁免和录制状态的回调监听参见源码示例。
注意 实际开发中因为鸿蒙的后台特性当录屏时应用切到后台大于三秒应用进程就会被挂起。所以需要设置后台任务的长时任务。保证录屏的正常。后面我会针对长时任务以录屏来举例此处先不处理。
三、源码示例 // 申请麦克风{name: ohos.permission.MICROPHONE,reason: $string:reason,usedScene: {abilities: [EntryAbility],when: always}},SCRecordTestPage.ets import { media } from kit.MediaKit
import { BusinessError } from kit.BasicServicesKit
import fs from ohos.file.fs;
import { abilityAccessCtrl, common, PermissionRequestResult, Permissions } from kit.AbilityKit
import { photoAccessHelper } from kit.MediaLibraryKit;
import { promptAction } from kit.ArkUI;
import { fileUri } from kit.CoreFileKit;Entry
Component
struct SCRecordTestPage {private TAG: string SCRecordTestPage;private avScreenCaptureRecorder: media.AVScreenCaptureRecorder | undefined undefined;private targetFileUri: string ;private saveButtonOptions: SaveButtonOptions {icon: SaveIconStyle.FULL_FILLED,text: SaveDescription.SAVE_FILE,buttonType: ButtonType.Capsule} // 设置安全控件按钮属性async aboutToAppear() {// 初始化屏幕录制渲染对象await this.createAVScreenCapture();}async createAVScreenCapture() {this.avScreenCaptureRecorder await media.createAVScreenCaptureRecorder();this.avScreenCaptureRecorder.on(stateChange, async (infoType: media.AVScreenCaptureStateCode) {switch (infoType) {case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_STARTED:console.info(录屏成功开始后会收到的回调);break;case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_CANCELED:this.avScreenCaptureRecorder?.release();this.avScreenCaptureRecorder undefined;console.info(不允许使用录屏功能);break;case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_STOPPED_BY_USER:this.avScreenCaptureRecorder?.release();this.avScreenCaptureRecorder undefined;console.info(通过录屏胶囊结束录屏底层录制会停止);break;case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_INTERRUPTED_BY_OTHER:console.info(录屏因其他中断而停止底层录制会停止);break;case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_STOPPED_BY_CALL:console.info(录屏过程因通话中断底层录制会停止);break;case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_MIC_UNAVAILABLE:console.info(录屏麦克风不可用);break;case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_MIC_MUTED_BY_USER:console.info(录屏麦克风被用户静音);break;case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_MIC_UNMUTED_BY_USER:console.info(录屏麦克风被用户取消静音);break;case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_ENTER_PRIVATE_SCENE:// 目前可以从系统直接注册监听到进入隐私场景console.info(录屏进入隐私场景);break;case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_EXIT_PRIVATE_SCENE:console.info(录屏退出隐私场景);break;case media.AVScreenCaptureStateCode.SCREENCAPTURE_STATE_STOPPED_BY_USER_SWITCHES:console.info(用户账号切换底层录制会停止);break;default:break;}})this.avScreenCaptureRecorder.on(error, (err) {console.info(处理异常情况);})let context getContext(this) as common.UIAbilityContext; // 获取设备A的UIAbilityContext信息// 沙箱路径let pathDir: string context.filesDir; // /data/storage/el2/base/haps/entry/files// 视频文件名字和路径let filesUri: string pathDir /Screen_ new Date().getTime() .mp4;// 缓存Uri用于保存媒体库使用this.targetFileUri filesUri;// 创建文件赋予写权限let curFile fs.openSync(filesUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);let avCaptureConfig: media.AVScreenCaptureRecordConfig {// 文件需要先有调用者创建赋予写权限将文件fd传给此参数fd: curFile.fd,// 除了fd其他参数都是可选可以不设置。默认宽高就是手机时机宽高。// frameWidth: 768,// frameHeight: 1280,}await this.avScreenCaptureRecorder?.init(avCaptureConfig);}build() {Column({ space: 50 }) {Button(选配-开启麦克风).onClick(() {this.questMicPermissions();}).height(60).width(100%)Button(开始录屏).onClick(() {this.startRecord()}).height(60).width(100%)Button(结束录屏).onClick(() {this.stopRecord()}).height(60).width(100%)SaveButton(this.saveButtonOptions) // 创建安全控件按钮.onClick(async (event, result: SaveButtonOnClickResult) {if (result SaveButtonOnClickResult.SUCCESS) {try {this.saveVideo();} catch (err) {console.error(create asset failed with error: ${err.code}, ${err.message});}} else {console.error(SaveButtonOnClickResult create asset failed);}})}.justifyContent(FlexAlign.Center).width(100%).height(100%).padding({ left: 30, right: 30})}/*** 申请麦克风权限*/private questMicPermissions(){const atManager: abilityAccessCtrl.AtManager abilityAccessCtrl.createAtManager();try {atManager.requestPermissionsFromUser(getContext(), [ohos.permission.MICROPHONE]).then((data) {if (data.authResults[0] 0) {} else {console.log(this.TAG, user rejected)}}).catch((err: BusinessError) {console.log(this.TAG, BusinessError err: JSON.stringify(err))})} catch (err) {console.log(this.TAG, catch err: JSON.stringify(err))}}/*** 开启录制*/private startRecord() {// 创建豁免隐私窗口这里填写的是子窗口id和主窗口id// let windowIDs [57, 86];// await this.avScreenCaptureRecorder?.skipPrivacyMode(windowIDs);this.avScreenCaptureRecorder?.startRecording().then(() {console.info(Succeeded in starting avScreenCaptureRecorder);}).catch((err: BusinessError) {console.info(Failed to start avScreenCaptureRecorder, error: err.message);})}/*** 暂停录制*/private stopRecord() {this.avScreenCaptureRecorder?.stopRecording().then(() {console.info(Succeeded in stopping avScreenCaptureRecorder);}).catch((err: BusinessError) {console.info(Failed to stop avScreenCaptureRecorder, error: err.message);})}/*** 保存视频到媒体库*/private saveVideo() {let titleStr Screen_ new Date().getTime()let context getContext(this);let phAccessHelper photoAccessHelper.getPhotoAccessHelper(context);let photoType: photoAccessHelper.PhotoType photoAccessHelper.PhotoType.VIDEO;let extension:string mp4;let options: photoAccessHelper.CreateOptions {title:titleStr}phAccessHelper.createAsset(photoType, extension, options).then(async (uriDes:string){try {let file_uri fs.openSync(this.targetFileUri, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);let file fs.openSync(uriDes, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);fs.copyFileSync(file_uri.fd, file.fd);fs.closeSync(file.fd);fs.closeSync(file_uri.fd);promptAction.showToast({message: 已保存至相册,duration: 3000});}catch (err) {console.error(error is JSON.stringify(err))}}).catch((err:Error){console.error(error is JSON.stringify(err))});}
}