PHP之 直播开发后端需要做什么——表设计及相关逻辑

前面已经说过直播开发的准备工作,下面,接着聊聊直播关于表的这块设计,以及开直播的相关逻辑。

当然,我接下来说的表的设计,仅为服务我们这边的业务所需,可能不适合所有的人,这里仅供参考,莫作细究。

关于推、拉流

开始之前,我首先说说这两个东西,因为,后面会用到,我不想说到一半又折回去,说这个。

登录腾讯云,进入控制台,点击“云直播”,就进入直播的控制界面了,这里,我默认你已经配置好“准备工作”里的域名配置,并且,生成推、拉流域名。在【域名管理】列表,找到你的推流域名。类似:

注意那个绿色的圆形小对勾,一定得变绿了,否则,你这个推流域名肯定是无效的,至于如何去配,腾讯云文档中“快速入门”有说明,前篇文“准备工作”都有细说,可供参考。

点击蓝色的域名,进入详情,你就会看到:

点击【推流配置】,进入到配置详情页,往下拉,拉倒底儿,你就会看到“推流地址示例代码”,有JAVA和PHP,选择哪个,你可以猜一下,^_^。

光看这个,就完事了么?当然不是,你还得注意一个地方【鉴权配置】,就在这个推流配置详情页,最上一栏:

你需要了解两个点:推流鉴权是否开启;主Key是什么。

还有,【推流地址生成器】这个模块,注意,“推流防盗链Key”,仔细观察,你就会发现这个跟上面【推流鉴权】的主KEY是一样的。

那么,这个东西是干什么的呢?仔细看下面的示例代码,其中有一个参数“安全秘钥”,是的,这个参数,你就得传这个KEY。

也许,你会说,为什么,示例代码这块不说明呢?如果你这么问,那我只能说,兄弟太年轻,想必还没踩过“微信支付”文档里给埋的雷吧?不然,你肯定就不这么问了,因为,被恶心过的人都知道,看透不说透,是腾讯文档一贯的作风,都没问候人家祖宗呢,能让你进这山门么。这才是个前奏,正题还没开始呢,坑还多着呐,进了鹅厂的地盘,不费点劲,就想吃到鹅肉,兄弟,你想多了。这是推流要注意的。

那,拉流的地址生成呢?是不是也在拉流域名的详情页的“拉流配置”呢,你要这么想,就又错了。是的,没有。其实,推流的示例代码,也可以用来生成拉流地址,只不过,你要先确认拉流地址的播放格式,这个在“拉流配置”有,这里,我选择的是.FLV格式的,为什么要选这个格式的拉流地址呢,主要考虑的是录播的功能,因为,录播格式选择了这种播放格式,另外,.FLV格式的还有其他优点,具体,可参考腾讯文档,我没太记住,骚瑞~!

为表歉意,这里,我把示例代码改造的生成推流、拉流地址的方法,贴出来,仅供参考: 
/**
 * 获取 推流 地址
 * 如果不传key和过期时间,将返回不含防盗链的url
 * @param $protocol : 协议
 * @param $domain : 您用来推流的域名
 * @param $streamName : 您用来区别不同推流地址的唯一流名称
 * @param $key : 安全密钥
 * @param $time : 过期时间 
 * @return String url  
 */
public function getLiveUrl($protocol, $domain, $streamName, $key = null, $time = null){
    if($key && $time){
        $txTime = strtoupper(base_convert(strtotime($time),10,16));
        //txSecret = MD5( KEY + streamName + txTime )
        $txSecret = md5($key.$streamName.$txTime);
        $ext_str = "?".http_build_query(array(
                "txSecret"=> $txSecret,
                "txTime"=> $txTime
            ));
    }
    return $protocol."://".$domain."/live/".$streamName . (isset($ext_str) ? $ext_str : "");
}
/**
 * 获取 拉流 地址
 * 如果不传key和过期时间,将返回不含防盗链的url
 * @param $protocol : 协议
 * @param $domain : 您用来推流的域名
 * @param $streamName : 您用来区别不同推流地址的唯一流名称
 * @param $key : 安全密钥
 * @param $time : 过期时间
 * @return String url
 */
