[Live-devel] [PATCH] Add Theora RTP Sink

Gilles Chanteperdrix gilles.chanteperdrix at xenomai.org
Sat Dec 14 11:19:43 PST 2013


---
 liveMedia/TheoraVideoRTPSink.cpp        |  184 +++++++++++++++++++++++++++++++
 liveMedia/include/TheoraVideoRTPSink.hh |   59 ++++++++++
 2 files changed, 243 insertions(+)
 create mode 100644 liveMedia/TheoraVideoRTPSink.cpp
 create mode 100644 liveMedia/include/TheoraVideoRTPSink.hh

diff --git a/liveMedia/TheoraVideoRTPSink.cpp b/liveMedia/TheoraVideoRTPSink.cpp
new file mode 100644
index 0000000..855b70a
--- /dev/null
+++ b/liveMedia/TheoraVideoRTPSink.cpp
@@ -0,0 +1,184 @@
+/*
+ * Theora Video RTP packetizer
+ * Copied from live555's VorbisAudioRTPSink
+ */
+
+#include "TheoraVideoRTPSink.hh"
+#include "Base64.hh"
+
+TheoraVideoRTPSink::TheoraVideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs,
+				       u_int8_t rtpPayloadFormat,
+				       u_int32_t rtpTimestampFrequency,
+				       unsigned width, unsigned height, enum PixFmt pf,
+				       u_int8_t* identificationHeader, unsigned identificationHeaderSize,
+				       u_int8_t* commentHeader, unsigned commentHeaderSize,
+				       u_int8_t* setupHeader, unsigned setupHeaderSize,
+				       u_int32_t identField)
+  : VideoRTPSink(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, "theora"),
+    fIdent(identField), fFmtpSDPLine(NULL) {
+  static const char *pf_to_str[] = {
+    "YCbCr-4:2:0",
+    "YCbCr-4:2:2",
+    "YCbCr-4:4:4",
+  };
+
+  // Create packed configuration headers, and encode this data into a "a=fmtp:" SDP line that we'll use to describe it:
+
+  // First, count how many headers (<=3) are included, and how many bytes will be used to encode these headers' sizes:
+  unsigned numHeaders = 0;
+  unsigned sizeSize[2]; // The number of bytes used to encode the lengths of the first two headers (but not the length of the 3rd)
+  sizeSize[0] = sizeSize[1] = 0;
+  if (identificationHeaderSize > 0) {
+    sizeSize[numHeaders++] = identificationHeaderSize < 128 ? 1 : identificationHeaderSize < 16384 ? 2 : 3;
+  }
+  if (commentHeaderSize > 0) {
+    sizeSize[numHeaders++] = commentHeaderSize < 128 ? 1 : commentHeaderSize < 16384 ? 2 : 3;
+  }
+  if (setupHeaderSize > 0) {
+    ++numHeaders;
+  } else {
+    sizeSize[1] = 0; // We have at most two headers, so the second one's length isn't encoded
+  }
+  if (numHeaders == 0) return; // With no headers, we can't set up a configuration
+  if (numHeaders == 1) sizeSize[0] = 0; // With only one header, its length isn't encoded
+
+  // Then figure out the size of the packed configuration headers, and allocate space for this:
+  unsigned length = identificationHeaderSize + commentHeaderSize + setupHeaderSize; // The "length" field in the packed headers
+  if (length > (unsigned)0xFFFF) return; // too big for a 16-bit field; we can't handle this
+  unsigned packedHeadersSize
+    = 4 // "Number of packed headers" field
+    + 3 // "ident" field
+    + 2 // "length" field
+    + 1 // "n. of headers" field
+    + sizeSize[0] + sizeSize[1] // "length1" and "length2" (if present) fields
+    + length;
+  u_int8_t* packedHeaders = new u_int8_t[packedHeadersSize];
+  if (packedHeaders == NULL) return;
+
+  // Fill in the 'packed headers':
+  u_int8_t* p = packedHeaders;
+  *p++ = 0; *p++ = 0; *p++ = 0; *p++ = 1; // "Number of packed headers": 1
+  *p++ = fIdent>>16; *p++ = fIdent>>8; *p++ = fIdent; // "Ident" (24 bits)
+  *p++ = length>>8; *p++ = length; // "length" (16 bits)
+  *p++ = numHeaders-1; // "n. of headers"
+  if (numHeaders > 1) {
+    // Fill in the "length1" header:
+    unsigned length1 = identificationHeaderSize > 0 ? identificationHeaderSize : commentHeaderSize;
+    if (length1 >= 16384) {
+      *p++ = 0x80; // flag, but no more, because we know length1 <= 32767
+    }
+    if (length1 >= 128) {
+      *p++ = 0x80|((length1&0x3F80)>>7); // flag + the second 7 bits
+    }
+    *p++ = length1&0x7F; // the low 7 bits
+
+    if (numHeaders > 2) { // numHeaders == 3
+      // Fill in the "length2" header (for the 'Comment' header):
+      unsigned length2 = commentHeaderSize;
+      if (length2 >= 16384) {
+	*p++ = 0x80; // flag, but no more, because we know length2 <= 32767
+      }
+      if (length2 >= 128) {
+	*p++ = 0x80|((length2&0x3F80)>>7); // flag + the second 7 bits
+      }
+      *p++ = length2&0x7F; // the low 7 bits
+    }
+  }
+  // Copy each header:
+  if (identificationHeader != NULL) memmove(p, identificationHeader, identificationHeaderSize); p += identificationHeaderSize;
+  if (commentHeader != NULL) memmove(p, commentHeader, commentHeaderSize); p += commentHeaderSize;
+  if (setupHeader != NULL) memmove(p, setupHeader, setupHeaderSize);
+
+  // Having set up the 'packed configuration headers', Base-64-encode this, and put it in our "a=fmtp:" SDP line:
+  char* base64PackedHeaders = base64Encode((char const*)packedHeaders, packedHeadersSize);
+  delete[] packedHeaders;
+
+  if (asprintf(&fFmtpSDPLine, "a=fmtp:%d sampling=%s;width=%u;height=%u;delivery-method=out_band/rtsp;configuration=%s\r\n", rtpPayloadType(), pf_to_str[pf], width, height, base64PackedHeaders) < 0)
+	  fFmtpSDPLine = NULL;
+  delete[] base64PackedHeaders;
+}
+
+TheoraVideoRTPSink::~TheoraVideoRTPSink() {
+  free(fFmtpSDPLine);
+}
+
+TheoraVideoRTPSink*
+TheoraVideoRTPSink::createNew(UsageEnvironment& env, Groupsock* RTPgs,
+			      u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency,
+			      unsigned width, unsigned height, enum PixFmt pf,
+			      u_int8_t* identificationHeader, unsigned identificationHeaderSize,
+			      u_int8_t* commentHeader, unsigned commentHeaderSize,
+			      u_int8_t* setupHeader, unsigned setupHeaderSize,
+			      u_int32_t identField) {
+  return new TheoraVideoRTPSink(env, RTPgs,
+				rtpPayloadFormat, rtpTimestampFrequency,
+				width, height, pf,
+				identificationHeader, identificationHeaderSize,
+				commentHeader, commentHeaderSize,
+				setupHeader, setupHeaderSize, identField);
+}
+
+char const* TheoraVideoRTPSink::auxSDPLine() {
+  return fFmtpSDPLine;
+}
+
+void TheoraVideoRTPSink
+::doSpecialFrameHandling(unsigned fragmentationOffset,
+			 unsigned char* frameStart,
+			 unsigned numBytesInFrame,
+			 struct timeval framePresentationTime,
+			 unsigned numRemainingBytes) {
+  // Set the 4-byte "payload header", as defined in http://svn.xiph.org/trunk/theora/doc/draft-ietf-avt-rtp-theora-00.txt
+  u_int8_t header[6];
+
+  // The three bytes of the header are our "Ident":
+  header[0] = fIdent>>16; header[1] = fIdent>>8; header[2] = fIdent;
+
+  // The final byte contains the "F", "TDT", and "numPkts" fields:
+  u_int8_t F; // Fragment type
+  if (numRemainingBytes > 0) {
+    if (fragmentationOffset > 0) {
+      F = 2<<6; // continuation fragment
+    } else {
+      F = 1<<6; // start fragment
+    }
+  } else {
+    if (fragmentationOffset > 0) {
+      F = 3<<6; // end fragment
+    } else {
+      F = 0<<6; // not fragmented
+    }
+  }
+  u_int8_t const TDT = 0<<4; // Theora Data Type (always a "Raw Theora payload")
+  u_int8_t numPkts = F == 0 ? (numFramesUsedSoFar() + 1): 0; // set to 0 when we're a fragment
+  header[3] = F|TDT|numPkts;
+
+  // There's also a 2-byte 'frame-specific' header: The length of the
+  // Theora data:
+  header[4] = numBytesInFrame >>8;
+  header[5] = numBytesInFrame;
+  setSpecialHeaderBytes(header, sizeof(header));
+
+  if (numRemainingBytes == 0) {
+    // This packet contains the last (or only) fragment of the frame.
+    // Set the RTP 'M' ('marker') bit:
+    setMarkerBit();
+  }
+
+  // Important: Also call our base class's doSpecialFrameHandling(),
+  // to set the packet's timestamp:
+  MultiFramedRTPSink::doSpecialFrameHandling(fragmentationOffset,
+					     frameStart, numBytesInFrame,
+					     framePresentationTime,
+					     numRemainingBytes);
+}
+
+Boolean TheoraVideoRTPSink::frameCanAppearAfterPacketStart(unsigned char const* /*frameStart*/,
+							   unsigned /*numBytesInFrame*/) const {
+  // Only one frame per packet:
+  return False;
+}
+
+unsigned TheoraVideoRTPSink::specialHeaderSize() const {
+  return 6;
+}
diff --git a/liveMedia/include/TheoraVideoRTPSink.hh b/liveMedia/include/TheoraVideoRTPSink.hh
new file mode 100644
index 0000000..989d383
--- /dev/null
+++ b/liveMedia/include/TheoraVideoRTPSink.hh
@@ -0,0 +1,59 @@
+/*
+ * Theora Video RTP packetizer
+ * Copied from live555's VorbisAudioRTPSink
+ */
+
+#ifndef _THEORA_VIDEO_RTP_SINK_HH
+#define _THEORA_VIDEO_RTP_SINK_HH
+
+#ifndef _VIDEO_RTP_SINK_HH
+#include "VideoRTPSink.hh"
+#endif
+
+class TheoraVideoRTPSink: public VideoRTPSink {
+public:
+  enum PixFmt {
+    YUV420,
+    YUV422,
+    YUV444,
+  };
+
+  static TheoraVideoRTPSink* createNew(UsageEnvironment& env,
+				       Groupsock* RTPgs,
+				       u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency,
+				       unsigned width, unsigned height, enum  PixFmt pf,
+				       // The following headers provide the 'configuration' information, for the SDP description:
+				       u_int8_t* identificationHeader, unsigned identificationHeaderSize,
+				       u_int8_t* commentHeader, unsigned commentHeaderSize,
+				       u_int8_t* setupHeader, unsigned setupHeaderSize, u_int32_t identField);
+
+protected:
+  TheoraVideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs,
+		     u_int8_t rtpPayloadFormat, u_int32_t rtpTimestampFrequency,
+		     unsigned width, unsigned height, enum  PixFmt pf,
+		     u_int8_t* identificationHeader, unsigned identificationHeaderSize,
+		     u_int8_t* commentHeader, unsigned commentHeaderSize,
+		     u_int8_t* setupHeader, unsigned setupHeaderSize,
+		     u_int32_t identField);
+	// called only by createNew()
+
+  virtual ~TheoraVideoRTPSink();
+
+private: // redefined virtual functions:
+  virtual char const* auxSDPLine(); // for the "a=fmtp:" SDP line
+
+  virtual void doSpecialFrameHandling(unsigned fragmentationOffset,
+				      unsigned char* frameStart,
+				      unsigned numBytesInFrame,
+				      struct timeval framePresentationTime,
+				      unsigned numRemainingBytes);
+  virtual Boolean frameCanAppearAfterPacketStart(unsigned char const* frameStart,
+						 unsigned numBytesInFrame) const;
+  virtual unsigned specialHeaderSize() const;
+
+private:
+  u_int32_t fIdent; // "Ident" field used by this stream.  (Only the low 24 bits of this are used.)
+  char* fFmtpSDPLine;
+};
+
+#endif
-- 
1.7.10.4



More information about the live-devel mailing list