[rtmpdump] r331 - in trunk/librtmp: rtmp.c rtmp.h

hyc subversion at mplayerhq.hu
Fri Mar 12 05:53:02 CET 2010


Author: hyc
Date: Fri Mar 12 05:53:01 2010
New Revision: 331

Log:
Add RTMP_Read() to simplify library usage

Modified:
   trunk/librtmp/rtmp.c
   trunk/librtmp/rtmp.h

Modified: trunk/librtmp/rtmp.c
==============================================================================
--- trunk/librtmp/rtmp.c	Wed Mar 10 20:29:40 2010	(r330)
+++ trunk/librtmp/rtmp.c	Fri Mar 12 05:53:01 2010	(r331)
@@ -2567,6 +2567,17 @@ RTMP_Close(RTMP *r)
   r->m_nClientBW2 = 2;
   r->m_nServerBW = 2500000;
 
+  r->m_read.buf = NULL;
+  r->m_read.dataType = 0;
+  r->m_read.bResume = 0;
+  r->m_read.status = 0;
+  r->m_read.bStopIgnoring = false;
+  r->m_read.bFoundKeyframe = false;
+  r->m_read.bFoundFlvKeyframe = false;
+  r->m_read.nResumeTS = 0;
+  r->m_read.nIgnoredFrameCounter = 0;
+  r->m_read.nIgnoredFlvFrameCounter = 0;
+
   for (i = 0; i < RTMP_CHANNELS; i++)
     {
       if (r->m_vecChannelsIn[i])
@@ -2822,3 +2833,541 @@ HTTP_read(RTMP *r, int fill)
     }
   return 0;
 }
