]> granicus.if.org Git - handbrake/commitdiff
Add VideoToolbox hardware encoding thru FFmpeg.
authorDamiano Galassi <damiog@gmail.com>
Thu, 15 Nov 2018 12:56:16 +0000 (13:56 +0100)
committerDamiano Galassi <damiog@gmail.com>
Thu, 15 Nov 2018 12:56:16 +0000 (13:56 +0100)
15 files changed:
contrib/ffmpeg/A04-videotoolbox.patch [new file with mode: 0644]
contrib/ffmpeg/module.defs
libhb/common.c
libhb/common.h
libhb/encavcodec.c
libhb/muxavformat.c
libhb/platform/macosx/vt_common.c [new file with mode: 0644]
libhb/platform/macosx/vt_common.h [new file with mode: 0644]
libhb/work.c
macosx/Base.lproj/Video.xib
macosx/HBVideo+UIAdditions.h
macosx/HBVideo+UIAdditions.m
macosx/HBVideo.m
macosx/HandBrake.xcodeproj/project.pbxproj
test/module.defs

diff --git a/contrib/ffmpeg/A04-videotoolbox.patch b/contrib/ffmpeg/A04-videotoolbox.patch
new file mode 100644 (file)
index 0000000..387af0d
--- /dev/null
@@ -0,0 +1,27 @@
+diff --git a/libavcodec/videotoolboxenc.c b/libavcodec/videotoolboxenc.c
+index 7796a68..e8b6245 100644
+--- a/libavcodec/videotoolboxenc.c
++++ b/libavcodec/videotoolboxenc.c
+@@ -866,6 +866,14 @@ static int get_cv_color_primaries(AVCodecContext *avctx,
+             *primaries = NULL;
+             break;
++        case AVCOL_PRI_BT470BG:
++                      *primaries = kCVImageBufferColorPrimaries_EBU_3213;
++            break;
++
++        case AVCOL_PRI_SMPTE170M:
++                      *primaries = kCVImageBufferColorPrimaries_SMPTE_C;
++            break;
++
+         case AVCOL_PRI_BT709:
+             *primaries = kCVImageBufferColorPrimaries_ITU_R_709_2;
+             break;
+@@ -1302,6 +1310,7 @@ static av_cold int vtenc_init(AVCodecContext *avctx)
+         vtctx->get_param_set_func = compat_keys.CMVideoFormatDescriptionGetHEVCParameterSetAtIndex;
+         if (!vtctx->get_param_set_func) return AVERROR(EINVAL);
+         if (!get_vt_hevc_profile_level(avctx, &profile_level)) return AVERROR(EINVAL);
++        vtctx->has_b_frames = avctx->max_b_frames > 0;
+     }
+     vtctx->session = NULL;
index 87f236380b85a10aed071ed0070f6049cb11b182..d2b41be0cbd9e1f15dfa76c8230ea73509ee7afe 100644 (file)
@@ -75,7 +75,10 @@ FFMPEG.CONFIGURE.extra += \
     --enable-muxer=ipod
 
 ifeq (darwin,$(BUILD.system))
-    FFMPEG.CONFIGURE.extra += --disable-audiotoolbox --disable-coreimage --disable-videotoolbox
+    FFMPEG.CONFIGURE.extra += --disable-audiotoolbox --disable-coreimage \
+        --enable-encoder=h264_videotoolbox \
+        --enable-encoder=hevc_videotoolbox
+
     ifeq (x86_64,$(BUILD.arch))
         FFMPEG.CONFIGURE.extra += --arch=x86_64
     endif
index 2d668f810d5969255c9467de43f1ed6ddd0c51cd..16085bb4464be02dadea36a545c2a5d04ef086a2 100644 (file)
 #include "vce_common.h"
 #endif
 
