# 5\_2-手机开播场景-连麦推流

* [1 功能概述](#_1-功能概述)
* [2 实现介绍](#_2-实现介绍)
  * [2.1 PLVStreamerPresenter](#_21-plvstreamerpresenter)
    * [2.1.1 初始化方法](#_211-初始化方法)
    * [2.1.2 设置默认配置](#_212-设置默认配置)
    * [2.1.3 推流画面的预览](#_213-推流画面的预览)
    * [2.1.4 开始上课方法](#_214-开始上课方法)
    * [2.1.5 结束上课方法](#_215-结束上课方法)
    * [2.1.6 允许用户上麦](#_216-允许用户上麦)
    * [2.1.7 挂断用户连麦](#_217-挂断用户连麦)
  * [2.2 PLVLSLinkMicAreaView](#_22-plvlslinkmicareaview)

#### 1 功能概述

推流和连麦模块是手机开播场景中的基础功能，支持摄像头开关、摄像头前置后置切换、麦克风开关功能，推流同时支持清晰度设置和摄像头镜像的功能。推流可以用于讲师上课，连麦支持音频连麦、视频连麦可自由选择，并支持多人连麦，丰富课堂内容。

推流流程：初始化推流引擎->设置推流配置参数->开始推流。

连麦流程：讲师端发起连麦->学员举手申请连麦->讲师端允许学员连麦->学员正式加入连麦。

#### 2 实现介绍

**2.1 PLVStreamerPresenter**

该类是手机开播场景下，针对推流和连麦进行封装的推流管理器，支持旁路CDN推流、支持课程管理、支持RTC连麦、连麦用户管理。

**2.1.1 初始化方法**

在demo项目`PLVLSStreamerViewController`中需对管理器实现初始化处理：

```objective-c
self.streamerPresenter = [[PLVStreamerPresenter alloc] init];
self.streamerPresenter.delegate = self;
self.streamerPresenter.previewType = PLVStreamerPresenterPreviewType_UserArray;
```

同时 `PLVLSStreamerViewController` 需遵循协议 `PLVStreamerPresenterDelegate`，并实现其中代理方法。更多内容可以参考demo项目中`PLVLSStreamerViewController` 的演示。

**2.1.2 设置默认配置**

根据具体的业务场景可以进行麦克风、摄像头、流属性、混流布局模式以及其他属性的默认配置：

```objective-c
// 设置 麦克风、摄像头默认配置 
// 仅在[prepareLocalPreviewCompletion:]方法调用前设置有效
self.streamerPresenter.micDefaultOpen = YES;// YES:麦克风默认开启 NO:麦克风默认关闭；默认值 NO
self.streamerPresenter.cameraDefaultOpen = YES;// YES:摄像头默认开启 NO:摄像头默认关闭；默认值 NO
self.streamerPresenter.cameraDefaultFront = YES;// YES:摄像头默认前置 NO:摄像头默认后置；默认值 后置

// 设置 流属性配置
[self.streamerPresenter setupStreamScale:PLVBLinkMicStreamScale16_9];
[self.streamerPresenter setupStreamQuality:PLVBLinkMicStreamQuality720P];

// 设置 混流配置
[self.streamerPresenter setupMixLayoutType:PLVRTCStreamerMixLayoutType_MainSpeaker];
```

**2.1.3 推流画面的预览**

以demo项目中 `PLVLSStreamerViewController`演示为例，socket 登录成功回调后加入RTC频道：

```objective-c
#pragma mark - PLVSocketManager Protocol

- (void)socketMananger_didLoginSuccess:(NSString *)ackString { // 登录成功
    // 登录成功
    [self.streamerPresenter joinRTCChannel];
}
```

此时房间状态发生改变，在下方示例的代理方法中设置本地画面预览的载体视图：

```objective-c
#pragma mark - PLVStreamerPresenterDelegate

/// ‘房间加入状态’ 发生改变
- (void)plvStreamerPresenter:(PLVStreamerPresenter *)presenter currentRtcRoomJoinStatus:(PLVStreamerPresenterRoomJoinStatus)currentRtcRoomJoinStatus inRTCRoomChanged:(BOOL)inRTCRoomChanged inRTCRoom:(BOOL)inRTCRoom{
    if (inRTCRoomChanged) {
        if (inRTCRoom) {
            [self preapareStartClass];
        }
    }
}

- (void)preapareStartClass {
    __weak typeof(self) weakSelf = self;
    [self.streamerPresenter prepareLocalPreviewCompletion:^(BOOL granted, BOOL prepareSuccess) {
        if (prepareSuccess) {
            [weakSelf.streamerPresenter setupLocalPreviewWithCanvaView:nil];
        }
        if (!granted) {
            NSString *msg = [NSString stringWithFormat:@"需要获取您的音视频权限，请前往设置"];
            [PLVAuthorizationManager showAlertWithTitle:@"提示" message:msg viewController:weakSelf];
        }
    }];
}
```

**2.1.4 开始上课方法**

`PLVStreamerPresenter`的`startClass`方法是用于开始上课的方法，内部调用`startPushStream`方法。`startPushStream`方法仅负责推流，而`startClass`方法将开始推流，且发送请求修改频道状态为“开始上课”。建议业务场景使用`startClass`方法。

在demo项目中`PLVLSStreamerViewController` 演示了倒计时结束时开始上课：

```objective-c
 __weak typeof(self) weakSelf = self;
 _coutBackView.countDownCompletedBlock = ^{
	[weakSelf.streamerPresenter startClass];
};
```

**2.1.5 结束上课方法**

`PLVStreamerPresenter`的`finishClass`方法是用于结束上课的方法，内部调用`stopPushStream`方法。`startPushStream`方法仅停止推流，而`finishClass`方法停止推流，且发送请求修改频道状态为“结束上课”。建议业务场景使用`finishClass`方法：

```objective-c
[self.streamerPresenter finishClass];
```

**2.1.6 允许用户上麦**

`PLVStreamerPresenter`的`allowRemoteUserJoinLinkMic`方法是用于允许用户上麦的方法：

```objective-c
/// 允许 某位远端用户 上麦
///
/// @param waitUser 远端用户等待连麦模型
/// @param emitCompleteBlock ‘允许 某位远端用户 上麦’的请求发送结果Block
- (void)allowRemoteUserJoinLinkMic:(PLVLinkMicWaitUser *)waitUser emitCompleteBlock:(nullable void (^)(BOOL emitSuccess))emitCompleteBlock;
```

在`PLVStreamerPresenter`的`addLinkMicWaitUserWithDict`内部方法中实现对用户上麦。

当RTC房间在线用户数组‘发生改变时，会调用`PLVLSLinkMicAreaView` 的`reloadLinkMicUserWindows`方法刷新连麦用户的画面：

```objective-c
/// ’RTC房间在线用户数组‘ 发生改变
- (void)plvStreamerPresenter:(PLVStreamerPresenter *)presenter linkMicOnlineUserListRefresh:(NSArray <PLVLinkMicOnlineUser *>*)onlineUserArray{
    [self.linkMicAreaView reloadLinkMicUserWindows];
}
```

**2.1.7 挂断用户连麦**

`PLVStreamerPresenter`的`closeAllLinkMicUser`方法是用于挂断全部连麦用户的方法：

```objective-c
[self.streamerPresenter closeAllLinkMicUser];
```

`PLVStreamerPresenter`的`closeRemoteUserLinkMic`方法是用于挂断某位远端用户的连麦的方法，在`PLVStreamerPresenter`内部使用，`closeAllLinkMicUser`方法应用`closeRemoteUserLinkMic`方法实现挂断全部连麦用户的方法。

更多实现可以参考demo项目中 `PLVLSStreamerViewController`。

**2.2 PLVLSLinkMicAreaView**

`PLVLSLinkMicAreaView`用于承载`PLVStreamerPresenter`推流管理器提供的数据。

在demo项目中`PLVLSStreamerViewController` 演示了如何添加 `PLVLSLinkMicAreaView` 控件：

```objective-c
self.linkMicAreaView = [[PLVLSLinkMicAreaView alloc] init];
self.linkMicAreaView.delegate = self;
[self.view addSubview:self.linkMicAreaView];
```

同时 `PLVLSStreamerViewController` 需遵循协议 `PLVLSLinkMicAreaViewDelegate`，并实现以下代理方法：

```objective-c
@protocol PLVLSLinkMicAreaViewDelegate <NSObject>

/// 连麦窗口列表视图 需要获取当前用户数组
///
/// @param linkMicAreaView 连麦窗口列表视图
- (NSArray *)plvLSLinkMicWindowsViewGetCurrentUserModelArray:(PLVLSLinkMicAreaView *)linkMicAreaView;

/// 连麦窗口列表视图 需要查询某个条件用户的下标值
///
/// @param linkMicAreaView 连麦窗口列表视图
/// @param filtrateBlockBlock 筛选条件Block
- (NSInteger)plvLSLinkMicWindowsView:(PLVLSLinkMicAreaView *)linkMicAreaView findUserModelIndexWithFiltrateBlock:(BOOL(^)(PLVLinkMicOnlineUser * enumerateUser))filtrateBlockBlock;

/// 连麦窗口列表视图 需要根据下标值获取对应用户
///
/// @param linkMicAreaView 连麦窗口列表视图
/// @param targetIndex 目标下标值
- (PLVLinkMicOnlineUser *)plvLSLinkMicWindowsView:(PLVLSLinkMicAreaView *)linkMicAreaView getUserModelFromOnlineUserArrayWithIndex:(NSInteger)targetIndex;

@end
```

并在 `-viewWillLayoutSubviews` 方法中，对 `PLVLSLinkMicAreaView` 进行布局：

```objective-c
self.linkMicAreaView.frame = CGRectMake(CGRectGetMaxX(self.documentAreaView.frame) + linkMicAreaViewLeftPadding, CGRectGetMaxY(self.statusAreaView.frame), linkMicAreaViewWidth, ducomentViewHeight);
```

上述内容的具体实现可以在demo项目中 `PLVLSStreamerViewController`找到。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://polyv.gitbook.io/document/docs/live/ios/52-shou-ji-kai-bo-chang-jing-lian-mai-tui-liu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
