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
)
408 const char opt
[] = "NBSO Z";
410 for (i
=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
);
598 if (!r
->Link
.playpath
.av_len
)
601 r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4));
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;
611 cmd
= malloc(len
+ server
->argc
* sizeof(AVal
));
613 argv
= (AVal
*)(cmd
+ len
);
614 argv
[0].av_val
= cmd
;
615 argv
[0].av_len
= sizeof("rtmpdump")-1;
616 ptr
+= sprintf(ptr
, "rtmpdump");
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
);
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
--)
699 av
.av_len
-= p
- av
.av_val
;
703 /* skip leading dot */
704 if (av
.av_val
[0] == '.')
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
++)
717 /* Add extension if none present */
718 if (file
[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");
740 printf("\n%s\n\n", cmd
);
742 server
->filetime
= now
;
743 free(server
->filename
.av_val
);
744 server
->filename
= argv
[argc
++];
745 spawn_dumper(argc
, argv
, cmd
);
750 pc
.m_body
= server
->connect
;
751 server
->connect
= NULL
;
752 RTMPPacket_Free(&pc
);
754 RTMP_SendCtrl(r
, 0, 1, 0);
756 RTMP_SendCtrl(r
, 1, 1, 0);
764 ServePacket(STREAMING_SERVER
*server
, RTMP
*r
, RTMPPacket
*packet
)
768 RTMP_Log(RTMP_LOGDEBUG
, "%s, received packet type %02X, size %u bytes", __FUNCTION__
,
769 packet
->m_packetType
, packet
->m_nBodySize
);
771 switch (packet
->m_packetType
)
773 case RTMP_PACKET_TYPE_CHUNK_SIZE
:
774 // HandleChangeChunkSize(r, packet);
777 case RTMP_PACKET_TYPE_BYTES_READ_REPORT
:
780 case RTMP_PACKET_TYPE_CONTROL
:
781 // HandleCtrl(r, packet);
784 case RTMP_PACKET_TYPE_SERVER_BW
:
785 // HandleServerBW(r, packet);
788 case RTMP_PACKET_TYPE_CLIENT_BW
:
789 // HandleClientBW(r, packet);
792 case RTMP_PACKET_TYPE_AUDIO
:
793 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
796 case RTMP_PACKET_TYPE_VIDEO
:
797 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
800 case RTMP_PACKET_TYPE_FLEX_STREAM_SEND
:
803 case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT
:
806 case RTMP_PACKET_TYPE_FLEX_MESSAGE
:
808 RTMP_Log(RTMP_LOGDEBUG
, "%s, flex message, size %u bytes, not fully supported",
809 __FUNCTION__
, packet
->m_nBodySize
);
810 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
813 /*RTMP_LIB_AMFObject obj;
814 int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);
816 RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);
822 if (ServeInvoke(server
, r
, packet
, 1))
826 case RTMP_PACKET_TYPE_INFO
:
829 case RTMP_PACKET_TYPE_SHARED_OBJECT
:
832 case RTMP_PACKET_TYPE_INVOKE
:
833 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: invoke %u bytes", __FUNCTION__
,
834 packet
->m_nBodySize
);
835 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
837 if (ServeInvoke(server
, r
, packet
, 0))
841 case RTMP_PACKET_TYPE_FLASH_VIDEO
:
844 RTMP_Log(RTMP_LOGDEBUG
, "%s, unknown packet type received: 0x%02x", __FUNCTION__
,
845 packet
->m_packetType
);
847 RTMP_LogHex(RTMP_LOGDEBUG
, packet
->m_body
, packet
->m_nBodySize
);
854 controlServerThread(void *unused
)
863 RTMP_LogPrintf("Exiting\n");
864 stopStreaming(rtmpServer
);
868 RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich
);
875 void doServe(STREAMING_SERVER
* server
, // server socket and state (our listening socket)
876 int sockfd
// client connection socket
879 server
->state
= STREAMING_IN_PROGRESS
;
881 RTMP
*rtmp
= RTMP_Alloc(); /* our session with the real client */
882 RTMPPacket packet
= { 0 };
884 // timeout for http requests
888 memset(&tv
, 0, sizeof(struct timeval
));
892 FD_SET(sockfd
, &fds
);
894 if (select(sockfd
+ 1, &fds
, NULL
, NULL
, &tv
) <= 0)
896 RTMP_Log(RTMP_LOGERROR
, "Request timeout/select failed, ignoring request");
902 rtmp
->m_sb
.sb_socket
= sockfd
;
903 if (sslCtx
&& !RTMP_TLS_Accept(rtmp
, sslCtx
))
905 RTMP_Log(RTMP_LOGERROR
, "TLS handshake failed");
908 if (!RTMP_Serve(rtmp
))
910 RTMP_Log(RTMP_LOGERROR
, "Handshake failed");
915 while (RTMP_IsConnected(rtmp
) && RTMP_ReadPacket(rtmp
, &packet
))
917 if (!RTMPPacket_IsReady(&packet
))
919 ServePacket(server
, rtmp
, &packet
);
920 RTMPPacket_Free(&packet
);
924 RTMP_LogPrintf("Closing connection... ");
926 /* Should probably be done by RTMP_Close() ... */
927 rtmp
->Link
.playpath
.av_val
= NULL
;
928 rtmp
->Link
.tcUrl
.av_val
= NULL
;
929 rtmp
->Link
.swfUrl
.av_val
= NULL
;
930 rtmp
->Link
.pageUrl
.av_val
= NULL
;
931 rtmp
->Link
.app
.av_val
= NULL
;
932 rtmp
->Link
.flashVer
.av_val
= NULL
;
933 if (rtmp
->Link
.usherToken
.av_val
)
935 free(rtmp
->Link
.usherToken
.av_val
);
936 rtmp
->Link
.usherToken
.av_val
= NULL
;
939 RTMP_LogPrintf("done!\n\n");
942 if (server
->state
== STREAMING_IN_PROGRESS
)
943 server
->state
= STREAMING_ACCEPTING
;
949 serverThread(void *arg
)
951 STREAMING_SERVER
*server
= arg
;
952 server
->state
= STREAMING_ACCEPTING
;
954 while (server
->state
== STREAMING_ACCEPTING
)
956 struct sockaddr_in addr
;
957 socklen_t addrlen
= sizeof(struct sockaddr_in
);
959 accept(server
->socket
, (struct sockaddr
*) &addr
, &addrlen
);
964 struct sockaddr_in dest
;
966 socklen_t destlen
= sizeof(struct sockaddr_in
);
967 getsockopt(sockfd
, SOL_IP
, SO_ORIGINAL_DST
, &dest
, &destlen
);
968 strcpy(destch
, inet_ntoa(dest
.sin_addr
));
969 RTMP_Log(RTMP_LOGDEBUG
, "%s: accepted connection from %s to %s\n", __FUNCTION__
,
970 inet_ntoa(addr
.sin_addr
), destch
);
972 RTMP_Log(RTMP_LOGDEBUG
, "%s: accepted connection from %s\n", __FUNCTION__
,
973 inet_ntoa(addr
.sin_addr
));
975 /* Create a new thread and transfer the control to that */
976 doServe(server
, sockfd
);
977 RTMP_Log(RTMP_LOGDEBUG
, "%s: processed request\n", __FUNCTION__
);
981 RTMP_Log(RTMP_LOGERROR
, "%s: accept failed", __FUNCTION__
);
984 server
->state
= STREAMING_STOPPED
;
989 startStreaming(const char *address
, int port
)
991 struct sockaddr_in addr
;
993 STREAMING_SERVER
*server
;
995 sockfd
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
998 RTMP_Log(RTMP_LOGERROR
, "%s, couldn't create socket", __FUNCTION__
);
1003 setsockopt(sockfd
, SOL_SOCKET
, SO_REUSEADDR
,
1004 (char *) &tmp
, sizeof(tmp
) );
1006 addr
.sin_family
= AF_INET
;
1007 addr
.sin_addr
.s_addr
= inet_addr(address
); //htonl(INADDR_ANY);
1008 addr
.sin_port
= htons(port
);
1010 if (bind(sockfd
, (struct sockaddr
*) &addr
, sizeof(struct sockaddr_in
)) ==
1013 RTMP_Log(RTMP_LOGERROR
, "%s, TCP bind failed for port number: %d", __FUNCTION__
,
1018 if (listen(sockfd
, 10) == -1)
1020 RTMP_Log(RTMP_LOGERROR
, "%s, listen failed", __FUNCTION__
);
1021 closesocket(sockfd
);
1025 server
= (STREAMING_SERVER
*) calloc(1, sizeof(STREAMING_SERVER
));
1026 server
->socket
= sockfd
;
1028 ThreadCreate(serverThread
, server
);
1034 stopStreaming(STREAMING_SERVER
* server
)
1038 if (server
->state
!= STREAMING_STOPPED
)
1040 if (server
->state
== STREAMING_IN_PROGRESS
)
1042 server
->state
= STREAMING_STOPPING
;
1044 // wait for streaming threads to exit
1045 while (server
->state
!= STREAMING_STOPPED
)
1049 if (closesocket(server
->socket
))
1050 RTMP_Log(RTMP_LOGERROR
, "%s: Failed to close listening socket, error %d",
1051 __FUNCTION__
, GetSockError());
1053 server
->state
= STREAMING_STOPPED
;
1059 sigIntHandler(int sig
)
1062 RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig
);
1064 stopStreaming(rtmpServer
);
1065 signal(SIGINT
, SIG_DFL
);
1069 main(int argc
, char **argv
)
1071 int nStatus
= RD_SUCCESS
;
1074 // http streaming server
1075 char DEFAULT_HTTP_STREAMING_DEVICE
[] = "0.0.0.0"; // 0.0.0.0 is any device
1077 char *rtmpStreamingDevice
= DEFAULT_HTTP_STREAMING_DEVICE
; // streaming device, default 0.0.0.0
1078 int nRtmpStreamingPort
= 1935; // port
1079 char *cert
= NULL
, *key
= NULL
;
1081 RTMP_LogPrintf("RTMP Server %s\n", RTMPDUMP_VERSION
);
1082 RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n");
1084 RTMP_debuglevel
= RTMP_LOGINFO
;
1086 for (i
= 1; i
< argc
; i
++)
1088 if (!strcmp(argv
[i
], "-z"))
1089 RTMP_debuglevel
= RTMP_LOGALL
;
1090 else if (!strcmp(argv
[i
], "-c") && i
+ 1 < argc
)
1092 else if (!strcmp(argv
[i
], "-k") && i
+ 1 < argc
)
1097 sslCtx
= RTMP_TLS_AllocServerContext(cert
, key
);
1100 memset(&defaultRTMPRequest
, 0, sizeof(RTMP_REQUEST
));
1102 defaultRTMPRequest
.rtmpport
= -1;
1103 defaultRTMPRequest
.protocol
= RTMP_PROTOCOL_UNDEFINED
;
1104 defaultRTMPRequest
.bLiveStream
= FALSE
; // is it a live stream? then we can't seek/resume
1106 defaultRTMPRequest
.timeout
= 300; // timeout connection afte 300 seconds
1107 defaultRTMPRequest
.bufferTime
= 20 * 1000;
1110 signal(SIGINT
, sigIntHandler
);
1112 signal(SIGPIPE
, SIG_IGN
);
1116 netstackdump
= fopen("netstackdump", "wb");
1117 netstackdump_read
= fopen("netstackdump_read", "wb");
1123 ThreadCreate(controlServerThread
, 0);
1125 // start http streaming
1127 startStreaming(rtmpStreamingDevice
, nRtmpStreamingPort
)) == 0)
1129 RTMP_Log(RTMP_LOGERROR
, "Failed to start RTMP server, exiting!");
1132 RTMP_LogPrintf("Streaming on rtmp://%s:%d\n", rtmpStreamingDevice
,
1133 nRtmpStreamingPort
);
1135 while (rtmpServer
->state
!= STREAMING_STOPPED
)
1139 RTMP_Log(RTMP_LOGDEBUG
, "Done, exiting...");
1142 RTMP_TLS_FreeServerContext(sslCtx
);
1147 if (netstackdump
!= 0)
1148 fclose(netstackdump
);
1149 if (netstackdump_read
!= 0)
1150 fclose(netstackdump_read
);
1156 AVreplace(AVal
*src
, const AVal
*orig
, const AVal
*repl
)
1158 char *srcbeg
= src
->av_val
;
1159 char *srcend
= src
->av_val
+ src
->av_len
;
1160 char *dest
, *sptr
, *dptr
;
1163 /* count occurrences of orig in src */
1165 while (sptr
< srcend
&& (sptr
= strstr(sptr
, orig
->av_val
)))
1168 sptr
+= orig
->av_len
;
1173 dest
= malloc(src
->av_len
+ 1 + (repl
->av_len
- orig
->av_len
) * n
);
1177 while (sptr
< srcend
&& (sptr
= strstr(sptr
, orig
->av_val
)))
1180 memcpy(dptr
, srcbeg
, n
);
1182 memcpy(dptr
, repl
->av_val
, repl
->av_len
);
1183 dptr
+= repl
->av_len
;
1184 sptr
+= orig
->av_len
;
1187 n
= srcend
- srcbeg
;
1188 memcpy(dptr
, srcbeg
, n
);
1192 src
->av_len
= dptr
- dest
;