• 沒有找到結果。

第四章 Client 端和 Server 端實作

4.2 Rdesktop 程式

rdesktop 是一個開放原始碼的 RDP Client,其操作平台在 unix、linux 作業系統下,

主要是參照 RDP 協定經過不斷嘗試,進行反組譯完成的程式。由於是開放原始碼,我

們 可 以 了 解 程 式 的 運 作 並 且 改 進 , 這 邊 要 更 改 的 目 的 有 兩 個 。 第 一 是 建 立 MPlayer_Control_Thread(),用來接收 Rdesktop Media Server 所傳來的播放影片的指令。

第二就是建立MPlayer_Display_Thread(),是用於讀取 MPlayer 影像輸出模組寫入到 fifo 檔的影像,並且顯示在rdesktop 的子視窗中。

main()函式前面會根據使用者的設定值和 RDP Server 進行協議,rdp_connect()連線 完成後,就會呼叫 ui_create_window()建立視窗。rdp_main_loop()只有呼叫 rdp_loop(),

並且是持續用迴圈呼叫。除非rdp 連線中斷,才會跳出這個迴圈。這個 rdp_main_loop() 的部分我們不做更動,而是在其他地方加入thread 程式。MPlayer_Control_Thread()是加 在 rdp_connect() 之 後 , 這 是 為 了 確 保 成 功 連 到 RDP Server 時 才 做 連 線 。 而 MPlayer_Display_Thread()則是建立在 ui_create_window()函式返回之前,這是因為建立

子視窗需要rdesktop 母視窗的 ID 參數。圖 4-12 中分叉點是同時執行,當 thread 建立後,

主程式繼續進行。

4.2.1 MPlayer_Control_Thread()函式

這個thread 建立一個 TCP 連線到 Rdesktop Media Server,並且等待播放指令。thread 執 行 時 ,main() 會 傳 入 伺 服 器 的 位 址 , 而 這 個 位 址 可 能 是 網 域 名 稱 。 所 以 利 用 gethostbyname()函式將伺服器網域名稱轉換成 IP 位址。利用 IP 位址建立 TCP 連線,連 線成功就會進入 read()讀取指令。因為是 blocking 模式,所以只要伺服器沒有送資料過 來,thread 就會被 block 在 read()。當有收到伺服器傳來的資料,會判斷是否為播放指令,

否則回到read()等待下個資料。當收到播放指令後,呼叫 MPlayer 播放 Rdesktop Media Server 的影片。如果 TCP 連線中斷,read()會回傳 0。此時會關閉 socket,等待 3 秒後重 新建立連線。

connect

read success?

command==PLAY? execute mplayer

read_byte==0?

close socket Mplayer_Control_Thread

nslookup create socket

sleep 3 seconds

N

圖4-13. MPlayer_Control_Thread()流程圖

4.2.2 MPlayer_Display_Thread()函式

Rdesktop 的視窗開啟是在 ui_create_window(),所以這邊會得到一個 rdesktop 主畫面 的視窗ID。而在開子視窗時需要主視窗 ID 這個參數。

thread 開啟時,先建立一個 fifo 檔案,並且開啟為讀取模式。然後讀取 fifo 檔案,

我們使用的fifo 是 blocking 模式,所以會等待 MPlayer 傳來的資料。當 MPlayer 播放影 片前,vo_rdp.c::config()會寫入 XImage 的結構資料到 fifo。如此我們可以從 fifo 檔案讀 取XImage 的結構資料。這結構資料有圖像的訊息,將其中的 bytes_per_line*height 可以 計算出圖像所佔用的記憶體空間。

再來從fifo 檔讀取 XImage 結構資料和 XImage 的圖像資料。讀取完後,將 XImage 結構資料中的 data 位址改成接收 XImage 圖像資料的位址。這時候利用主視窗 ID 建立 一個子視窗,再利用 XPutImage()將圖像資料貼到子視窗上。完成後回到讀取 fifo 檔,

再接收一次資料,更改XImage 結構資料 data 的位址,再貼圖到子視窗上,一直反復到 影片結束為止。

當影片結束時,vo_rdp.c::unint()會將 XImage 結構的 width 資料更改為 0 並寫入到 fifo 檔。此時讀取到 width 為 0 的 XImage 資料,就會進行關閉子視窗的程序,並且釋放

圖像的記憶體空間。結束後,程式回到第一次讀取 XImage 結構資料的地方,等待下一

個影片播放。

圖 4-14 為從 fifo 讀取資料的順序,第一次是讀取 XImage 的結構資料為 88bytes,

這時候從結構資料計算出一張圖像所需要的記憶體資訊,並且宣告一張圖像資料和一個 XImage 結構所要的記憶體空間。XImage 結構大小為 88 bytes,所以需要的記憶體空間 為(88 + height*bytes_per_line) bytes。第二次開始,讀取的資料為 XImage 結構和圖像資 料,如此讀取直到影片結束。如果影片有 N 張 frame,那最後一次讀取會是第(N+1)張 frame,這張將是無效的,此時我們僅要取得 XImage 結構資料的 width 為 0 的資訊。

圖4-14. 從 fifo 讀取資料

圖 4-15 為 MPlayer_Display_Thread()的流程圖,主要是由兩個迴圈構成。藍色大迴 圈是影片開始播放與結束的流程,綠色小迴圈為播放影片中的貼圖流程。

Mplayer_Display_Thread

Create fifo file

Open fifo file

Read XImage data structure from fifo

Allocate memory for XImage

& XImage data structure

Read XImage & XImage data structure from fifo

image_width==0?

Destory Window Free memory

Close fifo file Create Window

Window opened?

Window opened? XPutImage

Y

圖4-15. MPlayer_Display_Thread()流程圖