qtdemux: Ensure that raw audio and video have properly aligned buffers
authorSebastian Dröge <sebastian@centricular.com>
Sun, 20 Nov 2016 11:08:27 +0000 (13:08 +0200)
committerSebastian Dröge <sebastian@centricular.com>
Sun, 20 Nov 2016 11:08:27 +0000 (13:08 +0200)
That is, aligned to the basic type for audio and to 32 bytes for video.
Fixes crashes if the raw buffers are passed to SIMD processing functions.

https://bugzilla.gnome.org/show_bug.cgi?id=774428

gst/isomp4/qtdemux.c

index b66c34c..15ac144 100644 (file)
@@ -298,6 +298,8 @@ struct _QtDemuxStream
   GstAllocator *allocator;
   GstAllocationParams params;
 
+  gsize alignment;
+
   /* when a discontinuity is pending */
   gboolean discont;
 
@@ -1848,6 +1850,7 @@ _create_stream (void)
   stream->n_samples_moof = 0;
   stream->duration_moof = 0;
   stream->duration_last_moof = 0;
+  stream->alignment = 1;
   g_queue_init (&stream->protection_scheme_event_queue);
   return stream;
 }
@@ -5127,6 +5130,44 @@ clipped:
   }
 }
 
+static GstBuffer *
+gst_qtdemux_align_buffer (GstQTDemux * demux,
+    GstBuffer * buffer, gsize alignment)
+{
+  GstMapInfo map;
+
+  gst_buffer_map (buffer, &map, GST_MAP_READ);
+
+  if (map.size < sizeof (guintptr)) {
+    gst_buffer_unmap (buffer, &map);
+    return buffer;
+  }
+
+  if (((guintptr) map.data) & (alignment - 1)) {
+    GstBuffer *new_buffer;
+    GstAllocationParams params = { 0, alignment - 1, 0, 0, };
+
+    new_buffer = gst_buffer_new_allocate (NULL,
+        gst_buffer_get_size (buffer), &params);
+
+    /* Copy data "by hand", so ensure alignment is kept: */
+    gst_buffer_fill (new_buffer, 0, map.data, map.size);
+
+    gst_buffer_copy_into (new_buffer, buffer, GST_BUFFER_COPY_METADATA, 0, -1);
+    GST_DEBUG_OBJECT (demux,
+        "We want output aligned on %" G_GSIZE_FORMAT ", reallocated",
+        alignment);
+
+    gst_buffer_unmap (buffer, &map);
+    gst_buffer_unref (buffer);
+
+    return new_buffer;
+  }
+
+  gst_buffer_unmap (buffer, &map);
+  return buffer;
+}
+
 /* the input buffer metadata must be writable,
  * but time/duration etc not yet set and need not be preserved */
 static GstBuffer *
@@ -5248,6 +5289,8 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
       GST_BUFFER_FLAG_UNSET (buf, GST_BUFFER_FLAG_DISCONT);
     }
 
+    if (stream->alignment > 1)
+      buffer = gst_qtdemux_align_buffer (qtdemux, buffer, stream->alignment);
     gst_pad_push (stream->pad, buffer);
 
     stream->buffers = g_slist_delete_link (stream->buffers, stream->buffers);
@@ -5351,6 +5394,9 @@ gst_qtdemux_decorate_and_push_buffer (GstQTDemux * qtdemux,
     }
   }
 
+  if (stream->alignment > 1)
+    buf = gst_qtdemux_align_buffer (qtdemux, buf, stream->alignment);
+
   ret = gst_pad_push (stream->pad, buf);
 
   if (GST_CLOCK_TIME_IS_VALID (pts) && GST_CLOCK_TIME_IS_VALID (duration)) {
@@ -13137,6 +13183,7 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
       caps = gst_caps_new_empty_simple ("video/x-raw");
       gst_caps_set_simple (caps, "format", G_TYPE_STRING, "RGB8P", NULL);
       _codec ("Windows Raw RGB");
+      stream->alignment = 32;
       break;
     case FOURCC_raw_:
     {
@@ -13500,11 +13547,24 @@ qtdemux_video_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
 
     /* enable clipping for raw video streams */
     stream->need_clip = TRUE;
+    stream->alignment = 32;
   }
 
   return caps;
 }
 
+static guint
+round_up_pow2 (guint n)
+{
+  n = n - 1;
+  n = n | (n >> 1);
+  n = n | (n >> 2);
+  n = n | (n >> 4);
+  n = n | (n >> 8);
+  n = n | (n >> 16);
+  return n + 1;
+}
+
 static GstCaps *
 qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
     guint32 fourcc, const guint8 * data, int len, gchar ** codec_name)
@@ -13547,6 +13607,8 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
       caps = gst_caps_new_simple ("audio/x-raw",
           "format", G_TYPE_STRING, gst_audio_format_to_string (format),
           "layout", G_TYPE_STRING, "interleaved", NULL);
+      stream->alignment = GST_ROUND_UP_8 (depth);
+      stream->alignment = round_up_pow2 (stream->alignment);
       break;
     }
     case GST_MAKE_FOURCC ('f', 'l', '6', '4'):
@@ -13554,12 +13616,14 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
       caps = gst_caps_new_simple ("audio/x-raw",
           "format", G_TYPE_STRING, "F64BE",
           "layout", G_TYPE_STRING, "interleaved", NULL);
+      stream->alignment = 8;
       break;
     case GST_MAKE_FOURCC ('f', 'l', '3', '2'):
       _codec ("Raw 32-bit floating-point audio");
       caps = gst_caps_new_simple ("audio/x-raw",
           "format", G_TYPE_STRING, "F32BE",
           "layout", G_TYPE_STRING, "interleaved", NULL);
+      stream->alignment = 4;
       break;
     case FOURCC_in24:
       _codec ("Raw 24-bit PCM audio");
@@ -13568,12 +13632,14 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
       caps = gst_caps_new_simple ("audio/x-raw",
           "format", G_TYPE_STRING, "S24BE",
           "layout", G_TYPE_STRING, "interleaved", NULL);
+      stream->alignment = 4;
       break;
     case GST_MAKE_FOURCC ('i', 'n', '3', '2'):
       _codec ("Raw 32-bit PCM audio");
       caps = gst_caps_new_simple ("audio/x-raw",
           "format", G_TYPE_STRING, "S32BE",
           "layout", G_TYPE_STRING, "interleaved", NULL);
+      stream->alignment = 4;
       break;
     case FOURCC_ulaw:
       _codec ("Mu-law audio");
@@ -13753,6 +13819,8 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
             "format", G_TYPE_STRING, gst_audio_format_to_string (format),
             "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
             "non-interleaved" : "interleaved", NULL);
+        stream->alignment = GST_ROUND_UP_8 (depth);
+        stream->alignment = round_up_pow2 (stream->alignment);
       } else {
         if (width == 0)
           width = 32;
@@ -13771,6 +13839,7 @@ qtdemux_audio_caps (GstQTDemux * qtdemux, QtDemuxStream * stream,
             "format", G_TYPE_STRING, gst_audio_format_to_string (format),
             "layout", G_TYPE_STRING, (flags & FLAG_IS_NON_INTERLEAVED) ?
             "non-interleaved" : "interleaved", NULL);
+        stream->alignment = width / 8;
       }
       break;
     }