注册 登录
编程论坛 Delphi论坛

[原创]RM文件分析器

yeye55 发布于 2007-03-11 21:29, 11299 次点击
最近在研究RM格式文件,我想自己设计一个RM文件编辑器,先设计了这个分析器,界面做的很简单,不过已经具有简单的编辑功能:

以下是可执行文件

只有本站会员才能查看附件,请 登录


PS:设计中发现的一个问题:如何判断最尾部数据包的时间长度?对于音频数据包的时间长度都是116毫秒,也就是说每个音频数据包保存了116毫秒的音频数据,但是对于视频数据包呢?特别是VBR压缩格式的视频数据包,由于我无法判断最尾部数据包的时间长度,所以用这个程序连接RM文件时生成的文件在播放时会出现错误。如果有那位高手知道的一定告诉我,谢谢啦。

[此贴子已经被作者于2007-4-12 22:12:47编辑过]

46 回复
#2
Liming_6862007-03-12 18:24
先支持一下看,我喜欢这些.
#3
tdjdyq2007-04-11 18:13

好东西哦

#4
tdjdyq2007-04-11 22:03
楼主,你在不?我今天用了你写的RM分析器,很好用的,但是有几个地方不明白,想请教以下你呢!能加我QQ9033466,如果不方便邮箱也可以(sujiewen@gmail.con)!谢谢
#5
tdjdyq2007-04-11 22:16

楼主,我刚接触RM,我对它结构不是很熟悉,所以有写问题想跟你请教!这个问题是这样的,我对.RMF,PROP,MDPR,CONT,DATA,INDX这几部分,的全6个,还是有点了解,对INDX就不知道?还有,我想问下,你是怎么去分析里面实际数据的(就是数据包)以及INDX和文件关系?麻烦解决下我的迷惑!谢谢

#6
yeye552007-04-12 22:08

从逻辑上讲,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或记事本打开查看。

#7
tdjdyq2007-04-12 22:27

太感谢,楼主!谢谢!

#8
tdjdyq2007-04-13 09:24

楼主,为什么INDX下面内容应该是一些数字,怎么是一些乱码呢?难道是编码后的吗?那它有是怎么编码的?能跟我说说吗?谢谢!

#9
yeye552007-04-13 21:48

不太明白你的意思。

说一下RM文件的基本数据格式,RM文件的基本数据由:无符号32位整型(UINT32)、无符号16位整型(UINT16)、无符号8位整型(UINT8)、ASCII字符串组成。需要注意的是整型数据写入文件的方向与Delphi的数据写入方向不一致,这种情况在很多文件格式中都会遇到,比如说十进制整型值2904000,在RM文件中为002C4FC0,如果用Delphi的读写函数将整型值2904000写入文件,就会变为C04F2C00,所以从RM文件中读取或写入整型数据时需要将数据调一个头,具体的代码可以参考RealMediaFile.pas文件中593行开始的代码,这些代码负责从TStream流中读取写入数据,其它方法都是调用这几个函数来完成对RM文件中基本数据的读取。

#10
tdjdyq2007-04-15 09:23

我不明白这里 INDX块,这个块也会有多个,每个块中包含索引--记录子块--按道理说,INDX后面的数据是由ObjectVersion Timestamp Offset NumInterleavePackets这四部分组成,这四部分是应该都是整数,但是是有我用十六进制打开RM文件看,发现INDX后面的数据好象是编码后的数据?

#11
yeye552007-04-15 20:11

基本的数据文件格式有两种,一种是文本格式,一种是二进制数据格式,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个数据包。

以此类推,可以读出所有的索引记录。

#12
tdjdyq2007-04-17 11:12

谢谢楼主!

