/
CDNStreamingLiveExample.m
213 lines (178 loc) · 7.69 KB
/
CDNStreamingLiveExample.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
//
// CDNStreamingLiveExample.m
// Api-Examples-iOS
//
// Created by WorkSpace_Sun on 2021/11/24.
//
#import "CDNStreamingLiveExample.h"
#import "DirectLiveControlView.h"
#import "ScanViewController.h"
@interface CDNStreamingLiveExample () <QNRTCClientDelegate, ScanViewControllerDelegate,QNCDNStreamingDelegate>
@property (nonatomic, strong) QNCameraVideoTrack *cameraVideoTrack;
@property (nonatomic, strong) QNMicrophoneAudioTrack *microphoneAudioTrack;
@property (nonatomic, strong) QNVideoGLView *localRenderView;
@property (nonatomic, strong) QNCDNStreamingClient *cdnStreamingClient;
@property (nonatomic, strong) QNCDNStreamingConfig *streamingConfig;
@property (nonatomic, strong) DirectLiveControlView *controlView;
@property (nonatomic, copy) NSString *remoteUserID;
@property (nonatomic, assign) BOOL isStreaming;
@end
@implementation CDNStreamingLiveExample
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
self.isStreaming = NO;
[self loadSubviews];
[self initRTC];
self.title = @"CDN 直推";
}
/*!
* @warning 务必主动释放 SDK 资源
*/
- (void)clickBackItem {
[super clickBackItem];
// 离开房间 释放 client
// 清理配置
[QNRTC deinit];
}
/*!
* @abstract 初始化视图
*/
- (void)loadSubviews {
self.localView.text = @"本端视图";
self.remoteView.text = @"远端视图";
self.tips = @"Tips:本示例仅展示 RTMP 场景景下本地音视频 Track 的推流功能";
// 添加推流控制视图
self.controlView = [[[NSBundle mainBundle] loadNibNamed:@"DirectLiveControlView" owner:nil options:nil] lastObject];
self.controlView.publishUrlTF.text = @"";
[self.controlView.startButton addTarget:self action:@selector(startLiveStreaming) forControlEvents:UIControlEventTouchUpInside];
[self.controlView.stopButton addTarget:self action:@selector(stopLiveStreaming) forControlEvents:UIControlEventTouchUpInside];
[self.controlView.scanButton addTarget:self action:@selector(scanAction:) forControlEvents:UIControlEventTouchUpInside];
CGFloat topMargin = [[UIApplication sharedApplication] statusBarFrame].size.height + 44.0;
[self.localView mas_updateConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self.view).mas_offset(SCREEN_WIDTH / 4.0);
make.top.equalTo(self.view).mas_offset(topMargin);
make.width.mas_equalTo(SCREEN_WIDTH / 2.0);
make.height.mas_equalTo(SCREEN_WIDTH / 2.0 / 3 * 4);
}];
[self.controlScrollView addSubview:self.controlView];
[self.controlView mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.top.equalTo(self.controlScrollView);
make.width.mas_equalTo(SCREEN_WIDTH);
make.height.mas_equalTo(240);
}];
[self.controlView layoutIfNeeded];
self.controlScrollView.contentSize = self.controlView.frame.size;
// 初始化本地预览视图
self.localRenderView = [[QNVideoGLView alloc] init];
[self.localView addSubview:self.localRenderView];
self.localRenderView.hidden = YES;
[self.localRenderView mas_makeConstraints:^(MASConstraintMaker *make) {
make.edges.equalTo(self.localView);
}];
self.remoteView.hidden = YES;
}
- (void)scanAction:(UIButton *)button {
ScanViewController *scanVc = [ScanViewController new];
scanVc.delegate = self;
scanVc.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:scanVc animated:YES completion:nil];
}
- (void)scanQRResult:(NSString *)qrString {
if (qrString.length != 0) {
self.controlView.publishUrlTF.text = qrString;
}
}
/*!
* @abstract 初始化 RTC
*/
- (void)initRTC {
// QNRTC 配置
QNRTCConfiguration *configuration = [QNRTCConfiguration defaultConfiguration];
[QNRTC initRTC:configuration];
// 自定义采集配置
QNVideoEncoderConfig *videoConfig = [[QNVideoEncoderConfig alloc] initWithBitrate:2000 videoEncodeSize:CGSizeMake(720, 1280)];
QNCameraVideoTrackConfig *cameraVideoTrackConfig = [[QNCameraVideoTrackConfig alloc] initWithSourceTag:@"camera"
config:videoConfig
multiStreamEnable:NO];
// 使用自定义配置创建相机采集视频 Track
self.cameraVideoTrack = [QNRTC createCameraVideoTrackWithConfig:cameraVideoTrackConfig];
// 设置采集分辨率(要保证预览分辨率 sessionPreset 不小于 QNCameraVideoTrackConfig 的编码分辨率 videoEncodeSize)
self.cameraVideoTrack.videoFormat = AVCaptureSessionPreset1280x720;
// 创建麦克风采集音频 Track
self.microphoneAudioTrack = [QNRTC createMicrophoneAudioTrack];
// 开启本地预览
[self.cameraVideoTrack play:self.localRenderView];
self.localRenderView.hidden = NO;
// 创建 cdn streaming client
self.cdnStreamingClient = [QNRTC createCDNStreamingClient];
self.cdnStreamingClient.delegate = self;
}
#pragma mark - 开始 / 停止推流、QNRTCClientDelegate 中推流相关回调
/*!
* @abstract 开始推流。
*/
- (void)startLiveStreaming {
// 校验是否在推流中
if (self.isStreaming) {
[self showAlertWithTitle:@"状态提示" message:@"请先停止推流任务"];
return;
}
// 校验推流地址
if ([self.controlView.publishUrlTF.text isEqualToString:@""]) {
[self showAlertWithTitle:@"参数错误" message:@"请输入推流地址"];
return;
}
self.streamingConfig = [[QNCDNStreamingConfig alloc] init];
self.streamingConfig.publishUrl = self.controlView.publishUrlTF.text;
self.streamingConfig.audioTrack = self.microphoneAudioTrack;
self.streamingConfig.videoTrack = self.cameraVideoTrack;
// 开始推流
[self.cdnStreamingClient startWithConfig:self.streamingConfig];
}
/*!
* @abstract 停止推流。
*/
- (void)stopLiveStreaming {
// 校验是否在推流中
if (!self.isStreaming) {
[self showAlertWithTitle:@"状态提示" message:@"请先开始推流任务"];
return;
}
// 停止单人推流
[self.cdnStreamingClient stop];
}
#pragma mark - QNCDNStreamingDelegate
- (void)cdnStreamingClient:(QNCDNStreamingClient *)client didCDNStreamingConnectionStateChanged:(QNConnectionState)state
errorCode:(int)code
message:(NSString *)message {
dispatch_async(dispatch_get_main_queue(), ^{
switch (state) {
case QNConnectionStateConnected:
case QNConnectionStateReconnected: {
self.isStreaming = YES;
[self showAlertWithTitle:@"状态提示" message:@"开始推流成功"];;
break;
}
case QNConnectionStateConnecting: {
[self showAlertWithTitle:@"状态提示" message:@"推流连连中"];
}
break;
case QNConnectionStateReconnecting: {
[self showAlertWithTitle:@"状态提示" message:@"重连中"];
}
break;
case QNConnectionStateDisconnected: {
self.isStreaming = NO;
[self showAlertWithTitle:@"状态提示" message:@"停止推流成功"];
}
break;;
default:
break;
}
});
}
- (void)cdnStreamingClient:(QNCDNStreamingClient *)client didCDNStreamingStats:(QNCDNStreamingStats *)stats {
NSLog(@"%@",[NSString stringWithFormat:@"视频帧率:%d 视频码率:%d 视频码率:%d 丢包数:%d",stats.sendVideoFps,stats.videoBitrate,stats.audioBitrate,stats.droppedVideoFrames]);
}
@end