[rtmpdump] r212 - in trunk: rtmp.c rtmp.h rtmpdump.c rtmpsrv.c streams.c

hyc subversion at mplayerhq.hu
Sat Jan 9 01:23:23 CET 2010


Author: hyc
Date: Sat Jan  9 01:23:22 2010
New Revision: 212

Log:
Add --conn|-C option to append arbitrary AMF data to connect request

Modified:
   trunk/rtmp.c
   trunk/rtmp.h
   trunk/rtmpdump.c
   trunk/rtmpsrv.c
   trunk/streams.c

Modified: trunk/rtmp.c
==============================================================================
--- trunk/rtmp.c	Fri Jan  8 14:54:31 2010	(r211)
+++ trunk/rtmp.c	Sat Jan  9 01:23:22 2010	(r212)
@@ -956,6 +956,16 @@ SendConnectPacket(RTMP *r, RTMPPacket *c
       if (!enc)
         return false;
     }
+  if (r->Link.extras.o_num)
+    {
+      int i;
+      for (i=0; i < r->Link.extras.o_num; i++)
+        {
+          enc = AMFProp_Encode(&r->Link.extras.o_props[i], enc, pend);
+          if (!enc)
+            return false;
+        }
+    }
   packet.m_nBodySize = enc - packet.m_body;
 
   return RTMP_SendPacket(r, &packet, true);

Modified: trunk/rtmp.h
==============================================================================
--- trunk/rtmp.h	Fri Jan  8 14:54:31 2010	(r211)
+++ trunk/rtmp.h	Sat Jan  9 01:23:22 2010	(r212)
@@ -135,6 +135,7 @@ typedef struct RTMP_LNK
   AVal subscribepath;
   AVal token;
   bool authflag;
+  AMFObject extras;
 
   double seekTime;
   uint32_t length;

Modified: trunk/rtmpdump.c
==============================================================================
--- trunk/rtmpdump.c	Fri Jan  8 14:54:31 2010	(r211)
+++ trunk/rtmpdump.c	Sat Jan  9 01:23:22 2010	(r212)
@@ -1051,6 +1051,91 @@ Download(RTMP * rtmp,		// connected RTMP
 #define STR2AVAL(av,str)	av.av_val = str; av.av_len = strlen(av.av_val)
 
 int
+parseAMF(AMFObject *obj, const char *arg, int *depth)
+{
+  AMFObjectProperty prop = {{0,0}};
+  int i;
+  char *p;
+
+  if (arg[1] == ':')
+    {
+      p = (char *)arg+2;
+      switch(arg[0])
+        {
+        case 'B':
+          prop.p_type = AMF_BOOLEAN;
+          prop.p_vu.p_number = atoi(p);
+          break;
+        case 'S':
+          prop.p_type = AMF_STRING;
+          STR2AVAL(prop.p_vu.p_aval,p);
+          break;
+        case 'N':
+          prop.p_type = AMF_NUMBER;
+          prop.p_vu.p_number = strtod(p, NULL);
+          break;
+        case 'O':
+          i = atoi(p);
+          if (i)
+            {
+              prop.p_type = AMF_OBJECT;
+            }
+          else
+            {
+              (*depth)--;
+              return 0;
+            }
+          break;
+        default:
+          return -1;
+        }
+    }
+  else if (arg[2] == ':' && arg[0] == 'N')
+    {
+      p = strchr(arg+3, ':');
+      if (!p || !*depth)
+        return -1;
+      prop.p_name.av_val = (char *)arg+3;
+      prop.p_name.av_len = p - (arg+3);
+
+      p++;
+      switch(arg[1])
+        {
+        case 'B':
+          prop.p_type = AMF_BOOLEAN;
+          prop.p_vu.p_number = atoi(p);
+          break;
+        case 'S':
+          prop.p_type = AMF_STRING;
+          STR2AVAL(prop.p_vu.p_aval,p);
+          break;
+        case 'N':
+          prop.p_type = AMF_NUMBER;
+          prop.p_vu.p_number = strtod(p, NULL);
+          break;
+        default:
+          return -1;
+        }
+    }
+  else
+    return -1;
+
+  if (*depth)
+    {
+      AMFObject *o2;
+      for (i=0; i<*depth; i++)
+        {
+          o2 = &obj->o_props[obj->o_num-1].p_vu.p_object;
+          obj = o2;
+        }
+    }
+  AMF_AddProp(obj, &prop);
+  if (prop.p_type == AMF_OBJECT)
+    (*depth)++;
+  return 0;
+}
+
+int
 main(int argc, char **argv)
 {
   extern char *optarg;
@@ -1102,6 +1187,8 @@ main(int argc, char **argv)
   AVal flashVer = { 0, 0 };
   AVal token = { 0, 0 };
   char *sockshost = 0;
+  AMFObject extras = {0};
+  int edepth = 0;
 
 #ifdef CRYPTO
   unsigned char hash[HASHLEN];
@@ -1163,6 +1250,7 @@ main(int argc, char **argv)
     {"pageUrl", 1, NULL, 'p'},
     {"app", 1, NULL, 'a'},
     {"auth", 1, NULL, 'u'},
+    {"conn", 1, NULL, 'C'},
 #ifdef CRYPTO
     {"swfhash", 1, NULL, 'w'},
     {"swfsize", 1, NULL, 'x'},
@@ -1188,7 +1276,7 @@ main(int argc, char **argv)
 
   while ((opt =
 	  getopt_long(argc, argv,
-		      "hVveqzr:s:t:p:a:b:f:o:u:n:c:l:y:m:k:d:A:B:T:w:x:W:S:#",
+		      "hVveqzr:s:t:p:a:b:f:o:u:C:n:c:l:y:m:k:d:A:B:T:w:x:W:S:#",
 		      longopts, NULL)) != -1)
     {
       switch (opt)
@@ -1225,6 +1313,12 @@ main(int argc, char **argv)
 	  LogPrintf
 	    ("--auth|-u string        Authentication string to be appended to the connect string\n");
 	  LogPrintf
+	    ("--conn|-C type:data     Arbitrary AMF data to be appended to the connect string\n");
+	  LogPrintf
+	    ("                        B:boolean(0|1), S:string, N:number, O:object-flag(0|1),\n");
+	  LogPrintf
+	    ("                        NB:name:boolean, NS:name:string, NN:name:number\n");
+	  LogPrintf
 	    ("--flashVer|-f string    Flash version string (default: \"%s\")\n",
 	     DEFAULT_FLASH_VER);
 	  LogPrintf
@@ -1412,6 +1506,13 @@ main(int argc, char **argv)
 	case 'u':
 	  STR2AVAL(auth, optarg);
 	  break;
+        case 'C':
+          if (parseAMF(&extras, optarg, &edepth))
+            {
+              Log(LOGERROR, "Invalid AMF parameter: %s", optarg);
+              return RD_FAILED;
+            }
+          break;
 	case 'm':
 	  timeout = atoi(optarg);
 	  break;
@@ -1546,6 +1647,11 @@ main(int argc, char **argv)
 		   &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize,
 		   &flashVer, &subscribepath, dSeek, 0, bLiveStream, timeout);
 
+  /* backward compatibility, we always sent this as true before */
+  if (auth.av_len)
+    rtmp.Link.authflag = true;
+
+  rtmp.Link.extras = extras;
   rtmp.Link.token = token;
   off_t size = 0;
 

Modified: trunk/rtmpsrv.c
==============================================================================
--- trunk/rtmpsrv.c	Fri Jan  8 14:54:31 2010	(r211)
+++ trunk/rtmpsrv.c	Sat Jan  9 01:23:22 2010	(r212)
@@ -75,6 +75,7 @@ typedef struct
   int socket;
   int state;
   int streamID;
+  char *connect;
 
 } STREAMING_SERVER;
 
@@ -240,11 +241,53 @@ SendResultNumber(RTMP *r, double txn, do
   return RTMP_SendPacket(r, &packet, false);
 }
 
+static void
+dumpAMF(AMFObject *obj)
+{
+   int i;
+   const char opt[] = "NBSO";
+
+   for (i=0; i < obj->o_num; i++)
+     {
+       AMFObjectProperty *p = &obj->o_props[i];
+       LogPrintf(" -C ");
+       if (p->p_name.av_val)
+         LogPrintf("N");
+       LogPrintf("%c:", opt[p->p_type]);
+       if (p->p_name.av_val)
+         LogPrintf("%.*s:", p->p_name.av_len, p->p_name.av_val);
+       switch(p->p_type)
+         {
+         case AMF_BOOLEAN:
+           LogPrintf("%d", p->p_vu.p_number != 0);
+           break;
+         case AMF_STRING:
+           LogPrintf("%.*s", p->p_vu.p_aval.av_len, p->p_vu.p_aval.av_val);
+           break;
+         case AMF_NUMBER:
+           LogPrintf("%f", p->p_vu.p_number);
+           break;
+         case AMF_OBJECT:
+           LogPrintf("1");
+           dumpAMF(&p->p_vu.p_object);
+           LogPrintf(" -C O:0");
+           break;
+         default:
+           LogPrintf("<type %d>", p->p_type);
+         }
+    }
+}
 // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
 int
-ServeInvoke(STREAMING_SERVER *server, RTMP * r, const char *body, unsigned int nBodySize)
+ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int offset)
 {
+  const char *body;
+  unsigned int nBodySize;
   int ret = 0, nRes;
+
+  body = packet->m_body + offset;
+  nBodySize = packet->m_nBodySize - offset;
+
   if (body[0] != 0x02)		// make sure it is a string method name we start with
     {
       Log(LOGWARNING, "%s, Sanity failed. no string method in invoke packet",
@@ -271,6 +314,10 @@ ServeInvoke(STREAMING_SERVER *server, RT
       AMFObject cobj;
       AVal pname, pval;
       int i;
+
+      server->connect = packet->m_body;
+      packet->m_body = NULL;
+
       AMFProp_GetObject(AMF_GetProp(&obj, NULL, 2), &cobj);
       for (i=0; i<cobj.o_num; i++)
         {
@@ -278,16 +325,7 @@ ServeInvoke(STREAMING_SERVER *server, RT
           pval.av_val = NULL;
           pval.av_len = 0;
           if (cobj.o_props[i].p_type == AMF_STRING)
-            {
-              pval = cobj.o_props[i].p_vu.p_aval;
-              if (pval.av_val)
-                {
-                  char *p = malloc(pval.av_len+1);
-                  memcpy(p, pval.av_val, pval.av_len);
-                  pval.av_val = p;
-                  p[pval.av_len] = '\0';
-                }
-            }
+            pval = cobj.o_props[i].p_vu.p_aval;
           if (AVMATCH(&pname, &av_app))
             {
               r->Link.app = pval;
@@ -325,25 +363,15 @@ ServeInvoke(STREAMING_SERVER *server, RT
             {
               r->m_fEncoding = cobj.o_props[i].p_vu.p_number;
             }
-          /* Dup'd a sting we didn't recognize? */
-          if (pval.av_val)
-            free(pval.av_val);
         }
+      /* Still have more parameters? Copy them */
       if (obj.o_num > 3)
         {
-          r->Link.authflag = AMFProp_GetBoolean(&obj.o_props[3]);
-          if (obj.o_num > 4)
-          {
-            AMFProp_GetString(&obj.o_props[4], &pval);
-            if (pval.av_val)
-              {
-                char *p = malloc(pval.av_len+1);
-                memcpy(p, pval.av_val, pval.av_len);
-                pval.av_val = p;
-                p[pval.av_len] = '\0';
-                r->Link.auth = pval;
-              }
-          }
+          int i = obj.o_num - 3;
+          r->Link.extras.o_num = i;
+          r->Link.extras.o_props = malloc(i*sizeof(AMFObjectProperty));
+          memcpy(r->Link.extras.o_props, obj.o_props+3, i*sizeof(AMFObjectProperty));
+          obj.o_num = 3;
         }
       SendConnectResult(r, txn);
     }
@@ -357,6 +385,7 @@ ServeInvoke(STREAMING_SERVER *server, RT
     }
   else if (AVMATCH(&method, &av_play))
     {
+      RTMPPacket pc = {0};
       AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &r->Link.playpath);
       r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4));
       if (obj.o_num > 5)
@@ -375,9 +404,17 @@ ServeInvoke(STREAMING_SERVER *server, RT
             LogPrintf(" -p '%s'", r->Link.pageUrl.av_val);
           if (r->Link.auth.av_val)
             LogPrintf(" -u '%s'", r->Link.auth.av_val);
+          if (r->Link.extras.o_num)
+            {
+              dumpAMF(&r->Link.extras);
+              AMF_Reset(&r->Link.extras);
+            }
           LogPrintf(" -y '%.*s' -o output.flv\n\n",
             r->Link.playpath.av_len, r->Link.playpath.av_val);
         }
+      pc.m_body = server->connect;
+      server->connect = NULL;
+      RTMPPacket_Free(&pc);
       ret = 1;
     }
   AMF_Reset(&obj);
@@ -450,7 +487,7 @@ ServePacket(STREAMING_SERVER *server, RT
 
 	   obj.Dump(); */
 
-	ServeInvoke(server, r, packet->m_body + 1, packet->m_nBodySize - 1);
+	ServeInvoke(server, r, packet, 1);
 	break;
       }
     case 0x12:
@@ -467,7 +504,7 @@ ServePacket(STREAMING_SERVER *server, RT
 	  packet->m_nBodySize);
       //LogHex(packet.m_body, packet.m_nBodySize);
 
-      if (ServeInvoke(server, r, packet->m_body, packet->m_nBodySize))
+      if (ServeInvoke(server, r, packet, 0))
         RTMP_Close(r);
       break;
 
@@ -553,17 +590,11 @@ cleanup:
   RTMP_Close(&rtmp);
   /* Should probably be done by RTMP_Close() ... */
   rtmp.Link.playpath.av_val = NULL;
-  free(rtmp.Link.tcUrl.av_val);
   rtmp.Link.tcUrl.av_val = NULL;
-  free(rtmp.Link.swfUrl.av_val);
   rtmp.Link.swfUrl.av_val = NULL;
-  free(rtmp.Link.pageUrl.av_val);
   rtmp.Link.pageUrl.av_val = NULL;
-  free(rtmp.Link.app.av_val);
   rtmp.Link.app.av_val = NULL;
-  free(rtmp.Link.auth.av_val);
   rtmp.Link.auth.av_val = NULL;
-  free(rtmp.Link.flashVer.av_val);
   rtmp.Link.flashVer.av_val = NULL;
   LogPrintf("done!\n\n");
 
@@ -618,7 +649,7 @@ STREAMING_SERVER *
 startStreaming(const char *address, int port)
 {
   struct sockaddr_in addr;
-  int sockfd;
+  int sockfd, tmp;
   STREAMING_SERVER *server;
 
   sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
@@ -628,6 +659,10 @@ startStreaming(const char *address, int 
       return 0;
     }
 
+  tmp = 1;
+  setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
+				(char *) &tmp, sizeof(tmp) );
+
   addr.sin_family = AF_INET;
   addr.sin_addr.s_addr = inet_addr(address);	//htonl(INADDR_ANY);
   addr.sin_port = htons(port);

Modified: trunk/streams.c
==============================================================================
--- trunk/streams.c	Fri Jan  8 14:54:31 2010	(r211)
+++ trunk/streams.c	Sat Jan  9 01:23:22 2010	(r212)
@@ -96,6 +96,8 @@ typedef struct
   AVal flashVer;
   AVal token;
   AVal subscribepath;
+  AMFObject extras;
+  int edepth;
   uint32_t swfSize;
 
   uint32_t dStartOffset;
@@ -105,6 +107,91 @@ typedef struct
 
 #define STR2AVAL(av,str)	av.av_val = str; av.av_len = strlen(av.av_val)
 
+int
+parseAMF(AMFObject *obj, const char *arg, int *depth)
+{
+  AMFObjectProperty prop = {{0,0}};
+  int i;
+  char *p;
+
+  if (arg[1] == ':')
+    {
+      p = (char *)arg+2;
+      switch(arg[0])
+        {
+        case 'B':
+          prop.p_type = AMF_BOOLEAN;
+          prop.p_vu.p_number = atoi(p);
+          break;
+        case 'S':
+          prop.p_type = AMF_STRING;
+          STR2AVAL(prop.p_vu.p_aval,p);
+          break;
+        case 'N':
+          prop.p_type = AMF_NUMBER;
+          prop.p_vu.p_number = strtod(p, NULL);
+          break;
+        case 'O':
+          i = atoi(p);
+          if (i)
+            {
+              prop.p_type = AMF_OBJECT;
+            }
+          else
+            {
+              (*depth)--;
+              return 0;
+            }
+          break;
+        default:
+          return -1;
+        }
+    }
+  else if (arg[2] == ':' && arg[0] == 'N')
+    {
+      p = strchr(arg+3, ':');
+      if (!p || !*depth)
+        return -1;
+      prop.p_name.av_val = (char *)arg+3;
+      prop.p_name.av_len = p - (arg+3);
+
+      p++;
+      switch(arg[1])
+        {
+        case 'B':
+          prop.p_type = AMF_BOOLEAN;
+          prop.p_vu.p_number = atoi(p);
+          break;
+        case 'S':
+          prop.p_type = AMF_STRING;
+          STR2AVAL(prop.p_vu.p_aval,p);
+          break;
+        case 'N':
+          prop.p_type = AMF_NUMBER;
+          prop.p_vu.p_number = strtod(p, NULL);
+          break;
+        default:
+          return -1;
+        }
+    }
+  else
+    return -1;
+
+  if (*depth)
+    {
+      AMFObject *o2;
+      for (i=0; i<*depth; i++)
+        {
+          o2 = &obj->o_props[obj->o_num-1].p_vu.p_object;
+          obj = o2;
+        }
+    }
+  AMF_AddProp(obj, &prop);
+  if (prop.p_type == AMF_OBJECT)
+    (*depth)++;
+  return 0;
+}
+
 /* this request is formed from the parameters and used to initialize a new request,
  * thus it is a default settings list. All settings can be overriden by specifying the
  * parameters in the GET request. */
@@ -636,6 +723,11 @@ void processTCPrequest(STREAMING_SERVER 
   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);
+  /* backward compatibility, we always sent this as true before */
+  if (req.auth.av_len)
+    rtmp.Link.authflag = true;
+
+  rtmp.Link.extras = req.extras;
   rtmp.Link.token = req.token;
 
   LogPrintf("Connecting ... port: %d, app: %s\n", req.rtmpport, req.app);
@@ -1012,6 +1104,9 @@ ParseOption(char opt, char *arg, RTMP_RE
     case 'u':
       STR2AVAL(req->auth, arg);
       break;
+    case 'C':
+      parseAMF(&req->extras, optarg, &req->edepth);
+      break;
     case 'm':
       req->timeout = atoi(arg);
       break;
@@ -1085,6 +1180,7 @@ main(int argc, char **argv)
     {"swfVfy", 1, NULL, 'W'},
 #endif
     {"auth", 1, NULL, 'u'},
+    {"conn", 1, NULL, 'C'},
     {"flashVer", 1, NULL, 'f'},
     {"live", 0, NULL, 'v'},
     //{"flv",     1, NULL, 'o'},
@@ -1148,6 +1244,12 @@ main(int argc, char **argv)
 	  LogPrintf
 	    ("--auth|-u string        Authentication string to be appended to the connect string\n");
 	  LogPrintf
+	    ("--conn|-C type:data     Arbitrary AMF data to be appended to the connect string\n");
+	  LogPrintf
+	    ("                        B:boolean(0|1), S:string, N:number, O:object-flag(0|1),\n");
+	  LogPrintf
+	    ("                        NB:name:boolean, NS:name:string, NN:name:number\n");
+	  LogPrintf
 	    ("--flashVer|-f string    Flash version string (default: \"%s\")\n",
 	     DEFAULT_FLASH_VER);
 	  LogPrintf


More information about the rtmpdump mailing list