diff options
-rw-r--r-- | gtk/src/callbacks.c | 7 | ||||
-rw-r--r-- | gtk/src/hb-backend.c | 6 | ||||
-rw-r--r-- | gtk/src/makedeps.py | 10 | ||||
-rw-r--r-- | gtk/src/queuehandler.c | 6 | ||||
-rw-r--r-- | gtk/src/videohandler.c | 6 | ||||
-rw-r--r-- | libhb/common.c | 67 | ||||
-rw-r--r-- | libhb/common.h | 17 | ||||
-rw-r--r-- | libhb/encx264.c | 442 | ||||
-rw-r--r-- | libhb/encx264.h | 56 | ||||
-rw-r--r-- | libhb/h264_common.h | 3 | ||||
-rw-r--r-- | libhb/hb.c | 2 | ||||
-rw-r--r-- | libhb/hb_dict.c | 2 | ||||
-rw-r--r-- | libhb/muxavformat.c | 3 | ||||
-rw-r--r-- | libhb/ports.c | 32 | ||||
-rw-r--r-- | libhb/ports.h | 15 | ||||
-rw-r--r-- | libhb/preset.c | 2 | ||||
-rw-r--r-- | libhb/qsv_common.c | 2 | ||||
-rw-r--r-- | libhb/work.c | 21 | ||||
-rw-r--r-- | macosx/HBJob+HBJobConversion.m | 2 | ||||
-rw-r--r-- | macosx/HBVideo+UIAdditions.m | 17 | ||||
-rw-r--r-- | macosx/HBVideo.m | 2 | ||||
-rw-r--r-- | macosx/HBVideoController.m | 6 |
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(¶m, + if (pv->api->param_default_preset(¶m, 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(¶m, key, str); + ret = pv->api->param_parse(¶m, 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(¶m, job->encoder_profile, 1)) + if (apply_h264_profile(pv->api, ¶m, 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(¶m, job->encoder_level, - job->encoder_profile, 1) < 0) + if (apply_h264_level(pv->api, ¶m, 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( ¶m ); + pv->api->param_apply_fastfirstpass( ¶m ); } /* 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( ¶m ); + pv->x264 = pv->api->encoder_open( ¶m ); 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(¶m); @@ -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(¶m, h264_level, NULL, 0) != 0); + return (apply_h264_level(¶m, 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(¶m, x264_preset, x264_tune) < 0) + if (api->param_default_preset(¶m, 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(¶m, key, str); + api->param_parse(¶m, 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(¶m, h264_profile, 0); + apply_h264_profile(api, ¶m, 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(¶m, h264_level, h264_profile, 0); + apply_h264_level(api, ¶m, 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; |