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
37 #include <polarssl/version.h>
38 #include <polarssl/havege.h>
39 #include <polarssl/md5.h>
40 #include <polarssl/base64.h>
41 #define MD5_DIGEST_LENGTH 16
43 static const char *my_dhm_P
=
44 "E4004C1F94182000103D883A448B3F80" \
45 "2CE4B44A83301270002C20D0321CFD00" \
46 "11CCEF784C26A400F43DFB901BCA7538" \
47 "F2C6B176001CF5A0FD16D2C48B1D0C1C" \
48 "F6AC8E1DA6BCC3B4E1F96B0564965300" \
49 "FFA1D0B601EB2800F489AA512C4B248C" \
50 "01F76949A60BB7F00A40B1EAB64BDD48" \
51 "E8A700D60B7F1200FA8E77B0A979DABF";
53 static const char *my_dhm_G
= "4";
55 #elif defined(USE_GNUTLS)
56 #include <gnutls/gnutls.h>
57 #define MD5_DIGEST_LENGTH 16
58 #include <nettle/base64.h>
59 #include <nettle/md5.h>
60 #else /* USE_OPENSSL */
61 #include <openssl/ssl.h>
62 #include <openssl/rc4.h>
63 #include <openssl/md5.h>
64 #include <openssl/bio.h>
65 #include <openssl/buffer.h>
70 #define RTMP_SIG_SIZE 1536
71 #define RTMP_LARGE_HEADER_SIZE 12
73 static const int packetSize
[] = { 12, 8, 4, 1 };
77 const char RTMPProtocolStrings
[][7] = {
89 const char RTMPProtocolStringsLower
[][7] = {
101 static const char *RTMPT_cmds
[] = {
109 RTMPT_OPEN
=0, RTMPT_SEND
, RTMPT_IDLE
, RTMPT_CLOSE
112 static int DumpMetaData(AMFObject
*obj
);
113 static int HandShake(RTMP
*r
, int FP9HandShake
);
114 static int SocksNegotiate(RTMP
*r
);
116 static int SendConnectPacket(RTMP
*r
, RTMPPacket
*cp
);
117 static int SendCheckBW(RTMP
*r
);
118 static int SendCheckBWResult(RTMP
*r
, double txn
);
119 static int SendDeleteStream(RTMP
*r
, double dStreamId
);
120 static int SendFCSubscribe(RTMP
*r
, AVal
*subscribepath
);
121 static int SendPlay(RTMP
*r
);
122 static int SendBytesReceived(RTMP
*r
);
123 static int SendUsherToken(RTMP
*r
, AVal
*usherToken
);
126 static int SendBGHasStream(RTMP
*r
, double dId
, AVal
*playpath
);
129 static int HandleInvoke(RTMP
*r
, const char *body
, unsigned int nBodySize
);
130 static int HandleMetadata(RTMP
*r
, char *body
, unsigned int len
);
131 static void HandleChangeChunkSize(RTMP
*r
, const RTMPPacket
*packet
);
132 static void HandleAudio(RTMP
*r
, const RTMPPacket
*packet
);
133 static void HandleVideo(RTMP
*r
, const RTMPPacket
*packet
);
134 static void HandleCtrl(RTMP
*r
, const RTMPPacket
*packet
);
135 static void HandleServerBW(RTMP
*r
, const RTMPPacket
*packet
);
136 static void HandleClientBW(RTMP
*r
, const RTMPPacket
*packet
);
138 static int ReadN(RTMP
*r
, char *buffer
, int n
);
139 static int WriteN(RTMP
*r
, const char *buffer
, int n
);
141 static void DecodeTEA(AVal
*key
, AVal
*text
);
143 static int HTTP_Post(RTMP
*r
, RTMPTCmd cmd
, const char *buf
, int len
);
144 static int HTTP_read(RTMP
*r
, int fill
);
151 #include "handshake.h"
159 #elif defined(_WIN32)
160 return timeGetTime();
163 if (!clk_tck
) clk_tck
= sysconf(_SC_CLK_TCK
);
164 return times(&t
) * 1000 / clk_tck
;
175 RTMPPacket_Reset(RTMPPacket
*p
)
181 p
->m_nInfoField2
= 0;
182 p
->m_hasAbsTimestamp
= FALSE
;
188 RTMPPacket_Alloc(RTMPPacket
*p
, int nSize
)
190 char *ptr
= calloc(1, nSize
+ RTMP_MAX_HEADER_SIZE
);
193 p
->m_body
= ptr
+ RTMP_MAX_HEADER_SIZE
;
199 RTMPPacket_Free(RTMPPacket
*p
)
203 free(p
->m_body
- RTMP_MAX_HEADER_SIZE
);
209 RTMPPacket_Dump(RTMPPacket
*p
)
211 RTMP_Log(RTMP_LOGDEBUG
,
212 "RTMP PACKET: packet type: 0x%02x. channel: 0x%02x. info 1: %d info 2: %d. Body size: %u. body: 0x%02x",
213 p
->m_packetType
, p
->m_nChannel
, p
->m_nTimeStamp
, p
->m_nInfoField2
,
214 p
->m_nBodySize
, p
->m_body
? (unsigned char)p
->m_body
[0] : 0);
220 return RTMP_LIB_VERSION
;
228 /* Do this regardless of NO_SSL, we use havege for rtmpe too */
229 RTMP_TLS_ctx
= calloc(1,sizeof(struct tls_ctx
));
230 havege_init(&RTMP_TLS_ctx
->hs
);
231 #elif defined(USE_GNUTLS) && !defined(NO_SSL)
232 /* Technically we need to initialize libgcrypt ourselves if
233 * we're not going to call gnutls_global_init(). Ignoring this
236 gnutls_global_init();
237 RTMP_TLS_ctx
= malloc(sizeof(struct tls_ctx
));
238 gnutls_certificate_allocate_credentials(&RTMP_TLS_ctx
->cred
);
239 gnutls_priority_init(&RTMP_TLS_ctx
->prios
, "NORMAL", NULL
);
240 gnutls_certificate_set_x509_trust_file(RTMP_TLS_ctx
->cred
,
241 "ca.pem", GNUTLS_X509_FMT_PEM
);
242 #elif !defined(NO_SSL) /* USE_OPENSSL */
243 /* libcrypto doesn't need anything special */
244 SSL_load_error_strings();
246 OpenSSL_add_all_digests();
247 RTMP_TLS_ctx
= SSL_CTX_new(SSLv23_method());
248 SSL_CTX_set_options(RTMP_TLS_ctx
, SSL_OP_ALL
);
249 SSL_CTX_set_default_verify_paths(RTMP_TLS_ctx
);
255 RTMP_TLS_AllocServerContext(const char* cert
, const char* key
)
262 tls_server_ctx
*tc
= ctx
= calloc(1, sizeof(struct tls_server_ctx
));
263 tc
->dhm_P
= my_dhm_P
;
264 tc
->dhm_G
= my_dhm_G
;
265 tc
->hs
= &RTMP_TLS_ctx
->hs
;
266 if (x509parse_crtfile(&tc
->cert
, cert
)) {
270 if (x509parse_keyfile(&tc
->key
, key
, NULL
)) {
271 x509_free(&tc
->cert
);
275 #elif defined(USE_GNUTLS) && !defined(NO_SSL)
276 gnutls_certificate_allocate_credentials((gnutls_certificate_credentials
*) &ctx
);
277 if (gnutls_certificate_set_x509_key_file(ctx
, cert
, key
, GNUTLS_X509_FMT_PEM
) != 0) {
278 gnutls_certificate_free_credentials(ctx
);
281 #elif !defined(NO_SSL) /* USE_OPENSSL */
282 ctx
= SSL_CTX_new(SSLv23_server_method());
283 if (!SSL_CTX_use_certificate_chain_file(ctx
, cert
)) {
287 if (!SSL_CTX_use_PrivateKey_file(ctx
, key
, SSL_FILETYPE_PEM
)) {
297 RTMP_TLS_FreeServerContext(void *ctx
)
301 x509_free(&((tls_server_ctx
*)ctx
)->cert
);
302 rsa_free(&((tls_server_ctx
*)ctx
)->key
);
304 #elif defined(USE_GNUTLS) && !defined(NO_SSL)
305 gnutls_certificate_free_credentials(ctx
);
306 #elif !defined(NO_SSL) /* USE_OPENSSL */
315 return calloc(1, sizeof(RTMP
));
332 memset(r
, 0, sizeof(RTMP
));
333 r
->m_sb
.sb_socket
= -1;
334 r
->m_inChunkSize
= RTMP_DEFAULT_CHUNKSIZE
;
335 r
->m_outChunkSize
= RTMP_DEFAULT_CHUNKSIZE
;
336 r
->m_nBufferMS
= 30000;
337 r
->m_nClientBW
= 2500000;
339 r
->m_nServerBW
= 2500000;
340 r
->m_fAudioCodecs
= 3191.0;
341 r
->m_fVideoCodecs
= 252.0;
342 r
->Link
.timeout
= 30;
347 RTMP_EnableWrite(RTMP
*r
)
349 r
->Link
.protocol
|= RTMP_FEATURE_WRITE
;
353 RTMP_GetDuration(RTMP
*r
)
355 return r
->m_fDuration
;
359 RTMP_IsConnected(RTMP
*r
)
361 return r
->m_sb
.sb_socket
!= -1;
367 return r
->m_sb
.sb_socket
;
371 RTMP_IsTimedout(RTMP
*r
)
373 return r
->m_sb
.sb_timedout
;
377 RTMP_SetBufferMS(RTMP
*r
, int size
)
379 r
->m_nBufferMS
= size
;
383 RTMP_UpdateBufferMS(RTMP
*r
)
385 RTMP_SendCtrl(r
, 3, r
->m_stream_id
, r
->m_nBufferMS
);
391 #elif defined(__sun__)
393 #elif defined(__APPLE__)
395 #elif defined(__linux__)
400 #define DEF_VERSTR OSS " 10,0,32,18"
401 static const char DEFAULT_FLASH_VER
[] = DEF_VERSTR
;
402 const AVal RTMP_DefaultFlashVer
=
403 { (char *)DEFAULT_FLASH_VER
, sizeof(DEFAULT_FLASH_VER
) - 1 };
406 SocksSetup(RTMP
*r
, AVal
*sockshost
)
408 if (sockshost
->av_len
)
410 const char *socksport
= strchr(sockshost
->av_val
, ':');
411 char *hostname
= strdup(sockshost
->av_val
);
414 hostname
[socksport
- sockshost
->av_val
] = '\0';
415 r
->Link
.sockshost
.av_val
= hostname
;
416 r
->Link
.sockshost
.av_len
= strlen(hostname
);
418 r
->Link
.socksport
= socksport
? atoi(socksport
+ 1) : 1080;
419 RTMP_Log(RTMP_LOGDEBUG
, "Connecting via SOCKS proxy: %s:%d", r
->Link
.sockshost
.av_val
,
424 r
->Link
.sockshost
.av_val
= NULL
;
425 r
->Link
.sockshost
.av_len
= 0;
426 r
->Link
.socksport
= 0;
431 RTMP_SetupStream(RTMP
*r
,
448 int dStop
, int bLiveStream
, long int timeout
)
450 RTMP_Log(RTMP_LOGDEBUG
, "Protocol : %s", RTMPProtocolStrings
[protocol
&7]);
451 RTMP_Log(RTMP_LOGDEBUG
, "Hostname : %.*s", host
->av_len
, host
->av_val
);
452 RTMP_Log(RTMP_LOGDEBUG
, "Port : %d", port
);
453 RTMP_Log(RTMP_LOGDEBUG
, "Playpath : %s", playpath
->av_val
);
455 if (tcUrl
&& tcUrl
->av_val
)
456 RTMP_Log(RTMP_LOGDEBUG
, "tcUrl : %s", tcUrl
->av_val
);
457 if (swfUrl
&& swfUrl
->av_val
)
458 RTMP_Log(RTMP_LOGDEBUG
, "swfUrl : %s", swfUrl
->av_val
);
459 if (pageUrl
&& pageUrl
->av_val
)
460 RTMP_Log(RTMP_LOGDEBUG
, "pageUrl : %s", pageUrl
->av_val
);
461 if (app
&& app
->av_val
)
462 RTMP_Log(RTMP_LOGDEBUG
, "app : %.*s", app
->av_len
, app
->av_val
);
463 if (auth
&& auth
->av_val
)
464 RTMP_Log(RTMP_LOGDEBUG
, "auth : %s", auth
->av_val
);
465 if (subscribepath
&& subscribepath
->av_val
)
466 RTMP_Log(RTMP_LOGDEBUG
, "subscribepath : %s", subscribepath
->av_val
);
467 if (usherToken
&& usherToken
->av_val
)
468 RTMP_Log(RTMP_LOGDEBUG
, "NetStream.Authenticate.UsherToken : %s", usherToken
->av_val
);
469 if (flashVer
&& flashVer
->av_val
)
470 RTMP_Log(RTMP_LOGDEBUG
, "flashVer : %s", flashVer
->av_val
);
472 RTMP_Log(RTMP_LOGDEBUG
, "StartTime : %d msec", dStart
);
474 RTMP_Log(RTMP_LOGDEBUG
, "StopTime : %d msec", dStop
);
476 RTMP_Log(RTMP_LOGDEBUG
, "live : %s", bLiveStream
? "yes" : "no");
477 RTMP_Log(RTMP_LOGDEBUG
, "timeout : %ld sec", timeout
);
480 if (swfSHA256Hash
!= NULL
&& swfSize
> 0)
482 memcpy(r
->Link
.SWFHash
, swfSHA256Hash
->av_val
, sizeof(r
->Link
.SWFHash
));
483 r
->Link
.SWFSize
= swfSize
;
484 RTMP_Log(RTMP_LOGDEBUG
, "SWFSHA256:");
485 RTMP_LogHex(RTMP_LOGDEBUG
, r
->Link
.SWFHash
, sizeof(r
->Link
.SWFHash
));
486 RTMP_Log(RTMP_LOGDEBUG
, "SWFSize : %u", r
->Link
.SWFSize
);
494 SocksSetup(r
, sockshost
);
496 if (tcUrl
&& tcUrl
->av_len
)
497 r
->Link
.tcUrl
= *tcUrl
;
498 if (swfUrl
&& swfUrl
->av_len
)
499 r
->Link
.swfUrl
= *swfUrl
;
500 if (pageUrl
&& pageUrl
->av_len
)
501 r
->Link
.pageUrl
= *pageUrl
;
502 if (app
&& app
->av_len
)
504 if (auth
&& auth
->av_len
)
506 r
->Link
.auth
= *auth
;
507 r
->Link
.lFlags
|= RTMP_LF_AUTH
;
509 if (flashVer
&& flashVer
->av_len
)
510 r
->Link
.flashVer
= *flashVer
;
512 r
->Link
.flashVer
= RTMP_DefaultFlashVer
;
513 if (subscribepath
&& subscribepath
->av_len
)
514 r
->Link
.subscribepath
= *subscribepath
;
515 if (usherToken
&& usherToken
->av_len
)
516 r
->Link
.usherToken
= *usherToken
;
517 r
->Link
.seekTime
= dStart
;
518 r
->Link
.stopTime
= dStop
;
520 r
->Link
.lFlags
|= RTMP_LF_LIVE
;
521 r
->Link
.timeout
= timeout
;
523 r
->Link
.protocol
= protocol
;
524 r
->Link
.hostname
= *host
;
526 r
->Link
.playpath
= *playpath
;
528 if (r
->Link
.port
== 0)
530 if (protocol
& RTMP_FEATURE_SSL
)
532 else if (protocol
& RTMP_FEATURE_HTTP
)
539 enum { OPT_STR
=0, OPT_INT
, OPT_BOOL
, OPT_CONN
};
540 static const char *optinfo
[] = {
541 "string", "integer", "boolean", "AMF" };
543 #define OFF(x) offsetof(struct RTMP,x)
545 static struct urlopt
{
552 { AVC("socks"), OFF(Link
.sockshost
), OPT_STR
, 0,
553 "Use the specified SOCKS proxy" },
554 { AVC("app"), OFF(Link
.app
), OPT_STR
, 0,
555 "Name of target app on server" },
556 { AVC("tcUrl"), OFF(Link
.tcUrl
), OPT_STR
, 0,
557 "URL to played stream" },
558 { AVC("pageUrl"), OFF(Link
.pageUrl
), OPT_STR
, 0,
559 "URL of played media's web page" },
560 { AVC("swfUrl"), OFF(Link
.swfUrl
), OPT_STR
, 0,
561 "URL to player SWF file" },
562 { AVC("flashver"), OFF(Link
.flashVer
), OPT_STR
, 0,
563 "Flash version string (default " DEF_VERSTR
")" },
564 { AVC("conn"), OFF(Link
.extras
), OPT_CONN
, 0,
565 "Append arbitrary AMF data to Connect message" },
566 { AVC("playpath"), OFF(Link
.playpath
), OPT_STR
, 0,
567 "Path to target media on server" },
568 { AVC("playlist"), OFF(Link
.lFlags
), OPT_BOOL
, RTMP_LF_PLST
,
569 "Set playlist before play command" },
570 { AVC("live"), OFF(Link
.lFlags
), OPT_BOOL
, RTMP_LF_LIVE
,
571 "Stream is live, no seeking possible" },
572 { AVC("subscribe"), OFF(Link
.subscribepath
), OPT_STR
, 0,
573 "Stream to subscribe to" },
574 { AVC("jtv"), OFF(Link
.usherToken
), OPT_STR
, 0,
575 "Justin.tv authentication token" },
576 { AVC("token"), OFF(Link
.token
), OPT_STR
, 0,
577 "Key for SecureToken response" },
578 { AVC("swfVfy"), OFF(Link
.lFlags
), OPT_BOOL
, RTMP_LF_SWFV
,
579 "Perform SWF Verification" },
580 { AVC("swfAge"), OFF(Link
.swfAge
), OPT_INT
, 0,
581 "Number of days to use cached SWF hash" },
582 { AVC("start"), OFF(Link
.seekTime
), OPT_INT
, 0,
583 "Stream start position in milliseconds" },
584 { AVC("stop"), OFF(Link
.stopTime
), OPT_INT
, 0,
585 "Stream stop position in milliseconds" },
586 { AVC("buffer"), OFF(m_nBufferMS
), OPT_INT
, 0,
587 "Buffer time in milliseconds" },
588 { AVC("timeout"), OFF(Link
.timeout
), OPT_INT
, 0,
589 "Session timeout in seconds" },
590 { AVC("pubUser"), OFF(Link
.pubUser
), OPT_STR
, 0,
591 "Publisher username" },
592 { AVC("pubPasswd"), OFF(Link
.pubPasswd
), OPT_STR
, 0,
593 "Publisher password" },
597 static const AVal truth
[] = {
605 static void RTMP_OptUsage()
609 RTMP_Log(RTMP_LOGERROR
, "Valid RTMP options are:\n");
610 for (i
=0; options
[i
].name
.av_len
; i
++) {
611 RTMP_Log(RTMP_LOGERROR
, "%10s %-7s %s\n", options
[i
].name
.av_val
,
612 optinfo
[options
[i
].otype
], options
[i
].use
);
617 parseAMF(AMFObject
*obj
, AVal
*av
, int *depth
)
619 AMFObjectProperty prop
= {{0,0}};
621 char *p
, *arg
= av
->av_val
;
629 prop
.p_type
= AMF_BOOLEAN
;
630 prop
.p_vu
.p_number
= atoi(p
);
633 prop
.p_type
= AMF_STRING
;
634 prop
.p_vu
.p_aval
.av_val
= p
;
635 prop
.p_vu
.p_aval
.av_len
= av
->av_len
- (p
-arg
);
638 prop
.p_type
= AMF_NUMBER
;
639 prop
.p_vu
.p_number
= strtod(p
, NULL
);
642 prop
.p_type
= AMF_NULL
;
648 prop
.p_type
= AMF_OBJECT
;
660 else if (arg
[2] == ':' && arg
[0] == 'N')
662 p
= strchr(arg
+3, ':');
665 prop
.p_name
.av_val
= (char *)arg
+3;
666 prop
.p_name
.av_len
= p
- (arg
+3);
672 prop
.p_type
= AMF_BOOLEAN
;
673 prop
.p_vu
.p_number
= atoi(p
);
676 prop
.p_type
= AMF_STRING
;
677 prop
.p_vu
.p_aval
.av_val
= p
;
678 prop
.p_vu
.p_aval
.av_len
= av
->av_len
- (p
-arg
);
681 prop
.p_type
= AMF_NUMBER
;
682 prop
.p_vu
.p_number
= strtod(p
, NULL
);
685 prop
.p_type
= AMF_OBJECT
;
697 for (i
=0; i
<*depth
; i
++)
699 o2
= &obj
->o_props
[obj
->o_num
-1].p_vu
.p_object
;
703 AMF_AddProp(obj
, &prop
);
704 if (prop
.p_type
== AMF_OBJECT
)
709 int RTMP_SetOpt(RTMP
*r
, const AVal
*opt
, AVal
*arg
)
714 for (i
=0; options
[i
].name
.av_len
; i
++) {
715 if (opt
->av_len
!= options
[i
].name
.av_len
) continue;
716 if (strcasecmp(opt
->av_val
, options
[i
].name
.av_val
)) continue;
717 v
= (char *)r
+ options
[i
].off
;
718 switch(options
[i
].otype
) {
724 long l
= strtol(arg
->av_val
, NULL
, 0);
730 for (j
=0; truth
[j
].av_len
; j
++) {
731 if (arg
->av_len
!= truth
[j
].av_len
) continue;
732 if (strcasecmp(arg
->av_val
, truth
[j
].av_val
)) continue;
733 fl
|= options
[i
].omisc
; break; }
738 if (parseAMF(&r
->Link
.extras
, arg
, &r
->Link
.edepth
))
744 if (!options
[i
].name
.av_len
) {
745 RTMP_Log(RTMP_LOGERROR
, "Unknown option %s", opt
->av_val
);
753 int RTMP_SetupURL(RTMP
*r
, char *url
)
756 char *p1
, *p2
, *ptr
= strchr(url
, ' ');
758 unsigned int port
= 0;
764 ret
= RTMP_ParseURL(url
, &r
->Link
.protocol
, &r
->Link
.hostname
,
765 &port
, &r
->Link
.playpath0
, &r
->Link
.app
);
769 r
->Link
.playpath
= r
->Link
.playpath0
;
774 p2
= strchr(p1
, '=');
778 opt
.av_len
= p2
- p1
;
781 ptr
= strchr(p2
, ' ');
784 arg
.av_len
= ptr
- p2
;
785 /* skip repeated spaces */
789 arg
.av_len
= strlen(p2
);
794 for (p1
=p2
; port
>0;) {
799 sscanf(p1
+1, "%02x", &c
);
808 arg
.av_len
= p2
- arg
.av_val
;
810 ret
= RTMP_SetOpt(r
, &opt
, &arg
);
815 if (!r
->Link
.tcUrl
.av_len
)
817 r
->Link
.tcUrl
.av_val
= url
;
818 if (r
->Link
.app
.av_len
)
820 if (r
->Link
.app
.av_val
< url
+ len
)
822 /* if app is part of original url, just use it */
823 r
->Link
.tcUrl
.av_len
= r
->Link
.app
.av_len
+ (r
->Link
.app
.av_val
- url
);
827 len
= r
->Link
.hostname
.av_len
+ r
->Link
.app
.av_len
+
828 sizeof("rtmpte://:65535/");
829 r
->Link
.tcUrl
.av_val
= malloc(len
);
830 r
->Link
.tcUrl
.av_len
= snprintf(r
->Link
.tcUrl
.av_val
, len
,
832 RTMPProtocolStringsLower
[r
->Link
.protocol
],
833 r
->Link
.hostname
.av_len
, r
->Link
.hostname
.av_val
,
835 r
->Link
.app
.av_len
, r
->Link
.app
.av_val
);
836 r
->Link
.lFlags
|= RTMP_LF_FTCU
;
841 r
->Link
.tcUrl
.av_len
= strlen(url
);
846 if ((r
->Link
.lFlags
& RTMP_LF_SWFV
) && r
->Link
.swfUrl
.av_len
)
847 RTMP_HashSWF(r
->Link
.swfUrl
.av_val
, &r
->Link
.SWFSize
,
848 (unsigned char *)r
->Link
.SWFHash
, r
->Link
.swfAge
);
851 SocksSetup(r
, &r
->Link
.sockshost
);
853 if (r
->Link
.port
== 0)
855 if (r
->Link
.protocol
& RTMP_FEATURE_SSL
)
857 else if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
866 add_addr_info(struct sockaddr_in
*service
, AVal
*host
, int port
)
870 if (host
->av_val
[host
->av_len
])
872 hostname
= malloc(host
->av_len
+1);
873 memcpy(hostname
, host
->av_val
, host
->av_len
);
874 hostname
[host
->av_len
] = '\0';
878 hostname
= host
->av_val
;
881 service
->sin_addr
.s_addr
= inet_addr(hostname
);
882 if (service
->sin_addr
.s_addr
== INADDR_NONE
)
884 struct hostent
*host
= gethostbyname(hostname
);
885 if (host
== NULL
|| host
->h_addr
== NULL
)
887 RTMP_Log(RTMP_LOGERROR
, "Problem accessing the DNS. (addr: %s)", hostname
);
891 service
->sin_addr
= *(struct in_addr
*)host
->h_addr
;
894 service
->sin_port
= htons(port
);
896 if (hostname
!= host
->av_val
)
902 RTMP_Connect0(RTMP
*r
, struct sockaddr
* service
)
905 r
->m_sb
.sb_timedout
= FALSE
;
907 r
->m_fDuration
= 0.0;
909 r
->m_sb
.sb_socket
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
910 if (r
->m_sb
.sb_socket
!= -1)
912 if (connect(r
->m_sb
.sb_socket
, service
, sizeof(struct sockaddr
)) < 0)
914 int err
= GetSockError();
915 RTMP_Log(RTMP_LOGERROR
, "%s, failed to connect socket. %d (%s)",
916 __FUNCTION__
, err
, strerror(err
));
921 if (r
->Link
.socksport
)
923 RTMP_Log(RTMP_LOGDEBUG
, "%s ... SOCKS negotiation", __FUNCTION__
);
924 if (!SocksNegotiate(r
))
926 RTMP_Log(RTMP_LOGERROR
, "%s, SOCKS negotiation failed.", __FUNCTION__
);
934 RTMP_Log(RTMP_LOGERROR
, "%s, failed to create socket. Error: %d", __FUNCTION__
,
941 SET_RCVTIMEO(tv
, r
->Link
.timeout
);
943 (r
->m_sb
.sb_socket
, SOL_SOCKET
, SO_RCVTIMEO
, (char *)&tv
, sizeof(tv
)))
945 RTMP_Log(RTMP_LOGERROR
, "%s, Setting socket timeout to %ds failed!",
946 __FUNCTION__
, r
->Link
.timeout
);
950 setsockopt(r
->m_sb
.sb_socket
, IPPROTO_TCP
, TCP_NODELAY
, (char *) &on
, sizeof(on
));
956 RTMP_TLS_Accept(RTMP
*r
, void *ctx
)
958 #if defined(CRYPTO) && !defined(NO_SSL)
959 TLS_server(ctx
, r
->m_sb
.sb_ssl
);
960 TLS_setfd(r
->m_sb
.sb_ssl
, r
->m_sb
.sb_socket
);
961 if (TLS_accept(r
->m_sb
.sb_ssl
) < 0)
963 RTMP_Log(RTMP_LOGERROR
, "%s, TLS_Connect failed", __FUNCTION__
);
973 RTMP_Connect1(RTMP
*r
, RTMPPacket
*cp
)
975 if (r
->Link
.protocol
& RTMP_FEATURE_SSL
)
977 #if defined(CRYPTO) && !defined(NO_SSL)
978 TLS_client(RTMP_TLS_ctx
, r
->m_sb
.sb_ssl
);
979 TLS_setfd(r
->m_sb
.sb_ssl
, r
->m_sb
.sb_socket
);
980 if (TLS_connect(r
->m_sb
.sb_ssl
) < 0)
982 RTMP_Log(RTMP_LOGERROR
, "%s, TLS_Connect failed", __FUNCTION__
);
987 RTMP_Log(RTMP_LOGERROR
, "%s, no SSL/TLS support", __FUNCTION__
);
993 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
996 r
->m_clientID
.av_val
= NULL
;
997 r
->m_clientID
.av_len
= 0;
998 HTTP_Post(r
, RTMPT_OPEN
, "", 1);
999 if (HTTP_read(r
, 1) != 0)
1001 r
->m_msgCounter
= 0;
1002 RTMP_Log(RTMP_LOGDEBUG
, "%s, Could not connect for handshake", __FUNCTION__
);
1006 r
->m_msgCounter
= 0;
1008 RTMP_Log(RTMP_LOGDEBUG
, "%s, ... connected, handshaking", __FUNCTION__
);
1009 if (!HandShake(r
, TRUE
))
1011 RTMP_Log(RTMP_LOGERROR
, "%s, handshake failed.", __FUNCTION__
);
1015 RTMP_Log(RTMP_LOGDEBUG
, "%s, handshaked", __FUNCTION__
);
1017 if (!SendConnectPacket(r
, cp
))
1019 RTMP_Log(RTMP_LOGERROR
, "%s, RTMP connect failed.", __FUNCTION__
);
1027 RTMP_Connect(RTMP
*r
, RTMPPacket
*cp
)
1029 struct sockaddr_in service
;
1030 if (!r
->Link
.hostname
.av_len
)
1033 memset(&service
, 0, sizeof(struct sockaddr_in
));
1034 service
.sin_family
= AF_INET
;
1036 if (r
->Link
.socksport
)
1038 /* Connect via SOCKS */
1039 if (!add_addr_info(&service
, &r
->Link
.sockshost
, r
->Link
.socksport
))
1044 /* Connect directly */
1045 if (!add_addr_info(&service
, &r
->Link
.hostname
, r
->Link
.port
))
1049 if (!RTMP_Connect0(r
, (struct sockaddr
*)&service
))
1052 r
->m_bSendCounter
= TRUE
;
1054 return RTMP_Connect1(r
, cp
);
1058 SocksNegotiate(RTMP
*r
)
1061 struct sockaddr_in service
;
1062 memset(&service
, 0, sizeof(struct sockaddr_in
));
1064 add_addr_info(&service
, &r
->Link
.hostname
, r
->Link
.port
);
1065 addr
= htonl(service
.sin_addr
.s_addr
);
1069 4, 1, /* SOCKS 4, connect */
1070 (r
->Link
.port
>> 8) & 0xFF,
1071 (r
->Link
.port
) & 0xFF,
1072 (char)(addr
>> 24) & 0xFF, (char)(addr
>> 16) & 0xFF,
1073 (char)(addr
>> 8) & 0xFF, (char)addr
& 0xFF,
1075 }; /* NULL terminate */
1077 WriteN(r
, packet
, sizeof packet
);
1079 if (ReadN(r
, packet
, 8) != 8)
1082 if (packet
[0] == 0 && packet
[1] == 90)
1088 RTMP_Log(RTMP_LOGERROR
, "%s, SOCKS returned error code %d", __FUNCTION__
, packet
[1]);
1095 RTMP_ConnectStream(RTMP
*r
, int seekTime
)
1097 RTMPPacket packet
= { 0 };
1099 /* seekTime was already set by SetupStream / SetupURL.
1100 * This is only needed by ReconnectStream.
1103 r
->Link
.seekTime
= seekTime
;
1105 r
->m_mediaChannel
= 0;
1107 while (!r
->m_bPlaying
&& RTMP_IsConnected(r
) && RTMP_ReadPacket(r
, &packet
))
1109 if (RTMPPacket_IsReady(&packet
))
1111 if (!packet
.m_nBodySize
)
1113 if ((packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
) ||
1114 (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
) ||
1115 (packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
))
1117 RTMP_Log(RTMP_LOGWARNING
, "Received FLV packet before play()! Ignoring.");
1118 RTMPPacket_Free(&packet
);
1122 RTMP_ClientPacket(r
, &packet
);
1123 RTMPPacket_Free(&packet
);
1127 return r
->m_bPlaying
;
1131 RTMP_ReconnectStream(RTMP
*r
, int seekTime
)
1133 RTMP_DeleteStream(r
);
1135 RTMP_SendCreateStream(r
);
1137 return RTMP_ConnectStream(r
, seekTime
);
1141 RTMP_ToggleStream(RTMP
*r
)
1147 if (RTMP_IsTimedout(r
) && r
->m_read
.status
== RTMP_READ_EOF
)
1148 r
->m_read
.status
= 0;
1150 res
= RTMP_SendPause(r
, TRUE
, r
->m_pauseStamp
);
1157 res
= RTMP_SendPause(r
, FALSE
, r
->m_pauseStamp
);
1163 RTMP_DeleteStream(RTMP
*r
)
1165 if (r
->m_stream_id
< 0)
1168 r
->m_bPlaying
= FALSE
;
1170 SendDeleteStream(r
, r
->m_stream_id
);
1171 r
->m_stream_id
= -1;
1175 RTMP_GetNextMediaPacket(RTMP
*r
, RTMPPacket
*packet
)
1177 int bHasMediaPacket
= 0;
1179 while (!bHasMediaPacket
&& RTMP_IsConnected(r
)
1180 && RTMP_ReadPacket(r
, packet
))
1182 if (!RTMPPacket_IsReady(packet
))
1187 bHasMediaPacket
= RTMP_ClientPacket(r
, packet
);
1189 if (!bHasMediaPacket
)
1191 RTMPPacket_Free(packet
);
1193 else if (r
->m_pausing
== 3)
1195 if (packet
->m_nTimeStamp
<= r
->m_mediaStamp
)
1197 bHasMediaPacket
= 0;
1199 RTMP_Log(RTMP_LOGDEBUG
,
1200 "Skipped type: %02X, size: %d, TS: %d ms, abs TS: %d, pause: %d ms",
1201 packet
->m_packetType
, packet
->m_nBodySize
,
1202 packet
->m_nTimeStamp
, packet
->m_hasAbsTimestamp
,
1205 RTMPPacket_Free(packet
);
1212 if (bHasMediaPacket
)
1213 r
->m_bPlaying
= TRUE
;
1214 else if (r
->m_sb
.sb_timedout
&& !r
->m_pausing
)
1215 r
->m_pauseStamp
= r
->m_mediaChannel
< r
->m_channelsAllocatedIn
?
1216 r
->m_channelTimestamp
[r
->m_mediaChannel
] : 0;
1218 return bHasMediaPacket
;
1222 RTMP_ClientPacket(RTMP
*r
, RTMPPacket
*packet
)
1224 int bHasMediaPacket
= 0;
1225 switch (packet
->m_packetType
)
1227 case RTMP_PACKET_TYPE_CHUNK_SIZE
:
1229 HandleChangeChunkSize(r
, packet
);
1232 case RTMP_PACKET_TYPE_BYTES_READ_REPORT
:
1233 /* bytes read report */
1234 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: bytes read report", __FUNCTION__
);
1237 case RTMP_PACKET_TYPE_CONTROL
:
1239 HandleCtrl(r
, packet
);
1242 case RTMP_PACKET_TYPE_SERVER_BW
:
1244 HandleServerBW(r
, packet
);
1247 case RTMP_PACKET_TYPE_CLIENT_BW
:
1249 HandleClientBW(r
, packet
);
1252 case RTMP_PACKET_TYPE_AUDIO
:
1254 /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); */
1255 HandleAudio(r
, packet
);
1256 bHasMediaPacket
= 1;
1257 if (!r
->m_mediaChannel
)
1258 r
->m_mediaChannel
= packet
->m_nChannel
;
1260 r
->m_mediaStamp
= packet
->m_nTimeStamp
;
1263 case RTMP_PACKET_TYPE_VIDEO
:
1265 /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); */
1266 HandleVideo(r
, packet
);
1267 bHasMediaPacket
= 1;
1268 if (!r
->m_mediaChannel
)
1269 r
->m_mediaChannel
= packet
->m_nChannel
;
1271 r
->m_mediaStamp
= packet
->m_nTimeStamp
;
1274 case RTMP_PACKET_TYPE_FLEX_STREAM_SEND
:
1275 /* flex stream send */
1276 RTMP_Log(RTMP_LOGDEBUG
,
1277 "%s, flex stream send, size %u bytes, not supported, ignoring",
1278 __FUNCTION__
, packet
->m_nBodySize
);
1281 case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT
:
1282 /* flex shared object */
1283 RTMP_Log(RTMP_LOGDEBUG
,
1284 "%s, flex shared object, size %u bytes, not supported, ignoring",
1285 __FUNCTION__
, packet
->m_nBodySize
);
1288 case RTMP_PACKET_TYPE_FLEX_MESSAGE
:
1291 RTMP_Log(RTMP_LOGDEBUG
,
1292 "%s, flex message, size %u bytes, not fully supported",
1293 __FUNCTION__
, packet
->m_nBodySize
);
1294 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
1296 /* some DEBUG code */
1298 RTMP_LIB_AMFObject obj
;
1299 int nRes
= obj
.Decode(packet
.m_body
+1, packet
.m_nBodySize
-1);
1301 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding AMF3 packet", __FUNCTION__
);
1308 if (HandleInvoke(r
, packet
->m_body
+ 1, packet
->m_nBodySize
- 1) == 1)
1309 bHasMediaPacket
= 2;
1312 case RTMP_PACKET_TYPE_INFO
:
1313 /* metadata (notify) */
1314 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: notify %u bytes", __FUNCTION__
,
1315 packet
->m_nBodySize
);
1316 if (HandleMetadata(r
, packet
->m_body
, packet
->m_nBodySize
))
1317 bHasMediaPacket
= 1;
1320 case RTMP_PACKET_TYPE_SHARED_OBJECT
:
1321 RTMP_Log(RTMP_LOGDEBUG
, "%s, shared object, not supported, ignoring",
1325 case RTMP_PACKET_TYPE_INVOKE
:
1327 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: invoke %u bytes", __FUNCTION__
,
1328 packet
->m_nBodySize
);
1329 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
1331 if (HandleInvoke(r
, packet
->m_body
, packet
->m_nBodySize
) == 1)
1332 bHasMediaPacket
= 2;
1335 case RTMP_PACKET_TYPE_FLASH_VIDEO
:
1337 /* go through FLV packets and handle metadata packets */
1338 unsigned int pos
= 0;
1339 uint32_t nTimeStamp
= packet
->m_nTimeStamp
;
1341 while (pos
+ 11 < packet
->m_nBodySize
)
1343 uint32_t dataSize
= AMF_DecodeInt24(packet
->m_body
+ pos
+ 1); /* size without header (11) and prevTagSize (4) */
1345 if (pos
+ 11 + dataSize
+ 4 > packet
->m_nBodySize
)
1347 RTMP_Log(RTMP_LOGWARNING
, "Stream corrupt?!");
1350 if (packet
->m_body
[pos
] == 0x12)
1352 HandleMetadata(r
, packet
->m_body
+ pos
+ 11, dataSize
);
1354 else if (packet
->m_body
[pos
] == 8 || packet
->m_body
[pos
] == 9)
1356 nTimeStamp
= AMF_DecodeInt24(packet
->m_body
+ pos
+ 4);
1357 nTimeStamp
|= (packet
->m_body
[pos
+ 7] << 24);
1359 pos
+= (11 + dataSize
+ 4);
1362 r
->m_mediaStamp
= nTimeStamp
;
1365 /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: FLV tag(s) %lu bytes", __FUNCTION__, packet.m_nBodySize); */
1366 bHasMediaPacket
= 1;
1370 RTMP_Log(RTMP_LOGDEBUG
, "%s, unknown packet type received: 0x%02x", __FUNCTION__
,
1371 packet
->m_packetType
);
1373 RTMP_LogHex(RTMP_LOGDEBUG
, packet
->m_body
, packet
->m_nBodySize
);
1377 return bHasMediaPacket
;
1381 extern FILE *netstackdump
;
1382 extern FILE *netstackdump_read
;
1386 ReadN(RTMP
*r
, char *buffer
, int n
)
1388 int nOriginalSize
= n
;
1392 r
->m_sb
.sb_timedout
= FALSE
;
1395 memset(buffer
, 0, n
);
1401 int nBytes
= 0, nRead
;
1402 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
1405 while (!r
->m_resplen
)
1408 if (r
->m_sb
.sb_size
< 13 || refill
)
1411 HTTP_Post(r
, RTMPT_IDLE
, "", 1);
1412 if (RTMPSockBuf_Fill(&r
->m_sb
) < 1)
1414 if (!r
->m_sb
.sb_timedout
)
1419 if ((ret
= HTTP_read(r
, 0)) == -1)
1421 RTMP_Log(RTMP_LOGDEBUG
, "%s, No valid HTTP response found", __FUNCTION__
);
1434 if (r
->m_resplen
&& !r
->m_sb
.sb_size
)
1435 RTMPSockBuf_Fill(&r
->m_sb
);
1436 avail
= r
->m_sb
.sb_size
;
1437 if (avail
> r
->m_resplen
)
1438 avail
= r
->m_resplen
;
1442 avail
= r
->m_sb
.sb_size
;
1445 if (RTMPSockBuf_Fill(&r
->m_sb
) < 1)
1447 if (!r
->m_sb
.sb_timedout
)
1451 avail
= r
->m_sb
.sb_size
;
1454 nRead
= ((n
< avail
) ? n
: avail
);
1457 memcpy(ptr
, r
->m_sb
.sb_start
, nRead
);
1458 r
->m_sb
.sb_start
+= nRead
;
1459 r
->m_sb
.sb_size
-= nRead
;
1461 r
->m_nBytesIn
+= nRead
;
1462 if (r
->m_bSendCounter
1463 && r
->m_nBytesIn
> ( r
->m_nBytesInSent
+ r
->m_nClientBW
/ 10))
1464 if (!SendBytesReceived(r
))
1467 /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */
1469 fwrite(ptr
, 1, nBytes
, netstackdump_read
);
1474 RTMP_Log(RTMP_LOGDEBUG
, "%s, RTMP socket closed by peer", __FUNCTION__
);
1480 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
1481 r
->m_resplen
-= nBytes
;
1484 if (r
->Link
.rc4keyIn
)
1486 RC4_encrypt(r
->Link
.rc4keyIn
, nBytes
, ptr
);
1494 return nOriginalSize
- n
;
1498 WriteN(RTMP
*r
, const char *buffer
, int n
)
1500 const char *ptr
= buffer
;
1502 char *encrypted
= 0;
1503 char buf
[RTMP_BUFFER_CACHE_SIZE
];
1505 if (r
->Link
.rc4keyOut
)
1507 if (n
> sizeof(buf
))
1508 encrypted
= (char *)malloc(n
);
1510 encrypted
= (char *)buf
;
1512 RC4_encrypt2(r
->Link
.rc4keyOut
, n
, buffer
, ptr
);
1520 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
1521 nBytes
= HTTP_Post(r
, RTMPT_SEND
, ptr
, n
);
1523 nBytes
= RTMPSockBuf_Send(&r
->m_sb
, ptr
, n
);
1524 /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d\n", __FUNCTION__, nBytes); */
1528 int sockerr
= GetSockError();
1529 RTMP_Log(RTMP_LOGERROR
, "%s, RTMP send error %d (%d bytes)", __FUNCTION__
,
1532 if (sockerr
== EINTR
&& !RTMP_ctrlC
)
1548 if (encrypted
&& encrypted
!= buf
)
1555 #define SAVC(x) static const AVal av_##x = AVC(#x)
1567 SAVC(videoFunction
);
1568 SAVC(objectEncoding
);
1570 SAVC(secureTokenResponse
);
1575 SendConnectPacket(RTMP
*r
, RTMPPacket
*cp
)
1578 char pbuf
[4096], *pend
= pbuf
+ sizeof(pbuf
);
1582 return RTMP_SendPacket(r
, cp
, TRUE
);
1584 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1585 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
1586 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1587 packet
.m_nTimeStamp
= 0;
1588 packet
.m_nInfoField2
= 0;
1589 packet
.m_hasAbsTimestamp
= 0;
1590 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1592 enc
= packet
.m_body
;
1593 enc
= AMF_EncodeString(enc
, pend
, &av_connect
);
1594 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1595 *enc
++ = AMF_OBJECT
;
1597 enc
= AMF_EncodeNamedString(enc
, pend
, &av_app
, &r
->Link
.app
);
1600 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
1602 enc
= AMF_EncodeNamedString(enc
, pend
, &av_type
, &av_nonprivate
);
1606 if (r
->Link
.flashVer
.av_len
)
1608 enc
= AMF_EncodeNamedString(enc
, pend
, &av_flashVer
, &r
->Link
.flashVer
);
1612 if (r
->Link
.swfUrl
.av_len
)
1614 enc
= AMF_EncodeNamedString(enc
, pend
, &av_swfUrl
, &r
->Link
.swfUrl
);
1618 if (r
->Link
.tcUrl
.av_len
)
1620 enc
= AMF_EncodeNamedString(enc
, pend
, &av_tcUrl
, &r
->Link
.tcUrl
);
1624 if (!(r
->Link
.protocol
& RTMP_FEATURE_WRITE
))
1626 enc
= AMF_EncodeNamedBoolean(enc
, pend
, &av_fpad
, FALSE
);
1629 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_capabilities
, 15.0);
1632 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_audioCodecs
, r
->m_fAudioCodecs
);
1635 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_videoCodecs
, r
->m_fVideoCodecs
);
1638 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_videoFunction
, 1.0);
1641 if (r
->Link
.pageUrl
.av_len
)
1643 enc
= AMF_EncodeNamedString(enc
, pend
, &av_pageUrl
, &r
->Link
.pageUrl
);
1648 if (r
->m_fEncoding
!= 0.0 || r
->m_bSendEncoding
)
1649 { /* AMF0, AMF3 not fully supported yet */
1650 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_objectEncoding
, r
->m_fEncoding
);
1654 if (enc
+ 3 >= pend
)
1657 *enc
++ = 0; /* end of object - 0x00 0x00 0x09 */
1658 *enc
++ = AMF_OBJECT_END
;
1660 /* add auth string */
1661 if (r
->Link
.auth
.av_len
)
1663 enc
= AMF_EncodeBoolean(enc
, pend
, r
->Link
.lFlags
& RTMP_LF_AUTH
);
1666 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.auth
);
1670 if (r
->Link
.extras
.o_num
)
1673 for (i
= 0; i
< r
->Link
.extras
.o_num
; i
++)
1675 enc
= AMFProp_Encode(&r
->Link
.extras
.o_props
[i
], enc
, pend
);
1680 packet
.m_nBodySize
= enc
- packet
.m_body
;
1682 return RTMP_SendPacket(r
, &packet
, TRUE
);
1689 SendBGHasStream(RTMP
*r
, double dId
, AVal
*playpath
)
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_bgHasStream
);
1705 enc
= AMF_EncodeNumber(enc
, pend
, dId
);
1708 enc
= AMF_EncodeString(enc
, pend
, playpath
);
1712 packet
.m_nBodySize
= enc
- packet
.m_body
;
1714 return RTMP_SendPacket(r
, &packet
, TRUE
);
1721 RTMP_SendCreateStream(RTMP
*r
)
1724 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1727 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1728 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1729 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1730 packet
.m_nTimeStamp
= 0;
1731 packet
.m_nInfoField2
= 0;
1732 packet
.m_hasAbsTimestamp
= 0;
1733 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1735 enc
= packet
.m_body
;
1736 enc
= AMF_EncodeString(enc
, pend
, &av_createStream
);
1737 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1738 *enc
++ = AMF_NULL
; /* NULL */
1740 packet
.m_nBodySize
= enc
- packet
.m_body
;
1742 return RTMP_SendPacket(r
, &packet
, TRUE
);
1748 SendFCSubscribe(RTMP
*r
, AVal
*subscribepath
)
1751 char pbuf
[512], *pend
= pbuf
+ sizeof(pbuf
);
1753 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1754 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1755 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1756 packet
.m_nTimeStamp
= 0;
1757 packet
.m_nInfoField2
= 0;
1758 packet
.m_hasAbsTimestamp
= 0;
1759 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1761 RTMP_Log(RTMP_LOGDEBUG
, "FCSubscribe: %s", subscribepath
->av_val
);
1762 enc
= packet
.m_body
;
1763 enc
= AMF_EncodeString(enc
, pend
, &av_FCSubscribe
);
1764 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1766 enc
= AMF_EncodeString(enc
, pend
, subscribepath
);
1771 packet
.m_nBodySize
= enc
- packet
.m_body
;
1773 return RTMP_SendPacket(r
, &packet
, TRUE
);
1776 /* Justin.tv specific authentication */
1777 static const AVal av_NetStream_Authenticate_UsherToken
= AVC("NetStream.Authenticate.UsherToken");
1780 SendUsherToken(RTMP
*r
, AVal
*usherToken
)
1783 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1785 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1786 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1787 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1788 packet
.m_nTimeStamp
= 0;
1789 packet
.m_nInfoField2
= 0;
1790 packet
.m_hasAbsTimestamp
= 0;
1791 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1793 RTMP_Log(RTMP_LOGDEBUG
, "UsherToken: %s", usherToken
->av_val
);
1794 enc
= packet
.m_body
;
1795 enc
= AMF_EncodeString(enc
, pend
, &av_NetStream_Authenticate_UsherToken
);
1796 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1798 enc
= AMF_EncodeString(enc
, pend
, usherToken
);
1803 packet
.m_nBodySize
= enc
- packet
.m_body
;
1805 return RTMP_SendPacket(r
, &packet
, FALSE
);
1807 /******************************************/
1809 SAVC(releaseStream
);
1812 SendReleaseStream(RTMP
*r
)
1815 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1818 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1819 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1820 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1821 packet
.m_nTimeStamp
= 0;
1822 packet
.m_nInfoField2
= 0;
1823 packet
.m_hasAbsTimestamp
= 0;
1824 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1826 enc
= packet
.m_body
;
1827 enc
= AMF_EncodeString(enc
, pend
, &av_releaseStream
);
1828 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1830 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1834 packet
.m_nBodySize
= enc
- packet
.m_body
;
1836 return RTMP_SendPacket(r
, &packet
, FALSE
);
1842 SendFCPublish(RTMP
*r
)
1845 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1848 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1849 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1850 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1851 packet
.m_nTimeStamp
= 0;
1852 packet
.m_nInfoField2
= 0;
1853 packet
.m_hasAbsTimestamp
= 0;
1854 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1856 enc
= packet
.m_body
;
1857 enc
= AMF_EncodeString(enc
, pend
, &av_FCPublish
);
1858 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1860 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1864 packet
.m_nBodySize
= enc
- packet
.m_body
;
1866 return RTMP_SendPacket(r
, &packet
, FALSE
);
1872 SendFCUnpublish(RTMP
*r
)
1875 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1878 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1879 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1880 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1881 packet
.m_nTimeStamp
= 0;
1882 packet
.m_nInfoField2
= 0;
1883 packet
.m_hasAbsTimestamp
= 0;
1884 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1886 enc
= packet
.m_body
;
1887 enc
= AMF_EncodeString(enc
, pend
, &av_FCUnpublish
);
1888 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1890 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1894 packet
.m_nBodySize
= enc
- packet
.m_body
;
1896 return RTMP_SendPacket(r
, &packet
, FALSE
);
1904 SendPublish(RTMP
*r
)
1907 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1910 packet
.m_nChannel
= 0x04; /* source channel (invoke) */
1911 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
1912 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1913 packet
.m_nTimeStamp
= 0;
1914 packet
.m_nInfoField2
= r
->m_stream_id
;
1915 packet
.m_hasAbsTimestamp
= 0;
1916 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1918 enc
= packet
.m_body
;
1919 enc
= AMF_EncodeString(enc
, pend
, &av_publish
);
1920 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1922 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1926 /* FIXME: should we choose live based on Link.lFlags & RTMP_LF_LIVE? */
1927 enc
= AMF_EncodeString(enc
, pend
, &av_live
);
1931 packet
.m_nBodySize
= enc
- packet
.m_body
;
1933 return RTMP_SendPacket(r
, &packet
, TRUE
);
1939 SendDeleteStream(RTMP
*r
, double dStreamId
)
1942 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1945 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1946 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1947 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1948 packet
.m_nTimeStamp
= 0;
1949 packet
.m_nInfoField2
= 0;
1950 packet
.m_hasAbsTimestamp
= 0;
1951 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1953 enc
= packet
.m_body
;
1954 enc
= AMF_EncodeString(enc
, pend
, &av_deleteStream
);
1955 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1957 enc
= AMF_EncodeNumber(enc
, pend
, dStreamId
);
1959 packet
.m_nBodySize
= enc
- packet
.m_body
;
1961 /* no response expected */
1962 return RTMP_SendPacket(r
, &packet
, FALSE
);
1968 RTMP_SendPause(RTMP
*r
, int DoPause
, int iTime
)
1971 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1974 packet
.m_nChannel
= 0x08; /* video channel */
1975 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1976 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1977 packet
.m_nTimeStamp
= 0;
1978 packet
.m_nInfoField2
= 0;
1979 packet
.m_hasAbsTimestamp
= 0;
1980 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1982 enc
= packet
.m_body
;
1983 enc
= AMF_EncodeString(enc
, pend
, &av_pause
);
1984 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1986 enc
= AMF_EncodeBoolean(enc
, pend
, DoPause
);
1987 enc
= AMF_EncodeNumber(enc
, pend
, (double)iTime
);
1989 packet
.m_nBodySize
= enc
- packet
.m_body
;
1991 RTMP_Log(RTMP_LOGDEBUG
, "%s, %d, pauseTime=%d", __FUNCTION__
, DoPause
, iTime
);
1992 return RTMP_SendPacket(r
, &packet
, TRUE
);
1995 int RTMP_Pause(RTMP
*r
, int DoPause
)
1998 r
->m_pauseStamp
= r
->m_mediaChannel
< r
->m_channelsAllocatedIn
?
1999 r
->m_channelTimestamp
[r
->m_mediaChannel
] : 0;
2000 return RTMP_SendPause(r
, DoPause
, r
->m_pauseStamp
);
2006 RTMP_SendSeek(RTMP
*r
, int iTime
)
2009 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2012 packet
.m_nChannel
= 0x08; /* video channel */
2013 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2014 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2015 packet
.m_nTimeStamp
= 0;
2016 packet
.m_nInfoField2
= 0;
2017 packet
.m_hasAbsTimestamp
= 0;
2018 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2020 enc
= packet
.m_body
;
2021 enc
= AMF_EncodeString(enc
, pend
, &av_seek
);
2022 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
2024 enc
= AMF_EncodeNumber(enc
, pend
, (double)iTime
);
2026 packet
.m_nBodySize
= enc
- packet
.m_body
;
2028 r
->m_read
.flags
|= RTMP_READ_SEEKING
;
2029 r
->m_read
.nResumeTS
= 0;
2031 return RTMP_SendPacket(r
, &packet
, TRUE
);
2035 RTMP_SendServerBW(RTMP
*r
)
2038 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2040 packet
.m_nChannel
= 0x02; /* control channel (invoke) */
2041 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2042 packet
.m_packetType
= RTMP_PACKET_TYPE_SERVER_BW
;
2043 packet
.m_nTimeStamp
= 0;
2044 packet
.m_nInfoField2
= 0;
2045 packet
.m_hasAbsTimestamp
= 0;
2046 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2048 packet
.m_nBodySize
= 4;
2050 AMF_EncodeInt32(packet
.m_body
, pend
, r
->m_nServerBW
);
2051 return RTMP_SendPacket(r
, &packet
, FALSE
);
2055 RTMP_SendClientBW(RTMP
*r
)
2058 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2060 packet
.m_nChannel
= 0x02; /* control channel (invoke) */
2061 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2062 packet
.m_packetType
= RTMP_PACKET_TYPE_CLIENT_BW
;
2063 packet
.m_nTimeStamp
= 0;
2064 packet
.m_nInfoField2
= 0;
2065 packet
.m_hasAbsTimestamp
= 0;
2066 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2068 packet
.m_nBodySize
= 5;
2070 AMF_EncodeInt32(packet
.m_body
, pend
, r
->m_nClientBW
);
2071 packet
.m_body
[4] = r
->m_nClientBW2
;
2072 return RTMP_SendPacket(r
, &packet
, FALSE
);
2076 SendBytesReceived(RTMP
*r
)
2079 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2081 packet
.m_nChannel
= 0x02; /* control channel (invoke) */
2082 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2083 packet
.m_packetType
= RTMP_PACKET_TYPE_BYTES_READ_REPORT
;
2084 packet
.m_nTimeStamp
= 0;
2085 packet
.m_nInfoField2
= 0;
2086 packet
.m_hasAbsTimestamp
= 0;
2087 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2089 packet
.m_nBodySize
= 4;
2091 AMF_EncodeInt32(packet
.m_body
, pend
, r
->m_nBytesIn
); /* hard coded for now */
2092 r
->m_nBytesInSent
= r
->m_nBytesIn
;
2094 /*RTMP_Log(RTMP_LOGDEBUG, "Send bytes report. 0x%x (%d bytes)", (unsigned int)m_nBytesIn, m_nBytesIn); */
2095 return RTMP_SendPacket(r
, &packet
, FALSE
);
2101 SendCheckBW(RTMP
*r
)
2104 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2107 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2108 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2109 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2110 packet
.m_nTimeStamp
= 0; /* RTMP_GetTime(); */
2111 packet
.m_nInfoField2
= 0;
2112 packet
.m_hasAbsTimestamp
= 0;
2113 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2115 enc
= packet
.m_body
;
2116 enc
= AMF_EncodeString(enc
, pend
, &av__checkbw
);
2117 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
2120 packet
.m_nBodySize
= enc
- packet
.m_body
;
2122 /* triggers _onbwcheck and eventually results in _onbwdone */
2123 return RTMP_SendPacket(r
, &packet
, FALSE
);
2129 SendCheckBWResult(RTMP
*r
, double txn
)
2132 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2135 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2136 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2137 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2138 packet
.m_nTimeStamp
= 0x16 * r
->m_nBWCheckCounter
; /* temp inc value. till we figure it out. */
2139 packet
.m_nInfoField2
= 0;
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__result
);
2145 enc
= AMF_EncodeNumber(enc
, pend
, txn
);
2147 enc
= AMF_EncodeNumber(enc
, pend
, (double)r
->m_nBWCheckCounter
++);
2149 packet
.m_nBodySize
= enc
- packet
.m_body
;
2151 return RTMP_SendPacket(r
, &packet
, FALSE
);
2158 SendPong(RTMP
*r
, double txn
)
2161 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2164 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2165 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2166 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2167 packet
.m_nTimeStamp
= 0x16 * r
->m_nBWCheckCounter
; /* temp inc value. till we figure it out. */
2168 packet
.m_nInfoField2
= 0;
2169 packet
.m_hasAbsTimestamp
= 0;
2170 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2172 enc
= packet
.m_body
;
2173 enc
= AMF_EncodeString(enc
, pend
, &av_pong
);
2174 enc
= AMF_EncodeNumber(enc
, pend
, txn
);
2177 packet
.m_nBodySize
= enc
- packet
.m_body
;
2179 return RTMP_SendPacket(r
, &packet
, FALSE
);
2188 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
2191 packet
.m_nChannel
= 0x08; /* we make 8 our stream channel */
2192 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2193 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2194 packet
.m_nTimeStamp
= 0;
2195 packet
.m_nInfoField2
= r
->m_stream_id
; /*0x01000000; */
2196 packet
.m_hasAbsTimestamp
= 0;
2197 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2199 enc
= packet
.m_body
;
2200 enc
= AMF_EncodeString(enc
, pend
, &av_play
);
2201 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
2204 RTMP_Log(RTMP_LOGDEBUG
, "%s, seekTime=%d, stopTime=%d, sending play: %s",
2205 __FUNCTION__
, r
->Link
.seekTime
, r
->Link
.stopTime
,
2206 r
->Link
.playpath
.av_val
);
2207 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
2211 /* Optional parameters start and len.
2213 * start: -2, -1, 0, positive number
2214 * -2: looks for a live stream, then a recorded stream,
2215 * if not found any open a live stream
2216 * -1: plays a live stream
2217 * >=0: plays a recorded streams from 'start' milliseconds
2219 if (r
->Link
.lFlags
& RTMP_LF_LIVE
)
2220 enc
= AMF_EncodeNumber(enc
, pend
, -1000.0);
2223 if (r
->Link
.seekTime
> 0.0)
2224 enc
= AMF_EncodeNumber(enc
, pend
, r
->Link
.seekTime
); /* resume from here */
2226 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 */
2231 /* len: -1, 0, positive number
2232 * -1: plays live or recorded stream to the end (default)
2233 * 0: plays a frame 'start' ms away from the beginning
2234 * >0: plays a live or recoded stream for 'len' milliseconds
2236 /*enc += EncodeNumber(enc, -1.0); */ /* len */
2237 if (r
->Link
.stopTime
)
2239 enc
= AMF_EncodeNumber(enc
, pend
, r
->Link
.stopTime
- r
->Link
.seekTime
);
2244 packet
.m_nBodySize
= enc
- packet
.m_body
;
2246 return RTMP_SendPacket(r
, &packet
, TRUE
);
2253 SendPlaylist(RTMP
*r
)
2256 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
2259 packet
.m_nChannel
= 0x08; /* we make 8 our stream channel */
2260 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2261 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2262 packet
.m_nTimeStamp
= 0;
2263 packet
.m_nInfoField2
= r
->m_stream_id
; /*0x01000000; */
2264 packet
.m_hasAbsTimestamp
= 0;
2265 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2267 enc
= packet
.m_body
;
2268 enc
= AMF_EncodeString(enc
, pend
, &av_set_playlist
);
2269 enc
= AMF_EncodeNumber(enc
, pend
, 0);
2271 *enc
++ = AMF_ECMA_ARRAY
;
2275 *enc
++ = AMF_OBJECT
;
2276 enc
= AMF_EncodeNamedString(enc
, pend
, &av_0
, &r
->Link
.playpath
);
2279 if (enc
+ 3 >= pend
)
2283 *enc
++ = AMF_OBJECT_END
;
2285 packet
.m_nBodySize
= enc
- packet
.m_body
;
2287 return RTMP_SendPacket(r
, &packet
, TRUE
);
2291 SendSecureTokenResponse(RTMP
*r
, AVal
*resp
)
2294 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
2297 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2298 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2299 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2300 packet
.m_nTimeStamp
= 0;
2301 packet
.m_nInfoField2
= 0;
2302 packet
.m_hasAbsTimestamp
= 0;
2303 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2305 enc
= packet
.m_body
;
2306 enc
= AMF_EncodeString(enc
, pend
, &av_secureTokenResponse
);
2307 enc
= AMF_EncodeNumber(enc
, pend
, 0.0);
2309 enc
= AMF_EncodeString(enc
, pend
, resp
);
2313 packet
.m_nBodySize
= enc
- packet
.m_body
;
2315 return RTMP_SendPacket(r
, &packet
, FALSE
);
2319 from http://jira.red5.org/confluence/display/docs/Ping:
2321 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.
2323 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.
2325 * 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.
2326 * type 1: Tell the stream to clear the playing buffer.
2327 * type 3: Buffer time of the client. The third parameter is the buffer time in millisecond.
2328 * type 4: Reset a stream. Used together with type 0 in the case of VOD. Often sent before type 0.
2329 * type 6: Ping the client from server. The second parameter is the current time.
2330 * type 7: Pong reply from client. The second parameter is the time the server sent with his ping request.
2331 * type 26: SWFVerification request
2332 * type 27: SWFVerification response
2335 RTMP_SendCtrl(RTMP
*r
, short nType
, unsigned int nObject
, unsigned int nTime
)
2338 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2342 RTMP_Log(RTMP_LOGDEBUG
, "sending ctrl. type: 0x%04x", (unsigned short)nType
);
2344 packet
.m_nChannel
= 0x02; /* control channel (ping) */
2345 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2346 packet
.m_packetType
= RTMP_PACKET_TYPE_CONTROL
;
2347 packet
.m_nTimeStamp
= 0; /* RTMP_GetTime(); */
2348 packet
.m_nInfoField2
= 0;
2349 packet
.m_hasAbsTimestamp
= 0;
2350 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2353 case 0x03: nSize
= 10; break; /* buffer time */
2354 case 0x1A: nSize
= 3; break; /* SWF verify request */
2355 case 0x1B: nSize
= 44; break; /* SWF verify response */
2356 default: nSize
= 6; break;
2359 packet
.m_nBodySize
= nSize
;
2361 buf
= packet
.m_body
;
2362 buf
= AMF_EncodeInt16(buf
, pend
, nType
);
2367 memcpy(buf
, r
->Link
.SWFVerificationResponse
, 42);
2368 RTMP_Log(RTMP_LOGDEBUG
, "Sending SWFVerification response: ");
2369 RTMP_LogHex(RTMP_LOGDEBUG
, (uint8_t *)packet
.m_body
, packet
.m_nBodySize
);
2372 else if (nType
== 0x1A)
2374 *buf
= nObject
& 0xff;
2379 buf
= AMF_EncodeInt32(buf
, pend
, nObject
);
2382 buf
= AMF_EncodeInt32(buf
, pend
, nTime
);
2385 return RTMP_SendPacket(r
, &packet
, FALSE
);
2389 AV_erase(RTMP_METHOD
*vals
, int *num
, int i
, int freeit
)
2392 free(vals
[i
].name
.av_val
);
2394 for (; i
< *num
; i
++)
2396 vals
[i
] = vals
[i
+ 1];
2398 vals
[i
].name
.av_val
= NULL
;
2399 vals
[i
].name
.av_len
= 0;
2404 RTMP_DropRequest(RTMP
*r
, int i
, int freeit
)
2406 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, freeit
);
2410 AV_queue(RTMP_METHOD
**vals
, int *num
, AVal
*av
, int txn
)
2414 *vals
= realloc(*vals
, (*num
+ 16) * sizeof(RTMP_METHOD
));
2415 tmp
= malloc(av
->av_len
+ 1);
2416 memcpy(tmp
, av
->av_val
, av
->av_len
);
2417 tmp
[av
->av_len
] = '\0';
2418 (*vals
)[*num
].num
= txn
;
2419 (*vals
)[*num
].name
.av_len
= av
->av_len
;
2420 (*vals
)[(*num
)++].name
.av_val
= tmp
;
2424 AV_clear(RTMP_METHOD
*vals
, int num
)
2427 for (i
= 0; i
< num
; i
++)
2428 free(vals
[i
].name
.av_val
);
2435 b64enc(const unsigned char *input
, int length
, char *output
, int maxsize
)
2438 int buf_size
= maxsize
;
2439 if(base64_encode((unsigned char *) output
, &buf_size
, input
, length
) == 0)
2441 output
[buf_size
] = '\0';
2446 RTMP_Log(RTMP_LOGDEBUG
, "%s, error", __FUNCTION__
);
2449 #elif defined(USE_GNUTLS)
2450 if (BASE64_ENCODE_RAW_LENGTH(length
) <= maxsize
)
2451 base64_encode_raw((uint8_t*) output
, length
, input
);
2454 RTMP_Log(RTMP_LOGDEBUG
, "%s, error", __FUNCTION__
);
2457 #else /* USE_OPENSSL */
2461 b64
= BIO_new(BIO_f_base64());
2462 bmem
= BIO_new(BIO_s_mem());
2463 b64
= BIO_push(b64
, bmem
);
2464 BIO_write(b64
, input
, length
);
2465 if (BIO_flush(b64
) == 1)
2467 BIO_get_mem_ptr(b64
, &bptr
);
2468 memcpy(output
, bptr
->data
, bptr
->length
-1);
2469 output
[bptr
->length
-1] = '\0';
2473 RTMP_Log(RTMP_LOGDEBUG
, "%s, error", __FUNCTION__
);
2482 #define MD5_CTX md5_context
2483 #define MD5_Init(ctx) md5_starts(ctx)
2484 #define MD5_Update(ctx,data,len) md5_update(ctx,data,len)
2485 #define MD5_Final(dig,ctx) md5_finish(ctx,dig)
2486 #elif defined(USE_GNUTLS)
2487 typedef struct md5_ctx MD5_CTX
2488 #define MD5_Init(ctx) md5_init(ctx)
2489 #define MD5_Update(ctx,data,len) md5_update(ctx,len,data)
2490 #define MD5_Final(dig,ctx) md5_digest(ctx,MD5_DIGEST_LENGTH,dig)
2494 static const AVal av_authmod_adobe
= AVC("authmod=adobe");
2495 static const AVal av_authmod_llnw
= AVC("authmod=llnw");
2497 static void hexenc(unsigned char *inbuf
, int len
, char *dst
)
2501 sprintf(ptr
, "%02x", *inbuf
++);
2508 PublisherAuth(RTMP
*r
, AVal
*description
)
2510 char *token_in
= NULL
;
2512 unsigned char md5sum_val
[MD5_DIGEST_LENGTH
+1];
2514 int challenge2_data
;
2515 #define RESPONSE_LEN 32
2516 #define CHALLENGE2_LEN 16
2517 #define SALTED2_LEN (32+8+8+8)
2518 #define B64DIGEST_LEN 22 /* 16 byte digest => 22 b64 chars */
2519 #define B64INT_LEN 6 /* 4 byte int => 6 b64 chars */
2520 #define HEXHASH_LEN (2*MD5_DIGEST_LENGTH)
2521 char response
[RESPONSE_LEN
];
2522 char challenge2
[CHALLENGE2_LEN
];
2523 char salted2
[SALTED2_LEN
];
2526 if (strstr(description
->av_val
, av_authmod_adobe
.av_val
) != NULL
)
2528 if(strstr(description
->av_val
, "code=403 need auth") != NULL
)
2530 if (strstr(r
->Link
.app
.av_val
, av_authmod_adobe
.av_val
) != NULL
) {
2531 RTMP_Log(RTMP_LOGERROR
, "%s, wrong pubUser & pubPasswd for publisher auth", __FUNCTION__
);
2532 r
->Link
.pFlags
|= RTMP_PUB_CLEAN
;
2534 } else if(r
->Link
.pubUser
.av_len
&& r
->Link
.pubPasswd
.av_len
) {
2535 pubToken
.av_val
= malloc(r
->Link
.pubUser
.av_len
+ av_authmod_adobe
.av_len
+ 8);
2536 pubToken
.av_len
= sprintf(pubToken
.av_val
, "?%s&user=%s",
2537 av_authmod_adobe
.av_val
,
2538 r
->Link
.pubUser
.av_val
);
2539 RTMP_Log(RTMP_LOGDEBUG
, "%s, pubToken1: %s", __FUNCTION__
, pubToken
.av_val
);
2540 r
->Link
.pFlags
|= RTMP_PUB_NAME
;
2542 RTMP_Log(RTMP_LOGERROR
, "%s, need to set pubUser & pubPasswd for publisher auth", __FUNCTION__
);
2543 r
->Link
.pFlags
|= RTMP_PUB_CLEAN
;
2547 else if((token_in
= strstr(description
->av_val
, "?reason=needauth")) != NULL
)
2549 char *par
, *val
= NULL
, *orig_ptr
;
2550 AVal user
, salt
, opaque
, challenge
, *aptr
= NULL
;
2552 challenge
.av_len
= 0;
2554 ptr
= orig_ptr
= strdup(token_in
);
2558 ptr
= strchr(par
, '&');
2562 val
= strchr(par
, '=');
2567 aptr
->av_len
= par
- aptr
->av_val
- 1;
2570 if (strcmp(par
, "user") == 0){
2573 } else if (strcmp(par
, "salt") == 0){
2576 } else if (strcmp(par
, "opaque") == 0){
2577 opaque
.av_val
= val
;
2579 } else if (strcmp(par
, "challenge") == 0){
2580 challenge
.av_val
= val
;
2584 RTMP_Log(RTMP_LOGDEBUG
, "%s, par:\"%s\" = val:\"%s\"", __FUNCTION__
, par
, val
);
2587 aptr
->av_len
= strlen(aptr
->av_val
);
2589 /* hash1 = base64enc(md5(user + _aodbeAuthSalt + password)) */
2591 MD5_Update(&md5ctx
, user
.av_val
, user
.av_len
);
2592 MD5_Update(&md5ctx
, salt
.av_val
, salt
.av_len
);
2593 MD5_Update(&md5ctx
, r
->Link
.pubPasswd
.av_val
, r
->Link
.pubPasswd
.av_len
);
2594 MD5_Final(md5sum_val
, &md5ctx
);
2595 RTMP_Log(RTMP_LOGDEBUG
, "%s, md5(%s%s%s) =>", __FUNCTION__
,
2596 user
.av_val
, salt
.av_val
, r
->Link
.pubPasswd
.av_val
);
2597 RTMP_LogHexString(RTMP_LOGDEBUG
, md5sum_val
, MD5_DIGEST_LENGTH
);
2599 b64enc(md5sum_val
, MD5_DIGEST_LENGTH
, salted2
, SALTED2_LEN
);
2600 RTMP_Log(RTMP_LOGDEBUG
, "%s, b64(md5_1) = %s", __FUNCTION__
, salted2
);
2602 /* FIXME: what byte order does this depend on? */
2603 challenge2_data
= rand();
2605 b64enc((unsigned char *) &challenge2_data
, sizeof(int), challenge2
, CHALLENGE2_LEN
);
2606 RTMP_Log(RTMP_LOGDEBUG
, "%s, b64(%d) = %s", __FUNCTION__
, challenge2_data
, challenge2
);
2609 MD5_Update(&md5ctx
, salted2
, B64DIGEST_LEN
);
2610 /* response = base64enc(md5(hash1 + opaque + challenge2)) */
2612 MD5_Update(&md5ctx
, opaque
.av_val
, opaque
.av_len
);
2613 if (challenge
.av_len
)
2614 MD5_Update(&md5ctx
, challenge
.av_val
, challenge
.av_len
);
2615 MD5_Update(&md5ctx
, challenge2
, B64INT_LEN
);
2616 MD5_Final(md5sum_val
, &md5ctx
);
2618 RTMP_Log(RTMP_LOGDEBUG
, "%s, md5(%s%s%s) =>", __FUNCTION__
,
2619 salted2
, opaque
.av_len
? opaque
.av_val
: "", challenge2
);
2620 RTMP_LogHexString(RTMP_LOGDEBUG
, md5sum_val
, MD5_DIGEST_LENGTH
);
2622 b64enc(md5sum_val
, MD5_DIGEST_LENGTH
, response
, RESPONSE_LEN
);
2623 RTMP_Log(RTMP_LOGDEBUG
, "%s, b64(md5_2) = %s", __FUNCTION__
, response
);
2625 /* have all hashes, create auth token for the end of app */
2626 pubToken
.av_val
= malloc(32 + B64INT_LEN
+ B64DIGEST_LEN
+ opaque
.av_len
);
2627 pubToken
.av_len
= sprintf(pubToken
.av_val
,
2628 "&challenge=%s&response=%s&opaque=%s",
2631 opaque
.av_len
? opaque
.av_val
: "");
2632 RTMP_Log(RTMP_LOGDEBUG
, "%s, pubToken2: %s", __FUNCTION__
, pubToken
.av_val
);
2634 r
->Link
.pFlags
|= RTMP_PUB_RESP
|RTMP_PUB_CLATE
;
2636 else if(strstr(description
->av_val
, "?reason=authfailed") != NULL
)
2638 RTMP_Log(RTMP_LOGERROR
, "%s, Authentication failed: wrong password", __FUNCTION__
);
2639 r
->Link
.pFlags
|= RTMP_PUB_CLEAN
;
2642 else if(strstr(description
->av_val
, "?reason=nosuchuser") != NULL
)
2644 RTMP_Log(RTMP_LOGERROR
, "%s, Authentication failed: no such user", __FUNCTION__
);
2645 r
->Link
.pFlags
|= RTMP_PUB_CLEAN
;
2650 RTMP_Log(RTMP_LOGERROR
, "%s, Authentication failed: unknown auth mode: %s",
2651 __FUNCTION__
, description
->av_val
);
2652 r
->Link
.pFlags
|= RTMP_PUB_CLEAN
;
2656 ptr
= malloc(r
->Link
.app
.av_len
+ pubToken
.av_len
);
2657 strncpy(ptr
, r
->Link
.app
.av_val
, r
->Link
.app
.av_len
);
2658 strncpy(ptr
+ r
->Link
.app
.av_len
, pubToken
.av_val
, pubToken
.av_len
);
2659 r
->Link
.app
.av_len
+= pubToken
.av_len
;
2660 if(r
->Link
.pFlags
& RTMP_PUB_ALLOC
)
2661 free(r
->Link
.app
.av_val
);
2662 r
->Link
.app
.av_val
= ptr
;
2664 ptr
= malloc(r
->Link
.tcUrl
.av_len
+ pubToken
.av_len
);
2665 strncpy(ptr
, r
->Link
.tcUrl
.av_val
, r
->Link
.tcUrl
.av_len
);
2666 strncpy(ptr
+ r
->Link
.tcUrl
.av_len
, pubToken
.av_val
, pubToken
.av_len
);
2667 r
->Link
.tcUrl
.av_len
+= pubToken
.av_len
;
2668 if(r
->Link
.pFlags
& RTMP_PUB_ALLOC
)
2669 free(r
->Link
.tcUrl
.av_val
);
2670 r
->Link
.tcUrl
.av_val
= ptr
;
2672 free(pubToken
.av_val
);
2673 r
->Link
.pFlags
|= RTMP_PUB_ALLOC
;
2675 RTMP_Log(RTMP_LOGDEBUG
, "%s, new app: %.*s tcUrl: %.*s playpath: %s", __FUNCTION__
,
2676 r
->Link
.app
.av_len
, r
->Link
.app
.av_val
,
2677 r
->Link
.tcUrl
.av_len
, r
->Link
.tcUrl
.av_val
,
2678 r
->Link
.playpath
.av_val
);
2680 else if (strstr(description
->av_val
, av_authmod_llnw
.av_val
) != NULL
)
2682 if(strstr(description
->av_val
, "code=403 need auth") != NULL
)
2684 /* This part seems to be the same for llnw and adobe */
2686 if (strstr(r
->Link
.app
.av_val
, av_authmod_llnw
.av_val
) != NULL
) {
2687 RTMP_Log(RTMP_LOGERROR
, "%s, wrong pubUser & pubPasswd for publisher auth", __FUNCTION__
);
2688 r
->Link
.pFlags
|= RTMP_PUB_CLEAN
;
2690 } else if(r
->Link
.pubUser
.av_len
&& r
->Link
.pubPasswd
.av_len
) {
2691 pubToken
.av_val
= malloc(r
->Link
.pubUser
.av_len
+ av_authmod_llnw
.av_len
+ 8);
2692 pubToken
.av_len
= sprintf(pubToken
.av_val
, "?%s&user=%s",
2693 av_authmod_llnw
.av_val
,
2694 r
->Link
.pubUser
.av_val
);
2695 RTMP_Log(RTMP_LOGDEBUG
, "%s, pubToken1: %s", __FUNCTION__
, pubToken
.av_val
);
2696 r
->Link
.pFlags
|= RTMP_PUB_NAME
;
2698 RTMP_Log(RTMP_LOGERROR
, "%s, need to set pubUser & pubPasswd for publisher auth", __FUNCTION__
);
2699 r
->Link
.pFlags
|= RTMP_PUB_CLEAN
;
2703 else if((token_in
= strstr(description
->av_val
, "?reason=needauth")) != NULL
)
2706 char *par
, *val
= NULL
;
2707 char hash1
[HEXHASH_LEN
+1], hash2
[HEXHASH_LEN
+1], hash3
[HEXHASH_LEN
+1];
2708 AVal user
, nonce
, *aptr
= NULL
;
2712 * Seems to be closely based on HTTP Digest Auth:
2713 * http://tools.ietf.org/html/rfc2617
2714 * http://en.wikipedia.org/wiki/Digest_access_authentication
2717 const char authmod
[] = "llnw";
2718 const char realm
[] = "live";
2719 const char method
[] = "publish";
2720 const char qop
[] = "auth";
2721 /* nc = 1..connection count (or rather, number of times cnonce has been reused) */
2723 /* nchex = hexenc(nc) (8 hex digits according to RFC 2617) */
2725 /* cnonce = hexenc(4 random bytes) (initialized on first connection) */
2728 ptr
= orig_ptr
= strdup(token_in
);
2729 /* Extract parameters (we need user and nonce) */
2733 ptr
= strchr(par
, '&');
2737 val
= strchr(par
, '=');
2742 aptr
->av_len
= par
- aptr
->av_val
- 1;
2745 if (strcmp(par
, "user") == 0){
2748 } else if (strcmp(par
, "nonce") == 0){
2753 RTMP_Log(RTMP_LOGDEBUG
, "%s, par:\"%s\" = val:\"%s\"", __FUNCTION__
, par
, val
);
2756 aptr
->av_len
= strlen(aptr
->av_val
);
2758 /* FIXME: handle case where user==NULL or nonce==NULL */
2760 sprintf(nchex
, "%08x", nc
);
2761 sprintf(cnonce
, "%08x", rand());
2763 /* hash1 = hexenc(md5(user + ":" + realm + ":" + password)) */
2765 MD5_Update(&md5ctx
, user
.av_val
, user
.av_len
);
2766 MD5_Update(&md5ctx
, ":", 1);
2767 MD5_Update(&md5ctx
, realm
, sizeof(realm
)-1);
2768 MD5_Update(&md5ctx
, ":", 1);
2769 MD5_Update(&md5ctx
, r
->Link
.pubPasswd
.av_val
, r
->Link
.pubPasswd
.av_len
);
2770 MD5_Final(md5sum_val
, &md5ctx
);
2771 RTMP_Log(RTMP_LOGDEBUG
, "%s, md5(%s:%s:%s) =>", __FUNCTION__
,
2772 user
.av_val
, realm
, r
->Link
.pubPasswd
.av_val
);
2773 RTMP_LogHexString(RTMP_LOGDEBUG
, md5sum_val
, MD5_DIGEST_LENGTH
);
2774 hexenc(md5sum_val
, MD5_DIGEST_LENGTH
, hash1
);
2776 /* hash2 = hexenc(md5(method + ":/" + app + "/" + appInstance)) */
2777 /* Extract appname + appinstance without query parameters */
2778 apptmp
= r
->Link
.app
;
2779 ptr
= strchr(apptmp
.av_val
, '?');
2781 apptmp
.av_len
= ptr
- apptmp
.av_val
;
2784 MD5_Update(&md5ctx
, method
, sizeof(method
)-1);
2785 MD5_Update(&md5ctx
, ":/", 2);
2786 MD5_Update(&md5ctx
, apptmp
.av_val
, apptmp
.av_len
);
2787 MD5_Final(md5sum_val
, &md5ctx
);
2788 RTMP_Log(RTMP_LOGDEBUG
, "%s, md5(%s:/%.*s) =>", __FUNCTION__
,
2789 method
, apptmp
.av_len
, apptmp
.av_val
);
2790 RTMP_LogHexString(RTMP_LOGDEBUG
, md5sum_val
, MD5_DIGEST_LENGTH
);
2791 hexenc(md5sum_val
, MD5_DIGEST_LENGTH
, hash2
);
2793 /* hash3 = hexenc(md5(hash1 + ":" + nonce + ":" + nchex + ":" + cnonce + ":" + qop + ":" + hash2)) */
2795 MD5_Update(&md5ctx
, hash1
, HEXHASH_LEN
);
2796 MD5_Update(&md5ctx
, ":", 1);
2797 MD5_Update(&md5ctx
, nonce
.av_val
, nonce
.av_len
);
2798 MD5_Update(&md5ctx
, ":", 1);
2799 MD5_Update(&md5ctx
, nchex
, sizeof(nchex
)-1);
2800 MD5_Update(&md5ctx
, ":", 1);
2801 MD5_Update(&md5ctx
, cnonce
, sizeof(cnonce
)-1);
2802 MD5_Update(&md5ctx
, ":", 1);
2803 MD5_Update(&md5ctx
, qop
, sizeof(qop
)-1);
2804 MD5_Update(&md5ctx
, ":", 1);
2805 MD5_Update(&md5ctx
, hash2
, HEXHASH_LEN
);
2806 MD5_Final(md5sum_val
, &md5ctx
);
2807 RTMP_Log(RTMP_LOGDEBUG
, "%s, md5(%s:%s:%s:%s:%s:%s) =>", __FUNCTION__
,
2808 hash1
, nonce
.av_val
, nchex
, cnonce
, qop
, hash2
);
2809 RTMP_LogHexString(RTMP_LOGDEBUG
, md5sum_val
, MD5_DIGEST_LENGTH
);
2810 hexenc(md5sum_val
, MD5_DIGEST_LENGTH
, hash3
);
2812 /* pubToken = &authmod=<authmod>&user=<username>&nonce=<nonce>&cnonce=<cnonce>&nc=<nchex>&response=<hash3> */
2813 /* Append nonces and response to query string which already contains
2815 pubToken
.av_val
= malloc(64 + sizeof(authmod
)-1 + user
.av_len
+ nonce
.av_len
+ sizeof(cnonce
)-1 + sizeof(nchex
)-1 + HEXHASH_LEN
);
2816 sprintf(pubToken
.av_val
,
2817 "&nonce=%s&cnonce=%s&nc=%s&response=%s",
2818 nonce
.av_val
, cnonce
, nchex
, hash3
);
2819 pubToken
.av_len
= strlen(pubToken
.av_val
);
2820 RTMP_Log(RTMP_LOGDEBUG
, "%s, pubToken2: %s", __FUNCTION__
, pubToken
.av_val
);
2821 r
->Link
.pFlags
|= RTMP_PUB_RESP
|RTMP_PUB_CLATE
;
2825 else if(strstr(description
->av_val
, "?reason=authfail") != NULL
)
2827 RTMP_Log(RTMP_LOGERROR
, "%s, Authentication failed", __FUNCTION__
);
2828 r
->Link
.pFlags
|= RTMP_PUB_CLEAN
;
2831 else if(strstr(description
->av_val
, "?reason=nosuchuser") != NULL
)
2833 RTMP_Log(RTMP_LOGERROR
, "%s, Authentication failed: no such user", __FUNCTION__
);
2834 r
->Link
.pFlags
|= RTMP_PUB_CLEAN
;
2839 RTMP_Log(RTMP_LOGERROR
, "%s, Authentication failed: unknown auth mode: %s",
2840 __FUNCTION__
, description
->av_val
);
2841 r
->Link
.pFlags
|= RTMP_PUB_CLEAN
;
2845 ptr
= malloc(r
->Link
.app
.av_len
+ pubToken
.av_len
);
2846 strncpy(ptr
, r
->Link
.app
.av_val
, r
->Link
.app
.av_len
);
2847 strncpy(ptr
+ r
->Link
.app
.av_len
, pubToken
.av_val
, pubToken
.av_len
);
2848 r
->Link
.app
.av_len
+= pubToken
.av_len
;
2849 if(r
->Link
.pFlags
& RTMP_PUB_ALLOC
)
2850 free(r
->Link
.app
.av_val
);
2851 r
->Link
.app
.av_val
= ptr
;
2853 ptr
= malloc(r
->Link
.tcUrl
.av_len
+ pubToken
.av_len
);
2854 strncpy(ptr
, r
->Link
.tcUrl
.av_val
, r
->Link
.tcUrl
.av_len
);
2855 strncpy(ptr
+ r
->Link
.tcUrl
.av_len
, pubToken
.av_val
, pubToken
.av_len
);
2856 r
->Link
.tcUrl
.av_len
+= pubToken
.av_len
;
2857 if(r
->Link
.pFlags
& RTMP_PUB_ALLOC
)
2858 free(r
->Link
.tcUrl
.av_val
);
2859 r
->Link
.tcUrl
.av_val
= ptr
;
2861 free(pubToken
.av_val
);
2862 r
->Link
.pFlags
|= RTMP_PUB_ALLOC
;
2864 RTMP_Log(RTMP_LOGDEBUG
, "%s, new app: %.*s tcUrl: %.*s playpath: %s", __FUNCTION__
,
2865 r
->Link
.app
.av_len
, r
->Link
.app
.av_val
,
2866 r
->Link
.tcUrl
.av_len
, r
->Link
.tcUrl
.av_val
,
2867 r
->Link
.playpath
.av_val
);
2879 SAVC(onFCSubscribe
);
2880 SAVC(onFCUnsubscribe
);
2889 SAVC(playlist_ready
);
2890 static const AVal av_NetStream_Failed
= AVC("NetStream.Failed");
2891 static const AVal av_NetStream_Play_Failed
= AVC("NetStream.Play.Failed");
2892 static const AVal av_NetStream_Play_StreamNotFound
=
2893 AVC("NetStream.Play.StreamNotFound");
2894 static const AVal av_NetConnection_Connect_InvalidApp
=
2895 AVC("NetConnection.Connect.InvalidApp");
2896 static const AVal av_NetStream_Play_Start
= AVC("NetStream.Play.Start");
2897 static const AVal av_NetStream_Play_Complete
= AVC("NetStream.Play.Complete");
2898 static const AVal av_NetStream_Play_Stop
= AVC("NetStream.Play.Stop");
2899 static const AVal av_NetStream_Seek_Notify
= AVC("NetStream.Seek.Notify");
2900 static const AVal av_NetStream_Pause_Notify
= AVC("NetStream.Pause.Notify");
2901 static const AVal av_NetStream_Play_PublishNotify
=
2902 AVC("NetStream.Play.PublishNotify");
2903 static const AVal av_NetStream_Play_UnpublishNotify
=
2904 AVC("NetStream.Play.UnpublishNotify");
2905 static const AVal av_NetStream_Publish_Start
= AVC("NetStream.Publish.Start");
2906 static const AVal av_NetConnection_Connect_Rejected
=
2907 AVC("NetConnection.Connect.Rejected");
2909 /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */
2911 HandleInvoke(RTMP
*r
, const char *body
, unsigned int nBodySize
)
2917 if (body
[0] != 0x02) /* make sure it is a string method name we start with */
2919 RTMP_Log(RTMP_LOGWARNING
, "%s, Sanity failed. no string method in invoke packet",
2924 nRes
= AMF_Decode(&obj
, body
, nBodySize
, FALSE
);
2927 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding invoke packet", __FUNCTION__
);
2932 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 0), &method
);
2933 txn
= AMFProp_GetNumber(AMF_GetProp(&obj
, NULL
, 1));
2934 RTMP_Log(RTMP_LOGDEBUG
, "%s, server invoking <%s>", __FUNCTION__
, method
.av_val
);
2936 if (AVMATCH(&method
, &av__result
))
2938 AVal methodInvoked
= {0};
2941 for (i
=0; i
<r
->m_numCalls
; i
++) {
2942 if (r
->m_methodCalls
[i
].num
== (int)txn
) {
2943 methodInvoked
= r
->m_methodCalls
[i
].name
;
2944 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, FALSE
);
2948 if (!methodInvoked
.av_val
) {
2949 RTMP_Log(RTMP_LOGDEBUG
, "%s, received result id %f without matching request",
2954 RTMP_Log(RTMP_LOGDEBUG
, "%s, received result for method call <%s>", __FUNCTION__
,
2955 methodInvoked
.av_val
);
2957 if (AVMATCH(&methodInvoked
, &av_connect
))
2959 if (r
->Link
.token
.av_len
)
2961 AMFObjectProperty p
;
2962 if (RTMP_FindFirstMatchingProperty(&obj
, &av_secureToken
, &p
))
2964 DecodeTEA(&r
->Link
.token
, &p
.p_vu
.p_aval
);
2965 SendSecureTokenResponse(r
, &p
.p_vu
.p_aval
);
2968 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
2970 SendReleaseStream(r
);
2975 RTMP_SendServerBW(r
);
2976 RTMP_SendCtrl(r
, 3, 0, 300);
2978 RTMP_SendCreateStream(r
);
2980 if (!(r
->Link
.protocol
& RTMP_FEATURE_WRITE
))
2982 /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */
2983 if (r
->Link
.usherToken
.av_len
)
2984 SendUsherToken(r
, &r
->Link
.usherToken
);
2985 /* Send the FCSubscribe if live stream or if subscribepath is set */
2986 if (r
->Link
.subscribepath
.av_len
)
2987 SendFCSubscribe(r
, &r
->Link
.subscribepath
);
2988 else if (r
->Link
.lFlags
& RTMP_LF_LIVE
)
2989 SendFCSubscribe(r
, &r
->Link
.playpath
);
2992 else if (AVMATCH(&methodInvoked
, &av_createStream
))
2994 r
->m_stream_id
= (int)AMFProp_GetNumber(AMF_GetProp(&obj
, NULL
, 3));
2996 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
3002 if (r
->Link
.lFlags
& RTMP_LF_PLST
)
3005 RTMP_SendCtrl(r
, 3, r
->m_stream_id
, r
->m_nBufferMS
);
3008 else if (AVMATCH(&methodInvoked
, &av_play
) ||
3009 AVMATCH(&methodInvoked
, &av_publish
))
3011 r
->m_bPlaying
= TRUE
;
3013 free(methodInvoked
.av_val
);
3015 else if (AVMATCH(&method
, &av_onBWDone
))
3017 if (!r
->m_nBWCheckCounter
)
3020 else if (AVMATCH(&method
, &av_onFCSubscribe
))
3022 /* SendOnFCSubscribe(); */
3024 else if (AVMATCH(&method
, &av_onFCUnsubscribe
))
3029 else if (AVMATCH(&method
, &av_ping
))
3033 else if (AVMATCH(&method
, &av__onbwcheck
))
3035 SendCheckBWResult(r
, txn
);
3037 else if (AVMATCH(&method
, &av__onbwdone
))
3040 for (i
= 0; i
< r
->m_numCalls
; i
++)
3041 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av__checkbw
))
3043 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
3047 else if (AVMATCH(&method
, &av__error
))
3050 AVal methodInvoked
= {0};
3053 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
3055 for (i
=0; i
<r
->m_numCalls
; i
++)
3057 if (r
->m_methodCalls
[i
].num
== txn
)
3059 methodInvoked
= r
->m_methodCalls
[i
].name
;
3060 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, FALSE
);
3064 if (!methodInvoked
.av_val
)
3066 RTMP_Log(RTMP_LOGDEBUG
, "%s, received result id %f without matching request",
3071 RTMP_Log(RTMP_LOGDEBUG
, "%s, received error for method call <%s>", __FUNCTION__
,
3072 methodInvoked
.av_val
);
3074 if (AVMATCH(&methodInvoked
, &av_connect
))
3077 AVal code
, level
, description
;
3078 AMFProp_GetObject(AMF_GetProp(&obj
, NULL
, 3), &obj2
);
3079 AMFProp_GetString(AMF_GetProp(&obj2
, &av_code
, -1), &code
);
3080 AMFProp_GetString(AMF_GetProp(&obj2
, &av_level
, -1), &level
);
3081 AMFProp_GetString(AMF_GetProp(&obj2
, &av_description
, -1), &description
);
3082 RTMP_Log(RTMP_LOGDEBUG
, "%s, error description: %s", __FUNCTION__
, description
.av_val
);
3083 /* if PublisherAuth returns 1, then reconnect */
3084 PublisherAuth(r
, &description
);
3089 RTMP_Log(RTMP_LOGERROR
, "rtmp server sent error");
3091 free(methodInvoked
.av_val
);
3093 RTMP_Log(RTMP_LOGERROR
, "rtmp server sent error");
3096 else if (AVMATCH(&method
, &av_close
))
3098 RTMP_Log(RTMP_LOGERROR
, "rtmp server requested close");
3101 if ((r
->Link
.protocol
& RTMP_FEATURE_WRITE
) &&
3102 !(r
->Link
.pFlags
& RTMP_PUB_CLEAN
) &&
3103 ( !(r
->Link
.pFlags
& RTMP_PUB_NAME
) ||
3104 !(r
->Link
.pFlags
& RTMP_PUB_RESP
) ||
3105 (r
->Link
.pFlags
& RTMP_PUB_CLATE
) ) )
3108 if(r
->Link
.pFlags
& RTMP_PUB_CLATE
)
3109 r
->Link
.pFlags
|= RTMP_PUB_CLEAN
;
3110 RTMP_Log(RTMP_LOGERROR
, "authenticating publisher");
3112 if (!RTMP_Connect(r
, NULL
) || !RTMP_ConnectStream(r
, 0))
3117 else if (AVMATCH(&method
, &av_onStatus
))
3121 AMFProp_GetObject(AMF_GetProp(&obj
, NULL
, 3), &obj2
);
3122 AMFProp_GetString(AMF_GetProp(&obj2
, &av_code
, -1), &code
);
3123 AMFProp_GetString(AMF_GetProp(&obj2
, &av_level
, -1), &level
);
3125 RTMP_Log(RTMP_LOGDEBUG
, "%s, onStatus: %s", __FUNCTION__
, code
.av_val
);
3126 if (AVMATCH(&code
, &av_NetStream_Failed
)
3127 || AVMATCH(&code
, &av_NetStream_Play_Failed
)
3128 || AVMATCH(&code
, &av_NetStream_Play_StreamNotFound
)
3129 || AVMATCH(&code
, &av_NetConnection_Connect_InvalidApp
))
3131 r
->m_stream_id
= -1;
3133 RTMP_Log(RTMP_LOGERROR
, "Closing connection: %s", code
.av_val
);
3136 else if (AVMATCH(&code
, &av_NetStream_Play_Start
)
3137 || AVMATCH(&code
, &av_NetStream_Play_PublishNotify
))
3140 r
->m_bPlaying
= TRUE
;
3141 for (i
= 0; i
< r
->m_numCalls
; i
++)
3143 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av_play
))
3145 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
3151 else if (AVMATCH(&code
, &av_NetStream_Publish_Start
))
3154 r
->m_bPlaying
= TRUE
;
3155 for (i
= 0; i
< r
->m_numCalls
; i
++)
3157 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av_publish
))
3159 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
3165 /* Return 1 if this is a Play.Complete or Play.Stop */
3166 else if (AVMATCH(&code
, &av_NetStream_Play_Complete
)
3167 || AVMATCH(&code
, &av_NetStream_Play_Stop
)
3168 || AVMATCH(&code
, &av_NetStream_Play_UnpublishNotify
))
3174 else if (AVMATCH(&code
, &av_NetStream_Seek_Notify
))
3176 r
->m_read
.flags
&= ~RTMP_READ_SEEKING
;
3179 else if (AVMATCH(&code
, &av_NetStream_Pause_Notify
))
3181 if (r
->m_pausing
== 1 || r
->m_pausing
== 2)
3183 RTMP_SendPause(r
, FALSE
, r
->m_pauseStamp
);
3188 else if (AVMATCH(&method
, &av_playlist_ready
))
3191 for (i
= 0; i
< r
->m_numCalls
; i
++)
3193 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av_set_playlist
))
3195 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
3210 RTMP_FindFirstMatchingProperty(AMFObject
*obj
, const AVal
*name
,
3211 AMFObjectProperty
* p
)
3214 /* this is a small object search to locate the "duration" property */
3215 for (n
= 0; n
< obj
->o_num
; n
++)
3217 AMFObjectProperty
*prop
= AMF_GetProp(obj
, NULL
, n
);
3219 if (AVMATCH(&prop
->p_name
, name
))
3221 memcpy(p
, prop
, sizeof(*prop
));
3225 if (prop
->p_type
== AMF_OBJECT
)
3227 if (RTMP_FindFirstMatchingProperty(&prop
->p_vu
.p_object
, name
, p
))
3234 /* Like above, but only check if name is a prefix of property */
3236 RTMP_FindPrefixProperty(AMFObject
*obj
, const AVal
*name
,
3237 AMFObjectProperty
* p
)
3240 for (n
= 0; n
< obj
->o_num
; n
++)
3242 AMFObjectProperty
*prop
= AMF_GetProp(obj
, NULL
, n
);
3244 if (prop
->p_name
.av_len
> name
->av_len
&&
3245 !memcmp(prop
->p_name
.av_val
, name
->av_val
, name
->av_len
))
3247 memcpy(p
, prop
, sizeof(*prop
));
3251 if (prop
->p_type
== AMF_OBJECT
)
3253 if (RTMP_FindPrefixProperty(&prop
->p_vu
.p_object
, name
, p
))
3261 DumpMetaData(AMFObject
*obj
)
3263 AMFObjectProperty
*prop
;
3265 for (n
= 0; n
< obj
->o_num
; n
++)
3267 prop
= AMF_GetProp(obj
, NULL
, n
);
3268 if (prop
->p_type
!= AMF_OBJECT
)
3271 switch (prop
->p_type
)
3274 snprintf(str
, 255, "%.2f", prop
->p_vu
.p_number
);
3277 snprintf(str
, 255, "%s",
3278 prop
->p_vu
.p_number
!= 0. ? "TRUE" : "FALSE");
3281 snprintf(str
, 255, "%.*s", prop
->p_vu
.p_aval
.av_len
,
3282 prop
->p_vu
.p_aval
.av_val
);
3285 snprintf(str
, 255, "timestamp:%.2f", prop
->p_vu
.p_number
);
3288 snprintf(str
, 255, "INVALID TYPE 0x%02x",
3289 (unsigned char)prop
->p_type
);
3291 if (prop
->p_name
.av_len
)
3294 if (strlen(str
) >= 1 && str
[strlen(str
) - 1] == '\n')
3295 str
[strlen(str
) - 1] = '\0';
3296 RTMP_Log(RTMP_LOGINFO
, " %-22.*s%s", prop
->p_name
.av_len
,
3297 prop
->p_name
.av_val
, str
);
3302 if (prop
->p_name
.av_len
)
3303 RTMP_Log(RTMP_LOGINFO
, "%.*s:", prop
->p_name
.av_len
, prop
->p_name
.av_val
);
3304 DumpMetaData(&prop
->p_vu
.p_object
);
3316 HandleMetadata(RTMP
*r
, char *body
, unsigned int len
)
3318 /* allright we get some info here, so parse it and print it */
3319 /* also keep duration or filesize to make a nice progress bar */
3325 int nRes
= AMF_Decode(&obj
, body
, len
, FALSE
);
3328 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding meta data packet", __FUNCTION__
);
3333 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 0), &metastring
);
3335 if (AVMATCH(&metastring
, &av_onMetaData
))
3337 AMFObjectProperty prop
;
3339 RTMP_Log(RTMP_LOGINFO
, "Metadata:");
3341 if (RTMP_FindFirstMatchingProperty(&obj
, &av_duration
, &prop
))
3343 r
->m_fDuration
= prop
.p_vu
.p_number
;
3344 /*RTMP_Log(RTMP_LOGDEBUG, "Set duration: %.2f", m_fDuration); */
3346 /* Search for audio or video tags */
3347 if (RTMP_FindPrefixProperty(&obj
, &av_video
, &prop
))
3348 r
->m_read
.dataType
|= 1;
3349 if (RTMP_FindPrefixProperty(&obj
, &av_audio
, &prop
))
3350 r
->m_read
.dataType
|= 4;
3358 HandleChangeChunkSize(RTMP
*r
, const RTMPPacket
*packet
)
3360 if (packet
->m_nBodySize
>= 4)
3362 r
->m_inChunkSize
= AMF_DecodeInt32(packet
->m_body
);
3363 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: chunk size change to %d", __FUNCTION__
,
3369 HandleAudio(RTMP
*r
, const RTMPPacket
*packet
)
3374 HandleVideo(RTMP
*r
, const RTMPPacket
*packet
)
3379 HandleCtrl(RTMP
*r
, const RTMPPacket
*packet
)
3383 if (packet
->m_body
&& packet
->m_nBodySize
>= 2)
3384 nType
= AMF_DecodeInt16(packet
->m_body
);
3385 RTMP_Log(RTMP_LOGDEBUG
, "%s, received ctrl. type: %d, len: %d", __FUNCTION__
, nType
,
3386 packet
->m_nBodySize
);
3387 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
3389 if (packet
->m_nBodySize
>= 6)
3394 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3395 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream Begin %d", __FUNCTION__
, tmp
);
3399 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3400 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream EOF %d", __FUNCTION__
, tmp
);
3401 if (r
->m_pausing
== 1)
3406 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3407 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream Dry %d", __FUNCTION__
, tmp
);
3411 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3412 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream IsRecorded %d", __FUNCTION__
, tmp
);
3415 case 6: /* server ping. reply with pong. */
3416 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3417 RTMP_Log(RTMP_LOGDEBUG
, "%s, Ping %d", __FUNCTION__
, tmp
);
3418 RTMP_SendCtrl(r
, 0x07, tmp
, 0);
3421 /* FMS 3.5 servers send the following two controls to let the client
3422 * know when the server has sent a complete buffer. I.e., when the
3423 * server has sent an amount of data equal to m_nBufferMS in duration.
3424 * The server meters its output so that data arrives at the client
3425 * in realtime and no faster.
3427 * The rtmpdump program tries to set m_nBufferMS as large as
3428 * possible, to force the server to send data as fast as possible.
3429 * In practice, the server appears to cap this at about 1 hour's
3430 * worth of data. After the server has sent a complete buffer, and
3431 * sends this BufferEmpty message, it will wait until the play
3432 * duration of that buffer has passed before sending a new buffer.
3433 * The BufferReady message will be sent when the new buffer starts.
3434 * (There is no BufferReady message for the very first buffer;
3435 * presumably the Stream Begin message is sufficient for that
3438 * If the network speed is much faster than the data bitrate, then
3439 * there may be long delays between the end of one buffer and the
3440 * start of the next.
3442 * Since usually the network allows data to be sent at
3443 * faster than realtime, and rtmpdump wants to download the data
3444 * as fast as possible, we use this RTMP_LF_BUFX hack: when we
3445 * get the BufferEmpty message, we send a Pause followed by an
3446 * Unpause. This causes the server to send the next buffer immediately
3447 * instead of waiting for the full duration to elapse. (That's
3448 * also the purpose of the ToggleStream function, which rtmpdump
3449 * calls if we get a read timeout.)
3451 * Media player apps don't need this hack since they are just
3452 * going to play the data in realtime anyway. It also doesn't work
3453 * for live streams since they obviously can only be sent in
3454 * realtime. And it's all moot if the network speed is actually
3455 * slower than the media bitrate.
3458 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3459 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream BufferEmpty %d", __FUNCTION__
, tmp
);
3460 if (!(r
->Link
.lFlags
& RTMP_LF_BUFX
))
3464 r
->m_pauseStamp
= r
->m_mediaChannel
< r
->m_channelsAllocatedIn
?
3465 r
->m_channelTimestamp
[r
->m_mediaChannel
] : 0;
3466 RTMP_SendPause(r
, TRUE
, r
->m_pauseStamp
);
3469 else if (r
->m_pausing
== 2)
3471 RTMP_SendPause(r
, FALSE
, r
->m_pauseStamp
);
3477 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3478 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream BufferReady %d", __FUNCTION__
, tmp
);
3482 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3483 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream xx %d", __FUNCTION__
, tmp
);
3491 RTMP_Log(RTMP_LOGDEBUG
, "%s, SWFVerification ping received: ", __FUNCTION__
);
3492 if (packet
->m_nBodySize
> 2 && packet
->m_body
[2] > 0x01)
3494 RTMP_Log(RTMP_LOGERROR
,
3495 "%s: SWFVerification Type %d request not supported! Patches welcome...",
3496 __FUNCTION__
, packet
->m_body
[2]);
3499 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
3501 /* respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied */
3502 else if (r
->Link
.SWFSize
)
3504 RTMP_SendCtrl(r
, 0x1B, 0, 0);
3508 RTMP_Log(RTMP_LOGERROR
,
3509 "%s: Ignoring SWFVerification request, use --swfVfy!",
3513 RTMP_Log(RTMP_LOGERROR
,
3514 "%s: Ignoring SWFVerification request, no CRYPTO support!",
3521 HandleServerBW(RTMP
*r
, const RTMPPacket
*packet
)
3523 r
->m_nServerBW
= AMF_DecodeInt32(packet
->m_body
);
3524 RTMP_Log(RTMP_LOGDEBUG
, "%s: server BW = %d", __FUNCTION__
, r
->m_nServerBW
);
3528 HandleClientBW(RTMP
*r
, const RTMPPacket
*packet
)
3530 r
->m_nClientBW
= AMF_DecodeInt32(packet
->m_body
);
3531 if (packet
->m_nBodySize
> 4)
3532 r
->m_nClientBW2
= packet
->m_body
[4];
3534 r
->m_nClientBW2
= -1;
3535 RTMP_Log(RTMP_LOGDEBUG
, "%s: client BW = %d %d", __FUNCTION__
, r
->m_nClientBW
,
3540 DecodeInt32LE(const char *data
)
3542 unsigned char *c
= (unsigned char *)data
;
3545 val
= (c
[3] << 24) | (c
[2] << 16) | (c
[1] << 8) | c
[0];
3550 EncodeInt32LE(char *output
, int nVal
)
3563 RTMP_ReadPacket(RTMP
*r
, RTMPPacket
*packet
)
3565 uint8_t hbuf
[RTMP_MAX_HEADER_SIZE
] = { 0 };
3566 char *header
= (char *)hbuf
;
3567 int nSize
, hSize
, nToRead
, nChunk
;
3568 int didAlloc
= FALSE
;
3570 RTMP_Log(RTMP_LOGDEBUG2
, "%s: fd=%d", __FUNCTION__
, r
->m_sb
.sb_socket
);
3572 if (ReadN(r
, (char *)hbuf
, 1) == 0)
3574 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header", __FUNCTION__
);
3578 packet
->m_headerType
= (hbuf
[0] & 0xc0) >> 6;
3579 packet
->m_nChannel
= (hbuf
[0] & 0x3f);
3581 if (packet
->m_nChannel
== 0)
3583 if (ReadN(r
, (char *)&hbuf
[1], 1) != 1)
3585 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header 2nd byte",
3589 packet
->m_nChannel
= hbuf
[1];
3590 packet
->m_nChannel
+= 64;
3593 else if (packet
->m_nChannel
== 1)
3596 if (ReadN(r
, (char *)&hbuf
[1], 2) != 2)
3598 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header 3nd byte",
3602 tmp
= (hbuf
[2] << 8) + hbuf
[1];
3603 packet
->m_nChannel
= tmp
+ 64;
3604 RTMP_Log(RTMP_LOGDEBUG
, "%s, m_nChannel: %0x", __FUNCTION__
, packet
->m_nChannel
);
3608 nSize
= packetSize
[packet
->m_headerType
];
3610 if (packet
->m_nChannel
>= r
->m_channelsAllocatedIn
)
3612 int n
= packet
->m_nChannel
+ 10;
3613 int *timestamp
= realloc(r
->m_channelTimestamp
, sizeof(int) * n
);
3614 RTMPPacket
**packets
= realloc(r
->m_vecChannelsIn
, sizeof(RTMPPacket
*) * n
);
3616 free(r
->m_channelTimestamp
);
3618 free(r
->m_vecChannelsIn
);
3619 r
->m_channelTimestamp
= timestamp
;
3620 r
->m_vecChannelsIn
= packets
;
3621 if (!timestamp
|| !packets
) {
3622 r
->m_channelsAllocatedIn
= 0;
3625 memset(r
->m_channelTimestamp
+ r
->m_channelsAllocatedIn
, 0, sizeof(int) * (n
- r
->m_channelsAllocatedIn
));
3626 memset(r
->m_vecChannelsIn
+ r
->m_channelsAllocatedIn
, 0, sizeof(RTMPPacket
*) * (n
- r
->m_channelsAllocatedIn
));
3627 r
->m_channelsAllocatedIn
= n
;
3630 if (nSize
== RTMP_LARGE_HEADER_SIZE
) /* if we get a full header the timestamp is absolute */
3631 packet
->m_hasAbsTimestamp
= TRUE
;
3633 else if (nSize
< RTMP_LARGE_HEADER_SIZE
)
3634 { /* using values from the last message of this channel */
3635 if (r
->m_vecChannelsIn
[packet
->m_nChannel
])
3636 memcpy(packet
, r
->m_vecChannelsIn
[packet
->m_nChannel
],
3637 sizeof(RTMPPacket
));
3642 if (nSize
> 0 && ReadN(r
, header
, nSize
) != nSize
)
3644 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header. type: %x",
3645 __FUNCTION__
, (unsigned int)hbuf
[0]);
3649 hSize
= nSize
+ (header
- (char *)hbuf
);
3653 packet
->m_nTimeStamp
= AMF_DecodeInt24(header
);
3655 /*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); */
3659 packet
->m_nBodySize
= AMF_DecodeInt24(header
+ 3);
3660 packet
->m_nBytesRead
= 0;
3661 RTMPPacket_Free(packet
);
3665 packet
->m_packetType
= header
[6];
3668 packet
->m_nInfoField2
= DecodeInt32LE(header
+ 7);
3671 if (packet
->m_nTimeStamp
== 0xffffff)
3673 if (ReadN(r
, header
+ nSize
, 4) != 4)
3675 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read extended timestamp",
3679 packet
->m_nTimeStamp
= AMF_DecodeInt32(header
+ nSize
);
3684 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)hbuf
, hSize
);
3686 if (packet
->m_nBodySize
> 0 && packet
->m_body
== NULL
)
3688 if (!RTMPPacket_Alloc(packet
, packet
->m_nBodySize
))
3690 RTMP_Log(RTMP_LOGDEBUG
, "%s, failed to allocate packet", __FUNCTION__
);
3694 packet
->m_headerType
= (hbuf
[0] & 0xc0) >> 6;
3697 nToRead
= packet
->m_nBodySize
- packet
->m_nBytesRead
;
3698 nChunk
= r
->m_inChunkSize
;
3699 if (nToRead
< nChunk
)
3702 /* Does the caller want the raw chunk? */
3703 if (packet
->m_chunk
)
3705 packet
->m_chunk
->c_headerSize
= hSize
;
3706 memcpy(packet
->m_chunk
->c_header
, hbuf
, hSize
);
3707 packet
->m_chunk
->c_chunk
= packet
->m_body
+ packet
->m_nBytesRead
;
3708 packet
->m_chunk
->c_chunkSize
= nChunk
;
3711 if (ReadN(r
, packet
->m_body
+ packet
->m_nBytesRead
, nChunk
) != nChunk
)
3713 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet body. len: %u",
3714 __FUNCTION__
, packet
->m_nBodySize
);
3718 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)packet
->m_body
+ packet
->m_nBytesRead
, nChunk
);
3720 packet
->m_nBytesRead
+= nChunk
;
3722 /* keep the packet as ref for other packets on this channel */
3723 if (!r
->m_vecChannelsIn
[packet
->m_nChannel
])
3724 r
->m_vecChannelsIn
[packet
->m_nChannel
] = malloc(sizeof(RTMPPacket
));
3725 memcpy(r
->m_vecChannelsIn
[packet
->m_nChannel
], packet
, sizeof(RTMPPacket
));
3727 if (RTMPPacket_IsReady(packet
))
3729 /* make packet's timestamp absolute */
3730 if (!packet
->m_hasAbsTimestamp
)
3731 packet
->m_nTimeStamp
+= r
->m_channelTimestamp
[packet
->m_nChannel
]; /* timestamps seem to be always relative!! */
3733 r
->m_channelTimestamp
[packet
->m_nChannel
] = packet
->m_nTimeStamp
;
3735 /* reset the data from the stored packet. we keep the header since we may use it later if a new packet for this channel */
3736 /* arrives and requests to re-use some info (small packet header) */
3737 r
->m_vecChannelsIn
[packet
->m_nChannel
]->m_body
= NULL
;
3738 r
->m_vecChannelsIn
[packet
->m_nChannel
]->m_nBytesRead
= 0;
3739 r
->m_vecChannelsIn
[packet
->m_nChannel
]->m_hasAbsTimestamp
= FALSE
; /* can only be false if we reuse header */
3743 packet
->m_body
= NULL
; /* so it won't be erased on free */
3751 HandShake(RTMP
*r
, int FP9HandShake
)
3754 uint32_t uptime
, suptime
;
3757 char clientbuf
[RTMP_SIG_SIZE
+ 1], *clientsig
= clientbuf
+ 1;
3758 char serversig
[RTMP_SIG_SIZE
];
3760 clientbuf
[0] = 0x03; /* not encrypted */
3762 uptime
= htonl(RTMP_GetTime());
3763 memcpy(clientsig
, &uptime
, 4);
3765 memset(&clientsig
[4], 0, 4);
3768 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3769 clientsig
[i
] = 0xff;
3771 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3772 clientsig
[i
] = (char)(rand() % 256);
3775 if (!WriteN(r
, clientbuf
, RTMP_SIG_SIZE
+ 1))
3778 if (ReadN(r
, &type
, 1) != 1) /* 0x03 or 0x06 */
3781 RTMP_Log(RTMP_LOGDEBUG
, "%s: Type Answer : %02X", __FUNCTION__
, type
);
3783 if (type
!= clientbuf
[0])
3784 RTMP_Log(RTMP_LOGWARNING
, "%s: Type mismatch: client sent %d, server answered %d",
3785 __FUNCTION__
, clientbuf
[0], type
);
3787 if (ReadN(r
, serversig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3790 /* decode server response */
3792 memcpy(&suptime
, serversig
, 4);
3793 suptime
= ntohl(suptime
);
3795 RTMP_Log(RTMP_LOGDEBUG
, "%s: Server Uptime : %d", __FUNCTION__
, suptime
);
3796 RTMP_Log(RTMP_LOGDEBUG
, "%s: FMS Version : %d.%d.%d.%d", __FUNCTION__
,
3797 serversig
[4], serversig
[5], serversig
[6], serversig
[7]);
3799 /* 2nd part of handshake */
3800 if (!WriteN(r
, serversig
, RTMP_SIG_SIZE
))
3803 if (ReadN(r
, serversig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3806 bMatch
= (memcmp(serversig
, clientsig
, RTMP_SIG_SIZE
) == 0);
3809 RTMP_Log(RTMP_LOGWARNING
, "%s, client signature does not match!", __FUNCTION__
);
3818 char serverbuf
[RTMP_SIG_SIZE
+ 1], *serversig
= serverbuf
+ 1;
3819 char clientsig
[RTMP_SIG_SIZE
];
3823 if (ReadN(r
, serverbuf
, 1) != 1) /* 0x03 or 0x06 */
3826 RTMP_Log(RTMP_LOGDEBUG
, "%s: Type Request : %02X", __FUNCTION__
, serverbuf
[0]);
3828 if (serverbuf
[0] != 3)
3830 RTMP_Log(RTMP_LOGERROR
, "%s: Type unknown: client sent %02X",
3831 __FUNCTION__
, serverbuf
[0]);
3835 uptime
= htonl(RTMP_GetTime());
3836 memcpy(serversig
, &uptime
, 4);
3838 memset(&serversig
[4], 0, 4);
3840 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3841 serversig
[i
] = 0xff;
3843 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3844 serversig
[i
] = (char)(rand() % 256);
3847 if (!WriteN(r
, serverbuf
, RTMP_SIG_SIZE
+ 1))
3850 if (ReadN(r
, clientsig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3853 /* decode client response */
3855 memcpy(&uptime
, clientsig
, 4);
3856 uptime
= ntohl(uptime
);
3858 RTMP_Log(RTMP_LOGDEBUG
, "%s: Client Uptime : %d", __FUNCTION__
, uptime
);
3859 RTMP_Log(RTMP_LOGDEBUG
, "%s: Player Version: %d.%d.%d.%d", __FUNCTION__
,
3860 clientsig
[4], clientsig
[5], clientsig
[6], clientsig
[7]);
3862 /* 2nd part of handshake */
3863 if (!WriteN(r
, clientsig
, RTMP_SIG_SIZE
))
3866 if (ReadN(r
, clientsig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3869 bMatch
= (memcmp(serversig
, clientsig
, RTMP_SIG_SIZE
) == 0);
3872 RTMP_Log(RTMP_LOGWARNING
, "%s, client signature does not match!", __FUNCTION__
);
3879 RTMP_SendChunk(RTMP
*r
, RTMPChunk
*chunk
)
3882 char hbuf
[RTMP_MAX_HEADER_SIZE
];
3884 RTMP_Log(RTMP_LOGDEBUG2
, "%s: fd=%d, size=%d", __FUNCTION__
, r
->m_sb
.sb_socket
,
3885 chunk
->c_chunkSize
);
3886 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)chunk
->c_header
, chunk
->c_headerSize
);
3887 if (chunk
->c_chunkSize
)
3889 char *ptr
= chunk
->c_chunk
- chunk
->c_headerSize
;
3890 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)chunk
->c_chunk
, chunk
->c_chunkSize
);
3891 /* save header bytes we're about to overwrite */
3892 memcpy(hbuf
, ptr
, chunk
->c_headerSize
);
3893 memcpy(ptr
, chunk
->c_header
, chunk
->c_headerSize
);
3894 wrote
= WriteN(r
, ptr
, chunk
->c_headerSize
+ chunk
->c_chunkSize
);
3895 memcpy(ptr
, hbuf
, chunk
->c_headerSize
);
3898 wrote
= WriteN(r
, chunk
->c_header
, chunk
->c_headerSize
);
3903 RTMP_SendPacket(RTMP
*r
, RTMPPacket
*packet
, int queue
)
3905 const RTMPPacket
*prevPacket
;
3909 char *header
, *hptr
, *hend
, hbuf
[RTMP_MAX_HEADER_SIZE
], c
;
3911 char *buffer
, *tbuf
= NULL
, *toff
= NULL
;
3915 if (packet
->m_nChannel
>= r
->m_channelsAllocatedOut
)
3917 int n
= packet
->m_nChannel
+ 10;
3918 RTMPPacket
**packets
= realloc(r
->m_vecChannelsOut
, sizeof(RTMPPacket
*) * n
);
3920 free(r
->m_vecChannelsOut
);
3921 r
->m_vecChannelsOut
= NULL
;
3922 r
->m_channelsAllocatedOut
= 0;
3925 r
->m_vecChannelsOut
= packets
;
3926 memset(r
->m_vecChannelsOut
+ r
->m_channelsAllocatedOut
, 0, sizeof(RTMPPacket
*) * (n
- r
->m_channelsAllocatedOut
));
3927 r
->m_channelsAllocatedOut
= n
;
3930 prevPacket
= r
->m_vecChannelsOut
[packet
->m_nChannel
];
3931 if (prevPacket
&& packet
->m_headerType
!= RTMP_PACKET_SIZE_LARGE
)
3933 /* compress a bit by using the prev packet's attributes */
3934 if (prevPacket
->m_nBodySize
== packet
->m_nBodySize
3935 && prevPacket
->m_packetType
== packet
->m_packetType
3936 && packet
->m_headerType
== RTMP_PACKET_SIZE_MEDIUM
)
3937 packet
->m_headerType
= RTMP_PACKET_SIZE_SMALL
;
3939 if (prevPacket
->m_nTimeStamp
== packet
->m_nTimeStamp
3940 && packet
->m_headerType
== RTMP_PACKET_SIZE_SMALL
)
3941 packet
->m_headerType
= RTMP_PACKET_SIZE_MINIMUM
;
3942 last
= prevPacket
->m_nTimeStamp
;
3945 if (packet
->m_headerType
> 3) /* sanity */
3947 RTMP_Log(RTMP_LOGERROR
, "sanity failed!! trying to send header of type: 0x%02x.",
3948 (unsigned char)packet
->m_headerType
);
3952 nSize
= packetSize
[packet
->m_headerType
];
3953 hSize
= nSize
; cSize
= 0;
3954 t
= packet
->m_nTimeStamp
- last
;
3958 header
= packet
->m_body
- nSize
;
3959 hend
= packet
->m_body
;
3964 hend
= hbuf
+ sizeof(hbuf
);
3967 if (packet
->m_nChannel
> 319)
3969 else if (packet
->m_nChannel
> 63)
3977 if (nSize
> 1 && t
>= 0xffffff)
3984 c
= packet
->m_headerType
<< 6;
3988 c
|= packet
->m_nChannel
;
3999 int tmp
= packet
->m_nChannel
- 64;
4000 *hptr
++ = tmp
& 0xff;
4007 hptr
= AMF_EncodeInt24(hptr
, hend
, t
> 0xffffff ? 0xffffff : t
);
4012 hptr
= AMF_EncodeInt24(hptr
, hend
, packet
->m_nBodySize
);
4013 *hptr
++ = packet
->m_packetType
;
4017 hptr
+= EncodeInt32LE(hptr
, packet
->m_nInfoField2
);
4019 if (nSize
> 1 && t
>= 0xffffff)
4020 hptr
= AMF_EncodeInt32(hptr
, hend
, t
);
4022 nSize
= packet
->m_nBodySize
;
4023 buffer
= packet
->m_body
;
4024 nChunkSize
= r
->m_outChunkSize
;
4026 RTMP_Log(RTMP_LOGDEBUG2
, "%s: fd=%d, size=%d", __FUNCTION__
, r
->m_sb
.sb_socket
,
4028 /* send all chunks in one HTTP request */
4029 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
4031 int chunks
= (nSize
+nChunkSize
-1) / nChunkSize
;
4034 tlen
= chunks
* (cSize
+ 1) + nSize
+ hSize
;
4035 tbuf
= malloc(tlen
);
4041 while (nSize
+ hSize
)
4045 if (nSize
< nChunkSize
)
4048 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)header
, hSize
);
4049 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)buffer
, nChunkSize
);
4052 memcpy(toff
, header
, nChunkSize
+ hSize
);
4053 toff
+= nChunkSize
+ hSize
;
4057 wrote
= WriteN(r
, header
, nChunkSize
+ hSize
);
4061 nSize
-= nChunkSize
;
4062 buffer
+= nChunkSize
;
4067 header
= buffer
- 1;
4074 *header
= (0xc0 | c
);
4077 int tmp
= packet
->m_nChannel
- 64;
4078 header
[1] = tmp
& 0xff;
4080 header
[2] = tmp
>> 8;
4086 int wrote
= WriteN(r
, tbuf
, toff
-tbuf
);
4093 /* we invoked a remote method */
4094 if (packet
->m_packetType
== RTMP_PACKET_TYPE_INVOKE
)
4098 ptr
= packet
->m_body
+ 1;
4099 AMF_DecodeString(ptr
, &method
);
4100 RTMP_Log(RTMP_LOGDEBUG
, "Invoking %s", method
.av_val
);
4101 /* keep it in call queue till result arrives */
4104 ptr
+= 3 + method
.av_len
;
4105 txn
= (int)AMF_DecodeNumber(ptr
);
4106 AV_queue(&r
->m_methodCalls
, &r
->m_numCalls
, &method
, txn
);
4110 if (!r
->m_vecChannelsOut
[packet
->m_nChannel
])
4111 r
->m_vecChannelsOut
[packet
->m_nChannel
] = malloc(sizeof(RTMPPacket
));
4112 memcpy(r
->m_vecChannelsOut
[packet
->m_nChannel
], packet
, sizeof(RTMPPacket
));
4119 return SHandShake(r
);
4127 if (RTMP_IsConnected(r
))
4129 if (r
->m_stream_id
> 0)
4133 if ((r
->Link
.protocol
& RTMP_FEATURE_WRITE
))
4135 SendDeleteStream(r
, i
);
4137 if (r
->m_clientID
.av_val
)
4139 HTTP_Post(r
, RTMPT_CLOSE
, "", 1);
4140 free(r
->m_clientID
.av_val
);
4141 r
->m_clientID
.av_val
= NULL
;
4142 r
->m_clientID
.av_len
= 0;
4144 RTMPSockBuf_Close(&r
->m_sb
);
4147 r
->m_stream_id
= -1;
4148 r
->m_sb
.sb_socket
= -1;
4149 r
->m_nBWCheckCounter
= 0;
4151 r
->m_nBytesInSent
= 0;
4153 if (r
->m_read
.flags
& RTMP_READ_HEADER
) {
4154 free(r
->m_read
.buf
);
4155 r
->m_read
.buf
= NULL
;
4157 r
->m_read
.dataType
= 0;
4158 r
->m_read
.flags
= 0;
4159 r
->m_read
.status
= 0;
4160 r
->m_read
.nResumeTS
= 0;
4161 r
->m_read
.nIgnoredFrameCounter
= 0;
4162 r
->m_read
.nIgnoredFlvFrameCounter
= 0;
4164 r
->m_write
.m_nBytesRead
= 0;
4165 RTMPPacket_Free(&r
->m_write
);
4167 for (i
= 0; i
< r
->m_channelsAllocatedIn
; i
++)
4169 if (r
->m_vecChannelsIn
[i
])
4171 RTMPPacket_Free(r
->m_vecChannelsIn
[i
]);
4172 free(r
->m_vecChannelsIn
[i
]);
4173 r
->m_vecChannelsIn
[i
] = NULL
;
4176 free(r
->m_vecChannelsIn
);
4177 r
->m_vecChannelsIn
= NULL
;
4178 free(r
->m_channelTimestamp
);
4179 r
->m_channelTimestamp
= NULL
;
4180 r
->m_channelsAllocatedIn
= 0;
4181 for (i
= 0; i
< r
->m_channelsAllocatedOut
; i
++)
4183 if (r
->m_vecChannelsOut
[i
])
4185 free(r
->m_vecChannelsOut
[i
]);
4186 r
->m_vecChannelsOut
[i
] = NULL
;
4189 free(r
->m_vecChannelsOut
);
4190 r
->m_vecChannelsOut
= NULL
;
4191 r
->m_channelsAllocatedOut
= 0;
4192 AV_clear(r
->m_methodCalls
, r
->m_numCalls
);
4193 r
->m_methodCalls
= NULL
;
4195 r
->m_numInvokes
= 0;
4197 r
->m_bPlaying
= FALSE
;
4198 r
->m_sb
.sb_size
= 0;
4200 r
->m_msgCounter
= 0;
4204 if (r
->Link
.lFlags
& RTMP_LF_FTCU
)
4206 free(r
->Link
.tcUrl
.av_val
);
4207 r
->Link
.tcUrl
.av_val
= NULL
;
4208 r
->Link
.lFlags
^= RTMP_LF_FTCU
;
4212 if (!(r
->Link
.protocol
& RTMP_FEATURE_WRITE
) || (r
->Link
.pFlags
& RTMP_PUB_CLEAN
))
4214 free(r
->Link
.playpath0
.av_val
);
4215 r
->Link
.playpath0
.av_val
= NULL
;
4217 if ((r
->Link
.protocol
& RTMP_FEATURE_WRITE
) &&
4218 (r
->Link
.pFlags
& RTMP_PUB_CLEAN
) &&
4219 (r
->Link
.pFlags
& RTMP_PUB_ALLOC
))
4221 free(r
->Link
.app
.av_val
);
4222 r
->Link
.app
.av_val
= NULL
;
4223 free(r
->Link
.tcUrl
.av_val
);
4224 r
->Link
.tcUrl
.av_val
= NULL
;
4228 MDH_free(r
->Link
.dh
);
4231 if (r
->Link
.rc4keyIn
)
4233 RC4_free(r
->Link
.rc4keyIn
);
4234 r
->Link
.rc4keyIn
= NULL
;
4236 if (r
->Link
.rc4keyOut
)
4238 RC4_free(r
->Link
.rc4keyOut
);
4239 r
->Link
.rc4keyOut
= NULL
;
4242 free(r
->Link
.playpath0
.av_val
);
4243 r
->Link
.playpath0
.av_val
= NULL
;
4248 RTMPSockBuf_Fill(RTMPSockBuf
*sb
)
4253 sb
->sb_start
= sb
->sb_buf
;
4257 nBytes
= sizeof(sb
->sb_buf
) - 1 - sb
->sb_size
- (sb
->sb_start
- sb
->sb_buf
);
4258 #if defined(CRYPTO) && !defined(NO_SSL)
4261 nBytes
= TLS_read(sb
->sb_ssl
, sb
->sb_start
+ sb
->sb_size
, nBytes
);
4266 nBytes
= recv(sb
->sb_socket
, sb
->sb_start
+ sb
->sb_size
, nBytes
, 0);
4270 sb
->sb_size
+= nBytes
;
4274 int sockerr
= GetSockError();
4275 RTMP_Log(RTMP_LOGDEBUG
, "%s, recv returned %d. GetSockError(): %d (%s)",
4276 __FUNCTION__
, nBytes
, sockerr
, strerror(sockerr
));
4277 if (sockerr
== EINTR
&& !RTMP_ctrlC
)
4280 if (sockerr
== EWOULDBLOCK
|| sockerr
== EAGAIN
)
4282 sb
->sb_timedout
= TRUE
;
4293 RTMPSockBuf_Send(RTMPSockBuf
*sb
, const char *buf
, int len
)
4298 fwrite(buf
, 1, len
, netstackdump
);
4301 #if defined(CRYPTO) && !defined(NO_SSL)
4304 rc
= TLS_write(sb
->sb_ssl
, buf
, len
);
4309 rc
= send(sb
->sb_socket
, buf
, len
, 0);
4315 RTMPSockBuf_Close(RTMPSockBuf
*sb
)
4317 #if defined(CRYPTO) && !defined(NO_SSL)
4320 TLS_shutdown(sb
->sb_ssl
);
4321 TLS_close(sb
->sb_ssl
);
4325 if (sb
->sb_socket
!= -1)
4326 return closesocket(sb
->sb_socket
);
4330 #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
4333 DecodeTEA(AVal
*key
, AVal
*text
)
4335 uint32_t *v
, k
[4] = { 0 }, u
;
4336 uint32_t z
, y
, sum
= 0, e
, DELTA
= 0x9e3779b9;
4339 unsigned char *ptr
, *out
;
4341 /* prep key: pack 1st 16 chars into 4 LittleEndian ints */
4342 ptr
= (unsigned char *)key
->av_val
;
4346 p
= key
->av_len
> 16 ? 16 : key
->av_len
;
4347 for (i
= 0; i
< p
; i
++)
4349 u
|= ptr
[i
] << (n
* 8);
4361 /* any trailing chars */
4365 /* prep text: hex2bin, multiples of 4 */
4366 n
= (text
->av_len
+ 7) / 8;
4367 out
= malloc(n
* 8);
4368 ptr
= (unsigned char *)text
->av_val
;
4369 v
= (uint32_t *) out
;
4370 for (i
= 0; i
< n
; i
++)
4372 u
= (HEX2BIN(ptr
[0]) << 4) + HEX2BIN(ptr
[1]);
4373 u
|= ((HEX2BIN(ptr
[2]) << 4) + HEX2BIN(ptr
[3])) << 8;
4374 u
|= ((HEX2BIN(ptr
[4]) << 4) + HEX2BIN(ptr
[5])) << 16;
4375 u
|= ((HEX2BIN(ptr
[6]) << 4) + HEX2BIN(ptr
[7])) << 24;
4379 v
= (uint32_t *) out
;
4381 /* http://www.movable-type.co.uk/scripts/tea-block.html */
4382 #define MX (((z>>5)^(y<<2)) + ((y>>3)^(z<<4))) ^ ((sum^y) + (k[(p&3)^e]^z));
4390 for (p
= n
- 1; p
> 0; p
--)
4391 z
= v
[p
- 1], y
= v
[p
] -= MX
;
4398 memcpy(text
->av_val
, out
, text
->av_len
);
4403 HTTP_Post(RTMP
*r
, RTMPTCmd cmd
, const char *buf
, int len
)
4406 int hlen
= snprintf(hbuf
, sizeof(hbuf
), "POST /%s%s/%d HTTP/1.1\r\n"
4409 "User-Agent: Shockwave Flash\r\n"
4410 "Connection: Keep-Alive\r\n"
4411 "Cache-Control: no-cache\r\n"
4412 "Content-type: application/x-fcs\r\n"
4413 "Content-length: %d\r\n\r\n", RTMPT_cmds
[cmd
],
4414 r
->m_clientID
.av_val
? r
->m_clientID
.av_val
: "",
4415 r
->m_msgCounter
, r
->Link
.hostname
.av_len
, r
->Link
.hostname
.av_val
,
4417 RTMPSockBuf_Send(&r
->m_sb
, hbuf
, hlen
);
4418 hlen
= RTMPSockBuf_Send(&r
->m_sb
, buf
, len
);
4425 HTTP_read(RTMP
*r
, int fill
)
4432 RTMPSockBuf_Fill(&r
->m_sb
);
4433 if (r
->m_sb
.sb_size
< 13) {
4438 if (strncmp(r
->m_sb
.sb_start
, "HTTP/1.1 200 ", 13))
4440 r
->m_sb
.sb_start
[r
->m_sb
.sb_size
] = '\0';
4441 if (!strstr(r
->m_sb
.sb_start
, "\r\n\r\n")) {
4447 ptr
= r
->m_sb
.sb_start
+ sizeof("HTTP/1.1 200");
4448 while ((ptr
= strstr(ptr
, "Content-"))) {
4449 if (!strncasecmp(ptr
+8, "length:", 7)) break;
4454 hlen
= atoi(ptr
+16);
4455 ptr
= strstr(ptr
+16, "\r\n\r\n");
4459 if (ptr
+ (r
->m_clientID
.av_val
? 1 : hlen
) > r
->m_sb
.sb_start
+ r
->m_sb
.sb_size
)
4465 r
->m_sb
.sb_size
-= ptr
- r
->m_sb
.sb_start
;
4466 r
->m_sb
.sb_start
= ptr
;
4469 if (!r
->m_clientID
.av_val
)
4471 r
->m_clientID
.av_len
= hlen
;
4472 r
->m_clientID
.av_val
= malloc(hlen
+1);
4473 if (!r
->m_clientID
.av_val
)
4475 r
->m_clientID
.av_val
[0] = '/';
4476 memcpy(r
->m_clientID
.av_val
+1, ptr
, hlen
-1);
4477 r
->m_clientID
.av_val
[hlen
] = 0;
4478 r
->m_sb
.sb_size
= 0;
4482 r
->m_polling
= *ptr
++;
4483 r
->m_resplen
= hlen
- 1;
4490 #define MAX_IGNORED_FRAMES 50
4492 /* Read from the stream until we get a media packet.
4493 * Returns -3 if Play.Close/Stop, -2 if fatal error, -1 if no more media
4494 * packets, 0 if ignorable error, >0 if there is a media packet
4497 Read_1_Packet(RTMP
*r
, char *buf
, unsigned int buflen
)
4499 uint32_t prevTagSize
= 0;
4500 int rtnGetNextMediaPacket
= 0, ret
= RTMP_READ_EOF
;
4501 RTMPPacket packet
= { 0 };
4505 uint32_t nTimeStamp
= 0;
4508 rtnGetNextMediaPacket
= RTMP_GetNextMediaPacket(r
, &packet
);
4509 while (rtnGetNextMediaPacket
)
4511 char *packetBody
= packet
.m_body
;
4512 unsigned int nPacketLen
= packet
.m_nBodySize
;
4514 /* Return RTMP_READ_COMPLETE if this was completed nicely with
4515 * invoke message Play.Stop or Play.Complete
4517 if (rtnGetNextMediaPacket
== 2)
4519 RTMP_Log(RTMP_LOGDEBUG
,
4520 "Got Play.Complete or Play.Stop from server. "
4521 "Assuming stream is complete");
4522 ret
= RTMP_READ_COMPLETE
;
4526 r
->m_read
.dataType
|= (((packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
) << 2) |
4527 (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
));
4529 if (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
&& nPacketLen
<= 5)
4531 RTMP_Log(RTMP_LOGDEBUG
, "ignoring too small video packet: size: %d",
4533 ret
= RTMP_READ_IGNORE
;
4536 if (packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
&& nPacketLen
<= 1)
4538 RTMP_Log(RTMP_LOGDEBUG
, "ignoring too small audio packet: size: %d",
4540 ret
= RTMP_READ_IGNORE
;
4544 if (r
->m_read
.flags
& RTMP_READ_SEEKING
)
4546 ret
= RTMP_READ_IGNORE
;
4550 RTMP_Log(RTMP_LOGDEBUG
, "type: %02X, size: %d, TS: %d ms, abs TS: %d",
4551 packet
.m_packetType
, nPacketLen
, packet
.m_nTimeStamp
,
4552 packet
.m_hasAbsTimestamp
);
4553 if (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
)
4554 RTMP_Log(RTMP_LOGDEBUG
, "frametype: %02X", (*packetBody
& 0xf0));
4557 if (r
->m_read
.flags
& RTMP_READ_RESUME
)
4559 /* check the header if we get one */
4560 if (packet
.m_nTimeStamp
== 0)
4562 if (r
->m_read
.nMetaHeaderSize
> 0
4563 && packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
)
4567 AMF_Decode(&metaObj
, packetBody
, nPacketLen
, FALSE
);
4571 AMFProp_GetString(AMF_GetProp(&metaObj
, NULL
, 0),
4574 if (AVMATCH(&metastring
, &av_onMetaData
))
4577 if ((r
->m_read
.nMetaHeaderSize
!= nPacketLen
) ||
4579 (r
->m_read
.metaHeader
, packetBody
,
4580 r
->m_read
.nMetaHeaderSize
) != 0))
4582 ret
= RTMP_READ_ERROR
;
4585 AMF_Reset(&metaObj
);
4586 if (ret
== RTMP_READ_ERROR
)
4591 /* check first keyframe to make sure we got the right position
4592 * in the stream! (the first non ignored frame)
4594 if (r
->m_read
.nInitialFrameSize
> 0)
4596 /* video or audio data */
4597 if (packet
.m_packetType
== r
->m_read
.initialFrameType
4598 && r
->m_read
.nInitialFrameSize
== nPacketLen
)
4600 /* we don't compare the sizes since the packet can
4601 * contain several FLV packets, just make sure the
4602 * first frame is our keyframe (which we are going
4606 (r
->m_read
.initialFrame
, packetBody
,
4607 r
->m_read
.nInitialFrameSize
) == 0)
4609 RTMP_Log(RTMP_LOGDEBUG
, "Checked keyframe successfully!");
4610 r
->m_read
.flags
|= RTMP_READ_GOTKF
;
4611 /* ignore it! (what about audio data after it? it is
4612 * handled by ignoring all 0ms frames, see below)
4614 ret
= RTMP_READ_IGNORE
;
4619 /* hande FLV streams, even though the server resends the
4620 * keyframe as an extra video packet it is also included
4621 * in the first FLV stream chunk and we have to compare
4622 * it and filter it out !!
4624 if (packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
4626 /* basically we have to find the keyframe with the
4627 * correct TS being nResumeTS
4629 unsigned int pos
= 0;
4632 while (pos
+ 11 < nPacketLen
)
4634 /* size without header (11) and prevTagSize (4) */
4636 AMF_DecodeInt24(packetBody
+ pos
+ 1);
4637 ts
= AMF_DecodeInt24(packetBody
+ pos
+ 4);
4638 ts
|= (packetBody
[pos
+ 7] << 24);
4641 RTMP_Log(RTMP_LOGDEBUG
,
4642 "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms",
4643 packetBody
[pos
], dataSize
, ts
);
4645 /* ok, is it a keyframe?:
4646 * well doesn't work for audio!
4648 if (packetBody
[pos
/*6928, test 0 */ ] ==
4649 r
->m_read
.initialFrameType
4650 /* && (packetBody[11]&0xf0) == 0x10 */ )
4652 if (ts
== r
->m_read
.nResumeTS
)
4654 RTMP_Log(RTMP_LOGDEBUG
,
4655 "Found keyframe with resume-keyframe timestamp!");
4656 if (r
->m_read
.nInitialFrameSize
!= dataSize
4657 || memcmp(r
->m_read
.initialFrame
,
4658 packetBody
+ pos
+ 11,
4660 nInitialFrameSize
) != 0)
4662 RTMP_Log(RTMP_LOGERROR
,
4663 "FLV Stream: Keyframe doesn't match!");
4664 ret
= RTMP_READ_ERROR
;
4667 r
->m_read
.flags
|= RTMP_READ_GOTFLVK
;
4669 /* skip this packet?
4670 * check whether skippable:
4672 if (pos
+ 11 + dataSize
+ 4 > nPacketLen
)
4674 RTMP_Log(RTMP_LOGWARNING
,
4675 "Non skipable packet since it doesn't end with chunk, stream corrupt!");
4676 ret
= RTMP_READ_ERROR
;
4679 packetBody
+= (pos
+ 11 + dataSize
+ 4);
4680 nPacketLen
-= (pos
+ 11 + dataSize
+ 4);
4682 goto stopKeyframeSearch
;
4685 else if (r
->m_read
.nResumeTS
< ts
)
4687 /* the timestamp ts will only increase with
4688 * further packets, wait for seek
4690 goto stopKeyframeSearch
;
4693 pos
+= (11 + dataSize
+ 4);
4695 if (ts
< r
->m_read
.nResumeTS
)
4697 RTMP_Log(RTMP_LOGERROR
,
4698 "First packet does not contain keyframe, all "
4699 "timestamps are smaller than the keyframe "
4700 "timestamp; probably the resume seek failed?");
4704 if (!(r
->m_read
.flags
& RTMP_READ_GOTFLVK
))
4706 RTMP_Log(RTMP_LOGERROR
,
4707 "Couldn't find the seeked keyframe in this chunk!");
4708 ret
= RTMP_READ_IGNORE
;
4715 if (packet
.m_nTimeStamp
> 0
4716 && (r
->m_read
.flags
& (RTMP_READ_GOTKF
|RTMP_READ_GOTFLVK
)))
4718 /* another problem is that the server can actually change from
4719 * 09/08 video/audio packets to an FLV stream or vice versa and
4720 * our keyframe check will prevent us from going along with the
4721 * new stream if we resumed.
4723 * in this case set the 'found keyframe' variables to true.
4724 * We assume that if we found one keyframe somewhere and were
4725 * already beyond TS > 0 we have written data to the output
4726 * which means we can accept all forthcoming data including the
4727 * change between 08/09 <-> FLV packets
4729 r
->m_read
.flags
|= (RTMP_READ_GOTKF
|RTMP_READ_GOTFLVK
);
4732 /* skip till we find our keyframe
4733 * (seeking might put us somewhere before it)
4735 if (!(r
->m_read
.flags
& RTMP_READ_GOTKF
) &&
4736 packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4738 RTMP_Log(RTMP_LOGWARNING
,
4739 "Stream does not start with requested frame, ignoring data... ");
4740 r
->m_read
.nIgnoredFrameCounter
++;
4741 if (r
->m_read
.nIgnoredFrameCounter
> MAX_IGNORED_FRAMES
)
4742 ret
= RTMP_READ_ERROR
; /* fatal error, couldn't continue stream */
4744 ret
= RTMP_READ_IGNORE
;
4747 /* ok, do the same for FLV streams */
4748 if (!(r
->m_read
.flags
& RTMP_READ_GOTFLVK
) &&
4749 packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
4751 RTMP_Log(RTMP_LOGWARNING
,
4752 "Stream does not start with requested FLV frame, ignoring data... ");
4753 r
->m_read
.nIgnoredFlvFrameCounter
++;
4754 if (r
->m_read
.nIgnoredFlvFrameCounter
> MAX_IGNORED_FRAMES
)
4755 ret
= RTMP_READ_ERROR
;
4757 ret
= RTMP_READ_IGNORE
;
4761 /* we have to ignore the 0ms frames since these are the first
4762 * keyframes; we've got these so don't mess around with multiple
4763 * copies sent by the server to us! (if the keyframe is found at a
4764 * later position there is only one copy and it will be ignored by
4765 * the preceding if clause)
4767 if (!(r
->m_read
.flags
& RTMP_READ_NO_IGNORE
) &&
4768 packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4770 /* exclude type RTMP_PACKET_TYPE_FLASH_VIDEO since it can
4771 * contain several FLV packets
4773 if (packet
.m_nTimeStamp
== 0)
4775 ret
= RTMP_READ_IGNORE
;
4780 /* stop ignoring packets */
4781 r
->m_read
.flags
|= RTMP_READ_NO_IGNORE
;
4786 /* calculate packet size and allocate slop buffer if necessary */
4788 ((packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
4789 || packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
4790 || packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
) ? 11 : 0) +
4791 (packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
? 4 : 0);
4793 if (size
+ 4 > buflen
)
4795 /* the extra 4 is for the case of an FLV stream without a last
4796 * prevTagSize (we need extra 4 bytes to append it) */
4797 r
->m_read
.buf
= malloc(size
+ 4);
4798 if (r
->m_read
.buf
== 0)
4800 RTMP_Log(RTMP_LOGERROR
, "Couldn't allocate memory!");
4801 ret
= RTMP_READ_ERROR
; /* fatal error */
4805 ptr
= r
->m_read
.buf
;
4811 pend
= ptr
+ size
+ 4;
4813 /* use to return timestamp of last processed packet */
4815 /* audio (0x08), video (0x09) or metadata (0x12) packets :
4816 * construct 11 byte header then add rtmp packet's data */
4817 if (packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
4818 || packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
4819 || packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
)
4821 nTimeStamp
= r
->m_read
.nResumeTS
+ packet
.m_nTimeStamp
;
4822 prevTagSize
= 11 + nPacketLen
;
4824 *ptr
= packet
.m_packetType
;
4826 ptr
= AMF_EncodeInt24(ptr
, pend
, nPacketLen
);
4829 if(packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
) {
4832 if((packetBody
[0] & 0x0f) == 7) { /* CodecId = H264 */
4833 uint8_t packetType
= *(packetBody
+1);
4835 uint32_t ts
= AMF_DecodeInt24(packetBody
+2); /* composition time */
4836 int32_t cts
= (ts
+0xff800000)^0xff800000;
4837 RTMP_Log(RTMP_LOGDEBUG
, "cts : %d\n", cts
);
4840 /* get rid of the composition time */
4841 CRTMP::EncodeInt24(packetBody
+2, 0);
4843 RTMP_Log(RTMP_LOGDEBUG
, "VIDEO: nTimeStamp: 0x%08X (%d)\n", nTimeStamp
, nTimeStamp
);
4847 ptr
= AMF_EncodeInt24(ptr
, pend
, nTimeStamp
);
4848 *ptr
= (char)((nTimeStamp
& 0xFF000000) >> 24);
4852 ptr
= AMF_EncodeInt24(ptr
, pend
, 0);
4855 memcpy(ptr
, packetBody
, nPacketLen
);
4858 /* correct tagSize and obtain timestamp if we have an FLV stream */
4859 if (packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
4861 unsigned int pos
= 0;
4864 /* grab first timestamp and see if it needs fixing */
4865 nTimeStamp
= AMF_DecodeInt24(packetBody
+ 4);
4866 nTimeStamp
|= (packetBody
[7] << 24);
4867 delta
= packet
.m_nTimeStamp
- nTimeStamp
+ r
->m_read
.nResumeTS
;
4869 while (pos
+ 11 < nPacketLen
)
4871 /* size without header (11) and without prevTagSize (4) */
4872 uint32_t dataSize
= AMF_DecodeInt24(packetBody
+ pos
+ 1);
4873 nTimeStamp
= AMF_DecodeInt24(packetBody
+ pos
+ 4);
4874 nTimeStamp
|= (packetBody
[pos
+ 7] << 24);
4878 nTimeStamp
+= delta
;
4879 AMF_EncodeInt24(ptr
+pos
+4, pend
, nTimeStamp
);
4880 ptr
[pos
+7] = nTimeStamp
>>24;
4884 r
->m_read
.dataType
|= (((*(packetBody
+ pos
) == 0x08) << 2) |
4885 (*(packetBody
+ pos
) == 0x09));
4887 if (pos
+ 11 + dataSize
+ 4 > nPacketLen
)
4889 if (pos
+ 11 + dataSize
> nPacketLen
)
4891 RTMP_Log(RTMP_LOGERROR
,
4892 "Wrong data size (%u), stream corrupted, aborting!",
4894 ret
= RTMP_READ_ERROR
;
4897 RTMP_Log(RTMP_LOGWARNING
, "No tagSize found, appending!");
4899 /* we have to append a last tagSize! */
4900 prevTagSize
= dataSize
+ 11;
4901 AMF_EncodeInt32(ptr
+ pos
+ 11 + dataSize
, pend
,
4909 AMF_DecodeInt32(packetBody
+ pos
+ 11 + dataSize
);
4912 RTMP_Log(RTMP_LOGDEBUG
,
4913 "FLV Packet: type %02X, dataSize: %lu, tagSize: %lu, timeStamp: %lu ms",
4914 (unsigned char)packetBody
[pos
], dataSize
, prevTagSize
,
4918 if (prevTagSize
!= (dataSize
+ 11))
4921 RTMP_Log(RTMP_LOGWARNING
,
4922 "Tag and data size are not consitent, writing tag size according to dataSize+11: %d",
4926 prevTagSize
= dataSize
+ 11;
4927 AMF_EncodeInt32(ptr
+ pos
+ 11 + dataSize
, pend
,
4932 pos
+= prevTagSize
+ 4; /*(11+dataSize+4); */
4937 if (packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4939 /* FLV tag packets contain their own prevTagSize */
4940 AMF_EncodeInt32(ptr
, pend
, prevTagSize
);
4943 /* In non-live this nTimeStamp can contain an absolute TS.
4944 * Update ext timestamp with this absolute offset in non-live mode
4945 * otherwise report the relative one
4947 /* 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); */
4948 r
->m_read
.timestamp
= (r
->Link
.lFlags
& RTMP_LF_LIVE
) ? packet
.m_nTimeStamp
: nTimeStamp
;
4954 if (rtnGetNextMediaPacket
)
4955 RTMPPacket_Free(&packet
);
4959 len
= ret
> buflen
? buflen
: ret
;
4960 memcpy(buf
, r
->m_read
.buf
, len
);
4961 r
->m_read
.bufpos
= r
->m_read
.buf
+ len
;
4962 r
->m_read
.buflen
= ret
- len
;
4967 static const char flvHeader
[] = { 'F', 'L', 'V', 0x01,
4968 0x00, /* 0x04 == audio, 0x01 == video */
4969 0x00, 0x00, 0x00, 0x09,
4970 0x00, 0x00, 0x00, 0x00
4973 #define HEADERBUF (128*1024)
4975 RTMP_Read(RTMP
*r
, char *buf
, int size
)
4977 int nRead
= 0, total
= 0;
4979 /* can't continue */
4981 switch (r
->m_read
.status
) {
4983 case RTMP_READ_COMPLETE
:
4985 case RTMP_READ_ERROR
: /* corrupted stream, resume failed */
4986 SetSockError(EINVAL
);
4992 /* first time thru */
4993 if (!(r
->m_read
.flags
& RTMP_READ_HEADER
))
4995 if (!(r
->m_read
.flags
& RTMP_READ_RESUME
))
4997 char *mybuf
= malloc(HEADERBUF
), *end
= mybuf
+ HEADERBUF
;
4999 r
->m_read
.buf
= mybuf
;
5000 r
->m_read
.buflen
= HEADERBUF
;
5002 memcpy(mybuf
, flvHeader
, sizeof(flvHeader
));
5003 r
->m_read
.buf
+= sizeof(flvHeader
);
5004 r
->m_read
.buflen
-= sizeof(flvHeader
);
5006 while (r
->m_read
.timestamp
== 0)
5008 nRead
= Read_1_Packet(r
, r
->m_read
.buf
, r
->m_read
.buflen
);
5012 r
->m_read
.buf
= NULL
;
5013 r
->m_read
.buflen
= 0;
5014 r
->m_read
.status
= nRead
;
5017 /* buffer overflow, fix buffer and give up */
5018 if (r
->m_read
.buf
< mybuf
|| r
->m_read
.buf
> end
) {
5019 mybuf
= realloc(mybuf
, cnt
+ nRead
);
5020 memcpy(mybuf
+cnt
, r
->m_read
.buf
, nRead
);
5021 r
->m_read
.buf
= mybuf
+cnt
+nRead
;
5025 r
->m_read
.buf
+= nRead
;
5026 r
->m_read
.buflen
-= nRead
;
5027 if (r
->m_read
.dataType
== 5)
5030 mybuf
[4] = r
->m_read
.dataType
;
5031 r
->m_read
.buflen
= r
->m_read
.buf
- mybuf
;
5032 r
->m_read
.buf
= mybuf
;
5033 r
->m_read
.bufpos
= mybuf
;
5035 r
->m_read
.flags
|= RTMP_READ_HEADER
;
5038 if ((r
->m_read
.flags
& RTMP_READ_SEEKING
) && r
->m_read
.buf
)
5040 /* drop whatever's here */
5041 free(r
->m_read
.buf
);
5042 r
->m_read
.buf
= NULL
;
5043 r
->m_read
.bufpos
= NULL
;
5044 r
->m_read
.buflen
= 0;
5047 /* If there's leftover data buffered, use it up */
5050 nRead
= r
->m_read
.buflen
;
5053 memcpy(buf
, r
->m_read
.bufpos
, nRead
);
5054 r
->m_read
.buflen
-= nRead
;
5055 if (!r
->m_read
.buflen
)
5057 free(r
->m_read
.buf
);
5058 r
->m_read
.buf
= NULL
;
5059 r
->m_read
.bufpos
= NULL
;
5063 r
->m_read
.bufpos
+= nRead
;
5070 while (size
> 0 && (nRead
= Read_1_Packet(r
, buf
, size
)) >= 0)
5072 if (!nRead
) continue;
5079 r
->m_read
.status
= nRead
;
5086 static const AVal av_setDataFrame
= AVC("@setDataFrame");
5089 RTMP_Write(RTMP
*r
, const char *buf
, int size
)
5091 RTMPPacket
*pkt
= &r
->m_write
;
5093 int s2
= size
, ret
, num
;
5095 pkt
->m_nChannel
= 0x04; /* source channel */
5096 pkt
->m_nInfoField2
= r
->m_stream_id
;
5100 if (!pkt
->m_nBytesRead
)
5103 /* FLV pkt too small */
5107 if (buf
[0] == 'F' && buf
[1] == 'L' && buf
[2] == 'V')
5113 pkt
->m_packetType
= *buf
++;
5114 pkt
->m_nBodySize
= AMF_DecodeInt24(buf
);
5116 pkt
->m_nTimeStamp
= AMF_DecodeInt24(buf
);
5118 pkt
->m_nTimeStamp
|= *buf
++ << 24;
5122 if (((pkt
->m_packetType
== RTMP_PACKET_TYPE_AUDIO
5123 || pkt
->m_packetType
== RTMP_PACKET_TYPE_VIDEO
) &&
5124 !pkt
->m_nTimeStamp
) || pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
5126 pkt
->m_headerType
= RTMP_PACKET_SIZE_LARGE
;
5127 if (pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
5128 pkt
->m_nBodySize
+= 16;
5132 pkt
->m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
5135 if (!RTMPPacket_Alloc(pkt
, pkt
->m_nBodySize
))
5137 RTMP_Log(RTMP_LOGDEBUG
, "%s, failed to allocate packet", __FUNCTION__
);
5141 pend
= enc
+ pkt
->m_nBodySize
;
5142 if (pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
5144 enc
= AMF_EncodeString(enc
, pend
, &av_setDataFrame
);
5145 pkt
->m_nBytesRead
= enc
- pkt
->m_body
;
5150 enc
= pkt
->m_body
+ pkt
->m_nBytesRead
;
5152 num
= pkt
->m_nBodySize
- pkt
->m_nBytesRead
;
5155 memcpy(enc
, buf
, num
);
5156 pkt
->m_nBytesRead
+= num
;
5159 if (pkt
->m_nBytesRead
== pkt
->m_nBodySize
)
5161 ret
= RTMP_SendPacket(r
, pkt
, FALSE
);
5162 RTMPPacket_Free(pkt
);
5163 pkt
->m_nBytesRead
= 0;