RTMP protocol support (as a client)
[libav.git] / libavformat / rtmpproto.c
CommitLineData
9fd6b843
KS
1/*
2 * RTMP network protocol
3 * Copyright (c) 2009 Kostya 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/**
23 * @file libavformat/rtmpproto.c
24 * RTMP protocol
25 */
26
27#include "libavcodec/bytestream.h"
28#include "libavutil/avstring.h"
29#include "libavutil/lfg.h"
30#include "libavutil/sha.h"
31#include "avformat.h"
32
33#include "network.h"
34
35#include "flv.h"
36#include "rtmp.h"
37#include "rtmppkt.h"
38
39/* we can't use av_log() with URLContext yet... */
40#if LIBAVFORMAT_VERSION_MAJOR < 53
41#define LOG_CONTEXT NULL
42#else
43#define LOG_CONTEXT s
44#endif
45
46/** RTMP protocol handler state */
47typedef enum {
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
53} ClientState;
54
55/** protocol handler context */
56typedef 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
68} RTMPContext;
69
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 */
72static 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',
75
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
79};
80
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 */
83static 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',
87
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
91};
92
93/**
94 * Generates 'connect' call and sends it to the server.
95 */
96static void gen_connect(URLContext *s, RTMPContext *rt, const char *proto,
97 const char *host, int port, const char *app)
98{
99 RTMPPacket pkt;
100 uint8_t ver[32], *p;
101 char tcurl[512];
102
103 ff_rtmp_packet_create(&pkt, RTMP_VIDEO_CHANNEL, RTMP_PT_INVOKE, 0, 4096);
104 p = pkt.data;
105
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);
112
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);
130
131 pkt.data_size = p - pkt.data;
132
133 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
134}
135
136/**
137 * Generates 'createStream' call and sends it to the server. It should make
138 * the server allocate some channel for media streams.
139 */
140static void gen_create_stream(URLContext *s, RTMPContext *rt)
141{
142 RTMPPacket pkt;
143 uint8_t *p;
144
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);
147
148 p = pkt.data;
149 ff_amf_write_string(&p, "createStream");
150 ff_amf_write_number(&p, 3.0);
151 ff_amf_write_null(&p);
152
153 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
154 ff_rtmp_packet_destroy(&pkt);
155}
156
157/**
158 * Generates 'play' call and sends it to the server, then pings the server
159 * to start actual playing.
160 */
161static void gen_play(URLContext *s, RTMPContext *rt)
162{
163 RTMPPacket pkt;
164 uint8_t *p;
165
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 29 + strlen(rt->playpath));
169 pkt.extra = rt->main_channel_id;
170
171 p = pkt.data;
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);
176 ff_amf_write_number(&p, 0.0);
177
178 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
179 ff_rtmp_packet_destroy(&pkt);
180
181 // set client buffer time disguised in ping packet
182 ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING, 1, 10);
183
184 p = pkt.data;
185 bytestream_put_be16(&p, 3);
186 bytestream_put_be32(&p, 1);
187 bytestream_put_be32(&p, 256); //TODO: what is a good value here?
188
189 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
190 ff_rtmp_packet_destroy(&pkt);
191}
192
193/**
194 * Generates ping reply and sends it to the server.
195 */
196static void gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt)
197{
198 RTMPPacket pkt;
199 uint8_t *p;
200
201 ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING, ppkt->timestamp + 1, 6);
202 p = pkt.data;
203 bytestream_put_be16(&p, 7);
204 bytestream_put_be32(&p, AV_RB32(ppkt->data+2) + 1);
205 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
206 ff_rtmp_packet_destroy(&pkt);
207}
208
209//TODO: Move HMAC code somewhere. Eventually.
210#define HMAC_IPAD_VAL 0x36
211#define HMAC_OPAD_VAL 0x5C
212
213/**
214 * Calculates HMAC-SHA2 digest for RTMP handshake packets.
215 *
216 * @param src input buffer
217 * @param len input buffer length (should be 1536)
218 * @param gap offset in buffer where 32 bytes should not be taken into account
219 * when calculating digest (since it will be used to store that digest)
220 * @param key digest key
221 * @param keylen digest key length
222 * @param dst buffer where calculated digest will be stored (32 bytes)
223 */
224static void rtmp_calc_digest(const uint8_t *src, int len, int gap,
225 const uint8_t *key, int keylen, uint8_t *dst)
226{
227 struct AVSHA *sha;
228 uint8_t hmac_buf[64+32] = {0};
229 int i;
230
231 sha = av_mallocz(av_sha_size);
232
233 if (keylen < 64) {
234 memcpy(hmac_buf, key, keylen);
235 } else {
236 av_sha_init(sha, 256);
237 av_sha_update(sha,key, keylen);
238 av_sha_final(sha, hmac_buf);
239 }
240 for (i = 0; i < 64; i++)
241 hmac_buf[i] ^= HMAC_IPAD_VAL;
242
243 av_sha_init(sha, 256);
244 av_sha_update(sha, hmac_buf, 64);
245 if (gap <= 0) {
246 av_sha_update(sha, src, len);
247 } else { //skip 32 bytes used for storing digest
248 av_sha_update(sha, src, gap);
249 av_sha_update(sha, src + gap + 32, len - gap - 32);
250 }
251 av_sha_final(sha, hmac_buf + 64);
252
253 for (i = 0; i < 64; i++)
254 hmac_buf[i] ^= HMAC_IPAD_VAL ^ HMAC_OPAD_VAL; //reuse XORed key for opad
255 av_sha_init(sha, 256);
256 av_sha_update(sha, hmac_buf, 64+32);
257 av_sha_final(sha, dst);
258
259 av_free(sha);
260}
261
262/**
263 * Puts HMAC-SHA2 digest of packet data (except for the bytes where this digest
264 * will be stored) into that packet.
265 *
266 * @param buf handshake data (1536 bytes)
267 * @return offset to the digest inside input data
268 */
269static int rtmp_handshake_imprint_with_digest(uint8_t *buf)
270{
271 int i, digest_pos = 0;
272
273 for (i = 8; i < 12; i++)
274 digest_pos += buf[i];
275 digest_pos = (digest_pos % 728) + 12;
276
277 rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
278 rtmp_player_key, PLAYER_KEY_OPEN_PART_LEN,
279 buf + digest_pos);
280 return digest_pos;
281}
282
283/**
284 * Verifies that the received server response has the expected digest value.
285 *
286 * @param buf handshake data received from the server (1536 bytes)
287 * @param off position to search digest offset from
288 * @return 0 if digest is valid, digest position otherwise
289 */
290static int rtmp_validate_digest(uint8_t *buf, int off)
291{
292 int i, digest_pos = 0;
293 uint8_t digest[32];
294
295 for (i = 0; i < 4; i++)
296 digest_pos += buf[i + off];
297 digest_pos = (digest_pos % 728) + off + 4;
298
299 rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
300 rtmp_server_key, SERVER_KEY_OPEN_PART_LEN,
301 digest);
302 if (!memcmp(digest, buf + digest_pos, 32))
303 return digest_pos;
304 return 0;
305}
306
307/**
308 * Performs handshake with the server by means of exchanging pseudorandom data
309 * signed with HMAC-SHA2 digest.
310 *
311 * @return 0 if handshake succeeds, negative value otherwise
312 */
313static int rtmp_handshake(URLContext *s, RTMPContext *rt)
314{
315 AVLFG rnd;
316 uint8_t tosend [RTMP_HANDSHAKE_PACKET_SIZE+1] = {
317 3, // unencrypted data
318 0, 0, 0, 0, // client uptime
319 RTMP_CLIENT_VER1,
320 RTMP_CLIENT_VER2,
321 RTMP_CLIENT_VER3,
322 RTMP_CLIENT_VER4,
323 };
324 uint8_t clientdata[RTMP_HANDSHAKE_PACKET_SIZE];
325 uint8_t serverdata[RTMP_HANDSHAKE_PACKET_SIZE+1];
326 int i;
327 int server_pos, client_pos;
328 uint8_t digest[32];
329
330 av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Handshaking...\n");
331
332 av_lfg_init(&rnd, 0xDEADC0DE);
333 // generate handshake packet - 1536 bytes of pseudorandom data
334 for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
335 tosend[i] = av_lfg_get(&rnd) >> 24;
336 client_pos = rtmp_handshake_imprint_with_digest(tosend + 1);
337
338 url_write(rt->stream, tosend, RTMP_HANDSHAKE_PACKET_SIZE + 1);
339 i = url_read_complete(rt->stream, serverdata, RTMP_HANDSHAKE_PACKET_SIZE + 1);
340 if (i != RTMP_HANDSHAKE_PACKET_SIZE + 1) {
341 av_log(LOG_CONTEXT, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
342 return -1;
343 }
344 i = url_read_complete(rt->stream, clientdata, RTMP_HANDSHAKE_PACKET_SIZE);
345 if (i != RTMP_HANDSHAKE_PACKET_SIZE) {
346 av_log(LOG_CONTEXT, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
347 return -1;
348 }
349
350 av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
351 serverdata[5], serverdata[6], serverdata[7], serverdata[8]);
352
353 server_pos = rtmp_validate_digest(serverdata + 1, 772);
354 if (!server_pos) {
355 server_pos = rtmp_validate_digest(serverdata + 1, 8);
356 if (!server_pos) {
357 av_log(LOG_CONTEXT, AV_LOG_ERROR, "Server response validating failed\n");
358 return -1;
359 }
360 }
361
362 rtmp_calc_digest(tosend + 1 + client_pos, 32, 0,
363 rtmp_server_key, sizeof(rtmp_server_key),
364 digest);
365 rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE-32, 0,
366 digest, 32,
367 digest);
368 if (memcmp(digest, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) {
369 av_log(LOG_CONTEXT, AV_LOG_ERROR, "Signature mismatch\n");
370 return -1;
371 }
372
373 for (i = 0; i < RTMP_HANDSHAKE_PACKET_SIZE; i++)
374 tosend[i] = av_lfg_get(&rnd) >> 24;
375 rtmp_calc_digest(serverdata + 1 + server_pos, 32, 0,
376 rtmp_player_key, sizeof(rtmp_player_key),
377 digest);
378 rtmp_calc_digest(tosend, RTMP_HANDSHAKE_PACKET_SIZE - 32, 0,
379 digest, 32,
380 tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32);
381
382 // write reply back to the server
383 url_write(rt->stream, tosend, RTMP_HANDSHAKE_PACKET_SIZE);
384 return 0;
385}
386
387/**
388 * Parses received packet and may perform some action depending on
389 * the packet contents.
390 * @return 0 for no errors, negative values for serious errors which prevent
391 * further communications, positive values for uncritical errors
392 */
393static int rtmp_parse_result(URLContext *s, RTMPContext *rt, RTMPPacket *pkt)
394{
395 int i, t;
396 const uint8_t *data_end = pkt->data + pkt->data_size;
397
398 switch (pkt->type) {
399 case RTMP_PT_CHUNK_SIZE:
400 if (pkt->data_size != 4) {
401 av_log(LOG_CONTEXT, AV_LOG_ERROR,
402 "Chunk size change packet is not 4 bytes long (%d)\n", pkt->data_size);
403 return -1;
404 }
405 rt->chunk_size = AV_RB32(pkt->data);
406 if (rt->chunk_size <= 0) {
407 av_log(LOG_CONTEXT, AV_LOG_ERROR, "Incorrect chunk size %d\n", rt->chunk_size);
408 return -1;
409 }
410 av_log(LOG_CONTEXT, AV_LOG_DEBUG, "New chunk size = %d\n", rt->chunk_size);
411 break;
412 case RTMP_PT_PING:
413 t = AV_RB16(pkt->data);
414 if (t == 6)
415 gen_pong(s, rt, pkt);
416 break;
417 case RTMP_PT_INVOKE:
418 //TODO: check for the messages sent for wrong state?
419 if (!memcmp(pkt->data, "\002\000\006_error", 9)) {
420 uint8_t tmpstr[256];
421
422 if (!ff_amf_get_field_value(pkt->data + 9, data_end,
423 "description", tmpstr, sizeof(tmpstr)))
424 av_log(LOG_CONTEXT, AV_LOG_ERROR, "Server error: %s\n",tmpstr);
425 return -1;
426 } else if (!memcmp(pkt->data, "\002\000\007_result", 10)) {
427 switch (rt->state) {
428 case STATE_HANDSHAKED:
429 gen_create_stream(s, rt);
430 rt->state = STATE_CONNECTING;
431 break;
432 case STATE_CONNECTING:
433 //extract a number from the result
434 if (pkt->data[10] || pkt->data[19] != 5 || pkt->data[20]) {
435 av_log(LOG_CONTEXT, AV_LOG_WARNING, "Unexpected reply on connect()\n");
436 } else {
437 rt->main_channel_id = (int) av_int2dbl(AV_RB64(pkt->data + 21));
438 }
439 gen_play(s, rt);
440 rt->state = STATE_READY;
441 break;
442 }
443 } else if (!memcmp(pkt->data, "\002\000\010onStatus", 11)) {
444 const uint8_t* ptr = pkt->data + 11;
445 uint8_t tmpstr[256];
446 int t;
447
448 for (i = 0; i < 2; i++) {
449 t = ff_amf_tag_size(ptr, data_end);
450 if (t < 0)
451 return 1;
452 ptr += t;
453 }
454 t = ff_amf_get_field_value(ptr, data_end,
455 "level", tmpstr, sizeof(tmpstr));
456 if (!t && !strcmp(tmpstr, "error")) {
457 if (!ff_amf_get_field_value(ptr, data_end,
458 "description", tmpstr, sizeof(tmpstr)))
459 av_log(LOG_CONTEXT, AV_LOG_ERROR, "Server error: %s\n",tmpstr);
460 return -1;
461 }
462 t = ff_amf_get_field_value(ptr, data_end,
463 "code", tmpstr, sizeof(tmpstr));
464 if (!t && !strcmp(tmpstr, "NetStream.Play.Start")) {
465 rt->state = STATE_PLAYING;
466 return 0;
467 }
468 }
469 break;
470 }
471 return 0;
472}
473
474/**
475 * Interacts with the server by receiving and sending RTMP packets until
476 * there is some significant data (media data or expected status notification).
477 *
478 * @param s reading context
479 * @param for_header non-zero value tells function to work until it gets notification from the server that playing has been started, otherwise function will work until some media data is received (or an error happens)
480 * @return 0 for successful operation, negative value in case of error
481 */
482static int get_packet(URLContext *s, int for_header)
483{
484 RTMPContext *rt = s->priv_data;
485 int ret;
486
487 for(;;) {
488 RTMPPacket rpkt;
489 if ((ret = ff_rtmp_packet_read(rt->stream, &rpkt,
490 rt->chunk_size, rt->prev_pkt[0])) != 0) {
491 if (ret > 0) {
492 return AVERROR(EAGAIN);
493 } else {
494 return AVERROR(EIO);
495 }
496 }
497
498 ret = rtmp_parse_result(s, rt, &rpkt);
499 if (ret < 0) {//serious error in current packet
500 ff_rtmp_packet_destroy(&rpkt);
501 return -1;
502 }
503 if (for_header && rt->state == STATE_PLAYING) {
504 ff_rtmp_packet_destroy(&rpkt);
505 return 0;
506 }
507 if (!rpkt.data_size) {
508 ff_rtmp_packet_destroy(&rpkt);
509 continue;
510 }
511 if (rpkt.type == RTMP_PT_VIDEO || rpkt.type == RTMP_PT_AUDIO ||
512 rpkt.type == RTMP_PT_NOTIFY) {
513 uint8_t *p;
514 uint32_t ts = rpkt.timestamp;
515
516 if (rpkt.type == RTMP_PT_VIDEO) {
517 rt->video_ts += rpkt.timestamp;
518 ts = rt->video_ts;
519 } else if (rpkt.type == RTMP_PT_AUDIO) {
520 rt->audio_ts += rpkt.timestamp;
521 ts = rt->audio_ts;
522 }
523 // generate packet header and put data into buffer for FLV demuxer
524 rt->flv_off = 0;
525 rt->flv_size = rpkt.data_size + 15;
526 rt->flv_data = p = av_realloc(rt->flv_data, rt->flv_size);
527 bytestream_put_byte(&p, rpkt.type);
528 bytestream_put_be24(&p, rpkt.data_size);
529 bytestream_put_be24(&p, ts);
530 bytestream_put_byte(&p, ts >> 24);
531 bytestream_put_be24(&p, 0);
532 bytestream_put_buffer(&p, rpkt.data, rpkt.data_size);
533 bytestream_put_be32(&p, 0);
534 ff_rtmp_packet_destroy(&rpkt);
535 return 0;
536 } else if (rpkt.type == RTMP_PT_METADATA) {
537 // we got raw FLV data, make it available for FLV demuxer
538 rt->flv_off = 0;
539 rt->flv_size = rpkt.data_size;
540 rt->flv_data = av_realloc(rt->flv_data, rt->flv_size);
541 memcpy(rt->flv_data, rpkt.data, rpkt.data_size);
542 ff_rtmp_packet_destroy(&rpkt);
543 return 0;
544 }
545 ff_rtmp_packet_destroy(&rpkt);
546 }
547 return 0;
548}
549
550static int rtmp_close(URLContext *h)
551{
552 RTMPContext *rt = h->priv_data;
553
554 av_freep(&rt->flv_data);
555 url_close(rt->stream);
556 av_free(rt);
557 return 0;
558}
559
560/**
561 * Opens RTMP connection and verifies that the stream can be played.
562 *
563 * URL syntax: rtmp://server[:port][/app][/playpath]
564 * where 'app' is first one or two directories in the path
565 * (e.g. /ondemand/, /flash/live/, etc.)
566 * and 'playpath' is a file name (the rest of the path,
567 * may be prefixed with "mp4:")
568 */
569static int rtmp_open(URLContext *s, const char *uri, int flags)
570{
571 RTMPContext *rt;
572 char proto[8], hostname[256], path[1024], app[128], *fname;
573 uint8_t buf[2048];
574 int port, is_input;
575 int ret;
576
577 is_input = !(flags & URL_WRONLY);
578
579 rt = av_mallocz(sizeof(RTMPContext));
580 if (!rt)
581 return AVERROR(ENOMEM);
582 s->priv_data = rt;
583
584 url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port,
585 path, sizeof(path), s->filename);
586
587 if (port < 0)
588 port = RTMP_DEFAULT_PORT;
589 snprintf(buf, sizeof(buf), "tcp://%s:%d", hostname, port);
590
591 if (url_open(&rt->stream, buf, URL_RDWR) < 0)
592 goto fail;
593
594 if (!is_input) {
595 av_log(LOG_CONTEXT, AV_LOG_ERROR, "RTMP output is not supported yet.\n");
596 goto fail;
597 } else {
598 rt->state = STATE_START;
599 if (rtmp_handshake(s, rt))
600 return -1;
601
602 rt->chunk_size = 128;
603 rt->state = STATE_HANDSHAKED;
604 //extract "app" part from path
605 if (!strncmp(path, "/ondemand/", 10)) {
606 fname = path + 10;
607 memcpy(app, "ondemand", 9);
608 } else {
609 char *p = strchr(path + 1, '/');
610 if (!p) {
611 fname = path + 1;
612 app[0] = '\0';
613 } else {
614 fname = strchr(p + 1, '/');
615 if (!fname) {
616 fname = p + 1;
617 av_strlcpy(app, path + 1, p - path);
618 } else {
619 fname++;
620 av_strlcpy(app, path + 1, fname - path - 1);
621 }
622 }
623 }
624 if (!strcmp(fname + strlen(fname) - 4, ".f4v") ||
625 !strcmp(fname + strlen(fname) - 4, ".mp4")) {
626 memcpy(rt->playpath, "mp4:", 5);
627 } else {
628 rt->playpath[0] = 0;
629 }
630 strncat(rt->playpath, fname, sizeof(rt->playpath) - 5);
631
632 av_log(LOG_CONTEXT, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s\n",
633 proto, path, app, rt->playpath);
634 gen_connect(s, rt, proto, hostname, port, app);
635
636 do {
637 ret = get_packet(s, 1);
638 } while (ret == EAGAIN);
639 if (ret < 0)
640 goto fail;
641 // generate FLV header for demuxer
642 rt->flv_size = 13;
643 rt->flv_data = av_realloc(rt->flv_data, rt->flv_size);
644 rt->flv_off = 0;
645 memcpy(rt->flv_data, "FLV\1\5\0\0\0\011\0\0\0\0", rt->flv_size);
646 }
647
648 s->max_packet_size = url_get_max_packet_size(rt->stream);
649 s->is_streamed = 1;
650 return 0;
651
652fail:
653 rtmp_close(s);
654 return AVERROR(EIO);
655}
656
657static int rtmp_read(URLContext *s, uint8_t *buf, int size)
658{
659 RTMPContext *rt = s->priv_data;
660 int orig_size = size;
661 int ret;
662
663 while (size > 0) {
664 int data_left = rt->flv_size - rt->flv_off;
665
666 if (data_left >= size) {
667 memcpy(buf, rt->flv_data + rt->flv_off, size);
668 rt->flv_off += size;
669 return orig_size;
670 }
671 if (data_left > 0) {
672 memcpy(buf, rt->flv_data + rt->flv_off, data_left);
673 buf += data_left;
674 size -= data_left;
675 rt->flv_off = rt->flv_size;
676 }
677 if ((ret = get_packet(s, 0)) < 0)
678 return ret;
679 }
680 return orig_size;
681}
682
683static int rtmp_write(URLContext *h, uint8_t *buf, int size)
684{
685 return 0;
686}
687
688URLProtocol rtmp_protocol = {
689 "rtmp",
690 rtmp_open,
691 rtmp_read,
692 rtmp_write,
693 NULL, /* seek */
694 rtmp_close,
695};