Ignore requests without playpath
[rtmpdump.git] / rtmpdump.c
index f35fc22..13741a7 100644 (file)
@@ -14,7 +14,8 @@
  *
  *  You should have received a copy of the GNU General Public License
  *  along with RTMPDump; see the file COPYING.  If not, write to
- *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ *  Boston, MA  02110-1301, USA.
  *  http://www.gnu.org/copyleft/gpl.html
  *
  */
 #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 "librtmp/rtmp_sys.h"
+#include "librtmp/log.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
+#define RD_NO_CONNECT          3
+
+#define DEF_TIMEOUT    30      /* seconds */
+#define DEF_BUFTIME    (10 * 60 * 60 * 1000)   /* 10 hours default */
+#define DEF_SKIPFRM    0
 
 // starts sockets
-bool
+int
 InitSockets()
 {
 #ifdef WIN32
@@ -64,7 +63,7 @@ InitSockets()
   version = MAKEWORD(1, 1);
   return (WSAStartup(version, &wsaData) == 0);
 #else
-  return true;
+  return TRUE;
 #endif
 }
 
@@ -89,13 +88,12 @@ uint32_t nIgnoredFrameCounter = 0;
 #define MAX_IGNORED_FRAMES     50
 
 FILE *file = 0;
-bool bCtrlC = false;
 
 void
 sigIntHandler(int sig)
 {
-  bCtrlC = true;
-  LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig);
+  RTMP_ctrlC = TRUE;
+  RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig);
   // ignore all these signals now and let the connection close
   signal(SIGINT, SIG_IGN);
   signal(SIGTERM, SIG_IGN);
@@ -106,446 +104,31 @@ sigIntHandler(int sig)
 #endif
 }
 
-int
-WriteHeader(char **buf,                // target pointer, maybe preallocated
-           unsigned int len    // length of buffer if preallocated
-  )
+#define HEX2BIN(a)      (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
+int hex2bin(char *str, char **hex)
 {
-  char flvHeader[] = { 'F', 'L', 'V', 0x01,
-    0x05,                      // video + audio, we finalize later if the value is different
-    0x00, 0x00, 0x00, 0x09,
-    0x00, 0x00, 0x00, 0x00     // first prevTagSize=0
-  };
+  char *ptr;
+  int i, l = strlen(str);
 
-  unsigned int size = sizeof(flvHeader);
+  if (l & 1)
+       return 0;
 
-  if (size > len)
-    {
-      *buf = (char *) realloc(*buf, size);
-      if (*buf == 0)
-       {
-         Log(LOGERROR, "Couldn't reallocate memory!");
-         return -1;            // fatal error
-       }
-    }
-  memcpy(*buf, flvHeader, sizeof(flvHeader));
-  return size;
+  *hex = malloc(l/2);
+  ptr = *hex;
+  if (!ptr)
+    return 0;
+
+  for (i=0; i<l; i+=2)
+    *ptr++ = (HEX2BIN(str[i]) << 4) | HEX2BIN(str[i+1]);
+  return l/2;
 }
 
 static const AVal av_onMetaData = AVC("onMetaData");
 static const AVal av_duration = AVC("duration");
