[*] 屏幕采集

This commit is contained in:
acgist
2024-05-21 20:11:43 +08:00
parent e0303e444a
commit 77bc3856f1
14 changed files with 231 additions and 73 deletions

View File

@@ -13,15 +13,15 @@
{
"name": "default",
"material": {
"keyAlias" : "debugKey",
"signAlg" : "SHA256withECDSA",
"signAlg" : "SHA256withECDSA",
"keyAlias": "debugKey",
"keyPassword" : "0000001BEC83FE5E74E20C2BD84F2E7C3C00A1038029C6F2FE0121CA85487C1C334AFF202964F02D800AEC",
"storePassword": "0000001B2C10F7CA3C261A3B5F8DE65A4E13A65E37B6C298C3FD3C9A3ACCD5E10DF1F475F5370DF7996519",
"profile" : "C:/Users/acgis/.ohos/config/openharmony/default_taoyao_RrmPaKOZ5zMCU4QlpK1kz_6UoyvfsA4RcQxqexSUJ_8=.p7b",
"certpath" : "C:/Users/acgis/.ohos/config/openharmony/default_taoyao_RrmPaKOZ5zMCU4QlpK1kz_6UoyvfsA4RcQxqexSUJ_8=.cer",
"storeFile": "C:/Users/acgis/.ohos/config/openharmony/default_taoyao_RrmPaKOZ5zMCU4QlpK1kz_6UoyvfsA4RcQxqexSUJ_8=.p12",
"keyPassword" : "0000001BD55750CF879F40EE4EA1B96386BD83692A36E1776974D52D7439FEEEFCB8D2EC5C798FC7C815AE",
"storePassword": "0000001B2AC157AB2A1227E77D5E4925A0B59DE84E94B4D146D9C4DA5EAB030537941F0436A3C3E9F26424"
"storeFile": "C:/Users/acgis/.ohos/config/openharmony/default_taoyao_RrmPaKOZ5zMCU4QlpK1kz_6UoyvfsA4RcQxqexSUJ_8=.p12"
}
},
}
],
},
"modules": [

View File

@@ -4,11 +4,6 @@
"@ohos/hvigor-ohos-plugin": "3.2.4"
},
"execution": {
// "daemon" : true,
// "analyze" : "default",
// "parallel" : true,
// "typeCheck" : false,
// "incremental": true,
},
"logging": {
"level": "debug"

View File

@@ -38,14 +38,15 @@ add_library(
media/LocalClient.cpp
media/RemoteClient.cpp
media/MediaManager.cpp
media/VideoDecoder.cpp
media/VideoEncoder.cpp
media/Player.cpp
media/AudioPlayer.cpp
media/VideoPlayer.cpp
media/VideoDecoder.cpp
media/Capturer.cpp
media/VideoEncoder.cpp
media/AudioCapturer.cpp
media/VideoCapturer.cpp
media/CameraCapturer.cpp
media/ScreenCapturer.cpp
)
target_include_directories(
@@ -83,6 +84,8 @@ target_link_libraries(
libnative_media_venc.so
libnative_media_core.so
libnative_media_codecbase.so
# 屏幕录制
libnative_avscreen_capture.so
# libmediasoupclient
mediasoupclient
)

View File

@@ -20,7 +20,7 @@
#define TAOYAO_CAPTURER_HPP
// 本地音频采集
#define __TAOYAO_AUDIO_LOCAL__ true
#define __TAOYAO_AUDIO_LOCAL__ false
// 本地视频采集
#define __TAOYAO_VIDEO_LOCAL__ true
@@ -49,6 +49,10 @@
#include <ohaudio/native_audiocapturer.h>
#include <ohaudio/native_audiostreambuilder.h>
#include <multimedia/player_framework/native_avscreen_capture.h>
#include <multimedia/player_framework/native_avscreen_capture_base.h>
#include <multimedia/player_framework/native_avscreen_capture_errors.h>
namespace acgist {
/**
@@ -114,7 +118,22 @@ public:
/**
* 视频采集器
*/
class VideoCapturer: public Capturer<acgist::TaoyaoVideoTrackSource> {
class VideoCapturer : public Capturer<acgist::TaoyaoVideoTrackSource> {
public:
VideoCapturer() {};
virtual ~VideoCapturer() override {};
public:
virtual bool start() override = 0;
virtual bool stop() override = 0;
};
/**
* 相机采集器
*/
class CameraCapturer : public VideoCapturer {
public:
// ================ OpenGL ES ================
@@ -153,8 +172,8 @@ public:
Camera_OutputCapability* cameraOutputCapability = nullptr;
public:
VideoCapturer();
virtual ~VideoCapturer() override;
CameraCapturer();
virtual ~CameraCapturer() override;
public:
// 加载OpenGL ES
@@ -166,6 +185,24 @@ public:
};
/**
* 屏幕采集器
*/
class ScreenCapturer : public VideoCapturer {
public:
OH_AVScreenCapture* avScreenCapture = nullptr;
public:
ScreenCapturer();
virtual ~ScreenCapturer() override;
public:
virtual bool start() override;
virtual bool stop() override;
};
}
#endif // TAOYAO_CAPTURER_HPP

View File

@@ -18,6 +18,9 @@
#include "api/peer_connection_interface.h"
#define TAOYAO_VIDEO_SOURCE_SCREEN true
#define TAOYAO_VIDEO_SOURCE_CAMERA false
namespace acgist {
/**

View File

@@ -76,12 +76,6 @@ static int32_t OnError(OH_AudioCapturer* capturer, void* userData, OH_AudioStrea
return 0;
}
#include <fstream>
#include <iostream>
// TODO: 删除
static std::ofstream outfile("/data/storage/el2/base/haps/media/files/audio.pcm");
static int32_t OnReadData(OH_AudioCapturer* capturer, void* userData, void* buffer, int32_t length) {
OH_LOG_DEBUG(LOG_APP, "音频帧数据采集回调:%{public}d", length);
if(userData == nullptr) {
@@ -96,7 +90,6 @@ static int32_t OnReadData(OH_AudioCapturer* capturer, void* userData, void* buff
// 字节数量 * 8 / 位深 / 通道数量
size_t number_of_frames = length / 2;
// size_t number_of_frames = length * 8 / 16 / 2;
outfile.write((char*) buffer, length);
audioCapturer->source->OnData((uint16_t*) buffer, acgist::bitsPerSample, acgist::samplingRate, acgist::channelCount, number_of_frames);
return 0;
}

View File

@@ -61,7 +61,7 @@ static void onFrame(void* context);
// 检查EGL扩展
static bool CheckEglExtension(const char* extensions, const char* extension);
acgist::VideoCapturer::VideoCapturer() {
acgist::CameraCapturer::CameraCapturer() {
initOpenGLES();
Camera_ErrorCode ret = OH_Camera_GetCameraManager(&this->cameraManager);
TAOYAO_VIDEO_RET_LOG("配置相机管理:%{public}d", ret);
@@ -108,7 +108,7 @@ acgist::VideoCapturer::VideoCapturer() {
// OH_CaptureSession_SetZoomRatio(this->cameraCaptureSession, 0.5F);
}
acgist::VideoCapturer::~VideoCapturer() {
acgist::CameraCapturer::~CameraCapturer() {
releaseOpenGLES();
CaptureSession_Callbacks captureSession_Callbacks = { onSessionFocusStateChange, onSessionError };
Camera_ErrorCode ret = OH_CaptureSession_UnregisterCallback(this->cameraCaptureSession, &captureSession_Callbacks);
@@ -148,7 +148,7 @@ acgist::VideoCapturer::~VideoCapturer() {
TAOYAO_VIDEO_RET_LOG("释放相机管理:%{public}d", ret);
}
bool acgist::VideoCapturer::start() {
bool acgist::CameraCapturer::start() {
std::lock_guard<std::recursive_mutex> videoLock(videoMutex);
if (this->running) {
return true;
@@ -176,7 +176,7 @@ bool acgist::VideoCapturer::start() {
return ret = Camera_ErrorCode::CAMERA_OK;
}
bool acgist::VideoCapturer::stop() {
bool acgist::CameraCapturer::stop() {
std::lock_guard<std::recursive_mutex> videoLock(videoMutex);
if (!this->running) {
return true;
@@ -204,7 +204,7 @@ bool acgist::VideoCapturer::stop() {
return ret = Camera_ErrorCode::CAMERA_OK;
}
void acgist::VideoCapturer::initOpenGLES() {
void acgist::CameraCapturer::initOpenGLES() {
// EGL
static const char* EGL_EXT_PLATFORM_WAYLAND = "EGL_EXT_platform_wayland";
static const char* EGL_KHR_PLATFORM_WAYLAND = "EGL_KHR_platform_wayland";
@@ -257,14 +257,16 @@ void acgist::VideoCapturer::initOpenGLES() {
this->eglContext = eglCreateContext(this->eglDisplay, config, EGL_NO_CONTEXT, context_attribs);
}
const EGLint surfaceAttr[] = {
EGL_WIDTH, 1980,
EGL_HEIGHT, 1080,
// EGL_WIDTH, acgist::width,
// EGL_HEIGHT, acgist::height,
EGL_LARGEST_PBUFFER, EGL_TRUE,
EGL_NONE
};
this->eglSurface = eglCreatePbufferSurface(this->eglDisplay, config, surfaceAttr);
// OH_NativeWindow_CreateNativeWindow(this->eglSurface);
eglMakeCurrent(this->eglDisplay, this->eglSurface, this->eglSurface, this->eglContext);
// IMAGE WINDOW
@@ -278,7 +280,7 @@ void acgist::VideoCapturer::initOpenGLES() {
OH_LOG_INFO(LOG_APP, "视频采集SurfaceId%{public}lld", this->surfaceId);
}
void acgist::VideoCapturer::releaseOpenGLES() {
void acgist::CameraCapturer::releaseOpenGLES() {
if(this->textureId != 0) {
glDeleteTextures(this->textureSize, &this->textureId);
this->textureId = 0;
@@ -342,21 +344,20 @@ static void onSessionFocusStateChange(Camera_CaptureSession* session, Camera_Foc
static void onFrame(void* context) {
OH_LOG_DEBUG(LOG_APP, "视频帧数据采集回调:%{public}d", 1);
acgist::VideoCapturer* videoCapturer = (acgist::VideoCapturer*) context;
// OH_NativeImage_UpdateSurfaceImage(videoCapturer->nativeImage);
acgist::CameraCapturer* cameraCapturer = (acgist::CameraCapturer*) context;
// OH_NativeImage_UpdateSurfaceImage(cameraCapturer->nativeImage);
// 更新内容到OpenGL纹理。
uint32_t ret = OH_NativeImage_UpdateSurfaceImage(videoCapturer->nativeImage);
uint32_t ret = OH_NativeImage_UpdateSurfaceImage(cameraCapturer->nativeImage);
if (ret != 0) {
}
// 获取最近调用OH_NativeImage_UpdateSurfaceImage的纹理图像的时间戳和变化矩阵。
int64_t timeStamp = OH_NativeImage_GetTimestamp(videoCapturer->nativeImage);
int64_t timeStamp = OH_NativeImage_GetTimestamp(cameraCapturer->nativeImage);
float matrix[16];
ret = OH_NativeImage_GetTransformMatrix(videoCapturer->nativeImage, matrix);
ret = OH_NativeImage_GetTransformMatrix(cameraCapturer->nativeImage, matrix);
if (ret != 0) {
}
// 对update绑定到对应textureId的纹理做对应的opengl后处理后将纹理上屏
EGLBoolean eglRet = eglSwapBuffers(videoCapturer->eglDisplay, videoCapturer->eglSurface);
EGLBoolean eglRet = eglSwapBuffers(cameraCapturer->eglDisplay, cameraCapturer->eglSurface);
if (eglRet == EGL_FALSE) {
}
//HWTEST_F(NativeImageTest, OHNativeImageSetOnFrameAvailableListener001, Function | MediumTest | Level1)
@@ -396,7 +397,7 @@ if (eglRet == EGL_FALSE) {
// ret = OH_NativeImage_UpdateSurfaceImage(image);
// ASSERT_EQ(ret, SURFACE_ERROR_OK);
//}
// OHNativeWindow* nativeWindow = OH_NativeImage_AcquireNativeWindow(videoCapturer->nativeImage);
// OHNativeWindow* nativeWindow = OH_NativeImage_AcquireNativeWindow(cameraCapturer->nativeImage);
// int code = SET_BUFFER_GEOMETRY;
// int32_t width = 800;
// int32_t height = 600;
@@ -411,30 +412,30 @@ if (eglRet == EGL_FALSE) {
//// 通过OH_NativeWindow_NativeWindowFlushBuffer 提交给消费者使用,例如:显示在屏幕上。
//OH_NativeWindow_NativeWindowFlushBuffer(nativeWindow, buffer, fenceFd, region);
// // 更新内容到OpenGL纹理。
// ret = OH_NativeImage_UpdateSurfaceImage(videoCapturer->nativeImage);
// ret = OH_NativeImage_UpdateSurfaceImage(cameraCapturer->nativeImage);
//if (ret != 0) {
//}
//// 获取最近调用OH_NativeImage_UpdateSurfaceImage的纹理图像的时间戳和变化矩阵。
//int64_t timeStamp = OH_NativeImage_GetTimestamp(videoCapturer->nativeImage);
//int64_t timeStamp = OH_NativeImage_GetTimestamp(cameraCapturer->nativeImage);
//float matrix[16];
//ret = OH_NativeImage_GetTransformMatrix(videoCapturer->nativeImage, matrix);
//ret = OH_NativeImage_GetTransformMatrix(cameraCapturer->nativeImage, matrix);
//if (ret != 0) {
//}
//
//// 对update绑定到对应textureId的纹理做对应的opengl后处理后将纹理上屏
//EGLBoolean eglRet = eglSwapBuffers(videoCapturer->eglDisplay, videoCapturer->eglSurface);
//EGLBoolean eglRet = eglSwapBuffers(cameraCapturer->eglDisplay, cameraCapturer->eglSurface);
//if (eglRet == EGL_FALSE) {
//}
// eglMakeCurrent(videoCapturer->eglDisplay, videoCapturer->eglSurface, videoCapturer->eglSurface, videoCapturer->eglContext);
// OH_NativeImage_UpdateSurfaceImage(videoCapturer->nativeImage);
// OH_NativeImage_GetTimestamp(videoCapturer->nativeImage);
// eglMakeCurrent(cameraCapturer->eglDisplay, cameraCapturer->eglSurface, cameraCapturer->eglSurface, cameraCapturer->eglContext);
// OH_NativeImage_UpdateSurfaceImage(cameraCapturer->nativeImage);
// OH_NativeImage_GetTimestamp(cameraCapturer->nativeImage);
// float matrix[16];
// OH_NativeImage_GetTransformMatrix(videoCapturer->nativeImage, matrix);
// eglSwapBuffers(videoCapturer->eglDisplay, videoCapturer->eglSurface);
// OH_NativeImage_GetTransformMatrix(cameraCapturer->nativeImage, matrix);
// eglSwapBuffers(cameraCapturer->eglDisplay, cameraCapturer->eglSurface);
// char* chars = new char[1024];
// eglQueryNativePixmapNV(videoCapturer->eglDisplay, videoCapturer->eglSurface, &pixmap);
// EGLBoolean ret = eglCopyBuffers(videoCapturer->eglDisplay, videoCapturer->eglSurface, (EGLNativePixmapType) chars);
// eglQueryNativePixmapNV(cameraCapturer->eglDisplay, cameraCapturer->eglSurface, &pixmap);
// EGLBoolean ret = eglCopyBuffers(cameraCapturer->eglDisplay, cameraCapturer->eglSurface, (EGLNativePixmapType) chars);
// char buffer[1024];
// glReadPixels(0, 0, 10, 10, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
// OH_LOG_DEBUG(LOG_APP, "视频帧数据采集回调读取:%{public}d", ret);
@@ -447,7 +448,7 @@ if (eglRet == EGL_FALSE) {
// webrtc::VideoFrame::Builder builder;
// webrtc::VideoFrame videoFrame =
// builder.set_timestamp_ms(rtc::TimeMillis()).set_video_frame_buffer(videoFrameBuffer).build();
// for (auto iterator = videoCapturer->map.begin(); iterator != videoCapturer->map.end(); ++iterator) {
// for (auto iterator = cameraCapturer->map.begin(); iterator != cameraCapturer->map.end(); ++iterator) {
// iterator->second->OnFrame(videoFrame);
// }
// // TODO: 释放webrtc

View File

@@ -2,8 +2,8 @@
acgist::LocalClient::LocalClient(acgist::MediaManager* mediaManager) : acgist::RoomClient(mediaManager) {
this->mediaManager->newLocalClient();
this->audioTrack = this->mediaManager->getAudioTrack();
// this->videoTrack = this->mediaManager->getVideoTrack();
// this->audioTrack = this->mediaManager->getAudioTrack();
this->videoTrack = this->mediaManager->getVideoTrack();
}
acgist::LocalClient::~LocalClient() {

View File

@@ -109,8 +109,8 @@ int acgist::MediaManager::releaseLocalClient() {
}
bool acgist::MediaManager::startCapture() {
this->startAudioCapture();
// this->startVideoCapture();
// this->startAudioCapture();
this->startVideoCapture();
return true;
}
@@ -130,10 +130,10 @@ bool acgist::MediaManager::startAudioCapture() {
if(this->audioTrackSource == nullptr) {
OH_LOG_INFO(LOG_APP, "设置音频来源");
cricket::AudioOptions options;
options.highpass_filter = true;
options.auto_gain_control = true;
options.echo_cancellation = true;
options.noise_suppression = true;
// options.highpass_filter = true;
// options.auto_gain_control = true;
// options.echo_cancellation = true;
// options.noise_suppression = true;
this->audioTrackSource = this->peerConnectionFactory->CreateAudioSource(options);
}
#endif
@@ -143,7 +143,14 @@ bool acgist::MediaManager::startAudioCapture() {
bool acgist::MediaManager::startVideoCapture() {
if(this->videoCapturer == nullptr) {
OH_LOG_INFO(LOG_APP, "开始视频采集");
this->videoCapturer = new acgist::VideoCapturer();
#if TAOYAO_VIDEO_SOURCE_SCREEN
OH_LOG_INFO(LOG_APP, "开始屏幕采集");
this->videoCapturer = new acgist::ScreenCapturer();
#endif
#if TAOYAO_VIDEO_SOURCE_CAMERA
OH_LOG_INFO(LOG_APP, "开始相机采集");
this->videoCapturer = new acgist::CameraCapturer();
#endif
this->videoCapturer->start();
}
if(this->videoTrackSource == nullptr) {

View File

@@ -118,10 +118,10 @@ int acgist::Room::produceMedia() {
this->createRecvTransport();
}
if(this->audioProduce) {
this->produceAudio();
// this->produceAudio();
}
if(this->videoProduce) {
// this->produceVideo();
this->produceVideo();
}
return 0;
}

View File

@@ -0,0 +1,107 @@
#include "../include/Capturer.hpp"
#include <hilog/log.h>
#include "api/video/nv12_buffer.h"
#include "api/video/i420_buffer.h"
// 没法
static acgist::ScreenCapturer* screenCapturer = nullptr;
static void OnError(OH_AVScreenCapture* capture, int32_t errorCode) {
OH_LOG_ERROR(LOG_APP, "屏幕采集异常:%{public}d", errorCode);
}
static void OnAudioBufferAvailable(OH_AVScreenCapture* capture, bool isReady, OH_AudioCaptureSourceType type) {
// OH_LOG_DEBUG(LOG_APP, "屏幕采集音频数据帧");
if (isReady) {
OH_AudioBuffer* buffer = new OH_AudioBuffer;
int32_t ret = OH_AVScreenCapture_AcquireAudioBuffer(capture, &buffer, type);
(void) buffer->buf;
(void) buffer->size;
(void) buffer->timestamp;
delete buffer;
buffer = nullptr;
OH_AVScreenCapture_ReleaseAudioBuffer(capture, type);
}
}
static void OnVideoBufferAvailable(OH_AVScreenCapture* capture, bool isReady) {
OH_LOG_DEBUG(LOG_APP, "屏幕采集视频数据帧");
if (isReady) {
int32_t fence = 0;
int64_t timestamp = 0;
OH_Rect damage;
OH_NativeBuffer* buffer = OH_AVScreenCapture_AcquireVideoBuffer(capture, &fence, &timestamp, &damage);
void* virAddr = nullptr;
OH_NativeBuffer_Map(buffer, &virAddr);
OH_NativeBuffer_Config config;
OH_NativeBuffer_GetConfig(buffer, &config);
// rtc::scoped_refptr<webrtc::I420Buffer> videoFrameBuffer = webrtc::I420Buffer::Copy(config.width, config.height, (uint8_t*) virAddr, 0, (uint8_t*) virAddr, 0, (uint8_t*) virAddr, 0);
// webrtc::VideoFrame::Builder builder;
// webrtc::VideoFrame videoFrame = builder.set_timestamp_ms(timestamp).set_video_frame_buffer(videoFrameBuffer).build();
// screenCapturer->source->OnData(videoFrame);
OH_NativeBuffer_Unmap(buffer);
OH_AVScreenCapture_ReleaseVideoBuffer(capture);
}
}
acgist::ScreenCapturer::ScreenCapturer() {
this->avScreenCapture = OH_AVScreenCapture_Create();
OH_AVScreenCaptureCallback callback;
callback.onError = OnError;
callback.onAudioBufferAvailable = OnAudioBufferAvailable;
callback.onVideoBufferAvailable = OnVideoBufferAvailable;
OH_AVScreenCapture_SetCallback(this->avScreenCapture, callback);
OH_AudioCaptureInfo audioCaptureInfo = {
.audioSampleRate = acgist::samplingRate,
.audioChannels = acgist::channelCount,
.audioSource = OH_MIC
};
OH_AudioInfo audioInfo = {
.micCapInfo = audioCaptureInfo,
};
OH_VideoCaptureInfo videoCaptureInfo = {
// .videoFrameWidth = static_cast<int32_t>(acgist::width),
// .videoFrameHeight = static_cast<int32_t>(acgist::height),
.videoFrameWidth = static_cast<int32_t>(800),
.videoFrameHeight = static_cast<int32_t>(1280),
.videoSource = OH_VIDEO_SOURCE_SURFACE_RGBA
// .videoSource = OH_VIDEO_SOURCE_SURFACE_YUV
};
// OH_VideoEncInfo videoEncInfo = {
// .videoCodec = OH_VideoCodecFormat::OH_H264,
// .videoBitrate = acgist::bitrate,
// .videoFrameRate = acgist::frameRate
// };
OH_VideoInfo videoInfo = {
.videoCapInfo = videoCaptureInfo,
// .videoEncInfo = videoEncInfo
};
OH_AVScreenCaptureConfig config = {
.captureMode = OH_CAPTURE_HOME_SCREEN,
.dataType = OH_ORIGINAL_STREAM,
.audioInfo = audioInfo,
.videoInfo = videoInfo,
};
OH_AVScreenCapture_Init(this->avScreenCapture, config);
OH_AVScreenCapture_SetMicrophoneEnabled(this->avScreenCapture, false);
screenCapturer = this;
}
bool acgist::ScreenCapturer::start() {
OH_LOG_INFO(LOG_APP, "打开屏幕采集");
OH_AVScreenCapture_StartScreenCapture(this->avScreenCapture);
return true;
}
bool acgist::ScreenCapturer::stop() {
OH_LOG_INFO(LOG_APP, "关闭屏幕采集");
OH_AVScreenCapture_StopScreenCapture(this->avScreenCapture);
return true;
}
acgist::ScreenCapturer::~ScreenCapturer() {
OH_AVScreenCapture_Release(this->avScreenCapture);
this->avScreenCapture = nullptr;
}

View File

@@ -2,11 +2,11 @@
namespace acgist {
uint32_t width = 720;
uint32_t width = 640;
uint32_t height = 480;
uint64_t bitrate = 3'000'000L;
uint32_t iFrameInterval = 5'000;
double frameRate = 30.0;
double frameRate = 10.0;
int32_t samplingRate = 48'000;
int32_t channelCount = 2;
int32_t bitsPerSample = 16;

View File

@@ -13,6 +13,8 @@ const permissions: Array<Permissions> = [
"ohos.permission.MICROPHONE",
"ohos.permission.READ_MEDIA",
"ohos.permission.WRITE_MEDIA",
"ohos.permission.CAPTURE_SCREEN",
"ohos.permission.CAPTURE_VOICE_DOWNLINK_AUDIO",
];
function reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext) {

View File

@@ -38,37 +38,47 @@
"name": "ohos.permission.CAMERA",
"reason": "$string:app_name",
"usedScene": {
"when": "inuse"
"when": "inuse",
"abilities": [ "EntryAbility" ]
}
},
{
"name": "ohos.permission.INTERNET",
"reason": "$string:app_name",
"usedScene": {
"when": "always"
}
},
{
"name": "ohos.permission.MICROPHONE",
"reason": "$string:app_name",
"usedScene": {
"when": "inuse"
"when": "inuse",
"abilities": [ "EntryAbility" ]
}
},
{
"name": "ohos.permission.READ_MEDIA",
"reason": "$string:app_name",
"usedScene": {
"when": "always"
"when": "always",
"abilities": [ "EntryAbility" ]
}
},
{
"name": "ohos.permission.WRITE_MEDIA",
"reason": "$string:app_name",
"usedScene": {
"when": "always"
"when": "always",
"abilities": [ "EntryAbility" ]
}
},
// 🤡🤡🤡🤡
{
"name": "ohos.permission.CAPTURE_SCREEN",
"reason": "$string:app_name",
},
{
"name": "ohos.permission.CAPTURE_VOICE_DOWNLINK_AUDIO",
"reason": "$string:app_name",
},
]
}
}