summaryrefslogtreecommitdiffstats
path: root/libhb
diff options
context:
space:
mode:
authorRodeo <[email protected]>2014-02-10 17:38:41 +0000
committerRodeo <[email protected]>2014-02-10 17:38:41 +0000
commite1e76b53cd11e7706e884d802c5985633b849e4f (patch)
tree64630e6611b46ee27a7ba8804f96a8fce1524ab2 /libhb
parente22ffd488a656f6534ce93f85dcfdb8fdb7976bd (diff)
Initial x265 integration. Patch by Zhang Zhiqiang. Thanks!
Build with --enable-x265 (requires CMake). Use via HandBrakeCLI with -a none -e x265 -f raw Only raw HEVC output is supported for now (no audio or subtitles). git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@6023 b64f7644-9d1e-0410-96f1-a4d463321fa5
Diffstat (limited to 'libhb')
-rw-r--r--libhb/common.c32
-rw-r--r--libhb/common.h8
-rw-r--r--libhb/encx265.c295
-rw-r--r--libhb/hb.c3
-rw-r--r--libhb/hb_dict.c5
-rw-r--r--libhb/internal.h1
-rw-r--r--libhb/module.defs10
-rw-r--r--libhb/muxcommon.c4
-rw-r--r--libhb/work.c5
9 files changed, 362 insertions, 1 deletions
diff --git a/libhb/common.c b/libhb/common.c
index 720ff4976..aba04669b 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -21,6 +21,10 @@
#include "qsv_common.h"
#endif
+#ifdef USE_X265
+#include "x265.h"
+#endif
+
#ifdef SYS_MINGW
#include <windows.h>
#endif
@@ -35,6 +39,7 @@ enum
{
HB_GID_NONE = -1, // encoders must NEVER use it
HB_GID_VCODEC_H264,
+ HB_GID_VCODEC_H265,
HB_GID_VCODEC_MPEG2,
HB_GID_VCODEC_MPEG4,
HB_GID_VCODEC_THEORA,
@@ -52,6 +57,7 @@ enum
HB_GID_ACODEC_VORBIS,
HB_GID_MUX_MKV,
HB_GID_MUX_MP4,
+ HB_GID_MUX_RAW,
};
typedef struct
@@ -201,6 +207,7 @@ hb_encoder_internal_t hb_video_encoders[] =
// actual encoders
{ { "H.264 (x264)", "x264", HB_VCODEC_X264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264, },
{ { "H.264 (Intel QSV)", "qsv_h264", HB_VCODEC_QSV_H264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264, },
+ { { "H.265 (x265)", "x265", HB_VCODEC_X265, HB_MUX_RAW, }, NULL, 1, HB_GID_VCODEC_H265, },
{ { "MPEG-4", "mpeg4", HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG4, },
{ { "MPEG-2", "mpeg2", HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG2, },
{ { "Theora", "theora", HB_VCODEC_THEORA, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_THEORA, },
@@ -220,6 +227,9 @@ static int hb_video_encoder_is_enabled(int encoder)
case HB_VCODEC_THEORA:
case HB_VCODEC_FFMPEG_MPEG4:
case HB_VCODEC_FFMPEG_MPEG2:
+#ifdef USE_X265
+ case HB_VCODEC_X265:
+#endif
return 1;
default:
@@ -323,6 +333,7 @@ hb_container_internal_t hb_containers[] =
{ { "MPEG-4 (mp4v2)", "mp4v2", "mp4", HB_MUX_MP4V2, }, NULL, 1, HB_GID_MUX_MP4, },
{ { "Matroska (avformat)", "av_mkv", "mkv", HB_MUX_AV_MKV, }, NULL, 1, HB_GID_MUX_MKV, },
{ { "Matroska (libmkv)", "libmkv", "mkv", HB_MUX_LIBMKV, }, NULL, 1, HB_GID_MUX_MKV, },
+ { { "Raw", "raw", "raw", HB_MUX_RAW, }, NULL, 1, HB_GID_MUX_RAW, },
};
int hb_containers_count = sizeof(hb_containers) / sizeof(hb_containers[0]);
static int hb_container_is_enabled(int format)
@@ -339,6 +350,9 @@ static int hb_container_is_enabled(int format)
case HB_MUX_AV_MP4:
case HB_MUX_AV_MKV:
#endif
+#ifdef USE_X265
+ case HB_MUX_RAW:
+#endif
return 1;
default:
@@ -1108,6 +1122,9 @@ void hb_video_quality_get_limits(uint32_t codec, float *low, float *high,
switch (codec)
{
case HB_VCODEC_X264:
+#ifdef USE_X265
+ case HB_VCODEC_X265:
+#endif
*direction = 1;
*granularity = 0.1;
*low = 0.;
@@ -1137,6 +1154,9 @@ const char* hb_video_quality_get_name(uint32_t codec)
switch (codec)
{
case HB_VCODEC_X264:
+#ifdef USE_X265
+ case HB_VCODEC_X265:
+#endif
return "RF";
default:
@@ -1156,6 +1176,10 @@ const char* const* hb_video_encoder_get_presets(int encoder)
return hb_qsv_preset_get_names();
#endif
+#ifdef USE_X265
+ case HB_VCODEC_X265:
+ return x265_preset_names;
+#endif
default:
return NULL;
}
@@ -1168,6 +1192,10 @@ const char* const* hb_video_encoder_get_tunes(int encoder)
case HB_VCODEC_X264:
return x264_tune_names;
+#ifdef USE_X265
+ case HB_VCODEC_X265:
+ return x265_tune_names;
+#endif
default:
return NULL;
}
@@ -1181,6 +1209,10 @@ const char* const* hb_video_encoder_get_profiles(int encoder)
case HB_VCODEC_QSV_H264:
return hb_h264_profile_names;
+#ifdef USE_X265
+ case HB_VCODEC_X265:
+ return x265_profile_names;
+#endif
default:
return NULL;
}
diff --git a/libhb/common.h b/libhb/common.h
index eece75e55..db9440f4b 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -439,6 +439,7 @@ struct hb_job_s
#define HB_VCODEC_MASK 0x0000FFF
#define HB_VCODEC_X264 0x0000001
#define HB_VCODEC_THEORA 0x0000002
+#define HB_VCODEC_X265 0x0000004
#define HB_VCODEC_FFMPEG_MPEG4 0x0000010
#define HB_VCODEC_FFMPEG_MPEG2 0x0000020
#define HB_VCODEC_FFMPEG_MASK 0x00000F0
@@ -508,6 +509,7 @@ struct hb_job_s
#define HB_MUX_AV_MKV 0x200000
#define HB_MUX_MASK_MKV 0x300000
#define HB_MUX_MASK_AV 0x220000
+#define HB_MUX_RAW 0x001000
/* default muxer for each container */
#define HB_MUX_MP4 HB_MUX_AV_MP4
#define HB_MUX_MKV HB_MUX_AV_MKV
@@ -1077,6 +1079,7 @@ extern hb_work_object_t hb_encavcodec;
extern hb_work_object_t hb_encqsv;
extern hb_work_object_t hb_encx264;
extern hb_work_object_t hb_enctheora;
+extern hb_work_object_t hb_encx265;
extern hb_work_object_t hb_deca52;
extern hb_work_object_t hb_decdca;
extern hb_work_object_t hb_decavcodeca;
@@ -1218,4 +1221,9 @@ const char * const * hb_h264_levels();
// x264 option name/synonym helper
const char * hb_x264_encopt_name( const char * name );
+#ifdef USE_X265
+// x265 option name/synonym helper
+const char * hb_x265_encopt_name( const char * name );
+#endif
+
#endif
diff --git a/libhb/encx265.c b/libhb/encx265.c
new file mode 100644
index 000000000..afb6ef9e2
--- /dev/null
+++ b/libhb/encx265.c
@@ -0,0 +1,295 @@
+/* encx265.c
+
+ Copyright (c) 2003-2013 HandBrake Team
+ This file is part of the HandBrake source code
+ Homepage: <http://handbrake.fr/>.
+ It may be used under the terms of the GNU General Public License v2.
+ For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
+ */
+#ifdef USE_X265
+#include <stdarg.h>
+#include <time.h>
+
+#include "hb.h"
+#include "hb_dict.h"
+#include "x265.h"
+
+int encx265Init( hb_work_object_t *, hb_job_t * );
+int encx265Work( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** );
+void encx265Close( hb_work_object_t * );
+void writeNALs(hb_work_private_t *, const x265_nal *, int );
+
+hb_work_object_t hb_encx265 =
+{
+ WORK_ENCX265,
+ "H.265/HEVC encoder (libx265)",
+ encx265Init,
+ encx265Work,
+ encx265Close
+};
+
+#define FRAME_INFO_MAX2 (8) // 2^8 = 256; 90000/256 = 352 frames/sec
+#define FRAME_INFO_MIN2 (17) // 2^17 = 128K; 90000/131072 = 1.4 frames/sec
+#define FRAME_INFO_SIZE (1 << (FRAME_INFO_MIN2 - FRAME_INFO_MAX2 + 1))
+#define FRAME_INFO_MASK (FRAME_INFO_SIZE - 1)
+
+static const char * const hb_x265_encopt_synonyms[][2] =
+{
+ { "me", "motion", },
+ { NULL, NULL, },
+};
+
+struct hb_work_private_s
+{
+ hb_job_t * job;
+ x265_encoder * x265;
+ x265_param * param;
+ x265_picture pic_in;
+ x265_nal * p_nal;
+ uint32_t nal_count;
+ uint8_t * grey_data;
+
+ uint32_t frames_in;
+ uint32_t frames_out;
+ int chap_mark; // saved chap mark when we're propagating it
+ int64_t last_stop; // Debugging - stop time of previous input frame
+ int64_t next_chap;
+
+ struct {
+ int64_t duration;
+ } frame_info[FRAME_INFO_SIZE];
+
+ int i_type;
+ int numEncode;
+ int64_t i_pts;
+
+ FILE *fout;
+};
+
+/***********************************************************************
+ * hb_work_encx265_init
+ ***********************************************************************
+ *
+ **********************************************************************/
+int encx265Init( hb_work_object_t * w, hb_job_t * job )
+{
+ hb_work_private_t *pv = calloc(1, sizeof(hb_work_private_t));
+ w->private_data = pv;
+
+ pv->job = job;
+ pv->fout = fopen(job->file, "wb");
+ fseek(pv->fout, 0, SEEK_SET);
+
+ x265_param *param = pv->param = x265_param_alloc();
+
+ x265_param_default_preset(param, "medium", "psnr");
+
+ hb_log("Output video resolution: %dx%d", job->width, job->height);
+ param->sourceWidth = job->width;
+ param->sourceHeight = job->height;
+ param->frameRate = job->vrate/job->vrate_base;
+ param->poolNumThreads = hb_get_cpu_count();
+
+ param->logLevel = X265_LOG_INFO;
+ param->frameNumThreads = hb_get_cpu_count();
+ param->tuQTMaxInterDepth = 1;
+ param->tuQTMaxIntraDepth = 1;
+
+ hb_dict_t *x265_opts = NULL;
+ if (job->advanced_opts != NULL && *job->advanced_opts != '\0')
+ {
+ x265_opts = hb_encopts_to_dict(job->advanced_opts, job->vcodec);
+ }
+ /* iterate through x265_opts and parse the options */
+ int ret;
+ hb_dict_entry_t *entry = NULL;
+ while ((entry = hb_dict_next(x265_opts, entry)) != NULL)
+ {
+
+ ret = x265_param_parse( param, entry->key, entry->value );
+ /* Let x265 sanity check the options for us */
+ if( ret == X265_PARAM_BAD_NAME )
+ hb_log( "x265 options: Unknown suboption %s", entry->key );
+ if( ret == X265_PARAM_BAD_VALUE )
+ hb_log( "x265 options: Bad argument %s=%s", entry->key, entry->value ? entry->value : "(null)" );
+
+ }
+ hb_dict_free(&x265_opts);
+
+ param->subpelRefine = 1;
+ param->maxNumMergeCand = 1;
+ param->bEnablePsnr = 1;
+
+ if (job->vquality > 0)
+ param->rc.qp = (int)job->vquality;
+
+ param->rc.bitrate = job->vbitrate;
+
+ x265_setup_primitives(param, 0);
+
+ pv->x265 = x265_encoder_open( param );
+ if ( pv->x265 == NULL )
+ {
+ hb_error("encx265: x265_encoder_open failed.");
+ free( pv );
+ pv = NULL;
+ return 1;
+ }
+ pv->numEncode = 0;
+ if (!x265_encoder_headers(pv->x265, &pv->p_nal, &pv->nal_count))
+ {
+ writeNALs(pv, pv->p_nal, pv->nal_count);
+ }
+ x265_picture_init(param, &pv->pic_in);
+ return 0;
+}
+
+void encx265Close( hb_work_object_t * w )
+{
+ hb_work_private_t * pv = w->private_data;
+
+ x265_param_free(pv->param);
+ x265_encoder_close(pv->x265);
+ fclose(pv->fout);
+ free(pv);
+ w->private_data = NULL;
+}
+
+/*
+ * see comments in definition of 'frame_info' in pv struct for description
+ * of what these routines are doing.
+ */
+static void save_frame_info( hb_work_private_t * pv, hb_buffer_t * in )
+{
+ int i = (in->s.start >> FRAME_INFO_MAX2) & FRAME_INFO_MASK;
+ pv->frame_info[i].duration = in->s.stop - in->s.start;
+}
+
+void writeNALs(hb_work_private_t * pv, const x265_nal* nal, int nalcount)
+{
+ int i;
+ for (i = 0; i < nalcount; i++)
+ {
+ fwrite((const char*)nal->payload, 1, nal->sizeBytes, pv->fout);
+ nal++;
+ }
+}
+
+static hb_buffer_t *x265_encode( hb_work_object_t *w, hb_buffer_t *in )
+{
+ hb_work_private_t *pv = w->private_data;
+ hb_job_t *job = pv->job;
+
+ x265_picture pic_out;
+ int numEncode;
+
+ pv->pic_in.stride[0] = in->plane[0].stride;
+ pv->pic_in.stride[1] = in->plane[1].stride;
+ pv->pic_in.stride[2] = in->plane[2].stride;
+ pv->pic_in.planes[0] = in->plane[0].data;
+ pv->pic_in.planes[1] = in->plane[1].data;
+ pv->pic_in.planes[2] = in->plane[2].data;
+ pv->pic_in.bitDepth = 8;
+
+ if( in->s.new_chap && job->chapter_markers )
+ {
+ pv->i_type = X265_TYPE_IDR;
+ if( pv->next_chap == 0 )
+ {
+ pv->next_chap = in->s.start;
+ pv->chap_mark = in->s.new_chap;
+ }
+ in->s.new_chap = 0;
+ }
+ else
+ {
+ pv->i_type = X265_TYPE_AUTO;
+ }
+
+ if( pv->last_stop != in->s.start )
+ {
+ hb_log("encx265 input continuity err: last stop %"PRId64" start %"PRId64,
+ pv->last_stop, in->s.start);
+ }
+ pv->last_stop = in->s.stop;
+
+ save_frame_info( pv, in );
+
+ pv->pic_in.pts = in->s.start;
+ numEncode = x265_encoder_encode( pv->x265, &pv->p_nal, &pv->nal_count, &pv->pic_in, &pic_out );
+ pv->numEncode += numEncode;
+ if ( pv->nal_count > 0 )
+ writeNALs(pv, pv->p_nal, pv->nal_count);
+
+ return NULL;
+}
+
+int encx265Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
+ hb_buffer_t ** buf_out )
+{
+ hb_work_private_t *pv = w->private_data;
+ hb_buffer_t *in = *buf_in;
+ int numEncode;
+
+ *buf_out = NULL;
+ if (in->size <= 0)
+ {
+ x265_picture pic_out;
+ uint32_t i_nal;
+ x265_nal *nal;
+ x265_stats stats;
+ hb_buffer_t *last_buf = NULL;
+ while (1)
+ {
+ numEncode = x265_encoder_encode(pv->x265, &nal, &i_nal, NULL, &pic_out);
+ if (i_nal <= 0)
+ break;
+ pv->numEncode += numEncode;
+ writeNALs(pv, nal, i_nal);
+
+ }
+ // Flushed everything - add the eof to the end of the chain.
+ if ( last_buf == NULL )
+ *buf_out = in;
+ else
+ last_buf->next = in;
+
+
+ *buf_in = NULL;
+
+ x265_encoder_get_stats(pv->x265, &stats, sizeof(stats));
+ x265_encoder_close(pv->x265);
+
+ if (stats.encodedPictureCount)
+ {
+ hb_log("X265 encoded %d frames in %.2fs (%.2f fps), %.2f kb/s, ", stats.encodedPictureCount,
+ stats.elapsedEncodeTime, stats.encodedPictureCount / stats.elapsedEncodeTime, stats.bitrate);
+
+ hb_log("Global PSNR: %.3f\n", stats.globalPsnr);
+ }
+ else
+ hb_log("encoded 0 frames\n");
+
+ hb_log("Work done!");
+ exit(0);
+
+ return HB_WORK_DONE;
+ }
+
+ pv->pic_in.poc = pv->frames_in;
+ ++pv->frames_in;
+ ++pv->frames_out;
+ *buf_out = x265_encode( w, in );
+ return HB_WORK_OK;
+}
+
+const char * hb_x265_encopt_name(const char *name)
+{
+ int i;
+ for (i = 0; hb_x265_encopt_synonyms[i][0] != NULL; i++)
+ if (!strcmp(name, hb_x265_encopt_synonyms[i][1]))
+ return hb_x265_encopt_synonyms[i][0];
+ return name;
+}
+
+#endif
diff --git a/libhb/hb.c b/libhb/hb.c
index 156d1241d..6149acd87 100644
--- a/libhb/hb.c
+++ b/libhb/hb.c
@@ -1659,6 +1659,9 @@ int hb_global_init()
hb_register(&hb_enctheora);
hb_register(&hb_encvorbis);
hb_register(&hb_encx264);
+#ifdef USE_X265
+ hb_register(&hb_encx265);
+#endif
#ifdef USE_QSV
hb_register(&hb_encqsv);
#endif
diff --git a/libhb/hb_dict.c b/libhb/hb_dict.c
index 0e9cd0523..bc52141f5 100644
--- a/libhb/hb_dict.c
+++ b/libhb/hb_dict.c
@@ -186,6 +186,11 @@ hb_dict_t * hb_encopts_to_dict( const char * encopts, int encoder )
// x264 has multiple names for some options
if( encoder == HB_VCODEC_X264 )
name = hb_x264_encopt_name( name );
+#ifdef USE_X265
+ // x265 has multiple names for some options
+ if( encoder == HB_VCODEC_X265 )
+ name = hb_x265_encopt_name( name );
+#endif
hb_dict_set( &dict, name, value );
}
}
diff --git a/libhb/internal.h b/libhb/internal.h
index 6fa74d591..b37dfcdd1 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -421,6 +421,7 @@ enum
WORK_ENCAVCODEC,
WORK_ENCQSV,
WORK_ENCX264,
+ WORK_ENCX265,
WORK_ENCTHEORA,
WORK_DECA52,
WORK_DECAVCODEC,
diff --git a/libhb/module.defs b/libhb/module.defs
index ffb7e0a40..b5b81e3aa 100644
--- a/libhb/module.defs
+++ b/libhb/module.defs
@@ -1,6 +1,6 @@
__deps__ := A52DEC BZIP2 FAAC FFMPEG FONTCONFIG FREETYPE LAME LIBASS LIBDCA \
LIBDVDREAD LIBDVDNAV LIBICONV LIBMKV LIBOGG LIBSAMPLERATE LIBTHEORA LIBVORBIS LIBXML2 \
- MP4V2 PTHREADW32 X264 ZLIB LIBBLURAY FDKAAC LIBMFX
+ MP4V2 PTHREADW32 X264 X265 ZLIB LIBBLURAY FDKAAC LIBMFX
$(eval $(call import.MODULE.defs,LIBHB,libhb,$(__deps__)))
$(eval $(call import.GCC,LIBHB))
@@ -81,6 +81,10 @@ ifeq (1,$(FEATURE.qsv))
LIBHB.GCC.D += USE_QSV HAVE_THREADS=1
endif
+ifeq (1,$(FEATURE.x265))
+ LIBHB.GCC.D += USE_X265
+endif
+
## required for <libdvdread/*.h>
ifneq (,$(filter $(BUILD.arch),ppc ppc64))
LIBHB.GCC.D += WORDS_BIGENDIAN
@@ -134,6 +138,10 @@ ifeq (1,$(FEATURE.qsv))
LIBHB.dll.libs += $(CONTRIB.build/)lib/libmfx.a
endif
+ifeq (1,$(FEATURE.x265))
+LIBHB.dll.libs += $(CONTRIB.build/)lib/libx265.a
+endif
+
ifeq (1,$(FEATURE.mp4v2))
LIBHB.dll.libs += $(CONTRIB.build/)lib/libmp4v2.a
endif
diff --git a/libhb/muxcommon.c b/libhb/muxcommon.c
index b085bd769..4ac35d515 100644
--- a/libhb/muxcommon.c
+++ b/libhb/muxcommon.c
@@ -475,6 +475,10 @@ hb_work_object_t * hb_muxer_init( hb_job_t * job )
mux->m = hb_mux_avformat_init( job );
break;
#endif
+#ifdef USE_X265
+ case HB_MUX_RAW:
+ break;
+#endif
default:
hb_error( "No muxer selected, exiting" );
*job->done_error = HB_ERROR_INIT;
diff --git a/libhb/work.c b/libhb/work.c
index abdf19f4f..1016ba80e 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -1239,6 +1239,11 @@ static void do_job(hb_job_t *job)
case HB_VCODEC_THEORA:
w = hb_get_work( WORK_ENCTHEORA );
break;
+#ifdef USE_X265
+ case HB_VCODEC_X265:
+ w = hb_get_work( WORK_ENCX265 );
+ break;
+#endif
}
// Handle case where there are no filters.
// This really should never happen.