From b9d0c7110f5a47a206db45fd8ce4657a47d9ccb4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= <sebastian.droege@collabora.co.uk>
Date: Tue, 11 Dec 2012 16:54:09 +0000
Subject: [PATCH] avcodecmap: Take generic samplerate limitations into account

---
 ext/libav/gstavcodecmap.c | 176 +++++++++++++++++++++++++-------------
 1 file changed, 116 insertions(+), 60 deletions(-)

diff --git a/ext/libav/gstavcodecmap.c b/ext/libav/gstavcodecmap.c
index 87d484a948..cb1d3d141a 100644
--- a/ext/libav/gstavcodecmap.c
+++ b/ext/libav/gstavcodecmap.c
@@ -282,8 +282,9 @@ gst_ff_vid_caps_new (AVCodecContext * context, enum CodecID codec_id,
 /* same for audio - now with channels/sample rate
  */
 static GstCaps *
-gst_ff_aud_caps_new (AVCodecContext * context, enum CodecID codec_id,
-    gboolean encode, const char *mimetype, const char *fieldname, ...)
+gst_ff_aud_caps_new (AVCodecContext * context, AVCodec * codec,
+    enum CodecID codec_id, gboolean encode, const char *mimetype,
+    const char *fieldname, ...)
 {
   GstCaps *caps = NULL;
   GstStructure *structure = NULL;
@@ -424,8 +425,32 @@ gst_ff_aud_caps_new (AVCodecContext * context, enum CodecID codec_id,
       structure = gst_caps_get_structure (caps, 0);
       gst_structure_set_value (structure, "rate", &list);
       g_value_unset (&list);
-    } else
+    } else if (codec && codec->supported_samplerates
+        && codec->supported_samplerates[0]) {
+      GValue va = { 0, };
+      GValue v = { 0, };
+
+      if (!codec->supported_samplerates[1]) {
+        gst_caps_set_simple (caps, "rate", G_TYPE_INT,
+            codec->supported_samplerates[0], NULL);
+      } else {
+        const int *rates = codec->supported_samplerates;
+
+        g_value_init (&va, GST_TYPE_LIST);
+        g_value_init (&v, G_TYPE_INT);
+
+        while (*rates) {
+          g_value_set_int (&v, *rates);
+          gst_value_list_append_value (&va, &v);
+          rates++;
+        }
+        gst_caps_set_value (caps, "rate", &va);
+        g_value_unset (&va);
+        g_value_unset (&v);
+      }
+    } else {
       gst_caps_set_simple (caps, "rate", GST_TYPE_INT_RANGE, 4000, 96000, NULL);
+    }
   } else {
     caps = gst_caps_new_empty_simple (mimetype);
   }
@@ -573,20 +598,21 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
 
     case CODEC_ID_MP1:
       /* FIXME: bitrate */
-      caps = gst_ff_aud_caps_new (context, codec_id, encode, "audio/mpeg",
+      caps = gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/mpeg",
           "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 1, NULL);
       break;
 
     case CODEC_ID_MP2:
       /* FIXME: bitrate */
-      caps = gst_ff_aud_caps_new (context, codec_id, encode, "audio/mpeg",
+      caps = gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/mpeg",
           "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 2, NULL);
       break;
 
     case CODEC_ID_MP3:
       if (encode) {
         /* FIXME: bitrate */
-        caps = gst_ff_aud_caps_new (context, codec_id, encode, "audio/mpeg",
+        caps =
+            gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/mpeg",
             "mpegversion", G_TYPE_INT, 1, "layer", G_TYPE_INT, 3, NULL);
       } else {
         /* Decodes MPEG-1 layer 1/2/3. Samplerate, channels et al are
@@ -599,14 +625,14 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
 
     case CODEC_ID_MUSEPACK7:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode,
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode,
           "audio/x-ffmpeg-parsed-musepack", "streamversion", G_TYPE_INT, 7,
           NULL);
       break;
 
     case CODEC_ID_MUSEPACK8:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode,
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode,
           "audio/x-ffmpeg-parsed-musepack", "streamversion", G_TYPE_INT, 8,
           NULL);
       break;
@@ -614,41 +640,44 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
     case CODEC_ID_AC3:
       /* FIXME: bitrate */
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-ac3", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-ac3",
+          NULL);
       break;
 
     case CODEC_ID_EAC3:
       /* FIXME: bitrate */
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-eac3", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-eac3",
+          NULL);
       break;
 
     case CODEC_ID_TRUEHD:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-true-hd",
-          NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode,
+          "audio/x-true-hd", NULL);
       break;
 
     case CODEC_ID_ATRAC1:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode,
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode,
           "audio/x-vnd.sony.atrac1", NULL);
       break;
 
     case CODEC_ID_ATRAC3:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode,
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode,
           "audio/x-vnd.sony.atrac3", NULL);
       break;
 
     case CODEC_ID_DTS:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-dts", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-dts",
