gst/avi/avi-ids.h: Add vprp chunk related structures.
authorMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Sun, 29 Jun 2008 19:52:51 +0000 (19:52 +0000)
committerMark Nauwelaerts <mark.nauwelaerts@collabora.co.uk>
Sun, 29 Jun 2008 19:52:51 +0000 (19:52 +0000)
Original commit message from CVS:
* gst/avi/avi-ids.h:
Add vprp chunk related structures.
* gst/avi/gstavidemux.c: (gst_avi_demux_riff_parse_vprp),
(gst_avi_demux_parse_stream):
Parse optional vprp chunk and add calculated pixel-aspect-ratio
to caps.  Fixes #539482.
* gst/avi/gstavimux.h:
* gst/avi/gstavimux.c: (gst_avi_mux_pad_reset),
(gst_avi_mux_vidsink_set_caps), (gst_avi_mux_riff_get_avi_header):
Add a vprp chunk if non-trival pixel-aspect-ratio provided in caps.

ChangeLog
gst/avi/avi-ids.h
gst/avi/gstavidemux.c
gst/avi/gstavimux.c
gst/avi/gstavimux.h

index 03cd272..ac48c0d 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,16 @@
+2008-06-29  Mark Nauwelaerts  <mark.nauwelaerts@collabora.co.uk>
+
+       * gst/avi/avi-ids.h:
+       Add vprp chunk related structures.
+       * gst/avi/gstavidemux.c: (gst_avi_demux_riff_parse_vprp),
+       (gst_avi_demux_parse_stream):
+       Parse optional vprp chunk and add calculated pixel-aspect-ratio
+       to caps.  Fixes #539482.
+       * gst/avi/gstavimux.h:
+       * gst/avi/gstavimux.c: (gst_avi_mux_pad_reset),
+       (gst_avi_mux_vidsink_set_caps), (gst_avi_mux_riff_get_avi_header):
+       Add a vprp chunk if non-trival pixel-aspect-ratio provided in caps.
+
 2008-06-28  Mark Nauwelaerts  <mark.nauwelaerts@collabora.co.uk>
 
        * tests/check/elements/avimux.c: (check_avimux_pad):
index f9e2dd3..8ca550b 100644 (file)
@@ -45,4 +45,33 @@ typedef struct _gst_riff_avih {
   guint32 length;
 } gst_riff_avih;
 
+/* vprp (video properties) ODML header */
+/* see ODML spec for some/more explanation */
+#define GST_RIFF_TAG_vprp GST_MAKE_FOURCC ('v','p','r','p')
+#define GST_RIFF_VPRP_VIDEO_FIELDS        (2)
+
+typedef struct _gst_riff_vprp_video_field_desc {
+  guint32 compressed_bm_height;
+  guint32 compressed_bm_width;
+  guint32 valid_bm_height;
+  guint32 valid_bm_width;
+  guint32 valid_bm_x_offset;
+  guint32 valid_bm_y_offset;
+  guint32 video_x_t_offset;
+  guint32 video_y_start;
+} gst_riff_vprp_video_field_desc;
+
+typedef struct _gst_riff_vprp {
+  guint32 format_token;      /* whether fields defined by standard */
+  guint32 standard;          /* video display standard, UNKNOWN, PAL, etc */
+  guint32 vert_rate;         /* vertical refresh rate */
+  guint32 hor_t_total;       /* width */
+  guint32 vert_lines;        /* height */
+  guint32 aspect;            /* aspect ratio high word:low word */
+  guint32 width;             /* active width */
+  guint32 height;            /* active height */
+  guint32 fields;            /* field count */
+  gst_riff_vprp_video_field_desc field_info[GST_RIFF_VPRP_VIDEO_FIELDS];
+} gst_riff_vprp;
+
 #endif /* __GST_AVI_H__ */
