Check the return value from RTMP_SendBytesReceived()
[rtmpdump.git] / rtmpsrv.c
blobf1b6c66f9f5586228064948a104c1c4e859bc6b9
1 /* Simple RTMP Server
2 * Copyright (C) 2009 Andrej Stepanchuk
3 * Copyright (C) 2009 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);
99 typedef struct
101 char *hostname;
102 int rtmpport;
103 int protocol;
104 int bLiveStream; // is it a live stream? then we can't seek/resume
106 long int timeout; // timeout connection afte 300 seconds
107 uint32_t bufferTime;
109 char *rtmpurl;
110 AVal playpath;
111 AVal swfUrl;
112 AVal tcUrl;
113 AVal pageUrl;
114 AVal app;
115 AVal auth;
116 AVal swfHash;
117 AVal flashVer;
118 AVal subscribepath;
119 uint32_t swfSize;
121 uint32_t dStartOffset;
122 uint32_t dStopOffset;
123 uint32_t nTimeStamp;
124 } RTMP_REQUEST;
126 #define STR2AVAL(av,str) av.av_val = str; av.av_len = strlen(av.av_val)
128 /* this request is formed from the parameters and used to initialize a new request,
129 * thus it is a default settings list. All settings can be overriden by specifying the
130 * parameters in the GET request. */
131 RTMP_REQUEST defaultRTMPRequest;
133 #ifdef _DEBUG
134 uint32_t debugTS = 0;
136 int pnum = 0;
138 FILE *netstackdump = NULL;
139 FILE *netstackdump_read = NULL;
140 #endif
142 #define SAVC(x) static const AVal av_##x = AVC(#x)
144 SAVC(app);
145 SAVC(connect);
146 SAVC(flashVer);
147 SAVC(swfUrl);
148 SAVC(pageUrl);
149 SAVC(tcUrl);
150 SAVC(fpad);
151 SAVC(capabilities);
152 SAVC(audioCodecs);
153 SAVC(videoCodecs);
154 SAVC(videoFunction);
155 SAVC(objectEncoding);
156 SAVC(_result);
157 SAVC(createStream);
158 SAVC(getStreamLength);
159 SAVC(play);
160 SAVC(fmsVer);
161 SAVC(mode);
162 SAVC(level);
163 SAVC(code);
164 SAVC(description);
165 SAVC(secureToken);
167 static int
168 SendConnectResult(RTMP *r, double txn)
170 RTMPPacket packet;
171 char pbuf[384], *pend = pbuf+sizeof(pbuf);
172 AMFObject obj;
173 AMFObjectProperty p, op;
174 AVal av;
176 packet.m_nChannel = 0x03; // control channel (invoke)
177 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
178 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
179 packet.m_nTimeStamp = 0;
180 packet.m_nInfoField2 = 0;
181 packet.m_hasAbsTimestamp = 0;
182 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
184 char *enc = packet.m_body;
185 enc = AMF_EncodeString(enc, pend, &av__result);
186 enc = AMF_EncodeNumber(enc, pend, txn);
187 *enc++ = AMF_OBJECT;
189 STR2AVAL(av, "FMS/3,5,1,525");
190 enc = AMF_EncodeNamedString(enc, pend, &av_fmsVer, &av);
191 enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 31.0);
192 enc = AMF_EncodeNamedNumber(enc, pend, &av_mode, 1.0);
193 *enc++ = 0;
194 *enc++ = 0;
195 *enc++ = AMF_OBJECT_END;
197 *enc++ = AMF_OBJECT;
199 STR2AVAL(av, "status");
200 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av);
201 STR2AVAL(av, "NetConnection.Connect.Success");
202 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av);
203 STR2AVAL(av, "Connection succeeded.");
204 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av);
205 enc = AMF_EncodeNamedNumber(enc, pend, &av_objectEncoding, r->m_fEncoding);
206 #if 0
207 STR2AVAL(av, "58656322c972d6cdf2d776167575045f8484ea888e31c086f7b5ffbd0baec55ce442c2fb");
208 enc = AMF_EncodeNamedString(enc, pend, &av_secureToken, &av);
209 #endif
210 STR2AVAL(p.p_name, "version");
211 STR2AVAL(p.p_vu.p_aval, "3,5,1,525");
212 p.p_type = AMF_STRING;
213 obj.o_num = 1;
214 obj.o_props = &p;
215 op.p_type = AMF_OBJECT;
216 STR2AVAL(op.p_name, "data");
217 op.p_vu.p_object = obj;
218 enc = AMFProp_Encode(&op, enc, pend);
219 *enc++ = 0;
220 *enc++ = 0;
221 *enc++ = AMF_OBJECT_END;
222 *enc++ = 0;
223 *enc++ = 0;
224 *enc++ = AMF_OBJECT_END;
226 packet.m_nBodySize = enc - packet.m_body;
228 return RTMP_SendPacket(r, &packet, FALSE);
231 static int
232 SendResultNumber(RTMP *r, double txn, double ID)
234 RTMPPacket packet;
235 char pbuf[256], *pend = pbuf+sizeof(pbuf);
237 packet.m_nChannel = 0x03; // control channel (invoke)
238 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
239 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
240 packet.m_nTimeStamp = 0;
241 packet.m_nInfoField2 = 0;
242 packet.m_hasAbsTimestamp = 0;
243 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
245 char *enc = packet.m_body;
246 enc = AMF_EncodeString(enc, pend, &av__result);
247 enc = AMF_EncodeNumber(enc, pend, txn);
248 *enc++ = AMF_NULL;
249 enc = AMF_EncodeNumber(enc, pend, ID);
251 packet.m_nBodySize = enc - packet.m_body;
253 return RTMP_SendPacket(r, &packet, FALSE);
256 SAVC(onStatus);
257 SAVC(status);
258 static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start");
259 static const AVal av_Started_playing = AVC("Started playing");
260 static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop");
261 static const AVal av_Stopped_playing = AVC("Stopped playing");
262 SAVC(details);
263 SAVC(clientid);
265 static int
266 SendPlayStart(RTMP *r)
268 RTMPPacket packet;
269 char pbuf[512], *pend = pbuf+sizeof(pbuf);
271 packet.m_nChannel = 0x03; // control channel (invoke)
272 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
273 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
274 packet.m_nTimeStamp = 0;
275 packet.m_nInfoField2 = 0;
276 packet.m_hasAbsTimestamp = 0;
277 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
279 char *enc = packet.m_body;
280 enc = AMF_EncodeString(enc, pend, &av_onStatus);
281 enc = AMF_EncodeNumber(enc, pend, 0);
282 *enc++ = AMF_OBJECT;
284 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
285 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Start);
286 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_Started_playing);
287 enc = AMF_EncodeNamedString(enc, pend, &av_details, &r->Link.playpath);
288 enc = AMF_EncodeNamedString(enc, pend, &av_clientid, &av_clientid);
289 *enc++ = 0;
290 *enc++ = 0;
291 *enc++ = AMF_OBJECT_END;
293 packet.m_nBodySize = enc - packet.m_body;
294 return RTMP_SendPacket(r, &packet, FALSE);
297 static int
298 SendPlayStop(RTMP *r)
300 RTMPPacket packet;
301 char pbuf[512], *pend = pbuf+sizeof(pbuf);
303 packet.m_nChannel = 0x03; // control channel (invoke)
304 packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
305 packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
306 packet.m_nTimeStamp = 0;
307 packet.m_nInfoField2 = 0;
308 packet.m_hasAbsTimestamp = 0;
309 packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
311 char *enc = packet.m_body;
312 enc = AMF_EncodeString(enc, pend, &av_onStatus);
313 enc = AMF_EncodeNumber(enc, pend, 0);
314 *enc++ = AMF_OBJECT;
316 enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
317 enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Stop);
318 enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_Stopped_playing);
319 enc = AMF_EncodeNamedString(enc, pend, &av_details, &r->Link.playpath);
320 enc = AMF_EncodeNamedString(enc, pend, &av_clientid, &av_clientid);
321 *enc++ = 0;
322 *enc++ = 0;
323 *enc++ = AMF_OBJECT_END;
325 packet.m_nBodySize = enc - packet.m_body;
326 return RTMP_SendPacket(r, &packet, FALSE);
329 static void
330 spawn_dumper(int argc, AVal *av, char *cmd)
332 #ifdef WIN32
333 STARTUPINFO si = {0};
334 PROCESS_INFORMATION pi = {0};
336 si.cb = sizeof(si);
337 if (CreateProcess(NULL, cmd, NULL, NULL, FALSE, 0, NULL, NULL,
338 &si, &pi))
340 CloseHandle(pi.hThread);
341 CloseHandle(pi.hProcess);
343 #else
344 /* reap any dead children */
345 while (waitpid(-1, NULL, WNOHANG) > 0);
347 if (fork() == 0) {
348 char **argv = malloc((argc+1) * sizeof(char *));
349 int i;
351 for (i=0; i<argc; i++) {
352 argv[i] = av[i].av_val;
353 argv[i][av[i].av_len] = '\0';
355 argv[i] = NULL;
356 if ((i = execvp(argv[0], argv)))
357 _exit(i);
359 #endif
362 static int
363 countAMF(AMFObject *obj, int *argc)
365 int i, len;
367 for (i=0, len=0; i < obj->o_num; i++)
369 AMFObjectProperty *p = &obj->o_props[i];
370 len += 4;
371 (*argc)+= 2;
372 if (p->p_name.av_val)
373 len += 1;
374 len += 2;
375 if (p->p_name.av_val)
376 len += p->p_name.av_len + 1;
377 switch(p->p_type)
379 case AMF_BOOLEAN:
380 len += 1;
381 break;
382 case AMF_STRING:
383 len += p->p_vu.p_aval.av_len;
384 break;
385 case AMF_NUMBER:
386 len += 40;
387 break;
388 case AMF_OBJECT:
389 len += 9;
390 len += countAMF(&p->p_vu.p_object, argc);
391 (*argc) += 2;
392 break;
393 case AMF_NULL:
394 default:
395 break;
398 return len;
401 static char *
402 dumpAMF(AMFObject *obj, char *ptr, AVal *argv, int *argc)
404 int i, len, ac = *argc;
405 const char opt[] = "NBSO Z";
407 for (i=0, len=0; i < obj->o_num; i++)
409 AMFObjectProperty *p = &obj->o_props[i];
410 argv[ac].av_val = ptr+1;
411 argv[ac++].av_len = 2;
412 ptr += sprintf(ptr, " -C ");
413 argv[ac].av_val = ptr;
414 if (p->p_name.av_val)
415 *ptr++ = 'N';
416 *ptr++ = opt[p->p_type];
417 *ptr++ = ':';
418 if (p->p_name.av_val)
419 ptr += sprintf(ptr, "%.*s:", p->p_name.av_len, p->p_name.av_val);
420 switch(p->p_type)
422 case AMF_BOOLEAN:
423 *ptr++ = p->p_vu.p_number != 0 ? '1' : '0';
424 argv[ac].av_len = ptr - argv[ac].av_val;
425 break;
426 case AMF_STRING:
427 memcpy(ptr, p->p_vu.p_aval.av_val, p->p_vu.p_aval.av_len);
428 ptr += p->p_vu.p_aval.av_len;
429 argv[ac].av_len = ptr - argv[ac].av_val;
430 break;
431 case AMF_NUMBER:
432 ptr += sprintf(ptr, "%f", p->p_vu.p_number);
433 argv[ac].av_len = ptr - argv[ac].av_val;
434 break;
435 case AMF_OBJECT:
436 *ptr++ = '1';
437 argv[ac].av_len = ptr - argv[ac].av_val;
438 ac++;
439 *argc = ac;
440 ptr = dumpAMF(&p->p_vu.p_object, ptr, argv, argc);
441 ac = *argc;
442 argv[ac].av_val = ptr+1;
443 argv[ac++].av_len = 2;
444 argv[ac].av_val = ptr+4;
445 argv[ac].av_len = 3;
446 ptr += sprintf(ptr, " -C O:0");
447 break;
448 case AMF_NULL:
449 default:
450 argv[ac].av_len = ptr - argv[ac].av_val;
451 break;
453 ac++;
455 *argc = ac;
456 return ptr;
459 // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
461 ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int offset)
463 const char *body;
464 unsigned int nBodySize;
465 int ret = 0, nRes;
467 body = packet->m_body + offset;
468 nBodySize = packet->m_nBodySize - offset;
470 if (body[0] != 0x02) // make sure it is a string method name we start with
472 RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet",
473 __FUNCTION__);
474 return 0;
477 AMFObject obj;
478 nRes = AMF_Decode(&obj, body, nBodySize, FALSE);
479 if (nRes < 0)
481 RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__);
482 return 0;
485 AMF_Dump(&obj);
486 AVal method;
487 AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method);
488 double txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1));
489 RTMP_Log(RTMP_LOGDEBUG, "%s, client invoking <%s>", __FUNCTION__, method.av_val);
491 if (AVMATCH(&method, &av_connect))
493 AMFObject cobj;
494 AVal pname, pval;
495 int i;
497 server->connect = packet->m_body;
498 packet->m_body = NULL;
500 AMFProp_GetObject(AMF_GetProp(&obj, NULL, 2), &cobj);
501 for (i=0; i<cobj.o_num; i++)
503 pname = cobj.o_props[i].p_name;
504 pval.av_val = NULL;
505 pval.av_len = 0;
506 if (cobj.o_props[i].p_type == AMF_STRING)
507 pval = cobj.o_props[i].p_vu.p_aval;
508 if (AVMATCH(&pname, &av_app))
510 r->Link.app = pval;
511 pval.av_val = NULL;
512 if (!r->Link.app.av_val)
513 r->Link.app.av_val = "";
514 server->arglen += 6 + pval.av_len;
515 server->argc += 2;
517 else if (AVMATCH(&pname, &av_flashVer))
519 r->Link.flashVer = pval;
520 pval.av_val = NULL;
521 server->arglen += 6 + pval.av_len;
522 server->argc += 2;
524 else if (AVMATCH(&pname, &av_swfUrl))
526 r->Link.swfUrl = pval;
527 pval.av_val = NULL;
528 server->arglen += 6 + pval.av_len;
529 server->argc += 2;
531 else if (AVMATCH(&pname, &av_tcUrl))
533 r->Link.tcUrl = pval;
534 pval.av_val = NULL;
535 server->arglen += 6 + pval.av_len;
536 server->argc += 2;
538 else if (AVMATCH(&pname, &av_pageUrl))
540 r->Link.pageUrl = pval;
541 pval.av_val = NULL;
542 server->arglen += 6 + pval.av_len;
543 server->argc += 2;
545 else if (AVMATCH(&pname, &av_audioCodecs))
547 r->m_fAudioCodecs = cobj.o_props[i].p_vu.p_number;
549 else if (AVMATCH(&pname, &av_videoCodecs))
551 r->m_fVideoCodecs = cobj.o_props[i].p_vu.p_number;
553 else if (AVMATCH(&pname, &av_objectEncoding))
555 r->m_fEncoding = cobj.o_props[i].p_vu.p_number;
558 /* Still have more parameters? Copy them */
559 if (obj.o_num > 3)
561 int i = obj.o_num - 3;
562 r->Link.extras.o_num = i;
563 r->Link.extras.o_props = malloc(i*sizeof(AMFObjectProperty));
564 memcpy(r->Link.extras.o_props, obj.o_props+3, i*sizeof(AMFObjectProperty));
565 obj.o_num = 3;
566 server->arglen += countAMF(&r->Link.extras, &server->argc);
568 SendConnectResult(r, txn);
570 else if (AVMATCH(&method, &av_createStream))
572 SendResultNumber(r, txn, ++server->streamID);
574 else if (AVMATCH(&method, &av_getStreamLength))
576 SendResultNumber(r, txn, 10.0);
578 else if (AVMATCH(&method, &av_play))
580 char *file, *p, *q, *cmd, *ptr;
581 AVal *argv, av;
582 int len, argc;
583 uint32_t now;
584 RTMPPacket pc = {0};
585 AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &r->Link.playpath);
587 r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4));
588 if (obj.o_num > 5)
589 r->Link.length = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 5));
591 if (r->Link.tcUrl.av_len)
593 len = server->arglen + r->Link.playpath.av_len + 4 +
594 sizeof("rtmpdump") + r->Link.playpath.av_len + 12;
595 server->argc += 5;
597 cmd = malloc(len + server->argc * sizeof(AVal));
598 ptr = cmd;
599 argv = (AVal *)(cmd + len);
600 argv[0].av_val = cmd;
601 argv[0].av_len = sizeof("rtmpdump")-1;
602 ptr += sprintf(ptr, "rtmpdump");
603 argc = 1;
605 argv[argc].av_val = ptr + 1;
606 argv[argc++].av_len = 2;
607 argv[argc].av_val = ptr + 5;
608 ptr += sprintf(ptr," -r \"%s\"", r->Link.tcUrl.av_val);
609 argv[argc++].av_len = r->Link.tcUrl.av_len;
611 if (r->Link.app.av_val)
613 argv[argc].av_val = ptr + 1;
614 argv[argc++].av_len = 2;
615 argv[argc].av_val = ptr + 5;
616 ptr += sprintf(ptr, " -a \"%s\"", r->Link.app.av_val);
617 argv[argc++].av_len = r->Link.app.av_len;
619 if (r->Link.flashVer.av_val)
621 argv[argc].av_val = ptr + 1;
622 argv[argc++].av_len = 2;
623 argv[argc].av_val = ptr + 5;
624 ptr += sprintf(ptr, " -f \"%s\"", r->Link.flashVer.av_val);
625 argv[argc++].av_len = r->Link.flashVer.av_len;
627 if (r->Link.swfUrl.av_val)
629 argv[argc].av_val = ptr + 1;
630 argv[argc++].av_len = 2;
631 argv[argc].av_val = ptr + 5;
632 ptr += sprintf(ptr, " -W \"%s\"", r->Link.swfUrl.av_val);
633 argv[argc++].av_len = r->Link.swfUrl.av_len;
635 if (r->Link.pageUrl.av_val)
637 argv[argc].av_val = ptr + 1;
638 argv[argc++].av_len = 2;
639 argv[argc].av_val = ptr + 5;
640 ptr += sprintf(ptr, " -p \"%s\"", r->Link.pageUrl.av_val);
641 argv[argc++].av_len = r->Link.pageUrl.av_len;
643 if (r->Link.extras.o_num) {
644 ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc);
645 AMF_Reset(&r->Link.extras);
647 argv[argc].av_val = ptr + 1;
648 argv[argc++].av_len = 2;
649 argv[argc].av_val = ptr + 5;
650 ptr += sprintf(ptr, " -y \"%.*s\"",
651 r->Link.playpath.av_len, r->Link.playpath.av_val);
652 argv[argc++].av_len = r->Link.playpath.av_len;
654 av = r->Link.playpath;
655 /* strip trailing URL parameters */
656 q = memchr(av.av_val, '?', av.av_len);
657 if (q)
659 if (q == av.av_val)
661 av.av_val++;
662 av.av_len--;
664 else
666 av.av_len = q - av.av_val;
669 /* strip leading slash components */
670 for (p=av.av_val+av.av_len-1; p>=av.av_val; p--)
671 if (*p == '/')
673 p++;
674 av.av_len -= p - av.av_val;
675 av.av_val = p;
676 break;
678 /* skip leading dot */
679 if (av.av_val[0] == '.')
681 av.av_val++;
682 av.av_len--;
684 file = malloc(av.av_len+5);
686 memcpy(file, av.av_val, av.av_len);
687 file[av.av_len] = '\0';
688 for (p=file; *p; p++)
689 if (*p == ':')
690 *p = '_';
692 /* Add extension if none present */
693 if (file[av.av_len - 4] != '.')
695 av.av_len += 4;
697 /* Always use flv extension, regardless of original */
698 if (strcmp(file+av.av_len-4, ".flv"))
700 strcpy(file+av.av_len-4, ".flv");
702 argv[argc].av_val = ptr + 1;
703 argv[argc++].av_len = 2;
704 argv[argc].av_val = file;
705 argv[argc].av_len = av.av_len;
706 ptr += sprintf(ptr, " -o %s", file);
707 now = RTMP_GetTime();
708 if (now - server->filetime < DUPTIME && AVMATCH(&argv[argc], &server->filename))
710 printf("Duplicate request, skipping.\n");
711 free(file);
713 else
715 printf("\n%s\n\n", cmd);
716 fflush(stdout);
717 server->filetime = now;
718 free(server->filename.av_val);
719 server->filename = argv[argc++];
720 spawn_dumper(argc, argv, cmd);
723 free(cmd);
725 pc.m_body = server->connect;
726 server->connect = NULL;
727 RTMPPacket_Free(&pc);
728 ret = 1;
729 RTMP_SendCtrl(r, 0, 1, 0);
730 SendPlayStart(r);
731 RTMP_SendCtrl(r, 1, 1, 0);
732 SendPlayStop(r);
734 AMF_Reset(&obj);
735 return ret;
739 ServePacket(STREAMING_SERVER *server, RTMP *r, RTMPPacket *packet)
741 int ret = 0;
743 RTMP_Log(RTMP_LOGDEBUG, "%s, received packet type %02X, size %lu bytes", __FUNCTION__,
744 packet->m_packetType, packet->m_nBodySize);
746 switch (packet->m_packetType)
748 case 0x01:
749 // chunk size
750 // HandleChangeChunkSize(r, packet);
751 break;
753 case 0x03:
754 // bytes read report
755 break;
757 case 0x04:
758 // ctrl
759 // HandleCtrl(r, packet);
760 break;
762 case 0x05:
763 // server bw
764 // HandleServerBW(r, packet);
765 break;
767 case 0x06:
768 // client bw
769 // HandleClientBW(r, packet);
770 break;
772 case 0x08:
773 // audio data
774 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
775 break;
777 case 0x09:
778 // video data
779 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
780 break;
782 case 0x0F: // flex stream send
783 break;
785 case 0x10: // flex shared object
786 break;
788 case 0x11: // flex message
790 RTMP_Log(RTMP_LOGDEBUG, "%s, flex message, size %lu bytes, not fully supported",
791 __FUNCTION__, packet->m_nBodySize);
792 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
794 // some DEBUG code
795 /*RTMP_LIB_AMFObject obj;
796 int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);
797 if(nRes < 0) {
798 RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);
799 //return;
802 obj.Dump(); */
804 if (ServeInvoke(server, r, packet, 1))
805 RTMP_Close(r);
806 break;
808 case 0x12:
809 // metadata (notify)
810 break;
812 case 0x13:
813 /* shared object */
814 break;
816 case 0x14:
817 // invoke
818 RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %lu bytes", __FUNCTION__,
819 packet->m_nBodySize);
820 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
822 if (ServeInvoke(server, r, packet, 0))
823 RTMP_Close(r);
824 break;
826 case 0x16:
827 /* flv */
828 break;
829 default:
830 RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,
831 packet->m_packetType);
832 #ifdef _DEBUG
833 RTMP_LogHex(RTMP_LOGDEBUG, packet->m_body, packet->m_nBodySize);
834 #endif
836 return ret;
839 TFTYPE
840 controlServerThread(void *unused)
842 char ich;
843 while (1)
845 ich = getchar();
846 switch (ich)
848 case 'q':
849 RTMP_LogPrintf("Exiting\n");
850 stopStreaming(rtmpServer);
851 exit(0);
852 break;
853 default:
854 RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich);
857 TFRET();
861 void doServe(STREAMING_SERVER * server, // server socket and state (our listening socket)
862 int sockfd // client connection socket
865 server->state = STREAMING_IN_PROGRESS;
867 RTMP rtmp = { 0 }; /* our session with the real client */
868 RTMPPacket packet = { 0 };
870 // timeout for http requests
871 fd_set fds;
872 struct timeval tv;
874 memset(&tv, 0, sizeof(struct timeval));
875 tv.tv_sec = 5;
877 FD_ZERO(&fds);
878 FD_SET(sockfd, &fds);
880 if (select(sockfd + 1, &fds, NULL, NULL, &tv) <= 0)
882 RTMP_Log(RTMP_LOGERROR, "Request timeout/select failed, ignoring request");
883 goto quit;
885 else
887 RTMP_Init(&rtmp);
888 rtmp.m_sb.sb_socket = sockfd;
889 if (!RTMP_Serve(&rtmp))
891 RTMP_Log(RTMP_LOGERROR, "Handshake failed");
892 goto cleanup;
895 server->arglen = 0;
896 while (RTMP_IsConnected(&rtmp) && RTMP_ReadPacket(&rtmp, &packet))
898 if (!RTMPPacket_IsReady(&packet))
899 continue;
900 ServePacket(server, &rtmp, &packet);
901 RTMPPacket_Free(&packet);
904 cleanup:
905 RTMP_LogPrintf("Closing connection... ");
906 RTMP_Close(&rtmp);
907 /* Should probably be done by RTMP_Close() ... */
908 rtmp.Link.playpath.av_val = NULL;
909 rtmp.Link.tcUrl.av_val = NULL;
910 rtmp.Link.swfUrl.av_val = NULL;
911 rtmp.Link.pageUrl.av_val = NULL;
912 rtmp.Link.app.av_val = NULL;
913 rtmp.Link.flashVer.av_val = NULL;
914 RTMP_LogPrintf("done!\n\n");
916 quit:
917 if (server->state == STREAMING_IN_PROGRESS)
918 server->state = STREAMING_ACCEPTING;
920 return;
923 TFTYPE
924 serverThread(void *arg)
926 STREAMING_SERVER *server = arg;
927 server->state = STREAMING_ACCEPTING;
929 while (server->state == STREAMING_ACCEPTING)
931 struct sockaddr_in addr;
932 socklen_t addrlen = sizeof(struct sockaddr_in);
933 int sockfd =
934 accept(server->socket, (struct sockaddr *) &addr, &addrlen);
936 if (sockfd > 0)
938 #ifdef linux
939 struct sockaddr_in dest;
940 char destch[16];
941 socklen_t destlen = sizeof(struct sockaddr_in);
942 getsockopt(sockfd, SOL_IP, SO_ORIGINAL_DST, &dest, &destlen);
943 strcpy(destch, inet_ntoa(dest.sin_addr));
944 RTMP_Log(RTMP_LOGDEBUG, "%s: accepted connection from %s to %s\n", __FUNCTION__,
945 inet_ntoa(addr.sin_addr), destch);
946 #else
947 RTMP_Log(RTMP_LOGDEBUG, "%s: accepted connection from %s\n", __FUNCTION__,
948 inet_ntoa(addr.sin_addr));
949 #endif
950 /* Create a new thread and transfer the control to that */
951 doServe(server, sockfd);
952 RTMP_Log(RTMP_LOGDEBUG, "%s: processed request\n", __FUNCTION__);
954 else
956 RTMP_Log(RTMP_LOGERROR, "%s: accept failed", __FUNCTION__);
959 server->state = STREAMING_STOPPED;
960 TFRET();
963 STREAMING_SERVER *
964 startStreaming(const char *address, int port)
966 struct sockaddr_in addr;
967 int sockfd, tmp;
968 STREAMING_SERVER *server;
970 sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
971 if (sockfd == -1)
973 RTMP_Log(RTMP_LOGERROR, "%s, couldn't create socket", __FUNCTION__);
974 return 0;
977 tmp = 1;
978 setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
979 (char *) &tmp, sizeof(tmp) );
981 addr.sin_family = AF_INET;
982 addr.sin_addr.s_addr = inet_addr(address); //htonl(INADDR_ANY);
983 addr.sin_port = htons(port);
985 if (bind(sockfd, (struct sockaddr *) &addr, sizeof(struct sockaddr_in)) ==
988 RTMP_Log(RTMP_LOGERROR, "%s, TCP bind failed for port number: %d", __FUNCTION__,
989 port);
990 return 0;
993 if (listen(sockfd, 10) == -1)
995 RTMP_Log(RTMP_LOGERROR, "%s, listen failed", __FUNCTION__);
996 closesocket(sockfd);
997 return 0;
1000 server = (STREAMING_SERVER *) calloc(1, sizeof(STREAMING_SERVER));
1001 server->socket = sockfd;
1003 ThreadCreate(serverThread, server);
1005 return server;
1008 void
1009 stopStreaming(STREAMING_SERVER * server)
1011 assert(server);
1013 if (server->state != STREAMING_STOPPED)
1015 if (server->state == STREAMING_IN_PROGRESS)
1017 server->state = STREAMING_STOPPING;
1019 // wait for streaming threads to exit
1020 while (server->state != STREAMING_STOPPED)
1021 msleep(1);
1024 if (closesocket(server->socket))
1025 RTMP_Log(RTMP_LOGERROR, "%s: Failed to close listening socket, error %d",
1026 GetSockError());
1028 server->state = STREAMING_STOPPED;
1033 void
1034 sigIntHandler(int sig)
1036 RTMP_ctrlC = TRUE;
1037 RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig);
1038 if (rtmpServer)
1039 stopStreaming(rtmpServer);
1040 signal(SIGINT, SIG_DFL);
1044 main(int argc, char **argv)
1046 int nStatus = RD_SUCCESS;
1048 // http streaming server
1049 char DEFAULT_HTTP_STREAMING_DEVICE[] = "0.0.0.0"; // 0.0.0.0 is any device
1051 char *rtmpStreamingDevice = DEFAULT_HTTP_STREAMING_DEVICE; // streaming device, default 0.0.0.0
1052 int nRtmpStreamingPort = 1935; // port
1054 RTMP_LogPrintf("RTMP Server %s\n", RTMPDUMP_VERSION);
1055 RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n");
1057 RTMP_debuglevel = RTMP_LOGINFO;
1059 if (argc > 1 && !strcmp(argv[1], "-z"))
1060 RTMP_debuglevel = RTMP_LOGALL;
1062 // init request
1063 memset(&defaultRTMPRequest, 0, sizeof(RTMP_REQUEST));
1065 defaultRTMPRequest.rtmpport = -1;
1066 defaultRTMPRequest.protocol = RTMP_PROTOCOL_UNDEFINED;
1067 defaultRTMPRequest.bLiveStream = FALSE; // is it a live stream? then we can't seek/resume
1069 defaultRTMPRequest.timeout = 300; // timeout connection afte 300 seconds
1070 defaultRTMPRequest.bufferTime = 20 * 1000;
1073 signal(SIGINT, sigIntHandler);
1074 #ifndef WIN32
1075 signal(SIGPIPE, SIG_IGN);
1076 #endif
1078 #ifdef _DEBUG
1079 netstackdump = fopen("netstackdump", "wb");
1080 netstackdump_read = fopen("netstackdump_read", "wb");
1081 #endif
1083 InitSockets();
1085 // start text UI
1086 ThreadCreate(controlServerThread, 0);
1088 // start http streaming
1089 if ((rtmpServer =
1090 startStreaming(rtmpStreamingDevice, nRtmpStreamingPort)) == 0)
1092 RTMP_Log(RTMP_LOGERROR, "Failed to start RTMP server, exiting!");
1093 return RD_FAILED;
1095 RTMP_LogPrintf("Streaming on rtmp://%s:%d\n", rtmpStreamingDevice,
1096 nRtmpStreamingPort);
1098 while (rtmpServer->state != STREAMING_STOPPED)
1100 sleep(1);
1102 RTMP_Log(RTMP_LOGDEBUG, "Done, exiting...");
1104 CleanupSockets();
1106 #ifdef _DEBUG
1107 if (netstackdump != 0)
1108 fclose(netstackdump);
1109 if (netstackdump_read != 0)
1110 fclose(netstackdump_read);
1111 #endif
1112 return nStatus;