为了写着三个录制,发了N多个工单,坑有点大

  • 直接上正文
    • 公共部分
    • 页面录制
    • 合流录制
    • 单流录制
    • 结束录制
    • 结语

直接上正文

公共部分

/*** 获取声网ResourceId* @param $studio_id* @param $type* @return bool|array* @throws \think\Exception*/public function getResourceId($studio_id, $type){$studio_info = LiveStudioRepositories::find(['id' => $studio_id], ['title', 'admin_id']);$acquireUrl = config('agora.request_url') . 'v1/apps/' . config('agora.AppId') . '/cloud_recording/acquire';// 请求头if($type == 'web'){$cname = date('YmdHis') . mt_rand(100000, 999999);$scene = 1;}elseif($type == 'mix') {$cname = $studio_id;     // 非页面录制 cname 为待录制的频道名$scene = 0;}elseif($type == 'individual') {$cname = $studio_id;$scene = 2;                 // 单流录制 延时转码}$uid = $studio_info['admin_id'] . '000';       // 声网云端录制uid不能与任何参会者uid相同$header = $this->getHeader();$acquireData = ['cname' => $cname,  // 区分录制进程'uid' => $uid,'clientRequest' => ['resourceExpiredHour' => 24,'scene' => $scene,'region' => 'CN']];$acquireRes = $this->curl($acquireUrl, json_encode($acquireData, true), $header);return ['acquireRes' => $acquireRes, 'cname' => $cname, 'uid' => $uid, 'acquire' => json_encode($acquireData, true)];}/*** 获取header* @return string[]*/public function getHeader(){$credentials = config('agora.key') . ':' . config('agora.secret');$base64_credentials = base64_encode($credentials);// 请求头$header = ["Authorization: Basic " . $base64_credentials,'Content-type: application/json;charset=utf-8'];return $header;}/*** 声网curl* @param $url* @param $data* @param $header* @return bool|string*/public function curl($url, $data, $header){$curl = curl_init();// 发送 HTTP 请求curl_setopt_array($curl, array(CURLOPT_URL => $url,CURLOPT_RETURNTRANSFER => true,CURLOPT_ENCODING => '',CURLOPT_MAXREDIRS => 10,CURLOPT_TIMEOUT => 0,CURLOPT_FOLLOWLOCATION => true,CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,CURLOPT_CUSTOMREQUEST => 'POST',CURLOPT_SSL_VERIFYPEER => false,CURLOPT_POSTFIELDS => $data,CURLOPT_HTTPHEADER => $header));$response = curl_exec($curl);curl_close($curl);if ($response === false) {ErrorLogRepositories::add('getResourceId', curl_error($curl));}return $response;}/*** 获取录制模式* @param $type* @return bool|string*/public function getMode($type){switch ($type) {case 1:$mode = 'web';    // 页面录制break;case 2:$mode = 'mix';    // 合流录制break;case 3:$mode = 'individual';    // 单流录制break;default:$mode = false;}return $mode;}/*** 获取MP4文件名称* @param $res* @param $type* @return false|array*/// 因为单流录制的 mp4 格式不会直接得到,所以没有单流录制的获取public function getMp4($res, $type){$file_name_arr = [];$slice_start_at = 0;// 网页录制if ($type == WebCastEnum::TYPE_WEB) {$array = $res['serverResponse']['extensionServiceState'][0]['payload']['fileList'];foreach ($array as $item) {if (strtolower(substr($item['filename'], -3)) == 'mp4') {$file_name_arr[] = $item['filename'];$slice_start_at = substr($item['sliceStartTime'], 0, 10);   //返回的是毫秒级时间戳 需处理成11位}}}// 合流录制if ($type == WebCastEnum::TYPE_MIX) {$array = $res['serverResponse']['fileList'];foreach ($array as $item) {if (strtolower(substr($item['fileName'], -3)) == 'mp4') {$arr = explode('/', $item['fileName']);$file_name_arr[] = end($arr);$slice_start_at = substr($item['sliceStartTime'], 0, 10);   //返回的是毫秒级时间戳 需处理成11位}}}return [json_encode($file_name_arr, true), $slice_start_at];}

页面录制

/*** 页面录制* @param $param* @param $user* @return array|bool* @throws \think\Exception*/public function recordStartWeb($param, $user){$studio_id = $param['studio_id'];$role_type = $user['role_type'];if ($role_type != WebCastEnum::HOSTER && $role_type != WebCastEnum::JOINT) {$this->setError('没有权限进行此操作');return false;}$acquireRes = $this->getResourceId($studio_id, 'web');if (!$acquireRes['acquireRes']) {$this->setError('录制失败,resourceID获取失败');return false;}$cname = $acquireRes['cname'];$uid = $acquireRes['uid'];$acquireRes = json_decode($acquireRes['acquireRes'], true);if (!isset($acquireRes['resourceId'])) {$this->setError('录制失败,resourceID获取失败');return false;}$resource_id = $acquireRes['resourceId'];//拼接录制url$start_url = config('agora.request_url') . 'v1/apps/' . config('agora.AppId') . '/cloud_recording/resourceid/' . $resource_id . '/mode/web/start';//获取声网token$agora_token = $this->getAgoraToken(['studio_id' => $studio_id, 'uid' => $uid, 'role_type' => 1]);//获取header$header = $this->getHeader();$env = Env::get('env');$cast_link = config('env.'.$env)['web_link'];$url = 'https://www.baidu.com'; //录制页面url$day = date('Ymd');$Prefix = $studio_id . date('YmdHis');$startData = ['cname' => $cname,'uid' => $uid,'clientRequest' => ['token' => $agora_token['token'],'extensionServiceConfig' => ['errorHandlePolicy' => 'error_abort','extensionServices' => [['serviceName' => 'web_recorder_service','errorHandlePolicy' => 'error_abort','serviceParam' => ['url' => $url,'audioProfile' => 1, //48 kHz 采样率,音乐编码,单声道,编码码率约 128 Kbps'videoWidth' => 1280,'videoHeight' => 720,'maxRecordingHour' => 72, //最大录制时长]]]],'recordingConfig' => ['maxIdleTime' => 30  //不设置的话默认为30s:即推流中断30s,就会停止录制,建议设置此项],'recordingFileConfig' => ['avFileType' => ['mp4', 'hls']],'storageConfig' => [   //此处为第三方存储,可以设置其他存储'vendor' => 2, // 阿里云'region' => 3, // cn-shanghai'bucket' => 'xxx', // 阿里云bucket'accessKey' => config('aliyun.accessKeyId'),//阿里云accessKey'secretKey' => config('aliyun.accessKeySecret'),//阿里云secretKey'fileNamePrefix' => ['webCast', $day, $Prefix]]],];$res = $this->curl($start_url, json_encode($startData, true), $header);$res = json_decode($res, true);$sid = $res['sid'] ?? null;if ($sid == null) {$this->setError('录制失败! sid 获取失败');return false;}$this->setMessage('开启录制成功');// 记录(存储或者记录)$this->redis->select(0);$record = WebBroadcastParticipantRepositories::getOrderOne(['studio_id' => $studio_id, 'uid' => $uid],'id','desc',['record_id']);$web_start_at = LiveStudioWebCaseInteractiveRecordRepositories::getField(['id' => $record['record_id']], 'start_at');$data = ['studio_id' => $studio_id,'cname' => $cname,'uid' => $uid,'res' => $res,'type' => WebCastEnum::TYPE_WEB,'start_at' => time(),'file_name_prefix' => $day . '/' . $Prefix,'web_start_at' => $web_start_at,];$this->redis->set($sid, json_encode($data), config('redis.three_days_exp'));$data = ['sid' => $sid,];return $data;}

合流录制

    /*** 开启合流录制* @param $param* @param $user* @return array|bool* @throws \think\Exception*/public function recordStartMix($param, $user){$studio_id = $param['studio_id'];$role_type = $user['role_type'];if ($role_type != WebCastEnum::HOSTER && $role_type != WebCastEnum::JOINT) {$this->setError('没有权限进行此操作');return false;}$acquireRes = $this->getResourceId($studio_id, 'mix');if (!$acquireRes['acquireRes']) {$this->setError('录制失败,resourceID获取失败');return false;}$cname = $acquireRes['cname'];$uid = $acquireRes['uid'];$acquireRes = json_decode($acquireRes['acquireRes'], true);if (!isset($acquireRes['resourceId'])) {$this->setError('录制失败,resourceID获取失败');return false;}$resource_id = $acquireRes['resourceId'];//拼接录制url$start_url = config('agora.request_url') . 'v1/apps/' . config('agora.AppId') . '/cloud_recording/resourceid/' . $resource_id . '/mode/mix/start';//获取声网token$agora_token = $this->getAgoraToken(['studio_id' => $cname, 'uid' => $uid, 'role_type' => 1]);//获取header$header = $this->getHeader();$admin_id = LiveStudioRepositories::getField(['id' => $studio_id], 'admin_id');$day = date('Ymd');$Prefix = $studio_id . date('YmdHis');$startData = ['cname' => $cname,'uid' => $uid,'clientRequest' => ['token' => $agora_token['token'],'recordingConfig' => ['maxIdleTime' => 30,  //不设置的话默认为30s:即推流中断30s,就会停止录制,建议设置此项'audioProfile' => 1,'channelType' => 0,'transcodingConfig' => (object)['width' => 1280,'height' => 720,'bitrate' => 1130,'fps' => 15,'mixedVideoLayout' => 2,'maxResolutionUid' => (string)$admin_id,'backgroundColor' => '#FFFFFF']],'recordingFileConfig' => ['avFileType' => ['mp4', 'hls']],'storageConfig' => [        // 此处为第三方存储,可以设置其他存储'vendor' => 2,          // 阿里云'region' => 3,          // cn-beijing'bucket' => 'xxx',   // 阿里云bucket'accessKey' => config('aliyun.accessKeyId'),        // 阿里云accessKey'secretKey' => config('aliyun.accessKeySecret'),    // 阿里云secretKey'fileNamePrefix' => ['webCast', $day, $Prefix]                         // 目录文件夹]],];$res = $this->curl($start_url, json_encode($startData, true), $header);$res = json_decode($res, true);if (isset($res['code']) && $res['code'] != 200) {$err = '录制失败';if (isset($res['reason'])) {$err = $res['reason'];}$this->setError($err);return false;}$sid = $res['sid'] ?? null;if ($sid == null) {$this->setError('录制失败! sid 获取失败');return false;}// 记录(存储或者记录)$this->redis->select(0);$record = WebBroadcastParticipantRepositories::getOrderOne(['studio_id' => $studio_id, 'uid' => $uid],'id','desc',['record_id']);$web_start_at = LiveStudioWebCaseInteractiveRecordRepositories::getField(['id' => $record['record_id']], 'start_at');$data = ['studio_id' => $studio_id,'cname' => $cname,'uid' => $uid,'res' => $res,'type' => WebCastEnum::TYPE_MIX,'start_at' => time(),'file_name_prefix' => $day . '/' . $Prefix,'web_start_at' => $web_start_at,];$this->redis->set($sid, json_encode($data), config('redis.three_days_exp'));return ['sid' => $sid,];}

单流录制

单流录制MP4格式的录制文件需要延时转码,
具体文件什么时候开始转码 (回调事件 80),
什么时候结束转码 (回调事件 81),
详见链接 :https://docs.agora.io/cn/cloud-recording/cloud_recording_callback_rest?platform=RESTful

需要发工单开通消息回调服务
详见链接:https://docs-preprod.agora.io/cn/Agora%20Platform/ncs?platform=Android

    /*** 开启单流录制* @param $param* @param $user* @return array|bool* @throws \think\Exception*/public function recordStartIndividual($param, $user){$studio_id = $param['studio_id'];$role_type = $user['role_type'];// 主持和联席主持if ($role_type != WebCastEnum::HOSTER && $role_type != WebCastEnum::JOINT) {$this->setError('没有权限进行此操作');return false;}$acquireRes = $this->getResourceId($studio_id, 'individual');if (!$acquireRes['acquireRes']) {$this->setError('录制失败,resourceID获取失败');return false;}$cname = $acquireRes['cname'];$uid = $acquireRes['uid'];$acquire = $acquireRes['acquire'];$acquireRes = json_decode($acquireRes['acquireRes'], true);if (!isset($acquireRes['resourceId'])) {$this->setError('录制失败,resourceID获取失败');return false;}$resource_id = $acquireRes['resourceId'];//拼接录制url$start_url = config('agora.request_url') . 'v1/apps/' . config('agora.AppId') . '/cloud_recording/resourceid/' . $resource_id . '/mode/individual/start';//获取声网token$agora_token = $this->getAgoraToken(['studio_id' => $cname, 'uid' => $uid, 'role_type' => 1]);//获取header$header = $this->getHeader();$day = date('Ymd');$Prefix = $studio_id . date('YmdHis');$startData = ['cname' => $cname,'uid' => $uid,'clientRequest' => ['token' => $agora_token['token'],'appsCollection' => ['combinationPolicy' => 'postpone_transcoding'   // 延时转码],'recordingConfig' => ['maxIdleTime' => 30,       // 不设置的话默认为30s:即推流中断30s,就会停止录制,建议设置此项'channelType' => 0,'subscribeUidGroup' => 3    // 单流录制模式必填,预估的人数峰值],'storageConfig' => [        // 此处为第三方存储,可以设置其他存储'vendor' => 2,          // 阿里云'region' => 3,          // cn-beijing'bucket' => 'xxx',   // 阿里云bucket'accessKey' => config('aliyun.accessKeyId'),        // 阿里云accessKey'secretKey' => config('aliyun.accessKeySecret'),    // 阿里云secretKey'fileNamePrefix' => ['webCast', $day, $Prefix]                         // 目录文件夹]],];$res = $this->curl($start_url, json_encode($startData, true), $header);$res = json_decode($res, true);if (isset($res['code']) && $res['code'] != 200) {$err = '录制失败';if (isset($res['reason'])) {$err = $res['reason'];}$this->setError($err);return false;}$sid = $res['sid'] ?? null;if ($sid == null) {$this->setError('录制失败! sid 获取失败');return false;}$this->redis->select(0);$record = WebBroadcastParticipantRepositories::getOrderOne(['studio_id' => $studio_id, 'uid' => $uid],'id','desc',['record_id']);$web_start_at = LiveStudioWebCaseInteractiveRecordRepositories::getField(['id' => $record['record_id']], 'start_at');$data = ['studio_id' => $studio_id,'cname' => $cname,'uid' => $uid,'res' => $res,'type' => WebCastEnum::TYPE_INDIVIDUAL,'start_at' => time(),'file_name_prefix' => $day . '/' . $Prefix,'web_start_at' => $web_start_at,];$this->redis->set($sid, json_encode($data), config('redis.three_days_exp'));return ['sid' => $sid,];}

结束录制

    /*** 结束录制* @param $param* @return array|bool*/public function recordStop($param){$sid = $param['sid'];$this->redis->select(0);$redis_data = $this->redis->get($sid);if (!$redis_data) {$this->setError('sid不正确');return false;}$data = json_decode($redis_data, true);$resource_id = $data['res']['resourceId'];$cname = $data['cname'];$uid = $data['uid'];$start_at = $data['start_at'];$type = $data['type'];$studio_id = $data['studio_id'];$file_name_prefix = $data['file_name_prefix'];$web_start_at = $data['web_start_at'];//拼接录制url (业务需要,可以不用这一步)$mode = $this->getMode($type);if ($mode === false) {$this->setError('类型错误');return false;}// 结束录制url$stop_url = config('agora.request_url') . 'v1/apps/' . config('agora.AppId') . '/cloud_recording/resourceid/' . $resource_id . '/sid/' . $sid .'/mode/' . $mode . '/stop';//获取header$header = $this->getHeader();$stopData = ['cname' => $cname,'uid' => $uid,'clientRequest' => ['async_stop' => false]];$result = $this->curl($stop_url, json_encode($stopData, true), $header);$res = json_decode($result, true);if (isset($res['code']) && $res['code'] != 200) {$err = '录制结束失败';if (isset($res['serverResponse']['payload']['message'])) {$err = $res['serverResponse']['payload']['message'];}$this->setError($err);return false;}$this->setMessage('录制结束');$this->redis->del($sid);// 提出 mp4 文件list($file_str, $slice_start_at) = $this->getMp4($res, $type);if(!$slice_start_at) $slice_start_at = $start_at;$record_time = time() - $slice_start_at;$transcoding_finish = $type == WebCastEnum::TYPE_INDIVIDUAL ? 0 : 1;$add_data = ['studio_id' => $studio_id,'sid' => $sid,'result' => $result,'record_time' => $record_time,'type' => $type,'file_name_prefix' => $file_name_prefix,'file_name' => $file_str,'slice_start_at' => $slice_start_at,'web_start_at' => $web_start_at,'created_at' => time(),'transcoding_finish' => $transcoding_finish,];LiveStudioWebCaseAgoraRecordRepositories::getAddId($add_data);return ['res' => $res, 'record_time' => $record_time,'slice_start_at' => $slice_start_at,];}

结语

声网文档上都有这些东西,但是写的很分散,很难想象当前业务的文档中一些关键的点再另一篇文档中,而且还没有相关超链接跳转,就很那啥。
只能求助于工单,很影响效率

PHP使用声网的页面录制、合流录制、单流录制相关推荐

  1. 声网(agora)云端录制(页面录制)实践总结

    近来使用声网的云端录制功能中的页面录制,来实现用户视角的课程回放视频录制.由于是声网刚新出的功能,文档不是很详细,所以踩了一些坑.在此记录一二. 注:声网页面录制文档地址:https://docs.a ...

  2. 声网 Agora 本地服务端录制 SDK v3.0 版正式上线

    声网 Agora 本地服务端录制 SDK v3.0 版已经上线,并更新至声网官网「SDK和应用下载」. 新增特性 1. 支持录制以 H.265/HEVC 标准编码的视频流 v3.0 起支持录制以 H. ...

  3. 声网Agora 云端录制服务上线,30 分钟即可快速集成

    录制在很多实时音视频场景中都是必备功能,比如在线教育.金融双录.社交直播等.然而,对于很多不熟悉录制服务原理的人来讲,如果选错了录制服务,会遇到很多问题,例如录制文件丢失.维护与升级成本高等. 录制服 ...

  4. 线上教育核心竞争力是什么?声网发布在线素质、职业教育解决方案

    5月11日,声网在线上举办了主题为"聚焦场景力,释放生态力"的在线教育发布会,正式发布了新生态下在线教育多场景教学解决方案,包括在线音乐.在线美术.在线职业教育.在线编程.Stem ...

  5. 第三方直播SDK对比(腾讯云,阿里云,网易云信,七牛云,金山云,声网,即构科技)

    前言:由于现在直播很火,新加入的公司打算做直播功能,之前没接触于是先去看了下主流第三方平台的SDK,想看下哪个平台的更好一些.本文没什么技术含量,仅仅是将相关官网的资料整理,做了一点对比,方便看到各平 ...

  6. 在办公室装警报、参加杨超越编程大赛——“开发者之友”声网Agora团队是怎样炼成的?...

    声网Agora(以下简称「声网」)是全球首个也是最大的实时互动技术服务平台,根据2018年10月公开的数据,其全球开发者数量超过20万,连接20亿的终端用户.通过全球200多个数据节点的SD-RTN™ ...

  7. 如何实现一套可切换的声网+阿里的直播引擎

    前言 小盒的直播业务一开始是打算用两套引擎切换使用的,所以需要封装一下.而且因为声网和阿里的直播sdk的官方文档都不是很全面,甚至有的还有错误(可能是文档没及时更新)导致无法正常运行,接入时问题多多, ...

  8. 声网纳斯达克上市、TikTok推出TikTok For Business、亚马逊推出无代码应用| Decode the Week...

    世事皆如此,没人喜欢改变. Decode the Week≠音视频技术周刊  News Briefing 1. Instagram将TikTok克隆版本"Reels"扩展至欧洲市场 ...

  9. 基于声网 iOS SDK 实现视频直播应用

    视频互动直播是当前比较热门的玩法,我们经常见到有PK 连麦.直播答题.一起 KTV.电商直播.互动大班课.视频相亲等.本文将演示如何通过声网视频 SDK 在 iOS 端实现一个视频直播应用.话不多说, ...

最新文章

  1. 关联查询的resultMap写法示例
  2. 实际开发---php时间函数
  3. Linux应用集成MySQL数据库访问技巧
  4. 开源当自强:我们不是“便宜货”
  5. Angular之jwt令牌身份验证
  6. 实例应用 自定义页面taglib标签
  7. 删除U盘时提示无法停止‘通用卷’设备的解决方法
  8. 单链表的回文判断(O(n)时间复杂度和O(1)的空间复杂度)
  9. git add/rm/mv文件到暂存区
  10. 峥果智能连接不到服务器,峥果浴霸 ESP8285版本 固件
  11. Gym - 101808K Another Shortest Path Problem (Damascus University Collegiate)【并查集+LCA】
  12. Android网易云信无脑接入(IM+音视频)【网易云信】
  13. 淘宝补单怎么补才有效果?
  14. Gif录制神器GifCam
  15. lol云顶之奕助手_LOL云顶之弈助手app下载-LOL云顶之弈助手官网版下载v1.1.2-FC游戏网...
  16. sysvinit源码分析 Linux-init-process-analyse
  17. 如何利用TL431设计一个可调电压源
  18. Caché程序员必须知道符号与缩写 第二章 ObjectScript中使用的缩写
  19. Nt*和Zw*系列函数的区别
  20. 关于将URL中的特殊字符进行转码和解码

热门文章

  1. 数字化浪潮下 哪些银行业务或“生变”
  2. 为自己的站点实现访客统计
  3. 记一次Maximo移动端app报错的原因
  4. 爬梯:MongoDB入门到高级到SpringBoot-API
  5. 南大软院大神养成计划--CSS网页布局
  6. @SuppressWarnings(deprecation) java编程中方法上有这个注释是什么意思??
  7. 动态内存分配Dynamic allocation(C语言划重点)
  8. 锚( Anchor Boxes)
  9. 计算机数学基础知识点归纳,计算机数学基础--详细介绍
  10. iphone已停用连接itunes怎么解锁教程