Discussion:
[FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up rate control configuration
m***@gmail.com
2018-06-08 05:05:51 UTC
Permalink
Query which modes are supported and select between VBR and CBR based
on that - this removes all of the codec-specific rate control mode
selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173 ++++++++++++++++++++++++++++------------
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
diff --git a/doc/encoders.texi b/doc/encoders.texi
index 62a1509a96..0c0a307987 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -2643,8 +2643,6 @@ Always encodes using the standard quantisation and huffman tables -
@item mpeg2_vaapi
@option{profile} and @option{level} set the value of @emph{profile_and_level_indication}.
-No rate control is supported.
-
@item vp8_vaapi
B-frames are not supported.
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index f4c063734c..5de5483454 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -1217,7 +1217,6 @@ static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
int i;
VAConfigAttrib attr[] = {
- { VAConfigAttribRateControl },
{ VAConfigAttribEncMaxRefFrames },
{ VAConfigAttribEncPackedHeaders },
};
@@ -1241,32 +1240,6 @@ static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
continue;
}
switch (attr[i].type) {
- // Hack for backward compatibility: CBR was the only
- // usable RC mode for a long time, so old drivers will
- // only have it. Normal default options may now choose
- // VBR and then fail, however, so override it here with
- // CBR if that is the only supported mode.
- if (ctx->va_rc_mode == VA_RC_VBR &&
- !(attr[i].value & VA_RC_VBR) &&
- (attr[i].value & VA_RC_CBR)) {
- av_log(avctx, AV_LOG_WARNING, "VBR rate control is "
- "not supported with this driver version; "
- "using CBR instead.\n");
- ctx->va_rc_mode = VA_RC_CBR;
- }
- if (!(ctx->va_rc_mode & 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);
- return AVERROR(EINVAL);
- }
- ctx->config_attributes[ctx->nb_config_attributes++] =
- (VAConfigAttrib) {
- .type = VAConfigAttribRateControl,
- .value = ctx->va_rc_mode,
- };
- break;
{
unsigned int ref_l0 = attr[i].value & 0xffff;
@@ -1313,44 +1286,144 @@ static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- int rc_bits_per_second;
- int rc_target_percentage;
- int rc_window_size;
- int hrd_buffer_size;
- int hrd_initial_buffer_fullness;
+ int64_t rc_bits_per_second;
+ int rc_target_percentage;
+ int rc_window_size;
+ int64_t hrd_buffer_size;
+ int64_t hrd_initial_buffer_fullness;
int fr_num, fr_den;
+ VAConfigAttrib rc_attr = { VAConfigAttribRateControl };
+ VAStatus vas;
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile, ctx->va_entrypoint,
+ &rc_attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query rate control "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
- if (avctx->bit_rate > INT32_MAX) {
- av_log(avctx, AV_LOG_ERROR, "Target bitrate of 2^31 bps or "
- "higher is not supported.\n");
+ if (rc_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not report any "
+ "supported rate control modes: assuming constant-quality.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ }
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using constant-quality mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support "
+ "constant-quality mode (%#x).\n", rc_attr.value);
+ return AVERROR(EINVAL);
+ }
+ }
+
+ if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any "
+ "bitrate-targetted rate control modes.\n");
return AVERROR(EINVAL);
}
if (avctx->rc_buffer_size)
hrd_buffer_size = avctx->rc_buffer_size;
+ else if (avctx->rc_max_rate > 0)
+ hrd_buffer_size = avctx->rc_max_rate;
else
hrd_buffer_size = avctx->bit_rate;
- if (avctx->rc_initial_buffer_occupancy)
+ if (avctx->rc_initial_buffer_occupancy) {
+ if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer settings: "
+ "must have initial buffer size (%d) < "
+ "buffer size (%"PRId64").\n",
+ avctx->rc_initial_buffer_occupancy, hrd_buffer_size);
+ return AVERROR(EINVAL);
+ }
hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy;
- else
+ } else {
hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4;
+ }
+
+ if (avctx->rc_max_rate && avctx->rc_max_rate < avctx->bit_rate) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid bitrate settings: must have "
+ "bitrate (%"PRId64") <= maxrate (%"PRId64").\n",
+ avctx->bit_rate, avctx->rc_max_rate);
+ return AVERROR(EINVAL);
+ }
+
+ if (avctx->rc_max_rate > avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_VBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support "
+ "VBR mode (%#x), using CBR mode instead.\n",
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_CBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_VBR;
+ }
+
+ rc_bits_per_second = avctx->rc_max_rate;
+ rc_target_percentage = (avctx->bit_rate * 100) / avctx->rc_max_rate;
+
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support "
+ "CBR mode (%#x), using VBR mode instead.\n",
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal bitrate.
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
+
+ // We only have a target bitrate, but VAAPI requires that a
+ // maximum rate be supplied as well. Since the user has
+ // offered no particular constraint, arbitrarily pick a
+ // maximum rate of double the target rate.
+ rc_bits_per_second = 2 * avctx->bit_rate;
+ rc_target_percentage = 50;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- } else {
- rc_bits_per_second = avctx->rc_max_rate;
- rc_target_percentage = (avctx->bit_rate * 100) / rc_bits_per_second;
}
- rc_window_size = (hrd_buffer_size * 1000) / avctx->bit_rate;
}
+ rc_window_size = (hrd_buffer_size * 1000) / rc_bits_per_second;
+
+ av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s, %d%% of %"PRId64" bps "
+ "over %d ms.\n", ctx->va_rc_mode == VA_RC_VBR ? "VBR" : "CBR",
+ rc_target_percentage, rc_bits_per_second, rc_window_size);
+ av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, "
+ "initial fullness %"PRId64" bits.\n",
+ hrd_buffer_size, hrd_initial_buffer_fullness);
+
+ if (rc_bits_per_second > UINT32_MAX ||
+ hrd_buffer_size > UINT32_MAX ||
+ hrd_initial_buffer_fullness > UINT32_MAX) {
+ av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or "
+ "greater are not supported by VAAPI.\n");
+ return AVERROR(EINVAL);
+ }
+
Use a assert0() maybe more pithily
+ ctx->va_bit_rate = rc_bits_per_second;
+
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRateControl,
+ .value = ctx->va_rc_mode,
+ };
+
ctx->rc_params.misc.type = VAEncMiscParameterTypeRateControl;
ctx->rc_params.rc = (VAEncMiscParameterRateControl) {
.bits_per_second = rc_bits_per_second,
@@ -1611,6 +1684,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;
+ err = vaapi_encode_init_rate_control(avctx);
+ if (err < 0)
+ goto fail;
+
err = vaapi_encode_config_attributes(avctx);
if (err < 0)
goto fail;
@@ -1658,12 +1735,6 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
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)
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index b8f2ed6d21..6fac4781c3 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -116,8 +116,6 @@ typedef struct VAAPIEncodeContext {
// Use low power encoding mode.
int low_power;
- // Rate control mode.
- unsigned int va_rc_mode;
// Supported packed headers (initially the desired set, modified
// later to what is actually supported).
unsigned int va_packed_headers;
@@ -138,6 +136,10 @@ typedef struct VAAPIEncodeContext {
VAProfile va_profile;
// Encoding entrypoint (VAEntryoint*).
VAEntrypoint va_entrypoint;
+ // Rate control mode.
+ unsigned int va_rc_mode;
+ // Bitrate for codec-specific encoder parameters.
+ unsigned int va_bit_rate;
// Configuration attributes to use when creating va_config.
VAConfigAttrib config_attributes[MAX_CONFIG_ATTRIBUTES];
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index df6f39a946..1e6eb2e39b 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -425,9 +425,9 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
// Try to scale these to a sensible range so that the
// golomb encode of the value is not overlong.
hrd->bit_rate_scale =
- av_clip_uintp2(av_log2(avctx->bit_rate) - 15 - 6, 4);
+ av_clip_uintp2(av_log2(ctx->va_bit_rate) - 15 - 6, 4);
hrd->bit_rate_value_minus1[0] =
- (avctx->bit_rate >> hrd->bit_rate_scale + 6) - 1;
+ (ctx->va_bit_rate >> hrd->bit_rate_scale + 6) - 1;
hrd->cpb_size_scale =
av_clip_uintp2(av_log2(ctx->hrd_params.hrd.buffer_size) - 15 - 4, 4);
@@ -497,7 +497,7 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
.intra_idr_period = avctx->gop_size,
.ip_period = ctx->b_per_p + 1,
- .bits_per_second = avctx->bit_rate,
+ .bits_per_second = ctx->va_bit_rate,
.max_num_ref_frames = sps->max_num_ref_frames,
.picture_width_in_mbs = sps->pic_width_in_mbs_minus1 + 1,
.picture_height_in_mbs = sps->pic_height_in_map_units_minus1 + 1,
@@ -823,10 +823,6 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
priv->fixed_qp_p = 26;
priv->fixed_qp_b = 26;
- av_log(avctx, AV_LOG_DEBUG, "Using %s-bitrate = %"PRId64" bps.\n",
- ctx->va_rc_mode == VA_RC_CBR ? "constant" : "variable",
- avctx->bit_rate);
-
} else {
av_assert0(0 && "Invalid RC mode.");
}
@@ -934,14 +930,6 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
return AVERROR_PATCHWELCOME;
}
- if (avctx->bit_rate > 0) {
- if (avctx->rc_max_rate == avctx->bit_rate)
- ctx->va_rc_mode = VA_RC_CBR;
- else
- ctx->va_rc_mode = VA_RC_VBR;
- } else
- ctx->va_rc_mode = VA_RC_CQP;
-
ctx->va_packed_headers =
VA_ENC_PACKED_HEADER_SEQUENCE | // SPS and PPS.
VA_ENC_PACKED_HEADER_SLICE | // Slice headers.
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index b8b66b87cb..b296919b37 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -512,7 +512,7 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
.intra_period = avctx->gop_size,
.intra_idr_period = avctx->gop_size,
.ip_period = ctx->b_per_p + 1,
- .bits_per_second = avctx->bit_rate,
+ .bits_per_second = ctx->va_bit_rate,
.pic_width_in_luma_samples = sps->pic_width_in_luma_samples,
.pic_height_in_luma_samples = sps->pic_height_in_luma_samples,
@@ -1014,10 +1014,6 @@ static av_cold int vaapi_encode_h265_configure(AVCodecContext *avctx)
priv->fixed_qp_p = 30;
priv->fixed_qp_b = 30;
- av_log(avctx, AV_LOG_DEBUG, "Using %s-bitrate = %"PRId64" bps.\n",
- ctx->va_rc_mode == VA_RC_CBR ? "constant" : "variable",
- avctx->bit_rate);
-
} else {
av_assert0(0 && "Invalid RC mode.");
}
@@ -1068,14 +1064,6 @@ static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx)
if (avctx->level == FF_LEVEL_UNKNOWN)
avctx->level = priv->level;
- if (avctx->bit_rate > 0) {
- if (avctx->rc_max_rate == avctx->bit_rate)
- ctx->va_rc_mode = VA_RC_CBR;
- else
- ctx->va_rc_mode = VA_RC_VBR;
- } else
- ctx->va_rc_mode = VA_RC_CQP;
-
ctx->va_packed_headers =
VA_ENC_PACKED_HEADER_SEQUENCE | // VPS, SPS and PPS.
VA_ENC_PACKED_HEADER_SLICE | // Slice headers.
diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index b328beaa09..67ac2fba96 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -388,8 +388,6 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx)
ctx->codec = &vaapi_encode_type_mjpeg;
- ctx->va_rc_mode = VA_RC_CQP;
-
// The JPEG image header - see note above.
ctx->va_packed_headers =
VA_ENC_PACKED_HEADER_RAW_DATA;
@@ -402,6 +400,7 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx)
static const AVCodecDefault vaapi_encode_mjpeg_defaults[] = {
{ "global_quality", "80" },
+ { "b", "0" },
{ NULL },
};
diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index db79d72115..ff86b8817e 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -188,8 +188,8 @@ static int vaapi_encode_mpeg2_init_sequence_params(AVCodecContext *avctx)
memset(pce, 0, sizeof(*pce));
- if (avctx->bit_rate > 0) {
- priv->bit_rate = (avctx->bit_rate + 399) / 400;
+ if (ctx->va_bit_rate > 0) {
+ priv->bit_rate = (ctx->va_bit_rate + 399) / 400;
} else {
// Unknown (not a bitrate-targetting mode), so just use the
// highest value.
@@ -361,7 +361,7 @@ static int vaapi_encode_mpeg2_init_sequence_params(AVCodecContext *avctx)
.picture_width = avctx->width,
.picture_height = avctx->height,
- .bits_per_second = avctx->bit_rate,
+ .bits_per_second = ctx->va_bit_rate,
.frame_rate = av_q2d(priv->frame_rate),
.aspect_ratio_information = sh->aspect_ratio_information,
.vbv_buffer_size = priv->vbv_buffer_size,
@@ -615,8 +615,6 @@ static av_cold int vaapi_encode_mpeg2_init(AVCodecContext *avctx)
return AVERROR(EINVAL);
}
- ctx->va_rc_mode = VA_RC_CQP;
-
ctx->va_packed_headers = VA_ENC_PACKED_HEADER_SEQUENCE |
VA_ENC_PACKED_HEADER_PICTURE;
@@ -666,6 +664,7 @@ static const AVOption vaapi_encode_mpeg2_options[] = {
};
static const AVCodecDefault vaapi_encode_mpeg2_defaults[] = {
+ { "b", "0" },
{ "bf", "1" },
{ "g", "120" },
{ "i_qfactor", "1" },
diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c
index 9588826bfb..40871a6bbf 100644
--- a/libavcodec/vaapi_encode_vp8.c
+++ b/libavcodec/vaapi_encode_vp8.c
@@ -65,7 +65,7 @@ static int vaapi_encode_vp8_init_sequence_params(AVCodecContext *avctx)
vseq->kf_auto = 0;
if (!(ctx->va_rc_mode & VA_RC_CQP)) {
- vseq->bits_per_second = avctx->bit_rate;
+ vseq->bits_per_second = ctx->va_bit_rate;
vseq->intra_period = avctx->gop_size;
}
@@ -205,17 +205,6 @@ static av_cold int vaapi_encode_vp8_init(AVCodecContext *avctx)
ctx->codec = &vaapi_encode_type_vp8;
- if (avctx->flags & AV_CODEC_FLAG_QSCALE) {
- ctx->va_rc_mode = VA_RC_CQP;
- } else if (avctx->bit_rate > 0) {
- if (avctx->rc_max_rate == avctx->bit_rate)
- ctx->va_rc_mode = VA_RC_CBR;
- else
- ctx->va_rc_mode = VA_RC_VBR;
- } else {
- ctx->va_rc_mode = VA_RC_CQP;
- }
-
// Packed headers are not currently supported.
ctx->va_packed_headers = 0;
diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index 4d7cec0520..e400bc8b79 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -71,7 +71,7 @@ static int vaapi_encode_vp9_init_sequence_params(AVCodecContext *avctx)
vseq->kf_auto = 0;
if (!(ctx->va_rc_mode & VA_RC_CQP)) {
- vseq->bits_per_second = avctx->bit_rate;
+ vseq->bits_per_second = ctx->va_bit_rate;
vseq->intra_period = avctx->gop_size;
}
@@ -227,17 +227,6 @@ static av_cold int vaapi_encode_vp9_init(AVCodecContext *avctx)
ctx->codec = &vaapi_encode_type_vp9;
- if (avctx->flags & AV_CODEC_FLAG_QSCALE) {
- ctx->va_rc_mode = VA_RC_CQP;
- } else if (avctx->bit_rate > 0) {
- if (avctx->bit_rate == avctx->rc_max_rate)
- ctx->va_rc_mode = VA_RC_CBR;
- else
- ctx->va_rc_mode = VA_RC_VBR;
- } else {
- ctx->va_rc_mode = VA_RC_CQP;
- }
-
// Packed headers are not currently supported.
ctx->va_packed_headers = 0;
--
2.16.3
_______________________________________________
ffmpeg-devel mailing list
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
--
=======================================
Jun zhao/赵军
+++++++++++++++++++++++++++++++++++++++
Mark Thompson
2018-06-07 23:43:31 UTC
Permalink
---
doc/encoders.texi | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/doc/encoders.texi b/doc/encoders.texi
index d61a1cc4bc..3894774bef 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -2663,6 +2663,16 @@ Include access unit delimiters in the stream (not included by default).
Set @emph{general_tier_flag}. This may affect the level chosen for the stream
if it is not explicitly specified.

+@item sei
+Set SEI message types to include.
+Some combination of the following values:
+@table @samp
+@item hdr
+Include HDR metadata if the input frames have it
+(@emph{mastering_display_colour_volume} and @emph{content_light_level}
+messages).
+@end table
+
@end table

@item mjpeg_vaapi
--
2.16.3
Mark Thompson
2018-06-07 23:43:26 UTC
Permalink
---
libavcodec/cbs_h264_syntax_template.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libavcodec/cbs_h264_syntax_template.c b/libavcodec/cbs_h264_syntax_template.c
index 21edcb799e..f53c02467e 100644
--- a/libavcodec/cbs_h264_syntax_template.c
+++ b/libavcodec/cbs_h264_syntax_template.c
@@ -197,7 +197,7 @@ static int FUNC(vui_parameters)(CodedBitstreamContext *ctx, RWContext *rw,
infer(log2_max_mv_length_vertical, 15);

if ((sps->profile_idc == 44 || sps->profile_idc == 86 ||
- sps->profile_idc == 110 || sps->profile_idc == 110 ||
+ sps->profile_idc == 100 || sps->profile_idc == 110 ||
sps->profile_idc == 122 || sps->profile_idc == 244) &&
sps->constraint_set3_flag) {
infer(max_num_reorder_frames, 0);
--
2.16.3
Mark Thompson
2018-06-07 23:43:17 UTC
Permalink
---
doc/encoders.texi | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)

diff --git a/doc/encoders.texi b/doc/encoders.texi
index 861f9f4f1f..b451142cfb 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -2631,12 +2631,36 @@ Use CABAC.
@item cavlc
Use CAVLC.
@end table
+
+@item aud
+Include access unit delimiters in the stream (not included by default).
+
+@item sei
+Set SEI message types to include.
+Some combination of the following values:
+@table @samp
+@item identifier
+Include a @emph{user_data_unregistered} message containing information about
+the encoder.
+@item timing
+Include picture timing parameters (@emph{buffering_period} and
+@emph{pic_timing} messages).
+@item recovery_point
+Include recovery points where appropriate (@emph{recovery_point} messages).
+@end table
+
@end table

@item hevc_vaapi
@option{profile} and @option{level} set the values of
@emph{general_profile_idc} and @emph{general_level_idc} respectively.

+@table @option
+@item aud
+Include access unit delimiters in the stream (not included by default).
+
+@end table
+
@item mjpeg_vaapi
Always encodes using the standard quantisation and huffman tables -
@option{global_quality} scales the standard quantisation table (range 1-100).
--
2.16.3
Mark Thompson
2018-06-07 23:43:23 UTC
Permalink
---
libavcodec/vaapi_encode_h264.c | 34 ++++++++++++++++++++++++++++++----
1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 82166d4457..4034053dc0 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -30,6 +30,7 @@
#include "cbs.h"
#include "cbs_h264.h"
#include "h264.h"
+#include "h264_levels.h"
#include "h264_sei.h"
#include "internal.h"
#include "vaapi_encode.h"
@@ -294,6 +295,7 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
H264RawPPS *pps = &priv->raw_pps;
VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params;
VAEncPictureParameterBufferH264 *vpic = ctx->codec_picture_params;
+ int dpb_frames;

memset(&priv->current_access_unit, 0,
sizeof(priv->current_access_unit));
@@ -319,7 +321,32 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
sps->constraint_set5_flag = ctx->b_per_p == 0;
}

- sps->level_idc = avctx->level;
+ if (ctx->gop_size == 1)
+ dpb_frames = 0;
+ else
+ dpb_frames = 1 + (ctx->b_per_p > 0);
+
+ if (avctx->level != FF_LEVEL_UNKNOWN) {
+ sps->level_idc = avctx->level;
+ } else {
+ const H264LevelDescriptor *level;
+
+ level = ff_h264_guess_level(sps->profile_idc,
+ avctx->bit_rate,
+ priv->mb_width * 16,
+ priv->mb_height * 16,
+ dpb_frames);
+ if (level) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using level %s.\n", level->name);
+ if (level->constraint_set3_flag)
+ sps->constraint_set3_flag = 1;
+ sps->level_idc = level->level_idc;
+ } else {
+ av_log(avctx, AV_LOG_WARNING, "Stream will not conform "
+ "to any level: using level 6.2.\n");
+ sps->level_idc = 62;
+ }
+ }

sps->seq_parameter_set_id = 0;
sps->chroma_format_idc = 1;
@@ -329,8 +356,7 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
sps->log2_max_pic_order_cnt_lsb_minus4 =
av_clip(av_log2(ctx->b_per_p + 1) - 2, 0, 12);

- sps->max_num_ref_frames =
- ctx->gop_size == 1 ? 0 : 1 + (ctx->b_per_p > 0);
+ sps->max_num_ref_frames = dpb_frames;

sps->pic_width_in_mbs_minus1 = priv->mb_width - 1;
sps->pic_height_in_map_units_minus1 = priv->mb_height - 1;
@@ -1005,7 +1031,7 @@ static const AVOption vaapi_encode_h264_options[] = {

{ "level", "Set level (level_idc)",
OFFSET(level), AV_OPT_TYPE_INT,
- { .i64 = 51 }, 0x00, 0xff, FLAGS, "level" },
+ { .i64 = FF_LEVEL_UNKNOWN }, FF_LEVEL_UNKNOWN, 0xff, FLAGS, "level" },

#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
{ .i64 = value }, 0, 0, FLAGS, "level"
--
2.16.3
Xiang, Haihao
2018-06-15 02:24:45 UTC
Permalink
Post by Mark Thompson
---
libavcodec/vaapi_encode_h264.c | 34 ++++++++++++++++++++++++++++++----
1 file changed, 30 insertions(+), 4 deletions(-)
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 82166d4457..4034053dc0 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -30,6 +30,7 @@
#include "cbs.h"
#include "cbs_h264.h"
#include "h264.h"
+#include "h264_levels.h"
#include "h264_sei.h"
#include "internal.h"
#include "vaapi_encode.h"
@@ -294,6 +295,7 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
H264RawPPS *pps = &priv->raw_pps;
VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params;
VAEncPictureParameterBufferH264 *vpic = ctx->codec_picture_params;
+ int dpb_frames;
memset(&priv->current_access_unit, 0,
sizeof(priv->current_access_unit));
@@ -319,7 +321,32 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
sps->constraint_set5_flag = ctx->b_per_p == 0;
}
- sps->level_idc = avctx->level;
+ if (ctx->gop_size == 1)
+ dpb_frames = 0;
+ else
+ dpb_frames = 1 + (ctx->b_per_p > 0);
+
+ if (avctx->level != FF_LEVEL_UNKNOWN) {
+ sps->level_idc = avctx->level;
Is avctx->level always legal? if not, I think some checks on avctx->level should
be added.
Post by Mark Thompson
+ } else {
+ const H264LevelDescriptor *level;
+
+ level = ff_h264_guess_level(sps->profile_idc,
+ avctx->bit_rate,
+ priv->mb_width * 16,
+ priv->mb_height * 16,
+ dpb_frames);
+ if (level) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using level %s.\n", level->name);
+ if (level->constraint_set3_flag)
+ sps->constraint_set3_flag = 1;
+ sps->level_idc = level->level_idc;
+ } else {
+ av_log(avctx, AV_LOG_WARNING, "Stream will not conform "
+ "to any level: using level 6.2.\n");
+ sps->level_idc = 62;
+ }
+ }
sps->seq_parameter_set_id = 0;
sps->chroma_format_idc = 1;
@@ -329,8 +356,7 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
sps->log2_max_pic_order_cnt_lsb_minus4 =
av_clip(av_log2(ctx->b_per_p + 1) - 2, 0, 12);
- sps->max_num_ref_frames =
- ctx->gop_size == 1 ? 0 : 1 + (ctx->b_per_p > 0);
+ sps->max_num_ref_frames = dpb_frames;
sps->pic_width_in_mbs_minus1 = priv->mb_width - 1;
sps->pic_height_in_map_units_minus1 = priv->mb_height - 1;
@@ -1005,7 +1031,7 @@ static const AVOption vaapi_encode_h264_options[] = {
{ "level", "Set level (level_idc)",
OFFSET(level), AV_OPT_TYPE_INT,
- { .i64 = 51 }, 0x00, 0xff, FLAGS, "level" },
+ { .i64 = FF_LEVEL_UNKNOWN }, FF_LEVEL_UNKNOWN, 0xff, FLAGS, "level" },
#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
{ .i64 = value }, 0, 0, FLAGS, "level"
Mark Thompson
2018-06-17 14:17:21 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
---
libavcodec/vaapi_encode_h264.c | 34 ++++++++++++++++++++++++++++++----
1 file changed, 30 insertions(+), 4 deletions(-)
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 82166d4457..4034053dc0 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -30,6 +30,7 @@
#include "cbs.h"
#include "cbs_h264.h"
#include "h264.h"
+#include "h264_levels.h"
#include "h264_sei.h"
#include "internal.h"
#include "vaapi_encode.h"
@@ -294,6 +295,7 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
H264RawPPS *pps = &priv->raw_pps;
VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params;
VAEncPictureParameterBufferH264 *vpic = ctx->codec_picture_params;
+ int dpb_frames;
memset(&priv->current_access_unit, 0,
sizeof(priv->current_access_unit));
@@ -319,7 +321,32 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
sps->constraint_set5_flag = ctx->b_per_p == 0;
}
- sps->level_idc = avctx->level;
+ if (ctx->gop_size == 1)
+ dpb_frames = 0;
+ else
+ dpb_frames = 1 + (ctx->b_per_p > 0);
+
+ if (avctx->level != FF_LEVEL_UNKNOWN) {
+ sps->level_idc = avctx->level;
Is avctx->level always legal? if not, I think some checks on avctx->level should
be added.
I'll add a check that it fits in the 8-bit field. I don't think it should be checked any more than that when the user has specified an explicit value.
Post by Xiang, Haihao
Post by Mark Thompson
+ } else {
+ const H264LevelDescriptor *level;
+
+ level = ff_h264_guess_level(sps->profile_idc,
+ avctx->bit_rate,
+ priv->mb_width * 16,
+ priv->mb_height * 16,
+ dpb_frames);
+ if (level) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using level %s.\n", level->name);
+ if (level->constraint_set3_flag)
+ sps->constraint_set3_flag = 1;
+ sps->level_idc = level->level_idc;
+ } else {
+ av_log(avctx, AV_LOG_WARNING, "Stream will not conform "
+ "to any level: using level 6.2.\n");
+ sps->level_idc = 62;
+ }
+ }
sps->seq_parameter_set_id = 0;
sps->chroma_format_idc = 1;
@@ -329,8 +356,7 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
sps->log2_max_pic_order_cnt_lsb_minus4 =
av_clip(av_log2(ctx->b_per_p + 1) - 2, 0, 12);
- sps->max_num_ref_frames =
- ctx->gop_size == 1 ? 0 : 1 + (ctx->b_per_p > 0);
+ sps->max_num_ref_frames = dpb_frames;
sps->pic_width_in_mbs_minus1 = priv->mb_width - 1;
sps->pic_height_in_map_units_minus1 = priv->mb_height - 1;
@@ -1005,7 +1031,7 @@ static const AVOption vaapi_encode_h264_options[] = {
{ "level", "Set level (level_idc)",
OFFSET(level), AV_OPT_TYPE_INT,
- { .i64 = 51 }, 0x00, 0xff, FLAGS, "level" },
+ { .i64 = FF_LEVEL_UNKNOWN }, FF_LEVEL_UNKNOWN, 0xff, FLAGS, "level" },
#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
{ .i64 = value }, 0, 0, FLAGS, "level"
Thanks,

- Mark
Mark Thompson
2018-06-07 23:43:15 UTC
Permalink
constraint_set1_flag should be set for constrained baseline and main
profiles, because the stream conforms to main profile.

constraint_set3_flag should be set for high profile when the stream
is intra-only.

constraint_set4_flag should always be set for main and high profiles
because interlaced encoding is not supported.

constraint_set5_flag should be set for main and high profiles when
B-frames are not used.

Also fix the setting of max_num_ref_frames - use the gop_size value
to check for intra-only rather than the constraint flag (which is not
necessarily set).
---
libavcodec/vaapi_encode_h264.c | 20 ++++++++++++++------
1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index e2f4f4f2f5..a4594ef9f2 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -305,10 +305,19 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
sps->nal_unit_header.nal_unit_type = H264_NAL_SPS;

sps->profile_idc = avctx->profile & 0xff;
- sps->constraint_set1_flag =
- !!(avctx->profile & FF_PROFILE_H264_CONSTRAINED);
- sps->constraint_set3_flag =
- !!(avctx->profile & FF_PROFILE_H264_INTRA);
+
+ if (avctx->profile == FF_PROFILE_H264_CONSTRAINED_BASELINE ||
+ avctx->profile == FF_PROFILE_H264_MAIN)
+ sps->constraint_set1_flag = 1;
+
+ if (avctx->profile == FF_PROFILE_H264_HIGH)
+ sps->constraint_set3_flag = ctx->gop_size == 1;
+
+ if (avctx->profile == FF_PROFILE_H264_MAIN ||
+ avctx->profile == FF_PROFILE_H264_HIGH) {
+ sps->constraint_set4_flag = 1;
+ sps->constraint_set5_flag = ctx->b_per_p == 0;
+ }

sps->level_idc = avctx->level;

@@ -321,8 +330,7 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
av_clip(av_log2(ctx->b_per_p + 1) - 2, 0, 12);

sps->max_num_ref_frames =
- (avctx->profile & FF_PROFILE_H264_INTRA) ? 0 :
- 1 + (ctx->b_per_p > 0);
+ ctx->gop_size == 1 ? 0 : 1 + (ctx->b_per_p > 0);

sps->pic_width_in_mbs_minus1 = priv->mb_width - 1;
sps->pic_height_in_map_units_minus1 = priv->mb_height - 1;
--
2.16.3
Mark Thompson
2018-06-07 23:43:25 UTC
Permalink
The max and default values are 15, not 16.
---
libavcodec/cbs_h264_syntax_template.c | 8 ++++----
libavcodec/vaapi_encode_h264.c | 4 ++--
2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/libavcodec/cbs_h264_syntax_template.c b/libavcodec/cbs_h264_syntax_template.c
index 027b555db6..21edcb799e 100644
--- a/libavcodec/cbs_h264_syntax_template.c
+++ b/libavcodec/cbs_h264_syntax_template.c
@@ -185,16 +185,16 @@ static int FUNC(vui_parameters)(CodedBitstreamContext *ctx, RWContext *rw,
flag(motion_vectors_over_pic_boundaries_flag);
ue(max_bytes_per_pic_denom, 0, 16);
ue(max_bits_per_mb_denom, 0, 16);
- ue(log2_max_mv_length_horizontal, 0, 16);
- ue(log2_max_mv_length_vertical, 0, 16);
+ ue(log2_max_mv_length_horizontal, 0, 15);
+ ue(log2_max_mv_length_vertical, 0, 15);
ue(max_num_reorder_frames, 0, H264_MAX_DPB_FRAMES);
ue(max_dec_frame_buffering, 0, H264_MAX_DPB_FRAMES);
} else {
infer(motion_vectors_over_pic_boundaries_flag, 1);
infer(max_bytes_per_pic_denom, 2);
infer(max_bits_per_mb_denom, 1);
- infer(log2_max_mv_length_horizontal, 16);
- infer(log2_max_mv_length_vertical, 16);
+ infer(log2_max_mv_length_horizontal, 15);
+ infer(log2_max_mv_length_vertical, 15);

if ((sps->profile_idc == 44 || sps->profile_idc == 86 ||
sps->profile_idc == 110 || sps->profile_idc == 110 ||
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 4034053dc0..0d7780110c 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -491,8 +491,8 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)

sps->vui.bitstream_restriction_flag = 1;
sps->vui.motion_vectors_over_pic_boundaries_flag = 1;
- sps->vui.log2_max_mv_length_horizontal = 16;
- sps->vui.log2_max_mv_length_vertical = 16;
+ sps->vui.log2_max_mv_length_horizontal = 15;
+ sps->vui.log2_max_mv_length_vertical = 15;
sps->vui.max_num_reorder_frames = (ctx->b_per_p > 0);
sps->vui.max_dec_frame_buffering = sps->max_num_ref_frames;
--
2.16.3
Xiang, Haihao
2018-06-15 03:01:37 UTC
Permalink
Post by Mark Thompson
The max and default values are 15, not 16.
I guessed you mixed both h264 and h265. The h264 doc i have specifies the range
for log2_max_mv_length_vertical/log2_max_mv_length_horizontal is [0, 16] and the
value of log2_max_mv_length_vertical/log2_max_mv_length_horizontal should be
inferred to 16 when log2_max_mv_length_vertical/log2_max_mv_length_horizontal is
not present.
Post by Mark Thompson
---
libavcodec/cbs_h264_syntax_template.c | 8 ++++----
libavcodec/vaapi_encode_h264.c | 4 ++--
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/libavcodec/cbs_h264_syntax_template.c
b/libavcodec/cbs_h264_syntax_template.c
index 027b555db6..21edcb799e 100644
--- a/libavcodec/cbs_h264_syntax_template.c
+++ b/libavcodec/cbs_h264_syntax_template.c
@@ -185,16 +185,16 @@ static int FUNC(vui_parameters)(CodedBitstreamContext
*ctx, RWContext *rw,
flag(motion_vectors_over_pic_boundaries_flag);
ue(max_bytes_per_pic_denom, 0, 16);
ue(max_bits_per_mb_denom, 0, 16);
- ue(log2_max_mv_length_horizontal, 0, 16);
- ue(log2_max_mv_length_vertical, 0, 16);
+ ue(log2_max_mv_length_horizontal, 0, 15);
+ ue(log2_max_mv_length_vertical, 0, 15);
ue(max_num_reorder_frames, 0, H264_MAX_DPB_FRAMES);
ue(max_dec_frame_buffering, 0, H264_MAX_DPB_FRAMES);
} else {
infer(motion_vectors_over_pic_boundaries_flag, 1);
infer(max_bytes_per_pic_denom, 2);
infer(max_bits_per_mb_denom, 1);
- infer(log2_max_mv_length_horizontal, 16);
- infer(log2_max_mv_length_vertical, 16);
+ infer(log2_max_mv_length_horizontal, 15);
+ infer(log2_max_mv_length_vertical, 15);
if ((sps->profile_idc == 44 || sps->profile_idc == 86 ||
sps->profile_idc == 110 || sps->profile_idc == 110 ||
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 4034053dc0..0d7780110c 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -491,8 +491,8 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
sps->vui.bitstream_restriction_flag = 1;
sps->vui.motion_vectors_over_pic_boundaries_flag = 1;
- sps->vui.log2_max_mv_length_horizontal = 16;
- sps->vui.log2_max_mv_length_vertical = 16;
+ sps->vui.log2_max_mv_length_horizontal = 15;
+ sps->vui.log2_max_mv_length_vertical = 15;
sps->vui.max_num_reorder_frames = (ctx->b_per_p > 0);
sps->vui.max_dec_frame_buffering = sps->max_num_ref_frames;
Mark Thompson
2018-06-17 14:27:52 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
The max and default values are 15, not 16.
I guessed you mixed both h264 and h265. The h264 doc i have specifies the range
for log2_max_mv_length_vertical/log2_max_mv_length_horizontal is [0, 16] and the
value of log2_max_mv_length_vertical/log2_max_mv_length_horizontal should be
inferred to 16 when log2_max_mv_length_vertical/log2_max_mv_length_horizontal is
not present.
Try the 2016/10 version (or later).

I was initially thinking the previous 16 value was an error on my part, but Andreas Rheinhardt noted that it was 16 in older standards and therefore we need to continue to accept 16 as an input value.
Post by Xiang, Haihao
Post by Mark Thompson
---
libavcodec/cbs_h264_syntax_template.c | 8 ++++----
libavcodec/vaapi_encode_h264.c | 4 ++--
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/libavcodec/cbs_h264_syntax_template.c
b/libavcodec/cbs_h264_syntax_template.c
index 027b555db6..21edcb799e 100644
--- a/libavcodec/cbs_h264_syntax_template.c
+++ b/libavcodec/cbs_h264_syntax_template.c
@@ -185,16 +185,16 @@ static int FUNC(vui_parameters)(CodedBitstreamContext
*ctx, RWContext *rw,
flag(motion_vectors_over_pic_boundaries_flag);
ue(max_bytes_per_pic_denom, 0, 16);
ue(max_bits_per_mb_denom, 0, 16);
- ue(log2_max_mv_length_horizontal, 0, 16);
- ue(log2_max_mv_length_vertical, 0, 16);
+ ue(log2_max_mv_length_horizontal, 0, 15);
+ ue(log2_max_mv_length_vertical, 0, 15);
So, removed this change to the bounds but kept the new inferred values below.
Post by Xiang, Haihao
Post by Mark Thompson
ue(max_num_reorder_frames, 0, H264_MAX_DPB_FRAMES);
ue(max_dec_frame_buffering, 0, H264_MAX_DPB_FRAMES);
} else {
infer(motion_vectors_over_pic_boundaries_flag, 1);
infer(max_bytes_per_pic_denom, 2);
infer(max_bits_per_mb_denom, 1);
- infer(log2_max_mv_length_horizontal, 16);
- infer(log2_max_mv_length_vertical, 16);
+ infer(log2_max_mv_length_horizontal, 15);
+ infer(log2_max_mv_length_vertical, 15);
if ((sps->profile_idc == 44 || sps->profile_idc == 86 ||
sps->profile_idc == 110 || sps->profile_idc == 110 ||
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 4034053dc0..0d7780110c 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -491,8 +491,8 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
sps->vui.bitstream_restriction_flag = 1;
sps->vui.motion_vectors_over_pic_boundaries_flag = 1;
- sps->vui.log2_max_mv_length_horizontal = 16;
- sps->vui.log2_max_mv_length_vertical = 16;
+ sps->vui.log2_max_mv_length_horizontal = 15;
+ sps->vui.log2_max_mv_length_vertical = 15;
sps->vui.max_num_reorder_frames = (ctx->b_per_p > 0);
sps->vui.max_dec_frame_buffering = sps->max_num_ref_frames;
Thanks,

- Mark
Mark Thompson
2018-06-07 23:43:24 UTC
Permalink
---
doc/bitstream_filters.texi | 9 +++++
libavcodec/h264_metadata_bsf.c | 90 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 99 insertions(+)

diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi
index 7d7e97503a..d948c6d658 100644
--- a/doc/bitstream_filters.texi
+++ b/doc/bitstream_filters.texi
@@ -215,6 +215,15 @@ insert the string ``hello'' associated with the given UUID.
@item delete_filler
Deletes both filler NAL units and filler SEI messages.

+@item level
+Set the level in the SPS. Refer to H.264 section A.3 and tables A-1
+to A-5.
+
+The argument must be the name of a level (for example, @samp{4.2}), a
+level_idc value (for example, @samp{42}), or the special name @samp{auto}
+indicating that the filter should attempt to guess the level from the
+input stream properties.
+
@end table

@section h264_mp4toannexb
diff --git a/libavcodec/h264_metadata_bsf.c b/libavcodec/h264_metadata_bsf.c
index 90ad4aad98..cb1a835fb8 100644
--- a/libavcodec/h264_metadata_bsf.c
+++ b/libavcodec/h264_metadata_bsf.c
@@ -25,6 +25,7 @@
#include "cbs.h"
#include "cbs_h264.h"
#include "h264.h"
+#include "h264_levels.h"
#include "h264_sei.h"

enum {
@@ -39,6 +40,11 @@ enum {
FLIP_VERTICAL = 2,
};

+enum {
+ LEVEL_UNSET = -2,
+ LEVEL_AUTO = -1,
+};
+
typedef struct H264MetadataContext {
const AVClass *class;

@@ -74,6 +80,8 @@ typedef struct H264MetadataContext {
int display_orientation;
double rotate;
int flip;
+
+ int level;
} H264MetadataContext;


@@ -208,6 +216,58 @@ static int h264_metadata_update_sps(AVBSFContext *bsf,
CROP(bottom, crop_unit_y);
#undef CROP

+ if (ctx->level != LEVEL_UNSET) {
+ int level_idc;
+
+ if (ctx->level == LEVEL_AUTO) {
+ const H264LevelDescriptor *desc;
+ int64_t bit_rate;
+ int width, height;
+
+ if (sps->vui.nal_hrd_parameters_present_flag) {
+ bit_rate = (sps->vui.nal_hrd_parameters.bit_rate_value_minus1[0] + 1) *
+ (1 << (sps->vui.nal_hrd_parameters.bit_rate_scale + 6));
+ } else if (sps->vui.vcl_hrd_parameters_present_flag) {
+ bit_rate = (sps->vui.vcl_hrd_parameters.bit_rate_value_minus1[0] + 1) *
+ (1 << (sps->vui.vcl_hrd_parameters.bit_rate_scale + 6));
+ // Adjust for VCL vs. NAL limits.
+ bit_rate = bit_rate * 6 / 5;
+ } else {
+ bit_rate = 0;
+ }
+
+ width = 16 * (sps->pic_width_in_mbs_minus1 + 1);
+ height = 16 * (sps->pic_height_in_map_units_minus1 + 1) *
+ (2 - sps->frame_mbs_only_flag);
+
+ desc = ff_h264_guess_level(sps->profile_idc, bit_rate,
+ width, height,
+ sps->vui.max_dec_frame_buffering);
+ if (desc) {
+ level_idc = desc->level_idc;
+ } else {
+ av_log(bsf, AV_LOG_WARNING, "Stream does not appear to "
+ "conform to any level: using level 6.2.\n");
+ level_idc = 62;
+ }
+ } else {
+ level_idc = ctx->level;
+ }
+
+ if (level_idc == 9) {
+ if (sps->profile_idc == 66 ||
+ sps->profile_idc == 77 ||
+ sps->profile_idc == 88) {
+ sps->level_idc = 10;
+ sps->constraint_set3_flag = 1;
+ } else {
+ sps->level_idc = 9;
+ }
+ } else {
+ sps->level_idc = level_idc;
+ }
+ }
+
if (need_vui)
sps->vui_parameters_present_flag = 1;

@@ -683,6 +743,36 @@ static const AVOption h264_metadata_options[] = {
0, AV_OPT_TYPE_CONST,
{ .i64 = FLIP_VERTICAL }, .flags = FLAGS, .unit = "flip" },

+ { "level", "Set level (table A-1)",
+ OFFSET(level), AV_OPT_TYPE_INT,
+ { .i64 = LEVEL_UNSET }, LEVEL_UNSET, 0xff, FLAGS, "level" },
+ { "auto", "Attempt to guess level from stream properties",
+ 0, AV_OPT_TYPE_CONST,
+ { .i64 = LEVEL_AUTO }, 0, 0, FLAGS, "level" },
+#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
+ { .i64 = value }, 0, 0, FLAGS, "level"
+ { LEVEL("1", 10) },
+ { LEVEL("1b", 9) },
+ { LEVEL("1.1", 11) },
+ { LEVEL("1.2", 12) },
+ { LEVEL("1.3", 13) },
+ { LEVEL("2", 20) },
+ { LEVEL("2.1", 21) },
+ { LEVEL("2.2", 22) },
+ { LEVEL("3", 30) },
+ { LEVEL("3.1", 31) },
+ { LEVEL("3.2", 32) },
+ { LEVEL("4", 40) },
+ { LEVEL("4.1", 41) },
+ { LEVEL("4.2", 42) },
+ { LEVEL("5", 50) },
+ { LEVEL("5.1", 51) },
+ { LEVEL("5.2", 52) },
+ { LEVEL("6", 60) },
+ { LEVEL("6.1", 61) },
+ { LEVEL("6.2", 62) },
+#undef LEVEL
+
{ NULL }
};
--
2.16.3
Michael Niedermayer
2018-06-08 23:19:23 UTC
Permalink
Post by Mark Thompson
---
doc/bitstream_filters.texi | 9 +++++
libavcodec/h264_metadata_bsf.c | 90 ++++++++++++++++++++++++++++++++++++++++++
2 files changed, 99 insertions(+)
this breaks the mingw64 build

LD ffmpeg_g.exe
libavcodec/libavcodec.a(h264_metadata_bsf.o): In function `h264_metadata_update_sps':
mingw64/src/libavcodec/h264_metadata_bsf.c:243: undefined reference to `ff_h264_guess_level'
mingw64/src/libavcodec/h264_metadata_bsf.c:243:(.text+0x36c): relocation truncated to fit: R_X86_64_PC32 against undefined symbol `ff_h264_guess_level'
collect2: error: ld returned 1 exit status
make: *** [ffmpeg_g.exe] Error 1

[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

You can kill me, but you cannot change the truth.
Xiang, Haihao
2018-06-15 02:49:02 UTC
Permalink
Post by Mark Thompson
---
doc/bitstream_filters.texi | 9 +++++
libavcodec/h264_metadata_bsf.c | 90
++++++++++++++++++++++++++++++++++++++++++
2 files changed, 99 insertions(+)
diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi
index 7d7e97503a..d948c6d658 100644
--- a/doc/bitstream_filters.texi
+++ b/doc/bitstream_filters.texi
@@ -215,6 +215,15 @@ insert the string ``hello'' associated with the given UUID.
@item delete_filler
Deletes both filler NAL units and filler SEI messages.
+Set the level in the SPS. Refer to H.264 section A.3 and tables A-1
+to A-5.
+
+indicating that the filter should attempt to guess the level from the
+input stream properties.
+
@end table
@section h264_mp4toannexb
diff --git a/libavcodec/h264_metadata_bsf.c b/libavcodec/h264_metadata_bsf.c
index 90ad4aad98..cb1a835fb8 100644
--- a/libavcodec/h264_metadata_bsf.c
+++ b/libavcodec/h264_metadata_bsf.c
@@ -25,6 +25,7 @@
#include "cbs.h"
#include "cbs_h264.h"
#include "h264.h"
+#include "h264_levels.h"
#include "h264_sei.h"
enum {
@@ -39,6 +40,11 @@ enum {
FLIP_VERTICAL = 2,
};
+enum {
+ LEVEL_UNSET = -2,
+ LEVEL_AUTO = -1,
+};
+
typedef struct H264MetadataContext {
const AVClass *class;
@@ -74,6 +80,8 @@ typedef struct H264MetadataContext {
int display_orientation;
double rotate;
int flip;
+
+ int level;
} H264MetadataContext;
@@ -208,6 +216,58 @@ static int h264_metadata_update_sps(AVBSFContext *bsf,
CROP(bottom, crop_unit_y);
#undef CROP
+ if (ctx->level != LEVEL_UNSET) {
+ int level_idc;
+
+ if (ctx->level == LEVEL_AUTO) {
+ const H264LevelDescriptor *desc;
+ int64_t bit_rate;
+ int width, height;
+
+ if (sps->vui.nal_hrd_parameters_present_flag) {
+ bit_rate = (sps-
Post by Mark Thompson
vui.nal_hrd_parameters.bit_rate_value_minus1[0] + 1) *
+ (1 << (sps->vui.nal_hrd_parameters.bit_rate_scale + 6));
+ } else if (sps->vui.vcl_hrd_parameters_present_flag) {
+ bit_rate = (sps-
Post by Mark Thompson
vui.vcl_hrd_parameters.bit_rate_value_minus1[0] + 1) *
+ (1 << (sps->vui.vcl_hrd_parameters.bit_rate_scale + 6));
+ // Adjust for VCL vs. NAL limits.
+ bit_rate = bit_rate * 6 / 5;
+ } else {
+ bit_rate = 0;
+ }
+
+ width = 16 * (sps->pic_width_in_mbs_minus1 + 1);
+ height = 16 * (sps->pic_height_in_map_units_minus1 + 1) *
+ (2 - sps->frame_mbs_only_flag);
+
+ desc = ff_h264_guess_level(sps->profile_idc, bit_rate,
+ width, height,
+ sps->vui.max_dec_frame_buffering);
+ if (desc) {
+ level_idc = desc->level_idc;
+ } else {
+ av_log(bsf, AV_LOG_WARNING, "Stream does not appear to "
+ "conform to any level: using level 6.2.\n");
+ level_idc = 62;
+ }
+ } else {
+ level_idc = ctx->level;
+ }
+
+ if (level_idc == 9) {
+ if (sps->profile_idc == 66 ||
+ sps->profile_idc == 77 ||
+ sps->profile_idc == 88) {
+ sps->level_idc = 10;
+ sps->constraint_set3_flag = 1;
+ } else {
+ sps->level_idc = 9;
+ }
+ } else {
+ sps->level_idc = level_idc;
+ }
+ }
+
if (need_vui)
sps->vui_parameters_present_flag = 1;
@@ -683,6 +743,36 @@ static const AVOption h264_metadata_options[] = {
0, AV_OPT_TYPE_CONST,
{ .i64 = FLIP_VERTICAL }, .flags = FLAGS, .unit = "flip" },
+ { "level", "Set level (table A-1)",
+ OFFSET(level), AV_OPT_TYPE_INT,
+ { .i64 = LEVEL_UNSET }, LEVEL_UNSET, 0xff, FLAGS, "level" },
+ { "auto", "Attempt to guess level from stream properties",
+ 0, AV_OPT_TYPE_CONST,
+ { .i64 = LEVEL_AUTO }, 0, 0, FLAGS, "level" },
Could you please use the same code style for AV_OPT_TYPE_CONST ?
Post by Mark Thompson
+#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
+ { .i64 = value }, 0, 0, FLAGS, "level"
Also here.
Post by Mark Thompson
+ { LEVEL("1", 10) },
+ { LEVEL("1b", 9) },
+ { LEVEL("1.1", 11) },
+ { LEVEL("1.2", 12) },
+ { LEVEL("1.3", 13) },
+ { LEVEL("2", 20) },
+ { LEVEL("2.1", 21) },
+ { LEVEL("2.2", 22) },
+ { LEVEL("3", 30) },
+ { LEVEL("3.1", 31) },
+ { LEVEL("3.2", 32) },
+ { LEVEL("4", 40) },
+ { LEVEL("4.1", 41) },
+ { LEVEL("4.2", 42) },
+ { LEVEL("5", 50) },
+ { LEVEL("5.1", 51) },
+ { LEVEL("5.2", 52) },
+ { LEVEL("6", 60) },
+ { LEVEL("6.1", 61) },
+ { LEVEL("6.2", 62) },
+#undef LEVEL
+
{ NULL }
};
Mark Thompson
2018-06-17 14:20:04 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
---
doc/bitstream_filters.texi | 9 +++++
libavcodec/h264_metadata_bsf.c | 90
++++++++++++++++++++++++++++++++++++++++++
2 files changed, 99 insertions(+)
...
diff --git a/libavcodec/h264_metadata_bsf.c b/libavcodec/h264_metadata_bsf.c
index 90ad4aad98..cb1a835fb8 100644
--- a/libavcodec/h264_metadata_bsf.c
+++ b/libavcodec/h264_metadata_bsf.c
...
@@ -683,6 +743,36 @@ static const AVOption h264_metadata_options[] = {
0, AV_OPT_TYPE_CONST,
{ .i64 = FLIP_VERTICAL }, .flags = FLAGS, .unit = "flip" },
+ { "level", "Set level (table A-1)",
+ OFFSET(level), AV_OPT_TYPE_INT,
+ { .i64 = LEVEL_UNSET }, LEVEL_UNSET, 0xff, FLAGS, "level" },
+ { "auto", "Attempt to guess level from stream properties",
+ 0, AV_OPT_TYPE_CONST,
+ { .i64 = LEVEL_AUTO }, 0, 0, FLAGS, "level" },
Could you please use the same code style for AV_OPT_TYPE_CONST ?
Post by Mark Thompson
+#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
+ { .i64 = value }, 0, 0, FLAGS, "level"
Also here.
Both changed to use the designated initialiser.
Post by Xiang, Haihao
Post by Mark Thompson
+ { LEVEL("1", 10) },
+ { LEVEL("1b", 9) },
+ { LEVEL("1.1", 11) },
+ { LEVEL("1.2", 12) },
+ { LEVEL("1.3", 13) },
+ { LEVEL("2", 20) },
+ { LEVEL("2.1", 21) },
+ { LEVEL("2.2", 22) },
+ { LEVEL("3", 30) },
+ { LEVEL("3.1", 31) },
+ { LEVEL("3.2", 32) },
+ { LEVEL("4", 40) },
+ { LEVEL("4.1", 41) },
+ { LEVEL("4.2", 42) },
+ { LEVEL("5", 50) },
+ { LEVEL("5.1", 51) },
+ { LEVEL("5.2", 52) },
+ { LEVEL("6", 60) },
+ { LEVEL("6.1", 61) },
+ { LEVEL("6.2", 62) },
+#undef LEVEL
+
{ NULL }
};
Thanks,

- Mark
Mark Thompson
2018-06-07 23:43:30 UTC
Permalink
Sets the level based on the stream properties if it is not explicitly
set by the user. Also add a tier option to set general_tier_flag, since
that affects the level choice.
---
doc/encoders.texi | 4 ++++
libavcodec/vaapi_encode_h265.c | 34 +++++++++++++++++++++++++++++++---
2 files changed, 35 insertions(+), 3 deletions(-)

diff --git a/doc/encoders.texi b/doc/encoders.texi
index ceddfdda64..d61a1cc4bc 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -2659,6 +2659,10 @@ Include recovery points where appropriate (@emph{recovery_point} messages).
@item aud
Include access unit delimiters in the stream (not included by default).

+@item tier
+Set @emph{general_tier_flag}. This may affect the level chosen for the stream
+if it is not explicitly specified.
+
@end table

@item mjpeg_vaapi
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 2cee19be68..100f8338d7 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -30,6 +30,7 @@
#include "avcodec.h"
#include "cbs.h"
#include "cbs_h265.h"
+#include "h265_profile_level.h"
#include "hevc.h"
#include "hevc_sei.h"
#include "internal.h"
@@ -48,6 +49,7 @@ typedef struct VAAPIEncodeH265Context {
int qp;
int aud;
int profile;
+ int tier;
int level;
int sei;

@@ -315,7 +317,7 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
ptl->general_profile_space = 0;
ptl->general_profile_space = 0;
ptl->general_profile_idc = avctx->profile;
- ptl->general_tier_flag = 0;
+ ptl->general_tier_flag = priv->tier;

if (chroma_format == 1) {
ptl->general_profile_compatibility_flag[1] = bit_depth == 8;
@@ -340,7 +342,25 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)

ptl->general_lower_bit_rate_constraint_flag = 1;

- ptl->general_level_idc = avctx->level;
+ if (avctx->level != FF_LEVEL_UNKNOWN) {
+ ptl->general_level_idc = avctx->level;
+ } else {
+ const H265LevelDescriptor *level;
+
+ level = ff_h265_guess_level(ptl, avctx->bit_rate,
+ ctx->surface_width, ctx->surface_height,
+ 1, 1, 1, (ctx->b_per_p > 0) + 1);
+ if (level) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using level %s.\n", level->name);
+ ptl->general_level_idc = level->level_idc;
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Stream will not conform to "
+ "any normal level; using level 8.5.\n");
+ ptl->general_level_idc = 255;
+ // The tier flag must be set in level 8.5.
+ ptl->general_tier_flag = 1;
+ }
+ }

vps->vps_sub_layer_ordering_info_present_flag = 0;
vps->vps_max_dec_pic_buffering_minus1[0] = (ctx->b_per_p > 0) + 1;
@@ -1146,9 +1166,17 @@ static const AVOption vaapi_encode_h265_options[] = {
{ PROFILE("rext", FF_PROFILE_HEVC_REXT) },
#undef PROFILE

+ { "tier", "Set tier (general_tier_flag)",
+ OFFSET(tier), AV_OPT_TYPE_INT,
+ { .i64 = 0 }, 0, 1, FLAGS, "tier" },
+ { "main", NULL, 0, AV_OPT_TYPE_CONST,
+ { .i64 = 0 }, 0, 0, FLAGS, "tier" },
+ { "high", NULL, 0, AV_OPT_TYPE_CONST,
+ { .i64 = 1 }, 0, 0, FLAGS, "tier" },
+
{ "level", "Set level (general_level_idc)",
OFFSET(level), AV_OPT_TYPE_INT,
- { .i64 = 153 }, 0x00, 0xff, FLAGS, "level" },
+ { .i64 = FF_LEVEL_UNKNOWN }, FF_LEVEL_UNKNOWN, 0xff, FLAGS, "level" },

#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
{ .i64 = value }, 0, 0, FLAGS, "level"
--
2.16.3
Xiang, Haihao
2018-06-15 04:22:31 UTC
Permalink
Post by Mark Thompson
Sets the level based on the stream properties if it is not explicitly
set by the user. Also add a tier option to set general_tier_flag, since
that affects the level choice.
---
doc/encoders.texi | 4 ++++
libavcodec/vaapi_encode_h265.c | 34 +++++++++++++++++++++++++++++++---
2 files changed, 35 insertions(+), 3 deletions(-)
diff --git a/doc/encoders.texi b/doc/encoders.texi
index ceddfdda64..d61a1cc4bc 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -2659,6 +2659,10 @@ Include recovery points where appropriate
@item aud
Include access unit delimiters in the stream (not included by default).
+if it is not explicitly specified.
+
@end table
@item mjpeg_vaapi
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 2cee19be68..100f8338d7 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -30,6 +30,7 @@
#include "avcodec.h"
#include "cbs.h"
#include "cbs_h265.h"
+#include "h265_profile_level.h"
#include "hevc.h"
#include "hevc_sei.h"
#include "internal.h"
@@ -48,6 +49,7 @@ typedef struct VAAPIEncodeH265Context {
int qp;
int aud;
int profile;
+ int tier;
int level;
int sei;
@@ -315,7 +317,7 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
ptl->general_profile_space = 0;
ptl->general_profile_space = 0;
ptl->general_profile_idc = avctx->profile;
- ptl->general_tier_flag = 0;
+ ptl->general_tier_flag = priv->tier;
if (chroma_format == 1) {
ptl->general_profile_compatibility_flag[1] = bit_depth == 8;
@@ -340,7 +342,25 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
ptl->general_lower_bit_rate_constraint_flag = 1;
- ptl->general_level_idc = avctx->level;
+ if (avctx->level != FF_LEVEL_UNKNOWN) {
+ ptl->general_level_idc = avctx->level;
Fist check whether the level idc is legal? User may set a level which can not
be found in h265_levels.
Post by Mark Thompson
+ } else {
+ const H265LevelDescriptor *level;
+
+ level = ff_h265_guess_level(ptl, avctx->bit_rate,
+ ctx->surface_width, ctx->surface_height,
+ 1, 1, 1, (ctx->b_per_p > 0) + 1);
+ if (level) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using level %s.\n", level->name);
+ ptl->general_level_idc = level->level_idc;
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Stream will not conform to "
+ "any normal level; using level 8.5.\n");
+ ptl->general_level_idc = 255;
+ // The tier flag must be set in level 8.5.
+ ptl->general_tier_flag = 1;
+ }
+ }
vps->vps_sub_layer_ordering_info_present_flag = 0;
vps->vps_max_dec_pic_buffering_minus1[0] = (ctx->b_per_p > 0) + 1;
@@ -1146,9 +1166,17 @@ static const AVOption vaapi_encode_h265_options[] = {
{ PROFILE("rext", FF_PROFILE_HEVC_REXT) },
#undef PROFILE
+ { "tier", "Set tier (general_tier_flag)",
+ OFFSET(tier), AV_OPT_TYPE_INT,
+ { .i64 = 0 }, 0, 1, FLAGS, "tier" },
+ { "main", NULL, 0, AV_OPT_TYPE_CONST,
+ { .i64 = 0 }, 0, 0, FLAGS, "tier" },
+ { "high", NULL, 0, AV_OPT_TYPE_CONST,
+ { .i64 = 1 }, 0, 0, FLAGS, "tier" },
+
{ "level", "Set level (general_level_idc)",
OFFSET(level), AV_OPT_TYPE_INT,
- { .i64 = 153 }, 0x00, 0xff, FLAGS, "level" },
+ { .i64 = FF_LEVEL_UNKNOWN }, FF_LEVEL_UNKNOWN, 0xff, FLAGS, "level" },
#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
{ .i64 = value }, 0, 0, FLAGS, "level"
Mark Thompson
2018-06-17 14:34:03 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
Sets the level based on the stream properties if it is not explicitly
set by the user. Also add a tier option to set general_tier_flag, since
that affects the level choice.
---
doc/encoders.texi | 4 ++++
libavcodec/vaapi_encode_h265.c | 34 +++++++++++++++++++++++++++++++---
2 files changed, 35 insertions(+), 3 deletions(-)
...
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 2cee19be68..100f8338d7 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -30,6 +30,7 @@
#include "avcodec.h"
#include "cbs.h"
#include "cbs_h265.h"
+#include "h265_profile_level.h"
#include "hevc.h"
#include "hevc_sei.h"
#include "internal.h"
@@ -48,6 +49,7 @@ typedef struct VAAPIEncodeH265Context {
int qp;
int aud;
int profile;
+ int tier;
int level;
int sei;
@@ -315,7 +317,7 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
ptl->general_profile_space = 0;
ptl->general_profile_space = 0;
ptl->general_profile_idc = avctx->profile;
- ptl->general_tier_flag = 0;
+ ptl->general_tier_flag = priv->tier;
if (chroma_format == 1) {
ptl->general_profile_compatibility_flag[1] = bit_depth == 8;
@@ -340,7 +342,25 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
ptl->general_lower_bit_rate_constraint_flag = 1;
- ptl->general_level_idc = avctx->level;
+ if (avctx->level != FF_LEVEL_UNKNOWN) {
+ ptl->general_level_idc = avctx->level;
Fist check whether the level idc is legal? User may set a level which can not
be found in h265_levels.
The user should be able to specify an arbitrary value explicitly. As for H.264 I'll add a check that this fits in the 8-bit field, though.
Post by Xiang, Haihao
Post by Mark Thompson
+ } else {
+ const H265LevelDescriptor *level;
+
+ level = ff_h265_guess_level(ptl, avctx->bit_rate,
+ ctx->surface_width, ctx->surface_height,
+ 1, 1, 1, (ctx->b_per_p > 0) + 1);
+ if (level) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using level %s.\n", level->name);
+ ptl->general_level_idc = level->level_idc;
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Stream will not conform to "
+ "any normal level; using level 8.5.\n");
+ ptl->general_level_idc = 255;
+ // The tier flag must be set in level 8.5.
+ ptl->general_tier_flag = 1;
+ }
+ }
vps->vps_sub_layer_ordering_info_present_flag = 0;
vps->vps_max_dec_pic_buffering_minus1[0] = (ctx->b_per_p > 0) + 1;
@@ -1146,9 +1166,17 @@ static const AVOption vaapi_encode_h265_options[] = {
{ PROFILE("rext", FF_PROFILE_HEVC_REXT) },
#undef PROFILE
+ { "tier", "Set tier (general_tier_flag)",
+ OFFSET(tier), AV_OPT_TYPE_INT,
+ { .i64 = 0 }, 0, 1, FLAGS, "tier" },
+ { "main", NULL, 0, AV_OPT_TYPE_CONST,
+ { .i64 = 0 }, 0, 0, FLAGS, "tier" },
+ { "high", NULL, 0, AV_OPT_TYPE_CONST,
+ { .i64 = 1 }, 0, 0, FLAGS, "tier" },
+
{ "level", "Set level (general_level_idc)",
OFFSET(level), AV_OPT_TYPE_INT,
- { .i64 = 153 }, 0x00, 0xff, FLAGS, "level" },
+ { .i64 = FF_LEVEL_UNKNOWN }, FF_LEVEL_UNKNOWN, 0xff, FLAGS, "level" },
#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
{ .i64 = value }, 0, 0, FLAGS, "level"
Thanks,

- Mark
Mark Thompson
2018-06-07 23:43:14 UTC
Permalink
Add a larger warning more clearly explaining the consequences of missing
packed header support in the driver. Also only write the extradata if the
user actually requests it via the GLOBAL_HEADER flag.
---
libavcodec/vaapi_encode.c | 118 +++++++++++++++++++++-------------------
libavcodec/vaapi_encode.h | 7 ++-
libavcodec/vaapi_encode_h264.c | 2 +-
libavcodec/vaapi_encode_h265.c | 2 +-
libavcodec/vaapi_encode_mjpeg.c | 2 +-
libavcodec/vaapi_encode_mpeg2.c | 4 +-
libavcodec/vaapi_encode_vp8.c | 6 +-
libavcodec/vaapi_encode_vp9.c | 6 +-
8 files changed, 79 insertions(+), 68 deletions(-)

diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index af9224c98f..14d1846ea3 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -1210,60 +1210,6 @@ fail:
return err;
}

-static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
-{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAStatus vas;
- int i;
-
- VAConfigAttrib attr[] = {
- { VAConfigAttribEncPackedHeaders },
- };
-
- vas = vaGetConfigAttributes(ctx->hwctx->display,
- ctx->va_profile, ctx->va_entrypoint,
- attr, FF_ARRAY_ELEMS(attr));
- if (vas != VA_STATUS_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "Failed to fetch config "
- "attributes: %d (%s).\n", vas, vaErrorStr(vas));
- return AVERROR(EINVAL);
- }
-
- for (i = 0; i < FF_ARRAY_ELEMS(attr); i++) {
- if (attr[i].value == VA_ATTRIB_NOT_SUPPORTED) {
- // Unfortunately we have to treat this as "don't know" and hope
- // for the best, because the Intel MJPEG encoder returns this
- // for all the interesting attributes.
- av_log(avctx, AV_LOG_DEBUG, "Attribute (%d) is not supported.\n",
- attr[i].type);
- continue;
- }
- switch (attr[i].type) {
- 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 0;
-}
-
static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
@@ -1494,6 +1440,65 @@ static av_cold int vaapi_encode_init_gop_structure(AVCodecContext *avctx)
return 0;
}

+static av_cold int vaapi_encode_init_packed_headers(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAStatus vas;
+ VAConfigAttrib attr = { VAConfigAttribEncPackedHeaders };
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile,
+ ctx->va_entrypoint,
+ &attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query packed headers "
+ "attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
+
+ if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ if (ctx->packed_headers) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support any "
+ "packed headers (wanted %#x).\n", ctx->packed_headers);
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not support any "
+ "packed headers (none wanted).\n");
+ }
+ ctx->va_packed_headers = 0;
+ } else {
+ if (ctx->packed_headers & ~attr.value) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support some "
+ "wanted packed headers (wanted %#x, found %#x).\n",
+ ctx->packed_headers, attr.value);
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "All wanted packed headers "
+ "available (wanted %#x, found %#x).\n",
+ ctx->packed_headers, attr.value);
+ }
+ ctx->va_packed_headers = ctx->packed_headers & attr.value;
+ }
+
+ if (ctx->va_packed_headers) {
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribEncPackedHeaders,
+ .value = ctx->va_packed_headers,
+ };
+ }
+
+ if ( (ctx->packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE) &&
+ !(ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE) &&
+ (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support packed "
+ "sequence headers, but a global header is requested.\n");
+ av_log(avctx, AV_LOG_WARNING, "No global header will be written: "
+ "this may result in a stream which is not usable for some "
+ "purposes (e.g. not muxable to some containers).\n");
+ }
+
+ return 0;
+}
+
static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx)
{
#if VA_CHECK_VERSION(0, 36, 0)
@@ -1724,7 +1729,7 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;

- err = vaapi_encode_config_attributes(avctx);
+ err = vaapi_encode_init_packed_headers(avctx);
if (err < 0)
goto fail;

@@ -1813,7 +1818,8 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
ctx->issue_mode = ISSUE_MODE_MAXIMISE_THROUGHPUT;

if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE &&
- ctx->codec->write_sequence_header) {
+ ctx->codec->write_sequence_header &&
+ avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
char data[MAX_PARAM_BUFFER_SIZE];
size_t bit_len = 8 * sizeof(data);

diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index bbec721bca..e6acd933d6 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -116,9 +116,8 @@ typedef struct VAAPIEncodeContext {
// Use low power encoding mode.
int low_power;

- // Supported packed headers (initially the desired set, modified
- // later to what is actually supported).
- unsigned int va_packed_headers;
+ // Desired packed headers.
+ unsigned int packed_headers;

// The required size of surfaces. This is probably the input
// size (AVCodecContext.width|height) aligned up to whatever
@@ -140,6 +139,8 @@ typedef struct VAAPIEncodeContext {
unsigned int va_rc_mode;
// Bitrate for codec-specific encoder parameters.
unsigned int va_bit_rate;
+ // Packed headers which will actually be sent.
+ unsigned int va_packed_headers;

// Configuration attributes to use when creating va_config.
VAConfigAttrib config_attributes[MAX_CONFIG_ATTRIBUTES];
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 717be024ca..e2f4f4f2f5 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -930,7 +930,7 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
return AVERROR_PATCHWELCOME;
}

- ctx->va_packed_headers =
+ ctx->packed_headers =
VA_ENC_PACKED_HEADER_SEQUENCE | // SPS and PPS.
VA_ENC_PACKED_HEADER_SLICE | // Slice headers.
VA_ENC_PACKED_HEADER_MISC; // SEI.
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index b2d6b8d24d..04dfef27c8 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -1064,7 +1064,7 @@ static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx)
if (avctx->level == FF_LEVEL_UNKNOWN)
avctx->level = priv->level;

- ctx->va_packed_headers =
+ ctx->packed_headers =
VA_ENC_PACKED_HEADER_SEQUENCE | // VPS, SPS and PPS.
VA_ENC_PACKED_HEADER_SLICE | // Slice headers.
VA_ENC_PACKED_HEADER_MISC; // SEI
diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index 4982cd166f..f76645425a 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -389,7 +389,7 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx)
ctx->codec = &vaapi_encode_type_mjpeg;

// The JPEG image header - see note above.
- ctx->va_packed_headers =
+ ctx->packed_headers =
VA_ENC_PACKED_HEADER_RAW_DATA;

ctx->surface_width = FFALIGN(avctx->width, 8);
diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index b6edca9158..1afd753fc1 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -615,8 +615,8 @@ static av_cold int vaapi_encode_mpeg2_init(AVCodecContext *avctx)
return AVERROR(EINVAL);
}

- ctx->va_packed_headers = VA_ENC_PACKED_HEADER_SEQUENCE |
- VA_ENC_PACKED_HEADER_PICTURE;
+ ctx->packed_headers = VA_ENC_PACKED_HEADER_SEQUENCE |
+ VA_ENC_PACKED_HEADER_PICTURE;

ctx->surface_width = FFALIGN(avctx->width, 16);
ctx->surface_height = FFALIGN(avctx->height, 16);
diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c
index 4512609a19..51ba601c5e 100644
--- a/libavcodec/vaapi_encode_vp8.c
+++ b/libavcodec/vaapi_encode_vp8.c
@@ -200,8 +200,10 @@ static av_cold int vaapi_encode_vp8_init(AVCodecContext *avctx)

ctx->codec = &vaapi_encode_type_vp8;

- // Packed headers are not currently supported.
- ctx->va_packed_headers = 0;
+ // No packed headers are currently desired. VP8 has no metadata
+ // which would be useful to write, and no existing driver supports
+ // adding them anyway.
+ ctx->packed_headers = 0;

ctx->surface_width = FFALIGN(avctx->width, 16);
ctx->surface_height = FFALIGN(avctx->height, 16);
diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index d4069ec850..537a92c48c 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -228,8 +228,10 @@ static av_cold int vaapi_encode_vp9_init(AVCodecContext *avctx)

ctx->codec = &vaapi_encode_type_vp9;

- // Packed headers are not currently supported.
- ctx->va_packed_headers = 0;
+ // No packed headers are currently desired. They could be written,
+ // but there isn't any reason to do so - the one usable driver (i965)
+ // can write its own headers and there is no metadata to include.
+ ctx->packed_headers = 0;

// Surfaces must be aligned to superblock boundaries.
ctx->surface_width = FFALIGN(avctx->width, 64);
--
2.16.3
Xiang, Haihao
2018-06-14 07:15:12 UTC
Permalink
Post by Mark Thompson
Add a larger warning more clearly explaining the consequences of missing
packed header support in the driver. Also only write the extradata if the
user actually requests it via the GLOBAL_HEADER flag.
---
libavcodec/vaapi_encode.c | 118 +++++++++++++++++++++----------------
---
libavcodec/vaapi_encode.h | 7 ++-
libavcodec/vaapi_encode_h264.c | 2 +-
libavcodec/vaapi_encode_h265.c | 2 +-
libavcodec/vaapi_encode_mjpeg.c | 2 +-
libavcodec/vaapi_encode_mpeg2.c | 4 +-
libavcodec/vaapi_encode_vp8.c | 6 +-
libavcodec/vaapi_encode_vp9.c | 6 +-
8 files changed, 79 insertions(+), 68 deletions(-)
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index af9224c98f..14d1846ea3 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
return err;
}
-static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
-{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAStatus vas;
- int i;
-
- VAConfigAttrib attr[] = {
- { VAConfigAttribEncPackedHeaders },
- };
-
- vas = vaGetConfigAttributes(ctx->hwctx->display,
- ctx->va_profile, ctx->va_entrypoint,
- attr, FF_ARRAY_ELEMS(attr));
- if (vas != VA_STATUS_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "Failed to fetch config "
- "attributes: %d (%s).\n", vas, vaErrorStr(vas));
- return AVERROR(EINVAL);
- }
-
- for (i = 0; i < FF_ARRAY_ELEMS(attr); i++) {
- if (attr[i].value == VA_ATTRIB_NOT_SUPPORTED) {
- // Unfortunately we have to treat this as "don't know" and hope
- // for the best, because the Intel MJPEG encoder returns this
- // for all the interesting attributes.
- av_log(avctx, AV_LOG_DEBUG, "Attribute (%d) is not supported.\n",
- attr[i].type);
- continue;
- }
- switch (attr[i].type) {
- 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;
- av_assert0(0 && "Unexpected config attribute.");
- }
- }
-
- return 0;
-}
-
static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
@@ -1494,6 +1440,65 @@ static av_cold int
vaapi_encode_init_gop_structure(AVCodecContext *avctx)
return 0;
}
+static av_cold int vaapi_encode_init_packed_headers(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAStatus vas;
+ VAConfigAttrib attr = { VAConfigAttribEncPackedHeaders };
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile,
+ ctx->va_entrypoint,
+ &attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query packed headers "
+ "attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
+
+ if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ if (ctx->packed_headers) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support any "
+ "packed headers (wanted %#x).\n", ctx->packed_headers);
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not support any "
+ "packed headers (none wanted).\n");
+ }
+ ctx->va_packed_headers = 0;
+ } else {
+ if (ctx->packed_headers & ~attr.value) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support some "
+ "wanted packed headers (wanted %#x, found %#x).\n",
+ ctx->packed_headers, attr.value);
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "All wanted packed headers "
+ "available (wanted %#x, found %#x).\n",
+ ctx->packed_headers, attr.value);
+ }
+ ctx->va_packed_headers = ctx->packed_headers & attr.value;
+ }
+
+ if (ctx->va_packed_headers) {
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribEncPackedHeaders,
+ .value = ctx->va_packed_headers,
+ };
+ }
+
+ if ( (ctx->packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE) &&
+ !(ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE) &&
+ (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support packed "
+ "sequence headers, but a global header is requested.\n");
+ av_log(avctx, AV_LOG_WARNING, "No global header will be written: "
+ "this may result in a stream which is not usable for some "
+ "purposes (e.g. not muxable to some containers).\n");
+ }
+
+ return 0;
+}
+
static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx)
{
#if VA_CHECK_VERSION(0, 36, 0)
@@ -1724,7 +1729,7 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;
- err = vaapi_encode_config_attributes(avctx);
+ err = vaapi_encode_init_packed_headers(avctx);
if (err < 0)
goto fail;
@@ -1813,7 +1818,8 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
ctx->issue_mode = ISSUE_MODE_MAXIMISE_THROUGHPUT;
if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE &&
- ctx->codec->write_sequence_header) {
+ ctx->codec->write_sequence_header &&
+ avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
char data[MAX_PARAM_BUFFER_SIZE];
size_t bit_len = 8 * sizeof(data);
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index bbec721bca..e6acd933d6 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -116,9 +116,8 @@ typedef struct VAAPIEncodeContext {
// Use low power encoding mode.
int low_power;
- // Supported packed headers (initially the desired set, modified
- // later to what is actually supported).
- unsigned int va_packed_headers;
+ // Desired packed headers.
+ unsigned int packed_headers;
How about adding a prefix of desired_ to this field? I got a little bit confused
when reviewing this patch :-)
Post by Mark Thompson
// The required size of surfaces. This is probably the input
// size (AVCodecContext.width|height) aligned up to whatever
@@ -140,6 +139,8 @@ typedef struct VAAPIEncodeContext {
unsigned int va_rc_mode;
// Bitrate for codec-specific encoder parameters.
unsigned int va_bit_rate;
+ // Packed headers which will actually be sent.
+ unsigned int va_packed_headers;
// Configuration attributes to use when creating va_config.
VAConfigAttrib config_attributes[MAX_CONFIG_ATTRIBUTES];
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 717be024ca..e2f4f4f2f5 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -930,7 +930,7 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
return AVERROR_PATCHWELCOME;
}
- ctx->va_packed_headers =
+ ctx->packed_headers =
VA_ENC_PACKED_HEADER_SEQUENCE | // SPS and PPS.
VA_ENC_PACKED_HEADER_SLICE | // Slice headers.
VA_ENC_PACKED_HEADER_MISC; // SEI.
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index b2d6b8d24d..04dfef27c8 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -1064,7 +1064,7 @@ static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx)
if (avctx->level == FF_LEVEL_UNKNOWN)
avctx->level = priv->level;
- ctx->va_packed_headers =
+ ctx->packed_headers =
VA_ENC_PACKED_HEADER_SEQUENCE | // VPS, SPS and PPS.
VA_ENC_PACKED_HEADER_SLICE | // Slice headers.
VA_ENC_PACKED_HEADER_MISC; // SEI
diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index 4982cd166f..f76645425a 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -389,7 +389,7 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx)
ctx->codec = &vaapi_encode_type_mjpeg;
// The JPEG image header - see note above.
- ctx->va_packed_headers =
+ ctx->packed_headers =
VA_ENC_PACKED_HEADER_RAW_DATA;
ctx->surface_width = FFALIGN(avctx->width, 8);
diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index b6edca9158..1afd753fc1 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -615,8 +615,8 @@ static av_cold int vaapi_encode_mpeg2_init(AVCodecContext *avctx)
return AVERROR(EINVAL);
}
- ctx->va_packed_headers = VA_ENC_PACKED_HEADER_SEQUENCE |
- VA_ENC_PACKED_HEADER_PICTURE;
+ ctx->packed_headers = VA_ENC_PACKED_HEADER_SEQUENCE |
+ VA_ENC_PACKED_HEADER_PICTURE;
ctx->surface_width = FFALIGN(avctx->width, 16);
ctx->surface_height = FFALIGN(avctx->height, 16);
diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c
index 4512609a19..51ba601c5e 100644
--- a/libavcodec/vaapi_encode_vp8.c
+++ b/libavcodec/vaapi_encode_vp8.c
@@ -200,8 +200,10 @@ static av_cold int vaapi_encode_vp8_init(AVCodecContext *avctx)
ctx->codec = &vaapi_encode_type_vp8;
- // Packed headers are not currently supported.
- ctx->va_packed_headers = 0;
+ // No packed headers are currently desired. VP8 has no metadata
+ // which would be useful to write, and no existing driver supports
+ // adding them anyway.
+ ctx->packed_headers = 0;
ctx->surface_width = FFALIGN(avctx->width, 16);
ctx->surface_height = FFALIGN(avctx->height, 16);
diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index d4069ec850..537a92c48c 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -228,8 +228,10 @@ static av_cold int vaapi_encode_vp9_init(AVCodecContext *avctx)
ctx->codec = &vaapi_encode_type_vp9;
- // Packed headers are not currently supported.
- ctx->va_packed_headers = 0;
+ // No packed headers are currently desired. They could be written,
+ // but there isn't any reason to do so - the one usable driver (i965)
+ // can write its own headers and there is no metadata to include.
+ ctx->packed_headers = 0;
// Surfaces must be aligned to superblock boundaries.
ctx->surface_width = FFALIGN(avctx->width, 64);
Mark Thompson
2018-06-17 13:36:53 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
Add a larger warning more clearly explaining the consequences of missing
packed header support in the driver. Also only write the extradata if the
user actually requests it via the GLOBAL_HEADER flag.
---
libavcodec/vaapi_encode.c | 118 +++++++++++++++++++++----------------
---
libavcodec/vaapi_encode.h | 7 ++-
libavcodec/vaapi_encode_h264.c | 2 +-
libavcodec/vaapi_encode_h265.c | 2 +-
libavcodec/vaapi_encode_mjpeg.c | 2 +-
libavcodec/vaapi_encode_mpeg2.c | 4 +-
libavcodec/vaapi_encode_vp8.c | 6 +-
libavcodec/vaapi_encode_vp9.c | 6 +-
8 files changed, 79 insertions(+), 68 deletions(-)
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index af9224c98f..14d1846ea3 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
return err;
}
-static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
-{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAStatus vas;
- int i;
-
- VAConfigAttrib attr[] = {
- { VAConfigAttribEncPackedHeaders },
- };
-
- vas = vaGetConfigAttributes(ctx->hwctx->display,
- ctx->va_profile, ctx->va_entrypoint,
- attr, FF_ARRAY_ELEMS(attr));
- if (vas != VA_STATUS_SUCCESS) {
- av_log(avctx, AV_LOG_ERROR, "Failed to fetch config "
- "attributes: %d (%s).\n", vas, vaErrorStr(vas));
- return AVERROR(EINVAL);
- }
-
- for (i = 0; i < FF_ARRAY_ELEMS(attr); i++) {
- if (attr[i].value == VA_ATTRIB_NOT_SUPPORTED) {
- // Unfortunately we have to treat this as "don't know" and hope
- // for the best, because the Intel MJPEG encoder returns this
- // for all the interesting attributes.
- av_log(avctx, AV_LOG_DEBUG, "Attribute (%d) is not supported.\n",
- attr[i].type);
- continue;
- }
- switch (attr[i].type) {
- 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;
- av_assert0(0 && "Unexpected config attribute.");
- }
- }
-
- return 0;
-}
-
static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
@@ -1494,6 +1440,65 @@ static av_cold int
vaapi_encode_init_gop_structure(AVCodecContext *avctx)
return 0;
}
+static av_cold int vaapi_encode_init_packed_headers(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAStatus vas;
+ VAConfigAttrib attr = { VAConfigAttribEncPackedHeaders };
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile,
+ ctx->va_entrypoint,
+ &attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query packed headers "
+ "attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
+
+ if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ if (ctx->packed_headers) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support any "
+ "packed headers (wanted %#x).\n", ctx->packed_headers);
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not support any "
+ "packed headers (none wanted).\n");
+ }
+ ctx->va_packed_headers = 0;
+ } else {
+ if (ctx->packed_headers & ~attr.value) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support some "
+ "wanted packed headers (wanted %#x, found %#x).\n",
+ ctx->packed_headers, attr.value);
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "All wanted packed headers "
+ "available (wanted %#x, found %#x).\n",
+ ctx->packed_headers, attr.value);
+ }
+ ctx->va_packed_headers = ctx->packed_headers & attr.value;
+ }
+
+ if (ctx->va_packed_headers) {
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribEncPackedHeaders,
+ .value = ctx->va_packed_headers,
+ };
+ }
+
+ if ( (ctx->packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE) &&
+ !(ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE) &&
+ (avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support packed "
+ "sequence headers, but a global header is requested.\n");
+ av_log(avctx, AV_LOG_WARNING, "No global header will be written: "
+ "this may result in a stream which is not usable for some "
+ "purposes (e.g. not muxable to some containers).\n");
+ }
+
+ return 0;
+}
+
static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx)
{
#if VA_CHECK_VERSION(0, 36, 0)
@@ -1724,7 +1729,7 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;
- err = vaapi_encode_config_attributes(avctx);
+ err = vaapi_encode_init_packed_headers(avctx);
if (err < 0)
goto fail;
@@ -1813,7 +1818,8 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
ctx->issue_mode = ISSUE_MODE_MAXIMISE_THROUGHPUT;
if (ctx->va_packed_headers & VA_ENC_PACKED_HEADER_SEQUENCE &&
- ctx->codec->write_sequence_header) {
+ ctx->codec->write_sequence_header &&
+ avctx->flags & AV_CODEC_FLAG_GLOBAL_HEADER) {
char data[MAX_PARAM_BUFFER_SIZE];
size_t bit_len = 8 * sizeof(data);
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index bbec721bca..e6acd933d6 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -116,9 +116,8 @@ typedef struct VAAPIEncodeContext {
// Use low power encoding mode.
int low_power;
- // Supported packed headers (initially the desired set, modified
- // later to what is actually supported).
- unsigned int va_packed_headers;
+ // Desired packed headers.
+ unsigned int packed_headers;
How about adding a prefix of desired_ to this field? I got a little bit confused
when reviewing this patch :-)
Yeah, that would be clearer. Changed.
Post by Xiang, Haihao
Post by Mark Thompson
// The required size of surfaces. This is probably the input
// size (AVCodecContext.width|height) aligned up to whatever
@@ -140,6 +139,8 @@ typedef struct VAAPIEncodeContext {
unsigned int va_rc_mode;
// Bitrate for codec-specific encoder parameters.
unsigned int va_bit_rate;
+ // Packed headers which will actually be sent.
+ unsigned int va_packed_headers;
// Configuration attributes to use when creating va_config.
VAConfigAttrib config_attributes[MAX_CONFIG_ATTRIBUTES];
Thanks,

- Mark
Mark Thompson
2018-06-07 23:43:19 UTC
Permalink
---
configure | 2 +
libavcodec/Makefile | 1 +
libavcodec/cbs.c | 6 +
libavcodec/cbs_internal.h | 1 +
libavcodec/cbs_jpeg.c | 513 ++++++++++++++++++++++++++++++++++
libavcodec/cbs_jpeg.h | 128 +++++++++
libavcodec/cbs_jpeg_syntax_template.c | 191 +++++++++++++
7 files changed, 842 insertions(+)
create mode 100644 libavcodec/cbs_jpeg.c
create mode 100644 libavcodec/cbs_jpeg.h
create mode 100644 libavcodec/cbs_jpeg_syntax_template.c

diff --git a/configure b/configure
index 790f55be14..d908283954 100755
--- a/configure
+++ b/configure
@@ -2244,6 +2244,7 @@ CONFIG_EXTRA="
cbs
cbs_h264
cbs_h265
+ cbs_jpeg
cbs_mpeg2
cbs_vp9
dirac_parse
@@ -2507,6 +2508,7 @@ threads_if_any="$THREADS_LIST"
# subsystems
cbs_h264_select="cbs golomb"
cbs_h265_select="cbs golomb"
+cbs_jpeg_select="cbs"
cbs_mpeg2_select="cbs"
cbs_vp9_select="cbs"
dct_select="rdft"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 3ab071a039..2a1e0de110 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -64,6 +64,7 @@ OBJS-$(CONFIG_CABAC) += cabac.o
OBJS-$(CONFIG_CBS) += cbs.o
OBJS-$(CONFIG_CBS_H264) += cbs_h2645.o h2645_parse.o
OBJS-$(CONFIG_CBS_H265) += cbs_h2645.o h2645_parse.o
+OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o
OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o
OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o
OBJS-$(CONFIG_CRYSTALHD) += crystalhd.o
diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c
index be6c043b58..bb3ce95971 100644
--- a/libavcodec/cbs.c
+++ b/libavcodec/cbs.c
@@ -35,6 +35,9 @@ static const CodedBitstreamType *cbs_type_table[] = {
#if CONFIG_CBS_H265
&ff_cbs_type_h265,
#endif
+#if CONFIG_CBS_JPEG
+ &ff_cbs_type_jpeg,
+#endif
#if CONFIG_CBS_MPEG2
&ff_cbs_type_mpeg2,
#endif
@@ -50,6 +53,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = {
#if CONFIG_CBS_H265
AV_CODEC_ID_H265,
#endif
+#if CONFIG_CBS_JPEG
+ AV_CODEC_ID_MJPEG,
+#endif
#if CONFIG_CBS_MPEG2
AV_CODEC_ID_MPEG2VIDEO,
#endif
diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h
index 172b8a2515..e0e912e28e 100644
--- a/libavcodec/cbs_internal.h
+++ b/libavcodec/cbs_internal.h
@@ -88,6 +88,7 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc,

extern const CodedBitstreamType ff_cbs_type_h264;
extern const CodedBitstreamType ff_cbs_type_h265;
+extern const CodedBitstreamType ff_cbs_type_jpeg;
extern const CodedBitstreamType ff_cbs_type_mpeg2;
extern const CodedBitstreamType ff_cbs_type_vp9;

diff --git a/libavcodec/cbs_jpeg.c b/libavcodec/cbs_jpeg.c
new file mode 100644
index 0000000000..365db73394
--- /dev/null
+++ b/libavcodec/cbs_jpeg.c
@@ -0,0 +1,513 @@
+/*
+ * 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 "cbs.h"
+#include "cbs_internal.h"
+#include "cbs_jpeg.h"
+
+
+#define HEADER(name) do { \
+ ff_cbs_trace_header(ctx, name); \
+ } while (0)
+
+#define CHECK(call) do { \
+ err = (call); \
+ if (err < 0) \
+ return err; \
+ } while (0)
+
+#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL)
+
+#define u(width, name, range_min, range_max) \
+ xu(width, name, range_min, range_max, 0)
+#define us(width, name, sub, range_min, range_max) \
+ xu(width, name, range_min, range_max, 1, sub)
+
+
+#define READ
+#define READWRITE read
+#define RWContext GetBitContext
+#define FUNC(name) cbs_jpeg_read_ ## name
+
+#define xu(width, name, range_min, range_max, subs, ...) do { \
+ uint32_t value = range_min; \
+ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \
+ SUBSCRIPTS(subs, __VA_ARGS__), \
+ &value, range_min, range_max)); \
+ current->name = value; \
+ } while (0)
+
+#include "cbs_jpeg_syntax_template.c"
+
+#undef READ
+#undef READWRITE
+#undef RWContext
+#undef FUNC
+#undef xu
+
+#define WRITE
+#define READWRITE write
+#define RWContext PutBitContext
+#define FUNC(name) cbs_jpeg_write_ ## name
+
+#define xu(width, name, range_min, range_max, subs, ...) do { \
+ uint32_t value = current->name; \
+ CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \
+ SUBSCRIPTS(subs, __VA_ARGS__), \
+ value, range_min, range_max)); \
+ } while (0)
+
+
+#include "cbs_jpeg_syntax_template.c"
+
+#undef READ
+#undef READWRITE
+#undef RWContext
+#undef FUNC
+#undef xu
+
+
+static void cbs_jpeg_free_application_data(void *unit, uint8_t *content)
+{
+ JPEGRawApplicationData *ad = (JPEGRawApplicationData*)content;
+ av_buffer_unref(&ad->Ap_ref);
+ av_freep(&content);
+}
+
+static void cbs_jpeg_free_scan(void *unit, uint8_t *content)
+{
+ JPEGRawScan *scan = (JPEGRawScan*)content;
+ av_buffer_unref(&scan->data_ref);
+ av_freep(&content);
+}
+
+static int cbs_jpeg_split_fragment(CodedBitstreamContext *ctx,
+ CodedBitstreamFragment *frag,
+ int header)
+{
+ AVBufferRef *data_ref;
+ uint8_t *data;
+ size_t data_size;
+ int unit, start, end, marker, next_start, next_marker;
+ int err, i, j, length;
+
+ if (frag->data_size < 4) {
+ // Definitely too short to be meaningful.
+ return AVERROR_INVALIDDATA;
+ }
+
+ for (i = 0; i + 1 < frag->data_size && frag->data[i] != 0xff; i++);
+ if (i > 0) {
+ av_log(ctx->log_ctx, AV_LOG_WARNING, "Discarding %d bytes at "
+ "beginning of image.\n", i);
+ }
+ for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++);
+ if (i + 1 >= frag->data_size && frag->data[i]) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
+ "no SOI marker found.\n");
+ return AVERROR_INVALIDDATA;
+ }
+ marker = frag->data[i];
+ if (marker != JPEG_MARKER_SOI) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: first "
+ "marker is %02x, should be SOI.\n", marker);
+ return AVERROR_INVALIDDATA;
+ }
+ for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++);
+ if (i + 1 >= frag->data_size) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
+ "no image content found.\n");
+ return AVERROR_INVALIDDATA;
+ }
+ marker = frag->data[i];
+ start = i + 1;
+
+ for (unit = 0;; unit++) {
+ if (marker == JPEG_MARKER_EOI) {
+ break;
+ } else if (marker == JPEG_MARKER_SOS) {
+ for (i = start; i + 1 < frag->data_size; i++) {
+ if (frag->data[i] != 0xff)
+ continue;
+ end = i;
+ for (++i; i + 1 < frag->data_size &&
+ frag->data[i] == 0xff; i++);
+ if (i + 1 >= frag->data_size) {
+ next_marker = -1;
+ } else {
+ if (frag->data[i] == 0x00)
+ continue;
+ next_marker = frag->data[i];
+ next_start = i + 1;
+ }
+ break;
+ }
+ } else {
+ i = start;
+ if (i + 2 > frag->data_size) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
+ "truncated at %02x marker.\n", marker);
+ return AVERROR_INVALIDDATA;
+ }
+ length = AV_RB16(frag->data + i);
+ if (i + length > frag->data_size) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
+ "truncated at %02x marker segment.\n", marker);
+ return AVERROR_INVALIDDATA;
+ }
+ end = start + length;
+
+ i = end;
+ if (frag->data[i] != 0xff) {
+ next_marker = -1;
+ } else {
+ for (++i; i + 1 < frag->data_size &&
+ frag->data[i] == 0xff; i++);
+ if (i + 1 >= frag->data_size) {
+ next_marker = -1;
+ } else {
+ next_marker = frag->data[i];
+ next_start = i + 1;
+ }
+ }
+ }
+
+ if (marker == JPEG_MARKER_SOS) {
+ length = AV_RB16(frag->data + start);
+
+ data_ref = NULL;
+ data = av_malloc(end - start +
+ AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!data)
+ return AVERROR(ENOMEM);
+
+ memcpy(data, frag->data + start, length);
+ for (i = start + length, j = length; i < end; i++, j++) {
+ if (frag->data[i] == 0xff) {
+ while (frag->data[i] == 0xff)
+ ++i;
+ data[j] = 0xff;
+ } else {
+ data[j] = frag->data[i];
+ }
+ }
+ data_size = j;
+
+ memset(data + data_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+
+ } else {
+ data = frag->data + start;
+ data_size = end - start;
+ data_ref = frag->data_ref;
+ }
+
+ err = ff_cbs_insert_unit_data(ctx, frag, unit, marker,
+ data, data_size, data_ref);
+ if (err < 0) {
+ if (!data_ref)
+ av_freep(&data);
+ return err;
+ }
+
+ if (next_marker == -1)
+ break;
+ marker = next_marker;
+ start = next_start;
+ }
+
+ return 0;
+}
+
+static int cbs_jpeg_read_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ GetBitContext gbc;
+ int err;
+
+ err = init_get_bits(&gbc, unit->data, 8 * unit->data_size);
+ if (err < 0)
+ return err;
+
+ if (unit->type >= JPEG_MARKER_SOF0 &&
+ unit->type <= JPEG_MARKER_SOF3) {
+ err = ff_cbs_alloc_unit_content(ctx, unit,
+ sizeof(JPEGRawFrameHeader),
+ NULL);
+ if (err < 0)
+ return err;
+
+ err = cbs_jpeg_read_frame_header(ctx, &gbc, unit->content);
+ if (err < 0)
+ return err;
+
+ } else if (unit->type >= JPEG_MARKER_APPN &&
+ unit->type <= JPEG_MARKER_APPN + 15) {
+ err = ff_cbs_alloc_unit_content(ctx, unit,
+ sizeof(JPEGRawApplicationData),
+ &cbs_jpeg_free_application_data);
+ if (err < 0)
+ return err;
+
+ err = cbs_jpeg_read_application_data(ctx, &gbc, unit->content);
+ if (err < 0)
+ return err;
+
+ } else if (unit->type == JPEG_MARKER_SOS) {
+ JPEGRawScan *scan;
+ int pos;
+
+ err = ff_cbs_alloc_unit_content(ctx, unit,
+ sizeof(JPEGRawScan),
+ &cbs_jpeg_free_scan);
+ if (err < 0)
+ return err;
+ scan = unit->content;
+
+ err = cbs_jpeg_read_scan_header(ctx, &gbc, &scan->header);
+ if (err < 0)
+ return err;
+
+ pos = get_bits_count(&gbc);
+ av_assert0(pos % 8 == 0);
+ if (pos > 0) {
+ scan->data_size = unit->data_size - pos / 8;
+ scan->data_ref = av_buffer_ref(unit->data_ref);
+ if (!scan->data_ref)
+ return AVERROR(ENOMEM);
+ scan->data = unit->data + pos / 8;
+ }
+
+ } else {
+ switch (unit->type) {
+#define SEGMENT(marker, type, func) \
+ case JPEG_MARKER_ ## marker: \
+ { \
+ err = ff_cbs_alloc_unit_content(ctx, unit, \
+ sizeof(type), NULL); \
+ if (err < 0) \
+ return err; \
+ err = cbs_jpeg_read_ ## func(ctx, &gbc, unit->content); \
+ if (err < 0) \
+ return err; \
+ } \
+ break
+ SEGMENT(DQT, JPEGRawQuantisationTableSpecification, dqt);
+ SEGMENT(DHT, JPEGRawHuffmanTableSpecification, dht);
+ SEGMENT(COM, JPEGRawComment, comment);
+#undef SEGMENT
+ default:
+ return AVERROR(ENOSYS);
+ }
+ }
+
+ return 0;
+}
+
+static int cbs_jpeg_write_scan(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit,
+ PutBitContext *pbc)
+{
+ JPEGRawScan *scan = unit->content;
+ int i, err;
+
+ err = cbs_jpeg_write_scan_header(ctx, pbc, &scan->header);
+ if (err < 0)
+ return err;
+
+ if (scan->data) {
+ if (scan->data_size * 8 > put_bits_left(pbc))
+ return AVERROR(ENOSPC);
+
+ for (i = 0; i < scan->data_size; i++)
+ put_bits(pbc, 8, scan->data[i]);
+ }
+
+ return 0;
+}
+
+static int cbs_jpeg_write_segment(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit,
+ PutBitContext *pbc)
+{
+ int err;
+
+ if (unit->type >= JPEG_MARKER_SOF0 &&
+ unit->type <= JPEG_MARKER_SOF3) {
+ err = cbs_jpeg_write_frame_header(ctx, pbc, unit->content);
+ } else if (unit->type >= JPEG_MARKER_APPN &&
+ unit->type <= JPEG_MARKER_APPN + 15) {
+ err = cbs_jpeg_write_application_data(ctx, pbc, unit->content);
+ } else {
+ switch (unit->type) {
+#define SEGMENT(marker, func) \
+ case JPEG_MARKER_ ## marker: \
+ err = cbs_jpeg_write_ ## func(ctx, pbc, unit->content); \
+ break;
+ SEGMENT(DQT, dqt);
+ SEGMENT(DHT, dht);
+ SEGMENT(COM, comment);
+ default:
+ return AVERROR_PATCHWELCOME;
+ }
+ }
+
+ return err;
+}
+
+static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ CodedBitstreamJPEGContext *priv = ctx->priv_data;
+ PutBitContext pbc;
+ int err;
+
+ if (!priv->write_buffer) {
+ // Initial write buffer size is 1MB.
+ priv->write_buffer_size = 1024 * 1024;
+
+ reallocate_and_try_again:
+ err = av_reallocp(&priv->write_buffer, priv->write_buffer_size);
+ if (err < 0) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a "
+ "sufficiently large write buffer (last attempt "
+ "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size);
+ return err;
+ }
+ }
+
+ init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size);
+
+ if (unit->type == JPEG_MARKER_SOS)
+ err = cbs_jpeg_write_scan(ctx, unit, &pbc);
+ else
+ err = cbs_jpeg_write_segment(ctx, unit, &pbc);
+
+ if (err == AVERROR(ENOSPC)) {
+ // Overflow.
+ priv->write_buffer_size *= 2;
+ goto reallocate_and_try_again;
+ }
+ if (err < 0) {
+ // Write failed for some other reason.
+ return err;
+ }
+
+ if (put_bits_count(&pbc) % 8)
+ unit->data_bit_padding = 8 - put_bits_count(&pbc) % 8;
+ else
+ unit->data_bit_padding = 0;
+
+ unit->data_size = (put_bits_count(&pbc) + 7) / 8;
+ flush_put_bits(&pbc);
+
+ err = ff_cbs_alloc_unit_data(ctx, unit, unit->data_size);
+ if (err < 0)
+ return err;
+
+ memcpy(unit->data, priv->write_buffer, unit->data_size);
+
+ return 0;
+}
+
+static int cbs_jpeg_assemble_fragment(CodedBitstreamContext *ctx,
+ CodedBitstreamFragment *frag)
+{
+ const CodedBitstreamUnit *unit;
+ uint8_t *data;
+ size_t size, dp, sp;
+ int i;
+
+ size = 4; // SOI + EOI.
+ for (i = 0; i < frag->nb_units; i++) {
+ unit = &frag->units[i];
+ size += 2 + unit->data_size;
+ if (unit->type == JPEG_MARKER_SOS) {
+ for (sp = 0; sp < unit->data_size; sp++) {
+ if (unit->data[sp] == 0xff)
+ ++size;
+ }
+ }
+ }
+
+ frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!frag->data_ref)
+ return AVERROR(ENOMEM);
+ data = frag->data_ref->data;
+
+ dp = 0;
+
+ data[dp++] = 0xff;
+ data[dp++] = JPEG_MARKER_SOI;
+
+ for (i = 0; i < frag->nb_units; i++) {
+ unit = &frag->units[i];
+
+ data[dp++] = 0xff;
+ data[dp++] = unit->type;
+
+ if (unit->type != JPEG_MARKER_SOS) {
+ memcpy(data + dp, unit->data, unit->data_size);
+ dp += unit->data_size;
+ } else {
+ sp = AV_RB16(unit->data);
+ av_assert0(sp <= unit->data_size);
+ memcpy(data + dp, unit->data, sp);
+ dp += sp;
+
+ for (; sp < unit->data_size; sp++) {
+ if (unit->data[sp] == 0xff) {
+ data[dp++] = 0xff;
+ data[dp++] = 0x00;
+ } else {
+ data[dp++] = unit->data[sp];
+ }
+ }
+ }
+ }
+
+ data[dp++] = 0xff;
+ data[dp++] = JPEG_MARKER_EOI;
+
+ av_assert0(dp == size);
+
+ memset(data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+ frag->data = data;
+ frag->data_size = size;
+
+ return 0;
+}
+
+static void cbs_jpeg_close(CodedBitstreamContext *ctx)
+{
+ CodedBitstreamJPEGContext *priv = ctx->priv_data;
+
+ av_freep(&priv->write_buffer);
+}
+
+const CodedBitstreamType ff_cbs_type_jpeg = {
+ .codec_id = AV_CODEC_ID_MJPEG,
+
+ .priv_data_size = sizeof(CodedBitstreamJPEGContext),
+
+ .split_fragment = &cbs_jpeg_split_fragment,
+ .read_unit = &cbs_jpeg_read_unit,
+ .write_unit = &cbs_jpeg_write_unit,
+ .assemble_fragment = &cbs_jpeg_assemble_fragment,
+
+ .close = &cbs_jpeg_close,
+};
diff --git a/libavcodec/cbs_jpeg.h b/libavcodec/cbs_jpeg.h
new file mode 100644
index 0000000000..f427c4c616
--- /dev/null
+++ b/libavcodec/cbs_jpeg.h
@@ -0,0 +1,128 @@
+/*
+ * 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
+ */
+
+#ifndef AVCODEC_CBS_JPEG_H
+#define AVCODEC_CBS_JPEG_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+
+enum {
+ JPEG_MARKER_SOF0 = 0xc0,
+ JPEG_MARKER_SOF1 = 0xc1,
+ JPEG_MARKER_SOF2 = 0xc2,
+ JPEG_MARKER_SOF3 = 0xc3,
+
+ JPEG_MARKER_DHT = 0xc4,
+ JPEG_MARKER_SOI = 0xd8,
+ JPEG_MARKER_EOI = 0xd9,
+ JPEG_MARKER_SOS = 0xda,
+ JPEG_MARKER_DQT = 0xdb,
+
+ JPEG_MARKER_APPN = 0xe0,
+ JPEG_MARKER_JPGN = 0xf0,
+ JPEG_MARKER_COM = 0xfe,
+};
+
+enum {
+ JPEG_MAX_COMPONENTS = 255,
+
+ JPEG_MAX_HEIGHT = 65535,
+ JPEG_MAX_WIDTH = 65535,
+};
+
+
+typedef struct JPEGRawFrameHeader {
+ uint16_t Lf;
+ uint8_t P;
+ uint16_t Y;
+ uint16_t X;
+ uint16_t Nf;
+
+ uint8_t C [JPEG_MAX_COMPONENTS];
+ uint8_t H [JPEG_MAX_COMPONENTS];
+ uint8_t V [JPEG_MAX_COMPONENTS];
+ uint8_t Tq[JPEG_MAX_COMPONENTS];
+} JPEGRawFrameHeader;
+
+typedef struct JPEGRawScanHeader {
+ uint16_t Ls;
+ uint8_t Ns;
+
+ uint8_t Cs[JPEG_MAX_COMPONENTS];
+ uint8_t Td[JPEG_MAX_COMPONENTS];
+ uint8_t Ta[JPEG_MAX_COMPONENTS];
+
+ uint8_t Ss;
+ uint8_t Se;
+ uint8_t Ah;
+ uint8_t Al;
+} JPEGRawScanHeader;
+
+typedef struct JPEGRawScan {
+ JPEGRawScanHeader header;
+ uint8_t *data;
+ size_t data_size;
+ AVBufferRef *data_ref;
+} JPEGRawScan;
+
+typedef struct JPEGRawQuantisationTable {
+ uint8_t Pq;
+ uint8_t Tq;
+ uint16_t Q[64];
+} JPEGRawQuantisationTable;
+
+typedef struct JPEGRawQuantisationTableSpecification {
+ uint16_t Lq;
+ JPEGRawQuantisationTable table[4];
+} JPEGRawQuantisationTableSpecification;
+
+typedef struct JPEGRawHuffmanTable {
+ uint8_t Tc;
+ uint8_t Th;
+ uint8_t L[16];
+ uint8_t V[224];
+} JPEGRawHuffmanTable;
+
+typedef struct JPEGRawHuffmanTableSpecification {
+ uint16_t Lh;
+ JPEGRawHuffmanTable table[8];
+} JPEGRawHuffmanTableSpecification;
+
+typedef struct JPEGRawApplicationData {
+ uint16_t Lp;
+ uint8_t *Ap;
+ AVBufferRef *Ap_ref;
+} JPEGRawApplicationData;
+
+typedef struct JPEGRawComment {
+ uint16_t Lc;
+ uint8_t *Cm;
+ AVBufferRef *Cm_ref;
+} JPEGRawComment;
+
+
+typedef struct CodedBitstreamJPEGContext {
+ // Write buffer.
+ uint8_t *write_buffer;
+ size_t write_buffer_size;
+} CodedBitstreamJPEGContext;
+
+
+#endif /* AVCODEC_CBS_JPEG_H */
diff --git a/libavcodec/cbs_jpeg_syntax_template.c b/libavcodec/cbs_jpeg_syntax_template.c
new file mode 100644
index 0000000000..d3cd9ff62e
--- /dev/null
+++ b/libavcodec/cbs_jpeg_syntax_template.c
@@ -0,0 +1,191 @@
+/*
+ * 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
+ */
+
+static int FUNC(frame_header)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawFrameHeader *current)
+{
+ int err, i;
+
+ HEADER("Frame Header");
+
+ u(16, Lf, 8, 8 + 3 * JPEG_MAX_COMPONENTS);
+
+ u(8, P, 2, 16);
+ u(16, Y, 0, JPEG_MAX_HEIGHT);
+ u(16, X, 1, JPEG_MAX_WIDTH);
+ u(8, Nf, 1, JPEG_MAX_COMPONENTS);
+
+ for (i = 0; i < current->Nf; i++) {
+ us(8, C[i], i, 0, JPEG_MAX_COMPONENTS);
+ us(4, H[i], i, 1, 4);
+ us(4, V[i], i, 1, 4);
+ us(8, Tq[i], i, 0, 3);
+ }
+
+ return 0;
+}
+
+static int FUNC(quantisation_table)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawQuantisationTable *current)
+{
+ int err, i;
+
+ u(4, Pq, 0, 1);
+ u(4, Tq, 0, 3);
+
+ if (current->Pq) {
+ for (i = 0; i < 64; i++)
+ us(16, Q[i], i, 1, 255);
+ } else {
+ for (i = 0; i < 64; i++)
+ us(8, Q[i], i, 1, 255);
+ }
+
+ return 0;
+}
+
+static int FUNC(dqt)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawQuantisationTableSpecification *current)
+{
+ int err, i, n;
+
+ HEADER("Quantisation Tables");
+
+ u(16, Lq, 2, 2 + 4 * 65);
+ n = current->Lq / 65;
+
+ for (i = 0; i < n; i++)
+ CHECK(FUNC(quantisation_table)(ctx, rw, &current->table[i]));
+
+ return 0;
+}
+
+static int FUNC(huffman_table)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawHuffmanTable *current)
+{
+ int err, i, j, ij;
+
+ u(4, Tc, 0, 1);
+ u(4, Th, 0, 3);
+
+ for (i = 0; i < 16; i++)
+ us(8, L[i], i, 0, 224);
+
+ ij = 0;
+ for (i = 0; i < 16; i++) {
+ for (j = 0; j < current->L[i]; j++) {
+ us(8, V[ij], ij, 0, 255);
+ ++ij;
+ }
+ }
+
+ return 0;
+}
+
+static int FUNC(dht)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawHuffmanTableSpecification *current)
+{
+ int err, i, j, n;
+
+ HEADER("Huffman Tables");
+
+ u(16, Lh, 2, 2 + 8 * (1 + 16 + 256));
+
+ n = 2;
+ for (i = 0; n < current->Lh; i++) {
+ CHECK(FUNC(huffman_table)(ctx, rw, &current->table[i]));
+
+ ++n;
+ for (j = 0; j < 16; j++)
+ n += 1 + current->table[i].L[j];
+ }
+
+ return 0;
+}
+
+static int FUNC(scan_header)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawScanHeader *current)
+{
+ int err, j;
+
+ HEADER("Scan");
+
+ u(16, Ls, 6, 6 + 2 * JPEG_MAX_COMPONENTS);
+
+ u(8, Ns, 1, 4);
+ for (j = 0; j < current->Ns; j++) {
+ us(8, Cs[j], j, 0, JPEG_MAX_COMPONENTS);
+ us(4, Td[j], j, 0, 3);
+ us(4, Ta[j], j, 0, 3);
+ }
+
+ u(8, Ss, 0, 63);
+ u(8, Se, 0, 63);
+ u(4, Ah, 0, 13);
+ u(4, Al, 0, 15);
+
+ return 0;
+}
+
+static int FUNC(application_data)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawApplicationData *current)
+{
+ int err, i;
+
+ HEADER("Application Data");
+
+ u(16, Lp, 2, 65535);
+
+ if (current->Lp > 2) {
+#ifdef READ
+ current->Ap_ref = av_buffer_alloc(current->Lp - 2);
+ if (!current->Ap_ref)
+ return AVERROR(ENOMEM);
+ current->Ap = current->Ap_ref->data;
+#endif
+
+ for (i = 0; i < current->Lp - 2; i++)
+ us(8, Ap[i], i, 0, 255);
+ }
+
+ return 0;
+}
+
+static int FUNC(comment)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawComment *current)
+{
+ int err, i;
+
+ HEADER("Comment");
+
+ u(16, Lc, 2, 65535);
+
+ if (current->Lc > 2) {
+#ifdef READ
+ current->Cm_ref = av_buffer_alloc(current->Lc - 2);
+ if (!current->Cm_ref)
+ return AVERROR(ENOMEM);
+ current->Cm = current->Cm_ref->data;
+#endif
+
+ for (i = 0; i < current->Lc - 2; i++)
+ us(8, Cm[i], i, 0, 255);
+ }
+
+ return 0;
+}
--
2.16.3
James Almer
2018-06-08 04:18:43 UTC
Permalink
Post by Mark Thompson
+static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ CodedBitstreamJPEGContext *priv = ctx->priv_data;
+ PutBitContext pbc;
+ int err;
+
+ if (!priv->write_buffer) {
+ // Initial write buffer size is 1MB.
+ priv->write_buffer_size = 1024 * 1024;
+
+ err = av_reallocp(&priv->write_buffer, priv->write_buffer_size);
You could use av_fast_malloc() instead, since you don't care about the
previous contents of the buffer. But it will make no real difference
admittedly since at most it will be reallocated twice or thrice for
really big files and that's it.

I sent a patch implementing this change for the other modules some
weeks, which i guess you didn't see and i forgot about :p
Post by Mark Thompson
+ if (err < 0) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a "
+ "sufficiently large write buffer (last attempt "
+ "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size);
+ return err;
+ }
+ }
Mark Thompson
2018-06-13 21:28:00 UTC
Permalink
Post by James Almer
Post by Mark Thompson
+static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ CodedBitstreamJPEGContext *priv = ctx->priv_data;
+ PutBitContext pbc;
+ int err;
+
+ if (!priv->write_buffer) {
+ // Initial write buffer size is 1MB.
+ priv->write_buffer_size = 1024 * 1024;
+
+ err = av_reallocp(&priv->write_buffer, priv->write_buffer_size);
You could use av_fast_malloc() instead, since you don't care about the
previous contents of the buffer. But it will make no real difference
admittedly since at most it will be reallocated twice or thrice for
really big files and that's it.
I sent a patch implementing this change for the other modules some
weeks, which i guess you didn't see and i forgot about :p
That seems reasonable, but we should probably split this whole write-buffer stuff into a separate function so it doesn't keep being repeated in new places.

I'll leave it identical to the existing ones for now - if you'd like to pursue the fast_malloc patch (which I think looks fine) then I'll change it to match, and likewise if we can make a suitable new function.

Thanks,

- Mark
James Almer
2018-06-13 22:43:02 UTC
Permalink
Post by Mark Thompson
Post by James Almer
Post by Mark Thompson
+static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ CodedBitstreamJPEGContext *priv = ctx->priv_data;
+ PutBitContext pbc;
+ int err;
+
+ if (!priv->write_buffer) {
+ // Initial write buffer size is 1MB.
+ priv->write_buffer_size = 1024 * 1024;
+
+ err = av_reallocp(&priv->write_buffer, priv->write_buffer_size);
You could use av_fast_malloc() instead, since you don't care about the
previous contents of the buffer. But it will make no real difference
admittedly since at most it will be reallocated twice or thrice for
really big files and that's it.
I sent a patch implementing this change for the other modules some
weeks, which i guess you didn't see and i forgot about :p
That seems reasonable, but we should probably split this whole write-buffer stuff into a separate function so it doesn't keep being repeated in new places.
I'll leave it identical to the existing ones for now - if you'd like to pursue the fast_malloc patch (which I think looks fine) then I'll change it to match, and likewise if we can make a suitable new function.
Push as is. If we change it we can do it to all modules at the same time
later.
Post by Mark Thompson
Thanks,
- Mark
_______________________________________________
ffmpeg-devel mailing list
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
m***@gmail.com
2018-06-08 01:16:08 UTC
Permalink
Post by Mark Thompson
---
configure | 2 +
libavcodec/Makefile | 1 +
libavcodec/cbs.c | 6 +
libavcodec/cbs_internal.h | 1 +
libavcodec/cbs_jpeg.c | 513 ++++++++++++++++++++++++++++++++++
libavcodec/cbs_jpeg.h | 128 +++++++++
libavcodec/cbs_jpeg_syntax_template.c | 191 +++++++++++++
7 files changed, 842 insertions(+)
create mode 100644 libavcodec/cbs_jpeg.c
create mode 100644 libavcodec/cbs_jpeg.h
create mode 100644 libavcodec/cbs_jpeg_syntax_template.c
diff --git a/configure b/configure
index 790f55be14..d908283954 100755
--- a/configure
+++ b/configure
@@ -2244,6 +2244,7 @@ CONFIG_EXTRA="
cbs
cbs_h264
cbs_h265
+ cbs_jpeg
cbs_mpeg2
cbs_vp9
dirac_parse
@@ -2507,6 +2508,7 @@ threads_if_any="$THREADS_LIST"
# subsystems
cbs_h264_select="cbs golomb"
cbs_h265_select="cbs golomb"
+cbs_jpeg_select="cbs"
cbs_mpeg2_select="cbs"
cbs_vp9_select="cbs"
dct_select="rdft"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 3ab071a039..2a1e0de110 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -64,6 +64,7 @@ OBJS-$(CONFIG_CABAC) += cabac.o
OBJS-$(CONFIG_CBS) += cbs.o
OBJS-$(CONFIG_CBS_H264) += cbs_h2645.o h2645_parse.o
OBJS-$(CONFIG_CBS_H265) += cbs_h2645.o h2645_parse.o
+OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o
OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o
OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o
OBJS-$(CONFIG_CRYSTALHD) += crystalhd.o
diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c
index be6c043b58..bb3ce95971 100644
--- a/libavcodec/cbs.c
+++ b/libavcodec/cbs.c
@@ -35,6 +35,9 @@ static const CodedBitstreamType *cbs_type_table[] = {
#if CONFIG_CBS_H265
&ff_cbs_type_h265,
#endif
+#if CONFIG_CBS_JPEG
+ &ff_cbs_type_jpeg,
+#endif
#if CONFIG_CBS_MPEG2
&ff_cbs_type_mpeg2,
#endif
@@ -50,6 +53,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = {
#if CONFIG_CBS_H265
AV_CODEC_ID_H265,
#endif
+#if CONFIG_CBS_JPEG
+ AV_CODEC_ID_MJPEG,
+#endif
#if CONFIG_CBS_MPEG2
AV_CODEC_ID_MPEG2VIDEO,
#endif
diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h
index 172b8a2515..e0e912e28e 100644
--- a/libavcodec/cbs_internal.h
+++ b/libavcodec/cbs_internal.h
@@ -88,6 +88,7 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc,
extern const CodedBitstreamType ff_cbs_type_h264;
extern const CodedBitstreamType ff_cbs_type_h265;
+extern const CodedBitstreamType ff_cbs_type_jpeg;
extern const CodedBitstreamType ff_cbs_type_mpeg2;
extern const CodedBitstreamType ff_cbs_type_vp9;
diff --git a/libavcodec/cbs_jpeg.c b/libavcodec/cbs_jpeg.c
new file mode 100644
index 0000000000..365db73394
--- /dev/null
+++ b/libavcodec/cbs_jpeg.c
@@ -0,0 +1,513 @@
+/*
+ * 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 "cbs.h"
+#include "cbs_internal.h"
+#include "cbs_jpeg.h"
+
+
+#define HEADER(name) do { \
+ ff_cbs_trace_header(ctx, name); \
+ } while (0)
+
+#define CHECK(call) do { \
+ err = (call); \
+ if (err < 0) \
+ return err; \
+ } while (0)
+
+#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL)
+
+#define u(width, name, range_min, range_max) \
+ xu(width, name, range_min, range_max, 0)
+#define us(width, name, sub, range_min, range_max) \
+ xu(width, name, range_min, range_max, 1, sub)
+
+
+#define READ
+#define READWRITE read
+#define RWContext GetBitContext
+#define FUNC(name) cbs_jpeg_read_ ## name
+
+#define xu(width, name, range_min, range_max, subs, ...) do { \
+ uint32_t value = range_min; \
+ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \
+ SUBSCRIPTS(subs, __VA_ARGS__), \
+ &value, range_min, range_max)); \
+ current->name = value; \
+ } while (0)
+
+#include "cbs_jpeg_syntax_template.c"
+
+#undef READ
+#undef READWRITE
+#undef RWContext
+#undef FUNC
+#undef xu
+
+#define WRITE
+#define READWRITE write
+#define RWContext PutBitContext
+#define FUNC(name) cbs_jpeg_write_ ## name
+
+#define xu(width, name, range_min, range_max, subs, ...) do { \
+ uint32_t value = current->name; \
+ CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \
+ SUBSCRIPTS(subs, __VA_ARGS__), \
+ value, range_min, range_max)); \
+ } while (0)
+
+
+#include "cbs_jpeg_syntax_template.c"
+
+#undef READ
+#undef READWRITE
+#undef RWContext
+#undef FUNC
+#undef xu
+
+
+static void cbs_jpeg_free_application_data(void *unit, uint8_t *content)
+{
+ JPEGRawApplicationData *ad = (JPEGRawApplicationData*)content;
+ av_buffer_unref(&ad->Ap_ref);
+ av_freep(&content);
+}
+
+static void cbs_jpeg_free_scan(void *unit, uint8_t *content)
+{
+ JPEGRawScan *scan = (JPEGRawScan*)content;
+ av_buffer_unref(&scan->data_ref);
+ av_freep(&content);
+}
+
+static int cbs_jpeg_split_fragment(CodedBitstreamContext *ctx,
+ CodedBitstreamFragment *frag,
+ int header)
+{
+ AVBufferRef *data_ref;
+ uint8_t *data;
+ size_t data_size;
+ int unit, start, end, marker, next_start, next_marker;
+ int err, i, j, length;
+
+ if (frag->data_size < 4) {
+ // Definitely too short to be meaningful.
+ return AVERROR_INVALIDDATA;
+ }
+
+ for (i = 0; i + 1 < frag->data_size && frag->data[i] != 0xff; i++);
+ if (i > 0) {
+ av_log(ctx->log_ctx, AV_LOG_WARNING, "Discarding %d bytes at "
+ "beginning of image.\n", i);
+ }
+ for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++);
+ if (i + 1 >= frag->data_size && frag->data[i]) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
+ "no SOI marker found.\n");
+ return AVERROR_INVALIDDATA;
+ }
+ marker = frag->data[i];
+ if (marker != JPEG_MARKER_SOI) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: first "
+ "marker is %02x, should be SOI.\n", marker);
+ return AVERROR_INVALIDDATA;
+ }
+ for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++);
+ if (i + 1 >= frag->data_size) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
+ "no image content found.\n");
+ return AVERROR_INVALIDDATA;
+ }
+ marker = frag->data[i];
+ start = i + 1;
+
+ for (unit = 0;; unit++) {
+ if (marker == JPEG_MARKER_EOI) {
+ break;
+ } else if (marker == JPEG_MARKER_SOS) {
+ for (i = start; i + 1 < frag->data_size; i++) {
+ if (frag->data[i] != 0xff)
+ continue;
+ end = i;
+ for (++i; i + 1 < frag->data_size &&
+ frag->data[i] == 0xff; i++);
+ if (i + 1 >= frag->data_size) {
+ next_marker = -1;
+ } else {
+ if (frag->data[i] == 0x00)
+ continue;
+ next_marker = frag->data[i];
+ next_start = i + 1;
+ }
+ break;
+ }
+ } else {
+ i = start;
+ if (i + 2 > frag->data_size) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
+ "truncated at %02x marker.\n", marker);
+ return AVERROR_INVALIDDATA;
+ }
+ length = AV_RB16(frag->data + i);
+ if (i + length > frag->data_size) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
+ "truncated at %02x marker segment.\n", marker);
+ return AVERROR_INVALIDDATA;
+ }
+ end = start + length;
+
+ i = end;
+ if (frag->data[i] != 0xff) {
+ next_marker = -1;
+ } else {
+ for (++i; i + 1 < frag->data_size &&
+ frag->data[i] == 0xff; i++);
+ if (i + 1 >= frag->data_size) {
+ next_marker = -1;
+ } else {
+ next_marker = frag->data[i];
+ next_start = i + 1;
+ }
+ }
+ }
+
+ if (marker == JPEG_MARKER_SOS) {
+ length = AV_RB16(frag->data + start);
+
+ data_ref = NULL;
+ data = av_malloc(end - start +
+ AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!data)
+ return AVERROR(ENOMEM);
+
+ memcpy(data, frag->data + start, length);
+ for (i = start + length, j = length; i < end; i++, j++) {
+ if (frag->data[i] == 0xff) {
+ while (frag->data[i] == 0xff)
+ ++i;
+ data[j] = 0xff;
+ } else {
+ data[j] = frag->data[i];
+ }
+ }
+ data_size = j;
+
+ memset(data + data_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+
+ } else {
+ data = frag->data + start;
+ data_size = end - start;
+ data_ref = frag->data_ref;
+ }
+
+ err = ff_cbs_insert_unit_data(ctx, frag, unit, marker,
+ data, data_size, data_ref);
+ if (err < 0) {
+ if (!data_ref)
+ av_freep(&data);
+ return err;
+ }
+
+ if (next_marker == -1)
+ break;
+ marker = next_marker;
+ start = next_start;
+ }
+
+ return 0;
+}
+
+static int cbs_jpeg_read_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ GetBitContext gbc;
+ int err;
+
+ err = init_get_bits(&gbc, unit->data, 8 * unit->data_size);
+ if (err < 0)
+ return err;
+
+ if (unit->type >= JPEG_MARKER_SOF0 &&
+ unit->type <= JPEG_MARKER_SOF3) {
+ err = ff_cbs_alloc_unit_content(ctx, unit,
+ sizeof(JPEGRawFrameHeader),
+ NULL);
+ if (err < 0)
+ return err;
+
+ err = cbs_jpeg_read_frame_header(ctx, &gbc, unit->content);
+ if (err < 0)
+ return err;
+
+ } else if (unit->type >= JPEG_MARKER_APPN &&
+ unit->type <= JPEG_MARKER_APPN + 15) {
+ err = ff_cbs_alloc_unit_content(ctx, unit,
+ sizeof(JPEGRawApplicationData),
+ &cbs_jpeg_free_application_data);
+ if (err < 0)
+ return err;
+
+ err = cbs_jpeg_read_application_data(ctx, &gbc, unit->content);
+ if (err < 0)
+ return err;
+
+ } else if (unit->type == JPEG_MARKER_SOS) {
+ JPEGRawScan *scan;
+ int pos;
+
+ err = ff_cbs_alloc_unit_content(ctx, unit,
+ sizeof(JPEGRawScan),
+ &cbs_jpeg_free_scan);
+ if (err < 0)
+ return err;
+ scan = unit->content;
+
+ err = cbs_jpeg_read_scan_header(ctx, &gbc, &scan->header);
+ if (err < 0)
+ return err;
+
+ pos = get_bits_count(&gbc);
+ av_assert0(pos % 8 == 0);
+ if (pos > 0) {
+ scan->data_size = unit->data_size - pos / 8;
+ scan->data_ref = av_buffer_ref(unit->data_ref);
+ if (!scan->data_ref)
+ return AVERROR(ENOMEM);
+ scan->data = unit->data + pos / 8;
+ }
+
+ } else {
+ switch (unit->type) {
+#define SEGMENT(marker, type, func) \
+ case JPEG_MARKER_ ## marker: \
+ { \
+ err = ff_cbs_alloc_unit_content(ctx, unit, \
+ sizeof(type), NULL); \
+ if (err < 0) \
+ return err; \
+ err = cbs_jpeg_read_ ## func(ctx, &gbc, unit->content); \
+ if (err < 0) \
+ return err; \
+ } \
+ break
+ SEGMENT(DQT, JPEGRawQuantisationTableSpecification, dqt);
+ SEGMENT(DHT, JPEGRawHuffmanTableSpecification, dht);
+ SEGMENT(COM, JPEGRawComment, comment);
+#undef SEGMENT
+ return AVERROR(ENOSYS);
+ }
+ }
+
+ return 0;
+}
+
+static int cbs_jpeg_write_scan(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit,
+ PutBitContext *pbc)
+{
+ JPEGRawScan *scan = unit->content;
+ int i, err;
+
+ err = cbs_jpeg_write_scan_header(ctx, pbc, &scan->header);
+ if (err < 0)
+ return err;
+
+ if (scan->data) {
+ if (scan->data_size * 8 > put_bits_left(pbc))
+ return AVERROR(ENOSPC);
+
+ for (i = 0; i < scan->data_size; i++)
+ put_bits(pbc, 8, scan->data[i]);
+ }
+
+ return 0;
+}
+
+static int cbs_jpeg_write_segment(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit,
+ PutBitContext *pbc)
+{
+ int err;
+
+ if (unit->type >= JPEG_MARKER_SOF0 &&
+ unit->type <= JPEG_MARKER_SOF3) {
+ err = cbs_jpeg_write_frame_header(ctx, pbc, unit->content);
+ } else if (unit->type >= JPEG_MARKER_APPN &&
+ unit->type <= JPEG_MARKER_APPN + 15) {
+ err = cbs_jpeg_write_application_data(ctx, pbc, unit->content);
+ } else {
+ switch (unit->type) {
+#define SEGMENT(marker, func) \
+ case JPEG_MARKER_ ## marker: \
+ err = cbs_jpeg_write_ ## func(ctx, pbc, unit->content); \
+ break;
+ SEGMENT(DQT, dqt);
+ SEGMENT(DHT, dht);
+ SEGMENT(COM, comment);
+ return AVERROR_PATCHWELCOME;
+ }
+ }
+
+ return err;
+}
+
+static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ CodedBitstreamJPEGContext *priv = ctx->priv_data;
+ PutBitContext pbc;
+ int err;
+
+ if (!priv->write_buffer) {
+ // Initial write buffer size is 1MB.
+ priv->write_buffer_size = 1024 * 1024;
+
+ err = av_reallocp(&priv->write_buffer, priv->write_buffer_size);
+ if (err < 0) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a "
+ "sufficiently large write buffer (last attempt "
+ "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size);
+ return err;
+ }
+ }
+
+ init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size);
+
+ if (unit->type == JPEG_MARKER_SOS)
+ err = cbs_jpeg_write_scan(ctx, unit, &pbc);
+ else
+ err = cbs_jpeg_write_segment(ctx, unit, &pbc);
+
+ if (err == AVERROR(ENOSPC)) {
+ // Overflow.
+ priv->write_buffer_size *= 2;
+ goto reallocate_and_try_again;
+ }
+ if (err < 0) {
+ // Write failed for some other reason.
+ return err;
+ }
+
+ if (put_bits_count(&pbc) % 8)
+ unit->data_bit_padding = 8 - put_bits_count(&pbc) % 8;
+ else
+ unit->data_bit_padding = 0;
+
+ unit->data_size = (put_bits_count(&pbc) + 7) / 8;
+ flush_put_bits(&pbc);
+
+ err = ff_cbs_alloc_unit_data(ctx, unit, unit->data_size);
+ if (err < 0)
+ return err;
+
+ memcpy(unit->data, priv->write_buffer, unit->data_size);
+
+ return 0;
+}
+
+static int cbs_jpeg_assemble_fragment(CodedBitstreamContext *ctx,
+ CodedBitstreamFragment *frag)
+{
+ const CodedBitstreamUnit *unit;
+ uint8_t *data;
+ size_t size, dp, sp;
+ int i;
+
+ size = 4; // SOI + EOI.
+ for (i = 0; i < frag->nb_units; i++) {
+ unit = &frag->units[i];
+ size += 2 + unit->data_size;
+ if (unit->type == JPEG_MARKER_SOS) {
+ for (sp = 0; sp < unit->data_size; sp++) {
+ if (unit->data[sp] == 0xff)
+ ++size;
+ }
+ }
+ }
+
+ frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!frag->data_ref)
+ return AVERROR(ENOMEM);
+ data = frag->data_ref->data;
+
+ dp = 0;
+
+ data[dp++] = 0xff;
+ data[dp++] = JPEG_MARKER_SOI;
+
+ for (i = 0; i < frag->nb_units; i++) {
+ unit = &frag->units[i];
+
+ data[dp++] = 0xff;
+ data[dp++] = unit->type;
+
+ if (unit->type != JPEG_MARKER_SOS) {
+ memcpy(data + dp, unit->data, unit->data_size);
+ dp += unit->data_size;
+ } else {
+ sp = AV_RB16(unit->data);
+ av_assert0(sp <= unit->data_size);
+ memcpy(data + dp, unit->data, sp);
+ dp += sp;
+
+ for (; sp < unit->data_size; sp++) {
+ if (unit->data[sp] == 0xff) {
+ data[dp++] = 0xff;
+ data[dp++] = 0x00;
+ } else {
+ data[dp++] = unit->data[sp];
+ }
+ }
+ }
+ }
+
+ data[dp++] = 0xff;
+ data[dp++] = JPEG_MARKER_EOI;
+
+ av_assert0(dp == size);
+
+ memset(data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+ frag->data = data;
+ frag->data_size = size;
+
+ return 0;
+}
+
+static void cbs_jpeg_close(CodedBitstreamContext *ctx)
+{
+ CodedBitstreamJPEGContext *priv = ctx->priv_data;
+
+ av_freep(&priv->write_buffer);
+}
+
+const CodedBitstreamType ff_cbs_type_jpeg = {
+ .codec_id = AV_CODEC_ID_MJPEG,
+
+ .priv_data_size = sizeof(CodedBitstreamJPEGContext),
+
+ .split_fragment = &cbs_jpeg_split_fragment,
+ .read_unit = &cbs_jpeg_read_unit,
+ .write_unit = &cbs_jpeg_write_unit,
+ .assemble_fragment = &cbs_jpeg_assemble_fragment,
+
+ .close = &cbs_jpeg_close,
+};
diff --git a/libavcodec/cbs_jpeg.h b/libavcodec/cbs_jpeg.h
new file mode 100644
index 0000000000..f427c4c616
--- /dev/null
+++ b/libavcodec/cbs_jpeg.h
@@ -0,0 +1,128 @@
+/*
+ * 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
+ */
+
+#ifndef AVCODEC_CBS_JPEG_H
+#define AVCODEC_CBS_JPEG_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+
missing "libavutil/buffer.h" for AVBufferRef *, use make
checkheaders can reproduce the error
Post by Mark Thompson
+enum {
+ JPEG_MARKER_SOF0 = 0xc0,
+ JPEG_MARKER_SOF1 = 0xc1,
+ JPEG_MARKER_SOF2 = 0xc2,
+ JPEG_MARKER_SOF3 = 0xc3,
+
+ JPEG_MARKER_DHT = 0xc4,
+ JPEG_MARKER_SOI = 0xd8,
+ JPEG_MARKER_EOI = 0xd9,
+ JPEG_MARKER_SOS = 0xda,
+ JPEG_MARKER_DQT = 0xdb,
+
+ JPEG_MARKER_APPN = 0xe0,
+ JPEG_MARKER_JPGN = 0xf0,
+ JPEG_MARKER_COM = 0xfe,
+};
+
+enum {
+ JPEG_MAX_COMPONENTS = 255,
+
+ JPEG_MAX_HEIGHT = 65535,
+ JPEG_MAX_WIDTH = 65535,
+};
+
+
+typedef struct JPEGRawFrameHeader {
+ uint16_t Lf;
+ uint8_t P;
+ uint16_t Y;
+ uint16_t X;
+ uint16_t Nf;
+
+ uint8_t C [JPEG_MAX_COMPONENTS];
+ uint8_t H [JPEG_MAX_COMPONENTS];
+ uint8_t V [JPEG_MAX_COMPONENTS];
+ uint8_t Tq[JPEG_MAX_COMPONENTS];
+} JPEGRawFrameHeader;
+
+typedef struct JPEGRawScanHeader {
+ uint16_t Ls;
+ uint8_t Ns;
+
+ uint8_t Cs[JPEG_MAX_COMPONENTS];
+ uint8_t Td[JPEG_MAX_COMPONENTS];
+ uint8_t Ta[JPEG_MAX_COMPONENTS];
+
+ uint8_t Ss;
+ uint8_t Se;
+ uint8_t Ah;
+ uint8_t Al;
+} JPEGRawScanHeader;
+
+typedef struct JPEGRawScan {
+ JPEGRawScanHeader header;
+ uint8_t *data;
+ size_t data_size;
+ AVBufferRef *data_ref;
+} JPEGRawScan;
+
+typedef struct JPEGRawQuantisationTable {
+ uint8_t Pq;
+ uint8_t Tq;
+ uint16_t Q[64];
+} JPEGRawQuantisationTable;
+
+typedef struct JPEGRawQuantisationTableSpecification {
+ uint16_t Lq;
+ JPEGRawQuantisationTable table[4];
+} JPEGRawQuantisationTableSpecification;
+
+typedef struct JPEGRawHuffmanTable {
+ uint8_t Tc;
+ uint8_t Th;
+ uint8_t L[16];
+ uint8_t V[224];
+} JPEGRawHuffmanTable;
+
+typedef struct JPEGRawHuffmanTableSpecification {
+ uint16_t Lh;
+ JPEGRawHuffmanTable table[8];
+} JPEGRawHuffmanTableSpecification;
+
+typedef struct JPEGRawApplicationData {
+ uint16_t Lp;
+ uint8_t *Ap;
+ AVBufferRef *Ap_ref;
+} JPEGRawApplicationData;
+
+typedef struct JPEGRawComment {
+ uint16_t Lc;
+ uint8_t *Cm;
+ AVBufferRef *Cm_ref;
+} JPEGRawComment;
+
+
+typedef struct CodedBitstreamJPEGContext {
+ // Write buffer.
+ uint8_t *write_buffer;
+ size_t write_buffer_size;
+} CodedBitstreamJPEGContext;
+
+
+#endif /* AVCODEC_CBS_JPEG_H */
diff --git a/libavcodec/cbs_jpeg_syntax_template.c b/libavcodec/cbs_jpeg_syntax_template.c
new file mode 100644
index 0000000000..d3cd9ff62e
--- /dev/null
+++ b/libavcodec/cbs_jpeg_syntax_template.c
@@ -0,0 +1,191 @@
+/*
+ * 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
+ */
+
+static int FUNC(frame_header)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawFrameHeader *current)
+{
+ int err, i;
+
+ HEADER("Frame Header");
+
+ u(16, Lf, 8, 8 + 3 * JPEG_MAX_COMPONENTS);
+
+ u(8, P, 2, 16);
+ u(16, Y, 0, JPEG_MAX_HEIGHT);
+ u(16, X, 1, JPEG_MAX_WIDTH);
+ u(8, Nf, 1, JPEG_MAX_COMPONENTS);
+
+ for (i = 0; i < current->Nf; i++) {
+ us(8, C[i], i, 0, JPEG_MAX_COMPONENTS);
+ us(4, H[i], i, 1, 4);
+ us(4, V[i], i, 1, 4);
+ us(8, Tq[i], i, 0, 3);
+ }
+
+ return 0;
+}
+
+static int FUNC(quantisation_table)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawQuantisationTable *current)
+{
+ int err, i;
+
+ u(4, Pq, 0, 1);
+ u(4, Tq, 0, 3);
+
+ if (current->Pq) {
+ for (i = 0; i < 64; i++)
+ us(16, Q[i], i, 1, 255);
+ } else {
+ for (i = 0; i < 64; i++)
+ us(8, Q[i], i, 1, 255);
+ }
+
+ return 0;
+}
+
+static int FUNC(dqt)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawQuantisationTableSpecification *current)
+{
+ int err, i, n;
+
+ HEADER("Quantisation Tables");
+
+ u(16, Lq, 2, 2 + 4 * 65);
+ n = current->Lq / 65;
+
+ for (i = 0; i < n; i++)
+ CHECK(FUNC(quantisation_table)(ctx, rw, &current->table[i]));
+
+ return 0;
+}
+
+static int FUNC(huffman_table)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawHuffmanTable *current)
+{
+ int err, i, j, ij;
+
+ u(4, Tc, 0, 1);
+ u(4, Th, 0, 3);
+
+ for (i = 0; i < 16; i++)
+ us(8, L[i], i, 0, 224);
+
+ ij = 0;
+ for (i = 0; i < 16; i++) {
+ for (j = 0; j < current->L[i]; j++) {
+ us(8, V[ij], ij, 0, 255);
+ ++ij;
+ }
+ }
+
+ return 0;
+}
+
+static int FUNC(dht)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawHuffmanTableSpecification *current)
+{
+ int err, i, j, n;
+
+ HEADER("Huffman Tables");
+
+ u(16, Lh, 2, 2 + 8 * (1 + 16 + 256));
+
+ n = 2;
+ for (i = 0; n < current->Lh; i++) {
+ CHECK(FUNC(huffman_table)(ctx, rw, &current->table[i]));
+
+ ++n;
+ for (j = 0; j < 16; j++)
+ n += 1 + current->table[i].L[j];
+ }
+
+ return 0;
+}
+
+static int FUNC(scan_header)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawScanHeader *current)
+{
+ int err, j;
+
+ HEADER("Scan");
+
+ u(16, Ls, 6, 6 + 2 * JPEG_MAX_COMPONENTS);
+
+ u(8, Ns, 1, 4);
+ for (j = 0; j < current->Ns; j++) {
+ us(8, Cs[j], j, 0, JPEG_MAX_COMPONENTS);
+ us(4, Td[j], j, 0, 3);
+ us(4, Ta[j], j, 0, 3);
+ }
+
+ u(8, Ss, 0, 63);
+ u(8, Se, 0, 63);
+ u(4, Ah, 0, 13);
+ u(4, Al, 0, 15);
+
+ return 0;
+}
+
+static int FUNC(application_data)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawApplicationData *current)
+{
+ int err, i;
+
+ HEADER("Application Data");
+
+ u(16, Lp, 2, 65535);
+
+ if (current->Lp > 2) {
+#ifdef READ
+ current->Ap_ref = av_buffer_alloc(current->Lp - 2);
+ if (!current->Ap_ref)
+ return AVERROR(ENOMEM);
+ current->Ap = current->Ap_ref->data;
+#endif
+
+ for (i = 0; i < current->Lp - 2; i++)
+ us(8, Ap[i], i, 0, 255);
+ }
+
+ return 0;
+}
+
+static int FUNC(comment)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawComment *current)
+{
+ int err, i;
+
+ HEADER("Comment");
+
+ u(16, Lc, 2, 65535);
+
+ if (current->Lc > 2) {
+#ifdef READ
+ current->Cm_ref = av_buffer_alloc(current->Lc - 2);
+ if (!current->Cm_ref)
+ return AVERROR(ENOMEM);
+ current->Cm = current->Cm_ref->data;
+#endif
+
+ for (i = 0; i < current->Lc - 2; i++)
+ us(8, Cm[i], i, 0, 255);
+ }
+
+ return 0;
+}
--
2.16.3
_______________________________________________
ffmpeg-devel mailing list
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Mark Thompson
2018-06-13 21:22:12 UTC
Permalink
Post by m***@gmail.com
Post by Mark Thompson
---
configure | 2 +
libavcodec/Makefile | 1 +
libavcodec/cbs.c | 6 +
libavcodec/cbs_internal.h | 1 +
libavcodec/cbs_jpeg.c | 513 ++++++++++++++++++++++++++++++++++
libavcodec/cbs_jpeg.h | 128 +++++++++
libavcodec/cbs_jpeg_syntax_template.c | 191 +++++++++++++
7 files changed, 842 insertions(+)
create mode 100644 libavcodec/cbs_jpeg.c
create mode 100644 libavcodec/cbs_jpeg.h
create mode 100644 libavcodec/cbs_jpeg_syntax_template.c
...
diff --git a/libavcodec/cbs_jpeg.h b/libavcodec/cbs_jpeg.h
new file mode 100644
index 0000000000..f427c4c616
--- /dev/null
+++ b/libavcodec/cbs_jpeg.h
@@ -0,0 +1,128 @@
+/*
+ * 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
+ */
+
+#ifndef AVCODEC_CBS_JPEG_H
+#define AVCODEC_CBS_JPEG_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+
missing "libavutil/buffer.h" for AVBufferRef *, use make
checkheaders can reproduce the error
Yep. Fixed, thank you!

- Mark
Post by m***@gmail.com
Post by Mark Thompson
+enum {
+ JPEG_MARKER_SOF0 = 0xc0,
+ JPEG_MARKER_SOF1 = 0xc1,
+ JPEG_MARKER_SOF2 = 0xc2,
+ JPEG_MARKER_SOF3 = 0xc3,
+
+ JPEG_MARKER_DHT = 0xc4,
+ JPEG_MARKER_SOI = 0xd8,
+ JPEG_MARKER_EOI = 0xd9,
+ JPEG_MARKER_SOS = 0xda,
+ JPEG_MARKER_DQT = 0xdb,
+
+ JPEG_MARKER_APPN = 0xe0,
+ JPEG_MARKER_JPGN = 0xf0,
+ JPEG_MARKER_COM = 0xfe,
+};
+
+enum {
+ JPEG_MAX_COMPONENTS = 255,
+
+ JPEG_MAX_HEIGHT = 65535,
+ JPEG_MAX_WIDTH = 65535,
+};
+
+
+typedef struct JPEGRawFrameHeader {
+ uint16_t Lf;
+ uint8_t P;
+ uint16_t Y;
+ uint16_t X;
+ uint16_t Nf;
+
+ uint8_t C [JPEG_MAX_COMPONENTS];
+ uint8_t H [JPEG_MAX_COMPONENTS];
+ uint8_t V [JPEG_MAX_COMPONENTS];
+ uint8_t Tq[JPEG_MAX_COMPONENTS];
+} JPEGRawFrameHeader;
+
+typedef struct JPEGRawScanHeader {
+ uint16_t Ls;
+ uint8_t Ns;
+
+ uint8_t Cs[JPEG_MAX_COMPONENTS];
+ uint8_t Td[JPEG_MAX_COMPONENTS];
+ uint8_t Ta[JPEG_MAX_COMPONENTS];
+
+ uint8_t Ss;
+ uint8_t Se;
+ uint8_t Ah;
+ uint8_t Al;
+} JPEGRawScanHeader;
+
+typedef struct JPEGRawScan {
+ JPEGRawScanHeader header;
+ uint8_t *data;
+ size_t data_size;
+ AVBufferRef *data_ref;
+} JPEGRawScan;
+
+typedef struct JPEGRawQuantisationTable {
+ uint8_t Pq;
+ uint8_t Tq;
+ uint16_t Q[64];
+} JPEGRawQuantisationTable;
+
+typedef struct JPEGRawQuantisationTableSpecification {
+ uint16_t Lq;
+ JPEGRawQuantisationTable table[4];
+} JPEGRawQuantisationTableSpecification;
+
+typedef struct JPEGRawHuffmanTable {
+ uint8_t Tc;
+ uint8_t Th;
+ uint8_t L[16];
+ uint8_t V[224];
+} JPEGRawHuffmanTable;
+
+typedef struct JPEGRawHuffmanTableSpecification {
+ uint16_t Lh;
+ JPEGRawHuffmanTable table[8];
+} JPEGRawHuffmanTableSpecification;
+
+typedef struct JPEGRawApplicationData {
+ uint16_t Lp;
+ uint8_t *Ap;
+ AVBufferRef *Ap_ref;
+} JPEGRawApplicationData;
+
+typedef struct JPEGRawComment {
+ uint16_t Lc;
+ uint8_t *Cm;
+ AVBufferRef *Cm_ref;
+} JPEGRawComment;
+
+
+typedef struct CodedBitstreamJPEGContext {
+ // Write buffer.
+ uint8_t *write_buffer;
+ size_t write_buffer_size;
+} CodedBitstreamJPEGContext;
+
+
+#endif /* AVCODEC_CBS_JPEG_H */
Xiang, Haihao
2018-06-15 00:52:34 UTC
Permalink
Post by Mark Thompson
---
configure | 2 +
libavcodec/Makefile | 1 +
libavcodec/cbs.c | 6 +
libavcodec/cbs_internal.h | 1 +
libavcodec/cbs_jpeg.c | 513
++++++++++++++++++++++++++++++++++
libavcodec/cbs_jpeg.h | 128 +++++++++
libavcodec/cbs_jpeg_syntax_template.c | 191 +++++++++++++
7 files changed, 842 insertions(+)
create mode 100644 libavcodec/cbs_jpeg.c
create mode 100644 libavcodec/cbs_jpeg.h
create mode 100644 libavcodec/cbs_jpeg_syntax_template.c
diff --git a/configure b/configure
index 790f55be14..d908283954 100755
--- a/configure
+++ b/configure
@@ -2244,6 +2244,7 @@ CONFIG_EXTRA="
cbs
cbs_h264
cbs_h265
+ cbs_jpeg
cbs_mpeg2
cbs_vp9
dirac_parse
@@ -2507,6 +2508,7 @@ threads_if_any="$THREADS_LIST"
# subsystems
cbs_h264_select="cbs golomb"
cbs_h265_select="cbs golomb"
+cbs_jpeg_select="cbs"
cbs_mpeg2_select="cbs"
cbs_vp9_select="cbs"
dct_select="rdft"
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 3ab071a039..2a1e0de110 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -64,6 +64,7 @@ OBJS-$(CONFIG_CABAC) += cabac.o
OBJS-$(CONFIG_CBS) += cbs.o
OBJS-$(CONFIG_CBS_H264) += cbs_h2645.o h2645_parse.o
OBJS-$(CONFIG_CBS_H265) += cbs_h2645.o h2645_parse.o
+OBJS-$(CONFIG_CBS_JPEG) += cbs_jpeg.o
OBJS-$(CONFIG_CBS_MPEG2) += cbs_mpeg2.o
OBJS-$(CONFIG_CBS_VP9) += cbs_vp9.o
OBJS-$(CONFIG_CRYSTALHD) += crystalhd.o
diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c
index be6c043b58..bb3ce95971 100644
--- a/libavcodec/cbs.c
+++ b/libavcodec/cbs.c
@@ -35,6 +35,9 @@ static const CodedBitstreamType *cbs_type_table[] = {
#if CONFIG_CBS_H265
&ff_cbs_type_h265,
#endif
+#if CONFIG_CBS_JPEG
+ &ff_cbs_type_jpeg,
+#endif
#if CONFIG_CBS_MPEG2
&ff_cbs_type_mpeg2,
#endif
@@ -50,6 +53,9 @@ const enum AVCodecID ff_cbs_all_codec_ids[] = {
#if CONFIG_CBS_H265
AV_CODEC_ID_H265,
#endif
+#if CONFIG_CBS_JPEG
+ AV_CODEC_ID_MJPEG,
+#endif
#if CONFIG_CBS_MPEG2
AV_CODEC_ID_MPEG2VIDEO,
#endif
diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h
index 172b8a2515..e0e912e28e 100644
--- a/libavcodec/cbs_internal.h
+++ b/libavcodec/cbs_internal.h
@@ -88,6 +88,7 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc,
extern const CodedBitstreamType ff_cbs_type_h264;
extern const CodedBitstreamType ff_cbs_type_h265;
+extern const CodedBitstreamType ff_cbs_type_jpeg;
extern const CodedBitstreamType ff_cbs_type_mpeg2;
extern const CodedBitstreamType ff_cbs_type_vp9;
diff --git a/libavcodec/cbs_jpeg.c b/libavcodec/cbs_jpeg.c
new file mode 100644
index 0000000000..365db73394
--- /dev/null
+++ b/libavcodec/cbs_jpeg.c
@@ -0,0 +1,513 @@
+/*
+ * 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 "cbs.h"
+#include "cbs_internal.h"
+#include "cbs_jpeg.h"
+
+
+#define HEADER(name) do { \
+ ff_cbs_trace_header(ctx, name); \
+ } while (0)
+
+#define CHECK(call) do { \
+ err = (call); \
+ if (err < 0) \
+ return err; \
+ } while (0)
+
+#define SUBSCRIPTS(subs, ...) (subs > 0 ? ((int[subs + 1]){ subs, __VA_ARGS__ }) : NULL)
+
+#define u(width, name, range_min, range_max) \
+ xu(width, name, range_min, range_max, 0)
+#define us(width, name, sub, range_min, range_max) \
+ xu(width, name, range_min, range_max, 1, sub)
+
+
+#define READ
+#define READWRITE read
+#define RWContext GetBitContext
+#define FUNC(name) cbs_jpeg_read_ ## name
+
+#define xu(width, name, range_min, range_max, subs, ...) do { \
+ uint32_t value = range_min; \
+ CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \
+ SUBSCRIPTS(subs, __VA_ARGS__), \
+ &value, range_min, range_max)); \
+ current->name = value; \
+ } while (0)
+
+#include "cbs_jpeg_syntax_template.c"
+
+#undef READ
+#undef READWRITE
+#undef RWContext
+#undef FUNC
+#undef xu
+
+#define WRITE
+#define READWRITE write
+#define RWContext PutBitContext
+#define FUNC(name) cbs_jpeg_write_ ## name
+
+#define xu(width, name, range_min, range_max, subs, ...) do { \
+ uint32_t value = current->name; \
+ CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \
+ SUBSCRIPTS(subs, __VA_ARGS__), \
+ value, range_min, range_max)); \
+ } while (0)
+
+
+#include "cbs_jpeg_syntax_template.c"
+
+#undef READ
+#undef READWRITE
+#undef RWContext
+#undef FUNC
+#undef xu
+
+
+static void cbs_jpeg_free_application_data(void *unit, uint8_t *content)
+{
+ JPEGRawApplicationData *ad = (JPEGRawApplicationData*)content;
+ av_buffer_unref(&ad->Ap_ref);
+ av_freep(&content);
+}
+
+static void cbs_jpeg_free_scan(void *unit, uint8_t *content)
+{
+ JPEGRawScan *scan = (JPEGRawScan*)content;
+ av_buffer_unref(&scan->data_ref);
+ av_freep(&content);
+}
+
+static int cbs_jpeg_split_fragment(CodedBitstreamContext *ctx,
+ CodedBitstreamFragment *frag,
+ int header)
+{
+ AVBufferRef *data_ref;
+ uint8_t *data;
+ size_t data_size;
+ int unit, start, end, marker, next_start, next_marker;
+ int err, i, j, length;
+
+ if (frag->data_size < 4) {
+ // Definitely too short to be meaningful.
+ return AVERROR_INVALIDDATA;
+ }
+
+ for (i = 0; i + 1 < frag->data_size && frag->data[i] != 0xff; i++);
+ if (i > 0) {
+ av_log(ctx->log_ctx, AV_LOG_WARNING, "Discarding %d bytes at "
+ "beginning of image.\n", i);
+ }
+ for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++);
+ if (i + 1 >= frag->data_size && frag->data[i]) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
+ "no SOI marker found.\n");
+ return AVERROR_INVALIDDATA;
+ }
+ marker = frag->data[i];
+ if (marker != JPEG_MARKER_SOI) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: first "
+ "marker is %02x, should be SOI.\n", marker);
+ return AVERROR_INVALIDDATA;
+ }
+ for (++i; i + 1 < frag->data_size && frag->data[i] == 0xff; i++);
+ if (i + 1 >= frag->data_size) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
+ "no image content found.\n");
+ return AVERROR_INVALIDDATA;
+ }
+ marker = frag->data[i];
+ start = i + 1;
+
+ for (unit = 0;; unit++) {
+ if (marker == JPEG_MARKER_EOI) {
+ break;
+ } else if (marker == JPEG_MARKER_SOS) {
+ for (i = start; i + 1 < frag->data_size; i++) {
+ if (frag->data[i] != 0xff)
+ continue;
+ end = i;
+ for (++i; i + 1 < frag->data_size &&
+ frag->data[i] == 0xff; i++);
+ if (i + 1 >= frag->data_size) {
+ next_marker = -1;
+ } else {
+ if (frag->data[i] == 0x00)
+ continue;
+ next_marker = frag->data[i];
+ next_start = i + 1;
+ }
+ break;
+ }
+ } else {
+ i = start;
+ if (i + 2 > frag->data_size) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
+ "truncated at %02x marker.\n", marker);
+ return AVERROR_INVALIDDATA;
+ }
+ length = AV_RB16(frag->data + i);
+ if (i + length > frag->data_size) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid JPEG image: "
+ "truncated at %02x marker segment.\n", marker);
+ return AVERROR_INVALIDDATA;
+ }
+ end = start + length;
+
+ i = end;
+ if (frag->data[i] != 0xff) {
+ next_marker = -1;
+ } else {
+ for (++i; i + 1 < frag->data_size &&
+ frag->data[i] == 0xff; i++);
+ if (i + 1 >= frag->data_size) {
+ next_marker = -1;
+ } else {
+ next_marker = frag->data[i];
+ next_start = i + 1;
+ }
+ }
+ }
+
+ if (marker == JPEG_MARKER_SOS) {
+ length = AV_RB16(frag->data + start);
+
+ data_ref = NULL;
+ data = av_malloc(end - start +
+ AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!data)
+ return AVERROR(ENOMEM);
+
+ memcpy(data, frag->data + start, length);
+ for (i = start + length, j = length; i < end; i++, j++) {
+ if (frag->data[i] == 0xff) {
+ while (frag->data[i] == 0xff)
+ ++i;
+ data[j] = 0xff;
+ } else {
+ data[j] = frag->data[i];
+ }
+ }
+ data_size = j;
+
+ memset(data + data_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+
+ } else {
+ data = frag->data + start;
+ data_size = end - start;
+ data_ref = frag->data_ref;
+ }
+
+ err = ff_cbs_insert_unit_data(ctx, frag, unit, marker,
+ data, data_size, data_ref);
+ if (err < 0) {
+ if (!data_ref)
+ av_freep(&data);
+ return err;
+ }
+
+ if (next_marker == -1)
+ break;
+ marker = next_marker;
+ start = next_start;
+ }
+
+ return 0;
+}
+
+static int cbs_jpeg_read_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ GetBitContext gbc;
+ int err;
+
+ err = init_get_bits(&gbc, unit->data, 8 * unit->data_size);
+ if (err < 0)
+ return err;
+
+ if (unit->type >= JPEG_MARKER_SOF0 &&
+ unit->type <= JPEG_MARKER_SOF3) {
+ err = ff_cbs_alloc_unit_content(ctx, unit,
+ sizeof(JPEGRawFrameHeader),
+ NULL);
+ if (err < 0)
+ return err;
+
+ err = cbs_jpeg_read_frame_header(ctx, &gbc, unit->content);
+ if (err < 0)
+ return err;
+
+ } else if (unit->type >= JPEG_MARKER_APPN &&
+ unit->type <= JPEG_MARKER_APPN + 15) {
+ err = ff_cbs_alloc_unit_content(ctx, unit,
+ sizeof(JPEGRawApplicationData),
+ &cbs_jpeg_free_application_data);
+ if (err < 0)
+ return err;
+
+ err = cbs_jpeg_read_application_data(ctx, &gbc, unit->content);
+ if (err < 0)
+ return err;
+
+ } else if (unit->type == JPEG_MARKER_SOS) {
+ JPEGRawScan *scan;
+ int pos;
+
+ err = ff_cbs_alloc_unit_content(ctx, unit,
+ sizeof(JPEGRawScan),
+ &cbs_jpeg_free_scan);
+ if (err < 0)
+ return err;
+ scan = unit->content;
+
+ err = cbs_jpeg_read_scan_header(ctx, &gbc, &scan->header);
+ if (err < 0)
+ return err;
+
+ pos = get_bits_count(&gbc);
+ av_assert0(pos % 8 == 0);
+ if (pos > 0) {
+ scan->data_size = unit->data_size - pos / 8;
+ scan->data_ref = av_buffer_ref(unit->data_ref);
+ if (!scan->data_ref)
+ return AVERROR(ENOMEM);
+ scan->data = unit->data + pos / 8;
+ }
+
+ } else {
+ switch (unit->type) {
+#define SEGMENT(marker, type, func) \
+ case JPEG_MARKER_ ## marker: \
+ { \
+ err = ff_cbs_alloc_unit_content(ctx, unit, \
+ sizeof(type), NULL); \
+ if (err < 0) \
+ return err; \
+ err = cbs_jpeg_read_ ## func(ctx, &gbc, unit->content); \
+ if (err < 0) \
+ return err; \
+ } \
+ break
+ SEGMENT(DQT, JPEGRawQuantisationTableSpecification, dqt);
+ SEGMENT(DHT, JPEGRawHuffmanTableSpecification, dht);
+ SEGMENT(COM, JPEGRawComment, comment);
A free function should be provided for JPEGRawComment as JPEGRawComment includes
a AVBufferRef pointer.
Post by Mark Thompson
+#undef SEGMENT
+ return AVERROR(ENOSYS);
+ }
+ }
+
+ return 0;
+}
+
+static int cbs_jpeg_write_scan(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit,
+ PutBitContext *pbc)
+{
+ JPEGRawScan *scan = unit->content;
+ int i, err;
+
+ err = cbs_jpeg_write_scan_header(ctx, pbc, &scan->header);
+ if (err < 0)
+ return err;
+
+ if (scan->data) {
+ if (scan->data_size * 8 > put_bits_left(pbc))
+ return AVERROR(ENOSPC);
+
+ for (i = 0; i < scan->data_size; i++)
+ put_bits(pbc, 8, scan->data[i]);
+ }
+
+ return 0;
+}
+
+static int cbs_jpeg_write_segment(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit,
+ PutBitContext *pbc)
+{
+ int err;
+
+ if (unit->type >= JPEG_MARKER_SOF0 &&
+ unit->type <= JPEG_MARKER_SOF3) {
+ err = cbs_jpeg_write_frame_header(ctx, pbc, unit->content);
+ } else if (unit->type >= JPEG_MARKER_APPN &&
+ unit->type <= JPEG_MARKER_APPN + 15) {
+ err = cbs_jpeg_write_application_data(ctx, pbc, unit->content);
+ } else {
+ switch (unit->type) {
+#define SEGMENT(marker, func) \
+ case JPEG_MARKER_ ## marker: \
+ err = cbs_jpeg_write_ ## func(ctx, pbc, unit->content); \
+ break;
+ SEGMENT(DQT, dqt);
+ SEGMENT(DHT, dht);
+ SEGMENT(COM, comment);
+ return AVERROR_PATCHWELCOME;
+ }
+ }
+
+ return err;
+}
+
+static int cbs_jpeg_write_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ CodedBitstreamJPEGContext *priv = ctx->priv_data;
+ PutBitContext pbc;
+ int err;
+
+ if (!priv->write_buffer) {
+ // Initial write buffer size is 1MB.
+ priv->write_buffer_size = 1024 * 1024;
+
+ err = av_reallocp(&priv->write_buffer, priv->write_buffer_size);
+ if (err < 0) {
+ av_log(ctx->log_ctx, AV_LOG_ERROR, "Unable to allocate a "
+ "sufficiently large write buffer (last attempt "
+ "%"SIZE_SPECIFIER" bytes).\n", priv->write_buffer_size);
+ return err;
+ }
+ }
+
+ init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size);
+
+ if (unit->type == JPEG_MARKER_SOS)
+ err = cbs_jpeg_write_scan(ctx, unit, &pbc);
+ else
+ err = cbs_jpeg_write_segment(ctx, unit, &pbc);
+
+ if (err == AVERROR(ENOSPC)) {
+ // Overflow.
+ priv->write_buffer_size *= 2;
+ goto reallocate_and_try_again;
+ }
+ if (err < 0) {
+ // Write failed for some other reason.
+ return err;
+ }
+
+ if (put_bits_count(&pbc) % 8)
+ unit->data_bit_padding = 8 - put_bits_count(&pbc) % 8;
+ else
+ unit->data_bit_padding = 0;
+
+ unit->data_size = (put_bits_count(&pbc) + 7) / 8;
+ flush_put_bits(&pbc);
+
+ err = ff_cbs_alloc_unit_data(ctx, unit, unit->data_size);
+ if (err < 0)
+ return err;
+
+ memcpy(unit->data, priv->write_buffer, unit->data_size);
+
+ return 0;
+}
+
+static int cbs_jpeg_assemble_fragment(CodedBitstreamContext *ctx,
+ CodedBitstreamFragment *frag)
+{
+ const CodedBitstreamUnit *unit;
+ uint8_t *data;
+ size_t size, dp, sp;
+ int i;
+
+ size = 4; // SOI + EOI.
+ for (i = 0; i < frag->nb_units; i++) {
+ unit = &frag->units[i];
+ size += 2 + unit->data_size;
+ if (unit->type == JPEG_MARKER_SOS) {
+ for (sp = 0; sp < unit->data_size; sp++) {
+ if (unit->data[sp] == 0xff)
+ ++size;
+ }
+ }
+ }
+
+ frag->data_ref = av_buffer_alloc(size + AV_INPUT_BUFFER_PADDING_SIZE);
+ if (!frag->data_ref)
+ return AVERROR(ENOMEM);
+ data = frag->data_ref->data;
+
+ dp = 0;
+
+ data[dp++] = 0xff;
+ data[dp++] = JPEG_MARKER_SOI;
+
+ for (i = 0; i < frag->nb_units; i++) {
+ unit = &frag->units[i];
+
+ data[dp++] = 0xff;
+ data[dp++] = unit->type;
+
+ if (unit->type != JPEG_MARKER_SOS) {
+ memcpy(data + dp, unit->data, unit->data_size);
+ dp += unit->data_size;
+ } else {
+ sp = AV_RB16(unit->data);
+ av_assert0(sp <= unit->data_size);
+ memcpy(data + dp, unit->data, sp);
+ dp += sp;
+
+ for (; sp < unit->data_size; sp++) {
+ if (unit->data[sp] == 0xff) {
+ data[dp++] = 0xff;
+ data[dp++] = 0x00;
+ } else {
+ data[dp++] = unit->data[sp];
+ }
+ }
+ }
+ }
+
+ data[dp++] = 0xff;
+ data[dp++] = JPEG_MARKER_EOI;
+
+ av_assert0(dp == size);
+
+ memset(data + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
+ frag->data = data;
+ frag->data_size = size;
+
+ return 0;
+}
+
+static void cbs_jpeg_close(CodedBitstreamContext *ctx)
+{
+ CodedBitstreamJPEGContext *priv = ctx->priv_data;
+
+ av_freep(&priv->write_buffer);
+}
+
+const CodedBitstreamType ff_cbs_type_jpeg = {
+ .codec_id = AV_CODEC_ID_MJPEG,
+
+ .priv_data_size = sizeof(CodedBitstreamJPEGContext),
+
+ .split_fragment = &cbs_jpeg_split_fragment,
+ .read_unit = &cbs_jpeg_read_unit,
+ .write_unit = &cbs_jpeg_write_unit,
+ .assemble_fragment = &cbs_jpeg_assemble_fragment,
+
+ .close = &cbs_jpeg_close,
+};
diff --git a/libavcodec/cbs_jpeg.h b/libavcodec/cbs_jpeg.h
new file mode 100644
index 0000000000..f427c4c616
--- /dev/null
+++ b/libavcodec/cbs_jpeg.h
@@ -0,0 +1,128 @@
+/*
+ * 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
+ */
+
+#ifndef AVCODEC_CBS_JPEG_H
+#define AVCODEC_CBS_JPEG_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+
+enum {
+ JPEG_MARKER_SOF0 = 0xc0,
+ JPEG_MARKER_SOF1 = 0xc1,
+ JPEG_MARKER_SOF2 = 0xc2,
+ JPEG_MARKER_SOF3 = 0xc3,
+
+ JPEG_MARKER_DHT = 0xc4,
+ JPEG_MARKER_SOI = 0xd8,
+ JPEG_MARKER_EOI = 0xd9,
+ JPEG_MARKER_SOS = 0xda,
+ JPEG_MARKER_DQT = 0xdb,
+
+ JPEG_MARKER_APPN = 0xe0,
+ JPEG_MARKER_JPGN = 0xf0,
+ JPEG_MARKER_COM = 0xfe,
+};
+
+enum {
+ JPEG_MAX_COMPONENTS = 255,
+
+ JPEG_MAX_HEIGHT = 65535,
+ JPEG_MAX_WIDTH = 65535,
+};
+
+
+typedef struct JPEGRawFrameHeader {
+ uint16_t Lf;
+ uint8_t P;
+ uint16_t Y;
+ uint16_t X;
+ uint16_t Nf;
+
+ uint8_t C [JPEG_MAX_COMPONENTS];
+ uint8_t H [JPEG_MAX_COMPONENTS];
+ uint8_t V [JPEG_MAX_COMPONENTS];
+ uint8_t Tq[JPEG_MAX_COMPONENTS];
+} JPEGRawFrameHeader;
+
+typedef struct JPEGRawScanHeader {
+ uint16_t Ls;
+ uint8_t Ns;
+
+ uint8_t Cs[JPEG_MAX_COMPONENTS];
+ uint8_t Td[JPEG_MAX_COMPONENTS];
+ uint8_t Ta[JPEG_MAX_COMPONENTS];
+
+ uint8_t Ss;
+ uint8_t Se;
+ uint8_t Ah;
+ uint8_t Al;
+} JPEGRawScanHeader;
+
+typedef struct JPEGRawScan {
+ JPEGRawScanHeader header;
+ uint8_t *data;
+ size_t data_size;
+ AVBufferRef *data_ref;
+} JPEGRawScan;
+
+typedef struct JPEGRawQuantisationTable {
+ uint8_t Pq;
+ uint8_t Tq;
+ uint16_t Q[64];
+} JPEGRawQuantisationTable;
+
+typedef struct JPEGRawQuantisationTableSpecification {
+ uint16_t Lq;
+ JPEGRawQuantisationTable table[4];
+} JPEGRawQuantisationTableSpecification;
+
+typedef struct JPEGRawHuffmanTable {
+ uint8_t Tc;
+ uint8_t Th;
+ uint8_t L[16];
+ uint8_t V[224];
+} JPEGRawHuffmanTable;
+
+typedef struct JPEGRawHuffmanTableSpecification {
+ uint16_t Lh;
+ JPEGRawHuffmanTable table[8];
+} JPEGRawHuffmanTableSpecification;
+
+typedef struct JPEGRawApplicationData {
+ uint16_t Lp;
+ uint8_t *Ap;
+ AVBufferRef *Ap_ref;
+} JPEGRawApplicationData;
+
+typedef struct JPEGRawComment {
+ uint16_t Lc;
+ uint8_t *Cm;
+ AVBufferRef *Cm_ref;
+} JPEGRawComment;
+
+
+typedef struct CodedBitstreamJPEGContext {
+ // Write buffer.
+ uint8_t *write_buffer;
+ size_t write_buffer_size;
+} CodedBitstreamJPEGContext;
+
+
+#endif /* AVCODEC_CBS_JPEG_H */
diff --git a/libavcodec/cbs_jpeg_syntax_template.c
b/libavcodec/cbs_jpeg_syntax_template.c
new file mode 100644
index 0000000000..d3cd9ff62e
--- /dev/null
+++ b/libavcodec/cbs_jpeg_syntax_template.c
@@ -0,0 +1,191 @@
+/*
+ * 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
+ */
+
+static int FUNC(frame_header)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawFrameHeader *current)
+{
+ int err, i;
+
+ HEADER("Frame Header");
+
+ u(16, Lf, 8, 8 + 3 * JPEG_MAX_COMPONENTS);
+
+ u(8, P, 2, 16);
+ u(16, Y, 0, JPEG_MAX_HEIGHT);
+ u(16, X, 1, JPEG_MAX_WIDTH);
+ u(8, Nf, 1, JPEG_MAX_COMPONENTS);
+
+ for (i = 0; i < current->Nf; i++) {
+ us(8, C[i], i, 0, JPEG_MAX_COMPONENTS);
+ us(4, H[i], i, 1, 4);
+ us(4, V[i], i, 1, 4);
+ us(8, Tq[i], i, 0, 3);
+ }
+
+ return 0;
+}
+
+static int FUNC(quantisation_table)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawQuantisationTable *current)
+{
+ int err, i;
+
+ u(4, Pq, 0, 1);
+ u(4, Tq, 0, 3);
+
+ if (current->Pq) {
+ for (i = 0; i < 64; i++)
+ us(16, Q[i], i, 1, 255);
+ } else {
+ for (i = 0; i < 64; i++)
+ us(8, Q[i], i, 1, 255);
+ }
+
+ return 0;
+}
+
+static int FUNC(dqt)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawQuantisationTableSpecification *current)
+{
+ int err, i, n;
+
+ HEADER("Quantisation Tables");
+
+ u(16, Lq, 2, 2 + 4 * 65);
+ n = current->Lq / 65;
+
+ for (i = 0; i < n; i++)
+ CHECK(FUNC(quantisation_table)(ctx, rw, &current->table[i]));
+
+ return 0;
+}
+
+static int FUNC(huffman_table)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawHuffmanTable *current)
+{
+ int err, i, j, ij;
+
+ u(4, Tc, 0, 1);
+ u(4, Th, 0, 3);
+
+ for (i = 0; i < 16; i++)
+ us(8, L[i], i, 0, 224);
+
+ ij = 0;
+ for (i = 0; i < 16; i++) {
+ for (j = 0; j < current->L[i]; j++) {
+ us(8, V[ij], ij, 0, 255);
+ ++ij;
+ }
+ }
+
+ return 0;
+}
+
+static int FUNC(dht)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawHuffmanTableSpecification *current)
+{
+ int err, i, j, n;
+
+ HEADER("Huffman Tables");
+
+ u(16, Lh, 2, 2 + 8 * (1 + 16 + 256));
+
+ n = 2;
+ for (i = 0; n < current->Lh; i++) {
+ CHECK(FUNC(huffman_table)(ctx, rw, &current->table[i]));
+
+ ++n;
+ for (j = 0; j < 16; j++)
+ n += 1 + current->table[i].L[j];
+ }
+
+ return 0;
+}
+
+static int FUNC(scan_header)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawScanHeader *current)
+{
+ int err, j;
+
+ HEADER("Scan");
+
+ u(16, Ls, 6, 6 + 2 * JPEG_MAX_COMPONENTS);
+
+ u(8, Ns, 1, 4);
+ for (j = 0; j < current->Ns; j++) {
+ us(8, Cs[j], j, 0, JPEG_MAX_COMPONENTS);
+ us(4, Td[j], j, 0, 3);
+ us(4, Ta[j], j, 0, 3);
+ }
+
+ u(8, Ss, 0, 63);
+ u(8, Se, 0, 63);
+ u(4, Ah, 0, 13);
+ u(4, Al, 0, 15);
+
+ return 0;
+}
+
+static int FUNC(application_data)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawApplicationData *current)
+{
+ int err, i;
+
+ HEADER("Application Data");
+
+ u(16, Lp, 2, 65535);
+
+ if (current->Lp > 2) {
+#ifdef READ
+ current->Ap_ref = av_buffer_alloc(current->Lp - 2);
+ if (!current->Ap_ref)
+ return AVERROR(ENOMEM);
+ current->Ap = current->Ap_ref->data;
+#endif
+
+ for (i = 0; i < current->Lp - 2; i++)
+ us(8, Ap[i], i, 0, 255);
+ }
+
+ return 0;
+}
+
+static int FUNC(comment)(CodedBitstreamContext *ctx, RWContext *rw,
+ JPEGRawComment *current)
+{
+ int err, i;
+
+ HEADER("Comment");
+
+ u(16, Lc, 2, 65535);
+
+ if (current->Lc > 2) {
+#ifdef READ
+ current->Cm_ref = av_buffer_alloc(current->Lc - 2);
+ if (!current->Cm_ref)
+ return AVERROR(ENOMEM);
+ current->Cm = current->Cm_ref->data;
+#endif
+
+ for (i = 0; i < current->Lc - 2; i++)
+ us(8, Cm[i], i, 0, 255);
+ }
+
+ return 0;
+}
Mark Thompson
2018-06-17 13:59:44 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
---
configure | 2 +
libavcodec/Makefile | 1 +
libavcodec/cbs.c | 6 +
libavcodec/cbs_internal.h | 1 +
libavcodec/cbs_jpeg.c | 513
++++++++++++++++++++++++++++++++++
libavcodec/cbs_jpeg.h | 128 +++++++++
libavcodec/cbs_jpeg_syntax_template.c | 191 +++++++++++++
7 files changed, 842 insertions(+)
create mode 100644 libavcodec/cbs_jpeg.c
create mode 100644 libavcodec/cbs_jpeg.h
create mode 100644 libavcodec/cbs_jpeg_syntax_template.c
...
diff --git a/libavcodec/cbs_jpeg.c b/libavcodec/cbs_jpeg.c
new file mode 100644
index 0000000000..365db73394
--- /dev/null
+++ b/libavcodec/cbs_jpeg.c
...
+static int cbs_jpeg_read_unit(CodedBitstreamContext *ctx,
+ CodedBitstreamUnit *unit)
+{
+ GetBitContext gbc;
+ int err;
+
+ err = init_get_bits(&gbc, unit->data, 8 * unit->data_size);
+ if (err < 0)
+ return err;
+
+ if (unit->type >= JPEG_MARKER_SOF0 &&
+ unit->type <= JPEG_MARKER_SOF3) {
+ err = ff_cbs_alloc_unit_content(ctx, unit,
+ sizeof(JPEGRawFrameHeader),
+ NULL);
+ if (err < 0)
+ return err;
+
+ err = cbs_jpeg_read_frame_header(ctx, &gbc, unit->content);
+ if (err < 0)
+ return err;
+
+ } else if (unit->type >= JPEG_MARKER_APPN &&
+ unit->type <= JPEG_MARKER_APPN + 15) {
+ err = ff_cbs_alloc_unit_content(ctx, unit,
+ sizeof(JPEGRawApplicationData),
+ &cbs_jpeg_free_application_data);
+ if (err < 0)
+ return err;
+
+ err = cbs_jpeg_read_application_data(ctx, &gbc, unit->content);
+ if (err < 0)
+ return err;
+
+ } else if (unit->type == JPEG_MARKER_SOS) {
+ JPEGRawScan *scan;
+ int pos;
+
+ err = ff_cbs_alloc_unit_content(ctx, unit,
+ sizeof(JPEGRawScan),
+ &cbs_jpeg_free_scan);
+ if (err < 0)
+ return err;
+ scan = unit->content;
+
+ err = cbs_jpeg_read_scan_header(ctx, &gbc, &scan->header);
+ if (err < 0)
+ return err;
+
+ pos = get_bits_count(&gbc);
+ av_assert0(pos % 8 == 0);
+ if (pos > 0) {
+ scan->data_size = unit->data_size - pos / 8;
+ scan->data_ref = av_buffer_ref(unit->data_ref);
+ if (!scan->data_ref)
+ return AVERROR(ENOMEM);
+ scan->data = unit->data + pos / 8;
+ }
+
+ } else {
+ switch (unit->type) {
+#define SEGMENT(marker, type, func) \
+ case JPEG_MARKER_ ## marker: \
+ { \
+ err = ff_cbs_alloc_unit_content(ctx, unit, \
+ sizeof(type), NULL); \
+ if (err < 0) \
+ return err; \
+ err = cbs_jpeg_read_ ## func(ctx, &gbc, unit->content); \
+ if (err < 0) \
+ return err; \
+ } \
+ break
+ SEGMENT(DQT, JPEGRawQuantisationTableSpecification, dqt);
+ SEGMENT(DHT, JPEGRawHuffmanTableSpecification, dht);
+ SEGMENT(COM, JPEGRawComment, comment);
A free function should be provided for JPEGRawComment as JPEGRawComment includes
a AVBufferRef pointer.
Yep, fixed.
Post by Xiang, Haihao
Post by Mark Thompson
+#undef SEGMENT
+ return AVERROR(ENOSYS);
+ }
+ }
+
+ return 0;
+}
...
Thanks,

- Mark
Mark Thompson
2018-06-07 23:43:28 UTC
Permalink
Set profile compatibility/constraint flags properly (including the
constraint flags used for RExt profiles, as all streams we can currently
generate are RExt-compatible), and use that to add support for the "Main
Intra" and "Main 10 Intra" RExt subprofiles (for which we can re-use the
existing Main and Main10 VAAPI profiles).
---
libavcodec/Makefile | 2 +-
libavcodec/vaapi_encode_h265.c | 71 +++++++++++++++++++++++++++++++++---------
2 files changed, 57 insertions(+), 16 deletions(-)

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 079f546918..71b800c09d 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -371,7 +371,7 @@ OBJS-$(CONFIG_HEVC_QSV_DECODER) += qsvdec_h2645.o
OBJS-$(CONFIG_HEVC_QSV_ENCODER) += qsvenc_hevc.o hevc_ps_enc.o \
hevc_data.o
OBJS-$(CONFIG_HEVC_RKMPP_DECODER) += rkmppdec.o
-OBJS-$(CONFIG_HEVC_VAAPI_ENCODER) += vaapi_encode_h265.o
+OBJS-$(CONFIG_HEVC_VAAPI_ENCODER) += h265_profile_level.o vaapi_encode_h265.o
OBJS-$(CONFIG_HEVC_V4L2M2M_DECODER) += v4l2_m2m_dec.o
OBJS-$(CONFIG_HEVC_V4L2M2M_ENCODER) += v4l2_m2m_enc.o
OBJS-$(CONFIG_HNM4_VIDEO_DECODER) += hnm4video.o
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 97bb9cef6c..2cee19be68 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -23,6 +23,7 @@

#include "libavutil/avassert.h"
#include "libavutil/common.h"
+#include "libavutil/pixdesc.h"
#include "libavutil/opt.h"
#include "libavutil/mastering_display_metadata.h"

@@ -260,9 +261,12 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
H265RawVPS *vps = &priv->raw_vps;
H265RawSPS *sps = &priv->raw_sps;
H265RawPPS *pps = &priv->raw_pps;
+ H265RawProfileTierLevel *ptl = &vps->profile_tier_level;
H265RawVUI *vui = &sps->vui;
VAEncSequenceParameterBufferHEVC *vseq = ctx->codec_sequence_params;
VAEncPictureParameterBufferHEVC *vpic = ctx->codec_picture_params;
+ const AVPixFmtDescriptor *desc;
+ int chroma_format, bit_depth;
int i;

memset(&priv->current_access_unit, 0,
@@ -273,6 +277,25 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
memset(pps, 0, sizeof(*pps));


+ desc = av_pix_fmt_desc_get(priv->common.input_frames->sw_format);
+ av_assert0(desc);
+ if (desc->nb_components == 1) {
+ chroma_format = 0;
+ } else {
+ if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 1) {
+ chroma_format = 1;
+ } else if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 0) {
+ chroma_format = 2;
+ } else if (desc->log2_chroma_w == 0 && desc->log2_chroma_h == 0) {
+ chroma_format = 3;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Chroma format of input pixel format "
+ "%s is not supported.\n", desc->name);
+ }
+ }
+ bit_depth = desc->comp[0].depth;
+
+
// VPS

vps->nal_unit_header = (H265RawNALUnitHeader) {
@@ -289,19 +312,35 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
vps->vps_max_sub_layers_minus1 = 0;
vps->vps_temporal_id_nesting_flag = 1;

- vps->profile_tier_level = (H265RawProfileTierLevel) {
- .general_profile_space = 0,
- .general_profile_idc = avctx->profile,
- .general_tier_flag = 0,
+ ptl->general_profile_space = 0;
+ ptl->general_profile_space = 0;
+ ptl->general_profile_idc = avctx->profile;
+ ptl->general_tier_flag = 0;

- .general_progressive_source_flag = 1,
- .general_interlaced_source_flag = 0,
- .general_non_packed_constraint_flag = 1,
- .general_frame_only_constraint_flag = 1,
+ if (chroma_format == 1) {
+ ptl->general_profile_compatibility_flag[1] = bit_depth == 8;
+ ptl->general_profile_compatibility_flag[2] = bit_depth <= 10;
+ }
+ ptl->general_profile_compatibility_flag[4] = 1;

- .general_level_idc = avctx->level,
- };
- vps->profile_tier_level.general_profile_compatibility_flag[avctx->profile & 31] = 1;
+ ptl->general_progressive_source_flag = 1;
+ ptl->general_interlaced_source_flag = 0;
+ ptl->general_non_packed_constraint_flag = 1;
+ ptl->general_frame_only_constraint_flag = 1;
+
+ ptl->general_max_12bit_constraint_flag = bit_depth <= 12;
+ ptl->general_max_10bit_constraint_flag = bit_depth <= 10;
+ ptl->general_max_8bit_constraint_flag = bit_depth == 8;
+
+ ptl->general_max_422chroma_constraint_flag = chroma_format <= 2;
+ ptl->general_max_420chroma_constraint_flag = chroma_format <= 1;
+ ptl->general_max_monochrome_constraint_flag = chroma_format == 0;
+
+ ptl->general_intra_constraint_flag = ctx->gop_size == 1;
+
+ ptl->general_lower_bit_rate_constraint_flag = 1;
+
+ ptl->general_level_idc = avctx->level;

vps->vps_sub_layer_ordering_info_present_flag = 0;
vps->vps_max_dec_pic_buffering_minus1[0] = (ctx->b_per_p > 0) + 1;
@@ -343,7 +382,7 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)

sps->sps_seq_parameter_set_id = 0;

- sps->chroma_format_idc = 1; // YUV 4:2:0.
+ sps->chroma_format_idc = chroma_format;
sps->separate_colour_plane_flag = 0;

sps->pic_width_in_luma_samples = ctx->surface_width;
@@ -362,9 +401,8 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
sps->conformance_window_flag = 0;
}

- sps->bit_depth_luma_minus8 =
- avctx->profile == FF_PROFILE_HEVC_MAIN_10 ? 2 : 0;
- sps->bit_depth_chroma_minus8 = sps->bit_depth_luma_minus8;
+ sps->bit_depth_luma_minus8 = bit_depth - 8;
+ sps->bit_depth_chroma_minus8 = bit_depth - 8;

sps->log2_max_pic_order_cnt_lsb_minus4 = 8;

@@ -1023,8 +1061,10 @@ static av_cold int vaapi_encode_h265_configure(AVCodecContext *avctx)

static const VAAPIEncodeProfile vaapi_encode_h265_profiles[] = {
{ FF_PROFILE_HEVC_MAIN, 8, 3, 1, 1, VAProfileHEVCMain },
+ { FF_PROFILE_HEVC_REXT, 8, 3, 1, 1, VAProfileHEVCMain },
#if VA_CHECK_VERSION(0, 37, 0)
{ FF_PROFILE_HEVC_MAIN_10, 10, 3, 1, 1, VAProfileHEVCMain10 },
+ { FF_PROFILE_HEVC_REXT, 10, 3, 1, 1, VAProfileHEVCMain10 },
#endif
{ FF_PROFILE_UNKNOWN }
};
@@ -1103,6 +1143,7 @@ static const AVOption vaapi_encode_h265_options[] = {
{ .i64 = value }, 0, 0, FLAGS, "profile"
{ PROFILE("main", FF_PROFILE_HEVC_MAIN) },
{ PROFILE("main10", FF_PROFILE_HEVC_MAIN_10) },
+ { PROFILE("rext", FF_PROFILE_HEVC_REXT) },
#undef PROFILE

{ "level", "Set level (general_level_idc)",
--
2.16.3
Xiang, Haihao
2018-06-15 03:59:27 UTC
Permalink
Post by Mark Thompson
Set profile compatibility/constraint flags properly (including the
constraint flags used for RExt profiles, as all streams we can currently
generate are RExt-compatible), and use that to add support for the "Main
Intra" and "Main 10 Intra" RExt subprofiles (for which we can re-use the
existing Main and Main10 VAAPI profiles).
---
libavcodec/Makefile | 2 +-
libavcodec/vaapi_encode_h265.c | 71 +++++++++++++++++++++++++++++++++------
---
2 files changed, 57 insertions(+), 16 deletions(-)
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 079f546918..71b800c09d 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -371,7 +371,7 @@ OBJS-$(CONFIG_HEVC_QSV_DECODER) += qsvdec_h2645.o
OBJS-$(CONFIG_HEVC_QSV_ENCODER) += qsvenc_hevc.o hevc_ps_enc.o \
hevc_data.o
OBJS-$(CONFIG_HEVC_RKMPP_DECODER) += rkmppdec.o
-OBJS-$(CONFIG_HEVC_VAAPI_ENCODER) += vaapi_encode_h265.o
+OBJS-$(CONFIG_HEVC_VAAPI_ENCODER) += h265_profile_level.o vaapi_encode_h265.o
OBJS-$(CONFIG_HEVC_V4L2M2M_DECODER) += v4l2_m2m_dec.o
OBJS-$(CONFIG_HEVC_V4L2M2M_ENCODER) += v4l2_m2m_enc.o
OBJS-$(CONFIG_HNM4_VIDEO_DECODER) += hnm4video.o
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 97bb9cef6c..2cee19be68 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -23,6 +23,7 @@
#include "libavutil/avassert.h"
#include "libavutil/common.h"
+#include "libavutil/pixdesc.h"
#include "libavutil/opt.h"
#include "libavutil/mastering_display_metadata.h"
@@ -260,9 +261,12 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
H265RawVPS *vps = &priv->raw_vps;
H265RawSPS *sps = &priv->raw_sps;
H265RawPPS *pps = &priv->raw_pps;
+ H265RawProfileTierLevel *ptl = &vps->profile_tier_level;
H265RawVUI *vui = &sps->vui;
VAEncSequenceParameterBufferHEVC *vseq = ctx->codec_sequence_params;
VAEncPictureParameterBufferHEVC *vpic = ctx->codec_picture_params;
+ const AVPixFmtDescriptor *desc;
+ int chroma_format, bit_depth;
int i;
memset(&priv->current_access_unit, 0,
@@ -273,6 +277,25 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
memset(pps, 0, sizeof(*pps));
+ desc = av_pix_fmt_desc_get(priv->common.input_frames->sw_format);
+ av_assert0(desc);
+ if (desc->nb_components == 1) {
+ chroma_format = 0;
+ } else {
+ if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 1) {
+ chroma_format = 1;
+ } else if (desc->log2_chroma_w == 1 && desc->log2_chroma_h == 0) {
+ chroma_format = 2;
+ } else if (desc->log2_chroma_w == 0 && desc->log2_chroma_h == 0) {
+ chroma_format = 3;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Chroma format of input pixel format "
+ "%s is not supported.\n", desc->name);
+ }
+ }
+ bit_depth = desc->comp[0].depth;
+
+
// VPS
vps->nal_unit_header = (H265RawNALUnitHeader) {
@@ -289,19 +312,35 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
vps->vps_max_sub_layers_minus1 = 0;
vps->vps_temporal_id_nesting_flag = 1;
- vps->profile_tier_level = (H265RawProfileTierLevel) {
- .general_profile_space = 0,
- .general_profile_idc = avctx->profile,
- .general_tier_flag = 0,
+ ptl->general_profile_space = 0;
+ ptl->general_profile_space = 0;
A duplicate line.
Post by Mark Thompson
+ ptl->general_profile_idc = avctx->profile;
+ ptl->general_tier_flag = 0;
- .general_progressive_source_flag = 1,
- .general_interlaced_source_flag = 0,
- .general_non_packed_constraint_flag = 1,
- .general_frame_only_constraint_flag = 1,
+ if (chroma_format == 1) {
+ ptl->general_profile_compatibility_flag[1] = bit_depth == 8;
+ ptl->general_profile_compatibility_flag[2] = bit_depth <= 10;
+ }
+ ptl->general_profile_compatibility_flag[4] = 1;
- .general_level_idc = avctx->level,
- };
- vps->profile_tier_level.general_profile_compatibility_flag[avctx->profile & 31] = 1;
+ ptl->general_progressive_source_flag = 1;
+ ptl->general_interlaced_source_flag = 0;
+ ptl->general_non_packed_constraint_flag = 1;
+ ptl->general_frame_only_constraint_flag = 1;
+
+ ptl->general_max_12bit_constraint_flag = bit_depth <= 12;
+ ptl->general_max_10bit_constraint_flag = bit_depth <= 10;
+ ptl->general_max_8bit_constraint_flag = bit_depth == 8;
+
+ ptl->general_max_422chroma_constraint_flag = chroma_format <= 2;
+ ptl->general_max_420chroma_constraint_flag = chroma_format <= 1;
+ ptl->general_max_monochrome_constraint_flag = chroma_format == 0;
+
+ ptl->general_intra_constraint_flag = ctx->gop_size == 1;
+
+ ptl->general_lower_bit_rate_constraint_flag = 1;
+
+ ptl->general_level_idc = avctx->level;
vps->vps_sub_layer_ordering_info_present_flag = 0;
vps->vps_max_dec_pic_buffering_minus1[0] = (ctx->b_per_p > 0) + 1;
@@ -343,7 +382,7 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
sps->sps_seq_parameter_set_id = 0;
- sps->chroma_format_idc = 1; // YUV 4:2:0.
+ sps->chroma_format_idc = chroma_format;
sps->separate_colour_plane_flag = 0;
sps->pic_width_in_luma_samples = ctx->surface_width;
@@ -362,9 +401,8 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
sps->conformance_window_flag = 0;
}
- sps->bit_depth_luma_minus8 =
- avctx->profile == FF_PROFILE_HEVC_MAIN_10 ? 2 : 0;
- sps->bit_depth_chroma_minus8 = sps->bit_depth_luma_minus8;
+ sps->bit_depth_luma_minus8 = bit_depth - 8;
+ sps->bit_depth_chroma_minus8 = bit_depth - 8;
sps->log2_max_pic_order_cnt_lsb_minus4 = 8;
@@ -1023,8 +1061,10 @@ static av_cold int
vaapi_encode_h265_configure(AVCodecContext *avctx)
static const VAAPIEncodeProfile vaapi_encode_h265_profiles[] = {
{ FF_PROFILE_HEVC_MAIN, 8, 3, 1, 1, VAProfileHEVCMain },
+ { FF_PROFILE_HEVC_REXT, 8, 3, 1, 1, VAProfileHEVCMain },
#if VA_CHECK_VERSION(0, 37, 0)
{ FF_PROFILE_HEVC_MAIN_10, 10, 3, 1, 1, VAProfileHEVCMain10 },
+ { FF_PROFILE_HEVC_REXT, 10, 3, 1, 1, VAProfileHEVCMain10 },
#endif
{ FF_PROFILE_UNKNOWN }
};
@@ -1103,6 +1143,7 @@ static const AVOption vaapi_encode_h265_options[] = {
{ .i64 = value }, 0, 0, FLAGS, "profile"
{ PROFILE("main", FF_PROFILE_HEVC_MAIN) },
{ PROFILE("main10", FF_PROFILE_HEVC_MAIN_10) },
+ { PROFILE("rext", FF_PROFILE_HEVC_REXT) },
#undef PROFILE
{ "level", "Set level (general_level_idc)",
Mark Thompson
2018-06-17 14:31:28 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
Set profile compatibility/constraint flags properly (including the
constraint flags used for RExt profiles, as all streams we can currently
generate are RExt-compatible), and use that to add support for the "Main
Intra" and "Main 10 Intra" RExt subprofiles (for which we can re-use the
existing Main and Main10 VAAPI profiles).
---
libavcodec/Makefile | 2 +-
libavcodec/vaapi_encode_h265.c | 71 +++++++++++++++++++++++++++++++++------
---
2 files changed, 57 insertions(+), 16 deletions(-)
...
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 97bb9cef6c..2cee19be68 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
...
@@ -289,19 +312,35 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
vps->vps_max_sub_layers_minus1 = 0;
vps->vps_temporal_id_nesting_flag = 1;
- vps->profile_tier_level = (H265RawProfileTierLevel) {
- .general_profile_space = 0,
- .general_profile_idc = avctx->profile,
- .general_tier_flag = 0,
+ ptl->general_profile_space = 0;
+ ptl->general_profile_space = 0;
A duplicate line.
Removed.
Post by Xiang, Haihao
Post by Mark Thompson
+ ptl->general_profile_idc = avctx->profile;
+ ptl->general_tier_flag = 0;
- .general_progressive_source_flag = 1,
- .general_interlaced_source_flag = 0,
- .general_non_packed_constraint_flag = 1,
- .general_frame_only_constraint_flag = 1,
+ if (chroma_format == 1) {
+ ptl->general_profile_compatibility_flag[1] = bit_depth == 8;
+ ptl->general_profile_compatibility_flag[2] = bit_depth <= 10;
+ }
+ ptl->general_profile_compatibility_flag[4] = 1;
- .general_level_idc = avctx->level,
- };
- vps->profile_tier_level.general_profile_compatibility_flag[avctx->profile & 31] = 1;
+ ptl->general_progressive_source_flag = 1;
+ ptl->general_interlaced_source_flag = 0;
+ ptl->general_non_packed_constraint_flag = 1;
+ ptl->general_frame_only_constraint_flag = 1;
+
+ ptl->general_max_12bit_constraint_flag = bit_depth <= 12;
+ ptl->general_max_10bit_constraint_flag = bit_depth <= 10;
+ ptl->general_max_8bit_constraint_flag = bit_depth == 8;
+
+ ptl->general_max_422chroma_constraint_flag = chroma_format <= 2;
+ ptl->general_max_420chroma_constraint_flag = chroma_format <= 1;
+ ptl->general_max_monochrome_constraint_flag = chroma_format == 0;
+
+ ptl->general_intra_constraint_flag = ctx->gop_size == 1;
+
+ ptl->general_lower_bit_rate_constraint_flag = 1;
+
+ ptl->general_level_idc = avctx->level;
vps->vps_sub_layer_ordering_info_present_flag = 0;
vps->vps_max_dec_pic_buffering_minus1[0] = (ctx->b_per_p > 0) + 1;
@@ -343,7 +382,7 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
sps->sps_seq_parameter_set_id = 0;
- sps->chroma_format_idc = 1; // YUV 4:2:0.
+ sps->chroma_format_idc = chroma_format;
sps->separate_colour_plane_flag = 0;
sps->pic_width_in_luma_samples = ctx->surface_width;
@@ -362,9 +401,8 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
sps->conformance_window_flag = 0;
}
- sps->bit_depth_luma_minus8 =
- avctx->profile == FF_PROFILE_HEVC_MAIN_10 ? 2 : 0;
- sps->bit_depth_chroma_minus8 = sps->bit_depth_luma_minus8;
+ sps->bit_depth_luma_minus8 = bit_depth - 8;
+ sps->bit_depth_chroma_minus8 = bit_depth - 8;
sps->log2_max_pic_order_cnt_lsb_minus4 = 8;
...
Thanks,

- Mark
Mark Thompson
2018-06-07 23:43:13 UTC
Permalink
Choose what types of reference frames will be used based on what types
are available, and make the intra-only mode explicit (GOP size one,
which must be used for MJPEG).
---
libavcodec/vaapi_encode.c | 83 +++++++++++++++++++++++++++--------------
libavcodec/vaapi_encode.h | 1 +
libavcodec/vaapi_encode_h264.c | 4 +-
libavcodec/vaapi_encode_h265.c | 4 +-
libavcodec/vaapi_encode_mjpeg.c | 1 +
libavcodec/vaapi_encode_mpeg2.c | 2 +-
libavcodec/vaapi_encode_vp8.c | 7 +---
libavcodec/vaapi_encode_vp9.c | 7 ++--
8 files changed, 68 insertions(+), 41 deletions(-)

diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 0e806a29e3..af9224c98f 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -680,7 +680,7 @@ static int vaapi_encode_get_next(AVCodecContext *avctx,
return AVERROR(ENOMEM);

if (ctx->input_order == 0 || ctx->force_idr ||
- ctx->gop_counter >= avctx->gop_size) {
+ ctx->gop_counter >= ctx->gop_size) {
pic->type = PICTURE_TYPE_IDR;
ctx->force_idr = 0;
ctx->gop_counter = 1;
@@ -703,7 +703,7 @@ static int vaapi_encode_get_next(AVCodecContext *avctx,
// encode-after it, but not exceeding the GOP size.

for (i = 0; i < ctx->b_per_p &&
- ctx->gop_counter < avctx->gop_size; i++) {
+ ctx->gop_counter < ctx->gop_size; i++) {
pic = vaapi_encode_alloc(avctx);
if (!pic)
goto fail;
@@ -1217,7 +1217,6 @@ static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
int i;

VAConfigAttrib attr[] = {
- { VAConfigAttribEncMaxRefFrames },
{ VAConfigAttribEncPackedHeaders },
};

@@ -1240,24 +1239,6 @@ static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
continue;
}
switch (attr[i].type) {
- case VAConfigAttribEncMaxRefFrames:
- {
- unsigned int ref_l0 = attr[i].value & 0xffff;
- unsigned int ref_l1 = (attr[i].value >> 16) & 0xffff;
-
- if (avctx->gop_size > 1 && ref_l0 < 1) {
- av_log(avctx, AV_LOG_ERROR, "P frames are not "
- "supported (%#x).\n", attr[i].value);
- return AVERROR(EINVAL);
- }
- if (avctx->max_b_frames > 0 && ref_l1 < 1) {
- av_log(avctx, AV_LOG_WARNING, "B frames are not "
- "supported (%#x) by the underlying driver.\n",
- attr[i].value);
- avctx->max_b_frames = 0;
- }
- }
- break;
case VAConfigAttribEncPackedHeaders:
if (ctx->va_packed_headers & ~attr[i].value) {
// This isn't fatal, but packed headers are always
@@ -1465,6 +1446,54 @@ static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
return 0;
}

+static av_cold int vaapi_encode_init_gop_structure(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAStatus vas;
+ VAConfigAttrib attr = { VAConfigAttribEncMaxRefFrames };
+ uint32_t ref_l0, ref_l1;
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile,
+ ctx->va_entrypoint,
+ &attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query reference frames "
+ "attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
+
+ if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ ref_l0 = ref_l1 = 0;
+ } else {
+ ref_l0 = attr.value & 0xffff;
+ ref_l1 = attr.value >> 16 & 0xffff;
+ }
+
+ if (avctx->gop_size <= 1) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using intra frames only.\n");
+ ctx->gop_size = 1;
+ } else if (ref_l0 < 1) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any "
+ "reference frames.\n");
+ return AVERROR(EINVAL);
+ } else if (ref_l1 < 1 || avctx->max_b_frames < 1) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using intra and P-frames "
+ "(supported references: %d / %d).\n", ref_l0, ref_l1);
+ ctx->gop_size = avctx->gop_size;
+ ctx->p_per_i = INT_MAX;
+ ctx->b_per_p = 0;
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Using intra, P- and B-frames "
+ "(supported references: %d / %d).\n", ref_l0, ref_l1);
+ ctx->gop_size = avctx->gop_size;
+ ctx->p_per_i = INT_MAX;
+ ctx->b_per_p = avctx->max_b_frames;
+ }
+
+ return 0;
+}
+
static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx)
{
#if VA_CHECK_VERSION(0, 36, 0)
@@ -1636,7 +1665,7 @@ static av_cold int vaapi_encode_create_recon_frames(AVCodecContext *avctx)
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;
+ ctx->recon_frames->initial_pool_size = 3 + 2 * ctx->b_per_p;

err = av_hwframe_ctx_init(ctx->recon_frames_ref);
if (err < 0) {
@@ -1691,6 +1720,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;

+ err = vaapi_encode_init_gop_structure(avctx);
+ if (err < 0)
+ goto fail;
+
err = vaapi_encode_config_attributes(avctx);
if (err < 0)
goto fail;
@@ -1745,14 +1778,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
}

ctx->input_order = 0;
- ctx->output_delay = avctx->max_b_frames;
+ ctx->output_delay = ctx->b_per_p;
ctx->decode_delay = 1;
ctx->output_order = - ctx->output_delay - 1;

- // Currently we never generate I frames, only IDR.
- ctx->p_per_i = INT_MAX;
- 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);
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index 6fac4781c3..bbec721bca 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -224,6 +224,7 @@ typedef struct VAAPIEncodeContext {
int64_t ts_ring[MAX_REORDER_DELAY * 3];

// Frame type decision.
+ int gop_size;
int p_per_i;
int b_per_p;
int force_idr;
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 87c0d9acf3..717be024ca 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -493,8 +493,8 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
*vseq = (VAEncSequenceParameterBufferH264) {
.seq_parameter_set_id = sps->seq_parameter_set_id,
.level_idc = sps->level_idc,
- .intra_period = avctx->gop_size,
- .intra_idr_period = avctx->gop_size,
+ .intra_period = ctx->gop_size,
+ .intra_idr_period = ctx->gop_size,
.ip_period = ctx->b_per_p + 1,

.bits_per_second = ctx->va_bit_rate,
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 13ddad79ae..b2d6b8d24d 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -509,8 +509,8 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
.general_level_idc = vps->profile_tier_level.general_level_idc,
.general_tier_flag = vps->profile_tier_level.general_tier_flag,

- .intra_period = avctx->gop_size,
- .intra_idr_period = avctx->gop_size,
+ .intra_period = ctx->gop_size,
+ .intra_idr_period = ctx->gop_size,
.ip_period = ctx->b_per_p + 1,
.bits_per_second = ctx->va_bit_rate,

diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index 67ac2fba96..4982cd166f 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -401,6 +401,7 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx)
static const AVCodecDefault vaapi_encode_mjpeg_defaults[] = {
{ "global_quality", "80" },
{ "b", "0" },
+ { "g", "1" },
{ NULL },
};

diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index db72516187..b6edca9158 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -355,7 +355,7 @@ static int vaapi_encode_mpeg2_init_sequence_params(AVCodecContext *avctx)


*vseq = (VAEncSequenceParameterBufferMPEG2) {
- .intra_period = avctx->gop_size,
+ .intra_period = ctx->gop_size,
.ip_period = ctx->b_per_p + 1,

.picture_width = avctx->width,
diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c
index db67136556..4512609a19 100644
--- a/libavcodec/vaapi_encode_vp8.c
+++ b/libavcodec/vaapi_encode_vp8.c
@@ -66,7 +66,7 @@ static int vaapi_encode_vp8_init_sequence_params(AVCodecContext *avctx)

if (!(ctx->va_rc_mode & VA_RC_CQP)) {
vseq->bits_per_second = ctx->va_bit_rate;
- vseq->intra_period = avctx->gop_size;
+ vseq->intra_period = ctx->gop_size;
}

return 0;
@@ -198,11 +198,6 @@ static av_cold int vaapi_encode_vp8_init(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;

- if (avctx->max_b_frames > 0) {
- av_log(avctx, AV_LOG_ERROR, "B-frames are not supported.\n");
- return AVERROR_PATCHWELCOME;
- }
-
ctx->codec = &vaapi_encode_type_vp8;

// Packed headers are not currently supported.
diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index 2b0658ec1f..d4069ec850 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -72,7 +72,7 @@ static int vaapi_encode_vp9_init_sequence_params(AVCodecContext *avctx)

if (!(ctx->va_rc_mode & VA_RC_CQP)) {
vseq->bits_per_second = ctx->va_bit_rate;
- vseq->intra_period = avctx->gop_size;
+ vseq->intra_period = ctx->gop_size;
}

vpic->frame_width_src = avctx->width;
@@ -86,6 +86,7 @@ static int vaapi_encode_vp9_init_sequence_params(AVCodecContext *avctx)
static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic)
{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
VAAPIEncodeVP9Context *priv = avctx->priv_data;
VAEncPictureParameterBufferVP9 *vpic = pic->codec_picture_params;
int i;
@@ -102,7 +103,7 @@ static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx,
break;
case PICTURE_TYPE_P:
av_assert0(pic->nb_refs == 1);
- if (avctx->max_b_frames > 0) {
+ if (ctx->b_per_p > 0) {
if (priv->last_ref_dir) {
vpic->ref_flags.bits.ref_frame_ctrl_l0 = 2;
vpic->ref_flags.bits.ref_gf_idx = 1;
@@ -174,7 +175,7 @@ static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx,
vpic->filter_level = priv->loop_filter_level;
vpic->sharpness_level = priv->loop_filter_sharpness;

- if (avctx->max_b_frames > 0 && pic->type == PICTURE_TYPE_P)
+ if (ctx->b_per_p > 0 && pic->type == PICTURE_TYPE_P)
priv->last_ref_dir = !priv->last_ref_dir;

return 0;
--
2.16.3
Xiang, Haihao
2018-06-14 06:48:57 UTC
Permalink
Post by Mark Thompson
Choose what types of reference frames will be used based on what types
are available, and make the intra-only mode explicit (GOP size one,
which must be used for MJPEG).
---
libavcodec/vaapi_encode.c | 83 +++++++++++++++++++++++++++-------------
-
libavcodec/vaapi_encode.h | 1 +
libavcodec/vaapi_encode_h264.c | 4 +-
libavcodec/vaapi_encode_h265.c | 4 +-
libavcodec/vaapi_encode_mjpeg.c | 1 +
libavcodec/vaapi_encode_mpeg2.c | 2 +-
libavcodec/vaapi_encode_vp8.c | 7 +---
libavcodec/vaapi_encode_vp9.c | 7 ++--
8 files changed, 68 insertions(+), 41 deletions(-)
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 0e806a29e3..af9224c98f 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -680,7 +680,7 @@ static int vaapi_encode_get_next(AVCodecContext *avctx,
return AVERROR(ENOMEM);
if (ctx->input_order == 0 || ctx->force_idr ||
- ctx->gop_counter >= avctx->gop_size) {
+ ctx->gop_counter >= ctx->gop_size) {
pic->type = PICTURE_TYPE_IDR;
ctx->force_idr = 0;
ctx->gop_counter = 1;
@@ -703,7 +703,7 @@ static int vaapi_encode_get_next(AVCodecContext *avctx,
// encode-after it, but not exceeding the GOP size.
for (i = 0; i < ctx->b_per_p &&
- ctx->gop_counter < avctx->gop_size; i++) {
+ ctx->gop_counter < ctx->gop_size; i++) {
pic = vaapi_encode_alloc(avctx);
if (!pic)
goto fail;
@@ -1217,7 +1217,6 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
int i;
VAConfigAttrib attr[] = {
- { VAConfigAttribEncMaxRefFrames },
{ VAConfigAttribEncPackedHeaders },
};
@@ -1240,24 +1239,6 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
continue;
}
switch (attr[i].type) {
- {
- unsigned int ref_l0 = attr[i].value & 0xffff;
- unsigned int ref_l1 = (attr[i].value >> 16) & 0xffff;
-
- if (avctx->gop_size > 1 && ref_l0 < 1) {
- av_log(avctx, AV_LOG_ERROR, "P frames are not "
- "supported (%#x).\n", attr[i].value);
- return AVERROR(EINVAL);
- }
- if (avctx->max_b_frames > 0 && ref_l1 < 1) {
- av_log(avctx, AV_LOG_WARNING, "B frames are not "
- "supported (%#x) by the underlying driver.\n",
- attr[i].value);
- avctx->max_b_frames = 0;
- }
- }
- break;
if (ctx->va_packed_headers & ~attr[i].value) {
// This isn't fatal, but packed headers are always
@@ -1465,6 +1446,54 @@ static av_cold int
vaapi_encode_init_rate_control(AVCodecContext *avctx)
return 0;
}
+static av_cold int vaapi_encode_init_gop_structure(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAStatus vas;
+ VAConfigAttrib attr = { VAConfigAttribEncMaxRefFrames };
+ uint32_t ref_l0, ref_l1;
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile,
+ ctx->va_entrypoint,
+ &attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query reference frames "
+ "attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
+
+ if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ ref_l0 = ref_l1 = 0;
+ } else {
+ ref_l0 = attr.value & 0xffff;
+ ref_l1 = attr.value >> 16 & 0xffff;
+ }
+
+ if (avctx->gop_size <= 1) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using intra frames only.\n");
+ ctx->gop_size = 1;
+ } else if (ref_l0 < 1) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any "
+ "reference frames.\n");
+ return AVERROR(EINVAL);
+ } else if (ref_l1 < 1 || avctx->max_b_frames < 1) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using intra and P-frames "
+ "(supported references: %d / %d).\n", ref_l0, ref_l1);
+ ctx->gop_size = avctx->gop_size;
+ ctx->p_per_i = INT_MAX;
How about removing p_per_i? I see p_per_i is used only in the following 'else
if' statement and I don't think ctx->p_counter can be INT_MAX in reality.

} else if (ctx->p_counter >= ctx->p_per_i) {...}
Post by Mark Thompson
+ ctx->b_per_p = 0;
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Using intra, P- and B-frames "
+ "(supported references: %d / %d).\n", ref_l0, ref_l1);
+ ctx->gop_size = avctx->gop_size;
+ ctx->p_per_i = INT_MAX;
+ ctx->b_per_p = avctx->max_b_frames;
+ }
+
+ return 0;
+}
+
static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx)
{
#if VA_CHECK_VERSION(0, 36, 0)
@@ -1636,7 +1665,7 @@ static av_cold int
vaapi_encode_create_recon_frames(AVCodecContext *avctx)
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;
+ ctx->recon_frames->initial_pool_size = 3 + 2 * ctx->b_per_p;
err = av_hwframe_ctx_init(ctx->recon_frames_ref);
if (err < 0) {
@@ -1691,6 +1720,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;
+ err = vaapi_encode_init_gop_structure(avctx);
+ if (err < 0)
+ goto fail;
+
err = vaapi_encode_config_attributes(avctx);
if (err < 0)
goto fail;
@@ -1745,14 +1778,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
}
ctx->input_order = 0;
- ctx->output_delay = avctx->max_b_frames;
+ ctx->output_delay = ctx->b_per_p;
ctx->decode_delay = 1;
ctx->output_order = - ctx->output_delay - 1;
- // Currently we never generate I frames, only IDR.
- ctx->p_per_i = INT_MAX;
- 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);
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index 6fac4781c3..bbec721bca 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -224,6 +224,7 @@ typedef struct VAAPIEncodeContext {
int64_t ts_ring[MAX_REORDER_DELAY * 3];
// Frame type decision.
+ int gop_size;
int p_per_i;
int b_per_p;
int force_idr;
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 87c0d9acf3..717be024ca 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -493,8 +493,8 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
*vseq = (VAEncSequenceParameterBufferH264) {
.seq_parameter_set_id = sps->seq_parameter_set_id,
.level_idc = sps->level_idc,
- .intra_period = avctx->gop_size,
- .intra_idr_period = avctx->gop_size,
+ .intra_period = ctx->gop_size,
+ .intra_idr_period = ctx->gop_size,
.ip_period = ctx->b_per_p + 1,
.bits_per_second = ctx->va_bit_rate,
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 13ddad79ae..b2d6b8d24d 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -509,8 +509,8 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
.general_level_idc = vps->profile_tier_level.general_level_idc,
.general_tier_flag = vps->profile_tier_level.general_tier_flag,
- .intra_period = avctx->gop_size,
- .intra_idr_period = avctx->gop_size,
+ .intra_period = ctx->gop_size,
+ .intra_idr_period = ctx->gop_size,
.ip_period = ctx->b_per_p + 1,
.bits_per_second = ctx->va_bit_rate,
diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index 67ac2fba96..4982cd166f 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -401,6 +401,7 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx)
static const AVCodecDefault vaapi_encode_mjpeg_defaults[] = {
{ "global_quality", "80" },
{ "b", "0" },
+ { "g", "1" },
{ NULL },
};
diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index db72516187..b6edca9158 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -355,7 +355,7 @@ static int
vaapi_encode_mpeg2_init_sequence_params(AVCodecContext *avctx)
*vseq = (VAEncSequenceParameterBufferMPEG2) {
- .intra_period = avctx->gop_size,
+ .intra_period = ctx->gop_size,
.ip_period = ctx->b_per_p + 1,
.picture_width = avctx->width,
diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c
index db67136556..4512609a19 100644
--- a/libavcodec/vaapi_encode_vp8.c
+++ b/libavcodec/vaapi_encode_vp8.c
@@ -66,7 +66,7 @@ static int
vaapi_encode_vp8_init_sequence_params(AVCodecContext *avctx)
if (!(ctx->va_rc_mode & VA_RC_CQP)) {
vseq->bits_per_second = ctx->va_bit_rate;
- vseq->intra_period = avctx->gop_size;
+ vseq->intra_period = ctx->gop_size;
}
return 0;
@@ -198,11 +198,6 @@ static av_cold int vaapi_encode_vp8_init(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- if (avctx->max_b_frames > 0) {
- av_log(avctx, AV_LOG_ERROR, "B-frames are not supported.\n");
- return AVERROR_PATCHWELCOME;
- }
-
ctx->codec = &vaapi_encode_type_vp8;
// Packed headers are not currently supported.
diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index 2b0658ec1f..d4069ec850 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -72,7 +72,7 @@ static int
vaapi_encode_vp9_init_sequence_params(AVCodecContext *avctx)
if (!(ctx->va_rc_mode & VA_RC_CQP)) {
vseq->bits_per_second = ctx->va_bit_rate;
- vseq->intra_period = avctx->gop_size;
+ vseq->intra_period = ctx->gop_size;
}
vpic->frame_width_src = avctx->width;
@@ -86,6 +86,7 @@ static int
vaapi_encode_vp9_init_sequence_params(AVCodecContext *avctx)
static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic)
{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
VAAPIEncodeVP9Context *priv = avctx->priv_data;
VAEncPictureParameterBufferVP9 *vpic = pic->codec_picture_params;
int i;
@@ -102,7 +103,7 @@ static int
vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx,
break;
av_assert0(pic->nb_refs == 1);
- if (avctx->max_b_frames > 0) {
+ if (ctx->b_per_p > 0) {
if (priv->last_ref_dir) {
vpic->ref_flags.bits.ref_frame_ctrl_l0 = 2;
vpic->ref_flags.bits.ref_gf_idx = 1;
@@ -174,7 +175,7 @@ static int
vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx,
vpic->filter_level = priv->loop_filter_level;
vpic->sharpness_level = priv->loop_filter_sharpness;
- if (avctx->max_b_frames > 0 && pic->type == PICTURE_TYPE_P)
+ if (ctx->b_per_p > 0 && pic->type == PICTURE_TYPE_P)
priv->last_ref_dir = !priv->last_ref_dir;
return 0;
Mark Thompson
2018-06-17 13:21:42 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
Choose what types of reference frames will be used based on what types
are available, and make the intra-only mode explicit (GOP size one,
which must be used for MJPEG).
---
libavcodec/vaapi_encode.c | 83 +++++++++++++++++++++++++++-------------
-
libavcodec/vaapi_encode.h | 1 +
libavcodec/vaapi_encode_h264.c | 4 +-
libavcodec/vaapi_encode_h265.c | 4 +-
libavcodec/vaapi_encode_mjpeg.c | 1 +
libavcodec/vaapi_encode_mpeg2.c | 2 +-
libavcodec/vaapi_encode_vp8.c | 7 +---
libavcodec/vaapi_encode_vp9.c | 7 ++--
8 files changed, 68 insertions(+), 41 deletions(-)
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 0e806a29e3..af9224c98f 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -680,7 +680,7 @@ static int vaapi_encode_get_next(AVCodecContext *avctx,
return AVERROR(ENOMEM);
if (ctx->input_order == 0 || ctx->force_idr ||
- ctx->gop_counter >= avctx->gop_size) {
+ ctx->gop_counter >= ctx->gop_size) {
pic->type = PICTURE_TYPE_IDR;
ctx->force_idr = 0;
ctx->gop_counter = 1;
@@ -703,7 +703,7 @@ static int vaapi_encode_get_next(AVCodecContext *avctx,
// encode-after it, but not exceeding the GOP size.
for (i = 0; i < ctx->b_per_p &&
- ctx->gop_counter < avctx->gop_size; i++) {
+ ctx->gop_counter < ctx->gop_size; i++) {
pic = vaapi_encode_alloc(avctx);
if (!pic)
goto fail;
@@ -1217,7 +1217,6 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
int i;
VAConfigAttrib attr[] = {
- { VAConfigAttribEncMaxRefFrames },
{ VAConfigAttribEncPackedHeaders },
};
@@ -1240,24 +1239,6 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
continue;
}
switch (attr[i].type) {
- {
- unsigned int ref_l0 = attr[i].value & 0xffff;
- unsigned int ref_l1 = (attr[i].value >> 16) & 0xffff;
-
- if (avctx->gop_size > 1 && ref_l0 < 1) {
- av_log(avctx, AV_LOG_ERROR, "P frames are not "
- "supported (%#x).\n", attr[i].value);
- return AVERROR(EINVAL);
- }
- if (avctx->max_b_frames > 0 && ref_l1 < 1) {
- av_log(avctx, AV_LOG_WARNING, "B frames are not "
- "supported (%#x) by the underlying driver.\n",
- attr[i].value);
- avctx->max_b_frames = 0;
- }
- }
- break;
if (ctx->va_packed_headers & ~attr[i].value) {
// This isn't fatal, but packed headers are always
@@ -1465,6 +1446,54 @@ static av_cold int
vaapi_encode_init_rate_control(AVCodecContext *avctx)
return 0;
}
+static av_cold int vaapi_encode_init_gop_structure(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAStatus vas;
+ VAConfigAttrib attr = { VAConfigAttribEncMaxRefFrames };
+ uint32_t ref_l0, ref_l1;
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile,
+ ctx->va_entrypoint,
+ &attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query reference frames "
+ "attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
+
+ if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ ref_l0 = ref_l1 = 0;
+ } else {
+ ref_l0 = attr.value & 0xffff;
+ ref_l1 = attr.value >> 16 & 0xffff;
+ }
+
+ if (avctx->gop_size <= 1) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using intra frames only.\n");
+ ctx->gop_size = 1;
+ } else if (ref_l0 < 1) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any "
+ "reference frames.\n");
+ return AVERROR(EINVAL);
+ } else if (ref_l1 < 1 || avctx->max_b_frames < 1) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using intra and P-frames "
+ "(supported references: %d / %d).\n", ref_l0, ref_l1);
+ ctx->gop_size = avctx->gop_size;
+ ctx->p_per_i = INT_MAX;
How about removing p_per_i? I see p_per_i is used only in the following 'else
if' statement and I don't think ctx->p_counter can be INT_MAX in reality.
} else if (ctx->p_counter >= ctx->p_per_i) {...}
It's not currently user-visible, but changing these values does work to test the open-GOP handling in the codec-specific code. (E.g. in H.264, I frames as recovery points rather than IDR frames.)

I do intend to expose this at some point.
Post by Xiang, Haihao
Post by Mark Thompson
+ ctx->b_per_p = 0;
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Using intra, P- and B-frames "
+ "(supported references: %d / %d).\n", ref_l0, ref_l1);
+ ctx->gop_size = avctx->gop_size;
+ ctx->p_per_i = INT_MAX;
+ ctx->b_per_p = avctx->max_b_frames;
+ }
+
+ return 0;
+}
+
static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx)
{
#if VA_CHECK_VERSION(0, 36, 0)
@@ -1636,7 +1665,7 @@ static av_cold int
vaapi_encode_create_recon_frames(AVCodecContext *avctx)
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;
+ ctx->recon_frames->initial_pool_size = 3 + 2 * ctx->b_per_p;
err = av_hwframe_ctx_init(ctx->recon_frames_ref);
if (err < 0) {
@@ -1691,6 +1720,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;
+ err = vaapi_encode_init_gop_structure(avctx);
+ if (err < 0)
+ goto fail;
+
err = vaapi_encode_config_attributes(avctx);
if (err < 0)
goto fail;
@@ -1745,14 +1778,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
}
ctx->input_order = 0;
- ctx->output_delay = avctx->max_b_frames;
+ ctx->output_delay = ctx->b_per_p;
ctx->decode_delay = 1;
ctx->output_order = - ctx->output_delay - 1;
- // Currently we never generate I frames, only IDR.
- ctx->p_per_i = INT_MAX;
- 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);
Thanks,

- Mark
Mark Thompson
2018-06-07 23:43:29 UTC
Permalink
Adds support for determining for level limits, including mapping PTL
blocks to profiles to check profile-dependent level limits.
---
libavcodec/h265_profile_level.c | 245 ++++++++++++++++++++++++++++++++++++++++
libavcodec/h265_profile_level.h | 89 +++++++++++++++
2 files changed, 334 insertions(+)
create mode 100644 libavcodec/h265_profile_level.c
create mode 100644 libavcodec/h265_profile_level.h

diff --git a/libavcodec/h265_profile_level.c b/libavcodec/h265_profile_level.c
new file mode 100644
index 0000000000..aac1529c9b
--- /dev/null
+++ b/libavcodec/h265_profile_level.c
@@ -0,0 +1,245 @@
+/*
+ * 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 "h265_profile_level.h"
+
+
+static const H265LevelDescriptor h265_levels[] = {
+ // Name CpbFactor-Main MaxSliceSegmentsPerPicture
+ // | level_idc | CpbFactor-High MaxLumaSr BrFactor-High
+ // | | MaxLumaPs | | | MaxTileRows | BrFactor-Main | MinCr-Main
+ // | | | | | | | MaxTileCols | | | MinCr-High
+ { "1", 30, 36864, 350, 0, 16, 1, 1, 552960, 128, 0, 2, 2 },
+ { "2", 60, 122880, 1500, 0, 16, 1, 1, 3686400, 1500, 0, 2, 2 },
+ { "2.1", 63, 245760, 3000, 0, 20, 1, 1, 7372800, 3000, 0, 2, 2 },
+ { "3", 90, 552960, 6000, 0, 30, 2, 2, 16588800, 6000, 0, 2, 2 },
+ { "3.1", 93, 983040, 10000, 0, 40, 3, 3, 33177600, 10000, 0, 2, 2 },
+ { "4", 120, 2228224, 12000, 30000, 75, 5, 5, 66846720, 12000, 30000, 4, 4 },
+ { "4.1", 123, 2228224, 20000, 50000, 75, 5, 5, 133693440, 20000, 50000, 4, 4 },
+ { "5", 150, 8912896, 25000, 100000, 200, 11, 10, 267386880, 25000, 100000, 6, 4 },
+ { "5.1", 153, 8912896, 40000, 160000, 200, 11, 10, 534773760, 40000, 160000, 8, 4 },
+ { "5.2", 156, 8912896, 60000, 240000, 200, 11, 10, 1069547520, 60000, 240000, 8, 4 },
+ { "6", 180, 35651584, 60000, 240000, 600, 22, 20, 1069547520, 60000, 240000, 8, 4 },
+ { "6.1", 183, 35651584, 120000, 480000, 600, 22, 20, 2139095040, 120000, 480000, 8, 4 },
+ { "6.2", 186, 35651584, 240000, 800000, 600, 22, 20, 4278190080, 240000, 800000, 6, 4 },
+};
+
+static const H265ProfileDescriptor h265_profiles[] = {
+ // profile_idc 8bit one-picture
+ // HT-profile | 422chroma | lower-bit-rate
+ // | 14bit | | 420chroma | | CpbVclFactor MinCrScaleFactor
+ // | | 12bit | | | monochrome| | CpbNalFactor |
+ // | | | 10bit | | | intra | | | FormatCapabilityFactor
+ { "Monochrome", // | | | | | | | | | |
+ 4, 0, 2, 1, 1, 1, 1, 1, 1, 0, 0, 1, 667, 733, 1.000, 1.0 },
+ { "Monochrome 12",
+ 4, 0, 2, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1000, 1100, 1.500, 1.0 },
+ { "Monochrome 16",
+ 4, 0, 2, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1333, 1467, 2.000, 1.0 },
+ { "Main",
+ 1, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1000, 1100, 1.500, 1.0 },
+ { "Screen-Extended Main",
+ 9, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1000, 1100, 1.500, 1.0 },
+ { "Main 10",
+ 2, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1000, 1100, 1.875, 1.0 },
+ { "Screen-Extended Main 10",
+ 9, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1000, 1100, 1.875, 1.0 },
+ { "Main 12",
+ 4, 0, 2, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1500, 1650, 2.250, 1.0 },
+ { "Main Still Picture",
+ 3, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1000, 1100, 1.500, 1.0 },
+ { "Main 4:2:2 10",
+ 4, 0, 2, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1667, 1833, 2.500, 0.5 },
+ { "Main 4:2:2 12",
+ 4, 0, 2, 1, 0, 0, 1, 0, 0, 0, 0, 1, 2000, 2200, 3.000, 0.5 },
+ { "Main 4:4:4",
+ 4, 0, 2, 1, 1, 1, 0, 0, 0, 0, 0, 1, 2000, 2200, 3.000, 0.5 },
+ { "High Throughput 4:4:4",
+ 5, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 2000, 2200, 3.000, 0.5 },
+ { "Screen-Extended Main 4:4:4",
+ 9, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 2000, 2200, 3.000, 0.5 },
+ { "Screen-Extended High Throughput 4:4:4",
+ 9, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 2000, 2200, 3.000, 0.5 },
+ { "Main 4:4:4 10",
+ 4, 0, 2, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2500, 2750, 3.750, 0.5 },
+ { "High Throughput 4:4:4 10",
+ 5, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2500, 2750, 3.750, 0.5 },
+ { "Screen-Extended Main 4:4:4 10",
+ 9, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2500, 2750, 3.750, 0.5 },
+ { "Screen-Extended High Throughput 4:4:4 10",
+ 9, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 2500, 2750, 3.750, 0.5 },
+ { "Main 4:4:4 12",
+ 4, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 1, 3000, 3300, 4.500, 0.5 },
+ { "High Throughput 4:4:4 14",
+ 5, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3500, 3850, 5.250, 0.5 },
+ { "Screen-Extended High Throughput 4:4:4 14",
+ 9, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3500, 3850, 5.250, 0.5 },
+ { "Main Intra",
+ 4, 0, 2, 1, 1, 1, 1, 1, 0, 1, 0, 2, 1000, 1100, 1.500, 1.0 },
+ { "Main 10 Intra",
+ 4, 0, 2, 1, 1, 0, 1, 1, 0, 1, 0, 2, 1000, 1100, 1.875, 1.0 },
+ { "Main 12 Intra",
+ 4, 0, 2, 1, 0, 0, 1, 1, 0, 1, 0, 2, 1500, 1650, 2.250, 1.0 },
+ { "Main 4:2:2 10 Intra",
+ 4, 0, 2, 1, 1, 0, 1, 0, 0, 1, 0, 2, 1667, 1833, 2.500, 0.5 },
+ { "Main 4:2:2 12 Intra",
+ 4, 0, 2, 1, 0, 0, 1, 0, 0, 1, 0, 2, 2000, 2200, 3.000, 0.5 },
+ { "Main 4:4:4 Intra",
+ 4, 0, 2, 1, 1, 1, 0, 0, 0, 1, 0, 2, 2000, 2200, 3.000, 0.5 },
+ { "Main 4:4:4 10 Intra",
+ 4, 0, 2, 1, 1, 0, 0, 0, 0, 1, 0, 2, 2500, 2750, 3.750, 0.5 },
+ { "Main 4:4:4 12 Intra",
+ 4, 0, 2, 1, 0, 0, 0, 0, 0, 1, 0, 2, 3000, 3300, 4.500, 0.5 },
+ { "Main 4:4:4 16 Intra",
+ 4, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 2, 4000, 4400, 6.000, 0.5 },
+ { "Main 4:4:4 Still Picture",
+ 4, 0, 2, 1, 1, 1, 0, 0, 0, 1, 1, 2, 2000, 2200, 3.000, 0.5 },
+ { "Main 4:4:4 16 Still Picture",
+ 4, 0, 2, 0, 0, 0, 0, 0, 0, 1, 1, 2, 4000, 4400, 6.000, 0.5 },
+ { "High Throughput 4:4:4 16 Intra",
+ 5, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 4000, 4400, 6.000, 0.5 },
+};
+
+
+const H265LevelDescriptor *ff_h265_get_level(int level_idc)
+{
+ int i;
+
+ for (i = 0; i < FF_ARRAY_ELEMS(h265_levels); i++) {
+ if (h265_levels[i].level_idc == level_idc)
+ return &h265_levels[i];
+ }
+
+ return NULL;
+}
+
+const H265ProfileDescriptor *ff_h265_get_profile(const H265RawProfileTierLevel *ptl)
+{
+ int i;
+
+ if (ptl->general_profile_space)
+ return NULL;
+
+ for (i = 0; i < FF_ARRAY_ELEMS(h265_profiles); i++) {
+ const H265ProfileDescriptor *profile = &h265_profiles[i];
+
+ if (ptl->general_profile_idc &&
+ ptl->general_profile_idc != profile->profile_idc)
+ continue;
+ if (!ptl->general_profile_compatibility_flag[profile->profile_idc])
+ continue;
+
+#define check_flag(name) \
+ if (profile->name < 2) { \
+ if (profile->name != ptl->general_ ## name ## _constraint_flag) \
+ continue; \
+ }
+ check_flag(max_14bit);
+ check_flag(max_12bit);
+ check_flag(max_10bit);
+ check_flag(max_8bit);
+ check_flag(max_422chroma);
+ check_flag(max_420chroma);
+ check_flag(max_monochrome);
+ check_flag(intra);
+ check_flag(one_picture_only);
+ check_flag(lower_bit_rate);
+#undef check_flag
+
+ return profile;
+ }
+
+ return NULL;
+}
+
+const H265LevelDescriptor *ff_h265_guess_level(const H265RawProfileTierLevel *ptl,
+ int64_t bitrate,
+ int width, int height,
+ int slice_segments,
+ int tile_rows, int tile_cols,
+ int max_dec_pic_buffering)
+{
+ const H265ProfileDescriptor *profile;
+ int pic_size, hbr_factor;
+ int i;
+
+ if (ptl)
+ profile = ff_h265_get_profile(ptl);
+ else
+ profile = NULL;
+ if (!profile) {
+ // Default to using multiplication factors for Main profile.
+ profile = &h265_profiles[3];
+ }
+
+ pic_size = width * height;
+
+ if (profile->profile_idc == 1 || profile->profile_idc == 2) {
+ hbr_factor = 1;
+ } else if (profile->high_throughput) {
+ if (profile->intra)
+ hbr_factor = 24 - 12 * ptl->general_lower_bit_rate_constraint_flag;
+ else
+ hbr_factor = 6;
+ } else {
+ hbr_factor = 2 - ptl->general_lower_bit_rate_constraint_flag;
+ }
+
+ for (i = 0; i < FF_ARRAY_ELEMS(h265_levels); i++) {
+ const H265LevelDescriptor *level = &h265_levels[i];
+ int max_br, max_dpb_size;
+
+ if (pic_size > level->max_luma_ps)
+ continue;
+ if (width * width > 8 * level->max_luma_ps)
+ continue;
+ if (height * height > 8 * level->max_luma_ps)
+ continue;
+
+ if (slice_segments > level->max_slice_segments_per_picture)
+ continue;
+ if (tile_rows > level->max_tile_rows)
+ continue;
+ if (tile_cols > level->max_tile_cols)
+ continue;
+
+ if (ptl->general_tier_flag)
+ max_br = level->max_br_high;
+ else
+ max_br = level->max_br_main;
+ if (!max_br)
+ continue;
+ if (bitrate > profile->cpb_nal_factor * hbr_factor * max_br)
+ continue;
+
+ if (pic_size < (level->max_luma_ps >> 2))
+ max_dpb_size = 16;
+ else if (pic_size < (level->max_luma_ps >> 1))
+ max_dpb_size = 14;
+ else if (pic_size < (3 * level->max_luma_ps >> 2))
+ max_dpb_size = 9;
+ else
+ max_dpb_size = 7;
+ if (max_dec_pic_buffering > max_dpb_size)
+ continue;
+
+ return level;
+ }
+
+ return NULL;
+}
diff --git a/libavcodec/h265_profile_level.h b/libavcodec/h265_profile_level.h
new file mode 100644
index 0000000000..12c00f0077
--- /dev/null
+++ b/libavcodec/h265_profile_level.h
@@ -0,0 +1,89 @@
+/*
+ * 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
+ */
+
+#ifndef AVCODEC_H265_PROFILE_LEVEL_H
+#define AVCODEC_H265_PROFILE_LEVEL_H
+
+#include <stdint.h>
+
+#include "cbs_h265.h"
+
+
+typedef struct H265LevelDescriptor {
+ const char *name;
+ uint8_t level_idc;
+
+ // Table A.6.
+ uint32_t max_luma_ps;
+ uint32_t max_cpb_main;
+ uint32_t max_cpb_high;
+ uint16_t max_slice_segments_per_picture;
+ uint8_t max_tile_rows;
+ uint8_t max_tile_cols;
+
+ // Table A.7.
+ uint32_t max_luma_sr;
+ uint32_t max_br_main;
+ uint32_t max_br_high;
+ uint8_t min_cr_base_main;
+ uint8_t min_cr_base_high;
+} H265LevelDescriptor;
+
+typedef struct H265ProfileDescriptor {
+ const char *name;
+ uint8_t profile_idc;
+ uint8_t high_throughput;
+
+ // Tables A.2, A.3 and A.5.
+ uint8_t max_14bit;
+ uint8_t max_12bit;
+ uint8_t max_10bit;
+ uint8_t max_8bit;
+ uint8_t max_422chroma;
+ uint8_t max_420chroma;
+ uint8_t max_monochrome;
+ uint8_t intra;
+ uint8_t one_picture_only;
+ uint8_t lower_bit_rate;
+
+ // Table A.8.
+ uint16_t cpb_vcl_factor;
+ uint16_t cpb_nal_factor;
+ float format_capability_factor;
+ float min_cr_scale_factor;
+} H265ProfileDescriptor;
+
+
+const H265LevelDescriptor *ff_h265_get_level(int level_idc);
+
+const H265ProfileDescriptor *ff_h265_get_profile(const H265RawProfileTierLevel *ptl);
+
+
+/**
+ * Guess the level of a stream from some parameters.
+ *
+ * Unknown parameters may be zero, in which case they are ignored.
+ */
+const H265LevelDescriptor *ff_h265_guess_level(const H265RawProfileTierLevel *ptl,
+ int64_t bitrate,
+ int width, int height,
+ int slice_segments,
+ int tile_rows, int tile_cols,
+ int max_dec_pic_buffering);
+
+#endif /* AVCODEC_H265_PROFILE_LEVEL_H */
--
2.16.3
Mark Thompson
2018-06-07 23:43:20 UTC
Permalink
Also adds greyscale, 4:2:2, 4:4:4 and RGB support.
---
configure | 2 +-
doc/encoders.texi | 17 +-
libavcodec/vaapi_encode_mjpeg.c | 529 +++++++++++++++++++++++++---------------
3 files changed, 347 insertions(+), 201 deletions(-)

diff --git a/configure b/configure
index d908283954..cde32a8fad 100755
--- a/configure
+++ b/configure
@@ -2939,7 +2939,7 @@ mjpeg_cuvid_decoder_deps="cuvid"
mjpeg_qsv_encoder_deps="libmfx"
mjpeg_qsv_encoder_select="qsvenc"
mjpeg_vaapi_encoder_deps="VAEncPictureParameterBufferJPEG"
-mjpeg_vaapi_encoder_select="vaapi_encode jpegtables"
+mjpeg_vaapi_encoder_select="cbs_jpeg jpegtables vaapi_encode"
mpeg1_cuvid_decoder_deps="cuvid"
mpeg1_v4l2m2m_decoder_deps="v4l2_m2m mpeg1_v4l2_m2m"
mpeg2_crystalhd_decoder_select="crystalhd"
diff --git a/doc/encoders.texi b/doc/encoders.texi
index b451142cfb..ceddfdda64 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -2662,8 +2662,21 @@ Include access unit delimiters in the stream (not included by default).
@end table

@item mjpeg_vaapi
-Always encodes using the standard quantisation and huffman tables -
-@option{global_quality} scales the standard quantisation table (range 1-100).
+Only baseline DCT encoding is supported. The encoder always uses the standard
+quantisation and huffman tables - @option{global_quality} scales the standard
+quantisation table (range 1-100).
+
+For YUV, 4:2:0, 4:2:2 and 4:4:4 subsampling modes are supported. RGB is also
+supported, and will create an RGB JPEG.
+
+@table @option
+@item jfif
+Include JFIF header in each frame (not included by default).
+@item huffman
+Include standard huffman tables (on by default). Turning this off will save
+a few hundred bytes in each output frame, but may lose compatibility with some
+JPEG decoders which don't fully handle MJPEG.
+@end table

@item mpeg2_vaapi
@option{profile} and @option{level} set the value of @emph{profile_and_level_indication}.
diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index f76645425a..2f79070e58 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -23,9 +23,12 @@
#include "libavutil/common.h"
#include "libavutil/internal.h"
#include "libavutil/opt.h"
-#include "libavutil/pixfmt.h"
+#include "libavutil/pixdesc.h"

#include "avcodec.h"
+#include "bytestream.h"
+#include "cbs.h"
+#include "cbs_jpeg.h"
#include "internal.h"
#include "jpegtables.h"
#include "mjpeg.h"
@@ -58,253 +61,346 @@ static const unsigned char vaapi_encode_mjpeg_quant_chrominance[64] = {
typedef struct VAAPIEncodeMJPEGContext {
VAAPIEncodeContext common;

+ // User options.
+ int jfif;
+ int huffman;
+
+ // Derived settings.
int quality;
- int component_subsample_h[3];
- int component_subsample_v[3];
+ uint8_t jfif_data[14];
+
+ // Writer structures.
+ JPEGRawFrameHeader frame_header;
+ JPEGRawScan scan;
+ JPEGRawApplicationData jfif_header;
+ JPEGRawQuantisationTableSpecification quant_tables;
+ JPEGRawHuffmanTableSpecification huffman_tables;

- VAQMatrixBufferJPEG quant_tables;
- VAHuffmanTableBufferJPEGBaseline huffman_tables;
+ CodedBitstreamContext *cbc;
+ CodedBitstreamFragment current_fragment;
} VAAPIEncodeMJPEGContext;

-static av_cold void vaapi_encode_mjpeg_copy_huffman(unsigned char *dst_lengths,
- unsigned char *dst_values,
- const unsigned char *src_lengths,
- const unsigned char *src_values)
+static int vaapi_encode_mjpeg_write_image_header(AVCodecContext *avctx,
+ VAAPIEncodePicture *pic,
+ VAAPIEncodeSlice *slice,
+ char *data, size_t *data_len)
{
- int i, mt;
-
- ++src_lengths;
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+ CodedBitstreamFragment *frag = &priv->current_fragment;
+ int err;
+
+ if (priv->jfif) {
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_APPN + 0,
+ &priv->jfif_header, NULL);
+ if (err < 0)
+ goto fail;
+ }

- mt = 0;
- for (i = 0; i < 16; i++)
- mt += (dst_lengths[i] = src_lengths[i]);
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_DQT,
+ &priv->quant_tables, NULL);
+ if (err < 0)
+ goto fail;
+
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_SOF0,
+ &priv->frame_header, NULL);
+ if (err < 0)
+ goto fail;
+
+ if (priv->huffman) {
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_DHT,
+ &priv->huffman_tables, NULL);
+ if (err < 0)
+ goto fail;
+ }

- for (i = 0; i < mt; i++)
- dst_values[i] = src_values[i];
-}
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_SOS,
+ &priv->scan, NULL);
+ if (err < 0)
+ goto fail;

-static av_cold void vaapi_encode_mjpeg_init_tables(AVCodecContext *avctx)
-{
- VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
- VAQMatrixBufferJPEG *quant = &priv->quant_tables;
- VAHuffmanTableBufferJPEGBaseline *huff = &priv->huffman_tables;
- int i;
-
- quant->load_lum_quantiser_matrix = 1;
- quant->load_chroma_quantiser_matrix = 1;
+ err = ff_cbs_write_fragment_data(priv->cbc, frag);
+ if (err < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to write image header.\n");
+ return err;
+ }

- for (i = 0; i < 64; i++) {
- quant->lum_quantiser_matrix[i] =
- vaapi_encode_mjpeg_quant_luminance[i];
- quant->chroma_quantiser_matrix[i] =
- vaapi_encode_mjpeg_quant_chrominance[i];
+ if (*data_len < 8 * frag->data_size) {
+ av_log(avctx, AV_LOG_ERROR, "Image header too large: "
+ "%zu < %zu.\n", *data_len, 8 * frag->data_size);
+ err = AVERROR(ENOSPC);
+ goto fail;
}

- huff->load_huffman_table[0] = 1;
- vaapi_encode_mjpeg_copy_huffman(huff->huffman_table[0].num_dc_codes,
- huff->huffman_table[0].dc_values,
- avpriv_mjpeg_bits_dc_luminance,
- avpriv_mjpeg_val_dc);
- vaapi_encode_mjpeg_copy_huffman(huff->huffman_table[0].num_ac_codes,
- huff->huffman_table[0].ac_values,
- avpriv_mjpeg_bits_ac_luminance,
- avpriv_mjpeg_val_ac_luminance);
- memset(huff->huffman_table[0].pad, 0, sizeof(huff->huffman_table[0].pad));
-
- huff->load_huffman_table[1] = 1;
- vaapi_encode_mjpeg_copy_huffman(huff->huffman_table[1].num_dc_codes,
- huff->huffman_table[1].dc_values,
- avpriv_mjpeg_bits_dc_chrominance,
- avpriv_mjpeg_val_dc);
- vaapi_encode_mjpeg_copy_huffman(huff->huffman_table[1].num_ac_codes,
- huff->huffman_table[1].ac_values,
- avpriv_mjpeg_bits_ac_chrominance,
- avpriv_mjpeg_val_ac_chrominance);
- memset(huff->huffman_table[1].pad, 0, sizeof(huff->huffman_table[1].pad));
-}
+ // Remove the EOI at the end of the fragment.
+ memcpy(data, frag->data, frag->data_size - 2);
+ *data_len = 8 * (frag->data_size - 2);

-static void vaapi_encode_mjpeg_write_marker(PutBitContext *pbc, int marker)
-{
- put_bits(pbc, 8, 0xff);
- put_bits(pbc, 8, marker);
+ err = 0;
+fail:
+ ff_cbs_fragment_uninit(priv->cbc, frag);
+ return err;
}

-static int vaapi_encode_mjpeg_write_image_header(AVCodecContext *avctx,
+static int vaapi_encode_mjpeg_write_extra_buffer(AVCodecContext *avctx,
VAAPIEncodePicture *pic,
- VAAPIEncodeSlice *slice,
+ int index, int *type,
char *data, size_t *data_len)
{
- VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
- VAEncPictureParameterBufferJPEG *vpic = pic->codec_picture_params;
- VAEncSliceParameterBufferJPEG *vslice = slice->codec_slice_params;
- PutBitContext pbc;
- int t, i, quant_scale;
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+ int t, i, k;

- init_put_bits(&pbc, data, *data_len);
+ if (index == 0) {
+ // Write quantisation tables.
+ JPEGRawFrameHeader *fh = &priv->frame_header;
+ JPEGRawQuantisationTableSpecification *dqt = &priv->quant_tables;
+ VAQMatrixBufferJPEG *quant;
+
+ if (*data_len < sizeof(*quant))
+ return AVERROR(ENOSPC);
+ *type = VAQMatrixBufferType;
+ *data_len = sizeof(*quant);
+
+ quant = (VAQMatrixBufferJPEG*)data;
+ memset(quant, 0, sizeof(*quant));
+
+ quant->load_lum_quantiser_matrix = 1;
+ for (i = 0; i < 64; i++)
+ quant->lum_quantiser_matrix[i] = dqt->table[fh->Tq[0]].Q[i];
+
+ if (fh->Nf > 1) {
+ quant->load_chroma_quantiser_matrix = 1;
+ for (i = 0; i < 64; i++)
+ quant->chroma_quantiser_matrix[i] =
+ dqt->table[fh->Tq[1]].Q[i];
+ }

- vaapi_encode_mjpeg_write_marker(&pbc, SOI);
+ } else if (index == 1) {
+ // Write huffman tables.
+ JPEGRawScanHeader *sh = &priv->scan.header;
+ JPEGRawHuffmanTableSpecification *dht = &priv->huffman_tables;
+ VAHuffmanTableBufferJPEGBaseline *huff;
+
+ if (*data_len < sizeof(*huff))
+ return AVERROR(ENOSPC);
+ *type = VAHuffmanTableBufferType;
+ *data_len = sizeof(*huff);
+
+ huff = (VAHuffmanTableBufferJPEGBaseline*)data;
+ memset(huff, 0, sizeof(*huff));
+
+ for (t = 0; t < 1 + (sh->Ns > 1); t++) {
+ const JPEGRawHuffmanTable *ht;
+
+ huff->load_huffman_table[t] = 1;
+
+ ht = &dht->table[2 * t];
+ for (i = k = 0; i < 16; i++)
+ k += (huff->huffman_table[t].num_dc_codes[i] = ht->L[i]);
+ av_assert0(k <= sizeof(huff->huffman_table[t].dc_values));
+ for (i = 0; i < k; i++)
+ huff->huffman_table[t].dc_values[i] = ht->V[i];
+
+ ht = &dht->table[2 * t + 1];
+ for (i = k = 0; i < 16; i++)
+ k += (huff->huffman_table[t].num_ac_codes[i] = ht->L[i]);
+ av_assert0(k <= sizeof(huff->huffman_table[t].ac_values));
+ for (i = 0; i < k; i++)
+ huff->huffman_table[t].ac_values[i] = ht->V[i];
+ }

- // Quantisation table coefficients are scaled for quality by the driver,
- // so we also need to do it ourselves here so that headers match.
- if (priv->quality < 50)
- quant_scale = 5000 / priv->quality;
+ } else {
+ return AVERROR_EOF;
+ }
+ return 0;
+}
+
+static int vaapi_encode_mjpeg_init_picture_params(AVCodecContext *avctx,
+ VAAPIEncodePicture *pic)
+{
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+ JPEGRawFrameHeader *fh = &priv->frame_header;
+ JPEGRawScanHeader *sh = &priv->scan.header;
+ VAEncPictureParameterBufferJPEG *vpic = pic->codec_picture_params;
+ const AVPixFmtDescriptor *desc;
+ const uint8_t *components;
+ int t, i, quant_scale, len;
+
+ desc = av_pix_fmt_desc_get(priv->common.input_frames->sw_format);
+ av_assert0(desc);
+ if (desc->flags & AV_PIX_FMT_FLAG_RGB)
+ components = (uint8_t[3]) { 'R', 'G', 'B' };
else
- quant_scale = 200 - 2 * priv->quality;
+ components = (uint8_t[3]) { 1, 2, 3 };

- for (t = 0; t < 2; t++) {
- int q;
+ // Frame header.

- vaapi_encode_mjpeg_write_marker(&pbc, DQT);
+ fh->P = 8;
+ fh->Y = avctx->height;
+ fh->X = avctx->width;
+ fh->Nf = desc->nb_components;

- put_bits(&pbc, 16, 3 + 64); // Lq
- put_bits(&pbc, 4, 0); // Pq
- put_bits(&pbc, 4, t); // Tq
+ for (i = 0; i < fh->Nf; i++) {
+ fh->C[i] = components[i];
+ fh->H[i] = 1 + (i == 0 ? desc->log2_chroma_w : 0);
+ fh->V[i] = 1 + (i == 0 ? desc->log2_chroma_h : 0);

- for (i = 0; i < 64; i++) {
- q = i[t ? priv->quant_tables.chroma_quantiser_matrix
- : priv->quant_tables.lum_quantiser_matrix];
- q = (q * quant_scale) / 100;
- if (q < 1) q = 1;
- if (q > 255) q = 255;
- put_bits(&pbc, 8, q);
- }
+ fh->Tq[i] = !!i;
}

- vaapi_encode_mjpeg_write_marker(&pbc, SOF0);
+ fh->Lf = 8 + 3 * fh->Nf;

- put_bits(&pbc, 16, 8 + 3 * vpic->num_components); // Lf
- put_bits(&pbc, 8, vpic->sample_bit_depth); // P
- put_bits(&pbc, 16, vpic->picture_height); // Y
- put_bits(&pbc, 16, vpic->picture_width); // X
- put_bits(&pbc, 8, vpic->num_components); // Nf
+ // JFIF header.
+ if (priv->jfif) {
+ JPEGRawApplicationData *app = &priv->jfif_header;
+ AVRational sar = pic->input_image->sample_aspect_ratio;
+ int sar_w, sar_h;
+ PutByteContext pbc;

- for (i = 0; i < vpic->num_components; i++) {
- put_bits(&pbc, 8, vpic->component_id[i]); // Ci
- put_bits(&pbc, 4, priv->component_subsample_h[i]); // Hi
- put_bits(&pbc, 4, priv->component_subsample_v[i]); // Vi
- put_bits(&pbc, 8, vpic->quantiser_table_selector[i]); // Tqi
- }
-
- for (t = 0; t < 4; t++) {
- int mt;
- unsigned char *lengths, *values;
+ bytestream2_init_writer(&pbc, priv->jfif_data,
+ sizeof(priv->jfif_data));

- vaapi_encode_mjpeg_write_marker(&pbc, DHT);
+ bytestream2_put_buffer(&pbc, "JFIF", 5);
+ bytestream2_put_be16(&pbc, 0x0102);
+ bytestream2_put_byte(&pbc, 0);

- if ((t & 1) == 0) {
- lengths = priv->huffman_tables.huffman_table[t / 2].num_dc_codes;
- values = priv->huffman_tables.huffman_table[t / 2].dc_values;
+ av_reduce(&sar_w, &sar_h, sar.num, sar.den, 65535);
+ if (sar_w && sar_h) {
+ bytestream2_put_be16(&pbc, sar_w);
+ bytestream2_put_be16(&pbc, sar_h);
} else {
- lengths = priv->huffman_tables.huffman_table[t / 2].num_ac_codes;
- values = priv->huffman_tables.huffman_table[t / 2].ac_values;
+ bytestream2_put_be16(&pbc, 1);
+ bytestream2_put_be16(&pbc, 1);
}

- mt = 0;
- for (i = 0; i < 16; i++)
- mt += lengths[i];
+ bytestream2_put_byte(&pbc, 0);
+ bytestream2_put_byte(&pbc, 0);

- put_bits(&pbc, 16, 2 + 17 + mt); // Lh
- put_bits(&pbc, 4, t & 1); // Tc
- put_bits(&pbc, 4, t / 2); // Th
+ av_assert0(bytestream2_get_bytes_left_p(&pbc) == 0);

- for (i = 0; i < 16; i++)
- put_bits(&pbc, 8, lengths[i]);
- for (i = 0; i < mt; i++)
- put_bits(&pbc, 8, values[i]);
+ app->Lp = 2 + sizeof(priv->jfif_data);
+ app->Ap = priv->jfif_data;
+ app->Ap_ref = NULL;
}

- vaapi_encode_mjpeg_write_marker(&pbc, SOS);
+ // Quantisation tables.

- av_assert0(vpic->num_components == vslice->num_components);
+ if (priv->quality < 50)
+ quant_scale = 5000 / priv->quality;
+ else
+ quant_scale = 200 - 2 * priv->quality;

- put_bits(&pbc, 16, 6 + 2 * vslice->num_components); // Ls
- put_bits(&pbc, 8, vslice->num_components); // Ns
+ len = 2;

- for (i = 0; i < vslice->num_components; i++) {
- put_bits(&pbc, 8, vslice->components[i].component_selector); // Csj
- put_bits(&pbc, 4, vslice->components[i].dc_table_selector); // Tdj
- put_bits(&pbc, 4, vslice->components[i].ac_table_selector); // Taj
- }
+ for (t = 0; t < 1 + (fh->Nf > 1); t++) {
+ JPEGRawQuantisationTable *quant = &priv->quant_tables.table[t];
+ const uint8_t *data = t == 0 ?
+ vaapi_encode_mjpeg_quant_luminance :
+ vaapi_encode_mjpeg_quant_chrominance;

- put_bits(&pbc, 8, 0); // Ss
- put_bits(&pbc, 8, 63); // Se
- put_bits(&pbc, 4, 0); // Ah
- put_bits(&pbc, 4, 0); // Al
+ quant->Pq = 0;
+ quant->Tq = t;
+ for (i = 0; i < 64; i++)
+ quant->Q[i] = av_clip(data[i] * quant_scale / 100, 1, 255);

- *data_len = put_bits_count(&pbc);
- flush_put_bits(&pbc);
+ len += 65;
+ }

- return 0;
-}
+ priv->quant_tables.Lq = len;
+
+ // Huffman tables.
+
+ len = 2;
+
+ for (t = 0; t < 2 + 2 * (fh->Nf > 1); t++) {
+ JPEGRawHuffmanTable *huff = &priv->huffman_tables.table[t];
+ const uint8_t *lengths, *values;
+ int k;
+
+ switch (t) {
+ case 0:
+ lengths = avpriv_mjpeg_bits_dc_luminance + 1;
+ values = avpriv_mjpeg_val_dc;
+ break;
+ case 1:
+ lengths = avpriv_mjpeg_bits_ac_luminance + 1;
+ values = avpriv_mjpeg_val_ac_luminance;
+ break;
+ case 2:
+ lengths = avpriv_mjpeg_bits_dc_chrominance + 1;
+ values = avpriv_mjpeg_val_dc;
+ break;
+ case 3:
+ lengths = avpriv_mjpeg_bits_ac_chrominance + 1;
+ values = avpriv_mjpeg_val_ac_chrominance;
+ break;
+ }

-static int vaapi_encode_mjpeg_write_extra_buffer(AVCodecContext *avctx,
- VAAPIEncodePicture *pic,
- int index, int *type,
- char *data, size_t *data_len)
-{
- VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+ huff->Tc = t % 2;
+ huff->Th = t / 2;

- if (index == 0) {
- // Write quantisation tables.
- if (*data_len < sizeof(priv->quant_tables))
- return AVERROR(EINVAL);
- *type = VAQMatrixBufferType;
- memcpy(data, &priv->quant_tables,
- *data_len = sizeof(priv->quant_tables));
+ for (i = k = 0; i < 16; i++)
+ k += (huff->L[i] = lengths[i]);

- } else if (index == 1) {
- // Write huffman tables.
- if (*data_len < sizeof(priv->huffman_tables))
- return AVERROR(EINVAL);
- *type = VAHuffmanTableBufferType;
- memcpy(data, &priv->huffman_tables,
- *data_len = sizeof(priv->huffman_tables));
+ for (i = 0; i < k; i++)
+ huff->V[i] = values[i];

- } else {
- return AVERROR_EOF;
+ len += 17 + k;
}
- return 0;
-}

-static int vaapi_encode_mjpeg_init_picture_params(AVCodecContext *avctx,
- VAAPIEncodePicture *pic)
-{
- VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
- VAEncPictureParameterBufferJPEG *vpic = pic->codec_picture_params;
+ priv->huffman_tables.Lh = len;
+
+ // Scan header.
+
+ sh->Ns = fh->Nf;
+
+ for (i = 0; i < fh->Nf; i++) {
+ sh->Cs[i] = fh->C[i];
+ sh->Td[i] = i > 0;
+ sh->Ta[i] = i > 0;
+ }

- vpic->reconstructed_picture = pic->recon_surface;
- vpic->coded_buf = pic->output_buffer;
+ sh->Ss = 0;
+ sh->Se = 63;
+ sh->Ah = 0;
+ sh->Al = 0;

- vpic->picture_width = avctx->width;
- vpic->picture_height = avctx->height;
+ sh->Ls = 6 + 2 * sh->Ns;

- vpic->pic_flags.bits.profile = 0;
- vpic->pic_flags.bits.progressive = 0;
- vpic->pic_flags.bits.huffman = 1;
- vpic->pic_flags.bits.interleaved = 0;
- vpic->pic_flags.bits.differential = 0;

- vpic->sample_bit_depth = 8;
- vpic->num_scan = 1;
+ *vpic = (VAEncPictureParameterBufferJPEG) {
+ .reconstructed_picture = pic->recon_surface,
+ .coded_buf = pic->output_buffer,

- vpic->num_components = 3;
+ .picture_width = fh->X,
+ .picture_height = fh->Y,

- vpic->component_id[0] = 1;
- vpic->component_id[1] = 2;
- vpic->component_id[2] = 3;
+ .pic_flags.bits = {
+ .profile = 0,
+ .progressive = 0,
+ .huffman = 1,
+ .interleaved = 0,
+ .differential = 0,
+ },

- priv->component_subsample_h[0] = 2;
- priv->component_subsample_v[0] = 2;
- priv->component_subsample_h[1] = 1;
- priv->component_subsample_v[1] = 1;
- priv->component_subsample_h[2] = 1;
- priv->component_subsample_v[2] = 1;
+ .sample_bit_depth = fh->P,
+ .num_scan = 1,
+ .num_components = fh->Nf,

- vpic->quantiser_table_selector[0] = 0;
- vpic->quantiser_table_selector[1] = 1;
- vpic->quantiser_table_selector[2] = 1;
+ // The driver modifies the provided quantisation tables according
+ // to this quality value; the middle value of 50 makes that the
+ // identity so that they are used unchanged.
+ .quality = 50,
+ };

- vpic->quality = priv->quality;
+ for (i = 0; i < fh->Nf; i++) {
+ vpic->component_id[i] = fh->C[i];
+ vpic->quantiser_table_selector[i] = fh->Tq[i];
+ }

pic->nb_slices = 1;

@@ -315,17 +411,20 @@ static int vaapi_encode_mjpeg_init_slice_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic,
VAAPIEncodeSlice *slice)
{
- VAEncPictureParameterBufferJPEG *vpic = pic->codec_picture_params;
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+ JPEGRawScanHeader *sh = &priv->scan.header;
VAEncSliceParameterBufferJPEG *vslice = slice->codec_slice_params;
int i;

- vslice->restart_interval = 0;
+ *vslice = (VAEncSliceParameterBufferJPEG) {
+ .restart_interval = 0,
+ .num_components = sh->Ns,
+ };

- vslice->num_components = vpic->num_components;
- for (i = 0; i < vslice->num_components; i++) {
- vslice->components[i].component_selector = i + 1;
- vslice->components[i].dc_table_selector = (i > 0);
- vslice->components[i].ac_table_selector = (i > 0);
+ for (i = 0; i < sh->Ns; i++) {
+ vslice->components[i].component_selector = sh->Cs[i];
+ vslice->components[i].dc_table_selector = sh->Td[i];
+ vslice->components[i].ac_table_selector = sh->Ta[i];
}

return 0;
@@ -335,6 +434,7 @@ static av_cold int vaapi_encode_mjpeg_configure(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+ int err;

priv->quality = avctx->global_quality;
if (priv->quality < 1 || priv->quality > 100) {
@@ -354,14 +454,22 @@ static av_cold int vaapi_encode_mjpeg_configure(AVCodecContext *avctx)
ctx->va_packed_headers |= VA_ENC_PACKED_HEADER_SLICE;
}

- vaapi_encode_mjpeg_init_tables(avctx);
+ err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_MJPEG, avctx);
+ if (err < 0)
+ return err;

return 0;
}

static const VAAPIEncodeProfile vaapi_encode_mjpeg_profiles[] = {
+ { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
+ 8, 1, 0, 0, VAProfileJPEGBaseline },
{ FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
8, 3, 1, 1, VAProfileJPEGBaseline },
+ { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
+ 8, 3, 1, 0, VAProfileJPEGBaseline },
+ { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
+ 8, 3, 0, 0, VAProfileJPEGBaseline },
{ FF_PROFILE_UNKNOWN }
};

@@ -398,6 +506,30 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx)
return ff_vaapi_encode_init(avctx);
}

+static av_cold int vaapi_encode_mjpeg_close(AVCodecContext *avctx)
+{
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+
+ ff_cbs_close(&priv->cbc);
+
+ return ff_vaapi_encode_close(avctx);
+}
+
+#define OFFSET(x) offsetof(VAAPIEncodeMJPEGContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
+static const AVOption vaapi_encode_mjpeg_options[] = {
+ VAAPI_ENCODE_COMMON_OPTIONS,
+
+ { "jfif", "Include JFIF header",
+ OFFSET(jfif), AV_OPT_TYPE_BOOL,
+ { .i64 = 0 }, 0, 1, FLAGS },
+ { "huffman", "Include huffman tables",
+ OFFSET(huffman), AV_OPT_TYPE_BOOL,
+ { .i64 = 1 }, 0, 1, FLAGS },
+
+ { NULL },
+};
+
static const AVCodecDefault vaapi_encode_mjpeg_defaults[] = {
{ "global_quality", "80" },
{ "b", "0" },
@@ -408,6 +540,7 @@ static const AVCodecDefault vaapi_encode_mjpeg_defaults[] = {
static const AVClass vaapi_encode_mjpeg_class = {
.class_name = "mjpeg_vaapi",
.item_name = av_default_item_name,
+ .option = vaapi_encode_mjpeg_options,
.version = LIBAVUTIL_VERSION_INT,
};

@@ -419,7 +552,7 @@ AVCodec ff_mjpeg_vaapi_encoder = {
.priv_data_size = sizeof(VAAPIEncodeMJPEGContext),
.init = &vaapi_encode_mjpeg_init,
.encode2 = &ff_vaapi_encode2,
- .close = &ff_vaapi_encode_close,
+ .close = &vaapi_encode_mjpeg_close,
.priv_class = &vaapi_encode_mjpeg_class,
.capabilities = AV_CODEC_CAP_HARDWARE,
.defaults = vaapi_encode_mjpeg_defaults,
--
2.16.3
Xiang, Haihao
2018-06-15 01:37:18 UTC
Permalink
Post by Mark Thompson
Also adds greyscale, 4:2:2, 4:4:4 and RGB support.
---
configure | 2 +-
doc/encoders.texi | 17 +-
libavcodec/vaapi_encode_mjpeg.c | 529 +++++++++++++++++++++++++------------
---
3 files changed, 347 insertions(+), 201 deletions(-)
diff --git a/configure b/configure
index d908283954..cde32a8fad 100755
--- a/configure
+++ b/configure
@@ -2939,7 +2939,7 @@ mjpeg_cuvid_decoder_deps="cuvid"
mjpeg_qsv_encoder_deps="libmfx"
mjpeg_qsv_encoder_select="qsvenc"
mjpeg_vaapi_encoder_deps="VAEncPictureParameterBufferJPEG"
-mjpeg_vaapi_encoder_select="vaapi_encode jpegtables"
+mjpeg_vaapi_encoder_select="cbs_jpeg jpegtables vaapi_encode"
mpeg1_cuvid_decoder_deps="cuvid"
mpeg1_v4l2m2m_decoder_deps="v4l2_m2m mpeg1_v4l2_m2m"
mpeg2_crystalhd_decoder_select="crystalhd"
diff --git a/doc/encoders.texi b/doc/encoders.texi
index b451142cfb..ceddfdda64 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -2662,8 +2662,21 @@ Include access unit delimiters in the stream (not
included by default).
@end table
@item mjpeg_vaapi
-Always encodes using the standard quantisation and huffman tables -
+Only baseline DCT encoding is supported. The encoder always uses the standard
+quantisation table (range 1-100).
+
+For YUV, 4:2:0, 4:2:2 and 4:4:4 subsampling modes are supported. RGB is also
+supported, and will create an RGB JPEG.
+
+Include JFIF header in each frame (not included by default).
+Include standard huffman tables (on by default). Turning this off will save
+a few hundred bytes in each output frame, but may lose compatibility with some
+JPEG decoders which don't fully handle MJPEG.
@item mpeg2_vaapi
@option{profile} and @option{level} set the value of
@emph{profile_and_level_indication}.
diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index f76645425a..2f79070e58 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -23,9 +23,12 @@
#include "libavutil/common.h"
#include "libavutil/internal.h"
#include "libavutil/opt.h"
-#include "libavutil/pixfmt.h"
+#include "libavutil/pixdesc.h"
#include "avcodec.h"
+#include "bytestream.h"
+#include "cbs.h"
+#include "cbs_jpeg.h"
#include "internal.h"
#include "jpegtables.h"
#include "mjpeg.h"
@@ -58,253 +61,346 @@ static const unsigned char
vaapi_encode_mjpeg_quant_chrominance[64] = {
typedef struct VAAPIEncodeMJPEGContext {
VAAPIEncodeContext common;
+ // User options.
+ int jfif;
+ int huffman;
+
+ // Derived settings.
int quality;
- int component_subsample_h[3];
- int component_subsample_v[3];
+ uint8_t jfif_data[14];
+
+ // Writer structures.
+ JPEGRawFrameHeader frame_header;
+ JPEGRawScan scan;
+ JPEGRawApplicationData jfif_header;
+ JPEGRawQuantisationTableSpecification quant_tables;
+ JPEGRawHuffmanTableSpecification huffman_tables;
- VAQMatrixBufferJPEG quant_tables;
- VAHuffmanTableBufferJPEGBaseline huffman_tables;
+ CodedBitstreamContext *cbc;
+ CodedBitstreamFragment current_fragment;
} VAAPIEncodeMJPEGContext;
-static av_cold void vaapi_encode_mjpeg_copy_huffman(unsigned char *dst_lengths,
- unsigned char *dst_values,
- const unsigned char *src_lengths,
- const unsigned char *src_values)
+static int vaapi_encode_mjpeg_write_image_header(AVCodecContext *avctx,
+ VAAPIEncodePicture *pic,
+ VAAPIEncodeSlice *slice,
+ char *data, size_t *data_len)
{
- int i, mt;
-
- ++src_lengths;
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+ CodedBitstreamFragment *frag = &priv->current_fragment;
+ int err;
+
+ if (priv->jfif) {
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_APPN + 0,
+ &priv->jfif_header, NULL);
+ if (err < 0)
+ goto fail;
+ }
- mt = 0;
- for (i = 0; i < 16; i++)
- mt += (dst_lengths[i] = src_lengths[i]);
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_DQT,
+ &priv->quant_tables, NULL);
+ if (err < 0)
+ goto fail;
+
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_SOF0,
+ &priv->frame_header, NULL);
+ if (err < 0)
+ goto fail;
+
+ if (priv->huffman) {
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_DHT,
+ &priv->huffman_tables, NULL);
+ if (err < 0)
+ goto fail;
+ }
- for (i = 0; i < mt; i++)
- dst_values[i] = src_values[i];
-}
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_SOS,
+ &priv->scan, NULL);
+ if (err < 0)
+ goto fail;
-static av_cold void vaapi_encode_mjpeg_init_tables(AVCodecContext *avctx)
-{
- VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
- VAQMatrixBufferJPEG *quant = &priv->quant_tables;
- VAHuffmanTableBufferJPEGBaseline *huff = &priv->huffman_tables;
- int i;
-
- quant->load_lum_quantiser_matrix = 1;
- quant->load_chroma_quantiser_matrix = 1;
+ err = ff_cbs_write_fragment_data(priv->cbc, frag);
+ if (err < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to write image header.\n");
+ return err;
Should it be 'goto fail' ? Some new units have been inserted to the fragment so
ff_cbs_fragment_uninit() should be called to release resources.
Post by Mark Thompson
+ }
- for (i = 0; i < 64; i++) {
- quant->lum_quantiser_matrix[i] =
- vaapi_encode_mjpeg_quant_luminance[i];
- quant->chroma_quantiser_matrix[i] =
- vaapi_encode_mjpeg_quant_chrominance[i];
+ if (*data_len < 8 * frag->data_size) {
+ av_log(avctx, AV_LOG_ERROR, "Image header too large: "
+ "%zu < %zu.\n", *data_len, 8 * frag->data_size);
+ err = AVERROR(ENOSPC);
+ goto fail;
Could you change the last parameter name of this function to bit_len or
bit_data_len? I think it is more readable and user don't need to think why frag-
Post by Mark Thompson
data_size is multiplied by 8.
}
- huff->load_huffman_table[0] = 1;
- vaapi_encode_mjpeg_copy_huffman(huff->huffman_table[0].num_dc_codes,
- huff->huffman_table[0].dc_values,
- avpriv_mjpeg_bits_dc_luminance,
- avpriv_mjpeg_val_dc);
- vaapi_encode_mjpeg_copy_huffman(huff->huffman_table[0].num_ac_codes,
- huff->huffman_table[0].ac_values,
- avpriv_mjpeg_bits_ac_luminance,
- avpriv_mjpeg_val_ac_luminance);
- memset(huff->huffman_table[0].pad, 0, sizeof(huff-
Post by Mark Thompson
huffman_table[0].pad));
-
- huff->load_huffman_table[1] = 1;
- vaapi_encode_mjpeg_copy_huffman(huff->huffman_table[1].num_dc_codes,
- huff->huffman_table[1].dc_values,
- avpriv_mjpeg_bits_dc_chrominance,
- avpriv_mjpeg_val_dc);
- vaapi_encode_mjpeg_copy_huffman(huff->huffman_table[1].num_ac_codes,
- huff->huffman_table[1].ac_values,
- avpriv_mjpeg_bits_ac_chrominance,
- avpriv_mjpeg_val_ac_chrominance);
- memset(huff->huffman_table[1].pad, 0, sizeof(huff-
Post by Mark Thompson
huffman_table[1].pad));
-}
+ // Remove the EOI at the end of the fragment.
+ memcpy(data, frag->data, frag->data_size - 2);
+ *data_len = 8 * (frag->data_size - 2);
-static void vaapi_encode_mjpeg_write_marker(PutBitContext *pbc, int marker)
-{
- put_bits(pbc, 8, 0xff);
- put_bits(pbc, 8, marker);
+ err = 0;
+ ff_cbs_fragment_uninit(priv->cbc, frag);
+ return err;
}
-static int vaapi_encode_mjpeg_write_image_header(AVCodecContext *avctx,
+static int vaapi_encode_mjpeg_write_extra_buffer(AVCodecContext *avctx,
VAAPIEncodePicture *pic,
- VAAPIEncodeSlice *slice,
+ int index, int *type,
char *data, size_t *data_len)
{
- VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
- VAEncPictureParameterBufferJPEG *vpic = pic->codec_picture_params;
- VAEncSliceParameterBufferJPEG *vslice = slice->codec_slice_params;
- PutBitContext pbc;
- int t, i, quant_scale;
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+ int t, i, k;
- init_put_bits(&pbc, data, *data_len);
+ if (index == 0) {
+ // Write quantisation tables.
+ JPEGRawFrameHeader *fh = &priv->frame_header;
+ JPEGRawQuantisationTableSpecification *dqt = &priv->quant_tables;
+ VAQMatrixBufferJPEG *quant;
+
+ if (*data_len < sizeof(*quant))
+ return AVERROR(ENOSPC);
+ *type = VAQMatrixBufferType;
+ *data_len = sizeof(*quant);
+
+ quant = (VAQMatrixBufferJPEG*)data;
+ memset(quant, 0, sizeof(*quant));
+
+ quant->load_lum_quantiser_matrix = 1;
+ for (i = 0; i < 64; i++)
+ quant->lum_quantiser_matrix[i] = dqt->table[fh->Tq[0]].Q[i];
+
+ if (fh->Nf > 1) {
+ quant->load_chroma_quantiser_matrix = 1;
+ for (i = 0; i < 64; i++)
+ quant->chroma_quantiser_matrix[i] =
+ dqt->table[fh->Tq[1]].Q[i];
+ }
- vaapi_encode_mjpeg_write_marker(&pbc, SOI);
+ } else if (index == 1) {
+ // Write huffman tables.
+ JPEGRawScanHeader *sh = &priv->scan.header;
+ JPEGRawHuffmanTableSpecification *dht = &priv->huffman_tables;
+ VAHuffmanTableBufferJPEGBaseline *huff;
+
+ if (*data_len < sizeof(*huff))
+ return AVERROR(ENOSPC);
+ *type = VAHuffmanTableBufferType;
+ *data_len = sizeof(*huff);
+
+ huff = (VAHuffmanTableBufferJPEGBaseline*)data;
+ memset(huff, 0, sizeof(*huff));
+
+ for (t = 0; t < 1 + (sh->Ns > 1); t++) {
+ const JPEGRawHuffmanTable *ht;
+
+ huff->load_huffman_table[t] = 1;
+
+ ht = &dht->table[2 * t];
+ for (i = k = 0; i < 16; i++)
+ k += (huff->huffman_table[t].num_dc_codes[i] = ht->L[i]);
+ av_assert0(k <= sizeof(huff->huffman_table[t].dc_values));
+ for (i = 0; i < k; i++)
+ huff->huffman_table[t].dc_values[i] = ht->V[i];
+
+ ht = &dht->table[2 * t + 1];
+ for (i = k = 0; i < 16; i++)
+ k += (huff->huffman_table[t].num_ac_codes[i] = ht->L[i]);
+ av_assert0(k <= sizeof(huff->huffman_table[t].ac_values));
+ for (i = 0; i < k; i++)
+ huff->huffman_table[t].ac_values[i] = ht->V[i];
+ }
- // Quantisation table coefficients are scaled for quality by the driver,
- // so we also need to do it ourselves here so that headers match.
- if (priv->quality < 50)
- quant_scale = 5000 / priv->quality;
+ } else {
+ return AVERROR_EOF;
+ }
+ return 0;
+}
+
+static int vaapi_encode_mjpeg_init_picture_params(AVCodecContext *avctx,
+ VAAPIEncodePicture *pic)
+{
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+ JPEGRawFrameHeader *fh = &priv->frame_header;
+ JPEGRawScanHeader *sh = &priv->scan.header;
+ VAEncPictureParameterBufferJPEG *vpic = pic->codec_picture_params;
+ const AVPixFmtDescriptor *desc;
+ const uint8_t *components;
+ int t, i, quant_scale, len;
+
+ desc = av_pix_fmt_desc_get(priv->common.input_frames->sw_format);
+ av_assert0(desc);
+ if (desc->flags & AV_PIX_FMT_FLAG_RGB)
+ components = (uint8_t[3]) { 'R', 'G', 'B' };
else
- quant_scale = 200 - 2 * priv->quality;
+ components = (uint8_t[3]) { 1, 2, 3 };
- for (t = 0; t < 2; t++) {
- int q;
+ // Frame header.
- vaapi_encode_mjpeg_write_marker(&pbc, DQT);
+ fh->P = 8;
+ fh->Y = avctx->height;
+ fh->X = avctx->width;
+ fh->Nf = desc->nb_components;
- put_bits(&pbc, 16, 3 + 64); // Lq
- put_bits(&pbc, 4, 0); // Pq
- put_bits(&pbc, 4, t); // Tq
+ for (i = 0; i < fh->Nf; i++) {
+ fh->C[i] = components[i];
+ fh->H[i] = 1 + (i == 0 ? desc->log2_chroma_w : 0);
+ fh->V[i] = 1 + (i == 0 ? desc->log2_chroma_h : 0);
- for (i = 0; i < 64; i++) {
- q = i[t ? priv->quant_tables.chroma_quantiser_matrix
- : priv->quant_tables.lum_quantiser_matrix];
- q = (q * quant_scale) / 100;
- if (q < 1) q = 1;
- if (q > 255) q = 255;
- put_bits(&pbc, 8, q);
- }
+ fh->Tq[i] = !!i;
}
- vaapi_encode_mjpeg_write_marker(&pbc, SOF0);
+ fh->Lf = 8 + 3 * fh->Nf;
- put_bits(&pbc, 16, 8 + 3 * vpic->num_components); // Lf
- put_bits(&pbc, 8, vpic->sample_bit_depth); // P
- put_bits(&pbc, 16, vpic->picture_height); // Y
- put_bits(&pbc, 16, vpic->picture_width); // X
- put_bits(&pbc, 8, vpic->num_components); // Nf
+ // JFIF header.
+ if (priv->jfif) {
+ JPEGRawApplicationData *app = &priv->jfif_header;
+ AVRational sar = pic->input_image->sample_aspect_ratio;
+ int sar_w, sar_h;
+ PutByteContext pbc;
- for (i = 0; i < vpic->num_components; i++) {
- put_bits(&pbc, 8, vpic->component_id[i]); // Ci
- put_bits(&pbc, 4, priv->component_subsample_h[i]); // Hi
- put_bits(&pbc, 4, priv->component_subsample_v[i]); // Vi
- put_bits(&pbc, 8, vpic->quantiser_table_selector[i]); // Tqi
- }
-
- for (t = 0; t < 4; t++) {
- int mt;
- unsigned char *lengths, *values;
+ bytestream2_init_writer(&pbc, priv->jfif_data,
+ sizeof(priv->jfif_data));
- vaapi_encode_mjpeg_write_marker(&pbc, DHT);
+ bytestream2_put_buffer(&pbc, "JFIF", 5);
+ bytestream2_put_be16(&pbc, 0x0102);
+ bytestream2_put_byte(&pbc, 0);
- if ((t & 1) == 0) {
- lengths = priv->huffman_tables.huffman_table[t / 2].num_dc_codes;
- values = priv->huffman_tables.huffman_table[t / 2].dc_values;
+ av_reduce(&sar_w, &sar_h, sar.num, sar.den, 65535);
+ if (sar_w && sar_h) {
+ bytestream2_put_be16(&pbc, sar_w);
+ bytestream2_put_be16(&pbc, sar_h);
} else {
- lengths = priv->huffman_tables.huffman_table[t / 2].num_ac_codes;
- values = priv->huffman_tables.huffman_table[t / 2].ac_values;
+ bytestream2_put_be16(&pbc, 1);
+ bytestream2_put_be16(&pbc, 1);
}
- mt = 0;
- for (i = 0; i < 16; i++)
- mt += lengths[i];
+ bytestream2_put_byte(&pbc, 0);
+ bytestream2_put_byte(&pbc, 0);
- put_bits(&pbc, 16, 2 + 17 + mt); // Lh
- put_bits(&pbc, 4, t & 1); // Tc
- put_bits(&pbc, 4, t / 2); // Th
+ av_assert0(bytestream2_get_bytes_left_p(&pbc) == 0);
- for (i = 0; i < 16; i++)
- put_bits(&pbc, 8, lengths[i]);
- for (i = 0; i < mt; i++)
- put_bits(&pbc, 8, values[i]);
+ app->Lp = 2 + sizeof(priv->jfif_data);
+ app->Ap = priv->jfif_data;
+ app->Ap_ref = NULL;
}
- vaapi_encode_mjpeg_write_marker(&pbc, SOS);
+ // Quantisation tables.
- av_assert0(vpic->num_components == vslice->num_components);
+ if (priv->quality < 50)
+ quant_scale = 5000 / priv->quality;
+ else
+ quant_scale = 200 - 2 * priv->quality;
- put_bits(&pbc, 16, 6 + 2 * vslice->num_components); // Ls
- put_bits(&pbc, 8, vslice->num_components); // Ns
+ len = 2;
- for (i = 0; i < vslice->num_components; i++) {
- put_bits(&pbc, 8, vslice->components[i].component_selector); // Csj
- put_bits(&pbc, 4, vslice->components[i].dc_table_selector); // Tdj
- put_bits(&pbc, 4, vslice->components[i].ac_table_selector); // Taj
- }
+ for (t = 0; t < 1 + (fh->Nf > 1); t++) {
+ JPEGRawQuantisationTable *quant = &priv->quant_tables.table[t];
+ const uint8_t *data = t == 0 ?
+ vaapi_encode_mjpeg_quant_chrominance;
- put_bits(&pbc, 8, 0); // Ss
- put_bits(&pbc, 8, 63); // Se
- put_bits(&pbc, 4, 0); // Ah
- put_bits(&pbc, 4, 0); // Al
+ quant->Pq = 0;
+ quant->Tq = t;
+ for (i = 0; i < 64; i++)
+ quant->Q[i] = av_clip(data[i] * quant_scale / 100, 1, 255);
- *data_len = put_bits_count(&pbc);
- flush_put_bits(&pbc);
+ len += 65;
+ }
- return 0;
-}
+ priv->quant_tables.Lq = len;
+
+ // Huffman tables.
+
+ len = 2;
+
+ for (t = 0; t < 2 + 2 * (fh->Nf > 1); t++) {
+ JPEGRawHuffmanTable *huff = &priv->huffman_tables.table[t];
+ const uint8_t *lengths, *values;
+ int k;
+
+ switch (t) {
+ lengths = avpriv_mjpeg_bits_dc_luminance + 1;
+ values = avpriv_mjpeg_val_dc;
+ break;
+ lengths = avpriv_mjpeg_bits_ac_luminance + 1;
+ values = avpriv_mjpeg_val_ac_luminance;
+ break;
+ lengths = avpriv_mjpeg_bits_dc_chrominance + 1;
+ values = avpriv_mjpeg_val_dc;
+ break;
+ lengths = avpriv_mjpeg_bits_ac_chrominance + 1;
+ values = avpriv_mjpeg_val_ac_chrominance;
+ break;
+ }
-static int vaapi_encode_mjpeg_write_extra_buffer(AVCodecContext *avctx,
- VAAPIEncodePicture *pic,
- int index, int *type,
- char *data, size_t *data_len)
-{
- VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+ huff->Tc = t % 2;
+ huff->Th = t / 2;
- if (index == 0) {
- // Write quantisation tables.
- if (*data_len < sizeof(priv->quant_tables))
- return AVERROR(EINVAL);
- *type = VAQMatrixBufferType;
- memcpy(data, &priv->quant_tables,
- *data_len = sizeof(priv->quant_tables));
+ for (i = k = 0; i < 16; i++)
+ k += (huff->L[i] = lengths[i]);
- } else if (index == 1) {
- // Write huffman tables.
- if (*data_len < sizeof(priv->huffman_tables))
- return AVERROR(EINVAL);
- *type = VAHuffmanTableBufferType;
- memcpy(data, &priv->huffman_tables,
- *data_len = sizeof(priv->huffman_tables));
+ for (i = 0; i < k; i++)
+ huff->V[i] = values[i];
- } else {
- return AVERROR_EOF;
+ len += 17 + k;
}
- return 0;
-}
-static int vaapi_encode_mjpeg_init_picture_params(AVCodecContext *avctx,
- VAAPIEncodePicture *pic)
-{
- VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
- VAEncPictureParameterBufferJPEG *vpic = pic->codec_picture_params;
+ priv->huffman_tables.Lh = len;
+
+ // Scan header.
+
+ sh->Ns = fh->Nf;
+
+ for (i = 0; i < fh->Nf; i++) {
+ sh->Cs[i] = fh->C[i];
+ sh->Td[i] = i > 0;
+ sh->Ta[i] = i > 0;
+ }
- vpic->reconstructed_picture = pic->recon_surface;
- vpic->coded_buf = pic->output_buffer;
+ sh->Ss = 0;
+ sh->Se = 63;
+ sh->Ah = 0;
+ sh->Al = 0;
- vpic->picture_width = avctx->width;
- vpic->picture_height = avctx->height;
+ sh->Ls = 6 + 2 * sh->Ns;
- vpic->pic_flags.bits.profile = 0;
- vpic->pic_flags.bits.progressive = 0;
- vpic->pic_flags.bits.huffman = 1;
- vpic->pic_flags.bits.interleaved = 0;
- vpic->pic_flags.bits.differential = 0;
- vpic->sample_bit_depth = 8;
- vpic->num_scan = 1;
+ *vpic = (VAEncPictureParameterBufferJPEG) {
+ .reconstructed_picture = pic->recon_surface,
+ .coded_buf = pic->output_buffer,
- vpic->num_components = 3;
+ .picture_width = fh->X,
+ .picture_height = fh->Y,
- vpic->component_id[0] = 1;
- vpic->component_id[1] = 2;
- vpic->component_id[2] = 3;
+ .pic_flags.bits = {
+ .profile = 0,
+ .progressive = 0,
+ .huffman = 1,
+ .interleaved = 0,
+ .differential = 0,
+ },
- priv->component_subsample_h[0] = 2;
- priv->component_subsample_v[0] = 2;
- priv->component_subsample_h[1] = 1;
- priv->component_subsample_v[1] = 1;
- priv->component_subsample_h[2] = 1;
- priv->component_subsample_v[2] = 1;
+ .sample_bit_depth = fh->P,
+ .num_scan = 1,
+ .num_components = fh->Nf,
- vpic->quantiser_table_selector[0] = 0;
- vpic->quantiser_table_selector[1] = 1;
- vpic->quantiser_table_selector[2] = 1;
+ // The driver modifies the provided quantisation tables according
+ // to this quality value; the middle value of 50 makes that the
+ // identity so that they are used unchanged.
+ .quality = 50,
+ };
- vpic->quality = priv->quality;
+ for (i = 0; i < fh->Nf; i++) {
+ vpic->component_id[i] = fh->C[i];
+ vpic->quantiser_table_selector[i] = fh->Tq[i];
+ }
pic->nb_slices = 1;
@@ -315,17 +411,20 @@ static int
vaapi_encode_mjpeg_init_slice_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic,
VAAPIEncodeSlice *slice)
{
- VAEncPictureParameterBufferJPEG *vpic = pic->codec_picture_params;
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+ JPEGRawScanHeader *sh = &priv->scan.header;
VAEncSliceParameterBufferJPEG *vslice = slice->codec_slice_params;
int i;
- vslice->restart_interval = 0;
+ *vslice = (VAEncSliceParameterBufferJPEG) {
+ .restart_interval = 0,
+ .num_components = sh->Ns,
+ };
- vslice->num_components = vpic->num_components;
- for (i = 0; i < vslice->num_components; i++) {
- vslice->components[i].component_selector = i + 1;
- vslice->components[i].dc_table_selector = (i > 0);
- vslice->components[i].ac_table_selector = (i > 0);
+ for (i = 0; i < sh->Ns; i++) {
+ vslice->components[i].component_selector = sh->Cs[i];
+ vslice->components[i].dc_table_selector = sh->Td[i];
+ vslice->components[i].ac_table_selector = sh->Ta[i];
}
return 0;
@@ -335,6 +434,7 @@ static av_cold int
vaapi_encode_mjpeg_configure(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+ int err;
priv->quality = avctx->global_quality;
if (priv->quality < 1 || priv->quality > 100) {
@@ -354,14 +454,22 @@ static av_cold int
vaapi_encode_mjpeg_configure(AVCodecContext *avctx)
ctx->va_packed_headers |= VA_ENC_PACKED_HEADER_SLICE;
}
- vaapi_encode_mjpeg_init_tables(avctx);
+ err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_MJPEG, avctx);
+ if (err < 0)
+ return err;
return 0;
}
static const VAAPIEncodeProfile vaapi_encode_mjpeg_profiles[] = {
+ { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
+ 8, 1, 0, 0, VAProfileJPEGBaseline },
{ FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
8, 3, 1, 1, VAProfileJPEGBaseline },
+ { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
+ 8, 3, 1, 0, VAProfileJPEGBaseline },
+ { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
+ 8, 3, 0, 0, VAProfileJPEGBaseline },
{ FF_PROFILE_UNKNOWN }
};
@@ -398,6 +506,30 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx)
return ff_vaapi_encode_init(avctx);
}
+static av_cold int vaapi_encode_mjpeg_close(AVCodecContext *avctx)
+{
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+
+ ff_cbs_close(&priv->cbc);
+
+ return ff_vaapi_encode_close(avctx);
+}
+
+#define OFFSET(x) offsetof(VAAPIEncodeMJPEGContext, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
+static const AVOption vaapi_encode_mjpeg_options[] = {
+ VAAPI_ENCODE_COMMON_OPTIONS,
+
+ { "jfif", "Include JFIF header",
+ OFFSET(jfif), AV_OPT_TYPE_BOOL,
+ { .i64 = 0 }, 0, 1, FLAGS },
+ { "huffman", "Include huffman tables",
+ OFFSET(huffman), AV_OPT_TYPE_BOOL,
+ { .i64 = 1 }, 0, 1, FLAGS },
+
+ { NULL },
+};
+
static const AVCodecDefault vaapi_encode_mjpeg_defaults[] = {
{ "global_quality", "80" },
{ "b", "0" },
@@ -408,6 +540,7 @@ static const AVCodecDefault vaapi_encode_mjpeg_defaults[] = {
static const AVClass vaapi_encode_mjpeg_class = {
.class_name = "mjpeg_vaapi",
.item_name = av_default_item_name,
+ .option = vaapi_encode_mjpeg_options,
.version = LIBAVUTIL_VERSION_INT,
};
@@ -419,7 +552,7 @@ AVCodec ff_mjpeg_vaapi_encoder = {
.priv_data_size = sizeof(VAAPIEncodeMJPEGContext),
.init = &vaapi_encode_mjpeg_init,
.encode2 = &ff_vaapi_encode2,
- .close = &ff_vaapi_encode_close,
+ .close = &vaapi_encode_mjpeg_close,
.priv_class = &vaapi_encode_mjpeg_class,
.capabilities = AV_CODEC_CAP_HARDWARE,
.defaults = vaapi_encode_mjpeg_defaults,
Mark Thompson
2018-06-17 14:11:48 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
Also adds greyscale, 4:2:2, 4:4:4 and RGB support.
---
configure | 2 +-
doc/encoders.texi | 17 +-
libavcodec/vaapi_encode_mjpeg.c | 529 +++++++++++++++++++++++++------------
---
3 files changed, 347 insertions(+), 201 deletions(-)
...
diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index f76645425a..2f79070e58 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
...
+static int vaapi_encode_mjpeg_write_image_header(AVCodecContext *avctx,
+ VAAPIEncodePicture *pic,
+ VAAPIEncodeSlice *slice,
+ char *data, size_t *data_len)
{
- int i, mt;
-
- ++src_lengths;
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
+ CodedBitstreamFragment *frag = &priv->current_fragment;
+ int err;
+
+ if (priv->jfif) {
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_APPN + 0,
+ &priv->jfif_header, NULL);
+ if (err < 0)
+ goto fail;
+ }
- mt = 0;
- for (i = 0; i < 16; i++)
- mt += (dst_lengths[i] = src_lengths[i]);
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_DQT,
+ &priv->quant_tables, NULL);
+ if (err < 0)
+ goto fail;
+
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_SOF0,
+ &priv->frame_header, NULL);
+ if (err < 0)
+ goto fail;
+
+ if (priv->huffman) {
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_DHT,
+ &priv->huffman_tables, NULL);
+ if (err < 0)
+ goto fail;
+ }
- for (i = 0; i < mt; i++)
- dst_values[i] = src_values[i];
-}
+ err = ff_cbs_insert_unit_content(priv->cbc, frag, -1,
+ JPEG_MARKER_SOS,
+ &priv->scan, NULL);
+ if (err < 0)
+ goto fail;
-static av_cold void vaapi_encode_mjpeg_init_tables(AVCodecContext *avctx)
-{
- VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
- VAQMatrixBufferJPEG *quant = &priv->quant_tables;
- VAHuffmanTableBufferJPEGBaseline *huff = &priv->huffman_tables;
- int i;
-
- quant->load_lum_quantiser_matrix = 1;
- quant->load_chroma_quantiser_matrix = 1;
+ err = ff_cbs_write_fragment_data(priv->cbc, frag);
+ if (err < 0) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to write image header.\n");
+ return err;
Should it be 'goto fail' ? Some new units have been inserted to the fragment so
ff_cbs_fragment_uninit() should be called to release resources.
Yep, fixed.
Post by Xiang, Haihao
Post by Mark Thompson
+ }
- for (i = 0; i < 64; i++) {
- quant->lum_quantiser_matrix[i] =
- vaapi_encode_mjpeg_quant_luminance[i];
- quant->chroma_quantiser_matrix[i] =
- vaapi_encode_mjpeg_quant_chrominance[i];
+ if (*data_len < 8 * frag->data_size) {
+ av_log(avctx, AV_LOG_ERROR, "Image header too large: "
+ "%zu < %zu.\n", *data_len, 8 * frag->data_size);
+ err = AVERROR(ENOSPC);
+ goto fail;
Could you change the last parameter name of this function to bit_len or
bit_data_len? I think it is more readable and user don't need to think why frag-
Post by Mark Thompson
data_size is multiplied by 8.
I don't think I agree? It matches the naming and style used in all of the other codecs in these write-header functions - compare for example <http://git.videolan.org/?p=ffmpeg.git;a=blob;f=libavcodec/vaapi_encode_h264.c;h=905c50760e2ba88f809b1e1a2e6059fce7134d53;hb=HEAD#l115>.
Post by Xiang, Haihao
Post by Mark Thompson
}
- huff->load_huffman_table[0] = 1;
- vaapi_encode_mjpeg_copy_huffman(huff->huffman_table[0].num_dc_codes,
- huff->huffman_table[0].dc_values,
- avpriv_mjpeg_bits_dc_luminance,
- avpriv_mjpeg_val_dc);
- vaapi_encode_mjpeg_copy_huffman(huff->huffman_table[0].num_ac_codes,
- huff->huffman_table[0].ac_values,
- avpriv_mjpeg_bits_ac_luminance,
- avpriv_mjpeg_val_ac_luminance);
- memset(huff->huffman_table[0].pad, 0, sizeof(huff-
Post by Mark Thompson
huffman_table[0].pad));
-
- huff->load_huffman_table[1] = 1;
- vaapi_encode_mjpeg_copy_huffman(huff->huffman_table[1].num_dc_codes,
- huff->huffman_table[1].dc_values,
- avpriv_mjpeg_bits_dc_chrominance,
- avpriv_mjpeg_val_dc);
- vaapi_encode_mjpeg_copy_huffman(huff->huffman_table[1].num_ac_codes,
- huff->huffman_table[1].ac_values,
- avpriv_mjpeg_bits_ac_chrominance,
- avpriv_mjpeg_val_ac_chrominance);
- memset(huff->huffman_table[1].pad, 0, sizeof(huff-
Post by Mark Thompson
huffman_table[1].pad));
-}
+ // Remove the EOI at the end of the fragment.
+ memcpy(data, frag->data, frag->data_size - 2);
+ *data_len = 8 * (frag->data_size - 2);
-static void vaapi_encode_mjpeg_write_marker(PutBitContext *pbc, int marker)
-{
- put_bits(pbc, 8, 0xff);
- put_bits(pbc, 8, marker);
+ err = 0;
+ ff_cbs_fragment_uninit(priv->cbc, frag);
+ return err;
}
...
Thanks,

- Mark
Mark Thompson
2018-06-07 23:43:22 UTC
Permalink
Including a unit test.
---
libavcodec/Makefile | 3 +-
libavcodec/h264_levels.c | 130 +++++++++++++++++++++++++++++
libavcodec/h264_levels.h | 53 ++++++++++++
libavcodec/tests/.gitignore | 1 +
libavcodec/tests/h264_levels.c | 183 +++++++++++++++++++++++++++++++++++++++++
tests/fate/libavcodec.mak | 5 ++
6 files changed, 374 insertions(+), 1 deletion(-)
create mode 100644 libavcodec/h264_levels.c
create mode 100644 libavcodec/h264_levels.h
create mode 100644 libavcodec/tests/h264_levels.c

diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 2a1e0de110..079f546918 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -353,7 +353,7 @@ OBJS-$(CONFIG_H264_OMX_ENCODER) += omx.o
OBJS-$(CONFIG_H264_QSV_DECODER) += qsvdec_h2645.o
OBJS-$(CONFIG_H264_QSV_ENCODER) += qsvenc_h264.o
OBJS-$(CONFIG_H264_RKMPP_DECODER) += rkmppdec.o
-OBJS-$(CONFIG_H264_VAAPI_ENCODER) += vaapi_encode_h264.o
+OBJS-$(CONFIG_H264_VAAPI_ENCODER) += h264_levels.o vaapi_encode_h264.o
OBJS-$(CONFIG_H264_VIDEOTOOLBOX_ENCODER) += videotoolboxenc.o
OBJS-$(CONFIG_H264_V4L2M2M_DECODER) += v4l2_m2m_dec.o
OBJS-$(CONFIG_H264_V4L2M2M_ENCODER) += v4l2_m2m_enc.o
@@ -1129,6 +1129,7 @@ TESTPROGS-$(CONFIG_IDCTDSP) += dct
TESTPROGS-$(CONFIG_IIRFILTER) += iirfilter
TESTPROGS-$(HAVE_MMX) += motion
TESTPROGS-$(CONFIG_MPEGVIDEO) += mpeg12framerate
+TESTPROGS-$(CONFIG_H264_VAAPI_ENCODER) += h264_levels
TESTPROGS-$(CONFIG_RANGECODER) += rangecoder
TESTPROGS-$(CONFIG_SNOW_ENCODER) += snowenc

diff --git a/libavcodec/h264_levels.c b/libavcodec/h264_levels.c
new file mode 100644
index 0000000000..6b4e18a914
--- /dev/null
+++ b/libavcodec/h264_levels.c
@@ -0,0 +1,130 @@
+/*
+ * 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 "avcodec.h"
+#include "h264_levels.h"
+
+// H.264 table A-1.
+static const H264LevelDescriptor h264_levels[] = {
+ // Name MaxMBPS MaxBR MinCR
+ // | level_idc | MaxFS | MaxCPB | MaxMvsPer2Mb
+ // | | cs3f | | MaxDpbMbs | | MaxVmvR | |
+ { "1", 10, 0, 1485, 99, 396, 64, 175, 64, 2, 0 },
+ { "1b", 10, 1, 1485, 99, 396, 128, 350, 64, 2, 0 },
+ { "1b", 9, 0, 1485, 99, 396, 128, 350, 64, 2, 0 },
+ { "1.1", 11, 0, 3000, 396, 900, 192, 500, 128, 2, 0 },
+ { "1.2", 12, 0, 6000, 396, 2376, 384, 1000, 128, 2, 0 },
+ { "1.3", 13, 0, 11880, 396, 2376, 768, 2000, 128, 2, 0 },
+ { "2", 20, 0, 11880, 396, 2376, 2000, 2000, 128, 2, 0 },
+ { "2.1", 21, 0, 19800, 792, 4752, 4000, 4000, 256, 2, 0 },
+ { "2.2", 22, 0, 20250, 1620, 8100, 4000, 4000, 256, 2, 0 },
+ { "3", 30, 0, 40500, 1620, 8100, 10000, 10000, 256, 2, 32 },
+ { "3.1", 31, 0, 108000, 3600, 18000, 14000, 14000, 512, 4, 16 },
+ { "3.2", 32, 0, 216000, 5120, 20480, 20000, 20000, 512, 4, 16 },
+ { "4", 40, 0, 245760, 8192, 32768, 20000, 25000, 512, 4, 16 },
+ { "4.1", 41, 0, 245760, 8192, 32768, 50000, 62500, 512, 2, 16 },
+ { "4.2", 42, 0, 522240, 8704, 34816, 50000, 62500, 512, 2, 16 },
+ { "5", 50, 0, 589824, 22080, 110400, 135000, 135000, 512, 2, 16 },
+ { "5.1", 51, 0, 983040, 36864, 184320, 240000, 240000, 512, 2, 16 },
+ { "5.2", 52, 0, 2073600, 36864, 184320, 240000, 240000, 512, 2, 16 },
+ { "6", 60, 0, 4177920, 139264, 696320, 240000, 240000, 8192, 2, 16 },
+ { "6.1", 61, 0, 8355840, 139264, 696320, 480000, 480000, 8192, 2, 16 },
+ { "6.2", 62, 0, 16711680, 139264, 696320, 800000, 800000, 8192, 2, 16 },
+};
+
+// H.264 table A-2 plus values from A-1.
+static const struct {
+ int profile_idc;
+ int cpb_br_vcl_factor;
+ int cpb_br_nal_factor;
+} h264_br_factors[] = {
+ { 66, 1000, 1200 },
+ { 77, 1000, 1200 },
+ { 88, 1000, 1200 },
+ { 100, 1250, 1500 },
+ { 110, 3000, 3600 },
+ { 122, 4000, 4800 },
+ { 244, 4000, 4800 },
+ { 44, 4000, 4800 },
+};
+
+// We are only ever interested in the NAL bitrate factor.
+static int h264_get_br_factor(int profile_idc)
+{
+ int i;
+ for (i = 0; i < FF_ARRAY_ELEMS(h264_br_factors); i++) {
+ if (h264_br_factors[i].profile_idc == profile_idc)
+ return h264_br_factors[i].cpb_br_nal_factor;
+ }
+ // Default to the non-high profile value if not specified.
+ return 1200;
+}
+
+const H264LevelDescriptor *ff_h264_get_level(int level_idc,
+ int constraint_set3_flag)
+{
+ int i;
+ for (i = 0; i < FF_ARRAY_ELEMS(h264_levels); i++) {
+ if (h264_levels[i].level_idc == level_idc &&
+ h264_levels[i].constraint_set3_flag == constraint_set3_flag)
+ return &h264_levels[i];
+ }
+ return NULL;
+}
+
+const H264LevelDescriptor *ff_h264_guess_level(int profile_idc,
+ int64_t bitrate,
+ int width, int height,
+ int max_dec_frame_buffering)
+{
+ int width_mbs = (width + 15) / 16;
+ int height_mbs = (height + 15) / 16;
+ int no_cs3f = !(profile_idc == 66 ||
+ profile_idc == 77 ||
+ profile_idc == 88);
+ int i;
+
+ for (i = 0; i < FF_ARRAY_ELEMS(h264_levels); i++) {
+ const H264LevelDescriptor *level = &h264_levels[i];
+
+ if (level->constraint_set3_flag && no_cs3f)
+ continue;
+
+ if (bitrate > level->max_br * h264_get_br_factor(profile_idc))
+ continue;
+
+ if (width_mbs * height_mbs > level->max_fs)
+ continue;
+ if (width_mbs * width_mbs > 8 * level->max_fs)
+ continue;
+ if (height_mbs * height_mbs > 8 * level->max_fs)
+ continue;
+
+ if (width_mbs && height_mbs) {
+ int max_dpb_frames =
+ FFMIN(level->max_dpb_mbs / (width_mbs * height_mbs), 16);
+ if (max_dec_frame_buffering > max_dpb_frames)
+ continue;
+ }
+
+ return level;
+ }
+
+ // No usable levels found - frame is too big or bitrate is too high.
+ return NULL;
+}
diff --git a/libavcodec/h264_levels.h b/libavcodec/h264_levels.h
new file mode 100644
index 0000000000..4189fc61c2
--- /dev/null
+++ b/libavcodec/h264_levels.h
@@ -0,0 +1,53 @@
+/*
+ * 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
+ */
+
+#ifndef AVCODEC_H264_LEVELS_H
+#define AVCODEC_H264_LEVELS_H
+
+
+#include <stdint.h>
+
+typedef struct H264LevelDescriptor {
+ const char *name;
+ uint8_t level_idc;
+ uint8_t constraint_set3_flag;
+ uint32_t max_mbps;
+ uint32_t max_fs;
+ uint32_t max_dpb_mbs;
+ uint32_t max_br;
+ uint32_t max_cpb;
+ uint16_t max_v_mv_r;
+ uint8_t min_cr;
+ uint8_t max_mvs_per_2mb;
+} H264LevelDescriptor;
+
+const H264LevelDescriptor *ff_h264_get_level(int level_idc,
+ int constraint_set3_flag);
+
+/**
+ * Guess the level of a stream from some parameters.
+ *
+ * Unknown parameters may be zero, in which case they are ignored.
+ */
+const H264LevelDescriptor *ff_h264_guess_level(int profile_idc,
+ int64_t bitrate,
+ int width, int height,
+ int max_dec_frame_buffering);
+
+
+#endif /* AVCODEC_H264_LEVELS_H */
diff --git a/libavcodec/tests/.gitignore b/libavcodec/tests/.gitignore
index 350fb2990c..73945a7c82 100644
--- a/libavcodec/tests/.gitignore
+++ b/libavcodec/tests/.gitignore
@@ -7,6 +7,7 @@
/fft-fixed
/fft-fixed32
/golomb
+/h264_levels
/htmlsubtitles
/iirfilter
/imgconvert
diff --git a/libavcodec/tests/h264_levels.c b/libavcodec/tests/h264_levels.c
new file mode 100644
index 0000000000..794517eb6c
--- /dev/null
+++ b/libavcodec/tests/h264_levels.c
@@ -0,0 +1,183 @@
+/*
+ * 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 "libavutil/common.h"
+#include "libavcodec/h264_levels.h"
+
+static const struct {
+ int width;
+ int height;
+ int level_idc;
+} test_sizes[] = {
+ // First level usable at some standard sizes.
+ // (From H.264 table A-6.)
+ { 176, 144, 10 }, // QCIF
+ { 352, 288, 11 }, // CIF
+ { 640, 480, 22 }, // VGA
+ { 720, 480, 22 }, // NTSC
+ { 720, 576, 22 }, // PAL
+ { 800, 600, 31 }, // SVGA
+ { 1280, 720, 31 }, // 720p
+ { 1280, 1024, 32 }, // SXGA
+ { 1920, 1080, 40 }, // 1080p
+ { 2048, 1080, 42 }, // 2Kx1080
+ { 2048, 1536, 50 }, // 4XGA
+ { 3840, 2160, 51 }, // 4K
+ { 7680, 4320, 60 }, // 8K
+
+ // Overly wide or tall sizes.
+ { 1, 256, 10 },
+ { 1, 512, 11 },
+ { 1, 1024, 21 },
+ { 1, 1808, 22 },
+ { 1, 1824, 31 },
+ { 256, 1, 10 },
+ { 512, 1, 11 },
+ { 1024, 1, 21 },
+ { 1808, 1, 22 },
+ { 1824, 1, 31 },
+ { 512, 4096, 40 },
+ { 256, 4112, 42 },
+ { 8688, 1024, 51 },
+ { 8704, 512, 60 },
+ { 16880, 1, 60 },
+ { 16896, 1, 0 },
+};
+
+static const struct {
+ int width;
+ int height;
+ int dpb_size;
+ int level_idc;
+} test_dpb[] = {
+ // First level usable for some DPB sizes.
+ // (From H.264 table A-7.)
+ { 176, 144, 4, 10 },
+ { 176, 144, 8, 11 },
+ { 176, 144, 16, 12 },
+ { 1280, 720, 1, 31 },
+ { 1280, 720, 5, 31 },
+ { 1280, 720, 9, 40 },
+ { 1280, 720, 10, 50 },
+ { 1920, 1080, 1, 40 },
+ { 1920, 1080, 5, 50 },
+ { 1920, 1080, 13, 50 },
+ { 1920, 1080, 14, 51 },
+ { 3840, 2160, 5, 51 },
+ { 3840, 2160, 6, 60 },
+ { 3840, 2160, 16, 60 },
+ { 7680, 4320, 5, 60 },
+ { 7680, 4320, 6, 0 },
+};
+
+static const struct {
+ int64_t bitrate;
+ int profile_idc;
+ int level_idc;
+} test_bitrate[] = {
+ // Values where profile affects level at a given bitrate.
+ { 2500000, 77, 21 },
+ { 2500000, 100, 20 },
+ { 2500000, 244, 13 },
+ { 100000000, 77, 50 },
+ { 100000000, 100, 50 },
+ { 100000000, 244, 41 },
+ { 999999999, 77, 0 },
+ { 999999999, 100, 62 },
+ // Check level 1b.
+ { 32 * 1200, 66, 10 },
+ { 32 * 1500, 100, 10 },
+ { 96 * 1200, 66, 10 },
+ { 96 * 1500, 100, 9 },
+ { 144 * 1200, 66, 11 },
+ { 144 * 1500, 100, 11 },
+};
+
+static const struct {
+ const char *name;
+ int profile_idc;
+ int64_t bitrate;
+ int width;
+ int height;
+ int dpb_frames;
+ int level_idc;
+} test_all[] = {
+ { "Bluray 1080p 40Mb/s", 100, 40000000, 1920, 1080, 4, 41 },
+ { "Bluray 1080p 24Mb/s", 100, 24000000, 1920, 1080, 4, 40 },
+ { "Bluray 720p 40Mb/s", 100, 40000000, 1280, 720, 6, 41 },
+ { "Bluray 720p 24Mb/s", 100, 24000000, 1280, 720, 6, 40 },
+ { "Bluray PAL 40Mb/s", 100, 40000000, 720, 576, 6, 41 },
+ { "Bluray PAL 24Mb/s", 100, 24000000, 720, 576, 6, 32 },
+ { "Bluray PAL 16Mb/s", 100, 16800000, 720, 576, 6, 31 },
+ { "Bluray PAL 12Mb/s", 100, 12000000, 720, 576, 5, 30 },
+ { "Bluray NTSC 40Mb/s", 100, 40000000, 720, 480, 6, 41 },
+ { "Bluray NTSC 24Mb/s", 100, 24000000, 720, 480, 6, 32 },
+ { "Bluray NTSC 16Mb/s", 100, 16800000, 720, 480, 6, 31 },
+ { "Bluray NTSC 12Mb/s", 100, 12000000, 720, 480, 6, 30 },
+};
+
+int main(void)
+{
+ const H264LevelDescriptor *level;
+ int i;
+
+#define CHECK(expected, format, ...) do { \
+ if (expected ? (!level || level->level_idc != expected) \
+ : !!level) { \
+ av_log(NULL, AV_LOG_ERROR, "Incorrect level for " \
+ format ": expected %d, got %d.\n", __VA_ARGS__, \
+ expected, level ? level->level_idc : -1); \
+ return 1; \
+ } \
+ } while (0)
+
+ for (i = 0; i < FF_ARRAY_ELEMS(test_sizes); i++) {
+ level = ff_h264_guess_level(0, 0, test_sizes[i].width,
+ test_sizes[i].height, 0);
+ CHECK(test_sizes[i].level_idc, "size %dx%d",
+ test_sizes[i].width, test_sizes[i].height);
+ }
+
+ for (i = 0; i < FF_ARRAY_ELEMS(test_dpb); i++) {
+ level = ff_h264_guess_level(0, 0, test_dpb[i].width,
+ test_dpb[i].height,
+ test_dpb[i].dpb_size);
+ CHECK(test_dpb[i].level_idc, "size %dx%d dpb %d",
+ test_dpb[i].width, test_dpb[i].height,
+ test_dpb[i].dpb_size);
+ }
+
+ for (i = 0; i < FF_ARRAY_ELEMS(test_bitrate); i++) {
+ level = ff_h264_guess_level(test_bitrate[i].profile_idc,
+ test_bitrate[i].bitrate,
+ 0, 0, 0);
+ CHECK(test_bitrate[i].level_idc, "bitrate %"PRId64" profile %d",
+ test_bitrate[i].bitrate, test_bitrate[i].profile_idc);
+ }
+
+ for (i = 0; i < FF_ARRAY_ELEMS(test_all); i++) {
+ level = ff_h264_guess_level(test_all[i].profile_idc,
+ test_all[i].bitrate,
+ test_all[i].width,
+ test_all[i].height,
+ test_all[i].dpb_frames);
+ CHECK(test_all[i].level_idc, "%s", test_all[i].name);
+ }
+
+ return 0;
+}
diff --git a/tests/fate/libavcodec.mak b/tests/fate/libavcodec.mak
index d3b2dd874e..aa4c36b112 100644
--- a/tests/fate/libavcodec.mak
+++ b/tests/fate/libavcodec.mak
@@ -46,6 +46,11 @@ fate-dct8x8: libavcodec/tests/dct$(EXESUF)
fate-dct8x8: CMD = run libavcodec/tests/dct
fate-dct8x8: CMP = null

+FATE_LIBAVCODEC-$(CONFIG_H264_VAAPI_ENCODER) += fate-h264-levels
+fate-h264-levels: libavcodec/tests/h264_levels$(EXESUF)
+fate-h264-levels: CMD = run libavcodec/tests/h264_levels
+fate-h264-levels: REF = /dev/null
+
FATE_LIBAVCODEC-$(CONFIG_IIRFILTER) += fate-iirfilter
fate-iirfilter: libavcodec/tests/iirfilter$(EXESUF)
fate-iirfilter: CMD = run libavcodec/tests/iirfilter
--
2.16.3
Mark Thompson
2018-06-07 23:43:21 UTC
Permalink
Give the entries in the VAAPI format map table an explicit type and add
functions to do the necessary lookups. Add another field to this table
indicating whether the chroma planes are swapped (as in YV12), and use
that rather than explicit comparisons where swapping is needed.
---
libavutil/hwcontext_vaapi.c | 133 +++++++++++++++++++++++++-------------------
1 file changed, 76 insertions(+), 57 deletions(-)

diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
index 4088a96be4..8624369bb9 100644
--- a/libavutil/hwcontext_vaapi.c
+++ b/libavutil/hwcontext_vaapi.c
@@ -87,57 +87,82 @@ typedef struct VAAPIMapping {
int flags;
} VAAPIMapping;

-#define MAP(va, rt, av) { \
+typedef struct VAAPIFormat {
+ unsigned int fourcc;
+ unsigned int rt_format;
+ enum AVPixelFormat pix_fmt;
+ int chroma_planes_swapped;
+} VAAPIFormatDescriptor;
+
+#define MAP(va, rt, av, swap_uv) { \
VA_FOURCC_ ## va, \
VA_RT_FORMAT_ ## rt, \
- AV_PIX_FMT_ ## av \
+ AV_PIX_FMT_ ## av, \
+ swap_uv, \
}
// The map fourcc <-> pix_fmt isn't bijective because of the annoying U/V
// plane swap cases. The frame handling below tries to hide these.
-static const struct {
- unsigned int fourcc;
- unsigned int rt_format;
- enum AVPixelFormat pix_fmt;
-} vaapi_format_map[] = {
- MAP(NV12, YUV420, NV12),
- MAP(YV12, YUV420, YUV420P), // With U/V planes swapped.
- MAP(IYUV, YUV420, YUV420P),
+static const VAAPIFormatDescriptor vaapi_format_map[] = {
+ MAP(NV12, YUV420, NV12, 0),
#ifdef VA_FOURCC_I420
- MAP(I420, YUV420, YUV420P),
+ MAP(I420, YUV420, YUV420P, 0),
#endif
+ MAP(YV12, YUV420, YUV420P, 1),
+ MAP(IYUV, YUV420, YUV420P, 0),
+ MAP(422H, YUV422, YUV422P, 0),
#ifdef VA_FOURCC_YV16
- MAP(YV16, YUV422, YUV422P), // With U/V planes swapped.
+ MAP(YV16, YUV422, YUV422P, 1),
#endif
- MAP(422H, YUV422, YUV422P),
- MAP(UYVY, YUV422, UYVY422),
- MAP(YUY2, YUV422, YUYV422),
- MAP(411P, YUV411, YUV411P),
- MAP(422V, YUV422, YUV440P),
- MAP(444P, YUV444, YUV444P),
- MAP(Y800, YUV400, GRAY8),
+ MAP(UYVY, YUV422, UYVY422, 0),
+ MAP(YUY2, YUV422, YUYV422, 0),
+ MAP(411P, YUV411, YUV411P, 0),
+ MAP(422V, YUV422, YUV440P, 0),
+ MAP(444P, YUV444, YUV444P, 0),
+ MAP(Y800, YUV400, GRAY8, 0),
#ifdef VA_FOURCC_P010
- MAP(P010, YUV420_10BPP, P010),
+ MAP(P010, YUV420_10BPP, P010, 0),
#endif
- MAP(BGRA, RGB32, BGRA),
- MAP(BGRX, RGB32, BGR0),
- MAP(RGBA, RGB32, RGBA),
- MAP(RGBX, RGB32, RGB0),
+ MAP(BGRA, RGB32, BGRA, 0),
+ MAP(BGRX, RGB32, BGR0, 0),
+ MAP(RGBA, RGB32, RGBA, 0),
+ MAP(RGBX, RGB32, RGB0, 0),
#ifdef VA_FOURCC_ABGR
- MAP(ABGR, RGB32, ABGR),
- MAP(XBGR, RGB32, 0BGR),
+ MAP(ABGR, RGB32, ABGR, 0),
+ MAP(XBGR, RGB32, 0BGR, 0),
#endif
- MAP(ARGB, RGB32, ARGB),
- MAP(XRGB, RGB32, 0RGB),
+ MAP(ARGB, RGB32, ARGB, 0),
+ MAP(XRGB, RGB32, 0RGB, 0),
};
#undef MAP

-static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
+static const VAAPIFormatDescriptor *
+ vaapi_format_from_fourcc(unsigned int fourcc)
{
int i;
for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
if (vaapi_format_map[i].fourcc == fourcc)
- return vaapi_format_map[i].pix_fmt;
- return AV_PIX_FMT_NONE;
+ return &vaapi_format_map[i];
+ return NULL;
+}
+
+static const VAAPIFormatDescriptor *
+ vaapi_format_from_pix_fmt(enum AVPixelFormat pix_fmt)
+{
+ int i;
+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++)
+ if (vaapi_format_map[i].pix_fmt == pix_fmt)
+ return &vaapi_format_map[i];
+ return NULL;
+}
+
+static enum AVPixelFormat vaapi_pix_fmt_from_fourcc(unsigned int fourcc)
+{
+ const VAAPIFormatDescriptor *desc;
+ desc = vaapi_format_from_fourcc(fourcc);
+ if (desc)
+ return desc->pix_fmt;
+ else
+ return AV_PIX_FMT_NONE;
}

static int vaapi_get_image_format(AVHWDeviceContext *hwdev,
@@ -461,22 +486,16 @@ static int vaapi_frames_init(AVHWFramesContext *hwfc)
AVVAAPIFramesContext *avfc = hwfc->hwctx;
VAAPIFramesContext *ctx = hwfc->internal->priv;
AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
+ const VAAPIFormatDescriptor *desc;
VAImageFormat *expected_format;
AVBufferRef *test_surface = NULL;
VASurfaceID test_surface_id;
VAImage test_image;
VAStatus vas;
int err, i;
- unsigned int fourcc, rt_format;

- for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
- if (vaapi_format_map[i].pix_fmt == hwfc->sw_format) {
- fourcc = vaapi_format_map[i].fourcc;
- rt_format = vaapi_format_map[i].rt_format;
- break;
- }
- }
- if (i >= FF_ARRAY_ELEMS(vaapi_format_map)) {
+ desc = vaapi_format_from_pix_fmt(hwfc->sw_format);
+ if (!desc) {
av_log(hwfc, AV_LOG_ERROR, "Unsupported format: %s.\n",
av_get_pix_fmt_name(hwfc->sw_format));
return AVERROR(EINVAL);
@@ -517,7 +536,7 @@ static int vaapi_frames_init(AVHWFramesContext *hwfc)
.type = VASurfaceAttribPixelFormat,
.flags = VA_SURFACE_ATTRIB_SETTABLE,
.value.type = VAGenericValueTypeInteger,
- .value.value.i = fourcc,
+ .value.value.i = desc->fourcc,
};
}
av_assert0(i == ctx->nb_attributes);
@@ -526,7 +545,7 @@ static int vaapi_frames_init(AVHWFramesContext *hwfc)
ctx->nb_attributes = 0;
}

- ctx->rt_format = rt_format;
+ ctx->rt_format = desc->rt_format;

if (hwfc->initial_pool_size > 0) {
// This pool will be usable as a render target, so we need to store
@@ -716,6 +735,7 @@ static int vaapi_map_frame(AVHWFramesContext *hwfc,
AVVAAPIDeviceContext *hwctx = hwfc->device_ctx->hwctx;
VAAPIFramesContext *ctx = hwfc->internal->priv;
VASurfaceID surface_id;
+ const VAAPIFormatDescriptor *desc;
VAImageFormat *image_format;
VAAPIMapping *map;
VAStatus vas;
@@ -824,11 +844,9 @@ static int vaapi_map_frame(AVHWFramesContext *hwfc,
dst->data[i] = (uint8_t*)address + map->image.offsets[i];
dst->linesize[i] = map->image.pitches[i];
}
- if (
-#ifdef VA_FOURCC_YV16
- map->image.format.fourcc == VA_FOURCC_YV16 ||
-#endif
- map->image.format.fourcc == VA_FOURCC_YV12) {
+
+ desc = vaapi_format_from_fourcc(map->image.format.fourcc);
+ if (desc && desc->chroma_planes_swapped) {
// Chroma planes are YVU rather than YUV, so swap them.
FFSWAP(uint8_t*, dst->data[1], dst->data[2]);
}
@@ -981,9 +999,10 @@ static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
(AVHWFramesContext*)dst->hw_frames_ctx->data;
AVVAAPIDeviceContext *dst_dev = dst_fc->device_ctx->hwctx;
const AVDRMFrameDescriptor *desc;
+ const VAAPIFormatDescriptor *format_desc;
VASurfaceID surface_id;
VAStatus vas;
- uint32_t va_fourcc, va_rt_format;
+ uint32_t va_fourcc;
int err, i, j, k;

unsigned long buffer_handle;
@@ -1034,14 +1053,8 @@ static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
av_log(dst_fc, AV_LOG_DEBUG, "Map DRM object %d to VAAPI as "
"%08x.\n", desc->objects[0].fd, va_fourcc);

- for (i = 0; i < FF_ARRAY_ELEMS(vaapi_format_map); i++) {
- if (vaapi_format_map[i].fourcc == va_fourcc) {
- va_rt_format = vaapi_format_map[i].rt_format;
- break;
- }
- }
-
- av_assert0(i < FF_ARRAY_ELEMS(vaapi_format_map));
+ format_desc = vaapi_format_from_fourcc(va_fourcc);
+ av_assert0(format_desc);

buffer_handle = desc->objects[0].fd;
buffer_desc.pixel_format = va_fourcc;
@@ -1062,7 +1075,13 @@ static int vaapi_map_from_drm(AVHWFramesContext *src_fc, AVFrame *dst,
}
buffer_desc.num_planes = k;

- vas = vaCreateSurfaces(dst_dev->display, va_rt_format,
+ if (format_desc->chroma_planes_swapped &&
+ buffer_desc.num_planes == 3) {
+ FFSWAP(uint32_t, buffer_desc.pitches[1], buffer_desc.pitches[2]);
+ FFSWAP(uint32_t, buffer_desc.offsets[1], buffer_desc.offsets[2]);
+ }
+
+ vas = vaCreateSurfaces(dst_dev->display, format_desc->rt_format,
src->width, src->height,
&surface_id, 1,
attrs, FF_ARRAY_ELEMS(attrs));
--
2.16.3
Mark Thompson
2018-06-07 23:43:16 UTC
Permalink
---
libavcodec/vaapi_encode_h264.c | 2 +-
libavcodec/vaapi_encode_h265.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index a4594ef9f2..82166d4457 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -976,7 +976,7 @@ static const AVOption vaapi_encode_h264_options[] = {
{ "ac", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, INT_MIN, INT_MAX, FLAGS, "coder" },

{ "aud", "Include AUD",
- OFFSET(aud), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS },
+ OFFSET(aud), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },

{ "sei", "Set SEI to include",
OFFSET(sei), AV_OPT_TYPE_FLAGS,
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 04dfef27c8..97bb9cef6c 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -1093,7 +1093,7 @@ static const AVOption vaapi_encode_h265_options[] = {
OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 25 }, 0, 52, FLAGS },

{ "aud", "Include AUD",
- OFFSET(aud), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS },
+ OFFSET(aud), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, FLAGS },

{ "profile", "Set profile (general_profile_idc)",
OFFSET(profile), AV_OPT_TYPE_INT,
--
2.16.3
Mark Thompson
2018-06-07 23:43:18 UTC
Permalink
Clarify that the list is the naughty list, and therefore being on it is
not desirable. The i965 driver does not need to be on the list after
version 2.0 (when the standard parameter buffer rendering behaviour was
changed).
---
libavutil/hwcontext_vaapi.c | 27 +++++++++++++++++++--------
1 file changed, 19 insertions(+), 8 deletions(-)

diff --git a/libavutil/hwcontext_vaapi.c b/libavutil/hwcontext_vaapi.c
index a2387d4fc4..4088a96be4 100644
--- a/libavutil/hwcontext_vaapi.c
+++ b/libavutil/hwcontext_vaapi.c
@@ -279,11 +279,14 @@ static const struct {
const char *match_string;
unsigned int quirks;
} vaapi_driver_quirks_table[] = {
+#if !VA_CHECK_VERSION(1, 0, 0)
+ // The i965 driver did not conform before version 2.0.
{
"Intel i965 (Quick Sync)",
"i965",
AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS,
},
+#endif
{
"Intel iHD",
"ubit",
@@ -344,29 +347,37 @@ static int vaapi_device_init(AVHWDeviceContext *hwdev)
}
}

+ vendor_string = vaQueryVendorString(hwctx->display);
+ if (vendor_string)
+ av_log(hwdev, AV_LOG_VERBOSE, "VAAPI driver: %s.\n", vendor_string);
+
if (hwctx->driver_quirks & AV_VAAPI_DRIVER_QUIRK_USER_SET) {
- av_log(hwdev, AV_LOG_VERBOSE, "Not detecting driver: "
- "quirks set by user.\n");
+ av_log(hwdev, AV_LOG_VERBOSE, "Using quirks set by user (%#x).\n",
+ hwctx->driver_quirks);
} else {
// Detect the driver in use and set quirk flags if necessary.
- vendor_string = vaQueryVendorString(hwctx->display);
hwctx->driver_quirks = 0;
if (vendor_string) {
for (i = 0; i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table); i++) {
if (strstr(vendor_string,
vaapi_driver_quirks_table[i].match_string)) {
- av_log(hwdev, AV_LOG_VERBOSE, "Matched \"%s\" as known "
- "driver \"%s\".\n", vendor_string,
- vaapi_driver_quirks_table[i].friendly_name);
+ av_log(hwdev, AV_LOG_VERBOSE, "Matched driver string "
+ "as known nonstandard driver \"%s\", setting "
+ "quirks (%#x).\n",
+ vaapi_driver_quirks_table[i].friendly_name,
+ vaapi_driver_quirks_table[i].quirks);
hwctx->driver_quirks |=
vaapi_driver_quirks_table[i].quirks;
break;
}
}
if (!(i < FF_ARRAY_ELEMS(vaapi_driver_quirks_table))) {
- av_log(hwdev, AV_LOG_VERBOSE, "Unknown driver \"%s\", "
- "assuming standard behaviour.\n", vendor_string);
+ av_log(hwdev, AV_LOG_VERBOSE, "Driver not found in known "
+ "nonstandard list, using standard behaviour.\n");
}
+ } else {
+ av_log(hwdev, AV_LOG_VERBOSE, "Driver has no vendor string, "
+ "assuming standard behaviour.\n");
}
}
--
2.16.3
Mark Thompson
2018-06-07 23:43:27 UTC
Permalink
---
libavcodec/cbs_h264_syntax_template.c | 42 +++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)

diff --git a/libavcodec/cbs_h264_syntax_template.c b/libavcodec/cbs_h264_syntax_template.c
index f53c02467e..03f2a15b0b 100644
--- a/libavcodec/cbs_h264_syntax_template.c
+++ b/libavcodec/cbs_h264_syntax_template.c
@@ -211,6 +211,46 @@ static int FUNC(vui_parameters)(CodedBitstreamContext *ctx, RWContext *rw,
return 0;
}

+static int FUNC(vui_parameters_default)(CodedBitstreamContext *ctx,
+ RWContext *rw, H264RawVUI *current,
+ H264RawSPS *sps)
+{
+ infer(aspect_ratio_idc, 0);
+
+ infer(video_format, 5);
+ infer(video_full_range_flag, 0);
+ infer(colour_primaries, 2);
+ infer(transfer_characteristics, 2);
+ infer(matrix_coefficients, 2);
+
+ infer(chroma_sample_loc_type_top_field, 0);
+ infer(chroma_sample_loc_type_bottom_field, 0);
+
+ infer(fixed_frame_rate_flag, 0);
+ infer(low_delay_hrd_flag, 1);
+
+ infer(pic_struct_present_flag, 0);
+
+ infer(motion_vectors_over_pic_boundaries_flag, 1);
+ infer(max_bytes_per_pic_denom, 2);
+ infer(max_bits_per_mb_denom, 1);
+ infer(log2_max_mv_length_horizontal, 15);
+ infer(log2_max_mv_length_vertical, 15);
+
+ if ((sps->profile_idc == 44 || sps->profile_idc == 86 ||
+ sps->profile_idc == 100 || sps->profile_idc == 110 ||
+ sps->profile_idc == 122 || sps->profile_idc == 244) &&
+ sps->constraint_set3_flag) {
+ infer(max_num_reorder_frames, 0);
+ infer(max_dec_frame_buffering, 0);
+ } else {
+ infer(max_num_reorder_frames, H264_MAX_DPB_FRAMES);
+ infer(max_dec_frame_buffering, H264_MAX_DPB_FRAMES);
+ }
+
+ return 0;
+}
+
static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
H264RawSPS *current)
{
@@ -315,6 +355,8 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
flag(vui_parameters_present_flag);
if (current->vui_parameters_present_flag)
CHECK(FUNC(vui_parameters)(ctx, rw, &current->vui, current));
+ else
+ CHECK(FUNC(vui_parameters_default)(ctx, rw, &current->vui, current));

CHECK(FUNC(rbsp_trailing_bits)(ctx, rw));
--
2.16.3
Xiang, Haihao
2018-06-15 03:46:08 UTC
Permalink
Post by Mark Thompson
---
libavcodec/cbs_h264_syntax_template.c | 42
+++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/libavcodec/cbs_h264_syntax_template.c
b/libavcodec/cbs_h264_syntax_template.c
index f53c02467e..03f2a15b0b 100644
--- a/libavcodec/cbs_h264_syntax_template.c
+++ b/libavcodec/cbs_h264_syntax_template.c
@@ -211,6 +211,46 @@ static int FUNC(vui_parameters)(CodedBitstreamContext
*ctx, RWContext *rw,
return 0;
}
+static int FUNC(vui_parameters_default)(CodedBitstreamContext *ctx,
+ RWContext *rw, H264RawVUI *current,
+ H264RawSPS *sps)
+{
+ infer(aspect_ratio_idc, 0);
+
+ infer(video_format, 5);
+ infer(video_full_range_flag, 0);
+ infer(colour_primaries, 2);
+ infer(transfer_characteristics, 2);
+ infer(matrix_coefficients, 2);
+
+ infer(chroma_sample_loc_type_top_field, 0);
+ infer(chroma_sample_loc_type_bottom_field, 0);
+
+ infer(fixed_frame_rate_flag, 0);
+ infer(low_delay_hrd_flag, 1);
+
+ infer(pic_struct_present_flag, 0);
+
+ infer(motion_vectors_over_pic_boundaries_flag, 1);
+ infer(max_bytes_per_pic_denom, 2);
+ infer(max_bits_per_mb_denom, 1);
+ infer(log2_max_mv_length_horizontal, 15);
+ infer(log2_max_mv_length_vertical, 15);
Both log2_max_mv_length_horizontal and log2_max_mv_length_vertical should be 16
Post by Mark Thompson
+
+ if ((sps->profile_idc == 44 || sps->profile_idc == 86 ||
+ sps->profile_idc == 100 || sps->profile_idc == 110 ||
+ sps->profile_idc == 122 || sps->profile_idc == 244) &&
+ sps->constraint_set3_flag) {
+ infer(max_num_reorder_frames, 0);
+ infer(max_dec_frame_buffering, 0);
+ } else {
+ infer(max_num_reorder_frames, H264_MAX_DPB_FRAMES);
+ infer(max_dec_frame_buffering, H264_MAX_DPB_FRAMES);
+ }
+
+ return 0;
+}
+
static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
H264RawSPS *current)
{
@@ -315,6 +355,8 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
flag(vui_parameters_present_flag);
if (current->vui_parameters_present_flag)
CHECK(FUNC(vui_parameters)(ctx, rw, &current->vui, current));
+ else
+ CHECK(FUNC(vui_parameters_default)(ctx, rw, &current->vui, current));
CHECK(FUNC(rbsp_trailing_bits)(ctx, rw));
Mark Thompson
2018-06-17 14:29:35 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
---
libavcodec/cbs_h264_syntax_template.c | 42
+++++++++++++++++++++++++++++++++++
1 file changed, 42 insertions(+)
diff --git a/libavcodec/cbs_h264_syntax_template.c
b/libavcodec/cbs_h264_syntax_template.c
index f53c02467e..03f2a15b0b 100644
--- a/libavcodec/cbs_h264_syntax_template.c
+++ b/libavcodec/cbs_h264_syntax_template.c
@@ -211,6 +211,46 @@ static int FUNC(vui_parameters)(CodedBitstreamContext
*ctx, RWContext *rw,
return 0;
}
+static int FUNC(vui_parameters_default)(CodedBitstreamContext *ctx,
+ RWContext *rw, H264RawVUI *current,
+ H264RawSPS *sps)
+{
+ infer(aspect_ratio_idc, 0);
+
+ infer(video_format, 5);
+ infer(video_full_range_flag, 0);
+ infer(colour_primaries, 2);
+ infer(transfer_characteristics, 2);
+ infer(matrix_coefficients, 2);
+
+ infer(chroma_sample_loc_type_top_field, 0);
+ infer(chroma_sample_loc_type_bottom_field, 0);
+
+ infer(fixed_frame_rate_flag, 0);
+ infer(low_delay_hrd_flag, 1);
+
+ infer(pic_struct_present_flag, 0);
+
+ infer(motion_vectors_over_pic_boundaries_flag, 1);
+ infer(max_bytes_per_pic_denom, 2);
+ infer(max_bits_per_mb_denom, 1);
+ infer(log2_max_mv_length_horizontal, 15);
+ infer(log2_max_mv_length_vertical, 15);
Both log2_max_mv_length_horizontal and log2_max_mv_length_vertical should be 16
No - see previous mail.
Post by Xiang, Haihao
Post by Mark Thompson
+
+ if ((sps->profile_idc == 44 || sps->profile_idc == 86 ||
+ sps->profile_idc == 100 || sps->profile_idc == 110 ||
+ sps->profile_idc == 122 || sps->profile_idc == 244) &&
+ sps->constraint_set3_flag) {
+ infer(max_num_reorder_frames, 0);
+ infer(max_dec_frame_buffering, 0);
+ } else {
+ infer(max_num_reorder_frames, H264_MAX_DPB_FRAMES);
+ infer(max_dec_frame_buffering, H264_MAX_DPB_FRAMES);
+ }
+
+ return 0;
+}
+
static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
H264RawSPS *current)
{
@@ -315,6 +355,8 @@ static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw,
flag(vui_parameters_present_flag);
if (current->vui_parameters_present_flag)
CHECK(FUNC(vui_parameters)(ctx, rw, &current->vui, current));
+ else
+ CHECK(FUNC(vui_parameters_default)(ctx, rw, &current->vui, current));
CHECK(FUNC(rbsp_trailing_bits)(ctx, rw));
Mark Thompson
2018-06-07 23:43:00 UTC
Permalink
---
libavcodec/vaapi_encode_mpeg2.c | 53 +++++++++++++++++++----------------------
1 file changed, 25 insertions(+), 28 deletions(-)

diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index 42df77ea49..ae77a9ce76 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -28,6 +28,9 @@
#include "vaapi_encode.h"

typedef struct VAAPIEncodeMPEG2Context {
+ VAAPIEncodeContext common;
+
+ // Derived settings.
int mb_width;
int mb_height;

@@ -35,15 +38,6 @@ typedef struct VAAPIEncodeMPEG2Context {
int quant_p;
int quant_b;

- MPEG2RawSequenceHeader sequence_header;
- MPEG2RawExtensionData sequence_extension;
- MPEG2RawExtensionData sequence_display_extension;
- MPEG2RawGroupOfPicturesHeader gop_header;
- MPEG2RawPictureHeader picture_header;
- MPEG2RawExtensionData picture_coding_extension;
-
- int64_t last_i_frame;
-
unsigned int bit_rate;
unsigned int vbv_buffer_size;

@@ -52,6 +46,17 @@ typedef struct VAAPIEncodeMPEG2Context {
unsigned int f_code_horizontal;
unsigned int f_code_vertical;

+ // Stream state.
+ int64_t last_i_frame;
+
+ // Writer structures.
+ MPEG2RawSequenceHeader sequence_header;
+ MPEG2RawExtensionData sequence_extension;
+ MPEG2RawExtensionData sequence_display_extension;
+ MPEG2RawGroupOfPicturesHeader gop_header;
+ MPEG2RawPictureHeader picture_header;
+ MPEG2RawExtensionData picture_coding_extension;
+
CodedBitstreamContext *cbc;
CodedBitstreamFragment current_fragment;
} VAAPIEncodeMPEG2Context;
@@ -61,8 +66,7 @@ static int vaapi_encode_mpeg2_write_fragment(AVCodecContext *avctx,
char *data, size_t *data_len,
CodedBitstreamFragment *frag)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeMPEG2Context *priv = ctx->priv_data;
+ VAAPIEncodeMPEG2Context *priv = avctx->priv_data;
int err;

err = ff_cbs_write_fragment_data(priv->cbc, frag);
@@ -88,8 +92,7 @@ static int vaapi_encode_mpeg2_add_header(AVCodecContext *avctx,
CodedBitstreamFragment *frag,
int type, void *header)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeMPEG2Context *priv = ctx->priv_data;
+ VAAPIEncodeMPEG2Context *priv = avctx->priv_data;
int err;

err = ff_cbs_insert_unit_content(priv->cbc, frag, -1, type, header, NULL);
@@ -105,8 +108,7 @@ static int vaapi_encode_mpeg2_add_header(AVCodecContext *avctx,
static int vaapi_encode_mpeg2_write_sequence_header(AVCodecContext *avctx,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeMPEG2Context *priv = ctx->priv_data;
+ VAAPIEncodeMPEG2Context *priv = avctx->priv_data;
CodedBitstreamFragment *frag = &priv->current_fragment;
int err;

@@ -140,8 +142,7 @@ static int vaapi_encode_mpeg2_write_picture_header(AVCodecContext *avctx,
VAAPIEncodePicture *pic,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeMPEG2Context *priv = ctx->priv_data;
+ VAAPIEncodeMPEG2Context *priv = avctx->priv_data;
CodedBitstreamFragment *frag = &priv->current_fragment;
int err;

@@ -164,7 +165,7 @@ fail:
static int vaapi_encode_mpeg2_init_sequence_params(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeMPEG2Context *priv = ctx->priv_data;
+ VAAPIEncodeMPEG2Context *priv = avctx->priv_data;
MPEG2RawSequenceHeader *sh = &priv->sequence_header;
MPEG2RawSequenceExtension *se = &priv->sequence_extension.data.sequence;
MPEG2RawSequenceDisplayExtension *sde = &priv->sequence_display_extension.data.sequence_display;
@@ -416,8 +417,7 @@ static int vaapi_encode_mpeg2_init_sequence_params(AVCodecContext *avctx)
static int vaapi_encode_mpeg2_init_picture_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeMPEG2Context *priv = ctx->priv_data;
+ VAAPIEncodeMPEG2Context *priv = avctx->priv_data;
MPEG2RawPictureHeader *ph = &priv->picture_header;
MPEG2RawPictureCodingExtension *pce = &priv->picture_coding_extension.data.picture_coding;
VAEncPictureParameterBufferMPEG2 *vpic = pic->codec_picture_params;
@@ -482,9 +482,8 @@ static int vaapi_encode_mpeg2_init_slice_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic,
VAAPIEncodeSlice *slice)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeMPEG2Context *priv = avctx->priv_data;
VAEncSliceParameterBufferMPEG2 *vslice = slice->codec_slice_params;
- VAAPIEncodeMPEG2Context *priv = ctx->priv_data;
int qp;

vslice->macroblock_address = priv->mb_width * slice->index;
@@ -515,7 +514,7 @@ static int vaapi_encode_mpeg2_init_slice_params(AVCodecContext *avctx,
static av_cold int vaapi_encode_mpeg2_configure(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeMPEG2Context *priv = ctx->priv_data;
+ VAAPIEncodeMPEG2Context *priv = avctx->priv_data;
int err;

err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_MPEG2VIDEO, avctx);
@@ -638,11 +637,9 @@ static av_cold int vaapi_encode_mpeg2_init(AVCodecContext *avctx)

static av_cold int vaapi_encode_mpeg2_close(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeMPEG2Context *priv = ctx->priv_data;
+ VAAPIEncodeMPEG2Context *priv = avctx->priv_data;

- if (priv)
- ff_cbs_close(&priv->cbc);
+ ff_cbs_close(&priv->cbc);

return ff_vaapi_encode_close(avctx);
}
@@ -665,7 +662,7 @@ AVCodec ff_mpeg2_vaapi_encoder = {
.long_name = NULL_IF_CONFIG_SMALL("MPEG-2 (VAAPI)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_MPEG2VIDEO,
- .priv_data_size = sizeof(VAAPIEncodeContext),
+ .priv_data_size = sizeof(VAAPIEncodeMPEG2Context),
.init = &vaapi_encode_mpeg2_init,
.encode2 = &ff_vaapi_encode2,
.close = &vaapi_encode_mpeg2_close,
--
2.16.3
Mark Thompson
2018-06-07 23:42:58 UTC
Permalink
Matching previous commit for H.264.
---
libavcodec/vaapi_encode_h265.c | 182 ++++++++++++++++++++---------------------
1 file changed, 90 insertions(+), 92 deletions(-)

diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index bbba2b8cd9..757fd74c30 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -41,6 +41,16 @@ enum {
};

typedef struct VAAPIEncodeH265Context {
+ VAAPIEncodeContext common;
+
+ // User options.
+ int qp;
+ int aud;
+ int profile;
+ int level;
+ int sei;
+
+ // Derived settings.
unsigned int ctu_width;
unsigned int ctu_height;

@@ -48,16 +58,7 @@ typedef struct VAAPIEncodeH265Context {
int fixed_qp_p;
int fixed_qp_b;

- H265RawAUD aud;
- H265RawVPS vps;
- H265RawSPS sps;
- H265RawPPS pps;
- H265RawSlice slice;
- H265RawSEI sei;
-
- H265RawSEIMasteringDisplayColourVolume mastering_display;
- H265RawSEIContentLightLevelInfo content_light_level;
-
+ // Stream state.
int64_t last_idr_frame;
int pic_order_cnt;

@@ -65,27 +66,29 @@ typedef struct VAAPIEncodeH265Context {
int slice_type;
int pic_type;

+ // Writer structures.
+ H265RawAUD raw_aud;
+ H265RawVPS raw_vps;
+ H265RawSPS raw_sps;
+ H265RawPPS raw_pps;
+ H265RawSEI raw_sei;
+ H265RawSlice raw_slice;
+
+ H265RawSEIMasteringDisplayColourVolume sei_mastering_display;
+ H265RawSEIContentLightLevelInfo sei_content_light_level;
+
CodedBitstreamContext *cbc;
CodedBitstreamFragment current_access_unit;
int aud_needed;
int sei_needed;
} VAAPIEncodeH265Context;

-typedef struct VAAPIEncodeH265Options {
- int qp;
- int aud;
- int profile;
- int level;
- int sei;
-} VAAPIEncodeH265Options;
-

static int vaapi_encode_h265_write_access_unit(AVCodecContext *avctx,
char *data, size_t *data_len,
CodedBitstreamFragment *au)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH265Context *priv = ctx->priv_data;
+ VAAPIEncodeH265Context *priv = avctx->priv_data;
int err;

err = ff_cbs_write_fragment_data(priv->cbc, au);
@@ -111,8 +114,7 @@ static int vaapi_encode_h265_add_nal(AVCodecContext *avctx,
CodedBitstreamFragment *au,
void *nal_unit)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH265Context *priv = ctx->priv_data;
+ VAAPIEncodeH265Context *priv = avctx->priv_data;
H265RawNALUnitHeader *header = nal_unit;
int err;

@@ -130,27 +132,26 @@ static int vaapi_encode_h265_add_nal(AVCodecContext *avctx,
static int vaapi_encode_h265_write_sequence_header(AVCodecContext *avctx,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH265Context *priv = ctx->priv_data;
+ VAAPIEncodeH265Context *priv = avctx->priv_data;
CodedBitstreamFragment *au = &priv->current_access_unit;
int err;

if (priv->aud_needed) {
- err = vaapi_encode_h265_add_nal(avctx, au, &priv->aud);
+ err = vaapi_encode_h265_add_nal(avctx, au, &priv->raw_aud);
if (err < 0)
goto fail;
priv->aud_needed = 0;
}

- err = vaapi_encode_h265_add_nal(avctx, au, &priv->vps);
+ err = vaapi_encode_h265_add_nal(avctx, au, &priv->raw_vps);
if (err < 0)
goto fail;

- err = vaapi_encode_h265_add_nal(avctx, au, &priv->sps);
+ err = vaapi_encode_h265_add_nal(avctx, au, &priv->raw_sps);
if (err < 0)
goto fail;

- err = vaapi_encode_h265_add_nal(avctx, au, &priv->pps);
+ err = vaapi_encode_h265_add_nal(avctx, au, &priv->raw_pps);
if (err < 0)
goto fail;

@@ -165,19 +166,18 @@ static int vaapi_encode_h265_write_slice_header(AVCodecContext *avctx,
VAAPIEncodeSlice *slice,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH265Context *priv = ctx->priv_data;
+ VAAPIEncodeH265Context *priv = avctx->priv_data;
CodedBitstreamFragment *au = &priv->current_access_unit;
int err;

if (priv->aud_needed) {
- err = vaapi_encode_h265_add_nal(avctx, au, &priv->aud);
+ err = vaapi_encode_h265_add_nal(avctx, au, &priv->raw_aud);
if (err < 0)
goto fail;
priv->aud_needed = 0;
}

- err = vaapi_encode_h265_add_nal(avctx, au, &priv->slice);
+ err = vaapi_encode_h265_add_nal(avctx, au, &priv->raw_slice);
if (err < 0)
goto fail;

@@ -192,12 +192,13 @@ static int vaapi_encode_h265_write_extra_header(AVCodecContext *avctx,
int index, int *type,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH265Context *priv = ctx->priv_data;
+ VAAPIEncodeH265Context *priv = avctx->priv_data;
CodedBitstreamFragment *au = &priv->current_access_unit;
int err, i;

if (priv->sei_needed) {
+ H265RawSEI *sei = &priv->raw_sei;
+
if (priv->aud_needed) {
err = vaapi_encode_h265_add_nal(avctx, au, &priv->aud);
if (err < 0)
@@ -205,31 +206,32 @@ static int vaapi_encode_h265_write_extra_header(AVCodecContext *avctx,
priv->aud_needed = 0;
}

- memset(&priv->sei, 0, sizeof(priv->sei));
- priv->sei.nal_unit_header = (H265RawNALUnitHeader) {
- .nal_unit_type = HEVC_NAL_SEI_PREFIX,
- .nuh_layer_id = 0,
- .nuh_temporal_id_plus1 = 1,
+ *sei = (H265RawSEI) {
+ .nal_unit_header = {
+ .nal_unit_type = HEVC_NAL_SEI_PREFIX,
+ .nuh_layer_id = 0,
+ .nuh_temporal_id_plus1 = 1,
+ },
};

i = 0;

if (priv->sei_needed & SEI_MASTERING_DISPLAY) {
- priv->sei.payload[i].payload_type = HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO;
- priv->sei.payload[i].payload.mastering_display = priv->mastering_display;
+ sei->payload[i].payload_type = HEVC_SEI_TYPE_MASTERING_DISPLAY_INFO;
+ sei->payload[i].payload.mastering_display = priv->sei_mastering_display;
++i;
}

if (priv->sei_needed & SEI_CONTENT_LIGHT_LEVEL) {
- priv->sei.payload[i].payload_type = HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO;
- priv->sei.payload[i].payload.content_light_level = priv->content_light_level;
+ sei->payload[i].payload_type = HEVC_SEI_TYPE_CONTENT_LIGHT_LEVEL_INFO;
+ sei->payload[i].payload.content_light_level = priv->sei_content_light_level;
++i;
}

- priv->sei.payload_count = i;
- av_assert0(priv->sei.payload_count > 0);
+ sei->payload_count = i;
+ av_assert0(sei->payload_count > 0);

- err = vaapi_encode_h265_add_nal(avctx, au, &priv->sei);
+ err = vaapi_encode_h265_add_nal(avctx, au, sei);
if (err < 0)
goto fail;
priv->sei_needed = 0;
@@ -254,10 +256,10 @@ fail:
static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH265Context *priv = ctx->priv_data;
- H265RawVPS *vps = &priv->vps;
- H265RawSPS *sps = &priv->sps;
- H265RawPPS *pps = &priv->pps;
+ VAAPIEncodeH265Context *priv = avctx->priv_data;
+ H265RawVPS *vps = &priv->raw_vps;
+ H265RawSPS *sps = &priv->raw_sps;
+ H265RawPPS *pps = &priv->raw_pps;
H265RawVUI *vui = &sps->vui;
VAEncSequenceParameterBufferHEVC *vseq = ctx->codec_sequence_params;
VAEncPictureParameterBufferHEVC *vpic = ctx->codec_picture_params;
@@ -613,9 +615,7 @@ static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH265Context *priv = ctx->priv_data;
- VAAPIEncodeH265Options *opt = ctx->codec_options;
+ VAAPIEncodeH265Context *priv = avctx->priv_data;
VAEncPictureParameterBufferHEVC *vpic = pic->codec_picture_params;
int i;

@@ -651,14 +651,16 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx,
}
priv->pic_order_cnt = pic->display_order - priv->last_idr_frame;

- if (opt->aud) {
+ if (priv->aud) {
priv->aud_needed = 1;
- priv->aud.nal_unit_header = (H265RawNALUnitHeader) {
- .nal_unit_type = HEVC_NAL_AUD,
- .nuh_layer_id = 0,
- .nuh_temporal_id_plus1 = 1,
+ priv->raw_aud = (H265RawAUD) {
+ .nal_unit_header = {
+ .nal_unit_type = HEVC_NAL_AUD,
+ .nuh_layer_id = 0,
+ .nuh_temporal_id_plus1 = 1,
+ },
+ .pic_type = priv->pic_type,
};
- priv->aud.pic_type = priv->pic_type;
} else {
priv->aud_needed = 0;
}
@@ -668,7 +670,7 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx,
// Only look for the metadata on I/IDR frame on the output. We
// may force an IDR frame on the output where the medadata gets
// changed on the input frame.
- if ((opt->sei & SEI_MASTERING_DISPLAY) &&
+ if ((priv->sei & SEI_MASTERING_DISPLAY) &&
(pic->type == PICTURE_TYPE_I || pic->type == PICTURE_TYPE_IDR)) {
AVFrameSideData *sd =
av_frame_get_side_data(pic->input_image,
@@ -680,41 +682,43 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx,

// SEI is needed when both the primaries and luminance are set
if (mdm->has_primaries && mdm->has_luminance) {
+ H265RawSEIMasteringDisplayColourVolume *mdcv =
+ &priv->sei_mastering_display;
const int mapping[3] = {1, 2, 0};
const int chroma_den = 50000;
const int luma_den = 10000;

for (i = 0; i < 3; i++) {
const int j = mapping[i];
- priv->mastering_display.display_primaries_x[i] =
+ mdcv->display_primaries_x[i] =
FFMIN(lrint(chroma_den *
av_q2d(mdm->display_primaries[j][0])),
chroma_den);
- priv->mastering_display.display_primaries_y[i] =
+ mdcv->display_primaries_y[i] =
FFMIN(lrint(chroma_den *
av_q2d(mdm->display_primaries[j][1])),
chroma_den);
}

- priv->mastering_display.white_point_x =
+ mdcv->white_point_x =
FFMIN(lrint(chroma_den * av_q2d(mdm->white_point[0])),
chroma_den);
- priv->mastering_display.white_point_y =
+ mdcv->white_point_y =
FFMIN(lrint(chroma_den * av_q2d(mdm->white_point[1])),
chroma_den);

- priv->mastering_display.max_display_mastering_luminance =
+ mdcv->max_display_mastering_luminance =
lrint(luma_den * av_q2d(mdm->max_luminance));
- priv->mastering_display.min_display_mastering_luminance =
+ mdcv->min_display_mastering_luminance =
FFMIN(lrint(luma_den * av_q2d(mdm->min_luminance)),
- priv->mastering_display.max_display_mastering_luminance);
+ mdcv->max_display_mastering_luminance);

priv->sei_needed |= SEI_MASTERING_DISPLAY;
}
}
}

- if ((opt->sei & SEI_CONTENT_LIGHT_LEVEL) &&
+ if ((priv->sei & SEI_CONTENT_LIGHT_LEVEL) &&
(pic->type == PICTURE_TYPE_I || pic->type == PICTURE_TYPE_IDR)) {
AVFrameSideData *sd =
av_frame_get_side_data(pic->input_image,
@@ -723,11 +727,11 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx,
if (sd) {
AVContentLightMetadata *clm =
(AVContentLightMetadata *)sd->data;
+ H265RawSEIContentLightLevelInfo *clli =
+ &priv->sei_content_light_level;

- priv->content_light_level.max_content_light_level =
- FFMIN(clm->MaxCLL, 65535);
- priv->content_light_level.max_pic_average_light_level =
- FFMIN(clm->MaxFALL, 65535);
+ clli->max_content_light_level = FFMIN(clm->MaxCLL, 65535);
+ clli->max_pic_average_light_level = FFMIN(clm->MaxFALL, 65535);

priv->sei_needed |= SEI_CONTENT_LIGHT_LEVEL;
}
@@ -798,10 +802,10 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx,
VAAPIEncodeSlice *slice)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH265Context *priv = ctx->priv_data;
- const H265RawSPS *sps = &priv->sps;
- const H265RawPPS *pps = &priv->pps;
- H265RawSliceHeader *sh = &priv->slice.header;
+ VAAPIEncodeH265Context *priv = avctx->priv_data;
+ const H265RawSPS *sps = &priv->raw_sps;
+ const H265RawPPS *pps = &priv->raw_pps;
+ H265RawSliceHeader *sh = &priv->raw_slice.header;
VAEncPictureParameterBufferHEVC *vpic = pic->codec_picture_params;
VAEncSliceParameterBufferHEVC *vslice = slice->codec_slice_params;
int i;
@@ -972,8 +976,7 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx,
static av_cold int vaapi_encode_h265_configure(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH265Context *priv = ctx->priv_data;
- VAAPIEncodeH265Options *opt = ctx->codec_options;
+ VAAPIEncodeH265Context *priv = avctx->priv_data;
int err;

err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_HEVC, avctx);
@@ -988,7 +991,7 @@ static av_cold int vaapi_encode_h265_configure(AVCodecContext *avctx)
ctx->surface_height, priv->ctu_width, priv->ctu_height);

if (ctx->va_rc_mode == VA_RC_CQP) {
- priv->fixed_qp_p = opt->qp;
+ priv->fixed_qp_p = priv->qp;
if (avctx->i_quant_factor > 0.0)
priv->fixed_qp_idr = (int)((priv->fixed_qp_p * avctx->i_quant_factor +
avctx->i_quant_offset) + 0.5);
@@ -1047,16 +1050,15 @@ static const VAAPIEncodeType vaapi_encode_type_h265 = {

static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH265Options *opt =
- (VAAPIEncodeH265Options*)ctx->codec_options_data;
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeH265Context *priv = avctx->priv_data;

ctx->codec = &vaapi_encode_type_h265;

if (avctx->profile == FF_PROFILE_UNKNOWN)
- avctx->profile = opt->profile;
+ avctx->profile = priv->profile;
if (avctx->level == FF_LEVEL_UNKNOWN)
- avctx->level = opt->level;
+ avctx->level = priv->level;

switch (avctx->profile) {
case FF_PROFILE_HEVC_MAIN:
@@ -1102,17 +1104,14 @@ static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx)

static av_cold int vaapi_encode_h265_close(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH265Context *priv = ctx->priv_data;
+ VAAPIEncodeH265Context *priv = avctx->priv_data;

- if (priv)
- ff_cbs_close(&priv->cbc);
+ ff_cbs_close(&priv->cbc);

return ff_vaapi_encode_close(avctx);
}

-#define OFFSET(x) (offsetof(VAAPIEncodeContext, codec_options_data) + \
- offsetof(VAAPIEncodeH265Options, x))
+#define OFFSET(x) offsetof(VAAPIEncodeH265Context, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
static const AVOption vaapi_encode_h265_options[] = {
{ "qp", "Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)",
@@ -1189,8 +1188,7 @@ AVCodec ff_hevc_vaapi_encoder = {
.long_name = NULL_IF_CONFIG_SMALL("H.265/HEVC (VAAPI)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_HEVC,
- .priv_data_size = (sizeof(VAAPIEncodeContext) +
- sizeof(VAAPIEncodeH265Options)),
+ .priv_data_size = sizeof(VAAPIEncodeH265Context),
.init = &vaapi_encode_h265_init,
.encode2 = &ff_vaapi_encode2,
.close = &vaapi_encode_h265_close,
--
2.16.3
Mark Thompson
2018-06-07 23:43:12 UTC
Permalink
This was added in libva 2.1.0 (VAAPI 1.1.0). Use AVCodecContext.qmax,
matching the existing behaviour for qmin, and clean up the defaults so
that we only pass min/max when explicitly set.
---
doc/encoders.texi | 3 ++-
libavcodec/vaapi_encode.c | 3 +++
libavcodec/vaapi_encode_h264.c | 3 ++-
libavcodec/vaapi_encode_h265.c | 2 ++
libavcodec/vaapi_encode_mpeg2.c | 2 ++
libavcodec/vaapi_encode_vp8.c | 2 ++
libavcodec/vaapi_encode_vp9.c | 2 ++
7 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/doc/encoders.texi b/doc/encoders.texi
index 0c0a307987..861f9f4f1f 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -2588,7 +2588,8 @@ Speed / quality tradeoff: higher values are faster / worse quality.
Size / quality tradeoff: higher values are smaller / worse quality.
@item
@option{qmin}
-(only: @option{qmax} is not supported)
+@item
+@option{qmax}
@item
@option{i_qfactor} / @option{i_quant_factor}
@item
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 5de5483454..0e806a29e3 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -1432,6 +1432,9 @@ static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
.initial_qp = 0,
.min_qp = (avctx->qmin > 0 ? avctx->qmin : 0),
.basic_unit_size = 0,
+#if VA_CHECK_VERSION(1, 1, 0)
+ .max_qp = (avctx->qmax > 0 ? avctx->qmax : 0),
+#endif
};
vaapi_encode_add_global_param(avctx, &ctx->rc_params.misc,
sizeof(ctx->rc_params));
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 1e6eb2e39b..87c0d9acf3 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -1033,7 +1033,8 @@ static const AVCodecDefault vaapi_encode_h264_defaults[] = {
{ "i_qoffset", "0" },
{ "b_qfactor", "6/5" },
{ "b_qoffset", "0" },
- { "qmin", "0" },
+ { "qmin", "-1" },
+ { "qmax", "-1" },
{ NULL },
};

diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index b296919b37..13ddad79ae 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -1148,6 +1148,8 @@ static const AVCodecDefault vaapi_encode_h265_defaults[] = {
{ "i_qoffset", "0" },
{ "b_qfactor", "6/5" },
{ "b_qoffset", "0" },
+ { "qmin", "-1" },
+ { "qmax", "-1" },
{ NULL },
};

diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index ff86b8817e..db72516187 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -672,6 +672,8 @@ static const AVCodecDefault vaapi_encode_mpeg2_defaults[] = {
{ "b_qfactor", "6/5" },
{ "b_qoffset", "0" },
{ "global_quality", "10" },
+ { "qmin", "-1" },
+ { "qmax", "-1" },
{ NULL },
};

diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c
index 40871a6bbf..db67136556 100644
--- a/libavcodec/vaapi_encode_vp8.c
+++ b/libavcodec/vaapi_encode_vp8.c
@@ -230,6 +230,8 @@ static const AVCodecDefault vaapi_encode_vp8_defaults[] = {
{ "bf", "0" },
{ "g", "120" },
{ "global_quality", "40" },
+ { "qmin", "-1" },
+ { "qmax", "-1" },
{ NULL },
};

diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index e400bc8b79..2b0658ec1f 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -253,6 +253,8 @@ static const AVCodecDefault vaapi_encode_vp9_defaults[] = {
{ "bf", "0" },
{ "g", "250" },
{ "global_quality", "100" },
+ { "qmin", "-1" },
+ { "qmax", "-1" },
{ NULL },
};
--
2.16.3
Mark Thompson
2018-06-07 23:43:08 UTC
Permalink
Include the common options, and also named options for setting the profile
and level.
---
libavcodec/vaapi_encode_mpeg2.c | 53 +++++++++++++++++++++++++++++++++++++++--
1 file changed, 51 insertions(+), 2 deletions(-)

diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index 7f6c7833da..db79d72115 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -30,6 +30,10 @@
typedef struct VAAPIEncodeMPEG2Context {
VAAPIEncodeContext common;

+ // User options.
+ int profile;
+ int level;
+
// Derived settings.
int mb_width;
int mb_height;
@@ -581,10 +585,18 @@ static const VAAPIEncodeType vaapi_encode_type_mpeg2 = {

static av_cold int vaapi_encode_mpeg2_init(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeMPEG2Context *priv = avctx->priv_data;

ctx->codec = &vaapi_encode_type_mpeg2;

+ if (avctx->profile == FF_PROFILE_UNKNOWN)
+ avctx->profile = priv->profile;
+ if (avctx->level == FF_LEVEL_UNKNOWN)
+ avctx->level = priv->level;
+
+ // Reject unknown levels (these are required to set f_code for
+ // motion vector encoding).
switch (avctx->level) {
case 4: // High
case 6: // High 1440
@@ -623,8 +635,37 @@ static av_cold int vaapi_encode_mpeg2_close(AVCodecContext *avctx)
return ff_vaapi_encode_close(avctx);
}

+#define OFFSET(x) offsetof(VAAPIEncodeMPEG2Context, x)
+#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
+static const AVOption vaapi_encode_mpeg2_options[] = {
+ VAAPI_ENCODE_COMMON_OPTIONS,
+
+ { "profile", "Set profile (in profile_and_level_indication)",
+ OFFSET(profile), AV_OPT_TYPE_INT,
+ { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 7, FLAGS, "profile" },
+
+#define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
+ { .i64 = value }, 0, 0, FLAGS, "profile"
+ { PROFILE("simple", FF_PROFILE_MPEG2_SIMPLE) },
+ { PROFILE("main", FF_PROFILE_MPEG2_MAIN) },
+#undef PROFILE
+
+ { "level", "Set level (in profile_and_level_indication)",
+ OFFSET(level), AV_OPT_TYPE_INT,
+ { .i64 = 4 }, 0, 15, FLAGS, "level" },
+
+#define LEVEL(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
+ { .i64 = value }, 0, 0, FLAGS, "level"
+ { LEVEL("low", 10) },
+ { LEVEL("main", 8) },
+ { LEVEL("high_1440", 6) },
+ { LEVEL("high", 4) },
+#undef LEVEL
+
+ { NULL },
+};
+
static const AVCodecDefault vaapi_encode_mpeg2_defaults[] = {
- { "level", "4" },
{ "bf", "1" },
{ "g", "120" },
{ "i_qfactor", "1" },
@@ -635,6 +676,13 @@ static const AVCodecDefault vaapi_encode_mpeg2_defaults[] = {
{ NULL },
};

+static const AVClass vaapi_encode_mpeg2_class = {
+ .class_name = "mpeg2_vaapi",
+ .item_name = av_default_item_name,
+ .option = vaapi_encode_mpeg2_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVCodec ff_mpeg2_vaapi_encoder = {
.name = "mpeg2_vaapi",
.long_name = NULL_IF_CONFIG_SMALL("MPEG-2 (VAAPI)"),
@@ -644,6 +692,7 @@ AVCodec ff_mpeg2_vaapi_encoder = {
.init = &vaapi_encode_mpeg2_init,
.encode2 = &ff_vaapi_encode2,
.close = &vaapi_encode_mpeg2_close,
+ .priv_class = &vaapi_encode_mpeg2_class,
.capabilities = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_HARDWARE,
.defaults = vaapi_encode_mpeg2_defaults,
.pix_fmts = (const enum AVPixelFormat[]) {
--
2.16.3
Mark Thompson
2018-06-07 23:43:05 UTC
Permalink
---
libavcodec/vaapi_encode.c | 38 ++++++++++++++++++++++----------------
1 file changed, 22 insertions(+), 16 deletions(-)

diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index ed6d289c6b..27ce792fbe 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -969,6 +969,20 @@ fail:
return err;
}

+static av_cold void vaapi_encode_add_global_param(AVCodecContext *avctx,
+ VAEncMiscParameterBuffer *buffer,
+ size_t size)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+
+ av_assert0(ctx->nb_global_params < MAX_GLOBAL_PARAMS);
+
+ ctx->global_params [ctx->nb_global_params] = buffer;
+ ctx->global_params_size[ctx->nb_global_params] = size;
+
+ ++ctx->nb_global_params;
+}
+
static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
@@ -1191,20 +1205,16 @@ static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
.min_qp = (avctx->qmin > 0 ? avctx->qmin : 0),
.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);
+ vaapi_encode_add_global_param(avctx, &ctx->rc_params.misc,
+ 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);
+ vaapi_encode_add_global_param(avctx, &ctx->hrd_params.misc,
+ sizeof(ctx->hrd_params));

if (avctx->framerate.num > 0 && avctx->framerate.den > 0)
av_reduce(&fr_num, &fr_den,
@@ -1217,10 +1227,8 @@ static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
ctx->fr_params.fr.framerate = (unsigned int)fr_den << 16 | fr_num;

#if VA_CHECK_VERSION(0, 40, 0)
- ctx->global_params[ctx->nb_global_params] =
- &ctx->fr_params.misc;
- ctx->global_params_size[ctx->nb_global_params++] =
- sizeof(ctx->fr_params);
+ vaapi_encode_add_global_param(avctx, &ctx->fr_params.misc,
+ sizeof(ctx->fr_params));
#endif

return 0;
@@ -1476,10 +1484,8 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
ctx->quality_params.quality.quality_level =
avctx->compression_level;

- ctx->global_params[ctx->nb_global_params] =
- &ctx->quality_params.misc;
- ctx->global_params_size[ctx->nb_global_params++] =
- sizeof(ctx->quality_params);
+ vaapi_encode_add_global_param(avctx, &ctx->quality_params.misc,
+ sizeof(ctx->quality_params));
}
#else
av_log(avctx, AV_LOG_WARNING, "The encode compression level "
--
2.16.3
Mark Thompson
2018-06-07 23:42:56 UTC
Permalink
Set the minimum version to 0.35.0 (libva 1.3.0) and remove redundant
configure tests.
---
This is the version in Ubuntu 14.04 LTS - I don't think it's worth keeping support for anything older now.


configure | 25 ++++++++-----------------
libavcodec/vaapi_decode.c | 2 --
2 files changed, 8 insertions(+), 19 deletions(-)

diff --git a/configure b/configure
index 53224f0ed5..790f55be14 100755
--- a/configure
+++ b/configure
@@ -2862,7 +2862,7 @@ vc1_vdpau_hwaccel_deps="vdpau"
vc1_vdpau_hwaccel_select="vc1_decoder"
vp8_nvdec_hwaccel_deps="nvdec"
vp8_nvdec_hwaccel_select="vp8_decoder"
-vp8_vaapi_hwaccel_deps="vaapi VAPictureParameterBufferVP8"
+vp8_vaapi_hwaccel_deps="vaapi"
vp8_vaapi_hwaccel_select="vp8_decoder"
vp9_d3d11va_hwaccel_deps="d3d11va DXVA_PicParams_VP9"
vp9_d3d11va_hwaccel_select="vp9_decoder"
@@ -2916,7 +2916,6 @@ h264_qsv_decoder_select="h264_mp4toannexb_bsf h264_parser qsvdec"
h264_qsv_encoder_select="qsvenc"
h264_rkmpp_decoder_deps="rkmpp"
h264_rkmpp_decoder_select="h264_mp4toannexb_bsf"
-h264_vaapi_encoder_deps="VAEncPictureParameterBufferH264"
h264_vaapi_encoder_select="cbs_h264 vaapi_encode"
h264_v4l2m2m_decoder_deps="v4l2_m2m h264_v4l2_m2m"
h264_v4l2m2m_encoder_deps="v4l2_m2m h264_v4l2_m2m"
@@ -2947,7 +2946,6 @@ mpeg2_mmal_decoder_deps="mmal"
mpeg2_mediacodec_decoder_deps="mediacodec"
mpeg2_qsv_decoder_select="qsvdec mpegvideo_parser"
mpeg2_qsv_encoder_select="qsvenc"
-mpeg2_vaapi_encoder_deps="VAEncPictureParameterBufferMPEG2"
mpeg2_vaapi_encoder_select="cbs_mpeg2 vaapi_encode"
mpeg2_v4l2m2m_decoder_deps="v4l2_m2m mpeg2_v4l2_m2m"
mpeg4_crystalhd_decoder_select="crystalhd"
@@ -3326,7 +3324,7 @@ deconvolve_filter_select="fft"
deinterlace_qsv_filter_deps="libmfx"
deinterlace_vaapi_filter_deps="vaapi"
delogo_filter_deps="gpl"
-denoise_vaapi_filter_deps="vaapi VAProcPipelineParameterBuffer"
+denoise_vaapi_filter_deps="vaapi"
deshake_filter_select="pixelutils"
drawtext_filter_deps="libfreetype"
drawtext_filter_suggest="libfontconfig libfribidi"
@@ -3370,7 +3368,7 @@ perspective_filter_deps="gpl"
phase_filter_deps="gpl"
pp7_filter_deps="gpl"
pp_filter_deps="gpl postproc"
-procamp_vaapi_filter_deps="vaapi VAProcPipelineParameterBuffer"
+procamp_vaapi_filter_deps="vaapi"
program_opencl_filter_deps="opencl"
pullup_filter_deps="gpl"
removelogo_filter_deps="avcodec avformat swscale"
@@ -3382,7 +3380,7 @@ scale2ref_filter_deps="swscale"
scale_filter_deps="swscale"
scale_qsv_filter_deps="libmfx"
select_filter_select="pixelutils"
-sharpness_vaapi_filter_deps="vaapi VAProcPipelineParameterBuffer"
+sharpness_vaapi_filter_deps="vaapi"
showcqt_filter_deps="avcodec avformat swscale"
showcqt_filter_suggest="libfontconfig libfreetype"
showcqt_filter_select="fft"
@@ -3419,7 +3417,7 @@ libvmaf_filter_deps="libvmaf pthreads"
zmq_filter_deps="libzmq"
zoompan_filter_deps="swscale"
zscale_filter_deps="libzimg const_nan"
-scale_vaapi_filter_deps="vaapi VAProcPipelineParameterBuffer"
+scale_vaapi_filter_deps="vaapi"
vpp_qsv_filter_deps="libmfx"
vpp_qsv_filter_select="qsvvpp"

@@ -5891,13 +5889,9 @@ check_type "windows.h d3d11.h" "ID3D11VideoContext"
check_type "d3d9.h dxva2api.h" DXVA2_ConfigPictureDecode -D_WIN32_WINNT=0x0602

check_type "va/va.h va/va_dec_hevc.h" "VAPictureParameterBufferHEVC"
-check_type "va/va.h va/va_dec_vp8.h" "VAPictureParameterBufferVP8"
check_struct "va/va.h" "VADecPictureParameterBufferVP9" bit_depth
-check_type "va/va.h va/va_vpp.h" "VAProcPipelineParameterBuffer"
-check_type "va/va.h va/va_enc_h264.h" "VAEncPictureParameterBufferH264"
check_type "va/va.h va/va_enc_hevc.h" "VAEncPictureParameterBufferHEVC"
check_type "va/va.h va/va_enc_jpeg.h" "VAEncPictureParameterBufferJPEG"
-check_type "va/va.h va/va_enc_mpeg2.h" "VAEncPictureParameterBufferMPEG2"
check_type "va/va.h va/va_enc_vp8.h" "VAEncPictureParameterBufferVP8"
check_type "va/va.h va/va_enc_vp9.h" "VAEncPictureParameterBufferVP9"

@@ -6310,14 +6304,11 @@ test_cpp <<EOF && enable uwp && d3d11va_extralibs="-ldxgi -ld3d11"
EOF

enabled vaapi &&
- check_lib vaapi va/va.h vaInitialize -lva
-
-enabled vaapi &&
- check_cc vaapi "va/va.h" "vaCreateSurfaces(0, 0, 0, 0, 0, 0, 0, 0)"
+ check_pkg_config vaapi "libva >= 0.35.0" "va/va.h" vaInitialize

if enabled vaapi; then
- check_lib vaapi_drm "va/va.h va/va_drm.h" vaGetDisplayDRM -lva -lva-drm
- check_lib vaapi_x11 "va/va.h va/va_x11.h" vaGetDisplay -lva -lva-x11 -lX11
+ check_pkg_config vaapi_drm "libva-drm" "va/va_drm.h" vaGetDisplayDRM
+ check_pkg_config vaapi_x11 "libva-x11" "va/va_x11.h" vaGetDisplay
fi

enabled vaapi &&
diff --git a/libavcodec/vaapi_decode.c b/libavcodec/vaapi_decode.c
index d0a6b5817d..ece75c0815 100644
--- a/libavcodec/vaapi_decode.c
+++ b/libavcodec/vaapi_decode.c
@@ -389,9 +389,7 @@ static const struct {
MAP(VC1, VC1_MAIN, VC1Main ),
MAP(VC1, VC1_COMPLEX, VC1Advanced ),
MAP(VC1, VC1_ADVANCED, VC1Advanced ),
-#if VA_CHECK_VERSION(0, 35, 0)
MAP(VP8, UNKNOWN, VP8Version0_3 ),
-#endif
#if VA_CHECK_VERSION(0, 38, 0)
MAP(VP9, VP9_0, VP9Profile0 ),
#endif
--
2.16.3
Mark Thompson
2018-06-07 23:42:59 UTC
Permalink
---
libavcodec/vaapi_encode_mjpeg.c | 18 ++++++++----------
1 file changed, 8 insertions(+), 10 deletions(-)

diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index c949e89646..983c77d194 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -56,6 +56,8 @@ static const unsigned char vaapi_encode_mjpeg_quant_chrominance[64] = {
};

typedef struct VAAPIEncodeMJPEGContext {
+ VAAPIEncodeContext common;
+
int quality;
int component_subsample_h[3];
int component_subsample_v[3];
@@ -83,8 +85,7 @@ static av_cold void vaapi_encode_mjpeg_copy_huffman(unsigned char *dst_lengths,

static av_cold void vaapi_encode_mjpeg_init_tables(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeMJPEGContext *priv = ctx->priv_data;
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
VAQMatrixBufferJPEG *quant = &priv->quant_tables;
VAHuffmanTableBufferJPEGBaseline *huff = &priv->huffman_tables;
int i;
@@ -133,10 +134,9 @@ static int vaapi_encode_mjpeg_write_image_header(AVCodecContext *avctx,
VAAPIEncodeSlice *slice,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
VAEncPictureParameterBufferJPEG *vpic = pic->codec_picture_params;
VAEncSliceParameterBufferJPEG *vslice = slice->codec_slice_params;
- VAAPIEncodeMJPEGContext *priv = ctx->priv_data;
PutBitContext pbc;
int t, i, quant_scale;

@@ -242,8 +242,7 @@ static int vaapi_encode_mjpeg_write_extra_buffer(AVCodecContext *avctx,
int index, int *type,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeMJPEGContext *priv = ctx->priv_data;
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;

if (index == 0) {
// Write quantisation tables.
@@ -270,9 +269,8 @@ static int vaapi_encode_mjpeg_write_extra_buffer(AVCodecContext *avctx,
static int vaapi_encode_mjpeg_init_picture_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;
VAEncPictureParameterBufferJPEG *vpic = pic->codec_picture_params;
- VAAPIEncodeMJPEGContext *priv = ctx->priv_data;

vpic->reconstructed_picture = pic->recon_surface;
vpic->coded_buf = pic->output_buffer;
@@ -336,7 +334,7 @@ static int vaapi_encode_mjpeg_init_slice_params(AVCodecContext *avctx,
static av_cold int vaapi_encode_mjpeg_configure(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeMJPEGContext *priv = ctx->priv_data;
+ VAAPIEncodeMJPEGContext *priv = avctx->priv_data;

priv->quality = avctx->global_quality;
if (priv->quality < 1 || priv->quality > 100) {
@@ -417,7 +415,7 @@ AVCodec ff_mjpeg_vaapi_encoder = {
.long_name = NULL_IF_CONFIG_SMALL("MJPEG (VAAPI)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_MJPEG,
- .priv_data_size = sizeof(VAAPIEncodeContext),
+ .priv_data_size = sizeof(VAAPIEncodeMJPEGContext),
.init = &vaapi_encode_mjpeg_init,
.encode2 = &ff_vaapi_encode2,
.close = &ff_vaapi_encode_close,
--
2.16.3
Mark Thompson
2018-06-07 23:43:03 UTC
Permalink
The codec-specific context now contains both the common context and the
codec-specific options directly.
---
libavcodec/vaapi_encode.c | 10 ----------
libavcodec/vaapi_encode.h | 11 -----------
libavcodec/vaapi_encode_h264.c | 2 --
libavcodec/vaapi_encode_h265.c | 2 --
libavcodec/vaapi_encode_mjpeg.c | 2 --
libavcodec/vaapi_encode_mpeg2.c | 2 --
libavcodec/vaapi_encode_vp8.c | 2 --
libavcodec/vaapi_encode_vp9.c | 2 --
8 files changed, 33 deletions(-)

diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 910fd1b365..cedf3d3549 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -1372,17 +1372,9 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
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);
@@ -1583,7 +1575,5 @@ av_cold int ff_vaapi_encode_close(AVCodecContext *avctx)
av_buffer_unref(&ctx->input_frames_ref);
av_buffer_unref(&ctx->device_ref);

- av_freep(&ctx->priv_data);
-
return 0;
}
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index bcb9d57371..c7370a17e2 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -113,9 +113,6 @@ typedef struct VAAPIEncodeContext {
// Everything above this point must be set before calling
// ff_vaapi_encode_init().

- // Codec-specific state.
- void *priv_data;
-
// Configuration attributes to use when creating va_config.
VAConfigAttrib config_attributes[MAX_CONFIG_ATTRIBUTES];
int nb_config_attributes;
@@ -205,18 +202,10 @@ typedef struct VAAPIEncodeContext {
int gop_counter;
int p_counter;
int end_of_stream;
-
- // Codec-local options are allocated to follow this structure in
- // memory (in the AVCodec definition, set priv_data_size to
- // sizeof(VAAPIEncodeContext) + sizeof(VAAPIEncodeFooOptions)).
- void *codec_options;
- char codec_options_data[0];
} VAAPIEncodeContext;


typedef struct VAAPIEncodeType {
- size_t priv_data_size;
-
// Perform any extra codec-specific configuration after the
// codec context is initialised (set up the private data and
// add any necessary global parameters).
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 26061974a4..8f999b2311 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -867,8 +867,6 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
}

static const VAAPIEncodeType vaapi_encode_type_h264 = {
- .priv_data_size = sizeof(VAAPIEncodeH264Context),
-
.configure = &vaapi_encode_h264_configure,

.sequence_params_size = sizeof(VAEncSequenceParameterBufferH264),
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 757fd74c30..8f191efc4b 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -1026,8 +1026,6 @@ static av_cold int vaapi_encode_h265_configure(AVCodecContext *avctx)
}

static const VAAPIEncodeType vaapi_encode_type_h265 = {
- .priv_data_size = sizeof(VAAPIEncodeH265Context),
-
.configure = &vaapi_encode_h265_configure,

.sequence_params_size = sizeof(VAEncSequenceParameterBufferHEVC),
diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index 983c77d194..481981a71c 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -360,8 +360,6 @@ static av_cold int vaapi_encode_mjpeg_configure(AVCodecContext *avctx)
}

static const VAAPIEncodeType vaapi_encode_type_mjpeg = {
- .priv_data_size = sizeof(VAAPIEncodeMJPEGContext),
-
.configure = &vaapi_encode_mjpeg_configure,

.picture_params_size = sizeof(VAEncPictureParameterBufferJPEG),
diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index ae77a9ce76..5577fa9e04 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -553,8 +553,6 @@ static av_cold int vaapi_encode_mpeg2_configure(AVCodecContext *avctx)
}

static const VAAPIEncodeType vaapi_encode_type_mpeg2 = {
- .priv_data_size = sizeof(VAAPIEncodeMPEG2Context),
-
.configure = &vaapi_encode_mpeg2_configure,

.sequence_params_size = sizeof(VAEncSequenceParameterBufferMPEG2),
diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c
index ab5e0b2dda..6cdd30abda 100644
--- a/libavcodec/vaapi_encode_vp8.c
+++ b/libavcodec/vaapi_encode_vp8.c
@@ -178,8 +178,6 @@ static av_cold int vaapi_encode_vp8_configure(AVCodecContext *avctx)
static const VAAPIEncodeType vaapi_encode_type_vp8 = {
.configure = &vaapi_encode_vp8_configure,

- .priv_data_size = sizeof(VAAPIEncodeVP8Context),
-
.sequence_params_size = sizeof(VAEncSequenceParameterBufferVP8),
.init_sequence_params = &vaapi_encode_vp8_init_sequence_params,

diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index 6e62213bc9..bf99597e4c 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -206,8 +206,6 @@ static av_cold int vaapi_encode_vp9_configure(AVCodecContext *avctx)
static const VAAPIEncodeType vaapi_encode_type_vp9 = {
.configure = &vaapi_encode_vp9_configure,

- .priv_data_size = sizeof(VAAPIEncodeVP9Context),
-
.sequence_params_size = sizeof(VAEncSequenceParameterBufferVP9),
.init_sequence_params = &vaapi_encode_vp9_init_sequence_params,
--
2.16.3
Mark Thompson
2018-06-07 23:43:04 UTC
Permalink
---
libavcodec/vaapi_encode.c | 15 ++++++++++++---
libavcodec/vaapi_encode.h | 4 ++++
2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index cedf3d3549..ed6d289c6b 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -528,14 +528,23 @@ static int vaapi_encode_discard(AVCodecContext *avctx,
return 0;
}

-static VAAPIEncodePicture *vaapi_encode_alloc(void)
+static VAAPIEncodePicture *vaapi_encode_alloc(AVCodecContext *avctx)
{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
VAAPIEncodePicture *pic;

pic = av_mallocz(sizeof(*pic));
if (!pic)
return NULL;

+ if (ctx->codec->picture_priv_data_size > 0) {
+ pic->priv_data = av_mallocz(ctx->codec->picture_priv_data_size);
+ if (!pic->priv_data) {
+ av_freep(&pic);
+ return NULL;
+ }
+ }
+
pic->input_surface = VA_INVALID_ID;
pic->recon_surface = VA_INVALID_ID;
pic->output_buffer = VA_INVALID_ID;
@@ -668,7 +677,7 @@ static int vaapi_encode_get_next(AVCodecContext *avctx,
}
}

- pic = vaapi_encode_alloc();
+ pic = vaapi_encode_alloc(avctx);
if (!pic)
return AVERROR(ENOMEM);

@@ -697,7 +706,7 @@ static int vaapi_encode_get_next(AVCodecContext *avctx,

for (i = 0; i < ctx->b_per_p &&
ctx->gop_counter < avctx->gop_size; i++) {
- pic = vaapi_encode_alloc();
+ pic = vaapi_encode_alloc(avctx);
if (!pic)
goto fail;

diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index c7370a17e2..54dc4a475e 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -211,6 +211,10 @@ typedef struct VAAPIEncodeType {
// add any necessary global parameters).
int (*configure)(AVCodecContext *avctx);

+ // The size of any private data structure associated with each
+ // picture (can be zero if not required).
+ size_t picture_priv_data_size;
+
// The size of the parameter structures:
// sizeof(VAEnc{type}ParameterBuffer{codec}).
size_t sequence_params_size;
--
2.16.3
Xiang, Haihao
2018-06-12 04:32:38 UTC
Permalink
Post by Mark Thompson
---
libavcodec/vaapi_encode.c | 15 ++++++++++++---
libavcodec/vaapi_encode.h | 4 ++++
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index cedf3d3549..ed6d289c6b 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -528,14 +528,23 @@ static int vaapi_encode_discard(AVCodecContext *avctx,
return 0;
}
-static VAAPIEncodePicture *vaapi_encode_alloc(void)
+static VAAPIEncodePicture *vaapi_encode_alloc(AVCodecContext *avctx)
{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
VAAPIEncodePicture *pic;
pic = av_mallocz(sizeof(*pic));
if (!pic)
return NULL;
+ if (ctx->codec->picture_priv_data_size > 0) {
+ pic->priv_data = av_mallocz(ctx->codec->picture_priv_data_size);
+ if (!pic->priv_data) {
+ av_freep(&pic);
+ return NULL;
+ }
+ }
+
pic->input_surface = VA_INVALID_ID;
pic->recon_surface = VA_INVALID_ID;
pic->output_buffer = VA_INVALID_ID;
@@ -668,7 +677,7 @@ static int vaapi_encode_get_next(AVCodecContext *avctx,
}
}
- pic = vaapi_encode_alloc();
+ pic = vaapi_encode_alloc(avctx);
if (!pic)
return AVERROR(ENOMEM);
@@ -697,7 +706,7 @@ static int vaapi_encode_get_next(AVCodecContext *avctx,
for (i = 0; i < ctx->b_per_p &&
ctx->gop_counter < avctx->gop_size; i++) {
- pic = vaapi_encode_alloc();
+ pic = vaapi_encode_alloc(avctx);
if (!pic)
goto fail;
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index c7370a17e2..54dc4a475e 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -211,6 +211,10 @@ typedef struct VAAPIEncodeType {
// add any necessary global parameters).
int (*configure)(AVCodecContext *avctx);
+ // The size of any private data structure associated with each
+ // picture (can be zero if not required).
+ size_t picture_priv_data_size;
+
I didn't see this field is set in the existent vaapi codecs, is the private data
structure really needed for a picture? If not, I'd like to remove the field of
priv_data from VAAPIEncodePicture instead.
Post by Mark Thompson
// sizeof(VAEnc{type}ParameterBuffer{codec}).
size_t sequence_params_size;
Mark Thompson
2018-06-13 21:50:11 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
---
libavcodec/vaapi_encode.c | 15 ++++++++++++---
libavcodec/vaapi_encode.h | 4 ++++
2 files changed, 16 insertions(+), 3 deletions(-)
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index cedf3d3549..ed6d289c6b 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -528,14 +528,23 @@ static int vaapi_encode_discard(AVCodecContext *avctx,
return 0;
}
-static VAAPIEncodePicture *vaapi_encode_alloc(void)
+static VAAPIEncodePicture *vaapi_encode_alloc(AVCodecContext *avctx)
{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
VAAPIEncodePicture *pic;
pic = av_mallocz(sizeof(*pic));
if (!pic)
return NULL;
+ if (ctx->codec->picture_priv_data_size > 0) {
+ pic->priv_data = av_mallocz(ctx->codec->picture_priv_data_size);
+ if (!pic->priv_data) {
+ av_freep(&pic);
+ return NULL;
+ }
+ }
+
pic->input_surface = VA_INVALID_ID;
pic->recon_surface = VA_INVALID_ID;
pic->output_buffer = VA_INVALID_ID;
@@ -668,7 +677,7 @@ static int vaapi_encode_get_next(AVCodecContext *avctx,
}
}
- pic = vaapi_encode_alloc();
+ pic = vaapi_encode_alloc(avctx);
if (!pic)
return AVERROR(ENOMEM);
@@ -697,7 +706,7 @@ static int vaapi_encode_get_next(AVCodecContext *avctx,
for (i = 0; i < ctx->b_per_p &&
ctx->gop_counter < avctx->gop_size; i++) {
- pic = vaapi_encode_alloc();
+ pic = vaapi_encode_alloc(avctx);
if (!pic)
goto fail;
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index c7370a17e2..54dc4a475e 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -211,6 +211,10 @@ typedef struct VAAPIEncodeType {
// add any necessary global parameters).
int (*configure)(AVCodecContext *avctx);
+ // The size of any private data structure associated with each
+ // picture (can be zero if not required).
+ size_t picture_priv_data_size;
+
I didn't see this field is set in the existent vaapi codecs, is the private data
structure really needed for a picture? If not, I'd like to remove the field of
priv_data from VAAPIEncodePicture instead.
Right, it's not actually used yet in the current set. I'll push this patch back so it goes with others actually using it.

Thanks,

- Mark
Mark Thompson
2018-06-07 23:42:57 UTC
Permalink
This will make it easier to support options in common between different
encoders. It also cleans up some of the field naming.
---
libavcodec/vaapi_encode_h264.c | 228 +++++++++++++++++++++--------------------
1 file changed, 115 insertions(+), 113 deletions(-)

diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 905c50760e..26061974a4 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -47,6 +47,19 @@ static const uint8_t vaapi_encode_h264_sei_identifier_uuid[16] = {
};

typedef struct VAAPIEncodeH264Context {
+ VAAPIEncodeContext common;
+
+ // User options.
+ int qp;
+ int quality;
+ int low_power;
+ int coder;
+ int aud;
+ int sei;
+ int profile;
+ int level;
+
+ // Derived settings.
int mb_width;
int mb_height;

@@ -54,18 +67,7 @@ typedef struct VAAPIEncodeH264Context {
int fixed_qp_p;
int fixed_qp_b;

- H264RawAUD aud;
- H264RawSPS sps;
- H264RawPPS pps;
- H264RawSEI sei;
- H264RawSlice slice;
-
- H264RawSEIBufferingPeriod buffering_period;
- H264RawSEIPicTiming pic_timing;
- H264RawSEIRecoveryPoint recovery_point;
- H264RawSEIUserDataUnregistered identifier;
- char *identifier_string;
-
+ // Stream state.
int frame_num;
int pic_order_cnt;
int next_frame_num;
@@ -78,32 +80,33 @@ typedef struct VAAPIEncodeH264Context {
int cpb_delay;
int dpb_delay;

+ // Writer structures.
CodedBitstreamContext *cbc;
CodedBitstreamFragment current_access_unit;
+
+ H264RawAUD raw_aud;
+ H264RawSPS raw_sps;
+ H264RawPPS raw_pps;
+ H264RawSEI raw_sei;
+ H264RawSlice raw_slice;
+
+ H264RawSEIBufferingPeriod sei_buffering_period;
+ H264RawSEIPicTiming sei_pic_timing;
+ H264RawSEIRecoveryPoint sei_recovery_point;
+ H264RawSEIUserDataUnregistered sei_identifier;
+ char *sei_identifier_string;
+
int aud_needed;
int sei_needed;
int sei_cbr_workaround_needed;
} VAAPIEncodeH264Context;

-typedef struct VAAPIEncodeH264Options {
- int qp;
- int quality;
- int low_power;
- // Entropy encoder type.
- int coder;
- int aud;
- int sei;
- int profile;
- int level;
-} VAAPIEncodeH264Options;
-

static int vaapi_encode_h264_write_access_unit(AVCodecContext *avctx,
char *data, size_t *data_len,
CodedBitstreamFragment *au)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
int err;

err = ff_cbs_write_fragment_data(priv->cbc, au);
@@ -129,8 +132,7 @@ static int vaapi_encode_h264_add_nal(AVCodecContext *avctx,
CodedBitstreamFragment *au,
void *nal_unit)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
H264RawNALUnitHeader *header = nal_unit;
int err;

@@ -148,23 +150,22 @@ static int vaapi_encode_h264_add_nal(AVCodecContext *avctx,
static int vaapi_encode_h264_write_sequence_header(AVCodecContext *avctx,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
CodedBitstreamFragment *au = &priv->current_access_unit;
int err;

if (priv->aud_needed) {
- err = vaapi_encode_h264_add_nal(avctx, au, &priv->aud);
+ err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_aud);
if (err < 0)
goto fail;
priv->aud_needed = 0;
}

- err = vaapi_encode_h264_add_nal(avctx, au, &priv->sps);
+ err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_sps);
if (err < 0)
goto fail;

- err = vaapi_encode_h264_add_nal(avctx, au, &priv->pps);
+ err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_pps);
if (err < 0)
goto fail;

@@ -179,19 +180,18 @@ static int vaapi_encode_h264_write_slice_header(AVCodecContext *avctx,
VAAPIEncodeSlice *slice,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
CodedBitstreamFragment *au = &priv->current_access_unit;
int err;

if (priv->aud_needed) {
- err = vaapi_encode_h264_add_nal(avctx, au, &priv->aud);
+ err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_aud);
if (err < 0)
goto fail;
priv->aud_needed = 0;
}

- err = vaapi_encode_h264_add_nal(avctx, au, &priv->slice);
+ err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_slice);
if (err < 0)
goto fail;

@@ -206,48 +206,53 @@ static int vaapi_encode_h264_write_extra_header(AVCodecContext *avctx,
int index, int *type,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
CodedBitstreamFragment *au = &priv->current_access_unit;
int err, i;

if (priv->sei_needed) {
+ H264RawSEI *sei = &priv->raw_sei;
+
if (priv->aud_needed) {
- err = vaapi_encode_h264_add_nal(avctx, au, &priv->aud);
+ err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_aud);
if (err < 0)
goto fail;
priv->aud_needed = 0;
}

- memset(&priv->sei, 0, sizeof(priv->sei));
- priv->sei.nal_unit_header.nal_unit_type = H264_NAL_SEI;
+ *sei = (H264RawSEI) {
+ .nal_unit_header = {
+ .nal_unit_type = H264_NAL_SEI,
+ },
+ };

i = 0;
+
if (priv->sei_needed & SEI_IDENTIFIER) {
- priv->sei.payload[i].payload_type = H264_SEI_TYPE_USER_DATA_UNREGISTERED;
- priv->sei.payload[i].payload.user_data_unregistered = priv->identifier;
+ sei->payload[i].payload_type = H264_SEI_TYPE_USER_DATA_UNREGISTERED;
+ sei->payload[i].payload.user_data_unregistered = priv->sei_identifier;
++i;
}
- if (priv->sei_needed & SEI_TIMING) {
+ if (priv->sei & SEI_TIMING) {
if (pic->type == PICTURE_TYPE_IDR) {
- priv->sei.payload[i].payload_type = H264_SEI_TYPE_BUFFERING_PERIOD;
- priv->sei.payload[i].payload.buffering_period = priv->buffering_period;
+ sei->payload[i].payload_type = H264_SEI_TYPE_BUFFERING_PERIOD;
+ sei->payload[i].payload.buffering_period = priv->sei_buffering_period;
++i;
}
- priv->sei.payload[i].payload_type = H264_SEI_TYPE_PIC_TIMING;
- priv->sei.payload[i].payload.pic_timing = priv->pic_timing;
+ sei->payload[i].payload_type = H264_SEI_TYPE_PIC_TIMING;
+ sei->payload[i].payload.pic_timing = priv->sei_pic_timing;
++i;
}
if (priv->sei_needed & SEI_RECOVERY_POINT) {
- priv->sei.payload[i].payload_type = H264_SEI_TYPE_RECOVERY_POINT;
- priv->sei.payload[i].payload.recovery_point = priv->recovery_point;
+ sei->payload[i].payload_type = H264_SEI_TYPE_RECOVERY_POINT;
+ sei->payload[i].payload.recovery_point = priv->sei_recovery_point;
++i;
}

- priv->sei.payload_count = i;
- av_assert0(priv->sei.payload_count > 0);
+ sei->payload_count = i;
+ av_assert0(sei->payload_count > 0);

- err = vaapi_encode_h264_add_nal(avctx, au, &priv->sei);
+ err = vaapi_encode_h264_add_nal(avctx, au, sei);
if (err < 0)
goto fail;
priv->sei_needed = 0;
@@ -285,10 +290,9 @@ fail:
static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
- VAAPIEncodeH264Options *opt = ctx->codec_options;
- H264RawSPS *sps = &priv->sps;
- H264RawPPS *pps = &priv->pps;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
+ H264RawSPS *sps = &priv->raw_sps;
+ H264RawPPS *pps = &priv->raw_pps;
VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params;
VAEncPictureParameterBufferH264 *vpic = ctx->codec_picture_params;

@@ -411,8 +415,9 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
sps->vui.fixed_frame_rate_flag = 0;
}

- if (opt->sei & SEI_TIMING) {
+ if (priv->sei & SEI_TIMING) {
H264RawHRD *hrd = &sps->vui.nal_hrd_parameters;
+ H264RawSEIBufferingPeriod *bp = &priv->sei_buffering_period;

sps->vui.nal_hrd_parameters_present_flag = 1;

@@ -439,13 +444,13 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
hrd->dpb_output_delay_length_minus1 = 7;
hrd->time_offset_length = 0;

- priv->buffering_period.seq_parameter_set_id = sps->seq_parameter_set_id;
+ bp->seq_parameter_set_id = sps->seq_parameter_set_id;

// This calculation can easily overflow 32 bits.
- priv->buffering_period.nal.initial_cpb_removal_delay[0] = 90000 *
+ bp->nal.initial_cpb_removal_delay[0] = 90000 *
(uint64_t)ctx->hrd_params.hrd.initial_buffer_fullness /
ctx->hrd_params.hrd.buffer_size;
- priv->buffering_period.nal.initial_cpb_removal_delay_offset[0] = 0;
+ bp->nal.initial_cpb_removal_delay_offset[0] = 0;
} else {
sps->vui.nal_hrd_parameters_present_flag = 0;
sps->vui.low_delay_hrd_flag = 1 - sps->vui.fixed_frame_rate_flag;
@@ -468,7 +473,7 @@ static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
!(sps->profile_idc == FF_PROFILE_H264_BASELINE ||
sps->profile_idc == FF_PROFILE_H264_EXTENDED ||
sps->profile_idc == FF_PROFILE_H264_CAVLC_444);
- if (!opt->coder && pps->entropy_coding_mode_flag)
+ if (!priv->coder && pps->entropy_coding_mode_flag)
pps->entropy_coding_mode_flag = 0;

pps->num_ref_idx_l0_default_active_minus1 = 0;
@@ -576,9 +581,8 @@ static int vaapi_encode_h264_init_picture_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
- VAAPIEncodeH264Options *opt = ctx->codec_options;
- H264RawSPS *sps = &priv->sps;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
+ H264RawSPS *sps = &priv->raw_sps;
VAEncPictureParameterBufferH264 *vpic = pic->codec_picture_params;
int i;

@@ -619,36 +623,42 @@ static int vaapi_encode_h264_init_picture_params(AVCodecContext *avctx,
priv->pic_order_cnt = pic->display_order - priv->last_idr_frame;
priv->dpb_delay = pic->display_order - pic->encode_order + 1;

- if (opt->aud) {
+ if (priv->aud) {
priv->aud_needed = 1;
- priv->aud.nal_unit_header.nal_unit_type = H264_NAL_AUD;
- priv->aud.primary_pic_type = priv->primary_pic_type;
+ priv->raw_aud = (H264RawAUD) {
+ .nal_unit_header = {
+ .nal_unit_type = H264_NAL_AUD,
+ },
+ .primary_pic_type = priv->primary_pic_type,
+ };
} else {
priv->aud_needed = 0;
}

priv->sei_needed = 0;

- if (opt->sei & SEI_IDENTIFIER && pic->encode_order == 0)
+ if (priv->sei & SEI_IDENTIFIER && pic->encode_order == 0)
priv->sei_needed |= SEI_IDENTIFIER;
#if !CONFIG_VAAPI_1
if (ctx->va_rc_mode == VA_RC_CBR)
priv->sei_cbr_workaround_needed = 1;
#endif

- if (opt->sei & SEI_TIMING) {
- memset(&priv->pic_timing, 0, sizeof(priv->pic_timing));
-
- priv->pic_timing.cpb_removal_delay = 2 * priv->cpb_delay;
- priv->pic_timing.dpb_output_delay = 2 * priv->dpb_delay;
+ if (priv->sei & SEI_TIMING) {
+ priv->sei_pic_timing = (H264RawSEIPicTiming) {
+ .cpb_removal_delay = 2 * priv->cpb_delay,
+ .dpb_output_delay = 2 * priv->dpb_delay,
+ };

priv->sei_needed |= SEI_TIMING;
}

- if (opt->sei & SEI_RECOVERY_POINT && pic->type == PICTURE_TYPE_I) {
- priv->recovery_point.recovery_frame_cnt = 0;
- priv->recovery_point.exact_match_flag = 1;
- priv->recovery_point.broken_link_flag = ctx->b_per_p > 0;
+ if (priv->sei & SEI_RECOVERY_POINT && pic->type == PICTURE_TYPE_I) {
+ priv->sei_recovery_point = (H264RawSEIRecoveryPoint) {
+ .recovery_frame_cnt = 0,
+ .exact_match_flag = 1,
+ .broken_link_flag = ctx->b_per_p > 0,
+ };

priv->sei_needed |= SEI_RECOVERY_POINT;
}
@@ -699,11 +709,10 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic,
VAAPIEncodeSlice *slice)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
- H264RawSPS *sps = &priv->sps;
- H264RawPPS *pps = &priv->pps;
- H264RawSliceHeader *sh = &priv->slice.header;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
+ H264RawSPS *sps = &priv->raw_sps;
+ H264RawPPS *pps = &priv->raw_pps;
+ H264RawSliceHeader *sh = &priv->raw_slice.header;
VAEncPictureParameterBufferH264 *vpic = pic->codec_picture_params;
VAEncSliceParameterBufferH264 *vslice = slice->codec_slice_params;
int i;
@@ -779,8 +788,7 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx,
static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
- VAAPIEncodeH264Options *opt = ctx->codec_options;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
int err;

err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_H264, avctx);
@@ -791,7 +799,7 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
priv->mb_height = FFALIGN(avctx->height, 16) / 16;

if (ctx->va_rc_mode == VA_RC_CQP) {
- priv->fixed_qp_p = opt->qp;
+ priv->fixed_qp_p = priv->qp;
if (avctx->i_quant_factor > 0.0)
priv->fixed_qp_idr = (int)((priv->fixed_qp_p * avctx->i_quant_factor +
avctx->i_quant_offset) + 0.5);
@@ -803,7 +811,7 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
else
priv->fixed_qp_b = priv->fixed_qp_p;

- opt->sei &= ~SEI_TIMING;
+ priv->sei &= ~SEI_TIMING;

av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = "
"%d / %d / %d for IDR- / P- / B-frames.\n",
@@ -825,17 +833,17 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
}

if (avctx->compression_level == FF_COMPRESSION_DEFAULT)
- avctx->compression_level = opt->quality;
+ avctx->compression_level = priv->quality;

- if (opt->sei & SEI_IDENTIFIER) {
+ if (priv->sei & SEI_IDENTIFIER) {
const char *lavc = LIBAVCODEC_IDENT;
const char *vaapi = VA_VERSION_S;
const char *driver;
int len;

- memcpy(priv->identifier.uuid_iso_iec_11578,
+ memcpy(priv->sei_identifier.uuid_iso_iec_11578,
vaapi_encode_h264_sei_identifier_uuid,
- sizeof(priv->identifier.uuid_iso_iec_11578));
+ sizeof(priv->sei_identifier.uuid_iso_iec_11578));

driver = vaQueryVendorString(ctx->hwctx->display);
if (!driver)
@@ -843,15 +851,15 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)

len = snprintf(NULL, 0, "%s / VAAPI %s / %s", lavc, vaapi, driver);
if (len >= 0) {
- priv->identifier_string = av_malloc(len + 1);
- if (!priv->identifier_string)
+ priv->sei_identifier_string = av_malloc(len + 1);
+ if (!priv->sei_identifier_string)
return AVERROR(ENOMEM);

- snprintf(priv->identifier_string, len + 1,
+ snprintf(priv->sei_identifier_string, len + 1,
"%s / VAAPI %s / %s", lavc, vaapi, driver);

- priv->identifier.data = priv->identifier_string;
- priv->identifier.data_length = len + 1;
+ priv->sei_identifier.data = priv->sei_identifier_string;
+ priv->sei_identifier.data_length = len + 1;
}
}

@@ -883,16 +891,15 @@ static const VAAPIEncodeType vaapi_encode_type_h264 = {

static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Options *opt =
- (VAAPIEncodeH264Options*)ctx->codec_options_data;
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;

ctx->codec = &vaapi_encode_type_h264;

if (avctx->profile == FF_PROFILE_UNKNOWN)
- avctx->profile = opt->profile;
+ avctx->profile = priv->profile;
if (avctx->level == FF_LEVEL_UNKNOWN)
- avctx->level = opt->level;
+ avctx->level = priv->level;

switch (avctx->profile) {
case FF_PROFILE_H264_BASELINE:
@@ -937,7 +944,7 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
avctx->profile);
return AVERROR(EINVAL);
}
- if (opt->low_power) {
+ if (priv->low_power) {
#if VA_CHECK_VERSION(0, 39, 2)
ctx->va_entrypoint = VAEntrypointEncSliceLP;
#else
@@ -973,19 +980,15 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)

static av_cold int vaapi_encode_h264_close(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;

- if (priv) {
- ff_cbs_close(&priv->cbc);
- av_freep(&priv->identifier_string);
- }
+ ff_cbs_close(&priv->cbc);
+ av_freep(&priv->sei_identifier_string);

return ff_vaapi_encode_close(avctx);
}

-#define OFFSET(x) (offsetof(VAAPIEncodeContext, codec_options_data) + \
- offsetof(VAAPIEncodeH264Options, x))
+#define OFFSET(x) offsetof(VAAPIEncodeH264Context, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
static const AVOption vaapi_encode_h264_options[] = {
{ "qp", "Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)",
@@ -1084,8 +1087,7 @@ AVCodec ff_h264_vaapi_encoder = {
.long_name = NULL_IF_CONFIG_SMALL("H.264/AVC (VAAPI)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
- .priv_data_size = (sizeof(VAAPIEncodeContext) +
- sizeof(VAAPIEncodeH264Options)),
+ .priv_data_size = sizeof(VAAPIEncodeH264Context),
.init = &vaapi_encode_h264_init,
.encode2 = &ff_vaapi_encode2,
.close = &vaapi_encode_h264_close,
--
2.16.3
Xiang, Haihao
2018-06-11 06:08:46 UTC
Permalink
Post by Mark Thompson
This will make it easier to support options in common between different
encoders. It also cleans up some of the field naming.
---
libavcodec/vaapi_encode_h264.c | 228 +++++++++++++++++++++-------------------
-
1 file changed, 115 insertions(+), 113 deletions(-)
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 905c50760e..26061974a4 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -47,6 +47,19 @@ static const uint8_t
vaapi_encode_h264_sei_identifier_uuid[16] = {
};
typedef struct VAAPIEncodeH264Context {
+ VAAPIEncodeContext common;
+
+ // User options.
+ int qp;
+ int quality;
+ int low_power;
+ int coder;
+ int aud;
+ int sei;
+ int profile;
+ int level;
+
+ // Derived settings.
int mb_width;
int mb_height;
@@ -54,18 +67,7 @@ typedef struct VAAPIEncodeH264Context {
int fixed_qp_p;
int fixed_qp_b;
- H264RawAUD aud;
- H264RawSPS sps;
- H264RawPPS pps;
- H264RawSEI sei;
- H264RawSlice slice;
-
- H264RawSEIBufferingPeriod buffering_period;
- H264RawSEIPicTiming pic_timing;
- H264RawSEIRecoveryPoint recovery_point;
- H264RawSEIUserDataUnregistered identifier;
- char *identifier_string;
-
+ // Stream state.
int frame_num;
int pic_order_cnt;
int next_frame_num;
@@ -78,32 +80,33 @@ typedef struct VAAPIEncodeH264Context {
int cpb_delay;
int dpb_delay;
+ // Writer structures.
CodedBitstreamContext *cbc;
CodedBitstreamFragment current_access_unit;
+
+ H264RawAUD raw_aud;
+ H264RawSPS raw_sps;
+ H264RawPPS raw_pps;
+ H264RawSEI raw_sei;
+ H264RawSlice raw_slice;
+
+ H264RawSEIBufferingPeriod sei_buffering_period;
+ H264RawSEIPicTiming sei_pic_timing;
+ H264RawSEIRecoveryPoint sei_recovery_point;
+ H264RawSEIUserDataUnregistered sei_identifier;
+ char *sei_identifier_string;
+
int aud_needed;
int sei_needed;
int sei_cbr_workaround_needed;
} VAAPIEncodeH264Context;
-typedef struct VAAPIEncodeH264Options {
- int qp;
- int quality;
- int low_power;
- // Entropy encoder type.
- int coder;
- int aud;
- int sei;
- int profile;
- int level;
-} VAAPIEncodeH264Options;
-
static int vaapi_encode_h264_write_access_unit(AVCodecContext *avctx,
char *data, size_t *data_len,
CodedBitstreamFragment *au)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
int err;
err = ff_cbs_write_fragment_data(priv->cbc, au);
@@ -129,8 +132,7 @@ static int vaapi_encode_h264_add_nal(AVCodecContext *avctx,
CodedBitstreamFragment *au,
void *nal_unit)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
H264RawNALUnitHeader *header = nal_unit;
int err;
@@ -148,23 +150,22 @@ static int vaapi_encode_h264_add_nal(AVCodecContext *avctx,
static int vaapi_encode_h264_write_sequence_header(AVCodecContext *avctx,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
CodedBitstreamFragment *au = &priv->current_access_unit;
int err;
if (priv->aud_needed) {
- err = vaapi_encode_h264_add_nal(avctx, au, &priv->aud);
+ err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_aud);
if (err < 0)
goto fail;
priv->aud_needed = 0;
}
- err = vaapi_encode_h264_add_nal(avctx, au, &priv->sps);
+ err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_sps);
if (err < 0)
goto fail;
- err = vaapi_encode_h264_add_nal(avctx, au, &priv->pps);
+ err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_pps);
if (err < 0)
goto fail;
@@ -179,19 +180,18 @@ static int
vaapi_encode_h264_write_slice_header(AVCodecContext *avctx,
VAAPIEncodeSlice *slice,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
CodedBitstreamFragment *au = &priv->current_access_unit;
int err;
if (priv->aud_needed) {
- err = vaapi_encode_h264_add_nal(avctx, au, &priv->aud);
+ err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_aud);
if (err < 0)
goto fail;
priv->aud_needed = 0;
}
- err = vaapi_encode_h264_add_nal(avctx, au, &priv->slice);
+ err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_slice);
if (err < 0)
goto fail;
@@ -206,48 +206,53 @@ static int
vaapi_encode_h264_write_extra_header(AVCodecContext *avctx,
int index, int *type,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
CodedBitstreamFragment *au = &priv->current_access_unit;
int err, i;
if (priv->sei_needed) {
+ H264RawSEI *sei = &priv->raw_sei;
+
if (priv->aud_needed) {
- err = vaapi_encode_h264_add_nal(avctx, au, &priv->aud);
+ err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_aud);
if (err < 0)
goto fail;
priv->aud_needed = 0;
}
- memset(&priv->sei, 0, sizeof(priv->sei));
- priv->sei.nal_unit_header.nal_unit_type = H264_NAL_SEI;
+ *sei = (H264RawSEI) {
+ .nal_unit_header = {
+ .nal_unit_type = H264_NAL_SEI,
+ },
+ };
i = 0;
+
if (priv->sei_needed & SEI_IDENTIFIER) {
- priv->sei.payload[i].payload_type =
H264_SEI_TYPE_USER_DATA_UNREGISTERED;
- priv->sei.payload[i].payload.user_data_unregistered = priv-
Post by Mark Thompson
identifier;
+ sei->payload[i].payload_type =
H264_SEI_TYPE_USER_DATA_UNREGISTERED;
+ sei->payload[i].payload.user_data_unregistered = priv-
Post by Mark Thompson
sei_identifier;
++i;
}
- if (priv->sei_needed & SEI_TIMING) {
+ if (priv->sei & SEI_TIMING) {
Typo? I think timing info is written when it is needed.
Post by Mark Thompson
if (pic->type == PICTURE_TYPE_IDR) {
- priv->sei.payload[i].payload_type =
H264_SEI_TYPE_BUFFERING_PERIOD;
- priv->sei.payload[i].payload.buffering_period = priv-
Post by Mark Thompson
buffering_period;
+ sei->payload[i].payload_type =
H264_SEI_TYPE_BUFFERING_PERIOD;
+ sei->payload[i].payload.buffering_period = priv-
Post by Mark Thompson
sei_buffering_period;
++i;
}
- priv->sei.payload[i].payload_type = H264_SEI_TYPE_PIC_TIMING;
- priv->sei.payload[i].payload.pic_timing = priv->pic_timing;
+ sei->payload[i].payload_type = H264_SEI_TYPE_PIC_TIMING;
+ sei->payload[i].payload.pic_timing = priv->sei_pic_timing;
++i;
}
if (priv->sei_needed & SEI_RECOVERY_POINT) {
- priv->sei.payload[i].payload_type = H264_SEI_TYPE_RECOVERY_POINT;
- priv->sei.payload[i].payload.recovery_point = priv-
Post by Mark Thompson
recovery_point;
+ sei->payload[i].payload_type = H264_SEI_TYPE_RECOVERY_POINT;
+ sei->payload[i].payload.recovery_point = priv-
Post by Mark Thompson
sei_recovery_point;
++i;
}
- priv->sei.payload_count = i;
- av_assert0(priv->sei.payload_count > 0);
+ sei->payload_count = i;
+ av_assert0(sei->payload_count > 0);
- err = vaapi_encode_h264_add_nal(avctx, au, &priv->sei);
+ err = vaapi_encode_h264_add_nal(avctx, au, sei);
if (err < 0)
goto fail;
priv->sei_needed = 0;
static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
- VAAPIEncodeH264Options *opt = ctx->codec_options;
- H264RawSPS *sps = &priv->sps;
- H264RawPPS *pps = &priv->pps;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
+ H264RawSPS *sps = &priv->raw_sps;
+ H264RawPPS *pps = &priv->raw_pps;
VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params;
VAEncPictureParameterBufferH264 *vpic = ctx->codec_picture_params;
@@ -411,8 +415,9 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
sps->vui.fixed_frame_rate_flag = 0;
}
- if (opt->sei & SEI_TIMING) {
+ if (priv->sei & SEI_TIMING) {
H264RawHRD *hrd = &sps->vui.nal_hrd_parameters;
+ H264RawSEIBufferingPeriod *bp = &priv->sei_buffering_period;
sps->vui.nal_hrd_parameters_present_flag = 1;
@@ -439,13 +444,13 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
hrd->dpb_output_delay_length_minus1 = 7;
hrd->time_offset_length = 0;
- priv->buffering_period.seq_parameter_set_id = sps-
Post by Mark Thompson
seq_parameter_set_id;
+ bp->seq_parameter_set_id = sps->seq_parameter_set_id;
// This calculation can easily overflow 32 bits.
- priv->buffering_period.nal.initial_cpb_removal_delay[0] = 90000 *
+ bp->nal.initial_cpb_removal_delay[0] = 90000 *
(uint64_t)ctx->hrd_params.hrd.initial_buffer_fullness /
ctx->hrd_params.hrd.buffer_size;
- priv->buffering_period.nal.initial_cpb_removal_delay_offset[0] = 0;
+ bp->nal.initial_cpb_removal_delay_offset[0] = 0;
} else {
sps->vui.nal_hrd_parameters_present_flag = 0;
sps->vui.low_delay_hrd_flag = 1 - sps->vui.fixed_frame_rate_flag;
@@ -468,7 +473,7 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
!(sps->profile_idc == FF_PROFILE_H264_BASELINE ||
sps->profile_idc == FF_PROFILE_H264_EXTENDED ||
sps->profile_idc == FF_PROFILE_H264_CAVLC_444);
- if (!opt->coder && pps->entropy_coding_mode_flag)
+ if (!priv->coder && pps->entropy_coding_mode_flag)
pps->entropy_coding_mode_flag = 0;
pps->num_ref_idx_l0_default_active_minus1 = 0;
@@ -576,9 +581,8 @@ static int
vaapi_encode_h264_init_picture_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
- VAAPIEncodeH264Options *opt = ctx->codec_options;
- H264RawSPS *sps = &priv->sps;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
+ H264RawSPS *sps = &priv->raw_sps;
VAEncPictureParameterBufferH264 *vpic = pic->codec_picture_params;
int i;
@@ -619,36 +623,42 @@ static int
vaapi_encode_h264_init_picture_params(AVCodecContext *avctx,
priv->pic_order_cnt = pic->display_order - priv->last_idr_frame;
priv->dpb_delay = pic->display_order - pic->encode_order + 1;
- if (opt->aud) {
+ if (priv->aud) {
priv->aud_needed = 1;
- priv->aud.nal_unit_header.nal_unit_type = H264_NAL_AUD;
- priv->aud.primary_pic_type = priv->primary_pic_type;
+ priv->raw_aud = (H264RawAUD) {
+ .nal_unit_header = {
+ .nal_unit_type = H264_NAL_AUD,
+ },
+ .primary_pic_type = priv->primary_pic_type,
+ };
} else {
priv->aud_needed = 0;
}
priv->sei_needed = 0;
- if (opt->sei & SEI_IDENTIFIER && pic->encode_order == 0)
+ if (priv->sei & SEI_IDENTIFIER && pic->encode_order == 0)
priv->sei_needed |= SEI_IDENTIFIER;
#if !CONFIG_VAAPI_1
if (ctx->va_rc_mode == VA_RC_CBR)
priv->sei_cbr_workaround_needed = 1;
#endif
- if (opt->sei & SEI_TIMING) {
- memset(&priv->pic_timing, 0, sizeof(priv->pic_timing));
-
- priv->pic_timing.cpb_removal_delay = 2 * priv->cpb_delay;
- priv->pic_timing.dpb_output_delay = 2 * priv->dpb_delay;
+ if (priv->sei & SEI_TIMING) {
+ priv->sei_pic_timing = (H264RawSEIPicTiming) {
+ .cpb_removal_delay = 2 * priv->cpb_delay,
+ .dpb_output_delay = 2 * priv->dpb_delay,
+ };
priv->sei_needed |= SEI_TIMING;
}
- if (opt->sei & SEI_RECOVERY_POINT && pic->type == PICTURE_TYPE_I) {
- priv->recovery_point.recovery_frame_cnt = 0;
- priv->recovery_point.exact_match_flag = 1;
- priv->recovery_point.broken_link_flag = ctx->b_per_p > 0;
+ if (priv->sei & SEI_RECOVERY_POINT && pic->type == PICTURE_TYPE_I) {
+ priv->sei_recovery_point = (H264RawSEIRecoveryPoint) {
+ .recovery_frame_cnt = 0,
+ .exact_match_flag = 1,
+ .broken_link_flag = ctx->b_per_p > 0,
+ };
priv->sei_needed |= SEI_RECOVERY_POINT;
}
@@ -699,11 +709,10 @@ static int
vaapi_encode_h264_init_slice_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic,
VAAPIEncodeSlice *slice)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
- H264RawSPS *sps = &priv->sps;
- H264RawPPS *pps = &priv->pps;
- H264RawSliceHeader *sh = &priv->slice.header;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
+ H264RawSPS *sps = &priv->raw_sps;
+ H264RawPPS *pps = &priv->raw_pps;
+ H264RawSliceHeader *sh = &priv->raw_slice.header;
VAEncPictureParameterBufferH264 *vpic = pic->codec_picture_params;
VAEncSliceParameterBufferH264 *vslice = slice->codec_slice_params;
int i;
@@ -779,8 +788,7 @@ static int
vaapi_encode_h264_init_slice_params(AVCodecContext *avctx,
static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
- VAAPIEncodeH264Options *opt = ctx->codec_options;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
int err;
err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_H264, avctx);
@@ -791,7 +799,7 @@ static av_cold int
vaapi_encode_h264_configure(AVCodecContext *avctx)
priv->mb_height = FFALIGN(avctx->height, 16) / 16;
if (ctx->va_rc_mode == VA_RC_CQP) {
- priv->fixed_qp_p = opt->qp;
+ priv->fixed_qp_p = priv->qp;
if (avctx->i_quant_factor > 0.0)
priv->fixed_qp_idr = (int)((priv->fixed_qp_p * avctx-
Post by Mark Thompson
i_quant_factor +
avctx->i_quant_offset) + 0.5);
@@ -803,7 +811,7 @@ static av_cold int
vaapi_encode_h264_configure(AVCodecContext *avctx)
else
priv->fixed_qp_b = priv->fixed_qp_p;
- opt->sei &= ~SEI_TIMING;
+ priv->sei &= ~SEI_TIMING;
av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = "
"%d / %d / %d for IDR- / P- / B-frames.\n",
@@ -825,17 +833,17 @@ static av_cold int
vaapi_encode_h264_configure(AVCodecContext *avctx)
}
if (avctx->compression_level == FF_COMPRESSION_DEFAULT)
- avctx->compression_level = opt->quality;
+ avctx->compression_level = priv->quality;
- if (opt->sei & SEI_IDENTIFIER) {
+ if (priv->sei & SEI_IDENTIFIER) {
const char *lavc = LIBAVCODEC_IDENT;
const char *vaapi = VA_VERSION_S;
const char *driver;
int len;
- memcpy(priv->identifier.uuid_iso_iec_11578,
+ memcpy(priv->sei_identifier.uuid_iso_iec_11578,
vaapi_encode_h264_sei_identifier_uuid,
- sizeof(priv->identifier.uuid_iso_iec_11578));
+ sizeof(priv->sei_identifier.uuid_iso_iec_11578));
driver = vaQueryVendorString(ctx->hwctx->display);
if (!driver)
@@ -843,15 +851,15 @@ static av_cold int
vaapi_encode_h264_configure(AVCodecContext *avctx)
len = snprintf(NULL, 0, "%s / VAAPI %s / %s", lavc, vaapi, driver);
if (len >= 0) {
- priv->identifier_string = av_malloc(len + 1);
- if (!priv->identifier_string)
+ priv->sei_identifier_string = av_malloc(len + 1);
+ if (!priv->sei_identifier_string)
return AVERROR(ENOMEM);
- snprintf(priv->identifier_string, len + 1,
+ snprintf(priv->sei_identifier_string, len + 1,
"%s / VAAPI %s / %s", lavc, vaapi, driver);
- priv->identifier.data = priv->identifier_string;
- priv->identifier.data_length = len + 1;
+ priv->sei_identifier.data = priv->sei_identifier_string;
+ priv->sei_identifier.data_length = len + 1;
}
}
@@ -883,16 +891,15 @@ static const VAAPIEncodeType vaapi_encode_type_h264 = {
static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Options *opt =
- (VAAPIEncodeH264Options*)ctx->codec_options_data;
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
ctx->codec = &vaapi_encode_type_h264;
if (avctx->profile == FF_PROFILE_UNKNOWN)
- avctx->profile = opt->profile;
+ avctx->profile = priv->profile;
if (avctx->level == FF_LEVEL_UNKNOWN)
- avctx->level = opt->level;
+ avctx->level = priv->level;
switch (avctx->profile) {
@@ -937,7 +944,7 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
avctx->profile);
return AVERROR(EINVAL);
}
- if (opt->low_power) {
+ if (priv->low_power) {
#if VA_CHECK_VERSION(0, 39, 2)
ctx->va_entrypoint = VAEntrypointEncSliceLP;
#else
@@ -973,19 +980,15 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
static av_cold int vaapi_encode_h264_close(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
- if (priv) {
- ff_cbs_close(&priv->cbc);
- av_freep(&priv->identifier_string);
- }
+ ff_cbs_close(&priv->cbc);
+ av_freep(&priv->sei_identifier_string);
return ff_vaapi_encode_close(avctx);
}
-#define OFFSET(x) (offsetof(VAAPIEncodeContext, codec_options_data) + \
- offsetof(VAAPIEncodeH264Options, x))
+#define OFFSET(x) offsetof(VAAPIEncodeH264Context, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
static const AVOption vaapi_encode_h264_options[] = {
{ "qp", "Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)",
@@ -1084,8 +1087,7 @@ AVCodec ff_h264_vaapi_encoder = {
.long_name = NULL_IF_CONFIG_SMALL("H.264/AVC (VAAPI)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_H264,
- .priv_data_size = (sizeof(VAAPIEncodeContext) +
- sizeof(VAAPIEncodeH264Options)),
+ .priv_data_size = sizeof(VAAPIEncodeH264Context),
.init = &vaapi_encode_h264_init,
.encode2 = &ff_vaapi_encode2,
.close = &vaapi_encode_h264_close,
Mark Thompson
2018-06-13 21:47:38 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
This will make it easier to support options in common between different
encoders. It also cleans up some of the field naming.
---
libavcodec/vaapi_encode_h264.c | 228 +++++++++++++++++++++-------------------
-
1 file changed, 115 insertions(+), 113 deletions(-)
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 905c50760e..26061974a4 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
...
@@ -206,48 +206,53 @@ static int
vaapi_encode_h264_write_extra_header(AVCodecContext *avctx,
int index, int *type,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeH264Context *priv = ctx->priv_data;
+ VAAPIEncodeH264Context *priv = avctx->priv_data;
CodedBitstreamFragment *au = &priv->current_access_unit;
int err, i;
if (priv->sei_needed) {
+ H264RawSEI *sei = &priv->raw_sei;
+
if (priv->aud_needed) {
- err = vaapi_encode_h264_add_nal(avctx, au, &priv->aud);
+ err = vaapi_encode_h264_add_nal(avctx, au, &priv->raw_aud);
if (err < 0)
goto fail;
priv->aud_needed = 0;
}
- memset(&priv->sei, 0, sizeof(priv->sei));
- priv->sei.nal_unit_header.nal_unit_type = H264_NAL_SEI;
+ *sei = (H264RawSEI) {
+ .nal_unit_header = {
+ .nal_unit_type = H264_NAL_SEI,
+ },
+ };
i = 0;
+
if (priv->sei_needed & SEI_IDENTIFIER) {
- priv->sei.payload[i].payload_type =
H264_SEI_TYPE_USER_DATA_UNREGISTERED;
- priv->sei.payload[i].payload.user_data_unregistered = priv-
Post by Mark Thompson
identifier;
+ sei->payload[i].payload_type =
H264_SEI_TYPE_USER_DATA_UNREGISTERED;
+ sei->payload[i].payload.user_data_unregistered = priv-
Post by Mark Thompson
sei_identifier;
++i;
}
- if (priv->sei_needed & SEI_TIMING) {
+ if (priv->sei & SEI_TIMING) {
Typo? I think timing info is written when it is needed.
Yeah, fixed.

Thanks,

- Mark
Mark Thompson
2018-06-07 23:43:10 UTC
Permalink
The codec sequence headers may contain fields which can overwrite the
fine parameters given in the specific settings (e.g. a crude bitrate
value vs. the max-rate / target-percentage / etc. values in
VAEncMiscParameterRateControl). Always reapply all global parameters
after a sequence header to avoid this causing problems.
---
libavcodec/vaapi_encode.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)

diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 4da8a7083c..f4c063734c 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -207,9 +207,16 @@ static int vaapi_encode_issue(AVCodecContext *avctx,

pic->nb_param_buffers = 0;

- if (pic->encode_order == 0) {
- // Global parameter buffers are set on the first picture only.
+ if (pic->type == PICTURE_TYPE_IDR && ctx->codec->init_sequence_params) {
+ err = vaapi_encode_make_param_buffer(avctx, pic,
+ VAEncSequenceParameterBufferType,
+ ctx->codec_sequence_params,
+ ctx->codec->sequence_params_size);
+ if (err < 0)
+ goto fail;
+ }

+ if (pic->type == PICTURE_TYPE_IDR) {
for (i = 0; i < ctx->nb_global_params; i++) {
err = vaapi_encode_make_param_buffer(avctx, pic,
VAEncMiscParameterBufferType,
@@ -220,15 +227,6 @@ static int vaapi_encode_issue(AVCodecContext *avctx,
}
}

- if (pic->type == PICTURE_TYPE_IDR && ctx->codec->init_sequence_params) {
- err = vaapi_encode_make_param_buffer(avctx, pic,
- VAEncSequenceParameterBufferType,
- ctx->codec_sequence_params,
- ctx->codec->sequence_params_size);
- if (err < 0)
- goto fail;
- }
-
if (ctx->codec->init_picture_params) {
err = ctx->codec->init_picture_params(avctx, pic);
if (err < 0) {
--
2.16.3
Mark Thompson
2018-06-07 23:43:01 UTC
Permalink
---
libavcodec/vaapi_encode_vp8.c | 31 ++++++++++++++-----------------
1 file changed, 14 insertions(+), 17 deletions(-)

diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c
index a2e861a8d1..ab5e0b2dda 100644
--- a/libavcodec/vaapi_encode_vp8.c
+++ b/libavcodec/vaapi_encode_vp8.c
@@ -32,14 +32,16 @@


typedef struct VAAPIEncodeVP8Context {
- int q_index_i;
- int q_index_p;
-} VAAPIEncodeVP8Context;
+ VAAPIEncodeContext common;

-typedef struct VAAPIEncodeVP8Options {
+ // User options.
int loop_filter_level;
int loop_filter_sharpness;
-} VAAPIEncodeVP8Options;
+
+ // Derived settings.
+ int q_index_i;
+ int q_index_p;
+} VAAPIEncodeVP8Context;


#define vseq_var(name) vseq->name, name
@@ -73,9 +75,8 @@ static int vaapi_encode_vp8_init_sequence_params(AVCodecContext *avctx)
static int vaapi_encode_vp8_init_picture_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeVP8Context *priv = avctx->priv_data;
VAEncPictureParameterBufferVP8 *vpic = pic->codec_picture_params;
- VAAPIEncodeVP8Options *opt = ctx->codec_options;
int i;

vpic->reconstructed_frame = pic->recon_surface;
@@ -116,8 +117,8 @@ static int vaapi_encode_vp8_init_picture_params(AVCodecContext *avctx,
vpic->pic_flags.bits.version = 0;
vpic->pic_flags.bits.loop_filter_type = 0;
for (i = 0; i < 4; i++)
- vpic->loop_filter_level[i] = opt->loop_filter_level;
- vpic->sharpness_level = opt->loop_filter_sharpness;
+ vpic->loop_filter_level[i] = priv->loop_filter_level;
+ vpic->sharpness_level = priv->loop_filter_sharpness;

vpic->clamp_qindex_low = 0;
vpic->clamp_qindex_high = 127;
@@ -130,8 +131,7 @@ static int vaapi_encode_vp8_write_quant_table(AVCodecContext *avctx,
int index, int *type,
char *data, size_t *data_len)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeVP8Context *priv = ctx->priv_data;
+ VAAPIEncodeVP8Context *priv = avctx->priv_data;
VAQMatrixBufferVP8 quant;
int i, q;

@@ -161,8 +161,7 @@ static int vaapi_encode_vp8_write_quant_table(AVCodecContext *avctx,

static av_cold int vaapi_encode_vp8_configure(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeVP8Context *priv = ctx->priv_data;
+ VAAPIEncodeVP8Context *priv = avctx->priv_data;

priv->q_index_p = av_clip(avctx->global_quality, 0, VP8_MAX_QUANT);
if (avctx->i_quant_factor > 0.0)
@@ -225,8 +224,7 @@ static av_cold int vaapi_encode_vp8_init(AVCodecContext *avctx)
return ff_vaapi_encode_init(avctx);
}

-#define OFFSET(x) (offsetof(VAAPIEncodeContext, codec_options_data) + \
- offsetof(VAAPIEncodeVP8Options, x))
+#define OFFSET(x) offsetof(VAAPIEncodeVP8Context, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
static const AVOption vaapi_encode_vp8_options[] = {
{ "loop_filter_level", "Loop filter level",
@@ -256,8 +254,7 @@ AVCodec ff_vp8_vaapi_encoder = {
.long_name = NULL_IF_CONFIG_SMALL("VP8 (VAAPI)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_VP8,
- .priv_data_size = (sizeof(VAAPIEncodeContext) +
- sizeof(VAAPIEncodeVP8Options)),
+ .priv_data_size = sizeof(VAAPIEncodeVP8Context),
.init = &vaapi_encode_vp8_init,
.encode2 = &ff_vaapi_encode2,
.close = &ff_vaapi_encode_close,
--
2.16.3
Mark Thompson
2018-06-07 23:43:06 UTC
Permalink
Previously there was one fixed choice for each codec (e.g. H.265 -> Main
profile), and using anything else then required an explicit option from
the user. This changes to selecting the profile based on the input format
and the set of profiles actually supported by the driver (e.g. P010 input
will choose Main 10 profile for H.265 if the driver supports it).

The entrypoint and render target format are also chosen dynamically in the
same way, removing those explicit selections from the per-codec code.
---
doc/encoders.texi | 3 +
libavcodec/vaapi_encode.c | 271 ++++++++++++++++++++++++++++++++--------
libavcodec/vaapi_encode.h | 43 +++++--
libavcodec/vaapi_encode_h264.c | 45 ++-----
libavcodec/vaapi_encode_h265.c | 35 ++----
libavcodec/vaapi_encode_mjpeg.c | 13 +-
libavcodec/vaapi_encode_mpeg2.c | 36 ++----
libavcodec/vaapi_encode_vp8.c | 11 +-
libavcodec/vaapi_encode_vp9.c | 34 ++---
9 files changed, 310 insertions(+), 181 deletions(-)

diff --git a/doc/encoders.texi b/doc/encoders.texi
index 7b095754d1..16be6359b3 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -2565,6 +2565,9 @@ The following standard libavcodec options are used:
@option{bf} / @option{max_b_frames}
@item
@option{profile}
+
+If not set, this will be determined automatically from the format of the input
+frames and the profiles supported by the driver.
@item
@option{level}
@item
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 27ce792fbe..6104470b31 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -983,70 +983,247 @@ static av_cold void vaapi_encode_add_global_param(AVCodecContext *avctx,
++ctx->nb_global_params;
}

-static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
+typedef struct VAAPIEncodeRTFormat {
+ const char *name;
+ unsigned int value;
+ int depth;
+ int components;
+ int log2_chroma_w;
+ int log2_chroma_h;
+} VAAPIEncodeRTFormat;
+
+static const VAAPIEncodeRTFormat vaapi_encode_rt_formats[] = {
+ { "YUV400", VA_RT_FORMAT_YUV400, 8, 1, },
+ { "YUV420", VA_RT_FORMAT_YUV420, 8, 3, 1, 1 },
+ { "YUV422", VA_RT_FORMAT_YUV422, 8, 3, 1, 0 },
+ { "YUV444", VA_RT_FORMAT_YUV444, 8, 3, 0, 0 },
+ { "YUV411", VA_RT_FORMAT_YUV411, 8, 3, 2, 0 },
+#if VA_CHECK_VERSION(0, 38, 1)
+ { "YUV420_10", VA_RT_FORMAT_YUV420_10BPP, 10, 3, 1, 1 },
+#endif
+};
+
+static const VAEntrypoint vaapi_encode_entrypoints_normal[] = {
+ VAEntrypointEncSlice,
+ VAEntrypointEncPicture,
+#if VA_CHECK_VERSION(0, 39, 2)
+ VAEntrypointEncSliceLP,
+#endif
+ 0
+};
+#if VA_CHECK_VERSION(0, 39, 2)
+static const VAEntrypoint vaapi_encode_entrypoints_low_power[] = {
+ VAEntrypointEncSliceLP,
+ 0
+};
+#endif
+
+static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAProfile *va_profiles = NULL;
+ VAEntrypoint *va_entrypoints = NULL;
VAStatus vas;
- int i, n, err;
- VAProfile *profiles = NULL;
- VAEntrypoint *entrypoints = NULL;
- VAConfigAttrib attr[] = {
- { VAConfigAttribRTFormat },
- { VAConfigAttribRateControl },
- { VAConfigAttribEncMaxRefFrames },
- { VAConfigAttribEncPackedHeaders },
- };
+ const VAEntrypoint *usable_entrypoints;
+ const VAAPIEncodeProfile *profile;
+ const AVPixFmtDescriptor *desc;
+ VAConfigAttrib rt_format_attr;
+ const VAAPIEncodeRTFormat *rt_format;
+ int i, j, n, depth, err;
+
+
+ if (ctx->low_power) {
+#if VA_CHECK_VERSION(0, 39, 2)
+ usable_entrypoints = vaapi_encode_entrypoints_low_power;
+#else
+ av_log(avctx, AV_LOG_ERROR, "Low-power encoding is not "
+ "supported with this VAAPI version.\n");
+ return AVERROR(EINVAL);
+#endif
+ } else {
+ usable_entrypoints = vaapi_encode_entrypoints_normal;
+ }
+
+ desc = av_pix_fmt_desc_get(ctx->input_frames->sw_format);
+ if (!desc) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%d).\n",
+ ctx->input_frames->sw_format);
+ return AVERROR(EINVAL);
+ }
+ depth = desc->comp[0].depth;
+ for (i = 1; i < desc->nb_components; i++) {
+ if (desc->comp[i].depth != depth) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%s).\n",
+ desc->name);
+ return AVERROR(EINVAL);
+ }
+ }
+ av_log(avctx, AV_LOG_VERBOSE, "Input surface format is %s.\n",
+ desc->name);

n = vaMaxNumProfiles(ctx->hwctx->display);
- profiles = av_malloc_array(n, sizeof(VAProfile));
- if (!profiles) {
+ va_profiles = av_malloc_array(n, sizeof(VAProfile));
+ if (!va_profiles) {
err = AVERROR(ENOMEM);
goto fail;
}
- vas = vaQueryConfigProfiles(ctx->hwctx->display, profiles, &n);
+ vas = vaQueryConfigProfiles(ctx->hwctx->display, va_profiles, &n);
if (vas != VA_STATUS_SUCCESS) {
- av_log(ctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",
+ av_log(avctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",
vas, vaErrorStr(vas));
- err = AVERROR(ENOSYS);
+ err = AVERROR_EXTERNAL;
goto fail;
}
- for (i = 0; i < n; i++) {
- if (profiles[i] == ctx->va_profile)
- break;
+
+ av_assert0(ctx->codec->profiles);
+ for (i = 0; (ctx->codec->profiles[i].av_profile !=
+ FF_PROFILE_UNKNOWN); i++) {
+ profile = &ctx->codec->profiles[i];
+ if (depth != profile->depth ||
+ desc->nb_components != profile->components)
+ continue;
+ if (desc->nb_components > 1 &&
+ (desc->log2_chroma_w != profile->log2_chroma_w ||
+ desc->log2_chroma_h != profile->log2_chroma_h))
+ continue;
+ if (avctx->profile != profile->av_profile &&
+ avctx->profile != FF_PROFILE_UNKNOWN)
+ continue;
+
+ for (j = 0; j < n; j++) {
+ if (va_profiles[j] == profile->va_profile)
+ break;
+ }
+ if (j >= n) {
+ av_log(avctx, AV_LOG_VERBOSE, "Matching profile %d is "
+ "not supported by driver.\n", profile->va_profile);
+ continue;
+ }
+
+ ctx->profile = profile;
+ break;
}
- if (i >= n) {
- av_log(ctx, AV_LOG_ERROR, "Encoding profile not found (%d).\n",
- ctx->va_profile);
- err = AVERROR(ENOSYS);
- goto fail;
+ if (!ctx->profile) {
+ av_log(avctx, AV_LOG_ERROR, "No usable encoding profile found.\n");
+ return AVERROR(ENOSYS);
}

+ avctx->profile = profile->av_profile;
+ ctx->va_profile = profile->va_profile;
+#if VA_CHECK_VERSION(1, 0, 0)
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %s (%d).\n",
+ vaProfileStr(ctx->va_profile), ctx->va_profile);
+#else
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %d.\n",
+ ctx->va_profile);
+#endif
+
n = vaMaxNumEntrypoints(ctx->hwctx->display);
- entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
- if (!entrypoints) {
+ va_entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
+ if (!va_entrypoints) {
err = AVERROR(ENOMEM);
goto fail;
}
vas = vaQueryConfigEntrypoints(ctx->hwctx->display, ctx->va_profile,
- entrypoints, &n);
+ va_entrypoints, &n);
if (vas != VA_STATUS_SUCCESS) {
- av_log(ctx, AV_LOG_ERROR, "Failed to query entrypoints for "
+ av_log(avctx, AV_LOG_ERROR, "Failed to query entrypoints for "
"profile %u: %d (%s).\n", ctx->va_profile,
vas, vaErrorStr(vas));
- err = AVERROR(ENOSYS);
+ err = AVERROR_EXTERNAL;
goto fail;
}
+
for (i = 0; i < n; i++) {
- if (entrypoints[i] == ctx->va_entrypoint)
+ for (j = 0; usable_entrypoints[j]; j++) {
+ if (va_entrypoints[i] == usable_entrypoints[j])
+ break;
+ }
+ if (usable_entrypoints[j])
break;
}
if (i >= n) {
- av_log(ctx, AV_LOG_ERROR, "Encoding entrypoint not found "
- "(%d / %d).\n", ctx->va_profile, ctx->va_entrypoint);
+ av_log(avctx, AV_LOG_ERROR, "No usable encoding entrypoint found "
+ "for profile %d.\n", ctx->va_profile);
+ err = AVERROR(ENOSYS);
+ goto fail;
+ }
+
+ ctx->va_entrypoint = va_entrypoints[i];
+#if VA_CHECK_VERSION(1, 0, 0)
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %s (%d).\n",
+ vaEntrypointStr(ctx->va_entrypoint), ctx->va_entrypoint);
+#else
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %d.\n",
+ ctx->va_entrypoint);
+#endif
+
+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_encode_rt_formats); i++) {
+ rt_format = &vaapi_encode_rt_formats[i];
+ if (rt_format->depth == depth &&
+ rt_format->components == profile->components &&
+ rt_format->log2_chroma_w == profile->log2_chroma_w &&
+ rt_format->log2_chroma_h == profile->log2_chroma_h)
+ break;
+ }
+ if (i >= FF_ARRAY_ELEMS(vaapi_encode_rt_formats)) {
+ av_log(avctx, AV_LOG_ERROR, "No usable render target format "
+ "found for profile %d entrypoint %d.\n",
+ ctx->va_profile, ctx->va_entrypoint);
err = AVERROR(ENOSYS);
goto fail;
}

+ rt_format_attr = (VAConfigAttrib) { VAConfigAttribRTFormat };
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile, ctx->va_entrypoint,
+ &rt_format_attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query RT format "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ err = AVERROR_EXTERNAL;
+ goto fail;
+ }
+
+ if (rt_format_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_VERBOSE, "RT format config attribute not "
+ "supported by driver: assuming surface RT format %s "
+ "is valid.\n", rt_format->name);
+ } else if (!(rt_format_attr.value & rt_format->value)) {
+ av_log(avctx, AV_LOG_ERROR, "Surface RT format %s not supported "
+ "by driver for encoding profile %d entrypoint %d.\n",
+ rt_format->name, ctx->va_profile, ctx->va_entrypoint);
+ err = AVERROR(ENOSYS);
+ goto fail;
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI render target "
+ "format %s (%#x).\n", rt_format->name, rt_format->value);
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRTFormat,
+ .value = rt_format->value,
+ };
+ }
+
+ err = 0;
+fail:
+ av_freep(&va_profiles);
+ av_freep(&va_entrypoints);
+ return err;
+}
+
+static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAStatus vas;
+ int i;
+
+ VAConfigAttrib attr[] = {
+ { VAConfigAttribRateControl },
+ { VAConfigAttribEncMaxRefFrames },
+ { VAConfigAttribEncPackedHeaders },
+ };
+
vas = vaGetConfigAttributes(ctx->hwctx->display,
ctx->va_profile, ctx->va_entrypoint,
attr, FF_ARRAY_ELEMS(attr));
@@ -1066,20 +1243,6 @@ static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
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:
// Hack for backward compatibility: CBR was the only
// usable RC mode for a long time, so old drivers will
@@ -1098,8 +1261,7 @@ static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
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;
+ return AVERROR(EINVAL);
}
ctx->config_attributes[ctx->nb_config_attributes++] =
(VAConfigAttrib) {
@@ -1115,8 +1277,7 @@ static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
if (avctx->gop_size > 1 && ref_l0 < 1) {
av_log(avctx, AV_LOG_ERROR, "P frames are not "
"supported (%#x).\n", attr[i].value);
- err = AVERROR(EINVAL);
- goto fail;
+ return AVERROR(EINVAL);
}
if (avctx->max_b_frames > 0 && ref_l1 < 1) {
av_log(avctx, AV_LOG_WARNING, "B frames are not "
@@ -1148,11 +1309,7 @@ static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
}
}

- err = 0;
-fail:
- av_freep(&profiles);
- av_freep(&entrypoints);
- return err;
+ return 0;
}

static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
@@ -1407,6 +1564,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
ctx->device = (AVHWDeviceContext*)ctx->device_ref->data;
ctx->hwctx = ctx->device->hwctx;

+ err = vaapi_encode_profile_entrypoint(avctx);
+ if (err < 0)
+ goto fail;
+
err = vaapi_encode_config_attributes(avctx);
if (err < 0)
goto fail;
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index 54dc4a475e..212ab6726d 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -23,6 +23,10 @@

#include <va/va.h>

+#if VA_CHECK_VERSION(1, 0, 0)
+#include <va/va_str.h>
+#endif
+
#include "libavutil/hwcontext.h"
#include "libavutil/hwcontext_vaapi.h"

@@ -86,18 +90,32 @@ typedef struct VAAPIEncodePicture {
VAAPIEncodeSlice *slices;
} VAAPIEncodePicture;

+typedef struct VAAPIEncodeProfile {
+ // lavc profile value (FF_PROFILE_*).
+ int av_profile;
+ // Supported bit depth.
+ int depth;
+ // Number of components.
+ int components;
+ // Chroma subsampling in width dimension.
+ int log2_chroma_w;
+ // Chroma subsampling in height dimension.
+ int log2_chroma_h;
+ // VAAPI profile value.
+ VAProfile va_profile;
+} VAAPIEncodeProfile;
+
typedef struct VAAPIEncodeContext {
const AVClass *class;

// 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;
+ // Global options.
+
+ // Use low power encoding mode.
+ int low_power;
+
// Rate control mode.
unsigned int va_rc_mode;
// Supported packed headers (initially the desired set, modified
@@ -113,6 +131,14 @@ typedef struct VAAPIEncodeContext {
// Everything above this point must be set before calling
// ff_vaapi_encode_init().

+ // Chosen encoding profile details.
+ const VAAPIEncodeProfile *profile;
+
+ // Encoding profile (VAProfile*).
+ VAProfile va_profile;
+ // Encoding entrypoint (VAEntryoint*).
+ VAEntrypoint va_entrypoint;
+
// Configuration attributes to use when creating va_config.
VAConfigAttrib config_attributes[MAX_CONFIG_ATTRIBUTES];
int nb_config_attributes;
@@ -204,8 +230,11 @@ typedef struct VAAPIEncodeContext {
int end_of_stream;
} VAAPIEncodeContext;

-
typedef struct VAAPIEncodeType {
+ // List of supported profiles and corresponding VAAPI profiles.
+ // (Must end with FF_PROFILE_UNKNOWN.)
+ const VAAPIEncodeProfile *profiles;
+
// Perform any extra codec-specific configuration after the
// codec context is initialised (set up the private data and
// add any necessary global parameters).
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 8f999b2311..ab8bf13ef3 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -866,7 +866,17 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
return 0;
}

+static const VAAPIEncodeProfile vaapi_encode_h264_profiles[] = {
+ { FF_PROFILE_H264_HIGH, 8, 3, 1, 1, VAProfileH264High },
+ { FF_PROFILE_H264_MAIN, 8, 3, 1, 1, VAProfileH264Main },
+ { FF_PROFILE_H264_CONSTRAINED_BASELINE,
+ 8, 3, 1, 1, VAProfileH264ConstrainedBaseline },
+ { FF_PROFILE_UNKNOWN }
+};
+
static const VAAPIEncodeType vaapi_encode_type_h264 = {
+ .profiles = vaapi_encode_h264_profiles,
+
.configure = &vaapi_encode_h264_configure,

.sequence_params_size = sizeof(VAEncSequenceParameterBufferH264),
@@ -899,30 +909,17 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
if (avctx->level == FF_LEVEL_UNKNOWN)
avctx->level = priv->level;

+ // Reject unsupported profiles.
switch (avctx->profile) {
case FF_PROFILE_H264_BASELINE:
av_log(avctx, AV_LOG_WARNING, "H.264 baseline profile is not "
"supported, using constrained baseline profile instead.\n");
avctx->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE;
- case FF_PROFILE_H264_CONSTRAINED_BASELINE:
- ctx->va_profile = VAProfileH264ConstrainedBaseline;
- if (avctx->max_b_frames != 0) {
- avctx->max_b_frames = 0;
- av_log(avctx, AV_LOG_WARNING, "H.264 constrained baseline profile "
- "doesn't support encoding with B frames, disabling them.\n");
- }
- break;
- case FF_PROFILE_H264_MAIN:
- ctx->va_profile = VAProfileH264Main;
break;
case FF_PROFILE_H264_EXTENDED:
av_log(avctx, AV_LOG_ERROR, "H.264 extended profile "
"is not supported.\n");
return AVERROR_PATCHWELCOME;
- case FF_PROFILE_UNKNOWN:
- case FF_PROFILE_H264_HIGH:
- ctx->va_profile = VAProfileH264High;
- break;
case FF_PROFILE_H264_HIGH_10:
case FF_PROFILE_H264_HIGH_10_INTRA:
av_log(avctx, AV_LOG_ERROR, "H.264 10-bit profiles "
@@ -937,25 +934,9 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
av_log(avctx, AV_LOG_ERROR, "H.264 non-4:2:0 profiles "
"are not supported.\n");
return AVERROR_PATCHWELCOME;
- default:
- av_log(avctx, AV_LOG_ERROR, "Unknown H.264 profile %d.\n",
- avctx->profile);
- return AVERROR(EINVAL);
- }
- if (priv->low_power) {
-#if VA_CHECK_VERSION(0, 39, 2)
- ctx->va_entrypoint = VAEntrypointEncSliceLP;
-#else
- av_log(avctx, AV_LOG_ERROR, "Low-power encoding is not "
- "supported with this VAAPI version.\n");
- return AVERROR(EINVAL);
-#endif
- } else {
- ctx->va_entrypoint = VAEntrypointEncSlice;
}

- // Only 8-bit encode is supported.
- ctx->va_rt_format = VA_RT_FORMAT_YUV420;
+ ctx->low_power = priv->low_power;

if (avctx->bit_rate > 0) {
if (avctx->rc_max_rate == avctx->bit_rate)
@@ -1022,7 +1003,7 @@ static const AVOption vaapi_encode_h264_options[] = {

{ "profile", "Set profile (profile_idc and constraint_set*_flag)",
OFFSET(profile), AV_OPT_TYPE_INT,
- { .i64 = FF_PROFILE_H264_HIGH }, 0x0000, 0xffff, FLAGS, "profile" },
+ { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xffff, FLAGS, "profile" },

#define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
{ .i64 = value }, 0, 0, FLAGS, "profile"
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 8f191efc4b..9fa16593d0 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -1025,7 +1025,17 @@ static av_cold int vaapi_encode_h265_configure(AVCodecContext *avctx)
return 0;
}

+static const VAAPIEncodeProfile vaapi_encode_h265_profiles[] = {
+ { FF_PROFILE_HEVC_MAIN, 8, 3, 1, 1, VAProfileHEVCMain },
+#if VA_CHECK_VERSION(0, 37, 0)
+ { FF_PROFILE_HEVC_MAIN_10, 10, 3, 1, 1, VAProfileHEVCMain10 },
+#endif
+ { FF_PROFILE_UNKNOWN }
+};
+
static const VAAPIEncodeType vaapi_encode_type_h265 = {
+ .profiles = vaapi_encode_h265_profiles,
+
.configure = &vaapi_encode_h265_configure,

.sequence_params_size = sizeof(VAEncSequenceParameterBufferHEVC),
@@ -1058,29 +1068,6 @@ static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx)
if (avctx->level == FF_LEVEL_UNKNOWN)
avctx->level = priv->level;

- switch (avctx->profile) {
- case FF_PROFILE_HEVC_MAIN:
- case FF_PROFILE_UNKNOWN:
- ctx->va_profile = VAProfileHEVCMain;
- ctx->va_rt_format = VA_RT_FORMAT_YUV420;
- break;
- case FF_PROFILE_HEVC_MAIN_10:
-#ifdef VA_RT_FORMAT_YUV420_10BPP
- ctx->va_profile = VAProfileHEVCMain10;
- ctx->va_rt_format = VA_RT_FORMAT_YUV420_10BPP;
- break;
-#else
- av_log(avctx, AV_LOG_ERROR, "10-bit encoding is not "
- "supported with this VAAPI version.\n");
- return AVERROR(ENOSYS);
-#endif
- default:
- av_log(avctx, AV_LOG_ERROR, "Unknown H.265 profile %d.\n",
- avctx->profile);
- return AVERROR(EINVAL);
- }
- ctx->va_entrypoint = VAEntrypointEncSlice;
-
if (avctx->bit_rate > 0) {
if (avctx->rc_max_rate == avctx->bit_rate)
ctx->va_rc_mode = VA_RC_CBR;
@@ -1120,7 +1107,7 @@ static const AVOption vaapi_encode_h265_options[] = {

{ "profile", "Set profile (general_profile_idc)",
OFFSET(profile), AV_OPT_TYPE_INT,
- { .i64 = FF_PROFILE_HEVC_MAIN }, 0x00, 0xff, FLAGS, "profile" },
+ { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xff, FLAGS, "profile" },

#define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
{ .i64 = value }, 0, 0, FLAGS, "profile"
diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index 481981a71c..b328beaa09 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -359,7 +359,15 @@ static av_cold int vaapi_encode_mjpeg_configure(AVCodecContext *avctx)
return 0;
}

+static const VAAPIEncodeProfile vaapi_encode_mjpeg_profiles[] = {
+ { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
+ 8, 3, 1, 1, VAProfileJPEGBaseline },
+ { FF_PROFILE_UNKNOWN }
+};
+
static const VAAPIEncodeType vaapi_encode_type_mjpeg = {
+ .profiles = vaapi_encode_mjpeg_profiles,
+
.configure = &vaapi_encode_mjpeg_configure,

.picture_params_size = sizeof(VAEncPictureParameterBufferJPEG),
@@ -380,11 +388,6 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx)

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;

// The JPEG image header - see note above.
diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index 5577fa9e04..7f6c7833da 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -552,7 +552,15 @@ static av_cold int vaapi_encode_mpeg2_configure(AVCodecContext *avctx)
return 0;
}

+static const VAAPIEncodeProfile vaapi_encode_mpeg2_profiles[] = {
+ { FF_PROFILE_MPEG2_MAIN, 8, 3, 1, 1, VAProfileMPEG2Main },
+ { FF_PROFILE_MPEG2_SIMPLE, 8, 3, 1, 1, VAProfileMPEG2Simple },
+ { FF_PROFILE_UNKNOWN }
+};
+
static const VAAPIEncodeType vaapi_encode_type_mpeg2 = {
+ .profiles = vaapi_encode_mpeg2_profiles,
+
.configure = &vaapi_encode_mpeg2_configure,

.sequence_params_size = sizeof(VAEncSequenceParameterBufferMPEG2),
@@ -577,31 +585,6 @@ static av_cold int vaapi_encode_mpeg2_init(AVCodecContext *avctx)

ctx->codec = &vaapi_encode_type_mpeg2;

- switch (avctx->profile) {
- case FF_PROFILE_MPEG2_SIMPLE:
- ctx->va_profile = VAProfileMPEG2Simple;
- break;
- case FF_PROFILE_MPEG2_MAIN:
- ctx->va_profile = VAProfileMPEG2Main;
- break;
- case FF_PROFILE_MPEG2_422:
- av_log(avctx, AV_LOG_ERROR, "MPEG-2 4:2:2 profile "
- "is not supported.\n");
- return AVERROR_PATCHWELCOME;
- case FF_PROFILE_MPEG2_HIGH:
- av_log(avctx, AV_LOG_ERROR, "MPEG-2 high profile "
- "is not supported.\n");
- return AVERROR_PATCHWELCOME;
- case FF_PROFILE_MPEG2_SS:
- case FF_PROFILE_MPEG2_SNR_SCALABLE:
- av_log(avctx, AV_LOG_ERROR, "MPEG-2 scalable profiles "
- "are not supported.\n");
- return AVERROR_PATCHWELCOME;
- default:
- av_log(avctx, AV_LOG_ERROR, "Unknown MPEG-2 profile %d.\n",
- avctx->profile);
- return AVERROR(EINVAL);
- }
switch (avctx->level) {
case 4: // High
case 6: // High 1440
@@ -620,8 +603,6 @@ static av_cold int vaapi_encode_mpeg2_init(AVCodecContext *avctx)
return AVERROR(EINVAL);
}

- ctx->va_entrypoint = VAEntrypointEncSlice;
- ctx->va_rt_format = VA_RT_FORMAT_YUV420;
ctx->va_rc_mode = VA_RC_CQP;

ctx->va_packed_headers = VA_ENC_PACKED_HEADER_SEQUENCE |
@@ -643,7 +624,6 @@ static av_cold int vaapi_encode_mpeg2_close(AVCodecContext *avctx)
}

static const AVCodecDefault vaapi_encode_mpeg2_defaults[] = {
- { "profile", "4" },
{ "level", "4" },
{ "bf", "1" },
{ "g", "120" },
diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c
index 6cdd30abda..a502df7885 100644
--- a/libavcodec/vaapi_encode_vp8.c
+++ b/libavcodec/vaapi_encode_vp8.c
@@ -175,7 +175,14 @@ static av_cold int vaapi_encode_vp8_configure(AVCodecContext *avctx)
return 0;
}

+static const VAAPIEncodeProfile vaapi_encode_vp8_profiles[] = {
+ { 0 /* VP8 has no profiles */, 8, 3, 1, 1, VAProfileVP8Version0_3 },
+ { FF_PROFILE_UNKNOWN }
+};
+
static const VAAPIEncodeType vaapi_encode_type_vp8 = {
+ .profiles = vaapi_encode_vp8_profiles,
+
.configure = &vaapi_encode_vp8_configure,

.sequence_params_size = sizeof(VAEncSequenceParameterBufferVP8),
@@ -198,10 +205,6 @@ static av_cold int vaapi_encode_vp8_init(AVCodecContext *avctx)

ctx->codec = &vaapi_encode_type_vp8;

- ctx->va_profile = VAProfileVP8Version0_3;
- ctx->va_entrypoint = VAEntrypointEncSlice;
- ctx->va_rt_format = VA_RT_FORMAT_YUV420;
-
if (avctx->flags & AV_CODEC_FLAG_QSCALE) {
ctx->va_rc_mode = VA_RC_CQP;
} else if (avctx->bit_rate > 0) {
diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index bf99597e4c..88c0ce3b0a 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -203,7 +203,15 @@ static av_cold int vaapi_encode_vp9_configure(AVCodecContext *avctx)
return 0;
}

+static const VAAPIEncodeProfile vaapi_encode_vp9_profiles[] = {
+ { FF_PROFILE_VP9_0, 8, 3, 1, 1, VAProfileVP9Profile0 },
+ { FF_PROFILE_VP9_2, 10, 3, 1, 1, VAProfileVP9Profile2 },
+ { FF_PROFILE_UNKNOWN }
+};
+
static const VAAPIEncodeType vaapi_encode_type_vp9 = {
+ .profiles = vaapi_encode_vp9_profiles,
+
.configure = &vaapi_encode_vp9_configure,

.sequence_params_size = sizeof(VAEncSequenceParameterBufferVP9),
@@ -219,31 +227,6 @@ static av_cold int vaapi_encode_vp9_init(AVCodecContext *avctx)

ctx->codec = &vaapi_encode_type_vp9;

- switch (avctx->profile) {
- case FF_PROFILE_VP9_0:
- case FF_PROFILE_UNKNOWN:
- ctx->va_profile = VAProfileVP9Profile0;
- ctx->va_rt_format = VA_RT_FORMAT_YUV420;
- break;
- case FF_PROFILE_VP9_1:
- av_log(avctx, AV_LOG_ERROR, "VP9 profile 1 is not "
- "supported.\n");
- return AVERROR_PATCHWELCOME;
- case FF_PROFILE_VP9_2:
- ctx->va_profile = VAProfileVP9Profile2;
- ctx->va_rt_format = VA_RT_FORMAT_YUV420_10BPP;
- break;
- case FF_PROFILE_VP9_3:
- av_log(avctx, AV_LOG_ERROR, "VP9 profile 3 is not "
- "supported.\n");
- return AVERROR_PATCHWELCOME;
- default:
- av_log(avctx, AV_LOG_ERROR, "Unknown VP9 profile %d.\n",
- avctx->profile);
- return AVERROR(EINVAL);
- }
- ctx->va_entrypoint = VAEntrypointEncSlice;
-
if (avctx->flags & AV_CODEC_FLAG_QSCALE) {
ctx->va_rc_mode = VA_RC_CQP;
} else if (avctx->bit_rate > 0) {
@@ -276,7 +259,6 @@ static const AVOption vaapi_encode_vp9_options[] = {
};

static const AVCodecDefault vaapi_encode_vp9_defaults[] = {
- { "profile", "0" },
{ "b", "0" },
{ "bf", "0" },
{ "g", "250" },
--
2.16.3
Xiang, Haihao
2018-06-12 07:22:39 UTC
Permalink
Post by Mark Thompson
Previously there was one fixed choice for each codec (e.g. H.265 -> Main
profile), and using anything else then required an explicit option from
the user. This changes to selecting the profile based on the input format
and the set of profiles actually supported by the driver (e.g. P010 input
will choose Main 10 profile for H.265 if the driver supports it).
The entrypoint and render target format are also chosen dynamically in the
same way, removing those explicit selections from the per-codec code.
---
doc/encoders.texi | 3 +
libavcodec/vaapi_encode.c | 271 ++++++++++++++++++++++++++++++++-------
-
libavcodec/vaapi_encode.h | 43 +++++--
libavcodec/vaapi_encode_h264.c | 45 ++-----
libavcodec/vaapi_encode_h265.c | 35 ++----
libavcodec/vaapi_encode_mjpeg.c | 13 +-
libavcodec/vaapi_encode_mpeg2.c | 36 ++----
libavcodec/vaapi_encode_vp8.c | 11 +-
libavcodec/vaapi_encode_vp9.c | 34 ++---
9 files changed, 310 insertions(+), 181 deletions(-)
diff --git a/doc/encoders.texi b/doc/encoders.texi
index 7b095754d1..16be6359b3 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@option{bf} / @option{max_b_frames}
@item
@option{profile}
+
+If not set, this will be determined automatically from the format of the input
+frames and the profiles supported by the driver.
@item
@option{level}
@item
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 27ce792fbe..6104470b31 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -983,70 +983,247 @@ static av_cold void
vaapi_encode_add_global_param(AVCodecContext *avctx,
++ctx->nb_global_params;
}
-static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
+typedef struct VAAPIEncodeRTFormat {
+ const char *name;
+ unsigned int value;
+ int depth;
+ int components;
How about adding a prefix of 'nb_' to this field? I think nb_components is more
readable.
Post by Mark Thompson
+ int log2_chroma_w;
+ int log2_chroma_h;
+} VAAPIEncodeRTFormat;
+
+static const VAAPIEncodeRTFormat vaapi_encode_rt_formats[] = {
+ { "YUV400", VA_RT_FORMAT_YUV400, 8, 1, },
+ { "YUV420", VA_RT_FORMAT_YUV420, 8, 3, 1, 1 },
+ { "YUV422", VA_RT_FORMAT_YUV422, 8, 3, 1, 0 },
+ { "YUV444", VA_RT_FORMAT_YUV444, 8, 3, 0, 0 },
+ { "YUV411", VA_RT_FORMAT_YUV411, 8, 3, 2, 0 },
+#if VA_CHECK_VERSION(0, 38, 1)
+ { "YUV420_10", VA_RT_FORMAT_YUV420_10BPP, 10, 3, 1, 1 },
+#endif
+};
+
+static const VAEntrypoint vaapi_encode_entrypoints_normal[] = {
+ VAEntrypointEncSlice,
+ VAEntrypointEncPicture,
+#if VA_CHECK_VERSION(0, 39, 2)
+ VAEntrypointEncSliceLP,
+#endif
+ 0
+};
+#if VA_CHECK_VERSION(0, 39, 2)
+static const VAEntrypoint vaapi_encode_entrypoints_low_power[] = {
+ VAEntrypointEncSliceLP,
+ 0
+};
+#endif
+
+static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAProfile *va_profiles = NULL;
+ VAEntrypoint *va_entrypoints = NULL;
VAStatus vas;
- int i, n, err;
- VAProfile *profiles = NULL;
- VAEntrypoint *entrypoints = NULL;
- VAConfigAttrib attr[] = {
- { VAConfigAttribRTFormat },
- { VAConfigAttribRateControl },
- { VAConfigAttribEncMaxRefFrames },
- { VAConfigAttribEncPackedHeaders },
- };
+ const VAEntrypoint *usable_entrypoints;
+ const VAAPIEncodeProfile *profile;
+ const AVPixFmtDescriptor *desc;
+ VAConfigAttrib rt_format_attr;
+ const VAAPIEncodeRTFormat *rt_format;
+ int i, j, n, depth, err;
+
+
+ if (ctx->low_power) {
+#if VA_CHECK_VERSION(0, 39, 2)
+ usable_entrypoints = vaapi_encode_entrypoints_low_power;
+#else
+ av_log(avctx, AV_LOG_ERROR, "Low-power encoding is not "
+ "supported with this VAAPI version.\n");
Is it possible to report the minimal VAAPI version in the log in case user
doesn't know the requirement on vaapi version 0.39.2?
Post by Mark Thompson
+ return AVERROR(EINVAL);
+#endif
+ } else {
+ usable_entrypoints = vaapi_encode_entrypoints_normal;
+ }
+
+ desc = av_pix_fmt_desc_get(ctx->input_frames->sw_format);
+ if (!desc) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%d).\n",
+ ctx->input_frames->sw_format);
+ return AVERROR(EINVAL);
+ }
+ depth = desc->comp[0].depth;
+ for (i = 1; i < desc->nb_components; i++) {
+ if (desc->comp[i].depth != depth) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%s).\n",
+ desc->name);
+ return AVERROR(EINVAL);
+ }
+ }
+ av_log(avctx, AV_LOG_VERBOSE, "Input surface format is %s.\n",
+ desc->name);
n = vaMaxNumProfiles(ctx->hwctx->display);
- profiles = av_malloc_array(n, sizeof(VAProfile));
- if (!profiles) {
+ va_profiles = av_malloc_array(n, sizeof(VAProfile));
+ if (!va_profiles) {
err = AVERROR(ENOMEM);
goto fail;
}
- vas = vaQueryConfigProfiles(ctx->hwctx->display, profiles, &n);
+ vas = vaQueryConfigProfiles(ctx->hwctx->display, va_profiles, &n);
if (vas != VA_STATUS_SUCCESS) {
- av_log(ctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",
+ av_log(avctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",
vas, vaErrorStr(vas));
- err = AVERROR(ENOSYS);
+ err = AVERROR_EXTERNAL;
goto fail;
}
- for (i = 0; i < n; i++) {
- if (profiles[i] == ctx->va_profile)
- break;
+
+ av_assert0(ctx->codec->profiles);
+ for (i = 0; (ctx->codec->profiles[i].av_profile !=
+ FF_PROFILE_UNKNOWN); i++) {
+ profile = &ctx->codec->profiles[i];
+ if (depth != profile->depth ||
+ desc->nb_components != profile->components)
+ continue;
+ if (desc->nb_components > 1 &&
+ (desc->log2_chroma_w != profile->log2_chroma_w ||
+ desc->log2_chroma_h != profile->log2_chroma_h))
+ continue;
+ if (avctx->profile != profile->av_profile &&
+ avctx->profile != FF_PROFILE_UNKNOWN)
+ continue;
+
+ for (j = 0; j < n; j++) {
+ if (va_profiles[j] == profile->va_profile)
+ break;
+ }
+ if (j >= n) {
+ av_log(avctx, AV_LOG_VERBOSE, "Matching profile %d is "
+ "not supported by driver.\n", profile->va_profile);
Is it possible to report the profile string in the log as what you did below?
Post by Mark Thompson
+ continue;
+ }
+
+ ctx->profile = profile;
+ break;
}
- if (i >= n) {
- av_log(ctx, AV_LOG_ERROR, "Encoding profile not found (%d).\n",
- ctx->va_profile);
- err = AVERROR(ENOSYS);
- goto fail;
+ if (!ctx->profile) {
+ av_log(avctx, AV_LOG_ERROR, "No usable encoding profile found.\n");
+ return AVERROR(ENOSYS);
Set err to AVERROR(ENOSYS) then goto fail, otherwise it will result in memory
leak.
Post by Mark Thompson
}
+ avctx->profile = profile->av_profile;
+ ctx->va_profile = profile->va_profile;
+#if VA_CHECK_VERSION(1, 0, 0)
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %s (%d).\n",
+ vaProfileStr(ctx->va_profile), ctx->va_profile);
+#else
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %d.\n",
+ ctx->va_profile);
+#endif
+
n = vaMaxNumEntrypoints(ctx->hwctx->display);
- entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
- if (!entrypoints) {
+ va_entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
+ if (!va_entrypoints) {
err = AVERROR(ENOMEM);
goto fail;
}
vas = vaQueryConfigEntrypoints(ctx->hwctx->display, ctx->va_profile,
- entrypoints, &n);
+ va_entrypoints, &n);
if (vas != VA_STATUS_SUCCESS) {
- av_log(ctx, AV_LOG_ERROR, "Failed to query entrypoints for "
+ av_log(avctx, AV_LOG_ERROR, "Failed to query entrypoints for "
"profile %u: %d (%s).\n", ctx->va_profile,
Log profile string?
Post by Mark Thompson
vas, vaErrorStr(vas));
- err = AVERROR(ENOSYS);
+ err = AVERROR_EXTERNAL;
goto fail;
}
+
for (i = 0; i < n; i++) {
- if (entrypoints[i] == ctx->va_entrypoint)
+ for (j = 0; usable_entrypoints[j]; j++) {
+ if (va_entrypoints[i] == usable_entrypoints[j])
+ break;
+ }
+ if (usable_entrypoints[j])
break;
}
if (i >= n) {
- av_log(ctx, AV_LOG_ERROR, "Encoding entrypoint not found "
- "(%d / %d).\n", ctx->va_profile, ctx->va_entrypoint);
+ av_log(avctx, AV_LOG_ERROR, "No usable encoding entrypoint found "
+ "for profile %d.\n", ctx->va_profile);
Log profile string?
Post by Mark Thompson
+ err = AVERROR(ENOSYS);
+ goto fail;
+ }
+
+ ctx->va_entrypoint = va_entrypoints[i];
+#if VA_CHECK_VERSION(1, 0, 0)
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %s (%d).\n",
+ vaEntrypointStr(ctx->va_entrypoint), ctx->va_entrypoint);
+#else
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %d.\n",
+ ctx->va_entrypoint);
+#endif
+
+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_encode_rt_formats); i++) {
+ rt_format = &vaapi_encode_rt_formats[i];
+ if (rt_format->depth == depth &&
+ rt_format->components == profile->components &&
+ rt_format->log2_chroma_w == profile->log2_chroma_w &&
+ rt_format->log2_chroma_h == profile->log2_chroma_h)
+ break;
+ }
+ if (i >= FF_ARRAY_ELEMS(vaapi_encode_rt_formats)) {
+ av_log(avctx, AV_LOG_ERROR, "No usable render target format "
+ "found for profile %d entrypoint %d.\n",
+ ctx->va_profile, ctx->va_entrypoint);
Log profile and entrypoint strings?
Post by Mark Thompson
err = AVERROR(ENOSYS);
goto fail;
}
+ rt_format_attr = (VAConfigAttrib) { VAConfigAttribRTFormat };
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile, ctx->va_entrypoint,
+ &rt_format_attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query RT format "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ err = AVERROR_EXTERNAL;
+ goto fail;
+ }
+
+ if (rt_format_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_VERBOSE, "RT format config attribute not "
+ "supported by driver: assuming surface RT format %s "
+ "is valid.\n", rt_format->name);
I think it would be better to log it as a warning.
Post by Mark Thompson
+ } else if (!(rt_format_attr.value & rt_format->value)) {
+ av_log(avctx, AV_LOG_ERROR, "Surface RT format %s not supported "
+ "by driver for encoding profile %d entrypoint %d.\n",
+ rt_format->name, ctx->va_profile, ctx->va_entrypoint);
+ err = AVERROR(ENOSYS);
+ goto fail;
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI render target "
+ "format %s (%#x).\n", rt_format->name, rt_format->value);
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRTFormat,
+ .value = rt_format->value,
+ };
+ }
+
+ err = 0;
+ av_freep(&va_profiles);
+ av_freep(&va_entrypoints);
+ return err;
+}
+
+static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
+{
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAStatus vas;
+ int i;
+
+ VAConfigAttrib attr[] = {
+ { VAConfigAttribRateControl },
+ { VAConfigAttribEncMaxRefFrames },
+ { VAConfigAttribEncPackedHeaders },
+ };
+
vas = vaGetConfigAttributes(ctx->hwctx->display,
ctx->va_profile, ctx->va_entrypoint,
attr, FF_ARRAY_ELEMS(attr));
@@ -1066,20 +1243,6 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
continue;
}
switch (attr[i].type) {
- 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;
// Hack for backward compatibility: CBR was the only
// usable RC mode for a long time, so old drivers will
@@ -1098,8 +1261,7 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
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;
+ return AVERROR(EINVAL);
}
ctx->config_attributes[ctx->nb_config_attributes++] =
(VAConfigAttrib) {
@@ -1115,8 +1277,7 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
if (avctx->gop_size > 1 && ref_l0 < 1) {
av_log(avctx, AV_LOG_ERROR, "P frames are not "
"supported (%#x).\n", attr[i].value);
- err = AVERROR(EINVAL);
- goto fail;
+ return AVERROR(EINVAL);
}
if (avctx->max_b_frames > 0 && ref_l1 < 1) {
av_log(avctx, AV_LOG_WARNING, "B frames are not "
@@ -1148,11 +1309,7 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
}
}
- err = 0;
- av_freep(&profiles);
- av_freep(&entrypoints);
- return err;
+ return 0;
}
static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
@@ -1407,6 +1564,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
ctx->device = (AVHWDeviceContext*)ctx->device_ref->data;
ctx->hwctx = ctx->device->hwctx;
+ err = vaapi_encode_profile_entrypoint(avctx);
+ if (err < 0)
+ goto fail;
+
err = vaapi_encode_config_attributes(avctx);
if (err < 0)
goto fail;
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index 54dc4a475e..212ab6726d 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -23,6 +23,10 @@
#include <va/va.h>
+#if VA_CHECK_VERSION(1, 0, 0)
+#include <va/va_str.h>
+#endif
+
#include "libavutil/hwcontext.h"
#include "libavutil/hwcontext_vaapi.h"
@@ -86,18 +90,32 @@ typedef struct VAAPIEncodePicture {
VAAPIEncodeSlice *slices;
} VAAPIEncodePicture;
+typedef struct VAAPIEncodeProfile {
+ // lavc profile value (FF_PROFILE_*).
+ int av_profile;
+ // Supported bit depth.
+ int depth;
+ // Number of components.
+ int components;
+ // Chroma subsampling in width dimension.
+ int log2_chroma_w;
+ // Chroma subsampling in height dimension.
+ int log2_chroma_h;
+ // VAAPI profile value.
+ VAProfile va_profile;
+} VAAPIEncodeProfile;
+
typedef struct VAAPIEncodeContext {
const AVClass *class;
// 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;
+ // Global options.
+
+ // Use low power encoding mode.
+ int low_power;
+
// Rate control mode.
unsigned int va_rc_mode;
// Supported packed headers (initially the desired set, modified
@@ -113,6 +131,14 @@ typedef struct VAAPIEncodeContext {
// Everything above this point must be set before calling
// ff_vaapi_encode_init().
+ // Chosen encoding profile details.
+ const VAAPIEncodeProfile *profile;
+
+ // Encoding profile (VAProfile*).
+ VAProfile va_profile;
+ // Encoding entrypoint (VAEntryoint*).
+ VAEntrypoint va_entrypoint;
+
// Configuration attributes to use when creating va_config.
VAConfigAttrib config_attributes[MAX_CONFIG_ATTRIBUTES];
int nb_config_attributes;
@@ -204,8 +230,11 @@ typedef struct VAAPIEncodeContext {
int end_of_stream;
} VAAPIEncodeContext;
-
typedef struct VAAPIEncodeType {
+ // List of supported profiles and corresponding VAAPI profiles.
+ // (Must end with FF_PROFILE_UNKNOWN.)
+ const VAAPIEncodeProfile *profiles;
+
// Perform any extra codec-specific configuration after the
// codec context is initialised (set up the private data and
// add any necessary global parameters).
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index 8f999b2311..ab8bf13ef3 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -866,7 +866,17 @@ static av_cold int
vaapi_encode_h264_configure(AVCodecContext *avctx)
return 0;
}
+static const VAAPIEncodeProfile vaapi_encode_h264_profiles[] = {
+ { FF_PROFILE_H264_HIGH, 8, 3, 1, 1, VAProfileH264High },
+ { FF_PROFILE_H264_MAIN, 8, 3, 1, 1, VAProfileH264Main },
+ { FF_PROFILE_H264_CONSTRAINED_BASELINE,
+ 8, 3, 1, 1, VAProfileH264ConstrainedBaseline },
+ { FF_PROFILE_UNKNOWN }
+};
+
static const VAAPIEncodeType vaapi_encode_type_h264 = {
+ .profiles = vaapi_encode_h264_profiles,
+
.configure = &vaapi_encode_h264_configure,
.sequence_params_size = sizeof(VAEncSequenceParameterBufferH264),
@@ -899,30 +909,17 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
if (avctx->level == FF_LEVEL_UNKNOWN)
avctx->level = priv->level;
+ // Reject unsupported profiles.
switch (avctx->profile) {
av_log(avctx, AV_LOG_WARNING, "H.264 baseline profile is not "
"supported, using constrained baseline profile instead.\n");
avctx->profile = FF_PROFILE_H264_CONSTRAINED_BASELINE;
- ctx->va_profile = VAProfileH264ConstrainedBaseline;
- if (avctx->max_b_frames != 0) {
- avctx->max_b_frames = 0;
- av_log(avctx, AV_LOG_WARNING, "H.264 constrained baseline profile "
- "doesn't support encoding with B frames, disabling them.\n");
- }
- break;
- ctx->va_profile = VAProfileH264Main;
break;
av_log(avctx, AV_LOG_ERROR, "H.264 extended profile "
"is not supported.\n");
return AVERROR_PATCHWELCOME;
- ctx->va_profile = VAProfileH264High;
- break;
av_log(avctx, AV_LOG_ERROR, "H.264 10-bit profiles "
@@ -937,25 +934,9 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
av_log(avctx, AV_LOG_ERROR, "H.264 non-4:2:0 profiles "
"are not supported.\n");
return AVERROR_PATCHWELCOME;
- av_log(avctx, AV_LOG_ERROR, "Unknown H.264 profile %d.\n",
- avctx->profile);
- return AVERROR(EINVAL);
- }
- if (priv->low_power) {
-#if VA_CHECK_VERSION(0, 39, 2)
- ctx->va_entrypoint = VAEntrypointEncSliceLP;
-#else
- av_log(avctx, AV_LOG_ERROR, "Low-power encoding is not "
- "supported with this VAAPI version.\n");
- return AVERROR(EINVAL);
-#endif
- } else {
- ctx->va_entrypoint = VAEntrypointEncSlice;
}
- // Only 8-bit encode is supported.
- ctx->va_rt_format = VA_RT_FORMAT_YUV420;
+ ctx->low_power = priv->low_power;
if (avctx->bit_rate > 0) {
if (avctx->rc_max_rate == avctx->bit_rate)
@@ -1022,7 +1003,7 @@ static const AVOption vaapi_encode_h264_options[] = {
{ "profile", "Set profile (profile_idc and constraint_set*_flag)",
OFFSET(profile), AV_OPT_TYPE_INT,
- { .i64 = FF_PROFILE_H264_HIGH }, 0x0000, 0xffff, FLAGS, "profile" },
+ { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xffff, FLAGS, "profile" },
#define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
{ .i64 = value }, 0, 0, FLAGS, "profile"
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 8f191efc4b..9fa16593d0 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -1025,7 +1025,17 @@ static av_cold int
vaapi_encode_h265_configure(AVCodecContext *avctx)
return 0;
}
+static const VAAPIEncodeProfile vaapi_encode_h265_profiles[] = {
+ { FF_PROFILE_HEVC_MAIN, 8, 3, 1, 1, VAProfileHEVCMain },
+#if VA_CHECK_VERSION(0, 37, 0)
+ { FF_PROFILE_HEVC_MAIN_10, 10, 3, 1, 1, VAProfileHEVCMain10 },
+#endif
+ { FF_PROFILE_UNKNOWN }
+};
+
static const VAAPIEncodeType vaapi_encode_type_h265 = {
+ .profiles = vaapi_encode_h265_profiles,
+
.configure = &vaapi_encode_h265_configure,
.sequence_params_size = sizeof(VAEncSequenceParameterBufferHEVC),
@@ -1058,29 +1068,6 @@ static av_cold int
vaapi_encode_h265_init(AVCodecContext *avctx)
if (avctx->level == FF_LEVEL_UNKNOWN)
avctx->level = priv->level;
- switch (avctx->profile) {
- ctx->va_profile = VAProfileHEVCMain;
- ctx->va_rt_format = VA_RT_FORMAT_YUV420;
- break;
-#ifdef VA_RT_FORMAT_YUV420_10BPP
- ctx->va_profile = VAProfileHEVCMain10;
- ctx->va_rt_format = VA_RT_FORMAT_YUV420_10BPP;
- break;
-#else
- av_log(avctx, AV_LOG_ERROR, "10-bit encoding is not "
- "supported with this VAAPI version.\n");
- return AVERROR(ENOSYS);
-#endif
- av_log(avctx, AV_LOG_ERROR, "Unknown H.265 profile %d.\n",
- avctx->profile);
- return AVERROR(EINVAL);
- }
- ctx->va_entrypoint = VAEntrypointEncSlice;
-
if (avctx->bit_rate > 0) {
if (avctx->rc_max_rate == avctx->bit_rate)
ctx->va_rc_mode = VA_RC_CBR;
@@ -1120,7 +1107,7 @@ static const AVOption vaapi_encode_h265_options[] = {
{ "profile", "Set profile (general_profile_idc)",
OFFSET(profile), AV_OPT_TYPE_INT,
- { .i64 = FF_PROFILE_HEVC_MAIN }, 0x00, 0xff, FLAGS, "profile" },
+ { .i64 = FF_PROFILE_UNKNOWN }, FF_PROFILE_UNKNOWN, 0xff, FLAGS, "profile" },
#define PROFILE(name, value) name, NULL, 0, AV_OPT_TYPE_CONST, \
{ .i64 = value }, 0, 0, FLAGS, "profile"
diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index 481981a71c..b328beaa09 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -359,7 +359,15 @@ static av_cold int
vaapi_encode_mjpeg_configure(AVCodecContext *avctx)
return 0;
}
+static const VAAPIEncodeProfile vaapi_encode_mjpeg_profiles[] = {
+ { FF_PROFILE_MJPEG_HUFFMAN_BASELINE_DCT,
+ 8, 3, 1, 1, VAProfileJPEGBaseline },
+ { FF_PROFILE_UNKNOWN }
+};
+
static const VAAPIEncodeType vaapi_encode_type_mjpeg = {
+ .profiles = vaapi_encode_mjpeg_profiles,
+
.configure = &vaapi_encode_mjpeg_configure,
.picture_params_size = sizeof(VAEncPictureParameterBufferJPEG),
@@ -380,11 +388,6 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext *avctx)
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;
// The JPEG image header - see note above.
diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index 5577fa9e04..7f6c7833da 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -552,7 +552,15 @@ static av_cold int
vaapi_encode_mpeg2_configure(AVCodecContext *avctx)
return 0;
}
+static const VAAPIEncodeProfile vaapi_encode_mpeg2_profiles[] = {
+ { FF_PROFILE_MPEG2_MAIN, 8, 3, 1, 1, VAProfileMPEG2Main },
+ { FF_PROFILE_MPEG2_SIMPLE, 8, 3, 1, 1, VAProfileMPEG2Simple },
+ { FF_PROFILE_UNKNOWN }
+};
+
static const VAAPIEncodeType vaapi_encode_type_mpeg2 = {
+ .profiles = vaapi_encode_mpeg2_profiles,
+
.configure = &vaapi_encode_mpeg2_configure,
.sequence_params_size = sizeof(VAEncSequenceParameterBufferMPEG2),
@@ -577,31 +585,6 @@ static av_cold int vaapi_encode_mpeg2_init(AVCodecContext *avctx)
ctx->codec = &vaapi_encode_type_mpeg2;
- switch (avctx->profile) {
- ctx->va_profile = VAProfileMPEG2Simple;
- break;
- ctx->va_profile = VAProfileMPEG2Main;
- break;
- av_log(avctx, AV_LOG_ERROR, "MPEG-2 4:2:2 profile "
- "is not supported.\n");
- return AVERROR_PATCHWELCOME;
- av_log(avctx, AV_LOG_ERROR, "MPEG-2 high profile "
- "is not supported.\n");
- return AVERROR_PATCHWELCOME;
- av_log(avctx, AV_LOG_ERROR, "MPEG-2 scalable profiles "
- "are not supported.\n");
- return AVERROR_PATCHWELCOME;
- av_log(avctx, AV_LOG_ERROR, "Unknown MPEG-2 profile %d.\n",
- avctx->profile);
- return AVERROR(EINVAL);
- }
switch (avctx->level) {
case 4: // High
case 6: // High 1440
@@ -620,8 +603,6 @@ static av_cold int vaapi_encode_mpeg2_init(AVCodecContext *avctx)
return AVERROR(EINVAL);
}
- ctx->va_entrypoint = VAEntrypointEncSlice;
- ctx->va_rt_format = VA_RT_FORMAT_YUV420;
ctx->va_rc_mode = VA_RC_CQP;
ctx->va_packed_headers = VA_ENC_PACKED_HEADER_SEQUENCE |
@@ -643,7 +624,6 @@ static av_cold int vaapi_encode_mpeg2_close(AVCodecContext *avctx)
}
static const AVCodecDefault vaapi_encode_mpeg2_defaults[] = {
- { "profile", "4" },
{ "level", "4" },
{ "bf", "1" },
{ "g", "120" },
diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c
index 6cdd30abda..a502df7885 100644
--- a/libavcodec/vaapi_encode_vp8.c
+++ b/libavcodec/vaapi_encode_vp8.c
@@ -175,7 +175,14 @@ static av_cold int
vaapi_encode_vp8_configure(AVCodecContext *avctx)
return 0;
}
+static const VAAPIEncodeProfile vaapi_encode_vp8_profiles[] = {
+ { 0 /* VP8 has no profiles */, 8, 3, 1, 1, VAProfileVP8Version0_3 },
+ { FF_PROFILE_UNKNOWN }
+};
+
static const VAAPIEncodeType vaapi_encode_type_vp8 = {
+ .profiles = vaapi_encode_vp8_profiles,
+
.configure = &vaapi_encode_vp8_configure,
.sequence_params_size = sizeof(VAEncSequenceParameterBufferVP8),
@@ -198,10 +205,6 @@ static av_cold int vaapi_encode_vp8_init(AVCodecContext *avctx)
ctx->codec = &vaapi_encode_type_vp8;
- ctx->va_profile = VAProfileVP8Version0_3;
- ctx->va_entrypoint = VAEntrypointEncSlice;
- ctx->va_rt_format = VA_RT_FORMAT_YUV420;
-
if (avctx->flags & AV_CODEC_FLAG_QSCALE) {
ctx->va_rc_mode = VA_RC_CQP;
} else if (avctx->bit_rate > 0) {
diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index bf99597e4c..88c0ce3b0a 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -203,7 +203,15 @@ static av_cold int
vaapi_encode_vp9_configure(AVCodecContext *avctx)
return 0;
}
+static const VAAPIEncodeProfile vaapi_encode_vp9_profiles[] = {
+ { FF_PROFILE_VP9_0, 8, 3, 1, 1, VAProfileVP9Profile0 },
+ { FF_PROFILE_VP9_2, 10, 3, 1, 1, VAProfileVP9Profile2 },
+ { FF_PROFILE_UNKNOWN }
+};
+
static const VAAPIEncodeType vaapi_encode_type_vp9 = {
+ .profiles = vaapi_encode_vp9_profiles,
+
.configure = &vaapi_encode_vp9_configure,
.sequence_params_size = sizeof(VAEncSequenceParameterBufferVP9),
@@ -219,31 +227,6 @@ static av_cold int vaapi_encode_vp9_init(AVCodecContext *avctx)
ctx->codec = &vaapi_encode_type_vp9;
- switch (avctx->profile) {
- ctx->va_profile = VAProfileVP9Profile0;
- ctx->va_rt_format = VA_RT_FORMAT_YUV420;
- break;
- av_log(avctx, AV_LOG_ERROR, "VP9 profile 1 is not "
- "supported.\n");
- return AVERROR_PATCHWELCOME;
- ctx->va_profile = VAProfileVP9Profile2;
- ctx->va_rt_format = VA_RT_FORMAT_YUV420_10BPP;
- break;
- av_log(avctx, AV_LOG_ERROR, "VP9 profile 3 is not "
- "supported.\n");
- return AVERROR_PATCHWELCOME;
- av_log(avctx, AV_LOG_ERROR, "Unknown VP9 profile %d.\n",
- avctx->profile);
- return AVERROR(EINVAL);
- }
- ctx->va_entrypoint = VAEntrypointEncSlice;
-
if (avctx->flags & AV_CODEC_FLAG_QSCALE) {
ctx->va_rc_mode = VA_RC_CQP;
} else if (avctx->bit_rate > 0) {
@@ -276,7 +259,6 @@ static const AVOption vaapi_encode_vp9_options[] = {
};
static const AVCodecDefault vaapi_encode_vp9_defaults[] = {
- { "profile", "0" },
{ "b", "0" },
{ "bf", "0" },
{ "g", "250" },
Mark Thompson
2018-06-13 22:27:51 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
Previously there was one fixed choice for each codec (e.g. H.265 -> Main
profile), and using anything else then required an explicit option from
the user. This changes to selecting the profile based on the input format
and the set of profiles actually supported by the driver (e.g. P010 input
will choose Main 10 profile for H.265 if the driver supports it).
The entrypoint and render target format are also chosen dynamically in the
same way, removing those explicit selections from the per-codec code.
---
doc/encoders.texi | 3 +
libavcodec/vaapi_encode.c | 271 ++++++++++++++++++++++++++++++++-------
-
libavcodec/vaapi_encode.h | 43 +++++--
libavcodec/vaapi_encode_h264.c | 45 ++-----
libavcodec/vaapi_encode_h265.c | 35 ++----
libavcodec/vaapi_encode_mjpeg.c | 13 +-
libavcodec/vaapi_encode_mpeg2.c | 36 ++----
libavcodec/vaapi_encode_vp8.c | 11 +-
libavcodec/vaapi_encode_vp9.c | 34 ++---
9 files changed, 310 insertions(+), 181 deletions(-)
...
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 27ce792fbe..6104470b31 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -983,70 +983,247 @@ static av_cold void
vaapi_encode_add_global_param(AVCodecContext *avctx,
++ctx->nb_global_params;
}
-static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
+typedef struct VAAPIEncodeRTFormat {
+ const char *name;
+ unsigned int value;
+ int depth;
+ int components;
How about adding a prefix of 'nb_' to this field? I think nb_components is more
readable.
Sure. It should match the one in VAAPIEncodeProfile, so I'll change it there too.
Post by Xiang, Haihao
Post by Mark Thompson
+ int log2_chroma_w;
+ int log2_chroma_h;
+} VAAPIEncodeRTFormat;
+
+static const VAAPIEncodeRTFormat vaapi_encode_rt_formats[] = {
+ { "YUV400", VA_RT_FORMAT_YUV400, 8, 1, },
+ { "YUV420", VA_RT_FORMAT_YUV420, 8, 3, 1, 1 },
+ { "YUV422", VA_RT_FORMAT_YUV422, 8, 3, 1, 0 },
+ { "YUV444", VA_RT_FORMAT_YUV444, 8, 3, 0, 0 },
+ { "YUV411", VA_RT_FORMAT_YUV411, 8, 3, 2, 0 },
+#if VA_CHECK_VERSION(0, 38, 1)
+ { "YUV420_10", VA_RT_FORMAT_YUV420_10BPP, 10, 3, 1, 1 },
+#endif
+};
+
+static const VAEntrypoint vaapi_encode_entrypoints_normal[] = {
+ VAEntrypointEncSlice,
+ VAEntrypointEncPicture,
+#if VA_CHECK_VERSION(0, 39, 2)
+ VAEntrypointEncSliceLP,
+#endif
+ 0
+};
+#if VA_CHECK_VERSION(0, 39, 2)
+static const VAEntrypoint vaapi_encode_entrypoints_low_power[] = {
+ VAEntrypointEncSliceLP,
+ 0
+};
+#endif
+
+static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAProfile *va_profiles = NULL;
+ VAEntrypoint *va_entrypoints = NULL;
VAStatus vas;
- int i, n, err;
- VAProfile *profiles = NULL;
- VAEntrypoint *entrypoints = NULL;
- VAConfigAttrib attr[] = {
- { VAConfigAttribRTFormat },
- { VAConfigAttribRateControl },
- { VAConfigAttribEncMaxRefFrames },
- { VAConfigAttribEncPackedHeaders },
- };
+ const VAEntrypoint *usable_entrypoints;
+ const VAAPIEncodeProfile *profile;
+ const AVPixFmtDescriptor *desc;
+ VAConfigAttrib rt_format_attr;
+ const VAAPIEncodeRTFormat *rt_format;
+ int i, j, n, depth, err;
+
+
+ if (ctx->low_power) {
+#if VA_CHECK_VERSION(0, 39, 2)
+ usable_entrypoints = vaapi_encode_entrypoints_low_power;
+#else
+ av_log(avctx, AV_LOG_ERROR, "Low-power encoding is not "
+ "supported with this VAAPI version.\n");
Is it possible to report the minimal VAAPI version in the log in case user
doesn't know the requirement on vaapi version 0.39.2?
The VAAPI version is pretty much useless to users (without reading any source code, how would you find what VAAPI version 0.39.2 means?).

Maybe libva version? That's still not quite right, though, because it's more "not supported in this build" (and will continue to not be supported if you upgrade libva but don't rebuild with the new headers).
Post by Xiang, Haihao
Post by Mark Thompson
+ return AVERROR(EINVAL);
+#endif
+ } else {
+ usable_entrypoints = vaapi_encode_entrypoints_normal;
+ }
+
+ desc = av_pix_fmt_desc_get(ctx->input_frames->sw_format);
+ if (!desc) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%d).\n",
+ ctx->input_frames->sw_format);
+ return AVERROR(EINVAL);
+ }
+ depth = desc->comp[0].depth;
+ for (i = 1; i < desc->nb_components; i++) {
+ if (desc->comp[i].depth != depth) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%s).\n",
+ desc->name);
+ return AVERROR(EINVAL);
+ }
+ }
+ av_log(avctx, AV_LOG_VERBOSE, "Input surface format is %s.\n",
+ desc->name);
n = vaMaxNumProfiles(ctx->hwctx->display);
- profiles = av_malloc_array(n, sizeof(VAProfile));
- if (!profiles) {
+ va_profiles = av_malloc_array(n, sizeof(VAProfile));
+ if (!va_profiles) {
err = AVERROR(ENOMEM);
goto fail;
}
- vas = vaQueryConfigProfiles(ctx->hwctx->display, profiles, &n);
+ vas = vaQueryConfigProfiles(ctx->hwctx->display, va_profiles, &n);
if (vas != VA_STATUS_SUCCESS) {
- av_log(ctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",
+ av_log(avctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",
vas, vaErrorStr(vas));
- err = AVERROR(ENOSYS);
+ err = AVERROR_EXTERNAL;
goto fail;
}
- for (i = 0; i < n; i++) {
- if (profiles[i] == ctx->va_profile)
- break;
+
+ av_assert0(ctx->codec->profiles);
+ for (i = 0; (ctx->codec->profiles[i].av_profile !=
+ FF_PROFILE_UNKNOWN); i++) {
+ profile = &ctx->codec->profiles[i];
+ if (depth != profile->depth ||
+ desc->nb_components != profile->components)
+ continue;
+ if (desc->nb_components > 1 &&
+ (desc->log2_chroma_w != profile->log2_chroma_w ||
+ desc->log2_chroma_h != profile->log2_chroma_h))
+ continue;
+ if (avctx->profile != profile->av_profile &&
+ avctx->profile != FF_PROFILE_UNKNOWN)
+ continue;
+
+ for (j = 0; j < n; j++) {
+ if (va_profiles[j] == profile->va_profile)
+ break;
+ }
+ if (j >= n) {
+ av_log(avctx, AV_LOG_VERBOSE, "Matching profile %d is "
+ "not supported by driver.\n", profile->va_profile);
Is it possible to report the profile string in the log as what you did below?
The #ifdefs are very nasty, but they seemed merited for the "I chose this one" log line. See below for a different method, not sure it's better.
Post by Xiang, Haihao
Post by Mark Thompson
+ continue;
+ }
+
+ ctx->profile = profile;
+ break;
}
- if (i >= n) {
- av_log(ctx, AV_LOG_ERROR, "Encoding profile not found (%d).\n",
- ctx->va_profile);
- err = AVERROR(ENOSYS);
- goto fail;
+ if (!ctx->profile) {
+ av_log(avctx, AV_LOG_ERROR, "No usable encoding profile found.\n");
+ return AVERROR(ENOSYS);
Set err to AVERROR(ENOSYS) then goto fail, otherwise it will result in memory
leak.
Fixed.
Post by Xiang, Haihao
Post by Mark Thompson
}
+ avctx->profile = profile->av_profile;
+ ctx->va_profile = profile->va_profile;
+#if VA_CHECK_VERSION(1, 0, 0)
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %s (%d).\n",
+ vaProfileStr(ctx->va_profile), ctx->va_profile);
+#else
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %d.\n",
+ ctx->va_profile);
+#endif
+
n = vaMaxNumEntrypoints(ctx->hwctx->display);
- entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
- if (!entrypoints) {
+ va_entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
+ if (!va_entrypoints) {
err = AVERROR(ENOMEM);
goto fail;
}
vas = vaQueryConfigEntrypoints(ctx->hwctx->display, ctx->va_profile,
- entrypoints, &n);
+ va_entrypoints, &n);
if (vas != VA_STATUS_SUCCESS) {
- av_log(ctx, AV_LOG_ERROR, "Failed to query entrypoints for "
+ av_log(avctx, AV_LOG_ERROR, "Failed to query entrypoints for "
"profile %u: %d (%s).\n", ctx->va_profile,
Log profile string?
Post by Mark Thompson
vas, vaErrorStr(vas));
- err = AVERROR(ENOSYS);
+ err = AVERROR_EXTERNAL;
goto fail;
}
+
for (i = 0; i < n; i++) {
- if (entrypoints[i] == ctx->va_entrypoint)
+ for (j = 0; usable_entrypoints[j]; j++) {
+ if (va_entrypoints[i] == usable_entrypoints[j])
+ break;
+ }
+ if (usable_entrypoints[j])
break;
}
if (i >= n) {
- av_log(ctx, AV_LOG_ERROR, "Encoding entrypoint not found "
- "(%d / %d).\n", ctx->va_profile, ctx->va_entrypoint);
+ av_log(avctx, AV_LOG_ERROR, "No usable encoding entrypoint found "
+ "for profile %d.\n", ctx->va_profile);
Log profile string?
Post by Mark Thompson
+ err = AVERROR(ENOSYS);
+ goto fail;
+ }
+
+ ctx->va_entrypoint = va_entrypoints[i];
+#if VA_CHECK_VERSION(1, 0, 0)
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %s (%d).\n",
+ vaEntrypointStr(ctx->va_entrypoint), ctx->va_entrypoint);
+#else
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %d.\n",
+ ctx->va_entrypoint);
+#endif
+
+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_encode_rt_formats); i++) {
+ rt_format = &vaapi_encode_rt_formats[i];
+ if (rt_format->depth == depth &&
+ rt_format->components == profile->components &&
+ rt_format->log2_chroma_w == profile->log2_chroma_w &&
+ rt_format->log2_chroma_h == profile->log2_chroma_h)
+ break;
+ }
+ if (i >= FF_ARRAY_ELEMS(vaapi_encode_rt_formats)) {
+ av_log(avctx, AV_LOG_ERROR, "No usable render target format "
+ "found for profile %d entrypoint %d.\n",
+ ctx->va_profile, ctx->va_entrypoint);
Log profile and entrypoint strings?
Post by Mark Thompson
err = AVERROR(ENOSYS);
goto fail;
}
+ rt_format_attr = (VAConfigAttrib) { VAConfigAttribRTFormat };
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile, ctx->va_entrypoint,
+ &rt_format_attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query RT format "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ err = AVERROR_EXTERNAL;
+ goto fail;
+ }
+
+ if (rt_format_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_VERBOSE, "RT format config attribute not "
+ "supported by driver: assuming surface RT format %s "
+ "is valid.\n", rt_format->name);
I think it would be better to log it as a warning.
I'm not sure what a user would be meant to do with such a warning. Even if the driver doesn't make it queryable, we know what the answer should be and it will either work or not after this point with the known value.
Post by Xiang, Haihao
Post by Mark Thompson
+ } else if (!(rt_format_attr.value & rt_format->value)) {
+ av_log(avctx, AV_LOG_ERROR, "Surface RT format %s not supported "
+ "by driver for encoding profile %d entrypoint %d.\n",
+ rt_format->name, ctx->va_profile, ctx->va_entrypoint);
+ err = AVERROR(ENOSYS);
+ goto fail;
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI render target "
+ "format %s (%#x).\n", rt_format->name, rt_format->value);
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRTFormat,
+ .value = rt_format->value,
+ };
+ }
+
+ err = 0;
+ av_freep(&va_profiles);
+ av_freep(&va_entrypoints);
+ return err;
+}
+
...
Here's a different way to do the strings. The result is kindof stupid on libva < 2, so I'm not sure I would really argue for it. Would you prefer this?


diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 2ed12beb0c..f838ee5bd5 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -1020,6 +1020,7 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
const AVPixFmtDescriptor *desc;
VAConfigAttrib rt_format_attr;
const VAAPIEncodeRTFormat *rt_format;
+ const char *profile_string, *entrypoint_string;
int i, j, n, depth, err;


@@ -1081,6 +1082,12 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
avctx->profile != FF_PROFILE_UNKNOWN)
continue;

+#if VA_CHECK_VERSION(1, 0, 0)
+ profile_string = vaProfileStr(profile->va_profile);
+#else
+ profile_string = "[no profile names]";
+#endif
+
for (j = 0; j < n; j++) {
if (va_profiles[j] == profile->va_profile)
break;
@@ -1102,13 +1109,8 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)

avctx->profile = profile->av_profile;
ctx->va_profile = profile->va_profile;
-#if VA_CHECK_VERSION(1, 0, 0)
av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %s (%d).\n",
- vaProfileStr(ctx->va_profile), ctx->va_profile);
-#else
- av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %d.\n",
- ctx->va_profile);
-#endif
+ profile_string, ctx->va_profile);

n = vaMaxNumEntrypoints(ctx->hwctx->display);
va_entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
@@ -1120,8 +1122,8 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
va_entrypoints, &n);
if (vas != VA_STATUS_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to query entrypoints for "
- "profile %u: %d (%s).\n", ctx->va_profile,
- vas, vaErrorStr(vas));
+ "profile %s (%d): %d (%s).\n", profile_string,
+ ctx->va_profile, vas, vaErrorStr(vas));
err = AVERROR_EXTERNAL;
goto fail;
}
@@ -1136,19 +1138,19 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
}
if (i >= n) {
av_log(avctx, AV_LOG_ERROR, "No usable encoding entrypoint found "
- "for profile %d.\n", ctx->va_profile);
+ "for profile %s (%d).\n", profile_string, ctx->va_profile);
err = AVERROR(ENOSYS);
goto fail;
}

ctx->va_entrypoint = va_entrypoints[i];
#if VA_CHECK_VERSION(1, 0, 0)
- av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %s (%d).\n",
- vaEntrypointStr(ctx->va_entrypoint), ctx->va_entrypoint);
+ entrypoint_string = vaEntrypointStr(ctx->va_entrypoint);
#else
- av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %d.\n",
- ctx->va_entrypoint);
+ entrypoint_string = "[no entrypoint names]";
#endif
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %s (%d).\n",
+ entrypoint_string, ctx->va_entrypoint);

for (i = 0; i < FF_ARRAY_ELEMS(vaapi_encode_rt_formats); i++) {
rt_format = &vaapi_encode_rt_formats[i];
@@ -1160,8 +1162,9 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
}
if (i >= FF_ARRAY_ELEMS(vaapi_encode_rt_formats)) {
av_log(avctx, AV_LOG_ERROR, "No usable render target format "
- "found for profile %d entrypoint %d.\n",
- ctx->va_profile, ctx->va_entrypoint);
+ "found for profile %s (%d) entrypoint %s (%d).\n",
+ profile_string, ctx->va_profile,
+ entrypoint_string, ctx->va_entrypoint);
err = AVERROR(ENOSYS);
goto fail;
}
@@ -1183,8 +1186,9 @@ static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
"is valid.\n", rt_format->name);
} else if (!(rt_format_attr.value & rt_format->value)) {
av_log(avctx, AV_LOG_ERROR, "Surface RT format %s not supported "
- "by driver for encoding profile %d entrypoint %d.\n",
- rt_format->name, ctx->va_profile, ctx->va_entrypoint);
+ "by driver for encoding profile %s (%d) entrypoint %s (%d).\n",
+ rt_format->name, profile_string, ctx->va_profile,
+ entrypoint_string, ctx->va_entrypoint);
err = AVERROR(ENOSYS);
goto fail;
} else {
Xiang, Haihao
2018-06-14 04:51:11 UTC
Permalink
Post by Mark Thompson
Post by Xiang, Haihao
Post by Mark Thompson
Previously there was one fixed choice for each codec (e.g. H.265 -> Main
profile), and using anything else then required an explicit option from
the user. This changes to selecting the profile based on the input format
and the set of profiles actually supported by the driver (e.g. P010 input
will choose Main 10 profile for H.265 if the driver supports it).
The entrypoint and render target format are also chosen dynamically in the
same way, removing those explicit selections from the per-codec code.
---
doc/encoders.texi | 3 +
libavcodec/vaapi_encode.c | 271 ++++++++++++++++++++++++++++++++---
----
-
libavcodec/vaapi_encode.h | 43 +++++--
libavcodec/vaapi_encode_h264.c | 45 ++-----
libavcodec/vaapi_encode_h265.c | 35 ++----
libavcodec/vaapi_encode_mjpeg.c | 13 +-
libavcodec/vaapi_encode_mpeg2.c | 36 ++----
libavcodec/vaapi_encode_vp8.c | 11 +-
libavcodec/vaapi_encode_vp9.c | 34 ++---
9 files changed, 310 insertions(+), 181 deletions(-)
...
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 27ce792fbe..6104470b31 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -983,70 +983,247 @@ static av_cold void
vaapi_encode_add_global_param(AVCodecContext *avctx,
++ctx->nb_global_params;
}
-static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
+typedef struct VAAPIEncodeRTFormat {
+ const char *name;
+ unsigned int value;
+ int depth;
+ int components;
How about adding a prefix of 'nb_' to this field? I think nb_components is more
readable.
Sure. It should match the one in VAAPIEncodeProfile, so I'll change it there too.
Post by Xiang, Haihao
Post by Mark Thompson
+ int log2_chroma_w;
+ int log2_chroma_h;
+} VAAPIEncodeRTFormat;
+
+static const VAAPIEncodeRTFormat vaapi_encode_rt_formats[] = {
+ { "YUV400", VA_RT_FORMAT_YUV400, 8, 1, },
+ { "YUV420", VA_RT_FORMAT_YUV420, 8, 3, 1, 1 },
+ { "YUV422", VA_RT_FORMAT_YUV422, 8, 3, 1, 0 },
+ { "YUV444", VA_RT_FORMAT_YUV444, 8, 3, 0, 0 },
+ { "YUV411", VA_RT_FORMAT_YUV411, 8, 3, 2, 0 },
+#if VA_CHECK_VERSION(0, 38, 1)
+ { "YUV420_10", VA_RT_FORMAT_YUV420_10BPP, 10, 3, 1, 1 },
+#endif
+};
+
+static const VAEntrypoint vaapi_encode_entrypoints_normal[] = {
+ VAEntrypointEncSlice,
+ VAEntrypointEncPicture,
+#if VA_CHECK_VERSION(0, 39, 2)
+ VAEntrypointEncSliceLP,
+#endif
+ 0
+};
+#if VA_CHECK_VERSION(0, 39, 2)
+static const VAEntrypoint vaapi_encode_entrypoints_low_power[] = {
+ VAEntrypointEncSliceLP,
+ 0
+};
+#endif
+
+static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAProfile *va_profiles = NULL;
+ VAEntrypoint *va_entrypoints = NULL;
VAStatus vas;
- int i, n, err;
- VAProfile *profiles = NULL;
- VAEntrypoint *entrypoints = NULL;
- VAConfigAttrib attr[] = {
- { VAConfigAttribRTFormat },
- { VAConfigAttribRateControl },
- { VAConfigAttribEncMaxRefFrames },
- { VAConfigAttribEncPackedHeaders },
- };
+ const VAEntrypoint *usable_entrypoints;
+ const VAAPIEncodeProfile *profile;
+ const AVPixFmtDescriptor *desc;
+ VAConfigAttrib rt_format_attr;
+ const VAAPIEncodeRTFormat *rt_format;
+ int i, j, n, depth, err;
+
+
+ if (ctx->low_power) {
+#if VA_CHECK_VERSION(0, 39, 2)
+ usable_entrypoints = vaapi_encode_entrypoints_low_power;
+#else
+ av_log(avctx, AV_LOG_ERROR, "Low-power encoding is not "
+ "supported with this VAAPI version.\n");
Is it possible to report the minimal VAAPI version in the log in case user
doesn't know the requirement on vaapi version 0.39.2?
The VAAPI version is pretty much useless to users (without reading any source
code, how would you find what VAAPI version 0.39.2 means?).
Ok, I agree it is useless to user.
Post by Mark Thompson
Maybe libva version? That's still not quite right, though, because it's more
"not supported in this build" (and will continue to not be supported if you
upgrade libva but don't rebuild with the new headers).
May we ask user to rebuild ffmpeg once user upgrades libva in the log?
Post by Mark Thompson
Post by Xiang, Haihao
Post by Mark Thompson
+ return AVERROR(EINVAL);
+#endif
+ } else {
+ usable_entrypoints = vaapi_encode_entrypoints_normal;
+ }
+
+ desc = av_pix_fmt_desc_get(ctx->input_frames->sw_format);
+ if (!desc) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%d).\n",
+ ctx->input_frames->sw_format);
+ return AVERROR(EINVAL);
+ }
+ depth = desc->comp[0].depth;
+ for (i = 1; i < desc->nb_components; i++) {
+ if (desc->comp[i].depth != depth) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%s).\n",
+ desc->name);
+ return AVERROR(EINVAL);
+ }
+ }
+ av_log(avctx, AV_LOG_VERBOSE, "Input surface format is %s.\n",
+ desc->name);
n = vaMaxNumProfiles(ctx->hwctx->display);
- profiles = av_malloc_array(n, sizeof(VAProfile));
- if (!profiles) {
+ va_profiles = av_malloc_array(n, sizeof(VAProfile));
+ if (!va_profiles) {
err = AVERROR(ENOMEM);
goto fail;
}
- vas = vaQueryConfigProfiles(ctx->hwctx->display, profiles, &n);
+ vas = vaQueryConfigProfiles(ctx->hwctx->display, va_profiles, &n);
if (vas != VA_STATUS_SUCCESS) {
- av_log(ctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",
+ av_log(avctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",
vas, vaErrorStr(vas));
- err = AVERROR(ENOSYS);
+ err = AVERROR_EXTERNAL;
goto fail;
}
- for (i = 0; i < n; i++) {
- if (profiles[i] == ctx->va_profile)
- break;
+
+ av_assert0(ctx->codec->profiles);
+ for (i = 0; (ctx->codec->profiles[i].av_profile !=
+ FF_PROFILE_UNKNOWN); i++) {
+ profile = &ctx->codec->profiles[i];
+ if (depth != profile->depth ||
+ desc->nb_components != profile->components)
+ continue;
+ if (desc->nb_components > 1 &&
+ (desc->log2_chroma_w != profile->log2_chroma_w ||
+ desc->log2_chroma_h != profile->log2_chroma_h))
+ continue;
+ if (avctx->profile != profile->av_profile &&
+ avctx->profile != FF_PROFILE_UNKNOWN)
+ continue;
+
+ for (j = 0; j < n; j++) {
+ if (va_profiles[j] == profile->va_profile)
+ break;
+ }
+ if (j >= n) {
+ av_log(avctx, AV_LOG_VERBOSE, "Matching profile %d is "
+ "not supported by driver.\n", profile->va_profile);
Is it possible to report the profile string in the log as what you did below?
The #ifdefs are very nasty, but they seemed merited for the "I chose this one"
log line. See below for a different method, not sure it's better.
I prefer the new way although it is not so good on libva < 2.
Post by Mark Thompson
Post by Xiang, Haihao
Post by Mark Thompson
+ continue;
+ }
+
+ ctx->profile = profile;
+ break;
}
- if (i >= n) {
- av_log(ctx, AV_LOG_ERROR, "Encoding profile not found (%d).\n",
- ctx->va_profile);
- err = AVERROR(ENOSYS);
- goto fail;
+ if (!ctx->profile) {
+ av_log(avctx, AV_LOG_ERROR, "No usable encoding profile found.\n");
+ return AVERROR(ENOSYS);
Set err to AVERROR(ENOSYS) then goto fail, otherwise it will result in memory
leak.
Fixed.
Post by Xiang, Haihao
Post by Mark Thompson
}
+ avctx->profile = profile->av_profile;
+ ctx->va_profile = profile->va_profile;
+#if VA_CHECK_VERSION(1, 0, 0)
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %s (%d).\n",
+ vaProfileStr(ctx->va_profile), ctx->va_profile);
+#else
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %d.\n",
+ ctx->va_profile);
+#endif
+
n = vaMaxNumEntrypoints(ctx->hwctx->display);
- entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
- if (!entrypoints) {
+ va_entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
+ if (!va_entrypoints) {
err = AVERROR(ENOMEM);
goto fail;
}
vas = vaQueryConfigEntrypoints(ctx->hwctx->display, ctx->va_profile,
- entrypoints, &n);
+ va_entrypoints, &n);
if (vas != VA_STATUS_SUCCESS) {
- av_log(ctx, AV_LOG_ERROR, "Failed to query entrypoints for "
+ av_log(avctx, AV_LOG_ERROR, "Failed to query entrypoints for "
"profile %u: %d (%s).\n", ctx->va_profile,
Log profile string?
Post by Mark Thompson
vas, vaErrorStr(vas));
- err = AVERROR(ENOSYS);
+ err = AVERROR_EXTERNAL;
goto fail;
}
+
for (i = 0; i < n; i++) {
- if (entrypoints[i] == ctx->va_entrypoint)
+ for (j = 0; usable_entrypoints[j]; j++) {
+ if (va_entrypoints[i] == usable_entrypoints[j])
+ break;
+ }
+ if (usable_entrypoints[j])
break;
}
if (i >= n) {
- av_log(ctx, AV_LOG_ERROR, "Encoding entrypoint not found "
- "(%d / %d).\n", ctx->va_profile, ctx->va_entrypoint);
+ av_log(avctx, AV_LOG_ERROR, "No usable encoding entrypoint found "
+ "for profile %d.\n", ctx->va_profile);
Log profile string?
Post by Mark Thompson
+ err = AVERROR(ENOSYS);
+ goto fail;
+ }
+
+ ctx->va_entrypoint = va_entrypoints[i];
+#if VA_CHECK_VERSION(1, 0, 0)
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %s (%d).\n",
+ vaEntrypointStr(ctx->va_entrypoint), ctx->va_entrypoint);
+#else
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %d.\n",
+ ctx->va_entrypoint);
+#endif
+
+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_encode_rt_formats); i++) {
+ rt_format = &vaapi_encode_rt_formats[i];
+ if (rt_format->depth == depth &&
+ rt_format->components == profile->components &&
+ rt_format->log2_chroma_w == profile->log2_chroma_w &&
+ rt_format->log2_chroma_h == profile->log2_chroma_h)
+ break;
+ }
+ if (i >= FF_ARRAY_ELEMS(vaapi_encode_rt_formats)) {
+ av_log(avctx, AV_LOG_ERROR, "No usable render target format "
+ "found for profile %d entrypoint %d.\n",
+ ctx->va_profile, ctx->va_entrypoint);
Log profile and entrypoint strings?
Post by Mark Thompson
err = AVERROR(ENOSYS);
goto fail;
}
+ rt_format_attr = (VAConfigAttrib) { VAConfigAttribRTFormat };
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile, ctx->va_entrypoint,
+ &rt_format_attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query RT format "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ err = AVERROR_EXTERNAL;
+ goto fail;
+ }
+
+ if (rt_format_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_VERBOSE, "RT format config attribute not "
+ "supported by driver: assuming surface RT format %s "
+ "is valid.\n", rt_format->name);
I think it would be better to log it as a warning.
I'm not sure what a user would be meant to do with such a warning. Even if
the driver doesn't make it queryable, we know what the answer should be and it
will either work or not after this point with the known value.
User may file an driver issue for adding support for VAConfigAttribRTFormat
query once they see such warning. I think most user will ignore this message if
it is taken as verbose message.

Thanks
Haihao
Post by Mark Thompson
Post by Xiang, Haihao
Post by Mark Thompson
+ } else if (!(rt_format_attr.value & rt_format->value)) {
+ av_log(avctx, AV_LOG_ERROR, "Surface RT format %s not supported "
+ "by driver for encoding profile %d entrypoint %d.\n",
+ rt_format->name, ctx->va_profile, ctx->va_entrypoint);
+ err = AVERROR(ENOSYS);
+ goto fail;
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI render target "
+ "format %s (%#x).\n", rt_format->name, rt_format->value);
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRTFormat,
+ .value = rt_format->value,
+ };
+ }
+
+ err = 0;
+ av_freep(&va_profiles);
+ av_freep(&va_entrypoints);
+ return err;
+}
+
...
Here's a different way to do the strings. The result is kindof stupid on
libva < 2, so I'm not sure I would really argue for it. Would you prefer
this?
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 2ed12beb0c..f838ee5bd5 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -1020,6 +1020,7 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
const AVPixFmtDescriptor *desc;
VAConfigAttrib rt_format_attr;
const VAAPIEncodeRTFormat *rt_format;
+ const char *profile_string, *entrypoint_string;
int i, j, n, depth, err;
@@ -1081,6 +1082,12 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
avctx->profile != FF_PROFILE_UNKNOWN)
continue;
+#if VA_CHECK_VERSION(1, 0, 0)
+ profile_string = vaProfileStr(profile->va_profile);
+#else
+ profile_string = "[no profile names]";
+#endif
+
for (j = 0; j < n; j++) {
if (va_profiles[j] == profile->va_profile)
break;
@@ -1102,13 +1109,8 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
avctx->profile = profile->av_profile;
ctx->va_profile = profile->va_profile;
-#if VA_CHECK_VERSION(1, 0, 0)
av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %s (%d).\n",
- vaProfileStr(ctx->va_profile), ctx->va_profile);
-#else
- av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %d.\n",
- ctx->va_profile);
-#endif
+ profile_string, ctx->va_profile);
n = vaMaxNumEntrypoints(ctx->hwctx->display);
va_entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
@@ -1120,8 +1122,8 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
va_entrypoints, &n);
if (vas != VA_STATUS_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to query entrypoints for "
- "profile %u: %d (%s).\n", ctx->va_profile,
- vas, vaErrorStr(vas));
+ "profile %s (%d): %d (%s).\n", profile_string,
+ ctx->va_profile, vas, vaErrorStr(vas));
err = AVERROR_EXTERNAL;
goto fail;
}
@@ -1136,19 +1138,19 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
}
if (i >= n) {
av_log(avctx, AV_LOG_ERROR, "No usable encoding entrypoint found "
- "for profile %d.\n", ctx->va_profile);
+ "for profile %s (%d).\n", profile_string, ctx->va_profile);
err = AVERROR(ENOSYS);
goto fail;
}
ctx->va_entrypoint = va_entrypoints[i];
#if VA_CHECK_VERSION(1, 0, 0)
- av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %s (%d).\n",
- vaEntrypointStr(ctx->va_entrypoint), ctx->va_entrypoint);
+ entrypoint_string = vaEntrypointStr(ctx->va_entrypoint);
#else
- av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %d.\n",
- ctx->va_entrypoint);
+ entrypoint_string = "[no entrypoint names]";
#endif
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %s (%d).\n",
+ entrypoint_string, ctx->va_entrypoint);
for (i = 0; i < FF_ARRAY_ELEMS(vaapi_encode_rt_formats); i++) {
rt_format = &vaapi_encode_rt_formats[i];
@@ -1160,8 +1162,9 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
}
if (i >= FF_ARRAY_ELEMS(vaapi_encode_rt_formats)) {
av_log(avctx, AV_LOG_ERROR, "No usable render target format "
- "found for profile %d entrypoint %d.\n",
- ctx->va_profile, ctx->va_entrypoint);
+ "found for profile %s (%d) entrypoint %s (%d).\n",
+ profile_string, ctx->va_profile,
+ entrypoint_string, ctx->va_entrypoint);
err = AVERROR(ENOSYS);
goto fail;
}
@@ -1183,8 +1186,9 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
"is valid.\n", rt_format->name);
} else if (!(rt_format_attr.value & rt_format->value)) {
av_log(avctx, AV_LOG_ERROR, "Surface RT format %s not supported "
- "by driver for encoding profile %d entrypoint %d.\n",
- rt_format->name, ctx->va_profile, ctx->va_entrypoint);
+ "by driver for encoding profile %s (%d) entrypoint %s (%d).\n",
+ rt_format->name, profile_string, ctx->va_profile,
+ entrypoint_string, ctx->va_entrypoint);
err = AVERROR(ENOSYS);
goto fail;
} else {
_______________________________________________
ffmpeg-devel mailing list
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Mark Thompson
2018-06-17 13:05:59 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
Post by Xiang, Haihao
Post by Mark Thompson
Previously there was one fixed choice for each codec (e.g. H.265 -> Main
profile), and using anything else then required an explicit option from
the user. This changes to selecting the profile based on the input format
and the set of profiles actually supported by the driver (e.g. P010 input
will choose Main 10 profile for H.265 if the driver supports it).
The entrypoint and render target format are also chosen dynamically in the
same way, removing those explicit selections from the per-codec code.
---
doc/encoders.texi | 3 +
libavcodec/vaapi_encode.c | 271 ++++++++++++++++++++++++++++++++---
----
-
libavcodec/vaapi_encode.h | 43 +++++--
libavcodec/vaapi_encode_h264.c | 45 ++-----
libavcodec/vaapi_encode_h265.c | 35 ++----
libavcodec/vaapi_encode_mjpeg.c | 13 +-
libavcodec/vaapi_encode_mpeg2.c | 36 ++----
libavcodec/vaapi_encode_vp8.c | 11 +-
libavcodec/vaapi_encode_vp9.c | 34 ++---
9 files changed, 310 insertions(+), 181 deletions(-)
...
+static av_cold int vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAProfile *va_profiles = NULL;
+ VAEntrypoint *va_entrypoints = NULL;
VAStatus vas;
- int i, n, err;
- VAProfile *profiles = NULL;
- VAEntrypoint *entrypoints = NULL;
- VAConfigAttrib attr[] = {
- { VAConfigAttribRTFormat },
- { VAConfigAttribRateControl },
- { VAConfigAttribEncMaxRefFrames },
- { VAConfigAttribEncPackedHeaders },
- };
+ const VAEntrypoint *usable_entrypoints;
+ const VAAPIEncodeProfile *profile;
+ const AVPixFmtDescriptor *desc;
+ VAConfigAttrib rt_format_attr;
+ const VAAPIEncodeRTFormat *rt_format;
+ int i, j, n, depth, err;
+
+
+ if (ctx->low_power) {
+#if VA_CHECK_VERSION(0, 39, 2)
+ usable_entrypoints = vaapi_encode_entrypoints_low_power;
+#else
+ av_log(avctx, AV_LOG_ERROR, "Low-power encoding is not "
+ "supported with this VAAPI version.\n");
Is it possible to report the minimal VAAPI version in the log in case user
doesn't know the requirement on vaapi version 0.39.2?
The VAAPI version is pretty much useless to users (without reading any source
code, how would you find what VAAPI version 0.39.2 means?).
Ok, I agree it is useless to user.
Post by Mark Thompson
Maybe libva version? That's still not quite right, though, because it's more
"not supported in this build" (and will continue to not be supported if you
upgrade libva but don't rebuild with the new headers).
May we ask user to rebuild ffmpeg once user upgrades libva in the log?
On further thought, I don't think a recommendation is useful here. You end up having to suggest upgrading everything (libva, driver, ffmpeg), and even then it still may not be there (if you are using a driver or codec which doesn't expose this option).

So, I think it's most sensible to keep the simple text as originally written. If you have a string opinion then feel free to suggest your own patch for how this should work. (And it should probably be consistent with other similar cases, such as the quality-speed tradeoff option.)
Post by Xiang, Haihao
Post by Mark Thompson
Post by Xiang, Haihao
Post by Mark Thompson
+ return AVERROR(EINVAL);
+#endif
+ } else {
+ usable_entrypoints = vaapi_encode_entrypoints_normal;
+ }
+
+ desc = av_pix_fmt_desc_get(ctx->input_frames->sw_format);
+ if (!desc) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%d).\n",
+ ctx->input_frames->sw_format);
+ return AVERROR(EINVAL);
+ }
+ depth = desc->comp[0].depth;
+ for (i = 1; i < desc->nb_components; i++) {
+ if (desc->comp[i].depth != depth) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid input pixfmt (%s).\n",
+ desc->name);
+ return AVERROR(EINVAL);
+ }
+ }
+ av_log(avctx, AV_LOG_VERBOSE, "Input surface format is %s.\n",
+ desc->name);
n = vaMaxNumProfiles(ctx->hwctx->display);
- profiles = av_malloc_array(n, sizeof(VAProfile));
- if (!profiles) {
+ va_profiles = av_malloc_array(n, sizeof(VAProfile));
+ if (!va_profiles) {
err = AVERROR(ENOMEM);
goto fail;
}
- vas = vaQueryConfigProfiles(ctx->hwctx->display, profiles, &n);
+ vas = vaQueryConfigProfiles(ctx->hwctx->display, va_profiles, &n);
if (vas != VA_STATUS_SUCCESS) {
- av_log(ctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",
+ av_log(avctx, AV_LOG_ERROR, "Failed to query profiles: %d (%s).\n",
vas, vaErrorStr(vas));
- err = AVERROR(ENOSYS);
+ err = AVERROR_EXTERNAL;
goto fail;
}
- for (i = 0; i < n; i++) {
- if (profiles[i] == ctx->va_profile)
- break;
+
+ av_assert0(ctx->codec->profiles);
+ for (i = 0; (ctx->codec->profiles[i].av_profile !=
+ FF_PROFILE_UNKNOWN); i++) {
+ profile = &ctx->codec->profiles[i];
+ if (depth != profile->depth ||
+ desc->nb_components != profile->components)
+ continue;
+ if (desc->nb_components > 1 &&
+ (desc->log2_chroma_w != profile->log2_chroma_w ||
+ desc->log2_chroma_h != profile->log2_chroma_h))
+ continue;
+ if (avctx->profile != profile->av_profile &&
+ avctx->profile != FF_PROFILE_UNKNOWN)
+ continue;
+
+ for (j = 0; j < n; j++) {
+ if (va_profiles[j] == profile->va_profile)
+ break;
+ }
+ if (j >= n) {
+ av_log(avctx, AV_LOG_VERBOSE, "Matching profile %d is "
+ "not supported by driver.\n", profile->va_profile);
Is it possible to report the profile string in the log as what you did below?
The #ifdefs are very nasty, but they seemed merited for the "I chose this one"
log line. See below for a different method, not sure it's better.
I prefer the new way although it is not so good on libva < 2.
Ok, I'll change to doing that.
Post by Xiang, Haihao
Post by Mark Thompson
Post by Xiang, Haihao
Post by Mark Thompson
+ continue;
+ }
+
+ ctx->profile = profile;
+ break;
}
- if (i >= n) {
- av_log(ctx, AV_LOG_ERROR, "Encoding profile not found (%d).\n",
- ctx->va_profile);
- err = AVERROR(ENOSYS);
- goto fail;
+ if (!ctx->profile) {
+ av_log(avctx, AV_LOG_ERROR, "No usable encoding profile found.\n");
+ return AVERROR(ENOSYS);
Set err to AVERROR(ENOSYS) then goto fail, otherwise it will result in memory
leak.
Fixed.
Post by Xiang, Haihao
Post by Mark Thompson
}
+ avctx->profile = profile->av_profile;
+ ctx->va_profile = profile->va_profile;
+#if VA_CHECK_VERSION(1, 0, 0)
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %s (%d).\n",
+ vaProfileStr(ctx->va_profile), ctx->va_profile);
+#else
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %d.\n",
+ ctx->va_profile);
+#endif
+
n = vaMaxNumEntrypoints(ctx->hwctx->display);
- entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
- if (!entrypoints) {
+ va_entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
+ if (!va_entrypoints) {
err = AVERROR(ENOMEM);
goto fail;
}
vas = vaQueryConfigEntrypoints(ctx->hwctx->display, ctx->va_profile,
- entrypoints, &n);
+ va_entrypoints, &n);
if (vas != VA_STATUS_SUCCESS) {
- av_log(ctx, AV_LOG_ERROR, "Failed to query entrypoints for "
+ av_log(avctx, AV_LOG_ERROR, "Failed to query entrypoints for "
"profile %u: %d (%s).\n", ctx->va_profile,
Log profile string?
Post by Mark Thompson
vas, vaErrorStr(vas));
- err = AVERROR(ENOSYS);
+ err = AVERROR_EXTERNAL;
goto fail;
}
+
for (i = 0; i < n; i++) {
- if (entrypoints[i] == ctx->va_entrypoint)
+ for (j = 0; usable_entrypoints[j]; j++) {
+ if (va_entrypoints[i] == usable_entrypoints[j])
+ break;
+ }
+ if (usable_entrypoints[j])
break;
}
if (i >= n) {
- av_log(ctx, AV_LOG_ERROR, "Encoding entrypoint not found "
- "(%d / %d).\n", ctx->va_profile, ctx->va_entrypoint);
+ av_log(avctx, AV_LOG_ERROR, "No usable encoding entrypoint found "
+ "for profile %d.\n", ctx->va_profile);
Log profile string?
Post by Mark Thompson
+ err = AVERROR(ENOSYS);
+ goto fail;
+ }
+
+ ctx->va_entrypoint = va_entrypoints[i];
+#if VA_CHECK_VERSION(1, 0, 0)
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %s (%d).\n",
+ vaEntrypointStr(ctx->va_entrypoint), ctx->va_entrypoint);
+#else
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %d.\n",
+ ctx->va_entrypoint);
+#endif
+
+ for (i = 0; i < FF_ARRAY_ELEMS(vaapi_encode_rt_formats); i++) {
+ rt_format = &vaapi_encode_rt_formats[i];
+ if (rt_format->depth == depth &&
+ rt_format->components == profile->components &&
+ rt_format->log2_chroma_w == profile->log2_chroma_w &&
+ rt_format->log2_chroma_h == profile->log2_chroma_h)
+ break;
+ }
+ if (i >= FF_ARRAY_ELEMS(vaapi_encode_rt_formats)) {
+ av_log(avctx, AV_LOG_ERROR, "No usable render target format "
+ "found for profile %d entrypoint %d.\n",
+ ctx->va_profile, ctx->va_entrypoint);
Log profile and entrypoint strings?
Post by Mark Thompson
err = AVERROR(ENOSYS);
goto fail;
}
+ rt_format_attr = (VAConfigAttrib) { VAConfigAttribRTFormat };
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile, ctx->va_entrypoint,
+ &rt_format_attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query RT format "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ err = AVERROR_EXTERNAL;
+ goto fail;
+ }
+
+ if (rt_format_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_VERBOSE, "RT format config attribute not "
+ "supported by driver: assuming surface RT format %s "
+ "is valid.\n", rt_format->name);
I think it would be better to log it as a warning.
I'm not sure what a user would be meant to do with such a warning. Even if
the driver doesn't make it queryable, we know what the answer should be and it
will either work or not after this point with the known value.
User may file an driver issue for adding support for VAConfigAttribRTFormat
query once they see such warning. I think most user will ignore this message if
it is taken as verbose message.
I don't think end-users aren't going to file bugs of this form for a warning when it works. If it doesn't work then they may file a bug, in which case the verbose logging includes the right information to fix the problem.
Post by Xiang, Haihao
Post by Mark Thompson
Post by Xiang, Haihao
Post by Mark Thompson
+ } else if (!(rt_format_attr.value & rt_format->value)) {
+ av_log(avctx, AV_LOG_ERROR, "Surface RT format %s not supported "
+ "by driver for encoding profile %d entrypoint %d.\n",
+ rt_format->name, ctx->va_profile, ctx->va_entrypoint);
+ err = AVERROR(ENOSYS);
+ goto fail;
+ } else {
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI render target "
+ "format %s (%#x).\n", rt_format->name, rt_format->value);
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRTFormat,
+ .value = rt_format->value,
+ };
+ }
+
+ err = 0;
+ av_freep(&va_profiles);
+ av_freep(&va_entrypoints);
+ return err;
+}
+
...
Here's a different way to do the strings. The result is kindof stupid on
libva < 2, so I'm not sure I would really argue for it. Would you prefer
this?
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 2ed12beb0c..f838ee5bd5 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -1020,6 +1020,7 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
const AVPixFmtDescriptor *desc;
VAConfigAttrib rt_format_attr;
const VAAPIEncodeRTFormat *rt_format;
+ const char *profile_string, *entrypoint_string;
int i, j, n, depth, err;
@@ -1081,6 +1082,12 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
avctx->profile != FF_PROFILE_UNKNOWN)
continue;
+#if VA_CHECK_VERSION(1, 0, 0)
+ profile_string = vaProfileStr(profile->va_profile);
+#else
+ profile_string = "[no profile names]";
+#endif
+
for (j = 0; j < n; j++) {
if (va_profiles[j] == profile->va_profile)
break;
@@ -1102,13 +1109,8 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
avctx->profile = profile->av_profile;
ctx->va_profile = profile->va_profile;
-#if VA_CHECK_VERSION(1, 0, 0)
av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %s (%d).\n",
- vaProfileStr(ctx->va_profile), ctx->va_profile);
-#else
- av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI profile %d.\n",
- ctx->va_profile);
-#endif
+ profile_string, ctx->va_profile);
n = vaMaxNumEntrypoints(ctx->hwctx->display);
va_entrypoints = av_malloc_array(n, sizeof(VAEntrypoint));
@@ -1120,8 +1122,8 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
va_entrypoints, &n);
if (vas != VA_STATUS_SUCCESS) {
av_log(avctx, AV_LOG_ERROR, "Failed to query entrypoints for "
- "profile %u: %d (%s).\n", ctx->va_profile,
- vas, vaErrorStr(vas));
+ "profile %s (%d): %d (%s).\n", profile_string,
+ ctx->va_profile, vas, vaErrorStr(vas));
err = AVERROR_EXTERNAL;
goto fail;
}
@@ -1136,19 +1138,19 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
}
if (i >= n) {
av_log(avctx, AV_LOG_ERROR, "No usable encoding entrypoint found "
- "for profile %d.\n", ctx->va_profile);
+ "for profile %s (%d).\n", profile_string, ctx->va_profile);
err = AVERROR(ENOSYS);
goto fail;
}
ctx->va_entrypoint = va_entrypoints[i];
#if VA_CHECK_VERSION(1, 0, 0)
- av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %s (%d).\n",
- vaEntrypointStr(ctx->va_entrypoint), ctx->va_entrypoint);
+ entrypoint_string = vaEntrypointStr(ctx->va_entrypoint);
#else
- av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %d.\n",
- ctx->va_entrypoint);
+ entrypoint_string = "[no entrypoint names]";
#endif
+ av_log(avctx, AV_LOG_VERBOSE, "Using VAAPI entrypoint %s (%d).\n",
+ entrypoint_string, ctx->va_entrypoint);
for (i = 0; i < FF_ARRAY_ELEMS(vaapi_encode_rt_formats); i++) {
rt_format = &vaapi_encode_rt_formats[i];
@@ -1160,8 +1162,9 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
}
if (i >= FF_ARRAY_ELEMS(vaapi_encode_rt_formats)) {
av_log(avctx, AV_LOG_ERROR, "No usable render target format "
- "found for profile %d entrypoint %d.\n",
- ctx->va_profile, ctx->va_entrypoint);
+ "found for profile %s (%d) entrypoint %s (%d).\n",
+ profile_string, ctx->va_profile,
+ entrypoint_string, ctx->va_entrypoint);
err = AVERROR(ENOSYS);
goto fail;
}
@@ -1183,8 +1186,9 @@ static av_cold int
vaapi_encode_profile_entrypoint(AVCodecContext *avctx)
"is valid.\n", rt_format->name);
} else if (!(rt_format_attr.value & rt_format->value)) {
av_log(avctx, AV_LOG_ERROR, "Surface RT format %s not supported "
- "by driver for encoding profile %d entrypoint %d.\n",
- rt_format->name, ctx->va_profile, ctx->va_entrypoint);
+ "by driver for encoding profile %s (%d) entrypoint %s (%d).\n",
+ rt_format->name, profile_string, ctx->va_profile,
+ entrypoint_string, ctx->va_entrypoint);
err = AVERROR(ENOSYS);
goto fail;
} else {
As noted above, I've merged this part into the patch.

Thanks,

- Mark
Mark Thompson
2018-06-07 23:43:07 UTC
Permalink
The only common option here is low_power - it was previously supported
for H.264 only, that specific option is removed.
---
doc/encoders.texi | 14 ++++++++++++--
libavcodec/vaapi_encode.h | 9 +++++++++
libavcodec/vaapi_encode_h264.c | 8 ++------
libavcodec/vaapi_encode_h265.c | 2 ++
libavcodec/vaapi_encode_vp8.c | 1 +
libavcodec/vaapi_encode_vp9.c | 1 +
6 files changed, 27 insertions(+), 8 deletions(-)

diff --git a/doc/encoders.texi b/doc/encoders.texi
index 16be6359b3..62a1509a96 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -2599,6 +2599,18 @@ Size / quality tradeoff: higher values are smaller / worse quality.
@option{b_qoffset} / @option{b_quant_offset}
@end itemize

+All encoders support the following options:
+@itemize
+@item
+@option{low_power}
+
+Some drivers/platforms offer a second encoder for some codecs intended to use
+less power than the default encoder; setting this option will attempt to use
+that encoder. Note that it may support a reduced feature set, so some other
+options may not be available in this mode.
+@end itemize
+
+Each encoder also has its own specific options:
@table @option

@item h264_vaapi
@@ -2606,8 +2618,6 @@ Size / quality tradeoff: higher values are smaller / worse quality.
@option{level} sets the value of @emph{level_idc}.

@table @option
-@item low_power
-Use low-power encoding mode.
@item coder
Set entropy encoder (default is @emph{cabac}). Possible values:

diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index 212ab6726d..b8f2ed6d21 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -302,4 +302,13 @@ int ff_vaapi_encode2(AVCodecContext *avctx, AVPacket *pkt,
int ff_vaapi_encode_init(AVCodecContext *avctx);
int ff_vaapi_encode_close(AVCodecContext *avctx);

+
+#define VAAPI_ENCODE_COMMON_OPTIONS \
+ { "low_power", \
+ "Use low-power encoding mode (only available on some platforms; " \
+ "may not support all encoding features)", \
+ OFFSET(common.low_power), AV_OPT_TYPE_BOOL, \
+ { .i64 = 0 }, 0, 1, FLAGS }
+
+
#endif /* AVCODEC_VAAPI_ENCODE_H */
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index ab8bf13ef3..c72fb9f00a 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -52,7 +52,6 @@ typedef struct VAAPIEncodeH264Context {
// User options.
int qp;
int quality;
- int low_power;
int coder;
int aud;
int sei;
@@ -936,8 +935,6 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
return AVERROR_PATCHWELCOME;
}

- ctx->low_power = priv->low_power;
-
if (avctx->bit_rate > 0) {
if (avctx->rc_max_rate == avctx->bit_rate)
ctx->va_rc_mode = VA_RC_CBR;
@@ -970,13 +967,12 @@ static av_cold int vaapi_encode_h264_close(AVCodecContext *avctx)
#define OFFSET(x) offsetof(VAAPIEncodeH264Context, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
static const AVOption vaapi_encode_h264_options[] = {
+ VAAPI_ENCODE_COMMON_OPTIONS,
+
{ "qp", "Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)",
OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 20 }, 0, 52, FLAGS },
{ "quality", "Set encode quality (trades off against speed, higher is faster)",
OFFSET(quality), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 8, FLAGS },
- { "low_power", "Use low-power encoding mode (experimental: only supported "
- "on some platforms, does not support all features)",
- OFFSET(low_power), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS },
{ "coder", "Entropy coder type",
OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS, "coder" },
{ "cavlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS, "coder" },
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index 9fa16593d0..b8b66b87cb 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -1099,6 +1099,8 @@ static av_cold int vaapi_encode_h265_close(AVCodecContext *avctx)
#define OFFSET(x) offsetof(VAAPIEncodeH265Context, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
static const AVOption vaapi_encode_h265_options[] = {
+ VAAPI_ENCODE_COMMON_OPTIONS,
+
{ "qp", "Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)",
OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 25 }, 0, 52, FLAGS },

diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c
index a502df7885..9588826bfb 100644
--- a/libavcodec/vaapi_encode_vp8.c
+++ b/libavcodec/vaapi_encode_vp8.c
@@ -228,6 +228,7 @@ static av_cold int vaapi_encode_vp8_init(AVCodecContext *avctx)
#define OFFSET(x) offsetof(VAAPIEncodeVP8Context, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
static const AVOption vaapi_encode_vp8_options[] = {
+ VAAPI_ENCODE_COMMON_OPTIONS,
{ "loop_filter_level", "Loop filter level",
OFFSET(loop_filter_level), AV_OPT_TYPE_INT, { .i64 = 16 }, 0, 63, FLAGS },
{ "loop_filter_sharpness", "Loop filter sharpness",
diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index 88c0ce3b0a..4d7cec0520 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -251,6 +251,7 @@ static av_cold int vaapi_encode_vp9_init(AVCodecContext *avctx)
#define OFFSET(x) offsetof(VAAPIEncodeVP9Context, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
static const AVOption vaapi_encode_vp9_options[] = {
+ VAAPI_ENCODE_COMMON_OPTIONS,
{ "loop_filter_level", "Loop filter level",
OFFSET(loop_filter_level), AV_OPT_TYPE_INT, { .i64 = 16 }, 0, 63, FLAGS },
{ "loop_filter_sharpness", "Loop filter sharpness",
--
2.16.3
Mark Thompson
2018-06-07 23:43:09 UTC
Permalink
---
libavcodec/vaapi_encode.c | 84 +++++++++++++++++++++++++-----------------
libavcodec/vaapi_encode_h264.c | 7 ++--
2 files changed, 54 insertions(+), 37 deletions(-)

diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 6104470b31..4da8a7083c 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -1391,6 +1391,51 @@ static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
return 0;
}

+static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx)
+{
+#if VA_CHECK_VERSION(0, 36, 0)
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAStatus vas;
+ VAConfigAttrib attr = { VAConfigAttribEncQualityRange };
+ int quality = avctx->compression_level;
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile,
+ ctx->va_entrypoint,
+ &attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query quality "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
+
+ if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ if (quality != 0) {
+ av_log(avctx, AV_LOG_WARNING, "Quality attribute is not "
+ "supported: will use default quality level.\n");
+ }
+ } else {
+ if (quality > attr.value) {
+ av_log(avctx, AV_LOG_WARNING, "Invalid quality level: "
+ "valid range is 0-%d, using %d.\n",
+ attr.value, attr.value);
+ quality = attr.value;
+ }
+
+ ctx->quality_params.misc.type = VAEncMiscParameterTypeQualityLevel;
+ ctx->quality_params.quality.quality_level = quality;
+
+ vaapi_encode_add_global_param(avctx, &ctx->quality_params.misc,
+ sizeof(ctx->quality_params));
+ }
+#else
+ av_log(avctx, AV_LOG_WARNING, "The encode quality option is "
+ "not supported with this VAAPI version.\n");
+#endif
+
+ return 0;
+}
+
static void vaapi_encode_free_output_buffer(void *opaque,
uint8_t *data)
{
@@ -1572,6 +1617,12 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;

+ if (avctx->compression_level >= 0) {
+ err = vaapi_encode_init_quality(avctx);
+ if (err < 0)
+ goto fail;
+ }
+
vas = vaCreateConfig(ctx->hwctx->display,
ctx->va_profile, ctx->va_entrypoint,
ctx->config_attributes, ctx->nb_config_attributes,
@@ -1621,39 +1672,6 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
goto fail;
}

- if (avctx->compression_level >= 0) {
-#if VA_CHECK_VERSION(0, 36, 0)
- VAConfigAttrib attr = { VAConfigAttribEncQualityRange };
-
- vas = vaGetConfigAttributes(ctx->hwctx->display,
- ctx->va_profile,
- ctx->va_entrypoint,
- &attr, 1);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(avctx, AV_LOG_WARNING, "Failed to query quality "
- "attribute: will use default compression level.\n");
- } else {
- if (avctx->compression_level > attr.value) {
- av_log(avctx, AV_LOG_WARNING, "Invalid compression "
- "level: valid range is 0-%d, using %d.\n",
- attr.value, attr.value);
- avctx->compression_level = attr.value;
- }
-
- ctx->quality_params.misc.type =
- VAEncMiscParameterTypeQualityLevel;
- ctx->quality_params.quality.quality_level =
- avctx->compression_level;
-
- vaapi_encode_add_global_param(avctx, &ctx->quality_params.misc,
- sizeof(ctx->quality_params));
- }
-#else
- av_log(avctx, AV_LOG_WARNING, "The encode compression level "
- "option is not supported with this VAAPI version.\n");
-#endif
- }
-
ctx->input_order = 0;
ctx->output_delay = avctx->max_b_frames;
ctx->decode_delay = 1;
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index c72fb9f00a..df6f39a946 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -831,9 +831,6 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx)
av_assert0(0 && "Invalid RC mode.");
}

- if (avctx->compression_level == FF_COMPRESSION_DEFAULT)
- avctx->compression_level = priv->quality;
-
if (priv->sei & SEI_IDENTIFIER) {
const char *lavc = LIBAVCODEC_IDENT;
const char *vaapi = VA_VERSION_S;
@@ -907,6 +904,8 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
avctx->profile = priv->profile;
if (avctx->level == FF_LEVEL_UNKNOWN)
avctx->level = priv->level;
+ if (avctx->compression_level == FF_COMPRESSION_DEFAULT)
+ avctx->compression_level = priv->quality;

// Reject unsupported profiles.
switch (avctx->profile) {
@@ -972,7 +971,7 @@ static const AVOption vaapi_encode_h264_options[] = {
{ "qp", "Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)",
OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 20 }, 0, 52, FLAGS },
{ "quality", "Set encode quality (trades off against speed, higher is faster)",
- OFFSET(quality), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 8, FLAGS },
+ OFFSET(quality), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
{ "coder", "Entropy coder type",
OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS, "coder" },
{ "cavlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN, INT_MAX, FLAGS, "coder" },
--
2.16.3
Xiang, Haihao
2018-06-12 07:39:51 UTC
Permalink
Post by Mark Thompson
---
libavcodec/vaapi_encode.c | 84 +++++++++++++++++++++++++--------------
---
libavcodec/vaapi_encode_h264.c | 7 ++--
2 files changed, 54 insertions(+), 37 deletions(-)
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 6104470b31..4da8a7083c 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -1391,6 +1391,51 @@ static av_cold int
vaapi_encode_init_rate_control(AVCodecContext *avctx)
return 0;
}
+static av_cold int vaapi_encode_init_quality(AVCodecContext *avctx)
+{
+#if VA_CHECK_VERSION(0, 36, 0)
+ VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAStatus vas;
+ VAConfigAttrib attr = { VAConfigAttribEncQualityRange };
+ int quality = avctx->compression_level;
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile,
+ ctx->va_entrypoint,
+ &attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query quality "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
+
+ if (attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ if (quality != 0) {
+ av_log(avctx, AV_LOG_WARNING, "Quality attribute is not "
+ "supported: will use default quality level.\n");
+ }
+ } else {
+ if (quality > attr.value) {
+ av_log(avctx, AV_LOG_WARNING, "Invalid quality level: "
+ "valid range is 0-%d, using %d.\n",
+ attr.value, attr.value);
+ quality = attr.value;
+ }
+
+ ctx->quality_params.misc.type = VAEncMiscParameterTypeQualityLevel;
+ ctx->quality_params.quality.quality_level = quality;
+
+ vaapi_encode_add_global_param(avctx, &ctx->quality_params.misc,
+ sizeof(ctx->quality_params));
+ }
+#else
+ av_log(avctx, AV_LOG_WARNING, "The encode quality option is "
+ "not supported with this VAAPI version.\n");
Log the minimal VAAPI version for quality attribute?
Post by Mark Thompson
+#endif
+
+ return 0;
+}
+
static void vaapi_encode_free_output_buffer(void *opaque,
uint8_t *data)
{
@@ -1572,6 +1617,12 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;
+ if (avctx->compression_level >= 0) {
+ err = vaapi_encode_init_quality(avctx);
+ if (err < 0)
+ goto fail;
+ }
+
vas = vaCreateConfig(ctx->hwctx->display,
ctx->va_profile, ctx->va_entrypoint,
ctx->config_attributes, ctx->nb_config_attributes,
@@ -1621,39 +1672,6 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
goto fail;
}
- if (avctx->compression_level >= 0) {
-#if VA_CHECK_VERSION(0, 36, 0)
- VAConfigAttrib attr = { VAConfigAttribEncQualityRange };
-
- vas = vaGetConfigAttributes(ctx->hwctx->display,
- ctx->va_profile,
- ctx->va_entrypoint,
- &attr, 1);
- if (vas != VA_STATUS_SUCCESS) {
- av_log(avctx, AV_LOG_WARNING, "Failed to query quality "
- "attribute: will use default compression level.\n");
- } else {
- if (avctx->compression_level > attr.value) {
- av_log(avctx, AV_LOG_WARNING, "Invalid compression "
- "level: valid range is 0-%d, using %d.\n",
- attr.value, attr.value);
- avctx->compression_level = attr.value;
- }
-
- ctx->quality_params.misc.type =
- VAEncMiscParameterTypeQualityLevel;
- ctx->quality_params.quality.quality_level =
- avctx->compression_level;
-
- vaapi_encode_add_global_param(avctx, &ctx->quality_params.misc,
- sizeof(ctx->quality_params));
- }
-#else
- av_log(avctx, AV_LOG_WARNING, "The encode compression level "
- "option is not supported with this VAAPI version.\n");
-#endif
- }
-
ctx->input_order = 0;
ctx->output_delay = avctx->max_b_frames;
ctx->decode_delay = 1;
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index c72fb9f00a..df6f39a946 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -831,9 +831,6 @@ static av_cold int
vaapi_encode_h264_configure(AVCodecContext *avctx)
av_assert0(0 && "Invalid RC mode.");
}
- if (avctx->compression_level == FF_COMPRESSION_DEFAULT)
- avctx->compression_level = priv->quality;
-
if (priv->sei & SEI_IDENTIFIER) {
const char *lavc = LIBAVCODEC_IDENT;
const char *vaapi = VA_VERSION_S;
@@ -907,6 +904,8 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx)
avctx->profile = priv->profile;
if (avctx->level == FF_LEVEL_UNKNOWN)
avctx->level = priv->level;
+ if (avctx->compression_level == FF_COMPRESSION_DEFAULT)
+ avctx->compression_level = priv->quality;
// Reject unsupported profiles.
switch (avctx->profile) {
@@ -972,7 +971,7 @@ static const AVOption vaapi_encode_h264_options[] = {
{ "qp", "Constant QP (for P-frames; scaled by qfactor/qoffset for I/B)",
OFFSET(qp), AV_OPT_TYPE_INT, { .i64 = 20 }, 0, 52, FLAGS },
{ "quality", "Set encode quality (trades off against speed, higher is faster)",
- OFFSET(quality), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 8, FLAGS },
+ OFFSET(quality), AV_OPT_TYPE_INT, { .i64 = -1 }, -1, INT_MAX, FLAGS },
{ "coder", "Entropy coder type",
OFFSET(coder), AV_OPT_TYPE_INT, { .i64 = 1 }, 0, 1, FLAGS, "coder" },
{ "cavlc", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = 0 }, INT_MIN,
INT_MAX, FLAGS, "coder" },
Mark Thompson
2018-06-07 23:43:02 UTC
Permalink
---
libavcodec/vaapi_encode_vp9.c | 31 +++++++++++++++----------------
1 file changed, 15 insertions(+), 16 deletions(-)

diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index 9108699ac3..6e62213bc9 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -33,21 +33,25 @@


typedef struct VAAPIEncodeVP9Context {
+ VAAPIEncodeContext common;
+
+ // User options.
+ int loop_filter_level;
+ int loop_filter_sharpness;
+
+ // Derived settings.
int q_idx_idr;
int q_idx_p;
int q_idx_b;

+ // Stream state.
+
// Reference direction for B-like frames:
// 0 - most recent P/IDR frame is last.
// 1 - most recent P frame is golden.
int last_ref_dir;
} VAAPIEncodeVP9Context;

-typedef struct VAAPIEncodeVP9Options {
- int loop_filter_level;
- int loop_filter_sharpness;
-} VAAPIEncodeVP9Options;
-

#define vseq_var(name) vseq->name, name
#define vseq_field(name) vseq->seq_fields.bits.name, name
@@ -82,10 +86,8 @@ static int vaapi_encode_vp9_init_sequence_params(AVCodecContext *avctx)
static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx,
VAAPIEncodePicture *pic)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
+ VAAPIEncodeVP9Context *priv = avctx->priv_data;
VAEncPictureParameterBufferVP9 *vpic = pic->codec_picture_params;
- VAAPIEncodeVP9Context *priv = ctx->priv_data;
- VAAPIEncodeVP9Options *opt = ctx->codec_options;
int i;

vpic->reconstructed_frame = pic->recon_surface;
@@ -169,8 +171,8 @@ static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx,
vpic->chroma_ac_qindex_delta = 0;
vpic->chroma_dc_qindex_delta = 0;

- vpic->filter_level = opt->loop_filter_level;
- vpic->sharpness_level = opt->loop_filter_sharpness;
+ vpic->filter_level = priv->loop_filter_level;
+ vpic->sharpness_level = priv->loop_filter_sharpness;

if (avctx->max_b_frames > 0 && pic->type == PICTURE_TYPE_P)
priv->last_ref_dir = !priv->last_ref_dir;
@@ -180,8 +182,7 @@ static int vaapi_encode_vp9_init_picture_params(AVCodecContext *avctx,

static av_cold int vaapi_encode_vp9_configure(AVCodecContext *avctx)
{
- VAAPIEncodeContext *ctx = avctx->priv_data;
- VAAPIEncodeVP9Context *priv = ctx->priv_data;
+ VAAPIEncodeVP9Context *priv = avctx->priv_data;

priv->q_idx_p = av_clip(avctx->global_quality, 0, VP9_MAX_QUANT);
if (avctx->i_quant_factor > 0.0)
@@ -266,8 +267,7 @@ static av_cold int vaapi_encode_vp9_init(AVCodecContext *avctx)
return ff_vaapi_encode_init(avctx);
}

-#define OFFSET(x) (offsetof(VAAPIEncodeContext, codec_options_data) + \
- offsetof(VAAPIEncodeVP9Options, x))
+#define OFFSET(x) offsetof(VAAPIEncodeVP9Context, x)
#define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM)
static const AVOption vaapi_encode_vp9_options[] = {
{ "loop_filter_level", "Loop filter level",
@@ -298,8 +298,7 @@ AVCodec ff_vp9_vaapi_encoder = {
.long_name = NULL_IF_CONFIG_SMALL("VP9 (VAAPI)"),
.type = AVMEDIA_TYPE_VIDEO,
.id = AV_CODEC_ID_VP9,
- .priv_data_size = (sizeof(VAAPIEncodeContext) +
- sizeof(VAAPIEncodeVP9Options)),
+ .priv_data_size = sizeof(VAAPIEncodeVP9Context),
.init = &vaapi_encode_vp9_init,
.encode2 = &ff_vaapi_encode2,
.close = &ff_vaapi_encode_close,
--
2.16.3
Xiang, Haihao
2018-06-13 07:03:33 UTC
Permalink
Query which modes are supported and select between VBR and CBR based
on that - this removes all of the codec-specific rate control mode
selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173 ++++++++++++++++++++++++++++-----------
-
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
diff --git a/doc/encoders.texi b/doc/encoders.texi
index 62a1509a96..0c0a307987 100644
--- a/doc/encoders.texi
+++ b/doc/encoders.texi
@@ -2643,8 +2643,6 @@ Always encodes using the standard quantisation and
huffman tables -
@item mpeg2_vaapi
@option{profile} and @option{level} set the value of
@emph{profile_and_level_indication}.
-No rate control is supported.
-
@item vp8_vaapi
B-frames are not supported.
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index f4c063734c..5de5483454 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -1217,7 +1217,6 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
int i;
VAConfigAttrib attr[] = {
- { VAConfigAttribRateControl },
{ VAConfigAttribEncMaxRefFrames },
{ VAConfigAttribEncPackedHeaders },
};
@@ -1241,32 +1240,6 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
continue;
}
switch (attr[i].type) {
- // Hack for backward compatibility: CBR was the only
- // usable RC mode for a long time, so old drivers will
- // only have it. Normal default options may now choose
- // VBR and then fail, however, so override it here with
- // CBR if that is the only supported mode.
- if (ctx->va_rc_mode == VA_RC_VBR &&
- !(attr[i].value & VA_RC_VBR) &&
- (attr[i].value & VA_RC_CBR)) {
- av_log(avctx, AV_LOG_WARNING, "VBR rate control is "
- "not supported with this driver version; "
- "using CBR instead.\n");
- ctx->va_rc_mode = VA_RC_CBR;
- }
- if (!(ctx->va_rc_mode & 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);
- return AVERROR(EINVAL);
- }
- ctx->config_attributes[ctx->nb_config_attributes++] =
- (VAConfigAttrib) {
- .type = VAConfigAttribRateControl,
- .value = ctx->va_rc_mode,
- };
- break;
{
unsigned int ref_l0 = attr[i].value & 0xffff;
@@ -1313,44 +1286,144 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- int rc_bits_per_second;
- int rc_target_percentage;
- int rc_window_size;
- int hrd_buffer_size;
- int hrd_initial_buffer_fullness;
+ int64_t rc_bits_per_second;
+ int rc_target_percentage;
+ int rc_window_size;
+ int64_t hrd_buffer_size;
+ int64_t hrd_initial_buffer_fullness;
int fr_num, fr_den;
+ VAConfigAttrib rc_attr = { VAConfigAttribRateControl };
+ VAStatus vas;
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile, ctx->va_entrypoint,
+ &rc_attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query rate control "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
- if (avctx->bit_rate > INT32_MAX) {
- av_log(avctx, AV_LOG_ERROR, "Target bitrate of 2^31 bps or "
- "higher is not supported.\n");
+ if (rc_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not report any "
+ "supported rate control modes: assuming constant-quality.\n");
How about logging it as warning message?
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ }
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using constant-quality mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support "
+ "constant-quality mode (%#x).\n", rc_attr.value);
+ return AVERROR(EINVAL);
+ }
+ }
+
+ if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any "
+ "bitrate-targetted rate control modes.\n");
return AVERROR(EINVAL);
}
if (avctx->rc_buffer_size)
hrd_buffer_size = avctx->rc_buffer_size;
+ else if (avctx->rc_max_rate > 0)
+ hrd_buffer_size = avctx->rc_max_rate;
else
hrd_buffer_size = avctx->bit_rate;
- if (avctx->rc_initial_buffer_occupancy)
+ if (avctx->rc_initial_buffer_occupancy) {
+ if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer settings: "
+ "must have initial buffer size (%d) < "
+ "buffer size (%"PRId64").\n",
+ avctx->rc_initial_buffer_occupancy, hrd_buffer_size);
+ return AVERROR(EINVAL);
+ }
hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy;
- else
+ } else {
hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4;
+ }
+
+ if (avctx->rc_max_rate && avctx->rc_max_rate < avctx->bit_rate) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid bitrate settings: must have "
+ "bitrate (%"PRId64") <= maxrate (%"PRId64").\n",
+ avctx->bit_rate, avctx->rc_max_rate);
+ return AVERROR(EINVAL);
+ }
+
+ if (avctx->rc_max_rate > avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_VBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support "
+ "VBR mode (%#x), using CBR mode instead.\n",
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_CBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_VBR;
+ }
+
+ rc_bits_per_second = avctx->rc_max_rate;
+ rc_target_percentage = (avctx->bit_rate * 100) / avctx->rc_max_rate;
I think rc_target_percentage should be 100 for CBR case.
+
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support "
+ "CBR mode (%#x), using VBR mode instead.\n",
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal bitrate.
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
Is it better to take it as CBR when avctx->rc_max_rate is 0 and CBR is supported
by driver?
+
+ // We only have a target bitrate, but VAAPI requires that a
+ // maximum rate be supplied as well. Since the user has
+ // offered no particular constraint, arbitrarily pick a
+ // maximum rate of double the target rate.
+ rc_bits_per_second = 2 * avctx->bit_rate;
+ rc_target_percentage = 50;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- } else {
- rc_bits_per_second = avctx->rc_max_rate;
- rc_target_percentage = (avctx->bit_rate * 100) /
rc_bits_per_second;
}
- rc_window_size = (hrd_buffer_size * 1000) / avctx->bit_rate;
}
+ rc_window_size = (hrd_buffer_size * 1000) / rc_bits_per_second;
+
+ av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s, %d%% of %"PRId64" bps "
+ "over %d ms.\n", ctx->va_rc_mode == VA_RC_VBR ? "VBR" : "CBR",
+ rc_target_percentage, rc_bits_per_second, rc_window_size);
+ av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, "
+ "initial fullness %"PRId64" bits.\n",
+ hrd_buffer_size, hrd_initial_buffer_fullness);
+
+ if (rc_bits_per_second > UINT32_MAX ||
+ hrd_buffer_size > UINT32_MAX ||
+ hrd_initial_buffer_fullness > UINT32_MAX) {
+ av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or "
+ "greater are not supported by VAAPI.\n");
+ return AVERROR(EINVAL);
+ }
+
+ ctx->va_bit_rate = rc_bits_per_second;
+
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRateControl,
+ .value = ctx->va_rc_mode,
+ };
+
ctx->rc_params.misc.type = VAEncMiscParameterTypeRateControl;
ctx->rc_params.rc = (VAEncMiscParameterRateControl) {
.bits_per_second = rc_bits_per_second,
@@ -1611,6 +1684,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;
+ err = vaapi_encode_init_rate_control(avctx);
+ if (err < 0)
+ goto fail;
+
err = vaapi_encode_config_attributes(avctx);
if (err < 0)
goto fail;
@@ -1658,12 +1735,6 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
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)
diff --git a/libavcodec/vaapi_encode.h b/libavcodec/vaapi_encode.h
index b8f2ed6d21..6fac4781c3 100644
--- a/libavcodec/vaapi_encode.h
+++ b/libavcodec/vaapi_encode.h
@@ -116,8 +116,6 @@ typedef struct VAAPIEncodeContext {
// Use low power encoding mode.
int low_power;
- // Rate control mode.
- unsigned int va_rc_mode;
// Supported packed headers (initially the desired set, modified
// later to what is actually supported).
unsigned int va_packed_headers;
@@ -138,6 +136,10 @@ typedef struct VAAPIEncodeContext {
VAProfile va_profile;
// Encoding entrypoint (VAEntryoint*).
VAEntrypoint va_entrypoint;
+ // Rate control mode.
+ unsigned int va_rc_mode;
+ // Bitrate for codec-specific encoder parameters.
+ unsigned int va_bit_rate;
// Configuration attributes to use when creating va_config.
VAConfigAttrib config_attributes[MAX_CONFIG_ATTRIBUTES];
diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c
index df6f39a946..1e6eb2e39b 100644
--- a/libavcodec/vaapi_encode_h264.c
+++ b/libavcodec/vaapi_encode_h264.c
@@ -425,9 +425,9 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
// Try to scale these to a sensible range so that the
// golomb encode of the value is not overlong.
hrd->bit_rate_scale =
- av_clip_uintp2(av_log2(avctx->bit_rate) - 15 - 6, 4);
+ av_clip_uintp2(av_log2(ctx->va_bit_rate) - 15 - 6, 4);
hrd->bit_rate_value_minus1[0] =
- (avctx->bit_rate >> hrd->bit_rate_scale + 6) - 1;
+ (ctx->va_bit_rate >> hrd->bit_rate_scale + 6) - 1;
hrd->cpb_size_scale =
av_clip_uintp2(av_log2(ctx->hrd_params.hrd.buffer_size) - 15 - 4,
4);
@@ -497,7 +497,7 @@ static int
vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx)
.intra_idr_period = avctx->gop_size,
.ip_period = ctx->b_per_p + 1,
- .bits_per_second = avctx->bit_rate,
+ .bits_per_second = ctx->va_bit_rate,
.max_num_ref_frames = sps->max_num_ref_frames,
.picture_width_in_mbs = sps->pic_width_in_mbs_minus1 + 1,
.picture_height_in_mbs = sps->pic_height_in_map_units_minus1 + 1,
@@ -823,10 +823,6 @@ static av_cold int
vaapi_encode_h264_configure(AVCodecContext *avctx)
priv->fixed_qp_p = 26;
priv->fixed_qp_b = 26;
- av_log(avctx, AV_LOG_DEBUG, "Using %s-bitrate = %"PRId64" bps.\n",
- ctx->va_rc_mode == VA_RC_CBR ? "constant" : "variable",
- avctx->bit_rate);
-
} else {
av_assert0(0 && "Invalid RC mode.");
}
@@ -934,14 +930,6 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext
*avctx)
return AVERROR_PATCHWELCOME;
}
- if (avctx->bit_rate > 0) {
- if (avctx->rc_max_rate == avctx->bit_rate)
- ctx->va_rc_mode = VA_RC_CBR;
- else
- ctx->va_rc_mode = VA_RC_VBR;
- } else
- ctx->va_rc_mode = VA_RC_CQP;
-
ctx->va_packed_headers =
VA_ENC_PACKED_HEADER_SEQUENCE | // SPS and PPS.
VA_ENC_PACKED_HEADER_SLICE | // Slice headers.
diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c
index b8b66b87cb..b296919b37 100644
--- a/libavcodec/vaapi_encode_h265.c
+++ b/libavcodec/vaapi_encode_h265.c
@@ -512,7 +512,7 @@ static int
vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx)
.intra_period = avctx->gop_size,
.intra_idr_period = avctx->gop_size,
.ip_period = ctx->b_per_p + 1,
- .bits_per_second = avctx->bit_rate,
+ .bits_per_second = ctx->va_bit_rate,
.pic_width_in_luma_samples = sps->pic_width_in_luma_samples,
.pic_height_in_luma_samples = sps->pic_height_in_luma_samples,
@@ -1014,10 +1014,6 @@ static av_cold int
vaapi_encode_h265_configure(AVCodecContext *avctx)
priv->fixed_qp_p = 30;
priv->fixed_qp_b = 30;
- av_log(avctx, AV_LOG_DEBUG, "Using %s-bitrate = %"PRId64" bps.\n",
- ctx->va_rc_mode == VA_RC_CBR ? "constant" : "variable",
- avctx->bit_rate);
-
} else {
av_assert0(0 && "Invalid RC mode.");
}
@@ -1068,14 +1064,6 @@ static av_cold int
vaapi_encode_h265_init(AVCodecContext *avctx)
if (avctx->level == FF_LEVEL_UNKNOWN)
avctx->level = priv->level;
- if (avctx->bit_rate > 0) {
- if (avctx->rc_max_rate == avctx->bit_rate)
- ctx->va_rc_mode = VA_RC_CBR;
- else
- ctx->va_rc_mode = VA_RC_VBR;
- } else
- ctx->va_rc_mode = VA_RC_CQP;
-
ctx->va_packed_headers =
VA_ENC_PACKED_HEADER_SEQUENCE | // VPS, SPS and PPS.
VA_ENC_PACKED_HEADER_SLICE | // Slice headers.
diff --git a/libavcodec/vaapi_encode_mjpeg.c b/libavcodec/vaapi_encode_mjpeg.c
index b328beaa09..67ac2fba96 100644
--- a/libavcodec/vaapi_encode_mjpeg.c
+++ b/libavcodec/vaapi_encode_mjpeg.c
@@ -388,8 +388,6 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext
*avctx)
ctx->codec = &vaapi_encode_type_mjpeg;
- ctx->va_rc_mode = VA_RC_CQP;
-
// The JPEG image header - see note above.
ctx->va_packed_headers =
VA_ENC_PACKED_HEADER_RAW_DATA;
@@ -402,6 +400,7 @@ static av_cold int vaapi_encode_mjpeg_init(AVCodecContext
*avctx)
static const AVCodecDefault vaapi_encode_mjpeg_defaults[] = {
{ "global_quality", "80" },
+ { "b", "0" },
{ NULL },
};
diff --git a/libavcodec/vaapi_encode_mpeg2.c b/libavcodec/vaapi_encode_mpeg2.c
index db79d72115..ff86b8817e 100644
--- a/libavcodec/vaapi_encode_mpeg2.c
+++ b/libavcodec/vaapi_encode_mpeg2.c
@@ -188,8 +188,8 @@ static int
vaapi_encode_mpeg2_init_sequence_params(AVCodecContext *avctx)
memset(pce, 0, sizeof(*pce));
- if (avctx->bit_rate > 0) {
- priv->bit_rate = (avctx->bit_rate + 399) / 400;
+ if (ctx->va_bit_rate > 0) {
+ priv->bit_rate = (ctx->va_bit_rate + 399) / 400;
} else {
// Unknown (not a bitrate-targetting mode), so just use the
// highest value.
@@ -361,7 +361,7 @@ static int
vaapi_encode_mpeg2_init_sequence_params(AVCodecContext *avctx)
.picture_width = avctx->width,
.picture_height = avctx->height,
- .bits_per_second = avctx->bit_rate,
+ .bits_per_second = ctx->va_bit_rate,
.frame_rate = av_q2d(priv->frame_rate),
.aspect_ratio_information = sh->aspect_ratio_information,
.vbv_buffer_size = priv->vbv_buffer_size,
@@ -615,8 +615,6 @@ static av_cold int vaapi_encode_mpeg2_init(AVCodecContext
*avctx)
return AVERROR(EINVAL);
}
- ctx->va_rc_mode = VA_RC_CQP;
-
ctx->va_packed_headers = VA_ENC_PACKED_HEADER_SEQUENCE |
VA_ENC_PACKED_HEADER_PICTURE;
@@ -666,6 +664,7 @@ static const AVOption vaapi_encode_mpeg2_options[] = {
};
static const AVCodecDefault vaapi_encode_mpeg2_defaults[] = {
+ { "b", "0" },
{ "bf", "1" },
{ "g", "120" },
{ "i_qfactor", "1" },
diff --git a/libavcodec/vaapi_encode_vp8.c b/libavcodec/vaapi_encode_vp8.c
index 9588826bfb..40871a6bbf 100644
--- a/libavcodec/vaapi_encode_vp8.c
+++ b/libavcodec/vaapi_encode_vp8.c
@@ -65,7 +65,7 @@ static int
vaapi_encode_vp8_init_sequence_params(AVCodecContext *avctx)
vseq->kf_auto = 0;
if (!(ctx->va_rc_mode & VA_RC_CQP)) {
- vseq->bits_per_second = avctx->bit_rate;
+ vseq->bits_per_second = ctx->va_bit_rate;
vseq->intra_period = avctx->gop_size;
}
@@ -205,17 +205,6 @@ static av_cold int vaapi_encode_vp8_init(AVCodecContext
*avctx)
ctx->codec = &vaapi_encode_type_vp8;
- if (avctx->flags & AV_CODEC_FLAG_QSCALE) {
- ctx->va_rc_mode = VA_RC_CQP;
- } else if (avctx->bit_rate > 0) {
- if (avctx->rc_max_rate == avctx->bit_rate)
- ctx->va_rc_mode = VA_RC_CBR;
- else
- ctx->va_rc_mode = VA_RC_VBR;
- } else {
- ctx->va_rc_mode = VA_RC_CQP;
- }
-
// Packed headers are not currently supported.
ctx->va_packed_headers = 0;
diff --git a/libavcodec/vaapi_encode_vp9.c b/libavcodec/vaapi_encode_vp9.c
index 4d7cec0520..e400bc8b79 100644
--- a/libavcodec/vaapi_encode_vp9.c
+++ b/libavcodec/vaapi_encode_vp9.c
@@ -71,7 +71,7 @@ static int
vaapi_encode_vp9_init_sequence_params(AVCodecContext *avctx)
vseq->kf_auto = 0;
if (!(ctx->va_rc_mode & VA_RC_CQP)) {
- vseq->bits_per_second = avctx->bit_rate;
+ vseq->bits_per_second = ctx->va_bit_rate;
vseq->intra_period = avctx->gop_size;
}
@@ -227,17 +227,6 @@ static av_cold int vaapi_encode_vp9_init(AVCodecContext
*avctx)
ctx->codec = &vaapi_encode_type_vp9;
- if (avctx->flags & AV_CODEC_FLAG_QSCALE) {
- ctx->va_rc_mode = VA_RC_CQP;
- } else if (avctx->bit_rate > 0) {
- if (avctx->bit_rate == avctx->rc_max_rate)
- ctx->va_rc_mode = VA_RC_CBR;
- else
- ctx->va_rc_mode = VA_RC_VBR;
- } else {
- ctx->va_rc_mode = VA_RC_CQP;
- }
-
// Packed headers are not currently supported.
ctx->va_packed_headers = 0;
The logic for rate control is more clear to me in this patch.

Thanks
Haihao
Mark Thompson
2018-06-13 22:42:36 UTC
Permalink
Post by Xiang, Haihao
Query which modes are supported and select between VBR and CBR based
on that - this removes all of the codec-specific rate control mode
selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173 ++++++++++++++++++++++++++++-----------
-
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
...
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index f4c063734c..5de5483454 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
...
@@ -1313,44 +1286,144 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- int rc_bits_per_second;
- int rc_target_percentage;
- int rc_window_size;
- int hrd_buffer_size;
- int hrd_initial_buffer_fullness;
+ int64_t rc_bits_per_second;
+ int rc_target_percentage;
+ int rc_window_size;
+ int64_t hrd_buffer_size;
+ int64_t hrd_initial_buffer_fullness;
int fr_num, fr_den;
+ VAConfigAttrib rc_attr = { VAConfigAttribRateControl };
+ VAStatus vas;
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile, ctx->va_entrypoint,
+ &rc_attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query rate control "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
- if (avctx->bit_rate > INT32_MAX) {
- av_log(avctx, AV_LOG_ERROR, "Target bitrate of 2^31 bps or "
- "higher is not supported.\n");
+ if (rc_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not report any "
+ "supported rate control modes: assuming constant-quality.\n");
How about logging it as warning message?
What would a user do about it?

(Note that it gets logged for JPEG encoding on all versions of the i965 driver except the most recent, so if it were a warning it would be seen by everyone using those versions.)
Post by Xiang, Haihao
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ }
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using constant-quality mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support "
+ "constant-quality mode (%#x).\n", rc_attr.value);
+ return AVERROR(EINVAL);
+ }
+ }
+
+ if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any "
+ "bitrate-targetted rate control modes.\n");
return AVERROR(EINVAL);
}
if (avctx->rc_buffer_size)
hrd_buffer_size = avctx->rc_buffer_size;
+ else if (avctx->rc_max_rate > 0)
+ hrd_buffer_size = avctx->rc_max_rate;
else
hrd_buffer_size = avctx->bit_rate;
- if (avctx->rc_initial_buffer_occupancy)
+ if (avctx->rc_initial_buffer_occupancy) {
+ if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer settings: "
+ "must have initial buffer size (%d) < "
+ "buffer size (%"PRId64").\n",
+ avctx->rc_initial_buffer_occupancy, hrd_buffer_size);
+ return AVERROR(EINVAL);
+ }
hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy;
- else
+ } else {
hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4;
+ }
+
+ if (avctx->rc_max_rate && avctx->rc_max_rate < avctx->bit_rate) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid bitrate settings: must have "
+ "bitrate (%"PRId64") <= maxrate (%"PRId64").\n",
+ avctx->bit_rate, avctx->rc_max_rate);
+ return AVERROR(EINVAL);
+ }
+
+ if (avctx->rc_max_rate > avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_VBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support "
+ "VBR mode (%#x), using CBR mode instead.\n",
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_CBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_VBR;
+ }
+
+ rc_bits_per_second = avctx->rc_max_rate;
+ rc_target_percentage = (avctx->bit_rate * 100) / avctx->rc_max_rate;
I think rc_target_percentage should be 100 for CBR case.
Yes; fixed.

The rc_bits_per_second value is also wrong in that case (shouldn't be rc_max_rate if we can't use it), fixed similarly.
Post by Xiang, Haihao
+
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support "
+ "CBR mode (%#x), using VBR mode instead.\n",
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal bitrate.
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
Is it better to take it as CBR when avctx->rc_max_rate is 0 and CBR is supported
by driver?
I don't think so? VBR with the specified target is probably what you want in most cases, and I think anyone with specific constraints that want constant bitrate should expect to set maxrate to achieve that.
Post by Xiang, Haihao
+
+ // We only have a target bitrate, but VAAPI requires that a
+ // maximum rate be supplied as well. Since the user has
+ // offered no particular constraint, arbitrarily pick a
+ // maximum rate of double the target rate.
+ rc_bits_per_second = 2 * avctx->bit_rate;
+ rc_target_percentage = 50;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- } else {
- rc_bits_per_second = avctx->rc_max_rate;
- rc_target_percentage = (avctx->bit_rate * 100) /
rc_bits_per_second;
}
- rc_window_size = (hrd_buffer_size * 1000) / avctx->bit_rate;
}
+ rc_window_size = (hrd_buffer_size * 1000) / rc_bits_per_second;
+
+ av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s, %d%% of %"PRId64" bps "
+ "over %d ms.\n", ctx->va_rc_mode == VA_RC_VBR ? "VBR" : "CBR",
+ rc_target_percentage, rc_bits_per_second, rc_window_size);
+ av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, "
+ "initial fullness %"PRId64" bits.\n",
+ hrd_buffer_size, hrd_initial_buffer_fullness);
+
+ if (rc_bits_per_second > UINT32_MAX ||
+ hrd_buffer_size > UINT32_MAX ||
+ hrd_initial_buffer_fullness > UINT32_MAX) {
+ av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or "
+ "greater are not supported by VAAPI.\n");
+ return AVERROR(EINVAL);
+ }
+
+ ctx->va_bit_rate = rc_bits_per_second;
+
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRateControl,
+ .value = ctx->va_rc_mode,
+ };
+
ctx->rc_params.misc.type = VAEncMiscParameterTypeRateControl;
ctx->rc_params.rc = (VAEncMiscParameterRateControl) {
.bits_per_second = rc_bits_per_second,
@@ -1611,6 +1684,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;
+ err = vaapi_encode_init_rate_control(avctx);
+ if (err < 0)
+ goto fail;
+
err = vaapi_encode_config_attributes(avctx);
if (err < 0)
goto fail;
@@ -1658,12 +1735,6 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
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)
...
The logic for rate control is more clear to me in this patch.
Thanks,

- Mark
Xiang, Haihao
2018-06-14 06:07:42 UTC
Permalink
Post by Mark Thompson
Post by Xiang, Haihao
Query which modes are supported and select between VBR and CBR based
on that - this removes all of the codec-specific rate control mode
selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173 ++++++++++++++++++++++++++++-------
----
-
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
...
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index f4c063734c..5de5483454 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
...
@@ -1313,44 +1286,144 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- int rc_bits_per_second;
- int rc_target_percentage;
- int rc_window_size;
- int hrd_buffer_size;
- int hrd_initial_buffer_fullness;
+ int64_t rc_bits_per_second;
+ int rc_target_percentage;
+ int rc_window_size;
+ int64_t hrd_buffer_size;
+ int64_t hrd_initial_buffer_fullness;
int fr_num, fr_den;
+ VAConfigAttrib rc_attr = { VAConfigAttribRateControl };
+ VAStatus vas;
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile, ctx->va_entrypoint,
+ &rc_attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query rate control "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
- if (avctx->bit_rate > INT32_MAX) {
- av_log(avctx, AV_LOG_ERROR, "Target bitrate of 2^31 bps or "
- "higher is not supported.\n");
+ if (rc_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not report any "
+ "supported rate control modes: assuming constant-
quality.\n");
How about logging it as warning message?
What would a user do about it?
User may know what is not supported in the driver and he/she might get wrong
result, he/she may file an issue to the driver.
Post by Mark Thompson
(Note that it gets logged for JPEG encoding on all versions of the i965 driver
except the most recent, so if it were a warning it would be seen by everyone
using those versions.)
Post by Xiang, Haihao
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ }
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using constant-quality mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support "
+ "constant-quality mode (%#x).\n", rc_attr.value);
+ return AVERROR(EINVAL);
+ }
+ }
+
+ if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any "
+ "bitrate-targetted rate control modes.\n");
return AVERROR(EINVAL);
}
if (avctx->rc_buffer_size)
hrd_buffer_size = avctx->rc_buffer_size;
+ else if (avctx->rc_max_rate > 0)
+ hrd_buffer_size = avctx->rc_max_rate;
else
hrd_buffer_size = avctx->bit_rate;
- if (avctx->rc_initial_buffer_occupancy)
+ if (avctx->rc_initial_buffer_occupancy) {
+ if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer settings: "
+ "must have initial buffer size (%d) < "
+ "buffer size (%"PRId64").\n",
+ avctx->rc_initial_buffer_occupancy, hrd_buffer_size);
+ return AVERROR(EINVAL);
+ }
hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy;
- else
+ } else {
hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4;
+ }
+
+ if (avctx->rc_max_rate && avctx->rc_max_rate < avctx->bit_rate) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid bitrate settings: must have "
+ "bitrate (%"PRId64") <= maxrate (%"PRId64").\n",
+ avctx->bit_rate, avctx->rc_max_rate);
+ return AVERROR(EINVAL);
+ }
+
+ if (avctx->rc_max_rate > avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_VBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support "
+ "VBR mode (%#x), using CBR mode instead.\n",
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_CBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_VBR;
+ }
+
+ rc_bits_per_second = avctx->rc_max_rate;
+ rc_target_percentage = (avctx->bit_rate * 100) / avctx-
rc_max_rate;
I think rc_target_percentage should be 100 for CBR case.
Yes; fixed.
The rc_bits_per_second value is also wrong in that case (shouldn't be
rc_max_rate if we can't use it), fixed similarly.
Post by Xiang, Haihao
+
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support "
+ "CBR mode (%#x), using VBR mode instead.\n",
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal bitrate.
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
Is it better to take it as CBR when avctx->rc_max_rate is 0 and CBR is supported
by driver?
I don't think so? VBR with the specified target is probably what you want in
most cases, and I think anyone with specific constraints that want constant
bitrate should expect to set maxrate to achieve that.
I agree VBR is probably what an user wants in most case, however target percent
set to 50% is not suitable for most case. To get a specific target percent, user
should set both target bitrate and max bitrate, so it is reasonable to ask user
must set both target bitrate and max bitrate for VBR cases, and for CBR user may
set target bitrate only.

I just saw it is also VBR in QSV when max bitrate is not set so I'm fine to keep
consistency with QSV for this case.

Thanks
Haihao
Post by Mark Thompson
Post by Xiang, Haihao
+
+ // We only have a target bitrate, but VAAPI requires that a
+ // maximum rate be supplied as well. Since the user has
+ // offered no particular constraint, arbitrarily pick a
+ // maximum rate of double the target rate.
+ rc_bits_per_second = 2 * avctx->bit_rate;
+ rc_target_percentage = 50;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- } else {
- rc_bits_per_second = avctx->rc_max_rate;
- rc_target_percentage = (avctx->bit_rate * 100) /
rc_bits_per_second;
}
- rc_window_size = (hrd_buffer_size * 1000) / avctx->bit_rate;
}
+ rc_window_size = (hrd_buffer_size * 1000) / rc_bits_per_second;
+
+ av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s, %d%% of %"PRId64" bps "
+ "over %d ms.\n", ctx->va_rc_mode == VA_RC_VBR ? "VBR" : "CBR",
+ rc_target_percentage, rc_bits_per_second, rc_window_size);
+ av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, "
+ "initial fullness %"PRId64" bits.\n",
+ hrd_buffer_size, hrd_initial_buffer_fullness);
+
+ if (rc_bits_per_second > UINT32_MAX ||
+ hrd_buffer_size > UINT32_MAX ||
+ hrd_initial_buffer_fullness > UINT32_MAX) {
+ av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or "
+ "greater are not supported by VAAPI.\n");
+ return AVERROR(EINVAL);
+ }
+
+ ctx->va_bit_rate = rc_bits_per_second;
+
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRateControl,
+ .value = ctx->va_rc_mode,
+ };
+
ctx->rc_params.misc.type = VAEncMiscParameterTypeRateControl;
ctx->rc_params.rc = (VAEncMiscParameterRateControl) {
.bits_per_second = rc_bits_per_second,
@@ -1611,6 +1684,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;
+ err = vaapi_encode_init_rate_control(avctx);
+ if (err < 0)
+ goto fail;
+
err = vaapi_encode_config_attributes(avctx);
if (err < 0)
goto fail;
@@ -1658,12 +1735,6 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
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)
...
The logic for rate control is more clear to me in this patch.
Thanks,
- Mark
_______________________________________________
ffmpeg-devel mailing list
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Li, Zhong
2018-06-14 07:22:57 UTC
Permalink
-----Original Message-----
Of Xiang, Haihao
Sent: Thursday, June 14, 2018 2:08 PM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up rate
control configuration
Post by Mark Thompson
Post by Xiang, Haihao
Query which modes are supported and select between VBR and CBR
based on that - this removes all of the codec-specific rate
control mode selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173
++++++++++++++++++++++++++++-------
Post by Mark Thompson
Post by Xiang, Haihao
----
-
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
...
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index f4c063734c..5de5483454 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
...
@@ -1313,44 +1286,144 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx) static
av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- int rc_bits_per_second;
- int rc_target_percentage;
- int rc_window_size;
- int hrd_buffer_size;
- int hrd_initial_buffer_fullness;
+ int64_t rc_bits_per_second;
+ int rc_target_percentage;
+ int rc_window_size;
+ int64_t hrd_buffer_size;
+ int64_t hrd_initial_buffer_fullness;
int fr_num, fr_den;
+ VAConfigAttrib rc_attr = { VAConfigAttribRateControl };
+ VAStatus vas;
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile,
ctx->va_entrypoint,
Post by Mark Thompson
Post by Xiang, Haihao
+ &rc_attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query rate control
"
Post by Mark Thompson
Post by Xiang, Haihao
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
- if (avctx->bit_rate > INT32_MAX) {
- av_log(avctx, AV_LOG_ERROR, "Target bitrate of 2^31 bps
or "
Post by Mark Thompson
Post by Xiang, Haihao
- "higher is not supported.\n");
+ if (rc_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not report
any "
Post by Mark Thompson
Post by Xiang, Haihao
+ "supported rate control modes: assuming constant-
quality.\n");
How about logging it as warning message?
What would a user do about it?
User may know what is not supported in the driver and he/she might get
wrong result, he/she may file an issue to the driver.
Post by Mark Thompson
(Note that it gets logged for JPEG encoding on all versions of the
i965 driver except the most recent, so if it were a warning it would
be seen by everyone using those versions.)
Post by Xiang, Haihao
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ }
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using
constant-quality
Post by Mark Thompson
Post by Xiang, Haihao
mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "constant-quality mode (%#x).\n",
rc_attr.value);
Post by Mark Thompson
Post by Xiang, Haihao
+ return AVERROR(EINVAL);
+ }
+ }
+
+ if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any
"
Post by Mark Thompson
Post by Xiang, Haihao
+ "bitrate-targetted rate control modes.\n");
return AVERROR(EINVAL);
}
if (avctx->rc_buffer_size)
hrd_buffer_size = avctx->rc_buffer_size;
+ else if (avctx->rc_max_rate > 0)
+ hrd_buffer_size = avctx->rc_max_rate;
else
hrd_buffer_size = avctx->bit_rate;
- if (avctx->rc_initial_buffer_occupancy)
+ if (avctx->rc_initial_buffer_occupancy) {
+ if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer
settings: "
Post by Mark Thompson
Post by Xiang, Haihao
+ "must have initial buffer size (%d) < "
+ "buffer size (%"PRId64").\n",
+ avctx->rc_initial_buffer_occupancy,
hrd_buffer_size);
Post by Mark Thompson
Post by Xiang, Haihao
+ return AVERROR(EINVAL);
+ }
hrd_initial_buffer_fullness =
avctx->rc_initial_buffer_occupancy;
Post by Mark Thompson
Post by Xiang, Haihao
- else
+ } else {
hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4;
+ }
+
+ if (avctx->rc_max_rate && avctx->rc_max_rate < avctx->bit_rate)
{
Post by Mark Thompson
Post by Xiang, Haihao
+ must have
"
+ "bitrate (%"PRId64") <= maxrate (%"PRId64").\n",
+ avctx->bit_rate, avctx->rc_max_rate);
+ return AVERROR(EINVAL);
+ }
+
+ if (avctx->rc_max_rate > avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_VBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "VBR mode (%#x), using CBR mode
instead.\n",
Post by Mark Thompson
Post by Xiang, Haihao
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_CBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_VBR;
+ }
+
+ rc_bits_per_second = avctx->rc_max_rate;
+ rc_target_percentage = (avctx->bit_rate * 100) / avctx-
rc_max_rate;
I think rc_target_percentage should be 100 for CBR case.
Yes; fixed.
The rc_bits_per_second value is also wrong in that case (shouldn't be
rc_max_rate if we can't use it), fixed similarly.
Post by Xiang, Haihao
+
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "CBR mode (%#x), using VBR mode
instead.\n",
Post by Mark Thompson
Post by Xiang, Haihao
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal
bitrate.
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
Is it better to take it as CBR when avctx->rc_max_rate is 0 and CBR
is supported by driver?
I don't think so? VBR with the specified target is probably what you
want in most cases, and I think anyone with specific constraints that
want constant bitrate should expect to set maxrate to achieve that.
I agree VBR is probably what an user wants in most case, however target
percent set to 50% is not suitable for most case. To get a specific target
percent, user should set both target bitrate and max bitrate, so it is
reasonable to ask user must set both target bitrate and max bitrate for VBR
cases, and for CBR user may set target bitrate only.
How about set the max_rate to be a very larger number such as INT_MAX if user hasn't set it?
User may don't set max_rate on purpose, expecting better quality with unlimited bitrate fluctuation (common requirement for local video files).
Double of target_bit_rate is too strict IMHO. And I haven't such a limitation in x264 ABR mode.
I just saw it is also VBR in QSV when max bitrate is not set so I'm fine to keep
consistency with QSV for this case.
What will happen if user set a max_rate but without a setting for target_bitrate?
The mode will be VBR (if driver support) with target_bitrate=0.
Tried this on qsv, MSDK returns an error of invalid video parameters.
Is it ok for vaapi? And also with iHD driver?
Thanks
Haihao
Post by Mark Thompson
Post by Xiang, Haihao
+
+ // We only have a target bitrate, but VAAPI requires
that a
Post by Mark Thompson
Post by Xiang, Haihao
+ // maximum rate be supplied as well. Since the user
has
Post by Mark Thompson
Post by Xiang, Haihao
+ // offered no particular constraint, arbitrarily pick a
+ // maximum rate of double the target rate.
+ rc_bits_per_second = 2 * avctx->bit_rate;
+ rc_target_percentage = 50;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- } else {
- rc_bits_per_second = avctx->rc_max_rate;
- rc_target_percentage = (avctx->bit_rate * 100) /
rc_bits_per_second;
}
- rc_window_size = (hrd_buffer_size * 1000) / avctx->bit_rate;
}
+ rc_window_size = (hrd_buffer_size * 1000) /
+ rc_bits_per_second;
+
+ av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s, %d%%
of %"PRId64" bps "
Post by Mark Thompson
Post by Xiang, Haihao
+ "over %d ms.\n", ctx->va_rc_mode == VA_RC_VBR ?
"VBR" : "CBR",
Post by Mark Thompson
Post by Xiang, Haihao
+ rc_target_percentage, rc_bits_per_second,
rc_window_size);
Post by Mark Thompson
Post by Xiang, Haihao
+ av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, "
+ "initial fullness %"PRId64" bits.\n",
+ hrd_buffer_size, hrd_initial_buffer_fullness);
+
+ if (rc_bits_per_second > UINT32_MAX ||
+ hrd_buffer_size > UINT32_MAX ||
+ hrd_initial_buffer_fullness > UINT32_MAX) {
+ av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or "
+ "greater are not supported by VAAPI.\n");
+ return AVERROR(EINVAL);
+ }
+
+ ctx->va_bit_rate = rc_bits_per_second;
+
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRateControl,
+ .value = ctx->va_rc_mode,
+ };
+
ctx->rc_params.misc.type =
VAEncMiscParameterTypeRateControl;
Post by Mark Thompson
Post by Xiang, Haihao
ctx->rc_params.rc = (VAEncMiscParameterRateControl) {
.bits_per_second = rc_bits_per_second,
@@ -1611,6 +1684,10 @@ av_cold int
ff_vaapi_encode_init(AVCodecContext
*avctx)
if (err < 0)
goto fail;
+ err = vaapi_encode_init_rate_control(avctx);
+ if (err < 0)
+ goto fail;
+
err = vaapi_encode_config_attributes(avctx);
if (err < 0)
goto fail;
@@ -1658,12 +1735,6 @@ av_cold int
ff_vaapi_encode_init(AVCodecContext
*avctx)
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)
...
The logic for rate control is more clear to me in this patch.
Thanks,
- Mark
Xiang, Haihao
2018-06-15 08:03:33 UTC
Permalink
Post by Li, Zhong
-----Original Message-----
Of Xiang, Haihao
Sent: Thursday, June 14, 2018 2:08 PM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up rate
control configuration
Post by Mark Thompson
Post by Xiang, Haihao
Query which modes are supported and select between VBR and CBR
based on that - this removes all of the codec-specific rate
control mode selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173
++++++++++++++++++++++++++++-------
Post by Mark Thompson
Post by Xiang, Haihao
----
-
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
...
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index f4c063734c..5de5483454 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
...
@@ -1313,44 +1286,144 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx) static
av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- int rc_bits_per_second;
- int rc_target_percentage;
- int rc_window_size;
- int hrd_buffer_size;
- int hrd_initial_buffer_fullness;
+ int64_t rc_bits_per_second;
+ int rc_target_percentage;
+ int rc_window_size;
+ int64_t hrd_buffer_size;
+ int64_t hrd_initial_buffer_fullness;
int fr_num, fr_den;
+ VAConfigAttrib rc_attr = { VAConfigAttribRateControl };
+ VAStatus vas;
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile,
ctx->va_entrypoint,
Post by Mark Thompson
Post by Xiang, Haihao
+ &rc_attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query rate control
"
Post by Mark Thompson
Post by Xiang, Haihao
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
- if (avctx->bit_rate > INT32_MAX) {
- av_log(avctx, AV_LOG_ERROR, "Target bitrate of 2^31 bps
or "
Post by Mark Thompson
Post by Xiang, Haihao
- "higher is not supported.\n");
+ if (rc_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not report
any "
Post by Mark Thompson
Post by Xiang, Haihao
+ "supported rate control modes: assuming constant-
quality.\n");
How about logging it as warning message?
What would a user do about it?
User may know what is not supported in the driver and he/she might get
wrong result, he/she may file an issue to the driver.
Post by Mark Thompson
(Note that it gets logged for JPEG encoding on all versions of the
i965 driver except the most recent, so if it were a warning it would
be seen by everyone using those versions.)
Post by Xiang, Haihao
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ }
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using
constant-quality
Post by Mark Thompson
Post by Xiang, Haihao
mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "constant-quality mode (%#x).\n",
rc_attr.value);
Post by Mark Thompson
Post by Xiang, Haihao
+ return AVERROR(EINVAL);
+ }
+ }
+
+ if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any
"
Post by Mark Thompson
Post by Xiang, Haihao
+ "bitrate-targetted rate control modes.\n");
return AVERROR(EINVAL);
}
if (avctx->rc_buffer_size)
hrd_buffer_size = avctx->rc_buffer_size;
+ else if (avctx->rc_max_rate > 0)
+ hrd_buffer_size = avctx->rc_max_rate;
else
hrd_buffer_size = avctx->bit_rate;
- if (avctx->rc_initial_buffer_occupancy)
+ if (avctx->rc_initial_buffer_occupancy) {
+ if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer
settings: "
Post by Mark Thompson
Post by Xiang, Haihao
+ "must have initial buffer size (%d) < "
+ "buffer size (%"PRId64").\n",
+ avctx->rc_initial_buffer_occupancy,
hrd_buffer_size);
Post by Mark Thompson
Post by Xiang, Haihao
+ return AVERROR(EINVAL);
+ }
hrd_initial_buffer_fullness =
avctx->rc_initial_buffer_occupancy;
Post by Mark Thompson
Post by Xiang, Haihao
- else
+ } else {
hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4;
+ }
+
+ if (avctx->rc_max_rate && avctx->rc_max_rate < avctx->bit_rate)
{
Post by Mark Thompson
Post by Xiang, Haihao
+ must have
"
+ "bitrate (%"PRId64") <= maxrate (%"PRId64").\n",
+ avctx->bit_rate, avctx->rc_max_rate);
+ return AVERROR(EINVAL);
+ }
+
+ if (avctx->rc_max_rate > avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_VBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "VBR mode (%#x), using CBR mode
instead.\n",
Post by Mark Thompson
Post by Xiang, Haihao
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_CBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_VBR;
+ }
+
+ rc_bits_per_second = avctx->rc_max_rate;
+ rc_target_percentage = (avctx->bit_rate * 100) / avctx-
rc_max_rate;
I think rc_target_percentage should be 100 for CBR case.
Yes; fixed.
The rc_bits_per_second value is also wrong in that case (shouldn't be
rc_max_rate if we can't use it), fixed similarly.
Post by Xiang, Haihao
+
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "CBR mode (%#x), using VBR mode
instead.\n",
Post by Mark Thompson
Post by Xiang, Haihao
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal
bitrate.
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
Is it better to take it as CBR when avctx->rc_max_rate is 0 and CBR
is supported by driver?
I don't think so? VBR with the specified target is probably what you
want in most cases, and I think anyone with specific constraints that
want constant bitrate should expect to set maxrate to achieve that.
I agree VBR is probably what an user wants in most case, however target
percent set to 50% is not suitable for most case. To get a specific target
percent, user should set both target bitrate and max bitrate, so it is
reasonable to ask user must set both target bitrate and max bitrate for VBR
cases, and for CBR user may set target bitrate only.
How about set the max_rate to be a very larger number such as INT_MAX if user
hasn't set it?
User may don't set max_rate on purpose, expecting better quality with
unlimited bitrate fluctuation (common requirement for local video files).
Double of target_bit_rate is too strict IMHO. And I haven't such a limitation
in x264 ABR mode.
The max bitrate is used to limit the peak bitrate, so setting the max bitrate to
INT_MAX doesn't make sense to me.
Post by Li, Zhong
I just saw it is also VBR in QSV when max bitrate is not set so I'm fine to keep
consistency with QSV for this case.
What will happen if user set a max_rate but without a setting for target_bitrate?
The mode will be VBR (if driver support) with target_bitrate=0.
Tried this on qsv, MSDK returns an error of invalid video parameters.
Is it ok for vaapi? And also with iHD driver?
It is taken as CQP in ffmpeg VAAPI when target bitrate is 0 after applying this
patch, so it should work with iHD driver.
Post by Li, Zhong
Thanks
Haihao
Post by Mark Thompson
Post by Xiang, Haihao
+
+ // We only have a target bitrate, but VAAPI requires
that a
Post by Mark Thompson
Post by Xiang, Haihao
+ // maximum rate be supplied as well. Since the user
has
Post by Mark Thompson
Post by Xiang, Haihao
+ // offered no particular constraint, arbitrarily pick a
+ // maximum rate of double the target rate.
+ rc_bits_per_second = 2 * avctx->bit_rate;
+ rc_target_percentage = 50;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- } else {
- rc_bits_per_second = avctx->rc_max_rate;
- rc_target_percentage = (avctx->bit_rate * 100) /
rc_bits_per_second;
}
- rc_window_size = (hrd_buffer_size * 1000) / avctx->bit_rate;
}
+ rc_window_size = (hrd_buffer_size * 1000) /
+ rc_bits_per_second;
+
+ av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s, %d%%
of %"PRId64" bps "
Post by Mark Thompson
Post by Xiang, Haihao
+ "over %d ms.\n", ctx->va_rc_mode == VA_RC_VBR ?
"VBR" : "CBR",
Post by Mark Thompson
Post by Xiang, Haihao
+ rc_target_percentage, rc_bits_per_second,
rc_window_size);
Post by Mark Thompson
Post by Xiang, Haihao
+ av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, "
+ "initial fullness %"PRId64" bits.\n",
+ hrd_buffer_size, hrd_initial_buffer_fullness);
+
+ if (rc_bits_per_second > UINT32_MAX ||
+ hrd_buffer_size > UINT32_MAX ||
+ hrd_initial_buffer_fullness > UINT32_MAX) {
+ av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or "
+ "greater are not supported by VAAPI.\n");
+ return AVERROR(EINVAL);
+ }
+
+ ctx->va_bit_rate = rc_bits_per_second;
+
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRateControl,
+ .value = ctx->va_rc_mode,
+ };
+
ctx->rc_params.misc.type =
VAEncMiscParameterTypeRateControl;
Post by Mark Thompson
Post by Xiang, Haihao
ctx->rc_params.rc = (VAEncMiscParameterRateControl) {
.bits_per_second = rc_bits_per_second,
@@ -1611,6 +1684,10 @@ av_cold int
ff_vaapi_encode_init(AVCodecContext
*avctx)
if (err < 0)
goto fail;
+ err = vaapi_encode_init_rate_control(avctx);
+ if (err < 0)
+ goto fail;
+
err = vaapi_encode_config_attributes(avctx);
if (err < 0)
goto fail;
@@ -1658,12 +1735,6 @@ av_cold int
ff_vaapi_encode_init(AVCodecContext
*avctx)
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)
...
The logic for rate control is more clear to me in this patch.
Thanks,
- Mark
_______________________________________________
ffmpeg-devel mailing list
http://ffmpeg.org/mailman/listinfo/ffmpeg-devel
Mark Thompson
2018-06-17 13:50:52 UTC
Permalink
Post by Li, Zhong
-----Original Message-----
Of Xiang, Haihao
Sent: Thursday, June 14, 2018 2:08 PM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up rate
control configuration
Post by Mark Thompson
Post by Xiang, Haihao
Query which modes are supported and select between VBR and CBR
based on that - this removes all of the codec-specific rate
control mode selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173
++++++++++++++++++++++++++++-------
Post by Mark Thompson
Post by Xiang, Haihao
----
-
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
...
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index f4c063734c..5de5483454 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
...
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
This condition ^
Post by Li, Zhong
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using
constant-quality
Post by Mark Thompson
Post by Xiang, Haihao
mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "constant-quality mode (%#x).\n",
rc_attr.value);
Post by Mark Thompson
Post by Xiang, Haihao
+ return AVERROR(EINVAL);
+ }
+ }
...
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "CBR mode (%#x), using VBR mode
instead.\n",
Post by Mark Thompson
Post by Xiang, Haihao
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal
bitrate.
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
Is it better to take it as CBR when avctx->rc_max_rate is 0 and CBR
is supported by driver?
I don't think so? VBR with the specified target is probably what you
want in most cases, and I think anyone with specific constraints that
want constant bitrate should expect to set maxrate to achieve that.
I agree VBR is probably what an user wants in most case, however target
percent set to 50% is not suitable for most case. To get a specific target
percent, user should set both target bitrate and max bitrate, so it is
reasonable to ask user must set both target bitrate and max bitrate for VBR
cases, and for CBR user may set target bitrate only.
How about set the max_rate to be a very larger number such as INT_MAX if user hasn't set it?
User may don't set max_rate on purpose, expecting better quality with unlimited bitrate fluctuation (common requirement for local video files).
Double of target_bit_rate is too strict IMHO. And I haven't such a limitation in x264 ABR mode.
This unconstrained setup you describe was my intent (as you say, it's usually what you want for local files), but unfortunately the API doesn't really let us do it - the target bitrate is specified as an integer percentage of the max bitrate. 50% was an arbitrary value picked to not have a strong constraint but also not be small enough that we need to think about rounding/overflow problems.

We could try to pick a large value with the right properties (for example: if target < 2^32 / 100 then choose maxrate = target * 100, percentage = 1; otherwise choose percentage = 2^32 * 100 / bitrate, maxrate = bitrate * 100 / percentage), but that would be complex to test and I don't think the drivers handle this sort of setup very well.
Post by Li, Zhong
I just saw it is also VBR in QSV when max bitrate is not set so I'm fine to keep
consistency with QSV for this case.
What will happen if user set a max_rate but without a setting for target_bitrate?
The mode will be VBR (if driver support) with target_bitrate=0.
Tried this on qsv, MSDK returns an error of invalid video parameters.
Is it ok for vaapi? And also with iHD driver?
If AVCodecContext.bit_rate isn't set then we use constant-quality mode instead - see the block I've pointed out above.

There isn't currently any constant-quality with max-rate constraint mode in VAAPI.
Post by Li, Zhong
Post by Mark Thompson
Post by Xiang, Haihao
+
+ // We only have a target bitrate, but VAAPI requires
that a
Post by Mark Thompson
Post by Xiang, Haihao
+ // maximum rate be supplied as well. Since the user
has
Post by Mark Thompson
Post by Xiang, Haihao
+ // offered no particular constraint, arbitrarily pick a
+ // maximum rate of double the target rate.
+ rc_bits_per_second = 2 * avctx->bit_rate;
+ rc_target_percentage = 50;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- } else {
- rc_bits_per_second = avctx->rc_max_rate;
- rc_target_percentage = (avctx->bit_rate * 100) /
rc_bits_per_second;
}
- rc_window_size = (hrd_buffer_size * 1000) / avctx->bit_rate;
}
...
Thanks,

- Mark
Li, Zhong
2018-06-20 09:44:29 UTC
Permalink
-----Original Message-----
Of Mark Thompson
Sent: Sunday, June 17, 2018 9:51 PM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up rate
control configuration
Post by Li, Zhong
-----Original Message-----
Behalf
Post by Li, Zhong
Of Xiang, Haihao
Sent: Thursday, June 14, 2018 2:08 PM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up
rate control configuration
Post by Mark Thompson
Post by Xiang, Haihao
Query which modes are supported and select between VBR and CBR
based on that - this removes all of the codec-specific rate
control mode selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173
++++++++++++++++++++++++++++-------
Post by Mark Thompson
Post by Xiang, Haihao
----
-
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
...
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index f4c063734c..5de5483454 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
...
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
This condition ^
Post by Li, Zhong
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using
constant-quality
Post by Mark Thompson
Post by Xiang, Haihao
mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "constant-quality mode (%#x).\n",
rc_attr.value);
Post by Mark Thompson
Post by Xiang, Haihao
+ return AVERROR(EINVAL);
+ }
+ }
...
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "CBR mode (%#x), using VBR mode
instead.\n",
Post by Mark Thompson
Post by Xiang, Haihao
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal
bitrate.
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
Is it better to take it as CBR when avctx->rc_max_rate is 0 and CBR
is supported by driver?
I don't think so? VBR with the specified target is probably what
you want in most cases, and I think anyone with specific constraints
that want constant bitrate should expect to set maxrate to achieve that.
I agree VBR is probably what an user wants in most case, however
target percent set to 50% is not suitable for most case. To get a
specific target percent, user should set both target bitrate and max
bitrate, so it is reasonable to ask user must set both target bitrate
and max bitrate for VBR cases, and for CBR user may set target bitrate
only.
Post by Li, Zhong
How about set the max_rate to be a very larger number such as INT_MAX
if user hasn't set it?
Post by Li, Zhong
User may don't set max_rate on purpose, expecting better quality with
unlimited bitrate fluctuation (common requirement for local video files).
Post by Li, Zhong
Double of target_bit_rate is too strict IMHO. And I haven't such a
limitation in x264 ABR mode.
This unconstrained setup you describe was my intent (as you say, it's usually
what you want for local files), but unfortunately the API doesn't really let us
do it - the target bitrate is specified as an integer percentage of the max
bitrate. 50% was an arbitrary value picked to not have a strong constraint
but also not be small enough that we need to think about rounding/overflow
problems.
We could try to pick a large value with the right properties (for example: if
target < 2^32 / 100 then choose maxrate = target * 100, percentage = 1;
otherwise choose percentage = 2^32 * 100 / bitrate, maxrate = bitrate * 100
/ percentage), but that would be complex to test and I don't think the drivers
handle this sort of setup very well.
Post by Li, Zhong
I just saw it is also VBR in QSV when max bitrate is not set so I'm
fine to keep consistency with QSV for this case.
What will happen if user set a max_rate but without a setting for
target_bitrate?
Post by Li, Zhong
The mode will be VBR (if driver support) with target_bitrate=0.
Tried this on qsv, MSDK returns an error of invalid video parameters.
Is it ok for vaapi? And also with iHD driver?
If AVCodecContext.bit_rate isn't set then we use constant-quality mode
instead - see the block I've pointed out above.
There isn't currently any constant-quality with max-rate constraint mode in VAAPI.
Then the problem I see is that -max_rate hasn't been respected well if user set it (he may don't care about the target bitrate except the peak value).
Maybe we can add a warning at least?
Post by Li, Zhong
Post by Mark Thompson
Post by Xiang, Haihao
+
+ // We only have a target bitrate, but VAAPI requires
that a
Post by Mark Thompson
Post by Xiang, Haihao
+ // maximum rate be supplied as well. Since the user
has
Post by Mark Thompson
Post by Xiang, Haihao
+ // offered no particular constraint, arbitrarily pick a
+ // maximum rate of double the target rate.
+ rc_bits_per_second = 2 * avctx->bit_rate;
+ rc_target_percentage = 50;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- } else {
- rc_bits_per_second = avctx->rc_max_rate;
- rc_target_percentage = (avctx->bit_rate * 100) /
rc_bits_per_second;
}
- rc_window_size = (hrd_buffer_size * 1000) /
avctx->bit_rate;
Post by Li, Zhong
Post by Mark Thompson
Post by Xiang, Haihao
}
...
Thanks,
- Mark
Mark Thompson
2018-06-20 23:10:04 UTC
Permalink
Post by Li, Zhong
-----Original Message-----
Of Mark Thompson
Sent: Sunday, June 17, 2018 9:51 PM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up rate
control configuration
Post by Li, Zhong
-----Original Message-----
Behalf
Post by Li, Zhong
Of Xiang, Haihao
Sent: Thursday, June 14, 2018 2:08 PM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up
rate control configuration
Post by Mark Thompson
Post by Xiang, Haihao
Query which modes are supported and select between VBR and CBR
based on that - this removes all of the codec-specific rate
control mode selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173
++++++++++++++++++++++++++++-------
Post by Mark Thompson
Post by Xiang, Haihao
----
-
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
...
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index f4c063734c..5de5483454 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
...
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
This condition ^
Post by Li, Zhong
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using
constant-quality
Post by Mark Thompson
Post by Xiang, Haihao
mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "constant-quality mode (%#x).\n",
rc_attr.value);
Post by Mark Thompson
Post by Xiang, Haihao
+ return AVERROR(EINVAL);
+ }
+ }
...
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "CBR mode (%#x), using VBR mode
instead.\n",
Post by Mark Thompson
Post by Xiang, Haihao
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal
bitrate.
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
Is it better to take it as CBR when avctx->rc_max_rate is 0 and CBR
is supported by driver?
I don't think so? VBR with the specified target is probably what
you want in most cases, and I think anyone with specific constraints
that want constant bitrate should expect to set maxrate to achieve that.
I agree VBR is probably what an user wants in most case, however
target percent set to 50% is not suitable for most case. To get a
specific target percent, user should set both target bitrate and max
bitrate, so it is reasonable to ask user must set both target bitrate
and max bitrate for VBR cases, and for CBR user may set target bitrate
only.
Post by Li, Zhong
How about set the max_rate to be a very larger number such as INT_MAX
if user hasn't set it?
Post by Li, Zhong
User may don't set max_rate on purpose, expecting better quality with
unlimited bitrate fluctuation (common requirement for local video files).
Post by Li, Zhong
Double of target_bit_rate is too strict IMHO. And I haven't such a
limitation in x264 ABR mode.
This unconstrained setup you describe was my intent (as you say, it's usually
what you want for local files), but unfortunately the API doesn't really let us
do it - the target bitrate is specified as an integer percentage of the max
bitrate. 50% was an arbitrary value picked to not have a strong constraint
but also not be small enough that we need to think about rounding/overflow
problems.
We could try to pick a large value with the right properties (for example: if
target < 2^32 / 100 then choose maxrate = target * 100, percentage = 1;
otherwise choose percentage = 2^32 * 100 / bitrate, maxrate = bitrate * 100
/ percentage), but that would be complex to test and I don't think the drivers
handle this sort of setup very well.
Post by Li, Zhong
I just saw it is also VBR in QSV when max bitrate is not set so I'm
fine to keep consistency with QSV for this case.
What will happen if user set a max_rate but without a setting for
target_bitrate?
Post by Li, Zhong
The mode will be VBR (if driver support) with target_bitrate=0.
Tried this on qsv, MSDK returns an error of invalid video parameters.
Is it ok for vaapi? And also with iHD driver?
If AVCodecContext.bit_rate isn't set then we use constant-quality mode
instead - see the block I've pointed out above.
There isn't currently any constant-quality with max-rate constraint mode in VAAPI.
Then the problem I see is that -max_rate hasn't been respected well if user set it (he may don't care about the target bitrate except the peak value).
Maybe we can add a warning at least?
Given that it's really CQP, I don't think anyone would ever expect this to work? Encoders generally don't warn about ignoring extra irrelevant options in AVCodecContext.

(Is there any encoder at all which supports that combination? E.g. libx264 supports maxrate in CRF but not CQP mode.)

- Mark
Michael Niedermayer
2018-06-21 17:03:19 UTC
Permalink
Post by Mark Thompson
Post by Li, Zhong
-----Original Message-----
Of Mark Thompson
Sent: Sunday, June 17, 2018 9:51 PM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up rate
control configuration
Post by Li, Zhong
-----Original Message-----
Behalf
Post by Li, Zhong
Of Xiang, Haihao
Sent: Thursday, June 14, 2018 2:08 PM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up
rate control configuration
Post by Mark Thompson
Post by Xiang, Haihao
Query which modes are supported and select between VBR and CBR
based on that - this removes all of the codec-specific rate
control mode selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173
++++++++++++++++++++++++++++-------
Post by Mark Thompson
Post by Xiang, Haihao
----
-
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
...
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index f4c063734c..5de5483454 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
...
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
This condition ^
Post by Li, Zhong
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using
constant-quality
Post by Mark Thompson
Post by Xiang, Haihao
mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "constant-quality mode (%#x).\n",
rc_attr.value);
Post by Mark Thompson
Post by Xiang, Haihao
+ return AVERROR(EINVAL);
+ }
+ }
...
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "CBR mode (%#x), using VBR mode
instead.\n",
Post by Mark Thompson
Post by Xiang, Haihao
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal
bitrate.
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
Is it better to take it as CBR when avctx->rc_max_rate is 0 and CBR
is supported by driver?
I don't think so? VBR with the specified target is probably what
you want in most cases, and I think anyone with specific constraints
that want constant bitrate should expect to set maxrate to achieve that.
I agree VBR is probably what an user wants in most case, however
target percent set to 50% is not suitable for most case. To get a
specific target percent, user should set both target bitrate and max
bitrate, so it is reasonable to ask user must set both target bitrate
and max bitrate for VBR cases, and for CBR user may set target bitrate
only.
Post by Li, Zhong
How about set the max_rate to be a very larger number such as INT_MAX
if user hasn't set it?
Post by Li, Zhong
User may don't set max_rate on purpose, expecting better quality with
unlimited bitrate fluctuation (common requirement for local video files).
Post by Li, Zhong
Double of target_bit_rate is too strict IMHO. And I haven't such a
limitation in x264 ABR mode.
This unconstrained setup you describe was my intent (as you say, it's usually
what you want for local files), but unfortunately the API doesn't really let us
do it - the target bitrate is specified as an integer percentage of the max
bitrate. 50% was an arbitrary value picked to not have a strong constraint
but also not be small enough that we need to think about rounding/overflow
problems.
We could try to pick a large value with the right properties (for example: if
target < 2^32 / 100 then choose maxrate = target * 100, percentage = 1;
otherwise choose percentage = 2^32 * 100 / bitrate, maxrate = bitrate * 100
/ percentage), but that would be complex to test and I don't think the drivers
handle this sort of setup very well.
Post by Li, Zhong
I just saw it is also VBR in QSV when max bitrate is not set so I'm
fine to keep consistency with QSV for this case.
What will happen if user set a max_rate but without a setting for
target_bitrate?
Post by Li, Zhong
The mode will be VBR (if driver support) with target_bitrate=0.
Tried this on qsv, MSDK returns an error of invalid video parameters.
Is it ok for vaapi? And also with iHD driver?
If AVCodecContext.bit_rate isn't set then we use constant-quality mode
instead - see the block I've pointed out above.
There isn't currently any constant-quality with max-rate constraint mode in VAAPI.
Then the problem I see is that -max_rate hasn't been respected well if user set it (he may don't care about the target bitrate except the peak value).
Maybe we can add a warning at least?
Given that it's really CQP, I don't think anyone would ever expect this to work? Encoders generally don't warn about ignoring extra irrelevant options in AVCodecContext.
(Is there any encoder at all which supports that combination? E.g. libx264 supports maxrate in CRF but not CQP mode.)
if i understand correctly, then yes, see vbv_ignore_qmax. If max rate
cannot be achievied the mpegvideo encoders should attempt to encode the frame
again without qmax and at lower quality



[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB

it is not once nor twice but times without number that the same ideas make
their appearance in the world. -- Aristotle
Mark Thompson
2018-06-25 21:30:24 UTC
Permalink
Post by Michael Niedermayer
Post by Mark Thompson
Post by Li, Zhong
-----Original Message-----
Of Mark Thompson
Sent: Sunday, June 17, 2018 9:51 PM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up rate
control configuration
Post by Li, Zhong
-----Original Message-----
Behalf
Post by Li, Zhong
Of Xiang, Haihao
Sent: Thursday, June 14, 2018 2:08 PM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up
rate control configuration
Post by Mark Thompson
Post by Xiang, Haihao
Query which modes are supported and select between VBR and CBR
based on that - this removes all of the codec-specific rate
control mode selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173
++++++++++++++++++++++++++++-------
Post by Mark Thompson
Post by Xiang, Haihao
----
-
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
...
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index f4c063734c..5de5483454 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
...
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
This condition ^
Post by Li, Zhong
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using
constant-quality
Post by Mark Thompson
Post by Xiang, Haihao
mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "constant-quality mode (%#x).\n",
rc_attr.value);
Post by Mark Thompson
Post by Xiang, Haihao
+ return AVERROR(EINVAL);
+ }
+ }
...
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "CBR mode (%#x), using VBR mode
instead.\n",
Post by Mark Thompson
Post by Xiang, Haihao
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal
bitrate.
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
Is it better to take it as CBR when avctx->rc_max_rate is 0 and CBR
is supported by driver?
I don't think so? VBR with the specified target is probably what
you want in most cases, and I think anyone with specific constraints
that want constant bitrate should expect to set maxrate to achieve that.
I agree VBR is probably what an user wants in most case, however
target percent set to 50% is not suitable for most case. To get a
specific target percent, user should set both target bitrate and max
bitrate, so it is reasonable to ask user must set both target bitrate
and max bitrate for VBR cases, and for CBR user may set target bitrate
only.
Post by Li, Zhong
How about set the max_rate to be a very larger number such as INT_MAX
if user hasn't set it?
Post by Li, Zhong
User may don't set max_rate on purpose, expecting better quality with
unlimited bitrate fluctuation (common requirement for local video files).
Post by Li, Zhong
Double of target_bit_rate is too strict IMHO. And I haven't such a
limitation in x264 ABR mode.
This unconstrained setup you describe was my intent (as you say, it's usually
what you want for local files), but unfortunately the API doesn't really let us
do it - the target bitrate is specified as an integer percentage of the max
bitrate. 50% was an arbitrary value picked to not have a strong constraint
but also not be small enough that we need to think about rounding/overflow
problems.
We could try to pick a large value with the right properties (for example: if
target < 2^32 / 100 then choose maxrate = target * 100, percentage = 1;
otherwise choose percentage = 2^32 * 100 / bitrate, maxrate = bitrate * 100
/ percentage), but that would be complex to test and I don't think the drivers
handle this sort of setup very well.
Post by Li, Zhong
I just saw it is also VBR in QSV when max bitrate is not set so I'm
fine to keep consistency with QSV for this case.
What will happen if user set a max_rate but without a setting for
target_bitrate?
Post by Li, Zhong
The mode will be VBR (if driver support) with target_bitrate=0.
Tried this on qsv, MSDK returns an error of invalid video parameters.
Is it ok for vaapi? And also with iHD driver?
If AVCodecContext.bit_rate isn't set then we use constant-quality mode
instead - see the block I've pointed out above.
There isn't currently any constant-quality with max-rate constraint mode in VAAPI.
Then the problem I see is that -max_rate hasn't been respected well if user set it (he may don't care about the target bitrate except the peak value).
Maybe we can add a warning at least?
Given that it's really CQP, I don't think anyone would ever expect this to work? Encoders generally don't warn about ignoring extra irrelevant options in AVCodecContext.
(Is there any encoder at all which supports that combination? E.g. libx264 supports maxrate in CRF but not CQP mode.)
if i understand correctly, then yes, see vbv_ignore_qmax. If max rate
cannot be achievied the mpegvideo encoders should attempt to encode the frame
again without qmax and at lower quality
Ok, fair enough.

I've added a warning as below so that it's clear this case isn't supported.

diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index 53c3a2132a..25d89c65c9 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -1311,6 +1311,10 @@ static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
if (rc_attr.value & VA_RC_CQP) {
av_log(avctx, AV_LOG_VERBOSE, "Using constant-quality mode.\n");
ctx->va_rc_mode = VA_RC_CQP;
+ if (avctx->bit_rate > 0 || avctx->rc_max_rate > 0) {
+ av_log(avctx, AV_LOG_WARNING, "Bitrate target parameters "
+ "ignored in constant-quality mode.\n");
+ }
return 0;
} else {
av_log(avctx, AV_LOG_ERROR, "Driver does not support "

Thanks,

- Mark
Li, Zhong
2018-07-03 05:14:09 UTC
Permalink
-----Original Message-----
Of Mark Thompson
Sent: Tuesday, June 26, 2018 5:30 AM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up rate
control configuration
Post by Michael Niedermayer
Post by Mark Thompson
Post by Li, Zhong
-----Original Message-----
Behalf Of Mark Thompson
Sent: Sunday, June 17, 2018 9:51 PM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean up
rate control configuration
Post by Li, Zhong
-----Original Message-----
Behalf
Post by Li, Zhong
Of Xiang, Haihao
Sent: Thursday, June 14, 2018 2:08 PM
Subject: Re: [FFmpeg-devel] [PATCH v2 16/36] vaapi_encode: Clean
up rate control configuration
Post by Mark Thompson
Post by Xiang, Haihao
Query which modes are supported and select between VBR and
CBR
Post by Michael Niedermayer
Post by Mark Thompson
Post by Li, Zhong
Post by Li, Zhong
Post by Mark Thompson
Post by Xiang, Haihao
based on that - this removes all of the codec-specific rate
control mode selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173
++++++++++++++++++++++++++++-------
Post by Mark Thompson
Post by Xiang, Haihao
----
-
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
...
diff --git a/libavcodec/vaapi_encode.c
b/libavcodec/vaapi_encode.c index f4c063734c..5de5483454
100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
...
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
This condition ^
Post by Li, Zhong
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using
constant-quality
Post by Mark Thompson
Post by Xiang, Haihao
mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "constant-quality mode (%#x).\n",
rc_attr.value);
Post by Mark Thompson
Post by Xiang, Haihao
+ return AVERROR(EINVAL);
+ }
+ }
...
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does
not
Post by Michael Niedermayer
Post by Mark Thompson
Post by Li, Zhong
Post by Li, Zhong
support "
Post by Mark Thompson
Post by Xiang, Haihao
+ "CBR mode (%#x), using VBR mode
instead.\n",
Post by Mark Thompson
Post by Xiang, Haihao
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal
bitrate.
Post by Mark Thompson
Post by Xiang, Haihao
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
Is it better to take it as CBR when avctx->rc_max_rate is 0 and
CBR is supported by driver?
I don't think so? VBR with the specified target is probably
what you want in most cases, and I think anyone with specific
constraints that want constant bitrate should expect to set maxrate
to achieve that.
Post by Michael Niedermayer
Post by Mark Thompson
Post by Li, Zhong
Post by Li, Zhong
I agree VBR is probably what an user wants in most case, however
target percent set to 50% is not suitable for most case. To get a
specific target percent, user should set both target bitrate and
max bitrate, so it is reasonable to ask user must set both target
bitrate and max bitrate for VBR cases, and for CBR user may set
target bitrate
only.
Post by Li, Zhong
How about set the max_rate to be a very larger number such as INT_MAX
if user hasn't set it?
Post by Li, Zhong
User may don't set max_rate on purpose, expecting better quality with
unlimited bitrate fluctuation (common requirement for local video
files).
Post by Michael Niedermayer
Post by Mark Thompson
Post by Li, Zhong
Post by Li, Zhong
Double of target_bit_rate is too strict IMHO. And I haven't such a
limitation in x264 ABR mode.
This unconstrained setup you describe was my intent (as you say,
it's usually what you want for local files), but unfortunately the
API doesn't really let us do it - the target bitrate is specified
as an integer percentage of the max bitrate. 50% was an arbitrary
value picked to not have a strong constraint but also not be small
enough that we need to think about rounding/overflow problems.
We could try to pick a large value with the right properties (for
example: if target < 2^32 / 100 then choose maxrate = target * 100,
percentage = 1; otherwise choose percentage = 2^32 * 100 / bitrate,
maxrate = bitrate * 100 / percentage), but that would be complex to
test and I don't think the drivers handle this sort of setup very well.
Post by Li, Zhong
I just saw it is also VBR in QSV when max bitrate is not set so
I'm fine to keep consistency with QSV for this case.
What will happen if user set a max_rate but without a setting for
target_bitrate?
Post by Li, Zhong
The mode will be VBR (if driver support) with target_bitrate=0.
Tried this on qsv, MSDK returns an error of invalid video parameters.
Is it ok for vaapi? And also with iHD driver?
If AVCodecContext.bit_rate isn't set then we use constant-quality
mode instead - see the block I've pointed out above.
There isn't currently any constant-quality with max-rate constraint
mode in VAAPI.
Then the problem I see is that -max_rate hasn't been respected well if
user set it (he may don't care about the target bitrate except the peak value).
Post by Michael Niedermayer
Post by Mark Thompson
Post by Li, Zhong
Maybe we can add a warning at least?
Given that it's really CQP, I don't think anyone would ever expect this to
work? Encoders generally don't warn about ignoring extra irrelevant
options in AVCodecContext.
Post by Michael Niedermayer
Post by Mark Thompson
(Is there any encoder at all which supports that combination? E.g.
libx264 supports maxrate in CRF but not CQP mode.)
if i understand correctly, then yes, see vbv_ignore_qmax. If max rate
cannot be achievied the mpegvideo encoders should attempt to encode
the frame again without qmax and at lower quality
Ok, fair enough.
I've added a warning as below so that it's clear this case isn't supported.
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c index
53c3a2132a..25d89c65c9 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
@@ -1311,6 +1311,10 @@ static av_cold int
vaapi_encode_init_rate_control(AVCodecContext *avctx)
if (rc_attr.value & VA_RC_CQP) {
av_log(avctx, AV_LOG_VERBOSE, "Using constant-quality mode.\n");
ctx->va_rc_mode = VA_RC_CQP;
+ if (avctx->bit_rate > 0 || avctx->rc_max_rate > 0) {
+ av_log(avctx, AV_LOG_WARNING, "Bitrate target
parameters "
+ "ignored in constant-quality mode.\n");
+ }
return 0;
} else {
av_log(avctx, AV_LOG_ERROR, "Driver does not support "
Thanks,
- Mark
I have another idea: if rc_max_rate is set, then set rc_mode to be VBR (align with amfenc). Target bitrate can be set to a half (or anther value) of rc_max_rate if necessary.
CQP is not as good as CRF and should be low priority mode especially when max_rate is set.
Mark Thompson
2018-06-17 13:17:48 UTC
Permalink
Post by Xiang, Haihao
Post by Mark Thompson
Post by Xiang, Haihao
Query which modes are supported and select between VBR and CBR based
on that - this removes all of the codec-specific rate control mode
selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173 ++++++++++++++++++++++++++++-------
----
-
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
...
diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c
index f4c063734c..5de5483454 100644
--- a/libavcodec/vaapi_encode.c
+++ b/libavcodec/vaapi_encode.c
...
@@ -1313,44 +1286,144 @@ static av_cold int
vaapi_encode_config_attributes(AVCodecContext *avctx)
static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- int rc_bits_per_second;
- int rc_target_percentage;
- int rc_window_size;
- int hrd_buffer_size;
- int hrd_initial_buffer_fullness;
+ int64_t rc_bits_per_second;
+ int rc_target_percentage;
+ int rc_window_size;
+ int64_t hrd_buffer_size;
+ int64_t hrd_initial_buffer_fullness;
int fr_num, fr_den;
+ VAConfigAttrib rc_attr = { VAConfigAttribRateControl };
+ VAStatus vas;
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile, ctx->va_entrypoint,
+ &rc_attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query rate control "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
- if (avctx->bit_rate > INT32_MAX) {
- av_log(avctx, AV_LOG_ERROR, "Target bitrate of 2^31 bps or "
- "higher is not supported.\n");
+ if (rc_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not report any "
+ "supported rate control modes: assuming constant-
quality.\n");
How about logging it as warning message?
What would a user do about it?
User may know what is not supported in the driver and he/she might get wrong
result, he/she may file an issue to the driver.
If it's broken then the verbose logging provides the answer. If it isn't broken, as in
Post by Xiang, Haihao
Post by Mark Thompson
(Note that it gets logged for JPEG encoding on all versions of the i965 driver
except the most recent, so if it were a warning it would be seen by everyone
using those versions.)
then the warning is unhelpful.
Post by Xiang, Haihao
Post by Mark Thompson
Post by Xiang, Haihao
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ }
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using constant-quality mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support "
+ "constant-quality mode (%#x).\n", rc_attr.value);
+ return AVERROR(EINVAL);
+ }
+ }
+
+ if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any "
+ "bitrate-targetted rate control modes.\n");
return AVERROR(EINVAL);
}
if (avctx->rc_buffer_size)
hrd_buffer_size = avctx->rc_buffer_size;
+ else if (avctx->rc_max_rate > 0)
+ hrd_buffer_size = avctx->rc_max_rate;
else
hrd_buffer_size = avctx->bit_rate;
- if (avctx->rc_initial_buffer_occupancy)
+ if (avctx->rc_initial_buffer_occupancy) {
+ if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer settings: "
+ "must have initial buffer size (%d) < "
+ "buffer size (%"PRId64").\n",
+ avctx->rc_initial_buffer_occupancy, hrd_buffer_size);
+ return AVERROR(EINVAL);
+ }
hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy;
- else
+ } else {
hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4;
+ }
+
+ if (avctx->rc_max_rate && avctx->rc_max_rate < avctx->bit_rate) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid bitrate settings: must have "
+ "bitrate (%"PRId64") <= maxrate (%"PRId64").\n",
+ avctx->bit_rate, avctx->rc_max_rate);
+ return AVERROR(EINVAL);
+ }
+
+ if (avctx->rc_max_rate > avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_VBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support "
+ "VBR mode (%#x), using CBR mode instead.\n",
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_CBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_VBR;
+ }
+
+ rc_bits_per_second = avctx->rc_max_rate;
+ rc_target_percentage = (avctx->bit_rate * 100) / avctx-
rc_max_rate;
I think rc_target_percentage should be 100 for CBR case.
Yes; fixed.
The rc_bits_per_second value is also wrong in that case (shouldn't be
rc_max_rate if we can't use it), fixed similarly.
Post by Xiang, Haihao
+
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support "
+ "CBR mode (%#x), using VBR mode instead.\n",
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal bitrate.
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
Is it better to take it as CBR when avctx->rc_max_rate is 0 and CBR is supported
by driver?
I don't think so? VBR with the specified target is probably what you want in
most cases, and I think anyone with specific constraints that want constant
bitrate should expect to set maxrate to achieve that.
I agree VBR is probably what an user wants in most case, however target percent
set to 50% is not suitable for most case. To get a specific target percent, user
should set both target bitrate and max bitrate, so it is reasonable to ask user
must set both target bitrate and max bitrate for VBR cases, and for CBR user may
set target bitrate only.
I just saw it is also VBR in QSV when max bitrate is not set so I'm fine to keep
consistency with QSV for this case.
Max bitrate has to be set because of the API limitations (because the target bitrate is an integer percentage of max bitrate rather than a standalone value as found in pretty much every other API).

Here we'd like to say "no max bitrate constraint", but that isn't possible. I picked 50% because it's high enough to be mostly unconstrained, while not making numbers too much larger so that you might run into rounding or overflow issues. (E.g. picking 1% here might feel better somehow, but that overflows the 2^32 limit at only 40Mbps.)

If you prefer different settings for this case then can you explain what they would be?
Post by Xiang, Haihao
Post by Mark Thompson
Post by Xiang, Haihao
+
+ // We only have a target bitrate, but VAAPI requires that a
+ // maximum rate be supplied as well. Since the user has
+ // offered no particular constraint, arbitrarily pick a
+ // maximum rate of double the target rate.
+ rc_bits_per_second = 2 * avctx->bit_rate;
+ rc_target_percentage = 50;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- } else {
- rc_bits_per_second = avctx->rc_max_rate;
- rc_target_percentage = (avctx->bit_rate * 100) /
rc_bits_per_second;
}
- rc_window_size = (hrd_buffer_size * 1000) / avctx->bit_rate;
}
+ rc_window_size = (hrd_buffer_size * 1000) / rc_bits_per_second;
+
+ av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s, %d%% of %"PRId64" bps "
+ "over %d ms.\n", ctx->va_rc_mode == VA_RC_VBR ? "VBR" : "CBR",
+ rc_target_percentage, rc_bits_per_second, rc_window_size);
+ av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, "
+ "initial fullness %"PRId64" bits.\n",
+ hrd_buffer_size, hrd_initial_buffer_fullness);
+
+ if (rc_bits_per_second > UINT32_MAX ||
+ hrd_buffer_size > UINT32_MAX ||
+ hrd_initial_buffer_fullness > UINT32_MAX) {
+ av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or "
+ "greater are not supported by VAAPI.\n");
+ return AVERROR(EINVAL);
+ }
+
+ ctx->va_bit_rate = rc_bits_per_second;
+
+ ctx->config_attributes[ctx->nb_config_attributes++] =
+ (VAConfigAttrib) {
+ .type = VAConfigAttribRateControl,
+ .value = ctx->va_rc_mode,
+ };
+
ctx->rc_params.misc.type = VAEncMiscParameterTypeRateControl;
ctx->rc_params.rc = (VAEncMiscParameterRateControl) {
.bits_per_second = rc_bits_per_second,
@@ -1611,6 +1684,10 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
if (err < 0)
goto fail;
+ err = vaapi_encode_init_rate_control(avctx);
+ if (err < 0)
+ goto fail;
+
err = vaapi_encode_config_attributes(avctx);
if (err < 0)
goto fail;
@@ -1658,12 +1735,6 @@ av_cold int ff_vaapi_encode_init(AVCodecContext *avctx)
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)
...
Mark Thompson
2018-06-13 21:31:30 UTC
Permalink
Post by m***@gmail.com
Query which modes are supported and select between VBR and CBR based
on that - this removes all of the codec-specific rate control mode
selection code.
---
doc/encoders.texi | 2 -
libavcodec/vaapi_encode.c | 173 ++++++++++++++++++++++++++++------------
libavcodec/vaapi_encode.h | 6 +-
libavcodec/vaapi_encode_h264.c | 18 +----
libavcodec/vaapi_encode_h265.c | 14 +---
libavcodec/vaapi_encode_mjpeg.c | 3 +-
libavcodec/vaapi_encode_mpeg2.c | 9 +--
libavcodec/vaapi_encode_vp8.c | 13 +--
libavcodec/vaapi_encode_vp9.c | 13 +--
9 files changed, 137 insertions(+), 114 deletions(-)
diff --git a/doc/encoders.texi b/doc/encoders.texi
index 62a1509a96..0c0a307987 100644
...
@@ -1313,44 +1286,144 @@ static av_cold int vaapi_encode_config_attributes(AVCodecContext *avctx)
static av_cold int vaapi_encode_init_rate_control(AVCodecContext *avctx)
{
VAAPIEncodeContext *ctx = avctx->priv_data;
- int rc_bits_per_second;
- int rc_target_percentage;
- int rc_window_size;
- int hrd_buffer_size;
- int hrd_initial_buffer_fullness;
+ int64_t rc_bits_per_second;
+ int rc_target_percentage;
+ int rc_window_size;
+ int64_t hrd_buffer_size;
+ int64_t hrd_initial_buffer_fullness;
int fr_num, fr_den;
+ VAConfigAttrib rc_attr = { VAConfigAttribRateControl };
+ VAStatus vas;
+
+ vas = vaGetConfigAttributes(ctx->hwctx->display,
+ ctx->va_profile, ctx->va_entrypoint,
+ &rc_attr, 1);
+ if (vas != VA_STATUS_SUCCESS) {
+ av_log(avctx, AV_LOG_ERROR, "Failed to query rate control "
+ "config attribute: %d (%s).\n", vas, vaErrorStr(vas));
+ return AVERROR_EXTERNAL;
+ }
- if (avctx->bit_rate > INT32_MAX) {
- av_log(avctx, AV_LOG_ERROR, "Target bitrate of 2^31 bps or "
- "higher is not supported.\n");
+ if (rc_attr.value == VA_ATTRIB_NOT_SUPPORTED) {
+ av_log(avctx, AV_LOG_VERBOSE, "Driver does not report any "
+ "supported rate control modes: assuming constant-quality.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ }
+ if (avctx->flags & AV_CODEC_FLAG_QSCALE ||
+ avctx->bit_rate <= 0) {
+ if (rc_attr.value & VA_RC_CQP) {
+ av_log(avctx, AV_LOG_VERBOSE, "Using constant-quality mode.\n");
+ ctx->va_rc_mode = VA_RC_CQP;
+ return 0;
+ } else {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support "
+ "constant-quality mode (%#x).\n", rc_attr.value);
+ return AVERROR(EINVAL);
+ }
+ }
+
+ if (!(rc_attr.value & (VA_RC_CBR | VA_RC_VBR))) {
+ av_log(avctx, AV_LOG_ERROR, "Driver does not support any "
+ "bitrate-targetted rate control modes.\n");
return AVERROR(EINVAL);
}
if (avctx->rc_buffer_size)
hrd_buffer_size = avctx->rc_buffer_size;
+ else if (avctx->rc_max_rate > 0)
+ hrd_buffer_size = avctx->rc_max_rate;
else
hrd_buffer_size = avctx->bit_rate;
- if (avctx->rc_initial_buffer_occupancy)
+ if (avctx->rc_initial_buffer_occupancy) {
+ if (avctx->rc_initial_buffer_occupancy > hrd_buffer_size) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid RC buffer settings: "
+ "must have initial buffer size (%d) < "
+ "buffer size (%"PRId64").\n",
+ avctx->rc_initial_buffer_occupancy, hrd_buffer_size);
+ return AVERROR(EINVAL);
+ }
hrd_initial_buffer_fullness = avctx->rc_initial_buffer_occupancy;
- else
+ } else {
hrd_initial_buffer_fullness = hrd_buffer_size * 3 / 4;
+ }
+
+ if (avctx->rc_max_rate && avctx->rc_max_rate < avctx->bit_rate) {
+ av_log(avctx, AV_LOG_ERROR, "Invalid bitrate settings: must have "
+ "bitrate (%"PRId64") <= maxrate (%"PRId64").\n",
+ avctx->bit_rate, avctx->rc_max_rate);
+ return AVERROR(EINVAL);
+ }
+
+ if (avctx->rc_max_rate > avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_VBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support "
+ "VBR mode (%#x), using CBR mode instead.\n",
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_CBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_VBR;
+ }
+
+ rc_bits_per_second = avctx->rc_max_rate;
+ rc_target_percentage = (avctx->bit_rate * 100) / avctx->rc_max_rate;
+
+ } else if (avctx->rc_max_rate == avctx->bit_rate) {
+ if (!(rc_attr.value & VA_RC_CBR)) {
+ av_log(avctx, AV_LOG_WARNING, "Driver does not support "
+ "CBR mode (%#x), using VBR mode instead.\n",
+ rc_attr.value);
+ ctx->va_rc_mode = VA_RC_VBR;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+ }
- if (ctx->va_rc_mode == VA_RC_CBR) {
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- rc_window_size = 1000;
+
} else {
- if (avctx->rc_max_rate < avctx->bit_rate) {
- // Max rate is unset or invalid, just use the normal bitrate.
+ if (rc_attr.value & VA_RC_VBR) {
+ ctx->va_rc_mode = VA_RC_VBR;
+
+ // We only have a target bitrate, but VAAPI requires that a
+ // maximum rate be supplied as well. Since the user has
+ // offered no particular constraint, arbitrarily pick a
+ // maximum rate of double the target rate.
+ rc_bits_per_second = 2 * avctx->bit_rate;
+ rc_target_percentage = 50;
+ } else {
+ ctx->va_rc_mode = VA_RC_CBR;
+
rc_bits_per_second = avctx->bit_rate;
rc_target_percentage = 100;
- } else {
- rc_bits_per_second = avctx->rc_max_rate;
- rc_target_percentage = (avctx->bit_rate * 100) / rc_bits_per_second;
}
- rc_window_size = (hrd_buffer_size * 1000) / avctx->bit_rate;
}
+ rc_window_size = (hrd_buffer_size * 1000) / rc_bits_per_second;
+
+ av_log(avctx, AV_LOG_VERBOSE, "RC mode: %s, %d%% of %"PRId64" bps "
+ "over %d ms.\n", ctx->va_rc_mode == VA_RC_VBR ? "VBR" : "CBR",
+ rc_target_percentage, rc_bits_per_second, rc_window_size);
+ av_log(avctx, AV_LOG_VERBOSE, "RC buffer: %"PRId64" bits, "
+ "initial fullness %"PRId64" bits.\n",
+ hrd_buffer_size, hrd_initial_buffer_fullness);
+
+ if (rc_bits_per_second > UINT32_MAX ||
+ hrd_buffer_size > UINT32_MAX ||
+ hrd_initial_buffer_fullness > UINT32_MAX) {
+ av_log(avctx, AV_LOG_ERROR, "RC parameters of 2^32 or "
+ "greater are not supported by VAAPI.\n");
+ return AVERROR(EINVAL);
+ }
+
Use a assert0() maybe more pithily
I don't think it can be - it's end-user-triggerable, and there isn't any way for an API user to detect that. (Consider "-b:v 1T".)

Thanks,

- Mark
Loading...