2 * Copyright (C) 2005-2008 Team XBMC
4 * Copyright (C) 2008-2009 Andrej Stepanchuk
5 * Copyright (C) 2009-2010 Howard Chu
7 * This file is part of librtmp.
9 * librtmp is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation; either version 2.1,
12 * or (at your option) any later version.
14 * librtmp is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with librtmp see the file COPYING. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 * http://www.gnu.org/copyleft/lgpl.html
36 #include <polarssl/havege.h>
37 #elif defined(USE_GNUTLS)
38 #include <gnutls/gnutls.h>
39 #else /* USE_OPENSSL */
40 #include <openssl/ssl.h>
41 #include <openssl/rc4.h>
46 #define RTMP_SIG_SIZE 1536
47 #define RTMP_LARGE_HEADER_SIZE 12
49 static const int packetSize
[] = { 12, 8, 4, 1 };
53 const char RTMPProtocolStrings
[][7] = {
65 const char RTMPProtocolStringsLower
[][7] = {
77 static const char *RTMPT_cmds
[] = {
85 RTMPT_OPEN
=0, RTMPT_SEND
, RTMPT_IDLE
, RTMPT_CLOSE
88 static int DumpMetaData(AMFObject
*obj
);
89 static int HandShake(RTMP
*r
, int FP9HandShake
);
90 static int SocksNegotiate(RTMP
*r
);
92 static int SendConnectPacket(RTMP
*r
, RTMPPacket
*cp
);
93 static int SendCheckBW(RTMP
*r
);
94 static int SendCheckBWResult(RTMP
*r
, double txn
);
95 static int SendDeleteStream(RTMP
*r
, double dStreamId
);
96 static int SendFCSubscribe(RTMP
*r
, AVal
*subscribepath
);
97 static int SendPlay(RTMP
*r
);
98 static int SendBytesReceived(RTMP
*r
);
99 static int SendUsherToken(RTMP
*r
, AVal
*usherToken
);
102 static int SendBGHasStream(RTMP
*r
, double dId
, AVal
*playpath
);
105 static int HandleInvoke(RTMP
*r
, const char *body
, unsigned int nBodySize
);
106 static int HandleMetadata(RTMP
*r
, char *body
, unsigned int len
);
107 static void HandleChangeChunkSize(RTMP
*r
, const RTMPPacket
*packet
);
108 static void HandleAudio(RTMP
*r
, const RTMPPacket
*packet
);
109 static void HandleVideo(RTMP
*r
, const RTMPPacket
*packet
);
110 static void HandleCtrl(RTMP
*r
, const RTMPPacket
*packet
);
111 static void HandleServerBW(RTMP
*r
, const RTMPPacket
*packet
);
112 static void HandleClientBW(RTMP
*r
, const RTMPPacket
*packet
);
114 static int ReadN(RTMP
*r
, char *buffer
, int n
);
115 static int WriteN(RTMP
*r
, const char *buffer
, int n
);
117 static void DecodeTEA(AVal
*key
, AVal
*text
);
119 static int HTTP_Post(RTMP
*r
, RTMPTCmd cmd
, const char *buf
, int len
);
120 static int HTTP_read(RTMP
*r
, int fill
);
127 #include "handshake.h"
135 #elif defined(_WIN32)
136 return timeGetTime();
139 if (!clk_tck
) clk_tck
= sysconf(_SC_CLK_TCK
);
140 return times(&t
) * 1000 / clk_tck
;
151 RTMPPacket_Reset(RTMPPacket
*p
)
157 p
->m_nInfoField2
= 0;
158 p
->m_hasAbsTimestamp
= FALSE
;
164 RTMPPacket_Alloc(RTMPPacket
*p
, int nSize
)
166 char *ptr
= calloc(1, nSize
+ RTMP_MAX_HEADER_SIZE
);
169 p
->m_body
= ptr
+ RTMP_MAX_HEADER_SIZE
;
175 RTMPPacket_Free(RTMPPacket
*p
)
179 free(p
->m_body
- RTMP_MAX_HEADER_SIZE
);
185 RTMPPacket_Dump(RTMPPacket
*p
)
187 RTMP_Log(RTMP_LOGDEBUG
,
188 "RTMP PACKET: packet type: 0x%02x. channel: 0x%02x. info 1: %d info 2: %d. Body size: %lu. body: 0x%02x",
189 p
->m_packetType
, p
->m_nChannel
, p
->m_nTimeStamp
, p
->m_nInfoField2
,
190 p
->m_nBodySize
, p
->m_body
? (unsigned char)p
->m_body
[0] : 0);
196 return RTMP_LIB_VERSION
;
204 /* Do this regardless of NO_SSL, we use havege for rtmpe too */
205 RTMP_TLS_ctx
= calloc(1,sizeof(struct tls_ctx
));
206 havege_init(&RTMP_TLS_ctx
->hs
);
207 #elif defined(USE_GNUTLS) && !defined(NO_SSL)
208 /* Technically we need to initialize libgcrypt ourselves if
209 * we're not going to call gnutls_global_init(). Ignoring this
212 gnutls_global_init();
213 RTMP_TLS_ctx
= malloc(sizeof(struct tls_ctx
));
214 gnutls_certificate_allocate_credentials(&RTMP_TLS_ctx
->cred
);
215 gnutls_priority_init(&RTMP_TLS_ctx
->prios
, "NORMAL", NULL
);
216 gnutls_certificate_set_x509_trust_file(RTMP_TLS_ctx
->cred
,
217 "ca.pem", GNUTLS_X509_FMT_PEM
);
218 #elif !defined(NO_SSL) /* USE_OPENSSL */
219 /* libcrypto doesn't need anything special */
220 SSL_load_error_strings();
222 OpenSSL_add_all_digests();
223 RTMP_TLS_ctx
= SSL_CTX_new(SSLv23_method());
224 SSL_CTX_set_options(RTMP_TLS_ctx
, SSL_OP_ALL
);
225 SSL_CTX_set_default_verify_paths(RTMP_TLS_ctx
);
233 return calloc(1, sizeof(RTMP
));
250 memset(r
, 0, sizeof(RTMP
));
251 r
->m_sb
.sb_socket
= -1;
252 r
->m_inChunkSize
= RTMP_DEFAULT_CHUNKSIZE
;
253 r
->m_outChunkSize
= RTMP_DEFAULT_CHUNKSIZE
;
254 r
->m_nBufferMS
= 30000;
255 r
->m_nClientBW
= 2500000;
257 r
->m_nServerBW
= 2500000;
258 r
->m_fAudioCodecs
= 3191.0;
259 r
->m_fVideoCodecs
= 252.0;
260 r
->Link
.timeout
= 30;
265 RTMP_EnableWrite(RTMP
*r
)
267 r
->Link
.protocol
|= RTMP_FEATURE_WRITE
;
271 RTMP_GetDuration(RTMP
*r
)
273 return r
->m_fDuration
;
277 RTMP_IsConnected(RTMP
*r
)
279 return r
->m_sb
.sb_socket
!= -1;
285 return r
->m_sb
.sb_socket
;
289 RTMP_IsTimedout(RTMP
*r
)
291 return r
->m_sb
.sb_timedout
;
295 RTMP_SetBufferMS(RTMP
*r
, int size
)
297 r
->m_nBufferMS
= size
;
301 RTMP_UpdateBufferMS(RTMP
*r
)
303 RTMP_SendCtrl(r
, 3, r
->m_stream_id
, r
->m_nBufferMS
);
309 #elif defined(__sun__)
311 #elif defined(__APPLE__)
313 #elif defined(__linux__)
318 #define DEF_VERSTR OSS " 10,0,32,18"
319 static const char DEFAULT_FLASH_VER
[] = DEF_VERSTR
;
320 const AVal RTMP_DefaultFlashVer
=
321 { (char *)DEFAULT_FLASH_VER
, sizeof(DEFAULT_FLASH_VER
) - 1 };
324 RTMP_SetupStream(RTMP
*r
,
341 int dStop
, int bLiveStream
, long int timeout
)
343 RTMP_Log(RTMP_LOGDEBUG
, "Protocol : %s", RTMPProtocolStrings
[protocol
&7]);
344 RTMP_Log(RTMP_LOGDEBUG
, "Hostname : %.*s", host
->av_len
, host
->av_val
);
345 RTMP_Log(RTMP_LOGDEBUG
, "Port : %d", port
);
346 RTMP_Log(RTMP_LOGDEBUG
, "Playpath : %s", playpath
->av_val
);
348 if (tcUrl
&& tcUrl
->av_val
)
349 RTMP_Log(RTMP_LOGDEBUG
, "tcUrl : %s", tcUrl
->av_val
);
350 if (swfUrl
&& swfUrl
->av_val
)
351 RTMP_Log(RTMP_LOGDEBUG
, "swfUrl : %s", swfUrl
->av_val
);
352 if (pageUrl
&& pageUrl
->av_val
)
353 RTMP_Log(RTMP_LOGDEBUG
, "pageUrl : %s", pageUrl
->av_val
);
354 if (app
&& app
->av_val
)
355 RTMP_Log(RTMP_LOGDEBUG
, "app : %.*s", app
->av_len
, app
->av_val
);
356 if (auth
&& auth
->av_val
)
357 RTMP_Log(RTMP_LOGDEBUG
, "auth : %s", auth
->av_val
);
358 if (subscribepath
&& subscribepath
->av_val
)
359 RTMP_Log(RTMP_LOGDEBUG
, "subscribepath : %s", subscribepath
->av_val
);
360 if (usherToken
&& usherToken
->av_val
)
361 RTMP_Log(RTMP_LOGDEBUG
, "NetStream.Authenticate.UsherToken : %s", usherToken
->av_val
);
362 if (flashVer
&& flashVer
->av_val
)
363 RTMP_Log(RTMP_LOGDEBUG
, "flashVer : %s", flashVer
->av_val
);
365 RTMP_Log(RTMP_LOGDEBUG
, "StartTime : %d msec", dStart
);
367 RTMP_Log(RTMP_LOGDEBUG
, "StopTime : %d msec", dStop
);
369 RTMP_Log(RTMP_LOGDEBUG
, "live : %s", bLiveStream
? "yes" : "no");
370 RTMP_Log(RTMP_LOGDEBUG
, "timeout : %d sec", timeout
);
373 if (swfSHA256Hash
!= NULL
&& swfSize
> 0)
375 memcpy(r
->Link
.SWFHash
, swfSHA256Hash
->av_val
, sizeof(r
->Link
.SWFHash
));
376 r
->Link
.SWFSize
= swfSize
;
377 RTMP_Log(RTMP_LOGDEBUG
, "SWFSHA256:");
378 RTMP_LogHex(RTMP_LOGDEBUG
, r
->Link
.SWFHash
, sizeof(r
->Link
.SWFHash
));
379 RTMP_Log(RTMP_LOGDEBUG
, "SWFSize : %lu", r
->Link
.SWFSize
);
387 if (sockshost
->av_len
)
389 const char *socksport
= strchr(sockshost
->av_val
, ':');
390 char *hostname
= strdup(sockshost
->av_val
);
393 hostname
[socksport
- sockshost
->av_val
] = '\0';
394 r
->Link
.sockshost
.av_val
= hostname
;
395 r
->Link
.sockshost
.av_len
= strlen(hostname
);
397 r
->Link
.socksport
= socksport
? atoi(socksport
+ 1) : 1080;
398 RTMP_Log(RTMP_LOGDEBUG
, "Connecting via SOCKS proxy: %s:%d", r
->Link
.sockshost
.av_val
,
403 r
->Link
.sockshost
.av_val
= NULL
;
404 r
->Link
.sockshost
.av_len
= 0;
405 r
->Link
.socksport
= 0;
408 if (tcUrl
&& tcUrl
->av_len
)
409 r
->Link
.tcUrl
= *tcUrl
;
410 if (swfUrl
&& swfUrl
->av_len
)
411 r
->Link
.swfUrl
= *swfUrl
;
412 if (pageUrl
&& pageUrl
->av_len
)
413 r
->Link
.pageUrl
= *pageUrl
;
414 if (app
&& app
->av_len
)
416 if (auth
&& auth
->av_len
)
418 r
->Link
.auth
= *auth
;
419 r
->Link
.lFlags
|= RTMP_LF_AUTH
;
421 if (flashVer
&& flashVer
->av_len
)
422 r
->Link
.flashVer
= *flashVer
;
424 r
->Link
.flashVer
= RTMP_DefaultFlashVer
;
425 if (subscribepath
&& subscribepath
->av_len
)
426 r
->Link
.subscribepath
= *subscribepath
;
427 if (usherToken
&& usherToken
->av_len
)
428 r
->Link
.usherToken
= *usherToken
;
429 r
->Link
.seekTime
= dStart
;
430 r
->Link
.stopTime
= dStop
;
432 r
->Link
.lFlags
|= RTMP_LF_LIVE
;
433 r
->Link
.timeout
= timeout
;
435 r
->Link
.protocol
= protocol
;
436 r
->Link
.hostname
= *host
;
438 r
->Link
.playpath
= *playpath
;
440 if (r
->Link
.port
== 0)
442 if (protocol
& RTMP_FEATURE_SSL
)
444 else if (protocol
& RTMP_FEATURE_HTTP
)
451 enum { OPT_STR
=0, OPT_INT
, OPT_BOOL
, OPT_CONN
};
452 static const char *optinfo
[] = {
453 "string", "integer", "boolean", "AMF" };
455 #define OFF(x) offsetof(struct RTMP,x)
457 static struct urlopt
{
464 { AVC("socks"), OFF(Link
.sockshost
), OPT_STR
, 0,
465 "Use the specified SOCKS proxy" },
466 { AVC("app"), OFF(Link
.app
), OPT_STR
, 0,
467 "Name of target app on server" },
468 { AVC("tcUrl"), OFF(Link
.tcUrl
), OPT_STR
, 0,
469 "URL to played stream" },
470 { AVC("pageUrl"), OFF(Link
.pageUrl
), OPT_STR
, 0,
471 "URL of played media's web page" },
472 { AVC("swfUrl"), OFF(Link
.swfUrl
), OPT_STR
, 0,
473 "URL to player SWF file" },
474 { AVC("flashver"), OFF(Link
.flashVer
), OPT_STR
, 0,
475 "Flash version string (default " DEF_VERSTR
")" },
476 { AVC("conn"), OFF(Link
.extras
), OPT_CONN
, 0,
477 "Append arbitrary AMF data to Connect message" },
478 { AVC("playpath"), OFF(Link
.playpath
), OPT_STR
, 0,
479 "Path to target media on server" },
480 { AVC("playlist"), OFF(Link
.lFlags
), OPT_BOOL
, RTMP_LF_PLST
,
481 "Set playlist before play command" },
482 { AVC("live"), OFF(Link
.lFlags
), OPT_BOOL
, RTMP_LF_LIVE
,
483 "Stream is live, no seeking possible" },
484 { AVC("subscribe"), OFF(Link
.subscribepath
), OPT_STR
, 0,
485 "Stream to subscribe to" },
486 { AVC("jtv"), OFF(Link
.usherToken
), OPT_STR
, 0,
487 "Justin.tv authentication token" },
488 { AVC("token"), OFF(Link
.token
), OPT_STR
, 0,
489 "Key for SecureToken response" },
490 { AVC("swfVfy"), OFF(Link
.lFlags
), OPT_BOOL
, RTMP_LF_SWFV
,
491 "Perform SWF Verification" },
492 { AVC("swfAge"), OFF(Link
.swfAge
), OPT_INT
, 0,
493 "Number of days to use cached SWF hash" },
494 { AVC("start"), OFF(Link
.seekTime
), OPT_INT
, 0,
495 "Stream start position in milliseconds" },
496 { AVC("stop"), OFF(Link
.stopTime
), OPT_INT
, 0,
497 "Stream stop position in milliseconds" },
498 { AVC("buffer"), OFF(m_nBufferMS
), OPT_INT
, 0,
499 "Buffer time in milliseconds" },
500 { AVC("timeout"), OFF(Link
.timeout
), OPT_INT
, 0,
501 "Session timeout in seconds" },
505 static const AVal truth
[] = {
513 static void RTMP_OptUsage()
517 RTMP_Log(RTMP_LOGERROR
, "Valid RTMP options are:\n");
518 for (i
=0; options
[i
].name
.av_len
; i
++) {
519 RTMP_Log(RTMP_LOGERROR
, "%10s %-7s %s\n", options
[i
].name
.av_val
,
520 optinfo
[options
[i
].otype
], options
[i
].use
);
525 parseAMF(AMFObject
*obj
, AVal
*av
, int *depth
)
527 AMFObjectProperty prop
= {{0,0}};
529 char *p
, *arg
= av
->av_val
;
537 prop
.p_type
= AMF_BOOLEAN
;
538 prop
.p_vu
.p_number
= atoi(p
);
541 prop
.p_type
= AMF_STRING
;
542 prop
.p_vu
.p_aval
.av_val
= p
;
543 prop
.p_vu
.p_aval
.av_len
= av
->av_len
- (p
-arg
);
546 prop
.p_type
= AMF_NUMBER
;
547 prop
.p_vu
.p_number
= strtod(p
, NULL
);
550 prop
.p_type
= AMF_NULL
;
556 prop
.p_type
= AMF_OBJECT
;
568 else if (arg
[2] == ':' && arg
[0] == 'N')
570 p
= strchr(arg
+3, ':');
573 prop
.p_name
.av_val
= (char *)arg
+3;
574 prop
.p_name
.av_len
= p
- (arg
+3);
580 prop
.p_type
= AMF_BOOLEAN
;
581 prop
.p_vu
.p_number
= atoi(p
);
584 prop
.p_type
= AMF_STRING
;
585 prop
.p_vu
.p_aval
.av_val
= p
;
586 prop
.p_vu
.p_aval
.av_len
= av
->av_len
- (p
-arg
);
589 prop
.p_type
= AMF_NUMBER
;
590 prop
.p_vu
.p_number
= strtod(p
, NULL
);
593 prop
.p_type
= AMF_OBJECT
;
605 for (i
=0; i
<*depth
; i
++)
607 o2
= &obj
->o_props
[obj
->o_num
-1].p_vu
.p_object
;
611 AMF_AddProp(obj
, &prop
);
612 if (prop
.p_type
== AMF_OBJECT
)
617 int RTMP_SetOpt(RTMP
*r
, const AVal
*opt
, AVal
*arg
)
622 for (i
=0; options
[i
].name
.av_len
; i
++) {
623 if (opt
->av_len
!= options
[i
].name
.av_len
) continue;
624 if (strcasecmp(opt
->av_val
, options
[i
].name
.av_val
)) continue;
625 v
= (char *)r
+ options
[i
].off
;
626 switch(options
[i
].otype
) {
632 long l
= strtol(arg
->av_val
, NULL
, 0);
638 for (j
=0; truth
[j
].av_len
; j
++) {
639 if (arg
->av_len
!= truth
[j
].av_len
) continue;
640 if (strcasecmp(arg
->av_val
, truth
[j
].av_val
)) continue;
641 fl
|= options
[i
].omisc
; break; }
646 if (parseAMF(&r
->Link
.extras
, arg
, &r
->Link
.edepth
))
652 if (!options
[i
].name
.av_len
) {
653 RTMP_Log(RTMP_LOGERROR
, "Unknown option %s", opt
->av_val
);
661 int RTMP_SetupURL(RTMP
*r
, char *url
)
664 char *p1
, *p2
, *ptr
= strchr(url
, ' ');
666 unsigned int port
= 0;
672 ret
= RTMP_ParseURL(url
, &r
->Link
.protocol
, &r
->Link
.hostname
,
673 &port
, &r
->Link
.playpath0
, &r
->Link
.app
);
677 r
->Link
.playpath
= r
->Link
.playpath0
;
682 p2
= strchr(p1
, '=');
686 opt
.av_len
= p2
- p1
;
689 ptr
= strchr(p2
, ' ');
692 arg
.av_len
= ptr
- p2
;
693 /* skip repeated spaces */
697 arg
.av_len
= strlen(p2
);
702 for (p1
=p2
; port
>0;) {
707 sscanf(p1
+1, "%02x", &c
);
716 arg
.av_len
= p2
- arg
.av_val
;
718 ret
= RTMP_SetOpt(r
, &opt
, &arg
);
723 if (!r
->Link
.tcUrl
.av_len
)
725 r
->Link
.tcUrl
.av_val
= url
;
726 if (r
->Link
.app
.av_len
)
728 if (r
->Link
.app
.av_val
< url
+ len
)
730 /* if app is part of original url, just use it */
731 r
->Link
.tcUrl
.av_len
= r
->Link
.app
.av_len
+ (r
->Link
.app
.av_val
- url
);
735 len
= r
->Link
.hostname
.av_len
+ r
->Link
.app
.av_len
+
736 sizeof("rtmpte://:65535/");
737 r
->Link
.tcUrl
.av_val
= malloc(len
);
738 r
->Link
.tcUrl
.av_len
= snprintf(r
->Link
.tcUrl
.av_val
, len
,
740 RTMPProtocolStringsLower
[r
->Link
.protocol
],
741 r
->Link
.hostname
.av_len
, r
->Link
.hostname
.av_val
,
743 r
->Link
.app
.av_len
, r
->Link
.app
.av_val
);
744 r
->Link
.lFlags
|= RTMP_LF_FTCU
;
749 r
->Link
.tcUrl
.av_len
= strlen(url
);
754 if ((r
->Link
.lFlags
& RTMP_LF_SWFV
) && r
->Link
.swfUrl
.av_len
)
755 RTMP_HashSWF(r
->Link
.swfUrl
.av_val
, &r
->Link
.SWFSize
,
756 (unsigned char *)r
->Link
.SWFHash
, r
->Link
.swfAge
);
759 if (r
->Link
.port
== 0)
761 if (r
->Link
.protocol
& RTMP_FEATURE_SSL
)
763 else if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
772 add_addr_info(struct sockaddr_in
*service
, AVal
*host
, int port
)
776 if (host
->av_val
[host
->av_len
])
778 hostname
= malloc(host
->av_len
+1);
779 memcpy(hostname
, host
->av_val
, host
->av_len
);
780 hostname
[host
->av_len
] = '\0';
784 hostname
= host
->av_val
;
787 service
->sin_addr
.s_addr
= inet_addr(hostname
);
788 if (service
->sin_addr
.s_addr
== INADDR_NONE
)
790 struct hostent
*host
= gethostbyname(hostname
);
791 if (host
== NULL
|| host
->h_addr
== NULL
)
793 RTMP_Log(RTMP_LOGERROR
, "Problem accessing the DNS. (addr: %s)", hostname
);
797 service
->sin_addr
= *(struct in_addr
*)host
->h_addr
;
800 service
->sin_port
= htons(port
);
802 if (hostname
!= host
->av_val
)
808 RTMP_Connect0(RTMP
*r
, struct sockaddr
* service
)
811 r
->m_sb
.sb_timedout
= FALSE
;
813 r
->m_fDuration
= 0.0;
815 r
->m_sb
.sb_socket
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
816 if (r
->m_sb
.sb_socket
!= -1)
818 if (connect(r
->m_sb
.sb_socket
, service
, sizeof(struct sockaddr
)) < 0)
820 int err
= GetSockError();
821 RTMP_Log(RTMP_LOGERROR
, "%s, failed to connect socket. %d (%s)",
822 __FUNCTION__
, err
, strerror(err
));
827 if (r
->Link
.socksport
)
829 RTMP_Log(RTMP_LOGDEBUG
, "%s ... SOCKS negotiation", __FUNCTION__
);
830 if (!SocksNegotiate(r
))
832 RTMP_Log(RTMP_LOGERROR
, "%s, SOCKS negotiation failed.", __FUNCTION__
);
840 RTMP_Log(RTMP_LOGERROR
, "%s, failed to create socket. Error: %d", __FUNCTION__
,
847 SET_RCVTIMEO(tv
, r
->Link
.timeout
);
849 (r
->m_sb
.sb_socket
, SOL_SOCKET
, SO_RCVTIMEO
, (char *)&tv
, sizeof(tv
)))
851 RTMP_Log(RTMP_LOGERROR
, "%s, Setting socket timeout to %ds failed!",
852 __FUNCTION__
, r
->Link
.timeout
);
856 setsockopt(r
->m_sb
.sb_socket
, IPPROTO_TCP
, TCP_NODELAY
, (char *) &on
, sizeof(on
));
862 RTMP_Connect1(RTMP
*r
, RTMPPacket
*cp
)
864 if (r
->Link
.protocol
& RTMP_FEATURE_SSL
)
866 #if defined(CRYPTO) && !defined(NO_SSL)
867 TLS_client(RTMP_TLS_ctx
, r
->m_sb
.sb_ssl
);
868 TLS_setfd(r
->m_sb
.sb_ssl
, r
->m_sb
.sb_socket
);
869 if (TLS_connect(r
->m_sb
.sb_ssl
) < 0)
871 RTMP_Log(RTMP_LOGERROR
, "%s, TLS_Connect failed", __FUNCTION__
);
876 RTMP_Log(RTMP_LOGERROR
, "%s, no SSL/TLS support", __FUNCTION__
);
882 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
885 r
->m_clientID
.av_val
= NULL
;
886 r
->m_clientID
.av_len
= 0;
887 HTTP_Post(r
, RTMPT_OPEN
, "", 1);
888 if (HTTP_read(r
, 1) != 0)
891 RTMP_Log(RTMP_LOGDEBUG
, "%s, Could not connect for handshake", __FUNCTION__
);
897 RTMP_Log(RTMP_LOGDEBUG
, "%s, ... connected, handshaking", __FUNCTION__
);
898 if (!HandShake(r
, TRUE
))
900 RTMP_Log(RTMP_LOGERROR
, "%s, handshake failed.", __FUNCTION__
);
904 RTMP_Log(RTMP_LOGDEBUG
, "%s, handshaked", __FUNCTION__
);
906 if (!SendConnectPacket(r
, cp
))
908 RTMP_Log(RTMP_LOGERROR
, "%s, RTMP connect failed.", __FUNCTION__
);
916 RTMP_Connect(RTMP
*r
, RTMPPacket
*cp
)
918 struct sockaddr_in service
;
919 if (!r
->Link
.hostname
.av_len
)
922 memset(&service
, 0, sizeof(struct sockaddr_in
));
923 service
.sin_family
= AF_INET
;
925 if (r
->Link
.socksport
)
927 /* Connect via SOCKS */
928 if (!add_addr_info(&service
, &r
->Link
.sockshost
, r
->Link
.socksport
))
933 /* Connect directly */
934 if (!add_addr_info(&service
, &r
->Link
.hostname
, r
->Link
.port
))
938 if (!RTMP_Connect0(r
, (struct sockaddr
*)&service
))
941 r
->m_bSendCounter
= TRUE
;
943 return RTMP_Connect1(r
, cp
);
947 SocksNegotiate(RTMP
*r
)
950 struct sockaddr_in service
;
951 memset(&service
, 0, sizeof(struct sockaddr_in
));
953 add_addr_info(&service
, &r
->Link
.hostname
, r
->Link
.port
);
954 addr
= htonl(service
.sin_addr
.s_addr
);
958 4, 1, /* SOCKS 4, connect */
959 (r
->Link
.port
>> 8) & 0xFF,
960 (r
->Link
.port
) & 0xFF,
961 (char)(addr
>> 24) & 0xFF, (char)(addr
>> 16) & 0xFF,
962 (char)(addr
>> 8) & 0xFF, (char)addr
& 0xFF,
964 }; /* NULL terminate */
966 WriteN(r
, packet
, sizeof packet
);
968 if (ReadN(r
, packet
, 8) != 8)
971 if (packet
[0] == 0 && packet
[1] == 90)
977 RTMP_Log(RTMP_LOGERROR
, "%s, SOCKS returned error code %d", packet
[1]);
984 RTMP_ConnectStream(RTMP
*r
, int seekTime
)
986 RTMPPacket packet
= { 0 };
988 /* seekTime was already set by SetupStream / SetupURL.
989 * This is only needed by ReconnectStream.
992 r
->Link
.seekTime
= seekTime
;
994 r
->m_mediaChannel
= 0;
996 while (!r
->m_bPlaying
&& RTMP_IsConnected(r
) && RTMP_ReadPacket(r
, &packet
))
998 if (RTMPPacket_IsReady(&packet
))
1000 if (!packet
.m_nBodySize
)
1002 if ((packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
) ||
1003 (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
) ||
1004 (packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
))
1006 RTMP_Log(RTMP_LOGWARNING
, "Received FLV packet before play()! Ignoring.");
1007 RTMPPacket_Free(&packet
);
1011 RTMP_ClientPacket(r
, &packet
);
1012 RTMPPacket_Free(&packet
);
1016 return r
->m_bPlaying
;
1020 RTMP_ReconnectStream(RTMP
*r
, int seekTime
)
1022 RTMP_DeleteStream(r
);
1024 RTMP_SendCreateStream(r
);
1026 return RTMP_ConnectStream(r
, seekTime
);
1030 RTMP_ToggleStream(RTMP
*r
)
1036 if (RTMP_IsTimedout(r
) && r
->m_read
.status
== RTMP_READ_EOF
)
1037 r
->m_read
.status
= 0;
1039 res
= RTMP_SendPause(r
, TRUE
, r
->m_pauseStamp
);
1046 res
= RTMP_SendPause(r
, FALSE
, r
->m_pauseStamp
);
1052 RTMP_DeleteStream(RTMP
*r
)
1054 if (r
->m_stream_id
< 0)
1057 r
->m_bPlaying
= FALSE
;
1059 SendDeleteStream(r
, r
->m_stream_id
);
1060 r
->m_stream_id
= -1;
1064 RTMP_GetNextMediaPacket(RTMP
*r
, RTMPPacket
*packet
)
1066 int bHasMediaPacket
= 0;
1068 while (!bHasMediaPacket
&& RTMP_IsConnected(r
)
1069 && RTMP_ReadPacket(r
, packet
))
1071 if (!RTMPPacket_IsReady(packet
))
1076 bHasMediaPacket
= RTMP_ClientPacket(r
, packet
);
1078 if (!bHasMediaPacket
)
1080 RTMPPacket_Free(packet
);
1082 else if (r
->m_pausing
== 3)
1084 if (packet
->m_nTimeStamp
<= r
->m_mediaStamp
)
1086 bHasMediaPacket
= 0;
1088 RTMP_Log(RTMP_LOGDEBUG
,
1089 "Skipped type: %02X, size: %d, TS: %d ms, abs TS: %d, pause: %d ms",
1090 packet
->m_packetType
, packet
->m_nBodySize
,
1091 packet
->m_nTimeStamp
, packet
->m_hasAbsTimestamp
,
1100 if (bHasMediaPacket
)
1101 r
->m_bPlaying
= TRUE
;
1102 else if (r
->m_sb
.sb_timedout
&& !r
->m_pausing
)
1103 r
->m_pauseStamp
= r
->m_channelTimestamp
[r
->m_mediaChannel
];
1105 return bHasMediaPacket
;
1109 RTMP_ClientPacket(RTMP
*r
, RTMPPacket
*packet
)
1111 int bHasMediaPacket
= 0;
1112 switch (packet
->m_packetType
)
1114 case RTMP_PACKET_TYPE_CHUNK_SIZE
:
1116 HandleChangeChunkSize(r
, packet
);
1119 case RTMP_PACKET_TYPE_BYTES_READ_REPORT
:
1120 /* bytes read report */
1121 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: bytes read report", __FUNCTION__
);
1124 case RTMP_PACKET_TYPE_CONTROL
:
1126 HandleCtrl(r
, packet
);
1129 case RTMP_PACKET_TYPE_SERVER_BW
:
1131 HandleServerBW(r
, packet
);
1134 case RTMP_PACKET_TYPE_CLIENT_BW
:
1136 HandleClientBW(r
, packet
);
1139 case RTMP_PACKET_TYPE_AUDIO
:
1141 /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); */
1142 HandleAudio(r
, packet
);
1143 bHasMediaPacket
= 1;
1144 if (!r
->m_mediaChannel
)
1145 r
->m_mediaChannel
= packet
->m_nChannel
;
1147 r
->m_mediaStamp
= packet
->m_nTimeStamp
;
1150 case RTMP_PACKET_TYPE_VIDEO
:
1152 /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); */
1153 HandleVideo(r
, packet
);
1154 bHasMediaPacket
= 1;
1155 if (!r
->m_mediaChannel
)
1156 r
->m_mediaChannel
= packet
->m_nChannel
;
1158 r
->m_mediaStamp
= packet
->m_nTimeStamp
;
1161 case RTMP_PACKET_TYPE_FLEX_STREAM_SEND
:
1162 /* flex stream send */
1163 RTMP_Log(RTMP_LOGDEBUG
,
1164 "%s, flex stream send, size %lu bytes, not supported, ignoring",
1165 __FUNCTION__
, packet
->m_nBodySize
);
1168 case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT
:
1169 /* flex shared object */
1170 RTMP_Log(RTMP_LOGDEBUG
,
1171 "%s, flex shared object, size %lu bytes, not supported, ignoring",
1172 __FUNCTION__
, packet
->m_nBodySize
);
1175 case RTMP_PACKET_TYPE_FLEX_MESSAGE
:
1178 RTMP_Log(RTMP_LOGDEBUG
,
1179 "%s, flex message, size %lu bytes, not fully supported",
1180 __FUNCTION__
, packet
->m_nBodySize
);
1181 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
1183 /* some DEBUG code */
1185 RTMP_LIB_AMFObject obj
;
1186 int nRes
= obj
.Decode(packet
.m_body
+1, packet
.m_nBodySize
-1);
1188 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding AMF3 packet", __FUNCTION__
);
1195 if (HandleInvoke(r
, packet
->m_body
+ 1, packet
->m_nBodySize
- 1) == 1)
1196 bHasMediaPacket
= 2;
1199 case RTMP_PACKET_TYPE_INFO
:
1200 /* metadata (notify) */
1201 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: notify %lu bytes", __FUNCTION__
,
1202 packet
->m_nBodySize
);
1203 if (HandleMetadata(r
, packet
->m_body
, packet
->m_nBodySize
))
1204 bHasMediaPacket
= 1;
1207 case RTMP_PACKET_TYPE_SHARED_OBJECT
:
1208 RTMP_Log(RTMP_LOGDEBUG
, "%s, shared object, not supported, ignoring",
1212 case RTMP_PACKET_TYPE_INVOKE
:
1214 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: invoke %lu bytes", __FUNCTION__
,
1215 packet
->m_nBodySize
);
1216 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
1218 if (HandleInvoke(r
, packet
->m_body
, packet
->m_nBodySize
) == 1)
1219 bHasMediaPacket
= 2;
1222 case RTMP_PACKET_TYPE_FLASH_VIDEO
:
1224 /* go through FLV packets and handle metadata packets */
1225 unsigned int pos
= 0;
1226 uint32_t nTimeStamp
= packet
->m_nTimeStamp
;
1228 while (pos
+ 11 < packet
->m_nBodySize
)
1230 uint32_t dataSize
= AMF_DecodeInt24(packet
->m_body
+ pos
+ 1); /* size without header (11) and prevTagSize (4) */
1232 if (pos
+ 11 + dataSize
+ 4 > packet
->m_nBodySize
)
1234 RTMP_Log(RTMP_LOGWARNING
, "Stream corrupt?!");
1237 if (packet
->m_body
[pos
] == 0x12)
1239 HandleMetadata(r
, packet
->m_body
+ pos
+ 11, dataSize
);
1241 else if (packet
->m_body
[pos
] == 8 || packet
->m_body
[pos
] == 9)
1243 nTimeStamp
= AMF_DecodeInt24(packet
->m_body
+ pos
+ 4);
1244 nTimeStamp
|= (packet
->m_body
[pos
+ 7] << 24);
1246 pos
+= (11 + dataSize
+ 4);
1249 r
->m_mediaStamp
= nTimeStamp
;
1252 /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: FLV tag(s) %lu bytes", __FUNCTION__, packet.m_nBodySize); */
1253 bHasMediaPacket
= 1;
1257 RTMP_Log(RTMP_LOGDEBUG
, "%s, unknown packet type received: 0x%02x", __FUNCTION__
,
1258 packet
->m_packetType
);
1260 RTMP_LogHex(RTMP_LOGDEBUG
, packet
->m_body
, packet
->m_nBodySize
);
1264 return bHasMediaPacket
;
1268 extern FILE *netstackdump
;
1269 extern FILE *netstackdump_read
;
1273 ReadN(RTMP
*r
, char *buffer
, int n
)
1275 int nOriginalSize
= n
;
1279 r
->m_sb
.sb_timedout
= FALSE
;
1282 memset(buffer
, 0, n
);
1288 int nBytes
= 0, nRead
;
1289 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
1291 while (!r
->m_resplen
)
1293 if (r
->m_sb
.sb_size
< 144)
1296 HTTP_Post(r
, RTMPT_IDLE
, "", 1);
1297 if (RTMPSockBuf_Fill(&r
->m_sb
) < 1)
1299 if (!r
->m_sb
.sb_timedout
)
1304 if (HTTP_read(r
, 0) == -1)
1306 RTMP_Log(RTMP_LOGDEBUG
, "%s, No valid HTTP response found", __FUNCTION__
);
1311 if (r
->m_resplen
&& !r
->m_sb
.sb_size
)
1312 RTMPSockBuf_Fill(&r
->m_sb
);
1313 avail
= r
->m_sb
.sb_size
;
1314 if (avail
> r
->m_resplen
)
1315 avail
= r
->m_resplen
;
1319 avail
= r
->m_sb
.sb_size
;
1322 if (RTMPSockBuf_Fill(&r
->m_sb
) < 1)
1324 if (!r
->m_sb
.sb_timedout
)
1328 avail
= r
->m_sb
.sb_size
;
1331 nRead
= ((n
< avail
) ? n
: avail
);
1334 memcpy(ptr
, r
->m_sb
.sb_start
, nRead
);
1335 r
->m_sb
.sb_start
+= nRead
;
1336 r
->m_sb
.sb_size
-= nRead
;
1338 r
->m_nBytesIn
+= nRead
;
1339 if (r
->m_bSendCounter
1340 && r
->m_nBytesIn
> r
->m_nBytesInSent
+ r
->m_nClientBW
/ 2)
1341 if (!SendBytesReceived(r
))
1344 /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */
1346 fwrite(ptr
, 1, nBytes
, netstackdump_read
);
1351 RTMP_Log(RTMP_LOGDEBUG
, "%s, RTMP socket closed by peer", __FUNCTION__
);
1357 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
1358 r
->m_resplen
-= nBytes
;
1361 if (r
->Link
.rc4keyIn
)
1363 RC4_encrypt(r
->Link
.rc4keyIn
, nBytes
, ptr
);
1371 return nOriginalSize
- n
;
1375 WriteN(RTMP
*r
, const char *buffer
, int n
)
1377 const char *ptr
= buffer
;
1379 char *encrypted
= 0;
1380 char buf
[RTMP_BUFFER_CACHE_SIZE
];
1382 if (r
->Link
.rc4keyOut
)
1384 if (n
> sizeof(buf
))
1385 encrypted
= (char *)malloc(n
);
1387 encrypted
= (char *)buf
;
1389 RC4_encrypt2(r
->Link
.rc4keyOut
, n
, buffer
, ptr
);
1397 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
1398 nBytes
= HTTP_Post(r
, RTMPT_SEND
, ptr
, n
);
1400 nBytes
= RTMPSockBuf_Send(&r
->m_sb
, ptr
, n
);
1401 /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d\n", __FUNCTION__, nBytes); */
1405 int sockerr
= GetSockError();
1406 RTMP_Log(RTMP_LOGERROR
, "%s, RTMP send error %d (%d bytes)", __FUNCTION__
,
1409 if (sockerr
== EINTR
&& !RTMP_ctrlC
)
1425 if (encrypted
&& encrypted
!= buf
)
1432 #define SAVC(x) static const AVal av_##x = AVC(#x)
1444 SAVC(videoFunction
);
1445 SAVC(objectEncoding
);
1447 SAVC(secureTokenResponse
);
1452 SendConnectPacket(RTMP
*r
, RTMPPacket
*cp
)
1455 char pbuf
[4096], *pend
= pbuf
+ sizeof(pbuf
);
1459 return RTMP_SendPacket(r
, cp
, TRUE
);
1461 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1462 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
1463 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1464 packet
.m_nTimeStamp
= 0;
1465 packet
.m_nInfoField2
= 0;
1466 packet
.m_hasAbsTimestamp
= 0;
1467 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1469 enc
= packet
.m_body
;
1470 enc
= AMF_EncodeString(enc
, pend
, &av_connect
);
1471 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1472 *enc
++ = AMF_OBJECT
;
1474 enc
= AMF_EncodeNamedString(enc
, pend
, &av_app
, &r
->Link
.app
);
1477 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
1479 enc
= AMF_EncodeNamedString(enc
, pend
, &av_type
, &av_nonprivate
);
1483 if (r
->Link
.flashVer
.av_len
)
1485 enc
= AMF_EncodeNamedString(enc
, pend
, &av_flashVer
, &r
->Link
.flashVer
);
1489 if (r
->Link
.swfUrl
.av_len
)
1491 enc
= AMF_EncodeNamedString(enc
, pend
, &av_swfUrl
, &r
->Link
.swfUrl
);
1495 if (r
->Link
.tcUrl
.av_len
)
1497 enc
= AMF_EncodeNamedString(enc
, pend
, &av_tcUrl
, &r
->Link
.tcUrl
);
1501 if (!(r
->Link
.protocol
& RTMP_FEATURE_WRITE
))
1503 enc
= AMF_EncodeNamedBoolean(enc
, pend
, &av_fpad
, FALSE
);
1506 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_capabilities
, 15.0);
1509 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_audioCodecs
, r
->m_fAudioCodecs
);
1512 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_videoCodecs
, r
->m_fVideoCodecs
);
1515 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_videoFunction
, 1.0);
1518 if (r
->Link
.pageUrl
.av_len
)
1520 enc
= AMF_EncodeNamedString(enc
, pend
, &av_pageUrl
, &r
->Link
.pageUrl
);
1525 if (r
->m_fEncoding
!= 0.0 || r
->m_bSendEncoding
)
1526 { /* AMF0, AMF3 not fully supported yet */
1527 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_objectEncoding
, r
->m_fEncoding
);
1531 if (enc
+ 3 >= pend
)
1534 *enc
++ = 0; /* end of object - 0x00 0x00 0x09 */
1535 *enc
++ = AMF_OBJECT_END
;
1537 /* add auth string */
1538 if (r
->Link
.auth
.av_len
)
1540 enc
= AMF_EncodeBoolean(enc
, pend
, r
->Link
.lFlags
& RTMP_LF_AUTH
);
1543 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.auth
);
1547 if (r
->Link
.extras
.o_num
)
1550 for (i
= 0; i
< r
->Link
.extras
.o_num
; i
++)
1552 enc
= AMFProp_Encode(&r
->Link
.extras
.o_props
[i
], enc
, pend
);
1557 packet
.m_nBodySize
= enc
- packet
.m_body
;
1559 return RTMP_SendPacket(r
, &packet
, TRUE
);
1566 SendBGHasStream(RTMP
*r
, double dId
, AVal
*playpath
)
1569 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1572 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1573 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1574 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1575 packet
.m_nTimeStamp
= 0;
1576 packet
.m_nInfoField2
= 0;
1577 packet
.m_hasAbsTimestamp
= 0;
1578 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1580 enc
= packet
.m_body
;
1581 enc
= AMF_EncodeString(enc
, pend
, &av_bgHasStream
);
1582 enc
= AMF_EncodeNumber(enc
, pend
, dId
);
1585 enc
= AMF_EncodeString(enc
, pend
, playpath
);
1589 packet
.m_nBodySize
= enc
- packet
.m_body
;
1591 return RTMP_SendPacket(r
, &packet
, TRUE
);
1598 RTMP_SendCreateStream(RTMP
*r
)
1601 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1604 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1605 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1606 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1607 packet
.m_nTimeStamp
= 0;
1608 packet
.m_nInfoField2
= 0;
1609 packet
.m_hasAbsTimestamp
= 0;
1610 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1612 enc
= packet
.m_body
;
1613 enc
= AMF_EncodeString(enc
, pend
, &av_createStream
);
1614 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1615 *enc
++ = AMF_NULL
; /* NULL */
1617 packet
.m_nBodySize
= enc
- packet
.m_body
;
1619 return RTMP_SendPacket(r
, &packet
, TRUE
);
1625 SendFCSubscribe(RTMP
*r
, AVal
*subscribepath
)
1628 char pbuf
[512], *pend
= pbuf
+ sizeof(pbuf
);
1630 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1631 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1632 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1633 packet
.m_nTimeStamp
= 0;
1634 packet
.m_nInfoField2
= 0;
1635 packet
.m_hasAbsTimestamp
= 0;
1636 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1638 RTMP_Log(RTMP_LOGDEBUG
, "FCSubscribe: %s", subscribepath
->av_val
);
1639 enc
= packet
.m_body
;
1640 enc
= AMF_EncodeString(enc
, pend
, &av_FCSubscribe
);
1641 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1643 enc
= AMF_EncodeString(enc
, pend
, subscribepath
);
1648 packet
.m_nBodySize
= enc
- packet
.m_body
;
1650 return RTMP_SendPacket(r
, &packet
, TRUE
);
1653 /* Justin.tv specific authentication */
1654 static const AVal av_NetStream_Authenticate_UsherToken
= AVC("NetStream.Authenticate.UsherToken");
1657 SendUsherToken(RTMP
*r
, AVal
*usherToken
)
1660 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1662 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1663 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1664 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1665 packet
.m_nTimeStamp
= 0;
1666 packet
.m_nInfoField2
= 0;
1667 packet
.m_hasAbsTimestamp
= 0;
1668 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1670 RTMP_Log(RTMP_LOGDEBUG
, "UsherToken: %s", usherToken
->av_val
);
1671 enc
= packet
.m_body
;
1672 enc
= AMF_EncodeString(enc
, pend
, &av_NetStream_Authenticate_UsherToken
);
1673 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1675 enc
= AMF_EncodeString(enc
, pend
, usherToken
);
1680 packet
.m_nBodySize
= enc
- packet
.m_body
;
1682 return RTMP_SendPacket(r
, &packet
, FALSE
);
1684 /******************************************/
1686 SAVC(releaseStream
);
1689 SendReleaseStream(RTMP
*r
)
1692 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1695 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1696 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1697 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1698 packet
.m_nTimeStamp
= 0;
1699 packet
.m_nInfoField2
= 0;
1700 packet
.m_hasAbsTimestamp
= 0;
1701 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1703 enc
= packet
.m_body
;
1704 enc
= AMF_EncodeString(enc
, pend
, &av_releaseStream
);
1705 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1707 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1711 packet
.m_nBodySize
= enc
- packet
.m_body
;
1713 return RTMP_SendPacket(r
, &packet
, FALSE
);
1719 SendFCPublish(RTMP
*r
)
1722 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1725 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1726 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1727 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1728 packet
.m_nTimeStamp
= 0;
1729 packet
.m_nInfoField2
= 0;
1730 packet
.m_hasAbsTimestamp
= 0;
1731 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1733 enc
= packet
.m_body
;
1734 enc
= AMF_EncodeString(enc
, pend
, &av_FCPublish
);
1735 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1737 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1741 packet
.m_nBodySize
= enc
- packet
.m_body
;
1743 return RTMP_SendPacket(r
, &packet
, FALSE
);
1749 SendFCUnpublish(RTMP
*r
)
1752 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1755 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1756 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1757 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1758 packet
.m_nTimeStamp
= 0;
1759 packet
.m_nInfoField2
= 0;
1760 packet
.m_hasAbsTimestamp
= 0;
1761 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1763 enc
= packet
.m_body
;
1764 enc
= AMF_EncodeString(enc
, pend
, &av_FCUnpublish
);
1765 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1767 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1771 packet
.m_nBodySize
= enc
- packet
.m_body
;
1773 return RTMP_SendPacket(r
, &packet
, FALSE
);
1781 SendPublish(RTMP
*r
)
1784 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1787 packet
.m_nChannel
= 0x04; /* source channel (invoke) */
1788 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
1789 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1790 packet
.m_nTimeStamp
= 0;
1791 packet
.m_nInfoField2
= r
->m_stream_id
;
1792 packet
.m_hasAbsTimestamp
= 0;
1793 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1795 enc
= packet
.m_body
;
1796 enc
= AMF_EncodeString(enc
, pend
, &av_publish
);
1797 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1799 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1803 /* FIXME: should we choose live based on Link.lFlags & RTMP_LF_LIVE? */
1804 enc
= AMF_EncodeString(enc
, pend
, &av_live
);
1808 packet
.m_nBodySize
= enc
- packet
.m_body
;
1810 return RTMP_SendPacket(r
, &packet
, TRUE
);
1816 SendDeleteStream(RTMP
*r
, double dStreamId
)
1819 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1822 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1823 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1824 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1825 packet
.m_nTimeStamp
= 0;
1826 packet
.m_nInfoField2
= 0;
1827 packet
.m_hasAbsTimestamp
= 0;
1828 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1830 enc
= packet
.m_body
;
1831 enc
= AMF_EncodeString(enc
, pend
, &av_deleteStream
);
1832 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1834 enc
= AMF_EncodeNumber(enc
, pend
, dStreamId
);
1836 packet
.m_nBodySize
= enc
- packet
.m_body
;
1838 /* no response expected */
1839 return RTMP_SendPacket(r
, &packet
, FALSE
);
1845 RTMP_SendPause(RTMP
*r
, int DoPause
, int iTime
)
1848 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1851 packet
.m_nChannel
= 0x08; /* video channel */
1852 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1853 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1854 packet
.m_nTimeStamp
= 0;
1855 packet
.m_nInfoField2
= 0;
1856 packet
.m_hasAbsTimestamp
= 0;
1857 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1859 enc
= packet
.m_body
;
1860 enc
= AMF_EncodeString(enc
, pend
, &av_pause
);
1861 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1863 enc
= AMF_EncodeBoolean(enc
, pend
, DoPause
);
1864 enc
= AMF_EncodeNumber(enc
, pend
, (double)iTime
);
1866 packet
.m_nBodySize
= enc
- packet
.m_body
;
1868 RTMP_Log(RTMP_LOGDEBUG
, "%s, %d, pauseTime=%d", __FUNCTION__
, DoPause
, iTime
);
1869 return RTMP_SendPacket(r
, &packet
, TRUE
);
1872 int RTMP_Pause(RTMP
*r
, int DoPause
)
1875 r
->m_pauseStamp
= r
->m_channelTimestamp
[r
->m_mediaChannel
];
1876 return RTMP_SendPause(r
, DoPause
, r
->m_pauseStamp
);
1882 RTMP_SendSeek(RTMP
*r
, int iTime
)
1885 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1888 packet
.m_nChannel
= 0x08; /* video channel */
1889 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1890 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1891 packet
.m_nTimeStamp
= 0;
1892 packet
.m_nInfoField2
= 0;
1893 packet
.m_hasAbsTimestamp
= 0;
1894 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1896 enc
= packet
.m_body
;
1897 enc
= AMF_EncodeString(enc
, pend
, &av_seek
);
1898 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1900 enc
= AMF_EncodeNumber(enc
, pend
, (double)iTime
);
1902 packet
.m_nBodySize
= enc
- packet
.m_body
;
1904 r
->m_read
.flags
|= RTMP_READ_SEEKING
;
1905 r
->m_read
.nResumeTS
= 0;
1907 return RTMP_SendPacket(r
, &packet
, TRUE
);
1911 RTMP_SendServerBW(RTMP
*r
)
1914 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1916 packet
.m_nChannel
= 0x02; /* control channel (invoke) */
1917 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
1918 packet
.m_packetType
= RTMP_PACKET_TYPE_SERVER_BW
;
1919 packet
.m_nTimeStamp
= 0;
1920 packet
.m_nInfoField2
= 0;
1921 packet
.m_hasAbsTimestamp
= 0;
1922 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1924 packet
.m_nBodySize
= 4;
1926 AMF_EncodeInt32(packet
.m_body
, pend
, r
->m_nServerBW
);
1927 return RTMP_SendPacket(r
, &packet
, FALSE
);
1931 RTMP_SendClientBW(RTMP
*r
)
1934 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1936 packet
.m_nChannel
= 0x02; /* control channel (invoke) */
1937 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
1938 packet
.m_packetType
= RTMP_PACKET_TYPE_CLIENT_BW
;
1939 packet
.m_nTimeStamp
= 0;
1940 packet
.m_nInfoField2
= 0;
1941 packet
.m_hasAbsTimestamp
= 0;
1942 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1944 packet
.m_nBodySize
= 5;
1946 AMF_EncodeInt32(packet
.m_body
, pend
, r
->m_nClientBW
);
1947 packet
.m_body
[4] = r
->m_nClientBW2
;
1948 return RTMP_SendPacket(r
, &packet
, FALSE
);
1952 SendBytesReceived(RTMP
*r
)
1955 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1957 packet
.m_nChannel
= 0x02; /* control channel (invoke) */
1958 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1959 packet
.m_packetType
= RTMP_PACKET_TYPE_BYTES_READ_REPORT
;
1960 packet
.m_nTimeStamp
= 0;
1961 packet
.m_nInfoField2
= 0;
1962 packet
.m_hasAbsTimestamp
= 0;
1963 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1965 packet
.m_nBodySize
= 4;
1967 AMF_EncodeInt32(packet
.m_body
, pend
, r
->m_nBytesIn
); /* hard coded for now */
1968 r
->m_nBytesInSent
= r
->m_nBytesIn
;
1970 /*RTMP_Log(RTMP_LOGDEBUG, "Send bytes report. 0x%x (%d bytes)", (unsigned int)m_nBytesIn, m_nBytesIn); */
1971 return RTMP_SendPacket(r
, &packet
, FALSE
);
1977 SendCheckBW(RTMP
*r
)
1980 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1983 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1984 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
1985 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1986 packet
.m_nTimeStamp
= 0; /* RTMP_GetTime(); */
1987 packet
.m_nInfoField2
= 0;
1988 packet
.m_hasAbsTimestamp
= 0;
1989 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1991 enc
= packet
.m_body
;
1992 enc
= AMF_EncodeString(enc
, pend
, &av__checkbw
);
1993 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1996 packet
.m_nBodySize
= enc
- packet
.m_body
;
1998 /* triggers _onbwcheck and eventually results in _onbwdone */
1999 return RTMP_SendPacket(r
, &packet
, FALSE
);
2005 SendCheckBWResult(RTMP
*r
, double txn
)
2008 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2011 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2012 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2013 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2014 packet
.m_nTimeStamp
= 0x16 * r
->m_nBWCheckCounter
; /* temp inc value. till we figure it out. */
2015 packet
.m_nInfoField2
= 0;
2016 packet
.m_hasAbsTimestamp
= 0;
2017 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2019 enc
= packet
.m_body
;
2020 enc
= AMF_EncodeString(enc
, pend
, &av__result
);
2021 enc
= AMF_EncodeNumber(enc
, pend
, txn
);
2023 enc
= AMF_EncodeNumber(enc
, pend
, (double)r
->m_nBWCheckCounter
++);
2025 packet
.m_nBodySize
= enc
- packet
.m_body
;
2027 return RTMP_SendPacket(r
, &packet
, FALSE
);
2034 SendPong(RTMP
*r
, double txn
)
2037 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2040 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2041 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2042 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2043 packet
.m_nTimeStamp
= 0x16 * r
->m_nBWCheckCounter
; /* temp inc value. till we figure it out. */
2044 packet
.m_nInfoField2
= 0;
2045 packet
.m_hasAbsTimestamp
= 0;
2046 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2048 enc
= packet
.m_body
;
2049 enc
= AMF_EncodeString(enc
, pend
, &av_pong
);
2050 enc
= AMF_EncodeNumber(enc
, pend
, txn
);
2053 packet
.m_nBodySize
= enc
- packet
.m_body
;
2055 return RTMP_SendPacket(r
, &packet
, FALSE
);
2064 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
2067 packet
.m_nChannel
= 0x08; /* we make 8 our stream channel */
2068 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2069 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2070 packet
.m_nTimeStamp
= 0;
2071 packet
.m_nInfoField2
= r
->m_stream_id
; /*0x01000000; */
2072 packet
.m_hasAbsTimestamp
= 0;
2073 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2075 enc
= packet
.m_body
;
2076 enc
= AMF_EncodeString(enc
, pend
, &av_play
);
2077 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
2080 RTMP_Log(RTMP_LOGDEBUG
, "%s, seekTime=%d, stopTime=%d, sending play: %s",
2081 __FUNCTION__
, r
->Link
.seekTime
, r
->Link
.stopTime
,
2082 r
->Link
.playpath
.av_val
);
2083 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
2087 /* Optional parameters start and len.
2089 * start: -2, -1, 0, positive number
2090 * -2: looks for a live stream, then a recorded stream,
2091 * if not found any open a live stream
2092 * -1: plays a live stream
2093 * >=0: plays a recorded streams from 'start' milliseconds
2095 if (r
->Link
.lFlags
& RTMP_LF_LIVE
)
2096 enc
= AMF_EncodeNumber(enc
, pend
, -1000.0);
2099 if (r
->Link
.seekTime
> 0.0)
2100 enc
= AMF_EncodeNumber(enc
, pend
, r
->Link
.seekTime
); /* resume from here */
2102 enc
= AMF_EncodeNumber(enc
, pend
, 0.0); /*-2000.0);*/ /* recorded as default, -2000.0 is not reliable since that freezes the player if the stream is not found */
2107 /* len: -1, 0, positive number
2108 * -1: plays live or recorded stream to the end (default)
2109 * 0: plays a frame 'start' ms away from the beginning
2110 * >0: plays a live or recoded stream for 'len' milliseconds
2112 /*enc += EncodeNumber(enc, -1.0); */ /* len */
2113 if (r
->Link
.stopTime
)
2115 enc
= AMF_EncodeNumber(enc
, pend
, r
->Link
.stopTime
- r
->Link
.seekTime
);
2120 packet
.m_nBodySize
= enc
- packet
.m_body
;
2122 return RTMP_SendPacket(r
, &packet
, TRUE
);
2129 SendPlaylist(RTMP
*r
)
2132 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
2135 packet
.m_nChannel
= 0x08; /* we make 8 our stream channel */
2136 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2137 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2138 packet
.m_nTimeStamp
= 0;
2139 packet
.m_nInfoField2
= r
->m_stream_id
; /*0x01000000; */
2140 packet
.m_hasAbsTimestamp
= 0;
2141 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2143 enc
= packet
.m_body
;
2144 enc
= AMF_EncodeString(enc
, pend
, &av_set_playlist
);
2145 enc
= AMF_EncodeNumber(enc
, pend
, 0);
2147 *enc
++ = AMF_ECMA_ARRAY
;
2151 *enc
++ = AMF_OBJECT
;
2152 enc
= AMF_EncodeNamedString(enc
, pend
, &av_0
, &r
->Link
.playpath
);
2155 if (enc
+ 3 >= pend
)
2159 *enc
++ = AMF_OBJECT_END
;
2161 packet
.m_nBodySize
= enc
- packet
.m_body
;
2163 return RTMP_SendPacket(r
, &packet
, TRUE
);
2167 SendSecureTokenResponse(RTMP
*r
, AVal
*resp
)
2170 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
2173 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2174 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2175 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2176 packet
.m_nTimeStamp
= 0;
2177 packet
.m_nInfoField2
= 0;
2178 packet
.m_hasAbsTimestamp
= 0;
2179 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2181 enc
= packet
.m_body
;
2182 enc
= AMF_EncodeString(enc
, pend
, &av_secureTokenResponse
);
2183 enc
= AMF_EncodeNumber(enc
, pend
, 0.0);
2185 enc
= AMF_EncodeString(enc
, pend
, resp
);
2189 packet
.m_nBodySize
= enc
- packet
.m_body
;
2191 return RTMP_SendPacket(r
, &packet
, FALSE
);
2195 from http://jira.red5.org/confluence/display/docs/Ping:
2197 Ping is the most mysterious message in RTMP and till now we haven't fully interpreted it yet. In summary, Ping message is used as a special command that are exchanged between client and server. This page aims to document all known Ping messages. Expect the list to grow.
2199 The type of Ping packet is 0x4 and contains two mandatory parameters and two optional parameters. The first parameter is the type of Ping and in short integer. The second parameter is the target of the ping. As Ping is always sent in Channel 2 (control channel) and the target object in RTMP header is always 0 which means the Connection object, it's necessary to put an extra parameter to indicate the exact target object the Ping is sent to. The second parameter takes this responsibility. The value has the same meaning as the target object field in RTMP header. (The second value could also be used as other purposes, like RTT Ping/Pong. It is used as the timestamp.) The third and fourth parameters are optional and could be looked upon as the parameter of the Ping packet. Below is an unexhausted list of Ping messages.
2201 * type 0: Clear the stream. No third and fourth parameters. The second parameter could be 0. After the connection is established, a Ping 0,0 will be sent from server to client. The message will also be sent to client on the start of Play and in response of a Seek or Pause/Resume request. This Ping tells client to re-calibrate the clock with the timestamp of the next packet server sends.
2202 * type 1: Tell the stream to clear the playing buffer.
2203 * type 3: Buffer time of the client. The third parameter is the buffer time in millisecond.
2204 * type 4: Reset a stream. Used together with type 0 in the case of VOD. Often sent before type 0.
2205 * type 6: Ping the client from server. The second parameter is the current time.
2206 * type 7: Pong reply from client. The second parameter is the time the server sent with his ping request.
2207 * type 26: SWFVerification request
2208 * type 27: SWFVerification response
2211 RTMP_SendCtrl(RTMP
*r
, short nType
, unsigned int nObject
, unsigned int nTime
)
2214 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2218 RTMP_Log(RTMP_LOGDEBUG
, "sending ctrl. type: 0x%04x", (unsigned short)nType
);
2220 packet
.m_nChannel
= 0x02; /* control channel (ping) */
2221 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2222 packet
.m_packetType
= RTMP_PACKET_TYPE_CONTROL
;
2223 packet
.m_nTimeStamp
= 0; /* RTMP_GetTime(); */
2224 packet
.m_nInfoField2
= 0;
2225 packet
.m_hasAbsTimestamp
= 0;
2226 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2229 case 0x03: nSize
= 10; break; /* buffer time */
2230 case 0x1A: nSize
= 3; break; /* SWF verify request */
2231 case 0x1B: nSize
= 44; break; /* SWF verify response */
2232 default: nSize
= 6; break;
2235 packet
.m_nBodySize
= nSize
;
2237 buf
= packet
.m_body
;
2238 buf
= AMF_EncodeInt16(buf
, pend
, nType
);
2243 memcpy(buf
, r
->Link
.SWFVerificationResponse
, 42);
2244 RTMP_Log(RTMP_LOGDEBUG
, "Sending SWFVerification response: ");
2245 RTMP_LogHex(RTMP_LOGDEBUG
, (uint8_t *)packet
.m_body
, packet
.m_nBodySize
);
2248 else if (nType
== 0x1A)
2250 *buf
= nObject
& 0xff;
2255 buf
= AMF_EncodeInt32(buf
, pend
, nObject
);
2258 buf
= AMF_EncodeInt32(buf
, pend
, nTime
);
2261 return RTMP_SendPacket(r
, &packet
, FALSE
);
2265 AV_erase(RTMP_METHOD
*vals
, int *num
, int i
, int freeit
)
2268 free(vals
[i
].name
.av_val
);
2270 for (; i
< *num
; i
++)
2272 vals
[i
] = vals
[i
+ 1];
2274 vals
[i
].name
.av_val
= NULL
;
2275 vals
[i
].name
.av_len
= 0;
2280 RTMP_DropRequest(RTMP
*r
, int i
, int freeit
)
2282 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, freeit
);
2286 AV_queue(RTMP_METHOD
**vals
, int *num
, AVal
*av
, int txn
)
2290 *vals
= realloc(*vals
, (*num
+ 16) * sizeof(RTMP_METHOD
));
2291 tmp
= malloc(av
->av_len
+ 1);
2292 memcpy(tmp
, av
->av_val
, av
->av_len
);
2293 tmp
[av
->av_len
] = '\0';
2294 (*vals
)[*num
].num
= txn
;
2295 (*vals
)[*num
].name
.av_len
= av
->av_len
;
2296 (*vals
)[(*num
)++].name
.av_val
= tmp
;
2300 AV_clear(RTMP_METHOD
*vals
, int num
)
2303 for (i
= 0; i
< num
; i
++)
2304 free(vals
[i
].name
.av_val
);
2309 SAVC(onFCSubscribe
);
2310 SAVC(onFCUnsubscribe
);
2318 SAVC(playlist_ready
);
2319 static const AVal av_NetStream_Failed
= AVC("NetStream.Failed");
2320 static const AVal av_NetStream_Play_Failed
= AVC("NetStream.Play.Failed");
2321 static const AVal av_NetStream_Play_StreamNotFound
=
2322 AVC("NetStream.Play.StreamNotFound");
2323 static const AVal av_NetConnection_Connect_InvalidApp
=
2324 AVC("NetConnection.Connect.InvalidApp");
2325 static const AVal av_NetStream_Play_Start
= AVC("NetStream.Play.Start");
2326 static const AVal av_NetStream_Play_Complete
= AVC("NetStream.Play.Complete");
2327 static const AVal av_NetStream_Play_Stop
= AVC("NetStream.Play.Stop");
2328 static const AVal av_NetStream_Seek_Notify
= AVC("NetStream.Seek.Notify");
2329 static const AVal av_NetStream_Pause_Notify
= AVC("NetStream.Pause.Notify");
2330 static const AVal av_NetStream_Play_PublishNotify
=
2331 AVC("NetStream.Play.PublishNotify");
2332 static const AVal av_NetStream_Play_UnpublishNotify
=
2333 AVC("NetStream.Play.UnpublishNotify");
2334 static const AVal av_NetStream_Publish_Start
= AVC("NetStream.Publish.Start");
2336 /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */
2338 HandleInvoke(RTMP
*r
, const char *body
, unsigned int nBodySize
)
2344 if (body
[0] != 0x02) /* make sure it is a string method name we start with */
2346 RTMP_Log(RTMP_LOGWARNING
, "%s, Sanity failed. no string method in invoke packet",
2351 nRes
= AMF_Decode(&obj
, body
, nBodySize
, FALSE
);
2354 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding invoke packet", __FUNCTION__
);
2359 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 0), &method
);
2360 txn
= AMFProp_GetNumber(AMF_GetProp(&obj
, NULL
, 1));
2361 RTMP_Log(RTMP_LOGDEBUG
, "%s, server invoking <%s>", __FUNCTION__
, method
.av_val
);
2363 if (AVMATCH(&method
, &av__result
))
2365 AVal methodInvoked
= {0};
2368 for (i
=0; i
<r
->m_numCalls
; i
++) {
2369 if (r
->m_methodCalls
[i
].num
== (int)txn
) {
2370 methodInvoked
= r
->m_methodCalls
[i
].name
;
2371 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, FALSE
);
2375 if (!methodInvoked
.av_val
) {
2376 RTMP_Log(RTMP_LOGDEBUG
, "%s, received result id %d without matching request",
2381 RTMP_Log(RTMP_LOGDEBUG
, "%s, received result for method call <%s>", __FUNCTION__
,
2382 methodInvoked
.av_val
);
2384 if (AVMATCH(&methodInvoked
, &av_connect
))
2386 if (r
->Link
.token
.av_len
)
2388 AMFObjectProperty p
;
2389 if (RTMP_FindFirstMatchingProperty(&obj
, &av_secureToken
, &p
))
2391 DecodeTEA(&r
->Link
.token
, &p
.p_vu
.p_aval
);
2392 SendSecureTokenResponse(r
, &p
.p_vu
.p_aval
);
2395 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
2397 SendReleaseStream(r
);
2402 RTMP_SendServerBW(r
);
2403 RTMP_SendCtrl(r
, 3, 0, 300);
2405 RTMP_SendCreateStream(r
);
2407 if (!(r
->Link
.protocol
& RTMP_FEATURE_WRITE
))
2409 /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */
2410 if (r
->Link
.usherToken
.av_len
)
2411 SendUsherToken(r
, &r
->Link
.usherToken
);
2412 /* Send the FCSubscribe if live stream or if subscribepath is set */
2413 if (r
->Link
.subscribepath
.av_len
)
2414 SendFCSubscribe(r
, &r
->Link
.subscribepath
);
2415 else if (r
->Link
.lFlags
& RTMP_LF_LIVE
)
2416 SendFCSubscribe(r
, &r
->Link
.playpath
);
2419 else if (AVMATCH(&methodInvoked
, &av_createStream
))
2421 r
->m_stream_id
= (int)AMFProp_GetNumber(AMF_GetProp(&obj
, NULL
, 3));
2423 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
2429 if (r
->Link
.lFlags
& RTMP_LF_PLST
)
2432 RTMP_SendCtrl(r
, 3, r
->m_stream_id
, r
->m_nBufferMS
);
2435 else if (AVMATCH(&methodInvoked
, &av_play
) ||
2436 AVMATCH(&methodInvoked
, &av_publish
))
2438 r
->m_bPlaying
= TRUE
;
2440 free(methodInvoked
.av_val
);
2442 else if (AVMATCH(&method
, &av_onBWDone
))
2444 if (!r
->m_nBWCheckCounter
)
2447 else if (AVMATCH(&method
, &av_onFCSubscribe
))
2449 /* SendOnFCSubscribe(); */
2451 else if (AVMATCH(&method
, &av_onFCUnsubscribe
))
2456 else if (AVMATCH(&method
, &av_ping
))
2460 else if (AVMATCH(&method
, &av__onbwcheck
))
2462 SendCheckBWResult(r
, txn
);
2464 else if (AVMATCH(&method
, &av__onbwdone
))
2467 for (i
= 0; i
< r
->m_numCalls
; i
++)
2468 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av__checkbw
))
2470 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
2474 else if (AVMATCH(&method
, &av__error
))
2476 RTMP_Log(RTMP_LOGERROR
, "rtmp server sent error");
2478 else if (AVMATCH(&method
, &av_close
))
2480 RTMP_Log(RTMP_LOGERROR
, "rtmp server requested close");
2483 else if (AVMATCH(&method
, &av_onStatus
))
2487 AMFProp_GetObject(AMF_GetProp(&obj
, NULL
, 3), &obj2
);
2488 AMFProp_GetString(AMF_GetProp(&obj2
, &av_code
, -1), &code
);
2489 AMFProp_GetString(AMF_GetProp(&obj2
, &av_level
, -1), &level
);
2491 RTMP_Log(RTMP_LOGDEBUG
, "%s, onStatus: %s", __FUNCTION__
, code
.av_val
);
2492 if (AVMATCH(&code
, &av_NetStream_Failed
)
2493 || AVMATCH(&code
, &av_NetStream_Play_Failed
)
2494 || AVMATCH(&code
, &av_NetStream_Play_StreamNotFound
)
2495 || AVMATCH(&code
, &av_NetConnection_Connect_InvalidApp
))
2497 r
->m_stream_id
= -1;
2499 RTMP_Log(RTMP_LOGERROR
, "Closing connection: %s", code
.av_val
);
2502 else if (AVMATCH(&code
, &av_NetStream_Play_Start
)
2503 || AVMATCH(&code
, &av_NetStream_Play_PublishNotify
))
2506 r
->m_bPlaying
= TRUE
;
2507 for (i
= 0; i
< r
->m_numCalls
; i
++)
2509 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av_play
))
2511 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
2517 else if (AVMATCH(&code
, &av_NetStream_Publish_Start
))
2520 r
->m_bPlaying
= TRUE
;
2521 for (i
= 0; i
< r
->m_numCalls
; i
++)
2523 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av_publish
))
2525 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
2531 /* Return 1 if this is a Play.Complete or Play.Stop */
2532 else if (AVMATCH(&code
, &av_NetStream_Play_Complete
)
2533 || AVMATCH(&code
, &av_NetStream_Play_Stop
)
2534 || AVMATCH(&code
, &av_NetStream_Play_UnpublishNotify
))
2540 else if (AVMATCH(&code
, &av_NetStream_Seek_Notify
))
2542 r
->m_read
.flags
&= ~RTMP_READ_SEEKING
;
2545 else if (AVMATCH(&code
, &av_NetStream_Pause_Notify
))
2547 if (r
->m_pausing
== 1 || r
->m_pausing
== 2)
2549 RTMP_SendPause(r
, FALSE
, r
->m_pauseStamp
);
2554 else if (AVMATCH(&method
, &av_playlist_ready
))
2557 for (i
= 0; i
< r
->m_numCalls
; i
++)
2559 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av_set_playlist
))
2561 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
2576 RTMP_FindFirstMatchingProperty(AMFObject
*obj
, const AVal
*name
,
2577 AMFObjectProperty
* p
)
2580 /* this is a small object search to locate the "duration" property */
2581 for (n
= 0; n
< obj
->o_num
; n
++)
2583 AMFObjectProperty
*prop
= AMF_GetProp(obj
, NULL
, n
);
2585 if (AVMATCH(&prop
->p_name
, name
))
2591 if (prop
->p_type
== AMF_OBJECT
)
2593 if (RTMP_FindFirstMatchingProperty(&prop
->p_vu
.p_object
, name
, p
))
2600 /* Like above, but only check if name is a prefix of property */
2602 RTMP_FindPrefixProperty(AMFObject
*obj
, const AVal
*name
,
2603 AMFObjectProperty
* p
)
2606 for (n
= 0; n
< obj
->o_num
; n
++)
2608 AMFObjectProperty
*prop
= AMF_GetProp(obj
, NULL
, n
);
2610 if (prop
->p_name
.av_len
> name
->av_len
&&
2611 !memcmp(prop
->p_name
.av_val
, name
->av_val
, name
->av_len
))
2617 if (prop
->p_type
== AMF_OBJECT
)
2619 if (RTMP_FindPrefixProperty(&prop
->p_vu
.p_object
, name
, p
))
2627 DumpMetaData(AMFObject
*obj
)
2629 AMFObjectProperty
*prop
;
2631 for (n
= 0; n
< obj
->o_num
; n
++)
2633 prop
= AMF_GetProp(obj
, NULL
, n
);
2634 if (prop
->p_type
!= AMF_OBJECT
)
2637 switch (prop
->p_type
)
2640 snprintf(str
, 255, "%.2f", prop
->p_vu
.p_number
);
2643 snprintf(str
, 255, "%s",
2644 prop
->p_vu
.p_number
!= 0. ? "TRUE" : "FALSE");
2647 snprintf(str
, 255, "%.*s", prop
->p_vu
.p_aval
.av_len
,
2648 prop
->p_vu
.p_aval
.av_val
);
2651 snprintf(str
, 255, "timestamp:%.2f", prop
->p_vu
.p_number
);
2654 snprintf(str
, 255, "INVALID TYPE 0x%02x",
2655 (unsigned char)prop
->p_type
);
2657 if (prop
->p_name
.av_len
)
2660 if (strlen(str
) >= 1 && str
[strlen(str
) - 1] == '\n')
2661 str
[strlen(str
) - 1] = '\0';
2662 RTMP_Log(RTMP_LOGINFO
, " %-22.*s%s", prop
->p_name
.av_len
,
2663 prop
->p_name
.av_val
, str
);
2668 if (prop
->p_name
.av_len
)
2669 RTMP_Log(RTMP_LOGINFO
, "%.*s:", prop
->p_name
.av_len
, prop
->p_name
.av_val
);
2670 DumpMetaData(&prop
->p_vu
.p_object
);
2682 HandleMetadata(RTMP
*r
, char *body
, unsigned int len
)
2684 /* allright we get some info here, so parse it and print it */
2685 /* also keep duration or filesize to make a nice progress bar */
2691 int nRes
= AMF_Decode(&obj
, body
, len
, FALSE
);
2694 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding meta data packet", __FUNCTION__
);
2699 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 0), &metastring
);
2701 if (AVMATCH(&metastring
, &av_onMetaData
))
2703 AMFObjectProperty prop
;
2705 RTMP_Log(RTMP_LOGINFO
, "Metadata:");
2707 if (RTMP_FindFirstMatchingProperty(&obj
, &av_duration
, &prop
))
2709 r
->m_fDuration
= prop
.p_vu
.p_number
;
2710 /*RTMP_Log(RTMP_LOGDEBUG, "Set duration: %.2f", m_fDuration); */
2712 /* Search for audio or video tags */
2713 if (RTMP_FindPrefixProperty(&obj
, &av_video
, &prop
))
2714 r
->m_read
.dataType
|= 1;
2715 if (RTMP_FindPrefixProperty(&obj
, &av_audio
, &prop
))
2716 r
->m_read
.dataType
|= 4;
2724 HandleChangeChunkSize(RTMP
*r
, const RTMPPacket
*packet
)
2726 if (packet
->m_nBodySize
>= 4)
2728 r
->m_inChunkSize
= AMF_DecodeInt32(packet
->m_body
);
2729 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: chunk size change to %d", __FUNCTION__
,
2735 HandleAudio(RTMP
*r
, const RTMPPacket
*packet
)
2740 HandleVideo(RTMP
*r
, const RTMPPacket
*packet
)
2745 HandleCtrl(RTMP
*r
, const RTMPPacket
*packet
)
2749 if (packet
->m_body
&& packet
->m_nBodySize
>= 2)
2750 nType
= AMF_DecodeInt16(packet
->m_body
);
2751 RTMP_Log(RTMP_LOGDEBUG
, "%s, received ctrl. type: %d, len: %d", __FUNCTION__
, nType
,
2752 packet
->m_nBodySize
);
2753 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
2755 if (packet
->m_nBodySize
>= 6)
2760 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2761 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream Begin %d", __FUNCTION__
, tmp
);
2765 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2766 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream EOF %d", __FUNCTION__
, tmp
);
2767 if (r
->m_pausing
== 1)
2772 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2773 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream Dry %d", __FUNCTION__
, tmp
);
2777 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2778 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream IsRecorded %d", __FUNCTION__
, tmp
);
2781 case 6: /* server ping. reply with pong. */
2782 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2783 RTMP_Log(RTMP_LOGDEBUG
, "%s, Ping %d", __FUNCTION__
, tmp
);
2784 RTMP_SendCtrl(r
, 0x07, tmp
, 0);
2787 /* FMS 3.5 servers send the following two controls to let the client
2788 * know when the server has sent a complete buffer. I.e., when the
2789 * server has sent an amount of data equal to m_nBufferMS in duration.
2790 * The server meters its output so that data arrives at the client
2791 * in realtime and no faster.
2793 * The rtmpdump program tries to set m_nBufferMS as large as
2794 * possible, to force the server to send data as fast as possible.
2795 * In practice, the server appears to cap this at about 1 hour's
2796 * worth of data. After the server has sent a complete buffer, and
2797 * sends this BufferEmpty message, it will wait until the play
2798 * duration of that buffer has passed before sending a new buffer.
2799 * The BufferReady message will be sent when the new buffer starts.
2800 * (There is no BufferReady message for the very first buffer;
2801 * presumably the Stream Begin message is sufficient for that
2804 * If the network speed is much faster than the data bitrate, then
2805 * there may be long delays between the end of one buffer and the
2806 * start of the next.
2808 * Since usually the network allows data to be sent at
2809 * faster than realtime, and rtmpdump wants to download the data
2810 * as fast as possible, we use this RTMP_LF_BUFX hack: when we
2811 * get the BufferEmpty message, we send a Pause followed by an
2812 * Unpause. This causes the server to send the next buffer immediately
2813 * instead of waiting for the full duration to elapse. (That's
2814 * also the purpose of the ToggleStream function, which rtmpdump
2815 * calls if we get a read timeout.)
2817 * Media player apps don't need this hack since they are just
2818 * going to play the data in realtime anyway. It also doesn't work
2819 * for live streams since they obviously can only be sent in
2820 * realtime. And it's all moot if the network speed is actually
2821 * slower than the media bitrate.
2824 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2825 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream BufferEmpty %d", __FUNCTION__
, tmp
);
2826 if (!(r
->Link
.lFlags
& RTMP_LF_BUFX
))
2830 r
->m_pauseStamp
= r
->m_channelTimestamp
[r
->m_mediaChannel
];
2831 RTMP_SendPause(r
, TRUE
, r
->m_pauseStamp
);
2834 else if (r
->m_pausing
== 2)
2836 RTMP_SendPause(r
, FALSE
, r
->m_pauseStamp
);
2842 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2843 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream BufferReady %d", __FUNCTION__
, tmp
);
2847 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2848 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream xx %d", __FUNCTION__
, tmp
);
2856 RTMP_Log(RTMP_LOGDEBUG
, "%s, SWFVerification ping received: ", __FUNCTION__
);
2857 if (packet
->m_nBodySize
> 2 && packet
->m_body
[2] > 0x01)
2859 RTMP_Log(RTMP_LOGERROR
,
2860 "%s: SWFVerification Type %d request not supported! Patches welcome...",
2861 __FUNCTION__
, packet
->m_body
[2]);
2864 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
2866 /* respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied */
2867 else if (r
->Link
.SWFSize
)
2869 RTMP_SendCtrl(r
, 0x1B, 0, 0);
2873 RTMP_Log(RTMP_LOGERROR
,
2874 "%s: Ignoring SWFVerification request, use --swfVfy!",
2878 RTMP_Log(RTMP_LOGERROR
,
2879 "%s: Ignoring SWFVerification request, no CRYPTO support!",
2886 HandleServerBW(RTMP
*r
, const RTMPPacket
*packet
)
2888 r
->m_nServerBW
= AMF_DecodeInt32(packet
->m_body
);
2889 RTMP_Log(RTMP_LOGDEBUG
, "%s: server BW = %d", __FUNCTION__
, r
->m_nServerBW
);
2893 HandleClientBW(RTMP
*r
, const RTMPPacket
*packet
)
2895 r
->m_nClientBW
= AMF_DecodeInt32(packet
->m_body
);
2896 if (packet
->m_nBodySize
> 4)
2897 r
->m_nClientBW2
= packet
->m_body
[4];
2899 r
->m_nClientBW2
= -1;
2900 RTMP_Log(RTMP_LOGDEBUG
, "%s: client BW = %d %d", __FUNCTION__
, r
->m_nClientBW
,
2905 DecodeInt32LE(const char *data
)
2907 unsigned char *c
= (unsigned char *)data
;
2910 val
= (c
[3] << 24) | (c
[2] << 16) | (c
[1] << 8) | c
[0];
2915 EncodeInt32LE(char *output
, int nVal
)
2928 RTMP_ReadPacket(RTMP
*r
, RTMPPacket
*packet
)
2930 uint8_t hbuf
[RTMP_MAX_HEADER_SIZE
] = { 0 };
2931 char *header
= (char *)hbuf
;
2932 int nSize
, hSize
, nToRead
, nChunk
;
2933 int didAlloc
= FALSE
;
2935 RTMP_Log(RTMP_LOGDEBUG2
, "%s: fd=%d", __FUNCTION__
, r
->m_sb
.sb_socket
);
2937 if (ReadN(r
, (char *)hbuf
, 1) == 0)
2939 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header", __FUNCTION__
);
2943 packet
->m_headerType
= (hbuf
[0] & 0xc0) >> 6;
2944 packet
->m_nChannel
= (hbuf
[0] & 0x3f);
2946 if (packet
->m_nChannel
== 0)
2948 if (ReadN(r
, (char *)&hbuf
[1], 1) != 1)
2950 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header 2nd byte",
2954 packet
->m_nChannel
= hbuf
[1];
2955 packet
->m_nChannel
+= 64;
2958 else if (packet
->m_nChannel
== 1)
2961 if (ReadN(r
, (char *)&hbuf
[1], 2) != 2)
2963 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header 3nd byte",
2967 tmp
= (hbuf
[2] << 8) + hbuf
[1];
2968 packet
->m_nChannel
= tmp
+ 64;
2969 RTMP_Log(RTMP_LOGDEBUG
, "%s, m_nChannel: %0x", __FUNCTION__
, packet
->m_nChannel
);
2973 nSize
= packetSize
[packet
->m_headerType
];
2975 if (nSize
== RTMP_LARGE_HEADER_SIZE
) /* if we get a full header the timestamp is absolute */
2976 packet
->m_hasAbsTimestamp
= TRUE
;
2978 else if (nSize
< RTMP_LARGE_HEADER_SIZE
)
2979 { /* using values from the last message of this channel */
2980 if (r
->m_vecChannelsIn
[packet
->m_nChannel
])
2981 memcpy(packet
, r
->m_vecChannelsIn
[packet
->m_nChannel
],
2982 sizeof(RTMPPacket
));
2987 if (nSize
> 0 && ReadN(r
, header
, nSize
) != nSize
)
2989 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header. type: %x",
2990 __FUNCTION__
, (unsigned int)hbuf
[0]);
2994 hSize
= nSize
+ (header
- (char *)hbuf
);
2998 packet
->m_nTimeStamp
= AMF_DecodeInt24(header
);
3000 /*RTMP_Log(RTMP_LOGDEBUG, "%s, reading RTMP packet chunk on channel %x, headersz %i, timestamp %i, abs timestamp %i", __FUNCTION__, packet.m_nChannel, nSize, packet.m_nTimeStamp, packet.m_hasAbsTimestamp); */
3004 packet
->m_nBodySize
= AMF_DecodeInt24(header
+ 3);
3005 packet
->m_nBytesRead
= 0;
3006 RTMPPacket_Free(packet
);
3010 packet
->m_packetType
= header
[6];
3013 packet
->m_nInfoField2
= DecodeInt32LE(header
+ 7);
3016 if (packet
->m_nTimeStamp
== 0xffffff)
3018 if (ReadN(r
, header
+ nSize
, 4) != 4)
3020 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read extended timestamp",
3024 packet
->m_nTimeStamp
= AMF_DecodeInt32(header
+ nSize
);
3029 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)hbuf
, hSize
);
3031 if (packet
->m_nBodySize
> 0 && packet
->m_body
== NULL
)
3033 if (!RTMPPacket_Alloc(packet
, packet
->m_nBodySize
))
3035 RTMP_Log(RTMP_LOGDEBUG
, "%s, failed to allocate packet", __FUNCTION__
);
3039 packet
->m_headerType
= (hbuf
[0] & 0xc0) >> 6;
3042 nToRead
= packet
->m_nBodySize
- packet
->m_nBytesRead
;
3043 nChunk
= r
->m_inChunkSize
;
3044 if (nToRead
< nChunk
)
3047 /* Does the caller want the raw chunk? */
3048 if (packet
->m_chunk
)
3050 packet
->m_chunk
->c_headerSize
= hSize
;
3051 memcpy(packet
->m_chunk
->c_header
, hbuf
, hSize
);
3052 packet
->m_chunk
->c_chunk
= packet
->m_body
+ packet
->m_nBytesRead
;
3053 packet
->m_chunk
->c_chunkSize
= nChunk
;
3056 if (ReadN(r
, packet
->m_body
+ packet
->m_nBytesRead
, nChunk
) != nChunk
)
3058 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet body. len: %lu",
3059 __FUNCTION__
, packet
->m_nBodySize
);
3063 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)packet
->m_body
+ packet
->m_nBytesRead
, nChunk
);
3065 packet
->m_nBytesRead
+= nChunk
;
3067 /* keep the packet as ref for other packets on this channel */
3068 if (!r
->m_vecChannelsIn
[packet
->m_nChannel
])
3069 r
->m_vecChannelsIn
[packet
->m_nChannel
] = malloc(sizeof(RTMPPacket
));
3070 memcpy(r
->m_vecChannelsIn
[packet
->m_nChannel
], packet
, sizeof(RTMPPacket
));
3072 if (RTMPPacket_IsReady(packet
))
3074 /* make packet's timestamp absolute */
3075 if (!packet
->m_hasAbsTimestamp
)
3076 packet
->m_nTimeStamp
+= r
->m_channelTimestamp
[packet
->m_nChannel
]; /* timestamps seem to be always relative!! */
3078 r
->m_channelTimestamp
[packet
->m_nChannel
] = packet
->m_nTimeStamp
;
3080 /* reset the data from the stored packet. we keep the header since we may use it later if a new packet for this channel */
3081 /* arrives and requests to re-use some info (small packet header) */
3082 r
->m_vecChannelsIn
[packet
->m_nChannel
]->m_body
= NULL
;
3083 r
->m_vecChannelsIn
[packet
->m_nChannel
]->m_nBytesRead
= 0;
3084 r
->m_vecChannelsIn
[packet
->m_nChannel
]->m_hasAbsTimestamp
= FALSE
; /* can only be false if we reuse header */
3088 packet
->m_body
= NULL
; /* so it won't be erased on free */
3096 HandShake(RTMP
*r
, int FP9HandShake
)
3099 uint32_t uptime
, suptime
;
3102 char clientbuf
[RTMP_SIG_SIZE
+ 1], *clientsig
= clientbuf
+ 1;
3103 char serversig
[RTMP_SIG_SIZE
];
3105 clientbuf
[0] = 0x03; /* not encrypted */
3107 uptime
= htonl(RTMP_GetTime());
3108 memcpy(clientsig
, &uptime
, 4);
3110 memset(&clientsig
[4], 0, 4);
3113 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3114 clientsig
[i
] = 0xff;
3116 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3117 clientsig
[i
] = (char)(rand() % 256);
3120 if (!WriteN(r
, clientbuf
, RTMP_SIG_SIZE
+ 1))
3123 if (ReadN(r
, &type
, 1) != 1) /* 0x03 or 0x06 */
3126 RTMP_Log(RTMP_LOGDEBUG
, "%s: Type Answer : %02X", __FUNCTION__
, type
);
3128 if (type
!= clientbuf
[0])
3129 RTMP_Log(RTMP_LOGWARNING
, "%s: Type mismatch: client sent %d, server answered %d",
3130 __FUNCTION__
, clientbuf
[0], type
);
3132 if (ReadN(r
, serversig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3135 /* decode server response */
3137 memcpy(&suptime
, serversig
, 4);
3138 suptime
= ntohl(suptime
);
3140 RTMP_Log(RTMP_LOGDEBUG
, "%s: Server Uptime : %d", __FUNCTION__
, suptime
);
3141 RTMP_Log(RTMP_LOGDEBUG
, "%s: FMS Version : %d.%d.%d.%d", __FUNCTION__
,
3142 serversig
[4], serversig
[5], serversig
[6], serversig
[7]);
3144 /* 2nd part of handshake */
3145 if (!WriteN(r
, serversig
, RTMP_SIG_SIZE
))
3148 if (ReadN(r
, serversig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3151 bMatch
= (memcmp(serversig
, clientsig
, RTMP_SIG_SIZE
) == 0);
3154 RTMP_Log(RTMP_LOGWARNING
, "%s, client signature does not match!", __FUNCTION__
);
3163 char serverbuf
[RTMP_SIG_SIZE
+ 1], *serversig
= serverbuf
+ 1;
3164 char clientsig
[RTMP_SIG_SIZE
];
3168 if (ReadN(r
, serverbuf
, 1) != 1) /* 0x03 or 0x06 */
3171 RTMP_Log(RTMP_LOGDEBUG
, "%s: Type Request : %02X", __FUNCTION__
, serverbuf
[0]);
3173 if (serverbuf
[0] != 3)
3175 RTMP_Log(RTMP_LOGERROR
, "%s: Type unknown: client sent %02X",
3176 __FUNCTION__
, serverbuf
[0]);
3180 uptime
= htonl(RTMP_GetTime());
3181 memcpy(serversig
, &uptime
, 4);
3183 memset(&serversig
[4], 0, 4);
3185 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3186 serversig
[i
] = 0xff;
3188 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3189 serversig
[i
] = (char)(rand() % 256);
3192 if (!WriteN(r
, serverbuf
, RTMP_SIG_SIZE
+ 1))
3195 if (ReadN(r
, clientsig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3198 /* decode client response */
3200 memcpy(&uptime
, clientsig
, 4);
3201 uptime
= ntohl(uptime
);
3203 RTMP_Log(RTMP_LOGDEBUG
, "%s: Client Uptime : %d", __FUNCTION__
, uptime
);
3204 RTMP_Log(RTMP_LOGDEBUG
, "%s: Player Version: %d.%d.%d.%d", __FUNCTION__
,
3205 clientsig
[4], clientsig
[5], clientsig
[6], clientsig
[7]);
3207 /* 2nd part of handshake */
3208 if (!WriteN(r
, clientsig
, RTMP_SIG_SIZE
))
3211 if (ReadN(r
, clientsig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3214 bMatch
= (memcmp(serversig
, clientsig
, RTMP_SIG_SIZE
) == 0);
3217 RTMP_Log(RTMP_LOGWARNING
, "%s, client signature does not match!", __FUNCTION__
);
3224 RTMP_SendChunk(RTMP
*r
, RTMPChunk
*chunk
)
3227 char hbuf
[RTMP_MAX_HEADER_SIZE
];
3229 RTMP_Log(RTMP_LOGDEBUG2
, "%s: fd=%d, size=%d", __FUNCTION__
, r
->m_sb
.sb_socket
,
3230 chunk
->c_chunkSize
);
3231 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)chunk
->c_header
, chunk
->c_headerSize
);
3232 if (chunk
->c_chunkSize
)
3234 char *ptr
= chunk
->c_chunk
- chunk
->c_headerSize
;
3235 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)chunk
->c_chunk
, chunk
->c_chunkSize
);
3236 /* save header bytes we're about to overwrite */
3237 memcpy(hbuf
, ptr
, chunk
->c_headerSize
);
3238 memcpy(ptr
, chunk
->c_header
, chunk
->c_headerSize
);
3239 wrote
= WriteN(r
, ptr
, chunk
->c_headerSize
+ chunk
->c_chunkSize
);
3240 memcpy(ptr
, hbuf
, chunk
->c_headerSize
);
3243 wrote
= WriteN(r
, chunk
->c_header
, chunk
->c_headerSize
);
3248 RTMP_SendPacket(RTMP
*r
, RTMPPacket
*packet
, int queue
)
3250 const RTMPPacket
*prevPacket
= r
->m_vecChannelsOut
[packet
->m_nChannel
];
3254 char *header
, *hptr
, *hend
, hbuf
[RTMP_MAX_HEADER_SIZE
], c
;
3256 char *buffer
, *tbuf
= NULL
, *toff
= NULL
;
3260 if (prevPacket
&& packet
->m_headerType
!= RTMP_PACKET_SIZE_LARGE
)
3262 /* compress a bit by using the prev packet's attributes */
3263 if (prevPacket
->m_nBodySize
== packet
->m_nBodySize
3264 && prevPacket
->m_packetType
== packet
->m_packetType
3265 && packet
->m_headerType
== RTMP_PACKET_SIZE_MEDIUM
)
3266 packet
->m_headerType
= RTMP_PACKET_SIZE_SMALL
;
3268 if (prevPacket
->m_nTimeStamp
== packet
->m_nTimeStamp
3269 && packet
->m_headerType
== RTMP_PACKET_SIZE_SMALL
)
3270 packet
->m_headerType
= RTMP_PACKET_SIZE_MINIMUM
;
3271 last
= prevPacket
->m_nTimeStamp
;
3274 if (packet
->m_headerType
> 3) /* sanity */
3276 RTMP_Log(RTMP_LOGERROR
, "sanity failed!! trying to send header of type: 0x%02x.",
3277 (unsigned char)packet
->m_headerType
);
3281 nSize
= packetSize
[packet
->m_headerType
];
3282 hSize
= nSize
; cSize
= 0;
3283 t
= packet
->m_nTimeStamp
- last
;
3287 header
= packet
->m_body
- nSize
;
3288 hend
= packet
->m_body
;
3293 hend
= hbuf
+ sizeof(hbuf
);
3296 if (packet
->m_nChannel
> 319)
3298 else if (packet
->m_nChannel
> 63)
3306 if (nSize
> 1 && t
>= 0xffffff)
3313 c
= packet
->m_headerType
<< 6;
3317 c
|= packet
->m_nChannel
;
3328 int tmp
= packet
->m_nChannel
- 64;
3329 *hptr
++ = tmp
& 0xff;
3336 hptr
= AMF_EncodeInt24(hptr
, hend
, t
> 0xffffff ? 0xffffff : t
);
3341 hptr
= AMF_EncodeInt24(hptr
, hend
, packet
->m_nBodySize
);
3342 *hptr
++ = packet
->m_packetType
;
3346 hptr
+= EncodeInt32LE(hptr
, packet
->m_nInfoField2
);
3348 if (nSize
> 1 && t
>= 0xffffff)
3349 hptr
= AMF_EncodeInt32(hptr
, hend
, t
);
3351 nSize
= packet
->m_nBodySize
;
3352 buffer
= packet
->m_body
;
3353 nChunkSize
= r
->m_outChunkSize
;
3355 RTMP_Log(RTMP_LOGDEBUG2
, "%s: fd=%d, size=%d", __FUNCTION__
, r
->m_sb
.sb_socket
,
3357 /* send all chunks in one HTTP request */
3358 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
3360 int chunks
= (nSize
+nChunkSize
-1) / nChunkSize
;
3363 tlen
= chunks
* (cSize
+ 1) + nSize
+ hSize
;
3364 tbuf
= malloc(tlen
);
3370 while (nSize
+ hSize
)
3374 if (nSize
< nChunkSize
)
3377 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)header
, hSize
);
3378 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)buffer
, nChunkSize
);
3381 memcpy(toff
, header
, nChunkSize
+ hSize
);
3382 toff
+= nChunkSize
+ hSize
;
3386 wrote
= WriteN(r
, header
, nChunkSize
+ hSize
);
3390 nSize
-= nChunkSize
;
3391 buffer
+= nChunkSize
;
3396 header
= buffer
- 1;
3403 *header
= (0xc0 | c
);
3406 int tmp
= packet
->m_nChannel
- 64;
3407 header
[1] = tmp
& 0xff;
3409 header
[2] = tmp
>> 8;
3415 int wrote
= WriteN(r
, tbuf
, toff
-tbuf
);
3422 /* we invoked a remote method */
3423 if (packet
->m_packetType
== RTMP_PACKET_TYPE_INVOKE
)
3427 ptr
= packet
->m_body
+ 1;
3428 AMF_DecodeString(ptr
, &method
);
3429 RTMP_Log(RTMP_LOGDEBUG
, "Invoking %s", method
.av_val
);
3430 /* keep it in call queue till result arrives */
3433 ptr
+= 3 + method
.av_len
;
3434 txn
= (int)AMF_DecodeNumber(ptr
);
3435 AV_queue(&r
->m_methodCalls
, &r
->m_numCalls
, &method
, txn
);
3439 if (!r
->m_vecChannelsOut
[packet
->m_nChannel
])
3440 r
->m_vecChannelsOut
[packet
->m_nChannel
] = malloc(sizeof(RTMPPacket
));
3441 memcpy(r
->m_vecChannelsOut
[packet
->m_nChannel
], packet
, sizeof(RTMPPacket
));
3448 return SHandShake(r
);
3456 if (RTMP_IsConnected(r
))
3458 if (r
->m_stream_id
> 0)
3462 if ((r
->Link
.protocol
& RTMP_FEATURE_WRITE
))
3464 SendDeleteStream(r
, i
);
3466 if (r
->m_clientID
.av_val
)
3468 HTTP_Post(r
, RTMPT_CLOSE
, "", 1);
3469 free(r
->m_clientID
.av_val
);
3470 r
->m_clientID
.av_val
= NULL
;
3471 r
->m_clientID
.av_len
= 0;
3473 RTMPSockBuf_Close(&r
->m_sb
);
3476 r
->m_stream_id
= -1;
3477 r
->m_sb
.sb_socket
= -1;
3478 r
->m_nBWCheckCounter
= 0;
3480 r
->m_nBytesInSent
= 0;
3482 if (r
->m_read
.flags
& RTMP_READ_HEADER
) {
3483 free(r
->m_read
.buf
);
3484 r
->m_read
.buf
= NULL
;
3486 r
->m_read
.dataType
= 0;
3487 r
->m_read
.flags
= 0;
3488 r
->m_read
.status
= 0;
3489 r
->m_read
.nResumeTS
= 0;
3490 r
->m_read
.nIgnoredFrameCounter
= 0;
3491 r
->m_read
.nIgnoredFlvFrameCounter
= 0;
3493 r
->m_write
.m_nBytesRead
= 0;
3494 RTMPPacket_Free(&r
->m_write
);
3496 for (i
= 0; i
< RTMP_CHANNELS
; i
++)
3498 if (r
->m_vecChannelsIn
[i
])
3500 RTMPPacket_Free(r
->m_vecChannelsIn
[i
]);
3501 free(r
->m_vecChannelsIn
[i
]);
3502 r
->m_vecChannelsIn
[i
] = NULL
;
3504 if (r
->m_vecChannelsOut
[i
])
3506 free(r
->m_vecChannelsOut
[i
]);
3507 r
->m_vecChannelsOut
[i
] = NULL
;
3510 AV_clear(r
->m_methodCalls
, r
->m_numCalls
);
3511 r
->m_methodCalls
= NULL
;
3513 r
->m_numInvokes
= 0;
3515 r
->m_bPlaying
= FALSE
;
3516 r
->m_sb
.sb_size
= 0;
3518 r
->m_msgCounter
= 0;
3522 free(r
->Link
.playpath0
.av_val
);
3523 r
->Link
.playpath0
.av_val
= NULL
;
3525 if (r
->Link
.lFlags
& RTMP_LF_FTCU
)
3527 free(r
->Link
.tcUrl
.av_val
);
3528 r
->Link
.tcUrl
.av_val
= NULL
;
3529 r
->Link
.lFlags
^= RTMP_LF_FTCU
;
3535 MDH_free(r
->Link
.dh
);
3538 if (r
->Link
.rc4keyIn
)
3540 RC4_free(r
->Link
.rc4keyIn
);
3541 r
->Link
.rc4keyIn
= NULL
;
3543 if (r
->Link
.rc4keyOut
)
3545 RC4_free(r
->Link
.rc4keyOut
);
3546 r
->Link
.rc4keyOut
= NULL
;
3552 RTMPSockBuf_Fill(RTMPSockBuf
*sb
)
3557 sb
->sb_start
= sb
->sb_buf
;
3561 nBytes
= sizeof(sb
->sb_buf
) - sb
->sb_size
- (sb
->sb_start
- sb
->sb_buf
);
3562 #if defined(CRYPTO) && !defined(NO_SSL)
3565 nBytes
= TLS_read(sb
->sb_ssl
, sb
->sb_start
+ sb
->sb_size
, nBytes
);
3570 nBytes
= recv(sb
->sb_socket
, sb
->sb_start
+ sb
->sb_size
, nBytes
, 0);
3574 sb
->sb_size
+= nBytes
;
3578 int sockerr
= GetSockError();
3579 RTMP_Log(RTMP_LOGDEBUG
, "%s, recv returned %d. GetSockError(): %d (%s)",
3580 __FUNCTION__
, nBytes
, sockerr
, strerror(sockerr
));
3581 if (sockerr
== EINTR
&& !RTMP_ctrlC
)
3584 if (sockerr
== EWOULDBLOCK
|| sockerr
== EAGAIN
)
3586 sb
->sb_timedout
= TRUE
;
3597 RTMPSockBuf_Send(RTMPSockBuf
*sb
, const char *buf
, int len
)
3602 fwrite(buf
, 1, len
, netstackdump
);
3605 #if defined(CRYPTO) && !defined(NO_SSL)
3608 rc
= TLS_write(sb
->sb_ssl
, buf
, len
);
3613 rc
= send(sb
->sb_socket
, buf
, len
, 0);
3619 RTMPSockBuf_Close(RTMPSockBuf
*sb
)
3621 #if defined(CRYPTO) && !defined(NO_SSL)
3624 TLS_shutdown(sb
->sb_ssl
);
3625 TLS_close(sb
->sb_ssl
);
3629 if (sb
->sb_socket
!= -1)
3630 return closesocket(sb
->sb_socket
);
3634 #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
3637 DecodeTEA(AVal
*key
, AVal
*text
)
3639 uint32_t *v
, k
[4] = { 0 }, u
;
3640 uint32_t z
, y
, sum
= 0, e
, DELTA
= 0x9e3779b9;
3643 unsigned char *ptr
, *out
;
3645 /* prep key: pack 1st 16 chars into 4 LittleEndian ints */
3646 ptr
= (unsigned char *)key
->av_val
;
3650 p
= key
->av_len
> 16 ? 16 : key
->av_len
;
3651 for (i
= 0; i
< p
; i
++)
3653 u
|= ptr
[i
] << (n
* 8);
3665 /* any trailing chars */
3669 /* prep text: hex2bin, multiples of 4 */
3670 n
= (text
->av_len
+ 7) / 8;
3671 out
= malloc(n
* 8);
3672 ptr
= (unsigned char *)text
->av_val
;
3673 v
= (uint32_t *) out
;
3674 for (i
= 0; i
< n
; i
++)
3676 u
= (HEX2BIN(ptr
[0]) << 4) + HEX2BIN(ptr
[1]);
3677 u
|= ((HEX2BIN(ptr
[2]) << 4) + HEX2BIN(ptr
[3])) << 8;
3678 u
|= ((HEX2BIN(ptr
[4]) << 4) + HEX2BIN(ptr
[5])) << 16;
3679 u
|= ((HEX2BIN(ptr
[6]) << 4) + HEX2BIN(ptr
[7])) << 24;
3683 v
= (uint32_t *) out
;
3685 /* http://www.movable-type.co.uk/scripts/tea-block.html */
3686 #define MX (((z>>5)^(y<<2)) + ((y>>3)^(z<<4))) ^ ((sum^y) + (k[(p&3)^e]^z));
3694 for (p
= n
- 1; p
> 0; p
--)
3695 z
= v
[p
- 1], y
= v
[p
] -= MX
;
3702 memcpy(text
->av_val
, out
, text
->av_len
);
3707 HTTP_Post(RTMP
*r
, RTMPTCmd cmd
, const char *buf
, int len
)
3710 int hlen
= snprintf(hbuf
, sizeof(hbuf
), "POST /%s%s/%d HTTP/1.1\r\n"
3713 "User-Agent: Shockwave Flash\n"
3714 "Connection: Keep-Alive\n"
3715 "Cache-Control: no-cache\r\n"
3716 "Content-type: application/x-fcs\r\n"
3717 "Content-length: %d\r\n\r\n", RTMPT_cmds
[cmd
],
3718 r
->m_clientID
.av_val
? r
->m_clientID
.av_val
: "",
3719 r
->m_msgCounter
, r
->Link
.hostname
.av_len
, r
->Link
.hostname
.av_val
,
3721 RTMPSockBuf_Send(&r
->m_sb
, hbuf
, hlen
);
3722 hlen
= RTMPSockBuf_Send(&r
->m_sb
, buf
, len
);
3729 HTTP_read(RTMP
*r
, int fill
)
3735 RTMPSockBuf_Fill(&r
->m_sb
);
3736 if (r
->m_sb
.sb_size
< 144)
3738 if (strncmp(r
->m_sb
.sb_start
, "HTTP/1.1 200 ", 13))
3740 ptr
= r
->m_sb
.sb_start
+ sizeof("HTTP/1.1 200");
3741 while ((ptr
= strstr(ptr
, "Content-"))) {
3742 if (!strncasecmp(ptr
+8, "length:", 7)) break;
3747 hlen
= atoi(ptr
+16);
3748 ptr
= strstr(ptr
+16, "\r\n\r\n");
3752 r
->m_sb
.sb_size
-= ptr
- r
->m_sb
.sb_start
;
3753 r
->m_sb
.sb_start
= ptr
;
3756 if (!r
->m_clientID
.av_val
)
3758 r
->m_clientID
.av_len
= hlen
;
3759 r
->m_clientID
.av_val
= malloc(hlen
+1);
3760 if (!r
->m_clientID
.av_val
)
3762 r
->m_clientID
.av_val
[0] = '/';
3763 memcpy(r
->m_clientID
.av_val
+1, ptr
, hlen
-1);
3764 r
->m_clientID
.av_val
[hlen
] = 0;
3765 r
->m_sb
.sb_size
= 0;
3769 r
->m_polling
= *ptr
++;
3770 r
->m_resplen
= hlen
- 1;
3777 #define MAX_IGNORED_FRAMES 50
3779 /* Read from the stream until we get a media packet.
3780 * Returns -3 if Play.Close/Stop, -2 if fatal error, -1 if no more media
3781 * packets, 0 if ignorable error, >0 if there is a media packet
3784 Read_1_Packet(RTMP
*r
, char *buf
, unsigned int buflen
)
3786 uint32_t prevTagSize
= 0;
3787 int rtnGetNextMediaPacket
= 0, ret
= RTMP_READ_EOF
;
3788 RTMPPacket packet
= { 0 };
3792 uint32_t nTimeStamp
= 0;
3795 rtnGetNextMediaPacket
= RTMP_GetNextMediaPacket(r
, &packet
);
3796 while (rtnGetNextMediaPacket
)
3798 char *packetBody
= packet
.m_body
;
3799 unsigned int nPacketLen
= packet
.m_nBodySize
;
3801 /* Return RTMP_READ_COMPLETE if this was completed nicely with
3802 * invoke message Play.Stop or Play.Complete
3804 if (rtnGetNextMediaPacket
== 2)
3806 RTMP_Log(RTMP_LOGDEBUG
,
3807 "Got Play.Complete or Play.Stop from server. "
3808 "Assuming stream is complete");
3809 ret
= RTMP_READ_COMPLETE
;
3813 r
->m_read
.dataType
|= (((packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
) << 2) |
3814 (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
));
3816 if (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
&& nPacketLen
<= 5)
3818 RTMP_Log(RTMP_LOGDEBUG
, "ignoring too small video packet: size: %d",
3820 ret
= RTMP_READ_IGNORE
;
3823 if (packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
&& nPacketLen
<= 1)
3825 RTMP_Log(RTMP_LOGDEBUG
, "ignoring too small audio packet: size: %d",
3827 ret
= RTMP_READ_IGNORE
;
3831 if (r
->m_read
.flags
& RTMP_READ_SEEKING
)
3833 ret
= RTMP_READ_IGNORE
;
3837 RTMP_Log(RTMP_LOGDEBUG
, "type: %02X, size: %d, TS: %d ms, abs TS: %d",
3838 packet
.m_packetType
, nPacketLen
, packet
.m_nTimeStamp
,
3839 packet
.m_hasAbsTimestamp
);
3840 if (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
)
3841 RTMP_Log(RTMP_LOGDEBUG
, "frametype: %02X", (*packetBody
& 0xf0));
3844 if (r
->m_read
.flags
& RTMP_READ_RESUME
)
3846 /* check the header if we get one */
3847 if (packet
.m_nTimeStamp
== 0)
3849 if (r
->m_read
.nMetaHeaderSize
> 0
3850 && packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
)
3854 AMF_Decode(&metaObj
, packetBody
, nPacketLen
, FALSE
);
3858 AMFProp_GetString(AMF_GetProp(&metaObj
, NULL
, 0),
3861 if (AVMATCH(&metastring
, &av_onMetaData
))
3864 if ((r
->m_read
.nMetaHeaderSize
!= nPacketLen
) ||
3866 (r
->m_read
.metaHeader
, packetBody
,
3867 r
->m_read
.nMetaHeaderSize
) != 0))
3869 ret
= RTMP_READ_ERROR
;
3872 AMF_Reset(&metaObj
);
3873 if (ret
== RTMP_READ_ERROR
)
3878 /* check first keyframe to make sure we got the right position
3879 * in the stream! (the first non ignored frame)
3881 if (r
->m_read
.nInitialFrameSize
> 0)
3883 /* video or audio data */
3884 if (packet
.m_packetType
== r
->m_read
.initialFrameType
3885 && r
->m_read
.nInitialFrameSize
== nPacketLen
)
3887 /* we don't compare the sizes since the packet can
3888 * contain several FLV packets, just make sure the
3889 * first frame is our keyframe (which we are going
3893 (r
->m_read
.initialFrame
, packetBody
,
3894 r
->m_read
.nInitialFrameSize
) == 0)
3896 RTMP_Log(RTMP_LOGDEBUG
, "Checked keyframe successfully!");
3897 r
->m_read
.flags
|= RTMP_READ_GOTKF
;
3898 /* ignore it! (what about audio data after it? it is
3899 * handled by ignoring all 0ms frames, see below)
3901 ret
= RTMP_READ_IGNORE
;
3906 /* hande FLV streams, even though the server resends the
3907 * keyframe as an extra video packet it is also included
3908 * in the first FLV stream chunk and we have to compare
3909 * it and filter it out !!
3911 if (packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
3913 /* basically we have to find the keyframe with the
3914 * correct TS being nResumeTS
3916 unsigned int pos
= 0;
3919 while (pos
+ 11 < nPacketLen
)
3921 /* size without header (11) and prevTagSize (4) */
3923 AMF_DecodeInt24(packetBody
+ pos
+ 1);
3924 ts
= AMF_DecodeInt24(packetBody
+ pos
+ 4);
3925 ts
|= (packetBody
[pos
+ 7] << 24);
3928 RTMP_Log(RTMP_LOGDEBUG
,
3929 "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms",
3930 packetBody
[pos
], dataSize
, ts
);
3932 /* ok, is it a keyframe?:
3933 * well doesn't work for audio!
3935 if (packetBody
[pos
/*6928, test 0 */ ] ==
3936 r
->m_read
.initialFrameType
3937 /* && (packetBody[11]&0xf0) == 0x10 */ )
3939 if (ts
== r
->m_read
.nResumeTS
)
3941 RTMP_Log(RTMP_LOGDEBUG
,
3942 "Found keyframe with resume-keyframe timestamp!");
3943 if (r
->m_read
.nInitialFrameSize
!= dataSize
3944 || memcmp(r
->m_read
.initialFrame
,
3945 packetBody
+ pos
+ 11,
3947 nInitialFrameSize
) != 0)
3949 RTMP_Log(RTMP_LOGERROR
,
3950 "FLV Stream: Keyframe doesn't match!");
3951 ret
= RTMP_READ_ERROR
;
3954 r
->m_read
.flags
|= RTMP_READ_GOTFLVK
;
3956 /* skip this packet?
3957 * check whether skippable:
3959 if (pos
+ 11 + dataSize
+ 4 > nPacketLen
)
3961 RTMP_Log(RTMP_LOGWARNING
,
3962 "Non skipable packet since it doesn't end with chunk, stream corrupt!");
3963 ret
= RTMP_READ_ERROR
;
3966 packetBody
+= (pos
+ 11 + dataSize
+ 4);
3967 nPacketLen
-= (pos
+ 11 + dataSize
+ 4);
3969 goto stopKeyframeSearch
;
3972 else if (r
->m_read
.nResumeTS
< ts
)
3974 /* the timestamp ts will only increase with
3975 * further packets, wait for seek
3977 goto stopKeyframeSearch
;
3980 pos
+= (11 + dataSize
+ 4);
3982 if (ts
< r
->m_read
.nResumeTS
)
3984 RTMP_Log(RTMP_LOGERROR
,
3985 "First packet does not contain keyframe, all "
3986 "timestamps are smaller than the keyframe "
3987 "timestamp; probably the resume seek failed?");
3991 if (!(r
->m_read
.flags
& RTMP_READ_GOTFLVK
))
3993 RTMP_Log(RTMP_LOGERROR
,
3994 "Couldn't find the seeked keyframe in this chunk!");
3995 ret
= RTMP_READ_IGNORE
;
4002 if (packet
.m_nTimeStamp
> 0
4003 && (r
->m_read
.flags
& (RTMP_READ_GOTKF
|RTMP_READ_GOTFLVK
)))
4005 /* another problem is that the server can actually change from
4006 * 09/08 video/audio packets to an FLV stream or vice versa and
4007 * our keyframe check will prevent us from going along with the
4008 * new stream if we resumed.
4010 * in this case set the 'found keyframe' variables to true.
4011 * We assume that if we found one keyframe somewhere and were
4012 * already beyond TS > 0 we have written data to the output
4013 * which means we can accept all forthcoming data including the
4014 * change between 08/09 <-> FLV packets
4016 r
->m_read
.flags
|= (RTMP_READ_GOTKF
|RTMP_READ_GOTFLVK
);
4019 /* skip till we find our keyframe
4020 * (seeking might put us somewhere before it)
4022 if (!(r
->m_read
.flags
& RTMP_READ_GOTKF
) &&
4023 packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4025 RTMP_Log(RTMP_LOGWARNING
,
4026 "Stream does not start with requested frame, ignoring data... ");
4027 r
->m_read
.nIgnoredFrameCounter
++;
4028 if (r
->m_read
.nIgnoredFrameCounter
> MAX_IGNORED_FRAMES
)
4029 ret
= RTMP_READ_ERROR
; /* fatal error, couldn't continue stream */
4031 ret
= RTMP_READ_IGNORE
;
4034 /* ok, do the same for FLV streams */
4035 if (!(r
->m_read
.flags
& RTMP_READ_GOTFLVK
) &&
4036 packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
4038 RTMP_Log(RTMP_LOGWARNING
,
4039 "Stream does not start with requested FLV frame, ignoring data... ");
4040 r
->m_read
.nIgnoredFlvFrameCounter
++;
4041 if (r
->m_read
.nIgnoredFlvFrameCounter
> MAX_IGNORED_FRAMES
)
4042 ret
= RTMP_READ_ERROR
;
4044 ret
= RTMP_READ_IGNORE
;
4048 /* we have to ignore the 0ms frames since these are the first
4049 * keyframes; we've got these so don't mess around with multiple
4050 * copies sent by the server to us! (if the keyframe is found at a
4051 * later position there is only one copy and it will be ignored by
4052 * the preceding if clause)
4054 if (!(r
->m_read
.flags
& RTMP_READ_NO_IGNORE
) &&
4055 packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4057 /* exclude type RTMP_PACKET_TYPE_FLASH_VIDEO since it can
4058 * contain several FLV packets
4060 if (packet
.m_nTimeStamp
== 0)
4062 ret
= RTMP_READ_IGNORE
;
4067 /* stop ignoring packets */
4068 r
->m_read
.flags
|= RTMP_READ_NO_IGNORE
;
4073 /* calculate packet size and allocate slop buffer if necessary */
4075 ((packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
4076 || packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
4077 || packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
) ? 11 : 0) +
4078 (packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
? 4 : 0);
4080 if (size
+ 4 > buflen
)
4082 /* the extra 4 is for the case of an FLV stream without a last
4083 * prevTagSize (we need extra 4 bytes to append it) */
4084 r
->m_read
.buf
= malloc(size
+ 4);
4085 if (r
->m_read
.buf
== 0)
4087 RTMP_Log(RTMP_LOGERROR
, "Couldn't allocate memory!");
4088 ret
= RTMP_READ_ERROR
; /* fatal error */
4092 ptr
= r
->m_read
.buf
;
4098 pend
= ptr
+ size
+ 4;
4100 /* use to return timestamp of last processed packet */
4102 /* audio (0x08), video (0x09) or metadata (0x12) packets :
4103 * construct 11 byte header then add rtmp packet's data */
4104 if (packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
4105 || packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
4106 || packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
)
4108 nTimeStamp
= r
->m_read
.nResumeTS
+ packet
.m_nTimeStamp
;
4109 prevTagSize
= 11 + nPacketLen
;
4111 *ptr
= packet
.m_packetType
;
4113 ptr
= AMF_EncodeInt24(ptr
, pend
, nPacketLen
);
4116 if(packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
) {
4119 if((packetBody
[0] & 0x0f) == 7) { /* CodecId = H264 */
4120 uint8_t packetType
= *(packetBody
+1);
4122 uint32_t ts
= AMF_DecodeInt24(packetBody
+2); /* composition time */
4123 int32_t cts
= (ts
+0xff800000)^0xff800000;
4124 RTMP_Log(RTMP_LOGDEBUG
, "cts : %d\n", cts
);
4127 /* get rid of the composition time */
4128 CRTMP::EncodeInt24(packetBody
+2, 0);
4130 RTMP_Log(RTMP_LOGDEBUG
, "VIDEO: nTimeStamp: 0x%08X (%d)\n", nTimeStamp
, nTimeStamp
);
4134 ptr
= AMF_EncodeInt24(ptr
, pend
, nTimeStamp
);
4135 *ptr
= (char)((nTimeStamp
& 0xFF000000) >> 24);
4139 ptr
= AMF_EncodeInt24(ptr
, pend
, 0);
4142 memcpy(ptr
, packetBody
, nPacketLen
);
4145 /* correct tagSize and obtain timestamp if we have an FLV stream */
4146 if (packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
4148 unsigned int pos
= 0;
4151 /* grab first timestamp and see if it needs fixing */
4152 nTimeStamp
= AMF_DecodeInt24(packetBody
+ 4);
4153 nTimeStamp
|= (packetBody
[7] << 24);
4154 delta
= packet
.m_nTimeStamp
- nTimeStamp
+ r
->m_read
.nResumeTS
;
4156 while (pos
+ 11 < nPacketLen
)
4158 /* size without header (11) and without prevTagSize (4) */
4159 uint32_t dataSize
= AMF_DecodeInt24(packetBody
+ pos
+ 1);
4160 nTimeStamp
= AMF_DecodeInt24(packetBody
+ pos
+ 4);
4161 nTimeStamp
|= (packetBody
[pos
+ 7] << 24);
4165 nTimeStamp
+= delta
;
4166 AMF_EncodeInt24(ptr
+pos
+4, pend
, nTimeStamp
);
4167 ptr
[pos
+7] = nTimeStamp
>>24;
4171 r
->m_read
.dataType
|= (((*(packetBody
+ pos
) == 0x08) << 2) |
4172 (*(packetBody
+ pos
) == 0x09));
4174 if (pos
+ 11 + dataSize
+ 4 > nPacketLen
)
4176 if (pos
+ 11 + dataSize
> nPacketLen
)
4178 RTMP_Log(RTMP_LOGERROR
,
4179 "Wrong data size (%lu), stream corrupted, aborting!",
4181 ret
= RTMP_READ_ERROR
;
4184 RTMP_Log(RTMP_LOGWARNING
, "No tagSize found, appending!");
4186 /* we have to append a last tagSize! */
4187 prevTagSize
= dataSize
+ 11;
4188 AMF_EncodeInt32(ptr
+ pos
+ 11 + dataSize
, pend
,
4196 AMF_DecodeInt32(packetBody
+ pos
+ 11 + dataSize
);
4199 RTMP_Log(RTMP_LOGDEBUG
,
4200 "FLV Packet: type %02X, dataSize: %lu, tagSize: %lu, timeStamp: %lu ms",
4201 (unsigned char)packetBody
[pos
], dataSize
, prevTagSize
,
4205 if (prevTagSize
!= (dataSize
+ 11))
4208 RTMP_Log(RTMP_LOGWARNING
,
4209 "Tag and data size are not consitent, writing tag size according to dataSize+11: %d",
4213 prevTagSize
= dataSize
+ 11;
4214 AMF_EncodeInt32(ptr
+ pos
+ 11 + dataSize
, pend
,
4219 pos
+= prevTagSize
+ 4; /*(11+dataSize+4); */
4224 if (packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4226 /* FLV tag packets contain their own prevTagSize */
4227 AMF_EncodeInt32(ptr
, pend
, prevTagSize
);
4230 /* In non-live this nTimeStamp can contain an absolute TS.
4231 * Update ext timestamp with this absolute offset in non-live mode
4232 * otherwise report the relative one
4234 /* RTMP_Log(RTMP_LOGDEBUG, "type: %02X, size: %d, pktTS: %dms, TS: %dms, bLiveStream: %d", packet.m_packetType, nPacketLen, packet.m_nTimeStamp, nTimeStamp, r->Link.lFlags & RTMP_LF_LIVE); */
4235 r
->m_read
.timestamp
= (r
->Link
.lFlags
& RTMP_LF_LIVE
) ? packet
.m_nTimeStamp
: nTimeStamp
;
4241 if (rtnGetNextMediaPacket
)
4242 RTMPPacket_Free(&packet
);
4246 len
= ret
> buflen
? buflen
: ret
;
4247 memcpy(buf
, r
->m_read
.buf
, len
);
4248 r
->m_read
.bufpos
= r
->m_read
.buf
+ len
;
4249 r
->m_read
.buflen
= ret
- len
;
4254 static const char flvHeader
[] = { 'F', 'L', 'V', 0x01,
4255 0x00, /* 0x04 == audio, 0x01 == video */
4256 0x00, 0x00, 0x00, 0x09,
4257 0x00, 0x00, 0x00, 0x00
4260 #define HEADERBUF (128*1024)
4262 RTMP_Read(RTMP
*r
, char *buf
, int size
)
4264 int nRead
= 0, total
= 0;
4266 /* can't continue */
4268 switch (r
->m_read
.status
) {
4270 case RTMP_READ_COMPLETE
:
4272 case RTMP_READ_ERROR
: /* corrupted stream, resume failed */
4273 SetSockError(EINVAL
);
4279 /* first time thru */
4280 if (!(r
->m_read
.flags
& RTMP_READ_HEADER
))
4282 if (!(r
->m_read
.flags
& RTMP_READ_RESUME
))
4284 char *mybuf
= malloc(HEADERBUF
), *end
= mybuf
+ HEADERBUF
;
4286 r
->m_read
.buf
= mybuf
;
4287 r
->m_read
.buflen
= HEADERBUF
;
4289 memcpy(mybuf
, flvHeader
, sizeof(flvHeader
));
4290 r
->m_read
.buf
+= sizeof(flvHeader
);
4291 r
->m_read
.buflen
-= sizeof(flvHeader
);
4293 while (r
->m_read
.timestamp
== 0)
4295 nRead
= Read_1_Packet(r
, r
->m_read
.buf
, r
->m_read
.buflen
);
4299 r
->m_read
.buf
= NULL
;
4300 r
->m_read
.buflen
= 0;
4301 r
->m_read
.status
= nRead
;
4304 /* buffer overflow, fix buffer and give up */
4305 if (r
->m_read
.buf
< mybuf
|| r
->m_read
.buf
> end
) {
4306 mybuf
= realloc(mybuf
, cnt
+ nRead
);
4307 memcpy(mybuf
+cnt
, r
->m_read
.buf
, nRead
);
4308 r
->m_read
.buf
= mybuf
+cnt
+nRead
;
4312 r
->m_read
.buf
+= nRead
;
4313 r
->m_read
.buflen
-= nRead
;
4314 if (r
->m_read
.dataType
== 5)
4317 mybuf
[4] = r
->m_read
.dataType
;
4318 r
->m_read
.buflen
= r
->m_read
.buf
- mybuf
;
4319 r
->m_read
.buf
= mybuf
;
4320 r
->m_read
.bufpos
= mybuf
;
4322 r
->m_read
.flags
|= RTMP_READ_HEADER
;
4325 if ((r
->m_read
.flags
& RTMP_READ_SEEKING
) && r
->m_read
.buf
)
4327 /* drop whatever's here */
4328 free(r
->m_read
.buf
);
4329 r
->m_read
.buf
= NULL
;
4330 r
->m_read
.bufpos
= NULL
;
4331 r
->m_read
.buflen
= 0;
4334 /* If there's leftover data buffered, use it up */
4337 nRead
= r
->m_read
.buflen
;
4340 memcpy(buf
, r
->m_read
.bufpos
, nRead
);
4341 r
->m_read
.buflen
-= nRead
;
4342 if (!r
->m_read
.buflen
)
4344 free(r
->m_read
.buf
);
4345 r
->m_read
.buf
= NULL
;
4346 r
->m_read
.bufpos
= NULL
;
4350 r
->m_read
.bufpos
+= nRead
;
4357 while (size
> 0 && (nRead
= Read_1_Packet(r
, buf
, size
)) >= 0)
4359 if (!nRead
) continue;
4366 r
->m_read
.status
= nRead
;
4373 static const AVal av_setDataFrame
= AVC("@setDataFrame");
4376 RTMP_Write(RTMP
*r
, const char *buf
, int size
)
4378 RTMPPacket
*pkt
= &r
->m_write
;
4380 int s2
= size
, ret
, num
;
4382 pkt
->m_nChannel
= 0x04; /* source channel */
4383 pkt
->m_nInfoField2
= r
->m_stream_id
;
4387 if (!pkt
->m_nBytesRead
)
4390 /* FLV pkt too small */
4394 if (buf
[0] == 'F' && buf
[1] == 'L' && buf
[2] == 'V')
4400 pkt
->m_packetType
= *buf
++;
4401 pkt
->m_nBodySize
= AMF_DecodeInt24(buf
);
4403 pkt
->m_nTimeStamp
= AMF_DecodeInt24(buf
);
4405 pkt
->m_nTimeStamp
|= *buf
++ << 24;
4409 if (((pkt
->m_packetType
== RTMP_PACKET_TYPE_AUDIO
4410 || pkt
->m_packetType
== RTMP_PACKET_TYPE_VIDEO
) &&
4411 !pkt
->m_nTimeStamp
) || pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
4413 pkt
->m_headerType
= RTMP_PACKET_SIZE_LARGE
;
4414 if (pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
4415 pkt
->m_nBodySize
+= 16;
4419 pkt
->m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
4422 if (!RTMPPacket_Alloc(pkt
, pkt
->m_nBodySize
))
4424 RTMP_Log(RTMP_LOGDEBUG
, "%s, failed to allocate packet", __FUNCTION__
);
4428 pend
= enc
+ pkt
->m_nBodySize
;
4429 if (pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
4431 enc
= AMF_EncodeString(enc
, pend
, &av_setDataFrame
);
4432 pkt
->m_nBytesRead
= enc
- pkt
->m_body
;
4437 enc
= pkt
->m_body
+ pkt
->m_nBytesRead
;
4439 num
= pkt
->m_nBodySize
- pkt
->m_nBytesRead
;
4442 memcpy(enc
, buf
, num
);
4443 pkt
->m_nBytesRead
+= num
;
4446 if (pkt
->m_nBytesRead
== pkt
->m_nBodySize
)
4448 ret
= RTMP_SendPacket(r
, pkt
, FALSE
);
4449 RTMPPacket_Free(pkt
);
4450 pkt
->m_nBytesRead
= 0;