[FFmpeg-devel] [PATCH 2/3] Provided support for MPEG-5 EVC (Essential Video Coding) codec
Anton Khirnov
anton at khirnov.net
Fri Jul 1 12:46:46 EEST 2022
Quoting Dawid Kozinski (2022-06-22 08:49:04)
> - Added xeve encoder wrapper
> - Added xevd dencoder wrapper
> - Added documentation for xeve and xevd wrappers
> - Added parser for EVC format
> - Changes in project configuration file and libavcodec Makefile
>
> Signed-off-by: Dawid Kozinski <d.kozinski at samsung.com>
> ---
> configure | 8 +
> doc/decoders.texi | 24 ++
> doc/encoders.texi | 112 ++++++
> doc/general_contents.texi | 19 +
> libavcodec/Makefile | 3 +
> libavcodec/allcodecs.c | 2 +
> libavcodec/avcodec.h | 3 +
> libavcodec/evc_parser.c | 527 ++++++++++++++++++++++++++
> libavcodec/libxevd.c | 440 ++++++++++++++++++++++
> libavcodec/libxeve.c | 753 ++++++++++++++++++++++++++++++++++++++
> libavcodec/parsers.c | 1 +
> 11 files changed, 1892 insertions(+)
> create mode 100644 libavcodec/evc_parser.c
> create mode 100644 libavcodec/libxevd.c
> create mode 100644 libavcodec/libxeve.c
Numerous violations of our coding style, such as
- keywords (if, for, etc.) should be followed by a space before parentheses
- opening brace for blocks inside functions should be on the same line
as their keyword (if, for, etc.)
- useless extra parentheses in if()s
I will not point out each instance, please find and fix those.
> diff --git a/doc/encoders.texi b/doc/encoders.texi
> index 1850c99fe9..245df680d2 100644
> --- a/doc/encoders.texi
> +++ b/doc/encoders.texi
> @@ -2892,6 +2892,118 @@ ffmpeg -i input -c:v libxavs2 -xavs2-params RdoqLevel=0 output.avs2
> @end example
> @end table
>
> + at section libxeve
> +
> +eXtra-fast Essential Video Encoder (XEVE) MPEG-5 EVC encoder wrapper.
> +
> +This encoder requires the presence of the libxeve headers and library
> +during configuration. You need to explicitly configure the build with
> + at option{--enable-libxeve}.
> +
> + at float NOTE
> +Many libxeve encoder options are mapped to FFmpeg global codec options,
> +while unique encoder options are provided through private options.
> +Additionally the xeve-params private options allows one to pass a list
> +of key=value tuples as accepted by the libxeve @code{parse_xeve_params} function.
> + at end float
> +
> +The xeve project website is at @url{https://github.com/mpeg5/xeve}.
> +
> + at subsection Options
> +
> +The following options are supported by the libxeve wrapper.
> +The xeve-equivalent options or values are listed in parentheses for easy migration.
> +
> + at float NOTE
> +To reduce the duplication of documentation, only the private options
> +and some others requiring special attention are documented here. For
> +the documentation of the undocumented generic options, see
> + at ref{codec-options,,the Codec Options chapter}.
> + at end float
> +
> + at float NOTE
> +To get a more accurate and extensive documentation of the libxeve options,
> +invoke the command @code{xeve_app --help} or consult the libxeve documentation.
> + at end float
> +
> + at table @option
> + at item b (@emph{bitrate})
> +Set target video bitrate in bits/s.
> +Note that FFmpeg’s b option is expressed in bits/s, while xeve’s bitrate is in kilobits/s.
Looks broken.
> diff --git a/libavcodec/libxeve.c b/libavcodec/libxeve.c
> new file mode 100644
> index 0000000000..e115ce63d2
> --- /dev/null
> +++ b/libavcodec/libxeve.c
> +/**
> + * The structure stores all the states associated with the instance of Xeve MPEG-5 EVC encoder
> + */
> +typedef struct XeveContext {
> + const AVClass *class;
> +
> + XEVE id; // XEVE instance identifier
> + XEVE_CDSC cdsc; // coding parameters i.e profile, width & height of input frame, num of therads, frame rate ...
> + XEVE_BITB bitb; // bitstream buffer (output)
> + XEVE_STAT stat; // encoding status (output)
> + XEVE_IMGB imgb; // image buffer (input)
> +
> + State state; // encoder state (skipping, encoding, bumping)
> +
> + // Chroma subsampling
> + int width_luma;
> + int height_luma;
> + int width_chroma;
> + int height_chroma;
All these are only used in init, no need to store them in the context.
> +/**
> + * Gets Xeve pre-defined preset
> + *
> + * @param preset string describing Xeve encoder preset (fast, medium, slow, placebo)
> + * @return XEVE pre-defined profile on success, negative value on failure
> + */
> +static int get_preset_id(const char *preset)
> +{
> + if((!strcmp(preset, "fast")))
> + return XEVE_PRESET_FAST;
> + else if (!strcmp(preset, "medium"))
> + return XEVE_PRESET_MEDIUM;
> + else if (!strcmp(preset, "slow"))
> + return XEVE_PRESET_SLOW;
> + else if (!strcmp(preset, "placebo"))
> + return XEVE_PRESET_PLACEBO;
> + else
> + return AVERROR(EINVAL);
> +}
This parsing is unnecessary, change the relevant option into an int and
add named constants for it (see e.g. "coder" in libx264.c and many
others).
Same for tune.
> +/**
> + * Convert FFmpeg pixel format (AVPixelFormat) into XEVE pre-defined color space
> + *
> + * @param[in] px_fmt pixel format (@see https://ffmpeg.org/doxygen/trunk/pixfmt_8h.html#a9a8e335cf3be472042bc9f0cf80cd4c5)
> + *
> + * @return XEVE pre-defined color space (@see xeve.h) on success, XEVE_CF_UNKNOWN on failure
> + */
> +static int xeve_color_space(enum AVPixelFormat av_pix_fmt)
IIUC the "xeve_" namespace is used by the library, so we should not
declare any such names ourselves. Use "libxeve_" or something.
> +/**
> + * Parse : separated list of key=value parameters
> + *
> + * @param[in] avctx context for logger
> + * @param[in] key
> + * @param[in] value
> + * @param[out] cdsc contains all Xeve MPEG-5 EVC encoder encoder parameters that
> + * should be initialized before the encoder is use
> + *
> + * @return 0 on success, negative value on failure
> + */
> +static int parse_xeve_params(AVCodecContext *avctx, const char *key, const char *value, XEVE_CDSC* cdsc)
What is the point of parsing options here instead of declaring them as
AVOptions? Especially when vbv-bufsize can already be set using the
global "bufsize" option.
> + xeve_color_fmt(avctx->pix_fmt, &xectx->color_format);
> +
> +#if AV_HAVE_BIGENDIAN
> + cdsc->param.cs = XEVE_CS_SET(xectx->color_format, cdsc->param.codec_bit_depth, 1);
> +#else
> + cdsc->param.cs = XEVE_CS_SET(xectx->color_format, cdsc->param.codec_bit_depth, 0);
> +#endif
just pass AV_HAVE_BIGENDIAN as the last macro parameter, no need for
#ifs
> +static int set_extra_config(AVCodecContext* avctx, XEVE id, XeveContext *ctx)
> +{
> + int ret, size, value;
> + size = 4;
> +
> + // embed SEI messages identifying encoder parameters and command line arguments
> + // - 0: off\n"
> + // - 1: emit sei info"
> + //
> + // SEI - Supplemental enhancement information contains information
> + // that is not necessary to decode the samples of coded pictures from VCL NAL units.
> + // Some SEI message information is required to check bitstream conformance
> + // and for output timing decoder conformance.
> + // @see ISO_IEC_23094-1_2020 7.4.3.5
> + // @see ISO_IEC_23094-1_2020 Annex D
> + ret = xeve_config(id, XEVE_CFG_SET_SEI_CMD, &value, &size); // sei_cmd_info
Is this not reading value, which is uninitialized?
> +/**
> + * Encode raw data frame into EVC packet
> + *
> + * @param[in] avctx codec context
> + * @param[out] pkt output AVPacket containing encoded data
> + * @param[in] frame AVFrame containing the raw data to be encoded
> + * @param[out] got_packet encoder sets to 0 or 1 to indicate that a
> + * non-empty packet was returned in pkt
> + *
> + * @return 0 on success, negative error code on failure
> + */
> +static int libxeve_encode(AVCodecContext *avctx, AVPacket *avpkt,
> + const AVFrame *frame, int *got_packet)
> +{
> + XeveContext *xectx = avctx->priv_data;
> + int ret = -1;
> + int xeve_cs;
> +
> + if(xectx->state == STATE_SKIPPING && frame ) {
> + xectx->state = STATE_ENCODING; // Entering encoding process
> + } else if(xectx->state == STATE_ENCODING && frame == NULL) {
This state switching looks wrong.
frame will be NULL only at the end of encoding. It should never happen
that libxeve_encode() is called with frame==NULL and then later with
frame!=NULL.
Also, the logic might be simpler if you used the receive_packet callback
rather than encode.
> + if (setup_bumping(xectx->id) == 0)
> + xectx->state = STATE_BUMPING; // Entering bumping process
> + else {
> + av_log(avctx, AV_LOG_ERROR, "Failed to setup bumping\n");
> + xectx->state = STATE_SKIPPING;
> + }
> + }
> +
> + if(xectx->state == STATE_ENCODING) {
> + const AVPixFmtDescriptor *pixel_fmt_desc = av_pix_fmt_desc_get (frame->format);
> + if(!pixel_fmt_desc) {
> + av_log(avctx, AV_LOG_ERROR, "Invalid pixel format descriptor for pixel format: %s\n", av_get_pix_fmt_name(avctx->pix_fmt));
> + return AVERROR_INVALIDDATA;
> + }
This seems unused.
> +
> + xeve_cs = xeve_color_space(avctx->pix_fmt);
> + if(xeve_cs != XEVE_CS_YCBCR420 && xeve_cs != XEVE_CS_YCBCR420_10LE) {
> + av_log(avctx, AV_LOG_ERROR, "Invalid pixel format: %s\n", av_get_pix_fmt_name(avctx->pix_fmt));
> + return AVERROR_INVALIDDATA;
> + }
This should be done once during encoder init.
Also declaring a list of supported pixel formats in the FFCodec struct
guarantees that you will only get one of those formats, so you don't
need to check for it yourself.
> +
> + {
> + int i;
> + XEVE_IMGB *imgb = NULL;
> +
> + imgb = &xectx->imgb;
> +
> + for (i = 0; i < imgb->np; i++) {
> + imgb->a[i] = frame->data[i];
> + imgb->s[i] = frame->linesize[i];
> + }
How does the ownership semantics work here? Does libxeve make a copy?
> + if(xectx->id == NULL) {
> + av_log(avctx, AV_LOG_ERROR, "Invalid XEVE encoder\n");
> + return AVERROR_INVALIDDATA;
> + }
Can this happen?
> +
> + imgb->ts[0] = frame->pts;
> + imgb->ts[1] = 0;
Will a proper dts value be generated by the library?
--
Anton Khirnov
More information about the ffmpeg-devel
mailing list