+
+#define MAX_IGNORED_FRAMES	50
+
+/* Read from the stream until we get a media packet.
+ * 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
+ */
+static int
+Read_1_Packet(RTMP *r, char *buf, int buflen)
+{
+  uint32_t prevTagSize = 0;
+  int rtnGetNextMediaPacket = 0, ret = RTMP_READ_EOF;
+  RTMPPacket packet = { 0 };
+  bool recopy = false;
+
+  rtnGetNextMediaPacket = RTMP_GetNextMediaPacket(r, &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 = RTMP_READ_COMPLETE;
+	  break;
+	}
+
+      r->m_read.dataType |= (((packet.m_packetType == 0x08) << 2) |
+			     (packet.m_packetType == 0x09));
+
+      if (packet.m_packetType == 0x09 && nPacketLen <= 5)
+	{
+	  Log(LOGWARNING, "ignoring too small video packet: size: %d",
+	      nPacketLen);
+	  ret = RTMP_READ_IGNORE;
+	  break;
+	}
+      if (packet.m_packetType == 0x08 && nPacketLen <= 1)
+	{
+	  Log(LOGWARNING, "ignoring too small audio packet: size: %d",
+	      nPacketLen);
+	  ret = RTMP_READ_IGNORE;
+	  break;
+	}
+#if 1				/* _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
+
+      if (r->m_read.bResume)
+	{
+	  /* check the header if we get one */
+	  if (packet.m_nTimeStamp == 0)
+	    {
+	      if (r->m_read.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 ((r->m_read.nMetaHeaderSize != nPacketLen) ||
+			      (memcmp
+			       (r->m_read.metaHeader, packetBody,
+				r->m_read.nMetaHeaderSize) != 0))
+			    {
+			      ret = RTMP_READ_ERROR;
+			    }
+			}
+		      AMF_Reset(&metaObj);
+		      if (ret == RTMP_READ_ERROR)
+			break;
+		    }
+		}
+
+	      /* check first keyframe to make sure we got the right position
+	       * in the stream! (the first non ignored frame)
+	       */
+	      if (r->m_read.nInitialFrameSize > 0)
+		{
+		  /* video or audio data */
+		  if (packet.m_packetType == r->m_read.initialFrameType
+		      && r->m_read.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
+			  (r->m_read.initialFrame, packetBody,
+			   r->m_read.nInitialFrameSize) == 0)
+			{
+			  Log(LOGDEBUG, "Checked keyframe successfully!");
+			  r->m_read.bFoundKeyframe = true;
+			  /* ignore it! (what about audio data after it? it is
+			   * handled by ignoring all 0ms frames, see below)
+			   */
+			  ret = RTMP_READ_IGNORE;
+			  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)
+			{
+			  /* size without header (11) and prevTagSize (4) */
+			  uint32_t dataSize =
+			    AMF_DecodeInt24(packetBody + pos + 1);
+			  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 */ ] ==
+			      r->m_read.initialFrameType
+			      /* && (packetBody[11]&0xf0) == 0x10 */ )
+			    {
+			      if (ts == r->m_read.nResumeTS)
+				{
+				  Log(LOGDEBUG,
+				      "Found keyframe with resume-keyframe timestamp!");
+				  if (r->m_read.nInitialFrameSize != dataSize
+				      || memcmp(r->m_read.initialFrame,
+						packetBody + pos + 11,
+						r->m_read.
+						nInitialFrameSize) != 0)
+				    {
+				      Log(LOGERROR,
+					  "FLV Stream: Keyframe doesn't match!");
+				      ret = RTMP_READ_ERROR;
+				      break;
+				    }
+				  r->m_read.bFoundFlvKeyframe = true;
+
+				  /* skip this packet?
+				   * check whether skippable:
+				   */
+				  if (pos + 11 + dataSize + 4 > nPacketLen)
+				    {
+				      Log(LOGWARNING,
+					  "Non skipable packet since it doesn't end with chunk, stream corrupt!");
+				      ret = RTMP_READ_ERROR;
+				      break;
+				    }
+				  packetBody += (pos + 11 + dataSize + 4);
+				  nPacketLen -= (pos + 11 + dataSize + 4);
+
+				  goto stopKeyframeSearch;
+
+				}
+			      else if (r->m_read.nResumeTS < ts)
+				{
+				  /* the timestamp ts will only increase with
+				   * further packets, wait for seek
+				   */
+				  goto stopKeyframeSearch;
+				}
+			    }
+			  pos += (11 + dataSize + 4);
+			}
+		      if (ts < r->m_read.nResumeTS)
+			{
+			  Log(LOGERROR,
+			      "First packet does not contain keyframe, all "
+			      "timestamps are smaller than the keyframe "
+			      "timestamp; probably the resume seek failed?");
+			}
+		    stopKeyframeSearch:
+		      ;
+		      if (!r->m_read.bFoundFlvKeyframe)
+			{
+			  Log(LOGERROR,
+			      "Couldn't find the seeked keyframe in this chunk!");
+			  ret = RTMP_READ_IGNORE;
+			  break;
+			}
+		    }
+		}
+	    }
+
+	  if (packet.m_nTimeStamp > 0
+	      && (r->m_read.bFoundFlvKeyframe || r->m_read.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 including the
+	       * change between 08/09 <-> FLV packets
+	       */
+	      r->m_read.bFoundFlvKeyframe = true;
+	      r->m_read.bFoundKeyframe = true;
+	    }
+
+	  /* skip till we find our keyframe
+	   * (seeking might put us somewhere before it)
+	   */
+	  if (!r->m_read.bFoundKeyframe && packet.m_packetType != 0x16)
+	    {
+	      Log(LOGWARNING,
+		  "Stream does not start with requested frame, ignoring data... ");
+	      r->m_read.nIgnoredFrameCounter++;
+	      if (r->m_read.nIgnoredFrameCounter > MAX_IGNORED_FRAMES)
+		ret = RTMP_READ_ERROR;	/* fatal error, couldn't continue stream */
+	      else
+		ret = RTMP_READ_IGNORE;
+	      break;
+	    }
+	  /* ok, do the same for FLV streams */
+	  if (!r->m_read.bFoundFlvKeyframe && packet.m_packetType == 0x16)
+	    {
+	      Log(LOGWARNING,
+		  "Stream does not start with requested FLV frame, ignoring data... ");
+	      r->m_read.nIgnoredFlvFrameCounter++;
+	      if (r->m_read.nIgnoredFlvFrameCounter > MAX_IGNORED_FRAMES)
+		ret = RTMP_READ_ERROR;
+	      else
+		ret = RTMP_READ_IGNORE;
+	      break;
+	    }
+
+	  /* 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 (!r->m_read.bStopIgnoring && packet.m_packetType != 0x16)
+	    {			/* exclude type 0x16 (FLV) since it can
+				 * contain several FLV packets */
+	      if (packet.m_nTimeStamp == 0)
+		{
+		  ret = RTMP_READ_IGNORE;
+		  break;
+		}
+	      else
+		{
+		  r->m_read.bStopIgnoring = true;	/* stop ignoring packets */
+		}
+	    }
+	}
+
+      /* calculate packet size and allocate slop 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);
+
+      char *ptr, *pend;
+      if (size + 4 > buflen)
+	{
+	  /* the extra 4 is for the case of an FLV stream without a last
+	   * prevTagSize (we need extra 4 bytes to append it) */
+	  r->m_read.buf = malloc(size + 4);
+	  if (r->m_read.buf == 0)
+	    {
+	      Log(LOGERROR, "Couldn't allocate memory!");
+	      ret = RTMP_READ_ERROR;		// fatal error
+	      break;
+	    }
+	  recopy = true;
+	  ptr = r->m_read.buf;
+	}
+      else
+	{
+	  ptr = buf;
+	}
+      pend = ptr + size + 4;
+
+      /* use to return timestamp of last processed packet */
+      uint32_t nTimeStamp = 0;
+
+      /* 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)
+	{
+	  nTimeStamp = r->m_read.nResumeTS + packet.m_nTimeStamp;
+	  prevTagSize = 11 + nPacketLen;
+
+	  *ptr = packet.m_packetType;
+	  ptr++;
+	  ptr = AMF_EncodeInt24(ptr, pend, 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, pend, nTimeStamp);
+	  *ptr = (char)((nTimeStamp & 0xFF000000) >> 24);
+	  ptr++;
+
+	  /* stream id */
+	  ptr = AMF_EncodeInt24(ptr, pend, 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)
+	    {
+	      /* size without header (11) and without prevTagSize (4) */
+	      uint32_t dataSize = AMF_DecodeInt24(packetBody + pos + 1);
+	      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 */
+	      r->m_read.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 = RTMP_READ_ERROR;
+		      break;
+		    }
+		  Log(LOGWARNING, "No tagSize found, appending!");
+
+		  /* we have to append a last tagSize! */
+		  prevTagSize = dataSize + 11;
+		  AMF_EncodeInt32(ptr + pos + 11 + dataSize, pend,
+				  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, pend,
+				      prevTagSize);
+		    }
+		}
+
+	      pos += prevTagSize + 4;	//(11+dataSize+4);
+	    }
+	}
+      ptr += len;
+
+      if (packet.m_packetType != 0x16)
+	{
+	  /* FLV tag packets contain their own prevTagSize */
+	  AMF_EncodeInt32(ptr, pend, prevTagSize);
+	}
+
+      /* 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);
+      r->m_read.tsm = r->Link.bLiveStream ? packet.m_nTimeStamp : nTimeStamp;
+
+      ret = size;
+      break;
+    }
+
+  if (rtnGetNextMediaPacket)
+    RTMPPacket_Free(&packet);
+
+  if (recopy)
+    {
+      memcpy(buf, r->m_read.buf, buflen);
+      r->m_read.bufpos = r->m_read.buf + buflen;
+      r->m_read.buflen = ret - buflen;
+    }
+  return ret;
+}
+
+static const char flvHeader[] = { 'F', 'L', 'V', 0x01,
+  0x00,				/* 0x04 == audio, 0x01 == video */
+  0x00, 0x00, 0x00, 0x09,
+  0x00, 0x00, 0x00, 0x00
+};
+
+#define HEADERBUF	(128*1024)
+int
+RTMP_Read(RTMP *r, char *buf, int size)
+{
+  int nRead = 0, total = 0;
+
+  /* can't continue */
+  if (r->m_read.status < 0)
+    return -1;
+
+  /* first time thru */
+  if (!r->m_read.bDidHeader)
+    {
+      if (!r->m_read.bResume)
+	{
+	  char *mybuf = malloc(HEADERBUF);
+	  r->m_read.buf = mybuf;
+	  r->m_read.buflen = HEADERBUF;
+
+	  memcpy(mybuf, flvHeader, sizeof(flvHeader));
+	  r->m_read.buf += sizeof(flvHeader);
+	  r->m_read.buflen -= sizeof(flvHeader);
+
+	  while (r->m_read.tsm == 0)
+	    {
+	      nRead = Read_1_Packet(r, r->m_read.buf, r->m_read.buflen);
+	      if (nRead < 0)
+		{
+		  free(mybuf);
+		  r->m_read.buf = NULL;
+		  r->m_read.buflen = 0;
+		  r->m_read.status = nRead;
+		  return -1;
+		}
+	      r->m_read.buf += nRead;
+	      r->m_read.buflen -= nRead;
+	    }
+	  mybuf[4] = r->m_read.dataType;
+	  r->m_read.buflen = r->m_read.buf - mybuf;
+	  r->m_read.buf = mybuf;
+	  r->m_read.bufpos = mybuf;
+	}
+      r->m_read.bDidHeader = true;
+    }
+
+  /* If there's leftover data buffered, use it up */
+  if (r->m_read.buf)
+    {
+      nRead = r->m_read.buflen;
+      if (nRead > size)
+	nRead = size;
+      memcpy(buf, r->m_read.bufpos, nRead);
+      r->m_read.buflen -= nRead;
+      if (!r->m_read.buflen)
+	{
+	  free(r->m_read.buf);
+	  r->m_read.buf = NULL;
+	  r->m_read.bufpos = NULL;
+	}
+      else
+	{
+	  r->m_read.bufpos += nRead;
+	}
+      buf += nRead;
+      total += nRead;
+      size -= nRead;
+    }
+
+  while (size > 0 && (nRead = Read_1_Packet(r, buf, size)) >= 0)
+    {
+      buf += nRead;
+      total += nRead;
+      size -= nRead;
+    }
+  if (nRead < 0)
+    r->m_read.status = nRead;
+
+  if (size < 0)
+    total += size;
+  return total;
+}

