rtmp: Support 'rtmp_tcurl', an option which overrides the URL of the target stream.
[libav.git] / libavformat / rtmpproto.c
CommitLineData
9fd6b843
KS
1/*
2 * RTMP network protocol
3 * Copyright (c) 2009 Kostya Shishkov
4 *
2912e87a 5 * This file is part of Libav.
9fd6b843 6 *
2912e87a 7 * Libav is free software; you can redistribute it and/or
9fd6b843
KS
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 *
2912e87a 12 * Libav is distributed in the hope that it will be useful,
9fd6b843
KS
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
2912e87a 18 * License along with Libav; if not, write to the Free Software
9fd6b843
KS
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22/**
ba87f080 23 * @file
9fd6b843
KS
24 * RTMP protocol
25 */
26
27#include "libavcodec/bytestream.h"
28#include "libavutil/avstring.h"
3383a53e 29#include "libavutil/intfloat.h"
9fd6b843 30#include "libavutil/lfg.h"
6465562e 31#include "libavutil/opt.h"
9fd6b843
KS
32#include "libavutil/sha.h"
33#include "avformat.h"
e4a9e3cc 34#include "internal.h"
9fd6b843
KS
35
36#include "network.h"
37
38#include "flv.h"
39#include "rtmp.h"
40#include "rtmppkt.h"
0589da0a 41#include "url.h"
9fd6b843 42
cfac91fe
KS
43//#define DEBUG
44
6465562e 45#define APP_MAX_LENGTH 128
b3b17512 46#define PLAYPATH_MAX_LENGTH 256
55c9320e 47#define TCURL_MAX_LENGTH 512
6465562e 48
9fd6b843
KS
49/** RTMP protocol handler state */
50typedef enum {
51 STATE_START, ///< client has not done anything yet
52 STATE_HANDSHAKED, ///< client has performed handshake
6bf22e18
S
53 STATE_RELEASING, ///< client releasing stream before publish it (for output)
54 STATE_FCPUBLISH, ///< client FCPublishing stream (for output)
9fd6b843
KS
55 STATE_CONNECTING, ///< client connected to server successfully
56 STATE_READY, ///< client has sent all needed commands and waits for server reply
57 STATE_PLAYING, ///< client has started receiving multimedia data from server
6bf22e18 58 STATE_PUBLISHING, ///< client has started sending multimedia data to server (for output)
72b870b9 59 STATE_STOPPED, ///< the broadcast has been stopped
9fd6b843
KS
60} ClientState;
61
62/** protocol handler context */
63typedef struct RTMPContext {
6465562e 64 const AVClass *class;
9fd6b843
KS
65 URLContext* stream; ///< TCP stream used in interactions with RTMP server
66 RTMPPacket prev_pkt[2][RTMP_CHANNELS]; ///< packet history used when reading and sending packets
67 int chunk_size; ///< size of the chunks RTMP packets are divided into
b316991b 68 int is_input; ///< input/output flag
b3b17512 69 char *playpath; ///< stream identifier to play (with possible "mp4:" prefix)
b2e495af 70 int live; ///< 0: recorded, -1: live, -2: both
6465562e 71 char *app; ///< name of application
9fd6b843
KS
72 ClientState state; ///< current state
73 int main_channel_id; ///< an additional channel ID which is used for some invocations
74 uint8_t* flv_data; ///< buffer with data for demuxer
75 int flv_size; ///< current buffer size
76 int flv_off; ///< number of bytes read from current buffer
6bf22e18 77 RTMPPacket out_pkt; ///< rtmp packet, created from flv a/v or metadata (for output)
bf7c1719
KS
78 uint32_t client_report_size; ///< number of bytes after which client should report to server
79 uint32_t bytes_read; ///< number of bytes read from server
80 uint32_t last_bytes_read; ///< number of bytes read last reported to server
3ffe32eb 81 int skip_bytes; ///< number of bytes to skip from the input FLV stream in the next write call
b14629e5
MS
82 uint8_t flv_header[11]; ///< partial incoming flv packet header
83 int flv_header_bytes; ///< number of initialized bytes in flv_header
704af3e2 84 int nb_invokes; ///< keeps track of invoke messages
1eef08f9 85 int create_stream_invoke; ///< invoke id for the create stream command
55c9320e 86 char* tcurl; ///< url of the target stream
9fd6b843
KS
87} RTMPContext;
88
89#define PLAYER_KEY_OPEN_PART_LEN 30 ///< length of partial key used for first client digest signing
90/** Client key used for digest signing */
91static const uint8_t rtmp_player_key[] = {
92 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
93 'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ', '0', '0', '1',
94
95 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
96 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
97 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
98};
99
100#define SERVER_KEY_OPEN_PART_LEN 36 ///< length of partial key used for first server digest signing
101/** Key used for RTMP server digest signing */
102static const uint8_t rtmp_server_key[] = {
103 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
104 'F', 'l', 'a', 's', 'h', ' ', 'M', 'e', 'd', 'i', 'a', ' ',
105 'S', 'e', 'r', 'v', 'e', 'r', ' ', '0', '0', '1',
106
107 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
108 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
109 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
110};
111
112/**
49bd8e4b 113 * Generate 'connect' call and send it to the server.
9fd6b843 114 */
55c9320e 115static void gen_connect(URLContext *s, RTMPContext *rt)
9fd6b843
KS
116{
117 RTMPPacket pkt;
6bf22e18 118 uint8_t ver[64], *p;
9fd6b843 119
31da5966 120 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 4096);
9fd6b843
KS
121 p = pkt.data;
122
9fd6b843 123 ff_amf_write_string(&p, "connect");
1eef08f9 124 ff_amf_write_number(&p, ++rt->nb_invokes);
9fd6b843
KS
125 ff_amf_write_object_start(&p);
126 ff_amf_write_field_name(&p, "app");
5e9ad759 127 ff_amf_write_string(&p, rt->app);
9fd6b843 128
6bf22e18 129 if (rt->is_input) {
c7240611
KS
130 snprintf(ver, sizeof(ver), "%s %d,%d,%d,%d", RTMP_CLIENT_PLATFORM, RTMP_CLIENT_VER1,
131 RTMP_CLIENT_VER2, RTMP_CLIENT_VER3, RTMP_CLIENT_VER4);
6bf22e18
S
132 } else {
133 snprintf(ver, sizeof(ver), "FMLE/3.0 (compatible; %s)", LIBAVFORMAT_IDENT);
134 ff_amf_write_field_name(&p, "type");
135 ff_amf_write_string(&p, "nonprivate");
136 }
9fd6b843
KS
137 ff_amf_write_field_name(&p, "flashVer");
138 ff_amf_write_string(&p, ver);
139 ff_amf_write_field_name(&p, "tcUrl");
55c9320e 140 ff_amf_write_string(&p, rt->tcurl);
6bf22e18 141 if (rt->is_input) {
c7240611
KS
142 ff_amf_write_field_name(&p, "fpad");
143 ff_amf_write_bool(&p, 0);
144 ff_amf_write_field_name(&p, "capabilities");
145 ff_amf_write_number(&p, 15.0);
faba4a9b
SP
146
147 /* Tell the server we support all the audio codecs except
148 * SUPPORT_SND_INTEL (0x0008) and SUPPORT_SND_UNUSED (0x0010)
149 * which are unused in the RTMP protocol implementation. */
c7240611 150 ff_amf_write_field_name(&p, "audioCodecs");
faba4a9b 151 ff_amf_write_number(&p, 4071.0);
c7240611
KS
152 ff_amf_write_field_name(&p, "videoCodecs");
153 ff_amf_write_number(&p, 252.0);
154 ff_amf_write_field_name(&p, "videoFunction");
155 ff_amf_write_number(&p, 1.0);
6bf22e18 156 }
9fd6b843
KS
157 ff_amf_write_object_end(&p);
158
159 pkt.data_size = p - pkt.data;
160
161 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
f8caa544 162 ff_rtmp_packet_destroy(&pkt);
9fd6b843
KS
163}
164
165/**
49bd8e4b 166 * Generate 'releaseStream' call and send it to the server. It should make
6bf22e18
S
167 * the server release some channel for media streams.
168 */
169static void gen_release_stream(URLContext *s, RTMPContext *rt)
170{
171 RTMPPacket pkt;
172 uint8_t *p;
173
174 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0,
175 29 + strlen(rt->playpath));
176
7f804085 177 av_log(s, AV_LOG_DEBUG, "Releasing stream...\n");
6bf22e18
S
178 p = pkt.data;
179 ff_amf_write_string(&p, "releaseStream");
704af3e2 180 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18
S
181 ff_amf_write_null(&p);
182 ff_amf_write_string(&p, rt->playpath);
183
184 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
185 ff_rtmp_packet_destroy(&pkt);
186}
187
188/**
49bd8e4b 189 * Generate 'FCPublish' call and send it to the server. It should make
6bf22e18
S
190 * the server preapare for receiving media streams.
191 */
192static void gen_fcpublish_stream(URLContext *s, RTMPContext *rt)
193{
194 RTMPPacket pkt;
195 uint8_t *p;
196
197 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0,
198 25 + strlen(rt->playpath));
199
7f804085 200 av_log(s, AV_LOG_DEBUG, "FCPublish stream...\n");
6bf22e18
S
201 p = pkt.data;
202 ff_amf_write_string(&p, "FCPublish");
704af3e2 203 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18
S
204 ff_amf_write_null(&p);
205 ff_amf_write_string(&p, rt->playpath);
206
207 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
208 ff_rtmp_packet_destroy(&pkt);
209}
210
211/**
49bd8e4b 212 * Generate 'FCUnpublish' call and send it to the server. It should make
6bf22e18
S
213 * the server destroy stream.
214 */
215static void gen_fcunpublish_stream(URLContext *s, RTMPContext *rt)
216{
217 RTMPPacket pkt;
218 uint8_t *p;
219
220 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0,
221 27 + strlen(rt->playpath));
222
7f804085 223 av_log(s, AV_LOG_DEBUG, "UnPublishing stream...\n");
6bf22e18
S
224 p = pkt.data;
225 ff_amf_write_string(&p, "FCUnpublish");
704af3e2 226 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18
S
227 ff_amf_write_null(&p);
228 ff_amf_write_string(&p, rt->playpath);
229
230 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
231 ff_rtmp_packet_destroy(&pkt);
232}
233
234/**
49bd8e4b 235 * Generate 'createStream' call and send it to the server. It should make
9fd6b843
KS
236 * the server allocate some channel for media streams.
237 */
238static void gen_create_stream(URLContext *s, RTMPContext *rt)
239{
240 RTMPPacket pkt;
241 uint8_t *p;
242
7f804085 243 av_log(s, AV_LOG_DEBUG, "Creating stream...\n");
31da5966 244 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 25);
9fd6b843
KS
245
246 p = pkt.data;
247 ff_amf_write_string(&p, "createStream");
704af3e2 248 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18 249 ff_amf_write_null(&p);
1eef08f9 250 rt->create_stream_invoke = rt->nb_invokes;
6bf22e18
S
251
252 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
253 ff_rtmp_packet_destroy(&pkt);
254}
255
256
257/**
49bd8e4b 258 * Generate 'deleteStream' call and send it to the server. It should make
6bf22e18
S
259 * the server remove some channel for media streams.
260 */
261static void gen_delete_stream(URLContext *s, RTMPContext *rt)
262{
263 RTMPPacket pkt;
264 uint8_t *p;
265
7f804085 266 av_log(s, AV_LOG_DEBUG, "Deleting stream...\n");
6bf22e18
S
267 ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE, 0, 34);
268
269 p = pkt.data;
270 ff_amf_write_string(&p, "deleteStream");
1eef08f9 271 ff_amf_write_number(&p, ++rt->nb_invokes);
9fd6b843 272 ff_amf_write_null(&p);
6bf22e18 273 ff_amf_write_number(&p, rt->main_channel_id);
9fd6b843
KS
274
275 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
276 ff_rtmp_packet_destroy(&pkt);
277}
278
279/**
49bd8e4b 280 * Generate 'play' call and send it to the server, then ping the server
9fd6b843
KS
281 * to start actual playing.
282 */
283static void gen_play(URLContext *s, RTMPContext *rt)
284{
285 RTMPPacket pkt;
286 uint8_t *p;
287
7f804085 288 av_log(s, AV_LOG_DEBUG, "Sending play command for '%s'\n", rt->playpath);
9fd6b843 289 ff_rtmp_packet_create(&pkt, RTMP_VIDEO_CHANNEL, RTMP_PT_INVOKE, 0,
b2e495af 290 29 + strlen(rt->playpath));
9fd6b843
KS
291 pkt.extra = rt->main_channel_id;
292
293 p = pkt.data;
294 ff_amf_write_string(&p, "play");
1eef08f9 295 ff_amf_write_number(&p, ++rt->nb_invokes);
9fd6b843
KS
296 ff_amf_write_null(&p);
297 ff_amf_write_string(&p, rt->playpath);
b2e495af 298 ff_amf_write_number(&p, rt->live);
9fd6b843
KS
299
300 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
301 ff_rtmp_packet_destroy(&pkt);
302
303 // set client buffer time disguised in ping packet
304 ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING, 1, 10);
305
306 p = pkt.data;
307 bytestream_put_be16(&p, 3);
308 bytestream_put_be32(&p, 1);
309 bytestream_put_be32(&p, 256); //TODO: what is a good value here?
310
311 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
312 ff_rtmp_packet_destroy(&pkt);
313}
314
315/**
49bd8e4b 316 * Generate 'publish' call and send it to the server.
6bf22e18
S
317 */
318static void gen_publish(URLContext *s, RTMPContext *rt)
319{
320 RTMPPacket pkt;
321 uint8_t *p;
322
7f804085 323 av_log(s, AV_LOG_DEBUG, "Sending publish command for '%s'\n", rt->playpath);
6bf22e18
S
324 ff_rtmp_packet_create(&pkt, RTMP_SOURCE_CHANNEL, RTMP_PT_INVOKE, 0,
325 30 + strlen(rt->playpath));
326 pkt.extra = rt->main_channel_id;
327
328 p = pkt.data;
329 ff_amf_write_string(&p, "publish");
1eef08f9 330 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18
S
331 ff_amf_write_null(&p);
332 ff_amf_write_string(&p, rt->playpath);
333 ff_amf_write_string(&p, "live");
334
335 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
336 ff_rtmp_packet_destroy(&pkt);
337}
338
339/**
49bd8e4b 340 * Generate ping reply and send it to the server.
9fd6b843
KS
341 */
342static void gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt)
343{
344 RTMPPacket pkt;
345 uint8_t *p;
346
347 ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING, ppkt->timestamp + 1, 6);
348 p = pkt.data;
349 bytestream_put_be16(&p, 7);
4aaebf78 350 bytestream_put_be32(&p, AV_RB32(ppkt->data+2));
9fd6b843
KS
351 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
352 ff_rtmp_packet_destroy(&pkt);
353}
354
bf7c1719 355/**
34d908c0
RS
356 * Generate server bandwidth message and send it to the server.
357 */
358static void gen_server_bw(URLContext *s, RTMPContext *rt)
359{
360 RTMPPacket pkt;
361 uint8_t *p;
362
363 ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_SERVER_BW, 0, 4);
364 p = pkt.data;
365 bytestream_put_be32(&p, 2500000);
366 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
367 ff_rtmp_packet_destroy(&pkt);
368}
369
370/**
49bd8e4b 371 * Generate report on bytes read so far and send it to the server.
bf7c1719
KS
372 */
373static void gen_bytes_read(URLContext *s, RTMPContext *rt, uint32_t ts)
374{
375 RTMPPacket pkt;
376 uint8_t *p;
377
378 ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_BYTES_READ, ts, 4);
379 p = pkt.data;
380 bytestream_put_be32(&p, rt->bytes_read);
381 ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size, rt->prev_pkt[1]);
382 ff_rtmp_packet_destroy(&pkt);
383}
384
9fd6b843
KS
385//TODO: Move HMAC code somewhere. Eventually.
386#define HMAC_IPAD_VAL 0x36
387#define HMAC_OPAD_VAL 0x5C
388
389/**
49bd8e4b 390 * Calculate HMAC-SHA2 digest for RTMP handshake packets.
9fd6b843
KS
391 *
392 * @param src input buffer
393 * @param len input buffer length (should be 1536)
394 * @param gap offset in buffer where 32 bytes should not be taken into account
395 * when calculating digest (since it will be used to store that digest)
396 * @param key digest key
397 * @param keylen digest key length
398 * @param dst buffer where calculated digest will be stored (32 bytes)
399 */
400static void rtmp_calc_digest(const uint8_t *src, int len, int gap,
401 const uint8_t *key, int keylen, uint8_t *dst)
402{
403 struct AVSHA *sha;
404 uint8_t hmac_buf[64+32] = {0};
405 int i;
406
407 sha = av_mallocz(av_sha_size);
408
409 if (keylen < 64) {
410 memcpy(hmac_buf, key, keylen);
411 } else {
412 av_sha_init(sha, 256);
413 av_sha_update(sha,key, keylen);
414 av_sha_final(sha, hmac_buf);
415 }
416 for (i = 0; i < 64; i++)
417 hmac_buf[i] ^= HMAC_IPAD_VAL;
418
419 av_sha_init(sha, 256);
420 av_sha_update(sha, hmac_buf, 64);
421 if (gap <= 0) {
422 av_sha_update(sha, src, len);
423 } else { //skip 32 bytes used for storing digest
424 av_sha_update(sha, src, gap);
425 av_sha_update(sha, src + gap + 32, len - gap - 32);
426 }
427 av_sha_final(sha, hmac_buf + 64);
428
429 for (i = 0; i < 64; i++)
430 hmac_buf[i] ^= HMAC_IPAD_VAL ^ HMAC_OPAD_VAL; //reuse XORed key for opad
431 av_sha_init(sha, 256);
432 av_sha_update(sha, hmac_buf, 64+32);
433 av_sha_final(sha, dst);
434
435 av_free(sha);
436}
437
438/**
49bd8e4b 439 * Put HMAC-SHA2 digest of packet data (except for the bytes where this digest
9fd6b843
KS
440 * will be stored) into that packet.
441 *
442 * @param buf handshake data (1536 bytes)
443 * @return offset to the digest inside input data
444 */
445static int rtmp_handshake_imprint_with_digest(uint8_t *buf)
446{
447 int i, digest_pos = 0;
448
449 for (i = 8; i < 12; i++)
450 digest_pos += buf[i];
451 digest_pos = (digest_pos % 728) + 12;
452
453 rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
454 rtmp_player_key, PLAYER_KEY_OPEN_PART_LEN,
455 buf + digest_pos);
456 return digest_pos;
457}
458
459/**
49bd8e4b 460 * Verify that the received server response has the expected digest value.
9fd6b843
KS
461 *
462 * @param buf handshake data received from the server (1536 bytes)
463 * @param off position to search digest offset from
464 * @return 0 if digest is valid, digest position otherwise
465 */
466static int rtmp_validate_digest(uint8_t *buf, int off)
467{
468 int i, digest_pos = 0;
469 uint8_t digest[32];
470
471 for (i = 0; i < 4; i++)
472 digest_pos += buf[i + off];
473 digest_pos = (digest_pos % 728) + off + 4;
474
475 rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
476 rtmp_server_key, SERVER_KEY_OPEN_PART_LEN,
477 digest);
478 if (!memcmp(digest, buf + digest_pos, 32))
479 return digest_pos;
480 return 0;
481}
482
483/**
49bd8e4b 484 * Perform handshake with the server by means of exchanging pseudorandom data
9fd6b843
KS
485 * signed with HMAC-SHA2 digest.
486 *
487 * @return 0 if handshake succeeds, negative value otherwise
488 */
489static int rtmp_handshake(URLContext *s, RTMPContext *rt)
490{
491 AVLFG rnd;
492 uint8_t tosend [RTMP_HANDSHAKE_PACKET_SIZE+1] = {
493 3, // unencrypted data
494 0, 0, 0, 0, // client uptime
495 RTMP_CLIENT_VER1,
496 RTMP_CLIENT_VER2,
497 RTMP_CLIENT_VER3,
498 RTMP_CLIENT_VER4,
499 };
500 uint8_t clientdata[RTMP_HANDSHAKE_PACKET_SIZE];
501 uint8_t serverdata[RTMP_HANDSHAKE_PACKET_SIZE+1];
502 int i;
503 int server_pos, client_pos;
504 uint8_t digest[32];
505
7f804085 506 av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
9fd6b843
KS
507
508 av_lfg_init(&rnd, 0xDEADC0DE);
509 // generate handshake packet - 1536 bytes of pseudorandom data
510 for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
511 tosend[i] = av_lfg_get(&rnd) >> 24;
512 client_pos = rtmp_handshake_imprint_with_digest(tosend + 1);
513
925e908b 514 ffurl_write(rt->stream, tosend, RTMP_HANDSHAKE_PACKET_SIZE + 1);
dce37564 515 i = ffurl_read_complete(rt->stream, serverdata, RTMP_HANDSHAKE_PACKET_SIZE + 1);
9fd6b843 516 if (i != RTMP_HANDSHAKE_PACKET_SIZE + 1) {
7f804085 517 av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
9fd6b843
KS
518 return -1;
519 }
dce37564 520 i = ffurl_read_complete(rt->stream, clientdata, RTMP_HANDSHAKE_PACKET_SIZE);
9fd6b843 521 if (i != RTMP_HANDSHAKE_PACKET_SIZE) {
7f804085 522 av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
9fd6b843
KS
523 return -1;
524 }
525
7f804085 526 av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
9fd6b843
KS
527 serverdata[5], serverdata[6], serverdata[7], serverdata[8]);
528
e2ee11e8 529 if (rt->is_input && serverdata[5] >= 3) {
c7240611 530 server_pos = rtmp_validate_digest(serverdata + 1, 772);
9fd6b843 531 if (!server_pos) {
c7240611
KS
532 server_pos = rtmp_validate_digest(serverdata + 1, 8);
533 if (!server_pos) {
7f804085 534 av_log(s, AV_LOG_ERROR, "Server response validating failed\n");
c7240611
KS
535 return -1;
536 }
9fd6b843 537 }
9fd6b843 538
c7240611
KS
539 rtmp_calc_digest(tosend + 1 + client_pos, 32, 0,
540 rtmp_server_key, sizeof(rtmp_server_key),
541 digest);
542 rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE-32, 0,
543 digest, 32,
544 digest);
545 if (memcmp(digest, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) {
7f804085 546 av_log(s, AV_LOG_ERROR, "Signature mismatch\n");
c7240611
KS
547 return -1;
548 }
9fd6b843 549
c7240611
KS
550 for (i = 0; i < RTMP_HANDSHAKE_PACKET_SIZE; i++)
551 tosend[i] = av_lfg_get(&rnd) >> 24;
552 rtmp_calc_digest(serverdata + 1 + server_pos, 32, 0,
553 rtmp_player_key, sizeof(rtmp_player_key),
554 digest);
555 rtmp_calc_digest(tosend, RTMP_HANDSHAKE_PACKET_SIZE - 32, 0,
556 digest, 32,
557 tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32);
558
559 // write reply back to the server
925e908b 560 ffurl_write(rt->stream, tosend, RTMP_HANDSHAKE_PACKET_SIZE);
6bf22e18 561 } else {
925e908b 562 ffurl_write(rt->stream, serverdata+1, RTMP_HANDSHAKE_PACKET_SIZE);
6bf22e18
S
563 }
564
9fd6b843
KS
565 return 0;
566}
567
568/**
49bd8e4b 569 * Parse received packet and possibly perform some action depending on
9fd6b843
KS
570 * the packet contents.
571 * @return 0 for no errors, negative values for serious errors which prevent
572 * further communications, positive values for uncritical errors
573 */
574static int rtmp_parse_result(URLContext *s, RTMPContext *rt, RTMPPacket *pkt)
575{
576 int i, t;
577 const uint8_t *data_end = pkt->data + pkt->data_size;
578
cfac91fe 579#ifdef DEBUG
7f804085 580 ff_rtmp_packet_dump(s, pkt);
cfac91fe
KS
581#endif
582
9fd6b843
KS
583 switch (pkt->type) {
584 case RTMP_PT_CHUNK_SIZE:
585 if (pkt->data_size != 4) {
7f804085 586 av_log(s, AV_LOG_ERROR,
9fd6b843
KS
587 "Chunk size change packet is not 4 bytes long (%d)\n", pkt->data_size);
588 return -1;
589 }
6bf22e18
S
590 if (!rt->is_input)
591 ff_rtmp_packet_write(rt->stream, pkt, rt->chunk_size, rt->prev_pkt[1]);
9fd6b843
KS
592 rt->chunk_size = AV_RB32(pkt->data);
593 if (rt->chunk_size <= 0) {
7f804085 594 av_log(s, AV_LOG_ERROR, "Incorrect chunk size %d\n", rt->chunk_size);
9fd6b843
KS
595 return -1;
596 }
7f804085 597 av_log(s, AV_LOG_DEBUG, "New chunk size = %d\n", rt->chunk_size);
9fd6b843
KS
598 break;
599 case RTMP_PT_PING:
600 t = AV_RB16(pkt->data);
601 if (t == 6)
602 gen_pong(s, rt, pkt);
603 break;
bf7c1719
KS
604 case RTMP_PT_CLIENT_BW:
605 if (pkt->data_size < 4) {
7f804085 606 av_log(s, AV_LOG_ERROR,
bf7c1719
KS
607 "Client bandwidth report packet is less than 4 bytes long (%d)\n",
608 pkt->data_size);
609 return -1;
610 }
7f804085 611 av_log(s, AV_LOG_DEBUG, "Client bandwidth = %d\n", AV_RB32(pkt->data));
bf7c1719
KS
612 rt->client_report_size = AV_RB32(pkt->data) >> 1;
613 break;
9fd6b843
KS
614 case RTMP_PT_INVOKE:
615 //TODO: check for the messages sent for wrong state?
616 if (!memcmp(pkt->data, "\002\000\006_error", 9)) {
617 uint8_t tmpstr[256];
618
619 if (!ff_amf_get_field_value(pkt->data + 9, data_end,
620 "description", tmpstr, sizeof(tmpstr)))
7f804085 621 av_log(s, AV_LOG_ERROR, "Server error: %s\n",tmpstr);
9fd6b843
KS
622 return -1;
623 } else if (!memcmp(pkt->data, "\002\000\007_result", 10)) {
624 switch (rt->state) {
625 case STATE_HANDSHAKED:
6bf22e18
S
626 if (!rt->is_input) {
627 gen_release_stream(s, rt);
628 gen_fcpublish_stream(s, rt);
629 rt->state = STATE_RELEASING;
630 } else {
34d908c0 631 gen_server_bw(s, rt);
6bf22e18
S
632 rt->state = STATE_CONNECTING;
633 }
9fd6b843 634 gen_create_stream(s, rt);
6bf22e18
S
635 break;
636 case STATE_FCPUBLISH:
9fd6b843
KS
637 rt->state = STATE_CONNECTING;
638 break;
6bf22e18
S
639 case STATE_RELEASING:
640 rt->state = STATE_FCPUBLISH;
641 /* hack for Wowza Media Server, it does not send result for
642 * releaseStream and FCPublish calls */
643 if (!pkt->data[10]) {
3383a53e 644 int pkt_id = av_int2double(AV_RB64(pkt->data + 11));
1eef08f9 645 if (pkt_id == rt->create_stream_invoke)
6bf22e18
S
646 rt->state = STATE_CONNECTING;
647 }
e07c92e4 648 if (rt->state != STATE_CONNECTING)
6bf22e18 649 break;
9fd6b843
KS
650 case STATE_CONNECTING:
651 //extract a number from the result
652 if (pkt->data[10] || pkt->data[19] != 5 || pkt->data[20]) {
7f804085 653 av_log(s, AV_LOG_WARNING, "Unexpected reply on connect()\n");
9fd6b843 654 } else {
3383a53e 655 rt->main_channel_id = av_int2double(AV_RB64(pkt->data + 21));
9fd6b843 656 }
6bf22e18 657 if (rt->is_input) {
c7240611 658 gen_play(s, rt);
6bf22e18
S
659 } else {
660 gen_publish(s, rt);
661 }
9fd6b843
KS
662 rt->state = STATE_READY;
663 break;
664 }
665 } else if (!memcmp(pkt->data, "\002\000\010onStatus", 11)) {
666 const uint8_t* ptr = pkt->data + 11;
667 uint8_t tmpstr[256];
9fd6b843
KS
668
669 for (i = 0; i < 2; i++) {
670 t = ff_amf_tag_size(ptr, data_end);
671 if (t < 0)
672 return 1;
673 ptr += t;
674 }
675 t = ff_amf_get_field_value(ptr, data_end,
676 "level", tmpstr, sizeof(tmpstr));
677 if (!t && !strcmp(tmpstr, "error")) {
678 if (!ff_amf_get_field_value(ptr, data_end,
679 "description", tmpstr, sizeof(tmpstr)))
7f804085 680 av_log(s, AV_LOG_ERROR, "Server error: %s\n",tmpstr);
9fd6b843
KS
681 return -1;
682 }
683 t = ff_amf_get_field_value(ptr, data_end,
684 "code", tmpstr, sizeof(tmpstr));
6bf22e18 685 if (!t && !strcmp(tmpstr, "NetStream.Play.Start")) rt->state = STATE_PLAYING;
72b870b9
MS
686 if (!t && !strcmp(tmpstr, "NetStream.Play.Stop")) rt->state = STATE_STOPPED;
687 if (!t && !strcmp(tmpstr, "NetStream.Play.UnpublishNotify")) rt->state = STATE_STOPPED;
6bf22e18 688 if (!t && !strcmp(tmpstr, "NetStream.Publish.Start")) rt->state = STATE_PUBLISHING;
9fd6b843
KS
689 }
690 break;
691 }
692 return 0;
693}
694
695/**
49bd8e4b 696 * Interact with the server by receiving and sending RTMP packets until
9fd6b843
KS
697 * there is some significant data (media data or expected status notification).
698 *
699 * @param s reading context
1d8041b3
SS
700 * @param for_header non-zero value tells function to work until it
701 * gets notification from the server that playing has been started,
702 * otherwise function will work until some media data is received (or
703 * an error happens)
9fd6b843
KS
704 * @return 0 for successful operation, negative value in case of error
705 */
706static int get_packet(URLContext *s, int for_header)
707{
708 RTMPContext *rt = s->priv_data;
709 int ret;
56e29bf2
S
710 uint8_t *p;
711 const uint8_t *next;
712 uint32_t data_size;
713 uint32_t ts, cts, pts=0;
9fd6b843 714
72b870b9
MS
715 if (rt->state == STATE_STOPPED)
716 return AVERROR_EOF;
717
e07c92e4 718 for (;;) {
271c869c 719 RTMPPacket rpkt = { 0 };
9fd6b843 720 if ((ret = ff_rtmp_packet_read(rt->stream, &rpkt,
adb54961 721 rt->chunk_size, rt->prev_pkt[0])) <= 0) {
b381a823 722 if (ret == 0) {
9fd6b843
KS
723 return AVERROR(EAGAIN);
724 } else {
725 return AVERROR(EIO);
726 }
727 }
bf7c1719
KS
728 rt->bytes_read += ret;
729 if (rt->bytes_read > rt->last_bytes_read + rt->client_report_size) {
7f804085 730 av_log(s, AV_LOG_DEBUG, "Sending bytes read report\n");
bf7c1719
KS
731 gen_bytes_read(s, rt, rpkt.timestamp + 1);
732 rt->last_bytes_read = rt->bytes_read;
733 }
9fd6b843
KS
734
735 ret = rtmp_parse_result(s, rt, &rpkt);
736 if (ret < 0) {//serious error in current packet
737 ff_rtmp_packet_destroy(&rpkt);
738 return -1;
739 }
72b870b9
MS
740 if (rt->state == STATE_STOPPED) {
741 ff_rtmp_packet_destroy(&rpkt);
742 return AVERROR_EOF;
743 }
6bf22e18 744 if (for_header && (rt->state == STATE_PLAYING || rt->state == STATE_PUBLISHING)) {
9fd6b843
KS
745 ff_rtmp_packet_destroy(&rpkt);
746 return 0;
747 }
6bf22e18 748 if (!rpkt.data_size || !rt->is_input) {
9fd6b843
KS
749 ff_rtmp_packet_destroy(&rpkt);
750 continue;
751 }
752 if (rpkt.type == RTMP_PT_VIDEO || rpkt.type == RTMP_PT_AUDIO ||
afbacb93 753 (rpkt.type == RTMP_PT_NOTIFY && !memcmp("\002\000\012onMetaData", rpkt.data, 13))) {
56e29bf2 754 ts = rpkt.timestamp;
9fd6b843 755
9fd6b843
KS
756 // generate packet header and put data into buffer for FLV demuxer
757 rt->flv_off = 0;
758 rt->flv_size = rpkt.data_size + 15;
759 rt->flv_data = p = av_realloc(rt->flv_data, rt->flv_size);
760 bytestream_put_byte(&p, rpkt.type);
761 bytestream_put_be24(&p, rpkt.data_size);
762 bytestream_put_be24(&p, ts);
763 bytestream_put_byte(&p, ts >> 24);
764 bytestream_put_be24(&p, 0);
765 bytestream_put_buffer(&p, rpkt.data, rpkt.data_size);
766 bytestream_put_be32(&p, 0);
767 ff_rtmp_packet_destroy(&rpkt);
768 return 0;
769 } else if (rpkt.type == RTMP_PT_METADATA) {
770 // we got raw FLV data, make it available for FLV demuxer
771 rt->flv_off = 0;
772 rt->flv_size = rpkt.data_size;
773 rt->flv_data = av_realloc(rt->flv_data, rt->flv_size);
56e29bf2
S
774 /* rewrite timestamps */
775 next = rpkt.data;
776 ts = rpkt.timestamp;
777 while (next - rpkt.data < rpkt.data_size - 11) {
778 next++;
779 data_size = bytestream_get_be24(&next);
780 p=next;
781 cts = bytestream_get_be24(&next);
aae9a093 782 cts |= bytestream_get_byte(&next) << 24;
56e29bf2
S
783 if (pts==0)
784 pts=cts;
785 ts += cts - pts;
786 pts = cts;
787 bytestream_put_be24(&p, ts);
788 bytestream_put_byte(&p, ts >> 24);
789 next += data_size + 3 + 4;
790 }
9fd6b843
KS
791 memcpy(rt->flv_data, rpkt.data, rpkt.data_size);
792 ff_rtmp_packet_destroy(&rpkt);
793 return 0;
794 }
795 ff_rtmp_packet_destroy(&rpkt);
796 }
9fd6b843
KS
797}
798
799static int rtmp_close(URLContext *h)
800{
801 RTMPContext *rt = h->priv_data;
802
e07c92e4 803 if (!rt->is_input) {
6bf22e18
S
804 rt->flv_data = NULL;
805 if (rt->out_pkt.data_size)
806 ff_rtmp_packet_destroy(&rt->out_pkt);
615c2879
S
807 if (rt->state > STATE_FCPUBLISH)
808 gen_fcunpublish_stream(h, rt);
6bf22e18 809 }
615c2879
S
810 if (rt->state > STATE_HANDSHAKED)
811 gen_delete_stream(h, rt);
6bf22e18 812
9fd6b843 813 av_freep(&rt->flv_data);
e52a9145 814 ffurl_close(rt->stream);
9fd6b843
KS
815 return 0;
816}
817
818/**
49bd8e4b 819 * Open RTMP connection and verify that the stream can be played.
9fd6b843
KS
820 *
821 * URL syntax: rtmp://server[:port][/app][/playpath]
822 * where 'app' is first one or two directories in the path
823 * (e.g. /ondemand/, /flash/live/, etc.)
824 * and 'playpath' is a file name (the rest of the path,
825 * may be prefixed with "mp4:")
826 */
827static int rtmp_open(URLContext *s, const char *uri, int flags)
828{
7e580505 829 RTMPContext *rt = s->priv_data;
5e9ad759 830 char proto[8], hostname[256], path[1024], *fname;
6465562e 831 char *old_app;
9fd6b843 832 uint8_t buf[2048];
b316991b 833 int port;
9fd6b843
KS
834 int ret;
835
59d96941 836 rt->is_input = !(flags & AVIO_FLAG_WRITE);
9fd6b843 837
f3bfe388 838 av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port,
f984dcf6 839 path, sizeof(path), s->filename);
9fd6b843
KS
840
841 if (port < 0)
842 port = RTMP_DEFAULT_PORT;
57b5555c 843 ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL);
9fd6b843 844
6f1b7b39 845 if (ffurl_open(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
ddffc2fd 846 &s->interrupt_callback, NULL) < 0) {
59d96941 847 av_log(s , AV_LOG_ERROR, "Cannot open connection %s\n", buf);
9fd6b843 848 goto fail;
fe523958 849 }
9fd6b843 850
c7240611
KS
851 rt->state = STATE_START;
852 if (rtmp_handshake(s, rt))
02490bf3 853 goto fail;
9fd6b843 854
c7240611
KS
855 rt->chunk_size = 128;
856 rt->state = STATE_HANDSHAKED;
6465562e
SP
857
858 // Keep the application name when it has been defined by the user.
859 old_app = rt->app;
860
861 rt->app = av_malloc(APP_MAX_LENGTH);
862 if (!rt->app) {
863 rtmp_close(s);
864 return AVERROR(ENOMEM);
865 }
866
c7240611
KS
867 //extract "app" part from path
868 if (!strncmp(path, "/ondemand/", 10)) {
869 fname = path + 10;
870 memcpy(rt->app, "ondemand", 9);
871 } else {
872 char *p = strchr(path + 1, '/');
873 if (!p) {
874 fname = path + 1;
875 rt->app[0] = '\0';
9fd6b843 876 } else {
c7240611
KS
877 char *c = strchr(p + 1, ':');
878 fname = strchr(p + 1, '/');
879 if (!fname || c < fname) {
880 fname = p + 1;
881 av_strlcpy(rt->app, path + 1, p - path);
9fd6b843 882 } else {
c7240611
KS
883 fname++;
884 av_strlcpy(rt->app, path + 1, fname - path - 1);
9fd6b843
KS
885 }
886 }
c7240611 887 }
6465562e
SP
888
889 if (old_app) {
890 // The name of application has been defined by the user, override it.
891 av_free(rt->app);
892 rt->app = old_app;
893 }
894
b3b17512
SP
895 if (!rt->playpath) {
896 rt->playpath = av_malloc(PLAYPATH_MAX_LENGTH);
897 if (!rt->playpath) {
898 rtmp_close(s);
899 return AVERROR(ENOMEM);
900 }
901
902 if (!strchr(fname, ':') &&
903 (!strcmp(fname + strlen(fname) - 4, ".f4v") ||
904 !strcmp(fname + strlen(fname) - 4, ".mp4"))) {
905 memcpy(rt->playpath, "mp4:", 5);
906 } else {
907 rt->playpath[0] = 0;
908 }
909 strncat(rt->playpath, fname, PLAYPATH_MAX_LENGTH - 5);
c7240611 910 }
9fd6b843 911
55c9320e
SP
912 if (!rt->tcurl) {
913 rt->tcurl = av_malloc(TCURL_MAX_LENGTH);
914 ff_url_join(rt->tcurl, TCURL_MAX_LENGTH, proto, NULL, hostname,
915 port, "/%s", rt->app);
916 }
917
bf7c1719
KS
918 rt->client_report_size = 1048576;
919 rt->bytes_read = 0;
920 rt->last_bytes_read = 0;
921
7f804085 922 av_log(s, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s\n",
c7240611 923 proto, path, rt->app, rt->playpath);
55c9320e 924 gen_connect(s, rt);
9fd6b843 925
c7240611
KS
926 do {
927 ret = get_packet(s, 1);
928 } while (ret == EAGAIN);
929 if (ret < 0)
930 goto fail;
6bf22e18
S
931
932 if (rt->is_input) {
9fd6b843
KS
933 // generate FLV header for demuxer
934 rt->flv_size = 13;
935 rt->flv_data = av_realloc(rt->flv_data, rt->flv_size);
936 rt->flv_off = 0;
937 memcpy(rt->flv_data, "FLV\1\5\0\0\0\011\0\0\0\0", rt->flv_size);
6bf22e18
S
938 } else {
939 rt->flv_size = 0;
940 rt->flv_data = NULL;
941 rt->flv_off = 0;
b14629e5 942 rt->skip_bytes = 13;
9fd6b843
KS
943 }
944
5958df34 945 s->max_packet_size = rt->stream->max_packet_size;
9fd6b843
KS
946 s->is_streamed = 1;
947 return 0;
948
949fail:
950 rtmp_close(s);
951 return AVERROR(EIO);
952}
953
954static int rtmp_read(URLContext *s, uint8_t *buf, int size)
955{
956 RTMPContext *rt = s->priv_data;
957 int orig_size = size;
958 int ret;
959
960 while (size > 0) {
961 int data_left = rt->flv_size - rt->flv_off;
962
963 if (data_left >= size) {
964 memcpy(buf, rt->flv_data + rt->flv_off, size);
965 rt->flv_off += size;
966 return orig_size;
967 }
968 if (data_left > 0) {
969 memcpy(buf, rt->flv_data + rt->flv_off, data_left);
970 buf += data_left;
971 size -= data_left;
972 rt->flv_off = rt->flv_size;
e8ccf245 973 return data_left;
9fd6b843
KS
974 }
975 if ((ret = get_packet(s, 0)) < 0)
976 return ret;
977 }
978 return orig_size;
979}
980
9ad4c65f 981static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
9fd6b843 982{
9ad4c65f 983 RTMPContext *rt = s->priv_data;
6bf22e18
S
984 int size_temp = size;
985 int pktsize, pkttype;
986 uint32_t ts;
987 const uint8_t *buf_temp = buf;
988
6bf22e18 989 do {
b14629e5
MS
990 if (rt->skip_bytes) {
991 int skip = FFMIN(rt->skip_bytes, size_temp);
992 buf_temp += skip;
993 size_temp -= skip;
994 rt->skip_bytes -= skip;
995 continue;
996 }
997
998 if (rt->flv_header_bytes < 11) {
999 const uint8_t *header = rt->flv_header;
1000 int copy = FFMIN(11 - rt->flv_header_bytes, size_temp);
1001 bytestream_get_buffer(&buf_temp, rt->flv_header + rt->flv_header_bytes, copy);
1002 rt->flv_header_bytes += copy;
1003 size_temp -= copy;
1004 if (rt->flv_header_bytes < 11)
1005 break;
6bf22e18 1006
b14629e5
MS
1007 pkttype = bytestream_get_byte(&header);
1008 pktsize = bytestream_get_be24(&header);
1009 ts = bytestream_get_be24(&header);
1010 ts |= bytestream_get_byte(&header) << 24;
1011 bytestream_get_be24(&header);
6bf22e18
S
1012 rt->flv_size = pktsize;
1013
1014 //force 12bytes header
1015 if (((pkttype == RTMP_PT_VIDEO || pkttype == RTMP_PT_AUDIO) && ts == 0) ||
1016 pkttype == RTMP_PT_NOTIFY) {
1017 if (pkttype == RTMP_PT_NOTIFY)
1018 pktsize += 16;
1019 rt->prev_pkt[1][RTMP_SOURCE_CHANNEL].channel_id = 0;
1020 }
1021
1022 //this can be a big packet, it's better to send it right here
1023 ff_rtmp_packet_create(&rt->out_pkt, RTMP_SOURCE_CHANNEL, pkttype, ts, pktsize);
1024 rt->out_pkt.extra = rt->main_channel_id;
1025 rt->flv_data = rt->out_pkt.data;
1026
1027 if (pkttype == RTMP_PT_NOTIFY)
1028 ff_amf_write_string(&rt->flv_data, "@setDataFrame");
1029 }
1030
1031 if (rt->flv_size - rt->flv_off > size_temp) {
1032 bytestream_get_buffer(&buf_temp, rt->flv_data + rt->flv_off, size_temp);
1033 rt->flv_off += size_temp;
a14c7842 1034 size_temp = 0;
6bf22e18
S
1035 } else {
1036 bytestream_get_buffer(&buf_temp, rt->flv_data + rt->flv_off, rt->flv_size - rt->flv_off);
a14c7842 1037 size_temp -= rt->flv_size - rt->flv_off;
6bf22e18
S
1038 rt->flv_off += rt->flv_size - rt->flv_off;
1039 }
1040
1041 if (rt->flv_off == rt->flv_size) {
b14629e5
MS
1042 rt->skip_bytes = 4;
1043
6bf22e18
S
1044 ff_rtmp_packet_write(rt->stream, &rt->out_pkt, rt->chunk_size, rt->prev_pkt[1]);
1045 ff_rtmp_packet_destroy(&rt->out_pkt);
1046 rt->flv_size = 0;
1047 rt->flv_off = 0;
b14629e5 1048 rt->flv_header_bytes = 0;
6bf22e18 1049 }
a14c7842 1050 } while (buf_temp - buf < size);
6bf22e18 1051 return size;
9fd6b843
KS
1052}
1053
6465562e
SP
1054#define OFFSET(x) offsetof(RTMPContext, x)
1055#define DEC AV_OPT_FLAG_DECODING_PARAM
1056#define ENC AV_OPT_FLAG_ENCODING_PARAM
1057
1058static const AVOption rtmp_options[] = {
1059 {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
b2e495af
SP
1060 {"rtmp_live", "Specify that the media is a live stream.", OFFSET(live), AV_OPT_TYPE_INT, {-2}, INT_MIN, INT_MAX, DEC, "rtmp_live"},
1061 {"any", "both", 0, AV_OPT_TYPE_CONST, {-2}, 0, 0, DEC, "rtmp_live"},
1062 {"live", "live stream", 0, AV_OPT_TYPE_CONST, {-1}, 0, 0, DEC, "rtmp_live"},
1063 {"recorded", "recorded stream", 0, AV_OPT_TYPE_CONST, {0}, 0, 0, DEC, "rtmp_live"},
b3b17512 1064 {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
55c9320e 1065 {"rtmp_tcurl", "URL of the target stream. Defaults to rtmp://host[:port]/app.", OFFSET(tcurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
6465562e
SP
1066 { NULL },
1067};
1068
1069static const AVClass rtmp_class = {
1070 .class_name = "rtmp",
1071 .item_name = av_default_item_name,
1072 .option = rtmp_options,
1073 .version = LIBAVUTIL_VERSION_INT,
1074};
1075
c6610a21 1076URLProtocol ff_rtmp_protocol = {
c3b05d21
MS
1077 .name = "rtmp",
1078 .url_open = rtmp_open,
1079 .url_read = rtmp_read,
1080 .url_write = rtmp_write,
1081 .url_close = rtmp_close,
7e580505 1082 .priv_data_size = sizeof(RTMPContext),
32b83aee 1083 .flags = URL_PROTOCOL_FLAG_NETWORK,
6465562e 1084 .priv_data_class= &rtmp_class,
9fd6b843 1085};