視頻直播技術(shù)干貨(十三):B站實時視頻直播技術(shù)實踐和音視頻知識入門

本文由B端技術(shù)中心資深開發(fā)工程師馬家憶分享,原題“B站在實時音視頻技術(shù)領(lǐng)域的探索與實踐”,下文進行了排版和內(nèi)容優(yōu)化。

1、引言

直播行業(yè)從傳統(tǒng)的娛樂直播發(fā)展到教育直播、電商直播等形式,產(chǎn)生了很多新的玩法。傳統(tǒng)的直播是一位主播展示才藝,觀眾通過彈幕、送禮物等方式進行互動。隨著網(wǎng)絡質(zhì)量不斷地提高,用戶也對直播平臺產(chǎn)生的新的要求,實時互動直播的場景就出現(xiàn)了,觀眾可以同時觀看多位主播之間互動的畫面,讓直播間的氣氛更好。B站直播的連麥PK、視頻連線業(yè)務就提供了這個能力。主播看到的是對方主播實時的流(延遲400ms以內(nèi)),而觀眾看到的是“準實時”的流(延遲2~5s左右)。

本文講述搭建這樣一套最新流行的實時視頻直播系統(tǒng)需要了解的背景知識以及系統(tǒng)的整體架構(gòu),希望對大家有幫助。

2、系列文章

本文是系列文章中的第?13?篇,本系列總目錄如下:

視頻直播技術(shù)干貨(一):揭秘百萬級粉絲互動的Facebook實時視頻直播

視頻直播技術(shù)干貨(二):P2P技術(shù)如何將實時視頻直播帶寬降低75%?

視頻直播技術(shù)干貨(三):實時直播答題系統(tǒng)的實現(xiàn)思路與技術(shù)難點分享

視頻直播技術(shù)干貨(四):首次披露快手是如何做到百萬觀眾同場看直播仍能秒開且不卡頓的?

視頻直播技術(shù)干貨(五):七牛云使用QUIC協(xié)議實現(xiàn)實時視頻直播0卡頓

視頻直播技術(shù)干貨(六):新浪微博實時直播答題的百萬高并發(fā)架構(gòu)實踐

視頻直播技術(shù)干貨(七):實時視頻直播首屏耗時400ms內(nèi)的優(yōu)化實踐

視頻直播技術(shù)干貨(八):淘寶高清、低延時的實時視頻直播技術(shù)解密

視頻直播技術(shù)干貨(九):千萬級直播系統(tǒng)后端架構(gòu)設計的方方面面

視頻直播技術(shù)干貨(十):一文讀懂主流視頻直播系統(tǒng)的推拉流架構(gòu)、傳輸協(xié)議等

視頻直播技術(shù)干貨(十一):超低延時視頻直播技術(shù)的演進之路

視頻直播技術(shù)干貨(十二):從入門到放棄,快速學習Android端直播技術(shù)

視頻直播技術(shù)干貨(十三):B站實時視頻直播技術(shù)實踐和音視頻知識入門》(* 本文

3、關(guān)于作者

馬家憶:B端技術(shù)中心資深開發(fā)工程師。

4、實時音視頻關(guān)鍵技術(shù)概述

從0到1搭建一套實時音視頻系統(tǒng)并支撐現(xiàn)有的業(yè)務,如果沒有接觸過這方面的東西會感覺無從下手。

我們可以看到,1996年IETF就推出了RTP協(xié)議用于實時音視頻傳輸,2011年Google推出了WebRTC用于網(wǎng)頁端實時音視頻通話(見《了不起的WebRTC:生態(tài)日趨完善,或?qū)崟r音視頻技術(shù)白菜化》)。

從這些現(xiàn)有的協(xié)議和項目中,我們可以發(fā)現(xiàn)實時音視頻技術(shù)的關(guān)鍵點,評估自身現(xiàn)有的基礎(chǔ)組件支持情況并結(jié)合業(yè)務場景尋找適合自己的解決方案。

5、關(guān)鍵技術(shù)1:傳輸協(xié)議

RTP協(xié)議(Real-time Transport Protocol)定義了在互聯(lián)網(wǎng)上傳輸實時音視頻數(shù)據(jù)的標準格式,屬于應用層協(xié)議。RFC 3550描述RTP協(xié)議的傳輸層主要使用UDP,RFC 4571描述了RTP協(xié)議的TCP傳輸方式。

在我們的實時音視頻場景中應當優(yōu)先選擇UDP,理由如下:

1)TCP保證數(shù)據(jù)流的可靠性和順序性。TCP的超時重傳策略為了保證通用和公平,相對比較保守,重傳超時時間(RTO)可能會變的很大。假如中途丟失一個包,后續(xù)的包即使先到達也要緩存起來等待重傳完成以后才能送到應用層。在網(wǎng)絡狀況不佳的情況下,使用TCP傳輸會產(chǎn)生較大的延遲;

2)UDP允許數(shù)據(jù)包丟失、亂序和重復。即使數(shù)據(jù)丟失也不會阻塞接收緩沖區(qū)等待重傳,這就為實時性提供了保障。在上層的RTP協(xié)議中,協(xié)議頭部包含了時間戳和序列號,可以對數(shù)據(jù)包進行重排和丟棄,解決了亂序和重復的問題。如果接收端監(jiān)測到丟包,并且丟失的包是必要的且無法恢復,則發(fā)送NACK消息通知發(fā)送端重傳(下一節(jié)會詳細探討這個話題)。

UDP雖然在低延遲領(lǐng)域上有壓倒性的優(yōu)勢,但是用戶側(cè)有可能存在防火墻攔截所有的UDP包??紤]到在網(wǎng)絡環(huán)境足夠好的情況下使用TCP也能達到不錯的效果,因此我們做了一個降級策略,優(yōu)先使用UDP,當且僅當UDP不通的時候使用TCP。

6、關(guān)鍵技術(shù)2:丟包補償

前面講到我們的傳輸層協(xié)議優(yōu)先選擇UDP,那么就需要引入一些機制解決丟包問題。

前向糾錯FEC(Forward Error Correction)指的是發(fā)送端發(fā)送原始數(shù)據(jù)的同時附加部分冗余的信息,如果接收端檢測到原始數(shù)據(jù)丟失則嘗試使用冗余的信息進行恢復。發(fā)送端發(fā)送n個數(shù)據(jù)包,同時根據(jù)原始數(shù)據(jù)生成k個冗余的數(shù)據(jù)包,將n+k個數(shù)據(jù)包發(fā)送出去,接收端只要收到至少n個數(shù)據(jù)包就可以得到全部的原始數(shù)據(jù)。

FEC算法的關(guān)鍵在于異或。異或(Exclusive OR)是一個數(shù)學運算符,數(shù)學符號為“⊕”,兩邊數(shù)值轉(zhuǎn)換成二進制按位運算,相同時為0,不同時為1。

以一階冗余算法為例,n個數(shù)據(jù)包生成1個冗余包,發(fā)送n+1個數(shù)據(jù)包。我們發(fā)送三個數(shù)值分別為a、b、c,生成冗余數(shù)據(jù)x=a ⊕b ⊕ c一起發(fā)送。假如數(shù)值b在傳輸中丟失了,計算a ⊕c ⊕ x即可得到b。

在實際應用中,F(xiàn)EC沒有這么簡單,WebRTC實現(xiàn)了UlpFEC和FlexFEC,UlpFEC可以針對數(shù)據(jù)包的重要程度實施不同程度的保護以充分利用帶寬,F(xiàn)lexFEC還支持對列做冗余,同時WebRTC默認的音頻編碼器Opus本身就支持FEC。

前向糾錯適合少量隨機丟包的場景,可以無視網(wǎng)絡延遲時間,但是增加了帶寬消耗。

后向糾錯包括ARQ(Automatic Repeat Request)和PLC(Packet Loss Concealment)。ARQ指的是接收端檢測到數(shù)據(jù)丟失的時候發(fā)送NACK報文請求發(fā)送端重傳,適合突發(fā)大量丟包的場景,沒有額外的帶寬消耗,但是時效性取決于RTT,如果存在很多接收端還要考慮避免NACK風暴造成雪崩。PLC用于音頻,當數(shù)據(jù)缺失時使用模型根據(jù)前后數(shù)據(jù)預測丟失的數(shù)據(jù)。

總之,前向糾錯和后向糾錯各有優(yōu)缺點,需要搭配使用。

