LiveSDK.class.php
15.3 KB
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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
<?php
/**
* 腾讯云直播 SDK
* Created by PhpStorm.
* User: zhoutao
* Date: 2018/1/9
* Time: 下午4:32
*/
namespace Com;
use Org\Net\Snoopy;
class LiveSDK extends Live
{
/**
* 直播开关状态 0: 表示禁用 1: 表示允许推流 2: 表示断流
*/
const CHANNEL_STATUS_DISABLED = 0;
const CHANNEL_STATUS_ALLOW = 1;
const CHANNEL_STATUS_CUT_OFF = 2;
const CHANNEL_STATUS_LIST = [
self::CHANNEL_STATUS_DISABLED,
self::CHANNEL_STATUS_ALLOW,
self::CHANNEL_STATUS_CUT_OFF
];
/**
* 直播间管理动作 断流:forbid;恢复推流:resume
*/
const CHANNEL_MANAGER_FORBID = 'forbid';
const CHANNEL_MANAGER_RESUME = 'resume';
/**
* 直播间开启录制任务 0: 非实时 1: 实时
*/
const TAPE_TASK_SUB_TYPE_NOT_REAL_TIME = 0;
const TAPE_TASK_SUB_TYPE_REAL_TIME = 1;
/**
* 开启关闭推流
* @param string $channelId 直播码
* @param int $status 开关状态 0 表示禁用,1 表示允许推流,2 表示断流
* @return bool
*/
public function channelSetStatus($channelId, $status)
{
if (!in_array($status, self::CHANNEL_STATUS_LIST) || empty($channelId)) {
return false;
}
return $this->requestSdk('Live_Channel_SetStatus', 'fcgi', [
'Param.s.channel_id' => $channelId,
'Param.n.status' => $status
]);
}
/**
* 查询频道列表
* @param null $status 0:表示断流,1:表示开启,3:表示关闭 默认是不过滤
* @param int $page 分页页码 从 1 开始,默认为 1
* @param int $limit 分页大小 10~100,默认为 10
* @param string $orderField 排序字段 可选字段:create_time,默认为create_time
* @param null $orderType 排序方法 0:表示正序,1:表示倒序
* @return bool
*/
public function channelGetChannelList($status = null, $page = 1, $limit = 10, $orderField = '', $orderType = null)
{
// 如果有值则对应上字段传输
$params = [];
$field = [
'Param.n.status',
'Param.n.page_no',
'Param.n.page_size',
'Param.s.order_field',
'Param.n.order_type'
];
foreach (func_get_args() as $key => $item) {
if (!empty($item)) {
$params[$field[$key]] = $item;
}
};
return $this->requestSdk('Live_Channel_GetChannelList', 'fcgi', $params);
}
/**
* 暂停推流并延迟恢复接口
* @param string $channelId 直播码
* @param int $abstimeEnd 禁播截止的时间戳 禁播截止的绝对时间,请填写UNIX 时间戳(十进制),系统最多禁播三个月。
* @param string $action 动作 断流:forbid;恢复推流:resume
* @return bool
*/
public function channelManager($channelId, $abstimeEnd, $action)
{
return $this->requestSdk('channel_manager', 'fcgi', [
'Param.s.channel_id' => $channelId,
'Param.n.abstime_end' => $abstimeEnd,
'Param.s.action' => $action
]);
}
/**
* 创建录制任务
* @param String $channelId 频道 ID
* @param String $startTime 任务开始时间 标准的 date_time,需要 urlencode,如 2017-01-01%2010:10:01
* @param String $endTime 任务结束时间 标准的 date_time,需要 urlencode,如 2017-01-01%2010:10:01。
* @param Int $taskSubType 是否开启实时视频录制 默认 0,1 表示开启实时视频录制。
(1)若开启实时视频录制,调用接口则同步开始录制,此时如果传入任务开始时间参数,任务开始时间参数无效。
(2)开启实时视频录制的同时如果传入了任务结束时间,则按照任务结束时间结束录制。若没传入,则 30 分钟后自动结束录制。
(3)实时录制任务开始时间与任务结束时间超过 30 分钟,则 30 分钟后会自动结束录制,实时视频录制建议控制台在 5 分钟以内。
* @param String $fileFormat 录制文件格式 默认 flv;可取值 flv、hls、mp4、aac
* @param String $recordType 录制文件类型 默认 video
当 record_type 取值“video”时,file_format 可以取值 “flv”,"hls", "mp4"
当 record_type 取值“audio”时,file_format 可以取值 “aac”,“flv”,“hls”,“mp4”
* @return bool
*/
public function liveTapeStart(
$channelId, $startTime, $endTime,
$taskSubType = self::TAPE_TASK_SUB_TYPE_NOT_REAL_TIME, $fileFormat = 'flv', $recordType = 'video')
{
return $this->requestSdk('Live_Tape_Start', 'fcgi', [
'Param.s.channel_id' => $channelId,
'Param.s.start_time' => $startTime,
'Param.s.end_time' => $endTime,
'Param.n.task_sub_type' => $taskSubType,
'Param.s.file_format' => $fileFormat,
'Param.s.record_type' => $recordType
]);
}
/**
* 结束录制任务
* @param String $channelId 频道 ID
* @param String $taskId 任务 ID
* @param Int $taskSubType 是否开启实时视频录制 默认 0,1 表示开启小视频录制
* @return bool
*/
public function liveTapeStop($channelId, $taskId, $taskSubType = self::TAPE_TASK_SUB_TYPE_NOT_REAL_TIME)
{
return $this->requestSdk('Live_Tape_Stop', 'fcgi', [
'Param.s.channel_id' => $channelId,
'Param.s.task_id' => $taskId,
'Param.n.task_sub_type' => $taskSubType
]);
}
/**
* 查询录制文件
* @param String $channelId 直播码
* @param String $startTime 查询开始时间 格式为:2016-12-10 00:00:00
* @param String $endTime 查询结束时间 格式为:2016-12-10 00:00:00。结束时间距开始时间一天以内,且不能跨天
* @param Int $page 分页页码 从 1 开始,默认为 1
* @param Int $limit 分页大小 1~100,默认为 10
* @param String $orderType 排序方式 asc 表示升序,desc 表示降序,默认 asc
* @return bool
*/
public function liveTapeGetFilelist(
$channelId, $startTime = null, $endTime = null, $page = 1, $limit = 10, $orderType = 'asc')
{
// 如果有值则对应上字段传输
$params = [];
$field = [
'Param.s.channel_id',
'Param.s.start_time',
'Param.s.end_time',
'Param.n.page_no',
'Param.n.page_size',
'Param.s.sort_type',
];
foreach (func_get_args() as $key => $item) {
if (!empty($item)) {
$params[$field[$key]] = $item;
}
};
return $this->requestSdk('Live_Tape_GetFilelist', 'fcgi', $params);
}
/**
* 获取播放统计历史信息
* @param int $startTime 必填 查询起始时间 时间戳 15 天内的数据
* @param int $endTIme 必填 结束时间 时间戳建议时间跨度不大于 2 小时
* @param string $streamId 非必填 流 ID 不填就是获取总带宽
* @param string $domain 非必填 域名 若不填,取这个 APPID 下的总数据,需要填写 cname 前的原始播放域名
* @return mixed
*/
public function getLivePlayStatHistory($startTime, $endTIme, $streamId = '', $domain = '')
{
// 如果有值则对应上字段传输
$params = [];
$field = [
'Param.n.start_time',
'Param.n.end_time',
'Param.s.stream_id',
'Param.s.domain'
];
foreach (func_get_args() as $key => $item) {
if (!empty($item)) {
$params[$field[$key]] = $item;
}
};
return $this->requestSdk('Get_LivePlayStatHistory', 'statcgi', $params);
}
/**
* 查询指定直播流的推流和播放信息
* @param string $streamId 直播码 如不设置 stream_id:查询所有正在直播中的流
* @param int $page 分页页码 从 1 开始,默认为 1
* @param int $pageSize 分页大小 1~300,默认为 300
* @param string $pullDomain 拉流域名 即播放域名,如果不填则返回所有域名的播放数据
* @return bool
*/
public function getLiveStat($streamId, $page = 1, $pageSize = 300, $pullDomain = '')
{
// 如果有值则对应上字段传输
$params = [];
$field = [
'Param.s.stream_id',
'Param.n.page_no',
'Param.n.page_size',
'Param.s.pull_domain'
];
foreach (func_get_args() as $key => $item) {
if (!empty($item)) {
$params[$field[$key]] = $item;
}
};
return $this->requestSdk('Get_LiveStat', 'statcgi', $params);
}
/**
* 同 getLiveStat 方法 只是 仅返回播放统计信息以提高查询效率
* @param $streamId
* @param int $page
* @param int $pageSize
* @param string $pullDomain
* @return bool
*/
public function getLivePlayStat($streamId, $page = 1, $pageSize = 300, $pullDomain = '')
{
// 如果有值则对应上字段传输
$params = [];
$field = [
'Param.s.stream_id',
'Param.n.page_no',
'Param.n.page_size',
'Param.s.pull_domain'
];
foreach (func_get_args() as $key => $item) {
if (!empty($item)) {
$params[$field[$key]] = $item;
}
};
return $this->requestSdk('Get_LivePlayStat', 'statcgi', $params);
}
}
abstract class Live
{
/**
* 操作类
*/
const TYPE_OF_OPERATION = 'fcgi';
/**
* 统计类
*/
const TYPE_OF_STAT = 'statcgi';
/**
* 基础 URL
*/
const BASE_URL = 'http://%s.video.qcloud.com/common_access?%s';
/**
* API鉴权key
* @var string
*/
private $apiAuthKey = '';
/**
* 推流防盗链Key
* @var string
*/
private $antiTheftChain = '';
/**
* AppId
* @var int
*/
private $appId = null;
/**
* BizId
* @var int
*/
private $bizId = null;
public function __construct()
{
$this->apiAuthKey = cfg('TENCENT_LIVE_API_AUTH_KEY');
$this->antiTheftChain = cfg('TENCENT_LIVE_ANTI_THEFT_CHAIN_KEY');
$this->appId = cfg('TENCENT_LIVE_APPID');
$this->bizId = cfg('TENCENT_LIVE_BIZID');
}
/**
* 请求腾讯云 SDK
* @param $interface
* @param $type
* @param array $urlParams
* @param array $postParams
* @param array $headers
* @param string $method
* @param null $file
* @return bool
*/
protected function requestSdk(
$interface, $type, $urlParams = [], $postParams = [], $headers = [], $method = 'GET', $file = null
) {
// 防止太快过期 + 10秒
$t = NOW_TIME + 10;
$baseParams = [
'appid' => $this->appId,
'interface' => $interface,
't' => $t,
'sign' => md5($this->apiAuthKey . $t),
];
$params = [
self::BASE_URL,
$type,
http_build_query(array_merge($baseParams, $urlParams)),
];
$url = call_user_func_array('sprintf', $params);
\Think\Log::record('腾讯云直播 接口 ::: ' . $interface . ' 请求参数 ::: ' . var_export($postParams, true));
return $this->request($url, $postParams, $headers, $method, $file);
}
/**
* 请求接口数据
* @param string $url 请求URL
* @param string|array $reqParams 请求数据
* @param array $headers 请求头部
* @param string $method 请求方式, 如: GET/POST/DELETE/PUT
* @param mixed $files 文件路径
* @param bool $retry 是否重试
* @return bool
*/
protected function request($url, $reqParams, $headers = [], $method = 'GET', $files = null, $retry = false)
{
$snoopy = new Snoopy();
// 使用自定义的头字段,格式为 array(字段名 => 值, ... ...)
$snoopy->rawheaders = $headers;
// 非 GET 协议, 需要设置
$method = rstrtoupper($method);
$methods = array('GET', 'POST', 'PUT', 'DELETE');
if (!in_array($method, $methods)) {
$method = 'GET';
}
// 设置协议
if (!empty($files)) {
// 如果需要传文件
$method = 'POST';
$snoopy->set_submit_multipart();
} else {
$snoopy->set_submit_normal('application/json');
}
// 判断协议
$snoopy->set_submit_method($method);
switch (rstrtoupper($method)) {
case 'POST' :
case 'PUT' :
case 'DELETE' :
$result = $snoopy->submit($url, $reqParams, $files);
break;
default :
$result = $snoopy->fetch($url);
break;
}
// 如果读取错误
if (!$result || 200 != $snoopy->status) {
\Think\Log::record('请求错误::' . var_export($snoopy, true));
}
// 获取返回数据 (腾讯云返回的结果不是纯的 json 字符串)
preg_match("/\{.*\}/is", $snoopy->results, $matches);
$result = json_decode($matches[0], true);
\Think\Log::record('返回数据:::' . var_export($matches[0], true));
return $result;
}
/**
* 获取推流地址
* 如果不传key和过期时间,将返回不含防盗链的url
* @param String $livecode 直播码 bizid+"_"+stream_id 如 8888_test123456
* @param String $time 过期时间 10位数时间戳
* @return array 0 推流地址 1 推流名称
*/
public function getPushUrl($livecode, $time)
{
$txTime = strtoupper(base_convert($time, 10, 16));
$ext_str = '?' . http_build_query(array(
'bizid' => $this->bizId,
// MD5( KEY + livecode + txTime )
'txSecret' => md5($this->antiTheftChain . $livecode . $txTime),
'txTime' => $txTime,
));
return [
'rtmp://' . $this->bizId . '.livepush.myqcloud.com/live/',
$livecode . (isset($ext_str) ? $ext_str : '')
];
}
/**
* 获取播放地址
* @param String bizId 您在腾讯云分配到的bizid
* @param String $livecode 直播码 bizid+"_"+stream_id 如 8888_test123456
* @return String url
*/
public function getPlayUrl($livecode)
{
return array(
'rtmp://' . $this->bizId . '.liveplay.myqcloud.com/live/' . $livecode,
'http://' . $this->bizId . '.liveplay.myqcloud.com/live/' . $livecode . '.flv',
'http://' . $this->bizId . '.liveplay.myqcloud.com/live/' . $livecode . '.m3u8',
);
}
/**
* 获取直播码 腾讯云回调通知内容内的 stream_id 或者说 channel_id 两个是一样的 腾讯云自己的历史原因
* @param $id
* @return string
*/
public function getLiveCode()
{
return $this->bizId . '_' . $this->getStreamId();
}
/**
* 获取直播标识
* @return string
*/
private function getStreamId()
{
// 单个企业只会有一个
return strtolower(QY_DOMAIN);
}
}