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