+#ifdef __APPLE__
+#include "platform/macosx/vt_common.h"
+#endif
+
 static int mixdown_get_opus_coupled_stream_count(int mixdown);
 
 /**********************************************************************
@@ -246,6 +250,7 @@ hb_encoder_internal_t hb_video_encoders[]  =
     { { "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.264 (AMD VCE)",     "vce_h264",   "H.264 (AMD VCE)",      HB_VCODEC_FFMPEG_VCE_H264,   HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264,   },
     { { "H.264 (NVEnc)",       "nvenc_h264", "H.264 (NVEnc)",      HB_VCODEC_FFMPEG_NVENC_H264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264,   },
+    { { "H.264 (VideoToolbox)","vt_h264",    "H.264 (libavcodec)",      HB_VCODEC_FFMPEG_VT_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_8BIT,         HB_MUX_AV_MP4|HB_MUX_AV_MKV,   }, NULL, 1, HB_GID_VCODEC_H265,   },
     { { "H.265 10-bit (x265)", "x265_10bit", "H.265 10-bit (libx265)",  HB_VCODEC_X265_10BIT,        HB_MUX_AV_MP4|HB_MUX_AV_MKV,   }, NULL, 1, HB_GID_VCODEC_H265,   },
     { { "H.265 12-bit (x265)", "x265_12bit", "H.265 12-bit (libx265)",  HB_VCODEC_X265_12BIT,        HB_MUX_AV_MP4|HB_MUX_AV_MKV,   }, NULL, 1, HB_GID_VCODEC_H265,   },
@@ -254,6 +259,7 @@ hb_encoder_internal_t hb_video_encoders[]  =
     { { "H.265 10-bit (Intel QSV)","qsv_h265_10bit", "H.265 10-bit (Intel Media SDK)", HB_VCODEC_QSV_H265_10BIT,     HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H265,   },
     { { "H.265 (AMD VCE)",     "vce_h265",   "H.265 (AMD VCE)",      HB_VCODEC_FFMPEG_VCE_H265,   HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H265,   },
     { { "H.265 (NVEnc)",       "nvenc_h265", "H.265 (NVEnc)",      HB_VCODEC_FFMPEG_NVENC_H265, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H265,   },
+    { { "H.265 (VideoToolbox)","vt_h265",    "H.265 (libavcodec)",      HB_VCODEC_FFMPEG_VT_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,    },
@@ -293,6 +299,13 @@ static int hb_video_encoder_is_enabled(int encoder)
             return hb_nvenc_h265_available();
 #endif
 
+#ifdef __APPLE__
+        case HB_VCODEC_FFMPEG_VT_H264:
+            return hb_vt_h264_is_available();
+        case HB_VCODEC_FFMPEG_VT_H265:
+            return hb_vt_h265_is_available();
+#endif
+
 #ifdef USE_X265
         case HB_VCODEC_X265_8BIT:
         case HB_VCODEC_X265_10BIT:
@@ -1365,6 +1378,14 @@ void hb_video_quality_get_limits(uint32_t codec, float *low, float *high,
             *high        = 63.;
             break;
 
+        case HB_VCODEC_FFMPEG_VT_H264:
+        case HB_VCODEC_FFMPEG_VT_H265:
+            *direction   = 1;
+            *granularity = 0.1;
+            *low         = 0.;
+            *high        = 0.;
+            break;
+
         case HB_VCODEC_FFMPEG_MPEG2:
         case HB_VCODEC_FFMPEG_MPEG4:
         default:
@@ -1511,6 +1532,8 @@ const char* const* hb_video_encoder_get_profiles(int encoder)
 
         case HB_VCODEC_FFMPEG_NVENC_H264:
         case HB_VCODEC_FFMPEG_NVENC_H265:
+        case HB_VCODEC_FFMPEG_VT_H264:
+        case HB_VCODEC_FFMPEG_VT_H265:
             return hb_av_profile_get_names(encoder);
         default:
             return NULL;
@@ -1531,6 +1554,7 @@ const char* const* hb_video_encoder_get_levels(int encoder)
         case HB_VCODEC_X264_8BIT:
         case HB_VCODEC_X264_10BIT:
         case HB_VCODEC_FFMPEG_NVENC_H264:
+        case HB_VCODEC_FFMPEG_VT_H264:
             return hb_h264_level_names;
 
 #ifdef USE_VCE
@@ -1546,6 +1570,11 @@ const char* const* hb_video_encoder_get_levels(int encoder)
         case HB_VCODEC_FFMPEG_VCE_H265:
             return hb_h265_level_names;
 
+#ifdef __APPLE__
+        case HB_VCODEC_FFMPEG_VT_H265:
+            return hb_vt_h265_level_names;
+#endif
+
         default:
             return NULL;
     }
index 25b2bab994bb3ed3326a01c0a3e02c8cc9a0bf76..a3dca1e59cfc5889415da9678f6e99379b4735b6 100644 (file)
@@ -512,7 +512,9 @@ struct hb_job_s
 #define HB_VCODEC_FFMPEG_VCE_H265 0x00080000
 #define HB_VCODEC_FFMPEG_NVENC_H264 0x00100000
 #define HB_VCODEC_FFMPEG_NVENC_H265 0x00200000
-#define HB_VCODEC_FFMPEG_MASK  (0x00000F0|HB_VCODEC_FFMPEG_VCE_H264|HB_VCODEC_FFMPEG_VCE_H265|HB_VCODEC_FFMPEG_NVENC_H264|HB_VCODEC_FFMPEG_NVENC_H265)
+#define HB_VCODEC_FFMPEG_VT_H264 0x00400000
+#define HB_VCODEC_FFMPEG_VT_H265 0x00800000
+#define HB_VCODEC_FFMPEG_MASK  (0x00000F0|HB_VCODEC_FFMPEG_VCE_H264|HB_VCODEC_FFMPEG_VCE_H265|HB_VCODEC_FFMPEG_NVENC_H264|HB_VCODEC_FFMPEG_NVENC_H265|HB_VCODEC_FFMPEG_VT_H264|HB_VCODEC_FFMPEG_VT_H265)
 #define HB_VCODEC_QSV_H264     0x0000100
 #define HB_VCODEC_QSV_H265_8BIT     0x0000200
 #define HB_VCODEC_QSV_H265_10BIT    0x0000400
@@ -523,14 +525,14 @@ struct hb_job_s
 #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|HB_VCODEC_FFMPEG_VCE_H264|HB_VCODEC_FFMPEG_NVENC_H264)
+#define HB_VCODEC_H264_MASK    (HB_VCODEC_X264_MASK|HB_VCODEC_QSV_H264|HB_VCODEC_FFMPEG_VCE_H264|HB_VCODEC_FFMPEG_NVENC_H264|HB_VCODEC_FFMPEG_VT_H264)
 #define HB_VCODEC_X265_8BIT    0x0001000
 #define HB_VCODEC_X265         HB_VCODEC_X265_8BIT
 #define HB_VCODEC_X265_10BIT   0x0002000
 #define HB_VCODEC_X265_12BIT   0x0004000
 #define HB_VCODEC_X265_16BIT   0x0008000
 #define HB_VCODEC_X265_MASK    0x000F000
-#define HB_VCODEC_H265_MASK    (HB_VCODEC_X265_MASK|HB_VCODEC_QSV_H265_MASK|HB_VCODEC_FFMPEG_VCE_H265|HB_VCODEC_FFMPEG_NVENC_H265)
+#define HB_VCODEC_H265_MASK    (HB_VCODEC_X265_MASK|HB_VCODEC_QSV_H265_MASK|HB_VCODEC_FFMPEG_VCE_H265|HB_VCODEC_FFMPEG_NVENC_H265|HB_VCODEC_FFMPEG_VT_H265)
 
 /* define an invalid CQ value compatible with all CQ-capable codecs */
 #define HB_INVALID_VIDEO_QUALITY (-1000.)
