mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-24 01:00:37 +00:00
4a22cc8fb3
VA drivers allocate surfaces given their properties, so there's no need to provide a buffer size to the VA pool. Though, the buffer size is provided by the driver, or the canonical size is used for single planed surfaces. This patch removes the need to provide a size for the function gst_va_pool_new_with_config() and adds a helper method to retrieve the surface size, gst_va_pool_get_buffer_size(). Also change the callers accordingly. Changes for custom VA pool creation will be addressed in the following commits. Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5805>
2436 lines
78 KiB
C
2436 lines
78 KiB
C
/* 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 <libdrm/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
|
|
|
|
GstVideoCodecState *output_state =
|
|
gst_video_decoder_get_output_state (GST_VIDEO_DECODER (thiz));
|
|
|
|
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 = &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);
|
|
}
|
|
}
|
|
#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)) {
|
|
*filter = caps;
|
|
ret = TRUE;
|
|
}
|
|
|
|
done:
|
|
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);
|
|
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)
|
|
{
|
|
GstVideoCodecState *output_state;
|
|
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;
|
|
}
|
|
|
|
src_caps = gst_pad_query_caps (GST_VIDEO_DECODER (thiz)->srcpad, NULL);
|
|
temp_caps = gst_caps_make_writable (src_caps);
|
|
|
|
|
|
#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);
|
|
#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) {
|
|
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);
|
|
|
|
if (gst_caps_is_empty (gst_pad_peer_query_caps (GST_VIDEO_DECODER
|
|
(thiz)->srcpad, temp_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 (temp_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);
|
|
}
|
|
}
|
|
#endif
|
|
gst_caps_unref (temp_caps);
|
|
|
|
output_state =
|
|
gst_video_decoder_set_output_state (GST_VIDEO_DECODER (thiz),
|
|
format, width, height, thiz->input_state);
|
|
if (!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 = 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
|
|
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 (&output_state->info));
|
|
height =
|
|
GST_ROUND_UP_32 (thiz->param.mfx.FrameInfo.Height ? thiz->param.mfx.
|
|
FrameInfo.Height : GST_VIDEO_INFO_HEIGHT (&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 (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 (&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 */
|
|
}
|
|
gst_video_codec_state_unref (output_state);
|
|
|
|
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;
|
|
GstVideoCodecState *output_state =
|
|
gst_video_decoder_get_output_state (GST_VIDEO_DECODER (thiz));
|
|
|
|
src_info = &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);
|
|
gst_video_codec_state_unref (output_state);
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
gst_buffer_unref (buffer);
|
|
gst_buffer_pool_set_active (thiz->other_pool, FALSE);
|
|
gst_object_unref (thiz->other_pool);
|
|
|
|
error_active:
|
|
gst_video_codec_state_unref (output_state);
|
|
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) {
|
|
GstVideoCodecState *output_state =
|
|
gst_video_decoder_get_output_state (GST_VIDEO_DECODER (thiz));
|
|
/* detect whether the resolution change and negotiate with downstream if so */
|
|
if ((surface->surface->Info.CropW && surface->surface->Info.CropH)
|
|
&& ((output_state->info.width != surface->surface->Info.CropW)
|
|
|| (output_state->info.height != surface->surface->Info.CropH))) {
|
|
output_state->info.width = surface->surface->Info.CropW;
|
|
output_state->info.height = surface->surface->Info.CropH;
|
|
output_state->caps = gst_video_info_to_caps (&output_state->info);
|
|
if (!gst_video_decoder_negotiate (GST_VIDEO_DECODER (thiz))) {
|
|
GST_ERROR_OBJECT (thiz, "Failed to negotiate");
|
|
gst_video_codec_state_unref (output_state);
|
|
return GST_FLOW_NOT_NEGOTIATED;
|
|
}
|
|
}
|
|
gst_video_codec_state_unref (output_state);
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
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_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 {
|
|
GstVideoCodecState *output_state =
|
|
gst_video_decoder_get_output_state (GST_VIDEO_DECODER (thiz));
|
|
if (output_state) {
|
|
if (output_state->allocation_caps) {
|
|
if (!gst_msdkcaps_video_info_from_caps (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;
|
|
}
|
|
gst_video_codec_state_unref (output_state);
|
|
}
|
|
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
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);
|
|
|
|
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) {
|
|
GstVideoCodecState *output_state =
|
|
gst_video_decoder_get_output_state (GST_VIDEO_DECODER (thiz));
|
|
gst_clear_object (&thiz->pool);
|
|
GST_INFO_OBJECT (decoder, "create new MSDK bufferpool");
|
|
thiz->pool =
|
|
gst_msdkdec_create_buffer_pool (thiz, &output_state->info, min_buffers);
|
|
gst_video_codec_state_unref (output_state);
|
|
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.
|
|
*/
|
|
GstVideoCodecState *output_state = NULL;
|
|
|
|
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 */
|
|
output_state =
|
|
gst_video_decoder_get_output_state (GST_VIDEO_DECODER (thiz));
|
|
GST_VIDEO_INFO_WIDTH (&thiz->non_msdk_pool_info) =
|
|
GST_VIDEO_INFO_WIDTH (&output_state->info);
|
|
GST_VIDEO_INFO_HEIGHT (&thiz->non_msdk_pool_info) =
|
|
GST_VIDEO_INFO_HEIGHT (&output_state->info);
|
|
|
|
gst_video_codec_state_unref (output_state);
|
|
}
|
|
|
|
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;
|
|
GstVideoCodecState *output_state =
|
|
gst_video_decoder_get_output_state (GST_VIDEO_DECODER (thiz));
|
|
thiz->other_pool =
|
|
gst_msdkdec_create_buffer_pool (thiz, &output_state->info, min_buffers);
|
|
gst_video_codec_state_unref (output_state);
|
|
}
|
|
|
|
gst_query_set_nth_allocation_pool (query, 0, pool, size, min_buffers,
|
|
max_buffers);
|
|
|
|
gst_caps_unref (pool_caps);
|
|
gst_object_unref (pool);
|
|
|
|
return TRUE;
|
|
|
|
failed_to_parse_caps:
|
|
GST_ERROR_OBJECT (decoder, "failed to set buffer pool config");
|
|
gst_caps_unref (pool_caps);
|
|
return FALSE;
|
|
|
|
failed_to_create_pool:
|
|
GST_ERROR_OBJECT (decoder, "failed to set buffer pool config");
|
|
gst_caps_unref (pool_caps);
|
|
gst_object_unref (pool);
|
|
return FALSE;
|
|
|
|
error_set_config:
|
|
GST_ERROR_OBJECT (decoder, "failed to set buffer pool config");
|
|
gst_caps_unref (pool_caps);
|
|
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->pool = NULL;
|
|
thiz->context = NULL;
|
|
}
|