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
= (int)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
== 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 return closesocket(sb
->sb_socket
);
3632 #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
3635 DecodeTEA(AVal
*key
, AVal
*text
)
3637 uint32_t *v
, k
[4] = { 0 }, u
;
3638 uint32_t z
, y
, sum
= 0, e
, DELTA
= 0x9e3779b9;
3641 unsigned char *ptr
, *out
;
3643 /* prep key: pack 1st 16 chars into 4 LittleEndian ints */
3644 ptr
= (unsigned char *)key
->av_val
;
3648 p
= key
->av_len
> 16 ? 16 : key
->av_len
;
3649 for (i
= 0; i
< p
; i
++)
3651 u
|= ptr
[i
] << (n
* 8);
3663 /* any trailing chars */
3667 /* prep text: hex2bin, multiples of 4 */
3668 n
= (text
->av_len
+ 7) / 8;
3669 out
= malloc(n
* 8);
3670 ptr
= (unsigned char *)text
->av_val
;
3671 v
= (uint32_t *) out
;
3672 for (i
= 0; i
< n
; i
++)
3674 u
= (HEX2BIN(ptr
[0]) << 4) + HEX2BIN(ptr
[1]);
3675 u
|= ((HEX2BIN(ptr
[2]) << 4) + HEX2BIN(ptr
[3])) << 8;
3676 u
|= ((HEX2BIN(ptr
[4]) << 4) + HEX2BIN(ptr
[5])) << 16;
3677 u
|= ((HEX2BIN(ptr
[6]) << 4) + HEX2BIN(ptr
[7])) << 24;
3681 v
= (uint32_t *) out
;
3683 /* http://www.movable-type.co.uk/scripts/tea-block.html */
3684 #define MX (((z>>5)^(y<<2)) + ((y>>3)^(z<<4))) ^ ((sum^y) + (k[(p&3)^e]^z));
3692 for (p
= n
- 1; p
> 0; p
--)
3693 z
= v
[p
- 1], y
= v
[p
] -= MX
;
3700 memcpy(text
->av_val
, out
, text
->av_len
);
3705 HTTP_Post(RTMP
*r
, RTMPTCmd cmd
, const char *buf
, int len
)
3708 int hlen
= snprintf(hbuf
, sizeof(hbuf
), "POST /%s%s/%d HTTP/1.1\r\n"
3711 "User-Agent: Shockwave Flash\n"
3712 "Connection: Keep-Alive\n"
3713 "Cache-Control: no-cache\r\n"
3714 "Content-type: application/x-fcs\r\n"
3715 "Content-length: %d\r\n\r\n", RTMPT_cmds
[cmd
],
3716 r
->m_clientID
.av_val
? r
->m_clientID
.av_val
: "",
3717 r
->m_msgCounter
, r
->Link
.hostname
.av_len
, r
->Link
.hostname
.av_val
,
3719 RTMPSockBuf_Send(&r
->m_sb
, hbuf
, hlen
);
3720 hlen
= RTMPSockBuf_Send(&r
->m_sb
, buf
, len
);
3727 HTTP_read(RTMP
*r
, int fill
)
3733 RTMPSockBuf_Fill(&r
->m_sb
);
3734 if (r
->m_sb
.sb_size
< 144)
3736 if (strncmp(r
->m_sb
.sb_start
, "HTTP/1.1 200 ", 13))
3738 ptr
= r
->m_sb
.sb_start
+ sizeof("HTTP/1.1 200");
3739 while ((ptr
= strstr(ptr
, "Content-"))) {
3740 if (!strncasecmp(ptr
+8, "length:", 7)) break;
3745 hlen
= atoi(ptr
+16);
3746 ptr
= strstr(ptr
+16, "\r\n\r\n");
3750 r
->m_sb
.sb_size
-= ptr
- r
->m_sb
.sb_start
;
3751 r
->m_sb
.sb_start
= ptr
;
3754 if (!r
->m_clientID
.av_val
)
3756 r
->m_clientID
.av_len
= hlen
;
3757 r
->m_clientID
.av_val
= malloc(hlen
+1);
3758 if (!r
->m_clientID
.av_val
)
3760 r
->m_clientID
.av_val
[0] = '/';
3761 memcpy(r
->m_clientID
.av_val
+1, ptr
, hlen
-1);
3762 r
->m_clientID
.av_val
[hlen
] = 0;
3763 r
->m_sb
.sb_size
= 0;
3767 r
->m_polling
= *ptr
++;
3768 r
->m_resplen
= hlen
- 1;
3775 #define MAX_IGNORED_FRAMES 50
3777 /* Read from the stream until we get a media packet.
3778 * Returns -3 if Play.Close/Stop, -2 if fatal error, -1 if no more media
3779 * packets, 0 if ignorable error, >0 if there is a media packet
3782 Read_1_Packet(RTMP
*r
, char *buf
, unsigned int buflen
)
3784 uint32_t prevTagSize
= 0;
3785 int rtnGetNextMediaPacket
= 0, ret
= RTMP_READ_EOF
;
3786 RTMPPacket packet
= { 0 };
3790 uint32_t nTimeStamp
= 0;
3793 rtnGetNextMediaPacket
= RTMP_GetNextMediaPacket(r
, &packet
);
3794 while (rtnGetNextMediaPacket
)
3796 char *packetBody
= packet
.m_body
;
3797 unsigned int nPacketLen
= packet
.m_nBodySize
;
3799 /* Return RTMP_READ_COMPLETE if this was completed nicely with
3800 * invoke message Play.Stop or Play.Complete
3802 if (rtnGetNextMediaPacket
== 2)
3804 RTMP_Log(RTMP_LOGDEBUG
,
3805 "Got Play.Complete or Play.Stop from server. "
3806 "Assuming stream is complete");
3807 ret
= RTMP_READ_COMPLETE
;
3811 r
->m_read
.dataType
|= (((packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
) << 2) |
3812 (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
));
3814 if (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
&& nPacketLen
<= 5)
3816 RTMP_Log(RTMP_LOGDEBUG
, "ignoring too small video packet: size: %d",
3818 ret
= RTMP_READ_IGNORE
;
3821 if (packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
&& nPacketLen
<= 1)
3823 RTMP_Log(RTMP_LOGDEBUG
, "ignoring too small audio packet: size: %d",
3825 ret
= RTMP_READ_IGNORE
;
3829 if (r
->m_read
.flags
& RTMP_READ_SEEKING
)
3831 ret
= RTMP_READ_IGNORE
;
3835 RTMP_Log(RTMP_LOGDEBUG
, "type: %02X, size: %d, TS: %d ms, abs TS: %d",
3836 packet
.m_packetType
, nPacketLen
, packet
.m_nTimeStamp
,
3837 packet
.m_hasAbsTimestamp
);
3838 if (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
)
3839 RTMP_Log(RTMP_LOGDEBUG
, "frametype: %02X", (*packetBody
& 0xf0));
3842 if (r
->m_read
.flags
& RTMP_READ_RESUME
)
3844 /* check the header if we get one */
3845 if (packet
.m_nTimeStamp
== 0)
3847 if (r
->m_read
.nMetaHeaderSize
> 0
3848 && packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
)
3852 AMF_Decode(&metaObj
, packetBody
, nPacketLen
, FALSE
);
3856 AMFProp_GetString(AMF_GetProp(&metaObj
, NULL
, 0),
3859 if (AVMATCH(&metastring
, &av_onMetaData
))
3862 if ((r
->m_read
.nMetaHeaderSize
!= nPacketLen
) ||
3864 (r
->m_read
.metaHeader
, packetBody
,
3865 r
->m_read
.nMetaHeaderSize
) != 0))
3867 ret
= RTMP_READ_ERROR
;
3870 AMF_Reset(&metaObj
);
3871 if (ret
== RTMP_READ_ERROR
)
3876 /* check first keyframe to make sure we got the right position
3877 * in the stream! (the first non ignored frame)
3879 if (r
->m_read
.nInitialFrameSize
> 0)
3881 /* video or audio data */
3882 if (packet
.m_packetType
== r
->m_read
.initialFrameType
3883 && r
->m_read
.nInitialFrameSize
== nPacketLen
)
3885 /* we don't compare the sizes since the packet can
3886 * contain several FLV packets, just make sure the
3887 * first frame is our keyframe (which we are going
3891 (r
->m_read
.initialFrame
, packetBody
,
3892 r
->m_read
.nInitialFrameSize
) == 0)
3894 RTMP_Log(RTMP_LOGDEBUG
, "Checked keyframe successfully!");
3895 r
->m_read
.flags
|= RTMP_READ_GOTKF
;
3896 /* ignore it! (what about audio data after it? it is
3897 * handled by ignoring all 0ms frames, see below)
3899 ret
= RTMP_READ_IGNORE
;
3904 /* hande FLV streams, even though the server resends the
3905 * keyframe as an extra video packet it is also included
3906 * in the first FLV stream chunk and we have to compare
3907 * it and filter it out !!
3909 if (packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
3911 /* basically we have to find the keyframe with the
3912 * correct TS being nResumeTS
3914 unsigned int pos
= 0;
3917 while (pos
+ 11 < nPacketLen
)
3919 /* size without header (11) and prevTagSize (4) */
3921 AMF_DecodeInt24(packetBody
+ pos
+ 1);
3922 ts
= AMF_DecodeInt24(packetBody
+ pos
+ 4);
3923 ts
|= (packetBody
[pos
+ 7] << 24);
3926 RTMP_Log(RTMP_LOGDEBUG
,
3927 "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms",
3928 packetBody
[pos
], dataSize
, ts
);
3930 /* ok, is it a keyframe?:
3931 * well doesn't work for audio!
3933 if (packetBody
[pos
/*6928, test 0 */ ] ==
3934 r
->m_read
.initialFrameType
3935 /* && (packetBody[11]&0xf0) == 0x10 */ )
3937 if (ts
== r
->m_read
.nResumeTS
)
3939 RTMP_Log(RTMP_LOGDEBUG
,
3940 "Found keyframe with resume-keyframe timestamp!");
3941 if (r
->m_read
.nInitialFrameSize
!= dataSize
3942 || memcmp(r
->m_read
.initialFrame
,
3943 packetBody
+ pos
+ 11,
3945 nInitialFrameSize
) != 0)
3947 RTMP_Log(RTMP_LOGERROR
,
3948 "FLV Stream: Keyframe doesn't match!");
3949 ret
= RTMP_READ_ERROR
;
3952 r
->m_read
.flags
|= RTMP_READ_GOTFLVK
;
3954 /* skip this packet?
3955 * check whether skippable:
3957 if (pos
+ 11 + dataSize
+ 4 > nPacketLen
)
3959 RTMP_Log(RTMP_LOGWARNING
,
3960 "Non skipable packet since it doesn't end with chunk, stream corrupt!");
3961 ret
= RTMP_READ_ERROR
;
3964 packetBody
+= (pos
+ 11 + dataSize
+ 4);
3965 nPacketLen
-= (pos
+ 11 + dataSize
+ 4);
3967 goto stopKeyframeSearch
;
3970 else if (r
->m_read
.nResumeTS
< ts
)
3972 /* the timestamp ts will only increase with
3973 * further packets, wait for seek
3975 goto stopKeyframeSearch
;
3978 pos
+= (11 + dataSize
+ 4);
3980 if (ts
< r
->m_read
.nResumeTS
)
3982 RTMP_Log(RTMP_LOGERROR
,
3983 "First packet does not contain keyframe, all "
3984 "timestamps are smaller than the keyframe "
3985 "timestamp; probably the resume seek failed?");
3989 if (!(r
->m_read
.flags
& RTMP_READ_GOTFLVK
))
3991 RTMP_Log(RTMP_LOGERROR
,
3992 "Couldn't find the seeked keyframe in this chunk!");
3993 ret
= RTMP_READ_IGNORE
;
4000 if (packet
.m_nTimeStamp
> 0
4001 && (r
->m_read
.flags
& (RTMP_READ_GOTKF
|RTMP_READ_GOTFLVK
)))
4003 /* another problem is that the server can actually change from
4004 * 09/08 video/audio packets to an FLV stream or vice versa and
4005 * our keyframe check will prevent us from going along with the
4006 * new stream if we resumed.
4008 * in this case set the 'found keyframe' variables to true.
4009 * We assume that if we found one keyframe somewhere and were
4010 * already beyond TS > 0 we have written data to the output
4011 * which means we can accept all forthcoming data including the
4012 * change between 08/09 <-> FLV packets
4014 r
->m_read
.flags
|= (RTMP_READ_GOTKF
|RTMP_READ_GOTFLVK
);
4017 /* skip till we find our keyframe
4018 * (seeking might put us somewhere before it)
4020 if (!(r
->m_read
.flags
& RTMP_READ_GOTKF
) &&
4021 packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4023 RTMP_Log(RTMP_LOGWARNING
,
4024 "Stream does not start with requested frame, ignoring data... ");
4025 r
->m_read
.nIgnoredFrameCounter
++;
4026 if (r
->m_read
.nIgnoredFrameCounter
> MAX_IGNORED_FRAMES
)
4027 ret
= RTMP_READ_ERROR
; /* fatal error, couldn't continue stream */
4029 ret
= RTMP_READ_IGNORE
;
4032 /* ok, do the same for FLV streams */
4033 if (!(r
->m_read
.flags
& RTMP_READ_GOTFLVK
) &&
4034 packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
4036 RTMP_Log(RTMP_LOGWARNING
,
4037 "Stream does not start with requested FLV frame, ignoring data... ");
4038 r
->m_read
.nIgnoredFlvFrameCounter
++;
4039 if (r
->m_read
.nIgnoredFlvFrameCounter
> MAX_IGNORED_FRAMES
)
4040 ret
= RTMP_READ_ERROR
;
4042 ret
= RTMP_READ_IGNORE
;
4046 /* we have to ignore the 0ms frames since these are the first
4047 * keyframes; we've got these so don't mess around with multiple
4048 * copies sent by the server to us! (if the keyframe is found at a
4049 * later position there is only one copy and it will be ignored by
4050 * the preceding if clause)
4052 if (!(r
->m_read
.flags
& RTMP_READ_NO_IGNORE
) &&
4053 packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4055 /* exclude type RTMP_PACKET_TYPE_FLASH_VIDEO since it can
4056 * contain several FLV packets
4058 if (packet
.m_nTimeStamp
== 0)
4060 ret
= RTMP_READ_IGNORE
;
4065 /* stop ignoring packets */
4066 r
->m_read
.flags
|= RTMP_READ_NO_IGNORE
;
4071 /* calculate packet size and allocate slop buffer if necessary */
4073 ((packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
4074 || packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
4075 || packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
) ? 11 : 0) +
4076 (packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
? 4 : 0);
4078 if (size
+ 4 > buflen
)
4080 /* the extra 4 is for the case of an FLV stream without a last
4081 * prevTagSize (we need extra 4 bytes to append it) */
4082 r
->m_read
.buf
= malloc(size
+ 4);
4083 if (r
->m_read
.buf
== 0)
4085 RTMP_Log(RTMP_LOGERROR
, "Couldn't allocate memory!");
4086 ret
= RTMP_READ_ERROR
; /* fatal error */
4090 ptr
= r
->m_read
.buf
;
4096 pend
= ptr
+ size
+ 4;
4098 /* use to return timestamp of last processed packet */
4100 /* audio (0x08), video (0x09) or metadata (0x12) packets :
4101 * construct 11 byte header then add rtmp packet's data */
4102 if (packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
4103 || packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
4104 || packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
)
4106 nTimeStamp
= r
->m_read
.nResumeTS
+ packet
.m_nTimeStamp
;
4107 prevTagSize
= 11 + nPacketLen
;
4109 *ptr
= packet
.m_packetType
;
4111 ptr
= AMF_EncodeInt24(ptr
, pend
, nPacketLen
);
4114 if(packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
) {
4117 if((packetBody
[0] & 0x0f) == 7) { /* CodecId = H264 */
4118 uint8_t packetType
= *(packetBody
+1);
4120 uint32_t ts
= AMF_DecodeInt24(packetBody
+2); /* composition time */
4121 int32_t cts
= (ts
+0xff800000)^0xff800000;
4122 RTMP_Log(RTMP_LOGDEBUG
, "cts : %d\n", cts
);
4125 /* get rid of the composition time */
4126 CRTMP::EncodeInt24(packetBody
+2, 0);
4128 RTMP_Log(RTMP_LOGDEBUG
, "VIDEO: nTimeStamp: 0x%08X (%d)\n", nTimeStamp
, nTimeStamp
);
4132 ptr
= AMF_EncodeInt24(ptr
, pend
, nTimeStamp
);
4133 *ptr
= (char)((nTimeStamp
& 0xFF000000) >> 24);
4137 ptr
= AMF_EncodeInt24(ptr
, pend
, 0);
4140 memcpy(ptr
, packetBody
, nPacketLen
);
4143 /* correct tagSize and obtain timestamp if we have an FLV stream */
4144 if (packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
4146 unsigned int pos
= 0;
4149 /* grab first timestamp and see if it needs fixing */
4150 nTimeStamp
= AMF_DecodeInt24(packetBody
+ 4);
4151 nTimeStamp
|= (packetBody
[7] << 24);
4152 delta
= packet
.m_nTimeStamp
- nTimeStamp
+ r
->m_read
.nResumeTS
;
4154 while (pos
+ 11 < nPacketLen
)
4156 /* size without header (11) and without prevTagSize (4) */
4157 uint32_t dataSize
= AMF_DecodeInt24(packetBody
+ pos
+ 1);
4158 nTimeStamp
= AMF_DecodeInt24(packetBody
+ pos
+ 4);
4159 nTimeStamp
|= (packetBody
[pos
+ 7] << 24);
4163 nTimeStamp
+= delta
;
4164 AMF_EncodeInt24(ptr
+pos
+4, pend
, nTimeStamp
);
4165 ptr
[pos
+7] = nTimeStamp
>>24;
4169 r
->m_read
.dataType
|= (((*(packetBody
+ pos
) == 0x08) << 2) |
4170 (*(packetBody
+ pos
) == 0x09));
4172 if (pos
+ 11 + dataSize
+ 4 > nPacketLen
)
4174 if (pos
+ 11 + dataSize
> nPacketLen
)
4176 RTMP_Log(RTMP_LOGERROR
,
4177 "Wrong data size (%lu), stream corrupted, aborting!",
4179 ret
= RTMP_READ_ERROR
;
4182 RTMP_Log(RTMP_LOGWARNING
, "No tagSize found, appending!");
4184 /* we have to append a last tagSize! */
4185 prevTagSize
= dataSize
+ 11;
4186 AMF_EncodeInt32(ptr
+ pos
+ 11 + dataSize
, pend
,
4194 AMF_DecodeInt32(packetBody
+ pos
+ 11 + dataSize
);
4197 RTMP_Log(RTMP_LOGDEBUG
,
4198 "FLV Packet: type %02X, dataSize: %lu, tagSize: %lu, timeStamp: %lu ms",
4199 (unsigned char)packetBody
[pos
], dataSize
, prevTagSize
,
4203 if (prevTagSize
!= (dataSize
+ 11))
4206 RTMP_Log(RTMP_LOGWARNING
,
4207 "Tag and data size are not consitent, writing tag size according to dataSize+11: %d",
4211 prevTagSize
= dataSize
+ 11;
4212 AMF_EncodeInt32(ptr
+ pos
+ 11 + dataSize
, pend
,
4217 pos
+= prevTagSize
+ 4; /*(11+dataSize+4); */
4222 if (packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4224 /* FLV tag packets contain their own prevTagSize */
4225 AMF_EncodeInt32(ptr
, pend
, prevTagSize
);
4228 /* In non-live this nTimeStamp can contain an absolute TS.
4229 * Update ext timestamp with this absolute offset in non-live mode
4230 * otherwise report the relative one
4232 /* 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); */
4233 r
->m_read
.timestamp
= (r
->Link
.lFlags
& RTMP_LF_LIVE
) ? packet
.m_nTimeStamp
: nTimeStamp
;
4239 if (rtnGetNextMediaPacket
)
4240 RTMPPacket_Free(&packet
);
4244 len
= ret
> buflen
? buflen
: ret
;
4245 memcpy(buf
, r
->m_read
.buf
, len
);
4246 r
->m_read
.bufpos
= r
->m_read
.buf
+ len
;
4247 r
->m_read
.buflen
= ret
- len
;
4252 static const char flvHeader
[] = { 'F', 'L', 'V', 0x01,
4253 0x00, /* 0x04 == audio, 0x01 == video */
4254 0x00, 0x00, 0x00, 0x09,
4255 0x00, 0x00, 0x00, 0x00
4258 #define HEADERBUF (128*1024)
4260 RTMP_Read(RTMP
*r
, char *buf
, int size
)
4262 int nRead
= 0, total
= 0;
4264 /* can't continue */
4266 switch (r
->m_read
.status
) {
4268 case RTMP_READ_COMPLETE
:
4270 case RTMP_READ_ERROR
: /* corrupted stream, resume failed */
4271 SetSockError(EINVAL
);
4277 /* first time thru */
4278 if (!(r
->m_read
.flags
& RTMP_READ_HEADER
))
4280 if (!(r
->m_read
.flags
& RTMP_READ_RESUME
))
4282 char *mybuf
= malloc(HEADERBUF
), *end
= mybuf
+ HEADERBUF
;
4284 r
->m_read
.buf
= mybuf
;
4285 r
->m_read
.buflen
= HEADERBUF
;
4287 memcpy(mybuf
, flvHeader
, sizeof(flvHeader
));
4288 r
->m_read
.buf
+= sizeof(flvHeader
);
4289 r
->m_read
.buflen
-= sizeof(flvHeader
);
4291 while (r
->m_read
.timestamp
== 0)
4293 nRead
= Read_1_Packet(r
, r
->m_read
.buf
, r
->m_read
.buflen
);
4297 r
->m_read
.buf
= NULL
;
4298 r
->m_read
.buflen
= 0;
4299 r
->m_read
.status
= nRead
;
4302 /* buffer overflow, fix buffer and give up */
4303 if (r
->m_read
.buf
< mybuf
|| r
->m_read
.buf
> end
) {
4304 mybuf
= realloc(mybuf
, cnt
+ nRead
);
4305 memcpy(mybuf
+cnt
, r
->m_read
.buf
, nRead
);
4306 r
->m_read
.buf
= mybuf
+cnt
+nRead
;
4310 r
->m_read
.buf
+= nRead
;
4311 r
->m_read
.buflen
-= nRead
;
4312 if (r
->m_read
.dataType
== 5)
4315 mybuf
[4] = r
->m_read
.dataType
;
4316 r
->m_read
.buflen
= r
->m_read
.buf
- mybuf
;
4317 r
->m_read
.buf
= mybuf
;
4318 r
->m_read
.bufpos
= mybuf
;
4320 r
->m_read
.flags
|= RTMP_READ_HEADER
;
4323 if ((r
->m_read
.flags
& RTMP_READ_SEEKING
) && r
->m_read
.buf
)
4325 /* drop whatever's here */
4326 free(r
->m_read
.buf
);
4327 r
->m_read
.buf
= NULL
;
4328 r
->m_read
.bufpos
= NULL
;
4329 r
->m_read
.buflen
= 0;
4332 /* If there's leftover data buffered, use it up */
4335 nRead
= r
->m_read
.buflen
;
4338 memcpy(buf
, r
->m_read
.bufpos
, nRead
);
4339 r
->m_read
.buflen
-= nRead
;
4340 if (!r
->m_read
.buflen
)
4342 free(r
->m_read
.buf
);
4343 r
->m_read
.buf
= NULL
;
4344 r
->m_read
.bufpos
= NULL
;
4348 r
->m_read
.bufpos
+= nRead
;
4355 while (size
> 0 && (nRead
= Read_1_Packet(r
, buf
, size
)) >= 0)
4357 if (!nRead
) continue;
4364 r
->m_read
.status
= nRead
;
4371 static const AVal av_setDataFrame
= AVC("@setDataFrame");
4374 RTMP_Write(RTMP
*r
, const char *buf
, int size
)
4376 RTMPPacket
*pkt
= &r
->m_write
;
4378 int s2
= size
, ret
, num
;
4380 pkt
->m_nChannel
= 0x04; /* source channel */
4381 pkt
->m_nInfoField2
= r
->m_stream_id
;
4385 if (!pkt
->m_nBytesRead
)
4388 /* FLV pkt too small */
4392 if (buf
[0] == 'F' && buf
[1] == 'L' && buf
[2] == 'V')
4398 pkt
->m_packetType
= *buf
++;
4399 pkt
->m_nBodySize
= AMF_DecodeInt24(buf
);
4401 pkt
->m_nTimeStamp
= AMF_DecodeInt24(buf
);
4403 pkt
->m_nTimeStamp
|= *buf
++ << 24;
4407 if (((pkt
->m_packetType
== RTMP_PACKET_TYPE_AUDIO
4408 || pkt
->m_packetType
== RTMP_PACKET_TYPE_VIDEO
) &&
4409 !pkt
->m_nTimeStamp
) || pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
4411 pkt
->m_headerType
= RTMP_PACKET_SIZE_LARGE
;
4412 if (pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
4413 pkt
->m_nBodySize
+= 16;
4417 pkt
->m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
4420 if (!RTMPPacket_Alloc(pkt
, pkt
->m_nBodySize
))
4422 RTMP_Log(RTMP_LOGDEBUG
, "%s, failed to allocate packet", __FUNCTION__
);
4426 pend
= enc
+ pkt
->m_nBodySize
;
4427 if (pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
4429 enc
= AMF_EncodeString(enc
, pend
, &av_setDataFrame
);
4430 pkt
->m_nBytesRead
= enc
- pkt
->m_body
;
4435 enc
= pkt
->m_body
+ pkt
->m_nBytesRead
;
4437 num
= pkt
->m_nBodySize
- pkt
->m_nBytesRead
;
4440 memcpy(enc
, buf
, num
);
4441 pkt
->m_nBytesRead
+= num
;
4444 if (pkt
->m_nBytesRead
== pkt
->m_nBodySize
)
4446 ret
= RTMP_SendPacket(r
, pkt
, FALSE
);
4447 RTMPPacket_Free(pkt
);
4448 pkt
->m_nBytesRead
= 0;