??此文為系列文章,此系列主要講解RTSP客戶端的拉流及播放,文章持續更新,會從rtsp的基本協議講起,如何一步步實現音視頻的拉流過程,包括一系列涉及到的協議,rtsp,sdp, rtp(本系列文章的核心內容會放在rtp協議,會重點介紹講解rtp負載部分), rtcp, 從rtp解析aac,h264數據幀,得到幀后如何交給解碼庫(ffmpeg,libVLC,live555等)進行解碼,音視頻同步并播放音頻和視頻,如果內容涉及到TCP和UDP網絡通信內容,可以參考:
【Linux編程】一個基于 C++ 的 TCP 客戶端異步(epoll)框架(一))
【Linux編程】TcpServer 類的設計與實現:構建高性能的 TCP 服務器(二)
【Linux編程】C++ UDP的UdpClient 類詳解與網絡通信實現(三))
文章目錄
- 1.RTP協議基礎
- 1.1 RTP協議概述
- 1.2 RTP頭部結構
- 2. RTP負載:音頻AAC封裝模式
- 2.1 單幀模式
- 2.2 多幀模式
- 2.3. 分片模式
- 3.RTP負載:音頻AAC封裝格式
- 3.1 AU-headers-length
- 3.2 AU-headers
- 3.2.1 AU-size
- 3.2.2 AU-index/AU-index-delta
- 3.2.3 CTS-flag
- 3.2.4 CTS-delta
- 3.2.5 DTS-flag
- 3.2.6 DTS-delta
- 3.2.7 RAP-flag
- 3.2.8 Stream-state
- 3.3 AAC音頻數據
- 4. ADTS
- 4.1 AAC與ADTS的關系
- 4.2 ADTS 結構
- 4.2.1 ADTS 頭部詳解
??本篇文章是介紹關于RTSP拉流過程內容,如果涉及到推流協議的了解,請閱讀:
基于FFmpeg進行rtsp推流協議分析過程(詳細教程)(全息講解)
1.RTP協議基礎
1.1 RTP協議概述
??RTP(Real-time Transport Protocol)是一種用于網絡上傳輸實時數據(如音頻、視頻等)的協議,廣泛應用于流媒體、視頻會議、VoIP等場景。RTP協議的核心目標是為實時數據的傳輸提供時間戳、序列號等信息,以便接收端能夠正確地重組數據流,保證數據的實時性和順序性。RTP協議不保證數據的可靠傳輸,通常與RTCP(RTP Control Protocol)配合使用,以實現流量控制、擁塞控制等功能。
1.2 RTP頭部結構
??RTP頭在前面的文章中講解了,RTP頭部是RTP協議的關鍵組成部分,它包含了多個字段,用于描述RTP數據包的特征和傳輸狀態。RTP頭部的結構如下表所示:
2. RTP負載:音頻AAC封裝模式
?? 根據 RFC 3640 的定義,RTP 封裝 AAC 數據時主要有以下三種方式:一個 RTP 包攜帶一個 AU、一個 RTP 包攜帶多個 AU、一個 RTP 包攜帶一個 AU 的片段。
2.1 單幀模式
?? 一個 RTP 數據包中只攜帶一個完整的 Access Unit(AU),即一個音頻幀。 每個 RTP 數據包只包含一個完整的音頻幀,不會進行分片。適用于音頻幀較小且網絡 MTU 足夠大的情況。RTP 數據包的 Marker 位(M 位)通常設置為 1,表示這是一個完整的音頻幀。
2.2 多幀模式
?? 一個 RTP 數據包中攜帶多個完整的 Access Unit(AU),即多個音頻幀。 一個 RTP 數據包可以包含多個完整的音頻幀,以提高傳輸效率。每個音頻幀的大小可以通過 AU-header 中的 AU-size 字段指定。RTP 數據包的 Marker 位(M 位)通常設置為 1,表示這是最后一個音頻幀。
2.3. 分片模式
?? 一個 RTP 數據包中只攜帶一個 AU 的一個片段,多個 RTP 數據包共同組成一個完整的 AU。一個 AU 被分割成多個片段,每個片段攜帶在不同的 RTP 數據包中。所有分片的 RTP 數據包具有相同的時間戳,但序列號不同。最后一個分片的 RTP 數據包的 Marker 位(M 位)設置為 1,表示這是該 AU 的最后一個分片。適用于音頻幀較大且網絡 MTU 較小的情況,避免因音頻幀過大而導致的 IP 碎片化。
3.RTP負載:音頻AAC封裝格式
3.1 AU-headers-length
?? AU-headers-length 是在 RTP 封裝 AAC 音頻數據時的關鍵字段。它位于 RTP 載荷的起始位置,緊跟在 RTP 頭部之后。該字段的長度固定為 2 字節,其值以 bit 為單位,表示后續所有 AU-header(Audio Unit Header,音頻單元頭)的總長度。由于每個 AU-header 的長度固定為 16 bit,因此 AU-headers-length 的值總是 16 的倍數。這一特性使得接收端能夠快速準確地解析出后續的 AU-header 數量和位置。
?? 例如,當一個 RTP 包中只包含一個 AU-header 時,AU-headers-length 的值為 16;若包含兩個 AU-header,則其值為 32,以此類推。通過這種方式,接收端可以根據 AU-headers-length 的值,輕松計算出后續 AU-header 的數量,即 AU-headers-length 的值除以 16。這種設計不僅提高了數據解析的效率,還確保了數據傳輸的靈活性,允許一個 RTP 包中可以包含多個音頻單元(Audio Unit),從而適應不同的音頻編碼和傳輸需求。在實際應用中,AU-headers-length 字段的值對于接收端正確解析 RTP 包中的音頻數據至關重要。接收端首先讀取該字段的值,確定后續 AU-header 的總長度,進而可以準確地提取出每個 AU-header 的內容。每個 AU-header 包含了音頻單元的大小、索引等信息,這些信息對于后續音頻數據的解碼和播放具有重要意義。因此,AU-headers-length 字段在 RTP 封裝 AAC 數據的過程中,起到了橋梁的作用,連接了 RTP 頭部和音頻數據,確保了數據傳輸的完整性和準確性。
3.2 AU-headers
?? AU-headers(Access Unit Headers)是用于音頻編碼數據流中的頭部信息,主要在音頻數據的傳輸和解析過程中發揮重要作用。在音頻編碼標準中,AU-headers通常用于標識音頻數據的起始位置、長度、時間戳等關鍵信息,以便接收端能夠正確地解析和播放音頻流。AU-headers是音頻數據流中的一個組成部分,它位于音頻幀的前面,用于描述音頻幀的相關信息。根據不同的音頻編碼標準,AU-headers的結構和內容可能會有所不同。例如,在AAC(Advanced Audio Coding)編碼中,AU-headers通常包含音頻幀的長度、索引等信息,這些信息需要SDP內容來確定。
在此之前放一個SDP示例:
v=0
o=- 0 0 IN IP4 127.0.0.1
s=Stream
c=IN IP4 0.0.0.0
t=0 0
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAKKzZQHgGf58BagICAoAAAAMAgAAAGQeMGMs=,aO+CPLA=; profile-level-id=640028
a=control:trackID=0
m=audio 0 RTP/AVP 97
a=rtpmap:97 mpeg4-generic/48000/2
a=fmtp:97 profile-level-id=1; mode=AAC-hbr; sizelength=13; indexlength=3; indexdeltalength=3; config=119056e500
a=control:trackID=1
注: SDP來自于RTSP請求信息的DESCRIBE
方法。
3.2.1 AU-size
?? 表示音頻幀的長度(以字節為單位)。該字段的長度由SDP(Session Description Protocol)中的sizeLength
參數決定,sizeLength
的單位為bit。例如,sizeLength
= 13,表示AU-size字段占用13位,能夠表示的最大音頻幀長度為8191字節。這一字段對于接收端正確讀取音頻幀的大小至關重要。
3.2.2 AU-index/AU-index-delta
?? 用于標識 序列號。AU-Index 出現在第一個 AU-header 中,而 AU-Index-delta 出現在后續的 AU-header 中,表示相對于前一個 AU 的序列號差值
?? AU-index表示音頻幀在整個音頻流中的序號。該字段的長度由SDP中的indexLength
參數決定。在某些情況下,AU-index字段可能用于標識音頻幀的順序,特別是在音頻流中包含多個音頻幀的情況下。例如,當一個RTP包中包含多個音頻幀時,AU-index字段可以幫助接收端對音頻幀進行排序和管理。例如,indexlength
= 3 時,表示AU-index字段占用3位數據長度。
?? AU-index-delta表示音頻幀序號的增量。該字段的長度由SDP中的indexDeltaLength
參數決定。在某些音頻編碼標準中,AU-index-delta字段用于表示當前音頻幀序號與前一個音頻幀序號的差值。這種表示方式可以節省一些空間,特別是在音頻幀序號變化較小的情況下。
3.2.3 CTS-flag
?? 表示是否包含 CTS-delta 字段。值為 1 表示存在 CTS-delta 字段,值為 0 表示不存在,該字段由SDP 中的 CTSDeltaLength
參數定義了 CTS-delta 字段的位數,如果 CTSDeltaLength
為 0或不存在,則表示 CTS-delta 字段不存在。
3.2.4 CTS-delta
?? 表示 Composition Time Stamp (CTS) 的偏移量,相對于 RTP 頭部的時間戳,該字段的存在與否由 CTS-flag 字段決定,而 CTS-flag 的存在與否由 CTSDeltaLength
參數決定。
3.2.5 DTS-flag
?? 表示是否包含 DTS-delta 字段。值為 1 表示存在 DTS-delta 字段,值為 0 表示不存在, 該字段由SDP 中的 DTSDeltaLength
參數定義了 DTS-delta 字段的位數。如果 DTSDeltaLength
為 0,則表示 DTS-delta 字段不存在。
3.2.6 DTS-delta
?? 表示 Decoding Time Stamp (DTS) 的偏移量,相對于 CTS,該字段的存在與否由 DTS-flag 字段決定,而 DTS-flag 的存在與否由 DTSDeltaLength
參數決定。
3.2.7 RAP-flag
?? 表示該 Access Unit 是否為隨機訪問點。值為 1 表示是隨機訪問點,值為 0 表示不是,該字段由SDP 中的 randomAccessIndication
參數定義了 RAP-flag 字段的存在與否。值為 1 表示存在 RAP-flag 字段,值為 0 表示不存在。
3.2.8 Stream-state
?? 表示 MPEG-4 系統流的狀態,每個狀態由一個模數計數器的值標識。該字段由SDP 中的 streamStateIndication
參數定義了 Stream-state 字段的位數。如果 streamStateIndication
為 0或不存在,則表示 Stream-state 字段不存在。
3.3 AAC音頻數據
?? 之后就是裸的AAC音頻壓縮流了,RTP負載中封裝AAC音頻數據主要使用兩種格式:ADTS和LATM。ADTS格式較為簡單,但封裝開銷較大,而LATM格式更加高效,適合低延遲和低帶寬的實時傳輸應用。在實際應用中,根據網絡帶寬、延遲要求和音頻質量需求的不同,選擇適當的封裝格式是關鍵。但是在封裝RTP協議時,需要特別注意的是,在封裝AAC音頻時,往往去掉了ADTS信息的,解析時得到的是裸的AAC音頻壓縮流,我們在解碼時需要將ADTS還原。
4. ADTS
4.1 AAC與ADTS的關系
?? ADTS(Audio Data Transport Stream,音頻數據傳輸流)是AAC音頻的一種封裝格式,主要用于音頻數據的傳輸。ADTS為每幀AAC音頻數據添加了一個頭部信息,該頭部包含了音頻數據的編碼參數和幀的長度等信息。
?? 在實際應用中,AAC音頻數據通常被封裝在ADTS格式中進行傳輸。發送端將AAC音頻數據編碼成ADTS格式,接收端則解析ADTS頭部信息,提取AAC音頻數據進行解碼。這種封裝方式使得AAC音頻能夠在各種網絡環境中高效、可靠地傳輸。
4.2 ADTS 結構
?? ADTS(Audio Data Transport Stream)是AAC(Advanced Audio Coding)的一種常見傳輸流格式,通常用于封裝AAC音頻數據,使其適合在網絡上傳輸。ADTS幀由兩部分組成:
- ADTS頭部(Header):包含音頻的采樣率、聲道數、幀長度等信息。
- AAC音頻數據(Payload):實際的AAC音頻編碼數據。
4.2.1 ADTS 頭部詳解
?? ADTS頭部通常為7個字節(當存在CRC校驗時為9個字節)。千字文,不如表格來的直接: