RTP傳輸JPEG圖片到VLC實時播放(代碼)

一、環境是ubuntu

二、採集視頻。

我這裏採集的是YUYV422,然後可以通過兩種方式轉化,1.yuyv422->yuv420->jpeg->rtp->vlc(yuv420解碼)。2.yuyv422->rgb->jpeg->rtp->vlc(yuv420解碼)

V4L2編程可以參考:V4L2編程

YUYV422轉RGB參考:YUYV422TORGB

三、代碼下載

包含jpeg-9a庫,庫安裝百度就ok。

傳輸代碼看readme。

點擊這裏下載源代碼

四、封裝

jpeg參考RFC2435文檔,


Network Working Group                                       R. Pereira
Request for Comments: 2435                        TimeStep Corporation
Obsoletes: 2035                                               R. Adams
Category: Standards Track                            Cisco Systems Inc.
                                                         November 1998

針對JPEG壓縮視頻的RTP荷載格式
(RFC2435——RTP Payload Format for JPEG-compressed Video)


本備忘錄狀態 
本文檔講述了一種Internet通信的標準Internet跟蹤協議,並對其改進提出了討論和建
議。請參考最新版本的"Internet Official Protocol Standards" (STD 1) 來獲得本協議的
標準化進程和狀態,此備忘錄的發佈不受任何限制。
版權注意
版權歸因特網協會(1998)所有,保留一切權利。

摘要
本文檔描述了針對JPEG視頻流的RTP荷載格式。此種包格式針對編碼器參數基本不變化
的實時視頻流進行了優化。
本文檔是IETF下的視音頻傳輸工作組的產品。意見或建議請發到該工作組的郵件列表
[email protected]或直接發給作者。
   本備忘錄的大部分與RFC2035一致,對協議的改動見附錄D。

目  錄
1. 簡介	3
2. 術語	3
3. RTP上的JPEG	4
4. RTP/JPEG包格式	4
4.1 JPEG頭	4
4.1.1 類型特定:8比特	5
4.1.2 分段偏移: 24比特	5
3.1.3 類型: 8比特	5
3.1.4 Q: 8比特	5
3.1.5 寬度: 8比特	5
3.1.6 高度: 8比特	5
3.1.7 復位標記頭	6
3.1.8 量化表頭	6
3.1.9 JPEG荷載	7
4.  討論	7
4.1類型域	7
4.2 Q域	8
4.3 分片和組裝	9
4.4 復位標記	9
5.安全性問題	9
原文作者地址	10
參考文獻	11
附錄 A	12
附錄 B	13
附錄 C	18
附錄 D	22
版權聲明	23

1. 簡介
聯合圖像專家組(JPEG)標準[1,2,3]定義了一組針對連續色調靜止圖像的壓縮算法。這
個靜止圖像壓縮算法同樣也可以應用於視頻壓縮,把每一幀都當作一個獨立的靜態圖像來進
行壓縮,然後再按次序進行傳輸。這樣一種視頻編碼通常被稱作運動JPEG(Motion-JPEG)。
我們首先介紹JPEG的概況,然後描述RTP所支持的JPEG的子集,以及將JPEG幀通過
RTP包來傳輸的機制。
JPEG標準定義了四種操作模式:順序DCT模式,漸進DCT模式,無損模式,以及分級模
式。在不同的模式下,一幅圖像用一個或多個“節”來表示,每一節(在JPEG標準中稱爲一
幀)又進一步分成若干次掃描。在每一次掃描中,有一種到四種分量,這些分量代表着彩色
信號的分量(例如“紅綠藍”或一個亮度分量和兩個色差分量)。這些分量可以分開在不同
的掃描中編碼,也可以交織在一次單一的掃描中。
每一幀或每一次掃描前面都有一個頭,可選的壓縮參數定義,例如量化表和哈夫曼編碼
表。頭信息、可選參數以及一個定位符構成了一個頭區段。每一個掃描都是一個經過熵編碼
的比特流,位於兩個頭區段之間。定位符是字節對齊的,並且不能在熵編碼部分出現,這樣
對於掃描邊界的確定就無需解析整個碼流。
壓縮數據有三種表示格式:交換格式、緊縮格式和表格描述格式。交換格式包含在熵編
碼過程中用到的所有碼錶的定義,緊縮模式中省略了一些碼錶定義,假定他們在外部定義或
在前面的圖像中定義。
JPEG標準並不關心組成圖像的各個分量的含義或格式。諸如色彩空間和象素縱橫比這些
屬性在JPEG碼流的外部來定義。JPEG文件交換格式(JFIF)在應用標記段(APPO)提供這
些額外信息,它是一個事實上的標準。簡單說來,JFIF文件就是一個JPEG碼流加上一個APPO
段。對於視頻來說,另外還有一些參數在外部定義,比如幀率,逐行掃描還是隔行掃描等等。
儘管JPEG提供了一整套用於靈活壓縮的算法,但是目前能夠實現整套標準的低成本硬件
還沒出現。事實上,絕大部分JPEG硬件編解碼器都只實現了其中的一個子集,也就是順序
DCT模式。典型的做法是,頭區段信息由軟件來解碼,而用硬件來處理一個在YUV色彩空間
中表示的經過熵編碼的單一的掃描。
一次掃描中包含了一系列最小編碼單元(MCU),每個MCU定義了輸出圖像的一個小矩形
快的數據。
JPEG數據中的復位標記表示解碼器應當在當前點復位它的狀態。如JPEG中定義的那樣,
復位標記是唯一的能夠嵌入在熵編碼碼流裏的標記,但他們只能夠在MCU的邊界處出現。一
個復位間隔是指兩個復位標記之間的數據部分。每一幀的第一個復位間隔是一個例外,它們
前面沒有復位標記。當使用這些標記時,每一幀都由固定數目的復位間隔組成。      
2. 術語
本文檔中出現的關鍵字“必須”,“必須不”,“要求的”,“應該”,“不應該”,“會”,“不
會”,“建議”,“或許”,“可選的”按照RFC 2119[9]中的描述進行解釋。
3. RTP上的JPEG
爲了最大化硬件編解碼器的互操作性,我們假定使用順序DCT模式[1,附錄F],並且限
制預定義的RTP/JPEG類型碼爲單一掃描的隔行圖像。這甚至比基本JPEG更爲嚴格,很多硬
件實現都不能正確解碼基本JPEG(例如,很多硬件不能解碼逐行掃描)。
實際上,在一個視頻碼流中,大部分表格描述的數據在一個視頻碼流中很少發生變化,
這樣在省略掉所有可以省掉的表格之後,RTP/JPEG數據就可以用緊縮格式來表示了。每一幀
一開始是一個熵編碼的掃描。同時存在於幀頭和掃描頭中的信息都在RTP/JPEG頭中表示,
RTP/JPEG頭位於RTP頭和JPEG荷載之間。
類似於哈夫曼碼錶和色彩空間這樣的參數在整個視頻流的生命期中都保持不變,然而另
一些參數則是可以變化的,例如量化表和圖像大小(爲了實現自適應碼率傳輸,允許用戶手
工調節量化等級或分辨率)。因此RTP/JPEG頭中分配了專門的數據域來表示這些信息。因爲
量化表中只有一個小子集是經常使用的,我們用一個短整數來表示整個量化表集。一些特定
範圍的值表示使用自定義的量化表,這種情況下量化表位於JPEG荷載之前。圖像的寬和高是
顯式編碼的。
因爲一個JPEG幀一般總比網絡的最大包長要大,它必須被切分成若干個包。一種方法是
在RTP下面的網絡層來進行分片。但是,這種方法使得對於最終數據包流的碼流控制及有丟
包情況下的部分發送成爲不可能,而且幀長有可能超過網絡層的最大組裝長度(詳細信息參
考[10])。爲了克服這些問題,RTP/JPEG在RTP層定義了一個簡單的分片/組裝方案。
      
