--- /dev/null
+/* GStreamer
+ * Copyright (C) 2022 Seungha Yang <seungha@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:element-mfaacdec
+ * @title: mfaacdec
+ *
+ * This element decodes AAC compressed data into RAW audio data.
+ *
+ * Since: 1.22
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include <gst/pbutils/pbutils.h>
+#include "gstmfaudiodecoder.h"
+#include "gstmfaacdec.h"
+#include <wrl.h>
+#include <string.h>
+
+/* *INDENT-OFF* */
+using namespace Microsoft::WRL;
+/* *INDENT-ON* */
+
+GST_DEBUG_CATEGORY (gst_mf_aac_dec_debug);
+#define GST_CAT_DEFAULT gst_mf_aac_dec_debug
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/mpeg, "
+ "mpegversion = (int) {2, 4}, "
+ "stream-format = (string) raw, framed = (boolean) true, "
+ "channels = (int) [1, 6], rate = (int) [8000, 48000]")
+ );
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("audio/x-raw, "
+ "format = (string) " GST_AUDIO_NE (S16) ", "
+ "layout = (string) interleaved, "
+ "channels = (int) [1, 6], rate = (int) [8000, 48000]")
+ );
+
+typedef struct _GstMFAacDec
+{
+ GstMFAudioDecoder parent;
+} GstMFAacDec;
+
+typedef struct _GstMFAacDecClass
+{
+ GstMFAudioDecoderClass parent_class;
+} GstMFAacDecClass;
+
+static GTypeClass *parent_class = nullptr;
+
+static gboolean gst_mf_aac_dec_set_format (GstMFAudioDecoder * decoder,
+ GstMFTransform * transform, GstCaps * caps);
+
+static void
+gst_mf_aac_dec_class_init (GstMFAacDecClass * klass, gpointer data)
+{
+ GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+ GstMFAudioDecoderClass *decoder_class = GST_MF_AUDIO_DECODER_CLASS (klass);
+ GstMFAudioDecoderClassData *cdata = (GstMFAudioDecoderClassData *) data;
+ gchar *long_name;
+
+ parent_class = (GTypeClass *) g_type_class_peek_parent (klass);
+
+ long_name = g_strdup_printf ("Media Foundation %s", cdata->device_name);
+ gst_element_class_set_metadata (element_class, long_name,
+ "Codec/Decoder/Audio",
+ "Microsoft Media Foundation AAC Decoder",
+ "Seungha Yang <seungha@centricular.com>");
+ g_free (long_name);
+
+ gst_element_class_add_static_pad_template (element_class, &sink_template);
+ gst_element_class_add_static_pad_template (element_class, &src_template);
+
+ decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_mf_aac_dec_set_format);
+
+ decoder_class->codec_id = MFAudioFormat_AAC;
+ decoder_class->enum_flags = cdata->enum_flags;
+ decoder_class->device_index = cdata->device_index;
+
+ g_free (cdata->device_name);
+ g_free (cdata);
+}
+
+static void
+gst_mf_aac_dec_init (GstMFAacDec * self)
+{
+}
+
+/* Portion of HEAACWAVEINFO struct after wfx field
+ * plus 2 bytes AudioSpecificConfig() */
+typedef struct
+{
+ WORD wPayloadType;
+ WORD wAudioProfileLevelIndication;
+ WORD wStructType;
+ WORD wReserved1;
+ DWORD dwReserved2;
+
+ WORD AudioSpecificConfig;
+} AACWaveInfo;
+
+static gboolean
+gst_mf_aac_dec_set_format (GstMFAudioDecoder * decoder,
+ GstMFTransform * transform, GstCaps * caps)
+{
+ GstMFAacDec *self = (GstMFAacDec *) decoder;
+ HRESULT hr;
+ const GValue *value;
+ GstStructure *structure;
+ GstBuffer *codec_data;
+ ComPtr < IMFMediaType > in_type;
+ ComPtr < IMFMediaType > out_type;
+ AACWaveInfo wave_info;
+ GstMapInfo map_info;
+ guint channels, rate;
+ const guint8 *data;
+ GstAudioInfo in_audio_info, out_audio_info;
+ GList *output_list, *iter;
+ GstCaps *out_caps;
+
+ G_STATIC_ASSERT (sizeof (AACWaveInfo) >= 12);
+
+ if (!gst_audio_info_from_caps (&in_audio_info, caps)) {
+ GST_ERROR_OBJECT (self, "Failed to get audio info from caps");
+ return FALSE;
+ }
+
+ structure = gst_caps_get_structure (caps, 0);
+ value = gst_structure_get_value (structure, "codec_data");
+ if (!value) {
+ GST_ERROR_OBJECT (self, "Missing codec_data");
+ return FALSE;
+ }
+
+ codec_data = gst_value_get_buffer (value);
+ if (!codec_data || gst_buffer_get_size (codec_data) < 2) {
+ GST_ERROR_OBJECT (self, "Invalid codec_data");
+ return FALSE;
+ }
+
+ if (!gst_buffer_map (codec_data, &map_info, GST_MAP_READ)) {
+ GST_ERROR_OBJECT (self, "Invalid codec_data buffer");
+ return FALSE;
+ }
+
+ data = (guint8 *) map_info.data;
+ channels = gst_codec_utils_aac_get_channels (data, map_info.size);
+ rate = gst_codec_utils_aac_get_sample_rate (data, map_info.size);
+
+ /* Fallback to channels/rate values specified in caps */
+ if (channels == 0)
+ channels = in_audio_info.channels;
+
+ if (rate == 0)
+ rate = in_audio_info.rate;
+
+ memset (&wave_info, 0, sizeof (AACWaveInfo));
+ wave_info.wAudioProfileLevelIndication = 0xfe;
+ memcpy (&wave_info.AudioSpecificConfig, data, 2);
+
+ hr = MFCreateMediaType (&in_type);
+ if (!gst_mf_result (hr))
+ return FALSE;
+
+ hr = in_type->SetGUID (MF_MT_MAJOR_TYPE, MFMediaType_Audio);
+ if (!gst_mf_result (hr))
+ return FALSE;
+
+ hr = in_type->SetGUID (MF_MT_SUBTYPE, MFAudioFormat_AAC);
+ if (!gst_mf_result (hr))
+ return FALSE;
+
+ hr = in_type->SetUINT32 (MF_MT_AAC_PAYLOAD_TYPE, 0);
+ if (!gst_mf_result (hr))
+ return FALSE;
+
+ hr = in_type->SetUINT32 (MF_MT_AUDIO_NUM_CHANNELS, channels);
+ if (!gst_mf_result (hr))
+ return FALSE;
+
+ hr = in_type->SetUINT32 (MF_MT_AUDIO_SAMPLES_PER_SECOND, rate);
+ if (!gst_mf_result (hr))
+ return FALSE;
+
+ /* FIXME: should parse this somehow? */
+ hr = in_type->SetUINT32 (MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, 0xfe);
+ if (!gst_mf_result (hr))
+ return FALSE;
+
+ hr = in_type->SetBlob (MF_MT_USER_DATA, (UINT8 *) & wave_info, 12);
+ if (!gst_mf_result (hr))
+ return FALSE;
+
+ if (!gst_mf_transform_set_input_type (transform, in_type.Get ())) {
+ GST_ERROR_OBJECT (self, "Failed to set format");
+ return FALSE;
+ }
+
+ if (!gst_mf_transform_get_output_available_types (transform, &output_list)) {
+ GST_ERROR_OBJECT (self, "Failed to get output types");
+ return FALSE;
+ }
+
+ for (iter = output_list; iter; iter = g_list_next (iter)) {
+ GUID guid;
+ IMFMediaType *type = (IMFMediaType *) iter->data;
+ UINT32 bps;
+
+ hr = type->GetGUID (MF_MT_MAJOR_TYPE, &guid);
+ if (!gst_mf_result (hr))
+ continue;
+
+ if (!IsEqualGUID (guid, MFMediaType_Audio))
+ continue;
+
+ hr = type->GetGUID (MF_MT_SUBTYPE, &guid);
+ if (!gst_mf_result (hr))
+ continue;
+
+ if (!IsEqualGUID (guid, MFAudioFormat_PCM))
+ continue;
+
+ hr = type->GetUINT32 (MF_MT_AUDIO_BITS_PER_SAMPLE, &bps);
+ if (!gst_mf_result (hr))
+ continue;
+
+ if (bps != 16)
+ continue;
+
+ out_type = type;
+ break;
+ }
+
+ g_list_free_full (output_list, (GDestroyNotify) gst_mf_media_type_release);
+
+ if (!out_type) {
+ GST_ERROR_OBJECT (self, "Failed to select output type");
+ return FALSE;
+ }
+
+ if (!gst_mf_transform_set_output_type (transform, out_type.Get ())) {
+ GST_ERROR_OBJECT (self, "Failed to select output type");
+ return FALSE;
+ }
+
+ out_caps = gst_mf_media_type_to_caps (out_type.Get ());
+ if (!out_caps) {
+ GST_ERROR_OBJECT (self, "Failed to get output caps");
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT (self, "Output caps %" GST_PTR_FORMAT, out_caps);
+
+ if (!gst_audio_info_from_caps (&out_audio_info, out_caps)) {
+ GST_ERROR_OBJECT (self,
+ "Failed to convert caps to audio info %" GST_PTR_FORMAT, out_caps);
+ gst_caps_unref (out_caps);
+ }
+
+ gst_caps_unref (out_caps);
+
+ return gst_audio_decoder_set_output_format (GST_AUDIO_DECODER (self),
+ &out_audio_info);
+}
+
+static void
+gst_mf_aac_dec_register (GstPlugin * plugin, guint rank,
+ const gchar * device_name, guint32 enum_flags, guint device_index)
+{
+ GType type;
+ GstMFAudioDecoderClassData *cdata;
+ GTypeInfo type_info = {
+ sizeof (GstMFAacDecClass),
+ nullptr,
+ nullptr,
+ (GClassInitFunc) gst_mf_aac_dec_class_init,
+ nullptr,
+ nullptr,
+ sizeof (GstMFAacDec),
+ 0,
+ (GInstanceInitFunc) gst_mf_aac_dec_init,
+ };
+
+ cdata = g_new0 (GstMFAudioDecoderClassData, 1);
+ cdata->device_name = g_strdup (device_name);
+ cdata->enum_flags = enum_flags;
+ cdata->device_index = device_index;
+ type_info.class_data = cdata;
+
+ type = g_type_register_static (GST_TYPE_MF_AUDIO_DECODER, "GstMFAacDec",
+ &type_info, (GTypeFlags) 0);
+
+ if (!gst_element_register (plugin, "mfaacdec", rank, type))
+ GST_WARNING ("Failed to register plugin");
+}
+
+static gboolean
+gst_mf_aac_dec_plugin_init_internal (GstPlugin * plugin, guint rank,
+ GstMFTransform * transform, guint device_index, guint32 enum_flags)
+{
+ gchar *device_name = nullptr;
+
+ if (!gst_mf_transform_open (transform))
+ return FALSE;
+
+ g_object_get (transform, "device-name", &device_name, nullptr);
+ if (!device_name) {
+ GST_WARNING_OBJECT (transform, "Unknown device name");
+ return FALSE;
+ }
+
+ gst_mf_aac_dec_register (plugin, rank, device_name, enum_flags, device_index);
+ g_free (device_name);
+
+ return TRUE;
+}
+
+void
+gst_mf_aac_dec_plugin_init (GstPlugin * plugin, guint rank)
+{
+ GstMFTransformEnumParams enum_params = { 0, };
+ MFT_REGISTER_TYPE_INFO input_type;
+ GstMFTransform *transform;
+ gint i;
+ gboolean do_next;
+
+ GST_DEBUG_CATEGORY_INIT (gst_mf_aac_dec_debug, "mfaacdec", 0, "mfaacdec");
+
+ input_type.guidMajorType = MFMediaType_Audio;
+ input_type.guidSubtype = MFAudioFormat_AAC;
+
+ enum_params.category = MFT_CATEGORY_AUDIO_DECODER;
+ enum_params.enum_flags = (MFT_ENUM_FLAG_SYNCMFT |
+ MFT_ENUM_FLAG_SORTANDFILTER | MFT_ENUM_FLAG_SORTANDFILTER_APPROVED_ONLY);
+ enum_params.input_typeinfo = &input_type;
+
+ i = 0;
+ do {
+ enum_params.device_index = i++;
+ transform = gst_mf_transform_new (&enum_params);
+ do_next = TRUE;
+
+ if (!transform) {
+ do_next = FALSE;
+ } else {
+ if (gst_mf_aac_dec_plugin_init_internal (plugin, rank, transform,
+ enum_params.device_index, enum_params.enum_flags)) {
+ do_next = FALSE;
+ }
+ gst_clear_object (&transform);
+ }
+ } while (do_next);
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2022 Seungha Yang <seungha@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <gst/gst.h>
+
+G_BEGIN_DECLS
+
+void gst_mf_aac_dec_plugin_init (GstPlugin * plugin,
+ guint rank);
+
+G_END_DECLS
+
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2022 Seungha Yang <seungha@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gst/gst.h>
+#include "gstmfaudiodecoder.h"
+#include <wrl.h>
+#include <string.h>
+
+/* *INDENT-OFF* */
+using namespace Microsoft::WRL;
+/* *INDENT-ON* */
+
+GST_DEBUG_CATEGORY (gst_mf_audio_decoder_debug);
+#define GST_CAT_DEFAULT gst_mf_audio_decoder_debug
+
+#define gst_mf_audio_decoder_parent_class parent_class
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (GstMFAudioDecoder, gst_mf_audio_decoder,
+ GST_TYPE_AUDIO_DECODER,
+ GST_DEBUG_CATEGORY_INIT (gst_mf_audio_decoder_debug, "mfaudiodecoder", 0,
+ "mfaudiodecoder"));
+
+static gboolean gst_mf_audio_decoder_open (GstAudioDecoder * dec);
+static gboolean gst_mf_audio_decoder_close (GstAudioDecoder * dec);
+static gboolean gst_mf_audio_decoder_set_format (GstAudioDecoder * dec,
+ GstCaps * caps);
+static GstFlowReturn gst_mf_audio_decoder_handle_frame (GstAudioDecoder * dec,
+ GstBuffer * buffer);
+static GstFlowReturn gst_mf_audio_decoder_drain (GstAudioDecoder * dec);
+static void gst_mf_audio_decoder_flush (GstAudioDecoder * dec, gboolean hard);
+
+static void
+gst_mf_audio_decoder_class_init (GstMFAudioDecoderClass * klass)
+{
+ GstAudioDecoderClass *audiodec_class = GST_AUDIO_DECODER_CLASS (klass);
+
+ audiodec_class->open = GST_DEBUG_FUNCPTR (gst_mf_audio_decoder_open);
+ audiodec_class->close = GST_DEBUG_FUNCPTR (gst_mf_audio_decoder_close);
+ audiodec_class->set_format =
+ GST_DEBUG_FUNCPTR (gst_mf_audio_decoder_set_format);
+ audiodec_class->handle_frame =
+ GST_DEBUG_FUNCPTR (gst_mf_audio_decoder_handle_frame);
+ audiodec_class->flush = GST_DEBUG_FUNCPTR (gst_mf_audio_decoder_flush);
+
+ gst_type_mark_as_plugin_api (GST_TYPE_MF_AUDIO_DECODER,
+ (GstPluginAPIFlags) 0);
+}
+
+static void
+gst_mf_audio_decoder_init (GstMFAudioDecoder * self)
+{
+ gst_audio_decoder_set_drainable (GST_AUDIO_DECODER (self), TRUE);
+}
+
+static gboolean
+gst_mf_audio_decoder_open (GstAudioDecoder * dec)
+{
+ GstMFAudioDecoder *self = GST_MF_AUDIO_DECODER (dec);
+ GstMFAudioDecoderClass *klass = GST_MF_AUDIO_DECODER_GET_CLASS (dec);
+ GstMFTransformEnumParams enum_params = { 0, };
+ MFT_REGISTER_TYPE_INFO input_type;
+
+ input_type.guidMajorType = MFMediaType_Audio;
+ input_type.guidSubtype = klass->codec_id;
+
+ enum_params.category = MFT_CATEGORY_AUDIO_DECODER;
+ enum_params.enum_flags = klass->enum_flags;
+ enum_params.input_typeinfo = &input_type;
+ enum_params.device_index = klass->device_index;
+
+ GST_DEBUG_OBJECT (self, "Create MFT with enum flags 0x%x, device index %d",
+ klass->enum_flags, klass->device_index);
+
+ self->transform = gst_mf_transform_new (&enum_params);
+ if (!self->transform) {
+ GST_ERROR_OBJECT (self, "Cannot create MFT object");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_mf_audio_decoder_close (GstAudioDecoder * dec)
+{
+ GstMFAudioDecoder *self = GST_MF_AUDIO_DECODER (dec);
+
+ gst_clear_object (&self->transform);
+
+ return TRUE;
+}
+
+static gboolean
+gst_mf_audio_decoder_set_format (GstAudioDecoder * dec, GstCaps * caps)
+{
+ GstMFAudioDecoder *self = GST_MF_AUDIO_DECODER (dec);
+ GstMFAudioDecoderClass *klass = GST_MF_AUDIO_DECODER_GET_CLASS (dec);
+
+ g_assert (klass->set_format != nullptr);
+
+ GST_DEBUG_OBJECT (self, "Set format");
+
+ gst_mf_audio_decoder_drain (dec);
+
+ if (!gst_mf_transform_open (self->transform)) {
+ GST_ERROR_OBJECT (self, "Failed to open MFT");
+ return FALSE;
+ }
+
+ if (!klass->set_format (self, self->transform, caps)) {
+ GST_ERROR_OBJECT (self, "Failed to set format");
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gst_mf_audio_decoder_process_input (GstMFAudioDecoder * self,
+ GstBuffer * buffer)
+{
+ HRESULT hr;
+ ComPtr < IMFSample > sample;
+ ComPtr < IMFMediaBuffer > media_buffer;
+ BYTE *data;
+ gboolean res = FALSE;
+ GstMapInfo info;
+
+ if (!gst_buffer_map (buffer, &info, GST_MAP_READ)) {
+ GST_ELEMENT_ERROR (self,
+ RESOURCE, READ, ("Couldn't map input buffer"), (nullptr));
+ return FALSE;
+ }
+
+ GST_TRACE_OBJECT (self, "Process buffer %" GST_PTR_FORMAT, buffer);
+
+ hr = MFCreateSample (&sample);
+ if (!gst_mf_result (hr))
+ goto done;
+
+ hr = MFCreateMemoryBuffer (info.size, &media_buffer);
+ if (!gst_mf_result (hr))
+ goto done;
+
+ hr = media_buffer->Lock (&data, nullptr, nullptr);
+ if (!gst_mf_result (hr))
+ goto done;
+
+ memcpy (data, info.data, info.size);
+ media_buffer->Unlock ();
+
+ hr = media_buffer->SetCurrentLength (info.size);
+ if (!gst_mf_result (hr))
+ goto done;
+
+ hr = sample->AddBuffer (media_buffer.Get ());
+ if (!gst_mf_result (hr))
+ goto done;
+
+ if (!gst_mf_transform_process_input (self->transform, sample.Get ())) {
+ GST_ERROR_OBJECT (self, "Failed to process input");
+ goto done;
+ }
+
+ res = TRUE;
+
+done:
+ gst_buffer_unmap (buffer, &info);
+
+ return res;
+}
+
+static GstFlowReturn
+gst_mf_audio_decoder_process_output (GstMFAudioDecoder * self)
+{
+ HRESULT hr;
+ BYTE *data = nullptr;
+ ComPtr < IMFMediaBuffer > media_buffer;
+ ComPtr < IMFSample > sample;
+ GstBuffer *buffer;
+ GstFlowReturn res = GST_FLOW_ERROR;
+ DWORD buffer_len = 0;
+
+ res = gst_mf_transform_get_output (self->transform, &sample);
+
+ if (res != GST_FLOW_OK)
+ return res;
+
+ hr = sample->GetBufferByIndex (0, &media_buffer);
+ if (!gst_mf_result (hr))
+ return GST_FLOW_ERROR;
+
+ hr = media_buffer->Lock (&data, nullptr, &buffer_len);
+ if (!gst_mf_result (hr))
+ return GST_FLOW_ERROR;
+
+ /* Can happen while draining */
+ if (buffer_len == 0 || !data) {
+ GST_DEBUG_OBJECT (self, "Empty media buffer");
+ media_buffer->Unlock ();
+ return GST_FLOW_OK;
+ }
+
+ buffer = gst_audio_decoder_allocate_output_buffer (GST_AUDIO_DECODER (self),
+ buffer_len);
+ gst_buffer_fill (buffer, 0, data, buffer_len);
+ media_buffer->Unlock ();
+
+ return gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (self), buffer, 1);
+}
+
+static GstFlowReturn
+gst_mf_audio_decoder_handle_frame (GstAudioDecoder * dec, GstBuffer * buffer)
+{
+ GstMFAudioDecoder *self = GST_MF_AUDIO_DECODER (dec);
+ GstFlowReturn ret;
+
+ if (!buffer)
+ return gst_mf_audio_decoder_drain (dec);
+
+ if (!gst_mf_audio_decoder_process_input (self, buffer)) {
+ GST_ERROR_OBJECT (self, "Failed to process input");
+ return GST_FLOW_ERROR;
+ }
+
+ do {
+ ret = gst_mf_audio_decoder_process_output (self);
+ } while (ret == GST_FLOW_OK);
+
+ if (ret == GST_MF_TRANSFORM_FLOW_NEED_DATA)
+ ret = GST_FLOW_OK;
+
+ return ret;
+}
+
+static GstFlowReturn
+gst_mf_audio_decoder_drain (GstAudioDecoder * dec)
+{
+ GstMFAudioDecoder *self = GST_MF_AUDIO_DECODER (dec);
+ GstFlowReturn ret = GST_FLOW_OK;
+
+ if (!self->transform)
+ return GST_FLOW_OK;
+
+ gst_mf_transform_drain (self->transform);
+
+ do {
+ ret = gst_mf_audio_decoder_process_output (self);
+ } while (ret == GST_FLOW_OK);
+
+ if (ret == GST_MF_TRANSFORM_FLOW_NEED_DATA)
+ ret = GST_FLOW_OK;
+
+ return ret;
+}
+
+static void
+gst_mf_audio_decoder_flush (GstAudioDecoder * dec, gboolean hard)
+{
+ GstMFAudioDecoder *self = GST_MF_AUDIO_DECODER (dec);
+
+ if (!self->transform)
+ return;
+
+ gst_mf_transform_flush (self->transform);
+}
--- /dev/null
+/* GStreamer
+ * Copyright (C) 2022 Seungha Yang <seungha@centricular.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#pragma once
+
+#include <gst/gst.h>
+#include <gst/audio/audio.h>
+#include "gstmfutils.h"
+#include "gstmftransform.h"
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_MF_AUDIO_DECODER (gst_mf_audio_decoder_get_type())
+#define GST_MF_AUDIO_DECODER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MF_AUDIO_DECODER,GstMFAudioDecoder))
+#define GST_MF_AUDIO_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_MF_AUDIO_DECODER,GstMFAudioDecoderClass))
+#define GST_MF_AUDIO_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GST_TYPE_MF_AUDIO_DECODER,GstMFAudioDecoderClass))
+#define GST_IS_MF_AUDIO_DECODER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MF_AUDIO_DECODER))
+#define GST_IS_MF_AUDIO_DECODER_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_MF_AUDIO_DECODER))
+
+typedef struct _GstMFAudioDecoder GstMFAudioDecoder;
+typedef struct _GstMFAudioDecoderClass GstMFAudioDecoderClass;
+
+typedef struct
+{
+ gchar * device_name;
+ guint32 enum_flags;
+ guint device_index;
+} GstMFAudioDecoderClassData;
+
+struct _GstMFAudioDecoder
+{
+ GstAudioDecoder parent;
+
+ GstMFTransform *transform;
+};
+
+struct _GstMFAudioDecoderClass
+{
+ GstAudioDecoderClass parent_class;
+
+ GUID codec_id;
+ guint32 enum_flags;
+ guint device_index;
+
+ gboolean (*set_format) (GstMFAudioDecoder * decoder,
+ GstMFTransform * transform,
+ GstCaps * caps);
+};
+
+GType gst_mf_audio_decoder_get_type (void);
+
+G_END_DECLS
+
return caps;
}
+/* Desktop only defines */
+#ifndef KSAUDIO_SPEAKER_MONO
+#define KSAUDIO_SPEAKER_MONO (SPEAKER_FRONT_CENTER)
+#endif
+#ifndef KSAUDIO_SPEAKER_1POINT1
+#define KSAUDIO_SPEAKER_1POINT1 (SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY)
+#endif
+#ifndef KSAUDIO_SPEAKER_STEREO
+#define KSAUDIO_SPEAKER_STEREO (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT)
+#endif
+#ifndef KSAUDIO_SPEAKER_2POINT1
+#define KSAUDIO_SPEAKER_2POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY)
+#endif
+#ifndef KSAUDIO_SPEAKER_3POINT0
+#define KSAUDIO_SPEAKER_3POINT0 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER)
+#endif
+#ifndef KSAUDIO_SPEAKER_3POINT1
+#define KSAUDIO_SPEAKER_3POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
+ SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY)
+#endif
+#ifndef KSAUDIO_SPEAKER_QUAD
+#define KSAUDIO_SPEAKER_QUAD (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
+ SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
+#endif
+#define KSAUDIO_SPEAKER_SURROUND (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
+ SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER)
+#ifndef KSAUDIO_SPEAKER_5POINT0
+#define KSAUDIO_SPEAKER_5POINT0 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | \
+ SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT)
+#endif
+#define KSAUDIO_SPEAKER_5POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
+ SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \
+ SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT)
+#ifndef KSAUDIO_SPEAKER_7POINT0
+#define KSAUDIO_SPEAKER_7POINT0 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | \
+ SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \
+ SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT)
+#endif
+#ifndef KSAUDIO_SPEAKER_7POINT1
+#define KSAUDIO_SPEAKER_7POINT1 (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | \
+ SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | \
+ SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | \
+ SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER)
+#endif
+
+static struct
+{
+ guint64 mf_pos;
+ GstAudioChannelPosition gst_pos;
+} mf_to_gst_pos[] = {
+ {SPEAKER_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT},
+ {SPEAKER_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT},
+ {SPEAKER_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER},
+ {SPEAKER_LOW_FREQUENCY, GST_AUDIO_CHANNEL_POSITION_LFE1},
+ {SPEAKER_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT},
+ {SPEAKER_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
+ {SPEAKER_FRONT_LEFT_OF_CENTER,
+ GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER},
+ {SPEAKER_FRONT_RIGHT_OF_CENTER,
+ GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER},
+ {SPEAKER_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER},
+ /* Enum values diverge from this point onwards */
+ {SPEAKER_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT},
+ {SPEAKER_SIDE_RIGHT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT},
+ {SPEAKER_TOP_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_CENTER},
+ {SPEAKER_TOP_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT},
+ {SPEAKER_TOP_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER},
+ {SPEAKER_TOP_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT},
+ {SPEAKER_TOP_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT},
+ {SPEAKER_TOP_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER},
+ {SPEAKER_TOP_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT}
+};
+
+/* *INDENT-OFF* */
+static DWORD default_ch_masks[] = {
+ 0,
+ KSAUDIO_SPEAKER_MONO,
+ /* 2ch */
+ KSAUDIO_SPEAKER_STEREO,
+ /* 2.1ch */
+ /* KSAUDIO_SPEAKER_3POINT0 ? */
+ KSAUDIO_SPEAKER_2POINT1,
+ /* 4ch */
+ /* KSAUDIO_SPEAKER_3POINT1 or KSAUDIO_SPEAKER_SURROUND ? */
+ KSAUDIO_SPEAKER_QUAD,
+ /* 5ch */
+ KSAUDIO_SPEAKER_5POINT0,
+ /* 5.1ch */
+ KSAUDIO_SPEAKER_5POINT1,
+ /* 7ch */
+ KSAUDIO_SPEAKER_7POINT0,
+ /* 7.1ch */
+ KSAUDIO_SPEAKER_7POINT1,
+};
+/* *INDENT-ON* */
+
+static void
+gst_mf_media_audio_channel_mask_to_position (guint channels, DWORD mask,
+ GstAudioChannelPosition * position)
+{
+ guint i, ch;
+
+ for (i = 0, ch = 0; i < G_N_ELEMENTS (mf_to_gst_pos) && ch < channels; i++) {
+ if ((mask & mf_to_gst_pos[i].mf_pos) == 0)
+ continue;
+
+ position[ch] = mf_to_gst_pos[i].gst_pos;
+ ch++;
+ }
+}
+
+static GstCaps *
+gst_mf_media_type_to_audio_caps (IMFMediaType * media_type)
+{
+ GUID subtype;
+ HRESULT hr;
+ UINT32 bps;
+ GstAudioFormat format = GST_AUDIO_FORMAT_UNKNOWN;
+ GstAudioInfo info;
+ UINT32 rate, channels, mask;
+ GstAudioChannelPosition position[64];
+
+ hr = media_type->GetGUID (MF_MT_SUBTYPE, &subtype);
+ if (FAILED (hr)) {
+ GST_WARNING ("failed to get subtype, hr: 0x%x", (guint) hr);
+ return nullptr;
+ }
+
+ if (!IsEqualGUID (subtype, MFAudioFormat_PCM) &&
+ !IsEqualGUID (subtype, MFAudioFormat_Float)) {
+ GST_FIXME ("Unknown subtype");
+ return nullptr;
+ }
+
+ hr = media_type->GetUINT32 (MF_MT_AUDIO_BITS_PER_SAMPLE, &bps);
+ if (FAILED (hr)) {
+ GST_WARNING ("Failed to get bps, hr: 0x%x", (guint) hr);
+ return nullptr;
+ }
+
+ if (IsEqualGUID (subtype, MFAudioFormat_PCM)) {
+ format = gst_audio_format_build_integer (TRUE, G_LITTLE_ENDIAN, bps, bps);
+ } else if (bps == 32) {
+ format = GST_AUDIO_FORMAT_F32LE;
+ } else if (bps == 64) {
+ format = GST_AUDIO_FORMAT_F64LE;
+ }
+
+ if (format == GST_AUDIO_FORMAT_UNKNOWN) {
+ GST_WARNING ("Unknown audio format");
+ return nullptr;
+ }
+
+ hr = media_type->GetUINT32 (MF_MT_AUDIO_NUM_CHANNELS, &channels);
+ if (FAILED (hr) || channels == 0) {
+ GST_WARNING ("Unknown channels");
+ return nullptr;
+ }
+
+ hr = media_type->GetUINT32 (MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate);
+ if (FAILED (hr) || rate == 0) {
+ GST_WARNING ("Unknown rate");
+ return nullptr;
+ }
+
+ for (guint i = 0; i < G_N_ELEMENTS (position); i++)
+ position[i] = GST_AUDIO_CHANNEL_POSITION_NONE;
+
+ hr = media_type->GetUINT32 (MF_MT_AUDIO_CHANNEL_MASK, &mask);
+ if (FAILED (hr)) {
+ if (channels == 1) {
+ position[0] = GST_AUDIO_CHANNEL_POSITION_MONO;
+ } else if (channels == 2) {
+ position[0] = GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT;
+ position[1] = GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT;
+ } else if (channels <= 8) {
+ GST_WARNING ("Unknown channel position, use default value");
+ gst_mf_media_audio_channel_mask_to_position (channels,
+ default_ch_masks[channels], position);
+ } else {
+ GST_WARNING ("Failed to determine channel position");
+ return nullptr;
+ }
+ } else {
+ gst_mf_media_audio_channel_mask_to_position (channels, mask, position);
+ }
+
+ gst_audio_info_set_format (&info, format, rate, channels, position);
+
+ return gst_audio_info_to_caps (&info);
+}
+
GstCaps *
gst_mf_media_type_to_caps (IMFMediaType * media_type)
{
return nullptr;
}
- if (IsEqualGUID (major_type, MFMediaType_Video))
+ if (IsEqualGUID (major_type, MFMediaType_Video)) {
return gst_mf_media_type_to_video_caps (media_type);
+ } else if (IsEqualGUID (major_type, MFMediaType_Audio)) {
+ return gst_mf_media_type_to_audio_caps (media_type);
+ }
return nullptr;
}
#include <gst/gst.h>
#include <gst/video/video.h>
+#include <gst/audio/audio.h>
#ifndef INITGUID
#include <initguid.h>
G_END_DECLS
-#endif /* __GST_MF_UTILS_H__ */
\ No newline at end of file
+#endif /* __GST_MF_UTILS_H__ */
mf_sources = [
+ 'gstmfaacdec.cpp',
'gstmfaacenc.cpp',
+ 'gstmfaudiodecoder.cpp',
'gstmfaudioencoder.cpp',
'gstmfdevice.cpp',
'gstmfh264enc.cpp',
#include "gstmfvp9enc.h"
#include "gstmfaacenc.h"
#include "gstmfmp3enc.h"
+#include "gstmfaacdec.h"
#if GST_MF_HAVE_D3D11
#include <gst/d3d11/gstd3d11.h>
gst_mf_aac_enc_plugin_init (plugin, GST_RANK_SECONDARY);
gst_mf_mp3_enc_plugin_init (plugin, GST_RANK_SECONDARY);
+ gst_mf_aac_dec_plugin_init (plugin, GST_RANK_SECONDARY);
/* So that call MFShutdown() when this plugin is no more used
* (i.e., gst_deinit). Otherwise valgrind-like tools would complain