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)
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
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("\\\"");
109 int bLiveStream
; // is it a live stream? then we can't seek/resume
111 long int timeout
; // timeout connection afte 300 seconds
126 uint32_t dStartOffset
;
127 uint32_t dStopOffset
;
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
;
139 uint32_t debugTS
= 0;
143 FILE *netstackdump
= NULL
;
144 FILE *netstackdump_read
= NULL
;
147 #define SAVC(x) static const AVal av_##x = AVC(#x)
160 SAVC(objectEncoding
);
163 SAVC(getStreamLength
);
173 SendConnectResult(RTMP
*r
, double txn
)
176 char pbuf
[384], *pend
= pbuf
+sizeof(pbuf
);
178 AMFObjectProperty p
, op
;
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
);
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);
200 *enc
++ = AMF_OBJECT_END
;
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
);
212 STR2AVAL(av
, "58656322c972d6cdf2d776167575045f8484ea888e31c086f7b5ffbd0baec55ce442c2fb");
213 enc
= AMF_EncodeNamedString(enc
, pend
, &av_secureToken
, &av
);
215 STR2AVAL(p
.p_name
, "version");
216 STR2AVAL(p
.p_vu
.p_aval
, "3,5,1,525");
217 p
.p_type
= AMF_STRING
;
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
);
226 *enc
++ = AMF_OBJECT_END
;
228 packet
.m_nBodySize
= enc
- packet
.m_body
;
230 return RTMP_SendPacket(r
, &packet
, FALSE
);
234 SendResultNumber(RTMP
*r
, double txn
, double ID
)
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
);
251 enc
= AMF_EncodeNumber(enc
, pend
, ID
);
253 packet
.m_nBodySize
= enc
- packet
.m_body
;
255 return RTMP_SendPacket(r
, &packet
, FALSE
);
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");
266 static const AVal av_NetStream_Authenticate_UsherToken
= AVC("NetStream.Authenticate.UsherToken");
269 SendPlayStart(RTMP
*r
)
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);
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
);
294 *enc
++ = AMF_OBJECT_END
;
296 packet
.m_nBodySize
= enc
- packet
.m_body
;
297 return RTMP_SendPacket(r
, &packet
, FALSE
);
301 SendPlayStop(RTMP
*r
)
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);
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
);
326 *enc
++ = AMF_OBJECT_END
;
328 packet
.m_nBodySize
= enc
- packet
.m_body
;
329 return RTMP_SendPacket(r
, &packet
, FALSE
);
333 spawn_dumper(int argc
, AVal
*av
, char *cmd
)
336 STARTUPINFO si
= {0};
337 PROCESS_INFORMATION pi
= {0};
340 if (CreateProcess(NULL
, cmd
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
,
343 CloseHandle(pi
.hThread
);
344 CloseHandle(pi
.hProcess
);
347 /* reap any dead children */
348 while (waitpid(-1, NULL
, WNOHANG
) > 0);
351 char **argv
= malloc((argc
+1) * sizeof(char *));
354 for (i
=0; i
<argc
; i
++) {
355 argv
[i
] = av
[i
].av_val
;
356 argv
[i
][av
[i
].av_len
] = '\0';
359 if ((i
= execvp(argv
[0], argv
)))
366 countAMF(AMFObject
*obj
, int *argc
)
370 for (i
=0, len
=0; i
< obj
->o_num
; i
++)
372 AMFObjectProperty
*p
= &obj
->o_props
[i
];
375 if (p
->p_name
.av_val
)
378 if (p
->p_name
.av_val
)
379 len
+= p
->p_name
.av_len
+ 1;
386 len
+= p
->p_vu
.p_aval
.av_len
;
393 len
+= countAMF(&p
->p_vu
.p_object
, argc
);
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
)
419 *ptr
++ = opt
[p
->p_type
];
421 if (p
->p_name
.av_val
)
422 ptr
+= sprintf(ptr
, "%.*s:", p
->p_name
.av_len
, p
->p_name
.av_val
);
426 *ptr
++ = p
->p_vu
.p_number
!= 0 ? '1' : '0';
427 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
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
;
435 ptr
+= sprintf(ptr
, "%f", p
->p_vu
.p_number
);
436 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
440 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
443 ptr
= dumpAMF(&p
->p_vu
.p_object
, ptr
, argv
, argc
);
445 argv
[ac
].av_val
= ptr
+1;
446 argv
[ac
++].av_len
= 2;
447 argv
[ac
].av_val
= ptr
+4;
449 ptr
+= sprintf(ptr
, " -C O:0");
453 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
462 // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
464 ServeInvoke(STREAMING_SERVER
*server
, RTMP
* r
, RTMPPacket
*packet
, unsigned int offset
)
467 unsigned int nBodySize
;
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",
481 nRes
= AMF_Decode(&obj
, body
, nBodySize
, FALSE
);
484 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding invoke packet", __FUNCTION__
);
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
))
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
;
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
))
515 if (!r
->Link
.app
.av_val
)
516 r
->Link
.app
.av_val
= "";
517 server
->arglen
+= 6 + pval
.av_len
;
520 else if (AVMATCH(&pname
, &av_flashVer
))
522 r
->Link
.flashVer
= pval
;
524 server
->arglen
+= 6 + pval
.av_len
;
527 else if (AVMATCH(&pname
, &av_swfUrl
))
529 r
->Link
.swfUrl
= pval
;
531 server
->arglen
+= 6 + pval
.av_len
;
534 else if (AVMATCH(&pname
, &av_tcUrl
))
536 r
->Link
.tcUrl
= pval
;
538 server
->arglen
+= 6 + pval
.av_len
;
541 else if (AVMATCH(&pname
, &av_pageUrl
))
543 r
->Link
.pageUrl
= pval
;
545 server
->arglen
+= 6 + pval
.av_len
;
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 */
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
));
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
))
584 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 3), &usherToken
);
585 AVreplace(&usherToken
, &av_dquote
, &av_escdquote
);
586 server
->arglen
+= 6 + usherToken
.av_len
;
588 r
->Link
.usherToken
= usherToken
;
590 else if (AVMATCH(&method
, &av_play
))
592 char *file
, *p
, *q
, *cmd
, *ptr
;
597 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 3), &r
->Link
.playpath
);
599 r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4));
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;
609 cmd
= malloc(len
+ server
->argc
* sizeof(AVal
));
611 argv
= (AVal
*)(cmd
+ len
);
612 argv
[0].av_val
= cmd
;
613 argv
[0].av_len
= sizeof("rtmpdump")-1;
614 ptr
+= sprintf(ptr
, "rtmpdump");
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
);
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
--)
697 av
.av_len
-= p
- av
.av_val
;
701 /* skip leading dot */
702 if (av
.av_val
[0] == '.')
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
++)
715 /* Add extension if none present */
716 if (file
[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");
738 printf("\n%s\n\n", cmd
);
740 server
->filetime
= now
;
741 free(server
->filename
.av_val
);
742 server
->filename
= argv
[argc
++];
743 spawn_dumper(argc
, argv
, cmd
);
748 pc
.m_body
= server
->connect
;
749 server
->connect
= NULL
;
750 RTMPPacket_Free(&pc
);
752 RTMP_SendCtrl(r
, 0, 1, 0);
754 RTMP_SendCtrl(r
, 1, 1, 0);
762 ServePacket(STREAMING_SERVER
*server
, RTMP
*r
, RTMPPacket
*packet
)
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);
775 case RTMP_PACKET_TYPE_BYTES_READ_REPORT
:
778 case RTMP_PACKET_TYPE_CONTROL
:
779 // HandleCtrl(r, packet);
782 case RTMP_PACKET_TYPE_SERVER_BW
:
783 // HandleServerBW(r, packet);
786 case RTMP_PACKET_TYPE_CLIENT_BW
:
787 // HandleClientBW(r, packet);
790 case RTMP_PACKET_TYPE_AUDIO
:
791 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
794 case RTMP_PACKET_TYPE_VIDEO
:
795 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
798 case RTMP_PACKET_TYPE_FLEX_STREAM_SEND
:
801 case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT
:
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);
811 /*RTMP_LIB_AMFObject obj;
812 int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);
814 RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);
820 if (ServeInvoke(server
, r
, packet
, 1))
824 case RTMP_PACKET_TYPE_INFO
:
827 case RTMP_PACKET_TYPE_SHARED_OBJECT
:
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))
839 case RTMP_PACKET_TYPE_FLASH_VIDEO
:
842 RTMP_Log(RTMP_LOGDEBUG
, "%s, unknown packet type received: 0x%02x", __FUNCTION__
,
843 packet
->m_packetType
);
845 RTMP_LogHex(RTMP_LOGDEBUG
, packet
->m_body
, packet
->m_nBodySize
);
852 controlServerThread(void *unused
)
861 RTMP_LogPrintf("Exiting\n");
862 stopStreaming(rtmpServer
);
866 RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich
);
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
886 memset(&tv
, 0, sizeof(struct timeval
));
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");
900 rtmp
->m_sb
.sb_socket
= sockfd
;
901 if (sslCtx
&& !RTMP_TLS_Accept(rtmp
, sslCtx
))
903 RTMP_Log(RTMP_LOGERROR
, "TLS handshake failed");
906 if (!RTMP_Serve(rtmp
))
908 RTMP_Log(RTMP_LOGERROR
, "Handshake failed");
913 while (RTMP_IsConnected(rtmp
) && RTMP_ReadPacket(rtmp
, &packet
))
915 if (!RTMPPacket_IsReady(&packet
))
917 ServePacket(server
, rtmp
, &packet
);
918 RTMPPacket_Free(&packet
);
922 RTMP_LogPrintf("Closing connection... ");
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
;
937 RTMP_LogPrintf("done!\n\n");
940 if (server
->state
== STREAMING_IN_PROGRESS
)
941 server
->state
= STREAMING_ACCEPTING
;
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
);
957 accept(server
->socket
, (struct sockaddr
*) &addr
, &addrlen
);
962 struct sockaddr_in dest
;
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
);
970 RTMP_Log(RTMP_LOGDEBUG
, "%s: accepted connection from %s\n", __FUNCTION__
,
971 inet_ntoa(addr
.sin_addr
));
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__
);
979 RTMP_Log(RTMP_LOGERROR
, "%s: accept failed", __FUNCTION__
);
982 server
->state
= STREAMING_STOPPED
;
987 startStreaming(const char *address
, int port
)
989 struct sockaddr_in addr
;
991 STREAMING_SERVER
*server
;
993 sockfd
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
996 RTMP_Log(RTMP_LOGERROR
, "%s, couldn't create socket", __FUNCTION__
);
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__
,
1016 if (listen(sockfd
, 10) == -1)
1018 RTMP_Log(RTMP_LOGERROR
, "%s, listen failed", __FUNCTION__
);
1019 closesocket(sockfd
);
1023 server
= (STREAMING_SERVER
*) calloc(1, sizeof(STREAMING_SERVER
));
1024 server
->socket
= sockfd
;
1026 ThreadCreate(serverThread
, server
);
1032 stopStreaming(STREAMING_SERVER
* 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
)
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
;
1057 sigIntHandler(int sig
)
1060 RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig
);
1062 stopStreaming(rtmpServer
);
1063 signal(SIGINT
, SIG_DFL
);
1067 main(int argc
, char **argv
)
1069 int nStatus
= RD_SUCCESS
;
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
)
1090 else if (!strcmp(argv
[i
], "-k") && i
+ 1 < argc
)
1095 sslCtx
= RTMP_TLS_AllocServerContext(cert
, key
);
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
);
1110 signal(SIGPIPE
, SIG_IGN
);
1114 netstackdump
= fopen("netstackdump", "wb");
1115 netstackdump_read
= fopen("netstackdump_read", "wb");
1121 ThreadCreate(controlServerThread
, 0);
1123 // start http streaming
1125 startStreaming(rtmpStreamingDevice
, nRtmpStreamingPort
)) == 0)
1127 RTMP_Log(RTMP_LOGERROR
, "Failed to start RTMP server, exiting!");
1130 RTMP_LogPrintf("Streaming on rtmp://%s:%d\n", rtmpStreamingDevice
,
1131 nRtmpStreamingPort
);
1133 while (rtmpServer
->state
!= STREAMING_STOPPED
)
1137 RTMP_Log(RTMP_LOGDEBUG
, "Done, exiting...");
1140 RTMP_TLS_FreeServerContext(sslCtx
);
1145 if (netstackdump
!= 0)
1146 fclose(netstackdump
);
1147 if (netstackdump_read
!= 0)
1148 fclose(netstackdump_read
);
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
;
1161 /* count occurrences of orig in src */
1163 while (sptr
< srcend
&& (sptr
= strstr(sptr
, orig
->av_val
)))
1166 sptr
+= orig
->av_len
;
1171 dest
= malloc(src
->av_len
+ 1 + (repl
->av_len
- orig
->av_len
) * n
);
1175 while (sptr
< srcend
&& (sptr
= strstr(sptr
, orig
->av_val
)))
1178 memcpy(dptr
, srcbeg
, n
);
1180 memcpy(dptr
, repl
->av_val
, repl
->av_len
);
1181 dptr
+= repl
->av_len
;
1182 sptr
+= orig
->av_len
;
1185 n
= srcend
- srcbeg
;
1186 memcpy(dptr
, srcbeg
, n
);
1190 src
->av_len
= dptr
- dest
;