4. RTP/JPEG包格式
RTP的時間戳是以90000Hz採樣的,同一幀的每一個包都必須有同樣的時間戳。一幀的
最後一個包的RTP標誌位必須爲1。

4.1 JPEG頭
每一個包的RTP頭之後都緊跟着一個JPEG頭。這個頭的前8個字節,稱作“主JPEG頭”,
定義如下:

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   | 類型特定      |              分段偏移                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      類型     |      Q        |       寬      |      高       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   同一個JPEG幀的的各個包的所有數據域,除了“分段偏移”之外,都必須保持一致。
這個頭之後可能會跟着一個復位頭和/或量化表頭,這取決於“類型”域和“量化”域的
值。

4.1.1 類型特定:8比特
這個數據域的含義取決於“類型”域的值。如果沒有指定,這個域必須爲0並且被接收
端忽略。
4.1.2 分段偏移: 24比特
分段偏移是當前包在整個JPEG幀中的偏移位置,以字節爲單位,以網絡字節次序編碼(最
重要位在前)。分段偏移加上當前包中的荷載數據長度不能超出2^24字節。
3.1.3 類型: 8比特
類型域給出了可能出現在JPEG緊縮格式表格描述或JPEG未定義的JFIF風格參數的信
息。類型0-63在本文檔或本文檔將來的修改中定義,類型64-127與類型0-63相同,除
了在主JPEG頭後緊跟一個復位標記頭,並且在JPEG數據中存在復位標記。類型128-255
可以通過一個會話建立協議來動態定義(這不在本文檔的討論範圍之內)。
3.1.4 Q: 8比特
Q域定義了當前幀的量化表。Q值爲0-127時量化表可以通過類型域決定的一個參數來
計算出來(具體計算方法見後)。Q值爲128-255時會有一個量化表頭出現在當前幀第一個
包的主JPEG頭之後。這個量化表頭用來明確定義量化表。
3.1.5 寬度: 8比特
寬度域編碼圖像的寬度,以8象素爲單位(例如,寬度爲40表示圖像寬度爲320象素)。
最大寬度爲2040象素。
3.1.6 高度: 8比特
高度域編碼圖像的高度,以8象素爲單位(例如,高度爲30表示圖像高度爲240象素)。
當編碼交織視頻時,這裏表示的是一個視頻場的高度,因爲每個場是單獨編碼的。最大高度
是2040象素。
3.1.7 復位標記頭
在類型64-127時,復位標記必須緊跟在主JPEG頭之後。它提供了正確解碼一個包含復
位標記的數據流所需要的額外信息。   


    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           復位間隔            |F|L|          復位計數         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