index 38211be..a48ceee 100644 (file)
@@ -1197,6 +1197,127 @@ gst_avi_demux_read_subindexes_pull (GstAviDemux * avi,
 }
 
 /*
+ * gst_avi_demux_riff_parse_vprp:
+ * @element: caller element (used for debugging/error).
+ * @buf: input data to be used for parsing, stripped from header.
+ * @vprp: a pointer (returned by this function) to a filled-in vprp
+ *        structure. Caller should free it.
+ *
+ * Parses a video streamĀ“s vprp. This function takes ownership of @buf.
+ *
+ * Returns: TRUE if parsing succeeded, otherwise FALSE. The stream
+ *          should be skipped on error, but it is not fatal.
+ */
+static gboolean
+gst_avi_demux_riff_parse_vprp (GstElement * element,
+    GstBuffer * buf, gst_riff_vprp ** _vprp)
+{
+  gst_riff_vprp *vprp;
+  gint k;
+
+  g_return_val_if_fail (buf != NULL, FALSE);
+  g_return_val_if_fail (_vprp != NULL, FALSE);
+
+  if (GST_BUFFER_SIZE (buf) < G_STRUCT_OFFSET (gst_riff_vprp, field_info))
+    goto too_small;
+
+  vprp = g_memdup (GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
+
+#if (G_BYTE_ORDER == G_BIG_ENDIAN)
+  vprp->format_token = GUINT32_FROM_LE (vprp->format_token);
+  vprp->standard = GUINT32_FROM_LE (vprp->standard);
+  vprp->vert_rate = GUINT32_FROM_LE (vprp->vert_rate);
+  vprp->hor_t_total = GUINT32_FROM_LE (vprp->hor_t_total);
+  vprp->vert_lines = GUINT32_FROM_LE (vprp->vert_lines);
+  vprp->aspect = GUINT32_FROM_LE (vprp->aspect);
+  vprp->width = GUINT32_FROM_LE (vprp->width);
+  vprp->height = GUINT32_FROM_LE (vprp->height);
+  vprp->fields = GUINT32_FROM_LE (vprp->fields);
+#endif
+
+  /* size checking */
+  /* calculate fields based on size */
+  k = (GST_BUFFER_SIZE (buf) - G_STRUCT_OFFSET (gst_riff_vprp, field_info)) /
+      vprp->fields;
+  if (vprp->fields > k) {
+    GST_WARNING_OBJECT (element,
+        "vprp header indicated %d fields, only %d available", vprp->fields, k);
+    vprp->fields = k;
+  }
+  if (vprp->fields > GST_RIFF_VPRP_VIDEO_FIELDS) {
+    GST_WARNING_OBJECT (element,
+        "vprp header indicated %d fields, at most %d supported", vprp->fields,
+        GST_RIFF_VPRP_VIDEO_FIELDS);
+    vprp->fields = GST_RIFF_VPRP_VIDEO_FIELDS;
+  }
+#if (G_BYTE_ORDER == G_BIG_ENDIAN)
+  for (k = 0; k < vprp->fields; k++) {
+    gst_riff_vprp_video_field_desc *fd;
+
+    fd = &(vidpad->vprp.field_info[k]);
+    fd->compressed_bm_height = GUINT32_FROM_LE (fd->compressed_bm_height);
+    fd->compressed_bm_width = GUINT32_FROM_LE (fd->compressed_bm_width);
+    fd->valid_bm_height = GUINT32_FROM_LE (fd->valid_bm_height);
+    fd->valid_bm_width = GUINT16_FROM_LE (fd->valid_bm_width);
+    fd->valid_bm_x_offset = GUINT16_FROM_LE (fd->valid_bm_x_offset);
+    fd->valid_bm_y_offset = GUINT32_FROM_LE (fd->valid_bm_y_offset);
+    fd->video_x_t_offset = GUINT32_FROM_LE (fd->video_x_t_offset);
+    fd->video_y_start = GUINT32_FROM_LE (fd->video_y_start);
+  }
+#endif
+
+  /* debug */
+  GST_INFO_OBJECT (element, "vprp tag found in context vids:");
+  GST_INFO_OBJECT (element, " format_token  %d", vprp->format_token);
+  GST_INFO_OBJECT (element, " standard      %d", vprp->standard);
+  GST_INFO_OBJECT (element, " vert_rate     %d", vprp->vert_rate);
+  GST_INFO_OBJECT (element, " hor_t_total   %d", vprp->hor_t_total);
+  GST_INFO_OBJECT (element, " vert_lines    %d", vprp->vert_lines);
+  GST_INFO_OBJECT (element, " aspect        %d:%d", vprp->aspect >> 16,
+      vprp->aspect & 0xffff);
+  GST_INFO_OBJECT (element, " width         %d", vprp->width);
+  GST_INFO_OBJECT (element, " height        %d", vprp->height);
+  GST_INFO_OBJECT (element, " fields        %d", vprp->fields);
+  for (k = 0; k < vprp->fields; k++) {
+    gst_riff_vprp_video_field_desc *fd;
+
+    fd = &(vprp->field_info[k]);
+    GST_INFO_OBJECT (element, " field %u description:", k);
+    GST_INFO_OBJECT (element, "  compressed_bm_height  %d",
+        fd->compressed_bm_height);
+    GST_INFO_OBJECT (element, "  compressed_bm_width  %d",
+        fd->compressed_bm_width);
+    GST_INFO_OBJECT (element, "  valid_bm_height       %d",
+        fd->valid_bm_height);
+    GST_INFO_OBJECT (element, "  valid_bm_width        %d", fd->valid_bm_width);
+    GST_INFO_OBJECT (element, "  valid_bm_x_offset     %d",
+        fd->valid_bm_x_offset);
+    GST_INFO_OBJECT (element, "  valid_bm_y_offset     %d",
+        fd->valid_bm_y_offset);
+    GST_INFO_OBJECT (element, "  video_x_t_offset      %d",
+        fd->video_x_t_offset);
+    GST_INFO_OBJECT (element, "  video_y_start         %d", fd->video_y_start);
+  }
+
+  gst_buffer_unref (buf);
+
+  *_vprp = vprp;
+
+  return TRUE;
+
+  /* ERRORS */
+too_small:
+  {
+    GST_ERROR_OBJECT (element,
+        "Too small vprp (%d available, at least %d needed)",
+        GST_BUFFER_SIZE (buf),
+        (int) G_STRUCT_OFFSET (gst_riff_vprp, field_info));
+    gst_buffer_unref (buf);
+    return FALSE;
+  }
+}
+
+/*
  * gst_avi_demux_parse_stream:
  * @avi: calling element (used for debugging/errors).
  * @buf: input buffer used to parse the stream.
@@ -1223,7 +1344,8 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
   GstCaps *caps = NULL;
   GstPad *pad;
   GstElement *element;
-  gboolean got_strh = FALSE, got_strf = FALSE;
+  gboolean got_strh = FALSE, got_strf = FALSE, got_vprp = FALSE;
+  gst_riff_vprp *vprp = NULL;
 
   element = GST_ELEMENT_CAST (avi);
 
@@ -1294,6 +1416,30 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
         got_strf = TRUE;
         break;
       }
+      case GST_RIFF_TAG_vprp:
+      {
+        if (got_vprp) {
+          GST_WARNING_OBJECT (avi, "Ignoring additional vprp chunk");
+          break;
+        }
+        if (!got_strh) {
+          GST_ERROR_OBJECT (avi, "Found vprp chunk before strh chunk");
+          goto fail;
+        }
+        if (!got_strf) {
+          GST_ERROR_OBJECT (avi, "Found vprp chunk before strf chunk");
+          goto fail;
+        }
+
+        if (!gst_avi_demux_riff_parse_vprp (element, sub, &vprp)) {
+          GST_WARNING_OBJECT (avi, "Failed to parse vprp chunk");
+          /* not considered fatal */
+          g_free (vprp);
+          vprp = NULL;
+        } else
+          got_vprp = TRUE;
+        break;
+      }
       case GST_RIFF_TAG_strd:
         if (stream->initdata)
           gst_buffer_unref (stream->initdata);
