summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--contrib/ffmpeg/module.defs8
-rw-r--r--libhb/common.c33
-rw-r--r--libhb/common.h47
-rw-r--r--libhb/decavcodec.c243
-rw-r--r--libhb/decmpeg2.c4
-rw-r--r--libhb/encx264.h5
-rw-r--r--libhb/fifo.c13
-rw-r--r--libhb/hb.c21
-rw-r--r--libhb/internal.h13
-rw-r--r--libhb/module.defs15
-rw-r--r--libhb/muxavformat.c5
-rw-r--r--libhb/muxmkv.c85
-rw-r--r--libhb/muxmp4.c19
-rw-r--r--libhb/ports.c21
-rw-r--r--libhb/ports.h4
-rw-r--r--libhb/scan.c4
-rw-r--r--libhb/sync.c44
-rw-r--r--libhb/work.c186
-rw-r--r--make/configure.py3
-rw-r--r--make/include/main.defs4
-rw-r--r--test/module.defs5
-rw-r--r--test/test.c84
22 files changed, 785 insertions, 81 deletions
diff --git a/contrib/ffmpeg/module.defs b/contrib/ffmpeg/module.defs
index fdce86fde..6120ff39c 100644
--- a/contrib/ffmpeg/module.defs
+++ b/contrib/ffmpeg/module.defs
@@ -1,4 +1,8 @@
+ifeq (1,$(FEATURE.qsv))
+$(eval $(call import.MODULE.defs,FFMPEG,ffmpeg,YASM BZIP2 ZLIB FDKAAC PTHREADW32 LIBMFX))
+else
$(eval $(call import.MODULE.defs,FFMPEG,ffmpeg,YASM BZIP2 ZLIB FDKAAC))
+endif
$(eval $(call import.CONTRIB.defs,FFMPEG))
FFMPEG.FETCH.url = http://download.handbrake.fr/handbrake/contrib/libav-v9.6.tar.bz2
@@ -86,6 +90,10 @@ ifeq (none,$(FFMPEG.GCC.O))
FFMPEG.CONFIGURE.extra += --disable-optimizations
endif
+ifeq (1,$(FEATURE.qsv))
+ FFMPEG.CONFIGURE.extra += --enable-qsv
+endif
+
## enable compile verbosity
FFMPEG.BUILD.extra = V=1
diff --git a/libhb/common.c b/libhb/common.c
index 3eaa941b3..66f3b0440 100644
--- a/libhb/common.c
+++ b/libhb/common.c
@@ -12,9 +12,12 @@
#include <ctype.h>
#include <sys/time.h>
-#include "common.h"
-#include "lang.h"
#include "hb.h"
+#include "lang.h"
+#include "common.h"
+#ifdef USE_QSV
+#include "qsv_common.h"
+#endif
/**********************************************************************
* Global variables
@@ -191,6 +194,7 @@ hb_encoder_internal_t hb_video_encoders[] =
{ { "VP3 (Theora)", "libtheora", HB_VCODEC_THEORA, HB_MUX_MASK_MKV, }, NULL, 0, HB_GID_VCODEC_THEORA, },
// actual encoders
{ { "H.264 (x264)", "x264", HB_VCODEC_X264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264, },
+ { { "H.264 (Intel QSV)", "qsv_h264", HB_VCODEC_QSV_H264, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_H264, },
{ { "MPEG-4", "mpeg4", HB_VCODEC_FFMPEG_MPEG4, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG4, },
{ { "MPEG-2", "mpeg2", HB_VCODEC_FFMPEG_MPEG2, HB_MUX_MASK_MP4|HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_MPEG2, },
{ { "Theora", "theora", HB_VCODEC_THEORA, HB_MUX_MASK_MKV, }, NULL, 1, HB_GID_VCODEC_THEORA, },
@@ -200,6 +204,11 @@ static int hb_video_encoder_is_enabled(int encoder)
{
switch (encoder)
{
+#ifdef USE_QSV
+ case HB_VCODEC_QSV_H264:
+ return hb_qsv_available();
+#endif
+
// the following encoders are always enabled
case HB_VCODEC_X264:
case HB_VCODEC_THEORA:
@@ -2874,6 +2883,12 @@ static void job_setup( hb_job_t * job, hb_title_t * title )
job->list_attachment = hb_attachment_list_copy( title->list_attachment );
job->metadata = hb_metadata_copy( title->metadata );
+
+#ifdef USE_QSV
+ job->qsv_enc_info.is_init_done = 0;
+ job->qsv_decode = title->qsv_decode_support;
+ job->qsv_async_depth = AV_QSV_ASYNC_DEPTH_DEFAULT;
+#endif
}
static void job_clean( hb_job_t * job )
@@ -3133,6 +3148,20 @@ hb_filter_object_t * hb_filter_init( int filter_id )
filter = &hb_filter_rotate;
break;
+#ifdef USE_QSV
+ case HB_FILTER_QSV:
+ filter = &hb_filter_qsv;
+ break;
+
+ case HB_FILTER_QSV_PRE:
+ filter = &hb_filter_qsv_pre;
+ break;
+
+ case HB_FILTER_QSV_POST:
+ filter = &hb_filter_qsv_post;
+ break;
+#endif
+
default:
filter = NULL;
break;
diff --git a/libhb/common.h b/libhb/common.h
index 5a21eab84..38c97e487 100644
--- a/libhb/common.h
+++ b/libhb/common.h
@@ -105,6 +105,15 @@ typedef struct hb_lock_s hb_lock_t;
#include "audio_remap.h"
#include "libavutil/channel_layout.h"
+#ifdef USE_QSV
+
+#ifndef DEBUG_ASSERT
+#define DEBUG_ASSERT(x,y) { if ((x)) { hb_error("ASSERT: %s", y); exit(1); } }
+#endif
+
+#include "libavcodec/qsv.h"
+#endif
+
hb_list_t * hb_list_init();
int hb_list_count( const hb_list_t * );
void hb_list_add( hb_list_t *, void * );
@@ -403,12 +412,15 @@ struct hb_job_s
pass: 0, 1 or 2 (or -1 for scan)
advanced_opts: string of extra advanced encoder options
areBframes: boolean to note if b-frames are included in advanced_opts */
-#define HB_VCODEC_MASK 0x00000FF
+#define HB_VCODEC_MASK 0x0000FFF
#define HB_VCODEC_X264 0x0000001
#define HB_VCODEC_THEORA 0x0000002
#define HB_VCODEC_FFMPEG_MPEG4 0x0000010
#define HB_VCODEC_FFMPEG_MPEG2 0x0000020
#define HB_VCODEC_FFMPEG_MASK 0x00000F0
+#define HB_VCODEC_QSV_H264 0x0000100
+#define HB_VCODEC_QSV_MASK 0x0000F00
+#define HB_VCODEC_H264_MASK (HB_VCODEC_X264|HB_VCODEC_QSV_H264)
int vcodec;
float vquality;
@@ -500,6 +512,21 @@ struct hb_job_s
uint32_t frames_to_skip; // decode but discard this many frames
// initially (for frame accurate positioning
// to non-I frames).
+#ifdef USE_QSV
+ av_qsv_context *qsv;
+ int qsv_decode;
+ int qsv_async_depth;
+ // shared encoding parameters
+ // initialized by the QSV encoder, then used upstream (e.g. by filters) to
+ // configure their output so that it corresponds to what the encoder expects
+ struct
+ {
+ int pic_struct;
+ int align_width;
+ int align_height;
+ int is_init_done;
+ } qsv_enc_info;
+#endif
#ifdef __LIBHB__
/* Internal data */
@@ -833,6 +860,10 @@ struct hb_title_s
char *container_name;
int data_rate;
+#ifdef USE_QSV
+ int qsv_decode_support;
+#endif
+
hb_metadata_t *metadata;
hb_list_t * list_chapter;
@@ -929,6 +960,9 @@ typedef struct hb_work_info_s
int color_prim;
int color_transfer;
int color_matrix;
+#ifdef USE_QSV
+ int qsv_decode_support;
+#endif
};
struct
{ // info only valid for audio decoders
@@ -998,6 +1032,7 @@ extern hb_work_object_t hb_dectx3gsub;
extern hb_work_object_t hb_decssasub;
extern hb_work_object_t hb_decpgssub;
extern hb_work_object_t hb_encavcodec;
+extern hb_work_object_t hb_encqsv;
extern hb_work_object_t hb_encx264;
extern hb_work_object_t hb_enctheora;
extern hb_work_object_t hb_deca52;
@@ -1076,8 +1111,11 @@ struct hb_filter_object_s
enum
{
+ // for QSV - important to have before other filters
+ HB_FILTER_QSV_PRE = 1,
+
// First, filters that may change the framerate (drop or dup frames)
- HB_FILTER_DETELECINE = 1,
+ HB_FILTER_DETELECINE,
HB_FILTER_DECOMB,
HB_FILTER_DEINTERLACE,
HB_FILTER_VFR,
@@ -1089,6 +1127,11 @@ enum
// Finally filters that don't care what order they are in,
// except that they must be after the above filters
HB_FILTER_ROTATE,
+
+ // for QSV - important to have as a last one
+ HB_FILTER_QSV_POST,
+ // default MSDK VPP filter
+ HB_FILTER_QSV,
};
hb_filter_object_t * hb_filter_init( int filter_id );
diff --git a/libhb/decavcodec.c b/libhb/decavcodec.c
index 7dcfce90e..4c1972fdf 100644
--- a/libhb/decavcodec.c
+++ b/libhb/decavcodec.c
@@ -42,6 +42,11 @@
#include "hbffmpeg.h"
#include "audio_resample.h"
+#ifdef USE_QSV
+#include "enc_qsv.h"
+#include "qsv_common.h"
+#endif
+
static void compute_frame_duration( hb_work_private_t *pv );
static void flushDelayQueue( hb_work_private_t *pv );
static int decavcodecaInit( hb_work_object_t *, hb_job_t * );
@@ -101,8 +106,69 @@ struct hb_work_private_s
int wait_for_keyframe;
hb_audio_resample_t *resample;
+#ifdef USE_QSV
+ av_qsv_config qsv_config;
+ int qsv_decode;
+ const char *qsv_codec_name;
+#define USE_QSV_PTS_WORKAROUND // work around out-of-order output timestamps
+#ifdef USE_QSV_PTS_WORKAROUND
+ hb_list_t *qsv_pts_list;
+#endif
+#endif
};
+#ifdef USE_QSV_PTS_WORKAROUND
+// save/restore PTS if the decoder may not attach the right PTS to the frame
+static void hb_av_add_new_pts(hb_list_t *list, int64_t new_pts)
+{
+ int index = 0;
+ int64_t *cur_item, *new_item;
+ if (list != NULL && new_pts != AV_NOPTS_VALUE)
+ {
+ new_item = malloc(sizeof(int64_t));
+ if (new_item != NULL)
+ {
+ *new_item = new_pts;
+ // sort chronologically
+ for (index = 0; index < hb_list_count(list); index++)
+ {
+ cur_item = hb_list_item(list, index);
+ if (cur_item != NULL)
+ {
+ if (*cur_item == *new_item)
+ {
+ // no duplicates
+ free(new_item);
+ return;
+ }
+ if (*cur_item > *new_item)
+ {
+ // insert here
+ break;
+ }
+ }
+ }
+ hb_list_insert(list, index, new_item);
+ }
+ }
+}
+static int64_t hb_av_pop_next_pts(hb_list_t *list)
+{
+ int64_t *item, next_pts = AV_NOPTS_VALUE;
+ if (list != NULL && hb_list_count(list) > 0)
+ {
+ item = hb_list_item(list, 0);
+ if (item != NULL)
+ {
+ next_pts = *item;
+ hb_list_rem(list, item);
+ free(item);
+ }
+ }
+ return next_pts;
+}
+#endif
+
static void decodeAudio( hb_audio_t * audio, hb_work_private_t *pv, uint8_t *data, int size, int64_t pts );
static hb_buffer_t *link_buf_list( hb_work_private_t *pv );
@@ -285,7 +351,12 @@ static void closePrivData( hb_work_private_t ** ppv )
}
if ( pv->context && pv->context->codec )
{
- hb_avcodec_close( pv->context );
+#ifdef USE_QSV
+ if (!pv->qsv_decode)
+#endif
+ {
+ hb_avcodec_close(pv->context);
+ }
}
if ( pv->context )
{
@@ -297,6 +368,19 @@ static void closePrivData( hb_work_private_t ** ppv )
hb_list_empty( &pv->list );
}
hb_audio_resample_free(pv->resample);
+#ifdef USE_QSV_PTS_WORKAROUND
+ if (pv->qsv_decode && pv->qsv_pts_list != NULL)
+
+ {
+ while (hb_list_count(pv->qsv_pts_list) > 0)
+ {
+ int64_t *item = hb_list_item(pv->qsv_pts_list, 0);
+ hb_list_rem(pv->qsv_pts_list, item);
+ free(item);
+ }
+ hb_list_close(&pv->qsv_pts_list);
+ }
+#endif
free( pv );
}
*ppv = NULL;
@@ -593,6 +677,17 @@ static hb_buffer_t *copy_frame( hb_work_private_t *pv, AVFrame *frame )
h = pv->job->title->height;
}
hb_buffer_t *buf = hb_video_buffer_init( w, h );
+
+#ifdef USE_QSV
+ // no need to copy the frame data when decoding with QSV to opaque memory
+ if (pv->qsv_decode &&
+ pv->qsv_config.io_pattern == MFX_IOPATTERN_OUT_OPAQUE_MEMORY)
+ {
+ buf->qsv_details.qsv_atom = frame->data[2];
+ return buf;
+ }
+#endif
+
uint8_t *dst = buf->data;
if (context->pix_fmt != AV_PIX_FMT_YUV420P || w != context->width ||
@@ -795,10 +890,42 @@ static int decodeFrame( hb_work_object_t *w, uint8_t *data, int size, int sequen
avp.flags |= AV_PKT_FLAG_KEY;
}
+#ifdef USE_QSV_PTS_WORKAROUND
+ /*
+ * The MediaSDK decoder will return decoded frames in the correct order,
+ * but *sometimes* with the incorrect timestamp assigned to them.
+ *
+ * We work around it by saving the input timestamps (in chronological order)
+ * and restoring them after decoding.
+ */
+ if (pv->qsv_decode && avp.data != NULL)
+ {
+ hb_av_add_new_pts(pv->qsv_pts_list, avp.pts);
+ }
+#endif
+
if ( avcodec_decode_video2( pv->context, &frame, &got_picture, &avp ) < 0 )
{
++pv->decode_errors;
}
+
+#ifdef USE_QSV
+ if (pv->qsv_decode && pv->job->qsv == NULL && pv->video_codec_opened > 0)
+ {
+ // this is quite late, but we can't be certain that the QSV context is
+ // available until after we call avcodec_decode_video2() at least once
+ pv->job->qsv = pv->context->priv_data;
+ }
+#endif
+
+#ifdef USE_QSV_PTS_WORKAROUND
+ if (pv->qsv_decode && got_picture)
+ {
+ // we got a decoded frame, restore the lowest available PTS
+ frame.pkt_pts = hb_av_pop_next_pts(pv->qsv_pts_list);
+ }
+#endif
+
if ( global_verbosity_level <= 1 )
{
av_log_set_level( oldlevel );
@@ -998,12 +1125,23 @@ static void decodeVideo( hb_work_object_t *w, uint8_t *data, int size, int seque
} while ( pos < size );
/* the stuff above flushed the parser, now flush the decoder */
- if ( size <= 0 )
+ if (size <= 0)
{
- while ( decodeFrame( w, NULL, 0, sequence, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0 ) )
+ while (decodeFrame(w, NULL, 0, sequence, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0))
{
+ continue;
}
- flushDelayQueue( pv );
+#ifdef USE_QSV
+ if (pv->qsv_decode)
+ {
+ // flush a second time
+ while (decodeFrame(w, NULL, 0, sequence, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0))
+ {
+ continue;
+ }
+ }
+#endif
+ flushDelayQueue(pv);
}
}
@@ -1045,14 +1183,52 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job )
pv->title = w->title;
pv->list = hb_list_init();
+#ifdef USE_QSV
+ if (hb_qsv_decode_is_enabled(job))
+ {
+ // setup the QSV configuration
+ pv->qsv_config.impl_requested = MFX_IMPL_AUTO_ANY|MFX_IMPL_VIA_ANY;
+ pv->qsv_config.io_pattern = MFX_IOPATTERN_OUT_OPAQUE_MEMORY;
+ pv->qsv_config.async_depth = job->qsv_async_depth;
+ pv->qsv_config.sync_need = 0;
+ pv->qsv_config.usage_threaded = 1;
+ pv->qsv_config.additional_buffers = 64; // FIFO_LARGE
+ if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD)
+ {
+ // more surfaces may be needed for the lookahead
+ pv->qsv_config.additional_buffers = 160;
+ }
+ pv->qsv_codec_name = hb_qsv_decode_get_codec_name(w->codec_param);
+ pv->qsv_decode = 1;
+ }
+ else
+ {
+ pv->qsv_decode = 0;
+ }
+#endif
+
if( pv->job && pv->job->title && !pv->job->title->has_resolution_change )
{
pv->threads = HB_FFMPEG_THREADS_AUTO;
}
+
if ( pv->title->opaque_priv )
{
AVFormatContext *ic = (AVFormatContext*)pv->title->opaque_priv;
- AVCodec *codec = avcodec_find_decoder( w->codec_param );
+
+ AVCodec *codec = NULL;
+
+#ifdef USE_QSV
+ if (pv->qsv_decode)
+ {
+ codec = avcodec_find_decoder_by_name(pv->qsv_codec_name);
+ }
+ else
+#endif
+ {
+ codec = avcodec_find_decoder(w->codec_param);
+ }
+
if ( codec == NULL )
{
hb_log( "decavcodecvInit: failed to find codec for id (%d)", w->codec_param );
@@ -1064,6 +1240,17 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job )
pv->context->err_recognition = AV_EF_CRCCHECK;
pv->context->error_concealment = FF_EC_GUESS_MVS|FF_EC_DEBLOCK;
+#ifdef USE_QSV
+ if (pv->qsv_decode)
+ {
+#ifdef USE_QSV_PTS_WORKAROUND
+ pv->qsv_pts_list = hb_list_init();
+#endif
+ // set the QSV configuration before opening the decoder
+ pv->context->hwaccel_context = &pv->qsv_config;
+ }
+#endif
+
if ( hb_avcodec_open( pv->context, codec, NULL, pv->threads ) )
{
hb_log( "decavcodecvInit: avcodec_open failed" );
@@ -1080,7 +1267,20 @@ static int decavcodecvInit( hb_work_object_t * w, hb_job_t * job )
}
else
{
- AVCodec *codec = avcodec_find_decoder( w->codec_param );
+ AVCodec *codec = NULL;
+
+#ifdef USE_QSV
+ if (pv->qsv_decode)
+ {
+ codec = avcodec_find_decoder_by_name(pv->qsv_codec_name);
+ }
+ else
+#endif
+ {
+ codec = avcodec_find_decoder(w->codec_param);
+ }
+
+
pv->parser = av_parser_init( w->codec_param );
pv->context = avcodec_alloc_context3( codec );
pv->context->workaround_bugs = FF_BUG_AUTODETECT;
@@ -1207,7 +1407,19 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
// first frame because of M$ VC1 braindamage).
if ( !pv->video_codec_opened )
{
- AVCodec *codec = avcodec_find_decoder( w->codec_param );
+
+ AVCodec *codec = NULL;
+#ifdef USE_QSV
+ if (pv->qsv_decode)
+ {
+ codec = avcodec_find_decoder_by_name(pv->qsv_codec_name);
+ }
+ else
+#endif
+ {
+ codec = avcodec_find_decoder(w->codec_param);
+ }
+
if ( codec == NULL )
{
hb_log( "decavcodecvWork: failed to find codec for id (%d)", w->codec_param );
@@ -1231,6 +1443,18 @@ static int decavcodecvWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_buffer_close( &in );
return HB_WORK_OK;
}
+
+#ifdef USE_QSV
+ if (pv->qsv_decode)
+ {
+#ifdef USE_QSV_PTS_WORKAROUND
+ pv->qsv_pts_list = hb_list_init();
+#endif
+ // set the QSV configuration before opening the decoder
+ pv->context->hwaccel_context = &pv->qsv_config;
+ }
+#endif
+
// disable threaded decoding for scan, can cause crashes
if ( hb_avcodec_open( pv->context, codec, NULL, pv->threads ) )
{
@@ -1444,6 +1668,11 @@ static int decavcodecvInfo( hb_work_object_t *w, hb_work_info_t *info )
}
}
+#ifdef USE_QSV
+ info->qsv_decode_support = hb_qsv_decode_is_supported(pv->context->codec_id,
+ pv->context->pix_fmt);
+#endif
+
return 1;
}
diff --git a/libhb/decmpeg2.c b/libhb/decmpeg2.c
index 8844a09af..2a186295d 100644
--- a/libhb/decmpeg2.c
+++ b/libhb/decmpeg2.c
@@ -911,6 +911,10 @@ static int decmpeg2Info( hb_work_object_t *w, hb_work_info_t *info )
info->level = m->info->sequence->profile_level_id & 0xf;
info->name = "mpeg2";
+#ifdef USE_QSV
+ info->qsv_decode_support = 0;
+#endif
+
if( pv->libmpeg2->info->sequence->flags & SEQ_FLAG_COLOUR_DESCRIPTION )
{
switch( pv->libmpeg2->info->sequence->colour_primaries )
diff --git a/libhb/encx264.h b/libhb/encx264.h
index b0ff8b9ca..63fa1e04a 100644
--- a/libhb/encx264.h
+++ b/libhb/encx264.h
@@ -8,10 +8,7 @@
*/
#include "x264.h"
-
-static const char * const hb_h264_profile_names[] = { "auto", "high", "main", "baseline", 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, };
+#include "h264_common.h"
/* x264 preferred option names (left) and synonyms (right).
* The "preferred" names match names used in x264's param2string function more
diff --git a/libhb/fifo.c b/libhb/fifo.c
index a32242c03..be69616f1 100644
--- a/libhb/fifo.c
+++ b/libhb/fifo.c
@@ -389,6 +389,10 @@ hb_buffer_t * hb_buffer_dup( const hb_buffer_t * src )
hb_buffer_init_planes( buf );
}
+#ifdef USE_QSV
+ memcpy(&buf->qsv_details, &src->qsv_details, sizeof(src->qsv_details));
+#endif
+
return buf;
}
@@ -412,7 +416,7 @@ int hb_buffer_copy(hb_buffer_t * dst, const hb_buffer_t * src)
static void hb_buffer_init_planes_internal( hb_buffer_t * b, uint8_t * has_plane )
{
uint8_t * plane = b->data;
- int p, tot = 0;
+ int p;
for( p = 0; p < 4; p++ )
{
@@ -425,7 +429,6 @@ static void hb_buffer_init_planes_internal( hb_buffer_t * b, uint8_t * has_plane
b->plane[p].height = hb_image_height( b->f.fmt, b->f.height, p );
b->plane[p].size = b->plane[p].stride * b->plane[p].height_stride;
plane += b->plane[p].size;
- tot += b->plane[p].size;
}
}
}
@@ -508,6 +511,7 @@ void hb_video_buffer_realloc( hb_buffer_t * buf, int width, int height )
buf->f.width = width;
buf->f.height = height;
+ buf->size = size;
hb_buffer_init_planes_internal( buf, has_plane );
}
@@ -570,6 +574,11 @@ void hb_buffer_move_subs( hb_buffer_t * dst, hb_buffer_t * src )
// Note that dst takes ownership of the subtitles
dst->sub = src->sub;
src->sub = NULL;
+
+#ifdef USE_QSV
+ memcpy(&dst->qsv_details, &src->qsv_details, sizeof(src->qsv_details));
+#endif
+
}
hb_fifo_t * hb_fifo_init( int capacity, int thresh )
diff --git a/libhb/hb.c b/libhb/hb.c
index 2a9ed1070..629b2e3a3 100644
--- a/libhb/hb.c
+++ b/libhb/hb.c
@@ -13,6 +13,10 @@
#include <unistd.h>
#include <fcntl.h>
+#ifdef USE_QSV
+#include "qsv_common.h"
+#endif
+
#if defined( SYS_MINGW )
#include <io.h>
#if defined( PTW32_STATIC_LIB )
@@ -436,6 +440,11 @@ hb_handle_t * hb_init( int verbose, int update_check )
h->interjob = calloc( sizeof( hb_interjob_t ), 1 );
+#ifdef USE_QSV
+ /* Intel Quick Sync Video */
+ hb_qsv_info_print();
+#endif
+
/* Start library thread */
hb_log( "hb_init: starting libhb thread" );
h->die = 0;
@@ -1629,6 +1638,15 @@ int hb_global_init()
return -1;
}
+#ifdef USE_QSV
+ result = hb_qsv_info_init();
+ if (result < 0)
+ {
+ hb_error("hb_qsv_info_init failed!");
+ return -1;
+ }
+#endif
+
/* libavcodec */
hb_avcodec_init();
@@ -1663,6 +1681,9 @@ int hb_global_init()
hb_register(&hb_enctheora);
hb_register(&hb_encvorbis);
hb_register(&hb_encx264);
+#ifdef USE_QSV
+ hb_register(&hb_encqsv);
+#endif
hb_common_global_init();
diff --git a/libhb/internal.h b/libhb/internal.h
index d93119fb0..86878eb17 100644
--- a/libhb/internal.h
+++ b/libhb/internal.h
@@ -115,6 +115,12 @@ struct hb_buffer_s
int size;
} plane[4]; // 3 Color components + alpha
+ struct qsv
+ {
+ void *qsv_atom;
+ void *filter_details;
+ } qsv_details;
+
// PICTURESUB subtitle packets:
// Video packets (after processing by the hb_sync_video work-object):
@@ -404,6 +410,7 @@ enum
WORK_ENCVOBSUB,
WORK_RENDER,
WORK_ENCAVCODEC,
+ WORK_ENCQSV,
WORK_ENCX264,
WORK_ENCTHEORA,
WORK_DECA52,
@@ -431,6 +438,12 @@ extern hb_filter_object_t hb_filter_crop_scale;
extern hb_filter_object_t hb_filter_render_sub;
extern hb_filter_object_t hb_filter_vfr;
+#ifdef USE_QSV
+extern hb_filter_object_t hb_filter_qsv;
+extern hb_filter_object_t hb_filter_qsv_pre;
+extern hb_filter_object_t hb_filter_qsv_post;
+#endif
+
// Picture flags used by filters
#ifndef PIC_FLAG_REPEAT_FIRST_FIELD
#define PIC_FLAG_REPEAT_FIRST_FIELD 256
diff --git a/libhb/module.defs b/libhb/module.defs
index afaf6ffd1..b161f4c7f 100644
--- a/libhb/module.defs
+++ b/libhb/module.defs
@@ -1,6 +1,6 @@
__deps__ := A52DEC BZIP2 FAAC FFMPEG FONTCONFIG FREETYPE LAME LIBASS LIBDCA \
LIBDVDREAD LIBDVDNAV LIBICONV LIBMKV LIBOGG LIBSAMPLERATE LIBTHEORA LIBVORBIS LIBXML2 \
- MP4V2 MPEG2DEC PTHREADW32 X264 ZLIB LIBBLURAY FDKAAC
+ MP4V2 MPEG2DEC PTHREADW32 X264 ZLIB LIBBLURAY FDKAAC LIBMFX
$(eval $(call import.MODULE.defs,LIBHB,libhb,$(__deps__)))
$(eval $(call import.GCC,LIBHB))
@@ -13,7 +13,12 @@ LIBHB.build/ = $(BUILD/)libhb/
LIBHB.m4.in = $(wildcard $(LIBHB.src/)*.m4)
LIBHB.m4.out = $(patsubst $(LIBHB.src/)%.m4,$(LIBHB.build/)%,$(LIBHB.m4.in))
+ifeq (1,$(FEATURE.qsv))
LIBHB.c = $(wildcard $(LIBHB.src/)*.c)
+else
+LIBHB.c = $(filter-out $(wildcard $(LIBHB.src/)*qsv*.c), $(wildcard $(LIBHB.src/)*.c))
+endif
+
LIBHB.c.o = $(patsubst $(SRC/)%.c,$(BUILD/)%.o,$(LIBHB.c))
LIBHB.d = $(LIBHB.m4.out) $(LIBHB.h.out) \
$(foreach n,$(LIBHB.prerequisites),$($n.INSTALL.target) )
@@ -74,6 +79,10 @@ else
LIBHB.platform.D = SYS_UNKNOWN
endif
+ifeq (1,$(FEATURE.qsv))
+ LIBHB.GCC.D += USE_QSV HAVE_THREADS=1
+endif
+
## required for <libdvdread/*.h>
ifneq (,$(filter $(BUILD.arch),ppc ppc64))
LIBHB.GCC.D += WORDS_BIGENDIAN
@@ -123,6 +132,10 @@ ifeq (1,$(FEATURE.faac))
LIBHB.dll.libs += $(CONTRIB.build/)lib/libfaac.a
endif
+ifeq (1,$(FEATURE.qsv))
+LIBHB.dll.libs += $(CONTRIB.build/)lib/libmfx.a
+endif
+
ifeq (1,$(FEATURE.mp4v2))
LIBHB.dll.libs += $(CONTRIB.build/)lib/libmp4v2.a
endif
diff --git a/libhb/muxavformat.c b/libhb/muxavformat.c
index 767e8d314..ff53ebbe9 100644
--- a/libhb/muxavformat.c
+++ b/libhb/muxavformat.c
@@ -207,6 +207,7 @@ static int avformatInit( hb_mux_object_t * m )
switch (job->vcodec)
{
case HB_VCODEC_X264:
+ case HB_VCODEC_QSV_H264:
track->st->codec->codec_id = AV_CODEC_ID_H264;
/* Taken from x264 muxers.c */
@@ -1015,8 +1016,8 @@ static int avformatMux(hb_mux_object_t *m, hb_mux_data_t *track, hb_buffer_t *bu
pkt.pts = pts;
pkt.duration = duration;
- if (track->type == MUX_TYPE_VIDEO &&
- (job->vcodec == HB_VCODEC_X264 || job->vcodec & HB_VCODEC_FFMPEG_MASK))
+ if (track->type == MUX_TYPE_VIDEO && ((job->vcodec & HB_VCODEC_H264_MASK) ||
+ (job->vcodec & HB_VCODEC_FFMPEG_MASK)))
{
if (buf->s.frametype == HB_FRAME_IDR)
pkt.flags |= AV_PKT_FLAG_KEY;
diff --git a/libhb/muxmkv.c b/libhb/muxmkv.c
index ca72a7be8..f96a60e76 100644
--- a/libhb/muxmkv.c
+++ b/libhb/muxmkv.c
@@ -55,6 +55,49 @@ static uint8_t * create_flac_header( uint8_t *data, int size )
return out;
}
+static uint8_t* create_h264_header(hb_job_t *job, int *size)
+{
+ /* Taken from x264's muxers.c */
+ int avcC_len = (5 +
+ 1 + 2 + job->config.h264.sps_length +
+ 1 + 2 + job->config.h264.pps_length);
+#define MAX_AVCC_LEN 5 + 1 + 2 + 1024 + 1 + 2 + 1024 // FIXME
+ if (avcC_len > MAX_AVCC_LEN)
+ {
+ hb_log("create_h264_header: H.264 header too long (%d, max: %d)",
+ avcC_len, MAX_AVCC_LEN);
+ return NULL;
+ }
+ uint8_t *avcC = malloc(avcC_len);
+ if (avcC == NULL)
+ {
+ return NULL;
+ }
+
+ avcC[0] = 1;
+ avcC[1] = job->config.h264.sps[1]; /* AVCProfileIndication */
+ avcC[2] = job->config.h264.sps[2]; /* profile_compat */
+ avcC[3] = job->config.h264.sps[3]; /* AVCLevelIndication */
+ avcC[4] = 0xff; // nalu size length is four bytes
+ avcC[5] = 0xe1; // one sps
+
+ avcC[6] = job->config.h264.sps_length >> 8;
+ avcC[7] = job->config.h264.sps_length;
+ memcpy(avcC + 8, job->config.h264.sps, job->config.h264.sps_length);
+
+ avcC[8 + job->config.h264.sps_length] = 1; // one pps
+ avcC[9 + job->config.h264.sps_length] = job->config.h264.pps_length >> 8;
+ avcC[10 + job->config.h264.sps_length] = job->config.h264.pps_length;
+ memcpy(avcC + 11 + job->config.h264.sps_length,
+ job->config.h264.pps, job->config.h264.pps_length);
+
+ if (size != NULL)
+ {
+ *size = avcC_len;
+ }
+ return avcC;
+}
+
/**********************************************************************
* MKVInit
**********************************************************************
@@ -97,33 +140,15 @@ static int MKVInit( hb_mux_object_t * m )
switch (job->vcodec)
{
case HB_VCODEC_X264:
- track->codecID = MK_VCODEC_MP4AVC;
- /* Taken from x264 muxers.c */
- avcC_len = 5 + 1 + 2 + job->config.h264.sps_length + 1 + 2 + job->config.h264.pps_length;
- avcC = malloc(avcC_len);
- if (avcC == NULL) {
+ case HB_VCODEC_QSV_H264:
+ avcC = create_h264_header(job, &avcC_len);
+ if (avcC == NULL)
+ {
free(track);
return -1;
}
-
- avcC[0] = 1;
- avcC[1] = job->config.h264.sps[1]; /* AVCProfileIndication */
- avcC[2] = job->config.h264.sps[2]; /* profile_compat */
- avcC[3] = job->config.h264.sps[3]; /* AVCLevelIndication */
- avcC[4] = 0xff; // nalu size length is four bytes
- avcC[5] = 0xe1; // one sps
-
- avcC[6] = job->config.h264.sps_length >> 8;
- avcC[7] = job->config.h264.sps_length;
-
- memcpy(avcC+8, job->config.h264.sps, job->config.h264.sps_length);
-
- avcC[8+job->config.h264.sps_length] = 1; // one pps
- avcC[9+job->config.h264.sps_length] = job->config.h264.pps_length >> 8;
- avcC[10+job->config.h264.sps_length] = job->config.h264.pps_length;
-
- memcpy( avcC+11+job->config.h264.sps_length, job->config.h264.pps, job->config.h264.pps_length );
- track->codecPrivate = avcC;
+ track->codecID = MK_VCODEC_MP4AVC;
+ track->codecPrivate = avcC;
track->codecPrivateSize = avcC_len;
if (job->areBframes)
track->minCache = 1;
@@ -516,12 +541,12 @@ static int MKVMux(hb_mux_object_t *m, hb_mux_data_t *mux_data, hb_buffer_t *buf)
}
mk_addFrameData(m->file, mux_data->track, buf->data, buf->size);
mk_setFrameFlags(m->file, mux_data->track, timecode,
- (((job->vcodec == HB_VCODEC_X264 ||
- (job->vcodec & HB_VCODEC_FFMPEG_MASK)) &&
- mux_data == job->mux_data) ?
- (buf->s.frametype == HB_FRAME_IDR) :
- ((buf->s.frametype & HB_FRAME_KEY) != 0)), 0 );
- hb_buffer_close( &buf );
+ ((mux_data == job->mux_data &&
+ ((job->vcodec & HB_VCODEC_H264_MASK) ||
+ (job->vcodec & HB_VCODEC_FFMPEG_MASK))) ?
+ (buf->s.frametype == HB_FRAME_IDR) :
+ (buf->s.frametype & HB_FRAME_KEY) != 0), 0);
+ hb_buffer_close(&buf);
return 0;
}
diff --git a/libhb/muxmp4.c b/libhb/muxmp4.c
index 6f7ecab54..bd706d29c 100644
--- a/libhb/muxmp4.c
+++ b/libhb/muxmp4.c
@@ -136,7 +136,7 @@ static int MP4Init( hb_mux_object_t * m )
return 0;
}
- if( job->vcodec == HB_VCODEC_X264 )
+ if (job->vcodec & HB_VCODEC_H264_MASK)
{
/* Stolen from mp4creator */
MP4SetVideoProfileLevel( m->file, 0x7F );
@@ -691,8 +691,8 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
if( mux_data == job->mux_data )
{
/* Video */
- if( job->vcodec == HB_VCODEC_X264 ||
- ( job->vcodec & HB_VCODEC_FFMPEG_MASK ) )
+ if ((job->vcodec & HB_VCODEC_H264_MASK) ||
+ (job->vcodec & HB_VCODEC_FFMPEG_MASK))
{
if ( buf && buf->s.start < buf->s.renderOffset )
{
@@ -713,8 +713,8 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
stop = buf->s.start + buf->s.duration;
- if( job->vcodec == HB_VCODEC_X264 ||
- ( job->vcodec & HB_VCODEC_FFMPEG_MASK ) )
+ if ((job->vcodec & HB_VCODEC_H264_MASK) ||
+ (job->vcodec & HB_VCODEC_FFMPEG_MASK))
{
// x264 supplies us with DTS, so offset is PTS - DTS
offset = buf->s.start - buf->s.renderOffset;
@@ -751,8 +751,8 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
}
}
- if( job->vcodec == HB_VCODEC_X264 ||
- ( job->vcodec & HB_VCODEC_FFMPEG_MASK ) )
+ if ((job->vcodec & HB_VCODEC_H264_MASK) ||
+ (job->vcodec & HB_VCODEC_FFMPEG_MASK))
{
// x264 supplies us with DTS
if ( m->delay_buf )
@@ -829,9 +829,8 @@ static int MP4Mux( hb_mux_object_t * m, hb_mux_data_t * mux_data,
}
/* Here's where the sample actually gets muxed. */
- if( ( job->vcodec == HB_VCODEC_X264 ||
- ( job->vcodec & HB_VCODEC_FFMPEG_MASK ) )
- && mux_data == job->mux_data )
+ if (mux_data == job->mux_data && ((job->vcodec & HB_VCODEC_H264_MASK) ||
+ (job->vcodec & HB_VCODEC_FFMPEG_MASK)))
{
/* Compute dependency flags.
*
diff --git a/libhb/ports.c b/libhb/ports.c
index 9b9ab0200..94bb95227 100644
--- a/libhb/ports.c
+++ b/libhb/ports.c
@@ -141,6 +141,27 @@ uint64_t hb_get_date()
return( (uint64_t) tv.tv_sec * 1000 + (uint64_t) tv.tv_usec / 1000 );
}
+uint64_t hb_get_time_us()
+{
+#ifdef SYS_MINGW
+ static LARGE_INTEGER frequency;
+ LARGE_INTEGER cur_time;
+
+ if (frequency.QuadPart == 0)
+ {
+ QueryPerformanceFrequency(&frequency);
+ }
+
+ QueryPerformanceCounter(&cur_time);
+
+ return (uint64_t)(1000000 * cur_time.QuadPart / frequency.QuadPart);
+#else
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ return ((uint64_t)tv.tv_sec * 1000000 + (uint64_t)tv.tv_usec);
+#endif
+}
+
/************************************************************************
* hb_snooze()
************************************************************************
diff --git a/libhb/ports.h b/libhb/ports.h
index 38921300d..a87b7ebd6 100644
--- a/libhb/ports.h
+++ b/libhb/ports.h
@@ -36,7 +36,11 @@ extern void ff_cpu_cpuid(int index, int *eax, int *ebx, int *ecx, int *edx);
/************************************************************************
* Utils
***********************************************************************/
+// provide time in ms
uint64_t hb_get_date();
+// provide time in us
+uint64_t hb_get_time_us();
+
void hb_snooze( int delay );
int hb_platform_init();
#ifdef SYS_MINGW
diff --git a/libhb/scan.c b/libhb/scan.c
index 0d7dde7ff..507662f36 100644
--- a/libhb/scan.c
+++ b/libhb/scan.c
@@ -865,6 +865,10 @@ skip_preview:
title->color_transfer = vid_info.color_transfer;
title->color_matrix = vid_info.color_matrix;
+#ifdef USE_QSV
+ title->qsv_decode_support = vid_info.qsv_decode_support;
+#endif
+
// compute the aspect ratio based on the storage dimensions and the
// pixel aspect ratio (if supplied) or just storage dimensions if no PAR.
title->aspect = (double)title->width / (double)title->height;
diff --git a/libhb/sync.c b/libhb/sync.c
index c04e517d4..8134688b3 100644
--- a/libhb/sync.c
+++ b/libhb/sync.c
@@ -527,6 +527,27 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
hb_unlock( pv->common->mutex );
UpdateSearchState( w, next_start );
+#ifdef USE_QSV
+ // reclaim QSV resources before dropping the buffer
+ // when decoding without QSV, the QSV atom will be NULL
+ if (job != NULL && job->qsv != NULL && next->qsv_details.qsv_atom != NULL)
+ {
+ av_qsv_stage *stage = av_qsv_get_last_stage(next->qsv_details.qsv_atom);
+ if (stage != NULL)
+ {
+ av_qsv_wait_on_sync(job->qsv, stage);
+ if (stage->out.sync->in_use > 0)
+ {
+ ff_qsv_atomic_dec(&stage->out.sync->in_use);
+ }
+ if (stage->out.p_surface->Data.Locked > 0)
+ {
+ ff_qsv_atomic_dec(&stage->out.p_surface->Data.Locked);
+ }
+ }
+ av_qsv_flush_stages(job->qsv->pipes, &next->qsv_details.qsv_atom);
+ }
+#endif
hb_buffer_close( &next );
return HB_WORK_OK;
@@ -720,6 +741,29 @@ int syncVideoWork( hb_work_object_t * w, hb_buffer_t ** buf_in,
// don't drop a chapter mark when we drop the buffer
sync->chap_mark = next->s.new_chap;
}
+
+#ifdef USE_QSV
+ // reclaim QSV resources before dropping the buffer
+ // when decoding without QSV, the QSV atom will be NULL
+ if (job != NULL && job->qsv != NULL && next->qsv_details.qsv_atom != NULL)
+ {
+ av_qsv_stage *stage = av_qsv_get_last_stage(next->qsv_details.qsv_atom);
+ if (stage != NULL)
+ {
+ av_qsv_wait_on_sync(job->qsv, stage);
+ if (stage->out.sync->in_use > 0)
+ {
+ ff_qsv_atomic_dec(&stage->out.sync->in_use);
+ }
+ if (stage->out.p_surface->Data.Locked > 0)
+ {
+ ff_qsv_atomic_dec(&stage->out.p_surface->Data.Locked);
+ }
+ }
+ av_qsv_flush_stages(job->qsv->pipes, &next->qsv_details.qsv_atom);
+ }
+#endif
+
hb_buffer_close( &next );
return HB_WORK_OK;
}
diff --git a/libhb/work.c b/libhb/work.c
index b23a3619a..7f00ddab3 100644
--- a/libhb/work.c
+++ b/libhb/work.c
@@ -11,6 +11,11 @@
#include "a52dec/a52.h"
#include "libavformat/avformat.h"
+#ifdef USE_QSV
+#include "qsv_common.h"
+#include "qsv_filter_pp.h"
+#endif
+
typedef struct
{
hb_list_t * jobs;
@@ -231,8 +236,18 @@ void hb_display_job_info(hb_job_t *job)
hb_log(" * video track");
- hb_log(" + decoder: %s", title->video_codec_name );
-
+#ifdef USE_QSV
+ if (hb_qsv_decode_is_enabled(job))
+ {
+ hb_log(" + decoder: %s",
+ hb_qsv_decode_get_codec_name(title->video_codec_param));
+ }
+ else
+#endif
+ {
+ hb_log(" + decoder: %s", title->video_codec_name);
+ }
+
if( title->video_bitrate )
{
hb_log( " + bitrate %d kbps", title->video_bitrate / 1000 );
@@ -307,15 +322,15 @@ void hb_display_job_info(hb_job_t *job)
{
hb_log(" + options: %s", job->advanced_opts);
}
- if( job->h264_profile && *job->h264_profile &&
- job->vcodec == HB_VCODEC_X264 )
+ if (job->h264_profile != NULL && *job->h264_profile &&
+ (job->vcodec & HB_VCODEC_H264_MASK))
{
- hb_log( " + h264 profile: %s", job->h264_profile );
+ hb_log(" + h264 profile: %s", job->h264_profile);
}
- if( job->h264_level && *job->h264_level &&
- job->vcodec == HB_VCODEC_X264 )
+ if (job->h264_level != NULL && *job->h264_level &&
+ (job->vcodec & HB_VCODEC_H264_MASK))
{
- hb_log( " + h264 level: %s", job->h264_level );
+ hb_log(" + h264 level: %s", job->h264_level);
}
if (job->vquality >= 0)
@@ -659,6 +674,114 @@ static void do_job(hb_job_t *job)
}
}
+#ifdef USE_QSV
+ /*
+ * When QSV is used for decoding, not all CPU-based filters are supported,
+ * so we need to do a little extra setup here.
+ */
+ if (hb_qsv_decode_is_enabled(job))
+ {
+ int vpp_settings[7];
+ int num_cpu_filters = 0;
+ hb_filter_object_t *filter;
+ // default values for VPP filter
+ vpp_settings[0] = job->title->width;
+ vpp_settings[1] = job->title->height;
+ vpp_settings[2] = job->title->crop[0];
+ vpp_settings[3] = job->title->crop[1];
+ vpp_settings[4] = job->title->crop[2];
+ vpp_settings[5] = job->title->crop[3];
+ vpp_settings[6] = 0; // deinterlace: off
+ if (job->list_filter != NULL && hb_list_count(job->list_filter) > 0)
+ {
+ while (hb_list_count(job->list_filter) > num_cpu_filters)
+ {
+ filter = hb_list_item(job->list_filter, num_cpu_filters);
+ switch (filter->id)
+ {
+ // cropping and scaling always done via VPP filter
+ case HB_FILTER_CROP_SCALE:
+ if (filter->settings == NULL || *filter->settings == '\0')
+ {
+ // VPP defaults were set above, so not a problem
+ // however, this should never happen, print an error
+ hb_error("do_job: '%s': no settings!", filter->name);
+ }
+ else
+ {
+ sscanf(filter->settings, "%d:%d:%d:%d:%d:%d",
+ &vpp_settings[0], &vpp_settings[1],
+ &vpp_settings[2], &vpp_settings[3],
+ &vpp_settings[4], &vpp_settings[5]);
+ }
+ hb_list_rem(job->list_filter, filter);
+ hb_filter_close(&filter);
+ break;
+
+ // pick VPP or CPU deinterlace depending on settings
+ case HB_FILTER_DEINTERLACE:
+ if (filter->settings == NULL || !strcmp(filter->settings, "qsv"))
+ {
+ // deinterlacing via VPP filter
+ vpp_settings[6] = 1;
+ hb_list_rem(job->list_filter, filter);
+ hb_filter_close(&filter);
+ }
+ else
+ {
+ // validated
+ num_cpu_filters++;
+ }
+ break;
+
+ // then, validated filters
+ case HB_FILTER_ROTATE: // TODO: use Media SDK for this
+ case HB_FILTER_RENDER_SUB:
+ num_cpu_filters++;
+ break;
+
+ // finally, drop all unsupported filters
+ default:
+ hb_log("do_job: full QSV path, removing unsupported filter '%s'",
+ filter->name);
+ hb_list_rem(job->list_filter, filter);
+ hb_filter_close(&filter);
+ break;
+ }
+ }
+ if (num_cpu_filters > 0)
+ {
+ // we need filters to copy to system memory and back
+ filter = hb_filter_init(HB_FILTER_QSV_PRE);
+ hb_add_filter(job, filter, NULL);
+ filter = hb_filter_init(HB_FILTER_QSV_POST);
+ hb_add_filter(job, filter, NULL);
+ }
+ if (vpp_settings[0] != job->title->width ||
+ vpp_settings[1] != job->title->height ||
+ vpp_settings[2] >= 1 /* crop */ ||
+ vpp_settings[3] >= 1 /* crop */ ||
+ vpp_settings[4] >= 1 /* crop */ ||
+ vpp_settings[5] >= 1 /* crop */ ||
+ vpp_settings[6] >= 1 /* deinterlace */)
+ {
+ // we need the VPP filter
+ char *settings = hb_strdup_printf("%d:%d:%d:%d:%d:%d_dei:%d",
+ vpp_settings[0],
+ vpp_settings[1],
+ vpp_settings[2],
+ vpp_settings[3],
+ vpp_settings[4],
+ vpp_settings[5],
+ vpp_settings[6]);
+ filter = hb_filter_init(HB_FILTER_QSV);
+ hb_add_filter(job, filter, settings);
+ free(settings);
+ }
+ }
+ }
+#endif
+
// Filters have an effect on settings.
// So initialize the filters and update the job.
if( job->list_filter && hb_list_count( job->list_filter ) )
@@ -720,12 +843,25 @@ static void do_job(hb_job_t *job)
}
}
}
-
- job->fifo_mpeg2 = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE );
- job->fifo_raw = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
- job->fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
- job->fifo_mpeg4 = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE );
- job->fifo_render = NULL; // Attached to filter chain
+
+#ifdef USE_QSV
+ if (hb_qsv_decode_is_enabled(job))
+ {
+ job->fifo_mpeg2 = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE );
+ job->fifo_raw = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE );
+ job->fifo_sync = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE );
+ job->fifo_render = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE );
+ job->fifo_mpeg4 = hb_fifo_init( FIFO_MINI, FIFO_MINI_WAKE );
+ }
+ else
+#endif
+ {
+ job->fifo_mpeg2 = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE );
+ job->fifo_raw = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+ job->fifo_sync = hb_fifo_init( FIFO_SMALL, FIFO_SMALL_WAKE );
+ job->fifo_mpeg4 = hb_fifo_init( FIFO_LARGE, FIFO_LARGE_WAKE );
+ job->fifo_render = NULL; // Attached to filter chain
+ }
/* Audio fifos must be initialized before sync */
if (!job->indepth_scan)
@@ -1028,6 +1164,9 @@ static void do_job(hb_job_t *job)
case HB_VCODEC_X264:
w = hb_get_work( WORK_ENCX264 );
break;
+ case HB_VCODEC_QSV_H264:
+ w = hb_get_work( WORK_ENCQSV );
+ break;
case HB_VCODEC_THEORA:
w = hb_get_work( WORK_ENCTHEORA );
break;
@@ -1524,8 +1663,27 @@ static void filter_loop( void * _f )
}
buf_out = NULL;
+
+#ifdef USE_QSV
+ hb_buffer_t *last_buf_in = buf_in;
+#endif
+
f->status = f->work( f, &buf_in, &buf_out );
+#ifdef USE_QSV
+ if (f->status == HB_FILTER_DELAY &&
+ last_buf_in->qsv_details.filter_details != NULL && buf_out == NULL)
+ {
+ hb_filter_private_t_qsv *qsv_user = buf_in ? buf_in->qsv_details.filter_details : last_buf_in->qsv_details.filter_details ;
+ qsv_user->post.status = f->status;
+
+ hb_lock(qsv_user->post.frame_completed_lock);
+ qsv_user->post.frame_go = 1;
+ hb_cond_broadcast(qsv_user->post.frame_completed);
+ hb_unlock(qsv_user->post.frame_completed_lock);
+
+ }
+#endif
if ( buf_out && f->chapter_val && f->chapter_time <= buf_out->s.start )
{
buf_out->s.new_chap = f->chapter_val;
diff --git a/make/configure.py b/make/configure.py
index 9a98017ce..d558372ab 100644
--- a/make/configure.py
+++ b/make/configure.py
@@ -1175,6 +1175,8 @@ def createCLI():
grp.add_option( '--disable-gst', default=False, action='store_true', help=h )
h = IfHost( 'enable use of ffmpeg mpeg2 decoding', '*-*-*', none=optparse.SUPPRESS_HELP ).value
grp.add_option( '--enable-ff-mpeg2', default=False, action='store_true', help=h )
+ h = IfHost( 'enable use of Intel Quick Sync Video hardware acceleration', '*-*-*', none=optparse.SUPPRESS_HELP ).value
+ grp.add_option( '--enable-qsv', default=False, action='store_true', help=h )
h = IfHost( 'enable use of fdk-aac encoder', '*-*-*', none=optparse.SUPPRESS_HELP ).value
grp.add_option( '--enable-fdk-aac', dest="enable_fdk_aac", default=not host.match( '*-*-darwin*' ), action='store_true', help=h )
@@ -1645,6 +1647,7 @@ int main ()
doc.add( 'FEATURE.mp4v2', int( options.enable_mp4v2 ))
doc.add( 'FEATURE.libmkv', int( options.enable_libmkv ))
doc.add( 'FEATURE.avformat', int( options.enable_avformat ))
+ doc.add( 'FEATURE.qsv', int( options.enable_qsv ))
doc.add( 'FEATURE.xcode', int( not (Tools.xcodebuild.fail or options.disable_xcode or options.cross) ))
if not Tools.xcodebuild.fail and not options.disable_xcode:
diff --git a/make/include/main.defs b/make/include/main.defs
index 2554c11e7..bf6c5f4ec 100644
--- a/make/include/main.defs
+++ b/make/include/main.defs
@@ -68,6 +68,10 @@ ifneq ($(HAS.pthread),1)
endif
endif
+ifeq (1,$(FEATURE.qsv))
+ MODULES += contrib/libmfx
+endif
+
MODULES += contrib/x264
ifneq (,$(filter $(BUILD.system),cygwin mingw))
diff --git a/test/module.defs b/test/module.defs
index 66e95f2de..85c59533c 100644
--- a/test/module.defs
+++ b/test/module.defs
@@ -19,6 +19,11 @@ TEST.GCC.l = \
samplerate swscale theoraenc theoradec vorbis vorbisenc x264 \
bluray xml2 bz2 z
+ifeq (1,$(FEATURE.qsv))
+ TEST.GCC.l += mfx
+ TEST.GCC.D += USE_QSV HAVE_THREADS=1
+endif
+
ifeq (1,$(FEATURE.fdk_aac))
TEST.GCC.l += fdk-aac
endif
diff --git a/test/test.c b/test/test.c
index e76258977..1f7fd3acf 100644
--- a/test/test.c
+++ b/test/test.c
@@ -26,6 +26,10 @@
#include "lang.h"
#include "parsecsv.h"
+#ifdef USE_QSV
+#include "qsv_common.h"
+#endif
+
#if defined( __APPLE_CC__ )
#import <CoreServices/CoreServices.h>
#include <IOKit/IOKitLib.h>
@@ -132,6 +136,10 @@ static int start_at_frame = 0;
static int64_t stop_at_pts = 0;
static int stop_at_frame = 0;
static uint64_t min_title_duration = 10;
+#ifdef USE_QSV
+static int qsv_decode = 1;
+static int qsv_async_depth = -1;
+#endif
/* Exit cleanly on Ctrl-C */
static volatile int die = 0;
@@ -1869,6 +1877,14 @@ static int HandleEvents( hb_handle_t * h )
job->vcodec = vcodec;
}
+#ifdef USE_QSV
+ if (qsv_async_depth >= 0)
+ {
+ job->qsv_async_depth = qsv_async_depth;
+ }
+ job->qsv_decode = qsv_decode;
+#endif
+
/* Grab audio tracks */
if( atracks )
{
@@ -3073,9 +3089,22 @@ static void ShowHelp()
}
if( len )
fprintf( out, "%s\n", tmp );
- fprintf( out,
- " -x, --encopts <string> Specify advanced encoder options in the\n"
- " same style as mencoder (x264 and ffmpeg only):\n"
+ fprintf(out,
+ " -x, --encopts <string> Specify advanced encoder options in the\n");
+#ifdef USE_QSV
+if (hb_qsv_available())
+{
+ fprintf(out,
+ " same style as mencoder (x264/qsv/ffmpeg only):\n");
+}
+else
+#endif
+{
+ fprintf(out,
+ " same style as mencoder (x264 and ffmpeg only):\n");
+}
+
+ fprintf(out,
" option1=value1:option2=value2\n"
" --h264-profile When using x264, ensures compliance with the\n"
" <string> specified H.264 profile:\n"
@@ -3332,7 +3361,14 @@ static void ShowHelp()
"### Filters---------------------------------------------------------\n\n"
" -d, --deinterlace Deinterlace video with Libav, yadif or mcdeint\n"
- " <fast/slow/slower/bob> or omitted (default settings)\n"
+ " <fast/slow/slower/bob");
+#ifdef USE_QSV
+if (hb_qsv_available())
+{
+ fprintf(out, "/qsv");
+}
+#endif
+ fprintf( out, "> or omitted (default settings)\n"
" or\n"
" <YM:FD:MM:QP> (default 0:-1:-1:1)\n"
" -5, --decomb Selectively deinterlaces when it detects combing\n"
@@ -3415,10 +3451,22 @@ static void ShowHelp()
" If \"number\" is omitted, the first srt is default.\n"
" \"number\" is an 1 based index into the srt-file list\n"
"\n"
+ );
-
+#ifdef USE_QSV
+if (hb_qsv_available())
+{
+ fprintf( out,
+ "### Intel Quick Sync Video------------------------------------------------------\n\n"
+ " --disable-qsv-decoding Force software decoding of the video track.\n"
+ " --qsv-async-depth Specifies how many asynchronous operations should be\n"
+ " performed before the result is explicitly synchronized.\n"
+ " Default: 4. If zero, the value is not specified.\n"
+ "\n"
);
}
+#endif
+}
/****************************************************************************
* ShowPresets:
@@ -3560,7 +3608,9 @@ static int ParseOptions( int argc, char ** argv )
#define H264_LEVEL 286
#define NORMALIZE_MIX 287
#define AUDIO_DITHER 288
-
+ #define QSV_BASELINE 289
+ #define QSV_ASYNC_DEPTH 290
+
for( ;; )
{
static struct option long_options[] =
@@ -3568,7 +3618,12 @@ static int ParseOptions( int argc, char ** argv )
{ "help", no_argument, NULL, 'h' },
{ "update", no_argument, NULL, 'u' },
{ "verbose", optional_argument, NULL, 'v' },
- { "no-dvdnav", no_argument, NULL, DVDNAV },
+ { "no-dvdnav", no_argument, NULL, DVDNAV },
+#ifdef USE_QSV
+ { "qsv-baseline", no_argument, NULL, QSV_BASELINE },
+ { "qsv-async-depth", required_argument, NULL, QSV_ASYNC_DEPTH },
+ { "disable-qsv-decoding", no_argument, &qsv_decode, 0 },
+#endif
{ "format", required_argument, NULL, 'f' },
{ "input", required_argument, NULL, 'i' },
@@ -4185,6 +4240,21 @@ static int ParseOptions( int argc, char ** argv )
case MIN_DURATION:
min_title_duration = strtol( optarg, NULL, 0 );
break;
+#ifdef USE_QSV
+ case QSV_BASELINE:
+ if (hb_qsv_available())
+ {
+ /* XXX: for testing workarounds */
+ hb_qsv_info->capabilities &= ~HB_QSV_CAP_MSDK_API_1_6;
+ hb_qsv_info->capabilities &= ~HB_QSV_CAP_OPTION2_BRC;
+ hb_qsv_info->capabilities &= ~HB_QSV_CAP_OPTION2_TRELLIS;
+ hb_qsv_info->capabilities &= ~HB_QSV_CAP_OPTION2_LOOKAHEAD;
+ }
+ break;
+ case QSV_ASYNC_DEPTH:
+ qsv_async_depth = atoi(optarg);
+ break;
+#endif
default:
fprintf( stderr, "unknown option (%s)\n", argv[cur_optind] );
return -1;