diff options
-rw-r--r-- | contrib/ffmpeg/module.defs | 8 | ||||
-rw-r--r-- | libhb/common.c | 33 | ||||
-rw-r--r-- | libhb/common.h | 47 | ||||
-rw-r--r-- | libhb/decavcodec.c | 243 | ||||
-rw-r--r-- | libhb/decmpeg2.c | 4 | ||||
-rw-r--r-- | libhb/encx264.h | 5 | ||||
-rw-r--r-- | libhb/fifo.c | 13 | ||||
-rw-r--r-- | libhb/hb.c | 21 | ||||
-rw-r--r-- | libhb/internal.h | 13 | ||||
-rw-r--r-- | libhb/module.defs | 15 | ||||
-rw-r--r-- | libhb/muxavformat.c | 5 | ||||
-rw-r--r-- | libhb/muxmkv.c | 85 | ||||
-rw-r--r-- | libhb/muxmp4.c | 19 | ||||
-rw-r--r-- | libhb/ports.c | 21 | ||||
-rw-r--r-- | libhb/ports.h | 4 | ||||
-rw-r--r-- | libhb/scan.c | 4 | ||||
-rw-r--r-- | libhb/sync.c | 44 | ||||
-rw-r--r-- | libhb/work.c | 186 | ||||
-rw-r--r-- | make/configure.py | 3 | ||||
-rw-r--r-- | make/include/main.defs | 4 | ||||
-rw-r--r-- | test/module.defs | 5 | ||||
-rw-r--r-- | test/test.c | 84 |
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; |