【HarmonyOS】鸿蒙应用实现屏幕录制详解和源码

【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';

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))});}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/891957.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决在VS2019/2022中编译c++项目报错fatal error C1189: #error : “No Target Architecture“

解决在VS2019/2022中编译c项目报错fatal error C1189: #error : “No Target Architecture” 报错原因 在winnt.h中,不言而喻,一目了然: 代码节选: #if defined(_AMD64_) || defined(_X86_) #define PROBE_ALIGNMENT( _s ) TY…

Python教程丨Python环境搭建 (含IDE安装)——保姆级教程!

工欲善其事,必先利其器。 学习Python的第一步不要再加收藏夹了!提高执行力,先给自己装好Python。 1. Python 下载 1.1. 下载安装包 既然要下载Python,我们直接进入python官网下载即可 Python 官网:Welcome to Pyt…

实现AVL树

目录 AVL树概念 AVL树结构 AVL树插入 LL型 - 右单旋 RR型 - 左单旋 LR型 - 左右双旋 RL型 - 右左双旋 插入代码实现 AVL树测试 附AVL树实现完整代码 AVL树概念 前面的博客介绍了搜索二叉树,二叉搜索树-CSDN博客 在某些特定的情况下,⼆叉搜索树…

极客说|微软 Phi 系列小模型和多模态小模型

作者:胡平 - 微软云人工智能高级专家 「极客说」 是一档专注 AI 时代开发者分享的专栏,我们邀请来自微软以及技术社区专家,带来最前沿的技术干货与实践经验。在这里,您将看到深度教程、最佳实践和创新解决方案。关注「极客说」&am…

React+redux项目搭建流程

1.创建项目 create-react-app my-project --template typescript // 创建项目并使用typescript2.去除掉没用的文件夹,只保留部分有用的文件 3.项目配置: 配置项目的icon 配置项目的标题 配置项目的别名等(craco.config.ts&…

HTML+CSS+JS制作高仿小米官网网站(内附源码,含6个页面)

一、作品介绍 HTMLCSSJS制作一个高仿小米官网网站,包含首页、商品详情页、确认订单页、订单支付页、收货地址管理页、新增收获地址页等6个静态页面。其中每个页面都包含一个导航栏、一个主要区域和一个底部区域。 二、页面结构 1. 顶部导航栏 包含Logo、主导航菜…

obs directx11

创建逻辑 obs 在windows 下分为Opengl 和 directx 两种渲染模式,默认使用的是directx ,兼容性更好; 代码路径: E:\opensrc\obs_studio_src\obs-studio\UI\obs-app.cpp 选择渲染模式 const char* OBSApp::GetRenderModule() con…

QT实现 端口扫描暂停和继续功能 3

上篇QT给端口扫描工程增加线程2-CSDN博客 为按钮pushButton_Stop添加clicked事件,功能为暂停扫描,并在暂停后显示继续按钮,点击继续按钮之后继续扫描 1.更新UI 添加继续按钮 点击转到槽则会自动声明 2. 更新 MainWindow.h 需要新增的部分…

nginx-限流(请求/并发量)

一. 简述: 在做日常的web运维工作中,难免会遇到服务器流量异常,负载过大等情况。恶意攻击访问/爬虫等非正常性请求,会带来带宽的浪费,服务器压力增大,影响业务质量。 二. 限流方案: 对于这种情…

分布式ID生成-雪花算法实现无状态

雪花算法这里不再赘述,其缺点是有状态(多副本隔离时,依赖手动配置workId和datacenterId),代码如下: /*** 雪花算法ID生成器*/ public class SnowflakeIdWorker {/*** 开始时间截 (2017-01-01)*/private st…

Edge SCDN高效防护与智能加速

当今数字化时代,网络安全和内容分发效率已成为企业业务发展的关键因素。酷盾安全推出了Edge SCDN解决方案,为企业提供全方位的安全防护和高效的内容分发服务。 一、卓越的安全防护能力 1.DDoS攻击的精准防御:Edge SCDN具备强大的DDoS攻击检测…

在vscode上

第一步 安装插件 (1)从菜单处打开vscode,之后点击左侧“拓展”,在搜索栏输入“platform”,安装这个插件。 注:安装过程可能会慢一点,可以尝试连接自己的热点 (2)安装完…

产品心、用户脑、押重注......解读vivo穿越周期之道

出品 | 何玺 排版 | 叶媛 国内科技企业中,vivo绝对算个“异类”。给人以平和谦逊、稳健踏实的印象,却极具实力! 回望vivo发展历程,这家拥有近30年历史的超大型全球化产业科技生态型公司,从功能机到智能机一路走来&am…

jenkins入门4 --window执行execute shell

1、启动关闭jenkins 在Windows环境下,如果你需要关闭Jenkins服务,可以通过以下几种方式: 1、使用Windows服务管理器: 打开“运行”对话框(Win R),输入services.msc,然后回车。 在服…

矩阵碰一碰发视频源码搭建全解析,支持OEM

在数字化营销与互动体验需求日益增长的当下,矩阵碰一碰发视频功能以其独特的交互性和高效的信息传播能力,正逐渐成为吸引用户、提升品牌影响力的有力工具。本文将深入探讨如何搭建矩阵碰一碰发视频的源码,帮助开发者实现这一创新功能。 一、技…

软件确认测试和验收测试有什么区别?

在当今快速发展的软件行业中,软件确认测试与验收测试是软件产品生产周期中的重要步骤,但很多人容易混淆,那么这两者之间究竟有什么区别呢? 软件确认测试是一个旨在确保软件产品符合用户需求规格的过程。它对软件的功能、性能和可用性进行深…

cat命令详解

cat 是 Linux/Unix 中的一个非常常用的命令,主要用于 连接 文件并显示文件内容。它的名称来源于 concatenate(连接),不仅可以查看文件内容,还能将多个文件合并为一个文件,或用作其他数据流操作。 以下是对 …

[sdx12] Qualcomm SDX12查看基线版本

about.html文件 Build部分 Product SDX12.LE.1.0-00263-NBOOT.NEFS.PROD-1.90789.1 Distribution SDX12.LE.1.0|AMSS|Standard|OEM: Build Components部分 从以上截图可以看到以下模块的版本号及格式 BOOT 基线版本号 BOOT.BF.3.1.c3-00010-SDX12AAAAANAZB-1 Distr…

基于CLIP和DINOv2实现图像相似性方面的比较

概述 在人工智能领域,CLIP和DINOv2是计算机视觉领域的两大巨头。CLIP彻底改变了图像理解,而DINOv2为自监督学习带来了新的方法。 在本文中,我们将踏上一段旅程,揭示定义CLIP和DINOv2的优势和微妙之处。我们的目标是发现这些模型…

LS1046 XFI网口接近10Gbps

硬件平台: CPU LS1046A 1.8GHZ 软件平台: LINUX 4.19.32 BUILDROOT 测试软件: ipferf 整个过程比较曲折,网口默认不能达到这个速度,只有2Gbps以内。需要FMC配置后才能达到9.4Gbps。