Modified: trunk/librtmp/rtmp.h
==============================================================================
--- trunk/librtmp/rtmp.h	Wed Mar 10 20:29:40 2010	(r330)
+++ trunk/librtmp/rtmp.h	Fri Mar 12 05:53:01 2010	(r331)
@@ -177,6 +177,36 @@ extern "C"
 #endif
   } RTMP_LNK;
 
+  /* state for read() wrapper */
+  typedef struct RTMP_READ
+  {
+    char *buf;
+    char *bufpos;
+    unsigned int buflen;
+    uint32_t tsm;
+    uint8_t dataType;
+    uint8_t bResume;
+    uint8_t bDidHeader;
+    int8_t status;
+#define RTMP_READ_COMPLETE	-3
+#define RTMP_READ_ERROR	-2
+#define RTMP_READ_EOF	-1
+#define RTMP_READ_IGNORE	0
+
+    /* if bResume == TRUE */
+    uint8_t initialFrameType;
+    uint8_t bStopIgnoring;
+    uint8_t bFoundKeyframe;
+    uint8_t bFoundFlvKeyframe;
+    uint32_t nResumeTS;
+    char *metaHeader;
+    char *initialFrame;
+    uint32_t nMetaHeaderSize;
+    uint32_t nInitialFrameSize;
+    uint32_t nIgnoredFrameCounter;
+    uint32_t nIgnoredFlvFrameCounter;
+  } RTMP_READ;
+
   typedef struct RTMP
   {
     int m_inChunkSize;
@@ -193,9 +223,9 @@ extern "C"
     int m_nServerBW;
     int m_nClientBW;
     uint8_t m_nClientBW2;
-    bool m_bPlaying;
-    bool m_bSendEncoding;
-    bool m_bSendCounter;
+    uint8_t m_bPlaying;
+    uint8_t m_bSendEncoding;
+    uint8_t m_bSendCounter;
 
     AVal *m_methodCalls;	/* remote method calls queue */
     int m_numCalls;
@@ -217,6 +247,7 @@ extern "C"
     int m_unackd;
     AVal m_clientID;
 
+    RTMP_READ m_read;
     RTMPSockBuf m_sb;
   } RTMP;
 
@@ -280,6 +311,7 @@ extern "C"
   bool RTMP_SendSeek(RTMP *r, double dTime);
   bool RTMP_SendServerBW(RTMP *r);
   void RTMP_DropRequest(RTMP *r, int i, bool freeit);
+  int RTMP_Read(RTMP *r, char *buf, int size);
 
 #ifdef CRYPTO
 /* hashswf.c */


More information about the rtmpdump mailing list