Fix mismatched format string conversions
[rtmpdump.git] / rtmpsrv.c
blob91fc4dabab9bc35af95282e0cad05e81c0199b62
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
96 STREAMING_SERVER *startStreaming(const char *address, int port);
97 void stopStreaming(STREAMING_SERVER * server);
98 void AVreplace(AVal *src, const AVal *orig, const AVal *repl);
100 static const AVal av_dquote = AVC("\"");
101 static const AVal av_escdquote = AVC("\\\"");
103 typedef struct
105 char *hostname;
106 int rtmpport;
107 int protocol;
108 int bLiveStream; // is it a live stream? then we can't seek/resume
110 long int timeout; // timeout connection afte 300 seconds
111 uint32_t bufferTime;
113 char *rtmpurl;
114 AVal playpath;
115 AVal swfUrl;
116 AVal tcUrl;
117 AVal pageUrl;
118 AVal app;
119 AVal auth;
120 AVal swfHash;
121 AVal flashVer;
122 AVal subscribepath;
123 uint32_t swfSize;
125 uint32_t dStartOffset;
126 uint32_t dStopOffset;
127 uint32_t nTimeStamp;
128 } RTMP_REQUEST;
130 #define STR2AVAL(av,str) av.av_val = str; av.av_len = strlen(av.av_val)
132 /* this request is formed from the parameters and used to initialize a new request,
133 * thus it is a default settings list. All settings can be overriden by specifying the
134 * parameters in the GET request. */
135 RTMP_REQUEST defaultRTMPRequest;
137 #ifdef _DEBUG
138 uint32_t debugTS = 0;
140 int pnum = 0;
142 FILE *netstackdump = NULL;
143 FILE *netstackdump_read = NULL;
144 #endif
146 #define SAVC(x) static const AVal av_##x = AVC(#x)
148 SAVC(app);
149 SAVC(connect);
150 SAVC(flashVer);
151 SAVC(swfUrl);
152 SAVC(pageUrl);
153 SAVC(tcUrl);
154 SAVC(fpad);
155 SAVC(capabilities);
156 SAVC(audioCodecs);
157 SAVC(videoCodecs);
158 SAVC(videoFunction);
159 SAVC(objectEncoding);
160 SAVC(_result);
161 SAVC(createStream);
162 SAVC(getStreamLength);
163 SAVC(play);
164 SAVC(fmsVer);
165 SAVC(mode);
166 SAVC(level);
167 SAVC(code);
168 SAVC(description);
169 SAVC(secureToken);
171 static int
172 SendConnectResult(RTMP *r, double txn)
174 RTMPPacket packet;
175 char pbuf[384], *pend = pbuf+sizeof(pbuf);
176 AMFObject obj;
177 AMFObjectProperty p, op;
178 AVal av;
180 packet.m_nChannel = 0x03; // control channel (invoke)
181 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
182 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
183 packet.m_nTimeStamp = 0;
184 packet.m_nInfoField2 = 0;
185 packet.m_hasAbsTimestamp = 0;
186 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
188 char *enc = packet.m_body;
189 enc = AMF_EncodeString(enc, pend, &av__result);
190 enc = AMF_EncodeNumber(enc, pend, txn);
191 *enc++ = AMF_OBJECT;
193 STR2AVAL(av, "FMS/3,5,1,525");
194 enc = AMF_EncodeNamedString(enc, pend, &av_fmsVer, &av);
195 enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 31.0);
196 enc = AMF_EncodeNamedNumber(enc, pend, &av_mode, 1.0);
197 *enc++ = 0;
198 *enc++ = 0;
199 *enc++ = AMF_OBJECT_END;
201 *enc++ = AMF_OBJECT;
203 STR2AVAL(av, "status");
204 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av);
205 STR2AVAL(av, "NetConnection.Connect.Success");
206 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av);
207 STR2AVAL(av, "Connection succeeded.");
208 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av);
209 enc = AMF_EncodeNamedNumber(enc, pend, &av_objectEncoding, r->m_fEncoding);
210 #if 0
211 STR2AVAL(av, "58656322c972d6cdf2d776167575045f8484ea888e31c086f7b5ffbd0baec55ce442c2fb");
212 enc = AMF_EncodeNamedString(enc, pend, &av_secureToken, &av);
213 #endif
214 STR2AVAL(p.p_name, "version");
215 STR2AVAL(p.p_vu.p_aval, "3,5,1,525");
216 p.p_type = AMF_STRING;
217 obj.o_num = 1;
218 obj.o_props = &p;
219 op.p_type = AMF_OBJECT;
220 STR2AVAL(op.p_name, "data");
221 op.p_vu.p_object = obj;
222 enc = AMFProp_Encode(&op, enc, pend);
223 *enc++ = 0;
224 *enc++ = 0;
225 *enc++ = AMF_OBJECT_END;
226 *enc++ = 0;
227 *enc++ = 0;
228 *enc++ = AMF_OBJECT_END;
230 packet.m_nBodySize = enc - packet.m_body;
232 return RTMP_SendPacket(r, &packet, FALSE);
235 static int
236 SendResultNumber(RTMP *r, double txn, double ID)
238 RTMPPacket packet;
239 char pbuf[256], *pend = pbuf+sizeof(pbuf);
241 packet.m_nChannel = 0x03; // control channel (invoke)
242 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
243 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
244 packet.m_nTimeStamp = 0;
245 packet.m_nInfoField2 = 0;
246 packet.m_hasAbsTimestamp = 0;
247 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
249 char *enc = packet.m_body;
250 enc = AMF_EncodeString(enc, pend, &av__result);
251 enc = AMF_EncodeNumber(enc, pend, txn);
252 *enc++ = AMF_NULL;
253 enc = AMF_EncodeNumber(enc, pend, ID);
255 packet.m_nBodySize = enc - packet.m_body;
257 return RTMP_SendPacket(r, &packet, FALSE);
260 SAVC(onStatus);
261 SAVC(status);
262 static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start");
263 static const AVal av_Started_playing = AVC("Started playing");
264 static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop");
265 static const AVal av_Stopped_playing = AVC("Stopped playing");
266 SAVC(details);
267 SAVC(clientid);
268 static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken");
270 static int
271 SendPlayStart(RTMP *r)
273 RTMPPacket packet;
274 char pbuf[512], *pend = pbuf+sizeof(pbuf);
276 packet.m_nChannel = 0x03; // control channel (invoke)
277 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
278 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
279 packet.m_nTimeStamp = 0;
280 packet.m_nInfoField2 = 0;
281 packet.m_hasAbsTimestamp = 0;
282 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
284 char *enc = packet.m_body;
285 enc = AMF_EncodeString(enc, pend, &av_onStatus);
286 enc = AMF_EncodeNumber(enc, pend, 0);
287 *enc++ = AMF_OBJECT;
289 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
290 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Start);
291 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_Started_playing);
292 enc = AMF_EncodeNamedString(enc, pend, &av_details, &r->Link.playpath);
293 enc = AMF_EncodeNamedString(enc, pend, &av_clientid, &av_clientid);
294 *enc++ = 0;
295 *enc++ = 0;
296 *enc++ = AMF_OBJECT_END;
298 packet.m_nBodySize = enc - packet.m_body;
299 return RTMP_SendPacket(r, &packet, FALSE);
302 static int
303 SendPlayStop(RTMP *r)
305 RTMPPacket packet;
306 char pbuf[512], *pend = pbuf+sizeof(pbuf);
308 packet.m_nChannel = 0x03; // control channel (invoke)
309 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
310 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
311 packet.m_nTimeStamp = 0;
312 packet.m_nInfoField2 = 0;
313 packet.m_hasAbsTimestamp = 0;
314 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
316 char *enc = packet.m_body;
317 enc = AMF_EncodeString(enc, pend, &av_onStatus);
318 enc = AMF_EncodeNumber(enc, pend, 0);
319 *enc++ = AMF_OBJECT;
321 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
322 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Stop);
323 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_Stopped_playing);
324 enc = AMF_EncodeNamedString(enc, pend, &av_details, &r->Link.playpath);
325 enc = AMF_EncodeNamedString(enc, pend, &av_clientid, &av_clientid);
326 *enc++ = 0;
327 *enc++ = 0;
328 *enc++ = AMF_OBJECT_END;
330 packet.m_nBodySize = enc - packet.m_body;
331 return RTMP_SendPacket(r, &packet, FALSE);
334 static void
335 spawn_dumper(int argc, AVal *av, char *cmd)
337 #ifdef WIN32
338 STARTUPINFO si = {0};
339 PROCESS_INFORMATION pi = {0};
341 si.cb = sizeof(si);
342 if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL,
343 &si, &pi))
345 CloseHandle(pi.hThread);
346 CloseHandle(pi.hProcess);
348 #else
349 /* reap any dead children */
350 while (waitpid(-1, NULL, WNOHANG) > 0);
352 if (fork() == 0) {
353 char **argv = malloc((argc+1) * sizeof(char *));
354 int i;
356 for (i=0; i<argc; i++) {
357 argv[i] = av[i].av_val;
358 argv[i][av[i].av_len] = '\0';
360 argv[i] = NULL;
361 if ((i = execvp(argv[0], argv)))
362 _exit(i);
364 #endif
367 static int
368 countAMF(AMFObject *obj, int *argc)
370 int i, len;
372 for (i=0, len=0; i < obj->o_num; i++)
374 AMFObjectProperty *p = &obj->o_props[i];
375 len += 4;
376 (*argc)+= 2;
377 if (p->p_name.av_val)
378 len += 1;
379 len += 2;
380 if (p->p_name.av_val)
381 len += p->p_name.av_len + 1;
382 switch(p->p_type)
384 case AMF_BOOLEAN:
385 len += 1;
386 break;
387 case AMF_STRING:
388 len += p->p_vu.p_aval.av_len;
389 break;
390 case AMF_NUMBER:
391 len += 40;
392 break;
393 case AMF_OBJECT:
394 len += 9;
395 len += countAMF(&p->p_vu.p_object, argc);
396 (*argc) += 2;
397 break;
398 case AMF_NULL:
399 default:
400 break;
403 return len;
406 static char *
407 dumpAMF(AMFObject *obj, char *ptr, AVal *argv, int *argc)
409 int i, len, ac = *argc;
410 const char opt[] = "NBSO Z";
412 for (i=0, len=0; i < obj->o_num; i++)
414 AMFObjectProperty *p = &obj->o_props[i];
415 argv[ac].av_val = ptr+1;
416 argv[ac++].av_len = 2;
417 ptr += sprintf(ptr, " -C ");
418 argv[ac].av_val = ptr;
419 if (p->p_name.av_val)
420 *ptr++ = 'N';
421 *ptr++ = opt[p->p_type];
422 *ptr++ = ':';
423 if (p->p_name.av_val)
424 ptr += sprintf(ptr, "%.*s:", p->p_name.av_len, p->p_name.av_val);
425 switch(p->p_type)
427 case AMF_BOOLEAN:
428 *ptr++ = p->p_vu.p_number != 0 ? '1' : '0';
429 argv[ac].av_len = ptr - argv[ac].av_val;
430 break;
431 case AMF_STRING:
432 memcpy(ptr, p->p_vu.p_aval.av_val, p->p_vu.p_aval.av_len);
433 ptr += p->p_vu.p_aval.av_len;
434 argv[ac].av_len = ptr - argv[ac].av_val;
435 break;
436 case AMF_NUMBER:
437 ptr += sprintf(ptr, "%f", p->p_vu.p_number);
438 argv[ac].av_len = ptr - argv[ac].av_val;
439 break;
440 case AMF_OBJECT:
441 *ptr++ = '1';
442 argv[ac].av_len = ptr - argv[ac].av_val;
443 ac++;
444 *argc = ac;
445 ptr = dumpAMF(&p->p_vu.p_object, ptr, argv, argc);
446 ac = *argc;
447 argv[ac].av_val = ptr+1;
448 argv[ac++].av_len = 2;
449 argv[ac].av_val = ptr+4;
450 argv[ac].av_len = 3;
451 ptr += sprintf(ptr, " -C O:0");
452 break;
453 case AMF_NULL:
454 default:
455 argv[ac].av_len = ptr - argv[ac].av_val;
456 break;
458 ac++;
460 *argc = ac;
461 return ptr;
464 // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
466 ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int offset)
468 const char *body;
469 unsigned int nBodySize;
470 int ret = 0, nRes;
472 body = packet->m_body + offset;
473 nBodySize = packet->m_nBodySize - offset;
475 if (body[0] != 0x02) // make sure it is a string method name we start with
477 RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet",
478 __FUNCTION__);
479 return 0;
482 AMFObject obj;
483 nRes = AMF_Decode(&obj, body, nBodySize, FALSE);
484 if (nRes < 0)
486 RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__);
487 return 0;
490 AMF_Dump(&obj);
491 AVal method;
492 AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method);
493 double txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1));
494 RTMP_Log(RTMP_LOGDEBUG, "%s, client invoking <%s>", __FUNCTION__, method.av_val);
496 if (AVMATCH(&method, &av_connect))
498 AMFObject cobj;
499 AVal pname, pval;
500 int i;
502 server->connect = packet->m_body;
503 packet->m_body = NULL;
505 AMFProp_GetObject(AMF_GetProp(&obj, NULL, 2), &cobj);
506 for (i=0; i<cobj.o_num; i++)
508 pname = cobj.o_props[i].p_name;
509 pval.av_val = NULL;
510 pval.av_len = 0;
511 if (cobj.o_props[i].p_type == AMF_STRING)
512 pval = cobj.o_props[i].p_vu.p_aval;
513 if (AVMATCH(&pname, &av_app))
515 r->Link.app = pval;
516 pval.av_val = NULL;
517 if (!r->Link.app.av_val)
518 r->Link.app.av_val = "";
519 server->arglen += 6 + pval.av_len;
520 server->argc += 2;
522 else if (AVMATCH(&pname, &av_flashVer))
524 r->Link.flashVer = pval;
525 pval.av_val = NULL;
526 server->arglen += 6 + pval.av_len;
527 server->argc += 2;
529 else if (AVMATCH(&pname, &av_swfUrl))
531 r->Link.swfUrl = pval;
532 pval.av_val = NULL;
533 server->arglen += 6 + pval.av_len;
534 server->argc += 2;
536 else if (AVMATCH(&pname, &av_tcUrl))
538 r->Link.tcUrl = pval;
539 pval.av_val = NULL;
540 server->arglen += 6 + pval.av_len;
541 server->argc += 2;
543 else if (AVMATCH(&pname, &av_pageUrl))
545 r->Link.pageUrl = pval;
546 pval.av_val = NULL;
547 server->arglen += 6 + pval.av_len;
548 server->argc += 2;
550 else if (AVMATCH(&pname, &av_audioCodecs))
552 r->m_fAudioCodecs = cobj.o_props[i].p_vu.p_number;
554 else if (AVMATCH(&pname, &av_videoCodecs))
556 r->m_fVideoCodecs = cobj.o_props[i].p_vu.p_number;
558 else if (AVMATCH(&pname, &av_objectEncoding))
560 r->m_fEncoding = cobj.o_props[i].p_vu.p_number;
563 /* Still have more parameters? Copy them */
564 if (obj.o_num > 3)
566 int i = obj.o_num - 3;
567 r->Link.extras.o_num = i;
568 r->Link.extras.o_props = malloc(i*sizeof(AMFObjectProperty));
569 memcpy(r->Link.extras.o_props, obj.o_props+3, i*sizeof(AMFObjectProperty));
570 obj.o_num = 3;
571 server->arglen += countAMF(&r->Link.extras, &server->argc);
573 SendConnectResult(r, txn);
575 else if (AVMATCH(&method, &av_createStream))
577 SendResultNumber(r, txn, ++server->streamID);
579 else if (AVMATCH(&method, &av_getStreamLength))
581 SendResultNumber(r, txn, 10.0);
583 else if (AVMATCH(&method, &av_NetStream_Authenticate_UsherToken))
585 AVal usherToken;
586 AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &usherToken);
587 AVreplace(&usherToken, &av_dquote, &av_escdquote);
588 server->arglen += 6 + usherToken.av_len;
589 server->argc += 2;
590 r->Link.usherToken = usherToken;
592 else if (AVMATCH(&method, &av_play))
594 char *file, *p, *q, *cmd, *ptr;
595 AVal *argv, av;
596 int len, argc;
597 uint32_t now;
598 RTMPPacket pc = {0};
599 AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &r->Link.playpath);
601 r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4));
602 if (obj.o_num > 5)
603 r->Link.length = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 5));
605 if (r->Link.tcUrl.av_len)
607 len = server->arglen + r->Link.playpath.av_len + 4 +
608 sizeof("rtmpdump") + r->Link.playpath.av_len + 12;
609 server->argc += 5;
611 cmd = malloc(len + server->argc * sizeof(AVal));
612 ptr = cmd;
613 argv = (AVal *)(cmd + len);
614 argv[0].av_val = cmd;
615 argv[0].av_len = sizeof("rtmpdump")-1;
616 ptr += sprintf(ptr, "rtmpdump");
617 argc = 1;
619 argv[argc].av_val = ptr + 1;
620 argv[argc++].av_len = 2;
621 argv[argc].av_val = ptr + 5;
622 ptr += sprintf(ptr," -r \"%s\"", r->Link.tcUrl.av_val);
623 argv[argc++].av_len = r->Link.tcUrl.av_len;
625 if (r->Link.app.av_val)
627 argv[argc].av_val = ptr + 1;
628 argv[argc++].av_len = 2;
629 argv[argc].av_val = ptr + 5;
630 ptr += sprintf(ptr, " -a \"%s\"", r->Link.app.av_val);
631 argv[argc++].av_len = r->Link.app.av_len;
633 if (r->Link.flashVer.av_val)
635 argv[argc].av_val = ptr + 1;
636 argv[argc++].av_len = 2;
637 argv[argc].av_val = ptr + 5;
638 ptr += sprintf(ptr, " -f \"%s\"", r->Link.flashVer.av_val);
639 argv[argc++].av_len = r->Link.flashVer.av_len;
641 if (r->Link.swfUrl.av_val)
643 argv[argc].av_val = ptr + 1;
644 argv[argc++].av_len = 2;
645 argv[argc].av_val = ptr + 5;
646 ptr += sprintf(ptr, " -W \"%s\"", r->Link.swfUrl.av_val);
647 argv[argc++].av_len = r->Link.swfUrl.av_len;
649 if (r->Link.pageUrl.av_val)
651 argv[argc].av_val = ptr + 1;
652 argv[argc++].av_len = 2;
653 argv[argc].av_val = ptr + 5;
654 ptr += sprintf(ptr, " -p \"%s\"", r->Link.pageUrl.av_val);
655 argv[argc++].av_len = r->Link.pageUrl.av_len;
657 if (r->Link.usherToken.av_val)
659 argv[argc].av_val = ptr + 1;
660 argv[argc++].av_len = 2;
661 argv[argc].av_val = ptr + 5;
662 ptr += sprintf(ptr, " -j \"%s\"", r->Link.usherToken.av_val);
663 argv[argc++].av_len = r->Link.usherToken.av_len;
664 free(r->Link.usherToken.av_val);
665 r->Link.usherToken.av_val = NULL;
666 r->Link.usherToken.av_len = 0;
668 if (r->Link.extras.o_num) {
669 ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc);
670 AMF_Reset(&r->Link.extras);
672 argv[argc].av_val = ptr + 1;
673 argv[argc++].av_len = 2;
674 argv[argc].av_val = ptr + 5;
675 ptr += sprintf(ptr, " -y \"%.*s\"",
676 r->Link.playpath.av_len, r->Link.playpath.av_val);
677 argv[argc++].av_len = r->Link.playpath.av_len;
679 av = r->Link.playpath;
680 /* strip trailing URL parameters */
681 q = memchr(av.av_val, '?', av.av_len);
682 if (q)
684 if (q == av.av_val)
686 av.av_val++;
687 av.av_len--;
689 else
691 av.av_len = q - av.av_val;
694 /* strip leading slash components */
695 for (p=av.av_val+av.av_len-1; p>=av.av_val; p--)
696 if (*p == '/')
698 p++;
699 av.av_len -= p - av.av_val;
700 av.av_val = p;
701 break;
703 /* skip leading dot */
704 if (av.av_val[0] == '.')
706 av.av_val++;
707 av.av_len--;
709 file = malloc(av.av_len+5);
711 memcpy(file, av.av_val, av.av_len);
712 file[av.av_len] = '\0';
713 for (p=file; *p; p++)
714 if (*p == ':')
715 *p = '_';
717 /* Add extension if none present */
718 if (file[av.av_len - 4] != '.')
720 av.av_len += 4;
722 /* Always use flv extension, regardless of original */
723 if (strcmp(file+av.av_len-4, ".flv"))
725 strcpy(file+av.av_len-4, ".flv");
727 argv[argc].av_val = ptr + 1;
728 argv[argc++].av_len = 2;
729 argv[argc].av_val = file;
730 argv[argc].av_len = av.av_len;
731 ptr += sprintf(ptr, " -o %s", file);
732 now = RTMP_GetTime();
733 if (now - server->filetime < DUPTIME && AVMATCH(&argv[argc], &server->filename))
735 printf("Duplicate request, skipping.\n");
736 free(file);
738 else
740 printf("\n%s\n\n", cmd);
741 fflush(stdout);
742 server->filetime = now;
743 free(server->filename.av_val);
744 server->filename = argv[argc++];
745 spawn_dumper(argc, argv, cmd);
748 free(cmd);
750 pc.m_body = server->connect;
751 server->connect = NULL;
752 RTMPPacket_Free(&pc);
753 ret = 1;
754 RTMP_SendCtrl(r, 0, 1, 0);
755 SendPlayStart(r);
756 RTMP_SendCtrl(r, 1, 1, 0);
757 SendPlayStop(r);
759 AMF_Reset(&obj);
760 return ret;
764 ServePacket(STREAMING_SERVER *server, RTMP *r, RTMPPacket *packet)
766 int ret = 0;
768 RTMP_Log(RTMP_LOGDEBUG, "%s, received packet type %02X, size %lu bytes", __FUNCTION__,
769 packet->m_packetType, packet->m_nBodySize);
771 switch (packet->m_packetType)
773 case 0x01:
774 // chunk size
775 // HandleChangeChunkSize(r, packet);
776 break;
778 case 0x03:
779 // bytes read report
780 break;
782 case 0x04:
783 // ctrl
784 // HandleCtrl(r, packet);
785 break;
787 case 0x05:
788 // server bw
789 // HandleServerBW(r, packet);
790 break;
792 case 0x06:
793 // client bw
794 // HandleClientBW(r, packet);
795 break;
797 case 0x08:
798 // audio data
799 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
800 break;
802 case 0x09:
803 // video data
804 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
805 break;
807 case 0x0F: // flex stream send
808 break;
810 case 0x10: // flex shared object
811 break;
813 case 0x11: // flex message
815 RTMP_Log(RTMP_LOGDEBUG, "%s, flex message, size %lu bytes, not fully supported",
816 __FUNCTION__, packet->m_nBodySize);
817 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
819 // some DEBUG code
820 /*RTMP_LIB_AMFObject obj;
821 int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);
822 if(nRes < 0) {
823 RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);
824 //return;
827 obj.Dump(); */
829 if (ServeInvoke(server, r, packet, 1))
830 RTMP_Close(r);
831 break;
833 case 0x12:
834 // metadata (notify)
835 break;
837 case 0x13:
838 /* shared object */
839 break;
841 case 0x14:
842 // invoke
843 RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %lu bytes", __FUNCTION__,
844 packet->m_nBodySize);
845 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
847 if (ServeInvoke(server, r, packet, 0))
848 RTMP_Close(r);
849 break;
851 case 0x16:
852 /* flv */
853 break;
854 default:
855 RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,
856 packet->m_packetType);
857 #ifdef _DEBUG
858 RTMP_LogHex(RTMP_LOGDEBUG, packet->m_body, packet->m_nBodySize);
859 #endif
861 return ret;
864 TFTYPE
865 controlServerThread(void *unused)
867 char ich;
868 while (1)
870 ich = getchar();
871 switch (ich)
873 case 'q':
874 RTMP_LogPrintf("Exiting\n");
875 stopStreaming(rtmpServer);
876 exit(0);
877 break;
878 default:
879 RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich);
882 TFRET();
886 void doServe(STREAMING_SERVER * server, // server socket and state (our listening socket)
887 int sockfd // client connection socket
890 server->state = STREAMING_IN_PROGRESS;
892 RTMP rtmp = { 0 }; /* our session with the real client */
893 RTMPPacket packet = { 0 };
895 // timeout for http requests
896 fd_set fds;
897 struct timeval tv;
899 memset(&tv, 0, sizeof(struct timeval));
900 tv.tv_sec = 5;
902 FD_ZERO(&fds);
903 FD_SET(sockfd, &fds);
905 if (select(sockfd + 1, &fds, NULL, NULL, &tv) <= 0)
907 RTMP_Log(RTMP_LOGERROR, "Request timeout/select failed, ignoring request");
908 goto quit;
910 else
912 RTMP_Init(&rtmp);
913 rtmp.m_sb.sb_socket = sockfd;
914 if (!RTMP_Serve(&rtmp))
916 RTMP_Log(RTMP_LOGERROR, "Handshake failed");
917 goto cleanup;
920 server->arglen = 0;
921 while (RTMP_IsConnected(&rtmp) && RTMP_ReadPacket(&rtmp, &packet))
923 if (!RTMPPacket_IsReady(&packet))
924 continue;
925 ServePacket(server, &rtmp, &packet);
926 RTMPPacket_Free(&packet);
929 cleanup:
930 RTMP_LogPrintf("Closing connection... ");
931 RTMP_Close(&rtmp);
932 /* Should probably be done by RTMP_Close() ... */
933 rtmp.Link.playpath.av_val = NULL;
934 rtmp.Link.tcUrl.av_val = NULL;
935 rtmp.Link.swfUrl.av_val = NULL;
936 rtmp.Link.pageUrl.av_val = NULL;
937 rtmp.Link.app.av_val = NULL;
938 rtmp.Link.flashVer.av_val = NULL;
939 if (rtmp.Link.usherToken.av_val)
941 free(rtmp.Link.usherToken.av_val);
942 rtmp.Link.usherToken.av_val = NULL;
944 RTMP_LogPrintf("done!\n\n");
946 quit:
947 if (server->state == STREAMING_IN_PROGRESS)
948 server->state = STREAMING_ACCEPTING;
950 return;
953 TFTYPE
954 serverThread(void *arg)
956 STREAMING_SERVER *server = arg;
957 server->state = STREAMING_ACCEPTING;
959 while (server->state == STREAMING_ACCEPTING)
961 struct sockaddr_in addr;
962 socklen_t addrlen = sizeof(struct sockaddr_in);
963 int sockfd =
964 accept(server->socket, (struct sockaddr *) &addr, &addrlen);
966 if (sockfd > 0)
968 #ifdef linux
969 struct sockaddr_in dest;
970 char destch[16];
971 socklen_t destlen = sizeof(struct sockaddr_in);
972 getsockopt(sockfd, SOL_IP, SO_ORIGINAL_DST, &dest, &destlen);
973 strcpy(destch, inet_ntoa(dest.sin_addr));
974 RTMP_Log(RTMP_LOGDEBUG, "%s: accepted connection from %s to %s\n", __FUNCTION__,
975 inet_ntoa(addr.sin_addr), destch);
976 #else
977 RTMP_Log(RTMP_LOGDEBUG, "%s: accepted connection from %s\n", __FUNCTION__,
978 inet_ntoa(addr.sin_addr));
979 #endif
980 /* Create a new thread and transfer the control to that */
981 doServe(server, sockfd);
982 RTMP_Log(RTMP_LOGDEBUG, "%s: processed request\n", __FUNCTION__);
984 else
986 RTMP_Log(RTMP_LOGERROR, "%s: accept failed", __FUNCTION__);
989 server->state = STREAMING_STOPPED;
990 TFRET();
993 STREAMING_SERVER *
994 startStreaming(const char *address, int port)
996 struct sockaddr_in addr;
997 int sockfd, tmp;
998 STREAMING_SERVER *server;
1000 sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1001 if (sockfd == -1)
1003 RTMP_Log(RTMP_LOGERROR, "%s, couldn't create socket", __FUNCTION__);
1004 return 0;
1007 tmp = 1;
1008 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
1009 (char *) &tmp, sizeof(tmp) );
1011 addr.sin_family = AF_INET;
1012 addr.sin_addr.s_addr = inet_addr(address); //htonl(INADDR_ANY);
1013 addr.sin_port = htons(port);
1015 if (bind(sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) ==
1018 RTMP_Log(RTMP_LOGERROR, "%s, TCP bind failed for port number: %d", __FUNCTION__,
1019 port);
1020 return 0;
1023 if (listen(sockfd, 10) == -1)
1025 RTMP_Log(RTMP_LOGERROR, "%s, listen failed", __FUNCTION__);
1026 closesocket(sockfd);
1027 return 0;
1030 server = (STREAMING_SERVER *) calloc(1, sizeof(STREAMING_SERVER));
1031 server->socket = sockfd;
1033 ThreadCreate(serverThread, server);
1035 return server;
1038 void
1039 stopStreaming(STREAMING_SERVER * server)
1041 assert(server);
1043 if (server->state != STREAMING_STOPPED)
1045 if (server->state == STREAMING_IN_PROGRESS)
1047 server->state = STREAMING_STOPPING;
1049 // wait for streaming threads to exit
1050 while (server->state != STREAMING_STOPPED)
1051 msleep(1);
1054 if (closesocket(server->socket))
1055 RTMP_Log(RTMP_LOGERROR, "%s: Failed to close listening socket, error %d",
1056 GetSockError());
1058 server->state = STREAMING_STOPPED;
1063 void
1064 sigIntHandler(int sig)
1066 RTMP_ctrlC = TRUE;
1067 RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig);
1068 if (rtmpServer)
1069 stopStreaming(rtmpServer);
1070 signal(SIGINT, SIG_DFL);
1074 main(int argc, char **argv)
1076 int nStatus = RD_SUCCESS;
1078 // http streaming server
1079 char DEFAULT_HTTP_STREAMING_DEVICE[] = "0.0.0.0"; // 0.0.0.0 is any device
1081 char *rtmpStreamingDevice = DEFAULT_HTTP_STREAMING_DEVICE; // streaming device, default 0.0.0.0
1082 int nRtmpStreamingPort = 1935; // port
1084 RTMP_LogPrintf("RTMP Server %s\n", RTMPDUMP_VERSION);
1085 RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n");
1087 RTMP_debuglevel = RTMP_LOGINFO;
1089 if (argc > 1 && !strcmp(argv[1], "-z"))
1090 RTMP_debuglevel = RTMP_LOGALL;
1092 // init request
1093 memset(&defaultRTMPRequest, 0, sizeof(RTMP_REQUEST));
1095 defaultRTMPRequest.rtmpport = -1;
1096 defaultRTMPRequest.protocol = RTMP_PROTOCOL_UNDEFINED;
1097 defaultRTMPRequest.bLiveStream = FALSE; // is it a live stream? then we can't seek/resume
1099 defaultRTMPRequest.timeout = 300; // timeout connection afte 300 seconds
1100 defaultRTMPRequest.bufferTime = 20 * 1000;
1103 signal(SIGINT, sigIntHandler);
1104 #ifndef WIN32
1105 signal(SIGPIPE, SIG_IGN);
1106 #endif
1108 #ifdef _DEBUG
1109 netstackdump = fopen("netstackdump", "wb");
1110 netstackdump_read = fopen("netstackdump_read", "wb");
1111 #endif
1113 InitSockets();
1115 // start text UI
1116 ThreadCreate(controlServerThread, 0);
1118 // start http streaming
1119 if ((rtmpServer =
1120 startStreaming(rtmpStreamingDevice, nRtmpStreamingPort)) == 0)
1122 RTMP_Log(RTMP_LOGERROR, "Failed to start RTMP server, exiting!");
1123 return RD_FAILED;
1125 RTMP_LogPrintf("Streaming on rtmp://%s:%d\n", rtmpStreamingDevice,
1126 nRtmpStreamingPort);
1128 while (rtmpServer->state != STREAMING_STOPPED)
1130 sleep(1);
1132 RTMP_Log(RTMP_LOGDEBUG, "Done, exiting...");
1134 CleanupSockets();
1136 #ifdef _DEBUG
1137 if (netstackdump != 0)
1138 fclose(netstackdump);
1139 if (netstackdump_read != 0)
1140 fclose(netstackdump_read);
1141 #endif
1142 return nStatus;
1145 void
1146 AVreplace(AVal *src, const AVal *orig, const AVal *repl)
1148 char *srcbeg = src->av_val;
1149 char *srcend = src->av_val + src->av_len;
1150 char *dest, *sptr, *dptr;
1151 int n = 0;
1153 /* count occurrences of orig in src */
1154 sptr = src->av_val;
1155 while (sptr < srcend && (sptr = strstr(sptr, orig->av_val)))
1157 n++;
1158 sptr += orig->av_len;
1160 if (!n)
1161 return;
1163 dest = malloc(src->av_len + 1 + (repl->av_len - orig->av_len) * n);
1165 sptr = src->av_val;
1166 dptr = dest;
1167 while (sptr < srcend && (sptr = strstr(sptr, orig->av_val)))
1169 n = sptr - srcbeg;
1170 memcpy(dptr, srcbeg, n);
1171 dptr += n;
1172 memcpy(dptr, repl->av_val, repl->av_len);
1173 dptr += repl->av_len;
1174 sptr += orig->av_len;
1175 srcbeg = sptr;
1177 n = srcend - srcbeg;
1178 memcpy(dptr, srcbeg, n);
1179 dptr += n;
1180 *dptr = '\0';
1181 src->av_val = dest;
1182 src->av_len = dptr - dest;