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