2 * Copyright (C) 2009 Andrej Stepanchuk
3 * Copyright (C) 2009 Howard Chu
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with RTMPDump; see the file COPYING. If not, write to
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 * http://www.gnu.org/copyleft/gpl.html
23 /* This is a Proxy Server that displays the connection parameters from a
24 * client and then saves any data streamed to the client.
37 #include "librtmp/rtmp_sys.h"
38 #include "librtmp/log.h"
43 #include <linux/netfilter_ipv4.h>
48 #define RD_INCOMPLETE 2
50 #define PACKET_SIZE 1024*1024
53 #define InitSockets() {\
57 version = MAKEWORD(1,1); \
58 WSAStartup(version, &wsaData); }
60 #define CleanupSockets() WSACleanup()
63 #define CleanupSockets()
69 STREAMING_IN_PROGRESS
,
94 Plist
*rs_pkt
[2]; /* head, tail */
95 Plist
*rc_pkt
[2]; /* head, tail */
96 Flist
*f_head
, *f_tail
;
101 STREAMING_SERVER
*rtmpServer
= 0; // server structure pointer
103 STREAMING_SERVER
*startStreaming(const char *address
, int port
);
104 void stopStreaming(STREAMING_SERVER
* server
);
106 #define STR2AVAL(av,str) av.av_val = str; av.av_len = strlen(av.av_val)
109 uint32_t debugTS
= 0;
113 FILE *netstackdump
= NULL
;
114 FILE *netstackdump_read
= NULL
;
117 #define BUFFERTIME (4*60*60*1000) /* 4 hours */
119 #define SAVC(x) static const AVal av_##x = AVC(#x)
132 SAVC(objectEncoding
);
144 static const AVal av_NetStream_Failed
= AVC("NetStream.Failed");
145 static const AVal av_NetStream_Play_Failed
= AVC("NetStream.Play.Failed");
146 static const AVal av_NetStream_Play_StreamNotFound
=
147 AVC("NetStream.Play.StreamNotFound");
148 static const AVal av_NetConnection_Connect_InvalidApp
=
149 AVC("NetConnection.Connect.InvalidApp");
150 static const AVal av_NetStream_Play_Start
= AVC("NetStream.Play.Start");
151 static const AVal av_NetStream_Play_Complete
= AVC("NetStream.Play.Complete");
152 static const AVal av_NetStream_Play_Stop
= AVC("NetStream.Play.Stop");
154 static const char *cst
[] = { "client", "server" };
156 // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
158 ServeInvoke(STREAMING_SERVER
*server
, int which
, RTMPPacket
*pack
, const char *body
)
161 int nBodySize
= pack
->m_nBodySize
;
163 if (body
> pack
->m_body
)
166 if (body
[0] != 0x02) // make sure it is a string method name we start with
168 RTMP_Log(RTMP_LOGWARNING
, "%s, Sanity failed. no string method in invoke packet",
174 nRes
= AMF_Decode(&obj
, body
, nBodySize
, FALSE
);
177 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding invoke packet", __FUNCTION__
);
183 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 0), &method
);
184 RTMP_Log(RTMP_LOGDEBUG
, "%s, %s invoking <%s>", __FUNCTION__
, cst
[which
], method
.av_val
);
186 if (AVMATCH(&method
, &av_connect
))
191 AMFProp_GetObject(AMF_GetProp(&obj
, NULL
, 2), &cobj
);
192 RTMP_LogPrintf("Processing connect\n");
193 for (i
=0; i
<cobj
.o_num
; i
++)
195 pname
= cobj
.o_props
[i
].p_name
;
198 if (cobj
.o_props
[i
].p_type
== AMF_STRING
)
200 pval
= cobj
.o_props
[i
].p_vu
.p_aval
;
201 RTMP_LogPrintf("%.*s: %.*s\n", pname
.av_len
, pname
.av_val
, pval
.av_len
, pval
.av_val
);
203 if (AVMATCH(&pname
, &av_app
))
205 server
->rc
.Link
.app
= pval
;
208 else if (AVMATCH(&pname
, &av_flashVer
))
210 server
->rc
.Link
.flashVer
= pval
;
213 else if (AVMATCH(&pname
, &av_swfUrl
))
217 RTMP_HashSWF(pval
.av_val
, &server
->rc
.Link
.SWFSize
,
218 (unsigned char *)server
->rc
.Link
.SWFHash
, 30);
220 server
->rc
.Link
.swfUrl
= pval
;
223 else if (AVMATCH(&pname
, &av_tcUrl
))
225 char *r1
= NULL
, *r2
;
228 server
->rc
.Link
.tcUrl
= pval
;
229 if ((pval
.av_val
[0] | 0x40) == 'r' &&
230 (pval
.av_val
[1] | 0x40) == 't' &&
231 (pval
.av_val
[2] | 0x40) == 'm' &&
232 (pval
.av_val
[3] | 0x40) == 'p')
234 if (pval
.av_val
[4] == ':')
236 server
->rc
.Link
.protocol
= RTMP_PROTOCOL_RTMP
;
239 else if ((pval
.av_val
[4] | 0x40) == 'e' && pval
.av_val
[5] == ':')
241 server
->rc
.Link
.protocol
= RTMP_PROTOCOL_RTMPE
;
244 r2
= strchr(r1
, '/');
248 len
= pval
.av_len
- (r1
- pval
.av_val
);
252 server
->rc
.Link
.hostname
.av_val
= r2
;
253 r1
= strrchr(r2
, ':');
256 server
->rc
.Link
.hostname
.av_len
= r1
- r2
;
258 server
->rc
.Link
.port
= atoi(r1
);
262 server
->rc
.Link
.hostname
.av_len
= len
;
263 server
->rc
.Link
.port
= 1935;
268 else if (AVMATCH(&pname
, &av_pageUrl
))
270 server
->rc
.Link
.pageUrl
= pval
;
273 else if (AVMATCH(&pname
, &av_audioCodecs
))
275 server
->rc
.m_fAudioCodecs
= cobj
.o_props
[i
].p_vu
.p_number
;
277 else if (AVMATCH(&pname
, &av_videoCodecs
))
279 server
->rc
.m_fVideoCodecs
= cobj
.o_props
[i
].p_vu
.p_number
;
281 else if (AVMATCH(&pname
, &av_objectEncoding
))
283 server
->rc
.m_fEncoding
= cobj
.o_props
[i
].p_vu
.p_number
;
284 server
->rc
.m_bSendEncoding
= TRUE
;
286 /* Dup'd a string we didn't recognize? */
292 if (AMFProp_GetBoolean(&obj
.o_props
[3]))
293 server
->rc
.Link
.lFlags
|= RTMP_LF_AUTH
;
296 AMFProp_GetString(&obj
.o_props
[4], &server
->rc
.Link
.auth
);
300 if (!RTMP_Connect(&server
->rc
, pack
))
305 server
->rc
.m_bSendCounter
= FALSE
;
307 else if (AVMATCH(&method
, &av_play
))
313 char flvHeader
[] = { 'F', 'L', 'V', 0x01,
314 0x05, // video + audio, we finalize later if the value is different
315 0x00, 0x00, 0x00, 0x09,
316 0x00, 0x00, 0x00, 0x00 // first prevTagSize=0
320 server
->rc
.m_stream_id
= pack
->m_nInfoField2
;
321 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 3), &av
);
322 server
->rc
.Link
.playpath
= av
;
326 /* check for duplicates */
327 for (fl
= server
->f_head
; fl
; fl
=fl
->f_next
)
329 if (AVMATCH(&av
, &fl
->f_path
))
332 /* strip trailing URL parameters */
333 q
= memchr(av
.av_val
, '?', av
.av_len
);
343 av
.av_len
= q
- av
.av_val
;
346 /* strip leading slash components */
347 for (p
=av
.av_val
+av
.av_len
-1; p
>=av
.av_val
; p
--)
351 av
.av_len
-= p
- av
.av_val
;
355 /* skip leading dot */
356 if (av
.av_val
[0] == '.')
362 /* hope there aren't more than 255 dups */
365 file
= malloc(flen
+1);
367 memcpy(file
, av
.av_val
, av
.av_len
);
369 sprintf(file
+av
.av_len
, "%02x", count
);
371 file
[av
.av_len
] = '\0';
372 for (p
=file
; *p
; p
++)
375 RTMP_LogPrintf("Playpath: %.*s\nSaving as: %s\n",
376 server
->rc
.Link
.playpath
.av_len
, server
->rc
.Link
.playpath
.av_val
,
378 out
= fopen(file
, "wb");
384 fwrite(flvHeader
, 1, sizeof(flvHeader
), out
);
385 av
= server
->rc
.Link
.playpath
;
386 fl
= malloc(sizeof(Flist
)+av
.av_len
+1);
388 fl
->f_path
.av_len
= av
.av_len
;
389 fl
->f_path
.av_val
= (char *)(fl
+1);
390 memcpy(fl
->f_path
.av_val
, av
.av_val
, av
.av_len
);
391 fl
->f_path
.av_val
[av
.av_len
] = '\0';
394 server
->f_tail
->f_next
= fl
;
400 else if (AVMATCH(&method
, &av_onStatus
))
404 AMFProp_GetObject(AMF_GetProp(&obj
, NULL
, 3), &obj2
);
405 AMFProp_GetString(AMF_GetProp(&obj2
, &av_code
, -1), &code
);
406 AMFProp_GetString(AMF_GetProp(&obj2
, &av_level
, -1), &level
);
408 RTMP_Log(RTMP_LOGDEBUG
, "%s, onStatus: %s", __FUNCTION__
, code
.av_val
);
409 if (AVMATCH(&code
, &av_NetStream_Failed
)
410 || AVMATCH(&code
, &av_NetStream_Play_Failed
)
411 || AVMATCH(&code
, &av_NetStream_Play_StreamNotFound
)
412 || AVMATCH(&code
, &av_NetConnection_Connect_InvalidApp
))
417 if (AVMATCH(&code
, &av_NetStream_Play_Start
))
419 /* set up the next stream */
422 if (server
->f_cur
->f_next
)
423 server
->f_cur
= server
->f_cur
->f_next
;
427 for (server
->f_cur
= server
->f_head
; server
->f_cur
&&
428 !server
->f_cur
->f_file
; server
->f_cur
= server
->f_cur
->f_next
) ;
430 server
->rc
.m_bPlaying
= TRUE
;
433 // Return 1 if this is a Play.Complete or Play.Stop
434 if (AVMATCH(&code
, &av_NetStream_Play_Complete
)
435 || AVMATCH(&code
, &av_NetStream_Play_Stop
))
440 else if (AVMATCH(&method
, &av_closeStream
))
444 else if (AVMATCH(&method
, &av_close
))
446 RTMP_Close(&server
->rc
);
455 ServePacket(STREAMING_SERVER
*server
, int which
, RTMPPacket
*packet
)
459 RTMP_Log(RTMP_LOGDEBUG
, "%s, %s sent packet type %02X, size %u bytes", __FUNCTION__
,
460 cst
[which
], packet
->m_packetType
, packet
->m_nBodySize
);
462 switch (packet
->m_packetType
)
464 case RTMP_PACKET_TYPE_CHUNK_SIZE
:
466 // HandleChangeChunkSize(r, packet);
469 case RTMP_PACKET_TYPE_BYTES_READ_REPORT
:
473 case RTMP_PACKET_TYPE_CONTROL
:
475 // HandleCtrl(r, packet);
478 case RTMP_PACKET_TYPE_SERVER_BW
:
480 // HandleServerBW(r, packet);
483 case RTMP_PACKET_TYPE_CLIENT_BW
:
485 // HandleClientBW(r, packet);
488 case RTMP_PACKET_TYPE_AUDIO
:
490 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
493 case RTMP_PACKET_TYPE_VIDEO
:
495 //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
498 case RTMP_PACKET_TYPE_FLEX_STREAM_SEND
:
502 case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT
:
503 // flex shared object
506 case RTMP_PACKET_TYPE_FLEX_MESSAGE
:
509 ret
= ServeInvoke(server
, which
, packet
, packet
->m_body
+ 1);
512 case RTMP_PACKET_TYPE_INFO
:
516 case RTMP_PACKET_TYPE_SHARED_OBJECT
:
520 case RTMP_PACKET_TYPE_INVOKE
:
522 ret
= ServeInvoke(server
, which
, packet
, packet
->m_body
);
525 case RTMP_PACKET_TYPE_FLASH_VIDEO
:
530 RTMP_Log(RTMP_LOGDEBUG
, "%s, unknown packet type received: 0x%02x", __FUNCTION__
,
531 packet
->m_packetType
);
533 RTMP_LogHex(RTMP_LOGDEBUG
, packet
->m_body
, packet
->m_nBodySize
);
540 WriteStream(char **buf
, // target pointer, maybe preallocated
541 unsigned int *plen
, // length of buffer if preallocated
542 uint32_t *nTimeStamp
,
545 uint32_t prevTagSize
= 0;
546 int ret
= -1, len
= *plen
;
550 char *packetBody
= packet
->m_body
;
551 unsigned int nPacketLen
= packet
->m_nBodySize
;
553 // skip video info/command packets
554 if (packet
->m_packetType
== RTMP_PACKET_TYPE_VIDEO
&&
555 nPacketLen
== 2 && ((*packetBody
& 0xf0) == 0x50))
561 if (packet
->m_packetType
== RTMP_PACKET_TYPE_VIDEO
&& nPacketLen
<= 5)
563 RTMP_Log(RTMP_LOGWARNING
, "ignoring too small video packet: size: %d",
568 if (packet
->m_packetType
== RTMP_PACKET_TYPE_AUDIO
&& nPacketLen
<= 1)
570 RTMP_Log(RTMP_LOGWARNING
, "ignoring too small audio packet: size: %d",
576 RTMP_Log(RTMP_LOGDEBUG
, "type: %02X, size: %d, TS: %d ms", packet
->m_packetType
,
577 nPacketLen
, packet
->m_nTimeStamp
);
578 if (packet
->m_packetType
== RTMP_PACKET_TYPE_VIDEO
)
579 RTMP_Log(RTMP_LOGDEBUG
, "frametype: %02X", (*packetBody
& 0xf0));
582 // calculate packet size and reallocate buffer if necessary
583 unsigned int size
= nPacketLen
585 ((packet
->m_packetType
== RTMP_PACKET_TYPE_AUDIO
586 || packet
->m_packetType
== RTMP_PACKET_TYPE_VIDEO
587 || packet
->m_packetType
== RTMP_PACKET_TYPE_INFO
) ? 11 : 0)
588 + (packet
->m_packetType
!= 0x16 ? 4 : 0);
592 /* The extra 4 is for the case of an FLV stream without a last
593 * prevTagSize (we need extra 4 bytes to append it). */
594 *buf
= (char *) realloc(*buf
, size
+ 4);
597 RTMP_Log(RTMP_LOGERROR
, "Couldn't reallocate memory!");
598 ret
= -1; // fatal error
602 char *ptr
= *buf
, *pend
= ptr
+ size
+4;
604 /* audio (RTMP_PACKET_TYPE_AUDIO), video (RTMP_PACKET_TYPE_VIDEO)
605 * or metadata (RTMP_PACKET_TYPE_INFO) packets: construct 11 byte
606 * header then add rtmp packet's data. */
607 if (packet
->m_packetType
== RTMP_PACKET_TYPE_AUDIO
608 || packet
->m_packetType
== RTMP_PACKET_TYPE_VIDEO
609 || packet
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
612 //*dataType |= (((packet->m_packetType == RTMP_PACKET_TYPE_AUDIO)<<2)|(packet->m_packetType == RTMP_PACKET_TYPE_VIDEO));
614 (*nTimeStamp
) = packet
->m_nTimeStamp
;
615 prevTagSize
= 11 + nPacketLen
;
617 *ptr
++ = packet
->m_packetType
;
618 ptr
= AMF_EncodeInt24(ptr
, pend
, nPacketLen
);
619 ptr
= AMF_EncodeInt24(ptr
, pend
, *nTimeStamp
);
620 *ptr
= (char) (((*nTimeStamp
) & 0xFF000000) >> 24);
624 ptr
= AMF_EncodeInt24(ptr
, pend
, 0);
627 memcpy(ptr
, packetBody
, nPacketLen
);
628 unsigned int len
= nPacketLen
;
630 // correct tagSize and obtain timestamp if we have an FLV stream
631 if (packet
->m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
633 unsigned int pos
= 0;
635 while (pos
+ 11 < nPacketLen
)
637 uint32_t dataSize
= AMF_DecodeInt24(packetBody
+ pos
+ 1); // size without header (11) and without prevTagSize (4)
638 *nTimeStamp
= AMF_DecodeInt24(packetBody
+ pos
+ 4);
639 *nTimeStamp
|= (packetBody
[pos
+ 7] << 24);
643 *dataType
|= (((*(packetBody
+pos
) == RTMP_PACKET_TYPE_AUDIO
) << 2)
644 | (*(packetBody
+pos
) == RTMP_PACKET_TYPE_VIDEO
));
647 if (pos
+ 11 + dataSize
+ 4 > nPacketLen
)
649 if (pos
+ 11 + dataSize
> nPacketLen
)
651 RTMP_Log(RTMP_LOGERROR
,
652 "Wrong data size (%u), stream corrupted, aborting!",
657 RTMP_Log(RTMP_LOGWARNING
, "No tagSize found, appending!");
659 // we have to append a last tagSize!
660 prevTagSize
= dataSize
+ 11;
661 AMF_EncodeInt32(ptr
+ pos
+ 11 + dataSize
, pend
, prevTagSize
);
668 AMF_DecodeInt32(packetBody
+ pos
+ 11 + dataSize
);
671 RTMP_Log(RTMP_LOGDEBUG
,
672 "FLV Packet: type %02X, dataSize: %lu, tagSize: %lu, timeStamp: %lu ms",
673 (unsigned char) packetBody
[pos
], dataSize
, prevTagSize
,
677 if (prevTagSize
!= (dataSize
+ 11))
680 RTMP_Log(RTMP_LOGWARNING
,
681 "Tag and data size are not consitent, writing tag size according to dataSize+11: %d",
685 prevTagSize
= dataSize
+ 11;
686 AMF_EncodeInt32(ptr
+ pos
+ 11 + dataSize
, pend
, prevTagSize
);
690 pos
+= prevTagSize
+ 4; //(11+dataSize+4);
695 if (packet
->m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
696 { // FLV tag packets contain their own prevTagSize
697 AMF_EncodeInt32(ptr
, pend
, prevTagSize
);
708 return ret
; // no more media packets
712 controlServerThread(void *unused
)
721 RTMP_LogPrintf("Exiting\n");
722 stopStreaming(rtmpServer
);
727 RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich
);
733 TFTYPE
doServe(void *arg
) // server socket and state (our listening socket)
735 STREAMING_SERVER
*server
= arg
;
736 RTMPPacket pc
= { 0 }, ps
= { 0 };
737 RTMPChunk rk
= { 0 };
739 unsigned int buflen
= 131072;
741 int sockfd
= server
->socket
;
743 // timeout for http requests
747 server
->state
= STREAMING_IN_PROGRESS
;
749 memset(&tv
, 0, sizeof(struct timeval
));
753 FD_SET(sockfd
, &rfds
);
755 if (select(sockfd
+ 1, &rfds
, NULL
, NULL
, &tv
) <= 0)
757 RTMP_Log(RTMP_LOGERROR
, "Request timeout/select failed, ignoring request");
762 RTMP_Init(&server
->rs
);
763 RTMP_Init(&server
->rc
);
764 server
->rs
.m_sb
.sb_socket
= sockfd
;
765 if (!RTMP_Serve(&server
->rs
))
767 RTMP_Log(RTMP_LOGERROR
, "Handshake failed");
772 buf
= malloc(buflen
);
774 /* Just process the Connect request */
775 while (RTMP_IsConnected(&server
->rs
) && RTMP_ReadPacket(&server
->rs
, &ps
))
777 if (!RTMPPacket_IsReady(&ps
))
779 ServePacket(server
, 0, &ps
);
780 RTMPPacket_Free(&ps
);
781 if (RTMP_IsConnected(&server
->rc
))
787 /* We have our own timeout in select() */
788 server
->rc
.Link
.timeout
= 10;
789 server
->rs
.Link
.timeout
= 10;
790 while (RTMP_IsConnected(&server
->rs
) || RTMP_IsConnected(&server
->rc
))
795 cr
= server
->rc
.m_sb
.sb_size
;
796 sr
= server
->rs
.m_sb
.sb_size
;
803 n
= server
->rs
.m_sb
.sb_socket
;
804 if (server
->rc
.m_sb
.sb_socket
> n
)
805 n
= server
->rc
.m_sb
.sb_socket
;
807 if (RTMP_IsConnected(&server
->rs
))
808 FD_SET(sockfd
, &rfds
);
809 if (RTMP_IsConnected(&server
->rc
))
810 FD_SET(server
->rc
.m_sb
.sb_socket
, &rfds
);
812 /* give more time to start up if we're not playing yet */
813 tv
.tv_sec
= server
->f_cur
? 30 : 60;
816 if (select(n
+ 1, &rfds
, NULL
, NULL
, &tv
) <= 0)
818 if (server
->f_cur
&& server
->rc
.m_mediaChannel
&& !paused
)
820 server
->rc
.m_pauseStamp
= server
->rc
.m_channelTimestamp
[server
->rc
.m_mediaChannel
];
821 if (RTMP_ToggleStream(&server
->rc
))
827 RTMP_Log(RTMP_LOGERROR
, "Request timeout/select failed, ignoring request");
830 if (server
->rs
.m_sb
.sb_socket
> 0 &&
831 FD_ISSET(server
->rs
.m_sb
.sb_socket
, &rfds
))
833 if (server
->rc
.m_sb
.sb_socket
> 0 &&
834 FD_ISSET(server
->rc
.m_sb
.sb_socket
, &rfds
))
839 while (RTMP_ReadPacket(&server
->rs
, &ps
))
840 if (RTMPPacket_IsReady(&ps
))
842 /* change chunk size */
843 if (ps
.m_packetType
== RTMP_PACKET_TYPE_CHUNK_SIZE
)
845 if (ps
.m_nBodySize
>= 4)
847 server
->rs
.m_inChunkSize
= AMF_DecodeInt32(ps
.m_body
);
848 RTMP_Log(RTMP_LOGDEBUG
, "%s, client: chunk size change to %d", __FUNCTION__
,
849 server
->rs
.m_inChunkSize
);
850 server
->rc
.m_outChunkSize
= server
->rs
.m_inChunkSize
;
854 else if (ps
.m_packetType
== RTMP_PACKET_TYPE_BYTES_READ_REPORT
)
856 if (ps
.m_nBodySize
>= 4)
858 int count
= AMF_DecodeInt32(ps
.m_body
);
859 RTMP_Log(RTMP_LOGDEBUG
, "%s, client: bytes received = %d", __FUNCTION__
,
864 else if (ps
.m_packetType
== RTMP_PACKET_TYPE_CONTROL
)
866 short nType
= AMF_DecodeInt16(ps
.m_body
);
870 char *ptr
= ps
.m_body
+2;
873 id
= AMF_DecodeInt32(ptr
);
874 /* Assume the interesting media is on a non-zero stream */
877 len
= AMF_DecodeInt32(ptr
+4);
879 /* request a big buffer */
880 if (len
< BUFFERTIME
)
882 AMF_EncodeInt32(ptr
+4, ptr
+8, BUFFERTIME
);
885 RTMP_Log(RTMP_LOGDEBUG
, "%s, client: BufferTime change in stream %d to %d", __FUNCTION__
,
890 else if (ps
.m_packetType
== RTMP_PACKET_TYPE_FLEX_MESSAGE
891 || ps
.m_packetType
== RTMP_PACKET_TYPE_INVOKE
)
893 if (ServePacket(server
, 0, &ps
) && server
->f_cur
)
895 fclose(server
->f_cur
->f_file
);
896 server
->f_cur
->f_file
= NULL
;
897 server
->f_cur
= NULL
;
900 RTMP_SendPacket(&server
->rc
, &ps
, FALSE
);
901 RTMPPacket_Free(&ps
);
907 while (RTMP_ReadPacket(&server
->rc
, &pc
))
910 if (RTMPPacket_IsReady(&pc
))
914 if (pc
.m_nTimeStamp
<= server
->rc
.m_mediaStamp
)
917 server
->rc
.m_pausing
= 0;
919 /* change chunk size */
920 if (pc
.m_packetType
== RTMP_PACKET_TYPE_CHUNK_SIZE
)
922 if (pc
.m_nBodySize
>= 4)
924 server
->rc
.m_inChunkSize
= AMF_DecodeInt32(pc
.m_body
);
925 RTMP_Log(RTMP_LOGDEBUG
, "%s, server: chunk size change to %d", __FUNCTION__
,
926 server
->rc
.m_inChunkSize
);
927 server
->rs
.m_outChunkSize
= server
->rc
.m_inChunkSize
;
930 else if (pc
.m_packetType
== RTMP_PACKET_TYPE_CONTROL
)
932 short nType
= AMF_DecodeInt16(pc
.m_body
);
933 /* SWFverification */
936 if (server
->rc
.Link
.SWFSize
)
938 RTMP_SendCtrl(&server
->rc
, 0x1b, 0, 0);
942 /* The session will certainly fail right after this */
943 RTMP_Log(RTMP_LOGERROR
, "%s, server requested SWF verification, need CRYPTO support! ", __FUNCTION__
);
946 else if (server
->f_cur
&& (
947 pc
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
||
948 pc
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
||
949 pc
.m_packetType
== RTMP_PACKET_TYPE_INFO
||
950 pc
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
) &&
951 RTMP_ClientPacket(&server
->rc
, &pc
))
953 int len
= WriteStream(&buf
, &buflen
, &server
->stamp
, &pc
);
954 if (len
> 0 && fwrite(buf
, 1, len
, server
->f_cur
->f_file
) != len
)
957 else if (pc
.m_packetType
== RTMP_PACKET_TYPE_FLEX_MESSAGE
||
958 pc
.m_packetType
== RTMP_PACKET_TYPE_INVOKE
)
960 if (ServePacket(server
, 1, &pc
) && server
->f_cur
)
962 fclose(server
->f_cur
->f_file
);
963 server
->f_cur
->f_file
= NULL
;
964 server
->f_cur
= NULL
;
968 if (sendit
&& RTMP_IsConnected(&server
->rs
))
969 RTMP_SendChunk(&server
->rs
, &rk
);
970 if (RTMPPacket_IsReady(&pc
))
971 RTMPPacket_Free(&pc
);
975 if (!RTMP_IsConnected(&server
->rs
) && RTMP_IsConnected(&server
->rc
)
977 RTMP_Close(&server
->rc
);
981 RTMP_LogPrintf("Closing connection... ");
982 RTMP_Close(&server
->rs
);
983 RTMP_Close(&server
->rc
);
984 while (server
->f_head
)
986 Flist
*fl
= server
->f_head
;
987 server
->f_head
= fl
->f_next
;
992 server
->f_tail
= NULL
;
993 server
->f_cur
= NULL
;
995 /* Should probably be done by RTMP_Close() ... */
996 server
->rc
.Link
.hostname
.av_val
= NULL
;
997 server
->rc
.Link
.tcUrl
.av_val
= NULL
;
998 server
->rc
.Link
.swfUrl
.av_val
= NULL
;
999 server
->rc
.Link
.pageUrl
.av_val
= NULL
;
1000 server
->rc
.Link
.app
.av_val
= NULL
;
1001 server
->rc
.Link
.auth
.av_val
= NULL
;
1002 server
->rc
.Link
.flashVer
.av_val
= NULL
;
1003 RTMP_LogPrintf("done!\n\n");
1006 if (server
->state
== STREAMING_IN_PROGRESS
)
1007 server
->state
= STREAMING_ACCEPTING
;
1013 serverThread(void *arg
)
1015 STREAMING_SERVER
*server
= arg
;
1016 server
->state
= STREAMING_ACCEPTING
;
1018 while (server
->state
== STREAMING_ACCEPTING
)
1020 struct sockaddr_in addr
;
1021 socklen_t addrlen
= sizeof(struct sockaddr_in
);
1022 STREAMING_SERVER
*srv2
= malloc(sizeof(STREAMING_SERVER
));
1024 accept(server
->socket
, (struct sockaddr
*) &addr
, &addrlen
);
1029 struct sockaddr_in dest
;
1031 socklen_t destlen
= sizeof(struct sockaddr_in
);
1032 getsockopt(sockfd
, SOL_IP
, SO_ORIGINAL_DST
, &dest
, &destlen
);
1033 strcpy(destch
, inet_ntoa(dest
.sin_addr
));
1034 RTMP_Log(RTMP_LOGDEBUG
, "%s: accepted connection from %s to %s\n", __FUNCTION__
,
1035 inet_ntoa(addr
.sin_addr
), destch
);
1037 RTMP_Log(RTMP_LOGDEBUG
, "%s: accepted connection from %s\n", __FUNCTION__
,
1038 inet_ntoa(addr
.sin_addr
));
1041 srv2
->socket
= sockfd
;
1042 /* Create a new thread and transfer the control to that */
1043 ThreadCreate(doServe
, srv2
);
1044 RTMP_Log(RTMP_LOGDEBUG
, "%s: processed request\n", __FUNCTION__
);
1048 RTMP_Log(RTMP_LOGERROR
, "%s: accept failed", __FUNCTION__
);
1051 server
->state
= STREAMING_STOPPED
;
1056 startStreaming(const char *address
, int port
)
1058 struct sockaddr_in addr
;
1060 STREAMING_SERVER
*server
;
1062 sockfd
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
1065 RTMP_Log(RTMP_LOGERROR
, "%s, couldn't create socket", __FUNCTION__
);
1070 setsockopt(sockfd
, SOL_SOCKET
, SO_REUSEADDR
,
1071 (char *) &tmp
, sizeof(tmp
) );
1073 addr
.sin_family
= AF_INET
;
1074 addr
.sin_addr
.s_addr
= inet_addr(address
); //htonl(INADDR_ANY);
1075 addr
.sin_port
= htons(port
);
1077 if (bind(sockfd
, (struct sockaddr
*) &addr
, sizeof(struct sockaddr_in
)) ==
1080 RTMP_Log(RTMP_LOGERROR
, "%s, TCP bind failed for port number: %d", __FUNCTION__
,
1085 if (listen(sockfd
, 10) == -1)
1087 RTMP_Log(RTMP_LOGERROR
, "%s, listen failed", __FUNCTION__
);
1088 closesocket(sockfd
);
1092 server
= (STREAMING_SERVER
*) calloc(1, sizeof(STREAMING_SERVER
));
1093 server
->socket
= sockfd
;
1095 ThreadCreate(serverThread
, server
);
1101 stopStreaming(STREAMING_SERVER
* server
)
1105 if (server
->state
!= STREAMING_STOPPED
)
1107 int fd
= server
->socket
;
1109 if (server
->state
== STREAMING_IN_PROGRESS
)
1111 server
->state
= STREAMING_STOPPING
;
1113 // wait for streaming threads to exit
1114 while (server
->state
!= STREAMING_STOPPED
)
1118 if (fd
&& closesocket(fd
))
1119 RTMP_Log(RTMP_LOGERROR
, "%s: Failed to close listening socket, error %d",
1120 __FUNCTION__
, GetSockError());
1122 server
->state
= STREAMING_STOPPED
;
1128 sigIntHandler(int sig
)
1131 RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig
);
1133 stopStreaming(rtmpServer
);
1134 signal(SIGINT
, SIG_DFL
);
1138 main(int argc
, char **argv
)
1140 int nStatus
= RD_SUCCESS
;
1142 // rtmp streaming server
1143 char DEFAULT_RTMP_STREAMING_DEVICE
[] = "0.0.0.0"; // 0.0.0.0 is any device
1145 char *rtmpStreamingDevice
= DEFAULT_RTMP_STREAMING_DEVICE
; // streaming device, default 0.0.0.0
1146 int nRtmpStreamingPort
= 1935; // port
1148 RTMP_LogPrintf("RTMP Proxy Server %s\n", RTMPDUMP_VERSION
);
1149 RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n");
1151 RTMP_debuglevel
= RTMP_LOGINFO
;
1153 if (argc
> 1 && !strcmp(argv
[1], "-z"))
1154 RTMP_debuglevel
= RTMP_LOGALL
;
1156 signal(SIGINT
, sigIntHandler
);
1158 signal(SIGPIPE
, SIG_IGN
);
1162 netstackdump
= fopen("netstackdump", "wb");
1163 netstackdump_read
= fopen("netstackdump_read", "wb");
1169 ThreadCreate(controlServerThread
, 0);
1171 // start http streaming
1173 startStreaming(rtmpStreamingDevice
, nRtmpStreamingPort
)) == 0)
1175 RTMP_Log(RTMP_LOGERROR
, "Failed to start RTMP server, exiting!");
1178 RTMP_LogPrintf("Streaming on rtmp://%s:%d\n", rtmpStreamingDevice
,
1179 nRtmpStreamingPort
);
1181 while (rtmpServer
->state
!= STREAMING_STOPPED
)
1185 RTMP_Log(RTMP_LOGDEBUG
, "Done, exiting...");
1192 if (netstackdump
!= 0)
1193 fclose(netstackdump
);
1194 if (netstackdump_read
!= 0)
1195 fclose(netstackdump_read
);