Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * RTMP input format | |
3 | * Copyright (c) 2009 Konstantin Shishkov | |
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 | #include "libavcodec/bytestream.h" | |
23 | #include "libavutil/avstring.h" | |
24 | #include "libavutil/intfloat.h" | |
25 | #include "avformat.h" | |
26 | ||
27 | #include "rtmppkt.h" | |
28 | #include "flv.h" | |
29 | #include "url.h" | |
30 | ||
31 | void ff_amf_write_bool(uint8_t **dst, int val) | |
32 | { | |
33 | bytestream_put_byte(dst, AMF_DATA_TYPE_BOOL); | |
34 | bytestream_put_byte(dst, val); | |
35 | } | |
36 | ||
37 | void ff_amf_write_number(uint8_t **dst, double val) | |
38 | { | |
39 | bytestream_put_byte(dst, AMF_DATA_TYPE_NUMBER); | |
40 | bytestream_put_be64(dst, av_double2int(val)); | |
41 | } | |
42 | ||
43 | void ff_amf_write_string(uint8_t **dst, const char *str) | |
44 | { | |
45 | bytestream_put_byte(dst, AMF_DATA_TYPE_STRING); | |
46 | bytestream_put_be16(dst, strlen(str)); | |
47 | bytestream_put_buffer(dst, str, strlen(str)); | |
48 | } | |
49 | ||
50 | void ff_amf_write_string2(uint8_t **dst, const char *str1, const char *str2) | |
51 | { | |
52 | int len1 = 0, len2 = 0; | |
53 | if (str1) | |
54 | len1 = strlen(str1); | |
55 | if (str2) | |
56 | len2 = strlen(str2); | |
57 | bytestream_put_byte(dst, AMF_DATA_TYPE_STRING); | |
58 | bytestream_put_be16(dst, len1 + len2); | |
59 | bytestream_put_buffer(dst, str1, len1); | |
60 | bytestream_put_buffer(dst, str2, len2); | |
61 | } | |
62 | ||
63 | void ff_amf_write_null(uint8_t **dst) | |
64 | { | |
65 | bytestream_put_byte(dst, AMF_DATA_TYPE_NULL); | |
66 | } | |
67 | ||
68 | void ff_amf_write_object_start(uint8_t **dst) | |
69 | { | |
70 | bytestream_put_byte(dst, AMF_DATA_TYPE_OBJECT); | |
71 | } | |
72 | ||
73 | void ff_amf_write_field_name(uint8_t **dst, const char *str) | |
74 | { | |
75 | bytestream_put_be16(dst, strlen(str)); | |
76 | bytestream_put_buffer(dst, str, strlen(str)); | |
77 | } | |
78 | ||
79 | void ff_amf_write_object_end(uint8_t **dst) | |
80 | { | |
81 | /* first two bytes are field name length = 0, | |
82 | * AMF object should end with it and end marker | |
83 | */ | |
84 | bytestream_put_be24(dst, AMF_DATA_TYPE_OBJECT_END); | |
85 | } | |
86 | ||
87 | int ff_amf_read_bool(GetByteContext *bc, int *val) | |
88 | { | |
89 | if (bytestream2_get_byte(bc) != AMF_DATA_TYPE_BOOL) | |
90 | return AVERROR_INVALIDDATA; | |
91 | *val = bytestream2_get_byte(bc); | |
92 | return 0; | |
93 | } | |
94 | ||
95 | int ff_amf_read_number(GetByteContext *bc, double *val) | |
96 | { | |
97 | uint64_t read; | |
98 | if (bytestream2_get_byte(bc) != AMF_DATA_TYPE_NUMBER) | |
99 | return AVERROR_INVALIDDATA; | |
100 | read = bytestream2_get_be64(bc); | |
101 | *val = av_int2double(read); | |
102 | return 0; | |
103 | } | |
104 | ||
105 | int ff_amf_get_string(GetByteContext *bc, uint8_t *str, | |
106 | int strsize, int *length) | |
107 | { | |
108 | int stringlen = 0; | |
109 | int readsize; | |
110 | stringlen = bytestream2_get_be16(bc); | |
111 | if (stringlen + 1 > strsize) | |
112 | return AVERROR(EINVAL); | |
113 | readsize = bytestream2_get_buffer(bc, str, stringlen); | |
114 | if (readsize != stringlen) { | |
115 | av_log(NULL, AV_LOG_WARNING, | |
116 | "Unable to read as many bytes as AMF string signaled\n"); | |
117 | } | |
118 | str[readsize] = '\0'; | |
119 | *length = FFMIN(stringlen, readsize); | |
120 | return 0; | |
121 | } | |
122 | ||
123 | int ff_amf_read_string(GetByteContext *bc, uint8_t *str, | |
124 | int strsize, int *length) | |
125 | { | |
126 | if (bytestream2_get_byte(bc) != AMF_DATA_TYPE_STRING) | |
127 | return AVERROR_INVALIDDATA; | |
128 | return ff_amf_get_string(bc, str, strsize, length); | |
129 | } | |
130 | ||
131 | int ff_amf_read_null(GetByteContext *bc) | |
132 | { | |
133 | if (bytestream2_get_byte(bc) != AMF_DATA_TYPE_NULL) | |
134 | return AVERROR_INVALIDDATA; | |
135 | return 0; | |
136 | } | |
137 | ||
138 | int ff_rtmp_check_alloc_array(RTMPPacket **prev_pkt, int *nb_prev_pkt, | |
139 | int channel) | |
140 | { | |
141 | int nb_alloc; | |
142 | RTMPPacket *ptr; | |
143 | if (channel < *nb_prev_pkt) | |
144 | return 0; | |
145 | ||
146 | nb_alloc = channel + 16; | |
147 | // This can't use the av_reallocp family of functions, since we | |
148 | // would need to free each element in the array before the array | |
149 | // itself is freed. | |
150 | ptr = av_realloc_array(*prev_pkt, nb_alloc, sizeof(**prev_pkt)); | |
151 | if (!ptr) | |
152 | return AVERROR(ENOMEM); | |
153 | memset(ptr + *nb_prev_pkt, 0, (nb_alloc - *nb_prev_pkt) * sizeof(*ptr)); | |
154 | *prev_pkt = ptr; | |
155 | *nb_prev_pkt = nb_alloc; | |
156 | return 0; | |
157 | } | |
158 | ||
159 | int ff_rtmp_packet_read(URLContext *h, RTMPPacket *p, | |
160 | int chunk_size, RTMPPacket **prev_pkt, int *nb_prev_pkt) | |
161 | { | |
162 | uint8_t hdr; | |
163 | ||
164 | if (ffurl_read(h, &hdr, 1) != 1) | |
165 | return AVERROR(EIO); | |
166 | ||
167 | return ff_rtmp_packet_read_internal(h, p, chunk_size, prev_pkt, | |
168 | nb_prev_pkt, hdr); | |
169 | } | |
170 | ||
171 | static int rtmp_packet_read_one_chunk(URLContext *h, RTMPPacket *p, | |
172 | int chunk_size, RTMPPacket **prev_pkt_ptr, | |
173 | int *nb_prev_pkt, uint8_t hdr) | |
174 | { | |
175 | ||
176 | uint8_t buf[16]; | |
177 | int channel_id, timestamp, size; | |
178 | uint32_t ts_field; // non-extended timestamp or delta field | |
179 | uint32_t extra = 0; | |
180 | enum RTMPPacketType type; | |
181 | int written = 0; | |
182 | int ret, toread; | |
183 | RTMPPacket *prev_pkt; | |
184 | ||
185 | written++; | |
186 | channel_id = hdr & 0x3F; | |
187 | ||
188 | if (channel_id < 2) { //special case for channel number >= 64 | |
189 | buf[1] = 0; | |
190 | if (ffurl_read_complete(h, buf, channel_id + 1) != channel_id + 1) | |
191 | return AVERROR(EIO); | |
192 | written += channel_id + 1; | |
193 | channel_id = AV_RL16(buf) + 64; | |
194 | } | |
195 | if ((ret = ff_rtmp_check_alloc_array(prev_pkt_ptr, nb_prev_pkt, | |
196 | channel_id)) < 0) | |
197 | return ret; | |
198 | prev_pkt = *prev_pkt_ptr; | |
199 | size = prev_pkt[channel_id].size; | |
200 | type = prev_pkt[channel_id].type; | |
201 | extra = prev_pkt[channel_id].extra; | |
202 | ||
203 | hdr >>= 6; // header size indicator | |
204 | if (hdr == RTMP_PS_ONEBYTE) { | |
205 | ts_field = prev_pkt[channel_id].ts_field; | |
206 | } else { | |
207 | if (ffurl_read_complete(h, buf, 3) != 3) | |
208 | return AVERROR(EIO); | |
209 | written += 3; | |
210 | ts_field = AV_RB24(buf); | |
211 | if (hdr != RTMP_PS_FOURBYTES) { | |
212 | if (ffurl_read_complete(h, buf, 3) != 3) | |
213 | return AVERROR(EIO); | |
214 | written += 3; | |
215 | size = AV_RB24(buf); | |
216 | if (ffurl_read_complete(h, buf, 1) != 1) | |
217 | return AVERROR(EIO); | |
218 | written++; | |
219 | type = buf[0]; | |
220 | if (hdr == RTMP_PS_TWELVEBYTES) { | |
221 | if (ffurl_read_complete(h, buf, 4) != 4) | |
222 | return AVERROR(EIO); | |
223 | written += 4; | |
224 | extra = AV_RL32(buf); | |
225 | } | |
226 | } | |
227 | } | |
228 | if (ts_field == 0xFFFFFF) { | |
229 | if (ffurl_read_complete(h, buf, 4) != 4) | |
230 | return AVERROR(EIO); | |
231 | timestamp = AV_RB32(buf); | |
232 | } else { | |
233 | timestamp = ts_field; | |
234 | } | |
235 | if (hdr != RTMP_PS_TWELVEBYTES) | |
236 | timestamp += prev_pkt[channel_id].timestamp; | |
237 | ||
238 | if (!prev_pkt[channel_id].read) { | |
239 | if ((ret = ff_rtmp_packet_create(p, channel_id, type, timestamp, | |
240 | size)) < 0) | |
241 | return ret; | |
242 | p->read = written; | |
243 | p->offset = 0; | |
244 | prev_pkt[channel_id].ts_field = ts_field; | |
245 | prev_pkt[channel_id].timestamp = timestamp; | |
246 | } else { | |
247 | // previous packet in this channel hasn't completed reading | |
248 | RTMPPacket *prev = &prev_pkt[channel_id]; | |
249 | p->data = prev->data; | |
250 | p->size = prev->size; | |
251 | p->channel_id = prev->channel_id; | |
252 | p->type = prev->type; | |
253 | p->ts_field = prev->ts_field; | |
254 | p->extra = prev->extra; | |
255 | p->offset = prev->offset; | |
256 | p->read = prev->read + written; | |
257 | p->timestamp = prev->timestamp; | |
258 | prev->data = NULL; | |
259 | } | |
260 | p->extra = extra; | |
261 | // save history | |
262 | prev_pkt[channel_id].channel_id = channel_id; | |
263 | prev_pkt[channel_id].type = type; | |
264 | prev_pkt[channel_id].size = size; | |
265 | prev_pkt[channel_id].extra = extra; | |
266 | size = size - p->offset; | |
267 | ||
268 | toread = FFMIN(size, chunk_size); | |
269 | if (ffurl_read_complete(h, p->data + p->offset, toread) != toread) { | |
270 | ff_rtmp_packet_destroy(p); | |
271 | return AVERROR(EIO); | |
272 | } | |
273 | size -= toread; | |
274 | p->read += toread; | |
275 | p->offset += toread; | |
276 | ||
277 | if (size > 0) { | |
278 | RTMPPacket *prev = &prev_pkt[channel_id]; | |
279 | prev->data = p->data; | |
280 | prev->read = p->read; | |
281 | prev->offset = p->offset; | |
282 | p->data = NULL; | |
283 | return AVERROR(EAGAIN); | |
284 | } | |
285 | ||
286 | prev_pkt[channel_id].read = 0; // read complete; reset if needed | |
287 | return p->read; | |
288 | } | |
289 | ||
290 | int ff_rtmp_packet_read_internal(URLContext *h, RTMPPacket *p, int chunk_size, | |
291 | RTMPPacket **prev_pkt, int *nb_prev_pkt, | |
292 | uint8_t hdr) | |
293 | { | |
294 | while (1) { | |
295 | int ret = rtmp_packet_read_one_chunk(h, p, chunk_size, prev_pkt, | |
296 | nb_prev_pkt, hdr); | |
297 | if (ret > 0 || ret != AVERROR(EAGAIN)) | |
298 | return ret; | |
299 | ||
300 | if (ffurl_read(h, &hdr, 1) != 1) | |
301 | return AVERROR(EIO); | |
302 | } | |
303 | } | |
304 | ||
305 | int ff_rtmp_packet_write(URLContext *h, RTMPPacket *pkt, | |
306 | int chunk_size, RTMPPacket **prev_pkt_ptr, | |
307 | int *nb_prev_pkt) | |
308 | { | |
309 | uint8_t pkt_hdr[16], *p = pkt_hdr; | |
310 | int mode = RTMP_PS_TWELVEBYTES; | |
311 | int off = 0; | |
312 | int written = 0; | |
313 | int ret; | |
314 | RTMPPacket *prev_pkt; | |
315 | int use_delta; // flag if using timestamp delta, not RTMP_PS_TWELVEBYTES | |
316 | uint32_t timestamp; // full 32-bit timestamp or delta value | |
317 | ||
318 | if ((ret = ff_rtmp_check_alloc_array(prev_pkt_ptr, nb_prev_pkt, | |
319 | pkt->channel_id)) < 0) | |
320 | return ret; | |
321 | prev_pkt = *prev_pkt_ptr; | |
322 | ||
323 | //if channel_id = 0, this is first presentation of prev_pkt, send full hdr. | |
324 | use_delta = prev_pkt[pkt->channel_id].channel_id && | |
325 | pkt->extra == prev_pkt[pkt->channel_id].extra && | |
326 | pkt->timestamp >= prev_pkt[pkt->channel_id].timestamp; | |
327 | ||
328 | timestamp = pkt->timestamp; | |
329 | if (use_delta) { | |
330 | timestamp -= prev_pkt[pkt->channel_id].timestamp; | |
331 | } | |
332 | if (timestamp >= 0xFFFFFF) { | |
333 | pkt->ts_field = 0xFFFFFF; | |
334 | } else { | |
335 | pkt->ts_field = timestamp; | |
336 | } | |
337 | ||
338 | if (use_delta) { | |
339 | if (pkt->type == prev_pkt[pkt->channel_id].type && | |
340 | pkt->size == prev_pkt[pkt->channel_id].size) { | |
341 | mode = RTMP_PS_FOURBYTES; | |
342 | if (pkt->ts_field == prev_pkt[pkt->channel_id].ts_field) | |
343 | mode = RTMP_PS_ONEBYTE; | |
344 | } else { | |
345 | mode = RTMP_PS_EIGHTBYTES; | |
346 | } | |
347 | } | |
348 | ||
349 | if (pkt->channel_id < 64) { | |
350 | bytestream_put_byte(&p, pkt->channel_id | (mode << 6)); | |
351 | } else if (pkt->channel_id < 64 + 256) { | |
352 | bytestream_put_byte(&p, 0 | (mode << 6)); | |
353 | bytestream_put_byte(&p, pkt->channel_id - 64); | |
354 | } else { | |
355 | bytestream_put_byte(&p, 1 | (mode << 6)); | |
356 | bytestream_put_le16(&p, pkt->channel_id - 64); | |
357 | } | |
358 | if (mode != RTMP_PS_ONEBYTE) { | |
359 | bytestream_put_be24(&p, pkt->ts_field); | |
360 | if (mode != RTMP_PS_FOURBYTES) { | |
361 | bytestream_put_be24(&p, pkt->size); | |
362 | bytestream_put_byte(&p, pkt->type); | |
363 | if (mode == RTMP_PS_TWELVEBYTES) | |
364 | bytestream_put_le32(&p, pkt->extra); | |
365 | } | |
366 | } | |
367 | if (pkt->ts_field == 0xFFFFFF) | |
368 | bytestream_put_be32(&p, timestamp); | |
369 | // save history | |
370 | prev_pkt[pkt->channel_id].channel_id = pkt->channel_id; | |
371 | prev_pkt[pkt->channel_id].type = pkt->type; | |
372 | prev_pkt[pkt->channel_id].size = pkt->size; | |
373 | prev_pkt[pkt->channel_id].timestamp = pkt->timestamp; | |
374 | prev_pkt[pkt->channel_id].ts_field = pkt->ts_field; | |
375 | prev_pkt[pkt->channel_id].extra = pkt->extra; | |
376 | ||
377 | if ((ret = ffurl_write(h, pkt_hdr, p - pkt_hdr)) < 0) | |
378 | return ret; | |
379 | written = p - pkt_hdr + pkt->size; | |
380 | while (off < pkt->size) { | |
381 | int towrite = FFMIN(chunk_size, pkt->size - off); | |
382 | if ((ret = ffurl_write(h, pkt->data + off, towrite)) < 0) | |
383 | return ret; | |
384 | off += towrite; | |
385 | if (off < pkt->size) { | |
386 | uint8_t marker = 0xC0 | pkt->channel_id; | |
387 | if ((ret = ffurl_write(h, &marker, 1)) < 0) | |
388 | return ret; | |
389 | written++; | |
390 | } | |
391 | } | |
392 | return written; | |
393 | } | |
394 | ||
395 | int ff_rtmp_packet_create(RTMPPacket *pkt, int channel_id, RTMPPacketType type, | |
396 | int timestamp, int size) | |
397 | { | |
398 | if (size) { | |
f6fa7814 | 399 | pkt->data = av_realloc(NULL, size); |
2ba45a60 DM |
400 | if (!pkt->data) |
401 | return AVERROR(ENOMEM); | |
402 | } | |
403 | pkt->size = size; | |
404 | pkt->channel_id = channel_id; | |
405 | pkt->type = type; | |
406 | pkt->timestamp = timestamp; | |
407 | pkt->extra = 0; | |
408 | pkt->ts_field = 0; | |
409 | ||
410 | return 0; | |
411 | } | |
412 | ||
413 | void ff_rtmp_packet_destroy(RTMPPacket *pkt) | |
414 | { | |
415 | if (!pkt) | |
416 | return; | |
417 | av_freep(&pkt->data); | |
418 | pkt->size = 0; | |
419 | } | |
420 | ||
421 | int ff_amf_tag_size(const uint8_t *data, const uint8_t *data_end) | |
422 | { | |
423 | const uint8_t *base = data; | |
424 | AMFDataType type; | |
425 | unsigned nb = -1; | |
426 | int parse_key = 1; | |
427 | ||
428 | if (data >= data_end) | |
429 | return -1; | |
430 | switch ((type = *data++)) { | |
431 | case AMF_DATA_TYPE_NUMBER: return 9; | |
432 | case AMF_DATA_TYPE_BOOL: return 2; | |
433 | case AMF_DATA_TYPE_STRING: return 3 + AV_RB16(data); | |
434 | case AMF_DATA_TYPE_LONG_STRING: return 5 + AV_RB32(data); | |
435 | case AMF_DATA_TYPE_NULL: return 1; | |
436 | case AMF_DATA_TYPE_ARRAY: | |
437 | parse_key = 0; | |
438 | case AMF_DATA_TYPE_MIXEDARRAY: | |
439 | nb = bytestream_get_be32(&data); | |
440 | case AMF_DATA_TYPE_OBJECT: | |
441 | while (nb-- > 0 || type != AMF_DATA_TYPE_ARRAY) { | |
442 | int t; | |
443 | if (parse_key) { | |
444 | int size = bytestream_get_be16(&data); | |
445 | if (!size) { | |
446 | data++; | |
447 | break; | |
448 | } | |
449 | if (size < 0 || size >= data_end - data) | |
450 | return -1; | |
451 | data += size; | |
452 | } | |
453 | t = ff_amf_tag_size(data, data_end); | |
454 | if (t < 0 || t >= data_end - data) | |
455 | return -1; | |
456 | data += t; | |
457 | } | |
458 | return data - base; | |
459 | case AMF_DATA_TYPE_OBJECT_END: return 1; | |
460 | default: return -1; | |
461 | } | |
462 | } | |
463 | ||
464 | int ff_amf_get_field_value(const uint8_t *data, const uint8_t *data_end, | |
465 | const uint8_t *name, uint8_t *dst, int dst_size) | |
466 | { | |
467 | int namelen = strlen(name); | |
468 | int len; | |
469 | ||
470 | while (*data != AMF_DATA_TYPE_OBJECT && data < data_end) { | |
471 | len = ff_amf_tag_size(data, data_end); | |
472 | if (len < 0) | |
473 | len = data_end - data; | |
474 | data += len; | |
475 | } | |
476 | if (data_end - data < 3) | |
477 | return -1; | |
478 | data++; | |
479 | for (;;) { | |
480 | int size = bytestream_get_be16(&data); | |
481 | if (!size) | |
482 | break; | |
483 | if (size < 0 || size >= data_end - data) | |
484 | return -1; | |
485 | data += size; | |
486 | if (size == namelen && !memcmp(data-size, name, namelen)) { | |
487 | switch (*data++) { | |
488 | case AMF_DATA_TYPE_NUMBER: | |
489 | snprintf(dst, dst_size, "%g", av_int2double(AV_RB64(data))); | |
490 | break; | |
491 | case AMF_DATA_TYPE_BOOL: | |
492 | snprintf(dst, dst_size, "%s", *data ? "true" : "false"); | |
493 | break; | |
494 | case AMF_DATA_TYPE_STRING: | |
495 | len = bytestream_get_be16(&data); | |
496 | av_strlcpy(dst, data, FFMIN(len+1, dst_size)); | |
497 | break; | |
498 | default: | |
499 | return -1; | |
500 | } | |
501 | return 0; | |
502 | } | |
503 | len = ff_amf_tag_size(data, data_end); | |
504 | if (len < 0 || len >= data_end - data) | |
505 | return -1; | |
506 | data += len; | |
507 | } | |
508 | return -1; | |
509 | } | |
510 | ||
511 | static const char* rtmp_packet_type(int type) | |
512 | { | |
513 | switch (type) { | |
514 | case RTMP_PT_CHUNK_SIZE: return "chunk size"; | |
515 | case RTMP_PT_BYTES_READ: return "bytes read"; | |
516 | case RTMP_PT_PING: return "ping"; | |
517 | case RTMP_PT_SERVER_BW: return "server bandwidth"; | |
518 | case RTMP_PT_CLIENT_BW: return "client bandwidth"; | |
519 | case RTMP_PT_AUDIO: return "audio packet"; | |
520 | case RTMP_PT_VIDEO: return "video packet"; | |
521 | case RTMP_PT_FLEX_STREAM: return "Flex shared stream"; | |
522 | case RTMP_PT_FLEX_OBJECT: return "Flex shared object"; | |
523 | case RTMP_PT_FLEX_MESSAGE: return "Flex shared message"; | |
524 | case RTMP_PT_NOTIFY: return "notification"; | |
525 | case RTMP_PT_SHARED_OBJ: return "shared object"; | |
526 | case RTMP_PT_INVOKE: return "invoke"; | |
527 | case RTMP_PT_METADATA: return "metadata"; | |
528 | default: return "unknown"; | |
529 | } | |
530 | } | |
531 | ||
532 | static void amf_tag_contents(void *ctx, const uint8_t *data, | |
533 | const uint8_t *data_end) | |
534 | { | |
535 | unsigned int size, nb = -1; | |
536 | char buf[1024]; | |
537 | AMFDataType type; | |
538 | int parse_key = 1; | |
539 | ||
540 | if (data >= data_end) | |
541 | return; | |
542 | switch ((type = *data++)) { | |
543 | case AMF_DATA_TYPE_NUMBER: | |
544 | av_log(ctx, AV_LOG_DEBUG, " number %g\n", av_int2double(AV_RB64(data))); | |
545 | return; | |
546 | case AMF_DATA_TYPE_BOOL: | |
547 | av_log(ctx, AV_LOG_DEBUG, " bool %d\n", *data); | |
548 | return; | |
549 | case AMF_DATA_TYPE_STRING: | |
550 | case AMF_DATA_TYPE_LONG_STRING: | |
551 | if (type == AMF_DATA_TYPE_STRING) { | |
552 | size = bytestream_get_be16(&data); | |
553 | } else { | |
554 | size = bytestream_get_be32(&data); | |
555 | } | |
556 | size = FFMIN(size, sizeof(buf) - 1); | |
557 | memcpy(buf, data, size); | |
558 | buf[size] = 0; | |
559 | av_log(ctx, AV_LOG_DEBUG, " string '%s'\n", buf); | |
560 | return; | |
561 | case AMF_DATA_TYPE_NULL: | |
562 | av_log(ctx, AV_LOG_DEBUG, " NULL\n"); | |
563 | return; | |
564 | case AMF_DATA_TYPE_ARRAY: | |
565 | parse_key = 0; | |
566 | case AMF_DATA_TYPE_MIXEDARRAY: | |
567 | nb = bytestream_get_be32(&data); | |
568 | case AMF_DATA_TYPE_OBJECT: | |
569 | av_log(ctx, AV_LOG_DEBUG, " {\n"); | |
570 | while (nb-- > 0 || type != AMF_DATA_TYPE_ARRAY) { | |
571 | int t; | |
572 | if (parse_key) { | |
573 | size = bytestream_get_be16(&data); | |
574 | size = FFMIN(size, sizeof(buf) - 1); | |
575 | if (!size) { | |
576 | av_log(ctx, AV_LOG_DEBUG, " }\n"); | |
577 | data++; | |
578 | break; | |
579 | } | |
580 | memcpy(buf, data, size); | |
581 | buf[size] = 0; | |
582 | if (size >= data_end - data) | |
583 | return; | |
584 | data += size; | |
585 | av_log(ctx, AV_LOG_DEBUG, " %s: ", buf); | |
586 | } | |
587 | amf_tag_contents(ctx, data, data_end); | |
588 | t = ff_amf_tag_size(data, data_end); | |
589 | if (t < 0 || t >= data_end - data) | |
590 | return; | |
591 | data += t; | |
592 | } | |
593 | return; | |
594 | case AMF_DATA_TYPE_OBJECT_END: | |
595 | av_log(ctx, AV_LOG_DEBUG, " }\n"); | |
596 | return; | |
597 | default: | |
598 | return; | |
599 | } | |
600 | } | |
601 | ||
602 | void ff_rtmp_packet_dump(void *ctx, RTMPPacket *p) | |
603 | { | |
604 | av_log(ctx, AV_LOG_DEBUG, "RTMP packet type '%s'(%d) for channel %d, timestamp %d, extra field %d size %d\n", | |
605 | rtmp_packet_type(p->type), p->type, p->channel_id, p->timestamp, p->extra, p->size); | |
606 | if (p->type == RTMP_PT_INVOKE || p->type == RTMP_PT_NOTIFY) { | |
607 | uint8_t *src = p->data, *src_end = p->data + p->size; | |
608 | while (src < src_end) { | |
609 | int sz; | |
610 | amf_tag_contents(ctx, src, src_end); | |
611 | sz = ff_amf_tag_size(src, src_end); | |
612 | if (sz < 0) | |
613 | break; | |
614 | src += sz; | |
615 | } | |
616 | } else if (p->type == RTMP_PT_SERVER_BW){ | |
617 | av_log(ctx, AV_LOG_DEBUG, "Server BW = %d\n", AV_RB32(p->data)); | |
618 | } else if (p->type == RTMP_PT_CLIENT_BW){ | |
619 | av_log(ctx, AV_LOG_DEBUG, "Client BW = %d\n", AV_RB32(p->data)); | |
620 | } else if (p->type != RTMP_PT_AUDIO && p->type != RTMP_PT_VIDEO && p->type != RTMP_PT_METADATA) { | |
621 | int i; | |
622 | for (i = 0; i < p->size; i++) | |
623 | av_log(ctx, AV_LOG_DEBUG, " %02X", p->data[i]); | |
624 | av_log(ctx, AV_LOG_DEBUG, "\n"); | |
625 | } | |
626 | } | |
627 | ||
628 | int ff_amf_match_string(const uint8_t *data, int size, const char *str) | |
629 | { | |
630 | int len = strlen(str); | |
631 | int amf_len, type; | |
632 | ||
633 | if (size < 1) | |
634 | return 0; | |
635 | ||
636 | type = *data++; | |
637 | ||
638 | if (type != AMF_DATA_TYPE_LONG_STRING && | |
639 | type != AMF_DATA_TYPE_STRING) | |
640 | return 0; | |
641 | ||
642 | if (type == AMF_DATA_TYPE_LONG_STRING) { | |
643 | if ((size -= 4 + 1) < 0) | |
644 | return 0; | |
645 | amf_len = bytestream_get_be32(&data); | |
646 | } else { | |
647 | if ((size -= 2 + 1) < 0) | |
648 | return 0; | |
649 | amf_len = bytestream_get_be16(&data); | |
650 | } | |
651 | ||
652 | if (amf_len > size) | |
653 | return 0; | |
654 | ||
655 | if (amf_len != len) | |
656 | return 0; | |
657 | ||
658 | return !memcmp(data, str, len); | |
659 | } |