From: James Almer <jamrial@gmail.com> To: ffmpeg-devel@ffmpeg.org Subject: Re: [FFmpeg-devel] [FFmpeg-cvslog] avcodec: add QOA decoder Date: Sun, 26 Nov 2023 15:02:24 -0300 Message-ID: <86682e9c-f605-4873-bdbe-234483af7b75@gmail.com> (raw) In-Reply-To: <20231126164217.65B274100B2@natalya.videolan.org> > ffmpeg | branch: master | Paul B Mahol <onemda at gmail.com <https://ffmpeg.org/mailman/listinfo/ffmpeg-cvslog>> | Sat Sep 23 16:49:25 2023 +0200| [3609d2b78340c06920973dc46b85b517c789782f] | committer: Paul B Mahol > > avcodec: add QOA decoder > > >/http://git.videolan.org/gitweb.cgi/ffmpeg.git/?a=commit;h=3609d2b78340c06920973dc46b85b517c789782f > /--- > > Changelog | 1 + > libavcodec/Makefile | 1 + > libavcodec/allcodecs.c | 1 + > libavcodec/codec_desc.c | 7 ++ > libavcodec/codec_id.h | 1 + > libavcodec/qoadec.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++ > libavcodec/version.h | 2 +- > 7 files changed, 187 insertions(+), 1 deletion(-) > > diff --git a/Changelog b/Changelog > index 7d79be1c3e..d945904bc0 100644 > --- a/Changelog > +++ b/Changelog > @@ -5,6 +5,7 @@ version <next>: > - LEAD MCMP decoder > - EVC decoding using external library libxevd > - EVC encoding using external library libxeve > +- QOA decoder > > version 6.1: > - libaribcaption decoder > diff --git a/libavcodec/Makefile b/libavcodec/Makefile > index a5941d1284..748806e702 100644 > --- a/libavcodec/Makefile > +++ b/libavcodec/Makefile > @@ -623,6 +623,7 @@ OBJS-$(CONFIG_QCELP_DECODER) += qcelpdec.o \ > OBJS-$(CONFIG_QDM2_DECODER) += qdm2.o > OBJS-$(CONFIG_QDMC_DECODER) += qdmc.o > OBJS-$(CONFIG_QDRAW_DECODER) += qdrw.o > +OBJS-$(CONFIG_QOA_DECODER) += qoadec.o > OBJS-$(CONFIG_QOI_DECODER) += qoidec.o > OBJS-$(CONFIG_QOI_ENCODER) += qoienc.o > OBJS-$(CONFIG_QPEG_DECODER) += qpeg.o > diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c > index 87595683f9..b0f004e15c 100644 > --- a/libavcodec/allcodecs.c > +++ b/libavcodec/allcodecs.c > @@ -522,6 +522,7 @@ extern const FFCodec ff_paf_audio_decoder; > extern const FFCodec ff_qcelp_decoder; > extern const FFCodec ff_qdm2_decoder; > extern const FFCodec ff_qdmc_decoder; > +extern const FFCodec ff_qoa_decoder; > extern const FFCodec ff_ra_144_encoder; > extern const FFCodec ff_ra_144_decoder; > extern const FFCodec ff_ra_288_decoder; > diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c > index 432a9c9ea6..033344304c 100644 > --- a/libavcodec/codec_desc.c > +++ b/libavcodec/codec_desc.c > @@ -3427,6 +3427,13 @@ static const AVCodecDescriptor codec_descriptors[] = { > .long_name = NULL_IF_CONFIG_SMALL("OSQ (Original Sound Quality)"), > .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, > }, > + { > + .id = AV_CODEC_ID_QOA, > + .type = AVMEDIA_TYPE_AUDIO, > + .name = "qoa", > + .long_name = NULL_IF_CONFIG_SMALL("QOA (Quite OK Audio)"), > + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, > + }, > > /* subtitle codecs */ > { > diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h > index 84ce8e6aa9..d96e49430e 100644 > --- a/libavcodec/codec_id.h > +++ b/libavcodec/codec_id.h > @@ -545,6 +545,7 @@ enum AVCodecID { > AV_CODEC_ID_RKA, > AV_CODEC_ID_AC4, > AV_CODEC_ID_OSQ, > + AV_CODEC_ID_QOA, > > /* subtitle codecs */ > AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs. > diff --git a/libavcodec/qoadec.c b/libavcodec/qoadec.c > new file mode 100644 > index 0000000000..9b2abae833 > --- /dev/null > +++ b/libavcodec/qoadec.c > @@ -0,0 +1,175 @@ > +/* > + * QOA decoder > + * > + * This file is part of FFmpeg. > + * > + * FFmpeg is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * FFmpeg is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + * You should have received a copy of the GNU Lesser General Public > + * License along with FFmpeg; if not, write to the Free Software > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA > + */ > + > +#include "avcodec.h" > +#include "codec_internal.h" > +#include "decode.h" > +#include "get_bits.h" > +#include "bytestream.h" > +#include "mathops.h" > + > +#define QOA_SLICE_LEN 20 > +#define QOA_LMS_LEN 4 > + > +typedef struct QOAChannel { > + int history[QOA_LMS_LEN]; > + int weights[QOA_LMS_LEN]; > +} QOAChannel; > + > +typedef struct QOAContext { > + QOAChannel *ch; > +} QOAContext; > + > +static const int16_t qoa_dequant_tab[16][8] = { > + { 1, -1, 3, -3, 5, -5, 7, -7}, > + { 5, -5, 18, -18, 32, -32, 49, -49}, > + { 16, -16, 53, -53, 95, -95, 147, -147}, > + { 34, -34, 113, -113, 203, -203, 315, -315}, > + { 63, -63, 210, -210, 378, -378, 588, -588}, > + { 104, -104, 345, -345, 621, -621, 966, -966}, > + { 158, -158, 528, -528, 950, -950, 1477, -1477}, > + { 228, -228, 760, -760, 1368, -1368, 2128, -2128}, > + { 316, -316, 1053, -1053, 1895, -1895, 2947, -2947}, > + { 422, -422, 1405, -1405, 2529, -2529, 3934, -3934}, > + { 548, -548, 1828, -1828, 3290, -3290, 5117, -5117}, > + { 696, -696, 2320, -2320, 4176, -4176, 6496, -6496}, > + { 868, -868, 2893, -2893, 5207, -5207, 8099, -8099}, > + {1064, -1064, 3548, -3548, 6386, -6386, 9933, -9933}, > + {1286, -1286, 4288, -4288, 7718, -7718, 12005, -12005}, > + {1536, -1536, 5120, -5120, 9216, -9216, 14336, -14336}, > +}; > + > +static av_cold int qoa_decode_init(AVCodecContext *avctx) > +{ > + QOAContext *s = avctx->priv_data; > + > + avctx->sample_fmt = AV_SAMPLE_FMT_S16; > + > + s->ch = av_calloc(avctx->ch_layout.nb_channels, sizeof(*s->ch)); > + if (!s->ch) > + return AVERROR(ENOMEM); > + > + return 0; > +} > + > +static int qoa_lms_predict(QOAChannel *lms) > +{ > + int prediction = 0; > + for (int i = 0; i < QOA_LMS_LEN; i++) > + prediction += lms->weights[i] * lms->history[i]; > + return prediction >> 13; > +} > + > +static void qoa_lms_update(QOAChannel *lms, int sample, int residual) > +{ > + int delta = residual >> 4; > + for (int i = 0; i < QOA_LMS_LEN; i++) > + lms->weights[i] += lms->history[i] < 0 ? -delta : delta; > + for (int i = 0; i < QOA_LMS_LEN-1; i++) > + lms->history[i] = lms->history[i+1]; > + lms->history[QOA_LMS_LEN-1] = sample; > +} > + > +static int qoa_decode_frame(AVCodecContext *avctx, AVFrame *frame, > + int *got_frame_ptr, AVPacket *avpkt) > +{ > + QOAContext *s = avctx->priv_data; > + int ret, frame_size, nb_channels; > + GetByteContext gb; > + int16_t *samples; > + > + bytestream2_init(&gb, avpkt->data, avpkt->size); > + > + nb_channels = bytestream2_get_byte(&gb); > + if (avctx->ch_layout.nb_channels != nb_channels) > + return AVERROR_INVALIDDATA; > + > + avctx->sample_rate = bytestream2_get_be24(&gb); > + frame->nb_samples = bytestream2_get_be16(&gb); > + frame_size = bytestream2_get_be16(&gb); > + if (frame_size > avpkt->size) > + return AVERROR_INVALIDDATA; > + > + if (frame_size < 8 + QOA_LMS_LEN * 4 * nb_channels + > + 8LL * frame->nb_samples * nb_channels / QOA_SLICE_LEN) > + return AVERROR_INVALIDDATA; > + > + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) > + return ret; > + samples = (int16_t *)frame->data[0]; > + > + for (int ch = 0; ch < nb_channels; ch++) { > + QOAChannel *qch = &s->ch[ch]; > + > + for (int n = 0; n < QOA_LMS_LEN; n++) > + qch->history[n] = sign_extend(bytestream2_get_be16u(&gb), 16); > + for (int n = 0; n < QOA_LMS_LEN; n++) > + qch->weights[n] = sign_extend(bytestream2_get_be16u(&gb), 16); > + } > + > + for (int sample_index = 0; sample_index < frame->nb_samples * nb_channels; > + sample_index += QOA_SLICE_LEN) { > + for (int ch = 0; ch < nb_channels; ch++) { > + QOAChannel *lms = &s->ch[ch]; > + uint64_t slice = bytestream2_get_be64u(&gb); > + int scalefactor = (slice >> 60) & 0xf; > + int slice_start = sample_index * nb_channels + ch; > + int slice_end = av_clip(sample_index + QOA_SLICE_LEN, 0, frame->nb_samples) * nb_channels + ch; > + > + for (int si = slice_start; si < slice_end; si += nb_channels) { > + int predicted = qoa_lms_predict(lms); > + int quantized = (slice >> 57) & 0x7; > + int dequantized = qoa_dequant_tab[scalefactor][quantized]; > + int reconstructed = av_clip_int16(predicted + dequantized); > + > + samples[si] = reconstructed; > + slice <<= 3; > + > + qoa_lms_update(lms, reconstructed, dequantized); > + } > + } > + } > + > + *got_frame_ptr = 1; > + > + return avpkt->size; > +} > + > +static av_cold int qoa_decode_end(AVCodecContext *avctx) > +{ > + QOAContext *s = avctx->priv_data; > + av_freep(&s->ch); > + return 0; > +} > + > +const FFCodec ff_qoa_decoder = { > + .p.name = "qoa", > + CODEC_LONG_NAME("QOA (Quite OK Audio)"), > + .p.type = AVMEDIA_TYPE_AUDIO, > + .p.id = AV_CODEC_ID_QOA, > + .priv_data_size = sizeof(QOAContext), > + .init = qoa_decode_init, > + FF_CODEC_DECODE_CB(qoa_decode_frame), > + .close = qoa_decode_end, > + .p.capabilities = AV_CODEC_CAP_CHANNEL_CONF | The decoder is not setting channel info, it's using the one set by the caller and ensuring it doesn't change between frames. So this cap is not correct. > + AV_CODEC_CAP_DR1, > + .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_S16, > + AV_SAMPLE_FMT_NONE }, > +}; > diff --git a/libavcodec/version.h b/libavcodec/version.h > index 0ef6c991f3..1008fead27 100644 > --- a/libavcodec/version.h > +++ b/libavcodec/version.h > @@ -29,7 +29,7 @@ > > #include "version_major.h" > > -#define LIBAVCODEC_VERSION_MINOR 34 > +#define LIBAVCODEC_VERSION_MINOR 35 > #define LIBAVCODEC_VERSION_MICRO 100 > > #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ > _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
next parent reply other threads:[~2023-11-26 18:02 UTC|newest] Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top [not found] <20231126164217.65B274100B2@natalya.videolan.org> 2023-11-26 18:02 ` James Almer [this message] 2023-12-02 13:50 ` Anton Khirnov 2023-12-02 14:43 ` Paul B Mahol 2023-12-02 14:44 ` Anton Khirnov 2023-12-02 14:55 ` Paul B Mahol 2023-12-02 14:50 ` Anton Khirnov 2023-12-02 15:00 ` Paul B Mahol
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=86682e9c-f605-4873-bdbe-234483af7b75@gmail.com \ --to=jamrial@gmail.com \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git