Commit c8241e73 authored by Mark Thompson's avatar Mark Thompson

vaapi_encode: Refactor initialisation

This allows better checking of capabilities and will make it easier
to add more functionality later.

It also commonises some duplicated code around rate control setup
and adds more comments explaining the internals.

(cherry picked from commit 80a5d051)
parent 06d73d00
This diff is collapsed.
......@@ -48,15 +48,6 @@ enum {
PICTURE_TYPE_B = 3,
};
enum {
// All encode operations are done independently.
ISSUE_MODE_SERIALISE_EVERYTHING = 0,
// Overlap as many operations as possible.
ISSUE_MODE_MAXIMISE_THROUGHPUT,
// Overlap operations only when satisfying parallel dependencies.
ISSUE_MODE_MINIMISE_LATENCY,
};
typedef struct VAAPIEncodeSlice {
void *priv_data;
void *codec_slice_params;
......@@ -102,43 +93,65 @@ typedef struct VAAPIEncodeContext {
// Codec-specific hooks.
const struct VAAPIEncodeType *codec;
// Encoding profile (VAProfileXXX).
VAProfile va_profile;
// Encoding entrypoint (usually VAEntryointEncSlice).
VAEntrypoint va_entrypoint;
// Surface colour/sampling format (usually VA_RT_FORMAT_YUV420).
unsigned int va_rt_format;
// Rate control mode.
unsigned int va_rc_mode;
// The required size of surfaces. This is probably the input
// size (AVCodecContext.width|height) aligned up to whatever
// block size is required by the codec.
int surface_width;
int surface_height;
// Everything above this point must be set before calling
// ff_vaapi_encode_init().
// Codec-specific state.
void *priv_data;
VAProfile va_profile;
VAEntrypoint va_entrypoint;
// Configuration attributes to use when creating va_config.
VAConfigAttrib config_attributes[MAX_CONFIG_ATTRIBUTES];
int nb_config_attributes;
VAConfigID va_config;
VAContextID va_context;
int va_rc_mode;
AVBufferRef *device_ref;
AVHWDeviceContext *device;
AVVAAPIDeviceContext *hwctx;
// The hardware frame context containing the input frames.
AVBufferRef *input_frames_ref;
AVHWFramesContext *input_frames;
// Input size, set from input frames.
int input_width;
int input_height;
// Aligned size, set by codec init, becomes hwframe size.
int aligned_width;
int aligned_height;
int nb_recon_frames;
// The hardware frame context containing the reconstructed frames.
AVBufferRef *recon_frames_ref;
AVHWFramesContext *recon_frames;
// Pool of (reusable) bitstream output buffers.
AVBufferPool *output_buffer_pool;
VAConfigAttrib config_attributes[MAX_CONFIG_ATTRIBUTES];
int nb_config_attributes;
// Global parameters which will be applied at the start of the
// sequence (includes rate control parameters below).
VAEncMiscParameterBuffer *global_params[MAX_GLOBAL_PARAMS];
size_t global_params_size[MAX_GLOBAL_PARAMS];
int nb_global_params;
// Rate control parameters.
struct {
VAEncMiscParameterBuffer misc;
VAEncMiscParameterRateControl rc;
} rc_params;
struct {
VAEncMiscParameterBuffer misc;
VAEncMiscParameterHRD hrd;
} hrd_params;
// Per-sequence parameter structure (VAEncSequenceParameterBuffer*).
void *codec_sequence_params;
......@@ -158,7 +171,15 @@ typedef struct VAAPIEncodeContext {
// Next output order index (encode order).
int64_t output_order;
int issue_mode;
enum {
// All encode operations are done independently (synchronise
// immediately after every operation).
ISSUE_MODE_SERIALISE_EVERYTHING = 0,
// Overlap as many operations as possible.
ISSUE_MODE_MAXIMISE_THROUGHPUT,
// Overlap operations only when satisfying parallel dependencies.
ISSUE_MODE_MINIMISE_LATENCY,
} issue_mode;
// Timestamp handling.
int64_t first_pts;
......@@ -185,13 +206,18 @@ typedef struct VAAPIEncodeContext {
typedef struct VAAPIEncodeType {
size_t priv_data_size;
int (*init)(AVCodecContext *avctx);
int (*close)(AVCodecContext *avctx);
// Perform any extra codec-specific configuration after the
// codec context is initialised (set up the private data and
// add any necessary global parameters).
int (*configure)(AVCodecContext *avctx);
// The size of the parameter structures:
// sizeof(VAEnc{type}ParameterBuffer{codec}).
size_t sequence_params_size;
size_t picture_params_size;
size_t slice_params_size;
// Fill the parameter structures.
int (*init_sequence_params)(AVCodecContext *avctx);
int (*init_picture_params)(AVCodecContext *avctx,
VAAPIEncodePicture *pic);
......@@ -199,10 +225,13 @@ typedef struct VAAPIEncodeType {
VAAPIEncodePicture *pic,
VAAPIEncodeSlice *slice);
// The type used by the packed header: this should look like
// VAEncPackedHeader{something}.
int sequence_header_type;
int picture_header_type;
int slice_header_type;
// Write the packed header data to the provided buffer.
int (*write_sequence_header)(AVCodecContext *avctx,
char *data, size_t *data_len);
int (*write_picture_header)(AVCodecContext *avctx,
......@@ -213,10 +242,18 @@ typedef struct VAAPIEncodeType {
VAAPIEncodeSlice *slice,
char *data, size_t *data_len);
// Fill an extra parameter structure, which will then be
// passed to vaRenderPicture(). Will be called repeatedly
// with increasing index argument until AVERROR_EOF is
// returned.
int (*write_extra_buffer)(AVCodecContext *avctx,
VAAPIEncodePicture *pic,
int index, int *type,
char *data, size_t *data_len);
// Write an extra packed header. Will be called repeatedly
// with increasing index argument until AVERROR_EOF is
// returned.
int (*write_extra_header)(AVCodecContext *avctx,
VAAPIEncodePicture *pic,
int index, int *type,
......@@ -227,8 +264,7 @@ typedef struct VAAPIEncodeType {
int ff_vaapi_encode2(AVCodecContext *avctx, AVPacket *pkt,
const AVFrame *input_image, int *got_packet);
int ff_vaapi_encode_init(AVCodecContext *avctx,
const VAAPIEncodeType *type);
int ff_vaapi_encode_init(AVCodecContext *avctx);
int ff_vaapi_encode_close(AVCodecContext *avctx);
#endif /* AVCODEC_VAAPI_ENCODE_H */
This diff is collapsed.
......@@ -798,8 +798,8 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
vseq->intra_idr_period = 0;
vseq->ip_period = 0;
vseq->pic_width_in_luma_samples = ctx->aligned_width;
vseq->pic_height_in_luma_samples = ctx->aligned_height;
vseq->pic_width_in_luma_samples = ctx->surface_width;
vseq->pic_height_in_luma_samples = ctx->surface_height;
vseq->seq_fields.bits.chroma_format_idc = 1; // 4:2:0.
vseq->seq_fields.bits.separate_colour_plane_flag = 0;
......@@ -911,15 +911,15 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
mseq->vps_poc_proportional_to_timing_flag = 1;
mseq->vps_num_ticks_poc_diff_minus1 = 0;
if (ctx->input_width != ctx->aligned_width ||
ctx->input_height != ctx->aligned_height) {
if (avctx->width != ctx->surface_width ||
avctx->height != ctx->surface_height) {
mseq->conformance_window_flag = 1;
mseq->conf_win_left_offset = 0;
mseq->conf_win_right_offset =
(ctx->aligned_width - ctx->input_width) / 2;
(ctx->surface_width - avctx->width) / 2;
mseq->conf_win_top_offset = 0;
mseq->conf_win_bottom_offset =
(ctx->aligned_height - ctx->input_height) / 2;
(ctx->surface_height - avctx->height) / 2;
} else {
mseq->conformance_window_flag = 0;
}
......@@ -1154,68 +1154,20 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx,
return 0;
}
static av_cold int vaapi_encode_h265_init_constant_bitrate(AVCodecContext *avctx)
static av_cold int vaapi_encode_h265_configure(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
VAAPIEncodeH265Context *priv = ctx->priv_data;
int hrd_buffer_size;
int hrd_initial_buffer_fullness;
if (avctx->bit_rate > INT32_MAX) {
av_log(avctx, AV_LOG_ERROR, "Target bitrate of 2^31 bps or "
"higher is not supported.\n");
return AVERROR(EINVAL);
}
if (avctx->rc_buffer_size)
hrd_buffer_size = avctx->rc_buffer_size;
else
hrd_buffer_size = avctx->bit_rate;
if (avctx->rc_initial_buffer_occupancy)
hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy;
else
hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4;
priv->rc_params.misc.type = VAEncMiscParameterTypeRateControl;
priv->rc_params.rc = (VAEncMiscParameterRateControl) {
.bits_per_second = avctx->bit_rate,
.target_percentage = 66,
.window_size = 1000,
.initial_qp = (avctx->qmax >= 0 ? avctx->qmax : 40),
.min_qp = (avctx->qmin >= 0 ? avctx->qmin : 20),
.basic_unit_size = 0,
};
ctx->global_params[ctx->nb_global_params] =
&priv->rc_params.misc;
ctx->global_params_size[ctx->nb_global_params++] =
sizeof(priv->rc_params);
priv->hrd_params.misc.type = VAEncMiscParameterTypeHRD;
priv->hrd_params.hrd = (VAEncMiscParameterHRD) {
.initial_buffer_fullness = hrd_initial_buffer_fullness,
.buffer_size = hrd_buffer_size,
};
ctx->global_params[ctx->nb_global_params] =
&priv->hrd_params.misc;
ctx->global_params_size[ctx->nb_global_params++] =
sizeof(priv->hrd_params);
// These still need to be set for pic_init_qp/slice_qp_delta.
priv->fixed_qp_idr = 30;
priv->fixed_qp_p = 30;
priv->fixed_qp_b = 30;
VAAPIEncodeH265Options *opt = ctx->codec_options;
av_log(avctx, AV_LOG_DEBUG, "Using constant-bitrate = %"PRId64" bps.\n",
avctx->bit_rate);
return 0;
}
priv->ctu_width = FFALIGN(ctx->surface_width, 32) / 32;
priv->ctu_height = FFALIGN(ctx->surface_height, 32) / 32;
static av_cold int vaapi_encode_h265_init_fixed_qp(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
VAAPIEncodeH265Context *priv = ctx->priv_data;
VAAPIEncodeH265Options *opt = ctx->codec_options;
av_log(avctx, AV_LOG_VERBOSE, "Input %ux%u -> Surface %ux%u -> CTU %ux%u.\n",
avctx->width, avctx->height, ctx->surface_width,
ctx->surface_height, priv->ctu_width, priv->ctu_height);
if (ctx->va_rc_mode == VA_RC_CQP) {
priv->fixed_qp_p = opt->qp;
if (avctx->i_quant_factor > 0.0)
priv->fixed_qp_idr = (int)((priv->fixed_qp_p * avctx->i_quant_factor +
......@@ -1231,79 +1183,27 @@ static av_cold int vaapi_encode_h265_init_fixed_qp(AVCodecContext *avctx)
av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = "
"%d / %d / %d for IDR- / P- / B-frames.\n",
priv->fixed_qp_idr, priv->fixed_qp_p, priv->fixed_qp_b);
return 0;
}
static av_cold int vaapi_encode_h265_init_internal(AVCodecContext *avctx)
{
static const VAConfigAttrib default_config_attributes[] = {
{ .type = VAConfigAttribRTFormat,
.value = VA_RT_FORMAT_YUV420 },
{ .type = VAConfigAttribEncPackedHeaders,
.value = (VA_ENC_PACKED_HEADER_SEQUENCE |
VA_ENC_PACKED_HEADER_SLICE) },
};
VAAPIEncodeContext *ctx = avctx->priv_data;
VAAPIEncodeH265Context *priv = ctx->priv_data;
int i, err;
switch (avctx->profile) {
case FF_PROFILE_HEVC_MAIN:
case FF_PROFILE_UNKNOWN:
ctx->va_profile = VAProfileHEVCMain;
break;
case FF_PROFILE_HEVC_MAIN_10:
av_log(avctx, AV_LOG_ERROR, "H.265 main 10-bit profile "
"is not supported.\n");
return AVERROR_PATCHWELCOME;
default:
av_log(avctx, AV_LOG_ERROR, "Unknown H.265 profile %d.\n",
avctx->profile);
return AVERROR(EINVAL);
}
ctx->va_entrypoint = VAEntrypointEncSlice;
ctx->input_width = avctx->width;
ctx->input_height = avctx->height;
ctx->aligned_width = FFALIGN(ctx->input_width, 16);
ctx->aligned_height = FFALIGN(ctx->input_height, 16);
priv->ctu_width = FFALIGN(ctx->aligned_width, 32) / 32;
priv->ctu_height = FFALIGN(ctx->aligned_height, 32) / 32;
av_log(avctx, AV_LOG_VERBOSE, "Input %ux%u -> Aligned %ux%u -> CTU %ux%u.\n",
ctx->input_width, ctx->input_height, ctx->aligned_width,
ctx->aligned_height, priv->ctu_width, priv->ctu_height);
} else if (ctx->va_rc_mode == VA_RC_CBR) {
// These still need to be set for pic_init_qp/slice_qp_delta.
priv->fixed_qp_idr = 30;
priv->fixed_qp_p = 30;
priv->fixed_qp_b = 30;
for (i = 0; i < FF_ARRAY_ELEMS(default_config_attributes); i++) {
ctx->config_attributes[ctx->nb_config_attributes++] =
default_config_attributes[i];
}
av_log(avctx, AV_LOG_DEBUG, "Using constant-bitrate = %d bps.\n",
avctx->bit_rate);
if (avctx->bit_rate > 0) {
ctx->va_rc_mode = VA_RC_CBR;
err = vaapi_encode_h265_init_constant_bitrate(avctx);
} else {
ctx->va_rc_mode = VA_RC_CQP;
err = vaapi_encode_h265_init_fixed_qp(avctx);
av_assert0(0 && "Invalid RC mode.");
}
if (err < 0)
return err;
ctx->config_attributes[ctx->nb_config_attributes++] = (VAConfigAttrib) {
.type = VAConfigAttribRateControl,
.value = ctx->va_rc_mode,
};
ctx->nb_recon_frames = 20;
return 0;
}
static VAAPIEncodeType vaapi_encode_type_h265 = {
static const VAAPIEncodeType vaapi_encode_type_h265 = {
.priv_data_size = sizeof(VAAPIEncodeH265Context),
.init = &vaapi_encode_h265_init_internal,
.configure = &vaapi_encode_h265_configure,
.sequence_params_size = sizeof(VAEncSequenceParameterBufferHEVC),
.init_sequence_params = &vaapi_encode_h265_init_sequence_params,
......@@ -1323,7 +1223,39 @@ static VAAPIEncodeType vaapi_encode_type_h265 = {
static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx)
{
return ff_vaapi_encode_init(avctx, &vaapi_encode_type_h265);
VAAPIEncodeContext *ctx = avctx->priv_data;
ctx->codec = &vaapi_encode_type_h265;
switch (avctx->profile) {
case FF_PROFILE_HEVC_MAIN:
case FF_PROFILE_UNKNOWN:
ctx->va_profile = VAProfileHEVCMain;
break;
case FF_PROFILE_HEVC_MAIN_10:
av_log(avctx, AV_LOG_ERROR, "H.265 main 10-bit profile "
"is not supported.\n");
return AVERROR_PATCHWELCOME;
default:
av_log(avctx, AV_LOG_ERROR, "Unknown H.265 profile %d.\n",
avctx->profile);
return AVERROR(EINVAL);
}
ctx->va_entrypoint = VAEntrypointEncSlice;
// This will be dependent on profile when 10-bit is supported.
ctx->va_rt_format = VA_RT_FORMAT_YUV420;
if (avctx->bit_rate > 0)
ctx->va_rc_mode = VA_RC_CBR;
else
ctx->va_rc_mode = VA_RC_CQP;
ctx->surface_width = FFALIGN(avctx->width, 16);
ctx->surface_height = FFALIGN(avctx->height, 16);
return ff_vaapi_encode_init(avctx);
}
#define OFFSET(x) (offsetof(VAAPIEncodeContext, codec_options_data) + \
......
......@@ -277,8 +277,8 @@ static int vaapi_encode_mjpeg_init_picture_params(AVCodecContext *avctx,
vpic->reconstructed_picture = pic->recon_surface;
vpic->coded_buf = pic->output_buffer;
vpic->picture_width = ctx->input_width;
vpic->picture_height = ctx->input_height;
vpic->picture_width = avctx->width;
vpic->picture_height = avctx->height;
vpic->pic_flags.bits.profile = 0;
vpic->pic_flags.bits.progressive = 0;
......@@ -333,31 +333,10 @@ static int vaapi_encode_mjpeg_init_slice_params(AVCodecContext *avctx,
return 0;
}
static av_cold int vaapi_encode_mjpeg_init_internal(AVCodecContext *avctx)
static av_cold int vaapi_encode_mjpeg_configure(AVCodecContext *avctx)
{
static const VAConfigAttrib default_config_attributes[] = {
{ .type = VAConfigAttribRTFormat,
.value = VA_RT_FORMAT_YUV420 },
{ .type = VAConfigAttribEncPackedHeaders,
.value = VA_ENC_PACKED_HEADER_SEQUENCE },
};
VAAPIEncodeContext *ctx = avctx->priv_data;
VAAPIEncodeMJPEGContext *priv = ctx->priv_data;
int i;
ctx->va_profile = VAProfileJPEGBaseline;
ctx->va_entrypoint = VAEntrypointEncPicture;
ctx->input_width = avctx->width;
ctx->input_height = avctx->height;
ctx->aligned_width = FFALIGN(ctx->input_width, 8);
ctx->aligned_height = FFALIGN(ctx->input_height, 8);
for (i = 0; i < FF_ARRAY_ELEMS(default_config_attributes); i++) {
ctx->config_attributes[ctx->nb_config_attributes++] =
default_config_attributes[i];
}
priv->quality = avctx->global_quality;
if (priv->quality < 1 || priv->quality > 100) {
......@@ -374,7 +353,7 @@ static av_cold int vaapi_encode_mjpeg_init_internal(AVCodecContext *avctx)
static VAAPIEncodeType vaapi_encode_type_mjpeg = {
.priv_data_size = sizeof(VAAPIEncodeMJPEGContext),
.init = &vaapi_encode_mjpeg_init_internal,
.configure = &vaapi_encode_mjpeg_configure,
.picture_params_size = sizeof(VAEncPictureParameterBufferJPEG),
.init_picture_params = &vaapi_encode_mjpeg_init_picture_params,
......@@ -390,7 +369,21 @@ static VAAPIEncodeType vaapi_encode_type_mjpeg = {
static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx)
{
return ff_vaapi_encode_init(avctx, &vaapi_encode_type_mjpeg);
VAAPIEncodeContext *ctx = avctx->priv_data;
ctx->codec = &vaapi_encode_type_mjpeg;
ctx->va_profile = VAProfileJPEGBaseline;
ctx->va_entrypoint = VAEntrypointEncPicture;
ctx->va_rt_format = VA_RT_FORMAT_YUV420;
ctx->va_rc_mode = VA_RC_CQP;
ctx->surface_width = FFALIGN(avctx->width, 8);
ctx->surface_height = FFALIGN(avctx->height, 8);
return ff_vaapi_encode_init(avctx);
}
static const AVCodecDefault vaapi_encode_mjpeg_defaults[] = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment