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