aacparse: allow conversion from ADTS to raw AAC
authorVincent Penquerc'h <vincent.penquerch@collabora.co.uk>
Mon, 15 Jul 2013 16:11:45 +0000 (17:11 +0100)
committerVincent Penquerc'h <vincent.penquerch@collabora.co.uk>
Fri, 26 Jul 2013 08:44:11 +0000 (09:44 +0100)
Some muxers (eg, qtmux) only support raw AAC, so this allows linking
an encoder that outputs ADTS only to those muxers.

The conversion is simple (omit the first 7 or 9 bytes of the frame),
but has to be done in pre_push instead of handle_frame as 1.0 does
not seem to allow skipping bytes there as 0.10 used to.

Other conversions are not supported (yet).

gst/audioparsers/Makefile.am
gst/audioparsers/gstaacparse.c
gst/audioparsers/gstaacparse.h

index 1263c93..b2b2a4d 100644 (file)
@@ -8,6 +8,7 @@ libgstaudioparsers_la_SOURCES = \
 libgstaudioparsers_la_CFLAGS = \
        $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS)
 libgstaudioparsers_la_LIBADD = \
+       -lgstpbutils-$(GST_API_VERSION) \
        $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_API_VERSION) \
        -lgstaudio-$(GST_API_VERSION) \
        $(GST_BASE_LIBS) $(GST_LIBS)
index 8291917..784a2fa 100644 (file)
@@ -45,6 +45,7 @@
 #include <string.h>
 
 #include <gst/base/gstbitreader.h>
+#include <gst/pbutils/codec-utils.h>
 #include "gstaacparse.h"
 
 
@@ -91,22 +92,11 @@ static GstCaps *gst_aac_parse_sink_getcaps (GstBaseParse * parse,
 
 static GstFlowReturn gst_aac_parse_handle_frame (GstBaseParse * parse,
     GstBaseParseFrame * frame, gint * skipsize);
+static GstFlowReturn gst_aac_parse_pre_push_frame (GstBaseParse * parse,
+    GstBaseParseFrame * frame);
 
 G_DEFINE_TYPE (GstAacParse, gst_aac_parse, GST_TYPE_BASE_PARSE);
 
-static inline gint
-gst_aac_parse_get_sample_rate_from_index (guint sr_idx)
-{
-  static const guint aac_sample_rates[] = { 96000, 88200, 64000, 48000, 44100,
-    32000, 24000, 22050, 16000, 12000, 11025, 8000
-  };
-
-  if (sr_idx < G_N_ELEMENTS (aac_sample_rates))
-    return aac_sample_rates[sr_idx];
-  GST_WARNING ("Invalid sample rate index %u", sr_idx);
-  return 0;
-}
-
 /**
  * gst_aac_parse_class_init:
  * @klass: #GstAacParseClass.
@@ -135,6 +125,8 @@ gst_aac_parse_class_init (GstAacParseClass * klass)
   parse_class->set_sink_caps = GST_DEBUG_FUNCPTR (gst_aac_parse_sink_setcaps);
   parse_class->get_sink_caps = GST_DEBUG_FUNCPTR (gst_aac_parse_sink_getcaps);
   parse_class->handle_frame = GST_DEBUG_FUNCPTR (gst_aac_parse_handle_frame);
+  parse_class->pre_push_frame =
+      GST_DEBUG_FUNCPTR (gst_aac_parse_pre_push_frame);
 }
 
 
@@ -165,9 +157,11 @@ static gboolean
 gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps)
 {
   GstStructure *s;
-  GstCaps *src_caps = NULL;
+  GstCaps *src_caps = NULL, *allowed;
   gboolean res = FALSE;
   const gchar *stream_format;
+  GstBuffer *codec_data;
+  guint16 codec_data_data;
 
   GST_DEBUG_OBJECT (aacparse, "sink caps: %" GST_PTR_FORMAT, sink_caps);
   if (sink_caps)
@@ -178,6 +172,7 @@ gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps)
   gst_caps_set_simple (src_caps, "framed", G_TYPE_BOOLEAN, TRUE,
       "mpegversion", G_TYPE_INT, aacparse->mpegversion, NULL);
 
+  aacparse->output_header_type = aacparse->header_type;
   switch (aacparse->header_type) {
     case DSPAAC_HEADER_NONE:
       stream_format = "raw";
@@ -203,11 +198,55 @@ gst_aac_parse_set_src_caps (GstAacParse * aacparse, GstCaps * sink_caps)
   if (stream_format)
     gst_structure_set (s, "stream-format", G_TYPE_STRING, stream_format, NULL);
 
+  allowed = gst_pad_get_allowed_caps (GST_BASE_PARSE (aacparse)->srcpad);
+  if (!gst_caps_can_intersect (src_caps, allowed)) {
+    GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad,
+        "Caps can not intersect");
+    if (aacparse->header_type == DSPAAC_HEADER_ADTS) {
+      GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad,
+          "Input is ADTS, trying raw");
+      gst_caps_set_simple (src_caps, "stream-format", G_TYPE_STRING, "raw",
+          NULL);
+      if (gst_caps_can_intersect (src_caps, allowed)) {
+        GstMapInfo map;
+        int idx;
+
+        idx =
+            gst_codec_utils_aac_get_index_from_sample_rate
+            (aacparse->sample_rate);
+        if (idx < 0)
+          goto not_a_known_rate;
+
+        GST_DEBUG_OBJECT (GST_BASE_PARSE (aacparse)->srcpad,
+            "Caps can intersect, we will drop the ADTS layer");
+        aacparse->output_header_type = DSPAAC_HEADER_NONE;
+
+        /* The codec_data data is according to AudioSpecificConfig,
+           ISO/IEC 14496-3, 1.6.2.1 */
+        codec_data = gst_buffer_new_and_alloc (2);
+        gst_buffer_map (codec_data, &map, GST_MAP_WRITE);
+        codec_data_data =
+            (aacparse->object_type << 11) |
+            (idx << 7) | (aacparse->channels << 3);
+        GST_WRITE_UINT16_BE (map.data, codec_data_data);
+        gst_buffer_unmap (codec_data, &map);
+        gst_caps_set_simple (src_caps, "codec_data", GST_TYPE_BUFFER,
+            codec_data, NULL);
+      }
+    }
+  }
+  gst_caps_unref (allowed);
+
   GST_DEBUG_OBJECT (aacparse, "setting src caps: %" GST_PTR_FORMAT, src_caps);
 
   res = gst_pad_set_caps (GST_BASE_PARSE (aacparse)->srcpad, src_caps);
   gst_caps_unref (src_caps);
   return res;
