diff options
author | Rodeo <[email protected]> | 2013-08-22 20:34:44 +0000 |
---|---|---|
committer | Rodeo <[email protected]> | 2013-08-22 20:34:44 +0000 |
commit | 3326f988806a5decae025727784a19c8cc223833 (patch) | |
tree | cd75adb1975d223d7a0fd43a31030a78939e73d3 | |
parent | d41905d539046445e1b81499ff7bd04d170c91d4 (diff) |
Big merge, QSV to trunk: part 2 (new files).
git-svn-id: svn://svn.handbrake.fr/HandBrake/trunk@5738 b64f7644-9d1e-0410-96f1-a4d463321fa5
-rw-r--r-- | contrib/ffmpeg/A00-qsv.patch | 2285 | ||||
-rw-r--r-- | contrib/libmfx/module.defs | 6 | ||||
-rw-r--r-- | contrib/libmfx/module.rules | 2 | ||||
-rw-r--r-- | libhb/enc_qsv.c | 1543 | ||||
-rw-r--r-- | libhb/enc_qsv.h | 38 | ||||
-rw-r--r-- | libhb/h264_common.h | 17 | ||||
-rw-r--r-- | libhb/qsv_common.c | 780 | ||||
-rw-r--r-- | libhb/qsv_common.h | 119 | ||||
-rw-r--r-- | libhb/qsv_filter.c | 648 | ||||
-rw-r--r-- | libhb/qsv_filter.h | 35 | ||||
-rw-r--r-- | libhb/qsv_filter_pp.c | 916 | ||||
-rw-r--r-- | libhb/qsv_filter_pp.h | 114 | ||||
-rw-r--r-- | libhb/qsv_memory.c | 120 | ||||
-rw-r--r-- | libhb/qsv_memory.h | 55 |
14 files changed, 6678 insertions, 0 deletions
diff --git a/contrib/ffmpeg/A00-qsv.patch b/contrib/ffmpeg/A00-qsv.patch new file mode 100644 index 000000000..908033617 --- /dev/null +++ b/contrib/ffmpeg/A00-qsv.patch @@ -0,0 +1,2285 @@ +diff -Naur ../../libav-v9.6/configure ./configure +--- ../../libav-v9.6/configure 2013-05-12 08:39:07.000000000 +0200 ++++ ./configure 2013-08-14 10:48:00.520497159 +0200 +@@ -133,6 +133,7 @@ + --enable-vaapi enable VAAPI code + --enable-vda enable VDA code + --enable-vdpau enable VDPAU code ++ --enable-qsv enable QSV code + + Individual component options: + --disable-everything disable all components listed below +@@ -1076,6 +1077,7 @@ + vaapi + vda + vdpau ++ qsv + version3 + xmm_clobber_test + x11grab +@@ -1629,6 +1631,7 @@ + wmv3_dxva2_hwaccel_select="vc1_dxva2_hwaccel" + wmv3_vaapi_hwaccel_select="vc1_vaapi_hwaccel" + wmv3_vdpau_decoder_select="vc1_vdpau_decoder" ++h264_qsv_decoder_select="qsv h264_decoder" + + # parsers + h264_parser_select="error_resilience golomb h264dsp h264pred mpegvideo" +@@ -3584,6 +3587,12 @@ + check_cpp_condition vdpau/vdpau.h "defined VDP_DECODER_PROFILE_MPEG4_PART2_ASP" || + { echolog "Please upgrade to libvdpau >= 0.2 if you would like vdpau support." && disable vdpau; } + fi ++if enabled qsv; then ++ disable qsv ++ check_header msdk/mfxvideo.h && enable qsv ++else ++ disable qsv ++fi + + enabled debug && add_cflags -g"$debuglevel" && add_asflags -g"$debuglevel" + +@@ -3795,6 +3804,7 @@ + echo "libdxva2 enabled ${dxva2-no}" + echo "libva enabled ${vaapi-no}" + echo "libvdpau enabled ${vdpau-no}" ++echo "libqsv enabled ${qsv-no}" + echo "AVISynth enabled ${avisynth-no}" + echo "frei0r enabled ${frei0r-no}" + echo "gnutls enabled ${gnutls-no}" +diff -Naur ../../libav-v9.6/libavcodec/allcodecs.c ./libavcodec/allcodecs.c +--- ../../libav-v9.6/libavcodec/allcodecs.c 2013-05-12 08:39:07.000000000 +0200 ++++ ./libavcodec/allcodecs.c 2013-08-14 10:48:00.520497159 +0200 +@@ -143,6 +143,7 @@ + REGISTER_DECODER(H263I, h263i); + REGISTER_ENCODER(H263P, h263p); + REGISTER_DECODER(H264, h264); ++ REGISTER_DECODER(H264_QSV, h264_qsv); + REGISTER_DECODER(H264_VDPAU, h264_vdpau); + REGISTER_ENCDEC (HUFFYUV, huffyuv); + REGISTER_DECODER(IDCIN, idcin); +diff -Naur ../../libav-v9.6/libavcodec/Makefile ./libavcodec/Makefile +--- ../../libav-v9.6/libavcodec/Makefile 2013-05-12 08:39:07.000000000 +0200 ++++ ./libavcodec/Makefile 2013-08-14 10:48:00.521497282 +0200 +@@ -10,6 +10,7 @@ + vdpau.h \ + version.h \ + xvmc.h \ ++ qsv.h \ + + OBJS = allcodecs.o \ + audioconvert.o \ +@@ -196,6 +197,7 @@ + h264_loopfilter.o h264_direct.o \ + cabac.o h264_sei.o h264_ps.o \ + h264_refs.o h264_cavlc.o h264_cabac.o ++OBJS-$(CONFIG_H264_QSV_DECODER) += qsv_h264.o qsv.o + OBJS-$(CONFIG_H264_DXVA2_HWACCEL) += dxva2_h264.o + OBJS-$(CONFIG_H264_VAAPI_HWACCEL) += vaapi_h264.o + OBJS-$(CONFIG_H264_VDA_HWACCEL) += vda_h264.o +diff -Naur ../../libav-v9.6/libavcodec/qsv.c ./libavcodec/qsv.c +--- ../../libav-v9.6/libavcodec/qsv.c 1970-01-01 01:00:00.000000000 +0100 ++++ ./libavcodec/qsv.c 2013-08-19 21:32:01.704244071 +0200 +@@ -0,0 +1,646 @@ ++/* ********************************************************************* *\ ++ ++Copyright (C) 2013 Intel Corporation. All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions are met: ++- Redistributions of source code must retain the above copyright notice, ++this list of conditions and the following disclaimer. ++- Redistributions in binary form must reproduce the above copyright notice, ++this list of conditions and the following disclaimer in the documentation ++and/or other materials provided with the distribution. ++- Neither the name of Intel Corporation nor the names of its contributors ++may be used to endorse or promote products derived from this software ++without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR ++IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, ++INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++\* ********************************************************************* */ ++ ++#include "qsv.h" ++ ++#include "avcodec.h" ++#include "internal.h" ++ ++int av_qsv_get_free_encode_task(av_qsv_list * tasks) ++{ ++ int ret = MFX_ERR_NOT_FOUND; ++ int i = 0; ++ if (tasks) ++ for (i = 0; i < av_qsv_list_count(tasks); i++) { ++ av_qsv_task *task = av_qsv_list_item(tasks, i); ++ if (task->stage && task->stage->out.sync) ++ if (!(*task->stage->out.sync->p_sync)) { ++ ret = i; ++ break; ++ } ++ } ++ return ret; ++} ++ ++int av_qsv_get_free_sync(av_qsv_space * space, av_qsv_context * qsv) ++{ ++ int ret = -1; ++ int counter = 0; ++ ++ while (1) { ++ for (int i = 0; i < space->sync_num; i++) { ++ if (!(*(space->p_syncp[i]->p_sync)) && ++ 0 == space->p_syncp[i]->in_use ) { ++ if (i > space->sync_num_max_used) ++ space->sync_num_max_used = i; ++ ff_qsv_atomic_inc(&space->p_syncp[i]->in_use); ++ return i; ++ } ++ } ++#if HAVE_THREADS ++ if (++counter >= AV_QSV_REPEAT_NUM_DEFAULT) { ++#endif ++ av_log(NULL, AV_LOG_FATAL, "not enough to have %d sync point(s) allocated\n", ++ space->sync_num); ++ break; ++#if HAVE_THREADS ++ } ++ av_qsv_sleep(5); ++#endif ++ } ++ return ret; ++} ++ ++int av_qsv_get_free_surface(av_qsv_space * space, av_qsv_context * qsv, ++ mfxFrameInfo * info, av_qsv_split part) ++{ ++ int ret = -1; ++ int from = 0; ++ int up = space->surface_num; ++ int counter = 0; ++ ++ while (1) { ++ from = 0; ++ up = space->surface_num; ++ if (part == QSV_PART_LOWER) ++ up /= 2; ++ if (part == QSV_PART_UPPER) ++ from = up / 2; ++ ++ for (int i = from; i < up; i++) { ++ if (0 == space->p_surfaces[i]->Data.Locked) { ++ memcpy(&(space->p_surfaces[i]->Info), info, ++ sizeof(mfxFrameInfo)); ++ if (i > space->surface_num_max_used) ++ space->surface_num_max_used = i; ++ return i; ++ } ++ } ++#if HAVE_THREADS ++ if (++counter >= AV_QSV_REPEAT_NUM_DEFAULT) { ++#endif ++ av_log(NULL, AV_LOG_FATAL, ++ "not enough to have %d surface(s) allocated\n", up); ++ break; ++#if HAVE_THREADS ++ } ++ av_qsv_sleep(5); ++#endif ++ } ++ return ret; ++} ++ ++int ff_qsv_is_surface_in_pipe(mfxFrameSurface1 * p_surface, av_qsv_context * qsv) ++{ ++ int ret = 0; ++ int a, b,i; ++ av_qsv_list *list = 0; ++ av_qsv_stage *stage = 0; ++ ++ if (!p_surface) ++ return ret; ++ if (!qsv->pipes) ++ return ret; ++ ++ for (a = 0; a < av_qsv_list_count(qsv->pipes); a++) { ++ list = av_qsv_list_item(qsv->pipes, a); ++ for (b = 0; b < av_qsv_list_count(list); b++) { ++ stage = av_qsv_list_item(list, b); ++ if (p_surface == stage->out.p_surface) ++ return (stage->type << 16) | 2; ++ if (p_surface == stage->in.p_surface) ++ return (stage->type << 16) | 1; ++ } ++ } ++ return ret; ++} ++ ++int ff_qsv_is_sync_in_pipe(mfxSyncPoint * sync, av_qsv_context * qsv) ++{ ++ int ret = 0; ++ int a, b; ++ av_qsv_list *list = 0; ++ av_qsv_stage *stage = 0; ++ ++ if (!sync) ++ return ret; ++ if (!qsv->pipes) ++ return ret; ++ ++ for (a = 0; a < av_qsv_list_count(qsv->pipes); a++) { ++ list = av_qsv_list_item(qsv->pipes, a); ++ for (b = 0; b < av_qsv_list_count(list); b++) { ++ stage = av_qsv_list_item(list, b); ++ if (sync == stage->out.sync->p_sync) { ++ return 1; ++ } ++ } ++ } ++ return ret; ++} ++ ++av_qsv_stage *av_qsv_stage_init(void) ++{ ++ av_qsv_stage *stage = av_mallocz(sizeof(av_qsv_stage)); ++ return stage; ++} ++ ++void av_qsv_stage_clean(av_qsv_stage ** stage) ++{ ++ if ((*stage)->out.sync) { ++ if ((*stage)->out.sync->p_sync) ++ *(*stage)->out.sync->p_sync = 0; ++ if ((*stage)->out.sync->in_use > 0) ++ ff_qsv_atomic_dec(&(*stage)->out.sync->in_use); ++ (*stage)->out.sync = 0; ++ } ++ if ((*stage)->out.p_surface) { ++ (*stage)->out.p_surface = 0; ++ ++ } ++ if ((*stage)->in.p_surface) { ++ (*stage)->in.p_surface = 0; ++ } ++ ++ av_freep(stage); ++} ++ ++void av_qsv_add_context_usage(av_qsv_context * qsv, int is_threaded) ++{ ++ int is_active = 0; ++#if HAVE_THREADS ++ int mut_ret = 0; ++#endif ++ ++ is_active = ff_qsv_atomic_inc(&qsv->is_context_active); ++ if (is_active == 1) { ++ memset(&qsv->mfx_session, 0, sizeof(mfxSession)); ++ av_qsv_pipe_list_create(&qsv->pipes, is_threaded); ++ ++ qsv->dts_seq = av_qsv_list_init(is_threaded); ++ ++#if HAVE_THREADS ++ if (is_threaded) { ++ qsv->qts_seq_mutex = av_mallocz(sizeof(pthread_mutex_t)); ++ if (qsv->qts_seq_mutex){ ++ mut_ret = pthread_mutex_init(qsv->qts_seq_mutex, NULL); ++ if(mut_ret) ++ av_log(NULL, AV_LOG_ERROR, "pthread_mutex_init issue[%d] at %s\n",mut_ret,__FUNCTION__); ++ } ++ ++ } else ++#endif ++ qsv->qts_seq_mutex = 0; ++ } ++} ++ ++int av_qsv_context_clean(av_qsv_context * qsv) ++{ ++ int is_active = 0; ++ mfxStatus sts = MFX_ERR_NONE; ++#if HAVE_THREADS ++ int mut_ret = 0; ++#endif ++ ++ is_active = ff_qsv_atomic_dec(&qsv->is_context_active); ++ ++ // spaces would have to be cleaned on the own, ++ // here we care about the rest, common stuff ++ if (is_active == 0) { ++ ++ if (qsv->dts_seq) { ++ while (av_qsv_list_count(qsv->dts_seq)) ++ av_qsv_dts_pop(qsv); ++ ++ av_qsv_list_close(&qsv->dts_seq); ++ } ++#if HAVE_THREADS ++ if (qsv->qts_seq_mutex) { ++ mut_ret = pthread_mutex_destroy(qsv->qts_seq_mutex); ++ if(mut_ret) ++ av_log(NULL, AV_LOG_ERROR, "pthread_mutex_destroy issue[%d] at %s\n", mut_ret,__FUNCTION__); ++#endif ++ qsv->qts_seq_mutex = 0; ++#if HAVE_THREADS ++ } ++#endif ++ ++ if (qsv->pipes) ++ av_qsv_pipe_list_clean(&qsv->pipes); ++ ++ if (qsv->mfx_session) { ++ sts = MFXClose(qsv->mfx_session); ++ AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); ++ qsv->mfx_session = 0; ++ } ++ } ++ return 0; ++} ++ ++void av_qsv_pipe_list_create(av_qsv_list ** list, int is_threaded) ++{ ++ if (!*list) ++ *list = av_qsv_list_init(is_threaded); ++} ++ ++void av_qsv_pipe_list_clean(av_qsv_list ** list) ++{ ++ av_qsv_list *stage; ++ int i = 0; ++ if (*list) { ++ for (i = av_qsv_list_count(*list); i > 0; i--) { ++ stage = av_qsv_list_item(*list, i - 1); ++ av_qsv_flush_stages(*list, &stage); ++ } ++ av_qsv_list_close(list); ++ } ++} ++ ++void av_qsv_add_stagee(av_qsv_list ** list, av_qsv_stage * stage, int is_threaded) ++{ ++ if (!*list) ++ *list = av_qsv_list_init(is_threaded); ++ av_qsv_list_add(*list, stage); ++} ++ ++av_qsv_stage *av_qsv_get_last_stage(av_qsv_list * list) ++{ ++ av_qsv_stage *stage = 0; ++ int size = 0; ++ ++ av_qsv_list_lock(list); ++ size = av_qsv_list_count(list); ++ if (size > 0) ++ stage = av_qsv_list_item(list, size - 1); ++ av_qsv_list_unlock(list); ++ ++ return stage; ++} ++ ++void av_qsv_flush_stages(av_qsv_list * list, av_qsv_list ** item) ++{ ++ int i = 0; ++ int x = 0; ++ av_qsv_stage *stage = 0; ++ av_qsv_list *to_remove_list = 0; ++ av_qsv_list *to_remove_atom_list = 0; ++ av_qsv_list *to_remove_atom = 0; ++ ++ for (i = 0; i < av_qsv_list_count(*item); i++) { ++ stage = av_qsv_list_item(*item, i); ++ if(stage->pending){ ++ if(!to_remove_list) ++ to_remove_list = av_qsv_list_init(0); ++ av_qsv_list_add(to_remove_list, stage->pending); ++ } ++ av_qsv_stage_clean(&stage); ++ // should actually remove from the list but ok... ++ } ++ av_qsv_list_rem(list, *item); ++ av_qsv_list_close(item); ++ ++ if(to_remove_list){ ++ for (i = av_qsv_list_count(to_remove_list); i > 0; i--){ ++ to_remove_atom_list = av_qsv_list_item(to_remove_list, i-1); ++ for (x = av_qsv_list_count(to_remove_atom_list); x > 0; x--){ ++ to_remove_atom = av_qsv_list_item(to_remove_atom_list, x-1); ++ av_qsv_flush_stages(list,&to_remove_atom); ++ } ++ } ++ av_qsv_list_close(&to_remove_list); ++ } ++} ++ ++av_qsv_list *av_qsv_pipe_by_stage(av_qsv_list * list, av_qsv_stage * stage) ++{ ++ av_qsv_list *item = 0; ++ av_qsv_stage *cur_stage = 0; ++ int i = 0; ++ int a = 0; ++ for (i = 0; i < av_qsv_list_count(list); i++) { ++ item = av_qsv_list_item(list, i); ++ for (a = 0; a < av_qsv_list_count(item); a++) { ++ cur_stage = av_qsv_list_item(item, a); ++ if (cur_stage == stage) ++ return item; ++ } ++ } ++ return 0; ++} ++ ++// no duplicate of the same value, if end == 0 : working over full length ++void av_qsv_dts_ordered_insert(av_qsv_context * qsv, int start, int end, ++ int64_t dts, int iter) ++{ ++ av_qsv_dts *cur_dts = 0; ++ av_qsv_dts *new_dts = 0; ++ int i = 0; ++#if HAVE_THREADS ++ int mut_ret = 0; ++#endif ++ ++ ++#if HAVE_THREADS ++ if (iter == 0 && qsv->qts_seq_mutex){ ++ mut_ret = pthread_mutex_lock(qsv->qts_seq_mutex); ++ if(mut_ret) ++ av_log(NULL, AV_LOG_ERROR, "pthread_mutex_lock issue[%d] at %s\n",mut_ret, __FUNCTION__); ++ } ++#endif ++ ++ if (end == 0) ++ end = av_qsv_list_count(qsv->dts_seq); ++ ++ if (end <= start) { ++ new_dts = av_mallocz(sizeof(av_qsv_dts)); ++ if( new_dts ) { ++ new_dts->dts = dts; ++ av_qsv_list_add(qsv->dts_seq, new_dts); ++ } ++ } else ++ for (i = end; i > start; i--) { ++ cur_dts = av_qsv_list_item(qsv->dts_seq, i - 1); ++ if (cur_dts->dts < dts) { ++ new_dts = av_mallocz(sizeof(av_qsv_dts)); ++ if( new_dts ) { ++ new_dts->dts = dts; ++ av_qsv_list_insert(qsv->dts_seq, i, new_dts); ++ } ++ break; ++ } else if (cur_dts->dts == dts) ++ break; ++ } ++#if HAVE_THREADS ++ if (iter == 0 && qsv->qts_seq_mutex){ ++ mut_ret = pthread_mutex_unlock(qsv->qts_seq_mutex); ++ if(mut_ret) ++ av_log(NULL, AV_LOG_ERROR, "pthread_mutex_unlock issue[%d] at %s\n",mut_ret, __FUNCTION__); ++ } ++#endif ++} ++ ++void av_qsv_dts_pop(av_qsv_context * qsv) ++{ ++ av_qsv_dts *item = 0; ++#if HAVE_THREADS ++ int mut_ret = 0; ++#endif ++ ++#if HAVE_THREADS ++ if (qsv && qsv->qts_seq_mutex){ ++ mut_ret = pthread_mutex_lock(qsv->qts_seq_mutex); ++ if(mut_ret) ++ av_log(NULL, AV_LOG_ERROR, "pthread_mutex_lock issue[%d] at %s\n",mut_ret, __FUNCTION__); ++ } ++#endif ++ ++ if (av_qsv_list_count(qsv->dts_seq)) { ++ item = av_qsv_list_item(qsv->dts_seq, 0); ++ av_qsv_list_rem(qsv->dts_seq, item); ++ av_free(item); ++ } ++#if HAVE_THREADS ++ if (qsv && qsv->qts_seq_mutex){ ++ mut_ret = pthread_mutex_unlock(qsv->qts_seq_mutex); ++ if(mut_ret) ++ av_log(NULL, AV_LOG_ERROR, "pthread_mutex_lock issue[%d] at %s\n",mut_ret, __FUNCTION__); ++ } ++#endif ++} ++ ++ ++av_qsv_list *av_qsv_list_init(int is_threaded) ++{ ++ av_qsv_list *l; ++#if HAVE_THREADS ++ int mut_ret; ++#endif ++ ++ l = av_mallocz(sizeof(av_qsv_list)); ++ if (!l) ++ return 0; ++ l->items = av_mallocz(AV_QSV_JOB_SIZE_DEFAULT * sizeof(void *)); ++ if (!l->items) ++ return 0; ++ l->items_alloc = AV_QSV_JOB_SIZE_DEFAULT; ++ ++#if HAVE_THREADS ++ if (is_threaded) { ++ l->mutex = av_mallocz(sizeof(pthread_mutex_t)); ++ if (l->mutex){ ++ mut_ret = pthread_mutexattr_init(&l->mta); ++ if( mut_ret ) ++ av_log(NULL, AV_LOG_ERROR, "pthread_mutexattr_init issue[%d] at %s\n",mut_ret, __FUNCTION__); ++ mut_ret = pthread_mutexattr_settype(&l->mta, PTHREAD_MUTEX_RECURSIVE /*PTHREAD_MUTEX_ERRORCHECK*/); ++ if( mut_ret ) ++ av_log(NULL, AV_LOG_ERROR, "pthread_mutexattr_settype issue[%d] at %s\n",mut_ret, __FUNCTION__); ++ mut_ret = pthread_mutex_init(l->mutex, &l->mta); ++ if( mut_ret ) ++ av_log(NULL, AV_LOG_ERROR, "pthread_mutex_init issue[%d] at %s\n",mut_ret, __FUNCTION__); ++ } ++ } else ++#endif ++ l->mutex = 0; ++ return l; ++} ++ ++int av_qsv_list_count(av_qsv_list * l) ++{ ++ int count; ++ ++ av_qsv_list_lock(l); ++ count = l->items_count; ++ av_qsv_list_unlock(l); ++ return count; ++} ++ ++int av_qsv_list_add(av_qsv_list * l, void *p) ++{ ++ int pos = -1; ++ ++ if (!p) { ++ return pos; ++ } ++ ++ av_qsv_list_lock(l); ++ ++ if (l->items_count == l->items_alloc) { ++ /* We need a bigger boat */ ++ l->items_alloc += AV_QSV_JOB_SIZE_DEFAULT; ++ l->items = av_realloc(l->items, l->items_alloc * sizeof(void *)); ++ } ++ ++ l->items[l->items_count] = p; ++ pos = (l->items_count); ++ l->items_count++; ++ ++ av_qsv_list_unlock(l); ++ ++ return pos; ++} ++ ++void av_qsv_list_rem(av_qsv_list * l, void *p) ++{ ++ int i; ++ ++ av_qsv_list_lock(l); ++ ++ /* Find the item in the list */ ++ for (i = 0; i < l->items_count; i++) { ++ if (l->items[i] == p) { ++ /* Shift all items after it sizeof( void * ) bytes earlier */ ++ memmove(&l->items[i], &l->items[i + 1], ++ (l->items_count - i - 1) * sizeof(void *)); ++ ++ l->items_count--; ++ break; ++ } ++ } ++ ++ av_qsv_list_unlock(l); ++} ++ ++void *av_qsv_list_item(av_qsv_list * l, int i) ++{ ++ void *ret = NULL; ++ ++ if (i < 0) ++ return NULL; ++ ++ av_qsv_list_lock(l); ++ if( i < l->items_count) ++ ret = l->items[i]; ++ av_qsv_list_unlock(l); ++ return ret; ++} ++ ++void av_qsv_list_insert(av_qsv_list * l, int pos, void *p) ++{ ++ ++ if (!p) ++ return; ++ ++ av_qsv_list_lock(l); ++ ++ if (l->items_count == l->items_alloc) { ++ l->items_alloc += AV_QSV_JOB_SIZE_DEFAULT; ++ l->items = av_realloc(l->items, l->items_alloc * sizeof(void *)); ++ } ++ ++ if (l->items_count != pos) { ++ memmove(&l->items[pos + 1], &l->items[pos], ++ (l->items_count - pos) * sizeof(void *)); ++ } ++ ++ l->items[pos] = p; ++ l->items_count--; ++ ++ av_qsv_list_unlock(l); ++} ++ ++void av_qsv_list_close(av_qsv_list ** _l) ++{ ++ av_qsv_list *l = *_l; ++#if HAVE_THREADS ++ int mut_ret; ++#endif ++ ++ av_qsv_list_lock(l); ++ ++ av_free(l->items); ++ ++#if HAVE_THREADS ++ if (l->mutex){ ++ mut_ret = pthread_mutex_unlock(l->mutex); ++ if( mut_ret ) ++ av_log(NULL, AV_LOG_ERROR, "pthread_mutex_unlock issue[%d] at %s\n",mut_ret, __FUNCTION__); ++ mut_ret = pthread_mutex_destroy(&l->mutex); ++ mut_ret = pthread_mutexattr_destroy(&l->mta); ++ } ++#endif ++ av_freep(_l); ++} ++ ++int av_qsv_list_lock(av_qsv_list *l){ ++ int ret = 0; ++#if HAVE_THREADS ++ if (l->mutex){ ++ ret = pthread_mutex_lock(l->mutex); ++ if( ret ) ++ av_log(NULL, AV_LOG_ERROR, "pthread_mutex_lock issue[%d] at %s\n",ret, __FUNCTION__); ++ } ++#endif ++ return ret; ++} ++ ++int av_qsv_list_unlock(av_qsv_list *l){ ++ int ret = 0; ++#if HAVE_THREADS ++ if (l->mutex){ ++ ret = pthread_mutex_unlock(l->mutex); ++ if( ret ) ++ av_log(NULL, AV_LOG_ERROR, "pthread_mutex_unlock issue[%d] at %s\n",ret, __FUNCTION__); ++ } ++#endif ++ return ret; ++} ++ ++int av_is_qsv_available(mfxIMPL impl, mfxVersion * ver) ++{ ++ mfxStatus sts = MFX_ERR_NONE; ++ mfxSession mfx_session; ++ ++ memset(&mfx_session, 0, sizeof(mfxSession)); ++ sts = MFXInit(impl, ver, &mfx_session); ++ if (sts >= 0) ++ MFXClose(mfx_session); ++ return sts; ++} ++ ++void av_qsv_wait_on_sync(av_qsv_context *qsv, av_qsv_stage *stage) ++{ ++ int iter = 0; ++ mfxStatus sts = MFX_ERR_NONE; ++ if( stage ) ++ if(*stage->out.sync->p_sync){ ++ while(1){ ++ iter++; ++ sts = MFXVideoCORE_SyncOperation(qsv->mfx_session,*stage->out.sync->p_sync, AV_QSV_SYNC_TIME_DEFAULT); ++ if(MFX_WRN_IN_EXECUTION == sts){ ++ ++ if(iter>20) ++ AV_QSV_DEBUG_ASSERT(1, "Sync failed"); ++ ++ av_qsv_sleep(10); ++ continue; ++ } ++ AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); ++ break; ++ } ++ } ++} +\ No newline at end of file +diff -Naur ../../libav-v9.6/libavcodec/qsv.h ./libavcodec/qsv.h +--- ../../libav-v9.6/libavcodec/qsv.h 1970-01-01 01:00:00.000000000 +0100 ++++ ./libavcodec/qsv.h 2013-08-19 21:32:01.709244686 +0200 +@@ -0,0 +1,494 @@ ++/* ********************************************************************* *\ ++ ++Copyright (C) 2013 Intel Corporation. All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions are met: ++- Redistributions of source code must retain the above copyright notice, ++this list of conditions and the following disclaimer. ++- Redistributions in binary form must reproduce the above copyright notice, ++this list of conditions and the following disclaimer in the documentation ++and/or other materials provided with the distribution. ++- Neither the name of Intel Corporation nor the names of its contributors ++may be used to endorse or promote products derived from this software ++without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR ++IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, ++INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++\* ********************************************************************* */ ++ ++#ifndef AVCODEC_QSV_H ++#define AVCODEC_QSV_H ++ ++/** ++ * @file ++ * @ingroup lavc_codec_hwaccel_qsv ++ * Common header for QSV/MediaSDK acceleration ++ */ ++ ++/** ++ * @defgroup lavc_codec_hwaccel_qsv QSV/MediaSDK based Decode/Encode and VPP ++ * @ingroup lavc_codec_hwaccel ++ * ++ * As Intel Quick Sync Video (QSV) can decode/preprocess/encode with HW ++ * acceleration. ++ * ++ * Supported features: ++ * - access: ++ * - format AV_PIX_FMT_QSV_H264, AVCodec decoder based implementation ++ * - name "h264_qsv", avcodec_find_decoder_by_name( "h264_qsv") ++ * - IO Pattern: ++ * - Opaque memory: MFX_IOPATTERN_OUT_OPAQUE_MEMORY // Video memory is ++ * MFX_IMPL_HARDWARE or MFX_IMPL_AUTO and runtime support, ++ * otherwise: System Memory ++ * - System memory: MFX_IOPATTERN_OUT_SYSTEM_MEMORY ++ * - Allocators: ++ * - default allocator for System memory: MFX_MEMTYPE_SYSTEM_MEMORY ++ * - details: ++ * implementation as "per frame" ++ * ++ * TODO list: ++ * - access: ++ * - format AV_PIX_FMT_QSV_MPEG2 ++ * - format AV_PIX_FMT_QSV_VC1 ++ * - format AV_PIX_FMT_QSV, see "details" below ++ * - IO Pattern: ++ * - VIDEO_MEMORY // MFX_IOPATTERN_OUT_VIDEO_MEMORY ++ * - Allocators: ++ * - Video memory: MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET / ++ * MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET ++ * - details: ++ * "per slice" support: AV_PIX_FMT_QSV with AVHWAccel based implementation ++ * ++ * Note av_qsv_config struct required to fill in via ++ * AVCodecContext.hwaccel_context ++ * ++ * As per frame, note AVFrame.data[2] (qsv_atom) used for frame atom id, ++ * data/linesize should be used together with SYSTEM_MEMORY and tested ++ * ++ * Note: Compilation would require: ++ * - Intel MediaSDK headers, Full SDK is avaialble from the original web site: ++ * http://software.intel.com/en-us/vcsource/tools/media-SDK ++ * Will be referenced as msdk/*.h (mfxdefs.h, mfxstructures.h, ... ) ++ * and ++ * - Final application has to link against Intel MediaSDK dispatcher, available ++ * at MediaSDK as well ++ * ++ * Target OS: as per available dispatcher and driver support ++ * ++ * Implementation details: ++ * Provided struct av_qsv_context contain several struct av_qsv_space(s) for decode, ++ * VPP and encode. ++ * av_qsv_space just contain needed environment for the appropriate action. ++ * Based on this - pipeline (see pipes) will be build to pass details such as ++ * mfxFrameSurface1* and mfxSyncPoint* from one action to the next. ++ * ++ * Resources re-usage (av_qsv_flush_stages): ++ * av_qsv_context *qsv = (av_qsv_context *)video_codec_ctx->priv_data; ++ * av_qsv_list *pipe = (av_qsv_list *)video_frame->data[2]; ++ * av_qsv_flush_stages( qsv->pipes, &pipe ); ++ * ++ * DTS re-usage: ++ * av_qsv_dts_pop(qsv); ++ * ++ * for video,DX9/11 memory it has to be Unlock'ed as well ++ * ++ * Implementation is thread aware and uses synchronization point(s) from MediaSDK ++ * as per configuration. ++ * ++ * For the details of MediaSDK usage and options available - please refer to the ++ * available documentation at MediaSDK. ++ * ++ * Feature set used from MSDK is defined by AV_QSV_MSDK_VERSION_MAJOR and ++ * AV_QSV_MSDK_VERSION_MINOR ++ * ++ * @{ ++ */ ++ ++#include <stdint.h> ++#include <string.h> ++#include "msdk/mfxvideo.h" ++#include "libavutil/mem.h" ++#include "libavutil/time.h" ++ ++#ifdef HAVE_AV_CONFIG_H ++#include "config.h" ++#endif ++ ++#if HAVE_THREADS ++#if defined (__GNUC__) ++#include <pthread.h> ++#define ff_qsv_atomic_inc(ptr) __sync_add_and_fetch(ptr,1) ++#define ff_qsv_atomic_dec(ptr) __sync_sub_and_fetch (ptr,1) ++#elif HAVE_WINDOWS_H // MSVC case ++#include <windows.h> ++#if HAVE_PTHREADS ++#include <pthread.h> ++#elif HAVE_W32THREADS ++#include "w32pthreads.h" ++#endif ++#define ff_qsv_atomic_inc(ptr) InterlockedIncrement(ptr) ++#define ff_qsv_atomic_dec(ptr) InterlockedDecrement (ptr) ++#else ++// targeting only for MinGW or MSVC ++#endif ++ ++#else ++#define ff_qsv_atomic_inc(ptr) ((*ptr)++) ++#define ff_qsv_atomic_dec(ptr) ((*ptr)--) ++#endif ++ ++ ++// sleep is defined in milliseconds ++#define av_qsv_sleep(x) av_usleep((x)*1000) ++ ++#define AV_QSV_ZERO_MEMORY(VAR) {memset(&VAR, 0, sizeof(VAR));} ++#define AV_QSV_ALIGN32(X) (((mfxU32)((X)+31)) & (~ (mfxU32)31)) ++#define AV_QSV_ALIGN16(value) (((value + 15) >> 4) << 4) ++#ifndef AV_QSV_PRINT_RET_MSG ++#define AV_QSV_PRINT_RET_MSG(ERR) { av_log(NULL, AV_LOG_FATAL,"Error code %d,\t%s\t%d\n", ERR, __FUNCTION__, __LINE__); } ++#endif ++ ++#ifndef AV_QSV_DEBUG_ASSERT ++#define AV_QSV_DEBUG_ASSERT(x,y) {if ((x)) {av_log(NULL, AV_LOG_FATAL,"\nASSERT: %s\n",y);};} ++#endif ++ ++#define AV_QSV_CHECK_RESULT(P, X, ERR) {if ((X) > (P)) {AV_QSV_PRINT_RET_MSG(ERR); return ERR;}} ++#define AV_QSV_CHECK_POINTER(P, ERR) {if (!(P)) {AV_QSV_PRINT_RET_MSG(ERR); return ERR;}} ++#define AV_QSV_IGNORE_MFX_STS(P, X) {if ((X) == (P)) {P = MFX_ERR_NONE;}} ++ ++#define AV_QSV_ID_BUFFER MFX_MAKEFOURCC('B','U','F','F') ++#define AV_QSV_ID_FRAME MFX_MAKEFOURCC('F','R','M','E') ++ ++#define AV_QSV_SURFACE_NUM 80 ++#define AV_QSV_SYNC_NUM AV_QSV_SURFACE_NUM*3/4 ++#define AV_QSV_BUF_SIZE_DEFAULT 4096*2160*10 ++#define AV_QSV_JOB_SIZE_DEFAULT 10 ++#define AV_QSV_SYNC_TIME_DEFAULT 10000 ++// see av_qsv_get_free_sync, av_qsv_get_free_surface , 100 if usleep(10*1000)(10ms) == 1 sec ++#define AV_QSV_REPEAT_NUM_DEFAULT 100 ++#define AV_QSV_ASYNC_DEPTH_DEFAULT 4 ++ ++// version of MSDK/QSV API currently used ++#define AV_QSV_MSDK_VERSION_MAJOR 1 ++#define AV_QSV_MSDK_VERSION_MINOR 3 ++ ++typedef enum AV_QSV_STAGE_TYPE { ++ ++#define AV_QSV_DECODE_MASK 0x001 ++ AV_QSV_DECODE = 0x001, ++ ++#define AV_QSV_VPP_MASK 0x0F0 ++ // "Mandatory VPP filter" , might be with "Hint-based VPP filters" ++ AV_QSV_VPP_DEFAULT = 0x010, ++ // "User Modules" etc ++ AV_QSV_VPP_USER = 0x020, ++ ++#define av_QSV_ENCODE_MASK 0x100 ++ AV_QSV_ENCODE = 0x100 ++#define AV_QSV_ANY_MASK 0xFFF ++} AV_QSV_STAGE_TYPE; ++ ++ ++typedef struct av_qsv_list { ++ // practically pthread_mutex_t ++ void *mutex; ++#if HAVE_THREADS ++ pthread_mutexattr_t mta; ++#endif ++ ++ void **items; ++ int items_alloc; ++ ++ int items_count; ++} av_qsv_list; ++ ++typedef struct av_qsv_sync { ++ mfxSyncPoint* p_sync; ++ int in_use; ++} av_qsv_sync; ++ ++typedef struct av_qsv_stage { ++ AV_QSV_STAGE_TYPE type; ++ struct { ++ mfxBitstream *p_bs; ++ mfxFrameSurface1 *p_surface; ++ } in; ++ struct { ++ mfxBitstream *p_bs; ++ mfxFrameSurface1 *p_surface; ++ av_qsv_sync *sync; ++ } out; ++ av_qsv_list *pending; ++} av_qsv_stage; ++ ++typedef struct av_qsv_task { ++ mfxBitstream *bs; ++ av_qsv_stage *stage; ++} av_qsv_task; ++ ++ ++typedef struct av_qsv_space { ++ ++ uint8_t is_init_done; ++ ++ AV_QSV_STAGE_TYPE type; ++ ++ mfxVideoParam m_mfxVideoParam; ++ ++ mfxFrameAllocResponse response; ++ mfxFrameAllocRequest request[2]; // [0] - in, [1] - out, if needed ++ ++ mfxExtOpaqueSurfaceAlloc ext_opaque_alloc; ++ mfxExtBuffer **p_ext_params; ++ uint16_t p_ext_param_num; ++ ++ uint16_t surface_num_max_used; ++ uint16_t surface_num; ++ mfxFrameSurface1 *p_surfaces[AV_QSV_SURFACE_NUM]; ++ ++ uint16_t sync_num_max_used; ++ uint16_t sync_num; ++ av_qsv_sync *p_syncp[AV_QSV_SYNC_NUM]; ++ ++ mfxBitstream bs; ++ uint8_t *p_buf; ++ size_t p_buf_max_size; ++ ++ // only for encode and tasks ++ av_qsv_list *tasks; ++ ++ av_qsv_list *pending; ++ ++ // storage for allocations/mfxMemId* ++ mfxMemId *mids; ++} av_qsv_space; ++ ++typedef struct av_qsv_context { ++ volatile int is_context_active; ++ ++ mfxIMPL impl; ++ mfxSession mfx_session; ++ mfxVersion ver; ++ ++ // decode ++ av_qsv_space *dec_space; ++ // encode ++ av_qsv_space *enc_space; ++ // vpp ++ av_qsv_list *vpp_space; ++ ++ av_qsv_list *pipes; ++ ++ // MediaSDK starting from API version 1.6 includes DecodeTimeStamp ++ // in addition to TimeStamp ++ // see also AV_QSV_MSDK_VERSION_MINOR , AV_QSV_MSDK_VERSION_MAJOR ++ av_qsv_list *dts_seq; ++ ++ // practically pthread_mutex_t ++ void *qts_seq_mutex; ++ ++ int is_anex; ++ ++ void *qsv_config; ++ ++} av_qsv_context; ++ ++typedef enum { ++ QSV_PART_ANY = 0, ++ QSV_PART_LOWER, ++ QSV_PART_UPPER ++} av_qsv_split; ++ ++typedef struct { ++ int64_t dts; ++} av_qsv_dts; ++ ++typedef struct av_qsv_alloc_frame { ++ mfxU32 id; ++ mfxFrameInfo info; ++} av_qsv_alloc_frame; ++ ++typedef struct av_qsv_alloc_buffer { ++ mfxU32 id; ++ mfxU32 nbytes; ++ mfxU16 type; ++} av_qsv_alloc_buffer; ++ ++typedef struct av_qsv_allocators_space { ++ av_qsv_space *space; ++ mfxFrameAllocator frame_alloc; ++ mfxBufferAllocator buffer_alloc; ++} av_qsv_allocators_space; ++ ++typedef struct av_qsv_config { ++ /** ++ * Set asynch depth of processing with QSV ++ * Format: 0 and more ++ * ++ * - encoding: Set by user. ++ * - decoding: Set by user. ++ */ ++ int async_depth; ++ ++ /** ++ * Range of numbers that indicate trade-offs between quality and speed. ++ * Format: from 1/MFX_TARGETUSAGE_BEST_QUALITY to 7/MFX_TARGETUSAGE_BEST_SPEED inclusive ++ * ++ * - encoding: Set by user. ++ * - decoding: unused ++ */ ++ int target_usage; ++ ++ /** ++ * Number of reference frames; if NumRefFrame = 0, this parameter is not specified. ++ * Format: 0 and more ++ * ++ * - encoding: Set by user. ++ * - decoding: unused ++ */ ++ int num_ref_frame; ++ ++ /** ++ * Distance between I- or P- key frames; if it is zero, the GOP structure is unspecified. ++ * Note: If GopRefDist = 1, there are no B-frames used. ++ * ++ * - encoding: Set by user. ++ * - decoding: unused ++ */ ++ int gop_ref_dist; ++ ++ /** ++ * Number of pictures within the current GOP (Group of Pictures); if GopPicSize=0, ++ * then the GOP size is unspecified. If GopPicSize=1, only I-frames are used. ++ * ++ * - encoding: Set by user. ++ * - decoding: unused ++ */ ++ int gop_pic_size; ++ ++ /** ++ * Set type of surfaces used with QSV ++ * Format: "IOPattern enum" of Media SDK ++ * ++ * - encoding: Set by user. ++ * - decoding: Set by user. ++ */ ++ int io_pattern; ++ ++ /** ++ * Set amount of additional surfaces might be needed ++ * Format: ammount of additional buffers(surfaces+syncs) ++ * to allocate in advance ++ * ++ * - encoding: Set by user. ++ * - decoding: Set by user. ++ */ ++ int additional_buffers; ++ ++ /** ++ * If pipeline should be sync. ++ * Format: wait time in milliseconds, ++ * AV_QSV_SYNC_TIME_DEFAULT/10000 might be a good value ++ * ++ * - encoding: Set by user. ++ * - decoding: Set by user. ++ */ ++ int sync_need; ++ ++ /** ++ * Type of implementation needed ++ * ++ * - encoding: Set by user. ++ * - decoding: Set by user. ++ */ ++ int impl_requested; ++ ++ /** ++ * if QSV usage is multithreaded. ++ * Format: Yes/No, 1/0 ++ * ++ * - encoding: Set by user. ++ * - decoding: Set by user. ++ */ ++ int usage_threaded; ++ ++ /** ++ * if QSV use an external allocation (valid per session/mfxSession) ++ * Format: pointer to allocators, if default: 0 ++ * ++ * note that: ++ * System Memory: can be used without provided and external allocator, ++ * meaning MediaSDK will use an internal one ++ * Video Memory: in this case - we must provide an external allocator ++ * Also, Media SDK session doesn't require external allocator if the application ++ * uses opaque memory ++ * ++ * Calls SetFrameAllocator/SetBufferAllocator ++ * (MFXVideoCORE_SetFrameAllocator/MFXVideoCORE_SetBufferAllocator) ++ * are to pass allocators to Media SDK ++ * ++ * - encoding: Set by user. ++ * - decoding: Set by user. ++ */ ++ av_qsv_allocators_space *allocators; ++ ++} av_qsv_config; ++ ++#define ANEX_UNKNOWN 0 ++#define ANEX_PREFIX 1 ++#define ANEX_NO_PREFIX 2 ++ ++static const uint8_t ff_prefix_code[] = { 0x00, 0x00, 0x00, 0x01 }; ++ ++int av_qsv_get_free_sync(av_qsv_space *, av_qsv_context *); ++int av_qsv_get_free_surface(av_qsv_space *, av_qsv_context *, mfxFrameInfo *, ++ av_qsv_split); ++int av_qsv_get_free_encode_task(av_qsv_list *); ++ ++int av_is_qsv_available(mfxIMPL, mfxVersion *); ++void av_qsv_wait_on_sync(av_qsv_context *, av_qsv_stage *); ++ ++void av_qsv_add_context_usage(av_qsv_context *, int); ++ ++void av_qsv_pipe_list_create(av_qsv_list **, int); ++void av_qsv_pipe_list_clean(av_qsv_list **); ++ ++void av_qsv_add_stagee(av_qsv_list **, av_qsv_stage *, int); ++av_qsv_stage *av_qsv_get_last_stage(av_qsv_list *); ++av_qsv_list *av_qsv_pipe_by_stage(av_qsv_list *, av_qsv_stage *); ++void av_qsv_flush_stages(av_qsv_list *, av_qsv_list **); ++ ++void av_qsv_dts_ordered_insert(av_qsv_context *, int, int, int64_t, int); ++void av_qsv_dts_pop(av_qsv_context *); ++ ++av_qsv_stage *av_qsv_stage_init(void); ++void av_qsv_stage_clean(av_qsv_stage **); ++int av_qsv_context_clean(av_qsv_context *); ++ ++int ff_qsv_is_sync_in_pipe(mfxSyncPoint *, av_qsv_context *); ++int ff_qsv_is_surface_in_pipe(mfxFrameSurface1 *, av_qsv_context *); ++ ++av_qsv_list *av_qsv_list_init(int); ++int av_qsv_list_lock(av_qsv_list *); ++int av_qsv_list_unlock(av_qsv_list *); ++int av_qsv_list_add(av_qsv_list *, void *); ++void av_qsv_list_rem(av_qsv_list *, void *); ++void av_qsv_list_insert(av_qsv_list *, int, void *); ++void av_qsv_list_close(av_qsv_list **); ++ ++int av_qsv_list_count(av_qsv_list *); ++void *av_qsv_list_item(av_qsv_list *, int); ++ ++/* @} */ ++ ++#endif //AVCODEC_QSV_H +diff -Naur ../../libav-v9.6/libavcodec/qsv_h264.c ./libavcodec/qsv_h264.c +--- ../../libav-v9.6/libavcodec/qsv_h264.c 1970-01-01 01:00:00.000000000 +0100 ++++ ./libavcodec/qsv_h264.c 2013-08-19 21:32:01.705244194 +0200 +@@ -0,0 +1,974 @@ ++/* ********************************************************************* *\ ++ ++Copyright (C) 2013 Intel Corporation. All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions are met: ++- Redistributions of source code must retain the above copyright notice, ++this list of conditions and the following disclaimer. ++- Redistributions in binary form must reproduce the above copyright notice, ++this list of conditions and the following disclaimer in the documentation ++and/or other materials provided with the distribution. ++- Neither the name of Intel Corporation nor the names of its contributors ++may be used to endorse or promote products derived from this software ++without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR ++IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, ++INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++\* ********************************************************************* */ ++ ++#include "h264.h" ++#include "h264data.h" ++#include "qsv_h264.h" ++ ++static av_qsv_config av_qsv_default_config = { ++ .async_depth = AV_QSV_ASYNC_DEPTH_DEFAULT, ++ .target_usage = MFX_TARGETUSAGE_BALANCED, ++ .num_ref_frame = 0, ++ .gop_ref_dist = 0, ++ .gop_pic_size = 0, ++ .io_pattern = MFX_IOPATTERN_OUT_OPAQUE_MEMORY, ++ .additional_buffers = 0, ++ .sync_need = 0, ++ .impl_requested = MFX_IMPL_HARDWARE, ++ .usage_threaded = 0, ++ .allocators = 0, ++}; ++ ++static av_qsv_allocators_space av_qsv_default_system_allocators = { ++ // fill to access mids ++ .space = 0, ++ ++ .frame_alloc = { ++ .pthis = &av_qsv_default_system_allocators, ++ .Alloc = ff_qsv_mem_frame_alloc, ++ .Lock = ff_qsv_mem_frame_lock, ++ .Unlock = ff_qsv_mem_frame_unlock, ++ .GetHDL = ff_qsv_mem_frame_getHDL, ++ .Free = ff_qsv_mem_frame_free, ++ }, ++ .buffer_alloc = { ++ .pthis = &av_qsv_default_system_allocators, ++ .Alloc = ff_qsv_mem_buffer_alloc, ++ .Lock = ff_qsv_mem_buffer_lock, ++ .Unlock = ff_qsv_mem_buffer_unlock, ++ .Free = ff_qsv_mem_buffer_free, ++ }, ++}; ++ ++static const uint8_t ff_slice_code[] = { 0x00, 0x00, 0x01, 0x65 }; ++ ++int ff_qsv_nal_find_start_code(uint8_t * pb, size_t size) ++{ ++ if ((int) size < 4) ++ return 0; ++ ++ while ((4 <= size) && ((0 != pb[0]) || (0 != pb[1]) || (0 != pb[2]) || (1 != pb[3]))) { ++ pb += 1; ++ size -= 1; ++ } ++ ++ if (4 <= size) ++ return 1; ++ ++ return 0; ++} ++ ++int ff_qsv_dec_init_clean(AVCodecContext *avctx) ++{ ++ mfxStatus sts = MFX_ERR_NONE; ++ av_qsv_context *qsv = avctx->priv_data; ++ av_qsv_space *qsv_decode = qsv->dec_space; ++ av_qsv_context_clean(qsv); ++ av_freep(&avctx->priv_data); ++} ++int ff_qsv_dec_init(AVCodecContext * avctx) ++{ ++ int ret = 0; ++ mfxStatus sts = MFX_ERR_NONE; ++ size_t current_offset = 6; ++ int header_size = 0; ++ unsigned char *current_position; ++ size_t current_size; ++ ++ av_qsv_context *qsv = avctx->priv_data; ++ av_qsv_space *qsv_decode = qsv->dec_space; ++ av_qsv_config *qsv_config_context = avctx->hwaccel_context; ++ ++ qsv->impl = qsv_config_context->impl_requested; ++ ++ memset(&qsv->mfx_session, 0, sizeof(mfxSession)); ++ qsv->ver.Major = AV_QSV_MSDK_VERSION_MAJOR; ++ qsv->ver.Minor = AV_QSV_MSDK_VERSION_MINOR; ++ ++ sts = MFXInit(qsv->impl, &qsv->ver, &qsv->mfx_session); ++ AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); ++ ++ AV_QSV_ZERO_MEMORY(qsv_decode->m_mfxVideoParam); ++ AV_QSV_ZERO_MEMORY(qsv_decode->m_mfxVideoParam.mfx); ++ qsv_decode->m_mfxVideoParam.mfx.CodecId = MFX_CODEC_AVC; ++ qsv_decode->m_mfxVideoParam.IOPattern = ++ qsv_config_context->io_pattern; ++ ++ qsv_decode->m_mfxVideoParam.AsyncDepth = ++ qsv_config_context->async_depth; ++ ++ AV_QSV_ZERO_MEMORY(qsv_decode->bs); ++ { ++ current_position = avctx->extradata; ++ current_size = avctx->extradata_size; ++ ++ if (!ff_qsv_nal_find_start_code(current_position, current_size)) { ++ ++ while (current_offset <= current_size) { ++ int current_nal_size = ++ (unsigned char) current_position[current_offset] << 8 | ++ (unsigned char) current_position[current_offset + 1]; ++ unsigned char nal_type = ++ (unsigned char) current_position[current_offset + 2] & 0x1F; ++ ++ if (nal_type == NAL_SPS || nal_type == NAL_PPS) { ++ memcpy(&qsv_decode->p_buf[header_size], ff_prefix_code, ++ sizeof(ff_prefix_code)); ++ header_size += sizeof(ff_prefix_code); ++ memcpy(&qsv_decode->p_buf[header_size], ++ ¤t_position[current_offset + 2], ++ current_nal_size); ++ ++ // fix for PPS as it comes after SPS, so - last ++ if (nal_type == NAL_PPS) { ++ // fix of MFXVideoDECODE_DecodeHeader: needs one SLICE to find, any SLICE ++ memcpy(&qsv_decode->p_buf ++ [header_size + current_nal_size], ++ ff_slice_code, current_nal_size); ++ header_size += sizeof(ff_slice_code); ++ } ++ } ++ ++ header_size += current_nal_size; ++ current_offset += current_nal_size + 3; ++ } ++ } else { ++ memcpy(&qsv_decode->p_buf[0], avctx->extradata, ++ avctx->extradata_size); ++ header_size = avctx->extradata_size; ++ memcpy(&qsv_decode->p_buf ++ [header_size], ff_slice_code, sizeof(ff_slice_code)); ++ header_size += sizeof(ff_slice_code); ++ } ++ } ++ ++ qsv_decode->bs.Data = qsv_decode->p_buf; ++ qsv_decode->bs.DataLength = header_size; ++ qsv_decode->bs.MaxLength = qsv_decode->p_buf_max_size; ++ ++ if (qsv_decode->bs.DataLength > qsv_decode->bs.MaxLength) { ++ av_log(avctx, AV_LOG_FATAL, "DataLength > MaxLength\n"); ++ return -1; ++ } ++ ++ sts = MFXVideoDECODE_DecodeHeader(qsv->mfx_session, &qsv_decode->bs, ++ &qsv_decode->m_mfxVideoParam); ++ ++ AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); ++ ++ qsv_decode->bs.DataLength -= sizeof(ff_slice_code); ++ ++ memset(&qsv_decode->request, 0, sizeof(mfxFrameAllocRequest) * 2); ++ sts = MFXVideoDECODE_QueryIOSurf(qsv->mfx_session, ++ &qsv_decode->m_mfxVideoParam, ++ &qsv_decode->request); ++ ++ AV_QSV_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION); ++ AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); ++ ++ qsv_decode->surface_num = ++ FFMIN(qsv_decode->request[0].NumFrameSuggested + ++ qsv_config_context->async_depth + ++ qsv_config_context->additional_buffers, AV_QSV_SURFACE_NUM); ++ ++ if (qsv_decode->surface_num <= 0) ++ qsv_decode->surface_num = AV_QSV_SURFACE_NUM; ++ ++ if (qsv_decode->m_mfxVideoParam.IOPattern == ++ MFX_IOPATTERN_OUT_SYSTEM_MEMORY) { ++ ++ // as per non-opaque memory: ++ if (!qsv_config_context->allocators) { ++ av_log(avctx, AV_LOG_INFO, ++ "Using default allocators for QSV decode\n"); ++ ((av_qsv_config *) avctx->hwaccel_context)->allocators = ++ &av_qsv_default_system_allocators; ++ } ++ ++ qsv_config_context->allocators->space = qsv_decode; ++ ++ qsv_decode->request[0].NumFrameMin = qsv_decode->surface_num; ++ qsv_decode->request[0].NumFrameSuggested = qsv_decode->surface_num; ++ ++ qsv_decode->request[0].Type = MFX_MEMTYPE_EXTERNAL_FRAME | MFX_MEMTYPE_FROM_DECODE; ++ // qsv_decode->request[0].Type |= m_bd3dAlloc ? MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET : MFX_MEMTYPE_SYSTEM_MEMORY; ++ qsv_decode->request[0].Type |= MFX_MEMTYPE_SYSTEM_MEMORY; ++ ++ qsv_config_context->allocators-> ++ frame_alloc.Alloc(qsv_config_context->allocators, ++ &qsv_decode->request[0], ++ &qsv_decode->response); ++ } ++ ++ for (int i = 0; i < qsv_decode->surface_num; i++) { ++ qsv_decode->p_surfaces[i] = av_mallocz(sizeof(mfxFrameSurface1)); ++ AV_QSV_CHECK_POINTER(qsv_decode->p_surfaces[i], ++ AVERROR(ENOMEM)); ++ memcpy(&(qsv_decode->p_surfaces[i]->Info), ++ &(qsv_decode->request[0].Info), sizeof(mfxFrameInfo)); ++ ++ // for an external(like DX9/11) based allocation: ++ // we bind: ++ // m_pmfxSurfaces[i].Data.MemId = m_mfxResponse.mids[i]; ++ // else, System memory: ++ if (qsv_decode->m_mfxVideoParam.IOPattern == ++ MFX_IOPATTERN_OUT_SYSTEM_MEMORY) { ++ sts = ++ qsv_config_context->allocators-> ++ frame_alloc.Lock(qsv_config_context->allocators, ++ qsv_decode->response.mids[i], ++ &(qsv_decode->p_surfaces[i]->Data)); ++ AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); ++ } ++ } ++ ++ qsv_decode->sync_num = FFMIN(qsv_decode->surface_num, AV_QSV_SYNC_NUM); ++ for (int i = 0; i < qsv_decode->sync_num; i++) { ++ qsv_decode->p_syncp[i] = av_mallocz(sizeof(av_qsv_sync)); ++ AV_QSV_CHECK_POINTER(qsv_decode->p_syncp[i], AVERROR(ENOMEM)); ++ qsv_decode->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint)); ++ AV_QSV_CHECK_POINTER(qsv_decode->p_syncp[i]->p_sync, AVERROR(ENOMEM)); ++ } ++ ++ memset(&qsv_decode->ext_opaque_alloc, 0, ++ sizeof(mfxExtOpaqueSurfaceAlloc)); ++ ++ if (qsv_decode->m_mfxVideoParam.IOPattern == ++ MFX_IOPATTERN_OUT_OPAQUE_MEMORY) { ++ qsv_decode->m_mfxVideoParam.NumExtParam = qsv_decode->p_ext_param_num = 1; ++ ++ qsv_decode->p_ext_params = av_mallocz(sizeof(mfxExtBuffer *)*qsv_decode->p_ext_param_num); ++ AV_QSV_CHECK_POINTER(qsv_decode->p_ext_params, AVERROR(ENOMEM)); ++ ++ qsv_decode->m_mfxVideoParam.ExtParam = qsv_decode->p_ext_params; ++ ++ qsv_decode->ext_opaque_alloc.Out.Surfaces = qsv_decode->p_surfaces; ++ qsv_decode->ext_opaque_alloc.Out.NumSurface = qsv_decode->surface_num; ++ qsv_decode->ext_opaque_alloc.Out.Type = qsv_decode->request[0].Type; ++ ++ qsv_decode->ext_opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; ++ qsv_decode->ext_opaque_alloc.Header.BufferSz = sizeof(mfxExtOpaqueSurfaceAlloc); ++ qsv_decode->p_ext_params[0] = (mfxExtBuffer *) &qsv_decode->ext_opaque_alloc; ++ } ++ ++ sts = ++ MFXVideoDECODE_Init(qsv->mfx_session, ++ &qsv_decode->m_mfxVideoParam); ++ ++ AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); ++ ++ qsv_decode->is_init_done = 1; ++ return ret; ++} ++ ++av_cold int ff_qsv_decode_init(AVCodecContext * avctx) ++{ ++ av_qsv_context *qsv; ++ av_qsv_space *qsv_decode; ++ av_qsv_config **qsv_config_context = ++ (av_qsv_config **) & avctx->hwaccel_context; ++ ++ qsv = avctx->priv_data; ++ ++ if (qsv && qsv->dec_space && qsv->dec_space->is_init_done || !avctx->extradata_size) ++ return 0; ++ ++ if(!qsv) ++ qsv = av_mallocz(sizeof(av_qsv_context)); ++ if (!qsv) ++ return AVERROR(ENOMEM); ++ ++ if(!qsv_decode) ++ qsv_decode = av_mallocz(sizeof(av_qsv_space)); ++ if (!qsv_decode){ ++ free(qsv); ++ return AVERROR(ENOMEM); ++ } ++ avctx->priv_data = qsv; ++ qsv->dec_space = qsv_decode; ++ ++ qsv_decode->p_buf_max_size = AV_QSV_BUF_SIZE_DEFAULT; ++ if(!qsv_decode->p_buf) ++ qsv_decode->p_buf = av_malloc(qsv_decode->p_buf_max_size * sizeof(uint8_t)); ++ if (!qsv_decode->p_buf) ++ return AVERROR(ENOMEM); ++ ++ if (!(*qsv_config_context)) { ++ av_log(avctx, AV_LOG_INFO, ++ "Using default config for QSV decode\n"); ++ avctx->hwaccel_context = &av_qsv_default_config; ++ } else { ++ if ((*qsv_config_context)->io_pattern != ++ MFX_IOPATTERN_OUT_OPAQUE_MEMORY ++ && (*qsv_config_context)->io_pattern != ++ MFX_IOPATTERN_OUT_SYSTEM_MEMORY) { ++ av_log_missing_feature( avctx,"Only MFX_IOPATTERN_OUT_OPAQUE_MEMORY and MFX_IOPATTERN_OUT_SYSTEM_MEMORY are currently supported\n",0); ++ return AVERROR_PATCHWELCOME; ++ } ++ } ++ ++ qsv->qsv_config = avctx->hwaccel_context; ++ ++ av_qsv_add_context_usage(qsv, ++ HAVE_THREADS ++ ? (*qsv_config_context)->usage_threaded : ++ HAVE_THREADS); ++ ++ // allocation of p_syncp and p_surfaces inside of ff_qsv_dec_init ++ return ff_qsv_dec_init(avctx); ++} ++ ++static av_cold int qsv_decode_end(AVCodecContext * avctx) ++{ ++ mfxStatus sts = MFX_ERR_NONE; ++ av_qsv_context *qsv = avctx->priv_data; ++ av_qsv_config *qsv_config_context = avctx->hwaccel_context; ++ ++ if (qsv) { ++ av_qsv_space *qsv_decode = qsv->dec_space; ++ if (qsv_decode && qsv_decode->is_init_done) { ++ // todo: change to AV_LOG_INFO ++ av_log(avctx, AV_LOG_QUIET, ++ "qsv_decode report done, max_surfaces: %u/%u , max_syncs: %u/%u\n", ++ qsv_decode->surface_num_max_used, ++ qsv_decode->surface_num, qsv_decode->sync_num_max_used, ++ qsv_decode->sync_num); ++ } ++ ++ if (qsv_config_context ++ && qsv_config_context->io_pattern == ++ MFX_IOPATTERN_OUT_SYSTEM_MEMORY) { ++ if (qsv_config_context->allocators) { ++ sts = ++ qsv_config_context->allocators-> ++ frame_alloc.Free(qsv_config_context->allocators, ++ &qsv_decode->response); ++ AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); ++ } else { ++ av_log(avctx, AV_LOG_FATAL, ++ "No QSV allocators found for clean up\n"); ++ } ++ } ++ // closing the own resources ++ av_freep(&qsv_decode->p_buf); ++ ++ for (int i = 0; i < qsv_decode->surface_num; i++) { ++ av_freep(&qsv_decode->p_surfaces[i]); ++ } ++ qsv_decode->surface_num = 0; ++ ++ if( qsv_decode->p_ext_param_num || qsv_decode->p_ext_params ) ++ av_freep(&qsv_decode->p_ext_params); ++ qsv_decode->p_ext_param_num = 0; ++ ++ for (int i = 0; i < qsv_decode->sync_num; i++) { ++ av_freep(&qsv_decode->p_syncp[i]->p_sync); ++ av_freep(&qsv_decode->p_syncp[i]); ++ } ++ qsv_decode->sync_num = 0; ++ qsv_decode->is_init_done = 0; ++ ++ av_freep(&qsv->dec_space); ++ ++ // closing commong stuff ++ av_qsv_context_clean(qsv); ++ } ++ ++ return 0; ++} ++ ++static int qsv_decode_frame(AVCodecContext * avctx, void *data, ++ int *data_size, AVPacket * avpkt) ++{ ++ mfxStatus sts = MFX_ERR_NONE; ++ av_qsv_context *qsv = avctx->priv_data; ++ av_qsv_space *qsv_decode; ++ av_qsv_config *qsv_config_context = avctx->hwaccel_context; ++ int *got_picture_ptr = data_size; ++ int ret_value = 1; ++ uint8_t *current_position = avpkt->data; ++ int current_size = avpkt->size; ++ int frame_processed = 0; ++ size_t frame_length = 0; ++ int surface_idx = 0; ++ int extra_data_workaround = 0; ++ ++ int sync_idx = 0; ++ int current_nal_size; ++ unsigned char nal_type; ++ av_qsv_stage *new_stage = 0; ++ mfxBitstream *input_bs = NULL; ++ size_t current_offset = 2; ++ av_qsv_list *qsv_atom = 0; ++ av_qsv_list *pipe = 0; ++ ++ AVFrame *picture = (AVFrame *) data; ++ ++ *got_picture_ptr = 0; ++ ++ qsv = avctx->priv_data; ++ if(!qsv){ ++ extra_data_workaround = !avctx->extradata_size; ++ if(extra_data_workaround){ ++ avctx->extradata = avpkt->data; ++ avctx->extradata_size = avpkt->size; ++ } ++ sts = ff_qsv_decode_init(avctx); ++ qsv = avctx->priv_data; ++ if(extra_data_workaround){ ++ avctx->extradata = 0; ++ avctx->extradata_size = 0; ++ } ++ if(sts<0){ ++ ff_qsv_dec_init_clean(avctx); ++ *got_picture_ptr = 0; ++ return sts; ++ } ++ } ++ qsv_decode = qsv->dec_space; ++ ++ if (qsv_decode->bs.DataOffset + qsv_decode->bs.DataLength + ++ current_size > qsv_decode->bs.MaxLength) { ++ memmove(&qsv_decode->bs.Data[0], ++ qsv_decode->bs.Data + qsv_decode->bs.DataOffset, ++ qsv_decode->bs.DataLength); ++ qsv_decode->bs.DataOffset = 0; ++ } ++ ++ if (current_size) { ++ if(qsv->is_anex == ANEX_UNKNOWN){ ++ if (ff_qsv_nal_find_start_code(current_position, current_size) && current_position == avpkt->data) ++ qsv->is_anex = ANEX_PREFIX; ++ else ++ qsv->is_anex = ANEX_NO_PREFIX; ++ } ++ if (qsv->is_anex == ANEX_PREFIX){ ++ memcpy(&qsv_decode->bs.Data[0] + ++ qsv_decode->bs.DataLength + ++ qsv_decode->bs.DataOffset, ++ avpkt->data, ++ avpkt->size); ++ qsv_decode->bs.DataLength += avpkt->size; ++ frame_length += avpkt->size; ++ } ++ else ++ while (current_offset <= current_size) { ++ current_nal_size = ++ ((unsigned char) current_position[current_offset - 2] << 24 | ++ (unsigned char) current_position[current_offset - 1] << 16 | ++ (unsigned char) current_position[current_offset] << 8 | ++ (unsigned char) current_position[current_offset + 1]) - 1; ++ nal_type = ++ (unsigned char) current_position[current_offset + 2] & 0x1F; ++ { ++ frame_length += current_nal_size; ++ memcpy(&qsv_decode->bs.Data[0] + ++ qsv_decode->bs.DataLength + ++ qsv_decode->bs.DataOffset, ff_prefix_code, ++ sizeof(ff_prefix_code)); ++ qsv_decode->bs.DataLength += sizeof(ff_prefix_code); ++ memcpy(&qsv_decode->bs.Data[0] + ++ qsv_decode->bs.DataLength + ++ qsv_decode->bs.DataOffset, ++ ¤t_position[current_offset + 2], ++ current_nal_size + 1); ++ qsv_decode->bs.DataLength += current_nal_size + 1; ++ } ++ current_offset += current_nal_size + 5; ++ } ++ ++ if (qsv_decode->bs.DataLength > qsv_decode->bs.MaxLength) { ++ av_log(avctx, AV_LOG_FATAL, "DataLength > MaxLength\n"); ++ return -1; ++ } ++ } ++ ++ if (frame_length || current_size == 0) { ++ ++ qsv_decode->bs.TimeStamp = avpkt->pts; ++ ++ //not a drain ++ if ((current_size || qsv_decode->bs.DataLength)) ++ av_qsv_dts_ordered_insert(qsv, 0, 0, qsv_decode->bs.TimeStamp, 0); ++ ++ sts = MFX_ERR_NONE; ++ // ignore warnings, where warnings >0 , and not error codes <0 ++ while (MFX_ERR_NONE <= sts || MFX_ERR_MORE_SURFACE == sts ++ || MFX_WRN_DEVICE_BUSY == sts) { ++ ++ if (MFX_ERR_MORE_SURFACE == sts || MFX_ERR_NONE == sts) { ++ surface_idx = ++ av_qsv_get_free_surface(qsv_decode, qsv, ++ &qsv_decode->request[0].Info, ++ QSV_PART_ANY); ++ ++ if (surface_idx == -1) { ++ *got_picture_ptr = 0; ++ return 0; ++ } ++ } ++ ++ if (MFX_WRN_DEVICE_BUSY == sts) ++ av_qsv_sleep(10); ++ ++ sync_idx = av_qsv_get_free_sync(qsv_decode, qsv); ++ ++ if (sync_idx == -1) { ++ *got_picture_ptr = 0; ++ return 0; ++ } ++ new_stage = av_qsv_stage_init(); ++ input_bs = NULL; ++ // if to drain last ones ++ if (current_size || qsv_decode->bs.DataLength) ++ input_bs = &qsv_decode->bs; ++ // Decode a frame asynchronously (returns immediately) ++ // very first IDR / SLICE should be with SPS/PPS ++ sts = MFXVideoDECODE_DecodeFrameAsync(qsv->mfx_session, input_bs, ++ qsv_decode->p_surfaces ++ [surface_idx], ++ &new_stage->out.p_surface, ++ qsv_decode->p_syncp[sync_idx]->p_sync); ++ ++ new_stage->out.sync = qsv_decode->p_syncp[sync_idx]; ++ // have some results ++ if (MFX_ERR_NONE <= sts && MFX_WRN_DEVICE_BUSY != sts && ++ MFX_WRN_VIDEO_PARAM_CHANGED != sts) { ++ ++ ff_qsv_atomic_inc(&(new_stage->out.p_surface->Data.Locked)); ++ ++ new_stage->type = AV_QSV_DECODE; ++ new_stage->in.p_bs = input_bs; ++ new_stage->in.p_surface = qsv_decode->p_surfaces[surface_idx]; ++ ++ pipe = av_qsv_list_init(HAVE_THREADS ? qsv_config_context->usage_threaded : HAVE_THREADS); ++ av_qsv_add_stagee(&pipe, new_stage, ++ HAVE_THREADS ? ++ qsv_config_context->usage_threaded : ++ HAVE_THREADS); ++ ++ av_qsv_list_add(qsv->pipes, pipe); ++ qsv_atom = pipe; ++ ++ // usage for forced decode sync and results, can be avoided if sync done by next stage ++ // also note wait time for Sync and possible usage with MFX_WRN_IN_EXECUTION check ++ if (qsv_config_context->sync_need) { ++ sts = ++ MFXVideoCORE_SyncOperation(qsv->mfx_session, ++ qsv_decode->p_syncp[sync_idx]->p_sync, ++ qsv_config_context->sync_need); ++ AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); ++ ++ // no need to wait more -> force off ++ ff_qsv_atomic_dec(&qsv_decode->p_syncp[sync_idx]->in_use); ++ new_stage->out.sync = 0; ++ } ++ ++ sts = MFX_ERR_NONE; ++ break; ++ } ++ av_qsv_stage_clean(&new_stage); ++ ++ /* ++ Can be because of: ++ - runtime situation: ++ - drain procedure: ++ At the end of the bitstream, the application continuously calls the MFXVideoDECODE_DecodeFrameAsync function with a ++ NULL bitstream pointer to drain any remaining frames cached within the Intel ++ Media SDK decoder, until the function returns MFX_ERR_MORE_DATA. ++ */ ++ if (MFX_ERR_MORE_DATA == sts) { ++ // not a drain ++ if (current_size) { ++ *got_picture_ptr = 0; ++ return avpkt->size; ++ } ++ // drain ++ break; ++ } ++ if (MFX_ERR_MORE_SURFACE == sts ){ ++ continue; ++ } ++ ++ AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); ++ } ++ ++ frame_processed = 1; ++ } ++ ++ if (frame_processed) { ++ ++ if (current_size) { ++ *got_picture_ptr = 1; ++ ret_value = avpkt->size; ++ } else { ++ if (MFX_ERR_MORE_DATA != sts) { ++ *got_picture_ptr = 1; ++ ret_value = avpkt->size; ++ } else { ++ *got_picture_ptr = 0; ++ return 0; ++ } ++ } ++ ++ picture->pkt_pts = new_stage->out.p_surface->Data.TimeStamp; ++ picture->pts = new_stage->out.p_surface->Data.TimeStamp; ++ ++ picture->repeat_pict = (qsv_decode->m_mfxVideoParam.mfx.FrameInfo.PicStruct & MFX_PICSTRUCT_FIELD_REPEATED); ++ picture->interlaced_frame = !(qsv_decode->m_mfxVideoParam.mfx.FrameInfo.PicStruct & MFX_PICSTRUCT_PROGRESSIVE); ++ picture->top_field_first = (qsv_decode->m_mfxVideoParam.mfx.FrameInfo.PicStruct & MFX_PICSTRUCT_FIELD_TFF); ++ ++ // since we do not know it yet from MSDK, let's do just a simple way for now ++ picture->key_frame = (avctx->frame_number == 0) ? 1 : 0; ++ ++ if (qsv_decode->m_mfxVideoParam.IOPattern == MFX_IOPATTERN_OUT_SYSTEM_MEMORY) { ++ picture->data[0] = new_stage->out.p_surface->Data.Y; ++ picture->data[1] = new_stage->out.p_surface->Data.VU; ++ picture->linesize[0] = new_stage->out.p_surface->Info.Width; ++ picture->linesize[1] = new_stage->out.p_surface->Info.Width; ++ } else { ++ picture->data[0] = 0; ++ picture->data[1] = 0; ++ picture->linesize[0] = 0; ++ picture->linesize[1] = 0; ++ } ++ ++ picture->data[2] = qsv_atom; ++ picture->linesize[2] = 0; ++ } ++ ++ return ret_value; ++} ++ ++// Will be called when seeking ++static void qsv_flush_dpb(AVCodecContext * avctx) ++{ ++ av_qsv_context *qsv = avctx->priv_data; ++ av_qsv_space *qsv_decode = qsv->dec_space; ++ ++ qsv_decode->bs.DataOffset = 0; ++ qsv_decode->bs.DataLength = 0; ++ qsv_decode->bs.MaxLength = qsv_decode->p_buf_max_size; ++} ++ ++ ++mfxStatus ff_qsv_mem_frame_alloc(mfxHDL pthis, ++ mfxFrameAllocRequest * request, ++ mfxFrameAllocResponse * response) ++{ ++ mfxStatus sts = MFX_ERR_NONE; ++ ++ mfxU32 numAllocated = 0; ++ ++ mfxU32 width = AV_QSV_ALIGN32(request->Info.Width); ++ mfxU32 height = AV_QSV_ALIGN32(request->Info.Height); ++ mfxU32 nbytes; ++ ++ av_qsv_allocators_space *this_alloc = (av_qsv_allocators_space *) pthis; ++ av_qsv_alloc_frame *fs; ++ ++ if (!this_alloc->space) ++ return MFX_ERR_NOT_INITIALIZED; ++ ++ switch (request->Info.FourCC) { ++ case MFX_FOURCC_YV12: ++ case MFX_FOURCC_NV12: ++ nbytes = ++ width * height + (width >> 1) * (height >> 1) + ++ (width >> 1) * (height >> 1); ++ break; ++ case MFX_FOURCC_RGB3: ++ nbytes = width * height + width * height + width * height; ++ break; ++ case MFX_FOURCC_RGB4: ++ nbytes = ++ width * height + width * height + width * height + ++ width * height; ++ break; ++ case MFX_FOURCC_YUY2: ++ nbytes = ++ width * height + (width >> 1) * (height) + ++ (width >> 1) * (height); ++ break; ++ default: ++ return MFX_ERR_UNSUPPORTED; ++ } ++ ++ this_alloc->space->mids = ++ av_malloc(sizeof(mfxMemId) * request->NumFrameSuggested); ++ if (!this_alloc->space->mids) ++ return MFX_ERR_MEMORY_ALLOC; ++ ++ // allocate frames ++ for (numAllocated = 0; numAllocated < request->NumFrameSuggested; ++ numAllocated++) { ++ sts = ++ this_alloc->buffer_alloc.Alloc(this_alloc->buffer_alloc.pthis, ++ nbytes + ++ AV_QSV_ALIGN32(sizeof ++ (av_qsv_alloc_frame)), ++ request->Type, ++ &(this_alloc-> ++ space->mids[numAllocated])); ++ ++ if (MFX_ERR_NONE != sts) ++ break; ++ ++ sts = ++ this_alloc->buffer_alloc.Lock(this_alloc->buffer_alloc.pthis, ++ this_alloc-> ++ space->mids[numAllocated], ++ (mfxU8 **) & fs); ++ ++ if (MFX_ERR_NONE != sts) ++ break; ++ ++ fs->id = AV_QSV_ID_FRAME; ++ fs->info = request->Info; ++ this_alloc->buffer_alloc.Unlock(this_alloc->buffer_alloc.pthis, ++ this_alloc-> ++ space->mids[numAllocated]); ++ } ++ ++ // check the number of allocated frames ++ if (numAllocated < request->NumFrameMin) ++ return MFX_ERR_MEMORY_ALLOC; ++ ++ response->NumFrameActual = (mfxU16) numAllocated; ++ response->mids = this_alloc->space->mids; ++ ++ return MFX_ERR_NONE; ++} ++ ++mfxStatus ff_qsv_mem_frame_lock(mfxHDL pthis, mfxMemId mid, ++ mfxFrameData * ptr) ++{ ++ mfxStatus sts = MFX_ERR_NONE; ++ av_qsv_alloc_frame *fs = 0; ++ mfxU16 width; ++ mfxU16 height; ++ ++ av_qsv_allocators_space *this_alloc = (av_qsv_allocators_space *) pthis; ++ ++ if (!this_alloc->space) ++ return MFX_ERR_NOT_INITIALIZED; ++ if (!ptr) ++ return MFX_ERR_NULL_PTR; ++ ++ ++ sts = ++ this_alloc->buffer_alloc.Lock(this_alloc->buffer_alloc.pthis, mid, ++ (mfxU8 **) & fs); ++ ++ if (MFX_ERR_NONE != sts) ++ return sts; ++ ++ if (AV_QSV_ID_FRAME != fs->id) { ++ this_alloc->buffer_alloc.Unlock(this_alloc->buffer_alloc.pthis, ++ mid); ++ return MFX_ERR_INVALID_HANDLE; ++ } ++ ++ width = (mfxU16) AV_QSV_ALIGN32(fs->info.Width); ++ height = (mfxU16) AV_QSV_ALIGN32(fs->info.Height); ++ ptr->B = ptr->Y = ++ (mfxU8 *) fs + AV_QSV_ALIGN32(sizeof(av_qsv_allocators_space)); ++ ++ switch (fs->info.FourCC) { ++ case MFX_FOURCC_NV12: ++ ptr->U = ptr->Y + width * height; ++ ptr->V = ptr->U + 1; ++ ptr->Pitch = width; ++ break; ++ case MFX_FOURCC_YV12: ++ ptr->V = ptr->Y + width * height; ++ ptr->U = ptr->V + (width >> 1) * (height >> 1); ++ ptr->Pitch = width; ++ break; ++ case MFX_FOURCC_YUY2: ++ ptr->U = ptr->Y + 1; ++ ptr->V = ptr->Y + 3; ++ ptr->Pitch = 2 * width; ++ break; ++ case MFX_FOURCC_RGB3: ++ ptr->G = ptr->B + 1; ++ ptr->R = ptr->B + 2; ++ ptr->Pitch = 3 * width; ++ break; ++ case MFX_FOURCC_RGB4: ++ ptr->G = ptr->B + 1; ++ ptr->R = ptr->B + 2; ++ ptr->A = ptr->B + 3; ++ ptr->Pitch = 4 * width; ++ break; ++ default: ++ return MFX_ERR_UNSUPPORTED; ++ } ++ ++ return MFX_ERR_NONE; ++} ++ ++mfxStatus ff_qsv_mem_frame_unlock(mfxHDL pthis, mfxMemId mid, ++ mfxFrameData * ptr) ++{ ++ mfxStatus sts = MFX_ERR_NONE; ++ av_qsv_allocators_space *this_alloc = (av_qsv_allocators_space *) pthis; ++ ++ sts = ++ this_alloc->buffer_alloc.Unlock(this_alloc->buffer_alloc.pthis, ++ mid); ++ ++ if (MFX_ERR_NONE != sts) ++ return sts; ++ ++ if (NULL != ptr) { ++ ptr->Pitch = 0; ++ ptr->Y = 0; ++ ptr->U = 0; ++ ptr->V = 0; ++ } ++ ++ return MFX_ERR_NONE; ++} ++ ++mfxStatus ff_qsv_mem_frame_getHDL(mfxHDL pthis, mfxMemId mid, ++ mfxHDL * handle) ++{ ++ return MFX_ERR_UNSUPPORTED; ++} ++ ++mfxStatus ff_qsv_mem_frame_free(mfxHDL pthis, ++ mfxFrameAllocResponse * response) ++{ ++ mfxStatus sts = MFX_ERR_NONE; ++ av_qsv_allocators_space *this_alloc = (av_qsv_allocators_space *) pthis; ++ mfxU32 i; ++ ++ if (!response) ++ return MFX_ERR_NULL_PTR; ++ ++ if (!this_alloc->space) ++ return MFX_ERR_NOT_INITIALIZED; ++ ++ if (response->mids) ++ for (i = 0; i < response->NumFrameActual; i++) { ++ if (response->mids[i]) { ++ sts = ++ this_alloc->buffer_alloc.Free(this_alloc-> ++ buffer_alloc.pthis, ++ response->mids[i]); ++ if (MFX_ERR_NONE != sts) ++ return sts; ++ } ++ } ++ ++ av_freep(&response->mids); ++ ++ return sts; ++} ++ ++ ++mfxStatus ff_qsv_mem_buffer_alloc(mfxHDL pthis, mfxU32 nbytes, mfxU16 type, ++ mfxMemId * mid) ++{ ++ av_qsv_alloc_buffer *bs; ++ mfxU32 header_size; ++ mfxU8 *buffer_ptr; ++ ++ if (!mid) ++ return MFX_ERR_NULL_PTR; ++ ++ if (0 == (type & MFX_MEMTYPE_SYSTEM_MEMORY)) ++ return MFX_ERR_UNSUPPORTED; ++ ++ header_size = AV_QSV_ALIGN32(sizeof(av_qsv_alloc_buffer)); ++ buffer_ptr = (mfxU8 *) av_malloc(header_size + nbytes); ++ ++ if (!buffer_ptr) ++ return MFX_ERR_MEMORY_ALLOC; ++ ++ bs = (av_qsv_alloc_buffer *) buffer_ptr; ++ bs->id = AV_QSV_ID_BUFFER; ++ bs->type = type; ++ bs->nbytes = nbytes; ++ *mid = (mfxHDL) bs; ++ return MFX_ERR_NONE; ++} ++ ++mfxStatus ff_qsv_mem_buffer_lock(mfxHDL pthis, mfxMemId mid, mfxU8 ** ptr) ++{ ++ av_qsv_alloc_buffer *bs; ++ ++ if (!ptr) ++ return MFX_ERR_NULL_PTR; ++ ++ bs = (av_qsv_alloc_buffer *) mid; ++ ++ if (!bs) ++ return MFX_ERR_INVALID_HANDLE; ++ if (AV_QSV_ID_BUFFER != bs->id) ++ return MFX_ERR_INVALID_HANDLE; ++ ++ *ptr = (mfxU8 *) bs + AV_QSV_ALIGN32(sizeof(av_qsv_alloc_buffer)); ++ return MFX_ERR_NONE; ++} ++ ++mfxStatus ff_qsv_mem_buffer_unlock(mfxHDL pthis, mfxMemId mid) ++{ ++ av_qsv_alloc_buffer *bs = (av_qsv_alloc_buffer *) mid; ++ ++ if (!bs || AV_QSV_ID_BUFFER != bs->id) ++ return MFX_ERR_INVALID_HANDLE; ++ ++ return MFX_ERR_NONE; ++} ++ ++mfxStatus ff_qsv_mem_buffer_free(mfxHDL pthis, mfxMemId mid) ++{ ++ av_qsv_alloc_buffer *bs = (av_qsv_alloc_buffer *) mid; ++ if (!bs || AV_QSV_ID_BUFFER != bs->id) ++ return MFX_ERR_INVALID_HANDLE; ++ ++ av_freep(&bs); ++ return MFX_ERR_NONE; ++} ++ ++ ++AVCodec ff_h264_qsv_decoder = { ++ .name = "h264_qsv", ++ .type = AVMEDIA_TYPE_VIDEO, ++ .id = AV_CODEC_ID_H264, ++ .init = ff_qsv_decode_init, ++ .close = qsv_decode_end, ++ .decode = qsv_decode_frame, ++ .capabilities = CODEC_CAP_DR1 | CODEC_CAP_DELAY, ++ .flush = qsv_flush_dpb, ++ .long_name = NULL_IF_CONFIG_SMALL("H.264 / AVC / Intel QSV"), ++ .pix_fmts = (const enum PixelFormat[]) {AV_PIX_FMT_QSV_H264, ++ AV_PIX_FMT_NONE}, ++}; +diff -Naur ../../libav-v9.6/libavcodec/qsv_h264.h ./libavcodec/qsv_h264.h +--- ../../libav-v9.6/libavcodec/qsv_h264.h 1970-01-01 01:00:00.000000000 +0100 ++++ ./libavcodec/qsv_h264.h 2013-08-19 21:32:01.710244809 +0200 +@@ -0,0 +1,65 @@ ++/* ********************************************************************* *\ ++ ++Copyright (C) 2013 Intel Corporation. All rights reserved. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions are met: ++- Redistributions of source code must retain the above copyright notice, ++this list of conditions and the following disclaimer. ++- Redistributions in binary form must reproduce the above copyright notice, ++this list of conditions and the following disclaimer in the documentation ++and/or other materials provided with the distribution. ++- Neither the name of Intel Corporation nor the names of its contributors ++may be used to endorse or promote products derived from this software ++without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR ++IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES ++OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ++IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, ++INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT ++NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ++DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ++THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF ++THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++\* ********************************************************************* */ ++ ++#ifndef AVCODEC_QSV_H264_H ++#define AVCODEC_QSV_H264_H ++ ++#include "qsv.h" ++ ++int ff_qsv_dec_init(AVCodecContext *); ++int ff_qsv_nal_find_start_code(uint8_t * pb, size_t size); ++ ++int ff_qsv_dec_init_clean(AVCodecContext *avctx); ++av_cold int ff_qsv_decode_init(AVCodecContext * avctx); ++static av_cold int qsv_decode_end(AVCodecContext * avctx); ++static int qsv_decode_frame(AVCodecContext * avctx, void *data, ++ int *data_size, AVPacket * avpkt); ++static void qsv_flush_dpb(AVCodecContext * avctx); ++ ++ ++// Default for SYSTEM MEMORY ++// as from MFXFrameAllocator ++mfxStatus ff_qsv_mem_frame_alloc(mfxHDL pthis, ++ mfxFrameAllocRequest * request, ++ mfxFrameAllocResponse * response); ++mfxStatus ff_qsv_mem_frame_lock(mfxHDL pthis, mfxMemId mid, ++ mfxFrameData * ptr); ++mfxStatus ff_qsv_mem_frame_unlock(mfxHDL pthis, mfxMemId mid, ++ mfxFrameData * ptr); ++mfxStatus ff_qsv_mem_frame_getHDL(mfxHDL pthis, mfxMemId mid, ++ mfxHDL * handle); ++mfxStatus ff_qsv_mem_frame_free(mfxHDL pthis, ++ mfxFrameAllocResponse * response); ++// as from mfxBufferAllocator ++mfxStatus ff_qsv_mem_buffer_alloc(mfxHDL pthis, mfxU32 nbytes, mfxU16 type, ++ mfxMemId * mid); ++mfxStatus ff_qsv_mem_buffer_lock(mfxHDL pthis, mfxMemId mid, mfxU8 ** ptr); ++mfxStatus ff_qsv_mem_buffer_unlock(mfxHDL pthis, mfxMemId mid); ++mfxStatus ff_qsv_mem_buffer_free(mfxHDL pthis, mfxMemId mid); ++ ++#endif //AVCODEC_QSV_H264_H +diff -Naur ../../libav-v9.6/libavutil/pixfmt.h ./libavutil/pixfmt.h +--- ../../libav-v9.6/libavutil/pixfmt.h 2013-05-12 08:39:07.000000000 +0200 ++++ ./libavutil/pixfmt.h 2013-08-14 10:48:00.522497405 +0200 +@@ -178,6 +178,7 @@ + AV_PIX_FMT_YUVA422P16LE, ///< planar YUV 4:2:2 48bpp, (1 Cr & Cb sample per 2x1 Y & A samples, little-endian) + AV_PIX_FMT_YUVA444P16BE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, big-endian) + AV_PIX_FMT_YUVA444P16LE, ///< planar YUV 4:4:4 64bpp, (1 Cr & Cb sample per 1x1 Y & A samples, little-endian) ++ AV_PIX_FMT_QSV_H264, ///< H.264 HW decoding with QSV, data[2] contains qsv_atom information for MFX_IOPATTERN_OUT_OPAQUE_MEMORY, MFX_IOPATTERN_OUT_VIDEO_MEMORY + AV_PIX_FMT_NB, ///< number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of formats might differ between versions + + #if FF_API_PIX_FMT diff --git a/contrib/libmfx/module.defs b/contrib/libmfx/module.defs new file mode 100644 index 000000000..4377123b9 --- /dev/null +++ b/contrib/libmfx/module.defs @@ -0,0 +1,6 @@ +$(eval $(call import.MODULE.defs,LIBMFX,libmfx)) +$(eval $(call import.CONTRIB.defs,LIBMFX)) + +LIBMFX.FETCH.url = http://download.handbrake.fr/contrib/libmfx_intel_msdk_2013r2.tar.bz2 + +LIBMFX.CONFIGURE.bootstrap = rm -fr aclocal.m4 autom4te.cache; autoreconf -fiv; diff --git a/contrib/libmfx/module.rules b/contrib/libmfx/module.rules new file mode 100644 index 000000000..ffa581f04 --- /dev/null +++ b/contrib/libmfx/module.rules @@ -0,0 +1,2 @@ +$(eval $(call import.MODULE.rules,LIBMFX)) +$(eval $(call import.CONTRIB.rules,LIBMFX)) diff --git a/libhb/enc_qsv.c b/libhb/enc_qsv.c new file mode 100644 index 000000000..c1c832a81 --- /dev/null +++ b/libhb/enc_qsv.c @@ -0,0 +1,1543 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#include "hb.h" +#include "enc_qsv.h" +#include "qsv_common.h" +#include "qsv_memory.h" +#include "h264_common.h" + +int encqsvInit( hb_work_object_t *, hb_job_t * ); +int encqsvWork( hb_work_object_t *, hb_buffer_t **, hb_buffer_t ** ); +void encqsvClose( hb_work_object_t * ); + +hb_work_object_t hb_encqsv = +{ + WORK_ENCQSV, + "H.264/AVC encoder (Intel QSV)", + encqsvInit, + encqsvWork, + encqsvClose +}; + +struct hb_work_private_s +{ + hb_job_t *job; + uint32_t frames_in; + uint32_t frames_out; + int64_t last_start; + + hb_qsv_param_t param; + av_qsv_space enc_space; + + mfxEncodeCtrl force_keyframe; + struct + { + int index; + int64_t start; + } next_chapter; + +#define BFRM_DELAY_MAX 16 + // for DTS generation (when MSDK API < 1.6 or VFR) + int bfrm_delay; + int bfrm_workaround; + int64_t init_pts[BFRM_DELAY_MAX + 1]; + hb_list_t *list_dts; + + int async_depth; + int max_async_depth; + + // if encode-only, system memory used + int is_sys_mem; + struct SwsContext *sws_context_to_nv12; + + // whether to expect input from VPP or from QSV decode + int is_vpp_present; + + // whether the encoder is initialized + int init_done; + + hb_list_t *delayed_processing; +}; + +// for DTS generation (when MSDK API < 1.6 or VFR) +static void hb_qsv_add_new_dts(hb_list_t *list, int64_t new_dts) +{ + if (list != NULL) + { + int64_t *item = malloc(sizeof(int64_t)); + if (item != NULL) + { + *item = new_dts; + hb_list_add(list, item); + } + } +} +static int64_t hb_qsv_pop_next_dts(hb_list_t *list) +{ + int64_t next_dts = INT64_MIN; + if (list != NULL && hb_list_count(list) > 0) + { + int64_t *item = hb_list_item(list, 0); + if (item != NULL) + { + next_dts = *item; + hb_list_rem(list, item); + free(item); + } + } + return next_dts; +} + +static const char* qsv_h264_profile_xlat(int profile) +{ + switch (profile) + { + case MFX_PROFILE_AVC_CONSTRAINED_BASELINE: + return "Constrained Baseline"; + case MFX_PROFILE_AVC_BASELINE: + return "Baseline"; + case MFX_PROFILE_AVC_EXTENDED: + return "Extended"; + case MFX_PROFILE_AVC_MAIN: + return "Main"; + case MFX_PROFILE_AVC_CONSTRAINED_HIGH: + return "Constrained High"; + case MFX_PROFILE_AVC_PROGRESSIVE_HIGH: + return "Progressive High"; + case MFX_PROFILE_AVC_HIGH: + return "High"; + case MFX_PROFILE_UNKNOWN: + default: + return NULL; + } +} + +static const char* qsv_h264_level_xlat(int level) +{ + int i; + for (i = 0; hb_h264_level_names[i] != NULL; i++) + { + if (hb_h264_level_values[i] == level) + { + return hb_h264_level_names[i]; + } + } + return NULL; +} + +int qsv_enc_init(av_qsv_context *qsv, hb_work_private_t *pv) +{ + int i = 0; + mfxStatus sts; + hb_job_t *job = pv->job; + + if (pv->init_done) + { + return 0; + } + + if (qsv == NULL) + { + if (!pv->is_sys_mem) + { + hb_error("qsv_enc_init: decode enabled but no context!"); + return 3; + } + job->qsv = qsv = av_mallocz(sizeof(av_qsv_context)); + } + + av_qsv_space *qsv_encode = qsv->enc_space; + if (qsv_encode == NULL) + { + // if only for encode + if (pv->is_sys_mem) + { + // no need to use additional sync as encode only -> single thread + // XXX: this zeroes the session handle, so call it before MFXInit + av_qsv_add_context_usage(qsv, 0); + + // initialize the session + qsv->impl = MFX_IMPL_AUTO_ANY; + qsv->ver.Major = AV_QSV_MSDK_VERSION_MAJOR; + qsv->ver.Minor = AV_QSV_MSDK_VERSION_MINOR; + sts = MFXInit(qsv->impl, &qsv->ver, &qsv->mfx_session); + if (sts != MFX_ERR_NONE) + { + hb_error("qsv_enc_init: MFXInit failed (%d)", sts); + *job->die = 1; + return -1; + } + } + qsv->enc_space = qsv_encode = &pv->enc_space; + } + + if (!pv->is_sys_mem) + { + if (!pv->is_vpp_present && job->list_filter != NULL) + { + for (i = 0; i < hb_list_count(job->list_filter); i++) + { + hb_filter_object_t *filter = hb_list_item(job->list_filter, i); + if (filter->id == HB_FILTER_QSV_PRE || + filter->id == HB_FILTER_QSV_POST || + filter->id == HB_FILTER_QSV) + { + pv->is_vpp_present = 1; + break; + } + } + } + + if (pv->is_vpp_present) + { + if (qsv->vpp_space == NULL) + { + return 2; + } + for (i = 0; i < av_qsv_list_count(qsv->vpp_space); i++) + { + av_qsv_space *vpp = av_qsv_list_item(qsv->vpp_space, i); + if (!vpp->is_init_done) + { + return 2; + } + } + } + + av_qsv_space *dec_space = qsv->dec_space; + if (dec_space == NULL || !dec_space->is_init_done) + { + return 2; + } + } + else + { + pv->sws_context_to_nv12 = hb_sws_get_context(job->width, job->height, + AV_PIX_FMT_YUV420P, + job->width, job->height, + AV_PIX_FMT_NV12, + SWS_LANCZOS|SWS_ACCURATE_RND); + } + + // allocate tasks + qsv_encode->p_buf_max_size = AV_QSV_BUF_SIZE_DEFAULT; + qsv_encode->tasks = av_qsv_list_init(HAVE_THREADS); + for (i = 0; i < pv->max_async_depth; i++) + { + av_qsv_task *task = av_mallocz(sizeof(av_qsv_task)); + task->bs = av_mallocz(sizeof(mfxBitstream)); + task->bs->Data = av_mallocz(sizeof(uint8_t) * qsv_encode->p_buf_max_size); + task->bs->MaxLength = qsv_encode->p_buf_max_size; + task->bs->DataLength = 0; + task->bs->DataOffset = 0; + av_qsv_list_add(qsv_encode->tasks, task); + } + + // setup surface allocation + qsv_encode->m_mfxVideoParam.IOPattern = (pv->is_sys_mem ? + MFX_IOPATTERN_IN_SYSTEM_MEMORY : + MFX_IOPATTERN_IN_OPAQUE_MEMORY); + memset(&qsv_encode->request, 0, sizeof(mfxFrameAllocRequest) * 2); + sts = MFXVideoENCODE_QueryIOSurf(qsv->mfx_session, + &qsv_encode->m_mfxVideoParam, + &qsv_encode->request); + if (sts < MFX_ERR_NONE) // ignore warnings + { + hb_error("qsv_enc_init: MFXVideoENCODE_QueryIOSurf failed (%d)", sts); + *job->die = 1; + return -1; + } + + // allocate surfaces + if (pv->is_sys_mem) + { + qsv_encode->surface_num = FFMIN(qsv_encode->request[0].NumFrameSuggested + + pv->max_async_depth, AV_QSV_SURFACE_NUM); + if (qsv_encode->surface_num <= 0) + { + qsv_encode->surface_num = AV_QSV_SURFACE_NUM; + } + for (i = 0; i < qsv_encode->surface_num; i++) + { + qsv_encode->p_surfaces[i] = av_mallocz(sizeof(mfxFrameSurface1)); + AV_QSV_CHECK_POINTER(qsv_encode->p_surfaces[i], MFX_ERR_MEMORY_ALLOC); + memcpy(&(qsv_encode->p_surfaces[i]->Info), + &(qsv_encode->request[0].Info), sizeof(mfxFrameInfo)); + } + } + else + { + av_qsv_space *in_space = qsv->dec_space; + if (pv->is_vpp_present) + { + // we get our input from VPP instead + in_space = av_qsv_list_item(qsv->vpp_space, + av_qsv_list_count(qsv->vpp_space) - 1); + } + // introduced in API 1.3 + memset(&qsv_encode->ext_opaque_alloc, 0, sizeof(mfxExtOpaqueSurfaceAlloc)); + qsv_encode->ext_opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; + qsv_encode->ext_opaque_alloc.Header.BufferSz = sizeof(mfxExtOpaqueSurfaceAlloc); + qsv_encode->ext_opaque_alloc.In.Surfaces = in_space->p_surfaces; + qsv_encode->ext_opaque_alloc.In.NumSurface = in_space->surface_num; + qsv_encode->ext_opaque_alloc.In.Type = qsv_encode->request[0].Type; + qsv_encode->m_mfxVideoParam.ExtParam[qsv_encode->m_mfxVideoParam.NumExtParam++] = (mfxExtBuffer*)&qsv_encode->ext_opaque_alloc; + } + + // allocate sync points + qsv_encode->sync_num = (qsv_encode->surface_num ? + FFMIN(qsv_encode->surface_num, AV_QSV_SYNC_NUM) : + AV_QSV_SYNC_NUM); + for (i = 0; i < qsv_encode->sync_num; i++) + { + qsv_encode->p_syncp[i] = av_mallocz(sizeof(av_qsv_sync)); + AV_QSV_CHECK_POINTER(qsv_encode->p_syncp[i], MFX_ERR_MEMORY_ALLOC); + qsv_encode->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint)); + AV_QSV_CHECK_POINTER(qsv_encode->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC); + } + + // initialize the encoder + sts = MFXVideoENCODE_Init(qsv->mfx_session, &qsv_encode->m_mfxVideoParam); + if (sts < MFX_ERR_NONE) // ignore warnings + { + hb_error("qsv_enc_init: MFXVideoENCODE_Init failed (%d)", sts); + *job->die = 1; + return -1; + } + qsv_encode->is_init_done = 1; + + pv->init_done = 1; + return 0; +} + +/*********************************************************************** + * encqsvInit + *********************************************************************** + * + **********************************************************************/ +int encqsvInit(hb_work_object_t *w, hb_job_t *job) +{ + hb_work_private_t *pv = calloc(1, sizeof(hb_work_private_t)); + w->private_data = pv; + + pv->job = job; + pv->is_sys_mem = !hb_qsv_decode_is_enabled(job); + pv->delayed_processing = hb_list_init(); + pv->last_start = INT64_MIN; + pv->frames_in = 0; + pv->frames_out = 0; + pv->init_done = 0; + pv->is_vpp_present = 0; + + // set up a re-usable mfxEncodeCtrl to force keyframes (e.g. for chapters) + pv->force_keyframe.QP = 0; + pv->force_keyframe.FrameType = MFX_FRAMETYPE_I|MFX_FRAMETYPE_IDR|MFX_FRAMETYPE_REF; + pv->force_keyframe.NumExtParam = 0; + pv->force_keyframe.NumPayload = 0; + pv->force_keyframe.ExtParam = NULL; + pv->force_keyframe.Payload = NULL; + + pv->next_chapter.index = 0; + pv->next_chapter.start = INT64_MIN; + + // default encoding parameters + if (hb_qsv_param_default(&pv->param, &pv->enc_space.m_mfxVideoParam)) + { + hb_error("encqsvInit: hb_qsv_param_default failed"); + return -1; + } + + // set AsyncDepth to match that of decode and VPP + pv->param.videoParam->AsyncDepth = job->qsv_async_depth; + + // enable and set colorimetry (video signal information) + pv->param.videoSignalInfo.ColourDescriptionPresent = 1; + switch (job->color_matrix_code) + { + case 4: + // custom + pv->param.videoSignalInfo.ColourPrimaries = job->color_prim; + pv->param.videoSignalInfo.TransferCharacteristics = job->color_transfer; + pv->param.videoSignalInfo.MatrixCoefficients = job->color_matrix; + break; + case 3: + // ITU BT.709 HD content + pv->param.videoSignalInfo.ColourPrimaries = HB_COLR_PRI_BT709; + pv->param.videoSignalInfo.TransferCharacteristics = HB_COLR_TRA_BT709; + pv->param.videoSignalInfo.MatrixCoefficients = HB_COLR_MAT_BT709; + break; + case 2: + // ITU BT.601 DVD or SD TV content (PAL) + pv->param.videoSignalInfo.ColourPrimaries = HB_COLR_PRI_EBUTECH; + pv->param.videoSignalInfo.TransferCharacteristics = HB_COLR_TRA_BT709; + pv->param.videoSignalInfo.MatrixCoefficients = HB_COLR_MAT_SMPTE170M; + break; + case 1: + // ITU BT.601 DVD or SD TV content (NTSC) + pv->param.videoSignalInfo.ColourPrimaries = HB_COLR_PRI_SMPTEC; + pv->param.videoSignalInfo.TransferCharacteristics = HB_COLR_TRA_BT709; + pv->param.videoSignalInfo.MatrixCoefficients = HB_COLR_MAT_SMPTE170M; + break; + default: + // detected during scan + pv->param.videoSignalInfo.ColourPrimaries = job->title->color_prim; + pv->param.videoSignalInfo.TransferCharacteristics = job->title->color_transfer; + pv->param.videoSignalInfo.MatrixCoefficients = job->title->color_matrix; + break; + } + + // parse user-specified advanced options, if present + if (job->advanced_opts != NULL && job->advanced_opts[0] != '\0') + { + hb_dict_t *options_list; + hb_dict_entry_t *option = NULL; + options_list = hb_encopts_to_dict(job->advanced_opts, job->vcodec); + while ((option = hb_dict_next(options_list, option)) != NULL) + { + switch (hb_qsv_param_parse(&pv->param, + option->key, option->value, job->vcodec)) + { + case HB_QSV_PARAM_OK: + break; + + case HB_QSV_PARAM_BAD_NAME: + hb_log("encqsvInit: hb_qsv_param_parse: bad key %s", + option->key); + break; + case HB_QSV_PARAM_BAD_VALUE: + hb_log("encqsvInit: hb_qsv_param_parse: bad value %s for key %s", + option->value, option->key); + break; + case HB_QSV_PARAM_UNSUPPORTED: + hb_log("encqsvInit: hb_qsv_param_parse: unsupported option %s", + option->key); + break; + + case HB_QSV_PARAM_ERROR: + default: + hb_log("encqsvInit: hb_qsv_param_parse: unknown error"); + break; + } + } + hb_dict_free(&options_list); + } + + // reload colorimetry in case values were set in advanced_opts + if (pv->param.videoSignalInfo.ColourDescriptionPresent) + { + job->color_matrix_code = 4; + job->color_prim = pv->param.videoSignalInfo.ColourPrimaries; + job->color_transfer = pv->param.videoSignalInfo.TransferCharacteristics; + job->color_matrix = pv->param.videoSignalInfo.MatrixCoefficients; + } + else + { + job->color_matrix_code = 0; + job->color_prim = HB_COLR_PRI_UNDEF; + job->color_transfer = HB_COLR_TRA_UNDEF; + job->color_matrix = HB_COLR_MAT_UNDEF; + } + + // sanitize values that may exceed the Media SDK variable size + int64_t vrate, vrate_base; + int64_t par_width, par_height; + hb_limit_rational64(&vrate, &vrate_base, + job->vrate, job->vrate_base, UINT32_MAX); + hb_limit_rational64(&par_width, &par_height, + job->anamorphic.par_width, + job->anamorphic.par_height, UINT16_MAX); + + // some encoding parameters are used by filters to configure their output + if (pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE) + { + job->qsv_enc_info.align_height = AV_QSV_ALIGN32(job->height); + } + else + { + job->qsv_enc_info.align_height = AV_QSV_ALIGN16(job->height); + } + job->qsv_enc_info.align_width = AV_QSV_ALIGN16(job->width); + job->qsv_enc_info.pic_struct = pv->param.videoParam->mfx.FrameInfo.PicStruct; + job->qsv_enc_info.is_init_done = 1; + + // encode to H.264 and set FrameInfo + pv->param.videoParam->mfx.CodecId = MFX_CODEC_AVC; + pv->param.videoParam->mfx.CodecLevel = MFX_LEVEL_UNKNOWN; + pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_UNKNOWN; + pv->param.videoParam->mfx.FrameInfo.FourCC = MFX_FOURCC_NV12; + pv->param.videoParam->mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420; + pv->param.videoParam->mfx.FrameInfo.FrameRateExtN = vrate; + pv->param.videoParam->mfx.FrameInfo.FrameRateExtD = vrate_base; + pv->param.videoParam->mfx.FrameInfo.AspectRatioW = par_width; + pv->param.videoParam->mfx.FrameInfo.AspectRatioH = par_height; + pv->param.videoParam->mfx.FrameInfo.CropX = 0; + pv->param.videoParam->mfx.FrameInfo.CropY = 0; + pv->param.videoParam->mfx.FrameInfo.CropW = job->width; + pv->param.videoParam->mfx.FrameInfo.CropH = job->height; + pv->param.videoParam->mfx.FrameInfo.PicStruct = job->qsv_enc_info.pic_struct; + pv->param.videoParam->mfx.FrameInfo.Width = job->qsv_enc_info.align_width; + pv->param.videoParam->mfx.FrameInfo.Height = job->qsv_enc_info.align_height; + + // set H.264 profile and level + if (job->h264_profile != NULL && job->h264_profile[0] != '\0' && + strcasecmp(job->h264_profile, "auto")) + { + if (!strcasecmp(job->h264_profile, "baseline")) + { + pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_AVC_BASELINE; + } + else if (!strcasecmp(job->h264_profile, "main")) + { + pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_AVC_MAIN; + } + else if (!strcasecmp(job->h264_profile, "high")) + { + pv->param.videoParam->mfx.CodecProfile = MFX_PROFILE_AVC_HIGH; + } + else + { + hb_error("encqsvInit: bad profile %s", job->h264_profile); + return -1; + } + } + if (job->h264_level != NULL && job->h264_level[0] != '\0' && + strcasecmp(job->h264_level, "auto")) + { + int err; + int i = hb_qsv_atoindex(hb_h264_level_names, job->h264_level, &err); + if (err || i >= (sizeof(hb_h264_level_values) / + sizeof(hb_h264_level_values[0]))) + { + hb_error("encqsvInit: bad level %s", job->h264_level); + return -1; + } + else if (hb_qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6) + { + pv->param.videoParam->mfx.CodecLevel = HB_QSV_CLIP3(MFX_LEVEL_AVC_1, + MFX_LEVEL_AVC_52, + hb_h264_level_values[i]); + } + else + { + // Media SDK API < 1.6, MFX_LEVEL_AVC_52 unsupported + pv->param.videoParam->mfx.CodecLevel = HB_QSV_CLIP3(MFX_LEVEL_AVC_1, + MFX_LEVEL_AVC_51, + hb_h264_level_values[i]); + } + } + + // interlaced encoding is not always possible + if (pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE) + { + if (pv->param.videoParam->mfx.CodecProfile == MFX_PROFILE_AVC_CONSTRAINED_BASELINE || + pv->param.videoParam->mfx.CodecProfile == MFX_PROFILE_AVC_BASELINE || + pv->param.videoParam->mfx.CodecProfile == MFX_PROFILE_AVC_PROGRESSIVE_HIGH) + { + hb_error("encqsvInit: profile %s doesn't support interlaced encoding", + qsv_h264_profile_xlat(pv->param.videoParam->mfx.CodecProfile)); + return -1; + } + if ((pv->param.videoParam->mfx.CodecLevel >= MFX_LEVEL_AVC_1b && + pv->param.videoParam->mfx.CodecLevel <= MFX_LEVEL_AVC_2) || + (pv->param.videoParam->mfx.CodecLevel >= MFX_LEVEL_AVC_42)) + { + hb_error("encqsvInit: level %s doesn't support interlaced encoding", + qsv_h264_level_xlat(pv->param.videoParam->mfx.CodecLevel)); + return -1; + } + } + + // set rate control paremeters + if (job->vquality >= 0) + { + // introduced in API 1.1 + pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_CQP; + pv->param.videoParam->mfx.QPI = HB_QSV_CLIP3(0, 51, job->vquality + pv->param.rc.cqp_offsets[0]); + pv->param.videoParam->mfx.QPP = HB_QSV_CLIP3(0, 51, job->vquality + pv->param.rc.cqp_offsets[1]); + pv->param.videoParam->mfx.QPB = HB_QSV_CLIP3(0, 51, job->vquality + pv->param.rc.cqp_offsets[2]); + } + else if (job->vbitrate > 0) + { + // sanitize lookahead + if (!(hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD)) + { + // lookahead not supported + pv->param.rc.lookahead = 0; + } + else if (pv->param.rc.lookahead > 0 && + pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE) + { + // user force-enabled lookahead but we can't use it + hb_log("encqsvInit: MFX_RATECONTROL_LA not used (LookAhead is progressive-only)"); + pv->param.rc.lookahead = 0; + } + else if (pv->param.rc.lookahead < 0) + { + if (pv->param.rc.vbv_max_bitrate > 0 || + pv->param.videoParam->mfx.FrameInfo.PicStruct != MFX_PICSTRUCT_PROGRESSIVE) + { + // lookahead doesn't support VBV or interlaced encoding + pv->param.rc.lookahead = 0; + } + else + { + // set automatically based on target usage + pv->param.rc.lookahead = (pv->param.videoParam->mfx.TargetUsage <= MFX_TARGETUSAGE_2); + } + } + else + { + // user force-enabled or force-disabled lookahead + pv->param.rc.lookahead = !!pv->param.rc.lookahead; + } + if (pv->param.rc.lookahead) + { + // introduced in API 1.7 + pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_LA; + pv->param.videoParam->mfx.TargetKbps = job->vbitrate; + if (pv->param.rc.vbv_max_bitrate > 0) + { + hb_log("encqsvInit: MFX_RATECONTROL_LA, ignoring VBV"); + } + } + else if (job->vbitrate == pv->param.rc.vbv_max_bitrate) + { + // introduced in API 1.0 + pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_CBR; + pv->param.videoParam->mfx.MaxKbps = job->vbitrate; + pv->param.videoParam->mfx.TargetKbps = job->vbitrate; + pv->param.videoParam->mfx.BufferSizeInKB = (pv->param.rc.vbv_buffer_size / 8); + // only set BufferSizeInKB and InitialDelayInKB is bufsize is set + // else Media SDK will pick some good values for us automatically + if (pv->param.rc.vbv_buffer_size > 0) + { + if (pv->param.rc.vbv_buffer_init > 1.0) + { + pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_init / 8); + } + else + { + pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_size * + pv->param.rc.vbv_buffer_init / 8); + } + pv->param.videoParam->mfx.BufferSizeInKB = (pv->param.rc.vbv_buffer_size / 8); + } + } + else if (pv->param.rc.vbv_max_bitrate > 0) + { + // introduced in API 1.0 + pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_VBR; + pv->param.videoParam->mfx.MaxKbps = pv->param.rc.vbv_max_bitrate; + pv->param.videoParam->mfx.TargetKbps = job->vbitrate; + // only set BufferSizeInKB and InitialDelayInKB is bufsize is set + // else Media SDK will pick some good values for us automatically + if (pv->param.rc.vbv_buffer_size > 0) + { + if (pv->param.rc.vbv_buffer_init > 1.0) + { + pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_init / 8); + } + else + { + pv->param.videoParam->mfx.InitialDelayInKB = (pv->param.rc.vbv_buffer_size * + pv->param.rc.vbv_buffer_init / 8); + } + pv->param.videoParam->mfx.BufferSizeInKB = (pv->param.rc.vbv_buffer_size / 8); + } + } + else + { + // introduced in API 1.3 + // Media SDK will set Accuracy and Convergence for us automatically + pv->param.videoParam->mfx.RateControlMethod = MFX_RATECONTROL_AVBR; + pv->param.videoParam->mfx.TargetKbps = job->vbitrate; + } + } + else + { + hb_error("encqsvInit: invalid rate control (%d, %d)", + job->vquality, job->vbitrate); + return -1; + } + + // set the keyframe interval + if (pv->param.gop.gop_pic_size < 0) + { + int rate = (int)((double)job->vrate / (double)job->vrate_base + 0.5); + if (pv->param.videoParam->mfx.RateControlMethod == MFX_RATECONTROL_CQP) + { + // ensure B-pyramid is enabled for CQP on Haswell + pv->param.gop.gop_pic_size = 32; + } + else + { + // set the keyframe interval based on the framerate + pv->param.gop.gop_pic_size = 5 * rate + 1; + } + } + pv->param.videoParam->mfx.GopPicSize = pv->param.gop.gop_pic_size; + + // sanitize some settings that affect memory consumption + if (!pv->is_sys_mem) + { + // limit these to avoid running out of resources (causes hang) + pv->param.videoParam->mfx.GopRefDist = FFMIN(pv->param.videoParam->mfx.GopRefDist, + pv->param.rc.lookahead ? 8 : 16); + pv->param.codingOption2.LookAheadDepth = FFMIN(pv->param.codingOption2.LookAheadDepth, + pv->param.rc.lookahead ? 48 - pv->param.videoParam->mfx.GopRefDist : 0); + } + else + { + // encode-only is a bit less sensitive to memory issues + pv->param.videoParam->mfx.GopRefDist = FFMIN(pv->param.videoParam->mfx.GopRefDist, 16); + pv->param.codingOption2.LookAheadDepth = FFMIN(pv->param.codingOption2.LookAheadDepth, + pv->param.rc.lookahead ? 60 : 0); + } + + /* + * init a dummy encode-only session to get the SPS/PPS + * and the final output settings sanitized by Media SDK + * this is fine since the actual encode will use the same + * values for all parameters relevant to the H.264 bitstream + */ + mfxIMPL impl; + mfxStatus err; + mfxVersion version; + mfxVideoParam videoParam; + mfxExtBuffer* ExtParamArray[3]; + mfxSession session = (mfxSession)0; + mfxExtCodingOption option1_buf, *option1 = &option1_buf; + mfxExtCodingOption2 option2_buf, *option2 = &option2_buf; + mfxExtCodingOptionSPSPPS sps_pps_buf, *sps_pps = &sps_pps_buf; + impl = MFX_IMPL_AUTO_ANY|MFX_IMPL_VIA_ANY; + version.Major = HB_QSV_MINVERSION_MAJOR; + version.Minor = HB_QSV_MINVERSION_MINOR; + err = MFXInit(impl, &version, &session); + if (err != MFX_ERR_NONE) + { + hb_error("encqsvInit: MFXInit failed (%d)", err); + return -1; + } + err = MFXVideoENCODE_Init(session, pv->param.videoParam); + if (err < MFX_ERR_NONE) // ignore warnings + { + hb_error("encqsvInit: MFXVideoENCODE_Init failed (%d)", err); + MFXClose(session); + return -1; + } + memset(&videoParam, 0, sizeof(mfxVideoParam)); + videoParam.ExtParam = ExtParamArray; + videoParam.NumExtParam = 0; + // introduced in API 1.3 + memset(sps_pps, 0, sizeof(mfxExtCodingOptionSPSPPS)); + sps_pps->Header.BufferId = MFX_EXTBUFF_CODING_OPTION_SPSPPS; + sps_pps->Header.BufferSz = sizeof(mfxExtCodingOptionSPSPPS); + sps_pps->SPSId = 0; + sps_pps->SPSBuffer = w->config->h264.sps; + sps_pps->SPSBufSize = sizeof(w->config->h264.sps); + sps_pps->PPSId = 0; + sps_pps->PPSBuffer = w->config->h264.pps; + sps_pps->PPSBufSize = sizeof(w->config->h264.pps); + videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)sps_pps; + // introduced in API 1.0 + memset(option1, 0, sizeof(mfxExtCodingOption)); + option1->Header.BufferId = MFX_EXTBUFF_CODING_OPTION; + option1->Header.BufferSz = sizeof(mfxExtCodingOption); + videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)option1; + // introduced in API 1.6 + memset(option2, 0, sizeof(mfxExtCodingOption2)); + option2->Header.BufferId = MFX_EXTBUFF_CODING_OPTION2; + option2->Header.BufferSz = sizeof(mfxExtCodingOption2); + if (hb_qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6) + { + // attach to get the final output mfxExtCodingOption2 settings + videoParam.ExtParam[videoParam.NumExtParam++] = (mfxExtBuffer*)option2; + } + err = MFXVideoENCODE_GetVideoParam(session, &videoParam); + if (err == MFX_ERR_NONE) + { + // remove 32-bit NAL prefix (0x00 0x00 0x00 0x01) + w->config->h264.sps_length = sps_pps->SPSBufSize - 4; + memmove(w->config->h264.sps, w->config->h264.sps + 4, + w->config->h264.sps_length); + w->config->h264.pps_length = sps_pps->PPSBufSize - 4; + memmove(w->config->h264.pps, w->config->h264.pps + 4, + w->config->h264.pps_length); + } + else + { + hb_error("encqsvInit: MFXVideoENCODE_GetVideoParam failed (%d)", err); + MFXVideoENCODE_Close(session); + MFXClose (session); + return -1; + } + + // log implementation details before closing this session + if (pv->is_sys_mem) + { + hb_log("encqsvInit: using encode-only path"); + } + if ((MFXQueryIMPL (session, &impl) == MFX_ERR_NONE) && + (MFXQueryVersion(session, &version) == MFX_ERR_NONE)) + { + hb_log("encqsvInit: using %s implementation (%"PRIu16".%"PRIu16")", + impl == MFX_IMPL_SOFTWARE ? "software" : "hardware", + version.Major, version.Minor); + } + MFXVideoENCODE_Close(session); + MFXClose (session); + + // log main output settings + hb_log("encqsvInit: TargetUsage %"PRIu16" AsyncDepth %"PRIu16"", + videoParam.mfx.TargetUsage, videoParam.AsyncDepth); + hb_log("encqsvInit: GopRefDist %"PRIu16" GopPicSize %"PRIu16" NumRefFrame %"PRIu16"", + videoParam.mfx.GopRefDist, videoParam.mfx.GopPicSize, videoParam.mfx.NumRefFrame); + if (videoParam.mfx.RateControlMethod == MFX_RATECONTROL_CQP) + { + char qpi[7], qpp[9], qpb[9]; + snprintf(qpi, sizeof(qpi), "QPI %"PRIu16"", videoParam.mfx.QPI); + snprintf(qpp, sizeof(qpp), " QPP %"PRIu16"", videoParam.mfx.QPP); + snprintf(qpb, sizeof(qpb), " QPB %"PRIu16"", videoParam.mfx.QPB); + hb_log("encqsvInit: RateControlMethod CQP with %s%s%s", qpi, + videoParam.mfx.GopPicSize > 1 ? qpp : "", + videoParam.mfx.GopRefDist > 1 ? qpb : ""); + } + else + { + switch (videoParam.mfx.RateControlMethod) + { + case MFX_RATECONTROL_AVBR: + hb_log("encqsvInit: RateControlMethod AVBR TargetKbps %"PRIu16"", + videoParam.mfx.TargetKbps); + break; + case MFX_RATECONTROL_LA: + hb_log("encqsvInit: RateControlMethod LA TargetKbps %"PRIu16" LookAheadDepth %"PRIu16"", + videoParam.mfx.TargetKbps, option2->LookAheadDepth); + break; + case MFX_RATECONTROL_CBR: + case MFX_RATECONTROL_VBR: + hb_log("encqsvInit: RateControlMethod %s TargetKbps %"PRIu16" MaxKbps %"PRIu16" BufferSizeInKB %"PRIu16" InitialDelayInKB %"PRIu16"", + videoParam.mfx.RateControlMethod == MFX_RATECONTROL_CBR ? "CBR" : "VBR", + videoParam.mfx.TargetKbps, videoParam.mfx.MaxKbps, + videoParam.mfx.BufferSizeInKB, videoParam.mfx.InitialDelayInKB); + break; + default: + hb_log("encqsvInit: invalid rate control method %"PRIu16"", + videoParam.mfx.RateControlMethod); + return -1; + } + } + switch (videoParam.mfx.FrameInfo.PicStruct) + { + case MFX_PICSTRUCT_PROGRESSIVE: + hb_log("encqsvInit: PicStruct progressive"); + break; + case MFX_PICSTRUCT_FIELD_TFF: + hb_log("encqsvInit: PicStruct top field first"); + break; + case MFX_PICSTRUCT_FIELD_BFF: + hb_log("encqsvInit: PicStruct bottom field first"); + break; + default: + hb_error("encqsvInit: invalid PicStruct value 0x%"PRIx16"", + videoParam.mfx.FrameInfo.PicStruct); + return -1; + } + const char *cavlc, *rdopt; + switch (option1->CAVLC) + { + case MFX_CODINGOPTION_ON: + cavlc = "on"; + break; + case MFX_CODINGOPTION_OFF: + cavlc = "off"; + break; + default: + hb_error("encqsvInit: invalid CAVLC value %"PRIu16"", + option1->CAVLC); + return -1; + } + switch (option1->RateDistortionOpt) + { + case MFX_CODINGOPTION_ON: + rdopt = "on"; + break; + case MFX_CODINGOPTION_OFF: + rdopt = "off"; + break; + default: + hb_error("encqsvInit: invalid RateDistortionOpt value %"PRIu16"", + option1->RateDistortionOpt); + return -1; + } + hb_log("encqsvInit: CAVLC %s RateDistortionOpt %s", cavlc, rdopt); + if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_BRC) + { + const char *mbbrc, *extbrc; + switch (option2->MBBRC) + { + case MFX_CODINGOPTION_ON: + mbbrc = "on"; + break; + case MFX_CODINGOPTION_OFF: + mbbrc = "off"; + break; + case MFX_CODINGOPTION_ADAPTIVE: + mbbrc = "adaptive"; + break; + case MFX_CODINGOPTION_UNKNOWN: + mbbrc = "unknown (auto)"; + break; + default: + hb_error("encqsvInit: invalid MBBRC value %"PRIu16"", + option2->MBBRC); + return -1; + } + switch (option2->ExtBRC) + { + case MFX_CODINGOPTION_ON: + extbrc = "on"; + break; + case MFX_CODINGOPTION_OFF: + extbrc = "off"; + break; + case MFX_CODINGOPTION_ADAPTIVE: + extbrc = "adaptive"; + break; + case MFX_CODINGOPTION_UNKNOWN: + extbrc = "unknown (auto)"; + break; + default: + hb_error("encqsvInit: invalid ExtBRC value %"PRIu16"", + option2->ExtBRC); + return -1; + } + hb_log("encqsvInit: MBBRC %s ExtBRC %s", mbbrc, extbrc); + } + if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS) + { + switch (option2->Trellis) + { + case MFX_TRELLIS_OFF: + hb_log("encqsvInit: Trellis off"); + break; + case MFX_TRELLIS_UNKNOWN: + hb_log("encqsvInit: Trellis unknown (auto)"); + break; + default: + hb_log("encqsvInit: Trellis on (%s%s%s)", + option2->Trellis & MFX_TRELLIS_I ? "I" : "", + option2->Trellis & MFX_TRELLIS_P ? "P" : "", + option2->Trellis & MFX_TRELLIS_B ? "B" : ""); + break; + } + } + hb_log("encqsvInit: H.264 profile %s @ level %s", + qsv_h264_profile_xlat(videoParam.mfx.CodecProfile), + qsv_h264_level_xlat (videoParam.mfx.CodecLevel)); + + // AsyncDepth has now been set and/or modified by Media SDK + pv->max_async_depth = videoParam.AsyncDepth; + pv->async_depth = 0; + + // check whether B-frames are used + switch (videoParam.mfx.CodecProfile) + { + case MFX_PROFILE_AVC_BASELINE: + case MFX_PROFILE_AVC_CONSTRAINED_HIGH: + case MFX_PROFILE_AVC_CONSTRAINED_BASELINE: + pv->bfrm_delay = 0; + break; + default: + pv->bfrm_delay = 1; + break; + } + // sanitize + pv->bfrm_delay = FFMIN(pv->bfrm_delay, videoParam.mfx.GopRefDist - 1); + pv->bfrm_delay = FFMIN(pv->bfrm_delay, videoParam.mfx.GopPicSize - 2); + pv->bfrm_delay = FFMAX(pv->bfrm_delay, 0); + // let the muxer know whether to expect B-frames or not + job->areBframes = !!pv->bfrm_delay; + // check whether we need to generate DTS ourselves (MSDK API < 1.6 or VFR) + pv->bfrm_workaround = job->cfr != 1 || !(hb_qsv_info->capabilities & + HB_QSV_CAP_MSDK_API_1_6); + if (pv->bfrm_delay && pv->bfrm_workaround) + { + pv->bfrm_workaround = 1; + pv->list_dts = hb_list_init(); + } + else + { + pv->bfrm_workaround = 0; + pv->list_dts = NULL; + } + + return 0; +} + +void encqsvClose( hb_work_object_t * w ) +{ + int i = 0; + hb_work_private_t * pv = w->private_data; + + hb_log( "enc_qsv done: frames: %u in, %u out", pv->frames_in, pv->frames_out ); + + // if system memory ( encode only ) additional free(s) for surfaces + if( pv && pv->job && pv->job->qsv && + pv->job->qsv->is_context_active ){ + + av_qsv_context *qsv = pv->job->qsv; + + if(qsv && qsv->enc_space){ + av_qsv_space* qsv_encode = qsv->enc_space; + if(qsv_encode->is_init_done){ + if(pv->is_sys_mem){ + if( qsv_encode && qsv_encode->surface_num > 0) + for (i = 0; i < qsv_encode->surface_num; i++){ + if( qsv_encode->p_surfaces[i]->Data.Y){ + free(qsv_encode->p_surfaces[i]->Data.Y); + qsv_encode->p_surfaces[i]->Data.Y = 0; + } + if( qsv_encode->p_surfaces[i]->Data.VU){ + free(qsv_encode->p_surfaces[i]->Data.VU); + qsv_encode->p_surfaces[i]->Data.VU = 0; + } + if(qsv_encode->p_surfaces[i]) + av_freep(qsv_encode->p_surfaces[i]); + } + qsv_encode->surface_num = 0; + + sws_freeContext(pv->sws_context_to_nv12); + } + + for (i = av_qsv_list_count(qsv_encode->tasks); i > 1; i--){ + av_qsv_task* task = av_qsv_list_item(qsv_encode->tasks,i-1); + if(task && task->bs){ + av_freep(&task->bs->Data); + av_freep(&task->bs); + av_qsv_list_rem(qsv_encode->tasks,task); + } + } + av_qsv_list_close(&qsv_encode->tasks); + + for (i = 0; i < qsv_encode->surface_num; i++){ + av_freep(&qsv_encode->p_surfaces[i]); + } + qsv_encode->surface_num = 0; + + for (i = 0; i < qsv_encode->sync_num; i++){ + av_freep(&qsv_encode->p_syncp[i]->p_sync); + av_freep(&qsv_encode->p_syncp[i]); + } + qsv_encode->sync_num = 0; + + qsv_encode->is_init_done = 0; + } + } + + if(qsv){ + // closing the commong stuff + av_qsv_context_clean(qsv); + + if(pv->is_sys_mem){ + av_freep(&qsv); + } + } + } + + if (pv != NULL) + { + if (pv->list_dts != NULL) + { + while (hb_list_count(pv->list_dts) > 0) + { + int64_t *item = hb_list_item(pv->list_dts, 0); + hb_list_rem(pv->list_dts, item); + free(item); + } + hb_list_close(&pv->list_dts); + } + } + + free( pv ); + w->private_data = NULL; +} + +int encqsvWork( hb_work_object_t * w, hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + hb_work_private_t * pv = w->private_data; + hb_job_t * job = pv->job; + hb_buffer_t * in = *buf_in, *buf; + av_qsv_context *qsv = job->qsv; + av_qsv_space* qsv_encode; + hb_buffer_t *last_buf = NULL; + mfxStatus sts = MFX_ERR_NONE; + int is_end = 0; + av_qsv_list* received_item = 0; + av_qsv_stage* stage = 0; + + while(1){ + int ret = qsv_enc_init(qsv, pv); + qsv = job->qsv; + qsv_encode = qsv->enc_space; + if(ret >= 2) + av_qsv_sleep(1); + else + break; + } + *buf_out = NULL; + + if( in->size <= 0 ) + { + // do delayed frames yet + *buf_in = NULL; + is_end = 1; + } + + // input from decode, as called - we always have some to proceed with + while (1) + { + { + mfxEncodeCtrl *work_control = NULL; + mfxFrameSurface1 *work_surface = NULL; + + if (!is_end) + { + if (pv->is_sys_mem) + { + int surface_idx = av_qsv_get_free_surface(qsv_encode, qsv, + &qsv_encode->request[0].Info, QSV_PART_ANY); + work_surface = qsv_encode->p_surfaces[surface_idx]; + + if (work_surface->Data.Y == NULL) + { + // if nv12 and 422 12bits per pixel + work_surface->Data.Pitch = pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Width; + work_surface->Data.Y = calloc(1, + pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Width * + pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Height); + work_surface->Data.VU = calloc(1, + pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Width * + pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.Height / 2); + } + qsv_yuv420_to_nv12(pv->sws_context_to_nv12, work_surface, in); + } + else + { + received_item = in->qsv_details.qsv_atom; + stage = av_qsv_get_last_stage(received_item); + work_surface = stage->out.p_surface; + + // don't let qsv->dts_seq grow needlessly + av_qsv_dts_pop(qsv); + } + + work_surface->Data.TimeStamp = in->s.start; + + /* + * Debugging code to check that the upstream modules have generated + * a continuous, self-consistent frame stream. + */ + int64_t start = work_surface->Data.TimeStamp; + if (pv->last_start > start) + { + hb_log("encqsvWork: input continuity error, last start %"PRId64" start %"PRId64"", + pv->last_start, start); + } + pv->last_start = start; + + // for DTS generation (when MSDK API < 1.6 or VFR) + if (pv->bfrm_delay && pv->bfrm_workaround) + { + if (pv->frames_in <= BFRM_DELAY_MAX) + { + pv->init_pts[pv->frames_in] = work_surface->Data.TimeStamp; + } + if (pv->frames_in) + { + hb_qsv_add_new_dts(pv->list_dts, + work_surface->Data.TimeStamp); + } + } + + /* + * Chapters have to start with a keyframe so request that this + * frame be coded as IDR. Since there may be several frames + * buffered in the encoder, remember the timestamp so when this + * frame finally pops out of the encoder we'll mark its buffer + * as the start of a chapter. + */ + if (in->s.new_chap > 0 && job->chapter_markers) + { + if (!pv->next_chapter.index) + { + pv->next_chapter.start = work_surface->Data.TimeStamp; + pv->next_chapter.index = in->s.new_chap; + work_control = &pv->force_keyframe; + } + else + { + // however unlikely, this can happen in theory + hb_log("encqsvWork: got chapter %d before we could write chapter %d, dropping marker", + in->s.new_chap, pv->next_chapter.index); + } + // don't let 'work_loop' put a chapter mark on the wrong buffer + in->s.new_chap = 0; + } + + /* + * If interlaced encoding is requested during encoder initialization, + * but the input mfxFrameSurface1 is flagged as progressive here, + * the output bitstream will be progressive (according to MediaInfo). + * + * Assume the user knows what he's doing (say he is e.g. encoding a + * progressive-flagged source using interlaced compression - he may + * well have a good reason to do so; mis-flagged sources do exist). + */ + work_surface->Info.PicStruct = pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.PicStruct; + } + else{ + work_surface = NULL; + received_item = NULL; + } + int sync_idx = av_qsv_get_free_sync( qsv_encode, qsv ); + if (sync_idx == -1) + { + hb_error("qsv: Not enough resources allocated for QSV encode"); + return 0; + } + av_qsv_task *task = av_qsv_list_item( qsv_encode->tasks, pv->async_depth ); + + for (;;) + { + // Encode a frame asychronously (returns immediately) + sts = MFXVideoENCODE_EncodeFrameAsync(qsv->mfx_session, + work_control, work_surface, task->bs, + qsv_encode->p_syncp[sync_idx]->p_sync); + + if (MFX_ERR_MORE_DATA == sts || (MFX_ERR_NONE <= sts && MFX_WRN_DEVICE_BUSY != sts)) + if (work_surface && !pv->is_sys_mem) + ff_qsv_atomic_dec(&work_surface->Data.Locked); + + if( MFX_ERR_MORE_DATA == sts ){ + ff_qsv_atomic_dec(&qsv_encode->p_syncp[sync_idx]->in_use); + if(work_surface && received_item) + hb_list_add(pv->delayed_processing, received_item); + break; + } + + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + if (MFX_ERR_NONE <= sts /*&& !syncpE*/) // repeat the call if warning and no output + { + if (MFX_WRN_DEVICE_BUSY == sts){ + av_qsv_sleep(10); // wait if device is busy + continue; + } + + av_qsv_stage* new_stage = av_qsv_stage_init(); + new_stage->type = AV_QSV_ENCODE; + new_stage->in.p_surface = work_surface; + new_stage->out.sync = qsv_encode->p_syncp[sync_idx]; + + new_stage->out.p_bs = task->bs;//qsv_encode->bs; + task->stage = new_stage; + + pv->async_depth++; + + if(received_item){ + av_qsv_add_stagee( &received_item, new_stage,HAVE_THREADS ); + } + else{ + // flushing the end + int pipe_idx = av_qsv_list_add( qsv->pipes, av_qsv_list_init(HAVE_THREADS) ); + av_qsv_list* list_item = av_qsv_list_item( qsv->pipes, pipe_idx ); + av_qsv_add_stagee( &list_item, new_stage,HAVE_THREADS ); + } + + int i = 0; + for(i=hb_list_count(pv->delayed_processing); i > 0;i--){ + hb_list_t *item = hb_list_item(pv->delayed_processing,i-1); + if(item){ + hb_list_rem(pv->delayed_processing,item); + av_qsv_flush_stages(qsv->pipes, &item); + } + } + + break; + } + + ff_qsv_atomic_dec(&qsv_encode->p_syncp[sync_idx]->in_use); + + if (MFX_ERR_NOT_ENOUGH_BUFFER == sts) + DEBUG_ASSERT( 1,"The bitstream buffer size is insufficient." ); + + break; + } + } + + buf = NULL; + + do{ + + if(pv->async_depth==0) break; + + // working properly with sync depth approach of MediaSDK OR flushing, if at the end + if( (pv->async_depth >= pv->max_async_depth) || is_end ){ + + pv->async_depth--; + + av_qsv_task *task = av_qsv_list_item( qsv_encode->tasks, 0 ); + av_qsv_stage* stage = task->stage; + av_qsv_list* this_pipe = av_qsv_pipe_by_stage(qsv->pipes,stage); + sts = MFX_ERR_NONE; + + // only here we need to wait on operation been completed, therefore SyncOperation is used, + // after this step - we continue to work with bitstream, muxing ... + av_qsv_wait_on_sync( qsv,stage ); + + if(task->bs->DataLength>0){ + av_qsv_flush_stages( qsv->pipes, &this_pipe ); + + // see nal_encode + buf = hb_video_buffer_init( job->width, job->height ); + buf->size = 0; + buf->s.frametype = 0; + + // maping of FrameType(s) + if(task->bs->FrameType & MFX_FRAMETYPE_IDR ) buf->s.frametype = HB_FRAME_IDR; + else + if(task->bs->FrameType & MFX_FRAMETYPE_I ) buf->s.frametype = HB_FRAME_I; + else + if(task->bs->FrameType & MFX_FRAMETYPE_P ) buf->s.frametype = HB_FRAME_P; + else + if(task->bs->FrameType & MFX_FRAMETYPE_B ) buf->s.frametype = HB_FRAME_B; + + if(task->bs->FrameType & MFX_FRAMETYPE_REF ) buf->s.flags = HB_FRAME_REF; + + parse_nalus(task->bs->Data + task->bs->DataOffset,task->bs->DataLength, buf, pv->frames_out); + + if ( last_buf == NULL ) + *buf_out = buf; + else + last_buf->next = buf; + last_buf = buf; + + // simple for now but check on TimeStampCalc from MSDK + int64_t duration = ((double)pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD / + (double)pv->enc_space.m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN) * 90000.; + + // start -> PTS + // renderOffset -> DTS + buf->s.start = buf->s.renderOffset = task->bs->TimeStamp; + buf->s.stop = buf->s.start + duration; + buf->s.duration = duration; + if (pv->bfrm_delay) + { + if (!pv->bfrm_workaround) + { + buf->s.renderOffset = task->bs->DecodeTimeStamp; + } + else + { + // MSDK API < 1.6 or VFR, so generate our own DTS + if ((pv->frames_out == 0) && + (hb_qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6) && + (hb_qsv_info->capabilities & HB_QSV_CAP_H264_BPYRAMID)) + { + // with B-pyramid, the delay may be more than 1 frame, + // so compute the actual delay based on the initial DTS + // provided by MSDK; also, account for rounding errors + // (e.g. 24000/1001 fps @ 90kHz -> 3753.75 ticks/frame) + pv->bfrm_delay = ((task->bs->TimeStamp - + task->bs->DecodeTimeStamp + + (duration / 2)) / duration); + pv->bfrm_delay = FFMAX(pv->bfrm_delay, 1); + pv->bfrm_delay = FFMIN(pv->bfrm_delay, BFRM_DELAY_MAX); + } + /* + * Generate VFR-compatible output DTS based on input PTS. + * + * Depends on the B-frame delay: + * + * 0: ipts0, ipts1, ipts2... + * 1: ipts0 - ipts1, ipts1 - ipts1, ipts1, ipts2... + * 2: ipts0 - ipts2, ipts1 - ipts2, ipts2 - ipts2, ipts1... + * ...and so on. + */ + if (pv->frames_out <= pv->bfrm_delay) + { + buf->s.renderOffset = (pv->init_pts[pv->frames_out] - + pv->init_pts[pv->bfrm_delay]); + } + else + { + buf->s.renderOffset = hb_qsv_pop_next_dts(pv->list_dts); + } + } + + /* + * In the MP4 container, DT(0) = STTS(0) = 0. + * + * Which gives us: + * CT(0) = CTTS(0) + STTS(0) = CTTS(0) = PTS(0) - DTS(0) + * When DTS(0) < PTS(0), we then have: + * CT(0) > 0 for video, but not audio (breaks A/V sync). + * + * This is typically solved by writing an edit list shifting + * video samples by the initial delay, PTS(0) - DTS(0). + * + * See: + * ISO/IEC 14496-12:2008(E), ISO base media file format + * - 8.6.1.2 Decoding Time to Sample Box + */ + if (w->config->h264.init_delay == 0 && buf->s.renderOffset < 0) + { + w->config->h264.init_delay = -buf->s.renderOffset; + } + } + + /* + * If we have a chapter marker pending and this frame's + * presentation time stamp is at or after the marker's time stamp, + * use this as the chapter start. + */ + if (pv->next_chapter.index && buf->s.frametype == HB_FRAME_IDR && + pv->next_chapter.start <= buf->s.start) + { + buf->s.new_chap = pv->next_chapter.index; + pv->next_chapter.index = 0; + } + + // shift for fifo + if(pv->async_depth){ + av_qsv_list_rem(qsv_encode->tasks,task); + av_qsv_list_add(qsv_encode->tasks,task); + } + + task->bs->DataLength = 0; + task->bs->DataOffset = 0; + task->bs->MaxLength = qsv_encode->p_buf_max_size; + task->stage = 0; + pv->frames_out++; + } + } + }while(is_end); + + + if(is_end){ + if( !buf && MFX_ERR_MORE_DATA == sts ) + break; + + } + else + break; + + } + + if(!is_end) + ++pv->frames_in; + + if(is_end){ + *buf_in = NULL; + if(last_buf){ + last_buf->next = in; + } + else + *buf_out = in; + return HB_WORK_DONE; + } + else{ + return HB_WORK_OK; + } +} + +int nal_find_start_code(uint8_t** pb, size_t* size){ + if ((int) *size < 4 ) + return 0; + + // find start code by MSDK , see ff_prefix_code[] + while ((4 <= *size) && + ((0 != (*pb)[0]) || + (0 != (*pb)[1]) || + (1 != (*pb)[2]) )) + { + *pb += 1; + *size -= 1; + } + + if (4 <= *size) + return (((*pb)[0] << 24) | ((*pb)[1] << 16) | ((*pb)[2] << 8) | ((*pb)[3])); + + return 0; +} + +void parse_nalus(uint8_t *nal_inits, size_t length, hb_buffer_t *buf, uint32_t frame_num){ + uint8_t *offset = nal_inits; + size_t size = length; + + if( nal_find_start_code(&offset,&size) == 0 ) + size = 0; + + while( size > 0 ){ + + uint8_t* current_nal = offset + sizeof(ff_prefix_code)-1; + uint8_t *next_offset = offset + sizeof(ff_prefix_code); + size_t next_size = size - sizeof(ff_prefix_code); + size_t current_size = next_size; + if( nal_find_start_code(&next_offset,&next_size) == 0 ){ + size = 0; + current_size += 1; + } + else{ + current_size -= next_size; + if( next_offset > 0 && *(next_offset-1) != 0 ) + current_size += 1; + } + { + char size_position[4] = {0,0,0,0}; + size_position[1] = (current_size >> 24) & 0xFF; + size_position[1] = (current_size >> 16) & 0xFF; + size_position[2] = (current_size >> 8) & 0xFF; + size_position[3] = current_size & 0xFF; + + memcpy(buf->data + buf->size,&size_position ,sizeof(size_position)); + buf->size += sizeof(size_position); + + memcpy(buf->data + buf->size,current_nal ,current_size); + buf->size += current_size; + } + + if(size){ + size = next_size; + offset = next_offset; + } + } +} diff --git a/libhb/enc_qsv.h b/libhb/enc_qsv.h new file mode 100644 index 000000000..9d27347cd --- /dev/null +++ b/libhb/enc_qsv.h @@ -0,0 +1,38 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#ifndef ENC_QSV_H +#define ENC_QSV_H + +#include "hb.h" +#include "qsv_common.h" + +int nal_find_start_code(uint8_t** pb, size_t* size); +void parse_nalus( uint8_t *nal_inits, size_t length, hb_buffer_t *buf, uint32_t frame_num); + +#endif // ENC_QSV_H diff --git a/libhb/h264_common.h b/libhb/h264_common.h new file mode 100644 index 000000000..febe1965f --- /dev/null +++ b/libhb/h264_common.h @@ -0,0 +1,17 @@ +/* h264_common.h + + Copyright (c) 2003-2012 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 + */ + +#ifndef HB_H264_COMMON_H +#define HB_H264_COMMON_H + +static const char * const hb_h264_profile_names[] = { "auto", "high", "main", "baseline", NULL, }; +static const char * const hb_h264_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, }; + +#endif //HB_H264_COMMON_H diff --git a/libhb/qsv_common.c b/libhb/qsv_common.c new file mode 100644 index 000000000..7cc0bb70c --- /dev/null +++ b/libhb/qsv_common.c @@ -0,0 +1,780 @@ +/* qsv_common.c + * + * Copyright (c) 2003-2013 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 "ports.h" +#include "common.h" +#include "hb_dict.h" +#include "qsv_common.h" +#include "h264_common.h" + +// for x264_vidformat_names etc. +#include "x264.h" + +// avoids a warning +#include "libavutil/cpu.h" +extern void ff_cpu_cpuid(int index, int *eax, int *ebx, int *ecx, int *edx); + +// make the Intel QSV information available to the UIs +hb_qsv_info_t *hb_qsv_info = NULL; + +// availability and versions +static mfxVersion qsv_hardware_version; +static mfxVersion qsv_software_version; +static mfxVersion qsv_minimum_version; +static int qsv_hardware_available = 0; +static int qsv_software_available = 0; + +// check available Intel Media SDK version against a minimum +#define HB_CHECK_MFX_VERSION(MFX_VERSION, MAJOR, MINOR) \ + (MFX_VERSION.Major == MAJOR && MFX_VERSION.Minor >= MINOR) + +int hb_qsv_available() +{ + return hb_qsv_info != NULL && (qsv_hardware_available || + qsv_software_available); +} + +int hb_qsv_info_init() +{ + static int init_done = 0; + if (init_done) + return (hb_qsv_info == NULL); + init_done = 1; + + hb_qsv_info = calloc(1, sizeof(*hb_qsv_info)); + if (hb_qsv_info == NULL) + { + hb_error("hb_qsv_info_init: alloc failure"); + return -1; + } + + mfxSession session; + qsv_minimum_version.Major = HB_QSV_MINVERSION_MAJOR; + qsv_minimum_version.Minor = HB_QSV_MINVERSION_MINOR; + + // check for software fallback + if (MFXInit(MFX_IMPL_SOFTWARE, + &qsv_minimum_version, &session) == MFX_ERR_NONE) + { + qsv_software_available = 1; + // our minimum is supported, but query the actual version + MFXQueryVersion(session, &qsv_software_version); + MFXClose(session); + } + + // check for actual hardware support + if (MFXInit(MFX_IMPL_HARDWARE_ANY|MFX_IMPL_VIA_ANY, + &qsv_minimum_version, &session) == MFX_ERR_NONE) + { + qsv_hardware_available = 1; + // our minimum is supported, but query the actual version + MFXQueryVersion(session, &qsv_hardware_version); + MFXClose(session); + } + + // check for version-specific or hardware-specific capabilities + // we only use software as a fallback, so check hardware first + if (qsv_hardware_available) + { + if (HB_CHECK_MFX_VERSION(qsv_hardware_version, 1, 6)) + { + hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_BRC; + hb_qsv_info->capabilities |= HB_QSV_CAP_MSDK_API_1_6; + } + if (hb_get_cpu_platform() == HB_CPU_PLATFORM_INTEL_HSW) + { + if (HB_CHECK_MFX_VERSION(qsv_hardware_version, 1, 7)) + { + hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_TRELLIS; + hb_qsv_info->capabilities |= HB_QSV_CAP_OPTION2_LOOKAHEAD; + } + hb_qsv_info->capabilities |= HB_QSV_CAP_H264_BPYRAMID; + } + } + else if (qsv_software_available) + { + if (HB_CHECK_MFX_VERSION(qsv_software_version, 1, 6)) + { + hb_qsv_info->capabilities |= HB_QSV_CAP_MSDK_API_1_6; + hb_qsv_info->capabilities |= HB_QSV_CAP_H264_BPYRAMID; + } + } + + // note: we pass a pointer to MFXInit but it never gets modified + // let's make sure of it just to be safe though + if (qsv_minimum_version.Major != HB_QSV_MINVERSION_MAJOR || + qsv_minimum_version.Minor != HB_QSV_MINVERSION_MINOR) + { + hb_error("hb_qsv_info_init: minimum version (%d.%d) was modified", + qsv_minimum_version.Major, + qsv_minimum_version.Minor); + } + + // success + return 0; +} + +// we don't need it beyond this point +#undef HB_CHECK_MFX_VERSION + +void hb_qsv_info_print() +{ + if (hb_qsv_info == NULL) + return; + + // is QSV available? + hb_log("Intel Quick Sync Video support: %s", + hb_qsv_available() ? "yes": "no"); + + // if we have Quick Sync Video support, also print the details + if (hb_qsv_available()) + { + if (qsv_hardware_available) + { + hb_log(" - Intel Media SDK hardware: API %d.%d (minimum: %d.%d)", + qsv_hardware_version.Major, + qsv_hardware_version.Minor, + qsv_minimum_version.Major, + qsv_minimum_version.Minor); + } + if (qsv_software_available) + { + hb_log(" - Intel Media SDK software: API %d.%d (minimum: %d.%d)", + qsv_software_version.Major, + qsv_software_version.Minor, + qsv_minimum_version.Major, + qsv_minimum_version.Minor); + } + } +} + +const char* hb_qsv_decode_get_codec_name(enum AVCodecID codec_id) +{ + switch (codec_id) + { + case AV_CODEC_ID_H264: + return "h264_qsv"; + + default: + return NULL; + } +} + +int hb_qsv_decode_is_enabled(hb_job_t *job) +{ + return ((job != NULL && job->title->qsv_decode_support && job->qsv_decode) && + (job->vcodec & HB_VCODEC_QSV_MASK)); +} + +int hb_qsv_decode_is_supported(enum AVCodecID codec_id, + enum AVPixelFormat pix_fmt) +{ + switch (codec_id) + { + case AV_CODEC_ID_H264: + return (pix_fmt == AV_PIX_FMT_YUV420P || + pix_fmt == AV_PIX_FMT_YUVJ420P); + + default: + return 0; + } +} + +int hb_qsv_codingoption_xlat(int val) +{ + switch (HB_QSV_CLIP3(-1, 2, val)) + { + case 0: + return MFX_CODINGOPTION_OFF; + case 1: + case 2: // MFX_CODINGOPTION_ADAPTIVE, reserved + return MFX_CODINGOPTION_ON; + case -1: + default: + return MFX_CODINGOPTION_UNKNOWN; + } +} + +int hb_qsv_trellisvalue_xlat(int val) +{ + switch (HB_QSV_CLIP3(-1, 3, val)) + { + case 0: + return MFX_TRELLIS_OFF; + case 1: // I-frames only + return MFX_TRELLIS_I; + case 2: // I- and P-frames + return MFX_TRELLIS_I|MFX_TRELLIS_P; + case 3: // all frames + return MFX_TRELLIS_I|MFX_TRELLIS_P|MFX_TRELLIS_B; + case -1: + default: + return MFX_TRELLIS_UNKNOWN; + } +} + +int hb_qsv_atoindex(const char* const *arr, const char *str, int *err) +{ + int i; + for (i = 0; arr[i] != NULL; i++) + { + if (!strcasecmp(arr[i], str)) + { + break; + } + } + *err = (arr[i] == NULL); + return i; +} + +// adapted from libx264 +int hb_qsv_atobool(const char *str, int *err) +{ + if (!strcasecmp(str, "1") || + !strcasecmp(str, "yes") || + !strcasecmp(str, "true")) + { + return 1; + } + if (!strcasecmp(str, "0") || + !strcasecmp(str, "no") || + !strcasecmp(str, "false")) + { + return 0; + } + *err = 1; + return 0; +} + +// adapted from libx264 +int hb_qsv_atoi(const char *str, int *err) +{ + char *end; + int v = strtol(str, &end, 0); + if (end == str || end[0] != '\0') + { + *err = 1; + } + return v; +} + +// adapted from libx264 +float hb_qsv_atof(const char *str, int *err) +{ + char *end; + float v = strtod(str, &end); + if (end == str || end[0] != '\0') + { + *err = 1; + } + return v; +} + +int hb_qsv_param_parse(hb_qsv_param_t *param, + const char *key, const char *value, int vcodec) +{ + float fvalue; + int ivalue, error = 0; + if (param == NULL) + { + return HB_QSV_PARAM_ERROR; + } + if (value == NULL || value[0] == '\0') + { + value = "true"; + } + else if (value[0] == '=') + { + value++; + } + if (key == NULL || key[0] == '\0') + { + return HB_QSV_PARAM_BAD_NAME; + } + else if (!strncasecmp(key, "no-", 3)) + { + key += 3; + value = hb_qsv_atobool(value, &error) ? "false" : "true"; + if (error) + { + return HB_QSV_PARAM_BAD_VALUE; + } + } + if (!strcasecmp(key, "target-usage") || + !strcasecmp(key, "tu")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->videoParam->mfx.TargetUsage = HB_QSV_CLIP3(MFX_TARGETUSAGE_1, + MFX_TARGETUSAGE_7, + ivalue); + } + } + else if (!strcasecmp(key, "num-ref-frame") || + !strcasecmp(key, "ref")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->videoParam->mfx.NumRefFrame = HB_QSV_CLIP3(0, 16, ivalue); + } + } + else if (!strcasecmp(key, "gop-ref-dist")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->videoParam->mfx.GopRefDist = HB_QSV_CLIP3(0, 32, ivalue); + } + } + else if (!strcasecmp(key, "gop-pic-size") || + !strcasecmp(key, "keyint")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->gop.gop_pic_size = HB_QSV_CLIP3(-1, UINT16_MAX, ivalue); + } + } + else if (!strcasecmp(key, "scenecut")) + { + ivalue = hb_qsv_atobool(value, &error); + if (!error) + { + if (!ivalue) + { + param->videoParam->mfx.GopOptFlag |= MFX_GOP_STRICT; + } + else + { + param->videoParam->mfx.GopOptFlag &= ~MFX_GOP_STRICT; + } + } + } + else if (!strcasecmp(key, "cqp-offset-i")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->rc.cqp_offsets[0] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue); + } + } + else if (!strcasecmp(key, "cqp-offset-p")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->rc.cqp_offsets[1] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue); + } + } + else if (!strcasecmp(key, "cqp-offset-b")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->rc.cqp_offsets[2] = HB_QSV_CLIP3(INT16_MIN, INT16_MAX, ivalue); + } + } + else if (!strcasecmp(key, "vbv-init")) + { + fvalue = hb_qsv_atof(value, &error); + if (!error) + { + param->rc.vbv_buffer_init = HB_QSV_CLIP3(0, UINT16_MAX, fvalue); + } + } + else if (!strcasecmp(key, "vbv-bufsize")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->rc.vbv_buffer_size = HB_QSV_CLIP3(0, UINT16_MAX, ivalue); + } + } + else if (!strcasecmp(key, "vbv-maxrate")) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->rc.vbv_max_bitrate = HB_QSV_CLIP3(0, UINT16_MAX, ivalue); + } + } + else if (!strcasecmp(key, "cabac")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = !hb_qsv_atobool(value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->codingOption.CAVLC = hb_qsv_codingoption_xlat(ivalue); + } + } + else if (!strcasecmp(key, "rate-distorsion-opt") || + !strcasecmp(key, "rdo")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atobool(value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->codingOption.RateDistortionOpt = hb_qsv_codingoption_xlat(ivalue); + } + } + else if (!strcasecmp(key, "videoformat")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atoindex(x264_vidformat_names, value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoSignalInfo.VideoFormat = ivalue; + } + } + else if (!strcasecmp(key, "fullrange")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atoindex(x264_fullrange_names, value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoSignalInfo.VideoFullRange = ivalue; + } + } + else if (!strcasecmp(key, "colorprim")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atoindex(x264_colorprim_names, value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoSignalInfo.ColourDescriptionPresent = 1; + param->videoSignalInfo.ColourPrimaries = ivalue; + } + } + else if (!strcasecmp(key, "transfer")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atoindex(x264_transfer_names, value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoSignalInfo.ColourDescriptionPresent = 1; + param->videoSignalInfo.TransferCharacteristics = ivalue; + } + } + else if (!strcasecmp(key, "colormatrix")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atoindex(x264_colmatrix_names, value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoSignalInfo.ColourDescriptionPresent = 1; + param->videoSignalInfo.MatrixCoefficients = ivalue; + } + } + else if (!strcasecmp(key, "tff") || + !strcasecmp(key, "interlaced")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atobool(value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoParam->mfx.FrameInfo.PicStruct = (ivalue ? + MFX_PICSTRUCT_FIELD_TFF : + MFX_PICSTRUCT_PROGRESSIVE); + } + } + else if (!strcasecmp(key, "bff")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atobool(value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (!error) + { + param->videoParam->mfx.FrameInfo.PicStruct = (ivalue ? + MFX_PICSTRUCT_FIELD_BFF : + MFX_PICSTRUCT_PROGRESSIVE); + } + } + else if (!strcasecmp(key, "mbbrc")) + { + if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_BRC) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->codingOption2.MBBRC = hb_qsv_codingoption_xlat(ivalue); + } + } + else + { + return HB_QSV_PARAM_UNSUPPORTED; + } + } + else if (!strcasecmp(key, "extbrc")) + { + if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_BRC) + { + ivalue = hb_qsv_atoi(value, &error); + if (!error) + { + param->codingOption2.ExtBRC = hb_qsv_codingoption_xlat(ivalue); + } + } + else + { + return HB_QSV_PARAM_UNSUPPORTED; + } + } + else if (!strcasecmp(key, "lookahead") || + !strcasecmp(key, "la")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atobool(value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD) + { + if (!error) + { + param->rc.lookahead = ivalue; + } + } + else + { + return HB_QSV_PARAM_UNSUPPORTED; + } + } + else if (!strcasecmp(key, "lookahead-depth") || + !strcasecmp(key, "la-depth")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atoi(value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_LOOKAHEAD) + { + if (!error) + { + param->codingOption2.LookAheadDepth = HB_QSV_CLIP3(10, 100, + ivalue); + } + } + else + { + return HB_QSV_PARAM_UNSUPPORTED; + } + } + else if (!strcasecmp(key, "trellis")) + { + switch (vcodec) + { + case HB_VCODEC_QSV_H264: + ivalue = hb_qsv_atoi(value, &error); + break; + default: + return HB_QSV_PARAM_UNSUPPORTED; + } + if (hb_qsv_info->capabilities & HB_QSV_CAP_OPTION2_TRELLIS) + { + if (!error) + { + param->codingOption2.Trellis = hb_qsv_trellisvalue_xlat(ivalue); + } + } + else + { + return HB_QSV_PARAM_UNSUPPORTED; + } + } + else + { + /* + * TODO: + * - slice count control + * - open-gop + * - fake-interlaced (mfxExtCodingOption.FramePicture???) + * - intra-refresh + */ + return HB_QSV_PARAM_BAD_NAME; + } + return error ? HB_QSV_PARAM_BAD_VALUE : HB_QSV_PARAM_OK; +} + +int hb_qsv_param_default(hb_qsv_param_t *param, mfxVideoParam *videoParam) +{ + if (param != NULL && videoParam != NULL) + { + // introduced in API 1.0 + memset(¶m->codingOption, 0, sizeof(mfxExtCodingOption)); + param->codingOption.Header.BufferId = MFX_EXTBUFF_CODING_OPTION; + param->codingOption.Header.BufferSz = sizeof(mfxExtCodingOption); + param->codingOption.MECostType = 0; // reserved, must be 0 + param->codingOption.MESearchType = 0; // reserved, must be 0 + param->codingOption.MVSearchWindow.x = 0; // reserved, must be 0 + param->codingOption.MVSearchWindow.y = 0; // reserved, must be 0 + param->codingOption.RefPicListReordering = 0; // reserved, must be 0 + param->codingOption.IntraPredBlockSize = 0; // reserved, must be 0 + param->codingOption.InterPredBlockSize = 0; // reserved, must be 0 + param->codingOption.MVPrecision = 0; // reserved, must be 0 + param->codingOption.EndOfSequence = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.RateDistortionOpt = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.CAVLC = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.ResetRefList = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.MaxDecFrameBuffering = 0; // unspecified + param->codingOption.AUDelimiter = MFX_CODINGOPTION_OFF; + param->codingOption.SingleSeiNalUnit = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.PicTimingSEI = MFX_CODINGOPTION_OFF; + param->codingOption.VuiNalHrdParameters = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.FramePicture = MFX_CODINGOPTION_UNKNOWN; + // introduced in API 1.3 + param->codingOption.RefPicMarkRep = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.FieldOutput = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.NalHrdConformance = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.SingleSeiNalUnit = MFX_CODINGOPTION_UNKNOWN; + param->codingOption.VuiVclHrdParameters = MFX_CODINGOPTION_UNKNOWN; + // introduced in API 1.4 + param->codingOption.ViewOutput = MFX_CODINGOPTION_UNKNOWN; + // introduced in API 1.6 + param->codingOption.RecoveryPointSEI = MFX_CODINGOPTION_UNKNOWN; + + // introduced in API 1.3 + memset(¶m->videoSignalInfo, 0, sizeof(mfxExtVideoSignalInfo)); + param->videoSignalInfo.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO; + param->videoSignalInfo.Header.BufferSz = sizeof(mfxExtVideoSignalInfo); + param->videoSignalInfo.VideoFormat = 5; // undefined + param->videoSignalInfo.VideoFullRange = 0; // TV range + param->videoSignalInfo.ColourDescriptionPresent = 0; // don't write to bitstream + param->videoSignalInfo.ColourPrimaries = 2; // undefined + param->videoSignalInfo.TransferCharacteristics = 2; // undefined + param->videoSignalInfo.MatrixCoefficients = 2; // undefined + + // introduced in API 1.6 + memset(¶m->codingOption2, 0, sizeof(mfxExtCodingOption2)); + param->codingOption2.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2; + param->codingOption2.Header.BufferSz = sizeof(mfxExtCodingOption2); + param->codingOption2.IntRefType = 0; + param->codingOption2.IntRefCycleSize = 2; + param->codingOption2.IntRefQPDelta = 0; + param->codingOption2.MaxFrameSize = 0; + param->codingOption2.BitrateLimit = MFX_CODINGOPTION_ON; + param->codingOption2.ExtBRC = MFX_CODINGOPTION_OFF; + param->codingOption2.MBBRC = MFX_CODINGOPTION_UNKNOWN; + // introduced in API 1.7 + param->codingOption2.LookAheadDepth = 40; + param->codingOption2.Trellis = MFX_TRELLIS_UNKNOWN; + + // GOP & rate control + param->gop.gop_pic_size = -1; // set automatically + param->gop.int_ref_cycle_size = -1; // set automatically + param->rc.lookahead = -1; // set automatically + param->rc.cqp_offsets[0] = 0; + param->rc.cqp_offsets[1] = 2; + param->rc.cqp_offsets[2] = 4; + param->rc.vbv_max_bitrate = 0; + param->rc.vbv_buffer_size = 0; + param->rc.vbv_buffer_init = .5; + + // introduced in API 1.0 + memset(videoParam, 0, sizeof(mfxVideoParam)); + param->videoParam = videoParam; + param->videoParam->Protected = 0; // reserved, must be 0 + param->videoParam->NumExtParam = 0; + param->videoParam->IOPattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY; + param->videoParam->mfx.TargetUsage = MFX_TARGETUSAGE_2; + param->videoParam->mfx.GopOptFlag = MFX_GOP_CLOSED; + param->videoParam->mfx.NumThread = 0; // deprecated, must be 0 + param->videoParam->mfx.EncodedOrder = 0; // input is in display order + param->videoParam->mfx.IdrInterval = 0; // all I-frames are IDR + param->videoParam->mfx.NumSlice = 0; // use Media SDK default + param->videoParam->mfx.NumRefFrame = 0; // use Media SDK default + param->videoParam->mfx.GopPicSize = 0; // use Media SDK default + param->videoParam->mfx.GopRefDist = 4; // power of 2, >= 4: B-pyramid + // introduced in API 1.1 + param->videoParam->AsyncDepth = AV_QSV_ASYNC_DEPTH_DEFAULT; + // introduced in API 1.3 + param->videoParam->mfx.BRCParamMultiplier = 0; // no multiplier + + // FrameInfo: set by video encoder, except PicStruct + param->videoParam->mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; + + // attach supported mfxExtBuffer structures to the mfxVideoParam + param->videoParam->NumExtParam = 0; + param->videoParam->ExtParam = param->ExtParamArray; + param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)¶m->codingOption; + param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)¶m->videoSignalInfo; + if (hb_qsv_info->capabilities & HB_QSV_CAP_MSDK_API_1_6) + { + param->videoParam->ExtParam[param->videoParam->NumExtParam++] = (mfxExtBuffer*)¶m->codingOption2; + } + } + else + { + hb_error("hb_qsv_param_default: invalid pointer(s)"); + return -1; + } + return 0; +} diff --git a/libhb/qsv_common.h b/libhb/qsv_common.h new file mode 100644 index 000000000..cd85d33a2 --- /dev/null +++ b/libhb/qsv_common.h @@ -0,0 +1,119 @@ +/* qsv_common.h + * + * Copyright (c) 2003-2013 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 + */ + +#ifndef HB_QSV_COMMON_H +#define HB_QSV_COMMON_H + +#include "msdk/mfxvideo.h" +#include "libavcodec/avcodec.h" + +/* Minimum Intel Media SDK version (currently 1.3, for Sandy Bridge support) */ +#define HB_QSV_MINVERSION_MAJOR AV_QSV_MSDK_VERSION_MAJOR +#define HB_QSV_MINVERSION_MINOR AV_QSV_MSDK_VERSION_MINOR + +/* + * Get & store all available Intel Quick Sync information: + * + * - general availability + * - available implementations (hardware-accelerated, software fallback, etc.) + * - available codecs, filters, etc. for direct access (convenience) + * - supported API version + * - supported resolutions + */ +typedef struct hb_qsv_info_s +{ + // supported version-specific or hardware-specific capabilities + int capabilities; +#define HB_QSV_CAP_H264_BPYRAMID (1 << 0) // H.264: reference B-frames +#define HB_QSV_CAP_MSDK_API_1_6 (1 << 1) // Support for API 1.6 or later +#define HB_QSV_CAP_OPTION2_BRC (1 << 2) // mfxExtCodingOption2: MBBRC/ExtBRC +#define HB_QSV_CAP_OPTION2_LOOKAHEAD (1 << 3) // mfxExtCodingOption2: LookAhead +#define HB_QSV_CAP_OPTION2_TRELLIS (1 << 4) // mfxExtCodingOption2: Trellis + + // TODO: add available decoders, filters, encoders, + // maximum decode and encode resolution, etc. +} hb_qsv_info_t; + +/* Global Intel QSV information for use by the UIs */ +extern hb_qsv_info_t *hb_qsv_info; + +/* Intel Quick Sync Video utilities */ +int hb_qsv_available(); +int hb_qsv_info_init(); +void hb_qsv_info_print(); + +/* Intel Quick Sync Video DECODE utilities */ +const char* hb_qsv_decode_get_codec_name(enum AVCodecID codec_id); +int hb_qsv_decode_is_enabled(hb_job_t *job); +int hb_qsv_decode_is_supported(enum AVCodecID codec_id, enum AVPixelFormat pix_fmt); + +/* Media SDK parameters handling */ +enum +{ + HB_QSV_PARAM_OK, + HB_QSV_PARAM_ERROR, + HB_QSV_PARAM_BAD_NAME, + HB_QSV_PARAM_BAD_VALUE, + HB_QSV_PARAM_UNSUPPORTED, +}; + +typedef struct +{ + /* + * Supported mfxExtBuffer.BufferId values: + * + * MFX_EXTBUFF_AVC_REFLIST_CTRL + * MFX_EXTBUFF_AVC_TEMPORAL_LAYERS + * MFX_EXTBUFF_CODING_OPTION + * MFX_EXTBUFF_CODING_OPTION_SPSPPS + * MFX_EXTBUFF_CODING_OPTION2 + * MFX_EXTBUFF_ENCODER_CAPABILITY + * MFX_EXTBUFF_ENCODER_RESET_OPTION + * MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION + * MFX_EXTBUFF_PICTURE_TIMING_SEI + * MFX_EXTBUFF_VIDEO_SIGNAL_INFO + * + * This should cover all encode-compatible extended + * buffers that can be attached to an mfxVideoParam. + */ +#define HB_QSV_ENC_NUM_EXT_PARAM_MAX 10 + mfxExtBuffer* ExtParamArray[HB_QSV_ENC_NUM_EXT_PARAM_MAX]; + mfxExtCodingOption codingOption; + mfxExtCodingOption2 codingOption2; + mfxExtVideoSignalInfo videoSignalInfo; + struct + { + int gop_pic_size; + int int_ref_cycle_size; + } gop; + struct + { + int lookahead; + int cqp_offsets[3]; + int vbv_max_bitrate; + int vbv_buffer_size; + float vbv_buffer_init; + } rc; + + // assigned via hb_qsv_param_default, may be shared with another structure + mfxVideoParam *videoParam; +} hb_qsv_param_t; + +#define HB_QSV_CLIP3(min, max, val) ((val < min) ? min : (val > max) ? max : val) +int hb_qsv_codingoption_xlat(int val); +int hb_qsv_trellisvalue_xlat(int val); +int hb_qsv_atoindex(const char* const *arr, const char *str, int *err); +int hb_qsv_atobool (const char *str, int *err); +int hb_qsv_atoi (const char *str, int *err); +float hb_qsv_atof (const char *str, int *err); + +int hb_qsv_param_default(hb_qsv_param_t *param, mfxVideoParam *videoParam); +int hb_qsv_param_parse (hb_qsv_param_t *param, const char *key, const char *value, int vcodec); + +#endif diff --git a/libhb/qsv_filter.c b/libhb/qsv_filter.c new file mode 100644 index 000000000..3de9d6254 --- /dev/null +++ b/libhb/qsv_filter.c @@ -0,0 +1,648 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#include "hb.h" +#include "hbffmpeg.h" +#include "libavcodec/qsv.h" +#include "qsv_filter.h" +#include "enc_qsv.h" + +struct hb_filter_private_s +{ + hb_job_t *job; + hb_list_t *list; + + int width_in; + int height_in; + int pix_fmt; + int pix_fmt_out; + int width_out; + int height_out; + int crop[4]; + int deinterlace; + int is_frc_used; + + av_qsv_space *vpp_space; + + // FRC param(s) + mfxExtVPPFrameRateConversion frc_config; +}; + +static int hb_qsv_filter_init( hb_filter_object_t * filter, + hb_filter_init_t * init ); + +static int hb_qsv_filter_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); + +static int hb_qsv_filter_info( hb_filter_object_t * filter, + hb_filter_info_t * info ); + +static void hb_qsv_filter_close( hb_filter_object_t * filter ); + +hb_filter_object_t hb_filter_qsv = +{ + .id = HB_FILTER_QSV, + .enforce_order = 1, + .name = "Quick Sync Video VPP", + .settings = NULL, + .init = hb_qsv_filter_init, + .work = hb_qsv_filter_work, + .close = hb_qsv_filter_close, + .info = hb_qsv_filter_info, +}; + +static int filter_init( av_qsv_context* qsv, hb_filter_private_t * pv ){ + mfxStatus sts; + int i=0; + + if(!qsv) return 3; + + + if(!qsv->vpp_space){ + qsv->vpp_space = av_qsv_list_init(HAVE_THREADS); + } + if(!pv->vpp_space){ + for(i=0; i<av_qsv_list_count(qsv->vpp_space);i++){ + av_qsv_space *qsv_vpp = av_qsv_list_item( qsv->vpp_space, i ); + if(qsv_vpp->type == AV_QSV_VPP_DEFAULT){ + pv->vpp_space = qsv_vpp; + break; + } + } + } + + if(!pv->vpp_space){ + pv->vpp_space = calloc( 1, sizeof( av_qsv_space )); + pv->vpp_space->type = AV_QSV_VPP_DEFAULT; + av_qsv_list_add( qsv->vpp_space, pv->vpp_space ); + } + else + if(pv->vpp_space->is_init_done ) return 1; + + if(!qsv->dec_space || !qsv->dec_space->is_init_done) return 2; + + // we need to know final output settings before we can properly configure + if (!pv->job->qsv_enc_info.is_init_done) + { + return 2; + } + + av_qsv_add_context_usage(qsv,HAVE_THREADS); + + // see params needed like at mediasdk-man.pdf:"Appendix A: Configuration Parameter Constraints" + // for now - most will take from the decode + { + av_qsv_space *qsv_vpp = pv->vpp_space; + AV_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam); + + if (pv->deinterlace) + { + /* + * Input may be progressive, interlaced or even mixed, so init with + * MFX_PICSTRUCT_UNKNOWN and use per-frame field order information + * (mfxFrameSurface1.Info.PicStruct) + */ + qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct = MFX_PICSTRUCT_UNKNOWN; + qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; + } + else + { + /* Same PicStruct in/out: no filtering */ + qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct; + qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct; + } + + // FrameRate is important for VPP to start with + if( qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN == 0 && + qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD == 0 ){ + qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN = pv->job->title->rate; + qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD = pv->job->title->rate_base; + } + + qsv_vpp->m_mfxVideoParam.vpp.In.FourCC = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC; + qsv_vpp->m_mfxVideoParam.vpp.In.ChromaFormat = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat; + qsv_vpp->m_mfxVideoParam.vpp.In.CropX = pv->crop[2]; + qsv_vpp->m_mfxVideoParam.vpp.In.CropY = pv->crop[0]; + qsv_vpp->m_mfxVideoParam.vpp.In.CropW = pv-> width_in - pv->crop[3] - pv->crop[2]; + qsv_vpp->m_mfxVideoParam.vpp.In.CropH = pv->height_in - pv->crop[1] - pv->crop[0]; + qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtN = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN; + qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtD = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD; + qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioW = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW; + qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioH = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH; + qsv_vpp->m_mfxVideoParam.vpp.In.Width = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Width; + qsv_vpp->m_mfxVideoParam.vpp.In.Height = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.Height; + + qsv_vpp->m_mfxVideoParam.vpp.Out.FourCC = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC; + qsv_vpp->m_mfxVideoParam.vpp.Out.ChromaFormat = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropX = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropX; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropY = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropY; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropW = pv->width_out; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropH = pv->height_out; + qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN = pv->job->vrate; + qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD = pv->job->vrate_base; + qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioW = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW; + qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioH = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH; + qsv_vpp->m_mfxVideoParam.vpp.Out.Width = pv->job->qsv_enc_info.align_width; + qsv_vpp->m_mfxVideoParam.vpp.Out.Height = pv->job->qsv_enc_info.align_height; + + qsv_vpp->m_mfxVideoParam.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY; + + qsv_vpp->m_mfxVideoParam.AsyncDepth = pv->job->qsv_async_depth; + + memset(&qsv_vpp->request, 0, sizeof(mfxFrameAllocRequest)*2); + + sts = MFXVideoVPP_QueryIOSurf(qsv->mfx_session, &qsv_vpp->m_mfxVideoParam, qsv_vpp->request ); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + int num_surfaces_in = qsv_vpp->request[0].NumFrameSuggested; + int num_surfaces_out = qsv_vpp->request[1].NumFrameSuggested; + + av_qsv_config *config = qsv->qsv_config; + + + qsv_vpp->surface_num = FFMIN( num_surfaces_in + num_surfaces_out + qsv_vpp->m_mfxVideoParam.AsyncDepth + config ? config->additional_buffers/2 :0 , AV_QSV_SURFACE_NUM ); + if(qsv_vpp->surface_num <= 0 ) + qsv_vpp->surface_num = AV_QSV_SURFACE_NUM; + + int i = 0; + for (i = 0; i < qsv_vpp->surface_num; i++){ + qsv_vpp->p_surfaces[i] = av_mallocz( sizeof(mfxFrameSurface1) ); + AV_QSV_CHECK_POINTER(qsv_vpp->p_surfaces[i], MFX_ERR_MEMORY_ALLOC); + memcpy(&(qsv_vpp->p_surfaces[i]->Info), &(qsv_vpp->m_mfxVideoParam.vpp.Out), sizeof(mfxFrameInfo)); + } + + qsv_vpp->sync_num = FFMIN( qsv_vpp->surface_num, AV_QSV_SYNC_NUM ); + + for (i = 0; i < qsv_vpp->sync_num; i++){ + qsv_vpp->p_syncp[i] = av_mallocz(sizeof(av_qsv_sync)); + AV_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i], MFX_ERR_MEMORY_ALLOC); + qsv_vpp->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint)); + AV_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC); + } +/* + about available VPP filters, see "Table 4 Configurable VPP filters", mediasdk-man.pdf + Hints (optional feature) IDs: + MFX_EXTBUFF_VPP_DENOISE // Remove noise + // Value of 0-100 (inclusive) indicates + // the level of noise to remove. + MFX_EXTBUFF_VPP_DETAIL // Enhance picture details/edges: + // 0-100 value (inclusive) to indicate + // the level of details to be enhanced. + MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION // Convert input frame rate to match the output, based on frame interpolation: + // MFX_FRCALGM_PRESERVE_TIMESTAMP, + // MFX_FRCALGM_DISTRIBUTED_TIMESTAMP, + // MFX_FRCALGM_FRAME_INTERPOLATION + MFX_EXTBUFF_VPP_IMAGE_STABILIZATION // Perform image stabilization + // Stabilization modes: + // MFX_IMAGESTAB_MODE_UPSCALE + // MFX_IMAGESTAB_MODE_BOXING + MFX_EXTBUFF_VPP_PICSTRUCT_DETECTION // Perform detection of picture structure: + // Detected picture structure - top field first, bottom field first, progressive or unknown + // if video processor cannot detect picture structure. + MFX_EXTBUFF_VPP_PROCAMP // Adjust the brightness, contrast, saturation, and hue settings + + // Initialize extended buffer for frame processing + // - Process amplifier (ProcAmp) used to control brightness + // - mfxExtVPPDoUse: Define the processing algorithm to be used + // - mfxExtVPPProcAmp: ProcAmp configuration + // - mfxExtBuffer: Add extended buffers to VPP parameter configuration + mfxExtVPPDoUse extDoUse; + mfxU32 tabDoUseAlg[1]; + extDoUse.Header.BufferId = MFX_EXTBUFF_VPP_DOUSE; + extDoUse.Header.BufferSz = sizeof(mfxExtVPPDoUse); + extDoUse.NumAlg = 1; + extDoUse.AlgList = tabDoUseAlg; + tabDoUseAlg[0] = MFX_EXTBUFF_VPP_PROCAMP; + + mfxExtVPPProcAmp procampConfig; + procampConfig.Header.BufferId = MFX_EXTBUFF_VPP_PROCAMP; + procampConfig.Header.BufferSz = sizeof(mfxExtVPPProcAmp); + procampConfig.Hue = 0.0f; // Default + procampConfig.Saturation = 1.0f; // Default + procampConfig.Contrast = 1.0; // Default + procampConfig.Brightness = 40.0; // Adjust brightness + + mfxExtBuffer* ExtBuffer[2]; + ExtBuffer[0] = (mfxExtBuffer*)&extDoUse; + ExtBuffer[1] = (mfxExtBuffer*)&procampConfig; + VPPParams.NumExtParam = 2; + VPPParams.ExtParam = (mfxExtBuffer**)&ExtBuffer[0]; +*/ + memset(&qsv_vpp->ext_opaque_alloc, 0, sizeof(qsv_vpp->ext_opaque_alloc)); + + if( (qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtN / qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtD ) != + (qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN / qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD) ) + { + pv->is_frc_used = 1; + } + + qsv_vpp->m_mfxVideoParam.NumExtParam = qsv_vpp->p_ext_param_num = 1 + pv->is_frc_used; + + qsv_vpp->p_ext_params = av_mallocz(sizeof(mfxExtBuffer *)*qsv_vpp->p_ext_param_num); + AV_QSV_CHECK_POINTER(qsv_vpp->p_ext_params, MFX_ERR_MEMORY_ALLOC); + + qsv_vpp->m_mfxVideoParam.ExtParam = qsv_vpp->p_ext_params; + + qsv_vpp->ext_opaque_alloc.In.Surfaces = qsv->dec_space->p_surfaces; + qsv_vpp->ext_opaque_alloc.In.NumSurface = qsv->dec_space->surface_num; + qsv_vpp->ext_opaque_alloc.In.Type = qsv->dec_space->request[0].Type; + + qsv_vpp->ext_opaque_alloc.Out.Surfaces = qsv_vpp->p_surfaces; + qsv_vpp->ext_opaque_alloc.Out.NumSurface = qsv_vpp->surface_num; + qsv_vpp->ext_opaque_alloc.Out.Type = qsv->dec_space->request[0].Type; + + qsv_vpp->ext_opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; + qsv_vpp->ext_opaque_alloc.Header.BufferSz = sizeof(mfxExtOpaqueSurfaceAlloc); + qsv_vpp->p_ext_params[0] = (mfxExtBuffer*)&qsv_vpp->ext_opaque_alloc; + + if(pv->is_frc_used) + { + pv->frc_config.Header.BufferId = MFX_EXTBUFF_VPP_FRAME_RATE_CONVERSION; + pv->frc_config.Header.BufferSz = sizeof(mfxExtVPPFrameRateConversion); + pv->frc_config.Algorithm = MFX_FRCALGM_PRESERVE_TIMESTAMP; + + qsv_vpp->p_ext_params[1] = (mfxExtBuffer*)&pv->frc_config; + } + + sts = MFXVideoVPP_Init(qsv->mfx_session, &qsv_vpp->m_mfxVideoParam); + + AV_QSV_IGNORE_MFX_STS(sts, MFX_WRN_PARTIAL_ACCELERATION); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + qsv_vpp->is_init_done = 1; + } + return 0; +} + +static int hb_qsv_filter_init( hb_filter_object_t * filter, + hb_filter_init_t * init ) +{ + + filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); + hb_filter_private_t * pv = filter->private_data; + + pv->list = hb_list_init(); + // list of init params provided at work.c:~700 + pv->width_in = init->width; + pv->height_in = init->height; + pv->width_out = init->width; + pv->height_out = init->height; + memcpy( pv->crop, init->crop, sizeof( int[4] ) ); + + if (filter->settings != NULL) + { + sscanf(filter->settings, "%d:%d:%d:%d:%d:%d_dei:%d", + &pv->width_out, &pv->height_out, + &pv->crop[0], &pv->crop[1], &pv->crop[2], &pv->crop[3], + &pv->deinterlace); + } + + pv->job = init->job; + + // will be later as more params will be known + // filter_init(pv->job->qsv, pv); + + // just passing + init->vrate = init->vrate; + init->vrate_base = init->vrate_base; + + // framerate shaping not yet supported + init->cfr = 0; + + init->pix_fmt = pv->pix_fmt; + init->width = pv->width_out; + init->height = pv->height_out; + memcpy( init->crop, pv->crop, sizeof( int[4] ) ); + + return 0; +} + +static int hb_qsv_filter_info( hb_filter_object_t * filter, + hb_filter_info_t * info ) +{ + + hb_filter_private_t *pv = filter->private_data; + if (pv == NULL) + return -1; + + sprintf(info->human_readable_desc, + "source: %d * %d, crop (%d/%d/%d/%d): %d * %d, scale: %d * %d", + pv->width_in, pv->height_in, + pv->crop[0], pv->crop[1], pv->crop[2], pv->crop[3], + pv->width_in - pv->crop[2] - pv->crop[3], + pv->height_in - pv->crop[0] - pv->crop[1], + pv->width_out, pv->height_out); + + if (pv->deinterlace) + { + sprintf(info->human_readable_desc + strlen(info->human_readable_desc), + ", deinterlace"); + } + + return 0; +} + +void qsv_filter_close( av_qsv_context* qsv, AV_QSV_STAGE_TYPE vpp_type ){ + int i = 0; + av_qsv_space* vpp_space = 0; + + if(qsv && qsv->is_context_active && qsv->vpp_space) + for(i=av_qsv_list_count( qsv->vpp_space);i>0;i--){ + + vpp_space = av_qsv_list_item( qsv->vpp_space, i-1 ); + if( vpp_space->type == vpp_type && vpp_space->is_init_done){ + + hb_log( "qsv_filter[%s] done: max_surfaces: %u/%u , max_syncs: %u/%u", ((vpp_type == AV_QSV_VPP_DEFAULT)?"Default": "User") ,vpp_space->surface_num_max_used, vpp_space->surface_num, vpp_space->sync_num_max_used, vpp_space->sync_num ); + + for (i = 0; i < vpp_space->surface_num; i++){ + av_freep(&vpp_space->p_surfaces[i]); + } + vpp_space->surface_num = 0; + + if( vpp_space->p_ext_param_num || vpp_space->p_ext_params ) + av_freep(&vpp_space->p_ext_params); + vpp_space->p_ext_param_num = 0; + + for (i = 0; i < vpp_space->sync_num; i++){ + av_freep(&vpp_space->p_syncp[i]->p_sync); + av_freep(&vpp_space->p_syncp[i]); + } + vpp_space->sync_num = 0; + + av_qsv_list_rem(qsv->vpp_space,vpp_space); + if( av_qsv_list_count(qsv->vpp_space) == 0 ) + av_qsv_list_close(&qsv->vpp_space); + + vpp_space->is_init_done = 0; + break; + } + } +} + +static void hb_qsv_filter_close( hb_filter_object_t * filter ) +{ + int i = 0; + hb_filter_private_t * pv = filter->private_data; + + if ( !pv ) + { + return; + } + + av_qsv_context* qsv = pv->job->qsv; + if(qsv && qsv->vpp_space && av_qsv_list_count(qsv->vpp_space) > 0){ + + // closing local stuff + qsv_filter_close(qsv,AV_QSV_VPP_DEFAULT); + + // closing the commong stuff + av_qsv_context_clean(qsv); + } + hb_list_close(&pv->list); + free( pv ); + filter->private_data = NULL; +} + +int process_frame(av_qsv_list* received_item, av_qsv_context* qsv, hb_filter_private_t * pv ){ + + // 1 if have results , 0 - otherwise + int ret = 1; + + mfxStatus sts = MFX_ERR_NONE; + mfxFrameSurface1 *work_surface = NULL; + av_qsv_stage* stage = 0; + + av_qsv_space *qsv_vpp = pv->vpp_space; + + if(received_item){ + stage = av_qsv_get_last_stage( received_item ); + work_surface = stage->out.p_surface; + } + + int sync_idx = av_qsv_get_free_sync(qsv_vpp, qsv); + int surface_idx = -1; + + for(;;) + { + if (sync_idx == -1) + { + hb_error("qsv: Not enough resources allocated for QSV filter"); + ret = 0; + break; + } + if( sts == MFX_ERR_MORE_SURFACE || sts == MFX_ERR_NONE ) + surface_idx = av_qsv_get_free_surface(qsv_vpp, qsv, &(qsv_vpp->m_mfxVideoParam.vpp.Out), QSV_PART_ANY); + if (surface_idx == -1) { + hb_error("qsv: Not enough resources allocated for QSV filter"); + ret = 0; + break; + } + if (work_surface) { + work_surface->Info.CropX = pv->crop[2]; + work_surface->Info.CropY = pv->crop[0]; + work_surface->Info.CropW = pv->width_in - pv->crop[3] - pv->crop[2]; + work_surface->Info.CropH = pv->height_in - pv->crop[1] - pv->crop[0]; + } + + sts = MFXVideoVPP_RunFrameVPPAsync(qsv->mfx_session, work_surface, qsv_vpp->p_surfaces[surface_idx] , NULL, qsv_vpp->p_syncp[sync_idx]->p_sync); + + if( MFX_ERR_MORE_DATA == sts ){ + if(!qsv_vpp->pending){ + qsv_vpp->pending = av_qsv_list_init(0); + } + + // if we have no results, we should not miss resource(s) + av_qsv_list_add( qsv_vpp->pending, received_item); + + ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use); + + ret = 0; + break; + } + + if( MFX_ERR_MORE_DATA == sts || (MFX_ERR_NONE <= sts && MFX_WRN_DEVICE_BUSY != sts)){ + if (work_surface){ + ff_qsv_atomic_dec(&work_surface->Data.Locked); + } + } + + if( MFX_ERR_MORE_SURFACE == sts || MFX_ERR_NONE <= sts){ + if( MFX_ERR_MORE_SURFACE == sts ) + continue; + + if (qsv_vpp->p_surfaces[surface_idx] && MFX_WRN_DEVICE_BUSY != sts ) + ff_qsv_atomic_inc(&qsv_vpp->p_surfaces[surface_idx]->Data.Locked); + } + + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + if (MFX_ERR_NONE <= sts ) // repeat the call if warning and no output + { + if (MFX_WRN_DEVICE_BUSY == sts){ + av_qsv_sleep(10); // wait if device is busy + continue; + } + + // shouldnt be a case but drain + if(stage){ + av_qsv_stage* new_stage = av_qsv_stage_init(); + + new_stage->type = AV_QSV_VPP_DEFAULT; + new_stage->in.p_surface = work_surface; + new_stage->out.p_surface = qsv_vpp->p_surfaces[surface_idx]; + new_stage->out.sync = qsv_vpp->p_syncp[sync_idx]; + av_qsv_add_stagee( &received_item, new_stage,HAVE_THREADS ); + + // add pending resources for the proper reclaim later + if( qsv_vpp->pending ){ + if( av_qsv_list_count(qsv_vpp->pending)>0 ){ + new_stage->pending = qsv_vpp->pending; + } + qsv_vpp->pending = 0; + + // making free via decrement for all pending + int i = 0; + for (i = av_qsv_list_count(new_stage->pending); i > 0; i--){ + av_qsv_list *atom_list = av_qsv_list_item(new_stage->pending, i-1); + av_qsv_stage *stage = av_qsv_get_last_stage( atom_list ); + mfxFrameSurface1 *work_surface = stage->out.p_surface; + if (work_surface) + ff_qsv_atomic_dec(&work_surface->Data.Locked); + } + } + } + break; + } + + ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use); + + if (MFX_ERR_NOT_ENOUGH_BUFFER == sts) + DEBUG_ASSERT( 1,"The bitstream buffer size is insufficient." ); + + break; + } + + return ret; +} + +static int hb_qsv_filter_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ) +{ + + hb_filter_private_t * pv = filter->private_data; + hb_buffer_t * in = *buf_in; + hb_buffer_t * out = *buf_out; + int sts = 0; + + av_qsv_context* qsv = pv->job->qsv; + + if ( !pv ) + { + *buf_out = in; + *buf_in = NULL; + return HB_FILTER_OK; + } + + while(1){ + int ret = filter_init(qsv,pv); + if(ret >= 2) + av_qsv_sleep(1); + else + break; + } + + *buf_in = NULL; + + if ( in->size <= 0 ) + { + while(1){ + sts = process_frame(in->qsv_details.qsv_atom, qsv, pv); + if(sts) + hb_list_add(pv->list,in); + else + break; + } + + hb_list_add( pv->list, in ); + *buf_out = link_buf_list( pv ); + return HB_FILTER_DONE; + } + + sts = process_frame(in->qsv_details.qsv_atom, qsv, pv); + + if(sts){ + hb_list_add(pv->list,in); + } + + if( hb_list_count(pv->list) ){ + *buf_out = hb_list_item(pv->list,0); + out = *buf_out; + if(pv->is_frc_used && out) + { + mfxStatus sts = MFX_ERR_NONE; + if(out->qsv_details.qsv_atom){ + av_qsv_stage* stage = av_qsv_get_last_stage( out->qsv_details.qsv_atom ); + mfxFrameSurface1 *work_surface = stage->out.p_surface; + + av_qsv_wait_on_sync( qsv,stage ); + + av_qsv_space *qsv_vpp = pv->vpp_space; + int64_t duration = ((double)qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD/(double)qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN ) * 90000.; + out->s.start = work_surface->Data.TimeStamp; + out->s.stop = work_surface->Data.TimeStamp + duration; + } + } + hb_list_rem(pv->list,*buf_out); + } + else + *buf_out = NULL; + + return HB_FILTER_OK; +} + +// see devavcode.c +hb_buffer_t *link_buf_list( hb_filter_private_t *pv ) +{ + hb_buffer_t *head = hb_list_item( pv->list, 0 ); + + if ( head ) + { + hb_list_rem( pv->list, head ); + hb_buffer_t *last = head, *buf; + while ( ( buf = hb_list_item( pv->list, 0 ) ) != NULL ) + { + hb_list_rem( pv->list, buf ); + last->next = buf; + last = buf; + } + } + return head; +} + diff --git a/libhb/qsv_filter.h b/libhb/qsv_filter.h new file mode 100644 index 000000000..e55a85cdf --- /dev/null +++ b/libhb/qsv_filter.h @@ -0,0 +1,35 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#ifndef QSV_FILTER_H +#define QSV_FILTER_H + +hb_buffer_t *link_buf_list( hb_filter_private_t *pv ); +void qsv_filter_close( av_qsv_context* qsv, AV_QSV_STAGE_TYPE vpp_type ); + +#endif // QSV_FILTER_H diff --git a/libhb/qsv_filter_pp.c b/libhb/qsv_filter_pp.c new file mode 100644 index 000000000..1aef1eb80 --- /dev/null +++ b/libhb/qsv_filter_pp.c @@ -0,0 +1,916 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#include "hb.h" +#include "hbffmpeg.h" +#include "libavcodec/qsv.h" +#include "qsv_filter_pp.h" +#include "qsv_filter.h" +#include "qsv_memory.h" + + +static int hb_qsv_filter_pre_init( hb_filter_object_t * filter, + hb_filter_init_t * init ); +static int hb_qsv_filter_pre_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); +static int hb_qsv_filter_pre_info( hb_filter_object_t * filter, + hb_filter_info_t * info ); +static void hb_qsv_filter_pre_close( hb_filter_object_t * filter ); + +static int hb_qsv_filter_post_init( hb_filter_object_t * filter, + hb_filter_init_t * init ); +static int hb_qsv_filter_post_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ); +static int hb_qsv_filter_post_info( hb_filter_object_t * filter, + hb_filter_info_t * info ); +static void hb_qsv_filter_post_close( hb_filter_object_t * filter ); + + +hb_filter_object_t hb_filter_qsv_pre = +{ + .id = HB_FILTER_QSV_PRE, + .enforce_order = 1, + .name = "Quick Sync Video user filter (pre)", + .settings = NULL, + .init = hb_qsv_filter_pre_init, + .work = hb_qsv_filter_pre_work, + .close = hb_qsv_filter_pre_close, + .info = hb_qsv_filter_pre_info, +}; + +hb_filter_object_t hb_filter_qsv_post = +{ + .id = HB_FILTER_QSV_POST, + .enforce_order = 1, + .name = "Quick Sync Video user filter (post)", + .settings = NULL, + .init = hb_qsv_filter_post_init, + .work = hb_qsv_filter_post_work, + .close = hb_qsv_filter_post_close, + .info = hb_qsv_filter_post_info, +}; + + +static int filter_pre_init( av_qsv_context* qsv, hb_filter_private_t * pv ){ + mfxStatus sts = MFX_ERR_NONE; + int i=0; + + if(!qsv) return 3; + + av_qsv_space *prev_vpp = 0; + + if(!qsv->vpp_space){ + qsv->vpp_space = av_qsv_list_init(HAVE_THREADS); + // note some change as : when no size changes -> no VPP used + // impact on : prev_vpp + } + + if(!pv->vpp_space){ + for(i=0; i<av_qsv_list_count(qsv->vpp_space);i++){ + av_qsv_space *qsv_vpp = av_qsv_list_item( qsv->vpp_space, i ); + if(qsv_vpp->type == AV_QSV_VPP_USER){ + pv->vpp_space = qsv_vpp; + break; + } + else + if(qsv_vpp->type == AV_QSV_VPP_DEFAULT){ + prev_vpp = qsv_vpp; + } + + } + } + + if(!pv->vpp_space){ + pv->vpp_space = calloc( 1, sizeof( av_qsv_space )); + pv->vpp_space->type = AV_QSV_VPP_USER; + av_qsv_list_add( qsv->vpp_space, pv->vpp_space ); + av_qsv_add_context_usage(qsv,HAVE_THREADS); + } + else + if(pv->vpp_space->is_init_done ) return 1; + + if(!qsv->dec_space || !qsv->dec_space->is_init_done) return 2; + + av_qsv_space *qsv_vpp = pv->vpp_space; + + AV_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam); + + + if (prev_vpp) + { + memcpy( &qsv_vpp->m_mfxVideoParam.vpp, &prev_vpp->m_mfxVideoParam.vpp, sizeof(prev_vpp->m_mfxVideoParam.vpp)); + } + else + { + AV_QSV_ZERO_MEMORY(qsv_vpp->m_mfxVideoParam); + + // FrameRate is important for VPP to start with + if( qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN == 0 && + qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD == 0 ){ + qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN = pv->job->title->rate; + qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD = pv->job->title->rate_base; + } + + qsv_vpp->m_mfxVideoParam.vpp.In.FourCC = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC; + qsv_vpp->m_mfxVideoParam.vpp.In.ChromaFormat = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat; + qsv_vpp->m_mfxVideoParam.vpp.In.CropX = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropX; + qsv_vpp->m_mfxVideoParam.vpp.In.CropY = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropY; + qsv_vpp->m_mfxVideoParam.vpp.In.CropW = pv->job->title->width; + qsv_vpp->m_mfxVideoParam.vpp.In.CropH = pv->job->title->height; + qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct; + qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtN = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN; + qsv_vpp->m_mfxVideoParam.vpp.In.FrameRateExtD = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD; + qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioW = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW; + qsv_vpp->m_mfxVideoParam.vpp.In.AspectRatioH = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH; + qsv_vpp->m_mfxVideoParam.vpp.In.Width = AV_QSV_ALIGN16(pv->job->title->width); + qsv_vpp->m_mfxVideoParam.vpp.In.Height = (MFX_PICSTRUCT_PROGRESSIVE == qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct)? + AV_QSV_ALIGN16(pv->job->title->height) : AV_QSV_ALIGN32(pv->job->title->height); + + qsv_vpp->m_mfxVideoParam.vpp.Out.FourCC = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FourCC; + qsv_vpp->m_mfxVideoParam.vpp.Out.ChromaFormat = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.ChromaFormat; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropX = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropX; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropY = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.CropY; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropW = pv->job->title->width; + qsv_vpp->m_mfxVideoParam.vpp.Out.CropH = pv->job->title->height; + qsv_vpp->m_mfxVideoParam.vpp.Out.PicStruct = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.PicStruct; + qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtN = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtN; + qsv_vpp->m_mfxVideoParam.vpp.Out.FrameRateExtD = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.FrameRateExtD; + qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioW = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioW; + qsv_vpp->m_mfxVideoParam.vpp.Out.AspectRatioH = qsv->dec_space->m_mfxVideoParam.mfx.FrameInfo.AspectRatioH; + qsv_vpp->m_mfxVideoParam.vpp.Out.Width = AV_QSV_ALIGN16(pv->job->title->width); + qsv_vpp->m_mfxVideoParam.vpp.Out.Height = (MFX_PICSTRUCT_PROGRESSIVE == qsv_vpp->m_mfxVideoParam.vpp.In.PicStruct)? + AV_QSV_ALIGN16(pv->job->title->height) : AV_QSV_ALIGN32(pv->job->title->height); + + memset(&qsv_vpp->request, 0, sizeof(mfxFrameAllocRequest)*2); + } + + qsv_vpp->m_mfxVideoParam.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY; + + qsv_vpp->surface_num = FFMIN(prev_vpp ? prev_vpp->surface_num : qsv->dec_space->surface_num/2, AV_QSV_SURFACE_NUM); + + for(i = 0; i < qsv_vpp->surface_num; i++){ + qsv_vpp->p_surfaces[i] = av_mallocz( sizeof(mfxFrameSurface1) ); + AV_QSV_CHECK_POINTER(qsv_vpp->p_surfaces[i], MFX_ERR_MEMORY_ALLOC); + memcpy(&(qsv_vpp->p_surfaces[i]->Info), &(qsv_vpp->m_mfxVideoParam.vpp.Out), sizeof(mfxFrameInfo)); + } + + qsv_vpp->sync_num = FFMIN(prev_vpp ? prev_vpp->sync_num : qsv->dec_space->sync_num, AV_QSV_SYNC_NUM); + for (i = 0; i < qsv_vpp->sync_num; i++){ + qsv_vpp->p_syncp[i] = av_mallocz(sizeof(av_qsv_sync)); + AV_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i], MFX_ERR_MEMORY_ALLOC); + qsv_vpp->p_syncp[i]->p_sync = av_mallocz(sizeof(mfxSyncPoint)); + AV_QSV_CHECK_POINTER(qsv_vpp->p_syncp[i]->p_sync, MFX_ERR_MEMORY_ALLOC); + } + + memset(&qsv_vpp->ext_opaque_alloc, 0, sizeof(mfxExtOpaqueSurfaceAlloc)); + qsv_vpp->m_mfxVideoParam.NumExtParam = qsv_vpp->p_ext_param_num = 1; + + qsv_vpp->p_ext_params = av_mallocz(sizeof(mfxExtBuffer *)*qsv_vpp->p_ext_param_num); + AV_QSV_CHECK_POINTER(qsv_vpp->p_ext_params, MFX_ERR_MEMORY_ALLOC); + + qsv_vpp->m_mfxVideoParam.ExtParam = qsv_vpp->p_ext_params; + + qsv_vpp->ext_opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; + qsv_vpp->ext_opaque_alloc.Header.BufferSz = sizeof(mfxExtOpaqueSurfaceAlloc); + qsv_vpp->p_ext_params[0] = (mfxExtBuffer*)&qsv_vpp->ext_opaque_alloc; + + if(prev_vpp){ + qsv_vpp->ext_opaque_alloc.In.Surfaces = prev_vpp->p_surfaces; + qsv_vpp->ext_opaque_alloc.In.NumSurface = prev_vpp->surface_num; + } + else{ + qsv_vpp->ext_opaque_alloc.In.Surfaces = qsv->dec_space->p_surfaces; + qsv_vpp->ext_opaque_alloc.In.NumSurface = qsv->dec_space->surface_num; + } + qsv_vpp->ext_opaque_alloc.In.Type = qsv->dec_space->request[0].Type; + + qsv_vpp->ext_opaque_alloc.Out.Surfaces = qsv_vpp->p_surfaces; + qsv_vpp->ext_opaque_alloc.Out.NumSurface = qsv_vpp->surface_num; + qsv_vpp->ext_opaque_alloc.Out.Type = qsv->dec_space->request[0].Type; + + pv->qsv_user = hb_list_init(); + + qsv_filter_t *plugin = av_mallocz( sizeof(qsv_filter_t) ); + + plugin->pv = pv; + plugin->plug.pthis = plugin; + plugin->plug.PluginInit = qsv_PluginInit; + plugin->plug.PluginClose = qsv_PluginClose; + plugin->plug.GetPluginParam = qsv_GetPluginParam; + plugin->plug.Submit = qsv_Submit; + plugin->plug.Execute = qsv_Execute; + plugin->plug.FreeResources = qsv_FreeResources; + + hb_list_add(pv->qsv_user,plugin); + + sts=MFXVideoUSER_Register(qsv->mfx_session,0,&plugin->plug); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + plugin_init(plugin,&qsv_vpp->m_mfxVideoParam); + + qsv_vpp->is_init_done = 1; + + return 0; +} + +static int hb_qsv_filter_pre_info( hb_filter_object_t * filter, + hb_filter_info_t * info ){ + hb_filter_private_t * pv = filter->private_data; + if( !pv ) + return 0; + + sprintf(info->human_readable_desc, "copy data to system memory"); + + return 0; +} +static int hb_qsv_filter_pre_init( hb_filter_object_t * filter, + hb_filter_init_t * init ){ + filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); + hb_filter_private_t * pv = filter->private_data; + pv->job = init->job; + + pv->pre.frame_go = 0; + pv->pre.frame_completed = hb_cond_init(); + pv->pre.frame_completed_lock = hb_lock_init(); + + pv->post.frame_go = 0; + pv->post.frame_completed = hb_cond_init(); + pv->post.frame_completed_lock = hb_lock_init(); + + pv->pre_busy.frame_go = 0; + pv->pre_busy.frame_completed = hb_cond_init(); + pv->pre_busy.frame_completed_lock = hb_lock_init(); + + pv->post_busy.frame_go = 0; + pv->post_busy.frame_completed = hb_cond_init(); + pv->post_busy.frame_completed_lock = hb_lock_init(); + + pv->list = hb_list_init(); + + // just to remind: + // PIX_FMT_YUV420P, ///< planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples) , 3 planes: Y, U, V + // PIX_FMT_NV12, ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) + pv->sws_context_from_nv12 = hb_sws_get_context( + pv->job->title->width, pv->job->title->height, AV_PIX_FMT_NV12, + pv->job->title->width, pv->job->title->height, AV_PIX_FMT_YUV420P, + SWS_LANCZOS|SWS_ACCURATE_RND); + pv->sws_context_to_nv12 = hb_sws_get_context( + pv->job->title->width, pv->job->title->height, AV_PIX_FMT_YUV420P, + pv->job->title->width, pv->job->title->height, AV_PIX_FMT_NV12, + SWS_LANCZOS|SWS_ACCURATE_RND); + return 0; +} +int pre_process_frame(hb_buffer_t *in, av_qsv_context* qsv, hb_filter_private_t * pv ){ + + // 1 if have results , 0 otherwise + int ret = 1; + + av_qsv_list* received_item = in->qsv_details.qsv_atom; + + mfxStatus sts = MFX_ERR_NONE; + mfxFrameSurface1 *work_surface = NULL; + av_qsv_stage* stage = 0; + + av_qsv_space *qsv_vpp = pv->vpp_space; + + if (received_item) + { + stage = av_qsv_get_last_stage( received_item ); + work_surface = stage->out.p_surface; + } + + int sync_idx = av_qsv_get_free_sync(qsv_vpp, qsv); + int surface_idx = -1; + + for (;;) + { + if (sync_idx == -1) + { + hb_error("qsv: Not enough resources allocated for the preprocessing filter"); + ret = 0; + break; + } + + if (sts == MFX_ERR_MORE_SURFACE || sts == MFX_ERR_NONE) + surface_idx = av_qsv_get_free_surface(qsv_vpp, qsv, &(qsv_vpp->m_mfxVideoParam.vpp.Out), QSV_PART_ANY); + if (surface_idx == -1) { + hb_error("qsv: Not enough resources allocated for the preprocessing filter"); + ret = 0; + break; + } + + sts = MFXVideoUSER_ProcessFrameAsync(qsv->mfx_session, &work_surface, 1, &qsv_vpp->p_surfaces[surface_idx] , 1, qsv_vpp->p_syncp[sync_idx]->p_sync); + + if (MFX_ERR_MORE_DATA == sts) + { + if (!qsv_vpp->pending) + { + qsv_vpp->pending = av_qsv_list_init(0); + } + + // if we have no results, we should not miss resource(s) + av_qsv_list_add( qsv_vpp->pending, received_item); + + ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use); + + ret = 0; + break; + } + + if( MFX_ERR_MORE_SURFACE == sts || MFX_ERR_NONE <= sts){ + if( MFX_ERR_MORE_SURFACE == sts ) + continue; + + if (qsv_vpp->p_surfaces[surface_idx] && MFX_WRN_DEVICE_BUSY != sts ) + ff_qsv_atomic_inc(&qsv_vpp->p_surfaces[surface_idx]->Data.Locked); + } + + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + if (MFX_ERR_NONE <= sts ) // repeat the call if warning and no output + { + if (MFX_WRN_DEVICE_BUSY == sts){ + hb_lock(pv->pre_busy.frame_completed_lock); + while(!pv->pre_busy.frame_go){ + hb_cond_timedwait(pv->pre_busy.frame_completed,pv->pre_busy.frame_completed_lock,1000); + if(*pv->job->die) + break; + } + pv->pre_busy.frame_go = 0; + hb_unlock(pv->pre_busy.frame_completed_lock); + + continue; + } + hb_lock(pv->pre.frame_completed_lock); + while(!pv->pre.frame_go){ + hb_cond_timedwait(pv->pre.frame_completed,pv->pre.frame_completed_lock,1000); + if(*pv->job->die) + break; + } + pv->pre.frame_go = 0; + hb_unlock(pv->pre.frame_completed_lock); + + in = pv->pre.out; + + if (work_surface){ + ff_qsv_atomic_dec(&work_surface->Data.Locked); + } + + // inserting for the future, will be locked until very ready + if(stage){ + av_qsv_stage* new_stage = av_qsv_stage_init(); + + new_stage->type = AV_QSV_VPP_USER; + new_stage->in.p_surface = work_surface; + new_stage->out.p_surface = qsv_vpp->p_surfaces[surface_idx]; + new_stage->out.sync = qsv_vpp->p_syncp[sync_idx]; + av_qsv_add_stagee( &received_item, new_stage,HAVE_THREADS ); + + // add pending resources for the proper reclaim later + if( qsv_vpp->pending ){ + if( av_qsv_list_count(qsv_vpp->pending)>0 ){ + new_stage->pending = qsv_vpp->pending; + } + qsv_vpp->pending = 0; + + // making free via decrement for all pending + int i = 0; + for (i = av_qsv_list_count(new_stage->pending); i > 0; i--){ + av_qsv_list *atom_list = av_qsv_list_item(new_stage->pending, i-1); + av_qsv_stage *stage = av_qsv_get_last_stage( atom_list ); + mfxFrameSurface1 *work_surface = stage->out.p_surface; + if (work_surface) + ff_qsv_atomic_dec(&work_surface->Data.Locked); + } + } + } + + break; + } + + ff_qsv_atomic_dec(&qsv_vpp->p_syncp[sync_idx]->in_use); + + if (MFX_ERR_NOT_ENOUGH_BUFFER == sts) + DEBUG_ASSERT( 1,"The bitstream buffer size is insufficient." ); + + break; + } + + return ret; +} + +static int hb_qsv_filter_pre_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ){ + hb_filter_private_t * pv = filter->private_data; + hb_buffer_t * in = *buf_in; + hb_buffer_t * out = *buf_out; + int sts = 0; + + av_qsv_context* qsv = pv->job->qsv; + + if(!in->qsv_details.filter_details) + in->qsv_details.filter_details = pv; + + if ( in->size <= 0 ) + { + *buf_out = in; + *buf_in = NULL; + return HB_FILTER_DONE; + } + + while(1){ + int ret = filter_pre_init(qsv,pv); + if(ret >= 2) + av_qsv_sleep(1); + else + break; + } + + pv->pre.in = in; + pv->pre.out = in; + + sts = pre_process_frame(in, qsv, pv); + + if(sts){ + hb_list_add(pv->list,out); + } + + if( hb_list_count(pv->list) ){ + *buf_out = hb_list_item(pv->list,0); + hb_list_rem(pv->list,*buf_out); + *buf_in = NULL; + } + else{ + *buf_in = NULL; + *buf_out = in; + } + + return HB_FILTER_OK; +} +static void hb_qsv_filter_pre_close( hb_filter_object_t * filter ){ + int i = 0; + mfxStatus sts = MFX_ERR_NONE; + + hb_filter_private_t * pv = filter->private_data; + + if ( !pv ) + { + return; + } + + sws_freeContext(pv->sws_context_to_nv12); + sws_freeContext(pv->sws_context_from_nv12); + + av_qsv_context* qsv = pv->job->qsv; + if(qsv && qsv->vpp_space && av_qsv_list_count(qsv->vpp_space) > 0 ){ + if(pv->qsv_user && qsv->mfx_session){ + + sts=MFXVideoUSER_Unregister(qsv->mfx_session,0); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + for(i=hb_list_count(pv->qsv_user);i>0;i--){ + qsv_filter_t *plugin = hb_list_item(pv->qsv_user,i-1); + hb_list_rem(pv->qsv_user,plugin); + plugin_close(plugin); + } + hb_list_close(&pv->qsv_user); + } + + // closing local stuff + qsv_filter_close(qsv,AV_QSV_VPP_USER); + + // closing the commong stuff + av_qsv_context_clean(qsv); + } + hb_cond_close(&pv->pre.frame_completed); + hb_lock_close(&pv->pre.frame_completed_lock); + + hb_cond_close(&pv->post.frame_completed); + hb_lock_close(&pv->post.frame_completed_lock); + + hb_cond_close(&pv->pre_busy.frame_completed); + hb_lock_close(&pv->pre_busy.frame_completed_lock); + + hb_cond_close(&pv->post_busy.frame_completed); + hb_lock_close(&pv->post_busy.frame_completed_lock); + + hb_list_close( &pv->list ); + + free( pv ); + filter->private_data = NULL; +} + + +static int hb_qsv_filter_post_info( hb_filter_object_t * filter, + hb_filter_info_t * info ){ + hb_filter_private_t * pv = filter->private_data; + if( !pv ) + return 0; + + sprintf(info->human_readable_desc, "copy data to opaque memory"); + + return 0; +} +static int hb_qsv_filter_post_init( hb_filter_object_t * filter, + hb_filter_init_t * init ){ + filter->private_data = calloc( 1, sizeof(struct hb_filter_private_s) ); + hb_filter_private_t * pv = filter->private_data; + pv->job = init->job; + return 0; +} +static int hb_qsv_filter_post_work( hb_filter_object_t * filter, + hb_buffer_t ** buf_in, + hb_buffer_t ** buf_out ){ + hb_filter_private_t * pv = filter->private_data; + hb_buffer_t * in = *buf_in; + hb_buffer_t * out = *buf_out; + + if ( in->size <= 0 ) + { + *buf_out = in; + *buf_in = NULL; + return HB_FILTER_DONE; + } + + av_qsv_context* qsv = pv->job->qsv; + pv = in->qsv_details.filter_details; + + if (!pv) + { + *buf_out = NULL; + *buf_in = NULL; + return HB_FILTER_OK; + } + + while(1){ + int ret = filter_pre_init(qsv,pv); + if(ret >= 2) + av_qsv_sleep(1); + else + break; + } + + pv->post.in = in; + pv->post.out = out; + + // signal: input is prepared, can start inserting data back into pipeline + hb_lock(pv->post.frame_completed_lock); + pv->post.frame_go = 1; + hb_cond_broadcast(pv->post.frame_completed); + hb_unlock(pv->post.frame_completed_lock); + + // wait: on signal that data is ready + hb_lock(pv->post_busy.frame_completed_lock); + while(!pv->post_busy.frame_go){ + hb_cond_timedwait(pv->post_busy.frame_completed,pv->post_busy.frame_completed_lock,1000); + if(*pv->job->die) + break; + } + pv->post_busy.frame_go = 0; + hb_unlock(pv->post_busy.frame_completed_lock); + + if (pv->post.status == HB_FILTER_OK || pv->post.status == HB_FILTER_DONE) + { + *buf_out = in; + } + else + { + *buf_out = NULL; + pv->post.status = HB_FILTER_OK; + } + *buf_in = NULL; + + return HB_FILTER_OK; +} +static void hb_qsv_filter_post_close( hb_filter_object_t * filter ){ + hb_filter_private_t * pv = filter->private_data; + + if ( !pv ) + { + return; + } + + free( pv ); + filter->private_data = NULL; +} + + +mfxStatus MFX_CDECL qsv_PluginInit(mfxHDL pthis, mfxCoreInterface *core){ + mfxStatus sts = MFX_ERR_NONE; + + if(core && pthis){ + qsv_filter_t *plugin = pthis; + plugin->core = core; + + plugin->pluginparam.MaxThreadNum = 1; + plugin->pluginparam.ThreadPolicy = MFX_THREADPOLICY_SERIAL; + } + else + sts = MFX_ERR_NULL_PTR; + + return sts; +} +mfxStatus MFX_CDECL qsv_PluginClose (mfxHDL pthis){ + mfxStatus sts = MFX_ERR_NONE; + return sts; +} +mfxStatus MFX_CDECL qsv_GetPluginParam(mfxHDL pthis, mfxPluginParam *par){ + mfxStatus sts = MFX_ERR_NONE; + + if(pthis){ + qsv_filter_t *plugin = pthis; + *par = plugin->pluginparam; + } + else + sts = MFX_ERR_NULL_PTR; + return sts; +} +mfxStatus MFX_CDECL qsv_Submit(mfxHDL pthis, const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxThreadTask *task){ + mfxStatus sts = MFX_ERR_NONE; + + qsv_filter_t *plugin = pthis; + + mfxFrameSurface1 *surface_in = (mfxFrameSurface1 *)in[0]; + mfxFrameSurface1 *surface_out = (mfxFrameSurface1 *)out[0]; + mfxFrameSurface1 *real_surface_in = surface_in; + mfxFrameSurface1 *real_surface_out = surface_out; + + sts = plugin->core->GetRealSurface(plugin->core->pthis, surface_in, &real_surface_in); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, MFX_ERR_MEMORY_ALLOC); + + sts = plugin->core->GetRealSurface(plugin->core->pthis, surface_out, &real_surface_out); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, MFX_ERR_MEMORY_ALLOC); + + int task_idx = get_free_task(plugin->tasks); + + if (task_idx == -1) + { + return MFX_WRN_DEVICE_BUSY; + } + + plugin->core->IncreaseReference(plugin->core->pthis, &(real_surface_in->Data)); + plugin->core->IncreaseReference(plugin->core->pthis, &(real_surface_out->Data)); + + // to preserve timing if other filters are used in-between + surface_out->Data.TimeStamp = surface_in->Data.TimeStamp; + surface_out->Data.FrameOrder = surface_in->Data.FrameOrder; + + qsv_filter_task_t *current_task = hb_list_item(plugin->tasks,task_idx); + current_task->in = real_surface_in; + current_task->out = real_surface_out; + current_task->busy = 1; + current_task->pv = plugin->pv; + + *task = (mfxThreadTask)current_task; + + return sts; +} +mfxStatus MFX_CDECL qsv_Execute(mfxHDL pthis, mfxThreadTask task, mfxU32 uid_p, mfxU32 uid_a){ + mfxStatus sts = MFX_ERR_NONE; + + qsv_filter_task_t *current_task = (qsv_filter_task_t *)task; + qsv_filter_t *plugin = pthis; + + sts = (current_task->processor.process)(current_task,0); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + sts = MFX_TASK_DONE; + return sts; +} +mfxStatus MFX_CDECL qsv_FreeResources(mfxHDL pthis, mfxThreadTask task, mfxStatus sts){ + + qsv_filter_t *plugin = pthis; + qsv_filter_task_t *current_task = (qsv_filter_task_t *)task; + + plugin->core->DecreaseReference(plugin->core->pthis, &(current_task->in->Data)); + plugin->core->DecreaseReference(plugin->core->pthis, &(current_task->out->Data)); + + current_task->busy = 0; + + hb_lock(plugin->pv->pre_busy.frame_completed_lock); + plugin->pv->pre_busy.frame_go = 1; + hb_cond_broadcast(plugin->pv->pre_busy.frame_completed); + hb_unlock(plugin->pv->pre_busy.frame_completed_lock); + + return MFX_ERR_NONE; +} + +mfxStatus plugin_init(qsv_filter_t* plugin, mfxVideoParam *param){ + mfxStatus sts = MFX_ERR_NONE; + + if(plugin->is_init_done) return sts; + + plugin->videoparam = param; + + mfxExtOpaqueSurfaceAlloc* plugin_opaque_alloc = NULL; + + plugin_opaque_alloc = (mfxExtOpaqueSurfaceAlloc*) get_ext_buffer(plugin->videoparam->ExtParam, + plugin->videoparam->NumExtParam, MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION); + + if(!plugin_opaque_alloc || !plugin_opaque_alloc->In.Surfaces || !plugin_opaque_alloc->Out.Surfaces) + return MFX_ERR_INVALID_VIDEO_PARAM; + + sts = plugin->core->MapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->In.NumSurface, + plugin_opaque_alloc->In.Type, plugin_opaque_alloc->In.Surfaces); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + + sts = plugin->core->MapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->Out.NumSurface, + plugin_opaque_alloc->Out.Type, plugin_opaque_alloc->Out.Surfaces); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + + plugin->tasks = hb_list_init(); + qsv_filter_task_t *task = calloc( 1, sizeof( qsv_filter_task_t )); + + task->processor.process = process_filter; + task->processor.alloc = &plugin->core->FrameAllocator; + task->processor.core = plugin->core; + + hb_list_add(plugin->tasks,task); + + plugin->is_init_done = 1; + + return sts; +} + +mfxStatus plugin_close(qsv_filter_t* plugin){ + int i = 0; + mfxStatus sts = MFX_ERR_NONE; + + if(!plugin->is_init_done) return sts; + + mfxExtOpaqueSurfaceAlloc* plugin_opaque_alloc = NULL; + + plugin_opaque_alloc = (mfxExtOpaqueSurfaceAlloc*) get_ext_buffer(plugin->videoparam->ExtParam, + plugin->videoparam->NumExtParam, MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION); + + if(!plugin_opaque_alloc || !plugin_opaque_alloc->In.Surfaces || !plugin_opaque_alloc->Out.Surfaces) + return MFX_ERR_INVALID_VIDEO_PARAM; + + sts = plugin->core->UnmapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->In.NumSurface, + plugin_opaque_alloc->In.Type, plugin_opaque_alloc->In.Surfaces); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + + sts = plugin->core->UnmapOpaqueSurface(plugin->core->pthis, plugin_opaque_alloc->Out.NumSurface, + plugin_opaque_alloc->Out.Type, plugin_opaque_alloc->Out.Surfaces); + AV_QSV_CHECK_RESULT(sts, MFX_ERR_NONE, sts); + + if(plugin->tasks){ + for(i=hb_list_count(plugin->tasks);i>0;i--){ + qsv_filter_task_t *task = hb_list_item(plugin->tasks,i-1); + hb_list_rem(plugin->tasks,task); + free(task); + } + hb_list_close(&plugin->tasks); + } + + plugin->is_init_done = 0; + + return sts; +} + +mfxExtBuffer* get_ext_buffer(mfxExtBuffer** buffers, mfxU32 buffers_num, mfxU32 buffer_id){ + int i = 0; + if(!buffers) return 0; + for(i=0;i<buffers_num;i++){ + if(!buffers[i]) continue; + if(buffers[i]->BufferId == buffer_id) + return buffers[i]; + } + return 0; +} + +int get_free_task(hb_list_t* tasks){ + int ret = -1; + int i = 0; + for(i=0;i<hb_list_count(tasks);i++){ + qsv_filter_task_t* task = hb_list_item(tasks,i); + if(!task->busy){ + ret = i; + break; + } + } + return ret; +} + +mfxStatus lock_frame(mfxFrameAllocator *alloc,mfxFrameSurface1 *surface){ + mfxStatus sts = MFX_ERR_NONE; + // prevent double lock + if (surface->Data.Y != 0 && surface->Data.MemId !=0){ + return MFX_ERR_UNSUPPORTED; + } + // not allocated, therefore no lock + if (surface->Data.Y != 0){ + return MFX_ERR_NONE; + } + sts = alloc->Lock(alloc->pthis,surface->Data.MemId,&surface->Data); + return sts; +} + +mfxStatus unlock_frame(mfxFrameAllocator *alloc,mfxFrameSurface1 *surface){ + mfxStatus sts = MFX_ERR_NONE; + // not allocated + if (surface->Data.Y != 0 && surface->Data.MemId == 0){ + return MFX_ERR_NONE; + } + // not locked + if (surface->Data.Y == 0){ + return MFX_ERR_NONE; + } + sts = alloc->Unlock(alloc->pthis,surface->Data.MemId,&surface->Data); + return sts; +} + + +int process_filter(qsv_filter_task_t* task, void* params){ + mfxStatus sts = MFX_ERR_NONE; + + if (MFX_ERR_NONE != (sts = lock_frame(task->processor.alloc,task->in)))return sts; + if (MFX_ERR_NONE != (sts = lock_frame(task->processor.alloc,task->out))) + { + unlock_frame(task->processor.alloc,task->in); + return sts; + } + + qsv_nv12_to_yuv420(task->pv->sws_context_from_nv12,task->pv->pre.out, task->in, task->processor.core); + + // signal: input is prepared, converted from pipeline into internal buffer + hb_lock(task->pv->pre.frame_completed_lock); + task->pv->pre.frame_go = 1; + hb_cond_broadcast(task->pv->pre.frame_completed); + hb_unlock(task->pv->pre.frame_completed_lock); + + // wait: input is prepared, converted from pipeline into internal buffer + hb_lock(task->pv->post.frame_completed_lock); + while(!task->pv->post.frame_go){ + hb_cond_timedwait(task->pv->post.frame_completed,task->pv->post.frame_completed_lock,1000); + if(*task->pv->job->die) + break; + } + task->pv->post.frame_go = 0; + hb_unlock(task->pv->post.frame_completed_lock); + +// this is just a simple fun/test case +#if 0 + { + int i = 0; + char *cur_line; + char* luma = task->pv->post.in->plane[0].data; + int pitch = task->pv->post.in->plane[0].stride; + int h = task->pv->post.in->plane[0].height; + int w = task->pv->post.in->plane[0].width; + for (i = 0; i < h; i++){ + + cur_line = luma + i * pitch; + if(i>h/4 && i < 3*h/4 && i % 5 == 0 ) + memset(cur_line, 0 , w ); + } + } +#endif + + if(task->pv->post.in) + { + qsv_yuv420_to_nv12(task->pv->sws_context_to_nv12, task->out, task->pv->post.in); + } + + // signal: output is prepared, converted from internal buffer into pipeline + hb_lock(task->pv->post_busy.frame_completed_lock); + task->pv->post_busy.frame_go = 1; + hb_cond_broadcast(task->pv->post_busy.frame_completed); + hb_unlock(task->pv->post_busy.frame_completed_lock); + + unlock_frame(task->processor.alloc,task->in); + unlock_frame(task->processor.alloc,task->out); + + return sts; +} diff --git a/libhb/qsv_filter_pp.h b/libhb/qsv_filter_pp.h new file mode 100644 index 000000000..e70370321 --- /dev/null +++ b/libhb/qsv_filter_pp.h @@ -0,0 +1,114 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#ifndef QSV_FILTER_PP_H +#define QSV_FILTER_PP_H + +#include "msdk/mfxplugin.h" + +extern hb_buffer_t *link_buf_list( hb_filter_private_t *pv ); + +struct qsv_filter_task_s; + +typedef struct{ + mfxFrameAllocator *alloc; + mfxStatus (*process)(struct qsv_filter_task_s*,void*); + mfxCoreInterface *core; +}qsv_filter_processor_t; + +typedef struct qsv_filter_task_s{ + mfxFrameSurface1 *in; + mfxFrameSurface1 *out; + int busy; + hb_filter_private_t *pv; + qsv_filter_processor_t processor; +} qsv_filter_task_t; + +typedef struct qsv_filter_private_s{ + + int is_init_done; + + mfxCoreInterface *core; + mfxVideoParam *videoparam; + mfxPluginParam pluginparam; + + hb_filter_private_t *pv; + + mfxPlugin plug; + hb_list_t *tasks; +} qsv_filter_t; + +typedef struct hb_qsv_sync_s{ + int frame_go; + int status; + hb_cond_t *frame_completed; + hb_lock_t *frame_completed_lock; + + hb_buffer_t *in; + hb_buffer_t *out; +} hb_qsv_sync_t; + +typedef struct hb_filter_private_s +{ + hb_job_t *job; + hb_list_t *list; + + hb_qsv_sync_t pre; + hb_qsv_sync_t pre_busy; + + hb_qsv_sync_t post; + hb_qsv_sync_t post_busy; + + av_qsv_space *vpp_space; + hb_list_t *qsv_user; + + struct SwsContext* sws_context_to_nv12; + struct SwsContext* sws_context_from_nv12; +} hb_filter_private_t_qsv; + +// methods to be called by Media SDK +mfxStatus MFX_CDECL qsv_PluginInit(mfxHDL pthis, mfxCoreInterface *core); +mfxStatus MFX_CDECL qsv_PluginClose (mfxHDL pthis); +mfxStatus MFX_CDECL qsv_GetPluginParam(mfxHDL pthis, mfxPluginParam *par); +mfxStatus MFX_CDECL qsv_Submit(mfxHDL pthis, const mfxHDL *in, mfxU32 in_num, const mfxHDL *out, mfxU32 out_num, mfxThreadTask *task); +mfxStatus MFX_CDECL qsv_Execute(mfxHDL pthis, mfxThreadTask task, mfxU32 uid_p, mfxU32 uid_a); +mfxStatus MFX_CDECL qsv_FreeResources(mfxHDL pthis, mfxThreadTask task, mfxStatus sts); + +// methods to be called by us +mfxStatus plugin_init(qsv_filter_t*,mfxVideoParam*); +mfxStatus plugin_close(qsv_filter_t*); + +//internal functions +mfxExtBuffer* get_ext_buffer(mfxExtBuffer**, mfxU32, mfxU32); +int get_free_task(hb_list_t*); +mfxStatus process_filter(qsv_filter_task_t*,void*); +mfxStatus lock_frame(mfxFrameAllocator *,mfxFrameSurface1*); +mfxStatus unlock_frame(mfxFrameAllocator *,mfxFrameSurface1*); + + +#endif //QSV_FILTER_PP_H diff --git a/libhb/qsv_memory.c b/libhb/qsv_memory.c new file mode 100644 index 000000000..f04c77a76 --- /dev/null +++ b/libhb/qsv_memory.c @@ -0,0 +1,120 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#include "hb.h" +#include "hbffmpeg.h" +#include "qsv_memory.h" + +int qsv_nv12_to_yuv420(struct SwsContext* sws_context,hb_buffer_t* dst, mfxFrameSurface1* src, mfxCoreInterface *core){ + int ret = 0; + int i,j; + + int in_pitch = src->Data.Pitch; + int w = AV_QSV_ALIGN16(src->Info.Width); + int h = (MFX_PICSTRUCT_PROGRESSIVE == src->Info.PicStruct) ? AV_QSV_ALIGN16(src->Info.Height) : AV_QSV_ALIGN32(src->Info.Height); + uint8_t *in_luma = 0; + uint8_t *in_chroma = 0; + static int copyframe_in_use = 1; + + + mfxStatus sts = MFX_ERR_NONE; + mfxFrameSurface1 accel_dst; + + if (copyframe_in_use) + { + accel_dst.Info.FourCC = src->Info.FourCC; + accel_dst.Info.CropH = src->Info.CropH; + accel_dst.Info.CropW = src->Info.CropW; + accel_dst.Info.CropY = src->Info.CropY; + accel_dst.Info.CropX = src->Info.CropX; + accel_dst.Info.Width = w; + accel_dst.Info.Height = h; + accel_dst.Data.Pitch = src->Data.Pitch; + accel_dst.Data.Y = calloc( 1, in_pitch*h ); + accel_dst.Data.VU = calloc( 1, in_pitch*h/2 ); + + sts = core->CopyFrame(core->pthis, &accel_dst, src); + + if (sts < MFX_ERR_NONE) + { + free(accel_dst.Data.Y); + free(accel_dst.Data.VU); + copyframe_in_use = 0; + } + else + { + in_luma = accel_dst.Data.Y + accel_dst.Info.CropY * in_pitch + accel_dst.Info.CropX; + in_chroma = accel_dst.Data.VU + accel_dst.Info.CropY / 2 * in_pitch + accel_dst.Info.CropX; + } + } + + if (!copyframe_in_use) + { + in_luma = src->Data.Y + src->Info.CropY * in_pitch + src->Info.CropX; + in_chroma = src->Data.VU + src->Info.CropY / 2 * in_pitch + src->Info.CropX; + } + + hb_video_buffer_realloc( dst, w, h ); + + uint8_t *srcs[] = { in_luma, in_chroma }; + int srcs_stride[] = { in_pitch, in_pitch }; + + uint8_t *dsts[] = { dst->plane[0].data, dst->plane[1].data, dst->plane[2].data }; + int dsts_stride[] = { dst->plane[0].stride, dst->plane[1].stride, dst->plane[2].stride }; + + ret = sws_scale(sws_context, srcs, srcs_stride, 0, h, dsts, dsts_stride ); + + if (copyframe_in_use) + { + free(accel_dst.Data.Y); + free(accel_dst.Data.VU); + } + + return ret; +} + +int qsv_yuv420_to_nv12(struct SwsContext* sws_context,mfxFrameSurface1* dst, hb_buffer_t* src){ + int ret = 0; + + int w = src->plane[0].width; + int h = src->plane[0].height; + + int out_pitch = dst->Data.Pitch; + uint8_t *out_luma = dst->Data.Y; + uint8_t *out_chroma = dst->Data.VU; + + uint8_t *srcs[] = { src->plane[0].data, src->plane[1].data, src->plane[2].data }; + int srcs_stride[] = { src->plane[0].stride, src->plane[1].stride, src->plane[2].stride }; + + uint8_t *dsts[] = { out_luma, out_chroma }; + int dsts_stride[] = { out_pitch, out_pitch }; + + ret = sws_scale(sws_context, srcs, srcs_stride, 0, h, dsts, dsts_stride ); + + return ret; +} diff --git a/libhb/qsv_memory.h b/libhb/qsv_memory.h new file mode 100644 index 000000000..2d0f51208 --- /dev/null +++ b/libhb/qsv_memory.h @@ -0,0 +1,55 @@ +/* ********************************************************************* *\ + +Copyright (C) 2013 Intel Corporation. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +- Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. +- Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. +- Neither the name of Intel Corporation nor the names of its contributors +may be used to endorse or promote products derived from this software +without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +IN NO EVENT SHALL INTEL CORPORATION BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +\* ********************************************************************* */ + +#ifndef QSV_MEMORY_H +#define QSV_MEMORY_H + +#include "libavcodec/qsv.h" +#include "msdk/mfxplugin.h" + +typedef struct{ + + struct{ + // for "planes" , Y and VU + uint8_t *data[2]; + int strides[2]; + } qsv_data; + + struct{ + // for each plane, Y U V + uint8_t *data[3]; + int strides[3]; + } data; + int width; + int height; +} qsv_memory_copy_t; + +int qsv_nv12_to_yuv420(struct SwsContext* sws_context,hb_buffer_t* dst, mfxFrameSurface1* src,mfxCoreInterface *core); +int qsv_yuv420_to_nv12(struct SwsContext* sws_context,mfxFrameSurface1* dst, hb_buffer_t* src); + +#endif // QSV_MEMORY_H |