From 19388a7200e5d99c703271f05dba1c806720e808 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 30 Apr 2017 19:27:54 +0100 Subject: [PATCH 01/15] vaapi_encode: Move quality option to common code Use AVCodecContext.compression_level rather than a private option, replacing the H.264-specific quality option (which stays only for compatibility). This now works with the H.265 encoder in the i965 driver, as well as the existing cases with the H.264 encoder. --- doc/encoders.texi | 9 ++++++--- libavcodec/vaapi_encode.c | 35 ++++++++++++++++++++++++++++++++++ libavcodec/vaapi_encode.h | 6 ++++++ libavcodec/vaapi_encode_h264.c | 25 ++---------------------- 4 files changed, 49 insertions(+), 26 deletions(-) diff --git a/doc/encoders.texi b/doc/encoders.texi index 7cebe39c18..c369e03bfd 100644 --- a/doc/encoders.texi +++ b/doc/encoders.texi @@ -948,7 +948,13 @@ The following standard libavcodec options are used: @item @option{rc_init_occupancy} / @option{rc_initial_buffer_occupancy} @item +@option{compression_level} + +Speed / quality tradeoff: higher values are faster / worse quality. +@item @option{q} / @option{global_quality} + +Size / quality tradeoff: higher values are smaller / worse quality. @item @option{qmin} (only: @option{qmax} is not supported) @@ -969,9 +975,6 @@ The following standard libavcodec options are used: @option{level} sets the value of @emph{level_idc}. @table @option -@item quality -Set the local encoding quality/speed tradeoff (range 1-8, higher values are faster; not all -systems implement all levels). @item low_power Use low-power encoding mode. @end table diff --git a/libavcodec/vaapi_encode.c b/libavcodec/vaapi_encode.c index 6205184190..462ec7a8e7 100644 --- a/libavcodec/vaapi_encode.c +++ b/libavcodec/vaapi_encode.c @@ -1423,6 +1423,41 @@ 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; + + ctx->global_params[ctx->nb_global_params] = + &ctx->quality_params.misc; + ctx->global_params_size[ctx->nb_global_params++] = + 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.h b/libavcodec/vaapi_encode.h index fc62365148..1b0fed80e4 100644 --- a/libavcodec/vaapi_encode.h +++ b/libavcodec/vaapi_encode.h @@ -159,6 +159,12 @@ typedef struct VAAPIEncodeContext { VAEncMiscParameterBuffer misc; VAEncMiscParameterFrameRate fr; } fr_params; +#if VA_CHECK_VERSION(0, 36, 0) + struct { + VAEncMiscParameterBuffer misc; + VAEncMiscParameterBufferQualityLevel quality; + } quality_params; +#endif // Per-sequence parameter structure (VAEncSequenceParameterBuffer*). void *codec_sequence_params; diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c index 7583a20c14..e08cf61167 100644 --- a/libavcodec/vaapi_encode_h264.c +++ b/libavcodec/vaapi_encode_h264.c @@ -154,14 +154,6 @@ typedef struct VAAPIEncodeH264Context { // Rate control configuration. int send_timing_sei; - -#if VA_CHECK_VERSION(0, 36, 0) - // Speed-quality tradeoff setting. - struct { - VAEncMiscParameterBuffer misc; - VAEncMiscParameterBufferQualityLevel quality; - } quality_params; -#endif } VAAPIEncodeH264Context; typedef struct VAAPIEncodeH264Options { @@ -1141,21 +1133,8 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx) av_assert0(0 && "Invalid RC mode."); } - if (opt->quality > 0) { -#if VA_CHECK_VERSION(0, 36, 0) - priv->quality_params.misc.type = - VAEncMiscParameterTypeQualityLevel; - priv->quality_params.quality.quality_level = opt->quality; - - ctx->global_params[ctx->nb_global_params] = - &priv->quality_params.misc; - ctx->global_params_size[ctx->nb_global_params++] = - sizeof(priv->quality_params); -#else - av_log(avctx, AV_LOG_WARNING, "The encode quality option is not " - "supported with this VAAPI version.\n"); -#endif - } + if (avctx->compression_level == FF_COMPRESSION_DEFAULT) + avctx->compression_level = opt->quality; return 0; } From ebf3b9e8a875eb12312460aee505118791ef805f Mon Sep 17 00:00:00 2001 From: Vittorio Giovara Date: Tue, 8 Aug 2017 16:06:29 +0200 Subject: [PATCH 02/15] h264: Add support for alternative transfer characterics SEI The use of this SEI is for backward compatibility in HLG HDR systems: older devices that cannot interpret the "arib-std-b67" transfer will get the compatible transfer (usually bt709 or bt2020) from the VUI, while newer devices that can interpret HDR will read the SEI and use its value instead. Signed-off-by: Vittorio Giovara --- libavcodec/h264_sei.c | 11 +++++++++++ libavcodec/h264_sei.h | 7 +++++++ libavcodec/h264_slice.c | 6 ++++++ 3 files changed, 24 insertions(+) diff --git a/libavcodec/h264_sei.c b/libavcodec/h264_sei.c index 3ca2b7a6cd..03fca9017f 100644 --- a/libavcodec/h264_sei.c +++ b/libavcodec/h264_sei.c @@ -346,6 +346,14 @@ static int decode_display_orientation(H264SEIDisplayOrientation *h, return 0; } +static int decode_alternative_transfer(H264SEIAlternativeTransfer *h, + GetBitContext *gb) +{ + h->present = 1; + h->preferred_transfer_characteristics = get_bits(gb, 8); + return 0; +} + int ff_h264_sei_decode(H264SEIContext *h, GetBitContext *gb, const H264ParamSets *ps, void *logctx) { @@ -396,6 +404,9 @@ int ff_h264_sei_decode(H264SEIContext *h, GetBitContext *gb, case H264_SEI_TYPE_DISPLAY_ORIENTATION: ret = decode_display_orientation(&h->display_orientation, gb); break; + case H264_SEI_TYPE_ALTERNATIVE_TRANSFER: + ret = decode_alternative_transfer(&h->alternative_transfer, gb); + break; default: av_log(logctx, AV_LOG_DEBUG, "unknown SEI type %d\n", type); skip_bits(gb, 8 * size); diff --git a/libavcodec/h264_sei.h b/libavcodec/h264_sei.h index ce9768ec3d..f6ac6034da 100644 --- a/libavcodec/h264_sei.h +++ b/libavcodec/h264_sei.h @@ -33,6 +33,7 @@ typedef enum { H264_SEI_TYPE_RECOVERY_POINT = 6, ///< recovery point (frame # to decoder sync) H264_SEI_TYPE_FRAME_PACKING = 45, ///< frame packing arrangement H264_SEI_TYPE_DISPLAY_ORIENTATION = 47, ///< display orientation + H264_SEI_TYPE_ALTERNATIVE_TRANSFER = 147, ///< alternative transfer } H264_SEI_Type; /** @@ -115,6 +116,11 @@ typedef struct H264SEIDisplayOrientation { int hflip, vflip; } H264SEIDisplayOrientation; +typedef struct H264SEIAlternativeTransfer { + int present; + int preferred_transfer_characteristics; +} H264SEIAlternativeTransfer; + typedef struct H264SEIContext { H264SEIPictureTiming picture_timing; H264SEIAFD afd; @@ -124,6 +130,7 @@ typedef struct H264SEIContext { H264SEIBufferingPeriod buffering_period; H264SEIFramePacking frame_packing; H264SEIDisplayOrientation display_orientation; + H264SEIAlternativeTransfer alternative_transfer; } H264SEIContext; struct H264ParamSets; diff --git a/libavcodec/h264_slice.c b/libavcodec/h264_slice.c index c6309b298c..5dd01d836e 100644 --- a/libavcodec/h264_slice.c +++ b/libavcodec/h264_slice.c @@ -1154,6 +1154,12 @@ static int h264_export_frame_props(H264Context *h) a53->a53_caption_size = 0; } + if (h->sei.alternative_transfer.present && + av_color_transfer_name(h->sei.alternative_transfer.preferred_transfer_characteristics) && + h->sei.alternative_transfer.preferred_transfer_characteristics != AVCOL_TRC_UNSPECIFIED) { + h->avctx->color_trc = cur->f->color_trc = h->sei.alternative_transfer.preferred_transfer_characteristics; + } + return 0; } From 538e50875105c9d4a04bc4ed4a217e87f422137e Mon Sep 17 00:00:00 2001 From: Vittorio Giovara Date: Tue, 8 Aug 2017 16:30:32 +0200 Subject: [PATCH 03/15] pixfmt: Support chroma-derived and ictcp color matrices Signed-off-by: Vittorio Giovara --- libavutil/pixdesc.c | 3 +++ libavutil/pixfmt.h | 3 +++ libavutil/version.h | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/libavutil/pixdesc.c b/libavutil/pixdesc.c index 7fa6dd7c0b..b168ac7d0b 100644 --- a/libavutil/pixdesc.c +++ b/libavutil/pixdesc.c @@ -1806,6 +1806,9 @@ static const char * const color_space_names[] = { [AVCOL_SPC_BT2020_NCL] = "bt2020nc", [AVCOL_SPC_BT2020_CL] = "bt2020c", [AVCOL_SPC_SMPTE2085] = "smpte2085", + [AVCOL_SPC_CHROMA_DERIVED_NCL] = "chroma-derived-nc", + [AVCOL_SPC_CHROMA_DERIVED_CL] = "chroma-derived-c", + [AVCOL_SPC_ICTCP] = "ictcp", }; static const char * const chroma_location_names[] = { diff --git a/libavutil/pixfmt.h b/libavutil/pixfmt.h index 2ba7ad1c88..bcbb378378 100644 --- a/libavutil/pixfmt.h +++ b/libavutil/pixfmt.h @@ -384,6 +384,9 @@ enum AVColorSpace { AVCOL_SPC_BT2020_NCL = 9, ///< ITU-R BT2020 non-constant luminance system AVCOL_SPC_BT2020_CL = 10, ///< ITU-R BT2020 constant luminance system AVCOL_SPC_SMPTE2085 = 11, ///< SMPTE 2085, Y'D'zD'x + AVCOL_SPC_CHROMA_DERIVED_NCL = 12, ///< Chromaticity-derived non-constant luminance system + AVCOL_SPC_CHROMA_DERIVED_CL = 13, ///< Chromaticity-derived constant luminance system + AVCOL_SPC_ICTCP = 14, ///< ITU-R BT.2100-0, ICtCp AVCOL_SPC_NB, ///< Not part of ABI }; diff --git a/libavutil/version.h b/libavutil/version.h index 62fb38a27a..5d0bb61124 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -54,7 +54,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 56 -#define LIBAVUTIL_VERSION_MINOR 4 +#define LIBAVUTIL_VERSION_MINOR 5 #define LIBAVUTIL_VERSION_MICRO 0 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ From 18f1706f331bf5dd565774eae680508c8d3a97ad Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Thu, 4 May 2017 23:01:51 +0100 Subject: [PATCH 04/15] lavc: Add coded bitstream read/write API --- configure | 1 + libavcodec/Makefile | 1 + libavcodec/cbs.c | 460 ++++++++++++++++++++++++++++++++++++++ libavcodec/cbs.h | 274 +++++++++++++++++++++++ libavcodec/cbs_internal.h | 83 +++++++ 5 files changed, 819 insertions(+) create mode 100644 libavcodec/cbs.c create mode 100644 libavcodec/cbs.h create mode 100644 libavcodec/cbs_internal.h diff --git a/configure b/configure index 4510100f38..befaed1e58 100755 --- a/configure +++ b/configure @@ -1739,6 +1739,7 @@ CONFIG_EXTRA=" blockdsp bswapdsp cabac + cbs dirac_parse dvprofile faandct diff --git a/libavcodec/Makefile b/libavcodec/Makefile index bb568ddbe4..41da3ca7ba 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -53,6 +53,7 @@ OBJS-$(CONFIG_AUDIODSP) += audiodsp.o OBJS-$(CONFIG_BLOCKDSP) += blockdsp.o OBJS-$(CONFIG_BSWAPDSP) += bswapdsp.o OBJS-$(CONFIG_CABAC) += cabac.o +OBJS-$(CONFIG_CBS) += cbs.o OBJS-$(CONFIG_DCT) += dct.o dct32_fixed.o dct32_float.o OBJS-$(CONFIG_ERROR_RESILIENCE) += error_resilience.o OBJS-$(CONFIG_FAANDCT) += faandct.o diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c new file mode 100644 index 0000000000..3a205c393d --- /dev/null +++ b/libavcodec/cbs.c @@ -0,0 +1,460 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "config.h" + +#include "libavutil/avassert.h" +#include "libavutil/common.h" + +#include "cbs.h" +#include "cbs_internal.h" + + +static const CodedBitstreamType *cbs_type_table[] = { +}; + +int ff_cbs_init(CodedBitstreamContext *ctx, + enum AVCodecID codec_id, void *log_ctx) +{ + const CodedBitstreamType *type; + int i; + + type = NULL; + for (i = 0; i < FF_ARRAY_ELEMS(cbs_type_table); i++) { + if (cbs_type_table[i]->codec_id == codec_id) { + type = cbs_type_table[i]; + break; + } + } + if (!type) + return AVERROR(EINVAL); + + ctx->log_ctx = log_ctx; + ctx->codec = type; + + ctx->priv_data = av_mallocz(ctx->codec->priv_data_size); + if (!ctx->priv_data) + return AVERROR(ENOMEM); + + ctx->decompose_unit_types = NULL; + + ctx->trace_enable = 0; + ctx->trace_level = AV_LOG_TRACE; + + return 0; +} + +void ff_cbs_close(CodedBitstreamContext *ctx) +{ + if (ctx->codec && ctx->codec->close) + ctx->codec->close(ctx); + + av_freep(&ctx->priv_data); +} + +static void cbs_unit_uninit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) +{ + if (ctx->codec->free_unit && unit->content && !unit->content_external) + ctx->codec->free_unit(unit); + + av_freep(&unit->data); + unit->data_size = 0; + unit->data_bit_padding = 0; +} + +void ff_cbs_fragment_uninit(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) +{ + int i; + + for (i = 0; i < frag->nb_units; i++) + cbs_unit_uninit(ctx, &frag->units[i]); + av_freep(&frag->units); + frag->nb_units = 0; + + av_freep(&frag->data); + frag->data_size = 0; + frag->data_bit_padding = 0; +} + +static int cbs_read_fragment_content(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) +{ + int err, i, j; + + for (i = 0; i < frag->nb_units; i++) { + if (ctx->decompose_unit_types) { + for (j = 0; j < ctx->nb_decompose_unit_types; j++) { + if (ctx->decompose_unit_types[j] == frag->units[i].type) + break; + } + if (j >= ctx->nb_decompose_unit_types) + continue; + } + + err = ctx->codec->read_unit(ctx, &frag->units[i]); + if (err == AVERROR(ENOSYS)) { + av_log(ctx->log_ctx, AV_LOG_WARNING, + "Decomposition unimplemented for unit %d " + "(type %d).\n", i, frag->units[i].type); + } else if (err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to read unit %d " + "(type %d).\n", i, frag->units[i].type); + return err; + } + } + + return 0; +} + +int ff_cbs_read_extradata(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const AVCodecParameters *par) +{ + int err; + + memset(frag, 0, sizeof(*frag)); + + frag->data = par->extradata; + frag->data_size = par->extradata_size; + + err = ctx->codec->split_fragment(ctx, frag, 1); + if (err < 0) + return err; + + frag->data = NULL; + frag->data_size = 0; + + return cbs_read_fragment_content(ctx, frag); +} + +int ff_cbs_read_packet(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const AVPacket *pkt) +{ + int err; + + memset(frag, 0, sizeof(*frag)); + + frag->data = pkt->data; + frag->data_size = pkt->size; + + err = ctx->codec->split_fragment(ctx, frag, 0); + if (err < 0) + return err; + + frag->data = NULL; + frag->data_size = 0; + + return cbs_read_fragment_content(ctx, frag); +} + +int ff_cbs_read(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const uint8_t *data, size_t size) +{ + int err; + + memset(frag, 0, sizeof(*frag)); + + // (We won't write to this during split.) + frag->data = (uint8_t*)data; + frag->data_size = size; + + err = ctx->codec->split_fragment(ctx, frag, 0); + if (err < 0) + return err; + + frag->data = NULL; + frag->data_size = 0; + + return cbs_read_fragment_content(ctx, frag); +} + + +int ff_cbs_write_fragment_data(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) +{ + int err, i; + + for (i = 0; i < frag->nb_units; i++) { + if (!frag->units[i].content) + continue; + + err = ctx->codec->write_unit(ctx, &frag->units[i]); + if (err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to write unit %d " + "(type %d).\n", i, frag->units[i].type); + return err; + } + } + + err = ctx->codec->assemble_fragment(ctx, frag); + if (err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to assemble fragment.\n"); + return err; + } + + return 0; +} + +int ff_cbs_write_extradata(CodedBitstreamContext *ctx, + AVCodecParameters *par, + CodedBitstreamFragment *frag) +{ + int err; + + err = ff_cbs_write_fragment_data(ctx, frag); + if (err < 0) + return err; + + av_freep(&par->extradata); + + par->extradata = av_malloc(frag->data_size + + AV_INPUT_BUFFER_PADDING_SIZE); + if (!par->extradata) + return AVERROR(ENOMEM); + + memcpy(par->extradata, frag->data, frag->data_size); + memset(par->extradata + frag->data_size, 0, + AV_INPUT_BUFFER_PADDING_SIZE); + par->extradata_size = frag->data_size; + + return 0; +} + +int ff_cbs_write_packet(CodedBitstreamContext *ctx, + AVPacket *pkt, + CodedBitstreamFragment *frag) +{ + int err; + + err = ff_cbs_write_fragment_data(ctx, frag); + if (err < 0) + return err; + + av_new_packet(pkt, frag->data_size); + if (err < 0) + return err; + + memcpy(pkt->data, frag->data, frag->data_size); + pkt->size = frag->data_size; + + return 0; +} + + +void ff_cbs_trace_header(CodedBitstreamContext *ctx, + const char *name) +{ + if (!ctx->trace_enable) + return; + + av_log(ctx->log_ctx, ctx->trace_level, "%s\n", name); +} + +void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, int position, + const char *name, const char *bits, + int64_t value) +{ + size_t name_len, bits_len; + int pad; + + if (!ctx->trace_enable) + return; + + av_assert0(value >= INT_MIN && value <= UINT32_MAX); + + name_len = strlen(name); + bits_len = strlen(bits); + + if (name_len + bits_len > 60) + pad = bits_len + 2; + else + pad = 61 - name_len; + + av_log(ctx->log_ctx, ctx->trace_level, "%-10d %s%*s = %"PRId64"\n", + position, name, pad, bits, value); +} + +int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, BitstreamContext *bc, + int width, const char *name, uint32_t *write_to, + uint32_t range_min, uint32_t range_max) +{ + uint32_t value; + int position; + + av_assert0(width <= 32); + + if (ctx->trace_enable) + position = bitstream_tell(bc); + + value = bitstream_read(bc, width); + + if (ctx->trace_enable) { + char bits[33]; + int i; + for (i = 0; i < width; i++) + bits[i] = value >> (width - i - 1) & 1 ? '1' : '0'; + bits[i] = 0; + + ff_cbs_trace_syntax_element(ctx, position, name, bits, value); + } + + if (value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%"PRIu32", but must be in [%"PRIu32",%"PRIu32"].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + *write_to = value; + return 0; +} + +int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, + int width, const char *name, uint32_t value, + uint32_t range_min, uint32_t range_max) +{ + av_assert0(width <= 32); + + if (value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%"PRIu32", but must be in [%"PRIu32",%"PRIu32"].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + if (put_bits_left(pbc) < width) + return AVERROR(ENOSPC); + + if (ctx->trace_enable) { + char bits[33]; + int i; + for (i = 0; i < width; i++) + bits[i] = value >> (width - i - 1) & 1 ? '1' : '0'; + bits[i] = 0; + + ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), name, bits, value); + } + + if (width < 32) + put_bits(pbc, width, value); + else + put_bits32(pbc, value); + + return 0; +} + + +static int cbs_insert_unit(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int position) +{ + CodedBitstreamUnit *units; + + units = av_malloc_array(frag->nb_units + 1, sizeof(*units)); + if (!units) + return AVERROR(ENOMEM); + + if (position > 0) + memcpy(units, frag->units, position * sizeof(*units)); + if (position < frag->nb_units) + memcpy(units + position + 1, frag->units + position, + (frag->nb_units - position) * sizeof(*units)); + + memset(units + position, 0, sizeof(*units)); + + av_freep(&frag->units); + frag->units = units; + ++frag->nb_units; + + return 0; +} + +int ff_cbs_insert_unit_content(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int position, uint32_t type, + void *content) +{ + int err; + + if (position == -1) + position = frag->nb_units; + av_assert0(position >= 0 && position <= frag->nb_units); + + err = cbs_insert_unit(ctx, frag, position); + if (err < 0) + return err; + + frag->units[position].type = type; + frag->units[position].content = content; + frag->units[position].content_external = 1; + + return 0; +} + +int ff_cbs_insert_unit_data(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int position, uint32_t type, + uint8_t *data, size_t data_size) +{ + int err; + + if (position == -1) + position = frag->nb_units; + av_assert0(position >= 0 && position <= frag->nb_units); + + err = cbs_insert_unit(ctx, frag, position); + if (err < 0) + return err; + + frag->units[position].type = type; + frag->units[position].data = data; + frag->units[position].data_size = data_size; + + return 0; +} + +int ff_cbs_delete_unit(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int position) +{ + if (position < 0 || position >= frag->nb_units) + return AVERROR(EINVAL); + + cbs_unit_uninit(ctx, &frag->units[position]); + + --frag->nb_units; + + if (frag->nb_units == 0) { + av_freep(&frag->units); + + } else { + memmove(frag->units + position, + frag->units + position + 1, + (frag->nb_units - position) * sizeof(*frag->units)); + + // Don't bother reallocating the unit array. + } + + return 0; +} diff --git a/libavcodec/cbs.h b/libavcodec/cbs.h new file mode 100644 index 0000000000..01b2239b7b --- /dev/null +++ b/libavcodec/cbs.h @@ -0,0 +1,274 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_H +#define AVCODEC_CBS_H + +#include +#include + +#include "avcodec.h" + + +struct CodedBitstreamType; + +/** + * Coded bitstream unit structure. + * + * A bitstream unit the the smallest element of a bitstream which + * is meaningful on its own. For example, an H.264 NAL unit. + * + * See the codec-specific header for the meaning of this for any + * particular codec. + */ +typedef struct CodedBitstreamUnit { + /** + * Codec-specific type of this unit. + */ + uint32_t type; + + /** + * Pointer to the bitstream form of this unit. + * + * May be NULL if the unit currently only exists in decomposed form. + */ + uint8_t *data; + /** + * The number of bytes in the bitstream (including any padding bits + * in the final byte). + */ + size_t data_size; + /** + * The number of bits which should be ignored in the final byte. + * + * This supports non-byte-aligned bitstreams. + */ + size_t data_bit_padding; + + /** + * Pointer to the decomposed form of this unit. + * + * The type of this structure depends on both the codec and the + * type of this unit. May be NULL if the unit only exists in + * bitstream form. + */ + void *content; + /** + * Whether the content was supplied externally. + * + * If so, it should not be freed when freeing the unit. + */ + int content_external; +} CodedBitstreamUnit; + +/** + * Coded bitstream fragment structure, combining one or more units. + * + * This is any sequence of units. It need not form some greater whole, + * though in many cases it will. For example, an H.264 access unit, + * which is composed of a sequence of H.264 NAL units. + */ +typedef struct CodedBitstreamFragment { + /** + * Pointer to the bitstream form of this fragment. + * + * May be NULL if the fragment only exists as component units. + */ + uint8_t *data; + /** + * The number of bytes in the bitstream. + * + * The number of bytes in the bitstream (including any padding bits + * in the final byte). + */ + size_t data_size; + /** + * The number of bits which should be ignored in the final byte. + */ + size_t data_bit_padding; + + /** + * Number of units in this fragment. + * + * This may be zero if the fragment only exists in bistream form + * and has not been decomposed. + */ + int nb_units; + /** + * Pointer to an array of units of length nb_units. + * + * Must be NULL if nb_units is zero. + */ + CodedBitstreamUnit *units; +} CodedBitstreamFragment; + +/** + * Context structure for coded bitstream operations. + */ +typedef struct CodedBitstreamContext { + /** + * Logging context to be passed to all av_log() calls associated + * with this context. + */ + void *log_ctx; + + /** + * Internal codec-specific hooks. + */ + const struct CodedBitstreamType *codec; + + /** + * Internal codec-specific data. + * + * This contains any information needed when reading/writing + * bitsteams which will not necessarily be present in a fragment. + * For example, for H.264 it contains all currently visible + * parameter sets - they are required to determine the bitstream + * syntax but need not be present in every access unit. + */ + void *priv_data; + + /** + * Array of unit types which should be decomposed when reading. + * + * Types not in this list will be available in bitstream form only. + * If NULL, all supported types will be decomposed. + */ + uint32_t *decompose_unit_types; + /** + * Length of the decompose_unit_types array. + */ + int nb_decompose_unit_types; + + /** + * Enable trace output during read/write operations. + */ + int trace_enable; + /** + * Log level to use for trace output. + * + * From AV_LOG_*; defaults to AV_LOG_TRACE. + */ + int trace_level; +} CodedBitstreamContext; + + +/** + * Initialise a new context for the given codec. + */ +int ff_cbs_init(CodedBitstreamContext *ctx, + enum AVCodecID codec_id, void *log_ctx); + +/** + * Close a context and free all internal state. + */ +void ff_cbs_close(CodedBitstreamContext *ctx); + + +/** + * Read the extradata bitstream found in codec parameters into a + * fragment, then split into units and decompose. + * + * This also updates the internal state, so will need to be called for + * codecs with extradata to read parameter sets necessary for further + * parsing even if the fragment itself is not desired. + */ +int ff_cbs_read_extradata(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const AVCodecParameters *par); + +/** + * Read the data bitstream from a packet into a fragment, then + * split into units and decompose. + */ +int ff_cbs_read_packet(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const AVPacket *pkt); + +/** + * Read a bitstream from a memory region into a fragment, then + * split into units and decompose. + */ +int ff_cbs_read(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const uint8_t *data, size_t size); + + +/** + * Write the content of the fragment to its own internal buffer. + * + * Writes the content of all units and then assembles them into a new + * data buffer. When modifying the content of decomposed units, this + * can be used to regenerate the bitstream form of units or the whole + * fragment so that it can be extracted for other use. + */ +int ff_cbs_write_fragment_data(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag); + +/** + * Write the bitstream of a fragment to the extradata in codec parameters. + */ +int ff_cbs_write_extradata(CodedBitstreamContext *ctx, + AVCodecParameters *par, + CodedBitstreamFragment *frag); + +/** + * Write the bitstream of a fragment to a packet. + */ +int ff_cbs_write_packet(CodedBitstreamContext *ctx, + AVPacket *pkt, + CodedBitstreamFragment *frag); + + +/** + * Free all allocated memory in a fragment. + */ +void ff_cbs_fragment_uninit(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag); + + +/** + * Insert a new unit into a fragment with the given content. + * + * The content structure continues to be owned by the caller, and + * will not be freed when the unit is. + */ +int ff_cbs_insert_unit_content(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int position, uint32_t type, + void *content); + +/** + * Insert a new unit into a fragment with the given data bitstream. + * + * The data buffer will be owned by the unit after this operation. + */ +int ff_cbs_insert_unit_data(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int position, uint32_t type, + uint8_t *data, size_t data_size); + +/** + * Delete a unit from a fragment and free all memory it uses. + */ +int ff_cbs_delete_unit(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int position); + + +#endif /* AVCODEC_CBS_H */ diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h new file mode 100644 index 0000000000..a691790b70 --- /dev/null +++ b/libavcodec/cbs_internal.h @@ -0,0 +1,83 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_INTERNAL_H +#define AVCODEC_CBS_INTERNAL_H + +#include "avcodec.h" +#include "bitstream.h" +#include "cbs.h" +#include "put_bits.h" + + +typedef struct CodedBitstreamType { + enum AVCodecID codec_id; + + size_t priv_data_size; + + // Split frag->data into coded bitstream units, creating the + // frag->units array. Fill data but not content on each unit. + int (*split_fragment)(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int header); + + // Read the unit->data bitstream and decompose it, creating + // unit->content. + int (*read_unit)(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit); + + // Write the unit->data bitstream from unit->content. + int (*write_unit)(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit); + + // Read the data from all of frag->units and assemble it into + // a bitstream for the whole fragment. + int (*assemble_fragment)(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag); + + // Free the content and data of a single unit. + void (*free_unit)(CodedBitstreamUnit *unit); + + // Free the codec internal state. + void (*close)(CodedBitstreamContext *ctx); +} CodedBitstreamType; + + +// Helper functions for trace output. + +void ff_cbs_trace_header(CodedBitstreamContext *ctx, + const char *name); + +void ff_cbs_trace_syntax_element(CodedBitstreamContext *ctx, + int position, const char *name, + const char *bitstring, int64_t value); + + +// Helper functions for read/write of common bitstream elements, including +// generation of trace output. + +int ff_cbs_read_unsigned(CodedBitstreamContext *ctx, BitstreamContext *bc, + int width, const char *name, uint32_t *write_to, + uint32_t range_min, uint32_t range_max); + +int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, + int width, const char *name, uint32_t value, + uint32_t range_min, uint32_t range_max); + + +#endif /* AVCODEC_CBS_INTERNAL_H */ From acf06f45441be24c5cbae0920579cd69427326a1 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 14 May 2017 16:18:25 +0100 Subject: [PATCH 05/15] lavc: Add coded bitstream read/write support for H.264 --- configure | 2 + libavcodec/Makefile | 1 + libavcodec/cbs.c | 3 + libavcodec/cbs_h264.h | 427 +++++++++ libavcodec/cbs_h2645.c | 997 ++++++++++++++++++++ libavcodec/cbs_h2645.h | 43 + libavcodec/cbs_h264_syntax_template.c | 1230 +++++++++++++++++++++++++ libavcodec/cbs_internal.h | 3 + 8 files changed, 2706 insertions(+) create mode 100644 libavcodec/cbs_h264.h create mode 100644 libavcodec/cbs_h2645.c create mode 100644 libavcodec/cbs_h2645.h create mode 100644 libavcodec/cbs_h264_syntax_template.c diff --git a/configure b/configure index befaed1e58..54af2e0cb5 100755 --- a/configure +++ b/configure @@ -1740,6 +1740,7 @@ CONFIG_EXTRA=" bswapdsp cabac cbs + cbs_h264 dirac_parse dvprofile faandct @@ -1966,6 +1967,7 @@ w32threads_deps="atomics_native" threads_if_any="$THREADS_LIST" # subsystems +cbs_h264_select="cbs golomb" dct_select="rdft" dirac_parse_select="golomb" error_resilience_select="me_cmp" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 41da3ca7ba..30fc388f8c 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -54,6 +54,7 @@ OBJS-$(CONFIG_BLOCKDSP) += blockdsp.o OBJS-$(CONFIG_BSWAPDSP) += bswapdsp.o OBJS-$(CONFIG_CABAC) += cabac.o OBJS-$(CONFIG_CBS) += cbs.o +OBJS-$(CONFIG_CBS_H264) += cbs_h2645.o h2645_parse.o OBJS-$(CONFIG_DCT) += dct.o dct32_fixed.o dct32_float.o OBJS-$(CONFIG_ERROR_RESILIENCE) += error_resilience.o OBJS-$(CONFIG_FAANDCT) += faandct.o diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c index 3a205c393d..9352b535b2 100644 --- a/libavcodec/cbs.c +++ b/libavcodec/cbs.c @@ -28,6 +28,9 @@ static const CodedBitstreamType *cbs_type_table[] = { +#if CONFIG_CBS_H264 + &ff_cbs_type_h264, +#endif }; int ff_cbs_init(CodedBitstreamContext *ctx, diff --git a/libavcodec/cbs_h264.h b/libavcodec/cbs_h264.h new file mode 100644 index 0000000000..b58f19f17e --- /dev/null +++ b/libavcodec/cbs_h264.h @@ -0,0 +1,427 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_H264_H +#define AVCODEC_CBS_H264_H + +#include +#include + +#include "cbs_h2645.h" +#include "h264.h" + + +enum { + // This limit is arbitrary - it is sufficient for one message of each + // type plus some repeats, and will therefore easily cover all sane + // streams. However, it is possible to make technically-valid streams + // for which it will fail (for example, by including a large number of + // user-data-unregistered messages). + H264_MAX_SEI_PAYLOADS = 64, +}; + + +typedef struct H264RawNALUnitHeader { + uint8_t forbidden_zero_bit; + uint8_t nal_ref_idc; + uint8_t nal_unit_type; + + uint8_t svc_extension_flag; + uint8_t avc_3d_extension_flag; +} H264RawNALUnitHeader; + +typedef struct H264RawScalingList { + int8_t delta_scale[64]; +} H264RawScalingList; + +typedef struct H264RawHRD { + uint8_t cpb_cnt_minus1; + uint8_t bit_rate_scale; + uint8_t cpb_size_scale; + + uint32_t bit_rate_value_minus1[H264_MAX_CPB_CNT]; + uint32_t cpb_size_value_minus1[H264_MAX_CPB_CNT]; + uint8_t cbr_flag[H264_MAX_CPB_CNT]; + + uint8_t initial_cpb_removal_delay_length_minus1; + uint8_t cpb_removal_delay_length_minus1; + uint8_t dpb_output_delay_length_minus1; + uint8_t time_offset_length; +} H264RawHRD; + +typedef struct H264RawVUI { + uint8_t aspect_ratio_info_present_flag; + uint8_t aspect_ratio_idc; + uint16_t sar_width; + uint16_t sar_height; + + uint8_t overscan_info_present_flag; + uint8_t overscan_appropriate_flag; + + uint8_t video_signal_type_present_flag; + uint8_t video_format; + uint8_t video_full_range_flag; + uint8_t colour_description_present_flag; + uint8_t colour_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coefficients; + + uint8_t chroma_loc_info_present_flag; + uint8_t chroma_sample_loc_type_top_field; + uint8_t chroma_sample_loc_type_bottom_field; + + uint8_t timing_info_present_flag; + uint32_t num_units_in_tick; + uint32_t time_scale; + uint8_t fixed_frame_rate_flag; + + uint8_t nal_hrd_parameters_present_flag; + H264RawHRD nal_hrd_parameters; + uint8_t vcl_hrd_parameters_present_flag; + H264RawHRD vcl_hrd_parameters; + uint8_t low_delay_hrd_flag; + + uint8_t pic_struct_present_flag; + + uint8_t bitstream_restriction_flag; + uint8_t motion_vectors_over_pic_boundaries_flag; + uint8_t max_bytes_per_pic_denom; + uint8_t max_bits_per_mb_denom; + uint8_t log2_max_mv_length_horizontal; + uint8_t log2_max_mv_length_vertical; + uint8_t max_num_reorder_frames; + uint8_t max_dec_frame_buffering; +} H264RawVUI; + +typedef struct H264RawSPS { + H264RawNALUnitHeader nal_unit_header; + + uint8_t profile_idc; + uint8_t constraint_set0_flag; + uint8_t constraint_set1_flag; + uint8_t constraint_set2_flag; + uint8_t constraint_set3_flag; + uint8_t constraint_set4_flag; + uint8_t constraint_set5_flag; + uint8_t reserved_zero_2bits; + uint8_t level_idc; + + uint8_t seq_parameter_set_id; + + uint8_t chroma_format_idc; + uint8_t separate_colour_plane_flag; + uint8_t bit_depth_luma_minus8; + uint8_t bit_depth_chroma_minus8; + uint8_t qpprime_y_zero_transform_bypass_flag; + + uint8_t seq_scaling_matrix_present_flag; + uint8_t seq_scaling_list_present_flag[12]; + H264RawScalingList scaling_list_4x4[6]; + H264RawScalingList scaling_list_8x8[6]; + + uint8_t log2_max_frame_num_minus4; + uint8_t pic_order_cnt_type; + uint8_t log2_max_pic_order_cnt_lsb_minus4; + uint8_t delta_pic_order_always_zero_flag; + int32_t offset_for_non_ref_pic; + int32_t offset_for_top_to_bottom_field; + uint8_t num_ref_frames_in_pic_order_cnt_cycle; + int32_t offset_for_ref_frame[256]; + + uint8_t max_num_ref_frames; + uint8_t gaps_in_frame_num_allowed_flag; + + uint16_t pic_width_in_mbs_minus1; + uint16_t pic_height_in_map_units_minus1; + + uint8_t frame_mbs_only_flag; + uint8_t mb_adaptive_frame_field_flag; + uint8_t direct_8x8_inference_flag; + + uint8_t frame_cropping_flag; + uint16_t frame_crop_left_offset; + uint16_t frame_crop_right_offset; + uint16_t frame_crop_top_offset; + uint16_t frame_crop_bottom_offset; + + uint8_t vui_parameters_present_flag; + H264RawVUI vui; +} H264RawSPS; + +typedef struct H264RawSPSExtension { + H264RawNALUnitHeader nal_unit_header; + + uint8_t seq_parameter_set_id; + + uint8_t aux_format_idc; + uint8_t bit_depth_aux_minus8; + uint8_t alpha_incr_flag; + uint16_t alpha_opaque_value; + uint16_t alpha_transparent_value; + + uint8_t additional_extension_flag; +} H264RawSPSExtension; + +typedef struct H264RawPPS { + H264RawNALUnitHeader nal_unit_header; + + uint8_t pic_parameter_set_id; + uint8_t seq_parameter_set_id; + + uint8_t entropy_coding_mode_flag; + uint8_t bottom_field_pic_order_in_frame_present_flag; + + uint8_t num_slice_groups_minus1; + uint8_t slice_group_map_type; + uint16_t run_length_minus1[H264_MAX_SLICE_GROUPS]; + uint16_t top_left[H264_MAX_SLICE_GROUPS]; + uint16_t bottom_right[H264_MAX_SLICE_GROUPS]; + uint8_t slice_group_change_direction_flag; + uint16_t slice_group_change_rate_minus1; + uint16_t pic_size_in_map_units_minus1; + uint8_t slice_group_id[H264_MAX_MB_PIC_SIZE]; + + uint8_t num_ref_idx_l0_default_active_minus1; + uint8_t num_ref_idx_l1_default_active_minus1; + + uint8_t weighted_pred_flag; + uint8_t weighted_bipred_idc; + + int8_t pic_init_qp_minus26; + int8_t pic_init_qs_minus26; + int8_t chroma_qp_index_offset; + + uint8_t deblocking_filter_control_present_flag; + uint8_t constrained_intra_pred_flag; + + uint8_t more_rbsp_data; + + uint8_t redundant_pic_cnt_present_flag; + uint8_t transform_8x8_mode_flag; + + uint8_t pic_scaling_matrix_present_flag; + uint8_t pic_scaling_list_present_flag[12]; + H264RawScalingList scaling_list_4x4[6]; + H264RawScalingList scaling_list_8x8[6]; + + int8_t second_chroma_qp_index_offset; +} H264RawPPS; + +typedef struct H264RawAUD { + H264RawNALUnitHeader nal_unit_header; + + uint8_t primary_pic_type; +} H264RawAUD; + +typedef struct H264RawSEIBufferingPeriod { + uint8_t seq_parameter_set_id; + struct { + uint32_t initial_cpb_removal_delay[H264_MAX_CPB_CNT]; + uint32_t initial_cpb_removal_delay_offset[H264_MAX_CPB_CNT]; + } nal, vcl; +} H264RawSEIBufferingPeriod; + +typedef struct H264RawSEIPicTimestamp { + uint8_t ct_type; + uint8_t nuit_field_based_flag; + uint8_t counting_type; + uint8_t full_timestamp_flag; + uint8_t discontinuity_flag; + uint8_t cnt_dropped_flag; + uint8_t n_frames; + uint8_t seconds_flag; + uint8_t seconds_value; + uint8_t minutes_flag; + uint8_t minutes_value; + uint8_t hours_flag; + uint8_t hours_value; + uint32_t time_offset; +} H264RawSEIPicTimestamp; + +typedef struct H264RawSEIPicTiming { + uint32_t cpb_removal_delay; + uint32_t dpb_output_delay; + uint8_t pic_struct; + uint8_t clock_timestamp_flag[3]; + H264RawSEIPicTimestamp timestamp[3]; +} H264RawSEIPicTiming; + +typedef struct H264RawSEIUserDataRegistered { + uint8_t itu_t_t35_country_code; + uint8_t itu_t_t35_country_code_extension_byte; + uint8_t *data; + size_t data_length; +} H264RawSEIUserDataRegistered; + +typedef struct H264RawSEIUserDataUnregistered { + uint8_t uuid_iso_iec_11578[16]; + uint8_t *data; + size_t data_length; +} H264RawSEIUserDataUnregistered; + +typedef struct H264RawSEIRecoveryPoint { + uint16_t recovery_frame_cnt; + uint8_t exact_match_flag; + uint8_t broken_link_flag; + uint8_t changing_slice_group_idc; +} H264RawSEIRecoveryPoint; + +typedef struct H264RawSEIDisplayOrientation { + uint8_t display_orientation_cancel_flag; + uint8_t hor_flip; + uint8_t ver_flip; + uint16_t anticlockwise_rotation; + uint16_t display_orientation_repetition_period; + uint8_t display_orientation_extension_flag; +} H264RawSEIDisplayOrientation; + +typedef struct H264RawSEIPayload { + uint32_t payload_type; + uint32_t payload_size; + union { + H264RawSEIBufferingPeriod buffering_period; + H264RawSEIPicTiming pic_timing; + // H264RawSEIFiller filler -> no fields. + H264RawSEIUserDataRegistered user_data_registered; + H264RawSEIUserDataUnregistered user_data_unregistered; + H264RawSEIRecoveryPoint recovery_point; + H264RawSEIDisplayOrientation display_orientation; + struct { + uint8_t *data; + size_t data_length; + } other; + } payload; +} H264RawSEIPayload; + +typedef struct H264RawSEI { + H264RawNALUnitHeader nal_unit_header; + + H264RawSEIPayload payload[H264_MAX_SEI_PAYLOADS]; + uint8_t payload_count; +} H264RawSEI; + +typedef struct H264RawSliceHeader { + H264RawNALUnitHeader nal_unit_header; + + uint32_t first_mb_in_slice; + uint8_t slice_type; + + uint8_t pic_parameter_set_id; + + uint8_t colour_plane_id; + + uint16_t frame_num; + uint8_t field_pic_flag; + uint8_t bottom_field_flag; + + uint16_t idr_pic_id; + + uint16_t pic_order_cnt_lsb; + int32_t delta_pic_order_cnt_bottom; + int32_t delta_pic_order_cnt[2]; + + uint8_t redundant_pic_cnt; + uint8_t direct_spatial_mv_pred_flag; + + uint8_t num_ref_idx_active_override_flag; + uint8_t num_ref_idx_l0_active_minus1; + uint8_t num_ref_idx_l1_active_minus1; + + uint8_t ref_pic_list_modification_flag_l0; + uint8_t ref_pic_list_modification_flag_l1; + struct { + uint8_t modification_of_pic_nums_idc; + int32_t abs_diff_pic_num_minus1; + uint8_t long_term_pic_num; + } rplm_l0[H264_MAX_RPLM_COUNT], rplm_l1[H264_MAX_RPLM_COUNT]; + + uint8_t luma_log2_weight_denom; + uint8_t chroma_log2_weight_denom; + + uint8_t luma_weight_l0_flag[H264_MAX_REFS]; + int8_t luma_weight_l0[H264_MAX_REFS]; + int8_t luma_offset_l0[H264_MAX_REFS]; + uint8_t chroma_weight_l0_flag[H264_MAX_REFS]; + int8_t chroma_weight_l0[H264_MAX_REFS][2]; + int8_t chroma_offset_l0[H264_MAX_REFS][2]; + + uint8_t luma_weight_l1_flag[H264_MAX_REFS]; + int8_t luma_weight_l1[H264_MAX_REFS]; + int8_t luma_offset_l1[H264_MAX_REFS]; + uint8_t chroma_weight_l1_flag[H264_MAX_REFS]; + int8_t chroma_weight_l1[H264_MAX_REFS][2]; + int8_t chroma_offset_l1[H264_MAX_REFS][2]; + + uint8_t no_output_of_prior_pics_flag; + uint8_t long_term_reference_flag; + + uint8_t adaptive_ref_pic_marking_mode_flag; + struct { + uint8_t memory_management_control_operation; + int32_t difference_of_pic_nums_minus1; + uint8_t long_term_pic_num; + uint8_t long_term_frame_idx; + uint8_t max_long_term_frame_idx_plus1; + } mmco[H264_MAX_MMCO_COUNT]; + + uint8_t cabac_init_idc; + + int8_t slice_qp_delta; + + uint8_t sp_for_switch_flag; + int8_t slice_qs_delta; + + uint8_t disable_deblocking_filter_idc; + int8_t slice_alpha_c0_offset_div2; + int8_t slice_beta_offset_div2; + + uint16_t slice_group_change_cycle; +} H264RawSliceHeader; + +typedef struct H264RawSlice { + H264RawSliceHeader header; + + uint8_t *data; + size_t data_size; + int data_bit_start; +} H264RawSlice; + + +typedef struct CodedBitstreamH264Context { + // Reader/writer context in common with the H.265 implementation. + CodedBitstreamH2645Context common; + + // All currently available parameter sets. These are updated when + // any parameter set NAL unit is read/written with this context. + H264RawSPS *sps[H264_MAX_SPS_COUNT]; + H264RawPPS *pps[H264_MAX_PPS_COUNT]; + + // The currently active parameter sets. These are updated when any + // NAL unit refers to the relevant parameter set. These pointers + // must also be present in the arrays above. + const H264RawSPS *active_sps; + const H264RawPPS *active_pps; + + // The NAL unit type of the most recent normal slice. This is required + // to be able to read/write auxiliary slices, because IdrPicFlag is + // otherwise unknown. + uint8_t last_slice_nal_unit_type; +} CodedBitstreamH264Context; + + +#endif /* AVCODEC_CBS_H264_H */ diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c new file mode 100644 index 0000000000..2350f039fa --- /dev/null +++ b/libavcodec/cbs_h2645.c @@ -0,0 +1,997 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/attributes.h" +#include "libavutil/avassert.h" + +#include "bytestream.h" +#include "cbs.h" +#include "cbs_internal.h" +#include "cbs_h264.h" +#include "golomb.h" +#include "h264.h" +#include "h264_sei.h" +#include "h2645_parse.h" + + +static int cbs_read_ue_golomb(CodedBitstreamContext *ctx, BitstreamContext *bc, + const char *name, uint32_t *write_to, + uint32_t range_min, uint32_t range_max) +{ + uint32_t value; + int position; + + if (ctx->trace_enable) { + char bits[65]; + unsigned int k; + int i, j; + + position = bitstream_tell(bc); + + for (i = 0; i < 32; i++) { + k = bitstream_read_bit(bc); + bits[i] = k ? '1' : '0'; + if (k) + break; + } + if (i >= 32) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid ue-golomb " + "code found while reading %s: " + "more than 31 zeroes.\n", name); + return AVERROR_INVALIDDATA; + } + value = 1; + for (j = 0; j < i; j++) { + k = bitstream_read_bit(bc); + bits[i + j + 1] = k ? '1' : '0'; + value = value << 1 | k; + } + bits[i + j + 1] = 0; + --value; + + ff_cbs_trace_syntax_element(ctx, position, name, bits, value); + } else { + value = get_ue_golomb_long(bc); + } + + if (value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%"PRIu32", but must be in [%"PRIu32",%"PRIu32"].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + *write_to = value; + return 0; +} + +static int cbs_read_se_golomb(CodedBitstreamContext *ctx, BitstreamContext *bc, + const char *name, int32_t *write_to, + int32_t range_min, int32_t range_max) +{ + int32_t value; + int position; + + if (ctx->trace_enable) { + char bits[65]; + uint32_t v; + unsigned int k; + int i, j; + + position = bitstream_tell(bc); + + for (i = 0; i < 32; i++) { + k = bitstream_read_bit(bc); + bits[i] = k ? '1' : '0'; + if (k) + break; + } + if (i >= 32) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid se-golomb " + "code found while reading %s: " + "more than 31 zeroes.\n", name); + return AVERROR_INVALIDDATA; + } + v = 1; + for (j = 0; j < i; j++) { + k = bitstream_read_bit(bc); + bits[i + j + 1] = k ? '1' : '0'; + v = v << 1 | k; + } + bits[i + j + 1] = 0; + if (v & 1) + value = -(int32_t)(v / 2); + else + value = v / 2; + + ff_cbs_trace_syntax_element(ctx, position, name, bits, value); + } else { + value = get_se_golomb_long(bc); + } + + if (value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%"PRId32", but must be in [%"PRId32",%"PRId32"].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + + *write_to = value; + return 0; +} + +static int cbs_write_ue_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc, + const char *name, uint32_t value, + uint32_t range_min, uint32_t range_max) +{ + int len; + + if (value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%"PRIu32", but must be in [%"PRIu32",%"PRIu32"].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + av_assert0(value != UINT32_MAX); + + len = av_log2(value + 1); + if (put_bits_left(pbc) < 2 * len + 1) + return AVERROR(ENOSPC); + + if (ctx->trace_enable) { + char bits[65]; + int i; + + for (i = 0; i < len; i++) + bits[i] = '0'; + bits[len] = '1'; + for (i = 0; i < len; i++) + bits[len + i + 1] = (value + 1) >> (len - i - 1) & 1 ? '1' : '0'; + bits[len + len + 1] = 0; + + ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), name, bits, value); + } + + put_bits(pbc, len, 0); + if (len + 1 < 32) + put_bits(pbc, len + 1, value + 1); + else + put_bits32(pbc, value + 1); + + return 0; +} + +static int cbs_write_se_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc, + const char *name, int32_t value, + int32_t range_min, int32_t range_max) +{ + int len; + uint32_t uvalue; + + if (value < range_min || value > range_max) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s out of range: " + "%"PRId32", but must be in [%"PRId32",%"PRId32"].\n", + name, value, range_min, range_max); + return AVERROR_INVALIDDATA; + } + av_assert0(value != INT32_MIN); + + if (value == 0) + uvalue = 0; + else if (value > 0) + uvalue = 2 * (uint32_t)value - 1; + else + uvalue = 2 * (uint32_t)-value; + + len = av_log2(uvalue + 1); + if (put_bits_left(pbc) < 2 * len + 1) + return AVERROR(ENOSPC); + + if (ctx->trace_enable) { + char bits[65]; + int i; + + for (i = 0; i < len; i++) + bits[i] = '0'; + bits[len] = '1'; + for (i = 0; i < len; i++) + bits[len + i + 1] = (uvalue + 1) >> (len - i - 1) & 1 ? '1' : '0'; + bits[len + len + 1] = 0; + + ff_cbs_trace_syntax_element(ctx, put_bits_count(pbc), name, bits, value); + } + + put_bits(pbc, len, 0); + if (len + 1 < 32) + put_bits(pbc, len + 1, uvalue + 1); + else + put_bits32(pbc, uvalue + 1); + + return 0; +} + +#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 FUNC_NAME(rw, codec, name) cbs_ ## codec ## _ ## rw ## _ ## name +#define FUNC_H264(rw, name) FUNC_NAME(rw, h264, name) + + +#define READ +#define READWRITE read +#define RWContext BitstreamContext + +#define xu(width, name, var, range_min, range_max) do { \ + uint32_t value = range_min; \ + CHECK(ff_cbs_read_unsigned(ctx, rw, width, #name, \ + &value, range_min, range_max)); \ + var = value; \ + } while (0) +#define xue(name, var, range_min, range_max) do { \ + uint32_t value = range_min; \ + CHECK(cbs_read_ue_golomb(ctx, rw, #name, \ + &value, range_min, range_max)); \ + var = value; \ + } while (0) +#define xse(name, var, range_min, range_max) do { \ + int32_t value = range_min; \ + CHECK(cbs_read_se_golomb(ctx, rw, #name, \ + &value, range_min, range_max)); \ + var = value; \ + } while (0) + + +#define u(width, name, range_min, range_max) \ + xu(width, name, current->name, range_min, range_max) +#define flag(name) u(1, name, 0, 1) +#define ue(name, range_min, range_max) \ + xue(name, current->name, range_min, range_max) +#define se(name, range_min, range_max) \ + xse(name, current->name, range_min, range_max) + +#define infer(name, value) do { \ + current->name = value; \ + } while (0) + +static int cbs_h2645_read_more_rbsp_data(BitstreamContext *bc) +{ + int bits_left = bitstream_bits_left(bc); + if (bits_left > 8) + return 1; + if (bitstream_peek(bc, bits_left) == 1 << (bits_left - 1)) + return 0; + return 1; +} + +#define more_rbsp_data(var) ((var) = cbs_h2645_read_more_rbsp_data(rw)) + +#define byte_alignment(rw) (bitstream_tell(rw) % 8) + +#define allocate(name, size) do { \ + name = av_mallocz(size); \ + if (!name) \ + return AVERROR(ENOMEM); \ + } while (0) + +#define FUNC(name) FUNC_H264(READWRITE, name) +#include "cbs_h264_syntax_template.c" +#undef FUNC + +#undef READ +#undef READWRITE +#undef RWContext +#undef xu +#undef xue +#undef xse +#undef u +#undef flag +#undef ue +#undef se +#undef infer +#undef more_rbsp_data +#undef byte_alignment +#undef allocate + + +#define WRITE +#define READWRITE write +#define RWContext PutBitContext + +#define xu(width, name, var, range_min, range_max) do { \ + uint32_t value = var; \ + CHECK(ff_cbs_write_unsigned(ctx, rw, width, #name, \ + value, range_min, range_max)); \ + } while (0) +#define xue(name, var, range_min, range_max) do { \ + uint32_t value = var; \ + CHECK(cbs_write_ue_golomb(ctx, rw, #name, \ + value, range_min, range_max)); \ + } while (0) +#define xse(name, var, range_min, range_max) do { \ + int32_t value = var; \ + CHECK(cbs_write_se_golomb(ctx, rw, #name, \ + value, range_min, range_max)); \ + } while (0) + +#define u(width, name, range_min, range_max) \ + xu(width, name, current->name, range_min, range_max) +#define flag(name) u(1, name, 0, 1) +#define ue(name, range_min, range_max) \ + xue(name, current->name, range_min, range_max) +#define se(name, range_min, range_max) \ + xse(name, current->name, range_min, range_max) + +#define infer(name, value) do { \ + if (current->name != (value)) { \ + av_log(ctx->log_ctx, AV_LOG_WARNING, "Warning: " \ + "%s does not match inferred value: " \ + "%"PRId64", but should be %"PRId64".\n", \ + #name, (int64_t)current->name, (int64_t)(value)); \ + } \ + } while (0) + +#define more_rbsp_data(var) (var) + +#define byte_alignment(rw) (put_bits_count(rw) % 8) + +#define allocate(name, size) do { \ + if (!name) { \ + av_log(ctx->log_ctx, AV_LOG_ERROR, "%s must be set " \ + "for writing.\n", #name); \ + return AVERROR_INVALIDDATA; \ + } \ + } while (0) + +#define FUNC(name) FUNC_H264(READWRITE, name) +#include "cbs_h264_syntax_template.c" +#undef FUNC + +#undef WRITE +#undef READWRITE +#undef RWContext +#undef xu +#undef xue +#undef xse +#undef u +#undef flag +#undef ue +#undef se +#undef infer +#undef more_rbsp_data +#undef byte_alignment +#undef allocate + + +static void cbs_h264_free_sei(H264RawSEI *sei) +{ + int i; + for (i = 0; i < sei->payload_count; i++) { + H264RawSEIPayload *payload = &sei->payload[i]; + + switch (payload->payload_type) { + case H264_SEI_TYPE_BUFFERING_PERIOD: + case H264_SEI_TYPE_PIC_TIMING: + case H264_SEI_TYPE_RECOVERY_POINT: + case H264_SEI_TYPE_DISPLAY_ORIENTATION: + break; + case H264_SEI_TYPE_USER_DATA_REGISTERED: + av_freep(&payload->payload.user_data_registered.data); + break; + case H264_SEI_TYPE_USER_DATA_UNREGISTERED: + av_freep(&payload->payload.user_data_unregistered.data); + break; + default: + av_freep(&payload->payload.other.data); + break; + } + } +} + +static void cbs_h264_free_slice(H264RawSlice *slice) +{ + av_freep(&slice->data); +} + +static void cbs_h264_free_nal_unit(CodedBitstreamUnit *unit) +{ + switch (unit->type) { + case H264_NAL_SEI: + cbs_h264_free_sei(unit->content); + break; + case H264_NAL_IDR_SLICE: + case H264_NAL_SLICE: + cbs_h264_free_slice(unit->content); + break; + } + av_freep(&unit->content); +} + +static int cbs_h2645_fragment_add_nals(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + const H2645Packet *packet) +{ + int err, i; + + for (i = 0; i < packet->nb_nals; i++) { + const H2645NAL *nal = &packet->nals[i]; + uint8_t *data; + + data = av_malloc(nal->size); + if (!data) + return AVERROR(ENOMEM); + memcpy(data, nal->data, nal->size); + + err = ff_cbs_insert_unit_data(ctx, frag, -1, nal->type, + data, nal->size); + if (err < 0) { + av_freep(&data); + return err; + } + } + + return 0; +} + +static int cbs_h2645_split_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag, + int header) +{ + enum AVCodecID codec_id = ctx->codec->codec_id; + CodedBitstreamH2645Context *priv = ctx->priv_data; + GetByteContext gbc; + int err; + + av_assert0(frag->data && frag->nb_units == 0); + if (frag->data_size == 0) + return 0; + + if (header && frag->data[0] && codec_id == AV_CODEC_ID_H264) { + // AVCC header. + size_t size, start, end; + int i, count, version; + + priv->mp4 = 1; + + bytestream2_init(&gbc, frag->data, frag->data_size); + + if (bytestream2_get_bytes_left(&gbc) < 6) + return AVERROR_INVALIDDATA; + + version = bytestream2_get_byte(&gbc); + if (version != 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid AVCC header: " + "first byte %u.", version); + return AVERROR_INVALIDDATA; + } + + bytestream2_skip(&gbc, 3); + priv->nal_length_size = (bytestream2_get_byte(&gbc) & 3) + 1; + + // SPS array. + count = bytestream2_get_byte(&gbc) & 0x1f; + start = bytestream2_tell(&gbc); + for (i = 0; i < count; i++) { + if (bytestream2_get_bytes_left(&gbc) < 2 * (count - i)) + return AVERROR_INVALIDDATA; + size = bytestream2_get_be16(&gbc); + if (bytestream2_get_bytes_left(&gbc) < size) + return AVERROR_INVALIDDATA; + bytestream2_skip(&gbc, size); + } + end = bytestream2_tell(&gbc); + + err = ff_h2645_packet_split(&priv->read_packet, + frag->data + start, end - start, + ctx->log_ctx, 1, 2, AV_CODEC_ID_H264); + if (err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to split AVCC SPS array.\n"); + return err; + } + err = cbs_h2645_fragment_add_nals(ctx, frag, &priv->read_packet); + if (err < 0) + return err; + + // PPS array. + count = bytestream2_get_byte(&gbc); + start = bytestream2_tell(&gbc); + for (i = 0; i < count; i++) { + if (bytestream2_get_bytes_left(&gbc) < 2 * (count - i)) + return AVERROR_INVALIDDATA; + size = bytestream2_get_be16(&gbc); + if (bytestream2_get_bytes_left(&gbc) < size) + return AVERROR_INVALIDDATA; + bytestream2_skip(&gbc, size); + } + end = bytestream2_tell(&gbc); + + err = ff_h2645_packet_split(&priv->read_packet, + frag->data + start, end - start, + ctx->log_ctx, 1, 2, AV_CODEC_ID_H264); + if (err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to split AVCC PPS array.\n"); + return err; + } + err = cbs_h2645_fragment_add_nals(ctx, frag, &priv->read_packet); + if (err < 0) + return err; + + if (bytestream2_get_bytes_left(&gbc) > 0) { + av_log(ctx->log_ctx, AV_LOG_WARNING, "%u bytes left at end of AVCC " + "header.\n", bytestream2_get_bytes_left(&gbc)); + } + + } else { + // Annex B, or later MP4 with already-known parameters. + + err = ff_h2645_packet_split(&priv->read_packet, + frag->data, frag->data_size, + ctx->log_ctx, + priv->mp4, priv->nal_length_size, + codec_id); + if (err < 0) + return err; + + err = cbs_h2645_fragment_add_nals(ctx, frag, &priv->read_packet); + if (err < 0) + return err; + } + + return 0; +} + +#define cbs_h2645_replace_ps(h26n, ps_name, ps_var, id_element) \ +static int cbs_h26 ## h26n ## _replace_ ## ps_var(CodedBitstreamContext *ctx, \ + const H26 ## h26n ## Raw ## ps_name *ps_var) \ +{ \ + CodedBitstreamH26 ## h26n ## Context *priv = ctx->priv_data; \ + unsigned int id = ps_var->id_element; \ + if (id > FF_ARRAY_ELEMS(priv->ps_var)) { \ + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid " #ps_name \ + " id : %d.\n", id); \ + return AVERROR_INVALIDDATA; \ + } \ + av_freep(&priv->ps_var[id]); \ + priv->ps_var[id] = av_malloc(sizeof(*ps_var)); \ + if (!priv->ps_var[id]) \ + return AVERROR(ENOMEM); \ + memcpy(priv->ps_var[id], ps_var, sizeof(*ps_var)); \ + return 0; \ +} + +cbs_h2645_replace_ps(4, SPS, sps, seq_parameter_set_id) +cbs_h2645_replace_ps(4, PPS, pps, pic_parameter_set_id) + +static int cbs_h264_read_nal_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) +{ + BitstreamContext bc; + int err; + + err = bitstream_init(&bc, unit->data, 8 * unit->data_size); + if (err < 0) + return err; + + switch (unit->type) { + case H264_NAL_SPS: + { + H264RawSPS *sps; + + sps = av_mallocz(sizeof(*sps)); + if (!sps) + return AVERROR(ENOMEM); + err = cbs_h264_read_sps(ctx, &bc, sps); + if (err >= 0) + err = cbs_h264_replace_sps(ctx, sps); + if (err < 0) { + av_free(sps); + return err; + } + + unit->content = sps; + } + break; + + case H264_NAL_SPS_EXT: + { + H264RawSPSExtension *sps_ext; + + sps_ext = av_mallocz(sizeof(*sps_ext)); + if (!sps_ext) + return AVERROR(ENOMEM); + err = cbs_h264_read_sps_extension(ctx, &bc, sps_ext); + if (err < 0) { + av_free(sps_ext); + return err; + } + + unit->content = sps_ext; + } + break; + + case H264_NAL_PPS: + { + H264RawPPS *pps; + + pps = av_mallocz(sizeof(*pps)); + if (!pps) + return AVERROR(ENOMEM); + err = cbs_h264_read_pps(ctx, &bc, pps); + if (err >= 0) + err = cbs_h264_replace_pps(ctx, pps); + if (err < 0) { + av_free(pps); + return err; + } + + unit->content = pps; + } + break; + + case H264_NAL_SLICE: + case H264_NAL_IDR_SLICE: + case H264_NAL_AUXILIARY_SLICE: + { + H264RawSlice *slice; + int pos, len; + + slice = av_mallocz(sizeof(*slice)); + if (!slice) + return AVERROR(ENOMEM); + err = cbs_h264_read_slice_header(ctx, &bc, &slice->header); + if (err < 0) { + av_free(slice); + return err; + } + + pos = bitstream_tell(&bc); + len = unit->data_size; + if (!unit->data[len - 1]) { + int z; + for (z = 0; z < len && !unit->data[len - z - 1]; z++); + av_log(ctx->log_ctx, AV_LOG_DEBUG, "Deleted %d trailing zeroes " + "from slice data.\n", z); + len -= z; + } + + slice->data_size = len - pos / 8; + slice->data = av_malloc(slice->data_size); + if (!slice->data) { + av_free(slice); + return AVERROR(ENOMEM); + } + memcpy(slice->data, + unit->data + pos / 8, slice->data_size); + slice->data_bit_start = pos % 8; + + unit->content = slice; + } + break; + + case H264_NAL_AUD: + { + H264RawAUD *aud; + + aud = av_mallocz(sizeof(*aud)); + if (!aud) + return AVERROR(ENOMEM); + err = cbs_h264_read_aud(ctx, &bc, aud); + if (err < 0) { + av_free(aud); + return err; + } + + unit->content = aud; + } + break; + + case H264_NAL_SEI: + { + H264RawSEI *sei; + + sei = av_mallocz(sizeof(*sei)); + if (!sei) + return AVERROR(ENOMEM); + err = cbs_h264_read_sei(ctx, &bc, sei); + if (err < 0) { + cbs_h264_free_sei(sei); + return err; + } + + unit->content = sei; + } + break; + + default: + return AVERROR(ENOSYS); + } + + return 0; +} + +static int cbs_h264_write_nal_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) +{ + int err; + + switch (unit->type) { + case H264_NAL_SPS: + { + H264RawSPS *sps = unit->content; + + err = cbs_h264_write_sps(ctx, pbc, sps); + if (err < 0) + return err; + + err = cbs_h264_replace_sps(ctx, sps); + if (err < 0) + return err; + } + break; + + case H264_NAL_SPS_EXT: + { + H264RawSPSExtension *sps_ext; + + err = cbs_h264_write_sps_extension(ctx, pbc, sps_ext); + if (err < 0) + return err; + } + break; + + case H264_NAL_PPS: + { + H264RawPPS *pps = unit->content; + + err = cbs_h264_write_pps(ctx, pbc, pps); + if (err < 0) + return err; + + err = cbs_h264_replace_pps(ctx, pps); + if (err < 0) + return err; + } + break; + + case H264_NAL_SLICE: + case H264_NAL_IDR_SLICE: + { + H264RawSlice *slice = unit->content; + BitstreamContext bc; + int bits_left, end, zeroes; + + err = cbs_h264_write_slice_header(ctx, pbc, &slice->header); + if (err < 0) + return err; + + if (slice->data) { + if (slice->data_size * 8 + 8 > put_bits_left(pbc)) + return AVERROR(ENOSPC); + + bitstream_init(&bc, slice->data, slice->data_size * 8); + bitstream_skip(&bc, slice->data_bit_start); + + // Copy in two-byte blocks, but stop before copying the + // rbsp_stop_one_bit in the final byte. + while (bitstream_bits_left(&bc) > 23) + put_bits(pbc, 16, bitstream_read(&bc, 16)); + + bits_left = bitstream_bits_left(&bc); + end = bitstream_read(&bc, bits_left); + + // rbsp_stop_one_bit must be present here. + av_assert0(end); + zeroes = ff_ctz(end); + if (bits_left > zeroes + 1) + put_bits(pbc, bits_left - zeroes - 1, + end >> (zeroes + 1)); + put_bits(pbc, 1, 1); + while (put_bits_count(pbc) % 8 != 0) + put_bits(pbc, 1, 0); + } else { + // No slice data - that was just the header. + // (Bitstream may be unaligned!) + } + } + break; + + case H264_NAL_AUD: + { + err = cbs_h264_write_aud(ctx, pbc, unit->content); + if (err < 0) + return err; + } + break; + + case H264_NAL_SEI: + { + err = cbs_h264_write_sei(ctx, pbc, unit->content); + if (err < 0) + return err; + } + break; + + default: + av_log(ctx->log_ctx, AV_LOG_ERROR, "Write unimplemented for " + "NAL unit type %"PRIu32".\n", unit->type); + return AVERROR_PATCHWELCOME; + } + + return 0; +} + +static int cbs_h2645_write_nal_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) +{ + CodedBitstreamH2645Context *priv = ctx->priv_data; + enum AVCodecID codec_id = ctx->codec->codec_id; + 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 " + "%zu bytes).\n", priv->write_buffer_size); + return err; + } + } + + init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size); + + err = cbs_h264_write_nal_unit(ctx, unit, &pbc); + + if (err == AVERROR(ENOSPC)) { + // Overflow. + priv->write_buffer_size *= 2; + goto reallocate_and_try_again; + } + // Overflow but we didn't notice. + av_assert0(put_bits_count(&pbc) <= 8 * priv->write_buffer_size); + + 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 = av_reallocp(&unit->data, unit->data_size); + if (err < 0) + return err; + + memcpy(unit->data, priv->write_buffer, unit->data_size); + + return 0; +} + +static int cbs_h2645_assemble_fragment(CodedBitstreamContext *ctx, + CodedBitstreamFragment *frag) +{ + uint8_t *data; + size_t max_size, dp, sp; + int err, i, zero_run; + + for (i = 0; i < frag->nb_units; i++) { + // Data should already all have been written when we get here. + av_assert0(frag->units[i].data); + } + + max_size = 0; + for (i = 0; i < frag->nb_units; i++) { + // Start code + content with worst-case emulation prevention. + max_size += 3 + frag->units[i].data_size * 3 / 2; + } + + data = av_malloc(max_size); + if (!data) + return AVERROR(ENOMEM); + + dp = 0; + for (i = 0; i < frag->nb_units; i++) { + CodedBitstreamUnit *unit = &frag->units[i]; + + if (unit->data_bit_padding > 0) { + if (i < frag->nb_units - 1) + av_log(ctx->log_ctx, AV_LOG_WARNING, "Probably invalid " + "unaligned padding on non-final NAL unit.\n"); + else + frag->data_bit_padding = unit->data_bit_padding; + } + + if (unit->type == H264_NAL_SPS || + unit->type == H264_NAL_PPS || + i == 0 /* (Assume this is the start of an access unit.) */) { + // zero_byte + data[dp++] = 0; + } + // start_code_prefix_one_3bytes + data[dp++] = 0; + data[dp++] = 0; + data[dp++] = 1; + + zero_run = 0; + for (sp = 0; sp < unit->data_size; sp++) { + if (zero_run < 2) { + if (unit->data[sp] == 0) + ++zero_run; + else + zero_run = 0; + } else { + if ((unit->data[sp] & ~3) == 0) { + // emulation_prevention_three_byte + data[dp++] = 3; + } + zero_run = unit->data[sp] == 0; + } + data[dp++] = unit->data[sp]; + } + } + + av_assert0(dp <= max_size); + err = av_reallocp(&data, dp); + if (err) + return err; + + frag->data = data; + frag->data_size = dp; + + return 0; +} + +static void cbs_h264_close(CodedBitstreamContext *ctx) +{ + CodedBitstreamH264Context *h264 = ctx->priv_data; + int i; + + ff_h2645_packet_uninit(&h264->common.read_packet); + + av_freep(&h264->common.write_buffer); + + for (i = 0; i < FF_ARRAY_ELEMS(h264->sps); i++) + av_freep(&h264->sps[i]); + for (i = 0; i < FF_ARRAY_ELEMS(h264->pps); i++) + av_freep(&h264->pps[i]); +} + +const CodedBitstreamType ff_cbs_type_h264 = { + .codec_id = AV_CODEC_ID_H264, + + .priv_data_size = sizeof(CodedBitstreamH264Context), + + .split_fragment = &cbs_h2645_split_fragment, + .read_unit = &cbs_h264_read_nal_unit, + .write_unit = &cbs_h2645_write_nal_unit, + .assemble_fragment = &cbs_h2645_assemble_fragment, + + .free_unit = &cbs_h264_free_nal_unit, + .close = &cbs_h264_close, +}; diff --git a/libavcodec/cbs_h2645.h b/libavcodec/cbs_h2645.h new file mode 100644 index 0000000000..750247b6ad --- /dev/null +++ b/libavcodec/cbs_h2645.h @@ -0,0 +1,43 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_H2645_H +#define AVCODEC_CBS_H2645_H + +#include +#include + +#include "h2645_parse.h" + + +typedef struct CodedBitstreamH2645Context { + // If set, the stream being read is in MP4 (AVCC/HVCC) format. If not + // set, the stream is assumed to be in annex B format. + int mp4; + // Size in bytes of the NAL length field for MP4 format. + int nal_length_size; + // Packet reader. + H2645Packet read_packet; + + // Write buffer + uint8_t *write_buffer; + size_t write_buffer_size; +} CodedBitstreamH2645Context; + + +#endif /* AVCODEC_CBS_H2645_H */ diff --git a/libavcodec/cbs_h264_syntax_template.c b/libavcodec/cbs_h264_syntax_template.c new file mode 100644 index 0000000000..0fe18441c0 --- /dev/null +++ b/libavcodec/cbs_h264_syntax_template.c @@ -0,0 +1,1230 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +static int FUNC(rbsp_trailing_bits)(CodedBitstreamContext *ctx, RWContext *rw) +{ + int err; + av_unused int one = 1, zero = 0; + xu(1, rbsp_stop_one_bit, one, 1, 1); + while (byte_alignment(rw) != 0) + xu(1, rbsp_alignment_zero_bit, zero, 0, 0); + + return 0; +} + +static int FUNC(nal_unit_header)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawNALUnitHeader *current, + uint32_t valid_type_mask) +{ + int err; + + u(1, forbidden_zero_bit, 0, 0); + u(2, nal_ref_idc, 0, 3); + u(5, nal_unit_type, 0, 31); + + if (!(1 << current->nal_unit_type & valid_type_mask)) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid NAL unit type %d.\n", + current->nal_unit_type); + return AVERROR_INVALIDDATA; + } + + if (current->nal_unit_type == 14 || + current->nal_unit_type == 20 || + current->nal_unit_type == 21) { + if (current->nal_unit_type != 21) + flag(svc_extension_flag); + else + flag(avc_3d_extension_flag); + + if (current->svc_extension_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SVC not supported.\n"); + return AVERROR_PATCHWELCOME; + + } else if (current->avc_3d_extension_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "3DAVC not supported.\n"); + return AVERROR_PATCHWELCOME; + + } else { + av_log(ctx->log_ctx, AV_LOG_ERROR, "MVC not supported.\n"); + return AVERROR_PATCHWELCOME; + } + } + + return 0; +} + +static int FUNC(scaling_list)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawScalingList *current, + int size_of_scaling_list) +{ + int err, i, scale; + + scale = 8; + for (i = 0; i < size_of_scaling_list; i++) { + xse(delta_scale, current->delta_scale[i], -128, +127); + scale = (scale + current->delta_scale[i] + 256) % 256; + if (scale == 0) + break; + } + + return 0; +} + +static int FUNC(hrd_parameters)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawHRD *current) +{ + int err, i; + + ue(cpb_cnt_minus1, 0, 31); + u(4, bit_rate_scale, 0, 15); + u(4, cpb_size_scale, 0, 15); + + for (i = 0; i <= current->cpb_cnt_minus1; i++) { + ue(bit_rate_value_minus1[i], 0, UINT32_MAX - 1); + ue(cpb_size_value_minus1[i], 0, UINT32_MAX - 1); + flag(cbr_flag[i]); + } + + u(5, initial_cpb_removal_delay_length_minus1, 0, 31); + u(5, cpb_removal_delay_length_minus1, 0, 31); + u(5, dpb_output_delay_length_minus1, 0, 31); + u(5, time_offset_length, 0, 31); + + return 0; +} + +static int FUNC(vui_parameters)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawVUI *current, H264RawSPS *sps) +{ + int err; + + flag(aspect_ratio_info_present_flag); + if (current->aspect_ratio_info_present_flag) { + u(8, aspect_ratio_idc, 0, 255); + if (current->aspect_ratio_idc == 255) { + u(16, sar_width, 0, 65535); + u(16, sar_height, 0, 65535); + } + } else { + infer(aspect_ratio_idc, 0); + } + + flag(overscan_info_present_flag); + if (current->overscan_info_present_flag) + flag(overscan_appropriate_flag); + + flag(video_signal_type_present_flag); + if (current->video_signal_type_present_flag) { + u(3, video_format, 0, 7); + flag(video_full_range_flag); + flag(colour_description_present_flag); + if (current->colour_description_present_flag) { + u(8, colour_primaries, 0, 255); + u(8, transfer_characteristics, 0, 255); + u(8, matrix_coefficients, 0, 255); + } + } else { + infer(video_format, 5); + infer(video_full_range_flag, 0); + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + } + + flag(chroma_loc_info_present_flag); + if (current->chroma_loc_info_present_flag) { + ue(chroma_sample_loc_type_top_field, 0, 5); + ue(chroma_sample_loc_type_bottom_field, 0, 5); + } else { + infer(chroma_sample_loc_type_top_field, 0); + infer(chroma_sample_loc_type_bottom_field, 0); + } + + flag(timing_info_present_flag); + if (current->timing_info_present_flag) { + u(32, num_units_in_tick, 1, UINT32_MAX); + u(32, time_scale, 1, UINT32_MAX); + flag(fixed_frame_rate_flag); + } else { + infer(fixed_frame_rate_flag, 0); + } + + flag(nal_hrd_parameters_present_flag); + if (current->nal_hrd_parameters_present_flag) + CHECK(FUNC(hrd_parameters)(ctx, rw, ¤t->nal_hrd_parameters)); + + flag(vcl_hrd_parameters_present_flag); + if (current->vcl_hrd_parameters_present_flag) + CHECK(FUNC(hrd_parameters)(ctx, rw, ¤t->vcl_hrd_parameters)); + + if (current->nal_hrd_parameters_present_flag || + current->vcl_hrd_parameters_present_flag) + flag(low_delay_hrd_flag); + else + infer(low_delay_hrd_flag, 1 - current->fixed_frame_rate_flag); + + flag(pic_struct_present_flag); + + flag(bitstream_restriction_flag); + if (current->bitstream_restriction_flag) { + 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(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); + + if ((sps->profile_idc == 44 || sps->profile_idc == 86 || + sps->profile_idc == 110 || 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) +{ + int err, i; + + HEADER("Sequence Parameter Set"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + 1 << H264_NAL_SPS)); + + u(8, profile_idc, 0, 255); + + flag(constraint_set0_flag); + flag(constraint_set1_flag); + flag(constraint_set2_flag); + flag(constraint_set3_flag); + flag(constraint_set4_flag); + flag(constraint_set5_flag); + + u(2, reserved_zero_2bits, 0, 0); + + u(8, level_idc, 0, 255); + + ue(seq_parameter_set_id, 0, 31); + + if (current->profile_idc == 100 || current->profile_idc == 110 || + current->profile_idc == 122 || current->profile_idc == 244 || + current->profile_idc == 44 || current->profile_idc == 83 || + current->profile_idc == 86 || current->profile_idc == 118 || + current->profile_idc == 128 || current->profile_idc == 138) { + ue(chroma_format_idc, 0, 3); + + if (current->chroma_format_idc == 3) + flag(separate_colour_plane_flag); + else + infer(separate_colour_plane_flag, 0); + + ue(bit_depth_luma_minus8, 0, 6); + ue(bit_depth_chroma_minus8, 0, 6); + + flag(qpprime_y_zero_transform_bypass_flag); + + flag(seq_scaling_matrix_present_flag); + if (current->seq_scaling_matrix_present_flag) { + for (i = 0; i < ((current->chroma_format_idc != 3) ? 8 : 12); i++) { + flag(seq_scaling_list_present_flag[i]); + if (current->seq_scaling_list_present_flag[i]) { + if (i < 6) + CHECK(FUNC(scaling_list)(ctx, rw, + ¤t->scaling_list_4x4[i], + 16)); + else + CHECK(FUNC(scaling_list)(ctx, rw, + ¤t->scaling_list_8x8[i - 6], + 64)); + } + } + } + } else { + infer(chroma_format_idc, current->profile_idc == 183 ? 0 : 1); + + infer(separate_colour_plane_flag, 0); + infer(bit_depth_luma_minus8, 0); + infer(bit_depth_chroma_minus8, 0); + } + + ue(log2_max_frame_num_minus4, 0, 12); + ue(pic_order_cnt_type, 0, 2); + + if (current->pic_order_cnt_type == 0) { + ue(log2_max_pic_order_cnt_lsb_minus4, 0, 12); + } else if (current->pic_order_cnt_type == 1) { + flag(delta_pic_order_always_zero_flag); + se(offset_for_non_ref_pic, INT32_MIN + 1, INT32_MAX); + se(offset_for_top_to_bottom_field, INT32_MIN + 1, INT32_MAX); + ue(num_ref_frames_in_pic_order_cnt_cycle, 0, 255); + + for (i = 0; i < current->num_ref_frames_in_pic_order_cnt_cycle; i++) + se(offset_for_ref_frame[i], INT32_MIN + 1, INT32_MAX); + } + + ue(max_num_ref_frames, 0, H264_MAX_DPB_FRAMES); + flag(gaps_in_frame_num_allowed_flag); + + ue(pic_width_in_mbs_minus1, 0, H264_MAX_MB_WIDTH); + ue(pic_height_in_map_units_minus1, 0, H264_MAX_MB_HEIGHT); + + flag(frame_mbs_only_flag); + if (!current->frame_mbs_only_flag) + flag(mb_adaptive_frame_field_flag); + + flag(direct_8x8_inference_flag); + + flag(frame_cropping_flag); + if (current->frame_cropping_flag) { + ue(frame_crop_left_offset, 0, H264_MAX_WIDTH); + ue(frame_crop_right_offset, 0, H264_MAX_WIDTH); + ue(frame_crop_top_offset, 0, H264_MAX_HEIGHT); + ue(frame_crop_bottom_offset, 0, H264_MAX_HEIGHT); + } + + flag(vui_parameters_present_flag); + if (current->vui_parameters_present_flag) + CHECK(FUNC(vui_parameters)(ctx, rw, ¤t->vui, current)); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(sps_extension)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSPSExtension *current) +{ + int err; + + HEADER("Sequence Parameter Set Extension"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + 1 << H264_NAL_SPS_EXT)); + + ue(seq_parameter_set_id, 0, 31); + + ue(aux_format_idc, 0, 3); + + if (current->aux_format_idc != 0) { + int bits; + + ue(bit_depth_aux_minus8, 0, 4); + flag(alpha_incr_flag); + + bits = current->bit_depth_aux_minus8 + 9; + u(bits, alpha_opaque_value, 0, (1 << bits) - 1); + u(bits, alpha_transparent_value, 0, (1 << bits) - 1); + } + + flag(additional_extension_flag); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(pps)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawPPS *current) +{ + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps; + int err, i; + + HEADER("Picture Parameter Set"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + 1 << H264_NAL_PPS)); + + ue(pic_parameter_set_id, 0, 255); + ue(seq_parameter_set_id, 0, 31); + + sps = h264->sps[current->seq_parameter_set_id]; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + current->seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + + flag(entropy_coding_mode_flag); + flag(bottom_field_pic_order_in_frame_present_flag); + + ue(num_slice_groups_minus1, 0, 7); + if (current->num_slice_groups_minus1 > 0) { + unsigned int pic_size; + int iGroup; + + pic_size = (sps->pic_width_in_mbs_minus1 + 1) * + (sps->pic_height_in_map_units_minus1 + 1); + + ue(slice_group_map_type, 0, 6); + + if (current->slice_group_map_type == 0) { + for (iGroup = 0; iGroup <= current->num_slice_groups_minus1; iGroup++) + ue(run_length_minus1[iGroup], 0, pic_size - 1); + + } else if (current->slice_group_map_type == 2) { + for (iGroup = 0; iGroup < current->num_slice_groups_minus1; iGroup++) { + ue(top_left[iGroup], 0, pic_size - 1); + ue(bottom_right[iGroup], current->top_left[iGroup], pic_size - 1); + } + } else if (current->slice_group_map_type == 3 || + current->slice_group_map_type == 4 || + current->slice_group_map_type == 5) { + flag(slice_group_change_direction_flag); + ue(slice_group_change_rate_minus1, 0, pic_size - 1); + } else if (current->slice_group_map_type == 6) { + ue(pic_size_in_map_units_minus1, pic_size - 1, pic_size - 1); + for (i = 0; i <= current->pic_size_in_map_units_minus1; i++) + u(av_log2(2 * current->num_slice_groups_minus1 + 1), + slice_group_id[i], 0, current->num_slice_groups_minus1); + } + } + + ue(num_ref_idx_l0_default_active_minus1, 0, 31); + ue(num_ref_idx_l1_default_active_minus1, 0, 31); + + flag(weighted_pred_flag); + u(2, weighted_bipred_idc, 0, 2); + + se(pic_init_qp_minus26, -26 - 6 * sps->bit_depth_luma_minus8, +25); + se(pic_init_qs_minus26, -26, +25); + se(chroma_qp_index_offset, -12, +12); + + flag(deblocking_filter_control_present_flag); + flag(constrained_intra_pred_flag); + flag(redundant_pic_cnt_present_flag); + + if (more_rbsp_data(current->more_rbsp_data)) + { + flag(transform_8x8_mode_flag); + + flag(pic_scaling_matrix_present_flag); + if (current->pic_scaling_matrix_present_flag) { + for (i = 0; i < 6 + (((sps->chroma_format_idc != 3) ? 2 : 6) * + current->transform_8x8_mode_flag); i++) { + flag(pic_scaling_list_present_flag[i]); + if (current->pic_scaling_list_present_flag[i]) { + if (i < 6) + CHECK(FUNC(scaling_list)(ctx, rw, + ¤t->scaling_list_4x4[i], + 16)); + else + CHECK(FUNC(scaling_list)(ctx, rw, + ¤t->scaling_list_8x8[i - 6], + 64)); + } + } + } + + se(second_chroma_qp_index_offset, -12, +12); + } else { + infer(transform_8x8_mode_flag, 0); + infer(pic_scaling_matrix_present_flag, 0); + infer(second_chroma_qp_index_offset, current->chroma_qp_index_offset); + } + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(sei_buffering_period)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIBufferingPeriod *current) +{ + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps; + int err, i, length; + + ue(seq_parameter_set_id, 0, 31); + + sps = h264->sps[current->seq_parameter_set_id]; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + current->seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h264->active_sps = sps; + + if (sps->vui.nal_hrd_parameters_present_flag) { + for (i = 0; i <= sps->vui.nal_hrd_parameters.cpb_cnt_minus1; i++) { + length = sps->vui.nal_hrd_parameters.initial_cpb_removal_delay_length_minus1 + 1; + xu(length, initial_cpb_removal_delay[SchedSelIdx], + current->nal.initial_cpb_removal_delay[i], + 0, (1 << (uint64_t)length) - 1); + xu(length, initial_cpb_removal_delay_offset[SchedSelIdx], + current->nal.initial_cpb_removal_delay_offset[i], + 0, (1 << (uint64_t)length) - 1); + } + } + + if (sps->vui.vcl_hrd_parameters_present_flag) { + for (i = 0; i <= sps->vui.vcl_hrd_parameters.cpb_cnt_minus1; i++) { + length = sps->vui.vcl_hrd_parameters.initial_cpb_removal_delay_length_minus1 + 1; + xu(length, initial_cpb_removal_delay[SchedSelIdx], + current->vcl.initial_cpb_removal_delay[i], + 0, (1 << (uint64_t)length) - 1); + xu(length, initial_cpb_removal_delay_offset[SchedSelIdx], + current->vcl.initial_cpb_removal_delay_offset[i], + 0, (1 << (uint64_t)length) - 1); + } + } + + return 0; +} + +static int FUNC(sei_pic_timestamp)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIPicTimestamp *current) +{ + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps; + uint8_t time_offset_length; + int err; + + u(2, ct_type, 0, 2); + flag(nuit_field_based_flag); + u(5, counting_type, 0, 6); + flag(full_timestamp_flag); + flag(discontinuity_flag); + flag(cnt_dropped_flag); + u(8, n_frames, 0, 255); + if (current->full_timestamp_flag) { + u(6, seconds_value, 0, 59); + u(6, minutes_value, 0, 59); + u(5, hours_value, 0, 23); + } else { + flag(seconds_flag); + if (current->seconds_flag) { + u(6, seconds_value, 0, 59); + flag(minutes_flag); + if (current->minutes_flag) { + u(6, minutes_value, 0, 59); + flag(hours_flag); + if (current->hours_flag) + u(5, hours_value, 0, 23); + } + } + } + + sps = h264->active_sps; + if (sps->vui.nal_hrd_parameters_present_flag) + time_offset_length = sps->vui.nal_hrd_parameters.time_offset_length; + else if (sps->vui.vcl_hrd_parameters_present_flag) + time_offset_length = sps->vui.vcl_hrd_parameters.time_offset_length; + else + time_offset_length = 24; + + if (time_offset_length > 0) + u(time_offset_length, time_offset, + 0, (1 << (uint64_t)time_offset_length) - 1); + else + infer(time_offset, 0); + + return 0; +} + +static int FUNC(sei_pic_timing)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIPicTiming *current) +{ + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps; + int err; + + sps = h264->active_sps; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "No active SPS for pic_timing.\n"); + return AVERROR_INVALIDDATA; + } + + if (sps->vui.nal_hrd_parameters_present_flag || + sps->vui.vcl_hrd_parameters_present_flag) { + const H264RawHRD *hrd; + + if (sps->vui.nal_hrd_parameters_present_flag) + hrd = &sps->vui.nal_hrd_parameters; + else if (sps->vui.vcl_hrd_parameters_present_flag) + hrd = &sps->vui.vcl_hrd_parameters; + else { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "No HRD parameters for pic_timing.\n"); + return AVERROR_INVALIDDATA; + } + + u(hrd->cpb_removal_delay_length_minus1 + 1, cpb_removal_delay, + 0, (1 << (uint64_t)hrd->cpb_removal_delay_length_minus1) + 1); + u(hrd->dpb_output_delay_length_minus1 + 1, dpb_output_delay, + 0, (1 << (uint64_t)hrd->dpb_output_delay_length_minus1) + 1); + } + + if (sps->vui.pic_struct_present_flag) { + static const int num_clock_ts[9] = { + 1, 1, 1, 2, 2, 3, 3, 2, 3 + }; + int i; + + u(4, pic_struct, 0, 8); + if (current->pic_struct > 8) + return AVERROR_INVALIDDATA; + + for (i = 0; i < num_clock_ts[current->pic_struct]; i++) { + flag(clock_timestamp_flag[i]); + if (current->clock_timestamp_flag[i]) + CHECK(FUNC(sei_pic_timestamp)(ctx, rw, ¤t->timestamp[i])); + } + } + + return 0; +} + +static int FUNC(sei_user_data_registered)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIUserDataRegistered *current, + uint32_t *payload_size) +{ + int err, i, j; + + u(8, itu_t_t35_country_code, 0x00, 0xff); + if (current->itu_t_t35_country_code != 0xff) + i = 1; + else { + u(8, itu_t_t35_country_code_extension_byte, 0x00, 0xff); + i = 2; + } + +#ifdef READ + if (*payload_size < i) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid SEI user data registered payload.\n"); + return AVERROR_INVALIDDATA; + } + current->data_length = *payload_size - i; +#else + *payload_size = i + current->data_length; +#endif + + allocate(current->data, current->data_length); + for (j = 0; j < current->data_length; j++) + xu(8, itu_t_t35_payload_byte, current->data[j], 0x00, 0xff); + + return 0; +} + +static int FUNC(sei_user_data_unregistered)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIUserDataUnregistered *current, + uint32_t *payload_size) +{ + int err, i; + +#ifdef READ + if (*payload_size < 16) { + av_log(ctx->log_ctx, AV_LOG_ERROR, + "Invalid SEI user data unregistered payload.\n"); + return AVERROR_INVALIDDATA; + } + current->data_length = *payload_size - 16; +#else + *payload_size = 16 + current->data_length; +#endif + + for (i = 0; i < 16; i++) { + xu(8, uuid_iso_iec_11578, + current->uuid_iso_iec_11578[i], 0x00, 0xff); + } + + allocate(current->data, current->data_length); + + for (i = 0; i < current->data_length; i++) + xu(8, user_data_payload_byte, current->data[i], 0x00, 0xff); + + return 0; +} + +static int FUNC(sei_recovery_point)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIRecoveryPoint *current) +{ + int err; + + ue(recovery_frame_cnt, 0, 65535); + flag(exact_match_flag); + flag(broken_link_flag); + u(2, changing_slice_group_idc, 0, 2); + + return 0; +} + +static int FUNC(sei_display_orientation)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIDisplayOrientation *current) +{ + int err; + + flag(display_orientation_cancel_flag); + if (!current->display_orientation_cancel_flag) { + flag(hor_flip); + flag(ver_flip); + u(16, anticlockwise_rotation, 0, 65535); + ue(display_orientation_repetition_period, 0, 16384); + flag(display_orientation_extension_flag); + } + + return 0; +} + +static int FUNC(sei_payload)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEIPayload *current) +{ + int err, i; + int start_position, end_position; + +#ifdef READ + start_position = bitstream_tell(rw); +#else + start_position = put_bits_count(rw); +#endif + + switch (current->payload_type) { + case H264_SEI_TYPE_BUFFERING_PERIOD: + CHECK(FUNC(sei_buffering_period) + (ctx, rw, ¤t->payload.buffering_period)); + break; + case H264_SEI_TYPE_PIC_TIMING: + CHECK(FUNC(sei_pic_timing) + (ctx, rw, ¤t->payload.pic_timing)); + break; + case H264_SEI_TYPE_FILLER_PAYLOAD: + { + av_unused int ff_byte = 0xff; + for (i = 0; i < current->payload_size; i++) + xu(8, ff_byte, ff_byte, 0xff, 0xff); + } + break; + case H264_SEI_TYPE_USER_DATA_REGISTERED: + CHECK(FUNC(sei_user_data_registered) + (ctx, rw, ¤t->payload.user_data_registered, ¤t->payload_size)); + break; + case H264_SEI_TYPE_USER_DATA_UNREGISTERED: + CHECK(FUNC(sei_user_data_unregistered) + (ctx, rw, ¤t->payload.user_data_unregistered, ¤t->payload_size)); + break; + case H264_SEI_TYPE_RECOVERY_POINT: + CHECK(FUNC(sei_recovery_point) + (ctx, rw, ¤t->payload.recovery_point)); + break; + case H264_SEI_TYPE_DISPLAY_ORIENTATION: + CHECK(FUNC(sei_display_orientation) + (ctx, rw, ¤t->payload.display_orientation)); + break; + default: + { + allocate(current->payload.other.data, current->payload_size); + for (i = 0; i < current->payload_size; i++) + xu(8, payload_byte, current->payload.other.data[i], 0, 255); + } + } + + if (byte_alignment(rw)) { + av_unused int one = 1, zero = 0; + xu(1, bit_equal_to_one, one, 1, 1); + while (byte_alignment(rw)) + xu(1, bit_equal_to_zero, zero, 0, 0); + } + +#ifdef READ + end_position = bitstream_tell(rw); + if (end_position < start_position + 8 * current->payload_size) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Incorrect SEI payload length: " + "header %d bits, actually %d bits.\n", + 8 * current->payload_size, + end_position - start_position); + return AVERROR_INVALIDDATA; + } +#else + end_position = put_bits_count(rw); + current->payload_size = (end_position - start_position) / 8; +#endif + + return 0; +} + +static int FUNC(sei)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSEI *current) +{ + int err, k; + + HEADER("Supplemental Enhancement Information"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + 1 << H264_NAL_SEI)); + +#ifdef READ + for (k = 0; k < H264_MAX_SEI_PAYLOADS; k++) { + uint32_t payload_type = 0; + uint32_t payload_size = 0; + uint32_t tmp; + + while (bitstream_peek(rw, 8) == 0xff) { + xu(8, ff_byte, tmp, 0xff, 0xff); + payload_type += 255; + } + xu(8, last_payload_type_byte, tmp, 0, 254); + payload_type += tmp; + + while (bitstream_peek(rw, 8) == 0xff) { + xu(8, ff_byte, tmp, 0xff, 0xff); + payload_size += 255; + } + xu(8, last_payload_size_byte, tmp, 0, 254); + payload_size += tmp; + + current->payload[k].payload_type = payload_type; + current->payload[k].payload_size = payload_size; + + CHECK(FUNC(sei_payload)(ctx, rw, ¤t->payload[k])); + + if (!cbs_h2645_read_more_rbsp_data(rw)) + break; + } + if (k >= H264_MAX_SEI_PAYLOADS) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many payloads in " + "SEI message: found %d.\n", k); + return AVERROR_INVALIDDATA; + } + current->payload_count = k + 1; +#else + for (k = 0; k < current->payload_count; k++) { + PutBitContext start_state; + uint32_t tmp; + int need_size, i; + + // Somewhat clumsy: we write the payload twice when + // we don't know the size in advance. This will mess + // with trace output, but is otherwise harmless. + start_state = *rw; + need_size = !current->payload[k].payload_size; + for (i = 0; i < 1 + need_size; i++) { + *rw = start_state; + + tmp = current->payload[k].payload_type; + while (tmp >= 255) { + xu(8, ff_byte, 0xff, 0xff, 0xff); + tmp -= 255; + } + xu(8, last_payload_type_byte, tmp, 0, 254); + + tmp = current->payload[k].payload_size; + while (tmp >= 255) { + xu(8, ff_byte, 0xff, 0xff, 0xff); + tmp -= 255; + } + xu(8, last_payload_size_byte, tmp, 0, 254); + + CHECK(FUNC(sei_payload)(ctx, rw, ¤t->payload[k])); + } + } +#endif + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(aud)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawAUD *current) +{ + int err; + + HEADER("Access Unit Delimiter"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + 1 << H264_NAL_AUD)); + + u(3, primary_pic_type, 0, 7); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(ref_pic_list_modification)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSliceHeader *current) +{ + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps = h264->active_sps; + int err, i, mopn; + + if (current->slice_type % 5 != 2 && + current->slice_type % 5 != 4) { + flag(ref_pic_list_modification_flag_l0); + if (current->ref_pic_list_modification_flag_l0) { + for (i = 0; i < H264_MAX_RPLM_COUNT; i++) { + xue(modification_of_pic_nums_idc, + current->rplm_l0[i].modification_of_pic_nums_idc, 0, 3); + + mopn = current->rplm_l0[i].modification_of_pic_nums_idc; + if (mopn == 3) + break; + + if (mopn == 0 || mopn == 1) + xue(abs_diff_pic_num_minus1, + current->rplm_l0[i].abs_diff_pic_num_minus1, + 0, (1 + current->field_pic_flag) * + (1 << (sps->log2_max_frame_num_minus4 + 4))); + else if (mopn == 2) + xue(long_term_pic_num, + current->rplm_l0[i].long_term_pic_num, + 0, sps->max_num_ref_frames - 1); + } + } + } + + if (current->slice_type % 5 == 1) { + flag(ref_pic_list_modification_flag_l1); + if (current->ref_pic_list_modification_flag_l1) { + for (i = 0; i < H264_MAX_RPLM_COUNT; i++) { + xue(modification_of_pic_nums_idc, + current->rplm_l1[i].modification_of_pic_nums_idc, 0, 3); + + mopn = current->rplm_l1[i].modification_of_pic_nums_idc; + if (mopn == 3) + break; + + if (mopn == 0 || mopn == 1) + xue(abs_diff_pic_num_minus1, + current->rplm_l1[i].abs_diff_pic_num_minus1, + 0, (1 + current->field_pic_flag) * + (1 << (sps->log2_max_frame_num_minus4 + 4))); + else if (mopn == 2) + xue(long_term_pic_num, + current->rplm_l1[i].long_term_pic_num, + 0, sps->max_num_ref_frames - 1); + } + } + } + + return 0; +} + +static int FUNC(pred_weight_table)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSliceHeader *current) +{ + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps = h264->active_sps; + int chroma; + int err, i, j; + + ue(luma_log2_weight_denom, 0, 7); + + chroma = !sps->separate_colour_plane_flag && sps->chroma_format_idc != 0; + if (chroma) + ue(chroma_log2_weight_denom, 0, 7); + + for (i = 0; i <= current->num_ref_idx_l0_active_minus1; i++) { + flag(luma_weight_l0_flag[i]); + if (current->luma_weight_l0_flag[i]) { + se(luma_weight_l0[i], -128, +127); + se(luma_offset_l0[i], -128, +127); + } + if (chroma) { + flag(chroma_weight_l0_flag[i]); + if (current->chroma_weight_l0_flag[i]) { + for (j = 0; j < 2; j++) { + se(chroma_weight_l0[i][j], -128, +127); + se(chroma_offset_l0[i][j], -128, +127); + } + } + } + } + + if (current->slice_type % 5 == 1) { + for (i = 0; i <= current->num_ref_idx_l1_active_minus1; i++) { + flag(luma_weight_l1_flag[i]); + if (current->luma_weight_l1_flag[i]) { + se(luma_weight_l1[i], -128, +127); + se(luma_offset_l1[i], -128, +127); + } + if (chroma) { + flag(chroma_weight_l1_flag[i]); + if (current->chroma_weight_l1_flag[i]) { + for (j = 0; j < 2; j++) { + se(chroma_weight_l1[i][j], -128, +127); + se(chroma_offset_l1[i][j], -128, +127); + } + } + } + } + } + + return 0; +} + +static int FUNC(dec_ref_pic_marking)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSliceHeader *current, int idr_pic_flag) +{ + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps = h264->active_sps; + int err, i; + uint32_t mmco; + + if (idr_pic_flag) { + flag(no_output_of_prior_pics_flag); + flag(long_term_reference_flag); + } else { + flag(adaptive_ref_pic_marking_mode_flag); + if (current->adaptive_ref_pic_marking_mode_flag) { + for (i = 0; i < H264_MAX_MMCO_COUNT; i++) { + xue(memory_management_control_operation, + current->mmco[i].memory_management_control_operation, + 0, 6); + + mmco = current->mmco[i].memory_management_control_operation; + if (mmco == 0) + break; + + if (mmco == 1 || mmco == 3) + xue(difference_of_pic_nums_minus1, + current->mmco[i].difference_of_pic_nums_minus1, + 0, INT32_MAX); + if (mmco == 2) + xue(long_term_pic_num, + current->mmco[i].long_term_pic_num, + 0, sps->max_num_ref_frames - 1); + if (mmco == 3 || mmco == 6) + xue(long_term_frame_idx, + current->mmco[i].long_term_frame_idx, + 0, sps->max_num_ref_frames - 1); + if (mmco == 4) + xue(max_long_term_frame_idx_plus1, + current->mmco[i].max_long_term_frame_idx_plus1, + 0, sps->max_num_ref_frames); + } + if (i == H264_MAX_MMCO_COUNT) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many " + "memory management control operations.\n"); + return AVERROR_INVALIDDATA; + } + } + } + + return 0; +} + +static int FUNC(slice_header)(CodedBitstreamContext *ctx, RWContext *rw, + H264RawSliceHeader *current) +{ + CodedBitstreamH264Context *h264 = ctx->priv_data; + const H264RawSPS *sps; + const H264RawPPS *pps; + int err; + int idr_pic_flag; + int slice_type_i, slice_type_p, slice_type_b; + int slice_type_si, slice_type_sp; + + HEADER("Slice Header"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, + 1 << H264_NAL_SLICE | + 1 << H264_NAL_IDR_SLICE | + 1 << H264_NAL_AUXILIARY_SLICE)); + + if (current->nal_unit_header.nal_unit_type == H264_NAL_AUXILIARY_SLICE) { + if (!h264->last_slice_nal_unit_type) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Auxiliary slice " + "is not decodable without the main picture " + "in the same access unit.\n"); + return AVERROR_INVALIDDATA; + } + } else { + h264->last_slice_nal_unit_type = + current->nal_unit_header.nal_unit_type; + } + idr_pic_flag = h264->last_slice_nal_unit_type == H264_NAL_IDR_SLICE; + + ue(first_mb_in_slice, 0, H264_MAX_MB_PIC_SIZE - 1); + ue(slice_type, 0, 9); + + slice_type_i = current->slice_type % 5 == 2; + slice_type_p = current->slice_type % 5 == 0; + slice_type_b = current->slice_type % 5 == 1; + slice_type_si = current->slice_type % 5 == 4; + slice_type_sp = current->slice_type % 5 == 3; + + if (idr_pic_flag && !(slice_type_i || slice_type_si)) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid slice type %d " + "for IDR picture.\n", current->slice_type); + return AVERROR_INVALIDDATA; + } + + ue(pic_parameter_set_id, 0, 255); + + pps = h264->pps[current->pic_parameter_set_id]; + if (!pps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "PPS id %d not available.\n", + current->pic_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h264->active_pps = pps; + + sps = h264->sps[pps->seq_parameter_set_id]; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + pps->seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h264->active_sps = sps; + + if (sps->separate_colour_plane_flag) + u(2, colour_plane_id, 0, 2); + + u(sps->log2_max_frame_num_minus4 + 4, frame_num, + 0, (1 << (sps->log2_max_frame_num_minus4 + 4)) - 1); + + if (!sps->frame_mbs_only_flag) { + flag(field_pic_flag); + if (current->field_pic_flag) + flag(bottom_field_flag); + else + infer(bottom_field_flag, 0); + } else { + infer(field_pic_flag, 0); + infer(bottom_field_flag, 0); + } + + if (idr_pic_flag) + ue(idr_pic_id, 0, 65535); + + if (sps->pic_order_cnt_type == 0) { + u(sps->log2_max_pic_order_cnt_lsb_minus4 + 4, pic_order_cnt_lsb, + 0, (1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4)) - 1); + if (pps->bottom_field_pic_order_in_frame_present_flag && + !current->field_pic_flag) + se(delta_pic_order_cnt_bottom, INT32_MIN + 1, INT32_MAX); + + } else if (sps->pic_order_cnt_type == 1) { + if (!sps->delta_pic_order_always_zero_flag) { + se(delta_pic_order_cnt[0], INT32_MIN + 1, INT32_MAX); + if (pps->bottom_field_pic_order_in_frame_present_flag && + !current->field_pic_flag) + se(delta_pic_order_cnt[1], INT32_MIN + 1, INT32_MAX); + else + infer(delta_pic_order_cnt[1], 0); + } else { + infer(delta_pic_order_cnt[0], 0); + infer(delta_pic_order_cnt[1], 0); + } + } + + if (pps->redundant_pic_cnt_present_flag) + ue(redundant_pic_cnt, 0, 127); + + if (slice_type_b) + flag(direct_spatial_mv_pred_flag); + + if (slice_type_p || slice_type_sp || slice_type_b) { + flag(num_ref_idx_active_override_flag); + if (current->num_ref_idx_active_override_flag) { + ue(num_ref_idx_l0_active_minus1, 0, 31); + if (slice_type_b) + ue(num_ref_idx_l1_active_minus1, 0, 31); + } else { + infer(num_ref_idx_l0_active_minus1, + pps->num_ref_idx_l0_default_active_minus1); + infer(num_ref_idx_l1_active_minus1, + pps->num_ref_idx_l1_default_active_minus1); + } + } + + if (current->nal_unit_header.nal_unit_type == 20 || + current->nal_unit_header.nal_unit_type == 21) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "MVC / 3DAVC not supported.\n"); + return AVERROR_PATCHWELCOME; + } else { + CHECK(FUNC(ref_pic_list_modification)(ctx, rw, current)); + } + + if ((pps->weighted_pred_flag && (slice_type_p || slice_type_sp)) || + (pps->weighted_bipred_idc == 1 && slice_type_b)) { + CHECK(FUNC(pred_weight_table)(ctx, rw, current)); + } + + if (current->nal_unit_header.nal_ref_idc != 0) { + CHECK(FUNC(dec_ref_pic_marking)(ctx, rw, current, idr_pic_flag)); + } + + if (pps->entropy_coding_mode_flag && + !slice_type_i && !slice_type_si) { + ue(cabac_init_idc, 0, 2); + } + + se(slice_qp_delta, - 51 - 6 * sps->bit_depth_luma_minus8, + + 51 + 6 * sps->bit_depth_luma_minus8); + if (slice_type_sp || slice_type_si) { + if (slice_type_sp) + flag(sp_for_switch_flag); + se(slice_qs_delta, -51, +51); + } + + if (pps->deblocking_filter_control_present_flag) { + ue(disable_deblocking_filter_idc, 0, 2); + if (current->disable_deblocking_filter_idc != 1) { + se(slice_alpha_c0_offset_div2, -6, +6); + se(slice_beta_offset_div2, -6, +6); + } else { + infer(slice_alpha_c0_offset_div2, 0); + infer(slice_beta_offset_div2, 0); + } + } else { + infer(disable_deblocking_filter_idc, 0); + infer(slice_alpha_c0_offset_div2, 0); + infer(slice_beta_offset_div2, 0); + } + + if (pps->num_slice_groups_minus1 > 0 && + pps->slice_group_map_type >= 3 && + pps->slice_group_map_type <= 5) { + unsigned int pic_size, max, bits; + + pic_size = (sps->pic_width_in_mbs_minus1 + 1) * + (sps->pic_height_in_map_units_minus1 + 1); + max = (pic_size + pps->slice_group_change_rate_minus1) / + (pps->slice_group_change_rate_minus1 + 1); + bits = av_log2(2 * max - 1); + + u(bits, slice_group_change_cycle, 0, max); + } + + if (pps->entropy_coding_mode_flag) { + av_unused int one = 1; + while (byte_alignment(rw)) + xu(1, cabac_alignment_one_bit, one, 1, 1); + } + + return 0; +} diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h index a691790b70..e87a43970f 100644 --- a/libavcodec/cbs_internal.h +++ b/libavcodec/cbs_internal.h @@ -80,4 +80,7 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, uint32_t range_min, uint32_t range_max); +extern const CodedBitstreamType ff_cbs_type_h264; + + #endif /* AVCODEC_CBS_INTERNAL_H */ From 867381b8b51fa21fa2b8f071f508f3d39cc9c1f0 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 14 May 2017 16:32:00 +0100 Subject: [PATCH 06/15] lavc: Add coded bitstream read/write support for H.265 --- configure | 2 + libavcodec/Makefile | 1 + libavcodec/cbs.c | 3 + libavcodec/cbs_h2645.c | 410 ++++++- libavcodec/cbs_h265.h | 537 +++++++++ libavcodec/cbs_h265_syntax_template.c | 1502 +++++++++++++++++++++++++ libavcodec/cbs_internal.h | 1 + 7 files changed, 2453 insertions(+), 3 deletions(-) create mode 100644 libavcodec/cbs_h265.h create mode 100644 libavcodec/cbs_h265_syntax_template.c diff --git a/configure b/configure index 54af2e0cb5..535862fcaf 100755 --- a/configure +++ b/configure @@ -1741,6 +1741,7 @@ CONFIG_EXTRA=" cabac cbs cbs_h264 + cbs_h265 dirac_parse dvprofile faandct @@ -1968,6 +1969,7 @@ threads_if_any="$THREADS_LIST" # subsystems cbs_h264_select="cbs golomb" +cbs_h265_select="cbs golomb" dct_select="rdft" dirac_parse_select="golomb" error_resilience_select="me_cmp" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 30fc388f8c..9791fb342e 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -55,6 +55,7 @@ OBJS-$(CONFIG_BSWAPDSP) += bswapdsp.o 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_DCT) += dct.o dct32_fixed.o dct32_float.o OBJS-$(CONFIG_ERROR_RESILIENCE) += error_resilience.o OBJS-$(CONFIG_FAANDCT) += faandct.o diff --git a/libavcodec/cbs.c b/libavcodec/cbs.c index 9352b535b2..b5d5dfd2fd 100644 --- a/libavcodec/cbs.c +++ b/libavcodec/cbs.c @@ -31,6 +31,9 @@ static const CodedBitstreamType *cbs_type_table[] = { #if CONFIG_CBS_H264 &ff_cbs_type_h264, #endif +#if CONFIG_CBS_H265 + &ff_cbs_type_h265, +#endif }; int ff_cbs_init(CodedBitstreamContext *ctx, diff --git a/libavcodec/cbs_h2645.c b/libavcodec/cbs_h2645.c index 2350f039fa..23556f47f8 100644 --- a/libavcodec/cbs_h2645.c +++ b/libavcodec/cbs_h2645.c @@ -23,10 +23,12 @@ #include "cbs.h" #include "cbs_internal.h" #include "cbs_h264.h" +#include "cbs_h265.h" #include "golomb.h" #include "h264.h" #include "h264_sei.h" #include "h2645_parse.h" +#include "hevc.h" static int cbs_read_ue_golomb(CodedBitstreamContext *ctx, BitstreamContext *bc, @@ -237,6 +239,7 @@ static int cbs_write_se_golomb(CodedBitstreamContext *ctx, PutBitContext *pbc, #define FUNC_NAME(rw, codec, name) cbs_ ## codec ## _ ## rw ## _ ## name #define FUNC_H264(rw, name) FUNC_NAME(rw, h264, name) +#define FUNC_H265(rw, name) FUNC_NAME(rw, h265, name) #define READ @@ -299,6 +302,10 @@ static int cbs_h2645_read_more_rbsp_data(BitstreamContext *bc) #include "cbs_h264_syntax_template.c" #undef FUNC +#define FUNC(name) FUNC_H265(READWRITE, name) +#include "cbs_h265_syntax_template.c" +#undef FUNC + #undef READ #undef READWRITE #undef RWContext @@ -368,6 +375,10 @@ static int cbs_h2645_read_more_rbsp_data(BitstreamContext *bc) #include "cbs_h264_syntax_template.c" #undef FUNC +#define FUNC(name) FUNC_H265(READWRITE, name) +#include "cbs_h265_syntax_template.c" +#undef FUNC + #undef WRITE #undef READWRITE #undef RWContext @@ -428,6 +439,40 @@ static void cbs_h264_free_nal_unit(CodedBitstreamUnit *unit) av_freep(&unit->content); } +static void cbs_h265_free_nal_unit(CodedBitstreamUnit *unit) +{ + switch (unit->type) { + case HEVC_NAL_VPS: + av_freep(&((H265RawVPS*)unit->content)->extension_data.data); + break; + case HEVC_NAL_SPS: + av_freep(&((H265RawSPS*)unit->content)->extension_data.data); + break; + case HEVC_NAL_PPS: + av_freep(&((H265RawPPS*)unit->content)->extension_data.data); + break; + case HEVC_NAL_TRAIL_N: + case HEVC_NAL_TRAIL_R: + case HEVC_NAL_TSA_N: + case HEVC_NAL_TSA_R: + case HEVC_NAL_STSA_N: + case HEVC_NAL_STSA_R: + case HEVC_NAL_RADL_N: + case HEVC_NAL_RADL_R: + case HEVC_NAL_RASL_N: + case HEVC_NAL_RASL_R: + case HEVC_NAL_BLA_W_LP: + case HEVC_NAL_BLA_W_RADL: + case HEVC_NAL_BLA_N_LP: + case HEVC_NAL_IDR_W_RADL: + case HEVC_NAL_IDR_N_LP: + case HEVC_NAL_CRA_NUT: + av_freep(&((H265RawSlice*)unit->content)->data); + break; + } + av_freep(&unit->content); +} + static int cbs_h2645_fragment_add_nals(CodedBitstreamContext *ctx, CodedBitstreamFragment *frag, const H2645Packet *packet) @@ -542,6 +587,58 @@ static int cbs_h2645_split_fragment(CodedBitstreamContext *ctx, "header.\n", bytestream2_get_bytes_left(&gbc)); } + } else if (header && frag->data[0] && codec_id == AV_CODEC_ID_HEVC) { + // HVCC header. + size_t size, start, end; + int i, j, nb_arrays, nal_unit_type, nb_nals, version; + + priv->mp4 = 1; + + bytestream2_init(&gbc, frag->data, frag->data_size); + + if (bytestream2_get_bytes_left(&gbc) < 23) + return AVERROR_INVALIDDATA; + + version = bytestream2_get_byte(&gbc); + if (version != 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid HVCC header: " + "first byte %u.", version); + return AVERROR_INVALIDDATA; + } + + bytestream2_skip(&gbc, 20); + priv->nal_length_size = (bytestream2_get_byte(&gbc) & 3) + 1; + + nb_arrays = bytestream2_get_byte(&gbc); + for (i = 0; i < nb_arrays; i++) { + nal_unit_type = bytestream2_get_byte(&gbc) & 0x3f; + nb_nals = bytestream2_get_be16(&gbc); + + start = bytestream2_tell(&gbc); + for (j = 0; j < nb_nals; j++) { + if (bytestream2_get_bytes_left(&gbc) < 2) + return AVERROR_INVALIDDATA; + size = bytestream2_get_be16(&gbc); + if (bytestream2_get_bytes_left(&gbc) < size) + return AVERROR_INVALIDDATA; + bytestream2_skip(&gbc, size); + } + end = bytestream2_tell(&gbc); + + err = ff_h2645_packet_split(&priv->read_packet, + frag->data + start, end - start, + ctx->log_ctx, 1, 2, AV_CODEC_ID_HEVC); + if (err < 0) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Failed to split " + "HVCC array %d (%d NAL units of type %d).\n", + i, nb_nals, nal_unit_type); + return err; + } + err = cbs_h2645_fragment_add_nals(ctx, frag, &priv->read_packet); + if (err < 0) + return err; + } + } else { // Annex B, or later MP4 with already-known parameters. @@ -582,6 +679,9 @@ static int cbs_h26 ## h26n ## _replace_ ## ps_var(CodedBitstreamContext *ctx, \ cbs_h2645_replace_ps(4, SPS, sps, seq_parameter_set_id) cbs_h2645_replace_ps(4, PPS, pps, pic_parameter_set_id) +cbs_h2645_replace_ps(5, VPS, vps, vps_video_parameter_set_id) +cbs_h2645_replace_ps(5, SPS, sps, sps_seq_parameter_set_id) +cbs_h2645_replace_ps(5, PPS, pps, pps_pic_parameter_set_id) static int cbs_h264_read_nal_unit(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit) @@ -730,6 +830,150 @@ static int cbs_h264_read_nal_unit(CodedBitstreamContext *ctx, return 0; } +static int cbs_h265_read_nal_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit) +{ + BitstreamContext bc; + int err; + + err = bitstream_init(&bc, unit->data, 8 * unit->data_size); + if (err < 0) + return err; + + switch (unit->type) { + case HEVC_NAL_VPS: + { + H265RawVPS *vps; + + vps = av_mallocz(sizeof(*vps)); + if (!vps) + return AVERROR(ENOMEM); + err = cbs_h265_read_vps(ctx, &bc, vps); + if (err >= 0) + err = cbs_h265_replace_vps(ctx, vps); + if (err < 0) { + av_free(vps); + return err; + } + + unit->content = vps; + } + break; + case HEVC_NAL_SPS: + { + H265RawSPS *sps; + + sps = av_mallocz(sizeof(*sps)); + if (!sps) + return AVERROR(ENOMEM); + err = cbs_h265_read_sps(ctx, &bc, sps); + if (err >= 0) + err = cbs_h265_replace_sps(ctx, sps); + if (err < 0) { + av_free(sps); + return err; + } + + unit->content = sps; + } + break; + + case HEVC_NAL_PPS: + { + H265RawPPS *pps; + + pps = av_mallocz(sizeof(*pps)); + if (!pps) + return AVERROR(ENOMEM); + err = cbs_h265_read_pps(ctx, &bc, pps); + if (err >= 0) + err = cbs_h265_replace_pps(ctx, pps); + if (err < 0) { + av_free(pps); + return err; + } + + unit->content = pps; + } + break; + + case HEVC_NAL_TRAIL_N: + case HEVC_NAL_TRAIL_R: + case HEVC_NAL_TSA_N: + case HEVC_NAL_TSA_R: + case HEVC_NAL_STSA_N: + case HEVC_NAL_STSA_R: + case HEVC_NAL_RADL_N: + case HEVC_NAL_RADL_R: + case HEVC_NAL_RASL_N: + case HEVC_NAL_RASL_R: + case HEVC_NAL_BLA_W_LP: + case HEVC_NAL_BLA_W_RADL: + case HEVC_NAL_BLA_N_LP: + case HEVC_NAL_IDR_W_RADL: + case HEVC_NAL_IDR_N_LP: + case HEVC_NAL_CRA_NUT: + { + H265RawSlice *slice; + int pos, len; + + slice = av_mallocz(sizeof(*slice)); + if (!slice) + return AVERROR(ENOMEM); + err = cbs_h265_read_slice_segment_header(ctx, &bc, &slice->header); + if (err < 0) { + av_free(slice); + return err; + } + + pos = bitstream_tell(&bc); + len = unit->data_size; + if (!unit->data[len - 1]) { + int z; + for (z = 0; z < len && !unit->data[len - z - 1]; z++); + av_log(ctx->log_ctx, AV_LOG_DEBUG, "Deleted %d trailing zeroes " + "from slice data.\n", z); + len -= z; + } + + slice->data_size = len - pos / 8; + slice->data = av_malloc(slice->data_size); + if (!slice->data) { + av_free(slice); + return AVERROR(ENOMEM); + } + memcpy(slice->data, + unit->data + pos / 8, slice->data_size); + slice->data_bit_start = pos % 8; + + unit->content = slice; + } + break; + + case HEVC_NAL_AUD: + { + H265RawAUD *aud; + + aud = av_mallocz(sizeof(*aud)); + if (!aud) + return AVERROR(ENOMEM); + err = cbs_h265_read_aud(ctx, &bc, aud); + if (err < 0) { + av_free(aud); + return err; + } + + unit->content = aud; + } + break; + + default: + return AVERROR(ENOSYS); + } + + return 0; +} + static int cbs_h264_write_nal_unit(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit, PutBitContext *pbc) @@ -842,6 +1086,127 @@ static int cbs_h264_write_nal_unit(CodedBitstreamContext *ctx, return 0; } +static int cbs_h265_write_nal_unit(CodedBitstreamContext *ctx, + CodedBitstreamUnit *unit, + PutBitContext *pbc) +{ + int err; + + switch (unit->type) { + case HEVC_NAL_VPS: + { + H265RawVPS *vps = unit->content; + + err = cbs_h265_write_vps(ctx, pbc, vps); + if (err < 0) + return err; + + err = cbs_h265_replace_vps(ctx, vps); + if (err < 0) + return err; + } + break; + + case HEVC_NAL_SPS: + { + H265RawSPS *sps = unit->content; + + err = cbs_h265_write_sps(ctx, pbc, sps); + if (err < 0) + return err; + + err = cbs_h265_replace_sps(ctx, sps); + if (err < 0) + return err; + } + break; + + case HEVC_NAL_PPS: + { + H265RawPPS *pps = unit->content; + + err = cbs_h265_write_pps(ctx, pbc, pps); + if (err < 0) + return err; + + err = cbs_h265_replace_pps(ctx, pps); + if (err < 0) + return err; + } + break; + + case HEVC_NAL_TRAIL_N: + case HEVC_NAL_TRAIL_R: + case HEVC_NAL_TSA_N: + case HEVC_NAL_TSA_R: + case HEVC_NAL_STSA_N: + case HEVC_NAL_STSA_R: + case HEVC_NAL_RADL_N: + case HEVC_NAL_RADL_R: + case HEVC_NAL_RASL_N: + case HEVC_NAL_RASL_R: + case HEVC_NAL_BLA_W_LP: + case HEVC_NAL_BLA_W_RADL: + case HEVC_NAL_BLA_N_LP: + case HEVC_NAL_IDR_W_RADL: + case HEVC_NAL_IDR_N_LP: + case HEVC_NAL_CRA_NUT: + { + H265RawSlice *slice = unit->content; + BitstreamContext bc; + int bits_left, end, zeroes; + + err = cbs_h265_write_slice_segment_header(ctx, pbc, &slice->header); + if (err < 0) + return err; + + if (slice->data) { + if (slice->data_size * 8 + 8 > put_bits_left(pbc)) + return AVERROR(ENOSPC); + + bitstream_init(&bc, slice->data, slice->data_size * 8); + bitstream_skip(&bc, slice->data_bit_start); + + // Copy in two-byte blocks, but stop before copying the + // rbsp_stop_one_bit in the final byte. + while (bitstream_bits_left(&bc) > 23) + put_bits(pbc, 16, bitstream_read(&bc, 16)); + + bits_left = bitstream_bits_left(&bc); + end = bitstream_read(&bc, bits_left); + + // rbsp_stop_one_bit must be present here. + av_assert0(end); + zeroes = ff_ctz(end); + if (bits_left > zeroes + 1) + put_bits(pbc, bits_left - zeroes - 1, + end >> (zeroes + 1)); + put_bits(pbc, 1, 1); + while (put_bits_count(pbc) % 8 != 0) + put_bits(pbc, 1, 0); + } else { + // No slice data - that was just the header. + } + } + break; + + case HEVC_NAL_AUD: + { + err = cbs_h265_write_aud(ctx, pbc, unit->content); + if (err < 0) + return err; + } + break; + + default: + av_log(ctx->log_ctx, AV_LOG_ERROR, "Write unimplemented for " + "NAL unit type %d.\n", unit->type); + return AVERROR_PATCHWELCOME; + } + + return 0; +} + static int cbs_h2645_write_nal_unit(CodedBitstreamContext *ctx, CodedBitstreamUnit *unit) { @@ -866,7 +1231,10 @@ static int cbs_h2645_write_nal_unit(CodedBitstreamContext *ctx, init_put_bits(&pbc, priv->write_buffer, priv->write_buffer_size); - err = cbs_h264_write_nal_unit(ctx, unit, &pbc); + if (codec_id == AV_CODEC_ID_H264) + err = cbs_h264_write_nal_unit(ctx, unit, &pbc); + else + err = cbs_h265_write_nal_unit(ctx, unit, &pbc); if (err == AVERROR(ENOSPC)) { // Overflow. @@ -927,8 +1295,13 @@ static int cbs_h2645_assemble_fragment(CodedBitstreamContext *ctx, frag->data_bit_padding = unit->data_bit_padding; } - if (unit->type == H264_NAL_SPS || - unit->type == H264_NAL_PPS || + if ((ctx->codec->codec_id == AV_CODEC_ID_H264 && + (unit->type == H264_NAL_SPS || + unit->type == H264_NAL_PPS)) || + (ctx->codec->codec_id == AV_CODEC_ID_HEVC && + (unit->type == HEVC_NAL_VPS || + unit->type == HEVC_NAL_SPS || + unit->type == HEVC_NAL_PPS)) || i == 0 /* (Assume this is the start of an access unit.) */) { // zero_byte data[dp++] = 0; @@ -982,6 +1355,23 @@ static void cbs_h264_close(CodedBitstreamContext *ctx) av_freep(&h264->pps[i]); } +static void cbs_h265_close(CodedBitstreamContext *ctx) +{ + CodedBitstreamH265Context *h265 = ctx->priv_data; + int i; + + ff_h2645_packet_uninit(&h265->common.read_packet); + + av_freep(&h265->common.write_buffer); + + for (i = 0; i < FF_ARRAY_ELEMS(h265->vps); i++) + av_freep(&h265->vps[i]); + for (i = 0; i < FF_ARRAY_ELEMS(h265->sps); i++) + av_freep(&h265->sps[i]); + for (i = 0; i < FF_ARRAY_ELEMS(h265->pps); i++) + av_freep(&h265->pps[i]); +} + const CodedBitstreamType ff_cbs_type_h264 = { .codec_id = AV_CODEC_ID_H264, @@ -995,3 +1385,17 @@ const CodedBitstreamType ff_cbs_type_h264 = { .free_unit = &cbs_h264_free_nal_unit, .close = &cbs_h264_close, }; + +const CodedBitstreamType ff_cbs_type_h265 = { + .codec_id = AV_CODEC_ID_HEVC, + + .priv_data_size = sizeof(CodedBitstreamH265Context), + + .split_fragment = &cbs_h2645_split_fragment, + .read_unit = &cbs_h265_read_nal_unit, + .write_unit = &cbs_h2645_write_nal_unit, + .assemble_fragment = &cbs_h2645_assemble_fragment, + + .free_unit = &cbs_h265_free_nal_unit, + .close = &cbs_h265_close, +}; diff --git a/libavcodec/cbs_h265.h b/libavcodec/cbs_h265.h new file mode 100644 index 0000000000..6ee0725933 --- /dev/null +++ b/libavcodec/cbs_h265.h @@ -0,0 +1,537 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef AVCODEC_CBS_H265_H +#define AVCODEC_CBS_H265_H + +#include +#include + +#include "cbs_h2645.h" +#include "hevc.h" + + +typedef struct H265RawNALUnitHeader { + uint8_t forbidden_zero_bit; + uint8_t nal_unit_type; + uint8_t nuh_layer_id; + uint8_t nuh_temporal_id_plus1; +} H265RawNALUnitHeader; + +typedef struct H265RawProfileTierLevel { + uint8_t general_profile_space; + uint8_t general_tier_flag; + uint8_t general_profile_idc; + + uint8_t general_profile_compatibility_flag[32]; + + uint8_t general_progressive_source_flag; + uint8_t general_interlaced_source_flag; + uint8_t general_non_packed_constraint_flag; + uint8_t general_frame_only_constraint_flag; + + uint8_t general_max_12bit_constraint_flag; + uint8_t general_max_10bit_constraint_flag; + uint8_t general_max_8bit_constraint_flag; + uint8_t general_max_422chroma_constraint_flag; + uint8_t general_max_420chroma_constraint_flag; + uint8_t general_max_monochrome_constraint_flag; + uint8_t general_intra_constraint_flag; + uint8_t general_one_picture_only_constraint_flag; + uint8_t general_lower_bit_rate_constraint_flag; + uint8_t general_max_14bit_constraint_flag; + + uint8_t general_inbld_flag; + + uint8_t general_level_idc; + + uint8_t sub_layer_profile_present_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t sub_layer_level_present_flag[HEVC_MAX_SUB_LAYERS]; + + // TODO: much of that again for each sub-layer. +} H265RawProfileTierLevel; + +typedef struct H265RawSubLayerHRDParameters { + uint32_t bit_rate_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t cpb_size_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t cpb_size_du_value_minus1[HEVC_MAX_CPB_CNT]; + uint32_t bit_rate_du_value_minus1[HEVC_MAX_CPB_CNT]; + uint8_t cbr_flag[HEVC_MAX_CPB_CNT]; +} H265RawSubLayerHRDParameters; + +typedef struct H265RawHRDParameters { + uint8_t nal_hrd_parameters_present_flag; + uint8_t vcl_hrd_parameters_present_flag; + + uint8_t sub_pic_hrd_params_present_flag; + uint8_t tick_divisor_minus2; + uint8_t du_cpb_removal_delay_increment_length_minus1; + uint8_t sub_pic_cpb_params_in_pic_timing_sei_flag; + uint8_t dpb_output_delay_du_length_minus1; + + uint8_t bit_rate_scale; + uint8_t cpb_size_scale; + uint8_t cpb_size_du_scale; + + uint8_t initial_cpb_removal_delay_length_minus1; + uint8_t au_cpb_removal_delay_length_minus1; + uint8_t dpb_output_delay_length_minus1; + + uint8_t fixed_pic_rate_general_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t fixed_pic_rate_within_cvs_flag[HEVC_MAX_SUB_LAYERS]; + uint16_t elemental_duration_in_tc_minus1[HEVC_MAX_SUB_LAYERS]; + uint8_t low_delay_hrd_flag[HEVC_MAX_SUB_LAYERS]; + uint8_t cpb_cnt_minus1[HEVC_MAX_SUB_LAYERS]; + H265RawSubLayerHRDParameters nal_sub_layer_hrd_parameters[HEVC_MAX_SUB_LAYERS]; + H265RawSubLayerHRDParameters vcl_sub_layer_hrd_parameters[HEVC_MAX_SUB_LAYERS]; +} H265RawHRDParameters; + +typedef struct H265RawVUI { + uint8_t aspect_ratio_info_present_flag; + uint8_t aspect_ratio_idc; + uint16_t sar_width; + uint16_t sar_height; + + uint8_t overscan_info_present_flag; + uint8_t overscan_appropriate_flag; + + uint8_t video_signal_type_present_flag; + uint8_t video_format; + uint8_t video_full_range_flag; + uint8_t colour_description_present_flag; + uint8_t colour_primaries; + uint8_t transfer_characteristics; + uint8_t matrix_coefficients; + + uint8_t chroma_loc_info_present_flag; + uint8_t chroma_sample_loc_type_top_field; + uint8_t chroma_sample_loc_type_bottom_field; + + uint8_t neutral_chroma_indication_flag; + uint8_t field_seq_flag; + uint8_t frame_field_info_present_flag; + + uint8_t default_display_window_flag; + uint16_t def_disp_win_left_offset; + uint16_t def_disp_win_right_offset; + uint16_t def_disp_win_top_offset; + uint16_t def_disp_win_bottom_offset; + + uint8_t vui_timing_info_present_flag; + uint32_t vui_num_units_in_tick; + uint32_t vui_time_scale; + uint8_t vui_poc_proportional_to_timing_flag; + uint32_t vui_num_ticks_poc_diff_one_minus1; + uint8_t vui_hrd_parameters_present_flag; + H265RawHRDParameters hrd_parameters; + + uint8_t bitstream_restriction_flag; + uint8_t tiles_fixed_structure_flag; + uint8_t motion_vectors_over_pic_boundaries_flag; + uint8_t restricted_ref_pic_lists_flag; + uint16_t min_spatial_segmentation_idc; + uint8_t max_bytes_per_pic_denom; + uint8_t max_bits_per_min_cu_denom; + uint8_t log2_max_mv_length_horizontal; + uint8_t log2_max_mv_length_vertical; +} H265RawVUI; + +typedef struct H265RawPSExtensionData { + uint8_t *data; + size_t bit_length; +} H265RawPSExtensionData; + +typedef struct H265RawVPS { + H265RawNALUnitHeader nal_unit_header; + + uint8_t vps_video_parameter_set_id; + + uint8_t vps_base_layer_internal_flag; + uint8_t vps_base_layer_available_flag; + uint8_t vps_max_layers_minus1; + uint8_t vps_max_sub_layers_minus1; + uint8_t vps_temporal_id_nesting_flag; + + H265RawProfileTierLevel profile_tier_level; + + uint8_t vps_sub_layer_ordering_info_present_flag; + uint8_t vps_max_dec_pic_buffering_minus1[HEVC_MAX_SUB_LAYERS]; + uint8_t vps_max_num_reorder_pics[HEVC_MAX_SUB_LAYERS]; + uint32_t vps_max_latency_increase_plus1[HEVC_MAX_SUB_LAYERS]; + + uint8_t vps_max_layer_id; + uint16_t vps_num_layer_sets_minus1; + uint8_t layer_id_included_flag[HEVC_MAX_LAYER_SETS][HEVC_MAX_LAYERS]; + + uint8_t vps_timing_info_present_flag; + uint32_t vps_num_units_in_tick; + uint32_t vps_time_scale; + uint8_t vps_poc_proportional_to_timing_flag; + uint32_t vps_num_ticks_poc_diff_one_minus1; + uint16_t vps_num_hrd_parameters; + uint16_t hrd_layer_set_idx[HEVC_MAX_LAYER_SETS]; + uint8_t cprms_present_flag[HEVC_MAX_LAYER_SETS]; + H265RawHRDParameters hrd_parameters[HEVC_MAX_LAYER_SETS]; + + uint8_t vps_extension_flag; + H265RawPSExtensionData extension_data; +} H265RawVPS; + +typedef struct H265RawSTRefPicSet { + uint8_t inter_ref_pic_set_prediction_flag; + + uint8_t delta_idx_minus1; + uint8_t delta_rps_sign; + uint16_t abs_delta_rps_minus1; + + uint8_t used_by_curr_pic_flag[HEVC_MAX_REFS]; + uint8_t use_delta_flag[HEVC_MAX_REFS]; + + uint8_t num_negative_pics; + uint8_t num_positive_pics; + uint16_t delta_poc_s0_minus1[HEVC_MAX_REFS]; + uint8_t used_by_curr_pic_s0_flag[HEVC_MAX_REFS]; + uint16_t delta_poc_s1_minus1[HEVC_MAX_REFS]; + uint8_t used_by_curr_pic_s1_flag[HEVC_MAX_REFS]; +} H265RawSTRefPicSet; + +typedef struct H265RawScalingList { + uint8_t scaling_list_pred_mode_flag[4][6]; + uint8_t scaling_list_pred_matrix_id_delta[4][6]; + int16_t scaling_list_dc_coef_minus8[4][6]; + int8_t scaling_list_delta_coeff[4][6][64]; +} H265RawScalingList; + +typedef struct H265RawSPS { + H265RawNALUnitHeader nal_unit_header; + + uint8_t sps_video_parameter_set_id; + + uint8_t sps_max_sub_layers_minus1; + uint8_t sps_temporal_id_nesting_flag; + + H265RawProfileTierLevel profile_tier_level; + + uint8_t sps_seq_parameter_set_id; + + uint8_t chroma_format_idc; + uint8_t separate_colour_plane_flag; + + uint16_t pic_width_in_luma_samples; + uint16_t pic_height_in_luma_samples; + + uint8_t conformance_window_flag; + uint16_t conf_win_left_offset; + uint16_t conf_win_right_offset; + uint16_t conf_win_top_offset; + uint16_t conf_win_bottom_offset; + + uint8_t bit_depth_luma_minus8; + uint8_t bit_depth_chroma_minus8; + + uint8_t log2_max_pic_order_cnt_lsb_minus4; + + uint8_t sps_sub_layer_ordering_info_present_flag; + uint8_t sps_max_dec_pic_buffering_minus1[HEVC_MAX_SUB_LAYERS]; + uint8_t sps_max_num_reorder_pics[HEVC_MAX_SUB_LAYERS]; + uint32_t sps_max_latency_increase_plus1[HEVC_MAX_SUB_LAYERS]; + + uint8_t log2_min_luma_coding_block_size_minus3; + uint8_t log2_diff_max_min_luma_coding_block_size; + uint8_t log2_min_luma_transform_block_size_minus2; + uint8_t log2_diff_max_min_luma_transform_block_size; + uint8_t max_transform_hierarchy_depth_inter; + uint8_t max_transform_hierarchy_depth_intra; + + uint8_t scaling_list_enabled_flag; + uint8_t sps_scaling_list_data_present_flag; + H265RawScalingList scaling_list; + + uint8_t amp_enabled_flag; + uint8_t sample_adaptive_offset_enabled_flag; + + uint8_t pcm_enabled_flag; + uint8_t pcm_sample_bit_depth_luma_minus1; + uint8_t pcm_sample_bit_depth_chroma_minus1; + uint8_t log2_min_pcm_luma_coding_block_size_minus3; + uint8_t log2_diff_max_min_pcm_luma_coding_block_size; + uint8_t pcm_loop_filter_disabled_flag; + + uint8_t num_short_term_ref_pic_sets; + H265RawSTRefPicSet st_ref_pic_set[HEVC_MAX_SHORT_TERM_REF_PIC_SETS]; + + uint8_t long_term_ref_pics_present_flag; + uint8_t num_long_term_ref_pics_sps; + uint16_t lt_ref_pic_poc_lsb_sps[HEVC_MAX_LONG_TERM_REF_PICS]; + uint8_t used_by_curr_pic_lt_sps_flag[HEVC_MAX_LONG_TERM_REF_PICS]; + + uint8_t sps_temporal_mvp_enabled_flag; + uint8_t strong_intra_smoothing_enabled_flag; + + uint8_t vui_parameters_present_flag; + H265RawVUI vui; + + uint8_t sps_extension_present_flag; + uint8_t sps_range_extension_flag; + uint8_t sps_multilayer_extension_flag; + uint8_t sps_3d_extension_flag; + uint8_t sps_scc_extension_flag; + uint8_t sps_extension_4bits; + + H265RawPSExtensionData extension_data; + + // Range extension. + uint8_t transform_skip_rotation_enabled_flag; + uint8_t transform_skip_context_enabled_flag; + uint8_t implicit_rdpcm_enabled_flag; + uint8_t explicit_rdpcm_enabled_flag; + uint8_t extended_precision_processing_flag; + uint8_t intra_smoothing_disabled_flag; + uint8_t high_precision_offsets_enabled_flag; + uint8_t persistent_rice_adaptation_enabled_flag; + uint8_t cabac_bypass_alignment_enabled_flag; + + // Screen content coding extension. + uint8_t sps_curr_pic_ref_enabled_flag; + uint8_t palette_mode_enabled_flag; + uint8_t palette_max_size; + uint8_t delta_palette_max_predictor_size; + uint8_t sps_palette_predictor_initializer_present_flag; + uint8_t sps_num_palette_predictor_initializer_minus1; + uint16_t sps_palette_predictor_initializers[3][128]; + + uint8_t motion_vector_resolution_control_idc; + uint8_t intra_boundary_filtering_disable_flag; +} H265RawSPS; + +typedef struct H265RawPPS { + H265RawNALUnitHeader nal_unit_header; + + uint8_t pps_pic_parameter_set_id; + uint8_t pps_seq_parameter_set_id; + + uint8_t dependent_slice_segments_enabled_flag; + uint8_t output_flag_present_flag; + uint8_t num_extra_slice_header_bits; + uint8_t sign_data_hiding_enabled_flag; + uint8_t cabac_init_present_flag; + + uint8_t num_ref_idx_l0_default_active_minus1; + uint8_t num_ref_idx_l1_default_active_minus1; + + int8_t init_qp_minus26; + + uint8_t constrained_intra_pred_flag; + uint8_t transform_skip_enabled_flag; + uint8_t cu_qp_delta_enabled_flag; + uint8_t diff_cu_qp_delta_depth; + + int8_t pps_cb_qp_offset; + int8_t pps_cr_qp_offset; + uint8_t pps_slice_chroma_qp_offsets_present_flag; + + uint8_t weighted_pred_flag; + uint8_t weighted_bipred_flag; + + uint8_t transquant_bypass_enabled_flag; + uint8_t tiles_enabled_flag; + uint8_t entropy_coding_sync_enabled_flag; + + uint8_t num_tile_columns_minus1; + uint8_t num_tile_rows_minus1; + uint8_t uniform_spacing_flag; + uint16_t column_width_minus1[HEVC_MAX_TILE_COLUMNS]; + uint16_t row_height_minus1[HEVC_MAX_TILE_ROWS]; + uint8_t loop_filter_across_tiles_enabled_flag; + + uint8_t pps_loop_filter_across_slices_enabled_flag; + uint8_t deblocking_filter_control_present_flag; + uint8_t deblocking_filter_override_enabled_flag; + uint8_t pps_deblocking_filter_disabled_flag; + int8_t pps_beta_offset_div2; + int8_t pps_tc_offset_div2; + + uint8_t pps_scaling_list_data_present_flag; + H265RawScalingList scaling_list; + + uint8_t lists_modification_present_flag; + uint8_t log2_parallel_merge_level_minus2; + + uint8_t slice_segment_header_extension_present_flag; + + uint8_t pps_extension_present_flag; + uint8_t pps_range_extension_flag; + uint8_t pps_multilayer_extension_flag; + uint8_t pps_3d_extension_flag; + uint8_t pps_scc_extension_flag; + uint8_t pps_extension_4bits; + + H265RawPSExtensionData extension_data; + + // Range extension. + uint8_t log2_max_transform_skip_block_size_minus2; + uint8_t cross_component_prediction_enabled_flag; + uint8_t chroma_qp_offset_list_enabled_flag; + uint8_t diff_cu_chroma_qp_offset_depth; + uint8_t chroma_qp_offset_list_len_minus1; + int8_t cb_qp_offset_list[6]; + int8_t cr_qp_offset_list[6]; + uint8_t log2_sao_offset_scale_luma; + uint8_t log2_sao_offset_scale_chroma; + + // Screen content coding extension. + uint8_t pps_curr_pic_ref_enabled_flag; + uint8_t residual_adaptive_colour_transform_enabled_flag; + uint8_t pps_slice_act_qp_offsets_present_flag; + int8_t pps_act_y_qp_offset_plus5; + int8_t pps_act_cb_qp_offset_plus5; + int8_t pps_act_cr_qp_offset_plus3; + + uint8_t pps_palette_predictor_initializer_present_flag; + uint8_t pps_num_palette_predictor_initializer; + uint8_t monochrome_palette_flag; + uint8_t luma_bit_depth_entry_minus8; + uint8_t chroma_bit_depth_entry_minus8; + uint16_t pps_palette_predictor_initializers[3][128]; +} H265RawPPS; + +typedef struct H265RawAUD { + H265RawNALUnitHeader nal_unit_header; + + uint8_t pic_type; +} H265RawAUD; + +typedef struct H265RawSliceHeader { + H265RawNALUnitHeader nal_unit_header; + + uint8_t first_slice_segment_in_pic_flag; + uint8_t no_output_of_prior_pics_flag; + uint8_t slice_pic_parameter_set_id; + + uint8_t dependent_slice_segment_flag; + uint16_t slice_segment_address; + + uint8_t slice_reserved_flag[8]; + uint8_t slice_type; + + uint8_t pic_output_flag; + uint8_t colour_plane_id; + + uint16_t slice_pic_order_cnt_lsb; + + uint8_t short_term_ref_pic_set_sps_flag; + H265RawSTRefPicSet short_term_ref_pic_set; + uint8_t short_term_ref_pic_set_idx; + + uint8_t num_long_term_sps; + uint8_t num_long_term_pics; + uint8_t lt_idx_sps[HEVC_MAX_REFS]; + uint8_t poc_lsb_lt[HEVC_MAX_REFS]; + uint8_t used_by_curr_pic_lt_flag[HEVC_MAX_REFS]; + uint8_t delta_poc_msb_present_flag[HEVC_MAX_REFS]; + uint32_t delta_poc_msb_cycle_lt[HEVC_MAX_REFS]; + + uint8_t slice_temporal_mvp_enabled_flag; + + uint8_t slice_sao_luma_flag; + uint8_t slice_sao_chroma_flag; + + uint8_t num_ref_idx_active_override_flag; + uint8_t num_ref_idx_l0_active_minus1; + uint8_t num_ref_idx_l1_active_minus1; + + uint8_t ref_pic_list_modification_flag_l0; + uint8_t list_entry_l0[HEVC_MAX_REFS]; + uint8_t ref_pic_list_modification_flag_l1; + uint8_t list_entry_l1[HEVC_MAX_REFS]; + + uint8_t mvd_l1_zero_flag; + uint8_t cabac_init_flag; + uint8_t collocated_from_l0_flag; + uint8_t collocated_ref_idx; + + uint8_t luma_log2_weight_denom; + int8_t delta_chroma_log2_weight_denom; + uint8_t luma_weight_l0_flag[HEVC_MAX_REFS]; + uint8_t chroma_weight_l0_flag[HEVC_MAX_REFS]; + int8_t delta_luma_weight_l0[HEVC_MAX_REFS]; + int16_t luma_offset_l0[HEVC_MAX_REFS]; + int8_t delta_chroma_weight_l0[HEVC_MAX_REFS][2]; + int16_t chroma_offset_l0[HEVC_MAX_REFS][2]; + uint8_t luma_weight_l1_flag[HEVC_MAX_REFS]; + uint8_t chroma_weight_l1_flag[HEVC_MAX_REFS]; + int8_t delta_luma_weight_l1[HEVC_MAX_REFS]; + int16_t luma_offset_l1[HEVC_MAX_REFS]; + int8_t delta_chroma_weight_l1[HEVC_MAX_REFS][2]; + int16_t chroma_offset_l1[HEVC_MAX_REFS][2]; + + uint8_t five_minus_max_num_merge_cand; + uint8_t use_integer_mv_flag; + + int8_t slice_qp_delta; + int8_t slice_cb_qp_offset; + int8_t slice_cr_qp_offset; + int8_t slice_act_y_qp_offset; + int8_t slice_act_cb_qp_offset; + int8_t slice_act_cr_qp_offset; + uint8_t cu_chroma_qp_offset_enabled_flag; + + uint8_t deblocking_filter_override_flag; + uint8_t slice_deblocking_filter_disabled_flag; + int8_t slice_beta_offset_div2; + int8_t slice_tc_offset_div2; + uint8_t slice_loop_filter_across_slices_enabled_flag; + + uint16_t num_entry_point_offsets; + uint8_t offset_len_minus1; + uint32_t entry_point_offset_minus1[HEVC_MAX_ENTRY_POINT_OFFSETS]; + + uint16_t slice_segment_header_extension_length; + uint8_t slice_segment_header_extension_data_byte[256]; +} H265RawSliceHeader; + + +typedef struct H265RawSlice { + H265RawSliceHeader header; + + uint8_t *data; + size_t data_size; + int data_bit_start; +} H265RawSlice; + + +typedef struct CodedBitstreamH265Context { + // Reader/writer context in common with the H.264 implementation. + CodedBitstreamH2645Context common; + + // All currently available parameter sets. These are updated when + // any parameter set NAL unit is read/written with this context. + H265RawVPS *vps[HEVC_MAX_VPS_COUNT]; + H265RawSPS *sps[HEVC_MAX_SPS_COUNT]; + H265RawPPS *pps[HEVC_MAX_PPS_COUNT]; + + // The currently active parameter sets. These are updated when any + // NAL unit refers to the relevant parameter set. These pointers + // must also be present in the arrays above. + const H265RawVPS *active_vps; + const H265RawSPS *active_sps; + const H265RawPPS *active_pps; +} CodedBitstreamH265Context; + + +#endif /* AVCODEC_CBS_H265_H */ diff --git a/libavcodec/cbs_h265_syntax_template.c b/libavcodec/cbs_h265_syntax_template.c new file mode 100644 index 0000000000..40fdaf8a90 --- /dev/null +++ b/libavcodec/cbs_h265_syntax_template.c @@ -0,0 +1,1502 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +static int FUNC(rbsp_trailing_bits)(CodedBitstreamContext *ctx, RWContext *rw) +{ + int err; + av_unused int one = 1, zero = 0; + xu(1, rbsp_stop_one_bit, one, 1, 1); + while (byte_alignment(rw) != 0) + xu(1, rbsp_alignment_zero_bit, zero, 0, 0); + + return 0; +} + +static int FUNC(nal_unit_header)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawNALUnitHeader *current, + int expected_nal_unit_type) +{ + int err; + + u(1, forbidden_zero_bit, 0, 0); + + if (expected_nal_unit_type >= 0) + u(6, nal_unit_type, expected_nal_unit_type, + expected_nal_unit_type); + else + u(6, nal_unit_type, 0, 63); + + u(6, nuh_layer_id, 0, 62); + u(3, nuh_temporal_id_plus1, 1, 7); + + return 0; +} + +static int FUNC(byte_alignment)(CodedBitstreamContext *ctx, RWContext *rw) +{ + int err; + av_unused int one = 1, zero = 0; + xu(1, alignment_bit_equal_to_one, one, 1, 1); + while (byte_alignment(rw) != 0) + xu(1, alignment_bit_equal_to_zero, zero, 0, 0); + + return 0; +} + +static int FUNC(extension_data)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawPSExtensionData *current) +{ + int err; + size_t k; +#ifdef READ + BitstreamContext start; + uint8_t bit; + start = *rw; + for (k = 0; cbs_h2645_read_more_rbsp_data(rw); k++); + current->bit_length = k; + if (k > 0) { + *rw = start; + allocate(current->data, (current->bit_length + 7) / 8); + for (k = 0; k < current->bit_length; k++) { + xu(1, extension_data, bit, 0, 1); + current->data[k / 8] |= bit << (7 - k % 8); + } + } +#else + for (k = 0; k < current->bit_length; k++) + xu(1, extension_data, current->data[k / 8] >> (7 - k % 8), 0, 1); +#endif + return 0; +} + +static int FUNC(profile_tier_level)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawProfileTierLevel *current, + int profile_present_flag, + int max_num_sub_layers_minus1) +{ + av_unused unsigned int zero = 0; + int err, i, j; + + if (profile_present_flag) { + u(2, general_profile_space, 0, 0); + flag(general_tier_flag); + u(5, general_profile_idc, 0, 31); + + for (j = 0; j < 32; j++) + flag(general_profile_compatibility_flag[j]); + + flag(general_progressive_source_flag); + flag(general_interlaced_source_flag); + flag(general_non_packed_constraint_flag); + flag(general_frame_only_constraint_flag); + +#define profile_compatible(x) (current->general_profile_idc == (x) || \ + current->general_profile_compatibility_flag[x]) + if (profile_compatible(4) || profile_compatible(5) || + profile_compatible(6) || profile_compatible(7) || + profile_compatible(8) || profile_compatible(9) || + profile_compatible(10)) { + flag(general_max_12bit_constraint_flag); + flag(general_max_10bit_constraint_flag); + flag(general_max_8bit_constraint_flag); + flag(general_max_422chroma_constraint_flag); + flag(general_max_420chroma_constraint_flag); + flag(general_max_monochrome_constraint_flag); + flag(general_intra_constraint_flag); + flag(general_one_picture_only_constraint_flag); + flag(general_lower_bit_rate_constraint_flag); + + if (profile_compatible(5) || profile_compatible(9) || + profile_compatible(10)) { + flag(general_max_14bit_constraint_flag); + xu(24, general_reserved_zero_33bits, zero, 0, 0); + xu(9, general_reserved_zero_33bits, zero, 0, 0); + } else { + xu(24, general_reserved_zero_34bits, zero, 0, 0); + xu(10, general_reserved_zero_34bits, zero, 0, 0); + } + } else { + xu(24, general_reserved_zero_43bits, zero, 0, 0); + xu(19, general_reserved_zero_43bits, zero, 0, 0); + } + + if (profile_compatible(1) || profile_compatible(2) || + profile_compatible(3) || profile_compatible(4) || + profile_compatible(5) || profile_compatible(9)) { + flag(general_inbld_flag); + } else { + xu(1, general_reserved_zero_bit, zero, 0, 0); + } +#undef profile_compatible + } + + u(8, general_level_idc, 0, 255); + + for (i = 0; i < max_num_sub_layers_minus1; i++) { + flag(sub_layer_profile_present_flag[i]); + flag(sub_layer_level_present_flag[i]); + } + + if (max_num_sub_layers_minus1 > 0) { + for (i = max_num_sub_layers_minus1; i < 8; i++) { + av_unused int zero = 0; + xu(2, reserved_zero_2bits, zero, 0, 0); + } + } + + for (i = 0; i < max_num_sub_layers_minus1; i++) { + if (current->sub_layer_profile_present_flag[i]) + return AVERROR_PATCHWELCOME; + if (current->sub_layer_level_present_flag[i]) + return AVERROR_PATCHWELCOME; + } + + return 0; +} + +static int FUNC(sub_layer_hrd_parameters)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawHRDParameters *hrd, + int nal, int sub_layer_id) +{ + H265RawSubLayerHRDParameters *current; + int err, i; + + if (nal) + current = &hrd->nal_sub_layer_hrd_parameters[sub_layer_id]; + else + current = &hrd->vcl_sub_layer_hrd_parameters[sub_layer_id]; + + for (i = 0; i <= hrd->cpb_cnt_minus1[sub_layer_id]; i++) { + ue(bit_rate_value_minus1[i], 0, UINT32_MAX - 1); + ue(cpb_size_value_minus1[i], 0, UINT32_MAX - 1); + if (hrd->sub_pic_hrd_params_present_flag) { + ue(cpb_size_du_value_minus1[i], 0, UINT32_MAX - 1); + ue(bit_rate_du_value_minus1[i], 0, UINT32_MAX - 1); + } + flag(cbr_flag[i]); + } + + return 0; +} + +static int FUNC(hrd_parameters)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawHRDParameters *current, int common_inf_present_flag, + int max_num_sub_layers_minus1) +{ + int err, i; + + if (common_inf_present_flag) { + flag(nal_hrd_parameters_present_flag); + flag(vcl_hrd_parameters_present_flag); + + if (current->nal_hrd_parameters_present_flag || + current->vcl_hrd_parameters_present_flag) { + flag(sub_pic_hrd_params_present_flag); + if (current->sub_pic_hrd_params_present_flag) { + u(8, tick_divisor_minus2, 0, 255); + u(5, du_cpb_removal_delay_increment_length_minus1, 0, 31); + flag(sub_pic_cpb_params_in_pic_timing_sei_flag); + u(5, dpb_output_delay_du_length_minus1, 0, 31); + } + + u(4, bit_rate_scale, 0, 15); + u(4, cpb_size_scale, 0, 15); + if (current->sub_pic_hrd_params_present_flag) + u(4, cpb_size_du_scale, 0, 15); + + u(5, initial_cpb_removal_delay_length_minus1, 0, 31); + u(5, au_cpb_removal_delay_length_minus1, 0, 31); + u(5, dpb_output_delay_length_minus1, 0, 31); + } else { + infer(sub_pic_hrd_params_present_flag, 0); + + infer(initial_cpb_removal_delay_length_minus1, 23); + infer(au_cpb_removal_delay_length_minus1, 23); + infer(dpb_output_delay_length_minus1, 23); + } + } + + for (i = 0; i <= max_num_sub_layers_minus1; i++) { + flag(fixed_pic_rate_general_flag[i]); + + if (!current->fixed_pic_rate_general_flag[i]) + flag(fixed_pic_rate_within_cvs_flag[i]); + else + infer(fixed_pic_rate_within_cvs_flag[i], 1); + + if (current->fixed_pic_rate_within_cvs_flag[i]) { + ue(elemental_duration_in_tc_minus1[i], 0, 2047); + infer(low_delay_hrd_flag[i], 0); + } else + flag(low_delay_hrd_flag[i]); + + if (!current->low_delay_hrd_flag[i]) + ue(cpb_cnt_minus1[i], 0, 31); + else + infer(cpb_cnt_minus1[i], 0); + + if (current->nal_hrd_parameters_present_flag) + CHECK(FUNC(sub_layer_hrd_parameters)(ctx, rw, current, 0, i)); + if (current->vcl_hrd_parameters_present_flag) + CHECK(FUNC(sub_layer_hrd_parameters)(ctx, rw, current, 1, i)); + } + + return 0; +} + +static int FUNC(vui_parameters)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawVUI *current, const H265RawSPS *sps) +{ + int err; + + flag(aspect_ratio_info_present_flag); + if (current->aspect_ratio_info_present_flag) { + u(8, aspect_ratio_idc, 0, 255); + if (current->aspect_ratio_idc == 255) { + u(16, sar_width, 0, 65535); + u(16, sar_height, 0, 65535); + } + } else { + infer(aspect_ratio_idc, 0); + } + + flag(overscan_info_present_flag); + if (current->overscan_info_present_flag) + flag(overscan_appropriate_flag); + + flag(video_signal_type_present_flag); + if (current->video_signal_type_present_flag) { + u(3, video_format, 0, 7); + flag(video_full_range_flag); + flag(colour_description_present_flag); + if (current->colour_description_present_flag) { + u(8, colour_primaries, 0, 255); + u(8, transfer_characteristics, 0, 255); + u(8, matrix_coefficients, 0, 255); + } else { + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + } + } else { + infer(video_format, 5); + infer(video_full_range_flag, 0); + infer(colour_primaries, 2); + infer(transfer_characteristics, 2); + infer(matrix_coefficients, 2); + } + + flag(chroma_loc_info_present_flag); + if (current->chroma_loc_info_present_flag) { + ue(chroma_sample_loc_type_top_field, 0, 5); + ue(chroma_sample_loc_type_bottom_field, 0, 5); + } else { + infer(chroma_sample_loc_type_top_field, 0); + infer(chroma_sample_loc_type_bottom_field, 0); + } + + flag(neutral_chroma_indication_flag); + flag(field_seq_flag); + flag(frame_field_info_present_flag); + + flag(default_display_window_flag); + if (current->default_display_window_flag) { + ue(def_disp_win_left_offset, 0, 16384); + ue(def_disp_win_right_offset, 0, 16384); + ue(def_disp_win_top_offset, 0, 16384); + ue(def_disp_win_bottom_offset, 0, 16384); + } + + flag(vui_timing_info_present_flag); + if (current->vui_timing_info_present_flag) { + u(32, vui_num_units_in_tick, 1, UINT32_MAX); + u(32, vui_time_scale, 1, UINT32_MAX); + flag(vui_poc_proportional_to_timing_flag); + if (current->vui_poc_proportional_to_timing_flag) + ue(vui_num_ticks_poc_diff_one_minus1, 0, UINT32_MAX - 1); + + flag(vui_hrd_parameters_present_flag); + if (current->vui_hrd_parameters_present_flag) { + CHECK(FUNC(hrd_parameters)(ctx, rw, ¤t->hrd_parameters, + 1, sps->sps_max_sub_layers_minus1)); + } + } + + flag(bitstream_restriction_flag); + if (current->bitstream_restriction_flag) { + flag(tiles_fixed_structure_flag); + flag(motion_vectors_over_pic_boundaries_flag); + flag(restricted_ref_pic_lists_flag); + ue(min_spatial_segmentation_idc, 0, 4095); + ue(max_bytes_per_pic_denom, 0, 16); + ue(max_bits_per_min_cu_denom, 0, 16); + ue(log2_max_mv_length_horizontal, 0, 16); + ue(log2_max_mv_length_vertical, 0, 16); + } else { + infer(tiles_fixed_structure_flag, 0); + infer(motion_vectors_over_pic_boundaries_flag, 1); + infer(min_spatial_segmentation_idc, 0); + infer(max_bytes_per_pic_denom, 2); + infer(max_bits_per_min_cu_denom, 1); + infer(log2_max_mv_length_horizontal, 15); + infer(log2_max_mv_length_vertical, 15); + } + + return 0; +} + +static int FUNC(vps)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawVPS *current) +{ + int err, i, j; + + HEADER("Video Parameter Set"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, HEVC_NAL_VPS)); + + u(4, vps_video_parameter_set_id, 0, 15); + + flag(vps_base_layer_internal_flag); + flag(vps_base_layer_available_flag); + u(6, vps_max_layers_minus1, 0, HEVC_MAX_LAYERS - 1); + u(3, vps_max_sub_layers_minus1, 0, HEVC_MAX_SUB_LAYERS - 1); + flag(vps_temporal_id_nesting_flag); + + if (current->vps_max_sub_layers_minus1 == 0 && + current->vps_temporal_id_nesting_flag != 1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid stream: " + "vps_temporal_id_nesting_flag must be 1 if " + "vps_max_sub_layers_minus1 is 0.\n"); + return AVERROR_INVALIDDATA; + } + + { + av_unused uint16_t ffff = 0xffff; + xu(16, vps_reserved_0xffff_16bits, ffff, 0xffff, 0xffff); + } + + CHECK(FUNC(profile_tier_level)(ctx, rw, ¤t->profile_tier_level, + 1, current->vps_max_sub_layers_minus1)); + + flag(vps_sub_layer_ordering_info_present_flag); + for (i = (current->vps_sub_layer_ordering_info_present_flag ? + 0 : current->vps_max_sub_layers_minus1); + i <= current->vps_max_sub_layers_minus1; i++) { + ue(vps_max_dec_pic_buffering_minus1[i], 0, HEVC_MAX_DPB_SIZE - 1); + ue(vps_max_num_reorder_pics[i], 0, current->vps_max_dec_pic_buffering_minus1[i]); + ue(vps_max_latency_increase_plus1[i], 0, UINT32_MAX - 1); + } + if (!current->vps_sub_layer_ordering_info_present_flag) { + for (i = 0; i < current->vps_max_sub_layers_minus1; i++) { + infer(vps_max_dec_pic_buffering_minus1[i], + current->vps_max_dec_pic_buffering_minus1[current->vps_max_sub_layers_minus1]); + infer(vps_max_num_reorder_pics[i], + current->vps_max_num_reorder_pics[current->vps_max_sub_layers_minus1]); + infer(vps_max_latency_increase_plus1[i], + current->vps_max_latency_increase_plus1[current->vps_max_sub_layers_minus1]); + } + } + + u(6, vps_max_layer_id, 0, HEVC_MAX_LAYERS - 1); + ue(vps_num_layer_sets_minus1, 0, HEVC_MAX_LAYER_SETS - 1); + for (i = 1; i <= current->vps_num_layer_sets_minus1; i++) { + for (j = 0; j <= current->vps_max_layer_id; j++) + flag(layer_id_included_flag[i][j]); + } + for (j = 0; j <= current->vps_max_layer_id; j++) + infer(layer_id_included_flag[0][j], j == 0); + + flag(vps_timing_info_present_flag); + if (current->vps_timing_info_present_flag) { + u(32, vps_num_units_in_tick, 1, UINT32_MAX); + u(32, vps_time_scale, 1, UINT32_MAX); + flag(vps_poc_proportional_to_timing_flag); + if (current->vps_poc_proportional_to_timing_flag) + ue(vps_num_ticks_poc_diff_one_minus1, 0, UINT32_MAX - 1); + ue(vps_num_hrd_parameters, 0, current->vps_num_layer_sets_minus1 + 1); + for (i = 0; i < current->vps_num_hrd_parameters; i++) { + ue(hrd_layer_set_idx[i], + current->vps_base_layer_internal_flag ? 0 : 1, + current->vps_num_layer_sets_minus1); + if (i > 0) + flag(cprms_present_flag[i]); + else + infer(cprms_present_flag[0], 1); + + CHECK(FUNC(hrd_parameters)(ctx, rw, ¤t->hrd_parameters[i], + current->cprms_present_flag[i], + current->vps_max_sub_layers_minus1)); + } + } + + flag(vps_extension_flag); + if (current->vps_extension_flag) + CHECK(FUNC(extension_data)(ctx, rw, ¤t->extension_data)); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(st_ref_pic_set)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSTRefPicSet *current, int st_rps_idx, + const H265RawSPS *sps) +{ + int err, i, j; + + if (st_rps_idx != 0) + flag(inter_ref_pic_set_prediction_flag); + else + infer(inter_ref_pic_set_prediction_flag, 0); + + if (current->inter_ref_pic_set_prediction_flag) { + unsigned int ref_rps_idx, num_delta_pocs; + const H265RawSTRefPicSet *ref; + int delta_rps, d_poc; + int ref_delta_poc_s0[HEVC_MAX_REFS], ref_delta_poc_s1[HEVC_MAX_REFS]; + int delta_poc_s0[HEVC_MAX_REFS], delta_poc_s1[HEVC_MAX_REFS]; + uint8_t used_by_curr_pic_s0[HEVC_MAX_REFS], + used_by_curr_pic_s1[HEVC_MAX_REFS]; + + if (st_rps_idx == sps->num_short_term_ref_pic_sets) + ue(delta_idx_minus1, 0, st_rps_idx - 1); + else + infer(delta_idx_minus1, 0); + + ref_rps_idx = st_rps_idx - (current->delta_idx_minus1 + 1); + ref = &sps->st_ref_pic_set[ref_rps_idx]; + num_delta_pocs = ref->num_negative_pics + ref->num_positive_pics; + + flag(delta_rps_sign); + ue(abs_delta_rps_minus1, 0, INT16_MAX); + delta_rps = (1 - 2 * current->delta_rps_sign) * + (current->abs_delta_rps_minus1 + 1); + + for (j = 0; j <= num_delta_pocs; j++) { + flag(used_by_curr_pic_flag[j]); + if (!current->used_by_curr_pic_flag[j]) + flag(use_delta_flag[j]); + else + infer(use_delta_flag[j], 1); + } + + // Since the stored form of an RPS here is actually the delta-step + // form used when inter_ref_pic_set_prediction_flag is not set, we + // need to reconstruct that here in order to be able to refer to + // the RPS later (which is required for parsing, because we don't + // even know what syntax elements appear without it). Therefore, + // this code takes the delta-step form of the reference set, turns + // it into the delta-array form, applies the prediction process of + // 7.4.8, converts the result back to the delta-step form, and + // stores that as the current set for future use. Note that the + // inferences here mean that writers using prediction will need + // to fill in the delta-step values correctly as well - since the + // whole RPS prediction process is somewhat overly sophisticated, + // this hopefully forms a useful check for them to ensure their + // predicted form actually matches what was intended rather than + // an onerous additional requirement. + + d_poc = 0; + for (i = 0; i < ref->num_negative_pics; i++) { + d_poc -= ref->delta_poc_s0_minus1[i] + 1; + ref_delta_poc_s0[i] = d_poc; + } + d_poc = 0; + for (i = 0; i < ref->num_positive_pics; i++) { + d_poc += ref->delta_poc_s1_minus1[i] + 1; + ref_delta_poc_s1[i] = d_poc; + } + + i = 0; + for (j = ref->num_positive_pics - 1; j >= 0; j--) { + d_poc = ref_delta_poc_s1[j] + delta_rps; + if (d_poc < 0 && current->use_delta_flag[ref->num_negative_pics + j]) { + delta_poc_s0[i] = d_poc; + used_by_curr_pic_s0[i++] = + current->used_by_curr_pic_flag[ref->num_negative_pics + j]; + } + } + if (delta_rps < 0 && current->use_delta_flag[num_delta_pocs]) { + delta_poc_s0[i] = delta_rps; + used_by_curr_pic_s0[i++] = + current->used_by_curr_pic_flag[num_delta_pocs]; + } + for (j = 0; j < ref->num_negative_pics; j++) { + d_poc = ref_delta_poc_s0[j] + delta_rps; + if (d_poc < 0 && current->use_delta_flag[j]) { + delta_poc_s0[i] = d_poc; + used_by_curr_pic_s0[i++] = current->used_by_curr_pic_flag[j]; + } + } + + infer(num_negative_pics, i); + for (i = 0; i < current->num_negative_pics; i++) { + infer(delta_poc_s0_minus1[i], + -(delta_poc_s0[i] - (i == 0 ? 0 : delta_poc_s0[i - 1])) - 1); + infer(used_by_curr_pic_s0_flag[i], used_by_curr_pic_s0[i]); + } + + i = 0; + for (j = ref->num_negative_pics - 1; j >= 0; j--) { + d_poc = ref_delta_poc_s0[j] + delta_rps; + if (d_poc > 0 && current->use_delta_flag[j]) { + delta_poc_s1[i] = d_poc; + used_by_curr_pic_s1[i++] = current->used_by_curr_pic_flag[j]; + } + } + if (delta_rps > 0 && current->use_delta_flag[num_delta_pocs]) { + delta_poc_s1[i] = delta_rps; + used_by_curr_pic_s1[i++] = + current->used_by_curr_pic_flag[num_delta_pocs]; + } + for (j = 0; j < ref->num_positive_pics; j++) { + d_poc = ref_delta_poc_s1[j] + delta_rps; + if (d_poc > 0 && current->use_delta_flag[ref->num_negative_pics + j]) { + delta_poc_s1[i] = d_poc; + used_by_curr_pic_s1[i++] = + current->used_by_curr_pic_flag[ref->num_negative_pics + j]; + } + } + + infer(num_positive_pics, i); + for (i = 0; i < current->num_positive_pics; i++) { + infer(delta_poc_s1_minus1[i], + delta_poc_s1[i] - (i == 0 ? 0 : delta_poc_s1[i - 1]) - 1); + infer(used_by_curr_pic_s1_flag[i], used_by_curr_pic_s1[i]); + } + + } else { + ue(num_negative_pics, 0, 15); + ue(num_positive_pics, 0, 15 - current->num_negative_pics); + + for (i = 0; i < current->num_negative_pics; i++) { + ue(delta_poc_s0_minus1[i], 0, INT16_MAX); + flag(used_by_curr_pic_s0_flag[i]); + } + + for (i = 0; i < current->num_positive_pics; i++) { + ue(delta_poc_s1_minus1[i], 0, INT16_MAX); + flag(used_by_curr_pic_s1_flag[i]); + } + } + + return 0; +} + +static int FUNC(scaling_list_data)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawScalingList *current) +{ + int sizeId, matrixId; + int err, n, i; + + for (sizeId = 0; sizeId < 4; sizeId++) { + for (matrixId = 0; matrixId < 6; matrixId += (sizeId == 3 ? 3 : 1)) { + flag(scaling_list_pred_mode_flag[sizeId][matrixId]); + if (!current->scaling_list_pred_mode_flag[sizeId][matrixId]) { + ue(scaling_list_pred_matrix_id_delta[sizeId][matrixId], + 0, sizeId == 3 ? matrixId / 3 : matrixId); + } else { + n = FFMIN(64, 1 << (4 + (sizeId << 1))); + if (sizeId > 1) + se(scaling_list_dc_coef_minus8[sizeId - 2][matrixId], -7, +247); + for (i = 0; i < n; i++) { + xse(scaling_list_delta_coeff, + current->scaling_list_delta_coeff[sizeId][matrixId][i], + -128, +127); + } + } + } + } + + return 0; +} + +static int FUNC(sps_range_extension)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSPS *current) +{ + int err; + + flag(transform_skip_rotation_enabled_flag); + flag(transform_skip_context_enabled_flag); + flag(implicit_rdpcm_enabled_flag); + flag(explicit_rdpcm_enabled_flag); + flag(extended_precision_processing_flag); + flag(intra_smoothing_disabled_flag); + flag(high_precision_offsets_enabled_flag); + flag(persistent_rice_adaptation_enabled_flag); + flag(cabac_bypass_alignment_enabled_flag); + + return 0; +} + +static int FUNC(sps_scc_extension)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSPS *current) +{ + int err, comp, i; + + flag(sps_curr_pic_ref_enabled_flag); + + flag(palette_mode_enabled_flag); + if (current->palette_mode_enabled_flag) { + ue(palette_max_size, 0, 64); + ue(delta_palette_max_predictor_size, 0, 128); + + flag(sps_palette_predictor_initializer_present_flag); + if (current->sps_palette_predictor_initializer_present_flag) { + ue(sps_num_palette_predictor_initializer_minus1, 0, 128); + for (comp = 0; comp < (current->chroma_format_idc ? 3 : 1); comp++) { + int bit_depth = comp == 0 ? current->bit_depth_luma_minus8 + 8 + : current->bit_depth_chroma_minus8 + 8; + for (i = 0; i <= current->sps_num_palette_predictor_initializer_minus1; i++) + u(bit_depth, sps_palette_predictor_initializers[comp][i], + 0, (1 << bit_depth) - 1); + } + } + } + + u(2, motion_vector_resolution_control_idc, 0, 2); + flag(intra_boundary_filtering_disable_flag); + + return 0; +} + +static int FUNC(sps)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSPS *current) +{ + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawVPS *vps; + int err, i; + unsigned int min_cb_log2_size_y, ctb_log2_size_y, + min_cb_size_y, min_tb_log2_size_y; + + HEADER("Sequence Parameter Set"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, HEVC_NAL_SPS)); + + u(4, sps_video_parameter_set_id, 0, 15); + h265->active_vps = vps = h265->vps[current->sps_video_parameter_set_id]; + + u(3, sps_max_sub_layers_minus1, 0, HEVC_MAX_SUB_LAYERS - 1); + flag(sps_temporal_id_nesting_flag); + if (vps) { + if (vps->vps_max_sub_layers_minus1 > current->sps_max_sub_layers_minus1) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid stream: " + "sps_max_sub_layers_minus1 (%d) must be less than or equal to " + "vps_max_sub_layers_minus1 (%d).\n", + vps->vps_max_sub_layers_minus1, + current->sps_max_sub_layers_minus1); + return AVERROR_INVALIDDATA; + } + if (vps->vps_temporal_id_nesting_flag && + !current->sps_temporal_id_nesting_flag) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid stream: " + "sps_temporal_id_nesting_flag must be 1 if " + "vps_temporal_id_nesting_flag is 1.\n"); + return AVERROR_INVALIDDATA; + } + } + + CHECK(FUNC(profile_tier_level)(ctx, rw, ¤t->profile_tier_level, + 1, current->sps_max_sub_layers_minus1)); + + ue(sps_seq_parameter_set_id, 0, 15); + + ue(chroma_format_idc, 0, 3); + if (current->chroma_format_idc == 3) + flag(separate_colour_plane_flag); + else + infer(separate_colour_plane_flag, 0); + + ue(pic_width_in_luma_samples, 1, HEVC_MAX_WIDTH); + ue(pic_height_in_luma_samples, 1, HEVC_MAX_HEIGHT); + + flag(conformance_window_flag); + if (current->conformance_window_flag) { + ue(conf_win_left_offset, 0, current->pic_width_in_luma_samples); + ue(conf_win_right_offset, 0, current->pic_width_in_luma_samples); + ue(conf_win_top_offset, 0, current->pic_height_in_luma_samples); + ue(conf_win_bottom_offset, 0, current->pic_height_in_luma_samples); + } else { + infer(conf_win_left_offset, 0); + infer(conf_win_right_offset, 0); + infer(conf_win_top_offset, 0); + infer(conf_win_bottom_offset, 0); + } + + ue(bit_depth_luma_minus8, 0, 8); + ue(bit_depth_chroma_minus8, 0, 8); + + ue(log2_max_pic_order_cnt_lsb_minus4, 0, 12); + + flag(sps_sub_layer_ordering_info_present_flag); + for (i = (current->sps_sub_layer_ordering_info_present_flag ? + 0 : current->sps_max_sub_layers_minus1); + i <= current->sps_max_sub_layers_minus1; i++) { + ue(sps_max_dec_pic_buffering_minus1[i], 0, HEVC_MAX_DPB_SIZE - 1); + ue(sps_max_num_reorder_pics[i], 0, current->sps_max_dec_pic_buffering_minus1[i]); + ue(sps_max_latency_increase_plus1[i], 0, UINT32_MAX - 1); + } + if (!current->sps_sub_layer_ordering_info_present_flag) { + for (i = 0; i < current->sps_max_sub_layers_minus1; i++) { + infer(sps_max_dec_pic_buffering_minus1[i], + current->sps_max_dec_pic_buffering_minus1[current->sps_max_sub_layers_minus1]); + infer(sps_max_num_reorder_pics[i], + current->sps_max_num_reorder_pics[current->sps_max_sub_layers_minus1]); + infer(sps_max_latency_increase_plus1[i], + current->sps_max_latency_increase_plus1[current->sps_max_sub_layers_minus1]); + } + } + + ue(log2_min_luma_coding_block_size_minus3, 0, 3); + min_cb_log2_size_y = current->log2_min_luma_coding_block_size_minus3 + 3; + + ue(log2_diff_max_min_luma_coding_block_size, 0, 3); + ctb_log2_size_y = min_cb_log2_size_y + + current->log2_diff_max_min_luma_coding_block_size; + + min_cb_size_y = 1 << min_cb_log2_size_y; + if (current->pic_width_in_luma_samples % min_cb_size_y || + current->pic_height_in_luma_samples % min_cb_size_y) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Invalid dimensions: %ux%u not divisible " + "by MinCbSizeY = %u.\n", current->pic_width_in_luma_samples, + current->pic_height_in_luma_samples, min_cb_size_y); + return AVERROR_INVALIDDATA; + } + + ue(log2_min_luma_transform_block_size_minus2, 0, min_cb_log2_size_y - 3); + min_tb_log2_size_y = current->log2_min_luma_transform_block_size_minus2 + 2; + + ue(log2_diff_max_min_luma_transform_block_size, + 0, FFMIN(ctb_log2_size_y, 5) - min_tb_log2_size_y); + + ue(max_transform_hierarchy_depth_inter, + 0, ctb_log2_size_y - min_tb_log2_size_y); + ue(max_transform_hierarchy_depth_intra, + 0, ctb_log2_size_y - min_tb_log2_size_y); + + flag(scaling_list_enabled_flag); + if (current->scaling_list_enabled_flag) { + flag(sps_scaling_list_data_present_flag); + if (current->sps_scaling_list_data_present_flag) + CHECK(FUNC(scaling_list_data)(ctx, rw, ¤t->scaling_list)); + } else { + infer(sps_scaling_list_data_present_flag, 0); + } + + flag(amp_enabled_flag); + flag(sample_adaptive_offset_enabled_flag); + + flag(pcm_enabled_flag); + if (current->pcm_enabled_flag) { + u(4, pcm_sample_bit_depth_luma_minus1, + 0, current->bit_depth_luma_minus8 + 8 - 1); + u(4, pcm_sample_bit_depth_chroma_minus1, + 0, current->bit_depth_chroma_minus8 + 8 - 1); + + ue(log2_min_pcm_luma_coding_block_size_minus3, + FFMIN(min_cb_log2_size_y, 5) - 3, FFMIN(ctb_log2_size_y, 5) - 3); + ue(log2_diff_max_min_pcm_luma_coding_block_size, + 0, FFMIN(ctb_log2_size_y, 5) - (current->log2_min_pcm_luma_coding_block_size_minus3 + 3)); + + flag(pcm_loop_filter_disabled_flag); + } + + ue(num_short_term_ref_pic_sets, 0, HEVC_MAX_SHORT_TERM_REF_PIC_SETS); + for (i = 0; i < current->num_short_term_ref_pic_sets; i++) + CHECK(FUNC(st_ref_pic_set)(ctx, rw, ¤t->st_ref_pic_set[i], i, current)); + + flag(long_term_ref_pics_present_flag); + if (current->long_term_ref_pics_present_flag) { + ue(num_long_term_ref_pics_sps, 0, HEVC_MAX_LONG_TERM_REF_PICS); + for (i = 0; i < current->num_long_term_ref_pics_sps; i++) { + u(current->log2_max_pic_order_cnt_lsb_minus4 + 4, + lt_ref_pic_poc_lsb_sps[i], + 0, (1 << (current->log2_max_pic_order_cnt_lsb_minus4 + 4)) - 1); + flag(used_by_curr_pic_lt_sps_flag[i]); + } + } + + flag(sps_temporal_mvp_enabled_flag); + flag(strong_intra_smoothing_enabled_flag); + + flag(vui_parameters_present_flag); + if (current->vui_parameters_present_flag) + CHECK(FUNC(vui_parameters)(ctx, rw, ¤t->vui, current)); + + flag(sps_extension_present_flag); + if (current->sps_extension_present_flag) { + flag(sps_range_extension_flag); + flag(sps_multilayer_extension_flag); + flag(sps_3d_extension_flag); + flag(sps_scc_extension_flag); + u(4, sps_extension_4bits, 0, (1 << 4) - 1); + } + + if (current->sps_range_extension_flag) + CHECK(FUNC(sps_range_extension)(ctx, rw, current)); + if (current->sps_multilayer_extension_flag) + return AVERROR_PATCHWELCOME; + if (current->sps_3d_extension_flag) + return AVERROR_PATCHWELCOME; + if (current->sps_scc_extension_flag) + CHECK(FUNC(sps_scc_extension)(ctx, rw, current)); + if (current->sps_extension_4bits) + CHECK(FUNC(extension_data)(ctx, rw, ¤t->extension_data)); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(pps_range_extension)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawPPS *current) +{ + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps = h265->active_sps; + int err, i; + + if (current->transform_skip_enabled_flag) + ue(log2_max_transform_skip_block_size_minus2, 0, 4); + flag(cross_component_prediction_enabled_flag); + + flag(chroma_qp_offset_list_enabled_flag); + if (current->chroma_qp_offset_list_enabled_flag) { + ue(diff_cu_chroma_qp_offset_depth, + 0, sps->log2_diff_max_min_luma_coding_block_size); + ue(chroma_qp_offset_list_len_minus1, 0, 5); + for (i = 0; i <= current->chroma_qp_offset_list_len_minus1; i++) { + se(cb_qp_offset_list[i], -12, +12); + se(cr_qp_offset_list[i], -12, +12); + } + } + + ue(log2_sao_offset_scale_luma, 0, FFMAX(0, sps->bit_depth_luma_minus8 - 2)); + ue(log2_sao_offset_scale_chroma, 0, FFMAX(0, sps->bit_depth_chroma_minus8 - 2)); + + return 0; +} + +static int FUNC(pps_scc_extension)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawPPS *current) +{ + int err, comp, i; + + flag(pps_curr_pic_ref_enabled_flag); + + flag(residual_adaptive_colour_transform_enabled_flag); + if (current->residual_adaptive_colour_transform_enabled_flag) { + flag(pps_slice_act_qp_offsets_present_flag); + se(pps_act_y_qp_offset_plus5, -7, +17); + se(pps_act_cb_qp_offset_plus5, -7, +17); + se(pps_act_cr_qp_offset_plus3, -9, +15); + } else { + infer(pps_slice_act_qp_offsets_present_flag, 0); + infer(pps_act_y_qp_offset_plus5, 0); + infer(pps_act_cb_qp_offset_plus5, 0); + infer(pps_act_cr_qp_offset_plus3, 0); + } + + flag(pps_palette_predictor_initializer_present_flag); + if (current->pps_palette_predictor_initializer_present_flag) { + ue(pps_num_palette_predictor_initializer, 0, 128); + if (current->pps_num_palette_predictor_initializer > 0) { + flag(monochrome_palette_flag); + ue(luma_bit_depth_entry_minus8, 0, 8); + if (!current->monochrome_palette_flag) + ue(chroma_bit_depth_entry_minus8, 0, 8); + for (comp = 0; comp < (current->monochrome_palette_flag ? 1 : 3); comp++) { + int bit_depth = comp == 0 ? current->luma_bit_depth_entry_minus8 + 8 + : current->chroma_bit_depth_entry_minus8 + 8; + for (i = 0; i < current->pps_num_palette_predictor_initializer; i++) + u(bit_depth, pps_palette_predictor_initializers[comp][i], + 0, (1 << bit_depth) - 1); + } + } + } + + return 0; +} + +static int FUNC(pps)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawPPS *current) +{ + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps; + int err, i; + + HEADER("Picture Parameter Set"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, HEVC_NAL_PPS)); + + ue(pps_pic_parameter_set_id, 0, 63); + ue(pps_seq_parameter_set_id, 0, 15); + sps = h265->sps[current->pps_seq_parameter_set_id]; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + current->pps_seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h265->active_sps = sps; + + flag(dependent_slice_segments_enabled_flag); + flag(output_flag_present_flag); + u(3, num_extra_slice_header_bits, 0, 7); + flag(sign_data_hiding_enabled_flag); + flag(cabac_init_present_flag); + + ue(num_ref_idx_l0_default_active_minus1, 0, 14); + ue(num_ref_idx_l1_default_active_minus1, 0, 14); + + se(init_qp_minus26, -(26 + 6 * sps->bit_depth_luma_minus8), +25); + + flag(constrained_intra_pred_flag); + flag(transform_skip_enabled_flag); + flag(cu_qp_delta_enabled_flag); + if (current->cu_qp_delta_enabled_flag) + ue(diff_cu_qp_delta_depth, + 0, sps->log2_diff_max_min_luma_coding_block_size); + else + infer(diff_cu_qp_delta_depth, 0); + + se(pps_cb_qp_offset, -12, +12); + se(pps_cr_qp_offset, -12, +12); + flag(pps_slice_chroma_qp_offsets_present_flag); + + flag(weighted_pred_flag); + flag(weighted_bipred_flag); + + flag(transquant_bypass_enabled_flag); + flag(tiles_enabled_flag); + flag(entropy_coding_sync_enabled_flag); + + if (current->tiles_enabled_flag) { + ue(num_tile_columns_minus1, 0, HEVC_MAX_TILE_COLUMNS); + ue(num_tile_rows_minus1, 0, HEVC_MAX_TILE_ROWS); + flag(uniform_spacing_flag); + if (!current->uniform_spacing_flag) { + for (i = 0; i < current->num_tile_columns_minus1; i++) + ue(column_width_minus1[i], 0, sps->pic_width_in_luma_samples); + for (i = 0; i < current->num_tile_rows_minus1; i++) + ue(row_height_minus1[i], 0, sps->pic_height_in_luma_samples); + } + flag(loop_filter_across_tiles_enabled_flag); + } else { + infer(num_tile_columns_minus1, 0); + infer(num_tile_rows_minus1, 0); + } + + flag(pps_loop_filter_across_slices_enabled_flag); + flag(deblocking_filter_control_present_flag); + if (current->deblocking_filter_control_present_flag) { + flag(deblocking_filter_override_enabled_flag); + flag(pps_deblocking_filter_disabled_flag); + if (!current->pps_deblocking_filter_disabled_flag) { + se(pps_beta_offset_div2, -6, +6); + se(pps_tc_offset_div2, -6, +6); + } else { + infer(pps_beta_offset_div2, 0); + infer(pps_tc_offset_div2, 0); + } + } else { + infer(deblocking_filter_override_enabled_flag, 0); + infer(pps_deblocking_filter_disabled_flag, 0); + infer(pps_beta_offset_div2, 0); + infer(pps_tc_offset_div2, 0); + } + + flag(pps_scaling_list_data_present_flag); + if (current->pps_scaling_list_data_present_flag) + CHECK(FUNC(scaling_list_data)(ctx, rw, ¤t->scaling_list)); + + flag(lists_modification_present_flag); + + ue(log2_parallel_merge_level_minus2, + 0, (sps->log2_min_luma_coding_block_size_minus3 + 3 + + sps->log2_diff_max_min_luma_coding_block_size - 2)); + + flag(slice_segment_header_extension_present_flag); + + flag(pps_extension_present_flag); + if (current->pps_extension_present_flag) { + flag(pps_range_extension_flag); + flag(pps_multilayer_extension_flag); + flag(pps_3d_extension_flag); + flag(pps_scc_extension_flag); + u(4, pps_extension_4bits, 0, (1 << 4) - 1); + } + if (current->pps_range_extension_flag) + CHECK(FUNC(pps_range_extension)(ctx, rw, current)); + if (current->pps_multilayer_extension_flag) + return AVERROR_PATCHWELCOME; + if (current->pps_3d_extension_flag) + return AVERROR_PATCHWELCOME; + if (current->pps_scc_extension_flag) + CHECK(FUNC(pps_scc_extension)(ctx, rw, current)); + if (current->pps_extension_4bits) + CHECK(FUNC(extension_data)(ctx, rw, ¤t->extension_data)); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(aud)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawAUD *current) +{ + int err; + + HEADER("Access Unit Delimiter"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, HEVC_NAL_AUD)); + + u(3, pic_type, 0, 2); + + CHECK(FUNC(rbsp_trailing_bits)(ctx, rw)); + + return 0; +} + +static int FUNC(ref_pic_lists_modification)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSliceHeader *current, + unsigned int num_pic_total_curr) +{ + unsigned int entry_size; + int err, i; + + entry_size = av_log2(num_pic_total_curr - 1) + 1; + + flag(ref_pic_list_modification_flag_l0); + if (current->ref_pic_list_modification_flag_l0) { + for (i = 0; i <= current->num_ref_idx_l0_active_minus1; i++) + u(entry_size, list_entry_l0[i], 0, num_pic_total_curr - 1); + } + + if (current->slice_type == HEVC_SLICE_B) { + flag(ref_pic_list_modification_flag_l1); + if (current->ref_pic_list_modification_flag_l1) { + for (i = 0; i <= current->num_ref_idx_l1_active_minus1; i++) + u(entry_size, list_entry_l1[i], 0, num_pic_total_curr - 1); + } + } + + return 0; +} + +static int FUNC(pred_weight_table)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSliceHeader *current) +{ + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps = h265->active_sps; + int err, i, j; + int chroma = !sps->separate_colour_plane_flag && + sps->chroma_format_idc != 0; + + ue(luma_log2_weight_denom, 0, 7); + if (chroma) + se(delta_chroma_log2_weight_denom, -7, 7); + else + infer(delta_chroma_log2_weight_denom, 0); + + for (i = 0; i <= current->num_ref_idx_l0_active_minus1; i++) { + if (1 /* is not same POC and same layer_id */) + flag(luma_weight_l0_flag[i]); + else + infer(luma_weight_l0_flag[i], 0); + } + if (chroma) { + for (i = 0; i <= current->num_ref_idx_l0_active_minus1; i++) { + if (1 /* is not same POC and same layer_id */) + flag(chroma_weight_l0_flag[i]); + else + infer(chroma_weight_l0_flag[i], 0); + } + } + + for (i = 0; i <= current->num_ref_idx_l0_active_minus1; i++) { + if (current->luma_weight_l0_flag[i]) { + se(delta_luma_weight_l0[i], -128, +127); + se(luma_offset_l0[i], + -(1 << (sps->bit_depth_luma_minus8 + 8 - 1)), + +(1 << (sps->bit_depth_luma_minus8 + 8 - 1) - 1)); + } else { + infer(delta_luma_weight_l0[i], 0); + infer(luma_offset_l0[i], 0); + } + if (current->chroma_weight_l0_flag[i]) { + for (j = 0; j < 2; j++) { + se(delta_chroma_weight_l0[i][j], -128, +127); + se(chroma_offset_l0[i][j], + -(4 << (sps->bit_depth_chroma_minus8 + 8 - 1)), + +(4 << (sps->bit_depth_chroma_minus8 + 8 - 1) - 1)); + } + } else { + for (j = 0; j < 2; j++) { + infer(delta_chroma_weight_l0[i][j], 0); + infer(chroma_offset_l0[i][j], 0); + } + } + } + + if (current->slice_type == HEVC_SLICE_B) { + for (i = 0; i <= current->num_ref_idx_l1_active_minus1; i++) { + if (1 /* RefPicList1[i] is not CurrPic, nor is it in a different layer */) + flag(luma_weight_l1_flag[i]); + else + infer(luma_weight_l1_flag[i], 0); + } + if (chroma) { + for (i = 0; i <= current->num_ref_idx_l1_active_minus1; i++) { + if (1 /* RefPicList1[i] is not CurrPic, nor is it in a different layer */) + flag(chroma_weight_l1_flag[i]); + else + infer(chroma_weight_l1_flag[i], 0); + } + } + + for (i = 0; i <= current->num_ref_idx_l1_active_minus1; i++) { + if (current->luma_weight_l1_flag[i]) { + se(delta_luma_weight_l1[i], -128, +127); + se(luma_offset_l1[i], + - 1 << (sps->bit_depth_luma_minus8 + 8 - 1), + + 1 << (sps->bit_depth_luma_minus8 + 8 - 1) - 1); + } else { + infer(delta_luma_weight_l1[i], 0); + infer(luma_offset_l1[i], 0); + } + if (current->chroma_weight_l1_flag[i]) { + for (j = 0; j < 2; j++) { + se(delta_chroma_weight_l1[i][j], -128, +127); + se(chroma_offset_l1[i][j], + - 4 << (sps->bit_depth_chroma_minus8 + 8 - 1), + + 4 << (sps->bit_depth_chroma_minus8 + 8 - 1) - 1); + } + } else { + for (j = 0; j < 2; j++) { + infer(delta_chroma_weight_l1[i][j], 0); + infer(chroma_offset_l1[i][j], 0); + } + } + } + } + + return 0; +} + +static int FUNC(slice_segment_header)(CodedBitstreamContext *ctx, RWContext *rw, + H265RawSliceHeader *current) +{ + CodedBitstreamH265Context *h265 = ctx->priv_data; + const H265RawSPS *sps; + const H265RawPPS *pps; + unsigned int min_cb_log2_size_y, ctb_log2_size_y, ctb_size_y; + unsigned int pic_width_in_ctbs_y, pic_height_in_ctbs_y, pic_size_in_ctbs_y; + unsigned int num_pic_total_curr = 0; + int err, i; + + HEADER("Slice Segment Header"); + + CHECK(FUNC(nal_unit_header)(ctx, rw, ¤t->nal_unit_header, -1)); + + flag(first_slice_segment_in_pic_flag); + + if (current->nal_unit_header.nal_unit_type >= HEVC_NAL_BLA_W_LP && + current->nal_unit_header.nal_unit_type <= HEVC_NAL_IRAP_VCL23) + flag(no_output_of_prior_pics_flag); + + ue(slice_pic_parameter_set_id, 0, 63); + + pps = h265->pps[current->slice_pic_parameter_set_id]; + if (!pps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "PPS id %d not available.\n", + current->slice_pic_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h265->active_pps = pps; + + sps = h265->sps[pps->pps_seq_parameter_set_id]; + if (!sps) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "SPS id %d not available.\n", + pps->pps_seq_parameter_set_id); + return AVERROR_INVALIDDATA; + } + h265->active_sps = sps; + + min_cb_log2_size_y = sps->log2_min_luma_coding_block_size_minus3 + 3; + ctb_log2_size_y = min_cb_log2_size_y + sps->log2_diff_max_min_luma_coding_block_size; + ctb_size_y = 1 << ctb_log2_size_y; + pic_width_in_ctbs_y = + (sps->pic_width_in_luma_samples + ctb_size_y - 1) / ctb_size_y; + pic_height_in_ctbs_y = + (sps->pic_height_in_luma_samples + ctb_size_y - 1) / ctb_size_y; + pic_size_in_ctbs_y = pic_width_in_ctbs_y * pic_height_in_ctbs_y; + + if (!current->first_slice_segment_in_pic_flag) { + unsigned int address_size = av_log2(pic_size_in_ctbs_y - 1) + 1; + if (pps->dependent_slice_segments_enabled_flag) + flag(dependent_slice_segment_flag); + else + infer(dependent_slice_segment_flag, 0); + u(address_size, slice_segment_address, 0, pic_size_in_ctbs_y - 1); + } else { + infer(dependent_slice_segment_flag, 0); + } + + if (!current->dependent_slice_segment_flag) { + for (i = 0; i < pps->num_extra_slice_header_bits; i++) + flag(slice_reserved_flag[i]); + + ue(slice_type, 0, 2); + + if (pps->output_flag_present_flag) + flag(pic_output_flag); + + if (sps->separate_colour_plane_flag) + u(2, colour_plane_id, 0, 2); + + if (current->nal_unit_header.nal_unit_type != HEVC_NAL_IDR_W_RADL && + current->nal_unit_header.nal_unit_type != HEVC_NAL_IDR_N_LP) { + const H265RawSTRefPicSet *rps; + + u(sps->log2_max_pic_order_cnt_lsb_minus4 + 4, slice_pic_order_cnt_lsb, + 0, (1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4)) - 1); + + flag(short_term_ref_pic_set_sps_flag); + if (!current->short_term_ref_pic_set_sps_flag) { + CHECK(FUNC(st_ref_pic_set)(ctx, rw, ¤t->short_term_ref_pic_set, + sps->num_short_term_ref_pic_sets, sps)); + rps = ¤t->short_term_ref_pic_set; + } else if (sps->num_short_term_ref_pic_sets > 1) { + unsigned int idx_size = av_log2(sps->num_short_term_ref_pic_sets - 1) + 1; + u(idx_size, short_term_ref_pic_set_idx, + 0, sps->num_short_term_ref_pic_sets - 1); + rps = &sps->st_ref_pic_set[current->short_term_ref_pic_set_idx]; + } else { + infer(short_term_ref_pic_set_idx, 0); + rps = &sps->st_ref_pic_set[0]; + } + + num_pic_total_curr = 0; + for (i = 0; i < rps->num_negative_pics; i++) + if (rps->used_by_curr_pic_s0_flag[i]) + ++num_pic_total_curr; + for (i = 0; i < rps->num_positive_pics; i++) + if (rps->used_by_curr_pic_s1_flag[i]) + ++num_pic_total_curr; + + if (sps->long_term_ref_pics_present_flag) { + unsigned int idx_size; + + if (sps->num_long_term_ref_pics_sps > 0) { + ue(num_long_term_sps, 0, sps->num_long_term_ref_pics_sps); + idx_size = av_log2(sps->num_long_term_ref_pics_sps - 1) + 1; + } else { + infer(num_long_term_sps, 0); + idx_size = 0; + } + ue(num_long_term_pics, 0, HEVC_MAX_LONG_TERM_REF_PICS); + + for (i = 0; i < current->num_long_term_sps + + current->num_long_term_pics; i++) { + if (i < current->num_long_term_sps) { + if (sps->num_long_term_ref_pics_sps > 1) + u(idx_size, lt_idx_sps[i], + 0, sps->num_long_term_ref_pics_sps - 1); + if (sps->used_by_curr_pic_lt_sps_flag[current->lt_idx_sps[i]]) + ++num_pic_total_curr; + } else { + u(sps->log2_max_pic_order_cnt_lsb_minus4 + 4, poc_lsb_lt[i], + 0, (1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4)) - 1); + flag(used_by_curr_pic_lt_flag[i]); + if (current->used_by_curr_pic_lt_flag[i]) + ++num_pic_total_curr; + } + flag(delta_poc_msb_present_flag[i]); + if (current->delta_poc_msb_present_flag[i]) + ue(delta_poc_msb_cycle_lt[i], 0, UINT32_MAX - 1); + else + infer(delta_poc_msb_cycle_lt[i], 0); + } + } + + if (sps->sps_temporal_mvp_enabled_flag) + flag(slice_temporal_mvp_enabled_flag); + else + infer(slice_temporal_mvp_enabled_flag, 0); + + if (pps->pps_curr_pic_ref_enabled_flag) + ++num_pic_total_curr; + } + + if (sps->sample_adaptive_offset_enabled_flag) { + flag(slice_sao_luma_flag); + if (!sps->separate_colour_plane_flag && sps->chroma_format_idc != 0) + flag(slice_sao_chroma_flag); + else + infer(slice_sao_chroma_flag, 0); + } else { + infer(slice_sao_luma_flag, 0); + infer(slice_sao_chroma_flag, 0); + } + + if (current->slice_type == HEVC_SLICE_P || + current->slice_type == HEVC_SLICE_B) { + flag(num_ref_idx_active_override_flag); + if (current->num_ref_idx_active_override_flag) { + ue(num_ref_idx_l0_active_minus1, 0, 14); + if (current->slice_type == HEVC_SLICE_B) + ue(num_ref_idx_l1_active_minus1, 0, 14); + else + infer(num_ref_idx_l1_active_minus1, pps->num_ref_idx_l1_default_active_minus1); + } else { + infer(num_ref_idx_l0_active_minus1, pps->num_ref_idx_l0_default_active_minus1); + infer(num_ref_idx_l1_active_minus1, pps->num_ref_idx_l1_default_active_minus1); + } + + if (pps->lists_modification_present_flag && num_pic_total_curr > 1) + CHECK(FUNC(ref_pic_lists_modification)(ctx, rw, current, + num_pic_total_curr)); + + if (current->slice_type == HEVC_SLICE_B) + flag(mvd_l1_zero_flag); + if (pps->cabac_init_present_flag) + flag(cabac_init_flag); + else + infer(cabac_init_flag, 0); + if (current->slice_temporal_mvp_enabled_flag) { + if (current->slice_type == HEVC_SLICE_B) + flag(collocated_from_l0_flag); + else + infer(collocated_from_l0_flag, 1); + if (current->collocated_from_l0_flag) { + if (current->num_ref_idx_l0_active_minus1 > 0) + ue(collocated_ref_idx, 0, current->num_ref_idx_l0_active_minus1); + else + infer(collocated_ref_idx, 0); + } else { + if (current->num_ref_idx_l1_active_minus1 > 0) + ue(collocated_ref_idx, 0, current->num_ref_idx_l1_active_minus1); + else + infer(collocated_ref_idx, 0); + } + } + + if ((pps->weighted_pred_flag && current->slice_type == HEVC_SLICE_P) || + (pps->weighted_bipred_flag && current->slice_type == HEVC_SLICE_B)) + CHECK(FUNC(pred_weight_table)(ctx, rw, current)); + + ue(five_minus_max_num_merge_cand, 0, 4); + if (sps->motion_vector_resolution_control_idc == 2) + flag(use_integer_mv_flag); + else + infer(use_integer_mv_flag, sps->motion_vector_resolution_control_idc); + } + + se(slice_qp_delta, + - 6 * sps->bit_depth_luma_minus8 - (pps->init_qp_minus26 + 26), + + 51 - (pps->init_qp_minus26 + 26)); + if (pps->pps_slice_chroma_qp_offsets_present_flag) { + se(slice_cb_qp_offset, -12, +12); + se(slice_cr_qp_offset, -12, +12); + } else { + infer(slice_cb_qp_offset, 0); + infer(slice_cr_qp_offset, 0); + } + if (pps->pps_slice_act_qp_offsets_present_flag) { + se(slice_act_y_qp_offset, + -12 - (pps->pps_act_y_qp_offset_plus5 - 5), + +12 - (pps->pps_act_y_qp_offset_plus5 - 5)); + se(slice_act_cb_qp_offset, + -12 - (pps->pps_act_cb_qp_offset_plus5 - 5), + +12 - (pps->pps_act_cb_qp_offset_plus5 - 5)); + se(slice_act_cr_qp_offset, + -12 - (pps->pps_act_cr_qp_offset_plus3 - 3), + +12 - (pps->pps_act_cr_qp_offset_plus3 - 3)); + } else { + infer(slice_act_y_qp_offset, 0); + infer(slice_act_cb_qp_offset, 0); + infer(slice_act_cr_qp_offset, 0); + } + if (pps->chroma_qp_offset_list_enabled_flag) + flag(cu_chroma_qp_offset_enabled_flag); + else + infer(cu_chroma_qp_offset_enabled_flag, 0); + + if (pps->deblocking_filter_override_enabled_flag) + flag(deblocking_filter_override_flag); + else + infer(deblocking_filter_override_flag, 0); + if (current->deblocking_filter_override_flag) { + flag(slice_deblocking_filter_disabled_flag); + if (!current->slice_deblocking_filter_disabled_flag) { + se(slice_beta_offset_div2, -6, +6); + se(slice_tc_offset_div2, -6, +6); + } else { + infer(slice_beta_offset_div2, pps->pps_beta_offset_div2); + infer(slice_tc_offset_div2, pps->pps_tc_offset_div2); + } + } else { + infer(slice_deblocking_filter_disabled_flag, + pps->pps_deblocking_filter_disabled_flag); + infer(slice_beta_offset_div2, pps->pps_beta_offset_div2); + infer(slice_tc_offset_div2, pps->pps_tc_offset_div2); + } + if (pps->pps_loop_filter_across_slices_enabled_flag && + (current->slice_sao_luma_flag || current->slice_sao_chroma_flag || + !current->slice_deblocking_filter_disabled_flag)) + flag(slice_loop_filter_across_slices_enabled_flag); + else + infer(slice_loop_filter_across_slices_enabled_flag, + pps->pps_loop_filter_across_slices_enabled_flag); + } + + if (pps->tiles_enabled_flag || pps->entropy_coding_sync_enabled_flag) { + unsigned int num_entry_point_offsets_limit; + if (!pps->tiles_enabled_flag && pps->entropy_coding_sync_enabled_flag) + num_entry_point_offsets_limit = pic_height_in_ctbs_y - 1; + else if (pps->tiles_enabled_flag && !pps->entropy_coding_sync_enabled_flag) + num_entry_point_offsets_limit = + (pps->num_tile_columns_minus1 + 1) * (pps->num_tile_rows_minus1 + 1); + else + num_entry_point_offsets_limit = + (pps->num_tile_columns_minus1 + 1) * pic_height_in_ctbs_y - 1; + ue(num_entry_point_offsets, 0, num_entry_point_offsets_limit); + + if (current->num_entry_point_offsets > HEVC_MAX_ENTRY_POINT_OFFSETS) { + av_log(ctx->log_ctx, AV_LOG_ERROR, "Too many entry points: " + "%"PRIu16".\n", current->num_entry_point_offsets); + return AVERROR_PATCHWELCOME; + } + + if (current->num_entry_point_offsets > 0) { + ue(offset_len_minus1, 0, 31); + for (i = 0; i < current->num_entry_point_offsets; i++) + u(current->offset_len_minus1 + 1, entry_point_offset_minus1[i], + 0, (1 << (current->offset_len_minus1 + 1)) - 1); + } + } + + if (pps->slice_segment_header_extension_present_flag) { + ue(slice_segment_header_extension_length, 0, 256); + for (i = 0; i < current->slice_segment_header_extension_length; i++) + u(8, slice_segment_header_extension_data_byte[i], 0x00, 0xff); + } + + CHECK(FUNC(byte_alignment)(ctx, rw)); + + return 0; +} diff --git a/libavcodec/cbs_internal.h b/libavcodec/cbs_internal.h index e87a43970f..0457f4ab9e 100644 --- a/libavcodec/cbs_internal.h +++ b/libavcodec/cbs_internal.h @@ -81,6 +81,7 @@ int ff_cbs_write_unsigned(CodedBitstreamContext *ctx, PutBitContext *pbc, extern const CodedBitstreamType ff_cbs_type_h264; +extern const CodedBitstreamType ff_cbs_type_h265; #endif /* AVCODEC_CBS_INTERNAL_H */ From f11d8a5e8b185340cc50fcbc8a1437b0fbe7e931 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Thu, 4 May 2017 23:06:20 +0100 Subject: [PATCH 07/15] lavc: Add trace_headers bitstream filter Supports all streams that the coded bitstream infrastructure does (currently H.264 and H.265). --- configure | 1 + doc/bitstream_filters.texi | 8 +++ libavcodec/Makefile | 1 + libavcodec/bitstream_filters.c | 1 + libavcodec/trace_headers_bsf.c | 125 +++++++++++++++++++++++++++++++++ 5 files changed, 136 insertions(+) create mode 100644 libavcodec/trace_headers_bsf.c diff --git a/configure b/configure index 535862fcaf..13dd7f54e2 100755 --- a/configure +++ b/configure @@ -2324,6 +2324,7 @@ vc1_parser_select="vc1dsp" # bitstream_filters aac_adtstoasc_bsf_select="adts_header" mjpeg2jpeg_bsf_select="jpegtables" +trace_headers_bsf_select="cbs_h264 cbs_h265" # external libraries avisynth_deps="LoadLibrary" diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi index 64f91f4b54..119a2267af 100644 --- a/doc/bitstream_filters.texi +++ b/doc/bitstream_filters.texi @@ -95,6 +95,14 @@ This bitstream filter passes the packets through unchanged. @section remove_extradata +@section trace_headers + +Log trace output containing all syntax elements in the coded stream +headers (everything above the level of individual coded blocks). +This can be useful for debugging low-level stream issues. + +Supports H.264 and H.265. + @section vp9_superframe Combine VP9 frames into superframes. diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 9791fb342e..09772a85f7 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -786,6 +786,7 @@ OBJS-$(CONFIG_NOISE_BSF) += noise_bsf.o OBJS-$(CONFIG_NULL_BSF) += null_bsf.o OBJS-$(CONFIG_REMOVE_EXTRADATA_BSF) += remove_extradata_bsf.o OBJS-$(CONFIG_TEXT2MOVSUB_BSF) += movsub_bsf.o +OBJS-$(CONFIG_TRACE_HEADERS_BSF) += trace_headers_bsf.o OBJS-$(CONFIG_VP9_RAW_REORDER_BSF) += vp9_raw_reorder_bsf.o OBJS-$(CONFIG_VP9_SUPERFRAME_BSF) += vp9_superframe_bsf.o OBJS-$(CONFIG_VP9_SUPERFRAME_SPLIT_BSF) += vp9_superframe_split_bsf.o diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index 79ce40f9ed..2e423acaf0 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -38,6 +38,7 @@ extern const AVBitStreamFilter ff_null_bsf; extern const AVBitStreamFilter ff_text2movsub_bsf; extern const AVBitStreamFilter ff_noise_bsf; extern const AVBitStreamFilter ff_remove_extradata_bsf; +extern const AVBitStreamFilter ff_trace_headers_bsf; extern const AVBitStreamFilter ff_vp9_raw_reorder_bsf; extern const AVBitStreamFilter ff_vp9_superframe_bsf; extern const AVBitStreamFilter ff_vp9_superframe_split_bsf; diff --git a/libavcodec/trace_headers_bsf.c b/libavcodec/trace_headers_bsf.c new file mode 100644 index 0000000000..2d1fe636c2 --- /dev/null +++ b/libavcodec/trace_headers_bsf.c @@ -0,0 +1,125 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/avstring.h" +#include "libavutil/common.h" +#include "libavutil/log.h" + +#include "bsf.h" +#include "cbs.h" + + +typedef struct TraceHeadersContext { + CodedBitstreamContext cbc; +} TraceHeadersContext; + + +static int trace_headers_init(AVBSFContext *bsf) +{ + TraceHeadersContext *ctx = bsf->priv_data; + int err; + + err = ff_cbs_init(&ctx->cbc, bsf->par_in->codec_id, bsf); + if (err < 0) + return err; + + ctx->cbc.trace_enable = 1; + ctx->cbc.trace_level = AV_LOG_INFO; + + if (bsf->par_in->extradata) { + CodedBitstreamFragment ps; + + av_log(bsf, AV_LOG_INFO, "Extradata\n"); + + err = ff_cbs_read_extradata(&ctx->cbc, &ps, bsf->par_in); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read extradata.\n"); + return err; + } + + ff_cbs_fragment_uninit(&ctx->cbc, &ps); + } + + return 0; +} + +static void trace_headers_close(AVBSFContext *bsf) +{ + TraceHeadersContext *ctx = bsf->priv_data; + + ff_cbs_close(&ctx->cbc); +} + +static int trace_headers(AVBSFContext *bsf, AVPacket *out) +{ + TraceHeadersContext *ctx = bsf->priv_data; + CodedBitstreamFragment au; + AVPacket *in; + char tmp[256] = { 0 }; + int err; + + err = ff_bsf_get_packet(bsf, &in); + if (err < 0) + return err; + + if (in->flags & AV_PKT_FLAG_KEY) + av_strlcat(tmp, ", key frame", sizeof(tmp)); + if (in->flags & AV_PKT_FLAG_CORRUPT) + av_strlcat(tmp, ", corrupt", sizeof(tmp)); + + if (in->pts != AV_NOPTS_VALUE) + av_strlcatf(tmp, sizeof(tmp), ", pts %"PRId64, in->pts); + else + av_strlcat(tmp, ", no pts", sizeof(tmp)); + if (in->dts != AV_NOPTS_VALUE) + av_strlcatf(tmp, sizeof(tmp), ", dts %"PRId64, in->dts); + else + av_strlcat(tmp, ", no dts", sizeof(tmp)); + if (in->duration > 0) + av_strlcatf(tmp, sizeof(tmp), ", duration %"PRId64, in->duration); + + av_log(bsf, AV_LOG_INFO, "Packet: %d bytes%s.\n", in->size, tmp); + + err = ff_cbs_read_packet(&ctx->cbc, &au, in); + if (err < 0) + return err; + + ff_cbs_fragment_uninit(&ctx->cbc, &au); + + av_packet_move_ref(out, in); + av_packet_free(&in); + + return 0; +} + +static const enum AVCodecID trace_headers_codec_ids[] = { + AV_CODEC_ID_H264, + AV_CODEC_ID_HEVC, + AV_CODEC_ID_NONE, +}; + +const AVBitStreamFilter ff_trace_headers_bsf = { + .name = "trace_headers", + .priv_data_size = sizeof(TraceHeadersContext), + .init = &trace_headers_init, + .close = &trace_headers_close, + .filter = &trace_headers, + .codec_ids = trace_headers_codec_ids, +}; From 9e93001b6135a23fe4e200196c08fb4fbffed6fc Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Thu, 4 May 2017 23:09:02 +0100 Subject: [PATCH 08/15] lavc: Add h264_metadata bitstream filter This is able to modify some header metadata found in the SPS/VUI, and can also add/remove AUDs and insert user data in SEI NAL units. --- configure | 1 + doc/bitstream_filters.texi | 63 ++++ libavcodec/Makefile | 1 + libavcodec/bitstream_filters.c | 1 + libavcodec/h264_metadata_bsf.c | 512 +++++++++++++++++++++++++++++++++ 5 files changed, 578 insertions(+) create mode 100644 libavcodec/h264_metadata_bsf.c diff --git a/configure b/configure index 13dd7f54e2..f3d616800a 100755 --- a/configure +++ b/configure @@ -2323,6 +2323,7 @@ vc1_parser_select="vc1dsp" # bitstream_filters aac_adtstoasc_bsf_select="adts_header" +h264_metadata_bsf_select="cbs_h264" mjpeg2jpeg_bsf_select="jpegtables" trace_headers_bsf_select="cbs_h264 cbs_h265" diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi index 119a2267af..08f2f390cb 100644 --- a/doc/bitstream_filters.texi +++ b/doc/bitstream_filters.texi @@ -39,6 +39,69 @@ When this option is enabled, the long-term headers are removed from the bitstream after extraction. @end table +@section h264_metadata + +Modify metadata embedded in an H.264 stream. + +@table @option +@item aud +Insert or remove AUD NAL units in all access units of the stream. + +@table @samp +@item insert +@item remove +@end table + +@item sample_aspect_ratio +Set the sample aspect ratio of the stream in the VUI parameters. + +@item video_format +@item video_full_range_flag +Set the video format in the stream (see H.264 section E.2.1 and +table E-2). + +@item colour_primaries +@item transfer_characteristics +@item matrix_coefficients +Set the colour description in the stream (see H.264 section E.2.1 +and tables E-3, E-4 and E-5). + +@item chroma_sample_loc_type +Set the chroma sample location in the stream (see H.264 section +E.2.1 and figure E-1). + +@item tick_rate +Set the tick rate (num_units_in_tick / time_scale) in the VUI +parameters. This is the smallest time unit representable in the +stream, and in many cases represents the field rate of the stream +(double the frame rate). +@item fixed_frame_rate_flag +Set whether the stream has fixed framerate - typically this indicates +that the framerate is exactly half the tick rate, but the exact +meaning is dependent on interlacing and the picture structure (see +H.264 section E.2.1 and table E-6). + +@item crop_left +@item crop_right +@item crop_top +@item crop_bottom +Set the frame cropping offsets in the SPS. These values will replace +the current ones if the stream is already cropped. + +These fields are set in pixels. Note that some sizes may not be +representable if the chroma is subsampled or the stream is interlaced +(see H.264 section 7.4.2.1.1). + +@item sei_user_data +Insert a string as SEI unregistered user data. The argument must +be of the form @emph{UUID+string}, where the UUID is as hex digits +possibly separated by hyphens, and the string can be anything. + +For example, @samp{086f3693-b7b3-4f2c-9653-21492feee5b8+hello} will +insert the string ``hello'' associated with the given UUID. + +@end table + @section h264_mp4toannexb @section imx_dump_header diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 09772a85f7..c76dffe058 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -776,6 +776,7 @@ OBJS-$(CONFIG_CHOMP_BSF) += chomp_bsf.o OBJS-$(CONFIG_DUMP_EXTRADATA_BSF) += dump_extradata_bsf.o OBJS-$(CONFIG_EXTRACT_EXTRADATA_BSF) += extract_extradata_bsf.o \ h2645_parse.o +OBJS-$(CONFIG_H264_METADATA_BSF) += h264_metadata_bsf.o OBJS-$(CONFIG_H264_MP4TOANNEXB_BSF) += h264_mp4toannexb_bsf.o OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF) += hevc_mp4toannexb_bsf.o OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += imx_dump_header_bsf.o diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index 2e423acaf0..4ad50508cb 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -28,6 +28,7 @@ extern const AVBitStreamFilter ff_aac_adtstoasc_bsf; extern const AVBitStreamFilter ff_chomp_bsf; extern const AVBitStreamFilter ff_dump_extradata_bsf; extern const AVBitStreamFilter ff_extract_extradata_bsf; +extern const AVBitStreamFilter ff_h264_metadata_bsf; extern const AVBitStreamFilter ff_h264_mp4toannexb_bsf; extern const AVBitStreamFilter ff_hevc_mp4toannexb_bsf; extern const AVBitStreamFilter ff_imx_dump_header_bsf; diff --git a/libavcodec/h264_metadata_bsf.c b/libavcodec/h264_metadata_bsf.c new file mode 100644 index 0000000000..9bf96b397d --- /dev/null +++ b/libavcodec/h264_metadata_bsf.c @@ -0,0 +1,512 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/avstring.h" +#include "libavutil/common.h" +#include "libavutil/opt.h" + +#include "bsf.h" +#include "cbs.h" +#include "cbs_h264.h" +#include "h264.h" +#include "h264_sei.h" + +enum { + PASS, + INSERT, + REMOVE, +}; + +typedef struct H264MetadataContext { + const AVClass *class; + + CodedBitstreamContext cbc; + CodedBitstreamFragment access_unit; + + H264RawAUD aud_nal; + H264RawSEI sei_nal; + + int aud; + + AVRational sample_aspect_ratio; + + int video_format; + int video_full_range_flag; + int colour_primaries; + int transfer_characteristics; + int matrix_coefficients; + + int chroma_sample_loc_type; + + AVRational tick_rate; + int fixed_frame_rate_flag; + + int crop_left; + int crop_right; + int crop_top; + int crop_bottom; + + const char *sei_user_data; +} H264MetadataContext; + + +static int h264_metadata_update_sps(AVBSFContext *bsf, + H264RawSPS *sps) +{ + H264MetadataContext *ctx = bsf->priv_data; + int need_vui = 0; + int crop_unit_x, crop_unit_y; + + if (ctx->sample_aspect_ratio.num && ctx->sample_aspect_ratio.den) { + // Table E-1. + static const AVRational sar_idc[] = { + { 0, 0 }, // Unspecified (never written here). + { 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 }, + { 40, 33 }, { 24, 11 }, { 20, 11 }, { 32, 11 }, + { 80, 33 }, { 18, 11 }, { 15, 11 }, { 64, 33 }, + { 160, 99 }, { 4, 3 }, { 3, 2 }, { 2, 1 }, + }; + int num, den, i; + + av_reduce(&num, &den, ctx->sample_aspect_ratio.num, + ctx->sample_aspect_ratio.den, 65535); + + for (i = 1; i < FF_ARRAY_ELEMS(sar_idc); i++) { + if (num == sar_idc[i].num && + den == sar_idc[i].den) + break; + } + if (i == FF_ARRAY_ELEMS(sar_idc)) { + sps->vui.aspect_ratio_idc = 255; + sps->vui.sar_width = num; + sps->vui.sar_height = den; + } else { + sps->vui.aspect_ratio_idc = i; + } + sps->vui.aspect_ratio_info_present_flag = 1; + need_vui = 1; + } + +#define SET_OR_INFER(field, value, present_flag, infer) do { \ + if (value >= 0) { \ + field = value; \ + need_vui = 1; \ + } else if (!present_flag) \ + field = infer; \ + } while (0) + + if (ctx->video_format >= 0 || + ctx->video_full_range_flag >= 0 || + ctx->colour_primaries >= 0 || + ctx->transfer_characteristics >= 0 || + ctx->matrix_coefficients >= 0) { + + SET_OR_INFER(sps->vui.video_format, ctx->video_format, + sps->vui.video_signal_type_present_flag, 5); + + SET_OR_INFER(sps->vui.video_full_range_flag, + ctx->video_full_range_flag, + sps->vui.video_signal_type_present_flag, 0); + + if (ctx->colour_primaries >= 0 || + ctx->transfer_characteristics >= 0 || + ctx->matrix_coefficients >= 0) { + + SET_OR_INFER(sps->vui.colour_primaries, + ctx->colour_primaries, + sps->vui.colour_description_present_flag, 2); + + SET_OR_INFER(sps->vui.transfer_characteristics, + ctx->transfer_characteristics, + sps->vui.colour_description_present_flag, 2); + + SET_OR_INFER(sps->vui.matrix_coefficients, + ctx->matrix_coefficients, + sps->vui.colour_description_present_flag, 2); + + sps->vui.colour_description_present_flag = 1; + } + sps->vui.video_signal_type_present_flag = 1; + need_vui = 1; + } + + if (ctx->chroma_sample_loc_type >= 0) { + sps->vui.chroma_sample_loc_type_top_field = + ctx->chroma_sample_loc_type; + sps->vui.chroma_sample_loc_type_bottom_field = + ctx->chroma_sample_loc_type; + sps->vui.chroma_loc_info_present_flag = 1; + need_vui = 1; + } + + if (ctx->tick_rate.num && ctx->tick_rate.den) { + int num, den; + + av_reduce(&num, &den, ctx->tick_rate.num, ctx->tick_rate.den, + UINT32_MAX > INT_MAX ? UINT32_MAX : INT_MAX); + + sps->vui.time_scale = num; + sps->vui.num_units_in_tick = den; + + sps->vui.timing_info_present_flag = 1; + need_vui = 1; + } + SET_OR_INFER(sps->vui.fixed_frame_rate_flag, + ctx->fixed_frame_rate_flag, + sps->vui.timing_info_present_flag, 0); + + if (sps->separate_colour_plane_flag || sps->chroma_format_idc == 0) { + crop_unit_x = 1; + crop_unit_y = 2 - sps->frame_mbs_only_flag; + } else { + crop_unit_x = 1 + (sps->chroma_format_idc < 3); + crop_unit_y = (1 + (sps->chroma_format_idc < 2)) * + (2 - sps->frame_mbs_only_flag); + } +#define CROP(border, unit) do { \ + if (ctx->crop_ ## border >= 0) { \ + if (ctx->crop_ ## border % unit != 0) { \ + av_log(bsf, AV_LOG_ERROR, "Invalid value for crop_%s: " \ + "must be a multiple of %d.\n", #border, unit); \ + return AVERROR(EINVAL); \ + } \ + sps->frame_crop_ ## border ## _offset = \ + ctx->crop_ ## border / unit; \ + sps->frame_cropping_flag = 1; \ + } \ + } while (0) + CROP(left, crop_unit_x); + CROP(right, crop_unit_x); + CROP(top, crop_unit_y); + CROP(bottom, crop_unit_y); +#undef CROP + + if (need_vui) + sps->vui_parameters_present_flag = 1; + + return 0; +} + +static int h264_metadata_filter(AVBSFContext *bsf, AVPacket *out) +{ + H264MetadataContext *ctx = bsf->priv_data; + AVPacket *in = NULL; + CodedBitstreamFragment *au = &ctx->access_unit; + int err, i, j, has_sps; + char *sei_udu_string = NULL; + + err = ff_bsf_get_packet(bsf, &in); + if (err < 0) + goto fail; + + err = ff_cbs_read_packet(&ctx->cbc, au, in); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); + goto fail; + } + + if (au->nb_units == 0) { + av_log(bsf, AV_LOG_ERROR, "No NAL units in packet.\n"); + err = AVERROR_INVALIDDATA; + goto fail; + } + + // If an AUD is present, it must be the first NAL unit. + if (au->units[0].type == H264_NAL_AUD) { + if (ctx->aud == REMOVE) + ff_cbs_delete_unit(&ctx->cbc, au, 0); + } else { + if (ctx->aud == INSERT) { + static const int primary_pic_type_table[] = { + 0x084, // 2, 7 + 0x0a5, // 0, 2, 5, 7 + 0x0e7, // 0, 1, 2, 5, 6, 7 + 0x210, // 4, 9 + 0x318, // 3, 4, 8, 9 + 0x294, // 2, 4, 7, 9 + 0x3bd, // 0, 2, 3, 4, 5, 7, 8, 9 + 0x3ff, // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 + }; + int primary_pic_type_mask = 0xff; + H264RawAUD *aud = &ctx->aud_nal; + + for (i = 0; i < au->nb_units; i++) { + if (au->units[i].type == H264_NAL_SLICE || + au->units[i].type == H264_NAL_IDR_SLICE) { + H264RawSlice *slice = au->units[i].content; + for (j = 0; j < FF_ARRAY_ELEMS(primary_pic_type_table); j++) { + if (!(primary_pic_type_table[j] & + (1 << slice->header.slice_type))) + primary_pic_type_mask &= ~(1 << j); + } + } + } + for (j = 0; j < FF_ARRAY_ELEMS(primary_pic_type_table); j++) + if (primary_pic_type_mask & (1 << j)) + break; + if (j >= FF_ARRAY_ELEMS(primary_pic_type_table)) { + av_log(bsf, AV_LOG_ERROR, "No usable primary_pic_type: " + "invalid slice types?\n"); + err = AVERROR_INVALIDDATA; + goto fail; + } + + aud->nal_unit_header.nal_unit_type = H264_NAL_AUD; + aud->primary_pic_type = j; + + err = ff_cbs_insert_unit_content(&ctx->cbc, au, + 0, H264_NAL_AUD, aud); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n"); + goto fail; + } + } + } + + has_sps = 0; + for (i = 0; i < au->nb_units; i++) { + if (au->units[i].type == H264_NAL_SPS) { + err = h264_metadata_update_sps(bsf, au->units[i].content); + if (err < 0) + goto fail; + has_sps = 1; + } + } + + // Only insert the SEI in access units containing SPSs. + if (has_sps && ctx->sei_user_data) { + H264RawSEI *sei; + H264RawSEIPayload *payload; + H264RawSEIUserDataUnregistered *udu; + int sei_pos; + + for (i = 0; i < au->nb_units; i++) { + if (au->units[i].type == H264_NAL_SEI || + au->units[i].type == H264_NAL_SLICE || + au->units[i].type == H264_NAL_IDR_SLICE) + break; + } + sei_pos = i; + + if (sei_pos < au->nb_units && + au->units[sei_pos].type == H264_NAL_SEI) { + sei = au->units[sei_pos].content; + } else { + sei = &ctx->sei_nal; + memset(sei, 0, sizeof(*sei)); + + sei->nal_unit_header.nal_unit_type = H264_NAL_SEI; + + err = ff_cbs_insert_unit_content(&ctx->cbc, au, + sei_pos, H264_NAL_SEI, sei); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to insert SEI.\n"); + goto fail; + } + } + + payload = &sei->payload[sei->payload_count]; + + payload->payload_type = H264_SEI_TYPE_USER_DATA_UNREGISTERED; + udu = &payload->payload.user_data_unregistered; + + for (i = j = 0; j < 32 && ctx->sei_user_data[i]; i++) { + int c, v; + c = ctx->sei_user_data[i]; + if (c == '-') { + continue; + } else if (av_isxdigit(c)) { + c = av_tolower(c); + v = (c <= '9' ? c - '0' : c - 'a' + 10); + } else { + goto invalid_user_data; + } + if (i & 1) + udu->uuid_iso_iec_11578[j / 2] |= v; + else + udu->uuid_iso_iec_11578[j / 2] = v << 4; + ++j; + } + if (j == 32 && ctx->sei_user_data[i] == '+') { + sei_udu_string = av_strdup(ctx->sei_user_data + i + 1); + if (!sei_udu_string) { + err = AVERROR(ENOMEM); + goto sei_fail; + } + + udu->data = sei_udu_string; + udu->data_length = strlen(sei_udu_string); + + payload->payload_size = 16 + udu->data_length; + + } else { + invalid_user_data: + av_log(bsf, AV_LOG_ERROR, "Invalid user data: " + "must be \"UUID+string\".\n"); + err = AVERROR(EINVAL); + sei_fail: + memset(payload, 0, sizeof(&payload)); + goto fail; + } + + ++sei->payload_count; + } + + err = ff_cbs_write_packet(&ctx->cbc, out, au); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write packet.\n"); + goto fail; + } + + err = av_packet_copy_props(out, in); + if (err < 0) + goto fail; + + err = 0; +fail: + ff_cbs_fragment_uninit(&ctx->cbc, au); + av_freep(&sei_udu_string); + + av_packet_free(&in); + + return err; +} + +static int h264_metadata_init(AVBSFContext *bsf) +{ + H264MetadataContext *ctx = bsf->priv_data; + CodedBitstreamFragment *au = &ctx->access_unit; + int err, i; + + err = ff_cbs_init(&ctx->cbc, AV_CODEC_ID_H264, bsf); + if (err < 0) + return err; + + if (bsf->par_in->extradata) { + err = ff_cbs_read_extradata(&ctx->cbc, au, bsf->par_in); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read extradata.\n"); + goto fail; + } + + for (i = 0; i < au->nb_units; i++) { + if (au->units[i].type == H264_NAL_SPS) { + err = h264_metadata_update_sps(bsf, au->units[i].content); + if (err < 0) + goto fail; + } + } + + err = ff_cbs_write_extradata(&ctx->cbc, bsf->par_out, au); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write extradata.\n"); + goto fail; + } + } + + err = 0; +fail: + ff_cbs_fragment_uninit(&ctx->cbc, au); + return err; +} + +static void h264_metadata_close(AVBSFContext *bsf) +{ + H264MetadataContext *ctx = bsf->priv_data; + ff_cbs_close(&ctx->cbc); +} + +#define OFFSET(x) offsetof(H264MetadataContext, x) +static const AVOption h264_metadata_options[] = { + { "aud", "Access Unit Delimiter NAL units", + OFFSET(aud), AV_OPT_TYPE_INT, + { .i64 = PASS }, PASS, REMOVE, 0, "aud" }, + { "pass", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PASS }, .unit = "aud" }, + { "insert", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = INSERT }, .unit = "aud" }, + { "remove", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = REMOVE }, .unit = "aud" }, + + { "sample_aspect_ratio", "Set sample aspect ratio (table E-1)", + OFFSET(sample_aspect_ratio), AV_OPT_TYPE_RATIONAL, + { .i64 = 0 }, 0, 65535 }, + + { "video_format", "Set video format (table E-2)", + OFFSET(video_format), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 7 }, + { "video_full_range_flag", "Set video full range flag", + OFFSET(video_full_range_flag), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 1 }, + { "colour_primaries", "Set colour primaries (table E-3)", + OFFSET(colour_primaries), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 255 }, + { "transfer_characteristics", "Set transfer characteristics (table E-4)", + OFFSET(transfer_characteristics), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 255 }, + { "matrix_coefficients", "Set matrix coefficients (table E-5)", + OFFSET(matrix_coefficients), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 255 }, + + { "chroma_sample_loc_type", "Set chroma sample location type (figure E-1)", + OFFSET(chroma_sample_loc_type), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 6 }, + + { "tick_rate", "Set VUI tick rate (num_units_in_tick / time_scale)", + OFFSET(tick_rate), AV_OPT_TYPE_RATIONAL, + { .i64 = 0 }, 0, UINT_MAX }, + { "fixed_frame_rate_flag", "Set VUI fixed frame rate flag", + OFFSET(fixed_frame_rate_flag), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 1 }, + + { "crop_left", "Set left border crop offset", + OFFSET(crop_left), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, H264_MAX_WIDTH }, + { "crop_right", "Set right border crop offset", + OFFSET(crop_right), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, H264_MAX_WIDTH }, + { "crop_top", "Set top border crop offset", + OFFSET(crop_top), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, H264_MAX_HEIGHT }, + { "crop_bottom", "Set bottom border crop offset", + OFFSET(crop_bottom), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, H264_MAX_HEIGHT }, + + { "sei_user_data", "Insert SEI user data (UUID+string)", + OFFSET(sei_user_data), AV_OPT_TYPE_STRING, { .str = NULL } }, + + { NULL } +}; + +static const AVClass h264_metadata_class = { + .class_name = "h264_metadata_bsf", + .item_name = av_default_item_name, + .option = h264_metadata_options, + .version = LIBAVCODEC_VERSION_MAJOR, +}; + +static const enum AVCodecID h264_metadata_codec_ids[] = { + AV_CODEC_ID_H264, AV_CODEC_ID_NONE, +}; + +const AVBitStreamFilter ff_h264_metadata_bsf = { + .name = "h264_metadata", + .priv_data_size = sizeof(H264MetadataContext), + .priv_class = &h264_metadata_class, + .init = &h264_metadata_init, + .close = &h264_metadata_close, + .filter = &h264_metadata_filter, + .codec_ids = h264_metadata_codec_ids, +}; From e6874bc3af2f09af39b5d91b9c5f9ded67459696 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Thu, 4 May 2017 23:10:19 +0100 Subject: [PATCH 09/15] lavc: Add h264_redundant_pps bitstream filter This applies a specific fixup to some Bluray streams which contain redundant PPSs modifying irrelevant parameters of the stream which confuse other transformations which require correct extradata. A new single global PPS is created, and all of the redundant PPSs within the stream are removed. --- configure | 1 + doc/bitstream_filters.texi | 9 ++ libavcodec/Makefile | 1 + libavcodec/bitstream_filters.c | 1 + libavcodec/h264_redundant_pps_bsf.c | 178 ++++++++++++++++++++++++++++ 5 files changed, 190 insertions(+) create mode 100644 libavcodec/h264_redundant_pps_bsf.c diff --git a/configure b/configure index f3d616800a..bd4555dc87 100755 --- a/configure +++ b/configure @@ -2324,6 +2324,7 @@ vc1_parser_select="vc1dsp" # bitstream_filters aac_adtstoasc_bsf_select="adts_header" h264_metadata_bsf_select="cbs_h264" +h264_redundant_pps_bsf_select="cbs_h264" mjpeg2jpeg_bsf_select="jpegtables" trace_headers_bsf_select="cbs_h264 cbs_h265" diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi index 08f2f390cb..e1de3035ef 100644 --- a/doc/bitstream_filters.texi +++ b/doc/bitstream_filters.texi @@ -104,6 +104,15 @@ insert the string ``hello'' associated with the given UUID. @section h264_mp4toannexb +@section h264_redundant_pps + +This applies a specific fixup to some Bluray streams which contain +redundant PPSs modifying irrelevant parameters of the stream which +confuse other transformations which require correct extradata. + +A new single global PPS is created, and all of the redundant PPSs +within the stream are removed. + @section imx_dump_header @section mjpeg2jpeg diff --git a/libavcodec/Makefile b/libavcodec/Makefile index c76dffe058..b37a3aa091 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -778,6 +778,7 @@ OBJS-$(CONFIG_EXTRACT_EXTRADATA_BSF) += extract_extradata_bsf.o \ h2645_parse.o OBJS-$(CONFIG_H264_METADATA_BSF) += h264_metadata_bsf.o OBJS-$(CONFIG_H264_MP4TOANNEXB_BSF) += h264_mp4toannexb_bsf.o +OBJS-$(CONFIG_H264_REDUNDANT_PPS_BSF) += h264_redundant_pps_bsf.o OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF) += hevc_mp4toannexb_bsf.o OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += imx_dump_header_bsf.o OBJS-$(CONFIG_MJPEG2JPEG_BSF) += mjpeg2jpeg_bsf.o diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index 4ad50508cb..8fd46a734c 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -30,6 +30,7 @@ extern const AVBitStreamFilter ff_dump_extradata_bsf; extern const AVBitStreamFilter ff_extract_extradata_bsf; extern const AVBitStreamFilter ff_h264_metadata_bsf; extern const AVBitStreamFilter ff_h264_mp4toannexb_bsf; +extern const AVBitStreamFilter ff_h264_redundant_pps_bsf; extern const AVBitStreamFilter ff_hevc_mp4toannexb_bsf; extern const AVBitStreamFilter ff_imx_dump_header_bsf; extern const AVBitStreamFilter ff_mjpeg2jpeg_bsf; diff --git a/libavcodec/h264_redundant_pps_bsf.c b/libavcodec/h264_redundant_pps_bsf.c new file mode 100644 index 0000000000..abc7af788a --- /dev/null +++ b/libavcodec/h264_redundant_pps_bsf.c @@ -0,0 +1,178 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "libavutil/common.h" +#include "libavutil/mem.h" + +#include "bsf.h" +#include "cbs.h" +#include "cbs_h264.h" +#include "h264.h" + + +typedef struct H264RedundantPPSContext { + CodedBitstreamContext input; + CodedBitstreamContext output; + + CodedBitstreamFragment access_unit; + + int global_pic_init_qp; + int current_pic_init_qp; +} H264RedundantPPSContext; + + +static int h264_redundant_pps_fixup_pps(H264RedundantPPSContext *ctx, + H264RawPPS *pps) +{ + // Record the current value of pic_init_qp in order to fix up + // following slices, then overwrite with the global value. + ctx->current_pic_init_qp = pps->pic_init_qp_minus26 + 26; + pps->pic_init_qp_minus26 = ctx->global_pic_init_qp - 26; + + // Some PPSs have this set, so it must be set in all of them. + // (Slices which do not use such a PPS on input will still have + // *_weight_l*flag as zero and therefore write equivalently.) + pps->weighted_pred_flag = 1; + + return 0; +} + +static int h264_redundant_pps_fixup_slice(H264RedundantPPSContext *ctx, + H264RawSliceHeader *slice) +{ + int qp; + + qp = ctx->current_pic_init_qp + slice->slice_qp_delta; + slice->slice_qp_delta = qp - ctx->global_pic_init_qp; + + return 0; +} + +static int h264_redundant_pps_filter(AVBSFContext *bsf, AVPacket *out) +{ + H264RedundantPPSContext *ctx = bsf->priv_data; + AVPacket *in; + CodedBitstreamFragment *au = &ctx->access_unit; + int au_has_sps; + int err, i; + + err = ff_bsf_get_packet(bsf, &in); + if (err < 0) + return err; + + err = ff_cbs_read_packet(&ctx->input, au, in); + if (err < 0) + return err; + + au_has_sps = 0; + for (i = 0; i < au->nb_units; i++) { + CodedBitstreamUnit *nal = &au->units[i]; + + if (nal->type == H264_NAL_SPS) + au_has_sps = 1; + if (nal->type == H264_NAL_PPS) { + h264_redundant_pps_fixup_pps(ctx, nal->content); + if (!au_has_sps) { + av_log(ctx, AV_LOG_VERBOSE, "Deleting redundant PPS " + "at %"PRId64".\n", in->pts); + ff_cbs_delete_unit(&ctx->input, au, i); + } + } + if (nal->type == H264_NAL_SLICE || + nal->type == H264_NAL_IDR_SLICE) { + H264RawSlice *slice = nal->content; + h264_redundant_pps_fixup_slice(ctx, &slice->header); + } + } + + err = ff_cbs_write_packet(&ctx->output, out, au); + if (err < 0) + return err; + + ff_cbs_fragment_uninit(&ctx->output, au); + + err = av_packet_copy_props(out, in); + if (err < 0) + return err; + + av_packet_free(&in); + + return 0; +} + +static int h264_redundant_pps_init(AVBSFContext *bsf) +{ + H264RedundantPPSContext *ctx = bsf->priv_data; + CodedBitstreamFragment *au = &ctx->access_unit; + int err, i; + + err = ff_cbs_init(&ctx->input, AV_CODEC_ID_H264, bsf); + if (err < 0) + return err; + + err = ff_cbs_init(&ctx->output, AV_CODEC_ID_H264, bsf); + if (err < 0) + return err; + + ctx->global_pic_init_qp = 26; + + if (bsf->par_in->extradata) { + err = ff_cbs_read_extradata(&ctx->input, au, bsf->par_in); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read extradata.\n"); + return err; + } + + for (i = 0; i < au->nb_units; i++) { + if (au->units[i].type == H264_NAL_PPS) + h264_redundant_pps_fixup_pps(ctx, au->units[i].content); + } + + err = ff_cbs_write_extradata(&ctx->output, bsf->par_out, au); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write extradata.\n"); + return err; + } + + ff_cbs_fragment_uninit(&ctx->output, au); + } + + return 0; +} + +static void h264_redundant_pps_close(AVBSFContext *bsf) +{ + H264RedundantPPSContext *ctx = bsf->priv_data; + ff_cbs_close(&ctx->input); + ff_cbs_close(&ctx->output); +} + +static const enum AVCodecID h264_redundant_pps_codec_ids[] = { + AV_CODEC_ID_H264, AV_CODEC_ID_NONE, +}; + +const AVBitStreamFilter ff_h264_redundant_pps_bsf = { + .name = "h264_redundant_pps", + .priv_data_size = sizeof(H264RedundantPPSContext), + .init = &h264_redundant_pps_init, + .close = &h264_redundant_pps_close, + .filter = &h264_redundant_pps_filter, + .codec_ids = h264_redundant_pps_codec_ids, +}; From b31a9eae0233325c4b382c657f4b687d5d8b0812 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 23 Jul 2017 16:23:51 +0100 Subject: [PATCH 10/15] lavc: Add hevc_metadata bitstream filter This is able to modify some header metadata found in the VPS/SPS/VUI, and can also add/remove AUDs. --- configure | 1 + doc/bitstream_filters.texi | 54 ++++ libavcodec/Makefile | 1 + libavcodec/bitstream_filters.c | 1 + libavcodec/h265_metadata_bsf.c | 458 +++++++++++++++++++++++++++++++++ 5 files changed, 515 insertions(+) create mode 100644 libavcodec/h265_metadata_bsf.c diff --git a/configure b/configure index bd4555dc87..dfa3867ac0 100755 --- a/configure +++ b/configure @@ -2325,6 +2325,7 @@ vc1_parser_select="vc1dsp" aac_adtstoasc_bsf_select="adts_header" h264_metadata_bsf_select="cbs_h264" h264_redundant_pps_bsf_select="cbs_h264" +hevc_metadata_bsf_select="cbs_h265" mjpeg2jpeg_bsf_select="jpegtables" trace_headers_bsf_select="cbs_h264 cbs_h265" diff --git a/doc/bitstream_filters.texi b/doc/bitstream_filters.texi index e1de3035ef..21371ea9a6 100644 --- a/doc/bitstream_filters.texi +++ b/doc/bitstream_filters.texi @@ -113,6 +113,60 @@ confuse other transformations which require correct extradata. A new single global PPS is created, and all of the redundant PPSs within the stream are removed. +@section hevc_metadata + +Modify metadata embedded in an HEVC stream. + +@table @option +@item aud +Insert or remove AUD NAL units in all access units of the stream. + +@table @samp +@item insert +@item remove +@end table + +@item sample_aspect_ratio +Set the sample aspect ratio in the stream in the VUI parameters. + +@item video_format +@item video_full_range_flag +Set the video format in the stream (see H.265 section E.3.1 and +table E.2). + +@item colour_primaries +@item transfer_characteristics +@item matrix_coefficients +Set the colour description in the stream (see H.265 section E.3.1 +and tables E.3, E.4 and E.5). + +@item chroma_sample_loc_type +Set the chroma sample location in the stream (see H.265 section +E.3.1 and figure E.1). + +@item tick_rate +Set the tick rate in the VPS and VUI parameters (num_units_in_tick / +time_scale). Combined with @option{num_ticks_poc_diff_one}, this can +set a constant framerate in the stream. Note that it is likely to be +overridden by container parameters when the stream is in a container. + +@item num_ticks_poc_diff_one +Set poc_proportional_to_timing_flag in VPS and VUI and use this value +to set num_ticks_poc_diff_one_minus1 (see H.265 sections 7.4.3.1 and +E.3.1). Ignored if @option{tick_rate} is not also set. + +@item crop_left +@item crop_right +@item crop_top +@item crop_bottom +Set the conformance window cropping offsets in the SPS. These values +will replace the current ones if the stream is already cropped. + +These fields are set in pixels. Note that some sizes may not be +representable if the chroma is subsampled (H.265 section 7.4.3.2.1). + +@end table + @section imx_dump_header @section mjpeg2jpeg diff --git a/libavcodec/Makefile b/libavcodec/Makefile index b37a3aa091..e1ae1e4519 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -779,6 +779,7 @@ OBJS-$(CONFIG_EXTRACT_EXTRADATA_BSF) += extract_extradata_bsf.o \ OBJS-$(CONFIG_H264_METADATA_BSF) += h264_metadata_bsf.o OBJS-$(CONFIG_H264_MP4TOANNEXB_BSF) += h264_mp4toannexb_bsf.o OBJS-$(CONFIG_H264_REDUNDANT_PPS_BSF) += h264_redundant_pps_bsf.o +OBJS-$(CONFIG_HEVC_METADATA_BSF) += h265_metadata_bsf.o OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF) += hevc_mp4toannexb_bsf.o OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += imx_dump_header_bsf.o OBJS-$(CONFIG_MJPEG2JPEG_BSF) += mjpeg2jpeg_bsf.o diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c index 8fd46a734c..e90919f602 100644 --- a/libavcodec/bitstream_filters.c +++ b/libavcodec/bitstream_filters.c @@ -31,6 +31,7 @@ extern const AVBitStreamFilter ff_extract_extradata_bsf; extern const AVBitStreamFilter ff_h264_metadata_bsf; extern const AVBitStreamFilter ff_h264_mp4toannexb_bsf; extern const AVBitStreamFilter ff_h264_redundant_pps_bsf; +extern const AVBitStreamFilter ff_hevc_metadata_bsf; extern const AVBitStreamFilter ff_hevc_mp4toannexb_bsf; extern const AVBitStreamFilter ff_imx_dump_header_bsf; extern const AVBitStreamFilter ff_mjpeg2jpeg_bsf; diff --git a/libavcodec/h265_metadata_bsf.c b/libavcodec/h265_metadata_bsf.c new file mode 100644 index 0000000000..aef4a55fb7 --- /dev/null +++ b/libavcodec/h265_metadata_bsf.c @@ -0,0 +1,458 @@ +/* + * This file is part of Libav. + * + * Libav is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * Libav is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with Libav; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/common.h" +#include "libavutil/opt.h" + +#include "bsf.h" +#include "cbs.h" +#include "cbs_h265.h" +#include "hevc.h" + +enum { + PASS, + INSERT, + REMOVE, +}; + +typedef struct H265MetadataContext { + const AVClass *class; + + CodedBitstreamContext cbc; + CodedBitstreamFragment access_unit; + + H265RawAUD aud_nal; + + int aud; + + AVRational sample_aspect_ratio; + + int video_format; + int video_full_range_flag; + int colour_primaries; + int transfer_characteristics; + int matrix_coefficients; + + int chroma_sample_loc_type; + + AVRational tick_rate; + int poc_proportional_to_timing_flag; + int num_ticks_poc_diff_one; + + int crop_left; + int crop_right; + int crop_top; + int crop_bottom; +} H265MetadataContext; + + +static int h265_metadata_update_vps(AVBSFContext *bsf, + H265RawVPS *vps) +{ + H265MetadataContext *ctx = bsf->priv_data; + + if (ctx->tick_rate.num && ctx->tick_rate.den) { + int num, den; + + av_reduce(&num, &den, ctx->tick_rate.num, ctx->tick_rate.den, + UINT32_MAX > INT_MAX ? UINT32_MAX : INT_MAX); + + vps->vps_time_scale = num; + vps->vps_num_units_in_tick = den; + + vps->vps_timing_info_present_flag = 1; + + if (ctx->num_ticks_poc_diff_one > 0) { + vps->vps_num_ticks_poc_diff_one_minus1 = + ctx->num_ticks_poc_diff_one - 1; + vps->vps_poc_proportional_to_timing_flag = 1; + } else if (ctx->num_ticks_poc_diff_one == 0) { + vps->vps_poc_proportional_to_timing_flag = 0; + } + } + + return 0; +} + +static int h265_metadata_update_sps(AVBSFContext *bsf, + H265RawSPS *sps) +{ + H265MetadataContext *ctx = bsf->priv_data; + int need_vui = 0; + int crop_unit_x, crop_unit_y; + + if (ctx->sample_aspect_ratio.num && ctx->sample_aspect_ratio.den) { + // Table E-1. + static const AVRational sar_idc[] = { + { 0, 0 }, // Unspecified (never written here). + { 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 }, + { 40, 33 }, { 24, 11 }, { 20, 11 }, { 32, 11 }, + { 80, 33 }, { 18, 11 }, { 15, 11 }, { 64, 33 }, + { 160, 99 }, { 4, 3 }, { 3, 2 }, { 2, 1 }, + }; + int num, den, i; + + av_reduce(&num, &den, ctx->sample_aspect_ratio.num, + ctx->sample_aspect_ratio.den, 65535); + + for (i = 1; i < FF_ARRAY_ELEMS(sar_idc); i++) { + if (num == sar_idc[i].num && + den == sar_idc[i].den) + break; + } + if (i == FF_ARRAY_ELEMS(sar_idc)) { + sps->vui.aspect_ratio_idc = 255; + sps->vui.sar_width = num; + sps->vui.sar_height = den; + } else { + sps->vui.aspect_ratio_idc = i; + } + sps->vui.aspect_ratio_info_present_flag = 1; + need_vui = 1; + } + +#define SET_OR_INFER(field, value, present_flag, infer) do { \ + if (value >= 0) { \ + field = value; \ + need_vui = 1; \ + } else if (!present_flag) \ + field = infer; \ + } while (0) + + if (ctx->video_format >= 0 || + ctx->video_full_range_flag >= 0 || + ctx->colour_primaries >= 0 || + ctx->transfer_characteristics >= 0 || + ctx->matrix_coefficients >= 0) { + + SET_OR_INFER(sps->vui.video_format, ctx->video_format, + sps->vui.video_signal_type_present_flag, 5); + + SET_OR_INFER(sps->vui.video_full_range_flag, + ctx->video_full_range_flag, + sps->vui.video_signal_type_present_flag, 0); + + if (ctx->colour_primaries >= 0 || + ctx->transfer_characteristics >= 0 || + ctx->matrix_coefficients >= 0) { + + SET_OR_INFER(sps->vui.colour_primaries, + ctx->colour_primaries, + sps->vui.colour_description_present_flag, 2); + + SET_OR_INFER(sps->vui.transfer_characteristics, + ctx->transfer_characteristics, + sps->vui.colour_description_present_flag, 2); + + SET_OR_INFER(sps->vui.matrix_coefficients, + ctx->matrix_coefficients, + sps->vui.colour_description_present_flag, 2); + + sps->vui.colour_description_present_flag = 1; + } + sps->vui.video_signal_type_present_flag = 1; + need_vui = 1; + } + + if (ctx->chroma_sample_loc_type >= 0) { + sps->vui.chroma_sample_loc_type_top_field = + ctx->chroma_sample_loc_type; + sps->vui.chroma_sample_loc_type_bottom_field = + ctx->chroma_sample_loc_type; + sps->vui.chroma_loc_info_present_flag = 1; + need_vui = 1; + } + + if (ctx->tick_rate.num && ctx->tick_rate.den) { + int num, den; + + av_reduce(&num, &den, ctx->tick_rate.num, ctx->tick_rate.den, + UINT32_MAX > INT_MAX ? UINT32_MAX : INT_MAX); + + sps->vui.vui_time_scale = num; + sps->vui.vui_num_units_in_tick = den; + + sps->vui.vui_timing_info_present_flag = 1; + need_vui = 1; + + if (ctx->num_ticks_poc_diff_one > 0) { + sps->vui.vui_num_ticks_poc_diff_one_minus1 = + ctx->num_ticks_poc_diff_one - 1; + sps->vui.vui_poc_proportional_to_timing_flag = 1; + } else if (ctx->num_ticks_poc_diff_one == 0) { + sps->vui.vui_poc_proportional_to_timing_flag = 0; + } + } + + if (sps->separate_colour_plane_flag || sps->chroma_format_idc == 0) { + crop_unit_x = 1; + crop_unit_y = 1; + } else { + crop_unit_x = 1 + (sps->chroma_format_idc < 3); + crop_unit_y = 1 + (sps->chroma_format_idc < 2); + } +#define CROP(border, unit) do { \ + if (ctx->crop_ ## border >= 0) { \ + if (ctx->crop_ ## border % unit != 0) { \ + av_log(bsf, AV_LOG_ERROR, "Invalid value for crop_%s: " \ + "must be a multiple of %d.\n", #border, unit); \ + return AVERROR(EINVAL); \ + } \ + sps->conf_win_ ## border ## _offset = \ + ctx->crop_ ## border / unit; \ + sps->conformance_window_flag = 1; \ + } \ + } while (0) + CROP(left, crop_unit_x); + CROP(right, crop_unit_x); + CROP(top, crop_unit_y); + CROP(bottom, crop_unit_y); +#undef CROP + + if (need_vui) + sps->vui_parameters_present_flag = 1; + + return 0; +} + +static int h265_metadata_filter(AVBSFContext *bsf, AVPacket *out) +{ + H265MetadataContext *ctx = bsf->priv_data; + AVPacket *in = NULL; + CodedBitstreamFragment *au = &ctx->access_unit; + int err, i; + + err = ff_bsf_get_packet(bsf, &in); + if (err < 0) + goto fail; + + err = ff_cbs_read_packet(&ctx->cbc, au, in); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read packet.\n"); + goto fail; + } + + if (au->nb_units == 0) { + av_log(bsf, AV_LOG_ERROR, "No NAL units in packet.\n"); + err = AVERROR_INVALIDDATA; + goto fail; + } + + // If an AUD is present, it must be the first NAL unit. + if (au->units[0].type == HEVC_NAL_AUD) { + if (ctx->aud == REMOVE) + ff_cbs_delete_unit(&ctx->cbc, au, 0); + } else { + if (ctx->aud == INSERT) { + H265RawAUD *aud = &ctx->aud_nal; + int pic_type = 0, temporal_id = 8, layer_id = 0; + + for (i = 0; i < au->nb_units; i++) { + const H265RawNALUnitHeader *nal = au->units[i].content; + if (!nal) + continue; + if (nal->nuh_temporal_id_plus1 < temporal_id + 1) + temporal_id = nal->nuh_temporal_id_plus1 - 1; + + if (au->units[i].type <= HEVC_NAL_RSV_VCL31) { + const H265RawSlice *slice = au->units[i].content; + layer_id = nal->nuh_layer_id; + if (slice->header.slice_type == HEVC_SLICE_B && + pic_type < 2) + pic_type = 2; + if (slice->header.slice_type == HEVC_SLICE_P && + pic_type < 1) + pic_type = 1; + } + } + + aud->nal_unit_header = (H265RawNALUnitHeader) { + .nal_unit_type = HEVC_NAL_AUD, + .nuh_layer_id = layer_id, + .nuh_temporal_id_plus1 = temporal_id + 1, + }; + aud->pic_type = pic_type; + + err = ff_cbs_insert_unit_content(&ctx->cbc, au, + 0, HEVC_NAL_AUD, aud); + if (err) { + av_log(bsf, AV_LOG_ERROR, "Failed to insert AUD.\n"); + goto fail; + } + } + } + + for (i = 0; i < au->nb_units; i++) { + if (au->units[i].type == HEVC_NAL_VPS) { + err = h265_metadata_update_vps(bsf, au->units[i].content); + if (err < 0) + goto fail; + } + if (au->units[i].type == HEVC_NAL_SPS) { + err = h265_metadata_update_sps(bsf, au->units[i].content); + if (err < 0) + goto fail; + } + } + + err = ff_cbs_write_packet(&ctx->cbc, out, au); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write packet.\n"); + goto fail; + } + + err = av_packet_copy_props(out, in); + if (err < 0) + goto fail; + + err = 0; +fail: + ff_cbs_fragment_uninit(&ctx->cbc, au); + + av_packet_free(&in); + + return err; +} + +static int h265_metadata_init(AVBSFContext *bsf) +{ + H265MetadataContext *ctx = bsf->priv_data; + CodedBitstreamFragment *au = &ctx->access_unit; + int err, i; + + err = ff_cbs_init(&ctx->cbc, AV_CODEC_ID_HEVC, bsf); + if (err < 0) + return err; + + if (bsf->par_in->extradata) { + err = ff_cbs_read_extradata(&ctx->cbc, au, bsf->par_in); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to read extradata.\n"); + goto fail; + } + + for (i = 0; i < au->nb_units; i++) { + if (au->units[i].type == HEVC_NAL_VPS) { + err = h265_metadata_update_vps(bsf, au->units[i].content); + if (err < 0) + goto fail; + } + if (au->units[i].type == HEVC_NAL_SPS) { + err = h265_metadata_update_sps(bsf, au->units[i].content); + if (err < 0) + goto fail; + } + } + + err = ff_cbs_write_extradata(&ctx->cbc, bsf->par_out, au); + if (err < 0) { + av_log(bsf, AV_LOG_ERROR, "Failed to write extradata.\n"); + goto fail; + } + } + + err = 0; +fail: + ff_cbs_fragment_uninit(&ctx->cbc, au); + return err; +} + +static void h265_metadata_close(AVBSFContext *bsf) +{ + H265MetadataContext *ctx = bsf->priv_data; + ff_cbs_close(&ctx->cbc); +} + +#define OFFSET(x) offsetof(H265MetadataContext, x) +static const AVOption h265_metadata_options[] = { + { "aud", "Access Unit Delimiter NAL units", + OFFSET(aud), AV_OPT_TYPE_INT, + { .i64 = PASS }, PASS, REMOVE, 0, "aud" }, + { "pass", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = PASS }, .unit = "aud" }, + { "insert", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = INSERT }, .unit = "aud" }, + { "remove", NULL, 0, AV_OPT_TYPE_CONST, { .i64 = REMOVE }, .unit = "aud" }, + + { "sample_aspect_ratio", "Set sample aspect ratio (table E-1)", + OFFSET(sample_aspect_ratio), AV_OPT_TYPE_RATIONAL, + { .i64 = 0 }, 0, 65535 }, + + { "video_format", "Set video format (table E-2)", + OFFSET(video_format), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 7 }, + { "video_full_range_flag", "Set video full range flag", + OFFSET(video_full_range_flag), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 1 }, + { "colour_primaries", "Set colour primaries (table E-3)", + OFFSET(colour_primaries), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 255 }, + { "transfer_characteristics", "Set transfer characteristics (table E-4)", + OFFSET(transfer_characteristics), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 255 }, + { "matrix_coefficients", "Set matrix coefficients (table E-5)", + OFFSET(matrix_coefficients), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 255 }, + + { "chroma_sample_loc_type", "Set chroma sample location type (figure E-1)", + OFFSET(chroma_sample_loc_type), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, 6 }, + + { "tick_rate", + "Set VPS and VUI tick rate (num_units_in_tick / time_scale)", + OFFSET(tick_rate), AV_OPT_TYPE_RATIONAL, + { .i64 = 0 }, 0, UINT_MAX }, + { "num_ticks_poc_diff_one", + "Set VPS and VUI number of ticks per POC increment", + OFFSET(num_ticks_poc_diff_one), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, INT_MAX }, + + { "crop_left", "Set left border crop offset", + OFFSET(crop_left), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, HEVC_MAX_WIDTH }, + { "crop_right", "Set right border crop offset", + OFFSET(crop_right), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, HEVC_MAX_WIDTH }, + { "crop_top", "Set top border crop offset", + OFFSET(crop_top), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, HEVC_MAX_HEIGHT }, + { "crop_bottom", "Set bottom border crop offset", + OFFSET(crop_bottom), AV_OPT_TYPE_INT, + { .i64 = -1 }, -1, HEVC_MAX_HEIGHT }, + + { NULL } +}; + +static const AVClass h265_metadata_class = { + .class_name = "h265_metadata_bsf", + .item_name = av_default_item_name, + .option = h265_metadata_options, + .version = LIBAVCODEC_VERSION_MAJOR, +}; + +static const enum AVCodecID h265_metadata_codec_ids[] = { + AV_CODEC_ID_HEVC, AV_CODEC_ID_NONE, +}; + +const AVBitStreamFilter ff_hevc_metadata_bsf = { + .name = "hevc_metadata", + .priv_data_size = sizeof(H265MetadataContext), + .priv_class = &h265_metadata_class, + .init = &h265_metadata_init, + .close = &h265_metadata_close, + .filter = &h265_metadata_filter, + .codec_ids = h265_metadata_codec_ids, +}; From 7a4fac5e91789b73e07bd4ad20493cfde028df76 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 7 May 2017 15:01:42 +0100 Subject: [PATCH 11/15] vaapi_h264: Convert to use coded bitstream infrastructure --- configure | 2 +- libavcodec/Makefile | 2 +- libavcodec/vaapi_encode_h264.c | 1475 +++++++++++++------------------- 3 files changed, 576 insertions(+), 903 deletions(-) diff --git a/configure b/configure index dfa3867ac0..b6fa8f6827 100755 --- a/configure +++ b/configure @@ -2285,7 +2285,7 @@ h264_omx_encoder_deps="omx" h264_qsv_decoder_select="h264_mp4toannexb_bsf h264_parser qsvdec h264_qsv_hwaccel" h264_qsv_encoder_select="qsvenc" h264_vaapi_encoder_deps="VAEncPictureParameterBufferH264" -h264_vaapi_encoder_select="vaapi_encode golomb" +h264_vaapi_encoder_select="cbs_h264 vaapi_encode" hevc_nvenc_encoder_deps="nvenc" hevc_qsv_decoder_select="hevc_mp4toannexb_bsf hevc_parser hevc_qsv_hwaccel qsvdec" hevc_qsv_encoder_select="hevc_ps qsvenc" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index e1ae1e4519..456ee9bb03 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -275,7 +275,7 @@ OBJS-$(CONFIG_H264_NVENC_ENCODER) += nvenc_h264.o 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_VAAPI_ENCODER) += vaapi_encode_h264.o vaapi_encode_h26x.o +OBJS-$(CONFIG_H264_VAAPI_ENCODER) += vaapi_encode_h264.o OBJS-$(CONFIG_HAP_DECODER) += hapdec.o hap.o OBJS-$(CONFIG_HAP_ENCODER) += hapenc.o hap.o OBJS-$(CONFIG_HEVC_DECODER) += hevcdec.o hevc_mvs.o hevc_sei.o \ diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c index e08cf61167..b48070c950 100644 --- a/libavcodec/vaapi_encode_h264.c +++ b/libavcodec/vaapi_encode_h264.c @@ -16,128 +16,36 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include #include #include "libavutil/avassert.h" +#include "libavutil/common.h" #include "libavutil/internal.h" #include "libavutil/opt.h" -#include "libavutil/pixfmt.h" #include "avcodec.h" +#include "cbs.h" +#include "cbs_h264.h" #include "h264.h" #include "h264_sei.h" #include "internal.h" #include "vaapi_encode.h" -#include "vaapi_encode_h26x.h" enum { - SLICE_TYPE_P = 0, - SLICE_TYPE_B = 1, - SLICE_TYPE_I = 2, - SLICE_TYPE_SP = 3, - SLICE_TYPE_SI = 4, + SEI_TIMING = 0x01, + SEI_IDENTIFIER = 0x02, }; -// This structure contains all possibly-useful per-sequence syntax elements -// which are not already contained in the various VAAPI structures. -typedef struct VAAPIEncodeH264MiscSequenceParams { - unsigned int profile_idc; - char constraint_set0_flag; - char constraint_set1_flag; - char constraint_set2_flag; - char constraint_set3_flag; - char constraint_set4_flag; - char constraint_set5_flag; - - char separate_colour_plane_flag; - char qpprime_y_zero_transform_bypass_flag; - - char gaps_in_frame_num_allowed_flag; - char delta_pic_order_always_zero_flag; - char bottom_field_pic_order_in_frame_present_flag; - - unsigned int num_slice_groups_minus1; - unsigned int slice_group_map_type; - - int pic_init_qs_minus26; - - char overscan_info_present_flag; - char overscan_appropriate_flag; - - char video_signal_type_present_flag; - unsigned int video_format; - char video_full_range_flag; - char colour_description_present_flag; - unsigned int colour_primaries; - unsigned int transfer_characteristics; - unsigned int matrix_coefficients; - - char chroma_loc_info_present_flag; - unsigned int chroma_sample_loc_type_top_field; - unsigned int chroma_sample_loc_type_bottom_field; - - // Some timing elements are in VAEncSequenceParameterBufferH264. - char fixed_frame_rate_flag; - - char nal_hrd_parameters_present_flag; - char vcl_hrd_parameters_present_flag; - char low_delay_hrd_flag; - char pic_struct_present_flag; - - char motion_vectors_over_pic_boundaries_flag; - unsigned int max_bytes_per_pic_denom; - unsigned int max_bits_per_mb_denom; - unsigned int max_num_reorder_frames; - unsigned int max_dec_pic_buffering; - - unsigned int cpb_cnt_minus1; - unsigned int bit_rate_scale; - unsigned int cpb_size_scale; - unsigned int bit_rate_value_minus1[32]; - unsigned int cpb_size_value_minus1[32]; - char cbr_flag[32]; - unsigned int initial_cpb_removal_delay_length_minus1; - unsigned int cpb_removal_delay_length_minus1; - unsigned int dpb_output_delay_length_minus1; - unsigned int time_offset_length; - - unsigned int initial_cpb_removal_delay; - unsigned int initial_cpb_removal_delay_offset; - - unsigned int pic_struct; -} VAAPIEncodeH264MiscSequenceParams; - -// This structure contains all possibly-useful per-slice syntax elements -// which are not already contained in the various VAAPI structures. -typedef struct VAAPIEncodeH264MiscSliceParams { - unsigned int nal_unit_type; - unsigned int nal_ref_idc; - - unsigned int colour_plane_id; - char field_pic_flag; - char bottom_field_flag; - - unsigned int redundant_pic_cnt; - - char sp_for_switch_flag; - int slice_qs_delta; - - char ref_pic_list_modification_flag_l0; - char ref_pic_list_modification_flag_l1; - - char no_output_of_prior_pics_flag; - char long_term_reference_flag; - char adaptive_ref_pic_marking_mode_flag; -} VAAPIEncodeH264MiscSliceParams; - -typedef struct VAAPIEncodeH264Slice { - VAAPIEncodeH264MiscSliceParams misc_slice_params; -} VAAPIEncodeH264Slice; +// Random (version 4) ISO 11578 UUID. +static const uint8_t vaapi_encode_h264_sei_identifier_uuid[16] = { + 0x59, 0x94, 0x8b, 0x28, 0x11, 0xec, 0x45, 0xaf, + 0x96, 0x75, 0x19, 0xd4, 0x1f, 0xea, 0xa9, 0x4d, +}; typedef struct VAAPIEncodeH264Context { - VAAPIEncodeH264MiscSequenceParams misc_sequence_params; - int mb_width; int mb_height; @@ -145,582 +53,108 @@ typedef struct VAAPIEncodeH264Context { int fixed_qp_p; int fixed_qp_b; + H264RawSPS sps; + H264RawPPS pps; + H264RawSEI sei; + H264RawSlice slice; + + H264RawSEIBufferingPeriod buffering_period; + H264RawSEIPicTiming pic_timing; + H264RawSEIUserDataUnregistered identifier; + char *identifier_string; + + int frame_num; + int pic_order_cnt; int next_frame_num; int64_t last_idr_frame; int64_t idr_pic_count; + int primary_pic_type; + int slice_type; + int cpb_delay; int dpb_delay; - // Rate control configuration. - int send_timing_sei; + CodedBitstreamContext cbc; + CodedBitstreamFragment current_access_unit; + int sei_needed; } VAAPIEncodeH264Context; typedef struct VAAPIEncodeH264Options { int qp; int quality; int low_power; + int sei; } VAAPIEncodeH264Options; -#define vseq_var(name) vseq->name, name -#define vseq_field(name) vseq->seq_fields.bits.name, name -#define vvui_field(name) vseq->vui_fields.bits.name, name -#define vpic_var(name) vpic->name, name -#define vpic_field(name) vpic->pic_fields.bits.name, name -#define vslice_var(name) vslice->name, name -#define vslice_field(name) vslice->slice_fields.bits.name, name -#define mseq_var(name) mseq->name, name -#define mslice_var(name) mslice->name, name - -static void vaapi_encode_h264_write_nal_header(PutBitContext *pbc, - int nal_unit_type, int nal_ref_idc) -{ - u(1, 0, forbidden_zero_bit); - u(2, nal_ref_idc, nal_ref_idc); - u(5, nal_unit_type, nal_unit_type); -} - -static void vaapi_encode_h264_write_trailing_rbsp(PutBitContext *pbc) -{ - u(1, 1, rbsp_stop_one_bit); - while (put_bits_count(pbc) & 7) - u(1, 0, rbsp_alignment_zero_bit); -} - -static void vaapi_encode_h264_write_vui(PutBitContext *pbc, - VAAPIEncodeContext *ctx) -{ - VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params; - VAAPIEncodeH264Context *priv = ctx->priv_data; - VAAPIEncodeH264MiscSequenceParams *mseq = &priv->misc_sequence_params; - int i; - - u(1, vvui_field(aspect_ratio_info_present_flag)); - if (vseq->vui_fields.bits.aspect_ratio_info_present_flag) { - u(8, vseq_var(aspect_ratio_idc)); - if (vseq->aspect_ratio_idc == 255) { - u(16, vseq_var(sar_width)); - u(16, vseq_var(sar_height)); - } - } - - u(1, mseq_var(overscan_info_present_flag)); - if (mseq->overscan_info_present_flag) - u(1, mseq_var(overscan_appropriate_flag)); - - u(1, mseq_var(video_signal_type_present_flag)); - if (mseq->video_signal_type_present_flag) { - u(3, mseq_var(video_format)); - u(1, mseq_var(video_full_range_flag)); - u(1, mseq_var(colour_description_present_flag)); - if (mseq->colour_description_present_flag) { - u(8, mseq_var(colour_primaries)); - u(8, mseq_var(transfer_characteristics)); - u(8, mseq_var(matrix_coefficients)); - } - } - - u(1, mseq_var(chroma_loc_info_present_flag)); - if (mseq->chroma_loc_info_present_flag) { - ue(mseq_var(chroma_sample_loc_type_top_field)); - ue(mseq_var(chroma_sample_loc_type_bottom_field)); - } - - u(1, vvui_field(timing_info_present_flag)); - if (vseq->vui_fields.bits.timing_info_present_flag) { - u(32, vseq_var(num_units_in_tick)); - u(32, vseq_var(time_scale)); - u(1, mseq_var(fixed_frame_rate_flag)); - } - - u(1, mseq_var(nal_hrd_parameters_present_flag)); - if (mseq->nal_hrd_parameters_present_flag) { - ue(mseq_var(cpb_cnt_minus1)); - u(4, mseq_var(bit_rate_scale)); - u(4, mseq_var(cpb_size_scale)); - for (i = 0; i <= mseq->cpb_cnt_minus1; i++) { - ue(mseq_var(bit_rate_value_minus1[i])); - ue(mseq_var(cpb_size_value_minus1[i])); - u(1, mseq_var(cbr_flag[i])); - } - u(5, mseq_var(initial_cpb_removal_delay_length_minus1)); - u(5, mseq_var(cpb_removal_delay_length_minus1)); - u(5, mseq_var(dpb_output_delay_length_minus1)); - u(5, mseq_var(time_offset_length)); - } - u(1, mseq_var(vcl_hrd_parameters_present_flag)); - if (mseq->vcl_hrd_parameters_present_flag) { - av_assert0(0 && "vcl hrd parameters not supported"); - } - - if (mseq->nal_hrd_parameters_present_flag || - mseq->vcl_hrd_parameters_present_flag) - u(1, mseq_var(low_delay_hrd_flag)); - u(1, mseq_var(pic_struct_present_flag)); - - u(1, vvui_field(bitstream_restriction_flag)); - if (vseq->vui_fields.bits.bitstream_restriction_flag) { - u(1, mseq_var(motion_vectors_over_pic_boundaries_flag)); - ue(mseq_var(max_bytes_per_pic_denom)); - ue(mseq_var(max_bits_per_mb_denom)); - ue(vvui_field(log2_max_mv_length_horizontal)); - ue(vvui_field(log2_max_mv_length_vertical)); - ue(mseq_var(max_num_reorder_frames)); - ue(mseq_var(max_dec_pic_buffering)); - } -} - -static void vaapi_encode_h264_write_sps(PutBitContext *pbc, - VAAPIEncodeContext *ctx) -{ - VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params; - VAAPIEncodeH264Context *priv = ctx->priv_data; - VAAPIEncodeH264MiscSequenceParams *mseq = &priv->misc_sequence_params; - int i; - - vaapi_encode_h264_write_nal_header(pbc, H264_NAL_SPS, 3); - - u(8, mseq_var(profile_idc)); - u(1, mseq_var(constraint_set0_flag)); - u(1, mseq_var(constraint_set1_flag)); - u(1, mseq_var(constraint_set2_flag)); - u(1, mseq_var(constraint_set3_flag)); - u(1, mseq_var(constraint_set4_flag)); - u(1, mseq_var(constraint_set5_flag)); - u(2, 0, reserved_zero_2bits); - - u(8, vseq_var(level_idc)); - - ue(vseq_var(seq_parameter_set_id)); - - if (mseq->profile_idc == 100 || mseq->profile_idc == 110 || - mseq->profile_idc == 122 || mseq->profile_idc == 244 || - mseq->profile_idc == 44 || mseq->profile_idc == 83 || - mseq->profile_idc == 86 || mseq->profile_idc == 118 || - mseq->profile_idc == 128 || mseq->profile_idc == 138) { - ue(vseq_field(chroma_format_idc)); - - if (vseq->seq_fields.bits.chroma_format_idc == 3) - u(1, mseq_var(separate_colour_plane_flag)); - - ue(vseq_var(bit_depth_luma_minus8)); - ue(vseq_var(bit_depth_chroma_minus8)); - - u(1, mseq_var(qpprime_y_zero_transform_bypass_flag)); - - u(1, vseq_field(seq_scaling_matrix_present_flag)); - if (vseq->seq_fields.bits.seq_scaling_matrix_present_flag) { - av_assert0(0 && "scaling matrices not supported"); - } - } - - ue(vseq_field(log2_max_frame_num_minus4)); - ue(vseq_field(pic_order_cnt_type)); - - if (vseq->seq_fields.bits.pic_order_cnt_type == 0) { - ue(vseq_field(log2_max_pic_order_cnt_lsb_minus4)); - } else if (vseq->seq_fields.bits.pic_order_cnt_type == 1) { - u(1, mseq_var(delta_pic_order_always_zero_flag)); - se(vseq_var(offset_for_non_ref_pic)); - se(vseq_var(offset_for_top_to_bottom_field)); - ue(vseq_var(num_ref_frames_in_pic_order_cnt_cycle)); - - for (i = 0; i < vseq->num_ref_frames_in_pic_order_cnt_cycle; i++) - se(vseq_var(offset_for_ref_frame[i])); - } - - ue(vseq_var(max_num_ref_frames)); - u(1, mseq_var(gaps_in_frame_num_allowed_flag)); - - ue(vseq->picture_width_in_mbs - 1, pic_width_in_mbs_minus1); - ue(vseq->picture_height_in_mbs - 1, pic_height_in_mbs_minus1); - - u(1, vseq_field(frame_mbs_only_flag)); - if (!vseq->seq_fields.bits.frame_mbs_only_flag) - u(1, vseq_field(mb_adaptive_frame_field_flag)); - - u(1, vseq_field(direct_8x8_inference_flag)); - - u(1, vseq_var(frame_cropping_flag)); - if (vseq->frame_cropping_flag) { - ue(vseq_var(frame_crop_left_offset)); - ue(vseq_var(frame_crop_right_offset)); - ue(vseq_var(frame_crop_top_offset)); - ue(vseq_var(frame_crop_bottom_offset)); - } - - u(1, vseq_var(vui_parameters_present_flag)); - if (vseq->vui_parameters_present_flag) - vaapi_encode_h264_write_vui(pbc, ctx); - - vaapi_encode_h264_write_trailing_rbsp(pbc); -} - -static void vaapi_encode_h264_write_pps(PutBitContext *pbc, - VAAPIEncodeContext *ctx) -{ - VAEncPictureParameterBufferH264 *vpic = ctx->codec_picture_params; - VAAPIEncodeH264Context *priv = ctx->priv_data; - VAAPIEncodeH264MiscSequenceParams *mseq = &priv->misc_sequence_params; - - vaapi_encode_h264_write_nal_header(pbc, H264_NAL_PPS, 3); - - ue(vpic_var(pic_parameter_set_id)); - ue(vpic_var(seq_parameter_set_id)); - - u(1, vpic_field(entropy_coding_mode_flag)); - u(1, mseq_var(bottom_field_pic_order_in_frame_present_flag)); - - ue(mseq_var(num_slice_groups_minus1)); - if (mseq->num_slice_groups_minus1 > 0) { - ue(mseq_var(slice_group_map_type)); - av_assert0(0 && "slice groups not supported"); - } - - ue(vpic_var(num_ref_idx_l0_active_minus1)); - ue(vpic_var(num_ref_idx_l1_active_minus1)); - - u(1, vpic_field(weighted_pred_flag)); - u(2, vpic_field(weighted_bipred_idc)); - - se(vpic->pic_init_qp - 26, pic_init_qp_minus26); - se(mseq_var(pic_init_qs_minus26)); - se(vpic_var(chroma_qp_index_offset)); - - u(1, vpic_field(deblocking_filter_control_present_flag)); - u(1, vpic_field(constrained_intra_pred_flag)); - u(1, vpic_field(redundant_pic_cnt_present_flag)); - u(1, vpic_field(transform_8x8_mode_flag)); - - u(1, vpic_field(pic_scaling_matrix_present_flag)); - if (vpic->pic_fields.bits.pic_scaling_matrix_present_flag) { - av_assert0(0 && "scaling matrices not supported"); - } - - se(vpic_var(second_chroma_qp_index_offset)); - - vaapi_encode_h264_write_trailing_rbsp(pbc); -} - -static void vaapi_encode_h264_write_slice_header2(PutBitContext *pbc, - VAAPIEncodeContext *ctx, - VAAPIEncodePicture *pic, - VAAPIEncodeSlice *slice) -{ - VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params; - VAEncPictureParameterBufferH264 *vpic = pic->codec_picture_params; - VAEncSliceParameterBufferH264 *vslice = slice->codec_slice_params; - VAAPIEncodeH264Context *priv = ctx->priv_data; - VAAPIEncodeH264MiscSequenceParams *mseq = &priv->misc_sequence_params; - VAAPIEncodeH264Slice *pslice = slice->priv_data; - VAAPIEncodeH264MiscSliceParams *mslice = &pslice->misc_slice_params; - - vaapi_encode_h264_write_nal_header(pbc, mslice->nal_unit_type, - mslice->nal_ref_idc); - - ue(vslice->macroblock_address, first_mb_in_slice); - ue(vslice_var(slice_type)); - ue(vpic_var(pic_parameter_set_id)); - - if (mseq->separate_colour_plane_flag) { - u(2, mslice_var(colour_plane_id)); - } - - u(4 + vseq->seq_fields.bits.log2_max_frame_num_minus4, - (vpic->frame_num & - ((1 << (4 + vseq->seq_fields.bits.log2_max_frame_num_minus4)) - 1)), - frame_num); - - if (!vseq->seq_fields.bits.frame_mbs_only_flag) { - u(1, mslice_var(field_pic_flag)); - if (mslice->field_pic_flag) - u(1, mslice_var(bottom_field_flag)); - } - - if (vpic->pic_fields.bits.idr_pic_flag) { - ue(vslice_var(idr_pic_id)); - } - - if (vseq->seq_fields.bits.pic_order_cnt_type == 0) { - u(4 + vseq->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4, - vslice_var(pic_order_cnt_lsb)); - if (mseq->bottom_field_pic_order_in_frame_present_flag && - !mslice->field_pic_flag) { - se(vslice_var(delta_pic_order_cnt_bottom)); - } - } - - if (vseq->seq_fields.bits.pic_order_cnt_type == 1 && - !vseq->seq_fields.bits.delta_pic_order_always_zero_flag) { - se(vslice_var(delta_pic_order_cnt[0])); - if (mseq->bottom_field_pic_order_in_frame_present_flag && - !mslice->field_pic_flag) { - se(vslice_var(delta_pic_order_cnt[1])); - } - } - - if (vpic->pic_fields.bits.redundant_pic_cnt_present_flag) { - ue(mslice_var(redundant_pic_cnt)); - } - - if (vslice->slice_type == SLICE_TYPE_B) { - u(1, vslice_var(direct_spatial_mv_pred_flag)); - } - - if (vslice->slice_type == SLICE_TYPE_P || - vslice->slice_type == SLICE_TYPE_SP || - vslice->slice_type == SLICE_TYPE_B) { - u(1, vslice_var(num_ref_idx_active_override_flag)); - if (vslice->num_ref_idx_active_override_flag) { - ue(vslice_var(num_ref_idx_l0_active_minus1)); - if (vslice->slice_type == SLICE_TYPE_B) - ue(vslice_var(num_ref_idx_l1_active_minus1)); - } - } - - if (mslice->nal_unit_type == 20 || mslice->nal_unit_type == 21) { - av_assert0(0 && "no MVC support"); - } else { - if (vslice->slice_type % 5 != 2 && vslice->slice_type % 5 != 4) { - u(1, mslice_var(ref_pic_list_modification_flag_l0)); - if (mslice->ref_pic_list_modification_flag_l0) { - av_assert0(0 && "ref pic list modification"); - } - } - if (vslice->slice_type % 5 == 1) { - u(1, mslice_var(ref_pic_list_modification_flag_l1)); - if (mslice->ref_pic_list_modification_flag_l1) { - av_assert0(0 && "ref pic list modification"); - } - } - } - - if ((vpic->pic_fields.bits.weighted_pred_flag && - (vslice->slice_type == SLICE_TYPE_P || - vslice->slice_type == SLICE_TYPE_SP)) || - (vpic->pic_fields.bits.weighted_bipred_idc == 1 && - vslice->slice_type == SLICE_TYPE_B)) { - av_assert0(0 && "prediction weights not supported"); - } - - av_assert0(mslice->nal_ref_idc > 0 == - vpic->pic_fields.bits.reference_pic_flag); - if (mslice->nal_ref_idc != 0) { - if (vpic->pic_fields.bits.idr_pic_flag) { - u(1, mslice_var(no_output_of_prior_pics_flag)); - u(1, mslice_var(long_term_reference_flag)); - } else { - u(1, mslice_var(adaptive_ref_pic_marking_mode_flag)); - if (mslice->adaptive_ref_pic_marking_mode_flag) { - av_assert0(0 && "MMCOs not supported"); - } - } - } - - if (vpic->pic_fields.bits.entropy_coding_mode_flag && - vslice->slice_type != SLICE_TYPE_I && - vslice->slice_type != SLICE_TYPE_SI) { - ue(vslice_var(cabac_init_idc)); - } - - se(vslice_var(slice_qp_delta)); - if (vslice->slice_type == SLICE_TYPE_SP || - vslice->slice_type == SLICE_TYPE_SI) { - if (vslice->slice_type == SLICE_TYPE_SP) - u(1, mslice_var(sp_for_switch_flag)); - se(mslice_var(slice_qs_delta)); - } - - if (vpic->pic_fields.bits.deblocking_filter_control_present_flag) { - ue(vslice_var(disable_deblocking_filter_idc)); - if (vslice->disable_deblocking_filter_idc != 1) { - se(vslice_var(slice_alpha_c0_offset_div2)); - se(vslice_var(slice_beta_offset_div2)); - } - } - - if (mseq->num_slice_groups_minus1 > 0 && - mseq->slice_group_map_type >= 3 && mseq->slice_group_map_type <= 5) { - av_assert0(0 && "slice groups not supported"); - } - - // No alignment - this need not be a byte boundary. -} - -static void vaapi_encode_h264_write_buffering_period(PutBitContext *pbc, - VAAPIEncodeContext *ctx, - VAAPIEncodePicture *pic) -{ - VAAPIEncodeH264Context *priv = ctx->priv_data; - VAAPIEncodeH264MiscSequenceParams *mseq = &priv->misc_sequence_params; - VAEncPictureParameterBufferH264 *vpic = pic->codec_picture_params; - int i; - - ue(vpic_var(seq_parameter_set_id)); - - if (mseq->nal_hrd_parameters_present_flag) { - for (i = 0; i <= mseq->cpb_cnt_minus1; i++) { - u(mseq->initial_cpb_removal_delay_length_minus1 + 1, - mseq_var(initial_cpb_removal_delay)); - u(mseq->initial_cpb_removal_delay_length_minus1 + 1, - mseq_var(initial_cpb_removal_delay_offset)); - } - } - if (mseq->vcl_hrd_parameters_present_flag) { - av_assert0(0 && "vcl hrd parameters not supported"); - } -} - -static void vaapi_encode_h264_write_pic_timing(PutBitContext *pbc, - VAAPIEncodeContext *ctx, - VAAPIEncodePicture *pic) -{ - VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params; - VAAPIEncodeH264Context *priv = ctx->priv_data; - VAAPIEncodeH264MiscSequenceParams *mseq = &priv->misc_sequence_params; - int i, num_clock_ts; - - if (mseq->nal_hrd_parameters_present_flag || - mseq->vcl_hrd_parameters_present_flag) { - u(mseq->cpb_removal_delay_length_minus1 + 1, - 2 * vseq->num_units_in_tick * priv->cpb_delay, - cpb_removal_delay); - u(mseq->dpb_output_delay_length_minus1 + 1, - 2 * vseq->num_units_in_tick * priv->dpb_delay, - dpb_output_delay); - } - if (mseq->pic_struct_present_flag) { - u(4, mseq_var(pic_struct)); - num_clock_ts = (mseq->pic_struct <= 2 ? 1 : - mseq->pic_struct <= 4 ? 2 : - mseq->pic_struct <= 8 ? 3 : 0); - for (i = 0; i < num_clock_ts; i++) { - u(1, 0, clock_timestamp_flag[i]); - // No full timestamp information. - } - } -} - -static void vaapi_encode_h264_write_identifier(PutBitContext *pbc, - VAAPIEncodeContext *ctx, - VAAPIEncodePicture *pic) -{ - const char *lavc = LIBAVCODEC_IDENT; - const char *vaapi = VA_VERSION_S; - const char *driver = vaQueryVendorString(ctx->hwctx->display); - char tmp[256]; - int i; - - // Random (version 4) ISO 11578 UUID. - uint8_t uuid[16] = { - 0x59, 0x94, 0x8b, 0x28, 0x11, 0xec, 0x45, 0xaf, - 0x96, 0x75, 0x19, 0xd4, 0x1f, 0xea, 0xa9, 0x4d, - }; - - for (i = 0; i < 16; i++) - u(8, uuid[i], uuid_iso_iec_11578); - - snprintf(tmp, sizeof(tmp), "%s / VAAPI %s / %s", lavc, vaapi, driver); - for (i = 0; i < sizeof(tmp) && tmp[i]; i++) - u(8, tmp[i], user_data_payload_byte); -} - -static void vaapi_encode_h264_write_sei(PutBitContext *pbc, - VAAPIEncodeContext *ctx, - VAAPIEncodePicture *pic) +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; - PutBitContext payload_bits; - char payload[256]; - int payload_type, payload_size, i; - void (*write_payload)(PutBitContext *pbc, - VAAPIEncodeContext *ctx, - VAAPIEncodePicture *pic) = NULL; + int err; - vaapi_encode_h264_write_nal_header(pbc, H264_NAL_SEI, 0); - - for (payload_type = 0; payload_type < 64; payload_type++) { - switch (payload_type) { - case H264_SEI_TYPE_BUFFERING_PERIOD: - if (!priv->send_timing_sei || - pic->type != PICTURE_TYPE_IDR) - continue; - write_payload = &vaapi_encode_h264_write_buffering_period; - break; - case H264_SEI_TYPE_PIC_TIMING: - if (!priv->send_timing_sei) - continue; - write_payload = &vaapi_encode_h264_write_pic_timing; - break; - case H264_SEI_TYPE_USER_DATA_UNREGISTERED: - if (pic->encode_order != 0) - continue; - write_payload = &vaapi_encode_h264_write_identifier; - break; - default: - continue; - } - - init_put_bits(&payload_bits, payload, sizeof(payload)); - write_payload(&payload_bits, ctx, pic); - if (put_bits_count(&payload_bits) & 7) { - write_u(&payload_bits, 1, 1, bit_equal_to_one); - while (put_bits_count(&payload_bits) & 7) - write_u(&payload_bits, 1, 0, bit_equal_to_zero); - } - payload_size = put_bits_count(&payload_bits) / 8; - flush_put_bits(&payload_bits); - - u(8, payload_type, last_payload_type_byte); - u(8, payload_size, last_payload_size_byte); - for (i = 0; i < payload_size; i++) - u(8, payload[i] & 0xff, sei_payload); + err = ff_cbs_write_fragment_data(&priv->cbc, au); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to write packed header.\n"); + return err; } - vaapi_encode_h264_write_trailing_rbsp(pbc); + if (*data_len < 8 * au->data_size - au->data_bit_padding) { + av_log(avctx, AV_LOG_ERROR, "Access unit too large: " + "%zu < %zu.\n", *data_len, + 8 * au->data_size - au->data_bit_padding); + return AVERROR(ENOSPC); + } + + memcpy(data, au->data, au->data_size); + *data_len = 8 * au->data_size - au->data_bit_padding; + + return 0; +} + +static int vaapi_encode_h264_add_nal(AVCodecContext *avctx, + CodedBitstreamFragment *au, + void *nal_unit) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH264Context *priv = ctx->priv_data; + H264RawNALUnitHeader *header = nal_unit; + int err; + + err = ff_cbs_insert_unit_content(&priv->cbc, au, -1, + header->nal_unit_type, nal_unit); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to add NAL unit: " + "type = %d.\n", header->nal_unit_type); + return err; + } + + return 0; } static int vaapi_encode_h264_write_sequence_header(AVCodecContext *avctx, char *data, size_t *data_len) { - VAAPIEncodeContext *ctx = avctx->priv_data; - PutBitContext pbc; - char tmp[256]; + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH264Context *priv = ctx->priv_data; + CodedBitstreamFragment *au = &priv->current_access_unit; int err; - size_t nal_len, bit_len, bit_pos, next_len; - bit_len = *data_len; - bit_pos = 0; - - init_put_bits(&pbc, tmp, sizeof(tmp)); - vaapi_encode_h264_write_sps(&pbc, ctx); - nal_len = put_bits_count(&pbc); - flush_put_bits(&pbc); - - next_len = bit_len - bit_pos; - err = ff_vaapi_encode_h26x_nal_unit_to_byte_stream(data + bit_pos / 8, - &next_len, - tmp, nal_len); + err = vaapi_encode_h264_add_nal(avctx, au, &priv->sps); if (err < 0) - return err; - bit_pos += next_len; + goto fail; - init_put_bits(&pbc, tmp, sizeof(tmp)); - vaapi_encode_h264_write_pps(&pbc, ctx); - nal_len = put_bits_count(&pbc); - flush_put_bits(&pbc); - - next_len = bit_len - bit_pos; - err = ff_vaapi_encode_h26x_nal_unit_to_byte_stream(data + bit_pos / 8, - &next_len, - tmp, nal_len); + err = vaapi_encode_h264_add_nal(avctx, au, &priv->pps); if (err < 0) - return err; - bit_pos += next_len; + goto fail; - *data_len = bit_pos; - return 0; + err = vaapi_encode_h264_write_access_unit(avctx, data, data_len, au); +fail: + ff_cbs_fragment_uninit(&priv->cbc, au); + return err; } static int vaapi_encode_h264_write_slice_header(AVCodecContext *avctx, @@ -728,18 +162,19 @@ static int vaapi_encode_h264_write_slice_header(AVCodecContext *avctx, VAAPIEncodeSlice *slice, char *data, size_t *data_len) { - VAAPIEncodeContext *ctx = avctx->priv_data; - PutBitContext pbc; - char tmp[256]; - size_t header_len; + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH264Context *priv = ctx->priv_data; + CodedBitstreamFragment *au = &priv->current_access_unit; + int err; - init_put_bits(&pbc, tmp, sizeof(tmp)); - vaapi_encode_h264_write_slice_header2(&pbc, ctx, pic, slice); - header_len = put_bits_count(&pbc); - flush_put_bits(&pbc); + err = vaapi_encode_h264_add_nal(avctx, au, &priv->slice); + if (err < 0) + goto fail; - return ff_vaapi_encode_h26x_nal_unit_to_byte_stream(data, data_len, - tmp, header_len); + err = vaapi_encode_h264_write_access_unit(avctx, data, data_len, au); +fail: + ff_cbs_fragment_uninit(&priv->cbc, au); + return err; } static int vaapi_encode_h264_write_extra_header(AVCodecContext *avctx, @@ -747,251 +182,442 @@ 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; - PutBitContext pbc; - char tmp[256]; - size_t header_len; + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH264Context *priv = ctx->priv_data; + VAAPIEncodeH264Options *opt = ctx->codec_options; + CodedBitstreamFragment *au = &priv->current_access_unit; + int err, i; + + if (priv->sei_needed) { + memset(&priv->sei, 0, sizeof(priv->sei)); + priv->sei.nal_unit_header.nal_unit_type = H264_NAL_SEI; + + i = 0; + if (pic->encode_order == 0 && opt->sei & SEI_IDENTIFIER) { + priv->sei.payload[i].payload_type = H264_SEI_TYPE_USER_DATA_UNREGISTERED; + priv->sei.payload[i].payload.user_data_unregistered = priv->identifier; + ++i; + } + if (opt->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; + ++i; + } + priv->sei.payload[i].payload_type = H264_SEI_TYPE_PIC_TIMING; + priv->sei.payload[i].payload.pic_timing = priv->pic_timing; + ++i; + } + + priv->sei.payload_count = i; + av_assert0(priv->sei.payload_count > 0); + + err = vaapi_encode_h264_add_nal(avctx, au, &priv->sei); + if (err < 0) + goto fail; + priv->sei_needed = 0; + + err = vaapi_encode_h264_write_access_unit(avctx, data, data_len, au); + if (err < 0) + goto fail; + + ff_cbs_fragment_uninit(&priv->cbc, au); - if (index == 0 && ctx->va_rc_mode == VA_RC_CBR) { *type = VAEncPackedHeaderH264_SEI; - - init_put_bits(&pbc, tmp, sizeof(tmp)); - vaapi_encode_h264_write_sei(&pbc, ctx, pic); - header_len = put_bits_count(&pbc); - flush_put_bits(&pbc); - - return ff_vaapi_encode_h26x_nal_unit_to_byte_stream(data, data_len, - tmp, header_len); - + return 0; } else { return AVERROR_EOF; } + +fail: + ff_cbs_fragment_uninit(&priv->cbc, au); + return err; } static int vaapi_encode_h264_init_sequence_params(AVCodecContext *avctx) { - VAAPIEncodeContext *ctx = avctx->priv_data; - VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params; - VAEncPictureParameterBufferH264 *vpic = ctx->codec_picture_params; - VAAPIEncodeH264Context *priv = ctx->priv_data; - VAAPIEncodeH264MiscSequenceParams *mseq = &priv->misc_sequence_params; - int i; + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH264Context *priv = ctx->priv_data; + VAAPIEncodeH264Options *opt = ctx->codec_options; + H264RawSPS *sps = &priv->sps; + H264RawPPS *pps = &priv->pps; + VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params; + VAEncPictureParameterBufferH264 *vpic = ctx->codec_picture_params; - { - vseq->seq_parameter_set_id = 0; + memset(&priv->current_access_unit, 0, + sizeof(priv->current_access_unit)); - vseq->level_idc = avctx->level; + memset(sps, 0, sizeof(*sps)); + memset(pps, 0, sizeof(*pps)); - vseq->max_num_ref_frames = 1 + (avctx->max_b_frames > 0); + sps->nal_unit_header.nal_ref_idc = 3; + sps->nal_unit_header.nal_unit_type = H264_NAL_SPS; - vseq->picture_width_in_mbs = priv->mb_width; - vseq->picture_height_in_mbs = priv->mb_height; + 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); - vseq->seq_fields.bits.chroma_format_idc = 1; - vseq->seq_fields.bits.frame_mbs_only_flag = 1; - vseq->seq_fields.bits.direct_8x8_inference_flag = 1; - vseq->seq_fields.bits.log2_max_frame_num_minus4 = 4; - vseq->seq_fields.bits.pic_order_cnt_type = 0; - vseq->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = - av_clip(av_log2(avctx->max_b_frames + 1) - 2, 0, 12); + sps->level_idc = avctx->level; - if (avctx->width != ctx->surface_width || - avctx->height != ctx->surface_height) { - vseq->frame_cropping_flag = 1; + sps->seq_parameter_set_id = 0; + sps->chroma_format_idc = 1; - vseq->frame_crop_left_offset = 0; - vseq->frame_crop_right_offset = - (ctx->surface_width - avctx->width) / 2; - vseq->frame_crop_top_offset = 0; - vseq->frame_crop_bottom_offset = - (ctx->surface_height - avctx->height) / 2; - } else { - vseq->frame_cropping_flag = 0; - } + sps->log2_max_frame_num_minus4 = 4; + sps->pic_order_cnt_type = 0; + sps->log2_max_pic_order_cnt_lsb_minus4 = + av_clip(av_log2(ctx->b_per_p + 1) - 2, 0, 12); - vseq->vui_parameters_present_flag = 1; - if (avctx->sample_aspect_ratio.num != 0) { - vseq->vui_fields.bits.aspect_ratio_info_present_flag = 1; - // There is a large enum of these which we could support - // individually rather than using the generic X/Y form? - if (avctx->sample_aspect_ratio.num == - avctx->sample_aspect_ratio.den) { - vseq->aspect_ratio_idc = 1; - } else { - vseq->aspect_ratio_idc = 255; // Extended SAR. - vseq->sar_width = avctx->sample_aspect_ratio.num; - vseq->sar_height = avctx->sample_aspect_ratio.den; + sps->max_num_ref_frames = + (avctx->profile & FF_PROFILE_H264_INTRA) ? 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; + + sps->frame_mbs_only_flag = 1; + sps->direct_8x8_inference_flag = 1; + + if (avctx->width != 16 * priv->mb_width || + avctx->height != 16 * priv->mb_height) { + sps->frame_cropping_flag = 1; + + sps->frame_crop_left_offset = 0; + sps->frame_crop_right_offset = + (16 * priv->mb_width - avctx->width) / 2; + sps->frame_crop_top_offset = 0; + sps->frame_crop_bottom_offset = + (16 * priv->mb_height - avctx->height) / 2; + } else { + sps->frame_cropping_flag = 0; + } + + sps->vui_parameters_present_flag = 1; + + if (avctx->sample_aspect_ratio.num != 0 && + avctx->sample_aspect_ratio.den != 0) { + static const AVRational sar_idc[] = { + { 0, 0 }, + { 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 }, + { 40, 33 }, { 24, 11 }, { 20, 11 }, { 32, 11 }, + { 80, 33 }, { 18, 11 }, { 15, 11 }, { 64, 33 }, + { 160, 99 }, { 4, 3 }, { 3, 2 }, { 2, 1 }, + }; + int i; + for (i = 0; i < FF_ARRAY_ELEMS(sar_idc); i++) { + if (avctx->sample_aspect_ratio.num == sar_idc[i].num && + avctx->sample_aspect_ratio.den == sar_idc[i].den) { + sps->vui.aspect_ratio_idc = i; + break; } } + if (i >= FF_ARRAY_ELEMS(sar_idc)) { + sps->vui.aspect_ratio_idc = 255; + sps->vui.sar_width = avctx->sample_aspect_ratio.num; + sps->vui.sar_height = avctx->sample_aspect_ratio.den; + } + sps->vui.aspect_ratio_info_present_flag = 1; + } + + if (avctx->color_range != AVCOL_RANGE_UNSPECIFIED || + avctx->color_primaries != AVCOL_PRI_UNSPECIFIED || + avctx->color_trc != AVCOL_TRC_UNSPECIFIED || + avctx->colorspace != AVCOL_SPC_UNSPECIFIED) { + sps->vui.video_signal_type_present_flag = 1; + sps->vui.video_format = 5; // Unspecified. + sps->vui.video_full_range_flag = + avctx->color_range == AVCOL_RANGE_JPEG; + if (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED || avctx->color_trc != AVCOL_TRC_UNSPECIFIED || avctx->colorspace != AVCOL_SPC_UNSPECIFIED) { - mseq->video_signal_type_present_flag = 1; - mseq->video_format = 5; // Unspecified. - mseq->video_full_range_flag = 0; - mseq->colour_description_present_flag = 1; - // These enums are derived from the standard and hence - // we can just use the values directly. - mseq->colour_primaries = avctx->color_primaries; - mseq->transfer_characteristics = avctx->color_trc; - mseq->matrix_coefficients = avctx->colorspace; + sps->vui.colour_description_present_flag = 1; + sps->vui.colour_primaries = avctx->color_primaries; + sps->vui.transfer_characteristics = avctx->color_trc; + sps->vui.matrix_coefficients = avctx->colorspace; } - - vseq->vui_fields.bits.bitstream_restriction_flag = 1; - mseq->motion_vectors_over_pic_boundaries_flag = 1; - mseq->max_bytes_per_pic_denom = 0; - mseq->max_bits_per_mb_denom = 0; - vseq->vui_fields.bits.log2_max_mv_length_horizontal = 16; - vseq->vui_fields.bits.log2_max_mv_length_vertical = 16; - - mseq->max_num_reorder_frames = (avctx->max_b_frames > 0); - mseq->max_dec_pic_buffering = vseq->max_num_ref_frames; - - vseq->bits_per_second = avctx->bit_rate; - - vseq->vui_fields.bits.timing_info_present_flag = 1; - if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { - vseq->num_units_in_tick = avctx->framerate.den; - vseq->time_scale = 2 * avctx->framerate.num; - mseq->fixed_frame_rate_flag = 1; - } else { - vseq->num_units_in_tick = avctx->time_base.num; - vseq->time_scale = 2 * avctx->time_base.den; - mseq->fixed_frame_rate_flag = 0; - } - - if (ctx->va_rc_mode == VA_RC_CBR) { - priv->send_timing_sei = 1; - mseq->nal_hrd_parameters_present_flag = 1; - - mseq->cpb_cnt_minus1 = 0; - - // Try to scale these to a sensible range so that the - // golomb encode of the value is not overlong. - mseq->bit_rate_scale = - av_clip_uintp2(av_log2(avctx->bit_rate) - 15 - 6, 4); - mseq->bit_rate_value_minus1[0] = - (avctx->bit_rate >> mseq->bit_rate_scale + 6) - 1; - - mseq->cpb_size_scale = - av_clip_uintp2(av_log2(ctx->hrd_params.hrd.buffer_size) - 15 - 4, 4); - mseq->cpb_size_value_minus1[0] = - (ctx->hrd_params.hrd.buffer_size >> mseq->cpb_size_scale + 4) - 1; - - // CBR mode isn't actually available here, despite naming. - mseq->cbr_flag[0] = 0; - - mseq->initial_cpb_removal_delay_length_minus1 = 23; - mseq->cpb_removal_delay_length_minus1 = 23; - mseq->dpb_output_delay_length_minus1 = 7; - mseq->time_offset_length = 0; - - // This calculation can easily overflow 32 bits. - mseq->initial_cpb_removal_delay = 90000 * - (uint64_t)ctx->hrd_params.hrd.initial_buffer_fullness / - ctx->hrd_params.hrd.buffer_size; - - mseq->initial_cpb_removal_delay_offset = 0; - } else { - priv->send_timing_sei = 0; - mseq->nal_hrd_parameters_present_flag = 0; - } - - vseq->intra_period = avctx->gop_size; - vseq->intra_idr_period = avctx->gop_size; - vseq->ip_period = ctx->b_per_p + 1; + } else { + sps->vui.video_format = 5; + sps->vui.video_full_range_flag = 0; + sps->vui.colour_primaries = avctx->color_primaries; + sps->vui.transfer_characteristics = avctx->color_trc; + sps->vui.matrix_coefficients = avctx->colorspace; } - { - vpic->CurrPic.picture_id = VA_INVALID_ID; - vpic->CurrPic.flags = VA_PICTURE_H264_INVALID; - - for (i = 0; i < FF_ARRAY_ELEMS(vpic->ReferenceFrames); i++) { - vpic->ReferenceFrames[i].picture_id = VA_INVALID_ID; - vpic->ReferenceFrames[i].flags = VA_PICTURE_H264_INVALID; - } - - vpic->coded_buf = VA_INVALID_ID; - - vpic->pic_parameter_set_id = 0; - vpic->seq_parameter_set_id = 0; - - vpic->num_ref_idx_l0_active_minus1 = 0; - vpic->num_ref_idx_l1_active_minus1 = 0; - - vpic->pic_fields.bits.entropy_coding_mode_flag = - ((avctx->profile & 0xff) != 66); - vpic->pic_fields.bits.weighted_pred_flag = 0; - vpic->pic_fields.bits.weighted_bipred_idc = 0; - vpic->pic_fields.bits.transform_8x8_mode_flag = - ((avctx->profile & 0xff) >= 100); - - vpic->pic_init_qp = priv->fixed_qp_idr; + if (avctx->chroma_sample_location != AVCHROMA_LOC_UNSPECIFIED) { + sps->vui.chroma_loc_info_present_flag = 1; + sps->vui.chroma_sample_loc_type_top_field = + sps->vui.chroma_sample_loc_type_bottom_field = + avctx->chroma_sample_location - 1; } - { - mseq->profile_idc = avctx->profile & 0xff; - - if (avctx->profile & FF_PROFILE_H264_CONSTRAINED) - mseq->constraint_set1_flag = 1; - if (avctx->profile & FF_PROFILE_H264_INTRA) - mseq->constraint_set3_flag = 1; + sps->vui.timing_info_present_flag = 1; + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { + sps->vui.num_units_in_tick = avctx->framerate.den; + sps->vui.time_scale = 2 * avctx->framerate.num; + sps->vui.fixed_frame_rate_flag = 1; + } else { + sps->vui.num_units_in_tick = avctx->time_base.num; + sps->vui.time_scale = 2 * avctx->time_base.den; + sps->vui.fixed_frame_rate_flag = 0; } + if (opt->sei & SEI_TIMING) { + H264RawHRD *hrd = &sps->vui.nal_hrd_parameters; + + sps->vui.nal_hrd_parameters_present_flag = 1; + + hrd->cpb_cnt_minus1 = 0; + + // 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); + hrd->bit_rate_value_minus1[0] = + (avctx->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); + hrd->cpb_size_value_minus1[0] = + (ctx->hrd_params.hrd.buffer_size >> hrd->cpb_size_scale + 4) - 1; + + // CBR mode as defined for the HRD cannot be achieved without filler + // data, so this flag cannot be set even with VAAPI CBR modes. + hrd->cbr_flag[0] = 0; + + hrd->initial_cpb_removal_delay_length_minus1 = 23; + hrd->cpb_removal_delay_length_minus1 = 23; + hrd->dpb_output_delay_length_minus1 = 7; + hrd->time_offset_length = 0; + + priv->buffering_period.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 * + (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; + } else { + sps->vui.nal_hrd_parameters_present_flag = 0; + sps->vui.low_delay_hrd_flag = 1 - sps->vui.fixed_frame_rate_flag; + } + + 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.max_num_reorder_frames = (ctx->b_per_p > 0); + sps->vui.max_dec_frame_buffering = vseq->max_num_ref_frames; + + pps->nal_unit_header.nal_ref_idc = 3; + pps->nal_unit_header.nal_unit_type = H264_NAL_PPS; + + pps->pic_parameter_set_id = 0; + pps->seq_parameter_set_id = 0; + + pps->entropy_coding_mode_flag = + !(sps->profile_idc == FF_PROFILE_H264_BASELINE || + sps->profile_idc == FF_PROFILE_H264_EXTENDED || + sps->profile_idc == FF_PROFILE_H264_CAVLC_444); + + pps->num_ref_idx_l0_default_active_minus1 = 0; + pps->num_ref_idx_l1_default_active_minus1 = 0; + + pps->pic_init_qp_minus26 = priv->fixed_qp_idr - 26; + + if (sps->profile_idc == FF_PROFILE_H264_BASELINE || + sps->profile_idc == FF_PROFILE_H264_EXTENDED || + sps->profile_idc == FF_PROFILE_H264_MAIN) { + pps->more_rbsp_data = 0; + } else { + pps->more_rbsp_data = 1; + + pps->transform_8x8_mode_flag = 1; + } + + *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, + .ip_period = ctx->b_per_p + 1, + + .bits_per_second = avctx->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, + + .seq_fields.bits = { + .chroma_format_idc = sps->chroma_format_idc, + .frame_mbs_only_flag = sps->frame_mbs_only_flag, + .mb_adaptive_frame_field_flag = sps->mb_adaptive_frame_field_flag, + .seq_scaling_matrix_present_flag = sps->seq_scaling_matrix_present_flag, + .direct_8x8_inference_flag = sps->direct_8x8_inference_flag, + .log2_max_frame_num_minus4 = sps->log2_max_frame_num_minus4, + .pic_order_cnt_type = sps->pic_order_cnt_type, + .log2_max_pic_order_cnt_lsb_minus4 = sps->log2_max_pic_order_cnt_lsb_minus4, + .delta_pic_order_always_zero_flag = sps->delta_pic_order_always_zero_flag, + }, + + .bit_depth_luma_minus8 = sps->bit_depth_luma_minus8, + .bit_depth_chroma_minus8 = sps->bit_depth_chroma_minus8, + + .frame_cropping_flag = sps->frame_cropping_flag, + .frame_crop_left_offset = sps->frame_crop_left_offset, + .frame_crop_right_offset = sps->frame_crop_right_offset, + .frame_crop_top_offset = sps->frame_crop_top_offset, + .frame_crop_bottom_offset = sps->frame_crop_bottom_offset, + + .vui_parameters_present_flag = sps->vui_parameters_present_flag, + + .vui_fields.bits = { + .aspect_ratio_info_present_flag = sps->vui.aspect_ratio_info_present_flag, + .timing_info_present_flag = sps->vui.timing_info_present_flag, + .bitstream_restriction_flag = sps->vui.bitstream_restriction_flag, + .log2_max_mv_length_horizontal = sps->vui.log2_max_mv_length_horizontal, + .log2_max_mv_length_vertical = sps->vui.log2_max_mv_length_vertical, + }, + + .aspect_ratio_idc = sps->vui.aspect_ratio_idc, + .sar_width = sps->vui.sar_width, + .sar_height = sps->vui.sar_height, + .num_units_in_tick = sps->vui.num_units_in_tick, + .time_scale = sps->vui.time_scale, + }; + + *vpic = (VAEncPictureParameterBufferH264) { + .CurrPic = { + .picture_id = VA_INVALID_ID, + .flags = VA_PICTURE_H264_INVALID, + }, + + .coded_buf = VA_INVALID_ID, + + .pic_parameter_set_id = pps->pic_parameter_set_id, + .seq_parameter_set_id = pps->seq_parameter_set_id, + + .pic_init_qp = pps->pic_init_qp_minus26 + 26, + .num_ref_idx_l0_active_minus1 = pps->num_ref_idx_l0_default_active_minus1, + .num_ref_idx_l1_active_minus1 = pps->num_ref_idx_l1_default_active_minus1, + + .chroma_qp_index_offset = pps->chroma_qp_index_offset, + .second_chroma_qp_index_offset = pps->second_chroma_qp_index_offset, + + .pic_fields.bits = { + .entropy_coding_mode_flag = pps->entropy_coding_mode_flag, + .weighted_pred_flag = pps->weighted_pred_flag, + .weighted_bipred_idc = pps->weighted_bipred_idc, + .constrained_intra_pred_flag = pps->constrained_intra_pred_flag, + .transform_8x8_mode_flag = pps->transform_8x8_mode_flag, + .deblocking_filter_control_present_flag = + pps->deblocking_filter_control_present_flag, + .redundant_pic_cnt_present_flag = pps->redundant_pic_cnt_present_flag, + .pic_order_present_flag = + pps->bottom_field_pic_order_in_frame_present_flag, + .pic_scaling_matrix_present_flag = pps->pic_scaling_matrix_present_flag, + }, + }; + return 0; } static int vaapi_encode_h264_init_picture_params(AVCodecContext *avctx, VAAPIEncodePicture *pic) { - VAAPIEncodeContext *ctx = avctx->priv_data; - VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params; - VAEncPictureParameterBufferH264 *vpic = pic->codec_picture_params; - VAAPIEncodeH264Context *priv = ctx->priv_data; + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH264Context *priv = ctx->priv_data; + VAAPIEncodeH264Options *opt = ctx->codec_options; + H264RawSPS *sps = &priv->sps; + VAEncPictureParameterBufferH264 *vpic = pic->codec_picture_params; int i; + memset(&priv->current_access_unit, 0, + sizeof(priv->current_access_unit)); + if (pic->type == PICTURE_TYPE_IDR) { av_assert0(pic->display_order == pic->encode_order); - vpic->frame_num = 0; + priv->frame_num = 0; priv->next_frame_num = 1; - priv->cpb_delay = 0; + priv->cpb_delay = 0; priv->last_idr_frame = pic->display_order; + ++priv->idr_pic_count; + + priv->slice_type = 7; + priv->primary_pic_type = 0; } else { - vpic->frame_num = priv->next_frame_num; + priv->frame_num = priv->next_frame_num; + if (pic->type != PICTURE_TYPE_B) { - // nal_ref_idc != 0 - ++priv->next_frame_num; + // Reference picture, so frame_num advances. + priv->next_frame_num = (priv->frame_num + 1) & + ((1 << (4 + sps->log2_max_frame_num_minus4)) - 1); } ++priv->cpb_delay; + + if (pic->type == PICTURE_TYPE_I) { + priv->slice_type = 7; + priv->primary_pic_type = 0; + } else if (pic->type == PICTURE_TYPE_P) { + priv->slice_type = 5; + priv->primary_pic_type = 1; + } else { + priv->slice_type = 6; + priv->primary_pic_type = 2; + } } - priv->dpb_delay = pic->display_order - pic->encode_order + 1; + priv->pic_order_cnt = pic->display_order - priv->last_idr_frame; + priv->dpb_delay = pic->display_order - pic->encode_order + 1; - vpic->frame_num = vpic->frame_num & - ((1 << (4 + vseq->seq_fields.bits.log2_max_frame_num_minus4)) - 1); + if (opt->sei & SEI_IDENTIFIER && pic->encode_order == 0) + priv->sei_needed = 1; - vpic->CurrPic.picture_id = pic->recon_surface; - vpic->CurrPic.frame_idx = vpic->frame_num; - vpic->CurrPic.flags = 0; - vpic->CurrPic.TopFieldOrderCnt = pic->display_order - priv->last_idr_frame; - vpic->CurrPic.BottomFieldOrderCnt = pic->display_order - priv->last_idr_frame; + if (opt->sei & SEI_TIMING) { + memset(&priv->pic_timing, 0, sizeof(priv->pic_timing)); + + priv->pic_timing.cpb_removal_delay = + 2 * sps->vui.num_units_in_tick * priv->cpb_delay; + priv->pic_timing.dpb_output_delay = + 2 * sps->vui.num_units_in_tick * priv->dpb_delay; + + priv->sei_needed = 1; + } + + vpic->CurrPic = (VAPictureH264) { + .picture_id = pic->recon_surface, + .frame_idx = priv->frame_num, + .flags = 0, + .TopFieldOrderCnt = priv->pic_order_cnt, + .BottomFieldOrderCnt = priv->pic_order_cnt, + }; for (i = 0; i < pic->nb_refs; i++) { VAAPIEncodePicture *ref = pic->refs[i]; + unsigned int frame_num = (ref->encode_order - priv->last_idr_frame) & + ((1 << (4 + sps->log2_max_frame_num_minus4)) - 1); + unsigned int pic_order_cnt = ref->display_order - priv->last_idr_frame; + av_assert0(ref && ref->encode_order < pic->encode_order); - vpic->ReferenceFrames[i].picture_id = ref->recon_surface; - vpic->ReferenceFrames[i].frame_idx = ref->encode_order; - vpic->ReferenceFrames[i].flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE; - vpic->ReferenceFrames[i].TopFieldOrderCnt = ref->display_order - priv->last_idr_frame; - vpic->ReferenceFrames[i].BottomFieldOrderCnt = ref->display_order - priv->last_idr_frame; + vpic->ReferenceFrames[i] = (VAPictureH264) { + .picture_id = ref->recon_surface, + .frame_idx = frame_num, + .flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE, + .TopFieldOrderCnt = pic_order_cnt, + .BottomFieldOrderCnt = pic_order_cnt, + }; } for (; i < FF_ARRAY_ELEMS(vpic->ReferenceFrames); i++) { - vpic->ReferenceFrames[i].picture_id = VA_INVALID_ID; - vpic->ReferenceFrames[i].flags = VA_PICTURE_H264_INVALID; + vpic->ReferenceFrames[i] = (VAPictureH264) { + .picture_id = VA_INVALID_ID, + .flags = VA_PICTURE_H264_INVALID, + }; } vpic->coded_buf = pic->output_buffer; - vpic->pic_fields.bits.idr_pic_flag = (pic->type == PICTURE_TYPE_IDR); + vpic->frame_num = priv->frame_num; + + vpic->pic_fields.bits.idr_pic_flag = (pic->type == PICTURE_TYPE_IDR); vpic->pic_fields.bits.reference_pic_flag = (pic->type != PICTURE_TYPE_B); pic->nb_slices = 1; @@ -1003,58 +629,57 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx, VAAPIEncodePicture *pic, VAAPIEncodeSlice *slice) { - VAAPIEncodeContext *ctx = avctx->priv_data; - VAEncSequenceParameterBufferH264 *vseq = ctx->codec_sequence_params; - VAEncPictureParameterBufferH264 *vpic = pic->codec_picture_params; - VAEncSliceParameterBufferH264 *vslice = slice->codec_slice_params; - VAAPIEncodeH264Context *priv = ctx->priv_data; - VAAPIEncodeH264Slice *pslice; - VAAPIEncodeH264MiscSliceParams *mslice; + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH264Context *priv = ctx->priv_data; + H264RawSPS *sps = &priv->sps; + H264RawPPS *pps = &priv->pps; + H264RawSliceHeader *sh = &priv->slice.header; + VAEncPictureParameterBufferH264 *vpic = pic->codec_picture_params; + VAEncSliceParameterBufferH264 *vslice = slice->codec_slice_params; int i; - slice->priv_data = av_mallocz(sizeof(*pslice)); - if (!slice->priv_data) - return AVERROR(ENOMEM); - pslice = slice->priv_data; - mslice = &pslice->misc_slice_params; - - if (pic->type == PICTURE_TYPE_IDR) - mslice->nal_unit_type = H264_NAL_IDR_SLICE; - else - mslice->nal_unit_type = H264_NAL_SLICE; - - switch (pic->type) { - case PICTURE_TYPE_IDR: - vslice->slice_type = SLICE_TYPE_I; - mslice->nal_ref_idc = 3; - break; - case PICTURE_TYPE_I: - vslice->slice_type = SLICE_TYPE_I; - mslice->nal_ref_idc = 2; - break; - case PICTURE_TYPE_P: - vslice->slice_type = SLICE_TYPE_P; - mslice->nal_ref_idc = 1; - break; - case PICTURE_TYPE_B: - vslice->slice_type = SLICE_TYPE_B; - mslice->nal_ref_idc = 0; - break; - default: - av_assert0(0 && "invalid picture type"); + if (pic->type == PICTURE_TYPE_IDR) { + sh->nal_unit_header.nal_unit_type = H264_NAL_IDR_SLICE; + sh->nal_unit_header.nal_ref_idc = 3; + } else { + sh->nal_unit_header.nal_unit_type = H264_NAL_SLICE; + sh->nal_unit_header.nal_ref_idc = pic->type != PICTURE_TYPE_B; } // Only one slice per frame. - vslice->macroblock_address = 0; - vslice->num_macroblocks = priv->mb_width * priv->mb_height; + sh->first_mb_in_slice = 0; + sh->slice_type = priv->slice_type; + + sh->pic_parameter_set_id = pps->pic_parameter_set_id; + + sh->frame_num = priv->frame_num; + sh->idr_pic_id = priv->idr_pic_count; + + sh->pic_order_cnt_lsb = priv->pic_order_cnt & + ((1 << (4 + sps->log2_max_pic_order_cnt_lsb_minus4)) - 1); + + sh->direct_spatial_mv_pred_flag = 1; + + if (pic->type == PICTURE_TYPE_B) + sh->slice_qp_delta = priv->fixed_qp_b - (pps->pic_init_qp_minus26 + 26); + else if (pic->type == PICTURE_TYPE_P) + sh->slice_qp_delta = priv->fixed_qp_p - (pps->pic_init_qp_minus26 + 26); + else + sh->slice_qp_delta = priv->fixed_qp_idr - (pps->pic_init_qp_minus26 + 26); + + + vslice->macroblock_address = sh->first_mb_in_slice; + vslice->num_macroblocks = priv->mb_width * priv->mb_height; vslice->macroblock_info = VA_INVALID_ID; - vslice->pic_parameter_set_id = vpic->pic_parameter_set_id; - vslice->idr_pic_id = priv->idr_pic_count++; + vslice->slice_type = sh->slice_type % 5; + vslice->pic_parameter_set_id = sh->pic_parameter_set_id; + vslice->idr_pic_id = sh->idr_pic_id; - vslice->pic_order_cnt_lsb = (pic->display_order - priv->last_idr_frame) & - ((1 << (4 + vseq->seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4)) - 1); + vslice->pic_order_cnt_lsb = sh->pic_order_cnt_lsb; + + vslice->direct_spatial_mv_pred_flag = sh->direct_spatial_mv_pred_flag; for (i = 0; i < FF_ARRAY_ELEMS(vslice->RefPicList0); i++) { vslice->RefPicList0[i].picture_id = VA_INVALID_ID; @@ -1068,26 +693,15 @@ static int vaapi_encode_h264_init_slice_params(AVCodecContext *avctx, // Backward reference for P- or B-frame. av_assert0(pic->type == PICTURE_TYPE_P || pic->type == PICTURE_TYPE_B); - - vslice->num_ref_idx_l0_active_minus1 = 0; vslice->RefPicList0[0] = vpic->ReferenceFrames[0]; } if (pic->nb_refs >= 2) { // Forward reference for B-frame. av_assert0(pic->type == PICTURE_TYPE_B); - - vslice->num_ref_idx_l1_active_minus1 = 0; vslice->RefPicList1[0] = vpic->ReferenceFrames[1]; } - if (pic->type == PICTURE_TYPE_B) - vslice->slice_qp_delta = priv->fixed_qp_b - vpic->pic_init_qp; - else if (pic->type == PICTURE_TYPE_P) - vslice->slice_qp_delta = priv->fixed_qp_p - vpic->pic_init_qp; - else - vslice->slice_qp_delta = priv->fixed_qp_idr - vpic->pic_init_qp; - - vslice->direct_spatial_mv_pred_flag = 1; + vslice->slice_qp_delta = sh->slice_qp_delta; return 0; } @@ -1097,6 +711,11 @@ 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; + int err; + + err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_H264, avctx); + if (err < 0) + return err; priv->mb_width = FFALIGN(avctx->width, 16) / 16; priv->mb_height = FFALIGN(avctx->height, 16) / 16; @@ -1114,6 +733,8 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx) else priv->fixed_qp_b = priv->fixed_qp_p; + opt->sei &= ~SEI_TIMING; + av_log(avctx, AV_LOG_DEBUG, "Using fixed QP = " "%d / %d / %d for IDR- / P- / B-frames.\n", priv->fixed_qp_idr, priv->fixed_qp_p, priv->fixed_qp_b); @@ -1136,6 +757,34 @@ static av_cold int vaapi_encode_h264_configure(AVCodecContext *avctx) if (avctx->compression_level == FF_COMPRESSION_DEFAULT) avctx->compression_level = opt->quality; + if (opt->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, + vaapi_encode_h264_sei_identifier_uuid, + sizeof(priv->identifier.uuid_iso_iec_11578)); + + driver = vaQueryVendorString(ctx->hwctx->display); + if (!driver) + driver = "unknown driver"; + + 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) + return AVERROR(ENOMEM); + + snprintf(priv->identifier_string, len + 1, + "%s / VAAPI %s / %s", lavc, vaapi, driver); + + priv->identifier.data = priv->identifier_string; + priv->identifier.data_length = len + 1; + } + } + return 0; } @@ -1241,6 +890,19 @@ static av_cold int vaapi_encode_h264_init(AVCodecContext *avctx) return ff_vaapi_encode_init(avctx); } +static av_cold int vaapi_encode_h264_close(AVCodecContext *avctx) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH264Context *priv = ctx->priv_data; + + if (priv) { + ff_cbs_close(&priv->cbc); + av_freep(&priv->identifier_string); + } + + return ff_vaapi_encode_close(avctx); +} + #define OFFSET(x) (offsetof(VAAPIEncodeContext, codec_options_data) + \ offsetof(VAAPIEncodeH264Options, x)) #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM) @@ -1252,6 +914,17 @@ static const AVOption vaapi_encode_h264_options[] = { { "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 }, + + { "sei", "Set SEI to include", + OFFSET(sei), AV_OPT_TYPE_FLAGS, + { .i64 = SEI_IDENTIFIER | SEI_TIMING }, + 0, INT_MAX, FLAGS, "sei" }, + { "identifier", "Include encoder version identifier", + 0, AV_OPT_TYPE_CONST, { .i64 = SEI_IDENTIFIER }, + INT_MIN, INT_MAX, FLAGS, "sei" }, + { "timing", "Include timing parameters (buffering_period and pic_timing)", + 0, AV_OPT_TYPE_CONST, { .i64 = SEI_TIMING }, + INT_MIN, INT_MAX, FLAGS, "sei" }, { NULL }, }; @@ -1285,7 +958,7 @@ AVCodec ff_h264_vaapi_encoder = { sizeof(VAAPIEncodeH264Options)), .init = &vaapi_encode_h264_init, .encode2 = &ff_vaapi_encode2, - .close = &ff_vaapi_encode_close, + .close = &vaapi_encode_h264_close, .priv_class = &vaapi_encode_h264_class, .capabilities = AV_CODEC_CAP_DELAY, .defaults = vaapi_encode_h264_defaults, From 820a4483af13cf6fd51f13638e57bcd1c3f629d4 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 7 May 2017 22:58:56 +0100 Subject: [PATCH 12/15] vaapi_h264: Add support for AUD NAL units Adds a new private option to enable them (off by default). --- libavcodec/vaapi_encode_h264.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c index b48070c950..4f0e879e9b 100644 --- a/libavcodec/vaapi_encode_h264.c +++ b/libavcodec/vaapi_encode_h264.c @@ -53,6 +53,7 @@ typedef struct VAAPIEncodeH264Context { int fixed_qp_p; int fixed_qp_b; + H264RawAUD aud; H264RawSPS sps; H264RawPPS pps; H264RawSEI sei; @@ -77,6 +78,7 @@ typedef struct VAAPIEncodeH264Context { CodedBitstreamContext cbc; CodedBitstreamFragment current_access_unit; + int aud_needed; int sei_needed; } VAAPIEncodeH264Context; @@ -84,6 +86,7 @@ typedef struct VAAPIEncodeH264Options { int qp; int quality; int low_power; + int aud; int sei; } VAAPIEncodeH264Options; @@ -143,6 +146,13 @@ static int vaapi_encode_h264_write_sequence_header(AVCodecContext *avctx, CodedBitstreamFragment *au = &priv->current_access_unit; int err; + if (priv->aud_needed) { + err = vaapi_encode_h264_add_nal(avctx, au, &priv->aud); + if (err < 0) + goto fail; + priv->aud_needed = 0; + } + err = vaapi_encode_h264_add_nal(avctx, au, &priv->sps); if (err < 0) goto fail; @@ -167,6 +177,13 @@ static int vaapi_encode_h264_write_slice_header(AVCodecContext *avctx, CodedBitstreamFragment *au = &priv->current_access_unit; int err; + if (priv->aud_needed) { + err = vaapi_encode_h264_add_nal(avctx, au, &priv->aud); + if (err < 0) + goto fail; + priv->aud_needed = 0; + } + err = vaapi_encode_h264_add_nal(avctx, au, &priv->slice); if (err < 0) goto fail; @@ -189,6 +206,11 @@ static int vaapi_encode_h264_write_extra_header(AVCodecContext *avctx, int err, i; if (priv->sei_needed) { + if (priv->aud_needed) { + vaapi_encode_h264_add_nal(avctx, au, &priv->aud); + priv->aud_needed = 0; + } + memset(&priv->sei, 0, sizeof(priv->sei)); priv->sei.nal_unit_header.nal_unit_type = H264_NAL_SEI; @@ -569,6 +591,14 @@ 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) { + priv->aud_needed = 1; + priv->aud.nal_unit_header.nal_unit_type = H264_NAL_AUD; + priv->aud.primary_pic_type = priv->primary_pic_type; + } else { + priv->aud_needed = 0; + } + if (opt->sei & SEI_IDENTIFIER && pic->encode_order == 0) priv->sei_needed = 1; @@ -915,6 +945,9 @@ static const AVOption vaapi_encode_h264_options[] = { "on some platforms, does not support all features)", OFFSET(low_power), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS }, + { "aud", "Include AUD", + OFFSET(aud), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS }, + { "sei", "Set SEI to include", OFFSET(sei), AV_OPT_TYPE_FLAGS, { .i64 = SEI_IDENTIFIER | SEI_TIMING }, From a49ee60d5fdbdae1706a44cfbb814abb9793815f Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 7 May 2017 23:02:09 +0100 Subject: [PATCH 13/15] vaapi_h264: Add support for SEI recovery points Included by default with non-IDR intra frames. --- libavcodec/vaapi_encode_h264.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/libavcodec/vaapi_encode_h264.c b/libavcodec/vaapi_encode_h264.c index 4f0e879e9b..271644ebbe 100644 --- a/libavcodec/vaapi_encode_h264.c +++ b/libavcodec/vaapi_encode_h264.c @@ -37,6 +37,7 @@ enum { SEI_TIMING = 0x01, SEI_IDENTIFIER = 0x02, + SEI_RECOVERY_POINT = 0x04, }; // Random (version 4) ISO 11578 UUID. @@ -61,6 +62,7 @@ typedef struct VAAPIEncodeH264Context { H264RawSEIBufferingPeriod buffering_period; H264RawSEIPicTiming pic_timing; + H264RawSEIRecoveryPoint recovery_point; H264RawSEIUserDataUnregistered identifier; char *identifier_string; @@ -230,6 +232,11 @@ static int vaapi_encode_h264_write_extra_header(AVCodecContext *avctx, priv->sei.payload[i].payload.pic_timing = priv->pic_timing; ++i; } + if (opt->sei & SEI_RECOVERY_POINT && pic->type == PICTURE_TYPE_I) { + priv->sei.payload[i].payload_type = H264_SEI_TYPE_RECOVERY_POINT; + priv->sei.payload[i].payload.recovery_point = priv->recovery_point; + ++i; + } priv->sei.payload_count = i; av_assert0(priv->sei.payload_count > 0); @@ -613,6 +620,14 @@ static int vaapi_encode_h264_init_picture_params(AVCodecContext *avctx, priv->sei_needed = 1; } + 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; + + priv->sei_needed = 1; + } + vpic->CurrPic = (VAPictureH264) { .picture_id = pic->recon_surface, .frame_idx = priv->frame_num, @@ -950,7 +965,7 @@ static const AVOption vaapi_encode_h264_options[] = { { "sei", "Set SEI to include", OFFSET(sei), AV_OPT_TYPE_FLAGS, - { .i64 = SEI_IDENTIFIER | SEI_TIMING }, + { .i64 = SEI_IDENTIFIER | SEI_TIMING | SEI_RECOVERY_POINT }, 0, INT_MAX, FLAGS, "sei" }, { "identifier", "Include encoder version identifier", 0, AV_OPT_TYPE_CONST, { .i64 = SEI_IDENTIFIER }, @@ -958,6 +973,9 @@ static const AVOption vaapi_encode_h264_options[] = { { "timing", "Include timing parameters (buffering_period and pic_timing)", 0, AV_OPT_TYPE_CONST, { .i64 = SEI_TIMING }, INT_MIN, INT_MAX, FLAGS, "sei" }, + { "recovery_point", "Include recovery points where appropriate", + 0, AV_OPT_TYPE_CONST, { .i64 = SEI_RECOVERY_POINT }, + INT_MIN, INT_MAX, FLAGS, "sei" }, { NULL }, }; From ac12486714b48f9bd5d9167f90b77c936751d6ef Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 14 May 2017 21:36:24 +0100 Subject: [PATCH 14/15] vaapi_h265: Convert to use coded bitstream infrastructure Also improves the metadata and generally makes the configuration a bit cleaner. --- configure | 2 +- libavcodec/Makefile | 2 +- libavcodec/vaapi_encode_h265.c | 1555 ++++++++++++-------------------- libavcodec/vaapi_encode_h26x.c | 68 -- libavcodec/vaapi_encode_h26x.h | 45 - 5 files changed, 594 insertions(+), 1078 deletions(-) delete mode 100644 libavcodec/vaapi_encode_h26x.c delete mode 100644 libavcodec/vaapi_encode_h26x.h diff --git a/configure b/configure index b6fa8f6827..9704b9316a 100755 --- a/configure +++ b/configure @@ -2290,7 +2290,7 @@ hevc_nvenc_encoder_deps="nvenc" hevc_qsv_decoder_select="hevc_mp4toannexb_bsf hevc_parser hevc_qsv_hwaccel qsvdec" hevc_qsv_encoder_select="hevc_ps qsvenc" hevc_vaapi_encoder_deps="VAEncPictureParameterBufferHEVC" -hevc_vaapi_encoder_select="vaapi_encode golomb" +hevc_vaapi_encoder_select="cbs_h265 vaapi_encode" mjpeg_qsv_encoder_deps="libmfx" mjpeg_qsv_encoder_select="qsvenc" mjpeg_vaapi_encoder_deps="VAEncPictureParameterBufferJPEG" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 456ee9bb03..ffd17c9cf0 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -285,7 +285,7 @@ OBJS-$(CONFIG_HEVC_NVENC_ENCODER) += nvenc_hevc.o OBJS-$(CONFIG_HEVC_QSV_DECODER) += qsvdec_h2645.o OBJS-$(CONFIG_HEVC_QSV_ENCODER) += qsvenc_hevc.o hevc_ps_enc.o \ h2645_parse.o hevc_data.o -OBJS-$(CONFIG_HEVC_VAAPI_ENCODER) += vaapi_encode_h265.o vaapi_encode_h26x.o +OBJS-$(CONFIG_HEVC_VAAPI_ENCODER) += vaapi_encode_h265.o OBJS-$(CONFIG_HNM4_VIDEO_DECODER) += hnm4video.o OBJS-$(CONFIG_HQ_HQA_DECODER) += hq_hqa.o hq_hqadata.o hq_hqadsp.o \ canopus.o diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c index 9b029e2e20..f61dfaa1a9 100644 --- a/libavcodec/vaapi_encode_h265.c +++ b/libavcodec/vaapi_encode_h265.c @@ -16,164 +16,25 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include + #include #include #include "libavutil/avassert.h" -#include "libavutil/internal.h" +#include "libavutil/common.h" #include "libavutil/opt.h" -#include "libavutil/pixfmt.h" #include "avcodec.h" +#include "cbs.h" +#include "cbs_h265.h" #include "hevc.h" #include "internal.h" #include "put_bits.h" #include "vaapi_encode.h" -#include "vaapi_encode_h26x.h" -#define MAX_ST_REF_PIC_SETS 32 -#define MAX_DPB_PICS 16 -#define MAX_LAYERS 1 - - -typedef struct VAAPIEncodeH265STRPS { - char inter_ref_pic_set_prediction_flag; - - unsigned int num_negative_pics; - unsigned int num_positive_pics; - - unsigned int delta_poc_s0_minus1[MAX_DPB_PICS]; - char used_by_curr_pic_s0_flag[MAX_DPB_PICS]; - - unsigned int delta_poc_s1_minus1[MAX_DPB_PICS]; - char used_by_curr_pic_s1_flag[MAX_DPB_PICS]; -} VAAPIEncodeH265STRPS; - -// This structure contains all possibly-useful per-sequence syntax elements -// which are not already contained in the various VAAPI structures. -typedef struct VAAPIEncodeH265MiscSequenceParams { - - // Parameter set IDs. - unsigned int video_parameter_set_id; - unsigned int seq_parameter_set_id; - - // Layering. - unsigned int vps_max_layers_minus1; - unsigned int vps_max_sub_layers_minus1; - char vps_temporal_id_nesting_flag; - unsigned int vps_max_layer_id; - unsigned int vps_num_layer_sets_minus1; - unsigned int sps_max_sub_layers_minus1; - char sps_temporal_id_nesting_flag; - char layer_id_included_flag[MAX_LAYERS][64]; - - // Profile/tier/level parameters. - char general_profile_compatibility_flag[32]; - char general_progressive_source_flag; - char general_interlaced_source_flag; - char general_non_packed_constraint_flag; - char general_frame_only_constraint_flag; - char general_inbld_flag; - - // Decode/display ordering parameters. - unsigned int log2_max_pic_order_cnt_lsb_minus4; - char vps_sub_layer_ordering_info_present_flag; - unsigned int vps_max_dec_pic_buffering_minus1[MAX_LAYERS]; - unsigned int vps_max_num_reorder_pics[MAX_LAYERS]; - unsigned int vps_max_latency_increase_plus1[MAX_LAYERS]; - char sps_sub_layer_ordering_info_present_flag; - unsigned int sps_max_dec_pic_buffering_minus1[MAX_LAYERS]; - unsigned int sps_max_num_reorder_pics[MAX_LAYERS]; - unsigned int sps_max_latency_increase_plus1[MAX_LAYERS]; - - // Timing information. - char vps_timing_info_present_flag; - unsigned int vps_num_units_in_tick; - unsigned int vps_time_scale; - char vps_poc_proportional_to_timing_flag; - unsigned int vps_num_ticks_poc_diff_minus1; - - // Cropping information. - char conformance_window_flag; - unsigned int conf_win_left_offset; - unsigned int conf_win_right_offset; - unsigned int conf_win_top_offset; - unsigned int conf_win_bottom_offset; - - // Short-term reference picture sets. - unsigned int num_short_term_ref_pic_sets; - VAAPIEncodeH265STRPS st_ref_pic_set[MAX_ST_REF_PIC_SETS]; - - // Long-term reference pictures. - char long_term_ref_pics_present_flag; - unsigned int num_long_term_ref_pics_sps; - struct { - unsigned int lt_ref_pic_poc_lsb_sps; - char used_by_curr_pic_lt_sps_flag; - } lt_ref_pic; - - // Deblocking filter control. - char deblocking_filter_control_present_flag; - char deblocking_filter_override_enabled_flag; - char pps_deblocking_filter_disabled_flag; - int pps_beta_offset_div2; - int pps_tc_offset_div2; - - // Video Usability Information. - char vui_parameters_present_flag; - char aspect_ratio_info_present_flag; - unsigned int aspect_ratio_idc; - unsigned int sar_width; - unsigned int sar_height; - char video_signal_type_present_flag; - unsigned int video_format; - char video_full_range_flag; - char colour_description_present_flag; - unsigned int colour_primaries; - unsigned int transfer_characteristics; - unsigned int matrix_coeffs; - - // Oddments. - char uniform_spacing_flag; - char output_flag_present_flag; - char cabac_init_present_flag; - unsigned int num_extra_slice_header_bits; - char lists_modification_present_flag; - char pps_slice_chroma_qp_offsets_present_flag; - char pps_slice_chroma_offset_list_enabled_flag; -} VAAPIEncodeH265MiscSequenceParams; - -// This structure contains all possibly-useful per-slice syntax elements -// which are not already contained in the various VAAPI structures. -typedef struct VAAPIEncodeH265MiscSliceParams { - // Slice segments. - char first_slice_segment_in_pic_flag; - unsigned int slice_segment_address; - - // Short-term reference picture sets. - char short_term_ref_pic_set_sps_flag; - unsigned int short_term_ref_pic_idx; - VAAPIEncodeH265STRPS st_ref_pic_set; - - // Deblocking filter. - char deblocking_filter_override_flag; - - // Oddments. - char slice_reserved_flag[8]; - char no_output_of_prior_pics_flag; - char pic_output_flag; -} VAAPIEncodeH265MiscSliceParams; - -typedef struct VAAPIEncodeH265Slice { - VAAPIEncodeH265MiscSliceParams misc_slice_params; - - int64_t pic_order_cnt; -} VAAPIEncodeH265Slice; - typedef struct VAAPIEncodeH265Context { - VAAPIEncodeH265MiscSequenceParams misc_sequence_params; - unsigned int ctu_width; unsigned int ctu_height; @@ -181,17 +42,19 @@ typedef struct VAAPIEncodeH265Context { int fixed_qp_p; int fixed_qp_b; - int64_t last_idr_frame; + H265RawVPS vps; + H265RawSPS sps; + H265RawPPS pps; + H265RawSlice slice; - // Rate control configuration. - struct { - VAEncMiscParameterBuffer misc; - VAEncMiscParameterRateControl rc; - } rc_params; - struct { - VAEncMiscParameterBuffer misc; - VAEncMiscParameterHRD hrd; - } hrd_params; + int64_t last_idr_frame; + int pic_order_cnt; + + int slice_nal_unit; + int slice_type; + + CodedBitstreamContext cbc; + CodedBitstreamFragment current_access_unit; } VAAPIEncodeH265Context; typedef struct VAAPIEncodeH265Options { @@ -199,564 +62,77 @@ typedef struct VAAPIEncodeH265Options { } VAAPIEncodeH265Options; -#define vseq_var(name) vseq->name, name -#define vseq_field(name) vseq->seq_fields.bits.name, name -#define vpic_var(name) vpic->name, name -#define vpic_field(name) vpic->pic_fields.bits.name, name -#define vslice_var(name) vslice->name, name -#define vslice_field(name) vslice->slice_fields.bits.name, name -#define mseq_var(name) mseq->name, name -#define mslice_var(name) mslice->name, name -#define mstrps_var(name) mstrps->name, name - -static void vaapi_encode_h265_write_nal_unit_header(PutBitContext *pbc, - int nal_unit_type) +static int vaapi_encode_h265_write_access_unit(AVCodecContext *avctx, + char *data, size_t *data_len, + CodedBitstreamFragment *au) { - u(1, 0, forbidden_zero_bit); - u(6, nal_unit_type, nal_unit_type); - u(6, 0, nuh_layer_id); - u(3, 1, nuh_temporal_id_plus1); + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH265Context *priv = ctx->priv_data; + int err; + + err = ff_cbs_write_fragment_data(&priv->cbc, au); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to write packed header.\n"); + return err; + } + + if (*data_len < 8 * au->data_size - au->data_bit_padding) { + av_log(avctx, AV_LOG_ERROR, "Access unit too large: " + "%zu < %zu.\n", *data_len, + 8 * au->data_size - au->data_bit_padding); + return AVERROR(ENOSPC); + } + + memcpy(data, au->data, au->data_size); + *data_len = 8 * au->data_size - au->data_bit_padding; + + return 0; } -static void vaapi_encode_h265_write_rbsp_trailing_bits(PutBitContext *pbc) +static int vaapi_encode_h265_add_nal(AVCodecContext *avctx, + CodedBitstreamFragment *au, + void *nal_unit) { - u(1, 1, rbsp_stop_one_bit); - while (put_bits_count(pbc) & 7) - u(1, 0, rbsp_alignment_zero_bit); -} + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH265Context *priv = ctx->priv_data; + H265RawNALUnitHeader *header = nal_unit; + int err; -static void vaapi_encode_h265_write_profile_tier_level(PutBitContext *pbc, - VAAPIEncodeContext *ctx) -{ - VAEncSequenceParameterBufferHEVC *vseq = ctx->codec_sequence_params; - VAAPIEncodeH265Context *priv = ctx->priv_data; - VAAPIEncodeH265MiscSequenceParams *mseq = &priv->misc_sequence_params; - int j; - - if (1) { - u(2, 0, general_profile_space); - u(1, vseq_var(general_tier_flag)); - u(5, vseq_var(general_profile_idc)); - - for (j = 0; j < 32; j++) { - u(1, mseq_var(general_profile_compatibility_flag[j])); - } - - u(1, mseq_var(general_progressive_source_flag)); - u(1, mseq_var(general_interlaced_source_flag)); - u(1, mseq_var(general_non_packed_constraint_flag)); - u(1, mseq_var(general_frame_only_constraint_flag)); - - if (0) { - // Not main profile. - // Lots of extra constraint flags. - } else { - // put_bits only handles up to 31 bits. - u(23, 0, general_reserved_zero_43bits); - u(20, 0, general_reserved_zero_43bits); - } - - if (vseq->general_profile_idc >= 1 && vseq->general_profile_idc <= 5) { - u(1, mseq_var(general_inbld_flag)); - } else { - u(1, 0, general_reserved_zero_bit); - } + err = ff_cbs_insert_unit_content(&priv->cbc, au, -1, + header->nal_unit_type, nal_unit); + if (err < 0) { + av_log(avctx, AV_LOG_ERROR, "Failed to add NAL unit: " + "type = %d.\n", header->nal_unit_type); + return err; } - u(8, vseq_var(general_level_idc)); - - // No sublayers. -} - -static void vaapi_encode_h265_write_vps(PutBitContext *pbc, - VAAPIEncodeContext *ctx) -{ - VAAPIEncodeH265Context *priv = ctx->priv_data; - VAAPIEncodeH265MiscSequenceParams *mseq = &priv->misc_sequence_params; - int i, j; - - vaapi_encode_h265_write_nal_unit_header(pbc, HEVC_NAL_VPS); - - u(4, mseq->video_parameter_set_id, vps_video_parameter_set_id); - - u(1, 1, vps_base_layer_internal_flag); - u(1, 1, vps_base_layer_available_flag); - u(6, mseq_var(vps_max_layers_minus1)); - u(3, mseq_var(vps_max_sub_layers_minus1)); - u(1, mseq_var(vps_temporal_id_nesting_flag)); - - u(16, 0xffff, vps_reserved_0xffff_16bits); - - vaapi_encode_h265_write_profile_tier_level(pbc, ctx); - - u(1, mseq_var(vps_sub_layer_ordering_info_present_flag)); - for (i = (mseq->vps_sub_layer_ordering_info_present_flag ? - 0 : mseq->vps_max_sub_layers_minus1); - i <= mseq->vps_max_sub_layers_minus1; i++) { - ue(mseq_var(vps_max_dec_pic_buffering_minus1[i])); - ue(mseq_var(vps_max_num_reorder_pics[i])); - ue(mseq_var(vps_max_latency_increase_plus1[i])); - } - - u(6, mseq_var(vps_max_layer_id)); - ue(mseq_var(vps_num_layer_sets_minus1)); - for (i = 1; i <= mseq->vps_num_layer_sets_minus1; i++) { - for (j = 0; j < mseq->vps_max_layer_id; j++) - u(1, mseq_var(layer_id_included_flag[i][j])); - } - - u(1, mseq_var(vps_timing_info_present_flag)); - if (mseq->vps_timing_info_present_flag) { - u(1, 0, put_bits_hack_zero_bit); - u(31, mseq_var(vps_num_units_in_tick)); - u(1, 0, put_bits_hack_zero_bit); - u(31, mseq_var(vps_time_scale)); - u(1, mseq_var(vps_poc_proportional_to_timing_flag)); - if (mseq->vps_poc_proportional_to_timing_flag) { - ue(mseq_var(vps_num_ticks_poc_diff_minus1)); - } - ue(0, vps_num_hrd_parameters); - } - - u(1, 0, vps_extension_flag); - - vaapi_encode_h265_write_rbsp_trailing_bits(pbc); -} - -static void vaapi_encode_h265_write_st_ref_pic_set(PutBitContext *pbc, - int st_rps_idx, - VAAPIEncodeH265STRPS *mstrps) -{ - int i; - - if (st_rps_idx != 0) - u(1, mstrps_var(inter_ref_pic_set_prediction_flag)); - - if (mstrps->inter_ref_pic_set_prediction_flag) { - av_assert0(0 && "inter ref pic set prediction not supported"); - } else { - ue(mstrps_var(num_negative_pics)); - ue(mstrps_var(num_positive_pics)); - - for (i = 0; i < mstrps->num_negative_pics; i++) { - ue(mstrps_var(delta_poc_s0_minus1[i])); - u(1, mstrps_var(used_by_curr_pic_s0_flag[i])); - } - for (i = 0; i < mstrps->num_positive_pics; i++) { - ue(mstrps_var(delta_poc_s1_minus1[i])); - u(1, mstrps_var(used_by_curr_pic_s1_flag[i])); - } - } -} - -static void vaapi_encode_h265_write_vui_parameters(PutBitContext *pbc, - VAAPIEncodeContext *ctx) -{ - VAAPIEncodeH265Context *priv = ctx->priv_data; - VAAPIEncodeH265MiscSequenceParams *mseq = &priv->misc_sequence_params; - - u(1, mseq_var(aspect_ratio_info_present_flag)); - if (mseq->aspect_ratio_info_present_flag) { - u(8, mseq_var(aspect_ratio_idc)); - if (mseq->aspect_ratio_idc == 255) { - u(16, mseq_var(sar_width)); - u(16, mseq_var(sar_height)); - } - } - - u(1, 0, overscan_info_present_flag); - - u(1, mseq_var(video_signal_type_present_flag)); - if (mseq->video_signal_type_present_flag) { - u(3, mseq_var(video_format)); - u(1, mseq_var(video_full_range_flag)); - u(1, mseq_var(colour_description_present_flag)); - if (mseq->colour_description_present_flag) { - u(8, mseq_var(colour_primaries)); - u(8, mseq_var(transfer_characteristics)); - u(8, mseq_var(matrix_coeffs)); - } - } - - u(1, 0, chroma_loc_info_present_flag); - u(1, 0, neutral_chroma_indication_flag); - u(1, 0, field_seq_flag); - u(1, 0, frame_field_info_present_flag); - u(1, 0, default_display_window_flag); - u(1, 0, vui_timing_info_present_flag); - u(1, 0, bitstream_restriction_flag_flag); -} - -static void vaapi_encode_h265_write_sps(PutBitContext *pbc, - VAAPIEncodeContext *ctx) -{ - VAEncSequenceParameterBufferHEVC *vseq = ctx->codec_sequence_params; - VAAPIEncodeH265Context *priv = ctx->priv_data; - VAAPIEncodeH265MiscSequenceParams *mseq = &priv->misc_sequence_params; - int i; - - vaapi_encode_h265_write_nal_unit_header(pbc, HEVC_NAL_SPS); - - u(4, mseq->video_parameter_set_id, sps_video_parameter_set_id); - - u(3, mseq_var(sps_max_sub_layers_minus1)); - u(1, mseq_var(sps_temporal_id_nesting_flag)); - - vaapi_encode_h265_write_profile_tier_level(pbc, ctx); - - ue(mseq->seq_parameter_set_id, sps_seq_parameter_set_id); - ue(vseq_field(chroma_format_idc)); - if (vseq->seq_fields.bits.chroma_format_idc == 3) - u(1, 0, separate_colour_plane_flag); - - ue(vseq_var(pic_width_in_luma_samples)); - ue(vseq_var(pic_height_in_luma_samples)); - - u(1, mseq_var(conformance_window_flag)); - if (mseq->conformance_window_flag) { - ue(mseq_var(conf_win_left_offset)); - ue(mseq_var(conf_win_right_offset)); - ue(mseq_var(conf_win_top_offset)); - ue(mseq_var(conf_win_bottom_offset)); - } - - ue(vseq_field(bit_depth_luma_minus8)); - ue(vseq_field(bit_depth_chroma_minus8)); - - ue(mseq_var(log2_max_pic_order_cnt_lsb_minus4)); - - u(1, mseq_var(sps_sub_layer_ordering_info_present_flag)); - for (i = (mseq->sps_sub_layer_ordering_info_present_flag ? - 0 : mseq->sps_max_sub_layers_minus1); - i <= mseq->sps_max_sub_layers_minus1; i++) { - ue(mseq_var(sps_max_dec_pic_buffering_minus1[i])); - ue(mseq_var(sps_max_num_reorder_pics[i])); - ue(mseq_var(sps_max_latency_increase_plus1[i])); - } - - ue(vseq_var(log2_min_luma_coding_block_size_minus3)); - ue(vseq_var(log2_diff_max_min_luma_coding_block_size)); - ue(vseq_var(log2_min_transform_block_size_minus2)); - ue(vseq_var(log2_diff_max_min_transform_block_size)); - ue(vseq_var(max_transform_hierarchy_depth_inter)); - ue(vseq_var(max_transform_hierarchy_depth_intra)); - - u(1, vseq_field(scaling_list_enabled_flag)); - if (vseq->seq_fields.bits.scaling_list_enabled_flag) { - u(1, 0, sps_scaling_list_data_present_flag); - } - - u(1, vseq_field(amp_enabled_flag)); - u(1, vseq_field(sample_adaptive_offset_enabled_flag)); - - u(1, vseq_field(pcm_enabled_flag)); - if (vseq->seq_fields.bits.pcm_enabled_flag) { - u(4, vseq_var(pcm_sample_bit_depth_luma_minus1)); - u(4, vseq_var(pcm_sample_bit_depth_chroma_minus1)); - ue(vseq_var(log2_min_pcm_luma_coding_block_size_minus3)); - ue(vseq->log2_max_pcm_luma_coding_block_size_minus3 - - vseq->log2_min_pcm_luma_coding_block_size_minus3, - log2_diff_max_min_pcm_luma_coding_block_size); - u(1, vseq_field(pcm_loop_filter_disabled_flag)); - } - - ue(mseq_var(num_short_term_ref_pic_sets)); - for (i = 0; i < mseq->num_short_term_ref_pic_sets; i++) - vaapi_encode_h265_write_st_ref_pic_set(pbc, i, - &mseq->st_ref_pic_set[i]); - - u(1, mseq_var(long_term_ref_pics_present_flag)); - if (mseq->long_term_ref_pics_present_flag) { - ue(0, num_long_term_ref_pics_sps); - } - - u(1, vseq_field(sps_temporal_mvp_enabled_flag)); - u(1, vseq_field(strong_intra_smoothing_enabled_flag)); - - u(1, mseq_var(vui_parameters_present_flag)); - if (mseq->vui_parameters_present_flag) { - vaapi_encode_h265_write_vui_parameters(pbc, ctx); - } - - u(1, 0, sps_extension_present_flag); - - vaapi_encode_h265_write_rbsp_trailing_bits(pbc); -} - -static void vaapi_encode_h265_write_pps(PutBitContext *pbc, - VAAPIEncodeContext *ctx) -{ - VAEncPictureParameterBufferHEVC *vpic = ctx->codec_picture_params; - VAAPIEncodeH265Context *priv = ctx->priv_data; - VAAPIEncodeH265MiscSequenceParams *mseq = &priv->misc_sequence_params; - int i; - - vaapi_encode_h265_write_nal_unit_header(pbc, HEVC_NAL_PPS); - - ue(vpic->slice_pic_parameter_set_id, pps_pic_parameter_set_id); - ue(mseq->seq_parameter_set_id, pps_seq_parameter_set_id); - - u(1, vpic_field(dependent_slice_segments_enabled_flag)); - u(1, mseq_var(output_flag_present_flag)); - u(3, mseq_var(num_extra_slice_header_bits)); - u(1, vpic_field(sign_data_hiding_enabled_flag)); - u(1, mseq_var(cabac_init_present_flag)); - - ue(vpic_var(num_ref_idx_l0_default_active_minus1)); - ue(vpic_var(num_ref_idx_l1_default_active_minus1)); - - se(vpic->pic_init_qp - 26, init_qp_minus26); - - u(1, vpic_field(constrained_intra_pred_flag)); - u(1, vpic_field(transform_skip_enabled_flag)); - - u(1, vpic_field(cu_qp_delta_enabled_flag)); - if (vpic->pic_fields.bits.cu_qp_delta_enabled_flag) - ue(vpic_var(diff_cu_qp_delta_depth)); - - se(vpic_var(pps_cb_qp_offset)); - se(vpic_var(pps_cr_qp_offset)); - - u(1, mseq_var(pps_slice_chroma_qp_offsets_present_flag)); - u(1, vpic_field(weighted_pred_flag)); - u(1, vpic_field(weighted_bipred_flag)); - u(1, vpic_field(transquant_bypass_enabled_flag)); - u(1, vpic_field(tiles_enabled_flag)); - u(1, vpic_field(entropy_coding_sync_enabled_flag)); - - if (vpic->pic_fields.bits.tiles_enabled_flag) { - ue(vpic_var(num_tile_columns_minus1)); - ue(vpic_var(num_tile_rows_minus1)); - u(1, mseq_var(uniform_spacing_flag)); - if (!mseq->uniform_spacing_flag) { - for (i = 0; i < vpic->num_tile_columns_minus1; i++) - ue(vpic_var(column_width_minus1[i])); - for (i = 0; i < vpic->num_tile_rows_minus1; i++) - ue(vpic_var(row_height_minus1[i])); - } - u(1, vpic_field(loop_filter_across_tiles_enabled_flag)); - } - - u(1, vpic_field(pps_loop_filter_across_slices_enabled_flag)); - u(1, mseq_var(deblocking_filter_control_present_flag)); - if (mseq->deblocking_filter_control_present_flag) { - u(1, mseq_var(deblocking_filter_override_enabled_flag)); - u(1, mseq_var(pps_deblocking_filter_disabled_flag)); - if (!mseq->pps_deblocking_filter_disabled_flag) { - se(mseq_var(pps_beta_offset_div2)); - se(mseq_var(pps_tc_offset_div2)); - } - } - - u(1, 0, pps_scaling_list_data_present_flag); - // No scaling list data. - - u(1, mseq_var(lists_modification_present_flag)); - ue(vpic_var(log2_parallel_merge_level_minus2)); - u(1, 0, slice_segment_header_extension_present_flag); - u(1, 0, pps_extension_present_flag); - - vaapi_encode_h265_write_rbsp_trailing_bits(pbc); -} - -static void vaapi_encode_h265_write_slice_header2(PutBitContext *pbc, - VAAPIEncodeContext *ctx, - VAAPIEncodePicture *pic, - VAAPIEncodeSlice *slice) -{ - VAEncSequenceParameterBufferHEVC *vseq = ctx->codec_sequence_params; - VAEncPictureParameterBufferHEVC *vpic = pic->codec_picture_params; - VAEncSliceParameterBufferHEVC *vslice = slice->codec_slice_params; - VAAPIEncodeH265Context *priv = ctx->priv_data; - VAAPIEncodeH265MiscSequenceParams *mseq = &priv->misc_sequence_params; - VAAPIEncodeH265Slice *pslice = slice->priv_data; - VAAPIEncodeH265MiscSliceParams *mslice = &pslice->misc_slice_params; - int i; - - vaapi_encode_h265_write_nal_unit_header(pbc, vpic->nal_unit_type); - - u(1, mslice_var(first_slice_segment_in_pic_flag)); - if (vpic->nal_unit_type >= HEVC_NAL_BLA_W_LP && - vpic->nal_unit_type <= 23) - u(1, mslice_var(no_output_of_prior_pics_flag)); - - ue(vslice_var(slice_pic_parameter_set_id)); - - if (!mslice->first_slice_segment_in_pic_flag) { - if (vpic->pic_fields.bits.dependent_slice_segments_enabled_flag) - u(1, vslice_field(dependent_slice_segment_flag)); - u(av_log2((priv->ctu_width * priv->ctu_height) - 1) + 1, - mslice_var(slice_segment_address)); - } - if (!vslice->slice_fields.bits.dependent_slice_segment_flag) { - for (i = 0; i < mseq->num_extra_slice_header_bits; i++) - u(1, mslice_var(slice_reserved_flag[i])); - - ue(vslice_var(slice_type)); - if (mseq->output_flag_present_flag) - u(1, 1, pic_output_flag); - if (vseq->seq_fields.bits.separate_colour_plane_flag) - u(2, vslice_field(colour_plane_id)); - if (vpic->nal_unit_type != HEVC_NAL_IDR_W_RADL && - vpic->nal_unit_type != HEVC_NAL_IDR_N_LP) { - u(4 + mseq->log2_max_pic_order_cnt_lsb_minus4, - (pslice->pic_order_cnt & - ((1 << (mseq->log2_max_pic_order_cnt_lsb_minus4 + 4)) - 1)), - slice_pic_order_cnt_lsb); - - u(1, mslice_var(short_term_ref_pic_set_sps_flag)); - if (!mslice->short_term_ref_pic_set_sps_flag) { - vaapi_encode_h265_write_st_ref_pic_set(pbc, mseq->num_short_term_ref_pic_sets, - &mslice->st_ref_pic_set); - } else if (mseq->num_short_term_ref_pic_sets > 1) { - u(av_log2(mseq->num_short_term_ref_pic_sets - 1) + 1, - mslice_var(short_term_ref_pic_idx)); - } - - if (mseq->long_term_ref_pics_present_flag) { - av_assert0(0); - } - } - - if (vseq->seq_fields.bits.sps_temporal_mvp_enabled_flag) { - u(1, vslice_field(slice_temporal_mvp_enabled_flag)); - } - - if (vseq->seq_fields.bits.sample_adaptive_offset_enabled_flag) { - u(1, vslice_field(slice_sao_luma_flag)); - if (!vseq->seq_fields.bits.separate_colour_plane_flag && - vseq->seq_fields.bits.chroma_format_idc != 0) { - u(1, vslice_field(slice_sao_chroma_flag)); - } - } - - if (vslice->slice_type == HEVC_SLICE_P || vslice->slice_type == HEVC_SLICE_B) { - u(1, vslice_field(num_ref_idx_active_override_flag)); - if (vslice->slice_fields.bits.num_ref_idx_active_override_flag) { - ue(vslice_var(num_ref_idx_l0_active_minus1)); - if (vslice->slice_type == HEVC_SLICE_B) { - ue(vslice_var(num_ref_idx_l1_active_minus1)); - } - } - - if (mseq->lists_modification_present_flag) { - av_assert0(0); - // ref_pic_lists_modification() - } - if (vslice->slice_type == HEVC_SLICE_B) { - u(1, vslice_field(mvd_l1_zero_flag)); - } - if (mseq->cabac_init_present_flag) { - u(1, vslice_field(cabac_init_flag)); - } - if (vslice->slice_fields.bits.slice_temporal_mvp_enabled_flag) { - if (vslice->slice_type == HEVC_SLICE_B) - u(1, vslice_field(collocated_from_l0_flag)); - ue(vpic->collocated_ref_pic_index, collocated_ref_idx); - } - if ((vpic->pic_fields.bits.weighted_pred_flag && - vslice->slice_type == HEVC_SLICE_P) || - (vpic->pic_fields.bits.weighted_bipred_flag && - vslice->slice_type == HEVC_SLICE_B)) { - av_assert0(0); - // pred_weight_table() - } - ue(5 - vslice->max_num_merge_cand, five_minus_max_num_merge_cand); - } - - se(vslice_var(slice_qp_delta)); - if (mseq->pps_slice_chroma_qp_offsets_present_flag) { - se(vslice_var(slice_cb_qp_offset)); - se(vslice_var(slice_cr_qp_offset)); - } - if (mseq->pps_slice_chroma_offset_list_enabled_flag) { - u(1, 0, cu_chroma_qp_offset_enabled_flag); - } - if (mseq->deblocking_filter_override_enabled_flag) { - u(1, mslice_var(deblocking_filter_override_flag)); - } - if (mslice->deblocking_filter_override_flag) { - u(1, vslice_field(slice_deblocking_filter_disabled_flag)); - if (!vslice->slice_fields.bits.slice_deblocking_filter_disabled_flag) { - se(vslice_var(slice_beta_offset_div2)); - se(vslice_var(slice_tc_offset_div2)); - } - } - if (vpic->pic_fields.bits.pps_loop_filter_across_slices_enabled_flag && - (vslice->slice_fields.bits.slice_sao_luma_flag || - vslice->slice_fields.bits.slice_sao_chroma_flag || - vslice->slice_fields.bits.slice_deblocking_filter_disabled_flag)) { - u(1, vslice_field(slice_loop_filter_across_slices_enabled_flag)); - } - - if (vpic->pic_fields.bits.tiles_enabled_flag || - vpic->pic_fields.bits.entropy_coding_sync_enabled_flag) { - // num_entry_point_offsets - } - - if (0) { - // slice_segment_header_extension_length - } - } - - u(1, 1, alignment_bit_equal_to_one); - while (put_bits_count(pbc) & 7) - u(1, 0, alignment_bit_equal_to_zero); + return 0; } static int vaapi_encode_h265_write_sequence_header(AVCodecContext *avctx, char *data, size_t *data_len) { - VAAPIEncodeContext *ctx = avctx->priv_data; - PutBitContext pbc; - char tmp[256]; + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH265Context *priv = ctx->priv_data; + CodedBitstreamFragment *au = &priv->current_access_unit; int err; - size_t nal_len, bit_len, bit_pos, next_len; - bit_len = *data_len; - bit_pos = 0; - - init_put_bits(&pbc, tmp, sizeof(tmp)); - vaapi_encode_h265_write_vps(&pbc, ctx); - nal_len = put_bits_count(&pbc); - flush_put_bits(&pbc); - - next_len = bit_len - bit_pos; - err = ff_vaapi_encode_h26x_nal_unit_to_byte_stream(data + bit_pos / 8, - &next_len, - tmp, nal_len); + err = vaapi_encode_h265_add_nal(avctx, au, &priv->vps); if (err < 0) - return err; - bit_pos += next_len; + goto fail; - init_put_bits(&pbc, tmp, sizeof(tmp)); - vaapi_encode_h265_write_sps(&pbc, ctx); - nal_len = put_bits_count(&pbc); - flush_put_bits(&pbc); - - next_len = bit_len - bit_pos; - err = ff_vaapi_encode_h26x_nal_unit_to_byte_stream(data + bit_pos / 8, - &next_len, - tmp, nal_len); + err = vaapi_encode_h265_add_nal(avctx, au, &priv->sps); if (err < 0) - return err; - bit_pos += next_len; + goto fail; - init_put_bits(&pbc, tmp, sizeof(tmp)); - vaapi_encode_h265_write_pps(&pbc, ctx); - nal_len = put_bits_count(&pbc); - flush_put_bits(&pbc); - - next_len = bit_len - bit_pos; - err = ff_vaapi_encode_h26x_nal_unit_to_byte_stream(data + bit_pos / 8, - &next_len, - tmp, nal_len); + err = vaapi_encode_h265_add_nal(avctx, au, &priv->pps); if (err < 0) - return err; - bit_pos += next_len; + goto fail; - *data_len = bit_pos; - return 0; + err = vaapi_encode_h265_write_access_unit(avctx, data, data_len, au); +fail: + ff_cbs_fragment_uninit(&priv->cbc, au); + return err; } static int vaapi_encode_h265_write_slice_header(AVCodecContext *avctx, @@ -764,195 +140,377 @@ static int vaapi_encode_h265_write_slice_header(AVCodecContext *avctx, VAAPIEncodeSlice *slice, char *data, size_t *data_len) { - VAAPIEncodeContext *ctx = avctx->priv_data; - PutBitContext pbc; - char tmp[256]; - size_t header_len; + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH265Context *priv = ctx->priv_data; + CodedBitstreamFragment *au = &priv->current_access_unit; + int err; - init_put_bits(&pbc, tmp, sizeof(tmp)); - vaapi_encode_h265_write_slice_header2(&pbc, ctx, pic, slice); - header_len = put_bits_count(&pbc); - flush_put_bits(&pbc); + err = vaapi_encode_h265_add_nal(avctx, au, &priv->slice); + if (err < 0) + goto fail; - return ff_vaapi_encode_h26x_nal_unit_to_byte_stream(data, data_len, - tmp, header_len); + err = vaapi_encode_h265_write_access_unit(avctx, data, data_len, au); +fail: + ff_cbs_fragment_uninit(&priv->cbc, au); + return err; } static int vaapi_encode_h265_init_sequence_params(AVCodecContext *avctx) { - VAAPIEncodeContext *ctx = avctx->priv_data; - VAEncSequenceParameterBufferHEVC *vseq = ctx->codec_sequence_params; - VAEncPictureParameterBufferHEVC *vpic = ctx->codec_picture_params; - VAAPIEncodeH265Context *priv = ctx->priv_data; - VAAPIEncodeH265MiscSequenceParams *mseq = &priv->misc_sequence_params; + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH265Context *priv = ctx->priv_data; + H265RawVPS *vps = &priv->vps; + H265RawSPS *sps = &priv->sps; + H265RawPPS *pps = &priv->pps; + H265RawVUI *vui = &sps->vui; + VAEncSequenceParameterBufferHEVC *vseq = ctx->codec_sequence_params; + VAEncPictureParameterBufferHEVC *vpic = ctx->codec_picture_params; int i; - { - // general_profile_space == 0. - vseq->general_profile_idc = 1; // Main profile (ctx->codec_profile?) - vseq->general_tier_flag = 0; + memset(&priv->current_access_unit, 0, + sizeof(priv->current_access_unit)); - vseq->general_level_idc = avctx->level * 3; + memset(vps, 0, sizeof(*vps)); + memset(sps, 0, sizeof(*sps)); + memset(pps, 0, sizeof(*pps)); - vseq->intra_period = 0; - vseq->intra_idr_period = 0; - vseq->ip_period = 0; - vseq->pic_width_in_luma_samples = ctx->surface_width; - vseq->pic_height_in_luma_samples = ctx->surface_height; + // VPS - vseq->seq_fields.bits.chroma_format_idc = 1; // 4:2:0. - vseq->seq_fields.bits.separate_colour_plane_flag = 0; - vseq->seq_fields.bits.bit_depth_luma_minus8 = - avctx->profile == FF_PROFILE_HEVC_MAIN_10 ? 2 : 0; - vseq->seq_fields.bits.bit_depth_chroma_minus8 = - avctx->profile == FF_PROFILE_HEVC_MAIN_10 ? 2 : 0; - // Other misc flags all zero. + vps->nal_unit_header = (H265RawNALUnitHeader) { + .nal_unit_type = HEVC_NAL_VPS, + .nuh_layer_id = 0, + .nuh_temporal_id_plus1 = 1, + }; - // These have to come from the capabilities of the encoder. We have - // no way to query it, so just hardcode ones which worked for me... - // CTB size from 8x8 to 32x32. - vseq->log2_min_luma_coding_block_size_minus3 = 0; - vseq->log2_diff_max_min_luma_coding_block_size = 2; - // Transform size from 4x4 to 32x32. - vseq->log2_min_transform_block_size_minus2 = 0; - vseq->log2_diff_max_min_transform_block_size = 3; - // Full transform hierarchy allowed (2-5). - vseq->max_transform_hierarchy_depth_inter = 3; - vseq->max_transform_hierarchy_depth_intra = 3; + vps->vps_video_parameter_set_id = 0; - vseq->vui_parameters_present_flag = 0; + vps->vps_base_layer_internal_flag = 1; + vps->vps_base_layer_available_flag = 1; + vps->vps_max_layers_minus1 = 0; + vps->vps_max_sub_layers_minus1 = 0; + vps->vps_temporal_id_nesting_flag = 1; - vseq->bits_per_second = avctx->bit_rate; - if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { - vseq->vui_num_units_in_tick = avctx->framerate.den; - vseq->vui_time_scale = avctx->framerate.num; - } else { - vseq->vui_num_units_in_tick = avctx->time_base.num; - vseq->vui_time_scale = avctx->time_base.den; - } + vps->profile_tier_level = (H265RawProfileTierLevel) { + .general_profile_space = 0, + .general_profile_idc = avctx->profile, + .general_tier_flag = 0, - vseq->intra_period = avctx->gop_size; - vseq->intra_idr_period = avctx->gop_size; - vseq->ip_period = ctx->b_per_p + 1; + .general_progressive_source_flag = 1, + .general_interlaced_source_flag = 0, + .general_non_packed_constraint_flag = 1, + .general_frame_only_constraint_flag = 1, + + .general_level_idc = avctx->level, + }; + vps->profile_tier_level.general_profile_compatibility_flag[avctx->profile & 31] = 1; + + vps->vps_sub_layer_ordering_info_present_flag = 0; + vps->vps_max_dec_pic_buffering_minus1[0] = (ctx->b_per_p > 0) + 1; + vps->vps_max_num_reorder_pics[0] = (ctx->b_per_p > 0); + vps->vps_max_latency_increase_plus1[0] = 0; + + vps->vps_max_layer_id = 0; + vps->vps_num_layer_sets_minus1 = 0; + vps->layer_id_included_flag[0][0] = 1; + + vps->vps_timing_info_present_flag = 1; + if (avctx->framerate.num > 0 && avctx->framerate.den > 0) { + vps->vps_num_units_in_tick = avctx->framerate.den; + vps->vps_time_scale = avctx->framerate.num; + vps->vps_poc_proportional_to_timing_flag = 1; + vps->vps_num_ticks_poc_diff_one_minus1 = 0; + } else { + vps->vps_num_units_in_tick = avctx->time_base.num; + vps->vps_time_scale = avctx->time_base.den; + vps->vps_poc_proportional_to_timing_flag = 0; + } + vps->vps_num_hrd_parameters = 0; + + + // SPS + + sps->nal_unit_header = (H265RawNALUnitHeader) { + .nal_unit_type = HEVC_NAL_SPS, + .nuh_layer_id = 0, + .nuh_temporal_id_plus1 = 1, + }; + + sps->sps_video_parameter_set_id = vps->vps_video_parameter_set_id; + + sps->sps_max_sub_layers_minus1 = vps->vps_max_sub_layers_minus1; + sps->sps_temporal_id_nesting_flag = vps->vps_temporal_id_nesting_flag; + + sps->profile_tier_level = vps->profile_tier_level; + + sps->sps_seq_parameter_set_id = 0; + + sps->chroma_format_idc = 1; // YUV 4:2:0. + sps->separate_colour_plane_flag = 0; + + sps->pic_width_in_luma_samples = ctx->surface_width; + sps->pic_height_in_luma_samples = ctx->surface_height; + + if (avctx->width != ctx->surface_width || + avctx->height != ctx->surface_height) { + sps->conformance_window_flag = 1; + sps->conf_win_left_offset = 0; + sps->conf_win_right_offset = + (ctx->surface_width - avctx->width) / 2; + sps->conf_win_top_offset = 0; + sps->conf_win_bottom_offset = + (ctx->surface_height - avctx->height) / 2; + } else { + sps->conformance_window_flag = 0; } - { - vpic->decoded_curr_pic.picture_id = VA_INVALID_ID; - vpic->decoded_curr_pic.flags = VA_PICTURE_HEVC_INVALID; + sps->bit_depth_luma_minus8 = + avctx->profile == FF_PROFILE_HEVC_MAIN_10 ? 2 : 0; + sps->bit_depth_chroma_minus8 = sps->bit_depth_luma_minus8; - for (i = 0; i < FF_ARRAY_ELEMS(vpic->reference_frames); i++) { - vpic->reference_frames[i].picture_id = VA_INVALID_ID; - vpic->reference_frames[i].flags = VA_PICTURE_HEVC_INVALID; - } + sps->log2_max_pic_order_cnt_lsb_minus4 = 8; - vpic->collocated_ref_pic_index = 0xff; - - vpic->last_picture = 0; - - vpic->pic_init_qp = priv->fixed_qp_idr; - - vpic->diff_cu_qp_delta_depth = 0; - vpic->pps_cb_qp_offset = 0; - vpic->pps_cr_qp_offset = 0; - - // tiles_enabled_flag == 0, so ignore num_tile_(rows|columns)_minus1. - - vpic->log2_parallel_merge_level_minus2 = 0; - - // No limit on size. - vpic->ctu_max_bitsize_allowed = 0; - - vpic->num_ref_idx_l0_default_active_minus1 = 0; - vpic->num_ref_idx_l1_default_active_minus1 = 0; - - vpic->slice_pic_parameter_set_id = 0; - - vpic->pic_fields.bits.screen_content_flag = 0; - vpic->pic_fields.bits.enable_gpu_weighted_prediction = 0; - - // Per-CU QP changes are required for non-constant-QP modes. - vpic->pic_fields.bits.cu_qp_delta_enabled_flag = - ctx->va_rc_mode != VA_RC_CQP; + sps->sps_sub_layer_ordering_info_present_flag = + vps->vps_sub_layer_ordering_info_present_flag; + for (i = 0; i <= sps->sps_max_sub_layers_minus1; i++) { + sps->sps_max_dec_pic_buffering_minus1[i] = + vps->vps_max_dec_pic_buffering_minus1[i]; + sps->sps_max_num_reorder_pics[i] = + vps->vps_max_num_reorder_pics[i]; + sps->sps_max_latency_increase_plus1[i] = + vps->vps_max_latency_increase_plus1[i]; } - { - mseq->video_parameter_set_id = 5; - mseq->seq_parameter_set_id = 5; + // These have to come from the capabilities of the encoder. We have no + // way to query them, so just hardcode parameters which work on the Intel + // driver. + // CTB size from 8x8 to 32x32. + sps->log2_min_luma_coding_block_size_minus3 = 0; + sps->log2_diff_max_min_luma_coding_block_size = 2; + // Transform size from 4x4 to 32x32. + sps->log2_min_luma_transform_block_size_minus2 = 0; + sps->log2_diff_max_min_luma_transform_block_size = 3; + // Full transform hierarchy allowed (2-5). + sps->max_transform_hierarchy_depth_inter = 3; + sps->max_transform_hierarchy_depth_intra = 3; + // AMP works. + sps->amp_enabled_flag = 1; + // SAO and temporal MVP do not work. + sps->sample_adaptive_offset_enabled_flag = 0; + sps->sps_temporal_mvp_enabled_flag = 0; - mseq->vps_max_layers_minus1 = 0; - mseq->vps_max_sub_layers_minus1 = 0; - mseq->vps_temporal_id_nesting_flag = 1; - mseq->sps_max_sub_layers_minus1 = 0; - mseq->sps_temporal_id_nesting_flag = 1; + sps->pcm_enabled_flag = 0; - for (i = 0; i < 32; i++) { - mseq->general_profile_compatibility_flag[i] = - (i == vseq->general_profile_idc); - } + // STRPSs should ideally be here rather than defined individually in + // each slice, but the structure isn't completely fixed so for now + // don't bother. + sps->num_short_term_ref_pic_sets = 0; + sps->long_term_ref_pics_present_flag = 0; - mseq->general_progressive_source_flag = 1; - mseq->general_interlaced_source_flag = 0; - mseq->general_non_packed_constraint_flag = 0; - mseq->general_frame_only_constraint_flag = 1; - mseq->general_inbld_flag = 0; + sps->vui_parameters_present_flag = 1; - mseq->log2_max_pic_order_cnt_lsb_minus4 = 8; - mseq->vps_sub_layer_ordering_info_present_flag = 0; - mseq->vps_max_dec_pic_buffering_minus1[0] = (avctx->max_b_frames > 0) + 1; - mseq->vps_max_num_reorder_pics[0] = (avctx->max_b_frames > 0); - mseq->vps_max_latency_increase_plus1[0] = 0; - mseq->sps_sub_layer_ordering_info_present_flag = 0; - mseq->sps_max_dec_pic_buffering_minus1[0] = (avctx->max_b_frames > 0) + 1; - mseq->sps_max_num_reorder_pics[0] = (avctx->max_b_frames > 0); - mseq->sps_max_latency_increase_plus1[0] = 0; - - mseq->vps_timing_info_present_flag = 1; - mseq->vps_num_units_in_tick = avctx->time_base.num; - mseq->vps_time_scale = avctx->time_base.den; - mseq->vps_poc_proportional_to_timing_flag = 1; - mseq->vps_num_ticks_poc_diff_minus1 = 0; - - if (avctx->width != ctx->surface_width || - avctx->height != ctx->surface_height) { - mseq->conformance_window_flag = 1; - mseq->conf_win_left_offset = 0; - mseq->conf_win_right_offset = - (ctx->surface_width - avctx->width) / 2; - mseq->conf_win_top_offset = 0; - mseq->conf_win_bottom_offset = - (ctx->surface_height - avctx->height) / 2; - } else { - mseq->conformance_window_flag = 0; - } - - mseq->num_short_term_ref_pic_sets = 0; - // STRPSs should ideally be here rather than repeated in each slice. - - mseq->vui_parameters_present_flag = 1; - if (avctx->sample_aspect_ratio.num != 0) { - mseq->aspect_ratio_info_present_flag = 1; - if (avctx->sample_aspect_ratio.num == - avctx->sample_aspect_ratio.den) { - mseq->aspect_ratio_idc = 1; - } else { - mseq->aspect_ratio_idc = 255; // Extended SAR. - mseq->sar_width = avctx->sample_aspect_ratio.num; - mseq->sar_height = avctx->sample_aspect_ratio.den; + if (avctx->sample_aspect_ratio.num != 0 && + avctx->sample_aspect_ratio.den != 0) { + static const AVRational sar_idc[] = { + { 0, 0 }, + { 1, 1 }, { 12, 11 }, { 10, 11 }, { 16, 11 }, + { 40, 33 }, { 24, 11 }, { 20, 11 }, { 32, 11 }, + { 80, 33 }, { 18, 11 }, { 15, 11 }, { 64, 33 }, + { 160, 99 }, { 4, 3 }, { 3, 2 }, { 2, 1 }, + }; + int i; + for (i = 0; i < FF_ARRAY_ELEMS(sar_idc); i++) { + if (avctx->sample_aspect_ratio.num == sar_idc[i].num && + avctx->sample_aspect_ratio.den == sar_idc[i].den) { + vui->aspect_ratio_idc = i; + break; } } - if (1) { - // Should this be conditional on some of these being set? - mseq->video_signal_type_present_flag = 1; - mseq->video_format = 5; // Unspecified. - mseq->video_full_range_flag = 0; - mseq->colour_description_present_flag = 1; - mseq->colour_primaries = avctx->color_primaries; - mseq->transfer_characteristics = avctx->color_trc; - mseq->matrix_coeffs = avctx->colorspace; + if (i >= FF_ARRAY_ELEMS(sar_idc)) { + vui->aspect_ratio_idc = 255; + vui->sar_width = avctx->sample_aspect_ratio.num; + vui->sar_height = avctx->sample_aspect_ratio.den; } + vui->aspect_ratio_info_present_flag = 1; } + if (avctx->color_range != AVCOL_RANGE_UNSPECIFIED || + avctx->color_primaries != AVCOL_PRI_UNSPECIFIED || + avctx->color_trc != AVCOL_TRC_UNSPECIFIED || + avctx->colorspace != AVCOL_SPC_UNSPECIFIED) { + vui->video_signal_type_present_flag = 1; + vui->video_format = 5; // Unspecified. + vui->video_full_range_flag = + avctx->color_range == AVCOL_RANGE_JPEG; + + if (avctx->color_primaries != AVCOL_PRI_UNSPECIFIED || + avctx->color_trc != AVCOL_TRC_UNSPECIFIED || + avctx->colorspace != AVCOL_SPC_UNSPECIFIED) { + vui->colour_description_present_flag = 1; + vui->colour_primaries = avctx->color_primaries; + vui->transfer_characteristics = avctx->color_trc; + vui->matrix_coefficients = avctx->colorspace; + } + } else { + vui->video_format = 5; + vui->video_full_range_flag = 0; + vui->colour_primaries = avctx->color_primaries; + vui->transfer_characteristics = avctx->color_trc; + vui->matrix_coefficients = avctx->colorspace; + } + + if (avctx->chroma_sample_location != AVCHROMA_LOC_UNSPECIFIED) { + vui->chroma_loc_info_present_flag = 1; + vui->chroma_sample_loc_type_top_field = + vui->chroma_sample_loc_type_bottom_field = + avctx->chroma_sample_location - 1; + } + + vui->vui_timing_info_present_flag = 1; + vui->vui_num_units_in_tick = vps->vps_num_units_in_tick; + vui->vui_time_scale = vps->vps_time_scale; + vui->vui_poc_proportional_to_timing_flag = vps->vps_poc_proportional_to_timing_flag; + vui->vui_num_ticks_poc_diff_one_minus1 = vps->vps_num_ticks_poc_diff_one_minus1; + vui->vui_hrd_parameters_present_flag = 0; + + vui->bitstream_restriction_flag = 1; + vui->motion_vectors_over_pic_boundaries_flag = 1; + vui->restricted_ref_pic_lists_flag = 1; + vui->max_bytes_per_pic_denom = 0; + vui->max_bits_per_min_cu_denom = 0; + vui->log2_max_mv_length_horizontal = 15; + vui->log2_max_mv_length_vertical = 15; + + + // PPS + + pps->nal_unit_header = (H265RawNALUnitHeader) { + .nal_unit_type = HEVC_NAL_PPS, + .nuh_layer_id = 0, + .nuh_temporal_id_plus1 = 1, + }; + + pps->pps_pic_parameter_set_id = 0; + pps->pps_seq_parameter_set_id = sps->sps_seq_parameter_set_id; + + pps->num_ref_idx_l0_default_active_minus1 = 0; + pps->num_ref_idx_l1_default_active_minus1 = 0; + + pps->init_qp_minus26 = priv->fixed_qp_idr - 26; + + pps->cu_qp_delta_enabled_flag = (ctx->va_rc_mode != VA_RC_CQP); + pps->diff_cu_qp_delta_depth = 0; + + pps->pps_loop_filter_across_slices_enabled_flag = 1; + + + // Fill VAAPI parameter buffers. + + *vseq = (VAEncSequenceParameterBufferHEVC) { + .general_profile_idc = vps->profile_tier_level.general_profile_idc, + .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, + .ip_period = ctx->b_per_p + 1, + .bits_per_second = avctx->bit_rate, + + .pic_width_in_luma_samples = sps->pic_width_in_luma_samples, + .pic_height_in_luma_samples = sps->pic_height_in_luma_samples, + + .seq_fields.bits = { + .chroma_format_idc = sps->chroma_format_idc, + .separate_colour_plane_flag = sps->separate_colour_plane_flag, + .bit_depth_luma_minus8 = sps->bit_depth_luma_minus8, + .bit_depth_chroma_minus8 = sps->bit_depth_chroma_minus8, + .scaling_list_enabled_flag = sps->scaling_list_enabled_flag, + .strong_intra_smoothing_enabled_flag = + sps->strong_intra_smoothing_enabled_flag, + .amp_enabled_flag = sps->amp_enabled_flag, + .sample_adaptive_offset_enabled_flag = + sps->sample_adaptive_offset_enabled_flag, + .pcm_enabled_flag = sps->pcm_enabled_flag, + .pcm_loop_filter_disabled_flag = sps->pcm_loop_filter_disabled_flag, + .sps_temporal_mvp_enabled_flag = sps->sps_temporal_mvp_enabled_flag, + }, + + .log2_min_luma_coding_block_size_minus3 = + sps->log2_min_luma_coding_block_size_minus3, + .log2_diff_max_min_luma_coding_block_size = + sps->log2_diff_max_min_luma_coding_block_size, + .log2_min_transform_block_size_minus2 = + sps->log2_min_luma_transform_block_size_minus2, + .log2_diff_max_min_transform_block_size = + sps->log2_diff_max_min_luma_transform_block_size, + .max_transform_hierarchy_depth_inter = + sps->max_transform_hierarchy_depth_inter, + .max_transform_hierarchy_depth_intra = + sps->max_transform_hierarchy_depth_intra, + + .pcm_sample_bit_depth_luma_minus1 = + sps->pcm_sample_bit_depth_luma_minus1, + .pcm_sample_bit_depth_chroma_minus1 = + sps->pcm_sample_bit_depth_chroma_minus1, + .log2_min_pcm_luma_coding_block_size_minus3 = + sps->log2_min_pcm_luma_coding_block_size_minus3, + .log2_max_pcm_luma_coding_block_size_minus3 = + sps->log2_min_pcm_luma_coding_block_size_minus3 + + sps->log2_diff_max_min_pcm_luma_coding_block_size, + + .vui_parameters_present_flag = 0, + }; + + *vpic = (VAEncPictureParameterBufferHEVC) { + .decoded_curr_pic = { + .picture_id = VA_INVALID_ID, + .flags = VA_PICTURE_HEVC_INVALID, + }, + + .coded_buf = VA_INVALID_ID, + + .collocated_ref_pic_index = 0xff, + + .last_picture = 0, + + .pic_init_qp = pps->init_qp_minus26 + 26, + .diff_cu_qp_delta_depth = pps->diff_cu_qp_delta_depth, + .pps_cb_qp_offset = pps->pps_cb_qp_offset, + .pps_cr_qp_offset = pps->pps_cr_qp_offset, + + .num_tile_columns_minus1 = pps->num_tile_columns_minus1, + .num_tile_rows_minus1 = pps->num_tile_rows_minus1, + + .log2_parallel_merge_level_minus2 = pps->log2_parallel_merge_level_minus2, + .ctu_max_bitsize_allowed = 0, + + .num_ref_idx_l0_default_active_minus1 = + pps->num_ref_idx_l0_default_active_minus1, + .num_ref_idx_l1_default_active_minus1 = + pps->num_ref_idx_l1_default_active_minus1, + + .slice_pic_parameter_set_id = pps->pps_pic_parameter_set_id, + + .pic_fields.bits = { + .sign_data_hiding_enabled_flag = pps->sign_data_hiding_enabled_flag, + .constrained_intra_pred_flag = pps->constrained_intra_pred_flag, + .transform_skip_enabled_flag = pps->transform_skip_enabled_flag, + .cu_qp_delta_enabled_flag = pps->cu_qp_delta_enabled_flag, + .weighted_pred_flag = pps->weighted_pred_flag, + .weighted_bipred_flag = pps->weighted_bipred_flag, + .transquant_bypass_enabled_flag = pps->transquant_bypass_enabled_flag, + .tiles_enabled_flag = pps->tiles_enabled_flag, + .entropy_coding_sync_enabled_flag = pps->entropy_coding_sync_enabled_flag, + .loop_filter_across_tiles_enabled_flag = + pps->loop_filter_across_tiles_enabled_flag, + .scaling_list_data_present_flag = (sps->sps_scaling_list_data_present_flag | + pps->pps_scaling_list_data_present_flag), + .screen_content_flag = 0, + .enable_gpu_weighted_prediction = 0, + .no_output_of_prior_pics_flag = 0, + }, + }; + return 0; } @@ -960,65 +518,87 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx, VAAPIEncodePicture *pic) { VAAPIEncodeContext *ctx = avctx->priv_data; - VAEncPictureParameterBufferHEVC *vpic = pic->codec_picture_params; VAAPIEncodeH265Context *priv = ctx->priv_data; + VAEncPictureParameterBufferHEVC *vpic = pic->codec_picture_params; int i; if (pic->type == PICTURE_TYPE_IDR) { av_assert0(pic->display_order == pic->encode_order); + priv->last_idr_frame = pic->display_order; + + priv->slice_nal_unit = HEVC_NAL_IDR_W_RADL; + priv->slice_type = HEVC_SLICE_I; } else { av_assert0(pic->encode_order > priv->last_idr_frame); - // Display order need not be if we have RA[SD]L pictures, though. - } - vpic->decoded_curr_pic.picture_id = pic->recon_surface; - vpic->decoded_curr_pic.pic_order_cnt = - pic->display_order - priv->last_idr_frame; - vpic->decoded_curr_pic.flags = 0; + if (pic->type == PICTURE_TYPE_I) { + priv->slice_nal_unit = HEVC_NAL_CRA_NUT; + priv->slice_type = HEVC_SLICE_I; + } else if (pic->type == PICTURE_TYPE_P) { + av_assert0(pic->refs[0]); + priv->slice_nal_unit = HEVC_NAL_TRAIL_R; + priv->slice_type = HEVC_SLICE_P; + } else { + av_assert0(pic->refs[0] && pic->refs[1]); + if (pic->refs[1]->type == PICTURE_TYPE_I) + priv->slice_nal_unit = HEVC_NAL_RASL_N; + else + priv->slice_nal_unit = HEVC_NAL_TRAIL_N; + priv->slice_type = HEVC_SLICE_B; + } + } + priv->pic_order_cnt = pic->display_order - priv->last_idr_frame; + + vpic->decoded_curr_pic = (VAPictureHEVC) { + .picture_id = pic->recon_surface, + .pic_order_cnt = priv->pic_order_cnt, + .flags = 0, + }; for (i = 0; i < pic->nb_refs; i++) { VAAPIEncodePicture *ref = pic->refs[i]; - av_assert0(ref); - vpic->reference_frames[i].picture_id = ref->recon_surface; - vpic->reference_frames[i].pic_order_cnt = - ref->display_order - priv->last_idr_frame; - vpic->reference_frames[i].flags = - (ref->display_order < pic->display_order ? - VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE : 0) | - (ref->display_order > pic->display_order ? - VA_PICTURE_HEVC_RPS_ST_CURR_AFTER : 0); + av_assert0(ref && ref->encode_order < pic->encode_order); + + vpic->reference_frames[i] = (VAPictureHEVC) { + .picture_id = ref->recon_surface, + .pic_order_cnt = ref->display_order - priv->last_idr_frame, + .flags = (ref->display_order < pic->display_order ? + VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE : 0) | + (ref->display_order > pic->display_order ? + VA_PICTURE_HEVC_RPS_ST_CURR_AFTER : 0), + }; } for (; i < FF_ARRAY_ELEMS(vpic->reference_frames); i++) { - vpic->reference_frames[i].picture_id = VA_INVALID_ID; - vpic->reference_frames[i].flags = VA_PICTURE_HEVC_INVALID; + vpic->reference_frames[i] = (VAPictureHEVC) { + .picture_id = VA_INVALID_ID, + .flags = VA_PICTURE_HEVC_INVALID, + }; } vpic->coded_buf = pic->output_buffer; + vpic->nal_unit_type = priv->slice_nal_unit; + switch (pic->type) { case PICTURE_TYPE_IDR: - vpic->nal_unit_type = HEVC_NAL_IDR_W_RADL; - vpic->pic_fields.bits.idr_pic_flag = 1; - vpic->pic_fields.bits.coding_type = 1; + vpic->pic_fields.bits.idr_pic_flag = 1; + vpic->pic_fields.bits.coding_type = 1; vpic->pic_fields.bits.reference_pic_flag = 1; break; case PICTURE_TYPE_I: - vpic->nal_unit_type = HEVC_NAL_TRAIL_R; - vpic->pic_fields.bits.idr_pic_flag = 0; - vpic->pic_fields.bits.coding_type = 1; + vpic->pic_fields.bits.idr_pic_flag = 0; + vpic->pic_fields.bits.coding_type = 1; vpic->pic_fields.bits.reference_pic_flag = 1; break; case PICTURE_TYPE_P: - vpic->nal_unit_type = HEVC_NAL_TRAIL_R; - vpic->pic_fields.bits.idr_pic_flag = 0; - vpic->pic_fields.bits.coding_type = 2; + vpic->pic_fields.bits.idr_pic_flag = 0; + vpic->pic_fields.bits.coding_type = 2; vpic->pic_fields.bits.reference_pic_flag = 1; break; case PICTURE_TYPE_B: - vpic->nal_unit_type = HEVC_NAL_TRAIL_R; - vpic->pic_fields.bits.idr_pic_flag = 0; - vpic->pic_fields.bits.coding_type = 3; + vpic->pic_fields.bits.idr_pic_flag = 0; + vpic->pic_fields.bits.coding_type = 3; vpic->pic_fields.bits.reference_pic_flag = 0; break; default: @@ -1035,90 +615,40 @@ 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; VAEncPictureParameterBufferHEVC *vpic = pic->codec_picture_params; VAEncSliceParameterBufferHEVC *vslice = slice->codec_slice_params; - VAAPIEncodeH265Context *priv = ctx->priv_data; - VAAPIEncodeH265Slice *pslice; - VAAPIEncodeH265MiscSliceParams *mslice; int i; - slice->priv_data = av_mallocz(sizeof(*pslice)); - if (!slice->priv_data) - return AVERROR(ENOMEM); - pslice = slice->priv_data; - mslice = &pslice->misc_slice_params; + sh->nal_unit_header = (H265RawNALUnitHeader) { + .nal_unit_type = priv->slice_nal_unit, + .nuh_layer_id = 0, + .nuh_temporal_id_plus1 = 1, + }; + + sh->slice_pic_parameter_set_id = pps->pps_pic_parameter_set_id; // Currently we only support one slice per frame. - vslice->slice_segment_address = 0; - vslice->num_ctu_in_slice = priv->ctu_width * priv->ctu_height; + sh->first_slice_segment_in_pic_flag = 1; + sh->slice_segment_address = 0; - switch (pic->type) { - case PICTURE_TYPE_IDR: - case PICTURE_TYPE_I: - vslice->slice_type = HEVC_SLICE_I; - break; - case PICTURE_TYPE_P: - vslice->slice_type = HEVC_SLICE_P; - break; - case PICTURE_TYPE_B: - vslice->slice_type = HEVC_SLICE_B; - break; - default: - av_assert0(0 && "invalid picture type"); - } + sh->slice_type = priv->slice_type; - vslice->slice_pic_parameter_set_id = vpic->slice_pic_parameter_set_id; + sh->slice_pic_order_cnt_lsb = priv->pic_order_cnt & + (1 << (sps->log2_max_pic_order_cnt_lsb_minus4 + 4)) - 1; - pslice->pic_order_cnt = pic->display_order - priv->last_idr_frame; - - for (i = 0; i < FF_ARRAY_ELEMS(vslice->ref_pic_list0); i++) { - vslice->ref_pic_list0[i].picture_id = VA_INVALID_ID; - vslice->ref_pic_list0[i].flags = VA_PICTURE_HEVC_INVALID; - vslice->ref_pic_list1[i].picture_id = VA_INVALID_ID; - vslice->ref_pic_list1[i].flags = VA_PICTURE_HEVC_INVALID; - } - - av_assert0(pic->nb_refs <= 2); - if (pic->nb_refs >= 1) { - // Backward reference for P- or B-frame. - av_assert0(pic->type == PICTURE_TYPE_P || - pic->type == PICTURE_TYPE_B); - - vslice->num_ref_idx_l0_active_minus1 = 0; - vslice->ref_pic_list0[0] = vpic->reference_frames[0]; - } - if (pic->nb_refs >= 2) { - // Forward reference for B-frame. - av_assert0(pic->type == PICTURE_TYPE_B); - - vslice->num_ref_idx_l1_active_minus1 = 0; - vslice->ref_pic_list1[0] = vpic->reference_frames[1]; - } - - vslice->max_num_merge_cand = 5; - - if (pic->type == PICTURE_TYPE_B) - vslice->slice_qp_delta = priv->fixed_qp_b - vpic->pic_init_qp; - else if (pic->type == PICTURE_TYPE_P) - vslice->slice_qp_delta = priv->fixed_qp_p - vpic->pic_init_qp; - else - vslice->slice_qp_delta = priv->fixed_qp_idr - vpic->pic_init_qp; - - vslice->slice_fields.bits.last_slice_of_pic_flag = 1; - - mslice->first_slice_segment_in_pic_flag = 1; - - if (pic->type == PICTURE_TYPE_IDR) { - // No reference pictures. - } else if (0) { - mslice->short_term_ref_pic_set_sps_flag = 1; - mslice->short_term_ref_pic_idx = 0; - } else { + if (pic->type != PICTURE_TYPE_IDR) { + H265RawSTRefPicSet *rps; VAAPIEncodePicture *st; int used; - mslice->short_term_ref_pic_set_sps_flag = 0; - mslice->st_ref_pic_set.inter_ref_pic_set_prediction_flag = 0; + sh->short_term_ref_pic_set_sps_flag = 0; + + rps = &sh->short_term_ref_pic_set; + memset(rps, 0, sizeof(*rps)); for (st = ctx->pic_start; st; st = st->next) { if (st->encode_order >= pic->encode_order) { @@ -1131,28 +661,111 @@ static int vaapi_encode_h265_init_slice_params(AVCodecContext *avctx, used = 1; } if (!used) { - // Currently true, but need not be. - continue; + // Usually each picture always uses all of the others in the + // DPB as references. The one case we have to treat here is + // a non-IDR IRAP picture, which may need to hold unused + // references across itself to be used for the decoding of + // following RASL pictures. This looks for such an RASL + // picture, and keeps the reference if there is one. + VAAPIEncodePicture *rp; + for (rp = ctx->pic_start; rp; rp = rp->next) { + if (rp->encode_order < pic->encode_order) + continue; + if (rp->type != PICTURE_TYPE_B) + continue; + if (rp->refs[0] == st && rp->refs[1] == pic) + break; + } + if (!rp) + continue; } // This only works for one instance of each (delta_poc_sN_minus1 // is relative to the previous frame in the list, not relative to // the current frame directly). if (st->display_order < pic->display_order) { - i = mslice->st_ref_pic_set.num_negative_pics; - mslice->st_ref_pic_set.delta_poc_s0_minus1[i] = + rps->delta_poc_s0_minus1[rps->num_negative_pics] = pic->display_order - st->display_order - 1; - mslice->st_ref_pic_set.used_by_curr_pic_s0_flag[i] = used; - ++mslice->st_ref_pic_set.num_negative_pics; + rps->used_by_curr_pic_s0_flag[rps->num_negative_pics] = used; + ++rps->num_negative_pics; } else { - i = mslice->st_ref_pic_set.num_positive_pics; - mslice->st_ref_pic_set.delta_poc_s1_minus1[i] = + rps->delta_poc_s1_minus1[rps->num_positive_pics] = st->display_order - pic->display_order - 1; - mslice->st_ref_pic_set.used_by_curr_pic_s1_flag[i] = used; - ++mslice->st_ref_pic_set.num_positive_pics; + rps->used_by_curr_pic_s1_flag[rps->num_positive_pics] = used; + ++rps->num_positive_pics; } } + + sh->num_long_term_sps = 0; + sh->num_long_term_pics = 0; + + sh->slice_temporal_mvp_enabled_flag = + sps->sps_temporal_mvp_enabled_flag; + if (sh->slice_temporal_mvp_enabled_flag) { + sh->collocated_from_l0_flag = sh->slice_type == HEVC_SLICE_B; + sh->collocated_ref_idx = 0; + } + + sh->num_ref_idx_active_override_flag = 0; + sh->num_ref_idx_l0_active_minus1 = pps->num_ref_idx_l0_default_active_minus1; + sh->num_ref_idx_l1_active_minus1 = pps->num_ref_idx_l1_default_active_minus1; } + sh->slice_sao_luma_flag = sh->slice_sao_chroma_flag = + sps->sample_adaptive_offset_enabled_flag; + + if (pic->type == PICTURE_TYPE_B) + sh->slice_qp_delta = priv->fixed_qp_b - (pps->init_qp_minus26 + 26); + else if (pic->type == PICTURE_TYPE_P) + sh->slice_qp_delta = priv->fixed_qp_p - (pps->init_qp_minus26 + 26); + else + sh->slice_qp_delta = priv->fixed_qp_idr - (pps->init_qp_minus26 + 26); + + + *vslice = (VAEncSliceParameterBufferHEVC) { + .slice_segment_address = sh->slice_segment_address, + .num_ctu_in_slice = priv->ctu_width * priv->ctu_height, + + .slice_type = sh->slice_type, + .slice_pic_parameter_set_id = sh->slice_pic_parameter_set_id, + + .num_ref_idx_l0_active_minus1 = sh->num_ref_idx_l0_active_minus1, + .num_ref_idx_l1_active_minus1 = sh->num_ref_idx_l1_active_minus1, + .ref_pic_list0[0] = vpic->reference_frames[0], + .ref_pic_list1[0] = vpic->reference_frames[1], + + .luma_log2_weight_denom = sh->luma_log2_weight_denom, + .delta_chroma_log2_weight_denom = sh->delta_chroma_log2_weight_denom, + + .max_num_merge_cand = 5 - sh->five_minus_max_num_merge_cand, + + .slice_qp_delta = sh->slice_qp_delta, + .slice_cb_qp_offset = sh->slice_cb_qp_offset, + .slice_cr_qp_offset = sh->slice_cr_qp_offset, + + .slice_beta_offset_div2 = sh->slice_beta_offset_div2, + .slice_tc_offset_div2 = sh->slice_tc_offset_div2, + + .slice_fields.bits = { + .last_slice_of_pic_flag = 1, + .dependent_slice_segment_flag = sh->dependent_slice_segment_flag, + .colour_plane_id = sh->colour_plane_id, + .slice_temporal_mvp_enabled_flag = + sh->slice_temporal_mvp_enabled_flag, + .slice_sao_luma_flag = sh->slice_sao_luma_flag, + .slice_sao_chroma_flag = sh->slice_sao_chroma_flag, + .num_ref_idx_active_override_flag = + sh->num_ref_idx_active_override_flag, + .mvd_l1_zero_flag = sh->mvd_l1_zero_flag, + .cabac_init_flag = sh->cabac_init_flag, + .slice_deblocking_filter_disabled_flag = + sh->slice_deblocking_filter_disabled_flag, + .slice_loop_filter_across_slices_enabled_flag = + sh->slice_loop_filter_across_slices_enabled_flag, + .collocated_from_l0_flag = sh->collocated_from_l0_flag, + }, + }; + + return 0; } @@ -1161,6 +774,11 @@ 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; + int err; + + err = ff_cbs_init(&priv->cbc, AV_CODEC_ID_HEVC, avctx); + if (err < 0) + return err; priv->ctu_width = FFALIGN(ctx->surface_width, 32) / 32; priv->ctu_height = FFALIGN(ctx->surface_height, 32) / 32; @@ -1267,6 +885,17 @@ static av_cold int vaapi_encode_h265_init(AVCodecContext *avctx) return ff_vaapi_encode_init(avctx); } +static av_cold int vaapi_encode_h265_close(AVCodecContext *avctx) +{ + VAAPIEncodeContext *ctx = avctx->priv_data; + VAAPIEncodeH265Context *priv = ctx->priv_data; + + if (priv) + ff_cbs_close(&priv->cbc); + + return ff_vaapi_encode_close(avctx); +} + #define OFFSET(x) (offsetof(VAAPIEncodeContext, codec_options_data) + \ offsetof(VAAPIEncodeH265Options, x)) #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM) @@ -1305,7 +934,7 @@ AVCodec ff_hevc_vaapi_encoder = { sizeof(VAAPIEncodeH265Options)), .init = &vaapi_encode_h265_init, .encode2 = &ff_vaapi_encode2, - .close = &ff_vaapi_encode_close, + .close = &vaapi_encode_h265_close, .priv_class = &vaapi_encode_h265_class, .capabilities = AV_CODEC_CAP_DELAY, .defaults = vaapi_encode_h265_defaults, diff --git a/libavcodec/vaapi_encode_h26x.c b/libavcodec/vaapi_encode_h26x.c deleted file mode 100644 index bf9eb92135..0000000000 --- a/libavcodec/vaapi_encode_h26x.c +++ /dev/null @@ -1,68 +0,0 @@ -/* - * This file is part of Libav. - * - * Libav is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * Libav is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include - -#include "vaapi_encode_h26x.h" - -int ff_vaapi_encode_h26x_nal_unit_to_byte_stream(uint8_t *dst, size_t *dst_bit_len, - uint8_t *src, size_t src_bit_len) -{ - size_t dp, sp; - int zero_run = 0; - size_t dst_len = *dst_bit_len / 8; - size_t src_len = (src_bit_len + 7) / 8; - int trailing_zeroes = src_len * 8 - src_bit_len; - - if (dst_len < src_len + 4) { - // Definitely doesn't fit. - goto fail; - } - - // Start code. - dst[0] = dst[1] = dst[2] = 0; - dst[3] = 1; - dp = 4; - - for (sp = 0; sp < src_len; sp++) { - if (dp >= dst_len) - goto fail; - if (zero_run < 2) { - if (src[sp] == 0) - ++zero_run; - else - zero_run = 0; - } else { - if ((src[sp] & ~3) == 0) { - // emulation_prevention_three_byte - dst[dp++] = 3; - if (dp >= dst_len) - goto fail; - } - zero_run = src[sp] == 0; - } - dst[dp++] = src[sp]; - } - - *dst_bit_len = 8 * dp - trailing_zeroes; - return 0; - -fail: - *dst_bit_len = 0; - return AVERROR(ENOSPC); -} diff --git a/libavcodec/vaapi_encode_h26x.h b/libavcodec/vaapi_encode_h26x.h deleted file mode 100644 index d6db69a0ea..0000000000 --- a/libavcodec/vaapi_encode_h26x.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of Libav. - * - * Libav is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * Libav is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with Libav; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef AVCODEC_VAAPI_ENCODE_H26X_H -#define AVCODEC_VAAPI_ENCODE_H26X_H - -#include -#include - -#include "golomb.h" -#include "put_bits.h" - - -// Debug code may be interested in the name of the syntax element being -// for tracing purposes. Here, it is just discarded. - -#define write_u(pbc, width, value, name) put_bits(pbc, width, value) -#define write_ue(pbc, value, name) set_ue_golomb(pbc, value) -#define write_se(pbc, value, name) set_se_golomb(pbc, value) - -#define u(width, ...) write_u(pbc, width, __VA_ARGS__) -#define ue(...) write_ue(pbc, __VA_ARGS__) -#define se(...) write_se(pbc, __VA_ARGS__) - - -// Copy from src to dst, applying emulation prevention. -int ff_vaapi_encode_h26x_nal_unit_to_byte_stream(uint8_t *dst, size_t *dst_len, - uint8_t *src, size_t src_len); - -#endif /* AVCODEC_VAAPI_ENCODE_H26X_H */ From e3e8eab359238486dc233f7aa89b7bb3cb19ec38 Mon Sep 17 00:00:00 2001 From: Mark Thompson Date: Sun, 23 Jul 2017 23:22:54 +0100 Subject: [PATCH 15/15] vaapi_h265: Add support for AUD NAL units Matching the H.264 encoder. --- libavcodec/vaapi_encode_h265.c | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/libavcodec/vaapi_encode_h265.c b/libavcodec/vaapi_encode_h265.c index f61dfaa1a9..477065e2ce 100644 --- a/libavcodec/vaapi_encode_h265.c +++ b/libavcodec/vaapi_encode_h265.c @@ -42,6 +42,7 @@ typedef struct VAAPIEncodeH265Context { int fixed_qp_p; int fixed_qp_b; + H265RawAUD aud; H265RawVPS vps; H265RawSPS sps; H265RawPPS pps; @@ -52,13 +53,16 @@ typedef struct VAAPIEncodeH265Context { int slice_nal_unit; int slice_type; + int pic_type; CodedBitstreamContext cbc; CodedBitstreamFragment current_access_unit; + int aud_needed; } VAAPIEncodeH265Context; typedef struct VAAPIEncodeH265Options { int qp; + int aud; } VAAPIEncodeH265Options; @@ -117,6 +121,13 @@ static int vaapi_encode_h265_write_sequence_header(AVCodecContext *avctx, CodedBitstreamFragment *au = &priv->current_access_unit; int err; + if (priv->aud_needed) { + err = vaapi_encode_h265_add_nal(avctx, au, &priv->aud); + if (err < 0) + goto fail; + priv->aud_needed = 0; + } + err = vaapi_encode_h265_add_nal(avctx, au, &priv->vps); if (err < 0) goto fail; @@ -145,6 +156,13 @@ static int vaapi_encode_h265_write_slice_header(AVCodecContext *avctx, CodedBitstreamFragment *au = &priv->current_access_unit; int err; + if (priv->aud_needed) { + err = vaapi_encode_h265_add_nal(avctx, au, &priv->aud); + if (err < 0) + goto fail; + priv->aud_needed = 0; + } + err = vaapi_encode_h265_add_nal(avctx, au, &priv->slice); if (err < 0) goto fail; @@ -519,6 +537,7 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx, { VAAPIEncodeContext *ctx = avctx->priv_data; VAAPIEncodeH265Context *priv = ctx->priv_data; + VAAPIEncodeH265Options *opt = ctx->codec_options; VAEncPictureParameterBufferHEVC *vpic = pic->codec_picture_params; int i; @@ -529,16 +548,19 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx, priv->slice_nal_unit = HEVC_NAL_IDR_W_RADL; priv->slice_type = HEVC_SLICE_I; + priv->pic_type = 0; } else { av_assert0(pic->encode_order > priv->last_idr_frame); if (pic->type == PICTURE_TYPE_I) { priv->slice_nal_unit = HEVC_NAL_CRA_NUT; priv->slice_type = HEVC_SLICE_I; + priv->pic_type = 0; } else if (pic->type == PICTURE_TYPE_P) { av_assert0(pic->refs[0]); priv->slice_nal_unit = HEVC_NAL_TRAIL_R; priv->slice_type = HEVC_SLICE_P; + priv->pic_type = 1; } else { av_assert0(pic->refs[0] && pic->refs[1]); if (pic->refs[1]->type == PICTURE_TYPE_I) @@ -546,10 +568,23 @@ static int vaapi_encode_h265_init_picture_params(AVCodecContext *avctx, else priv->slice_nal_unit = HEVC_NAL_TRAIL_N; priv->slice_type = HEVC_SLICE_B; + priv->pic_type = 2; } } priv->pic_order_cnt = pic->display_order - priv->last_idr_frame; + if (opt->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->aud.pic_type = priv->pic_type; + } else { + priv->aud_needed = 0; + } + vpic->decoded_curr_pic = (VAPictureHEVC) { .picture_id = pic->recon_surface, .pic_order_cnt = priv->pic_order_cnt, @@ -902,6 +937,10 @@ static av_cold int vaapi_encode_h265_close(AVCodecContext *avctx) static const AVOption vaapi_encode_h265_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 }, + + { "aud", "Include AUD", + OFFSET(aud), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS }, + { NULL }, };