Mercurial > libavformat.hg
annotate rtp_h264.c @ 5639:829780403fc6 libavformat
Make sure the header value used to avoid repeating headers on seeking to the
start and to avoid initializing codecs with missing headers is set for all streams.
Fixes issue 1723.
author | reimar |
---|---|
date | Mon, 08 Feb 2010 20:25:36 +0000 |
parents | c8a47f9a36a8 |
children | 484fceabfad2 |
rev | line source |
---|---|
1460 | 1 /* |
2 * RTP H264 Protocol (RFC3984) | |
4251
77e0c7511d41
cosmetics: Remove pointless period after copyright statement non-sentences.
diego
parents:
4067
diff
changeset
|
3 * Copyright (c) 2006 Ryan Martell |
1460 | 4 * |
5 * This file is part of FFmpeg. | |
6 * | |
7 * FFmpeg is free software; you can redistribute it and/or | |
8 * modify it under the terms of the GNU Lesser General Public | |
9 * License as published by the Free Software Foundation; either | |
10 * version 2.1 of the License, or (at your option) any later version. | |
11 * | |
12 * FFmpeg is distributed in the hope that it will be useful, | |
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Lesser General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Lesser General Public | |
18 * License along with FFmpeg; if not, write to the Free Software | |
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
20 */ | |
21 | |
22 /** | |
4331
49c1d3b27727
Use full internal pathname in doxygen @file directives.
diego
parents:
4291
diff
changeset
|
23 * @file libavformat/rtp_h264.c |
1460 | 24 * @brief H.264 / RTP Code (RFC3984) |
25 * @author Ryan Martell <rdm4@martellventures.com> | |
26 * | |
27 * @note Notes: | |
28 * Notes: | |
29 * This currently supports packetization mode: | |
30 * Single Nal Unit Mode (0), or | |
31 * Non-Interleaved Mode (1). It currently does not support | |
32 * Interleaved Mode (2). (This requires implementing STAP-B, MTAP16, MTAP24, FU-B packet types) | |
33 * | |
34 * @note TODO: | |
35 * 1) RTCP sender reports for udp streams are required.. | |
36 * | |
37 */ | |
38 | |
3286 | 39 #include "libavutil/base64.h" |
40 #include "libavutil/avstring.h" | |
4872 | 41 #include "libavcodec/get_bits.h" |
1460 | 42 #include "avformat.h" |
43 #include "mpegts.h" | |
44 | |
45 #include <unistd.h> | |
1754 | 46 #include "network.h" |
1460 | 47 #include <assert.h> |
48 | |
4388 | 49 #include "rtpdec.h" |
1460 | 50 #include "rtp_h264.h" |
51 | |
52 /** | |
53 RTP/H264 specific private data. | |
54 */ | |
3975
44561554cb7e
Rename RTP payload contexts to PayloadContext, suggested by Luca in
rbultje
parents:
3292
diff
changeset
|
55 struct PayloadContext { |
1460 | 56 unsigned long cookie; ///< sanity check, to make sure we get the pointer we're expecting. |
57 | |
58 //sdp setup parameters | |
59 uint8_t profile_idc; ///< from the sdp setup parameters. | |
60 uint8_t profile_iop; ///< from the sdp setup parameters. | |
61 uint8_t level_idc; ///< from the sdp setup parameters. | |
62 int packetization_mode; ///< from the sdp setup parameters. | |
63 #ifdef DEBUG | |
64 int packet_types_received[32]; | |
65 #endif | |
3975
44561554cb7e
Rename RTP payload contexts to PayloadContext, suggested by Luca in
rbultje
parents:
3292
diff
changeset
|
66 }; |
1460 | 67 |
68 #define MAGIC_COOKIE (0xdeadbeef) ///< Cookie for the extradata; to verify we are what we think we are, and that we haven't been freed. | |
69 #define DEAD_COOKIE (0xdeaddead) ///< Cookie for the extradata; once it is freed. | |
70 | |
71 /* ---------------- private code */ | |
72 static void sdp_parse_fmtp_config_h264(AVStream * stream, | |
3975
44561554cb7e
Rename RTP payload contexts to PayloadContext, suggested by Luca in
rbultje
parents:
3292
diff
changeset
|
73 PayloadContext * h264_data, |
1460 | 74 char *attr, char *value) |
75 { | |
76 AVCodecContext *codec = stream->codec; | |
77 assert(codec->codec_id == CODEC_ID_H264); | |
78 assert(h264_data != NULL); | |
79 | |
80 if (!strcmp(attr, "packetization-mode")) { | |
4628 | 81 av_log(codec, AV_LOG_DEBUG, "RTP Packetization Mode: %d\n", atoi(value)); |
2362 | 82 h264_data->packetization_mode = atoi(value); |
1460 | 83 /* |
84 Packetization Mode: | |
85 0 or not present: Single NAL mode (Only nals from 1-23 are allowed) | |
86 1: Non-interleaved Mode: 1-23, 24 (STAP-A), 28 (FU-A) are allowed. | |
87 2: Interleaved Mode: 25 (STAP-B), 26 (MTAP16), 27 (MTAP24), 28 (FU-A), and 29 (FU-B) are allowed. | |
88 */ | |
89 if (h264_data->packetization_mode > 1) | |
4628 | 90 av_log(codec, AV_LOG_ERROR, |
91 "Interleaved RTP mode is not supported yet."); | |
1460 | 92 } else if (!strcmp(attr, "profile-level-id")) { |
93 if (strlen(value) == 6) { | |
94 char buffer[3]; | |
95 // 6 characters=3 bytes, in hex. | |
96 uint8_t profile_idc; | |
97 uint8_t profile_iop; | |
98 uint8_t level_idc; | |
99 | |
100 buffer[0] = value[0]; buffer[1] = value[1]; buffer[2] = '\0'; | |
101 profile_idc = strtol(buffer, NULL, 16); | |
102 buffer[0] = value[2]; buffer[1] = value[3]; | |
103 profile_iop = strtol(buffer, NULL, 16); | |
104 buffer[0] = value[4]; buffer[1] = value[5]; | |
105 level_idc = strtol(buffer, NULL, 16); | |
106 | |
107 // set the parameters... | |
4628 | 108 av_log(codec, AV_LOG_DEBUG, |
109 "RTP Profile IDC: %x Profile IOP: %x Level: %x\n", | |
1460 | 110 profile_idc, profile_iop, level_idc); |
111 h264_data->profile_idc = profile_idc; | |
112 h264_data->profile_iop = profile_iop; | |
113 h264_data->level_idc = level_idc; | |
114 } | |
115 } else if (!strcmp(attr, "sprop-parameter-sets")) { | |
116 uint8_t start_sequence[]= { 0, 0, 1 }; | |
117 codec->extradata_size= 0; | |
118 codec->extradata= NULL; | |
119 | |
120 while (*value) { | |
121 char base64packet[1024]; | |
122 uint8_t decoded_packet[1024]; | |
123 uint32_t packet_size; | |
124 char *dst = base64packet; | |
125 | |
126 while (*value && *value != ',' | |
127 && (dst - base64packet) < sizeof(base64packet) - 1) { | |
128 *dst++ = *value++; | |
129 } | |
130 *dst++ = '\0'; | |
131 | |
132 if (*value == ',') | |
133 value++; | |
134 | |
135 packet_size= av_base64_decode(decoded_packet, base64packet, sizeof(decoded_packet)); | |
136 if (packet_size) { | |
137 uint8_t *dest= av_malloc(packet_size+sizeof(start_sequence)+codec->extradata_size); | |
138 if(dest) | |
139 { | |
140 if(codec->extradata_size) | |
141 { | |
142 // av_realloc? | |
143 memcpy(dest, codec->extradata, codec->extradata_size); | |
144 av_free(codec->extradata); | |
145 } | |
146 | |
147 memcpy(dest+codec->extradata_size, start_sequence, sizeof(start_sequence)); | |
148 memcpy(dest+codec->extradata_size+sizeof(start_sequence), decoded_packet, packet_size); | |
149 | |
150 codec->extradata= dest; | |
151 codec->extradata_size+= sizeof(start_sequence)+packet_size; | |
152 } else { | |
4628 | 153 av_log(codec, AV_LOG_ERROR, "Unable to allocate memory for extradata!"); |
1460 | 154 } |
155 } | |
156 } | |
4628 | 157 av_log(codec, AV_LOG_DEBUG, "Extradata set to %p (size: %d)!", codec->extradata, codec->extradata_size); |
1460 | 158 } |
159 } | |
160 | |
161 // return 0 on packet, no more left, 1 on packet, 1 on partial packet... | |
4387
5c42816e12c6
Add "AVFormatContext *ctx" (that being the RTSP demuxer's) as first argument
rbultje
parents:
4331
diff
changeset
|
162 static int h264_handle_packet(AVFormatContext *ctx, |
5c42816e12c6
Add "AVFormatContext *ctx" (that being the RTSP demuxer's) as first argument
rbultje
parents:
4331
diff
changeset
|
163 PayloadContext *data, |
3976
64056a0c38ce
Change function prototype of RTPDynamicPayloadHandler.parse_packet() to
rbultje
parents:
3975
diff
changeset
|
164 AVStream *st, |
1460 | 165 AVPacket * pkt, |
166 uint32_t * timestamp, | |
167 const uint8_t * buf, | |
2941
6da0564c9d02
Add a flags field to the RTPDynamicPayloadPacketHandlerProc (PKT_FLAG_*).
rbultje
parents:
2702
diff
changeset
|
168 int len, int flags) |
1460 | 169 { |
170 uint8_t nal = buf[0]; | |
171 uint8_t type = (nal & 0x1f); | |
172 int result= 0; | |
173 uint8_t start_sequence[]= {0, 0, 1}; | |
174 | |
3292 | 175 #ifdef DEBUG |
1460 | 176 assert(data); |
177 assert(data->cookie == MAGIC_COOKIE); | |
3292 | 178 #endif |
1460 | 179 assert(buf); |
180 | |
181 if (type >= 1 && type <= 23) | |
182 type = 1; // simplify the case. (these are all the nal types used internally by the h264 codec) | |
183 switch (type) { | |
184 case 0: // undefined; | |
185 result= -1; | |
186 break; | |
187 | |
188 case 1: | |
189 av_new_packet(pkt, len+sizeof(start_sequence)); | |
190 memcpy(pkt->data, start_sequence, sizeof(start_sequence)); | |
191 memcpy(pkt->data+sizeof(start_sequence), buf, len); | |
192 #ifdef DEBUG | |
193 data->packet_types_received[nal & 0x1f]++; | |
194 #endif | |
195 break; | |
196 | |
197 case 24: // STAP-A (one packet, multiple nals) | |
198 // consume the STAP-A NAL | |
199 buf++; | |
200 len--; | |
201 // first we are going to figure out the total size.... | |
202 { | |
203 int pass= 0; | |
204 int total_length= 0; | |
205 uint8_t *dst= NULL; | |
206 | |
207 for(pass= 0; pass<2; pass++) { | |
208 const uint8_t *src= buf; | |
209 int src_len= len; | |
210 | |
211 do { | |
1673 | 212 uint16_t nal_size = AV_RB16(src); // this going to be a problem if unaligned (can it be?) |
1460 | 213 |
214 // consume the length of the aggregate... | |
215 src += 2; | |
216 src_len -= 2; | |
217 | |
218 if (nal_size <= src_len) { | |
219 if(pass==0) { | |
220 // counting... | |
221 total_length+= sizeof(start_sequence)+nal_size; | |
222 } else { | |
223 // copying | |
224 assert(dst); | |
225 memcpy(dst, start_sequence, sizeof(start_sequence)); | |
226 dst+= sizeof(start_sequence); | |
227 memcpy(dst, src, nal_size); | |
228 #ifdef DEBUG | |
229 data->packet_types_received[*src & 0x1f]++; | |
230 #endif | |
231 dst+= nal_size; | |
232 } | |
233 } else { | |
4628 | 234 av_log(ctx, AV_LOG_ERROR, |
1460 | 235 "nal size exceeds length: %d %d\n", nal_size, src_len); |
236 } | |
237 | |
238 // eat what we handled... | |
239 src += nal_size; | |
240 src_len -= nal_size; | |
241 | |
242 if (src_len < 0) | |
4628 | 243 av_log(ctx, AV_LOG_ERROR, |
1460 | 244 "Consumed more bytes than we got! (%d)\n", src_len); |
245 } while (src_len > 2); // because there could be rtp padding.. | |
246 | |
247 if(pass==0) { | |
248 // now we know the total size of the packet (with the start sequences added) | |
249 av_new_packet(pkt, total_length); | |
250 dst= pkt->data; | |
251 } else { | |
252 assert(dst-pkt->data==total_length); | |
253 } | |
254 } | |
255 } | |
256 break; | |
257 | |
258 case 25: // STAP-B | |
259 case 26: // MTAP-16 | |
260 case 27: // MTAP-24 | |
261 case 29: // FU-B | |
4628 | 262 av_log(ctx, AV_LOG_ERROR, |
1460 | 263 "Unhandled type (%d) (See RFC for implementation details\n", |
264 type); | |
265 result= -1; | |
266 break; | |
267 | |
268 case 28: // FU-A (fragmented nal) | |
269 buf++; | |
270 len--; // skip the fu_indicator | |
271 { | |
272 // these are the same as above, we just redo them here for clarity... | |
273 uint8_t fu_indicator = nal; | |
274 uint8_t fu_header = *buf; // read the fu_header. | |
2154
b849507913da
Remove the unnecessary masking when extracting the start bit in the H.264 RTP
takis
parents:
1983
diff
changeset
|
275 uint8_t start_bit = fu_header >> 7; |
1460 | 276 // uint8_t end_bit = (fu_header & 0x40) >> 6; |
277 uint8_t nal_type = (fu_header & 0x1f); | |
278 uint8_t reconstructed_nal; | |
279 | |
280 // reconstruct this packet's true nal; only the data follows.. | |
281 reconstructed_nal = fu_indicator & (0xe0); // the original nal forbidden bit and NRI are stored in this packet's nal; | |
2155
a661d4e7cab2
Remove the unnecessary masking when reconstructing the NAL unit header in the
takis
parents:
2154
diff
changeset
|
282 reconstructed_nal |= nal_type; |
1460 | 283 |
284 // skip the fu_header... | |
285 buf++; | |
286 len--; | |
287 | |
288 #ifdef DEBUG | |
289 if (start_bit) | |
2156
cfd57cd5252b
Remove the unnecessary masking when counting received packet types in the H.264
takis
parents:
2155
diff
changeset
|
290 data->packet_types_received[nal_type]++; |
1460 | 291 #endif |
292 if(start_bit) { | |
293 // copy in the start sequence, and the reconstructed nal.... | |
294 av_new_packet(pkt, sizeof(start_sequence)+sizeof(nal)+len); | |
295 memcpy(pkt->data, start_sequence, sizeof(start_sequence)); | |
296 pkt->data[sizeof(start_sequence)]= reconstructed_nal; | |
297 memcpy(pkt->data+sizeof(start_sequence)+sizeof(nal), buf, len); | |
298 } else { | |
299 av_new_packet(pkt, len); | |
300 memcpy(pkt->data, buf, len); | |
301 } | |
302 } | |
303 break; | |
304 | |
305 case 30: // undefined | |
306 case 31: // undefined | |
307 default: | |
4628 | 308 av_log(ctx, AV_LOG_ERROR, "Undefined type (%d)", type); |
1460 | 309 result= -1; |
310 break; | |
311 } | |
312 | |
4633
0c69b895a01f
Don't let finalize_packet() touch pkt->stream_index. Instead, let individual
rbultje
parents:
4628
diff
changeset
|
313 pkt->stream_index = st->index; |
0c69b895a01f
Don't let finalize_packet() touch pkt->stream_index. Instead, let individual
rbultje
parents:
4628
diff
changeset
|
314 |
1460 | 315 return result; |
316 } | |
317 | |
318 /* ---------------- public code */ | |
5113
75e51cba276e
Use named initializers and use new/free_context() instead of extradata()
rbultje
parents:
4872
diff
changeset
|
319 static PayloadContext *h264_new_context(void) |
1460 | 320 { |
3975
44561554cb7e
Rename RTP payload contexts to PayloadContext, suggested by Luca in
rbultje
parents:
3292
diff
changeset
|
321 PayloadContext *data = |
44561554cb7e
Rename RTP payload contexts to PayloadContext, suggested by Luca in
rbultje
parents:
3292
diff
changeset
|
322 av_mallocz(sizeof(PayloadContext) + |
1460 | 323 FF_INPUT_BUFFER_PADDING_SIZE); |
324 | |
325 if (data) { | |
326 data->cookie = MAGIC_COOKIE; | |
327 } | |
328 | |
329 return data; | |
330 } | |
331 | |
5113
75e51cba276e
Use named initializers and use new/free_context() instead of extradata()
rbultje
parents:
4872
diff
changeset
|
332 static void h264_free_context(PayloadContext *data) |
1460 | 333 { |
334 #ifdef DEBUG | |
335 int ii; | |
336 | |
337 for (ii = 0; ii < 32; ii++) { | |
338 if (data->packet_types_received[ii]) | |
339 av_log(NULL, AV_LOG_DEBUG, "Received %d packets of type %d\n", | |
340 data->packet_types_received[ii], ii); | |
341 } | |
342 #endif | |
343 | |
344 assert(data); | |
345 assert(data->cookie == MAGIC_COOKIE); | |
346 | |
347 // avoid stale pointers (assert) | |
348 data->cookie = DEAD_COOKIE; | |
349 | |
350 // and clear out this... | |
351 av_free(data); | |
352 } | |
353 | |
4067
8adccfc01be3
Change function prototype of the sdp_parse_a_line in DynamicProtocolHandler.
rbultje
parents:
3976
diff
changeset
|
354 static int parse_h264_sdp_line(AVFormatContext *s, int st_index, |
8adccfc01be3
Change function prototype of the sdp_parse_a_line in DynamicProtocolHandler.
rbultje
parents:
3976
diff
changeset
|
355 PayloadContext *h264_data, const char *line) |
1460 | 356 { |
4067
8adccfc01be3
Change function prototype of the sdp_parse_a_line in DynamicProtocolHandler.
rbultje
parents:
3976
diff
changeset
|
357 AVStream *stream = s->streams[st_index]; |
1460 | 358 AVCodecContext *codec = stream->codec; |
359 const char *p = line; | |
360 | |
361 assert(h264_data->cookie == MAGIC_COOKIE); | |
362 | |
2193
5ce5fad0dfac
replace the uses of old string functions that Reimar missed
mru
parents:
2156
diff
changeset
|
363 if (av_strstart(p, "framesize:", &p)) { |
1460 | 364 char buf1[50]; |
365 char *dst = buf1; | |
366 | |
367 // remove the protocol identifier.. | |
368 while (*p && *p == ' ') p++; // strip spaces. | |
369 while (*p && *p != ' ') p++; // eat protocol identifier | |
370 while (*p && *p == ' ') p++; // strip trailing spaces. | |
5352
c8a47f9a36a8
Fix a typo in rtp_h264.c:parse_h264_sdp_line(). Patch by Gordon Irlam
lucabe
parents:
5113
diff
changeset
|
371 while (*p && *p != '-' && (dst - buf1) < sizeof(buf1) - 1) { |
1460 | 372 *dst++ = *p++; |
373 } | |
374 *dst = '\0'; | |
375 | |
376 // a='framesize:96 320-240' | |
377 // set our parameters.. | |
378 codec->width = atoi(buf1); | |
379 codec->height = atoi(p + 1); // skip the - | |
380 codec->pix_fmt = PIX_FMT_YUV420P; | |
2193
5ce5fad0dfac
replace the uses of old string functions that Reimar missed
mru
parents:
2156
diff
changeset
|
381 } else if (av_strstart(p, "fmtp:", &p)) { |
1460 | 382 char attr[256]; |
383 char value[4096]; | |
384 | |
385 // remove the protocol identifier.. | |
386 while (*p && *p == ' ') p++; // strip spaces. | |
387 while (*p && *p != ' ') p++; // eat protocol identifier | |
388 while (*p && *p == ' ') p++; // strip trailing spaces. | |
389 | |
390 /* loop on each attribute */ | |
391 while (rtsp_next_attr_and_value | |
392 (&p, attr, sizeof(attr), value, sizeof(value))) { | |
393 /* grab the codec extra_data from the config parameter of the fmtp line */ | |
394 sdp_parse_fmtp_config_h264(stream, h264_data, attr, value); | |
395 } | |
2193
5ce5fad0dfac
replace the uses of old string functions that Reimar missed
mru
parents:
2156
diff
changeset
|
396 } else if (av_strstart(p, "cliprect:", &p)) { |
1460 | 397 // could use this if we wanted. |
398 } | |
399 | |
400 av_set_pts_info(stream, 33, 1, 90000); // 33 should be right, because the pts is 64 bit? (done elsewhere; this is a one time thing) | |
401 | |
402 return 0; // keep processing it the normal way... | |
403 } | |
404 | |
405 /** | |
406 This is the structure for expanding on the dynamic rtp protocols (makes everything static. yay!) | |
407 */ | |
408 RTPDynamicProtocolHandler ff_h264_dynamic_handler = { | |
5113
75e51cba276e
Use named initializers and use new/free_context() instead of extradata()
rbultje
parents:
4872
diff
changeset
|
409 .enc_name = "H264", |
75e51cba276e
Use named initializers and use new/free_context() instead of extradata()
rbultje
parents:
4872
diff
changeset
|
410 .codec_type = CODEC_TYPE_VIDEO, |
75e51cba276e
Use named initializers and use new/free_context() instead of extradata()
rbultje
parents:
4872
diff
changeset
|
411 .codec_id = CODEC_ID_H264, |
75e51cba276e
Use named initializers and use new/free_context() instead of extradata()
rbultje
parents:
4872
diff
changeset
|
412 .parse_sdp_a_line = parse_h264_sdp_line, |
75e51cba276e
Use named initializers and use new/free_context() instead of extradata()
rbultje
parents:
4872
diff
changeset
|
413 .open = h264_new_context, |
75e51cba276e
Use named initializers and use new/free_context() instead of extradata()
rbultje
parents:
4872
diff
changeset
|
414 .close = h264_free_context, |
75e51cba276e
Use named initializers and use new/free_context() instead of extradata()
rbultje
parents:
4872
diff
changeset
|
415 .parse_packet = h264_handle_packet |
1460 | 416 }; |