#13
tdjdyq2007-04-17 15:55
我先去研究音频文件,等有问题再想楼主请教。
#14
tdjdyq2007-04-24 09:00
楼主你好!我想问下,你是根据什么来确定INDX在文件的位置?
#15
yeye552007-04-24 19:37
有两种方法:第一种,PROP块的index_offset成员指出第一个INDX块在文件中的位置,每个INDX块的next_index_header成员指出下一个INDX块的位置。第二种方法,在INDX块之前是:.RMF块、PROP块、多个MDPR块、CONT块、DATA块,这些块都有size成员指出块的大小,依次读取这些块,将它们的size成员加起来就是第一个INDX块在文件中的位置,这种方法适合那些PROP块的index_offset成员有错误的RM文件格式。
#16
tdjdyq2007-04-27 23:13

谢谢!楼主我现在学习RTSP,我有好地方不太明白!我是想从RTSP服务器,下载一个RM文件,分析里面数据,我算出来NumInterleavePackets这个数字就是不对呢。楼主你这方面熟不?能给讲讲不?

#17
yeye552007-04-28 13:39
哦,RTSP协议我不熟。
#18
ouwind2007-04-28 19:21
rtsp做下载的话,只要把数据这部分rtsp的头去掉就可以了
问一下lz:我现在有这样的需求,做拖动效果,不用realplayer的控件
服务器和客户端都是我自己来实现,客户端是调用realplayer来做播放器,协议是http
假设我想让他从中间开始播放,我试了一下把metadata+第n个包开始发给realplayer,结果能播,但是没声音
而且把这些数据保存为文件也只能播放
#19
yeye552007-04-29 20:13
涉及到协议和网络传输方面的,我不太熟悉。
#20
kingstarer2007-05-03 19:13
LZ还在么?

我在写一个小程序改变rm文件的信息,也就是Content_Description块里面的内容,但发现修改了之后的文件不能播放

请问在修改时除了要修改Content_Description块的内容外还要修改哪些内容才行啊?
#21
yeye552007-05-03 20:05
汗!好象没有这个块,如果修改了CONT块(其它位于头部的块也一样)的内容,要重写整个文件,要重新写入数据包,并重新生成索引表,因为修改块的内容会改变块的长度,这会影响到其后数据包的位置,所以要重写整个文件,除非修改的部份和原来的内容长度一致,才可以避免这种情况。
#22
kingstarer2007-05-03 21:07

哦,是修改CONT块,谢谢!

具体是哪一些呢,LZ能说一下吗?

我用你的程序修改信息后发现修改了很多地方,如PROP里面的DataOffset等

#23
yeye552007-05-03 23:19

修改时建议先将整个块读入到内存中,然后进行修改,最后新建一个文件,将修改好的块和原文件中的其它数据复制到新文件中。

比如说:CONT块中的author成员,假设原内容是'abcd',现在修改为'abcd1234',这时要修改author_len成员,因为author的长度改变了,接着修改CONT块的size成员。由于CONT块的长度改变了,紧跟的DATA块、数据包、INDX块的位置也改变了,所以PROP块的data_offset成员、index_offset成员;INDX块的next_index_header成员;所有索引记录的offset成员都要进行修改。然后按照原文件中块的顺序写入到新文件中。

#24
sword12007-05-04 15:32
不错,谢谢:)
#25
kingstarer2007-05-04 20:33
LZ你好

我照着你说的去改后终于能播放了,但又出现了新问题

改动之后的文件不能拖动播放,是不是还要改什么啊?

我改了PROP块的data_offset成员、index_offset成员;INDX块的next_index_header成员;
还有CONT块里面的相关内容
#26
kingstarer2007-05-04 20:45
我用你的分析器打开了由你分析器修改的和由我的程序修改之后的文件,对比看了很多地方都没发现不同,但你的程序修改之后的文件能拖动播放,我的却不能(原文件是能拖动的)
#27
yeye552007-05-05 13:47
索引记录的offset成员也要进行修改。
#28
kingstarer2007-05-05 20:48

哦,谢谢LZ了,终于成功了

#29
kingstarer2007-05-05 20:50

不过我在修改过程中发现文件中的某些的参数并不正确,但是确可以正常播放

不知道哪些参数是关键参数,少了就无法播放??

#30
yeye552007-05-05 22:00
哦!这个我可没有研究过,不过RealPlayer的容错能力相当不错。
#31
kingstarer2007-05-06 19:35
哦,谢谢了

ps:
曾见过CONT块的size有错也不影响播放,结果造成我程序在读文件时出现错,因为我是以它的大小移动文件指针的,后来把程序改成顺序CONT块读成员才没事
#32
tdjdyq2007-05-08 15:39

楼主,有碰到一个问题,我想知道RM文件中,所以的RTP包大小是一样的吗?有没有极个别包比其它RTP包大或小?

#33
tdjdyq2007-05-08 19:04

楼主,在一个纯音频RM文件中,所以的RTP包的大小一样吗?

#34
yeye552007-05-09 14:02
一般来说,在一个文件中音频流数据包的大小是一致的。
#35
tdjdyq2007-05-09 17:06

那再什么情况下,有不一直的呢?谢谢

#36
yeye552007-05-09 18:58
在数据包有错误的情况下;或者RM文件经第三方软件(非官方软件)修改生成,软件处理不当的;或者采用特殊动态编码的数据包(这个非常罕见)。
#37
tdjdyq2007-05-09 19:04

谢谢楼主!

#38
tdjdyq2007-05-09 19:05
今天有碰到一个问题,在PROP中说明一个RTP的数据大小是651,但是实际是640,这是怎么回事呢?

[此贴子已经被作者于2007-5-9 19:08:43编辑过]


#39
yeye552007-05-09 20:01
一般以MDPR的MaxPacketSize和AvgPacketSize为准,PROP的MaxPacketSize和AvgPacketSize经常不准,特别是第三方软件修改过的文件。
#40
tdjdyq2007-05-14 15:14
楼主,你能不能在导出索引数据的时候,同时把音频的索引数据也导出来,现在RM文件查看器好象只能导出音频的索引呢
#41
walkpast2007-05-14 16:48
楼主你那个Media Properties Header中的type_specific_data详细信息是从哪看的,我在Real公开的文档里没找着嘛
https://common.helixcommunity.org/nonav/2003/HCS_SDK_r5/helixsdk.htm
是取得授权了的全文档里吗?
#42
yeye552007-05-14 19:00
以下是引用tdjdyq在2007-5-14 15:14:21的发言:
楼主,你能不能在导出索引数据的时候,同时把音频的索引数据也导出来,现在RM文件查看器好象只能导出音频的索引呢

都有导出吧,以文件名+流号的形式,如“mm1.rmvb流0索引记录.txt”;“mm1.rmvb流1索引记录.txt”;“mm1.rmvb流2索引记录.txt”。

#43
yeye552007-05-14 19:18
以下是引用walkpast在2007-5-14 16:48:24的发言:
楼主你那个Media Properties Header中的type_specific_data详细信息是从哪看的,我在Real公开的文档里没找着嘛
https://common.helixcommunity.org/nonav/2003/HCS_SDK_r5/helixsdk.htm
是取得授权了的全文档里吗?

这部分的内容我也查了很久,后来找到了一个C++格式的头文件rmfftype.h,在这个文件里有关于type_specific_data数据结构的定义,实际上我设计的这个程序在文件格式分析上大部分都参考了这个文件中的定义。但是对于音频流的type_specific_data结构有明显的错误,无法分析出正确的信息,其它格式的流却不会,不知是否是版本的问题。

rmfftype.h文件的下载地址:
https://bbs.bc-cn.net/viewFile.asp?BoardID=9&ID=17120

#44
walkpast2007-05-15 15:28

谢谢楼主,要研究又未取得授权也挺烦的

#45
tdjdyq2007-05-30 15:06

我的RM终于告一段落!要不是楼主这个工具,我有可能研究中!谢谢

#46
你大爷2008-03-29 14:36
好东西 找半天了 太感谢了
好东西 找半天了 太感谢了
#47
swy122013-12-09 20:30
太好了,找了N久,踏破铁鞋无觅处,得来全不费工夫
1