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