程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> Delphi寫的RM文件分析器

Delphi寫的RM文件分析器

編輯:Delphi

最近在研究RM格式文件,我想自己設計一個RM文件編輯器,先設計了這個分析器,界面做的很簡單,不過已經具有簡單的編輯功能:

PS:設計中發現的一個問題:如何判斷最尾部數據包的時間長度?對於音頻數據包的時間長度都是116毫秒,也就是說每個音頻數據包保存了116毫秒的音頻數據,但是對於視頻數據包呢?特別是VBR壓縮格式的視頻數據包,由於我無法判斷最尾部數據包的時間長度,所以用這個程序連接RM文件時生成的文件在播放時會出現錯誤。如果有那位高手知道的一定告訴我,謝謝啦。

從邏輯上講,RM文件由不同的流組成,每個流由一個MDPR頭、零個或多數據包、一個INDX索引塊組成,每個流都有一個唯一的媒體流標識。RM文件的實際存儲結構由各種格式的塊組成,每個塊又可以包含子塊,每個塊都有一個size成員以確定塊的長度,各個塊在文件中的基本順序如下:

.RMF塊
PROP塊,其中num_streams成員決定了會有幾個MDPR塊和INDX塊
MDPR塊,這個塊會有多個
CONT塊
DATA塊,這個塊中包含了多個數據包子塊,不同流的數據包以時間為順序交差存放
INDX塊,這個塊也會有多個,每個塊中包含索引記錄子塊

PROP塊的data_offset成員指出了DATA塊在文件中的位置,DATA塊中的5個成員之後就是第1個數據包,DATA塊中前5個成員占18字節,也就是說PROP塊的data_offset成員加上18就是第1個數據包在文件中的位置。數據包長度都不一樣,由數據包length成員可以確定數據包的長度,讀取第1個數據包確定其長度,計算出下一個數據包在文件中的位置,讀取下一個數據包,以此類推可以實現數據包的歷遍。

MDPR塊、數據包子塊、INDX塊,都有一個stream_number成員來確定媒體流標識,INDX塊中包含了多條記錄,每條記錄包含該媒體流的數據包在文件中的位置,但並不是所有的數據包都有對應的索引記錄,相隔一定的時間差才對應一條索引記錄,我設計的這個程序是相隔一定的時間(音頻流1857毫秒,視頻流83毫秒)後的關鍵幀數據包才建立一條索引記錄(數據包的flags成員為2時表明這個數據包為關鍵幀數據包)。試用不同的編輯器後發現:時間差多少、是否是關鍵幀並不重要,只要相隔一定的時間建立一條索引記錄,播放器都可以正常播放。

利用我這個程序打開一個RM格式文件,左邊的樹形窗口就可以看到各個塊在RM文件中的情況,使用菜單“工具/讀取數據包”可以歷遍查看文件中的所有數據包,使用菜單“工具/索引時間差”可以查看建立索引記錄時的最小時間差,使用菜單“工具/導出索引記錄”可以將索引記錄導出成用空格分隔的文本數據文件,這種文件可以用Excel或記事本打開查看。

說一下RM文件的基本數據格式,RM文件的基本數據由:無符號32位整型(UINT32)、無符號16位整型(UINT16)、無符號8位整型(UINT8)、ASCII字符串組成。需要注意的是整型數據寫入文件的方向與Delphi的數據寫入方向不一致,這種情況在很多文件格式中都會遇到,比如說十進制整型值2904000,在RM文件中為002C4FC0,如果用Delphi的讀寫函數將整型值2904000寫入文件,就會變為C04F2C00,所以從RM文件中讀取或寫入整型數據時需要將數據調一個頭,具體的代碼可以參考RealMediaFile.pas文件中593行開始的代碼,這些代碼負責從TStream流中讀取寫入數據,其它方法都是調用這幾個函數來完成對RM文件中基本數據的讀取。

基本的數據文件格式有兩種,一種是文本格式,一種是二進制數據格式,RM文件是二進制數據格式。

隨便找了一個RM文件,它的第一個INDX塊在16進制編輯器中如下:

002a43f0h: F2 37 C5 11 F2 37 C5 11 F2 49 4E 44 58 00 00 02 ; ?????INDX...
002a4400h: 1A 00 00 00 00 00 25 00 00 00 2A 46 13 00 00 00 ; ......%...*F....
002a4410h: 00 00 00 00 00 03 76 00 00 00 00 00 00 00 00 07 ; ......v.........
002a4420h: 42 00 01 2E EB 00 00 00 83 00 00 00 00 0E 83 00 ; B...?..?....?
002a4430h: 02 50 97 00 00 00 FD 00 00 00 00 15 C5 00 03 79 ; .P?..?....?.y
002a4440h: CA 00 00 01 76 00 00 00 00 1D 07 00 04 5A 24 00 ; ?..v........Z$.
002a4450h: 00 01 E8 00 00 00 00 24 48 00 05 66 A2 00 00 02 ; ..?...$H..f?..
002a4460h: 61 00 00 00 00 2B 8A 00 06 8D 79 00 00 02 D9 00 ; a....+?.峺...?

從002a43f9開始的4個字節49 4E 44 58是object_id,固定為INDX;緊接的4個字節00 00 02 1A是size,轉換為十進制為538表明這個INDX塊的總長度為538字節;緊接的2個字節00 00是object_version,一般為0;緊接的4個字節00 00 00 25是num_indices,表明這個INDX塊中有37條記錄;緊接的2個字節00 00是stream_number,表明這個INDX塊對標識為0的媒體流數據包進行索引;緊接的4個字節00 2A 46 13是next_index_header,表明下一個INDX塊從文件的2770451字節處開始。

接下來就是索引記錄,開始的2個字節00 00是object_version,一般為0;緊接的4個字節00 00 00 00是timestamp,表明該索引記錄對應的數據包時間戳為0毫秒;緊接的4個字節00 00 03 76是offset,表明該索引記錄對應的數據包在文件的886字節處;緊接的4個字節00 00 00 00是packet_count_for_this_packet,表明該索引記錄對應的數據包是該媒體流中的第1個數據包(編號從0開始)。

接下來是第2條索引記錄,開始的2個字節00 00是object_version,一般為0;緊接的4個字節00 00 07 42是timestamp,表明該索引記錄對應的數據包時間戳為1858毫秒;緊接的4個字節00 01 2E EB是offset,表明該索引記錄對應的數據包在文件的77547字節處;緊接的4個字節00 00 00 83是packet_count_for_this_packet,表明該索引記錄對應的數據包是該媒體流中的第132個數據包。

以此類推,可以讀出所有的索引記錄。

確定INDX在文件的位置

有兩種方法:第一種,PROP塊的index_offset成員指出第一個INDX塊在文件中的位置,每個INDX塊的next_index_header成員指出下一個INDX塊的位置。第二種方法,在INDX塊之前是:.RMF塊、PROP塊、多個MDPR塊、CONT塊、DATA塊,這些塊都有size成員指出塊的大小,依次讀取這些塊,將它們的size成員加起來就是第一個INDX塊在文件中的位置,這種方法適合那些PROP塊的index_offset成員有錯誤的RM文件格式。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved