]> granicus.if.org Git - rtmpdump/commitdiff
Add --conn|-C option to append arbitrary AMF data to connect request
authorhyc <hyc@400ebc74-4327-4243-bc38-086b20814532>
Sat, 9 Jan 2010 00:23:22 +0000 (00:23 +0000)
committerhyc <hyc@400ebc74-4327-4243-bc38-086b20814532>
Sat, 9 Jan 2010 00:23:22 +0000 (00:23 +0000)
git-svn-id: svn://svn.mplayerhq.hu/rtmpdump/trunk@212 400ebc74-4327-4243-bc38-086b20814532

rtmp.c
rtmp.h
rtmpdump.c
rtmpsrv.c
streams.c

diff --git a/rtmp.c b/rtmp.c
index 9174d7561fdcfa8a4b85da80d1d89f8213fe8dd8..2dfc717e57f9712bf476923faac6937c8234231e 100644 (file)
--- a/rtmp.c
+++ b/rtmp.c
@@ -956,6 +956,16 @@ SendConnectPacket(RTMP *r, RTMPPacket *cp)
       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);
diff --git a/rtmp.h b/rtmp.h
index cd27ac297a5b00ee01273fd384fd6499b9ba76c1..aa20767c7ed2ee8446edce6acdecade64638698f 100644 (file)
--- a/rtmp.h
+++ b/rtmp.h
@@ -135,6 +135,7 @@ typedef struct RTMP_LNK
   AVal subscribepath;
   AVal token;
   bool authflag;
+  AMFObject extras;
 
   double seekTime;
   uint32_t length;
index 3f6f7b061f60eaa99fb7943c9b5ae9a0dc0177ee..751727d50b3d92f8992544d2caae3f305aa235a2 100644 (file)
@@ -1050,6 +1050,91 @@ Download(RTMP * rtmp,            // connected RTMP object
 
 #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)
 {
@@ -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)
@@ -1224,6 +1312,12 @@ main(int argc, char **argv)
 #endif
          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);
@@ -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;
 
index 70c81496d3452506213c5d2f4d16787952742ace..9a3f861dffdf3826e6e729e6be20ae0a15c59916 100644 (file)
--- a/rtmpsrv.c
+++ b/rtmpsrv.c
@@ -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, double ID)
   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, RTMP * r, const char *body, unsigned int n
       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, RTMP * r, const char *body, unsigned int n
           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, RTMP * r, const char *body, unsigned int n
             {
               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, RTMP * r, const char *body, unsigned int n
     }
   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, RTMP * r, const char *body, unsigned int n
             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, RTMP *r, RTMPPacket *packet)
 
           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, RTMP *r, RTMPPacket *packet)
          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 port)
       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);
index 9120e7823c1e8ec4b96da81dd31ece416dfb59f4..303130f391ae5fbcb3a70e259687c8d54aeff248 100644 (file)
--- a/streams.c
+++ b/streams.c
@@ -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 * server,  // server socket and state (ou
   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_REQUEST * req)
     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'},
@@ -1147,6 +1243,12 @@ main(int argc, char **argv)
 #endif
          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);