• 沒有找到結果。

第三章 多媒體串流之自由軟體介紹及使用

3.2 OpenCV 與串流平台結合

3.2.1 串流平台簡介

串流平台可以分為兩個部份,Server 以及 Client,Server 端將影 像來源進行mpeg-4 壓縮,也就是將不含 bmp header 的 bmp raw data 壓縮成如第二章所示mpeg-4 video elementary stream,壓縮好的

elementary stream 則包裝成 rtp packet 進行網路傳送,而 Client 端則接

收rtp packet 並且進行 mpeg-4 的解壓縮,也就是將 mpeg-4 video elementary stream 解壓縮成 bmp raw data,由於 client/server 系統必須 在同一個時間處理不同的工作,例如server 必須一邊進行 mpeg-4 壓

OverallListenerThread()

RTSPProcessor Thread() Bandwidth

Smoother() StreamerletIs() Streaming Data ...

Raw Audiovisual Data...

MPEG4

將各種攝影機擷取而來的raw data 壓縮成 mpeg-4 elementary stream,

然後將這些bitstreams 放到 buffer 裡讓 StreamerletIs( )存取

StatusTineFunc( )

收集並且顯示client 的資訊於 GUI 上

OverallListenerThread( )

負責接收新的client 連線,並且每次當成功的建立起新的連線會創造 另外一個執行緒RTSPProcessorThread( )來管理這個新的連線

PacketScheduler( )

將已經包裝好的RTP pacets 藉由 UDP sockets 送入網際網路中 下面這些執行緒,在每次有新的client 連線進來時就會被建立起來

RTSPProcessorThread( )

負責接收、分析、抽取以及回應 RTSP messages,並且改變目前 server 的狀態,圖28 是 RTSP State Machine,舉例來說,當 Server 端目前 在Ready state,而且收到來自 client 端的 ”PLAY” message,

RTSPProcessorThread( )會將 state 改變成 Play state 並且做出對應的動 作

StreamerletIs( )

如果是採用UDP,則此執行緒負責將剛剛 Transcoder( )壓縮好的 mpeg-4 video elementary stream 包裝成 RTP packets,並將這些 packets

放在PacketScheduler( )能夠存取的 buffer 上,另一方面如果是採用 TCP 當做傳送協定,則此執行緒便不做包裝的動作,直接將壓縮好 的elementary stream 放在 TunnelMain( )能夠存取的 buffer 上

TunnelMain( )

藉由TCP Sockets 將 elementary stream 送入網際網路中 圖29 則是當 client 啟動時,同時間會執行的執行緒

圖29 Threads of client system

GUI thread

作為使用者與server 端溝通的介面

VideoStatusTimeFunc( )

負責在GUI 上面顯示目前接收封包的情況,同時把接收的 mpeg-4 elementary stream 解壓縮成 bmp 的 raw data,並把解壓縮的結果播放

出來

RTSPProcessorThread( )

與server 端相同,負責 RTSP 指令分析以及改變 client 的 RTSP 狀態

TunnelMain( )

藉由TCP 接收串流封包,並且把這些串流封包放在 VideoStatusTime Func( )能夠存取的地方

RecvThreadIsK( )

藉由UDP 接收 RTP packets,並且把這些封包放在 DepktThreadIsK( ) 能夠存取的地方

DePktThreadIsK( )

拆解RTP packets 取得裡面的 elementary streams,並將這些資料放在 VideoStatusTimeFunc( )能存取的地方

從以上這些介紹,我們可以在server 端或是 client 端加上影像處 理,在server 端我們可以加在 Transcoder( )這個執行緒裡,因為這個 執行緒將從各種攝影機抓取資料,抓到的資料必須轉成bmp 檔接著 我們可以在這邊進行影像處理以及串流處理,在client 端我們可以加 在VideoStatusTimeFunc( )這個執行緒之上,因為這個執行緒能將 elementary stream 解碼成 bmp 的 raw data,我們可以再建立另外一個 執行緒來存取這個raw data 並做影像處理。

在server 端我們將透過以下幾個 API 以及一些參數配置將影像處 理過後的bmp raw data 進行 mpeg-4 壓縮以及網路串流。首先,必須

配置好mpeg-4 壓縮輸入資料與輸出資料的位置,在這邊輸入資料是 指每一張進行過影像處理的bmp raw data 而輸出資料是指壓縮過後 的mpeg-4 video elementary stream,有可能是 ESDS data +I frame 也有 可能是P frame 其配置方式如圖 30 所示,其中 imageData 是經過影像 處理後的bmp raw data 的起始位置,而 TargetBuffer 則是壓縮過後的 mpeg-4 frame 的起始位置。接下來我們就要對此 bmp raw data 進行

圖30 配置 codec input data 與 output data

mpeg-4 壓縮,使用 API encore( ),此 API 會將壓縮好的 mpeg-4 elementary stream 放到 TargetBuffer 裡。

