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