}
av_log(avctx, AV_LOG_DEBUG, "Sync to pic %"PRId64"/%"PRId64" "
- "(recon surface %#x).\n", pic->display_order,
- pic->encode_order, pic->recon_surface);
+ "(input surface %#x).\n", pic->display_order,
+ pic->encode_order, pic->input_surface);
- vas = vaSyncSurface(ctx->hwctx->display, pic->recon_surface);
+ vas = vaSyncSurface(ctx->hwctx->display, pic->input_surface);
if (vas != VA_STATUS_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to sync to picture completion: "
"%d (%s).\n", vas, vaErrorStr(vas));
}
if (pic->type == PICTURE_TYPE_IDR) {
- if (ctx->codec->write_sequence_header) {
+ if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE &&
+ ctx->codec->write_sequence_header) {
bit_len = 8 * sizeof(data);
err = ctx->codec->write_sequence_header(avctx, data, &bit_len);
if (err < 0) {
}
}
- if (ctx->codec->write_picture_header) {
+ if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_PICTURE &&
+ ctx->codec->write_picture_header) {
bit_len = 8 * sizeof(data);
err = ctx->codec->write_picture_header(avctx, pic, data, &bit_len);
if (err < 0) {
}
}
- if (ctx->codec->write_extra_header) {
+ if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_MISC &&
+ ctx->codec->write_extra_header) {
for (i = 0;; i++) {
int type;
bit_len = 8 * sizeof(data);
err = AVERROR(ENOMEM);
goto fail;
}
+ slice->index = i;
pic->slices[i] = slice;
if (ctx->codec->slice_params_size > 0) {
}
}
- if (ctx->codec->write_slice_header) {
+ if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SLICE &&
+ ctx->codec->write_slice_header) {
bit_len = 8 * sizeof(data);
err = ctx->codec->write_slice_header(avctx, pic, slice,
data, &bit_len);
} else if (ctx->issue_mode == ISSUE_MODE_MAXIMISE_THROUGHPUT) {
int activity;
+ // Run through the list of all available pictures repeatedly
+ // and issue the first one found which has all dependencies
+ // available (including previously-issued but not necessarily
+ // completed pictures).
do {
activity = 0;
for (pic = ctx->pic_start; pic; pic = pic->next) {
if (err < 0)
return err;
activity = 1;
+ // Start again from the beginning of the list,
+ // because issuing this picture may have satisfied
+ // forward dependencies of earlier ones.
+ break;
}
} while(activity);
+ // If we had a defined target for this step then it will
+ // always have been issued by now.
if (target) {
av_assert0(target->encode_issued && "broken dependencies?");
}
}
}
- if (ctx->input_order == 0) {
- // First frame is always an IDR frame.
- av_assert0(!ctx->pic_start && !ctx->pic_end);
-
- pic = vaapi_encode_alloc();
- if (!pic)
- return AVERROR(ENOMEM);
-
- pic->type = PICTURE_TYPE_IDR;
- pic->display_order = 0;
- pic->encode_order = 0;
-
- ctx->pic_start = ctx->pic_end = pic;
-
- *pic_out = pic;
- return 0;
- }
-
pic = vaapi_encode_alloc();
if (!pic)
return AVERROR(ENOMEM);
- if (ctx->p_per_i == 0 || ctx->p_counter == ctx->p_per_i) {
- if (ctx->i_per_idr == 0 || ctx->i_counter == ctx->i_per_idr) {
- pic->type = PICTURE_TYPE_IDR;
- ctx->i_counter = 0;
- } else {
- pic->type = PICTURE_TYPE_I;
- ++ctx->i_counter;
- }
+ if (ctx->input_order == 0 || ctx->force_idr ||
+ ctx->gop_counter >= avctx->gop_size) {
+ pic->type = PICTURE_TYPE_IDR;
+ ctx->force_idr = 0;
+ ctx->gop_counter = 1;
+ ctx->p_counter = 0;
+ } else if (ctx->p_counter >= ctx->p_per_i) {
+ pic->type = PICTURE_TYPE_I;
+ ++ctx->gop_counter;
ctx->p_counter = 0;
} else {
pic->type = PICTURE_TYPE_P;
pic->refs[0] = ctx->pic_end;
pic->nb_refs = 1;
+ ++ctx->gop_counter;
++ctx->p_counter;
}
start = end = pic;
if (pic->type != PICTURE_TYPE_IDR) {
// If that was not an IDR frame, add B-frames display-before and
- // encode-after it.
+ // encode-after it, but not exceeding the GOP size.
- for (i = 0; i < ctx->b_per_p; i++) {
+ for (i = 0; i < ctx->b_per_p &&
+ ctx->gop_counter < avctx->gop_size; i++) {
pic = vaapi_encode_alloc();
if (!pic)
goto fail;
pic->display_order = ctx->input_order + ctx->b_per_p - i - 1;
pic->encode_order = pic->display_order + 1;
start = pic;
+
+ ++ctx->gop_counter;
}
}
- for (i = 0, pic = start; pic; i++, pic = pic->next) {
- pic->display_order = ctx->input_order + i;
- if (end->type == PICTURE_TYPE_IDR)
- pic->encode_order = ctx->input_order + i;
- else if (pic == end)
- pic->encode_order = ctx->input_order;
- else
- pic->encode_order = ctx->input_order + i + 1;
- }
+ if (ctx->input_order == 0) {
+ pic->display_order = 0;
+ pic->encode_order = 0;
- av_assert0(ctx->pic_end);
- ctx->pic_end->next = start;
- ctx->pic_end = end;
+ ctx->pic_start = ctx->pic_end = pic;
+ } else {
+ for (i = 0, pic = start; pic; i++, pic = pic->next) {
+ pic->display_order = ctx->input_order + i;
+ if (end->type == PICTURE_TYPE_IDR)
+ pic->encode_order = ctx->input_order + i;
+ else if (pic == end)
+ pic->encode_order = ctx->input_order;
+ else
+ pic->encode_order = ctx->input_order + i + 1;
+ }
+
+ av_assert0(ctx->pic_end);
+ ctx->pic_end->next = start;
+ ctx->pic_end = end;
+ }
*pic_out = start;
av_log(avctx, AV_LOG_DEBUG, "Pictures:");
return AVERROR(ENOMEM);
}
-static int vaapi_encode_mangle_end(AVCodecContext *avctx)
+static int vaapi_encode_truncate_gop(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
VAAPIEncodePicture *pic, *last_pic, *next;
// mangle anything.
}
- av_log(avctx, AV_LOG_DEBUG, "Pictures at end of stream:");
+ av_log(avctx, AV_LOG_DEBUG, "Pictures ending truncated GOP:");
for (pic = ctx->pic_start; pic; pic = pic->next) {
av_log(avctx, AV_LOG_DEBUG, " %s (%"PRId64"/%"PRId64")",
picture_type_name[pic->type],
av_log(avctx, AV_LOG_DEBUG, "Encode frame: %ux%u (%"PRId64").\n",
input_image->width, input_image->height, input_image->pts);
+ if (input_image->pict_type == AV_PICTURE_TYPE_I) {
+ err = vaapi_encode_truncate_gop(avctx);
+ if (err < 0)
+ goto fail;
+ ctx->force_idr = 1;
+ }
+
err = vaapi_encode_get_next(avctx, &pic);
if (err) {
av_log(avctx, AV_LOG_ERROR, "Input setup failed: %d.\n", err);
} else {
if (!ctx->end_of_stream) {
- err = vaapi_encode_mangle_end(avctx);
+ err = vaapi_encode_truncate_gop(avctx);
if (err < 0)
goto fail;
ctx->end_of_stream = 1;
return err;
}
-static av_cold int vaapi_encode_check_config(AVCodecContext *avctx)
+static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
VAStatus vas;
VAProfile *profiles = NULL;
VAEntrypoint *entrypoints = NULL;
VAConfigAttrib attr[] = {
- { VAConfigAttribRateControl },
- { VAConfigAttribEncMaxRefFrames },
+ { VAConfigAttribRTFormat },
+ { VAConfigAttribRateControl },
+ { VAConfigAttribEncMaxRefFrames },
+ { VAConfigAttribEncPackedHeaders },
};
n = vaMaxNumProfiles(ctx->hwctx->display);
continue;
}
switch (attr[i].type) {
+ case VAConfigAttribRTFormat:
+ if (!(ctx->va_rt_format & attr[i].value)) {
+ av_log(avctx, AV_LOG_ERROR, "Surface RT format %#x "
+ "is not supported (mask %#x).\n",
+ ctx->va_rt_format, attr[i].value);
+ err = AVERROR(EINVAL);
+ goto fail;
+ }
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRTFormat,
+ .value = ctx->va_rt_format,
+ };
+ break;
case VAConfigAttribRateControl:
if (!(ctx->va_rc_mode & attr[i].value)) {
- av_log(avctx, AV_LOG_ERROR, "Rate control mode is not "
- "supported: %x\n", attr[i].value);
+ av_log(avctx, AV_LOG_ERROR, "Rate control mode %#x "
+ "is not supported (mask: %#x).\n",
+ ctx->va_rc_mode, attr[i].value);
err = AVERROR(EINVAL);
goto fail;
}
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRateControl,
+ .value = ctx->va_rc_mode,
+ };
break;
case VAConfigAttribEncMaxRefFrames:
{
if (avctx->gop_size > 1 && ref_l0 < 1) {
av_log(avctx, AV_LOG_ERROR, "P frames are not "
- "supported (%x).\n", attr[i].value);
+ "supported (%#x).\n", attr[i].value);
err = AVERROR(EINVAL);
goto fail;
}
if (avctx->max_b_frames > 0 && ref_l1 < 1) {
av_log(avctx, AV_LOG_ERROR, "B frames are not "
- "supported (%x).\n", attr[i].value);
+ "supported (%#x).\n", attr[i].value);
err = AVERROR(EINVAL);
goto fail;
}
}
break;
+ case VAConfigAttribEncPackedHeaders:
+ if (ctx->va_packed_headers & ~attr[i].value) {
+ // This isn't fatal, but packed headers are always
+ // preferable because they are under our control.
+ // When absent, the driver is generating them and some
+ // features may not work (e.g. VUI or SEI in H.264).
+ av_log(avctx, AV_LOG_WARNING, "Warning: some packed "
+ "headers are not supported (want %#x, got %#x).\n",
+ ctx->va_packed_headers, attr[i].value);
+ ctx->va_packed_headers &= attr[i].value;
+ }
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribEncPackedHeaders,
+ .value = ctx->va_packed_headers,
+ };
+ break;
+ default:
+ av_assert0(0 && "Unexpected config attribute.");
}
}
return err;
}
+static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->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;
+
+ ctx->rc_params.misc.type = VAEncMiscParameterTypeRateControl;
+ ctx->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 : 18),
+ .basic_unit_size = 0,
+ };
+ ctx->global_params[ctx->nb_global_params] =
+ &ctx->rc_params.misc;
+ ctx->global_params_size[ctx->nb_global_params++] =
+ sizeof(ctx->rc_params);
+
+ ctx->hrd_params.misc.type = VAEncMiscParameterTypeHRD;
+ ctx->hrd_params.hrd = (VAEncMiscParameterHRD) {
+ .initial_buffer_fullness = hrd_initial_buffer_fullness,
+ .buffer_size = hrd_buffer_size,
+ };
+ ctx->global_params[ctx->nb_global_params] =
+ &ctx->hrd_params.misc;
+ ctx->global_params_size[ctx->nb_global_params++] =
+ sizeof(ctx->hrd_params);
+
+ return 0;
+}
+
static void vaapi_encode_free_output_buffer(void *opaque,
uint8_t *data)
{
// bound on that.
vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
VAEncCodedBufferType,
- 3 * ctx->aligned_width * ctx->aligned_height +
+ 3 * ctx->surface_width * ctx->surface_height +
(1 << 16), 1, 0, &buffer_id);
if (vas != VA_STATUS_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to create bitstream "
return ref;
}
-av_cold int ff_vaapi_encode_init(AVCodecContext *avctx,
- const VAAPIEncodeType *type)
+static av_cold int vaapi_encode_create_recon_frames(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- AVVAAPIFramesContext *recon_hwctx = NULL;
AVVAAPIHWConfig *hwconfig = NULL;
AVHWFramesConstraints *constraints = NULL;
enum AVPixelFormat recon_format;
- VAStatus vas;
int err, i;
- if (!avctx->hw_frames_ctx) {
- av_log(avctx, AV_LOG_ERROR, "A hardware frames reference is "
- "required to associate the encoding device.\n");
- return AVERROR(EINVAL);
- }
-
- ctx->codec = type;
- ctx->codec_options = ctx->codec_options_data;
-
- ctx->va_config = VA_INVALID_ID;
- ctx->va_context = VA_INVALID_ID;
-
- ctx->priv_data = av_mallocz(type->priv_data_size);
- if (!ctx->priv_data) {
- err = AVERROR(ENOMEM);
- goto fail;
- }
-
- ctx->input_frames_ref = av_buffer_ref(avctx->hw_frames_ctx);
- if (!ctx->input_frames_ref) {
- err = AVERROR(ENOMEM);
- goto fail;
- }
- ctx->input_frames = (AVHWFramesContext*)ctx->input_frames_ref->data;
-
- ctx->device_ref = av_buffer_ref(ctx->input_frames->device_ref);
- if (!ctx->device_ref) {
- err = AVERROR(ENOMEM);
- goto fail;
- }
- ctx->device = (AVHWDeviceContext*)ctx->device_ref->data;
- ctx->hwctx = ctx->device->hwctx;
-
- err = ctx->codec->init(avctx);
- if (err < 0)
- goto fail;
-
- err = vaapi_encode_check_config(avctx);
- if (err < 0)
- goto fail;
-
- vas = vaCreateConfig(ctx->hwctx->display,
- ctx->va_profile, ctx->va_entrypoint,
- ctx->config_attributes, ctx->nb_config_attributes,
- &ctx->va_config);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "Failed to create encode pipeline "
- "configuration: %d (%s).\n", vas, vaErrorStr(vas));
- err = AVERROR(EIO);
- goto fail;
- }
-
hwconfig = av_hwdevice_hwconfig_alloc(ctx->device_ref);
if (!hwconfig) {
err = AVERROR(ENOMEM);
av_log(avctx, AV_LOG_DEBUG, "Using %s as format of "
"reconstructed frames.\n", av_get_pix_fmt_name(recon_format));
- if (ctx->aligned_width < constraints->min_width ||
- ctx->aligned_height < constraints->min_height ||
- ctx->aligned_width > constraints->max_width ||
- ctx->aligned_height > constraints->max_height) {
+ if (ctx->surface_width < constraints->min_width ||
+ ctx->surface_height < constraints->min_height ||
+ ctx->surface_width > constraints->max_width ||
+ ctx->surface_height > constraints->max_height) {
av_log(avctx, AV_LOG_ERROR, "Hardware does not support encoding at "
"size %dx%d (constraints: width %d-%d height %d-%d).\n",
- ctx->aligned_width, ctx->aligned_height,
+ ctx->surface_width, ctx->surface_height,
constraints->min_width, constraints->max_width,
constraints->min_height, constraints->max_height);
err = AVERROR(EINVAL);
ctx->recon_frames->format = AV_PIX_FMT_VAAPI;
ctx->recon_frames->sw_format = recon_format;
- ctx->recon_frames->width = ctx->aligned_width;
- ctx->recon_frames->height = ctx->aligned_height;
- ctx->recon_frames->initial_pool_size = ctx->nb_recon_frames;
+ ctx->recon_frames->width = ctx->surface_width;
+ ctx->recon_frames->height = ctx->surface_height;
+ // At most three IDR/I/P frames and two runs of B frames can be in
+ // flight at any one time.
+ ctx->recon_frames->initial_pool_size = 3 + 2 * avctx->max_b_frames;
err = av_hwframe_ctx_init(ctx->recon_frames_ref);
if (err < 0) {
"frame context: %d.\n", err);
goto fail;
}
- recon_hwctx = ctx->recon_frames->hwctx;
+ err = 0;
+ fail:
+ av_freep(&hwconfig);
+ av_hwframe_constraints_free(&constraints);
+ return err;
+}
+
+av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ AVVAAPIFramesContext *recon_hwctx = NULL;
+ VAStatus vas;
+ int err;
+
+ if (!avctx->hw_frames_ctx) {
+ av_log(avctx, AV_LOG_ERROR, "A hardware frames reference is "
+ "required to associate the encoding device.\n");
+ return AVERROR(EINVAL);
+ }
+
+ ctx->codec_options = ctx->codec_options_data;
+
+ ctx->va_config = VA_INVALID_ID;
+ ctx->va_context = VA_INVALID_ID;
+
+ ctx->priv_data = av_mallocz(ctx->codec->priv_data_size);
+ if (!ctx->priv_data) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ ctx->input_frames_ref = av_buffer_ref(avctx->hw_frames_ctx);
+ if (!ctx->input_frames_ref) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+ ctx->input_frames = (AVHWFramesContext*)ctx->input_frames_ref->data;
+
+ ctx->device_ref = av_buffer_ref(ctx->input_frames->device_ref);
+ if (!ctx->device_ref) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+ ctx->device = (AVHWDeviceContext*)ctx->device_ref->data;
+ ctx->hwctx = ctx->device->hwctx;
+
+ err = vaapi_encode_config_attributes(avctx);
+ if (err < 0)
+ goto fail;
+
+ vas = vaCreateConfig(ctx->hwctx->display,
+ ctx->va_profile, ctx->va_entrypoint,
+ ctx->config_attributes, ctx->nb_config_attributes,
+ &ctx->va_config);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to create encode pipeline "
+ "configuration: %d (%s).\n", vas, vaErrorStr(vas));
+ err = AVERROR(EIO);
+ goto fail;
+ }
+
+ err = vaapi_encode_create_recon_frames(avctx);
+ if (err < 0)
+ goto fail;
+
+ recon_hwctx = ctx->recon_frames->hwctx;
vas = vaCreateContext(ctx->hwctx->display, ctx->va_config,
- ctx->aligned_width, ctx->aligned_height,
+ ctx->surface_width, ctx->surface_height,
VA_PROGRESSIVE,
recon_hwctx->surface_ids,
recon_hwctx->nb_surfaces,
goto fail;
}
+ ctx->output_buffer_pool =
+ av_buffer_pool_init2(sizeof(VABufferID), avctx,
+ &vaapi_encode_alloc_output_buffer, NULL);
+ if (!ctx->output_buffer_pool) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ if (ctx->va_rc_mode & ~VA_RC_CQP) {
+ err = vaapi_encode_init_rate_control(avctx);
+ if (err < 0)
+ goto fail;
+ }
+
+ if (ctx->codec->configure) {
+ err = ctx->codec->configure(avctx);
+ if (err < 0)
+ goto fail;
+ }
+
ctx->input_order = 0;
ctx->output_delay = avctx->max_b_frames;
ctx->decode_delay = 1;
ctx->output_order = - ctx->output_delay - 1;
+ // Currently we never generate I frames, only IDR.
+ ctx->p_per_i = ((avctx->gop_size + avctx->max_b_frames) /
+ (avctx->max_b_frames + 1));
+ ctx->b_per_p = avctx->max_b_frames;
+
if (ctx->codec->sequence_params_size > 0) {
ctx->codec_sequence_params =
av_mallocz(ctx->codec->sequence_params_size);
}
}
- ctx->output_buffer_pool =
- av_buffer_pool_init2(sizeof(VABufferID), avctx,
- &vaapi_encode_alloc_output_buffer, NULL);
- if (!ctx->output_buffer_pool) {
- err = AVERROR(ENOMEM);
- goto fail;
- }
-
- // All I are IDR for now.
- ctx->i_per_idr = 0;
- ctx->p_per_i = ((avctx->gop_size + avctx->max_b_frames) /
- (avctx->max_b_frames + 1));
- ctx->b_per_p = avctx->max_b_frames;
-
// This should be configurable somehow. (Needs testing on a machine
// where it actually overlaps properly, though.)
ctx->issue_mode = ISSUE_MODE_MAXIMISE_THROUGHPUT;
+ if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE &&
+ ctx->codec->write_sequence_header) {
+ char data[MAX_PARAM_BUFFER_SIZE];
+ size_t bit_len = 8 * sizeof(data);
+
+ err = ctx->codec->write_sequence_header(avctx, data, &bit_len);
+ if (err < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to write sequence header "
+ "for extradata: %d.\n", err);
+ goto fail;
+ } else {
+ avctx->extradata_size = (bit_len + 7) / 8;
+ avctx->extradata = av_mallocz(avctx->extradata_size +
+ AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!avctx->extradata) {
+ err = AVERROR(ENOMEM);
+ goto fail;
+ }
+ memcpy(avctx->extradata, data, avctx->extradata_size);
+ }
+ }
+
return 0;
fail:
- av_freep(&hwconfig);
- av_hwframe_constraints_free(&constraints);
ff_vaapi_encode_close(avctx);
return err;
}
ctx->va_config = VA_INVALID_ID;
}
- if (ctx->codec->close)
- ctx->codec->close(avctx);
-
av_buffer_pool_uninit(&ctx->output_buffer_pool);
av_freep(&ctx->codec_sequence_params);