* Use GST_PARAM_DOC_SHOW_DEFAULT flags for GPU ID related properties * Add doc caps * Add since markers Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/3250>
		
			
				
	
	
		
			293 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			293 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* 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
 | |
| 
 | |
| /**
 | |
|  * 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);
 | |
| }
 |