index dc94310c61e63ef1ae746412d76c418b109f42df..159421533ccc98630eb20e8140b63a1b153c140b 100644 (file)
@@ -83,6 +83,20 @@ static const char * const h265_nvenc_profile_names[] =
     "auto", "main", NULL // "main10", "rext"  We do not currently support 10bit encodes with this encoder. 
 };
 
+static const char * const h26x_vt_preset_name[] =
+{
+    "default", NULL
+};
+
+static const char * const h264_vt_profile_name[] =
+{
+    "auto", "baseline", "main", "high", NULL
+};
+
+static const char * const h265_vt_profile_name[] =
+{
+    "auto", "main", "main10", NULL
+};
 
 int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
 {
@@ -136,6 +150,10 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
                     hb_log("encavcodecInit: H.264 (AMD VCE)");
                     codec = avcodec_find_encoder_by_name("h264_amf");
                     break;
+                case HB_VCODEC_FFMPEG_VT_H264:
+                    hb_log("encavcodecInit: H.264 (VideoToolbox)");
+                    codec = avcodec_find_encoder_by_name("h264_videotoolbox");
+                    break;
             }
         }break;
         case AV_CODEC_ID_HEVC:
@@ -149,6 +167,10 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
                     hb_log("encavcodecInit: H.265 (AMD VCE)");
                     codec = avcodec_find_encoder_by_name("hevc_amf");
                     break;
+                case HB_VCODEC_FFMPEG_VT_H265:
+                    hb_log("encavcodecInit: H.265 (VideoToolbox)");
+                    codec = avcodec_find_encoder_by_name("hevc_videotoolbox");
+                    break;
             }
         }break;
         default:
@@ -406,6 +428,47 @@ int encavcodecInit( hb_work_object_t * w, hb_job_t * job )
         context->flags |= AV_CODEC_FLAG_GRAY;
     }
 