-
-// Returns -3 if Play.Close/Stop, -2 if fatal error, -1 if no more media packets, 0 if ignorable error, >0 if there is a media packet
-int
-WriteStream(RTMP * rtmp, char **buf,   // target pointer, maybe preallocated
-           unsigned int len,   // length of buffer if preallocated
-           uint32_t * tsm,     // pointer to timestamp, will contain timestamp of last video packet returned
-           bool bResume,       // resuming mode, will not write FLV header and compare metaHeader and first kexframe
-           bool bLiveStream,   // live mode, will not report absolute timestamps
-           uint32_t nResumeTS, // resume keyframe timestamp
-           char *metaHeader,   // pointer to meta header (if bResume == TRUE)
-           uint32_t nMetaHeaderSize,   // length of meta header, if zero meta header check omitted (if bResume == TRUE)
-           char *initialFrame, // pointer to initial keyframe (no FLV header or tagSize, raw data) (if bResume == TRUE)
-           uint8_t initialFrameType,   // initial frame type (audio or video)
-           uint32_t nInitialFrameSize, // length of initial frame in bytes, if zero initial frame check omitted (if bResume == TRUE)
-           uint8_t * dataType  // whenever we get a video/audio packet we set an appropriate flag here, this will be later written to the FLV header
-  )
-{
-  static bool bStopIgnoring = false;
-  static bool bFoundKeyframe = false;
-  static bool bFoundFlvKeyframe = false;
-
-  uint32_t prevTagSize = 0;
-  int rtnGetNextMediaPacket = 0, ret = -1;
-  RTMPPacket packet = { 0 };
-
-  rtnGetNextMediaPacket = RTMP_GetNextMediaPacket(rtmp, &packet);
-  while (rtnGetNextMediaPacket)
-    {
-      char *packetBody = packet.m_body;
-      unsigned int nPacketLen = packet.m_nBodySize;
-
-      // Return -3 if this was completed nicely with invoke message Play.Stop or Play.Complete
-      if (rtnGetNextMediaPacket == 2)
-       {
-         Log(LOGDEBUG,
-             "Got Play.Complete or Play.Stop from server. Assuming stream is complete");
-         ret = -3;
-         break;
-       }
-
-      // skip video info/command packets
-      if (packet.m_packetType == 0x09 &&
-         nPacketLen == 2 && ((*packetBody & 0xf0) == 0x50))
-       {
-         ret = 0;
-         break;
-       }
-
-      if (packet.m_packetType == 0x09 && nPacketLen <= 5)
-       {
-         Log(LOGWARNING, "ignoring too small video packet: size: %d",
-             nPacketLen);
-         ret = 0;
-         break;
-       }
-      if (packet.m_packetType == 0x08 && nPacketLen <= 1)
-       {
-         Log(LOGWARNING, "ignoring too small audio packet: size: %d",
-             nPacketLen);
-         ret = 0;
-         break;
-       }
-#ifdef _DEBUG
-      Log(LOGDEBUG, "type: %02X, size: %d, TS: %d ms, abs TS: %d",
-         packet.m_packetType, nPacketLen, packet.m_nTimeStamp,
-         packet.m_hasAbsTimestamp);
-      if (packet.m_packetType == 0x09)
-       Log(LOGDEBUG, "frametype: %02X", (*packetBody & 0xf0));
-#endif
-
-      // check the header if we get one
-      if (bResume && packet.m_nTimeStamp == 0)
-       {
-         if (nMetaHeaderSize > 0 && packet.m_packetType == 0x12)
-           {
-
-             AMFObject metaObj;
-             int nRes = AMF_Decode(&metaObj, packetBody, nPacketLen, false);
-             if (nRes >= 0)
-               {
-                 AVal metastring;
-                 AMFProp_GetString(AMF_GetProp(&metaObj, NULL, 0),
-                                   &metastring);
-
-                 if (AVMATCH(&metastring, &av_onMetaData))
-                   {
-                     // compare
-                     if ((nMetaHeaderSize != nPacketLen) ||
-                         (memcmp(metaHeader, packetBody, nMetaHeaderSize) !=
-                          0))
-                       {
-                         ret = -2;
-                       }
-                   }
-                 AMF_Reset(&metaObj);
-                 if (ret == -2)
-                   break;
-               }
-           }
-
-         // check first keyframe to make sure we got the right position in the stream!
-         // (the first non ignored frame)
-         if (nInitialFrameSize > 0)
-           {
-
-             // video or audio data
-             if (packet.m_packetType == initialFrameType
-                 && nInitialFrameSize == nPacketLen)
-               {
-                 // we don't compare the sizes since the packet can contain several FLV packets, just make
-                 // sure the first frame is our keyframe (which we are going to rewrite)
-                 if (memcmp(initialFrame, packetBody, nInitialFrameSize) ==
-                     0)
-                   {
-                     Log(LOGDEBUG, "Checked keyframe successfully!");
-                     bFoundKeyframe = true;
-                     ret = 0;  // ignore it! (what about audio data after it? it is handled by ignoring all 0ms frames, see below)
-                     break;
-                   }
-               }
-
-             // hande FLV streams, even though the server resends the keyframe as an extra video packet
-             // it is also included in the first FLV stream chunk and we have to compare it and
-             // filter it out !!
-             //
-             if (packet.m_packetType == 0x16)
-               {
-                 // basically we have to find the keyframe with the correct TS being nResumeTS
-                 unsigned int pos = 0;
-                 uint32_t ts = 0;
-
-                 while (pos + 11 < nPacketLen)
-                   {
-                     uint32_t dataSize = AMF_DecodeInt24(packetBody + pos + 1);        // size without header (11) and prevTagSize (4)
-                     ts = AMF_DecodeInt24(packetBody + pos + 4);
-                     ts |= (packetBody[pos + 7] << 24);
-
-#ifdef _DEBUG
-                     Log(LOGDEBUG,
-                         "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms",
-                         packetBody[pos], dataSize, ts);
-#endif
-                     // ok, is it a keyframe!!!: well doesn't work for audio!
-                     if (packetBody[pos /*6928, test 0 */ ] ==
-                         initialFrameType
-                         /* && (packetBody[11]&0xf0) == 0x10 */ )
-                       {
-                         if (ts == nResumeTS)
-                           {
-                             Log(LOGDEBUG,
-                                 "Found keyframe with resume-keyframe timestamp!");
-                             if (nInitialFrameSize != dataSize
-                                 || memcmp(initialFrame,
-                                           packetBody + pos + 11,
-                                           nInitialFrameSize) != 0)
-                               {
-                                 Log(LOGERROR,
-                                     "FLV Stream: Keyframe doesn't match!");
-                                 ret = -2;
-                                 break;
-                               }
-                             bFoundFlvKeyframe = true;
-
-                             // ok, skip this packet
-                             // check whether skipable:
-                             if (pos + 11 + dataSize + 4 > nPacketLen)
-                               {
-                                 Log(LOGWARNING,
-                                     "Non skipable packet since it doesn't end with chunk, stream corrupt!");
-                                 ret = -2;
-                                 break;
-                               }
-                             packetBody += (pos + 11 + dataSize + 4);
-                             nPacketLen -= (pos + 11 + dataSize + 4);
-
-                             goto stopKeyframeSearch;
-
-                           }
-                         else if (nResumeTS < ts)
-                           {
-                             goto stopKeyframeSearch;  // the timestamp ts will only increase with further packets, wait for seek
-                           }
-                       }
-                     pos += (11 + dataSize + 4);
-                   }
-                 if (ts < nResumeTS)
-                   {
-                     Log(LOGERROR,
-                         "First packet does not contain keyframe, all timestamps are smaller than the keyframe timestamp, so probably the resume seek failed?");
-                   }
-               stopKeyframeSearch:
-                 ;
-                 if (!bFoundFlvKeyframe)
-                   {
-                     Log(LOGERROR,
-                         "Couldn't find the seeked keyframe in this chunk!");
-                     ret = 0;
-                     break;
-                   }
-               }
-           }
-       }
-
-      if (bResume && packet.m_nTimeStamp > 0
-         && (bFoundFlvKeyframe || bFoundKeyframe))
-       {
-         // another problem is that the server can actually change from 09/08 video/audio packets to an FLV stream
-         // or vice versa and our keyframe check will prevent us from going along with the new stream if we resumed
-         //
-         // in this case set the 'found keyframe' variables to true
-         // We assume that if we found one keyframe somewhere and were already beyond TS > 0 we have written
-         // data to the output which means we can accept all forthcoming data inclusing the change between 08/09 <-> FLV
-         // packets
-         bFoundFlvKeyframe = true;
-         bFoundKeyframe = true;
-       }
-
-      // skip till we find out keyframe (seeking might put us somewhere before it)
-      if (bResume && !bFoundKeyframe && packet.m_packetType != 0x16)
-       {
-         Log(LOGWARNING,
-             "Stream does not start with requested frame, ignoring data... ");
-         nIgnoredFrameCounter++;
-         if (nIgnoredFrameCounter > MAX_IGNORED_FRAMES)
-           ret = -2;           // fatal error, couldn't continue stream
-         else
-           ret = 0;
-         break;
-       }
-      // ok, do the same for FLV streams
-      if (bResume && !bFoundFlvKeyframe && packet.m_packetType == 0x16)
-       {
-         Log(LOGWARNING,
-             "Stream does not start with requested FLV frame, ignoring data... ");
-         nIgnoredFlvFrameCounter++;
-         if (nIgnoredFlvFrameCounter > MAX_IGNORED_FRAMES)
-           ret = -2;
-         else
-           ret = 0;
-         break;
-       }
-
-      // if bResume, we continue a stream, we have to ignore the 0ms frames since these are the first keyframes, we've got these
-      // so don't mess around with multiple copies sent by the server to us! (if the keyframe is found at a later position
-      // there is only one copy and it will be ignored by the preceding if clause)
-      if (!bStopIgnoring && bResume && packet.m_packetType != 0x16)
-       {                       // exclude type 0x16 (FLV) since it can conatin several FLV packets
-         if (packet.m_nTimeStamp == 0)
-           {
-             ret = 0;
-             break;
-           }
-         else
-           {
-             bStopIgnoring = true;     // stop ignoring packets
-           }
-       }
-
-      // calculate packet size and reallocate buffer if necessary
-      unsigned int size = nPacketLen
-       +
-       ((packet.m_packetType == 0x08 || packet.m_packetType == 0x09
-         || packet.m_packetType ==
-         0x12) ? 11 : 0) + (packet.m_packetType != 0x16 ? 4 : 0);
-
-      if (size + 4 > len)
-       {                       // the extra 4 is for the case of an FLV stream without a last prevTagSize (we need extra 4 bytes to append it)
-         *buf = (char *) realloc(*buf, size + 4);
-         if (*buf == 0)
-           {
-             Log(LOGERROR, "Couldn't reallocate memory!");
-             ret = -1;         // fatal error
-             break;
-           }
-       }
-      char *ptr = *buf;
-
-      uint32_t nTimeStamp = 0; // use to return timestamp of last processed packet
-
-      // audio (0x08), video (0x09) or metadata (0x12) packets :
-      // construct 11 byte header then add rtmp packet's data
-      if (packet.m_packetType == 0x08 || packet.m_packetType == 0x09
-         || packet.m_packetType == 0x12)
-       {
-         // set data type
-         *dataType |=
-           (((packet.m_packetType == 0x08) << 2) | (packet.m_packetType ==
-                                                    0x09));
-
-         nTimeStamp = nResumeTS + packet.m_nTimeStamp;
-         prevTagSize = 11 + nPacketLen;
-
-         *ptr = packet.m_packetType;
-         ptr++;
-         ptr += AMF_EncodeInt24(ptr, nPacketLen);
-
-         /*if(packet.m_packetType == 0x09) { // video
-
-            // H264 fix:
-            if((packetBody[0] & 0x0f) == 7) { // CodecId = H264
-            uint8_t packetType = *(packetBody+1);
-
-            uint32_t ts = AMF_DecodeInt24(packetBody+2); // composition time
-            int32_t cts = (ts+0xff800000)^0xff800000;
-            Log(LOGDEBUG, "cts  : %d\n", cts);
-
-            nTimeStamp -= cts;
-            // get rid of the composition time
-            CRTMP::EncodeInt24(packetBody+2, 0);
-            }
-            Log(LOGDEBUG, "VIDEO: nTimeStamp: 0x%08X (%d)\n", nTimeStamp, nTimeStamp);
-            } */
-
-         ptr += AMF_EncodeInt24(ptr, nTimeStamp);
-         *ptr = (char) ((nTimeStamp & 0xFF000000) >> 24);
-         ptr++;
-
-         // stream id
-         ptr += AMF_EncodeInt24(ptr, 0);
-       }
-
-      memcpy(ptr, packetBody, nPacketLen);
-      unsigned int len = nPacketLen;
-
-      // correct tagSize and obtain timestamp if we have an FLV stream
-      if (packet.m_packetType == 0x16)
-       {
-         unsigned int pos = 0;
-
-         while (pos + 11 < nPacketLen)
-           {
-             uint32_t dataSize = AMF_DecodeInt24(packetBody + pos + 1);        // size without header (11) and without prevTagSize (4)
-             nTimeStamp = AMF_DecodeInt24(packetBody + pos + 4);
-             nTimeStamp |= (packetBody[pos + 7] << 24);
-
-             /*
-                CRTMP::EncodeInt24(ptr+pos+4, nTimeStamp);
-                ptr[pos+7] = (nTimeStamp>>24)&0xff;// */
-
-             // set data type
-             *dataType |=
-               (((*(packetBody + pos) ==
-                  0x08) << 2) | (*(packetBody + pos) == 0x09));
-
-             if (pos + 11 + dataSize + 4 > nPacketLen)
-               {
-                 if (pos + 11 + dataSize > nPacketLen)
-                   {
-                     Log(LOGERROR,
-                         "Wrong data size (%lu), stream corrupted, aborting!",
-                         dataSize);
-                     ret = -2;
-                     break;
-                   }
-                 Log(LOGWARNING, "No tagSize found, appending!");
-
-                 // we have to append a last tagSize!
-                 prevTagSize = dataSize + 11;
-                 AMF_EncodeInt32(ptr + pos + 11 + dataSize, prevTagSize);
-                 size += 4;
-                 len += 4;
-               }
-             else
-               {
-                 prevTagSize =
-                   AMF_DecodeInt32(packetBody + pos + 11 + dataSize);
-
-#ifdef _DEBUG
-                 Log(LOGDEBUG,
-                     "FLV Packet: type %02X, dataSize: %lu, tagSize: %lu, timeStamp: %lu ms",
-                     (unsigned char) packetBody[pos], dataSize, prevTagSize,
-                     nTimeStamp);
-#endif
-
-                 if (prevTagSize != (dataSize + 11))
-                   {
-#ifdef _DEBUG
-                     Log(LOGWARNING,
-                         "Tag and data size are not consitent, writing tag size according to dataSize+11: %d",
-                         dataSize + 11);
-#endif
-
-                     prevTagSize = dataSize + 11;
-                     AMF_EncodeInt32(ptr + pos + 11 + dataSize, prevTagSize);
-                   }
-               }
-
-             pos += prevTagSize + 4;   //(11+dataSize+4);
-           }
-       }
-      ptr += len;
-
-      if (packet.m_packetType != 0x16)
-       {                       // FLV tag packets contain their own prevTagSize
-         AMF_EncodeInt32(ptr, prevTagSize);
-         //ptr += 4;
-       }
-
-      // In non-live this nTimeStamp can contain an absolute TS.
-      // Update ext timestamp with this absolute offset in non-live mode otherwise report the relative one
-      // LogPrintf("\nDEBUG: type: %02X, size: %d, pktTS: %dms, TS: %dms, bLiveStream: %d", packet.m_packetType, nPacketLen, packet.m_nTimeStamp, nTimeStamp, bLiveStream);
-      if (tsm)
-       *tsm = bLiveStream ? packet.m_nTimeStamp : nTimeStamp;
-
-
-      ret = size;
-      break;
-    }
-
-  RTMPPacket_Free(&packet);
-  return ret;                  // no more media packets
-}
+static const AVal av_conn = AVC("conn");
+static const AVal av_token = AVC("token");
+static const AVal av_playlist = AVC("playlist");
+static const AVal av_true = AVC("true");
 
 int
 OpenResumeFile(const char *flvFile,    // file name [in]
@@ -555,8 +138,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;
@@ -571,64 +154,68 @@ 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!");
+         RTMP_Log(RTMP_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!");
+         RTMP_Log(RTMP_LOGERROR, "Invalid FLV file!");
          return RD_FAILED;
        }
 
-      if ((buffer[4] & 0x05) == 0)
+      if ((hbuf[4] & 0x05) == 0)
        {
-         Log(LOGERROR,
+         RTMP_Log(RTMP_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!");
+         RTMP_Log(RTMP_LOGERROR, "Invalid FLV file: missing first prevTagSize!");
          return RD_FAILED;
        }
-      prevTagSize = AMF_DecodeInt32(buffer);
+      prevTagSize = AMF_DecodeInt32(hbuf);
       if (prevTagSize != 0)
        {
-         Log(LOGWARNING,
+         RTMP_Log(RTMP_LOGWARNING,
              "First prevTagSize is not zero: prevTagSize = 0x%08X",
              prevTagSize);
        }
 
       // go through the file to find the meta data!
       off_t pos = dataOffset + 4;
-      bool bFoundMetaHeader = false;
+      int bFoundMetaHeader = FALSE;
 
       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);
@@ -636,10 +223,10 @@ OpenResumeFile(const char *flvFile,       // file name [in]
                break;
 
              AMFObject metaObj;
-             int nRes = AMF_Decode(&metaObj, buffer, dataSize, false);
+             int nRes = AMF_Decode(&metaObj, buffer, dataSize, FALSE);
              if (nRes < 0)
                {
-                 Log(LOGERROR, "%s, error decoding meta data packet",
+                 RTMP_Log(RTMP_LOGERROR, "%s, error decoding meta data packet",
                      __FUNCTION__);
                  break;
                }
@@ -663,10 +250,10 @@ OpenResumeFile(const char *flvFile,       // file name [in]
                      (&metaObj, &av_duration, &prop))
                    {
                      *duration = AMFProp_GetNumber(&prop);
-                     Log(LOGDEBUG, "File has duration: %f", *duration);
+                     RTMP_Log(RTMP_LOGDEBUG, "File has duration: %f", *duration);
                    }
 
-                 bFoundMetaHeader = true;
+                 bFoundMetaHeader = TRUE;
                  break;
                }
              //metaObj.Reset();
@@ -675,8 +262,9 @@ OpenResumeFile(const char *flvFile, // file name [in]
          pos += (dataSize + 11 + 4);
        }
 
+      free(buffer);
       if (!bFoundMetaHeader)
-       Log(LOGWARNING, "Couldn't locate meta data!");
+       RTMP_Log(RTMP_LOGWARNING, "Couldn't locate meta data!");
     }
 
   return RD_SUCCESS;
@@ -693,20 +281,22 @@ GetLastKeyframe(FILE * file,      // output file [in]
   const size_t bufferSize = 16;
   char buffer[bufferSize];
   uint8_t dataType;
-  bool bAudioOnly;
+  int bAudioOnly;
   off_t size;
 
   fseek(file, 0, SEEK_END);
   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,
+  RTMP_Log(RTMP_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
   //{
@@ -721,7 +311,7 @@ GetLastKeyframe(FILE * file,        // output file [in]
     skipkeyframe:
       if (size - tsize < 13)
        {
-         Log(LOGERROR,
+         RTMP_Log(RTMP_LOGERROR,
              "Unexpected start of file, error in tag sizes, couldn't arrive at prevTagSize=0");
          return RD_FAILED;
        }
@@ -729,22 +319,22 @@ GetLastKeyframe(FILE * file,      // output file [in]
       xread = fread(buffer, 1, 4, file);
       if (xread != 4)
        {
-         Log(LOGERROR, "Couldn't read prevTagSize from file!");
+         RTMP_Log(RTMP_LOGERROR, "Couldn't read prevTagSize from file!");
          return RD_FAILED;
        }
 
       prevTagSize = AMF_DecodeInt32(buffer);
-      //Log(LOGDEBUG, "Last packet: prevTagSize: %d", prevTagSize);
+      //RTMP_Log(RTMP_LOGDEBUG, "Last packet: prevTagSize: %d", prevTagSize);
 
       if (prevTagSize == 0)
        {
-         Log(LOGERROR, "Couldn't find keyframe to resume from!");
+         RTMP_Log(RTMP_LOGERROR, "Couldn't find keyframe to resume from!");
          return RD_FAILED;
        }
 
       if (prevTagSize < 0 || prevTagSize > size - 4 - 13)
        {
-         Log(LOGERROR,
+         RTMP_Log(RTMP_LOGERROR,
              "Last tag size must be greater/equal zero (prevTagSize=%d) and smaller then filesize, corrupt file!",
              prevTagSize);
          return RD_FAILED;
@@ -755,14 +345,14 @@ GetLastKeyframe(FILE * file,      // output file [in]
       fseeko(file, size - tsize, SEEK_SET);
       if (fread(buffer, 1, 12, file) != 12)
        {
-         Log(LOGERROR, "Couldn't read header!");
+         RTMP_Log(RTMP_LOGERROR, "Couldn't read header!");
          return RD_FAILED;
        }
       //*
 #ifdef _DEBUG
       uint32_t ts = AMF_DecodeInt24(buffer + 4);
       ts |= (buffer[7] << 24);
-      Log(LOGDEBUG, "%02X: TS: %d ms", buffer[0], ts);
+      RTMP_Log(RTMP_LOGDEBUG, "%02X: TS: %d ms", buffer[0], ts);
 #endif //*/
 
       // this just continues the loop whenever the number of skipped frames is > 0,
@@ -776,7 +366,7 @@ GetLastKeyframe(FILE * file,        // output file [in]
               && (buffer[0] != 0x09 || (buffer[11] & 0xf0) != 0x10)))
        {
 #ifdef _DEBUG
-         Log(LOGDEBUG,
+         RTMP_Log(RTMP_LOGDEBUG,
              "xxxxxxxxxxxxxxxxxxxxxxxx Well, lets go one more back!");
 #endif
          nSkipKeyFrames--;
@@ -794,43 +384,43 @@ GetLastKeyframe(FILE * file,      // output file [in]
   fseeko(file, size - tsize + 11, SEEK_SET);
   if (fread(*initialFrame, 1, *nInitialFrameSize, file) != *nInitialFrameSize)
     {
-      Log(LOGERROR, "Couldn't read last keyframe, aborting!");
+      RTMP_Log(RTMP_LOGERROR, "Couldn't read last keyframe, aborting!");
       return RD_FAILED;
     }
 
   *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
   //{
   //}
 
   if (*dSeek < 0)
     {
-      Log(LOGERROR,
+      RTMP_Log(RTMP_LOGERROR,
          "Last keyframe timestamp is negative, aborting, your file is corrupt!");
       return RD_FAILED;
     }
-  Log(LOGDEBUG, "Last keyframe found at: %d ms, size: %d, type: %02X", *dSeek,
+  RTMP_Log(RTMP_LOGDEBUG, "Last keyframe found at: %d ms, size: %d, type: %02X", *dSeek,
       *nInitialFrameSize, *initialFrameType);
 
   /*
      // now read the timestamp of the frame before the seekable keyframe:
      fseeko(file, size-tsize-4, SEEK_SET);
      if(fread(buffer, 1, 4, file) != 4) {
-     Log(LOGERROR, "Couldn't read prevTagSize from file!");
+     RTMP_Log(RTMP_LOGERROR, "Couldn't read prevTagSize from file!");
      goto start;
      }
      uint32_t prevTagSize = RTMP_LIB::AMF_DecodeInt32(buffer);
      fseeko(file, size-tsize-4-prevTagSize+4, SEEK_SET);
      if(fread(buffer, 1, 4, file) != 4) {
-     Log(LOGERROR, "Couldn't read previous timestamp!");
+     RTMP_Log(RTMP_LOGERROR, "Couldn't read previous timestamp!");
      goto start;
      }
      uint32_t timestamp = RTMP_LIB::AMF_DecodeInt24(buffer);
      timestamp |= (buffer[3]<<24);
 
-     Log(LOGDEBUG, "Previuos timestamp: %d ms", timestamp);
+     RTMP_Log(RTMP_LOGDEBUG, "Previous timestamp: %d ms", timestamp);
    */
 
   if (*dSeek != 0)
@@ -841,7 +431,7 @@ GetLastKeyframe(FILE * file,        // output file [in]
 
       // make sure the WriteStream doesn't write headers and ignores all the 0ms TS packets
       // (including several meta data headers and the keyframe we seeked to)
-      //bNoHeader = true; if bResume==true this is true anyway
+      //bNoHeader = TRUE; if bResume==true this is true anyway
     }
 
   //}
@@ -851,29 +441,27 @@ GetLastKeyframe(FILE * file,      // output file [in]
 
 int
 Download(RTMP * rtmp,          // connected RTMP object
-        FILE * file, uint32_t dSeek, uint32_t dLength, double duration, bool bResume, char *metaHeader, uint32_t nMetaHeaderSize, char *initialFrame, int initialFrameType, uint32_t nInitialFrameSize, int nSkipKeyFrames, bool bStdoutMode, bool bLiveStream, bool bHashes, bool bOverrideBufferTime, uint32_t bufferTime, double *percent)  // percentage downloaded [out]
+        FILE * file, uint32_t dSeek, uint32_t dStopOffset, double duration, int bResume, char *metaHeader, uint32_t nMetaHeaderSize, char *initialFrame, int initialFrameType, uint32_t nInitialFrameSize, int nSkipKeyFrames, int bStdoutMode, int bLiveStream, int bRealtimeStream, int bHashes, int bOverrideBufferTime, uint32_t bufferTime, double *percent)      // percentage downloaded [out]
 {
-  uint32_t timestamp = dSeek;
   int32_t now, lastUpdate;
-  uint8_t dataType = 0;                // will be written into the FLV header (position 4)
-  int bufferSize = 1024 * 1024;
-  char *buffer = (char *) malloc(bufferSize);
+  int bufferSize = 64 * 1024;
+  char *buffer;
   int nRead = 0;
   off_t size = ftello(file);
   unsigned long lastPercent = 0;
 
-  memset(buffer, 0, bufferSize);
+  rtmp->m_read.timestamp = dSeek;
 
   *percent = 0.0;
 
-  if (timestamp)
+  if (rtmp->m_read.timestamp)
     {
-      Log(LOGDEBUG, "Continuing at TS: %d ms\n", timestamp);
+      RTMP_Log(RTMP_LOGDEBUG, "Continuing at TS: %d ms\n", rtmp->m_read.timestamp);
     }
 
   if (bLiveStream)
     {
-      LogPrintf("Starting Live Stream\n");
+      RTMP_LogPrintf("Starting Live Stream\n");
     }
   else
     {
@@ -881,80 +469,65 @@ Download(RTMP * rtmp,             // connected RTMP object
       // Workaround to exit with 0 if the file is fully (> 99.9%) downloaded
       if (duration > 0)
        {
-         if ((double) timestamp >= (double) duration * 999.0)
+         if ((double) rtmp->m_read.timestamp >= (double) duration * 999.0)
            {
-             LogPrintf("Already Completed at: %.3f sec Duration=%.3f sec\n",
-                       (double) timestamp / 1000.0,
+             RTMP_LogPrintf("Already Completed at: %.3f sec Duration=%.3f sec\n",
+                       (double) rtmp->m_read.timestamp / 1000.0,
                        (double) duration / 1000.0);
              return RD_SUCCESS;
            }
          else
            {
-             *percent = ((double) timestamp) / (duration * 1000.0) * 100.0;
+             *percent = ((double) rtmp->m_read.timestamp) / (duration * 1000.0) * 100.0;
              *percent = ((double) (int) (*percent * 10.0)) / 10.0;
-             LogPrintf("%s download at: %.3f kB / %.3f sec (%.1f%%)\n",
+             RTMP_LogPrintf("%s download at: %.3f kB / %.3f sec (%.1f%%)\n",
                        bResume ? "Resuming" : "Starting",
-                       (double) size / 1024.0, (double) timestamp / 1000.0,
+                       (double) size / 1024.0, (double) rtmp->m_read.timestamp / 1000.0,
                        *percent);
            }
        }
       else
        {
-         LogPrintf("%s download at: %.3f kB\n",
+         RTMP_LogPrintf("%s download at: %.3f kB\n",
                    bResume ? "Resuming" : "Starting",
                    (double) size / 1024.0);
        }
+      if (bRealtimeStream)
+       RTMP_LogPrintf("  in approximately realtime (disabled BUFX speedup hack)\n");
     }
 
-  if (dLength > 0)
-    LogPrintf("For duration: %.3f sec\n", (double) dLength / 1000.0);
+  if (dStopOffset > 0)
+    RTMP_LogPrintf("For duration: %.3f sec\n", (double) (dStopOffset - dSeek) / 1000.0);
 
-  // write FLV header if not resuming
-  if (!bResume)
-    {
-      nRead = WriteHeader(&buffer, bufferSize);
-      if (nRead > 0)
-       {
-         if (fwrite(buffer, sizeof(unsigned char), nRead, file) !=
-             (size_t) nRead)
-           {
-             Log(LOGERROR, "%s: Failed writing FLV header, exiting!",
-                 __FUNCTION__);
-             free(buffer);
-             return RD_FAILED;
-           }
-         size += nRead;
-       }
-      else
-       {
-         Log(LOGERROR, "Couldn't obtain FLV header, exiting!");
-         free(buffer);
-         return RD_FAILED;
-       }
-    }
+  if (bResume && nInitialFrameSize > 0)
+    rtmp->m_read.flags |= RTMP_READ_RESUME;
+  rtmp->m_read.initialFrameType = initialFrameType;
+  rtmp->m_read.nResumeTS = dSeek;
+  rtmp->m_read.metaHeader = metaHeader;
+  rtmp->m_read.initialFrame = initialFrame;
+  rtmp->m_read.nMetaHeaderSize = nMetaHeaderSize;
+  rtmp->m_read.nInitialFrameSize = nInitialFrameSize;
+
+  buffer = (char *) malloc(bufferSize);
 
   now = RTMP_GetTime();
   lastUpdate = now - 1000;
   do
     {
-      nRead = WriteStream(rtmp, &buffer, bufferSize, &timestamp, bResume
-                         && nInitialFrameSize > 0, bLiveStream, dSeek,
-                         metaHeader, nMetaHeaderSize, initialFrame,
-                         initialFrameType, nInitialFrameSize, &dataType);
-
-      //LogPrintf("nRead: %d\n", nRead);
+      nRead = RTMP_Read(rtmp, buffer, bufferSize);
+      //RTMP_LogPrintf("nRead: %d\n", nRead);
       if (nRead > 0)
        {
          if (fwrite(buffer, sizeof(unsigned char), nRead, file) !=
              (size_t) nRead)
            {
-             Log(LOGERROR, "%s: Failed writing, exiting!", __FUNCTION__);
+             RTMP_Log(RTMP_LOGERROR, "%s: Failed writing, exiting!", __FUNCTION__);
              free(buffer);
              return RD_FAILED;
            }
          size += nRead;
 
-         //LogPrintf("write %dbytes (%.1f kB)\n", nRead, nRead/1024.0);
+         //RTMP_LogPrintf("write %dbytes (%.1f kB)\n", nRead, nRead/1024.0);
          if (duration <= 0)    // if duration unknown try to get it from the stream (onMetaData)
            duration = RTMP_GetDuration(rtmp);
 
@@ -965,19 +538,19 @@ Download(RTMP * rtmp,             // connected RTMP object
                {
                  bufferTime = (uint32_t) (duration * 1000.0) + 5000;   // extra 5sec to make sure we've got enough
 
-                 Log(LOGDEBUG,
+                 RTMP_Log(RTMP_LOGDEBUG,
                      "Detected that buffer time is less than duration, resetting to: %dms",
                      bufferTime);
                  RTMP_SetBufferMS(rtmp, bufferTime);
                  RTMP_UpdateBufferMS(rtmp);
                }
-             *percent = ((double) timestamp) / (duration * 1000.0) * 100.0;
+             *percent = ((double) rtmp->m_read.timestamp) / (duration * 1000.0) * 100.0;
              *percent = ((double) (int) (*percent * 10.0)) / 10.0;
              if (bHashes)
                {
                  if (lastPercent + 1 <= *percent)
                    {
-                     LogStatus("#");
+                     RTMP_LogStatus("#");
                      lastPercent = (unsigned long) *percent;
                    }
                }
@@ -986,9 +559,9 @@ Download(RTMP * rtmp,               // connected RTMP object
                  now = RTMP_GetTime();
                  if (abs(now - lastUpdate) > 200)
                    {
-                     LogStatus("\r%.3f kB / %.2f sec (%.1f%%)",
+                     RTMP_LogStatus("\r%.3f kB / %.2f sec (%.1f%%)",
                                (double) size / 1024.0,
-                               (double) (timestamp) / 1000.0, *percent);
+                               (double) (rtmp->m_read.timestamp) / 1000.0, *percent);
                      lastUpdate = now;
                    }
                }
@@ -999,46 +572,60 @@ Download(RTMP * rtmp,             // connected RTMP object
              if (abs(now - lastUpdate) > 200)
                {
                  if (bHashes)
-                   LogStatus("#");
+                   RTMP_LogStatus("#");
                  else
-                   LogStatus("\r%.3f kB / %.2f sec", (double) size / 1024.0,
-                             (double) (timestamp) / 1000.0);
+                   RTMP_LogStatus("\r%.3f kB / %.2f sec", (double) size / 1024.0,
+                             (double) (rtmp->m_read.timestamp) / 1000.0);
                  lastUpdate = now;
                }
            }
        }
-#ifdef _DEBUG
       else
        {
-         Log(LOGDEBUG, "zero read!");
-       }
+#ifdef _DEBUG
+         RTMP_Log(RTMP_LOGDEBUG, "zero read!");
 #endif
+         if (rtmp->m_read.status == RTMP_READ_EOF)
+           break;
+       }
 
     }
-  while (!bCtrlC && nRead > -1 && RTMP_IsConnected(rtmp));
+  while (!RTMP_ctrlC && nRead > -1 && RTMP_IsConnected(rtmp) && !RTMP_IsTimedout(rtmp));
   free(buffer);
+  if (nRead < 0)
+    nRead = rtmp->m_read.status;
 
-  Log(LOGDEBUG, "WriteStream returned: %d", nRead);
+  /* Final status update */
+  if (!bHashes)
+    {
+      if (duration > 0)
+       {
+         *percent = ((double) rtmp->m_read.timestamp) / (duration * 1000.0) * 100.0;
+         *percent = ((double) (int) (*percent * 10.0)) / 10.0;
+         RTMP_LogStatus("\r%.3f kB / %.2f sec (%.1f%%)",
+           (double) size / 1024.0,
+           (double) (rtmp->m_read.timestamp) / 1000.0, *percent);
+       }
+      else
+       {
+         RTMP_LogStatus("\r%.3f kB / %.2f sec", (double) size / 1024.0,
+           (double) (rtmp->m_read.timestamp) / 1000.0);
+       }
+    }
+
+  RTMP_Log(RTMP_LOGDEBUG, "RTMP_Read returned: %d", nRead);
 
   if (bResume && nRead == -2)
     {
-      LogPrintf("Couldn't resume FLV file, try --skip %d\n\n",
+      RTMP_LogPrintf("Couldn't resume FLV file, try --skip %d\n\n",
                nSkipKeyFrames + 1);
       return RD_FAILED;
     }
 
-  // finalize header by writing the correct dataType (video, audio, video+audio)
-  if (!bResume && dataType != 0x5 && !bStdoutMode)
-    {
-      //Log(LOGDEBUG, "Writing data type: %02X", dataType);
-      fseek(file, 4, SEEK_SET);
-      fwrite(&dataType, sizeof(unsigned char), 1, file);
-    }
-
   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;
@@ -1049,6 +636,91 @@ Download(RTMP * rtmp,             // connected RTMP object
 
 #define STR2AVAL(av,str)       av.av_val = str; av.av_len = strlen(av.av_val)
 
+void usage(char *prog)
+{
+         RTMP_LogPrintf
+           ("\n%s: This program dumps the media content streamed over RTMP.\n\n", prog);
+         RTMP_LogPrintf("--help|-h               Prints this help screen.\n");
+         RTMP_LogPrintf
+           ("--url|-i url            URL with options included (e.g. rtmp://host[:port]/path swfUrl=url tcUrl=url)\n");
+         RTMP_LogPrintf
+           ("--rtmp|-r url           URL (e.g. rtmp://host[:port]/path)\n");
+         RTMP_LogPrintf
+           ("--host|-n hostname      Overrides the hostname in the rtmp url\n");
+         RTMP_LogPrintf
+           ("--port|-c port          Overrides the port in the rtmp url\n");
+         RTMP_LogPrintf
+           ("--socks|-S host:port    Use the specified SOCKS proxy\n");
+         RTMP_LogPrintf
+           ("--protocol|-l num       Overrides the protocol in the rtmp url (0 - RTMP, 2 - RTMPE)\n");
+         RTMP_LogPrintf
+           ("--playpath|-y path      Overrides the playpath parsed from rtmp url\n");
+         RTMP_LogPrintf
+           ("--playlist|-Y           Set playlist before playing\n");
+         RTMP_LogPrintf("--swfUrl|-s url         URL to player swf file\n");
+         RTMP_LogPrintf
+           ("--tcUrl|-t url          URL to played stream (default: \"rtmp://host[:port]/app\")\n");
+         RTMP_LogPrintf("--pageUrl|-p url        Web URL of played programme\n");
+         RTMP_LogPrintf("--app|-a app            Name of target app on server\n");
+#ifdef CRYPTO
+         RTMP_LogPrintf
+           ("--swfhash|-w hexstring  SHA256 hash of the decompressed SWF file (32 bytes)\n");
+         RTMP_LogPrintf
+           ("--swfsize|-x num        Size of the decompressed SWF file, required for SWFVerification\n");
+         RTMP_LogPrintf
+           ("--swfVfy|-W url         URL to player swf file, compute hash/size automatically\n");
+         RTMP_LogPrintf
+           ("--swfAge|-X days        Number of days to use cached SWF hash before refreshing\n");
+#endif
+         RTMP_LogPrintf
+           ("--auth|-u string        Authentication string to be appended to the connect string\n");
+         RTMP_LogPrintf
+           ("--conn|-C type:data     Arbitrary AMF data to be appended to the connect string\n");
+         RTMP_LogPrintf
+           ("                        B:boolean(0|1), S:string, N:number, O:object-flag(0|1),\n");
+         RTMP_LogPrintf
+           ("                        Z:(null), NB:name:boolean, NS:name:string, NN:name:number\n");
+         RTMP_LogPrintf
+           ("--flashVer|-f string    Flash version string (default: \"%s\")\n",
+            RTMP_DefaultFlashVer.av_val);
+         RTMP_LogPrintf
+           ("--live|-v               Save a live stream, no --resume (seeking) of live streams possible\n");
+         RTMP_LogPrintf
+           ("--subscribe|-d string   Stream name to subscribe to (otherwise defaults to playpath if live is specifed)\n");
+         RTMP_LogPrintf
+           ("--realtime|-R           Don't attempt to speed up download via the Pause/Unpause BUFX hack\n");
+         RTMP_LogPrintf
+           ("--flv|-o string         FLV output file name, if the file name is - print stream to stdout\n");
+         RTMP_LogPrintf
+           ("--resume|-e             Resume a partial RTMP download\n");
+         RTMP_LogPrintf
+           ("--timeout|-m num        Timeout connection num seconds (default: %u)\n",
+            DEF_TIMEOUT);
+         RTMP_LogPrintf
+           ("--start|-A num          Start at num seconds into stream (not valid when using --live)\n");
+         RTMP_LogPrintf
+           ("--stop|-B num           Stop at num seconds into stream\n");
+         RTMP_LogPrintf
+           ("--token|-T key          Key for SecureToken response\n");
+         RTMP_LogPrintf
+           ("--jtv|-j JSON           Authentication token for Justin.tv legacy servers\n");
+         RTMP_LogPrintf
+           ("--hashes|-#             Display progress with hashes, not with the byte counter\n");
+         RTMP_LogPrintf
+           ("--buffer|-b             Buffer time in milliseconds (default: %u)\n",
+            DEF_BUFTIME);
+         RTMP_LogPrintf
+           ("--skip|-k num           Skip num keyframes when looking for last keyframe to resume from. Useful if resume fails (default: %d)\n\n",
+            DEF_SKIPFRM);
+         RTMP_LogPrintf
+           ("--quiet|-q              Suppresses all command output.\n");
+         RTMP_LogPrintf("--verbose|-V            Verbose command output.\n");
+         RTMP_LogPrintf("--debug|-z              Debug level command output.\n");
+         RTMP_LogPrintf
+           ("If you don't pass parameters for swfUrl, pageUrl, or auth these properties will not be included in the connect ");
+         RTMP_LogPrintf("packet.\n\n");
+}
+
 int
 main(int argc, char **argv)
 {
@@ -1058,13 +730,13 @@ main(int argc, char **argv)
   double percent = 0;
   double duration = 0.0;
 
-  int nSkipKeyFrames = 0;      // skip this number of keyframes when resuming
+  int nSkipKeyFrames = DEF_SKIPFRM;    // skip this number of keyframes when resuming
 
-  bool bOverrideBufferTime = false;    // if the user specifies a buffer time override this is true
-  bool bStdoutMode = true;     // if true print the stream directly to stdout, messages go to stderr
-  bool bResume = false;                // true in resume mode
+  int bOverrideBufferTime = FALSE;     // if the user specifies a buffer time override this is true
+  int bStdoutMode = TRUE;      // if true print the stream directly to stdout, messages go to stderr
+  int bResume = FALSE;         // true in resume mode
   uint32_t dSeek = 0;          // seek position in resume mode, 0 otherwise
-  uint32_t bufferTime = 10 * 60 * 60 * 1000;   // 10 hours as default
+  uint32_t bufferTime = DEF_BUFTIME;
 
   // meta header and initial frame for the resume mode (they are read from the file and compared with
   // the stream we are trying to continue
@@ -1076,20 +748,23 @@ main(int argc, char **argv)
   uint32_t nInitialFrameSize = 0;
   int initialFrameType = 0;    // tye: audio or video
 
-  char *hostname = 0;
+  AVal hostname = { 0, 0 };
   AVal playpath = { 0, 0 };
   AVal subscribepath = { 0, 0 };
+  AVal usherToken = { 0, 0 }; //Justin.tv auth token
   int port = -1;
   int protocol = RTMP_PROTOCOL_UNDEFINED;
-  bool bLiveStream = false;    // is it a live stream? then we can't seek/resume
-  bool bHashes = false;                // display byte counters not hashes by default
+  int retries = 0;
+  int bLiveStream = FALSE;     // is it a live stream? then we can't seek/resume
+  int bRealtimeStream = FALSE;  // If true, disable the BUFX hack (be patient)
+  int bHashes = FALSE;         // display byte counters not hashes by default
 
-  long int timeout = 120;      // timeout connection after 120 seconds
+  long int timeout = DEF_TIMEOUT;      // timeout connection after 120 seconds
   uint32_t dStartOffset = 0;   // seek position in non-live mode
   uint32_t dStopOffset = 0;
-  uint32_t dLength = 0;                // length to play from stream - calculated from seek position and dStopOffset
+  RTMP rtmp = { 0 };
 
-  char *rtmpurl = 0;
+  AVal fullUrl = { 0, 0 };
   AVal swfUrl = { 0, 0 };
   AVal tcUrl = { 0, 0 };
   AVal pageUrl = { 0, 0 };
@@ -1098,18 +773,15 @@ main(int argc, char **argv)
   AVal swfHash = { 0, 0 };
   uint32_t swfSize = 0;
   AVal flashVer = { 0, 0 };
-  char *sockshost = 0;
-
-  char *flvFile = 0;
+  AVal sockshost = { 0, 0 };
 
-#undef OSS
-#ifdef WIN32
-#define        OSS     "WIN"
-#else
-#define OSS    "LNX"
+#ifdef CRYPTO
+  int swfAge = 30;     /* 30 days for SWF cache by default */
+  int swfVfy = 0;
+  unsigned char hash[RTMP_SWF_HASHLEN];
 #endif
 
-  char DEFAULT_FLASH_VER[] = OSS " 10,0,22,87";
+  char *flvFile = 0;
 
   signal(SIGINT, sigIntHandler);
   signal(SIGTERM, sigIntHandler);
@@ -1119,7 +791,7 @@ main(int argc, char **argv)
   signal(SIGQUIT, sigIntHandler);
 #endif
 
-  /* sleep(30); */
+  RTMP_debuglevel = RTMP_LOGINFO;
 
   // Check for --quiet option before printing any output
   int index = 0;
@@ -1127,13 +799,24 @@ main(int argc, char **argv)
     {
       if (strcmp(argv[index], "--quiet") == 0
          || strcmp(argv[index], "-q") == 0)
-       debuglevel = LOGCRIT;
+       RTMP_debuglevel = RTMP_LOGCRIT;
       index++;
     }
 
-  LogPrintf("RTMPDump %s\n", RTMPDUMP_VERSION);
-  LogPrintf
-    ("(c) 2009 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n");
+  RTMP_LogPrintf("RTMPDump %s\n", RTMPDUMP_VERSION);
+  RTMP_LogPrintf
+    ("(c) 2010 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n");
+
+  if (!InitSockets())
+    {
+      RTMP_Log(RTMP_LOGERROR,
+         "Couldn't load sockets support on your platform, exiting!");
+      return RD_FAILED;
+    }
+
+  /* sleep(30); */
+
+  RTMP_Init(&rtmp);
 
   int opt;
   struct option longopts[] = {
@@ -1143,18 +826,24 @@ main(int argc, char **argv)
     {"socks", 1, NULL, 'S'},
     {"protocol", 1, NULL, 'l'},
     {"playpath", 1, NULL, 'y'},
+    {"playlist", 0, NULL, 'Y'},
+    {"url", 1, NULL, 'i'},
     {"rtmp", 1, NULL, 'r'},
     {"swfUrl", 1, NULL, 's'},
     {"tcUrl", 1, NULL, 't'},
     {"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'},
+    {"swfVfy", 1, NULL, 'W'},
+    {"swfAge", 1, NULL, 'X'},
 #endif
     {"flashVer", 1, NULL, 'f'},
     {"live", 0, NULL, 'v'},
+    {"realtime", 0, NULL, 'R'},
     {"flv", 1, NULL, 'o'},
     {"resume", 0, NULL, 'e'},
     {"timeout", 1, NULL, 'm'},
@@ -1163,94 +852,36 @@ 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'},
     {"verbose", 0, NULL, 'V'},
+    {"jtv", 1, NULL, 'j'},
     {0, 0, 0, 0}
   };
 
   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:#",
+                     "hVveqzRr:s:t:i:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:",
                      longopts, NULL)) != -1)
     {
       switch (opt)
        {
        case 'h':
-         LogPrintf
-           ("\nThis program dumps the media content streamed over rtmp.\n\n");
-         LogPrintf("--help|-h               Prints this help screen.\n");
-         LogPrintf
-           ("--rtmp|-r url           URL (e.g. rtmp//hotname[:port]/path)\n");
-         LogPrintf
-           ("--host|-n hostname      Overrides the hostname in the rtmp url\n");
-         LogPrintf
-           ("--port|-c port          Overrides the port in the rtmp url\n");
-         LogPrintf
-           ("--socks|-S host:port    Use the specified SOCKS proxy\n");
-         LogPrintf
-           ("--protocol|-l           Overrides the protocol in the rtmp url (0 - RTMP, 3 - RTMPE)\n");
-         LogPrintf
-           ("--playpath|-y           Overrides the playpath parsed from rtmp url\n");
-         LogPrintf("--swfUrl|-s url         URL to player swf file\n");
-         LogPrintf
-           ("--tcUrl|-t url          URL to played stream (default: \"rtmp://host[:port]/app\")\n");
-         LogPrintf("--pageUrl|-p url        Web URL of played programme\n");
-         LogPrintf("--app|-a app            Name of player used\n");
-#ifdef CRYPTO
-         LogPrintf
-           ("--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");
-#endif
-         LogPrintf
-           ("--auth|-u string        Authentication string to be appended to the connect string\n");
-         LogPrintf
-           ("--flashVer|-f string    Flash version string (default: \"%s\")\n",
-            DEFAULT_FLASH_VER);
-         LogPrintf
-           ("--live|-v               Save a live stream, no --resume (seeking) of live streams possible\n");
-         LogPrintf
-           ("--subscribe|-d string   Stream name to subscribe to (otherwise defaults to playpath if live is specifed)\n");
-         LogPrintf
-           ("--flv|-o string         FLV output file name, if the file name is - print stream to stdout\n");
-         LogPrintf
-           ("--resume|-e             Resume a partial RTMP download\n");
-         LogPrintf
-           ("--timeout|-m num        Timeout connection num seconds (default: %lu)\n",
-            timeout);
-         LogPrintf
-           ("--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
-           ("--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",
-            bufferTime);
-         LogPrintf
-           ("--skip|-k num           Skip num keyframes when looking for last keyframe to resume from. Useful if resume fails (default: %d)\n\n",
-            nSkipKeyFrames);
-         LogPrintf
-           ("--quiet|-q              Supresses all command output.\n");
-         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 ");
-         LogPrintf("packet.\n\n");
+         usage(argv[0]);
          return RD_SUCCESS;
 #ifdef CRYPTO
        case 'w':
          {
            int res = hex2bin(optarg, &swfHash.av_val);
-           if (res != 32)
+           if (res != RTMP_SWF_HASHLEN)
              {
                swfHash.av_val = NULL;
-               Log(LOGWARNING,
-                   "Couldn't parse swf hash hex string, not heyxstring or not 32 bytes, ignoring!");
+               RTMP_Log(RTMP_LOGWARNING,
+                   "Couldn't parse swf hash hex string, not hexstring or not %d bytes, ignoring!", RTMP_SWF_HASHLEN);
              }
-           swfHash.av_len = 32;
+           swfHash.av_len = RTMP_SWF_HASHLEN;
            break;
          }
        case 'x':
@@ -1258,7 +889,7 @@ main(int argc, char **argv)
            int size = atoi(optarg);
            if (size <= 0)
              {
-               Log(LOGERROR, "SWF Size must be at least 1, ignoring\n");
+               RTMP_Log(RTMP_LOGERROR, "SWF Size must be at least 1, ignoring\n");
              }
            else
              {
@@ -1266,18 +897,35 @@ main(int argc, char **argv)
              }
            break;
          }
+        case 'W':
+         STR2AVAL(swfUrl, optarg);
+         swfVfy = 1;
+          break;
+        case 'X':
+         {
+           int num = atoi(optarg);
+           if (num < 0)
+             {
+               RTMP_Log(RTMP_LOGERROR, "SWF Age must be non-negative, ignoring\n");
+             }
+           else
+             {
+               swfAge = num;
+             }
+         }
+          break;
 #endif
        case 'k':
          nSkipKeyFrames = atoi(optarg);
          if (nSkipKeyFrames < 0)
            {
-             Log(LOGERROR,
+             RTMP_Log(RTMP_LOGERROR,
                  "Number of keyframes skipped must be greater or equal zero, using zero!");
              nSkipKeyFrames = 0;
            }
          else
            {
-             Log(LOGDEBUG, "Number of skipped key frames for resume: %d",
+             RTMP_Log(RTMP_LOGDEBUG, "Number of skipped key frames for resume: %d",
                  nSkipKeyFrames);
            }
          break;
@@ -1286,77 +934,81 @@ main(int argc, char **argv)
            int32_t bt = atol(optarg);
            if (bt < 0)
              {
-               Log(LOGERROR,
+               RTMP_Log(RTMP_LOGERROR,
                    "Buffer time must be greater than zero, ignoring the specified value %d!",
                    bt);
              }
            else
              {
                bufferTime = bt;
-               bOverrideBufferTime = true;
+               bOverrideBufferTime = TRUE;
              }
            break;
          }
        case 'v':
-         bLiveStream = true;   // no seeking or resuming possible!
+         bLiveStream = TRUE;   // no seeking or resuming possible!
+         break;
+       case 'R':
+         bRealtimeStream = TRUE; // seeking and resuming is still possible
          break;
        case 'd':
          STR2AVAL(subscribepath, optarg);
          break;
        case 'n':
-         hostname = optarg;
+         STR2AVAL(hostname, optarg);
          break;
        case 'c':
          port = atoi(optarg);
          break;
        case 'l':
          protocol = atoi(optarg);
-         if (protocol != RTMP_PROTOCOL_RTMP
-             && protocol != RTMP_PROTOCOL_RTMPE)
+         if (protocol < RTMP_PROTOCOL_RTMP || protocol > RTMP_PROTOCOL_RTMPTS)
            {
-             Log(LOGERROR, "Unknown protocol specified: %d", protocol);
+             RTMP_Log(RTMP_LOGERROR, "Unknown protocol specified: %d", protocol);
              return RD_FAILED;
            }
          break;
        case 'y':
          STR2AVAL(playpath, optarg);
          break;
+       case 'Y':
+         RTMP_SetOpt(&rtmp, &av_playlist, (AVal *)&av_true);
+         break;
        case 'r':
          {
-           rtmpurl = optarg;
-
-           char *parsedHost = 0;
+           AVal parsedHost, parsedApp, parsedPlaypath;
            unsigned int parsedPort = 0;
-           char *parsedPlaypath = 0;
-           char *parsedApp = 0;
            int parsedProtocol = RTMP_PROTOCOL_UNDEFINED;
 
-           if (!ParseUrl
-               (rtmpurl, &parsedProtocol, &parsedHost, &parsedPort,
+           if (!RTMP_ParseURL
+               (optarg, &parsedProtocol, &parsedHost, &parsedPort,
                 &parsedPlaypath, &parsedApp))
              {
-               Log(LOGWARNING, "Couldn't parse the specified url (%s)!",
+               RTMP_Log(RTMP_LOGWARNING, "Couldn't parse the specified url (%s)!",
                    optarg);
              }
            else
              {
-               if (hostname == 0)
+               if (!hostname.av_len)
                  hostname = parsedHost;
                if (port == -1)
                  port = parsedPort;
-               if (playpath.av_len == 0 && parsedPlaypath)
+               if (playpath.av_len == 0 && parsedPlaypath.av_len)
                  {
-                   STR2AVAL(playpath, parsedPlaypath);
+                   playpath = parsedPlaypath;
                  }
                if (protocol == RTMP_PROTOCOL_UNDEFINED)
                  protocol = parsedProtocol;
-               if (app.av_len == 0 && parsedApp)
+               if (app.av_len == 0 && parsedApp.av_len)
                  {
-                   STR2AVAL(app, parsedApp);
+                   app = parsedApp;
                  }
              }
            break;
          }
+       case 'i':
+         STR2AVAL(fullUrl, optarg);
+          break;
        case 's':
          STR2AVAL(swfUrl, optarg);
          break;
@@ -1375,15 +1027,25 @@ main(int argc, char **argv)
        case 'o':
          flvFile = optarg;
          if (strcmp(flvFile, "-"))
-           bStdoutMode = false;
+           bStdoutMode = FALSE;
 
          break;
        case 'e':
-         bResume = true;
+         bResume = TRUE;
          break;
        case 'u':
          STR2AVAL(auth, optarg);
          break;
+       case 'C': {
+         AVal av;
+         STR2AVAL(av, optarg);
+         if (!RTMP_SetOpt(&rtmp, &av_conn, &av))
+           {
+             RTMP_Log(RTMP_LOGERROR, "Invalid AMF parameter: %s", optarg);
+             return RD_FAILED;
+           }
+         }
+         break;
        case 'm':
          timeout = atoi(optarg);
          break;
@@ -1393,112 +1055,129 @@ main(int argc, char **argv)
        case 'B':
          dStopOffset = (int) (atof(optarg) * 1000.0);
          break;
+       case 'T': {
+         AVal token;
+         STR2AVAL(token, optarg);
+         RTMP_SetOpt(&rtmp, &av_token, &token);
+         }
+         break;
        case '#':
-         bHashes = true;
+         bHashes = TRUE;
          break;
        case 'q':
-         debuglevel = LOGCRIT;
+         RTMP_debuglevel = RTMP_LOGCRIT;
          break;
        case 'V':
-         debuglevel = LOGDEBUG;
+         RTMP_debuglevel = RTMP_LOGDEBUG;
          break;
        case 'z':
-         debuglevel = LOGALL;
+         RTMP_debuglevel = RTMP_LOGALL;
          break;
        case 'S':
-         sockshost = optarg;
+         STR2AVAL(sockshost, optarg);
+         break;
+       case 'j':
+         STR2AVAL(usherToken, optarg);
          break;
        default:
-         LogPrintf("unknown option: %c\n", opt);
+         RTMP_LogPrintf("unknown option: %c\n", opt);
+         usage(argv[0]);
+         return RD_FAILED;
          break;
        }
     }
 
-  if (hostname == 0)
+  if (!hostname.av_len && !fullUrl.av_len)
     {
-      Log(LOGERROR,
+      RTMP_Log(RTMP_LOGERROR,
          "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname");
       return RD_FAILED;
     }
-  if (playpath.av_len == 0)
+  if (playpath.av_len == 0 && !fullUrl.av_len)
     {
-      Log(LOGERROR,
+      RTMP_Log(RTMP_LOGERROR,
          "You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath");
       return RD_FAILED;
     }
 
-  if (port == -1)
+  if (protocol == RTMP_PROTOCOL_UNDEFINED && !fullUrl.av_len)
     {
-      Log(LOGWARNING,
-         "You haven't specified a port (--port) or rtmp url (-r), using default port 1935");
-      port = 1935;
+      RTMP_Log(RTMP_LOGWARNING,
+         "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP");
+      protocol = RTMP_PROTOCOL_RTMP;
     }
-  if (port == 0)
+  if (port == -1 && !fullUrl.av_len)
     {
-      port = 1935;
+      RTMP_Log(RTMP_LOGWARNING,
+         "You haven't specified a port (--port) or rtmp url (-r), using default port 1935");
+      port = 0;
     }
-  if (protocol == RTMP_PROTOCOL_UNDEFINED)
+  if (port == 0 && !fullUrl.av_len)
     {
-      Log(LOGWARNING,
-         "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP");
-      protocol = RTMP_PROTOCOL_RTMP;
+      if (protocol & RTMP_FEATURE_SSL)
+       port = 443;
+      else if (protocol & RTMP_FEATURE_HTTP)
+       port = 80;
+      else
+       port = 1935;
     }
+
   if (flvFile == 0)
     {
-      Log(LOGWARNING,
+      RTMP_Log(RTMP_LOGWARNING,
          "You haven't specified an output file (-o filename), using stdout");
-      bStdoutMode = true;
+      bStdoutMode = TRUE;
     }
 
   if (bStdoutMode && bResume)
     {
-      Log(LOGWARNING,
+      RTMP_Log(RTMP_LOGWARNING,
          "Can't resume in stdout mode, ignoring --resume option");
-      bResume = false;
+      bResume = FALSE;
     }
 
   if (bLiveStream && bResume)
     {
-      Log(LOGWARNING, "Can't resume live stream, ignoring --resume option");
-      bResume = false;
+      RTMP_Log(RTMP_LOGWARNING, "Can't resume live stream, ignoring --resume option");
+      bResume = FALSE;
+    }
+
+#ifdef CRYPTO
+  if (swfVfy)
+    {
+      if (RTMP_HashSWF(swfUrl.av_val, &swfSize, hash, swfAge) == 0)
+        {
+          swfHash.av_val = (char *)hash;
+          swfHash.av_len = RTMP_SWF_HASHLEN;
+        }
     }
 
   if (swfHash.av_len == 0 && swfSize > 0)
     {
-      Log(LOGWARNING,
+      RTMP_Log(RTMP_LOGWARNING,
          "Ignoring SWF size, supply also the hash with --swfhash");
       swfSize = 0;
     }
 
   if (swfHash.av_len != 0 && swfSize == 0)
     {
-      Log(LOGWARNING,
+      RTMP_Log(RTMP_LOGWARNING,
          "Ignoring SWF hash, supply also the swf size  with --swfsize");
       swfHash.av_len = 0;
       swfHash.av_val = NULL;
     }
+#endif
 
-  if (flashVer.av_len == 0)
-    {
-      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)
+  if (tcUrl.av_len == 0)
     {
-      char str[512] = { 0 };
-
-      snprintf(str, 511, "%s://%s:%d/%s", RTMPProtocolStringsLower[protocol],
-              hostname, port, app.av_val);
-      tcUrl.av_len = strlen(str);
-      tcUrl.av_val = (char *) malloc(tcUrl.av_len + 1);
-      strcpy(tcUrl.av_val, str);
+         tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) +
+               hostname.av_len + app.av_len + sizeof("://:65535/");
+      tcUrl.av_val = (char *) malloc(tcUrl.av_len);
+         if (!tcUrl.av_val)
+           return RD_FAILED;
+      tcUrl.av_len = snprintf(tcUrl.av_val, tcUrl.av_len, "%s://%.*s:%d/%.*s",
+                  RTMPProtocolStringsLower[protocol], hostname.av_len,
+                  hostname.av_val, port, app.av_len, app.av_val);
     }
 
   int first = 1;
@@ -1509,21 +1188,34 @@ main(int argc, char **argv)
       // Live stream
       if (bLiveStream)
        {
-         Log(LOGWARNING,
+         RTMP_Log(RTMP_LOGWARNING,
              "Can't seek in a live stream, ignoring --start option");
          dStartOffset = 0;
        }
     }
 
-  RTMP rtmp = { 0 };
-  RTMP_Init(&rtmp);
-  RTMP_SetupStream(&rtmp, protocol, hostname, port, sockshost, &playpath,
-                  &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize,
-                  &flashVer, &subscribepath, dSeek, 0, bLiveStream, timeout);
+  if (!fullUrl.av_len)
+    {
+      RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath,
+                      &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize,
+                      &flashVer, &subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout);
+    }
+  else
+    {
+      if (RTMP_SetupURL(&rtmp, fullUrl.av_val) == FALSE)
+        {
+          RTMP_Log(RTMP_LOGERROR, "Couldn't parse URL: %s", fullUrl.av_val);
+          return RD_FAILED;
+       }
+    }
+
+  /* Try to keep the stream moving if it pauses on us */
+  if (!bLiveStream && !bRealtimeStream && !(protocol & RTMP_FEATURE_HTTP))
+    rtmp.Link.lFlags |= RTMP_LF_BUFX;
 
   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 =
@@ -1535,7 +1227,7 @@ main(int argc, char **argv)
       if (!file)
        {
          // file does not exist, so go back into normal mode
-         bResume = false;      // we are back in fresh file mode (otherwise finalizing file won't be done)
+         bResume = FALSE;      // we are back in fresh file mode (otherwise finalizing file won't be done)
        }
       else
        {
@@ -1544,15 +1236,15 @@ main(int argc, char **argv)
                                    &initialFrameType, &nInitialFrameSize);
          if (nStatus == RD_FAILED)
            {
-             Log(LOGDEBUG, "Failed to get last keyframe.");
+             RTMP_Log(RTMP_LOGDEBUG, "Failed to get last keyframe.");
              goto clean;
            }
 
          if (dSeek == 0)
            {
-             Log(LOGDEBUG,
+             RTMP_Log(RTMP_LOGDEBUG,
                  "Last keyframe is first frame in stream, switching from resume to normal mode!");
-             bResume = false;
+             bResume = FALSE;
            }
        }
     }
@@ -1569,7 +1261,7 @@ main(int argc, char **argv)
          file = fopen(flvFile, "w+b");
          if (file == 0)
            {
-             LogPrintf("Failed to open file! %s\n", flvFile);
+             RTMP_LogPrintf("Failed to open file! %s\n", flvFile);
              return RD_FAILED;
            }
        }
@@ -1580,23 +1272,23 @@ 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_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", bufferTime);
       RTMP_SetBufferMS(&rtmp, bufferTime);
 
       if (first)
        {
          first = 0;
-         LogPrintf("Connecting ...\n");
+         RTMP_LogPrintf("Connecting ...\n");
 
-         if (!RTMP_Connect(&rtmp))
+         if (!RTMP_Connect(&rtmp, NULL))
            {
-             nStatus = RD_FAILED;
+             nStatus = RD_NO_CONNECT;
              break;
            }
 
-         Log(LOGINFO, "Connected...");
+         RTMP_Log(RTMP_LOGINFO, "Connected...");
 
          // User defined seek offset
          if (dStartOffset > 0)
@@ -1604,7 +1296,7 @@ main(int argc, char **argv)
              // Don't need the start offset if resuming an existing file
              if (bResume)
                {
-                 Log(LOGWARNING,
+                 RTMP_Log(RTMP_LOGWARNING,
                      "Can't seek a resumed stream, ignoring --start option");
                  dStartOffset = 0;
                }
@@ -1617,18 +1309,16 @@ main(int argc, char **argv)
          // Calculate the length of the stream to still play
          if (dStopOffset > 0)
            {
-             dLength = dStopOffset - dSeek;
-
              // Quit if start seek is past required stop offset
-             if (dLength <= 0)
+             if (dStopOffset <= dSeek)
                {
-                 LogPrintf("Already Completed\n");
+                 RTMP_LogPrintf("Already Completed\n");
                  nStatus = RD_SUCCESS;
                  break;
                }
            }
 
-         if (!RTMP_ConnectStream(&rtmp, dSeek, dLength))
+         if (!RTMP_ConnectStream(&rtmp, dSeek))
            {
              nStatus = RD_FAILED;
              break;
@@ -1638,23 +1328,57 @@ main(int argc, char **argv)
        {
          nInitialFrameSize = 0;
 
-         Log(LOGINFO, "Connection timed out, trying to resume.\n\n");
-         if (!RTMP_ToggleStream(&rtmp))
+          if (retries)
+            {
+             RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n");
+             if (!RTMP_IsTimedout(&rtmp))
+               nStatus = RD_FAILED;
+             else
+               nStatus = RD_INCOMPLETE;
+             break;
+            }
+         RTMP_Log(RTMP_LOGINFO, "Connection timed out, trying to resume.\n\n");
+          /* Did we already try pausing, and it still didn't work? */
+          if (rtmp.m_pausing == 3)
+            {
+              /* Only one try at reconnecting... */
+              retries = 1;
+              dSeek = rtmp.m_pauseStamp;
+              if (dStopOffset > 0)
+                {
+                  if (dStopOffset <= dSeek)
+                    {
+                      RTMP_LogPrintf("Already Completed\n");
+                     nStatus = RD_SUCCESS;
+                     break;
+                    }
+                }
+              if (!RTMP_ReconnectStream(&rtmp, dSeek))
+                {
+                 RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n");
+                 if (!RTMP_IsTimedout(&rtmp))
+                   nStatus = RD_FAILED;
+                 else
+                   nStatus = RD_INCOMPLETE;
+                 break;
+                }
+            }
+         else if (!RTMP_ToggleStream(&rtmp))
            {
-             Log(LOGERROR, "Failed to resume the stream\n\n");
+             RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n");
              if (!RTMP_IsTimedout(&rtmp))
                nStatus = RD_FAILED;
              else
                nStatus = RD_INCOMPLETE;
              break;
            }
-         bResume = true;
+         bResume = TRUE;
        }
 
-      nStatus = Download(&rtmp, file, dSeek, dLength, duration, bResume,
+      nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, bResume,
                         metaHeader, nMetaHeaderSize, initialFrame,
-                        initialFrameType, nInitialFrameSize,
-                        nSkipKeyFrames, bStdoutMode, bLiveStream, bHashes,
+                        initialFrameType, nInitialFrameSize, nSkipKeyFrames,
+                        bStdoutMode, bLiveStream, bRealtimeStream, bHashes,
                         bOverrideBufferTime, bufferTime, &percent);
       free(initialFrame);
       initialFrame = NULL;
@@ -1667,17 +1391,17 @@ main(int argc, char **argv)
 
   if (nStatus == RD_SUCCESS)
     {
-      LogPrintf("Download complete\n");
+      RTMP_LogPrintf("Download complete\n");
     }
   else if (nStatus == RD_INCOMPLETE)
     {
-      LogPrintf
+      RTMP_LogPrintf
        ("Download may be incomplete (downloaded about %.2f%%), try resuming\n",
         percent);
     }
 
 clean:
-  Log(LOGDEBUG, "Closing connection.\n");
+  RTMP_Log(RTMP_LOGDEBUG, "Closing connection.\n");
   RTMP_Close(&rtmp);
 
   if (file != 0)