復位間隔域給出了兩個復位標記之間MCU的數目。它和JFIF頭中DRI標記段的16比特
值是一致的。這個值不能爲零。
   如果一幀中的復位間隔不能保證在包邊界處對齊,F比特和L比特必須設爲1,復位計數
必須設爲0x3fff。這樣接收端就必須在解碼之前首先重新組裝整個幀。
爲了支持部分幀解碼,必須把一幀分成若干塊,每一塊包含整數個復位間隔。復位計數
域給出第一個復位間隔在當前塊中的位置,從而接收端可以知道這些數據對應於當前幀的哪
個部分。復位間隔長度的選取應能夠使一個塊完全放進一個包中。在這種情況下,F比特和L
比特都必須設爲1。然而,如果一個塊要放在多個包裏,只有第一個包的F比特設爲1,也只
有最後一個包的L比特設爲1。
3.1.8 量化表頭
Q值爲128-255時,量化表頭必須出現在主JPEG頭之後(如果存在復位標記頭,則位
於復位標記頭之後)。它提供了一種在帶內描述與Q值對應的量化表的方法。

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |      MBZ      |     精度      |             長度              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                         量化表數據                            |
   |                              ...                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

   長度域給出了後面量化表數據的長度,以字節爲單位。長度域爲零表示當前幀沒有量化表
數據。詳細信息參考4.2。如果長度域的值比剩餘的字節數大,整個包必須丟棄。
包含量化表數據時,表的個數取決於JPEG類型域的值。例如。類型0使用兩個表(一個
用於亮度分量,另一個用於色差分量)。每個表是一個64個值得數組,按zig-zag次序,與
JFIF的DQT標記段一致。
   對於每一個量化表,精度域的一個比特指示了表中係數的大小。如果這個比特爲0,係數
爲8比特,表長度爲64個字節。如果該比特爲1,係數就是16比特的,表長度爲128字節。
對於16比特的表係數,字節次序是網絡次序。精度域的最右邊的比特對應於第一個表,後面
的表依次對應於左邊的下一個比特。超出表個數的那些比特必須被忽略。
對於Q值爲128-254的情況,Q值與量化表之間的映射必須是靜態的,也就是說,保證
接收端只需要讀一次與某個Q值對應的量化表,就可以正確解碼出所有用該Q值編碼的幀。
解碼器不能依賴於任何以前的量化表,而需要在每幀都重新載入這些量化表。Q=255並且長
度爲0的包是不允許的。