+          NULL);
       break;
 
     case CODEC_ID_APE:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode,
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode,
           "audio/x-ffmpeg-parsed-ape", NULL);
       if (context) {
         gst_caps_set_simple (caps,
@@ -658,12 +687,14 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
 
     case CODEC_ID_MLP:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-mlp", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-mlp",
+          NULL);
       break;
 
     case CODEC_ID_IMC:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-imc", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-imc",
+          NULL);
       break;
 
       /* MJPEG is normal JPEG, Motion-JPEG and Quicktime MJPEG-A. MJPEGB
@@ -776,7 +807,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
 
     case CODEC_ID_DVAUDIO:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-dv", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-dv",
+          NULL);
       break;
 
     case CODEC_ID_DVVIDEO:
@@ -826,21 +858,24 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
       gint version = (codec_id == CODEC_ID_WMAV1) ? 1 : 2;
 
       if (context) {
-        caps = gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-wma",
-            "wmaversion", G_TYPE_INT, version,
-            "block_align", G_TYPE_INT, context->block_align,
-            "bitrate", G_TYPE_INT, context->bit_rate, NULL);
+        caps =
+            gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-wma",
+            "wmaversion", G_TYPE_INT, version, "block_align", G_TYPE_INT,
+            context->block_align, "bitrate", G_TYPE_INT, context->bit_rate,
+            NULL);
       } else {
-        caps = gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-wma",
-            "wmaversion", G_TYPE_INT, version,
-            "block_align", GST_TYPE_INT_RANGE, 0, G_MAXINT,
-            "bitrate", GST_TYPE_INT_RANGE, 0, G_MAXINT, NULL);
+        caps =
+            gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-wma",
+            "wmaversion", G_TYPE_INT, version, "block_align",
+            GST_TYPE_INT_RANGE, 0, G_MAXINT, "bitrate", GST_TYPE_INT_RANGE, 0,
+            G_MAXINT, NULL);
       }
     }
       break;
     case CODEC_ID_WMAPRO:
     {
-      caps = gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-wma",
+      caps =
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-wma",
           "wmaversion", G_TYPE_INT, 3, NULL);
       break;
     }
@@ -848,7 +883,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
     case CODEC_ID_WMAVOICE:
     {
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-wms", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-wms",
+          NULL);
       break;
     }
 
@@ -857,7 +893,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
     {
       gint version = (codec_id == CODEC_ID_MACE3) ? 3 : 6;
 
-      caps = gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-mace",
+      caps =
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-mace",
           "maceversion", G_TYPE_INT, version, NULL);
     }
       break;
@@ -951,7 +988,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
     case CODEC_ID_AAC:
     {
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/mpeg", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/mpeg",
+          NULL);
 
       if (!encode) {
         GValue arr = { 0, };
@@ -993,7 +1031,7 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
       break;
     }
     case CODEC_ID_AAC_LATM:    /* LATM/LOAS AAC syntax */
-      caps = gst_ff_aud_caps_new (context, codec_id, encode, "audio/mpeg",
+      caps = gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/mpeg",
           "mpegversion", G_TYPE_INT, 4, "stream-format", G_TYPE_STRING, "loas",
           NULL);
       break;
@@ -1098,7 +1136,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
       break;
     case CODEC_ID_QDM2:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-qdm2", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-qdm2",
+          NULL);
       break;
 
     case CODEC_ID_MSZH:
@@ -1239,13 +1278,14 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
 
     case CODEC_ID_TRUESPEECH:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-truespeech",
-          NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode,
+          "audio/x-truespeech", NULL);
       break;
 
     case CODEC_ID_QCELP:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/qcelp", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/qcelp",
+          NULL);
       break;
 
     case CODEC_ID_AMV:
@@ -1341,7 +1381,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
           break;
       }
 
-      caps = gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-raw",
+      caps =
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-raw",
           "format", G_TYPE_STRING, gst_audio_format_to_string (format),
           "layout", G_TYPE_STRING, "interleaved", NULL);
     }
@@ -1349,18 +1390,20 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
 
     case CODEC_ID_PCM_MULAW:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-mulaw",
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-mulaw",
           NULL);
       break;
 
     case CODEC_ID_PCM_ALAW:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-alaw", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-alaw",
+          NULL);
       break;
 
     case CODEC_ID_ADPCM_G722:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/G722", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/G722",
+          NULL);
       if (context)
         gst_caps_set_simple (caps,
             "block_align", G_TYPE_INT, context->block_align,
@@ -1370,7 +1413,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
     case CODEC_ID_ADPCM_G726:
     {
       /* the G726 decoder can also handle G721 */
-      caps = gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-adpcm",
+      caps =
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-adpcm",
           "layout", G_TYPE_STRING, "g726", NULL);
       if (context)
         gst_caps_set_simple (caps,
@@ -1503,7 +1547,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
 
       /* FIXME: someone please check whether we need additional properties
        * in this caps definition. */
-      caps = gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-adpcm",
+      caps =
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-adpcm",
           "layout", G_TYPE_STRING, layout, NULL);
       if (context)
         gst_caps_set_simple (caps,
@@ -1513,34 +1558,39 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
       break;
 
     case CODEC_ID_AMR_NB:
-      caps = gst_ff_aud_caps_new (context, codec_id, encode, "audio/AMR", NULL);
+      caps =
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/AMR",
+          NULL);
       break;
 
     case CODEC_ID_AMR_WB:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/AMR-WB", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/AMR-WB",
+          NULL);
       break;
 
     case CODEC_ID_GSM:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-gsm", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-gsm",
