Index: gstrtph264pay.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/rtp/gstrtph264pay.c,v
retrieving revision 1.3
diff -p -r1.3 gstrtph264pay.c
*** gstrtph264pay.c	18 Oct 2007 08:27:56 -0000	1.3
--- gstrtph264pay.c	10 Dec 2007 09:20:17 -0000
***************
*** 26,31 ****
--- 26,37 ----
  
  #include "gstrtph264pay.h"
  
+ 
+ #define SPS_TYPE_ID  7
+ #define PPS_TYPE_ID  8
+ #define USE_MEMCMP
+ 
+ 
  GST_DEBUG_CATEGORY_STATIC (rtph264pay_debug);
  #define GST_CAT_DEFAULT (rtph264pay_debug)
  
*************** gst_rtp_h264_pay_class_init (GstRtpH264P
*** 116,121 ****
--- 122,130 ----
  static void
  gst_rtp_h264_pay_init (GstRtpH264Pay * rtph264pay, GstRtpH264PayClass * klass)
  {
+   rtph264pay->profile = 0;
+   rtph264pay->sps = NULL;
+   rtph264pay->pps = NULL;
  }
  
  static void
*************** gst_rtp_h264_pay_finalize (GObject * obj
*** 124,129 ****
--- 133,141 ----
    GstRtpH264Pay *rtph264pay;
  
    rtph264pay = GST_RTP_H264_PAY (object);
+   
+   if (rtph264pay->sps) g_free(rtph264pay->sps);
+   if (rtph264pay->pps) g_free(rtph264pay->pps);
  
    G_OBJECT_CLASS (parent_class)->finalize (object);
  }
*************** gst_rtp_h264_pay_setcaps (GstBaseRTPPayl
*** 143,148 ****
--- 155,395 ----
  }
  
  
+ static guint
+ next_start_code(guint8 *data, guint size)
+ {
+ #if 1 /* Boyer-Moore string matching algorithm, in a degenerative
+        sense because our search 'alphabet' is binary - 0 & 1 only.
+        This allow us to simplify the general BM algorithm to a very
+        simple form.
+       */
+   guint offset = 3; /* assume 1 is in the 4th byte */
+   while (offset < size) {
+     if (1 == data[offset]) {
+       unsigned int shift = offset;
+       if (0 == data[--shift]) { 
+ 	if (0 == data[--shift]) {
+ 	  if (0 == data[--shift]) {
+ 	    return shift;
+ 	  }
+ 	}
+       }
+       /* The jump is always 4 because of the 1 previously matched.
+ 	 All the 0's must be after this '1' matched at offset
+       */
+       offset += 4; 
+     } else if (0 == data[offset]) { /* maybe next byte is 1? */
+       offset++;
+     } else { /* can jump 4 bytes forward */
+       offset += 4; 
+     }
+     /* at each iteration, we rescan in a backward manner until
+        we match 0.0.0.1 in reverse order. Since our search string
+        has only 2 'alpabets' (i.e. 0 & 1), we know that any
+        mismatch will force us to shift a fix number of steps
+     */
+   }
+ 	
+ #else /* the obvious naive method */
+   guint val = 0xffffffff, offset = 0;
+   
+   while (offset < size) {
+     val <<= 8;
+     val |= *data++;
+     offset++;
+     
+     if (val == 0x00000001) {
+       return offset - 4;
+     }
+   }
+ #endif
+ 
+   GST_DEBUG("Cannot find next NAL start code. returning %u", size);
+ 
+   return size;
+ } /* guint next_start_code(guint8*, guint) */
+ 
+ 
+ 
+ static gboolean 
+ is_nal_equal(const guint8 *nal1, const guint8 *nal2, guint len)
+ {
+ #ifdef USE_MEMCMP
+   /* memcmp() has been proven to perform less optimal compared to
+      the implementation below (probably because of some shortcut
+      logic and not needing to determine the order between the
+      two memory blocks. The custom implementation runs 
+      runs about 20% faster. 
+   */
+   return (0 == memcmp(nal1, nal2, len));
+ #else
+   /* if we have a 64-bit processor, we may use guint64 to make 
+      this go faster. Otherwise with 32 bits, we are already
+      going faster than byte to byte compare.
+   */
+   guint remainder = len & 0x3;
+   guint num_int = len >> 2;
+   guint32 *pu1 = (guint32*)nal1, *pu2 = (guint32*)nal2;
+   guint i;
+   
+   /* compare by groups of sizeof(guint32) bytes */
+   for (i = 0; i < num_int; i++) {
+     /* XOR is faster than CMP?... */
+     if (pu1[i] ^ pu2[i]) return FALSE;
+   }
+ 
+   /* check that the remaining bytes are still equal */
+   if (!remainder) {
+     return 1;
+   } else if (1 == remainder) {
+     return nal1[--len] == nal2[len];
+   } else { /* 2 or 3 */
+     if (remainder & 1) { /* -1 if 3 bytes left */
+       if (nal1[--len] != nal2[len]) return 0;
+     }
+     
+     /* last 2 bytes */
+     return (nal1[--len] == nal2[len]) /* -1 */
+       && (nal1[--len] == nal2[len]);  /* -2 */
+   }
+ #endif /* USE_MEMCMP */
+ } /*  gint is_nal_equal(const guint8*, const guint8*, guint) */
+ 
+ 
+ static void 
+ gst_rtp_h264_pay_decode_nal(GstRtpH264Pay *payloader, 
+ 			    guint8 *data, guint size, gboolean *updated)
+ {
+   guint8 *sps = NULL, *pps = NULL;
+   guint sps_len = 0, pps_len = 0;
+ 
+   /* default is no update */
+   *updated = FALSE;
+ 
+   if (size <= 3) {
+     GST_WARNING("Encoded buffer len %u <= 3", size);
+   } else {
+     GST_DEBUG("NAL payload len=%u", size);
+ 
+     /* loop through all NAL units and save the locations of any
+        SPS / PPS for later processing. Only the last seen SPS
+        or PPS will be considered
+     */
+     while (size > 5) {
+       /* = 0, in case gcc complains of uninitialized use */
+       guint8 header, type;
+       guint len;
+ 
+       len = next_start_code(data, size);
+       header = data[0];
+       type = header & 0x1f;
+      
+       /* keep sps & pps separately so that we can update either one 
+ 	 independently */
+      
+       if (SPS_TYPE_ID == type) {
+ 	/* encode the entire SPS NAL in base64 */
+ 	GST_DEBUG("Found SPS %x %x %x Len=%u\n", (header >> 7), 
+ 		  (header >> 5) & 3, type, len);
+ 
+ 	sps = data;
+ 	sps_len = len;
+       } else if (PPS_TYPE_ID == type) {
+ 	
+ 	/* encoder the entire PPS NAL in base64 */
+ 	GST_DEBUG("Found PPS %x %x %x Len = %u\n",
+ 		  (header >> 7), (header >> 5) & 3, type, len);
+ 
+ 	pps = data;
+ 	pps_len = len;
+       } else {
+ 	GST_DEBUG("NAL: %x %x %x Len = %u\n", (header >> 7),
+ 		  (header >> 5) & 3, type, len);
+       }
+ 
+       /* end of loop */
+       if (len >= size - 4) {
+ 	break;
+       }
+ 
+       /* next NAL start */	
+       data += len + 4;
+       size -= len + 4;
+     } /* while */   
+     
+     /* If we encountered an SPS and/or a PPS, check if it's the
+        same as the one we have. If not, update our version and
+        set *updated to TRUE
+     */
+     if (sps_len > 0) {
+       if ((payloader->sps_len != sps_len)
+ 	  || !is_nal_equal(payloader->sps, sps, sps_len)) {
+ 	payloader->profile = (sps[1] << 16) +
+ 	  (sps[2] << 8) + sps[3];
+ 
+ 	GST_DEBUG("Profile level IDC = %06x", payloader->profile);
+ 	
+ 	if (payloader->sps_len) g_free(payloader->sps);
+ 
+ 	payloader->sps = sps_len ? g_new(guint8, sps_len) : NULL;
+ 	memcpy(payloader->sps, sps, sps_len);
+ 	payloader->sps_len = sps_len;
+ 	*updated = TRUE;
+       }
+     }
+ 
+     if (pps_len > 0) {
+       if ((payloader->pps_len != pps_len)
+ 	  || !is_nal_equal(payloader->pps, pps, pps_len)) {
+ 	if (payloader->pps_len) g_free(payloader->pps);
+ 	
+ 	payloader->pps = pps_len ? g_new(guint8, pps_len) : NULL;
+ 	memcpy(payloader->pps, pps, pps_len);
+ 	payloader->pps_len = pps_len;
+ 	*updated = TRUE;
+       }
+     }
+   }
+ } /*  void gst_rtp_h264_pay_decode_nal(GstRtpH264Pay*, guint8*, guint, 
+                                        gboolean*)
+   */
+ 
+ 
+ static void 
+ gst_rtp_h264_pay_parse_sps_pps(GstBaseRTPPayload *basepayload, 
+ 			       guint8 *data, guint size)
+ {
+   gboolean doUpdate = FALSE;
+   GstRtpH264Pay *payloader = GST_RTP_H264_PAY (basepayload);
+   
+   gst_rtp_h264_pay_decode_nal(payloader, data, size, &doUpdate);
+   
+   /* if has new SPS & PPS, update the output caps */
+   if (doUpdate) {
+     gchar *profile = g_new(gchar, 7); /* 6 + terminating NULL */
+     gchar *sps = (payloader->sps_len > 0) 
+       ? g_base64_encode(payloader->sps, payloader->sps_len) : "";
+     gchar *pps = (payloader->pps_len > 0) 
+       ? g_base64_encode(payloader->pps, payloader->pps_len) : "";
+ 
+     /* profile is 24 bit. Force it to respect the limit */
+     sprintf(profile, "%06x", payloader->profile & 0xffffff);
+ 
+     gst_basertppayload_set_outcaps (basepayload, "profile",
+ 				    G_TYPE_STRING, profile,
+ 				    "sps", G_TYPE_STRING, sps,
+ 				    "pps", G_TYPE_STRING, pps,
+ 				    NULL);
+     GST_DEBUG("outcaps udpate: profile=%s, sps=%s, pps=%s\n", 
+ 	      profile, sps, pps);
+ 
+     g_free(profile);
+     if (payloader->sps_len > 0) g_free(sps);
+     if (payloader->pps_len > 0) g_free(pps);
+   }
+ } /* void gst_rtp_h264_pay_parse_sps_pps(GstRtpH264Pay*, guint8*, guint) */
+ 
+ 
  static GstFlowReturn
  gst_rtp_h264_pay_handle_buffer (GstBaseRTPPayload * basepayload,
      GstBuffer * buffer)
*************** gst_rtp_h264_pay_handle_buffer (GstBaseR
*** 168,181 ****
  
    /* H264 stream analysis */
    pdata = data;
!   idxdata = size;
!   while (idxdata > 5 &&
!       (pdata[0] != 0x00 || pdata[1] != 0x00 || pdata[2] != 0x1 ||
!           (pdata[3] & 0x1f) < 1 || (pdata[3] & 0x1f) > 23)
!       ) {
!     pdata++;
!     idxdata--;
!     GST_DEBUG_OBJECT (basepayload, "idxdata=%d", idxdata);
    }
  
    if (idxdata < 5) {
--- 415,433 ----
  
    /* H264 stream analysis */
    pdata = data;
! 
!   /* use next_start_code() to scan buffer.
!      next_start_code() returns the offset in data, 
!      starting from zero to the first byte of 0.0.0.1
!      If no start code is found, it returns the value of the 
!      'size' parameter. 
!      pdata is unchanged by the call to next_start_code()
!   */
!   {
!     guint offset = next_start_code(pdata, size);
! 
!     pdata += offset;
!     idxdata = size - offset;
    }
  
    if (idxdata < 5) {
*************** gst_rtp_h264_pay_handle_buffer (GstBaseR
*** 184,191 ****
      return GST_FLOW_OK;
    }
  
!   pdata += 3;
!   idxdata -= 3;
  
    nalType = pdata[0] & 0x1f;
    GST_DEBUG_OBJECT (basepayload, "Processing Buffer with NAL TYPE=%d", nalType);
--- 436,447 ----
      return GST_FLOW_OK;
    }
  
!   pdata += 4;
!   idxdata -= 4;
! 
!   /* We know our stream is a valid H264 NAL packet, 
!      go parse it for SPS/PPS to enrich the caps */
!   gst_rtp_h264_pay_parse_sps_pps(basepayload, pdata, idxdata);
  
    nalType = pdata[0] & 0x1f;
    GST_DEBUG_OBJECT (basepayload, "Processing Buffer with NAL TYPE=%d", nalType);
Index: gstrtph264pay.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-good/gst/rtp/gstrtph264pay.h,v
retrieving revision 1.1
diff -p -r1.1 gstrtph264pay.h
*** gstrtph264pay.h	29 Mar 2007 08:08:49 -0000	1.1
--- gstrtph264pay.h	10 Dec 2007 09:20:17 -0000
*************** typedef struct _GstRtpH264PayClass GstRt
*** 42,47 ****
--- 42,51 ----
  struct _GstRtpH264Pay
  {
    GstBaseRTPPayload payload;
+ 
+   guint profile;
+   guint8 *sps, *pps;
+   guint sps_len, pps_len;
  };
  
  struct _GstRtpH264PayClass
