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