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
96 STREAMING_SERVER
*startStreaming(const char *address
, int port
);
97 void stopStreaming(STREAMING_SERVER
* server
);
98 void AVreplace(AVal
*src
, const AVal
*orig
, const AVal
*repl
);
100 static const AVal av_dquote
= AVC("\"");
101 static const AVal av_escdquote
= AVC("\\\"");
108 int bLiveStream
; // is it a live stream? then we can't seek/resume
110 long int timeout
; // timeout connection afte 300 seconds
125 uint32_t dStartOffset
;
126 uint32_t dStopOffset
;
130 #define STR2AVAL(av,str) av.av_val = str; av.av_len = strlen(av.av_val)
132 /* this request is formed from the parameters and used to initialize a new request,
133 * thus it is a default settings list. All settings can be overriden by specifying the
134 * parameters in the GET request. */
135 RTMP_REQUEST defaultRTMPRequest
;
138 uint32_t debugTS
= 0;
142 FILE *netstackdump
= NULL
;
143 FILE *netstackdump_read
= NULL
;
146 #define SAVC(x) static const AVal av_##x = AVC(#x)
159 SAVC(objectEncoding
);
162 SAVC(getStreamLength
);
172 SendConnectResult(RTMP
*r
, double txn
)
175 char pbuf
[384], *pend
= pbuf
+sizeof(pbuf
);
177 AMFObjectProperty p
, op
;
180 packet
.m_nChannel
= 0x03; // control channel (invoke)
181 packet
.m_headerType
= 1; /* RTMP_PACKET_SIZE_MEDIUM; */
182 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
183 packet
.m_nTimeStamp
= 0;
184 packet
.m_nInfoField2
= 0;
185 packet
.m_hasAbsTimestamp
= 0;
186 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
188 char *enc
= packet
.m_body
;
189 enc
= AMF_EncodeString(enc
, pend
, &av__result
);
190 enc
= AMF_EncodeNumber(enc
, pend
, txn
);
193 STR2AVAL(av
, "FMS/3,5,1,525");
194 enc
= AMF_EncodeNamedString(enc
, pend
, &av_fmsVer
, &av
);
195 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_capabilities
, 31.0);
196 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_mode
, 1.0);
199 *enc
++ = AMF_OBJECT_END
;
203 STR2AVAL(av
, "status");
204 enc
= AMF_EncodeNamedString(enc
, pend
, &av_level
, &av
);
205 STR2AVAL(av
, "NetConnection.Connect.Success");
206 enc
= AMF_EncodeNamedString(enc
, pend
, &av_code
, &av
);
207 STR2AVAL(av
, "Connection succeeded.");
208 enc
= AMF_EncodeNamedString(enc
, pend
, &av_description
, &av
);
209 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_objectEncoding
, r
->m_fEncoding
);
211 STR2AVAL(av
, "58656322c972d6cdf2d776167575045f8484ea888e31c086f7b5ffbd0baec55ce442c2fb");
212 enc
= AMF_EncodeNamedString(enc
, pend
, &av_secureToken
, &av
);
214 STR2AVAL(p
.p_name
, "version");
215 STR2AVAL(p
.p_vu
.p_aval
, "3,5,1,525");
216 p
.p_type
= AMF_STRING
;
219 op
.p_type
= AMF_OBJECT
;
220 STR2AVAL(op
.p_name
, "data");
221 op
.p_vu
.p_object
= obj
;
222 enc
= AMFProp_Encode(&op
, enc
, pend
);
225 *enc
++ = AMF_OBJECT_END
;
227 packet
.m_nBodySize
= enc
- packet
.m_body
;
229 return RTMP_SendPacket(r
, &packet
, FALSE
);
233 SendResultNumber(RTMP
*r
, double txn
, double ID
)
236 char pbuf
[256], *pend
= pbuf
+sizeof(pbuf
);
238 packet
.m_nChannel
= 0x03; // control channel (invoke)
239 packet
.m_headerType
= 1; /* RTMP_PACKET_SIZE_MEDIUM; */
240 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
241 packet
.m_nTimeStamp
= 0;
242 packet
.m_nInfoField2
= 0;
243 packet
.m_hasAbsTimestamp
= 0;
244 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
246 char *enc
= packet
.m_body
;
247 enc
= AMF_EncodeString(enc
, pend
, &av__result
);
248 enc
= AMF_EncodeNumber(enc
, pend
, txn
);
250 enc
= AMF_EncodeNumber(enc
, pend
, ID
);
252 packet
.m_nBodySize
= enc
- packet
.m_body
;
254 return RTMP_SendPacket(r
, &packet
, FALSE
);
259 static const AVal av_NetStream_Play_Start
= AVC("NetStream.Play.Start");
260 static const AVal av_Started_playing
= AVC("Started playing");
261 static const AVal av_NetStream_Play_Stop
= AVC("NetStream.Play.Stop");
262 static const AVal av_Stopped_playing
= AVC("Stopped playing");
265 static const AVal av_NetStream_Authenticate_UsherToken
= AVC("NetStream.Authenticate.UsherToken");
268 SendPlayStart(RTMP
*r
)
271 char pbuf
[512], *pend
= pbuf
+sizeof(pbuf
);
273 packet
.m_nChannel
= 0x03; // control channel (invoke)
274 packet
.m_headerType
= 1; /* RTMP_PACKET_SIZE_MEDIUM; */
275 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
276 packet
.m_nTimeStamp
= 0;
277 packet
.m_nInfoField2
= 0;
278 packet
.m_hasAbsTimestamp
= 0;
279 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
281 char *enc
= packet
.m_body
;
282 enc
= AMF_EncodeString(enc
, pend
, &av_onStatus
);
283 enc
= AMF_EncodeNumber(enc
, pend
, 0);
286 enc
= AMF_EncodeNamedString(enc
, pend
, &av_level
, &av_status
);
287 enc
= AMF_EncodeNamedString(enc
, pend
, &av_code
, &av_NetStream_Play_Start
);
288 enc
= AMF_EncodeNamedString(enc
, pend
, &av_description
, &av_Started_playing
);
289 enc
= AMF_EncodeNamedString(enc
, pend
, &av_details
, &r
->Link
.playpath
);
290 enc
= AMF_EncodeNamedString(enc
, pend
, &av_clientid
, &av_clientid
);
293 *enc
++ = AMF_OBJECT_END
;
295 packet
.m_nBodySize
= enc
- packet
.m_body
;
296 return RTMP_SendPacket(r
, &packet
, FALSE
);
300 SendPlayStop(RTMP
*r
)
303 char pbuf
[512], *pend
= pbuf
+sizeof(pbuf
);
305 packet
.m_nChannel
= 0x03; // control channel (invoke)
306 packet
.m_headerType
= 1; /* RTMP_PACKET_SIZE_MEDIUM; */
307 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
308 packet
.m_nTimeStamp
= 0;
309 packet
.m_nInfoField2
= 0;
310 packet
.m_hasAbsTimestamp
= 0;
311 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
313 char *enc
= packet
.m_body
;
314 enc
= AMF_EncodeString(enc
, pend
, &av_onStatus
);
315 enc
= AMF_EncodeNumber(enc
, pend
, 0);
318 enc
= AMF_EncodeNamedString(enc
, pend
, &av_level
, &av_status
);
319 enc
= AMF_EncodeNamedString(enc
, pend
, &av_code
, &av_NetStream_Play_Stop
);
320 enc
= AMF_EncodeNamedString(enc
, pend
, &av_description
, &av_Stopped_playing
);
321 enc
= AMF_EncodeNamedString(enc
, pend
, &av_details
, &r
->Link
.playpath
);
322 enc
= AMF_EncodeNamedString(enc
, pend
, &av_clientid
, &av_clientid
);
325 *enc
++ = AMF_OBJECT_END
;
327 packet
.m_nBodySize
= enc
- packet
.m_body
;
328 return RTMP_SendPacket(r
, &packet
, FALSE
);
332 spawn_dumper(int argc
, AVal
*av
, char *cmd
)
335 STARTUPINFO si
= {0};
336 PROCESS_INFORMATION pi
= {0};
339 if (CreateProcess(NULL
, cmd
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
,
342 CloseHandle(pi
.hThread
);
343 CloseHandle(pi
.hProcess
);
346 /* reap any dead children */
347 while (waitpid(-1, NULL
, WNOHANG
) > 0);
350 char **argv
= malloc((argc
+1) * sizeof(char *));
353 for (i
=0; i
<argc
; i
++) {
354 argv
[i
] = av
[i
].av_val
;
355 argv
[i
][av
[i
].av_len
] = '\0';
358 if ((i
= execvp(argv
[0], argv
)))
365 countAMF(AMFObject
*obj
, int *argc
)
369 for (i
=0, len
=0; i
< obj
->o_num
; i
++)
371 AMFObjectProperty
*p
= &obj
->o_props
[i
];
374 if (p
->p_name
.av_val
)
377 if (p
->p_name
.av_val
)
378 len
+= p
->p_name
.av_len
+ 1;
385 len
+= p
->p_vu
.p_aval
.av_len
;
392 len
+= countAMF(&p
->p_vu
.p_object
, argc
);
404 dumpAMF(AMFObject
*obj
, char *ptr
, AVal
*argv
, int *argc
)
406 int i
, len
, ac
= *argc
;
407 const char opt
[] = "NBSO Z";
409 for (i
=0, len
=0; i
< obj
->o_num
; i
++)
411 AMFObjectProperty
*p
= &obj
->o_props
[i
];
412 argv
[ac
].av_val
= ptr
+1;
413 argv
[ac
++].av_len
= 2;
414 ptr
+= sprintf(ptr
, " -C ");
415 argv
[ac
].av_val
= ptr
;
416 if (p
->p_name
.av_val
)
418 *ptr
++ = opt
[p
->p_type
];
420 if (p
->p_name
.av_val
)
421 ptr
+= sprintf(ptr
, "%.*s:", p
->p_name
.av_len
, p
->p_name
.av_val
);
425 *ptr
++ = p
->p_vu
.p_number
!= 0 ? '1' : '0';
426 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
429 memcpy(ptr
, p
->p_vu
.p_aval
.av_val
, p
->p_vu
.p_aval
.av_len
);
430 ptr
+= p
->p_vu
.p_aval
.av_len
;
431 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
434 ptr
+= sprintf(ptr
, "%f", p
->p_vu
.p_number
);
435 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
439 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
442 ptr
= dumpAMF(&p
->p_vu
.p_object
, ptr
, argv
, argc
);
444 argv
[ac
].av_val
= ptr
+1;
445 argv
[ac
++].av_len
= 2;
446 argv
[ac
].av_val
= ptr
+4;
448 ptr
+= sprintf(ptr
, " -C O:0");
452 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
461 // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
463 ServeInvoke(STREAMING_SERVER
*server
, RTMP
* r
, RTMPPacket
*packet
, unsigned int offset
)
466 unsigned int nBodySize
;
469 body
= packet
->m_body
+ offset
;
470 nBodySize
= packet
->m_nBodySize
- offset
;
472 if (body
[0] != 0x02) // make sure it is a string method name we start with
474 RTMP_Log(RTMP_LOGWARNING
, "%s, Sanity failed. no string method in invoke packet",
480 nRes
= AMF_Decode(&obj
, body
, nBodySize
, FALSE
);
483 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding invoke packet", __FUNCTION__
);
489 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 0), &method
);
490 double txn
= AMFProp_GetNumber(AMF_GetProp(&obj
, NULL
, 1));
491 RTMP_Log(RTMP_LOGDEBUG
, "%s, client invoking <%s>", __FUNCTION__
, method
.av_val
);
493 if (AVMATCH(&method
, &av_connect
))
499 server
->connect
= packet
->m_body
;
500 packet
->m_body
= NULL
;
502 AMFProp_GetObject(AMF_GetProp(&obj
, NULL
, 2), &cobj
);
503 for (i
=0; i
<cobj
.o_num
; i
++)
505 pname
= cobj
.o_props
[i
].p_name
;
508 if (cobj
.o_props
[i
].p_type
== AMF_STRING
)
509 pval
= cobj
.o_props
[i
].p_vu
.p_aval
;
510 if (AVMATCH(&pname
, &av_app
))
514 if (!r
->Link
.app
.av_val
)
515 r
->Link
.app
.av_val
= "";
516 server
->arglen
+= 6 + pval
.av_len
;
519 else if (AVMATCH(&pname
, &av_flashVer
))
521 r
->Link
.flashVer
= pval
;
523 server
->arglen
+= 6 + pval
.av_len
;
526 else if (AVMATCH(&pname
, &av_swfUrl
))
528 r
->Link
.swfUrl
= pval
;
530 server
->arglen
+= 6 + pval
.av_len
;
533 else if (AVMATCH(&pname
, &av_tcUrl
))
535 r
->Link
.tcUrl
= pval
;
537 server
->arglen
+= 6 + pval
.av_len
;
540 else if (AVMATCH(&pname
, &av_pageUrl
))
542 r
->Link
.pageUrl
= pval
;
544 server
->arglen
+= 6 + pval
.av_len
;
547 else if (AVMATCH(&pname
, &av_audioCodecs
))
549 r
->m_fAudioCodecs
= cobj
.o_props
[i
].p_vu
.p_number
;
551 else if (AVMATCH(&pname
, &av_videoCodecs
))
553 r
->m_fVideoCodecs
= cobj
.o_props
[i
].p_vu
.p_number
;
555 else if (AVMATCH(&pname
, &av_objectEncoding
))
557 r
->m_fEncoding
= cobj
.o_props
[i
].p_vu
.p_number
;
560 /* Still have more parameters? Copy them */
563 int i
= obj
.o_num
- 3;
564 r
->Link
.extras
.o_num
= i
;
565 r
->Link
.extras
.o_props
= malloc(i
*sizeof(AMFObjectProperty
));
566 memcpy(r
->Link
.extras
.o_props
, obj
.o_props
+3, i
*sizeof(AMFObjectProperty
));
568 server
->arglen
+= countAMF(&r
->Link
.extras
, &server
->argc
);
570 SendConnectResult(r
, txn
);
572 else if (AVMATCH(&method
, &av_createStream
))
574 SendResultNumber(r
, txn
, ++server
->streamID
);
576 else if (AVMATCH(&method
, &av_getStreamLength
))
578 SendResultNumber(r
, txn
, 10.0);
580 else if (AVMATCH(&method
, &av_NetStream_Authenticate_UsherToken
))
583 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 3), &usherToken
);
584 AVreplace(&usherToken
, &av_dquote
, &av_escdquote
);
585 server
->arglen
+= 6 + usherToken
.av_len
;
587 r
->Link
.usherToken
= usherToken
;
589 else if (AVMATCH(&method
, &av_play
))
591 char *file
, *p
, *q
, *cmd
, *ptr
;
596 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 3), &r
->Link
.playpath
);
598 r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4));
600 r->Link.length = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 5));
602 if (r
->Link
.tcUrl
.av_len
)
604 len
= server
->arglen
+ r
->Link
.playpath
.av_len
+ 4 +
605 sizeof("rtmpdump") + r
->Link
.playpath
.av_len
+ 12;
608 cmd
= malloc(len
+ server
->argc
* sizeof(AVal
));
610 argv
= (AVal
*)(cmd
+ len
);
611 argv
[0].av_val
= cmd
;
612 argv
[0].av_len
= sizeof("rtmpdump")-1;
613 ptr
+= sprintf(ptr
, "rtmpdump");
616 argv
[argc
].av_val
= ptr
+ 1;
617 argv
[argc
++].av_len
= 2;
618 argv
[argc
].av_val
= ptr
+ 5;
619 ptr
+= sprintf(ptr
," -r \"%s\"", r
->Link
.tcUrl
.av_val
);
620 argv
[argc
++].av_len
= r
->Link
.tcUrl
.av_len
;
622 if (r
->Link
.app
.av_val
)
624 argv
[argc
].av_val
= ptr
+ 1;
625 argv
[argc
++].av_len
= 2;
626 argv
[argc
].av_val
= ptr
+ 5;
627 ptr
+= sprintf(ptr
, " -a \"%s\"", r
->Link
.app
.av_val
);
628 argv
[argc
++].av_len
= r
->Link
.app
.av_len
;
630 if (r
->Link
.flashVer
.av_val
)
632 argv
[argc
].av_val
= ptr
+ 1;
633 argv
[argc
++].av_len
= 2;
634 argv
[argc
].av_val
= ptr
+ 5;
635 ptr
+= sprintf(ptr
, " -f \"%s\"", r
->Link
.flashVer
.av_val
);
636 argv
[argc
++].av_len
= r
->Link
.flashVer
.av_len
;
638 if (r
->Link
.swfUrl
.av_val
)
640 argv
[argc
].av_val
= ptr
+ 1;
641 argv
[argc
++].av_len
= 2;
642 argv
[argc
].av_val
= ptr
+ 5;
643 ptr
+= sprintf(ptr
, " -W \"%s\"", r
->Link
.swfUrl
.av_val
);
644 argv
[argc
++].av_len
= r
->Link
.swfUrl
.av_len
;
646 if (r
->Link
.pageUrl
.av_val
)
648 argv
[argc
].av_val
= ptr
+ 1;
649 argv
[argc
++].av_len
= 2;
650 argv
[argc
].av_val
= ptr
+ 5;
651 ptr
+= sprintf(ptr
, " -p \"%s\"", r
->Link
.pageUrl
.av_val
);
652 argv
[argc
++].av_len
= r
->Link
.pageUrl
.av_len
;
654 if (r
->Link
.usherToken
.av_val
)
656 argv
[argc
].av_val
= ptr
+ 1;
657 argv
[argc
++].av_len
= 2;
658 argv
[argc
].av_val
= ptr
+ 5;
659 ptr
+= sprintf(ptr
, " -j \"%s\"", r
->Link
.usherToken
.av_val
);
660 argv
[argc
++].av_len
= r
->Link
.usherToken
.av_len
;
661 free(r
->Link
.usherToken
.av_val
);
662 r
->Link
.usherToken
.av_val
= NULL
;
663 r
->Link
.usherToken
.av_len
= 0;
665 if (r
->Link
.extras
.o_num
) {
666 ptr
= dumpAMF(&r
->Link
.extras
, ptr
, argv
, &argc
);
667 AMF_Reset(&r
->Link
.extras
);
669 argv
[argc
].av_val
= ptr
+ 1;
670 argv
[argc
++].av_len
= 2;
671 argv
[argc
].av_val
= ptr
+ 5;
672 ptr
+= sprintf(ptr
, " -y \"%.*s\"",
673 r
->Link
.playpath
.av_len
, r
->Link
.playpath
.av_val
);
674 argv
[argc
++].av_len
= r
->Link
.playpath
.av_len
;
676 av
= r
->Link
.playpath
;
677 /* strip trailing URL parameters */
678 q
= memchr(av
.av_val
, '?', av
.av_len
);
688 av
.av_len
= q
- av
.av_val
;
691 /* strip leading slash components */
692 for (p
=av
.av_val
+av
.av_len
-1; p
>=av
.av_val
; p
--)
696 av
.av_len
-= p
- av
.av_val
;
700 /* skip leading dot */
701 if (av
.av_val
[0] == '.')
706 file
= malloc(av
.av_len
+5);
708 memcpy(file
, av
.av_val
, av
.av_len
);
709 file
[av
.av_len
] = '\0';
710 for (p
=file
; *p
; p
++)
714 /* Add extension if none present */
715 if (file
[av
.av_len
- 4] != '.')
719 /* Always use flv extension, regardless of original */
720 if (strcmp(file
+av
.av_len
-4, ".flv"))
722 strcpy(file
+av
.av_len
-4, ".flv");
724 argv
[argc
].av_val
= ptr
+ 1;
725 argv
[argc
++].av_len
= 2;
726 argv
[argc
].av_val
= file
;
727 argv
[argc
].av_len
= av
.av_len
;
728 ptr
+= sprintf(ptr
, " -o %s", file
);
729 now
= RTMP_GetTime();
730 if (now
- server
->filetime
< DUPTIME
&& AVMATCH(&argv
[argc
], &server
->filename
))
732 printf("Duplicate request, skipping.\n");
737 printf("\n%s\n\n", cmd
);
739 server
->filetime
= now
;
740 free(server
->filename
.av_val
);
741 server
->filename
= argv
[argc
++];
742 spawn_dumper(argc
, argv
, cmd
);
747 pc
.m_body
= server
->connect
;
748 server
->connect
= NULL
;
749 RTMPPacket_Free(&pc
);
751 RTMP_SendCtrl(r
, 0, 1, 0);
753 RTMP_SendCtrl(r
, 1, 1, 0);
761 ServePacket(STREAMING_SERVER
*server
, RTMP
*r
, RTMPPacket
*packet
)
765 RTMP_Log(RTMP_LOGDEBUG
, "%s, received packet type %02X, size %u bytes", __FUNCTION__
,
766 packet
->m_packetType
, packet
->m_nBodySize
);
768 switch (packet
->m_packetType
)
770 case RTMP_PACKET_TYPE_CHUNK_SIZE
:
771 // HandleChangeChunkSize(r, packet);
774 case RTMP_PACKET_TYPE_BYTES_READ_REPORT
:
777 case RTMP_PACKET_TYPE_CONTROL
:
778 // HandleCtrl(r, packet);
781 case RTMP_PACKET_TYPE_SERVER_BW
:
782 // HandleServerBW(r, packet);
785 case RTMP_PACKET_TYPE_CLIENT_BW
:
786 // HandleClientBW(r, packet);
789 case RTMP_PACKET_TYPE_AUDIO
:
790 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
793 case RTMP_PACKET_TYPE_VIDEO
:
794 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
797 case RTMP_PACKET_TYPE_FLEX_STREAM_SEND
:
800 case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT
:
803 case RTMP_PACKET_TYPE_FLEX_MESSAGE
:
805 RTMP_Log(RTMP_LOGDEBUG
, "%s, flex message, size %u bytes, not fully supported",
806 __FUNCTION__
, packet
->m_nBodySize
);
807 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
810 /*RTMP_LIB_AMFObject obj;
811 int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);
813 RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);
819 if (ServeInvoke(server
, r
, packet
, 1))
823 case RTMP_PACKET_TYPE_INFO
:
826 case RTMP_PACKET_TYPE_SHARED_OBJECT
:
829 case RTMP_PACKET_TYPE_INVOKE
:
830 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: invoke %u bytes", __FUNCTION__
,
831 packet
->m_nBodySize
);
832 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
834 if (ServeInvoke(server
, r
, packet
, 0))
838 case RTMP_PACKET_TYPE_FLASH_VIDEO
:
841 RTMP_Log(RTMP_LOGDEBUG
, "%s, unknown packet type received: 0x%02x", __FUNCTION__
,
842 packet
->m_packetType
);
844 RTMP_LogHex(RTMP_LOGDEBUG
, packet
->m_body
, packet
->m_nBodySize
);
851 controlServerThread(void *unused
)
860 RTMP_LogPrintf("Exiting\n");
861 stopStreaming(rtmpServer
);
865 RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich
);
872 void doServe(STREAMING_SERVER
* server
, // server socket and state (our listening socket)
873 int sockfd
// client connection socket
876 server
->state
= STREAMING_IN_PROGRESS
;
878 RTMP
*rtmp
= RTMP_Alloc(); /* our session with the real client */
879 RTMPPacket packet
= { 0 };
881 // timeout for http requests
885 memset(&tv
, 0, sizeof(struct timeval
));
889 FD_SET(sockfd
, &fds
);
891 if (select(sockfd
+ 1, &fds
, NULL
, NULL
, &tv
) <= 0)
893 RTMP_Log(RTMP_LOGERROR
, "Request timeout/select failed, ignoring request");
899 rtmp
->m_sb
.sb_socket
= sockfd
;
900 if (!RTMP_Serve(rtmp
))
902 RTMP_Log(RTMP_LOGERROR
, "Handshake failed");
907 while (RTMP_IsConnected(rtmp
) && RTMP_ReadPacket(rtmp
, &packet
))
909 if (!RTMPPacket_IsReady(&packet
))
911 ServePacket(server
, rtmp
, &packet
);
912 RTMPPacket_Free(&packet
);
916 RTMP_LogPrintf("Closing connection... ");
918 /* Should probably be done by RTMP_Close() ... */
919 rtmp
->Link
.playpath
.av_val
= NULL
;
920 rtmp
->Link
.tcUrl
.av_val
= NULL
;
921 rtmp
->Link
.swfUrl
.av_val
= NULL
;
922 rtmp
->Link
.pageUrl
.av_val
= NULL
;
923 rtmp
->Link
.app
.av_val
= NULL
;
924 rtmp
->Link
.flashVer
.av_val
= NULL
;
925 if (rtmp
->Link
.usherToken
.av_val
)
927 free(rtmp
->Link
.usherToken
.av_val
);
928 rtmp
->Link
.usherToken
.av_val
= NULL
;
931 RTMP_LogPrintf("done!\n\n");
934 if (server
->state
== STREAMING_IN_PROGRESS
)
935 server
->state
= STREAMING_ACCEPTING
;
941 serverThread(void *arg
)
943 STREAMING_SERVER
*server
= arg
;
944 server
->state
= STREAMING_ACCEPTING
;
946 while (server
->state
== STREAMING_ACCEPTING
)
948 struct sockaddr_in addr
;
949 socklen_t addrlen
= sizeof(struct sockaddr_in
);
951 accept(server
->socket
, (struct sockaddr
*) &addr
, &addrlen
);
956 struct sockaddr_in dest
;
958 socklen_t destlen
= sizeof(struct sockaddr_in
);
959 getsockopt(sockfd
, SOL_IP
, SO_ORIGINAL_DST
, &dest
, &destlen
);
960 strcpy(destch
, inet_ntoa(dest
.sin_addr
));
961 RTMP_Log(RTMP_LOGDEBUG
, "%s: accepted connection from %s to %s\n", __FUNCTION__
,
962 inet_ntoa(addr
.sin_addr
), destch
);
964 RTMP_Log(RTMP_LOGDEBUG
, "%s: accepted connection from %s\n", __FUNCTION__
,
965 inet_ntoa(addr
.sin_addr
));
967 /* Create a new thread and transfer the control to that */
968 doServe(server
, sockfd
);
969 RTMP_Log(RTMP_LOGDEBUG
, "%s: processed request\n", __FUNCTION__
);
973 RTMP_Log(RTMP_LOGERROR
, "%s: accept failed", __FUNCTION__
);
976 server
->state
= STREAMING_STOPPED
;
981 startStreaming(const char *address
, int port
)
983 struct sockaddr_in addr
;
985 STREAMING_SERVER
*server
;
987 sockfd
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
990 RTMP_Log(RTMP_LOGERROR
, "%s, couldn't create socket", __FUNCTION__
);
995 setsockopt(sockfd
, SOL_SOCKET
, SO_REUSEADDR
,
996 (char *) &tmp
, sizeof(tmp
) );
998 addr
.sin_family
= AF_INET
;
999 addr
.sin_addr
.s_addr
= inet_addr(address
); //htonl(INADDR_ANY);
1000 addr
.sin_port
= htons(port
);
1002 if (bind(sockfd
, (struct sockaddr
*) &addr
, sizeof(struct sockaddr_in
)) ==
1005 RTMP_Log(RTMP_LOGERROR
, "%s, TCP bind failed for port number: %d", __FUNCTION__
,
1010 if (listen(sockfd
, 10) == -1)
1012 RTMP_Log(RTMP_LOGERROR
, "%s, listen failed", __FUNCTION__
);
1013 closesocket(sockfd
);
1017 server
= (STREAMING_SERVER
*) calloc(1, sizeof(STREAMING_SERVER
));
1018 server
->socket
= sockfd
;
1020 ThreadCreate(serverThread
, server
);
1026 stopStreaming(STREAMING_SERVER
* server
)
1030 if (server
->state
!= STREAMING_STOPPED
)
1032 if (server
->state
== STREAMING_IN_PROGRESS
)
1034 server
->state
= STREAMING_STOPPING
;
1036 // wait for streaming threads to exit
1037 while (server
->state
!= STREAMING_STOPPED
)
1041 if (closesocket(server
->socket
))
1042 RTMP_Log(RTMP_LOGERROR
, "%s: Failed to close listening socket, error %d",
1043 __FUNCTION__
, GetSockError());
1045 server
->state
= STREAMING_STOPPED
;
1051 sigIntHandler(int sig
)
1054 RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig
);
1056 stopStreaming(rtmpServer
);
1057 signal(SIGINT
, SIG_DFL
);
1061 main(int argc
, char **argv
)
1063 int nStatus
= RD_SUCCESS
;
1065 // http streaming server
1066 char DEFAULT_HTTP_STREAMING_DEVICE
[] = "0.0.0.0"; // 0.0.0.0 is any device
1068 char *rtmpStreamingDevice
= DEFAULT_HTTP_STREAMING_DEVICE
; // streaming device, default 0.0.0.0
1069 int nRtmpStreamingPort
= 1935; // port
1071 RTMP_LogPrintf("RTMP Server %s\n", RTMPDUMP_VERSION
);
1072 RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n");
1074 RTMP_debuglevel
= RTMP_LOGINFO
;
1076 if (argc
> 1 && !strcmp(argv
[1], "-z"))
1077 RTMP_debuglevel
= RTMP_LOGALL
;
1080 memset(&defaultRTMPRequest
, 0, sizeof(RTMP_REQUEST
));
1082 defaultRTMPRequest
.rtmpport
= -1;
1083 defaultRTMPRequest
.protocol
= RTMP_PROTOCOL_UNDEFINED
;
1084 defaultRTMPRequest
.bLiveStream
= FALSE
; // is it a live stream? then we can't seek/resume
1086 defaultRTMPRequest
.timeout
= 300; // timeout connection afte 300 seconds
1087 defaultRTMPRequest
.bufferTime
= 20 * 1000;
1090 signal(SIGINT
, sigIntHandler
);
1092 signal(SIGPIPE
, SIG_IGN
);
1096 netstackdump
= fopen("netstackdump", "wb");
1097 netstackdump_read
= fopen("netstackdump_read", "wb");
1103 ThreadCreate(controlServerThread
, 0);
1105 // start http streaming
1107 startStreaming(rtmpStreamingDevice
, nRtmpStreamingPort
)) == 0)
1109 RTMP_Log(RTMP_LOGERROR
, "Failed to start RTMP server, exiting!");
1112 RTMP_LogPrintf("Streaming on rtmp://%s:%d\n", rtmpStreamingDevice
,
1113 nRtmpStreamingPort
);
1115 while (rtmpServer
->state
!= STREAMING_STOPPED
)
1119 RTMP_Log(RTMP_LOGDEBUG
, "Done, exiting...");
1124 if (netstackdump
!= 0)
1125 fclose(netstackdump
);
1126 if (netstackdump_read
!= 0)
1127 fclose(netstackdump_read
);
1133 AVreplace(AVal
*src
, const AVal
*orig
, const AVal
*repl
)
1135 char *srcbeg
= src
->av_val
;
1136 char *srcend
= src
->av_val
+ src
->av_len
;
1137 char *dest
, *sptr
, *dptr
;
1140 /* count occurrences of orig in src */
1142 while (sptr
< srcend
&& (sptr
= strstr(sptr
, orig
->av_val
)))
1145 sptr
+= orig
->av_len
;
1150 dest
= malloc(src
->av_len
+ 1 + (repl
->av_len
- orig
->av_len
) * n
);
1154 while (sptr
< srcend
&& (sptr
= strstr(sptr
, orig
->av_val
)))
1157 memcpy(dptr
, srcbeg
, n
);
1159 memcpy(dptr
, repl
->av_val
, repl
->av_len
);
1160 dptr
+= repl
->av_len
;
1161 sptr
+= orig
->av_len
;
1164 n
= srcend
- srcbeg
;
1165 memcpy(dptr
, srcbeg
, n
);
1169 src
->av_len
= dptr
- dest
;