+    if (job->vcodec == HB_VCODEC_FFMPEG_VT_H264)
+    {
+        // Set profile and level
+        if (job->encoder_profile != NULL && *job->encoder_profile)
+        {
+            if (!strcasecmp(job->encoder_profile, "baseline"))
+                av_dict_set(&av_opts, "profile", "baseline", 0);
+            else if (!strcasecmp(job->encoder_profile, "main"))
+                av_dict_set(&av_opts, "profile", "main", 0);
+            else if (!strcasecmp(job->encoder_profile, "high"))
+                av_dict_set(&av_opts, "profile", "high", 0);
+        }
+
+        if (job->encoder_level != NULL && *job->encoder_level)
+        {
+            int i = 1;
+            while (hb_h264_level_names[i] != NULL)
+            {
+                if (!strcasecmp(job->encoder_level, hb_h264_level_names[i]))
+                    av_dict_set(&av_opts, "level", job->encoder_level, 0);
+                ++i;
+            }
+        }
+
+        context->max_b_frames = 16;
+    }
+
+    if (job->vcodec == HB_VCODEC_FFMPEG_VT_H265)
+    {
+        // Set profile and level
+        if (job->encoder_profile != NULL && *job->encoder_profile)
+        {
+            if (!strcasecmp(job->encoder_profile, "main"))
+                av_dict_set(&av_opts, "profile", "main", 0);
+            else if (!strcasecmp(job->encoder_profile, "main10"))
+                av_dict_set(&av_opts, "profile", "main10", 0);
+        }
+
+        context->max_b_frames = 16;
+    }
+
     if (job->vcodec == HB_VCODEC_FFMPEG_VCE_H264)
     {
         // Set profile and level
@@ -935,6 +998,10 @@ const char* const* hb_av_preset_get_names(int encoder)
         case HB_VCODEC_FFMPEG_NVENC_H265:
             return h26x_nvenc_preset_names;
 
+        case HB_VCODEC_FFMPEG_VT_H264:
+        case HB_VCODEC_FFMPEG_VT_H265:
+            return h26x_vt_preset_name;
+
         default:
             return NULL;
     }
@@ -948,6 +1015,10 @@ const char* const* hb_av_profile_get_names(int encoder)
             return h264_nvenc_profile_names;
         case HB_VCODEC_FFMPEG_NVENC_H265:
             return h265_nvenc_profile_names;
+        case HB_VCODEC_FFMPEG_VT_H264:
+            return h264_vt_profile_name;
+        case HB_VCODEC_FFMPEG_VT_H265:
+            return h265_vt_profile_name;
 
          default:
              return NULL;
index 5f2d7fe871b82481efbec5db0b200fd60f8b12c5..9f15f52c1468a981ead0e8c6a13c29414f12188b 100644 (file)
@@ -252,6 +252,7 @@ static int avformatInit( hb_mux_object_t * m )
 
         case HB_VCODEC_FFMPEG_VCE_H264:
         case HB_VCODEC_FFMPEG_NVENC_H264:
+        case HB_VCODEC_FFMPEG_VT_H264:
             track->st->codecpar->codec_id = AV_CODEC_ID_H264;
             if (job->mux == HB_MUX_AV_MP4 && job->inline_parameter_sets)
             {
@@ -387,6 +388,7 @@ static int avformatInit( hb_mux_object_t * m )
 
         case HB_VCODEC_FFMPEG_VCE_H265:
         case HB_VCODEC_FFMPEG_NVENC_H265:
+        case HB_VCODEC_FFMPEG_VT_H265:
             track->st->codecpar->codec_id  = AV_CODEC_ID_HEVC;
             if (job->mux == HB_MUX_AV_MP4 && job->inline_parameter_sets)
             {
diff --git a/libhb/platform/macosx/vt_common.c b/libhb/platform/macosx/vt_common.c
new file mode 100644 (file)
index 0000000..be75018
--- /dev/null
@@ -0,0 +1,63 @@
+/* vt_common.c
+
+   Copyright (c) 2003-2018 HandBrake Team
+   This file is part of the HandBrake source code
+   Homepage: <http://handbrake.fr/>.
+   It may be used under the terms of the GNU General Public License v2.
+   For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
+ */
+
+#include "hb.h"
+#include "vt_common.h"
+
+#include <VideoToolbox/VideoToolbox.h>
+#include <CoreMedia/CoreMedia.h>
+#include <CoreVideo/CoreVideo.h>
+
+//#define VT_STATS
+
+#ifdef VT_STATS
+static void toggle_vt_gva_stats(bool state)
+{
+    CFPropertyListRef cf_state = state ? kCFBooleanTrue : kCFBooleanFalse;
+    CFPreferencesSetValue(CFSTR("gvaEncoderPerf"), cf_state, CFSTR("com.apple.GVAEncoder"), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
+    CFPreferencesSetValue(CFSTR("gvaEncoderPSNR"), cf_state, CFSTR("com.apple.GVAEncoder"), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
+    CFPreferencesSetValue(CFSTR("gvaEncoderSSIM"), cf_state, CFSTR("com.apple.GVAEncoder"), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
+
+    //CFPreferencesSetValue(CFSTR("gvaEncoderStats"), cf_state, CFSTR("com.apple.GVAEncoder"), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
+    //CFPreferencesSetValue(CFSTR("gvaDebug"), cf_state, CFSTR("com.apple.AppleGVA"), kCFPreferencesCurrentUser, kCFPreferencesCurrentHost);
+}
+#endif
+
+static const CFStringRef encoder_id_h264 = CFSTR("com.apple.videotoolbox.videoencoder.h264.gva");
+static const CFStringRef encoder_id_h265 = CFSTR("com.apple.videotoolbox.videoencoder.hevc.gva");
+
+int encvt_available(CFStringRef encoder)
+{
+    CFArrayRef encoder_list;
+    VTCopyVideoEncoderList(NULL, &encoder_list);
+    CFIndex size = CFArrayGetCount(encoder_list);
+
+    for (CFIndex i = 0; i < size; i++ )
+    {
+        CFDictionaryRef encoder_dict = CFArrayGetValueAtIndex(encoder_list, i);
+        CFStringRef encoder_id = CFDictionaryGetValue(encoder_dict, kVTVideoEncoderSpecification_EncoderID);
+        if (CFEqual(encoder_id, encoder))
+        {
+            CFRelease(encoder_list);
+            return 1;
+        }
+    }
+    CFRelease(encoder_list);
+    return 0;
+}
+
+int hb_vt_h264_is_available()
+{
+    return encvt_available(encoder_id_h264);
+}
+
+int hb_vt_h265_is_available()
+{
+    return encvt_available(encoder_id_h265);
+}
diff --git a/libhb/platform/macosx/vt_common.h b/libhb/platform/macosx/vt_common.h
new file mode 100644 (file)
index 0000000..22187d0
--- /dev/null
@@ -0,0 +1,16 @@
+/* vt_common.h
+
+   Copyright (c) 2003-2018 HandBrake Team
+   This file is part of the HandBrake source code
+   Homepage: <http://handbrake.fr/>.
+   It may be used under the terms of the GNU General Public License v2.
+   For full terms see the file COPYING file or visit http://www.gnu.org/licenses/gpl-2.0.html
+ */
+
+int  hb_vt_h264_is_available();
+int  hb_vt_h265_is_available();
+
+static const char * const hb_vt_h265_level_names[] =
+{
+    "auto",  NULL,
+};
index 9a3eb55a79fbb395335f459be583f5a5c2329138..350af0cc7f133c5ca73c1d39bf4ed3c9766b7c00 100644 (file)
@@ -284,6 +284,16 @@ hb_work_object_t* hb_video_encoder(hb_handle_t *h, int vcodec)
             w->codec_param = AV_CODEC_ID_HEVC;
             break;
 #endif
+#ifdef __APPLE__
+        case HB_VCODEC_FFMPEG_VT_H264:
+            w = hb_get_work(h, WORK_ENCAVCODEC);
+            w->codec_param = AV_CODEC_ID_H264;
+            break;
+        case HB_VCODEC_FFMPEG_VT_H265:
+            w = hb_get_work(h, WORK_ENCAVCODEC);
+            w->codec_param = AV_CODEC_ID_HEVC;
+            break;
+#endif
 
         default:
             hb_error("Unknown video codec (0x%x)", vcodec );
index 8f69b26bb6c408ad4f47c4b9748d5a91093f1acf..8bd3cd26283f6e3710737df825b110ba2ce2bc1b 100644 (file)
@@ -86,6 +86,7 @@
                     </textFieldCell>
                     <connections>
                         <binding destination="-2" name="textColor" keyPath="self.labelColor" id="EsL-F5-tAO"/>
+                        <binding destination="-2" name="enabled" keyPath="self.video.isConstantQualitySupported" id="vfD-XL-S4P"/>
                         <binding destination="-2" name="value" keyPath="self.video.quality" id="tMZ-Xb-TuF"/>
                     </connections>
                 </textField>
                         <font key="font" metaFont="smallSystem"/>
                     </buttonCell>
                     <connections>
+                        <binding destination="-2" name="enabled3" keyPath="self.video.twoPassSupported" previousBinding="c4g-dz-q05" id="rwS-GN-5Dd">
+                            <dictionary key="options">
+                                <integer key="NSMultipleValuesPlaceholder" value="-1"/>
+                                <integer key="NSNoSelectionPlaceholder" value="-1"/>
+                                <integer key="NSNotApplicablePlaceholder" value="-1"/>
+                                <integer key="NSNullPlaceholder" value="-1"/>
+                            </dictionary>
+                        </binding>
                         <binding destination="-2" name="enabled2" keyPath="self.video" previousBinding="7aV-7j-MzB" id="c4g-dz-q05">
                             <dictionary key="options">
                                 <integer key="NSMultipleValuesPlaceholder" value="-1"/>
@@ -253,6 +262,14 @@ x264 is lossless at RF 0.</string>
                                 <string key="NSValueTransformerName">NSIsNotNil</string>
                             </dictionary>
                         </binding>
+                        <binding destination="-2" name="enabled3" keyPath="self.video.isConstantQualitySupported" previousBinding="ywk-WQ-GNY" id="riO-a0-Jfa">
+                            <dictionary key="options">
+                                <integer key="NSMultipleValuesPlaceholder" value="-1"/>
+                                <integer key="NSNoSelectionPlaceholder" value="-1"/>
+                                <integer key="NSNotApplicablePlaceholder" value="-1"/>
+                                <integer key="NSNullPlaceholder" value="-1"/>
+                            </dictionary>
+                        </binding>
                         <binding destination="-2" name="value" keyPath="self.video.quality" previousBinding="nAO-gB-Jbd" id="C3d-pR-fJ2">
                             <dictionary key="options">
                                 <string key="NSValueTransformerName">HBQualityTransformer</string>
@@ -273,6 +290,7 @@ x264 is lossless at RF 0.</string>
                     <connections>
                         <binding destination="-2" name="textColor" keyPath="self.labelColor" id="S90-zY-jeW"/>
                         <binding destination="-2" name="value" keyPath="self.video.constantQualityLabel" id="ri5-aE-FP5"/>
+                        <binding destination="-2" name="enabled" keyPath="self.video.isConstantQualitySupported" id="6YN-nu-K6b"/>
                     </connections>
                 </textField>
                 <box autoresizesSubviews="NO" boxType="custom" borderType="none" title="x264 Presets" titlePosition="noTitle" transparent="YES" translatesAutoresizingMaskIntoConstraints="NO" id="A4U-3F-pYq">
@@ -289,6 +307,14 @@ x264 is lossless at RF 0.</string>
                         <font key="font" metaFont="smallSystem"/>
                     </buttonCell>
                     <connections>
+                        <binding destination="-2" name="enabled2" keyPath="self.video.isConstantQualitySupported" previousBinding="Ewd-OO-T3Z" id="cEw-6S-gon">
+                            <dictionary key="options">
+                                <integer key="NSMultipleValuesPlaceholder" value="-1"/>
+                                <integer key="NSNoSelectionPlaceholder" value="-1"/>
+                                <integer key="NSNotApplicablePlaceholder" value="-1"/>
+                                <integer key="NSNullPlaceholder" value="-1"/>
+                            </dictionary>
+                        </binding>
                         <binding destination="-2" name="enabled" keyPath="self.video" id="Ewd-OO-T3Z">
                             <dictionary key="options">
                                 <string key="NSValueTransformerName">NSIsNotNil</string>
index 5ce7648a7de5e2d9267b89cf8ef8f94928e9d5b9..2360969161ca7ee65ddca389955deb12de7ea5c4 100644 (file)
@@ -21,6 +21,7 @@
 @property (nonatomic, readonly) NSArray *levels;
 
 @property (nonatomic, readonly) BOOL fastDecodeSupported;
+@property (nonatomic, readonly) BOOL twoPassSupported;
 @property (nonatomic, readonly) BOOL turboTwoPassSupported;
 
 @property (nonatomic, readonly) NSString *unparseOptions;
@@ -29,6 +30,7 @@
 
 @property (nonatomic, readonly) double qualityMinValue;
 @property (nonatomic, readonly) double qualityMaxValue;
+@property (nonatomic, readonly) BOOL isConstantQualitySupported;
 
 @end
 
index e23c1ab72fc6e0387fcc94e15048316d6f71a760..d7cbc0eda598a78b8eca7cd74fbe74995e77d1fd 100644 (file)
             (self.encoder & HB_VCODEC_X265_MASK));
 }
 
++ (NSSet<NSString *> *)keyPathsForValuesAffectingTwoPassSupported
+{
+    return [NSSet setWithObjects:@"encoder", nil];
+}
+
+- (BOOL)twoPassSupported
+{
+    return !((self.encoder & HB_VCODEC_FFMPEG_VT_H264) ||
+            (self.encoder & HB_VCODEC_FFMPEG_VT_H265));
+}
+
 + (NSSet<NSString *> *)keyPathsForValuesAffectingConstantQualityLabel
 {
     return [NSSet setWithObjects:@"encoder", nil];
     return @(hb_video_quality_get_name(self.encoder));
 }
 
++ (NSSet<NSString *> *)keyPathsForValuesAffectingIsConstantQualitySupported
+{
+    return [NSSet setWithObjects:@"encoder", nil];
+}
+
+- (BOOL)isConstantQualitySupported
+{
+    return (self.qualityMaxValue == 0 && self.qualityMinValue == 0) == NO;
+}
+
 + (NSSet<NSString *> *)keyPathsForValuesAffectingUnparseOptions
 {
     return [NSSet setWithObjects:@"encoder", @"preset", @"tune", @"profile", @"level",
index 15c2f67ae6fa12c6ae296262bfb3d4d3414da877..1119aaa5cc7676b92a52e6550a9114cbeac1266d 100644 (file)
@@ -105,6 +105,7 @@ NSString * const HBVideoChangedNotification = @"HBVideoChangedNotification";
 
     if (!(self.undo.isUndoing || self.undo.isRedoing))
     {
+        [self validateQualityType];
         [self validatePresetsSettings];
         [self validateVideoOptionExtra:previousEncoder];
     }
@@ -281,11 +282,42 @@ NSString * const HBVideoChangedNotification = @"HBVideoChangedNotification";
     [self postChangedNotification];
 }
 
+- (void)validateQualityType
+{
+    if (self.qualityType != 0)
+    {
+        int direction;
+        float minValue, maxValue, granularity;
+        hb_video_quality_get_limits(self.encoder,
+                                    &minValue, &maxValue, &granularity, &direction);
+
+        if (minValue == 0 && maxValue == 0)
+        {
+            self.qualityType = 0;
+        }
+    }
+    else
+    {
+        if ((self.encoder & HB_VCODEC_FFMPEG_VT_H264) ||
+            (self.encoder & HB_VCODEC_FFMPEG_VT_H265))
+        {
+            self.twoPass = NO;
+        }
+    }
+}
+
 - (void)validatePresetsSettings
 {
     NSArray *presets = self.presets;
     if (presets.count && ![presets containsObject:self.preset]) {
-        self.preset = presets[self.mediumPresetIndex];
+        if (presets.count > self.mediumPresetIndex)
+        {
+            self.preset = presets[self.mediumPresetIndex];
+        }
+        else
+        {
+            self.preset = presets.firstObject;
+        }
     }
 
     NSArray *tunes = self.tunes;
index 586fcae64f8822afdc73508560946cdfe86e02ed..f0ad8c6d2136e14cbda6cde3fb594a5ae77ffae0 100644 (file)
                A9906B2C1A710920001D82D5 /* HBQueueController.m in Sources */ = {isa = PBXBuildFile; fileRef = A9906B2B1A710920001D82D5 /* HBQueueController.m */; };
                A99F40CF1B624E7E00750170 /* HBPictureViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A99F40CD1B624E7E00750170 /* HBPictureViewController.m */; };
                A9A0CBE81CCEA3670045B3DF /* HBPlayerTrack.m in Sources */ = {isa = PBXBuildFile; fileRef = A9A0CBE61CCEA1D10045B3DF /* HBPlayerTrack.m */; };
+               A9A25D9C21182741005A8A0F /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9A25D9B21182741005A8A0F /* CoreMedia.framework */; };
+               A9A25D9D21182753005A8A0F /* CoreMedia.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9A25D9B21182741005A8A0F /* CoreMedia.framework */; };
                A9A7E27C1FE2A0B5006BE79F /* HBPreviewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = A9A7E27A1FE2A0B5006BE79F /* HBPreviewViewController.m */; };
                A9A96B8220CAD2C200A39AFB /* HBPictureHUDController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9A96B8420CAD2C200A39AFB /* HBPictureHUDController.xib */; };
                A9A96B8520CAD2CC00A39AFB /* HBEncodingProgressHUDController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9A96B8720CAD2CC00A39AFB /* HBEncodingProgressHUDController.xib */; };
                A9A96BDD20CAD66000A39AFB /* OutputPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9A96BDB20CAD66000A39AFB /* OutputPanel.xib */; };
                A9A96BE020CAD66500A39AFB /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9A96BDE20CAD66500A39AFB /* Preferences.xib */; };
                A9A96BE320CAD6CD00A39AFB /* HBPreviewViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = A9A96BE120CAD6CD00A39AFB /* HBPreviewViewController.xib */; };