7、關(guān)鍵技術(shù)3:流量控制

流量控制指的是根據(jù)網(wǎng)絡狀況的波動估算可用帶寬,根據(jù)帶寬的變化自動調(diào)節(jié)音視頻碼率和發(fā)送速率。當網(wǎng)絡質(zhì)量變差的時候迅速降低數(shù)據(jù)量以確保實時性,網(wǎng)絡較好時則慢慢提升數(shù)據(jù)量帶來更清晰的畫面。在WebRTC中提供了優(yōu)秀的Google Congestion Control算法,包括基于延遲的評估和基于丟包率的評估,取兩種評估方式的最小值作為目標帶寬通知編碼器和數(shù)據(jù)發(fā)送模塊。

基于延遲的評估算法包括Transport-CC和Goog-REMB,目前最新版的WebRTC默認使用的是Transport-CC。Transport-CC在發(fā)送端進行帶寬評估,接收端通過TransportFeedback RTCP包向發(fā)送端反饋每個RTP包的到達時間,發(fā)送端在一個時間窗口內(nèi)計算每個RTP包到達時間與發(fā)送時間之差,通過Trendline濾波器處理后預測網(wǎng)絡狀況。假設我們當前處于Hold狀態(tài),如果檢測到網(wǎng)絡狀態(tài)為OverUse,此時應該減小數(shù)據(jù)量,變更為Decrease狀態(tài);如果檢測到網(wǎng)絡狀態(tài)為Normal,此時可以嘗試增加數(shù)據(jù)量,變更為Increase狀態(tài)。

基于丟包的評估算法是當網(wǎng)絡突發(fā)大量丟包時的兜底策略:

1)如果丟包率在2%以下的時候說明網(wǎng)絡質(zhì)量好,目標帶寬增加8%;

2)如果丟包率在在2%~10%說明當前發(fā)送數(shù)據(jù)的帶寬和網(wǎng)絡質(zhì)量相匹配可以保持不變;

3)如果丟包率大于10%說明網(wǎng)絡質(zhì)量差,目標帶寬減小到(1-丟包率*0.5)* 當前帶寬。

8、關(guān)鍵技術(shù)4:數(shù)據(jù)緩沖

如果我們只考慮實時性,那么收到數(shù)據(jù)就立刻解碼并渲染必然是最好的選擇,但是網(wǎng)絡并不穩(wěn)定,延遲、亂序、丟包、重復包都有可能發(fā)生。如果采用上面的策略,音頻可能因為網(wǎng)絡的抖動變的斷斷續(xù)續(xù),視頻可能因為丟包導致缺少參考幀從而出現(xiàn)黑屏或花屏,所以有必要引入一個緩沖區(qū),增加一點可以接受的延遲來保證用戶體驗。

在WebRTC中,視頻包會被放入JitterBuffer模塊進行處理,JitterBuffer會進行視頻包的排序、組裝成完整的幀、確保參考幀有效,然后把數(shù)據(jù)送到解碼器。同時,根據(jù)網(wǎng)絡狀況自適應地調(diào)節(jié)緩沖區(qū)的長度。音頻包會被放入NetEQ中,它維護了音頻的緩沖區(qū),同時負責將音頻同步到視頻。我們做播放器一般都是以音頻的時間為基準同步視頻,但是WebRTC剛好相反,它是以視頻為基準的。當音頻數(shù)據(jù)堆積的時候加速音頻播放,音頻數(shù)據(jù)不足的時候降低速度把音頻拉長。

9、關(guān)鍵技術(shù)5:回聲消除

在語音通話的場景中,麥克風采集到的聲音發(fā)送給遠端,遠端的揚聲器播放出來以后又被遠端的麥克風采集到這個聲音并傳送回來,這樣講話的人會感覺到有回聲,影響體驗。

WebRTC提供了回聲消除算法AEC,時延估計(Delay Estimation)模塊找到揚聲器信號和麥克風信號的時延,線性自適應濾波器(Linear Adaptive Filter)參考揚聲器信號估算回聲信號并將其剪去,最后通過非線性處理(Nonlinear Processing)模塊消除殘留的回聲。

10、關(guān)鍵技術(shù)6:最優(yōu)路徑