public function getLivePullUrl($protocol, $domain, $streamName, $key = null, $time = null){
    if($key && $time){
        $txTime = strtoupper(base_convert(strtotime($time),10,16));
        //txSecret = MD5( KEY + streamName + txTime )
        $txSecret = md5($key.$streamName.$txTime);
        $ext_str = "?".http_build_query(array(
                "txSecret"=> $txSecret,
                "txTime"=> $txTime
            ));
    }
    return $protocol."://".$domain."/live/".$streamName.'.flv' . (isset($ext_str) ? $ext_str : "");
}
哦,差点忘了,还有一个有效时间的问题,就是示例代码中的过期时间参数,一定要注意这个地方,如果你的推、拉流地址的有效时间过期了,那即使你推成功了,也会便秘(拉不出来),所以,这个时间的设定,一定得注意,你可以将这个有效时间设为明天,后天,或者,一个月后、一年后的今天,都是无所谓的,只要不过期,就OK。

当然,没必要那么长,当主播登录APP的时候,你就这个有效时间设置为当前时间加一天,就可以了,因为,没有哪个主播,可以牛逼到一开直播就二十多个小时,因为,直播这玩意,相当考验手机的性能,别说是二十个小时,你就是搞俩小时,都烫到你爆炸!开个玩笑,当然,也没这么夸张。但,开直播时间长了,手机确实很烫,不信,你可以体验一下试试!

以上,就是关于推拉流地址生成的相关内容,因为每个主播在直播的时候必须要这个东西,所以,提前准备好了!好了,这块就没什么其他要说的了。接下来,进入主题!

表的设计

对于直播而言,一个主播,对应一个直播间,也就是说,主播从他注册进入到APP,到他离开平台,他的直播间ID始终是唯一的,即直播间ID与播主的用户ID终生绑定。

因此,我们也可以将直播间间表,看作是一个配置表,主播和直播间的对应关系,以及直播间与流名称的对应关系配置表,为什么这么说呢,因为,你到后面,你就会发现,直播间表,一旦初始化,剩下的,除了直播主题和直播封面,以及直播状态会实时变化,外加状态同步流数据的时候会实时处理外,其他,就没什么变化了。

对了,这里,有一个点,要着重说一下,那就是直播间对应的流名称,这个东西,是做什么用的呢?说白了,它就是你业务库表数据与腾讯云数据的关系衔接点,你要确保从腾讯云的直播流中找到你直播间对应的ID,就得通过这个流名称,因此,这东西,你得存起来,当然,如果你直接用直播间的ID也可以,其实,是一个意思,换句话说,你的直播间ID是1005,那么,你的流名称里,也应该有1005相关的字样,比如“live_stream_1005”,这玩意说白了,就是为了方便你匹配,当然,存不存随你,但是,一定得跟直播间ID关联。

这里,需要说一下,可能有的人看表里的字段,貌似是用来存储推拉流地址的,是的,我一开始也是这么考虑的,但到后来发现,存整个地址,完全没有必要,因为,不同环境,域名不同,你得去配,还涉及个有效时间,这玩意,又不是直播间ID,生下来(第一次)是啥样,以后就是啥样,不是!但,流是啊,就跟直播间ID一样,打娘胎里,就定了!因此,我们这里最后,选择只存流名称,类似“teacher_live_1005”,这样,通过它,随用随生,即可。

因此,我们先设计直播间表,大致设计如下:
/*表: teacher_live_room*/------------------------

/*列信息*/-----------

Field                   Type              Collation           Null    Key     Default            Extra                         Comment
----------------------  ----------------  ------------------  ------  ------  -----------------  ---------------------------   -----------------------------------------------------------
clive_room_id           int(11) unsigned  (NULL)              NO      PRI     (NULL)             auto_increment                直播间ID
teacher_id                int(11)           (NULL)              NO              0                                              教练ID
clive_room_name         varchar(30)       utf8mb4_general_ci  NO                                                               直播间名称
clive_room_img          varchar(200)      utf8mb4_general_ci  NO                                                               直播封面图片
clive_room_push_url     varchar(200)      utf8mb4_general_ci  NO                                                               直播间推流地址
clive_room_play_url     varchar(200)      utf8mb4_general_ci  NO                                                               直播间播放地址
clive_room_visitor_num  smallint(5)       (NULL)              NO              0                                                观看人数
clive_room_is_top       tinyint(1)        (NULL)              NO              0                                                是否置顶:0,否;1,是
clive_room_sort         tinyint(1)        (NULL)              NO              0                                                置顶位置
clive_room_group_id     smallint(5)       (NULL)              NO              0                                                直播间讨论组ID
clive_room_create_time  timestamp         (NULL)              NO              CURRENT_TIMESTAMP                                创建时间
clive_room_update_time  timestamp         (NULL)              NO              CURRENT_TIMESTAMP  on update CURRENT_TIMESTAMP   更改时间
clive_room_status       tinyint(1)        (NULL)              NO              1                                                状态:0,禁播;1,正在直播;2,暂未直播

