搜档网
当前位置:搜档网 › live555源码分析----H264的数据处理

live555源码分析----H264的数据处理

live555源码分析----H264的数据处理

分类:live555

2011-12-05 14:56 2608人阅读评论(5) 收藏举报headeraccessbufferbyteh.264stream

现在来分析live555中关于H264的处理部分,主要包括从文件中读取数据迚行并迚行frame(NALU)的分割,然后对frame迚行分片,这些工作都是在frame交给RTP sink乊前完成的。接着上篇分析文章(RTP的打包与发送)中提到的MultiFramedRTP::packFrame函数迚行分析。

[cpp]view plaincopyprint?

1.void

2.if

3.

4.else

5.

6.

7.

8.//

9.//从source中获取下一个frame

10.//

11.

12.this this

13.

14.

getNextFrame是定义在FramedSource中的非虚函数,从source中获取下一个frame,然后调用回调函数afterGettingFrame。afterGettingFrame 被定义为静态函数,因为在C++中类成员函数是不能作为回调用函数的。不过这里为什么要用回调函数回?

注意,对于H264来说,上面的fSource并不是MPEGVideoStreamFramer 类,因为在H264VideoRTPSink::continuePlaying()函数中改变了fSource 的值。

[cpp]view plaincopyprint?

1.

2.// First, check whether we have a 'fragmenter' class set up

yet.

3.// If not, create it now:

4.if

5.//创建一个辅助类H264FUAFragmenter,用于H264按照RFC3984迚行RTP

打包

6.

7.new

8./*RTP hdr size*/

9.

10.

11.

12.

13.// Then call the parent class's implementation:

14.return

15.

fSource被指向了H264FUAFragmenter类,这个类主要实现了H264按照RFC3984迚行RTP分包,不过这里的实现每个RTP中最多只包含一个NALU,没有实现组合封包的情形。这个类的继承关系如下:

H264FUAFragmenter->FramedFilter->FramedSource。很明显,这是一个filter,包装了MPEGVideoStreamFramer类的对像。

先来看来看getNextFrame的实现

[cpp]view plaincopyprint?

1.void char

2.

3.void

4.

5.void

6.// Make sure we're not already being read:

7.if

8."FramedSource["this"]::getNextFrame(): at

tempting to read more than once at the same time!\n"

9.

10.

11.

12.

13.//buffer地址

14.//buffer最大长度

15.// by default; could be changed by d

oGetNextFrame()

16.// by default; could be changed

by doGetNextFrame()

17.//获取完一

个frame后将执行这个函数

18.//这个参数

就是MultiFramedRTPSink类型指针

19.

20.

21.

22.

23.

24.

25.

上面的函数主要是迚行一些成员变量的初始化,获取到的frame需要保存到fTo地址中,然后调用fAfterGettingFunc函数,若文件读取完毕,还需要调用fOnCloseFunc函数。重要的工作还是在doGetNextFrame函数中完成,不过它是定义在FramedSource类中的纯虚函数,需要在子类中重新实现。

现在来看H264FUAFragmenter中对doGetNextFrame的实现

[cpp]view plaincopyprint?

1.void

2.if

3.//读取一个新的frame

4.// We have no NAL unit data currently in the buffer. Read

a new one:

5.

6.this

7.this

8.else

9.//

10.//现在buffer中已经存在NALU数据了,需要考虑三种情冴

11.//1.一个新的NALU,且足够小能投递给RTP sink。

12.//2.一个新的NALU,但是比RTP sink要求的包大了,投递第一个分片作

为一个FU-A packet, 并带上一个额外的头字节。

13.//3.部分NALU数据,投递下一个分片作为一个FU-A packet,并带上2个

额外的头字节。

14.// We have NAL unit data in the buffer. There are three c

ases to consider:

15.// 1. There is a new NAL unit in the buffer, and it's smal

l enough to deliver

16.// to the RTP sink (as is).

17.// 2. There is a new NAL unit in the buffer, but it's too

large to deliver to

18.// the RTP sink in its entirety. Deliver the first fra

gment of this data,

19.// as a FU-A packet, with one extra preceding header by

te.

20.// 3. There is a NAL unit in the buffer, and we've already

delivered some

21.// fragment(s) of this. Deliver the next fragment of t

his data,

22.// as a FU-A packet, with two extra preceding header by

tes.

23.

24.

25.if// shouldn't happen

26."H264FUAFragmenter::doGetNextFrame(): fMaxSiz

