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
;
228 *enc
++ = AMF_OBJECT_END
;
230 packet
.m_nBodySize
= enc
- packet
.m_body
;
232 return RTMP_SendPacket(r
, &packet
, FALSE
);
236 SendResultNumber(RTMP
*r
, double txn
, double ID
)
239 char pbuf
[256], *pend
= pbuf
+sizeof(pbuf
);
241 packet
.m_nChannel
= 0x03; // control channel (invoke)
242 packet
.m_headerType
= 1; /* RTMP_PACKET_SIZE_MEDIUM; */
243 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
244 packet
.m_nTimeStamp
= 0;
245 packet
.m_nInfoField2
= 0;
246 packet
.m_hasAbsTimestamp
= 0;
247 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
249 char *enc
= packet
.m_body
;
250 enc
= AMF_EncodeString(enc
, pend
, &av__result
);
251 enc
= AMF_EncodeNumber(enc
, pend
, txn
);
253 enc
= AMF_EncodeNumber(enc
, pend
, ID
);
255 packet
.m_nBodySize
= enc
- packet
.m_body
;
257 return RTMP_SendPacket(r
, &packet
, FALSE
);
262 static const AVal av_NetStream_Play_Start
= AVC("NetStream.Play.Start");
263 static const AVal av_Started_playing
= AVC("Started playing");
264 static const AVal av_NetStream_Play_Stop
= AVC("NetStream.Play.Stop");
265 static const AVal av_Stopped_playing
= AVC("Stopped playing");
268 static const AVal av_NetStream_Authenticate_UsherToken
= AVC("NetStream.Authenticate.UsherToken");
271 SendPlayStart(RTMP
*r
)
274 char pbuf
[512], *pend
= pbuf
+sizeof(pbuf
);
276 packet
.m_nChannel
= 0x03; // control channel (invoke)
277 packet
.m_headerType
= 1; /* RTMP_PACKET_SIZE_MEDIUM; */
278 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
279 packet
.m_nTimeStamp
= 0;
280 packet
.m_nInfoField2
= 0;
281 packet
.m_hasAbsTimestamp
= 0;
282 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
284 char *enc
= packet
.m_body
;
285 enc
= AMF_EncodeString(enc
, pend
, &av_onStatus
);
286 enc
= AMF_EncodeNumber(enc
, pend
, 0);
289 enc
= AMF_EncodeNamedString(enc
, pend
, &av_level
, &av_status
);
290 enc
= AMF_EncodeNamedString(enc
, pend
, &av_code
, &av_NetStream_Play_Start
);
291 enc
= AMF_EncodeNamedString(enc
, pend
, &av_description
, &av_Started_playing
);
292 enc
= AMF_EncodeNamedString(enc
, pend
, &av_details
, &r
->Link
.playpath
);
293 enc
= AMF_EncodeNamedString(enc
, pend
, &av_clientid
, &av_clientid
);
296 *enc
++ = AMF_OBJECT_END
;
298 packet
.m_nBodySize
= enc
- packet
.m_body
;
299 return RTMP_SendPacket(r
, &packet
, FALSE
);
303 SendPlayStop(RTMP
*r
)
306 char pbuf
[512], *pend
= pbuf
+sizeof(pbuf
);
308 packet
.m_nChannel
= 0x03; // control channel (invoke)
309 packet
.m_headerType
= 1; /* RTMP_PACKET_SIZE_MEDIUM; */
310 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
311 packet
.m_nTimeStamp
= 0;
312 packet
.m_nInfoField2
= 0;
313 packet
.m_hasAbsTimestamp
= 0;
314 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
316 char *enc
= packet
.m_body
;
317 enc
= AMF_EncodeString(enc
, pend
, &av_onStatus
);
318 enc
= AMF_EncodeNumber(enc
, pend
, 0);
321 enc
= AMF_EncodeNamedString(enc
, pend
, &av_level
, &av_status
);
322 enc
= AMF_EncodeNamedString(enc
, pend
, &av_code
, &av_NetStream_Play_Stop
);
323 enc
= AMF_EncodeNamedString(enc
, pend
, &av_description
, &av_Stopped_playing
);
324 enc
= AMF_EncodeNamedString(enc
, pend
, &av_details
, &r
->Link
.playpath
);
325 enc
= AMF_EncodeNamedString(enc
, pend
, &av_clientid
, &av_clientid
);
328 *enc
++ = AMF_OBJECT_END
;
330 packet
.m_nBodySize
= enc
- packet
.m_body
;
331 return RTMP_SendPacket(r
, &packet
, FALSE
);
335 spawn_dumper(int argc
, AVal
*av
, char *cmd
)
338 STARTUPINFO si
= {0};
339 PROCESS_INFORMATION pi
= {0};
342 if (CreateProcess(NULL
, cmd
, NULL
, NULL
, FALSE
, 0, NULL
, NULL
,
345 CloseHandle(pi
.hThread
);
346 CloseHandle(pi
.hProcess
);
349 /* reap any dead children */
350 while (waitpid(-1, NULL
, WNOHANG
) > 0);
353 char **argv
= malloc((argc
+1) * sizeof(char *));
356 for (i
=0; i
<argc
; i
++) {
357 argv
[i
] = av
[i
].av_val
;
358 argv
[i
][av
[i
].av_len
] = '\0';
361 if ((i
= execvp(argv
[0], argv
)))
368 countAMF(AMFObject
*obj
, int *argc
)
372 for (i
=0, len
=0; i
< obj
->o_num
; i
++)
374 AMFObjectProperty
*p
= &obj
->o_props
[i
];
377 if (p
->p_name
.av_val
)
380 if (p
->p_name
.av_val
)
381 len
+= p
->p_name
.av_len
+ 1;
388 len
+= p
->p_vu
.p_aval
.av_len
;
395 len
+= countAMF(&p
->p_vu
.p_object
, argc
);
407 dumpAMF(AMFObject
*obj
, char *ptr
, AVal
*argv
, int *argc
)
409 int i
, len
, ac
= *argc
;
410 const char opt
[] = "NBSO Z";
412 for (i
=0, len
=0; i
< obj
->o_num
; i
++)
414 AMFObjectProperty
*p
= &obj
->o_props
[i
];
415 argv
[ac
].av_val
= ptr
+1;
416 argv
[ac
++].av_len
= 2;
417 ptr
+= sprintf(ptr
, " -C ");
418 argv
[ac
].av_val
= ptr
;
419 if (p
->p_name
.av_val
)
421 *ptr
++ = opt
[p
->p_type
];
423 if (p
->p_name
.av_val
)
424 ptr
+= sprintf(ptr
, "%.*s:", p
->p_name
.av_len
, p
->p_name
.av_val
);
428 *ptr
++ = p
->p_vu
.p_number
!= 0 ? '1' : '0';
429 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
432 memcpy(ptr
, p
->p_vu
.p_aval
.av_val
, p
->p_vu
.p_aval
.av_len
);
433 ptr
+= p
->p_vu
.p_aval
.av_len
;
434 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
437 ptr
+= sprintf(ptr
, "%f", p
->p_vu
.p_number
);
438 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
442 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
445 ptr
= dumpAMF(&p
->p_vu
.p_object
, ptr
, argv
, argc
);
447 argv
[ac
].av_val
= ptr
+1;
448 argv
[ac
++].av_len
= 2;
449 argv
[ac
].av_val
= ptr
+4;
451 ptr
+= sprintf(ptr
, " -C O:0");
455 argv
[ac
].av_len
= ptr
- argv
[ac
].av_val
;
464 // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
466 ServeInvoke(STREAMING_SERVER
*server
, RTMP
* r
, RTMPPacket
*packet
, unsigned int offset
)
469 unsigned int nBodySize
;
472 body
= packet
->m_body
+ offset
;
473 nBodySize
= packet
->m_nBodySize
- offset
;
475 if (body
[0] != 0x02) // make sure it is a string method name we start with
477 RTMP_Log(RTMP_LOGWARNING
, "%s, Sanity failed. no string method in invoke packet",
483 nRes
= AMF_Decode(&obj
, body
, nBodySize
, FALSE
);
486 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding invoke packet", __FUNCTION__
);
492 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 0), &method
);
493 double txn
= AMFProp_GetNumber(AMF_GetProp(&obj
, NULL
, 1));
494 RTMP_Log(RTMP_LOGDEBUG
, "%s, client invoking <%s>", __FUNCTION__
, method
.av_val
);
496 if (AVMATCH(&method
, &av_connect
))
502 server
->connect
= packet
->m_body
;
503 packet
->m_body
= NULL
;
505 AMFProp_GetObject(AMF_GetProp(&obj
, NULL
, 2), &cobj
);
506 for (i
=0; i
<cobj
.o_num
; i
++)
508 pname
= cobj
.o_props
[i
].p_name
;
511 if (cobj
.o_props
[i
].p_type
== AMF_STRING
)
512 pval
= cobj
.o_props
[i
].p_vu
.p_aval
;
513 if (AVMATCH(&pname
, &av_app
))
517 if (!r
->Link
.app
.av_val
)
518 r
->Link
.app
.av_val
= "";
519 server
->arglen
+= 6 + pval
.av_len
;
522 else if (AVMATCH(&pname
, &av_flashVer
))
524 r
->Link
.flashVer
= pval
;
526 server
->arglen
+= 6 + pval
.av_len
;
529 else if (AVMATCH(&pname
, &av_swfUrl
))
531 r
->Link
.swfUrl
= pval
;
533 server
->arglen
+= 6 + pval
.av_len
;
536 else if (AVMATCH(&pname
, &av_tcUrl
))
538 r
->Link
.tcUrl
= pval
;
540 server
->arglen
+= 6 + pval
.av_len
;
543 else if (AVMATCH(&pname
, &av_pageUrl
))
545 r
->Link
.pageUrl
= pval
;
547 server
->arglen
+= 6 + pval
.av_len
;
550 else if (AVMATCH(&pname
, &av_audioCodecs
))
552 r
->m_fAudioCodecs
= cobj
.o_props
[i
].p_vu
.p_number
;
554 else if (AVMATCH(&pname
, &av_videoCodecs
))
556 r
->m_fVideoCodecs
= cobj
.o_props
[i
].p_vu
.p_number
;
558 else if (AVMATCH(&pname
, &av_objectEncoding
))
560 r
->m_fEncoding
= cobj
.o_props
[i
].p_vu
.p_number
;
563 /* Still have more parameters? Copy them */
566 int i
= obj
.o_num
- 3;
567 r
->Link
.extras
.o_num
= i
;
568 r
->Link
.extras
.o_props
= malloc(i
*sizeof(AMFObjectProperty
));
569 memcpy(r
->Link
.extras
.o_props
, obj
.o_props
+3, i
*sizeof(AMFObjectProperty
));
571 server
->arglen
+= countAMF(&r
->Link
.extras
, &server
->argc
);
573 SendConnectResult(r
, txn
);
575 else if (AVMATCH(&method
, &av_createStream
))
577 SendResultNumber(r
, txn
, ++server
->streamID
);
579 else if (AVMATCH(&method
, &av_getStreamLength
))
581 SendResultNumber(r
, txn
, 10.0);
583 else if (AVMATCH(&method
, &av_NetStream_Authenticate_UsherToken
))
586 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 3), &usherToken
);
587 AVreplace(&usherToken
, &av_dquote
, &av_escdquote
);
588 server
->arglen
+= 6 + usherToken
.av_len
;
590 r
->Link
.usherToken
= usherToken
;
592 else if (AVMATCH(&method
, &av_play
))
594 char *file
, *p
, *q
, *cmd
, *ptr
;
599 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 3), &r
->Link
.playpath
);
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 %lu bytes", __FUNCTION__
,
769 packet
->m_packetType
, packet
->m_nBodySize
);
771 switch (packet
->m_packetType
)
775 // HandleChangeChunkSize(r, packet);
784 // HandleCtrl(r, packet);
789 // HandleServerBW(r, packet);
794 // HandleClientBW(r, packet);
799 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
804 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
807 case 0x0F: // flex stream send
810 case 0x10: // flex shared object
813 case 0x11: // flex message
815 RTMP_Log(RTMP_LOGDEBUG
, "%s, flex message, size %lu bytes, not fully supported",
816 __FUNCTION__
, packet
->m_nBodySize
);
817 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
820 /*RTMP_LIB_AMFObject obj;
821 int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1);
823 RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__);
829 if (ServeInvoke(server
, r
, packet
, 1))
843 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: invoke %lu bytes", __FUNCTION__
,
844 packet
->m_nBodySize
);
845 //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
847 if (ServeInvoke(server
, r
, packet
, 0))
855 RTMP_Log(RTMP_LOGDEBUG
, "%s, unknown packet type received: 0x%02x", __FUNCTION__
,
856 packet
->m_packetType
);
858 RTMP_LogHex(RTMP_LOGDEBUG
, packet
->m_body
, packet
->m_nBodySize
);
865 controlServerThread(void *unused
)
874 RTMP_LogPrintf("Exiting\n");
875 stopStreaming(rtmpServer
);
879 RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich
);
886 void doServe(STREAMING_SERVER
* server
, // server socket and state (our listening socket)
887 int sockfd
// client connection socket
890 server
->state
= STREAMING_IN_PROGRESS
;
892 RTMP rtmp
= { 0 }; /* our session with the real client */
893 RTMPPacket packet
= { 0 };
895 // timeout for http requests
899 memset(&tv
, 0, sizeof(struct timeval
));
903 FD_SET(sockfd
, &fds
);
905 if (select(sockfd
+ 1, &fds
, NULL
, NULL
, &tv
) <= 0)
907 RTMP_Log(RTMP_LOGERROR
, "Request timeout/select failed, ignoring request");
913 rtmp
.m_sb
.sb_socket
= sockfd
;
914 if (!RTMP_Serve(&rtmp
))
916 RTMP_Log(RTMP_LOGERROR
, "Handshake failed");
921 while (RTMP_IsConnected(&rtmp
) && RTMP_ReadPacket(&rtmp
, &packet
))
923 if (!RTMPPacket_IsReady(&packet
))
925 ServePacket(server
, &rtmp
, &packet
);
926 RTMPPacket_Free(&packet
);
930 RTMP_LogPrintf("Closing connection... ");
932 /* Should probably be done by RTMP_Close() ... */
933 rtmp
.Link
.playpath
.av_val
= NULL
;
934 rtmp
.Link
.tcUrl
.av_val
= NULL
;
935 rtmp
.Link
.swfUrl
.av_val
= NULL
;
936 rtmp
.Link
.pageUrl
.av_val
= NULL
;
937 rtmp
.Link
.app
.av_val
= NULL
;
938 rtmp
.Link
.flashVer
.av_val
= NULL
;
939 if (rtmp
.Link
.usherToken
.av_val
)
941 free(rtmp
.Link
.usherToken
.av_val
);
942 rtmp
.Link
.usherToken
.av_val
= NULL
;
944 RTMP_LogPrintf("done!\n\n");
947 if (server
->state
== STREAMING_IN_PROGRESS
)
948 server
->state
= STREAMING_ACCEPTING
;
954 serverThread(void *arg
)
956 STREAMING_SERVER
*server
= arg
;
957 server
->state
= STREAMING_ACCEPTING
;
959 while (server
->state
== STREAMING_ACCEPTING
)
961 struct sockaddr_in addr
;
962 socklen_t addrlen
= sizeof(struct sockaddr_in
);
964 accept(server
->socket
, (struct sockaddr
*) &addr
, &addrlen
);
969 struct sockaddr_in dest
;
971 socklen_t destlen
= sizeof(struct sockaddr_in
);
972 getsockopt(sockfd
, SOL_IP
, SO_ORIGINAL_DST
, &dest
, &destlen
);
973 strcpy(destch
, inet_ntoa(dest
.sin_addr
));
974 RTMP_Log(RTMP_LOGDEBUG
, "%s: accepted connection from %s to %s\n", __FUNCTION__
,
975 inet_ntoa(addr
.sin_addr
), destch
);
977 RTMP_Log(RTMP_LOGDEBUG
, "%s: accepted connection from %s\n", __FUNCTION__
,
978 inet_ntoa(addr
.sin_addr
));
980 /* Create a new thread and transfer the control to that */
981 doServe(server
, sockfd
);
982 RTMP_Log(RTMP_LOGDEBUG
, "%s: processed request\n", __FUNCTION__
);
986 RTMP_Log(RTMP_LOGERROR
, "%s: accept failed", __FUNCTION__
);
989 server
->state
= STREAMING_STOPPED
;
994 startStreaming(const char *address
, int port
)
996 struct sockaddr_in addr
;
998 STREAMING_SERVER
*server
;
1000 sockfd
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1003 RTMP_Log(RTMP_LOGERROR
, "%s, couldn't create socket", __FUNCTION__
);
1008 setsockopt(sockfd
, SOL_SOCKET
, SO_REUSEADDR
,
1009 (char *) &tmp
, sizeof(tmp
) );
1011 addr
.sin_family
= AF_INET
;
1012 addr
.sin_addr
.s_addr
= inet_addr(address
); //htonl(INADDR_ANY);
1013 addr
.sin_port
= htons(port
);
1015 if (bind(sockfd
, (struct sockaddr
*) &addr
, sizeof(struct sockaddr_in
)) ==
1018 RTMP_Log(RTMP_LOGERROR
, "%s, TCP bind failed for port number: %d", __FUNCTION__
,
1023 if (listen(sockfd
, 10) == -1)
1025 RTMP_Log(RTMP_LOGERROR
, "%s, listen failed", __FUNCTION__
);
1026 closesocket(sockfd
);
1030 server
= (STREAMING_SERVER
*) calloc(1, sizeof(STREAMING_SERVER
));
1031 server
->socket
= sockfd
;
1033 ThreadCreate(serverThread
, server
);
1039 stopStreaming(STREAMING_SERVER
* server
)
1043 if (server
->state
!= STREAMING_STOPPED
)
1045 if (server
->state
== STREAMING_IN_PROGRESS
)
1047 server
->state
= STREAMING_STOPPING
;
1049 // wait for streaming threads to exit
1050 while (server
->state
!= STREAMING_STOPPED
)
1054 if (closesocket(server
->socket
))
1055 RTMP_Log(RTMP_LOGERROR
, "%s: Failed to close listening socket, error %d",
1058 server
->state
= STREAMING_STOPPED
;
1064 sigIntHandler(int sig
)
1067 RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig
);
1069 stopStreaming(rtmpServer
);
1070 signal(SIGINT
, SIG_DFL
);
1074 main(int argc
, char **argv
)
1076 int nStatus
= RD_SUCCESS
;
1078 // http streaming server
1079 char DEFAULT_HTTP_STREAMING_DEVICE
[] = "0.0.0.0"; // 0.0.0.0 is any device
1081 char *rtmpStreamingDevice
= DEFAULT_HTTP_STREAMING_DEVICE
; // streaming device, default 0.0.0.0
1082 int nRtmpStreamingPort
= 1935; // port
1084 RTMP_LogPrintf("RTMP Server %s\n", RTMPDUMP_VERSION
);
1085 RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n");
1087 RTMP_debuglevel
= RTMP_LOGINFO
;
1089 if (argc
> 1 && !strcmp(argv
[1], "-z"))
1090 RTMP_debuglevel
= RTMP_LOGALL
;
1093 memset(&defaultRTMPRequest
, 0, sizeof(RTMP_REQUEST
));
1095 defaultRTMPRequest
.rtmpport
= -1;
1096 defaultRTMPRequest
.protocol
= RTMP_PROTOCOL_UNDEFINED
;
1097 defaultRTMPRequest
.bLiveStream
= FALSE
; // is it a live stream? then we can't seek/resume
1099 defaultRTMPRequest
.timeout
= 300; // timeout connection afte 300 seconds
1100 defaultRTMPRequest
.bufferTime
= 20 * 1000;
1103 signal(SIGINT
, sigIntHandler
);
1105 signal(SIGPIPE
, SIG_IGN
);
1109 netstackdump
= fopen("netstackdump", "wb");
1110 netstackdump_read
= fopen("netstackdump_read", "wb");
1116 ThreadCreate(controlServerThread
, 0);
1118 // start http streaming
1120 startStreaming(rtmpStreamingDevice
, nRtmpStreamingPort
)) == 0)
1122 RTMP_Log(RTMP_LOGERROR
, "Failed to start RTMP server, exiting!");
1125 RTMP_LogPrintf("Streaming on rtmp://%s:%d\n", rtmpStreamingDevice
,
1126 nRtmpStreamingPort
);
1128 while (rtmpServer
->state
!= STREAMING_STOPPED
)
1132 RTMP_Log(RTMP_LOGDEBUG
, "Done, exiting...");
1137 if (netstackdump
!= 0)
1138 fclose(netstackdump
);
1139 if (netstackdump_read
!= 0)
1140 fclose(netstackdump_read
);
1146 AVreplace(AVal
*src
, const AVal
*orig
, const AVal
*repl
)
1148 char *srcbeg
= src
->av_val
;
1149 char *srcend
= src
->av_val
+ src
->av_len
;
1150 char *dest
, *sptr
, *dptr
;
1153 /* count occurrences of orig in src */
1155 while (sptr
< srcend
&& (sptr
= strstr(sptr
, orig
->av_val
)))
1158 sptr
+= orig
->av_len
;
1163 dest
= malloc(src
->av_len
+ 1 + (repl
->av_len
- orig
->av_len
) * n
);
1167 while (sptr
< srcend
&& (sptr
= strstr(sptr
, orig
->av_val
)))
1170 memcpy(dptr
, srcbeg
, n
);
1172 memcpy(dptr
, repl
->av_val
, repl
->av_len
);
1173 dptr
+= repl
->av_len
;
1174 sptr
+= orig
->av_len
;
1177 n
= srcend
- srcbeg
;
1178 memcpy(dptr
, srcbeg
, n
);
1182 src
->av_len
= dptr
- dest
;