From f1d6824164a1b33c3befc8ad7e16aa94f866ee04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C3=ABl=20Carr=C3=A9?= Date: Tue, 17 Sep 2013 18:25:55 +0200 Subject: [PATCH] x265 encoder --- NEWS | 1 + configure.ac | 18 +++ modules/LIST | 1 + modules/codec/Makefile.am | 8 ++ modules/codec/x265.c | 227 ++++++++++++++++++++++++++++++++++++++ po/POTFILES.in | 1 + 6 files changed, 256 insertions(+) create mode 100644 modules/codec/x265.c diff --git a/NEWS b/NEWS index 0d9f313edd..9c2f51f3c1 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ Decoders: Encoder: * Support for MPEG-2 encoding using x262 + * Support for HEVC/H.265 encoding using x265 Streaming: * WebM streaming, including live sources, compatible with all major browsers diff --git a/configure.ac b/configure.ac index 5075f071a5..6a058a0660 100644 --- a/configure.ac +++ b/configure.ac @@ -2755,6 +2755,24 @@ AC_ARG_WITH(x262-tree, fi fi +dnl x265 encoder +AC_ARG_ENABLE(x265, + AS_HELP_STRING([--enable-x265],[H265 / HEVC encoding support with libx265 (default enabled)])) +AS_IF( [test "${enable_x265}" != "no"],[ + AC_CHECK_HEADER([x265.h], [ + AC_CHECK_LIB([x265],[x265_encoder_open], [ + VLC_ADD_PLUGIN([x265]) + VLC_ADD_LIBS([x265], [-lx265 -lstdc++ -lm -lpthread]) + ], [ + AC_MSG_ERROR([x265 library not found]) + ], [-lstdc++ -lm -lpthread]) + ], [ + AS_IF([test "${enable_x265}" = "yes"], [ + AC_MSG_ERROR([x265.h not found]) + ]) + ]) +]) + dnl dnl H264 encoder plugin (10-bit lib264) diff --git a/modules/LIST b/modules/LIST index 53da53e46c..45a7ad0c55 100644 --- a/modules/LIST +++ b/modules/LIST @@ -395,6 +395,7 @@ $Id$ * x262: MPEG-2 video encoder using x262 * x26410b: H264 10 bit video encoder using x264 * x264: H264 video encoder using x264 + * x265: H265 video encoder using x265 * xa: XA demuxer * xcb_apps: List the application windows using XCB * xcb_glx: a opengl provider using XCB OpenGL diff --git a/modules/codec/Makefile.am b/modules/codec/Makefile.am index b11d7de38a..7616909f3a 100644 --- a/modules/codec/Makefile.am +++ b/modules/codec/Makefile.am @@ -342,6 +342,14 @@ endif ### X26x encoders ### +libx265_plugin_la_SOURCES = codec/x265.c +libx265_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) +libx265_plugin_la_CFLAGS = $(AM_CFLAGS) $(CFLAGS_x265) +libx265_plugin_la_LDFLAGS = $(AM_LDFLAGS) $(LDFLAGS_x265) -rpath '$(codecdir)' +libx265_plugin_la_LIBADD = $(LIBS_x265) +EXTRA_LTLIBRARIES += libx265_plugin.la +codec_LTLIBRARIES += $(LTLIBx265) + libx262_plugin_la_SOURCES = codec/x264.c libx262_plugin_la_CPPFLAGS = $(AM_CPPFLAGS) -DMODULE_NAME_IS_x262 libx262_plugin_la_CFLAGS = $(AM_CFLAGS) $(CFLAGS_x262) diff --git a/modules/codec/x265.c b/modules/codec/x265.c new file mode 100644 index 0000000000..8a8305f8ed --- /dev/null +++ b/modules/codec/x265.c @@ -0,0 +1,227 @@ +/***************************************************************************** + * x265.c: HEVC/H.265 video encoder + ***************************************************************************** + * Copyright (C) 2013 Rafaël Carré + * + * Authors: Rafaël Carré + * + * 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 +#include +#include +#include +#include + +#include + +/***************************************************************************** + * 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); +} diff --git a/po/POTFILES.in b/po/POTFILES.in index b4fb994074..13f831c18b 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -406,6 +406,7 @@ modules/codec/uleaddvaudio.c modules/codec/vorbis.c modules/codec/wmafixed/wma.c modules/codec/x264.c +modules/codec/x265.c modules/codec/xwd.c modules/codec/zvbi.c modules/control/dbus/dbus.c