Merge branch 'master' into 0.11
[platform/upstream/gst-plugins-good.git] / gst / rtp / gstrtpjpegdepay.c
index 01216ad..14355f3 100644 (file)
 
 #include <gst/rtp/gstrtpbuffer.h>
 
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include "gstrtpjpegdepay.h"
 
 GST_DEBUG_CATEGORY_STATIC (rtpjpegdepay_debug);
 #define GST_CAT_DEFAULT (rtpjpegdepay_debug)
 
-/* elementfactory information */
-static const GstElementDetails gst_rtp_jpegdepay_details =
-GST_ELEMENT_DETAILS ("RTP JPEG depayloader",
-    "Codec/Depayloader/Network",
-    "Extracts JPEG video from RTP packets (RFC 2435)",
-    "Wim Taymans <wim.taymans@gmail.com>");
-
 static GstStaticPadTemplate gst_rtp_jpeg_depay_src_template =
 GST_STATIC_PAD_TEMPLATE ("src",
     GST_PAD_SRC,
@@ -50,70 +46,108 @@ static GstStaticPadTemplate gst_rtp_jpeg_depay_sink_template =
     GST_STATIC_CAPS ("application/x-rtp, "
         "media = (string) \"video\", "
         "payload = (int) " GST_RTP_PAYLOAD_DYNAMIC_STRING ", "
-        "clock-rate = (int) 90000, " "encoding-name = (string) \"JPEG\";"
+        "clock-rate = (int) 90000, " "encoding-name = (string) \"JPEG\"; "
+        /* optional SDP attributes */
+        /*
+         * "a-framerate = (string) 0.00, "
+         * "x-framerate = (string) 0.00, "
+         * "x-dimensions = (string) \"1234,1234\", "
+         */
         "application/x-rtp, "
         "media = (string) \"video\", "
         "payload = (int) " GST_RTP_PAYLOAD_JPEG_STRING ", "
-        "clock-rate = (int) 90000")
+        "clock-rate = (int) 90000"
+        /* optional SDP attributes */
+        /*
+         * "a-framerate = (string) 0.00, "
+         * "x-framerate = (string) 0.00, "
+         * "x-dimensions = (string) \"1234,1234\""
+         */
+    )
     );
 
-GST_BOILERPLATE (GstRtpJPEGDepay, gst_rtp_jpeg_depay, GstBaseRTPDepayload,
-    GST_TYPE_BASE_RTP_DEPAYLOAD);
+#define gst_rtp_jpeg_depay_parent_class parent_class
+G_DEFINE_TYPE (GstRtpJPEGDepay, gst_rtp_jpeg_depay,
+    GST_TYPE_RTP_BASE_DEPAYLOAD);
 
 static void gst_rtp_jpeg_depay_finalize (GObject * object);
 
-static gboolean gst_rtp_jpeg_depay_setcaps (GstBaseRTPDepayload * depayload,
+static GstStateChangeReturn gst_rtp_jpeg_depay_change_state (GstElement *
+    element, GstStateChange transition);
+
+static gboolean gst_rtp_jpeg_depay_setcaps (GstRTPBaseDepayload * depayload,
     GstCaps * caps);
-static GstBuffer *gst_rtp_jpeg_depay_process (GstBaseRTPDepayload * depayload,
+static GstBuffer *gst_rtp_jpeg_depay_process (GstRTPBaseDepayload * depayload,
     GstBuffer * buf);
 
 static void
-gst_rtp_jpeg_depay_base_init (gpointer klass)
-{
-  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
-
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&gst_rtp_jpeg_depay_src_template));
-  gst_element_class_add_pad_template (element_class,
-      gst_static_pad_template_get (&gst_rtp_jpeg_depay_sink_template));
-
-  gst_element_class_set_details (element_class, &gst_rtp_jpegdepay_details);
-}
-
-static void
 gst_rtp_jpeg_depay_class_init (GstRtpJPEGDepayClass * klass)
 {
   GObjectClass *gobject_class;
-  GstBaseRTPDepayloadClass *gstbasertpdepayload_class;
+  GstElementClass *gstelement_class;
+  GstRTPBaseDepayloadClass *gstrtpbasedepayload_class;
 
   gobject_class = (GObjectClass *) klass;
-  gstbasertpdepayload_class = (GstBaseRTPDepayloadClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+  gstrtpbasedepayload_class = (GstRTPBaseDepayloadClass *) klass;
 
   gobject_class->finalize = gst_rtp_jpeg_depay_finalize;
 
-  parent_class = g_type_class_peek_parent (klass);
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&gst_rtp_jpeg_depay_src_template));
+  gst_element_class_add_pad_template (gstelement_class,
+      gst_static_pad_template_get (&gst_rtp_jpeg_depay_sink_template));
+
+  gst_element_class_set_details_simple (gstelement_class,
+      "RTP JPEG depayloader", "Codec/Depayloader/Network/RTP",
+      "Extracts JPEG video from RTP packets (RFC 2435)",
+      "Wim Taymans <wim.taymans@gmail.com>");
 