encore((HANDLE*)(&handle),ENC_OPT_ENCODE,

&ENC_FRAME,NULL)

接下來,我們要把TargetBuffer 裡面的 bitstream 從圖 26 中 Trancoder( )

這個執行緒送到與StreamerletIs( )共享的 share memory 中,這個動作 則是藉由CapturToServer( )這個 API 來完成。

CaptureToServer(void** StreamServer,int stream_type,int

input_channel,int real_time_capture,Medialtem *mediabuffer ) ;

至於client 端則只要再建立一個執行緒,跟圖 29 的

VideoStatusTimeFunc( )這個執行緒共同分享 raw audiovisual data 即 可,圖29 的 raw audiovisual data 是放在 global_a->Display_bmp 中。

3.2.2 一個 OpenCV 與串流平台結合的例子

在這裡我們利用OpenCV 提供的影像處理函式,與串流平台進行整 合,首先必須先將OpenCV 所提供的 header 檔加入我們的 Project,

如圖31 所示,如此我們就可以使用這些 header 檔所提供給我們的 Structure,OpenCV 提供了一個 Structure IplImage 專門用來處理 bmp 的 raw data,因此不管是再 server 端從攝影機得到的 raw data 或是

圖31 將 header 檔加入 Project 中

Client 端經過解碼而得的 bmp raw data 我們都必須將這些 raw data 的

資料填進這個Structure 中以便後續影像處理的程序,圖 32 則是 IplImage 的成員,我們必須將這個結構裡面的 imageData 指向想要進 行影像處理的bmp raw data,同時還必須設定好 width,height 等資料

圖 32 IplImage 的成員

接下來,為了使用 OpenCV 所提供的 API,我們必須將之前編譯出來 的library 加入到我們的 Project 中,加入的方法如圖 33 所示,在 IDE 介面中選Project->Settings->Link,接著將 highgui.lib,cxcore.lib,cv.lib

加進來,這樣就可以使用OpenCV 的 API。在這邊我們將列出幾個

圖 33 於 IDE 介面中加入 Library OpenCV 與人臉追蹤有關的函式。

CvHaarClassifierCascade* cvLoadHaarClassifierCascade(const char* directory, CvSize orig_window_size );

此函式會載入利用海爾特徵的級聯分類器,OpenCV 已經幫我們訓練 了幾個級聯分類器都是以xml 的格式儲存,例如

haarcascade_frontalface_alt.xml 就是一個以正臉為目標樣本的分類器

CvSeq* cvHaarDetectObjects( const CvArr* image, CvHaarClassifierCascade*

cascade, CvMemStorage* storage, double scale_factor=1.1,int min_neighbors=3, int flags=0,CvSize min_size=cvSize(0,0) );

在這邊image 是待檢驗的影像來源,cascade 則是

cvLoadHaarClassifierCascade( )的回傳值,cvHaarDetectObjects( )會回 傳待檢驗的影像來源有幾個我們有興趣的目標例如”人臉”。

void cvRectangle( CvArr* img, CvPoint pt1, CvPoint pt2, CvScalar color, int thickness=1, int line_type=8, int shift=0 );

繪制簡單、指定粗細或帶填充的矩形,藉由cvHarrDetectObjects( )我 們可以得到目標在影像來源的位置,使用cvRectangle( )將目標以矩 形標記。

圖34 是結合臉部追蹤於 client 端的結果。

圖 34 結合影像處理與串流平台結果

第四章 RTP Payload Format for MPEG-4 Video Stream and MPEG-2 System

在本章我們將介紹一套標準的網路串流Open Source Live555,

Live555 實做了 RTP/RTSP 等標準網路串流協定,此套標準協定將幫 助我們建立一套標準的RTSP 串流平台。

在多媒體串流的應用中,為了達到即時傳輸的特性,我們捨棄了 TCP(Transmission control protocol)傳輸控制協議,因為 TCP 给與每個 封包序號,這個序號除了保證接收端能夠按照順序處理封包外,同時 也保證當封包遺失後,傳送端能夠重傳遺失的封包,但這個重傳的動 作將會影響到我們所要求的即時傳輸特性,因此,在要求即時傳輸的 多媒體串流應用中,我們捨棄了TCP 而改採用 UDP(User datagram protocol)用戶數據報協議,UDP 只提供資料的不可靠交付,也就是當 封包遺失後,UDP 將不採取任何動作,但同時我們也失去了當封包 順序錯誤時的回復能力,為了解決這個問題,我們再UDP 之上再加 上了RTP(Real-time transport protocol),RTP 在其檔頭裡面加了 16bits 的sequence number,利用此 sequence number 我們可以訂正封包順序 錯誤的情形。本章將敘述RTP 如何包裹 mpeg-4 video stream 及 mpeg-2 system,並藉由 live555[11]這套支援 RTP 串流格式平台驗證實際串流 情形。

