app is parsed from url
[rtmpdump.git] / rtmpdump.c
index 0e6e8fb..be524c9 100644 (file)
 #include <stdlib.h>
 #include <string.h>
 #include <math.h>
-#include <unistd.h>
+#include <stdio.h>
 
 #include <signal.h>            // to catch Ctrl-C
 #include <getopt.h>
 
+#include "rtmp.h"
+#include "log.h"
+#include "parseurl.h"
+
 #ifdef WIN32
 #define fseeko fseeko64
 #define ftello ftello64
-#include <winsock.h>
-#include <stdio.h>
 #include <io.h>
 #include <fcntl.h>
 #define        SET_BINMODE(f)  setmode(fileno(f), O_BINARY)
 #define        SET_BINMODE(f)
 #endif
 
-#include "rtmp.h"
-#include "log.h"
-#include "parseurl.h"
-
-int debuglevel = 1;
-
-#define RTMPDUMP_VERSION       "v2.0"
-
 #define RD_SUCCESS             0
 #define RD_FAILED              1
 #define RD_INCOMPLETE          2
@@ -89,19 +83,20 @@ uint32_t nIgnoredFrameCounter = 0;
 #define MAX_IGNORED_FRAMES     50
 
 FILE *file = 0;
-bool bCtrlC = false;
 
 void
 sigIntHandler(int sig)
 {
-  bCtrlC = true;
+  RTMP_ctrlC = true;
   LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig);
   // ignore all these signals now and let the connection close
-  signal(SIGHUP, SIG_IGN);
   signal(SIGINT, SIG_IGN);
-  signal(SIGPIPE, SIG_IGN);
   signal(SIGTERM, SIG_IGN);
+#ifndef WIN32
+  signal(SIGHUP, SIG_IGN);
+  signal(SIGPIPE, SIG_IGN);
   signal(SIGQUIT, SIG_IGN);
+#endif
 }
 
 int
@@ -407,7 +402,7 @@ WriteStream(RTMP * rtmp, char **buf,        // target pointer, maybe preallocated
              break;
            }
        }
-      char *ptr = *buf;
+      char *ptr = *buf, *pend = ptr+size+4;
 
       uint32_t nTimeStamp = 0; // use to return timestamp of last processed packet
 
@@ -426,7 +421,7 @@ WriteStream(RTMP * rtmp, char **buf,        // target pointer, maybe preallocated
 
          *ptr = packet.m_packetType;
          ptr++;
-         ptr += AMF_EncodeInt24(ptr, nPacketLen);
+         ptr = AMF_EncodeInt24(ptr, pend, nPacketLen);
 
          /*if(packet.m_packetType == 0x09) { // video
 
@@ -445,12 +440,12 @@ WriteStream(RTMP * rtmp, char **buf,      // target pointer, maybe preallocated
             Log(LOGDEBUG, "VIDEO: nTimeStamp: 0x%08X (%d)\n", nTimeStamp, nTimeStamp);
             } */
 
-         ptr += AMF_EncodeInt24(ptr, nTimeStamp);
+         ptr = AMF_EncodeInt24(ptr, pend, nTimeStamp);
          *ptr = (char) ((nTimeStamp & 0xFF000000) >> 24);
          ptr++;
 
          // stream id
-         ptr += AMF_EncodeInt24(ptr, 0);
+         ptr = AMF_EncodeInt24(ptr, pend, 0);
        }
 
       memcpy(ptr, packetBody, nPacketLen);
@@ -490,7 +485,7 @@ WriteStream(RTMP * rtmp, char **buf,        // target pointer, maybe preallocated
 
                  // we have to append a last tagSize!
                  prevTagSize = dataSize + 11;
-                 AMF_EncodeInt32(ptr + pos + 11 + dataSize, prevTagSize);
+                 AMF_EncodeInt32(ptr + pos + 11 + dataSize, pend, prevTagSize);
                  size += 4;
                  len += 4;
                }
@@ -515,7 +510,7 @@ WriteStream(RTMP * rtmp, char **buf,        // target pointer, maybe preallocated
 #endif
 
                      prevTagSize = dataSize + 11;
-                     AMF_EncodeInt32(ptr + pos + 11 + dataSize, prevTagSize);
+                     AMF_EncodeInt32(ptr + pos + 11 + dataSize, pend, prevTagSize);
                    }
                }
 