3.1.9 JPEG荷載
緊跟RTP/JPEG頭的數據是包含一次掃描的熵編碼的圖像數據。這次掃描不包含掃描頭,
掃描頭的信息可以從RTP/JPEG頭中推出。掃描的結束可能是隱含的(整幅圖象都已經完全解
碼),也可能是顯式的,即跟着一個EOI標記。一次掃描可能會用一些未定義字節填充到任
意長度(一些現存的硬件編解碼器會在一幀圖象的底部生成一些額外的行,解碼器需要對它
們進行哈夫曼解碼來去除這些額外的行。
類型碼決定着復位標記是否存在。如果某種類型支持復位標記,數據包的復位頭中必須
包含一個非零的復位間隔值,並且復位標記必須是字節對齊的,以一個0xFF起始。另外的
0xFF字節可以出現在復位間隔之中。在打包過程中,用這樣的方法來進行對齊,例如字對齊,
從而實現比較高效的拷貝。除此之外,復位標記不能出現在碼流中的任何其它地方。不支持
復位標記的類型的碼流在任何地方都不能包含復位標記。在數據包中,如果熵編碼產生了一
個0xFF字節,則必須在它後面填充一個0x00字節。[見文獻1的B.1.1.5]

4.  討論
4.1類型域
類型域定義了緊縮的表格描述和JPEG中未定義的額外的JFIF風格參數,因爲這些信息
在待傳輸的JPEG數據中不存在。
類型域定義了三種取值範圍。0-63的含義是固定的,在本文檔或本文檔的將來版本中
定義。64-127與0-63的區別僅在於包含復位標記,並且在主JPEG頭後緊跟着一個復位頭,
其餘都完全一致。128-255是可以由一個會話建立協議來動態定義的(這不再本文的討論範
圍之內)。
對於第一類取值範圍,類型0和類型1目前已經定義了,對應第二類範圍中的類型64和
類型65。類型0,1指的是基本DCT順序模式、8比特採樣、正方形象素、YUV三種顏色分量
以及標準哈夫曼碼錶[在文獻1的附錄K.3中定義],一次隔行掃描並帶一個掃描分量選擇子,
來指示是分量1,2還是3。Y,U和V分量分別對應於分量1,2,3。分量1使用0號哈夫曼
碼錶和0號量化表,分量2和3使用1號哈夫曼碼錶和1號量化表。
類型碼2-5定爲保留,並禁止使用。基於本文檔以前版本(RFC 2035)的應用應當更新
對於類型64和類型65的解釋,指示出有復位標記的存在。
這兩種RTP/JPEG類型當前的具體定義如下:

            類型    分量   水平採樣因子.垂直採樣因子 量化表序號
         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         |       |  1 (Y)  |     2     |     1     |     0     |
         | 0, 64 |  2 (U)  |     1     |     1     |     1     |
         |       |  3 (V)  |     1     |     1     |     1     |
         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
         |       |  1 (Y)  |     2     |     2     |     0     |
         | 1, 65 |  2 (U)  |     1     |     1     |     1     |
         |       |  3 (V)  |     1     |     1     |     1     |
         +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

採樣因子說明類型0的視頻的色度分量水平方向上二倍降採樣(一般稱爲4:2:2),
而類型1的視頻的色度分量在水平和垂直兩個方向上都二倍降採樣(一般稱爲4:2:0)。
類型0和類型1既可以用於傳輸漸進掃描的圖象數據,也可以用於傳輸隔行掃描的圖象
數據。這兩種不同的數據格式在主JPEG頭中加以區分。具體定義如下:
0:圖象是漸進掃描的。在計算機顯示器上,它可以按照制定的大小來顯示。
1:當前圖像是隔行掃描視頻信號的奇數場。主JPEG頭中給出的高度是整個圖象高度的
一半。當前場應當與後面緊跟的偶數場一起重新恢復出整幀圖象。偶數場的行恰好處
於奇數場對應行的上方。
2:當前圖象是隔行掃描視頻信號的偶數場。
3:當前圖象是隔行掃描視頻信號的一場,但它將按整幀圖象的大小來單獨顯示。在計算
機顯示器上,每一行都顯示兩遍,圖象高度加倍。
附錄B中給出了將RTP/JPEG頭中的信息變換到JPEG幀頭和掃描頭的C源碼。

4.2 Q域
對於JPEG類型0和類型1(以及相應的類型64和65),Q值1-99的定義如下。其它
128以下的值保留。
類型0和類型1都需要有兩個量化表。這些量化表的計算方法如下:對於1 <= Q <= 99,
用JPEG組織的公式[5]來計算一個標量量化因子S:
S = 5000 / Q          如果  1 <= Q <= 50
  = 200 - 2 * Q       如果 51 <= Q <= 99
然後把這個S值代入[1]中的表K.1和K.2(每個值都擴展到8比特),就分別得到了量
化表0和量化表1。計算量化表的C源碼在附錄A中給出。
當Q值在128-255之間時,就需要使用動態定義的量化表。這些量化表既可以在帶內定
義,也可以在帶外通過一個會話建立協議來定義。但在每一幀的第一個包中必須有一個量化
表頭。當量化表在帶外定義時,可以通過將包頭中的長度域設爲0來省略掉量化表。
當在帶內傳輸量化表時,並不需要在每一幀都重複傳送一遍。類似於帶外的情況,不包
含量化表的幀可以在包頭中將長度域設置爲0。儘管這樣做減小了傳輸量化錶帶來的
OVERHEAD,但是也帶來了一些負面效應。一個新的接收者在收到完整量化表之前接收到的所
有幀都不能夠正確解碼。

4.3 分片和組裝
由於JPEG的每一幀都相當大,必須經過切分才便於傳輸。在將一幀切分成若干個包的過
程中,應當避免在低層進行分片。如果要求支持部分幀解碼,被切分出的每一個包就應當包
含整數個復位間隔(如下)。組成同一幀的數據包的時間戳必須保持一致,並且最後一個包
的RTP標記位必須爲1。每個包的分段偏移域的值是這個包中數據在原來整個幀中的偏移位
置,以字節爲單位。這些包必須按照次序進行傳輸,並且它們所包含的圖象數據不能重疊。
   整個一幀圖象以一個分段偏移爲0的包爲起始,並以一個RTP標記位爲1的包爲結束。可
以通過RTP的順序號或者分段偏移結合每個包的長度來檢測丟包。數據的重組可以不使用分
段偏移的數據(只使用RTP標記位和RTP順序號),但是在出現包的亂序的情況下,就不可
能通過簡單的拷貝操作來實現圖象數據的重組。而且,如果前一幀的最後一個包丟失的話,
即使當前幀完好無損,接收段也不能夠正常恢復出當前幀。
4.4 復位標記
復位標記插入在JPEG碼流中,告訴接收端哈夫曼解碼器和直流預測器應當在當前位置復
位,並且允許從當前點開始進行部分解碼。然而,爲了充分實現部分解碼,解碼器必須知道
一個復位間隔中包含的是哪些MCU。爲此,原來的JPEG標準中在復位標記中提供了一個短的
次序號域。但是對於典型的網絡MTU長度來說,這個數域不夠長,不能很好的處理丟包問題。
因此,在RTP/JPEG的復位頭中包含了額外的信息來處理這個問題。
復位間隔的大小應當使得整數個復位間隔能夠恰好放在一個數據包裏。這樣就可以保證
這些包可以相互獨立地進行解碼。如果一個復位間隔的結束處超出了一個包的長度,可以使
用復位標記頭中的F比特和L比特來對它進行切分。但是這樣生成的包的集合必須全部接收
到,接收端纔可以正確解碼出那個復位間隔中的數據。
一旦解碼器接收到一個F和L都爲1的包,或者是一連串的包,第一個F爲1,最後一
個L爲1,它就可以開始解碼了。起始MCU在整幅圖象中的位置可以通過將復位計數的值與
復位間隔的值相乘來確定。這樣的一個包(或一連串包)可以包含任意數量的連續的復位間
隔。
爲了兼容生成碼流中就包含復位標記但無法按這些復位標記來分片的編碼器,將復位計
數域設爲0x3FFF並且F和L均爲1。這樣一種模式意味着解碼器必須對整幅圖象首先進行重
組,然後才能解碼。

5.安全性問題
本文檔中所定義的RTP包格式的安全性問題可以遵循[6]和[7]中的建議。這意味着媒體
數據流的安全性是通過加密來實現的。因爲對於媒體數據的壓縮是端到端的,加密操作可以
在壓縮操作之後執行,這樣在兩種操作之間就不存在任何衝突。
對於解碼端計算量不均衡的壓縮編碼計數,存在一種潛在的拒絕服務的攻擊威脅。攻擊
者可以在數據流中插入一些惡意的數據包,對於這些包的解碼會導致解碼器運算量過載。幸
運的是,我們的壓縮編碼算法並沒有明顯的計算量不均衡現象。
另一種潛在的拒絕服務威脅存在於我們提出的分片重組機制。接收端應當限制重組數據
的總長,以避免資源耗盡。
對於任何基於IP的協議,接收端在某些情況下可能會因爲接收到過多的數據包而發生過
載。網絡層的鑑定機制可以將來自不明來源或惡意來源的數據包丟棄,但是這樣做所帶來的
成本也是相當高的。在組播的環境裏,刪除某些源的數據包可以通過IGMP[8]的未來版本和
組播路由協議來實現,從而允許用戶來選擇哪些數據源是允許的,哪些是不允許的。
對於這種荷載格式的安全性考慮並不超出RTP規範中的內容。

原文作者地址
   Lance M. Berc
   Systems Research Center
   Digital Equipment Corporation
   130 Lytton Ave
   Palo Alto CA 94301

   Phone: +1 650 853 2100
   Email: [email protected]

   William C. Fenner
   Xerox PARC
   3333 Coyote Hill Road
   Palo Alto, CA 94304

   Phone: +1 650 812 4816
   Email: [email protected]

   Ron Frederick
   Xerox PARC
   3333 Coyote Hill Road
   Palo Alto, CA 94304

   Phone: +1 650 812 4459
   Email: [email protected]

   Steven McCanne
   University of California at Berkeley
   Electrical Engineering and Computer Science
   633 Soda Hall
   Berkeley, CA 94720

   Phone: +1 510 642 0865
   Email: [email protected]

   Paul Stewart
   Xerox PARC
   3333 Coyote Hill Road
   Palo Alto, CA 94304

   Phone: +1 650 812 4821
   Email: [email protected]

參考文獻

   [1]  ISO DIS 10918-1. Digital Compression and Coding of Continuous-
        tone Still Images (JPEG), CCITT Recommendation T.81.

   [2]  William B. Pennebaker, Joan L. Mitchell, JPEG: Still Image Data
        Compression Standard, Van Nostrand Reinhold, 1993.

   [3]  Gregory K. Wallace, The JPEG Sill Picture Compression Standard,
        Communications of the ACM, April 1991, Vol 34, No. 1, pp. 31-44.

   [4]  The JPEG File Interchange Format.  Maintained by C-Cube
        Microsystems, Inc., and available in
        ftp://ftp.uu.net/graphics/jpeg/jfif.ps.gz.

   [5]  Tom Lane et. Al., The Independent JPEG Group software JPEG
        codec.  Source code available in
        ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6a.tar.gz.

   [6]  Schulzrinne, H., Casner, S., Frederick, R. and V. Jacobson,
        "RTP: A Transport Protocol for Real-Time Applications", RFC
        1889, January 1996.

   [7]  Schulzrinne, H., "RTP Profile for Audio and Video Conferences
        with Minimal Control", RFC 1890, January 1996.

   [8]  Fenner, W., "Internet Group Management Protocol Version 2", RFC
        2236, November 1997.

   [9]  Bradner, S., "Key words for use in RFCs to Indicate Requirement
        Levels", BCP 14, RFC 2119, March 1997.

   [10] Kent C., and J. Mogul, "Fragmentation Considered Harmful",
        Proceedings of the ACM SIGCOMM '87 Workshop on Frontiers in
        Computer Communications Technology, August 1987.

附錄 A
下面的代碼用來通過一個Q因子值生成一個量化表:

/*
?	Table K.1 from JPEG spec.
 */
static const int jpeg_luma_quantizer[64] = {
        16, 11, 10, 16, 24, 40, 51, 61,
        12, 12, 14, 19, 26, 58, 60, 55,
        14, 13, 16, 24, 40, 57, 69, 56,
        14, 17, 22, 29, 51, 87, 80, 62,
        18, 22, 37, 56, 68, 109, 103, 77,
        24, 35, 55, 64, 81, 104, 113, 92,
        49, 64, 78, 87, 103, 121, 120, 101,
        72, 92, 95, 98, 112, 100, 103, 99
};

/*
?	Table K.2 from JPEG spec.
 */
static const int jpeg_chroma_quantizer[64] = {
        17, 18, 24, 47, 99, 99, 99, 99,
        18, 21, 26, 66, 99, 99, 99, 99,
        24, 26, 56, 99, 99, 99, 99, 99,
        47, 66, 99, 99, 99, 99, 99, 99,
        99, 99, 99, 99, 99, 99, 99, 99,
        99, 99, 99, 99, 99, 99, 99, 99,
        99, 99, 99, 99, 99, 99, 99, 99,
        99, 99, 99, 99, 99, 99, 99, 99
};

/*
?	Call MakeTables with the Q factor and two u_char[64] return arrays
 */
void
MakeTables(int q, u_char *lqt, u_char *cqt)
{
  int I;
  int factor = q;

  if (q < 1) factor = 1;
  if (q > 99) factor = 99;
  if (q < 50)
q = 5000 / factor;
  else
q = 200 – factor*2;

  for (I=0; I < 64; I++) {
int lq = (jpeg_luma_quantizer[I] * q + 50) / 100;
int cq = (jpeg_chroma_quantizer[I] * q + 50) / 100;

/* Limit the quantizers to 1 <= q <= 255 */
if (lq < 1) lq = 1;
else if (lq > 255) lq = 255;
lqt[I] = lq;

if (cq < 1) cq = 1;
else if (cq > 255) cq = 255;
cqt[I] = cq;
  }
}

附錄 B
下面這段代碼用來生成對應於那些RTP/JPEG中不存在的表描述數據的JPEG標記段。

U_char lum_dc_codelens[] = {
        0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0,
};

u_char lum_dc_symbols[] = {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
};

u_char lum_ac_codelens[] = {
        0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d,
};

u_char lum_ac_symbols[] = {
        0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
        0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
        0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
        0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
        0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
        0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
        0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
        0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
        0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
        0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
        0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
        0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
        0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
        0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
        0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
        0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
        0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
        0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
        0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
        0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
        0xf9, 0xfa,
};

u_char chm_dc_codelens[] = {
        0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
};

u_char chm_dc_symbols[] = {
        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,

};

u_char chm_ac_codelens[] = {
        0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77,
};

u_char chm_ac_symbols[] = {
        0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
        0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
        0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
        0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
        0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
        0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
        0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
        0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
        0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
        0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
        0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
        0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
        0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
        0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
        0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
        0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
        0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
        0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
        0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
        0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
        0xf9, 0xfa,
};

u_char *
MakeQuantHeader(u_char *p, u_char *qt, int tableNo)
{
        *p++ = 0xff;
        *p++ = 0xdb;            /* DQT */
        *p++ = 0;               /* length msb */
        *p++ = 67;              /* length lsb */
        *p++ = tableNo;
        memcpy(p, qt, 64);
        return (p + 64);
}

u_char *
MakeHuffmanHeader(u_char *p, u_char *codelens, int ncodes,
                  u_char *symbols, int nsymbols, int tableNo,
                  int tableClass)
{
        *p++ = 0xff;

        *p++ = 0xc4;            /* DHT */
        *p++ = 0;               /* length msb */
        *p++ = 3 + ncodes + nsymbols; /* length lsb */
        *p++ = (tableClass << 4) | tableNo;
        memcpy(p, codelens, ncodes);
        p += ncodes;
        memcpy(p, symbols, nsymbols);
        p += nsymbols;
        return (p);
}

u_char *
MakeDRIHeader(u_char *p, u_short dri) {
        *p++ = 0xff;
        *p++ = 0xdd;            /* DRI */
        *p++ = 0x0;             /* length msb */
        *p++ = 4;               /* length lsb */
        *p++ = dri >> 8;        /* dri msb */
        *p++ = dri & 0xff;      /* dri lsb */
        return (p);
}

/*
?	Arguments:
?	type, width, height: as supplied in RTP/JPEG header
?	lqt, cqt: quantization tables as either derived from
?	the Q field using MakeTables() or as specified
?	in section 4.2.
?	dri: restart interval in MCUs, or 0 if no restarts.
 *
?	p: pointer to return area
 *
?	Return value:
?	The length of the generated headers.
 *
?	Generate a frame and scan headers that can be prepended to the
?	RTP/JPEG data payload to produce a JPEG compressed image in
?	interchange format (except for possible trailing garbage and
?	absence of an EOI marker to terminate the scan).
 */
int MakeHeaders(u_char *p, int type, int w, int h, u_char *lqt,
                u_char *cqt, u_short dri)
{
        u_char *start = p;

        /* convert from blocks to pixels */
        w <<= 3;
        h <<= 3;

        *p++ = 0xff;
        *p++ = 0xd8;            /* SOI */

        p = MakeQuantHeader(p, lqt, 0);
        p = MakeQuantHeader(p, cqt, 1);

        if (dri != 0)
                p = MakeDRIHeader(p, dri);

        *p++ = 0xff;
        *p++ = 0xc0;            /* SOF */
        *p++ = 0;               /* length msb */
        *p++ = 17;              /* length lsb */
        *p++ = 8;               /* 8-bit precision */
        *p++ = h >> 8;          /* height msb */
        *p++ = h;               /* height lsb */
        *p++ = w >> 8;          /* width msb */
        *p++ = w;               /* wudth lsb */
        *p++ = 3;               /* number of components */
        *p++ = 0;               /* comp 0 */
        if (type == 0)
                *p++ = 0x21;    /* hsamp = 2, vsamp = 1 */
        else
                *p++ = 0x22;    /* hsamp = 2, vsamp = 2 */
        *p++ = 0;               /* quant table 0 */
        *p++ = 1;               /* comp 1 */
        *p++ = 0x11;            /* hsamp = 1, vsamp = 1 */
        *p++ = 1;               /* quant table 1 */
        *p++ = 2;               /* comp 2 */
        *p++ = 0x11;            /* hsamp = 1, vsamp = 1 */
        *p++ = 1;               /* quant table 1 */
        p = MakeHuffmanHeader(p, lum_dc_codelens,
sizeof(lum_dc_codelens),
lum_dc_symbols,
sizeof(lum_dc_symbols), 0, 0);
        p = MakeHuffmanHeader(p, lum_ac_codelens,
sizeof(lum_ac_codelens),
lum_ac_symbols,
sizeof(lum_ac_symbols), 0, 1);
        p = MakeHuffmanHeader(p, chm_dc_codelens,
sizeof(chm_dc_codelens),
chm_dc_symbols,
sizeof(chm_dc_symbols), 1, 0);
        p = MakeHuffmanHeader(p, chm_ac_codelens,
sizeof(chm_ac_codelens),
chm_ac_symbols,
sizeof(chm_ac_symbols), 1, 1);

        *p++ = 0xff;
        *p++ = 0xda;            /* SOS */
        *p++ = 0;               /* length msb */
        *p++ = 12;              /* length lsb */
        *p++ = 3;               /* 3 components */
        *p++ = 0;               /* comp 0 */
        *p++ = 0;               /* Success table 0 */
        *p++ = 1;               /* comp 1 */
        *p++ = 0x11;            /* Success table 1 */
        *p++ = 2;               /* comp 2 */
        *p++ = 0x11;            /* Success table 1 */
        *p++ = 0;               /* first DCT coeff */
        *p++ = 63;              /* last DCT coeff */
        *p++ = 0;               /* Successive approx. */

        return (p – start);
};

附錄 C
下面這段代碼用來闡明RTP/JPEG數據包分片和頭的生成過程。

   For clarity and brevity, the structure definitions are only valid for
   32-bit big-endian (most significant octet first) architectures. Bit
   fields are assumed to be packed tightly in big-endian bit order, with
   no additional padding. Modifications would be required to construct a
   portable implementation.

/*
?	RTP data header from RFC1889
 */
typedef struct {
        unsigned int version:2;   /* protocol version */
        unsigned int p:1;         /* padding flag */
        unsigned int x:1;         /* header extension flag */
        unsigned int cc:4;        /* CSRC count */
        unsigned int m:1;         /* marker bit */
        unsigned int pt:7;        /* payload type */
        u_int16 seq;              /* sequence number */
        u_int32 ts;               /* timestamp */
        u_int32 ssrc;             /* synchronization source */
        u_int32 csrc[1];          /* optional CSRC list */
} rtp_hdr_t;

#define RTP_HDR_SZ 12

/* The following definition is from RFC1890 */
#define RTP_PT_JPEG             26

struct jpeghdr {
        unsigned int tspec:8;   /* type-specific field */
        unsigned int off:24;    /* fragment byte offset */
        u_int8 type;            /* id of jpeg decoder params */
        u_int8 q;               /* quantization factor (or table id) */
        u_int8 width;           /* frame width in 8 pixel blocks */
        u_int8 height;          /* frame height in 8 pixel blocks */
};

struct jpeghdr_rst {
        u_int16 dri;
        unsigned int f:1;
        unsigned int l:1;
        unsigned int count:14;
};

struct jpeghdr_qtable {
        u_int8  mbz;
        u_int8  precision;
        u_int16 length;
};

#define RTP_JPEG_RESTART           0x40

/* Procedure SendFrame:
 *
?	Arguments:
?	start_seq: The sequence number for the first packet of the current
?	frame.
?	ts: RTP timestamp for the current frame
?	ssrc: RTP SSRC value
?	jpeg_data: Huffman encoded JPEG scan data
?	len: Length of the JPEG scan data
?	type: The value the RTP/JPEG type field should be set to
?	typespec: The value the RTP/JPEG type-specific field should be set
?	to
?	width: The width in pixels of the JPEG image
?	height: The height in pixels of the JPEG image
?	dri: The number of MCUs between restart markers (or 0 if there
?	are no restart markers in the data
?	q: The Q factor of the data, to be specified using the Independent
?	JPEG group's algorithm if 1 <= q <= 99, specified explicitly
?	with lqt and cqt if q >= 128, or undefined otherwise.
?	lqt: The quantization table for the luminance channel if q >= 128
?	cqt: The quantization table for the chrominance channels if
?	q >= 128
 *
?	Return value:
?	the sequence number to be sent for the first packet of the next
?	frame.
 *
?	The following are assumed to be defined:
 *
 * PACKET_SIZE                         - The size of the outgoing packet
?	send_packet(u_int8 *data, int len)  - Sends the packet to the network
 */

u_int16 SendFrame(u_int16 start_seq, u_int32 ts, u_int32 ssrc,
                   u_int8 *jpeg_data, int len, u_int8 type,
                   u_int8 typespec, int width, int height, int dri,
                   u_int8 q, u_int8 *lqt, u_int8 *cqt) {
        rtp_hdr_t rtphdr;
        struct jpeghdr jpghdr;
        struct jpeghdr_rst rsthdr;

        struct jpeghdr_qtable qtblhdr;
        u_int8 packet_buf[PACKET_SIZE];
        u_int8 *ptr;
        int bytes_left = len;
        int seq = start_seq;
        int pkt_len, data_len;

        /* Initialize RTP header
         */
        rtphdr.version = 2;
        rtphdr.p = 0;
        rtphdr.x = 0;
        rtphdr.cc = 0;
        rtphdr.m = 0;
        rtphdr.pt = RTP_PT_JPEG;
        rtphdr.seq = start_seq;
        rtphdr.ts = ts;
        rtphdr.ssrc = ssrc;

        /* Initialize JPEG header
         */
        jpghdr.tspec = typespec;
        jpghdr.off = 0;
        jpghdr.type = type | ((dri != 0) ? RTP_JPEG_RESTART : 0);
        jpghdr.q = q;
        jpghdr.width = width / 8;
        jpghdr.height = height / 8;

        /* Initialize DRI header
         */
        if (dri != 0) {
                rsthdr.dri = dri;
                rsthdr.f = 1;        /* This code does not align Ris */
                rsthdr.l = 1;
                rsthdr.count = 0x3fff;
        }

        /* Initialize quantization table header
         */
        if (q >= 128) {
                qtblhdr.mbz = 0;
                qtblhdr.precision = 0; /* This code uses 8 bit tables only */
                qtblhdr.length = 128;  /* 2 64-byte tables */
        }

        while (bytes_left > 0) {
                ptr = packet_buf + RTP_HDR_SZ;
                memcpy(ptr, &jpghdr, sizeof(jpghdr));

                ptr += sizeof(jpghdr);

                if (dri != 0) {
                        memcpy(ptr, &rsthdr, sizeof(rsthdr));
                        ptr += sizeof(rsthdr);
                }

                if (q >= 128 && jpghdr.off == 0) {
                        memcpy(ptr, &qtblhdr, sizeof(qtblhdr));
                        ptr += sizeof(qtblhdr);
                        memcpy(ptr, lqt, 64);
                        ptr += 64;
                        memcpy(ptr, cqt, 64);
                        ptr += 64;
                }

                data_len = PACKET_SIZE – (ptr – packet_buf);
                if (data_len >= bytes_left) {
                        data_len = bytes_left;
                        rtphdr.m = 1;
                }

                memcpy(packet_buf, &rtphdr, RTP_HDR_SZ);
                memcpy(ptr, jpeg_data + jpghdr.off, data_len);

                send_packet(packet_buf, (ptr – packet_buf) + data_len);

                jpghdr.off += data_len;
                bytes_left -= data_len;
                rtphdr.seq++;
        }
        return rtphdr.seq;
}

附錄 D
這一部分給出了本文檔相對於RFC 2035的改動。這些改動着眼於儘可能保持新版本對於
舊版本的兼容性。事實上,很多已經廢棄的約定仍然能夠在新版中正常地解碼。儘管如此,
我們仍然強烈反對在新版中使用一些舊地約定。
    o   類型0和類型1現在可以編碼隔行掃描的視頻圖象,用類型特定域中的兩個比特給
出指示即可。參見4.1節。
o   JPEG工作組曾經就如何更靈活地描述JPEG量化表發生過爭論。本備忘錄允許通過使
用一個可選地量化表頭來顯式地描述表係數。這些內容在節3.1.8和4.2中討論。
       

o   在RFC 2035中,在類型域中描述復位標記的信息,這樣就很難再加入新的類型。並
且,類型特定域用於記錄復位計數,這樣其它的一些類型特定信息就無法編碼。在
本備忘錄中,復位標記的指示移到了類型域中的一個特定比特位,並且加入了可選
頭來編碼一些必要的額外信息,而把類型特定域留出來用於它本來的用途。對於部
分幀解碼的處理提高了碼流的健壯性,能夠對抗一定程度的丟包。詳細信息參見
3.1.7和4.4。

版權聲明
   版權歸Internet協會所有(1998)。保留所有權利。
   本文及其譯本可以提供給其他任何人,可以準備繼續進行註釋,可以繼續拷貝、出版、發
布,無論是全部還是部分,沒有任何形式的限制,不過要在所有這樣的拷貝和後續工作中提
供上述聲明和本段文字。無論如何,本文檔本身不可以做任何的修改,比如刪除版權聲明或
是關於因特耐特協會、其他的因特耐特組織的參考資料等。除了是爲了開發Internet標準的
需要,或是需要把它翻譯成除英語外的其他語言的時候,在這種情況下,在Internet標準程
序中的版權定義必須被附加其中。
   上面提到的有限授權允許永遠不會被Internet協會或它的繼承者或它的下屬機構廢除。
   本文檔和包含在其中的信息以"As is"提供給讀者,Internet社區和Internet工程任務
組不做任何擔保、解釋和暗示,包括該信息使用不破壞任何權利或者任何可商用性擔保或特
定目的。




RFC2435—RTP Payload Format for JPEG-compressed Video        針對JPEG壓縮視頻的RTP荷載格式


1
RFC文檔中文翻譯計劃
五、總結

1.在傳輸視頻時,直接將壓縮後的jpeg圖片包裝到rtp協議並且加上jpeg頭,按上定義ok,但是目前我不清楚Q因子是拿來搞啥的,哪位大嬸知道,麻煩告訴我下。

2.在傳輸時要從第1幀開始,seq_num要爲1,但是在做h.264傳輸時,我用的是0也ok。

3.注意unsing long 是8個字節,unsigned int 才4個字節,這裏我吃虧了,導致視頻向右偏移。之前在rtp頭中用了兩個unsigned long,一直傳輸花屏。

4.特別還要注意網絡字節順序。

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