gstreamer/sys/msdk/gstmsdkenc.c
Hyunjun Ko 375a50a876 msdk: add async depth from each msdk element to GstMsdkContext to be shared
In case that pipeline is like ".. ! decoder ! encoder ! ..." with using
video memory,
decoder needs to know the async depth of the following msdk element so
that it could
allocate the correct number of video memory.

Otherwise, decoder's memory is exhausted while processing.

https://bugzilla.gnome.org/show_bug.cgi?id=790752
2018-02-13 13:52:14 -09:00

1508 lines
45 KiB
C

/* GStreamer Intel MSDK plugin
* Copyright (c) 2016, Oblong Industries, Inc.
* 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.
*/
/* TODO:
* - Add support for interlaced content
* - Add support for MVC AVC
* - Wrap more configuration options and maybe move properties to derived
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#ifdef _WIN32
# include <malloc.h>
#endif
#include <stdlib.h>
#include "gstmsdkenc.h"
#include "gstmsdkbufferpool.h"
#include "gstmsdkvideomemory.h"
#include "gstmsdksystemmemory.h"
#include "gstmsdkcontextutil.h"
static inline void *
_aligned_alloc (size_t alignment, size_t size)
{
#ifdef _WIN32
return _aligned_malloc (size, alignment);
#else
void *out;
if (posix_memalign (&out, alignment, size) != 0)
out = NULL;
return out;
#endif
}
#ifndef _WIN32
#define _aligned_free free
#endif
static void gst_msdkenc_close_encoder (GstMsdkEnc * thiz);
GST_DEBUG_CATEGORY_EXTERN (gst_msdkenc_debug);
#define GST_CAT_DEFAULT gst_msdkenc_debug
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS ("video/x-raw, "
"format = (string) { NV12, I420, YV12, YUY2, UYVY, BGRA }, "
"framerate = (fraction) [0, MAX], "
"width = (int) [ 16, MAX ], height = (int) [ 16, MAX ],"
"interlace-mode = (string) progressive")
);
enum
{
PROP_0,
PROP_HARDWARE,
PROP_ASYNC_DEPTH,
PROP_TARGET_USAGE,
PROP_RATE_CONTROL,
PROP_BITRATE,
PROP_QPI,
PROP_QPP,
PROP_QPB,
PROP_GOP_SIZE,
PROP_REF_FRAMES,
PROP_I_FRAMES,
PROP_B_FRAMES
};
#define PROP_HARDWARE_DEFAULT TRUE
#define PROP_ASYNC_DEPTH_DEFAULT 4
#define PROP_TARGET_USAGE_DEFAULT (MFX_TARGETUSAGE_BALANCED)
#define PROP_RATE_CONTROL_DEFAULT (MFX_RATECONTROL_CBR)
#define PROP_BITRATE_DEFAULT (2 * 1024)
#define PROP_QPI_DEFAULT 0
#define PROP_QPP_DEFAULT 0
#define PROP_QPB_DEFAULT 0
#define PROP_GOP_SIZE_DEFAULT 256
#define PROP_REF_FRAMES_DEFAULT 1
#define PROP_I_FRAMES_DEFAULT 0
#define PROP_B_FRAMES_DEFAULT 0
#define GST_MSDKENC_RATE_CONTROL_TYPE (gst_msdkenc_rate_control_get_type())
static GType
gst_msdkenc_rate_control_get_type (void)
{
static GType type = 0;
static const GEnumValue values[] = {
{MFX_RATECONTROL_CBR, "Constant Bitrate", "cbr"},
{MFX_RATECONTROL_VBR, "Variable Bitrate", "vbr"},
{MFX_RATECONTROL_CQP, "Constant Quantizer", "cqp"},
{MFX_RATECONTROL_AVBR, "Average Bitrate", "avbr"},
{0, NULL, NULL}
};
if (!type) {
type = g_enum_register_static ("GstMsdkEncRateControl", values);
}
return type;
}
#define gst_msdkenc_parent_class parent_class
G_DEFINE_TYPE (GstMsdkEnc, gst_msdkenc, GST_TYPE_VIDEO_ENCODER);
typedef struct
{
mfxFrameSurface1 *surface;
GstBuffer *buf;
} MsdkSurface;
void
gst_msdkenc_add_extra_param (GstMsdkEnc * thiz, mfxExtBuffer * param)
{
if (thiz->num_extra_params < MAX_EXTRA_PARAMS) {
thiz->extra_params[thiz->num_extra_params] = param;
thiz->num_extra_params++;
}
}
static void
gst_msdkenc_set_context (GstElement * element, GstContext * context)
{
GstMsdkContext *msdk_context = NULL;
GstMsdkEnc *thiz = GST_MSDKENC (element);
if (gst_msdk_context_get_context (context, &msdk_context)) {
gst_object_replace ((GstObject **) & thiz->context,
(GstObject *) msdk_context);
gst_object_unref (msdk_context);
}
GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
}
static gboolean
gst_msdkenc_init_encoder (GstMsdkEnc * thiz)
{
GstMsdkEncClass *klass = GST_MSDKENC_GET_CLASS (thiz);
GstVideoInfo *info;
mfxSession session;
mfxStatus status;
mfxFrameAllocRequest request[2];
guint i;
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;
GST_OBJECT_LOCK (thiz);
session = gst_msdk_context_get_session (thiz->context);
thiz->has_vpp = FALSE;
if (thiz->use_video_memory)
gst_msdk_set_frame_allocator (thiz->context);
if (info->finfo->format != GST_VIDEO_FORMAT_NV12) {
if (thiz->use_video_memory)
thiz->vpp_param.IOPattern =
MFX_IOPATTERN_IN_VIDEO_MEMORY | MFX_IOPATTERN_OUT_VIDEO_MEMORY;
else
thiz->vpp_param.IOPattern =
MFX_IOPATTERN_IN_SYSTEM_MEMORY | MFX_IOPATTERN_OUT_SYSTEM_MEMORY;
thiz->vpp_param.vpp.In.Width = GST_ROUND_UP_32 (info->width);
thiz->vpp_param.vpp.In.Height = GST_ROUND_UP_32 (info->height);
thiz->vpp_param.vpp.In.CropW = info->width;
thiz->vpp_param.vpp.In.CropH = info->height;
thiz->vpp_param.vpp.In.FrameRateExtN = info->fps_n;
thiz->vpp_param.vpp.In.FrameRateExtD = info->fps_d;
thiz->vpp_param.vpp.In.AspectRatioW = info->par_n;
thiz->vpp_param.vpp.In.AspectRatioH = info->par_d;
thiz->vpp_param.vpp.In.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
switch (info->finfo->format) {
case GST_VIDEO_FORMAT_NV12:
thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_NV12;
thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
break;
case GST_VIDEO_FORMAT_YV12:
case GST_VIDEO_FORMAT_I420:
thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_YV12;
thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
break;
case GST_VIDEO_FORMAT_YUY2:
thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_YUY2;
thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV422;
break;
case GST_VIDEO_FORMAT_UYVY:
thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_UYVY;
thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV422;
break;
case GST_VIDEO_FORMAT_BGRA:
thiz->vpp_param.vpp.In.FourCC = MFX_FOURCC_RGB4;
thiz->vpp_param.vpp.In.ChromaFormat = MFX_CHROMAFORMAT_YUV444;
break;
default:
g_assert_not_reached ();
break;
}
thiz->vpp_param.vpp.Out = thiz->vpp_param.vpp.In;
thiz->vpp_param.vpp.Out.FourCC = MFX_FOURCC_NV12;
thiz->vpp_param.vpp.Out.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
/* validate parameters and allow the Media SDK to make adjustments */
status = MFXVideoVPP_Query (session, &thiz->vpp_param, &thiz->vpp_param);
if (status < MFX_ERR_NONE) {
GST_ERROR_OBJECT (thiz, "Video VPP Query failed (%s)",
msdk_status_to_string (status));
goto no_vpp;
} else if (status > MFX_ERR_NONE) {
GST_WARNING_OBJECT (thiz, "Video VPP Query returned: %s",
msdk_status_to_string (status));
}
status = MFXVideoVPP_QueryIOSurf (session, &thiz->vpp_param, request);
if (status < MFX_ERR_NONE) {
GST_ERROR_OBJECT (thiz, "VPP Query IO surfaces failed (%s)",
msdk_status_to_string (status));
goto no_vpp;
} else if (status > MFX_ERR_NONE) {
GST_WARNING_OBJECT (thiz, "VPP Query IO surfaces returned: %s",
msdk_status_to_string (status));
}
if (thiz->use_video_memory)
request[0].NumFrameSuggested +=
gst_msdk_context_get_shared_async_depth (thiz->context);
thiz->num_vpp_surfaces = request[0].NumFrameSuggested;
if (thiz->use_video_memory)
gst_msdk_frame_alloc (thiz->context, &(request[0]),
&thiz->vpp_alloc_resp);
status = MFXVideoVPP_Init (session, &thiz->vpp_param);
if (status < MFX_ERR_NONE) {
GST_ERROR_OBJECT (thiz, "Init failed (%s)",
msdk_status_to_string (status));
goto no_vpp;
} else if (status > MFX_ERR_NONE) {
GST_WARNING_OBJECT (thiz, "Init returned: %s",
msdk_status_to_string (status));
}
status = MFXVideoVPP_GetVideoParam (session, &thiz->vpp_param);
if (status < MFX_ERR_NONE) {
GST_ERROR_OBJECT (thiz, "Get VPP Parameters failed (%s)",
msdk_status_to_string (status));
MFXVideoVPP_Close (session);
goto no_vpp;
} else if (status > MFX_ERR_NONE) {
GST_WARNING_OBJECT (thiz, "Get VPP Parameters returned: %s",
msdk_status_to_string (status));
}
thiz->has_vpp = TRUE;
}
thiz->param.AsyncDepth = thiz->async_depth;
if (thiz->use_video_memory)
thiz->param.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY;
else
thiz->param.IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY;
thiz->param.mfx.RateControlMethod = thiz->rate_control;
thiz->param.mfx.TargetKbps = thiz->bitrate;
thiz->param.mfx.TargetUsage = thiz->target_usage;
thiz->param.mfx.GopPicSize = thiz->gop_size;
thiz->param.mfx.GopRefDist = thiz->b_frames + 1;
thiz->param.mfx.IdrInterval = thiz->i_frames;
thiz->param.mfx.NumRefFrame = thiz->ref_frames;
thiz->param.mfx.EncodedOrder = 0; /* Take input frames in display order */
if (thiz->rate_control == MFX_RATECONTROL_CQP) {
thiz->param.mfx.QPI = thiz->qpi;
thiz->param.mfx.QPP = thiz->qpp;
thiz->param.mfx.QPB = thiz->qpb;
}
thiz->param.mfx.FrameInfo.Width = GST_ROUND_UP_32 (info->width);
thiz->param.mfx.FrameInfo.Height = GST_ROUND_UP_32 (info->height);
thiz->param.mfx.FrameInfo.CropW = info->width;
thiz->param.mfx.FrameInfo.CropH = info->height;
thiz->param.mfx.FrameInfo.FrameRateExtN = info->fps_n;
thiz->param.mfx.FrameInfo.FrameRateExtD = info->fps_d;
thiz->param.mfx.FrameInfo.AspectRatioW = info->par_n;
thiz->param.mfx.FrameInfo.AspectRatioH = info->par_d;
thiz->param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
thiz->param.mfx.FrameInfo.FourCC = MFX_FOURCC_NV12;
thiz->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
/* allow subclass configure further */
if (klass->configure) {
if (!klass->configure (thiz))
goto failed;
}
if (thiz->num_extra_params) {
thiz->param.NumExtParam = thiz->num_extra_params;
thiz->param.ExtParam = thiz->extra_params;
}
/* validate parameters and allow the Media SDK to make adjustments */
status = MFXVideoENCODE_Query (session, &thiz->param, &thiz->param);
if (status < MFX_ERR_NONE) {
GST_ERROR_OBJECT (thiz, "Video Encode Query failed (%s)",
msdk_status_to_string (status));
goto failed;
} else if (status > MFX_ERR_NONE) {
GST_WARNING_OBJECT (thiz, "Video Encode Query returned: %s",
msdk_status_to_string (status));
}
status = MFXVideoENCODE_QueryIOSurf (session, &thiz->param, request);
if (status < MFX_ERR_NONE) {
GST_ERROR_OBJECT (thiz, "Encode Query IO surfaces failed (%s)",
msdk_status_to_string (status));
goto failed;
} else if (status > MFX_ERR_NONE) {
GST_WARNING_OBJECT (thiz, "Encode Query IO surfaces returned: %s",
msdk_status_to_string (status));
}
if (thiz->has_vpp)
request[0].NumFrameSuggested += thiz->num_vpp_surfaces + 1 - 4;
if (thiz->use_video_memory)
gst_msdk_frame_alloc (thiz->context, &(request[0]), &thiz->alloc_resp);
/* Maximum of VPP output and encoder input, if using VPP */
if (thiz->has_vpp)
request[0].NumFrameSuggested =
MAX (request[0].NumFrameSuggested, request[1].NumFrameSuggested);
if (request[0].NumFrameSuggested < thiz->param.AsyncDepth) {
GST_ERROR_OBJECT (thiz, "Required %d surfaces (%d suggested), async %d",
request[0].NumFrameMin, request[0].NumFrameSuggested,
thiz->param.AsyncDepth);
goto failed;
}
/* This is VPP output (if any) and encoder input */
thiz->num_surfaces = request[0].NumFrameSuggested;
GST_DEBUG_OBJECT (thiz, "Required %d surfaces (%d suggested), allocated %d",
request[0].NumFrameMin, request[0].NumFrameSuggested, thiz->num_surfaces);
status = MFXVideoENCODE_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 = MFXVideoENCODE_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));
}
thiz->num_tasks = thiz->param.AsyncDepth;
thiz->tasks = g_new0 (MsdkEncTask, thiz->num_tasks);
for (i = 0; i < thiz->num_tasks; i++) {
thiz->tasks[i].output_bitstream.Data = _aligned_alloc (32,
thiz->param.mfx.BufferSizeInKB * 1024);
if (!thiz->tasks[i].output_bitstream.Data) {
GST_ERROR_OBJECT (thiz, "Memory allocation failed");
goto failed;
}
thiz->tasks[i].output_bitstream.MaxLength =
thiz->param.mfx.BufferSizeInKB * 1024;
}
thiz->next_task = 0;
thiz->reconfig = FALSE;
thiz->initialized = TRUE;
GST_OBJECT_UNLOCK (thiz);
return TRUE;
no_vpp:
failed:
GST_OBJECT_UNLOCK (thiz);
return FALSE;
}
static void
gst_msdkenc_close_encoder (GstMsdkEnc * thiz)
{
guint i;
mfxStatus status;
if (!thiz->context || !thiz->initialized)
return;
GST_DEBUG_OBJECT (thiz, "Closing encoder with context %" GST_PTR_FORMAT,
thiz->context);
gst_object_replace ((GstObject **) & thiz->msdk_pool, NULL);
gst_object_replace ((GstObject **) & thiz->msdk_converted_pool, NULL);
if (thiz->use_video_memory)
gst_msdk_frame_free (thiz->context, &thiz->alloc_resp);
status = MFXVideoENCODE_Close (gst_msdk_context_get_session (thiz->context));
if (status != MFX_ERR_NONE && status != MFX_ERR_NOT_INITIALIZED) {
GST_WARNING_OBJECT (thiz, "Encoder close failed (%s)",
msdk_status_to_string (status));
}
if (thiz->tasks) {
for (i = 0; i < thiz->num_tasks; i++) {
MsdkEncTask *task = &thiz->tasks[i];
if (task->output_bitstream.Data) {
_aligned_free (task->output_bitstream.Data);
}
}
}
g_free (thiz->tasks);
thiz->tasks = NULL;
/* Close VPP before freeing the surfaces. They are shared between encoder
* and VPP */
if (thiz->has_vpp) {
if (thiz->use_video_memory)
gst_msdk_frame_free (thiz->context, &thiz->vpp_alloc_resp);
status = MFXVideoVPP_Close (gst_msdk_context_get_session (thiz->context));
if (status != MFX_ERR_NONE && status != MFX_ERR_NOT_INITIALIZED) {
GST_WARNING_OBJECT (thiz, "VPP close failed (%s)",
msdk_status_to_string (status));
}
}
memset (&thiz->param, 0, sizeof (thiz->param));
thiz->num_extra_params = 0;
thiz->initialized = FALSE;
}
typedef struct
{
GstVideoCodecFrame *frame;
MsdkSurface *frame_surface;
MsdkSurface *converted_surface;
} FrameData;
static FrameData *
gst_msdkenc_queue_frame (GstMsdkEnc * thiz, GstVideoCodecFrame * frame,
GstVideoInfo * info)
{
FrameData *fdata;
fdata = g_slice_new (FrameData);
fdata->frame = gst_video_codec_frame_ref (frame);
thiz->pending_frames = g_list_prepend (thiz->pending_frames, fdata);
return fdata;
}
static MsdkSurface *
gst_msdkenc_create_surface (mfxFrameSurface1 * surface, GstBuffer * buf)
{
MsdkSurface *msdk_surface;
msdk_surface = g_slice_new0 (MsdkSurface);
msdk_surface->surface = surface;
msdk_surface->buf = buf;
return msdk_surface;
}
static void
gst_msdkenc_free_surface (MsdkSurface * surface)
{
if (surface->buf)
gst_buffer_unref (surface->buf);
g_slice_free (MsdkSurface, surface);
}
static void
gst_msdkenc_free_frame_data (GstMsdkEnc * thiz, FrameData * fdata)
{
if (fdata->frame_surface)
gst_msdkenc_free_surface (fdata->frame_surface);
if (thiz->has_vpp)
gst_msdkenc_free_surface (fdata->converted_surface);
gst_video_codec_frame_unref (fdata->frame);
g_slice_free (FrameData, fdata);
}
static void
gst_msdkenc_dequeue_frame (GstMsdkEnc * thiz, GstVideoCodecFrame * frame)
{
GList *l;
for (l = thiz->pending_frames; l; l = l->next) {
FrameData *fdata = l->data;
if (fdata->frame != frame)
continue;
gst_msdkenc_free_frame_data (thiz, fdata);
thiz->pending_frames = g_list_delete_link (thiz->pending_frames, l);
return;
}
}
static void
gst_msdkenc_dequeue_all_frames (GstMsdkEnc * thiz)
{
GList *l;
for (l = thiz->pending_frames; l; l = l->next) {
FrameData *fdata = l->data;
gst_msdkenc_free_frame_data (thiz, fdata);
}
g_list_free (thiz->pending_frames);
thiz->pending_frames = NULL;
}
static MsdkEncTask *
gst_msdkenc_get_free_task (GstMsdkEnc * thiz)
{
MsdkEncTask *tasks = thiz->tasks;
guint size = thiz->num_tasks;
guint start = thiz->next_task;
guint i;
if (tasks) {
for (i = 0; i < size; i++) {
guint t = (start + i) % size;
if (tasks[t].sync_point == NULL)
return &tasks[t];
}
}
return NULL;
}
static void
gst_msdkenc_reset_task (MsdkEncTask * task)
{
task->input_frame = NULL;
task->output_bitstream.DataLength = 0;
task->sync_point = NULL;
task->more_data = FALSE;
}
static GstFlowReturn
gst_msdkenc_finish_frame (GstMsdkEnc * thiz, MsdkEncTask * task,
gboolean discard)
{
GstVideoCodecFrame *frame = task->input_frame;
if (task->more_data) {
GstVideoCodecFrame *frame;
frame =
gst_video_encoder_get_frame (GST_VIDEO_ENCODER_CAST (thiz),
task->pending_frame_number);
if (frame) {
gst_msdkenc_dequeue_frame (thiz, frame);
gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), frame);
gst_msdkenc_reset_task (task);
return GST_FLOW_OK;
} else {
GST_ERROR_OBJECT (thiz,
"Couldn't find the pending frame %d to be finished",
task->pending_frame_number);
return GST_FLOW_ERROR;
}
}
if (!task->sync_point) {
return GST_FLOW_OK;
}
/* Wait for encoding operation to complete */
MFXVideoCORE_SyncOperation (gst_msdk_context_get_session (thiz->context),
task->sync_point, 10000);
if (!discard && task->output_bitstream.DataLength) {
GstBuffer *out_buf = NULL;
guint8 *data =
task->output_bitstream.Data + task->output_bitstream.DataOffset;
gsize size = task->output_bitstream.DataLength;
out_buf = gst_buffer_new_allocate (NULL, size, NULL);
gst_buffer_fill (out_buf, 0, data, size);
frame->output_buffer = out_buf;
frame->pts =
gst_util_uint64_scale (task->output_bitstream.TimeStamp, GST_SECOND,
90000);
frame->dts =
gst_util_uint64_scale (task->output_bitstream.DecodeTimeStamp,
GST_SECOND, 90000);
if ((task->output_bitstream.FrameType & MFX_FRAMETYPE_IDR) != 0 ||
(task->output_bitstream.FrameType & MFX_FRAMETYPE_xIDR) != 0) {
GST_VIDEO_CODEC_FRAME_SET_SYNC_POINT (frame);
}
/* Mark task as available */
gst_msdkenc_reset_task (task);
}
gst_msdkenc_dequeue_frame (thiz, frame);
return gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), frame);
}
static GstFlowReturn
gst_msdkenc_encode_frame (GstMsdkEnc * thiz, mfxFrameSurface1 * surface,
GstVideoCodecFrame * input_frame)
{
mfxSession session;
MsdkEncTask *task;
mfxStatus status;
if (G_UNLIKELY (thiz->context == NULL)) {
gst_msdkenc_dequeue_frame (thiz, input_frame);
gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), input_frame);
return GST_FLOW_NOT_NEGOTIATED;
}
session = gst_msdk_context_get_session (thiz->context);
task = gst_msdkenc_get_free_task (thiz);
for (;;) {
status = MFXVideoENCODE_EncodeFrameAsync (session, NULL, surface,
&task->output_bitstream, &task->sync_point);
if (status != MFX_WRN_DEVICE_BUSY)
break;
/* If device is busy, wait 1ms and retry, as per MSDK's recomendation */
g_usleep (1000);
};
if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA) {
GST_ELEMENT_ERROR (thiz, STREAM, ENCODE, ("Encode frame failed."),
("MSDK encode error (%s)", msdk_status_to_string (status)));
gst_msdkenc_dequeue_frame (thiz, input_frame);
gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), input_frame);
return GST_FLOW_ERROR;
}
if (task->sync_point) {
task->input_frame = input_frame;
thiz->next_task = ((task - thiz->tasks) + 1) % thiz->num_tasks;
} else if (status == MFX_ERR_MORE_DATA) {
task->more_data = TRUE;
task->pending_frame_number = input_frame->system_frame_number;
gst_video_codec_frame_unref (input_frame);
thiz->next_task = ((task - thiz->tasks) + 1) % thiz->num_tasks;
}
/* Ensure that next task is available */
task = thiz->tasks + thiz->next_task;
return gst_msdkenc_finish_frame (thiz, task, FALSE);
}
static guint
gst_msdkenc_maximum_delayed_frames (GstMsdkEnc * thiz)
{
return thiz->num_tasks;
}
static void
gst_msdkenc_set_latency (GstMsdkEnc * thiz)
{
GstVideoInfo *info = &thiz->input_state->info;
gint max_delayed_frames;
GstClockTime latency;
max_delayed_frames = gst_msdkenc_maximum_delayed_frames (thiz);
if (info->fps_n) {
latency = gst_util_uint64_scale_ceil (GST_SECOND * info->fps_d,
max_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,
max_delayed_frames, 25);
}
GST_INFO_OBJECT (thiz,
"Updating latency to %" GST_TIME_FORMAT " (%d frames)",
GST_TIME_ARGS (latency), max_delayed_frames);
gst_video_encoder_set_latency (GST_VIDEO_ENCODER (thiz), latency, latency);
}
static void
gst_msdkenc_flush_frames (GstMsdkEnc * thiz, gboolean discard)
{
guint i, t = thiz->next_task;
if (!thiz->tasks)
return;
for (i = 0; i < thiz->num_tasks; i++) {
gst_msdkenc_finish_frame (thiz, &thiz->tasks[t], discard);
t = (t + 1) % thiz->num_tasks;
}
}
static gboolean
gst_msdkenc_set_src_caps (GstMsdkEnc * thiz)
{
GstMsdkEncClass *klass = GST_MSDKENC_GET_CLASS (thiz);
GstCaps *outcaps = NULL;
GstVideoCodecState *state;
GstTagList *tags;
if (klass->set_src_caps)
outcaps = klass->set_src_caps (thiz);
if (!outcaps)
return FALSE;
state = gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (thiz),
outcaps, thiz->input_state);
GST_DEBUG_OBJECT (thiz, "output caps: %" GST_PTR_FORMAT, state->caps);
gst_video_codec_state_unref (state);
tags = gst_tag_list_new_empty ();
gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_ENCODER, "msdkenc",
GST_TAG_MAXIMUM_BITRATE, thiz->bitrate * 1024,
GST_TAG_NOMINAL_BITRATE, thiz->bitrate * 1024, NULL);
gst_video_encoder_merge_tags (GST_VIDEO_ENCODER (thiz), tags,
GST_TAG_MERGE_REPLACE);
gst_tag_list_unref (tags);
return TRUE;
}
static GstBufferPool *
gst_msdkenc_create_buffer_pool (GstMsdkEnc * thiz, GstCaps * caps,
guint num_buffers, gboolean set_align)
{
GstBufferPool *pool = NULL;
GstStructure *config;
GstAllocator *allocator = NULL;
GstVideoInfo info;
GstVideoAlignment align;
GstAllocationParams params = { 0, 31, 0, 0, };
mfxFrameAllocResponse *alloc_resp = NULL;
if (thiz->has_vpp)
alloc_resp = set_align ? &thiz->vpp_alloc_resp : &thiz->alloc_resp;
else
alloc_resp = &thiz->alloc_resp;
pool = gst_msdk_buffer_pool_new (thiz->context, alloc_resp);
if (!pool)
goto error_no_pool;
if (!gst_video_info_from_caps (&info, caps)) {
GST_INFO_OBJECT (thiz, "failed to get video info");
return FALSE;
}
gst_msdk_set_video_alignment (&info, &align);
gst_video_info_align (&info, &align);
if (thiz->use_video_memory)
allocator = gst_msdk_video_allocator_new (thiz->context, &info, alloc_resp);
else
allocator = gst_msdk_system_allocator_new (&info);
if (!allocator)
goto error_no_allocator;
config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool));
gst_buffer_pool_config_set_params (config, caps, info.size, 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);
if (thiz->use_video_memory)
gst_buffer_pool_config_add_option (config,
GST_BUFFER_POOL_OPTION_MSDK_USE_VIDEO_MEMORY);
gst_buffer_pool_config_set_video_alignment (config, &align);
gst_buffer_pool_config_set_allocator (config, allocator, &params);
gst_object_unref (allocator);
if (!gst_buffer_pool_set_config (pool, config))
goto error_pool_config;
if (set_align)
thiz->aligned_info = info;
return pool;
error_no_pool:
{
GST_INFO_OBJECT (thiz, "failed to create bufferpool");
return FALSE;
}
error_no_allocator:
{
GST_INFO_OBJECT (thiz, "failed to create allocator");
return FALSE;
}
error_pool_config:
{
GST_INFO_OBJECT (thiz, "failed to set config");
return FALSE;
}
}
static gboolean
gst_msdkenc_set_format (GstVideoEncoder * encoder, GstVideoCodecState * state)
{
GstMsdkEnc *thiz = GST_MSDKENC (encoder);
GstMsdkEncClass *klass = GST_MSDKENC_GET_CLASS (thiz);
if (state) {
if (thiz->input_state)
gst_video_codec_state_unref (thiz->input_state);
thiz->input_state = gst_video_codec_state_ref (state);
}
/* TODO: Currently d3d allocator is not implemented.
* So encoder uses system memory by default on Windows.
*/
#ifndef _WIN32
thiz->use_video_memory = TRUE;
#else
thiz->use_video_memory = FALSE;
#endif
GST_INFO_OBJECT (encoder, "This MSDK encoder uses %s memory",
thiz->use_video_memory ? "video" : "system");
if (klass->set_format) {
if (!klass->set_format (thiz))
return FALSE;
}
if (!gst_msdkenc_init_encoder (thiz))
return FALSE;
if (!gst_msdkenc_set_src_caps (thiz)) {
gst_msdkenc_close_encoder (thiz);
return FALSE;
}
if (!thiz->msdk_pool) {
guint num_buffers = gst_msdkenc_maximum_delayed_frames (thiz) + 1;
thiz->msdk_pool =
gst_msdkenc_create_buffer_pool (thiz, thiz->input_state->caps,
num_buffers, TRUE);
}
gst_msdkenc_set_latency (thiz);
/* Create another bufferpool if VPP requires */
if (thiz->has_vpp) {
GstVideoInfo *info = &thiz->input_state->info;
GstVideoInfo nv12_info;
GstCaps *caps;
GstBufferPool *pool = NULL;
gst_video_info_init (&nv12_info);
gst_video_info_set_format (&nv12_info, GST_VIDEO_FORMAT_NV12, info->width,
info->height);
caps = gst_video_info_to_caps (&nv12_info);
pool =
gst_msdkenc_create_buffer_pool (thiz, caps, thiz->num_surfaces, FALSE);
thiz->msdk_converted_pool = pool;
gst_caps_unref (caps);
}
return TRUE;
}
static MsdkSurface *
gst_msdkenc_get_surface_from_pool (GstMsdkEnc * thiz, GstBufferPool * pool,
GstBufferPoolAcquireParams * params)
{
GstBuffer *new_buffer;
mfxFrameSurface1 *new_surface;
MsdkSurface *msdk_surface;
if (!gst_buffer_pool_is_active (pool) &&
!gst_buffer_pool_set_active (pool, TRUE)) {
GST_ERROR_OBJECT (pool, "failed to activate buffer pool");
return NULL;
}
if (gst_buffer_pool_acquire_buffer (pool, &new_buffer, params) != GST_FLOW_OK) {
GST_ERROR_OBJECT (pool, "failed to acquire a buffer from pool");
return NULL;
}
if (gst_msdk_is_msdk_buffer (new_buffer))
new_surface = gst_msdk_get_surface_from_buffer (new_buffer);
else {
GST_ERROR_OBJECT (pool, "the acquired memory is not MSDK memory");
return NULL;
}
msdk_surface = gst_msdkenc_create_surface (new_surface, new_buffer);
return msdk_surface;
}
static MsdkSurface *
gst_msdkenc_get_surface_from_frame (GstMsdkEnc * thiz,
GstVideoCodecFrame * frame)
{
GstVideoFrame src_frame, out_frame;
MsdkSurface *msdk_surface;
GstBuffer *inbuf;
inbuf = frame->input_buffer;
if (gst_msdk_is_msdk_buffer (inbuf)) {
msdk_surface = g_slice_new0 (MsdkSurface);
msdk_surface->surface = gst_msdk_get_surface_from_buffer (inbuf);
return msdk_surface;
}
/* If upstream hasn't accpeted the proposed msdk bufferpool,
* just copy frame to msdk buffer and take a surface from it.
*/
if (!(msdk_surface =
gst_msdkenc_get_surface_from_pool (thiz, thiz->msdk_pool, NULL)))
goto error;
if (!gst_video_frame_map (&src_frame, &thiz->input_state->info, inbuf,
GST_MAP_READ)) {
GST_ERROR_OBJECT (thiz, "failed to map the frame for source");
goto error;
}
if (!gst_video_frame_map (&out_frame, &thiz->aligned_info, msdk_surface->buf,
GST_MAP_WRITE)) {
GST_ERROR_OBJECT (thiz, "failed to map the frame for destination");
gst_video_frame_unmap (&src_frame);
goto error;
}
if (!gst_video_frame_copy (&out_frame, &src_frame)) {
GST_ERROR_OBJECT (thiz, "failed to copy frame");
gst_video_frame_unmap (&out_frame);
gst_video_frame_unmap (&src_frame);
goto error;
}
gst_video_frame_unmap (&out_frame);
gst_video_frame_unmap (&src_frame);
gst_buffer_replace (&frame->input_buffer, msdk_surface->buf);
gst_buffer_unref (msdk_surface->buf);
msdk_surface->buf = NULL;
return msdk_surface;
error:
if (msdk_surface) {
if (msdk_surface->buf)
gst_buffer_unref (msdk_surface->buf);
g_slice_free (MsdkSurface, msdk_surface);
}
return NULL;
}
static GstFlowReturn
gst_msdkenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame)
{
GstMsdkEnc *thiz = GST_MSDKENC (encoder);
GstVideoInfo *info = &thiz->input_state->info;
FrameData *fdata;
MsdkSurface *surface;
if (thiz->reconfig) {
gst_msdkenc_flush_frames (thiz, FALSE);
gst_msdkenc_set_format (encoder, NULL);
}
if (G_UNLIKELY (thiz->context == NULL))
goto not_inited;
if (thiz->has_vpp) {
MsdkSurface *vpp_surface;
GstVideoFrame vframe;
mfxSession session;
mfxSyncPoint vpp_sync_point = NULL;
mfxStatus status;
vpp_surface = gst_msdkenc_get_surface_from_frame (thiz, frame);
if (!vpp_surface)
goto invalid_surface;
surface =
gst_msdkenc_get_surface_from_pool (thiz, thiz->msdk_converted_pool,
NULL);
if (!surface)
goto invalid_surface;
if (!gst_video_frame_map (&vframe, info, frame->input_buffer, GST_MAP_READ))
goto invalid_frame;
if (frame->pts != GST_CLOCK_TIME_NONE) {
vpp_surface->surface->Data.TimeStamp =
gst_util_uint64_scale (frame->pts, 90000, GST_SECOND);
surface->surface->Data.TimeStamp =
gst_util_uint64_scale (frame->pts, 90000, GST_SECOND);
} else {
vpp_surface->surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN;
surface->surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN;
}
session = gst_msdk_context_get_session (thiz->context);
for (;;) {
status =
MFXVideoVPP_RunFrameVPPAsync (session, vpp_surface->surface,
surface->surface, NULL, &vpp_sync_point);
if (status != MFX_WRN_DEVICE_BUSY)
break;
/* If device is busy, wait 1ms and retry, as per MSDK's recomendation */
g_usleep (1000);
};
gst_video_frame_unmap (&vframe);
if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA) {
GST_ELEMENT_ERROR (thiz, STREAM, ENCODE, ("Converting frame failed."),
("MSDK VPP error (%s)", msdk_status_to_string (status)));
gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), frame);
return GST_FLOW_ERROR;
}
fdata = g_slice_new0 (FrameData);
fdata->frame = gst_video_codec_frame_ref (frame);
fdata->frame_surface = vpp_surface;
fdata->converted_surface = surface;
thiz->pending_frames = g_list_prepend (thiz->pending_frames, fdata);
} else {
surface = gst_msdkenc_get_surface_from_frame (thiz, frame);
if (!surface)
goto invalid_surface;
fdata = gst_msdkenc_queue_frame (thiz, frame, info);
if (!fdata)
goto invalid_frame;
fdata->frame_surface = surface;
if (frame->pts != GST_CLOCK_TIME_NONE) {
surface->surface->Data.TimeStamp =
gst_util_uint64_scale (frame->pts, 90000, GST_SECOND);
} else {
surface->surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN;
}
}
return gst_msdkenc_encode_frame (thiz, surface->surface, frame);
/* ERRORS */
not_inited:
{
GST_WARNING_OBJECT (encoder, "Got buffer before set_caps was called");
return GST_FLOW_NOT_NEGOTIATED;
}
invalid_surface:
{
GST_ERROR_OBJECT (encoder, "Surface pool is full");
return GST_FLOW_ERROR;
}
invalid_frame:
{
GST_WARNING_OBJECT (encoder, "Failed to map frame");
return GST_FLOW_OK;
}
}
static gboolean
gst_msdkenc_start (GstVideoEncoder * encoder)
{
GstMsdkEnc *thiz = GST_MSDKENC (encoder);
if (gst_msdk_context_prepare (GST_ELEMENT_CAST (thiz), &thiz->context)) {
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_ENCODER) {
GstMsdkContext *parent_context;
parent_context = thiz->context;
thiz->context = gst_msdk_context_new_with_parent (parent_context);
gst_object_unref (parent_context);
GST_INFO_OBJECT (thiz,
"Creating new context %" GST_PTR_FORMAT " with joined session",
thiz->context);
} else {
gst_msdk_context_add_job_type (thiz->context, GST_MSDK_JOB_ENCODER);
}
} else {
gst_msdk_context_ensure_context (GST_ELEMENT_CAST (thiz), thiz->hardware,
GST_MSDK_JOB_ENCODER);
GST_INFO_OBJECT (thiz, "Creating new context %" GST_PTR_FORMAT,
thiz->context);
}
gst_msdk_context_add_shared_async_depth (thiz->context, thiz->async_depth);
/* Set the minimum pts to some huge value (1000 hours). This keeps
the dts at the start of the stream from needing to be
negative. */
gst_video_encoder_set_min_pts (encoder, GST_SECOND * 60 * 60 * 1000);
return TRUE;
}
static gboolean
gst_msdkenc_stop (GstVideoEncoder * encoder)
{
GstMsdkEnc *thiz = GST_MSDKENC (encoder);
gst_msdkenc_flush_frames (thiz, TRUE);
gst_msdkenc_close_encoder (thiz);
gst_msdkenc_dequeue_all_frames (thiz);
if (thiz->input_state)
gst_video_codec_state_unref (thiz->input_state);
thiz->input_state = NULL;
gst_object_replace ((GstObject **) & thiz->context, NULL);
return TRUE;
}
static gboolean
gst_msdkenc_flush (GstVideoEncoder * encoder)
{
GstMsdkEnc *thiz = GST_MSDKENC (encoder);
gst_msdkenc_flush_frames (thiz, TRUE);
gst_msdkenc_close_encoder (thiz);
gst_msdkenc_dequeue_all_frames (thiz);
gst_msdkenc_init_encoder (thiz);
return TRUE;
}
static GstFlowReturn
gst_msdkenc_finish (GstVideoEncoder * encoder)
{
GstMsdkEnc *thiz = GST_MSDKENC (encoder);
gst_msdkenc_flush_frames (thiz, FALSE);
return GST_FLOW_OK;
}
static gboolean
gst_msdkenc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
{
GstMsdkEnc *thiz = GST_MSDKENC (encoder);
GstVideoInfo info;
GstBufferPool *pool = NULL;
GstAllocator *allocator = NULL;
GstCaps *caps;
guint num_buffers;
if (!thiz->input_state)
return FALSE;
gst_query_parse_allocation (query, &caps, NULL);
if (!caps) {
GST_INFO_OBJECT (encoder, "failed to get caps");
return FALSE;
}
if (!gst_video_info_from_caps (&info, caps)) {
GST_INFO_OBJECT (encoder, "failed to get video info");
return FALSE;
}
num_buffers = gst_msdkenc_maximum_delayed_frames (thiz) + 1;
pool = gst_msdkenc_create_buffer_pool (thiz, caps, num_buffers, TRUE);
gst_query_add_allocation_pool (query, pool, GST_VIDEO_INFO_SIZE (&info),
num_buffers, 0);
gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
if (pool) {
GstStructure *config;
GstAllocationParams params = { 0, 31, 0, 0, };
config = gst_buffer_pool_get_config (GST_BUFFER_POOL_CAST (pool));
if (gst_buffer_pool_config_get_allocator (config, &allocator, NULL))
gst_query_add_allocation_param (query, allocator, &params);
gst_structure_free (config);
}
gst_object_unref (pool);
return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
query);
}
static void
gst_msdkenc_set_property (GObject * object, guint prop_id, const GValue * value,
GParamSpec * pspec)
{
GstMsdkEnc *thiz = GST_MSDKENC (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 PROP_HARDWARE:
thiz->hardware = g_value_get_boolean (value);
break;
case PROP_ASYNC_DEPTH:
thiz->async_depth = g_value_get_uint (value);
break;
case PROP_TARGET_USAGE:
thiz->target_usage = g_value_get_uint (value);
break;
case PROP_RATE_CONTROL:
thiz->rate_control = g_value_get_enum (value);
break;
case PROP_BITRATE:
thiz->bitrate = g_value_get_uint (value);
thiz->reconfig = TRUE;
break;
case PROP_QPI:
thiz->qpi = g_value_get_uint (value);
break;
case PROP_QPP:
thiz->qpp = g_value_get_uint (value);
break;
case PROP_QPB:
thiz->qpb = g_value_get_uint (value);
break;
case PROP_GOP_SIZE:
thiz->gop_size = g_value_get_uint (value);
break;
case PROP_REF_FRAMES:
thiz->ref_frames = g_value_get_uint (value);
break;
case PROP_I_FRAMES:
thiz->i_frames = g_value_get_uint (value);
break;
case PROP_B_FRAMES:
thiz->b_frames = 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_msdkenc_get_property (GObject * object, guint prop_id, GValue * value,
GParamSpec * pspec)
{
GstMsdkEnc *thiz = GST_MSDKENC (object);
GST_OBJECT_LOCK (thiz);
switch (prop_id) {
case PROP_HARDWARE:
g_value_set_boolean (value, thiz->hardware);
break;
case PROP_ASYNC_DEPTH:
g_value_set_uint (value, thiz->async_depth);
break;
case PROP_TARGET_USAGE:
g_value_set_uint (value, thiz->target_usage);
break;
case PROP_RATE_CONTROL:
g_value_set_enum (value, thiz->rate_control);
break;
case PROP_BITRATE:
g_value_set_uint (value, thiz->bitrate);
break;
case PROP_QPI:
g_value_set_uint (value, thiz->qpi);
break;
case PROP_QPP:
g_value_set_uint (value, thiz->qpp);
break;
case PROP_QPB:
g_value_set_uint (value, thiz->qpb);
break;
case PROP_GOP_SIZE:
g_value_set_uint (value, thiz->gop_size);
break;
case PROP_REF_FRAMES:
g_value_set_uint (value, thiz->ref_frames);
break;
case PROP_I_FRAMES:
g_value_set_uint (value, thiz->i_frames);
break;
case PROP_B_FRAMES:
g_value_set_uint (value, thiz->b_frames);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
GST_OBJECT_UNLOCK (thiz);
}
static void
gst_msdkenc_finalize (GObject * object)
{
GstMsdkEnc *thiz = GST_MSDKENC (object);
if (thiz->input_state)
gst_video_codec_state_unref (thiz->input_state);
thiz->input_state = NULL;
gst_object_replace ((GstObject **) & thiz->msdk_pool, NULL);
gst_object_replace ((GstObject **) & thiz->msdk_converted_pool, NULL);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static void
gst_msdkenc_class_init (GstMsdkEncClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *element_class;
GstVideoEncoderClass *gstencoder_class;
gobject_class = G_OBJECT_CLASS (klass);
element_class = GST_ELEMENT_CLASS (klass);
gstencoder_class = GST_VIDEO_ENCODER_CLASS (klass);
gobject_class->set_property = gst_msdkenc_set_property;
gobject_class->get_property = gst_msdkenc_get_property;
gobject_class->finalize = gst_msdkenc_finalize;
element_class->set_context = gst_msdkenc_set_context;
gstencoder_class->set_format = GST_DEBUG_FUNCPTR (gst_msdkenc_set_format);
gstencoder_class->handle_frame = GST_DEBUG_FUNCPTR (gst_msdkenc_handle_frame);
gstencoder_class->start = GST_DEBUG_FUNCPTR (gst_msdkenc_start);
gstencoder_class->stop = GST_DEBUG_FUNCPTR (gst_msdkenc_stop);
gstencoder_class->flush = GST_DEBUG_FUNCPTR (gst_msdkenc_flush);
gstencoder_class->finish = GST_DEBUG_FUNCPTR (gst_msdkenc_finish);
gstencoder_class->propose_allocation =
GST_DEBUG_FUNCPTR (gst_msdkenc_propose_allocation);
g_object_class_install_property (gobject_class, PROP_HARDWARE,
g_param_spec_boolean ("hardware", "Hardware", "Enable hardware encoders",
PROP_HARDWARE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, 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));
g_object_class_install_property (gobject_class, PROP_TARGET_USAGE,
g_param_spec_uint ("target-usage", "Target Usage",
"1: Best quality, 4: Balanced, 7: Best speed",
1, 7, PROP_TARGET_USAGE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_RATE_CONTROL,
g_param_spec_enum ("rate-control", "Rate Control",
"Rate control method", GST_MSDKENC_RATE_CONTROL_TYPE,
PROP_RATE_CONTROL_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_BITRATE,
g_param_spec_uint ("bitrate", "Bitrate", "Bitrate in kbit/sec", 1,
2000 * 1024, PROP_BITRATE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
GST_PARAM_MUTABLE_PLAYING));
g_object_class_install_property (gobject_class, PROP_QPI,
g_param_spec_uint ("qpi", "QPI",
"Constant quantizer for I frames (0 unlimited)",
0, 51, PROP_QPI_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_QPP,
g_param_spec_uint ("qpp", "QPP",
"Constant quantizer for P frames (0 unlimited)",
0, 51, PROP_QPP_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_QPB,
g_param_spec_uint ("qpb", "QPB",
"Constant quantizer for B frames (0 unlimited)",
0, 51, PROP_QPB_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_GOP_SIZE,
g_param_spec_uint ("gop-size", "GOP Size", "GOP Size", 0,
G_MAXINT, PROP_GOP_SIZE_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_REF_FRAMES,
g_param_spec_uint ("ref-frames", "Reference Frames",
"Number of reference frames",
0, G_MAXINT, PROP_REF_FRAMES_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_I_FRAMES,
g_param_spec_uint ("i-frames", "I Frames",
"Number of I frames between IDR frames",
0, G_MAXINT, PROP_I_FRAMES_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (gobject_class, PROP_B_FRAMES,
g_param_spec_uint ("b-frames", "B Frames",
"Number of B frames between I and P frames",
0, G_MAXINT, PROP_B_FRAMES_DEFAULT,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
gst_element_class_add_static_pad_template (element_class, &sink_factory);
}
static void
gst_msdkenc_init (GstMsdkEnc * thiz)
{
thiz->hardware = PROP_HARDWARE_DEFAULT;
thiz->async_depth = PROP_ASYNC_DEPTH_DEFAULT;
thiz->target_usage = PROP_TARGET_USAGE_DEFAULT;
thiz->rate_control = PROP_RATE_CONTROL_DEFAULT;
thiz->bitrate = PROP_BITRATE_DEFAULT;
thiz->qpi = PROP_QPI_DEFAULT;
thiz->qpp = PROP_QPP_DEFAULT;
thiz->qpb = PROP_QPB_DEFAULT;
thiz->gop_size = PROP_GOP_SIZE_DEFAULT;
thiz->ref_frames = PROP_REF_FRAMES_DEFAULT;
thiz->i_frames = PROP_I_FRAMES_DEFAULT;
thiz->b_frames = PROP_B_FRAMES_DEFAULT;
}