-  gstbasertpdepayload_class->set_caps = gst_rtp_jpeg_depay_setcaps;
-  gstbasertpdepayload_class->process = gst_rtp_jpeg_depay_process;
+  gstelement_class->change_state = gst_rtp_jpeg_depay_change_state;
+
+  gstrtpbasedepayload_class->set_caps = gst_rtp_jpeg_depay_setcaps;
+  gstrtpbasedepayload_class->process = gst_rtp_jpeg_depay_process;
 
   GST_DEBUG_CATEGORY_INIT (rtpjpegdepay_debug, "rtpjpegdepay", 0,
       "JPEG Video RTP Depayloader");
 }
 
 static void
-gst_rtp_jpeg_depay_init (GstRtpJPEGDepay * rtpjpegdepay,
-    GstRtpJPEGDepayClass * klass)
+gst_rtp_jpeg_depay_init (GstRtpJPEGDepay * rtpjpegdepay)
 {
   rtpjpegdepay->adapter = gst_adapter_new ();
 }
 
 static void
+gst_rtp_jpeg_depay_reset (GstRtpJPEGDepay * depay)
+{
+  gint i;
+
+  depay->width = 0;
+  depay->height = 0;
+  depay->media_width = 0;
+  depay->media_height = 0;
+  depay->frate_num = 0;
+  depay->frate_denom = 1;
+  depay->discont = TRUE;
+
+  for (i = 0; i < 255; i++) {
+    g_free (depay->qtables[i]);
+    depay->qtables[i] = NULL;
+  }
+
+  gst_adapter_clear (depay->adapter);
+}
+
+static void
 gst_rtp_jpeg_depay_finalize (GObject * object)
 {
   GstRtpJPEGDepay *rtpjpegdepay;
 
   rtpjpegdepay = GST_RTP_JPEG_DEPAY (object);
 
+  gst_rtp_jpeg_depay_reset (rtpjpegdepay);
+
   g_object_unref (rtpjpegdepay->adapter);
   rtpjpegdepay->adapter = NULL;
 
@@ -378,28 +412,72 @@ MakeHeaders (guint8 * p, int type, int width, int height, guint8 * qt,
 };
 
 static gboolean
-gst_rtp_jpeg_depay_setcaps (GstBaseRTPDepayload * depayload, GstCaps * caps)
+gst_rtp_jpeg_depay_setcaps (GstRTPBaseDepayload * depayload, GstCaps * caps)
 {
   GstRtpJPEGDepay *rtpjpegdepay;
   GstStructure *structure;
   gint clock_rate;
+  const gchar *media_attr;
 
   rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload);
 
   structure = gst_caps_get_structure (caps, 0);
+  GST_DEBUG_OBJECT (rtpjpegdepay, "Caps set: %" GST_PTR_FORMAT, caps);
 
   if (!gst_structure_get_int (structure, "clock-rate", &clock_rate))
     clock_rate = 90000;
   depayload->clock_rate = clock_rate;
 
+  /* reset defaults */
   rtpjpegdepay->width = 0;
   rtpjpegdepay->height = 0;
+  rtpjpegdepay->media_width = 0;
+  rtpjpegdepay->media_height = 0;
+  rtpjpegdepay->frate_num = 0;
+  rtpjpegdepay->frate_denom = 1;
+
+  /* check for optional SDP attributes */
+  if ((media_attr = gst_structure_get_string (structure, "x-dimensions"))) {
+    gint w, h;
+
+    if (sscanf (media_attr, "%d,%d", &w, &h) == 2) {
+      rtpjpegdepay->media_width = w;
+      rtpjpegdepay->media_height = h;
+    }
+  }
+
+  /* try to get a framerate */
+  media_attr = gst_structure_get_string (structure, "a-framerate");
+  if (!media_attr)
+    media_attr = gst_structure_get_string (structure, "x-framerate");
+
+  if (media_attr) {
+    GValue src = { 0 };
+    GValue dest = { 0 };
+    gchar *s;
+
+    /* canonicalise floating point string so we can handle framerate strings
+     * in the form "24.930" or "24,930" irrespective of the current locale */
+    s = g_strdup (media_attr);
+    g_strdelimit (s, ",", '.');
+
+    /* convert the float to a fraction */
+    g_value_init (&src, G_TYPE_DOUBLE);
+    g_value_set_double (&src, g_ascii_strtod (s, NULL));
+    g_value_init (&dest, GST_TYPE_FRACTION);
+    g_value_transform (&src, &dest);
+
+    rtpjpegdepay->frate_num = gst_value_get_fraction_numerator (&dest);
+    rtpjpegdepay->frate_denom = gst_value_get_fraction_denominator (&dest);
+
+    g_free (s);
+  }
 
   return TRUE;
 }
 
 static GstBuffer *