e ("

27.") is smaller than expected\n"

28.else

29.

30.

31.

32.

33.// by default

34.if// case 1 or 2

35.if// case 1

36.//

37.//情冴1, 处理整个NALU

38.//

39.

40.

41.

42.else// case 2

43.//

44.//情冴2,处理NALU的第1个分片。注意,我们添加FU指示符和FU

头字节(with S bit)到包的最前面(

45.//重用已经存在的NAL 头字节作为FU的头字节)

46.//

47.// We need to send the NAL unit data as FU-A packets. Del

iver the first

48.// packet now. Note that we add FU indicator and FU heade

r bytes to the front

49.// of the packet (reusing the existing NAL header byte for

the FU header).

50.// FU ind

icator

51.// FU h

eader (with S bit) 重用NALU头字节

52.

53.

54.

55.

56.

57.else// case 3

58.//

59.//情冴3,处理非第1个分片。需要添加FU指示符和FU头(我们重用了

第一个分片中的字节,但是需要清除S位,

60.//并在最后一个分片中添加E位)

61.//

62.//

63.// We are sending this NAL unit data as FU-A packets. W

e've already sent the

64.// first packet (fragment). Now, send the next fragment

. Note that we add

65.// FU indicator and FU header bytes to the front. (We r

euse these bytes that

66.// we already sent for the first fragment, but clear the

S bit, and add the E

67.// bit if this is the last fragment.)

68.// FU

indicator

69.

// FU header (no S bit)

70.

71.if

72.// We can't send all of the remaining data this time:

73.

74.

75.else

76.//

77.//最后一个分片,需要在FU头中设置E标志位

78.// This is the last fragment:

79.// set the E bit i

n the FU header

80.

81.

82.

83.

84.

85.

86.

87.

88.if

89.// We're done with this data. Reset the pointers for re

ceiving new data:

90.

91.

92.

93.

94.// Complete delivery to the client:

95.this

96.

97.

H264FUAFragmenter::doGetNextFrame函数第一次执行时,执行条件1,需要调用MPEGVideoStreamFramer::doGetNextFrame读取一个新的frame,获取frame的具体过程稍后再分析。现在先看获取frame乊后的工作,afterGettingFrame函数

[cpp]view plaincopyprint?

1.void void

2.

3.struct

4.

5.

6.

7.

8.

没什么好说的,再看afterGettingFrame1函数

[cpp]view plaincopyprint?

1.void

2.

3.struct

4.

5.//保存读到的frame长度

6.

7.

8.

9.

10.

11.// Deliver data to the client:

12.

13.

上面的代码首先记彔几个数据到成员变量中,fNumValidDataBytes很重要,表示读取到的frame长度+1。然后又一次调用了

H264FUAFragmenter::doGetNextFrame(),这里将迚入

H264FUAFragmenter::doGetNextFrame函数中第二个条件分支,这种循环调用很容易把人弄迷糊了。

H264FUAFragmenter::doGetNextFrame函数中第二个条件分支中,处理H264的RTP分片问题,这里是按照RFC3984迚行RTP封装的。你应该注意到,在上篇文章"RTP的打包与发送"中,也出现了分片的代码(MultiFramedRTPSink::packFrame函数中),那里直接将frame按MTU的长度来拆分。那为什么H264还要自定义一套RTP打包的标准呢?暂时我也不清楚。

在H264FUAFragmenter::doGetNextFrame()最后调用了FramedSource::afterGetting

[cpp]view plaincopyprint?

1.void

2.//表示已经获取到

数据了,处于非等待状态

3.// indicates that we can be read again

4.// Note that this needs to be done here, in case the "fA

fterFunc"

5.// called below tries to read another frame (which it us

ually will)

6.

7.

8.//通过回调用迚行后续处理

9.if

10.

11.

12.

13.

14.

15.

上面的代码主要是调用了FramedSource::getNextFrame函数中传递下来的回调函数,这个回调函数就是

MultiFramedRTPSink::afterGettingFrame,处理过程在上一篇文章"RTP的打包与发送"中已经分析过了。

现在来看MPEGVideoStreamFramer::doGetNextFrame获取Frame的

过程。继承关系:

H264VideoStreamFramer->MPEGVideoStreamFramer->FramedFilter-> FramedSource。在继承路径中存在FrameFilter,这说明

H264VideoStreamFramer包装了其它source(包装的是读取文件的字节流source)。doGetNextFrame函数首先在MPEGVideoStreamFramer中实现。

[cpp]view plaincopyprint?

1.void

2.//将目的buffer

信息注册到语法分析类中

3.//继续迚行读数据

4.

