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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章