msdk: Create plugin for Intel's Media SDK
authorJosep Torra <jtorra@oblong.com>
Fri, 1 Apr 2016 17:25:25 +0000 (19:25 +0200)
committerJosep Torra <n770galaxy@gmail.com>
Mon, 14 Nov 2016 14:16:45 +0000 (15:16 +0100)
[scott.d.phillips@intel.com: gst-indent *.c *.h]
[scott.d.phillips@intel.com: link with libva at build time]
[scott.d.phillips@intel.com: remove unused includes]
[scott.d.phillips@intel.com: Update README]

https://bugzilla.gnome.org/show_bug.cgi?id=770990

14 files changed:
sys/msdk/README [new file with mode: 0644]
sys/msdk/gstmsdk.c [new file with mode: 0644]
sys/msdk/gstmsdkenc.c [new file with mode: 0644]
sys/msdk/gstmsdkenc.h [new file with mode: 0644]
sys/msdk/gstmsdkh264enc.c [new file with mode: 0644]
sys/msdk/gstmsdkh264enc.h [new file with mode: 0644]
sys/msdk/gstmsdkh265enc.c [new file with mode: 0644]
sys/msdk/gstmsdkh265enc.h [new file with mode: 0644]
sys/msdk/gstmsdkmpeg2enc.c [new file with mode: 0644]
sys/msdk/gstmsdkmpeg2enc.h [new file with mode: 0644]
sys/msdk/gstmsdkvp8enc.c [new file with mode: 0644]
sys/msdk/gstmsdkvp8enc.h [new file with mode: 0644]
sys/msdk/msdk.h [new file with mode: 0644]
sys/msdk/msdk_libva.c [new file with mode: 0644]

diff --git a/sys/msdk/README b/sys/msdk/README
new file mode 100644 (file)
index 0000000..75fa832
--- /dev/null
@@ -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 (file)
index 0000000..e5ccfe5
--- /dev/null
@@ -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 <config.h>
+#endif
+
+#include <gst/gst.h>
+
+#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 (file)
index 0000000..fb92ad0
--- /dev/null
@@ -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 <config.h>
+#endif
+
+#include <stdlib.h>
+
+#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 (file)
index 0000000..1e316ec
--- /dev/null
@@ -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 <gst/gst.h>
+#include <gst/video/gstvideoencoder.h>
+#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 (file)
index 0000000..fd2aa00
--- /dev/null
@@ -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 <config.h>
+#endif
+
+#include "gstmsdkh264enc.h"
+
+#include <gst/pbutils/pbutils.h>
+
+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 <jtorra@oblong.com>");
+
+  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 (file)
index 0000000..28b982f
--- /dev/null
@@ -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 (file)
index 0000000..8c12ef2
--- /dev/null
@@ -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 <config.h>
+#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 <jtorra@oblong.com>");
+
+  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 (file)
index 0000000..7931b6d
--- /dev/null
@@ -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 (file)
index 0000000..c81c1ed
--- /dev/null
@@ -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 <config.h>
+#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 <jtorra@oblong.com>");
+
+  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 (file)
index 0000000..0439ba4
--- /dev/null
@@ -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 (file)
index 0000000..7c67417
--- /dev/null
@@ -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 <config.h>
+#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 <jtorra@oblong.com>");
+
+  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 (file)
index 0000000..8504ff0
--- /dev/null
@@ -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 (file)
index 0000000..7339c66
--- /dev/null
@@ -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 <string.h>
+#include <unistd.h>
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#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 (file)
index 0000000..fbb1f3b
--- /dev/null
@@ -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 <fcntl.h>
+
+#include <va/va_drm.h>
+#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";
+}