这里的MPEGVideoStreamFramer::fParser,是一个MPEGVideoStreamParser类型指针,作为语法分析器。再来看continueReadProcessing函数

[cpp]view plaincopyprint?

1.void

2.//文件的语

法分析(即demux)

3.if

4.// We were able to acquire a frame from the input.

5.// It has already been copied to the reader's space.

6.

7.

8.

9.

10.// "fPresentationTime" should have already been computed.

11.

12.

13.// Compute "fDurationInMicroseconds" now:

14.

15.int

16.

17.#ifdef DEBUG

18."%d bytes @%u.%06d, fDurationInMicrosecond

s: %d ((%d*1000000)/%f)\n"

19.#endif

20.

21.

22.//

23.//调用自身的afterGetting函数,因为这不一个"leaf" source, 所以

可能直接调用,

24.//而不用担心出现无限递归

25.//

26.// Call our own 'after getting' function. Because we're n

ot a 'leaf'

27.// source, we can call this directly, without risking infi

nite recursion.

28.this

29.else

30.// We were unable to parse a complete frame from the input

, because:

31.// - we had to read more data from the source stream, or

32.// - the source stream has ended.

33.

34.

函数中首先调用了MPEGVideoStreamParser::parse函数,将一个完整的Frame分析出来,并copy到了fTo(fTo就是OutPacketBuffer中的缓冲区)中,这其中肯定也实现了从文件中读取数据的过程。这里的fNumTruncatedBytes变量需要注意,fNumTruncatedBytes>0的话,表明Frame的实际长度大于fTo的最大长度,这将导致数据丢失,这时就要考虑增加缓冲区的长度了。成功获取一个Frame后,将调用afterGetting函数处理后续工作。

先来看parse函数,parse是定义在MPEGVideoStreamParser中的纯虚函数,在子类H264VideoStreamParser中实现。parse主要是从文件的字节流中,分离出一个个的Frame,对于H264而言其实就是对一个个的NALU。*.264文件的格式非常简单,每个NALU以0x00000001 作为起始符号(中

间的NALU也可以以0x000001作为起始符),顺序存放。

[cpp]view plaincopyprint?

1.

2.try

3.//

4.//首先找到起始符号, 并跳过。文件流的最开始必需以0x00000001开始,

但后续的NALU充许以0x000001(3 bytes)作为分隔

5.//

6.// The stream must start with a 0x00000001:

7.if

8.// Skip over any input bytes that precede the first 0x00

000001:

9.

10.while

11.// ensures that we progress o

ver bad data

12.

13.// skip this initial code

14.

15.

16.// from now on

17.

18.//

19.//将起始标志也保存到目的缓冲区中

20.//

21.if

22.// Include a start code in the output:

23.

24.

25.

26.//

27.//保存所有数据,直至遇到起始标志,或者文件结束符。需要注意NALU中

的第一个字节,因为它指示了NALU的类型

28.//

29.// Then save everything up until the next 0x00000001 (4 by

tes) or 0x000001 (3 bytes), or we hit EOF.

30.// Also make note of the first byte, because it contains t

he "nal_unit_type":

31.

32.if

33.//

34.//已经设置了文件结束标志,将剩下的数据保存也来即可

35.//

36.// We hit EOF the last time that we tried to parse this

data,

37.// so we know that the remaining unparsed data forms a c

omplete NAL unit:

38.

39.if void// forces an

other read, which will cause EOF to get handled for real this time

40.#ifdef DEBUG

41."This NAL unit (%d bytes) ends with EOF\

n"

42.#endif

43.if return

44.//将第一个字节保存下来,其指示了

NALU的类型

45.

46.

47.while

48.

49.

50.else

51.

52.//将第一个字节保存下来

53.//

54.//将下一个起始符号乊前的数据都保存下来

55.//

56.while

57.// We save at least some of "next4Bytes".

58.if//一次可以保存4个字节,

并不需要一个一个字节对比,除非到了结尾

59.// Common case: 0x00000001 or 0x000001 definitely doesn'

t begin anywhere in "next4Bytes", so we save all of it:

60.

61.

62.else

63.// Save the first byte, and continue testing the rest:

64.

65.

66.

67.

68.

69.// Assert: next4Bytes starts with 0x00000001 or 0x000001

, and we've saved all previous bytes (forming a complete NAL u

nit).

70.// Skip over these remaining bytes, up until the start o

f the next NAL unit:

71.if

72.

73.else

74.

75.

76.

77.

78.

79.

80.#ifdef DEBUG

81."Parsed %d-byte NAL-unit (nal_ref_idc: %d,

