/* GStreamer * Copyright (C) 2022 Seungha Yang * * 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 #include "gstmfaudiodecoder.h" #include #include /* *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 /** * GstMFAudioDecoder: * * Base class for MediaFoundation audio decoders * * Since: 1.22 */ #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); }