--- /dev/null
+/*****************************************************************************
+ * x265.c: HEVC/H.265 video encoder
+ *****************************************************************************
+ * Copyright (C) 2013 Rafaël Carré
+ *
+ * Authors: Rafaël Carré <funman@videolanorg>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+/*****************************************************************************
+ * Preamble
+ *****************************************************************************/
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_plugin.h>
+#include <vlc_threads.h>
+#include <vlc_sout.h>
+#include <vlc_codec.h>
+
+#include <x265.h>
+
+/*****************************************************************************
+ * Module descriptor
+ *****************************************************************************/
+static int Open (vlc_object_t *);
+static void Close(vlc_object_t *);
+
+vlc_module_begin ()
+ set_description(N_("H.265/HEVC encoder (x265)"))
+ set_capability("encoder", 200)
+ set_callbacks(Open, Close)
+ set_category(CAT_INPUT)
+ set_subcategory(SUBCAT_INPUT_VCODEC)
+vlc_module_end ()
+
+struct encoder_sys_t
+{
+ x265_t *h;
+ x265_param_t param;
+
+ bool write_headers;
+
+ mtime_t i_initial_delay;
+
+ mtime_t dts;
+ mtime_t initial_date;
+#ifndef NDEBUG
+ mtime_t start;
+#endif
+};
+
+static block_t *Encode(encoder_t *p_enc, picture_t *p_pict)
+{
+ encoder_sys_t *p_sys = p_enc->p_sys;
+ x265_picture_t pic;
+
+ if (likely(p_pict)) {
+ if (unlikely(p_sys->initial_date == 0)) {
+ p_sys->initial_date = p_pict->date;
+#ifndef NDEBUG
+ p_sys->start = mdate();
+#endif
+ }
+
+ for (int i = 0; i < p_pict->i_planes; i++) {
+ pic.planes[i] = p_pict->p[i].p_pixels;
+ pic.stride[i] = p_pict->p[i].i_pitch;
+ }
+ }
+
+ x265_nal_t *nal;
+ int i_nal = 0;
+ x265_encoder_encode(p_sys->h, &nal, &i_nal,
+ likely(p_pict) ? &pic : NULL, &pic);
+
+ if (!i_nal)
+ return NULL;
+
+ int i_out = 0;
+ for (int i = 0; i < i_nal; i++)
+ i_out += nal[i].i_payload;
+
+ int i_extra = 0;
+ if (unlikely(p_sys->write_headers)) {
+ i_extra = p_enc->fmt_out.i_extra;
+ p_sys->write_headers = false;
+ }
+
+ block_t *p_block = block_Alloc(i_extra + i_out);
+ if (!p_block)
+ return NULL;
+
+ if (unlikely(i_extra))
+ memcpy(p_block->p_buffer, p_enc->fmt_out.p_extra, i_extra);
+
+ /* all payloads are sequentially laid out in memory */
+ memcpy(p_block->p_buffer + i_extra, nal[0].p_payload, i_out);
+
+ /* This isn't really valid for streams with B-frames */
+ p_block->i_length = CLOCK_FREQ *
+ p_enc->fmt_in.video.i_frame_rate_base /
+ p_enc->fmt_in.video.i_frame_rate;
+
+ p_block->i_pts = p_sys->initial_date + pic.poc * p_block->i_length;
+ p_block->i_dts = p_sys->initial_date + p_sys->dts++ * p_block->i_length;
+
+#ifndef NDEBUG
+ msg_Dbg(p_enc, "%zu bytes (frame %"PRId64", %.2ffps)", p_block->i_buffer,
+ p_sys->dts, (float)p_sys->dts * CLOCK_FREQ / (mdate() - p_sys->start));
+#endif
+
+ return p_block;
+}
+
+static int Open (vlc_object_t *p_this)
+{
+ encoder_t *p_enc = (encoder_t *)p_this;
+ encoder_sys_t *p_sys;
+
+ if (p_enc->fmt_out.i_codec != VLC_CODEC_HEVC && !p_enc->b_force)
+ return VLC_EGENERIC;
+
+ p_enc->fmt_out.i_cat = VIDEO_ES;
+ p_enc->fmt_out.i_codec = VLC_CODEC_HEVC;
+ p_enc->p_sys = p_sys = malloc(sizeof(encoder_sys_t));
+ if (!p_sys)
+ return VLC_ENOMEM;
+
+ p_enc->fmt_in.i_codec = VLC_CODEC_I420;
+
+ x265_param_t *param = &p_sys->param;
+ x265_param_default(param);
+
+ param->frameNumThreads = vlc_GetCPUCount();
+ param->bEnableWavefront = 0; // buggy in x265, use frame threading for now
+ param->maxCUSize = 16; /* use smaller macroblock */
+
+ param->frameRate = p_enc->fmt_in.video.i_frame_rate /
+ p_enc->fmt_in.video.i_frame_rate_base;
+ param->sourceWidth = p_enc->fmt_in.video.i_visible_width;
+ param->sourceHeight = p_enc->fmt_in.video.i_visible_height;
+
+ if (param->sourceWidth & (param->maxCUSize - 1)) {
+ msg_Err(p_enc, "Width (%d) must be a multiple of %d",
+ param->sourceWidth, param->maxCUSize);
+ free(p_sys);
+ return VLC_EGENERIC;
+ }
+ if (param->sourceHeight & 7) {
+ msg_Err(p_enc, "Height (%d) must be a multiple of 8", param->sourceHeight);
+ free(p_sys);
+ return VLC_EGENERIC;
+ }
+
+ if (p_enc->fmt_out.i_bitrate > 0) {
+ param->rc.bitrate = p_enc->fmt_out.i_bitrate / 1000;
+ param->rc.rateControlMode = X265_RC_ABR;
+ }
+
+ p_sys->h = x265_encoder_open(param);
+ if (p_sys->h == NULL) {
+ msg_Err(p_enc, "cannot open x265 encoder");
+ free(p_sys);
+ return VLC_EGENERIC;
+ }
+
+ x265_nal_t *nal;
+ int i_nal;
+ if (x265_encoder_headers(p_sys->h, &nal, &i_nal)) {
+ msg_Err(p_enc, "cannot get x265 headers");
+ Close(VLC_OBJECT(p_enc));
+ return VLC_EGENERIC;
+ }
+
+ size_t i_extra = 0;
+ for (int i = 0; i < i_nal; i++)
+ i_extra += nal[i].i_payload;
+
+ p_enc->fmt_out.i_extra = i_extra;
+
+ uint8_t *p_extra = p_enc->fmt_out.p_extra = malloc(i_extra);
+ if (!p_extra) {
+ Close(VLC_OBJECT(p_enc));
+ return VLC_ENOMEM;
+ }
+
+ for (int i = 0; i < i_nal; i++) {
+ memcpy(p_extra, nal[i].p_payload, nal[i].i_payload);
+ p_extra += nal[i].i_payload;
+ }
+
+ p_sys->dts = 0;
+ p_sys->initial_date = 0;
+ p_sys->i_initial_delay = 0;
+ p_sys->write_headers = true;
+
+ p_enc->pf_encode_video = Encode;
+ p_enc->pf_encode_audio = NULL;
+
+ return VLC_SUCCESS;
+}
+
+static void Close(vlc_object_t *p_this)
+{
+ encoder_t *p_enc = (encoder_t *)p_this;
+ encoder_sys_t *p_sys = p_enc->p_sys;
+
+ x265_encoder_close(p_sys->h, NULL);
+
+ free(p_sys);
+}