1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-12-12 19:18:44 +02:00
FFmpeg/libavcodec/qsvenc.c
Haihao Xiang 115f5e8035 lavc/qsvenc: allows the SDK runtime to choose LowPower/non-LowPower modes
The SDK supports LowPower and non-LowPower modes, but some features are
available only under one of the two modes. Currently non-LowPower mode
is always chosen in FFmpeg if the mode is not set to LowPower
explicitly. User will experience some SDK errors if a LowPower related
feature is specified but the mode is not set to LowPower. With this
patch, the mode is set to unknown by default in FFmpeg, the SDK is able
to choose a workable mode for the specified features.

Reviewed-by: Soft Works <softworkz@hotmail.com>
Signed-off-by: James Almer <jamrial@gmail.com>
2021-08-12 22:25:11 -03:00

1680 lines
59 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 <string.h>
#include <sys/types.h>
#include <mfx/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"
static const struct {
mfxU16 profile;
const char *name;
} profile_names[] = {
{ MFX_PROFILE_AVC_BASELINE, "baseline" },
{ MFX_PROFILE_AVC_MAIN, "main" },
{ MFX_PROFILE_AVC_EXTENDED, "extended" },
{ MFX_PROFILE_AVC_HIGH, "high" },
#if QSV_VERSION_ATLEAST(1, 15)
{ MFX_PROFILE_AVC_HIGH_422, "high 422" },
#endif
#if QSV_VERSION_ATLEAST(1, 4)
{ MFX_PROFILE_AVC_CONSTRAINED_BASELINE, "constrained baseline" },
{ MFX_PROFILE_AVC_CONSTRAINED_HIGH, "constrained high" },
{ MFX_PROFILE_AVC_PROGRESSIVE_HIGH, "progressive high" },
#endif
{ MFX_PROFILE_MPEG2_SIMPLE, "simple" },
{ MFX_PROFILE_MPEG2_MAIN, "main" },
{ MFX_PROFILE_MPEG2_HIGH, "high" },
{ MFX_PROFILE_VC1_SIMPLE, "simple" },
{ MFX_PROFILE_VC1_MAIN, "main" },
{ MFX_PROFILE_VC1_ADVANCED, "advanced" },
#if QSV_VERSION_ATLEAST(1, 8)
{ MFX_PROFILE_HEVC_MAIN, "main" },
{ MFX_PROFILE_HEVC_MAIN10, "main10" },
{ MFX_PROFILE_HEVC_MAINSP, "mainsp" },
{ MFX_PROFILE_HEVC_REXT, "rext" },
#endif
};
static const char *print_profile(mfxU16 profile)
{
int i;
for (i = 0; i < FF_ARRAY_ELEMS(profile_names); i++)
if (profile == profile_names[i].profile)
return profile_names[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
#if QSV_HAVE_LA
{ MFX_RATECONTROL_LA, "LA" },
#endif
#if QSV_HAVE_ICQ
{ MFX_RATECONTROL_ICQ, "ICQ" },
{ MFX_RATECONTROL_LA_ICQ, "LA_ICQ" },
#endif
#if QSV_HAVE_VCM
{ MFX_RATECONTROL_VCM, "VCM" },
#endif
#if QSV_VERSION_ATLEAST(1, 10)
{ MFX_RATECONTROL_LA_EXT, "LA_EXT" },
#endif
#if QSV_HAVE_LA_HRD
{ MFX_RATECONTROL_LA_HRD, "LA_HRD" },
#endif
#if QSV_HAVE_QVBR
{ MFX_RATECONTROL_QVBR, "QVBR" },
#endif
};
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;
mfxExtCodingOption *co = (mfxExtCodingOption*)coding_opts[0];
#if QSV_HAVE_CO2
mfxExtCodingOption2 *co2 = (mfxExtCodingOption2*)coding_opts[1];
#endif
#if QSV_HAVE_CO3
mfxExtCodingOption3 *co3 = (mfxExtCodingOption3*)coding_opts[2];
#endif
#if QSV_HAVE_EXT_HEVC_TILES
mfxExtHEVCTiles *exthevctiles = (mfxExtHEVCTiles *)coding_opts[3 + QSV_HAVE_CO_VPS];
#endif
av_log(avctx, AV_LOG_VERBOSE, "profile: %s; level: %"PRIu16"\n",
print_profile(info->CodecProfile), info->CodecLevel);
av_log(avctx, AV_LOG_VERBOSE, "GopPicSize: %"PRIu16"; GopRefDist: %"PRIu16"; GopOptFlag: ",
info->GopPicSize, info->GopRefDist);
if (info->GopOptFlag & MFX_GOP_CLOSED)
av_log(avctx, AV_LOG_VERBOSE, "closed ");
if (info->GopOptFlag & MFX_GOP_STRICT)
av_log(avctx, AV_LOG_VERBOSE, "strict ");
av_log(avctx, AV_LOG_VERBOSE, "; IdrInterval: %"PRIu16"\n", 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
#if QSV_HAVE_LA
else if (info->RateControlMethod == MFX_RATECONTROL_LA
#if QSV_HAVE_LA_HRD
|| info->RateControlMethod == MFX_RATECONTROL_LA_HRD
#endif
) {
av_log(avctx, AV_LOG_VERBOSE,
"TargetKbps: %"PRIu16"; LookAheadDepth: %"PRIu16"; BRCParamMultiplier: %"PRIu16"\n",
info->TargetKbps, co2->LookAheadDepth, info->BRCParamMultiplier);
}
#endif
#if QSV_HAVE_ICQ
else if (info->RateControlMethod == MFX_RATECONTROL_ICQ) {
av_log(avctx, AV_LOG_VERBOSE, "ICQQuality: %"PRIu16"\n", info->ICQQuality);
} else if (info->RateControlMethod == MFX_RATECONTROL_LA_ICQ) {
av_log(avctx, AV_LOG_VERBOSE, "ICQQuality: %"PRIu16"; LookAheadDepth: %"PRIu16"\n",
info->ICQQuality, co2->LookAheadDepth);
}
#endif
#if QSV_HAVE_QVBR
else if (info->RateControlMethod == MFX_RATECONTROL_QVBR) {
av_log(avctx, AV_LOG_VERBOSE, "QVBRQuality: %"PRIu16"\n",
co3->QVBRQuality);
}
#endif
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));
#if QSV_HAVE_EXT_HEVC_TILES
if (avctx->codec_id == AV_CODEC_ID_HEVC)
av_log(avctx, AV_LOG_VERBOSE, "NumTileColumns: %"PRIu16"; NumTileRows: %"PRIu16"\n",
exthevctiles->NumTileColumns, exthevctiles->NumTileRows);
#endif
#if QSV_HAVE_CO2
av_log(avctx, AV_LOG_VERBOSE,
"RecoveryPointSEI: %s IntRefType: %"PRIu16"; IntRefCycleSize: %"PRIu16"; IntRefQPDelta: %"PRId16"\n",
print_threestate(co->RecoveryPointSEI), co2->IntRefType, co2->IntRefCycleSize, co2->IntRefQPDelta);
av_log(avctx, AV_LOG_VERBOSE, "MaxFrameSize: %d; ", co2->MaxFrameSize);
#if QSV_HAVE_MAX_SLICE_SIZE
av_log(avctx, AV_LOG_VERBOSE, "MaxSliceSize: %d; ", co2->MaxSliceSize);
#endif
av_log(avctx, AV_LOG_VERBOSE, "\n");
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 QSV_HAVE_TRELLIS
av_log(avctx, AV_LOG_VERBOSE, "Trellis: ");
if (co2->Trellis & MFX_TRELLIS_OFF) {
av_log(avctx, AV_LOG_VERBOSE, "off");
} else if (!co2->Trellis) {
av_log(avctx, AV_LOG_VERBOSE, "auto");
} else {
if (co2->Trellis & MFX_TRELLIS_I) av_log(avctx, AV_LOG_VERBOSE, "I");
if (co2->Trellis & MFX_TRELLIS_P) av_log(avctx, AV_LOG_VERBOSE, "P");
if (co2->Trellis & MFX_TRELLIS_B) av_log(avctx, AV_LOG_VERBOSE, "B");
}
av_log(avctx, AV_LOG_VERBOSE, "\n");
#endif
#if QSV_HAVE_VDENC
av_log(avctx, AV_LOG_VERBOSE, "VDENC: %s\n", print_threestate(info->LowPower));
#endif
#if QSV_VERSION_ATLEAST(1, 8)
av_log(avctx, AV_LOG_VERBOSE,
"RepeatPPS: %s; NumMbPerSlice: %"PRIu16"; LookAheadDS: ",
print_threestate(co2->RepeatPPS), co2->NumMbPerSlice);
switch (co2->LookAheadDS) {
case MFX_LOOKAHEAD_DS_OFF: av_log(avctx, AV_LOG_VERBOSE, "off"); break;
case MFX_LOOKAHEAD_DS_2x: av_log(avctx, AV_LOG_VERBOSE, "2x"); break;
case MFX_LOOKAHEAD_DS_4x: av_log(avctx, AV_LOG_VERBOSE, "4x"); break;
default: av_log(avctx, AV_LOG_VERBOSE, "unknown"); break;
}
av_log(avctx, AV_LOG_VERBOSE, "\n");
av_log(avctx, AV_LOG_VERBOSE, "AdaptiveI: %s; AdaptiveB: %s; BRefType: ",
print_threestate(co2->AdaptiveI), print_threestate(co2->AdaptiveB));
switch (co2->BRefType) {
case MFX_B_REF_OFF: av_log(avctx, AV_LOG_VERBOSE, "off"); break;
case MFX_B_REF_PYRAMID: av_log(avctx, AV_LOG_VERBOSE, "pyramid"); break;
default: av_log(avctx, AV_LOG_VERBOSE, "auto"); break;
}
av_log(avctx, AV_LOG_VERBOSE, "\n");
#endif
#if QSV_VERSION_ATLEAST(1, 9)
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);
#endif
#endif
#if QSV_HAVE_GPB
if (avctx->codec_id == AV_CODEC_ID_HEVC)
av_log(avctx, AV_LOG_VERBOSE,"GPB: %s\n", print_threestate(co3->GPB));
#endif
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);
}
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_la && !QSV_HAVE_LA) {
av_log(avctx, AV_LOG_ERROR,
"Lookahead ratecontrol mode requested, but is not supported by this SDK version\n");
return AVERROR(ENOSYS);
}
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 && avctx->global_quality > 0 && !QSV_HAVE_ICQ){
av_log(avctx, AV_LOG_ERROR,
"ICQ ratecontrol mode requested, but is not supported by this SDK version\n");
return AVERROR(ENOSYS);
}
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
#if QSV_HAVE_LA
else if (want_la) {
rc_mode = MFX_RATECONTROL_LA;
rc_desc = "VBR with lookahead (LA)";
#if QSV_HAVE_ICQ
if (avctx->global_quality > 0) {
rc_mode = MFX_RATECONTROL_LA_ICQ;
rc_desc = "intelligent constant quality with lookahead (LA_ICQ)";
}
#endif
}
#endif
#if QSV_HAVE_ICQ
else if (avctx->global_quality > 0 && !avctx->rc_max_rate) {
rc_mode = MFX_RATECONTROL_ICQ;
rc_desc = "intelligent constant quality (ICQ)";
}
#endif
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) {
rc_mode = MFX_RATECONTROL_AVBR;
rc_desc = "average variable bitrate (AVBR)";
}
#endif
#if QSV_HAVE_QVBR
else if (avctx->global_quality > 0) {
rc_mode = MFX_RATECONTROL_QVBR;
rc_desc = "constant quality with VBR algorithm (QVBR)";
}
#endif
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;
ff_qsv_map_pixfmt(sw_format, &q->param.mfx.FrameInfo.FourCC);
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;
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) {
#if QSV_HAVE_VDENC
q->param.mfx.LowPower = MFX_CODINGOPTION_ON;
#else
av_log(avctx, AV_LOG_WARNING, "The low_power option is "
"not supported with this MSDK version.\n");
q->low_power = 0;
q->param.mfx.LowPower = MFX_CODINGOPTION_OFF;
#endif
} 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;
ff_qsv_map_pixfmt(sw_format, &q->param.mfx.FrameInfo.FourCC);
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 QSV_HAVE_VCM
case MFX_RATECONTROL_VCM:
#endif
#if QSV_HAVE_QVBR
case MFX_RATECONTROL_QVBR:
#endif
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 QSV_HAVE_QVBR
if (q->param.mfx.RateControlMethod == MFX_RATECONTROL_QVBR)
q->extco3.QVBRQuality = av_clip(avctx->global_quality, 0, 51);
#endif
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);
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
#if QSV_HAVE_LA
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;
#if QSV_HAVE_ICQ
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;
#endif
#endif
}
// 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 QSV_HAVE_CO2
if (avctx->codec_id == AV_CODEC_ID_H264) {
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->bitrate_limit >= 0)
q->extco2.BitrateLimit = q->bitrate_limit ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
if (q->mbbrc >= 0)
q->extco2.MBBRC = q->mbbrc ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
if (q->max_frame_size >= 0)
q->extco2.MaxFrameSize = q->max_frame_size;
#if QSV_HAVE_MAX_SLICE_SIZE
if (q->max_slice_size >= 0)
q->extco2.MaxSliceSize = q->max_slice_size;
#endif
#if QSV_HAVE_TRELLIS
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;
#endif
#if QSV_VERSION_ATLEAST(1, 8)
q->extco2.LookAheadDS = q->look_ahead_downsampling;
q->extco2.RepeatPPS = q->repeat_pps ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
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;
#endif
}
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 QSV_VERSION_ATLEAST(1, 9)
if (avctx->qmin >= 0 && avctx->qmax >= 0 && avctx->qmin > avctx->qmax) {
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;
}
#endif
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;
}
#endif
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
}
#if QSV_HAVE_CO3
q->extco3.Header.BufferId = MFX_EXTBUFF_CODING_OPTION3;
q->extco3.Header.BufferSz = sizeof(q->extco3);
#if QSV_HAVE_GPB
if (avctx->codec_id == AV_CODEC_ID_HEVC)
q->extco3.GPB = q->gpb ? MFX_CODINGOPTION_ON : MFX_CODINGOPTION_OFF;
#endif
q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extco3;
#endif
}
#if QSV_HAVE_EXT_VP9_PARAM
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;
q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extvp9param;
}
#endif
#if QSV_HAVE_EXT_HEVC_TILES
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;
}
#endif
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 (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;
return 0;
}
static int qsv_retrieve_enc_vp9_params(AVCodecContext *avctx, QSVEncContext *q)
{
int ret = 0;
#if QSV_HAVE_EXT_VP9_PARAM
mfxExtVP9Param vp9_extend_buf = {
.Header.BufferId = MFX_EXTBUFF_VP9_PARAM,
.Header.BufferSz = sizeof(vp9_extend_buf),
};
#endif
#if QSV_HAVE_CO2
mfxExtCodingOption2 co2 = {
.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2,
.Header.BufferSz = sizeof(co2),
};
#endif
#if QSV_HAVE_CO3
mfxExtCodingOption3 co3 = {
.Header.BufferId = MFX_EXTBUFF_CODING_OPTION3,
.Header.BufferSz = sizeof(co3),
};
#endif
mfxExtBuffer *ext_buffers[] = {
#if QSV_HAVE_EXT_VP9_PARAM
(mfxExtBuffer*)&vp9_extend_buf,
#endif
#if QSV_HAVE_CO2
(mfxExtBuffer*)&co2,
#endif
#if QSV_HAVE_CO3
(mfxExtBuffer*)&co3,
#endif
};
q->param.ExtParam = ext_buffers;
q->param.NumExtParam = FF_ARRAY_ELEMS(ext_buffers);
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;
return 0;
}
static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q)
{
AVCPBProperties *cpb_props;
uint8_t sps_buf[128];
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),
};
#if QSV_HAVE_CO2
mfxExtCodingOption2 co2 = {
.Header.BufferId = MFX_EXTBUFF_CODING_OPTION2,
.Header.BufferSz = sizeof(co2),
};
#endif
#if QSV_HAVE_CO3
mfxExtCodingOption3 co3 = {
.Header.BufferId = MFX_EXTBUFF_CODING_OPTION3,
.Header.BufferSz = sizeof(co3),
};
#endif
#if QSV_HAVE_CO_VPS
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),
};
#endif
#if QSV_HAVE_EXT_HEVC_TILES
mfxExtHEVCTiles hevc_tile_buf = {
.Header.BufferId = MFX_EXTBUFF_HEVC_TILES,
.Header.BufferSz = sizeof(hevc_tile_buf),
};
#endif
mfxExtBuffer *ext_buffers[2 + QSV_HAVE_CO2 + QSV_HAVE_CO3 + QSV_HAVE_CO_VPS + QSV_HAVE_EXT_HEVC_TILES];
int need_pps = avctx->codec_id != AV_CODEC_ID_MPEG2VIDEO;
int ret, ext_buf_num = 0, extradata_offset = 0;
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&extradata;
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&co;
#if QSV_HAVE_CO2
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&co2;
#endif
#if QSV_HAVE_CO3
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&co3;
#endif
#if QSV_HAVE_CO_VPS
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;
#endif
#if QSV_HAVE_EXT_HEVC_TILES
if (avctx->codec_id == AV_CODEC_ID_HEVC)
ext_buffers[ext_buf_num++] = (mfxExtBuffer*)&hevc_tile_buf;
#endif
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)
#if QSV_HAVE_CO_VPS
|| (q->hevc_vps && !extradata_vps.VPSBufSize)
#endif
) {
av_log(avctx, AV_LOG_ERROR, "No extradata returned from libmfx.\n");
return AVERROR_UNKNOWN;
}
avctx->extradata_size = extradata.SPSBufSize + need_pps * extradata.PPSBufSize;
#if QSV_HAVE_CO_VPS
avctx->extradata_size += q->hevc_vps * extradata_vps.VPSBufSize;
#endif
avctx->extradata = av_malloc(avctx->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
if (!avctx->extradata)
return AVERROR(ENOMEM);
#if QSV_HAVE_CO_VPS
if (q->hevc_vps) {
memcpy(avctx->extradata, vps_buf, extradata_vps.VPSBufSize);
extradata_offset += extradata_vps.VPSBufSize;
}
#endif
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 + 1);
return 0;
}
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;
}
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,
q->param.IOPattern == MFX_IOPATTERN_IN_OPAQUE_MEMORY,
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;
}
static inline unsigned int qsv_fifo_item_size(void)
{
return sizeof(AVPacket) + sizeof(mfxSyncPoint*) + sizeof(mfxBitstream*);
}
static inline unsigned int qsv_fifo_size(const AVFifoBuffer* fifo)
{
return av_fifo_size(fifo)/qsv_fifo_item_size();
}
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_alloc(q->async_depth * qsv_fifo_item_size());
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 (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;
}
}
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_mallocz_array(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) {
ret = qsv_init_opaque_alloc(avctx, q);
if (ret < 0)
return ret;
}
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_free(enc_ctrl->Payload[i]);
}
enc_ctrl->NumPayload = 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);
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 = av_mallocz(sizeof(mfxPayload*) * QSV_MAX_ENC_PAYLOAD);
if (!frame->enc_ctrl.Payload) {
av_freep(&frame);
return AVERROR(ENOMEM);
}
*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 {
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;
qf->surface.Data.PitchLow = qf->frame->linesize[0];
qf->surface.Data.Y = qf->frame->data[0];
qf->surface.Data.UV = qf->frame->data[1];
}
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 encode_frame(AVCodecContext *avctx, QSVEncContext *q,
const AVFrame *frame)
{
AVPacket new_pkt = { 0 };
mfxBitstream *bs;
#if QSV_VERSION_ATLEAST(1, 26)
mfxExtAVCEncodedFrameInfo *enc_info;
mfxExtBuffer **enc_buf;
#endif
mfxFrameSurface1 *surf = NULL;
mfxSyncPoint *sync = 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(&new_pkt, q->packet_size);
if (ret < 0) {
av_log(avctx, AV_LOG_ERROR, "Error allocating the output packet\n");
return ret;
}
bs = av_mallocz(sizeof(*bs));
if (!bs) {
av_packet_unref(&new_pkt);
return AVERROR(ENOMEM);
}
bs->Data = new_pkt.data;
bs->MaxLength = new_pkt.size;
#if QSV_VERSION_ATLEAST(1, 26)
if (avctx->codec_id == AV_CODEC_ID_H264) {
enc_info = av_mallocz(sizeof(*enc_info));
if (!enc_info)
return AVERROR(ENOMEM);
enc_info->Header.BufferId = MFX_EXTBUFF_ENCODED_FRAME_INFO;
enc_info->Header.BufferSz = sizeof (*enc_info);
bs->NumExtParam = 1;
enc_buf = av_mallocz(sizeof(mfxExtBuffer *));
if (!enc_buf)
return AVERROR(ENOMEM);
enc_buf[0] = (mfxExtBuffer *)enc_info;
bs->ExtParam = enc_buf;
}
#endif
if (q->set_encode_ctrl_cb) {
q->set_encode_ctrl_cb(avctx, frame, &qsv_frame->enc_ctrl);
}
sync = av_mallocz(sizeof(*sync));
if (!sync) {
av_freep(&bs);
#if QSV_VERSION_ATLEAST(1, 26)
if (avctx->codec_id == AV_CODEC_ID_H264) {
av_freep(&enc_info);
av_freep(&enc_buf);
}
#endif
av_packet_unref(&new_pkt);
return AVERROR(ENOMEM);
}
do {
ret = MFXVideoENCODE_EncodeFrameAsync(q->session, enc_ctrl, surf, bs, 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) {
av_packet_unref(&new_pkt);
av_freep(&bs);
#if QSV_VERSION_ATLEAST(1, 26)
if (avctx->codec_id == AV_CODEC_ID_H264) {
av_freep(&enc_info);
av_freep(&enc_buf);
}
#endif
av_freep(&sync);
return (ret == MFX_ERR_MORE_DATA) ?
0 : ff_qsv_print_error(avctx, ret, "Error during encoding");
}
if (ret == MFX_WRN_INCOMPATIBLE_VIDEO_PARAM && frame->interlaced_frame)
print_interlace_msg(avctx, q);
if (*sync) {
av_fifo_generic_write(q->async_fifo, &new_pkt, sizeof(new_pkt), NULL);
av_fifo_generic_write(q->async_fifo, &sync, sizeof(sync), NULL);
av_fifo_generic_write(q->async_fifo, &bs, sizeof(bs), NULL);
} else {
av_freep(&sync);
av_packet_unref(&new_pkt);
av_freep(&bs);
#if QSV_VERSION_ATLEAST(1, 26)
if (avctx->codec_id == AV_CODEC_ID_H264) {
av_freep(&enc_info);
av_freep(&enc_buf);
}
#endif
}
return 0;
}
int ff_qsv_encode(AVCodecContext *avctx, QSVEncContext *q,
AVPacket *pkt, const AVFrame *frame, int *got_packet)
{
int ret;
ret = encode_frame(avctx, q, frame);
if (ret < 0)
return ret;
if ((qsv_fifo_size(q->async_fifo) >= q->async_depth) ||
(!frame && av_fifo_size(q->async_fifo))) {
AVPacket new_pkt;
mfxBitstream *bs;
mfxSyncPoint *sync;
#if QSV_VERSION_ATLEAST(1, 26)
mfxExtAVCEncodedFrameInfo *enc_info;
mfxExtBuffer **enc_buf;
#endif
enum AVPictureType pict_type;
av_fifo_generic_read(q->async_fifo, &new_pkt, sizeof(new_pkt), NULL);
av_fifo_generic_read(q->async_fifo, &sync, sizeof(sync), NULL);
av_fifo_generic_read(q->async_fifo, &bs, sizeof(bs), NULL);
do {
ret = MFXVideoCORE_SyncOperation(q->session, *sync, 1000);
} while (ret == MFX_WRN_IN_EXECUTION);
new_pkt.dts = av_rescale_q(bs->DecodeTimeStamp, (AVRational){1, 90000}, avctx->time_base);
new_pkt.pts = av_rescale_q(bs->TimeStamp, (AVRational){1, 90000}, avctx->time_base);
new_pkt.size = bs->DataLength;
if (bs->FrameType & MFX_FRAMETYPE_IDR || bs->FrameType & MFX_FRAMETYPE_xIDR) {
new_pkt.flags |= AV_PKT_FLAG_KEY;
pict_type = AV_PICTURE_TYPE_I;
} else if (bs->FrameType & MFX_FRAMETYPE_I || bs->FrameType & MFX_FRAMETYPE_xI)
pict_type = AV_PICTURE_TYPE_I;
else if (bs->FrameType & MFX_FRAMETYPE_P || bs->FrameType & MFX_FRAMETYPE_xP)
pict_type = AV_PICTURE_TYPE_P;
else if (bs->FrameType & MFX_FRAMETYPE_B || bs->FrameType & MFX_FRAMETYPE_xB)
pict_type = AV_PICTURE_TYPE_B;
else if (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", bs->FrameType);
return AVERROR_INVALIDDATA;
}
#if QSV_VERSION_ATLEAST(1, 26)
if (avctx->codec_id == AV_CODEC_ID_H264) {
enc_buf = bs->ExtParam;
enc_info = (mfxExtAVCEncodedFrameInfo *)(*bs->ExtParam);
ff_side_data_set_encoder_stats(&new_pkt,
enc_info->QP * FF_QP2LAMBDA, NULL, 0, pict_type);
av_freep(&enc_info);
av_freep(&enc_buf);
}
#endif
av_freep(&bs);
av_freep(&sync);
if (pkt->data) {
if (pkt->size < new_pkt.size) {
av_log(avctx, AV_LOG_ERROR, "Submitted buffer not large enough: %d < %d\n",
pkt->size, new_pkt.size);
av_packet_unref(&new_pkt);
return AVERROR(EINVAL);
}
memcpy(pkt->data, new_pkt.data, new_pkt.size);
pkt->size = new_pkt.size;
ret = av_packet_copy_props(pkt, &new_pkt);
av_packet_unref(&new_pkt);
if (ret < 0)
return ret;
} else
*pkt = new_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);
av_free(cur->enc_ctrl.Payload);
av_freep(&cur);
cur = q->work_frames;
}
while (q->async_fifo && av_fifo_size(q->async_fifo)) {
AVPacket pkt;
mfxSyncPoint *sync;
mfxBitstream *bs;
av_fifo_generic_read(q->async_fifo, &pkt, sizeof(pkt), NULL);
av_fifo_generic_read(q->async_fifo, &sync, sizeof(sync), NULL);
av_fifo_generic_read(q->async_fifo, &bs, sizeof(bs), NULL);
av_freep(&sync);
av_freep(&bs);
av_packet_unref(&pkt);
}
av_fifo_free(q->async_fifo);
q->async_fifo = NULL;
av_freep(&q->opaque_surfaces);
av_buffer_unref(&q->opaque_alloc_buf);
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,
};