• 沒有找到結果。

Video 資料的處理流程

三、 Java 多媒體系統動態分析

3.4 系統程式執行的細部運作

3.4.1 Video 資料的處理流程

圖 3-13 系統運作(V)

圖 3-13 為Video 資料的處理流程,接下來會分四個部分說明:(1)擷取影像 資料;(2)抽取影像資料放入Parser;(3)影像資料壓縮處理並送至多工器;(4) RTP 封包建立與發送之前置處理。

(1)擷取影像資料

當處理影像資料時,假設VFWRequestThread(V)已將 camera 開啟,接著 PushThread(V)拿到執行權時,會先睡 0.01 秒,當它再度拿到執行權時會呼叫 yield( )函式。在此函式意謂著 PushThread(V)即可將執行權讓給其他的可執行 狀態的thread。排程器會從可執行狀態的 thread 中,選出一個 thread 來執行,

此函式的目的是為了對多個thread 盡可能的達到公平的排程,把排程的機會給 等待中的thread。

PushThread(V)內會宣告一 buffer,接著會檢查 bufferQ1 可否放入資料,

當檢查結果為不可放入資料時,表示已有逾時的資料未處理,將忽略bufferQ1 內最早存入影像資料的queue,則會將記錄 queue 資料量的參數設為 0(接下說 明相同的程序時都稱之為「清空暫存器」),接著執行checkDataAllocation()函 式,將此次擷取的影像資料存入buffer。再將 buffer 內的資料放入 bufferQ1,

並且通知VFWTransferDataThread(V)隨時可讀取出資料,最後清空 buffer,為 下一張照片作準備。以上流程如圖 3-14。

圖 3-14 擷取影像資料 buffer = null (default value) sleep(10)

yield( )

Can write in bufferQ1 ? If no

Clear one of bufferQ1.

checkDataAllocation(buffer) Put buffer to bufferQ1.

bufferQ1.notifyAll() clear buffer.

(2)抽取影像資料放入Parser

首先VFWTransferDataThread(V)會先檢查可否讀取 bufferQ1 內的影像資 資料,若無法讀取bufferQ1 內的影像資資料時,VFWTransferDataThread(V) 會進入封鎖狀態,預設在 0.25 秒後解除封鎖,或是當PushThread(V)將影像資 料放入bufferQ1 時也會自動解除封鎖。當檢查 bufferQ1 為允許被讀取資料,接 著會設置一doneReading 的旗標,此旗標所代表為 bufferQ1 內的資料是否已被 讀取。

VFWTransferDataThread(V)會執行 pushData( )函式,此函式會詢問是否 能將影像資料放入RawBufferParser 內的 bufferQ2。當可以將影像資料放入 bufferQ2 時,VFWTransferDataThread(V)內會宣告一暫存器 buffer,接著執行 read(buffer)函式,read(buffer)函式同樣會再次檢查可否讀取 bufferQ1 內的影 像資料,當可以讀取資料時,將bufferQ1 的資料存入 buffer,接著清空 bufferQ1 內儲存此次影像資料的queue。doneReading 的旗標將會改為已讀取資料的狀 態。而bufferQ1 無法讀取資料時,就直接忽視此次的資料存取,同樣

doneReading 的旗標將會改為已讀取資料的狀態。將記錄 buffer discard 狀態的 旗標設為 1。

最後將buffer 內的資料放入 bufferQ2,並通知 SourceThread(A)隨時可以 取得影像資料。此時已完成將影像資料放入RawBufferParser 內的 bufferQ2。

以上流程如圖 3-15。

Can read bufferQ1 ? If no

bufferQ1.wait(250) flag:doneReading = false If can read bufferQ1 pushData( )

Can write in bufferQ2?

If no

bufferQ2.wait() buffer=null

read(buffer)

{if Can read bufferQ1 ? buffer=bufferQ1.read() clear bufferQ1

else

buffer.setDiscard(true)}

doneReading = true Put buffer to bufferQ2.

bufferQ2.notifyAll() 圖 3-15 抽取影像資料

(3)影像資料壓縮處理並送至多工器

SourceThread(V)首先會檢查 RawBufferParser 內的 bufferQ2 可否讀取資 料,當可以讀取資料時,則會將bufferQ2 內的資料存入暫存器 buffer1,接著將 buffer1 內的影像資料送入 BasicFilterModule 物件(此物件之建置請參考 2.3.3 節),此物件是負責對媒體資料作壓縮的處理,細節在 3.4.3 會說明。當影像資 料自BasicFilterModule 物件輸出時,為壓縮完成的資料並且切割為預設封包的 長 度 大 小 , 接 著 會 依 序 存 入 buffer1`。接著呼叫 process()函式,會詢問 BasicMuxModule 內的 bufferQ3 可否放入資料,若可以放入資料,則將 buffer1`

