# 快速开始云端录制 (/zh-CN/realtime-media/cloud-recording/get-started/quick-start)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

选择你要使用的接口或 SDK，完成云端录制的环境准备、参数配置、示例运行和核心流程验证。


## RESTful API

本文向新手介绍如何使用声网 RESTful API 开始第一次云端录制。

## 前提条件 [#前提条件]

### 开通服务并获取关键参数 [#开通服务并获取关键参数]

参考[开通云端录制服务](/zh-CN/realtime-media/cloud-recording/build/setup-and-access/enable-service)在声网控制台开通云端录制，并复制保存如下参数，以供后续使用：

- App ID
- 临时 Token
- 客户 ID
- 客户密钥

### 开通第三方云存储 [#开通第三方云存储]

云端录制文件需要上传到第三方云存储。开始录制前，请开通第三方云存储服务，并获取如下字段以备后用：

- Bucket
- Access Key
- Secret Key

<Accordions>
<Accordion title="开通方式">

开通方式请参考云存储官网文档，本文后续步骤以阿里云为例：

- [Amazon S3](https://aws.amazon.com/cn/s3/?c=s&sec=srv)
- [阿里云](https://www.aliyun.com/product/oss)
- [腾讯云](https://cloud.tencent.com/product/cos)
- [Microsoft Azure](https://azure.microsoft.com/zh-cn/services/storage/blobs/)
- [谷歌云](https://cloud.google.com/storage)
- [华为云](https://www.huaweicloud.com/product/obs.html)
- [百度智能云](https://cloud.baidu.com/product/bos.html)

</Accordion>
</Accordions>

### 模拟音视频互动场景 [#模拟音视频互动场景]

模拟一个音视频互动场景，云端录制服务后续会录制该场景下的音视频。

此处使用声网实时互动 [Web Demo](https://webdemo.agora.io/index.html) 在浏览器中模拟一个**基础视频直播**场景，频道内有一名主播发流：

1. 打开 Web Demo，完成**初始化设置**。
2. 进入**基础视频直播**页面，在 **Channel** 填入频道名为 show，点击 **Join as host**。

如果模拟成功，你会在 **local stream** 区域看到你的视频画面。

## 进行云端录制 [#进行云端录制]

本节介绍进行云端录制的极简流程。为方便说明，本节以录制之前模拟的音视频直播为例，并假设录制主播的音频和视频录制到 MP4 文件中。

### 申请录制服务器资源 [#申请录制服务器资源]

#### 请求 [#请求]

```bash
# [!code highlight:1]
curl --location --request POST 'https://api.sd-rtn.com/v1/apps/<your_app_id>/cloud_recording/acquire' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
# [!code highlight:1]
--header 'Authorization: <http_basic_auth>' \
--data '{
  "cname": "show",
  "uid": "123",
  "clientRequest": {}
}'
```

你可以使用[第三方在线工具](https://www.debugbear.com/basic-auth-header-generator)快速得到 Authorization 值。将客户 ID 和客户密钥分别填入 Username 和 Password 框，得到形如 `Authorization: Basic NDI1OTQ3N2I4MzYy...YwZjA=` 的结果。将该结果替代上述代码中的 `Authorization: <http_basic_auth>` 即可。

#### 响应 [#响应]

```json
{
    "cname": "show",
    "uid": "123",
    // [!code highlight:1]
    "resourceId": "SGXETXXX"
}
```

你需要复制 `resourceId` 以备后用。

### 开始录制 [#开始录制]

#### 请求 [#请求-1]

```bash
# [!code highlight:1]
curl --location --request POST 'https://api.sd-rtn.com/v1/apps/<your_app_id>/cloud_recording/resourceid/<resourceId_returned>/mode/mix/start' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
# [!code highlight:1]
--header 'Authorization: <http_basic_auth>' \
--data '{
  "cname": "show",
  "uid": "123",
  "clientRequest": {
    # [!code highlight:1]
    "token": "<your_token>",
    "recordingConfig": {
      "channelType": 1
    },
    # [!code highlight:10]
    "storageConfig": {
      "vendor": 2,
      "region": 3,
      "bucket": "<your_storage_bucket_name>",
      "accessKey": "<your_storage_access_key>",
      "secretKey": "<your_storage_secret_key>",
      "fileNamePrefix": [
        "quickstart"
      ]
    }
  }
}'
```

`"vendor": 2` 代表使用阿里云，如果你使用其他云存储厂商，请参考下表赋值：

|云存储厂商 |Vendor ID|
|-----|---|
|Amazon S3|1|
|阿里云|2|
|腾讯云|3|
|Microsoft Azure|5|
|谷歌云|6|
|华为云|7|
|百度智能云|8|

#### 响应 [#响应-1]

```json
{
    "cname": "show",
    "uid": "123",
    "resourceId": "SGXETXXX",
    // [!code highlight:1]
    "sid": "04a85XXX"
}
```

你需要复制 `sid` 以备后用。

## 查看录制文件 [#查看录制文件]

参考第三方云存储官方文档操作，查看云存储中的文件。录制文件位于 `quickstart` 目录下。如下截图以阿里云为例：

![阿里云 OSS 中录制文件目录示意图](https://web-cdn.agora.io/docs-portal/img/cloud-recording/quick-oss-1.png)

在录制过程中，会有一个 `.m3u8` 和多个 `.ts` 文件。点击任意一个 `.ts` 文件即可播放。

如果还需要查看 `.mp4` 文件，你需要先结束录制，再查看云存储文件。结束录制可参照如下代码：

```bash
# [!code highlight:1]
curl --location --request POST 'https://api.sd-rtn.com/v1/apps/<your_app_id>/cloud_recording/resourceid/<resourceId_returned>/sid/<sid_returned>/mode/mix/stop' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
# [!code highlight:1]
--header 'Authorization: <http_basic_auth>' \
--data '{
  "cname": "show",
  "uid": "123",
  "clientRequest": {}
}'
```

最终 `quickstart` 录制文件夹内包含如下：

```shell
.
├── <sid>_show.m3u8
├── <sid>_show_0.mp4
├── <sid>_show_20240507070329192.ts
├── <sid>_show_20240507070346226.ts
├── <sid>_show_20240507070402264.ts
├── <sid>_show_20240507070418236.ts
├── <sid>_show_20240507070434208.ts
├── <sid>_show_20240507070450246.ts
├── <sid>_show_20240507070506218.ts
├── <sid>_show_20240507070522256.ts
├── <sid>_show_20240507070538236.ts
├── <sid>_show_20240507070554266.ts
├── <sid>_show_20240507070610238.ts
├── <sid>_show_20240507070626210.ts
└── <sid>_show_20240507070642248.ts
```

有关录制文件的详细信息，请参考[录制文件介绍](/zh-CN/realtime-media/cloud-recording/build/manage-recorded-files/introduce-recorded-files)。

## 进阶设置 [#进阶设置]

本文提供的示例代码仅为快速上手时使用。在实际业务中，你需要根据实际业务场景修改请求参数。如下示例代码展示了录制直播频道中所有的音视频流，且输出的视频流需要转码的参数配置：

```bash
curl --location --request POST 'https://api.sd-rtn.com/v1/apps/<your_app_id>/cloud_recording/resourceid/<resourceId_returned>/mode/mix/start' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--header 'Authorization: <http_basic_auth>' \
--data '{
  "cname": "show",
  "uid": "123",
  "clientRequest": {
    "token": "<your_token>",
    # [!code highlight:20]
    "recordingConfig": {
      "channelType": 1,
      "streamTypes": 2,
      "audioProfile": 2,
      "maxIdleTime": 30,
      "transcodingConfig": {
        "width": 640,
        "height": 640,
        "fps": 15,
        "bitrate": 800,
        "mixedVideoLayout": 0,
        "backgroundColor": "#000000"
      },
      "subscribeVideoUids": [
        "#allstream#"
      ],
      "subscribeAudioUids": [
        "#allstream#"
      ]
    },
    "recordingFileConfig": {
      "avFileType": [
        "hls",
        "mp4"
      ]
    },
    "storageConfig": {
      "vendor": 2,
      "region": 3,
      "bucket": "<your_storage_bucket_name>",
      "accessKey": "<your_storage_access_key>",
      "secretKey": "<your_storage_secret_key>",
      "fileNamePrefix": [
        "quickstart"
      ]
    }
  }
}'
```

云端录制支持设置的所有参数请参考 [API 文档](/zh-CN/api-reference/api-ref/cloud-recording/start)。

## 排查错误 [#排查错误]

- [响应状态码和错误码](/zh-CN/realtime-media/cloud-recording/reference/response-code)
- [为什么成功开启云端录制后调用 query 方法返回 404？](/zh-CN/reference/faq/integration/return_404)
- [应用崩溃后对云端录制有什么影响？](/zh-CN/reference/faq/integration/abnormal_exit)
- [如何选择云存储 bucket 区域及处理跨区上传问题？](/zh-CN/reference/faq/integration/bucket_region)
- [为什么无法通过浏览器调用云端录制 RESTful API？](/zh-CN/reference/faq/product/call_api_in_browser)
- [为什么第三方云存储中没有录制文件？](/zh-CN/reference/faq/integration/fail_to_upload)
- [如何获取 M3U8 文件地址？](/zh-CN/reference/faq/integration/acquire_file_directory)
- [云端录制如何处理服务器断网、进程被杀问题？](/zh-CN/reference/faq/integration/high_availability)


## Go

本文向新手介绍如何使用声网 Go SDK 开始第一次云端录制。

Go SDK 旨在帮助开发者更轻松集成声网 RESTful API。该 SDK 具有如下特性：

- 简化通信流程：通过封装 RESTful API 的请求和响应处理，让你与 RESTful API 的通信更简单。
- 保障可用性：遇到 DNS 解析失败、网络错误、请求超时等网络问题时，会自动切换到最佳域名，确保 REST 服务的可用性。
- API 易用性：提供简洁易懂的 API，让你能轻松实现开启云端录制、停止云端录制等常用功能。
- 其他优势：基于 Go 语言编写，具有高效性、并发性、可扩展性。

## 前提条件 [#前提条件]

开始前请确保你的本地开发环境满足如下条件：

- [Go](https://go.dev/dl/) 1.18 或以上版本。
- 参考[开通云端录制服务](/zh-CN/realtime-media/cloud-recording/build/setup-and-access/enable-service)在声网控制台开通云端录制，并复制保存 App ID、临时 Token、客户 ID 和客户密钥。

  :::warning[注意]
  临时 Token 的有效期是 24 小时。Token 过期会导致加入频道失败。
  :::

- 云端录制文件需要上传到第三方云存储。开始录制前，请开通第三方云存储服务，并获取 Bucket、Access Key、Secret Key 以备用。

<Accordions type="multiple">
<Accordion title="开通方式">

开通方式请参考云存储官网文档，本文后续步骤以阿里云为例：

- [Amazon S3](https://aws.amazon.com/cn/s3/?c=s&sec=srv)
- [阿里云](https://www.aliyun.com/product/oss)
- [腾讯云](https://cloud.tencent.com/product/cos)
- [Microsoft Azure](https://azure.microsoft.com/zh-cn/services/storage/blobs/)
- [谷歌云](https://cloud.google.com/storage)
- [华为云](https://www.huaweicloud.com/product/obs.html)
- [百度智能云](https://cloud.baidu.com/product/bos.html)

</Accordion>
</Accordions>


- 在项目中实现了基础的音视频互动场景。你也可以参考[文档](/zh-CN/realtime-media/cloud-recording/get-started/quick-start/restful-api#模拟音视频互动场景)使用声网实时互动 [Web Demo](https://webdemo.agora.io/index.html) 在浏览器中模拟一个音视频互动场景。

## 创建项目并安装 SDK [#创建项目并安装-sdk]

#### 创建项目 [#创建项目]

参考如下步骤创建并配置新项目。

1. 创建一个名为 `test-recording` 的空项目文件夹。

2. 进入 `test-recording` 路径，执行如下命令初始化 Go 模块。

    ```shell
    go mod init test-recording
    ```

3. 在项目路径下，新建一个空的 `main.go` 文件，我们会在这个文件中实现一个极简的云端录制任务。

#### 安装并引入 SDK [#安装并引入-sdk]

按照如下命令行安装需要的依赖库，并将 SDK 引入项目中。

1. 在项目路径下，执行如下命令，安装 Go SDK。

    ```shell
    # 安装 Go SDK
    go get -u github.com/AgoraIO-Community/agora-rest-client-go
    # 更新依赖
    go mod tidy
    ```

2. 安装完成后，在你的 `main.go` 文件中添加如下代码，以引入 Go SDK。

    ```go
    import (
      "context"
      "log"
      "time"

      "github.com/AgoraIO-Community/agora-rest-client-go/agora/auth"
      "github.com/AgoraIO-Community/agora-rest-client-go/agora/domain"
      agoraLogger "github.com/AgoraIO-Community/agora-rest-client-go/agora/log"
      "github.com/AgoraIO-Community/agora-rest-client-go/services/cloudrecording"
      cloudRecordingAPI "github.com/AgoraIO-Community/agora-rest-client-go/services/cloudrecording/api"
      "github.com/AgoraIO-Community/agora-rest-client-go/services/cloudrecording/req"
    )
    ```

## 开始云端录制 [#开始云端录制]

本节介绍进行云端录制的极简流程。为方便说明，本节以录制之前模拟的音视频直播为例，并假设录制主播的音频和视频录制到 MP4 文件中。

### 定义变量 [#定义变量]

在 `main.go` 文件中，添加如下代码，定义或配置关键参数，取值参考[前提条件](#前提条件)。

```go
// 定义关键参数
const (
  appId    = "<your_app_id>"
  cname    = "show"
  // 录制服务加入频道的用户 ID
  uid      = "123"
  // 客户 ID
  username = "<your_key>"
  // 客户密钥
  password = "<your_secret>"
  token    = "<your_token>"
)

// 配置云存储
var storageConfig = &cloudRecordingAPI.StorageConfig{
  Vendor:         2,
  Region:         1,
  Bucket:         "<your_storage_bucket_name>",
  AccessKey:      "<your_storage_access_key>",
  SecretKey:      "<your_storage_secret_key>",
  FileNamePrefix: []string{
    "quickstart",
  },
}
```

其中，

- `cname` 值设为 `show`，表示加入录制实时音视频互动所在的频道。
- `Vendor` 值设为 `2` 代表使用阿里云；`Region` 值设为 `1` 表示 `CN_Shanghai`。如果你使用其他云存储厂商，参考[第三方云存储地区说明](/zh-CN/realtime-media/cloud-recording/reference/api-reference#第三方云存储地区说明)赋值。

### 创建并初始化 Client [#创建并初始化-client]

在 `main.go` 文件中，添加如下代码，创建并初始化 Client。

```go
// Initialize Cloud Recording Config
config := &cloudrecording.Config{
  AppID:      appId,
  Credential: auth.NewBasicAuthCredential(username, password),
  // Specify the region where the server is located. Options include CN, EU, AP, US.
  // The client will automatically switch to use the best domain based on the configured region.
  DomainArea: domain.CN,
  // Specify the log output level. Options include DebugLevel, InfoLevel, WarningLevel, ErrLevel.
  // To disable log output, set logger to DiscardLogger.
  Logger: agoraLogger.NewDefaultLogger(agoraLogger.DebugLevel),
}

// Initialize the cloud recording service client
cloudRecordingClient, err := cloudrecording.NewClient(config)
if err != nil {
  log.Fatal(err)
}
```

### 申请录制服务器资源 [#申请录制服务器资源]

调用 `acquire` 申请录制服务器资源。你需要存储 `resourceId` 以备后用。

```go
// Call the Acquire API of the cloud recording service client
acquireResp, err := cloudRecordingClient.MixRecording().
  Acquire(context.TODO(), cname, uid, &req.AcquireMixRecodingClientRequest{})
  // Handle non-business errors
if err != nil {
  log.Fatal(err)
}

// Handle business response
if acquireResp.IsSuccess() {
  log.Printf("acquire success:%+v\n", acquireResp)
} else {
  log.Fatalf("acquire failed:%+v\n", acquireResp)
}
```

### 开始录制 [#开始录制]

获取资源后，调用 `start` 开始录制。

```go
// Call the Start API of the cloud recording service client
resourceId := acquireResp.SuccessRes.ResourceId
startResp, err := cloudRecordingClient.MixRecording().
  Start(context.TODO(), resourceId, cname, uid, &req.StartMixRecordingClientRequest{
    Token: token,
    RecordingConfig: &cloudRecordingAPI.RecordingConfig{
      ChannelType:  1,
    },
    RecordingFileConfig: &cloudRecordingAPI.RecordingFileConfig{
      AvFileType: []string{
        "hls",
        "mp4",
      },
    },
    StorageConfig: storageConfig,
  })

  // Handle non-business errors
if err != nil {
  log.Fatal(err)
}

// Handle business response
if startResp.IsSuccess() {
  log.Printf("start success:%+v\n", startResp)
} else {
  log.Fatalf("start failed:%+v\n", startResp)
}

sid := startResp.SuccessResponse.Sid
```

### 停止录制 [#停止录制]

完成录制后，调用 `stop` 结束录制。

```go
// Call the Stop API of the cloud recording service client
stopResp, err := cloudRecordingClient.MixRecording().
  Stop(context.TODO(), resourceId, sid, cname, uid, true)
  // Handle non-business errors
if err != nil {
  log.Fatal(err)
}

// Handle business response
if stopResp.IsSuccess() {
  log.Printf("stop success:%+v\n", stopResp)
} else {
  log.Printf("stop failed:%+v\n", stopResp)
}
```

### 完整示例代码 [#完整示例代码]

`main.go` 文件中完整代码如下，你可自行比对：

<Accordions>
<Accordion title="完整示例代码">

```go
package main

import (
  "context"
  "log"
  "time"

  "github.com/AgoraIO-Community/agora-rest-client-go/agora/auth"
  "github.com/AgoraIO-Community/agora-rest-client-go/agora/domain"
  agoraLogger "github.com/AgoraIO-Community/agora-rest-client-go/agora/log"
  "github.com/AgoraIO-Community/agora-rest-client-go/services/cloudrecording"
  cloudRecordingAPI "github.com/AgoraIO-Community/agora-rest-client-go/services/cloudrecording/api"
  "github.com/AgoraIO-Community/agora-rest-client-go/services/cloudrecording/req"
)

const (
  appId    = "<your appId>"
  cname    = "<your cname>"
  // 录制服务加入频道的用户 ID
  uid      = "<your uid>"
  // 客户 ID
  username = "<the username of basic auth credential>"
  // 客户密钥
  password = "<the password of basic auth credential>"
  token    = "<your token>"
)

var storageConfig = &cloudRecordingAPI.StorageConfig{
  Vendor:    2,
  Region:    1,
  Bucket:    "",
  AccessKey: "",
  SecretKey: "",
  FileNamePrefix: []string{
    "quickstart",
  },
}

func main() {
  // Initialize Cloud Recording Config
  config := &cloudrecording.Config{
    AppID:      appId,
    Credential: auth.NewBasicAuthCredential(username, password),
    // Specify the region where the server is located. Options include CN, EU, AP, US.
    // The client will automatically switch to use the best domain based on the configured region.
    DomainArea: domain.CN,
    // Specify the log output level. Options include DebugLevel, InfoLevel, WarningLevel, ErrLevel.
    // To disable log output, set logger to DiscardLogger.
    Logger: agoraLogger.NewDefaultLogger(agoraLogger.DebugLevel),
  }

  // Initialize the cloud recording service client
  cloudRecordingClient, err := cloudrecording.NewClient(config)
  if err != nil {
    log.Fatal(err)
  }

  // Call the Acquire API of the cloud recording service client
  acquireResp, err := cloudRecordingClient.MixRecording().
    Acquire(context.TODO(), cname, uid, &req.AcquireMixRecodingClientRequest{})
    // Handle non-business errors
  if err != nil {
    log.Fatal(err)
  }

  // Handle business response
  if acquireResp.IsSuccess() {
    log.Printf("acquire success:%+v\n", acquireResp)
  } else {
    log.Fatalf("acquire failed:%+v\n", acquireResp)
  }

  // Call the Start API of the cloud recording service client
  resourceId := acquireResp.SuccessRes.ResourceId
  startResp, err := cloudRecordingClient.MixRecording().
    Start(context.TODO(), resourceId, cname, uid, &req.StartMixRecordingClientRequest{
      Token: token,
      RecordingConfig: &cloudRecordingAPI.RecordingConfig{
        ChannelType:  1,
      },
      RecordingFileConfig: &cloudRecordingAPI.RecordingFileConfig{
        AvFileType: []string{
          "hls",
          "mp4",
        },
      },
      StorageConfig: storageConfig,
    })
    // Handle non-business errors
  if err != nil {
    log.Fatal(err)
  }

  // Handle business response
  if startResp.IsSuccess() {
    log.Printf("start success:%+v\n", startResp)
  } else {
    log.Fatalf("start failed:%+v\n", startResp)
  }

  sid := startResp.SuccessResponse.Sid
  // Query
  for i := 0; i < 6; i++ {
    queryResp, err := cloudRecordingClient.MixRecording().
      QueryHLSAndMP4(context.TODO(), resourceId, sid)
      // Handle non-business errors
    if err != nil {
      log.Fatal(err)
    }

    // Handle business response
    if queryResp.IsSuccess() {
      log.Printf("query success:%+v\n", queryResp)
    } else {
      log.Printf("query failed:%+v\n", queryResp)
    }
    time.Sleep(time.Second * 10)
  }

  // Call the Stop API of the cloud recording service client
  stopResp, err := cloudRecordingClient.MixRecording().
    Stop(context.TODO(), resourceId, sid, cname, uid, true)
    // Handle non-business errors
  if err != nil {
    log.Fatal(err)
  }

  // Handle business response
  if stopResp.IsSuccess() {
    log.Printf("stop success:%+v\n", stopResp)
  } else {
    log.Printf("stop failed:%+v\n", stopResp)
  }
}
```

</Accordion>
</Accordions>

## 查看录制文件 [#查看录制文件]

参考第三方云存储官方文档操作，查看云存储中的文件。根据 `start` 方法中 `storageconfig.fileNamePrefix` 参数的设置，录制生成文件位于 `quickstart` 目录下。如下截图以阿里云为例：

![云存储中 quickstart 录制文件目录示意图](https://web-cdn.agora.io/docs-portal/img/cloud-recording/quick-oss-2.png)

录制过程中生成了 `.m3u8`、`.ts` 和 `mp4` 文件。最终 `quickstart` 文件夹内包含如下：

```shell
.
├── <sid>_testchannel.m3u8
├── <sid>_testchannel_0.mp4
└── <sid>_testchannel_20241119070642248.ts
```

根据你在 `start` 方法中设置的录制时间，云存储中可能会有多个 .ts 文件。录制文件介绍请参考[录制文件命名和切片规则](/zh-CN/realtime-media/cloud-recording/build/manage-recorded-files/introduce-recorded-files#合流和页面录制模式)。

## 进阶设置 [#进阶设置]

本文提供的示例代码仅为快速上手时使用。在实际业务中，你需要根据实际业务场景修改请求参数。如下示例代码展示了录制直播频道中所有的音视频流，且输出的视频流需要转码的参数配置：

```go
startResp, err := cloudRecordingClient.MixRecording().
  Start(context.TODO(), resourceId, cname, uid, &mixrecording.StartMixRecordingClientRequest{
    Token: token,
    RecordingConfig: &cloudRecordingAPI.RecordingConfig{
      ChannelType:  1,
      StreamTypes:  2,
      MaxIdleTime:  30,
      AudioProfile: 2,
      TranscodingConfig: &cloudRecordingAPI.TranscodingConfig{
        Width:            640,
        Height:           640,
        FPS:              15,
        BitRate:          800,
        MixedVideoLayout: 0,
        BackgroundColor:  "#000000",
      },
      SubscribeAudioUIDs: []string{
        "#allstream#",
      },
      SubscribeVideoUIDs: []string{
        "#allstream#",
      },
    },
    RecordingFileConfig: &cloudRecordingAPI.RecordingFileConfig{
      AvFileType: []string{
        "hls",
        "mp4",
      },
    },
    StorageConfig: storageConfig,
  })
```

云端录制支持设置的所有参数请参考 [API 文档](/zh-CN/api-reference/api-ref/cloud-recording/start)。

## 参考信息 [#参考信息]

### 示例项目 [#示例项目]

完成快速开始后，你可以体验更多云端录制示例：

<Cards>
  <Card title="Gitee 示例项目" href="https://gitee.com/agoraio-community/agora-rest-client-go/tree/main/examples/cloudrecording" />
  <Card title="GitHub 示例项目" href="https://github.com/AgoraIO-Community/agora-rest-client-go/tree/main/examples/cloudrecording" />
</Cards>

### 排查错误 [#排查错误]

- [响应状态码和错误码](/zh-CN/realtime-media/cloud-recording/reference/response-code)
- [为什么成功开启云端录制后调用 query 方法返回 404？](/zh-CN/reference/faq/integration/return_404)
- [如何选择云存储 bucket 区域及处理跨区上传问题？](/zh-CN/reference/faq/integration/bucket_region)
- [为什么第三方云存储中没有录制文件？](/zh-CN/reference/faq/integration/fail_to_upload)


## Java

本文向新手介绍如何使用声网 Java SDK 开始第一次云端录制。

Java SDK 旨在帮助开发者更轻松集成声网 RESTful API。该 SDK 具有如下特性：

- 简化通信流程：通过封装 RESTful API 的请求和响应处理，让你与 RESTful API 的通信更简单。
- 保障可用性：遇到 DNS 解析失败、网络错误、请求超时等网络问题时，会自动切换到最佳域名，确保 REST 服务的可用性。
- API 易用性：提供简洁易懂的 API，让你能轻松实现开启云端录制、停止云端录制等常用功能。
- 其他优势：基于 Java 语言编写，具有高效性、并发性、可扩展性。

## 前提条件 [#前提条件]

开始前请确保你的本地开发环境满足如下条件：

- [Java 1.8](https://www.oracle.com/cn/java/technologies/downloads/) 或以上版本。
- 参考[开通云端录制服务](/zh-CN/realtime-media/cloud-recording/build/setup-and-access/enable-service)在声网控制台开通云端录制，并复制保存 App ID、临时 Token、客户 ID 和客户密钥。

  :::warning[注意]
  临时 Token 的有效期是 24 小时。Token 过期会导致加入频道失败。
  :::

- 云端录制文件需要上传到第三方云存储。开始录制前，请开通第三方云存储服务，并获取 Bucket、Access Key、Secret Key 以备用。

<Accordions type="multiple">
<Accordion title="开通方式">

开通方式请参考云存储官网文档，本文后续步骤以阿里云为例：

- [Amazon S3](https://aws.amazon.com/cn/s3/?c=s&sec=srv)
- [阿里云](https://www.aliyun.com/product/oss)
- [腾讯云](https://cloud.tencent.com/product/cos)
- [Microsoft Azure](https://azure.microsoft.com/zh-cn/services/storage/blobs/)
- [谷歌云](https://cloud.google.com/storage)
- [华为云](https://www.huaweicloud.com/product/obs.html)
- [百度智能云](https://cloud.baidu.com/product/bos.html)

</Accordion>
</Accordions>


- 在项目中实现了基础的音视频互动场景。你也可以参考[文档](/zh-CN/realtime-media/cloud-recording/get-started/quick-start/restful-api#模拟音视频互动场景)使用声网实时互动 [Web Demo](https://webdemo.agora.io/index.html) 在浏览器中模拟一个音视频互动场景。

## 创建项目并安装 SDK [#创建项目并安装-sdk]

1. 在你的 IDE 中创建一个 Java 项目。以 IntelliJ IDEA 为例，设置你的项目名称、保存路径，并将 **Build system** 设为 **Maven**。完成创建后，点击 **Create**。

2. 在项目的 `pom.xml` 文件中的 `<dependencies>` 区域添加如下行，将 REST Client 的依赖添加到项目中：

    ```xml
    <dependencies>
        <dependency>
            <groupId>io.agora</groupId>
            <artifactId>agora-rest-client-core</artifactId>
            <version>0.5.0</version>
        </dependency>
    </dependencies>
    ```

## 进行云端录制 [#进行云端录制]

本节介绍进行云端录制的极简流程。为方便说明，本节以录制之前模拟的音视频直播为例，并假设录制主播的音频和视频录制到 MP4 文件中。

### 定义变量 [#定义变量]

在 `Main.java` 文件中，添加如下代码，定义或配置关键参数，取值参考[前提条件](#前提条件)。

```java
public class Main {
    // 定义关键参数
    private static String appId = "<your_app_id>";
    private static String cname = "show";
    // 录制服务加入频道的用户 ID
    private static String uid = "123";
    // 客户 ID
    private static String username = "<your_customer_id>";
    // 客户密钥
    private static String password = "<your_customer_secret>";
    private static String token = "<your_token>";

    // 配置云存储
    private static String accessKey = "<your_storage_access_key>";
    private static String secretKey = "<your_storage_secret_key>";
    private static Integer region = 1;
    private static String bucket = "<your_storage_bucket_name>";
    private static Integer vendor = 2;
}
```

其中，

- `cname` 值设为 `show`，表示加入录制实时音视频互动所在的频道。
- `vendor` 值设为 `2` 代表使用阿里云；`region` 值设为 `1` 表示 `CN_Shanghai`。如果你使用其他云存储厂商，参考[第三方云存储地区说明](/zh-CN/realtime-media/cloud-recording/reference/api-reference#第三方云存储地区说明)赋值。

### 创建并初始化 Client [#创建并初始化-client]

在 `main` 方法中，添加如下代码，创建并初始化 Client。

```java
Credential credential = new BasicAuthCredential(username, password);

// Initialize CloudRecordingConfig
CloudRecordingConfig config = CloudRecordingConfig.builder()
        .appId(appId)
        .credential(credential)
        // Specify the region where the server is located.
        // Optional values are CN, US, EU, AP, and the client will automatically
        // switch to use the best domain name according to the configured region
        .domainArea(DomainArea.CN)
        .build();

// Initialize CloudRecordingClient
CloudRecordingClient cloudRecordingClient = CloudRecordingClient.create(config);
```

### 申请录制服务器资源 [#申请录制服务器资源]

调用 `acquire` 申请录制服务器资源。

```java
AcquireResourceRes acquireResourceRes;

// Acquire resource
try {
  acquireResourceRes = cloudRecordingClient
          .mixScenario()
          .acquire(cname, uid, AcquireMixRecordingResourceClientReq.builder().build())
          .block();
} catch (AgoraException e) {
    System.out.printf("agora error:%s", e.getMessage());
    return;
} catch (Exception e) {
    System.out.printf("unknown error:%s", e.getMessage());
    return;
}

// Check if the response is null
if (acquireResourceRes == null || acquireResourceRes.getResourceId() == null) {
    System.out.println("failed to get resource");
    return;
}

System.out.printf("resourceId:%s", acquireResourceRes.getResourceId());

System.out.println("acquire resource success");
```

### 开始录制 [#开始录制]

获取资源后，调用 `start` 开始录制。

```java
// Define storage config
StartResourceReq.StorageConfig storageConfig = StartResourceReq.StorageConfig.builder()
        .accessKey(accessKey)
        .secretKey(secretKey)
        .bucket(bucket)
        .vendor(vendor)
        .region(region)
        .build();

// Define start resource request
StartMixRecordingResourceClientReq startResourceReq = StartMixRecordingResourceClientReq.builder()
        .token(token)
        .recordingConfig(StartResourceReq.RecordingConfig.builder()
                .channelType(1)
                .build())
        .recordingFileConfig(StartResourceReq.RecordingFileConfig.builder()
                .avFileType(Arrays.asList("hls", "mp4"))
                .build())
        .storageConfig(storageConfig)
        .build();


StartResourceRes startResourceRes;

// Start resource
try {
    startResourceRes = cloudRecordingClient
            .mixScenario()
            .start(cname, uid, acquireResourceRes.getResourceId(), startResourceReq)
            .block();

} catch (AgoraException e) {
    System.out.printf("agora error:%s", e.getMessage());
    return;
} catch (Exception e) {
    System.out.printf("unknown error:%s", e.getMessage());
    return;
}

// Check if the response is null
if (startResourceRes == null || startResourceRes.getSid() == null) {
    System.out.println("failed to start resource");
    return;
}

System.out.printf("sid:%s", startResourceRes.getSid());

System.out.println("start resource success");

Thread.sleep(3000);
```

### 停止录制 [#停止录制]

完成录制后，调用 `stop` 结束录制。

```java
StopResourceRes stopResourceRes;

// Stop resource
try {
    stopResourceRes = cloudRecordingClient
            .mixScenario()
            .stop(cname, uid, startResourceRes.getResourceId(), startResourceRes.getSid(), true)
            .block();
} catch (AgoraException e) {
    System.out.printf("agora error:%s", e.getMessage());
    return;
} catch (Exception e) {
    System.out.printf("unknown error:%s", e.getMessage());
    return;
}

// Check if the response is null
if (stopResourceRes == null || stopResourceRes.getSid() == null) {
    System.out.println("failed to stop resource");
} else {
    System.out.println("stop resource success");
}
```

### 完整示例代码 [#完整示例代码]

`Main.java` 文件中完整代码如下，你可自行比对：

<Accordions>
<Accordion title="完整示例代码">

```java
public class Main {
    private static String appId = "<your appId>";

    private static String cname = "<your cname>";
    // 录制服务加入频道的用户 ID
    private static String uid = "<your uid>";
    // 客户 ID
    private static String username = "<the username of basic auth credential>";
    // 客户密钥
    private static String password = "<the password of basic auth credential>";

    private static String token = "<your token>";

    private static String accessKey = "<your accessKey>";

    private static String secretKey = "<your secretKey>";

    private static Integer region = 1; // <your region>

    private static String bucket = "<your bucket>";

    private static Integer vendor = 2; // <your vendor>

    public static void main(String[] args) throws Exception {

        Credential credential = new BasicAuthCredential(username, password);

        // Initialize CloudRecordingConfig
        CloudRecordingConfig config = CloudRecordingConfig.builder()
                .appId(appId)
                .credential(credential)
                // Specify the region where the server is located.
                // Optional values are CN, US, EU, AP, and the client will automatically
                // switch to use the best domain name according to the configured region
                .domainArea(DomainArea.CN)
                .build();

        // Initialize CloudRecordingClient
        CloudRecordingClient cloudRecordingClient = CloudRecordingClient.create(config);

        AcquireResourceRes acquireResourceRes;

        // Acquire resource
        try {
            acquireResourceRes = cloudRecordingClient
                    .mixScenario()
                    .acquire(cname, uid, AcquireMixRecordingResourceClientReq.builder().build())
                    .block();
        } catch (AgoraException e) {
            System.out.printf("agora error:%s", e.getMessage());
            return;
        } catch (Exception e) {
            System.out.printf("unknown error:%s", e.getMessage());
            return;
        }

        // Check if the response is null
        if (acquireResourceRes == null || acquireResourceRes.getResourceId() == null) {
            System.out.println("failed to get resource");
            return;
        }

        System.out.printf("resourceId:%s", acquireResourceRes.getResourceId());

        System.out.println("acquire resource success");

        // Define storage config
        StartResourceReq.StorageConfig storageConfig = StartResourceReq.StorageConfig.builder()
                .accessKey(accessKey)
                .secretKey(secretKey)
                .bucket(bucket)
                .vendor(vendor)
                .region(region)
                .build();

        // Define start resource request
        StartMixRecordingResourceClientReq startResourceReq = StartMixRecordingResourceClientReq.builder()
                .token(token)
                .recordingConfig(StartResourceReq.RecordingConfig.builder()
                        .channelType(1)
                        .build())
                .recordingFileConfig(StartResourceReq.RecordingFileConfig.builder()
                        .avFileType(Arrays.asList("hls", "mp4"))
                        .build())
                .storageConfig(storageConfig)
                .build();


        StartResourceRes startResourceRes;

        // Start resource
        try {
            startResourceRes = cloudRecordingClient
                    .mixScenario()
                    .start(cname, uid, acquireResourceRes.getResourceId(), startResourceReq)
                    .block();

        } catch (AgoraException e) {
            System.out.printf("agora error:%s", e.getMessage());
            return;
        } catch (Exception e) {
            System.out.printf("unknown error:%s", e.getMessage());
            return;
        }

        // Check if the response is null
        if (startResourceRes == null || startResourceRes.getSid() == null) {
            System.out.println("failed to start resource");
            return;
        }

        System.out.printf("sid:%s", startResourceRes.getSid());

        System.out.println("start resource success");

        Thread.sleep(3000);

        QueryMixHLSAndMP4RecordingResourceRes queryResourceRes;

        // Query resource
        try {
            queryResourceRes = cloudRecordingClient
                    .mixScenario()
                    .queryHLSAndMP4(startResourceRes.getResourceId(), startResourceRes.getSid())
                    .block();

        } catch (AgoraException e) {
            System.out.printf("agora error:%s", e.getMessage());
            return;
        } catch (Exception e) {
            System.out.printf("unknown error:%s", e.getMessage());
            return;
        }

        if (queryResourceRes == null || queryResourceRes.getServerResponse() == null) {
            System.out.println("failed to query resource");
            return;
        }

        System.out.println("query resource success");

        Thread.sleep(3000);

        StopResourceRes stopResourceRes;

        // Stop resource
        try {
            stopResourceRes = cloudRecordingClient
                    .mixScenario()
                    .stop(cname, uid, startResourceRes.getResourceId(), startResourceRes.getSid(), true)
                    .block();
        } catch (AgoraException e) {
            System.out.printf("agora error:%s", e.getMessage());
            return;
        } catch (Exception e) {
            System.out.printf("unknown error:%s", e.getMessage());
            return;
        }

        // Check if the response is null
        if (stopResourceRes == null || stopResourceRes.getSid() == null) {
            System.out.println("failed to stop resource");
        } else {
            System.out.println("stop resource success");
        }
    }
}
```

</Accordion>
</Accordions>

## 查看录制文件 [#查看录制文件]

参考第三方云存储官方文档操作，查看云存储中的文件。根据 `start` 方法中 `storageconfig.fileNamePrefix` 参数的设置，录制生成文件位于 `quickstart` 目录下。如下截图以阿里云为例：

![云存储中 quickstart 录制文件目录示意图](https://web-cdn.agora.io/docs-portal/img/cloud-recording/quick-oss-2.png)

录制过程中生成了 `.m3u8`、`.ts` 和 `mp4` 文件。最终 `quickstart` 文件夹内包含如下：

```shell
.
├── <sid>_testchannel.m3u8
├── <sid>_testchannel_0.mp4
└── <sid>_testchannel_20241119070642248.ts
```

根据你在 `start` 方法中设置的录制时间，云存储中可能会有多个 .ts 文件。录制文件介绍请参考[录制文件命名和切片规则](/zh-CN/realtime-media/cloud-recording/build/manage-recorded-files/introduce-recorded-files#合流和页面录制模式)。

## 进阶设置 [#进阶设置]

本文提供的示例代码仅为快速上手时使用。在实际业务中，你需要根据实际业务场景修改请求参数。如下示例代码展示了录制直播频道中所有的音视频流，且输出的视频流需要转码的参数配置：

```java
// 配置录制选项
StartMixRecordingResourceClientReq startResourceReq = StartMixRecordingResourceClientReq.builder()
        .token(token)
        .recordingConfig(StartResourceReq.RecordingConfig.builder()
                .channelType(1)
                .streamTypes(2)
                .maxIdleTime(30)
                .audioProfile(2)
                .transcodingConfig(StartResourceReq.TranscodingConfig.builder()
                        .width(640)
                        .height(480)
                        .fps(15)
                        .bitrate(800)
                        .mixedVideoLayout(0)
                        .backgroundColor("#000000")
                        .build())
                .subscribeAudioUIDs(Collections.singletonList("#allstream#"))
                .subscribeVideoUIDs(Collections.singletonList("#allstream#"))
                .build())
        .recordingFileConfig(StartResourceReq.RecordingFileConfig.builder()
                .avFileType(Arrays.asList("hls", "mp4"))
                .build())
        .storageConfig(storageConfig)
        .build();
```

云端录制支持设置的所有参数请参考 [API 文档](/zh-CN/api-reference/api-ref/cloud-recording/start)。

## 参考信息 [#参考信息]

### 示例项目 [#示例项目]

完成快速开始后，你可以体验更多云端录制示例：

<Cards>
  <Card title="Gitee 示例项目" href="https://gitee.com/agoraio-community/agora-rest-client-java/tree/main/examples/cloudrecording" />
  <Card title="GitHub 示例项目" href="https://github.com/AgoraIO-Community/agora-rest-client-java/tree/main/examples/cloudrecording" />
</Cards>

### 排查错误 [#排查错误]

- [响应状态码和错误码](/zh-CN/realtime-media/cloud-recording/reference/response-code)
- [为什么成功开启云端录制后调用 query 方法返回 404？](/zh-CN/reference/faq/integration/return_404)
- [如何选择云存储 bucket 区域及处理跨区上传问题？](/zh-CN/reference/faq/integration/bucket_region)
- [为什么第三方云存储中没有录制文件？](/zh-CN/reference/faq/integration/fail_to_upload)


## Node.js

本文向新手介绍如何使用声网 Node.js SDK 开始第一次云端录制。

Node.js SDK 旨在帮助开发者更轻松集成声网 RESTful API。该 SDK 具有如下特性：

- 简化通信流程：通过封装 RESTful API 的请求和响应处理，让你与 RESTful API 的通信更简单。
- 保障可用性：遇到 DNS 解析失败、网络错误、请求超时等网络问题时，会自动切换到最佳域名，确保 REST 服务的可用性。
- API 易用性：提供简洁易懂的 API，让你能轻松实现开启云端录制、停止云端录制等常用功能。
- 其他优势：基于 Node.js 语言编写，具有高效性、并发性、可扩展性。

## 前提条件 [#前提条件]

开始前请确保你的本地开发环境满足如下条件：

- [Node.js](https://nodejs.org/zh-cn) 18 或以上版本。
- 参考[开通云端录制服务](/zh-CN/realtime-media/cloud-recording/build/setup-and-access/enable-service)在声网控制台开通云端录制，并复制保存 App ID、临时 Token、客户 ID 和客户密钥。

  :::warning[注意]
  临时 Token 的有效期是 24 小时。Token 过期会导致加入频道失败。
  :::

- 云端录制文件需要上传到第三方云存储。开始录制前，请开通第三方云存储服务，并获取 Bucket、Access Key、Secret Key 以备用。

<Accordions type="multiple">
<Accordion title="开通方式">

开通方式请参考云存储官网文档，本文后续步骤以阿里云为例：

- [Amazon S3](https://aws.amazon.com/cn/s3/?c=s&sec=srv)
- [阿里云](https://www.aliyun.com/product/oss)
- [腾讯云](https://cloud.tencent.com/product/cos)
- [Microsoft Azure](https://azure.microsoft.com/zh-cn/services/storage/blobs/)
- [谷歌云](https://cloud.google.com/storage)
- [华为云](https://www.huaweicloud.com/product/obs.html)
- [百度智能云](https://cloud.baidu.com/product/bos.html)

</Accordion>
</Accordions>


- 在项目中实现了基础的音视频互动场景。你也可以参考[文档](/zh-CN/realtime-media/cloud-recording/get-started/quick-start/restful-api#模拟音视频互动场景)使用声网实时互动 [Web Demo](https://webdemo.agora.io/index.html) 在浏览器中模拟一个音视频互动场景。

## 创建项目并安装 SDK [#创建项目并安装-sdk]

#### 创建项目 [#创建项目]

参考如下步骤创建并配置新项目。

1. 创建一个名为 `test-recording` 的空文件夹。

2. 进入 `test-recording` 文件路径，运行如下命令初始化项目：

   ```shell
   npm init -y
   ```

   完成初始化后，项目路径下会自动创建一个 `package.json` 文件。

3. 在 `test-recording/` 路径下，创建一个 `src/index.ts` 文件，我们会在这个文件中实现一个极简的云端录制任务。

#### 添加 TypeScript 依赖 [#添加-typescript-依赖]

由于使用 `.ts` 文件，我们需要在项目文件夹中添加对 TypeScript 的依赖。

1. 运行如下命令行安装 TypeScript：

   ```shell
   npm install typescript @types/node
   ```

   完成安装后项目路径下会自动创建一个 `package-lock.json` 文件。

2. 配置 TypeScript 的运行环境。

   1. 运行如下命令行创建 TypeScript 配置文件：

      ```shell
      npx tsc --init
      ```

      完成后项目路径下会自动创建一个 `tsconfig.json` 文件。将 `tsconfig.json` 中的代码替换为如下行：

      ```json
      {
        "compileOnSave": true,
        "compilerOptions": {
          "allowSyntheticDefaultImports": true,
          "experimentalDecorators": true,
          "module": "CommonJS",
          "moduleResolution": "node",
          "noImplicitAny": false,
          "noUnusedLocals": false,
          "noUnusedParameters": false,
          "removeComments": false,
          "resolveJsonModule": true,
          "skipLibCheck": true,
          "strictNullChecks": true,
          "target": "ES2015",
          "baseUrl": ".",
          "outDir": "dist",
          "rootDir": "./src",
          "sourceMap": true,
          "declaration": true
        },
        "include": ["./src"],
        "exclude": ["__tests__"]
      }
      ```

   2. 运行如下命令行创建一个 TypeScript 建构文件：

      ```shell
      touch tsconfig.build.json
      ```

      将 `tsconfig.build.json` 中的代码替换为如下行：

      ```json
      {
        "extends": "./tsconfig",
        "compilerOptions": {
          "sourceMap": false
        }
      }
      ```

3. 在根目录的 `package.json` 文件的 `scripts` 区域，添加如下行：

    ```json
    "build": "tsc --project tsconfig.build.json &&node dist/index.js"
    ```

#### 添加 REST Client 依赖库 [#添加-rest-client-依赖库]

运行如下命令行添加依赖：

```shell
npm install agora-rest-client
```

完成添加后，项目文件下会添加一个 `node_modules` 文件包。

## 开始云端录制 [#开始云端录制]

本节介绍进行云端录制的极简流程。为方便说明，本节以录制之前模拟的音视频直播为例，并假设将主播的音频和视频录制到 MP4 文件中。

### 引入依赖 [#引入依赖]

在 `src/index.ts` 中添加如下行，引入 `agora-rest-client` 及相关代码模块：

```typescript
import {
  DomainArea,
  BasicCredential,
  CloudRecordingClient,
  AcquireResourceRes,
  StartResourceRes,
  RecordingRequestChannelTypeEnum,
  QueryMixHLSAndMP4RecordingResourceRes,
  StopResourceRes,
} from "agora-rest-client";
```

### 定义变量 [#定义变量]

继续添加如下代码，定义和配置关键参数。取值参考[前提条件](#前提条件)。

```typescript
// 定义关键参数
const appId = "<your_app_id>";
const cname = "show";
// 录制服务加入频道的用户 ID
const uid = "123";
// 客户 ID
const username = "<your_customer_id>";
// 客户密钥
const password = "<your_customer_secret>";
const token = "<your_token>";

// 配置云存储
const storageConfig = {
  vendor: parseInt("2", 10),
  region: parseInt("1", 10),
  bucket: "<your_storage_bucket_name>",
  accessKey: "<your_storage_access_key>",
  secretKey: "<your_storage_secret_key>",
  fileNamePrefix: ["quickstart"],
};
```

其中，

- `cname` 值设为 `show`，表示加入录制实时音视频互动所在的频道。
- `vendor` 值设为 `2` 代表使用阿里云；`region` 值设为 `1` 表示 `CN_Shanghai`。如果你使用其他云存储厂商，参考[第三方云存储地区说明](/zh-CN/realtime-media/cloud-recording/reference/api-reference#第三方云存储地区说明)赋值。

### 创建并初始化 Client [#创建并初始化-client]

在 `src/index.ts` 中，添加如下代码，创建并初始化 Client。

```typescript
const credential = new BasicCredential(username, password);

const cloudRecordingClient = new CloudRecordingClient({
  appId,
  credential,
  // Specify the region where the server is located.
  // Optional values are CN, US, EU, AP, and the client will automatically
  // switch to use the best domain name according to the configured region
  domainArea: DomainArea.CN,
});
```

### 申请录制服务器资源 [#申请录制服务器资源]

调用 `acquire` 申请录制服务器资源。

```typescript
// Acquire resource
let acquireResourceRes: AcquireResourceRes;
try {
  acquireResourceRes = await cloudRecordingClient
    .mixScenario()
    .acquire(cname, uid, {
      resourceExpiredHour: 1,
    });
} catch (error) {
  console.error("Failed to acquire resource", error);
  return;
}

if (!acquireResourceRes.resourceId) {
  console.error("Resource ID is missing");
  return;
}

console.info(
  `acquire resource success,res:${JSON.stringify(acquireResourceRes)}`
);
```

### 开始录制 [#开始录制]

获取资源后，调用 `start` 开始录制：

```typescript
// Start resource
let startResourceRes: StartResourceRes;
try {
  startResourceRes = await cloudRecordingClient
    .mixScenario()
    .start(cname, uid, acquireResourceRes.resourceId, {
      token,
      recordingConfig: {
        channelType: RecordingRequestChannelTypeEnum.Live,
      },
      recordingFileConfig: {
        avFileType: ["hls", "mp4"],
      },
      storageConfig,
    });
} catch (error) {
  console.error("Failed to start resource", error);
  return;
}

if (!startResourceRes.sid) {
  console.error("SID is missing");
  return;
}

console.info(
  `start resource success,res:${JSON.stringify(startResourceRes)}`
);
```

### 停止录制 [#停止录制]

完成录制后，调用 `stop` 结束录制。

```typescript
// Stop resource
let stopResourceRes: StopResourceRes;
try {
  stopResourceRes = await cloudRecordingClient
    .mixScenario()
    .stop(
      cname,
      uid,
      acquireResourceRes.resourceId,
      startResourceRes.sid,
      true
    );
} catch (error) {
  console.error("Failed to stop resource", error);
  return;
}

console.info(`stop resource success,res:${JSON.stringify(stopResourceRes)}`);
```

### 完整示例代码 [#完整示例代码]

`src/index.ts` 文件中完整代码如下，你可以自行比对：

<Accordions>
<Accordion title="完整示例代码">

```typescript
import {
  DomainArea,
  BasicCredential,
  CloudRecordingClient,
  AcquireResourceRes,
  StartResourceRes,
  RecordingRequestChannelTypeEnum,
  QueryMixHLSAndMP4RecordingResourceRes,
  StopResourceRes,
} from "agora-rest-client";

const appId = "<your_app_id>";
const cname = "show";
// 录制服务加入频道的用户 ID
const uid = "123";
// 客户 ID
const username = "<your_customer_id>";
// 客户密钥
const password = "<yout_customer_secret>";
const token = "<your_token>";

if (!appId || !cname || !uid || !username || !password) {
  console.error("Required variables are missing");
  process.exit(1);
}

const credential = new BasicCredential(username, password);

const storageConfig = {
  vendor: parseInt("2", 10),
  region: parseInt("1", 10),
  bucket: "<your_storage_bucket_name>",
  accessKey: "<your_storage_access_key>",
  secretKey: "<your_storage_secret_key>",
  fileNamePrefix: ["quickstart"],
};

if (
  !storageConfig.vendor ||
  !storageConfig.region ||
  !storageConfig.bucket ||
  !storageConfig.accessKey ||
  !storageConfig.secretKey
) {
  console.error("Required storage configuration variables are missing");
  process.exit(1);
}

(async (): Promise<void> => {
  const cloudRecordingClient = new CloudRecordingClient({
    appId,
    credential,
    // Specify the region where the server is located.
    // Optional values are CN, US, EU, AP, and the client will automatically
    // switch to use the best domain name according to the configured region
    domainArea: DomainArea.CN,
  });

  // Acquire resource
  let acquireResourceRes: AcquireResourceRes;
  try {
    acquireResourceRes = await cloudRecordingClient
      .mixScenario()
      .acquire(cname, uid, {
        resourceExpiredHour: 1,
      });
  } catch (error) {
    console.error("Failed to acquire resource", error);
    return;
  }

  if (!acquireResourceRes.resourceId) {
    console.error("Resource ID is missing");
    return;
  }

  console.info(
    `acquire resource success,res:${JSON.stringify(acquireResourceRes)}`
  );

  // Start resource
  let startResourceRes: StartResourceRes;
  try {
    startResourceRes = await cloudRecordingClient
      .mixScenario()
      .start(cname, uid, acquireResourceRes.resourceId, {
        token,
        recordingConfig: {
          channelType: RecordingRequestChannelTypeEnum.Live,
        },
        recordingFileConfig: {
          avFileType: ["hls", "mp4"],
        },
        storageConfig,
      });
  } catch (error) {
    console.error("Failed to start resource", error);
    return;
  }

  if (!startResourceRes.sid) {
    console.error("SID is missing");
    return;
  }

  console.info(
    `start resource success,res:${JSON.stringify(startResourceRes)}`
  );

  await new Promise((resolve) => setTimeout(resolve, 10000));

  // Query resource
  let queryResourceRes: QueryMixHLSAndMP4RecordingResourceRes;

  try {
    queryResourceRes = await cloudRecordingClient
      .mixScenario()
      .queryHLSAndMP4(acquireResourceRes.resourceId, startResourceRes.sid);
  } catch (error) {
    console.error("Failed to query resource", error);
    return;
  }

  if (!queryResourceRes.serverResponse) {
    console.error("Query resource serverResponse is missing");
    return;
  }
  console.info(
    `query resource success,res:${JSON.stringify(queryResourceRes)}`
  );

  await new Promise((resolve) => setTimeout(resolve, 3000));

  // Stop resource
  let stopResourceRes: StopResourceRes;
  try {
    stopResourceRes = await cloudRecordingClient
      .mixScenario()
      .stop(
        cname,
        uid,
        acquireResourceRes.resourceId,
        startResourceRes.sid,
        true
      );
  } catch (error) {
    console.error("Failed to stop resource", error);
    return;
  }

  console.info(`stop resource success,res:${JSON.stringify(stopResourceRes)}`);
})();
```

</Accordion>
</Accordions>

## 查看录制文件 [#查看录制文件]

参考第三方云存储官方文档操作，查看云存储中的文件。根据 `storageConfig.fileNamePrefix` 参数的设置，录制生成文件位于 `quickstart` 目录下。如下截图以阿里云为例：

![云存储中 quickstart 录制文件目录示意图](https://web-cdn.agora.io/docs-portal/img/cloud-recording/quick-oss-2.png)

录制过程中生成了 `.m3u8`、`.ts` 和 `mp4` 文件。最终 `quickstart` 文件夹内包含如下：

```shell
.
├── <sid>_testchannel.m3u8
├── <sid>_testchannel_0.mp4
└── <sid>_testchannel_20241119070642248.ts
```

根据你在 `start` 方法中设置的录制时间，云存储中可能会有多个 .ts 文件。录制文件介绍请参考[录制文件命名和切片规则](/zh-CN/realtime-media/cloud-recording/build/manage-recorded-files/introduce-recorded-files#合流和页面录制模式)。

## 进阶设置 [#进阶设置]

本文提供的示例代码仅为快速上手时使用。在实际业务中，你需要根据实际业务场景修改请求参数。如下示例代码展示了录制直播频道中所有的音视频流，且输出的视频流需要转码的参数配置：

```typescript
startResourceRes = await cloudRecordingClient
  .mixeScenario()
  .start(cname, uid, acquireResourceRes.resourceId, {
    token,
    recordingConfig: {
      channelType: RecordingRequestChannelTypeEnum.Live,
      streamTypes: 2,
      maxIdleTime: 30,
      audioProfile: 2,
      transcodingConfig: {
        width: 640,
        height: 480,
        fps: 15,
        bitrate: 800,
        mixedVideoLayout: 0,
        backgroundColor: "#000000",
      },
      subscribeAudioUids: ["#allstream#"],
      subscribeVideoUids: ["#allstream#"],
    },

    recordingFileConfig: {
      avFileType: ["hls", "mp4"],
    },
    storageConfig,
  });
```

## 参考信息 [#参考信息]

### 示例项目 [#示例项目]

完成快速开始后，你可以体验更多云端录制示例：

<Cards>
  <Card title="Gitee 示例项目" href="https://gitee.com/agoraio-community/agora-rest-client-nodejs/tree/main/examples/cloudrecording" />
  <Card title="GitHub 示例项目" href="https://github.com/AgoraIO-Community/agora-rest-client-nodejs/tree/main/examples/cloudrecording" />
</Cards>

### 排查错误 [#排查错误]

- [响应状态码和错误码](/zh-CN/realtime-media/cloud-recording/reference/response-code)
- [为什么成功开启云端录制后调用 query 方法返回 404？](/zh-CN/reference/faq/integration/return_404)
- [如何选择云存储 bucket 区域及处理跨区上传问题？](/zh-CN/reference/faq/integration/bucket_region)
- [为什么第三方云存储中没有录制文件？](/zh-CN/reference/faq/integration/fail_to_upload)
