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