[FFmpeg-devel] [PATCH v3 2/3] lavc, doc, configure: add libdavs2 video decoder
Mark Thompson
sw at jkqxz.net
Thu May 31 01:32:24 EEST 2018
On 30/05/18 02:19, hwren wrote:
> Add avs2 video decoder via libdavs2 library.
>
> Signed-off-by: hwren <hwrenx at 126.com>
> ---
> Changelog | 1 +
> configure | 4 +
> doc/decoders.texi | 10 +++
> doc/general.texi | 8 ++
> libavcodec/Makefile | 1 +
> libavcodec/allcodecs.c | 1 +
> libavcodec/libdavs2.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++++
> 7 files changed, 229 insertions(+)
> create mode 100644 libavcodec/libdavs2.c
>
> ...
> diff --git a/libavcodec/libdavs2.c b/libavcodec/libdavs2.c
> new file mode 100644
> index 0000000..b4a5f72
> --- /dev/null
> +++ b/libavcodec/libdavs2.c
> @@ -0,0 +1,204 @@
> +/*
> + * AVS2 decoding using the davs2 library
> + *
> + * Copyright (C) 2018 Yiqun Xu, <yiqun.xu at vipl.ict.ac.cn>
> + * Falei Luo, <falei.luo at gmail.com>
> + * Huiwen Ren, <hwrenx at gmail.com>
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +#include "libavutil/avassert.h"
> +#include "libavutil/common.h"
> +#include "libavutil/avutil.h"
> +#include "avcodec.h"
> +#include "libavutil/imgutils.h"
> +#include "internal.h"
> +
> +#include <davs2.h>
> +
> +typedef struct DAVS2Context {
> + void *decoder;
> +
> + AVFrame *frame;
> + davs2_param_t param; // decoding parameters
> + davs2_packet_t packet; // input bitstream
> +
> + int decoded_frames;
> +
> + davs2_picture_t out_frame; // output data, frame data
> + davs2_seq_info_t headerset; // output data, sequence header
> +
> +}DAVS2Context;
> +
> +static av_cold davs2_init(AVCodecContext *avctx)
Missing return type.
> +{
> + DAVS2Context *cad = avctx->priv_data;
> +
> + /* init the decoder */
> + cad->param.threads = avctx->thread_count;
> + cad->param.i_info_level = 0;
> + cad->decoder = davs2_decoder_open(&cad->param);
> + avctx->flags |= AV_CODEC_FLAG_TRUNCATED;
>From avcodec.h:
/**
* AV_CODEC_FLAG_*.
* - encoding: Set by user.
* - decoding: Set by user.
*/
int flags;
The decoder should not be setting this field.
Having run this and seen it reading in 1024-byte chunks from a flat, I think what you actually need here to support flat files is a demuxer/parser which is aware of the structure of the codec and can split the input file into sensible packets. (Any input other than a flat file will already do this.)
> +
> + av_log(avctx, AV_LOG_VERBOSE, "decoder created. %p\n", cad->decoder);
> + return 0;
> +}
> +
> +static int davs_dump_frames(AVCodecContext *avctx, davs2_picture_t *pic, davs2_seq_info_t *headerset, AVFrame *frame)
> +{
> + DAVS2Context *cad = avctx->priv_data;
> + avctx->flags |= AV_CODEC_FLAG_TRUNCATED;
Remove this.
> + int bytes_per_sample = pic->bytes_per_sample;
> + int i;
> +
> + if (!headerset)
> + return 0;
> +
> + if (!pic || pic->ret_type == DAVS2_GOT_HEADER) {
> + avctx->width = headerset->horizontal_size;
> + avctx->height = headerset->vertical_size;
> + avctx->pix_fmt = headerset->output_bitdepth == 10 ? AV_PIX_FMT_YUV420P10 : AV_PIX_FMT_YUV420P;
It looks like the output bitdepth is actually set at build time of the library, right? Does that do downsampling/upsampling if the input has the other bitdepth, or will it fail in that case?
> +
> + AVRational r = av_d2q(headerset->frame_rate,4096);
> + avctx->framerate.num = r.num;
> + avctx->framerate.den = r.den;
AVCodecContext.framerate is also an AVRational, so you don't need this indirection (also fixes the mixed declarations and code).
Alternatively: the API appears to give you frame_rate_code as well, so indexing that into ff_mpeg12_frame_rate_tab[] will give you the right value without any rounding.
> + return 0;
> + }
> +
> + for (i = 0; i < 3; ++i) {
> + int size_plane = pic->width[i] * pic->lines[i] * bytes_per_sample;
> + frame->buf[i] = av_buffer_alloc(size_plane);
> + frame->data[i] = frame->buf[i]->data;
Crashes if the allocation failed - you need to immediately check after the allocation.
> + frame->linesize[i] = pic->width[i] * bytes_per_sample;
> + if (!frame->buf[i] || !frame->data[i] || !frame->linesize[i]){
> + av_log(avctx, AV_LOG_ERROR, "dump error: alloc failed.\n");
> + return AVERROR(EINVAL);
> + }
> + memcpy(frame->data[i], pic->planes[i], size_plane);
> + }
> +
> + frame->width = cad->headerset.horizontal_size;
> + frame->height = cad->headerset.vertical_size;
> + frame->pts = cad->out_frame.pts;
The timestamps on the output frames don't seem to make any sense?
E.g. from a raw 30000/1001fps stream made with xavs2 I got the output:
frame pts
1 0
2 48000
3 48000
4 48000
5 48000
6 48000
7 48000
8 48000
9 48000
10 AV_NOPTS_VALUE
11 288000
12 AV_NOPTS_VALUE
13 AV_NOPTS_VALUE
14 AV_NOPTS_VALUE
15 AV_NOPTS_VALUE
16 AV_NOPTS_VALUE
17 240000
18 AV_NOPTS_VALUE
19 672000
20 AV_NOPTS_VALUE
21 624000
22 AV_NOPTS_VALUE
23 720000
24 768000
25 576000
> + frame->pict_type = pic->type;
> + frame->format = avctx->pix_fmt;
> +
> + cad->decoded_frames++;
> + return 1;
> +}
> +
> +static av_cold davs2_end(AVCodecContext *avctx)
Missing return type.
> +{
> + DAVS2Context *cad = avctx->priv_data;
> +
> + /* close the decoder */
> + if (cad->decoder) {
> + davs2_decoder_close(cad->decoder);
> + av_log(avctx, AV_LOG_VERBOSE, "decoder destroyed. %p; frames %d\n", cad->decoder, cad->decoded_frames);
> + cad->decoder = NULL;
> + }
> +
> + return 0;
> +}
> +
> +static int davs2_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, AVPacket *avpkt)
> +{
> + DAVS2Context *cad = avctx->priv_data;
> + int buf_size = avpkt->size;
> + uint8_t *buf_ptr = avpkt->data;
> + AVFrame *frame = data;
> + int ret = 0;
> +
> + *got_frame = 0;
> + cad->frame = frame;
This field is assigned to but never read?
> + avctx->flags |= AV_CODEC_FLAG_TRUNCATED;
Remove this.
> +
> + if (!buf_size) {
> + cad->packet.data = buf_ptr;
> + cad->packet.len = buf_size;
> + cad->packet.pts = avpkt->pts;
> + cad->packet.dts = avpkt->dts;
> +
> + while (1) {
> + ret = davs2_decoder_flush(cad->decoder, &cad->headerset, &cad->out_frame);
> +
> + if (ret < 0)
> + return 0;
I think this looks like it's checking for an error, but actually it's checking for DAVS2_END. It might be clearer if you used the davs2_ret_e type and compared the symbolic name.
> +
> + if (cad->out_frame.ret_type != DAVS2_DEFAULT) {
> + *got_frame = davs_dump_frames(avctx, &cad->out_frame, &cad->headerset, frame);
> + davs2_decoder_frame_unref(cad->decoder, &cad->out_frame);
> + }
> + if (*got_frame)
> + break;
> + }
> + return 0;
> + } else {
> + while (buf_size > 0) {
> + int len = buf_size; // for API-3, pass all data in
> +
> + cad->packet.marker = 0;
> + cad->packet.data = buf_ptr;
> + cad->packet.len = len;
> + cad->packet.pts = avpkt->pts;
> + cad->packet.dts = avpkt->dts;
> +
> + len = davs2_decoder_decode(cad->decoder, &cad->packet, &cad->headerset, &cad->out_frame);
> +
> + if (cad->out_frame.ret_type != DAVS2_DEFAULT) {
> + *got_frame = davs_dump_frames(avctx, &cad->out_frame, &cad->headerset, frame);
> + davs2_decoder_frame_unref(cad->decoder, &cad->out_frame);
> + }
> +
> + if (len < 0) {
> + av_log(avctx, AV_LOG_ERROR, "A decoder error counted\n");
I'm not sure what you mean by "counted" here.
> + if (cad->decoder) {
> + davs2_decoder_close(cad->decoder);
> + av_log(avctx, AV_LOG_VERBOSE, "decoder destroyed. %p; frames %d\n", cad->decoder, cad->decoded_frames);
> + cad->decoder = NULL;
It is guaranteed that the close function will be called to clean up, so this doesn't need to be done here.
> + }
> + return AVERROR(EINVAL);
> + }
> +
> + buf_ptr += len;
> + buf_size -= len;
> +
> + if (*got_frame)
> + break;
> + }
> + }
> +
> + buf_size = (buf_ptr - avpkt->data);
> +
> + return buf_size;
> +}
> +
> +AVCodec ff_libdavs2_decoder = {
> + .name = "libdavs2",
> + .long_name = NULL_IF_CONFIG_SMALL("Decoder for AVS2/IEEE 1857.4"),
> + .type = AVMEDIA_TYPE_VIDEO,
> + .id = AV_CODEC_ID_AVS2,
> + .priv_data_size = sizeof(DAVS2Context),
> + .init = davs2_init,
> + .close = davs2_end,
> + .decode = davs2_decode_frame,
> + .capabilities = AV_CODEC_CAP_DELAY,//AV_CODEC_CAP_DR1 |
> + .pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV420P10,
> + AV_PIX_FMT_NONE },
> + .wrapper_name = "libdavs2",
> +};
>
- Mark
More information about the ffmpeg-devel
mailing list