summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohn Stebbins <[email protected]>2015-10-17 15:57:03 -0700
committerJohn Stebbins <[email protected]>2015-11-12 09:49:56 -0800
commit890a551270ef8110e47d9c503e1588d3d2bc710f (patch)
tree1e31de5f379c54157c9ec095b55a8eeadca5ef8a
parent88ce808b3083415950be070fa7bdc90d49a67eea (diff)
x264: add multilib support (a.k.a. 10-bit)
This adds the structure to load an libx264 10-bit shared library. The user must install this library themselves to an appropriate place.
-rw-r--r--gtk/src/callbacks.c7
-rw-r--r--gtk/src/hb-backend.c6
-rw-r--r--gtk/src/makedeps.py10
-rw-r--r--gtk/src/queuehandler.c6
-rw-r--r--gtk/src/videohandler.c6
-rw-r--r--libhb/common.c67
-rw-r--r--libhb/common.h17
-rw-r--r--libhb/encx264.c442
-rw-r--r--libhb/encx264.h56
-rw-r--r--libhb/h264_common.h3
-rw-r--r--libhb/hb.c2
-rw-r--r--libhb/hb_dict.c2
-rw-r--r--libhb/muxavformat.c3
-rw-r--r--libhb/ports.c32
-rw-r--r--libhb/ports.h15
-rw-r--r--libhb/preset.c2
-rw-r--r--libhb/qsv_common.c2
-rw-r--r--libhb/work.c21
-rw-r--r--macosx/HBJob+HBJobConversion.m2
-rw-r--r--macosx/HBVideo+UIAdditions.m17
-rw-r--r--macosx/HBVideo.m2
-rw-r--r--macosx/HBVideoController.m6
22 files changed, 551 insertions, 175 deletions
diff --git a/gtk/src/callbacks.c b/gtk/src/callbacks.c
index 003bb23a9..357dd4ba7 100644
--- a/gtk/src/callbacks.c
+++ b/gtk/src/callbacks.c
@@ -1008,7 +1008,7 @@ void ghb_show_container_options(signal_user_data_t *ud)
gtk_widget_set_visible(w2, (mux->format & HB_MUX_MASK_MP4));
gtk_widget_set_visible(w3, (mux->format & HB_MUX_MASK_MP4) &&
- (enc == HB_VCODEC_X264));
+ (enc == HB_VCODEC_X264_8BIT));
}
static void
@@ -2285,7 +2285,7 @@ vquality_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
vcodec = ghb_settings_video_encoder_codec(ud->settings, "VideoEncoder");
vquality = ghb_dict_get_double(ud->settings, "VideoQualitySlider");
- if (vcodec == HB_VCODEC_X264 && vquality < 1.0)
+ if ((vcodec & HB_VCODEC_X264_MASK) && vquality < 1.0)
{
// Set Profile to auto for lossless x264
ghb_ui_update(ud, "VideoProfile", ghb_string_value("auto"));
@@ -5130,7 +5130,8 @@ format_vquality_cb(GtkScale *scale, gdouble val, signal_user_data_t *ud)
return g_strdup_printf("%s: %d", vqname, (int)val);
} break;
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
{
if (val == 0.0)
{
diff --git a/gtk/src/hb-backend.c b/gtk/src/hb-backend.c
index d6e16d537..9b26552d0 100644
--- a/gtk/src/hb-backend.c
+++ b/gtk/src/hb-backend.c
@@ -1000,7 +1000,8 @@ ghb_vquality_default(signal_user_data_t *ud)
switch (vcodec)
{
case HB_VCODEC_X265:
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
return 20;
case HB_VCODEC_THEORA:
return 45;
@@ -3017,7 +3018,8 @@ ghb_build_advanced_opts_string(GhbValue *settings)
vcodec = ghb_settings_video_encoder_codec(settings, "VideoEncoder");
switch (vcodec)
{
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
return ghb_dict_get_string(settings, "x264Option");
default:
diff --git a/gtk/src/makedeps.py b/gtk/src/makedeps.py
index 7159a87c0..8abc4cc9d 100644
--- a/gtk/src/makedeps.py
+++ b/gtk/src/makedeps.py
@@ -53,13 +53,13 @@ dep_map = (
DepEntry("x264_subme", "x264_psy_rd", "<6", True, False),
DepEntry("x264_subme", "x264_psy_trell", "<6", True, False),
DepEntry("x264_trellis", "x264_psy_trell", "0", True, False),
- DepEntry("VideoEncoder", "x264FastDecode", "x264", False, True),
- DepEntry("VideoEncoder", "x264UseAdvancedOptions", "x264", False, True),
+ DepEntry("VideoEncoder", "x264FastDecode", "x264|x264_10bit", False, True),
+ DepEntry("VideoEncoder", "x264UseAdvancedOptions", "x264|x264_10bit", False, True),
DepEntry("HideAdvancedVideoSettings", "x264UseAdvancedOptions", "1", True, True),
- DepEntry("VideoEncoder", "VideoOptionExtraWindow", "x264|x265|mpeg4|mpeg2|VP8", False, True),
- DepEntry("VideoEncoder", "VideoOptionExtraLabel", "x264|x265|mpeg4|mpeg2|VP8", False, True),
+ DepEntry("VideoEncoder", "VideoOptionExtraWindow", "x264|x264_10bit|x265|mpeg4|mpeg2|VP8", False, True),
+ DepEntry("VideoEncoder", "VideoOptionExtraLabel", "x264|x264_10bit|x265|mpeg4|mpeg2|VP8", False, True),
DepEntry("x264UseAdvancedOptions", "VideoSettingsTable", "1", True, False),
- DepEntry("VideoEncoder", "x264_box", "x264", False, True),
+ DepEntry("VideoEncoder", "x264_box", "x264|x264_10bit", False, True),
DepEntry("x264UseAdvancedOptions", "x264_box", "0", True, False),
DepEntry("auto_name", "autoname_box", "1", False, False),
)
diff --git a/gtk/src/queuehandler.c b/gtk/src/queuehandler.c
index d99784070..ed2e16071 100644
--- a/gtk/src/queuehandler.c
+++ b/gtk/src/queuehandler.c
@@ -459,7 +459,7 @@ add_to_queue_list(signal_user_data_t *ud, GhbValue *settings, GtkTreeIter *piter
// Next line in the display (Video Encoder Options)
// Video Options: Preset - Tune - Profile - Level
- if (video_encoder->codec == HB_VCODEC_X264 &&
+ if ((video_encoder->codec & HB_VCODEC_X264_MASK) &&
!ghb_dict_get_bool(settings, "x264UseAdvancedOptions"))
{
const gchar *extra_opt = NULL;
@@ -489,7 +489,7 @@ add_to_queue_list(signal_user_data_t *ud, GhbValue *settings, GtkTreeIter *piter
XPRINT("%s%s", prefix, tune_opt);
prefix = ",";
}
- if (video_encoder->codec == HB_VCODEC_X264)
+ if (video_encoder->codec & HB_VCODEC_X264_MASK)
{
if (fastdecode)
{
@@ -522,7 +522,7 @@ add_to_queue_list(signal_user_data_t *ud, GhbValue *settings, GtkTreeIter *piter
XPRINT(_("<b>Advanced Options:</b> <small>%s</small>\n"), extra_opt);
}
}
- else if (video_encoder->codec == HB_VCODEC_X264)
+ else if (video_encoder->codec & HB_VCODEC_X264_MASK)
{
// Next line in the display (Video Encoder Options)
// Video Advanced Options: detailed settings
diff --git a/gtk/src/videohandler.c b/gtk/src/videohandler.c
index b4c0437b8..e463810cb 100644
--- a/gtk/src/videohandler.c
+++ b/gtk/src/videohandler.c
@@ -70,7 +70,7 @@ vcodec_changed_cb(GtkWidget *widget, signal_user_data_t *ud)
}
// Advanced options are only for x264
- if (encoder != HB_VCODEC_X264)
+ if (!(encoder & HB_VCODEC_X264_MASK))
{
ghb_ui_update(ud, "x264UseAdvancedOptions", ghb_boolean_value(FALSE));
}
@@ -101,7 +101,7 @@ ghb_video_setting_changed(GtkWidget *widget, signal_user_data_t *ud)
}
if (!ghb_dict_get_bool(ud->settings, "x264UseAdvancedOptions") &&
- encoder == HB_VCODEC_X264)
+ (encoder & HB_VCODEC_X264_MASK))
{
GString *str = g_string_new("");
const char *preset;
@@ -167,7 +167,7 @@ ghb_video_setting_changed(GtkWidget *widget, signal_user_data_t *ud)
{
level = "";
}
- new_opts = hb_x264_param_unparse(
+ new_opts = hb_x264_param_unparse(hb_video_encoder_get_depth(encoder),
preset, tunes, opts, profile, level, w, h);
if (new_opts)
ghb_update_x264Option(ud, new_opts);
diff --git a/libhb/common.c b/libhb/common.c
index 5f40b217d..e4ea0c9ed 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -18,6 +18,7 @@
#include "common.h"
#include "h264_common.h"
#include "h265_common.h"
+#include "encx264.h"
#ifdef USE_QSV
#include "qsv_common.h"
#endif
@@ -218,19 +219,20 @@ hb_encoder_t *hb_video_encoders_last_item = NULL;
hb_encoder_internal_t hb_video_encoders[] =
{
// legacy encoders, back to HB 0.9.4 whenever possible (disabled)
- { { "FFmpeg", "ffmpeg", NULL, HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_VCODEC_MPEG4, },
- { { "MPEG-4 (FFmpeg)", "ffmpeg4", NULL, HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_VCODEC_MPEG4, },
- { { "MPEG-2 (FFmpeg)", "ffmpeg2", NULL, HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_VCODEC_MPEG2, },
- { { "VP3 (Theora)", "libtheora", NULL, HB_VCODEC_THEORA, HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_VCODEC_THEORA, },
+ { { "FFmpeg", "ffmpeg", NULL, HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_VCODEC_MPEG4, },
+ { { "MPEG-4 (FFmpeg)", "ffmpeg4", NULL, HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_VCODEC_MPEG4, },
+ { { "MPEG-2 (FFmpeg)", "ffmpeg2", NULL, HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_VCODEC_MPEG2, },
+ { { "VP3 (Theora)", "libtheora", NULL, HB_VCODEC_THEORA, HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_VCODEC_THEORA, },
// actual encoders
- { { "H.264 (x264)", "x264", "H.264 (libx264)", HB_VCODEC_X264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264, },
- { { "H.264 (Intel QSV)", "qsv_h264", "H.264 (Intel Media SDK)", HB_VCODEC_QSV_H264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264, },
- { { "H.265 (x265)", "x265", "H.265 (libx265)", HB_VCODEC_X265, HB_MUX_AV_MP4|HB_MUX_AV_MKV, }, NULL, 1, HB_GID_VCODEC_H265, },
- { { "H.265 (Intel QSV)", "qsv_h265", "H.265 (Intel Media SDK)", HB_VCODEC_QSV_H265, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H265, },
- { { "MPEG-4", "mpeg4", "MPEG-4 (libavcodec)", HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG4, },
- { { "MPEG-2", "mpeg2", "MPEG-2 (libavcodec)", HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG2, },
- { { "VP8", "VP8", "VP8 (libvpx)", HB_VCODEC_FFMPEG_VP8, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_VP8, },
- { { "Theora", "theora", "Theora (libtheora)", HB_VCODEC_THEORA, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_THEORA, },
+ { { "H.264 (x264)", "x264", "H.264 (libx264)", HB_VCODEC_X264_8BIT, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264, },
+ { { "H.264 10-bit (x264)", "x264_10bit", "H.264 10-bit (libx264)", HB_VCODEC_X264_10BIT, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264, },
+ { { "H.264 (Intel QSV)", "qsv_h264", "H.264 (Intel Media SDK)", HB_VCODEC_QSV_H264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264, },
+ { { "H.265 (x265)", "x265", "H.265 (libx265)", HB_VCODEC_X265, HB_MUX_AV_MP4|HB_MUX_AV_MKV, }, NULL, 1, HB_GID_VCODEC_H265, },
+ { { "H.265 (Intel QSV)", "qsv_h265", "H.265 (Intel Media SDK)", HB_VCODEC_QSV_H265, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H265, },
+ { { "MPEG-4", "mpeg4", "MPEG-4 (libavcodec)", HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG4, },
+ { { "MPEG-2", "mpeg2", "MPEG-2 (libavcodec)", HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG2, },
+ { { "VP8", "VP8", "VP8 (libvpx)", HB_VCODEC_FFMPEG_VP8, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_VP8, },
+ { { "Theora", "theora", "Theora (libtheora)", HB_VCODEC_THEORA, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_THEORA, },
};
int hb_video_encoders_count = sizeof(hb_video_encoders) / sizeof(hb_video_encoders[0]);
static int hb_video_encoder_is_enabled(int encoder)
@@ -244,7 +246,6 @@ static int hb_video_encoder_is_enabled(int encoder)
switch (encoder)
{
// the following encoders are always enabled
- case HB_VCODEC_X264:
case HB_VCODEC_THEORA:
case HB_VCODEC_FFMPEG_MPEG4:
case HB_VCODEC_FFMPEG_MPEG2:
@@ -254,6 +255,14 @@ static int hb_video_encoder_is_enabled(int encoder)
#endif
return 1;
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
+ {
+ const x264_api_t *api;
+ api = hb_x264_api_get(hb_video_encoder_get_depth(encoder));
+ return (api != NULL);
+ }
+
default:
return 0;
}
@@ -1190,7 +1199,8 @@ void hb_video_quality_get_limits(uint32_t codec, float *low, float *high,
switch (codec)
{
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
#ifdef USE_X265
case HB_VCODEC_X265:
#endif
@@ -1236,7 +1246,8 @@ const char* hb_video_quality_get_name(uint32_t codec)
switch (codec)
{
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
#ifdef USE_X265
case HB_VCODEC_X265:
#endif
@@ -1250,6 +1261,17 @@ const char* hb_video_quality_get_name(uint32_t codec)
}
}
+int hb_video_encoder_get_depth(int encoder)
+{
+ switch (encoder)
+ {
+ case HB_VCODEC_X264_10BIT:
+ return 10;
+ default:
+ return 8;
+ }
+}
+
const char* const* hb_video_encoder_get_presets(int encoder)
{
#ifdef USE_QSV
@@ -1261,7 +1283,8 @@ const char* const* hb_video_encoder_get_presets(int encoder)
switch (encoder)
{
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
return x264_preset_names;
#ifdef USE_X265
@@ -1277,7 +1300,8 @@ const char* const* hb_video_encoder_get_tunes(int encoder)
{
switch (encoder)
{
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
return x264_tune_names;
#ifdef USE_X265
@@ -1300,8 +1324,10 @@ const char* const* hb_video_encoder_get_profiles(int encoder)
switch (encoder)
{
- case HB_VCODEC_X264:
- return hb_h264_profile_names;
+ case HB_VCODEC_X264_8BIT:
+ return hb_h264_profile_names_8bit;
+ case HB_VCODEC_X264_10BIT:
+ return hb_h264_profile_names_10bit;
case HB_VCODEC_X265:
return hb_h265_profile_names;
@@ -1322,7 +1348,8 @@ const char* const* hb_video_encoder_get_levels(int encoder)
switch (encoder)
{
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
return hb_h264_level_names;
default:
diff --git a/libhb/common.h b/libhb/common.h
index d38e4fce5..5152cbc70 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -386,6 +386,7 @@ const hb_rate_t* hb_audio_bitrate_get_next(const hb_rate_t *last);
void hb_video_quality_get_limits(uint32_t codec, float *low, float *high, float *granularity, int *direction);
const char* hb_video_quality_get_name(uint32_t codec);
+int hb_video_encoder_get_depth (int encoder);
const char* const* hb_video_encoder_get_presets (int encoder);
const char* const* hb_video_encoder_get_tunes (int encoder);
const char* const* hb_video_encoder_get_profiles(int encoder);
@@ -519,9 +520,8 @@ struct hb_job_s
cfr: 0 (vfr), 1 (cfr), 2 (pfr) [see render.c]
pass: 0, 1 or 2 (or -1 for scan)
areBframes: boolean to note if b-frames are used */
-#define HB_VCODEC_MASK 0x0000FFF
+#define HB_VCODEC_MASK 0x00FFFFF
#define HB_VCODEC_INVALID 0x0000000
-#define HB_VCODEC_X264 0x0000001
#define HB_VCODEC_THEORA 0x0000002
#define HB_VCODEC_X265 0x0000004
#define HB_VCODEC_FFMPEG_MPEG4 0x0000010
@@ -531,7 +531,11 @@ struct hb_job_s
#define HB_VCODEC_QSV_H264 0x0000100
#define HB_VCODEC_QSV_H265 0x0000200
#define HB_VCODEC_QSV_MASK 0x0000F00
-#define HB_VCODEC_H264_MASK (HB_VCODEC_X264|HB_VCODEC_QSV_H264)
+#define HB_VCODEC_X264_8BIT 0x0010000
+#define HB_VCODEC_X264 HB_VCODEC_X264_8BIT
+#define HB_VCODEC_X264_10BIT 0x0020000
+#define HB_VCODEC_X264_MASK 0x0030000
+#define HB_VCODEC_H264_MASK (HB_VCODEC_X264_MASK|HB_VCODEC_QSV_H264)
#define HB_VCODEC_H265_MASK (HB_VCODEC_X265|HB_VCODEC_QSV_H265)
int vcodec;
@@ -1296,9 +1300,10 @@ int hb_rgb2yuv(int rgb);
const char * hb_subsource_name( int source );
// unparse a set of x264 settings to an HB encopts string
-char * hb_x264_param_unparse(const char *x264_preset, const char *x264_tune,
- const char *x264_encopts, const char *h264_profile,
- const char *h264_level, int width, int height);
+char * hb_x264_param_unparse(int bit_depth, const char *x264_preset,
+ const char *x264_tune, const char *x264_encopts,
+ const char *h264_profile, const char *h264_level,
+ int width, int height);
// x264 option name/synonym helper
const char * hb_x264_encopt_name( const char * name );
diff --git a/libhb/encx264.c b/libhb/encx264.c
index f87573609..aa07a4dab 100644
--- a/libhb/encx264.c
+++ b/libhb/encx264.c
@@ -48,21 +48,27 @@ hb_work_object_t hb_encx264 =
struct hb_work_private_s
{
- hb_job_t * job;
- x264_t * x264;
- x264_picture_t pic_in;
+ hb_job_t * job;
+ x264_t * x264;
+ x264_picture_t pic_in;
- int64_t last_stop; // Debugging - stop time of previous input frame
+ int64_t last_stop; // Debugging - stop time of previous input frame
- hb_list_t *delayed_chapters;
- int64_t next_chapter_pts;
+ hb_list_t * delayed_chapters;
+ int64_t next_chapter_pts;
struct {
- int64_t duration;
+ int64_t duration;
} frame_info[FRAME_INFO_SIZE];
- char filename[1024];
+ char filename[1024];
+
+ // Multiple bit-depth
+ const x264_api_t * api;
};
+#define HB_X264_API_COUNT 2
+static x264_api_t x264_apis[HB_X264_API_COUNT];
+
// used in delayed_chapters list
struct chapter_s
{
@@ -70,6 +76,224 @@ struct chapter_s
int64_t start;
};
+const char *libx264_10bit_names[] = {
+ "libx26410b", "libx264_main10", NULL
+};
+
+const char *libx264_8bit_names[] = {
+ "libx264", "libx2648b", "libx264_main", NULL
+};
+
+static void * x264_lib_open(const char *names[])
+{
+ if (names == NULL)
+ return NULL;
+
+ void *h;
+ int ii = 0;
+ while (names[ii] != NULL)
+ {
+ char *name = hb_strdup_printf("%s%s", names[ii], HB_SO_EXT);
+ h = hb_dlopen(name);
+ free(name);
+ if (h != NULL)
+ {
+ return h;
+ }
+ ii++;
+ }
+ return NULL;
+}
+
+#if defined(SYS_LINUX)
+static void * x264_lib_open_ubuntu_10bit_search(const char *prefix)
+{
+ void * h;
+ const char * name = "libx264.so";
+
+ HB_DIR * dir;
+ struct dirent * entry;
+
+ dir = hb_opendir(prefix);
+ if (dir == NULL)
+ {
+ return NULL;
+ }
+
+ while ((entry = hb_readdir(dir)) != NULL)
+ {
+ if (!strncmp(entry->d_name, name, strlen(name)))
+ {
+ char *path = hb_strdup_printf("%s/%s", prefix, entry->d_name);
+ h = hb_dlopen(path);
+ free(path);
+ if (h != NULL)
+ {
+ hb_closedir(dir);
+ return h;
+ }
+ }
+ }
+ hb_closedir(dir);
+
+ return NULL;
+}
+
+static void * x264_lib_open_ubuntu_10bit(void)
+{
+ void *h = NULL;
+
+#if ARCH_X86_64
+ h = x264_lib_open_ubuntu_10bit_search("/usr/lib/x86_64-linux-gnu/x264-10bit");
+#elif ARCH_X86_32
+ h = x264_lib_open_ubuntu_10bit_search("/usr/lib/i386-linux-gnu/x264-10bit");
+#endif
+ if (h == NULL)
+ {
+ h = x264_lib_open_ubuntu_10bit_search("/usr/lib/x264-10bit");
+ }
+ return h;
+}
+#endif
+
+void hb_x264_global_init(void)
+{
+ x264_apis[0].bit_depth = x264_bit_depth;
+ x264_apis[0].param_default = x264_param_default;
+ x264_apis[0].param_default_preset = x264_param_default_preset;
+ x264_apis[0].param_apply_profile = x264_param_apply_profile;
+ x264_apis[0].param_apply_fastfirstpass = x264_param_apply_fastfirstpass;
+ x264_apis[0].param_parse = x264_param_parse;
+ x264_apis[0].encoder_open = x264_encoder_open;
+ x264_apis[0].encoder_headers = x264_encoder_headers;
+ x264_apis[0].encoder_encode = x264_encoder_encode;
+ x264_apis[0].encoder_delayed_frames = x264_encoder_delayed_frames;
+ x264_apis[0].encoder_close = x264_encoder_close;
+ x264_apis[0].picture_init = x264_picture_init;
+
+ // Invalidate other apis
+ x264_apis[1].bit_depth = -1;
+
+ // Attempt to dlopen a library for handling the bit-depth that we do
+ // not already have.
+ void *h;
+ if (x264_bit_depth == 8)
+ {
+ h = x264_lib_open(libx264_10bit_names);
+#if defined(SYS_LINUX)
+ if (h == NULL)
+ {
+ h = x264_lib_open_ubuntu_10bit();
+ }
+#endif
+ }
+ else
+ {
+ h = x264_lib_open(libx264_8bit_names);
+ }
+ if (h == NULL)
+ {
+ return;
+ }
+
+ int ii;
+ int *pbit_depth = (int*)hb_dlsym(h, "x264_bit_depth");
+ x264_apis[1].param_default = hb_dlsym(h, "x264_param_default");
+ x264_apis[1].param_default_preset = hb_dlsym(h, "x264_param_default_preset");
+ x264_apis[1].param_apply_profile = hb_dlsym(h, "x264_param_apply_profile");
+ x264_apis[1].param_apply_fastfirstpass =
+ hb_dlsym(h, "x264_param_apply_fastfirstpass");
+ x264_apis[1].param_parse = hb_dlsym(h, "x264_param_parse");
+ // x264 appends the build number to the end of x264_encoder_open
+ for (ii = 140; ii < 200; ii++)
+ {
+ char *name = hb_strdup_printf("x264_encoder_open_%d", ii);
+ x264_apis[1].encoder_open = hb_dlsym(h, name);
+ free(name);
+ if (x264_apis[1].encoder_open != NULL)
+ {
+ break;
+ }
+ }
+ x264_apis[1].encoder_headers = hb_dlsym(h, "x264_encoder_headers");
+ x264_apis[1].encoder_encode = hb_dlsym(h, "x264_encoder_encode");
+ x264_apis[1].encoder_delayed_frames =
+ hb_dlsym(h, "x264_encoder_delayed_frames");
+ x264_apis[1].encoder_close = hb_dlsym(h, "x264_encoder_close");
+ x264_apis[1].picture_init = hb_dlsym(h, "x264_picture_init");
+
+ if (pbit_depth != NULL &&
+ x264_apis[1].param_default != NULL &&
+ x264_apis[1].param_default_preset != NULL &&
+ x264_apis[1].param_apply_profile != NULL &&
+ x264_apis[1].param_apply_fastfirstpass != NULL &&
+ x264_apis[1].param_parse != NULL &&
+ x264_apis[1].encoder_open != NULL &&
+ x264_apis[1].encoder_headers != NULL &&
+ x264_apis[1].encoder_encode != NULL &&
+ x264_apis[1].encoder_delayed_frames != NULL &&
+ x264_apis[1].encoder_close != NULL &&
+ x264_apis[1].picture_init != NULL)
+ {
+ x264_apis[1].bit_depth = *pbit_depth;
+ }
+}
+
+const x264_api_t * hb_x264_api_get(int bit_depth)
+{
+ int ii;
+
+ for (ii = 0; ii < HB_X264_API_COUNT; ii++)
+ {
+ if (-1 != x264_apis[ii].bit_depth &&
+ bit_depth == x264_apis[ii].bit_depth)
+ {
+ return &x264_apis[ii];
+ }
+ }
+ return NULL;
+}
+
+#if 0
+/*
+ * Check whether a valid h264_level is compatible with the given framerate,
+ * resolution and interlaced compression/flags combination.
+ *
+ * width, height, fps_num and fps_den should be greater than zero.
+ *
+ * interlacing parameters can be set to zero when the information is
+ * unavailable, as apply_h264_level() will disable interlacing if necessary.
+ *
+ * Returns 0 if the level is valid and compatible, 1 otherwise.
+ */
+static int check_h264_level(const char *h264_level, int width, int height,
+ int fps_num, int fps_den, int interlaced,
+ int fake_interlaced);
+#endif
+
+/*
+ * Applies the restrictions of the requested H.264 level to an x264_param_t.
+ *
+ * Returns -1 if an invalid level (or no level) is specified. GUIs should be
+ * capable of always providing a valid level.
+ *
+ * Does not modify resolution/framerate but warns when they exceed level limits.
+ *
+ * Based on a x264_param_apply_level() draft and other x264 code.
+ */
+static int apply_h264_level(const x264_api_t *api, x264_param_t *param,
+ const char *h264_level, const char *x264_profile,
+ int verbose);
+
+/*
+ * Applies the restrictions of the requested H.264 profile to an x264_param_t.
+ *
+ * x264_param_apply_profile wrapper designed to always succeed when a valid
+ * H.264 profile is specified (unlike x264's function).
+ */
+static int apply_h264_profile(const x264_api_t *api, x264_param_t *param,
+ const char *h264_profile, int verbose);
+
/***********************************************************************
* hb_work_encx264_init
***********************************************************************
@@ -77,18 +301,25 @@ struct chapter_s
**********************************************************************/
int encx264Init( hb_work_object_t * w, hb_job_t * job )
{
- x264_param_t param;
- x264_nal_t * nal;
- int nal_count;
-
hb_work_private_t * pv = calloc( 1, sizeof( hb_work_private_t ) );
+ x264_param_t param;
+ x264_nal_t * nal;
+ int nal_count, bit_depth;
+
w->private_data = pv;
+ bit_depth = hb_video_encoder_get_depth(job->vcodec);
+ pv->api = hb_x264_api_get(bit_depth);
+ if (pv->api == NULL)
+ {
+ hb_error("encx264: hb_x264_api_get failed, bit-depth %d", bit_depth);
+ }
+
pv->job = job;
pv->next_chapter_pts = AV_NOPTS_VALUE;
pv->delayed_chapters = hb_list_init();
- if (x264_param_default_preset(&param,
+ if (pv->api->param_default_preset(&param,
job->encoder_preset, job->encoder_tune) < 0)
{
free( pv );
@@ -198,7 +429,7 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
char *str = hb_value_get_string_xform(value);
/* Here's where the strings are passed to libx264 for parsing. */
- ret = x264_param_parse(&param, key, str);
+ ret = pv->api->param_parse(&param, key, str);
/* Let x264 sanity check the options for us */
if (ret == X264_PARAM_BAD_NAME)
@@ -289,7 +520,7 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
/* Apply profile and level settings last, if present. */
if (job->encoder_profile != NULL && *job->encoder_profile)
{
- if (hb_apply_h264_profile(&param, job->encoder_profile, 1))
+ if (apply_h264_profile(pv->api, &param, job->encoder_profile, 1))
{
free(pv);
w->private_data = NULL;
@@ -298,8 +529,8 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
}
if (job->encoder_level != NULL && *job->encoder_level)
{
- if (hb_apply_h264_level(&param, job->encoder_level,
- job->encoder_profile, 1) < 0)
+ if (apply_h264_level(pv->api, &param, job->encoder_level,
+ job->encoder_profile, 1) < 0)
{
free(pv);
w->private_data = NULL;
@@ -310,7 +541,7 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
/* Turbo first pass */
if( job->pass_id == HB_PASS_ENCODE_1ST && job->fastfirstpass == 1 )
{
- x264_param_apply_fastfirstpass( &param );
+ pv->api->param_apply_fastfirstpass( &param );
}
/* B-pyramid is enabled by default. */
@@ -326,7 +557,8 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
}
/* Log the unparsed x264 options string. */
- char *x264_opts_unparsed = hb_x264_param_unparse(job->encoder_preset,
+ char *x264_opts_unparsed = hb_x264_param_unparse(pv->api->bit_depth,
+ job->encoder_preset,
job->encoder_tune,
job->encoder_options,
job->encoder_profile,
@@ -340,7 +572,7 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
free( x264_opts_unparsed );
hb_deep_log( 2, "encx264: opening libx264 (pass %d)", job->pass_id );
- pv->x264 = x264_encoder_open( &param );
+ pv->x264 = pv->api->encoder_open( &param );
if ( pv->x264 == NULL )
{
hb_error("encx264: x264_encoder_open failed.");
@@ -349,7 +581,7 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
return 1;
}
- x264_encoder_headers( pv->x264, &nal, &nal_count );
+ pv->api->encoder_headers( pv->x264, &nal, &nal_count );
/* Sequence Parameter Set */
memcpy(w->config->h264.sps, nal[0].p_payload + 4, nal[0].i_payload - 4);
@@ -359,9 +591,13 @@ int encx264Init( hb_work_object_t * w, hb_job_t * job )
memcpy(w->config->h264.pps, nal[1].p_payload + 4, nal[1].i_payload - 4);
w->config->h264.pps_length = nal[1].i_payload - 4;
- x264_picture_init( &pv->pic_in );
+ pv->api->picture_init( &pv->pic_in );
pv->pic_in.img.i_csp = X264_CSP_I420;
+ if (pv->api->bit_depth > 8)
+ {
+ pv->pic_in.img.i_csp |= X264_CSP_HIGH_DEPTH;
+ }
pv->pic_in.img.i_plane = 3;
return 0;
@@ -387,7 +623,7 @@ void encx264Close( hb_work_object_t * w )
hb_list_close(&pv->delayed_chapters);
}
- x264_encoder_close( pv->x264 );
+ pv->api->encoder_close( pv->x264 );
free( pv );
w->private_data = NULL;
@@ -555,18 +791,58 @@ static hb_buffer_t *nal_encode( hb_work_object_t *w, x264_picture_t *pic_out,
return buf;
}
+static hb_buffer_t * expand_buf(int bit_depth, hb_buffer_t *in)
+{
+ hb_buffer_t *buf;
+ int pp;
+ int shift = bit_depth - 8;
+
+ buf = hb_frame_buffer_init(AV_PIX_FMT_YUV420P16BE,
+ in->f.width, in->f.height);
+ for (pp = 0; pp < 3; pp++)
+ {
+ int xx, yy;
+ uint8_t *src = in->plane[pp].data;
+ uint16_t *dst = (uint16_t*)buf->plane[pp].data;
+ for (yy = 0; yy < in->plane[pp].height; yy++)
+ {
+ for (xx = 0; xx < in->plane[pp].width; xx++)
+ {
+ dst[xx] = (uint16_t)src[xx] << shift;
+ }
+ src += in->plane[pp].stride;
+ dst += buf->plane[pp].stride / 2;
+ }
+ }
+ return buf;
+}
+
static hb_buffer_t *x264_encode( hb_work_object_t *w, hb_buffer_t *in )
{
hb_work_private_t *pv = w->private_data;
- hb_job_t *job = pv->job;
+ hb_job_t *job = pv->job;
+ hb_buffer_t *tmp = NULL;
/* Point x264 at our current buffers Y(UV) data. */
- pv->pic_in.img.i_stride[0] = in->plane[0].stride;
- pv->pic_in.img.i_stride[1] = in->plane[1].stride;
- pv->pic_in.img.i_stride[2] = in->plane[2].stride;
- pv->pic_in.img.plane[0] = in->plane[0].data;
- pv->pic_in.img.plane[1] = in->plane[1].data;
- pv->pic_in.img.plane[2] = in->plane[2].data;
+ if (pv->pic_in.img.i_csp & X264_CSP_HIGH_DEPTH)
+ {
+ tmp = expand_buf(pv->api->bit_depth, in);
+ pv->pic_in.img.i_stride[0] = tmp->plane[0].stride;
+ pv->pic_in.img.i_stride[1] = tmp->plane[1].stride;
+ pv->pic_in.img.i_stride[2] = tmp->plane[2].stride;
+ pv->pic_in.img.plane[0] = tmp->plane[0].data;
+ pv->pic_in.img.plane[1] = tmp->plane[1].data;
+ pv->pic_in.img.plane[2] = tmp->plane[2].data;
+ }
+ else
+ {
+ pv->pic_in.img.i_stride[0] = in->plane[0].stride;
+ pv->pic_in.img.i_stride[1] = in->plane[1].stride;
+ pv->pic_in.img.i_stride[2] = in->plane[2].stride;
+ pv->pic_in.img.plane[0] = in->plane[0].data;
+ pv->pic_in.img.plane[1] = in->plane[1].data;
+ pv->pic_in.img.plane[2] = in->plane[2].data;
+ }
if( in->s.new_chap && job->chapter_markers )
{
@@ -626,11 +902,12 @@ static hb_buffer_t *x264_encode( hb_work_object_t *w, hb_buffer_t *in )
int i_nal;
x264_nal_t *nal;
- x264_encoder_encode( pv->x264, &nal, &i_nal, &pv->pic_in, &pic_out );
+ pv->api->encoder_encode( pv->x264, &nal, &i_nal, &pv->pic_in, &pic_out );
if ( i_nal > 0 )
{
return nal_encode( w, &pic_out, i_nal, nal );
}
+ hb_buffer_close(&tmp);
return NULL;
}
@@ -654,9 +931,9 @@ int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_list_clear(&list);
// flush delayed frames
- while ( x264_encoder_delayed_frames( pv->x264 ) )
+ while ( pv->api->encoder_delayed_frames( pv->x264 ) )
{
- x264_encoder_encode( pv->x264, &nal, &i_nal, NULL, &pic_out );
+ pv->api->encoder_encode( pv->x264, &nal, &i_nal, NULL, &pic_out );
if ( i_nal == 0 )
continue;
if ( i_nal < 0 )
@@ -678,11 +955,20 @@ int encx264Work( hb_work_object_t * w, hb_buffer_t ** buf_in,
return HB_WORK_OK;
}
-int hb_apply_h264_profile(x264_param_t *param, const char *h264_profile,
- int verbose)
+static int apply_h264_profile(const x264_api_t *api, x264_param_t *param,
+ const char *h264_profile, int verbose)
{
+ const char * const * profile_names;
+ if (api->bit_depth == 10)
+ {
+ profile_names = hb_h264_profile_names_10bit;
+ }
+ else
+ {
+ profile_names = hb_h264_profile_names_8bit;
+ }
if (h264_profile != NULL &&
- strcasecmp(h264_profile, hb_h264_profile_names[0]) != 0)
+ strcasecmp(h264_profile, profile_names[0]) != 0)
{
/*
* baseline profile doesn't support interlacing
@@ -693,7 +979,7 @@ int hb_apply_h264_profile(x264_param_t *param, const char *h264_profile,
{
if (verbose)
{
- hb_log("hb_apply_h264_profile [warning]: baseline profile doesn't support interlacing, disabling");
+ hb_log("apply_h264_profile [warning]: baseline profile doesn't support interlacing, disabling");
}
param->b_interlaced = param->b_fake_interlaced = 0;
}
@@ -706,20 +992,13 @@ int hb_apply_h264_profile(x264_param_t *param, const char *h264_profile,
{
if (verbose)
{
- hb_log("hb_apply_h264_profile [warning]: lossless requires high444 profile, disabling");
+ hb_log("apply_h264_profile [warning]: lossless requires high444 profile, disabling");
}
param->rc.f_rf_constant = 1.0;
}
- if (!strcasecmp(h264_profile, "high10") ||
- !strcasecmp(h264_profile, "high422"))
- {
- // arbitrary profile names may be specified via the CLI
- // map unsupported high10 and high422 profiles to high
- return x264_param_apply_profile(param, "high");
- }
- return x264_param_apply_profile(param, h264_profile);
+ return api->param_apply_profile(param, h264_profile);
}
- else if (!strcasecmp(h264_profile, hb_h264_profile_names[0]))
+ else if (!strcasecmp(h264_profile, profile_names[0]))
{
// "auto", do nothing
return 0;
@@ -727,14 +1006,15 @@ int hb_apply_h264_profile(x264_param_t *param, const char *h264_profile,
else
{
// error (profile not a string), abort
- hb_error("hb_apply_h264_profile: no profile specified");
+ hb_error("apply_h264_profile: no profile specified");
return -1;
}
}
-int hb_check_h264_level(const char *h264_level, int width, int height,
- int fps_num, int fps_den, int interlaced,
- int fake_interlaced)
+#if 0
+static int check_h264_level(const char *h264_level,
+ int width, int height, int fps_num,
+ int fps_den, int interlaced, int fake_interlaced)
{
x264_param_t param;
x264_param_default(&param);
@@ -744,11 +1024,13 @@ int hb_check_h264_level(const char *h264_level, int width, int height,
param.i_fps_den = fps_den;
param.b_interlaced = !!interlaced;
param.b_fake_interlaced = !!fake_interlaced;
- return (hb_apply_h264_level(&param, h264_level, NULL, 0) != 0);
+ return (apply_h264_level(&param, h264_level, NULL, 0) != 0);
}
+#endif
-int hb_apply_h264_level(x264_param_t *param, const char *h264_level,
- const char *h264_profile, int verbose)
+int apply_h264_level(const x264_api_t *api, x264_param_t *param,
+ const char *h264_level, const char *h264_profile,
+ int verbose)
{
float f_framerate;
const x264_level_t *x264_level = NULL;
@@ -779,7 +1061,7 @@ int hb_apply_h264_level(x264_param_t *param, const char *h264_level,
if (x264_level == NULL)
{
// error (invalid or unsupported level), abort
- hb_error("hb_apply_h264_level: invalid level %s", h264_level);
+ hb_error("apply_h264_level: invalid level %s", h264_level);
return -1;
}
}
@@ -791,7 +1073,7 @@ int hb_apply_h264_level(x264_param_t *param, const char *h264_level,
else
{
// error (level not a string), abort
- hb_error("hb_apply_h264_level: no level specified");
+ hb_error("apply_h264_level: no level specified");
return -1;
}
@@ -804,6 +1086,7 @@ int hb_apply_h264_level(x264_param_t *param, const char *h264_level,
HB_ENCX264_PROFILE_MAIN,
// High (no 4:2:2 or 10-bit support, so anything lossy is equivalent)
HB_ENCX264_PROFILE_HIGH,
+ HB_ENCX264_PROFILE_HIGH10,
// Lossless (4:2:0 8-bit for now)
HB_ENCX264_PROFILE_HIGH444,
} hb_encx264_profile;
@@ -847,7 +1130,14 @@ int hb_apply_h264_level(x264_param_t *param, const char *h264_level,
else if (param->analyse.b_transform_8x8 ||
param->i_cqm_preset != X264_CQM_FLAT)
{
- hb_encx264_profile = HB_ENCX264_PROFILE_HIGH;
+ if (api->bit_depth == 10)
+ {
+ hb_encx264_profile = HB_ENCX264_PROFILE_HIGH10;
+ }
+ else
+ {
+ hb_encx264_profile = HB_ENCX264_PROFILE_HIGH;
+ }
}
else
{
@@ -861,7 +1151,7 @@ int hb_apply_h264_level(x264_param_t *param, const char *h264_level,
if (param->i_width <= 0 || param->i_height <= 0)
{
// error (invalid width or height), abort
- hb_error("hb_apply_h264_level: invalid resolution (width: %d, height: %d)",
+ hb_error("apply_h264_level: invalid resolution (width: %d, height: %d)",
param->i_width, param->i_height);
return -1;
}
@@ -879,7 +1169,7 @@ int hb_apply_h264_level(x264_param_t *param, const char *h264_level,
{
if (verbose)
{
- hb_log("hb_apply_h264_level [warning]: interlaced flag not supported for level %s, disabling",
+ hb_log("apply_h264_level [warning]: interlaced flag not supported for level %s, disabling",
h264_level);
}
ret = 1;
@@ -937,7 +1227,9 @@ int hb_apply_h264_level(x264_param_t *param, const char *h264_level,
if (hb_encx264_profile != HB_ENCX264_PROFILE_HIGH444)
{
// High profile allows for higher VBV bufsize/maxrate
- int cbp_factor = hb_encx264_profile == HB_ENCX264_PROFILE_HIGH ? 5 : 4;
+ int cbp_factor;
+ cbp_factor = hb_encx264_profile == HB_ENCX264_PROFILE_HIGH10 ? 12 :
+ hb_encx264_profile == HB_ENCX264_PROFILE_HIGH ? 5 : 4;
if (!param->rc.i_vbv_max_bitrate)
{
param->rc.i_vbv_max_bitrate = (x264_level->bitrate * cbp_factor) / 4;
@@ -979,7 +1271,7 @@ int hb_apply_h264_level(x264_param_t *param, const char *h264_level,
{
if (verbose)
{
- hb_log("hb_apply_h264_level [warning]: frame size (%dx%d, %d macroblocks) too high for level %s (max. %d macroblocks)",
+ hb_log("apply_h264_level [warning]: frame size (%dx%d, %d macroblocks) too high for level %s (max. %d macroblocks)",
i_mb_width * 16, i_mb_height * 16, i_mb_size, h264_level,
x264_level->frame_size);
}
@@ -989,7 +1281,7 @@ int hb_apply_h264_level(x264_param_t *param, const char *h264_level,
{
if (verbose)
{
- hb_log("hb_apply_h264_level [warning]: framerate (%.3f) too high for level %s at %dx%d (max. %.3f)",
+ hb_log("apply_h264_level [warning]: framerate (%.3f) too high for level %s at %dx%d (max. %.3f)",
f_framerate, h264_level, param->i_width, param->i_height,
(float)x264_level->mbps / i_mb_size);
}
@@ -1004,7 +1296,7 @@ int hb_apply_h264_level(x264_param_t *param, const char *h264_level,
{
if (verbose)
{
- hb_log("hb_apply_h264_level [warning]: frame too wide (%d) for level %s (max. %d)",
+ hb_log("apply_h264_level [warning]: frame too wide (%d) for level %s (max. %d)",
param->i_width, h264_level, max_mb_side * 16);
}
ret = 1;
@@ -1013,7 +1305,7 @@ int hb_apply_h264_level(x264_param_t *param, const char *h264_level,
{
if (verbose)
{
- hb_log("hb_apply_h264_level [warning]: frame too tall (%d) for level %s (max. %d)",
+ hb_log("apply_h264_level [warning]: frame too tall (%d) for level %s (max. %d)",
param->i_height, h264_level, max_mb_side * 16);
}
ret = 1;
@@ -1034,24 +1326,28 @@ static hb_value_t * value_pair(hb_value_t * v1, hb_value_t * v2)
return array;
}
-char * hb_x264_param_unparse(const char *x264_preset, const char *x264_tune,
- const char *x264_encopts, const char *h264_profile,
- const char *h264_level, int width, int height)
+// TODO: add bit_depth
+char * hb_x264_param_unparse(int bit_depth, const char *x264_preset,
+ const char *x264_tune, const char *x264_encopts,
+ const char *h264_profile, const char *h264_level,
+ int width, int height)
{
int i;
char *unparsed_opts;
hb_dict_t *x264_opts;
x264_param_t defaults, param;
+ const x264_api_t *api;
/*
* get the global x264 defaults (what we compare against)
*/
- x264_param_default(&defaults);
+ api = hb_x264_api_get(bit_depth);
+ api->param_default(&defaults);
/*
* apply the defaults, preset and tune
*/
- if (x264_param_default_preset(&param, x264_preset, x264_tune) < 0)
+ if (api->param_default_preset(&param, x264_preset, x264_tune) < 0)
{
/*
* Note: GUIs should be able to always specifiy valid preset/tunes, so
@@ -1094,7 +1390,7 @@ char * hb_x264_param_unparse(const char *x264_preset, const char *x264_tune,
char *str = hb_value_get_string_xform(value);
// let's not pollute GUI logs with x264_param_parse return codes
- x264_param_parse(&param, key, str);
+ api->param_parse(&param, key, str);
free(str);
}
@@ -1104,7 +1400,7 @@ char * hb_x264_param_unparse(const char *x264_preset, const char *x264_tune,
if (h264_profile != NULL && *h264_profile)
{
// be quiet so at to not pollute GUI logs
- hb_apply_h264_profile(&param, h264_profile, 0);
+ apply_h264_profile(api, &param, h264_profile, 0);
}
/*
@@ -1112,11 +1408,11 @@ char * hb_x264_param_unparse(const char *x264_preset, const char *x264_tune,
*/
if (h264_level != NULL && *h264_level)
{
- // set width/height to avoid issues in hb_apply_h264_level
+ // set width/height to avoid issues in apply_h264_level
param.i_width = width;
param.i_height = height;
// be quiet so at to not pollute GUI logs
- hb_apply_h264_level(&param, h264_level, h264_profile, 0);
+ apply_h264_level(api, &param, h264_level, h264_profile, 0);
}
/*
diff --git a/libhb/encx264.h b/libhb/encx264.h
index eadad6859..c2fa26a44 100644
--- a/libhb/encx264.h
+++ b/libhb/encx264.h
@@ -7,6 +7,9 @@
For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
*/
+#ifndef HB_ENCX264_H
+#define HB_ENCX264_H
+
#include "x264.h"
#include "h264_common.h"
@@ -39,39 +42,24 @@ static const char * const hb_x264_encopt_synonyms[][2] =
{ NULL, NULL, },
};
-/*
- * Check whether a valid h264_level is compatible with the given framerate,
- * resolution and interlaced compression/flags combination.
- *
- * width, height, fps_num and fps_den should be greater than zero.
- *
- * interlacing parameters can be set to zero when the information is
- * unavailable, as hb_apply_h264_level() will disable interlacing if necessary.
- *
- * Returns 0 if the level is valid and compatible, 1 otherwise.
- */
-int hb_check_h264_level(const char *h264_level, int width, int height,
- int fps_num, int fps_den, int interlaced,
- int fake_interlaced);
+typedef struct x264_api_s
+{
+ int bit_depth;
+ void (*param_default)(x264_param_t*);
+ int (*param_default_preset)(x264_param_t*, const char*, const char*);
+ int (*param_apply_profile)(x264_param_t*, const char*);
+ void (*param_apply_fastfirstpass)(x264_param_t*);
+ int (*param_parse)(x264_param_t*, const char*, const char*);
+ x264_t* (*encoder_open)(x264_param_t*);
+ int (*encoder_headers)(x264_t*, x264_nal_t**, int*);
+ int (*encoder_encode)(x264_t*, x264_nal_t**, int*,
+ x264_picture_t*, x264_picture_t*);
+ int (*encoder_delayed_frames)(x264_t*);
+ void (*encoder_close)(x264_t*);
+ void (*picture_init)(x264_picture_t*);
+} x264_api_t;
-/*
- * Applies the restrictions of the requested H.264 level to an x264_param_t.
- *
- * Returns -1 if an invalid level (or no level) is specified. GUIs should be
- * capable of always providing a valid level.
- *
- * Does not modify resolution/framerate but warns when they exceed level limits.
- *
- * Based on a x264_param_apply_level() draft and other x264 code.
- */
-int hb_apply_h264_level(x264_param_t *param, const char *h264_level,
- const char *x264_profile, int verbose);
+void hb_x264_global_init(void);
+const x264_api_t * hb_x264_api_get(int bit_depth);
-/*
- * Applies the restrictions of the requested H.264 profile to an x264_param_t.
- *
- * x264_param_apply_profile wrapper designed to always succeed when a valid
- * H.264 profile is specified (unlike x264's function).
- */
-int hb_apply_h264_profile(x264_param_t *param, const char *h264_profile,
- int verbose);
+#endif // HB_ENCX264_H
diff --git a/libhb/h264_common.h b/libhb/h264_common.h
index fda77fee9..e7be2db40 100644
--- a/libhb/h264_common.h
+++ b/libhb/h264_common.h
@@ -10,7 +10,8 @@
#ifndef HB_H264_COMMON_H
#define HB_H264_COMMON_H
-static const char * const hb_h264_profile_names[] = { "auto", "high", "main", "baseline", NULL, };
+static const char * const hb_h264_profile_names_8bit[] = { "auto", "high", "main", "baseline", NULL, };
+static const char * const hb_h264_profile_names_10bit[] = { "auto", "high10", NULL, };
static const char * const hb_h264_level_names[] = { "auto", "1.0", "1b", "1.1", "1.2", "1.3", "2.0", "2.1", "2.2", "3.0", "3.1", "3.2", "4.0", "4.1", "4.2", "5.0", "5.1", "5.2", NULL, };
static const int const hb_h264_level_values[] = { -1, 10, 9, 11, 12, 13, 20, 21, 22, 30, 31, 32, 40, 41, 42, 50, 51, 52, 0, };
diff --git a/libhb/hb.c b/libhb/hb.c
index dedeadaf5..092e728bd 100644
--- a/libhb/hb.c
+++ b/libhb/hb.c
@@ -10,6 +10,7 @@
#include "hb.h"
#include "opencl.h"
#include "hbffmpeg.h"
+#include "encx264.h"
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
@@ -1772,6 +1773,7 @@ int hb_global_init()
hb_register(&hb_encqsv);
#endif
+ hb_x264_global_init();
hb_common_global_init();
/*
diff --git a/libhb/hb_dict.c b/libhb/hb_dict.c
index 1eb42b059..e634d8efd 100644
--- a/libhb/hb_dict.c
+++ b/libhb/hb_dict.c
@@ -591,7 +591,7 @@ hb_dict_t * hb_encopts_to_dict(const char * encopts, int encoder)
value++;
}
// x264 has multiple names for some options
- if (encoder == HB_VCODEC_X264)
+ if (encoder & HB_VCODEC_X264_MASK)
name = hb_x264_encopt_name(name);
#ifdef USE_X265
// x265 has multiple names for some options
diff --git a/libhb/muxavformat.c b/libhb/muxavformat.c
index 373c2ab75..1bbece7be 100644
--- a/libhb/muxavformat.c
+++ b/libhb/muxavformat.c
@@ -216,7 +216,8 @@ static int avformatInit( hb_mux_object_t * m )
int priv_size = 0;
switch (job->vcodec)
{
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
case HB_VCODEC_QSV_H264:
track->st->codec->codec_id = AV_CODEC_ID_H264;
diff --git a/libhb/ports.c b/libhb/ports.c
index 1bc4f283a..b23f1ff32 100644
--- a/libhb/ports.c
+++ b/libhb/ports.c
@@ -42,6 +42,7 @@
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
+#include <dlfcn.h>
#endif
#ifdef SYS_CYGWIN
@@ -1353,3 +1354,34 @@ void hb_system_sleep_private_disable(void *opaque)
}
#endif
}
+
+void * hb_dlopen(const char *name)
+{
+#ifdef SYS_MINGW
+ HMODULE h = LoadLibraryA(name);
+#else
+ void *h = dlopen(name, RTLD_LAZY | RTLD_LOCAL);
+#endif
+
+ return h;
+}
+
+void * hb_dlsym(void *h, const char *name)
+{
+#ifdef SYS_MINGW
+ FARPROC p = GetProcAddress(h, name);
+#else
+ void *p = dlsym(h, name);
+#endif
+ return p;
+}
+
+int hb_dlclose(void *h)
+{
+#ifdef SYS_MINGW
+ return FreeLibrary(h);
+#else
+ return dlclose(h);
+#endif
+}
+
diff --git a/libhb/ports.h b/libhb/ports.h
index 941c8feee..b23bd64a0 100644
--- a/libhb/ports.h
+++ b/libhb/ports.h
@@ -175,6 +175,21 @@ void hb_system_sleep_opaque_close(void **opaque);
void hb_system_sleep_private_enable(void *opaque);
void hb_system_sleep_private_disable(void *opaque);
+/************************************************************************
+* Loadable Libraries
+***********************************************************************/
+void * hb_dlopen(const char *name);
+void * hb_dlsym(void *h, const char *name);
+int hb_dlclose(void *h);
+
+#if defined( SYS_MINGW )
+#define HB_SO_EXT ".dll"
+#elif defined( SYS_DARWIN )
+#define HB_SO_EXT ".dylib"
+#else
+#define HB_SO_EXT ".so"
+#endif
+
#endif /* __LIBHB__ */
#endif
diff --git a/libhb/preset.c b/libhb/preset.c
index b2df84e3c..2a63458cd 100644
--- a/libhb/preset.c
+++ b/libhb/preset.c
@@ -1325,7 +1325,7 @@ int hb_preset_apply_video(const hb_dict_t *preset, hb_dict_t *job_dict)
hb_dict_set(video_dict, "ColorMatrixCode", hb_value_dup(color_value));
hb_dict_set(video_dict, "Encoder", hb_value_dup(vcodec_value));
- if (vcodec == HB_VCODEC_X264 &&
+ if ((vcodec & HB_VCODEC_X264_MASK) &&
hb_value_get_bool(hb_dict_get(preset, "x264UseAdvancedOptions")))
{
hb_dict_set(video_dict, "Options",
diff --git a/libhb/qsv_common.c b/libhb/qsv_common.c
index bbe9a1fe5..e59437ea6 100644
--- a/libhb/qsv_common.c
+++ b/libhb/qsv_common.c
@@ -1605,7 +1605,7 @@ const char* const* hb_qsv_profile_get_names(int encoder)
switch (encoder)
{
case HB_VCODEC_QSV_H264:
- return hb_h264_profile_names;
+ return hb_h264_profile_names_8bit;
case HB_VCODEC_QSV_H265:
return hb_h265_profile_names;
default:
diff --git a/libhb/work.c b/libhb/work.c
index 186178d7c..bb122f479 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -216,7 +216,8 @@ hb_work_object_t* hb_video_encoder(hb_handle_t *h, int vcodec)
w = hb_get_work(h, WORK_ENCAVCODEC);
w->codec_param = AV_CODEC_ID_VP8;
break;
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
w = hb_get_work(h, WORK_ENCX264);
break;
case HB_VCODEC_QSV_H264:
@@ -412,7 +413,8 @@ void hb_display_job_info(hb_job_t *job)
{
switch (job->vcodec)
{
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
case HB_VCODEC_X265:
case HB_VCODEC_QSV_H264:
case HB_VCODEC_QSV_H265:
@@ -425,7 +427,8 @@ void hb_display_job_info(hb_job_t *job)
{
switch (job->vcodec)
{
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
case HB_VCODEC_X265:
hb_log(" + tune: %s", job->encoder_tune);
default:
@@ -441,7 +444,8 @@ void hb_display_job_info(hb_job_t *job)
{
switch (job->vcodec)
{
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
case HB_VCODEC_X265:
case HB_VCODEC_QSV_H264:
case HB_VCODEC_QSV_H265:
@@ -454,7 +458,8 @@ void hb_display_job_info(hb_job_t *job)
{
switch (job->vcodec)
{
- case HB_VCODEC_X264:
+ case HB_VCODEC_X264_8BIT:
+ case HB_VCODEC_X264_10BIT:
case HB_VCODEC_QSV_H264:
case HB_VCODEC_QSV_H265:
hb_log(" + level: %s", job->encoder_level);
@@ -472,10 +477,10 @@ void hb_display_job_info(hb_job_t *job)
{
hb_log( " + bitrate: %d kbps, pass: %d", job->vbitrate, job->pass_id );
if(job->pass_id == HB_PASS_ENCODE_1ST && job->fastfirstpass == 1 &&
- (job->vcodec == HB_VCODEC_X264 || job->vcodec == HB_VCODEC_X265))
+ ((job->vcodec & HB_VCODEC_X264_MASK) || job->vcodec == HB_VCODEC_X265))
{
hb_log( " + fast first pass" );
- if (job->vcodec == HB_VCODEC_X264)
+ if (job->vcodec & HB_VCODEC_X264_MASK)
{
hb_log( " + options: ref=1:8x8dct=0:me=dia:trellis=0" );
hb_log( " analyse=i4x4 (if originally enabled, else analyse=none)" );
@@ -484,7 +489,7 @@ void hb_display_job_info(hb_job_t *job)
}
}
- if (job->color_matrix_code && job->vcodec == HB_VCODEC_X264)
+ if (job->color_matrix_code && (job->vcodec == HB_VCODEC_X264_MASK))
{
// color matrix is set:
// 1) at the stream level (x264 only),
diff --git a/macosx/HBJob+HBJobConversion.m b/macosx/HBJob+HBJobConversion.m
index a07ca98ff..f19e3c125 100644
--- a/macosx/HBJob+HBJobConversion.m
+++ b/macosx/HBJob+HBJobConversion.m
@@ -109,7 +109,7 @@
job->ipod_atom = self.mp4iPodCompatible;
}
- if (self.video.twoPass && (self.video.encoder == HB_VCODEC_X264 ||
+ if (self.video.twoPass && ((self.video.encoder & HB_VCODEC_X264_MASK) ||
self.video.encoder == HB_VCODEC_X265))
{
job->fastfirstpass = self.video.turboTwoPass;
diff --git a/macosx/HBVideo+UIAdditions.m b/macosx/HBVideo+UIAdditions.m
index f0e6b2678..0d6aaf1c1 100644
--- a/macosx/HBVideo+UIAdditions.m
+++ b/macosx/HBVideo+UIAdditions.m
@@ -83,7 +83,8 @@
- (BOOL)turboTwoPassSupported
{
- return (self.encoder == HB_VCODEC_X264 || self.encoder == HB_VCODEC_X265);
+ return ((self.encoder & HB_VCODEC_X264_MASK) ||
+ self.encoder == HB_VCODEC_X265);
}
/**
@@ -92,7 +93,7 @@
*/
- (NSString *)unparseOptions
{
- if (self.encoder != HB_VCODEC_X264)
+ if (!(self.encoder & HB_VCODEC_X264_MASK))
{
return @"";
}
@@ -132,12 +133,12 @@
}
// now, unparse
- char *fX264PresetsUnparsedUTF8String = hb_x264_param_unparse(x264_preset,
- x264_tune,
- advanced_opts,
- h264_profile,
- h264_level,
- self.job.picture.width, self.job.picture.height);
+ char *fX264PresetsUnparsedUTF8String = hb_x264_param_unparse(
+ hb_video_encoder_get_depth(self.encoder),
+ x264_preset, x264_tune, advanced_opts,
+ h264_profile, h264_level,
+ self.job.picture.width,
+ self.job.picture.height);
// update the text field
if (fX264PresetsUnparsedUTF8String != NULL)
{
diff --git a/macosx/HBVideo.m b/macosx/HBVideo.m
index 604e4aecc..70b7c09a1 100644
--- a/macosx/HBVideo.m
+++ b/macosx/HBVideo.m
@@ -576,7 +576,7 @@ NSString * const HBVideoChangedNotification = @"HBVideoChangedNotification";
if (hb_video_encoder_get_presets(self.encoder) != NULL)
{
- if (self.encoder == HB_VCODEC_X264 && [preset[@"x264UseAdvancedOptions"] boolValue])
+ if ((self.encoder & HB_VCODEC_X264_MASK) && [preset[@"x264UseAdvancedOptions"] boolValue])
{
// preset does not use the x264 preset system, reset the widgets.
self.preset = @"medium";
diff --git a/macosx/HBVideoController.m b/macosx/HBVideoController.m
index 8ade4da8b..9a0ecb14a 100644
--- a/macosx/HBVideoController.m
+++ b/macosx/HBVideoController.m
@@ -128,7 +128,7 @@ static void *HBVideoControllerContext = &HBVideoControllerContext;
}
else if ([keyPath isEqualToString:@"video.unparseOptions"])
{
- if (self.video.encoder == HB_VCODEC_X264)
+ if (self.video.encoder & HB_VCODEC_X264_MASK)
{
fDisplayX264PresetsUnparseTextField.stringValue = [NSString stringWithFormat:@"x264 Unparse: %@", self.video.unparseOptions];
}
@@ -214,7 +214,7 @@ static void *HBVideoControllerContext = &HBVideoControllerContext;
fPresetsBox.contentView = fPresetView;
[self setupPresetsSlider];
- if (self.video.encoder == HB_VCODEC_X264)
+ if (self.video.encoder & HB_VCODEC_X264_MASK)
{
self.advancedController.hidden = NO;
}
@@ -250,7 +250,7 @@ static void *HBVideoControllerContext = &HBVideoControllerContext;
*/
- (void)toggleAdvancedOptionsCheckBoxForEncoder:(int)encoder
{
- if ([[NSUserDefaults standardUserDefaults] boolForKey:@"HBShowAdvancedTab"] && (encoder == HB_VCODEC_X264))
+ if ([[NSUserDefaults standardUserDefaults] boolForKey:@"HBShowAdvancedTab"] && (encoder & HB_VCODEC_X264_MASK))
{
fX264UseAdvancedOptionsCheck.hidden = NO;
fDividerLine.hidden = YES;