mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-19 00:01:23 +00:00
cee619486a
Add MediaFoundation AAC encoder element. Before Windows 10, mono and stereo channels were supported audio channels configuration by AAC encoder MFT. However, on Windows 10, 5.1 channels support was introduced. To expose correct range of support format by this element whatever the OS version is, this element will enumerate all the supported format by the AAC encoder MFT and then will configure sink/src templates while plugin init. Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-bad/-/merge_requests/1280>
743 lines
21 KiB
C++
743 lines
21 KiB
C++
/* GStreamer
|
|
* Copyright (C) 2020 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-mfaacenc
|
|
* @title: mfaacenc
|
|
*
|
|
* This element encodes raw audio into AAC compressed data.
|
|
*
|
|
* ## Example pipelines
|
|
* |[
|
|
* gst-launch-1.0 -v audiotestsrc ! mfaacenc ! aacparse ! qtmux ! filesink location=audiotestsrc.mp4
|
|
* ]| This example pipeline will encode a test audio source to AAC using
|
|
* Media Foundation encoder, and muxes it in a mp4 container.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <gst/gst.h>
|
|
#include <gst/pbutils/pbutils.h>
|
|
#include "gstmfaudioenc.h"
|
|
#include "gstmfaacenc.h"
|
|
#include <wrl.h>
|
|
#include <set>
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
using namespace Microsoft::WRL;
|
|
|
|
GST_DEBUG_CATEGORY (gst_mf_aac_enc_debug);
|
|
#define GST_CAT_DEFAULT gst_mf_aac_enc_debug
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_BITRATE,
|
|
};
|
|
|
|
#define DEFAULT_BITRATE (0)
|
|
|
|
typedef struct _GstMFAacEnc
|
|
{
|
|
GstMFAudioEnc parent;
|
|
|
|
/* properteies */
|
|
guint bitrate;
|
|
} GstMFAacEnc;
|
|
|
|
typedef struct _GstMFAacEncClass
|
|
{
|
|
GstMFAudioEncClass parent_class;
|
|
|
|
} GstMFAacEncClass;
|
|
|
|
typedef struct
|
|
{
|
|
GstCaps *sink_caps;
|
|
GstCaps *src_caps;
|
|
gchar *device_name;
|
|
guint32 enum_flags;
|
|
guint device_index;
|
|
std::set<UINT32> bitrate_list;
|
|
} GstMFAacEncClassData;
|
|
|
|
static GstElementClass *parent_class = NULL;
|
|
|
|
static void gst_mf_aac_enc_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec);
|
|
static void gst_mf_aac_enc_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec);
|
|
static gboolean gst_mf_aac_enc_get_output_type (GstMFAudioEnc * mfenc,
|
|
GstAudioInfo * info, IMFMediaType ** output_type);
|
|
static gboolean gst_mf_aac_enc_get_input_type (GstMFAudioEnc * mfenc,
|
|
GstAudioInfo * info, IMFMediaType ** input_type);
|
|
static gboolean gst_mf_aac_enc_set_src_caps (GstMFAudioEnc * mfenc,
|
|
GstAudioInfo * info);
|
|
|
|
static void
|
|
gst_mf_aac_enc_class_init (GstMFAacEncClass * klass, gpointer data)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
|
|
GstMFAudioEncClass *mfenc_class = GST_MF_AUDIO_ENC_CLASS (klass);
|
|
GstMFAacEncClassData *cdata = (GstMFAacEncClassData *) data;
|
|
gchar *long_name;
|
|
gchar *classification;
|
|
guint max_bitrate = 0;
|
|
std::string bitrate_blurb;
|
|
|
|
parent_class = (GstElementClass *) g_type_class_peek_parent (klass);
|
|
|
|
gobject_class->get_property = gst_mf_aac_enc_get_property;
|
|
gobject_class->set_property = gst_mf_aac_enc_set_property;
|
|
|
|
bitrate_blurb =
|
|
"Bitrate in bit/sec, (0 = auto), valid values are { 0";
|
|
for (auto iter: cdata->bitrate_list) {
|
|
bitrate_blurb += ", " + std::to_string (iter);
|
|
/* std::set<> stores values in a sorted fashion */
|
|
max_bitrate = iter;
|
|
}
|
|
bitrate_blurb += " }";
|
|
|
|
g_object_class_install_property (gobject_class, PROP_BITRATE,
|
|
g_param_spec_uint ("bitrate", "Bitrate", bitrate_blurb.c_str(), 0,
|
|
max_bitrate, DEFAULT_BITRATE,
|
|
(GParamFlags) (GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
|
|
G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK)));
|
|
|
|
long_name = g_strdup_printf ("Media Foundation %s", cdata->device_name);
|
|
classification = g_strdup_printf ("Codec/Encoder/Audio%s",
|
|
(cdata->enum_flags & MFT_ENUM_FLAG_HARDWARE) == MFT_ENUM_FLAG_HARDWARE ?
|
|
"/Hardware" : "");
|
|
gst_element_class_set_metadata (element_class, long_name,
|
|
classification,
|
|
"Microsoft Media Foundation AAC Encoder",
|
|
"Seungha Yang <seungha@centricular.com>");
|
|
g_free (long_name);
|
|
g_free (classification);
|
|
|
|
gst_element_class_add_pad_template (element_class,
|
|
gst_pad_template_new ("sink", GST_PAD_SINK, GST_PAD_ALWAYS,
|
|
cdata->sink_caps));
|
|
gst_element_class_add_pad_template (element_class,
|
|
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
|
|
cdata->src_caps));
|
|
|
|
mfenc_class->get_output_type =
|
|
GST_DEBUG_FUNCPTR (gst_mf_aac_enc_get_output_type);
|
|
mfenc_class->get_input_type =
|
|
GST_DEBUG_FUNCPTR (gst_mf_aac_enc_get_input_type);
|
|
mfenc_class->set_src_caps =
|
|
GST_DEBUG_FUNCPTR (gst_mf_aac_enc_set_src_caps);
|
|
|
|
mfenc_class->codec_id = MFAudioFormat_AAC;
|
|
mfenc_class->enum_flags = cdata->enum_flags;
|
|
mfenc_class->device_index = cdata->device_index;
|
|
mfenc_class->frame_samples = 1024;
|
|
|
|
g_free (cdata->device_name);
|
|
gst_caps_unref (cdata->sink_caps);
|
|
gst_caps_unref (cdata->src_caps);
|
|
delete cdata;
|
|
}
|
|
|
|
static void
|
|
gst_mf_aac_enc_init (GstMFAacEnc * self)
|
|
{
|
|
self->bitrate = DEFAULT_BITRATE;
|
|
}
|
|
|
|
static void
|
|
gst_mf_aac_enc_get_property (GObject * object, guint prop_id,
|
|
GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstMFAacEnc *self = (GstMFAacEnc *) (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_BITRATE:
|
|
g_value_set_uint (value, self->bitrate);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_mf_aac_enc_set_property (GObject * object, guint prop_id,
|
|
const GValue * value, GParamSpec * pspec)
|
|
{
|
|
GstMFAacEnc *self = (GstMFAacEnc *) (object);
|
|
|
|
switch (prop_id) {
|
|
case PROP_BITRATE:
|
|
self->bitrate = g_value_get_uint (value);
|
|
break;
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
gst_mf_aac_enc_get_output_type (GstMFAudioEnc * mfenc, GstAudioInfo * info,
|
|
IMFMediaType ** output_type)
|
|
{
|
|
GstMFAacEnc *self = (GstMFAacEnc *) mfenc;
|
|
GstMFTransform *transform = mfenc->transform;
|
|
GList *output_list = NULL;
|
|
GList *iter;
|
|
ComPtr<IMFMediaType> target_output;
|
|
std::vector<ComPtr<IMFMediaType>> filtered_types;
|
|
std::set<UINT32> bitrate_list;
|
|
UINT32 bitrate;
|
|
UINT32 target_bitrate = 0;
|
|
HRESULT hr;
|
|
|
|
if (!gst_mf_transform_get_output_available_types (transform, &output_list)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't get available output type");
|
|
return FALSE;
|
|
}
|
|
|
|
/* 1. Filtering based on channels and sample rate */
|
|
for (iter = output_list; iter; iter = g_list_next (iter)) {
|
|
IMFMediaType *type = (IMFMediaType *) iter->data;
|
|
GUID guid = GUID_NULL;
|
|
UINT32 value;
|
|
|
|
hr = type->GetGUID (MF_MT_MAJOR_TYPE, &guid);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
if (!IsEqualGUID (guid, MFMediaType_Audio)) {
|
|
GST_WARNING_OBJECT (self, "Major type is not audio");
|
|
continue;
|
|
}
|
|
|
|
hr = type->GetGUID (MF_MT_SUBTYPE, &guid);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
if (!IsEqualGUID (guid, MFAudioFormat_AAC)) {
|
|
GST_WARNING_OBJECT (self, "Sub type is not AAC");
|
|
continue;
|
|
}
|
|
|
|
hr = type->GetUINT32 (MF_MT_AUDIO_NUM_CHANNELS, &value);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
if (value != GST_AUDIO_INFO_CHANNELS (info))
|
|
continue;
|
|
|
|
hr = type->GetUINT32 (MF_MT_AUDIO_SAMPLES_PER_SECOND, &value);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
if (value != GST_AUDIO_INFO_RATE (info))
|
|
continue;
|
|
|
|
hr = type->GetUINT32 (MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &value);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
filtered_types.push_back (type);
|
|
/* convert bytes to bit */
|
|
bitrate_list.insert (value * 8);
|
|
}
|
|
|
|
g_list_free_full (output_list, (GDestroyNotify) gst_mf_media_type_release);
|
|
|
|
if (filtered_types.empty()) {
|
|
GST_ERROR_OBJECT (self, "Couldn't find target output type");
|
|
return FALSE;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (self, "have %d candidate output", filtered_types.size());
|
|
|
|
/* 2. Find the best matching bitrate */
|
|
bitrate = self->bitrate;
|
|
|
|
/* Media Foundation AAC encoder supports sample-rate 44100 or 48000 */
|
|
if (bitrate == 0) {
|
|
/* http://wiki.hydrogenaud.io/index.php?title=Fraunhofer_FDK_AAC#Recommended_Sampling_Rate_and_Bitrate_Combinations
|
|
* was referenced but the supported range by MediaFoudation is much limited
|
|
* than it */
|
|
if (GST_AUDIO_INFO_CHANNELS (info) == 1) {
|
|
if (GST_AUDIO_INFO_RATE (info) <= 44100) {
|
|
bitrate = 96000;
|
|
} else {
|
|
bitrate = 160000;
|
|
}
|
|
} else if (GST_AUDIO_INFO_CHANNELS (info) == 2) {
|
|
if (GST_AUDIO_INFO_RATE (info) <= 44100) {
|
|
bitrate = 112000;
|
|
} else {
|
|
bitrate = 320000;
|
|
}
|
|
} else {
|
|
/* 5.1 */
|
|
if (GST_AUDIO_INFO_RATE (info) <= 44100) {
|
|
bitrate = 240000;
|
|
} else {
|
|
bitrate = 320000;
|
|
}
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (self, "Calculated bitrate %d", bitrate);
|
|
} else {
|
|
GST_DEBUG_OBJECT (self, "Requested bitrate %d", bitrate);
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (self, "Available bitrates");
|
|
for (auto it: bitrate_list)
|
|
GST_DEBUG_OBJECT (self, "\t%d", it);
|
|
|
|
/* Based on calculated or requested bitrate, find the closest supported
|
|
* bitrate */
|
|
{
|
|
const auto it = bitrate_list.lower_bound (bitrate);
|
|
if (it == bitrate_list.end()) {
|
|
target_bitrate = *std::prev (it);
|
|
} else {
|
|
target_bitrate = *it;
|
|
}
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (self, "Selected target bitrate %d", target_bitrate);
|
|
|
|
for (auto it: filtered_types) {
|
|
UINT32 value = 0;
|
|
|
|
it->GetUINT32 (MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &value);
|
|
if (value * 8 == target_bitrate) {
|
|
target_output = it;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!target_output) {
|
|
GST_ERROR_OBJECT (self, "Failed to decide final output type");
|
|
return FALSE;
|
|
}
|
|
|
|
*output_type = target_output.Detach();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_mf_aac_enc_get_input_type (GstMFAudioEnc * mfenc, GstAudioInfo * info,
|
|
IMFMediaType ** input_type)
|
|
{
|
|
GstMFAacEnc *self = (GstMFAacEnc *) mfenc;
|
|
GstMFTransform *transform = mfenc->transform;
|
|
GList *input_list = NULL;
|
|
GList *iter;
|
|
ComPtr<IMFMediaType> target_input;
|
|
std::vector<ComPtr<IMFMediaType>> filtered_types;
|
|
std::set<UINT32> bitrate_list;
|
|
HRESULT hr;
|
|
|
|
if (!gst_mf_transform_get_input_available_types (transform, &input_list)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't get available output type");
|
|
return FALSE;
|
|
}
|
|
|
|
/* 1. Filtering based on channels and sample rate */
|
|
for (iter = input_list; iter; iter = g_list_next (iter)) {
|
|
IMFMediaType *type = (IMFMediaType *) iter->data;
|
|
GUID guid = GUID_NULL;
|
|
UINT32 value;
|
|
|
|
hr = type->GetGUID (MF_MT_MAJOR_TYPE, &guid);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
if (!IsEqualGUID (guid, MFMediaType_Audio)) {
|
|
GST_WARNING_OBJECT (self, "Major type is not audio");
|
|
continue;
|
|
}
|
|
|
|
hr = type->GetGUID (MF_MT_SUBTYPE, &guid);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
if (!IsEqualGUID (guid, MFAudioFormat_PCM)) {
|
|
GST_WARNING_OBJECT (self, "Sub type is not PCM");
|
|
continue;
|
|
}
|
|
|
|
hr = type->GetUINT32 (MF_MT_AUDIO_NUM_CHANNELS, &value);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
if (value != GST_AUDIO_INFO_CHANNELS (info))
|
|
continue;
|
|
|
|
hr = type->GetUINT32 (MF_MT_AUDIO_SAMPLES_PER_SECOND, &value);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
if (value != GST_AUDIO_INFO_RATE (info))
|
|
continue;
|
|
|
|
filtered_types.push_back (type);
|
|
}
|
|
|
|
g_list_free_full (input_list, (GDestroyNotify) gst_mf_media_type_release);
|
|
|
|
if (filtered_types.empty()) {
|
|
GST_ERROR_OBJECT (self, "Couldn't find target input type");
|
|
return FALSE;
|
|
}
|
|
|
|
GST_DEBUG_OBJECT (self, "Total %d input types are available",
|
|
filtered_types.size());
|
|
|
|
/* Just select the first one */
|
|
target_input = *filtered_types.begin();
|
|
|
|
*input_type = target_input.Detach();
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gst_mf_aac_enc_set_src_caps (GstMFAudioEnc * mfenc,
|
|
GstAudioInfo * info)
|
|
{
|
|
GstMFAacEnc *self = (GstMFAacEnc *) mfenc;
|
|
HRESULT hr;
|
|
GstCaps *src_caps;
|
|
GstBuffer *codec_data;
|
|
UINT8 *blob = NULL;
|
|
UINT32 blob_size = 0;
|
|
gboolean ret;
|
|
ComPtr<IMFMediaType> output_type;
|
|
static const guint config_data_offset = 12;
|
|
|
|
if (!gst_mf_transform_get_output_current_type (mfenc->transform, &output_type)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't get current output type");
|
|
return FALSE;
|
|
}
|
|
|
|
/* user data contains the portion of the HEAACWAVEINFO structure that appears
|
|
* after the WAVEFORMATEX structure (that is, after the wfx member).
|
|
* This is followed by the AudioSpecificConfig() data,
|
|
* as defined by ISO/IEC 14496-3.
|
|
* https://docs.microsoft.com/en-us/windows/win32/medfound/aac-encoder
|
|
*
|
|
* The offset AudioSpecificConfig() data is 12 in this case
|
|
*/
|
|
hr = output_type->GetBlobSize (MF_MT_USER_DATA, &blob_size);
|
|
if (!gst_mf_result (hr) || blob_size <= config_data_offset) {
|
|
GST_ERROR_OBJECT (self,
|
|
"Couldn't get size of MF_MT_USER_DATA, size %d, %d", blob_size);
|
|
return FALSE;
|
|
}
|
|
|
|
hr = output_type->GetAllocatedBlob (MF_MT_USER_DATA, &blob, &blob_size);
|
|
if (!gst_mf_result (hr)) {
|
|
GST_ERROR_OBJECT (self, "Couldn't get user data blob");
|
|
return FALSE;
|
|
}
|
|
|
|
codec_data = gst_buffer_new_and_alloc (blob_size - config_data_offset);
|
|
gst_buffer_fill (codec_data, 0, blob + config_data_offset,
|
|
blob_size - config_data_offset);
|
|
|
|
src_caps = gst_caps_new_simple ("audio/mpeg",
|
|
"mpegversion", G_TYPE_INT, 4,
|
|
"stream-format", G_TYPE_STRING, "raw",
|
|
"channels", G_TYPE_INT, GST_AUDIO_INFO_CHANNELS (info),
|
|
"rate", G_TYPE_INT, GST_AUDIO_INFO_RATE (info),
|
|
"framed", G_TYPE_BOOLEAN, TRUE,
|
|
"codec_data", GST_TYPE_BUFFER, codec_data, NULL);
|
|
gst_buffer_unref (codec_data);
|
|
|
|
gst_codec_utils_aac_caps_set_level_and_profile (src_caps,
|
|
blob + config_data_offset, blob_size - config_data_offset);
|
|
CoTaskMemFree (blob);
|
|
|
|
ret = gst_audio_encoder_set_output_format (GST_AUDIO_ENCODER (self), src_caps);
|
|
if (!ret) {
|
|
GST_WARNING_OBJECT (self,
|
|
"Couldn't set output format %" GST_PTR_FORMAT, src_caps);
|
|
}
|
|
gst_caps_unref (src_caps);
|
|
|
|
return ret;
|
|
}
|
|
|
|
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)
|
|
{
|
|
GType type;
|
|
gchar *type_name;
|
|
gchar *feature_name;
|
|
gint i;
|
|
GstMFAacEncClassData *cdata;
|
|
gboolean is_default = TRUE;
|
|
GTypeInfo type_info = {
|
|
sizeof (GstMFAacEncClass),
|
|
NULL,
|
|
NULL,
|
|
(GClassInitFunc) gst_mf_aac_enc_class_init,
|
|
NULL,
|
|
NULL,
|
|
sizeof (GstMFAacEnc),
|
|
0,
|
|
(GInstanceInitFunc) gst_mf_aac_enc_init,
|
|
};
|
|
|
|
cdata = new GstMFAacEncClassData;
|
|
cdata->sink_caps = sink_caps;
|
|
cdata->src_caps = src_caps;
|
|
cdata->device_name = g_strdup (device_name);
|
|
cdata->enum_flags = enum_flags;
|
|
cdata->device_index = device_index;
|
|
cdata->bitrate_list = bitrate_list;
|
|
type_info.class_data = cdata;
|
|
|
|
type_name = g_strdup ("GstMFAacEnc");
|
|
feature_name = g_strdup ("mfaacenc");
|
|
|
|
i = 1;
|
|
while (g_type_from_name (type_name) != 0) {
|
|
g_free (type_name);
|
|
g_free (feature_name);
|
|
type_name = g_strdup_printf ("GstMFAacDevice%dEnc", i);
|
|
feature_name = g_strdup_printf ("mfaacdevice%denc", i);
|
|
is_default = FALSE;
|
|
i++;
|
|
}
|
|
|
|
type =
|
|
g_type_register_static (GST_TYPE_MF_AUDIO_ENC, type_name, &type_info,
|
|
(GTypeFlags) 0);
|
|
|
|
/* make lower rank than default device */
|
|
if (rank > 0 && !is_default)
|
|
rank--;
|
|
|
|
if (!gst_element_register (plugin, feature_name, rank, type))
|
|
GST_WARNING ("Failed to register plugin '%s'", type_name);
|
|
|
|
g_free (type_name);
|
|
g_free (feature_name);
|
|
}
|
|
|
|
static void
|
|
gst_mf_aac_enc_plugin_init_internal (GstPlugin * plugin, guint rank,
|
|
GstMFTransform * transform, guint device_index, guint32 enum_flags)
|
|
{
|
|
HRESULT hr;
|
|
gint i;
|
|
GstCaps *src_caps = NULL;
|
|
GstCaps *sink_caps = NULL;
|
|
gchar *device_name = NULL;
|
|
GList *output_list = NULL;
|
|
GList *iter;
|
|
std::set<UINT32> channels_list;
|
|
std::set<UINT32> rate_list;
|
|
std::set<UINT32> bitrate_list;
|
|
gboolean config_found = FALSE;
|
|
GValue channles_value = G_VALUE_INIT;
|
|
GValue rate_value = G_VALUE_INIT;
|
|
|
|
if (!gst_mf_transform_open (transform))
|
|
return;
|
|
|
|
g_object_get (transform, "device-name", &device_name, NULL);
|
|
if (!device_name) {
|
|
GST_WARNING_OBJECT (transform, "Unknown device name");
|
|
return;
|
|
}
|
|
|
|
if (!gst_mf_transform_get_output_available_types (transform, &output_list)) {
|
|
GST_WARNING_OBJECT (transform, "Couldn't get output types");
|
|
goto done;
|
|
}
|
|
|
|
GST_INFO_OBJECT (transform, "Have %d output type", g_list_length (output_list));
|
|
|
|
for (iter = output_list, i = 0; iter; iter = g_list_next (iter), i++) {
|
|
UINT32 channels, rate, bitrate;
|
|
GUID guid = GUID_NULL;
|
|
IMFMediaType *type = (IMFMediaType *) iter->data;
|
|
#ifndef GST_DISABLE_GST_DEBUG
|
|
gchar *msg = g_strdup_printf ("Output IMFMediaType %d", i);
|
|
gst_mf_dump_attributes ((IMFAttributes *) type, msg, GST_LEVEL_TRACE);
|
|
g_free (msg);
|
|
#endif
|
|
|
|
hr = type->GetGUID (MF_MT_MAJOR_TYPE, &guid);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
/* shouldn't happen */
|
|
if (!IsEqualGUID (guid, MFMediaType_Audio))
|
|
continue;
|
|
|
|
hr = type->GetGUID (MF_MT_SUBTYPE, &guid);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
/* shouldn't happen */
|
|
if (!IsEqualGUID (guid, MFAudioFormat_AAC))
|
|
continue;
|
|
|
|
/* Windows 10 channels 6 (5.1) channels so we cannot hard code it */
|
|
hr = type->GetUINT32 (MF_MT_AUDIO_NUM_CHANNELS, &channels);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
hr = type->GetUINT32 (MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
/* NOTE: MFT AAC encoder seems to support more bitrate than it's documented
|
|
* at https://docs.microsoft.com/en-us/windows/win32/medfound/aac-encoder
|
|
* We will pass supported bitrate values to class init
|
|
*/
|
|
hr = type->GetUINT32 (MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &bitrate);
|
|
if (!gst_mf_result (hr))
|
|
continue;
|
|
|
|
channels_list.insert (channels);
|
|
rate_list.insert (rate);
|
|
/* convert bytes to bit */
|
|
bitrate_list.insert (bitrate * 8);
|
|
|
|
config_found = TRUE;
|
|
}
|
|
|
|
if (!config_found) {
|
|
GST_WARNING_OBJECT (transform, "Couldn't find available configuration");
|
|
goto done;
|
|
}
|
|
|
|
src_caps =
|
|
gst_caps_from_string ("audio/mpeg, mpegversion = (int) 4, "
|
|
"stream-format = (string) raw, framed = (boolean) true, "
|
|
"base-profile = (string) lc");
|
|
sink_caps =
|
|
gst_caps_from_string ("audio/x-raw, layout = (string) interleaved, "
|
|
"format = (string) " GST_AUDIO_NE (S16));
|
|
|
|
g_value_init (&channles_value, GST_TYPE_LIST);
|
|
g_value_init (&rate_value, GST_TYPE_LIST);
|
|
|
|
for (auto it: channels_list) {
|
|
GValue channles = G_VALUE_INIT;
|
|
|
|
g_value_init (&channles, G_TYPE_INT);
|
|
g_value_set_int (&channles, (gint) it);
|
|
gst_value_list_append_and_take_value (&channles_value, &channles);
|
|
}
|
|
|
|
for (auto it: rate_list) {
|
|
GValue rate = G_VALUE_INIT;
|
|
|
|
g_value_init (&rate, G_TYPE_INT);
|
|
g_value_set_int (&rate, (gint) it);
|
|
gst_value_list_append_and_take_value (&rate_value, &rate);
|
|
}
|
|
|
|
gst_caps_set_value (src_caps, "channels", &channles_value);
|
|
gst_caps_set_value (sink_caps, "channels", &channles_value);
|
|
|
|
gst_caps_set_value (src_caps, "rate", &rate_value);
|
|
gst_caps_set_value (sink_caps, "rate", &rate_value);
|
|
|
|
GST_MINI_OBJECT_FLAG_SET (sink_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
|
|
GST_MINI_OBJECT_FLAG_SET (src_caps, GST_MINI_OBJECT_FLAG_MAY_BE_LEAKED);
|
|
|
|
gst_mf_aac_enc_register (plugin, rank, device_name, enum_flags, device_index,
|
|
sink_caps, src_caps, bitrate_list);
|
|
|
|
done:
|
|
if (output_list)
|
|
g_list_free_full (output_list, (GDestroyNotify) gst_mf_media_type_release);
|
|
g_free (device_name);
|
|
g_value_unset (&channles_value);
|
|
g_value_unset (&rate_value);
|
|
}
|
|
|
|
void
|
|
gst_mf_aac_enc_plugin_init (GstPlugin * plugin, guint rank)
|
|
{
|
|
GstMFTransformEnumParams enum_params = { 0, };
|
|
MFT_REGISTER_TYPE_INFO output_type;
|
|
GstMFTransform *transform;
|
|
gint i;
|
|
gboolean do_next;
|
|
|
|
GST_DEBUG_CATEGORY_INIT (gst_mf_aac_enc_debug, "mfaacenc", 0, "mfaacenc");
|
|
|
|
output_type.guidMajorType = MFMediaType_Audio;
|
|
output_type.guidSubtype = MFAudioFormat_AAC;
|
|
|
|
enum_params.category = MFT_CATEGORY_AUDIO_ENCODER;
|
|
enum_params.enum_flags = (MFT_ENUM_FLAG_HARDWARE | MFT_ENUM_FLAG_ASYNCMFT |
|
|
MFT_ENUM_FLAG_SORTANDFILTER | MFT_ENUM_FLAG_SORTANDFILTER_APPROVED_ONLY);
|
|
enum_params.output_typeinfo = &output_type;
|
|
|
|
/* register hardware encoders first (likey no hardware audio encoder) */
|
|
i = 0;
|
|
do {
|
|
enum_params.device_index = i++;
|
|
transform = gst_mf_transform_new (&enum_params);
|
|
do_next = TRUE;
|
|
|
|
if (!transform) {
|
|
do_next = FALSE;
|
|
} else {
|
|
gst_mf_aac_enc_plugin_init_internal (plugin, rank, transform,
|
|
enum_params.device_index, enum_params.enum_flags);
|
|
gst_clear_object (&transform);
|
|
}
|
|
} while (do_next);
|
|
|
|
/* register software encoders */
|
|
enum_params.enum_flags = (MFT_ENUM_FLAG_SYNCMFT |
|
|
MFT_ENUM_FLAG_SORTANDFILTER | MFT_ENUM_FLAG_SORTANDFILTER_APPROVED_ONLY);
|
|
i = 0;
|
|
do {
|
|
enum_params.device_index = i++;
|
|
transform = gst_mf_transform_new (&enum_params);
|
|
do_next = TRUE;
|
|
|
|
if (!transform) {
|
|
do_next = FALSE;
|
|
} else {
|
|
gst_mf_aac_enc_plugin_init_internal (plugin, rank, transform,
|
|
enum_params.device_index, enum_params.enum_flags);
|
|
gst_clear_object (&transform);
|
|
}
|
|
} while (do_next);
|
|
}
|