4.1 RTP(Real-Time Transport Protocol)

加密演算法,padding 的最後一個 byte 則指出有幾個 padding bytes 是 可以忽略的。

Extension(X) : 1bits

當此bit 設定為 1 時,代表這個 RTP 封包除了固定大小的檔頭外,還 放了額外的檔頭資訊。

CSRC count(CC) : 4bits

一個RTP Packet 可能是由兩個以上的來源藉由 Mixer 所組合而成的,

CSRC(Contributing source)則是來源串流的 ID,因此 CSRC count 會說 明這個RTP packet 是由幾個來源組成的。

圖 36 RTP Header Format[12]

Marker (M) : 1 bit

用來說明這個封包是否有重要的事件,例如這個RTP 封包的 Payload data 中有無 frame 的分界。

Payload type(PT) : 7 bits 指出payload data 的類型。

Sequence number : 16 bits

每增加一個RTP packet 這個數字就會線性增加,起始值是隨機的,藉 由這個數字,接收端可以偵測出封包遺失,以及將順序錯誤的封包進

行重新排列。

Timestamp : 32 bits

在即時擷取的影音資料時,這個值代表payload data 中第一個 byte 的 取樣時間。當我們要傳送的資料是已經儲存好的多媒體資料時,這個 值則是代表payload data 的播放時間點,因此只要 payload data 的影像 資料是屬於同一個VOP 則這些 RTP packets 的 Timestamp 的值都會是 一樣的,初始值則是隨機選擇。

SSRC : 32 bits

Synchronization source(SSRC)代表這個 RTP packet 的 ID,也就是當這

個RTP packet 藉由 Mixer 組合成其他 RTP 封包時,其他 RTP 封包的 CCRC 值就是這個 RTP 封包的 SSRC 值。

CSRC list : 0 到 15 個項目,每個項目 32bits

列出這個RTP 封包是由哪些來源所組成的,列出那些來源的 SSRC 值。

4.1.2 RTP Payload Format for MPEG-4 Audio/Visual Streams

RFC3016 中定義了如何切割 MPEG-4 video stream 於 RTP payload 當中,但首先我們要先介紹mpeg4 video stream 中 video packet 的觀 念,在mpeg4 video 的壓縮中,使用 video packet 的目的是為了幫助 解碼器進行錯誤回復,一個vop 當中可能會包含很多個 video packet,

video packet 如圖 36 所示,在網路傳輸的過程當中,常常會有封包遺

失的情形發生,不幸的是,當遺失的封包包含VOP header 資料時,

將會造成屬於這個VOP 的所有 Macorblock 都無法解碼,藉由 video packet 將可以解決這個問題,根據 ISO 14496-2,一個擁有 video packet

的mpeg-4 simpel profile 將會如圖 37 所示,video packet 由許多 Macroblock 所組成,當解碼端讀取到 video packet 時,會開始讀取 Macroblock,每讀取完一個 Macroblock 解碼端會判斷接下來是否出現 video packet header 或 VOP header 若是沒出現這兩個 header 解碼端 就判斷接下來仍然是Macroblock,圖 38 則是 video packet 的細部結構

圖 37 一個有 video packet 的 mpeg-4 video stream

參考圖38,video packet 首先由連續 16 個 0 形成 resync_marker,在 resync_marker 之後會有 Macroblock number,

圖 38 video packet structure

Macroblock number 的 bit 數可以從 1bit 到 14bits,這個 bit 數是跟 VOP 的大小有關,舉例來說,一個vop_width = 320 而 vop_height = 240 參

考表12[6],則這個 Macroblock number 需要 9bits 來表示所有 VOP 的 Macroblock。每一個 VOP 其 Macroblock 都有特定的 Macroblock number 最左上角的 Macroblock 其 Macroblock number 為 0,最右下 角的Macroblock 其 Macroblock number 最大,每增加一個 Macroblock

表 12 Length of macroblock_number code[6]

則Macroblock number 會增加 1,以剛剛那個例子來說,若一個 video packet 其 Macroblock number = 000011001,則代表這個 video packet 所攜帶的Macroblock 位於如圖 39 黑色部份所示的位置,在

圖 39 Macroblock number = 25

Macroblock number 之後就是 1 個 bit 的 HEC(header_extension_code) flag,若是這個 flag 被設定成”1”,則在 HEC 之後會列出這個 video packet 所屬的 VOP 其檔頭資訊,例如 vop_width、vop_height、

vop_time_increment 等資訊,因此藉由 video packet 所攜帶的資訊,就

算之前的rtp 封包遺失,解碼器還是能找到目前這個 rtp 封包其 Macroblock 位於 VOP 何處,同時也可以藉由重複出現的 VOP 檔頭

算之前的rtp 封包遺失,解碼器還是能找到目前這個 rtp 封包其 Macroblock 位於 VOP 何處,同時也可以藉由重複出現的 VOP 檔頭

相關文件