/*索引信息*/--------------

Table            Non_unique  Key_name  Seq_in_index  Column_name    Collation  Cardinality  Sub_part  Packed  Null    Index_type  Comment  Index_comment
---------------  ----------  --------  ------------  -------------  ---------  -----------  --------  ------  ------  ----------  -------  ---------------
teacher_live_room           0  PRIMARY              1  clive_room_id  A                   27    (NULL)  (NULL)          BTREE
紧接着,我们来说说第二个表,这里,我们叫做“场次表”。场次,是怎么个说法呢,举个简单的例子,小时候的露天电影,放电影的那个人,他放完这一场,可能还得去另一家赶另一场,而主播也是类似,他播完这一场,隔一段时间,可能又要播另一场,因此,他的每一场,就算是一个场次,一个主播可以播无数个场次,只要他精力足够。于是,就有了场次表,场次表。这个表,说白了,就是记录某个直播间的某个场次的开始、结束时间,录播时间、录播地址、录播文件大小,断流原因、断流时间,是否推流成功等信息的数据记录表。当然,根据实际业务定,即可,这里,我们就我们自己的业务,设计场次表如下:
/*表: teacher_live_room_scene*/------------------------------

/*列信息*/-----------

Field                         Type              Collation           Null    Key     Default            Extra                         Comment
----------------------------  ----------------  ------------------  ------  ------  -----------------  ---------------------------   --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
clive_rscene_id               int(11) unsigned  (NULL)              NO      PRI     (NULL)             auto_increment                直播场次自增ID
clive_room_id                 int(11)           (NULL)              NO              0                                                直播间ID
clive_rscene_name             varchar(100)      utf8mb4_general_ci  NO                                                               直播场次名称
clive_rscene_img              varchar(200)      utf8mb4_general_ci  NO                                                               封面图
clive_rscene_open_time        timestamp         (NULL)              YES             (NULL)                                           开播时间
clive_rscene_end_time         timestamp         (NULL)              YES             (NULL)                                           关播时间
clive_rscene_record_url       varchar(200)      utf8mb4_general_ci  NO                                                               录播地址
clive_rscene_is_push_success  tinyint(1)        (NULL)              NO              0                                                是否推流成功:0,否;1,是
clive_rscene_break_reason     varchar(100)      utf8mb4_general_ci  NO                                                               断流原因:1,主播端主动断流;2,主播端主动断流;3,主播端主动断开 TCP 连接;4,主播端 TCP 连接异常;7,收到流数据异常
clive_rscene_break_time       timestamp         (NULL)              YES             (NULL)                                           断流时间
clive_rscene_push_duration    int(11)           (NULL)              NO              0                                                推流时长(毫秒)
clive_rscene_sequence         varchar(30)       utf8mb4_general_ci  NO                                                               消息序列号
clive_rscene_in_people_num    smallint(5)       (NULL)              NO              0                                                总人气
clive_rscene_file_id          varbinary(20)     (NULL)              YES             0                                                点播 file ID,在点播平台可以唯一定位一个点播视频文件
clive_rscene_file_format      varchar(10)       utf8mb4_general_ci  YES                                                              录播文件格式:flv,hls,mp4,aac
clive_rscene_record_b_time    timestamp         (NULL)              YES             (NULL)                                           录播开始时间
clive_rscene_record_e_time    timestamp         (NULL)              YES             (NULL)                                           录播结束时间
clive_rscene_record_duration  smallint(5)       (NULL)              YES             0                                                录制时长,单位秒
clive_rscene_file_size        int(11)           (NULL)              YES             0                                                录制文件大小,单位字节
clive_rscene_create_time      timestamp         (NULL)              NO              CURRENT_TIMESTAMP                                创建时间
clive_rscene_update_time      timestamp         (NULL)              NO              CURRENT_TIMESTAMP  on update CURRENT_TIMESTAMP   更改时间
clive_rscene_status           tinyint(1)        (NULL)              NO              1                                                状态:-1,已结束;1,未结束

