mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-01-02 21:48:55 +00:00
mediafoundation: Add support for AAC decoding
See also https://docs.microsoft.com/en-us/windows/win32/medfound/aac-decoder Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1596>
This commit is contained in:
parent
0b26254a6a
commit
be957f6c61
8 changed files with 966 additions and 2 deletions
380
subprojects/gst-plugins-bad/sys/mediafoundation/gstmfaacdec.cpp
Normal file
380
subprojects/gst-plugins-bad/sys/mediafoundation/gstmfaacdec.cpp
Normal file
|
@ -0,0 +1,380 @@
|
||||||
|
/* 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);
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/* 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
|
||||||
|
|
|
@ -0,0 +1,285 @@
|
||||||
|
/* 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);
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
/* 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
|
||||||
|
|
|
@ -343,6 +343,198 @@ gst_mf_media_type_to_video_caps (IMFMediaType * media_type)
|
||||||
return caps;
|
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 *
|
GstCaps *
|
||||||
gst_mf_media_type_to_caps (IMFMediaType * media_type)
|
gst_mf_media_type_to_caps (IMFMediaType * media_type)
|
||||||
{
|
{
|
||||||
|
@ -357,8 +549,11 @@ gst_mf_media_type_to_caps (IMFMediaType * media_type)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsEqualGUID (major_type, MFMediaType_Video))
|
if (IsEqualGUID (major_type, MFMediaType_Video)) {
|
||||||
return gst_mf_media_type_to_video_caps (media_type);
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/video/video.h>
|
#include <gst/video/video.h>
|
||||||
|
#include <gst/audio/audio.h>
|
||||||
|
|
||||||
#ifndef INITGUID
|
#ifndef INITGUID
|
||||||
#include <initguid.h>
|
#include <initguid.h>
|
||||||
|
@ -75,4 +76,4 @@ void _gst_mf_dump_attributes (IMFAttributes * attr,
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __GST_MF_UTILS_H__ */
|
#endif /* __GST_MF_UTILS_H__ */
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
mf_sources = [
|
mf_sources = [
|
||||||
|
'gstmfaacdec.cpp',
|
||||||
'gstmfaacenc.cpp',
|
'gstmfaacenc.cpp',
|
||||||
|
'gstmfaudiodecoder.cpp',
|
||||||
'gstmfaudioencoder.cpp',
|
'gstmfaudioencoder.cpp',
|
||||||
'gstmfdevice.cpp',
|
'gstmfdevice.cpp',
|
||||||
'gstmfh264enc.cpp',
|
'gstmfh264enc.cpp',
|
||||||
|
|
|
@ -72,6 +72,7 @@
|
||||||
#include "gstmfvp9enc.h"
|
#include "gstmfvp9enc.h"
|
||||||
#include "gstmfaacenc.h"
|
#include "gstmfaacenc.h"
|
||||||
#include "gstmfmp3enc.h"
|
#include "gstmfmp3enc.h"
|
||||||
|
#include "gstmfaacdec.h"
|
||||||
|
|
||||||
#if GST_MF_HAVE_D3D11
|
#if GST_MF_HAVE_D3D11
|
||||||
#include <gst/d3d11/gstd3d11.h>
|
#include <gst/d3d11/gstd3d11.h>
|
||||||
|
@ -237,6 +238,7 @@ plugin_init (GstPlugin * plugin)
|
||||||
|
|
||||||
gst_mf_aac_enc_plugin_init (plugin, GST_RANK_SECONDARY);
|
gst_mf_aac_enc_plugin_init (plugin, GST_RANK_SECONDARY);
|
||||||
gst_mf_mp3_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
|
/* So that call MFShutdown() when this plugin is no more used
|
||||||
* (i.e., gst_deinit). Otherwise valgrind-like tools would complain
|
* (i.e., gst_deinit). Otherwise valgrind-like tools would complain
|
||||||
|
|
Loading…
Reference in a new issue