# 发布和订阅 (/zh-CN/realtime-media/rtc/build/initialize-and-channel/publish-subscribe)

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

## 发布音视频 [#发布音视频]

本文详细介绍如何使用声网 Web SDK 发布和取消发布本地音视频，以及订阅和取消订阅远端音视频。

完成本地轨道的创建并且成功加入频道后，就可以调用 [`AgoraRTCClient.publish`](/zh-CN/api-reference/rtc/web/interfaces/iagorartcclient#publish) 将本地的音视频数据发布到当前频道，以供频道中的其他用户订阅。

```javascript
const localAudioTrack = ...;
const localVideoTrack = ...;

// 你可以多次调用 publish 发布多个轨道
await client.publish(localAudioTrack);
await client.publish(localVideoTrack);

// 也可以一次性将需要发布的轨道一起发布
await client.publish([localAudioTrack, localVideoTrack]);
```

关于发布，注意事项如下：

- 同一时间只能发布一个视频轨道。
- 可以同时发布多个音频轨道，SDK 会自动混音（Safari 12 之前的版本不支持混音，无法使用此特性）。
- 可以多次调用该方法来发布不同的轨道，但是不能重复发布同一个轨道对象。
- 该方法为异步方法，使用时需要配合 `Promise` 或 `async/await`。

在发布过程中，可能因为网络环境或者 SDK 使用不当抛出以下错误：

- `INVALID_OPERATION`: 非法操作，说明在加入频道成功之前就调用了 `publish` 方法。
- `OPERATION_ABORTED`: 发布被中止，可能是因为在发布成功之前就主动调用 `leave` 离开了频道。
- `INVALID_LOCAL_TRACK`: 参数错误，传入了非法的 `LocalTrack` 对象。
- `CAN_NOT_PUBLISH_MULTIPLE_VIDEO_TRACKS`: 不允许同时发布多个视频轨道。
- `NOT_SUPPORTED`: 发布了多个音频轨道，但是浏览器不支持混音。
- `UNEXPECTED_RESPONSE`: 收到了声网服务器异常的返回，发布轨道失败。建议保留日志，联系声网[技术支持](https://ticket.shengwang.cn/)。
- `NO_ICE_CANDIDATE`: 找不到本地网络出口，可能是网络防火墙或者启用了禁止 WebRTC 的浏览器插件。详见[如何处理常见的 Web 浏览器控制台报错](/zh-CN/reference/faq/integration/console_error_web)。

## 取消发布音视频 [#取消发布音视频]

成功发布本地轨道后，如果想取消发布，可以调用 [`AgoraRTCClient.unpublish`](/zh-CN/api-reference/rtc/web/interfaces/iagorartcclient#unpublish)。

```javascript
// 发布音视频
await client.publish([localAudioTrack, localVideoTrack]);

// 取消发布视频，此时音频还在正常发布
await client.unpublish(localVideoTrack);

// 也可以一次将所有正在发布的轨道全部取消发布
await client.unpublish();
// 或者批量取消发布
await client.unpublish([localAudioTrack, localVideoTrack]);
```

关于取消发布，注意事项如下：

- 该方法可以多次调用。你可以使用 `publish` 和 `unpublish` 实现发布和取消发布某个本地轨道。
- 该方法为异步方法，使用时需要配合 `Promise` 或 `async/await`。

## 订阅音视频 [#订阅音视频]

当远端用户成功发布音视频轨道之后，SDK 会触发 [`user-published`](/zh-CN/api-reference/rtc/web/interfaces/iagorartcclient#event_user_published) 事件。这个事件携带两个参数：远端用户对象 (`user`) 和远端发布的媒体类型 (`mediaType`)。此时，你可以调用 [`AgoraRTCClient.subscribe`](/zh-CN/api-reference/rtc/web/interfaces/iagorartcclient#subscribe) 发起订阅。

该方法为异步方法，使用时需要配合 `Promise` 或者 `async/await`。

```javascript
client.on("user-published", async (user, mediaType) => {
  // 发起订阅
  await client.subscribe(user, mediaType);

  // 如果订阅的是音频轨道
  if (mediaType === "audio") {
    const audioTrack = user.audioTrack;
    // 自动播放音频
    audioTrack.play();
  } else {
    const videoTrack = user.videoTrack;
    // 自动播放视频
    videoTrack.play(DOM_ELEMENT);
  }
});
```

当订阅方法调用完成之后，你可以通过 `user.audioTrack` 和 `user.videoTrack` 获取相应的 [`RemoteAudioTrack`](/zh-CN/api-reference/rtc/web/interfaces/iremoteaudiotrack) 和 [`RemoteVideoTrack`](/zh-CN/api-reference/rtc/web/interfaces/iremotevideotrack) 对象。

订阅和发布不同，每次订阅只能订阅一个音频或视频轨道。即使发布端同时发布了音频轨道和视频轨道，SDK 也会触发两次 `user-published` 事件：一次 `user-published(audio)`，一次 `user-published(video)`。按照上面的代码逻辑，会完成两次订阅。

在订阅过程中，因为网络环境等因素 SDK 可能抛出如下错误：

- `INVALID_OPERATION`: 非法操作，可能在加入频道成功之前就调用了 `subscribe`。
- `INVALID_REMOTE_USER`: 传入了非法的远端用户对象，例如该用户不在频道内。
- `REMOTE_USER_IS_NOT_PUBLISHED`: 传入的远端用户没有发布 `subscribe` 方法中传入的媒体类型。
- `UNEXPECTED_RESPONSE`: 收到了声网服务器异常的返回，订阅失败。建议保留日志，联系声网[技术支持](https://ticket.shengwang.cn/)。
- `OPERATION_ABORTED`: 操作中止，可能在订阅成功之前就调用 `leave` 离开了频道。
- `NO_ICE_CANDIDATE`: 找不到本地网络出口，可能是网络防火墙或者启用了禁止 WebRTC 的浏览器插件。详见[如何处理常见的 Web 浏览器控制台报错](/zh-CN/reference/faq/integration/console_error_web#none-ice-candidate-not-alloweda-namecandidatea)。

## 取消订阅音视频 [#取消订阅音视频]

你可以通过 [`AgoraRTCClient.unsubscribe`](/zh-CN/api-reference/rtc/web/interfaces/iagorartcclient#unsubscribe) 取消订阅远端的音视频。

```javascript
// 订阅目标用户的音视频
await client.subscribe(user, "audio");
await client.subscribe(user, "video");

// 取消订阅视频
await client.unsubscribe(user, "video");
// 也可以取消订阅当前用户的所有媒体类型
await client.unsubscribe(user);
```

关于取消订阅，注意事项如下：

- 取消订阅成功后，SDK 会释放相应的 `RemoteTrack` 对象。一旦远端轨道对象被释放，SDK 会自动移除视频的播放元素，音频播放也会停止。
- 如果远端用户主动取消发布，本地会收到 `user-unpublished` 回调，收到该回调时 SDK 会自动释放相应的 `RemoteTrack` 对象，你无需再调用 `unsubscribe`。
- 该方法为异步方法，使用时需要配合 `Promise` 或 `async/await`。