+
+not_a_known_rate:
+  gst_caps_unref (allowed);
+  gst_caps_unref (src_caps);
+  return FALSE;
 }
 
 
@@ -250,7 +289,8 @@ gst_aac_parse_sink_setcaps (GstBaseParse * parse, GstCaps * caps)
 
       sr_idx = ((map.data[0] & 0x07) << 1) | ((map.data[1] & 0x80) >> 7);
       aacparse->object_type = (map.data[0] & 0xf8) >> 3;
-      aacparse->sample_rate = gst_aac_parse_get_sample_rate_from_index (sr_idx);
+      aacparse->sample_rate =
+          gst_codec_utils_aac_get_sample_rate_from_index (sr_idx);
       aacparse->channels = (map.data[1] & 0x78) >> 3;
       aacparse->header_type = DSPAAC_HEADER_NONE;
       aacparse->mpegversion = 4;
@@ -669,7 +709,7 @@ gst_aac_parse_parse_adts_header (GstAacParse * aacparse, const guint8 * data,
   if (rate) {
     gint sr_idx = (data[2] & 0x3c) >> 2;
 
-    *rate = gst_aac_parse_get_sample_rate_from_index (sr_idx);
+    *rate = gst_codec_utils_aac_get_sample_rate_from_index (sr_idx);
   }
   if (channels)
     *channels = ((data[2] & 0x01) << 2) | ((data[3] & 0xc0) >> 6);
@@ -860,7 +900,8 @@ gst_aac_parse_detect_stream (GstAacParse * aacparse,
 
     /* FIXME: This gives totally wrong results. Duration calculation cannot
        be based on this */
-    aacparse->sample_rate = gst_aac_parse_get_sample_rate_from_index (sr_idx);
+    aacparse->sample_rate =
+        gst_codec_utils_aac_get_sample_rate_from_index (sr_idx);
 
     /* baseparse is not given any fps,
      * so it will give up on timestamps, seeking, etc */
@@ -1056,6 +1097,26 @@ exit:
   return GST_FLOW_OK;
 }
 
+static GstFlowReturn
+gst_aac_parse_pre_push_frame (GstBaseParse * parse, GstBaseParseFrame * frame)
+{
+  GstAacParse *aacparse = GST_AAC_PARSE (parse);
+
+  /* As a special case, we can remove the ADTS framing and output raw AAC. */
+  if (aacparse->header_type == DSPAAC_HEADER_ADTS
+      && aacparse->output_header_type == DSPAAC_HEADER_NONE) {
+    guint header_size;
+    GstMapInfo map;
+    gst_buffer_map (frame->buffer, &map, GST_MAP_READ);
+    header_size = (map.data[1] & 1) ? 7 : 9;    /* optional CRC */
+    gst_buffer_unmap (frame->buffer, &map);
+    gst_buffer_resize (frame->buffer, header_size,
+        gst_buffer_get_size (frame->buffer) - header_size);
+  }
+
+  return GST_FLOW_OK;
+}
+
 
 /**
  * gst_aac_parse_start:
index 8eca9aa..51a2ed1 100644 (file)
@@ -80,6 +80,7 @@ struct _GstAacParse {
   gint           frame_samples;
 
   GstAacHeaderType header_type;
+  GstAacHeaderType output_header_type;
 };
 
 /**