liveMedia/QuickTimeGenericRTPSource.cpp

Go to the documentation of this file.
00001 /**********
00002 This library is free software; you can redistribute it and/or modify it under
00003 the terms of the GNU Lesser General Public License as published by the
00004 Free Software Foundation; either version 2.1 of the License, or (at your
00005 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
00006 
00007 This library is distributed in the hope that it will be useful, but WITHOUT
00008 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00009 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
00010 more details.
00011 
00012 You should have received a copy of the GNU Lesser General Public License
00013 along with this library; if not, write to the Free Software Foundation, Inc.,
00014 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
00015 **********/
00016 // "liveMedia"
00017 // Copyright (c) 1996-2012 Live Networks, Inc.  All rights reserved.
00018 // RTP Sources containing generic QuickTime stream data, as defined in
00019 //     <http://developer.apple.com/quicktime/icefloe/dispatch026.html>
00020 // Implementation
00021 
00022 #include "QuickTimeGenericRTPSource.hh"
00023 
00025 
00026 // A subclass of BufferedPacket, used to separate out
00027 // individual frames (when PCK == 2)
00028 
00029 class QTGenericBufferedPacket: public BufferedPacket {
00030 public:
00031   QTGenericBufferedPacket(QuickTimeGenericRTPSource& ourSource);
00032   virtual ~QTGenericBufferedPacket();
00033 
00034 private: // redefined virtual functions
00035   virtual unsigned nextEnclosedFrameSize(unsigned char*& framePtr,
00036                                          unsigned dataSize);
00037 private:
00038   QuickTimeGenericRTPSource& fOurSource;
00039 };
00040 
00041 class QTGenericBufferedPacketFactory: public BufferedPacketFactory {
00042 private: // redefined virtual functions
00043   virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource);
00044 };
00045 
00046 
00048 
00049 QuickTimeGenericRTPSource*
00050 QuickTimeGenericRTPSource::createNew(UsageEnvironment& env,
00051                                      Groupsock* RTPgs,
00052                                      unsigned char rtpPayloadFormat,
00053                                      unsigned rtpTimestampFrequency,
00054                                      char const* mimeTypeString) {
00055   return new QuickTimeGenericRTPSource(env, RTPgs, rtpPayloadFormat,
00056                                        rtpTimestampFrequency,
00057                                        mimeTypeString);
00058 }
00059 
00060 QuickTimeGenericRTPSource
00061 ::QuickTimeGenericRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
00062                             unsigned char rtpPayloadFormat,
00063                             unsigned rtpTimestampFrequency,
00064                             char const* mimeTypeString)
00065   : MultiFramedRTPSource(env, RTPgs,
00066                          rtpPayloadFormat, rtpTimestampFrequency,
00067                          new QTGenericBufferedPacketFactory),
00068     fMIMEtypeString(strDup(mimeTypeString)) {
00069   qtState.PCK = 0;
00070   qtState.timescale = 0;
00071   qtState.sdAtom = NULL;
00072   qtState.sdAtomSize = qtState.width = qtState.height = 0;
00073 }
00074 
00075 QuickTimeGenericRTPSource::~QuickTimeGenericRTPSource() {
00076   delete[] qtState.sdAtom;
00077   delete[] (char*)fMIMEtypeString;
00078 }
00079 
00080 Boolean QuickTimeGenericRTPSource
00081 ::processSpecialHeader(BufferedPacket* packet,
00082                        unsigned& resultSpecialHeaderSize) {
00083   unsigned char* headerStart = packet->data();
00084   unsigned packetSize = packet->dataSize();
00085 
00086   // The "QuickTime Header" must be at least 4 bytes in size:
00087   // Extract the known fields from the first 4 bytes:
00088   unsigned expectedHeaderSize = 4;
00089   if (packetSize < expectedHeaderSize) return False;
00090 
00091   unsigned char VER = (headerStart[0]&0xF0)>>4;
00092   if (VER > 1) return False; // unknown header version
00093   qtState.PCK = (headerStart[0]&0x0C)>>2;
00094 #ifdef DEBUG
00095   Boolean S = (headerStart[0]&0x02) != 0;
00096 #endif
00097   Boolean Q = (headerStart[0]&0x01) != 0;
00098 
00099   Boolean L = (headerStart[1]&0x80) != 0;
00100 
00101 #ifdef DEBUG
00102   Boolean D = (headerStart[2]&0x80) != 0;
00103   unsigned short payloadId = ((headerStart[2]&0x7F)<<8)|headerStart[3];
00104 #endif
00105   headerStart += 4;
00106 
00107 #ifdef DEBUG
00108   fprintf(stderr, "PCK: %d, S: %d, Q: %d, L: %d, D: %d, payloadId: %d\n", qtState.PCK, S, Q, L, D, payloadId);
00109 #endif
00110 
00111   if (Q) { // A "QuickTime Payload Description" follows
00112     expectedHeaderSize += 4;
00113     if (packetSize < expectedHeaderSize) return False;
00114 
00115 #ifdef DEBUG
00116     Boolean K = (headerStart[0]&0x80) != 0;
00117     Boolean F = (headerStart[0]&0x40) != 0;
00118     Boolean A = (headerStart[0]&0x20) != 0;
00119     Boolean Z = (headerStart[0]&0x10) != 0;
00120 #endif
00121     unsigned payloadDescriptionLength = (headerStart[2]<<8)|headerStart[3];
00122     headerStart += 4;
00123 
00124 #ifdef DEBUG
00125     fprintf(stderr, "\tK: %d, F: %d, A: %d, Z: %d, payloadDescriptionLength: %d\n", K, F, A, Z, payloadDescriptionLength);
00126 #endif
00127     // Make sure "payloadDescriptionLength" is valid
00128     if (payloadDescriptionLength < 12) return False;
00129     expectedHeaderSize += (payloadDescriptionLength - 4);
00130     unsigned nonPaddedSize = expectedHeaderSize;
00131     expectedHeaderSize += 3;
00132     expectedHeaderSize -= expectedHeaderSize%4; // adds padding
00133     if (packetSize < expectedHeaderSize) return False;
00134     unsigned char padding = expectedHeaderSize - nonPaddedSize;
00135 
00136 #ifdef DEBUG
00137     unsigned mediaType = (headerStart[0]<<24)|(headerStart[1]<<16)
00138       |(headerStart[2]<<8)|headerStart[3];
00139 #endif
00140     qtState.timescale = (headerStart[4]<<24)|(headerStart[5]<<16)
00141       |(headerStart[6]<<8)|headerStart[7];
00142     headerStart += 8;
00143 
00144     payloadDescriptionLength -= 12;
00145 #ifdef DEBUG
00146     fprintf(stderr, "\tmediaType: '%c%c%c%c', timescale: %d, %d bytes of TLVs left\n", mediaType>>24, (mediaType&0xFF0000)>>16, (mediaType&0xFF00)>>8, mediaType&0xFF, qtState.timescale, payloadDescriptionLength);
00147 #endif
00148 
00149     while (payloadDescriptionLength > 3) {
00150       unsigned short tlvLength = (headerStart[0]<<8)|headerStart[1];
00151       unsigned short tlvType = (headerStart[2]<<8)|headerStart[3];
00152       payloadDescriptionLength -= 4;
00153       if (tlvLength > payloadDescriptionLength) return False; // bad TLV
00154       headerStart += 4;
00155 #ifdef DEBUG
00156       fprintf(stderr, "\t\tTLV '%c%c', length %d, leaving %d remaining bytes\n", tlvType>>8, tlvType&0xFF, tlvLength, payloadDescriptionLength - tlvLength);
00157       for (int i = 0; i < tlvLength; ++i) fprintf(stderr, "%02x:", headerStart[i]); fprintf(stderr, "\n");
00158 #endif
00159 
00160       // Check for 'TLV's that we can use for our 'qtState'
00161       switch (tlvType) {
00162       case ('s'<<8|'d'): { // session description atom
00163         // Sanity check: the first 4 bytes of this must equal "tlvLength":
00164         unsigned atomLength  = (headerStart[0]<<24)|(headerStart[1]<<16)
00165           |(headerStart[2]<<8)|(headerStart[3]);
00166         if (atomLength != (unsigned)tlvLength) break;
00167 
00168         delete[] qtState.sdAtom; qtState.sdAtom = new char[tlvLength];
00169         memmove(qtState.sdAtom, headerStart, tlvLength);
00170         qtState.sdAtomSize = tlvLength;
00171         break;
00172       }
00173       case ('t'<<8|'w'): { // track width
00174         qtState.width = (headerStart[0]<<8)|headerStart[1];
00175         break;
00176       }
00177       case ('t'<<8|'h'): { // track height
00178         qtState.height = (headerStart[0]<<8)|headerStart[1];
00179         break;
00180       }
00181       }
00182 
00183       payloadDescriptionLength -= tlvLength;
00184       headerStart += tlvLength;
00185     }
00186     if (payloadDescriptionLength > 0) return False; // malformed TLV data
00187     headerStart += padding;
00188   }
00189 
00190   if (L) { // Sample-Specific info follows
00191     expectedHeaderSize += 4;
00192     if (packetSize < expectedHeaderSize) return False;
00193 
00194     unsigned ssInfoLength = (headerStart[2]<<8)|headerStart[3];
00195     headerStart += 4;
00196 
00197 #ifdef DEBUG
00198     fprintf(stderr, "\tssInfoLength: %d\n", ssInfoLength);
00199 #endif
00200     // Make sure "ssInfoLength" is valid
00201     if (ssInfoLength < 4) return False;
00202     expectedHeaderSize += (ssInfoLength - 4);
00203     unsigned nonPaddedSize = expectedHeaderSize;
00204     expectedHeaderSize += 3;
00205     expectedHeaderSize -= expectedHeaderSize%4; // adds padding
00206     if (packetSize < expectedHeaderSize) return False;
00207     unsigned char padding = expectedHeaderSize - nonPaddedSize;
00208 
00209     ssInfoLength -= 4;
00210     while (ssInfoLength > 3) {
00211       unsigned short tlvLength = (headerStart[0]<<8)|headerStart[1];
00212 #ifdef DEBUG
00213       unsigned short tlvType = (headerStart[2]<<8)|headerStart[3];
00214 #endif
00215       ssInfoLength -= 4;
00216       if (tlvLength > ssInfoLength) return False; // bad TLV
00217 #ifdef DEBUG
00218       fprintf(stderr, "\t\tTLV '%c%c', length %d, leaving %d remaining bytes\n", tlvType>>8, tlvType&0xFF, tlvLength, ssInfoLength - tlvLength);
00219       for (int i = 0; i < tlvLength; ++i) fprintf(stderr, "%02x:", headerStart[4+i]); fprintf(stderr, "\n");
00220 #endif
00221       ssInfoLength -= tlvLength;
00222       headerStart += 4 + tlvLength;
00223     }
00224     if (ssInfoLength > 0) return False; // malformed TLV data
00225     headerStart += padding;
00226   }
00227 
00228   fCurrentPacketBeginsFrame = fCurrentPacketCompletesFrame;
00229           // whether the *previous* packet ended a frame
00230   fCurrentPacketCompletesFrame = packet->rtpMarkerBit();
00231 
00232   resultSpecialHeaderSize = expectedHeaderSize;
00233 #ifdef DEBUG
00234   fprintf(stderr, "Result special header size: %d\n", resultSpecialHeaderSize);
00235 #endif
00236   return True;
00237 }
00238 
00239 char const* QuickTimeGenericRTPSource::MIMEtype() const {
00240   if (fMIMEtypeString == NULL) return MultiFramedRTPSource::MIMEtype();
00241 
00242   return fMIMEtypeString;
00243 }
00244 
00245 
00247 
00248 QTGenericBufferedPacket
00249 ::QTGenericBufferedPacket(QuickTimeGenericRTPSource& ourSource)
00250   : fOurSource(ourSource) {
00251 }
00252 
00253 QTGenericBufferedPacket::~QTGenericBufferedPacket() {
00254 }
00255 
00256 unsigned QTGenericBufferedPacket::
00257   nextEnclosedFrameSize(unsigned char*& framePtr, unsigned dataSize) {
00258   // We use the entire packet for a frame, unless "PCK" == 2
00259   if (fOurSource.qtState.PCK != 2) return dataSize;
00260 
00261   if (dataSize < 8) return 0; // sanity check
00262 
00263   unsigned short sampleLength = (framePtr[2]<<8)|framePtr[3];
00264   // later, extract and use the "timestamp" field #####
00265   framePtr += 8;
00266   dataSize -= 8;
00267 
00268   return sampleLength < dataSize ? sampleLength : dataSize;
00269 }
00270 
00271 BufferedPacket* QTGenericBufferedPacketFactory
00272 ::createNewPacket(MultiFramedRTPSource* ourSource) {
00273   return new QTGenericBufferedPacket((QuickTimeGenericRTPSource&)(*ourSource));
00274 }

Generated on Thu May 17 07:11:47 2012 for live by  doxygen 1.5.2