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/havege.h>
38 #include <polarssl/md5.h>
39 #include <polarssl/base64.h>
40 #define MD5_DIGEST_LENGTH 16
42 static const char *my_dhm_P
=
43 "E4004C1F94182000103D883A448B3F80" \
44 "2CE4B44A83301270002C20D0321CFD00" \
45 "11CCEF784C26A400F43DFB901BCA7538" \
46 "F2C6B176001CF5A0FD16D2C48B1D0C1C" \
47 "F6AC8E1DA6BCC3B4E1F96B0564965300" \
48 "FFA1D0B601EB2800F489AA512C4B248C" \
49 "01F76949A60BB7F00A40B1EAB64BDD48" \
50 "E8A700D60B7F1200FA8E77B0A979DABF";
52 static const char *my_dhm_G
= "4";
54 #elif defined(USE_GNUTLS)
55 #include <gnutls/gnutls.h>
56 #define MD5_DIGEST_LENGTH 16
57 #include <nettle/base64.h>
58 #include <nettle/md5.h>
59 #else /* USE_OPENSSL */
60 #include <openssl/ssl.h>
61 #include <openssl/rc4.h>
62 #include <openssl/md5.h>
63 #include <openssl/bio.h>
64 #include <openssl/buffer.h>
69 #define RTMP_SIG_SIZE 1536
70 #define RTMP_LARGE_HEADER_SIZE 12
72 static const int packetSize
[] = { 12, 8, 4, 1 };
76 const char RTMPProtocolStrings
[][7] = {
88 const char RTMPProtocolStringsLower
[][7] = {
100 static const char *RTMPT_cmds
[] = {
108 RTMPT_OPEN
=0, RTMPT_SEND
, RTMPT_IDLE
, RTMPT_CLOSE
111 static int DumpMetaData(AMFObject
*obj
);
112 static int HandShake(RTMP
*r
, int FP9HandShake
);
113 static int SocksNegotiate(RTMP
*r
);
115 static int SendConnectPacket(RTMP
*r
, RTMPPacket
*cp
);
116 static int SendCheckBW(RTMP
*r
);
117 static int SendCheckBWResult(RTMP
*r
, double txn
);
118 static int SendDeleteStream(RTMP
*r
, double dStreamId
);
119 static int SendFCSubscribe(RTMP
*r
, AVal
*subscribepath
);
120 static int SendPlay(RTMP
*r
);
121 static int SendBytesReceived(RTMP
*r
);
122 static int SendUsherToken(RTMP
*r
, AVal
*usherToken
);
125 static int SendBGHasStream(RTMP
*r
, double dId
, AVal
*playpath
);
128 static int HandleInvoke(RTMP
*r
, const char *body
, unsigned int nBodySize
);
129 static int HandleMetadata(RTMP
*r
, char *body
, unsigned int len
);
130 static void HandleChangeChunkSize(RTMP
*r
, const RTMPPacket
*packet
);
131 static void HandleAudio(RTMP
*r
, const RTMPPacket
*packet
);
132 static void HandleVideo(RTMP
*r
, const RTMPPacket
*packet
);
133 static void HandleCtrl(RTMP
*r
, const RTMPPacket
*packet
);
134 static void HandleServerBW(RTMP
*r
, const RTMPPacket
*packet
);
135 static void HandleClientBW(RTMP
*r
, const RTMPPacket
*packet
);
137 static int ReadN(RTMP
*r
, char *buffer
, int n
);
138 static int WriteN(RTMP
*r
, const char *buffer
, int n
);
140 static void DecodeTEA(AVal
*key
, AVal
*text
);
142 static int HTTP_Post(RTMP
*r
, RTMPTCmd cmd
, const char *buf
, int len
);
143 static int HTTP_read(RTMP
*r
, int fill
);
145 static void CloseInternal(RTMP
*r
, int reconnect
);
152 #include "handshake.h"
160 #elif defined(_WIN32)
161 return timeGetTime();
164 if (!clk_tck
) clk_tck
= sysconf(_SC_CLK_TCK
);
165 return times(&t
) * 1000 / clk_tck
;
176 RTMPPacket_Reset(RTMPPacket
*p
)
182 p
->m_nInfoField2
= 0;
183 p
->m_hasAbsTimestamp
= FALSE
;
189 RTMPPacket_Alloc(RTMPPacket
*p
, uint32_t nSize
)
192 if (nSize
> SIZE_MAX
- RTMP_MAX_HEADER_SIZE
)
194 ptr
= calloc(1, nSize
+ RTMP_MAX_HEADER_SIZE
);
197 p
->m_body
= ptr
+ RTMP_MAX_HEADER_SIZE
;
203 RTMPPacket_Free(RTMPPacket
*p
)
207 free(p
->m_body
- RTMP_MAX_HEADER_SIZE
);
213 RTMPPacket_Dump(RTMPPacket
*p
)
215 RTMP_Log(RTMP_LOGDEBUG
,
216 "RTMP PACKET: packet type: 0x%02x. channel: 0x%02x. info 1: %d info 2: %d. Body size: %u. body: 0x%02x",
217 p
->m_packetType
, p
->m_nChannel
, p
->m_nTimeStamp
, p
->m_nInfoField2
,
218 p
->m_nBodySize
, p
->m_body
? (unsigned char)p
->m_body
[0] : 0);
224 return RTMP_LIB_VERSION
;
232 /* Do this regardless of NO_SSL, we use havege for rtmpe too */
233 RTMP_TLS_ctx
= calloc(1,sizeof(struct tls_ctx
));
234 havege_init(&RTMP_TLS_ctx
->hs
);
235 #elif defined(USE_GNUTLS) && !defined(NO_SSL)
236 /* Technically we need to initialize libgcrypt ourselves if
237 * we're not going to call gnutls_global_init(). Ignoring this
240 gnutls_global_init();
241 RTMP_TLS_ctx
= malloc(sizeof(struct tls_ctx
));
242 gnutls_certificate_allocate_credentials(&RTMP_TLS_ctx
->cred
);
243 gnutls_priority_init(&RTMP_TLS_ctx
->prios
, "NORMAL", NULL
);
244 gnutls_certificate_set_x509_trust_file(RTMP_TLS_ctx
->cred
,
245 "ca.pem", GNUTLS_X509_FMT_PEM
);
246 #elif !defined(NO_SSL) /* USE_OPENSSL */
247 /* libcrypto doesn't need anything special */
248 SSL_load_error_strings();
250 OpenSSL_add_all_digests();
251 RTMP_TLS_ctx
= SSL_CTX_new(SSLv23_method());
252 SSL_CTX_set_options(RTMP_TLS_ctx
, SSL_OP_ALL
);
253 SSL_CTX_set_default_verify_paths(RTMP_TLS_ctx
);
259 RTMP_TLS_AllocServerContext(const char* cert
, const char* key
)
266 tls_server_ctx
*tc
= ctx
= calloc(1, sizeof(struct tls_server_ctx
));
267 tc
->dhm_P
= my_dhm_P
;
268 tc
->dhm_G
= my_dhm_G
;
269 tc
->hs
= &RTMP_TLS_ctx
->hs
;
270 if (x509parse_crtfile(&tc
->cert
, cert
)) {
274 if (x509parse_keyfile(&tc
->key
, key
, NULL
)) {
275 x509_free(&tc
->cert
);
279 #elif defined(USE_GNUTLS) && !defined(NO_SSL)
280 gnutls_certificate_allocate_credentials((gnutls_certificate_credentials
*) &ctx
);
281 if (gnutls_certificate_set_x509_key_file(ctx
, cert
, key
, GNUTLS_X509_FMT_PEM
) != 0) {
282 gnutls_certificate_free_credentials(ctx
);
285 #elif !defined(NO_SSL) /* USE_OPENSSL */
286 ctx
= SSL_CTX_new(SSLv23_server_method());
287 if (!SSL_CTX_use_certificate_chain_file(ctx
, cert
)) {
291 if (!SSL_CTX_use_PrivateKey_file(ctx
, key
, SSL_FILETYPE_PEM
)) {
301 RTMP_TLS_FreeServerContext(void *ctx
)
305 x509_free(&((tls_server_ctx
*)ctx
)->cert
);
306 rsa_free(&((tls_server_ctx
*)ctx
)->key
);
308 #elif defined(USE_GNUTLS) && !defined(NO_SSL)
309 gnutls_certificate_free_credentials(ctx
);
310 #elif !defined(NO_SSL) /* USE_OPENSSL */
319 return calloc(1, sizeof(RTMP
));
336 memset(r
, 0, sizeof(RTMP
));
337 r
->m_sb
.sb_socket
= -1;
338 r
->m_inChunkSize
= RTMP_DEFAULT_CHUNKSIZE
;
339 r
->m_outChunkSize
= RTMP_DEFAULT_CHUNKSIZE
;
340 r
->m_nBufferMS
= 30000;
341 r
->m_nClientBW
= 2500000;
343 r
->m_nServerBW
= 2500000;
344 r
->m_fAudioCodecs
= 3191.0;
345 r
->m_fVideoCodecs
= 252.0;
346 r
->Link
.timeout
= 30;
351 RTMP_EnableWrite(RTMP
*r
)
353 r
->Link
.protocol
|= RTMP_FEATURE_WRITE
;
357 RTMP_GetDuration(RTMP
*r
)
359 return r
->m_fDuration
;
363 RTMP_IsConnected(RTMP
*r
)
365 return r
->m_sb
.sb_socket
!= -1;
371 return r
->m_sb
.sb_socket
;
375 RTMP_IsTimedout(RTMP
*r
)
377 return r
->m_sb
.sb_timedout
;
381 RTMP_SetBufferMS(RTMP
*r
, int size
)
383 r
->m_nBufferMS
= size
;
387 RTMP_UpdateBufferMS(RTMP
*r
)
389 RTMP_SendCtrl(r
, 3, r
->m_stream_id
, r
->m_nBufferMS
);
395 #elif defined(__sun__)
397 #elif defined(__APPLE__)
399 #elif defined(__linux__)
404 #define DEF_VERSTR OSS " 10,0,32,18"
405 static const char DEFAULT_FLASH_VER
[] = DEF_VERSTR
;
406 const AVal RTMP_DefaultFlashVer
=
407 { (char *)DEFAULT_FLASH_VER
, sizeof(DEFAULT_FLASH_VER
) - 1 };
410 SocksSetup(RTMP
*r
, AVal
*sockshost
)
412 if (sockshost
->av_len
)
414 const char *socksport
= strchr(sockshost
->av_val
, ':');
415 char *hostname
= strdup(sockshost
->av_val
);
418 hostname
[socksport
- sockshost
->av_val
] = '\0';
419 r
->Link
.sockshost
.av_val
= hostname
;
420 r
->Link
.sockshost
.av_len
= strlen(hostname
);
422 r
->Link
.socksport
= socksport
? atoi(socksport
+ 1) : 1080;
423 RTMP_Log(RTMP_LOGDEBUG
, "Connecting via SOCKS proxy: %s:%d", r
->Link
.sockshost
.av_val
,
428 r
->Link
.sockshost
.av_val
= NULL
;
429 r
->Link
.sockshost
.av_len
= 0;
430 r
->Link
.socksport
= 0;
435 RTMP_SetupStream(RTMP
*r
,
452 int dStop
, int bLiveStream
, long int timeout
)
454 RTMP_Log(RTMP_LOGDEBUG
, "Protocol : %s", RTMPProtocolStrings
[protocol
&7]);
455 RTMP_Log(RTMP_LOGDEBUG
, "Hostname : %.*s", host
->av_len
, host
->av_val
);
456 RTMP_Log(RTMP_LOGDEBUG
, "Port : %d", port
);
457 RTMP_Log(RTMP_LOGDEBUG
, "Playpath : %s", playpath
->av_val
);
459 if (tcUrl
&& tcUrl
->av_val
)
460 RTMP_Log(RTMP_LOGDEBUG
, "tcUrl : %s", tcUrl
->av_val
);
461 if (swfUrl
&& swfUrl
->av_val
)
462 RTMP_Log(RTMP_LOGDEBUG
, "swfUrl : %s", swfUrl
->av_val
);
463 if (pageUrl
&& pageUrl
->av_val
)
464 RTMP_Log(RTMP_LOGDEBUG
, "pageUrl : %s", pageUrl
->av_val
);
465 if (app
&& app
->av_val
)
466 RTMP_Log(RTMP_LOGDEBUG
, "app : %.*s", app
->av_len
, app
->av_val
);
467 if (auth
&& auth
->av_val
)
468 RTMP_Log(RTMP_LOGDEBUG
, "auth : %s", auth
->av_val
);
469 if (subscribepath
&& subscribepath
->av_val
)
470 RTMP_Log(RTMP_LOGDEBUG
, "subscribepath : %s", subscribepath
->av_val
);
471 if (usherToken
&& usherToken
->av_val
)
472 RTMP_Log(RTMP_LOGDEBUG
, "NetStream.Authenticate.UsherToken : %s", usherToken
->av_val
);
473 if (flashVer
&& flashVer
->av_val
)
474 RTMP_Log(RTMP_LOGDEBUG
, "flashVer : %s", flashVer
->av_val
);
476 RTMP_Log(RTMP_LOGDEBUG
, "StartTime : %d msec", dStart
);
478 RTMP_Log(RTMP_LOGDEBUG
, "StopTime : %d msec", dStop
);
480 RTMP_Log(RTMP_LOGDEBUG
, "live : %s", bLiveStream
? "yes" : "no");
481 RTMP_Log(RTMP_LOGDEBUG
, "timeout : %ld sec", timeout
);
484 if (swfSHA256Hash
!= NULL
&& swfSize
> 0)
486 memcpy(r
->Link
.SWFHash
, swfSHA256Hash
->av_val
, sizeof(r
->Link
.SWFHash
));
487 r
->Link
.SWFSize
= swfSize
;
488 RTMP_Log(RTMP_LOGDEBUG
, "SWFSHA256:");
489 RTMP_LogHex(RTMP_LOGDEBUG
, r
->Link
.SWFHash
, sizeof(r
->Link
.SWFHash
));
490 RTMP_Log(RTMP_LOGDEBUG
, "SWFSize : %u", r
->Link
.SWFSize
);
498 SocksSetup(r
, sockshost
);
500 if (tcUrl
&& tcUrl
->av_len
)
501 r
->Link
.tcUrl
= *tcUrl
;
502 if (swfUrl
&& swfUrl
->av_len
)
503 r
->Link
.swfUrl
= *swfUrl
;
504 if (pageUrl
&& pageUrl
->av_len
)
505 r
->Link
.pageUrl
= *pageUrl
;
506 if (app
&& app
->av_len
)
508 if (auth
&& auth
->av_len
)
510 r
->Link
.auth
= *auth
;
511 r
->Link
.lFlags
|= RTMP_LF_AUTH
;
513 if (flashVer
&& flashVer
->av_len
)
514 r
->Link
.flashVer
= *flashVer
;
516 r
->Link
.flashVer
= RTMP_DefaultFlashVer
;
517 if (subscribepath
&& subscribepath
->av_len
)
518 r
->Link
.subscribepath
= *subscribepath
;
519 if (usherToken
&& usherToken
->av_len
)
520 r
->Link
.usherToken
= *usherToken
;
521 r
->Link
.seekTime
= dStart
;
522 r
->Link
.stopTime
= dStop
;
524 r
->Link
.lFlags
|= RTMP_LF_LIVE
;
525 r
->Link
.timeout
= timeout
;
527 r
->Link
.protocol
= protocol
;
528 r
->Link
.hostname
= *host
;
530 r
->Link
.playpath
= *playpath
;
532 if (r
->Link
.port
== 0)
534 if (protocol
& RTMP_FEATURE_SSL
)
536 else if (protocol
& RTMP_FEATURE_HTTP
)
543 enum { OPT_STR
=0, OPT_INT
, OPT_BOOL
, OPT_CONN
};
544 static const char *optinfo
[] = {
545 "string", "integer", "boolean", "AMF" };
547 #define OFF(x) offsetof(struct RTMP,x)
549 static struct urlopt
{
556 { AVC("socks"), OFF(Link
.sockshost
), OPT_STR
, 0,
557 "Use the specified SOCKS proxy" },
558 { AVC("app"), OFF(Link
.app
), OPT_STR
, 0,
559 "Name of target app on server" },
560 { AVC("tcUrl"), OFF(Link
.tcUrl
), OPT_STR
, 0,
561 "URL to played stream" },
562 { AVC("pageUrl"), OFF(Link
.pageUrl
), OPT_STR
, 0,
563 "URL of played media's web page" },
564 { AVC("swfUrl"), OFF(Link
.swfUrl
), OPT_STR
, 0,
565 "URL to player SWF file" },
566 { AVC("flashver"), OFF(Link
.flashVer
), OPT_STR
, 0,
567 "Flash version string (default " DEF_VERSTR
")" },
568 { AVC("conn"), OFF(Link
.extras
), OPT_CONN
, 0,
569 "Append arbitrary AMF data to Connect message" },
570 { AVC("playpath"), OFF(Link
.playpath
), OPT_STR
, 0,
571 "Path to target media on server" },
572 { AVC("playlist"), OFF(Link
.lFlags
), OPT_BOOL
, RTMP_LF_PLST
,
573 "Set playlist before play command" },
574 { AVC("live"), OFF(Link
.lFlags
), OPT_BOOL
, RTMP_LF_LIVE
,
575 "Stream is live, no seeking possible" },
576 { AVC("subscribe"), OFF(Link
.subscribepath
), OPT_STR
, 0,
577 "Stream to subscribe to" },
578 { AVC("jtv"), OFF(Link
.usherToken
), OPT_STR
, 0,
579 "Justin.tv authentication token" },
580 { AVC("token"), OFF(Link
.token
), OPT_STR
, 0,
581 "Key for SecureToken response" },
582 { AVC("swfVfy"), OFF(Link
.lFlags
), OPT_BOOL
, RTMP_LF_SWFV
,
583 "Perform SWF Verification" },
584 { AVC("swfAge"), OFF(Link
.swfAge
), OPT_INT
, 0,
585 "Number of days to use cached SWF hash" },
586 { AVC("start"), OFF(Link
.seekTime
), OPT_INT
, 0,
587 "Stream start position in milliseconds" },
588 { AVC("stop"), OFF(Link
.stopTime
), OPT_INT
, 0,
589 "Stream stop position in milliseconds" },
590 { AVC("buffer"), OFF(m_nBufferMS
), OPT_INT
, 0,
591 "Buffer time in milliseconds" },
592 { AVC("timeout"), OFF(Link
.timeout
), OPT_INT
, 0,
593 "Session timeout in seconds" },
594 { AVC("pubUser"), OFF(Link
.pubUser
), OPT_STR
, 0,
595 "Publisher username" },
596 { AVC("pubPasswd"), OFF(Link
.pubPasswd
), OPT_STR
, 0,
597 "Publisher password" },
601 static const AVal truth
[] = {
609 static void RTMP_OptUsage()
613 RTMP_Log(RTMP_LOGERROR
, "Valid RTMP options are:\n");
614 for (i
=0; options
[i
].name
.av_len
; i
++) {
615 RTMP_Log(RTMP_LOGERROR
, "%10s %-7s %s\n", options
[i
].name
.av_val
,
616 optinfo
[options
[i
].otype
], options
[i
].use
);
621 parseAMF(AMFObject
*obj
, AVal
*av
, int *depth
)
623 AMFObjectProperty prop
= {{0,0}};
625 char *p
, *arg
= av
->av_val
;
633 prop
.p_type
= AMF_BOOLEAN
;
634 prop
.p_vu
.p_number
= atoi(p
);
637 prop
.p_type
= AMF_STRING
;
638 prop
.p_vu
.p_aval
.av_val
= p
;
639 prop
.p_vu
.p_aval
.av_len
= av
->av_len
- (p
-arg
);
642 prop
.p_type
= AMF_NUMBER
;
643 prop
.p_vu
.p_number
= strtod(p
, NULL
);
646 prop
.p_type
= AMF_NULL
;
652 prop
.p_type
= AMF_OBJECT
;
664 else if (arg
[2] == ':' && arg
[0] == 'N')
666 p
= strchr(arg
+3, ':');
669 prop
.p_name
.av_val
= (char *)arg
+3;
670 prop
.p_name
.av_len
= p
- (arg
+3);
676 prop
.p_type
= AMF_BOOLEAN
;
677 prop
.p_vu
.p_number
= atoi(p
);
680 prop
.p_type
= AMF_STRING
;
681 prop
.p_vu
.p_aval
.av_val
= p
;
682 prop
.p_vu
.p_aval
.av_len
= av
->av_len
- (p
-arg
);
685 prop
.p_type
= AMF_NUMBER
;
686 prop
.p_vu
.p_number
= strtod(p
, NULL
);
689 prop
.p_type
= AMF_OBJECT
;
701 for (i
=0; i
<*depth
; i
++)
703 o2
= &obj
->o_props
[obj
->o_num
-1].p_vu
.p_object
;
707 AMF_AddProp(obj
, &prop
);
708 if (prop
.p_type
== AMF_OBJECT
)
713 int RTMP_SetOpt(RTMP
*r
, const AVal
*opt
, AVal
*arg
)
718 for (i
=0; options
[i
].name
.av_len
; i
++) {
719 if (opt
->av_len
!= options
[i
].name
.av_len
) continue;
720 if (strcasecmp(opt
->av_val
, options
[i
].name
.av_val
)) continue;
721 v
= (char *)r
+ options
[i
].off
;
722 switch(options
[i
].otype
) {
728 long l
= strtol(arg
->av_val
, NULL
, 0);
734 for (j
=0; truth
[j
].av_len
; j
++) {
735 if (arg
->av_len
!= truth
[j
].av_len
) continue;
736 if (strcasecmp(arg
->av_val
, truth
[j
].av_val
)) continue;
737 fl
|= options
[i
].omisc
; break; }
742 if (parseAMF(&r
->Link
.extras
, arg
, &r
->Link
.edepth
))
748 if (!options
[i
].name
.av_len
) {
749 RTMP_Log(RTMP_LOGERROR
, "Unknown option %s", opt
->av_val
);
757 int RTMP_SetupURL(RTMP
*r
, char *url
)
760 char *p1
, *p2
, *ptr
= strchr(url
, ' ');
762 unsigned int port
= 0;
768 ret
= RTMP_ParseURL(url
, &r
->Link
.protocol
, &r
->Link
.hostname
,
769 &port
, &r
->Link
.playpath0
, &r
->Link
.app
);
773 r
->Link
.playpath
= r
->Link
.playpath0
;
778 p2
= strchr(p1
, '=');
782 opt
.av_len
= p2
- p1
;
785 ptr
= strchr(p2
, ' ');
788 arg
.av_len
= ptr
- p2
;
789 /* skip repeated spaces */
793 arg
.av_len
= strlen(p2
);
798 for (p1
=p2
; port
>0;) {
803 sscanf(p1
+1, "%02x", &c
);
812 arg
.av_len
= p2
- arg
.av_val
;
814 ret
= RTMP_SetOpt(r
, &opt
, &arg
);
819 if (!r
->Link
.tcUrl
.av_len
)
821 r
->Link
.tcUrl
.av_val
= url
;
822 if (r
->Link
.app
.av_len
)
824 if (r
->Link
.app
.av_val
< url
+ len
)
826 /* if app is part of original url, just use it */
827 r
->Link
.tcUrl
.av_len
= r
->Link
.app
.av_len
+ (r
->Link
.app
.av_val
- url
);
831 len
= r
->Link
.hostname
.av_len
+ r
->Link
.app
.av_len
+
832 sizeof("rtmpte://:65535/");
833 r
->Link
.tcUrl
.av_val
= malloc(len
);
834 r
->Link
.tcUrl
.av_len
= snprintf(r
->Link
.tcUrl
.av_val
, len
,
836 RTMPProtocolStringsLower
[r
->Link
.protocol
],
837 r
->Link
.hostname
.av_len
, r
->Link
.hostname
.av_val
,
839 r
->Link
.app
.av_len
, r
->Link
.app
.av_val
);
840 r
->Link
.lFlags
|= RTMP_LF_FTCU
;
845 r
->Link
.tcUrl
.av_len
= strlen(url
);
850 if ((r
->Link
.lFlags
& RTMP_LF_SWFV
) && r
->Link
.swfUrl
.av_len
)
851 RTMP_HashSWF(r
->Link
.swfUrl
.av_val
, &r
->Link
.SWFSize
,
852 (unsigned char *)r
->Link
.SWFHash
, r
->Link
.swfAge
);
855 SocksSetup(r
, &r
->Link
.sockshost
);
857 if (r
->Link
.port
== 0)
859 if (r
->Link
.protocol
& RTMP_FEATURE_SSL
)
861 else if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
870 add_addr_info(struct sockaddr_in
*service
, AVal
*host
, int port
)
874 if (host
->av_val
[host
->av_len
])
876 hostname
= malloc(host
->av_len
+1);
877 memcpy(hostname
, host
->av_val
, host
->av_len
);
878 hostname
[host
->av_len
] = '\0';
882 hostname
= host
->av_val
;
885 service
->sin_addr
.s_addr
= inet_addr(hostname
);
886 if (service
->sin_addr
.s_addr
== INADDR_NONE
)
888 struct hostent
*host
= gethostbyname(hostname
);
889 if (host
== NULL
|| host
->h_addr
== NULL
)
891 RTMP_Log(RTMP_LOGERROR
, "Problem accessing the DNS. (addr: %s)", hostname
);
895 service
->sin_addr
= *(struct in_addr
*)host
->h_addr
;
898 service
->sin_port
= htons(port
);
900 if (hostname
!= host
->av_val
)
906 RTMP_Connect0(RTMP
*r
, struct sockaddr
* service
)
909 r
->m_sb
.sb_timedout
= FALSE
;
911 r
->m_fDuration
= 0.0;
913 r
->m_sb
.sb_socket
= socket(AF_INET
, SOCK_STREAM
, IPPROTO_TCP
);
914 if (r
->m_sb
.sb_socket
!= -1)
916 if (connect(r
->m_sb
.sb_socket
, service
, sizeof(struct sockaddr
)) < 0)
918 int err
= GetSockError();
919 RTMP_Log(RTMP_LOGERROR
, "%s, failed to connect socket. %d (%s)",
920 __FUNCTION__
, err
, strerror(err
));
925 if (r
->Link
.socksport
)
927 RTMP_Log(RTMP_LOGDEBUG
, "%s ... SOCKS negotiation", __FUNCTION__
);
928 if (!SocksNegotiate(r
))
930 RTMP_Log(RTMP_LOGERROR
, "%s, SOCKS negotiation failed.", __FUNCTION__
);
938 RTMP_Log(RTMP_LOGERROR
, "%s, failed to create socket. Error: %d", __FUNCTION__
,
945 SET_RCVTIMEO(tv
, r
->Link
.timeout
);
947 (r
->m_sb
.sb_socket
, SOL_SOCKET
, SO_RCVTIMEO
, (char *)&tv
, sizeof(tv
)))
949 RTMP_Log(RTMP_LOGERROR
, "%s, Setting socket timeout to %ds failed!",
950 __FUNCTION__
, r
->Link
.timeout
);
954 setsockopt(r
->m_sb
.sb_socket
, IPPROTO_TCP
, TCP_NODELAY
, (char *) &on
, sizeof(on
));
960 RTMP_TLS_Accept(RTMP
*r
, void *ctx
)
962 #if defined(CRYPTO) && !defined(NO_SSL)
963 TLS_server(ctx
, r
->m_sb
.sb_ssl
);
964 TLS_setfd(r
->m_sb
.sb_ssl
, r
->m_sb
.sb_socket
);
965 if (TLS_accept(r
->m_sb
.sb_ssl
) < 0)
967 RTMP_Log(RTMP_LOGERROR
, "%s, TLS_Connect failed", __FUNCTION__
);
977 RTMP_Connect1(RTMP
*r
, RTMPPacket
*cp
)
979 if (r
->Link
.protocol
& RTMP_FEATURE_SSL
)
981 #if defined(CRYPTO) && !defined(NO_SSL)
982 TLS_client(RTMP_TLS_ctx
, r
->m_sb
.sb_ssl
);
983 TLS_setfd(r
->m_sb
.sb_ssl
, r
->m_sb
.sb_socket
);
984 if (TLS_connect(r
->m_sb
.sb_ssl
) < 0)
986 RTMP_Log(RTMP_LOGERROR
, "%s, TLS_Connect failed", __FUNCTION__
);
991 RTMP_Log(RTMP_LOGERROR
, "%s, no SSL/TLS support", __FUNCTION__
);
997 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
1000 r
->m_clientID
.av_val
= NULL
;
1001 r
->m_clientID
.av_len
= 0;
1002 HTTP_Post(r
, RTMPT_OPEN
, "", 1);
1003 if (HTTP_read(r
, 1) != 0)
1005 r
->m_msgCounter
= 0;
1006 RTMP_Log(RTMP_LOGDEBUG
, "%s, Could not connect for handshake", __FUNCTION__
);
1010 r
->m_msgCounter
= 0;
1012 RTMP_Log(RTMP_LOGDEBUG
, "%s, ... connected, handshaking", __FUNCTION__
);
1013 if (!HandShake(r
, TRUE
))
1015 RTMP_Log(RTMP_LOGERROR
, "%s, handshake failed.", __FUNCTION__
);
1019 RTMP_Log(RTMP_LOGDEBUG
, "%s, handshaked", __FUNCTION__
);
1021 if (!SendConnectPacket(r
, cp
))
1023 RTMP_Log(RTMP_LOGERROR
, "%s, RTMP connect failed.", __FUNCTION__
);
1031 RTMP_Connect(RTMP
*r
, RTMPPacket
*cp
)
1033 struct sockaddr_in service
;
1034 if (!r
->Link
.hostname
.av_len
)
1037 memset(&service
, 0, sizeof(struct sockaddr_in
));
1038 service
.sin_family
= AF_INET
;
1040 if (r
->Link
.socksport
)
1042 /* Connect via SOCKS */
1043 if (!add_addr_info(&service
, &r
->Link
.sockshost
, r
->Link
.socksport
))
1048 /* Connect directly */
1049 if (!add_addr_info(&service
, &r
->Link
.hostname
, r
->Link
.port
))
1053 if (!RTMP_Connect0(r
, (struct sockaddr
*)&service
))
1056 r
->m_bSendCounter
= TRUE
;
1058 return RTMP_Connect1(r
, cp
);
1062 SocksNegotiate(RTMP
*r
)
1065 struct sockaddr_in service
;
1066 memset(&service
, 0, sizeof(struct sockaddr_in
));
1068 add_addr_info(&service
, &r
->Link
.hostname
, r
->Link
.port
);
1069 addr
= htonl(service
.sin_addr
.s_addr
);
1073 4, 1, /* SOCKS 4, connect */
1074 (r
->Link
.port
>> 8) & 0xFF,
1075 (r
->Link
.port
) & 0xFF,
1076 (char)(addr
>> 24) & 0xFF, (char)(addr
>> 16) & 0xFF,
1077 (char)(addr
>> 8) & 0xFF, (char)addr
& 0xFF,
1079 }; /* NULL terminate */
1081 WriteN(r
, packet
, sizeof packet
);
1083 if (ReadN(r
, packet
, 8) != 8)
1086 if (packet
[0] == 0 && packet
[1] == 90)
1092 RTMP_Log(RTMP_LOGERROR
, "%s, SOCKS returned error code %d", __FUNCTION__
, packet
[1]);
1099 RTMP_ConnectStream(RTMP
*r
, int seekTime
)
1101 RTMPPacket packet
= { 0 };
1103 /* seekTime was already set by SetupStream / SetupURL.
1104 * This is only needed by ReconnectStream.
1107 r
->Link
.seekTime
= seekTime
;
1109 r
->m_mediaChannel
= 0;
1111 while (!r
->m_bPlaying
&& RTMP_IsConnected(r
) && RTMP_ReadPacket(r
, &packet
))
1113 if (RTMPPacket_IsReady(&packet
))
1115 if (!packet
.m_nBodySize
)
1117 if ((packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
) ||
1118 (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
) ||
1119 (packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
))
1121 RTMP_Log(RTMP_LOGWARNING
, "Received FLV packet before play()! Ignoring.");
1122 RTMPPacket_Free(&packet
);
1126 RTMP_ClientPacket(r
, &packet
);
1127 RTMPPacket_Free(&packet
);
1131 return r
->m_bPlaying
;
1135 RTMP_ReconnectStream(RTMP
*r
, int seekTime
)
1137 RTMP_DeleteStream(r
);
1139 RTMP_SendCreateStream(r
);
1141 return RTMP_ConnectStream(r
, seekTime
);
1145 RTMP_ToggleStream(RTMP
*r
)
1151 if (RTMP_IsTimedout(r
) && r
->m_read
.status
== RTMP_READ_EOF
)
1152 r
->m_read
.status
= 0;
1154 res
= RTMP_SendPause(r
, TRUE
, r
->m_pauseStamp
);
1161 res
= RTMP_SendPause(r
, FALSE
, r
->m_pauseStamp
);
1167 RTMP_DeleteStream(RTMP
*r
)
1169 if (r
->m_stream_id
< 0)
1172 r
->m_bPlaying
= FALSE
;
1174 SendDeleteStream(r
, r
->m_stream_id
);
1175 r
->m_stream_id
= -1;
1179 RTMP_GetNextMediaPacket(RTMP
*r
, RTMPPacket
*packet
)
1181 int bHasMediaPacket
= 0;
1183 while (!bHasMediaPacket
&& RTMP_IsConnected(r
)
1184 && RTMP_ReadPacket(r
, packet
))
1186 if (!RTMPPacket_IsReady(packet
) || !packet
->m_nBodySize
)
1191 bHasMediaPacket
= RTMP_ClientPacket(r
, packet
);
1193 if (!bHasMediaPacket
)
1195 RTMPPacket_Free(packet
);
1197 else if (r
->m_pausing
== 3)
1199 if (packet
->m_nTimeStamp
<= r
->m_mediaStamp
)
1201 bHasMediaPacket
= 0;
1203 RTMP_Log(RTMP_LOGDEBUG
,
1204 "Skipped type: %02X, size: %d, TS: %d ms, abs TS: %d, pause: %d ms",
1205 packet
->m_packetType
, packet
->m_nBodySize
,
1206 packet
->m_nTimeStamp
, packet
->m_hasAbsTimestamp
,
1209 RTMPPacket_Free(packet
);
1216 if (bHasMediaPacket
)
1217 r
->m_bPlaying
= TRUE
;
1218 else if (r
->m_sb
.sb_timedout
&& !r
->m_pausing
)
1219 r
->m_pauseStamp
= r
->m_mediaChannel
< r
->m_channelsAllocatedIn
?
1220 r
->m_channelTimestamp
[r
->m_mediaChannel
] : 0;
1222 return bHasMediaPacket
;
1226 RTMP_ClientPacket(RTMP
*r
, RTMPPacket
*packet
)
1228 int bHasMediaPacket
= 0;
1229 switch (packet
->m_packetType
)
1231 case RTMP_PACKET_TYPE_CHUNK_SIZE
:
1233 HandleChangeChunkSize(r
, packet
);
1236 case RTMP_PACKET_TYPE_BYTES_READ_REPORT
:
1237 /* bytes read report */
1238 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: bytes read report", __FUNCTION__
);
1241 case RTMP_PACKET_TYPE_CONTROL
:
1243 HandleCtrl(r
, packet
);
1246 case RTMP_PACKET_TYPE_SERVER_BW
:
1248 HandleServerBW(r
, packet
);
1251 case RTMP_PACKET_TYPE_CLIENT_BW
:
1253 HandleClientBW(r
, packet
);
1256 case RTMP_PACKET_TYPE_AUDIO
:
1258 /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); */
1259 HandleAudio(r
, packet
);
1260 bHasMediaPacket
= 1;
1261 if (!r
->m_mediaChannel
)
1262 r
->m_mediaChannel
= packet
->m_nChannel
;
1264 r
->m_mediaStamp
= packet
->m_nTimeStamp
;
1267 case RTMP_PACKET_TYPE_VIDEO
:
1269 /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); */
1270 HandleVideo(r
, packet
);
1271 bHasMediaPacket
= 1;
1272 if (!r
->m_mediaChannel
)
1273 r
->m_mediaChannel
= packet
->m_nChannel
;
1275 r
->m_mediaStamp
= packet
->m_nTimeStamp
;
1278 case RTMP_PACKET_TYPE_FLEX_STREAM_SEND
:
1279 /* flex stream send */
1280 RTMP_Log(RTMP_LOGDEBUG
,
1281 "%s, flex stream send, size %u bytes, not supported, ignoring",
1282 __FUNCTION__
, packet
->m_nBodySize
);
1285 case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT
:
1286 /* flex shared object */
1287 RTMP_Log(RTMP_LOGDEBUG
,
1288 "%s, flex shared object, size %u bytes, not supported, ignoring",
1289 __FUNCTION__
, packet
->m_nBodySize
);
1292 case RTMP_PACKET_TYPE_FLEX_MESSAGE
:
1295 RTMP_Log(RTMP_LOGDEBUG
,
1296 "%s, flex message, size %u bytes, not fully supported",
1297 __FUNCTION__
, packet
->m_nBodySize
);
1298 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
1300 /* some DEBUG code */
1302 RTMP_LIB_AMFObject obj
;
1303 int nRes
= obj
.Decode(packet
.m_body
+1, packet
.m_nBodySize
-1);
1305 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding AMF3 packet", __FUNCTION__
);
1312 if (HandleInvoke(r
, packet
->m_body
+ 1, packet
->m_nBodySize
- 1) == 1)
1313 bHasMediaPacket
= 2;
1316 case RTMP_PACKET_TYPE_INFO
:
1317 /* metadata (notify) */
1318 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: notify %u bytes", __FUNCTION__
,
1319 packet
->m_nBodySize
);
1320 if (HandleMetadata(r
, packet
->m_body
, packet
->m_nBodySize
))
1321 bHasMediaPacket
= 1;
1324 case RTMP_PACKET_TYPE_SHARED_OBJECT
:
1325 RTMP_Log(RTMP_LOGDEBUG
, "%s, shared object, not supported, ignoring",
1329 case RTMP_PACKET_TYPE_INVOKE
:
1331 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: invoke %u bytes", __FUNCTION__
,
1332 packet
->m_nBodySize
);
1333 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
1335 if (HandleInvoke(r
, packet
->m_body
, packet
->m_nBodySize
) == 1)
1336 bHasMediaPacket
= 2;
1339 case RTMP_PACKET_TYPE_FLASH_VIDEO
:
1341 /* go through FLV packets and handle metadata packets */
1342 unsigned int pos
= 0;
1343 uint32_t nTimeStamp
= packet
->m_nTimeStamp
;
1345 while (pos
+ 11 < packet
->m_nBodySize
)
1347 uint32_t dataSize
= AMF_DecodeInt24(packet
->m_body
+ pos
+ 1); /* size without header (11) and prevTagSize (4) */
1349 if (pos
+ 11 + dataSize
+ 4 > packet
->m_nBodySize
)
1351 RTMP_Log(RTMP_LOGWARNING
, "Stream corrupt?!");
1354 if (packet
->m_body
[pos
] == 0x12)
1356 HandleMetadata(r
, packet
->m_body
+ pos
+ 11, dataSize
);
1358 else if (packet
->m_body
[pos
] == 8 || packet
->m_body
[pos
] == 9)
1360 nTimeStamp
= AMF_DecodeInt24(packet
->m_body
+ pos
+ 4);
1361 nTimeStamp
|= (packet
->m_body
[pos
+ 7] << 24);
1363 pos
+= (11 + dataSize
+ 4);
1366 r
->m_mediaStamp
= nTimeStamp
;
1369 /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: FLV tag(s) %lu bytes", __FUNCTION__, packet.m_nBodySize); */
1370 bHasMediaPacket
= 1;
1374 RTMP_Log(RTMP_LOGDEBUG
, "%s, unknown packet type received: 0x%02x", __FUNCTION__
,
1375 packet
->m_packetType
);
1377 RTMP_LogHex(RTMP_LOGDEBUG
, packet
->m_body
, packet
->m_nBodySize
);
1381 return bHasMediaPacket
;
1385 extern FILE *netstackdump
;
1386 extern FILE *netstackdump_read
;
1390 ReadN(RTMP
*r
, char *buffer
, int n
)
1392 int nOriginalSize
= n
;
1396 r
->m_sb
.sb_timedout
= FALSE
;
1399 memset(buffer
, 0, n
);
1405 int nBytes
= 0, nRead
;
1406 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
1409 while (!r
->m_resplen
)
1412 if (r
->m_sb
.sb_size
< 13 || refill
)
1415 HTTP_Post(r
, RTMPT_IDLE
, "", 1);
1416 if (RTMPSockBuf_Fill(&r
->m_sb
) < 1)
1418 if (!r
->m_sb
.sb_timedout
)
1423 if ((ret
= HTTP_read(r
, 0)) == -1)
1425 RTMP_Log(RTMP_LOGDEBUG
, "%s, No valid HTTP response found", __FUNCTION__
);
1438 if (r
->m_resplen
&& !r
->m_sb
.sb_size
)
1439 RTMPSockBuf_Fill(&r
->m_sb
);
1440 avail
= r
->m_sb
.sb_size
;
1441 if (avail
> r
->m_resplen
)
1442 avail
= r
->m_resplen
;
1446 avail
= r
->m_sb
.sb_size
;
1449 if (RTMPSockBuf_Fill(&r
->m_sb
) < 1)
1451 if (!r
->m_sb
.sb_timedout
)
1455 avail
= r
->m_sb
.sb_size
;
1458 nRead
= ((n
< avail
) ? n
: avail
);
1461 memcpy(ptr
, r
->m_sb
.sb_start
, nRead
);
1462 r
->m_sb
.sb_start
+= nRead
;
1463 r
->m_sb
.sb_size
-= nRead
;
1465 r
->m_nBytesIn
+= nRead
;
1466 if (r
->m_bSendCounter
1467 && r
->m_nBytesIn
> ( r
->m_nBytesInSent
+ r
->m_nClientBW
/ 10))
1468 if (!SendBytesReceived(r
))
1471 /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */
1473 fwrite(ptr
, 1, nBytes
, netstackdump_read
);
1478 RTMP_Log(RTMP_LOGDEBUG
, "%s, RTMP socket closed by peer", __FUNCTION__
);
1484 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
1485 r
->m_resplen
-= nBytes
;
1488 if (r
->Link
.rc4keyIn
)
1490 RC4_encrypt(r
->Link
.rc4keyIn
, nBytes
, ptr
);
1498 return nOriginalSize
- n
;
1502 WriteN(RTMP
*r
, const char *buffer
, int n
)
1504 const char *ptr
= buffer
;
1506 char *encrypted
= 0;
1507 char buf
[RTMP_BUFFER_CACHE_SIZE
];
1509 if (r
->Link
.rc4keyOut
)
1511 if (n
> sizeof(buf
))
1512 encrypted
= (char *)malloc(n
);
1514 encrypted
= (char *)buf
;
1516 RC4_encrypt2(r
->Link
.rc4keyOut
, n
, buffer
, ptr
);
1524 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
1525 nBytes
= HTTP_Post(r
, RTMPT_SEND
, ptr
, n
);
1527 nBytes
= RTMPSockBuf_Send(&r
->m_sb
, ptr
, n
);
1528 /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d\n", __FUNCTION__, nBytes); */
1532 int sockerr
= GetSockError();
1533 RTMP_Log(RTMP_LOGERROR
, "%s, RTMP send error %d (%d bytes)", __FUNCTION__
,
1536 if (sockerr
== EINTR
&& !RTMP_ctrlC
)
1552 if (encrypted
&& encrypted
!= buf
)
1559 #define SAVC(x) static const AVal av_##x = AVC(#x)
1571 SAVC(videoFunction
);
1572 SAVC(objectEncoding
);
1574 SAVC(secureTokenResponse
);
1579 SendConnectPacket(RTMP
*r
, RTMPPacket
*cp
)
1582 char pbuf
[4096], *pend
= pbuf
+ sizeof(pbuf
);
1586 return RTMP_SendPacket(r
, cp
, TRUE
);
1588 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1589 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
1590 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1591 packet
.m_nTimeStamp
= 0;
1592 packet
.m_nInfoField2
= 0;
1593 packet
.m_hasAbsTimestamp
= 0;
1594 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1596 enc
= packet
.m_body
;
1597 enc
= AMF_EncodeString(enc
, pend
, &av_connect
);
1598 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1599 *enc
++ = AMF_OBJECT
;
1601 enc
= AMF_EncodeNamedString(enc
, pend
, &av_app
, &r
->Link
.app
);
1604 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
1606 enc
= AMF_EncodeNamedString(enc
, pend
, &av_type
, &av_nonprivate
);
1610 if (r
->Link
.flashVer
.av_len
)
1612 enc
= AMF_EncodeNamedString(enc
, pend
, &av_flashVer
, &r
->Link
.flashVer
);
1616 if (r
->Link
.swfUrl
.av_len
)
1618 enc
= AMF_EncodeNamedString(enc
, pend
, &av_swfUrl
, &r
->Link
.swfUrl
);
1622 if (r
->Link
.tcUrl
.av_len
)
1624 enc
= AMF_EncodeNamedString(enc
, pend
, &av_tcUrl
, &r
->Link
.tcUrl
);
1628 if (!(r
->Link
.protocol
& RTMP_FEATURE_WRITE
))
1630 enc
= AMF_EncodeNamedBoolean(enc
, pend
, &av_fpad
, FALSE
);
1633 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_capabilities
, 15.0);
1636 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_audioCodecs
, r
->m_fAudioCodecs
);
1639 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_videoCodecs
, r
->m_fVideoCodecs
);
1642 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_videoFunction
, 1.0);
1645 if (r
->Link
.pageUrl
.av_len
)
1647 enc
= AMF_EncodeNamedString(enc
, pend
, &av_pageUrl
, &r
->Link
.pageUrl
);
1652 if (r
->m_fEncoding
!= 0.0 || r
->m_bSendEncoding
)
1653 { /* AMF0, AMF3 not fully supported yet */
1654 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_objectEncoding
, r
->m_fEncoding
);
1658 if (enc
+ 3 >= pend
)
1661 *enc
++ = 0; /* end of object - 0x00 0x00 0x09 */
1662 *enc
++ = AMF_OBJECT_END
;
1664 /* add auth string */
1665 if (r
->Link
.auth
.av_len
)
1667 enc
= AMF_EncodeBoolean(enc
, pend
, r
->Link
.lFlags
& RTMP_LF_AUTH
);
1670 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.auth
);
1674 if (r
->Link
.extras
.o_num
)
1677 for (i
= 0; i
< r
->Link
.extras
.o_num
; i
++)
1679 enc
= AMFProp_Encode(&r
->Link
.extras
.o_props
[i
], enc
, pend
);
1684 packet
.m_nBodySize
= enc
- packet
.m_body
;
1686 return RTMP_SendPacket(r
, &packet
, TRUE
);
1693 SendBGHasStream(RTMP
*r
, double dId
, AVal
*playpath
)
1696 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1699 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1700 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1701 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1702 packet
.m_nTimeStamp
= 0;
1703 packet
.m_nInfoField2
= 0;
1704 packet
.m_hasAbsTimestamp
= 0;
1705 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1707 enc
= packet
.m_body
;
1708 enc
= AMF_EncodeString(enc
, pend
, &av_bgHasStream
);
1709 enc
= AMF_EncodeNumber(enc
, pend
, dId
);
1712 enc
= AMF_EncodeString(enc
, pend
, playpath
);
1716 packet
.m_nBodySize
= enc
- packet
.m_body
;
1718 return RTMP_SendPacket(r
, &packet
, TRUE
);
1725 RTMP_SendCreateStream(RTMP
*r
)
1728 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1731 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1732 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1733 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1734 packet
.m_nTimeStamp
= 0;
1735 packet
.m_nInfoField2
= 0;
1736 packet
.m_hasAbsTimestamp
= 0;
1737 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1739 enc
= packet
.m_body
;
1740 enc
= AMF_EncodeString(enc
, pend
, &av_createStream
);
1741 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1742 *enc
++ = AMF_NULL
; /* NULL */
1744 packet
.m_nBodySize
= enc
- packet
.m_body
;
1746 return RTMP_SendPacket(r
, &packet
, TRUE
);
1752 SendFCSubscribe(RTMP
*r
, AVal
*subscribepath
)
1755 char pbuf
[512], *pend
= pbuf
+ sizeof(pbuf
);
1757 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1758 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1759 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1760 packet
.m_nTimeStamp
= 0;
1761 packet
.m_nInfoField2
= 0;
1762 packet
.m_hasAbsTimestamp
= 0;
1763 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1765 RTMP_Log(RTMP_LOGDEBUG
, "FCSubscribe: %s", subscribepath
->av_val
);
1766 enc
= packet
.m_body
;
1767 enc
= AMF_EncodeString(enc
, pend
, &av_FCSubscribe
);
1768 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1770 enc
= AMF_EncodeString(enc
, pend
, subscribepath
);
1775 packet
.m_nBodySize
= enc
- packet
.m_body
;
1777 return RTMP_SendPacket(r
, &packet
, TRUE
);
1780 /* Justin.tv specific authentication */
1781 static const AVal av_NetStream_Authenticate_UsherToken
= AVC("NetStream.Authenticate.UsherToken");
1784 SendUsherToken(RTMP
*r
, AVal
*usherToken
)
1787 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1789 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1790 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1791 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1792 packet
.m_nTimeStamp
= 0;
1793 packet
.m_nInfoField2
= 0;
1794 packet
.m_hasAbsTimestamp
= 0;
1795 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1797 RTMP_Log(RTMP_LOGDEBUG
, "UsherToken: %s", usherToken
->av_val
);
1798 enc
= packet
.m_body
;
1799 enc
= AMF_EncodeString(enc
, pend
, &av_NetStream_Authenticate_UsherToken
);
1800 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1802 enc
= AMF_EncodeString(enc
, pend
, usherToken
);
1807 packet
.m_nBodySize
= enc
- packet
.m_body
;
1809 return RTMP_SendPacket(r
, &packet
, FALSE
);
1811 /******************************************/
1813 SAVC(releaseStream
);
1816 SendReleaseStream(RTMP
*r
)
1819 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1822 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1823 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1824 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1825 packet
.m_nTimeStamp
= 0;
1826 packet
.m_nInfoField2
= 0;
1827 packet
.m_hasAbsTimestamp
= 0;
1828 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1830 enc
= packet
.m_body
;
1831 enc
= AMF_EncodeString(enc
, pend
, &av_releaseStream
);
1832 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1834 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1838 packet
.m_nBodySize
= enc
- packet
.m_body
;
1840 return RTMP_SendPacket(r
, &packet
, FALSE
);
1846 SendFCPublish(RTMP
*r
)
1849 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1852 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1853 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1854 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1855 packet
.m_nTimeStamp
= 0;
1856 packet
.m_nInfoField2
= 0;
1857 packet
.m_hasAbsTimestamp
= 0;
1858 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1860 enc
= packet
.m_body
;
1861 enc
= AMF_EncodeString(enc
, pend
, &av_FCPublish
);
1862 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1864 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1868 packet
.m_nBodySize
= enc
- packet
.m_body
;
1870 return RTMP_SendPacket(r
, &packet
, FALSE
);
1876 SendFCUnpublish(RTMP
*r
)
1879 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1882 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1883 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1884 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1885 packet
.m_nTimeStamp
= 0;
1886 packet
.m_nInfoField2
= 0;
1887 packet
.m_hasAbsTimestamp
= 0;
1888 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1890 enc
= packet
.m_body
;
1891 enc
= AMF_EncodeString(enc
, pend
, &av_FCUnpublish
);
1892 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1894 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1898 packet
.m_nBodySize
= enc
- packet
.m_body
;
1900 return RTMP_SendPacket(r
, &packet
, FALSE
);
1908 SendPublish(RTMP
*r
)
1911 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1914 packet
.m_nChannel
= 0x04; /* source channel (invoke) */
1915 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
1916 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1917 packet
.m_nTimeStamp
= 0;
1918 packet
.m_nInfoField2
= r
->m_stream_id
;
1919 packet
.m_hasAbsTimestamp
= 0;
1920 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1922 enc
= packet
.m_body
;
1923 enc
= AMF_EncodeString(enc
, pend
, &av_publish
);
1924 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1926 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1930 /* FIXME: should we choose live based on Link.lFlags & RTMP_LF_LIVE? */
1931 enc
= AMF_EncodeString(enc
, pend
, &av_live
);
1935 packet
.m_nBodySize
= enc
- packet
.m_body
;
1937 return RTMP_SendPacket(r
, &packet
, TRUE
);
1943 SendDeleteStream(RTMP
*r
, double dStreamId
)
1946 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1949 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1950 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1951 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1952 packet
.m_nTimeStamp
= 0;
1953 packet
.m_nInfoField2
= 0;
1954 packet
.m_hasAbsTimestamp
= 0;
1955 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1957 enc
= packet
.m_body
;
1958 enc
= AMF_EncodeString(enc
, pend
, &av_deleteStream
);
1959 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1961 enc
= AMF_EncodeNumber(enc
, pend
, dStreamId
);
1963 packet
.m_nBodySize
= enc
- packet
.m_body
;
1965 /* no response expected */
1966 return RTMP_SendPacket(r
, &packet
, FALSE
);
1972 RTMP_SendPause(RTMP
*r
, int DoPause
, int iTime
)
1975 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1978 packet
.m_nChannel
= 0x08; /* video channel */
1979 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1980 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1981 packet
.m_nTimeStamp
= 0;
1982 packet
.m_nInfoField2
= 0;
1983 packet
.m_hasAbsTimestamp
= 0;
1984 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1986 enc
= packet
.m_body
;
1987 enc
= AMF_EncodeString(enc
, pend
, &av_pause
);
1988 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1990 enc
= AMF_EncodeBoolean(enc
, pend
, DoPause
);
1991 enc
= AMF_EncodeNumber(enc
, pend
, (double)iTime
);
1993 packet
.m_nBodySize
= enc
- packet
.m_body
;
1995 RTMP_Log(RTMP_LOGDEBUG
, "%s, %d, pauseTime=%d", __FUNCTION__
, DoPause
, iTime
);
1996 return RTMP_SendPacket(r
, &packet
, TRUE
);
1999 int RTMP_Pause(RTMP
*r
, int DoPause
)
2002 r
->m_pauseStamp
= r
->m_mediaChannel
< r
->m_channelsAllocatedIn
?
2003 r
->m_channelTimestamp
[r
->m_mediaChannel
] : 0;
2004 return RTMP_SendPause(r
, DoPause
, r
->m_pauseStamp
);
2010 RTMP_SendSeek(RTMP
*r
, int iTime
)
2013 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2016 packet
.m_nChannel
= 0x08; /* video channel */
2017 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2018 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2019 packet
.m_nTimeStamp
= 0;
2020 packet
.m_nInfoField2
= 0;
2021 packet
.m_hasAbsTimestamp
= 0;
2022 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2024 enc
= packet
.m_body
;
2025 enc
= AMF_EncodeString(enc
, pend
, &av_seek
);
2026 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
2028 enc
= AMF_EncodeNumber(enc
, pend
, (double)iTime
);
2030 packet
.m_nBodySize
= enc
- packet
.m_body
;
2032 r
->m_read
.flags
|= RTMP_READ_SEEKING
;
2033 r
->m_read
.nResumeTS
= 0;
2035 return RTMP_SendPacket(r
, &packet
, TRUE
);
2039 RTMP_SendServerBW(RTMP
*r
)
2042 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2044 packet
.m_nChannel
= 0x02; /* control channel (invoke) */
2045 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2046 packet
.m_packetType
= RTMP_PACKET_TYPE_SERVER_BW
;
2047 packet
.m_nTimeStamp
= 0;
2048 packet
.m_nInfoField2
= 0;
2049 packet
.m_hasAbsTimestamp
= 0;
2050 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2052 packet
.m_nBodySize
= 4;
2054 AMF_EncodeInt32(packet
.m_body
, pend
, r
->m_nServerBW
);
2055 return RTMP_SendPacket(r
, &packet
, FALSE
);
2059 RTMP_SendClientBW(RTMP
*r
)
2062 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2064 packet
.m_nChannel
= 0x02; /* control channel (invoke) */
2065 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2066 packet
.m_packetType
= RTMP_PACKET_TYPE_CLIENT_BW
;
2067 packet
.m_nTimeStamp
= 0;
2068 packet
.m_nInfoField2
= 0;
2069 packet
.m_hasAbsTimestamp
= 0;
2070 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2072 packet
.m_nBodySize
= 5;
2074 AMF_EncodeInt32(packet
.m_body
, pend
, r
->m_nClientBW
);
2075 packet
.m_body
[4] = r
->m_nClientBW2
;
2076 return RTMP_SendPacket(r
, &packet
, FALSE
);
2080 SendBytesReceived(RTMP
*r
)
2083 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2085 packet
.m_nChannel
= 0x02; /* control channel (invoke) */
2086 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2087 packet
.m_packetType
= RTMP_PACKET_TYPE_BYTES_READ_REPORT
;
2088 packet
.m_nTimeStamp
= 0;
2089 packet
.m_nInfoField2
= 0;
2090 packet
.m_hasAbsTimestamp
= 0;
2091 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2093 packet
.m_nBodySize
= 4;
2095 AMF_EncodeInt32(packet
.m_body
, pend
, r
->m_nBytesIn
); /* hard coded for now */
2096 r
->m_nBytesInSent
= r
->m_nBytesIn
;
2098 /*RTMP_Log(RTMP_LOGDEBUG, "Send bytes report. 0x%x (%d bytes)", (unsigned int)m_nBytesIn, m_nBytesIn); */
2099 return RTMP_SendPacket(r
, &packet
, FALSE
);
2105 SendCheckBW(RTMP
*r
)
2108 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2111 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2112 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2113 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2114 packet
.m_nTimeStamp
= 0; /* RTMP_GetTime(); */
2115 packet
.m_nInfoField2
= 0;
2116 packet
.m_hasAbsTimestamp
= 0;
2117 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2119 enc
= packet
.m_body
;
2120 enc
= AMF_EncodeString(enc
, pend
, &av__checkbw
);
2121 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
2124 packet
.m_nBodySize
= enc
- packet
.m_body
;
2126 /* triggers _onbwcheck and eventually results in _onbwdone */
2127 return RTMP_SendPacket(r
, &packet
, FALSE
);
2133 SendCheckBWResult(RTMP
*r
, double txn
)
2136 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2139 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2140 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2141 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2142 packet
.m_nTimeStamp
= 0x16 * r
->m_nBWCheckCounter
; /* temp inc value. till we figure it out. */
2143 packet
.m_nInfoField2
= 0;
2144 packet
.m_hasAbsTimestamp
= 0;
2145 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2147 enc
= packet
.m_body
;
2148 enc
= AMF_EncodeString(enc
, pend
, &av__result
);
2149 enc
= AMF_EncodeNumber(enc
, pend
, txn
);
2151 enc
= AMF_EncodeNumber(enc
, pend
, (double)r
->m_nBWCheckCounter
++);
2153 packet
.m_nBodySize
= enc
- packet
.m_body
;
2155 return RTMP_SendPacket(r
, &packet
, FALSE
);
2162 SendPong(RTMP
*r
, double txn
)
2165 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2168 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2169 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2170 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2171 packet
.m_nTimeStamp
= 0x16 * r
->m_nBWCheckCounter
; /* temp inc value. till we figure it out. */
2172 packet
.m_nInfoField2
= 0;
2173 packet
.m_hasAbsTimestamp
= 0;
2174 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2176 enc
= packet
.m_body
;
2177 enc
= AMF_EncodeString(enc
, pend
, &av_pong
);
2178 enc
= AMF_EncodeNumber(enc
, pend
, txn
);
2181 packet
.m_nBodySize
= enc
- packet
.m_body
;
2183 return RTMP_SendPacket(r
, &packet
, FALSE
);
2192 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
2195 packet
.m_nChannel
= 0x08; /* we make 8 our stream channel */
2196 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2197 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2198 packet
.m_nTimeStamp
= 0;
2199 packet
.m_nInfoField2
= r
->m_stream_id
; /*0x01000000; */
2200 packet
.m_hasAbsTimestamp
= 0;
2201 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2203 enc
= packet
.m_body
;
2204 enc
= AMF_EncodeString(enc
, pend
, &av_play
);
2205 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
2208 RTMP_Log(RTMP_LOGDEBUG
, "%s, seekTime=%d, stopTime=%d, sending play: %s",
2209 __FUNCTION__
, r
->Link
.seekTime
, r
->Link
.stopTime
,
2210 r
->Link
.playpath
.av_val
);
2211 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
2215 /* Optional parameters start and len.
2217 * start: -2, -1, 0, positive number
2218 * -2: looks for a live stream, then a recorded stream,
2219 * if not found any open a live stream
2220 * -1: plays a live stream
2221 * >=0: plays a recorded streams from 'start' milliseconds
2223 if (r
->Link
.lFlags
& RTMP_LF_LIVE
)
2224 enc
= AMF_EncodeNumber(enc
, pend
, -1000.0);
2227 if (r
->Link
.seekTime
> 0.0)
2228 enc
= AMF_EncodeNumber(enc
, pend
, r
->Link
.seekTime
); /* resume from here */
2230 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 */
2235 /* len: -1, 0, positive number
2236 * -1: plays live or recorded stream to the end (default)
2237 * 0: plays a frame 'start' ms away from the beginning
2238 * >0: plays a live or recoded stream for 'len' milliseconds
2240 /*enc += EncodeNumber(enc, -1.0); */ /* len */
2241 if (r
->Link
.stopTime
)
2243 enc
= AMF_EncodeNumber(enc
, pend
, r
->Link
.stopTime
- r
->Link
.seekTime
);
2248 packet
.m_nBodySize
= enc
- packet
.m_body
;
2250 return RTMP_SendPacket(r
, &packet
, TRUE
);
2257 SendPlaylist(RTMP
*r
)
2260 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
2263 packet
.m_nChannel
= 0x08; /* we make 8 our stream channel */
2264 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2265 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2266 packet
.m_nTimeStamp
= 0;
2267 packet
.m_nInfoField2
= r
->m_stream_id
; /*0x01000000; */
2268 packet
.m_hasAbsTimestamp
= 0;
2269 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2271 enc
= packet
.m_body
;
2272 enc
= AMF_EncodeString(enc
, pend
, &av_set_playlist
);
2273 enc
= AMF_EncodeNumber(enc
, pend
, 0);
2275 *enc
++ = AMF_ECMA_ARRAY
;
2279 *enc
++ = AMF_OBJECT
;
2280 enc
= AMF_EncodeNamedString(enc
, pend
, &av_0
, &r
->Link
.playpath
);
2283 if (enc
+ 3 >= pend
)
2287 *enc
++ = AMF_OBJECT_END
;
2289 packet
.m_nBodySize
= enc
- packet
.m_body
;
2291 return RTMP_SendPacket(r
, &packet
, TRUE
);
2295 SendSecureTokenResponse(RTMP
*r
, AVal
*resp
)
2298 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
2301 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2302 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2303 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2304 packet
.m_nTimeStamp
= 0;
2305 packet
.m_nInfoField2
= 0;
2306 packet
.m_hasAbsTimestamp
= 0;
2307 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2309 enc
= packet
.m_body
;
2310 enc
= AMF_EncodeString(enc
, pend
, &av_secureTokenResponse
);
2311 enc
= AMF_EncodeNumber(enc
, pend
, 0.0);
2313 enc
= AMF_EncodeString(enc
, pend
, resp
);
2317 packet
.m_nBodySize
= enc
- packet
.m_body
;
2319 return RTMP_SendPacket(r
, &packet
, FALSE
);
2323 from http://jira.red5.org/confluence/display/docs/Ping:
2325 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.
2327 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.
2329 * 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.
2330 * type 1: Tell the stream to clear the playing buffer.
2331 * type 3: Buffer time of the client. The third parameter is the buffer time in millisecond.
2332 * type 4: Reset a stream. Used together with type 0 in the case of VOD. Often sent before type 0.
2333 * type 6: Ping the client from server. The second parameter is the current time.
2334 * type 7: Pong reply from client. The second parameter is the time the server sent with his ping request.
2335 * type 26: SWFVerification request
2336 * type 27: SWFVerification response
2339 RTMP_SendCtrl(RTMP
*r
, short nType
, unsigned int nObject
, unsigned int nTime
)
2342 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2346 RTMP_Log(RTMP_LOGDEBUG
, "sending ctrl. type: 0x%04x", (unsigned short)nType
);
2348 packet
.m_nChannel
= 0x02; /* control channel (ping) */
2349 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2350 packet
.m_packetType
= RTMP_PACKET_TYPE_CONTROL
;
2351 packet
.m_nTimeStamp
= 0; /* RTMP_GetTime(); */
2352 packet
.m_nInfoField2
= 0;
2353 packet
.m_hasAbsTimestamp
= 0;
2354 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2357 case 0x03: nSize
= 10; break; /* buffer time */
2358 case 0x1A: nSize
= 3; break; /* SWF verify request */
2359 case 0x1B: nSize
= 44; break; /* SWF verify response */
2360 default: nSize
= 6; break;
2363 packet
.m_nBodySize
= nSize
;
2365 buf
= packet
.m_body
;
2366 buf
= AMF_EncodeInt16(buf
, pend
, nType
);
2371 memcpy(buf
, r
->Link
.SWFVerificationResponse
, 42);
2372 RTMP_Log(RTMP_LOGDEBUG
, "Sending SWFVerification response: ");
2373 RTMP_LogHex(RTMP_LOGDEBUG
, (uint8_t *)packet
.m_body
, packet
.m_nBodySize
);
2376 else if (nType
== 0x1A)
2378 *buf
= nObject
& 0xff;
2383 buf
= AMF_EncodeInt32(buf
, pend
, nObject
);
2386 buf
= AMF_EncodeInt32(buf
, pend
, nTime
);
2389 return RTMP_SendPacket(r
, &packet
, FALSE
);
2393 AV_erase(RTMP_METHOD
*vals
, int *num
, int i
, int freeit
)
2396 free(vals
[i
].name
.av_val
);
2398 for (; i
< *num
; i
++)
2400 vals
[i
] = vals
[i
+ 1];
2402 vals
[i
].name
.av_val
= NULL
;
2403 vals
[i
].name
.av_len
= 0;
2408 RTMP_DropRequest(RTMP
*r
, int i
, int freeit
)
2410 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, freeit
);
2414 AV_queue(RTMP_METHOD
**vals
, int *num
, AVal
*av
, int txn
)
2418 *vals
= realloc(*vals
, (*num
+ 16) * sizeof(RTMP_METHOD
));
2419 tmp
= malloc(av
->av_len
+ 1);
2420 memcpy(tmp
, av
->av_val
, av
->av_len
);
2421 tmp
[av
->av_len
] = '\0';
2422 (*vals
)[*num
].num
= txn
;
2423 (*vals
)[*num
].name
.av_len
= av
->av_len
;
2424 (*vals
)[(*num
)++].name
.av_val
= tmp
;
2428 AV_clear(RTMP_METHOD
*vals
, int num
)
2431 for (i
= 0; i
< num
; i
++)
2432 free(vals
[i
].name
.av_val
);
2439 b64enc(const unsigned char *input
, int length
, char *output
, int maxsize
)
2442 size_t buf_size
= maxsize
;
2443 if(base64_encode((unsigned char *) output
, &buf_size
, input
, length
) == 0)
2445 output
[buf_size
] = '\0';
2450 RTMP_Log(RTMP_LOGDEBUG
, "%s, error", __FUNCTION__
);
2453 #elif defined(USE_GNUTLS)
2454 if (BASE64_ENCODE_RAW_LENGTH(length
) <= maxsize
)
2455 base64_encode_raw((uint8_t*) output
, length
, input
);
2458 RTMP_Log(RTMP_LOGDEBUG
, "%s, error", __FUNCTION__
);
2461 #else /* USE_OPENSSL */
2465 b64
= BIO_new(BIO_f_base64());
2466 bmem
= BIO_new(BIO_s_mem());
2467 b64
= BIO_push(b64
, bmem
);
2468 BIO_write(b64
, input
, length
);
2469 if (BIO_flush(b64
) == 1)
2471 BIO_get_mem_ptr(b64
, &bptr
);
2472 memcpy(output
, bptr
->data
, bptr
->length
-1);
2473 output
[bptr
->length
-1] = '\0';
2477 RTMP_Log(RTMP_LOGDEBUG
, "%s, error", __FUNCTION__
);
2486 #define MD5_CTX md5_context
2487 #define MD5_Init(ctx) md5_starts(ctx)
2488 #define MD5_Update(ctx,data,len) md5_update(ctx,(unsigned char *)data,len)
2489 #define MD5_Final(dig,ctx) md5_finish(ctx,dig)
2490 #elif defined(USE_GNUTLS)
2491 typedef struct md5_ctx MD5_CTX
;
2492 #define MD5_Init(ctx) md5_init(ctx)
2493 #define MD5_Update(ctx,data,len) md5_update(ctx,len,data)
2494 #define MD5_Final(dig,ctx) md5_digest(ctx,MD5_DIGEST_LENGTH,dig)
2498 static const AVal av_authmod_adobe
= AVC("authmod=adobe");
2499 static const AVal av_authmod_llnw
= AVC("authmod=llnw");
2501 static void hexenc(unsigned char *inbuf
, int len
, char *dst
)
2505 sprintf(ptr
, "%02x", *inbuf
++);
2512 AValChr(AVal
*av
, char c
)
2515 for (i
= 0; i
< av
->av_len
; i
++)
2516 if (av
->av_val
[i
] == c
)
2517 return &av
->av_val
[i
];
2522 PublisherAuth(RTMP
*r
, AVal
*description
)
2524 char *token_in
= NULL
;
2526 unsigned char md5sum_val
[MD5_DIGEST_LENGTH
+1];
2528 int challenge2_data
;
2529 #define RESPONSE_LEN 32
2530 #define CHALLENGE2_LEN 16
2531 #define SALTED2_LEN (32+8+8+8)
2532 #define B64DIGEST_LEN 24 /* 16 byte digest => 22 b64 chars + 2 chars padding */
2533 #define B64INT_LEN 8 /* 4 byte int => 6 b64 chars + 2 chars padding */
2534 #define HEXHASH_LEN (2*MD5_DIGEST_LENGTH)
2535 char response
[RESPONSE_LEN
];
2536 char challenge2
[CHALLENGE2_LEN
];
2537 char salted2
[SALTED2_LEN
];
2540 if (strstr(description
->av_val
, av_authmod_adobe
.av_val
) != NULL
)
2542 if(strstr(description
->av_val
, "code=403 need auth") != NULL
)
2544 if (strstr(r
->Link
.app
.av_val
, av_authmod_adobe
.av_val
) != NULL
) {
2545 RTMP_Log(RTMP_LOGERROR
, "%s, wrong pubUser & pubPasswd for publisher auth", __FUNCTION__
);
2547 } else if(r
->Link
.pubUser
.av_len
&& r
->Link
.pubPasswd
.av_len
) {
2548 pubToken
.av_val
= malloc(r
->Link
.pubUser
.av_len
+ av_authmod_adobe
.av_len
+ 8);
2549 pubToken
.av_len
= sprintf(pubToken
.av_val
, "?%s&user=%s",
2550 av_authmod_adobe
.av_val
,
2551 r
->Link
.pubUser
.av_val
);
2552 RTMP_Log(RTMP_LOGDEBUG
, "%s, pubToken1: %s", __FUNCTION__
, pubToken
.av_val
);
2554 RTMP_Log(RTMP_LOGERROR
, "%s, need to set pubUser & pubPasswd for publisher auth", __FUNCTION__
);
2558 else if((token_in
= strstr(description
->av_val
, "?reason=needauth")) != NULL
)
2560 char *par
, *val
= NULL
, *orig_ptr
;
2561 AVal user
, salt
, opaque
, challenge
, *aptr
= NULL
;
2563 challenge
.av_len
= 0;
2565 ptr
= orig_ptr
= strdup(token_in
);
2569 ptr
= strchr(par
, '&');
2573 val
= strchr(par
, '=');
2578 aptr
->av_len
= par
- aptr
->av_val
- 1;
2581 if (strcmp(par
, "user") == 0){
2584 } else if (strcmp(par
, "salt") == 0){
2587 } else if (strcmp(par
, "opaque") == 0){
2588 opaque
.av_val
= val
;
2590 } else if (strcmp(par
, "challenge") == 0){
2591 challenge
.av_val
= val
;
2595 RTMP_Log(RTMP_LOGDEBUG
, "%s, par:\"%s\" = val:\"%s\"", __FUNCTION__
, par
, val
);
2598 aptr
->av_len
= strlen(aptr
->av_val
);
2600 /* hash1 = base64enc(md5(user + _aodbeAuthSalt + password)) */
2602 MD5_Update(&md5ctx
, user
.av_val
, user
.av_len
);
2603 MD5_Update(&md5ctx
, salt
.av_val
, salt
.av_len
);
2604 MD5_Update(&md5ctx
, r
->Link
.pubPasswd
.av_val
, r
->Link
.pubPasswd
.av_len
);
2605 MD5_Final(md5sum_val
, &md5ctx
);
2606 RTMP_Log(RTMP_LOGDEBUG
, "%s, md5(%s%s%s) =>", __FUNCTION__
,
2607 user
.av_val
, salt
.av_val
, r
->Link
.pubPasswd
.av_val
);
2608 RTMP_LogHexString(RTMP_LOGDEBUG
, md5sum_val
, MD5_DIGEST_LENGTH
);
2610 b64enc(md5sum_val
, MD5_DIGEST_LENGTH
, salted2
, SALTED2_LEN
);
2611 RTMP_Log(RTMP_LOGDEBUG
, "%s, b64(md5_1) = %s", __FUNCTION__
, salted2
);
2613 challenge2_data
= rand();
2615 b64enc((unsigned char *) &challenge2_data
, sizeof(int), challenge2
, CHALLENGE2_LEN
);
2616 RTMP_Log(RTMP_LOGDEBUG
, "%s, b64(%d) = %s", __FUNCTION__
, challenge2_data
, challenge2
);
2619 MD5_Update(&md5ctx
, salted2
, B64DIGEST_LEN
);
2620 /* response = base64enc(md5(hash1 + opaque + challenge2)) */
2622 MD5_Update(&md5ctx
, opaque
.av_val
, opaque
.av_len
);
2623 else if (challenge
.av_len
)
2624 MD5_Update(&md5ctx
, challenge
.av_val
, challenge
.av_len
);
2625 MD5_Update(&md5ctx
, challenge2
, B64INT_LEN
);
2626 MD5_Final(md5sum_val
, &md5ctx
);
2628 RTMP_Log(RTMP_LOGDEBUG
, "%s, md5(%s%s%s) =>", __FUNCTION__
,
2629 salted2
, opaque
.av_len
? opaque
.av_val
: "", challenge2
);
2630 RTMP_LogHexString(RTMP_LOGDEBUG
, md5sum_val
, MD5_DIGEST_LENGTH
);
2632 b64enc(md5sum_val
, MD5_DIGEST_LENGTH
, response
, RESPONSE_LEN
);
2633 RTMP_Log(RTMP_LOGDEBUG
, "%s, b64(md5_2) = %s", __FUNCTION__
, response
);
2635 /* have all hashes, create auth token for the end of app */
2636 pubToken
.av_val
= malloc(32 + B64INT_LEN
+ B64DIGEST_LEN
+ opaque
.av_len
);
2637 pubToken
.av_len
= sprintf(pubToken
.av_val
,
2638 "&challenge=%s&response=%s&opaque=%s",
2641 opaque
.av_len
? opaque
.av_val
: "");
2642 RTMP_Log(RTMP_LOGDEBUG
, "%s, pubToken2: %s", __FUNCTION__
, pubToken
.av_val
);
2645 else if(strstr(description
->av_val
, "?reason=authfailed") != NULL
)
2647 RTMP_Log(RTMP_LOGERROR
, "%s, Authentication failed: wrong password", __FUNCTION__
);
2650 else if(strstr(description
->av_val
, "?reason=nosuchuser") != NULL
)
2652 RTMP_Log(RTMP_LOGERROR
, "%s, Authentication failed: no such user", __FUNCTION__
);
2657 RTMP_Log(RTMP_LOGERROR
, "%s, Authentication failed: unknown auth mode: %s",
2658 __FUNCTION__
, description
->av_val
);
2662 ptr
= malloc(r
->Link
.app
.av_len
+ pubToken
.av_len
);
2663 strncpy(ptr
, r
->Link
.app
.av_val
, r
->Link
.app
.av_len
);
2664 strncpy(ptr
+ r
->Link
.app
.av_len
, pubToken
.av_val
, pubToken
.av_len
);
2665 r
->Link
.app
.av_len
+= pubToken
.av_len
;
2666 if(r
->Link
.lFlags
& RTMP_LF_FAPU
)
2667 free(r
->Link
.app
.av_val
);
2668 r
->Link
.app
.av_val
= ptr
;
2670 ptr
= malloc(r
->Link
.tcUrl
.av_len
+ pubToken
.av_len
);
2671 strncpy(ptr
, r
->Link
.tcUrl
.av_val
, r
->Link
.tcUrl
.av_len
);
2672 strncpy(ptr
+ r
->Link
.tcUrl
.av_len
, pubToken
.av_val
, pubToken
.av_len
);
2673 r
->Link
.tcUrl
.av_len
+= pubToken
.av_len
;
2674 if(r
->Link
.lFlags
& RTMP_LF_FTCU
)
2675 free(r
->Link
.tcUrl
.av_val
);
2676 r
->Link
.tcUrl
.av_val
= ptr
;
2678 free(pubToken
.av_val
);
2679 r
->Link
.lFlags
|= RTMP_LF_FTCU
| RTMP_LF_FAPU
;
2681 RTMP_Log(RTMP_LOGDEBUG
, "%s, new app: %.*s tcUrl: %.*s playpath: %s", __FUNCTION__
,
2682 r
->Link
.app
.av_len
, r
->Link
.app
.av_val
,
2683 r
->Link
.tcUrl
.av_len
, r
->Link
.tcUrl
.av_val
,
2684 r
->Link
.playpath
.av_val
);
2686 else if (strstr(description
->av_val
, av_authmod_llnw
.av_val
) != NULL
)
2688 if(strstr(description
->av_val
, "code=403 need auth") != NULL
)
2690 /* This part seems to be the same for llnw and adobe */
2692 if (strstr(r
->Link
.app
.av_val
, av_authmod_llnw
.av_val
) != NULL
) {
2693 RTMP_Log(RTMP_LOGERROR
, "%s, wrong pubUser & pubPasswd for publisher auth", __FUNCTION__
);
2695 } else if(r
->Link
.pubUser
.av_len
&& r
->Link
.pubPasswd
.av_len
) {
2696 pubToken
.av_val
= malloc(r
->Link
.pubUser
.av_len
+ av_authmod_llnw
.av_len
+ 8);
2697 pubToken
.av_len
= sprintf(pubToken
.av_val
, "?%s&user=%s",
2698 av_authmod_llnw
.av_val
,
2699 r
->Link
.pubUser
.av_val
);
2700 RTMP_Log(RTMP_LOGDEBUG
, "%s, pubToken1: %s", __FUNCTION__
, pubToken
.av_val
);
2702 RTMP_Log(RTMP_LOGERROR
, "%s, need to set pubUser & pubPasswd for publisher auth", __FUNCTION__
);
2706 else if((token_in
= strstr(description
->av_val
, "?reason=needauth")) != NULL
)
2709 char *par
, *val
= NULL
;
2710 char hash1
[HEXHASH_LEN
+1], hash2
[HEXHASH_LEN
+1], hash3
[HEXHASH_LEN
+1];
2711 AVal user
, nonce
, *aptr
= NULL
;
2715 * Seems to be closely based on HTTP Digest Auth:
2716 * http://tools.ietf.org/html/rfc2617
2717 * http://en.wikipedia.org/wiki/Digest_access_authentication
2720 const char authmod
[] = "llnw";
2721 const char realm
[] = "live";
2722 const char method
[] = "publish";
2723 const char qop
[] = "auth";
2724 /* nc = 1..connection count (or rather, number of times cnonce has been reused) */
2726 /* nchex = hexenc(nc) (8 hex digits according to RFC 2617) */
2728 /* cnonce = hexenc(4 random bytes) (initialized on first connection) */
2731 ptr
= orig_ptr
= strdup(token_in
);
2732 /* Extract parameters (we need user and nonce) */
2736 ptr
= strchr(par
, '&');
2740 val
= strchr(par
, '=');
2745 aptr
->av_len
= par
- aptr
->av_val
- 1;
2748 if (strcmp(par
, "user") == 0){
2751 } else if (strcmp(par
, "nonce") == 0){
2756 RTMP_Log(RTMP_LOGDEBUG
, "%s, par:\"%s\" = val:\"%s\"", __FUNCTION__
, par
, val
);
2759 aptr
->av_len
= strlen(aptr
->av_val
);
2761 /* FIXME: handle case where user==NULL or nonce==NULL */
2763 sprintf(nchex
, "%08x", nc
);
2764 sprintf(cnonce
, "%08x", rand());
2766 /* hash1 = hexenc(md5(user + ":" + realm + ":" + password)) */
2768 MD5_Update(&md5ctx
, user
.av_val
, user
.av_len
);
2769 MD5_Update(&md5ctx
, ":", 1);
2770 MD5_Update(&md5ctx
, realm
, sizeof(realm
)-1);
2771 MD5_Update(&md5ctx
, ":", 1);
2772 MD5_Update(&md5ctx
, r
->Link
.pubPasswd
.av_val
, r
->Link
.pubPasswd
.av_len
);
2773 MD5_Final(md5sum_val
, &md5ctx
);
2774 RTMP_Log(RTMP_LOGDEBUG
, "%s, md5(%s:%s:%s) =>", __FUNCTION__
,
2775 user
.av_val
, realm
, r
->Link
.pubPasswd
.av_val
);
2776 RTMP_LogHexString(RTMP_LOGDEBUG
, md5sum_val
, MD5_DIGEST_LENGTH
);
2777 hexenc(md5sum_val
, MD5_DIGEST_LENGTH
, hash1
);
2779 /* hash2 = hexenc(md5(method + ":/" + app + "/" + appInstance)) */
2780 /* Extract appname + appinstance without query parameters */
2781 apptmp
= r
->Link
.app
;
2782 ptr
= AValChr(&apptmp
, '?');
2784 apptmp
.av_len
= ptr
- apptmp
.av_val
;
2787 MD5_Update(&md5ctx
, method
, sizeof(method
)-1);
2788 MD5_Update(&md5ctx
, ":/", 2);
2789 MD5_Update(&md5ctx
, apptmp
.av_val
, apptmp
.av_len
);
2790 if (!AValChr(&apptmp
, '/'))
2791 MD5_Update(&md5ctx
, "/_definst_", sizeof("/_definst_") - 1);
2792 MD5_Final(md5sum_val
, &md5ctx
);
2793 RTMP_Log(RTMP_LOGDEBUG
, "%s, md5(%s:/%.*s) =>", __FUNCTION__
,
2794 method
, apptmp
.av_len
, apptmp
.av_val
);
2795 RTMP_LogHexString(RTMP_LOGDEBUG
, md5sum_val
, MD5_DIGEST_LENGTH
);
2796 hexenc(md5sum_val
, MD5_DIGEST_LENGTH
, hash2
);
2798 /* hash3 = hexenc(md5(hash1 + ":" + nonce + ":" + nchex + ":" + cnonce + ":" + qop + ":" + hash2)) */
2800 MD5_Update(&md5ctx
, hash1
, HEXHASH_LEN
);
2801 MD5_Update(&md5ctx
, ":", 1);
2802 MD5_Update(&md5ctx
, nonce
.av_val
, nonce
.av_len
);
2803 MD5_Update(&md5ctx
, ":", 1);
2804 MD5_Update(&md5ctx
, nchex
, sizeof(nchex
)-1);
2805 MD5_Update(&md5ctx
, ":", 1);
2806 MD5_Update(&md5ctx
, cnonce
, sizeof(cnonce
)-1);
2807 MD5_Update(&md5ctx
, ":", 1);
2808 MD5_Update(&md5ctx
, qop
, sizeof(qop
)-1);
2809 MD5_Update(&md5ctx
, ":", 1);
2810 MD5_Update(&md5ctx
, hash2
, HEXHASH_LEN
);
2811 MD5_Final(md5sum_val
, &md5ctx
);
2812 RTMP_Log(RTMP_LOGDEBUG
, "%s, md5(%s:%s:%s:%s:%s:%s) =>", __FUNCTION__
,
2813 hash1
, nonce
.av_val
, nchex
, cnonce
, qop
, hash2
);
2814 RTMP_LogHexString(RTMP_LOGDEBUG
, md5sum_val
, MD5_DIGEST_LENGTH
);
2815 hexenc(md5sum_val
, MD5_DIGEST_LENGTH
, hash3
);
2817 /* pubToken = &authmod=<authmod>&user=<username>&nonce=<nonce>&cnonce=<cnonce>&nc=<nchex>&response=<hash3> */
2818 /* Append nonces and response to query string which already contains
2820 pubToken
.av_val
= malloc(64 + sizeof(authmod
)-1 + user
.av_len
+ nonce
.av_len
+ sizeof(cnonce
)-1 + sizeof(nchex
)-1 + HEXHASH_LEN
);
2821 sprintf(pubToken
.av_val
,
2822 "&nonce=%s&cnonce=%s&nc=%s&response=%s",
2823 nonce
.av_val
, cnonce
, nchex
, hash3
);
2824 pubToken
.av_len
= strlen(pubToken
.av_val
);
2825 RTMP_Log(RTMP_LOGDEBUG
, "%s, pubToken2: %s", __FUNCTION__
, pubToken
.av_val
);
2829 else if(strstr(description
->av_val
, "?reason=authfail") != NULL
)
2831 RTMP_Log(RTMP_LOGERROR
, "%s, Authentication failed", __FUNCTION__
);
2834 else if(strstr(description
->av_val
, "?reason=nosuchuser") != NULL
)
2836 RTMP_Log(RTMP_LOGERROR
, "%s, Authentication failed: no such user", __FUNCTION__
);
2841 RTMP_Log(RTMP_LOGERROR
, "%s, Authentication failed: unknown auth mode: %s",
2842 __FUNCTION__
, description
->av_val
);
2846 ptr
= malloc(r
->Link
.app
.av_len
+ pubToken
.av_len
);
2847 strncpy(ptr
, r
->Link
.app
.av_val
, r
->Link
.app
.av_len
);
2848 strncpy(ptr
+ r
->Link
.app
.av_len
, pubToken
.av_val
, pubToken
.av_len
);
2849 r
->Link
.app
.av_len
+= pubToken
.av_len
;
2850 if(r
->Link
.lFlags
& RTMP_LF_FAPU
)
2851 free(r
->Link
.app
.av_val
);
2852 r
->Link
.app
.av_val
= ptr
;
2854 ptr
= malloc(r
->Link
.tcUrl
.av_len
+ pubToken
.av_len
);
2855 strncpy(ptr
, r
->Link
.tcUrl
.av_val
, r
->Link
.tcUrl
.av_len
);
2856 strncpy(ptr
+ r
->Link
.tcUrl
.av_len
, pubToken
.av_val
, pubToken
.av_len
);
2857 r
->Link
.tcUrl
.av_len
+= pubToken
.av_len
;
2858 if(r
->Link
.lFlags
& RTMP_LF_FTCU
)
2859 free(r
->Link
.tcUrl
.av_val
);
2860 r
->Link
.tcUrl
.av_val
= ptr
;
2862 free(pubToken
.av_val
);
2863 r
->Link
.lFlags
|= RTMP_LF_FTCU
| RTMP_LF_FAPU
;
2865 RTMP_Log(RTMP_LOGDEBUG
, "%s, new app: %.*s tcUrl: %.*s playpath: %s", __FUNCTION__
,
2866 r
->Link
.app
.av_len
, r
->Link
.app
.av_val
,
2867 r
->Link
.tcUrl
.av_len
, r
->Link
.tcUrl
.av_val
,
2868 r
->Link
.playpath
.av_val
);
2880 SAVC(onFCSubscribe
);
2881 SAVC(onFCUnsubscribe
);
2890 SAVC(playlist_ready
);
2891 static const AVal av_NetStream_Failed
= AVC("NetStream.Failed");
2892 static const AVal av_NetStream_Play_Failed
= AVC("NetStream.Play.Failed");
2893 static const AVal av_NetStream_Play_StreamNotFound
=
2894 AVC("NetStream.Play.StreamNotFound");
2895 static const AVal av_NetConnection_Connect_InvalidApp
=
2896 AVC("NetConnection.Connect.InvalidApp");
2897 static const AVal av_NetStream_Play_Start
= AVC("NetStream.Play.Start");
2898 static const AVal av_NetStream_Play_Complete
= AVC("NetStream.Play.Complete");
2899 static const AVal av_NetStream_Play_Stop
= AVC("NetStream.Play.Stop");
2900 static const AVal av_NetStream_Seek_Notify
= AVC("NetStream.Seek.Notify");
2901 static const AVal av_NetStream_Pause_Notify
= AVC("NetStream.Pause.Notify");
2902 static const AVal av_NetStream_Play_PublishNotify
=
2903 AVC("NetStream.Play.PublishNotify");
2904 static const AVal av_NetStream_Play_UnpublishNotify
=
2905 AVC("NetStream.Play.UnpublishNotify");
2906 static const AVal av_NetStream_Publish_Start
= AVC("NetStream.Publish.Start");
2907 static const AVal av_NetConnection_Connect_Rejected
=
2908 AVC("NetConnection.Connect.Rejected");
2910 /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */
2912 HandleInvoke(RTMP
*r
, const char *body
, unsigned int nBodySize
)
2918 if (body
[0] != 0x02) /* make sure it is a string method name we start with */
2920 RTMP_Log(RTMP_LOGWARNING
, "%s, Sanity failed. no string method in invoke packet",
2925 nRes
= AMF_Decode(&obj
, body
, nBodySize
, FALSE
);
2928 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding invoke packet", __FUNCTION__
);
2933 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 0), &method
);
2934 txn
= AMFProp_GetNumber(AMF_GetProp(&obj
, NULL
, 1));
2935 RTMP_Log(RTMP_LOGDEBUG
, "%s, server invoking <%s>", __FUNCTION__
, method
.av_val
);
2937 if (AVMATCH(&method
, &av__result
))
2939 AVal methodInvoked
= {0};
2942 for (i
=0; i
<r
->m_numCalls
; i
++) {
2943 if (r
->m_methodCalls
[i
].num
== (int)txn
) {
2944 methodInvoked
= r
->m_methodCalls
[i
].name
;
2945 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, FALSE
);
2949 if (!methodInvoked
.av_val
) {
2950 RTMP_Log(RTMP_LOGDEBUG
, "%s, received result id %f without matching request",
2955 RTMP_Log(RTMP_LOGDEBUG
, "%s, received result for method call <%s>", __FUNCTION__
,
2956 methodInvoked
.av_val
);
2958 if (AVMATCH(&methodInvoked
, &av_connect
))
2960 if (r
->Link
.token
.av_len
)
2962 AMFObjectProperty p
;
2963 if (RTMP_FindFirstMatchingProperty(&obj
, &av_secureToken
, &p
))
2965 DecodeTEA(&r
->Link
.token
, &p
.p_vu
.p_aval
);
2966 SendSecureTokenResponse(r
, &p
.p_vu
.p_aval
);
2969 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
2971 SendReleaseStream(r
);
2976 RTMP_SendServerBW(r
);
2977 RTMP_SendCtrl(r
, 3, 0, 300);
2979 RTMP_SendCreateStream(r
);
2981 if (!(r
->Link
.protocol
& RTMP_FEATURE_WRITE
))
2983 /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */
2984 if (r
->Link
.usherToken
.av_len
)
2985 SendUsherToken(r
, &r
->Link
.usherToken
);
2986 /* Send the FCSubscribe if live stream or if subscribepath is set */
2987 if (r
->Link
.subscribepath
.av_len
)
2988 SendFCSubscribe(r
, &r
->Link
.subscribepath
);
2989 else if (r
->Link
.lFlags
& RTMP_LF_LIVE
)
2990 SendFCSubscribe(r
, &r
->Link
.playpath
);
2993 else if (AVMATCH(&methodInvoked
, &av_createStream
))
2995 r
->m_stream_id
= (int)AMFProp_GetNumber(AMF_GetProp(&obj
, NULL
, 3));
2997 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
3003 if (r
->Link
.lFlags
& RTMP_LF_PLST
)
3006 RTMP_SendCtrl(r
, 3, r
->m_stream_id
, r
->m_nBufferMS
);
3009 else if (AVMATCH(&methodInvoked
, &av_play
) ||
3010 AVMATCH(&methodInvoked
, &av_publish
))
3012 r
->m_bPlaying
= TRUE
;
3014 free(methodInvoked
.av_val
);
3016 else if (AVMATCH(&method
, &av_onBWDone
))
3018 if (!r
->m_nBWCheckCounter
)
3021 else if (AVMATCH(&method
, &av_onFCSubscribe
))
3023 /* SendOnFCSubscribe(); */
3025 else if (AVMATCH(&method
, &av_onFCUnsubscribe
))
3030 else if (AVMATCH(&method
, &av_ping
))
3034 else if (AVMATCH(&method
, &av__onbwcheck
))
3036 SendCheckBWResult(r
, txn
);
3038 else if (AVMATCH(&method
, &av__onbwdone
))
3041 for (i
= 0; i
< r
->m_numCalls
; i
++)
3042 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av__checkbw
))
3044 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
3048 else if (AVMATCH(&method
, &av__error
))
3051 AVal methodInvoked
= {0};
3054 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
3056 for (i
=0; i
<r
->m_numCalls
; i
++)
3058 if (r
->m_methodCalls
[i
].num
== txn
)
3060 methodInvoked
= r
->m_methodCalls
[i
].name
;
3061 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, FALSE
);
3065 if (!methodInvoked
.av_val
)
3067 RTMP_Log(RTMP_LOGDEBUG
, "%s, received result id %f without matching request",
3072 RTMP_Log(RTMP_LOGDEBUG
, "%s, received error for method call <%s>", __FUNCTION__
,
3073 methodInvoked
.av_val
);
3075 if (AVMATCH(&methodInvoked
, &av_connect
))
3078 AVal code
, level
, description
;
3079 AMFProp_GetObject(AMF_GetProp(&obj
, NULL
, 3), &obj2
);
3080 AMFProp_GetString(AMF_GetProp(&obj2
, &av_code
, -1), &code
);
3081 AMFProp_GetString(AMF_GetProp(&obj2
, &av_level
, -1), &level
);
3082 AMFProp_GetString(AMF_GetProp(&obj2
, &av_description
, -1), &description
);
3083 RTMP_Log(RTMP_LOGDEBUG
, "%s, error description: %s", __FUNCTION__
, description
.av_val
);
3084 /* if PublisherAuth returns 1, then reconnect */
3085 if (PublisherAuth(r
, &description
) == 1)
3087 CloseInternal(r
, 1);
3088 if (!RTMP_Connect(r
, NULL
) || !RTMP_ConnectStream(r
, 0))
3095 RTMP_Log(RTMP_LOGERROR
, "rtmp server sent error");
3097 free(methodInvoked
.av_val
);
3099 RTMP_Log(RTMP_LOGERROR
, "rtmp server sent error");
3102 else if (AVMATCH(&method
, &av_close
))
3104 RTMP_Log(RTMP_LOGERROR
, "rtmp server requested close");
3107 else if (AVMATCH(&method
, &av_onStatus
))
3111 AMFProp_GetObject(AMF_GetProp(&obj
, NULL
, 3), &obj2
);
3112 AMFProp_GetString(AMF_GetProp(&obj2
, &av_code
, -1), &code
);
3113 AMFProp_GetString(AMF_GetProp(&obj2
, &av_level
, -1), &level
);
3115 RTMP_Log(RTMP_LOGDEBUG
, "%s, onStatus: %s", __FUNCTION__
, code
.av_val
);
3116 if (AVMATCH(&code
, &av_NetStream_Failed
)
3117 || AVMATCH(&code
, &av_NetStream_Play_Failed
)
3118 || AVMATCH(&code
, &av_NetStream_Play_StreamNotFound
)
3119 || AVMATCH(&code
, &av_NetConnection_Connect_InvalidApp
))
3121 r
->m_stream_id
= -1;
3123 RTMP_Log(RTMP_LOGERROR
, "Closing connection: %s", code
.av_val
);
3126 else if (AVMATCH(&code
, &av_NetStream_Play_Start
)
3127 || AVMATCH(&code
, &av_NetStream_Play_PublishNotify
))
3130 r
->m_bPlaying
= TRUE
;
3131 for (i
= 0; i
< r
->m_numCalls
; i
++)
3133 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av_play
))
3135 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
3141 else if (AVMATCH(&code
, &av_NetStream_Publish_Start
))
3144 r
->m_bPlaying
= TRUE
;
3145 for (i
= 0; i
< r
->m_numCalls
; i
++)
3147 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av_publish
))
3149 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
3155 /* Return 1 if this is a Play.Complete or Play.Stop */
3156 else if (AVMATCH(&code
, &av_NetStream_Play_Complete
)
3157 || AVMATCH(&code
, &av_NetStream_Play_Stop
)
3158 || AVMATCH(&code
, &av_NetStream_Play_UnpublishNotify
))
3164 else if (AVMATCH(&code
, &av_NetStream_Seek_Notify
))
3166 r
->m_read
.flags
&= ~RTMP_READ_SEEKING
;
3169 else if (AVMATCH(&code
, &av_NetStream_Pause_Notify
))
3171 if (r
->m_pausing
== 1 || r
->m_pausing
== 2)
3173 RTMP_SendPause(r
, FALSE
, r
->m_pauseStamp
);
3178 else if (AVMATCH(&method
, &av_playlist_ready
))
3181 for (i
= 0; i
< r
->m_numCalls
; i
++)
3183 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av_set_playlist
))
3185 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
3200 RTMP_FindFirstMatchingProperty(AMFObject
*obj
, const AVal
*name
,
3201 AMFObjectProperty
* p
)
3204 /* this is a small object search to locate the "duration" property */
3205 for (n
= 0; n
< obj
->o_num
; n
++)
3207 AMFObjectProperty
*prop
= AMF_GetProp(obj
, NULL
, n
);
3209 if (AVMATCH(&prop
->p_name
, name
))
3211 memcpy(p
, prop
, sizeof(*prop
));
3215 if (prop
->p_type
== AMF_OBJECT
|| prop
->p_type
== AMF_ECMA_ARRAY
)
3217 if (RTMP_FindFirstMatchingProperty(&prop
->p_vu
.p_object
, name
, p
))
3224 /* Like above, but only check if name is a prefix of property */
3226 RTMP_FindPrefixProperty(AMFObject
*obj
, const AVal
*name
,
3227 AMFObjectProperty
* p
)
3230 for (n
= 0; n
< obj
->o_num
; n
++)
3232 AMFObjectProperty
*prop
= AMF_GetProp(obj
, NULL
, n
);
3234 if (prop
->p_name
.av_len
> name
->av_len
&&
3235 !memcmp(prop
->p_name
.av_val
, name
->av_val
, name
->av_len
))
3237 memcpy(p
, prop
, sizeof(*prop
));
3241 if (prop
->p_type
== AMF_OBJECT
)
3243 if (RTMP_FindPrefixProperty(&prop
->p_vu
.p_object
, name
, p
))
3251 DumpMetaData(AMFObject
*obj
)
3253 AMFObjectProperty
*prop
;
3255 for (n
= 0; n
< obj
->o_num
; n
++)
3258 prop
= AMF_GetProp(obj
, NULL
, n
);
3259 switch (prop
->p_type
)
3262 case AMF_ECMA_ARRAY
:
3263 case AMF_STRICT_ARRAY
:
3264 if (prop
->p_name
.av_len
)
3265 RTMP_Log(RTMP_LOGINFO
, "%.*s:", prop
->p_name
.av_len
, prop
->p_name
.av_val
);
3266 DumpMetaData(&prop
->p_vu
.p_object
);
3269 snprintf(str
, 255, "%.2f", prop
->p_vu
.p_number
);
3272 snprintf(str
, 255, "%s",
3273 prop
->p_vu
.p_number
!= 0. ? "TRUE" : "FALSE");
3276 len
= snprintf(str
, 255, "%.*s", prop
->p_vu
.p_aval
.av_len
,
3277 prop
->p_vu
.p_aval
.av_val
);
3278 if (len
>= 1 && str
[len
-1] == '\n')
3282 snprintf(str
, 255, "timestamp:%.2f", prop
->p_vu
.p_number
);
3285 snprintf(str
, 255, "INVALID TYPE 0x%02x",
3286 (unsigned char)prop
->p_type
);
3288 if (str
[0] && prop
->p_name
.av_len
)
3290 RTMP_Log(RTMP_LOGINFO
, " %-22.*s%s", prop
->p_name
.av_len
,
3291 prop
->p_name
.av_val
, str
);
3303 HandleMetadata(RTMP
*r
, char *body
, unsigned int len
)
3305 /* allright we get some info here, so parse it and print it */
3306 /* also keep duration or filesize to make a nice progress bar */
3312 int nRes
= AMF_Decode(&obj
, body
, len
, FALSE
);
3315 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding meta data packet", __FUNCTION__
);
3320 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 0), &metastring
);
3322 if (AVMATCH(&metastring
, &av_onMetaData
))
3324 AMFObjectProperty prop
;
3326 RTMP_Log(RTMP_LOGINFO
, "Metadata:");
3328 if (RTMP_FindFirstMatchingProperty(&obj
, &av_duration
, &prop
))
3330 r
->m_fDuration
= prop
.p_vu
.p_number
;
3331 /*RTMP_Log(RTMP_LOGDEBUG, "Set duration: %.2f", m_fDuration); */
3333 /* Search for audio or video tags */
3334 if (RTMP_FindPrefixProperty(&obj
, &av_video
, &prop
))
3335 r
->m_read
.dataType
|= 1;
3336 if (RTMP_FindPrefixProperty(&obj
, &av_audio
, &prop
))
3337 r
->m_read
.dataType
|= 4;
3345 HandleChangeChunkSize(RTMP
*r
, const RTMPPacket
*packet
)
3347 if (packet
->m_nBodySize
>= 4)
3349 r
->m_inChunkSize
= AMF_DecodeInt32(packet
->m_body
);
3350 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: chunk size change to %d", __FUNCTION__
,
3356 HandleAudio(RTMP
*r
, const RTMPPacket
*packet
)
3361 HandleVideo(RTMP
*r
, const RTMPPacket
*packet
)
3366 HandleCtrl(RTMP
*r
, const RTMPPacket
*packet
)
3370 if (packet
->m_body
&& packet
->m_nBodySize
>= 2)
3371 nType
= AMF_DecodeInt16(packet
->m_body
);
3372 RTMP_Log(RTMP_LOGDEBUG
, "%s, received ctrl. type: %d, len: %d", __FUNCTION__
, nType
,
3373 packet
->m_nBodySize
);
3374 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
3376 if (packet
->m_nBodySize
>= 6)
3381 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3382 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream Begin %d", __FUNCTION__
, tmp
);
3386 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3387 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream EOF %d", __FUNCTION__
, tmp
);
3388 if (r
->m_pausing
== 1)
3393 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3394 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream Dry %d", __FUNCTION__
, tmp
);
3398 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3399 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream IsRecorded %d", __FUNCTION__
, tmp
);
3402 case 6: /* server ping. reply with pong. */
3403 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3404 RTMP_Log(RTMP_LOGDEBUG
, "%s, Ping %d", __FUNCTION__
, tmp
);
3405 RTMP_SendCtrl(r
, 0x07, tmp
, 0);
3408 /* FMS 3.5 servers send the following two controls to let the client
3409 * know when the server has sent a complete buffer. I.e., when the
3410 * server has sent an amount of data equal to m_nBufferMS in duration.
3411 * The server meters its output so that data arrives at the client
3412 * in realtime and no faster.
3414 * The rtmpdump program tries to set m_nBufferMS as large as
3415 * possible, to force the server to send data as fast as possible.
3416 * In practice, the server appears to cap this at about 1 hour's
3417 * worth of data. After the server has sent a complete buffer, and
3418 * sends this BufferEmpty message, it will wait until the play
3419 * duration of that buffer has passed before sending a new buffer.
3420 * The BufferReady message will be sent when the new buffer starts.
3421 * (There is no BufferReady message for the very first buffer;
3422 * presumably the Stream Begin message is sufficient for that
3425 * If the network speed is much faster than the data bitrate, then
3426 * there may be long delays between the end of one buffer and the
3427 * start of the next.
3429 * Since usually the network allows data to be sent at
3430 * faster than realtime, and rtmpdump wants to download the data
3431 * as fast as possible, we use this RTMP_LF_BUFX hack: when we
3432 * get the BufferEmpty message, we send a Pause followed by an
3433 * Unpause. This causes the server to send the next buffer immediately
3434 * instead of waiting for the full duration to elapse. (That's
3435 * also the purpose of the ToggleStream function, which rtmpdump
3436 * calls if we get a read timeout.)
3438 * Media player apps don't need this hack since they are just
3439 * going to play the data in realtime anyway. It also doesn't work
3440 * for live streams since they obviously can only be sent in
3441 * realtime. And it's all moot if the network speed is actually
3442 * slower than the media bitrate.
3445 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3446 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream BufferEmpty %d", __FUNCTION__
, tmp
);
3447 if (!(r
->Link
.lFlags
& RTMP_LF_BUFX
))
3451 r
->m_pauseStamp
= r
->m_mediaChannel
< r
->m_channelsAllocatedIn
?
3452 r
->m_channelTimestamp
[r
->m_mediaChannel
] : 0;
3453 RTMP_SendPause(r
, TRUE
, r
->m_pauseStamp
);
3456 else if (r
->m_pausing
== 2)
3458 RTMP_SendPause(r
, FALSE
, r
->m_pauseStamp
);
3464 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3465 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream BufferReady %d", __FUNCTION__
, tmp
);
3469 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
3470 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream xx %d", __FUNCTION__
, tmp
);
3478 RTMP_Log(RTMP_LOGDEBUG
, "%s, SWFVerification ping received: ", __FUNCTION__
);
3479 if (packet
->m_nBodySize
> 2 && packet
->m_body
[2] > 0x01)
3481 RTMP_Log(RTMP_LOGERROR
,
3482 "%s: SWFVerification Type %d request not supported! Patches welcome...",
3483 __FUNCTION__
, packet
->m_body
[2]);
3486 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
3488 /* respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied */
3489 else if (r
->Link
.SWFSize
)
3491 RTMP_SendCtrl(r
, 0x1B, 0, 0);
3495 RTMP_Log(RTMP_LOGERROR
,
3496 "%s: Ignoring SWFVerification request, use --swfVfy!",
3500 RTMP_Log(RTMP_LOGERROR
,
3501 "%s: Ignoring SWFVerification request, no CRYPTO support!",
3508 HandleServerBW(RTMP
*r
, const RTMPPacket
*packet
)
3510 r
->m_nServerBW
= AMF_DecodeInt32(packet
->m_body
);
3511 RTMP_Log(RTMP_LOGDEBUG
, "%s: server BW = %d", __FUNCTION__
, r
->m_nServerBW
);
3515 HandleClientBW(RTMP
*r
, const RTMPPacket
*packet
)
3517 r
->m_nClientBW
= AMF_DecodeInt32(packet
->m_body
);
3518 if (packet
->m_nBodySize
> 4)
3519 r
->m_nClientBW2
= packet
->m_body
[4];
3521 r
->m_nClientBW2
= -1;
3522 RTMP_Log(RTMP_LOGDEBUG
, "%s: client BW = %d %d", __FUNCTION__
, r
->m_nClientBW
,
3527 DecodeInt32LE(const char *data
)
3529 unsigned char *c
= (unsigned char *)data
;
3532 val
= (c
[3] << 24) | (c
[2] << 16) | (c
[1] << 8) | c
[0];
3537 EncodeInt32LE(char *output
, int nVal
)
3550 RTMP_ReadPacket(RTMP
*r
, RTMPPacket
*packet
)
3552 uint8_t hbuf
[RTMP_MAX_HEADER_SIZE
] = { 0 };
3553 char *header
= (char *)hbuf
;
3554 int nSize
, hSize
, nToRead
, nChunk
;
3555 int didAlloc
= FALSE
;
3556 int extendedTimestamp
;
3558 RTMP_Log(RTMP_LOGDEBUG2
, "%s: fd=%d", __FUNCTION__
, r
->m_sb
.sb_socket
);
3560 if (ReadN(r
, (char *)hbuf
, 1) == 0)
3562 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header", __FUNCTION__
);
3566 packet
->m_headerType
= (hbuf
[0] & 0xc0) >> 6;
3567 packet
->m_nChannel
= (hbuf
[0] & 0x3f);
3569 if (packet
->m_nChannel
== 0)
3571 if (ReadN(r
, (char *)&hbuf
[1], 1) != 1)
3573 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header 2nd byte",
3577 packet
->m_nChannel
= hbuf
[1];
3578 packet
->m_nChannel
+= 64;
3581 else if (packet
->m_nChannel
== 1)
3584 if (ReadN(r
, (char *)&hbuf
[1], 2) != 2)
3586 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header 3nd byte",
3590 tmp
= (hbuf
[2] << 8) + hbuf
[1];
3591 packet
->m_nChannel
= tmp
+ 64;
3592 RTMP_Log(RTMP_LOGDEBUG
, "%s, m_nChannel: %0x", __FUNCTION__
, packet
->m_nChannel
);
3596 nSize
= packetSize
[packet
->m_headerType
];
3598 if (packet
->m_nChannel
>= r
->m_channelsAllocatedIn
)
3600 int n
= packet
->m_nChannel
+ 10;
3601 int *timestamp
= realloc(r
->m_channelTimestamp
, sizeof(int) * n
);
3602 RTMPPacket
**packets
= realloc(r
->m_vecChannelsIn
, sizeof(RTMPPacket
*) * n
);
3604 free(r
->m_channelTimestamp
);
3606 free(r
->m_vecChannelsIn
);
3607 r
->m_channelTimestamp
= timestamp
;
3608 r
->m_vecChannelsIn
= packets
;
3609 if (!timestamp
|| !packets
) {
3610 r
->m_channelsAllocatedIn
= 0;
3613 memset(r
->m_channelTimestamp
+ r
->m_channelsAllocatedIn
, 0, sizeof(int) * (n
- r
->m_channelsAllocatedIn
));
3614 memset(r
->m_vecChannelsIn
+ r
->m_channelsAllocatedIn
, 0, sizeof(RTMPPacket
*) * (n
- r
->m_channelsAllocatedIn
));
3615 r
->m_channelsAllocatedIn
= n
;
3618 if (nSize
== RTMP_LARGE_HEADER_SIZE
) /* if we get a full header the timestamp is absolute */
3619 packet
->m_hasAbsTimestamp
= TRUE
;
3621 else if (nSize
< RTMP_LARGE_HEADER_SIZE
)
3622 { /* using values from the last message of this channel */
3623 if (r
->m_vecChannelsIn
[packet
->m_nChannel
])
3624 memcpy(packet
, r
->m_vecChannelsIn
[packet
->m_nChannel
],
3625 sizeof(RTMPPacket
));
3630 if (nSize
> 0 && ReadN(r
, header
, nSize
) != nSize
)
3632 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header. type: %x",
3633 __FUNCTION__
, (unsigned int)hbuf
[0]);
3637 hSize
= nSize
+ (header
- (char *)hbuf
);
3641 packet
->m_nTimeStamp
= AMF_DecodeInt24(header
);
3643 /*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); */
3647 packet
->m_nBodySize
= AMF_DecodeInt24(header
+ 3);
3648 packet
->m_nBytesRead
= 0;
3652 packet
->m_packetType
= header
[6];
3655 packet
->m_nInfoField2
= DecodeInt32LE(header
+ 7);
3660 extendedTimestamp
= packet
->m_nTimeStamp
== 0xffffff;
3661 if (extendedTimestamp
)
3663 if (ReadN(r
, header
+ nSize
, 4) != 4)
3665 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read extended timestamp",
3669 packet
->m_nTimeStamp
= AMF_DecodeInt32(header
+ nSize
);
3673 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)hbuf
, hSize
);
3675 if (packet
->m_nBodySize
> 0 && packet
->m_body
== NULL
)
3677 if (!RTMPPacket_Alloc(packet
, packet
->m_nBodySize
))
3679 RTMP_Log(RTMP_LOGDEBUG
, "%s, failed to allocate packet", __FUNCTION__
);
3683 packet
->m_headerType
= (hbuf
[0] & 0xc0) >> 6;
3686 nToRead
= packet
->m_nBodySize
- packet
->m_nBytesRead
;
3687 nChunk
= r
->m_inChunkSize
;
3688 if (nToRead
< nChunk
)
3691 /* Does the caller want the raw chunk? */
3692 if (packet
->m_chunk
)
3694 packet
->m_chunk
->c_headerSize
= hSize
;
3695 memcpy(packet
->m_chunk
->c_header
, hbuf
, hSize
);
3696 packet
->m_chunk
->c_chunk
= packet
->m_body
+ packet
->m_nBytesRead
;
3697 packet
->m_chunk
->c_chunkSize
= nChunk
;
3700 if (ReadN(r
, packet
->m_body
+ packet
->m_nBytesRead
, nChunk
) != nChunk
)
3702 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet body. len: %u",
3703 __FUNCTION__
, packet
->m_nBodySize
);
3707 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)packet
->m_body
+ packet
->m_nBytesRead
, nChunk
);
3709 packet
->m_nBytesRead
+= nChunk
;
3711 /* keep the packet as ref for other packets on this channel */
3712 if (!r
->m_vecChannelsIn
[packet
->m_nChannel
])
3713 r
->m_vecChannelsIn
[packet
->m_nChannel
] = malloc(sizeof(RTMPPacket
));
3714 memcpy(r
->m_vecChannelsIn
[packet
->m_nChannel
], packet
, sizeof(RTMPPacket
));
3715 if (extendedTimestamp
)
3717 r
->m_vecChannelsIn
[packet
->m_nChannel
]->m_nTimeStamp
= 0xffffff;
3720 if (RTMPPacket_IsReady(packet
))
3722 /* make packet's timestamp absolute */
3723 if (!packet
->m_hasAbsTimestamp
)
3724 packet
->m_nTimeStamp
+= r
->m_channelTimestamp
[packet
->m_nChannel
]; /* timestamps seem to be always relative!! */
3726 r
->m_channelTimestamp
[packet
->m_nChannel
] = packet
->m_nTimeStamp
;
3728 /* reset the data from the stored packet. we keep the header since we may use it later if a new packet for this channel */
3729 /* arrives and requests to re-use some info (small packet header) */
3730 r
->m_vecChannelsIn
[packet
->m_nChannel
]->m_body
= NULL
;
3731 r
->m_vecChannelsIn
[packet
->m_nChannel
]->m_nBytesRead
= 0;
3732 r
->m_vecChannelsIn
[packet
->m_nChannel
]->m_hasAbsTimestamp
= FALSE
; /* can only be false if we reuse header */
3736 packet
->m_body
= NULL
; /* so it won't be erased on free */
3744 HandShake(RTMP
*r
, int FP9HandShake
)
3747 uint32_t uptime
, suptime
;
3750 char clientbuf
[RTMP_SIG_SIZE
+ 1], *clientsig
= clientbuf
+ 1;
3751 char serversig
[RTMP_SIG_SIZE
];
3753 clientbuf
[0] = 0x03; /* not encrypted */
3755 uptime
= htonl(RTMP_GetTime());
3756 memcpy(clientsig
, &uptime
, 4);
3758 memset(&clientsig
[4], 0, 4);
3761 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3762 clientsig
[i
] = 0xff;
3764 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3765 clientsig
[i
] = (char)(rand() % 256);
3768 if (!WriteN(r
, clientbuf
, RTMP_SIG_SIZE
+ 1))
3771 if (ReadN(r
, &type
, 1) != 1) /* 0x03 or 0x06 */
3774 RTMP_Log(RTMP_LOGDEBUG
, "%s: Type Answer : %02X", __FUNCTION__
, type
);
3776 if (type
!= clientbuf
[0])
3777 RTMP_Log(RTMP_LOGWARNING
, "%s: Type mismatch: client sent %d, server answered %d",
3778 __FUNCTION__
, clientbuf
[0], type
);
3780 if (ReadN(r
, serversig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3783 /* decode server response */
3785 memcpy(&suptime
, serversig
, 4);
3786 suptime
= ntohl(suptime
);
3788 RTMP_Log(RTMP_LOGDEBUG
, "%s: Server Uptime : %d", __FUNCTION__
, suptime
);
3789 RTMP_Log(RTMP_LOGDEBUG
, "%s: FMS Version : %d.%d.%d.%d", __FUNCTION__
,
3790 serversig
[4], serversig
[5], serversig
[6], serversig
[7]);
3792 /* 2nd part of handshake */
3793 if (!WriteN(r
, serversig
, RTMP_SIG_SIZE
))
3796 if (ReadN(r
, serversig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3799 bMatch
= (memcmp(serversig
, clientsig
, RTMP_SIG_SIZE
) == 0);
3802 RTMP_Log(RTMP_LOGWARNING
, "%s, client signature does not match!", __FUNCTION__
);
3811 char serverbuf
[RTMP_SIG_SIZE
+ 1], *serversig
= serverbuf
+ 1;
3812 char clientsig
[RTMP_SIG_SIZE
];
3816 if (ReadN(r
, serverbuf
, 1) != 1) /* 0x03 or 0x06 */
3819 RTMP_Log(RTMP_LOGDEBUG
, "%s: Type Request : %02X", __FUNCTION__
, serverbuf
[0]);
3821 if (serverbuf
[0] != 3)
3823 RTMP_Log(RTMP_LOGERROR
, "%s: Type unknown: client sent %02X",
3824 __FUNCTION__
, serverbuf
[0]);
3828 uptime
= htonl(RTMP_GetTime());
3829 memcpy(serversig
, &uptime
, 4);
3831 memset(&serversig
[4], 0, 4);
3833 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3834 serversig
[i
] = 0xff;
3836 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3837 serversig
[i
] = (char)(rand() % 256);
3840 if (!WriteN(r
, serverbuf
, RTMP_SIG_SIZE
+ 1))
3843 if (ReadN(r
, clientsig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3846 /* decode client response */
3848 memcpy(&uptime
, clientsig
, 4);
3849 uptime
= ntohl(uptime
);
3851 RTMP_Log(RTMP_LOGDEBUG
, "%s: Client Uptime : %d", __FUNCTION__
, uptime
);
3852 RTMP_Log(RTMP_LOGDEBUG
, "%s: Player Version: %d.%d.%d.%d", __FUNCTION__
,
3853 clientsig
[4], clientsig
[5], clientsig
[6], clientsig
[7]);
3855 /* 2nd part of handshake */
3856 if (!WriteN(r
, clientsig
, RTMP_SIG_SIZE
))
3859 if (ReadN(r
, clientsig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3862 bMatch
= (memcmp(serversig
, clientsig
, RTMP_SIG_SIZE
) == 0);
3865 RTMP_Log(RTMP_LOGWARNING
, "%s, client signature does not match!", __FUNCTION__
);
3872 RTMP_SendChunk(RTMP
*r
, RTMPChunk
*chunk
)
3875 char hbuf
[RTMP_MAX_HEADER_SIZE
];
3877 RTMP_Log(RTMP_LOGDEBUG2
, "%s: fd=%d, size=%d", __FUNCTION__
, r
->m_sb
.sb_socket
,
3878 chunk
->c_chunkSize
);
3879 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)chunk
->c_header
, chunk
->c_headerSize
);
3880 if (chunk
->c_chunkSize
)
3882 char *ptr
= chunk
->c_chunk
- chunk
->c_headerSize
;
3883 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)chunk
->c_chunk
, chunk
->c_chunkSize
);
3884 /* save header bytes we're about to overwrite */
3885 memcpy(hbuf
, ptr
, chunk
->c_headerSize
);
3886 memcpy(ptr
, chunk
->c_header
, chunk
->c_headerSize
);
3887 wrote
= WriteN(r
, ptr
, chunk
->c_headerSize
+ chunk
->c_chunkSize
);
3888 memcpy(ptr
, hbuf
, chunk
->c_headerSize
);
3891 wrote
= WriteN(r
, chunk
->c_header
, chunk
->c_headerSize
);
3896 RTMP_SendPacket(RTMP
*r
, RTMPPacket
*packet
, int queue
)
3898 const RTMPPacket
*prevPacket
;
3902 char *header
, *hptr
, *hend
, hbuf
[RTMP_MAX_HEADER_SIZE
], c
;
3904 char *buffer
, *tbuf
= NULL
, *toff
= NULL
;
3908 if (packet
->m_nChannel
>= r
->m_channelsAllocatedOut
)
3910 int n
= packet
->m_nChannel
+ 10;
3911 RTMPPacket
**packets
= realloc(r
->m_vecChannelsOut
, sizeof(RTMPPacket
*) * n
);
3913 free(r
->m_vecChannelsOut
);
3914 r
->m_vecChannelsOut
= NULL
;
3915 r
->m_channelsAllocatedOut
= 0;
3918 r
->m_vecChannelsOut
= packets
;
3919 memset(r
->m_vecChannelsOut
+ r
->m_channelsAllocatedOut
, 0, sizeof(RTMPPacket
*) * (n
- r
->m_channelsAllocatedOut
));
3920 r
->m_channelsAllocatedOut
= n
;
3923 prevPacket
= r
->m_vecChannelsOut
[packet
->m_nChannel
];
3924 if (prevPacket
&& packet
->m_headerType
!= RTMP_PACKET_SIZE_LARGE
)
3926 /* compress a bit by using the prev packet's attributes */
3927 if (prevPacket
->m_nBodySize
== packet
->m_nBodySize
3928 && prevPacket
->m_packetType
== packet
->m_packetType
3929 && packet
->m_headerType
== RTMP_PACKET_SIZE_MEDIUM
)
3930 packet
->m_headerType
= RTMP_PACKET_SIZE_SMALL
;
3932 if (prevPacket
->m_nTimeStamp
== packet
->m_nTimeStamp
3933 && packet
->m_headerType
== RTMP_PACKET_SIZE_SMALL
)
3934 packet
->m_headerType
= RTMP_PACKET_SIZE_MINIMUM
;
3935 last
= prevPacket
->m_nTimeStamp
;
3938 if (packet
->m_headerType
> 3) /* sanity */
3940 RTMP_Log(RTMP_LOGERROR
, "sanity failed!! trying to send header of type: 0x%02x.",
3941 (unsigned char)packet
->m_headerType
);
3945 nSize
= packetSize
[packet
->m_headerType
];
3946 hSize
= nSize
; cSize
= 0;
3947 t
= packet
->m_nTimeStamp
- last
;
3951 header
= packet
->m_body
- nSize
;
3952 hend
= packet
->m_body
;
3957 hend
= hbuf
+ sizeof(hbuf
);
3960 if (packet
->m_nChannel
> 319)
3962 else if (packet
->m_nChannel
> 63)
3974 RTMP_Log(RTMP_LOGWARNING
, "Larger timestamp than 24-bit: 0x%x", t
);
3978 c
= packet
->m_headerType
<< 6;
3982 c
|= packet
->m_nChannel
;
3993 int tmp
= packet
->m_nChannel
- 64;
3994 *hptr
++ = tmp
& 0xff;
4001 hptr
= AMF_EncodeInt24(hptr
, hend
, t
> 0xffffff ? 0xffffff : t
);
4006 hptr
= AMF_EncodeInt24(hptr
, hend
, packet
->m_nBodySize
);
4007 *hptr
++ = packet
->m_packetType
;
4011 hptr
+= EncodeInt32LE(hptr
, packet
->m_nInfoField2
);
4014 hptr
= AMF_EncodeInt32(hptr
, hend
, t
);
4016 nSize
= packet
->m_nBodySize
;
4017 buffer
= packet
->m_body
;
4018 nChunkSize
= r
->m_outChunkSize
;
4020 RTMP_Log(RTMP_LOGDEBUG2
, "%s: fd=%d, size=%d", __FUNCTION__
, r
->m_sb
.sb_socket
,
4022 /* send all chunks in one HTTP request */
4023 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
4025 int chunks
= (nSize
+nChunkSize
-1) / nChunkSize
;
4028 tlen
= chunks
* (cSize
+ 1) + nSize
+ hSize
;
4029 tbuf
= malloc(tlen
);
4035 while (nSize
+ hSize
)
4039 if (nSize
< nChunkSize
)
4042 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)header
, hSize
);
4043 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)buffer
, nChunkSize
);
4046 memcpy(toff
, header
, nChunkSize
+ hSize
);
4047 toff
+= nChunkSize
+ hSize
;
4051 wrote
= WriteN(r
, header
, nChunkSize
+ hSize
);
4055 nSize
-= nChunkSize
;
4056 buffer
+= nChunkSize
;
4061 header
= buffer
- 1;
4073 *header
= (0xc0 | c
);
4076 int tmp
= packet
->m_nChannel
- 64;
4077 header
[1] = tmp
& 0xff;
4079 header
[2] = tmp
>> 8;
4083 char* extendedTimestamp
= header
+ 1 + cSize
;
4084 AMF_EncodeInt32(extendedTimestamp
, extendedTimestamp
+ 4, t
);
4090 int wrote
= WriteN(r
, tbuf
, toff
-tbuf
);
4097 /* we invoked a remote method */
4098 if (packet
->m_packetType
== RTMP_PACKET_TYPE_INVOKE
)
4102 ptr
= packet
->m_body
+ 1;
4103 AMF_DecodeString(ptr
, &method
);
4104 RTMP_Log(RTMP_LOGDEBUG
, "Invoking %s", method
.av_val
);
4105 /* keep it in call queue till result arrives */
4108 ptr
+= 3 + method
.av_len
;
4109 txn
= (int)AMF_DecodeNumber(ptr
);
4110 AV_queue(&r
->m_methodCalls
, &r
->m_numCalls
, &method
, txn
);
4114 if (!r
->m_vecChannelsOut
[packet
->m_nChannel
])
4115 r
->m_vecChannelsOut
[packet
->m_nChannel
] = malloc(sizeof(RTMPPacket
));
4116 memcpy(r
->m_vecChannelsOut
[packet
->m_nChannel
], packet
, sizeof(RTMPPacket
));
4123 return SHandShake(r
);
4129 CloseInternal(r
, 0);
4133 CloseInternal(RTMP
*r
, int reconnect
)
4137 if (RTMP_IsConnected(r
))
4139 if (r
->m_stream_id
> 0)
4143 if ((r
->Link
.protocol
& RTMP_FEATURE_WRITE
))
4145 SendDeleteStream(r
, i
);
4147 if (r
->m_clientID
.av_val
)
4149 HTTP_Post(r
, RTMPT_CLOSE
, "", 1);
4150 free(r
->m_clientID
.av_val
);
4151 r
->m_clientID
.av_val
= NULL
;
4152 r
->m_clientID
.av_len
= 0;
4154 RTMPSockBuf_Close(&r
->m_sb
);
4157 r
->m_stream_id
= -1;
4158 r
->m_sb
.sb_socket
= -1;
4159 r
->m_nBWCheckCounter
= 0;
4161 r
->m_nBytesInSent
= 0;
4163 if (r
->m_read
.flags
& RTMP_READ_HEADER
) {
4164 free(r
->m_read
.buf
);
4165 r
->m_read
.buf
= NULL
;
4167 r
->m_read
.dataType
= 0;
4168 r
->m_read
.flags
= 0;
4169 r
->m_read
.status
= 0;
4170 r
->m_read
.nResumeTS
= 0;
4171 r
->m_read
.nIgnoredFrameCounter
= 0;
4172 r
->m_read
.nIgnoredFlvFrameCounter
= 0;
4174 r
->m_write
.m_nBytesRead
= 0;
4175 RTMPPacket_Free(&r
->m_write
);
4177 for (i
= 0; i
< r
->m_channelsAllocatedIn
; i
++)
4179 if (r
->m_vecChannelsIn
[i
])
4181 RTMPPacket_Free(r
->m_vecChannelsIn
[i
]);
4182 free(r
->m_vecChannelsIn
[i
]);
4183 r
->m_vecChannelsIn
[i
] = NULL
;
4186 free(r
->m_vecChannelsIn
);
4187 r
->m_vecChannelsIn
= NULL
;
4188 free(r
->m_channelTimestamp
);
4189 r
->m_channelTimestamp
= NULL
;
4190 r
->m_channelsAllocatedIn
= 0;
4191 for (i
= 0; i
< r
->m_channelsAllocatedOut
; i
++)
4193 if (r
->m_vecChannelsOut
[i
])
4195 free(r
->m_vecChannelsOut
[i
]);
4196 r
->m_vecChannelsOut
[i
] = NULL
;
4199 free(r
->m_vecChannelsOut
);
4200 r
->m_vecChannelsOut
= NULL
;
4201 r
->m_channelsAllocatedOut
= 0;
4202 AV_clear(r
->m_methodCalls
, r
->m_numCalls
);
4203 r
->m_methodCalls
= NULL
;
4205 r
->m_numInvokes
= 0;
4207 r
->m_bPlaying
= FALSE
;
4208 r
->m_sb
.sb_size
= 0;
4210 r
->m_msgCounter
= 0;
4214 if (r
->Link
.lFlags
& RTMP_LF_FTCU
&& !reconnect
)
4216 free(r
->Link
.tcUrl
.av_val
);
4217 r
->Link
.tcUrl
.av_val
= NULL
;
4218 r
->Link
.lFlags
^= RTMP_LF_FTCU
;
4220 if (r
->Link
.lFlags
& RTMP_LF_FAPU
&& !reconnect
)
4222 free(r
->Link
.app
.av_val
);
4223 r
->Link
.app
.av_val
= NULL
;
4224 r
->Link
.lFlags
^= RTMP_LF_FAPU
;
4229 free(r
->Link
.playpath0
.av_val
);
4230 r
->Link
.playpath0
.av_val
= NULL
;
4235 MDH_free(r
->Link
.dh
);
4238 if (r
->Link
.rc4keyIn
)
4240 RC4_free(r
->Link
.rc4keyIn
);
4241 r
->Link
.rc4keyIn
= NULL
;
4243 if (r
->Link
.rc4keyOut
)
4245 RC4_free(r
->Link
.rc4keyOut
);
4246 r
->Link
.rc4keyOut
= NULL
;
4252 RTMPSockBuf_Fill(RTMPSockBuf
*sb
)
4257 sb
->sb_start
= sb
->sb_buf
;
4261 nBytes
= sizeof(sb
->sb_buf
) - 1 - sb
->sb_size
- (sb
->sb_start
- sb
->sb_buf
);
4262 #if defined(CRYPTO) && !defined(NO_SSL)
4265 nBytes
= TLS_read(sb
->sb_ssl
, sb
->sb_start
+ sb
->sb_size
, nBytes
);
4270 nBytes
= recv(sb
->sb_socket
, sb
->sb_start
+ sb
->sb_size
, nBytes
, 0);
4274 sb
->sb_size
+= nBytes
;
4278 int sockerr
= GetSockError();
4279 RTMP_Log(RTMP_LOGDEBUG
, "%s, recv returned %d. GetSockError(): %d (%s)",
4280 __FUNCTION__
, nBytes
, sockerr
, strerror(sockerr
));
4281 if (sockerr
== EINTR
&& !RTMP_ctrlC
)
4284 if (sockerr
== EWOULDBLOCK
|| sockerr
== EAGAIN
)
4286 sb
->sb_timedout
= TRUE
;
4297 RTMPSockBuf_Send(RTMPSockBuf
*sb
, const char *buf
, int len
)
4302 fwrite(buf
, 1, len
, netstackdump
);
4305 #if defined(CRYPTO) && !defined(NO_SSL)
4308 rc
= TLS_write(sb
->sb_ssl
, buf
, len
);
4313 rc
= send(sb
->sb_socket
, buf
, len
, 0);
4319 RTMPSockBuf_Close(RTMPSockBuf
*sb
)
4321 #if defined(CRYPTO) && !defined(NO_SSL)
4324 TLS_shutdown(sb
->sb_ssl
);
4325 TLS_close(sb
->sb_ssl
);
4329 if (sb
->sb_socket
!= -1)
4330 return closesocket(sb
->sb_socket
);
4334 #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
4337 DecodeTEA(AVal
*key
, AVal
*text
)
4339 uint32_t *v
, k
[4] = { 0 }, u
;
4340 uint32_t z
, y
, sum
= 0, e
, DELTA
= 0x9e3779b9;
4343 unsigned char *ptr
, *out
;
4345 /* prep key: pack 1st 16 chars into 4 LittleEndian ints */
4346 ptr
= (unsigned char *)key
->av_val
;
4350 p
= key
->av_len
> 16 ? 16 : key
->av_len
;
4351 for (i
= 0; i
< p
; i
++)
4353 u
|= ptr
[i
] << (n
* 8);
4365 /* any trailing chars */
4369 /* prep text: hex2bin, multiples of 4 */
4370 n
= (text
->av_len
+ 7) / 8;
4371 out
= malloc(n
* 8);
4372 ptr
= (unsigned char *)text
->av_val
;
4373 v
= (uint32_t *) out
;
4374 for (i
= 0; i
< n
; i
++)
4376 u
= (HEX2BIN(ptr
[0]) << 4) + HEX2BIN(ptr
[1]);
4377 u
|= ((HEX2BIN(ptr
[2]) << 4) + HEX2BIN(ptr
[3])) << 8;
4378 u
|= ((HEX2BIN(ptr
[4]) << 4) + HEX2BIN(ptr
[5])) << 16;
4379 u
|= ((HEX2BIN(ptr
[6]) << 4) + HEX2BIN(ptr
[7])) << 24;
4383 v
= (uint32_t *) out
;
4385 /* http://www.movable-type.co.uk/scripts/tea-block.html */
4386 #define MX (((z>>5)^(y<<2)) + ((y>>3)^(z<<4))) ^ ((sum^y) + (k[(p&3)^e]^z));
4394 for (p
= n
- 1; p
> 0; p
--)
4395 z
= v
[p
- 1], y
= v
[p
] -= MX
;
4402 memcpy(text
->av_val
, out
, text
->av_len
);
4407 HTTP_Post(RTMP
*r
, RTMPTCmd cmd
, const char *buf
, int len
)
4410 int hlen
= snprintf(hbuf
, sizeof(hbuf
), "POST /%s%s/%d HTTP/1.1\r\n"
4413 "User-Agent: Shockwave Flash\r\n"
4414 "Connection: Keep-Alive\r\n"
4415 "Cache-Control: no-cache\r\n"
4416 "Content-type: application/x-fcs\r\n"
4417 "Content-length: %d\r\n\r\n", RTMPT_cmds
[cmd
],
4418 r
->m_clientID
.av_val
? r
->m_clientID
.av_val
: "",
4419 r
->m_msgCounter
, r
->Link
.hostname
.av_len
, r
->Link
.hostname
.av_val
,
4421 RTMPSockBuf_Send(&r
->m_sb
, hbuf
, hlen
);
4422 hlen
= RTMPSockBuf_Send(&r
->m_sb
, buf
, len
);
4429 HTTP_read(RTMP
*r
, int fill
)
4436 RTMPSockBuf_Fill(&r
->m_sb
);
4437 if (r
->m_sb
.sb_size
< 13) {
4442 if (strncmp(r
->m_sb
.sb_start
, "HTTP/1.1 200 ", 13))
4444 r
->m_sb
.sb_start
[r
->m_sb
.sb_size
] = '\0';
4445 if (!strstr(r
->m_sb
.sb_start
, "\r\n\r\n")) {
4451 ptr
= r
->m_sb
.sb_start
+ sizeof("HTTP/1.1 200");
4452 while ((ptr
= strstr(ptr
, "Content-"))) {
4453 if (!strncasecmp(ptr
+8, "length:", 7)) break;
4458 hlen
= atoi(ptr
+16);
4459 ptr
= strstr(ptr
+16, "\r\n\r\n");
4463 if (ptr
+ (r
->m_clientID
.av_val
? 1 : hlen
) > r
->m_sb
.sb_start
+ r
->m_sb
.sb_size
)
4469 r
->m_sb
.sb_size
-= ptr
- r
->m_sb
.sb_start
;
4470 r
->m_sb
.sb_start
= ptr
;
4473 if (!r
->m_clientID
.av_val
)
4475 r
->m_clientID
.av_len
= hlen
;
4476 r
->m_clientID
.av_val
= malloc(hlen
+1);
4477 if (!r
->m_clientID
.av_val
)
4479 r
->m_clientID
.av_val
[0] = '/';
4480 memcpy(r
->m_clientID
.av_val
+1, ptr
, hlen
-1);
4481 r
->m_clientID
.av_val
[hlen
] = 0;
4482 r
->m_sb
.sb_size
= 0;
4486 r
->m_polling
= *ptr
++;
4487 r
->m_resplen
= hlen
- 1;
4494 #define MAX_IGNORED_FRAMES 50
4496 /* Read from the stream until we get a media packet.
4497 * Returns -3 if Play.Close/Stop, -2 if fatal error, -1 if no more media
4498 * packets, 0 if ignorable error, >0 if there is a media packet
4501 Read_1_Packet(RTMP
*r
, char *buf
, unsigned int buflen
)
4503 uint32_t prevTagSize
= 0;
4504 int rtnGetNextMediaPacket
= 0, ret
= RTMP_READ_EOF
;
4505 RTMPPacket packet
= { 0 };
4509 uint32_t nTimeStamp
= 0;
4512 rtnGetNextMediaPacket
= RTMP_GetNextMediaPacket(r
, &packet
);
4513 while (rtnGetNextMediaPacket
)
4515 char *packetBody
= packet
.m_body
;
4516 unsigned int nPacketLen
= packet
.m_nBodySize
;
4518 /* Return RTMP_READ_COMPLETE if this was completed nicely with
4519 * invoke message Play.Stop or Play.Complete
4521 if (rtnGetNextMediaPacket
== 2)
4523 RTMP_Log(RTMP_LOGDEBUG
,
4524 "Got Play.Complete or Play.Stop from server. "
4525 "Assuming stream is complete");
4526 ret
= RTMP_READ_COMPLETE
;
4530 r
->m_read
.dataType
|= (((packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
) << 2) |
4531 (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
));
4533 if (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
&& nPacketLen
<= 5)
4535 RTMP_Log(RTMP_LOGDEBUG
, "ignoring too small video packet: size: %d",
4537 ret
= RTMP_READ_IGNORE
;
4540 if (packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
&& nPacketLen
<= 1)
4542 RTMP_Log(RTMP_LOGDEBUG
, "ignoring too small audio packet: size: %d",
4544 ret
= RTMP_READ_IGNORE
;
4548 if (r
->m_read
.flags
& RTMP_READ_SEEKING
)
4550 ret
= RTMP_READ_IGNORE
;
4554 RTMP_Log(RTMP_LOGDEBUG
, "type: %02X, size: %d, TS: %d ms, abs TS: %d",
4555 packet
.m_packetType
, nPacketLen
, packet
.m_nTimeStamp
,
4556 packet
.m_hasAbsTimestamp
);
4557 if (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
)
4558 RTMP_Log(RTMP_LOGDEBUG
, "frametype: %02X", (*packetBody
& 0xf0));
4561 if (r
->m_read
.flags
& RTMP_READ_RESUME
)
4563 /* check the header if we get one */
4564 if (packet
.m_nTimeStamp
== 0)
4566 if (r
->m_read
.nMetaHeaderSize
> 0
4567 && packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
)
4571 AMF_Decode(&metaObj
, packetBody
, nPacketLen
, FALSE
);
4575 AMFProp_GetString(AMF_GetProp(&metaObj
, NULL
, 0),
4578 if (AVMATCH(&metastring
, &av_onMetaData
))
4581 if ((r
->m_read
.nMetaHeaderSize
!= nPacketLen
) ||
4583 (r
->m_read
.metaHeader
, packetBody
,
4584 r
->m_read
.nMetaHeaderSize
) != 0))
4586 ret
= RTMP_READ_ERROR
;
4589 AMF_Reset(&metaObj
);
4590 if (ret
== RTMP_READ_ERROR
)
4595 /* check first keyframe to make sure we got the right position
4596 * in the stream! (the first non ignored frame)
4598 if (r
->m_read
.nInitialFrameSize
> 0)
4600 /* video or audio data */
4601 if (packet
.m_packetType
== r
->m_read
.initialFrameType
4602 && r
->m_read
.nInitialFrameSize
== nPacketLen
)
4604 /* we don't compare the sizes since the packet can
4605 * contain several FLV packets, just make sure the
4606 * first frame is our keyframe (which we are going
4610 (r
->m_read
.initialFrame
, packetBody
,
4611 r
->m_read
.nInitialFrameSize
) == 0)
4613 RTMP_Log(RTMP_LOGDEBUG
, "Checked keyframe successfully!");
4614 r
->m_read
.flags
|= RTMP_READ_GOTKF
;
4615 /* ignore it! (what about audio data after it? it is
4616 * handled by ignoring all 0ms frames, see below)
4618 ret
= RTMP_READ_IGNORE
;
4623 /* hande FLV streams, even though the server resends the
4624 * keyframe as an extra video packet it is also included
4625 * in the first FLV stream chunk and we have to compare
4626 * it and filter it out !!
4628 if (packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
4630 /* basically we have to find the keyframe with the
4631 * correct TS being nResumeTS
4633 unsigned int pos
= 0;
4636 while (pos
+ 11 < nPacketLen
)
4638 /* size without header (11) and prevTagSize (4) */
4640 AMF_DecodeInt24(packetBody
+ pos
+ 1);
4641 ts
= AMF_DecodeInt24(packetBody
+ pos
+ 4);
4642 ts
|= (packetBody
[pos
+ 7] << 24);
4645 RTMP_Log(RTMP_LOGDEBUG
,
4646 "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms",
4647 packetBody
[pos
], dataSize
, ts
);
4649 /* ok, is it a keyframe?:
4650 * well doesn't work for audio!
4652 if (packetBody
[pos
/*6928, test 0 */ ] ==
4653 r
->m_read
.initialFrameType
4654 /* && (packetBody[11]&0xf0) == 0x10 */ )
4656 if (ts
== r
->m_read
.nResumeTS
)
4658 RTMP_Log(RTMP_LOGDEBUG
,
4659 "Found keyframe with resume-keyframe timestamp!");
4660 if (r
->m_read
.nInitialFrameSize
!= dataSize
4661 || memcmp(r
->m_read
.initialFrame
,
4662 packetBody
+ pos
+ 11,
4664 nInitialFrameSize
) != 0)
4666 RTMP_Log(RTMP_LOGERROR
,
4667 "FLV Stream: Keyframe doesn't match!");
4668 ret
= RTMP_READ_ERROR
;
4671 r
->m_read
.flags
|= RTMP_READ_GOTFLVK
;
4673 /* skip this packet?
4674 * check whether skippable:
4676 if (pos
+ 11 + dataSize
+ 4 > nPacketLen
)
4678 RTMP_Log(RTMP_LOGWARNING
,
4679 "Non skipable packet since it doesn't end with chunk, stream corrupt!");
4680 ret
= RTMP_READ_ERROR
;
4683 packetBody
+= (pos
+ 11 + dataSize
+ 4);
4684 nPacketLen
-= (pos
+ 11 + dataSize
+ 4);
4686 goto stopKeyframeSearch
;
4689 else if (r
->m_read
.nResumeTS
< ts
)
4691 /* the timestamp ts will only increase with
4692 * further packets, wait for seek
4694 goto stopKeyframeSearch
;
4697 pos
+= (11 + dataSize
+ 4);
4699 if (ts
< r
->m_read
.nResumeTS
)
4701 RTMP_Log(RTMP_LOGERROR
,
4702 "First packet does not contain keyframe, all "
4703 "timestamps are smaller than the keyframe "
4704 "timestamp; probably the resume seek failed?");
4708 if (!(r
->m_read
.flags
& RTMP_READ_GOTFLVK
))
4710 RTMP_Log(RTMP_LOGERROR
,
4711 "Couldn't find the seeked keyframe in this chunk!");
4712 ret
= RTMP_READ_IGNORE
;
4719 if (packet
.m_nTimeStamp
> 0
4720 && (r
->m_read
.flags
& (RTMP_READ_GOTKF
|RTMP_READ_GOTFLVK
)))
4722 /* another problem is that the server can actually change from
4723 * 09/08 video/audio packets to an FLV stream or vice versa and
4724 * our keyframe check will prevent us from going along with the
4725 * new stream if we resumed.
4727 * in this case set the 'found keyframe' variables to true.
4728 * We assume that if we found one keyframe somewhere and were
4729 * already beyond TS > 0 we have written data to the output
4730 * which means we can accept all forthcoming data including the
4731 * change between 08/09 <-> FLV packets
4733 r
->m_read
.flags
|= (RTMP_READ_GOTKF
|RTMP_READ_GOTFLVK
);
4736 /* skip till we find our keyframe
4737 * (seeking might put us somewhere before it)
4739 if (!(r
->m_read
.flags
& RTMP_READ_GOTKF
) &&
4740 packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4742 RTMP_Log(RTMP_LOGWARNING
,
4743 "Stream does not start with requested frame, ignoring data... ");
4744 r
->m_read
.nIgnoredFrameCounter
++;
4745 if (r
->m_read
.nIgnoredFrameCounter
> MAX_IGNORED_FRAMES
)
4746 ret
= RTMP_READ_ERROR
; /* fatal error, couldn't continue stream */
4748 ret
= RTMP_READ_IGNORE
;
4751 /* ok, do the same for FLV streams */
4752 if (!(r
->m_read
.flags
& RTMP_READ_GOTFLVK
) &&
4753 packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
4755 RTMP_Log(RTMP_LOGWARNING
,
4756 "Stream does not start with requested FLV frame, ignoring data... ");
4757 r
->m_read
.nIgnoredFlvFrameCounter
++;
4758 if (r
->m_read
.nIgnoredFlvFrameCounter
> MAX_IGNORED_FRAMES
)
4759 ret
= RTMP_READ_ERROR
;
4761 ret
= RTMP_READ_IGNORE
;
4765 /* we have to ignore the 0ms frames since these are the first
4766 * keyframes; we've got these so don't mess around with multiple
4767 * copies sent by the server to us! (if the keyframe is found at a
4768 * later position there is only one copy and it will be ignored by
4769 * the preceding if clause)
4771 if (!(r
->m_read
.flags
& RTMP_READ_NO_IGNORE
) &&
4772 packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4774 /* exclude type RTMP_PACKET_TYPE_FLASH_VIDEO since it can
4775 * contain several FLV packets
4777 if (packet
.m_nTimeStamp
== 0)
4779 ret
= RTMP_READ_IGNORE
;
4784 /* stop ignoring packets */
4785 r
->m_read
.flags
|= RTMP_READ_NO_IGNORE
;
4790 /* calculate packet size and allocate slop buffer if necessary */
4792 ((packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
4793 || packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
4794 || packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
) ? 11 : 0) +
4795 (packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
? 4 : 0);
4797 if (size
+ 4 > buflen
)
4799 /* the extra 4 is for the case of an FLV stream without a last
4800 * prevTagSize (we need extra 4 bytes to append it) */
4801 r
->m_read
.buf
= malloc(size
+ 4);
4802 if (r
->m_read
.buf
== 0)
4804 RTMP_Log(RTMP_LOGERROR
, "Couldn't allocate memory!");
4805 ret
= RTMP_READ_ERROR
; /* fatal error */
4809 ptr
= r
->m_read
.buf
;
4815 pend
= ptr
+ size
+ 4;
4817 /* use to return timestamp of last processed packet */
4819 /* audio (0x08), video (0x09) or metadata (0x12) packets :
4820 * construct 11 byte header then add rtmp packet's data */
4821 if (packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
4822 || packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
4823 || packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
)
4825 nTimeStamp
= r
->m_read
.nResumeTS
+ packet
.m_nTimeStamp
;
4826 prevTagSize
= 11 + nPacketLen
;
4828 *ptr
= packet
.m_packetType
;
4830 ptr
= AMF_EncodeInt24(ptr
, pend
, nPacketLen
);
4833 if(packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
) {
4836 if((packetBody
[0] & 0x0f) == 7) { /* CodecId = H264 */
4837 uint8_t packetType
= *(packetBody
+1);
4839 uint32_t ts
= AMF_DecodeInt24(packetBody
+2); /* composition time */
4840 int32_t cts
= (ts
+0xff800000)^0xff800000;
4841 RTMP_Log(RTMP_LOGDEBUG
, "cts : %d\n", cts
);
4844 /* get rid of the composition time */
4845 CRTMP::EncodeInt24(packetBody
+2, 0);
4847 RTMP_Log(RTMP_LOGDEBUG
, "VIDEO: nTimeStamp: 0x%08X (%d)\n", nTimeStamp
, nTimeStamp
);
4851 ptr
= AMF_EncodeInt24(ptr
, pend
, nTimeStamp
);
4852 *ptr
= (char)((nTimeStamp
& 0xFF000000) >> 24);
4856 ptr
= AMF_EncodeInt24(ptr
, pend
, 0);
4859 memcpy(ptr
, packetBody
, nPacketLen
);
4862 /* correct tagSize and obtain timestamp if we have an FLV stream */
4863 if (packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
4865 unsigned int pos
= 0;
4868 /* grab first timestamp and see if it needs fixing */
4869 nTimeStamp
= AMF_DecodeInt24(packetBody
+ 4);
4870 nTimeStamp
|= (packetBody
[7] << 24);
4871 delta
= packet
.m_nTimeStamp
- nTimeStamp
+ r
->m_read
.nResumeTS
;
4873 while (pos
+ 11 < nPacketLen
)
4875 /* size without header (11) and without prevTagSize (4) */
4876 uint32_t dataSize
= AMF_DecodeInt24(packetBody
+ pos
+ 1);
4877 nTimeStamp
= AMF_DecodeInt24(packetBody
+ pos
+ 4);
4878 nTimeStamp
|= (packetBody
[pos
+ 7] << 24);
4882 nTimeStamp
+= delta
;
4883 AMF_EncodeInt24(ptr
+pos
+4, pend
, nTimeStamp
);
4884 ptr
[pos
+7] = nTimeStamp
>>24;
4888 r
->m_read
.dataType
|= (((*(packetBody
+ pos
) == 0x08) << 2) |
4889 (*(packetBody
+ pos
) == 0x09));
4891 if (pos
+ 11 + dataSize
+ 4 > nPacketLen
)
4893 if (pos
+ 11 + dataSize
> nPacketLen
)
4895 RTMP_Log(RTMP_LOGERROR
,
4896 "Wrong data size (%u), stream corrupted, aborting!",
4898 ret
= RTMP_READ_ERROR
;
4901 RTMP_Log(RTMP_LOGWARNING
, "No tagSize found, appending!");
4903 /* we have to append a last tagSize! */
4904 prevTagSize
= dataSize
+ 11;
4905 AMF_EncodeInt32(ptr
+ pos
+ 11 + dataSize
, pend
,
4913 AMF_DecodeInt32(packetBody
+ pos
+ 11 + dataSize
);
4916 RTMP_Log(RTMP_LOGDEBUG
,
4917 "FLV Packet: type %02X, dataSize: %lu, tagSize: %lu, timeStamp: %lu ms",
4918 (unsigned char)packetBody
[pos
], dataSize
, prevTagSize
,
4922 if (prevTagSize
!= (dataSize
+ 11))
4925 RTMP_Log(RTMP_LOGWARNING
,
4926 "Tag and data size are not consitent, writing tag size according to dataSize+11: %d",
4930 prevTagSize
= dataSize
+ 11;
4931 AMF_EncodeInt32(ptr
+ pos
+ 11 + dataSize
, pend
,
4936 pos
+= prevTagSize
+ 4; /*(11+dataSize+4); */
4941 if (packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4943 /* FLV tag packets contain their own prevTagSize */
4944 AMF_EncodeInt32(ptr
, pend
, prevTagSize
);
4947 /* In non-live this nTimeStamp can contain an absolute TS.
4948 * Update ext timestamp with this absolute offset in non-live mode
4949 * otherwise report the relative one
4951 /* 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); */
4952 r
->m_read
.timestamp
= (r
->Link
.lFlags
& RTMP_LF_LIVE
) ? packet
.m_nTimeStamp
: nTimeStamp
;
4958 if (rtnGetNextMediaPacket
)
4959 RTMPPacket_Free(&packet
);
4963 len
= ret
> buflen
? buflen
: ret
;
4964 memcpy(buf
, r
->m_read
.buf
, len
);
4965 r
->m_read
.bufpos
= r
->m_read
.buf
+ len
;
4966 r
->m_read
.buflen
= ret
- len
;
4971 static const char flvHeader
[] = { 'F', 'L', 'V', 0x01,
4972 0x00, /* 0x04 == audio, 0x01 == video */
4973 0x00, 0x00, 0x00, 0x09,
4974 0x00, 0x00, 0x00, 0x00
4977 #define HEADERBUF (128*1024)
4979 RTMP_Read(RTMP
*r
, char *buf
, int size
)
4981 int nRead
= 0, total
= 0;
4983 /* can't continue */
4985 switch (r
->m_read
.status
) {
4987 case RTMP_READ_COMPLETE
:
4989 case RTMP_READ_ERROR
: /* corrupted stream, resume failed */
4990 SetSockError(EINVAL
);
4996 /* first time thru */
4997 if (!(r
->m_read
.flags
& RTMP_READ_HEADER
))
4999 if (!(r
->m_read
.flags
& RTMP_READ_RESUME
))
5001 char *mybuf
= malloc(HEADERBUF
), *end
= mybuf
+ HEADERBUF
;
5003 r
->m_read
.buf
= mybuf
;
5004 r
->m_read
.buflen
= HEADERBUF
;
5006 memcpy(mybuf
, flvHeader
, sizeof(flvHeader
));
5007 r
->m_read
.buf
+= sizeof(flvHeader
);
5008 r
->m_read
.buflen
-= sizeof(flvHeader
);
5009 cnt
+= sizeof(flvHeader
);
5011 while (r
->m_read
.timestamp
== 0)
5013 nRead
= Read_1_Packet(r
, r
->m_read
.buf
, r
->m_read
.buflen
);
5017 r
->m_read
.buf
= NULL
;
5018 r
->m_read
.buflen
= 0;
5019 r
->m_read
.status
= nRead
;
5022 /* buffer overflow, fix buffer and give up */
5023 if (r
->m_read
.buf
< mybuf
|| r
->m_read
.buf
> end
) {
5024 mybuf
= realloc(mybuf
, cnt
+ nRead
);
5025 memcpy(mybuf
+cnt
, r
->m_read
.buf
, nRead
);
5026 free(r
->m_read
.buf
);
5027 r
->m_read
.buf
= mybuf
+cnt
+nRead
;
5031 r
->m_read
.buf
+= nRead
;
5032 r
->m_read
.buflen
-= nRead
;
5033 if (r
->m_read
.dataType
== 5)
5036 mybuf
[4] = r
->m_read
.dataType
;
5037 r
->m_read
.buflen
= r
->m_read
.buf
- mybuf
;
5038 r
->m_read
.buf
= mybuf
;
5039 r
->m_read
.bufpos
= mybuf
;
5041 r
->m_read
.flags
|= RTMP_READ_HEADER
;
5044 if ((r
->m_read
.flags
& RTMP_READ_SEEKING
) && r
->m_read
.buf
)
5046 /* drop whatever's here */
5047 free(r
->m_read
.buf
);
5048 r
->m_read
.buf
= NULL
;
5049 r
->m_read
.bufpos
= NULL
;
5050 r
->m_read
.buflen
= 0;
5053 /* If there's leftover data buffered, use it up */
5056 nRead
= r
->m_read
.buflen
;
5059 memcpy(buf
, r
->m_read
.bufpos
, nRead
);
5060 r
->m_read
.buflen
-= nRead
;
5061 if (!r
->m_read
.buflen
)
5063 free(r
->m_read
.buf
);
5064 r
->m_read
.buf
= NULL
;
5065 r
->m_read
.bufpos
= NULL
;
5069 r
->m_read
.bufpos
+= nRead
;
5076 while (size
> 0 && (nRead
= Read_1_Packet(r
, buf
, size
)) >= 0)
5078 if (!nRead
) continue;
5085 r
->m_read
.status
= nRead
;
5092 static const AVal av_setDataFrame
= AVC("@setDataFrame");
5095 RTMP_Write(RTMP
*r
, const char *buf
, int size
)
5097 RTMPPacket
*pkt
= &r
->m_write
;
5099 int s2
= size
, ret
, num
;
5101 pkt
->m_nChannel
= 0x04; /* source channel */
5102 pkt
->m_nInfoField2
= r
->m_stream_id
;
5106 if (!pkt
->m_nBytesRead
)
5109 /* FLV pkt too small */
5113 if (buf
[0] == 'F' && buf
[1] == 'L' && buf
[2] == 'V')
5119 pkt
->m_packetType
= *buf
++;
5120 pkt
->m_nBodySize
= AMF_DecodeInt24(buf
);
5122 pkt
->m_nTimeStamp
= AMF_DecodeInt24(buf
);
5124 pkt
->m_nTimeStamp
|= *buf
++ << 24;
5128 if (((pkt
->m_packetType
== RTMP_PACKET_TYPE_AUDIO
5129 || pkt
->m_packetType
== RTMP_PACKET_TYPE_VIDEO
) &&
5130 !pkt
->m_nTimeStamp
) || pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
5132 pkt
->m_headerType
= RTMP_PACKET_SIZE_LARGE
;
5133 if (pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
5134 pkt
->m_nBodySize
+= 16;
5138 pkt
->m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
5141 if (!RTMPPacket_Alloc(pkt
, pkt
->m_nBodySize
))
5143 RTMP_Log(RTMP_LOGDEBUG
, "%s, failed to allocate packet", __FUNCTION__
);
5147 pend
= enc
+ pkt
->m_nBodySize
;
5148 if (pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
5150 enc
= AMF_EncodeString(enc
, pend
, &av_setDataFrame
);
5151 pkt
->m_nBytesRead
= enc
- pkt
->m_body
;
5156 enc
= pkt
->m_body
+ pkt
->m_nBytesRead
;
5158 num
= pkt
->m_nBodySize
- pkt
->m_nBytesRead
;
5161 memcpy(enc
, buf
, num
);
5162 pkt
->m_nBytesRead
+= num
;
5165 if (pkt
->m_nBytesRead
== pkt
->m_nBodySize
)
5167 ret
= RTMP_SendPacket(r
, pkt
, FALSE
);
5168 RTMPPacket_Free(pkt
);
5169 pkt
->m_nBytesRead
= 0;