內 的 封 包 放 入 bufferQ3 , 並 且 通 知 RawBufferStreamThread(V) 隨 時 可 將 bufferQ3 內的封包做發送處理。

SourceThread(V)將封包放入 bufferQ3 之後,會檢查 buffer1'內紀錄媒體 資料結尾的旗標(FLAG_EOM),若旗標為 1,表示 buffer1`內的資料為媒體資 料結尾,因此drainSync 物件會鎖住 SourceThread(V),此 thread 會被封鎖 3 秒。除非當RawBufferStreamThread(V)將 bufferQ3 內所存的所有資料送出至 網際網路,則RawBufferStreamThread(V)會執行 drainSync.notifyAll()函式,

提早解除SourceThread(V)的封鎖狀態,使得 SourceThread(V)可以對下一張照

片做壓縮的處理。以上流程如圖 3-16。

當SourceThread(V)無法讀取 RawBufferParser 內 bufferQ2 的資料時,呼 叫bufferQ2.wait( )函式,bufferQ2 會封鎖 SourceThread(V),此 thread 會進 入封鎖狀態(如圖 3-16 的左圖),直到VFWTransferDataThread(V)將資料放入 bufferQ2,接著呼叫 bufferQ2.notifyAll( )函式,通知 SourceThread(V)隨時可 以自bufferQ2 取得資料。

當SourceThread(V)欲將資料放入 BasicMuxModule 的 bufferQ3 時,若無 法將資料放入bufferQ3,則表示多工器內的 bufferQ3 已存滿資料,因此,呼叫 bufferQ3.wait( )函式,bufferQ3 會鎖住 SourceThread(V),此 thread 會進入封 鎖狀態(如圖 3-16 之右圖);當RawBufferStreamThread(V)將 bufferQ3 內的資 料讀出時,會執行bufferQ3.notifyAll( )函式,通知 SourceThread(V)隨時可以 將封包放入bufferQ3。

Can read bufferQ2 ? If no, bufferQ2.wait( ) else{ buffer1=null buffer1=bufferQ2.read( ) clear bufferQ2

bufferQ2.notifyAll( )}

Encode buffer1 Multiplexer.process (buffer1’,index)

Æprrocess(buffer1’)

can write in bufferQ3 ? If no

bufferQ3.wait( ) else

buffer=null buffer=setData (buffer1’.getData( )) Put buffer to bufferQ3.

bufferQ3.notifyAll( ) If buffer1’ isEOM,

ÆdrainSync.wait(3000) 圖 3-16 資料壓縮流程

(4) RTP 封包建立與發送之前置處理

當RawBufferStreamThread(V)欲將 BasicMuxModule 內 bufferQ3 的封包 取出時,會先檢查bufferQ3 可否讀出資料,當可以讀出資料時,會呼叫

transferData( )函式,接著會再檢查一次 bufferQ3 可否讀出資料,若為可讀,則 會將資料存入RTPSinkStream 物件(此物件之建置請參考 2.3.4)內 buffer2,接 著將buffer2 內的封包建立 RTP 封包,由下層 UDP 送出,以上流程如圖 3-17。

當RawBufferStreamThread(V)檢查 bufferQ3 可否讀出資料,當不可讀時,

會執行bufferQ3.wait(250),RawBufferStreamThread(V)會在 0.25 秒後醒來,

或 是 直 到 SourceThread(V) 將 資 料 放 入 bufferQ3 時 , 通 知 RawBufferStreamThread(V) 隨 時 可 以 執 行 發 送 封 包 的 處 理 。 當 RawBufferStreamThread(V)呼叫 transferData( )函式時,會再檢查是否可以讀 取 bufferQ3 物件內的資料,若此時發現無法讀取,代表之前將資料放入多工器 時發生了錯誤,因此RawBufferStream Thread(V)會進入封鎖狀態,等待資料再 度放入bufferQ3。

buffer2ÆRTPPackage ÆUDP If cann’t read bufferQ3?

bufferQ3.wait( ) else

buffer2 =bufferQ3.read( ) If buffer2 isEOM

drainSync.notifyAll() Clear bufferQ3 bufferQ3.notifyAll( ) If cann’t read bufferQ3 ?

bufferQ3.wait(250) else

transferData( )

Æsome data ready to be sent over.

圖 3-17 RTP 封包建立與發送之前置處理 3.4.2 Audio資料的處理流程