/*索引信息*/--------------

Table                  Non_unique  Key_name  Seq_in_index  Column_name      Collation  Cardinality  Sub_part  Packed  Null    Index_type  Comment  Index_comment
---------------------  ----------  --------  ------------  ---------------  ---------  -----------  --------  ------  ------  ----------  -------  ---------------
teacher_live_room_scene           0  PRIMARY              1  clive_rscene_id  A                  921    (NULL)  (NULL)          BTREE

相关逻辑

 

直播相关的表有了,那么,其中涉及的逻辑是什么呢?是不是,就是开始直播的时候,主播上传封面和主题,开始直播,然后,将直播状态改为直播中,再给场次表,创建一条场次数据,记录一下场次相关的数据,就完事了?就酱,如果你真这么想,那就把直播给想简单了。

为什么这么说呢?

还是先简单说说,直播整体的一个大概流程吧。主播打开APP,进入开启直播页面,输入封面图片和直播主题,点击“开始直播”,这个时候,APP端,会调你后端的开启直播的接口,这个时候,你要将封面图片地址和主题保存到直播间表,场次表,注意,这两个表都有封面图片和主题字段,为什么要这么处理呢?这是为了越往后,场次数据越多,你通过直播间名称就能从N多直播的场次数据中很快定位到你要找的场次。

注意,这里的直播间表的封面图片和主题字段,这两个字段仅是暂存当前直播的显示信息,而场次表中的这两个字段,是真正意义上要永久存储直播封面和直播主题的,作为以后的记录数据。另外,还得记录直播开始时间哦,因为,这个东西,跟结束时间放一块,就是考核主播业绩的关键数据,一定不能忘!

还有一点,重点哦,直播间的状态,这个时候,一定千万不要置为“开始直播”!!!是的,你没有看错,我也没有写错,可能你会一脸“为什么,难道不应该设置为‘开始直播’么”,是的,换作一开始的我,我看到这里我也会如你一般迷惑。但,此刻我只能告诉你,这是一个坑,如果你真这么做了,等到你把所有的一切都写完,等到测试的时候,你就会发现有的人的直播间表数据和场次数据总会出现异常,要么,直播间表的数据的状态为直播中,而场次表的数据,则已结束,要么,就是刚开播没几秒,明明你还在直播中,可是直播间表和场次表,状态却为“已结束”?!你怎么改,都会存在你想想不到的地方,是的,这玩意,有延迟,好了,这里不多讲,暂且记下这个疑问,我后面给你一个满意的答复。

继续,当你记录完封面和直播主题数据后,给APP端返回直播间ID和场次ID,APP端拿到这两个数据,则表明业务这边已经处理完毕,那接着,他拿到当前这个主播的推流地址,使用腾讯云的SDK,将推流地址作为一个管道就可以进行推流了,通俗点讲,就是将直播的音视频数据通过手机采集,转换成流,再推到腾讯云的服务器上,然后,用户端,通过直播列表中的数据,选择她想看的直播,注意,直播列表的每个直播间数据都带着该直播间的拉流地址,一定记得返回这个东西,否则,用户点进去,根本就看不到任何东西。直播最主要的就是这两个东西,一推一拉,就跟太极阴阳鱼一样,永远都是一对,且,每对跟每队的都不一样。这样才能确保在上百上千个直播中,每个主播所播的内容,都能被不用的用户看到,且不串播,就是你进张三的直播间,永远不会看见李四的直播,大概就是这个意思。

拆雷填坑

剩下,我们就说说上文提到的,为什么在开启直播的时候,直播间的状态,不能设置为“正在直播”,而要不作处理。不作处理,那谁来给你处理呢?答案:查询直播中的流接口。地址是:https://cloud.tencent.com/document/product/267/20472

