diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/.gitignore b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..d2ff20141ceed86d87c0ea5d99481973005bab2b --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/.gitignore @@ -0,0 +1,12 @@ +/node_modules +/oh_modules +/local.properties +/.idea +**/build +/.hvigor +.cxx +/.clangd +/.clang-format +/.clang-tidy +**/.test +/.appanalyzer \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/app.json5 b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/app.json5 new file mode 100644 index 0000000000000000000000000000000000000000..b409266e20a53ae353c37684b0348f27cb4c832e --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/app.json5 @@ -0,0 +1,10 @@ +{ + "app": { + "bundleName": "com.sample.screencapturesample", + "vendor": "example", + "versionCode": 1000000, + "versionName": "1.0.0", + "icon": "$media:layered_image", + "label": "$string:app_name" + } +} diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/resources/base/element/string.json b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..a0e0f994dc6eee5c453fca24771359bb7ff37c92 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/resources/base/element/string.json @@ -0,0 +1,8 @@ +{ + "string": [ + { + "name": "app_name", + "value": "ScreenCaptureSample" + } + ] +} diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/resources/base/media/background.png b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..923f2b3f27e915d6871871deea0420eb45ce102f Binary files /dev/null and b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/resources/base/media/background.png differ diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/resources/base/media/foreground.png b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..97014d3e10e5ff511409c378cd4255713aecd85f Binary files /dev/null and b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/resources/base/media/foreground.png differ diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/resources/base/media/layered_image.json b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/AppScope/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/README_zh.md b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/README_zh.md new file mode 100644 index 0000000000000000000000000000000000000000..8ac7552432f4590dd73d8845a9699bc54cf4b2da --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/README_zh.md @@ -0,0 +1,81 @@ +# 录屏 Sample + +## 介绍 +录屏 Sample 调用了媒体 AVScreenCapture 组件提供的接口能力,提供屏幕捕获的功能,包含: +- 录屏存文件 +- 录屏取原始码流 +- 录屏取原始码流 Surface 模式 + +## 效果预览 +| 获取录音权限 | 应用主界面 | 隐私弹窗 | 录制过程界面 | +|----------------------|----------------------|----------------------|----------------------| +| ![image](./pic1.jpg) | ![image](./pic2.jpg) | ![image](./pic3.jpg) | ![image](./pic4.jpg) | + +使用说明 + +1. 启动应用,首次启动需要用户授予访问麦克风的权限。 +2. 选择对应的功能页签,点击 Start 按钮,启动屏幕录制。 +3. 启动录制后会弹出隐私弹窗,告知用户将被录屏。 +4. 选择允许后,启动录制后会弹出录制悬浮胶囊,并显示录制时间计时,此时可以操作屏幕,屏幕上的操作过程会被录制下来。 +5. 需要停止录屏时,点击应用停止按钮或点击悬浮半透明的红色按钮,屏幕录制停止。 +6. 录屏存储的文件保存在沙箱目录。 + +## 工程目录 + +仓目录结构如下: + +``` +entry/src/main # 录屏demo业务代码 +│ module.json5 # 编译相关文件 +├─cpp # ndk相关文件 +│ │ CMakeLists.txt +│ │ main.h +│ │ napi_init.cpp +│ │ sample_callback.cpp +│ │ sample_callback.h +│ │ sample_info.h +│ └─types # 映射文件 +│ +├─ets # 页面相关实现 +│ ├─entryability +│ ├─entrybackupability +│ └─pages # ets 页面实现 +│ Index.ets # 首页 +│ Menu.ets # 列表文件 +│ Scene1.ets # 录屏存文件场景 +│ Scene2.ets # 录屏取码流场景 +│ Scene3.ets # 录屏取码流Surface场景 +│ Scene4.ets # 其他场景 +│ +└─resources # 资源文件 +``` + +## 相关权限 + +ohos.permission.MICROPHONE +ohos.permission.KEEP_BACKGROUND_RUNNING +ohos.permission.WRITE_MEDIA + +## 依赖 + +不涉及 + +## 约束和限制 + +1. 本示例支持标准系统上运行,支持设备:RK3568; + +2. 本示例支持API19版本SDK,版本号:5.1.1.62; + +3. 本示例已支持使DevEco Studio 5.0.5 Beta1(构建版本:5.0.13.100,构建于:2025年4月25日)编译运行 + +## 下载 + +如需单独下载本工程,执行如下命令: + +``` +git init +git config core.sparsecheckout true +echo code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/ > .git/info/sparse-checkout +git remote add origin OpenHarmony/applications_app_samples +git pull origin master +``` \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/build-profile.json5 b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..b2ee63d84428b96eccbbfcd28a3c5edeb4199ef1 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/build-profile.json5 @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "app": { + "products": [ + { + "name": "default", + "signingConfig": "default", + "compileSdkVersion": 19, + "compatibleSdkVersion": 19, + "runtimeOS": "OpenHarmony" + } + ], + "buildModeSet": [ + { + "name": "debug" + }, + { + "name": "release" + } + ] + }, + "modules": [ + { + "name": "entry", + "srcPath": "./entry", + "targets": [ + { + "name": "default", + "applyToProducts": [ + "default" + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/code-linter.json5 b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/code-linter.json5 new file mode 100644 index 0000000000000000000000000000000000000000..073990fa45394e1f8e85d85418ee60a8953f9b99 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/code-linter.json5 @@ -0,0 +1,32 @@ +{ + "files": [ + "**/*.ets" + ], + "ignore": [ + "**/src/ohosTest/**/*", + "**/src/test/**/*", + "**/src/mock/**/*", + "**/node_modules/**/*", + "**/oh_modules/**/*", + "**/build/**/*", + "**/.preview/**/*" + ], + "ruleSet": [ + "plugin:@performance/recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "@security/no-unsafe-aes": "error", + "@security/no-unsafe-hash": "error", + "@security/no-unsafe-mac": "warn", + "@security/no-unsafe-dh": "error", + "@security/no-unsafe-dsa": "error", + "@security/no-unsafe-ecdsa": "error", + "@security/no-unsafe-rsa-encrypt": "error", + "@security/no-unsafe-rsa-sign": "error", + "@security/no-unsafe-rsa-key": "error", + "@security/no-unsafe-dsa-key": "error", + "@security/no-unsafe-dh-key": "error", + "@security/no-unsafe-3des": "error" + } +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/.gitignore b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e2713a2779c5a3e0eb879efe6115455592caeea5 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/.gitignore @@ -0,0 +1,6 @@ +/node_modules +/oh_modules +/.preview +/build +/.cxx +/.test \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/build-profile.json5 b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/build-profile.json5 new file mode 100644 index 0000000000000000000000000000000000000000..01bed8940ff6f2d5843f8d1d7e184725f59be699 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/build-profile.json5 @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "apiType": "stageMode", + "buildOption": { + "externalNativeOptions": { + "path": "./src/main/cpp/CMakeLists.txt", + "arguments": "", + "cppFlags": "", + } + }, + "buildOptionSet": [ + { + "name": "release", + "arkOptions": { + "obfuscation": { + "ruleOptions": { + "enable": false, + "files": [ + "./obfuscation-rules.txt" + ] + } + } + }, + "nativeLib": { + "debugSymbol": { + "strip": true, + "exclude": [] + } + } + }, + ], + "targets": [ + { + "name": "default" + }, + { + "name": "ohosTest", + } + ] +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/hvigorfile.ts b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6edcd90486dd5a853cf7d34c8647f08414ca7a3 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/hvigorfile.ts @@ -0,0 +1,6 @@ +import { hapTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/obfuscation-rules.txt b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/obfuscation-rules.txt new file mode 100644 index 0000000000000000000000000000000000000000..272efb6ca3f240859091bbbfc7c5802d52793b0b --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/obfuscation-rules.txt @@ -0,0 +1,23 @@ +# Define project specific obfuscation rules here. +# You can include the obfuscation configuration files in the current module's build-profile.json5. +# +# For more details, see +# https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/source-obfuscation-V5 + +# Obfuscation options: +# -disable-obfuscation: disable all obfuscations +# -enable-property-obfuscation: obfuscate the property names +# -enable-toplevel-obfuscation: obfuscate the names in the global scope +# -compact: remove unnecessary blank spaces and all line feeds +# -remove-log: remove all console.* statements +# -print-namecache: print the name cache that contains the mapping from the old names to new names +# -apply-namecache: reuse the given cache file + +# Keep options: +# -keep-property-name: specifies property names that you want to keep +# -keep-global-name: specifies names that you want to keep in the global scope + +-enable-property-obfuscation +-enable-toplevel-obfuscation +-enable-filename-obfuscation +-enable-export-obfuscation \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/oh-package.json5 b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..118bdd4fe7699368a010e04c24f5bfc887cf1298 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/oh-package.json5 @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "name": "entry", + "version": "1.0.0", + "description": "Please describe the basic information.", + "main": "", + "author": "", + "license": "", + "dependencies": { + "libentry.so": "file:./src/main/cpp/types/libentry" + } +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/CMakeLists.txt b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..93de6dd25e0801bf1810cd6f4e57e2149f802465 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/CMakeLists.txt @@ -0,0 +1,20 @@ +# the minimum version of CMake. +cmake_minimum_required(VERSION 3.5.0) +project(ScreenCaptureSample) + +set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR}) + +if(DEFINED PACKAGE_FIND_FILE) + include(${PACKAGE_FIND_FILE}) +endif() + +include_directories(${NATIVERENDER_ROOT_PATH} + ${NATIVERENDER_ROOT_PATH}/include + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include) + +add_library(entry SHARED napi_init.cpp sample_callback.cpp muxer.cpp) +target_link_libraries(entry PUBLIC libace_napi.z.so libnative_buffer.so libhilog_ndk.z.so librawfile.z.so libnative_avscreen_capture.so + libace_ndk.z.so libnative_media_codecbase.so libnative_media_core.so libnative_media_venc.so libnative_window.so libnative_media_avdemuxer.so + libnative_media_avsource.so libnative_image.so librawfile.z.so libnative_media_avmuxer.so libnative_media_adec.so libnative_media_aenc.so + libnative_media_vdec.so) \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/main.h b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/main.h new file mode 100644 index 0000000000000000000000000000000000000000..ea7b725fa86fb87ec4ff1344624804ce83e43459 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/main.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AVSCREENCAPTURENDKDEMO_MAIN_H +#define AVSCREENCAPTURENDKDEMO_MAIN_H +#endif // AVSCREENCAPTURENDKDEMO_MAIN_H + +#include "napi/native_api.h" +#include +#include +#include +#include +#include +#include +#include "hilog/log.h" +#include +#include "multimedia/player_framework/native_avcodec_videodecoder.h" +#include "multimedia/player_framework/native_avcodec_videoencoder.h" +#include +#include "native_window/external_window.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "sample_callback.h" +#include "muxer.h" + +static struct OH_AVScreenCapture *g_avCapture = {}; +static FILE *micFile_ = nullptr; +static FILE *vFile_ = nullptr; +static FILE *innerFile_ = nullptr; +std::unique_ptr g_muxer; + +static char filename[100] = {0}; +bool m_isRunning = false; +bool m_scSaveFileIsRunning = false; +bool m_scSurfaceIsRunning = false; +OH_AVCodec *g_videoEnc; +CodecUserData *g_encContext = nullptr; +SampleInfo sampleInfo_; +std::unique_ptr inputVideoThread_; +std::atomic isStarted_{false}; + +constexpr uint32_t DEFAULT_WIDTH = 4096; +// 配置视频帧高度(必须) +constexpr uint32_t DEFAULT_HEIGHT = 4096; +// 配置视频颜色格式(必须) +constexpr OH_AVPixelFormat DEFAULT_PIXELFORMAT = AV_PIXEL_FORMAT_NV12; \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/muxer.cpp b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/muxer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..83b830fbd2ab50726f8d62cf5a35291f5f06426d --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/muxer.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "muxer.h" +#include "hilog/log.h" + +#undef LOG_TAG +#define LOG_TAG "Muxer" + +namespace { +constexpr int32_t VERTICAL_ANGLE = 90; +constexpr int32_t HORIZONTAL_ANGLE = 0; +} + +Muxer::~Muxer() +{ + Release(); +} + +int32_t Muxer::Create(int32_t fd) +{ + muxer_ = OH_AVMuxer_Create(fd, AV_OUTPUT_FORMAT_MPEG_4); + return 0; +} + +int32_t Muxer::Config(SampleInfo &sampleInfo) +{ + OH_LOG_INFO(LOG_APP, "==DEMO== Config"); + OH_AVFormat *formatVideo = OH_AVFormat_CreateVideoFormat(sampleInfo.codecMime.data(), + sampleInfo.videoWidth, sampleInfo.videoHeight); + + OH_AVFormat_SetDoubleValue(formatVideo, OH_MD_KEY_FRAME_RATE, sampleInfo.frameRate); + OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_WIDTH, sampleInfo.videoWidth); + OH_AVFormat_SetIntValue(formatVideo, OH_MD_KEY_HEIGHT, sampleInfo.videoHeight); + OH_AVFormat_SetStringValue(formatVideo, OH_MD_KEY_CODEC_MIME, sampleInfo.codecMime.data()); + + int32_t ret = OH_AVMuxer_AddTrack(muxer_, &videoTrackId_, formatVideo); + OH_AVFormat_Destroy(formatVideo); + OH_LOG_INFO(LOG_APP, "==DEMO== Config End"); + return 0; +} + +int32_t Muxer::Start() +{ + int ret = OH_AVMuxer_Start(muxer_); + return 0; +} + +int32_t Muxer::WriteSample(OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr) +{ + int32_t ret = OH_AVBuffer_SetBufferAttr(buffer, &attr); + ret = OH_AVMuxer_WriteSampleBuffer(muxer_, videoTrackId_, buffer); + return 0; +} + +int32_t Muxer::Stop() +{ + int32_t ret = OH_AVMuxer_Stop(muxer_); + return 0; +} + +int32_t Muxer::Release() +{ + if (muxer_ != nullptr) { + OH_AVMuxer_Destroy(muxer_); + muxer_ = nullptr; + } + return 0; +} diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/muxer.h b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/muxer.h new file mode 100644 index 0000000000000000000000000000000000000000..9930a14a1ee2d17ed96cfecc754cb0c5b9bbc87c --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/muxer.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MUXER_H +#define MUXER_H +#define LOG_DOMAIN 0x3200 +#define LOG_TAG "MY_SCNDKDEMO" +#include +#include "sample_info.h" +#include "multimedia/player_framework/native_avmuxer.h" + +class Muxer { +public: + Muxer() = default; + ~Muxer(); + + int32_t Create(int32_t fd); + int32_t Config(SampleInfo &sampleInfo); + int32_t Start(); + int32_t WriteSample(OH_AVBuffer *buffer, OH_AVCodecBufferAttr &attr); + + int32_t Stop(); + int32_t Release(); + +private: + OH_AVMuxer *muxer_ = nullptr; + int32_t videoTrackId_ = -1; + int32_t audioTrackId_ = -1; + int32_t coverTrackId_ = -1; +}; + +#endif // MUXER_H \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/napi_init.cpp b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/napi_init.cpp new file mode 100644 index 0000000000000000000000000000000000000000..999058144c72cf0fa80c3f5b33a1e7707820b41c --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/napi_init.cpp @@ -0,0 +1,567 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "main.h" + +using namespace std; +#define LOG_TAG "MY_TAG" + +static napi_value Add(napi_env env, napi_callback_info info) { + size_t requireArgc = 2; + size_t argc = 2; + napi_value args[2] = {nullptr}; + + napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); + + napi_valuetype valuetype0; + napi_typeof(env, args[0], &valuetype0); + + napi_valuetype valuetype1; + napi_typeof(env, args[1], &valuetype1); + + double value0; + napi_get_value_double(env, args[0], &value0); + + double value1; + napi_get_value_double(env, args[1], &value1); + + napi_value sum; + napi_create_double(env, value0 + value1, &sum); + return sum; +} + +void OpenFile(std::string filename_) { + snprintf(filename, sizeof(filename), "data/storage/el2/base/files/MIC_%s.pcm", filename_.c_str()); + micFile_ = fopen(filename, "wb"); + if (micFile_ == nullptr) { + // OH_LOG_ERROR(LOG_APP, "OpenFile micFile_ audio open failed. %{public}s", strerror(errno)); + } + snprintf(filename, sizeof(filename), "data/storage/el2/base/files/INNER_%s.pcm", filename_.c_str()); + innerFile_ = fopen(filename, "wb"); + if (innerFile_ == nullptr) { + // OH_LOG_ERROR(LOG_APP, "OpenFile innerFile_ audio open failed. %{public}s", strerror(errno)); + } + snprintf(filename, sizeof(filename), "data/storage/el2/base/files/VIDEO_%s.yuv", filename_.c_str()); + vFile_ = fopen(filename, "wb"); + if (vFile_ == nullptr) { + // OH_LOG_ERROR(LOG_APP, "OpenFile vFile video open failed. %{public}s", strerror(errno)); + } +} + +void CloseFile(void) { + if (micFile_ != nullptr) { + fclose(micFile_); + micFile_ = nullptr; + } + if (innerFile_ != nullptr) { + fclose(innerFile_); + innerFile_ = nullptr; + } + if (vFile_ != nullptr) { + fclose(vFile_); + vFile_ = nullptr; + } +} + +void SetConfig(OH_AVScreenCaptureConfig &config) { + int32_t width = 720; + int32_t height = 1280; + OH_AudioCaptureInfo micCapInfo = {.audioSampleRate = 48000, .audioChannels = 2, .audioSource = OH_MIC}; + OH_AudioCaptureInfo innerCapInfo = {.audioSampleRate = 48000, .audioChannels = 2, .audioSource = OH_ALL_PLAYBACK}; + OH_AudioEncInfo audioEncInfo = {.audioBitrate = 48000, .audioCodecformat = OH_AudioCodecFormat::OH_AAC_LC}; + OH_AudioInfo audioInfo = {.micCapInfo = micCapInfo, .innerCapInfo = innerCapInfo, .audioEncInfo = audioEncInfo}; + + OH_VideoCaptureInfo videoCapInfo = { + .videoFrameWidth = width, .videoFrameHeight = height, .videoSource = OH_VIDEO_SOURCE_SURFACE_RGBA}; + OH_VideoEncInfo videoEncInfo = { + .videoCodec = OH_VideoCodecFormat::OH_H264, .videoBitrate = 2000000, .videoFrameRate = 30}; + OH_VideoInfo videoInfo = {.videoCapInfo = videoCapInfo, .videoEncInfo = videoEncInfo}; + + config = { + .captureMode = OH_CAPTURE_HOME_SCREEN, + .dataType = OH_ORIGINAL_STREAM, + .audioInfo = audioInfo, + .videoInfo = videoInfo, + }; +} + +void OnError(OH_AVScreenCapture *capture, int32_t errorCode, void *userData) { + (void)capture; + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture OnError errorCode is %{public}d", errorCode); + (void)userData; +} + +void OnStateChange(struct OH_AVScreenCapture *capture, OH_AVScreenCaptureStateCode stateCode, void *userData) { + if (stateCode == OH_SCREEN_CAPTURE_STATE_STARTED) { + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture OnStateChange started"); + // 处理状态变更 + // 可选 配置录屏旋转 + int32_t ret = OH_AVScreenCapture_SetCanvasRotation(capture, true); + // 可选 修改Canvas分辨率 + ret = OH_AVScreenCapture_ResizeCanvas(g_avCapture, 768, 1280); + // 可选 设置是否显示光标 + ret = OH_AVScreenCapture_ShowCursor(g_avCapture, true); + // 可选 设置视频最大帧率 + ret = OH_AVScreenCapture_SetMaxVideoFrameRate(g_avCapture, 30); + } + if (stateCode == OH_SCREEN_CAPTURE_STATE_INTERRUPTED_BY_OTHER) { + // 处理状态变更 + } + (void)userData; +} + +void OnBufferAvailable(OH_AVScreenCapture *capture, OH_AVBuffer *buffer, OH_AVScreenCaptureBufferType bufferType, + int64_t timestamp, void *userData) { + if (m_isRunning) { + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture OnBufferAvailable bufferType is %{public}d", bufferType); + if (bufferType == OH_SCREEN_CAPTURE_BUFFERTYPE_VIDEO) { + // 处理视频buffer + OH_NativeBuffer *nativebuffer = OH_AVBuffer_GetNativeBuffer(buffer); + if (nativebuffer != nullptr) { + int bufferLen = OH_AVBuffer_GetCapacity(buffer); + OH_AVCodecBufferAttr info; + int32_t ret = OH_AVBuffer_GetBufferAttr(buffer, &info); + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture size %{public}d", info.size); + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture bufferLen %{public}d", bufferLen); + + OH_NativeBuffer_Config config; + OH_NativeBuffer_GetConfig(nativebuffer, &config); + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture height %{public}d width %{public}d", config.height, + config.width); + // int32_t length = config.height * config.width * 4; + uint8_t *buf = OH_AVBuffer_GetAddr(buffer); + if (buf == nullptr) { + return; + } + fwrite(buf, 1, bufferLen, vFile_); + OH_NativeBuffer_Unreference(nativebuffer); + buffer = nullptr; + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture OnBufferAvailable inner audio"); + } + } else if (bufferType == OH_SCREEN_CAPTURE_BUFFERTYPE_AUDIO_INNER) { + // 处理内录buffer + int bufferLen = OH_AVBuffer_GetCapacity(buffer); + uint8_t *buf = OH_AVBuffer_GetAddr(buffer); + if (buf != nullptr) { + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture OnBufferAvailable inner audio"); + fwrite(buf, 1, bufferLen, innerFile_); + } + } else if (bufferType == OH_SCREEN_CAPTURE_BUFFERTYPE_AUDIO_MIC) { + // 处理麦克风buffer + int bufferLen = OH_AVBuffer_GetCapacity(buffer); + uint8_t *buf = OH_AVBuffer_GetAddr(buffer); + if (buf != nullptr) { + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture OnBufferAvailable mic audio"); + fwrite(buf, 1, bufferLen, micFile_); + } + } + } + return; +} + +void OnDisplaySelected(struct OH_AVScreenCapture *capture, uint64_t displayId, void *userData) +{ + (void)capture; + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture OnError errorCode is %{public}uld", displayId); + (void)userData; +} + +// 开始录屏原始码流 +static napi_value StartScreenCapture_01(napi_env env, napi_callback_info info) { + g_avCapture = OH_AVScreenCapture_Create(); + if (g_avCapture == nullptr) { + OH_LOG_ERROR(LOG_APP, "create screen capture failed"); + } + OH_AVScreenCaptureConfig config_; + SetConfig(config_); + OpenFile("Demo"); + bool isMicrophone = true; + OH_AVScreenCapture_SetMicrophoneEnabled(g_avCapture, isMicrophone); + OH_AVScreenCapture_SetErrorCallback(g_avCapture, OnError, nullptr); + OH_AVScreenCapture_SetStateCallback(g_avCapture, OnStateChange, nullptr); + OH_AVScreenCapture_SetDataCallback(g_avCapture, OnBufferAvailable, nullptr); + OH_AVScreenCapture_SetDisplayCallback(g_avCapture, OnDisplaySelected, nullptr); + // 可选,排除指定窗口/指定音频类型 start + struct OH_AVScreenCapture_ContentFilter *contentFilter = OH_AVScreenCapture_CreateContentFilter(); + OH_AVScreenCapture_ContentFilter_AddAudioContent(contentFilter, OH_SCREEN_CAPTURE_NOTIFICATION_AUDIO); + vector windowIdsExclude = { -111 }; + OH_AVScreenCapture_ContentFilter_AddWindowContent(contentFilter, + &windowIdsExclude[0], static_cast(windowIdsExclude.size())); + OH_AVScreenCapture_ExcludeContent(g_avCapture, contentFilter); + OH_AVScreenCapture_SkipPrivacyMode(g_avCapture, + &windowIdsExclude[0], static_cast(windowIdsExclude.size())); + OH_AVScreenCapture_ReleaseContentFilter(contentFilter); + // 可选,排除指定窗口/指定音频类型 end + int result = OH_AVScreenCapture_Init(g_avCapture, config_); + if (result != AV_SCREEN_CAPTURE_ERR_OK) { + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture OH_AVScreenCapture_Init failed %{public}d", result); + } + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture OH_AVScreenCapture_Init %{public}d", result); + + result = OH_AVScreenCapture_StartScreenCapture(g_avCapture); + if (result != AV_SCREEN_CAPTURE_ERR_OK) { + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture Started failed %{public}d", result); + OH_AVScreenCapture_Release(g_avCapture); + } + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture Started %{public}d", result); + + m_isRunning = true; + + napi_value res; + napi_create_int32(env, result, &res); + return res; +} + +// 开始录屏存文件 +static napi_value StartScreenCapture_02(napi_env env, napi_callback_info info) { + g_avCapture = OH_AVScreenCapture_Create(); + if (g_avCapture == nullptr) { + OH_LOG_ERROR(LOG_APP, "create screen capture failed"); + } + OH_AVScreenCaptureConfig config_; + + OH_RecorderInfo recorderInfo; + const std::string SCREEN_CAPTURE_ROOT = "/data/storage/el2/base/files/"; + int32_t outputFd = open((SCREEN_CAPTURE_ROOT + "saving_file.mp4").c_str(), O_RDWR | O_CREAT, 0777); + std::string fileUrl = "fd://" + std::to_string(outputFd); + recorderInfo.url = const_cast(fileUrl.c_str()); + recorderInfo.fileFormat = OH_ContainerFormatType::CFT_MPEG_4; + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture fileUrl %{public}s", fileUrl.c_str()); + + SetConfig(config_); + config_.captureMode = OH_CAPTURE_HOME_SCREEN; + config_.dataType = OH_CAPTURE_FILE; + config_.recorderInfo = recorderInfo; + bool isMicrophone = true; + OH_AVScreenCapture_SetMicrophoneEnabled(g_avCapture, isMicrophone); + OH_AVScreenCapture_SetStateCallback(g_avCapture, OnStateChange, nullptr); + OH_AVScreenCapture_SetDisplayCallback(g_avCapture, OnDisplaySelected, nullptr); + OH_AVSCREEN_CAPTURE_ErrCode result = OH_AVScreenCapture_Init(g_avCapture, config_); + if (result != AV_SCREEN_CAPTURE_ERR_OK) { + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture OH_AVScreenCapture_Init failed %{public}d", result); + } + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture OH_AVScreenCapture_Init %{public}d", result); + + result = OH_AVScreenCapture_StartScreenRecording(g_avCapture); + if (result != AV_SCREEN_CAPTURE_ERR_OK) { + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture Started failed %{public}d", result); + OH_AVScreenCapture_Release(g_avCapture); + } + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture Started %{public}d", result); + + m_scSaveFileIsRunning = true; + napi_value res; + napi_create_int32(env, result, &res); + return res; +} + +// 开始录屏原始码流SurfaceMode +void ThreadVideoRunMethod() { + while (m_scSurfaceIsRunning) { + OH_LOG_INFO(LOG_APP, "==DEMO== ThreadVideoRunMethod m_scSurfaceIsRunning %{public}d", m_scSurfaceIsRunning); + if (!isStarted_.load()) { + return; + } + std::unique_lock lock(g_encContext->outputMutex_); + bool condRet = g_encContext->outputCond_.wait_for( + lock, 2s, [&]() { return !isStarted_.load() || !g_encContext->outputBufferInfoQueue_.empty(); }); + if (!isStarted_.load()) { + return; + } + if (g_encContext->outputBufferInfoQueue_.empty()) { + continue; + } + CodecBufferInfo bufferInfo = g_encContext->outputBufferInfoQueue_.front(); + g_encContext->outputBufferInfoQueue_.pop(); + if (bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_EOS) { + lock.unlock(); + break; + } + lock.unlock(); + if (bufferInfo.buffer != nullptr) { + bufferInfo.attr.pts = (bufferInfo.attr.flags & AVCODEC_BUFFER_FLAGS_CODEC_DATA) + ? 0 + : (g_encContext->outputFrameCount_++ * 1000000 / sampleInfo_.frameRate); + g_muxer->WriteSample(reinterpret_cast(bufferInfo.buffer), bufferInfo.attr); + } + OH_VideoEncoder_FreeOutputBuffer(g_videoEnc, bufferInfo.bufferIndex); + } +} + +static napi_value StartScreenCapture_03(napi_env env, napi_callback_info info) { + isStarted_.store(false); + inputVideoThread_ = nullptr; + g_encContext = nullptr; + g_avCapture = OH_AVScreenCapture_Create(); + if (g_avCapture == nullptr) { + OH_LOG_ERROR(LOG_APP, "create screen capture failed"); + } + OH_AVScreenCaptureConfig config_; + SetConfig(config_); + bool isMicrophone = false; + OH_AVScreenCapture_SetMicrophoneEnabled(g_avCapture, isMicrophone); + OH_AVScreenCapture_SetErrorCallback(g_avCapture, OnError, nullptr); + OH_AVScreenCapture_SetStateCallback(g_avCapture, OnStateChange, nullptr); + OH_AVScreenCapture_SetDataCallback(g_avCapture, OnBufferAvailable, nullptr); + OH_AVScreenCapture_SetDisplayCallback(g_avCapture, OnDisplaySelected, nullptr); + int result = OH_AVScreenCapture_Init(g_avCapture, config_); + if (result != AV_SCREEN_CAPTURE_ERR_OK) { + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture OH_AVScreenCapture_Init failed %{public}d", result); + } + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture OH_AVScreenCapture_Init %{public}d", result); + + // 获取需要输入的Surface,以进行编码 + OH_AVCapability *capability = OH_AVCodec_GetCapability(OH_AVCODEC_MIMETYPE_VIDEO_AVC, true); + const char *name = OH_AVCapability_GetName(capability); + g_videoEnc = OH_VideoEncoder_CreateByName(name); + g_muxer = std::make_unique(); + const std::string SCREEN_CAPTURE_ROOT = "/data/storage/el2/base/files/"; + int32_t outputFd = open((SCREEN_CAPTURE_ROOT + "surface.mp4").c_str(), O_RDWR | O_CREAT, 0777); + g_muxer->Create(outputFd); + g_encContext = new CodecUserData; + g_encContext->sampleInfo = &sampleInfo_; + // 配置异步回调,调用 OH_VideoEncoder_SetCallback 接口 + OH_VideoEncoder_RegisterCallback(g_videoEnc, + {SampleCallback::OnError, SampleCallback::OnStreamChanged, + SampleCallback::OnNeedInputBuffer, SampleCallback::OnNewOutputBuffer}, + g_encContext); + OH_AVFormat *format = OH_AVFormat_Create(); + // 配置视频帧速率 + double frameRate = 30.0; + // 配置视频YUV值范围标志 + bool rangeFlag = false; + // 配置视频原色 + int32_t primary = static_cast(OH_ColorPrimary::COLOR_PRIMARY_BT709); + // 配置传输特性 + int32_t transfer = static_cast(OH_TransferCharacteristic::TRANSFER_CHARACTERISTIC_BT709); + // 配置最大矩阵系数 + int32_t matrix = static_cast(OH_MatrixCoefficient::MATRIX_COEFFICIENT_IDENTITY); + // 配置编码Profile + int32_t profile = static_cast(OH_AVCProfile::AVC_PROFILE_BASELINE); + // 配置编码比特率模式 + int32_t rateMode = static_cast(OH_VideoEncodeBitrateMode::CBR); + // 配置关键帧的间隔,单位为毫秒 + int32_t iFrameInterval = 23000; + // 配置所需的编码质量。只有在恒定质量模式下配置的编码器才支持此配置 + int32_t quality = 0; + // 配置比特率 + int64_t bitRate = 3000000; + OH_AVFormat_SetIntValue(format, OH_MD_KEY_WIDTH, DEFAULT_WIDTH); + sampleInfo_.videoWidth = DEFAULT_WIDTH; + OH_AVFormat_SetIntValue(format, OH_MD_KEY_HEIGHT, DEFAULT_HEIGHT); + sampleInfo_.videoHeight = DEFAULT_HEIGHT; + OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, DEFAULT_PIXELFORMAT); + OH_AVFormat_SetDoubleValue(format, OH_MD_KEY_FRAME_RATE, frameRate); + sampleInfo_.frameRate = frameRate; + sampleInfo_.videoCodecMime = sampleInfo_.codecMime.data(); + OH_AVFormat_SetIntValue(format, OH_MD_KEY_RANGE_FLAG, rangeFlag); + OH_AVFormat_SetIntValue(format, OH_MD_KEY_COLOR_PRIMARIES, primary); + OH_AVFormat_SetIntValue(format, OH_MD_KEY_TRANSFER_CHARACTERISTICS, transfer); + OH_AVFormat_SetIntValue(format, OH_MD_KEY_MATRIX_COEFFICIENTS, matrix); + OH_AVFormat_SetIntValue(format, OH_MD_KEY_I_FRAME_INTERVAL, iFrameInterval); + OH_AVFormat_SetIntValue(format, OH_MD_KEY_PROFILE, profile); + OH_AVFormat_SetIntValue(format, OH_MD_KEY_VIDEO_ENCODE_BITRATE_MODE, rateMode); + OH_AVFormat_SetLongValue(format, OH_MD_KEY_BITRATE, bitRate); + OH_AVFormat_SetIntValue(format, OH_MD_KEY_QUALITY, quality); + result = OH_VideoEncoder_Configure(g_videoEnc, format); + OH_LOG_INFO(LOG_APP, "==DEMO== OH_VideoEncoder_Configure ret=%{public}d", result); + OH_AVFormat_Destroy(format); + + // 从视频编码器获取输入Surface + OHNativeWindow *nativeWindow; + result = OH_VideoEncoder_GetSurface(g_videoEnc, &nativeWindow); + if (result != AV_ERR_OK) { + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture Started OH_VideoEncoder_GetSurface ret=%{public}d", result); + } + result = OH_VideoEncoder_Prepare(g_videoEnc); + g_muxer->Config(sampleInfo_); + g_muxer->Start(); + // 启动编码器 + int32_t retEnc = OH_VideoEncoder_Start(g_videoEnc); + isStarted_.store(true); + m_scSurfaceIsRunning = true; + inputVideoThread_ = std::make_unique(ThreadVideoRunMethod); + // 指定surface开始录屏 + result = OH_AVScreenCapture_StartScreenCaptureWithSurface(g_avCapture, nativeWindow); + if (result != AV_SCREEN_CAPTURE_ERR_OK) { + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture Started failed %{public}d", result); + OH_AVScreenCapture_Release(g_avCapture); + } + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture Started %{public}d", result); + napi_value res; + napi_create_int32(env, result, &res); + return res; +} + +// 停止 +static napi_value StopScreenCapture(napi_env env, napi_callback_info info) { + OH_LOG_INFO(LOG_APP, "==DEMO== ScreenCapture Stop"); + OH_AVSCREEN_CAPTURE_ErrCode result = AV_SCREEN_CAPTURE_ERR_OPERATE_NOT_PERMIT; + napi_value res; + if (m_scSurfaceIsRunning) { + int32_t ret = OH_VideoEncoder_NotifyEndOfStream(g_videoEnc); + if (ret != AV_ERR_OK) { + // 异常处理 + } + ret = OH_VideoEncoder_Stop(g_videoEnc); + if (ret != AV_ERR_OK) { + // 异常处理 + } + ret = OH_VideoEncoder_Destroy(g_videoEnc); + if (ret != AV_ERR_OK) { + // 异常处理 + } + g_videoEnc = nullptr; + g_muxer->Stop(); + m_scSurfaceIsRunning = false; + isStarted_.store(false); + if (inputVideoThread_ && inputVideoThread_->joinable()) { + inputVideoThread_->join(); + } + } + + if (m_scSaveFileIsRunning) { + if (g_avCapture == nullptr) { + OH_LOG_ERROR(LOG_APP, "capture_ is null."); + } + result = OH_AVScreenCapture_StopScreenRecording(g_avCapture); + if (result != AV_SCREEN_CAPTURE_ERR_BASE) { + OH_LOG_ERROR(LOG_APP, "StopScreenCapture OH_AVScreenCapture_StopScreenCapture Result: %{public}d", result); + } + OH_LOG_INFO(LOG_APP, "StopScreenCapture OH_AVScreenCapture_StopScreenCapture"); + result = OH_AVScreenCapture_Release(g_avCapture); + if (result != AV_SCREEN_CAPTURE_ERR_BASE) { + OH_LOG_ERROR(LOG_APP, "StopScreenCapture OH_AVScreenCapture_Release: %{public}d", result); + } + OH_LOG_INFO(LOG_APP, "OH_AVScreenCapture_Release success"); + m_scSaveFileIsRunning = false; + } else { + if (g_avCapture == nullptr) { + OH_LOG_ERROR(LOG_APP, "capture_ is null."); + } + result = OH_AVScreenCapture_StopScreenCapture(g_avCapture); + if (result != AV_SCREEN_CAPTURE_ERR_BASE) { + OH_LOG_ERROR(LOG_APP, "StopScreenCapture OH_AVScreenCapture_StopScreenCapture Result: %{public}d", result); + } + OH_LOG_INFO(LOG_APP, "StopScreenCapture OH_AVScreenCapture_StopScreenCapture"); + result = OH_AVScreenCapture_Release(g_avCapture); + if (result != AV_SCREEN_CAPTURE_ERR_BASE) { + OH_LOG_ERROR(LOG_APP, "StopScreenCapture OH_AVScreenCapture_Release: %{public}d", result); + } + OH_LOG_INFO(LOG_APP, "OH_AVScreenCapture_Release success"); + CloseFile(); + OH_LOG_INFO(LOG_APP, "CloseFile success"); + m_isRunning = false; + } + + napi_create_int32(env, result, &res); + return res; +} + + +// 调用老接口,仅提供代码实现,不建议使用 +void MockOnAudioBufferAvailable(OH_AVScreenCapture *screenCapture, bool isReady, OH_AudioCaptureSourceType type) +{ + if (isReady == true) { + OH_AudioBuffer *audioBuffer = (OH_AudioBuffer *)malloc(sizeof(OH_AudioBuffer)); + if (audioBuffer == nullptr) { + OH_LOG_INFO(LOG_APP, "audio buffer is nullptr"); + return; + } + if (OH_AVScreenCapture_AcquireAudioBuffer(screenCapture, &audioBuffer, type) == AV_SCREEN_CAPTURE_ERR_OK) { + if ((micFile_ != nullptr) && (audioBuffer->buf != nullptr) && (type == OH_MIC)) { + int32_t ret = fwrite(audioBuffer->buf, 1, audioBuffer->size, micFile_); + free(audioBuffer->buf); + audioBuffer->buf = nullptr; + } else if ((innerFile_ != nullptr) && (audioBuffer->buf != nullptr) && (type == OH_ALL_PLAYBACK)) { + int32_t ret = fwrite(audioBuffer->buf, 1, audioBuffer->size, innerFile_); + free(audioBuffer->buf); + audioBuffer->buf = nullptr; + } + free(audioBuffer); + audioBuffer = nullptr; + } + // do something + OH_AVScreenCapture_ReleaseAudioBuffer(screenCapture, type); + } else { + OH_LOG_INFO(LOG_APP, "AcquireAudioBuffer failed"); + } +} + +// 调用老接口,仅提供代码实现,不建议使用 +void MockOnVideoBufferAvailable(OH_AVScreenCapture *screenCapture, bool isReady) +{ + if (isReady == true) { + int32_t fence = 0; + int64_t timestamp = 0; + int32_t size = 4; + OH_Rect damage; + OH_NativeBuffer_Config config; + OH_NativeBuffer *nativeBuffer = + OH_AVScreenCapture_AcquireVideoBuffer(screenCapture, &fence, ×tamp, &damage); + if (nativeBuffer != nullptr) { + OH_NativeBuffer_GetConfig(nativeBuffer, &config); + int32_t length = config.height * config.width * size; + OH_NativeBuffer_Unreference(nativeBuffer); + } else { + OH_LOG_INFO(LOG_APP, "AcquireVideoBuffer failed"); + } + // do something + OH_AVScreenCapture_ReleaseVideoBuffer(screenCapture); + } +} + +// 调用老接口,仅提供代码实现,不建议使用 +void MockOnError(OH_AVScreenCapture *screenCapture, int32_t errorCode) +{ + // 错误处理 +} + +// 调用老接口,仅提供代码实现,不建议使用 +void SetScreenCaptureCallback() +{ + struct OH_AVScreenCaptureCallback callback; + callback.onAudioBufferAvailable = MockOnAudioBufferAvailable; + callback.onVideoBufferAvailable = MockOnVideoBufferAvailable; + callback.onError = MockOnError; + OH_AVScreenCapture_SetCallback(g_avCapture, callback); +} + +EXTERN_C_START +static napi_value Init(napi_env env, napi_value exports) { + napi_property_descriptor desc[] = { + {"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"startScreenCapture", nullptr, StartScreenCapture_01, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"stopScreenCapture", nullptr, StopScreenCapture, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"startCaptureAsFile", nullptr, StartScreenCapture_02, nullptr, nullptr, nullptr, napi_default, nullptr}, + {"startScreenCaptureWithSurface", nullptr, StartScreenCapture_03, nullptr, nullptr, nullptr, napi_default, + nullptr}, + }; + napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc); + return exports; +} +EXTERN_C_END + +static napi_module demoModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_register_func = Init, + .nm_modname = "entry", + .nm_priv = ((void *)0), + .reserved = {0}, +}; + +extern "C" __attribute__((constructor)) void RegisterScreencaptureModule(void) { napi_module_register(&demoModule); } diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/sample_callback.cpp b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/sample_callback.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b1383676595a839ab1f0cc3a9741e04807c1e956 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/sample_callback.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sample_callback.h" +#include "hilog/log.h" + +namespace { +constexpr int LIMIT_LOGD_FREQUENCY = 50; +} + +// 设置 OnError 回调函数 +void SampleCallback::OnError(OH_AVCodec *codec, int32_t errorCode, void *userData) { + // 回调的错误码由用户判断处理 + (void)codec; + (void)errorCode; + (void)userData; + OH_LOG_ERROR(LOG_APP, "On error, error code: %{public}d", errorCode); +} + +// 设置 OnStreamChanged 回调函数 +void SampleCallback::OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData) { +// surface模式下,该回调函数无作用 + (void)codec; + (void)format; + (void)userData; +} + +// 设置 OH_AVCodecOnNeedInputBuffer 回调函数,编码输入帧送入数据队列 +void SampleCallback::OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) { +// surface模式下,该回调函数无作用,用户通过获取的surface输入数据 + OH_LOG_INFO(LOG_APP, "==DEMO== Video OnNeedInputBuffer"); + (void)userData; + (void)index; + (void)buffer; + if (userData == nullptr) { + return; + } + (void)codec; + CodecUserData *codecUserData = static_cast(userData); + std::unique_lock lock(codecUserData->inputMutex_); + codecUserData->inputBufferInfoQueue_.emplace(index, buffer); + codecUserData->inputCond_.notify_all(); +} + +// 设置 OH_AVCodecOnNewOutputBuffer 回调函数,编码完成帧送入输出队列 +void SampleCallback::OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData) { + // 完成帧buffer对应的index,送入outIndexQueue队列 + // 完成帧的数据buffer送入outBufferQueue队列 + // 数据处理,请参考: + // - 释放编码帧 + OH_LOG_INFO(LOG_APP, "==DEMO== Video OnNewOutputBuffer"); + if (userData == nullptr) { + return; + } + (void)codec; + CodecUserData *codecUserData = static_cast(userData); + std::unique_lock lock(codecUserData->outputMutex_); + codecUserData->outputBufferInfoQueue_.emplace(index, buffer); + codecUserData->outputCond_.notify_all(); +} diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/sample_callback.h b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/sample_callback.h new file mode 100644 index 0000000000000000000000000000000000000000..2b6be13ee1c0caf12e124c2a9131c80efa26e870 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/sample_callback.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AVCODEC_SAMPLE_CALLBACK_H +#define AVCODEC_SAMPLE_CALLBACK_H +#define LOG_DOMAIN 0x3200 +#define LOG_TAG "MY_SCNDKDEMO" + +#include "sample_info.h" +#include +class SampleCallback { +public: + SampleCallback() {} + SampleCallback(SampleCallback *p1) {} + static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData); + static void OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData); + static void OnNeedInputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData); + static void OnNewOutputBuffer(OH_AVCodec *codec, uint32_t index, OH_AVBuffer *buffer, void *userData); + + OH_AVMuxer *muxer_ = nullptr; + int32_t g_videoTrackId = -1; +}; + +#endif //AVCODEC_SAMPLE_CALLBACK_H diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/sample_info.h b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/sample_info.h new file mode 100644 index 0000000000000000000000000000000000000000..9f7726786a4658be8cccec7b60af95f9b1beda6e --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/sample_info.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AVCODEC_SAMPLE_INFO_H +#define AVCODEC_SAMPLE_INFO_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ANNEXB_INPUT_ONLY 1 + +const std::string_view MIME_VIDEO_AVC = "video/avc"; +const std::string_view MIME_VIDEO_HEVC = "video/hevc"; + +constexpr int32_t BITRATE_10M = 10 * 1024 * 1024; // 10Mbps +constexpr int32_t BITRATE_20M = 20 * 1024 * 1024; // 20Mbps +constexpr int32_t BITRATE_30M = 30 * 1024 * 1024; // 30Mbps + +struct SampleInfo { + int32_t sampleId = 0; + + int32_t inputFd = -1; + int32_t outFd = -1; + int64_t inputFileOffset = 0; + int64_t inputFileSize = 0; + std::string inputFilePath; + std::string outputFilePath; + std::string videoCodecMime = ""; + std::string audioCodecMime = ""; + std::string codecMime = MIME_VIDEO_AVC.data(); + int32_t videoWidth = 0; + int32_t videoHeight = 0; + double frameRate = 0.0; + int64_t bitrate = 10 * 1024 * 1024; // 10Mbps; + int64_t frameInterval = 0; + int32_t perfmode = 0; + int64_t durationTime = 0; + uint32_t maxFrames = UINT32_MAX; + int32_t isHDRVivid = 0; + uint32_t repeatTimes = 1; + OH_AVPixelFormat pixelFormat = AV_PIXEL_FORMAT_NV12; + bool needDumpOutput = false; + uint32_t bitrateMode = CBR; + int32_t hevcProfile = HEVC_PROFILE_MAIN; + int32_t rotation = 0; + OHNativeWindow *window = nullptr; + + int32_t sampleRate = 44100; + int32_t channelCount = 2; + + uint32_t bufferSize = 0; + double readTime = 0; + double memcpyTime = 0; + double writeTime = 0; + void (*PlayDoneCallback)(void *context) = nullptr; + void *playDoneCallbackData = nullptr; +}; + +struct CodecBufferInfo { + uint32_t bufferIndex = 0; + uintptr_t *buffer = nullptr; + uint8_t *bufferAddr = nullptr; + OH_AVCodecBufferAttr attr = {0, 0, 0, AVCODEC_BUFFER_FLAGS_NONE}; + + CodecBufferInfo(uint8_t *addr) : bufferAddr(addr){}; + CodecBufferInfo(uint8_t *addr, int32_t bufferSize) + : bufferAddr(addr), attr({0, bufferSize, 0, AVCODEC_BUFFER_FLAGS_NONE}){}; + CodecBufferInfo(uint32_t argBufferIndex, OH_AVMemory *argBuffer, OH_AVCodecBufferAttr argAttr) + : bufferIndex(argBufferIndex), buffer(reinterpret_cast(argBuffer)), attr(argAttr){}; + CodecBufferInfo(uint32_t argBufferIndex, OH_AVMemory *argBuffer) + : bufferIndex(argBufferIndex), buffer(reinterpret_cast(argBuffer)){}; + CodecBufferInfo(uint32_t argBufferIndex, OH_AVBuffer *argBuffer) + : bufferIndex(argBufferIndex), buffer(reinterpret_cast(argBuffer)) { + OH_AVBuffer_GetBufferAttr(argBuffer, &attr); + }; +}; + +class AEncBufferSignal { +public: + std::mutex inMutex_; + std::mutex outMutex_; + std::mutex startMutex_; + std::condition_variable inCond_; + std::condition_variable outCond_; + std::condition_variable startCond_; + std::queue inQueue_; + std::queue outQueue_; + std::queue inBufferQueue_; + std::queue outBufferQueue_; + OH_AVCodecBufferAttr audioInfo = {0, 0, 0, AVCODEC_BUFFER_FLAGS_NONE}; +}; + +class CodecUserData { +public: + SampleInfo *sampleInfo = nullptr; + + uint32_t inputFrameCount_ = 0; + std::mutex inputMutex_; + std::condition_variable inputCond_; + std::queue inputBufferInfoQueue_; + + uint32_t outputFrameCount_ = 0; + std::mutex outputMutex_; + std::condition_variable outputCond_; + std::queue outputBufferInfoQueue_; + + void ClearQueue() { + { + std::unique_lock lock(inputMutex_); + auto emptyQueue = std::queue(); + inputBufferInfoQueue_.swap(emptyQueue); + } + { + std::unique_lock lock(outputMutex_); + auto emptyQueue = std::queue(); + outputBufferInfoQueue_.swap(emptyQueue); + } + } +}; + +#endif // AVCODEC_SAMPLE_INFO_H \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/types/libentry/Index.d.ts b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/types/libentry/Index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..8aadf80908aa25ce2e97e01548efe592451457de --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/types/libentry/Index.d.ts @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export const add: (a: number, b: number) => number; +export const startScreenCapture: () => number; +export const stopScreenCapture: () => number; +export const startCaptureAsFile: () => number; +export const startScreenCaptureWithSurface: () => number; diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/types/libentry/oh-package.json5 b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/types/libentry/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..6abf3b7c20f22c62aaac6a995a25cae672f73f35 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/cpp/types/libentry/oh-package.json5 @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "name": "libentry.so", + "types": "./Index.d.ts", + "version": "1.0.0", + "description": "Please describe the basic information." +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/entryability/EntryAbility.ets b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/entryability/EntryAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..94c65b96ba958e7df3ae5adb13ab7033b8e4f850 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/entryability/EntryAbility.ets @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { AbilityConstant, ConfigurationConstant, UIAbility, Want } from '@kit.AbilityKit'; +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { window } from '@kit.ArkUI'; +import common from '@ohos.app.ability.common'; +import abilityAccessCtrl, { Permissions } from '@ohos.abilityAccessCtrl'; +import { BusinessError } from '@ohos.base'; + +const DOMAIN = 0x0000; + +const permissions: Array = [ + 'ohos.permission.MICROPHONE', + 'ohos.permission.KEEP_BACKGROUND_RUNNING', + 'ohos.permission.WRITE_MEDIA', +]; + +function reqPermissionsFromUser(permissions: Array, context: common.UIAbilityContext): void { + let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); + // requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗 + atManager.requestPermissionsFromUser(context, permissions).then((data) => { + let grantStatus: Array = data.authResults; + let length: number = grantStatus.length; + for (let i = 0; i < length; i++) { + if (grantStatus[i] === 0) { + // 用户授权,可以继续访问目标操作 + } else { + // 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限 + return; + } + } + // 授权成功 + }).catch((err: BusinessError) => { + console.error(`Failed to request permissions from user. Code is ${err.code}, message is ${err.message}`); + }) +} + +export default class EntryAbility extends UIAbility { + onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { + this.context.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onCreate'); + } + + onDestroy(): void { + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onDestroy'); + } + + onWindowStageCreate(windowStage: window.WindowStage): void { + // Main window is created, set main page for this ability + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); + reqPermissionsFromUser(permissions, this.context); + windowStage.loadContent('pages/Index', (err) => { + if (err.code) { + hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err)); + return; + } + hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.'); + }); + } + + onWindowStageDestroy(): void { + // Main window is destroyed, release UI related resources + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); + } + + onForeground(): void { + // Ability has brought to foreground + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onForeground'); + } + + onBackground(): void { + // Ability has back to background + hilog.info(DOMAIN, 'testTag', '%{public}s', 'Ability onBackground'); + } +} diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets new file mode 100644 index 0000000000000000000000000000000000000000..0a97e21bd7a15599af76a806695860ff1eb0ebfe --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/entrybackupability/EntryBackupAbility.ets @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { BackupExtensionAbility, BundleVersion } from '@kit.CoreFileKit'; + +const DOMAIN = 0x0000; + +export default class EntryBackupAbility extends BackupExtensionAbility { + async onBackup() { + hilog.info(DOMAIN, 'testTag', 'onBackup ok'); + await Promise.resolve(); + } + + async onRestore(bundleVersion: BundleVersion) { + hilog.info(DOMAIN, 'testTag', 'onRestore ok %{public}s', JSON.stringify(bundleVersion)); + await Promise.resolve(); + } +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Index.ets b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Index.ets new file mode 100644 index 0000000000000000000000000000000000000000..9e32ea0a8ed31190566f148f39cac4931e943967 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Index.ets @@ -0,0 +1,92 @@ +import {Scene1} from './Scene1'; +import {Scene2} from './Scene2'; +import {Scene3} from './Scene3'; +import {Scene4} from './Scene4'; + +class DividerTmp { + strokeWidth: Length = 1 + startMargin: Length = 60 + endMargin: Length = 10 + color: ResourceColor = '#ffe9f0f0' + + constructor(strokeWidth: Length, startMargin: Length, endMargin: Length, color: ResourceColor) { + this.strokeWidth = strokeWidth + this.startMargin = startMargin + this.endMargin = endMargin + this.color = color + } +} + +@Entry({ routeName : 'Index' }) +@Component +struct Index { + @Provide('NavPathStack') pageInfos: NavPathStack = new NavPathStack() + @State egDivider: DividerTmp = new DividerTmp(1, 60, 10, '#ffe9f0f0') + + @Builder + PagesMap(name: string) { + if (name == 'Scene1') { + Scene1() + } else if (name == 'Scene2') { + Scene2() + } else if (name == 'Scene3') { + Scene3() + } else if (name == 'Scene4') { + Scene4() + } + } + + build() { + Column({ space: 15 }) { + + Navigation(this.pageInfos) { + List({ space: 12 }) { + ListItem() { + Text($r('app.string.sample_label')) + .fontSize(18) + } + ListItem() { + Text($r('app.string.saving_file_desc')) + .fontSize(20) + } + ListItem() { + Button($r('app.string.saving_file_desc')) + .width('80%') + .onClick(() => { + this.pageInfos.pushPathByName('Scene1', ''); + }) + } + ListItem() { + Text($r('app.string.streaming_desc')) + .fontSize(20) + } + ListItem() { + Button($r('app.string.streaming_desc'),{ type: ButtonType.Capsule, stateEffect: true }) + .width('80%') + .onClick(() => { + this.pageInfos.pushPathByName('Scene2', ''); + }) + } + ListItem() { + Button($r('app.string.streaming_surface_mode_desc')) + .width('80%') + .onClick(() => { + this.pageInfos.pushPathByName('Scene3', ''); + }) + } + } + .width("100%") + .margin({ top: 12 }) + .alignListItem(ListItemAlign.Center) + .divider(this.egDivider) + + } + .mode(NavigationMode.Stack) + .title($r('app.string.sample_label')) + .titleMode(NavigationTitleMode.Full) + .navDestination(this.PagesMap) + + } + .height('100%') + } +} diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Menu.ets b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Menu.ets new file mode 100644 index 0000000000000000000000000000000000000000..615b016994f395f9805a9b3879aa0a098b6f868e --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Menu.ets @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import router from '@ohos.router'; + +@Component +export struct MenuSCComponent { + @State select: boolean = true + @Builder + MyMenu(){ + Menu() { + MenuItemGroup({ header: $r('app.string.list_desc') }) { + MenuItem({ content: $r('app.string.index_desc') }) + .onChange((selected) => { + console.info("menuItem select" + selected); + router.pushUrl({ + url: 'pages/Index' // 目标url + }, router.RouterMode.Single, (err) => { + if (err) { + console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`); + return; + } + console.info('Invoke pushUrl succeeded.'); + }); + }) + + MenuItem({ content: $r('app.string.saving_file_desc') }) + .onChange((selected) => { + router.pushUrl({ + url: 'pages/Scene1' // 目标url + }, router.RouterMode.Single, (err) => { + if (err) { + console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`); + return; + } + console.info('Invoke pushUrl succeeded.'); + }); + }) + + MenuItem({ content: $r('app.string.streaming_desc') }) + .onChange((selected) => { + router.pushUrl({ + url: 'pages/Scene2' // 目标url + }, router.RouterMode.Single, (err) => { + if (err) { + console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`); + return; + } + console.info('Invoke pushUrl succeeded.'); + }); + }) + + MenuItem({ content: $r('app.string.streaming_surface_mode_desc')}) + .onChange((selected) => { + router.pushUrl({ + url: 'pages/Scene3' // 目标url + }, router.RouterMode.Single, (err) => { + if (err) { + console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`); + return; + } + console.info('Invoke pushUrl succeeded.'); + }); + }) + } + + MenuItemGroup({ header: $r('app.string.other_mode_desc') }) { + MenuItem({ content: $r('app.string.all_mode_desc') }) + .onChange((selected) => { + router.pushUrl({ + url: 'pages/Scene4' // 目标url + }, router.RouterMode.Single, (err) => { + if (err) { + console.error(`Invoke pushUrl failed, code is ${err.code}, message is ${err.message}`); + return; + } + console.info('Invoke pushUrl succeeded.'); + }); + }) + MenuItem({ + content: $r('app.string.all_mode_desc'), + }) + } + } + } + + build() { + Button($r('app.string.choose_mode_desc'), { type: ButtonType.Capsule, stateEffect: true }) + .fontSize(25) + .backgroundColor(0x317aff) + .fontWeight(800) + .bindMenu(this.MyMenu) + } + } \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Scene1.ets b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Scene1.ets new file mode 100644 index 0000000000000000000000000000000000000000..abc85d576aec7905e36b8caff40f6c9c24435f6e --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Scene1.ets @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import testNapi from 'libentry.so'; + +@Entry({ routeName : 'Scene1' }) +@Component +export struct Scene1 { + @State isCapturing: boolean = false; + @Consume('pageInfos') pageInfos: NavPathStack; + + build() { + NavDestination() { + Column({ space: 15 }) { + Column() { + Text($r('app.string.saving_file_desc')) + .fontSize(30) + .fontWeight(FontWeight.Bold) + .onClick(() => { + hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3)); + }) + } + .width('100%') + .height('20%') + Column() { + List({ space: 40 }) { + ListItem() { + Button($r('app.string.start_desc'), { type: ButtonType.Circle, stateEffect: true }) + .backgroundColor(0xF55A42) + .width(90) + .height(90) + .onClick(() => { + if (testNapi.startCaptureAsFile() == 0) { + this.isCapturing = true; + } + }) + } + ListItem() { + Button($r('app.string.stop_desc'), { type: ButtonType.Normal, stateEffect: true }) + .backgroundColor(0x000000) + .width(85) + .height(85) + .shadow({ radius: 10 }) + .onClick(() => { + if (this.isCapturing) { + testNapi.stopScreenCapture(); + this.isCapturing = false; + } + }) + } + }.listDirection(Axis.Horizontal).lanes({ minLength: 200, maxLength: 300 }) + } + .width('100%') + .height('20%') + + Column() { + } + .width('100%') + .height('20%') + } + .width('100%') + } + .title($r('app.string.saving_file_desc')) + } +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Scene2.ets b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Scene2.ets new file mode 100644 index 0000000000000000000000000000000000000000..095b230a1f7fb1c58af5f38bbb610655a7f10978 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Scene2.ets @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import testNapi from 'libentry.so'; + +@Entry({ routeName : 'Scene2' }) +@Component +export struct Scene2 { + @State isCapturing: boolean = false; + @Consume('pageInfos') pageInfos: NavPathStack; + + build() { + NavDestination() { + + Column({ space: 15 }) { + Column() { + Text($r('app.string.streaming_desc')) + .fontSize(30) + .fontWeight(FontWeight.Bold) + .onClick(() => { + hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3)); + }) + } + .width('100%') + .height('20%') + Column() { + List({ space: 40 }) { + ListItem() { + Button($r('app.string.start_desc'), { type: ButtonType.Circle, stateEffect: true }) + .backgroundColor(0xF55A42) + .width(90) + .height(90) + .onClick(() => { + if (testNapi.startScreenCapture() == 0) { + this.isCapturing = true; + } + }) + } + ListItem() { + Button($r('app.string.stop_desc'), { type: ButtonType.Normal, stateEffect: true }) + .backgroundColor(0x000000) + .width(85) + .height(85) + .shadow({ radius: 10 }) + .onClick(() => { + if (this.isCapturing) { + testNapi.stopScreenCapture(); + this.isCapturing = false; + } + }) + } + }.listDirection(Axis.Horizontal).lanes({ minLength: 200, maxLength: 300 }) + } + .width('100%') + .height('20%') + + Column() { + } + .width('100%') + .height('20%') + } + .width('100%') + } + .title($r('app.string.streaming_desc')) + } +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Scene3.ets b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Scene3.ets new file mode 100644 index 0000000000000000000000000000000000000000..8912dba01f4a690f650878ecc4cfc07704d0e8f5 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Scene3.ets @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import testNapi from 'libentry.so'; + +@Entry({ routeName : 'Scene3' }) +@Component +export struct Scene3 { + @State message: string = '录屏取码流Surface模式'; + @State isCapturing: boolean = false; + @Consume('pageInfos') pageInfos: NavPathStack; + + build() { + NavDestination() { + + Column({ space: 15 }) { + Column() { + Text(this.message) + .fontSize(30) + .fontWeight(FontWeight.Bold) + .onClick(() => { + hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3)); + }) + } + .width('100%') + .height('20%') + Column() { + List({ space: 40 }) { + ListItem() { + Button($r('app.string.start_desc'), { type: ButtonType.Circle, stateEffect: true }) + .backgroundColor(0xF55A42) + .width(90) + .height(90) + .onClick(() => { + if (testNapi.startScreenCaptureWithSurface() == 0) { + this.isCapturing = true; + } + }) + } + ListItem() { + Button($r('app.string.stop_desc'), { type: ButtonType.Normal, stateEffect: true }) + .backgroundColor(0x000000) + .width(85) + .height(85) + .shadow({ radius: 10 }) + .onClick(() => { + if (this.isCapturing) { + testNapi.stopScreenCapture(); + this.isCapturing = false; + } + }) + } + }.listDirection(Axis.Horizontal).lanes({ minLength: 200, maxLength: 300 }) + } + .width('100%') + .height('20%') + + Column() { + } + .width('100%') + .height('20%') + } + .width('100%') + } + .title($r('app.string.other_mode_desc')) + } +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Scene4.ets b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Scene4.ets new file mode 100644 index 0000000000000000000000000000000000000000..86edb413cf5efd28c1a0f0f2a001aa6a806f2fee --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/ets/pages/Scene4.ets @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import testNapi from 'libentry.so'; +import {MenuSCComponent} from './Menu'; + +@Entry({ routeName : 'Scene4' }) +@Component +export struct Scene4 { + @State isCapturing: boolean = false; + @Consume('pageInfos') pageInfos: NavPathStack; + + build() { + NavDestination() { + + Column({ space: 15 }) { + Column() { + MenuSCComponent() + Text($r('app.string.all_mode_desc')) + .fontSize(25) + .fontWeight(FontWeight.Bold) + } + .width('100%') + .height('10%').border({ width: 1 }) + + Column() { + Text($r('app.string.saving_file_eng_desc')) + .fontSize(20) + .fontWeight(FontWeight.Bold) + .onClick(() => { + hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3)); + }) + List({ space: 40 }) { + ListItem() { + Button($r('app.string.start_desc'), { type: ButtonType.Circle, stateEffect: true }) + .backgroundColor(0xF55A42) + .width(90) + .height(90) + .onClick(() => { + if (testNapi.startCaptureAsFile() == 0) { + this.isCapturing = true; + } + }) + } + + ListItem() { + } + + ListItem() { + Button($r('app.string.stop_desc'), { type: ButtonType.Normal, stateEffect: true }) + .backgroundColor(0x000000) + .width(85) + .height(85) + .shadow({ radius: 10 }) + .onClick(() => { + if (this.isCapturing) { + testNapi.stopScreenCapture(); + this.isCapturing = false; + } + }) + } + }.listDirection(Axis.Horizontal).lanes({ minLength: 200, maxLength: 300 }) + } + .width('100%') + .height('20%').border({ width: 1 }) + + Column() { + Text($r('app.string.streaming_eng_desc')) + .fontSize(20) + .fontWeight(FontWeight.Bold) + .onClick(() => { + hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3)); + }) + List({ space: 40 }) { + ListItem() { + Button($r('app.string.start_desc'), { type: ButtonType.Circle, stateEffect: true }) + .backgroundColor(0xF55A42) + .width(90) + .height(90) + .onClick(() => { + if (testNapi.startScreenCapture() == 0) { + this.isCapturing = true; + } + }) + } + ListItem() { + } + ListItem() { + Button($r('app.string.stop_desc'), { type: ButtonType.Normal, stateEffect: true }) + .backgroundColor(0x000000) + .width(85) + .height(85) + .shadow({ radius: 10 }) + .onClick(() => { + if (this.isCapturing) { + testNapi.stopScreenCapture(); + this.isCapturing = false; + } + }) + } + }.listDirection(Axis.Horizontal).lanes({ minLength: 200, maxLength: 300 }) + } + .width('100%') + .height('20%').border({ width: 1 }) + + Column() { + Text($r('app.string.streaming_surface_mode_eng_desc')) + .fontSize(20) + .fontWeight(FontWeight.Bold) + .onClick(() => { + hilog.info(0x0000, 'testTag', 'Test NAPI 2 + 3 = %{public}d', testNapi.add(2, 3)); + }) + List({ space: 40 }) { + ListItem() { + Button($r('app.string.start_desc'), { type: ButtonType.Circle, stateEffect: true }) + .backgroundColor(0xF55A42) + .width(90) + .height(90) + .onClick(() => { + if (testNapi.startScreenCaptureWithSurface() == 0) { + this.isCapturing = true; + } + }) + } + ListItem() { + } + ListItem() { + Button($r('app.string.stop_desc'), { type: ButtonType.Normal, stateEffect: true }) + .backgroundColor(0x000000) + .width(85) + .height(85) + .shadow({ radius: 10 }) + .onClick(() => { + if (this.isCapturing) { + testNapi.stopScreenCapture(); + this.isCapturing = false; + } + }) + } + }.listDirection(Axis.Horizontal).lanes({ minLength: 200, maxLength: 300 }) + } + .width('100%') + .height('20%').border({ width: 1 }) + } + .height('100%') + } + .title($r('app.string.all_mode_desc')) + } +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/module.json5 b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..0d035f4eaa6adcb091a1f5cef72599feae869e20 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/module.json5 @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "module": { + "name": "entry", + "type": "entry", + "description": "$string:module_desc", + "mainElement": "EntryAbility", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false, + "pages": "$profile:main_pages", + "abilities": [ + { + "name": "EntryAbility", + "srcEntry": "./ets/entryability/EntryAbility.ets", + "description": "$string:EntryAbility_desc", + "icon": "$media:layered_image", + "label": "$string:EntryAbility_label", + "startWindowIcon": "$media:startIcon", + "startWindowBackground": "$color:start_window_background", + "exported": true, + "skills": [ + { + "entities": [ + "entity.system.home" + ], + "actions": [ + "action.system.home" + ] + } + ] + } + ], + "extensionAbilities": [ + { + "name": "EntryBackupAbility", + "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets", + "type": "backup", + "exported": false, + "metadata": [ + { + "name": "ohos.extension.backup", + "resource": "$profile:backup_config" + } + ] + } + ], + + "requestPermissions": [ + { + "name": "ohos.permission.MICROPHONE", + "reason": "$string:EntryAbility_desc", + "usedScene": { + "abilities": [ + "EntryAbility" + ], + "when": "always" + } + }, + { + "name": "ohos.permission.KEEP_BACKGROUND_RUNNING", + "reason": "$string:EntryAbility_desc", + "usedScene": { + "abilities": [ + "EntryAbility" + ], + "when": "always" + } + }, + { + "name": "ohos.permission.WRITE_MEDIA", + "reason": "$string:EntryAbility_desc", + "usedScene": { + "abilities": [ + "EntryAbility" + ], + "when": "always" + } + } + ] + } +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/element/color.json b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..3c712962da3c2751c2b9ddb53559afcbd2b54a02 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#FFFFFF" + } + ] +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/element/float.json b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/element/float.json new file mode 100644 index 0000000000000000000000000000000000000000..33ea22304f9b1485b5f22d811023701b5d4e35b6 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/element/float.json @@ -0,0 +1,8 @@ +{ + "float": [ + { + "name": "page_text_font_size", + "value": "50fp" + } + ] +} diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/element/string.json b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/element/string.json new file mode 100644 index 0000000000000000000000000000000000000000..0423a79e1a85f6e1a2827e5b5dab97e8433afb69 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/element/string.json @@ -0,0 +1,72 @@ +{ + "string": [ + { + "name": "module_desc", + "value": "module description" + }, + { + "name": "EntryAbility_desc", + "value": "description" + }, + { + "name": "EntryAbility_label", + "value": "录屏Sample" + }, + { + "name": "sample_label", + "value": "AVScreenCapture录屏Sample" + }, + { + "name": "saving_file_desc", + "value": "录屏存文件" + }, + { + "name": "streaming_desc", + "value": "录屏取码流" + }, + { + "name": "streaming_surface_mode_desc", + "value": "录屏取码流Surface模式" + }, + { + "name": "list_desc", + "value": "录屏场景列表" + }, + { + "name": "index_desc", + "value": "主页" + }, + { + "name": "other_mode_desc", + "value": "其他场景" + }, + { + "name": "choose_mode_desc", + "value": "选择录屏场景" + }, + { + "name": "start_desc", + "value": "Start" + }, + { + "name": "stop_desc", + "value": "Stop" + }, + { + "name": "saving_file_eng_desc", + "value": "Saving File" + }, + { + "name": "streaming_eng_desc", + "value": "Original Stream" + }, + { + "name": "streaming_surface_mode_eng_desc", + "value": "Original Stream With Surface" + }, + { + "name": "all_mode_desc", + "value": "全量" + } + ] +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/media/background.png b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/media/background.png new file mode 100644 index 0000000000000000000000000000000000000000..923f2b3f27e915d6871871deea0420eb45ce102f Binary files /dev/null and b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/media/background.png differ diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/media/foreground.png b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/media/foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..97014d3e10e5ff511409c378cd4255713aecd85f Binary files /dev/null and b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/media/foreground.png differ diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/media/layered_image.json b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/media/layered_image.json new file mode 100644 index 0000000000000000000000000000000000000000..fb49920440fb4d246c82f9ada275e26123a2136a --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/media/layered_image.json @@ -0,0 +1,7 @@ +{ + "layered-image": + { + "background" : "$media:background", + "foreground" : "$media:foreground" + } +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/media/startIcon.png b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/media/startIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..205ad8b5a8a42e8762fbe4899b8e5e31ce822b8b Binary files /dev/null and b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/media/startIcon.png differ diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/profile/backup_config.json b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/profile/backup_config.json new file mode 100644 index 0000000000000000000000000000000000000000..78f40ae7c494d71e2482278f359ec790ca73471a --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/profile/backup_config.json @@ -0,0 +1,3 @@ +{ + "allowToBackupRestore": true +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/profile/main_pages.json b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/profile/main_pages.json new file mode 100644 index 0000000000000000000000000000000000000000..1898d94f58d6128ab712be2c68acc7c98e9ab9ce --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/base/profile/main_pages.json @@ -0,0 +1,5 @@ +{ + "src": [ + "pages/Index" + ] +} diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/dark/element/color.json b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/dark/element/color.json new file mode 100644 index 0000000000000000000000000000000000000000..79b11c2747aec33e710fd3a7b2b3c94dd9965499 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/main/resources/dark/element/color.json @@ -0,0 +1,8 @@ +{ + "color": [ + { + "name": "start_window_background", + "value": "#000000" + } + ] +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/mock/Libentry.mock.ets b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/mock/Libentry.mock.ets new file mode 100644 index 0000000000000000000000000000000000000000..eebf1ed910f6a8f2a9e7e565aa71b179b7b8b537 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/mock/Libentry.mock.ets @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const NativeMock: Record = { + 'add': (a: number, b: number) => { + return a + b; + }, +}; + +export default NativeMock; \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/mock/mock-config.json5 b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/mock/mock-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..2c7d2ba82b796a2850ced0a277d261d7d7355416 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/mock/mock-config.json5 @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "libentry.so": { + "source": "src/mock/Libentry.mock.ets" + } +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/ohosTest/ets/test/Ability.test.ets b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/ohosTest/ets/test/Ability.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..0f8ce9a2c012f8fe36114cef65216ef0b6254f41 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/ohosTest/ets/test/Ability.test.ets @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { hilog } from '@kit.PerformanceAnalysisKit'; +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function abilityTest() { + describe('ActsAbilityTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }) + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }) + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }) + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }) + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + hilog.info(0x0000, 'testTag', '%{public}s', 'it begin'); + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }) + }) +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/ohosTest/ets/test/List.test.ets b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/ohosTest/ets/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..1eac52fcebe8958e19a7b8fed2e8f39c520a3e42 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/ohosTest/ets/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import abilityTest from './Ability.test'; + +export default function testsuite() { + abilityTest(); +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/ohosTest/module.json5 b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/ohosTest/module.json5 new file mode 100644 index 0000000000000000000000000000000000000000..c3fd9dda3040d888d9d8b0b62bcb5d3b6fbeb614 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/ohosTest/module.json5 @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "module": { + "name": "entry_test", + "type": "feature", + "deviceTypes": [ + "default", + "tablet" + ], + "deliveryWithInstall": true, + "installationFree": false + } +} diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/test/List.test.ets b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/test/List.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..f1186b1f53c3a70930921c5dbd1417332bec56c9 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/test/List.test.ets @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import localUnitTest from './LocalUnit.test'; + +export default function testsuite() { + localUnitTest(); +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/test/LocalUnit.test.ets b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/test/LocalUnit.test.ets new file mode 100644 index 0000000000000000000000000000000000000000..7fc57c77dbf76d8df08a2b802a55b948e3fcf968 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/entry/src/test/LocalUnit.test.ets @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'; + +export default function localUnitTest() { + describe('localUnitTest', () => { + // Defines a test suite. Two parameters are supported: test suite name and test suite function. + beforeAll(() => { + // Presets an action, which is performed only once before all test cases of the test suite start. + // This API supports only one parameter: preset action function. + }); + beforeEach(() => { + // Presets an action, which is performed before each unit test case starts. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: preset action function. + }); + afterEach(() => { + // Presets a clear action, which is performed after each unit test case ends. + // The number of execution times is the same as the number of test cases defined by **it**. + // This API supports only one parameter: clear action function. + }); + afterAll(() => { + // Presets a clear action, which is performed after all test cases of the test suite end. + // This API supports only one parameter: clear action function. + }); + it('assertContain', 0, () => { + // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function. + let a = 'abc'; + let b = 'b'; + // Defines a variety of assertion methods, which are used to declare expected boolean conditions. + expect(a).assertContain(b); + expect(a).assertEqual(a); + }); + }); +} \ No newline at end of file diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/hvigor/hvigor-config.json5 b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/hvigor/hvigor-config.json5 new file mode 100644 index 0000000000000000000000000000000000000000..5bebc9755447385d82ce4138f54d991b1f85f348 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/hvigor/hvigor-config.json5 @@ -0,0 +1,22 @@ +{ + "modelVersion": "5.0.5", + "dependencies": { + }, + "execution": { + // "analyze": "normal", /* Define the build analyze mode. Value: [ "normal" | "advanced" | false ]. Default: "normal" */ + // "daemon": true, /* Enable daemon compilation. Value: [ true | false ]. Default: true */ + // "incremental": true, /* Enable incremental compilation. Value: [ true | false ]. Default: true */ + // "parallel": true, /* Enable parallel compilation. Value: [ true | false ]. Default: true */ + // "typeCheck": false, /* Enable typeCheck. Value: [ true | false ]. Default: false */ + }, + "logging": { + // "level": "info" /* Define the log level. Value: [ "debug" | "info" | "warn" | "error" ]. Default: "info" */ + }, + "debugging": { + // "stacktrace": false /* Disable stacktrace compilation. Value: [ true | false ]. Default: false */ + }, + "nodeOptions": { + // "maxOldSpaceSize": 8192 /* Enable nodeOptions maxOldSpaceSize compilation. Unit M. Used for the daemon process. Default: 8192*/ + // "exposeGC": true /* Enable to trigger garbage collection explicitly. Default: true*/ + } +} diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/hvigorfile.ts b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/hvigorfile.ts new file mode 100644 index 0000000000000000000000000000000000000000..f3cb9f1a87a81687554a76283af8df27d8bda775 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/hvigorfile.ts @@ -0,0 +1,6 @@ +import { appTasks } from '@ohos/hvigor-ohos-plugin'; + +export default { + system: appTasks, /* Built-in plugin of Hvigor. It cannot be modified. */ + plugins:[] /* Custom plugin to extend the functionality of Hvigor. */ +} diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/log.txt b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/log.txt new file mode 100644 index 0000000000000000000000000000000000000000..17cc32bea3df5e26a9a01a62cbdb5efc7e74cc25 --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/log.txt @@ -0,0 +1,520 @@ +18:20:09:121 log task queue not empty +18:20:09:121 log task size: 1 +18:20:09:121 log before pop task size: 1 +18:20:09:121 log after task size: 0 +18:20:09:631 log timer task size: 1 +18:20:10:396 log task queue not empty +18:20:10:396 log task size: 1 +18:20:10:396 log before pop task size: 1 +18:20:10:396 log after task size: 0 +18:20:10:904 log timer task size: 1 +19:13:41:986 log task queue not empty +19:13:41:986 log task size: 1 +19:13:41:986 log before pop task size: 1 +19:13:41:987 log after task size: 0 +19:13:42:494 log timer task size: 1 +19:13:42:962 log task queue not empty +19:13:42:962 log task size: 1 +19:13:42:962 log before pop task size: 1 +19:13:42:962 log after task size: 0 +19:13:43:334 log task queue not empty +19:13:43:336 log task size: 1 +19:13:43:337 log before pop task size: 1 +19:13:43:337 log after task size: 0 +19:13:43:465 log timer task size: 1 +19:13:43:733 log task queue not empty +19:13:43:734 log task size: 1 +19:13:43:734 log before pop task size: 1 +19:13:43:734 log after task size: 0 +19:13:43:852 log timer task size: 1 +19:13:44:077 log task queue not empty +19:13:44:077 log task size: 1 +19:13:44:077 log before pop task size: 1 +19:13:44:077 log after task size: 0 +19:13:44:208 log task queue not empty +19:13:44:209 log task size: 1 +19:13:44:209 log before pop task size: 1 +19:13:44:209 log after task size: 0 +19:13:44:236 log timer task size: 1 +19:13:44:592 log timer task size: 1 +19:13:44:716 log timer task size: 1 +19:13:47:615 log task queue not empty +19:13:47:615 log task size: 1 +19:13:47:615 log before pop task size: 1 +19:13:47:615 log after task size: 0 +19:13:47:897 log task queue not empty +19:13:47:897 log task size: 1 +19:13:47:898 log before pop task size: 1 +19:13:47:898 log after task size: 0 +19:13:47:965 log task queue not empty +19:13:47:966 log task size: 1 +19:13:47:966 log before pop task size: 1 +19:13:47:966 log after task size: 0 +19:13:48:120 log timer task size: 1 +19:13:48:413 log timer task size: 1 +19:13:48:474 log timer task size: 1 +19:13:48:691 log task queue not empty +19:13:48:691 log task size: 1 +19:13:48:691 log before pop task size: 1 +19:13:48:691 log after task size: 0 +19:13:49:013 log task queue not empty +19:13:49:013 log task size: 1 +19:13:49:013 log before pop task size: 1 +19:13:49:014 log after task size: 0 +19:13:49:077 log task queue not empty +19:13:49:077 log task size: 1 +19:13:49:077 log before pop task size: 1 +19:13:49:077 log after task size: 0 +19:13:49:201 log timer task size: 1 +19:13:49:524 log timer task size: 1 +19:13:49:587 log timer task size: 1 +19:13:51:414 log task queue not empty +19:13:51:414 log task size: 1 +19:13:51:414 log before pop task size: 1 +19:13:51:415 log after task size: 0 +19:13:51:904 log task queue not empty +19:13:51:904 log task size: 1 +19:13:51:904 log before pop task size: 1 +19:13:51:904 log after task size: 0 +19:13:51:918 log timer task size: 1 +19:13:52:203 log task queue not empty +19:13:52:203 log task size: 1 +19:13:52:203 log before pop task size: 1 +19:13:52:203 log after task size: 0 +19:13:52:268 log task queue not empty +19:13:52:268 log task size: 1 +19:13:52:268 log before pop task size: 1 +19:13:52:268 log after task size: 0 +19:13:52:413 log timer task size: 1 +19:13:52:706 log timer task size: 1 +19:13:52:783 log timer task size: 1 +19:13:52:836 log task queue not empty +19:13:52:837 log task size: 1 +19:13:52:837 log before pop task size: 1 +19:13:52:837 log after task size: 0 +19:13:52:886 log task queue not empty +19:13:52:886 log task size: 1 +19:13:52:886 log before pop task size: 1 +19:13:52:886 log after task size: 0 +19:13:53:338 log timer task size: 1 +19:13:53:400 log timer task size: 1 +19:13:55:319 log task queue not empty +19:13:55:319 log task size: 1 +19:13:55:319 log before pop task size: 1 +19:13:55:319 log after task size: 0 +19:13:55:729 log task queue not empty +19:13:55:730 log task size: 1 +19:13:55:730 log before pop task size: 1 +19:13:55:730 log after task size: 0 +19:13:55:821 log timer task size: 1 +19:13:55:975 log task queue not empty +19:13:55:975 log task size: 1 +19:13:55:976 log before pop task size: 1 +19:13:55:976 log after task size: 0 +19:13:56:238 log timer task size: 1 +19:13:56:485 log timer task size: 1 +19:13:56:775 log task queue not empty +19:13:56:776 log task size: 1 +19:13:56:776 log before pop task size: 1 +19:13:56:776 log after task size: 0 +19:13:56:841 log task queue not empty +19:13:56:841 log task size: 1 +19:13:56:841 log before pop task size: 1 +19:13:56:841 log after task size: 0 +19:13:57:287 log timer task size: 1 +19:13:57:349 log timer task size: 1 +19:14:14:524 log task queue not empty +19:14:14:524 log task size: 1 +19:14:14:524 log before pop task size: 1 +19:14:14:524 log after task size: 0 +19:14:15:039 log timer task size: 1 +19:14:16:166 log task queue not empty +19:14:16:166 log task size: 1 +19:14:16:166 log before pop task size: 1 +19:14:16:166 log after task size: 0 +19:14:16:673 log timer task size: 1 +19:14:36:030 log task queue not empty +19:14:36:030 log task size: 1 +19:14:36:030 log before pop task size: 1 +19:14:36:030 log after task size: 0 +19:14:36:514 log task queue not empty +19:14:36:514 log task size: 1 +19:14:36:514 log before pop task size: 1 +19:14:36:514 log after task size: 0 +19:14:36:534 log timer task size: 1 +19:14:37:022 log timer task size: 1 +19:15:12:978 log task queue not empty +19:15:12:978 log task size: 1 +19:15:12:978 log before pop task size: 1 +19:15:12:978 log after task size: 0 +19:15:13:483 log timer task size: 1 +19:15:18:387 log task queue not empty +19:15:18:387 log task size: 1 +19:15:18:387 log before pop task size: 1 +19:15:18:387 log after task size: 0 +19:15:18:388 log task queue not empty +19:15:18:388 log task size: 1 +19:15:18:388 log before pop task size: 1 +19:15:18:388 log after task size: 0 +19:15:18:487 log task queue not empty +19:15:18:487 log task size: 1 +19:15:18:487 log before pop task size: 1 +19:15:18:487 log after task size: 0 +19:15:18:868 log task queue not empty +19:15:18:868 log task size: 1 +19:15:18:868 log before pop task size: 1 +19:15:18:868 log after task size: 0 +19:15:18:903 log timer task size: 1 +19:15:18:996 log timer task size: 1 +19:15:19:013 log task queue not empty +19:15:19:013 log task size: 1 +19:15:19:013 log before pop task size: 1 +19:15:19:013 log after task size: 0 +19:15:19:371 log timer task size: 1 +19:15:19:428 log task queue not empty +19:15:19:429 log task size: 1 +19:15:19:429 log before pop task size: 1 +19:15:19:429 log after task size: 0 +19:15:19:496 log task queue not empty +19:15:19:496 log task size: 1 +19:15:19:496 log before pop task size: 1 +19:15:19:496 log after task size: 0 +19:15:19:524 log timer task size: 1 +19:15:19:931 log timer task size: 1 +19:15:20:011 log timer task size: 1 +19:15:20:324 log task queue not empty +19:15:20:324 log task size: 1 +19:15:20:324 log before pop task size: 1 +19:15:20:324 log after task size: 0 +19:15:20:834 log timer task size: 1 +19:15:21:268 log task queue not empty +19:15:21:269 log task size: 1 +19:15:21:269 log before pop task size: 1 +19:15:21:269 log after task size: 0 +19:15:21:697 log task queue not empty +19:15:21:697 log task size: 1 +19:15:21:697 log before pop task size: 1 +19:15:21:697 log after task size: 0 +19:15:21:780 log timer task size: 1 +19:15:22:205 log timer task size: 1 +19:15:22:261 log task queue not empty +19:15:22:261 log task size: 1 +19:15:22:261 log before pop task size: 1 +19:15:22:261 log after task size: 0 +19:15:22:621 log task queue not empty +19:15:22:621 log task size: 1 +19:15:22:621 log before pop task size: 1 +19:15:22:621 log after task size: 0 +19:15:22:766 log timer task size: 1 +19:15:22:975 log task queue not empty +19:15:22:975 log task size: 1 +19:15:22:975 log before pop task size: 1 +19:15:22:975 log after task size: 0 +19:15:23:127 log timer task size: 1 +19:15:23:315 log task queue not empty +19:15:23:315 log task size: 1 +19:15:23:315 log before pop task size: 1 +19:15:23:315 log after task size: 0 +19:15:23:381 log task queue not empty +19:15:23:381 log task size: 1 +19:15:23:381 log before pop task size: 1 +19:15:23:381 log after task size: 0 +19:15:23:488 log timer task size: 1 +19:15:23:817 log timer task size: 1 +19:15:23:894 log timer task size: 1 +19:15:33:945 log task queue not empty +19:15:33:945 log task size: 1 +19:15:33:946 log before pop task size: 1 +19:15:33:946 log after task size: 0 +19:15:34:056 log task queue not empty +19:15:34:056 log task size: 1 +19:15:34:056 log before pop task size: 1 +19:15:34:056 log after task size: 0 +19:15:34:457 log timer task size: 1 +19:15:34:569 log timer task size: 1 +19:15:36:076 log task queue not empty +19:15:36:076 log task size: 1 +19:15:36:076 log before pop task size: 1 +19:15:36:076 log after task size: 0 +19:15:36:586 log timer task size: 1 +19:15:36:636 log task queue not empty +19:15:36:636 log task size: 1 +19:15:36:636 log before pop task size: 1 +19:15:36:636 log after task size: 0 +19:15:37:146 log timer task size: 1 +19:15:38:352 log task queue not empty +19:15:38:352 log task size: 1 +19:15:38:352 log before pop task size: 1 +19:15:38:352 log after task size: 0 +19:15:38:497 log task queue not empty +19:15:38:498 log task size: 1 +19:15:38:498 log before pop task size: 1 +19:15:38:498 log after task size: 0 +19:15:38:858 log timer task size: 1 +19:15:39:014 log timer task size: 1 +19:15:39:541 log task queue not empty +19:15:39:541 log task size: 1 +19:15:39:542 log before pop task size: 1 +19:15:39:542 log after task size: 0 +19:15:39:740 log task queue not empty +19:15:39:740 log task size: 1 +19:15:39:740 log before pop task size: 1 +19:15:39:740 log after task size: 0 +19:15:39:837 log task queue not empty +19:15:39:837 log task size: 1 +19:15:39:837 log before pop task size: 1 +19:15:39:837 log after task size: 0 +19:15:40:009 log task queue not empty +19:15:40:009 log task size: 1 +19:15:40:009 log before pop task size: 1 +19:15:40:009 log after task size: 0 +19:15:40:044 log timer task size: 1 +19:15:40:137 log task queue not empty +19:15:40:137 log task size: 1 +19:15:40:137 log before pop task size: 1 +19:15:40:137 log after task size: 0 +19:15:40:246 log timer task size: 1 +19:15:40:353 log timer task size: 1 +19:15:40:463 log task queue not empty +19:15:40:463 log task size: 1 +19:15:40:463 log before pop task size: 1 +19:15:40:463 log after task size: 0 +19:15:40:522 log timer task size: 1 +19:15:40:645 log timer task size: 1 +19:15:40:973 log timer task size: 1 +19:15:41:472 log task queue not empty +19:15:41:474 log task size: 1 +19:15:41:474 log before pop task size: 1 +19:15:41:474 log after task size: 0 +19:15:41:986 log timer task size: 1 +19:17:34:364 log task queue not empty +19:17:34:364 log task size: 1 +19:17:34:364 log before pop task size: 1 +19:17:34:364 log after task size: 0 +19:17:34:874 log timer task size: 1 +19:17:35:177 log task queue not empty +19:17:35:178 log task size: 1 +19:17:35:178 log before pop task size: 1 +19:17:35:178 log after task size: 0 +19:17:35:178 log task queue not empty +19:17:35:178 log task size: 1 +19:17:35:178 log before pop task size: 1 +19:17:35:178 log after task size: 0 +19:17:35:179 log task queue not empty +19:17:35:179 log task size: 1 +19:17:35:179 log before pop task size: 1 +19:17:35:179 log after task size: 0 +19:17:35:180 log task queue not empty +19:17:35:180 log task size: 1 +19:17:35:180 log before pop task size: 1 +19:17:35:180 log after task size: 0 +19:17:35:180 log task queue not empty +19:17:35:180 log task size: 1 +19:17:35:180 log before pop task size: 1 +19:17:35:180 log after task size: 0 +19:17:35:181 log task queue not empty +19:17:35:181 log task size: 1 +19:17:35:181 log before pop task size: 1 +19:17:35:181 log after task size: 0 +19:17:35:682 log timer task size: 1 +19:17:35:682 log timer task size: 1 +19:17:35:682 log timer task size: 1 +19:17:36:217 log task queue not empty +19:17:36:217 log task size: 1 +19:17:36:218 log before pop task size: 1 +19:17:36:218 log after task size: 0 +19:17:36:726 log timer task size: 1 +19:17:38:953 log task queue not empty +19:17:38:953 log task size: 1 +19:17:38:953 log before pop task size: 1 +19:17:38:953 log after task size: 0 +19:17:39:465 log timer task size: 1 +19:17:39:529 log task queue not empty +19:17:39:529 log task size: 1 +19:17:39:530 log before pop task size: 1 +19:17:39:530 log after task size: 0 +19:17:39:530 log task queue not empty +19:17:39:530 log task size: 1 +19:17:39:530 log before pop task size: 1 +19:17:39:530 log after task size: 0 +19:17:39:531 log task queue not empty +19:17:39:531 log task size: 1 +19:17:39:531 log before pop task size: 1 +19:17:39:531 log after task size: 0 +19:17:39:531 log task queue not empty +19:17:39:532 log task size: 1 +19:17:39:532 log before pop task size: 1 +19:17:39:532 log after task size: 0 +19:17:39:532 log task queue not empty +19:17:39:532 log task size: 1 +19:17:39:532 log before pop task size: 1 +19:17:39:532 log after task size: 0 +19:17:39:533 log task queue not empty +19:17:39:533 log task size: 1 +19:17:39:533 log before pop task size: 1 +19:17:39:533 log after task size: 0 +19:17:39:533 log task queue not empty +19:17:39:533 log task size: 1 +19:17:39:533 log before pop task size: 1 +19:17:39:534 log after task size: 0 +19:17:39:534 log task queue not empty +19:17:39:534 log task size: 1 +19:17:39:534 log before pop task size: 1 +19:17:39:534 log after task size: 0 +19:17:39:535 log task queue not empty +19:17:39:535 log task size: 1 +19:17:39:535 log before pop task size: 1 +19:17:39:535 log after task size: 0 +19:17:39:535 log task queue not empty +19:17:39:535 log task size: 1 +19:17:39:535 log before pop task size: 1 +19:17:39:535 log after task size: 0 +19:17:39:536 log task queue not empty +19:17:39:536 log task size: 1 +19:17:39:536 log before pop task size: 1 +19:17:39:536 log after task size: 0 +19:17:39:536 log task queue not empty +19:17:39:537 log task size: 1 +19:17:39:537 log before pop task size: 1 +19:17:39:537 log after task size: 0 +19:17:39:537 log task queue not empty +19:17:39:537 log task size: 1 +19:17:39:537 log before pop task size: 1 +19:17:39:537 log after task size: 0 +19:17:39:538 log task queue not empty +19:17:39:538 log task size: 1 +19:17:39:538 log before pop task size: 1 +19:17:39:538 log after task size: 0 +19:17:39:538 log task queue not empty +19:17:39:538 log task size: 1 +19:17:39:539 log before pop task size: 1 +19:17:39:539 log after task size: 0 +19:17:40:031 log timer task size: 1 +19:17:40:047 log timer task size: 1 +19:17:40:047 log timer task size: 1 +19:17:40:047 log timer task size: 1 +19:17:40:047 log timer task size: 1 +19:17:40:047 log timer task size: 1 +19:17:40:047 log timer task size: 1 +19:17:40:583 log task queue not empty +19:17:40:583 log task size: 1 +19:17:40:583 log before pop task size: 1 +19:17:40:583 log after task size: 0 +19:17:41:096 log timer task size: 1 +19:17:41:417 log task queue not empty +19:17:41:417 log task size: 1 +19:17:41:417 log before pop task size: 1 +19:17:41:417 log after task size: 0 +19:17:41:627 log task queue not empty +19:17:41:627 log task size: 1 +19:17:41:627 log before pop task size: 1 +19:17:41:627 log after task size: 0 +19:17:41:919 log timer task size: 1 +19:17:42:139 log timer task size: 1 +19:17:42:197 log task queue not empty +19:17:42:197 log task size: 1 +19:17:42:197 log before pop task size: 1 +19:17:42:197 log after task size: 0 +19:17:42:198 log task queue not empty +19:17:42:198 log task size: 1 +19:17:42:198 log before pop task size: 1 +19:17:42:198 log after task size: 0 +19:17:42:199 log task queue not empty +19:17:42:199 log task size: 1 +19:17:42:199 log before pop task size: 1 +19:17:42:199 log after task size: 0 +19:17:42:199 log task queue not empty +19:17:42:199 log task size: 1 +19:17:42:199 log before pop task size: 1 +19:17:42:199 log after task size: 0 +19:17:42:200 log task queue not empty +19:17:42:200 log task size: 1 +19:17:42:200 log before pop task size: 1 +19:17:42:200 log after task size: 0 +19:17:42:613 log task queue not empty +19:17:42:613 log task size: 1 +19:17:42:613 log before pop task size: 1 +19:17:42:613 log after task size: 0 +19:17:42:614 log task queue not empty +19:17:42:614 log task size: 1 +19:17:42:614 log before pop task size: 1 +19:17:42:614 log after task size: 0 +19:17:42:615 log task queue not empty +19:17:42:615 log task size: 1 +19:17:42:615 log before pop task size: 1 +19:17:42:615 log after task size: 0 +19:17:42:615 log task queue not empty +19:17:42:615 log task size: 1 +19:17:42:616 log before pop task size: 1 +19:17:42:616 log after task size: 0 +19:17:42:616 log task queue not empty +19:17:42:616 log task size: 1 +19:17:42:616 log before pop task size: 1 +19:17:42:616 log after task size: 0 +19:17:42:617 log task queue not empty +19:17:42:617 log task size: 1 +19:17:42:617 log before pop task size: 1 +19:17:42:617 log after task size: 0 +19:17:42:617 log task queue not empty +19:17:42:617 log task size: 1 +19:17:42:617 log before pop task size: 1 +19:17:42:617 log after task size: 0 +19:17:42:618 log task queue not empty +19:17:42:618 log task size: 1 +19:17:42:618 log before pop task size: 1 +19:17:42:618 log after task size: 0 +19:17:42:618 log task queue not empty +19:17:42:618 log task size: 1 +19:17:42:618 log before pop task size: 1 +19:17:42:618 log after task size: 0 +19:17:42:619 log task queue not empty +19:17:42:619 log task size: 1 +19:17:42:619 log before pop task size: 1 +19:17:42:619 log after task size: 0 +19:17:42:619 log task queue not empty +19:17:42:619 log task size: 1 +19:17:42:619 log before pop task size: 1 +19:17:42:619 log after task size: 0 +19:17:42:620 log task queue not empty +19:17:42:620 log task size: 1 +19:17:42:620 log before pop task size: 1 +19:17:42:620 log after task size: 0 +19:17:42:620 log task queue not empty +19:17:42:621 log task size: 1 +19:17:42:621 log before pop task size: 1 +19:17:42:621 log after task size: 0 +19:17:42:621 log task queue not empty +19:17:42:621 log task size: 1 +19:17:42:621 log before pop task size: 1 +19:17:42:621 log after task size: 0 +19:17:42:622 log task queue not empty +19:17:42:622 log task size: 1 +19:17:42:622 log before pop task size: 1 +19:17:42:622 log after task size: 0 +19:17:42:622 log task queue not empty +19:17:42:622 log task size: 1 +19:17:42:622 log before pop task size: 1 +19:17:42:622 log after task size: 0 +19:17:42:623 log task queue not empty +19:17:42:623 log task size: 1 +19:17:42:623 log before pop task size: 1 +19:17:42:623 log after task size: 0 +19:17:42:699 log timer task size: 1 +19:17:42:700 log timer task size: 1 +19:17:42:714 log timer task size: 1 +19:17:42:714 log timer task size: 1 +19:17:43:061 log task queue not empty +19:17:43:062 log task size: 1 +19:17:43:062 log before pop task size: 1 +19:17:43:062 log after task size: 0 +19:17:43:117 log timer task size: 1 +19:17:43:117 log timer task size: 1 +19:17:43:117 log timer task size: 1 +19:17:43:117 log timer task size: 1 +19:17:43:132 log timer task size: 1 +19:17:43:132 log timer task size: 1 +19:17:43:132 log timer task size: 1 +19:17:43:132 log timer task size: 1 +19:17:43:132 log timer task size: 1 +19:17:43:132 log timer task size: 1 +19:17:43:570 log timer task size: 1 diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/oh-package.json5 b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/oh-package.json5 new file mode 100644 index 0000000000000000000000000000000000000000..3cd48480d047d47a1f7977cf549e698ee013edbc --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/oh-package.json5 @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +{ + "modelVersion": "5.0.5", + "description": "Please describe the basic information.", + "dependencies": { + }, + "devDependencies": { + "@ohos/hypium": "1.0.21", + "@ohos/hamock": "1.0.0" + } +} diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/ohosTest.md b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/ohosTest.md new file mode 100644 index 0000000000000000000000000000000000000000..102b4caae37c69dfc96fba377b1d14ebdd27d19b --- /dev/null +++ b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/ohosTest.md @@ -0,0 +1,10 @@ +# ScreenCaptureSample测试用例归档 + +## 用例表 + +| 测试功能 | 预置条件 | 输入 | 预期输出 | 是否自动 | 测试结果 | +|----------------|-----------|---------------------------------------------------------|---------------------------|------|------| +| 拉起应用 | 设备正常运行 | | 成功拉起应用 | 是 | Pass | +| 录屏存文件功能 | 进入示例应用 | 1. 点击录屏存文件按钮
2. 点击Start
3. 一段时间后点击Stop | 生成录屏文件 | 是 | Pass | +| 录屏取码流功能 | 进入示例应用 | 1. 点击录屏取码流按钮
2. 点击Start
3. 一段时间后点击Stop | 生成录屏码流文件、麦克风音频文件和设备内录音频文件 | 是 | Pass | +| 录屏取码流Surface模式 | 进入示例应用 | 1. 点击录屏取码流Surface模式按钮
2. 点击Start
3. 一段时间后点击Stop | 生成录屏文件 | 是 | Pass | diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/pic1.jpg b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/pic1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0f81da1361eb0a4ab40b23c15a0c8953fc2eb099 Binary files /dev/null and b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/pic1.jpg differ diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/pic2.jpg b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/pic2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b0be059b6ab1b7a647d4641aeebf48e397c74dc7 Binary files /dev/null and b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/pic2.jpg differ diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/pic3.jpg b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/pic3.jpg new file mode 100644 index 0000000000000000000000000000000000000000..635ff4f70f818a2441d17a3fcdc02c0989147e8d Binary files /dev/null and b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/pic3.jpg differ diff --git a/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/pic4.jpg b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/pic4.jpg new file mode 100644 index 0000000000000000000000000000000000000000..330f8bc6e522d31a3a2aca747b7583386e9a7416 Binary files /dev/null and b/code/DocsSample/Media/ScreenCapture/ScreenCaptureSample/pic4.jpg differ