h264_sei: Return meaningful values
[libav.git] / libavformat / rtmpproto.c
CommitLineData
9fd6b843
KS
1/*
2 * RTMP network protocol
de421b20 3 * Copyright (c) 2009 Konstantin Shishkov
9fd6b843 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"
08225d01 29#include "libavutil/base64.h"
3383a53e 30#include "libavutil/intfloat.h"
9fd6b843 31#include "libavutil/lfg.h"
08225d01 32#include "libavutil/md5.h"
6465562e 33#include "libavutil/opt.h"
e5f2731c 34#include "libavutil/random_seed.h"
9fd6b843
KS
35#include "libavutil/sha.h"
36#include "avformat.h"
e4a9e3cc 37#include "internal.h"
9fd6b843
KS
38
39#include "network.h"
40
41#include "flv.h"
42#include "rtmp.h"
acd554c1 43#include "rtmpcrypt.h"
9fd6b843 44#include "rtmppkt.h"
0589da0a 45#include "url.h"
9fd6b843 46
93f257db
SP
47#if CONFIG_ZLIB
48#include <zlib.h>
49#endif
50
6465562e 51#define APP_MAX_LENGTH 128
b3b17512 52#define PLAYPATH_MAX_LENGTH 256
55c9320e 53#define TCURL_MAX_LENGTH 512
e64673e4 54#define FLASHVER_MAX_LENGTH 64
e5f2731c 55#define RTMP_PKTDATA_DEFAULT_SIZE 4096
6465562e 56
9fd6b843
KS
57/** RTMP protocol handler state */
58typedef enum {
59 STATE_START, ///< client has not done anything yet
60 STATE_HANDSHAKED, ///< client has performed handshake
6bf22e18 61 STATE_FCPUBLISH, ///< client FCPublishing stream (for output)
9fd6b843 62 STATE_PLAYING, ///< client has started receiving multimedia data from server
6bf22e18 63 STATE_PUBLISHING, ///< client has started sending multimedia data to server (for output)
e5f2731c 64 STATE_RECEIVING, ///< received a publish command (for input)
72b870b9 65 STATE_STOPPED, ///< the broadcast has been stopped
9fd6b843
KS
66} ClientState;
67
f89584ca
SP
68typedef struct TrackedMethod {
69 char *name;
70 int id;
71} TrackedMethod;
72
9fd6b843
KS
73/** protocol handler context */
74typedef struct RTMPContext {
6465562e 75 const AVClass *class;
9fd6b843
KS
76 URLContext* stream; ///< TCP stream used in interactions with RTMP server
77 RTMPPacket prev_pkt[2][RTMP_CHANNELS]; ///< packet history used when reading and sending packets
f5ce90f2
JO
78 int in_chunk_size; ///< size of the chunks incoming RTMP packets are divided into
79 int out_chunk_size; ///< size of the chunks outgoing RTMP packets are divided into
b316991b 80 int is_input; ///< input/output flag
b3b17512 81 char *playpath; ///< stream identifier to play (with possible "mp4:" prefix)
b2e495af 82 int live; ///< 0: recorded, -1: live, -2: both
6465562e 83 char *app; ///< name of application
8ee3e187 84 char *conn; ///< append arbitrary AMF data to the Connect message
9fd6b843
KS
85 ClientState state; ///< current state
86 int main_channel_id; ///< an additional channel ID which is used for some invocations
87 uint8_t* flv_data; ///< buffer with data for demuxer
88 int flv_size; ///< current buffer size
89 int flv_off; ///< number of bytes read from current buffer
46743a85 90 int flv_nb_packets; ///< number of flv packets published
6bf22e18 91 RTMPPacket out_pkt; ///< rtmp packet, created from flv a/v or metadata (for output)
bf7c1719
KS
92 uint32_t client_report_size; ///< number of bytes after which client should report to server
93 uint32_t bytes_read; ///< number of bytes read from server
94 uint32_t last_bytes_read; ///< number of bytes read last reported to server
3ffe32eb 95 int skip_bytes; ///< number of bytes to skip from the input FLV stream in the next write call
b14629e5
MS
96 uint8_t flv_header[11]; ///< partial incoming flv packet header
97 int flv_header_bytes; ///< number of initialized bytes in flv_header
704af3e2 98 int nb_invokes; ///< keeps track of invoke messages
55c9320e 99 char* tcurl; ///< url of the target stream
e64673e4 100 char* flashver; ///< version of the flash plugin
635ac8e1
SP
101 char* swfhash; ///< SHA256 hash of the decompressed SWF file (32 bytes)
102 int swfhash_len; ///< length of the SHA256 hash
103 int swfsize; ///< size of the decompressed SWF file
05945db9 104 char* swfurl; ///< url of the swf player
93f257db 105 char* swfverify; ///< URL to player swf file, compute hash/size automatically
635ac8e1 106 char swfverification[42]; ///< hash of the SWF verification
758377a2 107 char* pageurl; ///< url of the web page
00cb52c6 108 char* subscribe; ///< name of live stream to subscribe
c2d38bea 109 int server_bw; ///< server bandwidth
9477c035 110 int client_buffer_time; ///< client buffer time in ms
46743a85 111 int flush_interval; ///< number of packets flushed in the same request (RTMPT only)
acd554c1 112 int encrypted; ///< use an encrypted connection (RTMPE only)
f89584ca
SP
113 TrackedMethod*tracked_methods; ///< tracked methods buffer
114 int nb_tracked_methods; ///< number of tracked methods
115 int tracked_methods_size; ///< size of the tracked methods buffer
e5f2731c
JO
116 int listen; ///< listen mode flag
117 int listen_timeout; ///< listen timeout to wait for new connections
118 int nb_streamid; ///< The next stream id to return on createStream calls
08225d01
MS
119 char username[50];
120 char password[50];
121 char auth_params[500];
122 int do_reconnect;
123 int auth_tried;
9fd6b843
KS
124} RTMPContext;
125
126#define PLAYER_KEY_OPEN_PART_LEN 30 ///< length of partial key used for first client digest signing
127/** Client key used for digest signing */
128static const uint8_t rtmp_player_key[] = {
129 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
130 'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ', '0', '0', '1',
131
132 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
133 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
134 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
135};
136
137#define SERVER_KEY_OPEN_PART_LEN 36 ///< length of partial key used for first server digest signing
138/** Key used for RTMP server digest signing */
139static const uint8_t rtmp_server_key[] = {
140 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
141 'F', 'l', 'a', 's', 'h', ' ', 'M', 'e', 'd', 'i', 'a', ' ',
142 'S', 'e', 'r', 'v', 'e', 'r', ' ', '0', '0', '1',
143
144 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
145 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
146 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
147};
148
f89584ca
SP
149static int add_tracked_method(RTMPContext *rt, const char *name, int id)
150{
151 void *ptr;
152
153 if (rt->nb_tracked_methods + 1 > rt->tracked_methods_size) {
154 rt->tracked_methods_size = (rt->nb_tracked_methods + 1) * 2;
155 ptr = av_realloc(rt->tracked_methods,
156 rt->tracked_methods_size * sizeof(*rt->tracked_methods));
157 if (!ptr)
158 return AVERROR(ENOMEM);
159 rt->tracked_methods = ptr;
160 }
161
162 rt->tracked_methods[rt->nb_tracked_methods].name = av_strdup(name);
163 if (!rt->tracked_methods[rt->nb_tracked_methods].name)
164 return AVERROR(ENOMEM);
165 rt->tracked_methods[rt->nb_tracked_methods].id = id;
166 rt->nb_tracked_methods++;
167
168 return 0;
169}
170
171static void del_tracked_method(RTMPContext *rt, int index)
172{
173 memmove(&rt->tracked_methods[index], &rt->tracked_methods[index + 1],
174 sizeof(*rt->tracked_methods) * (rt->nb_tracked_methods - index - 1));
175 rt->nb_tracked_methods--;
176}
177
a8103503
SP
178static int find_tracked_method(URLContext *s, RTMPPacket *pkt, int offset,
179 char **tracked_method)
180{
181 RTMPContext *rt = s->priv_data;
182 GetByteContext gbc;
183 double pkt_id;
184 int ret;
185 int i;
186
187 bytestream2_init(&gbc, pkt->data + offset, pkt->data_size - offset);
188 if ((ret = ff_amf_read_number(&gbc, &pkt_id)) < 0)
189 return ret;
190
191 for (i = 0; i < rt->nb_tracked_methods; i++) {
192 if (rt->tracked_methods[i].id != pkt_id)
193 continue;
194
195 *tracked_method = rt->tracked_methods[i].name;
196 del_tracked_method(rt, i);
197 break;
198 }
199
200 return 0;
201}
202
f89584ca
SP
203static void free_tracked_methods(RTMPContext *rt)
204{
205 int i;
206
207 for (i = 0; i < rt->nb_tracked_methods; i ++)
208 av_free(rt->tracked_methods[i].name);
209 av_free(rt->tracked_methods);
08225d01
MS
210 rt->tracked_methods = NULL;
211 rt->tracked_methods_size = 0;
212 rt->nb_tracked_methods = 0;
f89584ca
SP
213}
214
215static int rtmp_send_packet(RTMPContext *rt, RTMPPacket *pkt, int track)
216{
217 int ret;
218
219 if (pkt->type == RTMP_PT_INVOKE && track) {
220 GetByteContext gbc;
221 char name[128];
222 double pkt_id;
223 int len;
224
225 bytestream2_init(&gbc, pkt->data, pkt->data_size);
226 if ((ret = ff_amf_read_string(&gbc, name, sizeof(name), &len)) < 0)
227 goto fail;
228
229 if ((ret = ff_amf_read_number(&gbc, &pkt_id)) < 0)
230 goto fail;
231
232 if ((ret = add_tracked_method(rt, name, pkt_id)) < 0)
233 goto fail;
234 }
235
f5ce90f2 236 ret = ff_rtmp_packet_write(rt->stream, pkt, rt->out_chunk_size,
f89584ca
SP
237 rt->prev_pkt[1]);
238fail:
239 ff_rtmp_packet_destroy(pkt);
240 return ret;
241}
242
8ee3e187
SP
243static int rtmp_write_amf_data(URLContext *s, char *param, uint8_t **p)
244{
05338686 245 char *field, *value;
8ee3e187
SP
246 char type;
247
248 /* The type must be B for Boolean, N for number, S for string, O for
249 * object, or Z for null. For Booleans the data must be either 0 or 1 for
250 * FALSE or TRUE, respectively. Likewise for Objects the data must be
251 * 0 or 1 to end or begin an object, respectively. Data items in subobjects
252 * may be named, by prefixing the type with 'N' and specifying the name
253 * before the value (ie. NB:myFlag:1). This option may be used multiple times
254 * to construct arbitrary AMF sequences. */
255 if (param[0] && param[1] == ':') {
256 type = param[0];
257 value = param + 2;
258 } else if (param[0] == 'N' && param[1] && param[2] == ':') {
259 type = param[1];
05338686
MS
260 field = param + 3;
261 value = strchr(field, ':');
262 if (!value)
263 goto fail;
264 *value = '\0';
265 value++;
8ee3e187
SP
266
267 if (!field || !value)
268 goto fail;
269
270 ff_amf_write_field_name(p, field);
271 } else {
272 goto fail;
273 }
274
275 switch (type) {
276 case 'B':
277 ff_amf_write_bool(p, value[0] != '0');
278 break;
279 case 'S':
280 ff_amf_write_string(p, value);
281 break;
282 case 'N':
283 ff_amf_write_number(p, strtod(value, NULL));
284 break;
285 case 'Z':
286 ff_amf_write_null(p);
287 break;
288 case 'O':
289 if (value[0] != '0')
290 ff_amf_write_object_start(p);
291 else
292 ff_amf_write_object_end(p);
293 break;
294 default:
295 goto fail;
296 break;
297 }
298
299 return 0;
300
301fail:
302 av_log(s, AV_LOG_ERROR, "Invalid AMF parameter: %s\n", param);
303 return AVERROR(EINVAL);
304}
305
9fd6b843 306/**
49bd8e4b 307 * Generate 'connect' call and send it to the server.
9fd6b843 308 */
f645f1d6 309static int gen_connect(URLContext *s, RTMPContext *rt)
9fd6b843
KS
310{
311 RTMPPacket pkt;
e64673e4 312 uint8_t *p;
f645f1d6
SP
313 int ret;
314
315 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
316 0, 4096)) < 0)
317 return ret;
9fd6b843 318
9fd6b843
KS
319 p = pkt.data;
320
9fd6b843 321 ff_amf_write_string(&p, "connect");
1eef08f9 322 ff_amf_write_number(&p, ++rt->nb_invokes);
9fd6b843
KS
323 ff_amf_write_object_start(&p);
324 ff_amf_write_field_name(&p, "app");
08225d01 325 ff_amf_write_string2(&p, rt->app, rt->auth_params);
9fd6b843 326
e64673e4 327 if (!rt->is_input) {
6bf22e18
S
328 ff_amf_write_field_name(&p, "type");
329 ff_amf_write_string(&p, "nonprivate");
330 }
9fd6b843 331 ff_amf_write_field_name(&p, "flashVer");
e64673e4 332 ff_amf_write_string(&p, rt->flashver);
05945db9
SP
333
334 if (rt->swfurl) {
335 ff_amf_write_field_name(&p, "swfUrl");
336 ff_amf_write_string(&p, rt->swfurl);
337 }
338
9fd6b843 339 ff_amf_write_field_name(&p, "tcUrl");
08225d01 340 ff_amf_write_string2(&p, rt->tcurl, rt->auth_params);
6bf22e18 341 if (rt->is_input) {
c7240611
KS
342 ff_amf_write_field_name(&p, "fpad");
343 ff_amf_write_bool(&p, 0);
344 ff_amf_write_field_name(&p, "capabilities");
345 ff_amf_write_number(&p, 15.0);
faba4a9b
SP
346
347 /* Tell the server we support all the audio codecs except
348 * SUPPORT_SND_INTEL (0x0008) and SUPPORT_SND_UNUSED (0x0010)
349 * which are unused in the RTMP protocol implementation. */
c7240611 350 ff_amf_write_field_name(&p, "audioCodecs");
faba4a9b 351 ff_amf_write_number(&p, 4071.0);
c7240611
KS
352 ff_amf_write_field_name(&p, "videoCodecs");
353 ff_amf_write_number(&p, 252.0);
354 ff_amf_write_field_name(&p, "videoFunction");
355 ff_amf_write_number(&p, 1.0);
758377a2
SP
356
357 if (rt->pageurl) {
358 ff_amf_write_field_name(&p, "pageUrl");
359 ff_amf_write_string(&p, rt->pageurl);
360 }
6bf22e18 361 }
9fd6b843
KS
362 ff_amf_write_object_end(&p);
363
8ee3e187 364 if (rt->conn) {
05338686 365 char *param = rt->conn;
8ee3e187
SP
366
367 // Write arbitrary AMF data to the Connect message.
8ee3e187 368 while (param != NULL) {
05338686
MS
369 char *sep;
370 param += strspn(param, " ");
371 if (!*param)
372 break;
373 sep = strchr(param, ' ');
374 if (sep)
375 *sep = '\0';
8ee3e187
SP
376 if ((ret = rtmp_write_amf_data(s, param, &p)) < 0) {
377 // Invalid AMF parameter.
378 ff_rtmp_packet_destroy(&pkt);
379 return ret;
380 }
381
05338686
MS
382 if (sep)
383 param = sep + 1;
384 else
385 break;
8ee3e187
SP
386 }
387 }
388
9fd6b843
KS
389 pkt.data_size = p - pkt.data;
390
f89584ca 391 return rtmp_send_packet(rt, &pkt, 1);
9fd6b843
KS
392}
393
e5f2731c
JO
394static int read_connect(URLContext *s, RTMPContext *rt)
395{
396 RTMPPacket pkt = { 0 };
397 uint8_t *p;
398 const uint8_t *cp;
399 int ret;
400 char command[64];
401 int stringlen;
402 double seqnum;
403 uint8_t tmpstr[256];
404 GetByteContext gbc;
405
406 if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size,
407 rt->prev_pkt[1])) < 0)
408 return ret;
409 cp = pkt.data;
410 bytestream2_init(&gbc, cp, pkt.data_size);
411 if (ff_amf_read_string(&gbc, command, sizeof(command), &stringlen)) {
412 av_log(s, AV_LOG_ERROR, "Unable to read command string\n");
413 ff_rtmp_packet_destroy(&pkt);
414 return AVERROR_INVALIDDATA;
415 }
416 if (strcmp(command, "connect")) {
417 av_log(s, AV_LOG_ERROR, "Expecting connect, got %s\n", command);
418 ff_rtmp_packet_destroy(&pkt);
419 return AVERROR_INVALIDDATA;
420 }
421 ret = ff_amf_read_number(&gbc, &seqnum);
422 if (ret)
423 av_log(s, AV_LOG_WARNING, "SeqNum not found\n");
424 /* Here one could parse an AMF Object with data as flashVers and others. */
425 ret = ff_amf_get_field_value(gbc.buffer,
426 gbc.buffer + bytestream2_get_bytes_left(&gbc),
427 "app", tmpstr, sizeof(tmpstr));
428 if (ret)
429 av_log(s, AV_LOG_WARNING, "App field not found in connect\n");
430 if (!ret && strcmp(tmpstr, rt->app))
431 av_log(s, AV_LOG_WARNING, "App field don't match up: %s <-> %s\n",
432 tmpstr, rt->app);
433 ff_rtmp_packet_destroy(&pkt);
434
435 // Send Window Acknowledgement Size (as defined in speficication)
436 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
437 RTMP_PT_SERVER_BW, 0, 4)) < 0)
438 return ret;
439 p = pkt.data;
440 bytestream_put_be32(&p, rt->server_bw);
441 pkt.data_size = p - pkt.data;
442 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
443 rt->prev_pkt[1]);
444 ff_rtmp_packet_destroy(&pkt);
445 if (ret < 0)
446 return ret;
447 // Send Peer Bandwidth
448 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
449 RTMP_PT_CLIENT_BW, 0, 5)) < 0)
450 return ret;
451 p = pkt.data;
452 bytestream_put_be32(&p, rt->server_bw);
453 bytestream_put_byte(&p, 2); // dynamic
454 pkt.data_size = p - pkt.data;
455 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
456 rt->prev_pkt[1]);
457 ff_rtmp_packet_destroy(&pkt);
458 if (ret < 0)
459 return ret;
460
461 // Ping request
462 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
463 RTMP_PT_PING, 0, 6)) < 0)
464 return ret;
465
466 p = pkt.data;
467 bytestream_put_be16(&p, 0); // 0 -> Stream Begin
468 bytestream_put_be32(&p, 0);
469 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
470 rt->prev_pkt[1]);
471 ff_rtmp_packet_destroy(&pkt);
472 if (ret < 0)
473 return ret;
474
475 // Chunk size
476 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL,
477 RTMP_PT_CHUNK_SIZE, 0, 4)) < 0)
478 return ret;
479
480 p = pkt.data;
481 bytestream_put_be32(&p, rt->out_chunk_size);
482 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
483 rt->prev_pkt[1]);
484 ff_rtmp_packet_destroy(&pkt);
485 if (ret < 0)
486 return ret;
487
488 // Send result_ NetConnection.Connect.Success to connect
489 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL,
490 RTMP_PT_INVOKE, 0,
491 RTMP_PKTDATA_DEFAULT_SIZE)) < 0)
492 return ret;
493
494 p = pkt.data;
495 ff_amf_write_string(&p, "_result");
496 ff_amf_write_number(&p, seqnum);
497
498 ff_amf_write_object_start(&p);
499 ff_amf_write_field_name(&p, "fmsVer");
500 ff_amf_write_string(&p, "FMS/3,0,1,123");
501 ff_amf_write_field_name(&p, "capabilities");
502 ff_amf_write_number(&p, 31);
503 ff_amf_write_object_end(&p);
504
505 ff_amf_write_object_start(&p);
506 ff_amf_write_field_name(&p, "level");
507 ff_amf_write_string(&p, "status");
508 ff_amf_write_field_name(&p, "code");
509 ff_amf_write_string(&p, "NetConnection.Connect.Success");
510 ff_amf_write_field_name(&p, "description");
511 ff_amf_write_string(&p, "Connection succeeded.");
512 ff_amf_write_field_name(&p, "objectEncoding");
513 ff_amf_write_number(&p, 0);
514 ff_amf_write_object_end(&p);
515
516 pkt.data_size = p - pkt.data;
517 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
518 rt->prev_pkt[1]);
519 ff_rtmp_packet_destroy(&pkt);
520 if (ret < 0)
521 return ret;
522
523 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL,
524 RTMP_PT_INVOKE, 0, 30)) < 0)
525 return ret;
526 p = pkt.data;
527 ff_amf_write_string(&p, "onBWDone");
528 ff_amf_write_number(&p, 0);
529 ff_amf_write_null(&p);
530 ff_amf_write_number(&p, 8192);
531 pkt.data_size = p - pkt.data;
532 ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
533 rt->prev_pkt[1]);
534 ff_rtmp_packet_destroy(&pkt);
535
536 return ret;
537}
538
9fd6b843 539/**
49bd8e4b 540 * Generate 'releaseStream' call and send it to the server. It should make
6bf22e18
S
541 * the server release some channel for media streams.
542 */
f645f1d6 543static int gen_release_stream(URLContext *s, RTMPContext *rt)
6bf22e18
S
544{
545 RTMPPacket pkt;
546 uint8_t *p;
f645f1d6 547 int ret;
6bf22e18 548
f645f1d6
SP
549 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
550 0, 29 + strlen(rt->playpath))) < 0)
551 return ret;
6bf22e18 552
7f804085 553 av_log(s, AV_LOG_DEBUG, "Releasing stream...\n");
6bf22e18
S
554 p = pkt.data;
555 ff_amf_write_string(&p, "releaseStream");
704af3e2 556 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18
S
557 ff_amf_write_null(&p);
558 ff_amf_write_string(&p, rt->playpath);
559
7011a42b 560 return rtmp_send_packet(rt, &pkt, 1);
6bf22e18
S
561}
562
563/**
49bd8e4b 564 * Generate 'FCPublish' call and send it to the server. It should make
6bf22e18
S
565 * the server preapare for receiving media streams.
566 */
f645f1d6 567static int gen_fcpublish_stream(URLContext *s, RTMPContext *rt)
6bf22e18
S
568{
569 RTMPPacket pkt;
570 uint8_t *p;
f645f1d6 571 int ret;
6bf22e18 572
f645f1d6
SP
573 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
574 0, 25 + strlen(rt->playpath))) < 0)
575 return ret;
6bf22e18 576
7f804085 577 av_log(s, AV_LOG_DEBUG, "FCPublish stream...\n");
6bf22e18
S
578 p = pkt.data;
579 ff_amf_write_string(&p, "FCPublish");
704af3e2 580 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18
S
581 ff_amf_write_null(&p);
582 ff_amf_write_string(&p, rt->playpath);
583
7011a42b 584 return rtmp_send_packet(rt, &pkt, 1);
6bf22e18
S
585}
586
587/**
49bd8e4b 588 * Generate 'FCUnpublish' call and send it to the server. It should make
6bf22e18
S
589 * the server destroy stream.
590 */
f645f1d6 591static int gen_fcunpublish_stream(URLContext *s, RTMPContext *rt)
6bf22e18
S
592{
593 RTMPPacket pkt;
594 uint8_t *p;
f645f1d6 595 int ret;
6bf22e18 596
f645f1d6
SP
597 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
598 0, 27 + strlen(rt->playpath))) < 0)
599 return ret;
6bf22e18 600
7f804085 601 av_log(s, AV_LOG_DEBUG, "UnPublishing stream...\n");
6bf22e18
S
602 p = pkt.data;
603 ff_amf_write_string(&p, "FCUnpublish");
704af3e2 604 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18
S
605 ff_amf_write_null(&p);
606 ff_amf_write_string(&p, rt->playpath);
607
f89584ca 608 return rtmp_send_packet(rt, &pkt, 0);
6bf22e18
S
609}
610
611/**
49bd8e4b 612 * Generate 'createStream' call and send it to the server. It should make
9fd6b843
KS
613 * the server allocate some channel for media streams.
614 */
f645f1d6 615static int gen_create_stream(URLContext *s, RTMPContext *rt)
9fd6b843
KS
616{
617 RTMPPacket pkt;
618 uint8_t *p;
f645f1d6 619 int ret;
9fd6b843 620
7f804085 621 av_log(s, AV_LOG_DEBUG, "Creating stream...\n");
f645f1d6
SP
622
623 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
624 0, 25)) < 0)
625 return ret;
9fd6b843
KS
626
627 p = pkt.data;
628 ff_amf_write_string(&p, "createStream");
704af3e2 629 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18
S
630 ff_amf_write_null(&p);
631
f89584ca 632 return rtmp_send_packet(rt, &pkt, 1);
6bf22e18
S
633}
634
635
636/**
49bd8e4b 637 * Generate 'deleteStream' call and send it to the server. It should make
6bf22e18
S
638 * the server remove some channel for media streams.
639 */
f645f1d6 640static int gen_delete_stream(URLContext *s, RTMPContext *rt)
6bf22e18
S
641{
642 RTMPPacket pkt;
643 uint8_t *p;
f645f1d6 644 int ret;
6bf22e18 645
7f804085 646 av_log(s, AV_LOG_DEBUG, "Deleting stream...\n");
f645f1d6
SP
647
648 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
649 0, 34)) < 0)
650 return ret;
6bf22e18
S
651
652 p = pkt.data;
653 ff_amf_write_string(&p, "deleteStream");
1eef08f9 654 ff_amf_write_number(&p, ++rt->nb_invokes);
9fd6b843 655 ff_amf_write_null(&p);
6bf22e18 656 ff_amf_write_number(&p, rt->main_channel_id);
9fd6b843 657
f89584ca 658 return rtmp_send_packet(rt, &pkt, 0);
9fd6b843
KS
659}
660
661/**
9477c035
SP
662 * Generate client buffer time and send it to the server.
663 */
664static int gen_buffer_time(URLContext *s, RTMPContext *rt)
665{
666 RTMPPacket pkt;
667 uint8_t *p;
668 int ret;
669
670 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING,
671 1, 10)) < 0)
672 return ret;
673
674 p = pkt.data;
675 bytestream_put_be16(&p, 3);
676 bytestream_put_be32(&p, rt->main_channel_id);
677 bytestream_put_be32(&p, rt->client_buffer_time);
678
f89584ca 679 return rtmp_send_packet(rt, &pkt, 0);
9477c035
SP
680}
681
682/**
49bd8e4b 683 * Generate 'play' call and send it to the server, then ping the server
9fd6b843
KS
684 * to start actual playing.
685 */
f645f1d6 686static int gen_play(URLContext *s, RTMPContext *rt)
9fd6b843
KS
687{
688 RTMPPacket pkt;
689 uint8_t *p;
f645f1d6 690 int ret;
9fd6b843 691
7f804085 692 av_log(s, AV_LOG_DEBUG, "Sending play command for '%s'\n", rt->playpath);
f645f1d6
SP
693
694 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_VIDEO_CHANNEL, RTMP_PT_INVOKE,
695 0, 29 + strlen(rt->playpath))) < 0)
696 return ret;
697
9fd6b843
KS
698 pkt.extra = rt->main_channel_id;
699
700 p = pkt.data;
701 ff_amf_write_string(&p, "play");
1eef08f9 702 ff_amf_write_number(&p, ++rt->nb_invokes);
9fd6b843
KS
703 ff_amf_write_null(&p);
704 ff_amf_write_string(&p, rt->playpath);
b2e495af 705 ff_amf_write_number(&p, rt->live);
9fd6b843 706
f89584ca 707 return rtmp_send_packet(rt, &pkt, 1);
9fd6b843
KS
708}
709
710/**
49bd8e4b 711 * Generate 'publish' call and send it to the server.
6bf22e18 712 */
f645f1d6 713static int gen_publish(URLContext *s, RTMPContext *rt)
6bf22e18
S
714{
715 RTMPPacket pkt;
716 uint8_t *p;
f645f1d6 717 int ret;
6bf22e18 718
7f804085 719 av_log(s, AV_LOG_DEBUG, "Sending publish command for '%s'\n", rt->playpath);
f645f1d6
SP
720
721 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SOURCE_CHANNEL, RTMP_PT_INVOKE,
722 0, 30 + strlen(rt->playpath))) < 0)
723 return ret;
724
6bf22e18
S
725 pkt.extra = rt->main_channel_id;
726
727 p = pkt.data;
728 ff_amf_write_string(&p, "publish");
1eef08f9 729 ff_amf_write_number(&p, ++rt->nb_invokes);
6bf22e18
S
730 ff_amf_write_null(&p);
731 ff_amf_write_string(&p, rt->playpath);
732 ff_amf_write_string(&p, "live");
733
f89584ca 734 return rtmp_send_packet(rt, &pkt, 1);
6bf22e18
S
735}
736
737/**
49bd8e4b 738 * Generate ping reply and send it to the server.
9fd6b843 739 */
f645f1d6 740static int gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt)
9fd6b843
KS
741{
742 RTMPPacket pkt;
743 uint8_t *p;
f645f1d6
SP
744 int ret;
745
8ea1459b
SP
746 if (ppkt->data_size < 6) {
747 av_log(s, AV_LOG_ERROR, "Too short ping packet (%d)\n",
748 ppkt->data_size);
749 return AVERROR_INVALIDDATA;
750 }
751
f645f1d6
SP
752 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING,
753 ppkt->timestamp + 1, 6)) < 0)
754 return ret;
9fd6b843 755
9fd6b843
KS
756 p = pkt.data;
757 bytestream_put_be16(&p, 7);
4aaebf78 758 bytestream_put_be32(&p, AV_RB32(ppkt->data+2));
f645f1d6 759
f89584ca 760 return rtmp_send_packet(rt, &pkt, 0);
9fd6b843
KS
761}
762
bf7c1719 763/**
635ac8e1
SP
764 * Generate SWF verification message and send it to the server.
765 */
766static int gen_swf_verification(URLContext *s, RTMPContext *rt)
767{
768 RTMPPacket pkt;
769 uint8_t *p;
770 int ret;
771
772 av_log(s, AV_LOG_DEBUG, "Sending SWF verification...\n");
773 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_PING,
774 0, 44)) < 0)
775 return ret;
776
777 p = pkt.data;
778 bytestream_put_be16(&p, 27);
779 memcpy(p, rt->swfverification, 42);
780
781 return rtmp_send_packet(rt, &pkt, 0);
782}
783
784/**
34d908c0
RS
785 * Generate server bandwidth message and send it to the server.
786 */
f645f1d6 787static int gen_server_bw(URLContext *s, RTMPContext *rt)
34d908c0
RS
788{
789 RTMPPacket pkt;
790 uint8_t *p;
f645f1d6
SP
791 int ret;
792
793 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_SERVER_BW,
794 0, 4)) < 0)
795 return ret;
34d908c0 796
34d908c0 797 p = pkt.data;
c2d38bea 798 bytestream_put_be32(&p, rt->server_bw);
f645f1d6 799
f89584ca 800 return rtmp_send_packet(rt, &pkt, 0);
34d908c0
RS
801}
802
803/**
d55961fa
SP
804 * Generate check bandwidth message and send it to the server.
805 */
f645f1d6 806static int gen_check_bw(URLContext *s, RTMPContext *rt)
d55961fa
SP
807{
808 RTMPPacket pkt;
809 uint8_t *p;
f645f1d6 810 int ret;
d55961fa 811
f645f1d6
SP
812 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
813 0, 21)) < 0)
814 return ret;
d55961fa
SP
815
816 p = pkt.data;
817 ff_amf_write_string(&p, "_checkbw");
8b6a5a79 818 ff_amf_write_number(&p, ++rt->nb_invokes);
d55961fa
SP
819 ff_amf_write_null(&p);
820
fb7e7808 821 return rtmp_send_packet(rt, &pkt, 1);
d55961fa
SP
822}
823
824/**
49bd8e4b 825 * Generate report on bytes read so far and send it to the server.
bf7c1719 826 */
f645f1d6 827static int gen_bytes_read(URLContext *s, RTMPContext *rt, uint32_t ts)
bf7c1719
KS
828{
829 RTMPPacket pkt;
830 uint8_t *p;
f645f1d6
SP
831 int ret;
832
833 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_BYTES_READ,
834 ts, 4)) < 0)
835 return ret;
bf7c1719 836
bf7c1719
KS
837 p = pkt.data;
838 bytestream_put_be32(&p, rt->bytes_read);
f645f1d6 839
f89584ca 840 return rtmp_send_packet(rt, &pkt, 0);
bf7c1719
KS
841}
842
00cb52c6
SP
843static int gen_fcsubscribe_stream(URLContext *s, RTMPContext *rt,
844 const char *subscribe)
f9e77c17
SP
845{
846 RTMPPacket pkt;
847 uint8_t *p;
848 int ret;
849
850 if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
00cb52c6 851 0, 27 + strlen(subscribe))) < 0)
f9e77c17
SP
852 return ret;
853
854 p = pkt.data;
855 ff_amf_write_string(&p, "FCSubscribe");
856 ff_amf_write_number(&p, ++rt->nb_invokes);
857 ff_amf_write_null(&p);
00cb52c6 858 ff_amf_write_string(&p, subscribe);
f9e77c17 859
f89584ca 860 return rtmp_send_packet(rt, &pkt, 1);
f9e77c17
SP
861}
862
3505d557
SP
863int ff_rtmp_calc_digest(const uint8_t *src, int len, int gap,
864 const uint8_t *key, int keylen, uint8_t *dst)
9fd6b843
KS
865{
866 struct AVSHA *sha;
867 uint8_t hmac_buf[64+32] = {0};
868 int i;
869
e002e329 870 sha = av_sha_alloc();
08e93f5b
SP
871 if (!sha)
872 return AVERROR(ENOMEM);
9fd6b843
KS
873
874 if (keylen < 64) {
875 memcpy(hmac_buf, key, keylen);
876 } else {
877 av_sha_init(sha, 256);
878 av_sha_update(sha,key, keylen);
879 av_sha_final(sha, hmac_buf);
880 }
881 for (i = 0; i < 64; i++)
882 hmac_buf[i] ^= HMAC_IPAD_VAL;
883
884 av_sha_init(sha, 256);
885 av_sha_update(sha, hmac_buf, 64);
886 if (gap <= 0) {
887 av_sha_update(sha, src, len);
888 } else { //skip 32 bytes used for storing digest
889 av_sha_update(sha, src, gap);
890 av_sha_update(sha, src + gap + 32, len - gap - 32);
891 }
892 av_sha_final(sha, hmac_buf + 64);
893
894 for (i = 0; i < 64; i++)
895 hmac_buf[i] ^= HMAC_IPAD_VAL ^ HMAC_OPAD_VAL; //reuse XORed key for opad
896 av_sha_init(sha, 256);
897 av_sha_update(sha, hmac_buf, 64+32);
898 av_sha_final(sha, dst);
899
900 av_free(sha);
08e93f5b
SP
901
902 return 0;
9fd6b843
KS
903}
904
0e31088b
SP
905int ff_rtmp_calc_digest_pos(const uint8_t *buf, int off, int mod_val,
906 int add_val)
907{
908 int i, digest_pos = 0;
909
910 for (i = 0; i < 4; i++)
911 digest_pos += buf[i + off];
912 digest_pos = digest_pos % mod_val + add_val;
913
914 return digest_pos;
915}
916
9fd6b843 917/**
49bd8e4b 918 * Put HMAC-SHA2 digest of packet data (except for the bytes where this digest
9fd6b843
KS
919 * will be stored) into that packet.
920 *
921 * @param buf handshake data (1536 bytes)
acd554c1 922 * @param encrypted use an encrypted connection (RTMPE)
9fd6b843
KS
923 * @return offset to the digest inside input data
924 */
acd554c1 925static int rtmp_handshake_imprint_with_digest(uint8_t *buf, int encrypted)
9fd6b843 926{
0e31088b 927 int ret, digest_pos;
9fd6b843 928
acd554c1
SP
929 if (encrypted)
930 digest_pos = ff_rtmp_calc_digest_pos(buf, 772, 728, 776);
931 else
932 digest_pos = ff_rtmp_calc_digest_pos(buf, 8, 728, 12);
9fd6b843 933
3505d557
SP
934 ret = ff_rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
935 rtmp_player_key, PLAYER_KEY_OPEN_PART_LEN,
936 buf + digest_pos);
08e93f5b
SP
937 if (ret < 0)
938 return ret;
939
9fd6b843
KS
940 return digest_pos;
941}
942
943/**
49bd8e4b 944 * Verify that the received server response has the expected digest value.
9fd6b843
KS
945 *
946 * @param buf handshake data received from the server (1536 bytes)
947 * @param off position to search digest offset from
948 * @return 0 if digest is valid, digest position otherwise
949 */
950static int rtmp_validate_digest(uint8_t *buf, int off)
951{
9fd6b843 952 uint8_t digest[32];
0e31088b 953 int ret, digest_pos;
9fd6b843 954
0e31088b 955 digest_pos = ff_rtmp_calc_digest_pos(buf, off, 728, off + 4);
9fd6b843 956
3505d557
SP
957 ret = ff_rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
958 rtmp_server_key, SERVER_KEY_OPEN_PART_LEN,
959 digest);
08e93f5b
SP
960 if (ret < 0)
961 return ret;
962
9fd6b843
KS
963 if (!memcmp(digest, buf + digest_pos, 32))
964 return digest_pos;
965 return 0;
966}
967
635ac8e1
SP
968static int rtmp_calc_swf_verification(URLContext *s, RTMPContext *rt,
969 uint8_t *buf)
970{
971 uint8_t *p;
972 int ret;
973
974 if (rt->swfhash_len != 32) {
975 av_log(s, AV_LOG_ERROR,
976 "Hash of the decompressed SWF file is not 32 bytes long.\n");
977 return AVERROR(EINVAL);
978 }
979
980 p = &rt->swfverification[0];
981 bytestream_put_byte(&p, 1);
982 bytestream_put_byte(&p, 1);
983 bytestream_put_be32(&p, rt->swfsize);
984 bytestream_put_be32(&p, rt->swfsize);
985
986 if ((ret = ff_rtmp_calc_digest(rt->swfhash, 32, 0, buf, 32, p)) < 0)
987 return ret;
988
989 return 0;
990}
991
93f257db
SP
992#if CONFIG_ZLIB
993static int rtmp_uncompress_swfplayer(uint8_t *in_data, int64_t in_size,
994 uint8_t **out_data, int64_t *out_size)
995{
996 z_stream zs = { 0 };
997 void *ptr;
998 int size;
999 int ret = 0;
1000
1001 zs.avail_in = in_size;
1002 zs.next_in = in_data;
1003 ret = inflateInit(&zs);
1004 if (ret != Z_OK)
1005 return AVERROR_UNKNOWN;
1006
1007 do {
1008 uint8_t tmp_buf[16384];
1009
1010 zs.avail_out = sizeof(tmp_buf);
1011 zs.next_out = tmp_buf;
1012
1013 ret = inflate(&zs, Z_NO_FLUSH);
1014 if (ret != Z_OK && ret != Z_STREAM_END) {
1015 ret = AVERROR_UNKNOWN;
1016 goto fail;
1017 }
1018
1019 size = sizeof(tmp_buf) - zs.avail_out;
1020 if (!(ptr = av_realloc(*out_data, *out_size + size))) {
1021 ret = AVERROR(ENOMEM);
1022 goto fail;
1023 }
1024 *out_data = ptr;
1025
1026 memcpy(*out_data + *out_size, tmp_buf, size);
1027 *out_size += size;
1028 } while (zs.avail_out == 0);
1029
1030fail:
1031 inflateEnd(&zs);
1032 return ret;
1033}
1034#endif
1035
1036static int rtmp_calc_swfhash(URLContext *s)
1037{
1038 RTMPContext *rt = s->priv_data;
1039 uint8_t *in_data = NULL, *out_data = NULL, *swfdata;
1040 int64_t in_size, out_size;
1041 URLContext *stream;
1042 char swfhash[32];
1043 int swfsize;
1044 int ret = 0;
1045
1046 /* Get the SWF player file. */
1047 if ((ret = ffurl_open(&stream, rt->swfverify, AVIO_FLAG_READ,
1048 &s->interrupt_callback, NULL)) < 0) {
1049 av_log(s, AV_LOG_ERROR, "Cannot open connection %s.\n", rt->swfverify);
1050 goto fail;
1051 }
1052
1053 if ((in_size = ffurl_seek(stream, 0, AVSEEK_SIZE)) < 0) {
1054 ret = AVERROR(EIO);
1055 goto fail;
1056 }
1057
1058 if (!(in_data = av_malloc(in_size))) {
1059 ret = AVERROR(ENOMEM);
1060 goto fail;
1061 }
1062
1063 if ((ret = ffurl_read_complete(stream, in_data, in_size)) < 0)
1064 goto fail;
1065
1066 if (in_size < 3) {
1067 ret = AVERROR_INVALIDDATA;
1068 goto fail;
1069 }
1070
1071 if (!memcmp(in_data, "CWS", 3)) {
1072 /* Decompress the SWF player file using Zlib. */
1073 if (!(out_data = av_malloc(8))) {
1074 ret = AVERROR(ENOMEM);
1075 goto fail;
1076 }
1077 *in_data = 'F'; // magic stuff
1078 memcpy(out_data, in_data, 8);
1079 out_size = 8;
1080
1081#if CONFIG_ZLIB
1082 if ((ret = rtmp_uncompress_swfplayer(in_data + 8, in_size - 8,
1083 &out_data, &out_size)) < 0)
1084 goto fail;
1085#else
1086 av_log(s, AV_LOG_ERROR,
1087 "Zlib is required for decompressing the SWF player file.\n");
1088 ret = AVERROR(EINVAL);
1089 goto fail;
1090#endif
1091 swfsize = out_size;
1092 swfdata = out_data;
1093 } else {
1094 swfsize = in_size;
1095 swfdata = in_data;
1096 }
1097
1098 /* Compute the SHA256 hash of the SWF player file. */
1099 if ((ret = ff_rtmp_calc_digest(swfdata, swfsize, 0,
1100 "Genuine Adobe Flash Player 001", 30,
1101 swfhash)) < 0)
1102 goto fail;
1103
1104 /* Set SWFVerification parameters. */
1105 av_opt_set_bin(rt, "rtmp_swfhash", swfhash, 32, 0);
1106 rt->swfsize = swfsize;
1107
1108fail:
1109 av_freep(&in_data);
1110 av_freep(&out_data);
1111 ffurl_close(stream);
1112 return ret;
1113}
1114
9fd6b843 1115/**
49bd8e4b 1116 * Perform handshake with the server by means of exchanging pseudorandom data
9fd6b843
KS
1117 * signed with HMAC-SHA2 digest.
1118 *
1119 * @return 0 if handshake succeeds, negative value otherwise
1120 */
1121static int rtmp_handshake(URLContext *s, RTMPContext *rt)
1122{
1123 AVLFG rnd;
1124 uint8_t tosend [RTMP_HANDSHAKE_PACKET_SIZE+1] = {
1125 3, // unencrypted data
1126 0, 0, 0, 0, // client uptime
1127 RTMP_CLIENT_VER1,
1128 RTMP_CLIENT_VER2,
1129 RTMP_CLIENT_VER3,
1130 RTMP_CLIENT_VER4,
1131 };
1132 uint8_t clientdata[RTMP_HANDSHAKE_PACKET_SIZE];
1133 uint8_t serverdata[RTMP_HANDSHAKE_PACKET_SIZE+1];
1134 int i;
1135 int server_pos, client_pos;
acd554c1 1136 uint8_t digest[32], signature[32];
acd554c1 1137 int ret, type = 0;
9fd6b843 1138
7f804085 1139 av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
9fd6b843
KS
1140
1141 av_lfg_init(&rnd, 0xDEADC0DE);
1142 // generate handshake packet - 1536 bytes of pseudorandom data
1143 for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
1144 tosend[i] = av_lfg_get(&rnd) >> 24;
acd554c1 1145
f7bfb126 1146 if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
acd554c1
SP
1147 /* When the client wants to use RTMPE, we have to change the command
1148 * byte to 0x06 which means to use encrypted data and we have to set
1149 * the flash version to at least 9.0.115.0. */
1150 tosend[0] = 6;
1151 tosend[5] = 128;
1152 tosend[6] = 0;
1153 tosend[7] = 3;
1154 tosend[8] = 2;
1155
1156 /* Initialize the Diffie-Hellmann context and generate the public key
1157 * to send to the server. */
1158 if ((ret = ff_rtmpe_gen_pub_key(rt->stream, tosend + 1)) < 0)
1159 return ret;
1160 }
1161
f7bfb126 1162 client_pos = rtmp_handshake_imprint_with_digest(tosend + 1, rt->encrypted);
08e93f5b
SP
1163 if (client_pos < 0)
1164 return client_pos;
9fd6b843 1165
bba287fd
SP
1166 if ((ret = ffurl_write(rt->stream, tosend,
1167 RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
1168 av_log(s, AV_LOG_ERROR, "Cannot write RTMP handshake request\n");
1169 return ret;
1170 }
1171
177bcc95
SP
1172 if ((ret = ffurl_read_complete(rt->stream, serverdata,
1173 RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
7f804085 1174 av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
177bcc95 1175 return ret;
9fd6b843 1176 }
177bcc95
SP
1177
1178 if ((ret = ffurl_read_complete(rt->stream, clientdata,
1179 RTMP_HANDSHAKE_PACKET_SIZE)) < 0) {
7f804085 1180 av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
177bcc95 1181 return ret;
9fd6b843
KS
1182 }
1183
acd554c1 1184 av_log(s, AV_LOG_DEBUG, "Type answer %d\n", serverdata[0]);
7f804085 1185 av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
9fd6b843
KS
1186 serverdata[5], serverdata[6], serverdata[7], serverdata[8]);
1187
e2ee11e8 1188 if (rt->is_input && serverdata[5] >= 3) {
c7240611 1189 server_pos = rtmp_validate_digest(serverdata + 1, 772);
08e93f5b
SP
1190 if (server_pos < 0)
1191 return server_pos;
1192
9fd6b843 1193 if (!server_pos) {
acd554c1 1194 type = 1;
c7240611 1195 server_pos = rtmp_validate_digest(serverdata + 1, 8);
08e93f5b
SP
1196 if (server_pos < 0)
1197 return server_pos;
1198
c7240611 1199 if (!server_pos) {
7f804085 1200 av_log(s, AV_LOG_ERROR, "Server response validating failed\n");
a4d3f358 1201 return AVERROR(EIO);
c7240611 1202 }
9fd6b843 1203 }
9fd6b843 1204
635ac8e1
SP
1205 /* Generate SWFVerification token (SHA256 HMAC hash of decompressed SWF,
1206 * key are the last 32 bytes of the server handshake. */
1207 if (rt->swfsize) {
1208 if ((ret = rtmp_calc_swf_verification(s, rt, serverdata + 1 +
1209 RTMP_HANDSHAKE_PACKET_SIZE - 32)) < 0)
1210 return ret;
1211 }
1212
3505d557
SP
1213 ret = ff_rtmp_calc_digest(tosend + 1 + client_pos, 32, 0,
1214 rtmp_server_key, sizeof(rtmp_server_key),
1215 digest);
08e93f5b
SP
1216 if (ret < 0)
1217 return ret;
1218
3505d557 1219 ret = ff_rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE - 32,
acd554c1 1220 0, digest, 32, signature);
08e93f5b
SP
1221 if (ret < 0)
1222 return ret;
1223
f7bfb126 1224 if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
acd554c1
SP
1225 /* Compute the shared secret key sent by the server and initialize
1226 * the RC4 encryption. */
1227 if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
1228 tosend + 1, type)) < 0)
1229 return ret;
1230
1231 /* Encrypt the signature received by the server. */
1232 ff_rtmpe_encrypt_sig(rt->stream, signature, digest, serverdata[0]);
1233 }
1234
1235 if (memcmp(signature, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) {
7f804085 1236 av_log(s, AV_LOG_ERROR, "Signature mismatch\n");
a4d3f358 1237 return AVERROR(EIO);
c7240611 1238 }
9fd6b843 1239
c7240611
KS
1240 for (i = 0; i < RTMP_HANDSHAKE_PACKET_SIZE; i++)
1241 tosend[i] = av_lfg_get(&rnd) >> 24;
3505d557
SP
1242 ret = ff_rtmp_calc_digest(serverdata + 1 + server_pos, 32, 0,
1243 rtmp_player_key, sizeof(rtmp_player_key),
1244 digest);
08e93f5b
SP
1245 if (ret < 0)
1246 return ret;
1247
3505d557
SP
1248 ret = ff_rtmp_calc_digest(tosend, RTMP_HANDSHAKE_PACKET_SIZE - 32, 0,
1249 digest, 32,
1250 tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32);
08e93f5b
SP
1251 if (ret < 0)
1252 return ret;
c7240611 1253
f7bfb126 1254 if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
acd554c1
SP
1255 /* Encrypt the signature to be send to the server. */
1256 ff_rtmpe_encrypt_sig(rt->stream, tosend +
1257 RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
1258 serverdata[0]);
1259 }
1260
c7240611 1261 // write reply back to the server
bba287fd
SP
1262 if ((ret = ffurl_write(rt->stream, tosend,
1263 RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
1264 return ret;
acd554c1 1265
f7bfb126 1266 if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
acd554c1
SP
1267 /* Set RC4 keys for encryption and update the keystreams. */
1268 if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
1269 return ret;
1270 }
6bf22e18 1271 } else {
f7bfb126 1272 if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
acd554c1
SP
1273 /* Compute the shared secret key sent by the server and initialize
1274 * the RC4 encryption. */
1275 if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
1276 tosend + 1, 1)) < 0)
1277 return ret;
1278
1279 if (serverdata[0] == 9) {
1280 /* Encrypt the signature received by the server. */
1281 ff_rtmpe_encrypt_sig(rt->stream, signature, digest,
1282 serverdata[0]);
1283 }
1284 }
1285
bba287fd
SP
1286 if ((ret = ffurl_write(rt->stream, serverdata + 1,
1287 RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
1288 return ret;
acd554c1 1289
f7bfb126 1290 if (rt->encrypted && CONFIG_FFRTMPCRYPT_PROTOCOL) {
acd554c1
SP
1291 /* Set RC4 keys for encryption and update the keystreams. */
1292 if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
1293 return ret;
1294 }
6bf22e18
S
1295 }
1296
9fd6b843
KS
1297 return 0;
1298}
1299
e5f2731c
JO
1300static int rtmp_receive_hs_packet(RTMPContext* rt, uint32_t *first_int,
1301 uint32_t *second_int, char *arraydata,
1302 int size)
1303{
cb5ab02a 1304 int inoutsize;
e5f2731c
JO
1305
1306 inoutsize = ffurl_read_complete(rt->stream, arraydata,
1307 RTMP_HANDSHAKE_PACKET_SIZE);
1308 if (inoutsize <= 0)
1309 return AVERROR(EIO);
1310 if (inoutsize != RTMP_HANDSHAKE_PACKET_SIZE) {
1311 av_log(rt, AV_LOG_ERROR, "Erroneous Message size %d"
1312 " not following standard\n", (int)inoutsize);
1313 return AVERROR(EINVAL);
1314 }
1315
1316 *first_int = AV_RB32(arraydata);
1317 *second_int = AV_RB32(arraydata + 4);
1318 return 0;
1319}
1320
1321static int rtmp_send_hs_packet(RTMPContext* rt, uint32_t first_int,
1322 uint32_t second_int, char *arraydata, int size)
1323{
cb5ab02a 1324 int inoutsize;
e5f2731c
JO
1325
1326 AV_WB32(arraydata, first_int);
1327 AV_WB32(arraydata + 4, first_int);
1328 inoutsize = ffurl_write(rt->stream, arraydata,
1329 RTMP_HANDSHAKE_PACKET_SIZE);
1330 if (inoutsize != RTMP_HANDSHAKE_PACKET_SIZE) {
1331 av_log(rt, AV_LOG_ERROR, "Unable to write answer\n");
1332 return AVERROR(EIO);
1333 }
1334
1335 return 0;
1336}
1337
1338/**
1339 * rtmp handshake server side
1340 */
1341static int rtmp_server_handshake(URLContext *s, RTMPContext *rt)
1342{
1343 uint8_t buffer[RTMP_HANDSHAKE_PACKET_SIZE];
1344 uint32_t hs_epoch;
1345 uint32_t hs_my_epoch;
1346 uint8_t hs_c1[RTMP_HANDSHAKE_PACKET_SIZE];
1347 uint8_t hs_s1[RTMP_HANDSHAKE_PACKET_SIZE];
1348 uint32_t zeroes;
1349 uint32_t temp = 0;
1350 int randomidx = 0;
cb5ab02a 1351 int inoutsize = 0;
e5f2731c
JO
1352 int ret;
1353
1354 inoutsize = ffurl_read_complete(rt->stream, buffer, 1); // Receive C0
1355 if (inoutsize <= 0) {
1356 av_log(s, AV_LOG_ERROR, "Unable to read handshake\n");
1357 return AVERROR(EIO);
1358 }
1359 // Check Version
1360 if (buffer[0] != 3) {
1361 av_log(s, AV_LOG_ERROR, "RTMP protocol version mismatch\n");
1362 return AVERROR(EIO);
1363 }
1364 if (ffurl_write(rt->stream, buffer, 1) <= 0) { // Send S0
1365 av_log(s, AV_LOG_ERROR,
1366 "Unable to write answer - RTMP S0\n");
1367 return AVERROR(EIO);
1368 }
1369 /* Receive C1 */
1370 ret = rtmp_receive_hs_packet(rt, &hs_epoch, &zeroes, hs_c1,
1371 RTMP_HANDSHAKE_PACKET_SIZE);
1372 if (ret) {
1373 av_log(s, AV_LOG_ERROR, "RTMP Handshake C1 Error\n");
1374 return ret;
1375 }
1376 if (zeroes)
1377 av_log(s, AV_LOG_WARNING, "Erroneous C1 Message zero != 0\n");
1378 /* Send S1 */
1379 /* By now same epoch will be sent */
1380 hs_my_epoch = hs_epoch;
1381 /* Generate random */
2f1b2ff9 1382 for (randomidx = 8; randomidx < (RTMP_HANDSHAKE_PACKET_SIZE);
e5f2731c 1383 randomidx += 4)
2f1b2ff9 1384 AV_WB32(hs_s1 + randomidx, av_get_random_seed());
e5f2731c
JO
1385
1386 ret = rtmp_send_hs_packet(rt, hs_my_epoch, 0, hs_s1,
1387 RTMP_HANDSHAKE_PACKET_SIZE);
1388 if (ret) {
1389 av_log(s, AV_LOG_ERROR, "RTMP Handshake S1 Error\n");
1390 return ret;
1391 }
1392 /* Send S2 */
1393 ret = rtmp_send_hs_packet(rt, hs_epoch, 0, hs_c1,
1394 RTMP_HANDSHAKE_PACKET_SIZE);
1395 if (ret) {
1396 av_log(s, AV_LOG_ERROR, "RTMP Handshake S2 Error\n");
1397 return ret;
1398 }
1399 /* Receive C2 */
1400 ret = rtmp_receive_hs_packet(rt, &temp, &zeroes, buffer,
1401 RTMP_HANDSHAKE_PACKET_SIZE);
1402 if (ret) {
1403 av_log(s, AV_LOG_ERROR, "RTMP Handshake C2 Error\n");
1404 return ret;
1405 }
1406 if (temp != hs_my_epoch)
1407 av_log(s, AV_LOG_WARNING,
1408 "Erroneous C2 Message epoch does not match up with C1 epoch\n");
1409 if (memcmp(buffer + 8, hs_s1 + 8,
1410 RTMP_HANDSHAKE_PACKET_SIZE - 8))
1411 av_log(s, AV_LOG_WARNING,
1412 "Erroneous C2 Message random does not match up\n");
1413
1414 return 0;
1415}
1416
7be2a7d8
SP
1417static int handle_chunk_size(URLContext *s, RTMPPacket *pkt)
1418{
1419 RTMPContext *rt = s->priv_data;
1420 int ret;
1421
e49e6b64 1422 if (pkt->data_size < 4) {
7be2a7d8 1423 av_log(s, AV_LOG_ERROR,
e49e6b64 1424 "Too short chunk size change packet (%d)\n",
7be2a7d8 1425 pkt->data_size);
e7ea6883 1426 return AVERROR_INVALIDDATA;
7be2a7d8
SP
1427 }
1428
1429 if (!rt->is_input) {
f5ce90f2
JO
1430 /* Send the same chunk size change packet back to the server,
1431 * setting the outgoing chunk size to the same as the incoming one. */
1432 if ((ret = ff_rtmp_packet_write(rt->stream, pkt, rt->out_chunk_size,
7be2a7d8
SP
1433 rt->prev_pkt[1])) < 0)
1434 return ret;
f5ce90f2 1435 rt->out_chunk_size = AV_RB32(pkt->data);
7be2a7d8
SP
1436 }
1437
f5ce90f2
JO
1438 rt->in_chunk_size = AV_RB32(pkt->data);
1439 if (rt->in_chunk_size <= 0) {
1440 av_log(s, AV_LOG_ERROR, "Incorrect chunk size %d\n",
1441 rt->in_chunk_size);
e7ea6883 1442 return AVERROR_INVALIDDATA;
7be2a7d8 1443 }
f5ce90f2
JO
1444 av_log(s, AV_LOG_DEBUG, "New incoming chunk size = %d\n",
1445 rt->in_chunk_size);
7be2a7d8
SP
1446
1447 return 0;
1448}
1449
0ffd5161
SP
1450static int handle_ping(URLContext *s, RTMPPacket *pkt)
1451{
1452 RTMPContext *rt = s->priv_data;
1453 int t, ret;
1454
8ea1459b
SP
1455 if (pkt->data_size < 2) {
1456 av_log(s, AV_LOG_ERROR, "Too short ping packet (%d)\n",
1457 pkt->data_size);
1458 return AVERROR_INVALIDDATA;
1459 }
1460
0ffd5161
SP
1461 t = AV_RB16(pkt->data);
1462 if (t == 6) {
1463 if ((ret = gen_pong(s, rt, pkt)) < 0)
1464 return ret;
635ac8e1
SP
1465 } else if (t == 26) {
1466 if (rt->swfsize) {
1467 if ((ret = gen_swf_verification(s, rt)) < 0)
1468 return ret;
1469 } else {
1470 av_log(s, AV_LOG_WARNING, "Ignoring SWFVerification request.\n");
1471 }
0ffd5161
SP
1472 }
1473
1474 return 0;
1475}
1476
912ecc9a
SP
1477static int handle_client_bw(URLContext *s, RTMPPacket *pkt)
1478{
1479 RTMPContext *rt = s->priv_data;
1480
1481 if (pkt->data_size < 4) {
1482 av_log(s, AV_LOG_ERROR,
1483 "Client bandwidth report packet is less than 4 bytes long (%d)\n",
1484 pkt->data_size);
088a82bb 1485 return AVERROR_INVALIDDATA;
912ecc9a 1486 }
abf77a24
SP
1487
1488 rt->client_report_size = AV_RB32(pkt->data);
1489 if (rt->client_report_size <= 0) {
1490 av_log(s, AV_LOG_ERROR, "Incorrect client bandwidth %d\n",
1491 rt->client_report_size);
1492 return AVERROR_INVALIDDATA;
1493
1494 }
1495 av_log(s, AV_LOG_DEBUG, "Client bandwidth = %d\n", rt->client_report_size);
1496 rt->client_report_size >>= 1;
912ecc9a
SP
1497
1498 return 0;
1499}
1500
9b498148
SP
1501static int handle_server_bw(URLContext *s, RTMPPacket *pkt)
1502{
1503 RTMPContext *rt = s->priv_data;
1504
2357f606
SP
1505 if (pkt->data_size < 4) {
1506 av_log(s, AV_LOG_ERROR,
1507 "Too short server bandwidth report packet (%d)\n",
1508 pkt->data_size);
1509 return AVERROR_INVALIDDATA;
1510 }
1511
9b498148
SP
1512 rt->server_bw = AV_RB32(pkt->data);
1513 if (rt->server_bw <= 0) {
1514 av_log(s, AV_LOG_ERROR, "Incorrect server bandwidth %d\n",
1515 rt->server_bw);
be8f9492 1516 return AVERROR_INVALIDDATA;
9b498148
SP
1517 }
1518 av_log(s, AV_LOG_DEBUG, "Server bandwidth = %d\n", rt->server_bw);
1519
1520 return 0;
1521}
1522
08225d01
MS
1523static int do_adobe_auth(RTMPContext *rt, const char *user, const char *salt,
1524 const char *opaque, const char *challenge)
1525{
1526 uint8_t hash[16];
1527 char hashstr[AV_BASE64_SIZE(sizeof(hash))], challenge2[10];
1528 struct AVMD5 *md5 = av_md5_alloc();
1529 if (!md5)
1530 return AVERROR(ENOMEM);
1531
1532 snprintf(challenge2, sizeof(challenge2), "%08x", av_get_random_seed());
1533
1534 av_md5_init(md5);
1535 av_md5_update(md5, user, strlen(user));
1536 av_md5_update(md5, salt, strlen(salt));
1537 av_md5_update(md5, rt->password, strlen(rt->password));
1538 av_md5_final(md5, hash);
1539 av_base64_encode(hashstr, sizeof(hashstr), hash,
1540 sizeof(hash));
1541 av_md5_init(md5);
1542 av_md5_update(md5, hashstr, strlen(hashstr));
1543 if (opaque)
1544 av_md5_update(md5, opaque, strlen(opaque));
1545 else if (challenge)
1546 av_md5_update(md5, challenge, strlen(challenge));
1547 av_md5_update(md5, challenge2, strlen(challenge2));
1548 av_md5_final(md5, hash);
1549 av_base64_encode(hashstr, sizeof(hashstr), hash,
1550 sizeof(hash));
1551 snprintf(rt->auth_params, sizeof(rt->auth_params),
1552 "?authmod=%s&user=%s&challenge=%s&response=%s",
1553 "adobe", user, challenge2, hashstr);
1554 if (opaque)
1555 av_strlcatf(rt->auth_params, sizeof(rt->auth_params),
1556 "&opaque=%s", opaque);
1557
1558 av_free(md5);
1559 return 0;
1560}
1561
c1ea44c5
MS
1562static int do_llnw_auth(RTMPContext *rt, const char *user, const char *nonce)
1563{
1564 uint8_t hash[16];
1565 char hashstr1[33], hashstr2[33];
1566 const char *realm = "live";
1567 const char *method = "publish";
1568 const char *qop = "auth";
1569 const char *nc = "00000001";
1570 char cnonce[10];
1571 struct AVMD5 *md5 = av_md5_alloc();
1572 if (!md5)
1573 return AVERROR(ENOMEM);
1574
1575 snprintf(cnonce, sizeof(cnonce), "%08x", av_get_random_seed());
1576
1577 av_md5_init(md5);
1578 av_md5_update(md5, user, strlen(user));
1579 av_md5_update(md5, ":", 1);
1580 av_md5_update(md5, realm, strlen(realm));
1581 av_md5_update(md5, ":", 1);
1582 av_md5_update(md5, rt->password, strlen(rt->password));
1583 av_md5_final(md5, hash);
1584 ff_data_to_hex(hashstr1, hash, 16, 1);
1585 hashstr1[32] = '\0';
1586
1587 av_md5_init(md5);
1588 av_md5_update(md5, method, strlen(method));
1589 av_md5_update(md5, ":/", 2);
1590 av_md5_update(md5, rt->app, strlen(rt->app));
1591 av_md5_final(md5, hash);
1592 ff_data_to_hex(hashstr2, hash, 16, 1);
1593 hashstr2[32] = '\0';
1594
1595 av_md5_init(md5);
1596 av_md5_update(md5, hashstr1, strlen(hashstr1));
1597 av_md5_update(md5, ":", 1);
1598 if (nonce)
1599 av_md5_update(md5, nonce, strlen(nonce));
1600 av_md5_update(md5, ":", 1);
1601 av_md5_update(md5, nc, strlen(nc));
1602 av_md5_update(md5, ":", 1);
1603 av_md5_update(md5, cnonce, strlen(cnonce));
1604 av_md5_update(md5, ":", 1);
1605 av_md5_update(md5, qop, strlen(qop));
1606 av_md5_update(md5, ":", 1);
1607 av_md5_update(md5, hashstr2, strlen(hashstr2));
1608 av_md5_final(md5, hash);
1609 ff_data_to_hex(hashstr1, hash, 16, 1);
1610
1611 snprintf(rt->auth_params, sizeof(rt->auth_params),
1612 "?authmod=%s&user=%s&nonce=%s&cnonce=%s&nc=%s&response=%s",
1613 "llnw", user, nonce, cnonce, nc, hashstr1);
1614
1615 av_free(md5);
1616 return 0;
1617}
1618
08225d01
MS
1619static int handle_connect_error(URLContext *s, const char *desc)
1620{
1621 RTMPContext *rt = s->priv_data;
c1ea44c5 1622 char buf[300], *ptr, authmod[15];
08225d01
MS
1623 int i = 0, ret = 0;
1624 const char *user = "", *salt = "", *opaque = NULL,
c1ea44c5 1625 *challenge = NULL, *cptr = NULL, *nonce = NULL;
08225d01 1626
c1ea44c5
MS
1627 if (!(cptr = strstr(desc, "authmod=adobe")) &&
1628 !(cptr = strstr(desc, "authmod=llnw"))) {
08225d01
MS
1629 av_log(s, AV_LOG_ERROR,
1630 "Unknown connect error (unsupported authentication method?)\n");
1631 return AVERROR_UNKNOWN;
1632 }
c1ea44c5
MS
1633 cptr += strlen("authmod=");
1634 while (*cptr && *cptr != ' ' && i < sizeof(authmod) - 1)
1635 authmod[i++] = *cptr++;
1636 authmod[i] = '\0';
08225d01
MS
1637
1638 if (!rt->username[0] || !rt->password[0]) {
1639 av_log(s, AV_LOG_ERROR, "No credentials set\n");
1640 return AVERROR_UNKNOWN;
1641 }
1642
1643 if (strstr(desc, "?reason=authfailed")) {
1644 av_log(s, AV_LOG_ERROR, "Incorrect username/password\n");
1645 return AVERROR_UNKNOWN;
1646 } else if (strstr(desc, "?reason=nosuchuser")) {
1647 av_log(s, AV_LOG_ERROR, "Incorrect username\n");
1648 return AVERROR_UNKNOWN;
1649 }
1650
1651 if (rt->auth_tried) {
1652 av_log(s, AV_LOG_ERROR, "Authentication failed\n");
1653 return AVERROR_UNKNOWN;
1654 }
1655
1656 rt->auth_params[0] = '\0';
1657
1658 if (strstr(desc, "code=403 need auth")) {
1659 snprintf(rt->auth_params, sizeof(rt->auth_params),
c1ea44c5 1660 "?authmod=%s&user=%s", authmod, rt->username);
08225d01
MS
1661 return 0;
1662 }
1663
1664 if (!(cptr = strstr(desc, "?reason=needauth"))) {
1665 av_log(s, AV_LOG_ERROR, "No auth parameters found\n");
1666 return AVERROR_UNKNOWN;
1667 }
1668
1669 av_strlcpy(buf, cptr + 1, sizeof(buf));
1670 ptr = buf;
1671
1672 while (ptr) {
1673 char *next = strchr(ptr, '&');
1674 char *value = strchr(ptr, '=');
1675 if (next)
1676 *next++ = '\0';
1677 if (value)
1678 *value++ = '\0';
1679 if (!strcmp(ptr, "user")) {
1680 user = value;
1681 } else if (!strcmp(ptr, "salt")) {
1682 salt = value;
1683 } else if (!strcmp(ptr, "opaque")) {
1684 opaque = value;
1685 } else if (!strcmp(ptr, "challenge")) {
1686 challenge = value;
c1ea44c5
MS
1687 } else if (!strcmp(ptr, "nonce")) {
1688 nonce = value;
08225d01
MS
1689 }
1690 ptr = next;
1691 }
1692
c1ea44c5 1693 if (!strcmp(authmod, "adobe")) {
a5e6080a 1694 if ((ret = do_adobe_auth(rt, user, salt, opaque, challenge)) < 0)
c1ea44c5
MS
1695 return ret;
1696 } else {
1697 if ((ret = do_llnw_auth(rt, user, nonce)) < 0)
1698 return ret;
1699 }
08225d01
MS
1700
1701 rt->auth_tried = 1;
1702 return 0;
1703}
1704
3eebc1e1
SP
1705static int handle_invoke_error(URLContext *s, RTMPPacket *pkt)
1706{
08225d01 1707 RTMPContext *rt = s->priv_data;
3eebc1e1 1708 const uint8_t *data_end = pkt->data + pkt->data_size;
fb7e7808
SP
1709 char *tracked_method = NULL;
1710 int level = AV_LOG_ERROR;
3eebc1e1 1711 uint8_t tmpstr[256];
fb7e7808
SP
1712 int ret;
1713
1714 if ((ret = find_tracked_method(s, pkt, 9, &tracked_method)) < 0)
1715 return ret;
3eebc1e1
SP
1716
1717 if (!ff_amf_get_field_value(pkt->data + 9, data_end,
1718 "description", tmpstr, sizeof(tmpstr))) {
7011a42b
SP
1719 if (tracked_method && (!strcmp(tracked_method, "_checkbw") ||
1720 !strcmp(tracked_method, "releaseStream") ||
1721 !strcmp(tracked_method, "FCSubscribe") ||
1722 !strcmp(tracked_method, "FCPublish"))) {
1723 /* Gracefully ignore Adobe-specific historical artifact errors. */
fb7e7808
SP
1724 level = AV_LOG_WARNING;
1725 ret = 0;
08225d01
MS
1726 } else if (tracked_method && !strcmp(tracked_method, "connect")) {
1727 ret = handle_connect_error(s, tmpstr);
1728 if (!ret) {
1729 rt->do_reconnect = 1;
1730 level = AV_LOG_VERBOSE;
1731 }
fb7e7808 1732 } else
c76daa89 1733 ret = AVERROR_UNKNOWN;
fb7e7808 1734 av_log(s, level, "Server error: %s\n", tmpstr);
3eebc1e1
SP
1735 }
1736
fb7e7808
SP
1737 av_free(tracked_method);
1738 return ret;
3eebc1e1
SP
1739}
1740
e5f2731c
JO
1741static int send_invoke_response(URLContext *s, RTMPPacket *pkt)
1742{
1743 RTMPContext *rt = s->priv_data;
1744 double seqnum;
1745 char filename[64];
1746 char command[64];
1747 char statusmsg[128];
1748 int stringlen;
1749 char *pchar;
1750 const uint8_t *p = pkt->data;
1751 uint8_t *pp = NULL;
1752 RTMPPacket spkt = { 0 };
1753 GetByteContext gbc;
1754 int ret;
1755
1756 bytestream2_init(&gbc, p, pkt->data_size);
1757 if (ff_amf_read_string(&gbc, command, sizeof(command),
1758 &stringlen)) {
1759 av_log(s, AV_LOG_ERROR, "Error in PT_INVOKE\n");
1760 return AVERROR_INVALIDDATA;
1761 }
1762
1763 ret = ff_amf_read_number(&gbc, &seqnum);
1764 if (ret)
1765 return ret;
1766 ret = ff_amf_read_null(&gbc);
1767 if (ret)
1768 return ret;
1769 if (!strcmp(command, "FCPublish") ||
1770 !strcmp(command, "publish")) {
1771 ret = ff_amf_read_string(&gbc, filename,
1772 sizeof(filename), &stringlen);
1773 // check with url
1774 if (s->filename) {
1775 pchar = strrchr(s->filename, '/');
1776 if (!pchar) {
1777 av_log(s, AV_LOG_WARNING,
1778 "Unable to find / in url %s, bad format\n",
1779 s->filename);
1780 pchar = s->filename;
1781 }
1782 pchar++;
1783 if (strcmp(pchar, filename))
1784 av_log(s, AV_LOG_WARNING, "Unexpected stream %s, expecting"
1785 " %s\n", filename, pchar);
1786 }
1787 rt->state = STATE_RECEIVING;
1788 }
1789
1790 if (!strcmp(command, "FCPublish")) {
1791 if ((ret = ff_rtmp_packet_create(&spkt, RTMP_SYSTEM_CHANNEL,
1792 RTMP_PT_INVOKE, 0,
1793 RTMP_PKTDATA_DEFAULT_SIZE)) < 0) {
1794 av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
1795 return ret;
1796 }
1797 pp = spkt.data;
1798 ff_amf_write_string(&pp, "onFCPublish");
1799 } else if (!strcmp(command, "publish")) {
1800 PutByteContext pbc;
1801 // Send Stream Begin 1
1802 if ((ret = ff_rtmp_packet_create(&spkt, RTMP_NETWORK_CHANNEL,
1803 RTMP_PT_PING, 0, 6)) < 0) {
1804 av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
1805 return ret;
1806 }
1807 pp = spkt.data;
1808 bytestream2_init_writer(&pbc, pp, spkt.data_size);
1809 bytestream2_put_be16(&pbc, 0); // 0 -> Stream Begin
1810 bytestream2_put_be32(&pbc, rt->nb_streamid);
1811 ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
1812 rt->prev_pkt[1]);
1813 ff_rtmp_packet_destroy(&spkt);
1814 if (ret < 0)
1815 return ret;
1816
1817 // Send onStatus(NetStream.Publish.Start)
1818 if ((ret = ff_rtmp_packet_create(&spkt, RTMP_SYSTEM_CHANNEL,
1819 RTMP_PT_INVOKE, 0,
1820 RTMP_PKTDATA_DEFAULT_SIZE)) < 0) {
1821 av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
1822 return ret;
1823 }
1824 spkt.extra = pkt->extra;
1825 pp = spkt.data;
1826 ff_amf_write_string(&pp, "onStatus");
1827 ff_amf_write_number(&pp, 0);
1828 ff_amf_write_null(&pp);
1829
1830 ff_amf_write_object_start(&pp);
1831 ff_amf_write_field_name(&pp, "level");
1832 ff_amf_write_string(&pp, "status");
1833 ff_amf_write_field_name(&pp, "code");
1834 ff_amf_write_string(&pp, "NetStream.Publish.Start");
1835 ff_amf_write_field_name(&pp, "description");
1836 snprintf(statusmsg, sizeof(statusmsg),
1837 "%s is now published", filename);
1838 ff_amf_write_string(&pp, statusmsg);
1839 ff_amf_write_field_name(&pp, "details");
1840 ff_amf_write_string(&pp, filename);
1841 ff_amf_write_field_name(&pp, "clientid");
1842 snprintf(statusmsg, sizeof(statusmsg), "%s", LIBAVFORMAT_IDENT);
1843 ff_amf_write_string(&pp, statusmsg);
1844 ff_amf_write_object_end(&pp);
1845
1846 } else {
1847 if ((ret = ff_rtmp_packet_create(&spkt, RTMP_SYSTEM_CHANNEL,
1848 RTMP_PT_INVOKE, 0,
1849 RTMP_PKTDATA_DEFAULT_SIZE)) < 0) {
1850 av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
1851 return ret;
1852 }
1853 pp = spkt.data;
1854 ff_amf_write_string(&pp, "_result");
1855 ff_amf_write_number(&pp, seqnum);
1856 ff_amf_write_null(&pp);
1857 if (!strcmp(command, "createStream")) {
1858 rt->nb_streamid++;
1859 if (rt->nb_streamid == 0 || rt->nb_streamid == 2)
1860 rt->nb_streamid++; /* Values 0 and 2 are reserved */
1861 ff_amf_write_number(&pp, rt->nb_streamid);
1862 /* By now we don't control which streams are removed in
1863 * deleteStream. There is no stream creation control
1864 * if a client creates more than 2^32 - 2 streams. */
1865 }
1866 }
1867 spkt.data_size = pp - spkt.data;
1868 ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
1869 rt->prev_pkt[1]);
1870 ff_rtmp_packet_destroy(&spkt);
1871 return ret;
1872}
1873
5e6001db
SP
1874static int handle_invoke_result(URLContext *s, RTMPPacket *pkt)
1875{
1876 RTMPContext *rt = s->priv_data;
1877 char *tracked_method = NULL;
5e6001db 1878 int ret = 0;
5e6001db 1879
a8103503 1880 if ((ret = find_tracked_method(s, pkt, 10, &tracked_method)) < 0)
5e6001db
SP
1881 return ret;
1882
5e6001db
SP
1883 if (!tracked_method) {
1884 /* Ignore this reply when the current method is not tracked. */
1885 return ret;
1886 }
1887
1888 if (!memcmp(tracked_method, "connect", 7)) {
1889 if (!rt->is_input) {
1890 if ((ret = gen_release_stream(s, rt)) < 0)
1891 goto fail;
1892
1893 if ((ret = gen_fcpublish_stream(s, rt)) < 0)
1894 goto fail;
1895 } else {
1896 if ((ret = gen_server_bw(s, rt)) < 0)
1897 goto fail;
1898 }
1899
1900 if ((ret = gen_create_stream(s, rt)) < 0)
1901 goto fail;
1902
1903 if (rt->is_input) {
1904 /* Send the FCSubscribe command when the name of live
1905 * stream is defined by the user or if it's a live stream. */
1906 if (rt->subscribe) {
1907 if ((ret = gen_fcsubscribe_stream(s, rt, rt->subscribe)) < 0)
1908 goto fail;
1909 } else if (rt->live == -1) {
1910 if ((ret = gen_fcsubscribe_stream(s, rt, rt->playpath)) < 0)
1911 goto fail;
1912 }
1913 }
1914 } else if (!memcmp(tracked_method, "createStream", 12)) {
1915 //extract a number from the result
1916 if (pkt->data[10] || pkt->data[19] != 5 || pkt->data[20]) {
1917 av_log(s, AV_LOG_WARNING, "Unexpected reply on connect()\n");
1918 } else {
1919 rt->main_channel_id = av_int2double(AV_RB64(pkt->data + 21));
1920 }
1921
1922 if (!rt->is_input) {
1923 if ((ret = gen_publish(s, rt)) < 0)
1924 goto fail;
1925 } else {
1926 if ((ret = gen_play(s, rt)) < 0)
1927 goto fail;
1928 if ((ret = gen_buffer_time(s, rt)) < 0)
1929 goto fail;
1930 }
1931 }
1932
1933fail:
1934 av_free(tracked_method);
1935 return ret;
1936}
1937
71036a3a 1938static int handle_invoke_status(URLContext *s, RTMPPacket *pkt)
9fd6b843 1939{
6d1c9945 1940 RTMPContext *rt = s->priv_data;
71036a3a
SP
1941 const uint8_t *data_end = pkt->data + pkt->data_size;
1942 const uint8_t *ptr = pkt->data + 11;
1943 uint8_t tmpstr[256];
9fd6b843 1944 int i, t;
71036a3a
SP
1945
1946 for (i = 0; i < 2; i++) {
1947 t = ff_amf_tag_size(ptr, data_end);
1948 if (t < 0)
1949 return 1;
1950 ptr += t;
1951 }
1952
1953 t = ff_amf_get_field_value(ptr, data_end, "level", tmpstr, sizeof(tmpstr));
1954 if (!t && !strcmp(tmpstr, "error")) {
1955 if (!ff_amf_get_field_value(ptr, data_end,
1956 "description", tmpstr, sizeof(tmpstr)))
1957 av_log(s, AV_LOG_ERROR, "Server error: %s\n", tmpstr);
1958 return -1;
1959 }
1960
1961 t = ff_amf_get_field_value(ptr, data_end, "code", tmpstr, sizeof(tmpstr));
1962 if (!t && !strcmp(tmpstr, "NetStream.Play.Start")) rt->state = STATE_PLAYING;
1963 if (!t && !strcmp(tmpstr, "NetStream.Play.Stop")) rt->state = STATE_STOPPED;
1964 if (!t && !strcmp(tmpstr, "NetStream.Play.UnpublishNotify")) rt->state = STATE_STOPPED;
1965 if (!t && !strcmp(tmpstr, "NetStream.Publish.Start")) rt->state = STATE_PUBLISHING;
1966
1967 return 0;
1968}
1969
1970static int handle_invoke(URLContext *s, RTMPPacket *pkt)
1971{
1972 RTMPContext *rt = s->priv_data;
f89584ca 1973 int ret = 0;
9fd6b843 1974
6d1c9945
SP
1975 //TODO: check for the messages sent for wrong state?
1976 if (!memcmp(pkt->data, "\002\000\006_error", 9)) {
3eebc1e1
SP
1977 if ((ret = handle_invoke_error(s, pkt)) < 0)
1978 return ret;
6d1c9945 1979 } else if (!memcmp(pkt->data, "\002\000\007_result", 10)) {
5e6001db 1980 if ((ret = handle_invoke_result(s, pkt)) < 0)
f89584ca 1981 return ret;
6d1c9945 1982 } else if (!memcmp(pkt->data, "\002\000\010onStatus", 11)) {
71036a3a
SP
1983 if ((ret = handle_invoke_status(s, pkt)) < 0)
1984 return ret;
6d1c9945
SP
1985 } else if (!memcmp(pkt->data, "\002\000\010onBWDone", 11)) {
1986 if ((ret = gen_check_bw(s, rt)) < 0)
1987 return ret;
e5f2731c
JO
1988 } else if (!memcmp(pkt->data, "\002\000\015releaseStream", 16) ||
1989 !memcmp(pkt->data, "\002\000\011FCPublish", 12) ||
1990 !memcmp(pkt->data, "\002\000\007publish", 10) ||
1991 !memcmp(pkt->data, "\002\000\010_checkbw", 11) ||
1992 !memcmp(pkt->data, "\002\000\014createStream", 15)) {
6dc85054 1993 if ((ret = send_invoke_response(s, pkt)) < 0)
e5f2731c 1994 return ret;
6d1c9945
SP
1995 }
1996
f89584ca 1997 return ret;
6d1c9945
SP
1998}
1999
e5f2731c
JO
2000static int handle_notify(URLContext *s, RTMPPacket *pkt) {
2001 RTMPContext *rt = s->priv_data;
2002 const uint8_t *p = NULL;
2003 uint8_t *cp = NULL;
2004 uint8_t commandbuffer[64];
2005 char statusmsg[128];
2006 int stringlen;
2007 GetByteContext gbc;
2008 PutByteContext pbc;
2009 uint32_t ts;
2010 int old_flv_size;
2011 const uint8_t *datatowrite;
2012 unsigned datatowritelength;
2013
2014 p = pkt->data;
2015 bytestream2_init(&gbc, p, pkt->data_size);
2016 if (ff_amf_read_string(&gbc, commandbuffer, sizeof(commandbuffer),
2017 &stringlen))
2018 return AVERROR_INVALIDDATA;
2019 if (!strcmp(commandbuffer, "@setDataFrame")) {
2020 datatowrite = gbc.buffer;
2021 datatowritelength = bytestream2_get_bytes_left(&gbc);
2022 if (ff_amf_read_string(&gbc, statusmsg,
2023 sizeof(statusmsg), &stringlen))
2024 return AVERROR_INVALIDDATA;
2025 if (strcmp(statusmsg, "onMetaData")) {
2026 av_log(s, AV_LOG_INFO, "Expecting onMetadata but got %s\n",
2027 statusmsg);
2028 return 0;
2029 }
2030
2031 /* Provide ECMAArray to flv */
2032 ts = pkt->timestamp;
2033
2034 // generate packet header and put data into buffer for FLV demuxer
2035 if (rt->flv_off < rt->flv_size) {
2036 old_flv_size = rt->flv_size;
2037 rt->flv_size += datatowritelength + 15;
2038 } else {
2039 old_flv_size = 0;
2040 rt->flv_size = datatowritelength + 15;
2041 rt->flv_off = 0;
2042 }
2043
2044 cp = av_realloc(rt->flv_data, rt->flv_size);
2045 if (!cp)
2046 return AVERROR(ENOMEM);
2047 rt->flv_data = cp;
2048 bytestream2_init_writer(&pbc, cp, rt->flv_size);
2049 bytestream2_skip_p(&pbc, old_flv_size);
2050 bytestream2_put_byte(&pbc, pkt->type);
2051 bytestream2_put_be24(&pbc, datatowritelength);
2052 bytestream2_put_be24(&pbc, ts);
2053 bytestream2_put_byte(&pbc, ts >> 24);
2054 bytestream2_put_be24(&pbc, 0);
2055 bytestream2_put_buffer(&pbc, datatowrite, datatowritelength);
2056 bytestream2_put_be32(&pbc, 0);
2057 }
2058 return 0;
2059}
2060
6d1c9945
SP
2061/**
2062 * Parse received packet and possibly perform some action depending on
2063 * the packet contents.
2064 * @return 0 for no errors, negative values for serious errors which prevent
2065 * further communications, positive values for uncritical errors
2066 */
2067static int rtmp_parse_result(URLContext *s, RTMPContext *rt, RTMPPacket *pkt)
2068{
2069 int ret;
2070
2071#ifdef DEBUG
2072 ff_rtmp_packet_dump(s, pkt);
2073#endif
2074
2075 switch (pkt->type) {
fb96c1c5
LB
2076 case RTMP_PT_BYTES_READ:
2077 av_dlog(s, "received bytes read report\n");
2078 break;
6d1c9945
SP
2079 case RTMP_PT_CHUNK_SIZE:
2080 if ((ret = handle_chunk_size(s, pkt)) < 0)
2081 return ret;
2082 break;
2083 case RTMP_PT_PING:
2084 if ((ret = handle_ping(s, pkt)) < 0)
2085 return ret;
2086 break;
2087 case RTMP_PT_CLIENT_BW:
2088 if ((ret = handle_client_bw(s, pkt)) < 0)
2089 return ret;
2090 break;
2091 case RTMP_PT_SERVER_BW:
2092 if ((ret = handle_server_bw(s, pkt)) < 0)
2093 return ret;
2094 break;
2095 case RTMP_PT_INVOKE:
2096 if ((ret = handle_invoke(s, pkt)) < 0)
2097 return ret;
9fd6b843 2098 break;
08e087cc
JO
2099 case RTMP_PT_VIDEO:
2100 case RTMP_PT_AUDIO:
9c9c21ea 2101 case RTMP_PT_METADATA:
e5f2731c 2102 case RTMP_PT_NOTIFY:
9c9c21ea 2103 /* Audio, Video and Metadata packets are parsed in get_packet() */
08e087cc 2104 break;
9ff930aa
SP
2105 default:
2106 av_log(s, AV_LOG_VERBOSE, "Unknown packet type received 0x%02X\n", pkt->type);
2107 break;
9fd6b843
KS
2108 }
2109 return 0;
2110}
2111
2112/**
49bd8e4b 2113 * Interact with the server by receiving and sending RTMP packets until
9fd6b843
KS
2114 * there is some significant data (media data or expected status notification).
2115 *
2116 * @param s reading context
1d8041b3
SS
2117 * @param for_header non-zero value tells function to work until it
2118 * gets notification from the server that playing has been started,
2119 * otherwise function will work until some media data is received (or
2120 * an error happens)
9fd6b843
KS
2121 * @return 0 for successful operation, negative value in case of error
2122 */
2123static int get_packet(URLContext *s, int for_header)
2124{
2125 RTMPContext *rt = s->priv_data;
2126 int ret;
56e29bf2
S
2127 uint8_t *p;
2128 const uint8_t *next;
2129 uint32_t data_size;
2130 uint32_t ts, cts, pts=0;
9fd6b843 2131
72b870b9
MS
2132 if (rt->state == STATE_STOPPED)
2133 return AVERROR_EOF;
2134
e07c92e4 2135 for (;;) {
271c869c 2136 RTMPPacket rpkt = { 0 };
9fd6b843 2137 if ((ret = ff_rtmp_packet_read(rt->stream, &rpkt,
f5ce90f2 2138 rt->in_chunk_size, rt->prev_pkt[0])) <= 0) {
b381a823 2139 if (ret == 0) {
9fd6b843
KS
2140 return AVERROR(EAGAIN);
2141 } else {
2142 return AVERROR(EIO);
2143 }
2144 }
bf7c1719
KS
2145 rt->bytes_read += ret;
2146 if (rt->bytes_read > rt->last_bytes_read + rt->client_report_size) {
7f804085 2147 av_log(s, AV_LOG_DEBUG, "Sending bytes read report\n");
f645f1d6
SP
2148 if ((ret = gen_bytes_read(s, rt, rpkt.timestamp + 1)) < 0)
2149 return ret;
bf7c1719
KS
2150 rt->last_bytes_read = rt->bytes_read;
2151 }
9fd6b843
KS
2152
2153 ret = rtmp_parse_result(s, rt, &rpkt);
2154 if (ret < 0) {//serious error in current packet
2155 ff_rtmp_packet_destroy(&rpkt);
f645f1d6 2156 return ret;
9fd6b843 2157 }
08225d01
MS
2158 if (rt->do_reconnect && for_header) {
2159 ff_rtmp_packet_destroy(&rpkt);
2160 return 0;
2161 }
72b870b9
MS
2162 if (rt->state == STATE_STOPPED) {
2163 ff_rtmp_packet_destroy(&rpkt);
2164 return AVERROR_EOF;
2165 }
e5f2731c
JO
2166 if (for_header && (rt->state == STATE_PLAYING ||
2167 rt->state == STATE_PUBLISHING ||
2168 rt->state == STATE_RECEIVING)) {
9fd6b843
KS
2169 ff_rtmp_packet_destroy(&rpkt);
2170 return 0;
2171 }
6bf22e18 2172 if (!rpkt.data_size || !rt->is_input) {
9fd6b843
KS
2173 ff_rtmp_packet_destroy(&rpkt);
2174 continue;
2175 }
2176 if (rpkt.type == RTMP_PT_VIDEO || rpkt.type == RTMP_PT_AUDIO ||
afbacb93 2177 (rpkt.type == RTMP_PT_NOTIFY && !memcmp("\002\000\012onMetaData", rpkt.data, 13))) {
56e29bf2 2178 ts = rpkt.timestamp;
9fd6b843 2179
9fd6b843
KS
2180 // generate packet header and put data into buffer for FLV demuxer
2181 rt->flv_off = 0;
2182 rt->flv_size = rpkt.data_size + 15;
2183 rt->flv_data = p = av_realloc(rt->flv_data, rt->flv_size);
2184 bytestream_put_byte(&p, rpkt.type);
2185 bytestream_put_be24(&p, rpkt.data_size);
2186 bytestream_put_be24(&p, ts);
2187 bytestream_put_byte(&p, ts >> 24);
2188 bytestream_put_be24(&p, 0);
2189 bytestream_put_buffer(&p, rpkt.data, rpkt.data_size);
2190 bytestream_put_be32(&p, 0);
2191 ff_rtmp_packet_destroy(&rpkt);
2192 return 0;
e5f2731c
JO
2193 } else if (rpkt.type == RTMP_PT_NOTIFY) {
2194 ret = handle_notify(s, &rpkt);
2195 ff_rtmp_packet_destroy(&rpkt);
2196 if (ret) {
2197 av_log(s, AV_LOG_ERROR, "Handle notify error\n");
2198 return ret;
2199 }
2200 return 0;
9fd6b843
KS
2201 } else if (rpkt.type == RTMP_PT_METADATA) {
2202 // we got raw FLV data, make it available for FLV demuxer
2203 rt->flv_off = 0;
2204 rt->flv_size = rpkt.data_size;
2205 rt->flv_data = av_realloc(rt->flv_data, rt->flv_size);
56e29bf2
S
2206 /* rewrite timestamps */
2207 next = rpkt.data;
2208 ts = rpkt.timestamp;
2209 while (next - rpkt.data < rpkt.data_size - 11) {
2210 next++;
2211 data_size = bytestream_get_be24(&next);
2212 p=next;
2213 cts = bytestream_get_be24(&next);
aae9a093 2214 cts |= bytestream_get_byte(&next) << 24;
56e29bf2
S
2215 if (pts==0)
2216 pts=cts;
2217 ts += cts - pts;
2218 pts = cts;
2219 bytestream_put_be24(&p, ts);
2220 bytestream_put_byte(&p, ts >> 24);
2221 next += data_size + 3 + 4;
2222 }
9fd6b843
KS
2223 memcpy(rt->flv_data, rpkt.data, rpkt.data_size);
2224 ff_rtmp_packet_destroy(&rpkt);
2225 return 0;
2226 }
2227 ff_rtmp_packet_destroy(&rpkt);
2228 }
9fd6b843
KS
2229}
2230
2231static int rtmp_close(URLContext *h)
2232{
2233 RTMPContext *rt = h->priv_data;
f645f1d6 2234 int ret = 0;
9fd6b843 2235
e07c92e4 2236 if (!rt->is_input) {
6bf22e18
S
2237 rt->flv_data = NULL;
2238 if (rt->out_pkt.data_size)
2239 ff_rtmp_packet_destroy(&rt->out_pkt);
615c2879 2240 if (rt->state > STATE_FCPUBLISH)
f645f1d6 2241 ret = gen_fcunpublish_stream(h, rt);
6bf22e18 2242 }
615c2879 2243 if (rt->state > STATE_HANDSHAKED)
f645f1d6 2244 ret = gen_delete_stream(h, rt);
6bf22e18 2245
f89584ca 2246 free_tracked_methods(rt);
9fd6b843 2247 av_freep(&rt->flv_data);
e52a9145 2248 ffurl_close(rt->stream);
f645f1d6 2249 return ret;
9fd6b843
KS
2250}
2251
2252/**
49bd8e4b 2253 * Open RTMP connection and verify that the stream can be played.
9fd6b843
KS
2254 *
2255 * URL syntax: rtmp://server[:port][/app][/playpath]
2256 * where 'app' is first one or two directories in the path
2257 * (e.g. /ondemand/, /flash/live/, etc.)
2258 * and 'playpath' is a file name (the rest of the path,
2259 * may be prefixed with "mp4:")
2260 */
2261static int rtmp_open(URLContext *s, const char *uri, int flags)
2262{
7e580505 2263 RTMPContext *rt = s->priv_data;
08225d01 2264 char proto[8], hostname[256], path[1024], auth[100], *fname;
6465562e 2265 char *old_app;
9fd6b843 2266 uint8_t buf[2048];
b316991b 2267 int port;
86991ce2 2268 AVDictionary *opts = NULL;
9fd6b843
KS
2269 int ret;
2270
e5f2731c
JO
2271 if (rt->listen_timeout > 0)
2272 rt->listen = 1;
2273
59d96941 2274 rt->is_input = !(flags & AVIO_FLAG_WRITE);
9fd6b843 2275
08225d01
MS
2276 av_url_split(proto, sizeof(proto), auth, sizeof(auth),
2277 hostname, sizeof(hostname), &port,
f984dcf6 2278 path, sizeof(path), s->filename);
9fd6b843 2279
08225d01
MS
2280 if (auth[0]) {
2281 char *ptr = strchr(auth, ':');
2282 if (ptr) {
2283 *ptr = '\0';
2284 av_strlcpy(rt->username, auth, sizeof(rt->username));
2285 av_strlcpy(rt->password, ptr + 1, sizeof(rt->password));
2286 }
2287 }
2288
e5f2731c
JO
2289 if (rt->listen && strcmp(proto, "rtmp")) {
2290 av_log(s, AV_LOG_ERROR, "rtmp_listen not available for %s\n",
2291 proto);
2292 return AVERROR(EINVAL);
2293 }
86991ce2
SP
2294 if (!strcmp(proto, "rtmpt") || !strcmp(proto, "rtmpts")) {
2295 if (!strcmp(proto, "rtmpts"))
2296 av_dict_set(&opts, "ffrtmphttp_tls", "1", 1);
2297
8e50c57d 2298 /* open the http tunneling connection */
775c4d36 2299 ff_url_join(buf, sizeof(buf), "ffrtmphttp", NULL, hostname, port, NULL);
6aedabc9
SP
2300 } else if (!strcmp(proto, "rtmps")) {
2301 /* open the tls connection */
2302 if (port < 0)
2303 port = RTMPS_DEFAULT_PORT;
2304 ff_url_join(buf, sizeof(buf), "tls", NULL, hostname, port, NULL);
08cd95e8
SP
2305 } else if (!strcmp(proto, "rtmpe") || (!strcmp(proto, "rtmpte"))) {
2306 if (!strcmp(proto, "rtmpte"))
2307 av_dict_set(&opts, "ffrtmpcrypt_tunneling", "1", 1);
2308
acd554c1
SP
2309 /* open the encrypted connection */
2310 ff_url_join(buf, sizeof(buf), "ffrtmpcrypt", NULL, hostname, port, NULL);
2311 rt->encrypted = 1;
8e50c57d
SP
2312 } else {
2313 /* open the tcp connection */
2314 if (port < 0)
2315 port = RTMP_DEFAULT_PORT;
e5f2731c
JO
2316 if (rt->listen)
2317 ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port,
2318 "?listen&listen_timeout=%d",
2319 rt->listen_timeout * 1000);
2320 else
2321 ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, NULL);
8e50c57d 2322 }
9fd6b843 2323
08225d01 2324reconnect:
f645f1d6 2325 if ((ret = ffurl_open(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
86991ce2 2326 &s->interrupt_callback, &opts)) < 0) {
59d96941 2327 av_log(s , AV_LOG_ERROR, "Cannot open connection %s\n", buf);
9fd6b843 2328 goto fail;
fe523958 2329 }
9fd6b843 2330
93f257db
SP
2331 if (rt->swfverify) {
2332 if ((ret = rtmp_calc_swfhash(s)) < 0)
2333 goto fail;
2334 }
2335
c7240611 2336 rt->state = STATE_START;
e5f2731c
JO
2337 if (!rt->listen && (ret = rtmp_handshake(s, rt)) < 0)
2338 goto fail;
2339 if (rt->listen && (ret = rtmp_server_handshake(s, rt)) < 0)
02490bf3 2340 goto fail;
9fd6b843 2341
f5ce90f2
JO
2342 rt->out_chunk_size = 128;
2343 rt->in_chunk_size = 128; // Probably overwritten later
c7240611 2344 rt->state = STATE_HANDSHAKED;
6465562e
SP
2345
2346 // Keep the application name when it has been defined by the user.
2347 old_app = rt->app;
2348
2349 rt->app = av_malloc(APP_MAX_LENGTH);
2350 if (!rt->app) {
f645f1d6
SP
2351 ret = AVERROR(ENOMEM);
2352 goto fail;
6465562e
SP
2353 }
2354
c7240611
KS
2355 //extract "app" part from path
2356 if (!strncmp(path, "/ondemand/", 10)) {
2357 fname = path + 10;
2358 memcpy(rt->app, "ondemand", 9);
2359 } else {
4b7304e8
MS
2360 char *next = *path ? path + 1 : path;
2361 char *p = strchr(next, '/');
c7240611 2362 if (!p) {
4b7304e8 2363 fname = next;
c7240611 2364 rt->app[0] = '\0';
9fd6b843 2365 } else {
c6eeb9b7 2366 // make sure we do not mismatch a playpath for an application instance
c7240611
KS
2367 char *c = strchr(p + 1, ':');
2368 fname = strchr(p + 1, '/');
c6eeb9b7 2369 if (!fname || (c && c < fname)) {
c7240611
KS
2370 fname = p + 1;
2371 av_strlcpy(rt->app, path + 1, p - path);
9fd6b843 2372 } else {
c7240611
KS
2373 fname++;
2374 av_strlcpy(rt->app, path + 1, fname - path - 1);
9fd6b843
KS
2375 }
2376 }
c7240611 2377 }
6465562e
SP
2378
2379 if (old_app) {
2380 // The name of application has been defined by the user, override it.
2381 av_free(rt->app);
2382 rt->app = old_app;
2383 }
2384
b3b17512 2385 if (!rt->playpath) {
f862537d
SP
2386 int len = strlen(fname);
2387
b3b17512
SP
2388 rt->playpath = av_malloc(PLAYPATH_MAX_LENGTH);
2389 if (!rt->playpath) {
f645f1d6
SP
2390 ret = AVERROR(ENOMEM);
2391 goto fail;
b3b17512
SP
2392 }
2393
0a9a2257 2394 if (!strchr(fname, ':') && len >= 4 &&
f862537d
SP
2395 (!strcmp(fname + len - 4, ".f4v") ||
2396 !strcmp(fname + len - 4, ".mp4"))) {
b3b17512 2397 memcpy(rt->playpath, "mp4:", 5);
0a9a2257 2398 } else if (len >= 4 && !strcmp(fname + len - 4, ".flv")) {
f862537d 2399 fname[len - 4] = '\0';
b3b17512
SP
2400 } else {
2401 rt->playpath[0] = 0;
2402 }
d578f947 2403 av_strlcat(rt->playpath, fname, PLAYPATH_MAX_LENGTH);
c7240611 2404 }
9fd6b843 2405
55c9320e
SP
2406 if (!rt->tcurl) {
2407 rt->tcurl = av_malloc(TCURL_MAX_LENGTH);
08e93f5b
SP
2408 if (!rt->tcurl) {
2409 ret = AVERROR(ENOMEM);
2410 goto fail;
2411 }
55c9320e
SP
2412 ff_url_join(rt->tcurl, TCURL_MAX_LENGTH, proto, NULL, hostname,
2413 port, "/%s", rt->app);
2414 }
2415
e64673e4
SP
2416 if (!rt->flashver) {
2417 rt->flashver = av_malloc(FLASHVER_MAX_LENGTH);
08e93f5b
SP
2418 if (!rt->flashver) {
2419 ret = AVERROR(ENOMEM);
2420 goto fail;
2421 }
e64673e4
SP
2422 if (rt->is_input) {
2423 snprintf(rt->flashver, FLASHVER_MAX_LENGTH, "%s %d,%d,%d,%d",
2424 RTMP_CLIENT_PLATFORM, RTMP_CLIENT_VER1, RTMP_CLIENT_VER2,
2425 RTMP_CLIENT_VER3, RTMP_CLIENT_VER4);
2426 } else {
2427 snprintf(rt->flashver, FLASHVER_MAX_LENGTH,
2428 "FMLE/3.0 (compatible; %s)", LIBAVFORMAT_IDENT);
2429 }
2430 }
2431
bf7c1719
KS
2432 rt->client_report_size = 1048576;
2433 rt->bytes_read = 0;
2434 rt->last_bytes_read = 0;
c2d38bea 2435 rt->server_bw = 2500000;
bf7c1719 2436
7f804085 2437 av_log(s, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s\n",
c7240611 2438 proto, path, rt->app, rt->playpath);
e5f2731c
JO
2439 if (!rt->listen) {
2440 if ((ret = gen_connect(s, rt)) < 0)
2441 goto fail;
2442 } else {
2443 if (read_connect(s, s->priv_data) < 0)
2444 goto fail;
2445 rt->is_input = 1;
2446 }
9fd6b843 2447
c7240611
KS
2448 do {
2449 ret = get_packet(s, 1);
2450 } while (ret == EAGAIN);
2451 if (ret < 0)
2452 goto fail;
6bf22e18 2453
08225d01
MS
2454 if (rt->do_reconnect) {
2455 ffurl_close(rt->stream);
2456 rt->stream = NULL;
2457 rt->do_reconnect = 0;
2458 rt->nb_invokes = 0;
2459 memset(rt->prev_pkt, 0, sizeof(rt->prev_pkt));
2460 free_tracked_methods(rt);
2461 goto reconnect;
2462 }
2463
6bf22e18 2464 if (rt->is_input) {
9fd6b843
KS
2465 // generate FLV header for demuxer
2466 rt->flv_size = 13;
2467 rt->flv_data = av_realloc(rt->flv_data, rt->flv_size);
2468 rt->flv_off = 0;
2469 memcpy(rt->flv_data, "FLV\1\5\0\0\0\011\0\0\0\0", rt->flv_size);
6bf22e18
S
2470 } else {
2471 rt->flv_size = 0;
2472 rt->flv_data = NULL;
2473 rt->flv_off = 0;
b14629e5 2474 rt->skip_bytes = 13;
9fd6b843
KS
2475 }
2476
5958df34 2477 s->max_packet_size = rt->stream->max_packet_size;
9fd6b843
KS
2478 s->is_streamed = 1;
2479 return 0;
2480
2481fail:
86991ce2 2482 av_dict_free(&opts);
9fd6b843 2483 rtmp_close(s);
f645f1d6 2484 return ret;
9fd6b843
KS
2485}
2486
2487static int rtmp_read(URLContext *s, uint8_t *buf, int size)
2488{
2489 RTMPContext *rt = s->priv_data;
2490 int orig_size = size;
2491 int ret;
2492
2493 while (size > 0) {
2494 int data_left = rt->flv_size - rt->flv_off;
2495
2496 if (data_left >= size) {
2497 memcpy(buf, rt->flv_data + rt->flv_off, size);
2498 rt->flv_off += size;
2499 return orig_size;
2500 }
2501 if (data_left > 0) {
2502 memcpy(buf, rt->flv_data + rt->flv_off, data_left);
2503 buf += data_left;
2504 size -= data_left;
2505 rt->flv_off = rt->flv_size;
e8ccf245 2506 return data_left;
9fd6b843
KS
2507 }
2508 if ((ret = get_packet(s, 0)) < 0)
2509 return ret;
2510 }
2511 return orig_size;
2512}
2513
9ad4c65f 2514static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
9fd6b843 2515{
9ad4c65f 2516 RTMPContext *rt = s->priv_data;
6bf22e18
S
2517 int size_temp = size;
2518 int pktsize, pkttype;
2519 uint32_t ts;
2520 const uint8_t *buf_temp = buf;
7dc747f5 2521 uint8_t c;
f645f1d6 2522 int ret;
6bf22e18 2523
6bf22e18 2524 do {
b14629e5
MS
2525 if (rt->skip_bytes) {
2526 int skip = FFMIN(rt->skip_bytes, size_temp);
2527 buf_temp += skip;
2528 size_temp -= skip;
2529 rt->skip_bytes -= skip;
2530 continue;
2531 }
2532
2533 if (rt->flv_header_bytes < 11) {
2534 const uint8_t *header = rt->flv_header;
2535 int copy = FFMIN(11 - rt->flv_header_bytes, size_temp);
2536 bytestream_get_buffer(&buf_temp, rt->flv_header + rt->flv_header_bytes, copy);
2537 rt->flv_header_bytes += copy;
2538 size_temp -= copy;
2539 if (rt->flv_header_bytes < 11)
2540 break;
6bf22e18 2541
b14629e5
MS
2542 pkttype = bytestream_get_byte(&header);
2543 pktsize = bytestream_get_be24(&header);
2544 ts = bytestream_get_be24(&header);
2545 ts |= bytestream_get_byte(&header) << 24;
2546 bytestream_get_be24(&header);
6bf22e18
S
2547 rt->flv_size = pktsize;
2548
2549 //force 12bytes header
2550 if (((pkttype == RTMP_PT_VIDEO || pkttype == RTMP_PT_AUDIO) && ts == 0) ||
2551 pkttype == RTMP_PT_NOTIFY) {
2552 if (pkttype == RTMP_PT_NOTIFY)
2553 pktsize += 16;
2554 rt->prev_pkt[1][RTMP_SOURCE_CHANNEL].channel_id = 0;
2555 }
2556
2557 //this can be a big packet, it's better to send it right here
f645f1d6
SP
2558 if ((ret = ff_rtmp_packet_create(&rt->out_pkt, RTMP_SOURCE_CHANNEL,
2559 pkttype, ts, pktsize)) < 0)
2560 return ret;
2561
6bf22e18
S
2562 rt->out_pkt.extra = rt->main_channel_id;
2563 rt->flv_data = rt->out_pkt.data;
2564
2565 if (pkttype == RTMP_PT_NOTIFY)
2566 ff_amf_write_string(&rt->flv_data, "@setDataFrame");
2567 }
2568
2569 if (rt->flv_size - rt->flv_off > size_temp) {
2570 bytestream_get_buffer(&buf_temp, rt->flv_data + rt->flv_off, size_temp);
2571 rt->flv_off += size_temp;
a14c7842 2572 size_temp = 0;
6bf22e18
S
2573 } else {
2574 bytestream_get_buffer(&buf_temp, rt->flv_data + rt->flv_off, rt->flv_size - rt->flv_off);
a14c7842 2575 size_temp -= rt->flv_size - rt->flv_off;
6bf22e18
S
2576 rt->flv_off += rt->flv_size - rt->flv_off;
2577 }
2578
2579 if (rt->flv_off == rt->flv_size) {
b14629e5
MS
2580 rt->skip_bytes = 4;
2581
f89584ca 2582 if ((ret = rtmp_send_packet(rt, &rt->out_pkt, 0)) < 0)
bba287fd 2583 return ret;
6bf22e18
S
2584 rt->flv_size = 0;
2585 rt->flv_off = 0;
b14629e5 2586 rt->flv_header_bytes = 0;
46743a85 2587 rt->flv_nb_packets++;
6bf22e18 2588 }
a14c7842 2589 } while (buf_temp - buf < size);
7dc747f5 2590
46743a85
SP
2591 if (rt->flv_nb_packets < rt->flush_interval)
2592 return size;
2593 rt->flv_nb_packets = 0;
2594
7dc747f5
SP
2595 /* set stream into nonblocking mode */
2596 rt->stream->flags |= AVIO_FLAG_NONBLOCK;
2597
2598 /* try to read one byte from the stream */
2599 ret = ffurl_read(rt->stream, &c, 1);
2600
2601 /* switch the stream back into blocking mode */
2602 rt->stream->flags &= ~AVIO_FLAG_NONBLOCK;
2603
2604 if (ret == AVERROR(EAGAIN)) {
2605 /* no incoming data to handle */
2606 return size;
2607 } else if (ret < 0) {
2608 return ret;
2609 } else if (ret == 1) {
2610 RTMPPacket rpkt = { 0 };
2611
2612 if ((ret = ff_rtmp_packet_read_internal(rt->stream, &rpkt,
f5ce90f2 2613 rt->in_chunk_size,
7dc747f5
SP
2614 rt->prev_pkt[0], c)) <= 0)
2615 return ret;
2616
2617 if ((ret = rtmp_parse_result(s, rt, &rpkt)) < 0)
2618 return ret;
2619
2620 ff_rtmp_packet_destroy(&rpkt);
2621 }
2622
6bf22e18 2623 return size;
9fd6b843
KS
2624}
2625
6465562e
SP
2626#define OFFSET(x) offsetof(RTMPContext, x)
2627#define DEC AV_OPT_FLAG_DECODING_PARAM
2628#define ENC AV_OPT_FLAG_ENCODING_PARAM
2629
2630static const AVOption rtmp_options[] = {
2631 {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
e6153f17 2632 {"rtmp_buffer", "Set buffer time in milliseconds. The default is 3000.", OFFSET(client_buffer_time), AV_OPT_TYPE_INT, {.i64 = 3000}, 0, INT_MAX, DEC|ENC},
8ee3e187 2633 {"rtmp_conn", "Append arbitrary AMF data to the Connect message", OFFSET(conn), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
e64673e4 2634 {"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},
e6153f17
MS
2635 {"rtmp_flush_interval", "Number of packets flushed in the same request (RTMPT only).", OFFSET(flush_interval), AV_OPT_TYPE_INT, {.i64 = 10}, 0, INT_MAX, ENC},
2636 {"rtmp_live", "Specify that the media is a live stream.", OFFSET(live), AV_OPT_TYPE_INT, {.i64 = -2}, INT_MIN, INT_MAX, DEC, "rtmp_live"},
124134e4
MS
2637 {"any", "both", 0, AV_OPT_TYPE_CONST, {.i64 = -2}, 0, 0, DEC, "rtmp_live"},
2638 {"live", "live stream", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, DEC, "rtmp_live"},
2639 {"recorded", "recorded stream", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, DEC, "rtmp_live"},
758377a2 2640 {"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 2641 {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
00cb52c6 2642 {"rtmp_subscribe", "Name of live stream to subscribe to. Defaults to rtmp_playpath.", OFFSET(subscribe), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
635ac8e1 2643 {"rtmp_swfhash", "SHA256 hash of the decompressed SWF file (32 bytes).", OFFSET(swfhash), AV_OPT_TYPE_BINARY, .flags = DEC},
e6153f17 2644 {"rtmp_swfsize", "Size of the decompressed SWF file, required for SWFVerification.", OFFSET(swfsize), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC},
05945db9 2645 {"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},
93f257db 2646 {"rtmp_swfverify", "URL to player swf file, compute hash/size automatically.", OFFSET(swfverify), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
63ffa154 2647 {"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},
e6153f17
MS
2648 {"rtmp_listen", "Listen for incoming rtmp connections", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC, "rtmp_listen" },
2649 {"timeout", "Maximum timeout (in seconds) to wait for incoming connections. -1 is infinite. Implies -rtmp_listen 1", OFFSET(listen_timeout), AV_OPT_TYPE_INT, {.i64 = -1}, INT_MIN, INT_MAX, DEC, "rtmp_listen" },
6465562e
SP
2650 { NULL },
2651};
2652
12127b65
SP
2653#define RTMP_PROTOCOL(flavor) \
2654static const AVClass flavor##_class = { \
2655 .class_name = #flavor, \
2656 .item_name = av_default_item_name, \
2657 .option = rtmp_options, \
2658 .version = LIBAVUTIL_VERSION_INT, \
2659}; \
2660 \
2661URLProtocol ff_##flavor##_protocol = { \
2662 .name = #flavor, \
2663 .url_open = rtmp_open, \
2664 .url_read = rtmp_read, \
2665 .url_write = rtmp_write, \
2666 .url_close = rtmp_close, \
2667 .priv_data_size = sizeof(RTMPContext), \
2668 .flags = URL_PROTOCOL_FLAG_NETWORK, \
2669 .priv_data_class= &flavor##_class, \
6465562e
SP
2670};
2671
08cd95e8 2672
12127b65
SP
2673RTMP_PROTOCOL(rtmp)
2674RTMP_PROTOCOL(rtmpe)
2675RTMP_PROTOCOL(rtmps)
2676RTMP_PROTOCOL(rtmpt)
2677RTMP_PROTOCOL(rtmpte)
2678RTMP_PROTOCOL(rtmpts)