diff --git a/sys/msdk/README b/sys/msdk/README new file mode 100644 index 0000000000..75fa832711 --- /dev/null +++ b/sys/msdk/README @@ -0,0 +1,34 @@ + +# gst-msdk + +gst-msdk is a plugin for +[Intel Media SDK](https://software.intel.com/en-us/media-sdk), a +cross-platform API for developing media applications. The plugin has +multiple elements for video hardware encoding leveraging latest Intel +processors through Intel Media SDK. + +- MPEG2 encoding (*msdkmpeg2enc*) + +- H.264 encoding (*msdkh264enc*) + +- H.265 encoding (*msdkh265enc*) + +- VP8 encoding (*msdkvp8enc*) + + +It requires: + +- Intel Media SDK + + +# Giving it a try + +Encoding a simple video test source and saving it to a file. + + $ gst-launch-1.0 videotestsrc ! msdkh264enc ! filesink location=test.h264 + + +# License + +gst-mdk is freely available for download under the terms of the +[BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html). diff --git a/sys/msdk/gstmsdk.c b/sys/msdk/gstmsdk.c new file mode 100644 index 0000000000..e5ccfe5624 --- /dev/null +++ b/sys/msdk/gstmsdk.c @@ -0,0 +1,86 @@ +/* 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. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "gstmsdkh264enc.h" +#include "gstmsdkh265enc.h" +#include "gstmsdkmpeg2enc.h" +#include "gstmsdkvp8enc.h" + +GST_DEBUG_CATEGORY (gst_msdkenc_debug); +GST_DEBUG_CATEGORY (gst_msdkh264enc_debug); +GST_DEBUG_CATEGORY (gst_msdkh265enc_debug); +GST_DEBUG_CATEGORY (gst_msdkmpeg2enc_debug); +GST_DEBUG_CATEGORY (gst_msdkvp8enc_debug); + +static gboolean +plugin_init (GstPlugin * plugin) +{ + gboolean ret; + + GST_DEBUG_CATEGORY_INIT (gst_msdkenc_debug, "msdkenc", 0, "msdkenc"); + GST_DEBUG_CATEGORY_INIT (gst_msdkh264enc_debug, "msdkh264enc", 0, + "msdkh264enc"); + GST_DEBUG_CATEGORY_INIT (gst_msdkh265enc_debug, "msdkh265enc", 0, + "msdkh265enc"); + GST_DEBUG_CATEGORY_INIT (gst_msdkmpeg2enc_debug, "msdkmpeg2enc", 0, + "msdkmpeg2enc"); + GST_DEBUG_CATEGORY_INIT (gst_msdkvp8enc_debug, "msdkvp8enc", 0, "msdkvp8enc"); + + + if (!msdk_is_available ()) + return FALSE; + + ret = gst_element_register (plugin, "msdkh264enc", GST_RANK_NONE, + GST_TYPE_MSDKH264ENC); + + ret = gst_element_register (plugin, "msdkh265enc", GST_RANK_NONE, + GST_TYPE_MSDKH265ENC); + + ret = gst_element_register (plugin, "msdkmpeg2enc", GST_RANK_NONE, + GST_TYPE_MSDKMPEG2ENC); + + ret = gst_element_register (plugin, "msdkvp8enc", GST_RANK_NONE, + GST_TYPE_MSDKVP8ENC); + + return ret; +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + msdk, + "Intel Media SDK encoders", + plugin_init, VERSION, "BSD", "Oblong", "http://oblong.com/") diff --git a/sys/msdk/gstmsdkenc.c b/sys/msdk/gstmsdkenc.c new file mode 100644 index 0000000000..fb92ad01ac --- /dev/null +++ b/sys/msdk/gstmsdkenc.c @@ -0,0 +1,971 @@ +/* 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 +#endif + +#include + +#include "gstmsdkenc.h" + +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 }, " + "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); + +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 gboolean +gst_msdkenc_init_encoder (GstMsdkEnc * thiz) +{ + GstMsdkEncClass *klass = GST_MSDKENC_GET_CLASS (thiz); + GstVideoInfo *info; + mfxSession session; + mfxStatus status; + mfxFrameAllocRequest request; + guint i; + + if (!thiz->input_state) { + GST_DEBUG_OBJECT (thiz, "Have no input state yet"); + return FALSE; + } + info = &thiz->input_state->info; + + /* make sure that the encoder is closed */ + gst_msdkenc_close_encoder (thiz); + + thiz->context = msdk_open_context (thiz->hardware); + if (!thiz->context) { + GST_ERROR_OBJECT (thiz, "Context creation failed"); + return FALSE; + } + + GST_OBJECT_LOCK (thiz); + + thiz->param.AsyncDepth = thiz->async_depth; + 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_16 (info->width); + thiz->param.mfx.FrameInfo.Height = GST_ROUND_UP_16 (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; + thiz->param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; + + /* 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; + } + + session = msdk_context_get_session (thiz->context); + /* 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, "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; + } + + thiz->num_surfaces = request.NumFrameSuggested; + thiz->surfaces = g_new0 (mfxFrameSurface1, thiz->num_surfaces); + for (i = 0; i < thiz->num_surfaces; i++) { + memcpy (&thiz->surfaces[i].Info, &thiz->param.mfx.FrameInfo, + sizeof (mfxFrameInfo)); + } + if (GST_ROUND_UP_32 (info->width) != info->width + || GST_ROUND_UP_2 (info->height) != info->height) { + guint width = GST_ROUND_UP_32 (info->width); + guint height = GST_ROUND_UP_2 (info->height); + gsize Y_size = width * height; + gsize size = Y_size + (Y_size >> 1); + for (i = 0; i < thiz->num_surfaces; i++) { + mfxFrameSurface1 *surface = &thiz->surfaces[i]; + mfxU8 *data; + if (posix_memalign ((void **) &data, 32, size) != 0) { + GST_ERROR_OBJECT (thiz, "Memory allocation failed"); + goto failed; + } + + surface->Data.MemId = (mfxMemId) data; + surface->Data.Pitch = width; + surface->Data.Y = data; + surface->Data.UV = data + Y_size; + } + + GST_DEBUG_OBJECT (thiz, + "Allocated aligned memory, pixel data will be copied"); + } + + GST_DEBUG_OBJECT (thiz, "Required %d surfaces (%d suggested), allocated %d", + request.NumFrameMin, request.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++) { + if (posix_memalign ((void **) &thiz->tasks[i].output_bitstream.Data, 32, + thiz->param.mfx.BufferSizeInKB * 1024) != 0) { + 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; + + GST_OBJECT_UNLOCK (thiz); + + return TRUE; + +failed: + GST_OBJECT_UNLOCK (thiz); + msdk_close_context (thiz->context); + thiz->context = NULL; + return FALSE; +} + +static void +gst_msdkenc_close_encoder (GstMsdkEnc * thiz) +{ + guint i; + mfxStatus status; + + if (!thiz->context) + return; + + GST_DEBUG_OBJECT (thiz, "Closing encoder 0x%p", thiz->context); + + status = MFXVideoENCODE_Close (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) { + free (task->output_bitstream.Data); + } + } + } + g_free (thiz->tasks); + thiz->tasks = NULL; + + for (i = 0; i < thiz->num_surfaces; i++) { + mfxFrameSurface1 *surface = &thiz->surfaces[i]; + if (surface->Data.MemId) + free (surface->Data.MemId); + } + g_free (thiz->surfaces); + thiz->surfaces = NULL; + + msdk_close_context (thiz->context); + thiz->context = NULL; + memset (&thiz->param, 0, sizeof (thiz->param)); + thiz->num_extra_params = 0; +} + +typedef struct +{ + GstVideoCodecFrame *frame; + GstVideoFrame vframe; +} FrameData; + +static FrameData * +gst_msdkenc_queue_frame (GstMsdkEnc * thiz, GstVideoCodecFrame * frame, + GstVideoInfo * info) +{ + GstVideoFrame vframe; + FrameData *fdata; + + if (!gst_video_frame_map (&vframe, info, frame->input_buffer, GST_MAP_READ)) + return NULL; + + fdata = g_slice_new (FrameData); + fdata->frame = gst_video_codec_frame_ref (frame); + fdata->vframe = vframe; + + thiz->pending_frames = g_list_prepend (thiz->pending_frames, fdata); + + return 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_video_frame_unmap (&fdata->vframe); + gst_video_codec_frame_unref (fdata->frame); + g_slice_free (FrameData, 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_video_frame_unmap (&fdata->vframe); + gst_video_codec_frame_unref (fdata->frame); + g_slice_free (FrameData, 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; +} + +static GstFlowReturn +gst_msdkenc_finish_frame (GstMsdkEnc * thiz, MsdkEncTask * task, + gboolean discard) +{ + GstVideoCodecFrame *frame = task->input_frame; + + if (!task->sync_point) { + return GST_FLOW_OK; + } + + /* Wait for encoding operation to complete */ + MFXVideoCORE_SyncOperation (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; + 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 = 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 (task->sync_point) { + task->input_frame = input_frame; + thiz->next_task = ((task - thiz->tasks) + 1) % thiz->num_tasks; + } + + if (status != MFX_ERR_NONE && status != MFX_ERR_MORE_DATA) { + GST_ELEMENT_ERROR (thiz, STREAM, ENCODE, ("Encode frame failed."), + ("MSDK encode return code=%d", status)); + gst_msdkenc_dequeue_frame (thiz, input_frame); + gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (thiz), input_frame); + return GST_FLOW_ERROR; + } + + /* 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 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); + } + + 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; + } + + gst_msdkenc_set_latency (thiz); + + return TRUE; +} + +static GstFlowReturn +gst_msdkenc_handle_frame (GstVideoEncoder * encoder, GstVideoCodecFrame * frame) +{ + GstMsdkEnc *thiz = GST_MSDKENC (encoder); + GstVideoInfo *info = &thiz->input_state->info; + FrameData *fdata; + mfxFrameSurface1 *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; + + surface = msdk_get_free_surface (thiz->surfaces, thiz->num_surfaces); + if (!surface) + goto invalid_surface; + + fdata = gst_msdkenc_queue_frame (thiz, frame, info); + if (!fdata) + goto invalid_frame; + + msdk_frame_to_surface (&fdata->vframe, surface); + if (frame->pts != GST_CLOCK_TIME_NONE) { + surface->Data.TimeStamp = + gst_util_uint64_scale (frame->pts, 90000, GST_SECOND); + } else { + surface->Data.TimeStamp = MFX_TIMESTAMP_UNKNOWN; + } + + return gst_msdkenc_encode_frame (thiz, 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) +{ + 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; + + 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; + guint num_buffers; + + gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL); + + if (!thiz->input_state) + return FALSE; + + info = &thiz->input_state->info; + num_buffers = gst_msdkenc_maximum_delayed_frames (thiz) + 1; + + gst_query_add_allocation_pool (query, NULL, info->size, num_buffers, 0); + + 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; + + 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; + + 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; +} diff --git a/sys/msdk/gstmsdkenc.h b/sys/msdk/gstmsdkenc.h new file mode 100644 index 0000000000..1e316ec535 --- /dev/null +++ b/sys/msdk/gstmsdkenc.h @@ -0,0 +1,123 @@ +/* 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. + */ + +#ifndef __GST_MSDKENC_H__ +#define __GST_MSDKENC_H__ + +#include +#include +#include "msdk.h" + +G_BEGIN_DECLS + +#define GST_TYPE_MSDKENC \ + (gst_msdkenc_get_type()) +#define GST_MSDKENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MSDKENC,GstMsdkEnc)) +#define GST_MSDKENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MSDKENC,GstMsdkEncClass)) +#define GST_MSDKENC_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_MSDKENC,GstMsdkEncClass)) +#define GST_IS_MSDKENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MSDKENC)) +#define GST_IS_MSDKENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MSDKENC)) + +#define MAX_EXTRA_PARAMS 8 + +typedef struct _GstMsdkEnc GstMsdkEnc; +typedef struct _GstMsdkEncClass GstMsdkEncClass; +typedef struct _MsdkEncTask MsdkEncTask; + +struct _GstMsdkEnc +{ + GstVideoEncoder element; + + /* input description */ + GstVideoCodecState *input_state; + + /* List of frame/buffer mapping structs for + * pending frames */ + GList *pending_frames; + + /* MFX context */ + MsdkContext *context; + mfxVideoParam param; + guint num_surfaces; + mfxFrameSurface1 *surfaces; + guint num_tasks; + MsdkEncTask *tasks; + guint next_task; + + mfxExtBuffer *extra_params[MAX_EXTRA_PARAMS]; + guint num_extra_params; + + /* element properties */ + gboolean hardware; + + guint async_depth; + guint target_usage; + guint rate_control; + guint bitrate; + guint qpi; + guint qpp; + guint qpb; + guint gop_size; + guint ref_frames; + guint i_frames; + guint b_frames; + + gboolean reconfig; +}; + +struct _GstMsdkEncClass +{ + GstVideoEncoderClass parent_class; + + gboolean (*set_format) (GstMsdkEnc * encoder); + gboolean (*configure) (GstMsdkEnc * encoder); + GstCaps *(*set_src_caps) (GstMsdkEnc * encoder); +}; + +struct _MsdkEncTask +{ + GstVideoCodecFrame *input_frame; + mfxSyncPoint sync_point; + mfxBitstream output_bitstream; +}; + +GType gst_msdkenc_get_type (void); + +void gst_msdkenc_add_extra_param (GstMsdkEnc * thiz, mfxExtBuffer * param); + +G_END_DECLS + +#endif /* __GST_MSDKENC_H__ */ diff --git a/sys/msdk/gstmsdkh264enc.c b/sys/msdk/gstmsdkh264enc.c new file mode 100644 index 0000000000..fd2aa00084 --- /dev/null +++ b/sys/msdk/gstmsdkh264enc.c @@ -0,0 +1,341 @@ +/* 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. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "gstmsdkh264enc.h" + +#include + +GST_DEBUG_CATEGORY_EXTERN (gst_msdkh264enc_debug); +#define GST_CAT_DEFAULT gst_msdkh264enc_debug + +enum +{ + PROP_0, + PROP_CABAC, + PROP_LOW_POWER +}; + +#define PROP_CABAC_DEFAULT TRUE +#define PROP_LOWPOWER_DEFAULT FALSE + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h264, " + "framerate = (fraction) [0/1, MAX], " + "width = (int) [ 1, MAX ], height = (int) [ 1, MAX ], " + "stream-format = (string) byte-stream , alignment = (string) au , " + "profile = (string) { high, main, baseline, constrained-baseline }") + ); + +#define gst_msdkh264enc_parent_class parent_class +G_DEFINE_TYPE (GstMsdkH264Enc, gst_msdkh264enc, GST_TYPE_MSDKENC); + +static gboolean +gst_msdkh264enc_set_format (GstMsdkEnc * encoder) +{ + GstMsdkH264Enc *thiz = GST_MSDKH264ENC (encoder); + GstCaps *template_caps; + GstCaps *allowed_caps = NULL; + + thiz->profile = 0; + thiz->level = 0; + + template_caps = gst_static_pad_template_get_caps (&src_factory); + allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder)); + + /* If downstream has ANY caps let encoder decide profile and level */ + if (allowed_caps == template_caps) { + GST_INFO_OBJECT (thiz, + "downstream has ANY caps, profile/level set to auto"); + } else if (allowed_caps) { + GstStructure *s; + const gchar *profile; + const gchar *level; + + if (gst_caps_is_empty (allowed_caps)) { + gst_caps_unref (allowed_caps); + gst_caps_unref (template_caps); + return FALSE; + } + + allowed_caps = gst_caps_make_writable (allowed_caps); + allowed_caps = gst_caps_fixate (allowed_caps); + s = gst_caps_get_structure (allowed_caps, 0); + + profile = gst_structure_get_string (s, "profile"); + if (profile) { + if (!strcmp (profile, "high")) { + thiz->profile = MFX_PROFILE_AVC_HIGH; + } else if (!strcmp (profile, "main")) { + thiz->profile = MFX_PROFILE_AVC_MAIN; + } else if (!strcmp (profile, "baseline")) { + thiz->profile = MFX_PROFILE_AVC_BASELINE; + } else if (!strcmp (profile, "constrained-baseline")) { + thiz->profile = MFX_PROFILE_AVC_CONSTRAINED_BASELINE; + } else { + g_assert_not_reached (); + } + } + + level = gst_structure_get_string (s, "level"); + if (level) { + thiz->level = gst_codec_utils_h264_get_level_idc (level); + } + + gst_caps_unref (allowed_caps); + } + + gst_caps_unref (template_caps); + + return TRUE; +} + +static gboolean +gst_msdkh264enc_configure (GstMsdkEnc * encoder) +{ + GstMsdkH264Enc *thiz = GST_MSDKH264ENC (encoder); + + encoder->param.mfx.LowPower = + (thiz->lowpower ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF); + encoder->param.mfx.CodecId = MFX_CODEC_AVC; + encoder->param.mfx.CodecProfile = thiz->profile; + encoder->param.mfx.CodecLevel = thiz->level; + + thiz->option.Header.BufferId = MFX_EXTBUFF_CODING_OPTION; + thiz->option.Header.BufferSz = sizeof (thiz->option); + if (thiz->profile == MFX_PROFILE_AVC_CONSTRAINED_BASELINE || + thiz->profile == MFX_PROFILE_AVC_BASELINE || + thiz->profile == MFX_PROFILE_AVC_EXTENDED) { + thiz->option.CAVLC = MFX_CODINGOPTION_ON; + } else { + thiz->option.CAVLC = + (thiz->cabac ? MFX_CODINGOPTION_OFF : MFX_CODINGOPTION_ON); + } + + gst_msdkenc_add_extra_param (encoder, (mfxExtBuffer *) & thiz->option); + + return TRUE; +} + +static inline const gchar * +profile_to_string (gint profile) +{ + switch (profile) { + case MFX_PROFILE_AVC_HIGH: + return "high"; + case MFX_PROFILE_AVC_MAIN: + return "main"; + case MFX_PROFILE_AVC_BASELINE: + return "baseline"; + case MFX_PROFILE_AVC_CONSTRAINED_BASELINE: + return "constrained-baseline"; + default: + break; + } + + return NULL; +} + +static inline const gchar * +level_to_string (gint level) +{ + switch (level) { + case MFX_LEVEL_AVC_1: + return "1"; + case MFX_LEVEL_AVC_1b: + return "1.1"; + case MFX_LEVEL_AVC_11: + return "1.1"; + case MFX_LEVEL_AVC_12: + return "1.2"; + case MFX_LEVEL_AVC_13: + return "1.3"; + case MFX_LEVEL_AVC_2: + return "2"; + case MFX_LEVEL_AVC_21: + return "2.1"; + case MFX_LEVEL_AVC_22: + return "2.2"; + case MFX_LEVEL_AVC_3: + return "3"; + case MFX_LEVEL_AVC_31: + return "3.1"; + case MFX_LEVEL_AVC_32: + return "3.2"; + case MFX_LEVEL_AVC_4: + return "4"; + case MFX_LEVEL_AVC_41: + return "4.1"; + case MFX_LEVEL_AVC_42: + return "4.2"; + case MFX_LEVEL_AVC_5: + return "5"; + case MFX_LEVEL_AVC_51: + return "5.1"; + case MFX_LEVEL_AVC_52: + return "5.2"; + default: + break; + } + + return NULL; +} + +static GstCaps * +gst_msdkh264enc_set_src_caps (GstMsdkEnc * encoder) +{ + GstCaps *caps; + GstStructure *structure; + const gchar *profile; + const gchar *level; + + caps = gst_caps_new_empty_simple ("video/x-h264"); + structure = gst_caps_get_structure (caps, 0); + + gst_structure_set (structure, "stream-format", G_TYPE_STRING, "byte-stream", + NULL); + + gst_structure_set (structure, "alignment", G_TYPE_STRING, "au", NULL); + + profile = profile_to_string (encoder->param.mfx.CodecProfile); + if (profile) + gst_structure_set (structure, "profile", G_TYPE_STRING, profile, NULL); + + level = level_to_string (encoder->param.mfx.CodecLevel); + if (level) + gst_structure_set (structure, "level", G_TYPE_STRING, level, NULL); + + return caps; +} + +static void +gst_msdkh264enc_set_property (GObject * object, guint prop_id, + const GValue * value, GParamSpec * pspec) +{ + GstMsdkH264Enc *thiz = GST_MSDKH264ENC (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_CABAC: + thiz->cabac = g_value_get_boolean (value); + break; + case PROP_LOW_POWER: + thiz->lowpower = g_value_get_boolean (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_msdkh264enc_get_property (GObject * object, guint prop_id, GValue * value, + GParamSpec * pspec) +{ + GstMsdkH264Enc *thiz = GST_MSDKH264ENC (object); + + GST_OBJECT_LOCK (thiz); + switch (prop_id) { + case PROP_CABAC: + g_value_set_boolean (value, thiz->cabac); + break; + case PROP_LOW_POWER: + g_value_set_boolean (value, thiz->lowpower); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } + GST_OBJECT_UNLOCK (thiz); +} + +static void +gst_msdkh264enc_class_init (GstMsdkH264EncClass * klass) +{ + GObjectClass *gobject_class; + GstElementClass *element_class; + GstMsdkEncClass *encoder_class; + + gobject_class = G_OBJECT_CLASS (klass); + element_class = GST_ELEMENT_CLASS (klass); + encoder_class = GST_MSDKENC_CLASS (klass); + + gobject_class->set_property = gst_msdkh264enc_set_property; + gobject_class->get_property = gst_msdkh264enc_get_property; + + encoder_class->set_format = gst_msdkh264enc_set_format; + encoder_class->configure = gst_msdkh264enc_configure; + encoder_class->set_src_caps = gst_msdkh264enc_set_src_caps; + + g_object_class_install_property (gobject_class, PROP_CABAC, + g_param_spec_boolean ("cabac", "CABAC", "Enable CABAC entropy coding", + PROP_CABAC_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property (gobject_class, PROP_LOW_POWER, + g_param_spec_boolean ("low-power", "Low power", "Enable low power mode", + PROP_LOWPOWER_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + + gst_element_class_set_static_metadata (element_class, + "Intel MSDK H264 encoder", + "Codec/Encoder/Video", + "H264 video encoder based on Intel Media SDK", + "Josep Torra "); + + gst_element_class_add_static_pad_template (element_class, &src_factory); +} + +static void +gst_msdkh264enc_init (GstMsdkH264Enc * thiz) +{ + thiz->cabac = PROP_CABAC_DEFAULT; + thiz->lowpower = PROP_LOWPOWER_DEFAULT; +} diff --git a/sys/msdk/gstmsdkh264enc.h b/sys/msdk/gstmsdkh264enc.h new file mode 100644 index 0000000000..28b982f958 --- /dev/null +++ b/sys/msdk/gstmsdkh264enc.h @@ -0,0 +1,75 @@ +/* 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. + */ + +#ifndef __GST_MSDKH264ENC_H__ +#define __GST_MSDKH264ENC_H__ + +#include "gstmsdkenc.h" + +G_BEGIN_DECLS + +#define GST_TYPE_MSDKH264ENC \ + (gst_msdkh264enc_get_type()) +#define GST_MSDKH264ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MSDKH264ENC,GstMsdkH264Enc)) +#define GST_MSDKH264ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MSDKH264ENC,GstMsdkH264EncClass)) +#define GST_IS_MSDKH264ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MSDKH264ENC)) +#define GST_IS_MSDKH264ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MSDKH264ENC)) + +typedef struct _GstMsdkH264Enc GstMsdkH264Enc; +typedef struct _GstMsdkH264EncClass GstMsdkH264EncClass; + +struct _GstMsdkH264Enc +{ + GstMsdkEnc base; + + mfxExtCodingOption option; + + gint profile; + gint level; + + gboolean cabac; + gboolean lowpower; +}; + +struct _GstMsdkH264EncClass +{ + GstMsdkEncClass parent_class; +}; + +GType gst_msdkh264enc_get_type (void); + +G_END_DECLS + +#endif /* __GST_MSDKH264ENC_H__ */ diff --git a/sys/msdk/gstmsdkh265enc.c b/sys/msdk/gstmsdkh265enc.c new file mode 100644 index 0000000000..8c12ef249a --- /dev/null +++ b/sys/msdk/gstmsdkh265enc.c @@ -0,0 +1,155 @@ +/* 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. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "gstmsdkh265enc.h" + +GST_DEBUG_CATEGORY_EXTERN (gst_msdkh265enc_debug); +#define GST_CAT_DEFAULT gst_msdkh265enc_debug + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-h265, " + "framerate = (fraction) [0/1, MAX], " + "width = (int) [ 1, MAX ], height = (int) [ 1, MAX ], " + "stream-format = (string) byte-stream , alignment = (string) au , " + "profile = (string) main") + ); + +#define gst_msdkh265enc_parent_class parent_class +G_DEFINE_TYPE (GstMsdkH265Enc, gst_msdkh265enc, GST_TYPE_MSDKENC); + +static gboolean +gst_msdkh265enc_set_format (GstMsdkEnc * encoder) +{ + return TRUE; +} + +static gboolean +gst_msdkh265enc_configure (GstMsdkEnc * encoder) +{ + encoder->param.mfx.CodecId = MFX_CODEC_HEVC; + encoder->param.mfx.CodecProfile = MFX_PROFILE_HEVC_MAIN; + + return TRUE; +} + +static inline const gchar * +level_to_string (gint level) +{ + switch (level) { + case MFX_LEVEL_HEVC_1: + return "1"; + case MFX_LEVEL_HEVC_2: + return "2"; + case MFX_LEVEL_HEVC_21: + return "2.1"; + case MFX_LEVEL_HEVC_3: + return "3"; + case MFX_LEVEL_HEVC_31: + return "3.1"; + case MFX_LEVEL_HEVC_4: + return "4"; + case MFX_LEVEL_HEVC_41: + return "4.1"; + case MFX_LEVEL_HEVC_5: + return "5"; + case MFX_LEVEL_HEVC_51: + return "5.1"; + case MFX_LEVEL_HEVC_52: + return "5.2"; + case MFX_LEVEL_HEVC_6: + return "6"; + case MFX_LEVEL_HEVC_61: + return "6.1"; + case MFX_LEVEL_HEVC_62: + return "6.2"; + default: + break; + } + + return NULL; +} + +static GstCaps * +gst_msdkh265enc_set_src_caps (GstMsdkEnc * encoder) +{ + GstCaps *caps; + GstStructure *structure; + const gchar *level; + + caps = gst_caps_new_empty_simple ("video/x-h265"); + structure = gst_caps_get_structure (caps, 0); + + gst_structure_set (structure, "stream-format", G_TYPE_STRING, "byte-stream", + NULL); + + gst_structure_set (structure, "alignment", G_TYPE_STRING, "au", NULL); + + gst_structure_set (structure, "profile", G_TYPE_STRING, "main", NULL); + + level = level_to_string (encoder->param.mfx.CodecLevel); + if (level) + gst_structure_set (structure, "level", G_TYPE_STRING, level, NULL); + + return caps; +} + +static void +gst_msdkh265enc_class_init (GstMsdkH265EncClass * klass) +{ + GstElementClass *element_class; + GstMsdkEncClass *encoder_class; + + element_class = GST_ELEMENT_CLASS (klass); + encoder_class = GST_MSDKENC_CLASS (klass); + + encoder_class->set_format = gst_msdkh265enc_set_format; + encoder_class->configure = gst_msdkh265enc_configure; + encoder_class->set_src_caps = gst_msdkh265enc_set_src_caps; + + gst_element_class_set_static_metadata (element_class, + "Intel MSDK H265 encoder", + "Codec/Encoder/Video", + "H265 video encoder based on Intel Media SDK", + "Josep Torra "); + + gst_element_class_add_static_pad_template (element_class, &src_factory); +} + +static void +gst_msdkh265enc_init (GstMsdkH265Enc * thiz) +{ +} diff --git a/sys/msdk/gstmsdkh265enc.h b/sys/msdk/gstmsdkh265enc.h new file mode 100644 index 0000000000..7931b6d7bd --- /dev/null +++ b/sys/msdk/gstmsdkh265enc.h @@ -0,0 +1,67 @@ +/* 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. + */ + +#ifndef __GST_MSDKH265ENC_H__ +#define __GST_MSDKH265ENC_H__ + +#include "gstmsdkenc.h" + +G_BEGIN_DECLS + +#define GST_TYPE_MSDKH265ENC \ + (gst_msdkh265enc_get_type()) +#define GST_MSDKH265ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MSDKH265ENC,GstMsdkH265Enc)) +#define GST_MSDKH265ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MSDKH265ENC,GstMsdkH265EncClass)) +#define GST_IS_MSDKH265ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MSDKH265ENC)) +#define GST_IS_MSDKH265ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MSDKH265ENC)) + +typedef struct _GstMsdkH265Enc GstMsdkH265Enc; +typedef struct _GstMsdkH265EncClass GstMsdkH265EncClass; + +struct _GstMsdkH265Enc +{ + GstMsdkEnc base; +}; + +struct _GstMsdkH265EncClass +{ + GstMsdkEncClass parent_class; +}; + +GType gst_msdkh265enc_get_type (void); + +G_END_DECLS + +#endif /* __GST_MSDKH265ENC_H__ */ diff --git a/sys/msdk/gstmsdkmpeg2enc.c b/sys/msdk/gstmsdkmpeg2enc.c new file mode 100644 index 0000000000..c81c1edf07 --- /dev/null +++ b/sys/msdk/gstmsdkmpeg2enc.c @@ -0,0 +1,176 @@ +/* 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. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "gstmsdkmpeg2enc.h" + +GST_DEBUG_CATEGORY_EXTERN (gst_msdkmpeg2enc_debug); +#define GST_CAT_DEFAULT gst_msdkmpeg2enc_debug + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/mpeg, " + "framerate = (fraction) [0/1, MAX], " + "width = (int) [ 1, MAX ], height = (int) [ 1, MAX ], " + "mpegversion = (int) 2 , systemstream = (bool) false, " + "profile = (string) { high, main, simple }") + ); + +#define gst_msdkmpeg2enc_parent_class parent_class +G_DEFINE_TYPE (GstMsdkMPEG2Enc, gst_msdkmpeg2enc, GST_TYPE_MSDKENC); + +static gboolean +gst_msdkmpeg2enc_set_format (GstMsdkEnc * encoder) +{ + GstMsdkMPEG2Enc *thiz = GST_MSDKMPEG2ENC (encoder); + GstCaps *template_caps; + GstCaps *allowed_caps = NULL; + + thiz->profile = 0; + + template_caps = gst_static_pad_template_get_caps (&src_factory); + allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder)); + + /* If downstream has ANY caps let encoder decide profile and level */ + if (allowed_caps == template_caps) { + GST_INFO_OBJECT (thiz, + "downstream has ANY caps, profile/level set to auto"); + } else if (allowed_caps) { + GstStructure *s; + const gchar *profile; + + if (gst_caps_is_empty (allowed_caps)) { + gst_caps_unref (allowed_caps); + gst_caps_unref (template_caps); + return FALSE; + } + + allowed_caps = gst_caps_make_writable (allowed_caps); + allowed_caps = gst_caps_fixate (allowed_caps); + s = gst_caps_get_structure (allowed_caps, 0); + + profile = gst_structure_get_string (s, "profile"); + if (profile) { + if (!strcmp (profile, "high")) { + thiz->profile = MFX_PROFILE_MPEG2_HIGH; + } else if (!strcmp (profile, "main")) { + thiz->profile = MFX_PROFILE_MPEG2_MAIN; + } else if (!strcmp (profile, "simple")) { + thiz->profile = MFX_PROFILE_MPEG2_SIMPLE; + } else { + g_assert_not_reached (); + } + } + + gst_caps_unref (allowed_caps); + } + + gst_caps_unref (template_caps); + + return TRUE; +} + +static gboolean +gst_msdkmpeg2enc_configure (GstMsdkEnc * encoder) +{ + GstMsdkMPEG2Enc *thiz = GST_MSDKMPEG2ENC (encoder); + + encoder->param.mfx.CodecId = MFX_CODEC_MPEG2; + encoder->param.mfx.CodecProfile = thiz->profile; + encoder->param.mfx.CodecLevel = 0; + + return TRUE; +} + +static inline const gchar * +profile_to_string (gint profile) +{ + switch (profile) { + case MFX_PROFILE_MPEG2_HIGH: + return "high"; + case MFX_PROFILE_MPEG2_MAIN: + return "main"; + case MFX_PROFILE_MPEG2_SIMPLE: + return "simple"; + default: + break; + } + + return NULL; +} + +static GstCaps * +gst_msdkmpeg2enc_set_src_caps (GstMsdkEnc * encoder) +{ + GstCaps *caps; + GstStructure *structure; + const gchar *profile; + + caps = gst_caps_from_string ("video/mpeg, mpegversion=2, systemstream=false"); + structure = gst_caps_get_structure (caps, 0); + + profile = profile_to_string (encoder->param.mfx.CodecProfile); + if (profile) + gst_structure_set (structure, "profile", G_TYPE_STRING, profile, NULL); + + return caps; +} + +static void +gst_msdkmpeg2enc_class_init (GstMsdkMPEG2EncClass * klass) +{ + GstElementClass *element_class; + GstMsdkEncClass *encoder_class; + + element_class = GST_ELEMENT_CLASS (klass); + encoder_class = GST_MSDKENC_CLASS (klass); + + encoder_class->set_format = gst_msdkmpeg2enc_set_format; + encoder_class->configure = gst_msdkmpeg2enc_configure; + encoder_class->set_src_caps = gst_msdkmpeg2enc_set_src_caps; + + gst_element_class_set_static_metadata (element_class, + "Intel MSDK MPEG2 encoder", + "Codec/Encoder/Video", + "MPEG2 video encoder based on Intel Media SDK", + "Josep Torra "); + + gst_element_class_add_static_pad_template (element_class, &src_factory); +} + +static void +gst_msdkmpeg2enc_init (GstMsdkMPEG2Enc * thiz) +{ +} diff --git a/sys/msdk/gstmsdkmpeg2enc.h b/sys/msdk/gstmsdkmpeg2enc.h new file mode 100644 index 0000000000..0439ba4a3f --- /dev/null +++ b/sys/msdk/gstmsdkmpeg2enc.h @@ -0,0 +1,69 @@ +/* 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. + */ + +#ifndef __GST_MSDKMPEG2ENC_H__ +#define __GST_MSDKMPEG2ENC_H__ + +#include "gstmsdkenc.h" + +G_BEGIN_DECLS + +#define GST_TYPE_MSDKMPEG2ENC \ + (gst_msdkmpeg2enc_get_type()) +#define GST_MSDKMPEG2ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MSDKMPEG2ENC,GstMsdkMPEG2Enc)) +#define GST_MSDKMPEG2ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MSDKMPEG2ENC,GstMsdkMPEG2EncClass)) +#define GST_IS_MSDKMPEG2ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MSDKMPEG2ENC)) +#define GST_IS_MSDKMPEG2ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MSDKMPEG2ENC)) + +typedef struct _GstMsdkMPEG2Enc GstMsdkMPEG2Enc; +typedef struct _GstMsdkMPEG2EncClass GstMsdkMPEG2EncClass; + +struct _GstMsdkMPEG2Enc +{ + GstMsdkEnc base; + + gint profile; +}; + +struct _GstMsdkMPEG2EncClass +{ + GstMsdkEncClass parent_class; +}; + +GType gst_msdkmpeg2enc_get_type (void); + +G_END_DECLS + +#endif /* __GST_MSDKMPEG2ENC_H__ */ diff --git a/sys/msdk/gstmsdkvp8enc.c b/sys/msdk/gstmsdkvp8enc.c new file mode 100644 index 0000000000..7c67417069 --- /dev/null +++ b/sys/msdk/gstmsdkvp8enc.c @@ -0,0 +1,180 @@ +/* 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. + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "gstmsdkvp8enc.h" +#include "mfxvp8.h" + +GST_DEBUG_CATEGORY_EXTERN (gst_msdkvp8enc_debug); +#define GST_CAT_DEFAULT gst_msdkvp8enc_debug + +static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("video/x-vp8, " + "framerate = (fraction) [0/1, MAX], " + "width = (int) [ 1, MAX ], height = (int) [ 1, MAX ], " + "profile = (string) { 0, 1, 2, 3 }") + ); + +#define gst_msdkvp8enc_parent_class parent_class +G_DEFINE_TYPE (GstMsdkVP8Enc, gst_msdkvp8enc, GST_TYPE_MSDKENC); + +static gboolean +gst_msdkvp8enc_set_format (GstMsdkEnc * encoder) +{ + GstMsdkVP8Enc *thiz = GST_MSDKVP8ENC (encoder); + GstCaps *template_caps; + GstCaps *allowed_caps = NULL; + + thiz->profile = 0; + + template_caps = gst_static_pad_template_get_caps (&src_factory); + allowed_caps = gst_pad_get_allowed_caps (GST_VIDEO_ENCODER_SRC_PAD (encoder)); + + /* If downstream has ANY caps let encoder decide profile and level */ + if (allowed_caps == template_caps) { + GST_INFO_OBJECT (thiz, + "downstream has ANY caps, profile/level set to auto"); + } else if (allowed_caps) { + GstStructure *s; + const gchar *profile; + + if (gst_caps_is_empty (allowed_caps)) { + gst_caps_unref (allowed_caps); + gst_caps_unref (template_caps); + return FALSE; + } + + allowed_caps = gst_caps_make_writable (allowed_caps); + allowed_caps = gst_caps_fixate (allowed_caps); + s = gst_caps_get_structure (allowed_caps, 0); + + profile = gst_structure_get_string (s, "profile"); + if (profile) { + if (!strcmp (profile, "3")) { + thiz->profile = MFX_PROFILE_VP8_3; + } else if (!strcmp (profile, "2")) { + thiz->profile = MFX_PROFILE_VP8_2; + } else if (!strcmp (profile, "1")) { + thiz->profile = MFX_PROFILE_VP8_1; + } else if (!strcmp (profile, "0")) { + thiz->profile = MFX_PROFILE_VP8_0; + } else { + g_assert_not_reached (); + } + } + + gst_caps_unref (allowed_caps); + } + + gst_caps_unref (template_caps); + + return TRUE; +} + +static gboolean +gst_msdkvp8enc_configure (GstMsdkEnc * encoder) +{ + GstMsdkVP8Enc *thiz = GST_MSDKVP8ENC (encoder); + + encoder->param.mfx.CodecId = MFX_CODEC_VP8; + encoder->param.mfx.CodecProfile = thiz->profile; + encoder->param.mfx.CodecLevel = 0; + + return TRUE; +} + +static inline const gchar * +profile_to_string (gint profile) +{ + switch (profile) { + case MFX_PROFILE_VP8_3: + return "3"; + case MFX_PROFILE_VP8_2: + return "2"; + case MFX_PROFILE_VP8_1: + return "1"; + case MFX_PROFILE_VP8_0: + return "0"; + default: + break; + } + + return NULL; +} + +static GstCaps * +gst_msdkvp8enc_set_src_caps (GstMsdkEnc * encoder) +{ + GstCaps *caps; + GstStructure *structure; + const gchar *profile; + + caps = gst_caps_new_empty_simple ("video/x-vp8"); + structure = gst_caps_get_structure (caps, 0); + + profile = profile_to_string (encoder->param.mfx.CodecProfile); + if (profile) + gst_structure_set (structure, "profile", G_TYPE_STRING, profile, NULL); + + return caps; +} + +static void +gst_msdkvp8enc_class_init (GstMsdkVP8EncClass * klass) +{ + GstElementClass *element_class; + GstMsdkEncClass *encoder_class; + + element_class = GST_ELEMENT_CLASS (klass); + encoder_class = GST_MSDKENC_CLASS (klass); + + encoder_class->set_format = gst_msdkvp8enc_set_format; + encoder_class->configure = gst_msdkvp8enc_configure; + encoder_class->set_src_caps = gst_msdkvp8enc_set_src_caps; + + gst_element_class_set_static_metadata (element_class, + "Intel MSDK VP8 encoder", + "Codec/Encoder/Video", + "VP8 video encoder based on Intel Media SDK", + "Josep Torra "); + + gst_element_class_add_static_pad_template (element_class, &src_factory); +} + +static void +gst_msdkvp8enc_init (GstMsdkVP8Enc * thiz) +{ +} diff --git a/sys/msdk/gstmsdkvp8enc.h b/sys/msdk/gstmsdkvp8enc.h new file mode 100644 index 0000000000..8504ff0a62 --- /dev/null +++ b/sys/msdk/gstmsdkvp8enc.h @@ -0,0 +1,69 @@ +/* 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. + */ + +#ifndef __GST_MSDKVP8ENC_H__ +#define __GST_MSDKVP8ENC_H__ + +#include "gstmsdkenc.h" + +G_BEGIN_DECLS + +#define GST_TYPE_MSDKVP8ENC \ + (gst_msdkvp8enc_get_type()) +#define GST_MSDKVP8ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MSDKVP8ENC,GstMsdkVP8Enc)) +#define GST_MSDKVP8ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MSDKVP8ENC,GstMsdkVP8EncClass)) +#define GST_IS_MSDKVP8ENC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MSDKVP8ENC)) +#define GST_IS_MSDKVP8ENC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MSDKVP8ENC)) + +typedef struct _GstMsdkVP8Enc GstMsdkVP8Enc; +typedef struct _GstMsdkVP8EncClass GstMsdkVP8EncClass; + +struct _GstMsdkVP8Enc +{ + GstMsdkEnc base; + + gint profile; +}; + +struct _GstMsdkVP8EncClass +{ + GstMsdkEncClass parent_class; +}; + +GType gst_msdkvp8enc_get_type (void); + +G_END_DECLS + +#endif /* __GST_MSDKVP8ENC_H__ */ diff --git a/sys/msdk/msdk.h b/sys/msdk/msdk.h new file mode 100644 index 0000000000..7339c66cc6 --- /dev/null +++ b/sys/msdk/msdk.h @@ -0,0 +1,60 @@ +/* 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. + */ + +#ifndef __MSDK_H__ +#define __MSDK_H__ + +#include +#include + +#include +#include +#include "mfxvideo.h" + +G_BEGIN_DECLS + +typedef struct _MsdkContext MsdkContext; + +gboolean msdk_is_available (void); + +MsdkContext *msdk_open_context (gboolean hardware); +void msdk_close_context (MsdkContext * context); +mfxSession msdk_context_get_session (MsdkContext * context); + +mfxFrameSurface1 *msdk_get_free_surface (mfxFrameSurface1 * surfaces, + guint size); +void msdk_frame_to_surface (GstVideoFrame * frame, mfxFrameSurface1 * surface); + +const gchar *msdk_status_to_string (mfxStatus status); + +G_END_DECLS + +#endif /* __MSDK_H__ */ diff --git a/sys/msdk/msdk_libva.c b/sys/msdk/msdk_libva.c new file mode 100644 index 0000000000..fbb1f3bf05 --- /dev/null +++ b/sys/msdk/msdk_libva.c @@ -0,0 +1,361 @@ +/* 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: + * - discover dri_path instead of having it hardcoded + */ + +#include + +#include +#include "msdk.h" + +GST_DEBUG_CATEGORY_EXTERN (gst_msdkenc_debug); +#define GST_CAT_DEFAULT gst_msdkenc_debug + +#define INVALID_INDEX ((guint) -1) + +struct _MsdkContext +{ + mfxSession session; + gint fd; + VADisplay dpy; +}; + +static inline void +msdk_close_session (mfxSession session) +{ + mfxStatus status; + + if (!session) + return; + + status = MFXClose (session); + if (status != MFX_ERR_NONE) + GST_ERROR ("Close failed (%s)", msdk_status_to_string (status)); +} + +static inline mfxSession +msdk_open_session (gboolean hardware) +{ + mfxSession session = NULL; + mfxVersion version = { {1, 1} }; + mfxIMPL implementation; + mfxStatus status; + + static const gchar *implementation_names[] = { + "AUTO", "SOFTWARE", "HARDWARE", "AUTO_ANY", "HARDWARE_ANY", "HARDWARE2", + "HARDWARE3", "HARDWARE4", "RUNTIME" + }; + + status = MFXInit (hardware ? MFX_IMPL_HARDWARE_ANY : MFX_IMPL_SOFTWARE, + &version, &session); + if (status != MFX_ERR_NONE) { + GST_ERROR ("Intel Media SDK not available (%s)", + msdk_status_to_string (status)); + goto failed; + } + + MFXQueryIMPL (session, &implementation); + if (status != MFX_ERR_NONE) { + GST_ERROR ("Query implementation failed (%s)", + msdk_status_to_string (status)); + goto failed; + } + + MFXQueryVersion (session, &version); + if (status != MFX_ERR_NONE) { + GST_ERROR ("Query version failed (%s)", msdk_status_to_string (status)); + goto failed; + } + + GST_INFO ("MSDK implementation: 0x%04x (%s)", implementation, + implementation_names[MFX_IMPL_BASETYPE (implementation)]); + GST_INFO ("MSDK version: %d.%d", version.Major, version.Minor); + + return session; + +failed: + msdk_close_session (session); + return NULL; +} + +gboolean +msdk_is_available (void) +{ + mfxSession session = msdk_open_session (FALSE); + if (!session) { + return FALSE; + } + + msdk_close_session (session); + return TRUE; +} + +static gboolean +msdk_use_vaapi_on_context (MsdkContext * context) +{ + gint fd; + gint maj_ver, min_ver; + VADisplay va_dpy = NULL; + VAStatus va_status; + mfxStatus status; + /* maybe /dev/dri/renderD128 */ + static const gchar *dri_path = "/dev/dri/card0"; + + fd = open (dri_path, O_RDWR); + if (fd < 0) { + GST_ERROR ("Couldn't open %s", dri_path); + return FALSE; + } + + va_dpy = vaGetDisplayDRM (fd); + if (!va_dpy) { + GST_ERROR ("Couldn't get a VA DRM display"); + goto failed; + } + + va_status = vaInitialize (va_dpy, &maj_ver, &min_ver); + if (va_status != VA_STATUS_SUCCESS) { + GST_ERROR ("Couldn't initialize VA DRM display"); + goto failed; + } + + status = MFXVideoCORE_SetHandle (context->session, MFX_HANDLE_VA_DISPLAY, + (mfxHDL) va_dpy); + if (status != MFX_ERR_NONE) { + GST_ERROR ("Setting VAAPI handle failed (%s)", + msdk_status_to_string (status)); + goto failed; + } + + context->fd = fd; + context->dpy = va_dpy; + + return TRUE; + +failed: + if (va_dpy) + vaTerminate (va_dpy); + close (fd); + return FALSE; +} + +MsdkContext * +msdk_open_context (gboolean hardware) +{ + MsdkContext *context = g_slice_new0 (MsdkContext); + context->fd = -1; + + context->session = msdk_open_session (hardware); + if (!context->session) + goto failed; + + if (hardware) { + if (!msdk_use_vaapi_on_context (context)) + goto failed; + } + + return context; + +failed: + msdk_close_session (context->session); + g_slice_free (MsdkContext, context); + return NULL; +} + +void +msdk_close_context (MsdkContext * context) +{ + if (!context) + return; + + msdk_close_session (context->session); + if (context->dpy) + vaTerminate (context->dpy); + if (context->fd >= 0) + close (context->fd); + g_slice_free (MsdkContext, context); +} + +mfxSession +msdk_context_get_session (MsdkContext * context) +{ + return context->session; +} + +static inline guint +msdk_get_free_surface_index (mfxFrameSurface1 * surfaces, guint size) +{ + if (surfaces) { + for (guint i = 0; i < size; i++) { + if (!surfaces[i].Data.Locked) + return i; + } + } + + return INVALID_INDEX; +} + +mfxFrameSurface1 * +msdk_get_free_surface (mfxFrameSurface1 * surfaces, guint size) +{ + guint idx = INVALID_INDEX; + + /* Poll the pool for a maximum of 20 milisecnds */ + for (guint i = 0; i < 2000; i++) { + idx = msdk_get_free_surface_index (surfaces, size); + + if (idx != INVALID_INDEX) + break; + + g_usleep (10); + } + + return (idx == INVALID_INDEX ? NULL : &surfaces[idx]); +} + +/* FIXME: Only NV12 is supported by now, add other YUV formats */ +void +msdk_frame_to_surface (GstVideoFrame * frame, mfxFrameSurface1 * surface) +{ + guint8 *src, *dst; + guint sstride, dstride; + guint width, height; + + if (!surface->Data.MemId) { + surface->Data.Y = GST_VIDEO_FRAME_COMP_DATA (frame, 0); + surface->Data.UV = GST_VIDEO_FRAME_COMP_DATA (frame, 1); + surface->Data.Pitch = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); + return; + } + + /* Y Plane */ + width = GST_VIDEO_FRAME_COMP_WIDTH (frame, 0); + height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 0); + src = GST_VIDEO_FRAME_COMP_DATA (frame, 0); + sstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 0); + dst = surface->Data.Y; + dstride = surface->Data.Pitch; + + for (guint i = 0; i < height; i++) { + memcpy (dst, src, width); + src += sstride; + dst += dstride; + } + + /* UV Plane */ + height = GST_VIDEO_FRAME_COMP_HEIGHT (frame, 1); + src = GST_VIDEO_FRAME_COMP_DATA (frame, 1); + sstride = GST_VIDEO_FRAME_COMP_STRIDE (frame, 1); + dst = surface->Data.UV; + + for (guint i = 0; i < height; i++) { + memcpy (dst, src, width); + src += sstride; + dst += dstride; + } +} + +const gchar * +msdk_status_to_string (mfxStatus status) +{ + switch (status) { + /* no error */ + case MFX_ERR_NONE: + return "no error"; + /* reserved for unexpected errors */ + case MFX_ERR_UNKNOWN: + return "unknown error"; + /* error codes <0 */ + case MFX_ERR_NULL_PTR: + return "null pointer"; + case MFX_ERR_UNSUPPORTED: + return "undeveloped feature"; + case MFX_ERR_MEMORY_ALLOC: + return "failed to allocate memory"; + case MFX_ERR_NOT_ENOUGH_BUFFER: + return "insufficient buffer at input/output"; + case MFX_ERR_INVALID_HANDLE: + return "invalid handle"; + case MFX_ERR_LOCK_MEMORY: + return "failed to lock the memory block"; + case MFX_ERR_NOT_INITIALIZED: + return "member function called before initialization"; + case MFX_ERR_NOT_FOUND: + return "the specified object is not found"; + case MFX_ERR_MORE_DATA: + return "expect more data at input"; + case MFX_ERR_MORE_SURFACE: + return "expect more surface at output"; + case MFX_ERR_ABORTED: + return "operation aborted"; + case MFX_ERR_DEVICE_LOST: + return "lose the HW acceleration device"; + case MFX_ERR_INCOMPATIBLE_VIDEO_PARAM: + return "incompatible video parameters"; + case MFX_ERR_INVALID_VIDEO_PARAM: + return "invalid video parameters"; + case MFX_ERR_UNDEFINED_BEHAVIOR: + return "undefined behavior"; + case MFX_ERR_DEVICE_FAILED: + return "device operation failure"; + case MFX_ERR_MORE_BITSTREAM: + return "expect more bitstream buffers at output"; + case MFX_ERR_INCOMPATIBLE_AUDIO_PARAM: + return "incompatible audio parameters"; + case MFX_ERR_INVALID_AUDIO_PARAM: + return "invalid audio parameters"; + /* warnings >0 */ + case MFX_WRN_IN_EXECUTION: + return "the previous asynchronous operation is in execution"; + case MFX_WRN_DEVICE_BUSY: + return "the HW acceleration device is busy"; + case MFX_WRN_VIDEO_PARAM_CHANGED: + return "the video parameters are changed during decoding"; + case MFX_WRN_PARTIAL_ACCELERATION: + return "SW is used"; + case MFX_WRN_INCOMPATIBLE_VIDEO_PARAM: + return "incompatible video parameters"; + case MFX_WRN_VALUE_NOT_CHANGED: + return "the value is saturated based on its valid range"; + case MFX_WRN_OUT_OF_RANGE: + return "the value is out of valid range"; + case MFX_WRN_FILTER_SKIPPED: + return "one of requested filters has been skipped"; + case MFX_WRN_INCOMPATIBLE_AUDIO_PARAM: + return "incompatible audio parameters"; + default: + break; + } + return "undefiend error"; +}