@@ -1362,6 +1508,21 @@ gst_avi_demux_parse_stream (GstAviDemux * avi, GstBuffer * buf)
       if (!caps) {
         caps = gst_caps_new_simple ("video/x-avi-unknown", "fourcc",
             GST_TYPE_FOURCC, fourcc, NULL);
+      } else if (got_vprp && vprp) {
+        guint32 aspect_n, aspect_d;
+        gint n, d;
+
+        aspect_n = vprp->aspect >> 16;
+        aspect_d = vprp->aspect & 0xffff;
+        /* calculate the pixel aspect ratio using w/h and aspect ratio */
+        n = aspect_n * stream->strf.vids->height;
+        d = aspect_d * stream->strf.vids->width;
+        if (n && d)
+          gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
+              n, d, NULL);
+        /* very local, not needed elsewhere */
+        g_free (vprp);
+        vprp = NULL;
       }
       tag_name = GST_TAG_VIDEO_CODEC;
       avi->num_v_streams++;
@@ -1483,6 +1644,7 @@ fail:
       gst_buffer_unref (buf);
     if (sub)
       gst_buffer_unref (sub);
+    g_free (vprp);
     g_free (codec_name);
     g_free (stream->strh);
     g_free (stream->strf.data);
index 3beff5e..80d8ba8 100644 (file)
@@ -337,6 +337,7 @@ gst_avi_mux_pad_reset (GstAviPad * avipad, gboolean free)
     }
 
     memset (&(vidpad->vids), 0, sizeof (gst_riff_strf_vids));