圖 3-18 為Audio 資料的處理流程,在此小節只說明擷取音訊資料,並將音 訊資料存入Parser 的暫存器,後續資料壓縮和封包傳送的流程,與處理影像資 料時相同,因此後續部份在此不再說明。

1

Can write in bufferQ1?

If no

clear bufferQ1 else

{

data write in bufferQ1 transferData

(DirectSoundStream) }

Can write in bufferQ2 ? If no,bufferQ2.wait( ) else buffer=null read(buffer) {

buffer = bufferQ1.read( ) Clear bufferQ1

}

Put buffer back to bufferQ2 bufferQ2.notifyAll( )

BasicSourceModule RawBufferParser buffer1

bufferQ2

RequestThread(A)

DirectSound PushThread (A) SourceThread(A)

RawBufferStream Thread(A)

Object

buffer Thread Path

Can read bufferQ2 ? If no, bufferQ2.wait( ) else{ buffer1=null buffer1=bufferQ2.read( ) clear bufferQ2

bufferQ2.notifyAll( )}

Encode buffer1 Multiplexer.process (buffer1’,index) Æprrocess(buffer1’)

can write in bufferQ3 ? If no

bufferQ3.wait( ) else

buffer=null buffer=setData (buffer1’.getData( )) Put buffer to bufferQ3.

bufferQ3.notifyAll( ) If buffer1’ isEOM drainSync.wait(3000)

1 2

If cann’t read bufferQ3 ? bufferQ3.wait(250) else

transferData( )

Æsome data ready to be sent over.

If cann’t read bufferQ3?

bufferQ3.wait( ) else

buffer2 =bufferQ3.read( ) If buffer2 isEOM

drainSync. notifyAll() Clear bufferQ3 bufferQ3.notifyAll( )

buffer2ÆRTPPackage ÆUDP

圖 3-18 系統運作(A)

當處理音訊資料時,我們先假設RequestThread(A)已開啟 mike,接著 DirectSoundPushThread(A)將擷取到的資料存入 bufferQ1,因此會檢查 bufferQ1 可否放入資料,當可放入資料時,會將抓取的音訊資料放入 bufferQ1。

接著呼叫transferData( )函式,檢查 RawBufferParser 內的 bufferQ2 可否放入 資料,若可以放入資料時,則會宣告一暫存器buffer,將 bufferQ1 的內的音訊 資料放入buffer,接著清空 bufferQ1,最後將 buffer 內的資料存入 bufferQ2,

以上流程如圖 3-19。當圖 3-19 執行完成時,則表示已將擷取到的媒體資料:音 訊資料個別取出,並放入RawBufferParser 物件,準備進行下一階段的處理。

DirectSoundPushThread(A) 只 會 在 資 料 無 法 放 入 bufferQ2 時 , 呼 叫 bufferQ2.wait( )函式,此 thread 被 bufferQ2 封鎖,其表示 bufferQ2 不允許放 入資料,必須等待SourceThread(A)讀出 bufferQ2 內的資料時,執行 bufferQ2.

notifyAll( )函式通知 DirectSound PushThread(A)隨時可將資料放入 bufferQ2。

以上情形如圖 3-19 的右圖。

Can write in bufferQ1?

If no

clear bufferQ1 else

{

data write in bufferQ1 transferData

(DirectSoundStream) }

Can write in bufferQ2 ? If no,bufferQ2.wait( ) else buffer=null read(buffer)

{

buffer = bufferQ1.read( ) Clear bufferQ1

}

Put buffer back to bufferQ2 bufferQ2.notifyAll( )

圖 3-19 擷取&抽取音訊資料 3.4.3資料壓縮與傳遞

此小節將說明SourceThread 執行媒體資料壓縮處理後,資料傳遞至多工器 的流程,流程如圖 3-20。在圖 3-20 中,紅色的虛線箭頭表被呼叫的函數執行結 束後會再回到do-while 迴圈,分別為圖 3-20 中 NO.3 和 NO.5。SourceThread 將媒體資料自RawBufferParser 物件讀出並存入 buffer1 物件,接著執行

oc.writeReport( )函式,此函式會將 buffer1 物件內的資料放入

BasicInputConnector 物件內的暫存器,並且呼叫 BasicFilterModule.Process() 函式,do-while 迴圈為此函式的主體,以上流程如圖 3-20 中的 NO.1。

接著執行ic.getValidBuffer( )函式,將 BasicInputConnector 物件內的媒體 資料放入inputBuffer 物件,接著設置一 outputBuffer 物件,接著執行

