summaryrefslogtreecommitdiff
path: root/libavcodec/qsvenc.c
diff options
context:
space:
mode:
Diffstat (limited to 'libavcodec/qsvenc.c')
-rw-r--r--libavcodec/qsvenc.c130
1 files changed, 90 insertions, 40 deletions
diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c
index 85af146ad8..5eb506fb76 100644
--- a/libavcodec/qsvenc.c
+++ b/libavcodec/qsvenc.c
@@ -4,20 +4,20 @@
* copyright (c) 2013 Yukinori Yamazoe
* copyright (c) 2015 Anton Khirnov
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * 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.
*
- * Libav is distributed in the hope that it will be useful,
+ * 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 Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
@@ -32,6 +32,7 @@
#include "libavutil/log.h"
#include "libavutil/time.h"
#include "libavutil/imgutils.h"
+#include "libavcodec/bytestream.h"
#include "avcodec.h"
#include "internal.h"
@@ -133,9 +134,6 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q,
#if QSV_HAVE_CO2
mfxExtCodingOption2 *co2 = (mfxExtCodingOption2*)coding_opts[1];
#endif
-#if QSV_HAVE_CO3
- mfxExtCodingOption3 *co3 = (mfxExtCodingOption3*)coding_opts[2];
-#endif
av_log(avctx, AV_LOG_VERBOSE, "profile: %s; level: %"PRIu16"\n",
print_profile(info->CodecProfile), info->CodecLevel);
@@ -187,12 +185,6 @@ static void dump_video_param(AVCodecContext *avctx, QSVEncContext *q,
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);
@@ -273,7 +265,7 @@ static int select_rc_mode(AVCodecContext *avctx, QSVEncContext *q)
const char *rc_desc;
mfxU16 rc_mode;
- int want_la = q->la_depth >= 10;
+ int want_la = q->look_ahead;
int want_qscale = !!(avctx->flags & AV_CODEC_FLAG_QSCALE);
int want_vcm = q->vcm;
@@ -366,8 +358,6 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q)
return AVERROR_BUG;
q->param.mfx.CodecId = ret;
- q->width_align = avctx->codec_id == AV_CODEC_ID_HEVC ? 32 : 16;
-
if (avctx->level > 0)
q->param.mfx.CodecLevel = avctx->level;
@@ -389,20 +379,39 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q)
ff_qsv_map_pixfmt(sw_format, &q->param.mfx.FrameInfo.FourCC);
- q->param.mfx.FrameInfo.Width = FFALIGN(avctx->width, q->width_align);
- q->param.mfx.FrameInfo.Height = FFALIGN(avctx->height, 32);
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.PicStruct = MFX_PICSTRUCT_PROGRESSIVE;
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;
+ // TODO: detect version of MFX--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 ? 32 : 16;
+ 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;
@@ -448,11 +457,11 @@ static int init_video_param(AVCodecContext *avctx, QSVEncContext *q)
#if QSV_HAVE_LA
case MFX_RATECONTROL_LA:
q->param.mfx.TargetKbps = avctx->bit_rate / 1000;
- q->extco2.LookAheadDepth = q->la_depth;
+ q->extco2.LookAheadDepth = q->look_ahead_depth;
break;
#if QSV_HAVE_ICQ
case MFX_RATECONTROL_LA_ICQ:
- q->extco2.LookAheadDepth = q->la_depth;
+ q->extco2.LookAheadDepth = q->look_ahead_depth;
case MFX_RATECONTROL_ICQ:
q->param.mfx.ICQQuality = avctx->global_quality;
break;
@@ -474,6 +483,9 @@ FF_ENABLE_DEPRECATION_WARNINGS
q->extco.CAVLC = q->cavlc ? MFX_CODINGOPTION_ON
: MFX_CODINGOPTION_UNKNOWN;
+ 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;
@@ -537,6 +549,10 @@ FF_ENABLE_DEPRECATION_WARNINGS
#endif
q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->extco2;
+
+#if QSV_HAVE_LA_DS
+ q->extco2.LookAheadDS = q->look_ahead_downsampling;
+#endif
}
#endif
}
@@ -575,12 +591,6 @@ static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q)
.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[] = {
(mfxExtBuffer*)&extradata,
@@ -588,9 +598,6 @@ static int qsv_retrieve_enc_params(AVCodecContext *avctx, QSVEncContext *q)
#if QSV_HAVE_CO2
(mfxExtBuffer*)&co2,
#endif
-#if QSV_HAVE_CO3
- (mfxExtBuffer*)&co3,
-#endif
};
int need_pps = avctx->codec_id != AV_CODEC_ID_MPEG2VIDEO;
@@ -684,15 +691,22 @@ static int qsvenc_init_session(AVCodecContext *avctx, QSVEncContext *q)
if (!q->frames_ctx.hw_frames_ctx)
return AVERROR(ENOMEM);
- ret = ff_qsv_init_session_hwcontext(avctx, &q->internal_session,
- &q->frames_ctx, q->load_plugins,
- q->param.IOPattern == MFX_IOPATTERN_IN_OPAQUE_MEMORY);
+ ret = ff_qsv_init_session_frames(avctx, &q->internal_session,
+ &q->frames_ctx, q->load_plugins,
+ q->param.IOPattern == MFX_IOPATTERN_IN_OPAQUE_MEMORY);
if (ret < 0) {
av_buffer_unref(&q->frames_ctx.hw_frames_ctx);
return ret;
}
q->session = q->internal_session;
+ } else if (avctx->hw_device_ctx) {
+ ret = ff_qsv_init_session_device(avctx, &q->internal_session,
+ avctx->hw_device_ctx, q->load_plugins);
+ if (ret < 0)
+ return ret;
+
+ q->session = q->internal_session;
} else {
ret = ff_qsv_init_internal_session(avctx, &q->internal_session,
q->load_plugins);
@@ -750,10 +764,18 @@ int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q)
if (ret < 0)
return ret;
+ 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 the encoding parameters");
+ "Error querying (IOSurf) the encoding parameters");
if (opaque_alloc) {
ret = qsv_init_opaque_alloc(avctx, q);
@@ -809,11 +831,23 @@ int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q)
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);
av_frame_unref(cur->frame);
cur->used = 0;
}
@@ -848,6 +882,11 @@ static int get_free_frame(QSVEncContext *q, QSVFrame **f)
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;
@@ -857,7 +896,7 @@ static int get_free_frame(QSVEncContext *q, QSVFrame **f)
}
static int submit_frame(QSVEncContext *q, const AVFrame *frame,
- mfxFrameSurface1 **surface)
+ QSVFrame **new_frame)
{
QSVFrame *qf;
int ret;
@@ -883,7 +922,7 @@ static int submit_frame(QSVEncContext *q, const AVFrame *frame,
} else {
/* make a copy if the input is not padded as libmfx requires */
if (frame->height & 31 || frame->linesize[0] & (q->width_align - 1)) {
- qf->frame->height = FFALIGN(frame->height, 32);
+ qf->frame->height = FFALIGN(frame->height, q->height_align);
qf->frame->width = FFALIGN(frame->width, q->width_align);
ret = ff_get_buffer(q->avctx, qf->frame, AV_GET_BUFFER_FLAG_REF);
@@ -923,7 +962,7 @@ static int submit_frame(QSVEncContext *q, const AVFrame *frame,
qf->surface.Data.TimeStamp = av_rescale_q(frame->pts, q->avctx->time_base, (AVRational){1, 90000});
- *surface = &qf->surface;
+ *new_frame = qf;
return 0;
}
@@ -948,15 +987,21 @@ static int encode_frame(AVCodecContext *avctx, QSVEncContext *q,
mfxFrameSurface1 *surf = NULL;
mfxSyncPoint *sync = NULL;
+ QSVFrame *qsv_frame = NULL;
+ mfxEncodeCtrl* enc_ctrl = NULL;
int ret;
if (frame) {
- ret = submit_frame(q, frame, &surf);
+ 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;
+ }
ret = av_new_packet(&new_pkt, q->packet_size);
if (ret < 0) {
@@ -972,6 +1017,10 @@ static int encode_frame(AVCodecContext *avctx, QSVEncContext *q,
bs->Data = new_pkt.data;
bs->MaxLength = new_pkt.size;
+ 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);
@@ -980,9 +1029,9 @@ static int encode_frame(AVCodecContext *avctx, QSVEncContext *q,
}
do {
- ret = MFXVideoENCODE_EncodeFrameAsync(q->session, NULL, surf, bs, sync);
+ ret = MFXVideoENCODE_EncodeFrameAsync(q->session, enc_ctrl, surf, bs, sync);
if (ret == MFX_WRN_DEVICE_BUSY)
- av_usleep(1);
+ av_usleep(500);
} while (ret == MFX_WRN_DEVICE_BUSY || ret == MFX_WRN_IN_EXECUTION);
if (ret > 0)
@@ -1099,6 +1148,7 @@ int ff_qsv_enc_close(AVCodecContext *avctx, QSVEncContext *q)
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;
}