lavc/qsvdec: Replace current parser with MFXVideoDECODE_DecodeHeader()

Using MSDK parser can improve qsv decoder pass rate in some cases (E.g:
sps declares a wrong level_idc, smaller than it should be).
And it is necessary for adding new qsv decoders such as MJPEG and VP9
since current parser can't provide enough information.
Actually using MFXVideoDECODE_DecodeHeader() was disscussed at
https://ffmpeg.org/pipermail/ffmpeg-devel/2015-July/175734.html and merged as commit 1acb19d,
but was overwritten when merged libav patches (commit: 1f26a23) without any explain.

Split decode header from decode_init, and call it for everyframe to
detect format/resoultion change. It can fix some regression issues such
as hevc 10bits decoding.

Signed-off-by: Zhong Li <zhong.li@intel.com>
Signed-off-by: Dmitry Rogozhkin <dmitry.v.rogozhkin@intel.com>
This commit is contained in:
Zhong Li 2019-08-13 14:11:09 +08:00
parent f3dfd34f27
commit 00d0a4aa9e
4 changed files with 100 additions and 88 deletions

View File

@ -147,19 +147,21 @@ static int check_dec_param(AVCodecContext *avctx, QSVContext *q, mfxVideoParam *
return 1; return 1;
} }
static int qsv_decode_init(AVCodecContext *avctx, QSVContext *q) static int qsv_decode_preinit(AVCodecContext *avctx, QSVContext *q, enum AVPixelFormat pix_fmt, mfxVideoParam *param)
{ {
const AVPixFmtDescriptor *desc;
mfxSession session = NULL; mfxSession session = NULL;
int iopattern = 0; int iopattern = 0;
mfxVideoParam param = { 0 };
int frame_width = avctx->coded_width;
int frame_height = avctx->coded_height;
int ret; int ret;
enum AVPixelFormat pix_fmts[3] = {
AV_PIX_FMT_QSV, /* opaque format in case of video memory output */
pix_fmt, /* system memory format obtained from bitstream parser */
AV_PIX_FMT_NONE };
desc = av_pix_fmt_desc_get(avctx->sw_pix_fmt); ret = ff_get_format(avctx, pix_fmts);
if (!desc) if (ret < 0) {
return AVERROR_BUG; q->orig_pix_fmt = avctx->pix_fmt = AV_PIX_FMT_NONE;
return ret;
}
if (!q->async_fifo) { if (!q->async_fifo) {
q->async_fifo = av_fifo_alloc(q->async_depth * qsv_fifo_item_size()); q->async_fifo = av_fifo_alloc(q->async_depth * qsv_fifo_item_size());
@ -197,54 +199,72 @@ static int qsv_decode_init(AVCodecContext *avctx, QSVContext *q)
return ret; return ret;
} }
ret = ff_qsv_codec_id_to_mfx(avctx->codec_id); param->IOPattern = q->iopattern;
if (ret < 0) param->AsyncDepth = q->async_depth;
return ret; param->ExtParam = q->ext_buffers;
param->NumExtParam = q->nb_ext_buffers;
param.mfx.CodecId = ret; return 0;
param.mfx.CodecProfile = ff_qsv_profile_to_mfx(avctx->codec_id, avctx->profile); }
param.mfx.CodecLevel = ff_qsv_level_to_mfx(avctx->codec_id, avctx->level);
param.mfx.FrameInfo.BitDepthLuma = desc->comp[0].depth; static int qsv_decode_init(AVCodecContext *avctx, QSVContext *q, mfxVideoParam *param)
param.mfx.FrameInfo.BitDepthChroma = desc->comp[0].depth; {
param.mfx.FrameInfo.Shift = desc->comp[0].depth > 8; int ret;
param.mfx.FrameInfo.FourCC = q->fourcc;
param.mfx.FrameInfo.Width = frame_width;
param.mfx.FrameInfo.Height = frame_height;
param.mfx.FrameInfo.ChromaFormat = MFX_CHROMAFORMAT_YUV420;
switch (avctx->field_order) { avctx->width = param->mfx.FrameInfo.CropW;
case AV_FIELD_PROGRESSIVE: avctx->height = param->mfx.FrameInfo.CropH;
param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_PROGRESSIVE; avctx->coded_width = param->mfx.FrameInfo.Width;
break; avctx->coded_height = param->mfx.FrameInfo.Height;
case AV_FIELD_TT: avctx->level = param->mfx.CodecLevel;
param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_FIELD_TFF; avctx->profile = param->mfx.CodecProfile;
break; avctx->field_order = ff_qsv_map_picstruct(param->mfx.FrameInfo.PicStruct);
case AV_FIELD_BB: avctx->pix_fmt = ff_qsv_map_fourcc(param->mfx.FrameInfo.FourCC);
param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_FIELD_BFF;
break;
default:
param.mfx.FrameInfo.PicStruct = MFX_PICSTRUCT_UNKNOWN;
break;
}
param.IOPattern = q->iopattern; ret = MFXVideoDECODE_Init(q->session, param);
param.AsyncDepth = q->async_depth;
param.ExtParam = q->ext_buffers;
param.NumExtParam = q->nb_ext_buffers;
if (!check_dec_param(avctx, q, &param)) {
//Just give a warning instead of an error since it is still decodable possibly.
av_log(avctx, AV_LOG_WARNING,
"Current input bitstream is not supported by QSV decoder.\n");
}
ret = MFXVideoDECODE_Init(q->session, &param);
if (ret < 0) if (ret < 0)
return ff_qsv_print_error(avctx, ret, return ff_qsv_print_error(avctx, ret,
"Error initializing the MFX video decoder"); "Error initializing the MFX video decoder");
q->frame_info = param.mfx.FrameInfo; q->frame_info = param->mfx.FrameInfo;
return 0;
}
static int qsv_decode_header(AVCodecContext *avctx, QSVContext *q, AVPacket *avpkt, enum AVPixelFormat pix_fmt, mfxVideoParam *param)
{
int ret;
mfxBitstream bs = { 0 };
if (avpkt->size) {
bs.Data = avpkt->data;
bs.DataLength = avpkt->size;
bs.MaxLength = bs.DataLength;
bs.TimeStamp = avpkt->pts;
if (avctx->field_order == AV_FIELD_PROGRESSIVE)
bs.DataFlag |= MFX_BITSTREAM_COMPLETE_FRAME;
} else
return AVERROR_INVALIDDATA;
if(!q->session) {
ret = qsv_decode_preinit(avctx, q, pix_fmt, param);
if (ret < 0)
return ret;
}
ret = ff_qsv_codec_id_to_mfx(avctx->codec_id);
if (ret < 0)
return ret;
param->mfx.CodecId = ret;
ret = MFXVideoDECODE_DecodeHeader(q->session, &bs, param);
if (MFX_ERR_MORE_DATA == ret) {
return AVERROR(EAGAIN);
}
if (ret < 0)
return ff_qsv_print_error(avctx, ret,
"Error decoding stream header");
return 0; return 0;
} }
@ -527,7 +547,8 @@ int ff_qsv_process_data(AVCodecContext *avctx, QSVContext *q,
uint8_t *dummy_data; uint8_t *dummy_data;
int dummy_size; int dummy_size;
int ret; int ret;
const AVPixFmtDescriptor *desc; mfxVideoParam param = { 0 };
enum AVPixelFormat pix_fmt = AV_PIX_FMT_NV12;
if (!q->avctx_internal) { if (!q->avctx_internal) {
q->avctx_internal = avcodec_alloc_context3(NULL); q->avctx_internal = avcodec_alloc_context3(NULL);
@ -541,7 +562,6 @@ int ff_qsv_process_data(AVCodecContext *avctx, QSVContext *q,
return AVERROR(ENOMEM); return AVERROR(ENOMEM);
q->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES; q->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
q->orig_pix_fmt = AV_PIX_FMT_NONE;
} }
if (!pkt->size) if (!pkt->size)
@ -554,15 +574,23 @@ int ff_qsv_process_data(AVCodecContext *avctx, QSVContext *q,
pkt->data, pkt->size, pkt->pts, pkt->dts, pkt->data, pkt->size, pkt->pts, pkt->dts,
pkt->pos); pkt->pos);
avctx->field_order = q->parser->field_order;
/* TODO: flush delayed frames on reinit */ /* TODO: flush delayed frames on reinit */
if (q->parser->format != q->orig_pix_fmt ||
FFALIGN(q->parser->coded_width, 16) != FFALIGN(avctx->coded_width, 16) || // sw_pix_fmt, coded_width/height should be set for ff_get_format(),
FFALIGN(q->parser->coded_height, 16) != FFALIGN(avctx->coded_height, 16)) { // assume sw_pix_fmt is NV12 and coded_width/height to be 1280x720,
enum AVPixelFormat pix_fmts[3] = { AV_PIX_FMT_QSV, // the assumption may be not corret but will be updated after header decoded if not true.
AV_PIX_FMT_NONE, if (q->orig_pix_fmt != AV_PIX_FMT_NONE)
AV_PIX_FMT_NONE }; pix_fmt = q->orig_pix_fmt;
enum AVPixelFormat qsv_format; if (!avctx->coded_width)
avctx->coded_width = 1280;
if (!avctx->coded_height)
avctx->coded_height = 720;
ret = qsv_decode_header(avctx, q, pkt, pix_fmt, &param);
if (ret >= 0 && (q->orig_pix_fmt != ff_qsv_map_fourcc(param.mfx.FrameInfo.FourCC) ||
avctx->coded_width != param.mfx.FrameInfo.Width ||
avctx->coded_height != param.mfx.FrameInfo.Height)) {
AVPacket zero_pkt = {0}; AVPacket zero_pkt = {0};
if (q->buffered_count) { if (q->buffered_count) {
@ -571,45 +599,24 @@ int ff_qsv_process_data(AVCodecContext *avctx, QSVContext *q,
q->buffered_count--; q->buffered_count--;
return qsv_decode(avctx, q, frame, got_frame, &zero_pkt); return qsv_decode(avctx, q, frame, got_frame, &zero_pkt);
} }
q->reinit_flag = 0; q->reinit_flag = 0;
qsv_format = ff_qsv_map_pixfmt(q->parser->format, &q->fourcc); q->orig_pix_fmt = avctx->pix_fmt = pix_fmt = ff_qsv_map_fourcc(param.mfx.FrameInfo.FourCC);
if (qsv_format < 0) {
av_log(avctx, AV_LOG_ERROR,
"Decoding pixel format '%s' is not supported\n",
av_get_pix_fmt_name(q->parser->format));
ret = AVERROR(ENOSYS);
goto reinit_fail;
}
q->orig_pix_fmt = q->parser->format; avctx->coded_width = param.mfx.FrameInfo.Width;
avctx->pix_fmt = pix_fmts[1] = qsv_format; avctx->coded_height = param.mfx.FrameInfo.Height;
avctx->width = q->parser->width;
avctx->height = q->parser->height;
avctx->coded_width = FFALIGN(q->parser->coded_width, 16);
avctx->coded_height = FFALIGN(q->parser->coded_height, 16);
avctx->level = q->avctx_internal->level;
avctx->profile = q->avctx_internal->profile;
ret = ff_get_format(avctx, pix_fmts); ret = qsv_decode_preinit(avctx, q, pix_fmt, &param);
if (ret < 0) if (ret < 0)
goto reinit_fail; goto reinit_fail;
q->initialized = 0;
}
avctx->pix_fmt = ret; if (!q->initialized) {
ret = qsv_decode_init(avctx, q, &param);
desc = av_pix_fmt_desc_get(avctx->pix_fmt);
if (!desc)
goto reinit_fail;
if (desc->comp[0].depth > 8) {
avctx->coded_width = FFALIGN(q->parser->coded_width, 32);
avctx->coded_height = FFALIGN(q->parser->coded_height, 32);
}
ret = qsv_decode_init(avctx, q);
if (ret < 0) if (ret < 0)
goto reinit_fail; goto reinit_fail;
q->initialized = 1;
} }
return qsv_decode(avctx, q, frame, got_frame, pkt); return qsv_decode(avctx, q, frame, got_frame, pkt);
@ -622,4 +629,5 @@ reinit_fail:
void ff_qsv_decode_flush(AVCodecContext *avctx, QSVContext *q) void ff_qsv_decode_flush(AVCodecContext *avctx, QSVContext *q)
{ {
q->orig_pix_fmt = AV_PIX_FMT_NONE; q->orig_pix_fmt = AV_PIX_FMT_NONE;
q->initialized = 0;
} }

View File

@ -63,6 +63,8 @@ typedef struct QSVContext {
uint32_t fourcc; uint32_t fourcc;
mfxFrameInfo frame_info; mfxFrameInfo frame_info;
int initialized;
// options set by the caller // options set by the caller
int async_depth; int async_depth;
int iopattern; int iopattern;

View File

@ -103,6 +103,7 @@ static av_cold int qsv_decode_init(AVCodecContext *avctx)
} }
} }
s->qsv.orig_pix_fmt = AV_PIX_FMT_NV12;
s->packet_fifo = av_fifo_alloc(sizeof(AVPacket)); s->packet_fifo = av_fifo_alloc(sizeof(AVPacket));
if (!s->packet_fifo) { if (!s->packet_fifo) {
ret = AVERROR(ENOMEM); ret = AVERROR(ENOMEM);

View File

@ -90,6 +90,7 @@ static av_cold int qsv_decode_init(AVCodecContext *avctx)
} }
#endif #endif
s->qsv.orig_pix_fmt = AV_PIX_FMT_NV12;
s->packet_fifo = av_fifo_alloc(sizeof(AVPacket)); s->packet_fifo = av_fifo_alloc(sizeof(AVPacket));
if (!s->packet_fifo) { if (!s->packet_fifo) {
ret = AVERROR(ENOMEM); ret = AVERROR(ENOMEM);