• 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-mfmp3enc
22  * @title: mfmp3enc
23  *
24  * This element encodes raw audio into MP3 compressed data.
25  *
26  * ## Example pipelines
27  * |[
28  * gst-launch-1.0 -v audiotestsrc ! mfmp3enc ! filesink location=test.mp3
29  * ]| This example pipeline will encode a test audio source to MP3 using
30  * Media Foundation encoder
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 "gstmfmp3enc.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_mp3_enc_debug);
51 #define GST_CAT_DEFAULT gst_mf_mp3_enc_debug
52 
53 enum
54 {
55   PROP_0,
56   PROP_BITRATE,
57 };
58 
59 #define DEFAULT_BITRATE (0)
60 
61 typedef struct _GstMFMp3Enc
62 {
63   GstMFAudioEnc parent;
64 
65   /* properties */
66   guint bitrate;
67 } GstMFMp3Enc;
68 
69 typedef struct _GstMFMp3EncClass
70 {
71   GstMFAudioEncClass parent_class;
72 
73 } GstMFMp3EncClass;
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 } GstMFMp3EncClassData;
85 /* *INDENT-ON* */
86 
87 static GstElementClass *parent_class = NULL;
88 
89 static void gst_mf_mp3_enc_get_property (GObject * object, guint prop_id,
90     GValue * value, GParamSpec * pspec);
91 static void gst_mf_mp3_enc_set_property (GObject * object, guint prop_id,
92     const GValue * value, GParamSpec * pspec);
93 static gboolean gst_mf_mp3_enc_get_output_type (GstMFAudioEnc * mfenc,
94     GstAudioInfo * info, IMFMediaType ** output_type);
95 static gboolean gst_mf_mp3_enc_get_input_type (GstMFAudioEnc * mfenc,
96     GstAudioInfo * info, IMFMediaType ** input_type);
97 static gboolean gst_mf_mp3_enc_set_src_caps (GstMFAudioEnc * mfenc,
98     GstAudioInfo * info);
99 
100 static void
gst_mf_mp3_enc_class_init(GstMFMp3EncClass * klass,gpointer data)101 gst_mf_mp3_enc_class_init (GstMFMp3EncClass * 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   GstMFMp3EncClassData *cdata = (GstMFMp3EncClassData *) 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_mp3_enc_get_property;
115   gobject_class->set_property = gst_mf_mp3_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 MP3 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_mp3_enc_get_output_type);
154   mfenc_class->get_input_type =
155       GST_DEBUG_FUNCPTR (gst_mf_mp3_enc_get_input_type);
156   mfenc_class->set_src_caps = GST_DEBUG_FUNCPTR (gst_mf_mp3_enc_set_src_caps);
157 
158   mfenc_class->codec_id = MFAudioFormat_MP3;
159   mfenc_class->enum_flags = cdata->enum_flags;
160   mfenc_class->device_index = cdata->device_index;
161   mfenc_class->frame_samples = 1152;
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_mp3_enc_init(GstMFMp3Enc * self)170 gst_mf_mp3_enc_init (GstMFMp3Enc * self)
171 {
172   self->bitrate = DEFAULT_BITRATE;
173 }
174 
175 static void
gst_mf_mp3_enc_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)176 gst_mf_mp3_enc_get_property (GObject * object, guint prop_id,
177     GValue * value, GParamSpec * pspec)
178 {
179   GstMFMp3Enc *self = (GstMFMp3Enc *) (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_mp3_enc_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)192 gst_mf_mp3_enc_set_property (GObject * object, guint prop_id,
193     const GValue * value, GParamSpec * pspec)
194 {
195   GstMFMp3Enc *self = (GstMFMp3Enc *) (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_mp3_enc_get_output_type(GstMFAudioEnc * mfenc,GstAudioInfo * info,IMFMediaType ** output_type)208 gst_mf_mp3_enc_get_output_type (GstMFAudioEnc * mfenc, GstAudioInfo * info,
209     IMFMediaType ** output_type)
210 {
211   GstMFMp3Enc *self = (GstMFMp3Enc *) 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_MP3)) {
247       GST_WARNING_OBJECT (self, "Sub type is not MP3");
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   if (bitrate == 0) {
287     /* default of MFT
288      * https://docs.microsoft.com/en-us/windows/win32/medfound/mp3-audio-encoder
289      */
290     if (GST_AUDIO_INFO_CHANNELS (info) == 1) {
291       bitrate = 128000;
292     } else {
293       bitrate = 320000;
294     }
295 
296     GST_DEBUG_OBJECT (self, "Calculated bitrate %d", bitrate);
297   } else {
298     GST_DEBUG_OBJECT (self, "Requested bitrate %d", bitrate);
299   }
300 
301   GST_DEBUG_OBJECT (self, "Available bitrates");
302 
303   /* *INDENT-OFF* */
304   for (auto it: bitrate_list)
305     GST_DEBUG_OBJECT (self, "\t%d", it);
306 
307   /* Based on calculated or requested bitrate, find the closest supported
308    * bitrate */
309   {
310     const auto it = bitrate_list.lower_bound (bitrate);
311     if (it == bitrate_list.end()) {
312       target_bitrate = *std::prev (it);
313     } else {
314       target_bitrate = *it;
315     }
316   }
317 
318   GST_DEBUG_OBJECT (self, "Selected target bitrate %d", target_bitrate);
319 
320   for (auto it: filtered_types) {
321     UINT32 value = 0;
322 
323     it->GetUINT32 (MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &value);
324     if (value * 8 == target_bitrate) {
325       target_output = it;
326       break;
327     }
328   }
329   /* *INDENT-ON* */
330 
331   if (!target_output) {
332     GST_ERROR_OBJECT (self, "Failed to decide final output type");
333     return FALSE;
334   }
335 
336   *output_type = target_output.Detach ();
337 
338   return TRUE;
339 }
340 
341 static gboolean
gst_mf_mp3_enc_get_input_type(GstMFAudioEnc * mfenc,GstAudioInfo * info,IMFMediaType ** input_type)342 gst_mf_mp3_enc_get_input_type (GstMFAudioEnc * mfenc, GstAudioInfo * info,
343     IMFMediaType ** input_type)
344 {
345   GstMFMp3Enc *self = (GstMFMp3Enc *) mfenc;
346   GstMFTransform *transform = mfenc->transform;
347   GList *input_list = NULL;
348   GList *iter;
349   ComPtr < IMFMediaType > target_input;
350   std::vector < ComPtr < IMFMediaType >> filtered_types;
351   std::set < UINT32 > bitrate_list;
352   HRESULT hr;
353 
354   if (!gst_mf_transform_get_input_available_types (transform, &input_list)) {
355     GST_ERROR_OBJECT (self, "Couldn't get available output type");
356     return FALSE;
357   }
358 
359   /* 1. Filtering based on channels and sample rate */
360   for (iter = input_list; iter; iter = g_list_next (iter)) {
361     IMFMediaType *type = (IMFMediaType *) iter->data;
362     GUID guid = GUID_NULL;
363     UINT32 value;
364 
365     hr = type->GetGUID (MF_MT_MAJOR_TYPE, &guid);
366     if (!gst_mf_result (hr))
367       continue;
368 
369     if (!IsEqualGUID (guid, MFMediaType_Audio)) {
370       GST_WARNING_OBJECT (self, "Major type is not audio");
371       continue;
372     }
373 
374     hr = type->GetGUID (MF_MT_SUBTYPE, &guid);
375     if (!gst_mf_result (hr))
376       continue;
377 
378     if (!IsEqualGUID (guid, MFAudioFormat_PCM)) {
379       GST_WARNING_OBJECT (self, "Sub type is not PCM");
380       continue;
381     }
382 
383     hr = type->GetUINT32 (MF_MT_AUDIO_NUM_CHANNELS, &value);
384     if (!gst_mf_result (hr))
385       continue;
386 
387     if (value != GST_AUDIO_INFO_CHANNELS (info))
388       continue;
389 
390     hr = type->GetUINT32 (MF_MT_AUDIO_SAMPLES_PER_SECOND, &value);
391     if (!gst_mf_result (hr))
392       continue;
393 
394     if (value != GST_AUDIO_INFO_RATE (info))
395       continue;
396 
397     filtered_types.push_back (type);
398   }
399 
400   g_list_free_full (input_list, (GDestroyNotify) gst_mf_media_type_release);
401 
402   if (filtered_types.empty ()) {
403     GST_ERROR_OBJECT (self, "Couldn't find target input type");
404     return FALSE;
405   }
406 
407   GST_DEBUG_OBJECT (self, "Total %d input types are available",
408       filtered_types.size ());
409 
410   /* Just select the first one */
411   target_input = *filtered_types.begin ();
412 
413   *input_type = target_input.Detach ();
414 
415   return TRUE;
416 }
417 
418 static gboolean
gst_mf_mp3_enc_set_src_caps(GstMFAudioEnc * mfenc,GstAudioInfo * info)419 gst_mf_mp3_enc_set_src_caps (GstMFAudioEnc * mfenc, GstAudioInfo * info)
420 {
421   GstMFMp3Enc *self = (GstMFMp3Enc *) mfenc;
422   GstCaps *src_caps;
423   gboolean ret;
424   ComPtr < IMFMediaType > output_type;
425   gint version = 1;
426 
427   if (!gst_mf_transform_get_output_current_type (mfenc->transform,
428           &output_type)) {
429     GST_ERROR_OBJECT (self, "Couldn't get current output type");
430     return FALSE;
431   }
432 
433   if (GST_AUDIO_INFO_RATE (info) == 32000 ||
434       GST_AUDIO_INFO_RATE (info) == 44100 ||
435       GST_AUDIO_INFO_RATE (info) == 48000)
436     version = 1;
437   else
438     version = 2;
439 
440   src_caps = gst_caps_new_simple ("audio/mpeg",
441       "mpegversion", G_TYPE_INT, 1,
442       "mpegaudioversion", G_TYPE_INT, version,
443       "layer", G_TYPE_INT, 3,
444       "channels", G_TYPE_INT, GST_AUDIO_INFO_CHANNELS (info),
445       "rate", G_TYPE_INT, GST_AUDIO_INFO_RATE (info), NULL);
446 
447   ret =
448       gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (self), src_caps);
449   if (!ret) {
450     GST_WARNING_OBJECT (self,
451         "Couldn't set output format %" GST_PTR_FORMAT, src_caps);
452   }
453   gst_caps_unref (src_caps);
454 
455   return ret;
456 }
457 
458 static void
gst_mf_mp3_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)459 gst_mf_mp3_enc_register (GstPlugin * plugin, guint rank,
460     const gchar * device_name, guint32 enum_flags, guint device_index,
461     GstCaps * sink_caps, GstCaps * src_caps,
462     const std::set < UINT32 > &bitrate_list)
463 {
464   GType type;
465   gchar *type_name;
466   gchar *feature_name;
467   gint i;
468   GstMFMp3EncClassData *cdata;
469   gboolean is_default = TRUE;
470   GTypeInfo type_info = {
471     sizeof (GstMFMp3EncClass),
472     NULL,
473     NULL,
474     (GClassInitFunc) gst_mf_mp3_enc_class_init,
475     NULL,
476     NULL,
477     sizeof (GstMFMp3Enc),
478     0,
479     (GInstanceInitFunc) gst_mf_mp3_enc_init,
480   };
481 
482   cdata = new GstMFMp3EncClassData;
483   cdata->sink_caps = sink_caps;
484   cdata->src_caps = src_caps;
485   cdata->device_name = g_strdup (device_name);
486   cdata->enum_flags = enum_flags;
487   cdata->device_index = device_index;
488   cdata->bitrate_list = bitrate_list;
489   type_info.class_data = cdata;
490 
491   type_name = g_strdup ("GstMFMp3Enc");
492   feature_name = g_strdup ("mfmp3enc");
493 
494   i = 1;
495   while (g_type_from_name (type_name) != 0) {
496     g_free (type_name);
497     g_free (feature_name);
498     type_name = g_strdup_printf ("GstMFMp3Device%dEnc", i);
499     feature_name = g_strdup_printf ("mfmp3device%denc", i);
500     is_default = FALSE;
501     i++;
502   }
503 
504   type =
505       g_type_register_static (GST_TYPE_MF_AUDIO_ENC, type_name, &type_info,
506       (GTypeFlags) 0);
507 
508   /* make lower rank than default device */
509   if (rank > 0 && !is_default)
510     rank--;
511 
512   if (!gst_element_register (plugin, feature_name, rank, type))
513     GST_WARNING ("Failed to register plugin '%s'", type_name);
514 
515   g_free (type_name);
516   g_free (feature_name);
517 }
518 
519 static gboolean
gst_mf_mp3_enc_create_template_caps(const std::set<UINT32> & rate_list,gint channels,GstCaps ** sink_caps,GstCaps ** src_caps)520 gst_mf_mp3_enc_create_template_caps (const std::set < UINT32 > &rate_list,
521     gint channels, GstCaps ** sink_caps, GstCaps ** src_caps)
522 {
523   GstCaps *sink = NULL;
524   GstCaps *src = NULL;
525   GValue rate_value = G_VALUE_INIT;
526 
527   if (rate_list.empty ()) {
528     GST_WARNING ("No available rate for channels %d", channels);
529     return FALSE;
530   }
531 
532   if (channels != 0) {
533     sink =
534         gst_caps_from_string ("audio/x-raw, "
535         "format = (string) " GST_AUDIO_NE (S16)
536         ", layout = (string) interleaved");
537     src =
538         gst_caps_from_string ("audio/mpeg, mpegversion = (int) 1,"
539         "layer = (int) 3");
540 
541     gst_caps_set_simple (sink, "channels", G_TYPE_INT, channels, NULL);
542     gst_caps_set_simple (src, "channels", G_TYPE_INT, channels, NULL);
543   } else {
544     sink =
545         gst_caps_from_string ("audio/x-raw, "
546         "format = (string) " GST_AUDIO_NE (S16)
547         ", layout = (string) interleaved, channels = (int) [ 1, 2 ]");
548     src =
549         gst_caps_from_string ("audio/mpeg, mpegversion = (int) 1,"
550         "layer = (int) 3,  channels = (int) [ 1, 2 ]");
551   }
552 
553   g_value_init (&rate_value, GST_TYPE_LIST);
554 
555   /* *INDENT-OFF* */
556   for (const auto &it: rate_list) {
557     GValue rate = G_VALUE_INIT;
558 
559     g_value_init (&rate, G_TYPE_INT);
560     g_value_set_int (&rate, (gint) it);
561     gst_value_list_append_and_take_value (&rate_value, &rate);
562   }
563   /* *INDENT-ON* */
564 
565   gst_caps_set_value (src, "rate", &rate_value);
566   gst_caps_set_value (sink, "rate", &rate_value);
567 
568   g_value_unset (&rate_value);
569 
570   if (*sink_caps == NULL)
571     *sink_caps = sink;
572   else
573     *sink_caps = gst_caps_merge (*sink_caps, sink);
574 
575   if (*src_caps == NULL)
576     *src_caps = src;
577   else
578     *src_caps = gst_caps_merge (*src_caps, src);
579 
580   return TRUE;
581 }
582 
583 static void
gst_mf_mp3_enc_plugin_init_internal(GstPlugin * plugin,guint rank,GstMFTransform * transform,guint device_index,guint32 enum_flags)584 gst_mf_mp3_enc_plugin_init_internal (GstPlugin * plugin, guint rank,
585     GstMFTransform * transform, guint device_index, guint32 enum_flags)
586 {
587   HRESULT hr;
588   gint i;
589   GstCaps *src_caps = NULL;
590   GstCaps *sink_caps = NULL;
591   gchar *device_name = NULL;
592   GList *output_list = NULL;
593   GList *iter;
594   std::set < UINT32 > mono_rate_list;
595   std::set < UINT32 > stereo_rate_list;
596   std::set < UINT32 > bitrate_list;
597   gboolean config_found = FALSE;
598 
599   if (!gst_mf_transform_open (transform))
600     return;
601 
602   g_object_get (transform, "device-name", &device_name, NULL);
603   if (!device_name) {
604     GST_WARNING_OBJECT (transform, "Unknown device name");
605     return;
606   }
607 
608   if (!gst_mf_transform_get_output_available_types (transform, &output_list)) {
609     GST_WARNING_OBJECT (transform, "Couldn't get output types");
610     goto done;
611   }
612 
613   GST_INFO_OBJECT (transform, "Have %d output type",
614       g_list_length (output_list));
615 
616   for (iter = output_list, i = 0; iter; iter = g_list_next (iter), i++) {
617     UINT32 channels, rate, bitrate;
618     GUID guid = GUID_NULL;
619     IMFMediaType *type = (IMFMediaType *) iter->data;
620 #ifndef GST_DISABLE_GST_DEBUG
621     gchar *msg = g_strdup_printf ("Output IMFMediaType %d", i);
622     gst_mf_dump_attributes ((IMFAttributes *) type, msg, GST_LEVEL_TRACE);
623     g_free (msg);
624 #endif
625 
626     hr = type->GetGUID (MF_MT_MAJOR_TYPE, &guid);
627     if (!gst_mf_result (hr))
628       continue;
629 
630     /* shouldn't happen */
631     if (!IsEqualGUID (guid, MFMediaType_Audio))
632       continue;
633 
634     hr = type->GetGUID (MF_MT_SUBTYPE, &guid);
635     if (!gst_mf_result (hr))
636       continue;
637 
638     /* shouldn't happen */
639     if (!IsEqualGUID (guid, MFAudioFormat_MP3))
640       continue;
641 
642     hr = type->GetUINT32 (MF_MT_AUDIO_NUM_CHANNELS, &channels);
643     if (!gst_mf_result (hr))
644       continue;
645 
646     if (channels != 1 && channels != 2) {
647       GST_WARNING_OBJECT (transform, "Unknown channels %d", channels);
648       continue;
649     }
650 
651     hr = type->GetUINT32 (MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate);
652     if (!gst_mf_result (hr))
653       continue;
654 
655     hr = type->GetUINT32 (MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &bitrate);
656     if (!gst_mf_result (hr))
657       continue;
658 
659     if (channels == 1)
660       mono_rate_list.insert (rate);
661     else if (channels == 2)
662       stereo_rate_list.insert (rate);
663     else
664       g_assert_not_reached ();
665 
666     /* convert bytes to bit */
667     bitrate_list.insert (bitrate * 8);
668 
669     config_found = TRUE;
670   }
671 
672   if (!config_found) {
673     GST_WARNING_OBJECT (transform, "Couldn't find available configuration");
674     goto done;
675   }
676 
677   /* MFT might support more rate and channels combination than documented
678    * https://docs.microsoft.com/en-us/windows/win32/medfound/mp3-audio-encoder
679    *
680    * Configure caps per channels if supported rate values are different
681    */
682   if (!mono_rate_list.empty () && !stereo_rate_list.empty () &&
683       mono_rate_list == stereo_rate_list) {
684     gst_mf_mp3_enc_create_template_caps (mono_rate_list,
685         0, &sink_caps, &src_caps);
686   } else {
687     if (!mono_rate_list.empty ()) {
688       gst_mf_mp3_enc_create_template_caps (mono_rate_list,
689           1, &sink_caps, &src_caps);
690     }
691 
692     if (!stereo_rate_list.empty ()) {
693       gst_mf_mp3_enc_create_template_caps (stereo_rate_list,
694           2, &sink_caps, &src_caps);
695     }
696   }
697 
698   if (!sink_caps || !src_caps) {
699     GST_WARNING_OBJECT (transform, "Failed to configure template caps");
700     gst_clear_caps (&sink_caps);
701     gst_clear_caps (&src_caps);
702     goto done;
703   }
704 
705   GST_MINI_OBJECT_FLAG_SET (sink_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
706   GST_MINI_OBJECT_FLAG_SET (src_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
707 
708   gst_mf_mp3_enc_register (plugin, rank, device_name, enum_flags, device_index,
709       sink_caps, src_caps, bitrate_list);
710 
711 done:
712   if (output_list)
713     g_list_free_full (output_list, (GDestroyNotify) gst_mf_media_type_release);
714   g_free (device_name);
715 }
716 
717 void
gst_mf_mp3_enc_plugin_init(GstPlugin * plugin,guint rank)718 gst_mf_mp3_enc_plugin_init (GstPlugin * plugin, guint rank)
719 {
720   GstMFTransformEnumParams enum_params = { 0, };
721   MFT_REGISTER_TYPE_INFO output_type;
722   GstMFTransform *transform;
723   gint i;
724   gboolean do_next;
725 
726   GST_DEBUG_CATEGORY_INIT (gst_mf_mp3_enc_debug, "mfmp3enc", 0, "mfmp3enc");
727 
728   output_type.guidMajorType = MFMediaType_Audio;
729   output_type.guidSubtype = MFAudioFormat_MP3;
730 
731   enum_params.category = MFT_CATEGORY_AUDIO_ENCODER;
732   enum_params.enum_flags = (MFT_ENUM_FLAG_SYNCMFT |
733       MFT_ENUM_FLAG_SORTANDFILTER | MFT_ENUM_FLAG_SORTANDFILTER_APPROVED_ONLY);
734   enum_params.output_typeinfo = &output_type;
735 
736   i = 0;
737   do {
738     enum_params.device_index = i++;
739     transform = gst_mf_transform_new (&enum_params);
740     do_next = TRUE;
741 
742     if (!transform) {
743       do_next = FALSE;
744     } else {
745       gst_mf_mp3_enc_plugin_init_internal (plugin, rank, transform,
746           enum_params.device_index, enum_params.enum_flags);
747       gst_clear_object (&transform);
748     }
749   } while (do_next);
750 }
751