@@ -526,7 +521,7 @@ WriteStream(RTMP * rtmp, char **buf,        // target pointer, maybe preallocated
 
       if (packet.m_packetType != 0x16)
        {                       // FLV tag packets contain their own prevTagSize
-         AMF_EncodeInt32(ptr, prevTagSize);
+         AMF_EncodeInt32(ptr, pend, prevTagSize);
          //ptr += 4;
        }
 
@@ -541,7 +536,8 @@ WriteStream(RTMP * rtmp, char **buf,        // target pointer, maybe preallocated
       break;
     }
 
-  RTMPPacket_Free(&packet);
+  if (rtnGetNextMediaPacket)
+    RTMPPacket_Free(&packet);
   return ret;                  // no more media packets
 }
 
@@ -553,8 +549,8 @@ OpenResumeFile(const char *flvFile, // file name [in]
               uint32_t * nMetaHeaderSize,      // length of metaHeader [out]
               double *duration)        // duration of the stream in ms [out]
 {
-  const size_t bufferSize = 1024;
-  char buffer[bufferSize];
+  size_t bufferSize = 0;
+  char hbuf[16], *buffer = NULL;
 
   *nMetaHeaderSize = 0;
   *size = 0;
@@ -569,38 +565,38 @@ OpenResumeFile(const char *flvFile,       // file name [in]
 
   if (*size > 0)
     {
-      // verify FLV format and read header 
+      // verify FLV format and read header
       uint32_t prevTagSize = 0;
 
       // check we've got a valid FLV file to continue!
-      if (fread(buffer, 1, 13, *file) != 13)
+      if (fread(hbuf, 1, 13, *file) != 13)
        {
          Log(LOGERROR, "Couldn't read FLV file header!");
          return RD_FAILED;
        }
-      if (buffer[0] != 'F' || buffer[1] != 'L' || buffer[2] != 'V'
-         || buffer[3] != 0x01)
+      if (hbuf[0] != 'F' || hbuf[1] != 'L' || hbuf[2] != 'V'
+         || hbuf[3] != 0x01)
        {
-         Log(LOGERROR, "Inavlid FLV file!");
+         Log(LOGERROR, "Invalid FLV file!");
          return RD_FAILED;
        }
 
-      if ((buffer[4] & 0x05) == 0)
+      if ((hbuf[4] & 0x05) == 0)
        {
          Log(LOGERROR,
              "FLV file contains neither video nor audio, aborting!");
          return RD_FAILED;
        }
 
-      uint32_t dataOffset = AMF_DecodeInt32(buffer + 5);
+      uint32_t dataOffset = AMF_DecodeInt32(hbuf + 5);
       fseek(*file, dataOffset, SEEK_SET);
 
-      if (fread(buffer, 1, 4, *file) != 4)
+      if (fread(hbuf, 1, 4, *file) != 4)
        {
          Log(LOGERROR, "Invalid FLV file: missing first prevTagSize!");
          return RD_FAILED;
        }
-      prevTagSize = AMF_DecodeInt32(buffer);
+      prevTagSize = AMF_DecodeInt32(hbuf);
       if (prevTagSize != 0)
        {
          Log(LOGWARNING,
@@ -615,18 +611,22 @@ OpenResumeFile(const char *flvFile,       // file name [in]
       while (pos < *size - 4 && !bFoundMetaHeader)
        {
          fseeko(*file, pos, SEEK_SET);
-         if (fread(buffer, 1, 4, *file) != 4)
+         if (fread(hbuf, 1, 4, *file) != 4)
            break;
 
-         uint32_t dataSize = AMF_DecodeInt24(buffer + 1);
+         uint32_t dataSize = AMF_DecodeInt24(hbuf + 1);
 
-         if (buffer[0] == 0x12)
+         if (hbuf[0] == 0x12)
            {
              if (dataSize > bufferSize)
                {
-                 Log(LOGERROR, "%s: dataSize (%d) > bufferSize (%d)",
-                     __FUNCTION__, dataSize, bufferSize);
-                 return RD_FAILED;
+                  /* round up to next page boundary */
+                  bufferSize = dataSize + 4095;
+                 bufferSize ^= (bufferSize & 4095);
+                 free(buffer);
+                  buffer = malloc(bufferSize);
+                  if (!buffer)
+                   return RD_FAILED;
                }
 
              fseeko(*file, pos + 11, SEEK_SET);
@@ -673,6 +673,7 @@ OpenResumeFile(const char *flvFile, // file name [in]
          pos += (dataSize + 11 + 4);
        }
 
+      free(buffer);
       if (!bFoundMetaHeader)
        Log(LOGWARNING, "Couldn't locate meta data!");
     }
@@ -698,13 +699,15 @@ GetLastKeyframe(FILE * file,      // output file [in]
   size = ftello(file);
 
   fseek(file, 4, SEEK_SET);
-  fread(&dataType, sizeof(uint8_t), 1, file);
+  if (fread(&dataType, sizeof(uint8_t), 1, file) != 1)
+    return RD_FAILED;
+
   bAudioOnly = (dataType & 0x4) && !(dataType & 0x1);
 
   Log(LOGDEBUG, "bAudioOnly: %d, size: %llu", bAudioOnly,
       (unsigned long long) size);
 
-  // ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams) 
+  // ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams)
 
   //if(!bAudioOnly) // we have to handle video/video+audio different since we have non-seekable frames
   //{
@@ -798,7 +801,7 @@ GetLastKeyframe(FILE * file,        // output file [in]
 
   *dSeek = AMF_DecodeInt24(buffer + 4);        // set seek position to keyframe tmestamp
   *dSeek |= (buffer[7] << 24);
-  //} 
+  //}
   //else // handle audio only, we can seek anywhere we'd like
   //{
   //}
@@ -1013,7 +1016,7 @@ Download(RTMP * rtmp,             // connected RTMP object
 #endif
 
     }
-  while (!bCtrlC && nRead > -1 && RTMP_IsConnected(rtmp));
+  while (!RTMP_ctrlC && nRead > -1 && RTMP_IsConnected(rtmp));
   free(buffer);
 
   Log(LOGDEBUG, "WriteStream returned: %d", nRead);
@@ -1036,7 +1039,7 @@ Download(RTMP * rtmp,             // connected RTMP object
   if (nRead == -3)
     return RD_SUCCESS;
 
-  if ((duration > 0 && *percent < 99.9) || bCtrlC || nRead < 0
+  if ((duration > 0 && *percent < 99.9) || RTMP_ctrlC || nRead < 0
       || RTMP_IsTimedout(rtmp))
     {
       return RD_INCOMPLETE;
@@ -1096,19 +1099,31 @@ main(int argc, char **argv)
   AVal swfHash = { 0, 0 };
   uint32_t swfSize = 0;
   AVal flashVer = { 0, 0 };
+  AVal token = { 0, 0 };
   char *sockshost = 0;
 
+#ifdef CRYPTO
+  unsigned char hash[HASHLEN];
+#endif
+
   char *flvFile = 0;
 
-  char DEFAULT_FLASH_VER[] = "LNX 10,0,22,87";
+#undef OSS
+#ifdef WIN32
+#define        OSS     "WIN"
+#else
+#define OSS    "LNX"
+#endif
+
+  char DEFAULT_FLASH_VER[] = OSS " 10,0,22,87";
 
-  signal(SIGHUP, sigIntHandler);
   signal(SIGINT, sigIntHandler);
-  signal(SIGPIPE, sigIntHandler);
   signal(SIGTERM, sigIntHandler);
+#ifndef WIN32
+  signal(SIGHUP, sigIntHandler);
+  signal(SIGPIPE, sigIntHandler);
   signal(SIGQUIT, sigIntHandler);
-
-  /* sleep(30); */
+#endif
 
   // Check for --quiet option before printing any output
   int index = 0;
@@ -1122,7 +1137,16 @@ main(int argc, char **argv)
 
   LogPrintf("RTMPDump %s\n", RTMPDUMP_VERSION);
   LogPrintf
-    ("(c) 2009 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n");
+    ("(c) 2010 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n");
+
+  if (!InitSockets())
+    {
+      Log(LOGERROR,
+         "Couldn't load sockets support on your platform, exiting!");
+      return RD_FAILED;
+    }
+
+  /* sleep(30); */
 
   int opt;
   struct option longopts[] = {
@@ -1141,6 +1165,7 @@ main(int argc, char **argv)
 #ifdef CRYPTO
     {"swfhash", 1, NULL, 'w'},
     {"swfsize", 1, NULL, 'x'},
+    {"swfVfy", 1, NULL, 'W'},
 #endif
     {"flashVer", 1, NULL, 'f'},
     {"live", 0, NULL, 'v'},
@@ -1152,6 +1177,7 @@ main(int argc, char **argv)
     {"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'},
@@ -1161,7 +1187,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:w:x:S:#",
+                     "hVveqzr:s:t:p:a:b:f:o:u:n:c:l:y:m:k:d:A:B:T:w:x:W:S:#",
                      longopts, NULL)) != -1)
     {
       switch (opt)
@@ -1192,6 +1218,8 @@ main(int argc, char **argv)
            ("--swfhash|-w hexstring  SHA256 hash of the decompressed SWF file (32 bytes)\n");
          LogPrintf
            ("--swfsize|-x num        Size of the decompressed SWF file, required for SWFVerification\n");
+         LogPrintf
+           ("--swfVfy|-W url         URL to player swf file, compute hash/size automatically\n");
 #endif
          LogPrintf
            ("--auth|-u string        Authentication string to be appended to the connect string\n");
@@ -1214,6 +1242,8 @@ main(int argc, char **argv)
          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
            ("--buffer|-b             Buffer time in milliseconds (default: %lu), this option makes only sense in stdout mode (-o -)\n",
@@ -1226,20 +1256,20 @@ main(int argc, char **argv)
          LogPrintf("--verbose|-V            Verbose command output.\n");
          LogPrintf("--debug|-z              Debug level command output.\n");
          LogPrintf
-           ("If you don't pass parameters for swfUrl, pageUrl, app or auth these propertiews will not be included in the connect ");
+           ("If you don't pass parameters for swfUrl, pageUrl, or auth these properties will not be included in the connect ");
          LogPrintf("packet.\n\n");
          return RD_SUCCESS;
 #ifdef CRYPTO
        case 'w':
          {
            int res = hex2bin(optarg, &swfHash.av_val);
-           if (res != 32)
+           if (res != HASHLEN)
              {
                swfHash.av_val = NULL;
                Log(LOGWARNING,
-                   "Couldn't parse swf hash hex string, not heyxstring or not 32 bytes, ignoring!");
+                   "Couldn't parse swf hash hex string, not heyxstring or not %d bytes, ignoring!", HASHLEN);
              }
-           swfHash.av_len = 32;
+           swfHash.av_len = HASHLEN;
            break;
          }
        case 'x':
@@ -1255,6 +1285,14 @@ main(int argc, char **argv)
              }
            break;
          }
+        case 'W':
+         STR2AVAL(swfUrl, optarg);
+          if (RTMP_HashSWF(optarg, &swfSize, hash, 1) == 0)
+            {
+              swfHash.av_val = (char *)hash;
+              swfHash.av_len = HASHLEN;
+            }
+          break;
 #endif
        case 'k':
          nSkipKeyFrames = atoi(optarg);
@@ -1382,6 +1420,9 @@ main(int argc, char **argv)
        case 'B':
          dStopOffset = (int) (atof(optarg) * 1000.0);
          break;
+       case 'T':
+         STR2AVAL(token, optarg);
+         break;
        case '#':
          bHashes = true;
          break;
@@ -1472,12 +1513,6 @@ main(int argc, char **argv)
       STR2AVAL(flashVer, DEFAULT_FLASH_VER);
     }
 
-  if (!InitSockets())
-    {
-      Log(LOGERROR,
-         "Couldn't load sockets support on your platform, exiting!");
-      return RD_FAILED;
-    }
 
   if (tcUrl.av_len == 0 && app.av_len != 0)
     {
@@ -1510,9 +1545,10 @@ main(int argc, char **argv)
                   &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) 
+  // ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams)
   if (bResume)
     {
       nStatus =
@@ -1569,7 +1605,7 @@ main(int argc, char **argv)
   netstackdump_read = fopen("netstackdump_read", "wb");
 #endif
 
-  while (!bCtrlC)
+  while (!RTMP_ctrlC)
     {
       Log(LOGDEBUG, "Setting buffer time to: %dms", bufferTime);
       RTMP_SetBufferMS(&rtmp, bufferTime);
@@ -1579,7 +1615,7 @@ main(int argc, char **argv)
          first = 0;
          LogPrintf("Connecting ...\n");
 
-         if (!RTMP_Connect(&rtmp))
+         if (!RTMP_Connect(&rtmp, NULL))
            {
              nStatus = RD_FAILED;
              break;