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)
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.
37 #include "librtmp/rtmp_sys.h"
38 #include "librtmp/log.h"
43 #include <linux/netfilter_ipv4.h>
47 #include <sys/types.h>
53 #define RD_INCOMPLETE 2
55 #define PACKET_SIZE 1024*1024
58 #define InitSockets() {\
62 version = MAKEWORD(1,1); \
63 WSAStartup(version, &wsaData); }
65 #define CleanupSockets() WSACleanup()
68 #define CleanupSockets()
71 #define DUPTIME 5000 /* interval we disallow duplicate requests, in msec */
76 STREAMING_IN_PROGRESS
,
88 uint32_t filetime
; /* time of last download we started */
89 AVal filename
; /* name of last download */
94 STREAMING_SERVER
*rtmpServer
= 0; // server structure pointer
96 STREAMING_SERVER
*startStreaming(const char *address
, int port
);
97 void stopStreaming(STREAMING_SERVER
* server
);
104 int bLiveStream
; // is it a live stream? then we can't seek/resume
106 long int timeout
; // timeout connection afte 300 seconds
121 uint32_t dStartOffset
;
122 uint32_t dStopOffset
;
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
;
134 uint32_t debugTS
= 0;
138 FILE *netstackdump
= NULL
;
139 FILE *netstackdump_read
= NULL
;
142 #define SAVC(x) static const AVal av_##x = AVC(#x)
155 SAVC(objectEncoding
);
158 SAVC(getStreamLength
);
168 SendConnectResult(RTMP
*r
, double txn
)
171 char pbuf
[384], *pend
= pbuf
+sizeof(pbuf
);
173 AMFObjectProperty p
, op
;
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
);
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);
195 *enc
++ = AMF_OBJECT_END
;
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
);
207 STR2AVAL(av
, "58656322c972d6cdf2d776167575045f8484ea888e31c086f7b5ffbd0baec55ce442c2fb");
208 enc
= AMF_EncodeNamedString(enc
, pend
, &av_secureToken
, &av
);
210 STR2AVAL(p
.p_name
, "version");
211 STR2AVAL(p
.p_vu
.p_aval
, "3,5,1,525");
212 p
.p_type
= AMF_STRING
;
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
);
221 *enc
++ = AMF_OBJECT_END
;
224 *enc
++ = AMF_OBJECT_END
;
226 packet
.m_nBodySize
= enc
- packet
.m_body
;
228 return RTMP_SendPacket(r
, &packet
, FALSE
);
232 SendResultNumber(RTMP
*r
, double txn
, double ID
)
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
);
249 enc
= AMF_EncodeNumber(enc
, pend
, ID
);
251 packet
.m_nBodySize
= enc
- packet
.m_body
;
253 return RTMP_SendPacket(r
, &packet
, FALSE
);
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");
266 SendPlayStart(RTMP
*r
)
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);
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
);
291 *enc
++ = AMF_OBJECT_END
;
293 packet
.m_nBodySize
= enc
- packet
.m_body
;
294 return RTMP_SendPacket(r
, &packet
, FALSE
);
298 SendPlayStop(RTMP
*r
)
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);
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
);
323 *enc
++ = AMF_OBJECT_END
;
325 packet
.m_nBodySize
= enc
- packet
.m_body
;
326 return RTMP_SendPacket(r
, &packet
, FALSE
);
330 spawn_dumper(int argc
, AVal
*av
, char *cmd
)
333 STARTUPINFO si
= {0};
334 PROCESS_INFORMATION pi
= {0};
337 if (CreateProcess(NULL
, cmd
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
,
340 CloseHandle(pi
.hThread
);
341 CloseHandle(pi
.hProcess
);
344 /* reap any dead children */
345 while (waitpid(-1, NULL
, WNOHANG
) > 0);
348 char **argv
= malloc((argc
+1) * sizeof(char *));
351 for (i
=0; i
<argc
; i
++) {
352 argv
[i
] = av
[i
].av_val
;
353 argv
[i
][av
[i
].av_len
] = '\0';
356 if ((i
= execvp(argv
[0], argv
)))
363 countAMF(AMFObject
*obj
, int *argc
)
367 for (i
=0, len
=0; i
< obj
->o_num
; i
++)
369 AMFObjectProperty
*p
= &obj
->o_props
[i
];
372 if (p
->p_name
.av_val
)
375 if (p
->p_name
.av_val
)
376 len
+= p
->p_name
.av_len
+ 1;
383 len
+= p
->p_vu
.p_aval
.av_len
;
390 len
+= countAMF(&p
->p_vu
.p_object
, argc
);
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
)
416 *ptr
++ = opt
[p
->p_type
];
418 if (p
->p_name
.av_val
)
419 ptr
+= sprintf(ptr
, "%.*s:", p
->p_name
.av_len
, p
->p_name
.av_val
);
423 *ptr
++ = p
->p_vu
.p_number
!= 0 ? '1' : '0';
424 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
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
;
432 ptr
+= sprintf(ptr
, "%f", p
->p_vu
.p_number
);
433 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
437 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
440 ptr
= dumpAMF(&p
->p_vu
.p_object
, ptr
, argv
, argc
);
442 argv
[ac
].av_val
= ptr
+1;
443 argv
[ac
++].av_len
= 2;
444 argv
[ac
].av_val
= ptr
+4;
446 ptr
+= sprintf(ptr
, " -C O:0");
450 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
459 // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
461 ServeInvoke(STREAMING_SERVER
*server
, RTMP
* r
, RTMPPacket
*packet
, unsigned int offset
)
464 unsigned int nBodySize
;
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",
478 nRes
= AMF_Decode(&obj
, body
, nBodySize
, FALSE
);
481 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding invoke packet", __FUNCTION__
);
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
))
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
;
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
))
512 if (!r
->Link
.app
.av_val
)
513 r
->Link
.app
.av_val
= "";
514 server
->arglen
+= 6 + pval
.av_len
;
517 else if (AVMATCH(&pname
, &av_flashVer
))
519 r
->Link
.flashVer
= pval
;
521 server
->arglen
+= 6 + pval
.av_len
;
524 else if (AVMATCH(&pname
, &av_swfUrl
))
526 r
->Link
.swfUrl
= pval
;
528 server
->arglen
+= 6 + pval
.av_len
;
531 else if (AVMATCH(&pname
, &av_tcUrl
))
533 r
->Link
.tcUrl
= pval
;
535 server
->arglen
+= 6 + pval
.av_len
;
538 else if (AVMATCH(&pname
, &av_pageUrl
))
540 r
->Link
.pageUrl
= pval
;
542 server
->arglen
+= 6 + pval
.av_len
;
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 */
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
));
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
;
585 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 3), &r
->Link
.playpath
);
587 r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4));
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;
597 cmd
= malloc(len
+ server
->argc
* sizeof(AVal
));
599 argv
= (AVal
*)(cmd
+ len
);
600 argv
[0].av_val
= cmd
;
601 argv
[0].av_len
= sizeof("rtmpdump")-1;
602 ptr
+= sprintf(ptr
, "rtmpdump");
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
);
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
--)
674 av
.av_len
-= p
- av
.av_val
;
678 /* skip leading dot */
679 if (av
.av_val
[0] == '.')
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
++)
692 /* Add extension if none present */
693 if (file
[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");
715 printf("\n%s\n\n", cmd
);
717 server
->filetime
= now
;
718 free(server
->filename
.av_val
);
719 server
->filename
= argv
[argc
++];
720 spawn_dumper(argc
, argv
, cmd
);
725 pc
.m_body
= server
->connect
;
726 server
->connect
= NULL
;
727 RTMPPacket_Free(&pc
);
729 RTMP_SendCtrl(r
, 0, 1, 0);
731 RTMP_SendCtrl(r
, 1, 1, 0);
739 ServePacket(STREAMING_SERVER
*server
, RTMP
*r
, RTMPPacket
*packet
)
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
)
750 // HandleChangeChunkSize(r, packet);
759 // HandleCtrl(r, packet);
764 // HandleServerBW(r, packet);
769 // HandleClientBW(r, packet);
774 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
779 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
782 case 0x0F: // flex stream send
785 case 0x10: // flex shared object
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);
795 /*RTMP_LIB_AMFObject obj;
796 int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);
798 RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);
804 if (ServeInvoke(server
, r
, packet
, 1))
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))
830 RTMP_Log(RTMP_LOGDEBUG
, "%s, unknown packet type received: 0x%02x", __FUNCTION__
,
831 packet
->m_packetType
);
833 RTMP_LogHex(RTMP_LOGDEBUG
, packet
->m_body
, packet
->m_nBodySize
);
840 controlServerThread(void *unused
)
849 RTMP_LogPrintf("Exiting\n");
850 stopStreaming(rtmpServer
);
854 RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich
);
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
874 memset(&tv
, 0, sizeof(struct timeval
));
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");
888 rtmp
.m_sb
.sb_socket
= sockfd
;
889 if (!RTMP_Serve(&rtmp
))
891 RTMP_Log(RTMP_LOGERROR
, "Handshake failed");
896 while (RTMP_IsConnected(&rtmp
) && RTMP_ReadPacket(&rtmp
, &packet
))
898 if (!RTMPPacket_IsReady(&packet
))
900 ServePacket(server
, &rtmp
, &packet
);
901 RTMPPacket_Free(&packet
);
905 RTMP_LogPrintf("Closing connection... ");
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");
917 if (server
->state
== STREAMING_IN_PROGRESS
)
918 server
->state
= STREAMING_ACCEPTING
;
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
);
934 accept(server
->socket
, (struct sockaddr
*) &addr
, &addrlen
);
939 struct sockaddr_in dest
;
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
);
947 RTMP_Log(RTMP_LOGDEBUG
, "%s: accepted connection from %s\n", __FUNCTION__
,
948 inet_ntoa(addr
.sin_addr
));
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__
);
956 RTMP_Log(RTMP_LOGERROR
, "%s: accept failed", __FUNCTION__
);
959 server
->state
= STREAMING_STOPPED
;
964 startStreaming(const char *address
, int port
)
966 struct sockaddr_in addr
;
968 STREAMING_SERVER
*server
;
970 sockfd
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
973 RTMP_Log(RTMP_LOGERROR
, "%s, couldn't create socket", __FUNCTION__
);
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__
,
993 if (listen(sockfd
, 10) == -1)
995 RTMP_Log(RTMP_LOGERROR
, "%s, listen failed", __FUNCTION__
);
1000 server
= (STREAMING_SERVER
*) calloc(1, sizeof(STREAMING_SERVER
));
1001 server
->socket
= sockfd
;
1003 ThreadCreate(serverThread
, server
);
1009 stopStreaming(STREAMING_SERVER
* 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
)
1024 if (closesocket(server
->socket
))
1025 RTMP_Log(RTMP_LOGERROR
, "%s: Failed to close listening socket, error %d",
1028 server
->state
= STREAMING_STOPPED
;
1034 sigIntHandler(int sig
)
1037 RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig
);
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
;
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
);
1075 signal(SIGPIPE
, SIG_IGN
);
1079 netstackdump
= fopen("netstackdump", "wb");
1080 netstackdump_read
= fopen("netstackdump_read", "wb");
1086 ThreadCreate(controlServerThread
, 0);
1088 // start http streaming
1090 startStreaming(rtmpStreamingDevice
, nRtmpStreamingPort
)) == 0)
1092 RTMP_Log(RTMP_LOGERROR
, "Failed to start RTMP server, exiting!");
1095 RTMP_LogPrintf("Streaming on rtmp://%s:%d\n", rtmpStreamingDevice
,
1096 nRtmpStreamingPort
);
1098 while (rtmpServer
->state
!= STREAMING_STOPPED
)
1102 RTMP_Log(RTMP_LOGDEBUG
, "Done, exiting...");
1107 if (netstackdump
!= 0)
1108 fclose(netstackdump
);
1109 if (netstackdump_read
!= 0)
1110 fclose(netstackdump_read
);