Fix AMF3ReadString
[rtmpdump.git] / rtmpsrv.c
bloba9e90457e33f3b2953817af474ae7145ae1ecc99
1 /* Simple RTMP Server
2 * Copyright (C) 2009 Andrej Stepanchuk
3 * Copyright (C) 2009-2011 Howard Chu
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
8 * any later version.
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with RTMPDump; see the file COPYING. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 * http://www.gnu.org/copyleft/gpl.html
23 /* This is just a stub for an RTMP server. It doesn't do anything
24 * beyond obtaining the connection parameters from the client.
27 #include <stdlib.h>
28 #include <string.h>
29 #include <math.h>
30 #include <limits.h>
32 #include <signal.h>
33 #include <getopt.h>
35 #include <assert.h>
37 #include "librtmp/rtmp_sys.h"
38 #include "librtmp/log.h"
40 #include "thread.h"
42 #ifdef linux
43 #include <linux/netfilter_ipv4.h>
44 #endif
46 #ifndef WIN32
47 #include <sys/types.h>
48 #include <sys/wait.h>
49 #endif
51 #define RD_SUCCESS 0
52 #define RD_FAILED 1
53 #define RD_INCOMPLETE 2
55 #define PACKET_SIZE 1024*1024
57 #ifdef WIN32
58 #define InitSockets() {\
59 WORD version; \
60 WSADATA wsaData; \
62 version = MAKEWORD(1,1); \
63 WSAStartup(version, &wsaData); }
65 #define CleanupSockets() WSACleanup()
66 #else
67 #define InitSockets()
68 #define CleanupSockets()
69 #endif
71 #define DUPTIME 5000 /* interval we disallow duplicate requests, in msec */
73 enum
75 STREAMING_ACCEPTING,
76 STREAMING_IN_PROGRESS,
77 STREAMING_STOPPING,
78 STREAMING_STOPPED
81 typedef struct
83 int socket;
84 int state;
85 int streamID;
86 int arglen;
87 int argc;
88 uint32_t filetime; /* time of last download we started */
89 AVal filename; /* name of last download */
90 char *connect;
92 } STREAMING_SERVER;
94 STREAMING_SERVER *rtmpServer = 0; // server structure pointer
95 void *sslCtx = NULL;
97 STREAMING_SERVER *startStreaming(const char *address, int port);
98 void stopStreaming(STREAMING_SERVER * server);
99 void AVreplace(AVal *src, const AVal *orig, const AVal *repl);
101 static const AVal av_dquote = AVC("\"");
102 static const AVal av_escdquote = AVC("\\\"");
104 typedef struct
106 char *hostname;
107 int rtmpport;
108 int protocol;
109 int bLiveStream; // is it a live stream? then we can't seek/resume
111 long int timeout; // timeout connection afte 300 seconds
112 uint32_t bufferTime;
114 char *rtmpurl;
115 AVal playpath;
116 AVal swfUrl;
117 AVal tcUrl;
118 AVal pageUrl;
119 AVal app;
120 AVal auth;
121 AVal swfHash;
122 AVal flashVer;
123 AVal subscribepath;
124 uint32_t swfSize;
126 uint32_t dStartOffset;
127 uint32_t dStopOffset;
128 uint32_t nTimeStamp;
129 } RTMP_REQUEST;
131 #define STR2AVAL(av,str) av.av_val = str; av.av_len = strlen(av.av_val)
133 /* this request is formed from the parameters and used to initialize a new request,
134 * thus it is a default settings list. All settings can be overriden by specifying the
135 * parameters in the GET request. */
136 RTMP_REQUEST defaultRTMPRequest;
138 #ifdef _DEBUG
139 uint32_t debugTS = 0;
141 int pnum = 0;
143 FILE *netstackdump = NULL;
144 FILE *netstackdump_read = NULL;
145 #endif
147 #define SAVC(x) static const AVal av_##x = AVC(#x)
149 SAVC(app);
150 SAVC(connect);
151 SAVC(flashVer);
152 SAVC(swfUrl);
153 SAVC(pageUrl);
154 SAVC(tcUrl);
155 SAVC(fpad);
156 SAVC(capabilities);
157 SAVC(audioCodecs);
158 SAVC(videoCodecs);
159 SAVC(videoFunction);
160 SAVC(objectEncoding);
161 SAVC(_result);
162 SAVC(createStream);
163 SAVC(getStreamLength);
164 SAVC(play);
165 SAVC(fmsVer);
166 SAVC(mode);
167 SAVC(level);
168 SAVC(code);
169 SAVC(description);
170 SAVC(secureToken);
172 static int
173 SendConnectResult(RTMP *r, double txn)
175 RTMPPacket packet;
176 char pbuf[384], *pend = pbuf+sizeof(pbuf);
177 AMFObject obj;
178 AMFObjectProperty p, op;
179 AVal av;
181 packet.m_nChannel = 0x03; // control channel (invoke)
182 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
183 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
184 packet.m_nTimeStamp = 0;
185 packet.m_nInfoField2 = 0;
186 packet.m_hasAbsTimestamp = 0;
187 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
189 char *enc = packet.m_body;
190 enc = AMF_EncodeString(enc, pend, &av__result);
191 enc = AMF_EncodeNumber(enc, pend, txn);
192 *enc++ = AMF_OBJECT;
194 STR2AVAL(av, "FMS/3,5,1,525");
195 enc = AMF_EncodeNamedString(enc, pend, &av_fmsVer, &av);
196 enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 31.0);
197 enc = AMF_EncodeNamedNumber(enc, pend, &av_mode, 1.0);
198 *enc++ = 0;
199 *enc++ = 0;
200 *enc++ = AMF_OBJECT_END;
202 *enc++ = AMF_OBJECT;
204 STR2AVAL(av, "status");
205 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av);
206 STR2AVAL(av, "NetConnection.Connect.Success");
207 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av);
208 STR2AVAL(av, "Connection succeeded.");
209 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av);
210 enc = AMF_EncodeNamedNumber(enc, pend, &av_objectEncoding, r->m_fEncoding);
211 #if 0
212 STR2AVAL(av, "58656322c972d6cdf2d776167575045f8484ea888e31c086f7b5ffbd0baec55ce442c2fb");
213 enc = AMF_EncodeNamedString(enc, pend, &av_secureToken, &av);
214 #endif
215 STR2AVAL(p.p_name, "version");
216 STR2AVAL(p.p_vu.p_aval, "3,5,1,525");
217 p.p_type = AMF_STRING;
218 obj.o_num = 1;
219 obj.o_props = &p;
220 op.p_type = AMF_OBJECT;
221 STR2AVAL(op.p_name, "data");
222 op.p_vu.p_object = obj;
223 enc = AMFProp_Encode(&op, enc, pend);
224 *enc++ = 0;
225 *enc++ = 0;
226 *enc++ = AMF_OBJECT_END;
228 packet.m_nBodySize = enc - packet.m_body;
230 return RTMP_SendPacket(r, &packet, FALSE);
233 static int
234 SendResultNumber(RTMP *r, double txn, double ID)
236 RTMPPacket packet;
237 char pbuf[256], *pend = pbuf+sizeof(pbuf);
239 packet.m_nChannel = 0x03; // control channel (invoke)
240 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
241 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
242 packet.m_nTimeStamp = 0;
243 packet.m_nInfoField2 = 0;
244 packet.m_hasAbsTimestamp = 0;
245 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
247 char *enc = packet.m_body;
248 enc = AMF_EncodeString(enc, pend, &av__result);
249 enc = AMF_EncodeNumber(enc, pend, txn);
250 *enc++ = AMF_NULL;
251 enc = AMF_EncodeNumber(enc, pend, ID);
253 packet.m_nBodySize = enc - packet.m_body;
255 return RTMP_SendPacket(r, &packet, FALSE);
258 SAVC(onStatus);
259 SAVC(status);
260 static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start");
261 static const AVal av_Started_playing = AVC("Started playing");
262 static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop");
263 static const AVal av_Stopped_playing = AVC("Stopped playing");
264 SAVC(details);
265 SAVC(clientid);
266 static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken");
268 static int
269 SendPlayStart(RTMP *r)
271 RTMPPacket packet;
272 char pbuf[512], *pend = pbuf+sizeof(pbuf);
274 packet.m_nChannel = 0x03; // control channel (invoke)
275 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
276 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
277 packet.m_nTimeStamp = 0;
278 packet.m_nInfoField2 = 0;
279 packet.m_hasAbsTimestamp = 0;
280 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
282 char *enc = packet.m_body;
283 enc = AMF_EncodeString(enc, pend, &av_onStatus);
284 enc = AMF_EncodeNumber(enc, pend, 0);
285 *enc++ = AMF_OBJECT;
287 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
288 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Start);
289 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_Started_playing);
290 enc = AMF_EncodeNamedString(enc, pend, &av_details, &r->Link.playpath);
291 enc = AMF_EncodeNamedString(enc, pend, &av_clientid, &av_clientid);
292 *enc++ = 0;
293 *enc++ = 0;
294 *enc++ = AMF_OBJECT_END;
296 packet.m_nBodySize = enc - packet.m_body;
297 return RTMP_SendPacket(r, &packet, FALSE);
300 static int
301 SendPlayStop(RTMP *r)
303 RTMPPacket packet;
304 char pbuf[512], *pend = pbuf+sizeof(pbuf);
306 packet.m_nChannel = 0x03; // control channel (invoke)
307 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
308 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
309 packet.m_nTimeStamp = 0;
310 packet.m_nInfoField2 = 0;
311 packet.m_hasAbsTimestamp = 0;
312 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
314 char *enc = packet.m_body;
315 enc = AMF_EncodeString(enc, pend, &av_onStatus);
316 enc = AMF_EncodeNumber(enc, pend, 0);
317 *enc++ = AMF_OBJECT;
319 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
320 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Stop);
321 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_Stopped_playing);
322 enc = AMF_EncodeNamedString(enc, pend, &av_details, &r->Link.playpath);
323 enc = AMF_EncodeNamedString(enc, pend, &av_clientid, &av_clientid);
324 *enc++ = 0;
325 *enc++ = 0;
326 *enc++ = AMF_OBJECT_END;
328 packet.m_nBodySize = enc - packet.m_body;
329 return RTMP_SendPacket(r, &packet, FALSE);
332 static void
333 spawn_dumper(int argc, AVal *av, char *cmd)
335 #ifdef WIN32
336 STARTUPINFO si = {0};
337 PROCESS_INFORMATION pi = {0};
339 si.cb = sizeof(si);
340 if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL,
341 &si, &pi))
343 CloseHandle(pi.hThread);
344 CloseHandle(pi.hProcess);
346 #else
347 /* reap any dead children */
348 while (waitpid(-1, NULL, WNOHANG) > 0);
350 if (fork() == 0) {
351 char **argv = malloc((argc+1) * sizeof(char *));
352 int i;
354 for (i=0; i<argc; i++) {
355 argv[i] = av[i].av_val;
356 argv[i][av[i].av_len] = '\0';
358 argv[i] = NULL;
359 if ((i = execvp(argv[0], argv)))
360 _exit(i);
362 #endif
365 static int
366 countAMF(AMFObject *obj, int *argc)
368 int i, len;
370 for (i=0, len=0; i < obj->o_num; i++)
372 AMFObjectProperty *p = &obj->o_props[i];
373 len += 4;
374 (*argc)+= 2;
375 if (p->p_name.av_val)
376 len += 1;
377 len += 2;
378 if (p->p_name.av_val)
379 len += p->p_name.av_len + 1;
380 switch(p->p_type)
382 case AMF_BOOLEAN:
383 len += 1;
384 break;
385 case AMF_STRING:
386 len += p->p_vu.p_aval.av_len;
387 break;
388 case AMF_NUMBER:
389 len += 40;
390 break;
391 case AMF_OBJECT:
392 len += 9;
393 len += countAMF(&p->p_vu.p_object, argc);
394 (*argc) += 2;
395 break;
396 case AMF_NULL:
397 default:
398 break;
401 return len;
404 static char *
405 dumpAMF(AMFObject *obj, char *ptr, AVal *argv, int *argc)
407 int i, len, ac = *argc;
408 const char opt[] = "NBSO Z";
410 for (i=0, len=0; i < obj->o_num; i++)
412 AMFObjectProperty *p = &obj->o_props[i];
413 argv[ac].av_val = ptr+1;
414 argv[ac++].av_len = 2;
415 ptr += sprintf(ptr, " -C ");
416 argv[ac].av_val = ptr;
417 if (p->p_name.av_val)
418 *ptr++ = 'N';
419 *ptr++ = opt[p->p_type];
420 *ptr++ = ':';
421 if (p->p_name.av_val)
422 ptr += sprintf(ptr, "%.*s:", p->p_name.av_len, p->p_name.av_val);
423 switch(p->p_type)
425 case AMF_BOOLEAN:
426 *ptr++ = p->p_vu.p_number != 0 ? '1' : '0';
427 argv[ac].av_len = ptr - argv[ac].av_val;
428 break;
429 case AMF_STRING:
430 memcpy(ptr, p->p_vu.p_aval.av_val, p->p_vu.p_aval.av_len);
431 ptr += p->p_vu.p_aval.av_len;
432 argv[ac].av_len = ptr - argv[ac].av_val;
433 break;
434 case AMF_NUMBER:
435 ptr += sprintf(ptr, "%f", p->p_vu.p_number);
436 argv[ac].av_len = ptr - argv[ac].av_val;
437 break;
438 case AMF_OBJECT:
439 *ptr++ = '1';
440 argv[ac].av_len = ptr - argv[ac].av_val;
441 ac++;
442 *argc = ac;
443 ptr = dumpAMF(&p->p_vu.p_object, ptr, argv, argc);
444 ac = *argc;
445 argv[ac].av_val = ptr+1;
446 argv[ac++].av_len = 2;
447 argv[ac].av_val = ptr+4;
448 argv[ac].av_len = 3;
449 ptr += sprintf(ptr, " -C O:0");
450 break;
451 case AMF_NULL:
452 default:
453 argv[ac].av_len = ptr - argv[ac].av_val;
454 break;
456 ac++;
458 *argc = ac;
459 return ptr;
462 // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
464 ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int offset)
466 const char *body;
467 unsigned int nBodySize;
468 int ret = 0, nRes;
470 body = packet->m_body + offset;
471 nBodySize = packet->m_nBodySize - offset;
473 if (body[0] != 0x02) // make sure it is a string method name we start with
475 RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet",
476 __FUNCTION__);
477 return 0;
480 AMFObject obj;
481 nRes = AMF_Decode(&obj, body, nBodySize, FALSE);
482 if (nRes < 0)
484 RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__);
485 return 0;
488 AMF_Dump(&obj);
489 AVal method;
490 AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method);
491 double txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1));
492 RTMP_Log(RTMP_LOGDEBUG, "%s, client invoking <%s>", __FUNCTION__, method.av_val);
494 if (AVMATCH(&method, &av_connect))
496 AMFObject cobj;
497 AVal pname, pval;
498 int i;
500 server->connect = packet->m_body;
501 packet->m_body = NULL;
503 AMFProp_GetObject(AMF_GetProp(&obj, NULL, 2), &cobj);
504 for (i=0; i<cobj.o_num; i++)
506 pname = cobj.o_props[i].p_name;
507 pval.av_val = NULL;
508 pval.av_len = 0;
509 if (cobj.o_props[i].p_type == AMF_STRING)
510 pval = cobj.o_props[i].p_vu.p_aval;
511 if (AVMATCH(&pname, &av_app))
513 r->Link.app = pval;
514 pval.av_val = NULL;
515 if (!r->Link.app.av_val)
516 r->Link.app.av_val = "";
517 server->arglen += 6 + pval.av_len;
518 server->argc += 2;
520 else if (AVMATCH(&pname, &av_flashVer))
522 r->Link.flashVer = pval;
523 pval.av_val = NULL;
524 server->arglen += 6 + pval.av_len;
525 server->argc += 2;
527 else if (AVMATCH(&pname, &av_swfUrl))
529 r->Link.swfUrl = pval;
530 pval.av_val = NULL;
531 server->arglen += 6 + pval.av_len;
532 server->argc += 2;
534 else if (AVMATCH(&pname, &av_tcUrl))
536 r->Link.tcUrl = pval;
537 pval.av_val = NULL;
538 server->arglen += 6 + pval.av_len;
539 server->argc += 2;
541 else if (AVMATCH(&pname, &av_pageUrl))
543 r->Link.pageUrl = pval;
544 pval.av_val = NULL;
545 server->arglen += 6 + pval.av_len;
546 server->argc += 2;
548 else if (AVMATCH(&pname, &av_audioCodecs))
550 r->m_fAudioCodecs = cobj.o_props[i].p_vu.p_number;
552 else if (AVMATCH(&pname, &av_videoCodecs))
554 r->m_fVideoCodecs = cobj.o_props[i].p_vu.p_number;
556 else if (AVMATCH(&pname, &av_objectEncoding))
558 r->m_fEncoding = cobj.o_props[i].p_vu.p_number;
561 /* Still have more parameters? Copy them */
562 if (obj.o_num > 3)
564 int i = obj.o_num - 3;
565 r->Link.extras.o_num = i;
566 r->Link.extras.o_props = malloc(i*sizeof(AMFObjectProperty));
567 memcpy(r->Link.extras.o_props, obj.o_props+3, i*sizeof(AMFObjectProperty));
568 obj.o_num = 3;
569 server->arglen += countAMF(&r->Link.extras, &server->argc);
571 SendConnectResult(r, txn);
573 else if (AVMATCH(&method, &av_createStream))
575 SendResultNumber(r, txn, ++server->streamID);
577 else if (AVMATCH(&method, &av_getStreamLength))
579 SendResultNumber(r, txn, 10.0);
581 else if (AVMATCH(&method, &av_NetStream_Authenticate_UsherToken))
583 AVal usherToken;
584 AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &usherToken);
585 AVreplace(&usherToken, &av_dquote, &av_escdquote);
586 server->arglen += 6 + usherToken.av_len;
587 server->argc += 2;
588 r->Link.usherToken = usherToken;
590 else if (AVMATCH(&method, &av_play))
592 char *file, *p, *q, *cmd, *ptr;
593 AVal *argv, av;
594 int len, argc;
595 uint32_t now;
596 RTMPPacket pc = {0};
597 AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &r->Link.playpath);
599 r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4));
600 if (obj.o_num > 5)
601 r->Link.length = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 5));
603 if (r->Link.tcUrl.av_len)
605 len = server->arglen + r->Link.playpath.av_len + 4 +
606 sizeof("rtmpdump") + r->Link.playpath.av_len + 12;
607 server->argc += 5;
609 cmd = malloc(len + server->argc * sizeof(AVal));
610 ptr = cmd;
611 argv = (AVal *)(cmd + len);
612 argv[0].av_val = cmd;
613 argv[0].av_len = sizeof("rtmpdump")-1;
614 ptr += sprintf(ptr, "rtmpdump");
615 argc = 1;
617 argv[argc].av_val = ptr + 1;
618 argv[argc++].av_len = 2;
619 argv[argc].av_val = ptr + 5;
620 ptr += sprintf(ptr," -r \"%s\"", r->Link.tcUrl.av_val);
621 argv[argc++].av_len = r->Link.tcUrl.av_len;
623 if (r->Link.app.av_val)
625 argv[argc].av_val = ptr + 1;
626 argv[argc++].av_len = 2;
627 argv[argc].av_val = ptr + 5;
628 ptr += sprintf(ptr, " -a \"%s\"", r->Link.app.av_val);
629 argv[argc++].av_len = r->Link.app.av_len;
631 if (r->Link.flashVer.av_val)
633 argv[argc].av_val = ptr + 1;
634 argv[argc++].av_len = 2;
635 argv[argc].av_val = ptr + 5;
636 ptr += sprintf(ptr, " -f \"%s\"", r->Link.flashVer.av_val);
637 argv[argc++].av_len = r->Link.flashVer.av_len;
639 if (r->Link.swfUrl.av_val)
641 argv[argc].av_val = ptr + 1;
642 argv[argc++].av_len = 2;
643 argv[argc].av_val = ptr + 5;
644 ptr += sprintf(ptr, " -W \"%s\"", r->Link.swfUrl.av_val);
645 argv[argc++].av_len = r->Link.swfUrl.av_len;
647 if (r->Link.pageUrl.av_val)
649 argv[argc].av_val = ptr + 1;
650 argv[argc++].av_len = 2;
651 argv[argc].av_val = ptr + 5;
652 ptr += sprintf(ptr, " -p \"%s\"", r->Link.pageUrl.av_val);
653 argv[argc++].av_len = r->Link.pageUrl.av_len;
655 if (r->Link.usherToken.av_val)
657 argv[argc].av_val = ptr + 1;
658 argv[argc++].av_len = 2;
659 argv[argc].av_val = ptr + 5;
660 ptr += sprintf(ptr, " -j \"%s\"", r->Link.usherToken.av_val);
661 argv[argc++].av_len = r->Link.usherToken.av_len;
662 free(r->Link.usherToken.av_val);
663 r->Link.usherToken.av_val = NULL;
664 r->Link.usherToken.av_len = 0;
666 if (r->Link.extras.o_num) {
667 ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc);
668 AMF_Reset(&r->Link.extras);
670 argv[argc].av_val = ptr + 1;
671 argv[argc++].av_len = 2;
672 argv[argc].av_val = ptr + 5;
673 ptr += sprintf(ptr, " -y \"%.*s\"",
674 r->Link.playpath.av_len, r->Link.playpath.av_val);
675 argv[argc++].av_len = r->Link.playpath.av_len;
677 av = r->Link.playpath;
678 /* strip trailing URL parameters */
679 q = memchr(av.av_val, '?', av.av_len);
680 if (q)
682 if (q == av.av_val)
684 av.av_val++;
685 av.av_len--;
687 else
689 av.av_len = q - av.av_val;
692 /* strip leading slash components */
693 for (p=av.av_val+av.av_len-1; p>=av.av_val; p--)
694 if (*p == '/')
696 p++;
697 av.av_len -= p - av.av_val;
698 av.av_val = p;
699 break;
701 /* skip leading dot */
702 if (av.av_val[0] == '.')
704 av.av_val++;
705 av.av_len--;
707 file = malloc(av.av_len+5);
709 memcpy(file, av.av_val, av.av_len);
710 file[av.av_len] = '\0';
711 for (p=file; *p; p++)
712 if (*p == ':')
713 *p = '_';
715 /* Add extension if none present */
716 if (file[av.av_len - 4] != '.')
718 av.av_len += 4;
720 /* Always use flv extension, regardless of original */
721 if (strcmp(file+av.av_len-4, ".flv"))
723 strcpy(file+av.av_len-4, ".flv");
725 argv[argc].av_val = ptr + 1;
726 argv[argc++].av_len = 2;
727 argv[argc].av_val = file;
728 argv[argc].av_len = av.av_len;
729 ptr += sprintf(ptr, " -o %s", file);
730 now = RTMP_GetTime();
731 if (now - server->filetime < DUPTIME && AVMATCH(&argv[argc], &server->filename))
733 printf("Duplicate request, skipping.\n");
734 free(file);
736 else
738 printf("\n%s\n\n", cmd);
739 fflush(stdout);
740 server->filetime = now;
741 free(server->filename.av_val);
742 server->filename = argv[argc++];
743 spawn_dumper(argc, argv, cmd);
746 free(cmd);
748 pc.m_body = server->connect;
749 server->connect = NULL;
750 RTMPPacket_Free(&pc);
751 ret = 1;
752 RTMP_SendCtrl(r, 0, 1, 0);
753 SendPlayStart(r);
754 RTMP_SendCtrl(r, 1, 1, 0);
755 SendPlayStop(r);
757 AMF_Reset(&obj);
758 return ret;
762 ServePacket(STREAMING_SERVER *server, RTMP *r, RTMPPacket *packet)
764 int ret = 0;
766 RTMP_Log(RTMP_LOGDEBUG, "%s, received packet type %02X, size %u bytes", __FUNCTION__,
767 packet->m_packetType, packet->m_nBodySize);
769 switch (packet->m_packetType)
771 case RTMP_PACKET_TYPE_CHUNK_SIZE:
772 // HandleChangeChunkSize(r, packet);
773 break;
775 case RTMP_PACKET_TYPE_BYTES_READ_REPORT:
776 break;
778 case RTMP_PACKET_TYPE_CONTROL:
779 // HandleCtrl(r, packet);
780 break;
782 case RTMP_PACKET_TYPE_SERVER_BW:
783 // HandleServerBW(r, packet);
784 break;
786 case RTMP_PACKET_TYPE_CLIENT_BW:
787 // HandleClientBW(r, packet);
788 break;
790 case RTMP_PACKET_TYPE_AUDIO:
791 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
792 break;
794 case RTMP_PACKET_TYPE_VIDEO:
795 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
796 break;
798 case RTMP_PACKET_TYPE_FLEX_STREAM_SEND:
799 break;
801 case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT:
802 break;
804 case RTMP_PACKET_TYPE_FLEX_MESSAGE:
806 RTMP_Log(RTMP_LOGDEBUG, "%s, flex message, size %u bytes, not fully supported",
807 __FUNCTION__, packet->m_nBodySize);
808 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
810 // some DEBUG code
811 /*RTMP_LIB_AMFObject obj;
812 int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);
813 if(nRes < 0) {
814 RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);
815 //return;
818 obj.Dump(); */
820 if (ServeInvoke(server, r, packet, 1))
821 RTMP_Close(r);
822 break;
824 case RTMP_PACKET_TYPE_INFO:
825 break;
827 case RTMP_PACKET_TYPE_SHARED_OBJECT:
828 break;
830 case RTMP_PACKET_TYPE_INVOKE:
831 RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,
832 packet->m_nBodySize);
833 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
835 if (ServeInvoke(server, r, packet, 0))
836 RTMP_Close(r);
837 break;
839 case RTMP_PACKET_TYPE_FLASH_VIDEO:
840 break;
841 default:
842 RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,
843 packet->m_packetType);
844 #ifdef _DEBUG
845 RTMP_LogHex(RTMP_LOGDEBUG, packet->m_body, packet->m_nBodySize);
846 #endif
848 return ret;
851 TFTYPE
852 controlServerThread(void *unused)
854 char ich;
855 while (1)
857 ich = getchar();
858 switch (ich)
860 case 'q':
861 RTMP_LogPrintf("Exiting\n");
862 stopStreaming(rtmpServer);
863 exit(0);
864 break;
865 default:
866 RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich);
869 TFRET();
873 void doServe(STREAMING_SERVER * server, // server socket and state (our listening socket)
874 int sockfd // client connection socket
877 server->state = STREAMING_IN_PROGRESS;
879 RTMP *rtmp = RTMP_Alloc(); /* our session with the real client */
880 RTMPPacket packet = { 0 };
882 // timeout for http requests
883 fd_set fds;
884 struct timeval tv;
886 memset(&tv, 0, sizeof(struct timeval));
887 tv.tv_sec = 5;
889 FD_ZERO(&fds);
890 FD_SET(sockfd, &fds);
892 if (select(sockfd + 1, &fds, NULL, NULL, &tv) <= 0)
894 RTMP_Log(RTMP_LOGERROR, "Request timeout/select failed, ignoring request");
895 goto quit;
897 else
899 RTMP_Init(rtmp);
900 rtmp->m_sb.sb_socket = sockfd;
901 if (sslCtx && !RTMP_TLS_Accept(rtmp, sslCtx))
903 RTMP_Log(RTMP_LOGERROR, "TLS handshake failed");
904 goto cleanup;
906 if (!RTMP_Serve(rtmp))
908 RTMP_Log(RTMP_LOGERROR, "Handshake failed");
909 goto cleanup;
912 server->arglen = 0;
913 while (RTMP_IsConnected(rtmp) && RTMP_ReadPacket(rtmp, &packet))
915 if (!RTMPPacket_IsReady(&packet))
916 continue;
917 ServePacket(server, rtmp, &packet);
918 RTMPPacket_Free(&packet);
921 cleanup:
922 RTMP_LogPrintf("Closing connection... ");
923 RTMP_Close(rtmp);
924 /* Should probably be done by RTMP_Close() ... */
925 rtmp->Link.playpath.av_val = NULL;
926 rtmp->Link.tcUrl.av_val = NULL;
927 rtmp->Link.swfUrl.av_val = NULL;
928 rtmp->Link.pageUrl.av_val = NULL;
929 rtmp->Link.app.av_val = NULL;
930 rtmp->Link.flashVer.av_val = NULL;
931 if (rtmp->Link.usherToken.av_val)
933 free(rtmp->Link.usherToken.av_val);
934 rtmp->Link.usherToken.av_val = NULL;
936 RTMP_Free(rtmp);
937 RTMP_LogPrintf("done!\n\n");
939 quit:
940 if (server->state == STREAMING_IN_PROGRESS)
941 server->state = STREAMING_ACCEPTING;
943 return;
946 TFTYPE
947 serverThread(void *arg)
949 STREAMING_SERVER *server = arg;
950 server->state = STREAMING_ACCEPTING;
952 while (server->state == STREAMING_ACCEPTING)
954 struct sockaddr_in addr;
955 socklen_t addrlen = sizeof(struct sockaddr_in);
956 int sockfd =
957 accept(server->socket, (struct sockaddr *) &addr, &addrlen);
959 if (sockfd > 0)
961 #ifdef linux
962 struct sockaddr_in dest;
963 char destch[16];
964 socklen_t destlen = sizeof(struct sockaddr_in);
965 getsockopt(sockfd, SOL_IP, SO_ORIGINAL_DST, &dest, &destlen);
966 strcpy(destch, inet_ntoa(dest.sin_addr));
967 RTMP_Log(RTMP_LOGDEBUG, "%s: accepted connection from %s to %s\n", __FUNCTION__,
968 inet_ntoa(addr.sin_addr), destch);
969 #else
970 RTMP_Log(RTMP_LOGDEBUG, "%s: accepted connection from %s\n", __FUNCTION__,
971 inet_ntoa(addr.sin_addr));
972 #endif
973 /* Create a new thread and transfer the control to that */
974 doServe(server, sockfd);
975 RTMP_Log(RTMP_LOGDEBUG, "%s: processed request\n", __FUNCTION__);
977 else
979 RTMP_Log(RTMP_LOGERROR, "%s: accept failed", __FUNCTION__);
982 server->state = STREAMING_STOPPED;
983 TFRET();
986 STREAMING_SERVER *
987 startStreaming(const char *address, int port)
989 struct sockaddr_in addr;
990 int sockfd, tmp;
991 STREAMING_SERVER *server;
993 sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
994 if (sockfd == -1)
996 RTMP_Log(RTMP_LOGERROR, "%s, couldn't create socket", __FUNCTION__);
997 return 0;
1000 tmp = 1;
1001 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
1002 (char *) &tmp, sizeof(tmp) );
1004 addr.sin_family = AF_INET;
1005 addr.sin_addr.s_addr = inet_addr(address); //htonl(INADDR_ANY);
1006 addr.sin_port = htons(port);
1008 if (bind(sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) ==
1011 RTMP_Log(RTMP_LOGERROR, "%s, TCP bind failed for port number: %d", __FUNCTION__,
1012 port);
1013 return 0;
1016 if (listen(sockfd, 10) == -1)
1018 RTMP_Log(RTMP_LOGERROR, "%s, listen failed", __FUNCTION__);
1019 closesocket(sockfd);
1020 return 0;
1023 server = (STREAMING_SERVER *) calloc(1, sizeof(STREAMING_SERVER));
1024 server->socket = sockfd;
1026 ThreadCreate(serverThread, server);
1028 return server;
1031 void
1032 stopStreaming(STREAMING_SERVER * server)
1034 assert(server);
1036 if (server->state != STREAMING_STOPPED)
1038 if (server->state == STREAMING_IN_PROGRESS)
1040 server->state = STREAMING_STOPPING;
1042 // wait for streaming threads to exit
1043 while (server->state != STREAMING_STOPPED)
1044 msleep(1);
1047 if (closesocket(server->socket))
1048 RTMP_Log(RTMP_LOGERROR, "%s: Failed to close listening socket, error %d",
1049 __FUNCTION__, GetSockError());
1051 server->state = STREAMING_STOPPED;
1056 void
1057 sigIntHandler(int sig)
1059 RTMP_ctrlC = TRUE;
1060 RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig);
1061 if (rtmpServer)
1062 stopStreaming(rtmpServer);
1063 signal(SIGINT, SIG_DFL);
1067 main(int argc, char **argv)
1069 int nStatus = RD_SUCCESS;
1070 int i;
1072 // http streaming server
1073 char DEFAULT_HTTP_STREAMING_DEVICE[] = "0.0.0.0"; // 0.0.0.0 is any device
1075 char *rtmpStreamingDevice = DEFAULT_HTTP_STREAMING_DEVICE; // streaming device, default 0.0.0.0
1076 int nRtmpStreamingPort = 1935; // port
1077 char *cert = NULL, *key = NULL;
1079 RTMP_LogPrintf("RTMP Server %s\n", RTMPDUMP_VERSION);
1080 RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n");
1082 RTMP_debuglevel = RTMP_LOGINFO;
1084 for (i = 1; i < argc; i++)
1086 if (!strcmp(argv[i], "-z"))
1087 RTMP_debuglevel = RTMP_LOGALL;
1088 else if (!strcmp(argv[i], "-c") && i + 1 < argc)
1089 cert = argv[++i];
1090 else if (!strcmp(argv[i], "-k") && i + 1 < argc)
1091 key = argv[++i];
1094 if (cert && key)
1095 sslCtx = RTMP_TLS_AllocServerContext(cert, key);
1097 // init request
1098 memset(&defaultRTMPRequest, 0, sizeof(RTMP_REQUEST));
1100 defaultRTMPRequest.rtmpport = -1;
1101 defaultRTMPRequest.protocol = RTMP_PROTOCOL_UNDEFINED;
1102 defaultRTMPRequest.bLiveStream = FALSE; // is it a live stream? then we can't seek/resume
1104 defaultRTMPRequest.timeout = 300; // timeout connection afte 300 seconds
1105 defaultRTMPRequest.bufferTime = 20 * 1000;
1108 signal(SIGINT, sigIntHandler);
1109 #ifndef WIN32
1110 signal(SIGPIPE, SIG_IGN);
1111 #endif
1113 #ifdef _DEBUG
1114 netstackdump = fopen("netstackdump", "wb");
1115 netstackdump_read = fopen("netstackdump_read", "wb");
1116 #endif
1118 InitSockets();
1120 // start text UI
1121 ThreadCreate(controlServerThread, 0);
1123 // start http streaming
1124 if ((rtmpServer =
1125 startStreaming(rtmpStreamingDevice, nRtmpStreamingPort)) == 0)
1127 RTMP_Log(RTMP_LOGERROR, "Failed to start RTMP server, exiting!");
1128 return RD_FAILED;
1130 RTMP_LogPrintf("Streaming on rtmp://%s:%d\n", rtmpStreamingDevice,
1131 nRtmpStreamingPort);
1133 while (rtmpServer->state != STREAMING_STOPPED)
1135 sleep(1);
1137 RTMP_Log(RTMP_LOGDEBUG, "Done, exiting...");
1139 if (sslCtx)
1140 RTMP_TLS_FreeServerContext(sslCtx);
1142 CleanupSockets();
1144 #ifdef _DEBUG
1145 if (netstackdump != 0)
1146 fclose(netstackdump);
1147 if (netstackdump_read != 0)
1148 fclose(netstackdump_read);
1149 #endif
1150 return nStatus;
1153 void
1154 AVreplace(AVal *src, const AVal *orig, const AVal *repl)
1156 char *srcbeg = src->av_val;
1157 char *srcend = src->av_val + src->av_len;
1158 char *dest, *sptr, *dptr;
1159 int n = 0;
1161 /* count occurrences of orig in src */
1162 sptr = src->av_val;
1163 while (sptr < srcend && (sptr = strstr(sptr, orig->av_val)))
1165 n++;
1166 sptr += orig->av_len;
1168 if (!n)
1169 return;
1171 dest = malloc(src->av_len + 1 + (repl->av_len - orig->av_len) * n);
1173 sptr = src->av_val;
1174 dptr = dest;
1175 while (sptr < srcend && (sptr = strstr(sptr, orig->av_val)))
1177 n = sptr - srcbeg;
1178 memcpy(dptr, srcbeg, n);
1179 dptr += n;
1180 memcpy(dptr, repl->av_val, repl->av_len);
1181 dptr += repl->av_len;
1182 sptr += orig->av_len;
1183 srcbeg = sptr;
1185 n = srcend - srcbeg;
1186 memcpy(dptr, srcbeg, n);
1187 dptr += n;
1188 *dptr = '\0';
1189 src->av_val = dest;
1190 src->av_len = dptr - dest;