使用AVScreenCapture录屏写文件(C/C++)
=============================

屏幕录制主要为主屏幕录屏功能。

开发者可以调用[AVScreenCapture](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/media-kit-intro#avscreencapture)模块的C API接口，完成屏幕录制，采集设备内、麦克风等的音视频源数据。可以调用录屏模块获取音视频文件，然后通过文件的形式流转到其他模块进行播放或处理，达成文件形式分享屏幕内容的场景。

录屏模块和窗口（Window）、图形（Graphic）等模块协同完成整个视频采集的流程。

使用AVScreenCapture录制屏幕涉及到AVScreenCapture实例的创建、音视频采集参数的配置、采集的开始与停止、资源的释放等。

开始屏幕录制时正在通话中或者屏幕录制过程中来电，录屏将自动停止。因通话中断的录屏会上报OH\_SCREEN\_CAPTURE\_STATE\_STOPPED\_BY\_CALL状态。

屏幕录制过程中发生系统用户切换事件时，录屏将自动停止。因系统用户切换中断的录屏会上报OH\_SCREEN\_CAPTURE\_STATE\_STOPPED\_BY\_USER\_SWITCHES状态。

本开发指导将以完成一次屏幕数据录制的过程为例，向开发者讲解如何使用AVScreenCapture进行屏幕录制，详细的API声明请参考[AVScreenCapture API参考](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-avscreencapture)。

如果配置了采集麦克风音频数据，需对应配置麦克风权限ohos.permission.MICROPHONE和申请长时任务，配置方式请参见[向用户申请权限](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/request-user-authorization)、[申请长时任务](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/continuous-task)。

从API version 22开始，在PC/2in1设备上对应用进行录屏时，可通过申请权限**ohos.permission.TIMEOUT\_SCREENOFF\_DISABLE\_LOCK**，实现在屏幕熄灭但不锁屏的场景下，继续保持录制的效果，配置方式请参见[声明权限](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/declare-permissions)。

从API version 22开始，在PC/2in1设备上对应用进行录屏时，可通过申请权限**ohos.permission.CUSTOM\_SCREEN\_RECORDING**，实现在录制屏幕时不再弹出隐私告警弹窗。配置方式请参见[受限开放权限](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/restricted-permissions)。

#### 开发步骤及注意事项

使用AVScreenCapture时要明确其状态的变化，在创建实例后，调用对应的方法可以进入指定的状态实现对应的行为。

在确定的状态下执行不合适的方法会导致AVScreenCapture发生错误，开发者需要在调用状态转换的方法前进行状态检查，避免程序运行异常。

**在 CMake 脚本中链接动态库**

target\_link\_libraries(entry PUBLIC libnative\_avscreen\_capture.so)

1.  添加头文件。
    
    #include "napi/native\_api.h"
    #include <multimedia/player\_framework/native\_avscreen\_capture.h>
    #include <multimedia/player\_framework/native\_avscreen\_capture\_base.h>
    #include <multimedia/player\_framework/native\_avscreen\_capture\_errors.h>
    #include <fcntl.h>
    #include <string>
    #include <unistd.h>
    
2.  创建AVScreenCapture实例capture。
    
    OH\_AVScreenCapture\* capture = OH\_AVScreenCapture\_Create();
    
3.  配置屏幕录制参数。
    
    创建AVScreenCapture实例capture后，可以设置屏幕录制所需要的参数。
    
    其中，录屏存文件时默认录制内录，麦克风可以动态开关，可以同时内外录制。
    
    同时，录屏存文件需要设置状态回调，感知录制状态。
    
    // 录屏时获取麦克风或者内录，内录参数必填，如果都设置了，内录和麦克风的参数设置需要一致。
    OH\_AudioCaptureInfo micCapInfo = {
        .audioSampleRate = 48000,
        .audioChannels = 2,
        .audioSource = OH\_MIC
    };
    
    OH\_AudioCaptureInfo innerCapInfo = {
        .audioSampleRate = 48000,
        .audioChannels = 2,
        .audioSource = OH\_ALL\_PLAYBACK
    };
    // 录屏音频输出规格配置。audioBitrate保证输出文件的比特率为设置的预期比特率，和audioSampleRate无强关联。
    OH\_AudioEncInfo audioEncInfo = {
        .audioBitrate = 48000,
        .audioCodecformat = OH\_AAC\_LC
    };
    
    OH\_VideoCaptureInfo videoCapInfo = {
        .videoFrameWidth = 768,
        .videoFrameHeight = 1280,
        .videoSource = OH\_VIDEO\_SOURCE\_SURFACE\_RGBA
    };
    
    OH\_VideoEncInfo videoEncInfo = {
        .videoCodec = OH\_H264,
        .videoBitrate = 2000000,
        .videoFrameRate = 30
    };
    
    OH\_AudioInfo audioInfo = {
        .innerCapInfo = innerCapInfo,
        .audioEncInfo = audioEncInfo
    };
    
    OH\_VideoInfo videoInfo = {
        .videoCapInfo = videoCapInfo,
        .videoEncInfo = videoEncInfo
    };
    
    config = {
        .captureMode = OH\_CAPTURE\_HOME\_SCREEN,
        .dataType = OH\_CAPTURE\_FILE,
        .audioInfo = audioInfo,
        .videoInfo = videoInfo,
    };
    
    OH\_AVScreenCapture\_Init(capture, config);
    
4.  调用StartScreenRecording()方法开始进行屏幕录制。
    
    OH\_AVScreenCapture\_StartScreenRecording(capture);
    
5.  调用StopScreenRecording()方法停止录制。
    
    OH\_AVScreenCapture\_StopScreenRecording(capture);
    
6.  调用Release()方法销毁实例，释放资源。
    
    OH\_AVScreenCapture\_Release(capture);
    

#### 完整示例

下面展示了使用AVScreenCapture屏幕录制存文件的完整示例代码。

#include "napi/native\_api.h"
#include <multimedia/player\_framework/native\_avscreen\_capture.h>
#include <multimedia/player\_framework/native\_avscreen\_capture\_base.h>
#include <multimedia/player\_framework/native\_avscreen\_capture\_errors.h>
#include <fcntl.h>
#include <string>
#include <unistd.h>

int32\_t outputFd;
struct OH\_AVScreenCapture\* capture;

void OnStateChange(struct OH\_AVScreenCapture \*capture, OH\_AVScreenCaptureStateCode stateCode, void \*userData) {
    (void)capture;
    
    if (stateCode == OH\_SCREEN\_CAPTURE\_STATE\_STARTED) {
        // 处理状态变更。
    }
    if (stateCode == OH\_SCREEN\_CAPTURE\_STATE\_STOPPED\_BY\_CALL ||
        stateCode == OH\_SCREEN\_CAPTURE\_STATE\_STOPPED\_BY\_USER\_SWITCHES) {
        // 录屏中断状态处理。
    }
    if (stateCode == OH\_SCREEN\_CAPTURE\_STATE\_INTERRUPTED\_BY\_OTHER) {
        // 处理状态变更。
    }
    (void)userData;
}

// 获取录屏屏幕id的回调函数OnDisplaySelected()。
void OnDisplaySelected(struct OH\_AVScreenCapture \*capture, uint64\_t displayId, void \*userData) {
    (void)capture;
    (void)displayId;
    (void)userData;
}

// 录屏内容变更回调函数OnCaptureContentChanged()。
void OnCaptureContentChanged(struct OH\_AVScreenCapture \*capture, OH\_AVScreenCaptureContentChangedEvent event, OH\_Rect \*area, void \*userData) {
    (void)capture;
    if (event == OH\_SCREEN\_CAPTURE\_CONTENT\_HIDE) {
        // 处理录屏内容变为隐藏。
    }
    if (event == OH\_SCREEN\_CAPTURE\_CONTENT\_VISIBLE) {
        // 处理录屏内容变为可见。
        // 录屏内容变为可见时，可通过回调回传的area参数，获取窗口的位置信息。
    }
    if (event == OH\_SCREEN\_CAPTURE\_CONTENT\_UNAVAILABLE) {
        // 处理录屏内容变为不可用，如录屏窗口关闭。
    }
    (void)area;
    (void)userData;
}

// 手工确认页面用户选择结果的回调函数OnUserSelected()。
void OnUserSelected(OH\_AVScreenCapture\* capture, OH\_AVScreenCapture\_UserSelectionInfo\* selections, void \*userData) {
    (void)capture;
    (void)userData;
    int selectType = 0;
    uint64\_t displayId = 0;

    // 通过获取接口，拿到对应的选择类型和屏幕Id。OH\_AVScreenCapture\_UserSelectionInfo\* selections仅在OnUserSelected回调中有效。
    OH\_AVSCREEN\_CAPTURE\_ErrCode errorSelectType = OH\_AVScreenCapture\_GetCaptureTypeSelected(selections, &selectType);
    OH\_AVSCREEN\_CAPTURE\_ErrCode errorDisplayId = OH\_AVScreenCapture\_GetDisplayIdSelected(selections, &displayId);
}

// 开始录屏时调用StartScreenCapture。
static napi\_value StartScreenCapture(napi\_env env, napi\_callback\_info info) {
    // 初始化录屏参数，传入配置信息OH\_AVScreenCaptureConfig。
    OH\_AVScreenCaptureConfig config;
    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\_VideoCaptureInfo videoCapInfo = {
        .videoFrameWidth = 768,
        .videoFrameHeight = 1280,
        .videoSource = OH\_VIDEO\_SOURCE\_SURFACE\_RGBA
    };

    OH\_VideoEncInfo videoEncInfo = {
        .videoCodec = OH\_VideoCodecFormat::OH\_H264,
        .videoBitrate = 2000000,
        .videoFrameRate = 30
    };

    OH\_AudioInfo audioInfo = {
        .micCapInfo = micCapInfo,
        .innerCapInfo = innerCapInfo,
        .audioEncInfo = audioEncInfo
    };

    OH\_VideoInfo videoInfo = {
        .videoCapInfo = videoCapInfo,
        .videoEncInfo = videoEncInfo
    };

    config = {
        .captureMode = OH\_CAPTURE\_HOME\_SCREEN,
        .dataType = OH\_CAPTURE\_FILE,
        .audioInfo = audioInfo,
        .videoInfo = videoInfo,
    };

    // 实例化ScreenCapture。
    capture = OH\_AVScreenCapture\_Create();

    OH\_RecorderInfo recorderInfo;
    const std::string SCREEN\_CAPTURE\_ROOT = "/data/storage/el2/base/files/";
    outputFd = open((SCREEN\_CAPTURE\_ROOT + "screen01.mp4").c\_str(), O\_RDWR | O\_CREAT, 0777);

    // 处理打开失败或创建失败的情况，返回报错结果。
    if (outputFd == -1) {
        napi\_value errCode;
        napi\_create\_double(env, AV\_SCREEN\_CAPTURE\_ERR\_IO, &errCode);
        return errCode;
    }

    std::string fileUrl = "fd://" + std::to\_string(outputFd);
    recorderInfo.url = const\_cast<char \*>(fileUrl.c\_str());
    recorderInfo.fileFormat = OH\_ContainerFormatType::CFT\_MPEG\_4;
    config.recorderInfo = recorderInfo;

    // 设置状态回调。
    OH\_AVScreenCapture\_SetStateCallback(capture, OnStateChange, nullptr);

    // 可选，设置录屏内容变化回调。
    OH\_Rect\* area = nullptr;
    OH\_AVScreenCapture\_SetCaptureContentChangedCallback(capture, OnCaptureContentChanged, area);

    // 可选，设置隐私窗口屏蔽模式。
    int value = 0;
    OH\_AVScreenCapture\_CaptureStrategy\* strategy = OH\_AVScreenCapture\_CreateCaptureStrategy();
    OH\_AVScreenCapture\_StrategyForPrivacyMaskMode(strategy, value);
    OH\_AVScreenCapture\_SetCaptureStrategy(capture, strategy);

    // 可选，设置录屏屏幕Id回调，必须在开始录屏前调用。
    OH\_AVScreenCapture\_SetDisplayCallback(capture, OnDisplaySelected, nullptr);

    // 可选 设置手工确认页面用户选择结果的回调，必须在开始录屏前调用。
    OH\_AVScreenCapture\_SetSelectionCallback(capture, OnUserSelected, nullptr);

    // 可选，设置光标显示开关，开始录屏前后均可调用。
    OH\_AVScreenCapture\_ShowCursor(capture, false);

    // 进行初始化操作。
    int32\_t retInit = OH\_AVScreenCapture\_Init(capture, config);

    // 可选（API version 20开始支持）：可以根据需要设置区域坐标和大小，设置想要捕获的区域，如下方创建了一个从（0，0）为起点的长100，宽100的矩形区域。此接口也可以在开始录屏以后设置。
    OH\_Rect\* region = new OH\_Rect;
    region->x = 0;
    region->y = 0;
    region->width = 100;
    region->height = 100;
    uint64\_t regionDisplayId = 0; // 传入矩形区域所在的屏幕Id。
    OH\_AVScreenCapture\_SetCaptureArea(capture, regionDisplayId, region);
    
    // 对申请的内存进行释放。
    delete region;

    // 开始录屏。
    int32\_t retStart = OH\_AVScreenCapture\_StartScreenRecording(capture);

    // 开始录屏失败的情况处理，返回报错结果。
    if (retStart != AV\_SCREEN\_CAPTURE\_ERR\_OK) {
        napi\_value errCode;
        napi\_create\_double(env, retStart, &errCode);
        return errCode;
    }

    // 结束录屏见StopScreenCapture。
    
    // 返回调用结果，示例仅返回随意值。
    napi\_value code;
    napi\_create\_double(env, AV\_SCREEN\_CAPTURE\_ERR\_OK, &code);

    return code;
}

// 结束录屏时调用StopScreenCapture。
static napi\_value StopScreenCapture(napi\_env env, napi\_callback\_info info) {
    if (capture != nullptr) {
        // 结束录屏。
        int32\_t retStop = OH\_AVScreenCapture\_StopScreenRecording(capture);

        // 关闭文件访问
        close(outputFd);

        // 结束录屏失败的情况处理，返回报错结果。
        if (retStop != AV\_SCREEN\_CAPTURE\_ERR\_OK) {
            napi\_value errCode;
            napi\_create\_double(env, retStop, &errCode);
            return errCode;
        }

        // 释放ScreenCapture。
        int32\_t retRelease = OH\_AVScreenCapture\_Release(capture);

        // 释放ScreenCapture失败情况下处理，返回报错结果。
        if (retRelease != AV\_SCREEN\_CAPTURE\_ERR\_OK) {
            napi\_value errCode;
            napi\_create\_double(env, retRelease, &errCode);
            return errCode;
        }

        capture = nullptr;
    }

    // 返回成功结果。
    napi\_value code;
    napi\_create\_double(env, AV\_SCREEN\_CAPTURE\_ERR\_OK, &code);

    return code;
}

EXTERN\_C\_START
static napi\_value Init(napi\_env env, napi\_value exports) {
    napi\_property\_descriptor desc\[\] = {
        {"startScreenCapture", nullptr, StartScreenCapture, nullptr, nullptr, nullptr, napi\_default, nullptr},
        {"stopScreenCapture", nullptr, StopScreenCapture, 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 RegisterEntryModule(void) { napi\_module\_register(&demoModule); }