2 * Copyright (C) 2005-2008 Team XBMC
4 * Copyright (C) 2008-2009 Andrej Stepanchuk
5 * Copyright (C) 2009-2010 Howard Chu
7 * This file is part of librtmp.
9 * librtmp is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation; either version 2.1,
12 * or (at your option) any later version.
14 * librtmp is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with librtmp see the file COPYING. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 * http://www.gnu.org/copyleft/lgpl.html
36 #include <polarssl/version.h>
37 #include <polarssl/havege.h>
39 static const char *my_dhm_P
=
40 "E4004C1F94182000103D883A448B3F80" \
41 "2CE4B44A83301270002C20D0321CFD00" \
42 "11CCEF784C26A400F43DFB901BCA7538" \
43 "F2C6B176001CF5A0FD16D2C48B1D0C1C" \
44 "F6AC8E1DA6BCC3B4E1F96B0564965300" \
45 "FFA1D0B601EB2800F489AA512C4B248C" \
46 "01F76949A60BB7F00A40B1EAB64BDD48" \
47 "E8A700D60B7F1200FA8E77B0A979DABF";
49 static const char *my_dhm_G
= "4";
51 #elif defined(USE_GNUTLS)
52 #include <gnutls/gnutls.h>
53 #else /* USE_OPENSSL */
54 #include <openssl/ssl.h>
55 #include <openssl/rc4.h>
60 #define RTMP_SIG_SIZE 1536
61 #define RTMP_LARGE_HEADER_SIZE 12
63 static const int packetSize
[] = { 12, 8, 4, 1 };
67 const char RTMPProtocolStrings
[][7] = {
79 const char RTMPProtocolStringsLower
[][7] = {
91 static const char *RTMPT_cmds
[] = {
99 RTMPT_OPEN
=0, RTMPT_SEND
, RTMPT_IDLE
, RTMPT_CLOSE
102 static int DumpMetaData(AMFObject
*obj
);
103 static int HandShake(RTMP
*r
, int FP9HandShake
);
104 static int SocksNegotiate(RTMP
*r
);
106 static int SendConnectPacket(RTMP
*r
, RTMPPacket
*cp
);
107 static int SendCheckBW(RTMP
*r
);
108 static int SendCheckBWResult(RTMP
*r
, double txn
);
109 static int SendDeleteStream(RTMP
*r
, double dStreamId
);
110 static int SendFCSubscribe(RTMP
*r
, AVal
*subscribepath
);
111 static int SendPlay(RTMP
*r
);
112 static int SendBytesReceived(RTMP
*r
);
113 static int SendUsherToken(RTMP
*r
, AVal
*usherToken
);
116 static int SendBGHasStream(RTMP
*r
, double dId
, AVal
*playpath
);
119 static int HandleInvoke(RTMP
*r
, const char *body
, unsigned int nBodySize
);
120 static int HandleMetadata(RTMP
*r
, char *body
, unsigned int len
);
121 static void HandleChangeChunkSize(RTMP
*r
, const RTMPPacket
*packet
);
122 static void HandleAudio(RTMP
*r
, const RTMPPacket
*packet
);
123 static void HandleVideo(RTMP
*r
, const RTMPPacket
*packet
);
124 static void HandleCtrl(RTMP
*r
, const RTMPPacket
*packet
);
125 static void HandleServerBW(RTMP
*r
, const RTMPPacket
*packet
);
126 static void HandleClientBW(RTMP
*r
, const RTMPPacket
*packet
);
128 static int ReadN(RTMP
*r
, char *buffer
, int n
);
129 static int WriteN(RTMP
*r
, const char *buffer
, int n
);
131 static void DecodeTEA(AVal
*key
, AVal
*text
);
133 static int HTTP_Post(RTMP
*r
, RTMPTCmd cmd
, const char *buf
, int len
);
134 static int HTTP_read(RTMP
*r
, int fill
);
141 #include "handshake.h"
149 #elif defined(_WIN32)
150 return timeGetTime();
153 if (!clk_tck
) clk_tck
= sysconf(_SC_CLK_TCK
);
154 return times(&t
) * 1000 / clk_tck
;
165 RTMPPacket_Reset(RTMPPacket
*p
)
171 p
->m_nInfoField2
= 0;
172 p
->m_hasAbsTimestamp
= FALSE
;
178 RTMPPacket_Alloc(RTMPPacket
*p
, int nSize
)
180 char *ptr
= calloc(1, nSize
+ RTMP_MAX_HEADER_SIZE
);
183 p
->m_body
= ptr
+ RTMP_MAX_HEADER_SIZE
;
189 RTMPPacket_Free(RTMPPacket
*p
)
193 free(p
->m_body
- RTMP_MAX_HEADER_SIZE
);
199 RTMPPacket_Dump(RTMPPacket
*p
)
201 RTMP_Log(RTMP_LOGDEBUG
,
202 "RTMP PACKET: packet type: 0x%02x. channel: 0x%02x. info 1: %d info 2: %d. Body size: %u. body: 0x%02x",
203 p
->m_packetType
, p
->m_nChannel
, p
->m_nTimeStamp
, p
->m_nInfoField2
,
204 p
->m_nBodySize
, p
->m_body
? (unsigned char)p
->m_body
[0] : 0);
210 return RTMP_LIB_VERSION
;
218 /* Do this regardless of NO_SSL, we use havege for rtmpe too */
219 RTMP_TLS_ctx
= calloc(1,sizeof(struct tls_ctx
));
220 havege_init(&RTMP_TLS_ctx
->hs
);
221 #elif defined(USE_GNUTLS) && !defined(NO_SSL)
222 /* Technically we need to initialize libgcrypt ourselves if
223 * we're not going to call gnutls_global_init(). Ignoring this
226 gnutls_global_init();
227 RTMP_TLS_ctx
= malloc(sizeof(struct tls_ctx
));
228 gnutls_certificate_allocate_credentials(&RTMP_TLS_ctx
->cred
);
229 gnutls_priority_init(&RTMP_TLS_ctx
->prios
, "NORMAL", NULL
);
230 gnutls_certificate_set_x509_trust_file(RTMP_TLS_ctx
->cred
,
231 "ca.pem", GNUTLS_X509_FMT_PEM
);
232 #elif !defined(NO_SSL) /* USE_OPENSSL */
233 /* libcrypto doesn't need anything special */
234 SSL_load_error_strings();
236 OpenSSL_add_all_digests();
237 RTMP_TLS_ctx
= SSL_CTX_new(SSLv23_method());
238 SSL_CTX_set_options(RTMP_TLS_ctx
, SSL_OP_ALL
);
239 SSL_CTX_set_default_verify_paths(RTMP_TLS_ctx
);
245 RTMP_TLS_AllocServerContext(const char* cert
, const char* key
)
252 tls_server_ctx
*tc
= ctx
= calloc(1, sizeof(struct tls_server_ctx
));
253 tc
->dhm_P
= my_dhm_P
;
254 tc
->dhm_G
= my_dhm_G
;
255 tc
->hs
= &RTMP_TLS_ctx
->hs
;
256 if (x509parse_crtfile(&tc
->cert
, cert
)) {
260 if (x509parse_keyfile(&tc
->key
, key
, NULL
)) {
261 x509_free(&tc
->cert
);
265 #elif defined(USE_GNUTLS) && !defined(NO_SSL)
266 gnutls_certificate_allocate_credentials((gnutls_certificate_credentials
*) &ctx
);
267 if (gnutls_certificate_set_x509_key_file(ctx
, cert
, key
, GNUTLS_X509_FMT_PEM
) != 0) {
268 gnutls_certificate_free_credentials(ctx
);
271 #elif !defined(NO_SSL) /* USE_OPENSSL */
272 ctx
= SSL_CTX_new(SSLv23_server_method());
273 FILE *f
= fopen(key
, "r");
278 EVP_PKEY
*k
= PEM_read_PrivateKey(f
, NULL
, NULL
, NULL
);
284 SSL_CTX_use_PrivateKey(ctx
, k
);
286 f
= fopen(cert
, "r");
291 X509
*c
= PEM_read_X509(f
, NULL
, NULL
, NULL
);
297 SSL_CTX_use_certificate(ctx
, c
);
305 RTMP_TLS_FreeServerContext(void *ctx
)
309 x509_free(&((tls_server_ctx
*)ctx
)->cert
);
310 rsa_free(&((tls_server_ctx
*)ctx
)->key
);
312 #elif defined(USE_GNUTLS) && !defined(NO_SSL)
313 gnutls_certificate_free_credentials(ctx
);
314 #elif !defined(NO_SSL) /* USE_OPENSSL */
323 return calloc(1, sizeof(RTMP
));
340 memset(r
, 0, sizeof(RTMP
));
341 r
->m_sb
.sb_socket
= -1;
342 r
->m_inChunkSize
= RTMP_DEFAULT_CHUNKSIZE
;
343 r
->m_outChunkSize
= RTMP_DEFAULT_CHUNKSIZE
;
344 r
->m_nBufferMS
= 30000;
345 r
->m_nClientBW
= 2500000;
347 r
->m_nServerBW
= 2500000;
348 r
->m_fAudioCodecs
= 3191.0;
349 r
->m_fVideoCodecs
= 252.0;
350 r
->Link
.timeout
= 30;
355 RTMP_EnableWrite(RTMP
*r
)
357 r
->Link
.protocol
|= RTMP_FEATURE_WRITE
;
361 RTMP_GetDuration(RTMP
*r
)
363 return r
->m_fDuration
;
367 RTMP_IsConnected(RTMP
*r
)
369 return r
->m_sb
.sb_socket
!= -1;
375 return r
->m_sb
.sb_socket
;
379 RTMP_IsTimedout(RTMP
*r
)
381 return r
->m_sb
.sb_timedout
;
385 RTMP_SetBufferMS(RTMP
*r
, int size
)
387 r
->m_nBufferMS
= size
;
391 RTMP_UpdateBufferMS(RTMP
*r
)
393 RTMP_SendCtrl(r
, 3, r
->m_stream_id
, r
->m_nBufferMS
);
399 #elif defined(__sun__)
401 #elif defined(__APPLE__)
403 #elif defined(__linux__)
408 #define DEF_VERSTR OSS " 10,0,32,18"
409 static const char DEFAULT_FLASH_VER
[] = DEF_VERSTR
;
410 const AVal RTMP_DefaultFlashVer
=
411 { (char *)DEFAULT_FLASH_VER
, sizeof(DEFAULT_FLASH_VER
) - 1 };
414 SocksSetup(RTMP
*r
, AVal
*sockshost
)
416 if (sockshost
->av_len
)
418 const char *socksport
= strchr(sockshost
->av_val
, ':');
419 char *hostname
= strdup(sockshost
->av_val
);
422 hostname
[socksport
- sockshost
->av_val
] = '\0';
423 r
->Link
.sockshost
.av_val
= hostname
;
424 r
->Link
.sockshost
.av_len
= strlen(hostname
);
426 r
->Link
.socksport
= socksport
? atoi(socksport
+ 1) : 1080;
427 RTMP_Log(RTMP_LOGDEBUG
, "Connecting via SOCKS proxy: %s:%d", r
->Link
.sockshost
.av_val
,
432 r
->Link
.sockshost
.av_val
= NULL
;
433 r
->Link
.sockshost
.av_len
= 0;
434 r
->Link
.socksport
= 0;
439 RTMP_SetupStream(RTMP
*r
,
456 int dStop
, int bLiveStream
, long int timeout
)
458 RTMP_Log(RTMP_LOGDEBUG
, "Protocol : %s", RTMPProtocolStrings
[protocol
&7]);
459 RTMP_Log(RTMP_LOGDEBUG
, "Hostname : %.*s", host
->av_len
, host
->av_val
);
460 RTMP_Log(RTMP_LOGDEBUG
, "Port : %d", port
);
461 RTMP_Log(RTMP_LOGDEBUG
, "Playpath : %s", playpath
->av_val
);
463 if (tcUrl
&& tcUrl
->av_val
)
464 RTMP_Log(RTMP_LOGDEBUG
, "tcUrl : %s", tcUrl
->av_val
);
465 if (swfUrl
&& swfUrl
->av_val
)
466 RTMP_Log(RTMP_LOGDEBUG
, "swfUrl : %s", swfUrl
->av_val
);
467 if (pageUrl
&& pageUrl
->av_val
)
468 RTMP_Log(RTMP_LOGDEBUG
, "pageUrl : %s", pageUrl
->av_val
);
469 if (app
&& app
->av_val
)
470 RTMP_Log(RTMP_LOGDEBUG
, "app : %.*s", app
->av_len
, app
->av_val
);
471 if (auth
&& auth
->av_val
)
472 RTMP_Log(RTMP_LOGDEBUG
, "auth : %s", auth
->av_val
);
473 if (subscribepath
&& subscribepath
->av_val
)
474 RTMP_Log(RTMP_LOGDEBUG
, "subscribepath : %s", subscribepath
->av_val
);
475 if (usherToken
&& usherToken
->av_val
)
476 RTMP_Log(RTMP_LOGDEBUG
, "NetStream.Authenticate.UsherToken : %s", usherToken
->av_val
);
477 if (flashVer
&& flashVer
->av_val
)
478 RTMP_Log(RTMP_LOGDEBUG
, "flashVer : %s", flashVer
->av_val
);
480 RTMP_Log(RTMP_LOGDEBUG
, "StartTime : %d msec", dStart
);
482 RTMP_Log(RTMP_LOGDEBUG
, "StopTime : %d msec", dStop
);
484 RTMP_Log(RTMP_LOGDEBUG
, "live : %s", bLiveStream
? "yes" : "no");
485 RTMP_Log(RTMP_LOGDEBUG
, "timeout : %ld sec", timeout
);
488 if (swfSHA256Hash
!= NULL
&& swfSize
> 0)
490 memcpy(r
->Link
.SWFHash
, swfSHA256Hash
->av_val
, sizeof(r
->Link
.SWFHash
));
491 r
->Link
.SWFSize
= swfSize
;
492 RTMP_Log(RTMP_LOGDEBUG
, "SWFSHA256:");
493 RTMP_LogHex(RTMP_LOGDEBUG
, r
->Link
.SWFHash
, sizeof(r
->Link
.SWFHash
));
494 RTMP_Log(RTMP_LOGDEBUG
, "SWFSize : %u", r
->Link
.SWFSize
);
502 SocksSetup(r
, sockshost
);
504 if (tcUrl
&& tcUrl
->av_len
)
505 r
->Link
.tcUrl
= *tcUrl
;
506 if (swfUrl
&& swfUrl
->av_len
)
507 r
->Link
.swfUrl
= *swfUrl
;
508 if (pageUrl
&& pageUrl
->av_len
)
509 r
->Link
.pageUrl
= *pageUrl
;
510 if (app
&& app
->av_len
)
512 if (auth
&& auth
->av_len
)
514 r
->Link
.auth
= *auth
;
515 r
->Link
.lFlags
|= RTMP_LF_AUTH
;
517 if (flashVer
&& flashVer
->av_len
)
518 r
->Link
.flashVer
= *flashVer
;
520 r
->Link
.flashVer
= RTMP_DefaultFlashVer
;
521 if (subscribepath
&& subscribepath
->av_len
)
522 r
->Link
.subscribepath
= *subscribepath
;
523 if (usherToken
&& usherToken
->av_len
)
524 r
->Link
.usherToken
= *usherToken
;
525 r
->Link
.seekTime
= dStart
;
526 r
->Link
.stopTime
= dStop
;
528 r
->Link
.lFlags
|= RTMP_LF_LIVE
;
529 r
->Link
.timeout
= timeout
;
531 r
->Link
.protocol
= protocol
;
532 r
->Link
.hostname
= *host
;
534 r
->Link
.playpath
= *playpath
;
536 if (r
->Link
.port
== 0)
538 if (protocol
& RTMP_FEATURE_SSL
)
540 else if (protocol
& RTMP_FEATURE_HTTP
)
547 enum { OPT_STR
=0, OPT_INT
, OPT_BOOL
, OPT_CONN
};
548 static const char *optinfo
[] = {
549 "string", "integer", "boolean", "AMF" };
551 #define OFF(x) offsetof(struct RTMP,x)
553 static struct urlopt
{
560 { AVC("socks"), OFF(Link
.sockshost
), OPT_STR
, 0,
561 "Use the specified SOCKS proxy" },
562 { AVC("app"), OFF(Link
.app
), OPT_STR
, 0,
563 "Name of target app on server" },
564 { AVC("tcUrl"), OFF(Link
.tcUrl
), OPT_STR
, 0,
565 "URL to played stream" },
566 { AVC("pageUrl"), OFF(Link
.pageUrl
), OPT_STR
, 0,
567 "URL of played media's web page" },
568 { AVC("swfUrl"), OFF(Link
.swfUrl
), OPT_STR
, 0,
569 "URL to player SWF file" },
570 { AVC("flashver"), OFF(Link
.flashVer
), OPT_STR
, 0,
571 "Flash version string (default " DEF_VERSTR
")" },
572 { AVC("conn"), OFF(Link
.extras
), OPT_CONN
, 0,
573 "Append arbitrary AMF data to Connect message" },
574 { AVC("playpath"), OFF(Link
.playpath
), OPT_STR
, 0,
575 "Path to target media on server" },
576 { AVC("playlist"), OFF(Link
.lFlags
), OPT_BOOL
, RTMP_LF_PLST
,
577 "Set playlist before play command" },
578 { AVC("live"), OFF(Link
.lFlags
), OPT_BOOL
, RTMP_LF_LIVE
,
579 "Stream is live, no seeking possible" },
580 { AVC("subscribe"), OFF(Link
.subscribepath
), OPT_STR
, 0,
581 "Stream to subscribe to" },
582 { AVC("jtv"), OFF(Link
.usherToken
), OPT_STR
, 0,
583 "Justin.tv authentication token" },
584 { AVC("token"), OFF(Link
.token
), OPT_STR
, 0,
585 "Key for SecureToken response" },
586 { AVC("swfVfy"), OFF(Link
.lFlags
), OPT_BOOL
, RTMP_LF_SWFV
,
587 "Perform SWF Verification" },
588 { AVC("swfAge"), OFF(Link
.swfAge
), OPT_INT
, 0,
589 "Number of days to use cached SWF hash" },
590 { AVC("start"), OFF(Link
.seekTime
), OPT_INT
, 0,
591 "Stream start position in milliseconds" },
592 { AVC("stop"), OFF(Link
.stopTime
), OPT_INT
, 0,
593 "Stream stop position in milliseconds" },
594 { AVC("buffer"), OFF(m_nBufferMS
), OPT_INT
, 0,
595 "Buffer time in milliseconds" },
596 { AVC("timeout"), OFF(Link
.timeout
), OPT_INT
, 0,
597 "Session timeout in seconds" },
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
))
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
,
1215 if (bHasMediaPacket
)
1216 r
->m_bPlaying
= TRUE
;
1217 else if (r
->m_sb
.sb_timedout
&& !r
->m_pausing
)
1218 r
->m_pauseStamp
= r
->m_channelTimestamp
[r
->m_mediaChannel
];
1220 return bHasMediaPacket
;
1224 RTMP_ClientPacket(RTMP
*r
, RTMPPacket
*packet
)
1226 int bHasMediaPacket
= 0;
1227 switch (packet
->m_packetType
)
1229 case RTMP_PACKET_TYPE_CHUNK_SIZE
:
1231 HandleChangeChunkSize(r
, packet
);
1234 case RTMP_PACKET_TYPE_BYTES_READ_REPORT
:
1235 /* bytes read report */
1236 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: bytes read report", __FUNCTION__
);
1239 case RTMP_PACKET_TYPE_CONTROL
:
1241 HandleCtrl(r
, packet
);
1244 case RTMP_PACKET_TYPE_SERVER_BW
:
1246 HandleServerBW(r
, packet
);
1249 case RTMP_PACKET_TYPE_CLIENT_BW
:
1251 HandleClientBW(r
, packet
);
1254 case RTMP_PACKET_TYPE_AUDIO
:
1256 /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); */
1257 HandleAudio(r
, packet
);
1258 bHasMediaPacket
= 1;
1259 if (!r
->m_mediaChannel
)
1260 r
->m_mediaChannel
= packet
->m_nChannel
;
1262 r
->m_mediaStamp
= packet
->m_nTimeStamp
;
1265 case RTMP_PACKET_TYPE_VIDEO
:
1267 /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); */
1268 HandleVideo(r
, packet
);
1269 bHasMediaPacket
= 1;
1270 if (!r
->m_mediaChannel
)
1271 r
->m_mediaChannel
= packet
->m_nChannel
;
1273 r
->m_mediaStamp
= packet
->m_nTimeStamp
;
1276 case RTMP_PACKET_TYPE_FLEX_STREAM_SEND
:
1277 /* flex stream send */
1278 RTMP_Log(RTMP_LOGDEBUG
,
1279 "%s, flex stream send, size %u bytes, not supported, ignoring",
1280 __FUNCTION__
, packet
->m_nBodySize
);
1283 case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT
:
1284 /* flex shared object */
1285 RTMP_Log(RTMP_LOGDEBUG
,
1286 "%s, flex shared object, size %u bytes, not supported, ignoring",
1287 __FUNCTION__
, packet
->m_nBodySize
);
1290 case RTMP_PACKET_TYPE_FLEX_MESSAGE
:
1293 RTMP_Log(RTMP_LOGDEBUG
,
1294 "%s, flex message, size %u bytes, not fully supported",
1295 __FUNCTION__
, packet
->m_nBodySize
);
1296 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
1298 /* some DEBUG code */
1300 RTMP_LIB_AMFObject obj
;
1301 int nRes
= obj
.Decode(packet
.m_body
+1, packet
.m_nBodySize
-1);
1303 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding AMF3 packet", __FUNCTION__
);
1310 if (HandleInvoke(r
, packet
->m_body
+ 1, packet
->m_nBodySize
- 1) == 1)
1311 bHasMediaPacket
= 2;
1314 case RTMP_PACKET_TYPE_INFO
:
1315 /* metadata (notify) */
1316 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: notify %u bytes", __FUNCTION__
,
1317 packet
->m_nBodySize
);
1318 if (HandleMetadata(r
, packet
->m_body
, packet
->m_nBodySize
))
1319 bHasMediaPacket
= 1;
1322 case RTMP_PACKET_TYPE_SHARED_OBJECT
:
1323 RTMP_Log(RTMP_LOGDEBUG
, "%s, shared object, not supported, ignoring",
1327 case RTMP_PACKET_TYPE_INVOKE
:
1329 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: invoke %u bytes", __FUNCTION__
,
1330 packet
->m_nBodySize
);
1331 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
1333 if (HandleInvoke(r
, packet
->m_body
, packet
->m_nBodySize
) == 1)
1334 bHasMediaPacket
= 2;
1337 case RTMP_PACKET_TYPE_FLASH_VIDEO
:
1339 /* go through FLV packets and handle metadata packets */
1340 unsigned int pos
= 0;
1341 uint32_t nTimeStamp
= packet
->m_nTimeStamp
;
1343 while (pos
+ 11 < packet
->m_nBodySize
)
1345 uint32_t dataSize
= AMF_DecodeInt24(packet
->m_body
+ pos
+ 1); /* size without header (11) and prevTagSize (4) */
1347 if (pos
+ 11 + dataSize
+ 4 > packet
->m_nBodySize
)
1349 RTMP_Log(RTMP_LOGWARNING
, "Stream corrupt?!");
1352 if (packet
->m_body
[pos
] == 0x12)
1354 HandleMetadata(r
, packet
->m_body
+ pos
+ 11, dataSize
);
1356 else if (packet
->m_body
[pos
] == 8 || packet
->m_body
[pos
] == 9)
1358 nTimeStamp
= AMF_DecodeInt24(packet
->m_body
+ pos
+ 4);
1359 nTimeStamp
|= (packet
->m_body
[pos
+ 7] << 24);
1361 pos
+= (11 + dataSize
+ 4);
1364 r
->m_mediaStamp
= nTimeStamp
;
1367 /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: FLV tag(s) %lu bytes", __FUNCTION__, packet.m_nBodySize); */
1368 bHasMediaPacket
= 1;
1372 RTMP_Log(RTMP_LOGDEBUG
, "%s, unknown packet type received: 0x%02x", __FUNCTION__
,
1373 packet
->m_packetType
);
1375 RTMP_LogHex(RTMP_LOGDEBUG
, packet
->m_body
, packet
->m_nBodySize
);
1379 return bHasMediaPacket
;
1383 extern FILE *netstackdump
;
1384 extern FILE *netstackdump_read
;
1388 ReadN(RTMP
*r
, char *buffer
, int n
)
1390 int nOriginalSize
= n
;
1394 r
->m_sb
.sb_timedout
= FALSE
;
1397 memset(buffer
, 0, n
);
1403 int nBytes
= 0, nRead
;
1404 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
1407 while (!r
->m_resplen
)
1410 if (r
->m_sb
.sb_size
< 13 || refill
)
1413 HTTP_Post(r
, RTMPT_IDLE
, "", 1);
1414 if (RTMPSockBuf_Fill(&r
->m_sb
) < 1)
1416 if (!r
->m_sb
.sb_timedout
)
1421 if ((ret
= HTTP_read(r
, 0)) == -1)
1423 RTMP_Log(RTMP_LOGDEBUG
, "%s, No valid HTTP response found", __FUNCTION__
);
1436 if (r
->m_resplen
&& !r
->m_sb
.sb_size
)
1437 RTMPSockBuf_Fill(&r
->m_sb
);
1438 avail
= r
->m_sb
.sb_size
;
1439 if (avail
> r
->m_resplen
)
1440 avail
= r
->m_resplen
;
1444 avail
= r
->m_sb
.sb_size
;
1447 if (RTMPSockBuf_Fill(&r
->m_sb
) < 1)
1449 if (!r
->m_sb
.sb_timedout
)
1453 avail
= r
->m_sb
.sb_size
;
1456 nRead
= ((n
< avail
) ? n
: avail
);
1459 memcpy(ptr
, r
->m_sb
.sb_start
, nRead
);
1460 r
->m_sb
.sb_start
+= nRead
;
1461 r
->m_sb
.sb_size
-= nRead
;
1463 r
->m_nBytesIn
+= nRead
;
1464 if (r
->m_bSendCounter
1465 && r
->m_nBytesIn
> ( r
->m_nBytesInSent
+ r
->m_nClientBW
/ 10))
1466 if (!SendBytesReceived(r
))
1469 /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */
1471 fwrite(ptr
, 1, nBytes
, netstackdump_read
);
1476 RTMP_Log(RTMP_LOGDEBUG
, "%s, RTMP socket closed by peer", __FUNCTION__
);
1482 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
1483 r
->m_resplen
-= nBytes
;
1486 if (r
->Link
.rc4keyIn
)
1488 RC4_encrypt(r
->Link
.rc4keyIn
, nBytes
, ptr
);
1496 return nOriginalSize
- n
;
1500 WriteN(RTMP
*r
, const char *buffer
, int n
)
1502 const char *ptr
= buffer
;
1504 char *encrypted
= 0;
1505 char buf
[RTMP_BUFFER_CACHE_SIZE
];
1507 if (r
->Link
.rc4keyOut
)
1509 if (n
> sizeof(buf
))
1510 encrypted
= (char *)malloc(n
);
1512 encrypted
= (char *)buf
;
1514 RC4_encrypt2(r
->Link
.rc4keyOut
, n
, buffer
, ptr
);
1522 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
1523 nBytes
= HTTP_Post(r
, RTMPT_SEND
, ptr
, n
);
1525 nBytes
= RTMPSockBuf_Send(&r
->m_sb
, ptr
, n
);
1526 /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d\n", __FUNCTION__, nBytes); */
1530 int sockerr
= GetSockError();
1531 RTMP_Log(RTMP_LOGERROR
, "%s, RTMP send error %d (%d bytes)", __FUNCTION__
,
1534 if (sockerr
== EINTR
&& !RTMP_ctrlC
)
1550 if (encrypted
&& encrypted
!= buf
)
1557 #define SAVC(x) static const AVal av_##x = AVC(#x)
1569 SAVC(videoFunction
);
1570 SAVC(objectEncoding
);
1572 SAVC(secureTokenResponse
);
1577 SendConnectPacket(RTMP
*r
, RTMPPacket
*cp
)
1580 char pbuf
[4096], *pend
= pbuf
+ sizeof(pbuf
);
1584 return RTMP_SendPacket(r
, cp
, TRUE
);
1586 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1587 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
1588 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1589 packet
.m_nTimeStamp
= 0;
1590 packet
.m_nInfoField2
= 0;
1591 packet
.m_hasAbsTimestamp
= 0;
1592 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1594 enc
= packet
.m_body
;
1595 enc
= AMF_EncodeString(enc
, pend
, &av_connect
);
1596 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1597 *enc
++ = AMF_OBJECT
;
1599 enc
= AMF_EncodeNamedString(enc
, pend
, &av_app
, &r
->Link
.app
);
1602 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
1604 enc
= AMF_EncodeNamedString(enc
, pend
, &av_type
, &av_nonprivate
);
1608 if (r
->Link
.flashVer
.av_len
)
1610 enc
= AMF_EncodeNamedString(enc
, pend
, &av_flashVer
, &r
->Link
.flashVer
);
1614 if (r
->Link
.swfUrl
.av_len
)
1616 enc
= AMF_EncodeNamedString(enc
, pend
, &av_swfUrl
, &r
->Link
.swfUrl
);
1620 if (r
->Link
.tcUrl
.av_len
)
1622 enc
= AMF_EncodeNamedString(enc
, pend
, &av_tcUrl
, &r
->Link
.tcUrl
);
1626 if (!(r
->Link
.protocol
& RTMP_FEATURE_WRITE
))
1628 enc
= AMF_EncodeNamedBoolean(enc
, pend
, &av_fpad
, FALSE
);
1631 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_capabilities
, 15.0);
1634 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_audioCodecs
, r
->m_fAudioCodecs
);
1637 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_videoCodecs
, r
->m_fVideoCodecs
);
1640 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_videoFunction
, 1.0);
1643 if (r
->Link
.pageUrl
.av_len
)
1645 enc
= AMF_EncodeNamedString(enc
, pend
, &av_pageUrl
, &r
->Link
.pageUrl
);
1650 if (r
->m_fEncoding
!= 0.0 || r
->m_bSendEncoding
)
1651 { /* AMF0, AMF3 not fully supported yet */
1652 enc
= AMF_EncodeNamedNumber(enc
, pend
, &av_objectEncoding
, r
->m_fEncoding
);
1656 if (enc
+ 3 >= pend
)
1659 *enc
++ = 0; /* end of object - 0x00 0x00 0x09 */
1660 *enc
++ = AMF_OBJECT_END
;
1662 /* add auth string */
1663 if (r
->Link
.auth
.av_len
)
1665 enc
= AMF_EncodeBoolean(enc
, pend
, r
->Link
.lFlags
& RTMP_LF_AUTH
);
1668 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.auth
);
1672 if (r
->Link
.extras
.o_num
)
1675 for (i
= 0; i
< r
->Link
.extras
.o_num
; i
++)
1677 enc
= AMFProp_Encode(&r
->Link
.extras
.o_props
[i
], enc
, pend
);
1682 packet
.m_nBodySize
= enc
- packet
.m_body
;
1684 return RTMP_SendPacket(r
, &packet
, TRUE
);
1691 SendBGHasStream(RTMP
*r
, double dId
, AVal
*playpath
)
1694 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1697 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1698 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1699 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1700 packet
.m_nTimeStamp
= 0;
1701 packet
.m_nInfoField2
= 0;
1702 packet
.m_hasAbsTimestamp
= 0;
1703 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1705 enc
= packet
.m_body
;
1706 enc
= AMF_EncodeString(enc
, pend
, &av_bgHasStream
);
1707 enc
= AMF_EncodeNumber(enc
, pend
, dId
);
1710 enc
= AMF_EncodeString(enc
, pend
, playpath
);
1714 packet
.m_nBodySize
= enc
- packet
.m_body
;
1716 return RTMP_SendPacket(r
, &packet
, TRUE
);
1723 RTMP_SendCreateStream(RTMP
*r
)
1726 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1729 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1730 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1731 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1732 packet
.m_nTimeStamp
= 0;
1733 packet
.m_nInfoField2
= 0;
1734 packet
.m_hasAbsTimestamp
= 0;
1735 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1737 enc
= packet
.m_body
;
1738 enc
= AMF_EncodeString(enc
, pend
, &av_createStream
);
1739 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1740 *enc
++ = AMF_NULL
; /* NULL */
1742 packet
.m_nBodySize
= enc
- packet
.m_body
;
1744 return RTMP_SendPacket(r
, &packet
, TRUE
);
1750 SendFCSubscribe(RTMP
*r
, AVal
*subscribepath
)
1753 char pbuf
[512], *pend
= pbuf
+ sizeof(pbuf
);
1755 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1756 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1757 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1758 packet
.m_nTimeStamp
= 0;
1759 packet
.m_nInfoField2
= 0;
1760 packet
.m_hasAbsTimestamp
= 0;
1761 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1763 RTMP_Log(RTMP_LOGDEBUG
, "FCSubscribe: %s", subscribepath
->av_val
);
1764 enc
= packet
.m_body
;
1765 enc
= AMF_EncodeString(enc
, pend
, &av_FCSubscribe
);
1766 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1768 enc
= AMF_EncodeString(enc
, pend
, subscribepath
);
1773 packet
.m_nBodySize
= enc
- packet
.m_body
;
1775 return RTMP_SendPacket(r
, &packet
, TRUE
);
1778 /* Justin.tv specific authentication */
1779 static const AVal av_NetStream_Authenticate_UsherToken
= AVC("NetStream.Authenticate.UsherToken");
1782 SendUsherToken(RTMP
*r
, AVal
*usherToken
)
1785 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1787 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1788 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1789 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1790 packet
.m_nTimeStamp
= 0;
1791 packet
.m_nInfoField2
= 0;
1792 packet
.m_hasAbsTimestamp
= 0;
1793 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1795 RTMP_Log(RTMP_LOGDEBUG
, "UsherToken: %s", usherToken
->av_val
);
1796 enc
= packet
.m_body
;
1797 enc
= AMF_EncodeString(enc
, pend
, &av_NetStream_Authenticate_UsherToken
);
1798 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1800 enc
= AMF_EncodeString(enc
, pend
, usherToken
);
1805 packet
.m_nBodySize
= enc
- packet
.m_body
;
1807 return RTMP_SendPacket(r
, &packet
, FALSE
);
1809 /******************************************/
1811 SAVC(releaseStream
);
1814 SendReleaseStream(RTMP
*r
)
1817 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1820 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1821 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1822 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1823 packet
.m_nTimeStamp
= 0;
1824 packet
.m_nInfoField2
= 0;
1825 packet
.m_hasAbsTimestamp
= 0;
1826 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1828 enc
= packet
.m_body
;
1829 enc
= AMF_EncodeString(enc
, pend
, &av_releaseStream
);
1830 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1832 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1836 packet
.m_nBodySize
= enc
- packet
.m_body
;
1838 return RTMP_SendPacket(r
, &packet
, FALSE
);
1844 SendFCPublish(RTMP
*r
)
1847 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1850 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1851 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1852 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1853 packet
.m_nTimeStamp
= 0;
1854 packet
.m_nInfoField2
= 0;
1855 packet
.m_hasAbsTimestamp
= 0;
1856 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1858 enc
= packet
.m_body
;
1859 enc
= AMF_EncodeString(enc
, pend
, &av_FCPublish
);
1860 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1862 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1866 packet
.m_nBodySize
= enc
- packet
.m_body
;
1868 return RTMP_SendPacket(r
, &packet
, FALSE
);
1874 SendFCUnpublish(RTMP
*r
)
1877 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1880 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1881 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1882 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1883 packet
.m_nTimeStamp
= 0;
1884 packet
.m_nInfoField2
= 0;
1885 packet
.m_hasAbsTimestamp
= 0;
1886 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1888 enc
= packet
.m_body
;
1889 enc
= AMF_EncodeString(enc
, pend
, &av_FCUnpublish
);
1890 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1892 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1896 packet
.m_nBodySize
= enc
- packet
.m_body
;
1898 return RTMP_SendPacket(r
, &packet
, FALSE
);
1906 SendPublish(RTMP
*r
)
1909 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
1912 packet
.m_nChannel
= 0x04; /* source channel (invoke) */
1913 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
1914 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1915 packet
.m_nTimeStamp
= 0;
1916 packet
.m_nInfoField2
= r
->m_stream_id
;
1917 packet
.m_hasAbsTimestamp
= 0;
1918 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1920 enc
= packet
.m_body
;
1921 enc
= AMF_EncodeString(enc
, pend
, &av_publish
);
1922 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1924 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
1928 /* FIXME: should we choose live based on Link.lFlags & RTMP_LF_LIVE? */
1929 enc
= AMF_EncodeString(enc
, pend
, &av_live
);
1933 packet
.m_nBodySize
= enc
- packet
.m_body
;
1935 return RTMP_SendPacket(r
, &packet
, TRUE
);
1941 SendDeleteStream(RTMP
*r
, double dStreamId
)
1944 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1947 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
1948 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1949 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1950 packet
.m_nTimeStamp
= 0;
1951 packet
.m_nInfoField2
= 0;
1952 packet
.m_hasAbsTimestamp
= 0;
1953 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1955 enc
= packet
.m_body
;
1956 enc
= AMF_EncodeString(enc
, pend
, &av_deleteStream
);
1957 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1959 enc
= AMF_EncodeNumber(enc
, pend
, dStreamId
);
1961 packet
.m_nBodySize
= enc
- packet
.m_body
;
1963 /* no response expected */
1964 return RTMP_SendPacket(r
, &packet
, FALSE
);
1970 RTMP_SendPause(RTMP
*r
, int DoPause
, int iTime
)
1973 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
1976 packet
.m_nChannel
= 0x08; /* video channel */
1977 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
1978 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
1979 packet
.m_nTimeStamp
= 0;
1980 packet
.m_nInfoField2
= 0;
1981 packet
.m_hasAbsTimestamp
= 0;
1982 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
1984 enc
= packet
.m_body
;
1985 enc
= AMF_EncodeString(enc
, pend
, &av_pause
);
1986 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
1988 enc
= AMF_EncodeBoolean(enc
, pend
, DoPause
);
1989 enc
= AMF_EncodeNumber(enc
, pend
, (double)iTime
);
1991 packet
.m_nBodySize
= enc
- packet
.m_body
;
1993 RTMP_Log(RTMP_LOGDEBUG
, "%s, %d, pauseTime=%d", __FUNCTION__
, DoPause
, iTime
);
1994 return RTMP_SendPacket(r
, &packet
, TRUE
);
1997 int RTMP_Pause(RTMP
*r
, int DoPause
)
2000 r
->m_pauseStamp
= r
->m_channelTimestamp
[r
->m_mediaChannel
];
2001 return RTMP_SendPause(r
, DoPause
, r
->m_pauseStamp
);
2007 RTMP_SendSeek(RTMP
*r
, int iTime
)
2010 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2013 packet
.m_nChannel
= 0x08; /* video channel */
2014 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2015 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2016 packet
.m_nTimeStamp
= 0;
2017 packet
.m_nInfoField2
= 0;
2018 packet
.m_hasAbsTimestamp
= 0;
2019 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2021 enc
= packet
.m_body
;
2022 enc
= AMF_EncodeString(enc
, pend
, &av_seek
);
2023 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
2025 enc
= AMF_EncodeNumber(enc
, pend
, (double)iTime
);
2027 packet
.m_nBodySize
= enc
- packet
.m_body
;
2029 r
->m_read
.flags
|= RTMP_READ_SEEKING
;
2030 r
->m_read
.nResumeTS
= 0;
2032 return RTMP_SendPacket(r
, &packet
, TRUE
);
2036 RTMP_SendServerBW(RTMP
*r
)
2039 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2041 packet
.m_nChannel
= 0x02; /* control channel (invoke) */
2042 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2043 packet
.m_packetType
= RTMP_PACKET_TYPE_SERVER_BW
;
2044 packet
.m_nTimeStamp
= 0;
2045 packet
.m_nInfoField2
= 0;
2046 packet
.m_hasAbsTimestamp
= 0;
2047 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2049 packet
.m_nBodySize
= 4;
2051 AMF_EncodeInt32(packet
.m_body
, pend
, r
->m_nServerBW
);
2052 return RTMP_SendPacket(r
, &packet
, FALSE
);
2056 RTMP_SendClientBW(RTMP
*r
)
2059 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2061 packet
.m_nChannel
= 0x02; /* control channel (invoke) */
2062 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2063 packet
.m_packetType
= RTMP_PACKET_TYPE_CLIENT_BW
;
2064 packet
.m_nTimeStamp
= 0;
2065 packet
.m_nInfoField2
= 0;
2066 packet
.m_hasAbsTimestamp
= 0;
2067 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2069 packet
.m_nBodySize
= 5;
2071 AMF_EncodeInt32(packet
.m_body
, pend
, r
->m_nClientBW
);
2072 packet
.m_body
[4] = r
->m_nClientBW2
;
2073 return RTMP_SendPacket(r
, &packet
, FALSE
);
2077 SendBytesReceived(RTMP
*r
)
2080 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2082 packet
.m_nChannel
= 0x02; /* control channel (invoke) */
2083 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2084 packet
.m_packetType
= RTMP_PACKET_TYPE_BYTES_READ_REPORT
;
2085 packet
.m_nTimeStamp
= 0;
2086 packet
.m_nInfoField2
= 0;
2087 packet
.m_hasAbsTimestamp
= 0;
2088 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2090 packet
.m_nBodySize
= 4;
2092 AMF_EncodeInt32(packet
.m_body
, pend
, r
->m_nBytesIn
); /* hard coded for now */
2093 r
->m_nBytesInSent
= r
->m_nBytesIn
;
2095 /*RTMP_Log(RTMP_LOGDEBUG, "Send bytes report. 0x%x (%d bytes)", (unsigned int)m_nBytesIn, m_nBytesIn); */
2096 return RTMP_SendPacket(r
, &packet
, FALSE
);
2102 SendCheckBW(RTMP
*r
)
2105 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2108 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2109 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2110 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2111 packet
.m_nTimeStamp
= 0; /* RTMP_GetTime(); */
2112 packet
.m_nInfoField2
= 0;
2113 packet
.m_hasAbsTimestamp
= 0;
2114 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2116 enc
= packet
.m_body
;
2117 enc
= AMF_EncodeString(enc
, pend
, &av__checkbw
);
2118 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
2121 packet
.m_nBodySize
= enc
- packet
.m_body
;
2123 /* triggers _onbwcheck and eventually results in _onbwdone */
2124 return RTMP_SendPacket(r
, &packet
, FALSE
);
2130 SendCheckBWResult(RTMP
*r
, double txn
)
2133 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2136 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2137 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2138 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2139 packet
.m_nTimeStamp
= 0x16 * r
->m_nBWCheckCounter
; /* temp inc value. till we figure it out. */
2140 packet
.m_nInfoField2
= 0;
2141 packet
.m_hasAbsTimestamp
= 0;
2142 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2144 enc
= packet
.m_body
;
2145 enc
= AMF_EncodeString(enc
, pend
, &av__result
);
2146 enc
= AMF_EncodeNumber(enc
, pend
, txn
);
2148 enc
= AMF_EncodeNumber(enc
, pend
, (double)r
->m_nBWCheckCounter
++);
2150 packet
.m_nBodySize
= enc
- packet
.m_body
;
2152 return RTMP_SendPacket(r
, &packet
, FALSE
);
2159 SendPong(RTMP
*r
, double txn
)
2162 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2165 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2166 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2167 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2168 packet
.m_nTimeStamp
= 0x16 * r
->m_nBWCheckCounter
; /* temp inc value. till we figure it out. */
2169 packet
.m_nInfoField2
= 0;
2170 packet
.m_hasAbsTimestamp
= 0;
2171 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2173 enc
= packet
.m_body
;
2174 enc
= AMF_EncodeString(enc
, pend
, &av_pong
);
2175 enc
= AMF_EncodeNumber(enc
, pend
, txn
);
2178 packet
.m_nBodySize
= enc
- packet
.m_body
;
2180 return RTMP_SendPacket(r
, &packet
, FALSE
);
2189 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
2192 packet
.m_nChannel
= 0x08; /* we make 8 our stream channel */
2193 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2194 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2195 packet
.m_nTimeStamp
= 0;
2196 packet
.m_nInfoField2
= r
->m_stream_id
; /*0x01000000; */
2197 packet
.m_hasAbsTimestamp
= 0;
2198 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2200 enc
= packet
.m_body
;
2201 enc
= AMF_EncodeString(enc
, pend
, &av_play
);
2202 enc
= AMF_EncodeNumber(enc
, pend
, ++r
->m_numInvokes
);
2205 RTMP_Log(RTMP_LOGDEBUG
, "%s, seekTime=%d, stopTime=%d, sending play: %s",
2206 __FUNCTION__
, r
->Link
.seekTime
, r
->Link
.stopTime
,
2207 r
->Link
.playpath
.av_val
);
2208 enc
= AMF_EncodeString(enc
, pend
, &r
->Link
.playpath
);
2212 /* Optional parameters start and len.
2214 * start: -2, -1, 0, positive number
2215 * -2: looks for a live stream, then a recorded stream,
2216 * if not found any open a live stream
2217 * -1: plays a live stream
2218 * >=0: plays a recorded streams from 'start' milliseconds
2220 if (r
->Link
.lFlags
& RTMP_LF_LIVE
)
2221 enc
= AMF_EncodeNumber(enc
, pend
, -1000.0);
2224 if (r
->Link
.seekTime
> 0.0)
2225 enc
= AMF_EncodeNumber(enc
, pend
, r
->Link
.seekTime
); /* resume from here */
2227 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 */
2232 /* len: -1, 0, positive number
2233 * -1: plays live or recorded stream to the end (default)
2234 * 0: plays a frame 'start' ms away from the beginning
2235 * >0: plays a live or recoded stream for 'len' milliseconds
2237 /*enc += EncodeNumber(enc, -1.0); */ /* len */
2238 if (r
->Link
.stopTime
)
2240 enc
= AMF_EncodeNumber(enc
, pend
, r
->Link
.stopTime
- r
->Link
.seekTime
);
2245 packet
.m_nBodySize
= enc
- packet
.m_body
;
2247 return RTMP_SendPacket(r
, &packet
, TRUE
);
2254 SendPlaylist(RTMP
*r
)
2257 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
2260 packet
.m_nChannel
= 0x08; /* we make 8 our stream channel */
2261 packet
.m_headerType
= RTMP_PACKET_SIZE_LARGE
;
2262 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2263 packet
.m_nTimeStamp
= 0;
2264 packet
.m_nInfoField2
= r
->m_stream_id
; /*0x01000000; */
2265 packet
.m_hasAbsTimestamp
= 0;
2266 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2268 enc
= packet
.m_body
;
2269 enc
= AMF_EncodeString(enc
, pend
, &av_set_playlist
);
2270 enc
= AMF_EncodeNumber(enc
, pend
, 0);
2272 *enc
++ = AMF_ECMA_ARRAY
;
2276 *enc
++ = AMF_OBJECT
;
2277 enc
= AMF_EncodeNamedString(enc
, pend
, &av_0
, &r
->Link
.playpath
);
2280 if (enc
+ 3 >= pend
)
2284 *enc
++ = AMF_OBJECT_END
;
2286 packet
.m_nBodySize
= enc
- packet
.m_body
;
2288 return RTMP_SendPacket(r
, &packet
, TRUE
);
2292 SendSecureTokenResponse(RTMP
*r
, AVal
*resp
)
2295 char pbuf
[1024], *pend
= pbuf
+ sizeof(pbuf
);
2298 packet
.m_nChannel
= 0x03; /* control channel (invoke) */
2299 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2300 packet
.m_packetType
= RTMP_PACKET_TYPE_INVOKE
;
2301 packet
.m_nTimeStamp
= 0;
2302 packet
.m_nInfoField2
= 0;
2303 packet
.m_hasAbsTimestamp
= 0;
2304 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2306 enc
= packet
.m_body
;
2307 enc
= AMF_EncodeString(enc
, pend
, &av_secureTokenResponse
);
2308 enc
= AMF_EncodeNumber(enc
, pend
, 0.0);
2310 enc
= AMF_EncodeString(enc
, pend
, resp
);
2314 packet
.m_nBodySize
= enc
- packet
.m_body
;
2316 return RTMP_SendPacket(r
, &packet
, FALSE
);
2320 from http://jira.red5.org/confluence/display/docs/Ping:
2322 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.
2324 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.
2326 * 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.
2327 * type 1: Tell the stream to clear the playing buffer.
2328 * type 3: Buffer time of the client. The third parameter is the buffer time in millisecond.
2329 * type 4: Reset a stream. Used together with type 0 in the case of VOD. Often sent before type 0.
2330 * type 6: Ping the client from server. The second parameter is the current time.
2331 * type 7: Pong reply from client. The second parameter is the time the server sent with his ping request.
2332 * type 26: SWFVerification request
2333 * type 27: SWFVerification response
2336 RTMP_SendCtrl(RTMP
*r
, short nType
, unsigned int nObject
, unsigned int nTime
)
2339 char pbuf
[256], *pend
= pbuf
+ sizeof(pbuf
);
2343 RTMP_Log(RTMP_LOGDEBUG
, "sending ctrl. type: 0x%04x", (unsigned short)nType
);
2345 packet
.m_nChannel
= 0x02; /* control channel (ping) */
2346 packet
.m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
2347 packet
.m_packetType
= RTMP_PACKET_TYPE_CONTROL
;
2348 packet
.m_nTimeStamp
= 0; /* RTMP_GetTime(); */
2349 packet
.m_nInfoField2
= 0;
2350 packet
.m_hasAbsTimestamp
= 0;
2351 packet
.m_body
= pbuf
+ RTMP_MAX_HEADER_SIZE
;
2354 case 0x03: nSize
= 10; break; /* buffer time */
2355 case 0x1A: nSize
= 3; break; /* SWF verify request */
2356 case 0x1B: nSize
= 44; break; /* SWF verify response */
2357 default: nSize
= 6; break;
2360 packet
.m_nBodySize
= nSize
;
2362 buf
= packet
.m_body
;
2363 buf
= AMF_EncodeInt16(buf
, pend
, nType
);
2368 memcpy(buf
, r
->Link
.SWFVerificationResponse
, 42);
2369 RTMP_Log(RTMP_LOGDEBUG
, "Sending SWFVerification response: ");
2370 RTMP_LogHex(RTMP_LOGDEBUG
, (uint8_t *)packet
.m_body
, packet
.m_nBodySize
);
2373 else if (nType
== 0x1A)
2375 *buf
= nObject
& 0xff;
2380 buf
= AMF_EncodeInt32(buf
, pend
, nObject
);
2383 buf
= AMF_EncodeInt32(buf
, pend
, nTime
);
2386 return RTMP_SendPacket(r
, &packet
, FALSE
);
2390 AV_erase(RTMP_METHOD
*vals
, int *num
, int i
, int freeit
)
2393 free(vals
[i
].name
.av_val
);
2395 for (; i
< *num
; i
++)
2397 vals
[i
] = vals
[i
+ 1];
2399 vals
[i
].name
.av_val
= NULL
;
2400 vals
[i
].name
.av_len
= 0;
2405 RTMP_DropRequest(RTMP
*r
, int i
, int freeit
)
2407 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, freeit
);
2411 AV_queue(RTMP_METHOD
**vals
, int *num
, AVal
*av
, int txn
)
2415 *vals
= realloc(*vals
, (*num
+ 16) * sizeof(RTMP_METHOD
));
2416 tmp
= malloc(av
->av_len
+ 1);
2417 memcpy(tmp
, av
->av_val
, av
->av_len
);
2418 tmp
[av
->av_len
] = '\0';
2419 (*vals
)[*num
].num
= txn
;
2420 (*vals
)[*num
].name
.av_len
= av
->av_len
;
2421 (*vals
)[(*num
)++].name
.av_val
= tmp
;
2425 AV_clear(RTMP_METHOD
*vals
, int num
)
2428 for (i
= 0; i
< num
; i
++)
2429 free(vals
[i
].name
.av_val
);
2434 SAVC(onFCSubscribe
);
2435 SAVC(onFCUnsubscribe
);
2443 SAVC(playlist_ready
);
2444 static const AVal av_NetStream_Failed
= AVC("NetStream.Failed");
2445 static const AVal av_NetStream_Play_Failed
= AVC("NetStream.Play.Failed");
2446 static const AVal av_NetStream_Play_StreamNotFound
=
2447 AVC("NetStream.Play.StreamNotFound");
2448 static const AVal av_NetConnection_Connect_InvalidApp
=
2449 AVC("NetConnection.Connect.InvalidApp");
2450 static const AVal av_NetStream_Play_Start
= AVC("NetStream.Play.Start");
2451 static const AVal av_NetStream_Play_Complete
= AVC("NetStream.Play.Complete");
2452 static const AVal av_NetStream_Play_Stop
= AVC("NetStream.Play.Stop");
2453 static const AVal av_NetStream_Seek_Notify
= AVC("NetStream.Seek.Notify");
2454 static const AVal av_NetStream_Pause_Notify
= AVC("NetStream.Pause.Notify");
2455 static const AVal av_NetStream_Play_PublishNotify
=
2456 AVC("NetStream.Play.PublishNotify");
2457 static const AVal av_NetStream_Play_UnpublishNotify
=
2458 AVC("NetStream.Play.UnpublishNotify");
2459 static const AVal av_NetStream_Publish_Start
= AVC("NetStream.Publish.Start");
2461 /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */
2463 HandleInvoke(RTMP
*r
, const char *body
, unsigned int nBodySize
)
2469 if (body
[0] != 0x02) /* make sure it is a string method name we start with */
2471 RTMP_Log(RTMP_LOGWARNING
, "%s, Sanity failed. no string method in invoke packet",
2476 nRes
= AMF_Decode(&obj
, body
, nBodySize
, FALSE
);
2479 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding invoke packet", __FUNCTION__
);
2484 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 0), &method
);
2485 txn
= AMFProp_GetNumber(AMF_GetProp(&obj
, NULL
, 1));
2486 RTMP_Log(RTMP_LOGDEBUG
, "%s, server invoking <%s>", __FUNCTION__
, method
.av_val
);
2488 if (AVMATCH(&method
, &av__result
))
2490 AVal methodInvoked
= {0};
2493 for (i
=0; i
<r
->m_numCalls
; i
++) {
2494 if (r
->m_methodCalls
[i
].num
== (int)txn
) {
2495 methodInvoked
= r
->m_methodCalls
[i
].name
;
2496 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, FALSE
);
2500 if (!methodInvoked
.av_val
) {
2501 RTMP_Log(RTMP_LOGDEBUG
, "%s, received result id %f without matching request",
2506 RTMP_Log(RTMP_LOGDEBUG
, "%s, received result for method call <%s>", __FUNCTION__
,
2507 methodInvoked
.av_val
);
2509 if (AVMATCH(&methodInvoked
, &av_connect
))
2511 if (r
->Link
.token
.av_len
)
2513 AMFObjectProperty p
;
2514 if (RTMP_FindFirstMatchingProperty(&obj
, &av_secureToken
, &p
))
2516 DecodeTEA(&r
->Link
.token
, &p
.p_vu
.p_aval
);
2517 SendSecureTokenResponse(r
, &p
.p_vu
.p_aval
);
2520 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
2522 SendReleaseStream(r
);
2527 RTMP_SendServerBW(r
);
2528 RTMP_SendCtrl(r
, 3, 0, 300);
2530 RTMP_SendCreateStream(r
);
2532 if (!(r
->Link
.protocol
& RTMP_FEATURE_WRITE
))
2534 /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */
2535 if (r
->Link
.usherToken
.av_len
)
2536 SendUsherToken(r
, &r
->Link
.usherToken
);
2537 /* Send the FCSubscribe if live stream or if subscribepath is set */
2538 if (r
->Link
.subscribepath
.av_len
)
2539 SendFCSubscribe(r
, &r
->Link
.subscribepath
);
2540 else if (r
->Link
.lFlags
& RTMP_LF_LIVE
)
2541 SendFCSubscribe(r
, &r
->Link
.playpath
);
2544 else if (AVMATCH(&methodInvoked
, &av_createStream
))
2546 r
->m_stream_id
= (int)AMFProp_GetNumber(AMF_GetProp(&obj
, NULL
, 3));
2548 if (r
->Link
.protocol
& RTMP_FEATURE_WRITE
)
2554 if (r
->Link
.lFlags
& RTMP_LF_PLST
)
2557 RTMP_SendCtrl(r
, 3, r
->m_stream_id
, r
->m_nBufferMS
);
2560 else if (AVMATCH(&methodInvoked
, &av_play
) ||
2561 AVMATCH(&methodInvoked
, &av_publish
))
2563 r
->m_bPlaying
= TRUE
;
2565 free(methodInvoked
.av_val
);
2567 else if (AVMATCH(&method
, &av_onBWDone
))
2569 if (!r
->m_nBWCheckCounter
)
2572 else if (AVMATCH(&method
, &av_onFCSubscribe
))
2574 /* SendOnFCSubscribe(); */
2576 else if (AVMATCH(&method
, &av_onFCUnsubscribe
))
2581 else if (AVMATCH(&method
, &av_ping
))
2585 else if (AVMATCH(&method
, &av__onbwcheck
))
2587 SendCheckBWResult(r
, txn
);
2589 else if (AVMATCH(&method
, &av__onbwdone
))
2592 for (i
= 0; i
< r
->m_numCalls
; i
++)
2593 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av__checkbw
))
2595 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
2599 else if (AVMATCH(&method
, &av__error
))
2601 RTMP_Log(RTMP_LOGERROR
, "rtmp server sent error");
2603 else if (AVMATCH(&method
, &av_close
))
2605 RTMP_Log(RTMP_LOGERROR
, "rtmp server requested close");
2608 else if (AVMATCH(&method
, &av_onStatus
))
2612 AMFProp_GetObject(AMF_GetProp(&obj
, NULL
, 3), &obj2
);
2613 AMFProp_GetString(AMF_GetProp(&obj2
, &av_code
, -1), &code
);
2614 AMFProp_GetString(AMF_GetProp(&obj2
, &av_level
, -1), &level
);
2616 RTMP_Log(RTMP_LOGDEBUG
, "%s, onStatus: %s", __FUNCTION__
, code
.av_val
);
2617 if (AVMATCH(&code
, &av_NetStream_Failed
)
2618 || AVMATCH(&code
, &av_NetStream_Play_Failed
)
2619 || AVMATCH(&code
, &av_NetStream_Play_StreamNotFound
)
2620 || AVMATCH(&code
, &av_NetConnection_Connect_InvalidApp
))
2622 r
->m_stream_id
= -1;
2624 RTMP_Log(RTMP_LOGERROR
, "Closing connection: %s", code
.av_val
);
2627 else if (AVMATCH(&code
, &av_NetStream_Play_Start
)
2628 || AVMATCH(&code
, &av_NetStream_Play_PublishNotify
))
2631 r
->m_bPlaying
= TRUE
;
2632 for (i
= 0; i
< r
->m_numCalls
; i
++)
2634 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av_play
))
2636 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
2642 else if (AVMATCH(&code
, &av_NetStream_Publish_Start
))
2645 r
->m_bPlaying
= TRUE
;
2646 for (i
= 0; i
< r
->m_numCalls
; i
++)
2648 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av_publish
))
2650 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
2656 /* Return 1 if this is a Play.Complete or Play.Stop */
2657 else if (AVMATCH(&code
, &av_NetStream_Play_Complete
)
2658 || AVMATCH(&code
, &av_NetStream_Play_Stop
)
2659 || AVMATCH(&code
, &av_NetStream_Play_UnpublishNotify
))
2665 else if (AVMATCH(&code
, &av_NetStream_Seek_Notify
))
2667 r
->m_read
.flags
&= ~RTMP_READ_SEEKING
;
2670 else if (AVMATCH(&code
, &av_NetStream_Pause_Notify
))
2672 if (r
->m_pausing
== 1 || r
->m_pausing
== 2)
2674 RTMP_SendPause(r
, FALSE
, r
->m_pauseStamp
);
2679 else if (AVMATCH(&method
, &av_playlist_ready
))
2682 for (i
= 0; i
< r
->m_numCalls
; i
++)
2684 if (AVMATCH(&r
->m_methodCalls
[i
].name
, &av_set_playlist
))
2686 AV_erase(r
->m_methodCalls
, &r
->m_numCalls
, i
, TRUE
);
2701 RTMP_FindFirstMatchingProperty(AMFObject
*obj
, const AVal
*name
,
2702 AMFObjectProperty
* p
)
2705 /* this is a small object search to locate the "duration" property */
2706 for (n
= 0; n
< obj
->o_num
; n
++)
2708 AMFObjectProperty
*prop
= AMF_GetProp(obj
, NULL
, n
);
2710 if (AVMATCH(&prop
->p_name
, name
))
2712 memcpy(p
, prop
, sizeof(*prop
));
2716 if (prop
->p_type
== AMF_OBJECT
)
2718 if (RTMP_FindFirstMatchingProperty(&prop
->p_vu
.p_object
, name
, p
))
2725 /* Like above, but only check if name is a prefix of property */
2727 RTMP_FindPrefixProperty(AMFObject
*obj
, const AVal
*name
,
2728 AMFObjectProperty
* p
)
2731 for (n
= 0; n
< obj
->o_num
; n
++)
2733 AMFObjectProperty
*prop
= AMF_GetProp(obj
, NULL
, n
);
2735 if (prop
->p_name
.av_len
> name
->av_len
&&
2736 !memcmp(prop
->p_name
.av_val
, name
->av_val
, name
->av_len
))
2738 memcpy(p
, prop
, sizeof(*prop
));
2742 if (prop
->p_type
== AMF_OBJECT
)
2744 if (RTMP_FindPrefixProperty(&prop
->p_vu
.p_object
, name
, p
))
2752 DumpMetaData(AMFObject
*obj
)
2754 AMFObjectProperty
*prop
;
2756 for (n
= 0; n
< obj
->o_num
; n
++)
2758 prop
= AMF_GetProp(obj
, NULL
, n
);
2759 if (prop
->p_type
!= AMF_OBJECT
)
2762 switch (prop
->p_type
)
2765 snprintf(str
, 255, "%.2f", prop
->p_vu
.p_number
);
2768 snprintf(str
, 255, "%s",
2769 prop
->p_vu
.p_number
!= 0. ? "TRUE" : "FALSE");
2772 snprintf(str
, 255, "%.*s", prop
->p_vu
.p_aval
.av_len
,
2773 prop
->p_vu
.p_aval
.av_val
);
2776 snprintf(str
, 255, "timestamp:%.2f", prop
->p_vu
.p_number
);
2779 snprintf(str
, 255, "INVALID TYPE 0x%02x",
2780 (unsigned char)prop
->p_type
);
2782 if (prop
->p_name
.av_len
)
2785 if (strlen(str
) >= 1 && str
[strlen(str
) - 1] == '\n')
2786 str
[strlen(str
) - 1] = '\0';
2787 RTMP_Log(RTMP_LOGINFO
, " %-22.*s%s", prop
->p_name
.av_len
,
2788 prop
->p_name
.av_val
, str
);
2793 if (prop
->p_name
.av_len
)
2794 RTMP_Log(RTMP_LOGINFO
, "%.*s:", prop
->p_name
.av_len
, prop
->p_name
.av_val
);
2795 DumpMetaData(&prop
->p_vu
.p_object
);
2807 HandleMetadata(RTMP
*r
, char *body
, unsigned int len
)
2809 /* allright we get some info here, so parse it and print it */
2810 /* also keep duration or filesize to make a nice progress bar */
2816 int nRes
= AMF_Decode(&obj
, body
, len
, FALSE
);
2819 RTMP_Log(RTMP_LOGERROR
, "%s, error decoding meta data packet", __FUNCTION__
);
2824 AMFProp_GetString(AMF_GetProp(&obj
, NULL
, 0), &metastring
);
2826 if (AVMATCH(&metastring
, &av_onMetaData
))
2828 AMFObjectProperty prop
;
2830 RTMP_Log(RTMP_LOGINFO
, "Metadata:");
2832 if (RTMP_FindFirstMatchingProperty(&obj
, &av_duration
, &prop
))
2834 r
->m_fDuration
= prop
.p_vu
.p_number
;
2835 /*RTMP_Log(RTMP_LOGDEBUG, "Set duration: %.2f", m_fDuration); */
2837 /* Search for audio or video tags */
2838 if (RTMP_FindPrefixProperty(&obj
, &av_video
, &prop
))
2839 r
->m_read
.dataType
|= 1;
2840 if (RTMP_FindPrefixProperty(&obj
, &av_audio
, &prop
))
2841 r
->m_read
.dataType
|= 4;
2849 HandleChangeChunkSize(RTMP
*r
, const RTMPPacket
*packet
)
2851 if (packet
->m_nBodySize
>= 4)
2853 r
->m_inChunkSize
= AMF_DecodeInt32(packet
->m_body
);
2854 RTMP_Log(RTMP_LOGDEBUG
, "%s, received: chunk size change to %d", __FUNCTION__
,
2860 HandleAudio(RTMP
*r
, const RTMPPacket
*packet
)
2865 HandleVideo(RTMP
*r
, const RTMPPacket
*packet
)
2870 HandleCtrl(RTMP
*r
, const RTMPPacket
*packet
)
2874 if (packet
->m_body
&& packet
->m_nBodySize
>= 2)
2875 nType
= AMF_DecodeInt16(packet
->m_body
);
2876 RTMP_Log(RTMP_LOGDEBUG
, "%s, received ctrl. type: %d, len: %d", __FUNCTION__
, nType
,
2877 packet
->m_nBodySize
);
2878 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
2880 if (packet
->m_nBodySize
>= 6)
2885 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2886 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream Begin %d", __FUNCTION__
, tmp
);
2890 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2891 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream EOF %d", __FUNCTION__
, tmp
);
2892 if (r
->m_pausing
== 1)
2897 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2898 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream Dry %d", __FUNCTION__
, tmp
);
2902 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2903 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream IsRecorded %d", __FUNCTION__
, tmp
);
2906 case 6: /* server ping. reply with pong. */
2907 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2908 RTMP_Log(RTMP_LOGDEBUG
, "%s, Ping %d", __FUNCTION__
, tmp
);
2909 RTMP_SendCtrl(r
, 0x07, tmp
, 0);
2912 /* FMS 3.5 servers send the following two controls to let the client
2913 * know when the server has sent a complete buffer. I.e., when the
2914 * server has sent an amount of data equal to m_nBufferMS in duration.
2915 * The server meters its output so that data arrives at the client
2916 * in realtime and no faster.
2918 * The rtmpdump program tries to set m_nBufferMS as large as
2919 * possible, to force the server to send data as fast as possible.
2920 * In practice, the server appears to cap this at about 1 hour's
2921 * worth of data. After the server has sent a complete buffer, and
2922 * sends this BufferEmpty message, it will wait until the play
2923 * duration of that buffer has passed before sending a new buffer.
2924 * The BufferReady message will be sent when the new buffer starts.
2925 * (There is no BufferReady message for the very first buffer;
2926 * presumably the Stream Begin message is sufficient for that
2929 * If the network speed is much faster than the data bitrate, then
2930 * there may be long delays between the end of one buffer and the
2931 * start of the next.
2933 * Since usually the network allows data to be sent at
2934 * faster than realtime, and rtmpdump wants to download the data
2935 * as fast as possible, we use this RTMP_LF_BUFX hack: when we
2936 * get the BufferEmpty message, we send a Pause followed by an
2937 * Unpause. This causes the server to send the next buffer immediately
2938 * instead of waiting for the full duration to elapse. (That's
2939 * also the purpose of the ToggleStream function, which rtmpdump
2940 * calls if we get a read timeout.)
2942 * Media player apps don't need this hack since they are just
2943 * going to play the data in realtime anyway. It also doesn't work
2944 * for live streams since they obviously can only be sent in
2945 * realtime. And it's all moot if the network speed is actually
2946 * slower than the media bitrate.
2949 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2950 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream BufferEmpty %d", __FUNCTION__
, tmp
);
2951 if (!(r
->Link
.lFlags
& RTMP_LF_BUFX
))
2955 r
->m_pauseStamp
= r
->m_channelTimestamp
[r
->m_mediaChannel
];
2956 RTMP_SendPause(r
, TRUE
, r
->m_pauseStamp
);
2959 else if (r
->m_pausing
== 2)
2961 RTMP_SendPause(r
, FALSE
, r
->m_pauseStamp
);
2967 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2968 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream BufferReady %d", __FUNCTION__
, tmp
);
2972 tmp
= AMF_DecodeInt32(packet
->m_body
+ 2);
2973 RTMP_Log(RTMP_LOGDEBUG
, "%s, Stream xx %d", __FUNCTION__
, tmp
);
2981 RTMP_Log(RTMP_LOGDEBUG
, "%s, SWFVerification ping received: ", __FUNCTION__
);
2982 if (packet
->m_nBodySize
> 2 && packet
->m_body
[2] > 0x01)
2984 RTMP_Log(RTMP_LOGERROR
,
2985 "%s: SWFVerification Type %d request not supported! Patches welcome...",
2986 __FUNCTION__
, packet
->m_body
[2]);
2989 /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
2991 /* respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied */
2992 else if (r
->Link
.SWFSize
)
2994 RTMP_SendCtrl(r
, 0x1B, 0, 0);
2998 RTMP_Log(RTMP_LOGERROR
,
2999 "%s: Ignoring SWFVerification request, use --swfVfy!",
3003 RTMP_Log(RTMP_LOGERROR
,
3004 "%s: Ignoring SWFVerification request, no CRYPTO support!",
3011 HandleServerBW(RTMP
*r
, const RTMPPacket
*packet
)
3013 r
->m_nServerBW
= AMF_DecodeInt32(packet
->m_body
);
3014 RTMP_Log(RTMP_LOGDEBUG
, "%s: server BW = %d", __FUNCTION__
, r
->m_nServerBW
);
3018 HandleClientBW(RTMP
*r
, const RTMPPacket
*packet
)
3020 r
->m_nClientBW
= AMF_DecodeInt32(packet
->m_body
);
3021 if (packet
->m_nBodySize
> 4)
3022 r
->m_nClientBW2
= packet
->m_body
[4];
3024 r
->m_nClientBW2
= -1;
3025 RTMP_Log(RTMP_LOGDEBUG
, "%s: client BW = %d %d", __FUNCTION__
, r
->m_nClientBW
,
3030 DecodeInt32LE(const char *data
)
3032 unsigned char *c
= (unsigned char *)data
;
3035 val
= (c
[3] << 24) | (c
[2] << 16) | (c
[1] << 8) | c
[0];
3040 EncodeInt32LE(char *output
, int nVal
)
3053 RTMP_ReadPacket(RTMP
*r
, RTMPPacket
*packet
)
3055 uint8_t hbuf
[RTMP_MAX_HEADER_SIZE
] = { 0 };
3056 char *header
= (char *)hbuf
;
3057 int nSize
, hSize
, nToRead
, nChunk
;
3058 int didAlloc
= FALSE
;
3060 RTMP_Log(RTMP_LOGDEBUG2
, "%s: fd=%d", __FUNCTION__
, r
->m_sb
.sb_socket
);
3062 if (ReadN(r
, (char *)hbuf
, 1) == 0)
3064 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header", __FUNCTION__
);
3068 packet
->m_headerType
= (hbuf
[0] & 0xc0) >> 6;
3069 packet
->m_nChannel
= (hbuf
[0] & 0x3f);
3071 if (packet
->m_nChannel
== 0)
3073 if (ReadN(r
, (char *)&hbuf
[1], 1) != 1)
3075 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header 2nd byte",
3079 packet
->m_nChannel
= hbuf
[1];
3080 packet
->m_nChannel
+= 64;
3083 else if (packet
->m_nChannel
== 1)
3086 if (ReadN(r
, (char *)&hbuf
[1], 2) != 2)
3088 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header 3nd byte",
3092 tmp
= (hbuf
[2] << 8) + hbuf
[1];
3093 packet
->m_nChannel
= tmp
+ 64;
3094 RTMP_Log(RTMP_LOGDEBUG
, "%s, m_nChannel: %0x", __FUNCTION__
, packet
->m_nChannel
);
3098 nSize
= packetSize
[packet
->m_headerType
];
3100 if (nSize
== RTMP_LARGE_HEADER_SIZE
) /* if we get a full header the timestamp is absolute */
3101 packet
->m_hasAbsTimestamp
= TRUE
;
3103 else if (nSize
< RTMP_LARGE_HEADER_SIZE
)
3104 { /* using values from the last message of this channel */
3105 if (r
->m_vecChannelsIn
[packet
->m_nChannel
])
3106 memcpy(packet
, r
->m_vecChannelsIn
[packet
->m_nChannel
],
3107 sizeof(RTMPPacket
));
3112 if (nSize
> 0 && ReadN(r
, header
, nSize
) != nSize
)
3114 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet header. type: %x",
3115 __FUNCTION__
, (unsigned int)hbuf
[0]);
3119 hSize
= nSize
+ (header
- (char *)hbuf
);
3123 packet
->m_nTimeStamp
= AMF_DecodeInt24(header
);
3125 /*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); */
3129 packet
->m_nBodySize
= AMF_DecodeInt24(header
+ 3);
3130 packet
->m_nBytesRead
= 0;
3131 RTMPPacket_Free(packet
);
3135 packet
->m_packetType
= header
[6];
3138 packet
->m_nInfoField2
= DecodeInt32LE(header
+ 7);
3141 if (packet
->m_nTimeStamp
== 0xffffff)
3143 if (ReadN(r
, header
+ nSize
, 4) != 4)
3145 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read extended timestamp",
3149 packet
->m_nTimeStamp
= AMF_DecodeInt32(header
+ nSize
);
3154 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)hbuf
, hSize
);
3156 if (packet
->m_nBodySize
> 0 && packet
->m_body
== NULL
)
3158 if (!RTMPPacket_Alloc(packet
, packet
->m_nBodySize
))
3160 RTMP_Log(RTMP_LOGDEBUG
, "%s, failed to allocate packet", __FUNCTION__
);
3164 packet
->m_headerType
= (hbuf
[0] & 0xc0) >> 6;
3167 nToRead
= packet
->m_nBodySize
- packet
->m_nBytesRead
;
3168 nChunk
= r
->m_inChunkSize
;
3169 if (nToRead
< nChunk
)
3172 /* Does the caller want the raw chunk? */
3173 if (packet
->m_chunk
)
3175 packet
->m_chunk
->c_headerSize
= hSize
;
3176 memcpy(packet
->m_chunk
->c_header
, hbuf
, hSize
);
3177 packet
->m_chunk
->c_chunk
= packet
->m_body
+ packet
->m_nBytesRead
;
3178 packet
->m_chunk
->c_chunkSize
= nChunk
;
3181 if (ReadN(r
, packet
->m_body
+ packet
->m_nBytesRead
, nChunk
) != nChunk
)
3183 RTMP_Log(RTMP_LOGERROR
, "%s, failed to read RTMP packet body. len: %u",
3184 __FUNCTION__
, packet
->m_nBodySize
);
3188 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)packet
->m_body
+ packet
->m_nBytesRead
, nChunk
);
3190 packet
->m_nBytesRead
+= nChunk
;
3192 /* keep the packet as ref for other packets on this channel */
3193 if (!r
->m_vecChannelsIn
[packet
->m_nChannel
])
3194 r
->m_vecChannelsIn
[packet
->m_nChannel
] = malloc(sizeof(RTMPPacket
));
3195 memcpy(r
->m_vecChannelsIn
[packet
->m_nChannel
], packet
, sizeof(RTMPPacket
));
3197 if (RTMPPacket_IsReady(packet
))
3199 /* make packet's timestamp absolute */
3200 if (!packet
->m_hasAbsTimestamp
)
3201 packet
->m_nTimeStamp
+= r
->m_channelTimestamp
[packet
->m_nChannel
]; /* timestamps seem to be always relative!! */
3203 r
->m_channelTimestamp
[packet
->m_nChannel
] = packet
->m_nTimeStamp
;
3205 /* reset the data from the stored packet. we keep the header since we may use it later if a new packet for this channel */
3206 /* arrives and requests to re-use some info (small packet header) */
3207 r
->m_vecChannelsIn
[packet
->m_nChannel
]->m_body
= NULL
;
3208 r
->m_vecChannelsIn
[packet
->m_nChannel
]->m_nBytesRead
= 0;
3209 r
->m_vecChannelsIn
[packet
->m_nChannel
]->m_hasAbsTimestamp
= FALSE
; /* can only be false if we reuse header */
3213 packet
->m_body
= NULL
; /* so it won't be erased on free */
3221 HandShake(RTMP
*r
, int FP9HandShake
)
3224 uint32_t uptime
, suptime
;
3227 char clientbuf
[RTMP_SIG_SIZE
+ 1], *clientsig
= clientbuf
+ 1;
3228 char serversig
[RTMP_SIG_SIZE
];
3230 clientbuf
[0] = 0x03; /* not encrypted */
3232 uptime
= htonl(RTMP_GetTime());
3233 memcpy(clientsig
, &uptime
, 4);
3235 memset(&clientsig
[4], 0, 4);
3238 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3239 clientsig
[i
] = 0xff;
3241 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3242 clientsig
[i
] = (char)(rand() % 256);
3245 if (!WriteN(r
, clientbuf
, RTMP_SIG_SIZE
+ 1))
3248 if (ReadN(r
, &type
, 1) != 1) /* 0x03 or 0x06 */
3251 RTMP_Log(RTMP_LOGDEBUG
, "%s: Type Answer : %02X", __FUNCTION__
, type
);
3253 if (type
!= clientbuf
[0])
3254 RTMP_Log(RTMP_LOGWARNING
, "%s: Type mismatch: client sent %d, server answered %d",
3255 __FUNCTION__
, clientbuf
[0], type
);
3257 if (ReadN(r
, serversig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3260 /* decode server response */
3262 memcpy(&suptime
, serversig
, 4);
3263 suptime
= ntohl(suptime
);
3265 RTMP_Log(RTMP_LOGDEBUG
, "%s: Server Uptime : %d", __FUNCTION__
, suptime
);
3266 RTMP_Log(RTMP_LOGDEBUG
, "%s: FMS Version : %d.%d.%d.%d", __FUNCTION__
,
3267 serversig
[4], serversig
[5], serversig
[6], serversig
[7]);
3269 /* 2nd part of handshake */
3270 if (!WriteN(r
, serversig
, RTMP_SIG_SIZE
))
3273 if (ReadN(r
, serversig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3276 bMatch
= (memcmp(serversig
, clientsig
, RTMP_SIG_SIZE
) == 0);
3279 RTMP_Log(RTMP_LOGWARNING
, "%s, client signature does not match!", __FUNCTION__
);
3288 char serverbuf
[RTMP_SIG_SIZE
+ 1], *serversig
= serverbuf
+ 1;
3289 char clientsig
[RTMP_SIG_SIZE
];
3293 if (ReadN(r
, serverbuf
, 1) != 1) /* 0x03 or 0x06 */
3296 RTMP_Log(RTMP_LOGDEBUG
, "%s: Type Request : %02X", __FUNCTION__
, serverbuf
[0]);
3298 if (serverbuf
[0] != 3)
3300 RTMP_Log(RTMP_LOGERROR
, "%s: Type unknown: client sent %02X",
3301 __FUNCTION__
, serverbuf
[0]);
3305 uptime
= htonl(RTMP_GetTime());
3306 memcpy(serversig
, &uptime
, 4);
3308 memset(&serversig
[4], 0, 4);
3310 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3311 serversig
[i
] = 0xff;
3313 for (i
= 8; i
< RTMP_SIG_SIZE
; i
++)
3314 serversig
[i
] = (char)(rand() % 256);
3317 if (!WriteN(r
, serverbuf
, RTMP_SIG_SIZE
+ 1))
3320 if (ReadN(r
, clientsig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3323 /* decode client response */
3325 memcpy(&uptime
, clientsig
, 4);
3326 uptime
= ntohl(uptime
);
3328 RTMP_Log(RTMP_LOGDEBUG
, "%s: Client Uptime : %d", __FUNCTION__
, uptime
);
3329 RTMP_Log(RTMP_LOGDEBUG
, "%s: Player Version: %d.%d.%d.%d", __FUNCTION__
,
3330 clientsig
[4], clientsig
[5], clientsig
[6], clientsig
[7]);
3332 /* 2nd part of handshake */
3333 if (!WriteN(r
, clientsig
, RTMP_SIG_SIZE
))
3336 if (ReadN(r
, clientsig
, RTMP_SIG_SIZE
) != RTMP_SIG_SIZE
)
3339 bMatch
= (memcmp(serversig
, clientsig
, RTMP_SIG_SIZE
) == 0);
3342 RTMP_Log(RTMP_LOGWARNING
, "%s, client signature does not match!", __FUNCTION__
);
3349 RTMP_SendChunk(RTMP
*r
, RTMPChunk
*chunk
)
3352 char hbuf
[RTMP_MAX_HEADER_SIZE
];
3354 RTMP_Log(RTMP_LOGDEBUG2
, "%s: fd=%d, size=%d", __FUNCTION__
, r
->m_sb
.sb_socket
,
3355 chunk
->c_chunkSize
);
3356 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)chunk
->c_header
, chunk
->c_headerSize
);
3357 if (chunk
->c_chunkSize
)
3359 char *ptr
= chunk
->c_chunk
- chunk
->c_headerSize
;
3360 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)chunk
->c_chunk
, chunk
->c_chunkSize
);
3361 /* save header bytes we're about to overwrite */
3362 memcpy(hbuf
, ptr
, chunk
->c_headerSize
);
3363 memcpy(ptr
, chunk
->c_header
, chunk
->c_headerSize
);
3364 wrote
= WriteN(r
, ptr
, chunk
->c_headerSize
+ chunk
->c_chunkSize
);
3365 memcpy(ptr
, hbuf
, chunk
->c_headerSize
);
3368 wrote
= WriteN(r
, chunk
->c_header
, chunk
->c_headerSize
);
3373 RTMP_SendPacket(RTMP
*r
, RTMPPacket
*packet
, int queue
)
3375 const RTMPPacket
*prevPacket
= r
->m_vecChannelsOut
[packet
->m_nChannel
];
3379 char *header
, *hptr
, *hend
, hbuf
[RTMP_MAX_HEADER_SIZE
], c
;
3381 char *buffer
, *tbuf
= NULL
, *toff
= NULL
;
3385 if (prevPacket
&& packet
->m_headerType
!= RTMP_PACKET_SIZE_LARGE
)
3387 /* compress a bit by using the prev packet's attributes */
3388 if (prevPacket
->m_nBodySize
== packet
->m_nBodySize
3389 && prevPacket
->m_packetType
== packet
->m_packetType
3390 && packet
->m_headerType
== RTMP_PACKET_SIZE_MEDIUM
)
3391 packet
->m_headerType
= RTMP_PACKET_SIZE_SMALL
;
3393 if (prevPacket
->m_nTimeStamp
== packet
->m_nTimeStamp
3394 && packet
->m_headerType
== RTMP_PACKET_SIZE_SMALL
)
3395 packet
->m_headerType
= RTMP_PACKET_SIZE_MINIMUM
;
3396 last
= prevPacket
->m_nTimeStamp
;
3399 if (packet
->m_headerType
> 3) /* sanity */
3401 RTMP_Log(RTMP_LOGERROR
, "sanity failed!! trying to send header of type: 0x%02x.",
3402 (unsigned char)packet
->m_headerType
);
3406 nSize
= packetSize
[packet
->m_headerType
];
3407 hSize
= nSize
; cSize
= 0;
3408 t
= packet
->m_nTimeStamp
- last
;
3412 header
= packet
->m_body
- nSize
;
3413 hend
= packet
->m_body
;
3418 hend
= hbuf
+ sizeof(hbuf
);
3421 if (packet
->m_nChannel
> 319)
3423 else if (packet
->m_nChannel
> 63)
3431 if (nSize
> 1 && t
>= 0xffffff)
3438 c
= packet
->m_headerType
<< 6;
3442 c
|= packet
->m_nChannel
;
3453 int tmp
= packet
->m_nChannel
- 64;
3454 *hptr
++ = tmp
& 0xff;
3461 hptr
= AMF_EncodeInt24(hptr
, hend
, t
> 0xffffff ? 0xffffff : t
);
3466 hptr
= AMF_EncodeInt24(hptr
, hend
, packet
->m_nBodySize
);
3467 *hptr
++ = packet
->m_packetType
;
3471 hptr
+= EncodeInt32LE(hptr
, packet
->m_nInfoField2
);
3473 if (nSize
> 1 && t
>= 0xffffff)
3474 hptr
= AMF_EncodeInt32(hptr
, hend
, t
);
3476 nSize
= packet
->m_nBodySize
;
3477 buffer
= packet
->m_body
;
3478 nChunkSize
= r
->m_outChunkSize
;
3480 RTMP_Log(RTMP_LOGDEBUG2
, "%s: fd=%d, size=%d", __FUNCTION__
, r
->m_sb
.sb_socket
,
3482 /* send all chunks in one HTTP request */
3483 if (r
->Link
.protocol
& RTMP_FEATURE_HTTP
)
3485 int chunks
= (nSize
+nChunkSize
-1) / nChunkSize
;
3488 tlen
= chunks
* (cSize
+ 1) + nSize
+ hSize
;
3489 tbuf
= malloc(tlen
);
3495 while (nSize
+ hSize
)
3499 if (nSize
< nChunkSize
)
3502 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)header
, hSize
);
3503 RTMP_LogHexString(RTMP_LOGDEBUG2
, (uint8_t *)buffer
, nChunkSize
);
3506 memcpy(toff
, header
, nChunkSize
+ hSize
);
3507 toff
+= nChunkSize
+ hSize
;
3511 wrote
= WriteN(r
, header
, nChunkSize
+ hSize
);
3515 nSize
-= nChunkSize
;
3516 buffer
+= nChunkSize
;
3521 header
= buffer
- 1;
3528 *header
= (0xc0 | c
);
3531 int tmp
= packet
->m_nChannel
- 64;
3532 header
[1] = tmp
& 0xff;
3534 header
[2] = tmp
>> 8;
3540 int wrote
= WriteN(r
, tbuf
, toff
-tbuf
);
3547 /* we invoked a remote method */
3548 if (packet
->m_packetType
== RTMP_PACKET_TYPE_INVOKE
)
3552 ptr
= packet
->m_body
+ 1;
3553 AMF_DecodeString(ptr
, &method
);
3554 RTMP_Log(RTMP_LOGDEBUG
, "Invoking %s", method
.av_val
);
3555 /* keep it in call queue till result arrives */
3558 ptr
+= 3 + method
.av_len
;
3559 txn
= (int)AMF_DecodeNumber(ptr
);
3560 AV_queue(&r
->m_methodCalls
, &r
->m_numCalls
, &method
, txn
);
3564 if (!r
->m_vecChannelsOut
[packet
->m_nChannel
])
3565 r
->m_vecChannelsOut
[packet
->m_nChannel
] = malloc(sizeof(RTMPPacket
));
3566 memcpy(r
->m_vecChannelsOut
[packet
->m_nChannel
], packet
, sizeof(RTMPPacket
));
3573 return SHandShake(r
);
3581 if (RTMP_IsConnected(r
))
3583 if (r
->m_stream_id
> 0)
3587 if ((r
->Link
.protocol
& RTMP_FEATURE_WRITE
))
3589 SendDeleteStream(r
, i
);
3591 if (r
->m_clientID
.av_val
)
3593 HTTP_Post(r
, RTMPT_CLOSE
, "", 1);
3594 free(r
->m_clientID
.av_val
);
3595 r
->m_clientID
.av_val
= NULL
;
3596 r
->m_clientID
.av_len
= 0;
3598 RTMPSockBuf_Close(&r
->m_sb
);
3601 r
->m_stream_id
= -1;
3602 r
->m_sb
.sb_socket
= -1;
3603 r
->m_nBWCheckCounter
= 0;
3605 r
->m_nBytesInSent
= 0;
3607 if (r
->m_read
.flags
& RTMP_READ_HEADER
) {
3608 free(r
->m_read
.buf
);
3609 r
->m_read
.buf
= NULL
;
3611 r
->m_read
.dataType
= 0;
3612 r
->m_read
.flags
= 0;
3613 r
->m_read
.status
= 0;
3614 r
->m_read
.nResumeTS
= 0;
3615 r
->m_read
.nIgnoredFrameCounter
= 0;
3616 r
->m_read
.nIgnoredFlvFrameCounter
= 0;
3618 r
->m_write
.m_nBytesRead
= 0;
3619 RTMPPacket_Free(&r
->m_write
);
3621 for (i
= 0; i
< RTMP_CHANNELS
; i
++)
3623 if (r
->m_vecChannelsIn
[i
])
3625 RTMPPacket_Free(r
->m_vecChannelsIn
[i
]);
3626 free(r
->m_vecChannelsIn
[i
]);
3627 r
->m_vecChannelsIn
[i
] = NULL
;
3629 if (r
->m_vecChannelsOut
[i
])
3631 free(r
->m_vecChannelsOut
[i
]);
3632 r
->m_vecChannelsOut
[i
] = NULL
;
3635 AV_clear(r
->m_methodCalls
, r
->m_numCalls
);
3636 r
->m_methodCalls
= NULL
;
3638 r
->m_numInvokes
= 0;
3640 r
->m_bPlaying
= FALSE
;
3641 r
->m_sb
.sb_size
= 0;
3643 r
->m_msgCounter
= 0;
3647 free(r
->Link
.playpath0
.av_val
);
3648 r
->Link
.playpath0
.av_val
= NULL
;
3650 if (r
->Link
.lFlags
& RTMP_LF_FTCU
)
3652 free(r
->Link
.tcUrl
.av_val
);
3653 r
->Link
.tcUrl
.av_val
= NULL
;
3654 r
->Link
.lFlags
^= RTMP_LF_FTCU
;
3660 MDH_free(r
->Link
.dh
);
3663 if (r
->Link
.rc4keyIn
)
3665 RC4_free(r
->Link
.rc4keyIn
);
3666 r
->Link
.rc4keyIn
= NULL
;
3668 if (r
->Link
.rc4keyOut
)
3670 RC4_free(r
->Link
.rc4keyOut
);
3671 r
->Link
.rc4keyOut
= NULL
;
3677 RTMPSockBuf_Fill(RTMPSockBuf
*sb
)
3682 sb
->sb_start
= sb
->sb_buf
;
3686 nBytes
= sizeof(sb
->sb_buf
) - 1 - sb
->sb_size
- (sb
->sb_start
- sb
->sb_buf
);
3687 #if defined(CRYPTO) && !defined(NO_SSL)
3690 nBytes
= TLS_read(sb
->sb_ssl
, sb
->sb_start
+ sb
->sb_size
, nBytes
);
3695 nBytes
= recv(sb
->sb_socket
, sb
->sb_start
+ sb
->sb_size
, nBytes
, 0);
3699 sb
->sb_size
+= nBytes
;
3703 int sockerr
= GetSockError();
3704 RTMP_Log(RTMP_LOGDEBUG
, "%s, recv returned %d. GetSockError(): %d (%s)",
3705 __FUNCTION__
, nBytes
, sockerr
, strerror(sockerr
));
3706 if (sockerr
== EINTR
&& !RTMP_ctrlC
)
3709 if (sockerr
== EWOULDBLOCK
|| sockerr
== EAGAIN
)
3711 sb
->sb_timedout
= TRUE
;
3722 RTMPSockBuf_Send(RTMPSockBuf
*sb
, const char *buf
, int len
)
3727 fwrite(buf
, 1, len
, netstackdump
);
3730 #if defined(CRYPTO) && !defined(NO_SSL)
3733 rc
= TLS_write(sb
->sb_ssl
, buf
, len
);
3738 rc
= send(sb
->sb_socket
, buf
, len
, 0);
3744 RTMPSockBuf_Close(RTMPSockBuf
*sb
)
3746 #if defined(CRYPTO) && !defined(NO_SSL)
3749 TLS_shutdown(sb
->sb_ssl
);
3750 TLS_close(sb
->sb_ssl
);
3754 if (sb
->sb_socket
!= -1)
3755 return closesocket(sb
->sb_socket
);
3759 #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
3762 DecodeTEA(AVal
*key
, AVal
*text
)
3764 uint32_t *v
, k
[4] = { 0 }, u
;
3765 uint32_t z
, y
, sum
= 0, e
, DELTA
= 0x9e3779b9;
3768 unsigned char *ptr
, *out
;
3770 /* prep key: pack 1st 16 chars into 4 LittleEndian ints */
3771 ptr
= (unsigned char *)key
->av_val
;
3775 p
= key
->av_len
> 16 ? 16 : key
->av_len
;
3776 for (i
= 0; i
< p
; i
++)
3778 u
|= ptr
[i
] << (n
* 8);
3790 /* any trailing chars */
3794 /* prep text: hex2bin, multiples of 4 */
3795 n
= (text
->av_len
+ 7) / 8;
3796 out
= malloc(n
* 8);
3797 ptr
= (unsigned char *)text
->av_val
;
3798 v
= (uint32_t *) out
;
3799 for (i
= 0; i
< n
; i
++)
3801 u
= (HEX2BIN(ptr
[0]) << 4) + HEX2BIN(ptr
[1]);
3802 u
|= ((HEX2BIN(ptr
[2]) << 4) + HEX2BIN(ptr
[3])) << 8;
3803 u
|= ((HEX2BIN(ptr
[4]) << 4) + HEX2BIN(ptr
[5])) << 16;
3804 u
|= ((HEX2BIN(ptr
[6]) << 4) + HEX2BIN(ptr
[7])) << 24;
3808 v
= (uint32_t *) out
;
3810 /* http://www.movable-type.co.uk/scripts/tea-block.html */
3811 #define MX (((z>>5)^(y<<2)) + ((y>>3)^(z<<4))) ^ ((sum^y) + (k[(p&3)^e]^z));
3819 for (p
= n
- 1; p
> 0; p
--)
3820 z
= v
[p
- 1], y
= v
[p
] -= MX
;
3827 memcpy(text
->av_val
, out
, text
->av_len
);
3832 HTTP_Post(RTMP
*r
, RTMPTCmd cmd
, const char *buf
, int len
)
3835 int hlen
= snprintf(hbuf
, sizeof(hbuf
), "POST /%s%s/%d HTTP/1.1\r\n"
3838 "User-Agent: Shockwave Flash\n"
3839 "Connection: Keep-Alive\n"
3840 "Cache-Control: no-cache\r\n"
3841 "Content-type: application/x-fcs\r\n"
3842 "Content-length: %d\r\n\r\n", RTMPT_cmds
[cmd
],
3843 r
->m_clientID
.av_val
? r
->m_clientID
.av_val
: "",
3844 r
->m_msgCounter
, r
->Link
.hostname
.av_len
, r
->Link
.hostname
.av_val
,
3846 RTMPSockBuf_Send(&r
->m_sb
, hbuf
, hlen
);
3847 hlen
= RTMPSockBuf_Send(&r
->m_sb
, buf
, len
);
3854 HTTP_read(RTMP
*r
, int fill
)
3861 RTMPSockBuf_Fill(&r
->m_sb
);
3862 if (r
->m_sb
.sb_size
< 13) {
3867 if (strncmp(r
->m_sb
.sb_start
, "HTTP/1.1 200 ", 13))
3869 r
->m_sb
.sb_start
[r
->m_sb
.sb_size
] = '\0';
3870 if (!strstr(r
->m_sb
.sb_start
, "\r\n\r\n")) {
3876 ptr
= r
->m_sb
.sb_start
+ sizeof("HTTP/1.1 200");
3877 while ((ptr
= strstr(ptr
, "Content-"))) {
3878 if (!strncasecmp(ptr
+8, "length:", 7)) break;
3883 hlen
= atoi(ptr
+16);
3884 ptr
= strstr(ptr
+16, "\r\n\r\n");
3888 if (ptr
+ (r
->m_clientID
.av_val
? 1 : hlen
) > r
->m_sb
.sb_start
+ r
->m_sb
.sb_size
)
3894 r
->m_sb
.sb_size
-= ptr
- r
->m_sb
.sb_start
;
3895 r
->m_sb
.sb_start
= ptr
;
3898 if (!r
->m_clientID
.av_val
)
3900 r
->m_clientID
.av_len
= hlen
;
3901 r
->m_clientID
.av_val
= malloc(hlen
+1);
3902 if (!r
->m_clientID
.av_val
)
3904 r
->m_clientID
.av_val
[0] = '/';
3905 memcpy(r
->m_clientID
.av_val
+1, ptr
, hlen
-1);
3906 r
->m_clientID
.av_val
[hlen
] = 0;
3907 r
->m_sb
.sb_size
= 0;
3911 r
->m_polling
= *ptr
++;
3912 r
->m_resplen
= hlen
- 1;
3919 #define MAX_IGNORED_FRAMES 50
3921 /* Read from the stream until we get a media packet.
3922 * Returns -3 if Play.Close/Stop, -2 if fatal error, -1 if no more media
3923 * packets, 0 if ignorable error, >0 if there is a media packet
3926 Read_1_Packet(RTMP
*r
, char *buf
, unsigned int buflen
)
3928 uint32_t prevTagSize
= 0;
3929 int rtnGetNextMediaPacket
= 0, ret
= RTMP_READ_EOF
;
3930 RTMPPacket packet
= { 0 };
3934 uint32_t nTimeStamp
= 0;
3937 rtnGetNextMediaPacket
= RTMP_GetNextMediaPacket(r
, &packet
);
3938 while (rtnGetNextMediaPacket
)
3940 char *packetBody
= packet
.m_body
;
3941 unsigned int nPacketLen
= packet
.m_nBodySize
;
3943 /* Return RTMP_READ_COMPLETE if this was completed nicely with
3944 * invoke message Play.Stop or Play.Complete
3946 if (rtnGetNextMediaPacket
== 2)
3948 RTMP_Log(RTMP_LOGDEBUG
,
3949 "Got Play.Complete or Play.Stop from server. "
3950 "Assuming stream is complete");
3951 ret
= RTMP_READ_COMPLETE
;
3955 r
->m_read
.dataType
|= (((packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
) << 2) |
3956 (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
));
3958 if (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
&& nPacketLen
<= 5)
3960 RTMP_Log(RTMP_LOGDEBUG
, "ignoring too small video packet: size: %d",
3962 ret
= RTMP_READ_IGNORE
;
3965 if (packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
&& nPacketLen
<= 1)
3967 RTMP_Log(RTMP_LOGDEBUG
, "ignoring too small audio packet: size: %d",
3969 ret
= RTMP_READ_IGNORE
;
3973 if (r
->m_read
.flags
& RTMP_READ_SEEKING
)
3975 ret
= RTMP_READ_IGNORE
;
3979 RTMP_Log(RTMP_LOGDEBUG
, "type: %02X, size: %d, TS: %d ms, abs TS: %d",
3980 packet
.m_packetType
, nPacketLen
, packet
.m_nTimeStamp
,
3981 packet
.m_hasAbsTimestamp
);
3982 if (packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
)
3983 RTMP_Log(RTMP_LOGDEBUG
, "frametype: %02X", (*packetBody
& 0xf0));
3986 if (r
->m_read
.flags
& RTMP_READ_RESUME
)
3988 /* check the header if we get one */
3989 if (packet
.m_nTimeStamp
== 0)
3991 if (r
->m_read
.nMetaHeaderSize
> 0
3992 && packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
)
3996 AMF_Decode(&metaObj
, packetBody
, nPacketLen
, FALSE
);
4000 AMFProp_GetString(AMF_GetProp(&metaObj
, NULL
, 0),
4003 if (AVMATCH(&metastring
, &av_onMetaData
))
4006 if ((r
->m_read
.nMetaHeaderSize
!= nPacketLen
) ||
4008 (r
->m_read
.metaHeader
, packetBody
,
4009 r
->m_read
.nMetaHeaderSize
) != 0))
4011 ret
= RTMP_READ_ERROR
;
4014 AMF_Reset(&metaObj
);
4015 if (ret
== RTMP_READ_ERROR
)
4020 /* check first keyframe to make sure we got the right position
4021 * in the stream! (the first non ignored frame)
4023 if (r
->m_read
.nInitialFrameSize
> 0)
4025 /* video or audio data */
4026 if (packet
.m_packetType
== r
->m_read
.initialFrameType
4027 && r
->m_read
.nInitialFrameSize
== nPacketLen
)
4029 /* we don't compare the sizes since the packet can
4030 * contain several FLV packets, just make sure the
4031 * first frame is our keyframe (which we are going
4035 (r
->m_read
.initialFrame
, packetBody
,
4036 r
->m_read
.nInitialFrameSize
) == 0)
4038 RTMP_Log(RTMP_LOGDEBUG
, "Checked keyframe successfully!");
4039 r
->m_read
.flags
|= RTMP_READ_GOTKF
;
4040 /* ignore it! (what about audio data after it? it is
4041 * handled by ignoring all 0ms frames, see below)
4043 ret
= RTMP_READ_IGNORE
;
4048 /* hande FLV streams, even though the server resends the
4049 * keyframe as an extra video packet it is also included
4050 * in the first FLV stream chunk and we have to compare
4051 * it and filter it out !!
4053 if (packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
4055 /* basically we have to find the keyframe with the
4056 * correct TS being nResumeTS
4058 unsigned int pos
= 0;
4061 while (pos
+ 11 < nPacketLen
)
4063 /* size without header (11) and prevTagSize (4) */
4065 AMF_DecodeInt24(packetBody
+ pos
+ 1);
4066 ts
= AMF_DecodeInt24(packetBody
+ pos
+ 4);
4067 ts
|= (packetBody
[pos
+ 7] << 24);
4070 RTMP_Log(RTMP_LOGDEBUG
,
4071 "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms",
4072 packetBody
[pos
], dataSize
, ts
);
4074 /* ok, is it a keyframe?:
4075 * well doesn't work for audio!
4077 if (packetBody
[pos
/*6928, test 0 */ ] ==
4078 r
->m_read
.initialFrameType
4079 /* && (packetBody[11]&0xf0) == 0x10 */ )
4081 if (ts
== r
->m_read
.nResumeTS
)
4083 RTMP_Log(RTMP_LOGDEBUG
,
4084 "Found keyframe with resume-keyframe timestamp!");
4085 if (r
->m_read
.nInitialFrameSize
!= dataSize
4086 || memcmp(r
->m_read
.initialFrame
,
4087 packetBody
+ pos
+ 11,
4089 nInitialFrameSize
) != 0)
4091 RTMP_Log(RTMP_LOGERROR
,
4092 "FLV Stream: Keyframe doesn't match!");
4093 ret
= RTMP_READ_ERROR
;
4096 r
->m_read
.flags
|= RTMP_READ_GOTFLVK
;
4098 /* skip this packet?
4099 * check whether skippable:
4101 if (pos
+ 11 + dataSize
+ 4 > nPacketLen
)
4103 RTMP_Log(RTMP_LOGWARNING
,
4104 "Non skipable packet since it doesn't end with chunk, stream corrupt!");
4105 ret
= RTMP_READ_ERROR
;
4108 packetBody
+= (pos
+ 11 + dataSize
+ 4);
4109 nPacketLen
-= (pos
+ 11 + dataSize
+ 4);
4111 goto stopKeyframeSearch
;
4114 else if (r
->m_read
.nResumeTS
< ts
)
4116 /* the timestamp ts will only increase with
4117 * further packets, wait for seek
4119 goto stopKeyframeSearch
;
4122 pos
+= (11 + dataSize
+ 4);
4124 if (ts
< r
->m_read
.nResumeTS
)
4126 RTMP_Log(RTMP_LOGERROR
,
4127 "First packet does not contain keyframe, all "
4128 "timestamps are smaller than the keyframe "
4129 "timestamp; probably the resume seek failed?");
4133 if (!(r
->m_read
.flags
& RTMP_READ_GOTFLVK
))
4135 RTMP_Log(RTMP_LOGERROR
,
4136 "Couldn't find the seeked keyframe in this chunk!");
4137 ret
= RTMP_READ_IGNORE
;
4144 if (packet
.m_nTimeStamp
> 0
4145 && (r
->m_read
.flags
& (RTMP_READ_GOTKF
|RTMP_READ_GOTFLVK
)))
4147 /* another problem is that the server can actually change from
4148 * 09/08 video/audio packets to an FLV stream or vice versa and
4149 * our keyframe check will prevent us from going along with the
4150 * new stream if we resumed.
4152 * in this case set the 'found keyframe' variables to true.
4153 * We assume that if we found one keyframe somewhere and were
4154 * already beyond TS > 0 we have written data to the output
4155 * which means we can accept all forthcoming data including the
4156 * change between 08/09 <-> FLV packets
4158 r
->m_read
.flags
|= (RTMP_READ_GOTKF
|RTMP_READ_GOTFLVK
);
4161 /* skip till we find our keyframe
4162 * (seeking might put us somewhere before it)
4164 if (!(r
->m_read
.flags
& RTMP_READ_GOTKF
) &&
4165 packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4167 RTMP_Log(RTMP_LOGWARNING
,
4168 "Stream does not start with requested frame, ignoring data... ");
4169 r
->m_read
.nIgnoredFrameCounter
++;
4170 if (r
->m_read
.nIgnoredFrameCounter
> MAX_IGNORED_FRAMES
)
4171 ret
= RTMP_READ_ERROR
; /* fatal error, couldn't continue stream */
4173 ret
= RTMP_READ_IGNORE
;
4176 /* ok, do the same for FLV streams */
4177 if (!(r
->m_read
.flags
& RTMP_READ_GOTFLVK
) &&
4178 packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
4180 RTMP_Log(RTMP_LOGWARNING
,
4181 "Stream does not start with requested FLV frame, ignoring data... ");
4182 r
->m_read
.nIgnoredFlvFrameCounter
++;
4183 if (r
->m_read
.nIgnoredFlvFrameCounter
> MAX_IGNORED_FRAMES
)
4184 ret
= RTMP_READ_ERROR
;
4186 ret
= RTMP_READ_IGNORE
;
4190 /* we have to ignore the 0ms frames since these are the first
4191 * keyframes; we've got these so don't mess around with multiple
4192 * copies sent by the server to us! (if the keyframe is found at a
4193 * later position there is only one copy and it will be ignored by
4194 * the preceding if clause)
4196 if (!(r
->m_read
.flags
& RTMP_READ_NO_IGNORE
) &&
4197 packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4199 /* exclude type RTMP_PACKET_TYPE_FLASH_VIDEO since it can
4200 * contain several FLV packets
4202 if (packet
.m_nTimeStamp
== 0)
4204 ret
= RTMP_READ_IGNORE
;
4209 /* stop ignoring packets */
4210 r
->m_read
.flags
|= RTMP_READ_NO_IGNORE
;
4215 /* calculate packet size and allocate slop buffer if necessary */
4217 ((packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
4218 || packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
4219 || packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
) ? 11 : 0) +
4220 (packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
? 4 : 0);
4222 if (size
+ 4 > buflen
)
4224 /* the extra 4 is for the case of an FLV stream without a last
4225 * prevTagSize (we need extra 4 bytes to append it) */
4226 r
->m_read
.buf
= malloc(size
+ 4);
4227 if (r
->m_read
.buf
== 0)
4229 RTMP_Log(RTMP_LOGERROR
, "Couldn't allocate memory!");
4230 ret
= RTMP_READ_ERROR
; /* fatal error */
4234 ptr
= r
->m_read
.buf
;
4240 pend
= ptr
+ size
+ 4;
4242 /* use to return timestamp of last processed packet */
4244 /* audio (0x08), video (0x09) or metadata (0x12) packets :
4245 * construct 11 byte header then add rtmp packet's data */
4246 if (packet
.m_packetType
== RTMP_PACKET_TYPE_AUDIO
4247 || packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
4248 || packet
.m_packetType
== RTMP_PACKET_TYPE_INFO
)
4250 nTimeStamp
= r
->m_read
.nResumeTS
+ packet
.m_nTimeStamp
;
4251 prevTagSize
= 11 + nPacketLen
;
4253 *ptr
= packet
.m_packetType
;
4255 ptr
= AMF_EncodeInt24(ptr
, pend
, nPacketLen
);
4258 if(packet
.m_packetType
== RTMP_PACKET_TYPE_VIDEO
) {
4261 if((packetBody
[0] & 0x0f) == 7) { /* CodecId = H264 */
4262 uint8_t packetType
= *(packetBody
+1);
4264 uint32_t ts
= AMF_DecodeInt24(packetBody
+2); /* composition time */
4265 int32_t cts
= (ts
+0xff800000)^0xff800000;
4266 RTMP_Log(RTMP_LOGDEBUG
, "cts : %d\n", cts
);
4269 /* get rid of the composition time */
4270 CRTMP::EncodeInt24(packetBody
+2, 0);
4272 RTMP_Log(RTMP_LOGDEBUG
, "VIDEO: nTimeStamp: 0x%08X (%d)\n", nTimeStamp
, nTimeStamp
);
4276 ptr
= AMF_EncodeInt24(ptr
, pend
, nTimeStamp
);
4277 *ptr
= (char)((nTimeStamp
& 0xFF000000) >> 24);
4281 ptr
= AMF_EncodeInt24(ptr
, pend
, 0);
4284 memcpy(ptr
, packetBody
, nPacketLen
);
4287 /* correct tagSize and obtain timestamp if we have an FLV stream */
4288 if (packet
.m_packetType
== RTMP_PACKET_TYPE_FLASH_VIDEO
)
4290 unsigned int pos
= 0;
4293 /* grab first timestamp and see if it needs fixing */
4294 nTimeStamp
= AMF_DecodeInt24(packetBody
+ 4);
4295 nTimeStamp
|= (packetBody
[7] << 24);
4296 delta
= packet
.m_nTimeStamp
- nTimeStamp
+ r
->m_read
.nResumeTS
;
4298 while (pos
+ 11 < nPacketLen
)
4300 /* size without header (11) and without prevTagSize (4) */
4301 uint32_t dataSize
= AMF_DecodeInt24(packetBody
+ pos
+ 1);
4302 nTimeStamp
= AMF_DecodeInt24(packetBody
+ pos
+ 4);
4303 nTimeStamp
|= (packetBody
[pos
+ 7] << 24);
4307 nTimeStamp
+= delta
;
4308 AMF_EncodeInt24(ptr
+pos
+4, pend
, nTimeStamp
);
4309 ptr
[pos
+7] = nTimeStamp
>>24;
4313 r
->m_read
.dataType
|= (((*(packetBody
+ pos
) == 0x08) << 2) |
4314 (*(packetBody
+ pos
) == 0x09));
4316 if (pos
+ 11 + dataSize
+ 4 > nPacketLen
)
4318 if (pos
+ 11 + dataSize
> nPacketLen
)
4320 RTMP_Log(RTMP_LOGERROR
,
4321 "Wrong data size (%u), stream corrupted, aborting!",
4323 ret
= RTMP_READ_ERROR
;
4326 RTMP_Log(RTMP_LOGWARNING
, "No tagSize found, appending!");
4328 /* we have to append a last tagSize! */
4329 prevTagSize
= dataSize
+ 11;
4330 AMF_EncodeInt32(ptr
+ pos
+ 11 + dataSize
, pend
,
4338 AMF_DecodeInt32(packetBody
+ pos
+ 11 + dataSize
);
4341 RTMP_Log(RTMP_LOGDEBUG
,
4342 "FLV Packet: type %02X, dataSize: %lu, tagSize: %lu, timeStamp: %lu ms",
4343 (unsigned char)packetBody
[pos
], dataSize
, prevTagSize
,
4347 if (prevTagSize
!= (dataSize
+ 11))
4350 RTMP_Log(RTMP_LOGWARNING
,
4351 "Tag and data size are not consitent, writing tag size according to dataSize+11: %d",
4355 prevTagSize
= dataSize
+ 11;
4356 AMF_EncodeInt32(ptr
+ pos
+ 11 + dataSize
, pend
,
4361 pos
+= prevTagSize
+ 4; /*(11+dataSize+4); */
4366 if (packet
.m_packetType
!= RTMP_PACKET_TYPE_FLASH_VIDEO
)
4368 /* FLV tag packets contain their own prevTagSize */
4369 AMF_EncodeInt32(ptr
, pend
, prevTagSize
);
4372 /* In non-live this nTimeStamp can contain an absolute TS.
4373 * Update ext timestamp with this absolute offset in non-live mode
4374 * otherwise report the relative one
4376 /* 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); */
4377 r
->m_read
.timestamp
= (r
->Link
.lFlags
& RTMP_LF_LIVE
) ? packet
.m_nTimeStamp
: nTimeStamp
;
4383 if (rtnGetNextMediaPacket
)
4384 RTMPPacket_Free(&packet
);
4388 len
= ret
> buflen
? buflen
: ret
;
4389 memcpy(buf
, r
->m_read
.buf
, len
);
4390 r
->m_read
.bufpos
= r
->m_read
.buf
+ len
;
4391 r
->m_read
.buflen
= ret
- len
;
4396 static const char flvHeader
[] = { 'F', 'L', 'V', 0x01,
4397 0x00, /* 0x04 == audio, 0x01 == video */
4398 0x00, 0x00, 0x00, 0x09,
4399 0x00, 0x00, 0x00, 0x00
4402 #define HEADERBUF (128*1024)
4404 RTMP_Read(RTMP
*r
, char *buf
, int size
)
4406 int nRead
= 0, total
= 0;
4408 /* can't continue */
4410 switch (r
->m_read
.status
) {
4412 case RTMP_READ_COMPLETE
:
4414 case RTMP_READ_ERROR
: /* corrupted stream, resume failed */
4415 SetSockError(EINVAL
);
4421 /* first time thru */
4422 if (!(r
->m_read
.flags
& RTMP_READ_HEADER
))
4424 if (!(r
->m_read
.flags
& RTMP_READ_RESUME
))
4426 char *mybuf
= malloc(HEADERBUF
), *end
= mybuf
+ HEADERBUF
;
4428 r
->m_read
.buf
= mybuf
;
4429 r
->m_read
.buflen
= HEADERBUF
;
4431 memcpy(mybuf
, flvHeader
, sizeof(flvHeader
));
4432 r
->m_read
.buf
+= sizeof(flvHeader
);
4433 r
->m_read
.buflen
-= sizeof(flvHeader
);
4435 while (r
->m_read
.timestamp
== 0)
4437 nRead
= Read_1_Packet(r
, r
->m_read
.buf
, r
->m_read
.buflen
);
4441 r
->m_read
.buf
= NULL
;
4442 r
->m_read
.buflen
= 0;
4443 r
->m_read
.status
= nRead
;
4446 /* buffer overflow, fix buffer and give up */
4447 if (r
->m_read
.buf
< mybuf
|| r
->m_read
.buf
> end
) {
4448 mybuf
= realloc(mybuf
, cnt
+ nRead
);
4449 memcpy(mybuf
+cnt
, r
->m_read
.buf
, nRead
);
4450 r
->m_read
.buf
= mybuf
+cnt
+nRead
;
4454 r
->m_read
.buf
+= nRead
;
4455 r
->m_read
.buflen
-= nRead
;
4456 if (r
->m_read
.dataType
== 5)
4459 mybuf
[4] = r
->m_read
.dataType
;
4460 r
->m_read
.buflen
= r
->m_read
.buf
- mybuf
;
4461 r
->m_read
.buf
= mybuf
;
4462 r
->m_read
.bufpos
= mybuf
;
4464 r
->m_read
.flags
|= RTMP_READ_HEADER
;
4467 if ((r
->m_read
.flags
& RTMP_READ_SEEKING
) && r
->m_read
.buf
)
4469 /* drop whatever's here */
4470 free(r
->m_read
.buf
);
4471 r
->m_read
.buf
= NULL
;
4472 r
->m_read
.bufpos
= NULL
;
4473 r
->m_read
.buflen
= 0;
4476 /* If there's leftover data buffered, use it up */
4479 nRead
= r
->m_read
.buflen
;
4482 memcpy(buf
, r
->m_read
.bufpos
, nRead
);
4483 r
->m_read
.buflen
-= nRead
;
4484 if (!r
->m_read
.buflen
)
4486 free(r
->m_read
.buf
);
4487 r
->m_read
.buf
= NULL
;
4488 r
->m_read
.bufpos
= NULL
;
4492 r
->m_read
.bufpos
+= nRead
;
4499 while (size
> 0 && (nRead
= Read_1_Packet(r
, buf
, size
)) >= 0)
4501 if (!nRead
) continue;
4508 r
->m_read
.status
= nRead
;
4515 static const AVal av_setDataFrame
= AVC("@setDataFrame");
4518 RTMP_Write(RTMP
*r
, const char *buf
, int size
)
4520 RTMPPacket
*pkt
= &r
->m_write
;
4522 int s2
= size
, ret
, num
;
4524 pkt
->m_nChannel
= 0x04; /* source channel */
4525 pkt
->m_nInfoField2
= r
->m_stream_id
;
4529 if (!pkt
->m_nBytesRead
)
4532 /* FLV pkt too small */
4536 if (buf
[0] == 'F' && buf
[1] == 'L' && buf
[2] == 'V')
4542 pkt
->m_packetType
= *buf
++;
4543 pkt
->m_nBodySize
= AMF_DecodeInt24(buf
);
4545 pkt
->m_nTimeStamp
= AMF_DecodeInt24(buf
);
4547 pkt
->m_nTimeStamp
|= *buf
++ << 24;
4551 if (((pkt
->m_packetType
== RTMP_PACKET_TYPE_AUDIO
4552 || pkt
->m_packetType
== RTMP_PACKET_TYPE_VIDEO
) &&
4553 !pkt
->m_nTimeStamp
) || pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
4555 pkt
->m_headerType
= RTMP_PACKET_SIZE_LARGE
;
4556 if (pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
4557 pkt
->m_nBodySize
+= 16;
4561 pkt
->m_headerType
= RTMP_PACKET_SIZE_MEDIUM
;
4564 if (!RTMPPacket_Alloc(pkt
, pkt
->m_nBodySize
))
4566 RTMP_Log(RTMP_LOGDEBUG
, "%s, failed to allocate packet", __FUNCTION__
);
4570 pend
= enc
+ pkt
->m_nBodySize
;
4571 if (pkt
->m_packetType
== RTMP_PACKET_TYPE_INFO
)
4573 enc
= AMF_EncodeString(enc
, pend
, &av_setDataFrame
);
4574 pkt
->m_nBytesRead
= enc
- pkt
->m_body
;
4579 enc
= pkt
->m_body
+ pkt
->m_nBytesRead
;
4581 num
= pkt
->m_nBodySize
- pkt
->m_nBytesRead
;
4584 memcpy(enc
, buf
, num
);
4585 pkt
->m_nBytesRead
+= num
;
4588 if (pkt
->m_nBytesRead
== pkt
->m_nBodySize
)
4590 ret
= RTMP_SendPacket(r
, pkt
, FALSE
);
4591 RTMPPacket_Free(pkt
);
4592 pkt
->m_nBytesRead
= 0;