codec.process( , )函式,此函式會呼叫 encoder 對 inputBuffer 物件內的資料以 數個macroblock 做壓縮處理,接著將壓縮後的資料存入 outputBuffer 物件。最 後對outputBuffer 物件設置 sequenceNumber 和 TimeStamp,接著程式執行會 再回到do-while 迴圈,以上流程如圖 3-20 中的 NO.2 與 NO.3。

BasicFilterModule

do{

if (readPendingFlag)

inputBuffer =storedInputBuffer else {

inputBuffer =ic.getValidBuffer( )}

If(!writePending)

outputBuffer =oc.getEmptyBuffer( )

codec.process(inputBuffer , outputBuffer)

oc.writeReport( )

writePending =false

If(!outputBuffer.isEOM()) ){

readPendingFlag =true

storedInputBuffer =inputBuffer } else {readPendingFlag =false}

}while( readPendingFlag =ture )

encodeFrameNative(inputBuffer,outputBuffer)

NativeEncoder.process(Buffer inputBuffer, Buffer outputBuffer) BasicInputConnector

BasicOutputConnector BasicSourceModule

buffer1

oc.writeReport( )

buffer1’ = ic’.getValidBuffer( ) BasicMuxModule

multiplexer.

process(buffer1’, idx)

SourceThread

Ic =BasicInputConnector oc =BasicOutputConnector

圖 3-20 資料壓縮與傳遞之流程

執行oc.writeReport( )函式,此函式會將 outputBuffer 物件內的資料存入 BasicOutputConnector 物件內的暫存器,接著將暫存器內的資料存入 buffer1’,

接著呼叫multiplexer.process(buffer1’, index)將資料放入多工器的 bufferQ3 中,接著程式執行會再回到do-while 迴圈,以上流程如圖 3-20 中的 NO.4 與 NO.5。

設置一邏輯變數:writePending為false,接著執行outputBuffer.isEOM()函 式,此函式會檢查outputBuffer物件內FLAG_EOM旗標,當旗標為1時表示此為 媒體資料最尾端,並回傳布林值:ture。當判定outputBuffer物件內所存資料為媒 體資料最尾端時,會設置一邏輯變數: readPendingFlag =false,當while()判定 readPendingFlag =ture條件不成立時,則此迴圈結束執行,此表示inputBuffer 物件內的資料已全部壓縮完成並送至多工器,Process()函式也就執行結束,以 上流程如圖3-20中的NO.6。

當判定outputBuffer物件內所存資料並非媒體資料最尾端時,會設置一邏輯 變數: readPendingFlag =ture,當while()判定readPendingFlag =ture條件成立 時,則此迴圈會繼續執行,此表示inputBuffer物件內的資料並未處理完畢,會 再次呼叫encoder執行nputBuffer物件內資料後續的壓縮處理。

3.5 RTP 封包建立與發送

由3.4.3節可知經壓縮處理完成的資料已是符合資料串流的媒體格式,

媒體資料將被封裝程RTP封包,送至網際網路,以下以編碼為H.263的影像格式 為例加以說明。

由圖3-13可知,buffer1’物件存放壓縮為H.263格式的資料,buffer1’物件為 數個picture layer的GOB(groups of blocks),而buffer1’物件包含H.263的payload header和sub-bitstream,由國際電信聯盟(ITU)所制定的RFC2190,根據[9],

可得知影像封包架構如圖3-21,因此接著必須設置RTP標頭,以下會說明如何設 置RTP的標頭。

圖3-21 Video Packet Structure

由圖3-13可知buffer2內存放buffer1’的媒體資料,接著檢查buffer2內所存的 媒體資料格式是Audio還是Video。若是判斷為Audio則會執行transmitAudio( ) 函式,Video則會呼叫transmitVideo( )函式,由於Audio和Video處理的流程相 似,因此接下來會以Video的觀點來說明。當呼叫transmitVideo( )函式時,會執 行以下程序:1.建立RTP封包 2.封包送出至網際網路。

1.建立RTP封包: 將媒體資料(buffer2)封裝為RTP封包,設置RTP標頭所有

參數。由圖2-8,執行RTPTransmitter.transmitPacket(buffer2, SendStream) 函式,自buffer2取出TimeStamp的數值,此數值為擷取自系統的時間點,將此 數值乘上90除以Time-scale (1000000),將此數值存入SendStream,此數值為設

參數。由圖2-8,執行RTPTransmitter.transmitPacket(buffer2, SendStream) 函式,自buffer2取出TimeStamp的數值,此數值為擷取自系統的時間點,將此 數值乘上90除以Time-scale (1000000),將此數值存入SendStream,此數值為設

相關文件