實時音視頻對網(wǎng)絡的要求非常高,如果通話雙方距離很遠,那么通話質(zhì)量是很難保證的。城市A的設備給城市D的設備發(fā)送數(shù)據(jù),直接發(fā)送未必是最優(yōu)的選擇,從城市B和城市C中轉(zhuǎn)一下有可能更快。

理想的解決方案是在全球部署加速節(jié)點,用戶就近接入。根據(jù)加速節(jié)點之間的實時網(wǎng)絡質(zhì)量探測數(shù)據(jù),找到一條最優(yōu)傳輸路徑,避開網(wǎng)絡的擁堵。

11、開源音視頻框架WebRTC簡述

剛才介紹了實時音視頻系統(tǒng)實現(xiàn)過程中所需的關(guān)鍵技術(shù),多次提到了WebRTC。顯然,對于絕大多數(shù)團隊來說,這些內(nèi)容如果全部自主研發(fā)幾乎是不可能的事情,而我們站在WebRTC的基礎(chǔ)上去設計自己的系統(tǒng)是較為明智的選擇。

WebRTC的代碼非常復雜,想要把它搞清楚是一件非常困難的任務,我第一次看到WebRTC的代碼根本就不知道從哪里下手。

幸運的是,WebRTC官方提供了架構(gòu)圖,可以先幫助我們對它進行一個宏觀的了解。

WebRTC整體架構(gòu)大概可以分為接口層、會話層、引擎層和設備I/O層:

1)接口層包括Web API和WebRTC C++ API,Web API給Web開發(fā)者提供了JavaScript接口,這樣Web端就具備了接入WebRTC的能力;WebRTC C++ API面向的是瀏覽器開發(fā)者,讓瀏覽器開發(fā)商具備集成WebRTC的能力。當然,WebRTC C++ API也可以用于Native客戶端接入;

2)會話層主要包含信令相關(guān)的邏輯,比如媒體協(xié)商,P2P連接管理等;

3)引擎層是WebRTC最核心的功能,包括音頻引擎、視頻引擎和傳輸模塊。音頻引擎包含音頻編解碼器(Opus)、NetEQ和著名的3A(回聲消除、自動增益、降噪)算法;視頻引擎包括視頻編解碼器(VP8、VP9、H264)、JitterBufer和圖像增強(降噪)算法;傳輸模塊包含SRTP、多路復用和P2P模塊;

4)設備I/O層主要和硬件交互,包括音視頻采集和渲染,以及網(wǎng)絡I/O。

上面一節(jié)描述的實時音視頻關(guān)鍵技術(shù)中,WebRTC實現(xiàn)了除“最優(yōu)路徑”之外的全部內(nèi)容。WebRTC幾乎每個模塊都是可以按需替換的,便于我們增加定制的內(nèi)容。我們可以根據(jù)實際需求決定如何使用WebRTC,Native客戶端可以通PeerConnection接口接入,服務端拿到RTP/RTCP包以后完全可以直接調(diào)用引擎層處理拿到最終的YUV和PCM數(shù)據(jù),甚至只把WebRTC內(nèi)部模塊摳出來用在自己的系統(tǒng)上也是沒問題的。

更多相關(guān)資料可閱讀:

零基礎(chǔ)入門:基于開源WebRTC,從0到1實現(xiàn)實時音視頻聊天功能

實時音視頻入門學習:開源工程WebRTC的技術(shù)原理和使用淺析

零基礎(chǔ)快速入門WebRTC:基本概念、關(guān)鍵技術(shù)、與WebSocket的區(qū)別等

12、B站視頻直播系統(tǒng)架構(gòu)

我們回到B站的連麥PK業(yè)務場景,兩位主播進行互動PK,同時大量觀眾在直播間觀看PK的過程。

顯然,兩位主播通話要求低延遲,必須使用實時音視頻系統(tǒng)交互;而觀眾觀看直播的延遲要求相對沒那么嚴格,可以采取傳統(tǒng)直播的模式,通過RTMP或者SRT推流到CDN,用戶從CDN拉流。

然后我們要思考兩個問題。

12.1 問題1:主播之間的音視頻通話是采用P2P還是服務器中轉(zhuǎn)?

首先對P2P和服務器中轉(zhuǎn)兩種方案做個對比:

對于P2P方案來說,只需要部署STUN和TURN服務器,如果成功建立P2P連接那么后續(xù)媒體數(shù)據(jù)傳輸就不需要經(jīng)過服務器,所以具有成本優(yōu)勢。然而,P2P的缺點也很明顯,如果打洞失敗還是需要TURN服務器中轉(zhuǎn),且建立連接的過程耗時較高,用戶之間距離較遠的情況下網(wǎng)絡質(zhì)量不可控,而且現(xiàn)有的第三方網(wǎng)絡加速服務基本上都不支持P2P。

我們這里選擇服務器中轉(zhuǎn)的方案,因為實時音視頻本身對網(wǎng)絡的要求比較高,不會設置過高的碼率,所以網(wǎng)絡傳輸?shù)臄?shù)據(jù)量是可控的,成本能夠接受。而且我們的實時音視頻數(shù)據(jù)要對接AI審核,還要實現(xiàn)服務器混流,這是P2P方案做不到的。

12.2 問題2:推送給觀眾的流到CDN,這個工作放在主播客戶端還是服務器?

兩位主播PK對應的是兩路流,觀眾只從CDN拉一路流,所以必須有個地方做混流。這里的混流指的是把兩位主播的視頻進行拼接、音頻進行混合,然后打包成一路流。主播客戶端能收到對方的流,可以和自己的流做混流;在前面提到的服務器中轉(zhuǎn)方案中,服務器有雙方的流,同樣也可以完成混流。

我們先對比一下兩種方案的優(yōu)劣:

服務器進行混流需要先解碼再編碼,這需要消耗大量計算資源,所以成本很高;主播客戶端進行混流需要額外增加一路流的編碼和上傳,對設備性能和上行帶寬來說也是很大的挑戰(zhàn)。

主播客戶端需要等待服務器把對方的流發(fā)送過來才能混流,所以從延遲的角度來看服務器混流稍微占據(jù)優(yōu)勢,不過這個延遲相比CDN的延遲可以忽略不計。如果后期對通話質(zhì)量要求變高,主播的設備性能和上行帶寬跟不上,我們可以很容易增加服務器來擴展計算資源和帶寬,所以在可擴展性方面服務器混流勝出。

另外,當主播從正常直播切換到連麥PK狀態(tài)的時候,采用服務器混流必須先把直播的流停掉再由服務器接管,中間的時間差可能會產(chǎn)生卡頓或黑屏影響觀眾體驗,而主播客戶端混流可以做到無縫切換。

所以,這兩種方案各有優(yōu)缺點,我們采取折衷的辦法:如果主播的設備負載較低且上行帶寬比較充足,優(yōu)先采用主播客戶端混流的方式,否則降級為服務器混流。

12.3 開始架構(gòu)設計

上面兩個問題分析清楚了,就可以開始設計了。

這是我們的系統(tǒng)整體架構(gòu):

rtc-service主要提供信令、頻道管理、主播管理、公有云上媒體服務器集群的健康檢查和節(jié)點分配、同步主播狀態(tài)到業(yè)務服務器、記錄通話流水。

rtc-job是對rtc-service的補充,定期檢查當前在線主播的狀態(tài),發(fā)現(xiàn)主播異常下線時觸發(fā)兜底邏輯。

rtc-router負責收發(fā)主播的音視頻數(shù)據(jù)。主播可以收到同一個頻道內(nèi)其他人的音視頻流。如果需要服務器混流,則訪問注冊中心并采用Google的有界負載一致性哈希算法(Consistent Hashing with Bounded Loads)選取rtc-mixer節(jié)點,并往對應節(jié)點推送主播的音視頻流。

rtc-mixer負責混流,根據(jù)需求拼接畫面和混音,然后推送到CDN,觀眾通過CDN拉流。

主播的客戶端并沒有直接向rtc-router發(fā)送數(shù)據(jù),而是通過第三方的四層加速網(wǎng)絡轉(zhuǎn)發(fā)。我們前面提到了“最優(yōu)路徑”的概念,第三方的四層加速服務可以讓用戶接入最近的加速節(jié)點,然后尋找最優(yōu)路徑把數(shù)據(jù)轉(zhuǎn)發(fā)到我們的公有云節(jié)點??蛻舳酥荒芸吹降谌降募铀俟?jié)點IP,看不到我們公有云媒體服務器的IP,這在一定程度上可以防止服務器遭到攻擊;其次,我們可以在保證異地多活的前提下讓公有云集群相對比較集中,節(jié)省成本。

