summaryrefslogtreecommitdiff
path: root/ext/x265
diff options
context:
space:
mode:
authorThijs Vermeir <thijsvermeir@gmail.com>2014-03-05 16:39:30 +0100
committerThijs Vermeir <thijsvermeir@gmail.com>2014-10-15 17:11:39 +0200
commitf241294662f3b48c10e527e9949aa2449e51739a (patch)
tree0a50677952621dec62d841c9f239dcf6ef919b19 /ext/x265
parent771b94b3ca441ae6b487965ca879ae67d0860848 (diff)
downloadgstreamer-plugins-bad-f241294662f3b48c10e527e9949aa2449e51739a.tar.gz
x265enc: add x265 encoder element
Diffstat (limited to 'ext/x265')
-rw-r--r--ext/x265/Makefile.am32
-rw-r--r--ext/x265/gstx265enc.c1236
-rw-r--r--ext/x265/gstx265enc.h87
3 files changed, 1355 insertions, 0 deletions
diff --git a/ext/x265/Makefile.am b/ext/x265/Makefile.am
new file mode 100644
index 000000000..d2a810253
--- /dev/null
+++ b/ext/x265/Makefile.am
@@ -0,0 +1,32 @@
+plugin_LTLIBRARIES = libgstx265.la
+
+libgstx265_la_SOURCES = gstx265enc.c
+libgstx265_la_CFLAGS = \
+ $(GST_PLUGINS_BASE_CFLAGS) \
+ $(GST_CFLAGS) \
+ $(X265_CFLAGS)
+libgstx265_la_LIBADD = \
+ $(GST_PLUGINS_BASE_LIBS) \
+ -lgstvideo-$(GST_API_VERSION) \
+ -lgstpbutils-$(GST_API_VERSION) \
+ $(GST_LIBS) \
+ $(X265_LIBS)
+libgstx265_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgstx265_la_LIBTOOLFLAGS = $(GST_PLUGIN_LIBTOOLFLAGS)
+
+noinst_HEADERS = gstx265enc.h
+
+Android.mk: Makefile.am $(BUILT_SOURCES)
+ androgenizer \
+ -:PROJECT libgstx265 -:SHARED libgstx265 \
+ -:TAGS eng debug \
+ -:REL_TOP $(top_srcdir) -:ABS_TOP $(abs_top_srcdir) \
+ -:SOURCES $(libgstx265_la_SOURCES) \
+ -:CPPFLAGS $(CPPFLAGS) \
+ -:CFLAGS $(DEFS) $(DEFAULT_INCLUDES) $(libgstx265_la_CFLAGS) -I'$$(GSTREAMER_AGGREGATE_TOP)/x265' \
+ -:LDFLAGS $(libgstx265_la_LDFLAGS) \
+ $(libgstx265_la_LIBADD) \
+ -ldl \
+ -:PASSTHROUGH LOCAL_ARM_MODE:=arm \
+ LOCAL_MODULE_PATH:='$$(TARGET_OUT)/lib/gstreamer-0.10' \
+ > $@
diff --git a/ext/x265/gstx265enc.c b/ext/x265/gstx265enc.c
new file mode 100644
index 000000000..662979414
--- /dev/null
+++ b/ext/x265/gstx265enc.c
@@ -0,0 +1,1236 @@
+/* GStreamer H265 encoder plugin
+ * Copyright (C) 2005 Michal Benes <michal.benes@itonis.tv>
+ * Copyright (C) 2005 Josef Zlomek <josef.zlomek@itonis.tv>
+ * Copyright (C) 2008 Mark Nauwelaerts <mnauw@users.sf.net>
+ * Copyright (C) 2014 Thijs Vermeir <thijs.vermeir@barco.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * SECTION:element-x265enc
+ *
+ * This element encodes raw video into H265 compressed data.
+ *
+ **/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "gstx265enc.h"
+
+#include <gst/pbutils/pbutils.h>
+#include <gst/video/video.h>
+#include <gst/video/gstvideometa.h>
+#include <gst/video/gstvideopool.h>
+
+#include <string.h>
+#include <stdlib.h>
+
+GST_DEBUG_CATEGORY_STATIC (x265_enc_debug);
+#define GST_CAT_DEFAULT x265_enc_debug
+
+enum
+{
+ PROP_0,
+ PROP_BITRATE,
+ PROP_OPTION_STRING,
+ PROP_X265_LOG_LEVEL,
+ PROP_SPEED_PRESET,
+ PROP_TUNE
+};
+
+static GString *x265enc_defaults;
+
+#define PROP_BITRATE_DEFAULT (2 * 1024)
+#define PROP_OPTION_STRING_DEFAULT ""
+#define PROP_LOG_LEVEL_DEFAULT -1 // None
+#define PROP_SPEED_PRESET_DEFAULT 6 // Medium
+#define PROP_TUNE_DEFAULT 2 // SSIM
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define FORMATS "I420, Y444"
+#else
+#define FORMATS "I420, Y444"
+#endif
+
+#define GST_X265_ENC_LOG_LEVEL_TYPE (gst_x265_enc_log_level_get_type())
+static GType
+gst_x265_enc_log_level_get_type (void)
+{
+ static GType log_level = 0;
+
+ static const GEnumValue log_levels[] = {
+ {X265_LOG_NONE, "No logging", "none"},
+ {X265_LOG_ERROR, "Error", "error"},
+ {X265_LOG_WARNING, "Warning", "warning"},
+ {X265_LOG_INFO, "Info", "info"},
+ {X265_LOG_DEBUG, "Debug", "debug"},
+ {X265_LOG_FULL, "Full", "full"},
+ {0, NULL, NULL}
+ };
+
+ if (!log_level) {
+ log_level = g_enum_register_static ("GstX265LogLevel", log_levels);
+ }
+ return log_level;
+}
+
+#define GST_X265_ENC_SPEED_PRESET_TYPE (gst_x265_enc_speed_preset_get_type())
+static GType
+gst_x265_enc_speed_preset_get_type (void)
+{
+ static GType speed_preset = 0;
+ static GEnumValue *speed_presets;
+ int n, i;
+
+ if (speed_preset != 0)
+ return speed_preset;
+
+ n = 0;
+ while (x265_preset_names[n] != NULL)
+ n++;
+
+ speed_presets = g_new0 (GEnumValue, n + 2);
+
+ speed_presets[0].value = 0;
+ speed_presets[0].value_name = "No preset";
+ speed_presets[0].value_nick = "No preset";
+
+ for (i = 0; i < n; i++) {
+ speed_presets[i + 1].value = i + 1;
+ speed_presets[i + 1].value_name = x265_preset_names[i];
+ speed_presets[i + 1].value_nick = x265_preset_names[i];
+ }
+
+ speed_preset = g_enum_register_static ("GstX265SpeedPreset", speed_presets);
+
+ return speed_preset;
+}
+
+#define GST_X265_ENC_TUNE_TYPE (gst_x265_enc_tune_get_type())
+static GType
+gst_x265_enc_tune_get_type (void)
+{
+ static GType tune = 0;
+ static GEnumValue *tune_values;
+ int n, i;
+
+ if (tune != 0)
+ return tune;
+
+ n = 0;
+ while (x265_tune_names[n] != NULL)
+ n++;
+
+ tune_values = g_new0 (GEnumValue, n + 2);
+
+ tune_values[0].value = 0;
+ tune_values[0].value_name = "No tunning";
+ tune_values[0].value_nick = "No tunning";
+
+ for (i = 0; i < n; i++) {
+ tune_values[i + 1].value = i + 1;
+ tune_values[i + 1].value_name = x265_tune_names[i];
+ tune_values[i + 1].value_nick = x265_tune_names[i];
+ }
+
+ tune = g_enum_register_static ("GstX265Tune", tune_values);
+
+ return tune;
+}
+
+static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
+ GST_PAD_SINK,
+ GST_PAD_ALWAYS,
+ GST_STATIC_CAPS ("video/x-raw, "
+ "format = (string) { " FORMATS " }, "
+ "framerate = (fraction) [0, MAX], "
+ "width = (int) [ 4, MAX ], " "height = (int) [ 4, MAX ]")
+ );
+
+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) [ 4, MAX ], " "height = (int) [ 4, MAX ], "
+ "stream-format = (string) { avc, byte-stream }, "
+ "alignment = (string) au, " "profile = (string) { main }")
+ );
+
+static void gst_x265_enc_finalize (GObject * object);
+static gboolean gst_x265_enc_start (GstVideoEncoder * encoder);
+static gboolean gst_x265_enc_stop (GstVideoEncoder * encoder);
+static gboolean gst_x265_enc_flush (GstVideoEncoder * encoder);
+
+static gboolean gst_x265_enc_init_encoder (GstX265Enc * encoder);
+static void gst_x265_enc_close_encoder (GstX265Enc * encoder);
+
+static GstFlowReturn gst_x265_enc_finish (GstVideoEncoder * encoder);
+static GstFlowReturn gst_x265_enc_handle_frame (GstVideoEncoder * encoder,
+ GstVideoCodecFrame * frame);
+static void gst_x265_enc_flush_frames (GstX265Enc * encoder, gboolean send);
+static GstFlowReturn gst_x265_enc_encode_frame (GstX265Enc * encoder,
+ x265_picture * pic_in, GstVideoCodecFrame * input_frame, guint32 * i_nal,
+ gboolean send);
+static gboolean gst_x265_enc_set_format (GstVideoEncoder * video_enc,
+ GstVideoCodecState * state);
+static gboolean gst_x265_enc_propose_allocation (GstVideoEncoder * encoder,
+ GstQuery * query);
+
+static void gst_x265_enc_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec);
+static void gst_x265_enc_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec);
+
+#define gst_x265_enc_parent_class parent_class
+G_DEFINE_TYPE_WITH_CODE (GstX265Enc, gst_x265_enc, GST_TYPE_VIDEO_ENCODER,
+ G_IMPLEMENT_INTERFACE (GST_TYPE_PRESET, NULL));
+
+static void
+set_value (GValue * val, gint count, ...)
+{
+ const gchar *fmt = NULL;
+ GValue sval = G_VALUE_INIT;
+ va_list ap;
+ gint i;
+
+ g_value_init (&sval, G_TYPE_STRING);
+
+ if (count > 1)
+ g_value_init (val, GST_TYPE_LIST);
+
+ va_start (ap, count);
+ for (i = 0; i < count; i++) {
+ fmt = va_arg (ap, const gchar *);
+ g_value_set_string (&sval, fmt);
+ if (count > 1) {
+ gst_value_list_append_value (val, &sval);
+ }
+ }
+ va_end (ap);
+
+ if (count == 1)
+ *val = sval;
+ else
+ g_value_unset (&sval);
+}
+
+static void
+gst_x265_enc_add_x265_chroma_format (GstStructure * s,
+ int x265_chroma_format_local)
+{
+ GValue fmt = G_VALUE_INIT;
+
+ GST_INFO ("This x265 build supports 8-bit depth");
+ if (x265_chroma_format_local == 0) {
+ set_value (&fmt, 2, "I420", "Y444");
+ } else if (x265_chroma_format_local == X265_CSP_I444) {
+ set_value (&fmt, 1, "Y444");
+ } else if (x265_chroma_format_local == X265_CSP_I420) {
+ set_value (&fmt, 1, "I420");
+ } else {
+ GST_ERROR ("Unsupported chroma format %d", x265_chroma_format_local);
+ }
+
+ if (G_VALUE_TYPE (&fmt) != G_TYPE_INVALID)
+ gst_structure_take_value (s, "format", &fmt);
+}
+
+static GstCaps *
+gst_x265_enc_get_supported_input_caps (void)
+{
+ GstCaps *caps;
+ int x265_chroma_format = 0;
+
+ caps = gst_caps_new_simple ("video/x-raw",
+ "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, G_MAXINT, 1,
+ "width", GST_TYPE_INT_RANGE, 4, G_MAXINT,
+ "height", GST_TYPE_INT_RANGE, 4, G_MAXINT, NULL);
+
+ gst_x265_enc_add_x265_chroma_format (gst_caps_get_structure (caps, 0),
+ x265_chroma_format);
+
+ GST_DEBUG ("returning %" GST_PTR_FORMAT, caps);
+ return caps;
+}
+
+static GstCaps *
+gst_x265_enc_sink_getcaps (GstVideoEncoder * enc, GstCaps * filter)
+{
+ GstCaps *supported_incaps;
+ GstCaps *allowed;
+ GstCaps *filter_caps, *fcaps;
+ gint i, j;
+
+ supported_incaps = gst_x265_enc_get_supported_input_caps ();
+
+ if (!supported_incaps)
+ supported_incaps = gst_pad_get_pad_template_caps (enc->sinkpad);
+ allowed = gst_pad_get_allowed_caps (enc->srcpad);
+
+ if (!allowed || gst_caps_is_empty (allowed) || gst_caps_is_any (allowed)) {
+ fcaps = supported_incaps;
+ goto done;
+ }
+
+ GST_LOG_OBJECT (enc, "template caps %" GST_PTR_FORMAT, supported_incaps);
+ GST_LOG_OBJECT (enc, "allowed caps %" GST_PTR_FORMAT, allowed);
+
+ filter_caps = gst_caps_new_empty ();
+
+ for (i = 0; i < gst_caps_get_size (supported_incaps); i++) {
+ GQuark q_name =
+ gst_structure_get_name_id (gst_caps_get_structure (supported_incaps,
+ i));
+
+ for (j = 0; j < gst_caps_get_size (allowed); j++) {
+ const GstStructure *allowed_s = gst_caps_get_structure (allowed, j);
+ const GValue *val;
+ GstStructure *s;
+
+ s = gst_structure_new_id_empty (q_name);
+ if ((val = gst_structure_get_value (allowed_s, "width")))
+ gst_structure_set_value (s, "width", val);
+ if ((val = gst_structure_get_value (allowed_s, "height")))
+ gst_structure_set_value (s, "height", val);
+ if ((val = gst_structure_get_value (allowed_s, "framerate")))
+ gst_structure_set_value (s, "framerate", val);
+ if ((val = gst_structure_get_value (allowed_s, "pixel-aspect-ratio")))
+ gst_structure_set_value (s, "pixel-aspect-ratio", val);
+
+ filter_caps = gst_caps_merge_structure (filter_caps, s);
+ }
+ }
+
+ fcaps = gst_caps_intersect (filter_caps, supported_incaps);
+ gst_caps_unref (filter_caps);
+ gst_caps_unref (supported_incaps);
+
+ if (filter) {
+ GST_LOG_OBJECT (enc, "intersecting with %" GST_PTR_FORMAT, filter);
+ filter_caps = gst_caps_intersect (fcaps, filter);
+ gst_caps_unref (fcaps);
+ fcaps = filter_caps;
+ }
+
+done:
+ gst_caps_replace (&allowed, NULL);
+
+ GST_LOG_OBJECT (enc, "proxy caps %" GST_PTR_FORMAT, fcaps);
+
+ return fcaps;
+}
+
+static void
+gst_x265_enc_class_init (GstX265EncClass * klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *element_class;
+ GstVideoEncoderClass *gstencoder_class;
+
+ x265enc_defaults = g_string_new ("");
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ element_class = GST_ELEMENT_CLASS (klass);
+ gstencoder_class = GST_VIDEO_ENCODER_CLASS (klass);
+
+ gobject_class->set_property = gst_x265_enc_set_property;
+ gobject_class->get_property = gst_x265_enc_get_property;
+ gobject_class->finalize = gst_x265_enc_finalize;
+
+ gstencoder_class->set_format = GST_DEBUG_FUNCPTR (gst_x265_enc_set_format);
+ gstencoder_class->handle_frame =
+ GST_DEBUG_FUNCPTR (gst_x265_enc_handle_frame);
+ gstencoder_class->start = GST_DEBUG_FUNCPTR (gst_x265_enc_start);
+ gstencoder_class->stop = GST_DEBUG_FUNCPTR (gst_x265_enc_stop);
+ gstencoder_class->flush = GST_DEBUG_FUNCPTR (gst_x265_enc_flush);
+ gstencoder_class->finish = GST_DEBUG_FUNCPTR (gst_x265_enc_finish);
+ gstencoder_class->getcaps = GST_DEBUG_FUNCPTR (gst_x265_enc_sink_getcaps);
+ gstencoder_class->propose_allocation =
+ GST_DEBUG_FUNCPTR (gst_x265_enc_propose_allocation);
+
+ g_object_class_install_property (gobject_class, PROP_BITRATE,
+ g_param_spec_uint ("bitrate", "Bitrate", "Bitrate in kbit/sec", 1,
+ 100 * 1024, PROP_BITRATE_DEFAULT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
+ GST_PARAM_MUTABLE_PLAYING));
+
+ g_object_class_install_property (gobject_class, PROP_OPTION_STRING,
+ g_param_spec_string ("option-string", "Option string",
+ "String of x264 options (overridden by element properties)",
+ PROP_OPTION_STRING_DEFAULT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_X265_LOG_LEVEL,
+ g_param_spec_enum ("log-level", "(internal) x265 log level",
+ "x265 log level", GST_X265_ENC_LOG_LEVEL_TYPE,
+ PROP_LOG_LEVEL_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_SPEED_PRESET,
+ g_param_spec_enum ("speed-preset", "Speed preset",
+ "Preset name for speed/quality tradeoff options",
+ GST_X265_ENC_SPEED_PRESET_TYPE, PROP_SPEED_PRESET_DEFAULT,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_TUNE,
+ g_param_spec_enum ("tune", "Tune options",
+ "Preset name for tuning options", GST_X265_ENC_TUNE_TYPE,
+ PROP_TUNE_DEFAULT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ gst_element_class_set_static_metadata (element_class,
+ "x265enc", "Codec/Encoder/Video", "H265 Encoder",
+ "Thijs Vermeir <thijs.vermeir@barco.com>");
+
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&sink_factory));
+ gst_element_class_add_pad_template (element_class,
+ gst_static_pad_template_get (&src_factory));
+}
+
+/* initialize the new element
+ * instantiate pads and add them to element
+ * set functions
+ * initialize structure
+ */
+static void
+gst_x265_enc_init (GstX265Enc * encoder)
+{
+ x265_param_default (&encoder->x265param);
+
+ encoder->push_header = TRUE;
+
+ encoder->bitrate = PROP_BITRATE_DEFAULT;
+ encoder->option_string_prop = g_string_new (PROP_OPTION_STRING_DEFAULT);
+ encoder->log_level = PROP_LOG_LEVEL_DEFAULT;
+ encoder->speed_preset = PROP_SPEED_PRESET_DEFAULT;
+ encoder->tune = PROP_TUNE_DEFAULT;
+}
+
+typedef struct
+{
+ GstVideoCodecFrame *frame;
+ GstVideoFrame vframe;
+} FrameData;
+
+static FrameData *
+gst_x265_enc_queue_frame (GstX265Enc * enc, 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;
+
+ enc->pending_frames = g_list_prepend (enc->pending_frames, fdata);
+
+ return fdata;
+}
+
+static void
+gst_x265_enc_dequeue_frame (GstX265Enc * enc, GstVideoCodecFrame * frame)
+{
+ GList *l;
+
+ for (l = enc->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);
+
+ enc->pending_frames = g_list_delete_link (enc->pending_frames, l);
+ return;
+ }
+}
+
+static void
+gst_x265_enc_dequeue_all_frames (GstX265Enc * enc)
+{
+ GList *l;
+
+ for (l = enc->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 (enc->pending_frames);
+ enc->pending_frames = NULL;
+}
+
+static gboolean
+gst_x265_enc_start (GstVideoEncoder * encoder)
+{
+ //GstX265Enc *x265enc = GST_X265_ENC (encoder);
+
+ return TRUE;
+}
+
+static gboolean
+gst_x265_enc_stop (GstVideoEncoder * encoder)
+{
+ GstX265Enc *x265enc = GST_X265_ENC (encoder);
+
+ GST_DEBUG_OBJECT (encoder, "stop encoder");
+
+ gst_x265_enc_flush_frames (x265enc, FALSE);
+ gst_x265_enc_close_encoder (x265enc);
+ gst_x265_enc_dequeue_all_frames (x265enc);
+
+ if (x265enc->input_state)
+ gst_video_codec_state_unref (x265enc->input_state);
+ x265enc->input_state = NULL;
+
+ return TRUE;
+}
+
+
+static gboolean
+gst_x265_enc_flush (GstVideoEncoder * encoder)
+{
+ GstX265Enc *x265enc = GST_X265_ENC (encoder);
+
+ GST_DEBUG_OBJECT (encoder, "flushing encoder");
+
+ gst_x265_enc_flush_frames (x265enc, FALSE);
+ gst_x265_enc_close_encoder (x265enc);
+ gst_x265_enc_dequeue_all_frames (x265enc);
+
+ gst_x265_enc_init_encoder (x265enc);
+
+ return TRUE;
+}
+
+static void
+gst_x265_enc_finalize (GObject * object)
+{
+ GstX265Enc *encoder = GST_X265_ENC (object);
+
+ if (encoder->input_state)
+ gst_video_codec_state_unref (encoder->input_state);
+ encoder->input_state = NULL;
+
+ gst_x265_enc_close_encoder (encoder);
+
+ G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static gint
+gst_x265_enc_gst_to_x265_video_format (GstVideoFormat format, gint * nplanes)
+{
+ switch (format) {
+ case GST_VIDEO_FORMAT_I420:
+ case GST_VIDEO_FORMAT_YV12:
+ if (nplanes)
+ *nplanes = 3;
+ return X265_CSP_I420;
+ case GST_VIDEO_FORMAT_Y444:
+ if (nplanes)
+ *nplanes = 3;
+ return X265_CSP_I444;
+ default:
+ g_return_val_if_reached (GST_VIDEO_FORMAT_UNKNOWN);
+ }
+}
+
+/*
+ * gst_x265_enc_parse_options
+ * @encoder: Encoder to which options are assigned
+ * @str: Option string
+ *
+ * Parse option string and assign to x265 parameters
+ *
+ */
+static gboolean
+gst_x265_enc_parse_options (GstX265Enc * encoder, const gchar * str)
+{
+ GStrv kvpairs;
+ guint npairs, i;
+ gint parse_result = 0, ret = 0;
+ gchar *options = (gchar *) str;
+
+ while (*options == ':')
+ options++;
+
+ kvpairs = g_strsplit (options, ":", 0);
+ npairs = g_strv_length (kvpairs);
+
+ for (i = 0; i < npairs; i++) {
+ GStrv key_val = g_strsplit (kvpairs[i], "=", 2);
+
+ parse_result =
+ x265_param_parse (&encoder->x265param, key_val[0], key_val[1]);
+
+ if (parse_result == X265_PARAM_BAD_NAME) {
+ GST_ERROR_OBJECT (encoder, "Bad name for option %s=%s",
+ key_val[0] ? key_val[0] : "", key_val[1] ? key_val[1] : "");
+ }
+ if (parse_result == X265_PARAM_BAD_VALUE) {
+ GST_ERROR_OBJECT (encoder,
+ "Bad value for option %s=%s (Note: a NULL value for a non-boolean triggers this)",
+ key_val[0] ? key_val[0] : "", key_val[1] ? key_val[1] : "");
+ }
+
+ g_strfreev (key_val);
+
+ if (parse_result)
+ ret++;
+ }
+
+ g_strfreev (kvpairs);
+ return !ret;
+}
+
+/*
+ * gst_x265_enc_init_encoder
+ * @encoder: Encoder which should be initialized.
+ *
+ * Initialize x265 encoder.
+ *
+ */
+static gboolean
+gst_x265_enc_init_encoder (GstX265Enc * encoder)
+{
+ GstVideoInfo *info;
+
+ if (!encoder->input_state) {
+ GST_DEBUG_OBJECT (encoder, "Have no input state yet");
+ return FALSE;
+ }
+
+ info = &encoder->input_state->info;
+
+ /* make sure that the encoder is closed */
+ gst_x265_enc_close_encoder (encoder);
+
+ GST_OBJECT_LOCK (encoder);
+
+ if (x265_param_default_preset (&encoder->x265param,
+ x265_preset_names[encoder->speed_preset - 1],
+ x265_tune_names[encoder->tune]) < 0) {
+ GST_DEBUG_OBJECT (encoder, "preset or tune unrecognized");
+ GST_OBJECT_UNLOCK (encoder);
+ return FALSE;
+ }
+
+ /* set up encoder parameters */
+ encoder->x265param.logLevel = encoder->log_level;
+ encoder->x265param.internalCsp =
+ gst_x265_enc_gst_to_x265_video_format (info->finfo->format, NULL);
+ if (info->fps_d == 0 || info->fps_n == 0) {
+ } else {
+ encoder->x265param.fpsNum = info->fps_n;
+ encoder->x265param.fpsDenom = info->fps_d;
+ }
+ encoder->x265param.sourceWidth = info->width;
+ encoder->x265param.sourceHeight = info->height;
+ if (info->par_d > 0) {
+ encoder->x265param.vui.sarWidth = info->par_n;
+ encoder->x265param.vui.sarHeight = info->par_d;
+ }
+ encoder->x265param.rc.bitrate = encoder->bitrate;
+ encoder->x265param.rc.rateControlMode = X265_RC_ABR;
+
+ /* apply option-string property */
+ if (encoder->option_string_prop && encoder->option_string_prop->len) {
+ GST_DEBUG_OBJECT (encoder, "Applying option-string: %s",
+ encoder->option_string_prop->str);
+ if (gst_x265_enc_parse_options (encoder,
+ encoder->option_string_prop->str) == FALSE) {
+ GST_DEBUG_OBJECT (encoder, "Your option-string contains errors.");
+ GST_OBJECT_UNLOCK (encoder);
+ return FALSE;
+ }
+ }
+
+ encoder->reconfig = FALSE;
+
+ /* good start, will be corrected if needed */
+ encoder->dts_offset = 0;
+
+ GST_OBJECT_UNLOCK (encoder);
+
+ encoder->x265enc = x265_encoder_open (&encoder->x265param);
+ if (!encoder->x265enc) {
+ GST_ELEMENT_ERROR (encoder, STREAM, ENCODE,
+ ("Can not initialize x265 encoder."), (NULL));
+ return FALSE;
+ }
+
+ encoder->push_header = TRUE;
+
+ return TRUE;
+}
+
+/* gst_x265_enc_close_encoder
+ * @encoder: Encoder which should close.
+ *
+ * Close x265 encoder.
+ */
+static void
+gst_x265_enc_close_encoder (GstX265Enc * encoder)
+{
+ if (encoder->x265enc != NULL) {
+ x265_encoder_close (encoder->x265enc);
+ encoder->x265enc = NULL;
+ }
+}
+
+static x265_nal *
+gst_x265_enc_bytestream_to_nal (x265_nal * input)
+{
+ x265_nal *output;
+ int i, j, zeros;
+
+ output = g_malloc (sizeof (x265_nal));
+ output->payload = g_malloc (input->sizeBytes - 4);
+ output->sizeBytes = input->sizeBytes - 4;
+ output->type = input->type;
+
+ zeros = 0;
+ for (i = 4, j = 0; i < input->sizeBytes; (i++, j++)) {
+ if (input->payload[i] == 0x00) {
+ zeros++;
+ } else if (input->payload[i] == 0x03 && zeros == 2) {
+ zeros = 0;
+ j--;
+ output->sizeBytes--;
+ continue;
+ } else {
+ zeros = 0;
+ }
+ output->payload[j] = input->payload[i];
+ }
+
+ return output;
+}
+
+static void
+x265_nal_free (x265_nal * nal)
+{
+ g_free (nal->payload);
+ g_free (nal);
+}
+
+static gboolean
+gst_x265_enc_set_level_tier_and_profile (GstX265Enc * encoder, GstCaps * caps)
+{
+ x265_nal *nal, *vps_nal;
+ guint32 i_nal;
+ int header_return;
+
+ GST_DEBUG_OBJECT (encoder, "set profile, level and tier");
+
+ header_return = x265_encoder_headers (encoder->x265enc, &nal, &i_nal);
+ if (header_return < 0) {
+ GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Encode x265 header failed."),
+ ("x265_encoder_headers return code=%d", header_return));
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT (encoder, "%d nal units in header", i_nal);
+
+ g_assert (nal[0].type == NAL_UNIT_VPS);
+ vps_nal = gst_x265_enc_bytestream_to_nal (&nal[0]);
+
+ GST_MEMDUMP ("VPS", vps_nal->payload, vps_nal->sizeBytes);
+
+ if (!gst_codec_utils_h265_caps_set_level_tier_and_profile (caps,
+ vps_nal->payload + 6, vps_nal->sizeBytes - 6)) {
+ GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Encode x265 failed."),
+ ("Failed to find correct level, tier or profile in VPS"));
+ return FALSE;
+ }
+
+ x265_nal_free (vps_nal);
+
+ return TRUE;
+}
+
+static GstBuffer *
+gst_x265_enc_get_header_buffer (GstX265Enc * encoder)
+{
+ x265_nal *nal;
+ guint32 i_nal, i, offset, vps_idx;
+ int header_return;
+ GstBuffer *buf;
+
+ header_return = x265_encoder_headers (encoder->x265enc, &nal, &i_nal);
+ if (header_return < 0) {
+ GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Encode x265 header failed."),
+ ("x265_encoder_headers return code=%d", header_return));
+ return FALSE;
+ }
+
+ GST_DEBUG_OBJECT (encoder, "%d nal units in header", i_nal);
+
+ /* x265 returns also non header nal units with the call x265_encoder_headers.
+ * The usefull headers are sequential (VPS, SPS and PPS), so we look for this
+ * nal units and only copy these tree nal units as the header */
+
+ for (vps_idx = 0; vps_idx < i_nal; vps_idx++) {
+ if (nal[vps_idx].type == 32) {
+ break;
+ }
+ }
+
+ if (vps_idx >= i_nal - 3) {
+ GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Encode x265 header failed."),
+ ("x265_encoder_headers did not return VPS, SPS and PPS"));
+ return FALSE;
+ }
+
+ offset = 0;
+ buf =
+ gst_buffer_new_allocate (NULL,
+ nal[vps_idx].sizeBytes + nal[vps_idx + 1].sizeBytes + nal[vps_idx +
+ 2].sizeBytes, NULL);
+ for (i = 0; i < i_nal; i++) {
+ gst_buffer_fill (buf, offset, nal[i + vps_idx].payload,
+ nal[i + vps_idx].sizeBytes);
+ offset += nal[i + vps_idx].sizeBytes;
+ }
+
+ return buf;
+}
+
+/* gst_x265_enc_set_src_caps
+ * Returns: TRUE on success.
+ */
+static gboolean
+gst_x265_enc_set_src_caps (GstX265Enc * encoder, GstCaps * caps)
+{
+ GstCaps *outcaps;
+ GstStructure *structure;
+ GstVideoCodecState *state;
+ GstTagList *tags;
+
+ outcaps = gst_caps_new_empty_simple ("video/x-h265");
+ structure = gst_caps_get_structure (outcaps, 0);
+
+ gst_structure_set (structure, "stream-format", G_TYPE_STRING, "byte-stream",
+ NULL);
+ gst_structure_set (structure, "alignment", G_TYPE_STRING, "au", NULL);
+
+ if (!gst_x265_enc_set_level_tier_and_profile (encoder, outcaps)) {
+ gst_caps_unref (outcaps);
+ return FALSE;
+ }
+
+ state = gst_video_encoder_set_output_state (GST_VIDEO_ENCODER (encoder),
+ outcaps, encoder->input_state);
+ GST_DEBUG_OBJECT (encoder, "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, "x265",
+ GST_TAG_ENCODER_VERSION, x265_version_str, NULL);
+ gst_video_encoder_merge_tags (GST_VIDEO_ENCODER (encoder), tags,
+ GST_TAG_MERGE_REPLACE);
+ gst_tag_list_unref (tags);
+
+ return TRUE;
+}
+
+static void
+gst_x265_enc_set_latency (GstX265Enc * encoder)
+{
+ GstVideoInfo *info = &encoder->input_state->info;
+
+ if (info->fps_n) {
+ GstClockTime latency;
+ gint max_delayed_frames;
+
+ // FIXME get a real value from the encoder, this is currently not exposed
+ max_delayed_frames = 5;
+ latency = gst_util_uint64_scale_ceil (GST_SECOND * info->fps_d,
+ max_delayed_frames, info->fps_n);
+
+ GST_INFO_OBJECT (encoder,
+ "Updating latency to %" GST_TIME_FORMAT " (%d frames)",
+ GST_TIME_ARGS (latency), max_delayed_frames);
+
+ gst_video_encoder_set_latency (GST_VIDEO_ENCODER (encoder), latency,
+ latency);
+ } else {
+ /* We can't do live as we don't know our latency */
+ gst_video_encoder_set_latency (GST_VIDEO_ENCODER (encoder),
+ 0, GST_CLOCK_TIME_NONE);
+ }
+}
+
+static gboolean
+gst_x265_enc_set_format (GstVideoEncoder * video_enc,
+ GstVideoCodecState * state)
+{
+ GstX265Enc *encoder = GST_X265_ENC (video_enc);
+ GstVideoInfo *info = &state->info;
+ gboolean level_ok = TRUE;
+
+ /* If the encoder is initialized, do not reinitialize it again if not
+ * necessary */
+ if (encoder->x265enc) {
+ GstVideoInfo *old = &encoder->input_state->info;
+
+ if (info->finfo->format == old->finfo->format
+ && info->width == old->width && info->height == old->height
+ && info->fps_n == old->fps_n && info->fps_d == old->fps_d
+ && info->par_n == old->par_n && info->par_d == old->par_d) {
+ gst_video_codec_state_unref (encoder->input_state);
+ encoder->input_state = gst_video_codec_state_ref (state);
+ return TRUE;
+ }
+
+ /* clear out pending frames */
+ gst_x265_enc_flush_frames (encoder, TRUE);
+ }
+
+ if (encoder->input_state)
+ gst_video_codec_state_unref (encoder->input_state);
+ encoder->input_state = gst_video_codec_state_ref (state);
+
+ if (!level_ok)
+ return FALSE;
+
+ if (!gst_x265_enc_init_encoder (encoder))
+ return FALSE;
+
+ if (!gst_x265_enc_set_src_caps (encoder, state->caps)) {
+ gst_x265_enc_close_encoder (encoder);
+ return FALSE;
+ }
+
+ gst_x265_enc_set_latency (encoder);
+
+ return TRUE;
+}
+
+static GstFlowReturn
+gst_x265_enc_finish (GstVideoEncoder * encoder)
+{
+ GST_DEBUG_OBJECT (encoder, "finish encoder");
+
+ gst_x265_enc_flush_frames (GST_X265_ENC (encoder), TRUE);
+ gst_x265_enc_flush_frames (GST_X265_ENC (encoder), TRUE);
+ return GST_FLOW_OK;
+}
+
+static gboolean
+gst_x265_enc_propose_allocation (GstVideoEncoder * encoder, GstQuery * query)
+{
+ gst_query_add_allocation_meta (query, GST_VIDEO_META_API_TYPE, NULL);
+
+ return GST_VIDEO_ENCODER_CLASS (parent_class)->propose_allocation (encoder,
+ query);
+}
+
+/* chain function
+ * this function does the actual processing
+ */
+static GstFlowReturn
+gst_x265_enc_handle_frame (GstVideoEncoder * video_enc,
+ GstVideoCodecFrame * frame)
+{
+ GstX265Enc *encoder = GST_X265_ENC (video_enc);
+ GstVideoInfo *info = &encoder->input_state->info;
+ GstFlowReturn ret;
+ x265_picture pic_in;
+ guint32 i_nal, i;
+ FrameData *fdata;
+ gint nplanes = 0;
+
+ if (G_UNLIKELY (encoder->x265enc == NULL))
+ goto not_inited;
+
+ /* set up input picture */
+ x265_picture_init (&encoder->x265param, &pic_in);
+
+ fdata = gst_x265_enc_queue_frame (encoder, frame, info);
+ if (!fdata)
+ goto invalid_frame;
+
+ pic_in.colorSpace =
+ gst_x265_enc_gst_to_x265_video_format (info->finfo->format, &nplanes);
+ for (i = 0; i < nplanes; i++) {
+ pic_in.planes[i] = GST_VIDEO_FRAME_PLANE_DATA (&fdata->vframe, i);
+ pic_in.stride[i] = GST_VIDEO_FRAME_COMP_STRIDE (&fdata->vframe, i);
+ }
+
+ pic_in.sliceType = X265_TYPE_AUTO;
+ pic_in.pts = frame->pts;
+ pic_in.dts = frame->dts;
+ pic_in.bitDepth = 8;
+ pic_in.userData = GINT_TO_POINTER (frame->system_frame_number);
+
+ ret = gst_x265_enc_encode_frame (encoder, &pic_in, frame, &i_nal, TRUE);
+
+ /* input buffer is released later on */
+ return ret;
+
+/* ERRORS */
+not_inited:
+ {
+ GST_WARNING_OBJECT (encoder, "Got buffer before set_caps was called");
+ return GST_FLOW_NOT_NEGOTIATED;
+ }
+invalid_frame:
+ {
+ GST_ERROR_OBJECT (encoder, "Failed to map frame");
+ return GST_FLOW_ERROR;
+ }
+}
+
+static GstFlowReturn
+gst_x265_enc_encode_frame (GstX265Enc * encoder, x265_picture * pic_in,
+ GstVideoCodecFrame * input_frame, guint32 * i_nal, gboolean send)
+{
+ GstVideoCodecFrame *frame = NULL;
+ GstBuffer *out_buf = NULL;
+ x265_picture pic_out;
+ x265_nal *nal;
+ int i_size, i, offset;
+ int encoder_return;
+ GstFlowReturn ret = GST_FLOW_OK;
+ gboolean update_latency = FALSE;
+
+ if (G_UNLIKELY (encoder->x265enc == NULL)) {
+ if (input_frame)
+ gst_video_codec_frame_unref (input_frame);
+ return GST_FLOW_NOT_NEGOTIATED;
+ }
+
+ GST_OBJECT_LOCK (encoder);
+ if (encoder->reconfig) {
+ // x265_encoder_reconfig is not yet implemented thus we shut down and re-create encoder
+ gst_x265_enc_init_encoder (encoder);
+ update_latency = TRUE;
+ }
+
+ if (pic_in && input_frame) {
+ if (GST_VIDEO_CODEC_FRAME_IS_FORCE_KEYFRAME (input_frame)) {
+ GST_INFO_OBJECT (encoder, "Forcing key frame");
+ pic_in->sliceType = X265_TYPE_IDR;
+ }
+ }
+ GST_OBJECT_UNLOCK (encoder);
+
+ if (G_UNLIKELY (update_latency))
+ gst_x265_enc_set_latency (encoder);
+
+ encoder_return = x265_encoder_encode (encoder->x265enc,
+ &nal, i_nal, pic_in, &pic_out);
+
+ GST_DEBUG_OBJECT (encoder, "encoder result (%d) with %u nal units",
+ encoder_return, *i_nal);
+
+ if (encoder_return < 0) {
+ GST_ELEMENT_ERROR (encoder, STREAM, ENCODE, ("Encode x265 frame failed."),
+ ("x265_encoder_encode return code=%d", encoder_return));
+ ret = GST_FLOW_ERROR;
+ /* Make sure we finish this frame */
+ frame = input_frame;
+ goto out;
+ }
+
+ /* Input frame is now queued */
+ if (input_frame)
+ gst_video_codec_frame_unref (input_frame);
+
+ if (!*i_nal) {
+ ret = GST_FLOW_OK;
+ GST_LOG_OBJECT (encoder, "no output yet");
+ goto out;
+ }
+
+ frame = gst_video_encoder_get_frame (GST_VIDEO_ENCODER (encoder),
+ GPOINTER_TO_INT (pic_out.userData));
+ g_assert (frame || !send);
+
+ GST_DEBUG_OBJECT (encoder,
+ "output picture ready POC=%d system=%d frame found %d", pic_out.poc,
+ GPOINTER_TO_INT (pic_out.userData), frame != NULL);
+
+ if (!send || !frame) {
+ GST_LOG_OBJECT (encoder, "not sending (%d) or frame not found (%d)", send,
+ frame != NULL);
+ ret = GST_FLOW_OK;
+ goto out;
+ }
+
+ i_size = 0;
+ offset = 0;
+ for (i = 0; i < *i_nal; i++)
+ i_size += nal[i].sizeBytes;
+ out_buf = gst_buffer_new_allocate (NULL, i_size, NULL);
+ for (i = 0; i < *i_nal; i++) {
+ gst_buffer_fill (out_buf, offset, nal[i].payload, nal[i].sizeBytes);
+ offset += nal[i].sizeBytes;
+ }
+
+ frame->output_buffer = out_buf;
+
+ if (encoder->push_header) {
+ GstBuffer *header;
+
+ header = gst_x265_enc_get_header_buffer (encoder);
+ frame->output_buffer = gst_buffer_append (header, frame->output_buffer);
+ encoder->push_header = FALSE;
+ }
+
+ GST_LOG_OBJECT (encoder,
+ "output: dts %" G_GINT64_FORMAT " pts %" G_GINT64_FORMAT,
+ (gint64) pic_out.dts, (gint64) pic_out.pts);
+
+ frame->dts = pic_out.dts + encoder->dts_offset;
+
+out:
+ if (frame) {
+ gst_x265_enc_dequeue_frame (encoder, frame);
+ ret = gst_video_encoder_finish_frame (GST_VIDEO_ENCODER (encoder), frame);
+ }
+
+ return ret;
+}
+
+static void
+gst_x265_enc_flush_frames (GstX265Enc * encoder, gboolean send)
+{
+ GstFlowReturn flow_ret;
+ guint32 i_nal;
+
+ /* first send the remaining frames */
+ if (encoder->x265enc)
+ do {
+ flow_ret = gst_x265_enc_encode_frame (encoder, NULL, NULL, &i_nal, send);
+ } while (flow_ret == GST_FLOW_OK && i_nal > 0);
+}
+
+static void
+gst_x265_enc_reconfig (GstX265Enc * encoder)
+{
+ encoder->x265param.rc.bitrate = encoder->bitrate;
+ encoder->reconfig = TRUE;
+}
+
+static void
+gst_x265_enc_set_property (GObject * object, guint prop_id,
+ const GValue * value, GParamSpec * pspec)
+{
+ GstX265Enc *encoder;
+ GstState state;
+
+ encoder = GST_X265_ENC (object);
+
+ GST_OBJECT_LOCK (encoder);
+
+ state = GST_STATE (encoder);
+ if ((state != GST_STATE_READY && state != GST_STATE_NULL) &&
+ !(pspec->flags & GST_PARAM_MUTABLE_PLAYING))
+ goto wrong_state;
+
+ switch (prop_id) {
+ case PROP_BITRATE:
+ encoder->bitrate = g_value_get_uint (value);
+ break;
+ case PROP_OPTION_STRING:
+ g_string_assign (encoder->option_string_prop, g_value_get_string (value));
+ break;
+ case PROP_X265_LOG_LEVEL:
+ encoder->log_level = g_value_get_enum (value);
+ break;
+ case PROP_SPEED_PRESET:
+ encoder->speed_preset = g_value_get_enum (value);
+ break;
+ case PROP_TUNE:
+ encoder->tune = g_value_get_enum (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+
+ gst_x265_enc_reconfig (encoder);
+ GST_OBJECT_UNLOCK (encoder);
+ return;
+
+wrong_state:
+ {
+ GST_WARNING_OBJECT (encoder, "setting property in wrong state");
+ GST_OBJECT_UNLOCK (encoder);
+ }
+}
+
+static void
+gst_x265_enc_get_property (GObject * object, guint prop_id,
+ GValue * value, GParamSpec * pspec)
+{
+ GstX265Enc *encoder;
+
+ encoder = GST_X265_ENC (object);
+
+ GST_OBJECT_LOCK (encoder);
+ switch (prop_id) {
+ case PROP_BITRATE:
+ g_value_set_uint (value, encoder->bitrate);
+ break;
+ case PROP_OPTION_STRING:
+ g_value_set_string (value, encoder->option_string_prop->str);
+ break;
+ case PROP_X265_LOG_LEVEL:
+ g_value_set_enum (value, encoder->log_level);
+ break;
+ case PROP_SPEED_PRESET:
+ g_value_set_enum (value, encoder->speed_preset);
+ break;
+ case PROP_TUNE:
+ g_value_set_enum (value, encoder->tune);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+ GST_OBJECT_UNLOCK (encoder);
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+ GST_DEBUG_CATEGORY_INIT (x265_enc_debug, "x265enc", 0,
+ "h265 encoding element");
+
+ GST_INFO ("x265 build: %u", X265_BUILD);
+
+ return gst_element_register (plugin, "x265enc",
+ GST_RANK_PRIMARY, GST_TYPE_X265_ENC);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ x265,
+ "x265-based H265 plugins",
+ plugin_init, VERSION, "GPL", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
diff --git a/ext/x265/gstx265enc.h b/ext/x265/gstx265enc.h
new file mode 100644
index 000000000..a94784e26
--- /dev/null
+++ b/ext/x265/gstx265enc.h
@@ -0,0 +1,87 @@
+/* GStreamer H265 encoder plugin
+ * Copyright (C) 2005 Michal Benes <michal.benes@itonis.tv>
+ * Copyright (C) 2005 Josef Zlomek <josef.zlomek@itonis.tv>
+ * Copyright (C) 2014 Thijs Vermeir <thijs.vermeir@barco.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __GST_X265_ENC_H__
+#define __GST_X265_ENC_H__
+
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/video/gstvideoencoder.h>
+#include "_stdint.h"
+#include <x265.h>
+
+G_BEGIN_DECLS
+#define GST_TYPE_X265_ENC \
+ (gst_x265_enc_get_type())
+#define GST_X265_ENC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_X265_ENC,GstX265Enc))
+#define GST_X265_ENC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_X265_ENC,GstX265EncClass))
+#define GST_IS_X265_ENC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_X265_ENC))
+#define GST_IS_X265_ENC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_X265_ENC))
+typedef struct _GstX265Enc GstX265Enc;
+typedef struct _GstX265EncClass GstX265EncClass;
+
+struct _GstX265Enc
+{
+ GstVideoEncoder element;
+
+ /*< private > */
+ x265_encoder *x265enc;
+ x265_param x265param;
+ GstClockTime dts_offset;
+ gboolean push_header;
+
+ /* List of frame/buffer mapping structs for
+ * pending frames */
+ GList *pending_frames;
+
+ /* properties */
+ guint bitrate;
+ gint log_level;
+ gint tune;
+ gint speed_preset;
+ GString *option_string_prop; /* option-string property */
+ /*GString *option_string; *//* used by set prop */
+
+ /* input description */
+ GstVideoCodecState *input_state;
+
+ /* configuration changed while playing */
+ gboolean reconfig;
+
+ /* from the downstream caps */
+ const gchar *peer_profile;
+ gboolean peer_intra_profile;
+ /*const x265_level_t *peer_level; */
+};
+
+struct _GstX265EncClass
+{
+ GstVideoEncoderClass parent_class;
+};
+
+GType gst_x265_enc_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_X265_ENC_H__ */