FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavformat/rtmpproto.c
Date: 2026-09-26 14:26:21
Exec Total Coverage
Lines: 0 1630 0.0%
Functions: 0 63 0.0%
Branches: 0 910 0.0%

Line Branch Exec Source
1 /*
2 * RTMP network protocol
3 * Copyright (c) 2009 Konstantin Shishkov
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
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 *
12 * FFmpeg is distributed in the hope that it will be useful,
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
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * @file
24 * RTMP protocol
25 */
26
27 #include "config_components.h"
28
29 #include "libavcodec/bytestream.h"
30 #include "libavutil/avstring.h"
31 #include "libavutil/base64.h"
32 #include "libavutil/intfloat.h"
33 #include "libavutil/lfg.h"
34 #include "libavutil/md5.h"
35 #include "libavutil/mem.h"
36 #include "libavutil/opt.h"
37 #include "libavutil/random_seed.h"
38 #include "avformat.h"
39 #include "internal.h"
40
41 #include "network.h"
42
43 #include "flv.h"
44 #include "rtmp.h"
45 #include "rtmpcrypt.h"
46 #include "rtmppkt.h"
47 #include "url.h"
48 #include "version.h"
49
50 #if CONFIG_ZLIB
51 #include <zlib.h>
52 #endif
53
54 #define APP_MAX_LENGTH 1024
55 #define TCURL_MAX_LENGTH 1024
56 #define FLASHVER_MAX_LENGTH 64
57 #define RTMP_PKTDATA_DEFAULT_SIZE 4096
58 #define RTMP_HEADER 11
59
60 /** RTMP protocol handler state */
61 typedef enum {
62 STATE_START, ///< client has not done anything yet
63 STATE_HANDSHAKED, ///< client has performed handshake
64 STATE_FCPUBLISH, ///< client FCPublishing stream (for output)
65 STATE_PLAYING, ///< client has started receiving multimedia data from server
66 STATE_SEEKING, ///< client has started the seek operation. Back on STATE_PLAYING when the time comes
67 STATE_PUBLISHING, ///< client has started sending multimedia data to server (for output)
68 STATE_RECEIVING, ///< received a publish command (for input)
69 STATE_SENDING, ///< received a play command (for output)
70 STATE_STOPPED, ///< the broadcast has been stopped
71 } ClientState;
72
73 typedef struct TrackedMethod {
74 char *name;
75 int id;
76 } TrackedMethod;
77
78 /** protocol handler context */
79 typedef struct RTMPContext {
80 const AVClass *class;
81 URLContext* stream; ///< TCP stream used in interactions with RTMP server
82 RTMPPacket *prev_pkt[2]; ///< packet history used when reading and sending packets ([0] for reading, [1] for writing)
83 int nb_prev_pkt[2]; ///< number of elements in prev_pkt
84 int in_chunk_size; ///< size of the chunks incoming RTMP packets are divided into
85 int out_chunk_size; ///< size of the chunks outgoing RTMP packets are divided into
86 int is_input; ///< input/output flag
87 char *playpath; ///< stream identifier to play (with possible "mp4:" prefix)
88 int live; ///< 0: recorded, -1: live, -2: both
89 char *app; ///< name of application
90 char *conn; ///< append arbitrary AMF data to the Connect message
91 ClientState state; ///< current state
92 int stream_id; ///< ID assigned by the server for the stream
93 uint8_t* flv_data; ///< buffer with data for demuxer
94 int flv_size; ///< current buffer size
95 int flv_off; ///< number of bytes read from current buffer
96 int flv_nb_packets; ///< number of flv packets published
97 RTMPPacket out_pkt; ///< rtmp packet, created from flv a/v or metadata (for output)
98 uint32_t receive_report_size; ///< number of bytes after which we should report the number of received bytes to the peer
99 uint64_t bytes_read; ///< number of bytes read from server
100 uint64_t last_bytes_read; ///< number of bytes read last reported to server
101 uint32_t last_timestamp; ///< last timestamp received in a packet
102 int skip_bytes; ///< number of bytes to skip from the input FLV stream in the next write call
103 int has_audio; ///< presence of audio data
104 int has_video; ///< presence of video data
105 int received_metadata; ///< Indicates if we have received metadata about the streams
106 uint8_t flv_header[RTMP_HEADER]; ///< partial incoming flv packet header
107 int flv_header_bytes; ///< number of initialized bytes in flv_header
108 int nb_invokes; ///< keeps track of invoke messages
109 char* tcurl; ///< url of the target stream
110 char* flashver; ///< version of the flash plugin
111 char* swfhash; ///< SHA256 hash of the decompressed SWF file (32 bytes)
112 int swfhash_len; ///< length of the SHA256 hash
113 int swfsize; ///< size of the decompressed SWF file
114 char* swfurl; ///< url of the swf player
115 char* swfverify; ///< URL to player swf file, compute hash/size automatically
116 char swfverification[42]; ///< hash of the SWF verification
117 char* pageurl; ///< url of the web page
118 char* subscribe; ///< name of live stream to subscribe
119 int max_sent_unacked; ///< max unacked sent bytes
120 int client_buffer_time; ///< client buffer time in ms
121 int flush_interval; ///< number of packets flushed in the same request (RTMPT only)
122 int encrypted; ///< use an encrypted connection (RTMPE only)
123 TrackedMethod*tracked_methods; ///< tracked methods buffer
124 int nb_tracked_methods; ///< number of tracked methods
125 int tracked_methods_size; ///< size of the tracked methods buffer
126 int listen; ///< listen mode flag
127 int listen_timeout; ///< listen timeout to wait for new connections
128 int nb_streamid; ///< The next stream id to return on createStream calls
129 double duration; ///< Duration of the stream in seconds as returned by the server (only valid if non-zero)
130 int tcp_nodelay; ///< Use TCP_NODELAY to disable Nagle's algorithm if set to 1
131 char *enhanced_codecs; ///< codec list in enhanced rtmp
132 char username[50];
133 char password[50];
134 char auth_params[500];
135 int do_reconnect;
136 int auth_tried;
137 } RTMPContext;
138
139 #define PLAYER_KEY_OPEN_PART_LEN 30 ///< length of partial key used for first client digest signing
140 /** Client key used for digest signing */
141 static const uint8_t rtmp_player_key[] = {
142 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
143 'F', 'l', 'a', 's', 'h', ' ', 'P', 'l', 'a', 'y', 'e', 'r', ' ', '0', '0', '1',
144
145 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
146 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
147 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
148 };
149
150 #define SERVER_KEY_OPEN_PART_LEN 36 ///< length of partial key used for first server digest signing
151 /** Key used for RTMP server digest signing */
152 static const uint8_t rtmp_server_key[] = {
153 'G', 'e', 'n', 'u', 'i', 'n', 'e', ' ', 'A', 'd', 'o', 'b', 'e', ' ',
154 'F', 'l', 'a', 's', 'h', ' ', 'M', 'e', 'd', 'i', 'a', ' ',
155 'S', 'e', 'r', 'v', 'e', 'r', ' ', '0', '0', '1',
156
157 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, 0x2E, 0x00, 0xD0, 0xD1, 0x02,
158 0x9E, 0x7E, 0x57, 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, 0x93, 0xB8,
159 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
160 };
161
162 static int handle_chunk_size(URLContext *s, RTMPPacket *pkt);
163 static int handle_window_ack_size(URLContext *s, RTMPPacket *pkt);
164 static int handle_set_peer_bw(URLContext *s, RTMPPacket *pkt);
165
166 ✗ static size_t zstrlen(const char *c)
167 {
168 ✗ if(c)
169 ✗ return strlen(c);
170 ✗ return 0;
171 }
172
173 ✗ static int add_tracked_method(RTMPContext *rt, const char *name, int id)
174 {
175 int err;
176
177 ✗ if (rt->nb_tracked_methods + 1 > rt->tracked_methods_size) {
178 ✗ rt->tracked_methods_size = (rt->nb_tracked_methods + 1) * 2;
179 ✗ if ((err = av_reallocp_array(&rt->tracked_methods, rt->tracked_methods_size,
180 sizeof(*rt->tracked_methods))) < 0) {
181 ✗ rt->nb_tracked_methods = 0;
182 ✗ rt->tracked_methods_size = 0;
183 ✗ return err;
184 }
185 }
186
187 ✗ rt->tracked_methods[rt->nb_tracked_methods].name = av_strdup(name);
188 ✗ if (!rt->tracked_methods[rt->nb_tracked_methods].name)
189 ✗ return AVERROR(ENOMEM);
190 ✗ rt->tracked_methods[rt->nb_tracked_methods].id = id;
191 ✗ rt->nb_tracked_methods++;
192
193 ✗ return 0;
194 }
195
196 ✗ static void del_tracked_method(RTMPContext *rt, int index)
197 {
198 ✗ memmove(&rt->tracked_methods[index], &rt->tracked_methods[index + 1],
199 ✗ sizeof(*rt->tracked_methods) * (rt->nb_tracked_methods - index - 1));
200 ✗ rt->nb_tracked_methods--;
201 ✗ }
202
203 ✗ static int find_tracked_method(URLContext *s, RTMPPacket *pkt, int offset,
204 char **tracked_method)
205 {
206 ✗ RTMPContext *rt = s->priv_data;
207 GetByteContext gbc;
208 double pkt_id;
209 int ret;
210 int i;
211
212 ✗ bytestream2_init(&gbc, pkt->data + offset, pkt->size - offset);
213 ✗ if ((ret = ff_amf_read_number(&gbc, &pkt_id)) < 0)
214 ✗ return ret;
215
216 ✗ for (i = 0; i < rt->nb_tracked_methods; i++) {
217 ✗ if (rt->tracked_methods[i].id != pkt_id)
218 ✗ continue;
219
220 ✗ *tracked_method = rt->tracked_methods[i].name;
221 ✗ del_tracked_method(rt, i);
222 ✗ break;
223 }
224
225 ✗ return 0;
226 }
227
228 ✗ static void free_tracked_methods(RTMPContext *rt)
229 {
230 int i;
231
232 ✗ for (i = 0; i < rt->nb_tracked_methods; i ++)
233 ✗ av_freep(&rt->tracked_methods[i].name);
234 ✗ av_freep(&rt->tracked_methods);
235 ✗ rt->tracked_methods_size = 0;
236 ✗ rt->nb_tracked_methods = 0;
237 ✗ }
238
239 ✗ static int rtmp_send_packet(RTMPContext *rt, RTMPPacket *pkt, int track)
240 {
241 int ret;
242
243 ✗ if (pkt->type == RTMP_PT_INVOKE && track) {
244 GetByteContext gbc;
245 char name[128];
246 double pkt_id;
247 int len;
248
249 ✗ bytestream2_init(&gbc, pkt->data, pkt->size);
250 ✗ if ((ret = ff_amf_read_string(&gbc, name, sizeof(name), &len)) < 0)
251 ✗ goto fail;
252
253 ✗ if ((ret = ff_amf_read_number(&gbc, &pkt_id)) < 0)
254 ✗ goto fail;
255
256 ✗ if ((ret = add_tracked_method(rt, name, pkt_id)) < 0)
257 ✗ goto fail;
258 }
259
260 ✗ ret = ff_rtmp_packet_write(rt->stream, pkt, rt->out_chunk_size,
261 &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
262 ✗ fail:
263 ✗ ff_rtmp_packet_destroy(pkt);
264 ✗ return ret;
265 }
266
267 ✗ static int rtmp_write_amf_data(URLContext *s, char *param, uint8_t **p)
268 {
269 char *field, *value;
270 char type;
271
272 /* The type must be B for Boolean, N for number, S for string, O for
273 * object, or Z for null. For Booleans the data must be either 0 or 1 for
274 * FALSE or TRUE, respectively. Likewise for Objects the data must be
275 * 0 or 1 to end or begin an object, respectively. Data items in subobjects
276 * may be named, by prefixing the type with 'N' and specifying the name
277 * before the value (ie. NB:myFlag:1). This option may be used multiple times
278 * to construct arbitrary AMF sequences. */
279 ✗ if (param[0] && param[1] == ':') {
280 ✗ type = param[0];
281 ✗ value = param + 2;
282 ✗ } else if (param[0] == 'N' && param[1] && param[2] == ':') {
283 ✗ type = param[1];
284 ✗ field = param + 3;
285 ✗ value = strchr(field, ':');
286 ✗ if (!value)
287 ✗ goto fail;
288 ✗ *value = '\0';
289 ✗ value++;
290
291 ✗ ff_amf_write_field_name(p, field);
292 } else {
293 ✗ goto fail;
294 }
295
296 ✗ switch (type) {
297 ✗ case 'B':
298 ✗ ff_amf_write_bool(p, value[0] != '0');
299 ✗ break;
300 ✗ case 'S':
301 ✗ ff_amf_write_string(p, value);
302 ✗ break;
303 ✗ case 'N':
304 ✗ ff_amf_write_number(p, strtod(value, NULL));
305 ✗ break;
306 ✗ case 'Z':
307 ✗ ff_amf_write_null(p);
308 ✗ break;
309 ✗ case 'O':
310 ✗ if (value[0] != '0')
311 ✗ ff_amf_write_object_start(p);
312 else
313 ✗ ff_amf_write_object_end(p);
314 ✗ break;
315 ✗ default:
316 ✗ goto fail;
317 break;
318 }
319
320 ✗ return 0;
321
322 ✗ fail:
323 ✗ av_log(s, AV_LOG_ERROR, "Invalid AMF parameter: %s\n", param);
324 ✗ return AVERROR(EINVAL);
325 }
326
327 /**
328 * Generate 'connect' call and send it to the server.
329 */
330 ✗ static int gen_connect(URLContext *s, RTMPContext *rt)
331 {
332 RTMPPacket pkt;
333 uint8_t *p;
334 int ret;
335
336 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
337 0, 4096 + APP_MAX_LENGTH
338 ✗ + strlen(rt->auth_params) + strlen(rt->flashver)
339 ✗ + zstrlen(rt->enhanced_codecs)/5*7
340 ✗ + zstrlen(rt->swfurl)
341 ✗ + zstrlen(rt->swfverify)
342 ✗ + zstrlen(rt->tcurl)
343 ✗ + zstrlen(rt->auth_params)
344 ✗ + zstrlen(rt->pageurl)
345 ✗ + zstrlen(rt->conn)*3
346 )) < 0)
347 ✗ return ret;
348
349 ✗ p = pkt.data;
350
351 ✗ ff_amf_write_string(&p, "connect");
352 ✗ ff_amf_write_number(&p, ++rt->nb_invokes);
353 ✗ ff_amf_write_object_start(&p);
354 ✗ ff_amf_write_field_name(&p, "app");
355 ✗ ff_amf_write_string2(&p, rt->app, rt->auth_params);
356
357 ✗ if (rt->enhanced_codecs) {
358 ✗ uint32_t list_len = 0;
359 ✗ char *fourcc_data = rt->enhanced_codecs;
360 ✗ int fourcc_str_len = strlen(fourcc_data);
361
362 // check the string, fourcc + ',' + ... + end fourcc correct length should be (4+1)*n+4
363 ✗ if ((fourcc_str_len + 1) % 5 != 0) {
364 ✗ av_log(s, AV_LOG_ERROR, "Malformed rtmp_enhanched_codecs, "
365 "should be of the form hvc1[,av01][,vp09][,vvc1][,...]\n");
366 ✗ ff_rtmp_packet_destroy(&pkt);
367 ✗ return AVERROR(EINVAL);
368 }
369
370 ✗ list_len = (fourcc_str_len + 1) / 5;
371 ✗ ff_amf_write_field_name(&p, "fourCcList");
372 ✗ ff_amf_write_array_start(&p, list_len);
373
374 ✗ while(fourcc_data - rt->enhanced_codecs < fourcc_str_len) {
375 unsigned char fourcc[5];
376 ✗ if (!strncmp(fourcc_data, "ac-3", 4) ||
377 ✗ !strncmp(fourcc_data, "av01", 4) ||
378 ✗ !strncmp(fourcc_data, "avc1", 4) ||
379 ✗ !strncmp(fourcc_data, "ec-3", 4) ||
380 ✗ !strncmp(fourcc_data, "fLaC", 4) ||
381 ✗ !strncmp(fourcc_data, "hvc1", 4) ||
382 ✗ !strncmp(fourcc_data, "vvc1", 4) ||
383 ✗ !strncmp(fourcc_data, ".mp3", 4) ||
384 ✗ !strncmp(fourcc_data, "mp4a", 4) ||
385 ✗ !strncmp(fourcc_data, "Opus", 4) ||
386 ✗ !strncmp(fourcc_data, "vp09", 4)) {
387 ✗ av_strlcpy(fourcc, fourcc_data, sizeof(fourcc));
388 ✗ ff_amf_write_string(&p, fourcc);
389 } else {
390 ✗ av_log(s, AV_LOG_ERROR, "Unsupported codec fourcc, %.*s\n", 4, fourcc_data);
391 ✗ ff_rtmp_packet_destroy(&pkt);
392 ✗ return AVERROR_PATCHWELCOME;
393 }
394
395 ✗ fourcc_data += 5;
396 }
397 }
398
399 ✗ if (!rt->is_input) {
400 ✗ ff_amf_write_field_name(&p, "type");
401 ✗ ff_amf_write_string(&p, "nonprivate");
402 }
403 ✗ ff_amf_write_field_name(&p, "flashVer");
404 ✗ ff_amf_write_string(&p, rt->flashver);
405
406 ✗ if (rt->swfurl || rt->swfverify) {
407 ✗ ff_amf_write_field_name(&p, "swfUrl");
408 ✗ if (rt->swfurl)
409 ✗ ff_amf_write_string(&p, rt->swfurl);
410 else
411 ✗ ff_amf_write_string(&p, rt->swfverify);
412 }
413
414 ✗ ff_amf_write_field_name(&p, "tcUrl");
415 ✗ ff_amf_write_string2(&p, rt->tcurl, rt->auth_params);
416 ✗ if (rt->is_input) {
417 ✗ ff_amf_write_field_name(&p, "fpad");
418 ✗ ff_amf_write_bool(&p, 0);
419 ✗ ff_amf_write_field_name(&p, "capabilities");
420 ✗ ff_amf_write_number(&p, 15.0);
421
422 /* Tell the server we support all the audio codecs except
423 * SUPPORT_SND_INTEL (0x0008) and SUPPORT_SND_UNUSED (0x0010)
424 * which are unused in the RTMP protocol implementation. */
425 ✗ ff_amf_write_field_name(&p, "audioCodecs");
426 ✗ ff_amf_write_number(&p, 4071.0);
427 ✗ ff_amf_write_field_name(&p, "videoCodecs");
428 ✗ ff_amf_write_number(&p, 252.0);
429 ✗ ff_amf_write_field_name(&p, "videoFunction");
430 ✗ ff_amf_write_number(&p, 1.0);
431
432 ✗ if (rt->pageurl) {
433 ✗ ff_amf_write_field_name(&p, "pageUrl");
434 ✗ ff_amf_write_string(&p, rt->pageurl);
435 }
436 }
437 ✗ ff_amf_write_object_end(&p);
438
439 ✗ if (rt->conn) {
440 ✗ char *param = rt->conn;
441
442 // Write arbitrary AMF data to the Connect message.
443 ✗ while (param) {
444 char *sep;
445 ✗ param += strspn(param, " ");
446 ✗ if (!*param)
447 ✗ break;
448 ✗ sep = strchr(param, ' ');
449 ✗ if (sep)
450 ✗ *sep = '\0';
451 ✗ if ((ret = rtmp_write_amf_data(s, param, &p)) < 0) {
452 // Invalid AMF parameter.
453 ✗ ff_rtmp_packet_destroy(&pkt);
454 ✗ return ret;
455 }
456
457 ✗ if (sep)
458 ✗ param = sep + 1;
459 else
460 ✗ break;
461 }
462 }
463
464 ✗ pkt.size = p - pkt.data;
465
466 ✗ return rtmp_send_packet(rt, &pkt, 1);
467 }
468
469
470 #define RTMP_CTRL_ABORT_MESSAGE (2)
471
472 ✗ static int read_connect(URLContext *s, RTMPContext *rt)
473 {
474 ✗ RTMPPacket pkt = { 0 };
475 uint8_t *p;
476 const uint8_t *cp;
477 int ret;
478 char command[64];
479 int stringlen;
480 double seqnum;
481 uint8_t tmpstr[256];
482 GetByteContext gbc;
483
484 // handle RTMP Protocol Control Messages
485 for (;;) {
486 ✗ if ((ret = ff_rtmp_packet_read(rt->stream, &pkt, rt->in_chunk_size,
487 &rt->prev_pkt[0], &rt->nb_prev_pkt[0])) < 0)
488 ✗ return ret;
489 #ifdef DEBUG
490 ff_rtmp_packet_dump(s, &pkt);
491 #endif
492 ✗ if (pkt.type == RTMP_PT_CHUNK_SIZE) {
493 ✗ if ((ret = handle_chunk_size(s, &pkt)) < 0) {
494 ✗ ff_rtmp_packet_destroy(&pkt);
495 ✗ return ret;
496 }
497 ✗ } else if (pkt.type == RTMP_CTRL_ABORT_MESSAGE) {
498 ✗ av_log(s, AV_LOG_ERROR, "received abort message\n");
499 ✗ ff_rtmp_packet_destroy(&pkt);
500 ✗ return AVERROR_UNKNOWN;
501 ✗ } else if (pkt.type == RTMP_PT_BYTES_READ) {
502 ✗ av_log(s, AV_LOG_TRACE, "received acknowledgement\n");
503 ✗ } else if (pkt.type == RTMP_PT_WINDOW_ACK_SIZE) {
504 ✗ if ((ret = handle_window_ack_size(s, &pkt)) < 0) {
505 ✗ ff_rtmp_packet_destroy(&pkt);
506 ✗ return ret;
507 }
508 ✗ } else if (pkt.type == RTMP_PT_SET_PEER_BW) {
509 ✗ if ((ret = handle_set_peer_bw(s, &pkt)) < 0) {
510 ✗ ff_rtmp_packet_destroy(&pkt);
511 ✗ return ret;
512 }
513 ✗ } else if (pkt.type == RTMP_PT_INVOKE) {
514 // received RTMP Command Message
515 ✗ break;
516 } else {
517 ✗ av_log(s, AV_LOG_ERROR, "Unknown control message type (%d)\n", pkt.type);
518 }
519 ✗ ff_rtmp_packet_destroy(&pkt);
520 }
521
522 ✗ cp = pkt.data;
523 ✗ bytestream2_init(&gbc, cp, pkt.size);
524 ✗ if (ff_amf_read_string(&gbc, command, sizeof(command), &stringlen)) {
525 ✗ av_log(s, AV_LOG_ERROR, "Unable to read command string\n");
526 ✗ ff_rtmp_packet_destroy(&pkt);
527 ✗ return AVERROR_INVALIDDATA;
528 }
529 ✗ if (strcmp(command, "connect")) {
530 ✗ av_log(s, AV_LOG_ERROR, "Expecting connect, got %s\n", command);
531 ✗ ff_rtmp_packet_destroy(&pkt);
532 ✗ return AVERROR_INVALIDDATA;
533 }
534 ✗ ret = ff_amf_read_number(&gbc, &seqnum);
535 ✗ if (ret)
536 ✗ av_log(s, AV_LOG_WARNING, "SeqNum not found\n");
537 /* Here one could parse an AMF Object with data as flashVers and others. */
538 ✗ ret = ff_amf_get_field_value(gbc.buffer,
539 ✗ gbc.buffer + bytestream2_get_bytes_left(&gbc),
540 "app", tmpstr, sizeof(tmpstr));
541 ✗ if (ret)
542 ✗ av_log(s, AV_LOG_WARNING, "App field not found in connect\n");
543 ✗ if (!ret && strcmp(tmpstr, rt->app))
544 ✗ av_log(s, AV_LOG_WARNING, "App field don't match up: %s <-> %s\n",
545 tmpstr, rt->app);
546 ✗ ff_rtmp_packet_destroy(&pkt);
547
548 // Send Window Acknowledgement Size (as defined in specification)
549 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
550 RTMP_PT_WINDOW_ACK_SIZE, 0, 4)) < 0)
551 ✗ return ret;
552 ✗ p = pkt.data;
553 // Inform the peer about how often we want acknowledgements about what
554 // we send. (We don't check for the acknowledgements currently.)
555 ✗ bytestream_put_be32(&p, rt->max_sent_unacked);
556 ✗ pkt.size = p - pkt.data;
557 ✗ ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
558 &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
559 ✗ ff_rtmp_packet_destroy(&pkt);
560 ✗ if (ret < 0)
561 ✗ return ret;
562 // Set Peer Bandwidth
563 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
564 RTMP_PT_SET_PEER_BW, 0, 5)) < 0)
565 ✗ return ret;
566 ✗ p = pkt.data;
567 // Tell the peer to only send this many bytes unless it gets acknowledgements.
568 // This could be any arbitrary value we want here.
569 ✗ bytestream_put_be32(&p, rt->max_sent_unacked);
570 ✗ bytestream_put_byte(&p, 2); // dynamic
571 ✗ pkt.size = p - pkt.data;
572 ✗ ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
573 &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
574 ✗ ff_rtmp_packet_destroy(&pkt);
575 ✗ if (ret < 0)
576 ✗ return ret;
577
578 // User control
579 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
580 RTMP_PT_USER_CONTROL, 0, 6)) < 0)
581 ✗ return ret;
582
583 ✗ p = pkt.data;
584 ✗ bytestream_put_be16(&p, 0); // 0 -> Stream Begin
585 ✗ bytestream_put_be32(&p, 0); // Stream 0
586 ✗ ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
587 &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
588 ✗ ff_rtmp_packet_destroy(&pkt);
589 ✗ if (ret < 0)
590 ✗ return ret;
591
592 // Chunk size
593 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,
594 RTMP_PT_CHUNK_SIZE, 0, 4)) < 0)
595 ✗ return ret;
596
597 ✗ p = pkt.data;
598 ✗ bytestream_put_be32(&p, rt->out_chunk_size);
599 ✗ ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
600 &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
601 ✗ ff_rtmp_packet_destroy(&pkt);
602 ✗ if (ret < 0)
603 ✗ return ret;
604
605 // Send _result NetConnection.Connect.Success to connect
606 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL,
607 RTMP_PT_INVOKE, 0,
608 RTMP_PKTDATA_DEFAULT_SIZE)) < 0)
609 ✗ return ret;
610
611 ✗ p = pkt.data;
612 ✗ ff_amf_write_string(&p, "_result");
613 ✗ ff_amf_write_number(&p, seqnum);
614
615 ✗ ff_amf_write_object_start(&p);
616 ✗ ff_amf_write_field_name(&p, "fmsVer");
617 ✗ ff_amf_write_string(&p, "FMS/3,0,1,123");
618 ✗ ff_amf_write_field_name(&p, "capabilities");
619 ✗ ff_amf_write_number(&p, 31);
620 ✗ ff_amf_write_object_end(&p);
621
622 ✗ ff_amf_write_object_start(&p);
623 ✗ ff_amf_write_field_name(&p, "level");
624 ✗ ff_amf_write_string(&p, "status");
625 ✗ ff_amf_write_field_name(&p, "code");
626 ✗ ff_amf_write_string(&p, "NetConnection.Connect.Success");
627 ✗ ff_amf_write_field_name(&p, "description");
628 ✗ ff_amf_write_string(&p, "Connection succeeded.");
629 ✗ ff_amf_write_field_name(&p, "objectEncoding");
630 ✗ ff_amf_write_number(&p, 0);
631 ✗ ff_amf_write_object_end(&p);
632
633 ✗ pkt.size = p - pkt.data;
634 ✗ ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
635 &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
636 ✗ ff_rtmp_packet_destroy(&pkt);
637 ✗ if (ret < 0)
638 ✗ return ret;
639
640 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL,
641 RTMP_PT_INVOKE, 0, 30)) < 0)
642 ✗ return ret;
643 ✗ p = pkt.data;
644 ✗ ff_amf_write_string(&p, "onBWDone");
645 ✗ ff_amf_write_number(&p, 0);
646 ✗ ff_amf_write_null(&p);
647 ✗ ff_amf_write_number(&p, 8192);
648 ✗ pkt.size = p - pkt.data;
649 ✗ ret = ff_rtmp_packet_write(rt->stream, &pkt, rt->out_chunk_size,
650 &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
651 ✗ ff_rtmp_packet_destroy(&pkt);
652
653 ✗ return ret;
654 }
655
656 /**
657 * Generate 'releaseStream' call and send it to the server. It should make
658 * the server release some channel for media streams.
659 */
660 ✗ static int gen_release_stream(URLContext *s, RTMPContext *rt)
661 {
662 RTMPPacket pkt;
663 uint8_t *p;
664 int ret;
665
666 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
667 ✗ 0, 29 + strlen(rt->playpath))) < 0)
668 ✗ return ret;
669
670 ✗ av_log(s, AV_LOG_DEBUG, "Releasing stream...\n");
671 ✗ p = pkt.data;
672 ✗ ff_amf_write_string(&p, "releaseStream");
673 ✗ ff_amf_write_number(&p, ++rt->nb_invokes);
674 ✗ ff_amf_write_null(&p);
675 ✗ ff_amf_write_string(&p, rt->playpath);
676
677 ✗ return rtmp_send_packet(rt, &pkt, 1);
678 }
679
680 /**
681 * Generate 'FCPublish' call and send it to the server. It should make
682 * the server prepare for receiving media streams.
683 */
684 ✗ static int gen_fcpublish_stream(URLContext *s, RTMPContext *rt)
685 {
686 RTMPPacket pkt;
687 uint8_t *p;
688 int ret;
689
690 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
691 ✗ 0, 25 + strlen(rt->playpath))) < 0)
692 ✗ return ret;
693
694 ✗ av_log(s, AV_LOG_DEBUG, "FCPublish stream...\n");
695 ✗ p = pkt.data;
696 ✗ ff_amf_write_string(&p, "FCPublish");
697 ✗ ff_amf_write_number(&p, ++rt->nb_invokes);
698 ✗ ff_amf_write_null(&p);
699 ✗ ff_amf_write_string(&p, rt->playpath);
700
701 ✗ return rtmp_send_packet(rt, &pkt, 1);
702 }
703
704 /**
705 * Generate 'FCUnpublish' call and send it to the server. It should make
706 * the server destroy stream.
707 */
708 ✗ static int gen_fcunpublish_stream(URLContext *s, RTMPContext *rt)
709 {
710 RTMPPacket pkt;
711 uint8_t *p;
712 int ret;
713
714 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
715 ✗ 0, 27 + strlen(rt->playpath))) < 0)
716 ✗ return ret;
717
718 ✗ av_log(s, AV_LOG_DEBUG, "UnPublishing stream...\n");
719 ✗ p = pkt.data;
720 ✗ ff_amf_write_string(&p, "FCUnpublish");
721 ✗ ff_amf_write_number(&p, ++rt->nb_invokes);
722 ✗ ff_amf_write_null(&p);
723 ✗ ff_amf_write_string(&p, rt->playpath);
724
725 ✗ return rtmp_send_packet(rt, &pkt, 0);
726 }
727
728 /**
729 * Generate 'createStream' call and send it to the server. It should make
730 * the server allocate some channel for media streams.
731 */
732 ✗ static int gen_create_stream(URLContext *s, RTMPContext *rt)
733 {
734 RTMPPacket pkt;
735 uint8_t *p;
736 int ret;
737
738 ✗ av_log(s, AV_LOG_DEBUG, "Creating stream...\n");
739
740 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
741 0, 25)) < 0)
742 ✗ return ret;
743
744 ✗ p = pkt.data;
745 ✗ ff_amf_write_string(&p, "createStream");
746 ✗ ff_amf_write_number(&p, ++rt->nb_invokes);
747 ✗ ff_amf_write_null(&p);
748
749 ✗ return rtmp_send_packet(rt, &pkt, 1);
750 }
751
752
753 /**
754 * Generate 'deleteStream' call and send it to the server. It should make
755 * the server remove some channel for media streams.
756 */
757 ✗ static int gen_delete_stream(URLContext *s, RTMPContext *rt)
758 {
759 RTMPPacket pkt;
760 uint8_t *p;
761 int ret;
762
763 ✗ av_log(s, AV_LOG_DEBUG, "Deleting stream...\n");
764
765 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
766 0, 34)) < 0)
767 ✗ return ret;
768
769 ✗ p = pkt.data;
770 ✗ ff_amf_write_string(&p, "deleteStream");
771 ✗ ff_amf_write_number(&p, ++rt->nb_invokes);
772 ✗ ff_amf_write_null(&p);
773 ✗ ff_amf_write_number(&p, rt->stream_id);
774
775 ✗ return rtmp_send_packet(rt, &pkt, 0);
776 }
777
778 /**
779 * Generate 'getStreamLength' call and send it to the server. If the server
780 * knows the duration of the selected stream, it will reply with the duration
781 * in seconds.
782 */
783 ✗ static int gen_get_stream_length(URLContext *s, RTMPContext *rt)
784 {
785 RTMPPacket pkt;
786 uint8_t *p;
787 int ret;
788
789 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SOURCE_CHANNEL, RTMP_PT_INVOKE,
790 ✗ 0, 31 + strlen(rt->playpath))) < 0)
791 ✗ return ret;
792
793 ✗ p = pkt.data;
794 ✗ ff_amf_write_string(&p, "getStreamLength");
795 ✗ ff_amf_write_number(&p, ++rt->nb_invokes);
796 ✗ ff_amf_write_null(&p);
797 ✗ ff_amf_write_string(&p, rt->playpath);
798
799 ✗ return rtmp_send_packet(rt, &pkt, 1);
800 }
801
802 /**
803 * Generate client buffer time and send it to the server.
804 */
805 ✗ static int gen_buffer_time(URLContext *s, RTMPContext *rt)
806 {
807 RTMPPacket pkt;
808 uint8_t *p;
809 int ret;
810
811 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_USER_CONTROL,
812 1, 10)) < 0)
813 ✗ return ret;
814
815 ✗ p = pkt.data;
816 ✗ bytestream_put_be16(&p, 3); // SetBuffer Length
817 ✗ bytestream_put_be32(&p, rt->stream_id);
818 ✗ bytestream_put_be32(&p, rt->client_buffer_time);
819
820 ✗ return rtmp_send_packet(rt, &pkt, 0);
821 }
822
823 /**
824 * Generate 'play' call and send it to the server, then ping the server
825 * to start actual playing.
826 */
827 ✗ static int gen_play(URLContext *s, RTMPContext *rt)
828 {
829 RTMPPacket pkt;
830 uint8_t *p;
831 int ret;
832
833 ✗ av_log(s, AV_LOG_DEBUG, "Sending play command for '%s'\n", rt->playpath);
834
835 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SOURCE_CHANNEL, RTMP_PT_INVOKE,
836 ✗ 0, 29 + strlen(rt->playpath))) < 0)
837 ✗ return ret;
838
839 ✗ pkt.extra = rt->stream_id;
840
841 ✗ p = pkt.data;
842 ✗ ff_amf_write_string(&p, "play");
843 ✗ ff_amf_write_number(&p, ++rt->nb_invokes);
844 ✗ ff_amf_write_null(&p);
845 ✗ ff_amf_write_string(&p, rt->playpath);
846 ✗ ff_amf_write_number(&p, rt->live * 1000);
847
848 ✗ return rtmp_send_packet(rt, &pkt, 1);
849 }
850
851 ✗ static int gen_seek(URLContext *s, RTMPContext *rt, int64_t timestamp)
852 {
853 RTMPPacket pkt;
854 uint8_t *p;
855 int ret;
856
857 ✗ av_log(s, AV_LOG_DEBUG, "Sending seek command for timestamp %"PRId64"\n",
858 timestamp);
859
860 ✗ if ((ret = ff_rtmp_packet_create(&pkt, 3, RTMP_PT_INVOKE, 0, 26)) < 0)
861 ✗ return ret;
862
863 ✗ pkt.extra = rt->stream_id;
864
865 ✗ p = pkt.data;
866 ✗ ff_amf_write_string(&p, "seek");
867 ✗ ff_amf_write_number(&p, 0); //no tracking back responses
868 ✗ ff_amf_write_null(&p); //as usual, the first null param
869 ✗ ff_amf_write_number(&p, timestamp); //where we want to jump
870
871 ✗ return rtmp_send_packet(rt, &pkt, 1);
872 }
873
874 /**
875 * Generate a pause packet that either pauses or unpauses the current stream.
876 */
877 ✗ static int gen_pause(URLContext *s, RTMPContext *rt, int pause, uint32_t timestamp)
878 {
879 RTMPPacket pkt;
880 uint8_t *p;
881 int ret;
882
883 ✗ av_log(s, AV_LOG_DEBUG, "Sending pause command for timestamp %d\n",
884 timestamp);
885
886 ✗ if ((ret = ff_rtmp_packet_create(&pkt, 3, RTMP_PT_INVOKE, 0, 29)) < 0)
887 ✗ return ret;
888
889 ✗ pkt.extra = rt->stream_id;
890
891 ✗ p = pkt.data;
892 ✗ ff_amf_write_string(&p, "pause");
893 ✗ ff_amf_write_number(&p, 0); //no tracking back responses
894 ✗ ff_amf_write_null(&p); //as usual, the first null param
895 ✗ ff_amf_write_bool(&p, pause); // pause or unpause
896 ✗ ff_amf_write_number(&p, timestamp); //where we pause the stream
897
898 ✗ return rtmp_send_packet(rt, &pkt, 1);
899 }
900
901 /**
902 * Generate 'publish' call and send it to the server.
903 */
904 ✗ static int gen_publish(URLContext *s, RTMPContext *rt)
905 {
906 RTMPPacket pkt;
907 uint8_t *p;
908 int ret;
909
910 ✗ av_log(s, AV_LOG_DEBUG, "Sending publish command for '%s'\n", rt->playpath);
911
912 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SOURCE_CHANNEL, RTMP_PT_INVOKE,
913 ✗ 0, 30 + strlen(rt->playpath))) < 0)
914 ✗ return ret;
915
916 ✗ pkt.extra = rt->stream_id;
917
918 ✗ p = pkt.data;
919 ✗ ff_amf_write_string(&p, "publish");
920 ✗ ff_amf_write_number(&p, ++rt->nb_invokes);
921 ✗ ff_amf_write_null(&p);
922 ✗ ff_amf_write_string(&p, rt->playpath);
923 ✗ ff_amf_write_string(&p, "live");
924
925 ✗ return rtmp_send_packet(rt, &pkt, 1);
926 }
927
928 /**
929 * Generate ping reply and send it to the server.
930 */
931 ✗ static int gen_pong(URLContext *s, RTMPContext *rt, RTMPPacket *ppkt)
932 {
933 RTMPPacket pkt;
934 uint8_t *p;
935 int ret;
936
937 ✗ if (ppkt->size < 6) {
938 ✗ av_log(s, AV_LOG_ERROR, "Too short ping packet (%d)\n",
939 ppkt->size);
940 ✗ return AVERROR_INVALIDDATA;
941 }
942
943 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL,RTMP_PT_USER_CONTROL,
944 ✗ ppkt->timestamp + 1, 6)) < 0)
945 ✗ return ret;
946
947 ✗ p = pkt.data;
948 ✗ bytestream_put_be16(&p, 7); // PingResponse
949 ✗ bytestream_put_be32(&p, AV_RB32(ppkt->data+2));
950
951 ✗ return rtmp_send_packet(rt, &pkt, 0);
952 }
953
954 /**
955 * Generate SWF verification message and send it to the server.
956 */
957 ✗ static int gen_swf_verification(URLContext *s, RTMPContext *rt)
958 {
959 RTMPPacket pkt;
960 uint8_t *p;
961 int ret;
962
963 ✗ av_log(s, AV_LOG_DEBUG, "Sending SWF verification...\n");
964 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_USER_CONTROL,
965 0, 44)) < 0)
966 ✗ return ret;
967
968 ✗ p = pkt.data;
969 ✗ bytestream_put_be16(&p, 27);
970 ✗ memcpy(p, rt->swfverification, 42);
971
972 ✗ return rtmp_send_packet(rt, &pkt, 0);
973 }
974
975 /**
976 * Generate window acknowledgement size message and send it to the server.
977 */
978 ✗ static int gen_window_ack_size(URLContext *s, RTMPContext *rt)
979 {
980 RTMPPacket pkt;
981 uint8_t *p;
982 int ret;
983
984 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_WINDOW_ACK_SIZE,
985 0, 4)) < 0)
986 ✗ return ret;
987
988 ✗ p = pkt.data;
989 ✗ bytestream_put_be32(&p, rt->max_sent_unacked);
990
991 ✗ return rtmp_send_packet(rt, &pkt, 0);
992 }
993
994 /**
995 * Generate check bandwidth message and send it to the server.
996 */
997 ✗ static int gen_check_bw(URLContext *s, RTMPContext *rt)
998 {
999 RTMPPacket pkt;
1000 uint8_t *p;
1001 int ret;
1002
1003 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
1004 0, 21)) < 0)
1005 ✗ return ret;
1006
1007 ✗ p = pkt.data;
1008 ✗ ff_amf_write_string(&p, "_checkbw");
1009 ✗ ff_amf_write_number(&p, ++rt->nb_invokes);
1010 ✗ ff_amf_write_null(&p);
1011
1012 ✗ return rtmp_send_packet(rt, &pkt, 1);
1013 }
1014
1015 /**
1016 * Generate report on bytes read so far and send it to the server.
1017 */
1018 ✗ static int gen_bytes_read(URLContext *s, RTMPContext *rt, uint32_t ts)
1019 {
1020 RTMPPacket pkt;
1021 uint8_t *p;
1022 int ret;
1023
1024 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_NETWORK_CHANNEL, RTMP_PT_BYTES_READ,
1025 ts, 4)) < 0)
1026 ✗ return ret;
1027
1028 ✗ p = pkt.data;
1029 ✗ bytestream_put_be32(&p, rt->bytes_read);
1030
1031 ✗ return rtmp_send_packet(rt, &pkt, 0);
1032 }
1033
1034 ✗ static int gen_fcsubscribe_stream(URLContext *s, RTMPContext *rt,
1035 const char *subscribe)
1036 {
1037 RTMPPacket pkt;
1038 uint8_t *p;
1039 int ret;
1040
1041 ✗ if ((ret = ff_rtmp_packet_create(&pkt, RTMP_SYSTEM_CHANNEL, RTMP_PT_INVOKE,
1042 ✗ 0, 27 + strlen(subscribe))) < 0)
1043 ✗ return ret;
1044
1045 ✗ p = pkt.data;
1046 ✗ ff_amf_write_string(&p, "FCSubscribe");
1047 ✗ ff_amf_write_number(&p, ++rt->nb_invokes);
1048 ✗ ff_amf_write_null(&p);
1049 ✗ ff_amf_write_string(&p, subscribe);
1050
1051 ✗ return rtmp_send_packet(rt, &pkt, 1);
1052 }
1053
1054 /**
1055 * Put HMAC-SHA2 digest of packet data (except for the bytes where this digest
1056 * will be stored) into that packet.
1057 *
1058 * @param buf handshake data (1536 bytes)
1059 * @param encrypted use an encrypted connection (RTMPE)
1060 * @return offset to the digest inside input data
1061 */
1062 ✗ static int rtmp_handshake_imprint_with_digest(uint8_t *buf, int encrypted)
1063 {
1064 int ret, digest_pos;
1065
1066 ✗ if (encrypted)
1067 ✗ digest_pos = ff_rtmp_calc_digest_pos(buf, 772, 728, 776);
1068 else
1069 ✗ digest_pos = ff_rtmp_calc_digest_pos(buf, 8, 728, 12);
1070
1071 ✗ ret = ff_rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
1072 rtmp_player_key, PLAYER_KEY_OPEN_PART_LEN,
1073 buf + digest_pos);
1074 ✗ if (ret < 0)
1075 ✗ return ret;
1076
1077 ✗ return digest_pos;
1078 }
1079
1080 /**
1081 * Verify that the received server response has the expected digest value.
1082 *
1083 * @param buf handshake data received from the server (1536 bytes)
1084 * @param off position to search digest offset from
1085 * @return 0 if digest is valid, digest position otherwise
1086 */
1087 ✗ static int rtmp_validate_digest(uint8_t *buf, int off)
1088 {
1089 uint8_t digest[32];
1090 int ret, digest_pos;
1091
1092 ✗ digest_pos = ff_rtmp_calc_digest_pos(buf, off, 728, off + 4);
1093
1094 ✗ ret = ff_rtmp_calc_digest(buf, RTMP_HANDSHAKE_PACKET_SIZE, digest_pos,
1095 rtmp_server_key, SERVER_KEY_OPEN_PART_LEN,
1096 digest);
1097 ✗ if (ret < 0)
1098 ✗ return ret;
1099
1100 ✗ if (!memcmp(digest, buf + digest_pos, 32))
1101 ✗ return digest_pos;
1102 ✗ return 0;
1103 }
1104
1105 ✗ static int rtmp_calc_swf_verification(URLContext *s, RTMPContext *rt,
1106 uint8_t *buf)
1107 {
1108 uint8_t *p;
1109 int ret;
1110
1111 ✗ if (rt->swfhash_len != 32) {
1112 ✗ av_log(s, AV_LOG_ERROR,
1113 "Hash of the decompressed SWF file is not 32 bytes long.\n");
1114 ✗ return AVERROR(EINVAL);
1115 }
1116
1117 ✗ p = &rt->swfverification[0];
1118 ✗ bytestream_put_byte(&p, 1);
1119 ✗ bytestream_put_byte(&p, 1);
1120 ✗ bytestream_put_be32(&p, rt->swfsize);
1121 ✗ bytestream_put_be32(&p, rt->swfsize);
1122
1123 ✗ if ((ret = ff_rtmp_calc_digest(rt->swfhash, 32, 0, buf, 32, p)) < 0)
1124 ✗ return ret;
1125
1126 ✗ return 0;
1127 }
1128
1129 #if CONFIG_ZLIB
1130 ✗ static int rtmp_uncompress_swfplayer(uint8_t *in_data, int64_t in_size,
1131 uint8_t **out_data, int64_t *out_size)
1132 {
1133 ✗ z_stream zs = { 0 };
1134 void *ptr;
1135 int size;
1136 ✗ int ret = 0;
1137
1138 ✗ zs.avail_in = in_size;
1139 ✗ zs.next_in = in_data;
1140 ✗ ret = inflateInit(&zs);
1141 ✗ if (ret != Z_OK)
1142 ✗ return AVERROR_UNKNOWN;
1143
1144 do {
1145 uint8_t tmp_buf[16384];
1146
1147 ✗ zs.avail_out = sizeof(tmp_buf);
1148 ✗ zs.next_out = tmp_buf;
1149
1150 ✗ ret = inflate(&zs, Z_NO_FLUSH);
1151 ✗ if (ret != Z_OK && ret != Z_STREAM_END) {
1152 ✗ ret = AVERROR_UNKNOWN;
1153 ✗ goto fail;
1154 }
1155
1156 ✗ size = sizeof(tmp_buf) - zs.avail_out;
1157 ✗ if (!(ptr = av_realloc(*out_data, *out_size + size))) {
1158 ✗ ret = AVERROR(ENOMEM);
1159 ✗ goto fail;
1160 }
1161 ✗ *out_data = ptr;
1162
1163 ✗ memcpy(*out_data + *out_size, tmp_buf, size);
1164 ✗ *out_size += size;
1165 ✗ } while (zs.avail_out == 0);
1166
1167 ✗ fail:
1168 ✗ inflateEnd(&zs);
1169 ✗ return ret;
1170 }
1171 #endif
1172
1173 ✗ static int rtmp_calc_swfhash(URLContext *s)
1174 {
1175 ✗ RTMPContext *rt = s->priv_data;
1176 ✗ uint8_t *in_data = NULL, *out_data = NULL, *swfdata;
1177 int64_t in_size;
1178 ✗ URLContext *stream = NULL;
1179 char swfhash[32];
1180 int swfsize;
1181 ✗ int ret = 0;
1182
1183 /* Get the SWF player file. */
1184 ✗ if ((ret = ffurl_open_whitelist(&stream, rt->swfverify, AVIO_FLAG_READ,
1185 ✗ &s->interrupt_callback, NULL,
1186 s->protocol_whitelist, s->protocol_blacklist, s)) < 0) {
1187 ✗ av_log(s, AV_LOG_ERROR, "Cannot open connection %s.\n", rt->swfverify);
1188 ✗ goto fail;
1189 }
1190
1191 ✗ if ((in_size = ffurl_seek(stream, 0, AVSEEK_SIZE)) < 0) {
1192 ✗ ret = AVERROR(EIO);
1193 ✗ goto fail;
1194 }
1195
1196 ✗ if (!(in_data = av_malloc(in_size))) {
1197 ✗ ret = AVERROR(ENOMEM);
1198 ✗ goto fail;
1199 }
1200
1201 ✗ if ((ret = ffurl_read_complete(stream, in_data, in_size)) < 0)
1202 ✗ goto fail;
1203
1204 ✗ if (in_size < 3) {
1205 ✗ ret = AVERROR_INVALIDDATA;
1206 ✗ goto fail;
1207 }
1208
1209 ✗ if (!memcmp(in_data, "CWS", 3)) {
1210 #if CONFIG_ZLIB
1211 int64_t out_size;
1212 ✗ if (in_size < 8) {
1213 ✗ ret = AVERROR_INVALIDDATA;
1214 ✗ goto fail;
1215 }
1216 /* Decompress the SWF player file using Zlib. */
1217 ✗ if (!(out_data = av_malloc(8))) {
1218 ✗ ret = AVERROR(ENOMEM);
1219 ✗ goto fail;
1220 }
1221 ✗ *in_data = 'F'; // magic stuff
1222 ✗ memcpy(out_data, in_data, 8);
1223 ✗ out_size = 8;
1224
1225 ✗ if ((ret = rtmp_uncompress_swfplayer(in_data + 8, in_size - 8,
1226 &out_data, &out_size)) < 0)
1227 ✗ goto fail;
1228 ✗ swfsize = out_size;
1229 ✗ swfdata = out_data;
1230 #else
1231 av_log(s, AV_LOG_ERROR,
1232 "Zlib is required for decompressing the SWF player file.\n");
1233 ret = AVERROR(EINVAL);
1234 goto fail;
1235 #endif
1236 } else {
1237 ✗ swfsize = in_size;
1238 ✗ swfdata = in_data;
1239 }
1240
1241 /* Compute the SHA256 hash of the SWF player file. */
1242 ✗ if ((ret = ff_rtmp_calc_digest(swfdata, swfsize, 0,
1243 "Genuine Adobe Flash Player 001", 30,
1244 swfhash)) < 0)
1245 ✗ goto fail;
1246
1247 /* Set SWFVerification parameters. */
1248 ✗ av_opt_set_bin(rt, "rtmp_swfhash", swfhash, 32, 0);
1249 ✗ rt->swfsize = swfsize;
1250
1251 ✗ fail:
1252 ✗ av_freep(&in_data);
1253 ✗ av_freep(&out_data);
1254 ✗ ffurl_close(stream);
1255 ✗ return ret;
1256 }
1257
1258 /**
1259 * Perform handshake with the server by means of exchanging pseudorandom data
1260 * signed with HMAC-SHA2 digest.
1261 *
1262 * @return 0 if handshake succeeds, negative value otherwise
1263 */
1264 ✗ static int rtmp_handshake(URLContext *s, RTMPContext *rt)
1265 {
1266 AVLFG rnd;
1267 ✗ uint8_t tosend [RTMP_HANDSHAKE_PACKET_SIZE+1] = {
1268 3, // unencrypted data
1269 0, 0, 0, 0, // client uptime
1270 RTMP_CLIENT_VER1,
1271 RTMP_CLIENT_VER2,
1272 RTMP_CLIENT_VER3,
1273 RTMP_CLIENT_VER4,
1274 };
1275 uint8_t clientdata[RTMP_HANDSHAKE_PACKET_SIZE];
1276 uint8_t serverdata[RTMP_HANDSHAKE_PACKET_SIZE+1];
1277 int i;
1278 int server_pos, client_pos;
1279 uint8_t digest[32], signature[32];
1280 int ret;
1281 #if CONFIG_FFRTMPCRYPT_PROTOCOL
1282 int type = 0;
1283 #endif
1284
1285 ✗ av_log(s, AV_LOG_DEBUG, "Handshaking...\n");
1286
1287 ✗ av_lfg_init(&rnd, 0xDEADC0DE);
1288 // generate handshake packet - 1536 bytes of pseudorandom data
1289 ✗ for (i = 9; i <= RTMP_HANDSHAKE_PACKET_SIZE; i++)
1290 ✗ tosend[i] = av_lfg_get(&rnd) >> 24;
1291
1292 #if CONFIG_FFRTMPCRYPT_PROTOCOL
1293 if (rt->encrypted) {
1294 /* When the client wants to use RTMPE, we have to change the command
1295 * byte to 0x06 which means to use encrypted data and we have to set
1296 * the flash version to at least 9.0.115.0. */
1297 tosend[0] = 6;
1298 tosend[5] = 128;
1299 tosend[6] = 0;
1300 tosend[7] = 3;
1301 tosend[8] = 2;
1302
1303 /* Initialize the Diffie-Hellmann context and generate the public key
1304 * to send to the server. */
1305 if ((ret = ff_rtmpe_gen_pub_key(rt->stream, tosend + 1)) < 0)
1306 return ret;
1307 }
1308 #endif
1309
1310 ✗ client_pos = rtmp_handshake_imprint_with_digest(tosend + 1, rt->encrypted);
1311 ✗ if (client_pos < 0)
1312 ✗ return client_pos;
1313
1314 ✗ if ((ret = ffurl_write(rt->stream, tosend,
1315 RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
1316 ✗ av_log(s, AV_LOG_ERROR, "Cannot write RTMP handshake request\n");
1317 ✗ return ret;
1318 }
1319
1320 ✗ if ((ret = ffurl_read_complete(rt->stream, serverdata,
1321 RTMP_HANDSHAKE_PACKET_SIZE + 1)) < 0) {
1322 ✗ av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
1323 ✗ return ret;
1324 }
1325
1326 ✗ if ((ret = ffurl_read_complete(rt->stream, clientdata,
1327 RTMP_HANDSHAKE_PACKET_SIZE)) < 0) {
1328 ✗ av_log(s, AV_LOG_ERROR, "Cannot read RTMP handshake response\n");
1329 ✗ return ret;
1330 }
1331
1332 ✗ av_log(s, AV_LOG_DEBUG, "Type answer %d\n", serverdata[0]);
1333 ✗ av_log(s, AV_LOG_DEBUG, "Server version %d.%d.%d.%d\n",
1334 ✗ serverdata[5], serverdata[6], serverdata[7], serverdata[8]);
1335
1336 ✗ if (rt->is_input && serverdata[5] >= 3) {
1337 ✗ server_pos = rtmp_validate_digest(serverdata + 1, 772);
1338 ✗ if (server_pos < 0)
1339 ✗ return server_pos;
1340
1341 ✗ if (!server_pos) {
1342 #if CONFIG_FFRTMPCRYPT_PROTOCOL
1343 type = 1;
1344 #endif
1345 ✗ server_pos = rtmp_validate_digest(serverdata + 1, 8);
1346 ✗ if (server_pos < 0)
1347 ✗ return server_pos;
1348
1349 ✗ if (!server_pos) {
1350 ✗ av_log(s, AV_LOG_ERROR, "Server response validating failed\n");
1351 ✗ return AVERROR(EIO);
1352 }
1353 }
1354
1355 /* Generate SWFVerification token (SHA256 HMAC hash of decompressed SWF,
1356 * key are the last 32 bytes of the server handshake. */
1357 ✗ if (rt->swfsize) {
1358 ✗ if ((ret = rtmp_calc_swf_verification(s, rt, serverdata + 1 +
1359 RTMP_HANDSHAKE_PACKET_SIZE - 32)) < 0)
1360 ✗ return ret;
1361 }
1362
1363 ✗ ret = ff_rtmp_calc_digest(tosend + 1 + client_pos, 32, 0,
1364 rtmp_server_key, sizeof(rtmp_server_key),
1365 digest);
1366 ✗ if (ret < 0)
1367 ✗ return ret;
1368
1369 ✗ ret = ff_rtmp_calc_digest(clientdata, RTMP_HANDSHAKE_PACKET_SIZE - 32,
1370 0, digest, 32, signature);
1371 ✗ if (ret < 0)
1372 ✗ return ret;
1373
1374 #if CONFIG_FFRTMPCRYPT_PROTOCOL
1375 if (rt->encrypted) {
1376 /* Compute the shared secret key sent by the server and initialize
1377 * the RC4 encryption. */
1378 if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
1379 tosend + 1, type)) < 0)
1380 return ret;
1381
1382 /* Encrypt the signature received by the server. */
1383 ff_rtmpe_encrypt_sig(rt->stream, signature, digest, serverdata[0]);
1384 }
1385 #endif
1386
1387 ✗ if (memcmp(signature, clientdata + RTMP_HANDSHAKE_PACKET_SIZE - 32, 32)) {
1388 ✗ av_log(s, AV_LOG_ERROR, "Signature mismatch\n");
1389 ✗ return AVERROR(EIO);
1390 }
1391
1392 ✗ for (i = 0; i < RTMP_HANDSHAKE_PACKET_SIZE; i++)
1393 ✗ tosend[i] = av_lfg_get(&rnd) >> 24;
1394 ✗ ret = ff_rtmp_calc_digest(serverdata + 1 + server_pos, 32, 0,
1395 rtmp_player_key, sizeof(rtmp_player_key),
1396 digest);
1397 ✗ if (ret < 0)
1398 ✗ return ret;
1399
1400 ✗ ret = ff_rtmp_calc_digest(tosend, RTMP_HANDSHAKE_PACKET_SIZE - 32, 0,
1401 digest, 32,
1402 tosend + RTMP_HANDSHAKE_PACKET_SIZE - 32);
1403 ✗ if (ret < 0)
1404 ✗ return ret;
1405
1406 #if CONFIG_FFRTMPCRYPT_PROTOCOL
1407 if (rt->encrypted) {
1408 /* Encrypt the signature to be send to the server. */
1409 ff_rtmpe_encrypt_sig(rt->stream, tosend +
1410 RTMP_HANDSHAKE_PACKET_SIZE - 32, digest,
1411 serverdata[0]);
1412 }
1413 #endif
1414
1415 // write reply back to the server
1416 ✗ if ((ret = ffurl_write(rt->stream, tosend,
1417 RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
1418 ✗ return ret;
1419
1420 #if CONFIG_FFRTMPCRYPT_PROTOCOL
1421 if (rt->encrypted) {
1422 /* Set RC4 keys for encryption and update the keystreams. */
1423 if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
1424 return ret;
1425 }
1426 #endif
1427 } else {
1428 #if CONFIG_FFRTMPCRYPT_PROTOCOL
1429 if (rt->encrypted) {
1430 /* Compute the shared secret key sent by the server and initialize
1431 * the RC4 encryption. */
1432 if ((ret = ff_rtmpe_compute_secret_key(rt->stream, serverdata + 1,
1433 tosend + 1, 1)) < 0)
1434 return ret;
1435
1436 if (serverdata[0] == 9) {
1437 /* Encrypt the signature received by the server. */
1438 ff_rtmpe_encrypt_sig(rt->stream, signature, digest,
1439 serverdata[0]);
1440 }
1441 }
1442 #endif
1443
1444 ✗ if ((ret = ffurl_write(rt->stream, serverdata + 1,
1445 RTMP_HANDSHAKE_PACKET_SIZE)) < 0)
1446 ✗ return ret;
1447
1448 #if CONFIG_FFRTMPCRYPT_PROTOCOL
1449 if (rt->encrypted) {
1450 /* Set RC4 keys for encryption and update the keystreams. */
1451 if ((ret = ff_rtmpe_update_keystream(rt->stream)) < 0)
1452 return ret;
1453 }
1454 #endif
1455 }
1456
1457 ✗ return 0;
1458 }
1459
1460 ✗ static int rtmp_receive_hs_packet(RTMPContext* rt, uint32_t *first_int,
1461 uint32_t *second_int, char *arraydata,
1462 int size)
1463 {
1464 int inoutsize;
1465
1466 ✗ inoutsize = ffurl_read_complete(rt->stream, arraydata,
1467 RTMP_HANDSHAKE_PACKET_SIZE);
1468 ✗ if (inoutsize <= 0)
1469 ✗ return AVERROR(EIO);
1470 ✗ if (inoutsize != RTMP_HANDSHAKE_PACKET_SIZE) {
1471 ✗ av_log(rt, AV_LOG_ERROR, "Erroneous Message size %d"
1472 " not following standard\n", (int)inoutsize);
1473 ✗ return AVERROR(EINVAL);
1474 }
1475
1476 ✗ *first_int = AV_RB32(arraydata);
1477 ✗ *second_int = AV_RB32(arraydata + 4);
1478 ✗ return 0;
1479 }
1480
1481 ✗ static int rtmp_send_hs_packet(RTMPContext* rt, uint32_t first_int,
1482 uint32_t second_int, char *arraydata, int size)
1483 {
1484 int inoutsize;
1485
1486 ✗ AV_WB32(arraydata, first_int);
1487 ✗ AV_WB32(arraydata + 4, second_int);
1488 ✗ inoutsize = ffurl_write(rt->stream, arraydata,
1489 RTMP_HANDSHAKE_PACKET_SIZE);
1490 ✗ if (inoutsize != RTMP_HANDSHAKE_PACKET_SIZE) {
1491 ✗ av_log(rt, AV_LOG_ERROR, "Unable to write answer\n");
1492 ✗ return AVERROR(EIO);
1493 }
1494
1495 ✗ return 0;
1496 }
1497
1498 /**
1499 * rtmp handshake server side
1500 */
1501 ✗ static int rtmp_server_handshake(URLContext *s, RTMPContext *rt)
1502 {
1503 uint8_t buffer[RTMP_HANDSHAKE_PACKET_SIZE];
1504 uint32_t hs_epoch;
1505 uint32_t hs_my_epoch;
1506 uint8_t hs_c1[RTMP_HANDSHAKE_PACKET_SIZE];
1507 uint8_t hs_s1[RTMP_HANDSHAKE_PACKET_SIZE];
1508 uint32_t zeroes;
1509 ✗ uint32_t temp = 0;
1510 ✗ int randomidx = 0;
1511 ✗ int inoutsize = 0;
1512 int ret;
1513
1514 ✗ inoutsize = ffurl_read_complete(rt->stream, buffer, 1); // Receive C0
1515 ✗ if (inoutsize <= 0) {
1516 ✗ av_log(s, AV_LOG_ERROR, "Unable to read handshake\n");
1517 ✗ return AVERROR(EIO);
1518 }
1519 // Check Version
1520 ✗ if (buffer[0] != 3) {
1521 ✗ av_log(s, AV_LOG_ERROR, "RTMP protocol version mismatch\n");
1522 ✗ return AVERROR(EIO);
1523 }
1524 ✗ if (ffurl_write(rt->stream, buffer, 1) <= 0) { // Send S0
1525 ✗ av_log(s, AV_LOG_ERROR,
1526 "Unable to write answer - RTMP S0\n");
1527 ✗ return AVERROR(EIO);
1528 }
1529 /* Receive C1 */
1530 ✗ ret = rtmp_receive_hs_packet(rt, &hs_epoch, &zeroes, hs_c1,
1531 RTMP_HANDSHAKE_PACKET_SIZE);
1532 ✗ if (ret) {
1533 ✗ av_log(s, AV_LOG_ERROR, "RTMP Handshake C1 Error\n");
1534 ✗ return ret;
1535 }
1536 /* Send S1 */
1537 /* By now same epoch will be sent */
1538 ✗ hs_my_epoch = hs_epoch;
1539 /* Generate random */
1540 ✗ for (randomidx = 8; randomidx < (RTMP_HANDSHAKE_PACKET_SIZE);
1541 ✗ randomidx += 4)
1542 ✗ AV_WB32(hs_s1 + randomidx, av_get_random_seed());
1543
1544 ✗ ret = rtmp_send_hs_packet(rt, hs_my_epoch, 0, hs_s1,
1545 RTMP_HANDSHAKE_PACKET_SIZE);
1546 ✗ if (ret) {
1547 ✗ av_log(s, AV_LOG_ERROR, "RTMP Handshake S1 Error\n");
1548 ✗ return ret;
1549 }
1550 /* Send S2 */
1551 ✗ ret = rtmp_send_hs_packet(rt, hs_epoch, 0, hs_c1,
1552 RTMP_HANDSHAKE_PACKET_SIZE);
1553 ✗ if (ret) {
1554 ✗ av_log(s, AV_LOG_ERROR, "RTMP Handshake S2 Error\n");
1555 ✗ return ret;
1556 }
1557 /* Receive C2 */
1558 ✗ ret = rtmp_receive_hs_packet(rt, &temp, &zeroes, buffer,
1559 RTMP_HANDSHAKE_PACKET_SIZE);
1560 ✗ if (ret) {
1561 ✗ av_log(s, AV_LOG_ERROR, "RTMP Handshake C2 Error\n");
1562 ✗ return ret;
1563 }
1564 ✗ if (temp != hs_my_epoch)
1565 ✗ av_log(s, AV_LOG_WARNING,
1566 "Erroneous C2 Message epoch does not match up with C1 epoch\n");
1567 ✗ if (memcmp(buffer + 8, hs_s1 + 8,
1568 RTMP_HANDSHAKE_PACKET_SIZE - 8))
1569 ✗ av_log(s, AV_LOG_WARNING,
1570 "Erroneous C2 Message random does not match up\n");
1571
1572 ✗ return 0;
1573 }
1574
1575 ✗ static int handle_chunk_size(URLContext *s, RTMPPacket *pkt)
1576 {
1577 ✗ RTMPContext *rt = s->priv_data;
1578 int ret;
1579
1580 ✗ if (pkt->size < 4) {
1581 ✗ av_log(s, AV_LOG_ERROR,
1582 "Too short chunk size change packet (%d)\n",
1583 pkt->size);
1584 ✗ return AVERROR_INVALIDDATA;
1585 }
1586
1587 ✗ if (!rt->is_input) {
1588 /* Send the same chunk size change packet back to the server,
1589 * setting the outgoing chunk size to the same as the incoming one. */
1590 ✗ if ((ret = ff_rtmp_packet_write(rt->stream, pkt, rt->out_chunk_size,
1591 &rt->prev_pkt[1], &rt->nb_prev_pkt[1])) < 0)
1592 ✗ return ret;
1593 ✗ rt->out_chunk_size = AV_RB32(pkt->data);
1594 }
1595
1596 ✗ rt->in_chunk_size = AV_RB32(pkt->data);
1597 ✗ if (rt->in_chunk_size <= 0) {
1598 ✗ av_log(s, AV_LOG_ERROR, "Incorrect chunk size %d\n",
1599 rt->in_chunk_size);
1600 ✗ return AVERROR_INVALIDDATA;
1601 }
1602 ✗ av_log(s, AV_LOG_DEBUG, "New incoming chunk size = %d\n",
1603 rt->in_chunk_size);
1604
1605 ✗ return 0;
1606 }
1607
1608 ✗ static int handle_user_control(URLContext *s, RTMPPacket *pkt)
1609 {
1610 ✗ RTMPContext *rt = s->priv_data;
1611 int t, ret;
1612
1613 ✗ if (pkt->size < 2) {
1614 ✗ av_log(s, AV_LOG_ERROR, "Too short user control packet (%d)\n",
1615 pkt->size);
1616 ✗ return AVERROR_INVALIDDATA;
1617 }
1618
1619 ✗ t = AV_RB16(pkt->data);
1620 ✗ if (t == 6) { // PingRequest
1621 ✗ if ((ret = gen_pong(s, rt, pkt)) < 0)
1622 ✗ return ret;
1623 ✗ } else if (t == 26) {
1624 ✗ if (rt->swfsize) {
1625 ✗ if ((ret = gen_swf_verification(s, rt)) < 0)
1626 ✗ return ret;
1627 } else {
1628 ✗ av_log(s, AV_LOG_WARNING, "Ignoring SWFVerification request.\n");
1629 }
1630 }
1631
1632 ✗ return 0;
1633 }
1634
1635 ✗ static int handle_set_peer_bw(URLContext *s, RTMPPacket *pkt)
1636 {
1637 ✗ RTMPContext *rt = s->priv_data;
1638
1639 ✗ if (pkt->size < 4) {
1640 ✗ av_log(s, AV_LOG_ERROR,
1641 "Peer bandwidth packet is less than 4 bytes long (%d)\n",
1642 pkt->size);
1643 ✗ return AVERROR_INVALIDDATA;
1644 }
1645
1646 // We currently don't check how much the peer has acknowledged of
1647 // what we have sent. To do that properly, we should call
1648 // gen_window_ack_size here, to tell the peer that we want an
1649 // acknowledgement with (at least) that interval.
1650 ✗ rt->max_sent_unacked = AV_RB32(pkt->data);
1651 ✗ if (rt->max_sent_unacked <= 0) {
1652 ✗ av_log(s, AV_LOG_ERROR, "Incorrect set peer bandwidth %d\n",
1653 rt->max_sent_unacked);
1654 ✗ return AVERROR_INVALIDDATA;
1655
1656 }
1657 ✗ av_log(s, AV_LOG_DEBUG, "Max sent, unacked = %d\n", rt->max_sent_unacked);
1658
1659 ✗ return 0;
1660 }
1661
1662 ✗ static int handle_window_ack_size(URLContext *s, RTMPPacket *pkt)
1663 {
1664 ✗ RTMPContext *rt = s->priv_data;
1665
1666 ✗ if (pkt->size < 4) {
1667 ✗ av_log(s, AV_LOG_ERROR,
1668 "Too short window acknowledgement size packet (%d)\n",
1669 pkt->size);
1670 ✗ return AVERROR_INVALIDDATA;
1671 }
1672
1673 ✗ rt->receive_report_size = AV_RB32(pkt->data);
1674 ✗ if (rt->receive_report_size <= 0) {
1675 ✗ av_log(s, AV_LOG_ERROR, "Incorrect window acknowledgement size %d\n",
1676 rt->receive_report_size);
1677 ✗ return AVERROR_INVALIDDATA;
1678 }
1679 ✗ av_log(s, AV_LOG_DEBUG, "Window acknowledgement size = %d\n", rt->receive_report_size);
1680 // Send an Acknowledgement packet after receiving half the maximum
1681 // size, to make sure the peer can keep on sending without waiting
1682 // for acknowledgements.
1683 ✗ rt->receive_report_size >>= 1;
1684
1685 ✗ return 0;
1686 }
1687
1688 ✗ static int do_adobe_auth(RTMPContext *rt, const char *user, const char *salt,
1689 const char *opaque, const char *challenge)
1690 {
1691 uint8_t hash[16];
1692 char hashstr[AV_BASE64_SIZE(sizeof(hash))], challenge2[10];
1693 ✗ struct AVMD5 *md5 = av_md5_alloc();
1694 ✗ if (!md5)
1695 ✗ return AVERROR(ENOMEM);
1696
1697 ✗ snprintf(challenge2, sizeof(challenge2), "%08x", av_get_random_seed());
1698
1699 ✗ av_md5_init(md5);
1700 ✗ av_md5_update(md5, user, strlen(user));
1701 ✗ av_md5_update(md5, salt, strlen(salt));
1702 ✗ av_md5_update(md5, rt->password, strlen(rt->password));
1703 ✗ av_md5_final(md5, hash);
1704 ✗ av_base64_encode(hashstr, sizeof(hashstr), hash,
1705 sizeof(hash));
1706 ✗ av_md5_init(md5);
1707 ✗ av_md5_update(md5, hashstr, strlen(hashstr));
1708 ✗ if (opaque)
1709 ✗ av_md5_update(md5, opaque, strlen(opaque));
1710 ✗ else if (challenge)
1711 ✗ av_md5_update(md5, challenge, strlen(challenge));
1712 ✗ av_md5_update(md5, challenge2, strlen(challenge2));
1713 ✗ av_md5_final(md5, hash);
1714 ✗ av_base64_encode(hashstr, sizeof(hashstr), hash,
1715 sizeof(hash));
1716 ✗ snprintf(rt->auth_params, sizeof(rt->auth_params),
1717 "?authmod=%s&user=%s&challenge=%s&response=%s",
1718 "adobe", user, challenge2, hashstr);
1719 ✗ if (opaque)
1720 ✗ av_strlcatf(rt->auth_params, sizeof(rt->auth_params),
1721 "&opaque=%s", opaque);
1722
1723 ✗ av_free(md5);
1724 ✗ return 0;
1725 }
1726
1727 ✗ static int do_llnw_auth(RTMPContext *rt, const char *user, const char *nonce)
1728 {
1729 uint8_t hash[16];
1730 char hashstr1[33], hashstr2[33];
1731 ✗ const char *realm = "live";
1732 ✗ const char *method = "publish";
1733 ✗ const char *qop = "auth";
1734 ✗ const char *nc = "00000001";
1735 char cnonce[10];
1736 ✗ struct AVMD5 *md5 = av_md5_alloc();
1737 ✗ if (!md5)
1738 ✗ return AVERROR(ENOMEM);
1739
1740 ✗ snprintf(cnonce, sizeof(cnonce), "%08x", av_get_random_seed());
1741
1742 ✗ av_md5_init(md5);
1743 ✗ av_md5_update(md5, user, strlen(user));
1744 ✗ av_md5_update(md5, ":", 1);
1745 ✗ av_md5_update(md5, realm, strlen(realm));
1746 ✗ av_md5_update(md5, ":", 1);
1747 ✗ av_md5_update(md5, rt->password, strlen(rt->password));
1748 ✗ av_md5_final(md5, hash);
1749 ✗ ff_data_to_hex(hashstr1, hash, 16, 1);
1750
1751 ✗ av_md5_init(md5);
1752 ✗ av_md5_update(md5, method, strlen(method));
1753 ✗ av_md5_update(md5, ":/", 2);
1754 ✗ av_md5_update(md5, rt->app, strlen(rt->app));
1755 ✗ if (!strchr(rt->app, '/'))
1756 ✗ av_md5_update(md5, "/_definst_", strlen("/_definst_"));
1757 ✗ av_md5_final(md5, hash);
1758 ✗ ff_data_to_hex(hashstr2, hash, 16, 1);
1759
1760 ✗ av_md5_init(md5);
1761 ✗ av_md5_update(md5, hashstr1, strlen(hashstr1));
1762 ✗ av_md5_update(md5, ":", 1);
1763 ✗ if (nonce)
1764 ✗ av_md5_update(md5, nonce, strlen(nonce));
1765 ✗ av_md5_update(md5, ":", 1);
1766 ✗ av_md5_update(md5, nc, strlen(nc));
1767 ✗ av_md5_update(md5, ":", 1);
1768 ✗ av_md5_update(md5, cnonce, strlen(cnonce));
1769 ✗ av_md5_update(md5, ":", 1);
1770 ✗ av_md5_update(md5, qop, strlen(qop));
1771 ✗ av_md5_update(md5, ":", 1);
1772 ✗ av_md5_update(md5, hashstr2, strlen(hashstr2));
1773 ✗ av_md5_final(md5, hash);
1774 ✗ ff_data_to_hex(hashstr1, hash, 16, 1);
1775
1776 ✗ snprintf(rt->auth_params, sizeof(rt->auth_params),
1777 "?authmod=%s&user=%s&nonce=%s&cnonce=%s&nc=%s&response=%s",
1778 "llnw", user, nonce, cnonce, nc, hashstr1);
1779
1780 ✗ av_free(md5);
1781 ✗ return 0;
1782 }
1783
1784 ✗ static int handle_connect_error(URLContext *s, const char *desc)
1785 {
1786 ✗ RTMPContext *rt = s->priv_data;
1787 char buf[300], *ptr, authmod[15];
1788 ✗ int i = 0, ret = 0;
1789 ✗ const char *user = "", *salt = "", *opaque = NULL,
1790 ✗ *challenge = NULL, *cptr = NULL, *nonce = NULL;
1791
1792 ✗ if (!(cptr = strstr(desc, "authmod=adobe")) &&
1793 ✗ !(cptr = strstr(desc, "authmod=llnw"))) {
1794 ✗ av_log(s, AV_LOG_ERROR,
1795 "Unknown connect error (unsupported authentication method?)\n");
1796 ✗ return AVERROR_UNKNOWN;
1797 }
1798 ✗ cptr += strlen("authmod=");
1799 ✗ while (*cptr && *cptr != ' ' && i < sizeof(authmod) - 1)
1800 ✗ authmod[i++] = *cptr++;
1801 ✗ authmod[i] = '\0';
1802
1803 ✗ if (!rt->username[0] || !rt->password[0]) {
1804 ✗ av_log(s, AV_LOG_ERROR, "No credentials set\n");
1805 ✗ return AVERROR_UNKNOWN;
1806 }
1807
1808 ✗ if (strstr(desc, "?reason=authfailed")) {
1809 ✗ av_log(s, AV_LOG_ERROR, "Incorrect username/password\n");
1810 ✗ return AVERROR_UNKNOWN;
1811 ✗ } else if (strstr(desc, "?reason=nosuchuser")) {
1812 ✗ av_log(s, AV_LOG_ERROR, "Incorrect username\n");
1813 ✗ return AVERROR_UNKNOWN;
1814 }
1815
1816 ✗ if (rt->auth_tried) {
1817 ✗ av_log(s, AV_LOG_ERROR, "Authentication failed\n");
1818 ✗ return AVERROR_UNKNOWN;
1819 }
1820
1821 ✗ rt->auth_params[0] = '\0';
1822
1823 ✗ if (strstr(desc, "code=403 need auth")) {
1824 ✗ snprintf(rt->auth_params, sizeof(rt->auth_params),
1825 ✗ "?authmod=%s&user=%s", authmod, rt->username);
1826 ✗ return 0;
1827 }
1828
1829 ✗ if (!(cptr = strstr(desc, "?reason=needauth"))) {
1830 ✗ av_log(s, AV_LOG_ERROR, "No auth parameters found\n");
1831 ✗ return AVERROR_UNKNOWN;
1832 }
1833
1834 ✗ av_strlcpy(buf, cptr + 1, sizeof(buf));
1835 ✗ ptr = buf;
1836
1837 ✗ while (ptr) {
1838 ✗ char *next = strchr(ptr, '&');
1839 ✗ char *value = strchr(ptr, '=');
1840 ✗ if (next)
1841 ✗ *next++ = '\0';
1842 ✗ if (value) {
1843 ✗ *value++ = '\0';
1844 ✗ if (!strcmp(ptr, "user")) {
1845 ✗ user = value;
1846 ✗ } else if (!strcmp(ptr, "salt")) {
1847 ✗ salt = value;
1848 ✗ } else if (!strcmp(ptr, "opaque")) {
1849 ✗ opaque = value;
1850 ✗ } else if (!strcmp(ptr, "challenge")) {
1851 ✗ challenge = value;
1852 ✗ } else if (!strcmp(ptr, "nonce")) {
1853 ✗ nonce = value;
1854 } else {
1855 ✗ av_log(s, AV_LOG_INFO, "Ignoring unsupported var %s\n", ptr);
1856 }
1857 } else {
1858 ✗ av_log(s, AV_LOG_WARNING, "Variable %s has NULL value\n", ptr);
1859 }
1860 ✗ ptr = next;
1861 }
1862
1863 ✗ if (!strcmp(authmod, "adobe")) {
1864 ✗ if ((ret = do_adobe_auth(rt, user, salt, opaque, challenge)) < 0)
1865 ✗ return ret;
1866 } else {
1867 ✗ if ((ret = do_llnw_auth(rt, user, nonce)) < 0)
1868 ✗ return ret;
1869 }
1870
1871 ✗ rt->auth_tried = 1;
1872 ✗ return 0;
1873 }
1874
1875 ✗ static int handle_invoke_error(URLContext *s, RTMPPacket *pkt)
1876 {
1877 ✗ RTMPContext *rt = s->priv_data;
1878 ✗ const uint8_t *data_end = pkt->data + pkt->size;
1879 ✗ char *tracked_method = NULL;
1880 ✗ int level = AV_LOG_ERROR;
1881 uint8_t tmpstr[256];
1882 int ret;
1883
1884 ✗ if ((ret = find_tracked_method(s, pkt, 9, &tracked_method)) < 0)
1885 ✗ return ret;
1886
1887 ✗ if (!ff_amf_get_field_value(pkt->data + 9, data_end,
1888 "description", tmpstr, sizeof(tmpstr))) {
1889 ✗ if (tracked_method && (!strcmp(tracked_method, "_checkbw") ||
1890 ✗ !strcmp(tracked_method, "releaseStream") ||
1891 ✗ !strcmp(tracked_method, "FCSubscribe") ||
1892 ✗ !strcmp(tracked_method, "FCPublish"))) {
1893 /* Gracefully ignore Adobe-specific historical artifact errors. */
1894 ✗ level = AV_LOG_WARNING;
1895 ✗ ret = 0;
1896 ✗ } else if (tracked_method && !strcmp(tracked_method, "getStreamLength")) {
1897 ✗ level = rt->live ? AV_LOG_DEBUG : AV_LOG_WARNING;
1898 ✗ ret = 0;
1899 ✗ } else if (tracked_method && !strcmp(tracked_method, "connect")) {
1900 ✗ ret = handle_connect_error(s, tmpstr);
1901 ✗ if (!ret) {
1902 ✗ rt->do_reconnect = 1;
1903 ✗ level = AV_LOG_VERBOSE;
1904 }
1905 } else
1906 ✗ ret = AVERROR_UNKNOWN;
1907 ✗ av_log(s, level, "Server error: %s\n", tmpstr);
1908 }
1909
1910 ✗ av_free(tracked_method);
1911 ✗ return ret;
1912 }
1913
1914 ✗ static int write_begin(URLContext *s)
1915 {
1916 ✗ RTMPContext *rt = s->priv_data;
1917 PutByteContext pbc;
1918 ✗ RTMPPacket spkt = { 0 };
1919 int ret;
1920
1921 // Send Stream Begin 1
1922 ✗ if ((ret = ff_rtmp_packet_create(&spkt, RTMP_NETWORK_CHANNEL,
1923 RTMP_PT_USER_CONTROL, 0, 6)) < 0) {
1924 ✗ av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
1925 ✗ return ret;
1926 }
1927
1928 ✗ bytestream2_init_writer(&pbc, spkt.data, spkt.size);
1929 ✗ bytestream2_put_be16(&pbc, 0); // 0 -> Stream Begin
1930 ✗ bytestream2_put_be32(&pbc, rt->nb_streamid);
1931
1932 ✗ ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
1933 &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
1934
1935 ✗ ff_rtmp_packet_destroy(&spkt);
1936
1937 ✗ return ret;
1938 }
1939
1940 ✗ static int write_status(URLContext *s, RTMPPacket *pkt,
1941 const char *status, const char *description, const char *details)
1942 {
1943 ✗ RTMPContext *rt = s->priv_data;
1944 ✗ RTMPPacket spkt = { 0 };
1945 uint8_t *pp;
1946 int ret;
1947
1948 ✗ if ((ret = ff_rtmp_packet_create(&spkt, RTMP_SYSTEM_CHANNEL,
1949 RTMP_PT_INVOKE, 0,
1950 RTMP_PKTDATA_DEFAULT_SIZE
1951 ✗ + strlen(status) + strlen(description)
1952 ✗ + zstrlen(details))) < 0) {
1953 ✗ av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
1954 ✗ return ret;
1955 }
1956
1957 ✗ pp = spkt.data;
1958 ✗ spkt.extra = pkt->extra;
1959 ✗ ff_amf_write_string(&pp, "onStatus");
1960 ✗ ff_amf_write_number(&pp, 0);
1961 ✗ ff_amf_write_null(&pp);
1962
1963 ✗ ff_amf_write_object_start(&pp);
1964 ✗ ff_amf_write_field_name(&pp, "level");
1965 ✗ ff_amf_write_string(&pp, "status");
1966 ✗ ff_amf_write_field_name(&pp, "code");
1967 ✗ ff_amf_write_string(&pp, status);
1968 ✗ ff_amf_write_field_name(&pp, "description");
1969 ✗ ff_amf_write_string(&pp, description);
1970 ✗ if (details) {
1971 ✗ ff_amf_write_field_name(&pp, "details");
1972 ✗ ff_amf_write_string(&pp, details);
1973 }
1974 ✗ ff_amf_write_object_end(&pp);
1975
1976 ✗ spkt.size = pp - spkt.data;
1977 ✗ ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
1978 &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
1979 ✗ ff_rtmp_packet_destroy(&spkt);
1980
1981 ✗ return ret;
1982 }
1983
1984 ✗ static int send_invoke_response(URLContext *s, RTMPPacket *pkt)
1985 {
1986 ✗ RTMPContext *rt = s->priv_data;
1987 double seqnum;
1988 char filename[128];
1989 char command[64];
1990 int stringlen;
1991 char *pchar;
1992 ✗ const uint8_t *p = pkt->data;
1993 ✗ uint8_t *pp = NULL;
1994 ✗ RTMPPacket spkt = { 0 };
1995 GetByteContext gbc;
1996 int ret;
1997
1998 ✗ bytestream2_init(&gbc, p, pkt->size);
1999 ✗ if (ff_amf_read_string(&gbc, command, sizeof(command),
2000 &stringlen)) {
2001 ✗ av_log(s, AV_LOG_ERROR, "Error in PT_INVOKE\n");
2002 ✗ return AVERROR_INVALIDDATA;
2003 }
2004
2005 ✗ ret = ff_amf_read_number(&gbc, &seqnum);
2006 ✗ if (ret)
2007 ✗ return ret;
2008 ✗ ret = ff_amf_read_null(&gbc);
2009 ✗ if (ret)
2010 ✗ return ret;
2011 ✗ if (!strcmp(command, "FCPublish") ||
2012 ✗ !strcmp(command, "publish")) {
2013 ✗ ret = ff_amf_read_string(&gbc, filename,
2014 sizeof(filename), &stringlen);
2015 ✗ if (ret) {
2016 ✗ if (ret == AVERROR(EINVAL))
2017 ✗ av_log(s, AV_LOG_ERROR, "Unable to parse stream name - name too long?\n");
2018 else
2019 ✗ av_log(s, AV_LOG_ERROR, "Unable to parse stream name\n");
2020 ✗ return ret;
2021 }
2022 // check with url
2023 ✗ if (s->filename) {
2024 ✗ pchar = strrchr(s->filename, '/');
2025 ✗ if (!pchar) {
2026 ✗ av_log(s, AV_LOG_WARNING,
2027 "Unable to find / in url %s, bad format\n",
2028 s->filename);
2029 ✗ pchar = s->filename;
2030 }
2031 ✗ pchar++;
2032 ✗ if (strcmp(pchar, filename))
2033 ✗ av_log(s, AV_LOG_WARNING, "Unexpected stream %s, expecting"
2034 " %s\n", filename, pchar);
2035 }
2036 ✗ rt->state = STATE_RECEIVING;
2037 }
2038
2039 ✗ if (!strcmp(command, "FCPublish")) {
2040 ✗ if ((ret = ff_rtmp_packet_create(&spkt, RTMP_SYSTEM_CHANNEL,
2041 RTMP_PT_INVOKE, 0,
2042 RTMP_PKTDATA_DEFAULT_SIZE)) < 0) {
2043 ✗ av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
2044 ✗ return ret;
2045 }
2046 ✗ pp = spkt.data;
2047 ✗ ff_amf_write_string(&pp, "onFCPublish");
2048 ✗ } else if (!strcmp(command, "publish")) {
2049 char statusmsg[sizeof(filename) + 32];
2050 ✗ snprintf(statusmsg, sizeof(statusmsg), "%s is now published", filename);
2051 ✗ ret = write_begin(s);
2052 ✗ if (ret < 0)
2053 ✗ return ret;
2054
2055 // Send onStatus(NetStream.Publish.Start)
2056 ✗ return write_status(s, pkt, "NetStream.Publish.Start",
2057 statusmsg, filename);
2058 ✗ } else if (!strcmp(command, "play")) {
2059 ✗ ret = write_begin(s);
2060 ✗ if (ret < 0)
2061 ✗ return ret;
2062 ✗ rt->state = STATE_SENDING;
2063 ✗ return write_status(s, pkt, "NetStream.Play.Start",
2064 "playing stream", NULL);
2065 } else {
2066 ✗ if ((ret = ff_rtmp_packet_create(&spkt, RTMP_SYSTEM_CHANNEL,
2067 RTMP_PT_INVOKE, 0,
2068 RTMP_PKTDATA_DEFAULT_SIZE)) < 0) {
2069 ✗ av_log(s, AV_LOG_ERROR, "Unable to create response packet\n");
2070 ✗ return ret;
2071 }
2072 ✗ pp = spkt.data;
2073 ✗ ff_amf_write_string(&pp, "_result");
2074 ✗ ff_amf_write_number(&pp, seqnum);
2075 ✗ ff_amf_write_null(&pp);
2076 ✗ if (!strcmp(command, "createStream")) {
2077 ✗ rt->nb_streamid++;
2078 ✗ if (rt->nb_streamid == 0 || rt->nb_streamid == 2)
2079 ✗ rt->nb_streamid++; /* Values 0 and 2 are reserved */
2080 ✗ ff_amf_write_number(&pp, rt->nb_streamid);
2081 /* By now we don't control which streams are removed in
2082 * deleteStream. There is no stream creation control
2083 * if a client creates more than 2^32 - 2 streams. */
2084 }
2085 }
2086 ✗ spkt.size = pp - spkt.data;
2087 ✗ ret = ff_rtmp_packet_write(rt->stream, &spkt, rt->out_chunk_size,
2088 &rt->prev_pkt[1], &rt->nb_prev_pkt[1]);
2089 ✗ ff_rtmp_packet_destroy(&spkt);
2090 ✗ return ret;
2091 }
2092
2093 /**
2094 * Read the AMF_NUMBER response ("_result") to a function call
2095 * (e.g. createStream()). This response should be made up of the AMF_STRING
2096 * "result", a NULL object and then the response encoded as AMF_NUMBER. On a
2097 * successful response, we will return set the value to number (otherwise number
2098 * will not be changed).
2099 *
2100 * @return 0 if reading the value succeeds, negative value otherwise
2101 */
2102 ✗ static int read_number_result(RTMPPacket *pkt, double *number)
2103 {
2104 // We only need to fit "_result" in this.
2105 uint8_t strbuffer[8];
2106 int stringlen;
2107 double numbuffer;
2108 GetByteContext gbc;
2109
2110 ✗ bytestream2_init(&gbc, pkt->data, pkt->size);
2111
2112 // Value 1/4: "_result" as AMF_STRING
2113 ✗ if (ff_amf_read_string(&gbc, strbuffer, sizeof(strbuffer), &stringlen))
2114 ✗ return AVERROR_INVALIDDATA;
2115 ✗ if (strcmp(strbuffer, "_result"))
2116 ✗ return AVERROR_INVALIDDATA;
2117 // Value 2/4: The callee reference number
2118 ✗ if (ff_amf_read_number(&gbc, &numbuffer))
2119 ✗ return AVERROR_INVALIDDATA;
2120 // Value 3/4: Null
2121 ✗ if (ff_amf_read_null(&gbc))
2122 ✗ return AVERROR_INVALIDDATA;
2123 // Value 4/4: The response as AMF_NUMBER
2124 ✗ if (ff_amf_read_number(&gbc, &numbuffer))
2125 ✗ return AVERROR_INVALIDDATA;
2126 else
2127 ✗ *number = numbuffer;
2128
2129 ✗ return 0;
2130 }
2131
2132 ✗ static int handle_invoke_result(URLContext *s, RTMPPacket *pkt)
2133 {
2134 ✗ RTMPContext *rt = s->priv_data;
2135 ✗ char *tracked_method = NULL;
2136 ✗ int ret = 0;
2137
2138 ✗ if ((ret = find_tracked_method(s, pkt, 10, &tracked_method)) < 0)
2139 ✗ return ret;
2140
2141 ✗ if (!tracked_method) {
2142 /* Ignore this reply when the current method is not tracked. */
2143 ✗ return ret;
2144 }
2145
2146 ✗ if (!strcmp(tracked_method, "connect")) {
2147 ✗ if (!rt->is_input) {
2148 ✗ if ((ret = gen_release_stream(s, rt)) < 0)
2149 ✗ goto fail;
2150
2151 ✗ if ((ret = gen_fcpublish_stream(s, rt)) < 0)
2152 ✗ goto fail;
2153 } else {
2154 ✗ if ((ret = gen_window_ack_size(s, rt)) < 0)
2155 ✗ goto fail;
2156 }
2157
2158 ✗ if ((ret = gen_create_stream(s, rt)) < 0)
2159 ✗ goto fail;
2160
2161 ✗ if (rt->is_input) {
2162 /* Send the FCSubscribe command when the name of live
2163 * stream is defined by the user or if it's a live stream. */
2164 ✗ if (rt->subscribe) {
2165 ✗ if ((ret = gen_fcsubscribe_stream(s, rt, rt->subscribe)) < 0)
2166 ✗ goto fail;
2167 ✗ } else if (rt->live == -1) {
2168 ✗ if ((ret = gen_fcsubscribe_stream(s, rt, rt->playpath)) < 0)
2169 ✗ goto fail;
2170 }
2171 }
2172 ✗ } else if (!strcmp(tracked_method, "createStream")) {
2173 double stream_id;
2174 ✗ if (read_number_result(pkt, &stream_id)) {
2175 ✗ av_log(s, AV_LOG_WARNING, "Unexpected reply on connect()\n");
2176 } else {
2177 ✗ rt->stream_id = stream_id;
2178 }
2179
2180 ✗ if (!rt->is_input) {
2181 ✗ if ((ret = gen_publish(s, rt)) < 0)
2182 ✗ goto fail;
2183 } else {
2184 ✗ if (rt->live != -1) {
2185 ✗ if ((ret = gen_get_stream_length(s, rt)) < 0)
2186 ✗ goto fail;
2187 }
2188 ✗ if ((ret = gen_play(s, rt)) < 0)
2189 ✗ goto fail;
2190 ✗ if ((ret = gen_buffer_time(s, rt)) < 0)
2191 ✗ goto fail;
2192 }
2193 ✗ } else if (!strcmp(tracked_method, "getStreamLength")) {
2194 ✗ if (read_number_result(pkt, &rt->duration)) {
2195 ✗ av_log(s, AV_LOG_WARNING, "Unexpected reply on getStreamLength()\n");
2196 }
2197 }
2198
2199 ✗ fail:
2200 ✗ av_free(tracked_method);
2201 ✗ return ret;
2202 }
2203
2204 ✗ static int handle_invoke_status(URLContext *s, RTMPPacket *pkt)
2205 {
2206 ✗ RTMPContext *rt = s->priv_data;
2207 ✗ const uint8_t *data_end = pkt->data + pkt->size;
2208 ✗ const uint8_t *ptr = pkt->data + RTMP_HEADER;
2209 uint8_t tmpstr[256];
2210 int i, t;
2211
2212 ✗ for (i = 0; i < 2; i++) {
2213 ✗ t = ff_amf_tag_size(ptr, data_end);
2214 ✗ if (t < 0)
2215 ✗ return 1;
2216 ✗ ptr += t;
2217 }
2218
2219 ✗ t = ff_amf_get_field_value(ptr, data_end, "level", tmpstr, sizeof(tmpstr));
2220 ✗ if (!t && !strcmp(tmpstr, "error")) {
2221 ✗ t = ff_amf_get_field_value(ptr, data_end,
2222 "description", tmpstr, sizeof(tmpstr));
2223 ✗ if (t || !tmpstr[0])
2224 ✗ t = ff_amf_get_field_value(ptr, data_end, "code",
2225 tmpstr, sizeof(tmpstr));
2226 ✗ if (!t)
2227 ✗ av_log(s, AV_LOG_ERROR, "Server error: %s\n", tmpstr);
2228 ✗ return -1;
2229 }
2230
2231 ✗ t = ff_amf_get_field_value(ptr, data_end, "code", tmpstr, sizeof(tmpstr));
2232 ✗ if (!t && !strcmp(tmpstr, "NetStream.Play.Start")) rt->state = STATE_PLAYING;
2233 ✗ if (!t && !strcmp(tmpstr, "NetStream.Play.Stop")) rt->state = STATE_STOPPED;
2234 ✗ if (!t && !strcmp(tmpstr, "NetStream.Play.UnpublishNotify")) rt->state = STATE_STOPPED;
2235 ✗ if (!t && !strcmp(tmpstr, "NetStream.Publish.Start")) rt->state = STATE_PUBLISHING;
2236 ✗ if (!t && !strcmp(tmpstr, "NetStream.Seek.Notify")) rt->state = STATE_PLAYING;
2237
2238 ✗ return 0;
2239 }
2240
2241 ✗ static int handle_invoke(URLContext *s, RTMPPacket *pkt)
2242 {
2243 ✗ RTMPContext *rt = s->priv_data;
2244 ✗ int ret = 0;
2245
2246 //TODO: check for the messages sent for wrong state?
2247 ✗ if (ff_amf_match_string(pkt->data, pkt->size, "_error")) {
2248 ✗ if ((ret = handle_invoke_error(s, pkt)) < 0)
2249 ✗ return ret;
2250 ✗ } else if (ff_amf_match_string(pkt->data, pkt->size, "_result")) {
2251 ✗ if ((ret = handle_invoke_result(s, pkt)) < 0)
2252 ✗ return ret;
2253 ✗ } else if (ff_amf_match_string(pkt->data, pkt->size, "onStatus")) {
2254 ✗ if ((ret = handle_invoke_status(s, pkt)) < 0)
2255 ✗ return ret;
2256 ✗ } else if (ff_amf_match_string(pkt->data, pkt->size, "onBWDone")) {
2257 ✗ if ((ret = gen_check_bw(s, rt)) < 0)
2258 ✗ return ret;
2259 ✗ } else if (ff_amf_match_string(pkt->data, pkt->size, "releaseStream") ||
2260 ✗ ff_amf_match_string(pkt->data, pkt->size, "FCPublish") ||
2261 ✗ ff_amf_match_string(pkt->data, pkt->size, "publish") ||
2262 ✗ ff_amf_match_string(pkt->data, pkt->size, "play") ||
2263 ✗ ff_amf_match_string(pkt->data, pkt->size, "_checkbw") ||
2264 ✗ ff_amf_match_string(pkt->data, pkt->size, "createStream")) {
2265 ✗ if ((ret = send_invoke_response(s, pkt)) < 0)
2266 ✗ return ret;
2267 }
2268
2269 ✗ return ret;
2270 }
2271
2272 ✗ static int update_offset(RTMPContext *rt, int size)
2273 {
2274 int old_flv_size;
2275
2276 ✗ if (size < 0)
2277 ✗ return AVERROR(EINVAL);
2278
2279 // generate packet header and put data into buffer for FLV demuxer
2280 ✗ if (rt->flv_off < rt->flv_size) {
2281 // There is old unread data in the buffer, thus append at the end
2282 ✗ if (rt->flv_size > INT_MAX - size)
2283 ✗ return AVERROR(ERANGE);
2284 ✗ old_flv_size = rt->flv_size;
2285 ✗ rt->flv_size += size;
2286 } else {
2287 // All data has been read, write the new data at the start of the buffer
2288 ✗ old_flv_size = 0;
2289 ✗ rt->flv_size = size;
2290 ✗ rt->flv_off = 0;
2291 }
2292
2293 ✗ return old_flv_size;
2294 }
2295
2296 ✗ static int append_flv_data(RTMPContext *rt, RTMPPacket *pkt, int skip)
2297 {
2298 int old_flv_size, ret;
2299 PutByteContext pbc;
2300 ✗ const uint8_t *data = pkt->data + skip;
2301 ✗ const int size = pkt->size - skip;
2302 ✗ uint32_t ts = pkt->timestamp;
2303
2304 ✗ if (pkt->type == RTMP_PT_AUDIO) {
2305 ✗ rt->has_audio = 1;
2306 ✗ } else if (pkt->type == RTMP_PT_VIDEO) {
2307 ✗ rt->has_video = 1;
2308 }
2309
2310 ✗ if (size > INT_MAX - 15)
2311 ✗ return AVERROR(ERANGE);
2312 ✗ old_flv_size = update_offset(rt, size + 15);
2313 ✗ if (old_flv_size < 0)
2314 ✗ return old_flv_size;
2315
2316 ✗ if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0) {
2317 ✗ rt->flv_size = rt->flv_off = 0;
2318 ✗ return ret;
2319 }
2320 ✗ bytestream2_init_writer(&pbc, rt->flv_data, rt->flv_size);
2321 ✗ bytestream2_skip_p(&pbc, old_flv_size);
2322 ✗ bytestream2_put_byte(&pbc, pkt->type);
2323 ✗ bytestream2_put_be24(&pbc, size);
2324 ✗ bytestream2_put_be24(&pbc, ts);
2325 ✗ bytestream2_put_byte(&pbc, ts >> 24);
2326 ✗ bytestream2_put_be24(&pbc, 0);
2327 ✗ bytestream2_put_buffer(&pbc, data, size);
2328 ✗ bytestream2_put_be32(&pbc, size + RTMP_HEADER);
2329
2330 ✗ return 0;
2331 }
2332
2333 ✗ static int handle_notify(URLContext *s, RTMPPacket *pkt)
2334 {
2335 ✗ RTMPContext *rt = s->priv_data;
2336 uint8_t commandbuffer[64];
2337 char statusmsg[128];
2338 ✗ int stringlen, ret, skip = 0;
2339 GetByteContext gbc;
2340
2341 ✗ bytestream2_init(&gbc, pkt->data, pkt->size);
2342 ✗ if (ff_amf_read_string(&gbc, commandbuffer, sizeof(commandbuffer),
2343 &stringlen))
2344 ✗ return AVERROR_INVALIDDATA;
2345
2346 ✗ if (!strcmp(commandbuffer, "onMetaData")) {
2347 // metadata properties should be stored in a mixed array
2348 ✗ if (bytestream2_get_byte(&gbc) == AMF_DATA_TYPE_MIXEDARRAY) {
2349 // We have found a metaData Array so flv can determine the streams
2350 // from this.
2351 ✗ rt->received_metadata = 1;
2352 // skip 32-bit max array index
2353 ✗ bytestream2_skip(&gbc, 4);
2354 ✗ while (bytestream2_get_bytes_left(&gbc) > 3) {
2355 ✗ if (ff_amf_get_string(&gbc, statusmsg, sizeof(statusmsg),
2356 &stringlen))
2357 ✗ return AVERROR_INVALIDDATA;
2358 // We do not care about the content of the property (yet).
2359 ✗ stringlen = ff_amf_tag_size(gbc.buffer, gbc.buffer_end);
2360 ✗ if (stringlen < 0)
2361 ✗ return AVERROR_INVALIDDATA;
2362 ✗ bytestream2_skip(&gbc, stringlen);
2363
2364 // The presence of the following properties indicates that the
2365 // respective streams are present.
2366 ✗ if (!strcmp(statusmsg, "videocodecid")) {
2367 ✗ rt->has_video = 1;
2368 }
2369 ✗ if (!strcmp(statusmsg, "audiocodecid")) {
2370 ✗ rt->has_audio = 1;
2371 }
2372 }
2373 ✗ if (bytestream2_get_be24(&gbc) != AMF_END_OF_OBJECT)
2374 ✗ return AVERROR_INVALIDDATA;
2375 }
2376 }
2377
2378 // Skip the @setDataFrame string and validate it is a notification
2379 ✗ if (!strcmp(commandbuffer, "@setDataFrame")) {
2380 ✗ skip = gbc.buffer - pkt->data;
2381 ✗ ret = ff_amf_read_string(&gbc, statusmsg,
2382 sizeof(statusmsg), &stringlen);
2383 ✗ if (ret < 0)
2384 ✗ return AVERROR_INVALIDDATA;
2385 }
2386
2387 ✗ return append_flv_data(rt, pkt, skip);
2388 }
2389
2390 /**
2391 * Parse received packet and possibly perform some action depending on
2392 * the packet contents.
2393 * @return 0 for no errors, negative values for serious errors which prevent
2394 * further communications, positive values for uncritical errors
2395 */
2396 ✗ static int rtmp_parse_result(URLContext *s, RTMPContext *rt, RTMPPacket *pkt)
2397 {
2398 int ret;
2399
2400 #ifdef DEBUG
2401 ff_rtmp_packet_dump(s, pkt);
2402 #endif
2403
2404 ✗ switch (pkt->type) {
2405 ✗ case RTMP_PT_BYTES_READ:
2406 ✗ av_log(s, AV_LOG_TRACE, "received bytes read report\n");
2407 ✗ break;
2408 ✗ case RTMP_PT_CHUNK_SIZE:
2409 ✗ if ((ret = handle_chunk_size(s, pkt)) < 0)
2410 ✗ return ret;
2411 ✗ break;
2412 ✗ case RTMP_PT_USER_CONTROL:
2413 ✗ if ((ret = handle_user_control(s, pkt)) < 0)
2414 ✗ return ret;
2415 ✗ break;
2416 ✗ case RTMP_PT_SET_PEER_BW:
2417 ✗ if ((ret = handle_set_peer_bw(s, pkt)) < 0)
2418 ✗ return ret;
2419 ✗ break;
2420 ✗ case RTMP_PT_WINDOW_ACK_SIZE:
2421 ✗ if ((ret = handle_window_ack_size(s, pkt)) < 0)
2422 ✗ return ret;
2423 ✗ break;
2424 ✗ case RTMP_PT_INVOKE:
2425 ✗ if ((ret = handle_invoke(s, pkt)) < 0)
2426 ✗ return ret;
2427 ✗ break;
2428 ✗ case RTMP_PT_VIDEO:
2429 case RTMP_PT_AUDIO:
2430 case RTMP_PT_METADATA:
2431 case RTMP_PT_NOTIFY:
2432 /* Audio, Video and Metadata packets are parsed in get_packet() */
2433 ✗ break;
2434 ✗ default:
2435 ✗ av_log(s, AV_LOG_VERBOSE, "Unknown packet type received 0x%02X\n", pkt->type);
2436 ✗ break;
2437 }
2438 ✗ return 0;
2439 }
2440
2441 ✗ static int handle_metadata(RTMPContext *rt, RTMPPacket *pkt)
2442 {
2443 int ret, old_flv_size, type;
2444 PutByteContext pbc;
2445 GetByteContext gbc;
2446 uint32_t size;
2447 ✗ uint32_t ts, cts, pts = 0;
2448
2449 ✗ old_flv_size = update_offset(rt, pkt->size);
2450 ✗ if (old_flv_size < 0)
2451 ✗ return old_flv_size;
2452
2453 ✗ if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0) {
2454 ✗ rt->flv_size = rt->flv_off = 0;
2455 ✗ return ret;
2456 }
2457
2458 ✗ bytestream2_init(&gbc, pkt->data, pkt->size);
2459 ✗ bytestream2_init_writer(&pbc, rt->flv_data, rt->flv_size);
2460 ✗ bytestream2_skip_p(&pbc, old_flv_size);
2461
2462 /* copy data while rewriting timestamps */
2463 ✗ ts = pkt->timestamp;
2464
2465 ✗ while (bytestream2_get_bytes_left(&gbc) > RTMP_HEADER) {
2466 ✗ type = bytestream2_get_byte(&gbc);
2467 ✗ size = bytestream2_get_be24(&gbc);
2468 ✗ cts = bytestream2_get_be24(&gbc);
2469 ✗ cts |= bytestream2_get_byte(&gbc) << 24;
2470 ✗ if (!pts)
2471 ✗ pts = cts;
2472 ✗ ts += cts - pts;
2473 ✗ pts = cts;
2474 ✗ if (size + 3 + 4 > bytestream2_get_bytes_left(&gbc))
2475 ✗ break;
2476 ✗ bytestream2_put_byte(&pbc, type);
2477 ✗ bytestream2_put_be24(&pbc, size);
2478 ✗ bytestream2_put_be24(&pbc, ts);
2479 ✗ bytestream2_put_byte(&pbc, ts >> 24);
2480 ✗ bytestream2_copy_buffer(&pbc, &gbc, size + 3);
2481 ✗ bytestream2_skip(&gbc, 4);
2482 ✗ bytestream2_put_be32(&pbc, size + RTMP_HEADER);
2483 }
2484 ✗ if (bytestream2_tell_p(&pbc) != rt->flv_size) {
2485 ✗ av_log(rt, AV_LOG_WARNING, "Incomplete flv packets in "
2486 "RTMP_PT_METADATA packet\n");
2487 ✗ rt->flv_size = bytestream2_tell_p(&pbc);
2488 }
2489
2490 ✗ return 0;
2491 }
2492
2493 /**
2494 * Interact with the server by receiving and sending RTMP packets until
2495 * there is some significant data (media data or expected status notification).
2496 *
2497 * @param s reading context
2498 * @param for_header non-zero value tells function to work until it
2499 * gets notification from the server that playing has been started,
2500 * otherwise function will work until some media data is received (or
2501 * an error happens)
2502 * @return 0 for successful operation, negative value in case of error
2503 */
2504 ✗ static int get_packet(URLContext *s, int for_header)
2505 {
2506 ✗ RTMPContext *rt = s->priv_data;
2507 int ret;
2508
2509 ✗ if (rt->state == STATE_STOPPED)
2510 ✗ return AVERROR_EOF;
2511
2512 ✗ for (;;) {
2513 ✗ RTMPPacket rpkt = { 0 };
2514 ✗ if ((ret = ff_rtmp_packet_read(rt->stream, &rpkt,
2515 rt->in_chunk_size, &rt->prev_pkt[0],
2516 &rt->nb_prev_pkt[0])) <= 0) {
2517 ✗ if (ret == 0) {
2518 ✗ return AVERROR(EAGAIN);
2519 } else {
2520 ✗ return AVERROR(EIO);
2521 }
2522 }
2523
2524 // Track timestamp for later use
2525 ✗ rt->last_timestamp = rpkt.timestamp;
2526
2527 ✗ rt->bytes_read += ret;
2528 ✗ if (rt->bytes_read - rt->last_bytes_read > rt->receive_report_size) {
2529 ✗ av_log(s, AV_LOG_DEBUG, "Sending bytes read report\n");
2530 ✗ if ((ret = gen_bytes_read(s, rt, rpkt.timestamp + 1)) < 0) {
2531 ✗ ff_rtmp_packet_destroy(&rpkt);
2532 ✗ return ret;
2533 }
2534 ✗ rt->last_bytes_read = rt->bytes_read;
2535 }
2536
2537 ✗ ret = rtmp_parse_result(s, rt, &rpkt);
2538
2539 // At this point we must check if we are in the seek state and continue
2540 // with the next packet. handle_invoke will get us out of this state
2541 // when the right message is encountered
2542 ✗ if (rt->state == STATE_SEEKING) {
2543 ✗ ff_rtmp_packet_destroy(&rpkt);
2544 // We continue, let the natural flow of things happen:
2545 // AVERROR(EAGAIN) or handle_invoke gets us out of here
2546 ✗ continue;
2547 }
2548
2549 ✗ if (ret < 0) {//serious error in current packet
2550 ✗ ff_rtmp_packet_destroy(&rpkt);
2551 ✗ return ret;
2552 }
2553 ✗ if (rt->do_reconnect && for_header) {
2554 ✗ ff_rtmp_packet_destroy(&rpkt);
2555 ✗ return 0;
2556 }
2557 ✗ if (rt->state == STATE_STOPPED) {
2558 ✗ ff_rtmp_packet_destroy(&rpkt);
2559 ✗ return AVERROR_EOF;
2560 }
2561 ✗ if (for_header && (rt->state == STATE_PLAYING ||
2562 ✗ rt->state == STATE_PUBLISHING ||
2563 ✗ rt->state == STATE_SENDING ||
2564 ✗ rt->state == STATE_RECEIVING)) {
2565 ✗ ff_rtmp_packet_destroy(&rpkt);
2566 ✗ return 0;
2567 }
2568 ✗ if (!rpkt.size || !rt->is_input) {
2569 ✗ ff_rtmp_packet_destroy(&rpkt);
2570 ✗ continue;
2571 }
2572 ✗ if (rpkt.type == RTMP_PT_VIDEO || rpkt.type == RTMP_PT_AUDIO) {
2573 ✗ ret = append_flv_data(rt, &rpkt, 0);
2574 ✗ ff_rtmp_packet_destroy(&rpkt);
2575 ✗ return ret;
2576 ✗ } else if (rpkt.type == RTMP_PT_NOTIFY) {
2577 ✗ ret = handle_notify(s, &rpkt);
2578 ✗ ff_rtmp_packet_destroy(&rpkt);
2579 ✗ return ret;
2580 ✗ } else if (rpkt.type == RTMP_PT_METADATA) {
2581 ✗ ret = handle_metadata(rt, &rpkt);
2582 ✗ ff_rtmp_packet_destroy(&rpkt);
2583 ✗ return ret;
2584 }
2585 ✗ ff_rtmp_packet_destroy(&rpkt);
2586 }
2587 }
2588
2589 ✗ static int rtmp_close(URLContext *h)
2590 {
2591 ✗ RTMPContext *rt = h->priv_data;
2592 ✗ int ret = 0, i, j;
2593
2594 ✗ if (!rt->is_input) {
2595 ✗ rt->flv_data = NULL;
2596 ✗ if (rt->out_pkt.size)
2597 ✗ ff_rtmp_packet_destroy(&rt->out_pkt);
2598 ✗ if (rt->state > STATE_FCPUBLISH)
2599 ✗ ret = gen_fcunpublish_stream(h, rt);
2600 }
2601 ✗ if (rt->state > STATE_HANDSHAKED)
2602 ✗ ret = gen_delete_stream(h, rt);
2603 ✗ for (i = 0; i < 2; i++) {
2604 ✗ for (j = 0; j < rt->nb_prev_pkt[i]; j++)
2605 ✗ ff_rtmp_packet_destroy(&rt->prev_pkt[i][j]);
2606 ✗ av_freep(&rt->prev_pkt[i]);
2607 }
2608
2609 ✗ free_tracked_methods(rt);
2610 ✗ av_freep(&rt->flv_data);
2611 ✗ ffurl_closep(&rt->stream);
2612 ✗ return ret;
2613 }
2614
2615 /**
2616 * Insert a fake onMetadata packet into the FLV stream to notify the FLV
2617 * demuxer about the duration of the stream.
2618 *
2619 * This should only be done if there was no real onMetadata packet sent by the
2620 * server at the start of the stream and if we were able to retrieve a valid
2621 * duration via a getStreamLength call.
2622 *
2623 * @return 0 for successful operation, negative value in case of error
2624 */
2625 ✗ static int inject_fake_duration_metadata(RTMPContext *rt)
2626 {
2627 // We need to insert the metadata packet directly after the FLV
2628 // header, i.e. we need to move all other already read data by the
2629 // size of our fake metadata packet.
2630
2631 uint8_t* p;
2632 // Keep old flv_data pointer
2633 ✗ uint8_t* old_flv_data = rt->flv_data;
2634 // Allocate a new flv_data pointer with enough space for the additional package
2635 ✗ if (!(rt->flv_data = av_malloc(rt->flv_size + 55))) {
2636 ✗ rt->flv_data = old_flv_data;
2637 ✗ return AVERROR(ENOMEM);
2638 }
2639
2640 // Copy FLV header
2641 ✗ memcpy(rt->flv_data, old_flv_data, 13);
2642 // Copy remaining packets
2643 ✗ memcpy(rt->flv_data + 13 + 55, old_flv_data + 13, rt->flv_size - 13);
2644 // Increase the size by the injected packet
2645 ✗ rt->flv_size += 55;
2646 // Delete the old FLV data
2647 ✗ av_freep(&old_flv_data);
2648
2649 ✗ p = rt->flv_data + 13;
2650 ✗ bytestream_put_byte(&p, FLV_TAG_TYPE_META);
2651 ✗ bytestream_put_be24(&p, 40); // size of data part (sum of all parts below)
2652 ✗ bytestream_put_be24(&p, 0); // timestamp
2653 ✗ bytestream_put_be32(&p, 0); // reserved
2654
2655 // first event name as a string
2656 ✗ bytestream_put_byte(&p, AMF_DATA_TYPE_STRING);
2657 // "onMetaData" as AMF string
2658 ✗ bytestream_put_be16(&p, 10);
2659 ✗ bytestream_put_buffer(&p, "onMetaData", 10);
2660
2661 // mixed array (hash) with size and string/type/data tuples
2662 ✗ bytestream_put_byte(&p, AMF_DATA_TYPE_MIXEDARRAY);
2663 ✗ bytestream_put_be32(&p, 1); // metadata_count
2664
2665 // "duration" as AMF string
2666 ✗ bytestream_put_be16(&p, 8);
2667 ✗ bytestream_put_buffer(&p, "duration", 8);
2668 ✗ bytestream_put_byte(&p, AMF_DATA_TYPE_NUMBER);
2669 ✗ bytestream_put_be64(&p, av_double2int(rt->duration));
2670
2671 // Finalise object
2672 ✗ bytestream_put_be16(&p, 0); // Empty string
2673 ✗ bytestream_put_byte(&p, AMF_END_OF_OBJECT);
2674 ✗ bytestream_put_be32(&p, 40 + RTMP_HEADER); // size of data part (sum of all parts above)
2675
2676 ✗ return 0;
2677 }
2678
2679 /**
2680 * Open RTMP connection and verify that the stream can be played.
2681 *
2682 * URL syntax: rtmp://server[:port][/app][/playpath]
2683 * where 'app' is first one or two directories in the path
2684 * (e.g. /ondemand/, /flash/live/, etc.)
2685 * and 'playpath' is a file name (the rest of the path,
2686 * may be prefixed with "mp4:")
2687 */
2688 ✗ static int rtmp_open(URLContext *s, const char *uri, int flags, AVDictionary **opts)
2689 {
2690 ✗ RTMPContext *rt = s->priv_data;
2691 char proto[8], hostname[256], path[1024], auth[100], *fname;
2692 char *old_app, *qmark, *n, fname_buffer[1024];
2693 uint8_t buf[2048];
2694 int port;
2695 int ret;
2696
2697 ✗ if (rt->listen_timeout > 0)
2698 ✗ rt->listen = 1;
2699
2700 ✗ rt->is_input = !(flags & AVIO_FLAG_WRITE);
2701
2702 ✗ av_url_split(proto, sizeof(proto), auth, sizeof(auth),
2703 hostname, sizeof(hostname), &port,
2704 ✗ path, sizeof(path), s->filename);
2705
2706 ✗ n = strchr(path, ' ');
2707 ✗ if (n) {
2708 ✗ av_log(s, AV_LOG_WARNING,
2709 "Detected librtmp style URL parameters, these aren't supported "
2710 "by the libavformat internal RTMP handler currently enabled. "
2711 "See the documentation for the correct way to pass parameters.\n");
2712 ✗ *n = '\0'; // Trim not supported part
2713 }
2714
2715 ✗ if (auth[0]) {
2716 ✗ char *ptr = strchr(auth, ':');
2717 ✗ if (ptr) {
2718 ✗ *ptr = '\0';
2719 ✗ av_strlcpy(rt->username, auth, sizeof(rt->username));
2720 ✗ av_strlcpy(rt->password, ptr + 1, sizeof(rt->password));
2721 }
2722 }
2723
2724 ✗ if (rt->listen && strcmp(proto, "rtmp")) {
2725 ✗ av_log(s, AV_LOG_ERROR, "rtmp_listen not available for %s\n",
2726 proto);
2727 ✗ return AVERROR(EINVAL);
2728 }
2729 ✗ if (!strcmp(proto, "rtmpt") || !strcmp(proto, "rtmpts")) {
2730 ✗ if (!strcmp(proto, "rtmpts"))
2731 ✗ av_dict_set(opts, "ffrtmphttp_tls", "1", AV_DICT_MATCH_CASE);
2732
2733 /* open the http tunneling connection */
2734 ✗ ff_url_join(buf, sizeof(buf), "ffrtmphttp", NULL, hostname, port, NULL);
2735 ✗ } else if (!strcmp(proto, "rtmps")) {
2736 /* open the tls connection */
2737 ✗ if (port < 0)
2738 ✗ port = RTMPS_DEFAULT_PORT;
2739 ✗ ff_url_join(buf, sizeof(buf), "tls", NULL, hostname, port, NULL);
2740 ✗ } else if (!strcmp(proto, "rtmpe") || (!strcmp(proto, "rtmpte"))) {
2741 ✗ if (!strcmp(proto, "rtmpte"))
2742 ✗ av_dict_set(opts, "ffrtmpcrypt_tunneling", "1", 1);
2743
2744 /* open the encrypted connection */
2745 ✗ ff_url_join(buf, sizeof(buf), "ffrtmpcrypt", NULL, hostname, port, NULL);
2746 ✗ rt->encrypted = 1;
2747 } else {
2748 /* open the tcp connection */
2749 ✗ if (port < 0)
2750 ✗ port = RTMP_DEFAULT_PORT;
2751 ✗ if (rt->listen)
2752 ✗ ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port,
2753 "?listen&listen_timeout=%d&tcp_nodelay=%d",
2754 ✗ rt->listen_timeout < 0 ? -1 : rt->listen_timeout * 1000,
2755 rt->tcp_nodelay);
2756 else
2757 ✗ ff_url_join(buf, sizeof(buf), "tcp", NULL, hostname, port, "?tcp_nodelay=%d", rt->tcp_nodelay);
2758 }
2759
2760 ✗ reconnect:
2761 ✗ if ((ret = ffurl_open_whitelist(&rt->stream, buf, AVIO_FLAG_READ_WRITE,
2762 ✗ &s->interrupt_callback, opts,
2763 s->protocol_whitelist, s->protocol_blacklist, s)) < 0) {
2764 ✗ av_log(s , AV_LOG_ERROR, "Cannot open connection %s\n", buf);
2765 ✗ goto fail;
2766 }
2767
2768 ✗ if (rt->swfverify) {
2769 ✗ if ((ret = rtmp_calc_swfhash(s)) < 0)
2770 ✗ goto fail;
2771 }
2772
2773 ✗ rt->state = STATE_START;
2774 ✗ if (!rt->listen && (ret = rtmp_handshake(s, rt)) < 0)
2775 ✗ goto fail;
2776 ✗ if (rt->listen && (ret = rtmp_server_handshake(s, rt)) < 0)
2777 ✗ goto fail;
2778
2779 ✗ rt->out_chunk_size = 128;
2780 ✗ rt->in_chunk_size = 128; // Probably overwritten later
2781 ✗ rt->state = STATE_HANDSHAKED;
2782
2783 // Keep the application name when it has been defined by the user.
2784 ✗ old_app = rt->app;
2785
2786 ✗ rt->app = av_malloc(APP_MAX_LENGTH);
2787 ✗ if (!rt->app) {
2788 ✗ ret = AVERROR(ENOMEM);
2789 ✗ goto fail;
2790 }
2791
2792 //extract "app" part from path
2793 ✗ qmark = strchr(path, '?');
2794 ✗ if (qmark && strstr(qmark, "slist=")) {
2795 char* amp;
2796 // After slist we have the playpath, the full path is used as app
2797 ✗ av_strlcpy(rt->app, path + 1, APP_MAX_LENGTH);
2798 ✗ fname = strstr(path, "slist=") + 6;
2799 // Strip any further query parameters from fname
2800 ✗ amp = strchr(fname, '&');
2801 ✗ if (amp) {
2802 ✗ av_strlcpy(fname_buffer, fname, FFMIN(amp - fname + 1,
2803 sizeof(fname_buffer)));
2804 ✗ fname = fname_buffer;
2805 }
2806 ✗ } else if (!strncmp(path, "/ondemand/", 10)) {
2807 ✗ fname = path + 10;
2808 ✗ memcpy(rt->app, "ondemand", 9);
2809 } else {
2810 ✗ char *next = *path ? path + 1 : path;
2811 ✗ char *p = strchr(next, '/');
2812 ✗ if (!p) {
2813 ✗ if (old_app) {
2814 // If name of application has been defined by the user, assume that
2815 // playpath is provided in the URL
2816 ✗ fname = next;
2817 } else {
2818 ✗ fname = NULL;
2819 ✗ av_strlcpy(rt->app, next, APP_MAX_LENGTH);
2820 }
2821 } else {
2822 // make sure we do not mismatch a playpath for an application instance
2823 ✗ char *c = strchr(p + 1, ':');
2824 ✗ fname = strchr(p + 1, '/');
2825 ✗ if (!fname || (c && c < fname)) {
2826 ✗ fname = p + 1;
2827 ✗ av_strlcpy(rt->app, path + 1, FFMIN(p - path, APP_MAX_LENGTH));
2828 } else {
2829 ✗ fname++;
2830 ✗ av_strlcpy(rt->app, path + 1, FFMIN(fname - path - 1, APP_MAX_LENGTH));
2831 }
2832 }
2833 }
2834
2835 ✗ if (old_app) {
2836 // The name of application has been defined by the user, override it.
2837 ✗ if (strlen(old_app) >= APP_MAX_LENGTH) {
2838 ✗ ret = AVERROR(EINVAL);
2839 ✗ goto fail;
2840 }
2841 ✗ av_free(rt->app);
2842 ✗ rt->app = old_app;
2843 }
2844
2845 ✗ if (!rt->playpath) {
2846 ✗ int max_len = 1;
2847 ✗ if (fname)
2848 ✗ max_len = strlen(fname) + 5; // add prefix "mp4:"
2849 ✗ rt->playpath = av_malloc(max_len);
2850 ✗ if (!rt->playpath) {
2851 ✗ ret = AVERROR(ENOMEM);
2852 ✗ goto fail;
2853 }
2854
2855 ✗ if (fname) {
2856 ✗ int len = strlen(fname);
2857 ✗ if (!strchr(fname, ':') && len >= 4 &&
2858 ✗ (!strcmp(fname + len - 4, ".f4v") ||
2859 ✗ !strcmp(fname + len - 4, ".mp4"))) {
2860 ✗ memcpy(rt->playpath, "mp4:", 5);
2861 } else {
2862 ✗ if (len >= 4 && !strcmp(fname + len - 4, ".flv"))
2863 ✗ fname[len - 4] = '\0';
2864 ✗ rt->playpath[0] = 0;
2865 }
2866 ✗ av_strlcat(rt->playpath, fname, max_len);
2867 } else {
2868 ✗ rt->playpath[0] = '\0';
2869 }
2870 }
2871
2872 ✗ if (!rt->tcurl) {
2873 ✗ rt->tcurl = av_malloc(TCURL_MAX_LENGTH);
2874 ✗ if (!rt->tcurl) {
2875 ✗ ret = AVERROR(ENOMEM);
2876 ✗ goto fail;
2877 }
2878 ✗ ff_url_join(rt->tcurl, TCURL_MAX_LENGTH, proto, NULL, hostname,
2879 port, "/%s", rt->app);
2880 }
2881
2882 ✗ if (!rt->flashver) {
2883 ✗ rt->flashver = av_malloc(FLASHVER_MAX_LENGTH);
2884 ✗ if (!rt->flashver) {
2885 ✗ ret = AVERROR(ENOMEM);
2886 ✗ goto fail;
2887 }
2888 ✗ if (rt->is_input) {
2889 ✗ snprintf(rt->flashver, FLASHVER_MAX_LENGTH, "%s %d,%d,%d,%d",
2890 RTMP_CLIENT_PLATFORM, RTMP_CLIENT_VER1, RTMP_CLIENT_VER2,
2891 RTMP_CLIENT_VER3, RTMP_CLIENT_VER4);
2892 } else {
2893 ✗ snprintf(rt->flashver, FLASHVER_MAX_LENGTH,
2894 "FMLE/3.0 (compatible; %s)", LIBAVFORMAT_IDENT);
2895 }
2896 }
2897 ✗ if ( strlen(rt->flashver) > FLASHVER_MAX_LENGTH
2898 ✗ || strlen(rt->tcurl ) > TCURL_MAX_LENGTH
2899 ) {
2900 ✗ ret = AVERROR(EINVAL);
2901 ✗ goto fail;
2902 }
2903
2904 ✗ rt->receive_report_size = 1048576;
2905 ✗ rt->bytes_read = 0;
2906 ✗ rt->has_audio = 0;
2907 ✗ rt->has_video = 0;
2908 ✗ rt->received_metadata = 0;
2909 ✗ rt->last_bytes_read = 0;
2910 ✗ rt->max_sent_unacked = 2500000;
2911 ✗ rt->duration = 0;
2912
2913 ✗ av_log(s, AV_LOG_DEBUG, "Proto = %s, path = %s, app = %s, fname = %s\n",
2914 proto, path, rt->app, rt->playpath);
2915 ✗ if (!rt->listen) {
2916 ✗ if ((ret = gen_connect(s, rt)) < 0)
2917 ✗ goto fail;
2918 } else {
2919 ✗ if ((ret = read_connect(s, s->priv_data)) < 0)
2920 ✗ goto fail;
2921 }
2922
2923 do {
2924 ✗ ret = get_packet(s, 1);
2925 ✗ } while (ret == AVERROR(EAGAIN));
2926 ✗ if (ret < 0)
2927 ✗ goto fail;
2928
2929 ✗ if (rt->do_reconnect) {
2930 int i;
2931 ✗ ffurl_closep(&rt->stream);
2932 ✗ rt->do_reconnect = 0;
2933 ✗ rt->nb_invokes = 0;
2934 ✗ for (i = 0; i < 2; i++)
2935 ✗ memset(rt->prev_pkt[i], 0,
2936 ✗ sizeof(**rt->prev_pkt) * rt->nb_prev_pkt[i]);
2937 ✗ free_tracked_methods(rt);
2938 ✗ goto reconnect;
2939 }
2940
2941 ✗ if (rt->is_input) {
2942 // generate FLV header for demuxer
2943 ✗ rt->flv_size = 13;
2944 ✗ if ((ret = av_reallocp(&rt->flv_data, rt->flv_size)) < 0)
2945 ✗ goto fail;
2946 ✗ rt->flv_off = 0;
2947 ✗ memcpy(rt->flv_data, "FLV\1\0\0\0\0\011\0\0\0\0", rt->flv_size);
2948
2949 // Read packets until we reach the first A/V packet or read metadata.
2950 // If there was a metadata package in front of the A/V packets, we can
2951 // build the FLV header from this. If we do not receive any metadata,
2952 // the FLV decoder will allocate the needed streams when their first
2953 // audio or video packet arrives.
2954 ✗ while (!rt->has_audio && !rt->has_video && !rt->received_metadata) {
2955 ✗ if ((ret = get_packet(s, 0)) < 0)
2956 ✗ goto fail;
2957 }
2958
2959 // Either after we have read the metadata or (if there is none) the
2960 // first packet of an A/V stream, we have a better knowledge about the
2961 // streams, so set the FLV header accordingly.
2962 ✗ if (rt->has_audio) {
2963 ✗ rt->flv_data[4] |= FLV_HEADER_FLAG_HASAUDIO;
2964 }
2965 ✗ if (rt->has_video) {
2966 ✗ rt->flv_data[4] |= FLV_HEADER_FLAG_HASVIDEO;
2967 }
2968
2969 // If we received the first packet of an A/V stream and no metadata but
2970 // the server returned a valid duration, create a fake metadata packet
2971 // to inform the FLV decoder about the duration.
2972 ✗ if (!rt->received_metadata && rt->duration > 0) {
2973 ✗ if ((ret = inject_fake_duration_metadata(rt)) < 0)
2974 ✗ goto fail;
2975 }
2976 } else {
2977 ✗ rt->flv_size = 0;
2978 ✗ rt->flv_data = NULL;
2979 ✗ rt->flv_off = 0;
2980 ✗ rt->skip_bytes = 13;
2981 }
2982
2983 ✗ s->max_packet_size = rt->stream->max_packet_size;
2984 ✗ s->is_streamed = 1;
2985 ✗ return 0;
2986
2987 ✗ fail:
2988 ✗ rtmp_close(s);
2989 ✗ return ret;
2990 }
2991
2992 ✗ static int rtmp_read(URLContext *s, uint8_t *buf, int size)
2993 {
2994 ✗ RTMPContext *rt = s->priv_data;
2995 ✗ int orig_size = size;
2996 int ret;
2997
2998 ✗ while (size > 0) {
2999 ✗ int data_left = rt->flv_size - rt->flv_off;
3000
3001 ✗ if (data_left >= size) {
3002 ✗ memcpy(buf, rt->flv_data + rt->flv_off, size);
3003 ✗ rt->flv_off += size;
3004 ✗ return orig_size;
3005 }
3006 ✗ if (data_left > 0) {
3007 ✗ memcpy(buf, rt->flv_data + rt->flv_off, data_left);
3008 ✗ buf += data_left;
3009 ✗ size -= data_left;
3010 ✗ rt->flv_off = rt->flv_size;
3011 ✗ return data_left;
3012 }
3013 ✗ if ((ret = get_packet(s, 0)) < 0)
3014 ✗ return ret;
3015 }
3016 ✗ return orig_size;
3017 }
3018
3019 ✗ static int64_t rtmp_seek(void *opaque, int stream_index, int64_t timestamp,
3020 int flags)
3021 {
3022 ✗ URLContext *s = opaque;
3023 ✗ RTMPContext *rt = s->priv_data;
3024 int ret;
3025 ✗ av_log(s, AV_LOG_DEBUG,
3026 "Seek on stream index %d at timestamp %"PRId64" with flags %08x\n",
3027 stream_index, timestamp, flags);
3028 ✗ if ((ret = gen_seek(s, rt, timestamp)) < 0) {
3029 ✗ av_log(s, AV_LOG_ERROR,
3030 "Unable to send seek command on stream index %d at timestamp "
3031 "%"PRId64" with flags %08x\n",
3032 stream_index, timestamp, flags);
3033 ✗ return ret;
3034 }
3035 ✗ rt->flv_off = rt->flv_size;
3036 ✗ rt->state = STATE_SEEKING;
3037 ✗ return timestamp;
3038 }
3039
3040 ✗ static int rtmp_pause(void *opaque, int pause)
3041 {
3042 ✗ URLContext *s = opaque;
3043 ✗ RTMPContext *rt = s->priv_data;
3044 int ret;
3045 ✗ av_log(s, AV_LOG_DEBUG, "Pause at timestamp %d\n",
3046 rt->last_timestamp);
3047 ✗ if ((ret = gen_pause(s, rt, pause, rt->last_timestamp)) < 0) {
3048 ✗ av_log(s, AV_LOG_ERROR, "Unable to send pause command at timestamp %d\n",
3049 rt->last_timestamp);
3050 ✗ return ret;
3051 }
3052 ✗ return 0;
3053 }
3054
3055 ✗ static int rtmp_write(URLContext *s, const uint8_t *buf, int size)
3056 {
3057 ✗ RTMPContext *rt = s->priv_data;
3058 ✗ int size_temp = size;
3059 int pktsize, pkttype, copy;
3060 uint32_t ts;
3061 ✗ const uint8_t *buf_temp = buf;
3062 uint8_t c;
3063 int ret;
3064
3065 do {
3066 ✗ if (rt->skip_bytes) {
3067 ✗ int skip = FFMIN(rt->skip_bytes, size_temp);
3068 ✗ buf_temp += skip;
3069 ✗ size_temp -= skip;
3070 ✗ rt->skip_bytes -= skip;
3071 ✗ continue;
3072 }
3073
3074 ✗ if (rt->flv_header_bytes < RTMP_HEADER) {
3075 ✗ const uint8_t *header = rt->flv_header;
3076 ✗ int channel = RTMP_AUDIO_CHANNEL;
3077
3078 ✗ copy = FFMIN(RTMP_HEADER - rt->flv_header_bytes, size_temp);
3079 ✗ bytestream_get_buffer(&buf_temp, rt->flv_header + rt->flv_header_bytes, copy);
3080 ✗ rt->flv_header_bytes += copy;
3081 ✗ size_temp -= copy;
3082 ✗ if (rt->flv_header_bytes < RTMP_HEADER)
3083 ✗ break;
3084
3085 ✗ pkttype = bytestream_get_byte(&header);
3086 ✗ pktsize = bytestream_get_be24(&header);
3087 ✗ ts = bytestream_get_be24(&header);
3088 ✗ ts |= bytestream_get_byte(&header) << 24;
3089 ✗ bytestream_get_be24(&header);
3090 ✗ rt->flv_size = pktsize;
3091
3092 ✗ if (pkttype == RTMP_PT_VIDEO)
3093 ✗ channel = RTMP_VIDEO_CHANNEL;
3094
3095 ✗ if (((pkttype == RTMP_PT_VIDEO || pkttype == RTMP_PT_AUDIO) && ts == 0) ||
3096 pkttype == RTMP_PT_NOTIFY) {
3097 ✗ if ((ret = ff_rtmp_check_alloc_array(&rt->prev_pkt[1],
3098 &rt->nb_prev_pkt[1],
3099 channel)) < 0)
3100 ✗ return ret;
3101 // Force sending a full 12 bytes header by clearing the
3102 // channel id, to make it not match a potential earlier
3103 // packet in the same channel.
3104 ✗ rt->prev_pkt[1][channel].channel_id = 0;
3105 }
3106
3107 //this can be a big packet, it's better to send it right here
3108 ✗ if ((ret = ff_rtmp_packet_create(&rt->out_pkt, channel,
3109 pkttype, ts, pktsize)) < 0)
3110 ✗ return ret;
3111
3112 // If rt->listen, then we're running as a a server and should
3113 // use the ID that we've sent in Stream Begin and in the
3114 // _result to createStream.
3115 // Otherwise, we're running as a client and should use the ID
3116 // that we've received in the createStream from the server.
3117 ✗ rt->out_pkt.extra = (rt->listen) ? rt->nb_streamid : rt->stream_id;
3118 ✗ rt->flv_data = rt->out_pkt.data;
3119 }
3120
3121 ✗ copy = FFMIN(rt->flv_size - rt->flv_off, size_temp);
3122 ✗ bytestream_get_buffer(&buf_temp, rt->flv_data + rt->flv_off, copy);
3123 ✗ rt->flv_off += copy;
3124 ✗ size_temp -= copy;
3125
3126 ✗ if (rt->flv_off == rt->flv_size) {
3127 ✗ rt->skip_bytes = 4;
3128
3129 ✗ if (rt->out_pkt.type == RTMP_PT_NOTIFY) {
3130 // For onMetaData and |RtmpSampleAccess packets, we want
3131 // @setDataFrame prepended to the packet before it gets sent.
3132 // However, not all RTMP_PT_NOTIFY packets (e.g., onTextData
3133 // and onCuePoint).
3134 uint8_t commandbuffer[64];
3135 ✗ int stringlen = 0;
3136 GetByteContext gbc;
3137
3138 ✗ bytestream2_init(&gbc, rt->flv_data, rt->flv_size);
3139 ✗ if (!ff_amf_read_string(&gbc, commandbuffer, sizeof(commandbuffer),
3140 &stringlen)) {
3141 ✗ if (!strcmp(commandbuffer, "onMetaData") ||
3142 ✗ !strcmp(commandbuffer, "|RtmpSampleAccess")) {
3143 uint8_t *ptr;
3144 ✗ if ((ret = av_reallocp(&rt->out_pkt.data, rt->out_pkt.size + 16)) < 0) {
3145 ✗ rt->flv_size = rt->flv_off = rt->flv_header_bytes = 0;
3146 ✗ return ret;
3147 }
3148 ✗ memmove(rt->out_pkt.data + 16, rt->out_pkt.data, rt->out_pkt.size);
3149 ✗ rt->out_pkt.size += 16;
3150 ✗ ptr = rt->out_pkt.data;
3151 ✗ ff_amf_write_string(&ptr, "@setDataFrame");
3152 }
3153 }
3154 }
3155
3156 ✗ if ((ret = rtmp_send_packet(rt, &rt->out_pkt, 0)) < 0)
3157 ✗ return ret;
3158 ✗ rt->flv_size = 0;
3159 ✗ rt->flv_off = 0;
3160 ✗ rt->flv_header_bytes = 0;
3161 ✗ rt->flv_nb_packets++;
3162 }
3163 ✗ } while (buf_temp - buf < size);
3164
3165 ✗ if (rt->flv_nb_packets < rt->flush_interval)
3166 ✗ return size;
3167 ✗ rt->flv_nb_packets = 0;
3168
3169 /* set stream into nonblocking mode */
3170 ✗ rt->stream->flags |= AVIO_FLAG_NONBLOCK;
3171
3172 /* try to read one byte from the stream */
3173 ✗ ret = ffurl_read(rt->stream, &c, 1);
3174
3175 /* switch the stream back into blocking mode */
3176 ✗ rt->stream->flags &= ~AVIO_FLAG_NONBLOCK;
3177
3178 ✗ if (ret == AVERROR(EAGAIN)) {
3179 /* no incoming data to handle */
3180 ✗ return size;
3181 ✗ } else if (ret < 0) {
3182 ✗ return ret;
3183 ✗ } else if (ret == 1) {
3184 ✗ RTMPPacket rpkt = { 0 };
3185
3186 ✗ if ((ret = ff_rtmp_packet_read_internal(rt->stream, &rpkt,
3187 rt->in_chunk_size,
3188 &rt->prev_pkt[0],
3189 &rt->nb_prev_pkt[0], c)) <= 0)
3190 ✗ return ret;
3191
3192 ✗ if ((ret = rtmp_parse_result(s, rt, &rpkt)) < 0)
3193 ✗ return ret;
3194
3195 ✗ ff_rtmp_packet_destroy(&rpkt);
3196 }
3197
3198 ✗ return size;
3199 }
3200
3201 #define OFFSET(x) offsetof(RTMPContext, x)
3202 #define DEC AV_OPT_FLAG_DECODING_PARAM
3203 #define ENC AV_OPT_FLAG_ENCODING_PARAM
3204
3205 static const AVOption rtmp_options[] = {
3206 {"rtmp_app", "Name of application to connect to on the RTMP server", OFFSET(app), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
3207 {"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},
3208 {"rtmp_conn", "Append arbitrary AMF data to the Connect message", OFFSET(conn), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
3209 {"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},
3210 {"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},
3211 {"rtmp_enhanced_codecs", "Specify the codec(s) to use in an enhanced rtmp live stream", OFFSET(enhanced_codecs), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, ENC},
3212 {"rtmp_live", "Specify that the media is a live stream.", OFFSET(live), AV_OPT_TYPE_INT, {.i64 = -2}, INT_MIN, INT_MAX, DEC, .unit = "rtmp_live"},
3213 {"any", "both", 0, AV_OPT_TYPE_CONST, {.i64 = -2}, 0, 0, DEC, .unit = "rtmp_live"},
3214 {"live", "live stream", 0, AV_OPT_TYPE_CONST, {.i64 = -1}, 0, 0, DEC, .unit = "rtmp_live"},
3215 {"recorded", "recorded stream", 0, AV_OPT_TYPE_CONST, {.i64 = 0}, 0, 0, DEC, .unit = "rtmp_live"},
3216 {"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},
3217 {"rtmp_playpath", "Stream identifier to play or to publish", OFFSET(playpath), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC|ENC},
3218 {"rtmp_subscribe", "Name of live stream to subscribe to. Defaults to rtmp_playpath.", OFFSET(subscribe), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
3219 {"rtmp_swfhash", "SHA256 hash of the decompressed SWF file (32 bytes).", OFFSET(swfhash), AV_OPT_TYPE_BINARY, .flags = DEC},
3220 {"rtmp_swfsize", "Size of the decompressed SWF file, required for SWFVerification.", OFFSET(swfsize), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, DEC},
3221 {"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},
3222 {"rtmp_swfverify", "URL to player swf file, compute hash/size automatically.", OFFSET(swfverify), AV_OPT_TYPE_STRING, {.str = NULL }, 0, 0, DEC},
3223 {"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},
3224 {"rtmp_listen", "Listen for incoming rtmp connections", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC, .unit = "rtmp_listen" },
3225 {"listen", "Listen for incoming rtmp connections", OFFSET(listen), AV_OPT_TYPE_INT, {.i64 = 0}, INT_MIN, INT_MAX, DEC, .unit = "rtmp_listen" },
3226 {"tcp_nodelay", "Use TCP_NODELAY to disable Nagle's algorithm", OFFSET(tcp_nodelay), AV_OPT_TYPE_INT, {.i64 = 0}, 0, 1, DEC|ENC},
3227 {"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, .unit = "rtmp_listen" },
3228 { NULL },
3229 };
3230
3231 #define RTMP_PROTOCOL_0(flavor)
3232 #define RTMP_PROTOCOL_1(flavor) \
3233 static const AVClass flavor##_class = { \
3234 .class_name = #flavor, \
3235 .item_name = av_default_item_name, \
3236 .option = rtmp_options, \
3237 .version = LIBAVUTIL_VERSION_INT, \
3238 }; \
3239 \
3240 const URLProtocol ff_##flavor##_protocol = { \
3241 .name = #flavor, \
3242 .url_open2 = rtmp_open, \
3243 .url_read = rtmp_read, \
3244 .url_read_seek = rtmp_seek, \
3245 .url_read_pause = rtmp_pause, \
3246 .url_write = rtmp_write, \
3247 .url_close = rtmp_close, \
3248 .priv_data_size = sizeof(RTMPContext), \
3249 .flags = URL_PROTOCOL_FLAG_NETWORK, \
3250 .priv_data_class= &flavor##_class, \
3251 };
3252 #define RTMP_PROTOCOL_2(flavor, enabled) \
3253 RTMP_PROTOCOL_ ## enabled(flavor)
3254 #define RTMP_PROTOCOL_3(flavor, config) \
3255 RTMP_PROTOCOL_2(flavor, config)
3256 #define RTMP_PROTOCOL(flavor, uppercase) \
3257 RTMP_PROTOCOL_3(flavor, CONFIG_ ## uppercase ## _PROTOCOL)
3258
3259 RTMP_PROTOCOL(rtmp, RTMP)
3260 RTMP_PROTOCOL(rtmpe, RTMPE)
3261 RTMP_PROTOCOL(rtmps, RTMPS)
3262 RTMP_PROTOCOL(rtmpt, RTMPT)
3263 RTMP_PROTOCOL(rtmpte, RTMPTE)
3264 RTMP_PROTOCOL(rtmpts, RTMPTS)
3265