服務的可用性和容錯性也是一個很重要的問題,假如在主播PK即將勝利的時刻服務出現(xiàn)故障,彈出"PK異常終止請重新再來",這很令人絕望。我們不僅要保證服務可用,還要盡最大可能保證服務出現(xiàn)故障時減小對用戶的影響,讓流程能夠走下去。接下來討論系統(tǒng)中每個風險模塊為了實現(xiàn)這個目標所采取的措施:

四層加速網(wǎng)絡故障。這個屬于第三方廠商提供的服務,每個廠商提供的接入方式大同小異,基本上就是附加的header有差別,所以同時對接多家廠商對客戶端來說是很容易做到的??蛻舳诉M行連通性檢查,只要存在至少一家廠商的服務可用,就不會影響業(yè)務。

公有云上的rtc-router和rtc-mixer故障。在公有云上部署服務,盡量要多廠商、多區(qū)域部署,防止單機房整體宕機。我們同樣準備了多個集群,每個集群都部署了多臺rtc-router、rtc-mixer和ZooKeeper,單個集群可以獨立工作,如果單個集群不可用或者負載達到上限則會被熔斷。核心機房的rtc-service會對公有云上的集群進行健康檢查,如果rtc-router宕機,rtc-service會通過信令通道通知客戶端切換到同集群中其他服務器,當同集群沒有可用機器時則切換集群。如果rtc-mixer宕機,rtc-router會通過ZooKeeper重新選擇一臺接管混流任務。

核心機房的rtc-service和rtc-job故障。這部分內(nèi)容和B站大部分核心服務部署在同樣的集群,復用了B站比較成熟的高可用架構(gòu)。這部分內(nèi)容可以參考其他文章,這里不再贅述。

13、本文小結(jié)

如果我們把實時音視頻技術(shù)比作一座富麗堂皇的城池,這篇文章只能帶領(lǐng)大家來到城門口。我們也不會停止探索的腳步。希望大家讀到這里能夠有所收獲,如有疏漏,歡迎批評指正。

14、參考資料

[1]?實時語音通訊的回音及回音消除概述

[2]?實時語音通訊的回音消除技術(shù)詳解

[3]?實時語音通訊丟包補償技術(shù)詳解

[4]?零基礎(chǔ),史上最通俗視頻編碼技術(shù)入門

[5]?IM實時音視頻聊天時的回聲消除技術(shù)詳解

[6]?學習RFC3550:RTP/RTCP實時傳輸協(xié)議基礎(chǔ)知識

[7]?基于RTMP數(shù)據(jù)傳輸協(xié)議的實時流媒體技術(shù)研究(論文全文)

[8]?愛奇藝技術(shù)分享:輕松詼諧,講解視頻編解碼技術(shù)的過去、現(xiàn)在和將來

[9]?零基礎(chǔ)入門:實時音視頻技術(shù)基礎(chǔ)知識全面盤點

[10]?實時音視頻面視必備:快速掌握11個視頻技術(shù)相關(guān)的基礎(chǔ)概念

[11]?零基礎(chǔ)入門:基于開源WebRTC,從0到1實現(xiàn)實時音視頻聊天功能

[12]?實時音視頻入門學習:開源工程WebRTC的技術(shù)原理和使用淺析

[13]?零基礎(chǔ)快速入門WebRTC:基本概念、關(guān)鍵技術(shù)、與WebSocket的區(qū)別等

[14]?移動端實時音視頻直播技術(shù)詳解(五):推流和傳輸

[15]?移動端實時音視頻直播技術(shù)詳解(六):延遲優(yōu)化

[16]?實時視頻直播客戶端技術(shù)盤點:Native、html5、WebRTC、微信小程序

[17]?淺談開發(fā)實時視頻直播平臺的技術(shù)要點

[18]?視頻直播技術(shù)干貨:一文讀懂主流視頻直播系統(tǒng)的推拉流架構(gòu)、傳輸協(xié)議等

(本文已同步發(fā)布于:http://www.52im.net/thread-4785-1-1.html)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容