+               A9AB9AA5211819A500BB3C7E /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9AB9AA4211819A500BB3C7E /* VideoToolbox.framework */; };
+               A9AB9AA621181CA900BB3C7E /* VideoToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9AB9AA4211819A500BB3C7E /* VideoToolbox.framework */; };
+               A9AB9AA821181CC700BB3C7E /* CoreVideo.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9AB9AA721181CC700BB3C7E /* CoreVideo.framework */; };
                A9ABD1A61E2A0F0700EC8B65 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9ABD1A51E2A0F0700EC8B65 /* CoreText.framework */; };
                A9ABD1A71E2A0F7500EC8B65 /* CoreText.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9ABD1A51E2A0F0700EC8B65 /* CoreText.framework */; };
                A9ABD1A91E2A0F8200EC8B65 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A9ABD1A81E2A0F8200EC8B65 /* CoreGraphics.framework */; };
                A99F40CD1B624E7E00750170 /* HBPictureViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBPictureViewController.m; sourceTree = "<group>"; };
                A9A0CBE51CCEA1D10045B3DF /* HBPlayerTrack.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HBPlayerTrack.h; sourceTree = "<group>"; };
                A9A0CBE61CCEA1D10045B3DF /* HBPlayerTrack.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HBPlayerTrack.m; sourceTree = "<group>"; };
+               A9A25D9B21182741005A8A0F /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
                A9A7E2791FE2A0B5006BE79F /* HBPreviewViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HBPreviewViewController.h; sourceTree = "<group>"; };
                A9A7E27A1FE2A0B5006BE79F /* HBPreviewViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HBPreviewViewController.m; sourceTree = "<group>"; };
                A9A96B8320CAD2C200A39AFB /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/HBPictureHUDController.xib; sourceTree = "<group>"; };
                A9AA447B1970724D00D7DEFC /* HBAdvancedController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HBAdvancedController.h; sourceTree = "<group>"; };
                A9AA447C1970726500D7DEFC /* HBQueueController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HBQueueController.h; sourceTree = "<group>"; };
                A9AA447D1970729300D7DEFC /* HBPreviewGenerator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HBPreviewGenerator.h; sourceTree = "<group>"; };
+               A9AB9AA4211819A500BB3C7E /* VideoToolbox.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = VideoToolbox.framework; path = System/Library/Frameworks/VideoToolbox.framework; sourceTree = SDKROOT; };
+               A9AB9AA721181CC700BB3C7E /* CoreVideo.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreVideo.framework; path = System/Library/Frameworks/CoreVideo.framework; sourceTree = SDKROOT; };
                A9ABD1A51E2A0F0700EC8B65 /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
                A9ABD1A81E2A0F8200EC8B65 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; };
                A9B34D74197696FE00871B7D /* DiskArbitration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = DiskArbitration.framework; path = System/Library/Frameworks/DiskArbitration.framework; sourceTree = SDKROOT; };
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               A9A25D9C21182741005A8A0F /* CoreMedia.framework in Frameworks */,
+                               A9AB9AA821181CC700BB3C7E /* CoreVideo.framework in Frameworks */,
+                               A9AB9AA621181CA900BB3C7E /* VideoToolbox.framework in Frameworks */,
                                A9ABD1A91E2A0F8200EC8B65 /* CoreGraphics.framework in Frameworks */,
                                A9ABD1A71E2A0F7500EC8B65 /* CoreText.framework in Frameworks */,
                                A9E165521C523016003EF30E /* libavfilter.a in Frameworks */,
                        isa = PBXFrameworksBuildPhase;
                        buildActionMask = 2147483647;
                        files = (
+                               A9A25D9D21182753005A8A0F /* CoreMedia.framework in Frameworks */,
+                               A9AB9AA5211819A500BB3C7E /* VideoToolbox.framework in Frameworks */,
                                A9ABD1AA1E2A0F8F00EC8B65 /* CoreGraphics.framework in Frameworks */,
                                A9ABD1A61E2A0F0700EC8B65 /* CoreText.framework in Frameworks */,
                                A91119A31C7DD591001C463C /* IOKit.framework in Frameworks */,
                273F203414ADBAC30021BE6D /* Frameworks */ = {
                        isa = PBXGroup;
                        children = (
+                               A9A25D9B21182741005A8A0F /* CoreMedia.framework */,
+                               A9AB9AA721181CC700BB3C7E /* CoreVideo.framework */,
+                               A9AB9AA4211819A500BB3C7E /* VideoToolbox.framework */,
                                A9ABD1A81E2A0F8200EC8B65 /* CoreGraphics.framework */,
                                A9ABD1A51E2A0F0700EC8B65 /* CoreText.framework */,
                                A91CE2D31C7DABE40068F46F /* libiconv.tbd */,
index eaa10ea69940a135e6a72de1a175d286330ed052..92ae40c45faf1e2744ed337512b86698a4554904 100644 (file)
@@ -74,7 +74,7 @@ endif
 TEST.GCC.I += $(LIBHB.GCC.I)
 
 ifeq ($(BUILD.system),darwin)
-    TEST.GCC.f += IOKit CoreServices CoreText CoreGraphics AudioToolbox Foundation
+    TEST.GCC.f += IOKit CoreServices CoreText CoreGraphics AudioToolbox VideoToolbox CoreMedia CoreVideo Foundation
     TEST.GCC.l += iconv
 else ifeq ($(BUILD.system),linux)
     TEST.GCC.l += pthread dl m