| /* GStreamer Intel MSDK plugin | |
| * Copyright (c) 2016, Intel Corporation | |
| * All rights reserved. | |
| * | |
| * Redistribution and use in source and binary forms, with or without | |
| * modification, are permitted provided that the following conditions are met: | |
| * | |
| * 1. Redistributions of source code must retain the above copyright notice, | |
| * this list of conditions and the following disclaimer. | |
| * | |
| * 2. Redistributions in binary form must reproduce the above copyright notice, | |
| * this list of conditions and the following disclaimer in the documentation | |
| * and/or other materials provided with the distribution. | |
| * | |
| * 3. Neither the name of the copyright holder nor the names of its contributors | |
| * may be used to endorse or promote products derived from this software | |
| * without specific prior written permission. | |
| * | |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR | |
| * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
| * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE | |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, | |
| * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| */ | |
| #ifdef HAVE_CONFIG_H | |
| # include <config.h> | |
| #endif | |
| #include <stdlib.h> | |
| #include "gstmsdkdec.h" | |
| #include "gstmsdkcontextutil.h" | |
| #include "gstmsdkallocator.h" | |
| #include "gstmsdkcaps.h" | |
| #ifndef _WIN32 | |
| #include <drm_fourcc.h> | |
| #include <gst/va/gstvaallocator.h> | |
| #include <gst/va/gstvavideoformat.h> | |
| #else | |
| #include <gst/d3d11/gstd3d11.h> | |
| #endif | |
| GST_DEBUG_CATEGORY_EXTERN (gst_msdkdec_debug); | |
| #define GST_CAT_DEFAULT gst_msdkdec_debug | |
| #define PROP_HARDWARE_DEFAULT TRUE | |
| #define PROP_ASYNC_DEPTH_DEFAULT 1 | |
| #define IS_ALIGNED(i, n) (((i) & ((n)-1)) == 0) | |
| #define GST_TO_MFX_TIME(time) ((time) == GST_CLOCK_TIME_NONE ? \ | |
| MFX_TIMESTAMP_UNKNOWN : gst_util_uint64_scale_round ((time), 9, 100000)) | |
| #define MFX_TO_GST_TIME(time) ((time) == MFX_TIMESTAMP_UNKNOWN ? \ | |
| GST_CLOCK_TIME_NONE : gst_util_uint64_scale_round ((time), 100000, 9)) | |
| #define MFX_TIME_IS_VALID(time) ((time) != MFX_TIMESTAMP_UNKNOWN) | |
| #define GST_MSDK_FRAME_SURFACE gst_msdk_frame_surface_quark_get () | |
| #define gst_msdkdec_parent_class parent_class | |
| G_DEFINE_TYPE (GstMsdkDec, gst_msdkdec, GST_TYPE_VIDEO_DECODER); | |
| struct _MsdkDecTask | |
| { | |
| GstMsdkSurface *surface; | |
| mfxSyncPoint sync_point; | |
| gboolean decode_only; | |
| }; | |
| static GstFlowReturn gst_msdkdec_drain (GstVideoDecoder * decoder); | |
| static gboolean gst_msdkdec_flush (GstVideoDecoder * decoder); | |
| static gboolean gst_msdkdec_negotiate (GstMsdkDec * thiz, gboolean hard_reset); | |
| void | |
| gst_msdkdec_add_bs_extra_param (GstMsdkDec * thiz, mfxExtBuffer * param) | |
| { | |
| if (thiz->num_bs_extra_params < MAX_BS_EXTRA_PARAMS) { | |
| thiz->bs_extra_params[thiz->num_bs_extra_params] = param; | |
| thiz->num_bs_extra_params++; | |
| } | |
| } | |
| void | |
| gst_msdkdec_add_video_extra_param (GstMsdkDec * thiz, mfxExtBuffer * param) | |
| { | |
| if (thiz->num_video_extra_params < MAX_VIDEO_EXTRA_PARAMS) { | |
| thiz->video_extra_params[thiz->num_video_extra_params] = param; | |
| thiz->num_video_extra_params++; | |
| } | |
| } | |
| static GstVideoCodecFrame * | |
| gst_msdkdec_get_oldest_frame (GstVideoDecoder * decoder) | |
| { | |
| GstVideoCodecFrame *frame = NULL, *old_frame = NULL; | |
| GList *frames, *l; | |
| gint count = 0; | |
| frames = gst_video_decoder_get_frames (decoder); | |
| for (l = frames; l != NULL; l = l->next) { | |
| GstVideoCodecFrame *f = l->data; | |
| if (!GST_CLOCK_TIME_IS_VALID (f->pts)) { | |
| GST_INFO | |
| ("Frame doesn't have a valid pts yet, Use gst_video_decoder_get_oldest_frame()" | |
| "with out considering the PTS for selecting the frame to be finished"); | |
| old_frame = gst_video_decoder_get_oldest_frame (decoder); | |
| break; | |
| } | |
| if (!frame || frame->pts > f->pts) | |
| frame = f; | |
| count++; | |
| } | |
| if (old_frame) | |
| frame = old_frame; | |
| if (frame) { | |
| GST_LOG_OBJECT (decoder, | |
| "Oldest frame is %d %" GST_TIME_FORMAT " and %d frames left", | |
| frame->system_frame_number, GST_TIME_ARGS (frame->pts), count - 1); | |
| gst_video_codec_frame_ref (frame); | |
| } | |
| if (old_frame) | |
| gst_video_codec_frame_unref (old_frame); | |
| g_list_free_full (frames, (GDestroyNotify) gst_video_codec_frame_unref); | |
| return frame; | |
| } | |
| static inline void | |
| free_surface (GstMsdkSurface * s) | |
| { | |
| gst_buffer_unref (s->buf); | |
| g_slice_free (GstMsdkSurface, s); | |
| } | |
| static gboolean | |
| gst_msdkdec_free_unlocked_msdk_surfaces (GstMsdkDec * thiz, | |
| gboolean check_avail_surface) | |
| { | |
| GList *l; | |
| GstMsdkSurface *surface; | |
| for (l = thiz->locked_msdk_surfaces; l;) { | |
| GList *next = l->next; | |
| surface = l->data; | |
| if (surface->surface->Data.Locked == 0 && | |
| GST_MINI_OBJECT_REFCOUNT_VALUE (surface->buf) == 1) { | |
| free_surface (surface); | |
| thiz->locked_msdk_surfaces = | |
| g_list_delete_link (thiz->locked_msdk_surfaces, l); | |
| /* When check_avail_surface flag is enabled, it means we only | |
| * need to find one available surface instead of releasing all | |
| * the unlocked surfaces, so we can return TEUR here. | |
| */ | |
| if (check_avail_surface) | |
| return TRUE; | |
| } | |
| l = next; | |
| } | |
| /* We need to check if all surfaces are in used */ | |
| if (g_list_length (thiz->locked_msdk_surfaces) == | |
| thiz->alloc_resp.NumFrameActual) | |
| return FALSE; | |
| else | |
| return TRUE; | |
| } | |
| static GstMsdkSurface * | |
| allocate_output_surface (GstMsdkDec * thiz) | |
| { | |
| GstMsdkSurface *msdk_surface = NULL; | |
| GstBuffer *out_buffer = NULL; | |
| GstMemory *mem = NULL; | |
| mfxFrameSurface1 *mfx_surface = NULL; | |
| gint n = 0; | |
| guint retry_times = 1000; | |
| #ifdef _WIN32 | |
| GstMapInfo map_info; | |
| #endif | |
| /* Free un-unsed msdk surfaces firstly, hence the associated mfx | |
| * surfaces will be moved from used list to available list */ | |
| if (!gst_msdkdec_free_unlocked_msdk_surfaces (thiz, FALSE)) { | |
| for (n = 0; n < retry_times; n++) { | |
| /* It is MediaSDK/oneVPL's requirement that only the pre-allocated | |
| * surfaces can be used during the whole decoding process. | |
| * In the case of decoder plus multi-encoders, it is possible | |
| * that all surfaces are used by downstreams and no more surfaces | |
| * available for decoder. So here we need to wait until there is at | |
| * least one surface is free for decoder. | |
| */ | |
| g_usleep (1000); | |
| if (gst_msdkdec_free_unlocked_msdk_surfaces (thiz, TRUE)) | |
| break; | |
| } | |
| if (n == retry_times) { | |
| GST_WARNING ("No available unlocked msdk surfaces"); | |
| return NULL; | |
| } | |
| } | |
| if ((gst_buffer_pool_acquire_buffer (thiz->alloc_pool, &out_buffer, NULL)) | |
| != GST_FLOW_OK) { | |
| GST_ERROR_OBJECT (thiz, "Failed to allocate output buffer"); | |
| return NULL; | |
| } | |
| #ifdef _WIN32 | |
| /* For d3d11 we should call gst_buffer_map with GST_MAP_WRITE | | |
| * GST_MAP_D3D11 flags to make sure the staging texture has been uploaded | |
| */ | |
| if (!gst_buffer_map (out_buffer, &map_info, GST_MAP_WRITE | GST_MAP_D3D11)) { | |
| GST_ERROR ("Failed to map buffer"); | |
| return NULL; | |
| } | |
| #endif | |
| mem = gst_buffer_peek_memory (out_buffer, 0); | |
| msdk_surface = g_slice_new0 (GstMsdkSurface); | |
| if ((mfx_surface = gst_mini_object_get_qdata (GST_MINI_OBJECT_CAST (mem), | |
| GST_MSDK_FRAME_SURFACE))) { | |
| msdk_surface->surface = mfx_surface; | |
| msdk_surface->from_qdata = TRUE; | |
| #ifdef _WIN32 | |
| gst_buffer_unmap (out_buffer, &map_info); | |
| #endif | |
| } else { | |
| GST_ERROR ("No available surfaces"); | |
| g_slice_free (GstMsdkSurface, msdk_surface); | |
| return NULL; | |
| } | |
| msdk_surface->buf = out_buffer; | |
| if (!thiz->sfc) | |
| gst_msdk_update_mfx_frame_info_from_mfx_video_param | |
| (&msdk_surface->surface->Info, &thiz->param); | |
| thiz->locked_msdk_surfaces = | |
| g_list_append (thiz->locked_msdk_surfaces, msdk_surface); | |
| return msdk_surface; | |
| } | |
| static void | |
| gst_msdkdec_close_decoder (GstMsdkDec * thiz, gboolean reset_param) | |
| { | |
| mfxStatus status; | |
| if (!thiz->context || !thiz->initialized) | |
| return; | |
| GST_DEBUG_OBJECT (thiz, "Closing decoder with context %" GST_PTR_FORMAT, | |
| thiz->context); | |
| gst_msdk_frame_free (thiz->context, &thiz->alloc_resp); | |
| status = MFXVideoDECODE_Close (gst_msdk_context_get_session (thiz->context)); | |
| if (status != MFX_ERR_NONE && status != MFX_ERR_NOT_INITIALIZED) { | |
| GST_WARNING_OBJECT (thiz, "Decoder close failed (%s)", | |
| msdk_status_to_string (status)); | |
| } | |
| g_array_set_size (thiz->tasks, 0); | |
| if (reset_param) | |
| memset (&thiz->param, 0, sizeof (thiz->param)); | |
| thiz->num_bs_extra_params = 0; | |
| thiz->num_video_extra_params = 0; | |
| thiz->initialized = FALSE; | |
| gst_adapter_clear (thiz->adapter); | |
| } | |
| static void | |
| gst_msdkdec_set_context (GstElement * element, GstContext * context) | |
| { | |
| GstMsdkContext *msdk_context = NULL; | |
| GstMsdkDec *thiz = GST_MSDKDEC (element); | |
| if (gst_msdk_context_get_context (context, &msdk_context)) { | |
| gst_object_replace ((GstObject **) & thiz->context, | |
| (GstObject *) msdk_context); | |
| gst_object_unref (msdk_context); | |
| } else | |
| #ifndef _WIN32 | |
| if (gst_msdk_context_from_external_va_display (context, | |
| thiz->hardware, 0 /* GST_MSDK_JOB_DECODER will be set later */ , | |
| &msdk_context)) { | |
| gst_object_replace ((GstObject **) & thiz->context, | |
| (GstObject *) msdk_context); | |
| gst_object_unref (msdk_context); | |
| } | |
| #else | |
| if (gst_msdk_context_from_external_d3d11_device (context, | |
| thiz->hardware, 0 /* GST_MSDK_JOB_DECODER will be set later */ , | |
| &msdk_context)) { | |
| gst_object_replace ((GstObject **) & thiz->context, | |
| (GstObject *) msdk_context); | |
| gst_object_unref (msdk_context); | |
| } | |
| #endif | |
| GST_ELEMENT_CLASS (parent_class)->set_context (element, context); | |
| } | |
| static gboolean | |
| gst_msdkdec_init_decoder (GstMsdkDec * thiz) | |
| { | |
| GstMsdkDecClass *klass = GST_MSDKDEC_GET_CLASS (thiz); | |
| GstVideoInfo *info, *output_info; | |
| mfxSession session; | |
| mfxStatus status; | |
| mfxFrameAllocRequest request; | |
| gint shared_async_depth; | |
| #if (MFX_VERSION >= 1022) | |
| mfxExtDecVideoProcessing ext_dec_video_proc; | |
| #endif | |
| if (thiz->initialized) | |
| return TRUE; | |
| if (!thiz->context) { | |
| GST_WARNING_OBJECT (thiz, "No MSDK Context"); | |
| return FALSE; | |
| } | |
| if (!thiz->input_state) { | |
| GST_DEBUG_OBJECT (thiz, "Have no input state yet"); | |
| return FALSE; | |
| } | |
| info = &thiz->input_state->info; | |
| output_info = &thiz->output_state->info; | |
| GST_OBJECT_LOCK (thiz); | |
| gst_msdk_set_frame_allocator (thiz->context); | |
| thiz->param.IOPattern = MFX_IOPATTERN_OUT_VIDEO_MEMORY; | |
| thiz->param.AsyncDepth = thiz->async_depth; | |
| /* We expect msdk to fill the width and height values */ | |
| if (!(thiz->param.mfx.FrameInfo.Width && thiz->param.mfx.FrameInfo.Height)) | |
| goto failed; | |
| klass->preinit_decoder (thiz); | |
| /* Set frame rate only if provided. | |
| * If not, frame rate will be assumed inside the driver. | |
| * Also we respect the upstream provided fps values */ | |
| if (info->fps_n > 0 && info->fps_d > 0 | |
| && info->fps_n != thiz->param.mfx.FrameInfo.FrameRateExtN | |
| && info->fps_d != thiz->param.mfx.FrameInfo.FrameRateExtD) { | |
| thiz->param.mfx.FrameInfo.FrameRateExtN = info->fps_n; | |
| thiz->param.mfx.FrameInfo.FrameRateExtD = info->fps_d; | |
| } | |
| if (info->par_n && info->par_d && !thiz->param.mfx.FrameInfo.AspectRatioW | |
| && !thiz->param.mfx.FrameInfo.AspectRatioH) { | |
| thiz->param.mfx.FrameInfo.AspectRatioW = info->par_n; | |
| thiz->param.mfx.FrameInfo.AspectRatioH = info->par_d; | |
| } | |
| thiz->param.mfx.FrameInfo.FourCC = | |
| thiz->param.mfx.FrameInfo.FourCC ? thiz->param.mfx. | |
| FrameInfo.FourCC : MFX_FOURCC_NV12; | |
| thiz->param.mfx.FrameInfo.ChromaFormat = | |
| thiz->param.mfx.FrameInfo.ChromaFormat ? thiz->param.mfx. | |
| FrameInfo.ChromaFormat : MFX_CHROMAFORMAT_YUV420; | |
| #if (MFX_VERSION >= 1022) | |
| if (output_info && thiz->sfc) { | |
| memset (&ext_dec_video_proc, 0, sizeof (ext_dec_video_proc)); | |
| ext_dec_video_proc.Header.BufferId = MFX_EXTBUFF_DEC_VIDEO_PROCESSING; | |
| ext_dec_video_proc.Header.BufferSz = sizeof (ext_dec_video_proc); | |
| ext_dec_video_proc.In.CropW = thiz->param.mfx.FrameInfo.CropW; | |
| ext_dec_video_proc.In.CropH = thiz->param.mfx.FrameInfo.CropH; | |
| ext_dec_video_proc.In.CropX = 0; | |
| ext_dec_video_proc.In.CropY = 0; | |
| ext_dec_video_proc.Out.FourCC = | |
| gst_msdk_get_mfx_fourcc_from_format (output_info->finfo->format); | |
| ext_dec_video_proc.Out.ChromaFormat = | |
| gst_msdk_get_mfx_chroma_from_format (output_info->finfo->format); | |
| ext_dec_video_proc.Out.Width = GST_ROUND_UP_16 (output_info->width); | |
| ext_dec_video_proc.Out.Height = GST_ROUND_UP_32 (output_info->height); | |
| ext_dec_video_proc.Out.CropW = output_info->width; | |
| ext_dec_video_proc.Out.CropH = output_info->height; | |
| ext_dec_video_proc.Out.CropX = 0; | |
| ext_dec_video_proc.Out.CropY = 0; | |
| gst_msdkdec_add_video_extra_param (thiz, | |
| (mfxExtBuffer *) & ext_dec_video_proc); | |
| } | |
| #endif | |
| if (thiz->num_video_extra_params) { | |
| thiz->param.NumExtParam = thiz->num_video_extra_params; | |
| thiz->param.ExtParam = thiz->video_extra_params; | |
| } | |
| session = gst_msdk_context_get_session (thiz->context); | |
| /* validate parameters and allow MFX to make adjustments */ | |
| status = MFXVideoDECODE_Query (session, &thiz->param, &thiz->param); | |
| if (status < MFX_ERR_NONE) { | |
| GST_ERROR_OBJECT (thiz, "Video Decode Query failed (%s)", | |
| msdk_status_to_string (status)); | |
| goto failed; | |
| } else if (status > MFX_ERR_NONE) { | |
| GST_WARNING_OBJECT (thiz, "Video Decode Query returned: %s", | |
| msdk_status_to_string (status)); | |
| } | |
| klass->postinit_decoder (thiz); | |
| status = MFXVideoDECODE_QueryIOSurf (session, &thiz->param, &request); | |
| if (status < MFX_ERR_NONE) { | |
| GST_ERROR_OBJECT (thiz, "Query IO surfaces failed (%s)", | |
| msdk_status_to_string (status)); | |
| goto failed; | |
| } else if (status > MFX_ERR_NONE) { | |
| GST_WARNING_OBJECT (thiz, "Query IO surfaces returned: %s", | |
| msdk_status_to_string (status)); | |
| } | |
| if (request.NumFrameSuggested < thiz->param.AsyncDepth) { | |
| GST_ERROR_OBJECT (thiz, "Required %d surfaces (%d suggested), async %d", | |
| request.NumFrameMin, request.NumFrameSuggested, thiz->param.AsyncDepth); | |
| goto failed; | |
| } | |
| /* account for downstream requirement */ | |
| if (G_LIKELY (thiz->min_prealloc_buffers)) | |
| request.NumFrameSuggested += thiz->min_prealloc_buffers; | |
| else | |
| GST_WARNING_OBJECT (thiz, | |
| "Allocating resources without considering the downstream requirement" | |
| "or extra scratch surface count"); | |
| shared_async_depth = gst_msdk_context_get_shared_async_depth (thiz->context); | |
| request.NumFrameSuggested += shared_async_depth; | |
| request.Type |= MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET; | |
| if (thiz->use_dmabuf) | |
| request.Type |= MFX_MEMTYPE_EXPORT_FRAME; | |
| #if (MFX_VERSION >= 1022) | |
| if (thiz->sfc) { | |
| request.Info.Width = ext_dec_video_proc.Out.Width; | |
| request.Info.Height = ext_dec_video_proc.Out.Height; | |
| } | |
| #endif | |
| gst_msdk_frame_alloc (thiz->context, &request, &thiz->alloc_resp); | |
| thiz->alloc_pool = gst_msdk_context_get_alloc_pool (thiz->context); | |
| /* update the prealloc_buffer count, which will be used later | |
| * as GstBufferPool min_buffers */ | |
| thiz->min_prealloc_buffers = request.NumFrameSuggested; | |
| GST_DEBUG_OBJECT (thiz, "Required %d surfaces (%d suggested)", | |
| request.NumFrameMin, request.NumFrameSuggested); | |
| status = MFXVideoDECODE_Init (session, &thiz->param); | |
| if (status < MFX_ERR_NONE) { | |
| GST_ERROR_OBJECT (thiz, "Init failed (%s)", msdk_status_to_string (status)); | |
| goto failed; | |
| } else if (status > MFX_ERR_NONE) { | |
| GST_WARNING_OBJECT (thiz, "Init returned: %s", | |
| msdk_status_to_string (status)); | |
| } | |
| status = MFXVideoDECODE_GetVideoParam (session, &thiz->param); | |
| if (status < MFX_ERR_NONE) { | |
| GST_ERROR_OBJECT (thiz, "Get Video Parameters failed (%s)", | |
| msdk_status_to_string (status)); | |
| goto failed; | |
| } else if (status > MFX_ERR_NONE) { | |
| GST_WARNING_OBJECT (thiz, "Get Video Parameters returned: %s", | |
| msdk_status_to_string (status)); | |
| } | |
| g_array_set_size (thiz->tasks, 0); /* resets array content */ | |
| g_array_set_size (thiz->tasks, thiz->param.AsyncDepth); | |
| thiz->next_task = 0; | |
| GST_OBJECT_UNLOCK (thiz); | |
| thiz->initialized = TRUE; | |
| return TRUE; | |
| failed: | |
| GST_OBJECT_UNLOCK (thiz); | |
| return FALSE; | |
| } | |
| static gboolean | |
| pad_accept_memory (GstMsdkDec * thiz, const gchar * mem_type, GstCaps ** filter) | |
| { | |
| gboolean ret = FALSE; | |
| GstCaps *caps, *out_caps; | |
| GstPad *pad; | |
| pad = GST_VIDEO_DECODER_SRC_PAD (thiz); | |
| caps = gst_caps_copy (*filter); | |
| gst_caps_set_features (caps, 0, gst_caps_features_from_string (mem_type)); | |
| #ifndef _WIN32 | |
| /* In dma caps case, we need to set drm-format for raw caps */ | |
| if (gst_msdkcaps_has_feature (caps, GST_CAPS_FEATURE_MEMORY_DMABUF)) { | |
| GstStructure *s = gst_caps_get_structure (caps, 0); | |
| if (gst_structure_has_field (s, "format")) { | |
| const gchar *fmt_str = gst_structure_get_string (s, "format"); | |
| GstVideoFormat fmt = gst_video_format_from_string (fmt_str); | |
| guint32 fourcc = gst_va_drm_fourcc_from_video_format (fmt); | |
| gchar *drm_str = | |
| gst_video_dma_drm_fourcc_to_string (fourcc, thiz->modifier); | |
| gst_msdkcaps_set_strings (caps, mem_type, "drm-format", drm_str); | |
| gst_caps_set_simple (caps, "format", G_TYPE_STRING, "DMA_DRM", NULL); | |
| g_free (drm_str); | |
| } | |
| } | |
| #endif | |
| out_caps = gst_pad_peer_query_caps (pad, caps); | |
| if (!out_caps || gst_caps_is_empty (out_caps)) | |
| goto done; | |
| if (gst_msdkcaps_has_feature (out_caps, mem_type)) { | |
| gst_caps_replace (filter, caps); | |
| ret = TRUE; | |
| } | |
| done: | |
| if (caps) | |
| gst_caps_unref (caps); | |
| if (out_caps) | |
| gst_caps_unref (out_caps); | |
| return ret; | |
| } | |
| static GstCaps * | |
| gst_msdkdec_getcaps (GstVideoDecoder * decoder, GstCaps * filter) | |
| { | |
| GstCaps *caps, *tmp = NULL; | |
| caps = gst_pad_get_pad_template_caps (decoder->sinkpad); | |
| if (caps) { | |
| if (filter) { | |
| tmp = gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST); | |
| gst_caps_unref (caps); | |
| caps = tmp; | |
| } | |
| } else { | |
| caps = gst_video_decoder_proxy_getcaps (decoder, NULL, filter); | |
| } | |
| return caps; | |
| } | |
| static gboolean | |
| gst_msdkdec_fixate_format (GstMsdkDec * thiz, GstCaps * caps, | |
| GstVideoFormat fmt) | |
| { | |
| GValue gfmt = G_VALUE_INIT; | |
| g_return_val_if_fail (caps != NULL, FALSE); | |
| g_return_val_if_fail (gst_caps_is_writable (caps), FALSE); | |
| g_return_val_if_fail (fmt != GST_VIDEO_FORMAT_UNKNOWN, FALSE); | |
| g_value_init (&gfmt, G_TYPE_STRING); | |
| for (guint i = 0; i < gst_caps_get_size (caps); i++) { | |
| GstStructure *s = gst_caps_get_structure (caps, i); | |
| #ifndef _WIN32 | |
| GstCapsFeatures *f = gst_caps_get_features (caps, i); | |
| if (gst_caps_features_contains (f, GST_CAPS_FEATURE_MEMORY_DMABUF)) { | |
| const GValue *drm_fmts = gst_structure_get_value (s, "drm-format"); | |
| guint32 fourcc = gst_va_drm_fourcc_from_video_format (fmt); | |
| gchar *drm_str = | |
| gst_video_dma_drm_fourcc_to_string (fourcc, thiz->modifier); | |
| g_value_set_string (&gfmt, drm_str); | |
| g_free (drm_str); | |
| if (!gst_value_can_intersect (&gfmt, drm_fmts)) | |
| goto failed; | |
| gst_structure_set_value (s, "drm-format", &gfmt); | |
| continue; | |
| } | |
| #endif | |
| const GValue *fmts = gst_structure_get_value (s, "format"); | |
| g_value_set_string (&gfmt, gst_video_format_to_string (fmt)); | |
| if (!gst_value_can_intersect (&gfmt, fmts)) | |
| goto failed; | |
| gst_structure_set_value (s, "format", &gfmt); | |
| } | |
| g_value_unset (&gfmt); | |
| return TRUE; | |
| failed: | |
| g_value_unset (&gfmt); | |
| return FALSE; | |
| } | |
| static gboolean | |
| gst_msdkdec_set_src_caps (GstMsdkDec * thiz, gboolean need_allocation) | |
| { | |
| GstVideoInfo vinfo; | |
| GstVideoAlignment align; | |
| GstCaps *allocation_caps = NULL; | |
| GstCaps *allowed_caps = NULL, *temp_caps, *out_caps, *src_caps; | |
| GstVideoFormat format; | |
| guint width, height; | |
| guint alloc_w, alloc_h; | |
| int out_width = 0, out_height = 0; | |
| gint dar_n = -1, dar_d = -1; | |
| GstStructure *outs = NULL; | |
| const gchar *out_format; | |
| GValue v_width = G_VALUE_INIT; | |
| GValue v_height = G_VALUE_INIT; | |
| gboolean has_drm_format = FALSE; | |
| /* use display width and display height in output state, which | |
| * will be used for caps negotiation */ | |
| width = | |
| thiz->param.mfx.FrameInfo.CropW ? thiz->param.mfx. | |
| FrameInfo.CropW : GST_VIDEO_INFO_WIDTH (&thiz->input_state->info); | |
| height = | |
| thiz->param.mfx.FrameInfo.CropH ? thiz->param.mfx. | |
| FrameInfo.CropH : GST_VIDEO_INFO_HEIGHT (&thiz->input_state->info); | |
| format = | |
| gst_msdk_get_video_format_from_mfx_fourcc (thiz->param.mfx. | |
| FrameInfo.FourCC); | |
| if (format == GST_VIDEO_FORMAT_UNKNOWN) { | |
| GST_WARNING_OBJECT (thiz, "Failed to find a valid video format"); | |
| return FALSE; | |
| } | |
| #ifndef _WIN32 | |
| /* Get possible modifier before negotiation really happens */ | |
| GstVaDisplay *display = | |
| (GstVaDisplay *) gst_msdk_context_get_va_display (thiz->context); | |
| thiz->modifier = gst_va_dmabuf_get_modifier_for_format (display, format, | |
| VA_SURFACE_ATTRIB_USAGE_HINT_DECODER); | |
| gst_object_unref (display); | |
| #endif | |
| #if (MFX_VERSION >= 1022) | |
| /* SFC is triggered (for AVC and HEVC) when default output format is not | |
| * accepted by downstream or when downstream requests for a smaller | |
| * resolution (i.e. SFC supports down-scaling) | |
| * Here we need to do the query twice: the first time uses default color | |
| * format and bitstream's original size to query peer pad, empty caps | |
| * means default format and/or size are not accepted by downstream; | |
| * then we need the second query to decide src caps' color format and size, | |
| * and let SFC work. */ | |
| if (thiz->param.mfx.CodecId == MFX_CODEC_AVC || | |
| thiz->param.mfx.CodecId == MFX_CODEC_HEVC) { | |
| temp_caps = gst_pad_query_caps (GST_VIDEO_DECODER (thiz)->srcpad, NULL); | |
| temp_caps = gst_caps_make_writable (temp_caps); | |
| if (!gst_msdkdec_fixate_format (thiz, temp_caps, format)) { | |
| GST_ERROR_OBJECT (thiz, "Format is not negotiable"); | |
| gst_caps_unref (temp_caps); | |
| return FALSE; | |
| } | |
| g_value_init (&v_width, G_TYPE_INT); | |
| g_value_init (&v_height, G_TYPE_INT); | |
| g_value_set_int (&v_width, width); | |
| g_value_set_int (&v_height, height); | |
| gst_caps_set_value (temp_caps, "width", &v_width); | |
| gst_caps_set_value (temp_caps, "height", &v_height); | |
| g_value_unset (&v_width); | |
| g_value_unset (&v_height); | |
| src_caps = gst_pad_peer_query_caps (GST_VIDEO_DECODER | |
| (thiz)->srcpad, temp_caps); | |
| gst_caps_unref (temp_caps); | |
| if (gst_caps_is_empty (src_caps)) { | |
| if (!gst_util_fraction_multiply (width, height, | |
| GST_VIDEO_INFO_PAR_N (&thiz->input_state->info), | |
| GST_VIDEO_INFO_PAR_D (&thiz->input_state->info), | |
| &dar_n, &dar_d)) { | |
| GST_ERROR_OBJECT (thiz, "Error to calculate the output scaled size"); | |
| gst_caps_unref (src_caps); | |
| return FALSE; | |
| } | |
| allowed_caps = | |
| gst_pad_get_allowed_caps (GST_VIDEO_DECODER (thiz)->srcpad); | |
| outs = gst_caps_get_structure (allowed_caps, 0); | |
| if (gst_structure_has_field (outs, "drm-format")) { | |
| out_format = gst_structure_get_string (outs, "drm-format"); | |
| has_drm_format = TRUE; | |
| } else { | |
| out_format = gst_structure_get_string (outs, "format"); | |
| } | |
| if (out_format) { | |
| if (has_drm_format) { | |
| #ifndef _WIN32 | |
| guint drm_fourcc = | |
| gst_video_dma_drm_fourcc_from_string (out_format, NULL); | |
| format = gst_va_video_format_from_drm_fourcc (drm_fourcc); | |
| #endif | |
| } else { | |
| format = gst_video_format_from_string (out_format); | |
| } | |
| thiz->sfc = TRUE; | |
| } | |
| gst_structure_get_int (outs, "width", &out_width); | |
| gst_structure_get_int (outs, "height", &out_height); | |
| if (!out_width && !out_height) { | |
| out_width = width; | |
| out_height = height; | |
| } else { | |
| /* When user does not set out_width, fill it to fit DAR */ | |
| if (!out_width) | |
| out_width = gst_util_uint64_scale (out_height, dar_n, dar_d); | |
| /* When user does not set out_height, fill it to fit DAR */ | |
| if (!out_height) | |
| out_height = gst_util_uint64_scale (out_width, dar_d, dar_n); | |
| if (out_width > width || out_height > height) | |
| goto sfc_error; | |
| else if (out_width < width || out_height < height) { | |
| width = out_width; | |
| height = out_height; | |
| thiz->sfc = TRUE; | |
| } | |
| } | |
| gst_caps_unref (allowed_caps); | |
| } | |
| gst_caps_unref (src_caps); | |
| } | |
| #endif | |
| thiz->output_state = | |
| gst_video_decoder_set_output_state (GST_VIDEO_DECODER (thiz), | |
| format, width, height, thiz->input_state); | |
| if (!thiz->output_state) | |
| return FALSE; | |
| /* Find allocation width and height */ | |
| alloc_w = | |
| GST_ROUND_UP_16 (thiz->param.mfx.FrameInfo.Width ? thiz->param.mfx. | |
| FrameInfo.Width : width); | |
| alloc_h = | |
| GST_ROUND_UP_32 (thiz->param.mfx.FrameInfo.Height ? thiz->param.mfx. | |
| FrameInfo.Height : height); | |
| /* Ensure output_state->caps and info have same width and height | |
| * Also, mandate 32 bit alignment */ | |
| vinfo = thiz->output_state->info; | |
| if (width == out_width || height == out_height) | |
| gst_msdk_set_video_alignment (&vinfo, 0, 0, &align); | |
| else | |
| gst_msdk_set_video_alignment (&vinfo, alloc_w, alloc_h, &align); | |
| gst_video_info_align (&vinfo, &align); | |
| out_caps = gst_video_info_to_caps (&vinfo); | |
| #ifndef _WIN32 | |
| if (pad_accept_memory (thiz, GST_CAPS_FEATURE_MEMORY_VA, &out_caps)) { | |
| GST_INFO_OBJECT (thiz, "Decoder src pad accepts VAMemory caps"); | |
| } else if (pad_accept_memory (thiz, GST_CAPS_FEATURE_MEMORY_DMABUF, | |
| &out_caps)) { | |
| GST_INFO_OBJECT (thiz, "Decoder src pad accepts DMABuf caps"); | |
| } | |
| #else | |
| if (pad_accept_memory (thiz, GST_CAPS_FEATURE_MEMORY_D3D11_MEMORY, &out_caps)) { | |
| GST_INFO_OBJECT (thiz, "Decoder src pad accepts D3D11Memory caps"); | |
| } | |
| #endif | |
| thiz->output_state->caps = out_caps; | |
| if (need_allocation) { | |
| /* Find allocation width and height */ | |
| width = | |
| GST_ROUND_UP_16 (thiz->param.mfx.FrameInfo.Width ? thiz->param.mfx. | |
| FrameInfo.Width : GST_VIDEO_INFO_WIDTH (&thiz->output_state->info)); | |
| height = | |
| GST_ROUND_UP_32 (thiz->param.mfx.FrameInfo.Height ? thiz->param.mfx. | |
| FrameInfo.Height : GST_VIDEO_INFO_HEIGHT (&thiz->output_state->info)); | |
| /* set allocation width and height in allocation_caps, | |
| * which may or may not be similar to the output_state caps */ | |
| allocation_caps = gst_caps_copy (thiz->output_state->caps); | |
| gst_caps_set_simple (allocation_caps, "width", G_TYPE_INT, width, "height", | |
| G_TYPE_INT, height, NULL); | |
| GST_INFO_OBJECT (thiz, "new alloc caps = %" GST_PTR_FORMAT, | |
| allocation_caps); | |
| gst_caps_replace (&thiz->output_state->allocation_caps, allocation_caps); | |
| gst_caps_unref (allocation_caps); | |
| } else { | |
| /* We keep the allocation parameters as it is to avoid pool re-negotiation. | |
| * For codecs like VP9, dynamic resolution change doesn't require allocation | |
| * reset if the new video frame resolution is lower than the | |
| * already configured one */ | |
| } | |
| return TRUE; | |
| sfc_error: | |
| GST_ERROR_OBJECT (thiz, "Decoder SFC cannot do up-scaling"); | |
| gst_caps_unref (allowed_caps); | |
| gst_caps_unref (temp_caps); | |
| return FALSE; | |
| } | |
| static void | |
| gst_msdkdec_set_latency (GstMsdkDec * thiz) | |
| { | |
| GstVideoInfo *info = &thiz->input_state->info; | |
| gint min_delayed_frames; | |
| GstClockTime latency; | |
| min_delayed_frames = thiz->async_depth; | |
| if (info->fps_n) { | |
| latency = gst_util_uint64_scale_ceil (GST_SECOND * info->fps_d, | |
| min_delayed_frames, info->fps_n); | |
| } else { | |
| /* FIXME: Assume 25fps. This is better than reporting no latency at | |
| * all and then later failing in live pipelines | |
| */ | |
| latency = gst_util_uint64_scale_ceil (GST_SECOND * 1, | |
| min_delayed_frames, 25); | |
| } | |
| GST_INFO_OBJECT (thiz, | |
| "Updating latency to %" GST_TIME_FORMAT " (%d frames)", | |
| GST_TIME_ARGS (latency), min_delayed_frames); | |
| gst_video_decoder_set_latency (GST_VIDEO_DECODER (thiz), latency, latency); | |
| } | |
| static gint | |
| _find_msdk_surface (gconstpointer msdk_surface, gconstpointer comp_surface) | |
| { | |
| GstMsdkSurface *cached_surface = (GstMsdkSurface *) msdk_surface; | |
| mfxFrameSurface1 *_surface = (mfxFrameSurface1 *) comp_surface; | |
| return cached_surface ? cached_surface->surface != _surface : -1; | |
| } | |
| static void | |
| finish_task (GstMsdkDec * thiz, MsdkDecTask * task) | |
| { | |
| GstMsdkSurface *surface = task->surface; | |
| if (surface) { | |
| thiz->locked_msdk_surfaces = | |
| g_list_append (thiz->locked_msdk_surfaces, surface); | |
| } | |
| task->sync_point = NULL; | |
| task->surface = NULL; | |
| task->decode_only = FALSE; | |
| } | |
| static void | |
| gst_msdkdec_frame_corruption_report (GstMsdkDec * thiz, mfxU16 corruption) | |
| { | |
| if (!thiz->report_error || !corruption) | |
| return; | |
| if (corruption & MFX_CORRUPTION_MINOR) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Corruption] Minor corruption detected!"), (NULL)); | |
| if (corruption & MFX_CORRUPTION_MAJOR) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Corruption] Major corruption detected!"), (NULL)); | |
| if (corruption & MFX_CORRUPTION_ABSENT_TOP_FIELD) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Corruption] Absent top field!"), (NULL)); | |
| if (corruption & MFX_CORRUPTION_ABSENT_BOTTOM_FIELD) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Corruption] Absent bottom field!"), (NULL)); | |
| if (corruption & MFX_CORRUPTION_REFERENCE_FRAME) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Corruption] Corrupted reference frame!"), (NULL)); | |
| if (corruption & MFX_CORRUPTION_REFERENCE_LIST) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Corruption] Corrupted reference list!"), (NULL)); | |
| } | |
| static gboolean | |
| _copy_to_sys_mem (GstMsdkDec * thiz, GstMsdkSurface * surface, | |
| GstVideoCodecFrame * frame) | |
| { | |
| GstBuffer *buffer = NULL; | |
| GstVideoFrame src_frame; | |
| GstVideoFrame dst_frame; | |
| GstVideoInfo *src_info; | |
| GstVideoInfo dst_info; | |
| src_info = &thiz->output_state->info; | |
| gst_video_info_set_format (&dst_info, GST_VIDEO_INFO_FORMAT (src_info), | |
| GST_VIDEO_INFO_WIDTH (src_info), GST_VIDEO_INFO_HEIGHT (src_info)); | |
| if (!gst_buffer_pool_is_active (thiz->other_pool) && | |
| !gst_buffer_pool_set_active (thiz->other_pool, TRUE)) { | |
| GST_ERROR_OBJECT (thiz, "Failed to activate buffer pool"); | |
| goto error_active; | |
| } | |
| if (gst_buffer_pool_acquire_buffer (thiz->other_pool, &buffer, NULL) | |
| != GST_FLOW_OK) { | |
| GST_ERROR ("Failed to acquire buffer from pool"); | |
| goto error; | |
| } | |
| if (!gst_video_frame_map (&src_frame, src_info, surface->buf, GST_MAP_READ)) { | |
| GST_ERROR_OBJECT (thiz, "Failed to map buf to src frame"); | |
| goto error; | |
| } | |
| if (!gst_video_frame_map (&dst_frame, &dst_info, buffer, GST_MAP_WRITE)) { | |
| GST_ERROR_OBJECT (thiz, "Failed to map buf to dst frame"); | |
| gst_video_frame_unmap (&src_frame); | |
| goto error; | |
| } | |
| if (!gst_video_frame_copy (&dst_frame, &src_frame)) { | |
| GST_ERROR_OBJECT (thiz, "Failed to copy surface data"); | |
| gst_video_frame_unmap (&src_frame); | |
| gst_video_frame_unmap (&dst_frame); | |
| goto error; | |
| } | |
| frame->output_buffer = buffer; | |
| gst_video_frame_unmap (&src_frame); | |
| gst_video_frame_unmap (&dst_frame); | |
| return TRUE; | |
| error: | |
| gst_buffer_unref (buffer); | |
| gst_buffer_pool_set_active (thiz->other_pool, FALSE); | |
| gst_object_unref (thiz->other_pool); | |
| error_active: | |
| return FALSE; | |
| } | |
| static GstFlowReturn | |
| gst_msdkdec_finish_task (GstMsdkDec * thiz, MsdkDecTask * task) | |
| { | |
| GstVideoDecoder *decoder = GST_VIDEO_DECODER (thiz); | |
| GstFlowReturn flow; | |
| GstVideoCodecFrame *frame; | |
| GstMsdkSurface *surface; | |
| mfxStatus status; | |
| guint64 pts = MFX_TIMESTAMP_UNKNOWN; | |
| if (G_LIKELY (task->sync_point)) { | |
| status = | |
| MFXVideoCORE_SyncOperation (gst_msdk_context_get_session | |
| (thiz->context), task->sync_point, 300000); | |
| if (status != MFX_ERR_NONE) { | |
| GST_ERROR_OBJECT (thiz, "failed to do sync operation"); | |
| return GST_FLOW_ERROR; | |
| } | |
| } | |
| surface = task->surface; | |
| if (surface) { | |
| gst_msdkdec_frame_corruption_report (thiz, | |
| surface->surface->Data.Corrupted); | |
| GST_DEBUG_OBJECT (thiz, "Decoded MFX TimeStamp: %" G_GUINT64_FORMAT, | |
| (guint64) surface->surface->Data.TimeStamp); | |
| pts = surface->surface->Data.TimeStamp; | |
| if (thiz->param.mfx.CodecId == MFX_CODEC_VP9) { | |
| /* detect whether the resolution change and negotiate with downstream if so */ | |
| if ((surface->surface->Info.CropW && surface->surface->Info.CropH) | |
| && ((thiz->output_state->info.width != surface->surface->Info.CropW) | |
| || (thiz->output_state->info.height != | |
| surface->surface->Info.CropH))) { | |
| thiz->output_state->info.width = surface->surface->Info.CropW; | |
| thiz->output_state->info.height = surface->surface->Info.CropH; | |
| thiz->output_state->caps = | |
| gst_video_info_to_caps (&thiz->output_state->info); | |
| if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (thiz))) { | |
| GST_ERROR_OBJECT (thiz, "Failed to negotiate"); | |
| return GST_FLOW_NOT_NEGOTIATED; | |
| } | |
| } | |
| } | |
| } | |
| if (G_LIKELY (task->sync_point || (surface && task->decode_only))) { | |
| gboolean decode_only = task->decode_only; | |
| frame = gst_msdkdec_get_oldest_frame (decoder); | |
| /* align decoder frame list with current decoded position */ | |
| while (frame && MFX_TIME_IS_VALID (pts) | |
| && GST_CLOCK_TIME_IS_VALID (frame->pts) | |
| && GST_TO_MFX_TIME (frame->pts) < pts) { | |
| GST_INFO_OBJECT (thiz, "Discarding frame: %p PTS: %" GST_TIME_FORMAT | |
| " MFX TimeStamp: %" G_GUINT64_FORMAT, | |
| frame, GST_TIME_ARGS (frame->pts), GST_TO_MFX_TIME (frame->pts)); | |
| gst_video_decoder_release_frame (decoder, frame); | |
| frame = gst_msdkdec_get_oldest_frame (decoder); | |
| } | |
| if (G_LIKELY (frame)) { | |
| if (!thiz->do_copy) { | |
| /* gst_video_decoder_finish_frame will call gst_buffer_make_writable | |
| * we need this to avoid copy buffer */ | |
| GST_MINI_OBJECT_FLAG_SET (surface->buf, GST_MINI_OBJECT_FLAG_LOCKABLE); | |
| frame->output_buffer = gst_buffer_ref (surface->buf); | |
| } else { | |
| /* We need to do the copy from video memory to system memory */ | |
| if (!_copy_to_sys_mem (thiz, surface, frame)) | |
| return GST_FLOW_ERROR; | |
| } | |
| GST_DEBUG_OBJECT (thiz, "surface %p TimeStamp: %" G_GUINT64_FORMAT | |
| " frame %p TimeStamp: %" G_GUINT64_FORMAT, | |
| surface->surface, (guint64) surface->surface->Data.TimeStamp, | |
| frame, GST_TO_MFX_TIME (frame->pts)); | |
| } | |
| finish_task (thiz, task); | |
| if (!frame) | |
| return GST_FLOW_FLUSHING; | |
| if (decode_only) | |
| GST_VIDEO_CODEC_FRAME_SET_DECODE_ONLY (frame); | |
| frame->pts = MFX_TO_GST_TIME (pts); | |
| flow = gst_video_decoder_finish_frame (decoder, frame); | |
| if (flow == GST_FLOW_ERROR) | |
| GST_ERROR_OBJECT (thiz, "Failed to finish frame"); | |
| return flow; | |
| } | |
| finish_task (thiz, task); | |
| return GST_FLOW_OK; | |
| } | |
| static gboolean | |
| gst_msdkdec_context_prepare (GstMsdkDec * thiz) | |
| { | |
| /* Try to find an existing context from the pipeline. This may (indirectly) | |
| * invoke gst_msdkdec_set_context, which will set thiz->context. */ | |
| if (!gst_msdk_context_find (GST_ELEMENT_CAST (thiz), &thiz->context)) | |
| return FALSE; | |
| if (thiz->context == thiz->old_context) { | |
| GST_INFO_OBJECT (thiz, "Found old context %" GST_PTR_FORMAT | |
| ", reusing as-is", thiz->context); | |
| return TRUE; | |
| } | |
| GST_INFO_OBJECT (thiz, "Found context %" GST_PTR_FORMAT " from neighbour", | |
| thiz->context); | |
| if (!(gst_msdk_context_get_job_type (thiz->context) & GST_MSDK_JOB_DECODER)) { | |
| gst_msdk_context_add_job_type (thiz->context, GST_MSDK_JOB_DECODER); | |
| return TRUE; | |
| } | |
| /* Found an existing context that's already being used as a decoder, clone | |
| * the MFX session inside it to create a new one */ | |
| { | |
| GstMsdkContext *parent_context, *msdk_context; | |
| GST_INFO_OBJECT (thiz, "Creating new context %" GST_PTR_FORMAT " with " | |
| "joined session", thiz->context); | |
| parent_context = thiz->context; | |
| msdk_context = gst_msdk_context_new_with_parent (parent_context); | |
| if (!msdk_context) { | |
| GST_ERROR_OBJECT (thiz, "Failed to create a context with parent context " | |
| "as %" GST_PTR_FORMAT, parent_context); | |
| return FALSE; | |
| } | |
| thiz->context = msdk_context; | |
| gst_msdk_context_add_shared_async_depth (thiz->context, | |
| gst_msdk_context_get_shared_async_depth (parent_context)); | |
| gst_object_unref (parent_context); | |
| } | |
| return TRUE; | |
| } | |
| static gboolean | |
| gst_msdkdec_start (GstVideoDecoder * decoder) | |
| { | |
| GstMsdkDec *thiz = GST_MSDKDEC (decoder); | |
| if (!gst_msdkdec_context_prepare (thiz)) { | |
| if (!gst_msdk_ensure_new_context (GST_ELEMENT_CAST (thiz), | |
| thiz->hardware, GST_MSDK_JOB_DECODER, &thiz->context)) | |
| return FALSE; | |
| GST_INFO_OBJECT (thiz, "Creating new context %" GST_PTR_FORMAT, | |
| thiz->context); | |
| } | |
| /* Save the current context in a separate field so that we know whether it | |
| * has changed between calls to _start() */ | |
| gst_object_replace ((GstObject **) & thiz->old_context, | |
| (GstObject *) thiz->context); | |
| gst_msdk_context_add_shared_async_depth (thiz->context, thiz->async_depth); | |
| return TRUE; | |
| } | |
| static gboolean | |
| gst_msdkdec_close (GstVideoDecoder * decoder) | |
| { | |
| GstMsdkDec *thiz = GST_MSDKDEC (decoder); | |
| gst_clear_object (&thiz->context); | |
| return TRUE; | |
| } | |
| static gboolean | |
| gst_msdkdec_stop (GstVideoDecoder * decoder) | |
| { | |
| GstMsdkDec *thiz = GST_MSDKDEC (decoder); | |
| gst_msdkdec_flush (decoder); | |
| if (thiz->input_state) { | |
| gst_video_codec_state_unref (thiz->input_state); | |
| thiz->input_state = NULL; | |
| } | |
| g_clear_pointer (&thiz->output_state, gst_video_codec_state_unref); | |
| if (thiz->pool) { | |
| gst_object_unref (thiz->pool); | |
| thiz->pool = NULL; | |
| } | |
| if (thiz->other_pool) { | |
| gst_object_unref (thiz->other_pool); | |
| thiz->other_pool = NULL; | |
| } | |
| gst_object_replace ((GstObject **) & thiz->alloc_pool, NULL); | |
| gst_video_info_init (&thiz->non_msdk_pool_info); | |
| gst_msdkdec_close_decoder (thiz, TRUE); | |
| return TRUE; | |
| } | |
| static gboolean | |
| gst_msdkdec_set_format (GstVideoDecoder * decoder, GstVideoCodecState * state) | |
| { | |
| GstMsdkDec *thiz = GST_MSDKDEC (decoder); | |
| if (thiz->input_state) { | |
| /* mark for re-negotiation if display resolution or any other video info | |
| * changes like framerate. */ | |
| if (!gst_video_info_is_equal (&thiz->input_state->info, &state->info)) { | |
| GST_INFO_OBJECT (thiz, "Schedule renegotiation as video info changed"); | |
| thiz->do_renego = TRUE; | |
| } | |
| gst_video_codec_state_unref (thiz->input_state); | |
| } | |
| thiz->input_state = gst_video_codec_state_ref (state); | |
| /* we don't set output state here to avoid caching of mismatched | |
| * video information if there is dynamic resolution change in the stream. | |
| * All negotiation code is consolidated in gst_msdkdec_negotiate() and | |
| * this will be invoked from handle_frame() */ | |
| gst_msdkdec_set_latency (thiz); | |
| return TRUE; | |
| } | |
| static void | |
| release_msdk_surfaces (GstMsdkDec * thiz) | |
| { | |
| GList *l; | |
| GstMsdkSurface *surface; | |
| gint locked = 0; | |
| gst_msdkdec_free_unlocked_msdk_surfaces (thiz, FALSE); | |
| for (l = thiz->locked_msdk_surfaces; l; l = l->next) { | |
| surface = (GstMsdkSurface *) l->data; | |
| free_surface (surface); | |
| locked++; | |
| } | |
| if (locked) | |
| GST_ERROR_OBJECT (thiz, "msdk still locked %d surfaces", locked); | |
| g_list_free (thiz->locked_msdk_surfaces); | |
| thiz->locked_msdk_surfaces = NULL; | |
| } | |
| /* This will get invoked in the following situations: | |
| * 1: beginning of the stream, which requires initialization (== complete reset) | |
| * 2: upstream notified a resolution change and set do_renego to TRUE. | |
| * new resolution may or may not requires full reset | |
| * 3: upstream failed to notify the resolution change but | |
| * msdk detected the change (eg: vp9 stream in ivf elementary form | |
| * with varying resolution frames). | |
| * | |
| * for any input configuration change, we deal with notification | |
| * from upstream and also use msdk APIs to handle the parameter initialization | |
| * efficiently | |
| */ | |
| static gboolean | |
| gst_msdkdec_negotiate (GstMsdkDec * thiz, gboolean hard_reset) | |
| { | |
| GstVideoDecoder *decoder = GST_VIDEO_DECODER (thiz); | |
| GST_DEBUG_OBJECT (thiz, | |
| "Start Negotiating caps, pool and Init the msdk decdoer subsystem"); | |
| if (hard_reset) { | |
| /* Retrieve any pending frames and push them downstream */ | |
| if (gst_msdkdec_drain (GST_VIDEO_DECODER (thiz)) != GST_FLOW_OK) | |
| goto error_drain; | |
| /* This will initiate the allocation query which will help to flush | |
| * all the pending buffers in the pipeline so that we can stop | |
| * the active bufferpool and safely invoke gst_msdk_frame_free() */ | |
| if (thiz->initialized) { | |
| GstCaps *caps = gst_pad_get_current_caps (decoder->srcpad); | |
| GstQuery *query = NULL; | |
| if (caps) { | |
| query = gst_query_new_allocation (caps, FALSE); | |
| gst_pad_peer_query (decoder->srcpad, query); | |
| gst_query_unref (query); | |
| gst_caps_unref (caps); | |
| } | |
| } | |
| /* De-initialize the decoder if it is already active */ | |
| /* Do not reset the mfxVideoParam since it already | |
| * has the required parameters for new session decode */ | |
| gst_msdkdec_close_decoder (thiz, FALSE); | |
| /* request for pool re-negotiation by setting do_realloc */ | |
| thiz->do_realloc = TRUE; | |
| } | |
| /* At this point all pending frames (if there are any) are pushed downstream | |
| * and we are ready to negotiate the output caps */ | |
| if (!gst_msdkdec_set_src_caps (thiz, hard_reset)) | |
| return FALSE; | |
| /* this will initiate the allocation query, we create the | |
| * bufferpool in decide_allocation in order to account | |
| * for the downstream min_buffer requirement | |
| * Required initializations for MediaSDK operations | |
| * will all be initialized from decide_allocation after considering | |
| * some of the downstream requirements */ | |
| if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (thiz))) | |
| goto error_negotiate; | |
| thiz->do_renego = FALSE; | |
| thiz->do_realloc = FALSE; | |
| return TRUE; | |
| error_drain: | |
| GST_ERROR_OBJECT (thiz, "Failed to Drain the queued decoded frames"); | |
| return FALSE; | |
| error_negotiate: | |
| GST_ERROR_OBJECT (thiz, "Failed to re-negotiate"); | |
| return FALSE; | |
| } | |
| static inline gboolean | |
| find_msdk_surface (GstMsdkDec * thiz, MsdkDecTask * task, | |
| mfxFrameSurface1 * out_surface) | |
| { | |
| GList *l; | |
| task->surface = NULL; | |
| if (!out_surface) | |
| return TRUE; | |
| l = g_list_find_custom (thiz->locked_msdk_surfaces, out_surface, | |
| _find_msdk_surface); | |
| if (!l) { | |
| GST_ERROR_OBJECT (thiz, "msdk return an invalid surface %p", out_surface); | |
| return FALSE; | |
| } | |
| task->surface = (GstMsdkSurface *) l->data; | |
| thiz->locked_msdk_surfaces = | |
| g_list_delete_link (thiz->locked_msdk_surfaces, l); | |
| return TRUE; | |
| } | |
| static void | |
| gst_msdkdec_error_report (GstMsdkDec * thiz) | |
| { | |
| if (!thiz->report_error) | |
| return; | |
| #if (MFX_VERSION >= 1025) | |
| else { | |
| if (thiz->error_report.ErrorTypes & MFX_ERROR_SPS) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Error] SPS Error detected!"), (NULL)); | |
| if (thiz->error_report.ErrorTypes & MFX_ERROR_PPS) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Error] PPS Error detected!"), (NULL)); | |
| if (thiz->error_report.ErrorTypes & MFX_ERROR_SLICEHEADER) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Error] SliceHeader Error detected!"), (NULL)); | |
| if (thiz->error_report.ErrorTypes & MFX_ERROR_FRAME_GAP) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Error] Frame Gap Error detected!"), (NULL)); | |
| #ifdef ONEVPL_EXPERIMENTAL | |
| if (thiz->error_report.ErrorTypes & MFX_ERROR_JPEG_APP0_MARKER) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Error] APP0 unknown marker detected!"), (NULL)); | |
| if (thiz->error_report.ErrorTypes & MFX_ERROR_JPEG_APP14_MARKER) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Error] APP14 unknown marker detected!"), (NULL)); | |
| if (thiz->error_report.ErrorTypes & MFX_ERROR_JPEG_DQT_MARKER) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Error] DQT unknown marker detected!"), (NULL)); | |
| if (thiz->error_report.ErrorTypes & MFX_ERROR_JPEG_SOF0_MARKER) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Error] SOF0 unknown marker detected!"), (NULL)); | |
| if (thiz->error_report.ErrorTypes & MFX_ERROR_JPEG_DHT_MARKER) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Error] DHT unknown marker detected!"), (NULL)); | |
| if (thiz->error_report.ErrorTypes & MFX_ERROR_JPEG_DRI_MARKER) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Error] DRI unknown marker detected!"), (NULL)); | |
| if (thiz->error_report.ErrorTypes & MFX_ERROR_JPEG_SOS_MARKER) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Error] SOS unknown marker detected!"), (NULL)); | |
| if (thiz->error_report.ErrorTypes & MFX_ERROR_JPEG_UNKNOWN_MARKER) | |
| GST_ELEMENT_WARNING (thiz, STREAM, DECODE, | |
| ("[Error] Error unknown marker detected!"), (NULL)); | |
| #endif | |
| } | |
| #endif | |
| } | |
| static GstFlowReturn | |
| gst_msdkdec_handle_frame (GstVideoDecoder * decoder, GstVideoCodecFrame * frame) | |
| { | |
| GstMsdkDec *thiz = GST_MSDKDEC (decoder); | |
| GstMsdkDecClass *klass = GST_MSDKDEC_GET_CLASS (thiz); | |
| GstFlowReturn flow; | |
| GstBuffer *input_buffer = NULL; | |
| GstVideoInfo alloc_info; | |
| MsdkDecTask *task = NULL; | |
| mfxBitstream bitstream; | |
| GstMsdkSurface *surface = NULL; | |
| mfxFrameSurface1 *out_surface = NULL; | |
| mfxSession session; | |
| mfxStatus status; | |
| GstMapInfo map_info; | |
| guint i, retry_err_incompatible = 0; | |
| gsize data_size; | |
| gboolean hard_reset = FALSE; | |
| GstClockTime pts = GST_CLOCK_TIME_NONE; | |
| /* configure the subclass in order to fill the CodecID field of | |
| * mfxVideoParam and also to load the PluginID for some of the | |
| * codecs which is mandatory to invoke the | |
| * MFXVideoDECODE_DecodeHeader API. | |
| * | |
| * For non packetized formats (currently only vc1), there | |
| * could be headers received as codec_data which are not available | |
| * instream and in that case subclass implementation will | |
| * push it to the internal adapter. We invoke the subclass configure | |
| * well early to make sure the codec_data received has been correctly | |
| * pushed to the adapter by the subclasses before doing | |
| * the DecodeHeader() later on | |
| */ | |
| if (!thiz->initialized || thiz->do_renego) { | |
| /* Clear the internal adapter in re-negotiation for non-packetized | |
| * formats */ | |
| if (!gst_video_decoder_get_packetized (decoder)) | |
| gst_adapter_clear (thiz->adapter); | |
| if (!klass->configure || !klass->configure (thiz)) { | |
| flow = GST_FLOW_OK; | |
| goto error; | |
| } | |
| } | |
| /* Current frame-codec could be pushed and released before this | |
| * function ends -- because msdkdec pushes the oldest frame, | |
| * according its PTS, and it could be this very same frame-codec | |
| * among others pending frame-codecs. | |
| * | |
| * Instead of copying the input data into the mfxBitstream, let's | |
| * keep an extra reference to frame-codec's input buffer */ | |
| input_buffer = gst_buffer_ref (frame->input_buffer); | |
| if (!gst_buffer_map (input_buffer, &map_info, GST_MAP_READ)) { | |
| gst_buffer_unref (input_buffer); | |
| return GST_FLOW_ERROR; | |
| } | |
| memset (&bitstream, 0, sizeof (bitstream)); | |
| /* Add extended buffers */ | |
| if (thiz->num_bs_extra_params) { | |
| bitstream.NumExtParam = thiz->num_bs_extra_params; | |
| bitstream.ExtParam = thiz->bs_extra_params; | |
| } | |
| if (gst_video_decoder_get_packetized (decoder)) { | |
| /* Packetized stream: we prefer to have a parser as a connected upstream | |
| * element to the decoder */ | |
| pts = frame->pts; | |
| bitstream.Data = map_info.data; | |
| bitstream.DataLength = map_info.size; | |
| bitstream.MaxLength = map_info.size; | |
| bitstream.TimeStamp = GST_TO_MFX_TIME (pts); | |
| /* | |
| * MFX_BITSTREAM_COMPLETE_FRAME was removed since commit df59db9, however | |
| * some customers still use DecodedOrder (deprecated in msdk-2017 version) | |
| * for low-latency streaming of non-b-frame encoded streams, which needs to | |
| * output the frame at once, so add it back for this case | |
| */ | |
| if (thiz->param.mfx.DecodedOrder == GST_MSDKDEC_OUTPUT_ORDER_DECODE) | |
| bitstream.DataFlag |= MFX_BITSTREAM_COMPLETE_FRAME; | |
| } else { | |
| /* Non packetized streams: eg: vc1 advanced profile with per buffer bdu */ | |
| gst_adapter_push (thiz->adapter, gst_buffer_ref (input_buffer)); | |
| data_size = gst_adapter_available (thiz->adapter); | |
| bitstream.Data = (mfxU8 *) gst_adapter_map (thiz->adapter, data_size); | |
| bitstream.DataLength = (mfxU32) data_size; | |
| bitstream.MaxLength = bitstream.DataLength; | |
| bitstream.TimeStamp = GST_TO_MFX_TIME (pts); | |
| } | |
| GST_DEBUG_OBJECT (thiz, | |
| "mfxBitStream=> DataLength:%d DataOffset:%d MaxLength:%d " | |
| "PTS: %" GST_TIME_FORMAT " MFX TimeStamp %" G_GUINT64_FORMAT, | |
| bitstream.DataLength, bitstream.DataOffset, bitstream.MaxLength, | |
| GST_TIME_ARGS (pts), (guint64) bitstream.TimeStamp); | |
| session = gst_msdk_context_get_session (thiz->context); | |
| if (!thiz->initialized || thiz->do_renego) { | |
| /* gstreamer caps will not provide all the necessary parameters | |
| * required for optimal decode configuration. For example: the required number | |
| * of surfaces to be allocated can be calculated based on H264 SEI header | |
| * and this information can't be retrieved from the negotiated caps. | |
| * So instead of introducing a codecparser dependency to parse the headers | |
| * inside msdk plugin, we simply use the mfx APIs to extract header information */ | |
| #if (MFX_VERSION >= 1025) | |
| if (thiz->report_error) | |
| thiz->error_report.ErrorTypes = 0; | |
| #endif | |
| status = MFXVideoDECODE_DecodeHeader (session, &bitstream, &thiz->param); | |
| GST_DEBUG_OBJECT (decoder, "DecodeHeader => %d", status); | |
| gst_msdkdec_error_report (thiz); | |
| if (status == MFX_ERR_MORE_DATA) { | |
| flow = GST_FLOW_OK; | |
| goto done; | |
| } | |
| if (!klass->post_configure (thiz)) { | |
| flow = GST_FLOW_ERROR; | |
| goto error; | |
| } | |
| if (!thiz->initialized) | |
| hard_reset = TRUE; | |
| else { | |
| if (thiz->output_state) { | |
| if (thiz->output_state->allocation_caps) { | |
| if (!gst_msdkcaps_video_info_from_caps | |
| (thiz->output_state->allocation_caps, &alloc_info, NULL)) { | |
| GST_ERROR_OBJECT (thiz, "Failed to get video info from caps"); | |
| flow = GST_FLOW_ERROR; | |
| goto error; | |
| } | |
| /* Check whether we need complete reset for dynamic resolution change */ | |
| if (thiz->param.mfx.FrameInfo.Width > | |
| GST_VIDEO_INFO_WIDTH (&alloc_info) | |
| || thiz->param.mfx.FrameInfo.Height > | |
| GST_VIDEO_INFO_HEIGHT (&alloc_info)) | |
| hard_reset = TRUE; | |
| } | |
| } | |
| } | |
| /* if subclass requested for the force reset */ | |
| if (thiz->force_reset_on_res_change) | |
| hard_reset = TRUE; | |
| if (!gst_msdkdec_negotiate (thiz, hard_reset)) { | |
| GST_ELEMENT_ERROR (thiz, CORE, NEGOTIATION, | |
| ("Could not negotiate the stream"), (NULL)); | |
| flow = GST_FLOW_ERROR; | |
| goto error; | |
| } | |
| } | |
| /* gst_msdkdec_handle_frame owns one ref on input argument |frame|. At this | |
| * point this frame is not used so just unref it right away. | |
| * gst_msdkdec_finish_task is fetching the frames itself. */ | |
| gst_video_codec_frame_unref (frame); | |
| frame = NULL; | |
| for (;;) { | |
| task = &g_array_index (thiz->tasks, MsdkDecTask, thiz->next_task); | |
| flow = gst_msdkdec_finish_task (thiz, task); | |
| if (flow != GST_FLOW_OK) { | |
| if (flow == GST_FLOW_ERROR) | |
| GST_ERROR_OBJECT (thiz, "Failed to finish a task"); | |
| goto error; | |
| } | |
| if (!surface) { | |
| surface = allocate_output_surface (thiz); | |
| if (!surface) { | |
| /* Can't get a surface for some reason; finish tasks, then see if | |
| a surface becomes available. */ | |
| for (i = 0; i < thiz->tasks->len - 1; i++) { | |
| thiz->next_task = (thiz->next_task + 1) % thiz->tasks->len; | |
| task = &g_array_index (thiz->tasks, MsdkDecTask, thiz->next_task); | |
| flow = gst_msdkdec_finish_task (thiz, task); | |
| if (flow != GST_FLOW_OK) | |
| goto error; | |
| surface = allocate_output_surface (thiz); | |
| if (surface) | |
| break; | |
| } | |
| if (!surface) { | |
| GST_ERROR_OBJECT (thiz, "Couldn't get a surface"); | |
| flow = GST_FLOW_ERROR; | |
| goto error; | |
| } | |
| } | |
| } | |
| #if (MFX_VERSION >= 1025) | |
| if (thiz->report_error) | |
| thiz->error_report.ErrorTypes = 0; | |
| #endif | |
| status = | |
| MFXVideoDECODE_DecodeFrameAsync (session, &bitstream, surface->surface, | |
| &out_surface, &task->sync_point); | |
| if (!find_msdk_surface (thiz, task, out_surface)) { | |
| flow = GST_FLOW_ERROR; | |
| goto done; | |
| } | |
| GST_DEBUG_OBJECT (decoder, "DecodeFrameAsync => %d", status); | |
| gst_msdkdec_error_report (thiz); | |
| /* media-sdk requires complete reset since the surface is inadequate | |
| * for further decoding */ | |
| if (status == MFX_ERR_INCOMPATIBLE_VIDEO_PARAM && | |
| retry_err_incompatible++ < 1) { | |
| /* MFX_ERR_INCOMPATIBLE_VIDEO_PARAM means the current mfx surface is not | |
| * suitable for the current frame. Call MFXVideoDECODE_DecodeHeader to get | |
| * the current frame size, then do memory re-allocation, otherwise | |
| * MFXVideoDECODE_DecodeFrameAsync will still fail on next call */ | |
| #if (MFX_VERSION >= 1025) | |
| if (thiz->report_error) | |
| thiz->error_report.ErrorTypes = 0; | |
| #endif | |
| status = MFXVideoDECODE_DecodeHeader (session, &bitstream, &thiz->param); | |
| GST_DEBUG_OBJECT (decoder, "DecodeHeader => %d", status); | |
| gst_msdkdec_error_report (thiz); | |
| if (status == MFX_ERR_MORE_DATA) { | |
| flow = GST_FLOW_OK; | |
| goto done; | |
| } | |
| /* Requires memory re-allocation, do a hard reset */ | |
| if (!gst_msdkdec_negotiate (thiz, TRUE)) | |
| goto error; | |
| /* The current surface is freed when doing a hard reset; a new surface is | |
| * required for the new resolution */ | |
| surface = NULL; | |
| continue; | |
| } | |
| retry_err_incompatible = 0; | |
| if (G_LIKELY (status == MFX_ERR_NONE) | |
| || (status == MFX_WRN_VIDEO_PARAM_CHANGED)) { | |
| thiz->next_task = (thiz->next_task + 1) % thiz->tasks->len; | |
| if (surface->surface->Data.Locked > 0) | |
| surface = NULL; | |
| if (bitstream.DataLength == 0) { | |
| flow = GST_FLOW_OK; | |
| /* Don't release it if the current surface is in use */ | |
| if (surface && task->surface->surface == surface->surface) | |
| surface = NULL; | |
| break; | |
| } | |
| } else if (status == MFX_ERR_MORE_DATA) { | |
| if (task->surface) { | |
| task->decode_only = TRUE; | |
| thiz->next_task = (thiz->next_task + 1) % thiz->tasks->len; | |
| } | |
| if (surface->surface->Data.Locked > 0) | |
| surface = NULL; | |
| flow = GST_VIDEO_DECODER_FLOW_NEED_DATA; | |
| break; | |
| } else if (status == MFX_ERR_MORE_SURFACE) { | |
| surface = NULL; | |
| continue; | |
| } else if (status == MFX_WRN_DEVICE_BUSY) { | |
| /* If device is busy, wait 1ms and retry, as per MSDK's recommendation */ | |
| g_usleep (1000); | |
| if (surface->surface->Data.Locked > 0) | |
| surface = NULL; | |
| /* If the current surface is still busy, we should do sync operation, | |
| * then try to decode again | |
| */ | |
| thiz->next_task = (thiz->next_task + 1) % thiz->tasks->len; | |
| } else if (status < MFX_ERR_NONE) { | |
| GST_ERROR_OBJECT (thiz, "DecodeFrameAsync failed (%s)", | |
| msdk_status_to_string (status)); | |
| flow = GST_FLOW_ERROR; | |
| break; | |
| } | |
| } | |
| if (!gst_video_decoder_get_packetized (decoder)) { | |
| /* flush out the data which has already been consumed by msdk */ | |
| gst_adapter_flush (thiz->adapter, bitstream.DataOffset); | |
| } | |
| /* | |
| * DecodedOrder was deprecated in msdk-2017 version, but some | |
| * customers still using this for low-latency streaming of non-b-frame | |
| * encoded streams, which needs to output the frame at once | |
| */ | |
| if (thiz->param.mfx.DecodedOrder == GST_MSDKDEC_OUTPUT_ORDER_DECODE) | |
| gst_msdkdec_finish_task (thiz, task); | |
| done: | |
| gst_buffer_unmap (input_buffer, &map_info); | |
| gst_buffer_unref (input_buffer); | |
| return flow; | |
| error: | |
| if (input_buffer) { | |
| gst_buffer_unmap (input_buffer, &map_info); | |
| gst_buffer_unref (input_buffer); | |
| } | |
| if (frame) | |
| gst_video_decoder_drop_frame (decoder, frame); | |
| return flow; | |
| } | |
| static GstFlowReturn | |
| gst_msdkdec_parse (GstVideoDecoder * decoder, GstVideoCodecFrame * frame, | |
| GstAdapter * adapter, gboolean at_eos) | |
| { | |
| gsize size; | |
| GstFlowReturn ret; | |
| GstBuffer *buffer; | |
| GstMsdkDec *thiz = GST_MSDKDEC (decoder); | |
| /* Don't parse the input buffer indeed, it will invoke | |
| * gst_msdkdec_handle_frame to handle the input buffer */ | |
| size = gst_adapter_available (adapter); | |
| gst_video_decoder_add_to_frame (decoder, size); | |
| ret = gst_video_decoder_have_frame (decoder); | |
| size = gst_adapter_available (thiz->adapter); | |
| if (size) { | |
| /* The base class will set up a new frame for parsing as | |
| * soon as there is valid data in the buffer */ | |
| buffer = gst_adapter_get_buffer (thiz->adapter, size); | |
| gst_adapter_flush (thiz->adapter, size); | |
| gst_adapter_push (adapter, buffer); | |
| } | |
| return ret; | |
| } | |
| #ifndef _WIN32 | |
| static GstBufferPool * | |
| gst_msdk_create_va_pool (GstMsdkDec * thiz, GstVideoInfo * info, | |
| guint num_buffers) | |
| { | |
| GstBufferPool *pool = NULL; | |
| GstAllocator *allocator; | |
| GArray *formats = NULL; | |
| GstAllocationParams alloc_params = { 0, 31, 0, 0 }; | |
| GstVaDisplay *display = NULL; | |
| GstCaps *caps = NULL; | |
| display = (GstVaDisplay *) gst_msdk_context_get_va_display (thiz->context); | |
| if (thiz->use_dmabuf) | |
| allocator = gst_va_dmabuf_allocator_new (display); | |
| else { | |
| formats = g_array_new (FALSE, FALSE, sizeof (GstVideoFormat)); | |
| g_array_append_val (formats, GST_VIDEO_INFO_FORMAT (info)); | |
| allocator = gst_va_allocator_new (display, formats); | |
| } | |
| gst_object_unref (display); | |
| if (!allocator) { | |
| GST_ERROR_OBJECT (thiz, "Failed to create allocator"); | |
| if (formats) | |
| g_array_unref (formats); | |
| return NULL; | |
| } | |
| if (thiz->use_dmabuf && thiz->modifier != DRM_FORMAT_MOD_INVALID) { | |
| caps = gst_msdkcaps_video_info_to_drm_caps (info, thiz->modifier); | |
| gst_caps_set_features (caps, 0, | |
| gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_DMABUF, NULL)); | |
| } else { | |
| caps = gst_video_info_to_caps (info); | |
| } | |
| pool = gst_va_pool_new_with_config (caps, num_buffers, num_buffers, | |
| VA_SURFACE_ATTRIB_USAGE_HINT_DECODER, GST_VA_FEATURE_AUTO, | |
| allocator, &alloc_params); | |
| gst_object_unref (allocator); | |
| gst_caps_unref (caps); | |
| GST_LOG_OBJECT (thiz, "Creating va pool"); | |
| return pool; | |
| } | |
| #else | |
| static GstBufferPool * | |
| gst_msdk_create_d3d11_pool (GstMsdkDec * thiz, GstVideoInfo * info, | |
| guint num_buffers) | |
| { | |
| GstBufferPool *pool = NULL; | |
| GstD3D11Device *device; | |
| GstStructure *config; | |
| GstD3D11AllocationParams *params; | |
| device = gst_msdk_context_get_d3d11_device (thiz->context); | |
| pool = gst_d3d11_buffer_pool_new (device); | |
| config = gst_buffer_pool_get_config (pool); | |
| params = gst_d3d11_allocation_params_new (device, info, | |
| GST_D3D11_ALLOCATION_FLAG_DEFAULT, | |
| D3D11_BIND_DECODER | D3D11_BIND_SHADER_RESOURCE, 0); | |
| gst_buffer_pool_config_set_d3d11_allocation_params (config, params); | |
| gst_d3d11_allocation_params_free (params); | |
| return pool; | |
| } | |
| #endif | |
| static GstBufferPool * | |
| gst_msdkdec_create_buffer_pool (GstMsdkDec * thiz, GstVideoInfo * info, | |
| guint num_buffers) | |
| { | |
| GstBufferPool *pool = NULL; | |
| GstStructure *config; | |
| GstCaps *caps; | |
| GstVideoAlignment align; | |
| GstVideoInfo vinfo = *info; | |
| gst_msdk_set_video_alignment (&vinfo, 0, 0, &align); | |
| gst_video_info_align (&vinfo, &align); | |
| if (thiz->do_copy) | |
| pool = gst_video_buffer_pool_new (); | |
| else { | |
| #ifndef _WIN32 | |
| pool = gst_msdk_create_va_pool (thiz, &vinfo, num_buffers); | |
| #else | |
| pool = gst_msdk_create_d3d11_pool (thiz, &vinfo, num_buffers); | |
| #endif | |
| } | |
| if (!pool) | |
| goto error_no_pool; | |
| #ifndef _WIN32 | |
| if (thiz->use_dmabuf) { | |
| caps = gst_msdkcaps_video_info_to_drm_caps (&vinfo, thiz->modifier); | |
| gst_caps_set_features (caps, 0, | |
| gst_caps_features_new (GST_CAPS_FEATURE_MEMORY_DMABUF, NULL)); | |
| } else { | |
| caps = gst_video_info_to_caps (&vinfo); | |
| } | |
| #else | |
| caps = gst_video_info_to_caps (&vinfo); | |
| #endif | |
| config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool)); | |
| gst_buffer_pool_config_set_params (config, caps, | |
| GST_VIDEO_INFO_SIZE (&vinfo), num_buffers, 0); | |
| gst_buffer_pool_config_add_option (config, GST_BUFFER_POOL_OPTION_VIDEO_META); | |
| gst_buffer_pool_config_add_option (config, | |
| GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT); | |
| gst_buffer_pool_config_set_video_alignment (config, &align); | |
| gst_caps_unref (caps); | |
| if (!gst_buffer_pool_set_config (pool, config)) | |
| goto error_pool_config; | |
| return pool; | |
| error_no_pool: | |
| { | |
| GST_INFO_OBJECT (thiz, "Failed to create bufferpool"); | |
| return NULL; | |
| } | |
| error_pool_config: | |
| { | |
| GST_INFO_OBJECT (thiz, "Failed to set config"); | |
| gst_object_unref (pool); | |
| return NULL; | |
| } | |
| } | |
| static gboolean | |
| gst_msdkdec_decide_allocation (GstVideoDecoder * decoder, GstQuery * query) | |
| { | |
| GstMsdkDec *thiz = GST_MSDKDEC (decoder); | |
| GstBufferPool *pool = NULL; | |
| GstStructure *pool_config = NULL; | |
| GstCaps *pool_caps = NULL; | |
| guint size = 0; | |
| guint min_buffers = 0; | |
| guint max_buffers = 0; | |
| gboolean has_videometa, has_video_alignment; | |
| GstAllocator *allocator = NULL; | |
| GstAllocationParams params; | |
| gboolean update_pool = FALSE; | |
| gboolean update_allocator = FALSE; | |
| GstVideoInfo vinfo; | |
| if (!thiz->param.mfx.FrameInfo.Width || !thiz->param.mfx.FrameInfo.Height) | |
| return FALSE; | |
| gst_query_parse_allocation (query, &pool_caps, NULL); | |
| if (!gst_msdkcaps_video_info_from_caps (pool_caps, &vinfo, NULL)) | |
| goto failed_to_parse_caps; | |
| if (gst_query_get_n_allocation_params (query) > 0) { | |
| gst_query_parse_nth_allocation_param (query, 0, &allocator, ¶ms); | |
| update_allocator = TRUE; | |
| } | |
| if (!allocator) | |
| gst_allocation_params_init (¶ms); | |
| if (gst_query_get_n_allocation_pools (query) > 0) { | |
| gst_query_parse_nth_allocation_pool (query, | |
| 0, &pool, &size, &min_buffers, &max_buffers); | |
| update_pool = TRUE; | |
| } | |
| if (!pool) | |
| pool = gst_video_buffer_pool_new (); | |
| size = MAX (size, vinfo.size); | |
| pool_config = gst_buffer_pool_get_config (pool); | |
| gst_buffer_pool_config_set_params (pool_config, | |
| pool_caps, size, min_buffers, max_buffers); | |
| gst_buffer_pool_config_set_allocator (pool_config, allocator, ¶ms); | |
| if (!gst_buffer_pool_set_config (pool, pool_config)) { | |
| pool_config = gst_buffer_pool_get_config (pool); | |
| if (!gst_buffer_pool_config_validate_params (pool_config, | |
| pool_caps, size, min_buffers, max_buffers)) { | |
| gst_object_unref (pool); | |
| pool = gst_video_buffer_pool_new (); | |
| gst_buffer_pool_config_set_params (pool_config, | |
| pool_caps, size, min_buffers, max_buffers); | |
| gst_buffer_pool_config_set_allocator (pool_config, allocator, ¶ms); | |
| } | |
| if (!gst_buffer_pool_set_config (pool, pool_config)) | |
| return FALSE; | |
| } | |
| if (update_allocator) | |
| gst_query_set_nth_allocation_param (query, 0, allocator, ¶ms); | |
| else | |
| gst_query_add_allocation_param (query, allocator, ¶ms); | |
| if (update_pool) | |
| gst_query_set_nth_allocation_pool (query, | |
| 0, pool, size, min_buffers, max_buffers); | |
| else | |
| gst_query_add_allocation_pool (query, pool, size, min_buffers, max_buffers); | |
| /* Get the buffer pool config decided on by the base class. The base | |
| class ensures that there will always be at least a 0th pool in | |
| the query. */ | |
| pool_config = gst_buffer_pool_get_config (pool); | |
| has_videometa = gst_query_find_allocation_meta | |
| (query, GST_VIDEO_META_API_TYPE, NULL); | |
| has_video_alignment = gst_buffer_pool_has_option | |
| (pool, GST_BUFFER_POOL_OPTION_VIDEO_ALIGNMENT); | |
| /* Get the caps of pool and increase the min and max buffers by async_depth. | |
| * We will always have that number of decode operations in-flight */ | |
| min_buffers += thiz->async_depth; | |
| if (max_buffers) | |
| max_buffers += thiz->async_depth; | |
| /* increase the min_buffers by 1 for smooth display in render pipeline */ | |
| min_buffers += 1; | |
| /* this will get updated with msdk requirement */ | |
| thiz->min_prealloc_buffers = min_buffers; | |
| if (gst_msdkcaps_has_feature (pool_caps, GST_CAPS_FEATURE_MEMORY_DMABUF)) { | |
| thiz->use_dmabuf = TRUE; | |
| } | |
| /* Decoder always use its own pool. So we create a pool if msdk APIs | |
| * previously requested for allocation (do_realloc = TRUE) */ | |
| if (thiz->do_realloc || !thiz->pool) { | |
| gst_clear_object (&thiz->pool); | |
| GST_INFO_OBJECT (decoder, "create new MSDK bufferpool"); | |
| thiz->pool = gst_msdkdec_create_buffer_pool | |
| (thiz, &thiz->output_state->info, min_buffers); | |
| if (!thiz->pool) { | |
| GST_ERROR_OBJECT (decoder, "failed to create new pool"); | |
| goto failed_to_create_pool; | |
| } | |
| } | |
| #ifndef _WIN32 | |
| if (!(GST_IS_VA_ALLOCATOR (allocator) || | |
| GST_IS_VA_DMABUF_ALLOCATOR (allocator))) | |
| thiz->ds_has_known_allocator = FALSE; | |
| #else | |
| if (!GST_IS_D3D11_BUFFER_POOL (pool)) { | |
| thiz->ds_has_known_allocator = FALSE; | |
| } | |
| #endif | |
| if (allocator) | |
| gst_object_unref (allocator); | |
| /* If downstream supports video meta and video alignment, or downstream | |
| * doesn't have known allocator (known allocator refers to va allocator | |
| * or d3d allocator), we replace with our own bufferpool and use it. | |
| */ | |
| if ((has_videometa && has_video_alignment) | |
| || !thiz->ds_has_known_allocator) { | |
| GstStructure *config; | |
| gst_structure_free (pool_config); | |
| gst_object_unref (pool); | |
| pool = gst_object_ref (thiz->pool); | |
| /* Set the allocator of new msdk bufferpool */ | |
| config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool)); | |
| if (gst_buffer_pool_config_get_allocator (config, &allocator, NULL)) | |
| gst_query_set_nth_allocation_param (query, 0, allocator, NULL); | |
| gst_structure_free (config); | |
| } else { | |
| /* When downstream doesn't have videometa or alignment support, | |
| * or downstream pool is va/d3d pool,we will use downstream pool | |
| * and keep decoder's own pool as side-pool. | |
| */ | |
| GST_INFO_OBJECT (decoder, "Keep MSDK bufferpool as a side-pool"); | |
| /* Update params to downstream's pool */ | |
| gst_buffer_pool_config_set_params (pool_config, pool_caps, size, | |
| min_buffers, max_buffers); | |
| if (!gst_buffer_pool_set_config (pool, pool_config)) | |
| goto error_set_config; | |
| thiz->non_msdk_pool_info = vinfo; | |
| /* update width and height with actual negotiated values */ | |
| GST_VIDEO_INFO_WIDTH (&thiz->non_msdk_pool_info) = | |
| GST_VIDEO_INFO_WIDTH (&thiz->output_state->info); | |
| GST_VIDEO_INFO_HEIGHT (&thiz->non_msdk_pool_info) = | |
| GST_VIDEO_INFO_HEIGHT (&thiz->output_state->info); | |
| } | |
| gst_msdk_context_set_alloc_pool (thiz->context, pool); | |
| /* Initialize MSDK decoder before new bufferpool tries to alloc each buffer, | |
| * which requires information about frame allocation. | |
| * No effect if already initialized. | |
| */ | |
| if (!gst_msdkdec_init_decoder (thiz)) | |
| return FALSE; | |
| /* get the updated min_buffers, which account for the msdk requirement as well */ | |
| min_buffers = thiz->min_prealloc_buffers; | |
| if (!has_videometa && !thiz->ds_has_known_allocator | |
| && gst_msdkcaps_has_feature (pool_caps, | |
| GST_CAPS_FEATURE_MEMORY_SYSTEM_MEMORY)) { | |
| /* We need to create other pool with system memory for copy use under conditions: | |
| * (1) downstream has no videometa; (2) downstream allocator is unknown; | |
| * (3) negotiated caps is raw. | |
| */ | |
| thiz->do_copy = TRUE; | |
| thiz->other_pool = | |
| gst_msdkdec_create_buffer_pool (thiz, &thiz->output_state->info, | |
| min_buffers); | |
| } | |
| gst_query_set_nth_allocation_pool (query, 0, pool, size, min_buffers, | |
| max_buffers); | |
| gst_object_unref (pool); | |
| return TRUE; | |
| failed_to_parse_caps: | |
| GST_ERROR_OBJECT (decoder, "failed to set buffer pool config"); | |
| return FALSE; | |
| failed_to_create_pool: | |
| GST_ERROR_OBJECT (decoder, "failed to set buffer pool config"); | |
| gst_object_unref (pool); | |
| return FALSE; | |
| error_set_config: | |
| GST_ERROR_OBJECT (decoder, "failed to set buffer pool config"); | |
| gst_object_unref (pool); | |
| return FALSE; | |
| } | |
| static GstFlowReturn | |
| gst_msdkdec_drain (GstVideoDecoder * decoder) | |
| { | |
| GstMsdkDec *thiz = GST_MSDKDEC (decoder); | |
| GstFlowReturn flow; | |
| MsdkDecTask *task; | |
| GstMsdkSurface *surface = NULL; | |
| mfxFrameSurface1 *out_surface; | |
| mfxSession session; | |
| mfxStatus status; | |
| guint i; | |
| if (!thiz->initialized) | |
| return GST_FLOW_OK; | |
| session = gst_msdk_context_get_session (thiz->context); | |
| for (;;) { | |
| task = &g_array_index (thiz->tasks, MsdkDecTask, thiz->next_task); | |
| if ((flow = gst_msdkdec_finish_task (thiz, task)) != GST_FLOW_OK) { | |
| if (flow != GST_FLOW_FLUSHING) | |
| GST_WARNING_OBJECT (decoder, | |
| "failed to finish the task %p, but keep draining for the remaining frames", | |
| task); | |
| } | |
| if (!surface) { | |
| surface = allocate_output_surface (thiz); | |
| if (!surface) | |
| return GST_FLOW_ERROR; | |
| } | |
| #if (MFX_VERSION >= 1025) | |
| if (thiz->report_error) | |
| thiz->error_report.ErrorTypes = 0; | |
| #endif | |
| status = | |
| MFXVideoDECODE_DecodeFrameAsync (session, NULL, surface->surface, | |
| &out_surface, &task->sync_point); | |
| if (!find_msdk_surface (thiz, task, out_surface)) { | |
| return GST_FLOW_ERROR; | |
| } | |
| GST_DEBUG_OBJECT (decoder, "DecodeFrameAsync => %d", status); | |
| gst_msdkdec_error_report (thiz); | |
| if (G_LIKELY (status == MFX_ERR_NONE)) { | |
| thiz->next_task = (thiz->next_task + 1) % thiz->tasks->len; | |
| surface = NULL; | |
| } else if (status == MFX_WRN_VIDEO_PARAM_CHANGED) { | |
| continue; | |
| } else if (status == MFX_WRN_DEVICE_BUSY) { | |
| /* If device is busy, wait 1ms and retry, as per MSDK's recomendation */ | |
| g_usleep (1000); | |
| /* If the current surface is still busy, we should do sync operation, | |
| * then try to decode again | |
| */ | |
| thiz->next_task = (thiz->next_task + 1) % thiz->tasks->len; | |
| } else if (status == MFX_ERR_MORE_DATA) { | |
| break; | |
| } else if (status == MFX_ERR_MORE_SURFACE) { | |
| surface = NULL; | |
| continue; | |
| } else if (status < MFX_ERR_NONE) | |
| return GST_FLOW_ERROR; | |
| } | |
| for (i = 0; i < thiz->tasks->len; i++) { | |
| task = &g_array_index (thiz->tasks, MsdkDecTask, thiz->next_task); | |
| gst_msdkdec_finish_task (thiz, task); | |
| thiz->next_task = (thiz->next_task + 1) % thiz->tasks->len; | |
| } | |
| return GST_FLOW_OK; | |
| } | |
| static gboolean | |
| gst_msdkdec_flush (GstVideoDecoder * decoder) | |
| { | |
| GstMsdkDec *thiz = GST_MSDKDEC (decoder); | |
| GstFlowReturn ret; | |
| ret = gst_msdkdec_drain (GST_VIDEO_DECODER_CAST (thiz)); | |
| return ret == GST_FLOW_OK; | |
| } | |
| static GstFlowReturn | |
| gst_msdkdec_finish (GstVideoDecoder * decoder) | |
| { | |
| return gst_msdkdec_drain (decoder); | |
| } | |
| static gboolean | |
| gst_msdkdec_query (GstVideoDecoder * decoder, GstQuery * query, | |
| GstPadDirection dir) | |
| { | |
| GstMsdkDec *thiz = GST_MSDKDEC (decoder); | |
| gboolean ret = FALSE; | |
| switch (GST_QUERY_TYPE (query)) { | |
| case GST_QUERY_CONTEXT:{ | |
| GstMsdkContext *msdk_context = NULL; | |
| gst_object_replace ((GstObject **) & msdk_context, | |
| (GstObject *) thiz->context); | |
| ret = gst_msdk_handle_context_query (GST_ELEMENT_CAST (decoder), | |
| query, msdk_context); | |
| gst_clear_object (&msdk_context); | |
| break; | |
| } | |
| default: | |
| if (dir == GST_PAD_SRC) { | |
| ret = | |
| GST_VIDEO_DECODER_CLASS (parent_class)->src_query (decoder, query); | |
| } else { | |
| ret = | |
| GST_VIDEO_DECODER_CLASS (parent_class)->sink_query (decoder, query); | |
| } | |
| break; | |
| } | |
| return ret; | |
| } | |
| static gboolean | |
| gst_msdkdec_src_query (GstVideoDecoder * decoder, GstQuery * query) | |
| { | |
| return gst_msdkdec_query (decoder, query, GST_PAD_SRC); | |
| } | |
| static gboolean | |
| gst_msdkdec_sink_query (GstVideoDecoder * decoder, GstQuery * query) | |
| { | |
| return gst_msdkdec_query (decoder, query, GST_PAD_SINK); | |
| } | |
| static void | |
| gst_msdkdec_set_property (GObject * object, guint prop_id, const GValue * value, | |
| GParamSpec * pspec) | |
| { | |
| GstMsdkDec *thiz = GST_MSDKDEC (object); | |
| GstState state; | |
| GST_OBJECT_LOCK (thiz); | |
| state = GST_STATE (thiz); | |
| if ((state != GST_STATE_READY && state != GST_STATE_NULL) && | |
| !(pspec->flags & GST_PARAM_MUTABLE_PLAYING)) | |
| goto wrong_state; | |
| switch (prop_id) { | |
| case GST_MSDKDEC_PROP_HARDWARE: | |
| thiz->hardware = g_value_get_boolean (value); | |
| break; | |
| case GST_MSDKDEC_PROP_ASYNC_DEPTH: | |
| thiz->async_depth = g_value_get_uint (value); | |
| break; | |
| default: | |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
| break; | |
| } | |
| GST_OBJECT_UNLOCK (thiz); | |
| return; | |
| /* ERROR */ | |
| wrong_state: | |
| { | |
| GST_WARNING_OBJECT (thiz, "setting property in wrong state"); | |
| GST_OBJECT_UNLOCK (thiz); | |
| } | |
| } | |
| static void | |
| gst_msdkdec_get_property (GObject * object, guint prop_id, GValue * value, | |
| GParamSpec * pspec) | |
| { | |
| GstMsdkDec *thiz = GST_MSDKDEC (object); | |
| GST_OBJECT_LOCK (thiz); | |
| switch (prop_id) { | |
| case GST_MSDKDEC_PROP_HARDWARE: | |
| g_value_set_boolean (value, thiz->hardware); | |
| break; | |
| case GST_MSDKDEC_PROP_ASYNC_DEPTH: | |
| g_value_set_uint (value, thiz->async_depth); | |
| break; | |
| default: | |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); | |
| break; | |
| } | |
| GST_OBJECT_UNLOCK (thiz); | |
| } | |
| static void | |
| gst_msdkdec_dispose (GObject * object) | |
| { | |
| GstMsdkDec *thiz = GST_MSDKDEC (object); | |
| g_clear_object (&thiz->adapter); | |
| gst_clear_object (&thiz->context); | |
| gst_clear_object (&thiz->old_context); | |
| G_OBJECT_CLASS (parent_class)->dispose (object); | |
| } | |
| static void | |
| gst_msdkdec_finalize (GObject * object) | |
| { | |
| GstMsdkDec *thiz = GST_MSDKDEC (object); | |
| g_array_unref (thiz->tasks); | |
| thiz->tasks = NULL; | |
| release_msdk_surfaces (thiz); | |
| G_OBJECT_CLASS (parent_class)->finalize (object); | |
| } | |
| static gboolean | |
| gst_msdkdec_post_configure (GstMsdkDec * decoder) | |
| { | |
| /* Do nothing */ | |
| return TRUE; | |
| } | |
| static gboolean | |
| gst_msdkdec_preinit_decoder (GstMsdkDec * decoder) | |
| { | |
| decoder->param.mfx.FrameInfo.Width = | |
| GST_ROUND_UP_16 (decoder->param.mfx.FrameInfo.Width); | |
| decoder->param.mfx.FrameInfo.Height = | |
| GST_ROUND_UP_32 (decoder->param.mfx.FrameInfo.Height); | |
| decoder->param.mfx.FrameInfo.PicStruct = | |
| decoder->param.mfx.FrameInfo.PicStruct ? decoder->param.mfx. | |
| FrameInfo.PicStruct : MFX_PICSTRUCT_PROGRESSIVE; | |
| return TRUE; | |
| } | |
| static gboolean | |
| gst_msdkdec_postinit_decoder (GstMsdkDec * decoder) | |
| { | |
| /* Do nothing */ | |
| return TRUE; | |
| } | |
| static gboolean | |
| gst_msdkdec_transform_meta (GstVideoDecoder * decoder, | |
| GstVideoCodecFrame * frame, GstMeta * meta) | |
| { | |
| const GstMetaInfo *info = meta->info; | |
| if (GST_VIDEO_DECODER_CLASS (parent_class)->transform_meta (decoder, frame, | |
| meta)) | |
| return TRUE; | |
| if (!g_strcmp0 (g_type_name (info->type), "GstVideoRegionOfInterestMeta")) | |
| return TRUE; | |
| return FALSE; | |
| } | |
| static void | |
| gst_msdkdec_class_init (GstMsdkDecClass * klass) | |
| { | |
| GObjectClass *gobject_class; | |
| GstElementClass *element_class; | |
| GstVideoDecoderClass *decoder_class; | |
| gobject_class = G_OBJECT_CLASS (klass); | |
| element_class = GST_ELEMENT_CLASS (klass); | |
| decoder_class = GST_VIDEO_DECODER_CLASS (klass); | |
| gobject_class->set_property = gst_msdkdec_set_property; | |
| gobject_class->get_property = gst_msdkdec_get_property; | |
| gobject_class->dispose = gst_msdkdec_dispose; | |
| gobject_class->finalize = gst_msdkdec_finalize; | |
| element_class->set_context = gst_msdkdec_set_context; | |
| decoder_class->close = GST_DEBUG_FUNCPTR (gst_msdkdec_close); | |
| decoder_class->start = GST_DEBUG_FUNCPTR (gst_msdkdec_start); | |
| decoder_class->stop = GST_DEBUG_FUNCPTR (gst_msdkdec_stop); | |
| decoder_class->set_format = GST_DEBUG_FUNCPTR (gst_msdkdec_set_format); | |
| decoder_class->finish = GST_DEBUG_FUNCPTR (gst_msdkdec_finish); | |
| decoder_class->handle_frame = GST_DEBUG_FUNCPTR (gst_msdkdec_handle_frame); | |
| decoder_class->parse = GST_DEBUG_FUNCPTR (gst_msdkdec_parse); | |
| decoder_class->decide_allocation = | |
| GST_DEBUG_FUNCPTR (gst_msdkdec_decide_allocation); | |
| decoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_msdkdec_getcaps); | |
| decoder_class->flush = GST_DEBUG_FUNCPTR (gst_msdkdec_flush); | |
| decoder_class->drain = GST_DEBUG_FUNCPTR (gst_msdkdec_drain); | |
| decoder_class->transform_meta = | |
| GST_DEBUG_FUNCPTR (gst_msdkdec_transform_meta); | |
| decoder_class->src_query = GST_DEBUG_FUNCPTR (gst_msdkdec_src_query); | |
| decoder_class->sink_query = GST_DEBUG_FUNCPTR (gst_msdkdec_sink_query); | |
| klass->post_configure = GST_DEBUG_FUNCPTR (gst_msdkdec_post_configure); | |
| klass->preinit_decoder = GST_DEBUG_FUNCPTR (gst_msdkdec_preinit_decoder); | |
| klass->postinit_decoder = GST_DEBUG_FUNCPTR (gst_msdkdec_postinit_decoder); | |
| g_object_class_install_property (gobject_class, GST_MSDKDEC_PROP_HARDWARE, | |
| g_param_spec_boolean ("hardware", "Hardware", "Enable hardware decoders", | |
| PROP_HARDWARE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); | |
| g_object_class_install_property (gobject_class, GST_MSDKDEC_PROP_ASYNC_DEPTH, | |
| g_param_spec_uint ("async-depth", "Async Depth", | |
| "Depth of asynchronous pipeline", | |
| 1, 20, PROP_ASYNC_DEPTH_DEFAULT, | |
| G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); | |
| } | |
| static void | |
| gst_msdkdec_init (GstMsdkDec * thiz) | |
| { | |
| gst_video_info_init (&thiz->non_msdk_pool_info); | |
| thiz->tasks = g_array_new (FALSE, TRUE, sizeof (MsdkDecTask)); | |
| thiz->hardware = PROP_HARDWARE_DEFAULT; | |
| thiz->async_depth = PROP_ASYNC_DEPTH_DEFAULT; | |
| thiz->do_renego = TRUE; | |
| thiz->do_realloc = TRUE; | |
| thiz->force_reset_on_res_change = TRUE; | |
| thiz->report_error = FALSE; | |
| thiz->sfc = FALSE; | |
| thiz->ds_has_known_allocator = TRUE; | |
| #ifndef _WIN32 | |
| thiz->modifier = DRM_FORMAT_MOD_INVALID; | |
| #endif | |
| thiz->adapter = gst_adapter_new (); | |
| thiz->input_state = NULL; | |
| thiz->output_state = NULL; | |
| thiz->pool = NULL; | |
| thiz->context = NULL; | |
| } |