• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 /**
21  * SECTION:element-mfaacenc
22  * @title: mfaacenc
23  *
24  * This element encodes raw audio into AAC compressed data.
25  *
26  * ## Example pipelines
27  * |[
28  * gst-launch-1.0 -v audiotestsrc ! mfaacenc ! aacparse ! qtmux ! filesink location=audiotestsrc.mp4
29  * ]| This example pipeline will encode a test audio source to AAC using
30  * Media Foundation encoder, and muxes it in a mp4 container.
31  */
32 
33 #ifdef HAVE_CONFIG_H
34 #include "config.h"
35 #endif
36 
37 #include <gst/gst.h>
38 #include <gst/pbutils/pbutils.h>
39 #include "gstmfaudioenc.h"
40 #include "gstmfaacenc.h"
41 #include <wrl.h>
42 #include <set>
43 #include <vector>
44 #include <string>
45 
46 /* *INDENT-OFF* */
47 using namespace Microsoft::WRL;
48 /* *INDENT-ON* */
49 
50 GST_DEBUG_CATEGORY (gst_mf_aac_enc_debug);
51 #define GST_CAT_DEFAULT gst_mf_aac_enc_debug
52 
53 enum
54 {
55   PROP_0,
56   PROP_BITRATE,
57 };
58 
59 #define DEFAULT_BITRATE (0)
60 
61 typedef struct _GstMFAacEnc
62 {
63   GstMFAudioEnc parent;
64 
65   /* properties */
66   guint bitrate;
67 } GstMFAacEnc;
68 
69 typedef struct _GstMFAacEncClass
70 {
71   GstMFAudioEncClass parent_class;
72 
73 } GstMFAacEncClass;
74 
75 /* *INDENT-OFF* */
76 typedef struct
77 {
78   GstCaps *sink_caps;
79   GstCaps *src_caps;
80   gchar *device_name;
81   guint32 enum_flags;
82   guint device_index;
83   std::set<UINT32> bitrate_list;
84 } GstMFAacEncClassData;
85 /* *INDENT-ON* */
86 
87 static GstElementClass *parent_class = NULL;
88 
89 static void gst_mf_aac_enc_get_property (GObject * object, guint prop_id,
90     GValue * value, GParamSpec * pspec);
91 static void gst_mf_aac_enc_set_property (GObject * object, guint prop_id,
92     const GValue * value, GParamSpec * pspec);
93 static gboolean gst_mf_aac_enc_get_output_type (GstMFAudioEnc * mfenc,
94     GstAudioInfo * info, IMFMediaType ** output_type);
95 static gboolean gst_mf_aac_enc_get_input_type (GstMFAudioEnc * mfenc,
96     GstAudioInfo * info, IMFMediaType ** input_type);
97 static gboolean gst_mf_aac_enc_set_src_caps (GstMFAudioEnc * mfenc,
98     GstAudioInfo * info);
99 
100 static void
gst_mf_aac_enc_class_init(GstMFAacEncClass * klass,gpointer data)101 gst_mf_aac_enc_class_init (GstMFAacEncClass * klass, gpointer data)
102 {
103   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
104   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
105   GstMFAudioEncClass *mfenc_class = GST_MF_AUDIO_ENC_CLASS (klass);
106   GstMFAacEncClassData *cdata = (GstMFAacEncClassData *) data;
107   gchar *long_name;
108   gchar *classification;
109   guint max_bitrate = 0;
110   std::string bitrate_blurb;
111 
112   parent_class = (GstElementClass *) g_type_class_peek_parent (klass);
113 
114   gobject_class->get_property = gst_mf_aac_enc_get_property;
115   gobject_class->set_property = gst_mf_aac_enc_set_property;
116 
117   bitrate_blurb = "Bitrate in bit/sec, (0 = auto), valid values are { 0";
118 
119   /* *INDENT-OFF* */
120   for (auto iter: cdata->bitrate_list) {
121     bitrate_blurb += ", " + std::to_string (iter);
122     /* std::set<> stores values in a sorted fashion */
123     max_bitrate = iter;
124   }
125   bitrate_blurb += " }";
126   /* *INDENT-ON* */
127 
128   g_object_class_install_property (gobject_class, PROP_BITRATE,
129       g_param_spec_uint ("bitrate", "Bitrate", bitrate_blurb.c_str (), 0,
130           max_bitrate, DEFAULT_BITRATE,
131           (GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
132               G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK)));
133 
134   long_name = g_strdup_printf ("Media Foundation %s", cdata->device_name);
135   classification = g_strdup_printf ("Codec/Encoder/Audio%s",
136       (cdata->enum_flags & MFT_ENUM_FLAG_HARDWARE) == MFT_ENUM_FLAG_HARDWARE ?
137       "/Hardware" : "");
138   gst_element_class_set_metadata (element_class, long_name,
139       classification,
140       "Microsoft Media Foundation AAC Encoder",
141       "Seungha Yang <seungha@centricular.com>");
142   g_free (long_name);
143   g_free (classification);
144 
145   gst_element_class_add_pad_template (element_class,
146       gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
147           cdata->sink_caps));
148   gst_element_class_add_pad_template (element_class,
149       gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
150           cdata->src_caps));
151 
152   mfenc_class->get_output_type =
153       GST_DEBUG_FUNCPTR (gst_mf_aac_enc_get_output_type);
154   mfenc_class->get_input_type =
155       GST_DEBUG_FUNCPTR (gst_mf_aac_enc_get_input_type);
156   mfenc_class->set_src_caps = GST_DEBUG_FUNCPTR (gst_mf_aac_enc_set_src_caps);
157 
158   mfenc_class->codec_id = MFAudioFormat_AAC;
159   mfenc_class->enum_flags = cdata->enum_flags;
160   mfenc_class->device_index = cdata->device_index;
161   mfenc_class->frame_samples = 1024;
162 
163   g_free (cdata->device_name);
164   gst_caps_unref (cdata->sink_caps);
165   gst_caps_unref (cdata->src_caps);
166   delete cdata;
167 }
168 
169 static void
gst_mf_aac_enc_init(GstMFAacEnc * self)170 gst_mf_aac_enc_init (GstMFAacEnc * self)
171 {
172   self->bitrate = DEFAULT_BITRATE;
173 }
174 
175 static void
gst_mf_aac_enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)176 gst_mf_aac_enc_get_property (GObject * object, guint prop_id,
177     GValue * value, GParamSpec * pspec)
178 {
179   GstMFAacEnc *self = (GstMFAacEnc *) (object);
180 
181   switch (prop_id) {
182     case PROP_BITRATE:
183       g_value_set_uint (value, self->bitrate);
184       break;
185     default:
186       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
187       break;
188   }
189 }
190 
191 static void
gst_mf_aac_enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)192 gst_mf_aac_enc_set_property (GObject * object, guint prop_id,
193     const GValue * value, GParamSpec * pspec)
194 {
195   GstMFAacEnc *self = (GstMFAacEnc *) (object);
196 
197   switch (prop_id) {
198     case PROP_BITRATE:
199       self->bitrate = g_value_get_uint (value);
200       break;
201     default:
202       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
203       break;
204   }
205 }
206 
207 static gboolean
gst_mf_aac_enc_get_output_type(GstMFAudioEnc * mfenc,GstAudioInfo * info,IMFMediaType ** output_type)208 gst_mf_aac_enc_get_output_type (GstMFAudioEnc * mfenc, GstAudioInfo * info,
209     IMFMediaType ** output_type)
210 {
211   GstMFAacEnc *self = (GstMFAacEnc *) mfenc;
212   GstMFTransform *transform = mfenc->transform;
213   GList *output_list = NULL;
214   GList *iter;
215   ComPtr < IMFMediaType > target_output;
216   std::vector < ComPtr < IMFMediaType >> filtered_types;
217   std::set < UINT32 > bitrate_list;
218   UINT32 bitrate;
219   UINT32 target_bitrate = 0;
220   HRESULT hr;
221 
222   if (!gst_mf_transform_get_output_available_types (transform, &output_list)) {
223     GST_ERROR_OBJECT (self, "Couldn't get available output type");
224     return FALSE;
225   }
226 
227   /* 1. Filtering based on channels and sample rate */
228   for (iter = output_list; iter; iter = g_list_next (iter)) {
229     IMFMediaType *type = (IMFMediaType *) iter->data;
230     GUID guid = GUID_NULL;
231     UINT32 value;
232 
233     hr = type->GetGUID (MF_MT_MAJOR_TYPE, &guid);
234     if (!gst_mf_result (hr))
235       continue;
236 
237     if (!IsEqualGUID (guid, MFMediaType_Audio)) {
238       GST_WARNING_OBJECT (self, "Major type is not audio");
239       continue;
240     }
241 
242     hr = type->GetGUID (MF_MT_SUBTYPE, &guid);
243     if (!gst_mf_result (hr))
244       continue;
245 
246     if (!IsEqualGUID (guid, MFAudioFormat_AAC)) {
247       GST_WARNING_OBJECT (self, "Sub type is not AAC");
248       continue;
249     }
250 
251     hr = type->GetUINT32 (MF_MT_AUDIO_NUM_CHANNELS, &value);
252     if (!gst_mf_result (hr))
253       continue;
254 
255     if (value != GST_AUDIO_INFO_CHANNELS (info))
256       continue;
257 
258     hr = type->GetUINT32 (MF_MT_AUDIO_SAMPLES_PER_SECOND, &value);
259     if (!gst_mf_result (hr))
260       continue;
261 
262     if (value != GST_AUDIO_INFO_RATE (info))
263       continue;
264 
265     hr = type->GetUINT32 (MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &value);
266     if (!gst_mf_result (hr))
267       continue;
268 
269     filtered_types.push_back (type);
270     /* convert bytes to bit */
271     bitrate_list.insert (value * 8);
272   }
273 
274   g_list_free_full (output_list, (GDestroyNotify) gst_mf_media_type_release);
275 
276   if (filtered_types.empty ()) {
277     GST_ERROR_OBJECT (self, "Couldn't find target output type");
278     return FALSE;
279   }
280 
281   GST_DEBUG_OBJECT (self, "have %d candidate output", filtered_types.size ());
282 
283   /* 2. Find the best matching bitrate */
284   bitrate = self->bitrate;
285 
286   /* Media Foundation AAC encoder supports sample-rate 44100 or 48000 */
287   if (bitrate == 0) {
288     /* http://wiki.hydrogenaud.io/index.php?title=Fraunhofer_FDK_AAC#Recommended_Sampling_Rate_and_Bitrate_Combinations
289      * was referenced but the supported range by MediaFoudation is much limited
290      * than it */
291     if (GST_AUDIO_INFO_CHANNELS (info) == 1) {
292       if (GST_AUDIO_INFO_RATE (info) <= 44100) {
293         bitrate = 96000;
294       } else {
295         bitrate = 160000;
296       }
297     } else if (GST_AUDIO_INFO_CHANNELS (info) == 2) {
298       if (GST_AUDIO_INFO_RATE (info) <= 44100) {
299         bitrate = 112000;
300       } else {
301         bitrate = 320000;
302       }
303     } else {
304       /* 5.1 */
305       if (GST_AUDIO_INFO_RATE (info) <= 44100) {
306         bitrate = 240000;
307       } else {
308         bitrate = 320000;
309       }
310     }
311 
312     GST_DEBUG_OBJECT (self, "Calculated bitrate %d", bitrate);
313   } else {
314     GST_DEBUG_OBJECT (self, "Requested bitrate %d", bitrate);
315   }
316 
317   GST_DEBUG_OBJECT (self, "Available bitrates");
318 
319   /* *INDENT-OFF* */
320   for (auto it: bitrate_list)
321     GST_DEBUG_OBJECT (self, "\t%d", it);
322 
323   /* Based on calculated or requested bitrate, find the closest supported
324    * bitrate */
325   {
326     const auto it = bitrate_list.lower_bound (bitrate);
327     if (it == bitrate_list.end()) {
328       target_bitrate = *std::prev (it);
329     } else {
330       target_bitrate = *it;
331     }
332   }
333 
334   GST_DEBUG_OBJECT (self, "Selected target bitrate %d", target_bitrate);
335 
336   for (auto it: filtered_types) {
337     UINT32 value = 0;
338 
339     it->GetUINT32 (MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &value);
340     if (value * 8 == target_bitrate) {
341       target_output = it;
342       break;
343     }
344   }
345   /* *INDENT-ON* */
346 
347   if (!target_output) {
348     GST_ERROR_OBJECT (self, "Failed to decide final output type");
349     return FALSE;
350   }
351 
352   *output_type = target_output.Detach ();
353 
354   return TRUE;
355 }
356 
357 static gboolean
gst_mf_aac_enc_get_input_type(GstMFAudioEnc * mfenc,GstAudioInfo * info,IMFMediaType ** input_type)358 gst_mf_aac_enc_get_input_type (GstMFAudioEnc * mfenc, GstAudioInfo * info,
359     IMFMediaType ** input_type)
360 {
361   GstMFAacEnc *self = (GstMFAacEnc *) mfenc;
362   GstMFTransform *transform = mfenc->transform;
363   GList *input_list = NULL;
364   GList *iter;
365   ComPtr < IMFMediaType > target_input;
366   std::vector < ComPtr < IMFMediaType >> filtered_types;
367   std::set < UINT32 > bitrate_list;
368   HRESULT hr;
369 
370   if (!gst_mf_transform_get_input_available_types (transform, &input_list)) {
371     GST_ERROR_OBJECT (self, "Couldn't get available output type");
372     return FALSE;
373   }
374 
375   /* 1. Filtering based on channels and sample rate */
376   for (iter = input_list; iter; iter = g_list_next (iter)) {
377     IMFMediaType *type = (IMFMediaType *) iter->data;
378     GUID guid = GUID_NULL;
379     UINT32 value;
380 
381     hr = type->GetGUID (MF_MT_MAJOR_TYPE, &guid);
382     if (!gst_mf_result (hr))
383       continue;
384 
385     if (!IsEqualGUID (guid, MFMediaType_Audio)) {
386       GST_WARNING_OBJECT (self, "Major type is not audio");
387       continue;
388     }
389 
390     hr = type->GetGUID (MF_MT_SUBTYPE, &guid);
391     if (!gst_mf_result (hr))
392       continue;
393 
394     if (!IsEqualGUID (guid, MFAudioFormat_PCM)) {
395       GST_WARNING_OBJECT (self, "Sub type is not PCM");
396       continue;
397     }
398 
399     hr = type->GetUINT32 (MF_MT_AUDIO_NUM_CHANNELS, &value);
400     if (!gst_mf_result (hr))
401       continue;
402 
403     if (value != GST_AUDIO_INFO_CHANNELS (info))
404       continue;
405 
406     hr = type->GetUINT32 (MF_MT_AUDIO_SAMPLES_PER_SECOND, &value);
407     if (!gst_mf_result (hr))
408       continue;
409 
410     if (value != GST_AUDIO_INFO_RATE (info))
411       continue;
412 
413     filtered_types.push_back (type);
414   }
415 
416   g_list_free_full (input_list, (GDestroyNotify) gst_mf_media_type_release);
417 
418   if (filtered_types.empty ()) {
419     GST_ERROR_OBJECT (self, "Couldn't find target input type");
420     return FALSE;
421   }
422 
423   GST_DEBUG_OBJECT (self, "Total %d input types are available",
424       filtered_types.size ());
425 
426   /* Just select the first one */
427   target_input = *filtered_types.begin ();
428 
429   *input_type = target_input.Detach ();
430 
431   return TRUE;
432 }
433 
434 static gboolean
gst_mf_aac_enc_set_src_caps(GstMFAudioEnc * mfenc,GstAudioInfo * info)435 gst_mf_aac_enc_set_src_caps (GstMFAudioEnc * mfenc, GstAudioInfo * info)
436 {
437   GstMFAacEnc *self = (GstMFAacEnc *) mfenc;
438   HRESULT hr;
439   GstCaps *src_caps;
440   GstBuffer *codec_data;
441   UINT8 *blob = NULL;
442   UINT32 blob_size = 0;
443   gboolean ret;
444   ComPtr < IMFMediaType > output_type;
445   static const guint config_data_offset = 12;
446 
447   if (!gst_mf_transform_get_output_current_type (mfenc->transform,
448           &output_type)) {
449     GST_ERROR_OBJECT (self, "Couldn't get current output type");
450     return FALSE;
451   }
452 
453   /* user data contains the portion of the HEAACWAVEINFO structure that appears
454    * after the WAVEFORMATEX structure (that is, after the wfx member).
455    * This is followed by the AudioSpecificConfig() data,
456    * as defined by ISO/IEC 14496-3.
457    * https://docs.microsoft.com/en-us/windows/win32/medfound/aac-encoder
458    *
459    * The offset AudioSpecificConfig() data is 12 in this case
460    */
461   hr = output_type->GetBlobSize (MF_MT_USER_DATA, &blob_size);
462   if (!gst_mf_result (hr) || blob_size <= config_data_offset) {
463     GST_ERROR_OBJECT (self,
464         "Couldn't get size of MF_MT_USER_DATA, size %d, %d", blob_size);
465     return FALSE;
466   }
467 
468   hr = output_type->GetAllocatedBlob (MF_MT_USER_DATA, &blob, &blob_size);
469   if (!gst_mf_result (hr)) {
470     GST_ERROR_OBJECT (self, "Couldn't get user data blob");
471     return FALSE;
472   }
473 
474   codec_data = gst_buffer_new_and_alloc (blob_size - config_data_offset);
475   gst_buffer_fill (codec_data, 0, blob + config_data_offset,
476       blob_size - config_data_offset);
477 
478   src_caps = gst_caps_new_simple ("audio/mpeg",
479       "mpegversion", G_TYPE_INT, 4,
480       "stream-format", G_TYPE_STRING, "raw",
481       "channels", G_TYPE_INT, GST_AUDIO_INFO_CHANNELS (info),
482       "rate", G_TYPE_INT, GST_AUDIO_INFO_RATE (info),
483       "framed", G_TYPE_BOOLEAN, TRUE,
484       "codec_data", GST_TYPE_BUFFER, codec_data, NULL);
485   gst_buffer_unref (codec_data);
486 
487   gst_codec_utils_aac_caps_set_level_and_profile (src_caps,
488       blob + config_data_offset, blob_size - config_data_offset);
489   CoTaskMemFree (blob);
490 
491   ret =
492       gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (self), src_caps);
493   if (!ret) {
494     GST_WARNING_OBJECT (self,
495         "Couldn't set output format %" GST_PTR_FORMAT, src_caps);
496   }
497   gst_caps_unref (src_caps);
498 
499   return ret;
500 }
501 
502 static void
gst_mf_aac_enc_register(GstPlugin * plugin,guint rank,const gchar * device_name,guint32 enum_flags,guint device_index,GstCaps * sink_caps,GstCaps * src_caps,const std::set<UINT32> & bitrate_list)503 gst_mf_aac_enc_register (GstPlugin * plugin, guint rank,
504     const gchar * device_name, guint32 enum_flags, guint device_index,
505     GstCaps * sink_caps, GstCaps * src_caps,
506     const std::set < UINT32 > &bitrate_list)
507 {
508   GType type;
509   gchar *type_name;
510   gchar *feature_name;
511   gint i;
512   GstMFAacEncClassData *cdata;
513   gboolean is_default = TRUE;
514   GTypeInfo type_info = {
515     sizeof (GstMFAacEncClass),
516     NULL,
517     NULL,
518     (GClassInitFunc) gst_mf_aac_enc_class_init,
519     NULL,
520     NULL,
521     sizeof (GstMFAacEnc),
522     0,
523     (GInstanceInitFunc) gst_mf_aac_enc_init,
524   };
525 
526   cdata = new GstMFAacEncClassData;
527   cdata->sink_caps = sink_caps;
528   cdata->src_caps = src_caps;
529   cdata->device_name = g_strdup (device_name);
530   cdata->enum_flags = enum_flags;
531   cdata->device_index = device_index;
532   cdata->bitrate_list = bitrate_list;
533   type_info.class_data = cdata;
534 
535   type_name = g_strdup ("GstMFAacEnc");
536   feature_name = g_strdup ("mfaacenc");
537 
538   i = 1;
539   while (g_type_from_name (type_name) != 0) {
540     g_free (type_name);
541     g_free (feature_name);
542     type_name = g_strdup_printf ("GstMFAacDevice%dEnc", i);
543     feature_name = g_strdup_printf ("mfaacdevice%denc", i);
544     is_default = FALSE;
545     i++;
546   }
547 
548   type =
549       g_type_register_static (GST_TYPE_MF_AUDIO_ENC, type_name, &type_info,
550       (GTypeFlags) 0);
551 
552   /* make lower rank than default device */
553   if (rank > 0 && !is_default)
554     rank--;
555 
556   if (!gst_element_register (plugin, feature_name, rank, type))
557     GST_WARNING ("Failed to register plugin '%s'", type_name);
558 
559   g_free (type_name);
560   g_free (feature_name);
561 }
562 
563 static void
gst_mf_aac_enc_plugin_init_internal(GstPlugin * plugin,guint rank,GstMFTransform * transform,guint device_index,guint32 enum_flags)564 gst_mf_aac_enc_plugin_init_internal (GstPlugin * plugin, guint rank,
565     GstMFTransform * transform, guint device_index, guint32 enum_flags)
566 {
567   HRESULT hr;
568   gint i;
569   GstCaps *src_caps = NULL;
570   GstCaps *sink_caps = NULL;
571   gchar *device_name = NULL;
572   GList *output_list = NULL;
573   GList *iter;
574   std::set < UINT32 > channels_list;
575   std::set < UINT32 > rate_list;
576   std::set < UINT32 > bitrate_list;
577   gboolean config_found = FALSE;
578   GValue channles_value = G_VALUE_INIT;
579   GValue rate_value = G_VALUE_INIT;
580 
581   if (!gst_mf_transform_open (transform))
582     return;
583 
584   g_object_get (transform, "device-name", &device_name, NULL);
585   if (!device_name) {
586     GST_WARNING_OBJECT (transform, "Unknown device name");
587     return;
588   }
589 
590   if (!gst_mf_transform_get_output_available_types (transform, &output_list)) {
591     GST_WARNING_OBJECT (transform, "Couldn't get output types");
592     goto done;
593   }
594 
595   GST_INFO_OBJECT (transform, "Have %d output type",
596       g_list_length (output_list));
597 
598   for (iter = output_list, i = 0; iter; iter = g_list_next (iter), i++) {
599     UINT32 channels, rate, bitrate;
600     GUID guid = GUID_NULL;
601     IMFMediaType *type = (IMFMediaType *) iter->data;
602 #ifndef GST_DISABLE_GST_DEBUG
603     gchar *msg = g_strdup_printf ("Output IMFMediaType %d", i);
604     gst_mf_dump_attributes ((IMFAttributes *) type, msg, GST_LEVEL_TRACE);
605     g_free (msg);
606 #endif
607 
608     hr = type->GetGUID (MF_MT_MAJOR_TYPE, &guid);
609     if (!gst_mf_result (hr))
610       continue;
611 
612     /* shouldn't happen */
613     if (!IsEqualGUID (guid, MFMediaType_Audio))
614       continue;
615 
616     hr = type->GetGUID (MF_MT_SUBTYPE, &guid);
617     if (!gst_mf_result (hr))
618       continue;
619 
620     /* shouldn't happen */
621     if (!IsEqualGUID (guid, MFAudioFormat_AAC))
622       continue;
623 
624     /* Windows 10 channels 6 (5.1) channels so we cannot hard code it */
625     hr = type->GetUINT32 (MF_MT_AUDIO_NUM_CHANNELS, &channels);
626     if (!gst_mf_result (hr))
627       continue;
628 
629     hr = type->GetUINT32 (MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate);
630     if (!gst_mf_result (hr))
631       continue;
632 
633     /* NOTE: MFT AAC encoder seems to support more bitrate than it's documented
634      * at https://docs.microsoft.com/en-us/windows/win32/medfound/aac-encoder
635      * We will pass supported bitrate values to class init
636      */
637     hr = type->GetUINT32 (MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &bitrate);
638     if (!gst_mf_result (hr))
639       continue;
640 
641     channels_list.insert (channels);
642     rate_list.insert (rate);
643     /* convert bytes to bit */
644     bitrate_list.insert (bitrate * 8);
645 
646     config_found = TRUE;
647   }
648 
649   if (!config_found) {
650     GST_WARNING_OBJECT (transform, "Couldn't find available configuration");
651     goto done;
652   }
653 
654   src_caps =
655       gst_caps_from_string ("audio/mpeg, mpegversion = (int) 4, "
656       "stream-format = (string) raw, framed = (boolean) true, "
657       "base-profile = (string) lc");
658   sink_caps =
659       gst_caps_from_string ("audio/x-raw, layout = (string) interleaved, "
660       "format = (string) " GST_AUDIO_NE (S16));
661 
662   g_value_init (&channles_value, GST_TYPE_LIST);
663   g_value_init (&rate_value, GST_TYPE_LIST);
664 
665   /* *INDENT-OFF* */
666   for (auto it: channels_list) {
667     GValue channles = G_VALUE_INIT;
668 
669     g_value_init (&channles, G_TYPE_INT);
670     g_value_set_int (&channles, (gint) it);
671     gst_value_list_append_and_take_value (&channles_value, &channles);
672   }
673 
674   for (auto it: rate_list) {
675     GValue rate = G_VALUE_INIT;
676 
677     g_value_init (&rate, G_TYPE_INT);
678     g_value_set_int (&rate, (gint) it);
679     gst_value_list_append_and_take_value (&rate_value, &rate);
680   }
681   /* *INDENT-ON* */
682 
683   gst_caps_set_value (src_caps, "channels", &channles_value);
684   gst_caps_set_value (sink_caps, "channels", &channles_value);
685 
686   gst_caps_set_value (src_caps, "rate", &rate_value);
687   gst_caps_set_value (sink_caps, "rate", &rate_value);
688 
689   GST_MINI_OBJECT_FLAG_SET (sink_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
690   GST_MINI_OBJECT_FLAG_SET (src_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
691 
692   gst_mf_aac_enc_register (plugin, rank, device_name, enum_flags, device_index,
693       sink_caps, src_caps, bitrate_list);
694 
695 done:
696   if (output_list)
697     g_list_free_full (output_list, (GDestroyNotify) gst_mf_media_type_release);
698   g_free (device_name);
699   g_value_unset (&channles_value);
700   g_value_unset (&rate_value);
701 }
702 
703 void
gst_mf_aac_enc_plugin_init(GstPlugin * plugin,guint rank)704 gst_mf_aac_enc_plugin_init (GstPlugin * plugin, guint rank)
705 {
706   GstMFTransformEnumParams enum_params = { 0, };
707   MFT_REGISTER_TYPE_INFO output_type;
708   GstMFTransform *transform;
709   gint i;
710   gboolean do_next;
711 
712   GST_DEBUG_CATEGORY_INIT (gst_mf_aac_enc_debug, "mfaacenc", 0, "mfaacenc");
713 
714   output_type.guidMajorType = MFMediaType_Audio;
715   output_type.guidSubtype = MFAudioFormat_AAC;
716 
717   enum_params.category = MFT_CATEGORY_AUDIO_ENCODER;
718   enum_params.enum_flags = (MFT_ENUM_FLAG_SYNCMFT |
719       MFT_ENUM_FLAG_SORTANDFILTER | MFT_ENUM_FLAG_SORTANDFILTER_APPROVED_ONLY);
720   enum_params.output_typeinfo = &output_type;
721 
722   i = 0;
723   do {
724     enum_params.device_index = i++;
725     transform = gst_mf_transform_new (&enum_params);
726     do_next = TRUE;
727 
728     if (!transform) {
729       do_next = FALSE;
730     } else {
731       gst_mf_aac_enc_plugin_init_internal (plugin, rank, transform,
732           enum_params.device_index, enum_params.enum_flags);
733       gst_clear_object (&transform);
734     }
735   } while (do_next);
736 }
737