在腾讯云控制台,你可以发现很多关于云直播的API接口,关于流的、直播回调的、转码的、鉴黄的、录播的等等,好多,这些都是服务腾讯云直播相关业务的,你可以根据实际业务需要进行挑选(传送门:https://cloud.tencent.com/document/product/267)。

为什么要通过“查询直播中的流”这个接口来处理我们自己库表中的直播状态呢?这是因为推流可能因为网络的问题出现延迟。比如说,你在开启直播的时候,你表里的直播间状态已经是“直播中”了,可实际情况可能是,因网络问题,你这个流压根就没推成功,换句话说,你在那再怎么自嗨,别人一手都看不见!是的,之所以用人家的接口,就是为了确保数据的实时同步!

可能,你觉得,我开启直播的时候,状态置为“直播中”,主播点击“结束直播”按钮的时候,让APP端调接口,将直播状态改为“直播结束”不就OK了吗?为什么,还要这么麻烦?如果你真要这么问,那我怼你一句,如果主播断网了,或者,异常关闭APP了,上哪调你的接口去?!

所以,直播流这块,现实比你想象的要复杂的多,尤其是这块,你开了,不光要确保状态,还得确保流推成功了,另外,还得确保你开播的这段时间,直播列表,永远都能看到正在直播的直播间,不能,有的人家还在播呢,你直播状态已经因为数据状态改为“结束直播”找不到了,或者,主播异常退出,但直播列表还能看到主播的直播间,结果一点进去,发现黑不隆冬的,啥都看不见。那这,就影响用户体验了,老板肯定会端杯咖啡,站你身后,默默地为你“加油鼓劲”——啥时候能搞好,整的我们的产品都没人用了,能不能快点!?啊!

那么,这块究竟要怎么搞呢?其实,我一开始,就是掉坑里了,后端通过接口处理直播间表状态和场次状态,结果,弄了个难看,直播过程中,各种问题,有的明明在直播,结果,直播列表看不见了,而有的用户还在直播间,教练却异常退出了,反正各种问题,我这边是接口也改状态,调流接口的定时任务也在改,断流回调也在处理,导致整个一团乱麻,老板急眼了,搞的我也差点就晾菜刀了!总之,一言难尽。

那么,这块的方案究竟是怎样的呢?一句话,直播间的状态,接口别管,让“查询直播中的流接口”来处理!这个接口,获取的是腾讯云上正在直播的流列表,也就是说它返给你的事一组数据,这组数据里的流都是正在直播的,你通过流匹配到直播间,在这个流列表当中的直播间,你就将它们的状态改为“正在直播”,不在这个列表当中的直播间,将它们的状态改为“直播结束”。另外,流接口中没有返回数据的,你还要去过滤一遍直播间表,将所有的直播间状态改为“直播结束”,即可。这里需要说明的一点,是你直播列表查询的时候,只通过“直播间表”的状态来判断就行,千万不要关联上场次表的状态,一定记住,场次表的角色,它就是个记录员,端端菜可以,上桌吃,就算了!

以上是正常情况的处理,别忘了,还有异常退出的。你正常结束的,我可以通过接口记录场次的直播结束时间,但是,异常退出,就不行了,可能有的人说,使用“断流回调”不就可以了吗,那我问你,触发断流了,就真的结束了吗?恐怕不是吧,主播重新发起直播,那流有接上了,她再继续接着直播一小时,那你一小时前通过断流就给人家记录直播结束时间了,合适么?确定她不会找你拼命?!人家这可是用生命在工作啊!而且,还有一个问题,腾讯云的“断流回调”消息通知是不可靠的,文档里说,只要你不返回{“code”:0},它就会发每隔60秒请求一次,并且一共请求3次,然而,事实是,丫就回调一次,再也没后文了,你能怎么办,你能拿人家怎么办?

所以,异常退出的解决方案,就是使用定时任务,用直播间表关联场次表,去查询“当天”“直播已结束”“场次结束时间为空”的直播间ID、场次ID、直播间更改时间,然后,将直播间的更改时间,作为异常退出没有结束时间的场次数据的直播结束时间,批量去更改,即可。

行了,关于直播表设计,以及相关的逻辑,就暂且说到这里,至于“直播中的流”接口,下篇再说,那里,还有道道呢!

The END!                                                                                    2020/4/16 00:25:55

-----------------------------------------------------------------------------------------------
附加:
腾讯云文档中心:https://cloud.tencent.com/document/product
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章