2012年7月4日 星期三

JPEG/RTP 封包分析


MJPEG(MotionJPEG)封包分析

RFC2435 定義了 RTP 上傳送的 JPEG 影像,由於各家廠商實作方式不同,曾在互通測試時產生問題,NVR所接收到的MJPEG無法正常顯示,所以特別針對此份規則書以及可能產生問題的封包加以分析。



RFC2435 簡介

1. JPEG有三種格式
the interchange format, the abbreviated format, or the table-specification format
當JPEG 在 RTP上傳送時,可以採用 abbreviated 格式,以節省傳輸量。
 
2. JPEG over RTP
a. 限制 JPEG 格式為 single-scan, interleaved images.

b. 同一個Frame的每個封包都具有相同的TimeStamp,而最後一個封包的Marker欄位會設定為1,如下圖。 
圖1. Marker = TRUE


c. RTP header 後會立刻接 JPEG main header,此header固定有6個欄位,如下, 
此處需注意 Width與Height都使用 8bits存放,以8像素為基本單位,因此最大高度/寬度為2040(255*8)像素。設定超過此值,會造成無法預知的情況。 此處整理了幾個超過2040的作法。 
圖2. Main Header

d. 在 JPEG main header 之後,可能會跟著兩個 header,分別是 Restart Marker header 與 Quantization Table。是否要額外加上這兩個header,則視Main Header中的欄位Type與Q來決定。  
  • 當Q值為0~127時,可以參考Type 來計算出  Quantization Table。
  • 當Q值為128~255時,則需要在每個Frame的第一個封包就帶 Quantization Table Header 
  • 當Type值為64~127時,JPEG Main header後需要緊接著 Restart Marker header。 64~127 與 0~63 差異僅在 restart marker,其餘完全一致。 
  • 參考圖3,當F=1,L=1,且Restart Count=0x3FFF時,表示接收端需收完此張JPEG的所有封包,重組封包後,才能夠開始進行JPEG解碼。
   
圖3. Restart Marker Header and  Quantization Table Header


3. TYPE值
TYPE 0,1 (64,65)的定義如下,

參考上面表格
type 0 表示 YUV422
type 1 表示  YUV420

4. Q值
對於TYPE = 0,1 (64,65) 的情況,當 Q < 128時,量化表有固定的計算方式(實際上只定義0~99, 100~128的部分保留),此部分可參考RFC2435 Chapter 4.2 與 appendix A。摘錄 Chapter 4.2 The Q Field 的換算公式如下:
For 1 <= Q <= 99, the Independent JPEG Group's formula [5] is used to produce a scale factor S as:
   S = 5000 / Q          for  1 <= Q <= 50
      = 200 - 2 * Q       for 51 <= Q <= 99 
This value is then used to scale Tables K.1 and K.2 from [1] (saturating each value to 8 bits) to give quantization table numbers 0 and 1, respectively.  C source code is provided in Appendix A to

5. Fragmentation 和 reassemble
一張JPEG圖片的傳送會分成許多封包傳送,其中第一個封包其分段偏移(Fragment offset)為0,而最後一個封包其 RTP Marker 欄位為 True,可參考圖1。

6. 附錄A: 提供一個公式,可以透過 Q 值算出量化表。若要在傳送 RTP  JPEG時,省略 Quantation Table,則應採用此處的範例程式。摘錄如下:

live555 的 JPEGVideoRTPSource.cpp 也是使用類似作法,差異在於其函數 makeDefaultQtables()將量化表格先改成 zig-zag 的排序順序。這部分可能會造成互通時的一些問題。摘錄 libjpeg.txt 說明如下:  
CAUTION: prior to library version 6a, jpeg_add_quant_table expected
the basic table to be given in JPEG zigzag order.  If you need to
write code that works with either older or newer versions of this
routine, you must check the library version number.  Something like
"#if JPEG_LIB_VERSION >= 61" is the right test. 
參考 http://lists.live555.com/pipermail/live-devel/2013-January/016400.html,大部份JPEG 實作都會使用 zig-zag 的排列方式將資料填入 Qtables。
7.  附錄B: 提供一個重建 JPEG mark segments 的程式碼。

問題現象
  • 將IPCam設定為 MJPEG,進行RTP串流溝通,使用某家 NVR 軟體時,畫面無法正常顯示。
  • 使用 VLC 軟體進行驗證,則可以正常顯示。
  • 錯誤發生時, IPCAM 使用TYPE=1的格式傳送 MJPEG。
  • 使用其他IPCam 對照,發現其他IPCam使用TYPE=65的格式,此NVR軟體可以正常顯示。
  • 錯誤原因猜測
  • 可能是NVR軟體並不支援 TYPE=1 的格式。
  • 可能是 NVR軟體預設的  Quantization Table 計算方式有誤。 
  • 可能是 JPEG 數據格式的關係. (Main Header中的Type,以上述封包為例,type specific為0,表示progressive image) 
  • 最終解決方式,加上Restart Marker header後,此NVR便可以正確解析MJPEG (註:TYPE=1與TYPE=65差異便是在於Restart Marker,推測可能此NVR並沒有完全遵照SPEC實作)

參考資料