+          NULL);
       break;
 
     case CODEC_ID_GSM_MS:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/ms-gsm", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/ms-gsm",
+          NULL);
       break;
 
     case CODEC_ID_NELLYMOSER:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-nellymoser",
-          NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode,
+          "audio/x-nellymoser", NULL);
       break;
 
     case CODEC_ID_SIPR:
     {
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-sipro",
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-sipro",
           NULL);
       if (context) {
         gst_caps_set_simple (caps,
@@ -1572,7 +1622,7 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
 
       /* FIXME: properties? */
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode,
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode,
           "audio/x-pn-realaudio", "raversion", G_TYPE_INT, version, NULL);
       if (context) {
         gst_caps_set_simple (caps,
@@ -1609,7 +1659,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
 
       /* FIXME: someone please check whether we need additional properties
        * in this caps definition. */
-      caps = gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-dpcm",
+      caps =
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-dpcm",
           "layout", G_TYPE_STRING, layout, NULL);
       if (context)
         gst_caps_set_simple (caps,
@@ -1624,7 +1675,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
 
     case CODEC_ID_ALAC:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-alac", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-alac",
+          NULL);
       if (context) {
         gst_caps_set_simple (caps,
             "samplesize", G_TYPE_INT, context->bits_per_coded_sample, NULL);
@@ -1648,7 +1700,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
       break;
     case CODEC_ID_TTA:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-tta", NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode, "audio/x-tta",
+          NULL);
       if (context) {
         gst_caps_set_simple (caps,
             "samplesize", G_TYPE_INT, context->bits_per_coded_sample, NULL);
@@ -1656,8 +1709,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
       break;
     case CODEC_ID_TWINVQ:
       caps =
-          gst_ff_aud_caps_new (context, codec_id, encode, "audio/x-twin-vq",
-          NULL);
+          gst_ff_aud_caps_new (context, NULL, codec_id, encode,
+          "audio/x-twin-vq", NULL);
       break;
     default:
       GST_DEBUG ("Unknown codec ID %d, please add mapping here", codec_id);
@@ -1681,7 +1734,8 @@ gst_ffmpeg_codecid_to_caps (enum CodecID codec_id,
           break;
         case AVMEDIA_TYPE_AUDIO:
           mime = g_strdup_printf ("audio/x-gst-av-%s", codec->name);
-          caps = gst_ff_aud_caps_new (context, codec_id, encode, mime, NULL);
+          caps =
+              gst_ff_aud_caps_new (context, NULL, codec_id, encode, mime, NULL);
           if (context)
             gst_caps_set_simple (caps,
                 "block_align", G_TYPE_INT, context->block_align,
@@ -1784,7 +1838,7 @@ gst_ffmpeg_smpfmt_to_audioformat (enum AVSampleFormat sample_fmt)
 
 static GstCaps *
 gst_ffmpeg_smpfmt_to_caps (enum AVSampleFormat sample_fmt,
-    AVCodecContext * context, enum CodecID codec_id)
+    AVCodecContext * context, AVCodec * codec, enum CodecID codec_id)
 {
   GstCaps *caps = NULL;
   GstAudioFormat format;
@@ -1792,7 +1846,7 @@ gst_ffmpeg_smpfmt_to_caps (enum AVSampleFormat sample_fmt,
   format = gst_ffmpeg_smpfmt_to_audioformat (sample_fmt);
 
   if (format != GST_AUDIO_FORMAT_UNKNOWN) {
-    caps = gst_ff_aud_caps_new (context, codec_id, TRUE, "audio/x-raw",
+    caps = gst_ff_aud_caps_new (context, codec, codec_id, TRUE, "audio/x-raw",
         "format", G_TYPE_STRING, gst_audio_format_to_string (format),
         "layout", G_TYPE_STRING, "interleaved", NULL);
     GST_LOG ("caps for sample_fmt=%d: %" GST_PTR_FORMAT, sample_fmt, caps);
@@ -1862,9 +1916,11 @@ gst_ffmpeg_codectype_to_audio_caps (AVCodecContext * context,
 
   if (context) {
     /* Specific codec context */
-    caps = gst_ffmpeg_smpfmt_to_caps (context->sample_fmt, context, codec_id);
+    caps =
+        gst_ffmpeg_smpfmt_to_caps (context->sample_fmt, context, codec,
+        codec_id);
   } else {
-    caps = gst_ff_aud_caps_new (context, codec_id, TRUE, "audio/x-raw",
+    caps = gst_ff_aud_caps_new (context, codec, codec_id, TRUE, "audio/x-raw",
         "layout", G_TYPE_STRING, "interleaved", NULL);
 
     gst_ffmpeg_audio_set_sample_fmts (caps, codec ? codec->sample_fmts : NULL);