OLD | NEW |
---|---|
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/protocol/rtp_utils.h" | 5 #include "remoting/protocol/rtp_utils.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "third_party/libjingle/source/talk/base/byteorder.h" | 8 #include "third_party/libjingle/source/talk/base/byteorder.h" |
9 | 9 |
10 using talk_base::GetBE16; | 10 using talk_base::GetBE16; |
11 using talk_base::GetBE32; | 11 using talk_base::GetBE32; |
12 using talk_base::SetBE16; | 12 using talk_base::SetBE16; |
13 using talk_base::SetBE32; | 13 using talk_base::SetBE32; |
14 | 14 |
15 namespace remoting { | 15 namespace remoting { |
16 namespace protocol { | 16 namespace protocol { |
17 | 17 |
18 namespace { | 18 namespace { |
19 const int kRtpBaseHeaderSize = 12; | 19 const int kRtpBaseHeaderSize = 12; |
20 const uint8 kRtpVersionNumber = 2; | 20 const uint8 kRtpVersionNumber = 2; |
21 const int kRtpMaxSources = 16; | 21 const int kRtpMaxSources = 16; |
22 } // namespace | 22 } // namespace |
23 | 23 |
24 int GetRtpHeaderSize(int sources) { | 24 static inline uint8 ExtractBits(uint8 byte, int bits_count, int shift) { |
25 DCHECK_GE(sources, 0); | 25 return (byte >> shift) & ((1 << bits_count) - 1); |
26 DCHECK_LT(sources, kRtpMaxSources); | |
27 return kRtpBaseHeaderSize + sources * 4; | |
28 } | 26 } |
29 | 27 |
30 void PackRtpHeader(uint8* buffer, int buffer_size, | 28 // RTP Header format: |
31 const RtpHeader& header) { | 29 // |
30 // 0 1 2 3 | |
31 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | |
32 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
33 // |V=2|P|X| CC |M| PT | sequence number | | |
34 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
35 // | timestamp | | |
36 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
37 // | synchronization source (SSRC) identifier | | |
38 // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | |
awong
2010/11/16 01:08:40
nice diagram.:)
Sergey Ulanov
2010/11/16 01:52:25
I didn't draw it myself, it's from RTP spec :)
| |
39 // | contributing source (CSRC) identifiers | | |
40 // | .... | | |
41 // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | |
42 | |
43 int GetRtpHeaderSize(const RtpHeader& header) { | |
44 DCHECK_GE(header.sources, 0); | |
45 DCHECK_LT(header.sources, kRtpMaxSources); | |
46 return kRtpBaseHeaderSize + header.sources * 4; | |
47 } | |
48 | |
49 void PackRtpHeader(const RtpHeader& header, uint8* buffer, int buffer_size) { | |
32 DCHECK_LT(header.sources, kRtpMaxSources); | 50 DCHECK_LT(header.sources, kRtpMaxSources); |
33 DCHECK_LT(header.payload_type, 1 << 7); | 51 DCHECK_LT(header.payload_type, 1 << 7); |
34 CHECK_GT(buffer_size, GetRtpHeaderSize(header.sources)); | 52 CHECK_GE(buffer_size, GetRtpHeaderSize(header)); |
35 | 53 |
36 buffer[0] = (kRtpVersionNumber << 6) + | 54 buffer[0] = (kRtpVersionNumber << 6) + |
37 ((uint8)header.padding << 5) + | 55 ((uint8)header.padding << 5) + |
38 ((uint8)header.extension << 4) + | 56 ((uint8)header.extension << 4) + |
39 header.sources; | 57 header.sources; |
40 buffer[1] = ((uint8)header.marker << 7) + | 58 buffer[1] = ((uint8)header.marker << 7) + |
41 header.payload_type; | 59 header.payload_type; |
42 SetBE16(buffer + 2, header.sequence_number); | 60 SetBE16(buffer + 2, header.sequence_number); |
43 SetBE32(buffer + 4, header.timestamp); | 61 SetBE32(buffer + 4, header.timestamp); |
44 SetBE32(buffer + 8, header.sync_source_id); | 62 SetBE32(buffer + 8, header.sync_source_id); |
45 | 63 |
46 for (int i = 0; i < header.sources; i++) { | 64 for (int i = 0; i < header.sources; i++) { |
47 SetBE32(buffer + i * 4 + 12, header.source_id[i]); | 65 SetBE32(buffer + i * 4 + 12, header.source_id[i]); |
48 } | 66 } |
49 } | 67 } |
50 | 68 |
51 static inline uint8 ExtractBits(uint8 byte, int bits_count, int shift) { | |
52 return (byte >> shift) & ((1 << bits_count) - 1); | |
53 } | |
54 | |
55 int UnpackRtpHeader(const uint8* buffer, int buffer_size, RtpHeader* header) { | 69 int UnpackRtpHeader(const uint8* buffer, int buffer_size, RtpHeader* header) { |
56 if (buffer_size < kRtpBaseHeaderSize) { | 70 if (buffer_size < kRtpBaseHeaderSize) { |
57 return -1; | 71 return -1; |
58 } | 72 } |
59 | 73 |
60 int version = ExtractBits(buffer[0], 2, 6); | 74 int version = ExtractBits(buffer[0], 2, 6); |
61 if (version != kRtpVersionNumber) { | 75 if (version != kRtpVersionNumber) { |
62 return -1; | 76 return -1; |
63 } | 77 } |
64 | 78 |
65 header->padding = ExtractBits(buffer[0], 1, 5) != 0; | 79 header->padding = ExtractBits(buffer[0], 1, 5) != 0; |
66 header->extension = ExtractBits(buffer[0], 1, 4) != 0; | 80 header->extension = ExtractBits(buffer[0], 1, 4) != 0; |
67 header->sources = ExtractBits(buffer[0], 4, 0); | 81 header->sources = ExtractBits(buffer[0], 4, 0); |
68 | 82 |
69 header->marker = ExtractBits(buffer[1], 1, 7) != 0; | 83 header->marker = ExtractBits(buffer[1], 1, 7) != 0; |
70 header->payload_type = ExtractBits(buffer[1], 7, 0); | 84 header->payload_type = ExtractBits(buffer[1], 7, 0); |
71 | 85 |
72 header->sequence_number = GetBE16(buffer + 2); | 86 header->sequence_number = GetBE16(buffer + 2); |
73 header->timestamp = GetBE32(buffer + 4); | 87 header->timestamp = GetBE32(buffer + 4); |
74 header->sync_source_id = GetBE32(buffer + 8); | 88 header->sync_source_id = GetBE32(buffer + 8); |
75 | 89 |
76 DCHECK_LT(header->sources, 16); | 90 DCHECK_LT(header->sources, 16); |
77 | 91 |
78 if (buffer_size < GetRtpHeaderSize(header->sources)) { | 92 if (buffer_size < GetRtpHeaderSize(*header)) { |
79 return -1; | 93 return -1; |
80 } | 94 } |
81 for (int i = 0; i < header->sources; i++) { | 95 for (int i = 0; i < header->sources; i++) { |
82 header->source_id[i] = GetBE32(buffer + i * 4 + 12); | 96 header->source_id[i] = GetBE32(buffer + i * 4 + 12); |
83 } | 97 } |
84 | 98 |
85 return GetRtpHeaderSize(header->sources); | 99 return GetRtpHeaderSize(*header); |
100 } | |
101 | |
102 // VP8 Payload Descriptor format: | |
103 // | |
104 // 0 1 2 3 | |
105 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 | |
106 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
107 // | RSV |I|N|FI |B| PictureID (integer #bytes) | | |
108 // +-+-+-+-+-+-+-+-+ | | |
109 // : : | |
110 // | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
111 // | : (VP8 data or VP8 payload header; byte aligned)| | |
112 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | |
113 // | |
114 // I: 1 bit | |
Alpha Left Google
2010/11/15 23:46:35
Doesn't say what is RSV in the description.
Sergey Ulanov
2010/11/16 00:22:55
Done.
| |
115 // PictureID present. When set to one, a PictureID is provided after | |
116 // the first byte of the payload descriptor. When set to zero, the | |
117 // PictureID is omitted, and the one-byte payload descriptor is | |
118 // immediately followed by the VP8 payload. | |
119 // | |
120 // N: 1 bit | |
121 // Non-reference frame. When set to one, the frame can be discarded | |
122 // without affecting any other future or past frames. | |
123 // | |
124 // FI: 2 bits | |
125 // Fragmentation information field. This field contains information | |
126 // about the fragmentation of VP8 payloads carried in the RTP | |
127 // packet. The four different values are listed below. | |
128 // | |
129 // FI Fragmentation status | |
130 // 00 The RTP packet contains no fragmented VP8 partitions. The | |
131 // payload is one or several complete partitions. | |
132 // 01 The RTP packet contains the first part of a fragmented | |
133 // partition. The fragment must be placed in its own RTP packet. | |
134 // 10 The RTP packet contains a fragment that is neither the first nor | |
135 // the last part of a fragmented partition. The fragment must be | |
136 // placed in its own RTP packet. | |
137 // 11 The RTP packet contains the last part of a fragmented | |
138 // partition. The fragment must be placed in its own RTP packet. | |
139 // | |
140 // B: 1 bit | |
141 // Beginning VP8 frame. When set to 1 this signals that a new VP8 | |
142 // frame starts in this RTP packet. | |
143 // | |
144 // PictureID: Multiple of 8 bits | |
145 // This is a running index of the frames. The field is present only if | |
146 // the I bit is equal to one. The most significant bit of each byte is | |
147 // an extension flag. The 7 following bits carry (parts of) the | |
148 // PictureID. If the extension flag is one, the PictureID continues in | |
149 // the next byte. If the extension flag is zero, the 7 remaining bits | |
150 // are the last (and least significant) bits in the PictureID. The | |
151 // sender may choose any number of bytes for the PictureID. The | |
152 // PictureID SHALL start on a random number, and SHALL wrap after | |
153 // reaching the maximum ID as chosen by the application | |
154 | |
155 int GetVp8DescriptorSize(const Vp8Descriptor& descriptor) { | |
156 if (descriptor.picture_id == kuint32max) | |
157 return 1; | |
158 int result = 2; | |
159 // We need 1 byte per each 7 bits in picture_id. | |
160 uint32 picture_id = descriptor.picture_id >> 7; | |
161 while (picture_id > 0) { | |
162 picture_id >>= 7; | |
163 ++result; | |
164 } | |
165 return result; | |
166 } | |
167 | |
168 void PackVp8Descriptor(const Vp8Descriptor& descriptor, uint8* buffer, | |
169 int buffer_size) { | |
170 CHECK_GT(buffer_size, 0); | |
Alpha Left Google
2010/11/15 23:46:35
don't use CHECK here.
Sergey Ulanov
2010/11/16 00:22:55
Why? I think, it is better to use CHECK here to pr
awong
2010/11/16 01:08:40
I think CHECK makes sense. allowing a negative si
| |
171 | |
172 buffer[0] = | |
Alpha Left Google
2010/11/15 23:46:35
This doesn't seem to match the diagram above..
Fo
Sergey Ulanov
2010/11/16 00:22:55
B is frame_beginning. It is 7'th bit in the first
Alpha Left Google
2010/11/16 00:47:08
I see.. if this is the case this code won't work o
| |
173 ((uint8)(descriptor.picture_id != kuint32max) << 4) + | |
awong
2010/11/16 01:08:40
Since we're playing in bits, I'm personally happie
Sergey Ulanov
2010/11/16 01:52:25
Agree that | is better here. Fixed here and in Pac
| |
174 ((uint8)descriptor.non_reference_frame << 3) + | |
175 (descriptor.fragmentation_info << 1) + | |
176 ((uint8)descriptor.frame_beginning); | |
177 | |
178 uint32 picture_id = descriptor.picture_id; | |
179 if (picture_id == kuint32max) | |
180 return; | |
181 | |
182 int pos = 1; | |
183 while (picture_id > 0) { | |
184 CHECK_LT(pos, buffer_size); | |
Alpha Left Google
2010/11/15 23:46:35
and here.
Sergey Ulanov
2010/11/16 00:22:55
Same reason as above. The check is fairly cheap, a
| |
185 buffer[pos] = picture_id & 0x7F; | |
186 picture_id >>= 7; | |
187 | |
188 // Set the extension bit if neccessary. | |
189 if (picture_id > 0) | |
190 buffer[pos] |= 1 << 7; | |
191 ++pos; | |
192 } | |
193 } | |
194 | |
195 int UnpackVp8Descriptor(const uint8* buffer, int buffer_size, | |
196 Vp8Descriptor* descriptor) { | |
197 if (buffer_size <= 0) | |
198 return -1; | |
199 | |
200 bool picture_id_present = ExtractBits(buffer[0], 1, 4) != 0; | |
Alpha Left Google
2010/11/15 23:46:35
What about
= !!ExtractBits(...);
Sergey Ulanov
2010/11/16 00:22:55
IMHO, !=0 looks nicer, no?
| |
201 descriptor->non_reference_frame = ExtractBits(buffer[0], 1, 3) != 0; | |
Alpha Left Google
2010/11/15 23:46:35
ditto.
| |
202 descriptor->fragmentation_info = ExtractBits(buffer[0], 2, 1); | |
203 descriptor->frame_beginning = ExtractBits(buffer[0], 1, 0) != 0; | |
Alpha Left Google
2010/11/15 23:46:35
ditto.
| |
204 | |
205 // Decode PictureID if it is present. | |
206 if (picture_id_present) { | |
207 bool extension = true; | |
208 int pos = 1; | |
209 descriptor->picture_id = 0; | |
210 while (extension) { | |
211 if (pos >= buffer_size) | |
212 return -1; | |
213 | |
214 descriptor->picture_id |= buffer[pos] & 0x7F; | |
215 extension = (buffer[pos] & 0x80) != 0; | |
216 pos += 1; | |
217 } | |
218 return pos; | |
219 } else { | |
220 descriptor->picture_id = kuint32max; | |
221 return 1; | |
222 } | |
86 } | 223 } |
87 | 224 |
88 } // namespace protocol | 225 } // namespace protocol |
89 } // namespace remoting | 226 } // namespace remoting |
OLD | NEW |