rtmp: Factorize the code by adding handle_chunk_size
[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"
acd554c1 40#include "rtmpcrypt.h"
9fd6b843 41#include "rtmppkt.h"
0589da0a 42#include "url.h"
9fd6b843 43
cfac91fe
KS
44//#define DEBUG
45
6465562e 46#define APP_MAX_LENGTH 128
b3b17512 47#define PLAYPATH_MAX_LENGTH 256
55c9320e 48#define TCURL_MAX_LENGTH 512
e64673e4 49#define FLASHVER_MAX_LENGTH 64
6465562e 50
9fd6b843
KS
51/** RTMP protocol handler state */
52typedef enum {
53 STATE_START, ///< client has not done anything yet
54 STATE_HANDSHAKED, ///< client has performed handshake
6bf22e18
S
55 STATE_RELEASING, ///< client releasing stream before publish it (for output)
56 STATE_FCPUBLISH, ///< client FCPublishing stream (for output)
9fd6b843
KS
57 STATE_CONNECTING, ///< client connected to server successfully
58 STATE_READY, ///< client has sent all needed commands and waits for server reply
59 STATE_PLAYING, ///< client has started receiving multimedia data from server
6bf22e18 60 STATE_PUBLISHING, ///< client has started sending multimedia data to server (for output)
72b870b9 61 STATE_STOPPED, ///< the broadcast has been stopped
9fd6b843
KS
62} ClientState;
63
64/** protocol handler context */
65typedef struct RTMPContext {
6465562e 66 const AVClass *class;
9fd6b843
KS
67 URLContext* stream; ///< TCP stream used in interactions with RTMP server
68 RTMPPacket prev_pkt[2][RTMP_CHANNELS]; ///< packet history used when reading and sending packets
69 int chunk_size; ///< size of the chunks RTMP packets are divided into
b316991b 70 int is_input; ///< input/output flag
b3b17512 71 char *playpath; ///< stream identifier to play (with possible "mp4:" prefix)
b2e495af 72 int live; ///< 0: recorded, -1: live, -2: both
6465562e 73 char *app; ///< name of application
8ee3e187 74 char *conn; ///< append arbitrary AMF data to the Connect message
9fd6b843
KS
75 ClientState state; ///< current state
76 int main_channel_id; ///< an additional channel ID which is used for some invocations
77 uint8_t* flv_data; ///< buffer with data for demuxer
78 int flv_size; ///< current buffer size
79 int flv_off; ///< number of bytes read from current buffer
46743a85 80 int flv_nb_packets; ///< number of flv packets published
6bf22e18 81 RTMPPacket out_pkt; ///< rtmp packet, created from flv a/v or metadata (for output)
bf7c1719
KS
82 uint32_t client_report_size; ///< number of bytes after which client should report to server
83 uint32_t bytes_read; ///< number of bytes read from server
84 uint32_t last_bytes_read; ///< number of bytes read last reported to server
3ffe32eb 85 int skip_bytes; ///< number of bytes to skip from the input FLV stream in the next write call
b14629e5
MS
86 uint8_t flv_header[11]; ///< partial incoming flv packet header
87 int flv_header_bytes; ///< number of initialized bytes in flv_header
704af3e2 88 int nb_invokes; ///< keeps track of invoke messages
1eef08f9 89 int create_stream_invoke; ///< invoke id for the create stream command
55c9320e 90 char* tcurl; ///< url of the target stream
e64673e4 91 char* flashver; ///< version of the flash plugin
05945db9 92 char* swfurl; ///< url of the swf player
758377a2 93 char* pageurl; ///< url of the web page
c2d38bea 94 int server_bw; ///< server bandwidth
9477c035 95 int client_buffer_time; ///< client buffer time in ms
46743a85 96 int flush_interval; ///< number of packets flushed in the same request (RTMPT only)
acd554c1 97 int encrypted; ///< use an encrypted connection (RTMPE only)
9fd6b843
KS
98} RTMPContext;
99
100#define PLAYER_KEY_OPEN_PART_LEN 30 ///< length of partial key used for first client digest signing
101/** Client key used for digest signing */
102static const uint8_t rtmp_player_key[] = {
103 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
104 'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ', '0', '0', '1',
105
106 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
107 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
108 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
109};
110
111#define SERVER_KEY_OPEN_PART_LEN 36 ///< length of partial key used for first server digest signing
112/** Key used for RTMP server digest signing */
113static const uint8_t rtmp_server_key[] = {
114 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
115 'F', 'l', 'a', 's', 'h', ' ', 'M', 'e', 'd', 'i', 'a', ' ',
116 'S', 'e', 'r', 'v', 'e', 'r', ' ', '0', '0', '1',
117
118 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
119 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
120 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
121};
122
8ee3e187
SP
123static int rtmp_write_amf_data(URLContext *s, char *param, uint8_t **p)
124{
05338686 125 char *field, *value;
8ee3e187
SP
126 char type;
127
128 /* The type must be B for Boolean, N for number, S for string, O for
129 * object, or Z for null. For Booleans the data must be either 0 or 1 for
130 * FALSE or TRUE, respectively. Likewise for Objects the data must be
131 * 0 or 1 to end or begin an object, respectively. Data items in subobjects
132 * may be named, by prefixing the type with 'N' and specifying the name
133 * before the value (ie. NB:myFlag:1). This option may be used multiple times
134 * to construct arbitrary AMF sequences. */
135 if (param[0] && param[1] == ':') {
136 type = param[0];
137 value = param + 2;
138 } else if (param[0] == 'N' && param[1] && param[2] == ':') {
139 type = param[1];
05338686
MS
140 field = param + 3;
141 value = strchr(field, ':');
142 if (!value)
143 goto fail;
144 *value = '\0';
145 value++;
8ee3e187
SP
146
147 if (!field || !value)
148 goto fail;
149
150 ff_amf_write_field_name(p, field);
151 } else {
152 goto fail;
153 }
154
155 switch (type) {
156 case 'B':
157 ff_amf_write_bool(p, value[0] != '0');
158 break;
159 case 'S':
160 ff_amf_write_string(p, value);
161 break;
162 case 'N':
163 ff_amf_write_number(p, strtod(value, NULL));
164 break;
165 case 'Z':
166 ff_amf_write_null(p);
167 break;
168 case 'O':
169 if (value[0] != '0')
170 ff_amf_write_object_start(p);
171 else
172 ff_amf_write_object_end(p);
173 break;
174 default:
175 goto fail;
176 break;
177 }
178
179 return 0;
180
181fail:
182 av_log(s, AV_LOG_ERROR, "Invalid AMF parameter: %s\n", param);
183 return AVERROR(EINVAL);
184}
185
9fd6b843 186/**
49bd8e4b 187 * Generate 'connect' call and send it to the server.
9fd6b843 188 */
f645f1d6 189static int gen_connect(URLContext *s, RTMPContext *rt)
9fd6b843
KS
190{
191 RTMPPacket pkt;
e64673e4 192 uint8_t *p;
f645f1d6
SP
193 int ret;
194
195 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
196 0, 4096)) < 0)
197 return ret;
9fd6b843 198
9fd6b843
KS
199 p = pkt.data;
200
9fd6b843 201 ff_amf_write_string(&p, "connect");
1eef08f9 202 ff_amf_write_number(&p, ++rt->nb_invokes);
9fd6b843
KS
203 ff_amf_write_object_start(&p);
204 ff_amf_write_field_name(&p, "app");
5e9ad759 205 ff_amf_write_string(&p, rt->app);
9fd6b843 206
e64673e4 207 if (!rt->is_input) {
6bf22e18
S
208 ff_amf_write_field_name(&p, "type");
209 ff_amf_write_string(&p, "nonprivate");
210 }
9fd6b843 211 ff_amf_write_field_name(&p, "flashVer");
e64673e4 212 ff_amf_write_string(&p, rt->flashver);
05945db9
SP
213
214 if (rt->swfurl) {
215 ff_amf_write_field_name(&p, "swfUrl");
216 ff_amf_write_string(&p, rt->swfurl);
217 }
218
9fd6b843 219 ff_amf_write_field_name(&p, "tcUrl");
55c9320e 220 ff_amf_write_string(&p, rt->tcurl);
6bf22e18 221 if (rt->is_input) {
c7240611
KS
222 ff_amf_write_field_name(&p, "fpad");
223 ff_amf_write_bool(&p, 0);
224 ff_amf_write_field_name(&p, "capabilities");
225 ff_amf_write_number(&p, 15.0);
faba4a9b
SP
226
227 /* Tell the server we support all the audio codecs except
228 * SUPPORT_SND_INTEL (0x0008) and SUPPORT_SND_UNUSED (0x0010)
229 * which are unused in the RTMP protocol implementation. */
c7240611 230 ff_amf_write_field_name(&p, "audioCodecs");
faba4a9b 231 ff_amf_write_number(&p, 4071.0);
c7240611
KS
232 ff_amf_write_field_name(&p, "videoCodecs");
233 ff_amf_write_number(&p, 252.0);
234 ff_amf_write_field_name(&p, "videoFunction");
235 ff_amf_write_number(&p, 1.0);
758377a2
SP
236
237 if (rt->pageurl) {
238 ff_amf_write_field_name(&p, "pageUrl");
239 ff_amf_write_string(&p, rt->pageurl);
240 }
6bf22e18 241 }
9fd6b843
KS
242 ff_amf_write_object_end(&p);
243
8ee3e187 244 if (rt->conn) {
05338686 245 char *param = rt->conn;
8ee3e187
SP
246
247 // Write arbitrary AMF data to the Connect message.
8ee3e187 248 while (param != NULL) {
05338686
MS
249 char *sep;
250 param += strspn(param, " ");
251 if (!*param)
252 break;
253 sep = strchr(param, ' ');
254 if (sep)
255 *sep = '\0';
8ee3e187
SP
256 if ((ret = rtmp_write_amf_data(s, param, &p)) < 0) {
257 // Invalid AMF parameter.
258 ff_rtmp_packet_destroy(&pkt);
259 return ret;
260 }
261
05338686
MS
262 if (sep)
263 param = sep + 1;
264 else
265 break;
8ee3e187
SP
266 }
267 }
268
9fd6b843
KS
269 pkt.data_size = p - pkt.data;
270
bba287fd
SP
271 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
272 rt->prev_pkt[1]);
f8caa544 273 ff_rtmp_packet_destroy(&pkt);
f645f1d6 274
bba287fd 275 return ret;
9fd6b843
KS
276}
277
278/**
49bd8e4b 279 * Generate 'releaseStream' call and send it to the server. It should make
6bf22e18
S
280 * the server release some channel for media streams.
281 */
f645f1d6 282static int gen_release_stream(URLContext *s, RTMPContext *rt)
6bf22e18
S
283{
284 RTMPPacket pkt;
285 uint8_t *p;
f645f1d6 286 int ret;
6bf22e18 287
f645f1d6
SP
288 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
289 0, 29 + strlen(rt->playpath))) < 0)
290 return ret;
6bf22e18 291
7f804085 292 av_log(s, AV_LOG_DEBUG, "Releasing stream...\n");
6bf22e18
S
293 p = pkt.data;
294 ff_amf_write_string(&p, "releaseStream");
704af3e2 295 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18
S
296 ff_amf_write_null(&p);
297 ff_amf_write_string(&p, rt->playpath);
298
bba287fd
SP
299 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
300 rt->prev_pkt[1]);
6bf22e18 301 ff_rtmp_packet_destroy(&pkt);
f645f1d6 302
bba287fd 303 return ret;
6bf22e18
S
304}
305
306/**
49bd8e4b 307 * Generate 'FCPublish' call and send it to the server. It should make
6bf22e18
S
308 * the server preapare for receiving media streams.
309 */
f645f1d6 310static int gen_fcpublish_stream(URLContext *s, RTMPContext *rt)
6bf22e18
S
311{
312 RTMPPacket pkt;
313 uint8_t *p;
f645f1d6 314 int ret;
6bf22e18 315
f645f1d6
SP
316 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
317 0, 25 + strlen(rt->playpath))) < 0)
318 return ret;
6bf22e18 319
7f804085 320 av_log(s, AV_LOG_DEBUG, "FCPublish stream...\n");
6bf22e18
S
321 p = pkt.data;
322 ff_amf_write_string(&p, "FCPublish");
704af3e2 323 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18
S
324 ff_amf_write_null(&p);
325 ff_amf_write_string(&p, rt->playpath);
326
bba287fd
SP
327 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
328 rt->prev_pkt[1]);
6bf22e18 329 ff_rtmp_packet_destroy(&pkt);
f645f1d6 330
bba287fd 331 return ret;
6bf22e18
S
332}
333
334/**
49bd8e4b 335 * Generate 'FCUnpublish' call and send it to the server. It should make
6bf22e18
S
336 * the server destroy stream.
337 */
f645f1d6 338static int gen_fcunpublish_stream(URLContext *s, RTMPContext *rt)
6bf22e18
S
339{
340 RTMPPacket pkt;
341 uint8_t *p;
f645f1d6 342 int ret;
6bf22e18 343
f645f1d6
SP
344 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
345 0, 27 + strlen(rt->playpath))) < 0)
346 return ret;
6bf22e18 347
7f804085 348 av_log(s, AV_LOG_DEBUG, "UnPublishing stream...\n");
6bf22e18
S
349 p = pkt.data;
350 ff_amf_write_string(&p, "FCUnpublish");
704af3e2 351 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18
S
352 ff_amf_write_null(&p);
353 ff_amf_write_string(&p, rt->playpath);
354
bba287fd
SP
355 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
356 rt->prev_pkt[1]);
6bf22e18 357 ff_rtmp_packet_destroy(&pkt);
f645f1d6 358
bba287fd 359 return ret;
6bf22e18
S
360}
361
362/**
49bd8e4b 363 * Generate 'createStream' call and send it to the server. It should make
9fd6b843
KS
364 * the server allocate some channel for media streams.
365 */
f645f1d6 366static int gen_create_stream(URLContext *s, RTMPContext *rt)
9fd6b843
KS
367{
368 RTMPPacket pkt;
369 uint8_t *p;
f645f1d6 370 int ret;
9fd6b843 371
7f804085 372 av_log(s, AV_LOG_DEBUG, "Creating stream...\n");
f645f1d6
SP
373
374 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
375 0, 25)) < 0)
376 return ret;
9fd6b843
KS
377
378 p = pkt.data;
379 ff_amf_write_string(&p, "createStream");
704af3e2 380 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18 381 ff_amf_write_null(&p);
1eef08f9 382 rt->create_stream_invoke = rt->nb_invokes;
6bf22e18 383
bba287fd
SP
384 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
385 rt->prev_pkt[1]);
6bf22e18 386 ff_rtmp_packet_destroy(&pkt);
f645f1d6 387
bba287fd 388 return ret;
6bf22e18
S
389}
390
391
392/**
49bd8e4b 393 * Generate 'deleteStream' call and send it to the server. It should make
6bf22e18
S
394 * the server remove some channel for media streams.
395 */
f645f1d6 396static int gen_delete_stream(URLContext *s, RTMPContext *rt)
6bf22e18
S
397{
398 RTMPPacket pkt;
399 uint8_t *p;
f645f1d6 400 int ret;
6bf22e18 401
7f804085 402 av_log(s, AV_LOG_DEBUG, "Deleting stream...\n");
f645f1d6
SP
403
404 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
405 0, 34)) < 0)
406 return ret;
6bf22e18
S
407
408 p = pkt.data;
409 ff_amf_write_string(&p, "deleteStream");
1eef08f9 410 ff_amf_write_number(&p, ++rt->nb_invokes);
9fd6b843 411 ff_amf_write_null(&p);
6bf22e18 412 ff_amf_write_number(&p, rt->main_channel_id);
9fd6b843 413
bba287fd
SP
414 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
415 rt->prev_pkt[1]);
9fd6b843 416 ff_rtmp_packet_destroy(&pkt);
f645f1d6 417
bba287fd 418 return ret;
9fd6b843
KS
419}
420
421/**
9477c035
SP
422 * Generate client buffer time and send it to the server.
423 */
424static int gen_buffer_time(URLContext *s, RTMPContext *rt)
425{
426 RTMPPacket pkt;
427 uint8_t *p;
428 int ret;
429
430 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING,
431 1, 10)) < 0)
432 return ret;
433
434 p = pkt.data;
435 bytestream_put_be16(&p, 3);
436 bytestream_put_be32(&p, rt->main_channel_id);
437 bytestream_put_be32(&p, rt->client_buffer_time);
438
439 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
440 rt->prev_pkt[1]);
441 ff_rtmp_packet_destroy(&pkt);
442
443 return ret;
444}
445
446/**
49bd8e4b 447 * Generate 'play' call and send it to the server, then ping the server
9fd6b843
KS
448 * to start actual playing.
449 */
f645f1d6 450static int gen_play(URLContext *s, RTMPContext *rt)
9fd6b843
KS
451{
452 RTMPPacket pkt;
453 uint8_t *p;
f645f1d6 454 int ret;
9fd6b843 455
7f804085 456 av_log(s, AV_LOG_DEBUG, "Sending play command for '%s'\n", rt->playpath);
f645f1d6
SP
457
458 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_VIDEO_CHANNEL, RTMP_PT_INVOKE,
459 0, 29 + strlen(rt->playpath))) < 0)
460 return ret;
461
9fd6b843
KS
462 pkt.extra = rt->main_channel_id;
463
464 p = pkt.data;
465 ff_amf_write_string(&p, "play");
1eef08f9 466 ff_amf_write_number(&p, ++rt->nb_invokes);
9fd6b843
KS
467 ff_amf_write_null(&p);
468 ff_amf_write_string(&p, rt->playpath);
b2e495af 469 ff_amf_write_number(&p, rt->live);
9fd6b843 470
bba287fd
SP
471 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
472 rt->prev_pkt[1]);
9fd6b843
KS
473 ff_rtmp_packet_destroy(&pkt);
474
bba287fd 475 return ret;
9fd6b843
KS
476}
477
478/**
49bd8e4b 479 * Generate 'publish' call and send it to the server.
6bf22e18 480 */
f645f1d6 481static int gen_publish(URLContext *s, RTMPContext *rt)
6bf22e18
S
482{
483 RTMPPacket pkt;
484 uint8_t *p;
f645f1d6 485 int ret;
6bf22e18 486
7f804085 487 av_log(s, AV_LOG_DEBUG, "Sending publish command for '%s'\n", rt->playpath);
f645f1d6
SP
488
489 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SOURCE_CHANNEL, RTMP_PT_INVOKE,
490 0, 30 + strlen(rt->playpath))) < 0)
491 return ret;
492
6bf22e18
S
493 pkt.extra = rt->main_channel_id;
494
495 p = pkt.data;
496 ff_amf_write_string(&p, "publish");
1eef08f9 497 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18
S
498 ff_amf_write_null(&p);
499 ff_amf_write_string(&p, rt->playpath);
500 ff_amf_write_string(&p, "live");
501
bba287fd
SP
502 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
503 rt->prev_pkt[1]);
6bf22e18 504 ff_rtmp_packet_destroy(&pkt);
f645f1d6
SP
505
506 return ret;
6bf22e18
S
507}
508
509/**
49bd8e4b 510 * Generate ping reply and send it to the server.
9fd6b843 511 */
f645f1d6 512static int gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt)
9fd6b843
KS
513{
514 RTMPPacket pkt;
515 uint8_t *p;
f645f1d6
SP
516 int ret;
517
518 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING,
519 ppkt->timestamp + 1, 6)) < 0)
520 return ret;
9fd6b843 521
9fd6b843
KS
522 p = pkt.data;
523 bytestream_put_be16(&p, 7);
4aaebf78 524 bytestream_put_be32(&p, AV_RB32(ppkt->data+2));
bba287fd
SP
525 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
526 rt->prev_pkt[1]);
9fd6b843 527 ff_rtmp_packet_destroy(&pkt);
f645f1d6 528
bba287fd 529 return ret;
9fd6b843
KS
530}
531
bf7c1719 532/**
34d908c0
RS
533 * Generate server bandwidth message and send it to the server.
534 */
f645f1d6 535static int gen_server_bw(URLContext *s, RTMPContext *rt)
34d908c0
RS
536{
537 RTMPPacket pkt;
538 uint8_t *p;
f645f1d6
SP
539 int ret;
540
541 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_SERVER_BW,
542 0, 4)) < 0)
543 return ret;
34d908c0 544
34d908c0 545 p = pkt.data;
c2d38bea 546 bytestream_put_be32(&p, rt->server_bw);
bba287fd
SP
547 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
548 rt->prev_pkt[1]);
34d908c0 549 ff_rtmp_packet_destroy(&pkt);
f645f1d6 550
bba287fd 551 return ret;
34d908c0
RS
552}
553
554/**
d55961fa
SP
555 * Generate check bandwidth message and send it to the server.
556 */
f645f1d6 557static int gen_check_bw(URLContext *s, RTMPContext *rt)
d55961fa
SP
558{
559 RTMPPacket pkt;
560 uint8_t *p;
f645f1d6 561 int ret;
d55961fa 562
f645f1d6
SP
563 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
564 0, 21)) < 0)
565 return ret;
d55961fa
SP
566
567 p = pkt.data;
568 ff_amf_write_string(&p, "_checkbw");
569 ff_amf_write_number(&p, ++rt->nb_invokes);
570 ff_amf_write_null(&p);
571
bba287fd
SP
572 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
573 rt->prev_pkt[1]);
d55961fa 574 ff_rtmp_packet_destroy(&pkt);
f645f1d6
SP
575
576 return ret;
d55961fa
SP
577}
578
579/**
49bd8e4b 580 * Generate report on bytes read so far and send it to the server.
bf7c1719 581 */
f645f1d6 582static int gen_bytes_read(URLContext *s, RTMPContext *rt, uint32_t ts)
bf7c1719
KS
583{
584 RTMPPacket pkt;
585 uint8_t *p;
f645f1d6
SP
586 int ret;
587
588 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_BYTES_READ,
589 ts, 4)) < 0)
590 return ret;
bf7c1719 591
bf7c1719
KS
592 p = pkt.data;
593 bytestream_put_be32(&p, rt->bytes_read);
bba287fd
SP
594 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->chunk_size,
595 rt->prev_pkt[1]);
bf7c1719 596 ff_rtmp_packet_destroy(&pkt);
f645f1d6 597
bba287fd 598 return ret;
bf7c1719
KS
599}
600
3505d557
SP
601int ff_rtmp_calc_digest(const uint8_t *src, int len, int gap,
602 const uint8_t *key, int keylen, uint8_t *dst)
9fd6b843
KS
603{
604 struct AVSHA *sha;
605 uint8_t hmac_buf[64+32] = {0};
606 int i;
607
608 sha = av_mallocz(av_sha_size);
08e93f5b
SP
609 if (!sha)
610 return AVERROR(ENOMEM);
9fd6b843
KS
611
612 if (keylen < 64) {
613 memcpy(hmac_buf, key, keylen);
614 } else {
615 av_sha_init(sha, 256);
616 av_sha_update(sha,key, keylen);
617 av_sha_final(sha, hmac_buf);
618 }
619 for (i = 0; i < 64; i++)
620 hmac_buf[i] ^= HMAC_IPAD_VAL;
621
622 av_sha_init(sha, 256);
623 av_sha_update(sha, hmac_buf, 64);
624 if (gap <= 0) {
625 av_sha_update(sha, src, len);
626 } else { //skip 32 bytes used for storing digest
627 av_sha_update(sha, src, gap);
628 av_sha_update(sha, src + gap + 32, len - gap - 32);
629 }
630 av_sha_final(sha, hmac_buf + 64);
631
632 for (i = 0; i < 64; i++)
633 hmac_buf[i] ^= HMAC_IPAD_VAL ^ HMAC_OPAD_VAL; //reuse XORed key for opad
634 av_sha_init(sha, 256);
635 av_sha_update(sha, hmac_buf, 64+32);
636 av_sha_final(sha, dst);
637
638 av_free(sha);
08e93f5b
SP
639
640 return 0;
9fd6b843
KS
641}
642
0e31088b
SP
643int ff_rtmp_calc_digest_pos(const uint8_t *buf, int off, int mod_val,
644 int add_val)
645{
646 int i, digest_pos = 0;
647
648 for (i = 0; i < 4; i++)
649 digest_pos += buf[i + off];
650 digest_pos = digest_pos % mod_val + add_val;
651
652 return digest_pos;
653}
654
9fd6b843 655/**
49bd8e4b 656 * Put HMAC-SHA2 digest of packet data (except for the bytes where this digest
9fd6b843
KS
657 * will be stored) into that packet.
658 *
659 * @param buf handshake data (1536 bytes)
acd554c1 660 * @param encrypted use an encrypted connection (RTMPE)
9fd6b843
KS
661 * @return offset to the digest inside input data
662 */
acd554c1 663static int rtmp_handshake_imprint_with_digest(uint8_t *buf, int encrypted)
9fd6b843 664{
0e31088b 665 int ret, digest_pos;
9fd6b843 666
acd554c1
SP
667 if (encrypted)
668 digest_pos = ff_rtmp_calc_digest_pos(buf, 772, 728, 776);
669 else
670 digest_pos = ff_rtmp_calc_digest_pos(buf, 8, 728, 12);
9fd6b843 671
3505d557
SP
672 ret = ff_rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
673 rtmp_player_key, PLAYER_KEY_OPEN_PART_LEN,
674 buf + digest_pos);
08e93f5b
SP
675 if (ret < 0)
676 return ret;
677
9fd6b843
KS
678 return digest_pos;
679}
680
681/**
49bd8e4b 682 * Verify that the received server response has the expected digest value.
9fd6b843
KS
683 *
684 * @param buf handshake data received from the server (1536 bytes)
685 * @param off position to search digest offset from
686 * @return 0 if digest is valid, digest position otherwise
687 */
688static int rtmp_validate_digest(uint8_t *buf, int off)
689{
9fd6b843 690 uint8_t digest[32];
0e31088b 691 int ret, digest_pos;
9fd6b843 692
0e31088b 693 digest_pos = ff_rtmp_calc_digest_pos(buf, off, 728, off + 4);
9fd6b843 694
3505d557
SP
695 ret = ff_rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
696 rtmp_server_key, SERVER_KEY_OPEN_PART_LEN,
697 digest);
08e93f5b
SP
698 if (ret < 0)
699 return ret;
700
9fd6b843
KS
701 if (!memcmp(digest, buf + digest_pos, 32))
702 return digest_pos;
703 return 0;
704}
705
706/**
49bd8e4b 707 * Perform handshake with the server by means of exchanging pseudorandom data
9fd6b843
KS
708 * signed with HMAC-SHA2 digest.
709 *
710 * @return 0 if handshake succeeds, negative value otherwise
711 */
712static int rtmp_handshake(URLContext *s, RTMPContext *rt)
713{
714 AVLFG rnd;
715 uint8_t tosend [RTMP_HANDSHAKE_PACKET_SIZE+1] = {
716 3, // unencrypted data
717 0, 0, 0, 0, // client uptime
718 RTMP_CLIENT_VER1,
719 RTMP_CLIENT_VER2,
720 RTMP_CLIENT_VER3,
721 RTMP_CLIENT_VER4,
722 };
723 uint8_t clientdata[RTMP_HANDSHAKE_PACKET_SIZE];
724 uint8_t serverdata[RTMP_HANDSHAKE_PACKET_SIZE+1];
725 int i;
726 int server_pos, client_pos;
acd554c1 727 uint8_t digest[32], signature[32];
acd554c1 728 int ret, type = 0;
9fd6b843 729
7f804085 730 av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
9fd6b843
KS
731
732 av_lfg_init(&rnd, 0xDEADC0DE);
733 // generate handshake packet - 1536 bytes of pseudorandom data
734 for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
735 tosend[i] = av_lfg_get(&rnd) >> 24;
acd554c1 736
f7bfb126 737 if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
acd554c1
SP
738 /* When the client wants to use RTMPE, we have to change the command
739 * byte to 0x06 which means to use encrypted data and we have to set
740 * the flash version to at least 9.0.115.0. */
741 tosend[0] = 6;
742 tosend[5] = 128;
743 tosend[6] = 0;
744 tosend[7] = 3;
745 tosend[8] = 2;
746
747 /* Initialize the Diffie-Hellmann context and generate the public key
748 * to send to the server. */
749 if ((ret = ff_rtmpe_gen_pub_key(rt->stream, tosend + 1)) < 0)
750 return ret;
751 }
752
f7bfb126 753 client_pos = rtmp_handshake_imprint_with_digest(tosend + 1, rt->encrypted);
08e93f5b
SP
754 if (client_pos < 0)
755 return client_pos;
9fd6b843 756
bba287fd
SP
757 if ((ret = ffurl_write(rt->stream, tosend,
758 RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
759 av_log(s, AV_LOG_ERROR, "Cannot write RTMP handshake request\n");
760 return ret;
761 }
762
177bcc95
SP
763 if ((ret = ffurl_read_complete(rt->stream, serverdata,
764 RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
7f804085 765 av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
177bcc95 766 return ret;
9fd6b843 767 }
177bcc95
SP
768
769 if ((ret = ffurl_read_complete(rt->stream, clientdata,
770 RTMP_HANDSHAKE_PACKET_SIZE)) < 0) {
7f804085 771 av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
177bcc95 772 return ret;
9fd6b843
KS
773 }
774
acd554c1 775 av_log(s, AV_LOG_DEBUG, "Type answer %d\n", serverdata[0]);
7f804085 776 av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
9fd6b843
KS
777 serverdata[5], serverdata[6], serverdata[7], serverdata[8]);
778
e2ee11e8 779 if (rt->is_input && serverdata[5] >= 3) {
c7240611 780 server_pos = rtmp_validate_digest(serverdata + 1, 772);
08e93f5b
SP
781 if (server_pos < 0)
782 return server_pos;
783
9fd6b843 784 if (!server_pos) {
acd554c1 785 type = 1;
c7240611 786 server_pos = rtmp_validate_digest(serverdata + 1, 8);
08e93f5b
SP
787 if (server_pos < 0)
788 return server_pos;
789
c7240611 790 if (!server_pos) {
7f804085 791 av_log(s, AV_LOG_ERROR, "Server response validating failed\n");
a4d3f358 792 return AVERROR(EIO);
c7240611 793 }
9fd6b843 794 }
9fd6b843 795
3505d557
SP
796 ret = ff_rtmp_calc_digest(tosend + 1 + client_pos, 32, 0,
797 rtmp_server_key, sizeof(rtmp_server_key),
798 digest);
08e93f5b
SP
799 if (ret < 0)
800 return ret;
801
3505d557 802 ret = ff_rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE - 32,
acd554c1 803 0, digest, 32, signature);
08e93f5b
SP
804 if (ret < 0)
805 return ret;
806
f7bfb126 807 if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
acd554c1
SP
808 /* Compute the shared secret key sent by the server and initialize
809 * the RC4 encryption. */
810 if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
811 tosend + 1, type)) < 0)
812 return ret;
813
814 /* Encrypt the signature received by the server. */
815 ff_rtmpe_encrypt_sig(rt->stream, signature, digest, serverdata[0]);
816 }
817
818 if (memcmp(signature, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) {
7f804085 819 av_log(s, AV_LOG_ERROR, "Signature mismatch\n");
a4d3f358 820 return AVERROR(EIO);
c7240611 821 }
9fd6b843 822
c7240611
KS
823 for (i = 0; i < RTMP_HANDSHAKE_PACKET_SIZE; i++)
824 tosend[i] = av_lfg_get(&rnd) >> 24;
3505d557
SP
825 ret = ff_rtmp_calc_digest(serverdata + 1 + server_pos, 32, 0,
826 rtmp_player_key, sizeof(rtmp_player_key),
827 digest);
08e93f5b
SP
828 if (ret < 0)
829 return ret;
830
3505d557
SP
831 ret = ff_rtmp_calc_digest(tosend, RTMP_HANDSHAKE_PACKET_SIZE - 32, 0,
832 digest, 32,
833 tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32);
08e93f5b
SP
834 if (ret < 0)
835 return ret;
c7240611 836
f7bfb126 837 if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
acd554c1
SP
838 /* Encrypt the signature to be send to the server. */
839 ff_rtmpe_encrypt_sig(rt->stream, tosend +
840 RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
841 serverdata[0]);
842 }
843
c7240611 844 // write reply back to the server
bba287fd
SP
845 if ((ret = ffurl_write(rt->stream, tosend,
846 RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
847 return ret;
acd554c1 848
f7bfb126 849 if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
acd554c1
SP
850 /* Set RC4 keys for encryption and update the keystreams. */
851 if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
852 return ret;
853 }
6bf22e18 854 } else {
f7bfb126 855 if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
acd554c1
SP
856 /* Compute the shared secret key sent by the server and initialize
857 * the RC4 encryption. */
858 if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
859 tosend + 1, 1)) < 0)
860 return ret;
861
862 if (serverdata[0] == 9) {
863 /* Encrypt the signature received by the server. */
864 ff_rtmpe_encrypt_sig(rt->stream, signature, digest,
865 serverdata[0]);
866 }
867 }
868
bba287fd
SP
869 if ((ret = ffurl_write(rt->stream, serverdata + 1,
870 RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
871 return ret;
acd554c1 872
f7bfb126 873 if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
acd554c1
SP
874 /* Set RC4 keys for encryption and update the keystreams. */
875 if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
876 return ret;
877 }
6bf22e18
S
878 }
879
9fd6b843
KS
880 return 0;
881}
882
7be2a7d8
SP
883static int handle_chunk_size(URLContext *s, RTMPPacket *pkt)
884{
885 RTMPContext *rt = s->priv_data;
886 int ret;
887
888 if (pkt->data_size != 4) {
889 av_log(s, AV_LOG_ERROR,
890 "Chunk size change packet is not 4 bytes long (%d)\n",
891 pkt->data_size);
892 return -1;
893 }
894
895 if (!rt->is_input) {
896 if ((ret = ff_rtmp_packet_write(rt->stream, pkt, rt->chunk_size,
897 rt->prev_pkt[1])) < 0)
898 return ret;
899 }
900
901 rt->chunk_size = AV_RB32(pkt->data);
902 if (rt->chunk_size <= 0) {
903 av_log(s, AV_LOG_ERROR, "Incorrect chunk size %d\n", rt->chunk_size);
904 return -1;
905 }
906 av_log(s, AV_LOG_DEBUG, "New chunk size = %d\n", rt->chunk_size);
907
908 return 0;
909}
910
0ffd5161
SP
911static int handle_ping(URLContext *s, RTMPPacket *pkt)
912{
913 RTMPContext *rt = s->priv_data;
914 int t, ret;
915
916 t = AV_RB16(pkt->data);
917 if (t == 6) {
918 if ((ret = gen_pong(s, rt, pkt)) < 0)
919 return ret;
920 }
921
922 return 0;
923}
924
912ecc9a
SP
925static int handle_client_bw(URLContext *s, RTMPPacket *pkt)
926{
927 RTMPContext *rt = s->priv_data;
928
929 if (pkt->data_size < 4) {
930 av_log(s, AV_LOG_ERROR,
931 "Client bandwidth report packet is less than 4 bytes long (%d)\n",
932 pkt->data_size);
933 return -1;
934 }
935 av_log(s, AV_LOG_DEBUG, "Client bandwidth = %d\n", AV_RB32(pkt->data));
936 rt->client_report_size = AV_RB32(pkt->data) >> 1;
937
938 return 0;
939}
940
9b498148
SP
941static int handle_server_bw(URLContext *s, RTMPPacket *pkt)
942{
943 RTMPContext *rt = s->priv_data;
944
945 rt->server_bw = AV_RB32(pkt->data);
946 if (rt->server_bw <= 0) {
947 av_log(s, AV_LOG_ERROR, "Incorrect server bandwidth %d\n",
948 rt->server_bw);
949 return AVERROR(EINVAL);
950 }
951 av_log(s, AV_LOG_DEBUG, "Server bandwidth = %d\n", rt->server_bw);
952
953 return 0;
954}
955
9fd6b843 956/**
49bd8e4b 957 * Parse received packet and possibly perform some action depending on
9fd6b843
KS
958 * the packet contents.
959 * @return 0 for no errors, negative values for serious errors which prevent
960 * further communications, positive values for uncritical errors
961 */
962static int rtmp_parse_result(URLContext *s, RTMPContext *rt, RTMPPacket *pkt)
963{
964 int i, t;
965 const uint8_t *data_end = pkt->data + pkt->data_size;
f645f1d6 966 int ret;
9fd6b843 967
cfac91fe 968#ifdef DEBUG
7f804085 969 ff_rtmp_packet_dump(s, pkt);
cfac91fe
KS
970#endif
971
9fd6b843
KS
972 switch (pkt->type) {
973 case RTMP_PT_CHUNK_SIZE:
7be2a7d8
SP
974 if ((ret = handle_chunk_size(s, pkt)) < 0)
975 return ret;
9fd6b843
KS
976 break;
977 case RTMP_PT_PING:
0ffd5161
SP
978 if ((ret = handle_ping(s, pkt)) < 0)
979 return ret;
9fd6b843 980 break;
bf7c1719 981 case RTMP_PT_CLIENT_BW:
912ecc9a
SP
982 if ((ret = handle_client_bw(s, pkt)) < 0)
983 return ret;
bf7c1719 984 break;
c2d38bea 985 case RTMP_PT_SERVER_BW:
9b498148
SP
986 if ((ret = handle_server_bw(s, pkt)) < 0)
987 return ret;
c2d38bea 988 break;
9fd6b843
KS
989 case RTMP_PT_INVOKE:
990 //TODO: check for the messages sent for wrong state?
991 if (!memcmp(pkt->data, "\002\000\006_error", 9)) {
992 uint8_t tmpstr[256];
993
994 if (!ff_amf_get_field_value(pkt->data + 9, data_end,
995 "description", tmpstr, sizeof(tmpstr)))
7f804085 996 av_log(s, AV_LOG_ERROR, "Server error: %s\n",tmpstr);
9fd6b843
KS
997 return -1;
998 } else if (!memcmp(pkt->data, "\002\000\007_result", 10)) {
999 switch (rt->state) {
1000 case STATE_HANDSHAKED:
6bf22e18 1001 if (!rt->is_input) {
f645f1d6
SP
1002 if ((ret = gen_release_stream(s, rt)) < 0)
1003 return ret;
1004 if ((ret = gen_fcpublish_stream(s, rt)) < 0)
1005 return ret;
6bf22e18
S
1006 rt->state = STATE_RELEASING;
1007 } else {
f645f1d6
SP
1008 if ((ret = gen_server_bw(s, rt)) < 0)
1009 return ret;
6bf22e18
S
1010 rt->state = STATE_CONNECTING;
1011 }
f645f1d6
SP
1012 if ((ret = gen_create_stream(s, rt)) < 0)
1013 return ret;
6bf22e18
S
1014 break;
1015 case STATE_FCPUBLISH:
9fd6b843
KS
1016 rt->state = STATE_CONNECTING;
1017 break;
6bf22e18
S
1018 case STATE_RELEASING:
1019 rt->state = STATE_FCPUBLISH;
1020 /* hack for Wowza Media Server, it does not send result for
1021 * releaseStream and FCPublish calls */
1022 if (!pkt->data[10]) {
3383a53e 1023 int pkt_id = av_int2double(AV_RB64(pkt->data + 11));
1eef08f9 1024 if (pkt_id == rt->create_stream_invoke)
6bf22e18
S
1025 rt->state = STATE_CONNECTING;
1026 }
e07c92e4 1027 if (rt->state != STATE_CONNECTING)
6bf22e18 1028 break;
9fd6b843
KS
1029 case STATE_CONNECTING:
1030 //extract a number from the result
1031 if (pkt->data[10] || pkt->data[19] != 5 || pkt->data[20]) {
7f804085 1032 av_log(s, AV_LOG_WARNING, "Unexpected reply on connect()\n");
9fd6b843 1033 } else {
3383a53e 1034 rt->main_channel_id = av_int2double(AV_RB64(pkt->data + 21));
9fd6b843 1035 }
6bf22e18 1036 if (rt->is_input) {
f645f1d6
SP
1037 if ((ret = gen_play(s, rt)) < 0)
1038 return ret;
9477c035
SP
1039 if ((ret = gen_buffer_time(s, rt)) < 0)
1040 return ret;
6bf22e18 1041 } else {
f645f1d6
SP
1042 if ((ret = gen_publish(s, rt)) < 0)
1043 return ret;
6bf22e18 1044 }
9fd6b843
KS
1045 rt->state = STATE_READY;
1046 break;
1047 }
1048 } else if (!memcmp(pkt->data, "\002\000\010onStatus", 11)) {
1049 const uint8_t* ptr = pkt->data + 11;
1050 uint8_t tmpstr[256];
9fd6b843
KS
1051
1052 for (i = 0; i < 2; i++) {
1053 t = ff_amf_tag_size(ptr, data_end);
1054 if (t < 0)
1055 return 1;
1056 ptr += t;
1057 }
1058 t = ff_amf_get_field_value(ptr, data_end,
1059 "level", tmpstr, sizeof(tmpstr));
1060 if (!t && !strcmp(tmpstr, "error")) {
1061 if (!ff_amf_get_field_value(ptr, data_end,
1062 "description", tmpstr, sizeof(tmpstr)))
7f804085 1063 av_log(s, AV_LOG_ERROR, "Server error: %s\n",tmpstr);
9fd6b843
KS
1064 return -1;
1065 }
1066 t = ff_amf_get_field_value(ptr, data_end,
1067 "code", tmpstr, sizeof(tmpstr));
6bf22e18 1068 if (!t && !strcmp(tmpstr, "NetStream.Play.Start")) rt->state = STATE_PLAYING;
72b870b9
MS
1069 if (!t && !strcmp(tmpstr, "NetStream.Play.Stop")) rt->state = STATE_STOPPED;
1070 if (!t && !strcmp(tmpstr, "NetStream.Play.UnpublishNotify")) rt->state = STATE_STOPPED;
6bf22e18 1071 if (!t && !strcmp(tmpstr, "NetStream.Publish.Start")) rt->state = STATE_PUBLISHING;
d55961fa 1072 } else if (!memcmp(pkt->data, "\002\000\010onBWDone", 11)) {
f645f1d6
SP
1073 if ((ret = gen_check_bw(s, rt)) < 0)
1074 return ret;
9fd6b843
KS
1075 }
1076 break;
08e087cc
JO
1077 case RTMP_PT_VIDEO:
1078 case RTMP_PT_AUDIO:
1079 /* Audio and Video packets are parsed in get_packet() */
1080 break;
9ff930aa
SP
1081 default:
1082 av_log(s, AV_LOG_VERBOSE, "Unknown packet type received 0x%02X\n", pkt->type);
1083 break;
9fd6b843
KS
1084 }
1085 return 0;
1086}
1087
1088/**
49bd8e4b 1089 * Interact with the server by receiving and sending RTMP packets until
9fd6b843
KS
1090 * there is some significant data (media data or expected status notification).
1091 *
1092 * @param s reading context
1d8041b3
SS
1093 * @param for_header non-zero value tells function to work until it
1094 * gets notification from the server that playing has been started,
1095 * otherwise function will work until some media data is received (or
1096 * an error happens)
9fd6b843
KS
1097 * @return 0 for successful operation, negative value in case of error
1098 */
1099static int get_packet(URLContext *s, int for_header)
1100{
1101 RTMPContext *rt = s->priv_data;
1102 int ret;
56e29bf2
S
1103 uint8_t *p;
1104 const uint8_t *next;
1105 uint32_t data_size;
1106 uint32_t ts, cts, pts=0;
9fd6b843 1107
72b870b9
MS
1108 if (rt->state == STATE_STOPPED)
1109 return AVERROR_EOF;
1110
e07c92e4 1111 for (;;) {
271c869c 1112 RTMPPacket rpkt = { 0 };
9fd6b843 1113 if ((ret = ff_rtmp_packet_read(rt->stream, &rpkt,
adb54961 1114 rt->chunk_size, rt->prev_pkt[0])) <= 0) {
b381a823 1115 if (ret == 0) {
9fd6b843
KS
1116 return AVERROR(EAGAIN);
1117 } else {
1118 return AVERROR(EIO);
1119 }
1120 }
bf7c1719
KS
1121 rt->bytes_read += ret;
1122 if (rt->bytes_read > rt->last_bytes_read + rt->client_report_size) {
7f804085 1123 av_log(s, AV_LOG_DEBUG, "Sending bytes read report\n");
f645f1d6
SP
1124 if ((ret = gen_bytes_read(s, rt, rpkt.timestamp + 1)) < 0)
1125 return ret;
bf7c1719
KS
1126 rt->last_bytes_read = rt->bytes_read;
1127 }
9fd6b843
KS
1128
1129 ret = rtmp_parse_result(s, rt, &rpkt);
1130 if (ret < 0) {//serious error in current packet
1131 ff_rtmp_packet_destroy(&rpkt);
f645f1d6 1132 return ret;
9fd6b843 1133 }
72b870b9
MS
1134 if (rt->state == STATE_STOPPED) {
1135 ff_rtmp_packet_destroy(&rpkt);
1136 return AVERROR_EOF;
1137 }
6bf22e18 1138 if (for_header && (rt->state == STATE_PLAYING || rt->state == STATE_PUBLISHING)) {
9fd6b843
KS
1139 ff_rtmp_packet_destroy(&rpkt);
1140 return 0;
1141 }
6bf22e18 1142 if (!rpkt.data_size || !rt->is_input) {
9fd6b843
KS
1143 ff_rtmp_packet_destroy(&rpkt);
1144 continue;
1145 }
1146 if (rpkt.type == RTMP_PT_VIDEO || rpkt.type == RTMP_PT_AUDIO ||
afbacb93 1147 (rpkt.type == RTMP_PT_NOTIFY && !memcmp("\002\000\012onMetaData", rpkt.data, 13))) {
56e29bf2 1148 ts = rpkt.timestamp;
9fd6b843 1149
9fd6b843
KS
1150 // generate packet header and put data into buffer for FLV demuxer
1151 rt->flv_off = 0;
1152 rt->flv_size = rpkt.data_size + 15;
1153 rt->flv_data = p = av_realloc(rt->flv_data, rt->flv_size);
1154 bytestream_put_byte(&p, rpkt.type);
1155 bytestream_put_be24(&p, rpkt.data_size);
1156 bytestream_put_be24(&p, ts);
1157 bytestream_put_byte(&p, ts >> 24);
1158 bytestream_put_be24(&p, 0);
1159 bytestream_put_buffer(&p, rpkt.data, rpkt.data_size);
1160 bytestream_put_be32(&p, 0);
1161 ff_rtmp_packet_destroy(&rpkt);
1162 return 0;
1163 } else if (rpkt.type == RTMP_PT_METADATA) {
1164 // we got raw FLV data, make it available for FLV demuxer
1165 rt->flv_off = 0;
1166 rt->flv_size = rpkt.data_size;
1167 rt->flv_data = av_realloc(rt->flv_data, rt->flv_size);
56e29bf2
S
1168 /* rewrite timestamps */
1169 next = rpkt.data;
1170 ts = rpkt.timestamp;
1171 while (next - rpkt.data < rpkt.data_size - 11) {
1172 next++;
1173 data_size = bytestream_get_be24(&next);
1174 p=next;
1175 cts = bytestream_get_be24(&next);
aae9a093 1176 cts |= bytestream_get_byte(&next) << 24;
56e29bf2
S
1177 if (pts==0)
1178 pts=cts;
1179 ts += cts - pts;
1180 pts = cts;
1181 bytestream_put_be24(&p, ts);
1182 bytestream_put_byte(&p, ts >> 24);
1183 next += data_size + 3 + 4;
1184 }
9fd6b843
KS
1185 memcpy(rt->flv_data, rpkt.data, rpkt.data_size);
1186 ff_rtmp_packet_destroy(&rpkt);
1187 return 0;
1188 }
1189 ff_rtmp_packet_destroy(&rpkt);
1190 }
9fd6b843
KS
1191}
1192
1193static int rtmp_close(URLContext *h)
1194{
1195 RTMPContext *rt = h->priv_data;
f645f1d6 1196 int ret = 0;
9fd6b843 1197
e07c92e4 1198 if (!rt->is_input) {
6bf22e18
S
1199 rt->flv_data = NULL;
1200 if (rt->out_pkt.data_size)
1201 ff_rtmp_packet_destroy(&rt->out_pkt);
615c2879 1202 if (rt->state > STATE_FCPUBLISH)
f645f1d6 1203 ret = gen_fcunpublish_stream(h, rt);
6bf22e18 1204 }
615c2879 1205 if (rt->state > STATE_HANDSHAKED)
f645f1d6 1206 ret = gen_delete_stream(h, rt);
6bf22e18 1207
9fd6b843 1208 av_freep(&rt->flv_data);
e52a9145 1209 ffurl_close(rt->stream);
f645f1d6 1210 return ret;
9fd6b843
KS
1211}
1212
1213/**
49bd8e4b 1214 * Open RTMP connection and verify that the stream can be played.
9fd6b843
KS
1215 *
1216 * URL syntax: rtmp://server[:port][/app][/playpath]
1217 * where 'app' is first one or two directories in the path
1218 * (e.g. /ondemand/, /flash/live/, etc.)
1219 * and 'playpath' is a file name (the rest of the path,
1220 * may be prefixed with "mp4:")
1221 */
1222static int rtmp_open(URLContext *s, const char *uri, int flags)
1223{
7e580505 1224 RTMPContext *rt = s->priv_data;
5e9ad759 1225 char proto[8], hostname[256], path[1024], *fname;
6465562e 1226 char *old_app;
9fd6b843 1227 uint8_t buf[2048];
b316991b 1228 int port;
86991ce2 1229 AVDictionary *opts = NULL;
9fd6b843
KS
1230 int ret;
1231
59d96941 1232 rt->is_input = !(flags & AVIO_FLAG_WRITE);
9fd6b843 1233
f3bfe388 1234 av_url_split(proto, sizeof(proto), NULL, 0, hostname, sizeof(hostname), &port,
f984dcf6 1235 path, sizeof(path), s->filename);
9fd6b843 1236
86991ce2
SP
1237 if (!strcmp(proto, "rtmpt") || !strcmp(proto, "rtmpts")) {
1238 if (!strcmp(proto, "rtmpts"))
1239 av_dict_set(&opts, "ffrtmphttp_tls", "1", 1);
1240
8e50c57d 1241 /* open the http tunneling connection */
775c4d36 1242 ff_url_join(buf, sizeof(buf), "ffrtmphttp", NULL, hostname, port, NULL);
6aedabc9
SP
1243 } else if (!strcmp(proto, "rtmps")) {
1244 /* open the tls connection */
1245 if (port < 0)
1246 port = RTMPS_DEFAULT_PORT;
1247 ff_url_join(buf, sizeof(buf), "tls", NULL, hostname, port, NULL);
08cd95e8
SP
1248 } else if (!strcmp(proto, "rtmpe") || (!strcmp(proto, "rtmpte"))) {
1249 if (!strcmp(proto, "rtmpte"))
1250 av_dict_set(&opts, "ffrtmpcrypt_tunneling", "1", 1);
1251
acd554c1
SP
1252 /* open the encrypted connection */
1253 ff_url_join(buf, sizeof(buf), "ffrtmpcrypt", NULL, hostname, port, NULL);
1254 rt->encrypted = 1;
8e50c57d
SP
1255 } else {
1256 /* open the tcp connection */
1257 if (port < 0)
1258 port = RTMP_DEFAULT_PORT;
1259 ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL);
1260 }
9fd6b843 1261
f645f1d6 1262 if ((ret = ffurl_open(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
86991ce2 1263 &s->interrupt_callback, &opts)) < 0) {
59d96941 1264 av_log(s , AV_LOG_ERROR, "Cannot open connection %s\n", buf);
9fd6b843 1265 goto fail;
fe523958 1266 }
9fd6b843 1267
c7240611 1268 rt->state = STATE_START;
f645f1d6 1269 if ((ret = rtmp_handshake(s, rt)) < 0)
02490bf3 1270 goto fail;
9fd6b843 1271
c7240611
KS
1272 rt->chunk_size = 128;
1273 rt->state = STATE_HANDSHAKED;
6465562e
SP
1274
1275 // Keep the application name when it has been defined by the user.
1276 old_app = rt->app;
1277
1278 rt->app = av_malloc(APP_MAX_LENGTH);
1279 if (!rt->app) {
f645f1d6
SP
1280 ret = AVERROR(ENOMEM);
1281 goto fail;
6465562e
SP
1282 }
1283
c7240611
KS
1284 //extract "app" part from path
1285 if (!strncmp(path, "/ondemand/", 10)) {
1286 fname = path + 10;
1287 memcpy(rt->app, "ondemand", 9);
1288 } else {
4b7304e8
MS
1289 char *next = *path ? path + 1 : path;
1290 char *p = strchr(next, '/');
c7240611 1291 if (!p) {
4b7304e8 1292 fname = next;
c7240611 1293 rt->app[0] = '\0';
9fd6b843 1294 } else {
c6eeb9b7 1295 // make sure we do not mismatch a playpath for an application instance
c7240611
KS
1296 char *c = strchr(p + 1, ':');
1297 fname = strchr(p + 1, '/');
c6eeb9b7 1298 if (!fname || (c && c < fname)) {
c7240611
KS
1299 fname = p + 1;
1300 av_strlcpy(rt->app, path + 1, p - path);
9fd6b843 1301 } else {
c7240611
KS
1302 fname++;
1303 av_strlcpy(rt->app, path + 1, fname - path - 1);
9fd6b843
KS
1304 }
1305 }
c7240611 1306 }
6465562e
SP
1307
1308 if (old_app) {
1309 // The name of application has been defined by the user, override it.
1310 av_free(rt->app);
1311 rt->app = old_app;
1312 }
1313
b3b17512 1314 if (!rt->playpath) {
f862537d
SP
1315 int len = strlen(fname);
1316
b3b17512
SP
1317 rt->playpath = av_malloc(PLAYPATH_MAX_LENGTH);
1318 if (!rt->playpath) {
f645f1d6
SP
1319 ret = AVERROR(ENOMEM);
1320 goto fail;
b3b17512
SP
1321 }
1322
0a9a2257 1323 if (!strchr(fname, ':') && len >= 4 &&
f862537d
SP
1324 (!strcmp(fname + len - 4, ".f4v") ||
1325 !strcmp(fname + len - 4, ".mp4"))) {
b3b17512 1326 memcpy(rt->playpath, "mp4:", 5);
0a9a2257 1327 } else if (len >= 4 && !strcmp(fname + len - 4, ".flv")) {
f862537d 1328 fname[len - 4] = '\0';
b3b17512
SP
1329 } else {
1330 rt->playpath[0] = 0;
1331 }
1332 strncat(rt->playpath, fname, PLAYPATH_MAX_LENGTH - 5);
c7240611 1333 }
9fd6b843 1334
55c9320e
SP
1335 if (!rt->tcurl) {
1336 rt->tcurl = av_malloc(TCURL_MAX_LENGTH);
08e93f5b
SP
1337 if (!rt->tcurl) {
1338 ret = AVERROR(ENOMEM);
1339 goto fail;
1340 }
55c9320e
SP
1341 ff_url_join(rt->tcurl, TCURL_MAX_LENGTH, proto, NULL, hostname,
1342 port, "/%s", rt->app);
1343 }
1344
e64673e4
SP
1345 if (!rt->flashver) {
1346 rt->flashver = av_malloc(FLASHVER_MAX_LENGTH);
08e93f5b
SP
1347 if (!rt->flashver) {
1348 ret = AVERROR(ENOMEM);
1349 goto fail;
1350 }
e64673e4
SP
1351 if (rt->is_input) {
1352 snprintf(rt->flashver, FLASHVER_MAX_LENGTH, "%s %d,%d,%d,%d",
1353 RTMP_CLIENT_PLATFORM, RTMP_CLIENT_VER1, RTMP_CLIENT_VER2,
1354 RTMP_CLIENT_VER3, RTMP_CLIENT_VER4);
1355 } else {
1356 snprintf(rt->flashver, FLASHVER_MAX_LENGTH,
1357 "FMLE/3.0 (compatible; %s)", LIBAVFORMAT_IDENT);
1358 }
1359 }
1360
bf7c1719
KS
1361 rt->client_report_size = 1048576;
1362 rt->bytes_read = 0;
1363 rt->last_bytes_read = 0;
c2d38bea 1364 rt->server_bw = 2500000;
bf7c1719 1365
7f804085 1366 av_log(s, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s\n",
c7240611 1367 proto, path, rt->app, rt->playpath);
f645f1d6
SP
1368 if ((ret = gen_connect(s, rt)) < 0)
1369 goto fail;
9fd6b843 1370
c7240611
KS
1371 do {
1372 ret = get_packet(s, 1);
1373 } while (ret == EAGAIN);
1374 if (ret < 0)
1375 goto fail;
6bf22e18
S
1376
1377 if (rt->is_input) {
9fd6b843
KS
1378 // generate FLV header for demuxer
1379 rt->flv_size = 13;
1380 rt->flv_data = av_realloc(rt->flv_data, rt->flv_size);
1381 rt->flv_off = 0;
1382 memcpy(rt->flv_data, "FLV\1\5\0\0\0\011\0\0\0\0", rt->flv_size);
6bf22e18
S
1383 } else {
1384 rt->flv_size = 0;
1385 rt->flv_data = NULL;
1386 rt->flv_off = 0;
b14629e5 1387 rt->skip_bytes = 13;
9fd6b843
KS
1388 }
1389
5958df34 1390 s->max_packet_size = rt->stream->max_packet_size;
9fd6b843
KS
1391 s->is_streamed = 1;
1392 return 0;
1393
1394fail:
86991ce2 1395 av_dict_free(&opts);
9fd6b843 1396 rtmp_close(s);
f645f1d6 1397 return ret;
9fd6b843
KS
1398}
1399
1400static int rtmp_read(URLContext *s, uint8_t *buf, int size)
1401{
1402 RTMPContext *rt = s->priv_data;
1403 int orig_size = size;
1404 int ret;
1405
1406 while (size > 0) {
1407 int data_left = rt->flv_size - rt->flv_off;
1408
1409 if (data_left >= size) {
1410 memcpy(buf, rt->flv_data + rt->flv_off, size);
1411 rt->flv_off += size;
1412 return orig_size;
1413 }
1414 if (data_left > 0) {
1415 memcpy(buf, rt->flv_data + rt->flv_off, data_left);
1416 buf += data_left;
1417 size -= data_left;
1418 rt->flv_off = rt->flv_size;
e8ccf245 1419 return data_left;
9fd6b843
KS
1420 }
1421 if ((ret = get_packet(s, 0)) < 0)
1422 return ret;
1423 }
1424 return orig_size;
1425}
1426
9ad4c65f 1427static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
9fd6b843 1428{
9ad4c65f 1429 RTMPContext *rt = s->priv_data;
6bf22e18
S
1430 int size_temp = size;
1431 int pktsize, pkttype;
1432 uint32_t ts;
1433 const uint8_t *buf_temp = buf;
7dc747f5 1434 uint8_t c;
f645f1d6 1435 int ret;
6bf22e18 1436
6bf22e18 1437 do {
b14629e5
MS
1438 if (rt->skip_bytes) {
1439 int skip = FFMIN(rt->skip_bytes, size_temp);
1440 buf_temp += skip;
1441 size_temp -= skip;
1442 rt->skip_bytes -= skip;
1443 continue;
1444 }
1445
1446 if (rt->flv_header_bytes < 11) {
1447 const uint8_t *header = rt->flv_header;
1448 int copy = FFMIN(11 - rt->flv_header_bytes, size_temp);
1449 bytestream_get_buffer(&buf_temp, rt->flv_header + rt->flv_header_bytes, copy);
1450 rt->flv_header_bytes += copy;
1451 size_temp -= copy;
1452 if (rt->flv_header_bytes < 11)
1453 break;
6bf22e18 1454
b14629e5
MS
1455 pkttype = bytestream_get_byte(&header);
1456 pktsize = bytestream_get_be24(&header);
1457 ts = bytestream_get_be24(&header);
1458 ts |= bytestream_get_byte(&header) << 24;
1459 bytestream_get_be24(&header);
6bf22e18
S
1460 rt->flv_size = pktsize;
1461
1462 //force 12bytes header
1463 if (((pkttype == RTMP_PT_VIDEO || pkttype == RTMP_PT_AUDIO) && ts == 0) ||
1464 pkttype == RTMP_PT_NOTIFY) {
1465 if (pkttype == RTMP_PT_NOTIFY)
1466 pktsize += 16;
1467 rt->prev_pkt[1][RTMP_SOURCE_CHANNEL].channel_id = 0;
1468 }
1469
1470 //this can be a big packet, it's better to send it right here
f645f1d6
SP
1471 if ((ret = ff_rtmp_packet_create(&rt->out_pkt, RTMP_SOURCE_CHANNEL,
1472 pkttype, ts, pktsize)) < 0)
1473 return ret;
1474
6bf22e18
S
1475 rt->out_pkt.extra = rt->main_channel_id;
1476 rt->flv_data = rt->out_pkt.data;
1477
1478 if (pkttype == RTMP_PT_NOTIFY)
1479 ff_amf_write_string(&rt->flv_data, "@setDataFrame");
1480 }
1481
1482 if (rt->flv_size - rt->flv_off > size_temp) {
1483 bytestream_get_buffer(&buf_temp, rt->flv_data + rt->flv_off, size_temp);
1484 rt->flv_off += size_temp;
a14c7842 1485 size_temp = 0;
6bf22e18
S
1486 } else {
1487 bytestream_get_buffer(&buf_temp, rt->flv_data + rt->flv_off, rt->flv_size - rt->flv_off);
a14c7842 1488 size_temp -= rt->flv_size - rt->flv_off;
6bf22e18
S
1489 rt->flv_off += rt->flv_size - rt->flv_off;
1490 }
1491
1492 if (rt->flv_off == rt->flv_size) {
b14629e5
MS
1493 rt->skip_bytes = 4;
1494
bba287fd
SP
1495 if ((ret = ff_rtmp_packet_write(rt->stream, &rt->out_pkt,
1496 rt->chunk_size, rt->prev_pkt[1])) < 0)
1497 return ret;
6bf22e18
S
1498 ff_rtmp_packet_destroy(&rt->out_pkt);
1499 rt->flv_size = 0;
1500 rt->flv_off = 0;
b14629e5 1501 rt->flv_header_bytes = 0;
46743a85 1502 rt->flv_nb_packets++;
6bf22e18 1503 }
a14c7842 1504 } while (buf_temp - buf < size);
7dc747f5 1505
46743a85
SP
1506 if (rt->flv_nb_packets < rt->flush_interval)
1507 return size;
1508 rt->flv_nb_packets = 0;
1509
7dc747f5
SP
1510 /* set stream into nonblocking mode */
1511 rt->stream->flags |= AVIO_FLAG_NONBLOCK;
1512
1513 /* try to read one byte from the stream */
1514 ret = ffurl_read(rt->stream, &c, 1);
1515
1516 /* switch the stream back into blocking mode */
1517 rt->stream->flags &= ~AVIO_FLAG_NONBLOCK;
1518
1519 if (ret == AVERROR(EAGAIN)) {
1520 /* no incoming data to handle */
1521 return size;
1522 } else if (ret < 0) {
1523 return ret;
1524 } else if (ret == 1) {
1525 RTMPPacket rpkt = { 0 };
1526
1527 if ((ret = ff_rtmp_packet_read_internal(rt->stream, &rpkt,
1528 rt->chunk_size,
1529 rt->prev_pkt[0], c)) <= 0)
1530 return ret;
1531
1532 if ((ret = rtmp_parse_result(s, rt, &rpkt)) < 0)
1533 return ret;
1534
1535 ff_rtmp_packet_destroy(&rpkt);
1536 }
1537
6bf22e18 1538 return size;
9fd6b843
KS
1539}
1540
6465562e
SP
1541#define OFFSET(x) offsetof(RTMPContext, x)
1542#define DEC AV_OPT_FLAG_DECODING_PARAM
1543#define ENC AV_OPT_FLAG_ENCODING_PARAM
1544
1545static const AVOption rtmp_options[] = {
1546 {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
8517e9c4 1547 {"rtmp_buffer", "Set buffer time in milliseconds. The default is 3000.", OFFSET(client_buffer_time), AV_OPT_TYPE_INT, {3000}, 0, INT_MAX, DEC|ENC},
8ee3e187 1548 {"rtmp_conn", "Append arbitrary AMF data to the Connect message", OFFSET(conn), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
e64673e4 1549 {"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},
46743a85 1550 {"rtmp_flush_interval", "Number of packets flushed in the same request (RTMPT only).", OFFSET(flush_interval), AV_OPT_TYPE_INT, {10}, 0, INT_MAX, ENC},
b2e495af
SP
1551 {"rtmp_live", "Specify that the media is a live stream.", OFFSET(live), AV_OPT_TYPE_INT, {-2}, INT_MIN, INT_MAX, DEC, "rtmp_live"},
1552 {"any", "both", 0, AV_OPT_TYPE_CONST, {-2}, 0, 0, DEC, "rtmp_live"},
1553 {"live", "live stream", 0, AV_OPT_TYPE_CONST, {-1}, 0, 0, DEC, "rtmp_live"},
1554 {"recorded", "recorded stream", 0, AV_OPT_TYPE_CONST, {0}, 0, 0, DEC, "rtmp_live"},
758377a2 1555 {"rtmp_pageurl", "URL of the web page in which the media was embedded. By default no value will be sent.", OFFSET(pageurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
b3b17512 1556 {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
05945db9 1557 {"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},
63ffa154 1558 {"rtmp_tcurl", "URL of the target stream. Defaults to proto://host[:port]/app.", OFFSET(tcurl), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
6465562e
SP
1559 { NULL },
1560};
1561
1562static const AVClass rtmp_class = {
1563 .class_name = "rtmp",
1564 .item_name = av_default_item_name,
1565 .option = rtmp_options,
1566 .version = LIBAVUTIL_VERSION_INT,
1567};
1568
c6610a21 1569URLProtocol ff_rtmp_protocol = {
c3b05d21
MS
1570 .name = "rtmp",
1571 .url_open = rtmp_open,
1572 .url_read = rtmp_read,
1573 .url_write = rtmp_write,
1574 .url_close = rtmp_close,
7e580505 1575 .priv_data_size = sizeof(RTMPContext),
32b83aee 1576 .flags = URL_PROTOCOL_FLAG_NETWORK,
6465562e 1577 .priv_data_class= &rtmp_class,
9fd6b843 1578};
8e50c57d 1579
acd554c1
SP
1580static const AVClass rtmpe_class = {
1581 .class_name = "rtmpe",
1582 .item_name = av_default_item_name,
1583 .option = rtmp_options,
1584 .version = LIBAVUTIL_VERSION_INT,
1585};
1586
1587URLProtocol ff_rtmpe_protocol = {
1588 .name = "rtmpe",
1589 .url_open = rtmp_open,
1590 .url_read = rtmp_read,
1591 .url_write = rtmp_write,
1592 .url_close = rtmp_close,
1593 .priv_data_size = sizeof(RTMPContext),
1594 .flags = URL_PROTOCOL_FLAG_NETWORK,
1595 .priv_data_class = &rtmpe_class,
1596};
1597
6aedabc9
SP
1598static const AVClass rtmps_class = {
1599 .class_name = "rtmps",
1600 .item_name = av_default_item_name,
1601 .option = rtmp_options,
1602 .version = LIBAVUTIL_VERSION_INT,
1603};
1604
1605URLProtocol ff_rtmps_protocol = {
1606 .name = "rtmps",
1607 .url_open = rtmp_open,
1608 .url_read = rtmp_read,
1609 .url_write = rtmp_write,
1610 .url_close = rtmp_close,
1611 .priv_data_size = sizeof(RTMPContext),
1612 .flags = URL_PROTOCOL_FLAG_NETWORK,
1613 .priv_data_class = &rtmps_class,
1614};
1615
8e50c57d
SP
1616static const AVClass rtmpt_class = {
1617 .class_name = "rtmpt",
1618 .item_name = av_default_item_name,
1619 .option = rtmp_options,
1620 .version = LIBAVUTIL_VERSION_INT,
1621};
1622
1623URLProtocol ff_rtmpt_protocol = {
1624 .name = "rtmpt",
1625 .url_open = rtmp_open,
1626 .url_read = rtmp_read,
1627 .url_write = rtmp_write,
1628 .url_close = rtmp_close,
1629 .priv_data_size = sizeof(RTMPContext),
1630 .flags = URL_PROTOCOL_FLAG_NETWORK,
1631 .priv_data_class = &rtmpt_class,
1632};
86991ce2 1633
08cd95e8
SP
1634static const AVClass rtmpte_class = {
1635 .class_name = "rtmpte",
1636 .item_name = av_default_item_name,
1637 .option = rtmp_options,
1638 .version = LIBAVUTIL_VERSION_INT,
1639};
1640
1641URLProtocol ff_rtmpte_protocol = {
1642 .name = "rtmpte",
1643 .url_open = rtmp_open,
1644 .url_read = rtmp_read,
1645 .url_write = rtmp_write,
1646 .url_close = rtmp_close,
1647 .priv_data_size = sizeof(RTMPContext),
1648 .flags = URL_PROTOCOL_FLAG_NETWORK,
1649 .priv_data_class = &rtmpte_class,
1650};
1651
86991ce2
SP
1652static const AVClass rtmpts_class = {
1653 .class_name = "rtmpts",
1654 .item_name = av_default_item_name,
1655 .option = rtmp_options,
1656 .version = LIBAVUTIL_VERSION_INT,
1657};
1658
1659URLProtocol ff_rtmpts_protocol = {
1660 .name = "rtmpts",
1661 .url_open = rtmp_open,
1662 .url_read = rtmp_read,
1663 .url_write = rtmp_write,
1664 .url_close = rtmp_close,
1665 .priv_data_size = sizeof(RTMPContext),
1666 .flags = URL_PROTOCOL_FLAG_NETWORK,
1667 .priv_data_class = &rtmpts_class,
1668};