2 * RTMP network protocol
3 * Copyright (c) 2009 Kostya Shishkov
5 * This file is part of FFmpeg.
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.
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.
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
23 * @file libavformat/rtmpproto.c
27 #include "libavcodec/bytestream.h"
28 #include "libavutil/avstring.h"
29 #include "libavutil/lfg.h"
30 #include "libavutil/sha.h"
39 /* we can't use av_log() with URLContext yet... */
40 #if LIBAVFORMAT_VERSION_MAJOR < 53
41 #define LOG_CONTEXT NULL
46 /** RTMP protocol handler state */
48 STATE_START
, ///< client has not done anything yet
49 STATE_HANDSHAKED
, ///< client has performed handshake
50 STATE_CONNECTING
, ///< client connected to server successfully
51 STATE_READY
, ///< client has sent all needed commands and waits for server reply
52 STATE_PLAYING
, ///< client has started receiving multimedia data from server
55 /** protocol handler context */
56 typedef struct RTMPContext
{
57 URLContext
* stream
; ///< TCP stream used in interactions with RTMP server
58 RTMPPacket prev_pkt
[2][RTMP_CHANNELS
]; ///< packet history used when reading and sending packets
59 int chunk_size
; ///< size of the chunks RTMP packets are divided into
60 char playpath
[256]; ///< path to filename to play (with possible "mp4:" prefix)
61 ClientState state
; ///< current state
62 int main_channel_id
; ///< an additional channel ID which is used for some invocations
63 uint8_t* flv_data
; ///< buffer with data for demuxer
64 int flv_size
; ///< current buffer size
65 int flv_off
; ///< number of bytes read from current buffer
66 uint32_t video_ts
; ///< current video timestamp in milliseconds
67 uint32_t audio_ts
; ///< current audio timestamp in milliseconds
70 #define PLAYER_KEY_OPEN_PART_LEN 30 ///< length of partial key used for first client digest signing
71 /** Client key used for digest signing */
72 static const uint8_t rtmp_player_key
[] = {
73 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
74 'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ', '0', '0', '1',
76 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
77 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
78 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
81 #define SERVER_KEY_OPEN_PART_LEN 36 ///< length of partial key used for first server digest signing
82 /** Key used for RTMP server digest signing */
83 static const uint8_t rtmp_server_key
[] = {
84 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
85 'F', 'l', 'a', 's', 'h', ' ', 'M', 'e', 'd', 'i', 'a', ' ',
86 'S', 'e', 'r', 'v', 'e', 'r', ' ', '0', '0', '1',
88 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
89 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
90 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
94 * Generates 'connect' call and sends it to the server.
96 static void gen_connect(URLContext
*s
, RTMPContext
*rt
, const char *proto
,
97 const char *host
, int port
, const char *app
)
103 ff_rtmp_packet_create(&pkt
, RTMP_VIDEO_CHANNEL
, RTMP_PT_INVOKE
, 0, 4096);
106 snprintf(tcurl
, sizeof(tcurl
), "%s://%s:%d/%s", proto
, host
, port
, app
);
107 ff_amf_write_string(&p
, "connect");
108 ff_amf_write_number(&p
, 1.0);
109 ff_amf_write_object_start(&p
);
110 ff_amf_write_field_name(&p
, "app");
111 ff_amf_write_string(&p
, app
);
113 snprintf(ver
, sizeof(ver
), "%s %d,%d,%d,%d", RTMP_CLIENT_PLATFORM
, RTMP_CLIENT_VER1
,
114 RTMP_CLIENT_VER2
, RTMP_CLIENT_VER3
, RTMP_CLIENT_VER4
);
115 ff_amf_write_field_name(&p
, "flashVer");
116 ff_amf_write_string(&p
, ver
);
117 ff_amf_write_field_name(&p
, "tcUrl");
118 ff_amf_write_string(&p
, tcurl
);
119 ff_amf_write_field_name(&p
, "fpad");
120 ff_amf_write_bool(&p
, 0);
121 ff_amf_write_field_name(&p
, "capabilities");
122 ff_amf_write_number(&p
, 15.0);
123 ff_amf_write_field_name(&p
, "audioCodecs");
124 ff_amf_write_number(&p
, 1639.0);
125 ff_amf_write_field_name(&p
, "videoCodecs");
126 ff_amf_write_number(&p
, 252.0);
127 ff_amf_write_field_name(&p
, "videoFunction");
128 ff_amf_write_number(&p
, 1.0);
129 ff_amf_write_object_end(&p
);
131 pkt
.data_size
= p
- pkt
.data
;
133 ff_rtmp_packet_write(rt
->stream
, &pkt
, rt
->chunk_size
, rt
->prev_pkt
[1]);
137 * Generates 'createStream' call and sends it to the server. It should make
138 * the server allocate some channel for media streams.
140 static void gen_create_stream(URLContext
*s
, RTMPContext
*rt
)
145 av_log(LOG_CONTEXT
, AV_LOG_DEBUG
, "Creating stream...\n");
146 ff_rtmp_packet_create(&pkt
, RTMP_VIDEO_CHANNEL
, RTMP_PT_INVOKE
, 0, 25);
149 ff_amf_write_string(&p
, "createStream");
150 ff_amf_write_number(&p
, 3.0);
151 ff_amf_write_null(&p
);
153 ff_rtmp_packet_write(rt
->stream
, &pkt
, rt
->chunk_size
, rt
->prev_pkt
[1]);
154 ff_rtmp_packet_destroy(&pkt
);
158 * Generates 'play' call and sends it to the server, then pings the server
159 * to start actual playing.
161 static void gen_play(URLContext
*s
, RTMPContext
*rt
)
166 av_log(LOG_CONTEXT
, AV_LOG_DEBUG
, "Sending play command for '%s'\n", rt
->playpath
);
167 ff_rtmp_packet_create(&pkt
, RTMP_VIDEO_CHANNEL
, RTMP_PT_INVOKE
, 0,
168 20 + strlen(rt
->playpath
));
169 pkt
.extra
= rt
->main_channel_id
;
172 ff_amf_write_string(&p
, "play");
173 ff_amf_write_number(&p
, 0.0);
174 ff_amf_write_null(&p
);
175 ff_amf_write_string(&p
, rt
->playpath
);
177 ff_rtmp_packet_write(rt
->stream
, &pkt
, rt
->chunk_size
, rt
->prev_pkt
[1]);
178 ff_rtmp_packet_destroy(&pkt
);
180 // set client buffer time disguised in ping packet
181 ff_rtmp_packet_create(&pkt
, RTMP_NETWORK_CHANNEL
, RTMP_PT_PING
, 1, 10);
184 bytestream_put_be16(&p
, 3);
185 bytestream_put_be32(&p
, 1);
186 bytestream_put_be32(&p
, 256); //TODO: what is a good value here?
188 ff_rtmp_packet_write(rt
->stream
, &pkt
, rt
->chunk_size
, rt
->prev_pkt
[1]);
189 ff_rtmp_packet_destroy(&pkt
);
193 * Generates ping reply and sends it to the server.
195 static void gen_pong(URLContext
*s
, RTMPContext
*rt
, RTMPPacket
*ppkt
)
200 ff_rtmp_packet_create(&pkt
, RTMP_NETWORK_CHANNEL
, RTMP_PT_PING
, ppkt
->timestamp
+ 1, 6);
202 bytestream_put_be16(&p
, 7);
203 bytestream_put_be32(&p
, AV_RB32(ppkt
->data
+2) + 1);
204 ff_rtmp_packet_write(rt
->stream
, &pkt
, rt
->chunk_size
, rt
->prev_pkt
[1]);
205 ff_rtmp_packet_destroy(&pkt
);
208 //TODO: Move HMAC code somewhere. Eventually.
209 #define HMAC_IPAD_VAL 0x36
210 #define HMAC_OPAD_VAL 0x5C
213 * Calculates HMAC-SHA2 digest for RTMP handshake packets.
215 * @param src input buffer
216 * @param len input buffer length (should be 1536)
217 * @param gap offset in buffer where 32 bytes should not be taken into account
218 * when calculating digest (since it will be used to store that digest)
219 * @param key digest key
220 * @param keylen digest key length
221 * @param dst buffer where calculated digest will be stored (32 bytes)
223 static void rtmp_calc_digest(const uint8_t *src
, int len
, int gap
,
224 const uint8_t *key
, int keylen
, uint8_t *dst
)
227 uint8_t hmac_buf
[64+32] = {0};
230 sha
= av_mallocz(av_sha_size
);
233 memcpy(hmac_buf
, key
, keylen
);
235 av_sha_init(sha
, 256);
236 av_sha_update(sha
,key
, keylen
);
237 av_sha_final(sha
, hmac_buf
);
239 for (i
= 0; i
< 64; i
++)
240 hmac_buf
[i
] ^= HMAC_IPAD_VAL
;
242 av_sha_init(sha
, 256);
243 av_sha_update(sha
, hmac_buf
, 64);
245 av_sha_update(sha
, src
, len
);
246 } else { //skip 32 bytes used for storing digest
247 av_sha_update(sha
, src
, gap
);
248 av_sha_update(sha
, src
+ gap
+ 32, len
- gap
- 32);
250 av_sha_final(sha
, hmac_buf
+ 64);
252 for (i
= 0; i
< 64; i
++)
253 hmac_buf
[i
] ^= HMAC_IPAD_VAL
^ HMAC_OPAD_VAL
; //reuse XORed key for opad
254 av_sha_init(sha
, 256);
255 av_sha_update(sha
, hmac_buf
, 64+32);
256 av_sha_final(sha
, dst
);
262 * Puts HMAC-SHA2 digest of packet data (except for the bytes where this digest
263 * will be stored) into that packet.
265 * @param buf handshake data (1536 bytes)
266 * @return offset to the digest inside input data
268 static int rtmp_handshake_imprint_with_digest(uint8_t *buf
)
270 int i
, digest_pos
= 0;
272 for (i
= 8; i
< 12; i
++)
273 digest_pos
+= buf
[i
];
274 digest_pos
= (digest_pos
% 728) + 12;
276 rtmp_calc_digest(buf
, RTMP_HANDSHAKE_PACKET_SIZE
, digest_pos
,
277 rtmp_player_key
, PLAYER_KEY_OPEN_PART_LEN
,
283 * Verifies that the received server response has the expected digest value.
285 * @param buf handshake data received from the server (1536 bytes)
286 * @param off position to search digest offset from
287 * @return 0 if digest is valid, digest position otherwise
289 static int rtmp_validate_digest(uint8_t *buf
, int off
)
291 int i
, digest_pos
= 0;
294 for (i
= 0; i
< 4; i
++)
295 digest_pos
+= buf
[i
+ off
];
296 digest_pos
= (digest_pos
% 728) + off
+ 4;
298 rtmp_calc_digest(buf
, RTMP_HANDSHAKE_PACKET_SIZE
, digest_pos
,
299 rtmp_server_key
, SERVER_KEY_OPEN_PART_LEN
,
301 if (!memcmp(digest
, buf
+ digest_pos
, 32))
307 * Performs handshake with the server by means of exchanging pseudorandom data
308 * signed with HMAC-SHA2 digest.
310 * @return 0 if handshake succeeds, negative value otherwise
312 static int rtmp_handshake(URLContext
*s
, RTMPContext
*rt
)
315 uint8_t tosend
[RTMP_HANDSHAKE_PACKET_SIZE
+1] = {
316 3, // unencrypted data
317 0, 0, 0, 0, // client uptime
323 uint8_t clientdata
[RTMP_HANDSHAKE_PACKET_SIZE
];
324 uint8_t serverdata
[RTMP_HANDSHAKE_PACKET_SIZE
+1];
326 int server_pos
, client_pos
;
329 av_log(LOG_CONTEXT
, AV_LOG_DEBUG
, "Handshaking...\n");
331 av_lfg_init(&rnd
, 0xDEADC0DE);
332 // generate handshake packet - 1536 bytes of pseudorandom data
333 for (i
= 9; i
<= RTMP_HANDSHAKE_PACKET_SIZE
; i
++)
334 tosend
[i
] = av_lfg_get(&rnd
) >> 24;
335 client_pos
= rtmp_handshake_imprint_with_digest(tosend
+ 1);
337 url_write(rt
->stream
, tosend
, RTMP_HANDSHAKE_PACKET_SIZE
+ 1);
338 i
= url_read_complete(rt
->stream
, serverdata
, RTMP_HANDSHAKE_PACKET_SIZE
+ 1);
339 if (i
!= RTMP_HANDSHAKE_PACKET_SIZE
+ 1) {
340 av_log(LOG_CONTEXT
, AV_LOG_ERROR
, "Cannot read RTMP handshake response\n");
343 i
= url_read_complete(rt
->stream
, clientdata
, RTMP_HANDSHAKE_PACKET_SIZE
);
344 if (i
!= RTMP_HANDSHAKE_PACKET_SIZE
) {
345 av_log(LOG_CONTEXT
, AV_LOG_ERROR
, "Cannot read RTMP handshake response\n");
349 av_log(LOG_CONTEXT
, AV_LOG_DEBUG
, "Server version %d.%d.%d.%d\n",
350 serverdata
[5], serverdata
[6], serverdata
[7], serverdata
[8]);
352 server_pos
= rtmp_validate_digest(serverdata
+ 1, 772);
354 server_pos
= rtmp_validate_digest(serverdata
+ 1, 8);
356 av_log(LOG_CONTEXT
, AV_LOG_ERROR
, "Server response validating failed\n");
361 rtmp_calc_digest(tosend
+ 1 + client_pos
, 32, 0,
362 rtmp_server_key
, sizeof(rtmp_server_key
),
364 rtmp_calc_digest(clientdata
, RTMP_HANDSHAKE_PACKET_SIZE
-32, 0,
367 if (memcmp(digest
, clientdata
+ RTMP_HANDSHAKE_PACKET_SIZE
- 32, 32)) {
368 av_log(LOG_CONTEXT
, AV_LOG_ERROR
, "Signature mismatch\n");
372 for (i
= 0; i
< RTMP_HANDSHAKE_PACKET_SIZE
; i
++)
373 tosend
[i
] = av_lfg_get(&rnd
) >> 24;
374 rtmp_calc_digest(serverdata
+ 1 + server_pos
, 32, 0,
375 rtmp_player_key
, sizeof(rtmp_player_key
),
377 rtmp_calc_digest(tosend
, RTMP_HANDSHAKE_PACKET_SIZE
- 32, 0,
379 tosend
+ RTMP_HANDSHAKE_PACKET_SIZE
- 32);
381 // write reply back to the server
382 url_write(rt
->stream
, tosend
, RTMP_HANDSHAKE_PACKET_SIZE
);
387 * Parses received packet and may perform some action depending on
388 * the packet contents.
389 * @return 0 for no errors, negative values for serious errors which prevent
390 * further communications, positive values for uncritical errors
392 static int rtmp_parse_result(URLContext
*s
, RTMPContext
*rt
, RTMPPacket
*pkt
)
395 const uint8_t *data_end
= pkt
->data
+ pkt
->data_size
;
398 case RTMP_PT_CHUNK_SIZE
:
399 if (pkt
->data_size
!= 4) {
400 av_log(LOG_CONTEXT
, AV_LOG_ERROR
,
401 "Chunk size change packet is not 4 bytes long (%d)\n", pkt
->data_size
);
404 rt
->chunk_size
= AV_RB32(pkt
->data
);
405 if (rt
->chunk_size
<= 0) {
406 av_log(LOG_CONTEXT
, AV_LOG_ERROR
, "Incorrect chunk size %d\n", rt
->chunk_size
);
409 av_log(LOG_CONTEXT
, AV_LOG_DEBUG
, "New chunk size = %d\n", rt
->chunk_size
);
412 t
= AV_RB16(pkt
->data
);
414 gen_pong(s
, rt
, pkt
);
417 //TODO: check for the messages sent for wrong state?
418 if (!memcmp(pkt
->data
, "\002\000\006_error", 9)) {
421 if (!ff_amf_get_field_value(pkt
->data
+ 9, data_end
,
422 "description", tmpstr
, sizeof(tmpstr
)))
423 av_log(LOG_CONTEXT
, AV_LOG_ERROR
, "Server error: %s\n",tmpstr
);
425 } else if (!memcmp(pkt
->data
, "\002\000\007_result", 10)) {
427 case STATE_HANDSHAKED
:
428 gen_create_stream(s
, rt
);
429 rt
->state
= STATE_CONNECTING
;
431 case STATE_CONNECTING
:
432 //extract a number from the result
433 if (pkt
->data
[10] || pkt
->data
[19] != 5 || pkt
->data
[20]) {
434 av_log(LOG_CONTEXT
, AV_LOG_WARNING
, "Unexpected reply on connect()\n");
436 rt
->main_channel_id
= (int) av_int2dbl(AV_RB64(pkt
->data
+ 21));
439 rt
->state
= STATE_READY
;
442 } else if (!memcmp(pkt
->data
, "\002\000\010onStatus", 11)) {
443 const uint8_t* ptr
= pkt
->data
+ 11;
447 for (i
= 0; i
< 2; i
++) {
448 t
= ff_amf_tag_size(ptr
, data_end
);
453 t
= ff_amf_get_field_value(ptr
, data_end
,
454 "level", tmpstr
, sizeof(tmpstr
));
455 if (!t
&& !strcmp(tmpstr
, "error")) {
456 if (!ff_amf_get_field_value(ptr
, data_end
,
457 "description", tmpstr
, sizeof(tmpstr
)))
458 av_log(LOG_CONTEXT
, AV_LOG_ERROR
, "Server error: %s\n",tmpstr
);
461 t
= ff_amf_get_field_value(ptr
, data_end
,
462 "code", tmpstr
, sizeof(tmpstr
));
463 if (!t
&& !strcmp(tmpstr
, "NetStream.Play.Start")) {
464 rt
->state
= STATE_PLAYING
;
474 * Interacts with the server by receiving and sending RTMP packets until
475 * there is some significant data (media data or expected status notification).
477 * @param s reading context
478 * @param for_header non-zero value tells function to work until it
479 * gets notification from the server that playing has been started,
480 * otherwise function will work until some media data is received (or
482 * @return 0 for successful operation, negative value in case of error
484 static int get_packet(URLContext
*s
, int for_header
)
486 RTMPContext
*rt
= s
->priv_data
;
491 if ((ret
= ff_rtmp_packet_read(rt
->stream
, &rpkt
,
492 rt
->chunk_size
, rt
->prev_pkt
[0])) != 0) {
494 return AVERROR(EAGAIN
);
500 ret
= rtmp_parse_result(s
, rt
, &rpkt
);
501 if (ret
< 0) {//serious error in current packet
502 ff_rtmp_packet_destroy(&rpkt
);
505 if (for_header
&& rt
->state
== STATE_PLAYING
) {
506 ff_rtmp_packet_destroy(&rpkt
);
509 if (!rpkt
.data_size
) {
510 ff_rtmp_packet_destroy(&rpkt
);
513 if (rpkt
.type
== RTMP_PT_VIDEO
|| rpkt
.type
== RTMP_PT_AUDIO
||
514 rpkt
.type
== RTMP_PT_NOTIFY
) {
516 uint32_t ts
= rpkt
.timestamp
;
518 if (rpkt
.type
== RTMP_PT_VIDEO
) {
519 rt
->video_ts
+= rpkt
.timestamp
;
521 } else if (rpkt
.type
== RTMP_PT_AUDIO
) {
522 rt
->audio_ts
+= rpkt
.timestamp
;
525 // generate packet header and put data into buffer for FLV demuxer
527 rt
->flv_size
= rpkt
.data_size
+ 15;
528 rt
->flv_data
= p
= av_realloc(rt
->flv_data
, rt
->flv_size
);
529 bytestream_put_byte(&p
, rpkt
.type
);
530 bytestream_put_be24(&p
, rpkt
.data_size
);
531 bytestream_put_be24(&p
, ts
);
532 bytestream_put_byte(&p
, ts
>> 24);
533 bytestream_put_be24(&p
, 0);
534 bytestream_put_buffer(&p
, rpkt
.data
, rpkt
.data_size
);
535 bytestream_put_be32(&p
, 0);
536 ff_rtmp_packet_destroy(&rpkt
);
538 } else if (rpkt
.type
== RTMP_PT_METADATA
) {
539 // we got raw FLV data, make it available for FLV demuxer
541 rt
->flv_size
= rpkt
.data_size
;
542 rt
->flv_data
= av_realloc(rt
->flv_data
, rt
->flv_size
);
543 memcpy(rt
->flv_data
, rpkt
.data
, rpkt
.data_size
);
544 ff_rtmp_packet_destroy(&rpkt
);
547 ff_rtmp_packet_destroy(&rpkt
);
552 static int rtmp_close(URLContext
*h
)
554 RTMPContext
*rt
= h
->priv_data
;
556 av_freep(&rt
->flv_data
);
557 url_close(rt
->stream
);
563 * Opens RTMP connection and verifies that the stream can be played.
565 * URL syntax: rtmp://server[:port][/app][/playpath]
566 * where 'app' is first one or two directories in the path
567 * (e.g. /ondemand/, /flash/live/, etc.)
568 * and 'playpath' is a file name (the rest of the path,
569 * may be prefixed with "mp4:")
571 static int rtmp_open(URLContext
*s
, const char *uri
, int flags
)
574 char proto
[8], hostname
[256], path
[1024], app
[128], *fname
;
579 is_input
= !(flags
& URL_WRONLY
);
581 rt
= av_mallocz(sizeof(RTMPContext
));
583 return AVERROR(ENOMEM
);
586 url_split(proto
, sizeof(proto
), NULL
, 0, hostname
, sizeof(hostname
), &port
,
587 path
, sizeof(path
), s
->filename
);
590 port
= RTMP_DEFAULT_PORT
;
591 snprintf(buf
, sizeof(buf
), "tcp://%s:%d", hostname
, port
);
593 if (url_open(&rt
->stream
, buf
, URL_RDWR
) < 0)
597 av_log(LOG_CONTEXT
, AV_LOG_ERROR
, "RTMP output is not supported yet.\n");
600 rt
->state
= STATE_START
;
601 if (rtmp_handshake(s
, rt
))
604 rt
->chunk_size
= 128;
605 rt
->state
= STATE_HANDSHAKED
;
606 //extract "app" part from path
607 if (!strncmp(path
, "/ondemand/", 10)) {
609 memcpy(app
, "ondemand", 9);
611 char *p
= strchr(path
+ 1, '/');
616 char *c
= strchr(p
+ 1, ':');
617 fname
= strchr(p
+ 1, '/');
618 if (!fname
|| c
< fname
) {
620 av_strlcpy(app
, path
+ 1, p
- path
);
623 av_strlcpy(app
, path
+ 1, fname
- path
- 1);
627 if (!strchr(fname
, ':') &&
628 (!strcmp(fname
+ strlen(fname
) - 4, ".f4v") ||
629 !strcmp(fname
+ strlen(fname
) - 4, ".mp4"))) {
630 memcpy(rt
->playpath
, "mp4:", 5);
634 strncat(rt
->playpath
, fname
, sizeof(rt
->playpath
) - 5);
636 av_log(LOG_CONTEXT
, AV_LOG_DEBUG
, "Proto = %s, path = %s, app = %s, fname = %s\n",
637 proto
, path
, app
, rt
->playpath
);
638 gen_connect(s
, rt
, proto
, hostname
, port
, app
);
641 ret
= get_packet(s
, 1);
642 } while (ret
== EAGAIN
);
645 // generate FLV header for demuxer
647 rt
->flv_data
= av_realloc(rt
->flv_data
, rt
->flv_size
);
649 memcpy(rt
->flv_data
, "FLV\1\5\0\0\0\011\0\0\0\0", rt
->flv_size
);
652 s
->max_packet_size
= url_get_max_packet_size(rt
->stream
);
661 static int rtmp_read(URLContext
*s
, uint8_t *buf
, int size
)
663 RTMPContext
*rt
= s
->priv_data
;
664 int orig_size
= size
;
668 int data_left
= rt
->flv_size
- rt
->flv_off
;
670 if (data_left
>= size
) {
671 memcpy(buf
, rt
->flv_data
+ rt
->flv_off
, size
);
676 memcpy(buf
, rt
->flv_data
+ rt
->flv_off
, data_left
);
679 rt
->flv_off
= rt
->flv_size
;
681 if ((ret
= get_packet(s
, 0)) < 0)
687 static int rtmp_write(URLContext
*h
, uint8_t *buf
, int size
)
692 URLProtocol rtmp_protocol
= {