1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-18 03:19:31 +02:00
FFmpeg/libavcodec/qsvenc.c
Haihao Xiang a5b6e29227 lavc/qsvenc: use VBR if maxrate is not specified on Windows
Currently AVBR is disabled and VBR is the default method if maxrate is
not specified on Linux, but AVBR is the default one if maxrate is not
specified on Windows. In order to make user experience better accross
Linux and Windows, use VBR by default on Windows if maxrate is not
specified. User need to set both avbr_accuracy and avbr_convergence to
non-zero explicitly and not to specify maxrate if AVBR is expected.

In addition, AVBR works for H264 and HEVC only in the SDK.

$ ffmpeg.exe -v verbose -f lavfi -i yuvtestsrc -vf "format=nv12" -c:v
vp9_qsv -f null -
2022-09-07 14:01:06 +08:00

1956 lines
73 KiB
C

/*
* Intel MediaSDK QSV encoder utility functions
*
* copyright (c) 2013 Yukinori Yamazoe
* copyright (c) 2015 Anton Khirnov
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config_components.h"
#include <string.h>
#include <sys/types.h>
#include <mfxvideo.h>
#include "libavutil/common.h"
#include "libavutil/hwcontext.h"
#include "libavutil/hwcontext_qsv.h"
#include "libavutil/mem.h"
#include "libavutil/log.h"
#include "libavutil/time.h"
#include "libavutil/imgutils.h"
#include "libavcodec/bytestream.h"
#include "avcodec.h"
#include "internal.h"
#include "packet_internal.h"
#include "qsv.h"
#include "qsv_internal.h"
#include "qsvenc.h"
struct profile_names {
mfxU16 profile;
const char *name;
};
static const struct profile_names avc_profiles[] = {
{ MFX_PROFILE_AVC_BASELINE, "avc baseline" },
{ MFX_PROFILE_AVC_MAIN, "avc main" },
{ MFX_PROFILE_AVC_EXTENDED, "avc extended" },
{ MFX_PROFILE_AVC_HIGH, "avc high" },
{ MFX_PROFILE_AVC_HIGH_422, "avc high 422" },
{ MFX_PROFILE_AVC_CONSTRAINED_BASELINE, "avc constrained baseline" },
{ MFX_PROFILE_AVC_CONSTRAINED_HIGH, "avc constrained high" },
{ MFX_PROFILE_AVC_PROGRESSIVE_HIGH, "avc progressive high" },
};
static const struct profile_names mpeg2_profiles[] = {
{ MFX_PROFILE_MPEG2_SIMPLE, "mpeg2 simple" },
{ MFX_PROFILE_MPEG2_MAIN, "mpeg2 main" },
{ MFX_PROFILE_MPEG2_HIGH, "mpeg2 high" },
};
static const struct profile_names hevc_profiles[] = {
{ MFX_PROFILE_HEVC_MAIN, "hevc main" },
{ MFX_PROFILE_HEVC_MAIN10, "hevc main10" },
{ MFX_PROFILE_HEVC_MAINSP, "hevc mainsp" },
{ MFX_PROFILE_HEVC_REXT, "hevc rext" },
#if QSV_VERSION_ATLEAST(1, 32)
{ MFX_PROFILE_HEVC_SCC, "hevc scc" },
#endif
};
static const struct profile_names vp9_profiles[] = {
{ MFX_PROFILE_VP9_0, "vp9 0" },
{ MFX_PROFILE_VP9_1, "vp9 1" },
{ MFX_PROFILE_VP9_2, "vp9 2" },
{ MFX_PROFILE_VP9_3, "vp9 3" },
};
typedef struct QSVPacket {
AVPacket pkt;
mfxSyncPoint *sync;
mfxBitstream *bs;
} QSVPacket;
static const char *print_profile(enum AVCodecID codec_id, mfxU16 profile)
{
const struct profile_names *profiles;
int i, num_profiles;
switch (codec_id) {
case AV_CODEC_ID_H264:
profiles = avc_profiles;
num_profiles = FF_ARRAY_ELEMS(avc_profiles);
break;
case AV_CODEC_ID_MPEG2VIDEO:
profiles = mpeg2_profiles;
num_profiles = FF_ARRAY_ELEMS(mpeg2_profiles);
break;
case AV_CODEC_ID_HEVC:
profiles = hevc_profiles;
num_profiles = FF_ARRAY_ELEMS(hevc_profiles);
break;
case AV_CODEC_ID_VP9:
profiles = vp9_profiles;
num_profiles = FF_ARRAY_ELEMS(vp9_profiles);
break;
default:
return "unknown";
}
for (i = 0; i < num_profiles; i++)
if (profile == profiles[i].profile)
return profiles[i].name;
return "unknown";
}
static const struct {
mfxU16 rc_mode;
const char *name;
} rc_names[] = {
{ MFX_RATECONTROL_CBR, "CBR" },
{ MFX_RATECONTROL_VBR, "VBR" },
{ MFX_RATECONTROL_CQP, "CQP" },
#if QSV_HAVE_AVBR
{ MFX_RATECONTROL_AVBR, "AVBR" },
#endif
{ MFX_RATECONTROL_LA, "LA" },
{ MFX_RATECONTROL_ICQ, "ICQ" },
{ MFX_RATECONTROL_LA_ICQ, "LA_ICQ" },
#if QSV_HAVE_VCM
{ MFX_RATECONTROL_VCM, "VCM" },
#endif
#if !QSV_ONEVPL
{ MFX_RATECONTROL_LA_EXT, "LA_EXT" },
#endif
{ MFX_RATECONTROL_LA_HRD, "LA_HRD" },
{ MFX_RATECONTROL_QVBR, "QVBR" },
};
#define UPDATE_PARAM(a, b) \
do { \
if ((a) != (b)) { \
a = b; \
updated = 1; \
} \
} while (0) \
static const char *print_ratecontrol(mfxU16 rc_mode)
{
int i;
for (i = 0; i < FF_ARRAY_ELEMS(rc_names); i++)
if (rc_mode == rc_names[i].rc_mode)
return rc_names[i].name;
return "unknown";
}
static const char *print_threestate(mfxU16 val)
{
if (val == MFX_CODINGOPTION_ON)
return "ON";
else if (val == MFX_CODINGOPTION_OFF)
return "OFF";
return "unknown";
}
static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q,
mfxExtBuffer **coding_opts)
{
mfxInfoMFX *info = &q->param.mfx;
// co is always at index 1
mfxExtCodingOption *co = (mfxExtCodingOption*)coding_opts[1];
mfxExtCodingOption2 *co2 = NULL;
mfxExtCodingOption3 *co3 = NULL;
mfxExtHEVCTiles *exthevctiles = NULL;
const char *tmp_str = NULL;
if (q->co2_idx > 0)
co2 = (mfxExtCodingOption2*)coding_opts[q->co2_idx];
if (q->co3_idx > 0)
co3 = (mfxExtCodingOption3*)coding_opts[q->co3_idx];
if (q->exthevctiles_idx > 0)
exthevctiles = (mfxExtHEVCTiles *)coding_opts[q->exthevctiles_idx];
av_log(avctx, AV_LOG_VERBOSE, "profile: %s; level: %"PRIu16"\n",
print_profile(avctx->codec_id, info->CodecProfile), info->CodecLevel);
av_log(avctx, AV_LOG_VERBOSE,
"GopPicSize: %"PRIu16"; GopRefDist: %"PRIu16"; GopOptFlag:%s%s; IdrInterval: %"PRIu16"\n",
info->GopPicSize, info->GopRefDist,
info->GopOptFlag & MFX_GOP_CLOSED ? " closed" : "",
info->GopOptFlag & MFX_GOP_STRICT ? " strict" : "",
info->IdrInterval);
av_log(avctx, AV_LOG_VERBOSE, "TargetUsage: %"PRIu16"; RateControlMethod: %s\n",
info->TargetUsage, print_ratecontrol(info->RateControlMethod));
if (info->RateControlMethod == MFX_RATECONTROL_CBR ||
info->RateControlMethod == MFX_RATECONTROL_VBR
#if QSV_HAVE_VCM
|| info->RateControlMethod == MFX_RATECONTROL_VCM
#endif
) {
av_log(avctx, AV_LOG_VERBOSE,
"BufferSizeInKB: %"PRIu16"; InitialDelayInKB: %"PRIu16"; TargetKbps: %"PRIu16"; MaxKbps: %"PRIu16"; BRCParamMultiplier: %"PRIu16"\n",
info->BufferSizeInKB, info->InitialDelayInKB, info->TargetKbps, info->MaxKbps, info->BRCParamMultiplier);
} else if (info->RateControlMethod == MFX_RATECONTROL_CQP) {
av_log(avctx, AV_LOG_VERBOSE, "QPI: %"PRIu16"; QPP: %"PRIu16"; QPB: %"PRIu16"\n",
info->QPI, info->QPP, info->QPB);
}
#if QSV_HAVE_AVBR
else if (info->RateControlMethod == MFX_RATECONTROL_AVBR) {
av_log(avctx, AV_LOG_VERBOSE,
"TargetKbps: %"PRIu16"; Accuracy: %"PRIu16"; Convergence: %"PRIu16"; BRCParamMultiplier: %"PRIu16"\n",
info->TargetKbps, info->Accuracy, info->Convergence, info->BRCParamMultiplier);
}
#endif
else if (info->RateControlMethod == MFX_RATECONTROL_LA
|| info->RateControlMethod == MFX_RATECONTROL_LA_HRD
) {
av_log(avctx, AV_LOG_VERBOSE,
"TargetKbps: %"PRIu16"; BRCParamMultiplier: %"PRIu16"\n",
info->TargetKbps, info->BRCParamMultiplier);
} else if (info->RateControlMethod == MFX_RATECONTROL_ICQ ||
info->RateControlMethod == MFX_RATECONTROL_LA_ICQ)
av_log(avctx, AV_LOG_VERBOSE, "ICQQuality: %"PRIu16"\n", info->ICQQuality);
av_log(avctx, AV_LOG_VERBOSE, "NumSlice: %"PRIu16"; NumRefFrame: %"PRIu16"\n",
info->NumSlice, info->NumRefFrame);
av_log(avctx, AV_LOG_VERBOSE, "RateDistortionOpt: %s\n",
print_threestate(co->RateDistortionOpt));
av_log(avctx, AV_LOG_VERBOSE, "RecoveryPointSEI: %s\n", print_threestate(co->RecoveryPointSEI));
av_log(avctx, AV_LOG_VERBOSE, "VDENC: %s\n", print_threestate(info->LowPower));
if (avctx->codec_id == AV_CODEC_ID_H264) {
av_log(avctx, AV_LOG_VERBOSE, "Entropy coding: %s; MaxDecFrameBuffering: %"PRIu16"\n",
co->CAVLC == MFX_CODINGOPTION_ON ? "CAVLC" : "CABAC", co->MaxDecFrameBuffering);
av_log(avctx, AV_LOG_VERBOSE,
"NalHrdConformance: %s; SingleSeiNalUnit: %s; VuiVclHrdParameters: %s VuiNalHrdParameters: %s\n",
print_threestate(co->NalHrdConformance), print_threestate(co->SingleSeiNalUnit),
print_threestate(co->VuiVclHrdParameters), print_threestate(co->VuiNalHrdParameters));
} else if ((avctx->codec_id == AV_CODEC_ID_HEVC) && QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 28)) {
av_log(avctx, AV_LOG_VERBOSE,
"NalHrdConformance: %s; VuiNalHrdParameters: %s\n",
print_threestate(co->NalHrdConformance), print_threestate(co->VuiNalHrdParameters));
}
av_log(avctx, AV_LOG_VERBOSE, "FrameRateExtD: %"PRIu32"; FrameRateExtN: %"PRIu32" \n",
info->FrameInfo.FrameRateExtD, info->FrameInfo.FrameRateExtN);
if (co2) {
if ((info->RateControlMethod == MFX_RATECONTROL_VBR && q->extbrc && q->look_ahead_depth > 0) ||
(info->RateControlMethod == MFX_RATECONTROL_LA) ||
(info->RateControlMethod == MFX_RATECONTROL_LA_HRD) ||
(info->RateControlMethod == MFX_RATECONTROL_LA_ICQ))
av_log(avctx, AV_LOG_VERBOSE, "LookAheadDepth: %"PRIu16"\n", co2->LookAheadDepth);
av_log(avctx, AV_LOG_VERBOSE, "IntRefType: %"PRIu16"; IntRefCycleSize: %"PRIu16"; IntRefQPDelta: %"PRId16"\n",
co2->IntRefType, co2->IntRefCycleSize, co2->IntRefQPDelta);
av_log(avctx, AV_LOG_VERBOSE, "MaxFrameSize: %d; MaxSliceSize: %d\n",
co2->MaxFrameSize, co2->MaxSliceSize);
av_log(avctx, AV_LOG_VERBOSE,
"BitrateLimit: %s; MBBRC: %s; ExtBRC: %s\n",
print_threestate(co2->BitrateLimit), print_threestate(co2->MBBRC),
print_threestate(co2->ExtBRC));
if (co2->Trellis & MFX_TRELLIS_OFF) {
av_log(avctx, AV_LOG_VERBOSE, "Trellis: off\n");
} else if (!co2->Trellis) {
av_log(avctx, AV_LOG_VERBOSE, "Trellis: auto\n");
} else {
char trellis_type[4];
int i = 0;
if (co2->Trellis & MFX_TRELLIS_I) trellis_type[i++] = 'I';
if (co2->Trellis & MFX_TRELLIS_P) trellis_type[i++] = 'P';
if (co2->Trellis & MFX_TRELLIS_B) trellis_type[i++] = 'B';
trellis_type[i] = 0;
av_log(avctx, AV_LOG_VERBOSE, "Trellis: %s\n", trellis_type);
}
switch (co2->LookAheadDS) {
case MFX_LOOKAHEAD_DS_OFF: tmp_str = "off"; break;
case MFX_LOOKAHEAD_DS_2x: tmp_str = "2x"; break;
case MFX_LOOKAHEAD_DS_4x: tmp_str = "4x"; break;
default: tmp_str = "unknown"; break;
}
av_log(avctx, AV_LOG_VERBOSE,
"RepeatPPS: %s; NumMbPerSlice: %"PRIu16"; LookAheadDS: %s\n",
print_threestate(co2->RepeatPPS), co2->NumMbPerSlice, tmp_str);
switch (co2->BRefType) {
case MFX_B_REF_OFF: tmp_str = "off"; break;
case MFX_B_REF_PYRAMID: tmp_str = "pyramid"; break;
default: tmp_str = "auto"; break;
}
av_log(avctx, AV_LOG_VERBOSE,
"AdaptiveI: %s; AdaptiveB: %s; BRefType:%s\n",
print_threestate(co2->AdaptiveI), print_threestate(co2->AdaptiveB), tmp_str);
av_log(avctx, AV_LOG_VERBOSE,
"MinQPI: %"PRIu8"; MaxQPI: %"PRIu8"; MinQPP: %"PRIu8"; MaxQPP: %"PRIu8"; MinQPB: %"PRIu8"; MaxQPB: %"PRIu8"\n",
co2->MinQPI, co2->MaxQPI, co2->MinQPP, co2->MaxQPP, co2->MinQPB, co2->MaxQPB);
av_log(avctx, AV_LOG_VERBOSE, "DisableDeblockingIdc: %"PRIu32" \n", co2->DisableDeblockingIdc);
}
if (co3) {
if (info->RateControlMethod == MFX_RATECONTROL_QVBR)
av_log(avctx, AV_LOG_VERBOSE, "QVBRQuality: %"PRIu16"\n", co3->QVBRQuality);
switch (co3->PRefType) {
case MFX_P_REF_DEFAULT: av_log(avctx, AV_LOG_VERBOSE, "PRefType: default\n"); break;
case MFX_P_REF_SIMPLE: av_log(avctx, AV_LOG_VERBOSE, "PRefType: simple\n"); break;
case MFX_P_REF_PYRAMID: av_log(avctx, AV_LOG_VERBOSE, "PRefType: pyramid\n"); break;
default: av_log(avctx, AV_LOG_VERBOSE, "PRefType: unknown\n"); break;
}
if (avctx->codec_id == AV_CODEC_ID_HEVC)
av_log(avctx, AV_LOG_VERBOSE,"GPB: %s\n", print_threestate(co3->GPB));
av_log(avctx, AV_LOG_VERBOSE, "TransformSkip: %s \n", print_threestate(co3->TransformSkip));
av_log(avctx, AV_LOG_VERBOSE, "IntRefCycleDist: %"PRId16"\n", co3->IntRefCycleDist);
av_log(avctx, AV_LOG_VERBOSE, "LowDelayBRC: %s\n", print_threestate(co3->LowDelayBRC));
av_log(avctx, AV_LOG_VERBOSE, "MaxFrameSizeI: %d; ", co3->MaxFrameSizeI);
av_log(avctx, AV_LOG_VERBOSE, "MaxFrameSizeP: %d\n", co3->MaxFrameSizeP);
av_log(avctx, AV_LOG_VERBOSE, "ScenarioInfo: %"PRId16"\n", co3->ScenarioInfo);
}
if (exthevctiles) {
av_log(avctx, AV_LOG_VERBOSE, "NumTileColumns: %"PRIu16"; NumTileRows: %"PRIu16"\n",
exthevctiles->NumTileColumns, exthevctiles->NumTileRows);
}
}
static void dump_video_vp9_param(AVCodecContext *avctx, QSVEncContext *q,
mfxExtBuffer **coding_opts)
{
mfxInfoMFX *info = &q->param.mfx;
mfxExtVP9Param *vp9_param = NULL;
mfxExtCodingOption2 *co2 = NULL;
if (q->vp9_idx >= 0)
vp9_param = (mfxExtVP9Param *)coding_opts[q->vp9_idx];
if (q->co2_idx >= 0)
co2 = (mfxExtCodingOption2*)coding_opts[q->co2_idx];
av_log(avctx, AV_LOG_VERBOSE, "profile: %s \n",
print_profile(avctx->codec_id, info->CodecProfile));
av_log(avctx, AV_LOG_VERBOSE,
"GopPicSize: %"PRIu16"; GopRefDist: %"PRIu16"; GopOptFlag:%s%s; IdrInterval: %"PRIu16"\n",
info->GopPicSize, info->GopRefDist,
info->GopOptFlag & MFX_GOP_CLOSED ? " closed" : "",
info->GopOptFlag & MFX_GOP_STRICT ? " strict" : "",
info->IdrInterval);
av_log(avctx, AV_LOG_VERBOSE, "TargetUsage: %"PRIu16"; RateControlMethod: %s\n",
info->TargetUsage, print_ratecontrol(info->RateControlMethod));
if (info->RateControlMethod == MFX_RATECONTROL_CBR ||
info->RateControlMethod == MFX_RATECONTROL_VBR) {
av_log(avctx, AV_LOG_VERBOSE,
"BufferSizeInKB: %"PRIu16"; InitialDelayInKB: %"PRIu16"; TargetKbps: %"PRIu16"; MaxKbps: %"PRIu16"; BRCParamMultiplier: %"PRIu16"\n",
info->BufferSizeInKB, info->InitialDelayInKB, info->TargetKbps, info->MaxKbps, info->BRCParamMultiplier);
} else if (info->RateControlMethod == MFX_RATECONTROL_CQP) {
av_log(avctx, AV_LOG_VERBOSE, "QPI: %"PRIu16"; QPP: %"PRIu16"; QPB: %"PRIu16"\n",
info->QPI, info->QPP, info->QPB);
}
else if (info->RateControlMethod == MFX_RATECONTROL_ICQ) {
av_log(avctx, AV_LOG_VERBOSE, "ICQQuality: %"PRIu16"\n", info->ICQQuality);
}
else {
av_log(avctx, AV_LOG_VERBOSE, "Unsupported ratecontrol method: %d \n", info->RateControlMethod);
}
av_log(avctx, AV_LOG_VERBOSE, "NumRefFrame: %"PRIu16"\n", info->NumRefFrame);
av_log(avctx, AV_LOG_VERBOSE, "FrameRateExtD: %"PRIu32"; FrameRateExtN: %"PRIu32" \n",
info->FrameInfo.FrameRateExtD, info->FrameInfo.FrameRateExtN);
if (co2) {
av_log(avctx, AV_LOG_VERBOSE,
"IntRefType: %"PRIu16"; IntRefCycleSize: %"PRIu16"; IntRefQPDelta: %"PRId16"\n",
co2->IntRefType, co2->IntRefCycleSize, co2->IntRefQPDelta);
av_log(avctx, AV_LOG_VERBOSE, "MaxFrameSize: %d\n", co2->MaxFrameSize);
av_log(avctx, AV_LOG_VERBOSE,
"BitrateLimit: %s; MBBRC: %s; ExtBRC: %s\n",
print_threestate(co2->BitrateLimit), print_threestate(co2->MBBRC),
print_threestate(co2->ExtBRC));
av_log(avctx, AV_LOG_VERBOSE, "VDENC: %s\n", print_threestate(info->LowPower));
av_log(avctx, AV_LOG_VERBOSE,
"MinQPI: %"PRIu8"; MaxQPI: %"PRIu8"; MinQPP: %"PRIu8"; MaxQPP: %"PRIu8"; MinQPB: %"PRIu8"; MaxQPB: %"PRIu8"\n",
co2->MinQPI, co2->MaxQPI, co2->MinQPP, co2->MaxQPP, co2->MinQPB, co2->MaxQPB);
}
if (vp9_param) {
av_log(avctx, AV_LOG_VERBOSE, "WriteIVFHeaders: %s \n",
print_threestate(vp9_param->WriteIVFHeaders));
}
}
static void dump_video_mjpeg_param(AVCodecContext *avctx, QSVEncContext *q)
{
mfxInfoMFX *info = &q->param.mfx;
av_log(avctx, AV_LOG_VERBOSE, "Interleaved: %"PRIu16" \n", info->Interleaved);
av_log(avctx, AV_LOG_VERBOSE, "Quality: %"PRIu16" \n", info->Quality);
av_log(avctx, AV_LOG_VERBOSE, "RestartInterval: %"PRIu16" \n", info->RestartInterval);
av_log(avctx, AV_LOG_VERBOSE, "FrameRateExtD: %"PRIu32"; FrameRateExtN: %"PRIu32" \n",
info->FrameInfo.FrameRateExtD, info->FrameInfo.FrameRateExtN);
}
static int select_rc_mode(AVCodecContext *avctx, QSVEncContext *q)
{
const char *rc_desc;
mfxU16 rc_mode;
int want_la = q->look_ahead;
int want_qscale = !!(avctx->flags & AV_CODEC_FLAG_QSCALE);
int want_vcm = q->vcm;
if (want_vcm && !QSV_HAVE_VCM) {
av_log(avctx, AV_LOG_ERROR,
"VCM ratecontrol mode requested, but is not supported by this SDK version\n");
return AVERROR(ENOSYS);
}
if (want_la + want_qscale + want_vcm > 1) {
av_log(avctx, AV_LOG_ERROR,
"More than one of: { constant qscale, lookahead, VCM } requested, "
"only one of them can be used at a time.\n");
return AVERROR(EINVAL);
}
if (want_qscale) {
rc_mode = MFX_RATECONTROL_CQP;
rc_desc = "constant quantization parameter (CQP)";
}
#if QSV_HAVE_VCM
else if (want_vcm) {
rc_mode = MFX_RATECONTROL_VCM;
rc_desc = "video conferencing mode (VCM)";
}
#endif
else if (want_la) {
rc_mode = MFX_RATECONTROL_LA;
rc_desc = "VBR with lookahead (LA)";
if (avctx->global_quality > 0) {
rc_mode = MFX_RATECONTROL_LA_ICQ;
rc_desc = "intelligent constant quality with lookahead (LA_ICQ)";
}
}
else if (avctx->global_quality > 0 && !avctx->rc_max_rate) {
rc_mode = MFX_RATECONTROL_ICQ;
rc_desc = "intelligent constant quality (ICQ)";
}
else if (avctx->rc_max_rate == avctx->bit_rate) {
rc_mode = MFX_RATECONTROL_CBR;
rc_desc = "constant bitrate (CBR)";
}
#if QSV_HAVE_AVBR
else if (!avctx->rc_max_rate &&
(avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_HEVC) &&
q->avbr_accuracy &&
q->avbr_convergence) {
rc_mode = MFX_RATECONTROL_AVBR;
rc_desc = "average variable bitrate (AVBR)";
}
#endif
else if (avctx->global_quality > 0) {
rc_mode = MFX_RATECONTROL_QVBR;
rc_desc = "constant quality with VBR algorithm (QVBR)";
}
else {
rc_mode = MFX_RATECONTROL_VBR;
rc_desc = "variable bitrate (VBR)";
}
q->param.mfx.RateControlMethod = rc_mode;
av_log(avctx, AV_LOG_VERBOSE, "Using the %s ratecontrol method\n", rc_desc);
return 0;
}
static int check_enc_param(AVCodecContext *avctx, QSVEncContext *q)
{
mfxVideoParam param_out = { .mfx.CodecId = q->param.mfx.CodecId };
mfxStatus ret;
#define UNMATCH(x) (param_out.mfx.x != q->param.mfx.x)
ret = MFXVideoENCODE_Query(q->session, &q->param, &param_out);
if (ret < 0) {
if (UNMATCH(CodecId))
av_log(avctx, AV_LOG_ERROR, "Current codec type is unsupported\n");
if (UNMATCH(CodecProfile))
av_log(avctx, AV_LOG_ERROR, "Current profile is unsupported\n");
if (UNMATCH(RateControlMethod))
av_log(avctx, AV_LOG_ERROR, "Selected ratecontrol mode is unsupported\n");
if (UNMATCH(LowPower))
av_log(avctx, AV_LOG_ERROR, "Low power mode is unsupported\n");
if (UNMATCH(FrameInfo.FrameRateExtN) || UNMATCH(FrameInfo.FrameRateExtD))
av_log(avctx, AV_LOG_ERROR, "Current frame rate is unsupported\n");
if (UNMATCH(FrameInfo.PicStruct))
av_log(avctx, AV_LOG_ERROR, "Current picture structure is unsupported\n");
if (UNMATCH(FrameInfo.Width) || UNMATCH(FrameInfo.Height))
av_log(avctx, AV_LOG_ERROR, "Current resolution is unsupported\n");
if (UNMATCH(FrameInfo.FourCC))
av_log(avctx, AV_LOG_ERROR, "Current pixel format is unsupported\n");
return 0;
}
return 1;
}
static int init_video_param_jpeg(AVCodecContext *avctx, QSVEncContext *q)
{
enum AVPixelFormat sw_format = avctx->pix_fmt == AV_PIX_FMT_QSV ?
avctx->sw_pix_fmt : avctx->pix_fmt;
const AVPixFmtDescriptor *desc;
int ret;
ret = ff_qsv_codec_id_to_mfx(avctx->codec_id);
if (ret < 0)
return AVERROR_BUG;
q->param.mfx.CodecId = ret;
if (avctx->level > 0)
q->param.mfx.CodecLevel = avctx->level;
q->param.mfx.CodecProfile = q->profile;
desc = av_pix_fmt_desc_get(sw_format);
if (!desc)
return AVERROR_BUG;
ret = ff_qsv_map_pixfmt(sw_format, &q->param.mfx.FrameInfo.FourCC);
if (ret < 0)
return AVERROR_BUG;
q->param.mfx.FrameInfo.CropX = 0;
q->param.mfx.FrameInfo.CropY = 0;
q->param.mfx.FrameInfo.CropW = avctx->width;
q->param.mfx.FrameInfo.CropH = avctx->height;
q->param.mfx.FrameInfo.AspectRatioW = avctx->sample_aspect_ratio.num;
q->param.mfx.FrameInfo.AspectRatioH = avctx->sample_aspect_ratio.den;
q->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420 +
!desc->log2_chroma_w + !desc->log2_chroma_h;
q->param.mfx.FrameInfo.BitDepthLuma = desc->comp[0].depth;
q->param.mfx.FrameInfo.BitDepthChroma = desc->comp[0].depth;
q->param.mfx.FrameInfo.Shift = desc->comp[0].depth > 8;
q->param.mfx.FrameInfo.Width = FFALIGN(avctx->width, 16);
q->param.mfx.FrameInfo.Height = FFALIGN(avctx->height, 16);
if (avctx->hw_frames_ctx) {
AVHWFramesContext *frames_ctx = (AVHWFramesContext *)avctx->hw_frames_ctx->data;
AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx;
q->param.mfx.FrameInfo.Width = frames_hwctx->surfaces[0].Info.Width;
q->param.mfx.FrameInfo.Height = frames_hwctx->surfaces[0].Info.Height;
}
if (avctx->framerate.den > 0 && avctx->framerate.num > 0) {
q->param.mfx.FrameInfo.FrameRateExtN = avctx->framerate.num;
q->param.mfx.FrameInfo.FrameRateExtD = avctx->framerate.den;
} else {
q->param.mfx.FrameInfo.FrameRateExtN = avctx->time_base.den;
q->param.mfx.FrameInfo.FrameRateExtD = avctx->time_base.num;
}
q->param.mfx.Interleaved = 1;
q->param.mfx.Quality = av_clip(avctx->global_quality, 1, 100);
q->param.mfx.RestartInterval = 0;
q->width_align = 16;
q->height_align = 16;
q->param.mfx.FrameInfo.Width = FFALIGN(avctx->width, q->width_align);
q->param.mfx.FrameInfo.Height = FFALIGN(avctx->height, q->height_align);
return 0;
}
static int init_video_param(AVCodecContext *avctx, QSVEncContext *q)
{
enum AVPixelFormat sw_format = avctx->pix_fmt == AV_PIX_FMT_QSV ?
avctx->sw_pix_fmt : avctx->pix_fmt;
const AVPixFmtDescriptor *desc;
float quant;
int target_bitrate_kbps, max_bitrate_kbps, brc_param_multiplier;
int buffer_size_in_kilobytes, initial_delay_in_kilobytes;
int ret;
ret = ff_qsv_codec_id_to_mfx(avctx->codec_id);
if (ret < 0)
return AVERROR_BUG;
q->param.mfx.CodecId = ret;
if (avctx->level > 0)
q->param.mfx.CodecLevel = avctx->level;
if (avctx->compression_level == FF_COMPRESSION_DEFAULT) {
avctx->compression_level = q->preset;
} else if (avctx->compression_level >= 0) {
if (avctx->compression_level > MFX_TARGETUSAGE_BEST_SPEED) {
av_log(avctx, AV_LOG_WARNING, "Invalid compression level: "
"valid range is 0-%d, using %d instead\n",
MFX_TARGETUSAGE_BEST_SPEED, MFX_TARGETUSAGE_BEST_SPEED);
avctx->compression_level = MFX_TARGETUSAGE_BEST_SPEED;
}
}
if (q->low_power == 1) {
q->param.mfx.LowPower = MFX_CODINGOPTION_ON;
} else if (q->low_power == -1)
q->param.mfx.LowPower = MFX_CODINGOPTION_UNKNOWN;
else
q->param.mfx.LowPower = MFX_CODINGOPTION_OFF;
q->param.mfx.CodecProfile = q->profile;
q->param.mfx.TargetUsage = avctx->compression_level;
q->param.mfx.GopPicSize = FFMAX(0, avctx->gop_size);
q->param.mfx.GopRefDist = FFMAX(-1, avctx->max_b_frames) + 1;
q->param.mfx.GopOptFlag = avctx->flags & AV_CODEC_FLAG_CLOSED_GOP ?
MFX_GOP_CLOSED : MFX_GOP_STRICT;
q->param.mfx.IdrInterval = q->idr_interval;
q->param.mfx.NumSlice = avctx->slices;
q->param.mfx.NumRefFrame = FFMAX(0, avctx->refs);
q->param.mfx.EncodedOrder = 0;
q->param.mfx.BufferSizeInKB = 0;
desc = av_pix_fmt_desc_get(sw_format);
if (!desc)
return AVERROR_BUG;
ret = ff_qsv_map_pixfmt(sw_format, &q->param.mfx.FrameInfo.FourCC);
if (ret < 0)
return AVERROR_BUG;
q->param.mfx.FrameInfo.CropX = 0;
q->param.mfx.FrameInfo.CropY = 0;
q->param.mfx.FrameInfo.CropW = avctx->width;
q->param.mfx.FrameInfo.CropH = avctx->height;
q->param.mfx.FrameInfo.AspectRatioW = avctx->sample_aspect_ratio.num;
q->param.mfx.FrameInfo.AspectRatioH = avctx->sample_aspect_ratio.den;
q->param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420 +
!desc->log2_chroma_w + !desc->log2_chroma_h;
q->param.mfx.FrameInfo.BitDepthLuma = desc->comp[0].depth;
q->param.mfx.FrameInfo.BitDepthChroma = desc->comp[0].depth;
q->param.mfx.FrameInfo.Shift = desc->comp[0].depth > 8;
// If the minor version is greater than or equal to 19,
// then can use the same alignment settings as H.264 for HEVC
q->width_align = (avctx->codec_id != AV_CODEC_ID_HEVC ||
QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 19)) ? 16 : 32;
q->param.mfx.FrameInfo.Width = FFALIGN(avctx->width, q->width_align);
if (avctx->flags & AV_CODEC_FLAG_INTERLACED_DCT) {
// it is important that PicStruct be setup correctly from the
// start--otherwise, encoding doesn't work and results in a bunch
// of incompatible video parameter errors
q->param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_FIELD_TFF;
// height alignment always must be 32 for interlaced video
q->height_align = 32;
} else {
q->param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
// for progressive video, the height should be aligned to 16 for
// H.264. For HEVC, depending on the version of MFX, it should be
// either 32 or 16. The lower number is better if possible.
q->height_align = avctx->codec_id == AV_CODEC_ID_HEVC ? 32 : 16;
}
q->param.mfx.FrameInfo.Height = FFALIGN(avctx->height, q->height_align);
if (avctx->hw_frames_ctx) {
AVHWFramesContext *frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx;
q->param.mfx.FrameInfo.Width = frames_hwctx->surfaces[0].Info.Width;
q->param.mfx.FrameInfo.Height = frames_hwctx->surfaces[0].Info.Height;
}
if (avctx->framerate.den > 0 && avctx->framerate.num > 0) {
q->param.mfx.FrameInfo.FrameRateExtN = avctx->framerate.num;
q->param.mfx.FrameInfo.FrameRateExtD = avctx->framerate.den;
} else {
q->param.mfx.FrameInfo.FrameRateExtN = avctx->time_base.den;
q->param.mfx.FrameInfo.FrameRateExtD = avctx->time_base.num;
}
ret = select_rc_mode(avctx, q);
if (ret < 0)
return ret;
//libmfx BRC parameters are 16 bits thus maybe overflow, then BRCParamMultiplier is needed
buffer_size_in_kilobytes = avctx->rc_buffer_size / 8000;
initial_delay_in_kilobytes = avctx->rc_initial_buffer_occupancy / 8000;
target_bitrate_kbps = avctx->bit_rate / 1000;
max_bitrate_kbps = avctx->rc_max_rate / 1000;
brc_param_multiplier = (FFMAX(FFMAX3(target_bitrate_kbps, max_bitrate_kbps, buffer_size_in_kilobytes),
initial_delay_in_kilobytes) + 0x10000) / 0x10000;
switch (q->param.mfx.RateControlMethod) {
case MFX_RATECONTROL_CBR:
case MFX_RATECONTROL_VBR:
if (q->extbrc) {
q->extco2.LookAheadDepth = q->look_ahead_depth;
}
#if QSV_HAVE_VCM
case MFX_RATECONTROL_VCM:
#endif
case MFX_RATECONTROL_QVBR:
q->param.mfx.BufferSizeInKB = buffer_size_in_kilobytes / brc_param_multiplier;
q->param.mfx.InitialDelayInKB = initial_delay_in_kilobytes / brc_param_multiplier;
q->param.mfx.TargetKbps = target_bitrate_kbps / brc_param_multiplier;
q->param.mfx.MaxKbps = max_bitrate_kbps / brc_param_multiplier;
q->param.mfx.BRCParamMultiplier = brc_param_multiplier;
if (q->param.mfx.RateControlMethod == MFX_RATECONTROL_QVBR)
q->extco3.QVBRQuality = av_clip(avctx->global_quality, 0, 51);
break;
case MFX_RATECONTROL_CQP:
quant = avctx->global_quality / FF_QP2LAMBDA;
q->param.mfx.QPI = av_clip(quant * fabs(avctx->i_quant_factor) + avctx->i_quant_offset, 0, 51);
q->param.mfx.QPP = av_clip(quant, 0, 51);
q->param.mfx.QPB = av_clip(quant * fabs(avctx->b_quant_factor) + avctx->b_quant_offset, 0, 51);
q->old_global_quality = avctx->global_quality;
q->old_i_quant_factor = avctx->i_quant_factor;
q->old_i_quant_offset = avctx->i_quant_offset;
q->old_b_quant_factor = avctx->b_quant_factor;
q->old_b_quant_offset = avctx->b_quant_offset;
break;
#if QSV_HAVE_AVBR
case MFX_RATECONTROL_AVBR:
q->param.mfx.TargetKbps = target_bitrate_kbps / brc_param_multiplier;
q->param.mfx.Convergence = q->avbr_convergence;
q->param.mfx.Accuracy = q->avbr_accuracy;
q->param.mfx.BRCParamMultiplier = brc_param_multiplier;
break;
#endif
case MFX_RATECONTROL_LA:
q->param.mfx.TargetKbps = target_bitrate_kbps / brc_param_multiplier;
q->extco2.LookAheadDepth = q->look_ahead_depth;
q->param.mfx.BRCParamMultiplier = brc_param_multiplier;
break;
case MFX_RATECONTROL_LA_ICQ:
q->extco2.LookAheadDepth = q->look_ahead_depth;
case MFX_RATECONTROL_ICQ:
q->param.mfx.ICQQuality = av_clip(avctx->global_quality, 1, 51);
break;
}
// The HEVC encoder plugin currently fails with some old libmfx version if coding options
// are provided. Can't find the extract libmfx version which fixed it, just enable it from
// V1.28 in order to keep compatibility security.
if (((avctx->codec_id != AV_CODEC_ID_HEVC) || QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 28))
&& (avctx->codec_id != AV_CODEC_ID_VP9)) {
q->extco.Header.BufferId = MFX_EXTBUFF_CODING_OPTION;
q->extco.Header.BufferSz = sizeof(q->extco);
q->extco.PicTimingSEI = q->pic_timing_sei ?
MFX_CODINGOPTION_ON : MFX_CODINGOPTION_UNKNOWN;
if (q->rdo >= 0)
q->extco.RateDistortionOpt = q->rdo > 0 ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
if (avctx->codec_id == AV_CODEC_ID_H264) {
q->extco.CAVLC = q->cavlc ? MFX_CODINGOPTION_ON
: MFX_CODINGOPTION_UNKNOWN;
if (avctx->strict_std_compliance != FF_COMPLIANCE_NORMAL)
q->extco.NalHrdConformance = avctx->strict_std_compliance > FF_COMPLIANCE_NORMAL ?
MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
if (q->single_sei_nal_unit >= 0)
q->extco.SingleSeiNalUnit = q->single_sei_nal_unit ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
if (q->recovery_point_sei >= 0)
q->extco.RecoveryPointSEI = q->recovery_point_sei ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
q->extco.MaxDecFrameBuffering = q->max_dec_frame_buffering;
q->extco.AUDelimiter = q->aud ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
} else if (avctx->codec_id == AV_CODEC_ID_HEVC) {
if (avctx->strict_std_compliance != FF_COMPLIANCE_NORMAL)
q->extco.NalHrdConformance = avctx->strict_std_compliance > FF_COMPLIANCE_NORMAL ?
MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
if (q->recovery_point_sei >= 0)
q->extco.RecoveryPointSEI = q->recovery_point_sei ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
q->extco.AUDelimiter = q->aud ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
}
q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extco;
if (avctx->codec_id == AV_CODEC_ID_H264) {
if (q->bitrate_limit >= 0)
q->extco2.BitrateLimit = q->bitrate_limit ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
if (avctx->trellis >= 0)
q->extco2.Trellis = (avctx->trellis == 0) ? MFX_TRELLIS_OFF : (MFX_TRELLIS_I | MFX_TRELLIS_P | MFX_TRELLIS_B);
else
q->extco2.Trellis = MFX_TRELLIS_UNKNOWN;
q->extco2.LookAheadDS = q->look_ahead_downsampling;
q->extco2.RepeatPPS = q->repeat_pps ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
}
if (avctx->codec_id == AV_CODEC_ID_H264 || avctx->codec_id == AV_CODEC_ID_HEVC) {
if (q->extbrc >= 0)
q->extco2.ExtBRC = q->extbrc ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
if (q->max_frame_size >= 0)
q->extco2.MaxFrameSize = q->max_frame_size;
if (q->int_ref_type >= 0)
q->extco2.IntRefType = q->int_ref_type;
if (q->int_ref_cycle_size >= 0)
q->extco2.IntRefCycleSize = q->int_ref_cycle_size;
if (q->int_ref_qp_delta != INT16_MIN)
q->extco2.IntRefQPDelta = q->int_ref_qp_delta;
if (q->max_slice_size >= 0)
q->extco2.MaxSliceSize = q->max_slice_size;
q->extco2.DisableDeblockingIdc = q->dblk_idc;
if (q->b_strategy >= 0)
q->extco2.BRefType = q->b_strategy ? MFX_B_REF_PYRAMID : MFX_B_REF_OFF;
if (q->adaptive_i >= 0)
q->extco2.AdaptiveI = q->adaptive_i ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
if (q->adaptive_b >= 0)
q->extco2.AdaptiveB = q->adaptive_b ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
if ((avctx->qmin >= 0 && avctx->qmax >= 0 && avctx->qmin > avctx->qmax) ||
(q->max_qp_i >= 0 && q->min_qp_i >= 0 && q->min_qp_i > q->max_qp_i) ||
(q->max_qp_p >= 0 && q->min_qp_p >= 0 && q->min_qp_p > q->max_qp_p) ||
(q->max_qp_b >= 0 && q->min_qp_b >= 0 && q->min_qp_b > q->max_qp_b)) {
av_log(avctx, AV_LOG_ERROR,
"qmin and or qmax are set but invalid,"
" please make sure min <= max\n");
return AVERROR(EINVAL);
}
if (avctx->qmin >= 0) {
q->extco2.MinQPI = avctx->qmin > 51 ? 51 : avctx->qmin;
q->extco2.MinQPP = q->extco2.MinQPB = q->extco2.MinQPI;
}
if (avctx->qmax >= 0) {
q->extco2.MaxQPI = avctx->qmax > 51 ? 51 : avctx->qmax;
q->extco2.MaxQPP = q->extco2.MaxQPB = q->extco2.MaxQPI;
}
if (q->min_qp_i >= 0)
q->extco2.MinQPI = q->min_qp_i > 51 ? 51 : q->min_qp_i;
if (q->max_qp_i >= 0)
q->extco2.MaxQPI = q->max_qp_i > 51 ? 51 : q->max_qp_i;
if (q->min_qp_p >= 0)
q->extco2.MinQPP = q->min_qp_p > 51 ? 51 : q->min_qp_p;
if (q->max_qp_p >= 0)
q->extco2.MaxQPP = q->max_qp_p > 51 ? 51 : q->max_qp_p;
if (q->min_qp_b >= 0)
q->extco2.MinQPB = q->min_qp_b > 51 ? 51 : q->min_qp_b;
if (q->max_qp_b >= 0)
q->extco2.MaxQPB = q->max_qp_b > 51 ? 51 : q->max_qp_b;
if (q->mbbrc >= 0)
q->extco2.MBBRC = q->mbbrc ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
q->extco2.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2;
q->extco2.Header.BufferSz = sizeof(q->extco2);
q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extco2;
}
if (avctx->codec_id == AV_CODEC_ID_H264) {
#if QSV_HAVE_MF
if (QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 25)) {
q->extmfp.Header.BufferId = MFX_EXTBUFF_MULTI_FRAME_PARAM;
q->extmfp.Header.BufferSz = sizeof(q->extmfp);
q->extmfp.MFMode = q->mfmode;
av_log(avctx,AV_LOG_VERBOSE,"MFMode:%d\n", q->extmfp.MFMode);
q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extmfp;
}
#endif
}
q->extco3.Header.BufferId = MFX_EXTBUFF_CODING_OPTION3;
q->extco3.Header.BufferSz = sizeof(q->extco3);
if (avctx->codec_id == AV_CODEC_ID_HEVC ||
avctx->codec_id == AV_CODEC_ID_H264) {
switch (q->p_strategy) {
case 0:
q->extco3.PRefType = MFX_P_REF_DEFAULT;
break;
case 1:
q->extco3.PRefType = MFX_P_REF_SIMPLE;
break;
case 2:
q->extco3.PRefType = MFX_P_REF_PYRAMID;
break;
default:
q->extco3.PRefType = MFX_P_REF_DEFAULT;
av_log(avctx, AV_LOG_WARNING,
"invalid p_strategy, set to default\n");
break;
}
if (q->extco3.PRefType == MFX_P_REF_PYRAMID &&
avctx->max_b_frames != 0) {
av_log(avctx, AV_LOG_WARNING,
"Please set max_b_frames(-bf) to 0 to enable P-pyramid\n");
}
if (q->int_ref_cycle_dist >= 0)
q->extco3.IntRefCycleDist = q->int_ref_cycle_dist;
if (q->low_delay_brc >= 0)
q->extco3.LowDelayBRC = q->low_delay_brc ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
if (q->max_frame_size_i >= 0)
q->extco3.MaxFrameSizeI = q->max_frame_size_i;
if (q->max_frame_size_p >= 0)
q->extco3.MaxFrameSizeP = q->max_frame_size_p;
q->extco3.ScenarioInfo = q->scenario;
}
if (avctx->codec_id == AV_CODEC_ID_HEVC) {
if (q->transform_skip >= 0)
q->extco3.TransformSkip = q->transform_skip ? MFX_CODINGOPTION_ON :
MFX_CODINGOPTION_OFF;
else
q->extco3.TransformSkip = MFX_CODINGOPTION_UNKNOWN;
q->extco3.GPB = q->gpb ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
}
q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extco3;
}
if (avctx->codec_id == AV_CODEC_ID_VP9) {
q->extvp9param.Header.BufferId = MFX_EXTBUFF_VP9_PARAM;
q->extvp9param.Header.BufferSz = sizeof(q->extvp9param);
q->extvp9param.WriteIVFHeaders = MFX_CODINGOPTION_OFF;
#if QSV_HAVE_EXT_VP9_TILES
q->extvp9param.NumTileColumns = q->tile_cols;
q->extvp9param.NumTileRows = q->tile_rows;
#endif
q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extvp9param;
}
if (avctx->codec_id == AV_CODEC_ID_HEVC) {
q->exthevctiles.Header.BufferId = MFX_EXTBUFF_HEVC_TILES;
q->exthevctiles.Header.BufferSz = sizeof(q->exthevctiles);
q->exthevctiles.NumTileColumns = q->tile_cols;
q->exthevctiles.NumTileRows = q->tile_rows;
q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->exthevctiles;
}
q->extvsi.VideoFullRange = (avctx->color_range == AVCOL_RANGE_JPEG);
q->extvsi.ColourDescriptionPresent = 0;
if (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED ||
avctx->color_trc != AVCOL_TRC_UNSPECIFIED ||
avctx->colorspace != AVCOL_SPC_UNSPECIFIED) {
q->extvsi.ColourDescriptionPresent = 1;
q->extvsi.ColourPrimaries = avctx->color_primaries;
q->extvsi.TransferCharacteristics = avctx->color_trc;
q->extvsi.MatrixCoefficients = avctx->colorspace;
}
if ((avctx->codec_id != AV_CODEC_ID_VP9) && (q->extvsi.VideoFullRange || q->extvsi.ColourDescriptionPresent)) {
q->extvsi.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO;
q->extvsi.Header.BufferSz = sizeof(q->extvsi);
q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extvsi;
}
if (!check_enc_param(avctx,q)) {
av_log(avctx, AV_LOG_ERROR,
"some encoding parameters are not supported by the QSV "
"runtime. Please double check the input parameters.\n");
return AVERROR(ENOSYS);
}
return 0;
}
static int qsv_retrieve_enc_jpeg_params(AVCodecContext *avctx, QSVEncContext *q)
{
int ret = 0;
ret = MFXVideoENCODE_GetVideoParam(q->session, &q->param);
if (ret < 0)
return ff_qsv_print_error(avctx, ret,
"Error calling GetVideoParam");
q->packet_size = q->param.mfx.BufferSizeInKB * q->param.mfx.BRCParamMultiplier * 1000;
// for qsv mjpeg the return value maybe 0 so alloc the buffer
if (q->packet_size == 0)
q->packet_size = q->param.mfx.FrameInfo.Height * q->param.mfx.FrameInfo.Width * 4;
dump_video_mjpeg_param(avctx, q);
return 0;
}
static int qsv_retrieve_enc_vp9_params(AVCodecContext *avctx, QSVEncContext *q)
{
int ret = 0;
mfxExtVP9Param vp9_extend_buf = {
.Header.BufferId = MFX_EXTBUFF_VP9_PARAM,
.Header.BufferSz = sizeof(vp9_extend_buf),
};
mfxExtCodingOption2 co2 = {
.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2,
.Header.BufferSz = sizeof(co2),
};
mfxExtCodingOption3 co3 = {
.Header.BufferId = MFX_EXTBUFF_CODING_OPTION3,
.Header.BufferSz = sizeof(co3),
};
mfxExtBuffer *ext_buffers[3];
int ext_buf_num = 0;
q->co2_idx = q->co3_idx = q->vp9_idx = -1;
// It is possible the runtime doesn't support the given ext buffer
if (QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 6)) {
q->co2_idx = ext_buf_num;
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&co2;
}
if (QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 11)) {
q->co3_idx = ext_buf_num;
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&co3;
}
if (QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 26)) {
q->vp9_idx = ext_buf_num;
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&vp9_extend_buf;
}
q->param.ExtParam = ext_buffers;
q->param.NumExtParam = ext_buf_num;
ret = MFXVideoENCODE_GetVideoParam(q->session, &q->param);
if (ret < 0)
return ff_qsv_print_error(avctx, ret,
"Error calling GetVideoParam");
q->packet_size = q->param.mfx.BufferSizeInKB * q->param.mfx.BRCParamMultiplier * 1000;
dump_video_vp9_param(avctx, q, ext_buffers);
return 0;
}
static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q)
{
AVCPBProperties *cpb_props;
uint8_t sps_buf[512];
uint8_t pps_buf[128];
mfxExtCodingOptionSPSPPS extradata = {
.Header.BufferId = MFX_EXTBUFF_CODING_OPTION_SPSPPS,
.Header.BufferSz = sizeof(extradata),
.SPSBuffer = sps_buf, .SPSBufSize = sizeof(sps_buf),
.PPSBuffer = pps_buf, .PPSBufSize = sizeof(pps_buf)
};
mfxExtCodingOption co = {
.Header.BufferId = MFX_EXTBUFF_CODING_OPTION,
.Header.BufferSz = sizeof(co),
};
mfxExtCodingOption2 co2 = {
.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2,
.Header.BufferSz = sizeof(co2),
};
mfxExtCodingOption3 co3 = {
.Header.BufferId = MFX_EXTBUFF_CODING_OPTION3,
.Header.BufferSz = sizeof(co3),
};
uint8_t vps_buf[128];
mfxExtCodingOptionVPS extradata_vps = {
.Header.BufferId = MFX_EXTBUFF_CODING_OPTION_VPS,
.Header.BufferSz = sizeof(extradata_vps),
.VPSBuffer = vps_buf,
.VPSBufSize = sizeof(vps_buf),
};
mfxExtHEVCTiles hevc_tile_buf = {
.Header.BufferId = MFX_EXTBUFF_HEVC_TILES,
.Header.BufferSz = sizeof(hevc_tile_buf),
};
mfxExtBuffer *ext_buffers[6];
int need_pps = avctx->codec_id != AV_CODEC_ID_MPEG2VIDEO;
int ret, ext_buf_num = 0, extradata_offset = 0;
q->co2_idx = q->co3_idx = q->exthevctiles_idx = -1;
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&extradata;
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&co;
// It is possible the runtime doesn't support the given ext buffer
if (QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 6)) {
q->co2_idx = ext_buf_num;
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&co2;
}
if (QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 11)) {
q->co3_idx = ext_buf_num;
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&co3;
}
q->hevc_vps = ((avctx->codec_id == AV_CODEC_ID_HEVC) && QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 17));
if (q->hevc_vps)
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&extradata_vps;
if (avctx->codec_id == AV_CODEC_ID_HEVC && QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 13)) {
q->exthevctiles_idx = ext_buf_num;
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&hevc_tile_buf;
}
q->param.ExtParam = ext_buffers;
q->param.NumExtParam = ext_buf_num;
ret = MFXVideoENCODE_GetVideoParam(q->session, &q->param);
if (ret < 0)
return ff_qsv_print_error(avctx, ret,
"Error calling GetVideoParam");
q->packet_size = q->param.mfx.BufferSizeInKB * q->param.mfx.BRCParamMultiplier * 1000;
if (!extradata.SPSBufSize || (need_pps && !extradata.PPSBufSize)
|| (q->hevc_vps && !extradata_vps.VPSBufSize)
) {
av_log(avctx, AV_LOG_ERROR, "No extradata returned from libmfx.\n");
return AVERROR_UNKNOWN;
}
avctx->extradata_size = extradata.SPSBufSize + need_pps * extradata.PPSBufSize;
avctx->extradata_size += q->hevc_vps * extradata_vps.VPSBufSize;
avctx->extradata = av_malloc(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!avctx->extradata)
return AVERROR(ENOMEM);
if (q->hevc_vps) {
memcpy(avctx->extradata, vps_buf, extradata_vps.VPSBufSize);
extradata_offset += extradata_vps.VPSBufSize;
}
memcpy(avctx->extradata + extradata_offset, sps_buf, extradata.SPSBufSize);
extradata_offset += extradata.SPSBufSize;
if (need_pps) {
memcpy(avctx->extradata + extradata_offset, pps_buf, extradata.PPSBufSize);
extradata_offset += extradata.PPSBufSize;
}
memset(avctx->extradata + avctx->extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
cpb_props = ff_add_cpb_side_data(avctx);
if (!cpb_props)
return AVERROR(ENOMEM);
cpb_props->max_bitrate = avctx->rc_max_rate;
cpb_props->min_bitrate = avctx->rc_min_rate;
cpb_props->avg_bitrate = avctx->bit_rate;
cpb_props->buffer_size = avctx->rc_buffer_size;
dump_video_param(avctx, q, ext_buffers);
return 0;
}
#if QSV_HAVE_OPAQUE
static int qsv_init_opaque_alloc(AVCodecContext *avctx, QSVEncContext *q)
{
AVQSVContext *qsv = avctx->hwaccel_context;
mfxFrameSurface1 *surfaces;
int nb_surfaces, i;
nb_surfaces = qsv->nb_opaque_surfaces + q->req.NumFrameSuggested;
q->opaque_alloc_buf = av_buffer_allocz(sizeof(*surfaces) * nb_surfaces);
if (!q->opaque_alloc_buf)
return AVERROR(ENOMEM);
q->opaque_surfaces = av_malloc_array(nb_surfaces, sizeof(*q->opaque_surfaces));
if (!q->opaque_surfaces)
return AVERROR(ENOMEM);
surfaces = (mfxFrameSurface1*)q->opaque_alloc_buf->data;
for (i = 0; i < nb_surfaces; i++) {
surfaces[i].Info = q->req.Info;
q->opaque_surfaces[i] = surfaces + i;
}
q->opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION;
q->opaque_alloc.Header.BufferSz = sizeof(q->opaque_alloc);
q->opaque_alloc.In.Surfaces = q->opaque_surfaces;
q->opaque_alloc.In.NumSurface = nb_surfaces;
q->opaque_alloc.In.Type = q->req.Type;
q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->opaque_alloc;
qsv->nb_opaque_surfaces = nb_surfaces;
qsv->opaque_surfaces = q->opaque_alloc_buf;
qsv->opaque_alloc_type = q->req.Type;
return 0;
}
#endif
static int qsvenc_init_session(AVCodecContext *avctx, QSVEncContext *q)
{
int ret;
if (avctx->hwaccel_context) {
AVQSVContext *qsv = avctx->hwaccel_context;
q->session = qsv->session;
} else if (avctx->hw_frames_ctx) {
q->frames_ctx.hw_frames_ctx = av_buffer_ref(avctx->hw_frames_ctx);
if (!q->frames_ctx.hw_frames_ctx)
return AVERROR(ENOMEM);
ret = ff_qsv_init_session_frames(avctx, &q->internal_qs.session,
&q->frames_ctx, q->load_plugins,
#if QSV_HAVE_OPAQUE
q->param.IOPattern == MFX_IOPATTERN_IN_OPAQUE_MEMORY,
#else
0,
#endif
MFX_GPUCOPY_OFF);
if (ret < 0) {
av_buffer_unref(&q->frames_ctx.hw_frames_ctx);
return ret;
}
q->session = q->internal_qs.session;
} else if (avctx->hw_device_ctx) {
ret = ff_qsv_init_session_device(avctx, &q->internal_qs.session,
avctx->hw_device_ctx, q->load_plugins,
MFX_GPUCOPY_OFF);
if (ret < 0)
return ret;
q->session = q->internal_qs.session;
} else {
ret = ff_qsv_init_internal_session(avctx, &q->internal_qs,
q->load_plugins, MFX_GPUCOPY_OFF);
if (ret < 0)
return ret;
q->session = q->internal_qs.session;
}
return 0;
}
int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q)
{
int iopattern = 0;
int opaque_alloc = 0;
int ret;
q->param.AsyncDepth = q->async_depth;
q->async_fifo = av_fifo_alloc2(q->async_depth, sizeof(QSVPacket), 0);
if (!q->async_fifo)
return AVERROR(ENOMEM);
if (avctx->hwaccel_context) {
AVQSVContext *qsv = avctx->hwaccel_context;
iopattern = qsv->iopattern;
opaque_alloc = qsv->opaque_alloc;
}
if (avctx->hw_frames_ctx) {
AVHWFramesContext *frames_ctx = (AVHWFramesContext*)avctx->hw_frames_ctx->data;
AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx;
if (!iopattern) {
#if QSV_HAVE_OPAQUE
if (frames_hwctx->frame_type & MFX_MEMTYPE_OPAQUE_FRAME)
iopattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY;
else if (frames_hwctx->frame_type &
(MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET | MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET))
iopattern = MFX_IOPATTERN_IN_VIDEO_MEMORY;
#else
if (frames_hwctx->frame_type &
(MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET | MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET))
iopattern = MFX_IOPATTERN_IN_VIDEO_MEMORY;
#endif
}
}
if (!iopattern)
iopattern = MFX_IOPATTERN_IN_SYSTEM_MEMORY;
q->param.IOPattern = iopattern;
ff_qsv_print_iopattern(avctx, iopattern, "Encoder");
ret = qsvenc_init_session(avctx, q);
if (ret < 0)
return ret;
ret = MFXQueryVersion(q->session,&q->ver);
if (ret < 0) {
return ff_qsv_print_error(avctx, ret,
"Error querying mfx version");
}
// in the mfxInfoMFX struct, JPEG is different from other codecs
switch (avctx->codec_id) {
case AV_CODEC_ID_MJPEG:
ret = init_video_param_jpeg(avctx, q);
break;
default:
ret = init_video_param(avctx, q);
break;
}
if (ret < 0)
return ret;
if (avctx->hwaccel_context) {
AVQSVContext *qsv = avctx->hwaccel_context;
int i, j;
q->extparam = av_calloc(qsv->nb_ext_buffers + q->nb_extparam_internal,
sizeof(*q->extparam));
if (!q->extparam)
return AVERROR(ENOMEM);
q->param.ExtParam = q->extparam;
for (i = 0; i < qsv->nb_ext_buffers; i++)
q->param.ExtParam[i] = qsv->ext_buffers[i];
q->param.NumExtParam = qsv->nb_ext_buffers;
for (i = 0; i < q->nb_extparam_internal; i++) {
for (j = 0; j < qsv->nb_ext_buffers; j++) {
if (qsv->ext_buffers[j]->BufferId == q->extparam_internal[i]->BufferId)
break;
}
if (j < qsv->nb_ext_buffers)
continue;
q->param.ExtParam[q->param.NumExtParam++] = q->extparam_internal[i];
}
} else {
q->param.ExtParam = q->extparam_internal;
q->param.NumExtParam = q->nb_extparam_internal;
}
ret = MFXVideoENCODE_Query(q->session, &q->param, &q->param);
if (ret == MFX_WRN_PARTIAL_ACCELERATION) {
av_log(avctx, AV_LOG_WARNING, "Encoder will work with partial HW acceleration\n");
} else if (ret < 0) {
return ff_qsv_print_error(avctx, ret,
"Error querying encoder params");
}
ret = MFXVideoENCODE_QueryIOSurf(q->session, &q->param, &q->req);
if (ret < 0)
return ff_qsv_print_error(avctx, ret,
"Error querying (IOSurf) the encoding parameters");
if (opaque_alloc) {
#if QSV_HAVE_OPAQUE
ret = qsv_init_opaque_alloc(avctx, q);
if (ret < 0)
return ret;
#else
av_log(avctx, AV_LOG_ERROR, "User is requesting to allocate OPAQUE surface, "
"however libmfx %d.%d doesn't support OPAQUE memory.\n",
q->ver.Major, q->ver.Minor);
return AVERROR_UNKNOWN;
#endif
}
ret = MFXVideoENCODE_Init(q->session, &q->param);
if (ret < 0)
return ff_qsv_print_error(avctx, ret,
"Error initializing the encoder");
else if (ret > 0)
ff_qsv_print_warning(avctx, ret,
"Warning in encoder initialization");
switch (avctx->codec_id) {
case AV_CODEC_ID_MJPEG:
ret = qsv_retrieve_enc_jpeg_params(avctx, q);
break;
case AV_CODEC_ID_VP9:
ret = qsv_retrieve_enc_vp9_params(avctx, q);
break;
default:
ret = qsv_retrieve_enc_params(avctx, q);
break;
}
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR, "Error retrieving encoding parameters.\n");
return ret;
}
q->avctx = avctx;
return 0;
}
static void free_encoder_ctrl_payloads(mfxEncodeCtrl* enc_ctrl)
{
if (enc_ctrl) {
int i;
for (i = 0; i < enc_ctrl->NumPayload && i < QSV_MAX_ENC_PAYLOAD; i++) {
av_freep(&enc_ctrl->Payload[i]);
}
enc_ctrl->NumPayload = 0;
}
}
static void free_encoder_ctrl_extparam(mfxEncodeCtrl* enc_ctrl)
{
if (enc_ctrl) {
int i;
for (i = 0; i < enc_ctrl->NumExtParam && i < QSV_MAX_ENC_EXTPARAM; i++) {
if (enc_ctrl->ExtParam[i])
av_freep(&(enc_ctrl->ExtParam[i]));
}
enc_ctrl->NumExtParam = 0;
}
}
static void clear_unused_frames(QSVEncContext *q)
{
QSVFrame *cur = q->work_frames;
while (cur) {
if (cur->used && !cur->surface.Data.Locked) {
free_encoder_ctrl_payloads(&cur->enc_ctrl);
free_encoder_ctrl_extparam(&cur->enc_ctrl);
//do not reuse enc_ctrl from previous frame
memset(&cur->enc_ctrl, 0, sizeof(cur->enc_ctrl));
cur->enc_ctrl.Payload = cur->payloads;
cur->enc_ctrl.ExtParam = cur->extparam;
if (cur->frame->format == AV_PIX_FMT_QSV) {
av_frame_unref(cur->frame);
}
cur->used = 0;
}
cur = cur->next;
}
}
static int get_free_frame(QSVEncContext *q, QSVFrame **f)
{
QSVFrame *frame, **last;
clear_unused_frames(q);
frame = q->work_frames;
last = &q->work_frames;
while (frame) {
if (!frame->used) {
*f = frame;
frame->used = 1;
return 0;
}
last = &frame->next;
frame = frame->next;
}
frame = av_mallocz(sizeof(*frame));
if (!frame)
return AVERROR(ENOMEM);
frame->frame = av_frame_alloc();
if (!frame->frame) {
av_freep(&frame);
return AVERROR(ENOMEM);
}
frame->enc_ctrl.Payload = frame->payloads;
frame->enc_ctrl.ExtParam = frame->extparam;
*last = frame;
*f = frame;
frame->used = 1;
return 0;
}
static int submit_frame(QSVEncContext *q, const AVFrame *frame,
QSVFrame **new_frame)
{
QSVFrame *qf;
int ret;
ret = get_free_frame(q, &qf);
if (ret < 0)
return ret;
if (frame->format == AV_PIX_FMT_QSV) {
ret = av_frame_ref(qf->frame, frame);
if (ret < 0)
return ret;
qf->surface = *(mfxFrameSurface1*)qf->frame->data[3];
if (q->frames_ctx.mids) {
ret = ff_qsv_find_surface_idx(&q->frames_ctx, qf);
if (ret < 0)
return ret;
qf->surface.Data.MemId = &q->frames_ctx.mids[ret];
}
} else {
/* make a copy if the input is not padded as libmfx requires */
/* and to make allocation continious for data[0]/data[1] */
if ((frame->height & 31 || frame->linesize[0] & (q->width_align - 1)) ||
(frame->data[1] - frame->data[0] != frame->linesize[0] * FFALIGN(qf->frame->height, q->height_align))) {
qf->frame->height = FFALIGN(frame->height, q->height_align);
qf->frame->width = FFALIGN(frame->width, q->width_align);
qf->frame->format = frame->format;
if (!qf->frame->data[0]) {
ret = av_frame_get_buffer(qf->frame, q->width_align);
if (ret < 0)
return ret;
}
qf->frame->height = frame->height;
qf->frame->width = frame->width;
ret = av_frame_copy(qf->frame, frame);
if (ret < 0) {
av_frame_unref(qf->frame);
return ret;
}
} else {
av_frame_unref(qf->frame);
ret = av_frame_ref(qf->frame, frame);
if (ret < 0)
return ret;
}
qf->surface.Info = q->param.mfx.FrameInfo;
qf->surface.Info.PicStruct =
!frame->interlaced_frame ? MFX_PICSTRUCT_PROGRESSIVE :
frame->top_field_first ? MFX_PICSTRUCT_FIELD_TFF :
MFX_PICSTRUCT_FIELD_BFF;
if (frame->repeat_pict == 1)
qf->surface.Info.PicStruct |= MFX_PICSTRUCT_FIELD_REPEATED;
else if (frame->repeat_pict == 2)
qf->surface.Info.PicStruct |= MFX_PICSTRUCT_FRAME_DOUBLING;
else if (frame->repeat_pict == 4)
qf->surface.Info.PicStruct |= MFX_PICSTRUCT_FRAME_TRIPLING;
ret = ff_qsv_map_frame_to_surface(qf->frame, &qf->surface);
if (ret < 0) {
av_log(q->avctx, AV_LOG_ERROR, "map frame to surface failed.\n");
return ret;
}
}
qf->surface.Data.TimeStamp = av_rescale_q(frame->pts, q->avctx->time_base, (AVRational){1, 90000});
*new_frame = qf;
return 0;
}
static void print_interlace_msg(AVCodecContext *avctx, QSVEncContext *q)
{
if (q->param.mfx.CodecId == MFX_CODEC_AVC) {
if (q->param.mfx.CodecProfile == MFX_PROFILE_AVC_BASELINE ||
q->param.mfx.CodecLevel < MFX_LEVEL_AVC_21 ||
q->param.mfx.CodecLevel > MFX_LEVEL_AVC_41)
av_log(avctx, AV_LOG_WARNING,
"Interlaced coding is supported"
" at Main/High Profile Level 2.2-4.0\n");
}
}
static int set_roi_encode_ctrl(AVCodecContext *avctx, const AVFrame *frame,
mfxEncodeCtrl *enc_ctrl)
{
AVFrameSideData *sd = NULL;
int mb_size;
if (avctx->codec_id == AV_CODEC_ID_H264)
mb_size = 16;
else if (avctx->codec_id == AV_CODEC_ID_H265)
mb_size = 32;
else
return 0;
if (frame)
sd = av_frame_get_side_data(frame, AV_FRAME_DATA_REGIONS_OF_INTEREST);
if (sd) {
mfxExtEncoderROI *enc_roi = NULL;
AVRegionOfInterest *roi;
uint32_t roi_size;
int nb_roi, i;
roi = (AVRegionOfInterest *)sd->data;
roi_size = roi->self_size;
if (!roi_size || sd->size % roi_size) {
av_log(avctx, AV_LOG_ERROR, "Invalid ROI Data.\n");
return AVERROR(EINVAL);
}
nb_roi = sd->size / roi_size;
if (nb_roi > QSV_MAX_ROI_NUM) {
av_log(avctx, AV_LOG_WARNING, "More ROIs set than "
"supported by driver (%d > %d).\n",
nb_roi, QSV_MAX_ROI_NUM);
nb_roi = QSV_MAX_ROI_NUM;
}
enc_roi = av_mallocz(sizeof(*enc_roi));
if (!enc_roi)
return AVERROR(ENOMEM);
enc_roi->Header.BufferId = MFX_EXTBUFF_ENCODER_ROI;
enc_roi->Header.BufferSz = sizeof(*enc_roi);
enc_roi->NumROI = nb_roi;
enc_roi->ROIMode = MFX_ROI_MODE_QP_DELTA;
for (i = 0; i < nb_roi; i++) {
roi = (AVRegionOfInterest *)(sd->data + roi_size * i);
enc_roi->ROI[i].Top = FFALIGN(roi->top, mb_size);
enc_roi->ROI[i].Bottom = FFALIGN(roi->bottom, mb_size);
enc_roi->ROI[i].Left = FFALIGN(roi->left, mb_size);
enc_roi->ROI[i].Right = FFALIGN(roi->right, mb_size);
enc_roi->ROI[i].DeltaQP =
roi->qoffset.num * 51 / roi->qoffset.den;
av_log(avctx, AV_LOG_DEBUG, "ROI: (%d,%d)-(%d,%d) -> %+d.\n",
roi->top, roi->left, roi->bottom, roi->right,
enc_roi->ROI[i].DeltaQP);
}
enc_ctrl->ExtParam[enc_ctrl->NumExtParam] = (mfxExtBuffer *)enc_roi;
enc_ctrl->NumExtParam++;
}
return 0;
}
static int update_qp(AVCodecContext *avctx, QSVEncContext *q)
{
int updated = 0, new_qp = 0;
if (avctx->codec_id != AV_CODEC_ID_H264 && avctx->codec_id != AV_CODEC_ID_HEVC)
return 0;
if (q->param.mfx.RateControlMethod == MFX_RATECONTROL_CQP) {
UPDATE_PARAM(q->old_global_quality, avctx->global_quality);
UPDATE_PARAM(q->old_i_quant_factor, avctx->i_quant_factor);
UPDATE_PARAM(q->old_i_quant_offset, avctx->i_quant_offset);
UPDATE_PARAM(q->old_b_quant_factor, avctx->b_quant_factor);
UPDATE_PARAM(q->old_b_quant_offset, avctx->b_quant_offset);
if (!updated)
return 0;
new_qp = avctx->global_quality / FF_QP2LAMBDA;
q->param.mfx.QPI = av_clip(new_qp * fabs(avctx->i_quant_factor) +
avctx->i_quant_offset, 0, 51);
q->param.mfx.QPP = av_clip(new_qp, 0, 51);
q->param.mfx.QPB = av_clip(new_qp * fabs(avctx->b_quant_factor) +
avctx->b_quant_offset, 0, 51);
av_log(avctx, AV_LOG_DEBUG,
"Reset qp = %d/%d/%d for idr/p/b frames\n",
q->param.mfx.QPI, q->param.mfx.QPP, q->param.mfx.QPB);
}
return updated;
}
static int update_parameters(AVCodecContext *avctx, QSVEncContext *q,
const AVFrame *frame)
{
int needReset = 0, ret = 0;
if (!frame)
return 0;
needReset = update_qp(avctx, q);
if (!needReset)
return 0;
if (avctx->hwaccel_context) {
AVQSVContext *qsv = avctx->hwaccel_context;
int i, j;
q->param.ExtParam = q->extparam;
for (i = 0; i < qsv->nb_ext_buffers; i++)
q->param.ExtParam[i] = qsv->ext_buffers[i];
q->param.NumExtParam = qsv->nb_ext_buffers;
for (i = 0; i < q->nb_extparam_internal; i++) {
for (j = 0; j < qsv->nb_ext_buffers; j++) {
if (qsv->ext_buffers[j]->BufferId == q->extparam_internal[i]->BufferId)
break;
}
if (j < qsv->nb_ext_buffers)
continue;
q->param.ExtParam[q->param.NumExtParam++] = q->extparam_internal[i];
}
} else {
q->param.ExtParam = q->extparam_internal;
q->param.NumExtParam = q->nb_extparam_internal;
}
av_log(avctx, AV_LOG_DEBUG, "Parameter change, call msdk reset.\n");
ret = MFXVideoENCODE_Reset(q->session, &q->param);
if (ret < 0)
return ff_qsv_print_error(avctx, ret, "Error during resetting");
return 0;
}
static int encode_frame(AVCodecContext *avctx, QSVEncContext *q,
const AVFrame *frame)
{
QSVPacket pkt = { { 0 } };
mfxExtAVCEncodedFrameInfo *enc_info = NULL;
mfxExtBuffer **enc_buf = NULL;
mfxFrameSurface1 *surf = NULL;
QSVFrame *qsv_frame = NULL;
mfxEncodeCtrl* enc_ctrl = NULL;
int ret;
if (frame) {
ret = submit_frame(q, frame, &qsv_frame);
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR, "Error submitting the frame for encoding.\n");
return ret;
}
}
if (qsv_frame) {
surf = &qsv_frame->surface;
enc_ctrl = &qsv_frame->enc_ctrl;
if (frame->pict_type == AV_PICTURE_TYPE_I) {
enc_ctrl->FrameType = MFX_FRAMETYPE_I | MFX_FRAMETYPE_REF;
if (q->forced_idr)
enc_ctrl->FrameType |= MFX_FRAMETYPE_IDR;
}
}
ret = av_new_packet(&pkt.pkt, q->packet_size);
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR, "Error allocating the output packet\n");
return ret;
}
pkt.bs = av_mallocz(sizeof(*pkt.bs));
if (!pkt.bs)
goto nomem;
pkt.bs->Data = pkt.pkt.data;
pkt.bs->MaxLength = pkt.pkt.size;
if (avctx->codec_id == AV_CODEC_ID_H264) {
enc_info = av_mallocz(sizeof(*enc_info));
if (!enc_info)
goto nomem;
enc_info->Header.BufferId = MFX_EXTBUFF_ENCODED_FRAME_INFO;
enc_info->Header.BufferSz = sizeof (*enc_info);
pkt.bs->NumExtParam = 1;
enc_buf = av_mallocz(sizeof(mfxExtBuffer *));
if (!enc_buf)
goto nomem;
enc_buf[0] = (mfxExtBuffer *)enc_info;
pkt.bs->ExtParam = enc_buf;
}
if (q->set_encode_ctrl_cb) {
q->set_encode_ctrl_cb(avctx, frame, &qsv_frame->enc_ctrl);
}
if ((avctx->codec_id == AV_CODEC_ID_H264 ||
avctx->codec_id == AV_CODEC_ID_H265) &&
enc_ctrl && QSV_RUNTIME_VERSION_ATLEAST(q->ver, 1, 8)) {
ret = set_roi_encode_ctrl(avctx, frame, enc_ctrl);
if (ret < 0)
goto free;
}
pkt.sync = av_mallocz(sizeof(*pkt.sync));
if (!pkt.sync)
goto nomem;
do {
ret = MFXVideoENCODE_EncodeFrameAsync(q->session, enc_ctrl, surf, pkt.bs, pkt.sync);
if (ret == MFX_WRN_DEVICE_BUSY)
av_usleep(500);
} while (ret == MFX_WRN_DEVICE_BUSY || ret == MFX_WRN_IN_EXECUTION);
if (ret > 0)
ff_qsv_print_warning(avctx, ret, "Warning during encoding");
if (ret < 0) {
ret = (ret == MFX_ERR_MORE_DATA) ?
0 : ff_qsv_print_error(avctx, ret, "Error during encoding");
goto free;
}
if (ret == MFX_WRN_INCOMPATIBLE_VIDEO_PARAM && frame && frame->interlaced_frame)
print_interlace_msg(avctx, q);
ret = 0;
if (*pkt.sync) {
av_fifo_write(q->async_fifo, &pkt, 1);
} else {
free:
av_freep(&pkt.sync);
av_packet_unref(&pkt.pkt);
av_freep(&pkt.bs);
if (avctx->codec_id == AV_CODEC_ID_H264) {
av_freep(&enc_info);
av_freep(&enc_buf);
}
}
return ret;
nomem:
ret = AVERROR(ENOMEM);
goto free;
}
int ff_qsv_encode(AVCodecContext *avctx, QSVEncContext *q,
AVPacket *pkt, const AVFrame *frame, int *got_packet)
{
int ret;
ret = update_parameters(avctx, q, frame);
if (ret < 0)
return ret;
ret = encode_frame(avctx, q, frame);
if (ret < 0)
return ret;
if ((av_fifo_can_read(q->async_fifo) >= q->async_depth) ||
(!frame && av_fifo_can_read(q->async_fifo))) {
QSVPacket qpkt;
mfxExtAVCEncodedFrameInfo *enc_info;
mfxExtBuffer **enc_buf;
enum AVPictureType pict_type;
av_fifo_read(q->async_fifo, &qpkt, 1);
do {
ret = MFXVideoCORE_SyncOperation(q->session, *qpkt.sync, 1000);
} while (ret == MFX_WRN_IN_EXECUTION);
qpkt.pkt.dts = av_rescale_q(qpkt.bs->DecodeTimeStamp, (AVRational){1, 90000}, avctx->time_base);
qpkt.pkt.pts = av_rescale_q(qpkt.bs->TimeStamp, (AVRational){1, 90000}, avctx->time_base);
qpkt.pkt.size = qpkt.bs->DataLength;
if (qpkt.bs->FrameType & MFX_FRAMETYPE_IDR || qpkt.bs->FrameType & MFX_FRAMETYPE_xIDR) {
qpkt.pkt.flags |= AV_PKT_FLAG_KEY;
pict_type = AV_PICTURE_TYPE_I;
} else if (qpkt.bs->FrameType & MFX_FRAMETYPE_I || qpkt.bs->FrameType & MFX_FRAMETYPE_xI)
pict_type = AV_PICTURE_TYPE_I;
else if (qpkt.bs->FrameType & MFX_FRAMETYPE_P || qpkt.bs->FrameType & MFX_FRAMETYPE_xP)
pict_type = AV_PICTURE_TYPE_P;
else if (qpkt.bs->FrameType & MFX_FRAMETYPE_B || qpkt.bs->FrameType & MFX_FRAMETYPE_xB)
pict_type = AV_PICTURE_TYPE_B;
else if (qpkt.bs->FrameType == MFX_FRAMETYPE_UNKNOWN) {
pict_type = AV_PICTURE_TYPE_NONE;
av_log(avctx, AV_LOG_WARNING, "Unknown FrameType, set pict_type to AV_PICTURE_TYPE_NONE.\n");
} else {
av_log(avctx, AV_LOG_ERROR, "Invalid FrameType:%d.\n", qpkt.bs->FrameType);
return AVERROR_INVALIDDATA;
}
if (avctx->codec_id == AV_CODEC_ID_H264) {
enc_buf = qpkt.bs->ExtParam;
enc_info = (mfxExtAVCEncodedFrameInfo *)(*enc_buf);
ff_side_data_set_encoder_stats(&qpkt.pkt,
enc_info->QP * FF_QP2LAMBDA, NULL, 0, pict_type);
av_freep(&enc_info);
av_freep(&enc_buf);
}
av_freep(&qpkt.bs);
av_freep(&qpkt.sync);
av_packet_move_ref(pkt, &qpkt.pkt);
*got_packet = 1;
}
return 0;
}
int ff_qsv_enc_close(AVCodecContext *avctx, QSVEncContext *q)
{
QSVFrame *cur;
if (q->session)
MFXVideoENCODE_Close(q->session);
q->session = NULL;
ff_qsv_close_internal_session(&q->internal_qs);
av_buffer_unref(&q->frames_ctx.hw_frames_ctx);
av_buffer_unref(&q->frames_ctx.mids_buf);
cur = q->work_frames;
while (cur) {
q->work_frames = cur->next;
av_frame_free(&cur->frame);
free_encoder_ctrl_extparam(&cur->enc_ctrl);
free_encoder_ctrl_payloads(&cur->enc_ctrl);
av_freep(&cur);
cur = q->work_frames;
}
if (q->async_fifo) {
QSVPacket pkt;
while (av_fifo_read(q->async_fifo, &pkt, 1) >= 0) {
if (avctx->codec_id == AV_CODEC_ID_H264) {
mfxExtBuffer **enc_buf = pkt.bs->ExtParam;
mfxExtAVCEncodedFrameInfo *enc_info = (mfxExtAVCEncodedFrameInfo *)(*enc_buf);
av_freep(&enc_info);
av_freep(&enc_buf);
}
av_freep(&pkt.sync);
av_freep(&pkt.bs);
av_packet_unref(&pkt.pkt);
}
av_fifo_freep2(&q->async_fifo);
}
#if QSV_HAVE_OPAQUE
av_freep(&q->opaque_surfaces);
av_buffer_unref(&q->opaque_alloc_buf);
#endif
av_freep(&q->extparam);
return 0;
}
const AVCodecHWConfigInternal *const ff_qsv_enc_hw_configs[] = {
HW_CONFIG_ENCODER_FRAMES(QSV, QSV),
HW_CONFIG_ENCODER_DEVICE(NV12, QSV),
HW_CONFIG_ENCODER_DEVICE(P010, QSV),
NULL,
};