+    memset (&(vidpad->vprp), 0, sizeof (gst_riff_vprp));
   } else {
     GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;
 
@@ -425,9 +426,10 @@ gst_avi_mux_vidsink_set_caps (GstPad * pad, GstCaps * vscaps)
   GstAviCollectData *collect_pad;
   GstStructure *structure;
   const gchar *mimetype;
-  const GValue *fps;
+  const GValue *fps, *par;
   const GValue *codec_data;
   gint width, height;
+  gint par_n, par_d;
 
   avimux = GST_AVI_MUX (gst_pad_get_parent (pad));
 
@@ -463,6 +465,36 @@ gst_avi_mux_vidsink_set_caps (GstPad * pad, GstCaps * vscaps)
   avipad->parent.hdr.rate = gst_value_get_fraction_numerator (fps);
   avipad->parent.hdr.scale = gst_value_get_fraction_denominator (fps);
 
+  /* (pixel) aspect ratio data, if any */
+  par = gst_structure_get_value (structure, "pixel-aspect-ratio");
+  /* only use video properties header if there is non-trivial aspect info */
+  if (par && GST_VALUE_HOLDS_FRACTION (par) &&
+      ((par_n = gst_value_get_fraction_numerator (par)) !=
+          (par_d = gst_value_get_fraction_denominator (par)))) {
+    GValue to_ratio = { 0, };
+    guint ratio_n, ratio_d;
+
+    /* some fraction voodoo to obtain simplest possible ratio */
+    g_value_init (&to_ratio, GST_TYPE_FRACTION);
+    gst_value_set_fraction (&to_ratio, width * par_n, height * par_d);
+    ratio_n = gst_value_get_fraction_numerator (&to_ratio);
+    ratio_d = gst_value_get_fraction_denominator (&to_ratio);
+    GST_DEBUG_OBJECT (avimux, "generating vprp data with aspect ratio %d/%d",
+        ratio_n, ratio_d);
+    /* simply fill in */
+    avipad->vprp.vert_rate = avipad->parent.hdr.rate / avipad->parent.hdr.scale;
+    avipad->vprp.hor_t_total = width;
+    avipad->vprp.vert_lines = height;
+    avipad->vprp.aspect = (ratio_n) << 16 | (ratio_d & 0xffff);
+    avipad->vprp.width = width;
+    avipad->vprp.height = height;
+    avipad->vprp.fields = 1;
+    avipad->vprp.field_info[0].compressed_bm_height = height;
+    avipad->vprp.field_info[0].compressed_bm_width = width;
+    avipad->vprp.field_info[0].valid_bm_height = height;
+    avipad->vprp.field_info[0].valid_bm_width = width;
+  }
+
   /* codec initialization data, if any */
   codec_data = gst_structure_get_value (structure, "codec_data");
   if (codec_data) {
@@ -957,6 +989,7 @@ gst_avi_mux_riff_get_avi_header (GstAviMux * avimux)
   size += avimux->codec_data_size + 100 + sizeof (gst_riff_avih)
       + (g_slist_length (avimux->sinkpads) * (100 + sizeof (gst_riff_strh_full)
           + sizeof (gst_riff_strf_vids)
+          + sizeof (gst_riff_vprp)
           + sizeof (gst_riff_strf_auds)
           + ODML_SUPERINDEX_SIZE));
   buffer = gst_buffer_new_and_alloc (size);
@@ -1003,13 +1036,21 @@ gst_avi_mux_riff_get_avi_header (GstAviMux * avimux)
     GstAviPad *avipad = (GstAviPad *) node->data;
     GstAviVideoPad *vidpad = (GstAviVideoPad *) avipad;
     GstAviAudioPad *audpad = (GstAviAudioPad *) avipad;
-    guint codec_size = 0, strl_size = 0;
+    guint codec_size = 0, strl_size = 0, vprp_size = 0;
 
     if (avipad->is_video) {
       if (vidpad->vids_codec_data)
         codec_size = GST_BUFFER_SIZE (vidpad->vids_codec_data);
       strl_size = sizeof (gst_riff_strh_full) + sizeof (gst_riff_strf_vids)
           + codec_size + 4 * 5 + ODML_SUPERINDEX_SIZE;
+      if (vidpad->vprp.aspect) {
+        /* let's be on the safe side */
+        vidpad->vprp.fields = MIN (vidpad->vprp.fields,
+            GST_RIFF_VPRP_VIDEO_FIELDS);
+        vprp_size = G_STRUCT_OFFSET (gst_riff_vprp, field_info)
+            + (vidpad->vprp.fields * sizeof (gst_riff_vprp_video_field_desc));
+        strl_size += 4 * 2 + vprp_size;
+      }
     } else {
       if (audpad->auds_codec_data)
         codec_size = GST_BUFFER_SIZE (audpad->auds_codec_data);
@@ -1072,6 +1113,42 @@ gst_avi_mux_riff_get_avi_header (GstAviMux * avimux)
         buffdata += codec_size;
         highmark += codec_size;
       }
+
+      /* add video property data, mainly for aspect ratio, if any */
+      if (vprp_size) {
+        gint f;
+
+        /* the vprp header */
+        memcpy (buffdata + 0, "vprp", 4);
+        GST_WRITE_UINT32_LE (buffdata + 4, vprp_size);
+        /* the actual data */
+        GST_WRITE_UINT32_LE (buffdata + 8, vidpad->vprp.format_token);
+        GST_WRITE_UINT32_LE (buffdata + 12, vidpad->vprp.standard);
+        GST_WRITE_UINT32_LE (buffdata + 16, vidpad->vprp.vert_rate);
+        GST_WRITE_UINT32_LE (buffdata + 20, vidpad->vprp.hor_t_total);
+        GST_WRITE_UINT32_LE (buffdata + 24, vidpad->vprp.vert_lines);
+        GST_WRITE_UINT32_LE (buffdata + 28, vidpad->vprp.aspect);
+        GST_WRITE_UINT32_LE (buffdata + 32, vidpad->vprp.width);
+        GST_WRITE_UINT32_LE (buffdata + 36, vidpad->vprp.height);
+        GST_WRITE_UINT32_LE (buffdata + 40, vidpad->vprp.fields);
+        buffdata += codec_size + 44;
+        highmark += codec_size + 44;
+        for (f = 0; f < vidpad->vprp.fields; ++f) {
+          gst_riff_vprp_video_field_desc *fd;
+
+          fd = &(vidpad->vprp.field_info[f]);
+          GST_WRITE_UINT32_LE (buffdata + 0, fd->compressed_bm_height);
+          GST_WRITE_UINT32_LE (buffdata + 4, fd->compressed_bm_width);
+          GST_WRITE_UINT32_LE (buffdata + 8, fd->valid_bm_height);
+          GST_WRITE_UINT32_LE (buffdata + 12, fd->valid_bm_width);
+          GST_WRITE_UINT32_LE (buffdata + 16, fd->valid_bm_x_offset);
+          GST_WRITE_UINT32_LE (buffdata + 20, fd->valid_bm_y_offset);
+          GST_WRITE_UINT32_LE (buffdata + 24, fd->video_x_t_offset);
+          GST_WRITE_UINT32_LE (buffdata + 28, fd->video_y_start);
+          buffdata += codec_size + 32;
+          highmark += codec_size + 32;
+        }
+      }
     } else {
       /* the audio header */
       memcpy (buffdata + 0, "strf", 4);
index 0100e03..6fb9a47 100644 (file)
@@ -89,6 +89,8 @@ typedef struct _GstAviVideoPad {
   gst_riff_strf_vids vids;
   /* extra data */
   GstBuffer *vids_codec_data;
+  /* ODML video properties */
+  gst_riff_vprp vprp;
 
 } GstAviVideoPad;