-gst_rtp_jpeg_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
+gst_rtp_jpeg_depay_process (GstRTPBaseDepayload * depayload, GstBuffer * buf)
 {
   GstRtpJPEGDepay *rtpjpegdepay;
   GstBuffer *outbuf;
@@ -410,19 +488,22 @@ gst_rtp_jpeg_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
   guint type, width, height;
   guint16 dri, precision, length;
   guint8 *qtable;
+  GstRTPBuffer rtp = { NULL };
 
   rtpjpegdepay = GST_RTP_JPEG_DEPAY (depayload);
 
   if (GST_BUFFER_IS_DISCONT (buf)) {
     gst_adapter_clear (rtpjpegdepay->adapter);
+    rtpjpegdepay->discont = TRUE;
   }
 
-  payload_len = gst_rtp_buffer_get_payload_len (buf);
+  gst_rtp_buffer_map (buf, GST_MAP_READ, &rtp);
+  payload_len = gst_rtp_buffer_get_payload_len (&rtp);
 
   if (payload_len < 8)
     goto empty_packet;
 
-  payload = gst_rtp_buffer_get_payload (buf);
+  payload = gst_rtp_buffer_get_payload (&rtp);
   header_len = 0;
 
   /*  0                   1                   2                   3
@@ -439,6 +520,14 @@ gst_rtp_jpeg_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
   width = payload[6] * 8;
   height = payload[7] * 8;
 
+  /* allow frame dimensions > 2040, passed in SDP session or media attributes
+   * from gstrtspsrc.c (gst_rtspsrc_sdp_attributes_to_caps), or in caps */
+  if (!width)
+    width = rtpjpegdepay->media_width;
+
+  if (!height)
+    height = rtpjpegdepay->media_height;
+
   if (width == 0 || height == 0)
     goto invalid_dimension;
 
@@ -513,14 +602,16 @@ gst_rtp_jpeg_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
   }
 
   if (frag_offset == 0) {
+    GstMapInfo map;
     guint size;
 
     if (rtpjpegdepay->width != width || rtpjpegdepay->height != height) {
       GstCaps *outcaps;
 
       outcaps =
-          gst_caps_new_simple ("image/jpeg", "framerate", GST_TYPE_FRACTION, 0,
-          1, "width", G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
+          gst_caps_new_simple ("image/jpeg", "framerate", GST_TYPE_FRACTION,
+          rtpjpegdepay->frate_num, rtpjpegdepay->frate_denom, "width",
+          G_TYPE_INT, width, "height", G_TYPE_INT, height, NULL);
       gst_pad_set_caps (depayload->srcpad, outcaps);
       gst_caps_unref (outcaps);
 
@@ -554,26 +645,26 @@ gst_rtp_jpeg_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
     }
     /* max header length, should be big enough */
     outbuf = gst_buffer_new_and_alloc (1000);
-    size = MakeHeaders (GST_BUFFER_DATA (outbuf), type,
-        width, height, qtable, precision, dri);
+    gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
+    size = MakeHeaders (map.data, type, width, height, qtable, precision, dri);
+    gst_buffer_unmap (outbuf, &map);
+    gst_buffer_resize (outbuf, 0, size);
 
     GST_DEBUG_OBJECT (rtpjpegdepay, "pushing %u bytes of header", size);
 
-    GST_BUFFER_SIZE (outbuf) = size;
-
     gst_adapter_push (rtpjpegdepay->adapter, outbuf);
   }
 
   /* take JPEG data, push in the adapter */
   GST_DEBUG_OBJECT (rtpjpegdepay, "pushing data at offset %d", header_len);
-  outbuf = gst_rtp_buffer_get_payload_subbuffer (buf, header_len, -1);
+  outbuf = gst_rtp_buffer_get_payload_subbuffer (&rtp, header_len, -1);
   gst_adapter_push (rtpjpegdepay->adapter, outbuf);
   outbuf = NULL;
 
-  if (gst_rtp_buffer_get_marker (buf)) {
+  if (gst_rtp_buffer_get_marker (&rtp)) {
     guint avail;
     guint8 end[2];
-    guint8 *data;
+    GstMapInfo map;
 
     /* last buffer take all data out of the adapter */
     avail = gst_adapter_available (rtpjpegdepay->adapter);
@@ -588,18 +679,26 @@ gst_rtp_jpeg_depay_process (GstBaseRTPDepayload * depayload, GstBuffer * buf)
 
       /* no EOI marker, add one */
       outbuf = gst_buffer_new_and_alloc (2);
-      data = GST_BUFFER_DATA (outbuf);
-      data[0] = 0xff;
-      data[1] = 0xd9;
+      gst_buffer_map (outbuf, &map, GST_MAP_WRITE);
+      map.data[0] = 0xff;
+      map.data[1] = 0xd9;
+      gst_buffer_unmap (outbuf, &map);
 
       gst_adapter_push (rtpjpegdepay->adapter, outbuf);
       avail += 2;
     }
     outbuf = gst_adapter_take_buffer (rtpjpegdepay->adapter, avail);
 
+    if (rtpjpegdepay->discont) {
+      GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
+      rtpjpegdepay->discont = FALSE;
+    }
+
     GST_DEBUG_OBJECT (rtpjpegdepay, "returning %u bytes", avail);
   }
 
+  gst_rtp_buffer_unmap (&rtp);
+
   return outbuf;
 
   /* ERRORS */
@@ -607,24 +706,57 @@ empty_packet:
   {
     GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, DECODE,
         ("Empty Payload."), (NULL));
+    gst_rtp_buffer_unmap (&rtp);
     return NULL;
   }
 invalid_dimension:
   {
     GST_ELEMENT_WARNING (rtpjpegdepay, STREAM, FORMAT,
         ("Invalid Dimension %dx%d.", width, height), (NULL));
+    gst_rtp_buffer_unmap (&rtp);
     return NULL;
   }
 no_qtable:
   {
     GST_WARNING_OBJECT (rtpjpegdepay, "no qtable");
+    gst_rtp_buffer_unmap (&rtp);
     return NULL;
   }
 }
 
+
+static GstStateChangeReturn
+gst_rtp_jpeg_depay_change_state (GstElement * element,
+    GstStateChange transition)
+{
+  GstRtpJPEGDepay *rtpjpegdepay;
+  GstStateChangeReturn ret;
+
+  rtpjpegdepay = GST_RTP_JPEG_DEPAY (element);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_READY_TO_PAUSED:
+      gst_rtp_jpeg_depay_reset (rtpjpegdepay);
+      break;
+    default:
+      break;
+  }
+
+  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
+
+  switch (transition) {
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      break;
+    default:
+      break;
+  }
+  return ret;
+}
+
+
 gboolean
 gst_rtp_jpeg_depay_plugin_init (GstPlugin * plugin)
 {
   return gst_element_register (plugin, "rtpjpegdepay",
-      GST_RANK_MARGINAL, GST_TYPE_RTP_JPEG_DEPAY);
+      GST_RANK_SECONDARY, GST_TYPE_RTP_JPEG_DEPAY);
 }