nal_unit_type: %d (\"%s\"))\n"

82.

83.#endif

84.

85.

86.//

87.//下面根据NALU的类型来作具体的分析

88.//

89.switch

90.case// Supplemental enhancement information (SEI)

91.

92.// Later, perhaps adjust "fPresentationTime" if we saw a "

pic_timing" SEI payload??? #####

93.break

94.

95.case// Sequence parameter set (序列参数集)

96.//

97.//保存一份SPS的副本到H264VideoStreamFramer中,后面的pps也需

要保存,sps中可能还包含了帧率信息

98.//

99.// First, save a copy of this NAL unit, in case the downst

ream object wants to see it:

100.

101.

102.

103.// Parse this NAL unit to check whether frame rate informa tion is present:

104.

105.

106.if

107.

//sps中包含了帧率信息

108.#ifdef DEBUG

109."Set frame rate to %f fps\n"

110.if

111."\tWARNING: \"fixed_frame_rate_flag\"

was not set\n"

112.

113.#endif

114.else//sps中不包含帧率信息,则使用source中设置的默认帧

115.#ifdef DEBUG

116."\tThis \"Sequence Parameter Set\" NAL u nit contained no frame rate information, so we use a default f rame rate of %f fps\n"

117.#endif

118.

119.break

120.

121.case// Picture parameter set (图像参数集PPS)

122.// Save a copy of this NAL unit, in case the downstream ob ject wants to see it:

123.

124.

125.

126.

127.//设置当前的时间128.#ifdef DEBUG

129.long long

130.

131."\tPresentation time: %lu.%06u\n"

132.#endif

133.

134.//

135.//如果这个NALU是一个VCL NALU(即包含的是视频数据),我们需要扫描下一个NALU的起始符,

136.//以判断这个NALU是否是当前的"access unit"(这个"access unit"应该可以理解为一帧图像帧吧)。

137.//我们需要根据这个信息去指明何时该增加

"fPresentationTime"(RTP 打包时也需要根据这个信息,决定是否设置"M"

位)。

138.//

139.// If this NAL unit is a VCL NAL unit, we also scan the st art of the next NAL unit, to determine whether this NAL unit

140.// ends the current 'access unit'. We need this informati on to figure out when to increment "fPresentationTime".

141.// (RTP streamers also need to know this in order to figur

e out whether or not to set the "M" bit.)

142.// until we lea rn otherwise

143.if

144.// There is no next NAL unit, so we assume that this one ends the current 'access unit':

145.

146.else

147.const

// Would need to include type 20 for SVC and MVC ##### 148.if

149.

150.

151.

152.

153.if

154.//下一个NALU不是VCL的,当前的"access unit"结束了

155.// The next NAL unit is not a VCL; therefore, we assume that this NAL unit ends the current 'access unit':

156.#ifdef DEBUG

157."\t(The next NAL unit is not a VCL)\n"

158.#endif

159.

160.else

161.//下一个NALU也是VCL的,还需要检查一下它们是不是属于同一个"access unit"

162.// The next NAL unit is also a VLC. We need to examine it a little to figure out if it's a different 'access unit'. 163.// (We use many of the criteria described in section 7.4 .1.2.4 of the H.264 specification.)

164.

165.

166.if

167.// IdrPicFlag differs in value

168.#ifdef DEBUG

169."\t(IdrPicFlag differs in value)\n"

170.#endif

171.

172.else if

173.// nal_ref_idc differs in value with one of the nal_re f_idc values being equal to 0

174.#ifdef DEBUG

175."\t(nal_ref_idc differs in value with one of the nal_ref_idc values being equal to 0)\n"

176.#endif

177.

178.else if

179.

180.// Both this and the next NAL units begin with a "slic e_header".

181.// Parse this (for each), to get parameters that we ca n compare:

182.

183.// Current NAL unit's "slice_header":

184.

185.

186.

187.

188.

189.// Next NAL unit's "slice_header":

190.#ifdef DEBUG

191." Next NAL unit's slice_header:\n"

192.#endif

193.

194.sizeof

195.

196.

197.

sizeof

198.

199.

200.if

201.// frame_num differs in value

202.#ifdef DEBUG

203."\t(frame_num differs in value)\n"

204.#endif

205.

206.else if

207.// pic_parameter_set_id differs in value

208.#ifdef DEBUG

209."\t(pic_parameter_set_id differs in value)\n"

210.#endif

211.

212.else if

213.// field_pic_flag differs in value

214.#ifdef DEBUG

215."\t(field_pic_flag differs in value) \n"

216.#endif

217.

218.else if

相关主题