static bool FillBuffer(RTMP * r);
+static void DecodeTEA(AVal *key, AVal *text);
+
uint32_t
RTMP_GetTime()
{
SAVC(videoCodecs);
SAVC(videoFunction);
SAVC(objectEncoding);
+SAVC(secureToken);
+SAVC(secureTokenResponse);
static bool
SendConnectPacket(RTMP * r)
return RTMP_SendPacket(r, &packet, true);
}
+static bool
+SendSecureTokenResponse(RTMP *r, AVal *resp)
+{
+ RTMPPacket packet;
+ char pbuf[1024], *pend = pbuf+sizeof(pbuf);
+
+ packet.m_nChannel = 0x03; /* control channel (invoke) */
+ packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
+ packet.m_packetType = 0x14;
+ packet.m_nInfoField2 = 0;
+ packet.m_nInfoField1 = 0;
+ packet.m_hasAbsTimestamp = 0;
+ packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
+
+ char *enc = packet.m_body;
+ enc = AMF_EncodeString(enc, pend, &av_secureTokenResponse);
+ enc = AMF_EncodeNumber(enc, pend, 0.0);
+ *enc++ = AMF_NULL;
+ enc = AMF_EncodeString(enc, pend, resp);
+ if (!enc)
+ return false;
+
+ packet.m_nBodySize = enc - packet.m_body;
+
+ return RTMP_SendPacket(r, &packet, false);
+}
+
/*
from http://jira.red5.org/confluence/display/docs/Ping:
if (AVMATCH(&methodInvoked, &av_connect))
{
+ if (r->Link.token.av_len)
+ {
+ AMFObjectProperty p;
+ if (RTMP_FindFirstMatchingProperty(&obj, &av_secureToken, &p))
+ {
+ DecodeTEA(&r->Link.token, &p.p_vu.p_aval);
+ SendSecureTokenResponse(r, &p.p_vu.p_aval);
+ }
+ }
SendServerBW(r);
SendCtrl(r, 3, 0, 300);
SendCreateStream(r, 2.0);
- // Send the FCSubscribe if live stream or if subscribepath is set
+ /* Send the FCSubscribe if live stream or if subscribepath is set */
if (r->Link.subscribepath.av_len)
SendFCSubscribe(r, &r->Link.subscribepath);
else if (r->Link.bLiveStream)
if (prop->p_type == AMF_OBJECT)
{
- return RTMP_FindFirstMatchingProperty(&prop->p_vu.p_object, name,
- p);
+ if (RTMP_FindFirstMatchingProperty(&prop->p_vu.p_object, name, p))
+ return true;
}
}
return false;
return true;
}
+
+#define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
+#define BIN2HEX(a) (((a)>9)?((a)+96):((a)+48))
+
+static void
+DecodeTEA(AVal *key, AVal *text)
+{
+ uint32_t *v, k[4] = {0}, u;
+ uint32_t z, y, sum=0, e, DELTA=0x9e3779b9;
+ int32_t p, q;
+ int i, n;
+ unsigned char *ptr, *out;
+
+ /* prep key */
+ ptr = (unsigned char *)key->av_val;
+ u = 0; n = 0;
+ v = k;
+ p = key->av_len > 16 ? 16:key->av_len;
+ for (i=0; i<p; i++)
+ {
+ u |= ptr[i] << (n*8);
+ if (n==3)
+ {
+ *v++ = u;
+ u = 0;
+ n = 0;
+ }
+ else
+ {
+ n++;
+ }
+ }
+ *v = u;
+
+ /* prep text */
+ n = (text->av_len+7)/8;
+ out = malloc(n*8);
+ ptr = (unsigned char *)text->av_val;
+ v = (uint32_t *)out;
+ for (i=0; i<n; i++)
+ {
+ u = (HEX2BIN(ptr[0]) << 4) + HEX2BIN(ptr[1]);
+ u |= ((HEX2BIN(ptr[2]) << 4) + HEX2BIN(ptr[3])) << 8;
+ u |= ((HEX2BIN(ptr[4]) << 4) + HEX2BIN(ptr[5])) << 16;
+ u |= ((HEX2BIN(ptr[6]) << 4) + HEX2BIN(ptr[7])) << 24;
+ *v++ = u;
+ ptr += 8;
+ }
+ v = (uint32_t *)out;
+
+#define MX (((z>>5)^(y<<2)) + ((y>>3)^(z<<4))) ^ ((sum^y) + (k[(p&3)^e]^z));
+ z=v[n-1];
+ y=v[0];
+ q = 6+52/n ;
+ sum = q*DELTA ;
+ while (sum != 0)
+ {
+ e = sum>>2 & 3;
+ for (p=n-1; p>0; p--) z = v[p-1], y = v[p] -= MX;
+ z = v[n-1];
+ y = v[0] -= MX;
+ sum -= DELTA;
+ }
+ /* bin 2 hex */
+ text->av_len /= 2;
+ memcpy(text->av_val, out, text->av_len);
+ free(out);
+}
AVal auth;
AVal flashVer;
AVal subscribepath;
+ AVal token;
double seekTime;
uint32_t length;
AVal swfHash = { 0, 0 };
uint32_t swfSize = 0;
AVal flashVer = { 0, 0 };
+ AVal token = { 0, 0 };
char *sockshost = 0;
char *flvFile = 0;
{"subscribe", 1, NULL, 'd'},
{"start", 1, NULL, 'A'},
{"stop", 1, NULL, 'B'},
+ {"token", 1, NULL, 'T'},
{"hashes", 0, NULL, '#'},
{"debug", 0, NULL, 'z'},
{"quiet", 0, NULL, 'q'},
while ((opt =
getopt_long(argc, argv,
- "hVveqzr:s:t:p:a:b:f:o:u:n:c:l:y:m:k:d:A:B:w:x:S:#",
+ "hVveqzr:s:t:p:a:b:f:o:u:n:c:l:y:m:k:d:A:B:T:w:x:S:#",
longopts, NULL)) != -1)
{
switch (opt)
("--start|-A num Start at num seconds into stream (not valid when using --live)\n");
LogPrintf
("--stop|-B num Stop at num seconds into stream\n");
+ LogPrintf
+ ("--token|-T key Key for SecureToken response\n");
LogPrintf
("--hashes|-# Display progress with hashes, not with the byte counter\n");
LogPrintf
case 'B':
dStopOffset = (int) (atof(optarg) * 1000.0);
break;
+ case 'T':
+ STR2AVAL(token, optarg);
+ break;
case '#':
bHashes = true;
break;
&tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize,
&flashVer, &subscribepath, dSeek, 0, bLiveStream, timeout);
+ rtmp.Link.token = token;
off_t size = 0;
// ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams)
SAVC(level);
SAVC(code);
SAVC(description);
+SAVC(secureToken);
static bool
SendConnectResult(RTMP *r, double txn)
STR2AVAL(av, "Connection succeeded.");
enc = AMF_EncodeNamedString(enc, pend, &av_description, &av);
enc = AMF_EncodeNamedNumber(enc, pend, &av_objectEncoding, r->m_fEncoding);
+#if 0
+ STR2AVAL(av, "58656322c972d6cdf2d776167575045f8484ea888e31c086f7b5ffbd0baec55ce442c2fb");
+ enc = AMF_EncodeNamedString(enc, pend, &av_secureToken, &av);
+#endif
STR2AVAL(p.p_name, "version");
STR2AVAL(p.p_vu.p_aval, "3,5,1,525");
p.p_type = AMF_STRING;
AVal auth;
AVal swfHash;
AVal flashVer;
+ AVal token;
AVal subscribepath;
uint32_t swfSize;
RTMP_SetupStream(&rtmp, req.protocol, req.hostname, req.rtmpport, NULL, // sockshost
&req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, dSeek, -1, // length
req.bLiveStream, req.timeout);
+ rtmp.Link.token = req.token;
LogPrintf("Connecting ... port: %d, app: %s\n", req.rtmpport, req.app);
if (!RTMP_Connect(&rtmp))
req->dStopOffset = atoi(arg) * 1000;
//printf("dStartOffset = %d\n", dStartOffset);
break;
+ case 'T':
+ STR2AVAL(req->token, arg);
+ break;
case 'q':
debuglevel = LOGCRIT;
break;
{"subscribe", 1, NULL, 'd'},
{"start", 1, NULL, 'A'},
{"stop", 1, NULL, 'B'},
+ {"token", 1, NULL, 'T'},
{"debug", 0, NULL, 'z'},
{"quiet", 0, NULL, 'q'},
{"verbose", 0, NULL, 'V'},
while ((opt =
getopt_long(argc, argv,
- "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:g:w:x:", longopts,
+ "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:", longopts,
NULL)) != -1)
{
switch (opt)
("--start|-A num Start at num seconds into stream (not valid when using --live)\n");
LogPrintf
("--stop|-B num Stop at num seconds into stream\n");
+ LogPrintf
+ ("--token|-T key Key for SecureToken response\n");
LogPrintf
("--buffer|-b Buffer time in milliseconds (default: %lu)\n\n",
defaultRTMPRequest.bufferTime);