diff mbox series

[FFmpeg-devel] libavcodec, libavformat: Added DFPWM1a codec and raw format

Message ID 43daccc7-c618-993e-912d-54e633e8c7f2@gmail.com
State New
Headers show
Series [FFmpeg-devel] libavcodec, libavformat: Added DFPWM1a codec and raw format | expand

Checks

Context Check Description
andriy/configure_aarch64_jetson warning Failed to apply patch
yinshiyou/configure_loongarch64 warning Failed to apply patch
andriy/configure_armv7_RPi4 warning Failed to apply patch

Commit Message

Jack Bruienne Feb. 25, 2022, 7:54 a.m. UTC
From the wiki page (https://wiki.vexatos.com/dfpwm):
> DFPWM (Dynamic Filter Pulse Width Modulation) is an audio codec
> created by Ben “GreaseMonkey” Russell in 2012, originally to be used
> as a voice codec for asiekierka's pixmess, a C remake of 64pixels.
> It is a 1-bit-per-sample codec which uses a dynamic-strength one-pole
> low-pass filter as a predictor. Due to the fact that a raw DPFWM decoding
> creates a high-pitched whine, it is often followed by some post-processing
> filters to make the stream more listenable.

It has recently gained popularity through the ComputerCraft mod for
Minecraft, which added support for audio through this codec, as well as
the Computronics expansion which preceeded the official support. These
both implement the slightly adjusted 1a version of the codec, which is
the version I have chosen for this patch.

This patch adds both a new codec (with encoding and decoding), as well as
a raw audio format to be able to read/write the raw files that are most
commonly used (as no other container format supports it yet).

The codec sources are pretty simple: they use the reference codec with
a basic wrapper to connect it to the FFmpeg AVCodec system. There's a
bit of extra code to convert from unsigned to signed 8-bit audio, as the
codec implementation operates on signed data, which FFmpeg doesn't support.

The muxers are mostly copied from the PCM demuxer and the raw muxers, as
DFPWM is typically stored as raw data.

This patch will be highly useful to ComputerCraft developers who are
working with audio, as it is the standard format for audio, and there
are few user-friendly encoders out there. It will streamline the process
for importing audio, replacing the need to write code or use tools that
require very specific input formats.

You may use the CraftOS-PC program (https://www.craftos-pc.cc) to test
out DFPWM playback. To use it, run the program and type this command:
"attach left speaker" Then run "speaker play <file.dfpwm>" for each file.
The app runs in a sandbox, so files have to be transferred in first;
the easiest way to do this is to simply drag the file on the window.
(Or copy files to the folder at https://www.craftos-pc.cc/docs/saves.)

Sample DFPWM files can be generated with an online tool at
https://music.madefor.cc. This is the current best way to encode DFPWM
files. Simply drag an audio file onto the page, and it will encode it,
giving a download link on the page.

I've made sure to update all of the docs as per Developer§7, and I've
tested it as per section 8. Test files encoded to DFPWM play correctly
in ComputerCraft, and other files that work in CC are correctly decoded.
I have also verified that corrupt files do not crash the decoder - this
should theoretically not be an issue as the result size is constant with
respect to the input size.

One thing I noticed is that this sample file fails to decode to raw:
https://samples.ffmpeg.org/ogg/virginradio-three-consecutive-chains.ogg
It reports "Application provided invalid, non monotonically increasing
dts to muxer in stream 0", which appears to be because the initial
timestamp is not 0:00. This affects all raw muxers, including PCM.

Signed-off-by: Jack Bruienne <jackbruienne@gmail.com>
---
  Changelog                 |   1 +
  MAINTAINERS               |   2 +
  doc/general_contents.texi |   2 +
  libavcodec/Makefile       |   2 +
  libavcodec/allcodecs.c    |   2 +
  libavcodec/codec_desc.c   |   7 ++
  libavcodec/codec_id.h     |   1 +
  libavcodec/dfpwmdec.c     | 138 +++++++++++++++++++++++++++++++++++++
  libavcodec/dfpwmenc.c     | 140 ++++++++++++++++++++++++++++++++++++++
  libavcodec/utils.c        |   2 +
  libavcodec/version.h      |   2 +-
  libavformat/Makefile      |   2 +
  libavformat/allformats.c  |   2 +
  libavformat/dfpwmdec.c    | 107 +++++++++++++++++++++++++++++
  libavformat/rawenc.c      |  13 ++++
  libavformat/version.h     |   4 +-
  16 files changed, 424 insertions(+), 3 deletions(-)
  create mode 100644 libavcodec/dfpwmdec.c
  create mode 100644 libavcodec/dfpwmenc.c
  create mode 100644 libavformat/dfpwmdec.c

AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \
 
LIBAVFORMAT_VERSION_MINOR, \

Comments

Paul B Mahol Feb. 25, 2022, 8:15 a.m. UTC | #1
On Fri, Feb 25, 2022 at 02:54:35AM -0500, Jack Bruienne wrote:
> From the wiki page (https://wiki.vexatos.com/dfpwm):
> > DFPWM (Dynamic Filter Pulse Width Modulation) is an audio codec
> > created by Ben “GreaseMonkey” Russell in 2012, originally to be used
> > as a voice codec for asiekierka's pixmess, a C remake of 64pixels.
> > It is a 1-bit-per-sample codec which uses a dynamic-strength one-pole
> > low-pass filter as a predictor. Due to the fact that a raw DPFWM decoding
> > creates a high-pitched whine, it is often followed by some post-processing
> > filters to make the stream more listenable.
> 
> It has recently gained popularity through the ComputerCraft mod for
> Minecraft, which added support for audio through this codec, as well as
> the Computronics expansion which preceeded the official support. These
> both implement the slightly adjusted 1a version of the codec, which is
> the version I have chosen for this patch.
> 
> This patch adds both a new codec (with encoding and decoding), as well as
> a raw audio format to be able to read/write the raw files that are most
> commonly used (as no other container format supports it yet).
> 
> The codec sources are pretty simple: they use the reference codec with
> a basic wrapper to connect it to the FFmpeg AVCodec system. There's a
> bit of extra code to convert from unsigned to signed 8-bit audio, as the
> codec implementation operates on signed data, which FFmpeg doesn't support.
> 
> The muxers are mostly copied from the PCM demuxer and the raw muxers, as
> DFPWM is typically stored as raw data.
> 
> This patch will be highly useful to ComputerCraft developers who are
> working with audio, as it is the standard format for audio, and there
> are few user-friendly encoders out there. It will streamline the process
> for importing audio, replacing the need to write code or use tools that
> require very specific input formats.
> 
> You may use the CraftOS-PC program (https://www.craftos-pc.cc) to test
> out DFPWM playback. To use it, run the program and type this command:
> "attach left speaker" Then run "speaker play <file.dfpwm>" for each file.
> The app runs in a sandbox, so files have to be transferred in first;
> the easiest way to do this is to simply drag the file on the window.
> (Or copy files to the folder at https://www.craftos-pc.cc/docs/saves.)
> 
> Sample DFPWM files can be generated with an online tool at
> https://music.madefor.cc. This is the current best way to encode DFPWM
> files. Simply drag an audio file onto the page, and it will encode it,
> giving a download link on the page.
> 
> I've made sure to update all of the docs as per Developer§7, and I've
> tested it as per section 8. Test files encoded to DFPWM play correctly
> in ComputerCraft, and other files that work in CC are correctly decoded.
> I have also verified that corrupt files do not crash the decoder - this
> should theoretically not be an issue as the result size is constant with
> respect to the input size.
> 
> One thing I noticed is that this sample file fails to decode to raw:
> https://samples.ffmpeg.org/ogg/virginradio-three-consecutive-chains.ogg
> It reports "Application provided invalid, non monotonically increasing
> dts to muxer in stream 0", which appears to be because the initial
> timestamp is not 0:00. This affects all raw muxers, including PCM.
> 
> Signed-off-by: Jack Bruienne <jackbruienne@gmail.com>
> ---
>  Changelog                 |   1 +
>  MAINTAINERS               |   2 +
>  doc/general_contents.texi |   2 +
>  libavcodec/Makefile       |   2 +
>  libavcodec/allcodecs.c    |   2 +
>  libavcodec/codec_desc.c   |   7 ++
>  libavcodec/codec_id.h     |   1 +
>  libavcodec/dfpwmdec.c     | 138 +++++++++++++++++++++++++++++++++++++
>  libavcodec/dfpwmenc.c     | 140 ++++++++++++++++++++++++++++++++++++++
>  libavcodec/utils.c        |   2 +
>  libavcodec/version.h      |   2 +-
>  libavformat/Makefile      |   2 +
>  libavformat/allformats.c  |   2 +
>  libavformat/dfpwmdec.c    | 107 +++++++++++++++++++++++++++++
>  libavformat/rawenc.c      |  13 ++++
>  libavformat/version.h     |   4 +-
>  16 files changed, 424 insertions(+), 3 deletions(-)
>  create mode 100644 libavcodec/dfpwmdec.c
>  create mode 100644 libavcodec/dfpwmenc.c
>  create mode 100644 libavformat/dfpwmdec.c
> 
> diff --git a/Changelog b/Changelog
> index 5ad2cef..ec688da 100644
> --- a/Changelog
> +++ b/Changelog
> @@ -4,6 +4,7 @@ releases are sorted from youngest to oldest.
>  version 5.1:
>  - dialogue enhance audio filter
>  - dropped obsolete XvMC hwaccel
> +- DFPWM audio encoder/decoder and raw muxer/demuxer


Keep empty line here above and everywhere else you had removed them.
Is patch corrupted somehow? Just attach file.
And split demuxer and muxer addition from decoder and encoder.

>    version 5.0:
> diff --git a/MAINTAINERS b/MAINTAINERS
> index f33ccbd..931cf4b 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -161,6 +161,7 @@ Codecs:
>    cscd.c                                Reimar Doeffinger
>    cuviddec.c                            Timo Rothenpieler
>    dca*                                  foo86
> +  dfpwm*                                Jack Bruienne
>    dirac*                                Rostislav Pehlivanov
>    dnxhd*                                Baptiste Coudurier
>    dolby_e*                              foo86
> @@ -415,6 +416,7 @@ Muxers/Demuxers:
>    dashdec.c                             Steven Liu
>    dashenc.c                             Karthick Jeyapal
>    daud.c                                Reimar Doeffinger
> +  dfpwmdec.c                            Jack Bruienne
>    dss.c                                 Oleksij Rempel
>    dtsdec.c                              foo86
>    dtshddec.c                            Paul B Mahol
> diff --git a/doc/general_contents.texi b/doc/general_contents.texi
> index df1692c..fcd9da1 100644
> --- a/doc/general_contents.texi
> +++ b/doc/general_contents.texi
> @@ -578,6 +578,7 @@ library:
>  @item raw aptX                  @tab X @tab X
>  @item raw aptX HD               @tab X @tab X
>  @item raw Chinese AVS video     @tab X @tab X
> +@item raw DFPWM                 @tab X @tab X
>  @item raw Dirac                 @tab X @tab X
>  @item raw DNxHD                 @tab X @tab X
>  @item raw DTS                   @tab X @tab X
> @@ -1194,6 +1195,7 @@ following image formats are supported:
>  @item CRI HCA                @tab     @tab X
>  @item Delphine Software International CIN audio  @tab     @tab  X
>      @tab Codec used in Delphine Software International games.
> +@item DFPWM                  @tab  X  @tab  X
>  @item Digital Speech Standard - Standard Play mode (DSS SP) @tab @tab  X
>  @item Discworld II BMV Audio @tab     @tab  X
>  @item COOK                   @tab     @tab  X
> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
> index 6076b4a..7474220 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -289,6 +289,8 @@ OBJS-$(CONFIG_DERF_DPCM_DECODER)       += dpcm.o
>  OBJS-$(CONFIG_DIRAC_DECODER)           += diracdec.o dirac.o diracdsp.o
> diractab.o \
>                                            dirac_arith.o dirac_dwt.o
> dirac_vlc.o
>  OBJS-$(CONFIG_DFA_DECODER)             += dfa.o
> +OBJS-$(CONFIG_DFPWM_DECODER)           += dfpwmdec.o
> +OBJS-$(CONFIG_DFPWM_ENCODER)           += dfpwmenc.o
>  OBJS-$(CONFIG_DNXHD_DECODER)           += dnxhddec.o dnxhddata.o
>  OBJS-$(CONFIG_DNXHD_ENCODER)           += dnxhdenc.o dnxhddata.o
>  OBJS-$(CONFIG_DOLBY_E_DECODER)         += dolby_e.o dolby_e_parse.o
> kbdwin.o
> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
> index d1e1019..c3a0c26 100644
> --- a/libavcodec/allcodecs.c
> +++ b/libavcodec/allcodecs.c
> @@ -437,6 +437,8 @@ extern const AVCodec ff_bmv_audio_decoder;
>  extern const AVCodec ff_cook_decoder;
>  extern const AVCodec ff_dca_encoder;
>  extern const AVCodec ff_dca_decoder;
> +extern const AVCodec ff_dfpwm_encoder;
> +extern const AVCodec ff_dfpwm_decoder;
>  extern const AVCodec ff_dolby_e_decoder;
>  extern const AVCodec ff_dsd_lsbf_decoder;
>  extern const AVCodec ff_dsd_msbf_decoder;
> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
> index 725c687..87ca591 100644
> --- a/libavcodec/codec_desc.c
> +++ b/libavcodec/codec_desc.c
> @@ -3237,6 +3237,13 @@ static const AVCodecDescriptor codec_descriptors[] =
> {
>          .long_name = NULL_IF_CONFIG_SMALL("MSN Siren"),
>          .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
>      },
> +    {
> +        .id        = AV_CODEC_ID_DFPWM,
> +        .type      = AVMEDIA_TYPE_AUDIO,
> +        .name      = "dfpwm",
> +        .long_name = NULL_IF_CONFIG_SMALL("DFPWM1a audio"),

You could use full description here instead of just DFPWM1a.


> +        .props     = AV_CODEC_PROP_LOSSY,
> +    },
>       /* subtitle codecs */
>      {
> diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
> index ab265ec..3ffb9bd 100644
> --- a/libavcodec/codec_id.h
> +++ b/libavcodec/codec_id.h
> @@ -516,6 +516,7 @@ enum AVCodecID {
>      AV_CODEC_ID_HCA,
>      AV_CODEC_ID_FASTAUDIO,
>      AV_CODEC_ID_MSNSIREN,
> +    AV_CODEC_ID_DFPWM,

Keep empty line here.

>       /* subtitle codecs */
>      AV_CODEC_ID_FIRST_SUBTITLE = 0x17000,          ///< A dummy ID pointing
> at the start of subtitle codecs.
> diff --git a/libavcodec/dfpwmdec.c b/libavcodec/dfpwmdec.c
> new file mode 100644
> index 0000000..9f12841
> --- /dev/null
> +++ b/libavcodec/dfpwmdec.c
> @@ -0,0 +1,138 @@
> +/*
> + * DFPWM decoder
> + * Copyright (c) 2022 Jack Bruienne
> + * Copyright (c) 2012, 2016 Ben "GreaseMonkey" Russell
> + *
> + * 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
> + */
> +
> +/**
> + * @file
> + * DFPWM1a decoder
> + */
> +
> +#include "libavutil/internal.h"
> +#include "avcodec.h"
> +#include "codec_id.h"
> +#include "internal.h"
> +
> +typedef struct {
> +    int fq, q, s, lt;
> +} DFPWMState;
> +
> +// DFPWM codec from https://github.com/ChenThread/dfpwm/blob/master/1a/
> +// Licensed in the public domain
> +
> +#ifndef CONST_PREC
> +#define CONST_PREC 10
> +#endif
> +
> +static void au_decompress(DFPWMState *state, int fs, int len, int8_t
> *outbuf, uint8_t *inbuf)
> +{
> +    int i, j;
> +    uint8_t d;
> +    for (i = 0; i < len; i++) {
> +        // get bits
> +        d = *(inbuf++);
> +        for (j = 0; j < 8; j++) {
> +            int nq, lq, st, ns, ov;
> +            // set target
> +            int t = ((d&1) ? 127 : -128);
> +            d >>= 1;
> +
> +            // adjust charge
> +            nq = state->q + ((state->s * (t-state->q) +
> (1<<(CONST_PREC-1)))>>CONST_PREC);
> +            if(nq == state->q && nq != t)
> +                state->q += (t == 127 ? 1 : -1);
> +            lq = state->q;
> +            state->q = nq;
> +
> +            // adjust strength
> +            st = (t != state->lt ? 0 : (1<<CONST_PREC)-1);
> +            ns = state->s;
> +            if(ns != st)
> +                ns += (st != 0 ? 1 : -1);
> +#if CONST_PREC > 8
> +            if(ns < 1+(1<<(CONST_PREC-8))) ns = 1+(1<<(CONST_PREC-8));
> +#endif
> +            state->s = ns;
> +
> +            // FILTER: perform antijerk
> +            ov = (t != state->lt ? (nq+lq)>>1 : nq);
> +
> +            // FILTER: perform LPF
> +            state->fq += ((fs*(ov-state->fq) + 0x80)>>8);
> +            ov = state->fq;
> +
> +            // output sample
> +            *(outbuf++) = ov;
> +
> +            state->lt = t;
> +        }
> +    }
> +}
> +
> +static av_cold int dfpwm_dec_init(struct AVCodecContext *ctx)
> +{
> +    DFPWMState *state = ctx->priv_data;
> +
> +    state->fq = 0;
> +    state->q = 0;
> +    state->s = 0;
> +    state->lt = -128;
> +
> +    return 0;
> +}
> +
> +static av_cold int dfpwm_dec_end(struct AVCodecContext *ctx)
> +{
> +    return 0;
> +}

Remove if not gonna be used and is empty.

> +
> +static int dfpwm_dec_frame(struct AVCodecContext *ctx, void *data,
> +    int *got_frame, struct AVPacket *packet)
> +{
> +    DFPWMState *state = ctx->priv_data;
> +    AVFrame *frame = data;
> +
> +    frame->format = AV_SAMPLE_FMT_U8;
> +    frame->nb_samples = packet->size * 8;
> +    frame->channel_layout = AV_CH_LAYOUT_MONO;
> +
> +    av_frame_get_buffer(frame, 0);

Use ff_get_buffer()

See how libavcodec/pcm.c codecs handles that.

> +
> +    au_decompress(state, 100, packet->size, frame->data[0], packet->data);
> +
> +    // convert from signed to unsigned 8-bit, as DFPWM outputs S8 but
> FFmpeg needs U8
> +    for (int i = 0; i < packet->size * 8; i++) frame->data[0][i] =
> ((int8_t*)frame->data[0])[i] + 128;

Cant you remove this wrapper, for such trivial code is not helping here.
Than you can do conversion from S8 to U8 on the fly without need for extra buffer.

> +
> +    if (got_frame) *got_frame = 1;
> +    return packet->size;
> +}
> +
> +const AVCodec ff_dfpwm_decoder = {
> +    .name           = "dfpwm",
> +    .long_name      = NULL_IF_CONFIG_SMALL("DFPWM1a audio"),
> +    .type           = AVMEDIA_TYPE_AUDIO,
> +    .id             = AV_CODEC_ID_DFPWM,
> +    .priv_data_size = sizeof(DFPWMState),
> +    .init           = dfpwm_dec_init,
> +    .close          = dfpwm_dec_end,
> +    .decode         = dfpwm_dec_frame,
> +    .capabilities   = 0,

This flags are not correct, or optimal in any way.

> +    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
> +};
> \ No newline at end of file
> diff --git a/libavcodec/dfpwmenc.c b/libavcodec/dfpwmenc.c
> new file mode 100644
> index 0000000..97fc42e
> --- /dev/null
> +++ b/libavcodec/dfpwmenc.c
> @@ -0,0 +1,140 @@
> +/*
> + * DFPWM encoder
> + * Copyright (c) 2022 Jack Bruienne
> + * Copyright (c) 2012, 2016 Ben "GreaseMonkey" Russell
> + *
> + * 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
> + */
> +
> +/**
> + * @file
> + * DFPWM1a encoder
> + */
> +
> +#include "libavutil/internal.h"
> +#include "avcodec.h"
> +#include "codec_id.h"
> +#include "internal.h"
> +
> +typedef struct {
> +    int fq, q, s, lt;
> +} DFPWMState;
> +
> +// DFPWM codec from https://github.com/ChenThread/dfpwm/blob/master/1a/
> +// Licensed in the public domain
> +
> +#ifndef CONST_PREC
> +#define CONST_PREC 10
> +#endif
> +
> +// note, len denotes how many compressed bytes there are (uncompressed
> bytes / 8).
> +static void au_compress(DFPWMState *state, int len, uint8_t *outbuf, int8_t
> *inbuf)
> +{
> +    int i, j;
> +    uint8_t d = 0;
> +    for (i = 0; i < len; i++) {
> +        for (j = 0; j < 8; j++) {
> +            int nq, st, ns;
> +            // get sample
> +            int v = *(inbuf++);
> +            // set bit / target
> +            int t = (v < state->q || v == -128 ? -128 : 127);
> +            d >>= 1;
> +            if(t > 0)
> +                d |= 0x80;
> +
> +            // adjust charge
> +            nq = state->q + ((state->s * (t-state->q) +
> (1<<(CONST_PREC-1)))>>CONST_PREC);
> +            if(nq == state->q && nq != t)
> +                nq += (t == 127 ? 1 : -1);
> +            state->q = nq;
> +
> +            // adjust strength
> +            st = (t != state->lt ? 0 : (1<<CONST_PREC)-1);
> +            ns = state->s;
> +            if(ns != st)
> +                ns += (st != 0 ? 1 : -1);
> +#if CONST_PREC > 8
> +            if(ns < 1+(1<<(CONST_PREC-8))) ns = 1+(1<<(CONST_PREC-8));
> +#endif
> +            state->s = ns;
> +
> +            state->lt = t;
> +
> +            //fprintf(stderr, "%4i %4i %4i %4i\n", v, *q, *s, t);
> +            //usleep(10000);
> +        }
> +
> +        // output bits
> +        *(outbuf++) = d;
> +    }
> +}
> +
> +static av_cold int dfpwm_enc_init(struct AVCodecContext *ctx)
> +{
> +    DFPWMState *state = ctx->priv_data;
> +
> +    state->fq = 0;
> +    state->q = 0;
> +    state->s = 0;
> +    state->lt = -128;
> +
> +    return 0;
> +}
> +
> +static av_cold int dfpwm_enc_end(struct AVCodecContext *ctx)
> +{
> +    return 0;
> +}

Please remove functions that do nothing.

> +
> +static int dfpwm_enc_frame(struct AVCodecContext *ctx, struct AVPacket
> *packet,
> +    const struct AVFrame *frame, int *got_packet)
> +{
> +    DFPWMState *state = ctx->priv_data;
> +    int size = frame->nb_samples / 8 + (frame->nb_samples % 8 > 0 ? 1 : 0);
> +    int8_t *data = av_malloc(size * 8);
> +
> +    if (!data) return AVERROR(ENOMEM);
> +
> +    if (packet->size < size) av_grow_packet(packet, size - packet->size);
> +    else if (packet->size > size) av_shrink_packet(packet, size);
> +
> +    // make a temporary S8 buffer as DFPWM needs S8 but FFmpeg uses U8
> +    for (int i = 0; i < frame->nb_samples; i++) data[i] = frame->data[0][i]
> - 128;
> +    for (int i = frame->nb_samples; i < size * 8; i++) data[i] = 0;
> +
> +    au_compress(state, size, packet->data, data);
> +    av_free(data);

Using extra buffer for such trivial code is not optimal solution.

> +
> +    if (got_packet) *got_packet = 1;
> +    return 0;
> +}
> +
> +const AVCodec ff_dfpwm_encoder = {
> +    .name            = "dfpwm",
> +    .long_name       = NULL_IF_CONFIG_SMALL("DFPWM1a audio"),
> +    .type            = AVMEDIA_TYPE_AUDIO,
> +    .id              = AV_CODEC_ID_DFPWM,
> +    .priv_data_size  = sizeof(DFPWMState),
> +    .init            = dfpwm_enc_init,
> +    .close           = dfpwm_enc_end,
> +    .encode2         = dfpwm_enc_frame,
> +    .sample_fmts     = (const enum AVSampleFormat[]){AV_SAMPLE_FMT_U8,
> AV_SAMPLE_FMT_NONE},
> +    .channel_layouts = (const uint64_t[]){AV_CH_LAYOUT_MONO, 0},
> +    .capabilities    = AV_CODEC_CAP_VARIABLE_FRAME_SIZE,
> +    .caps_internal   = FF_CODEC_CAP_INIT_THREADSAFE,
> +};
> diff --git a/libavcodec/utils.c b/libavcodec/utils.c
> index 6f9d90a..066da76 100644
> --- a/libavcodec/utils.c
> +++ b/libavcodec/utils.c
> @@ -577,6 +577,8 @@ enum AVCodecID av_get_pcm_codec(enum AVSampleFormat fmt,
> int be)
>  int av_get_bits_per_sample(enum AVCodecID codec_id)
>  {
>      switch (codec_id) {
> +    case AV_CODEC_ID_DFPWM:
> +        return 1;
>      case AV_CODEC_ID_ADPCM_SBPRO_2:
>          return 2;
>      case AV_CODEC_ID_ADPCM_SBPRO_3:
> diff --git a/libavcodec/version.h b/libavcodec/version.h
> index d900503..84f3979 100644
> --- a/libavcodec/version.h
> +++ b/libavcodec/version.h
> @@ -28,7 +28,7 @@
>  #include "libavutil/version.h"
>   #define LIBAVCODEC_VERSION_MAJOR  59
> -#define LIBAVCODEC_VERSION_MINOR  21
> +#define LIBAVCODEC_VERSION_MINOR  22
>  #define LIBAVCODEC_VERSION_MICRO 100
>   #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
> diff --git a/libavformat/Makefile b/libavformat/Makefile
> index 6566e40..b89073a 100644
> --- a/libavformat/Makefile
> +++ b/libavformat/Makefile
> @@ -165,6 +165,8 @@ OBJS-$(CONFIG_DAUD_MUXER)                += daudenc.o
>  OBJS-$(CONFIG_DCSTR_DEMUXER)             += dcstr.o
>  OBJS-$(CONFIG_DERF_DEMUXER)              += derf.o pcm.o
>  OBJS-$(CONFIG_DFA_DEMUXER)               += dfa.o
> +OBJS-$(CONFIG_DFPWM_DEMUXER)             += dfpwmdec.o pcm.o
> +OBJS-$(CONFIG_DFPWM_MUXER)               += rawenc.o
>  OBJS-$(CONFIG_DHAV_DEMUXER)              += dhav.o
>  OBJS-$(CONFIG_DIRAC_DEMUXER)             += diracdec.o rawdec.o
>  OBJS-$(CONFIG_DIRAC_MUXER)               += rawenc.o
> diff --git a/libavformat/allformats.c b/libavformat/allformats.c
> index d066a77..587ad59 100644
> --- a/libavformat/allformats.c
> +++ b/libavformat/allformats.c
> @@ -124,6 +124,8 @@ extern const AVOutputFormat ff_daud_muxer;
>  extern const AVInputFormat  ff_dcstr_demuxer;
>  extern const AVInputFormat  ff_derf_demuxer;
>  extern const AVInputFormat  ff_dfa_demuxer;
> +extern const AVInputFormat  ff_dfpwm_demuxer;
> +extern const AVOutputFormat ff_dfpwm_muxer;
>  extern const AVInputFormat  ff_dhav_demuxer;
>  extern const AVInputFormat  ff_dirac_demuxer;
>  extern const AVOutputFormat ff_dirac_muxer;
> diff --git a/libavformat/dfpwmdec.c b/libavformat/dfpwmdec.c
> new file mode 100644
> index 0000000..ad5bfa5
> --- /dev/null
> +++ b/libavformat/dfpwmdec.c
> @@ -0,0 +1,107 @@
> +/*
> + * RAW PCM demuxers
> + * Copyright (c) 2002 Fabrice Bellard
> + * Copyright (c) 2022 Jack Bruienne
> + *
> + * 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/avstring.h"
> +#include "avformat.h"
> +#include "internal.h"
> +#include "pcm.h"
> +#include "libavutil/log.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/avassert.h"
> +
> +typedef struct DFPWMAudioDemuxerContext {
> +    AVClass *class;
> +    int sample_rate;
> +} DFPWMAudioDemuxerContext;
> +
> +static int dfpwm_read_header(AVFormatContext *s)
> +{
> +    DFPWMAudioDemuxerContext *s1 = s->priv_data;
> +    AVCodecParameters *par;
> +    AVStream *st;
> +    uint8_t *mime_type = NULL;
> +
> +    st = avformat_new_stream(s, NULL);
> +    if (!st)
> +        return AVERROR(ENOMEM);
> +    par = st->codecpar;
> +
> +    par->codec_type  = AVMEDIA_TYPE_AUDIO;
> +    par->codec_id    = s->iformat->raw_codec_id;
> +    par->sample_rate = s1->sample_rate;
> +    par->channels    = 1;
> +
> +    av_opt_get(s->pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type);
> +    if (mime_type && s->iformat->mime_type) {
> +        int rate = 0;
> +        const char *options;
> +        if (av_stristart(mime_type, s->iformat->mime_type, &options)) { /*
> audio/L16 */
> +            while (options = strchr(options, ';')) {
> +                options++;
> +                if (!rate)
> +                    sscanf(options, " rate=%d",     &rate);
> +            }
> +            if (rate <= 0) {
> +                av_log(s, AV_LOG_ERROR,
> +                       "Invalid sample_rate found in mime_type \"%s\"\n",
> +                       mime_type);
> +                av_freep(&mime_type);
> +                return AVERROR_INVALIDDATA;
> +            }
> +            par->sample_rate = rate;
> +        }
> +    }
> +    av_freep(&mime_type);
> +
> +    par->bits_per_coded_sample = av_get_bits_per_sample(par->codec_id);
> +
> +    av_assert0(par->bits_per_coded_sample > 0);
> +
> +    par->block_align = 1;
> +
> +    avpriv_set_pts_info(st, 64, 1, par->sample_rate);
> +    return 0;
> +}
> +
> +static const AVOption dfpwm_options[] = {
> +    { "sample_rate", "", offsetof(DFPWMAudioDemuxerContext, sample_rate),
> AV_OPT_TYPE_INT, {.i64 = 44100}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
> +    { NULL },
> +};
> +static const AVClass dfpwm_demuxer_class = {
> +    .class_name = "dfpwm demuxer",
> +    .item_name  = av_default_item_name,
> +    .option     = dfpwm_options,
> +    .version    = LIBAVUTIL_VERSION_INT,
> +};
> +
> +const AVInputFormat ff_dfpwm_demuxer = {
> +    .name           = "dfpwm",
> +    .long_name      = NULL_IF_CONFIG_SMALL("raw DFPWM1a data"),
> +    .priv_data_size = sizeof(DFPWMAudioDemuxerContext),
> +    .read_header    = dfpwm_read_header,
> +    .read_packet    = ff_pcm_read_packet,
> +    .read_seek      = ff_pcm_read_seek,
> +    .flags          = AVFMT_GENERIC_INDEX,
> +    .extensions     = "dfpwm",
> +    .raw_codec_id   = AV_CODEC_ID_DFPWM,
> +    .priv_class     = &dfpwm_demuxer_class,
> +};
> \ No newline at end of file
> diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c
> index 4bbae77..17b627b 100644
> --- a/libavformat/rawenc.c
> +++ b/libavformat/rawenc.c
> @@ -192,6 +192,19 @@ const AVOutputFormat ff_data_muxer = {
>  };
>  #endif
>  +#if CONFIG_DFPWM_MUXER
> +const AVOutputFormat ff_dfpwm_muxer = {
> +    .name              = "dfpwm",
> +    .long_name         = NULL_IF_CONFIG_SMALL("raw DFPWM1a audio"),
> +    .extensions        = "dfpwm",
> +    .audio_codec       = AV_CODEC_ID_DFPWM,
> +    .video_codec       = AV_CODEC_ID_NONE,
> +    .init              = force_one_stream,
> +    .write_packet      = ff_raw_write_packet,
> +    .flags             = AVFMT_NOTIMESTAMPS,
> +};
> +#endif
> +
>  #if CONFIG_DIRAC_MUXER
>  const AVOutputFormat ff_dirac_muxer = {
>      .name              = "dirac",
> diff --git a/libavformat/version.h b/libavformat/version.h
> index 2623457..0f89af4 100644
> --- a/libavformat/version.h
> +++ b/libavformat/version.h
> @@ -32,8 +32,8 @@
>  // Major bumping may affect Ticket5467, 5421, 5451(compatibility with
> Chromium)
>  // Also please add any ticket numbers that you believe might be affected
> here
>  #define LIBAVFORMAT_VERSION_MAJOR  59
> -#define LIBAVFORMAT_VERSION_MINOR  17
> -#define LIBAVFORMAT_VERSION_MICRO 102
> +#define LIBAVFORMAT_VERSION_MINOR  18
> +#define LIBAVFORMAT_VERSION_MICRO 100
>   #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR,
> \
> 
> LIBAVFORMAT_VERSION_MINOR, \
> -- 
> 2.35.1
> 
> _______________________________________________
> 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".
Jack Bruienne Feb. 25, 2022, 11:42 p.m. UTC | #2
Thanks for looking over the patch. I used git format-patch to make the 
email and then sent it with Thunderbird; it appears that the command on 
the website doesn't put the patch in an attachment. I'll be adding 
--attach for future patches to fix this.

I will be sending split updated patches fixing the issues you mentioned 
promptly.

On 2/25/22 03:15, Paul B Mahol wrote:

> On Fri, Feb 25, 2022 at 02:54:35AM -0500, Jack Bruienne wrote:
>>  From the wiki page (https://wiki.vexatos.com/dfpwm):
>>> DFPWM (Dynamic Filter Pulse Width Modulation) is an audio codec
>>> created by Ben “GreaseMonkey” Russell in 2012, originally to be used
>>> as a voice codec for asiekierka's pixmess, a C remake of 64pixels.
>>> It is a 1-bit-per-sample codec which uses a dynamic-strength one-pole
>>> low-pass filter as a predictor. Due to the fact that a raw DPFWM decoding
>>> creates a high-pitched whine, it is often followed by some post-processing
>>> filters to make the stream more listenable.
>> It has recently gained popularity through the ComputerCraft mod for
>> Minecraft, which added support for audio through this codec, as well as
>> the Computronics expansion which preceeded the official support. These
>> both implement the slightly adjusted 1a version of the codec, which is
>> the version I have chosen for this patch.
>>
>> This patch adds both a new codec (with encoding and decoding), as well as
>> a raw audio format to be able to read/write the raw files that are most
>> commonly used (as no other container format supports it yet).
>>
>> The codec sources are pretty simple: they use the reference codec with
>> a basic wrapper to connect it to the FFmpeg AVCodec system. There's a
>> bit of extra code to convert from unsigned to signed 8-bit audio, as the
>> codec implementation operates on signed data, which FFmpeg doesn't support.
>>
>> The muxers are mostly copied from the PCM demuxer and the raw muxers, as
>> DFPWM is typically stored as raw data.
>>
>> This patch will be highly useful to ComputerCraft developers who are
>> working with audio, as it is the standard format for audio, and there
>> are few user-friendly encoders out there. It will streamline the process
>> for importing audio, replacing the need to write code or use tools that
>> require very specific input formats.
>>
>> You may use the CraftOS-PC program (https://www.craftos-pc.cc) to test
>> out DFPWM playback. To use it, run the program and type this command:
>> "attach left speaker" Then run "speaker play <file.dfpwm>" for each file.
>> The app runs in a sandbox, so files have to be transferred in first;
>> the easiest way to do this is to simply drag the file on the window.
>> (Or copy files to the folder at https://www.craftos-pc.cc/docs/saves.)
>>
>> Sample DFPWM files can be generated with an online tool at
>> https://music.madefor.cc. This is the current best way to encode DFPWM
>> files. Simply drag an audio file onto the page, and it will encode it,
>> giving a download link on the page.
>>
>> I've made sure to update all of the docs as per Developer§7, and I've
>> tested it as per section 8. Test files encoded to DFPWM play correctly
>> in ComputerCraft, and other files that work in CC are correctly decoded.
>> I have also verified that corrupt files do not crash the decoder - this
>> should theoretically not be an issue as the result size is constant with
>> respect to the input size.
>>
>> One thing I noticed is that this sample file fails to decode to raw:
>> https://samples.ffmpeg.org/ogg/virginradio-three-consecutive-chains.ogg
>> It reports "Application provided invalid, non monotonically increasing
>> dts to muxer in stream 0", which appears to be because the initial
>> timestamp is not 0:00. This affects all raw muxers, including PCM.
>>
>> Signed-off-by: Jack Bruienne <jackbruienne@gmail.com>
>> ---
>>   Changelog                 |   1 +
>>   MAINTAINERS               |   2 +
>>   doc/general_contents.texi |   2 +
>>   libavcodec/Makefile       |   2 +
>>   libavcodec/allcodecs.c    |   2 +
>>   libavcodec/codec_desc.c   |   7 ++
>>   libavcodec/codec_id.h     |   1 +
>>   libavcodec/dfpwmdec.c     | 138 +++++++++++++++++++++++++++++++++++++
>>   libavcodec/dfpwmenc.c     | 140 ++++++++++++++++++++++++++++++++++++++
>>   libavcodec/utils.c        |   2 +
>>   libavcodec/version.h      |   2 +-
>>   libavformat/Makefile      |   2 +
>>   libavformat/allformats.c  |   2 +
>>   libavformat/dfpwmdec.c    | 107 +++++++++++++++++++++++++++++
>>   libavformat/rawenc.c      |  13 ++++
>>   libavformat/version.h     |   4 +-
>>   16 files changed, 424 insertions(+), 3 deletions(-)
>>   create mode 100644 libavcodec/dfpwmdec.c
>>   create mode 100644 libavcodec/dfpwmenc.c
>>   create mode 100644 libavformat/dfpwmdec.c
>>
>> diff --git a/Changelog b/Changelog
>> index 5ad2cef..ec688da 100644
>> --- a/Changelog
>> +++ b/Changelog
>> @@ -4,6 +4,7 @@ releases are sorted from youngest to oldest.
>>   version 5.1:
>>   - dialogue enhance audio filter
>>   - dropped obsolete XvMC hwaccel
>> +- DFPWM audio encoder/decoder and raw muxer/demuxer
>
> Keep empty line here above and everywhere else you had removed them.
> Is patch corrupted somehow? Just attach file.
> And split demuxer and muxer addition from decoder and encoder.
>
>>     version 5.0:
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index f33ccbd..931cf4b 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -161,6 +161,7 @@ Codecs:
>>     cscd.c                                Reimar Doeffinger
>>     cuviddec.c                            Timo Rothenpieler
>>     dca*                                  foo86
>> +  dfpwm*                                Jack Bruienne
>>     dirac*                                Rostislav Pehlivanov
>>     dnxhd*                                Baptiste Coudurier
>>     dolby_e*                              foo86
>> @@ -415,6 +416,7 @@ Muxers/Demuxers:
>>     dashdec.c                             Steven Liu
>>     dashenc.c                             Karthick Jeyapal
>>     daud.c                                Reimar Doeffinger
>> +  dfpwmdec.c                            Jack Bruienne
>>     dss.c                                 Oleksij Rempel
>>     dtsdec.c                              foo86
>>     dtshddec.c                            Paul B Mahol
>> diff --git a/doc/general_contents.texi b/doc/general_contents.texi
>> index df1692c..fcd9da1 100644
>> --- a/doc/general_contents.texi
>> +++ b/doc/general_contents.texi
>> @@ -578,6 +578,7 @@ library:
>>   @item raw aptX                  @tab X @tab X
>>   @item raw aptX HD               @tab X @tab X
>>   @item raw Chinese AVS video     @tab X @tab X
>> +@item raw DFPWM                 @tab X @tab X
>>   @item raw Dirac                 @tab X @tab X
>>   @item raw DNxHD                 @tab X @tab X
>>   @item raw DTS                   @tab X @tab X
>> @@ -1194,6 +1195,7 @@ following image formats are supported:
>>   @item CRI HCA                @tab     @tab X
>>   @item Delphine Software International CIN audio  @tab     @tab  X
>>       @tab Codec used in Delphine Software International games.
>> +@item DFPWM                  @tab  X  @tab  X
>>   @item Digital Speech Standard - Standard Play mode (DSS SP) @tab @tab  X
>>   @item Discworld II BMV Audio @tab     @tab  X
>>   @item COOK                   @tab     @tab  X
>> diff --git a/libavcodec/Makefile b/libavcodec/Makefile
>> index 6076b4a..7474220 100644
>> --- a/libavcodec/Makefile
>> +++ b/libavcodec/Makefile
>> @@ -289,6 +289,8 @@ OBJS-$(CONFIG_DERF_DPCM_DECODER)       += dpcm.o
>>   OBJS-$(CONFIG_DIRAC_DECODER)           += diracdec.o dirac.o diracdsp.o
>> diractab.o \
>>                                             dirac_arith.o dirac_dwt.o
>> dirac_vlc.o
>>   OBJS-$(CONFIG_DFA_DECODER)             += dfa.o
>> +OBJS-$(CONFIG_DFPWM_DECODER)           += dfpwmdec.o
>> +OBJS-$(CONFIG_DFPWM_ENCODER)           += dfpwmenc.o
>>   OBJS-$(CONFIG_DNXHD_DECODER)           += dnxhddec.o dnxhddata.o
>>   OBJS-$(CONFIG_DNXHD_ENCODER)           += dnxhdenc.o dnxhddata.o
>>   OBJS-$(CONFIG_DOLBY_E_DECODER)         += dolby_e.o dolby_e_parse.o
>> kbdwin.o
>> diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
>> index d1e1019..c3a0c26 100644
>> --- a/libavcodec/allcodecs.c
>> +++ b/libavcodec/allcodecs.c
>> @@ -437,6 +437,8 @@ extern const AVCodec ff_bmv_audio_decoder;
>>   extern const AVCodec ff_cook_decoder;
>>   extern const AVCodec ff_dca_encoder;
>>   extern const AVCodec ff_dca_decoder;
>> +extern const AVCodec ff_dfpwm_encoder;
>> +extern const AVCodec ff_dfpwm_decoder;
>>   extern const AVCodec ff_dolby_e_decoder;
>>   extern const AVCodec ff_dsd_lsbf_decoder;
>>   extern const AVCodec ff_dsd_msbf_decoder;
>> diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
>> index 725c687..87ca591 100644
>> --- a/libavcodec/codec_desc.c
>> +++ b/libavcodec/codec_desc.c
>> @@ -3237,6 +3237,13 @@ static const AVCodecDescriptor codec_descriptors[] =
>> {
>>           .long_name = NULL_IF_CONFIG_SMALL("MSN Siren"),
>>           .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
>>       },
>> +    {
>> +        .id        = AV_CODEC_ID_DFPWM,
>> +        .type      = AVMEDIA_TYPE_AUDIO,
>> +        .name      = "dfpwm",
>> +        .long_name = NULL_IF_CONFIG_SMALL("DFPWM1a audio"),
> You could use full description here instead of just DFPWM1a.
>
>
>> +        .props     = AV_CODEC_PROP_LOSSY,
>> +    },
>>        /* subtitle codecs */
>>       {
>> diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
>> index ab265ec..3ffb9bd 100644
>> --- a/libavcodec/codec_id.h
>> +++ b/libavcodec/codec_id.h
>> @@ -516,6 +516,7 @@ enum AVCodecID {
>>       AV_CODEC_ID_HCA,
>>       AV_CODEC_ID_FASTAUDIO,
>>       AV_CODEC_ID_MSNSIREN,
>> +    AV_CODEC_ID_DFPWM,
> Keep empty line here.
>
>>        /* subtitle codecs */
>>       AV_CODEC_ID_FIRST_SUBTITLE = 0x17000,          ///< A dummy ID pointing
>> at the start of subtitle codecs.
>> diff --git a/libavcodec/dfpwmdec.c b/libavcodec/dfpwmdec.c
>> new file mode 100644
>> index 0000000..9f12841
>> --- /dev/null
>> +++ b/libavcodec/dfpwmdec.c
>> @@ -0,0 +1,138 @@
>> +/*
>> + * DFPWM decoder
>> + * Copyright (c) 2022 Jack Bruienne
>> + * Copyright (c) 2012, 2016 Ben "GreaseMonkey" Russell
>> + *
>> + * 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
>> + */
>> +
>> +/**
>> + * @file
>> + * DFPWM1a decoder
>> + */
>> +
>> +#include "libavutil/internal.h"
>> +#include "avcodec.h"
>> +#include "codec_id.h"
>> +#include "internal.h"
>> +
>> +typedef struct {
>> +    int fq, q, s, lt;
>> +} DFPWMState;
>> +
>> +// DFPWM codec from https://github.com/ChenThread/dfpwm/blob/master/1a/
>> +// Licensed in the public domain
>> +
>> +#ifndef CONST_PREC
>> +#define CONST_PREC 10
>> +#endif
>> +
>> +static void au_decompress(DFPWMState *state, int fs, int len, int8_t
>> *outbuf, uint8_t *inbuf)
>> +{
>> +    int i, j;
>> +    uint8_t d;
>> +    for (i = 0; i < len; i++) {
>> +        // get bits
>> +        d = *(inbuf++);
>> +        for (j = 0; j < 8; j++) {
>> +            int nq, lq, st, ns, ov;
>> +            // set target
>> +            int t = ((d&1) ? 127 : -128);
>> +            d >>= 1;
>> +
>> +            // adjust charge
>> +            nq = state->q + ((state->s * (t-state->q) +
>> (1<<(CONST_PREC-1)))>>CONST_PREC);
>> +            if(nq == state->q && nq != t)
>> +                state->q += (t == 127 ? 1 : -1);
>> +            lq = state->q;
>> +            state->q = nq;
>> +
>> +            // adjust strength
>> +            st = (t != state->lt ? 0 : (1<<CONST_PREC)-1);
>> +            ns = state->s;
>> +            if(ns != st)
>> +                ns += (st != 0 ? 1 : -1);
>> +#if CONST_PREC > 8
>> +            if(ns < 1+(1<<(CONST_PREC-8))) ns = 1+(1<<(CONST_PREC-8));
>> +#endif
>> +            state->s = ns;
>> +
>> +            // FILTER: perform antijerk
>> +            ov = (t != state->lt ? (nq+lq)>>1 : nq);
>> +
>> +            // FILTER: perform LPF
>> +            state->fq += ((fs*(ov-state->fq) + 0x80)>>8);
>> +            ov = state->fq;
>> +
>> +            // output sample
>> +            *(outbuf++) = ov;
>> +
>> +            state->lt = t;
>> +        }
>> +    }
>> +}
>> +
>> +static av_cold int dfpwm_dec_init(struct AVCodecContext *ctx)
>> +{
>> +    DFPWMState *state = ctx->priv_data;
>> +
>> +    state->fq = 0;
>> +    state->q = 0;
>> +    state->s = 0;
>> +    state->lt = -128;
>> +
>> +    return 0;
>> +}
>> +
>> +static av_cold int dfpwm_dec_end(struct AVCodecContext *ctx)
>> +{
>> +    return 0;
>> +}
> Remove if not gonna be used and is empty.
>
>> +
>> +static int dfpwm_dec_frame(struct AVCodecContext *ctx, void *data,
>> +    int *got_frame, struct AVPacket *packet)
>> +{
>> +    DFPWMState *state = ctx->priv_data;
>> +    AVFrame *frame = data;
>> +
>> +    frame->format = AV_SAMPLE_FMT_U8;
>> +    frame->nb_samples = packet->size * 8;
>> +    frame->channel_layout = AV_CH_LAYOUT_MONO;
>> +
>> +    av_frame_get_buffer(frame, 0);
> Use ff_get_buffer()
>
> See how libavcodec/pcm.c codecs handles that.
>
>> +
>> +    au_decompress(state, 100, packet->size, frame->data[0], packet->data);
>> +
>> +    // convert from signed to unsigned 8-bit, as DFPWM outputs S8 but
>> FFmpeg needs U8
>> +    for (int i = 0; i < packet->size * 8; i++) frame->data[0][i] =
>> ((int8_t*)frame->data[0])[i] + 128;
> Cant you remove this wrapper, for such trivial code is not helping here.
> Than you can do conversion from S8 to U8 on the fly without need for extra buffer.
>
>> +
>> +    if (got_frame) *got_frame = 1;
>> +    return packet->size;
>> +}
>> +
>> +const AVCodec ff_dfpwm_decoder = {
>> +    .name           = "dfpwm",
>> +    .long_name      = NULL_IF_CONFIG_SMALL("DFPWM1a audio"),
>> +    .type           = AVMEDIA_TYPE_AUDIO,
>> +    .id             = AV_CODEC_ID_DFPWM,
>> +    .priv_data_size = sizeof(DFPWMState),
>> +    .init           = dfpwm_dec_init,
>> +    .close          = dfpwm_dec_end,
>> +    .decode         = dfpwm_dec_frame,
>> +    .capabilities   = 0,
> This flags are not correct, or optimal in any way.
>
>> +    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
>> +};
>> \ No newline at end of file
>> diff --git a/libavcodec/dfpwmenc.c b/libavcodec/dfpwmenc.c
>> new file mode 100644
>> index 0000000..97fc42e
>> --- /dev/null
>> +++ b/libavcodec/dfpwmenc.c
>> @@ -0,0 +1,140 @@
>> +/*
>> + * DFPWM encoder
>> + * Copyright (c) 2022 Jack Bruienne
>> + * Copyright (c) 2012, 2016 Ben "GreaseMonkey" Russell
>> + *
>> + * 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
>> + */
>> +
>> +/**
>> + * @file
>> + * DFPWM1a encoder
>> + */
>> +
>> +#include "libavutil/internal.h"
>> +#include "avcodec.h"
>> +#include "codec_id.h"
>> +#include "internal.h"
>> +
>> +typedef struct {
>> +    int fq, q, s, lt;
>> +} DFPWMState;
>> +
>> +// DFPWM codec from https://github.com/ChenThread/dfpwm/blob/master/1a/
>> +// Licensed in the public domain
>> +
>> +#ifndef CONST_PREC
>> +#define CONST_PREC 10
>> +#endif
>> +
>> +// note, len denotes how many compressed bytes there are (uncompressed
>> bytes / 8).
>> +static void au_compress(DFPWMState *state, int len, uint8_t *outbuf, int8_t
>> *inbuf)
>> +{
>> +    int i, j;
>> +    uint8_t d = 0;
>> +    for (i = 0; i < len; i++) {
>> +        for (j = 0; j < 8; j++) {
>> +            int nq, st, ns;
>> +            // get sample
>> +            int v = *(inbuf++);
>> +            // set bit / target
>> +            int t = (v < state->q || v == -128 ? -128 : 127);
>> +            d >>= 1;
>> +            if(t > 0)
>> +                d |= 0x80;
>> +
>> +            // adjust charge
>> +            nq = state->q + ((state->s * (t-state->q) +
>> (1<<(CONST_PREC-1)))>>CONST_PREC);
>> +            if(nq == state->q && nq != t)
>> +                nq += (t == 127 ? 1 : -1);
>> +            state->q = nq;
>> +
>> +            // adjust strength
>> +            st = (t != state->lt ? 0 : (1<<CONST_PREC)-1);
>> +            ns = state->s;
>> +            if(ns != st)
>> +                ns += (st != 0 ? 1 : -1);
>> +#if CONST_PREC > 8
>> +            if(ns < 1+(1<<(CONST_PREC-8))) ns = 1+(1<<(CONST_PREC-8));
>> +#endif
>> +            state->s = ns;
>> +
>> +            state->lt = t;
>> +
>> +            //fprintf(stderr, "%4i %4i %4i %4i\n", v, *q, *s, t);
>> +            //usleep(10000);
>> +        }
>> +
>> +        // output bits
>> +        *(outbuf++) = d;
>> +    }
>> +}
>> +
>> +static av_cold int dfpwm_enc_init(struct AVCodecContext *ctx)
>> +{
>> +    DFPWMState *state = ctx->priv_data;
>> +
>> +    state->fq = 0;
>> +    state->q = 0;
>> +    state->s = 0;
>> +    state->lt = -128;
>> +
>> +    return 0;
>> +}
>> +
>> +static av_cold int dfpwm_enc_end(struct AVCodecContext *ctx)
>> +{
>> +    return 0;
>> +}
> Please remove functions that do nothing.
>
>> +
>> +static int dfpwm_enc_frame(struct AVCodecContext *ctx, struct AVPacket
>> *packet,
>> +    const struct AVFrame *frame, int *got_packet)
>> +{
>> +    DFPWMState *state = ctx->priv_data;
>> +    int size = frame->nb_samples / 8 + (frame->nb_samples % 8 > 0 ? 1 : 0);
>> +    int8_t *data = av_malloc(size * 8);
>> +
>> +    if (!data) return AVERROR(ENOMEM);
>> +
>> +    if (packet->size < size) av_grow_packet(packet, size - packet->size);
>> +    else if (packet->size > size) av_shrink_packet(packet, size);
>> +
>> +    // make a temporary S8 buffer as DFPWM needs S8 but FFmpeg uses U8
>> +    for (int i = 0; i < frame->nb_samples; i++) data[i] = frame->data[0][i]
>> - 128;
>> +    for (int i = frame->nb_samples; i < size * 8; i++) data[i] = 0;
>> +
>> +    au_compress(state, size, packet->data, data);
>> +    av_free(data);
> Using extra buffer for such trivial code is not optimal solution.
>
>> +
>> +    if (got_packet) *got_packet = 1;
>> +    return 0;
>> +}
>> +
>> +const AVCodec ff_dfpwm_encoder = {
>> +    .name            = "dfpwm",
>> +    .long_name       = NULL_IF_CONFIG_SMALL("DFPWM1a audio"),
>> +    .type            = AVMEDIA_TYPE_AUDIO,
>> +    .id              = AV_CODEC_ID_DFPWM,
>> +    .priv_data_size  = sizeof(DFPWMState),
>> +    .init            = dfpwm_enc_init,
>> +    .close           = dfpwm_enc_end,
>> +    .encode2         = dfpwm_enc_frame,
>> +    .sample_fmts     = (const enum AVSampleFormat[]){AV_SAMPLE_FMT_U8,
>> AV_SAMPLE_FMT_NONE},
>> +    .channel_layouts = (const uint64_t[]){AV_CH_LAYOUT_MONO, 0},
>> +    .capabilities    = AV_CODEC_CAP_VARIABLE_FRAME_SIZE,
>> +    .caps_internal   = FF_CODEC_CAP_INIT_THREADSAFE,
>> +};
>> diff --git a/libavcodec/utils.c b/libavcodec/utils.c
>> index 6f9d90a..066da76 100644
>> --- a/libavcodec/utils.c
>> +++ b/libavcodec/utils.c
>> @@ -577,6 +577,8 @@ enum AVCodecID av_get_pcm_codec(enum AVSampleFormat fmt,
>> int be)
>>   int av_get_bits_per_sample(enum AVCodecID codec_id)
>>   {
>>       switch (codec_id) {
>> +    case AV_CODEC_ID_DFPWM:
>> +        return 1;
>>       case AV_CODEC_ID_ADPCM_SBPRO_2:
>>           return 2;
>>       case AV_CODEC_ID_ADPCM_SBPRO_3:
>> diff --git a/libavcodec/version.h b/libavcodec/version.h
>> index d900503..84f3979 100644
>> --- a/libavcodec/version.h
>> +++ b/libavcodec/version.h
>> @@ -28,7 +28,7 @@
>>   #include "libavutil/version.h"
>>    #define LIBAVCODEC_VERSION_MAJOR  59
>> -#define LIBAVCODEC_VERSION_MINOR  21
>> +#define LIBAVCODEC_VERSION_MINOR  22
>>   #define LIBAVCODEC_VERSION_MICRO 100
>>    #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
>> diff --git a/libavformat/Makefile b/libavformat/Makefile
>> index 6566e40..b89073a 100644
>> --- a/libavformat/Makefile
>> +++ b/libavformat/Makefile
>> @@ -165,6 +165,8 @@ OBJS-$(CONFIG_DAUD_MUXER)                += daudenc.o
>>   OBJS-$(CONFIG_DCSTR_DEMUXER)             += dcstr.o
>>   OBJS-$(CONFIG_DERF_DEMUXER)              += derf.o pcm.o
>>   OBJS-$(CONFIG_DFA_DEMUXER)               += dfa.o
>> +OBJS-$(CONFIG_DFPWM_DEMUXER)             += dfpwmdec.o pcm.o
>> +OBJS-$(CONFIG_DFPWM_MUXER)               += rawenc.o
>>   OBJS-$(CONFIG_DHAV_DEMUXER)              += dhav.o
>>   OBJS-$(CONFIG_DIRAC_DEMUXER)             += diracdec.o rawdec.o
>>   OBJS-$(CONFIG_DIRAC_MUXER)               += rawenc.o
>> diff --git a/libavformat/allformats.c b/libavformat/allformats.c
>> index d066a77..587ad59 100644
>> --- a/libavformat/allformats.c
>> +++ b/libavformat/allformats.c
>> @@ -124,6 +124,8 @@ extern const AVOutputFormat ff_daud_muxer;
>>   extern const AVInputFormat  ff_dcstr_demuxer;
>>   extern const AVInputFormat  ff_derf_demuxer;
>>   extern const AVInputFormat  ff_dfa_demuxer;
>> +extern const AVInputFormat  ff_dfpwm_demuxer;
>> +extern const AVOutputFormat ff_dfpwm_muxer;
>>   extern const AVInputFormat  ff_dhav_demuxer;
>>   extern const AVInputFormat  ff_dirac_demuxer;
>>   extern const AVOutputFormat ff_dirac_muxer;
>> diff --git a/libavformat/dfpwmdec.c b/libavformat/dfpwmdec.c
>> new file mode 100644
>> index 0000000..ad5bfa5
>> --- /dev/null
>> +++ b/libavformat/dfpwmdec.c
>> @@ -0,0 +1,107 @@
>> +/*
>> + * RAW PCM demuxers
>> + * Copyright (c) 2002 Fabrice Bellard
>> + * Copyright (c) 2022 Jack Bruienne
>> + *
>> + * 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/avstring.h"
>> +#include "avformat.h"
>> +#include "internal.h"
>> +#include "pcm.h"
>> +#include "libavutil/log.h"
>> +#include "libavutil/opt.h"
>> +#include "libavutil/avassert.h"
>> +
>> +typedef struct DFPWMAudioDemuxerContext {
>> +    AVClass *class;
>> +    int sample_rate;
>> +} DFPWMAudioDemuxerContext;
>> +
>> +static int dfpwm_read_header(AVFormatContext *s)
>> +{
>> +    DFPWMAudioDemuxerContext *s1 = s->priv_data;
>> +    AVCodecParameters *par;
>> +    AVStream *st;
>> +    uint8_t *mime_type = NULL;
>> +
>> +    st = avformat_new_stream(s, NULL);
>> +    if (!st)
>> +        return AVERROR(ENOMEM);
>> +    par = st->codecpar;
>> +
>> +    par->codec_type  = AVMEDIA_TYPE_AUDIO;
>> +    par->codec_id    = s->iformat->raw_codec_id;
>> +    par->sample_rate = s1->sample_rate;
>> +    par->channels    = 1;
>> +
>> +    av_opt_get(s->pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type);
>> +    if (mime_type && s->iformat->mime_type) {
>> +        int rate = 0;
>> +        const char *options;
>> +        if (av_stristart(mime_type, s->iformat->mime_type, &options)) { /*
>> audio/L16 */
>> +            while (options = strchr(options, ';')) {
>> +                options++;
>> +                if (!rate)
>> +                    sscanf(options, " rate=%d",     &rate);
>> +            }
>> +            if (rate <= 0) {
>> +                av_log(s, AV_LOG_ERROR,
>> +                       "Invalid sample_rate found in mime_type \"%s\"\n",
>> +                       mime_type);
>> +                av_freep(&mime_type);
>> +                return AVERROR_INVALIDDATA;
>> +            }
>> +            par->sample_rate = rate;
>> +        }
>> +    }
>> +    av_freep(&mime_type);
>> +
>> +    par->bits_per_coded_sample = av_get_bits_per_sample(par->codec_id);
>> +
>> +    av_assert0(par->bits_per_coded_sample > 0);
>> +
>> +    par->block_align = 1;
>> +
>> +    avpriv_set_pts_info(st, 64, 1, par->sample_rate);
>> +    return 0;
>> +}
>> +
>> +static const AVOption dfpwm_options[] = {
>> +    { "sample_rate", "", offsetof(DFPWMAudioDemuxerContext, sample_rate),
>> AV_OPT_TYPE_INT, {.i64 = 44100}, 0, INT_MAX, AV_OPT_FLAG_DECODING_PARAM },
>> +    { NULL },
>> +};
>> +static const AVClass dfpwm_demuxer_class = {
>> +    .class_name = "dfpwm demuxer",
>> +    .item_name  = av_default_item_name,
>> +    .option     = dfpwm_options,
>> +    .version    = LIBAVUTIL_VERSION_INT,
>> +};
>> +
>> +const AVInputFormat ff_dfpwm_demuxer = {
>> +    .name           = "dfpwm",
>> +    .long_name      = NULL_IF_CONFIG_SMALL("raw DFPWM1a data"),
>> +    .priv_data_size = sizeof(DFPWMAudioDemuxerContext),
>> +    .read_header    = dfpwm_read_header,
>> +    .read_packet    = ff_pcm_read_packet,
>> +    .read_seek      = ff_pcm_read_seek,
>> +    .flags          = AVFMT_GENERIC_INDEX,
>> +    .extensions     = "dfpwm",
>> +    .raw_codec_id   = AV_CODEC_ID_DFPWM,
>> +    .priv_class     = &dfpwm_demuxer_class,
>> +};
>> \ No newline at end of file
>> diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c
>> index 4bbae77..17b627b 100644
>> --- a/libavformat/rawenc.c
>> +++ b/libavformat/rawenc.c
>> @@ -192,6 +192,19 @@ const AVOutputFormat ff_data_muxer = {
>>   };
>>   #endif
>>   +#if CONFIG_DFPWM_MUXER
>> +const AVOutputFormat ff_dfpwm_muxer = {
>> +    .name              = "dfpwm",
>> +    .long_name         = NULL_IF_CONFIG_SMALL("raw DFPWM1a audio"),
>> +    .extensions        = "dfpwm",
>> +    .audio_codec       = AV_CODEC_ID_DFPWM,
>> +    .video_codec       = AV_CODEC_ID_NONE,
>> +    .init              = force_one_stream,
>> +    .write_packet      = ff_raw_write_packet,
>> +    .flags             = AVFMT_NOTIMESTAMPS,
>> +};
>> +#endif
>> +
>>   #if CONFIG_DIRAC_MUXER
>>   const AVOutputFormat ff_dirac_muxer = {
>>       .name              = "dirac",
>> diff --git a/libavformat/version.h b/libavformat/version.h
>> index 2623457..0f89af4 100644
>> --- a/libavformat/version.h
>> +++ b/libavformat/version.h
>> @@ -32,8 +32,8 @@
>>   // Major bumping may affect Ticket5467, 5421, 5451(compatibility with
>> Chromium)
>>   // Also please add any ticket numbers that you believe might be affected
>> here
>>   #define LIBAVFORMAT_VERSION_MAJOR  59
>> -#define LIBAVFORMAT_VERSION_MINOR  17
>> -#define LIBAVFORMAT_VERSION_MICRO 102
>> +#define LIBAVFORMAT_VERSION_MINOR  18
>> +#define LIBAVFORMAT_VERSION_MICRO 100
>>    #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR,
>> \
>>
>> LIBAVFORMAT_VERSION_MINOR, \
>> -- 
>> 2.35.1
>>
>> _______________________________________________
>> 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".
> _______________________________________________
> 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".
diff mbox series

Patch

diff --git a/Changelog b/Changelog
index 5ad2cef..ec688da 100644
--- a/Changelog
+++ b/Changelog
@@ -4,6 +4,7 @@  releases are sorted from youngest to oldest.
  version 5.1:
  - dialogue enhance audio filter
  - dropped obsolete XvMC hwaccel
+- DFPWM audio encoder/decoder and raw muxer/demuxer
    version 5.0:
diff --git a/MAINTAINERS b/MAINTAINERS
index f33ccbd..931cf4b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -161,6 +161,7 @@  Codecs:
    cscd.c                                Reimar Doeffinger
    cuviddec.c                            Timo Rothenpieler
    dca*                                  foo86
+  dfpwm*                                Jack Bruienne
    dirac*                                Rostislav Pehlivanov
    dnxhd*                                Baptiste Coudurier
    dolby_e*                              foo86
@@ -415,6 +416,7 @@  Muxers/Demuxers:
    dashdec.c                             Steven Liu
    dashenc.c                             Karthick Jeyapal
    daud.c                                Reimar Doeffinger
+  dfpwmdec.c                            Jack Bruienne
    dss.c                                 Oleksij Rempel
    dtsdec.c                              foo86
    dtshddec.c                            Paul B Mahol
diff --git a/doc/general_contents.texi b/doc/general_contents.texi
index df1692c..fcd9da1 100644
--- a/doc/general_contents.texi
+++ b/doc/general_contents.texi
@@ -578,6 +578,7 @@  library:
  @item raw aptX                  @tab X @tab X
  @item raw aptX HD               @tab X @tab X
  @item raw Chinese AVS video     @tab X @tab X
+@item raw DFPWM                 @tab X @tab X
  @item raw Dirac                 @tab X @tab X
  @item raw DNxHD                 @tab X @tab X
  @item raw DTS                   @tab X @tab X
@@ -1194,6 +1195,7 @@  following image formats are supported:
  @item CRI HCA                @tab     @tab X
  @item Delphine Software International CIN audio  @tab     @tab  X
      @tab Codec used in Delphine Software International games.
+@item DFPWM                  @tab  X  @tab  X
  @item Digital Speech Standard - Standard Play mode (DSS SP) @tab 
@tab  X
  @item Discworld II BMV Audio @tab     @tab  X
  @item COOK                   @tab     @tab  X
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 6076b4a..7474220 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -289,6 +289,8 @@  OBJS-$(CONFIG_DERF_DPCM_DECODER)       += dpcm.o
  OBJS-$(CONFIG_DIRAC_DECODER)           += diracdec.o dirac.o 
diracdsp.o diractab.o \
                                            dirac_arith.o dirac_dwt.o 
dirac_vlc.o
  OBJS-$(CONFIG_DFA_DECODER)             += dfa.o
+OBJS-$(CONFIG_DFPWM_DECODER)           += dfpwmdec.o
+OBJS-$(CONFIG_DFPWM_ENCODER)           += dfpwmenc.o
  OBJS-$(CONFIG_DNXHD_DECODER)           += dnxhddec.o dnxhddata.o
  OBJS-$(CONFIG_DNXHD_ENCODER)           += dnxhdenc.o dnxhddata.o
  OBJS-$(CONFIG_DOLBY_E_DECODER)         += dolby_e.o dolby_e_parse.o 
kbdwin.o
diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c
index d1e1019..c3a0c26 100644
--- a/libavcodec/allcodecs.c
+++ b/libavcodec/allcodecs.c
@@ -437,6 +437,8 @@  extern const AVCodec ff_bmv_audio_decoder;
  extern const AVCodec ff_cook_decoder;
  extern const AVCodec ff_dca_encoder;
  extern const AVCodec ff_dca_decoder;
+extern const AVCodec ff_dfpwm_encoder;
+extern const AVCodec ff_dfpwm_decoder;
  extern const AVCodec ff_dolby_e_decoder;
  extern const AVCodec ff_dsd_lsbf_decoder;
  extern const AVCodec ff_dsd_msbf_decoder;
diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c
index 725c687..87ca591 100644
--- a/libavcodec/codec_desc.c
+++ b/libavcodec/codec_desc.c
@@ -3237,6 +3237,13 @@  static const AVCodecDescriptor 
codec_descriptors[] = {
          .long_name = NULL_IF_CONFIG_SMALL("MSN Siren"),
          .props     = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY,
      },
+    {
+        .id        = AV_CODEC_ID_DFPWM,
+        .type      = AVMEDIA_TYPE_AUDIO,
+        .name      = "dfpwm",
+        .long_name = NULL_IF_CONFIG_SMALL("DFPWM1a audio"),
+        .props     = AV_CODEC_PROP_LOSSY,
+    },
       /* subtitle codecs */
      {
diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h
index ab265ec..3ffb9bd 100644
--- a/libavcodec/codec_id.h
+++ b/libavcodec/codec_id.h
@@ -516,6 +516,7 @@  enum AVCodecID {
      AV_CODEC_ID_HCA,
      AV_CODEC_ID_FASTAUDIO,
      AV_CODEC_ID_MSNSIREN,
+    AV_CODEC_ID_DFPWM,
       /* subtitle codecs */
      AV_CODEC_ID_FIRST_SUBTITLE = 0x17000,          ///< A dummy ID 
pointing at the start of subtitle codecs.
diff --git a/libavcodec/dfpwmdec.c b/libavcodec/dfpwmdec.c
new file mode 100644
index 0000000..9f12841
--- /dev/null
+++ b/libavcodec/dfpwmdec.c
@@ -0,0 +1,138 @@ 
+/*
+ * DFPWM decoder
+ * Copyright (c) 2022 Jack Bruienne
+ * Copyright (c) 2012, 2016 Ben "GreaseMonkey" Russell
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * DFPWM1a decoder
+ */
+
+#include "libavutil/internal.h"
+#include "avcodec.h"
+#include "codec_id.h"
+#include "internal.h"
+
+typedef struct {
+    int fq, q, s, lt;
+} DFPWMState;
+
+// DFPWM codec from https://github.com/ChenThread/dfpwm/blob/master/1a/
+// Licensed in the public domain
+
+#ifndef CONST_PREC
+#define CONST_PREC 10
+#endif
+
+static void au_decompress(DFPWMState *state, int fs, int len, int8_t 
*outbuf, uint8_t *inbuf)
+{
+    int i, j;
+    uint8_t d;
+    for (i = 0; i < len; i++) {
+        // get bits
+        d = *(inbuf++);
+        for (j = 0; j < 8; j++) {
+            int nq, lq, st, ns, ov;
+            // set target
+            int t = ((d&1) ? 127 : -128);
+            d >>= 1;
+
+            // adjust charge
+            nq = state->q + ((state->s * (t-state->q) + 
(1<<(CONST_PREC-1)))>>CONST_PREC);
+            if(nq == state->q && nq != t)
+                state->q += (t == 127 ? 1 : -1);
+            lq = state->q;
+            state->q = nq;
+
+            // adjust strength
+            st = (t != state->lt ? 0 : (1<<CONST_PREC)-1);
+            ns = state->s;
+            if(ns != st)
+                ns += (st != 0 ? 1 : -1);
+#if CONST_PREC > 8
+            if(ns < 1+(1<<(CONST_PREC-8))) ns = 1+(1<<(CONST_PREC-8));
+#endif
+            state->s = ns;
+
+            // FILTER: perform antijerk
+            ov = (t != state->lt ? (nq+lq)>>1 : nq);
+
+            // FILTER: perform LPF
+            state->fq += ((fs*(ov-state->fq) + 0x80)>>8);
+            ov = state->fq;
+
+            // output sample
+            *(outbuf++) = ov;
+
+            state->lt = t;
+        }
+    }
+}
+
+static av_cold int dfpwm_dec_init(struct AVCodecContext *ctx)
+{
+    DFPWMState *state = ctx->priv_data;
+
+    state->fq = 0;
+    state->q = 0;
+    state->s = 0;
+    state->lt = -128;
+
+    return 0;
+}
+
+static av_cold int dfpwm_dec_end(struct AVCodecContext *ctx)
+{
+    return 0;
+}
+
+static int dfpwm_dec_frame(struct AVCodecContext *ctx, void *data,
+    int *got_frame, struct AVPacket *packet)
+{
+    DFPWMState *state = ctx->priv_data;
+    AVFrame *frame = data;
+
+    frame->format = AV_SAMPLE_FMT_U8;
+    frame->nb_samples = packet->size * 8;
+    frame->channel_layout = AV_CH_LAYOUT_MONO;
+
+    av_frame_get_buffer(frame, 0);
+
+    au_decompress(state, 100, packet->size, frame->data[0], packet->data);
+
+    // convert from signed to unsigned 8-bit, as DFPWM outputs S8 but 
FFmpeg needs U8
+    for (int i = 0; i < packet->size * 8; i++) frame->data[0][i] = 
((int8_t*)frame->data[0])[i] + 128;
+
+    if (got_frame) *got_frame = 1;
+    return packet->size;
+}
+
+const AVCodec ff_dfpwm_decoder = {
+    .name           = "dfpwm",
+    .long_name      = NULL_IF_CONFIG_SMALL("DFPWM1a audio"),
+    .type           = AVMEDIA_TYPE_AUDIO,
+    .id             = AV_CODEC_ID_DFPWM,
+    .priv_data_size = sizeof(DFPWMState),
+    .init           = dfpwm_dec_init,
+    .close          = dfpwm_dec_end,
+    .decode         = dfpwm_dec_frame,
+    .capabilities   = 0,
+    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
+};
\ No newline at end of file
diff --git a/libavcodec/dfpwmenc.c b/libavcodec/dfpwmenc.c
new file mode 100644
index 0000000..97fc42e
--- /dev/null
+++ b/libavcodec/dfpwmenc.c
@@ -0,0 +1,140 @@ 
+/*
+ * DFPWM encoder
+ * Copyright (c) 2022 Jack Bruienne
+ * Copyright (c) 2012, 2016 Ben "GreaseMonkey" Russell
+ *
+ * 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
+ */
+
+/**
+ * @file
+ * DFPWM1a encoder
+ */
+
+#include "libavutil/internal.h"
+#include "avcodec.h"
+#include "codec_id.h"
+#include "internal.h"
+
+typedef struct {
+    int fq, q, s, lt;
+} DFPWMState;
+
+// DFPWM codec from https://github.com/ChenThread/dfpwm/blob/master/1a/
+// Licensed in the public domain
+
+#ifndef CONST_PREC
+#define CONST_PREC 10
+#endif
+
+// note, len denotes how many compressed bytes there are (uncompressed 
bytes / 8).
+static void au_compress(DFPWMState *state, int len, uint8_t *outbuf, 
int8_t *inbuf)
+{
+    int i, j;
+    uint8_t d = 0;
+    for (i = 0; i < len; i++) {
+        for (j = 0; j < 8; j++) {
+            int nq, st, ns;
+            // get sample
+            int v = *(inbuf++);
+            // set bit / target
+            int t = (v < state->q || v == -128 ? -128 : 127);
+            d >>= 1;
+            if(t > 0)
+                d |= 0x80;
+
+            // adjust charge
+            nq = state->q + ((state->s * (t-state->q) + 
(1<<(CONST_PREC-1)))>>CONST_PREC);
+            if(nq == state->q && nq != t)
+                nq += (t == 127 ? 1 : -1);
+            state->q = nq;
+
+            // adjust strength
+            st = (t != state->lt ? 0 : (1<<CONST_PREC)-1);
+            ns = state->s;
+            if(ns != st)
+                ns += (st != 0 ? 1 : -1);
+#if CONST_PREC > 8
+            if(ns < 1+(1<<(CONST_PREC-8))) ns = 1+(1<<(CONST_PREC-8));
+#endif
+            state->s = ns;
+
+            state->lt = t;
+
+            //fprintf(stderr, "%4i %4i %4i %4i\n", v, *q, *s, t);
+            //usleep(10000);
+        }
+
+        // output bits
+        *(outbuf++) = d;
+    }
+}
+
+static av_cold int dfpwm_enc_init(struct AVCodecContext *ctx)
+{
+    DFPWMState *state = ctx->priv_data;
+
+    state->fq = 0;
+    state->q = 0;
+    state->s = 0;
+    state->lt = -128;
+
+    return 0;
+}
+
+static av_cold int dfpwm_enc_end(struct AVCodecContext *ctx)
+{
+    return 0;
+}
+
+static int dfpwm_enc_frame(struct AVCodecContext *ctx, struct AVPacket 
*packet,
+    const struct AVFrame *frame, int *got_packet)
+{
+    DFPWMState *state = ctx->priv_data;
+    int size = frame->nb_samples / 8 + (frame->nb_samples % 8 > 0 ? 1 : 0);
+    int8_t *data = av_malloc(size * 8);
+
+    if (!data) return AVERROR(ENOMEM);
+
+    if (packet->size < size) av_grow_packet(packet, size - packet->size);
+    else if (packet->size > size) av_shrink_packet(packet, size);
+
+    // make a temporary S8 buffer as DFPWM needs S8 but FFmpeg uses U8
+    for (int i = 0; i < frame->nb_samples; i++) data[i] = 
frame->data[0][i] - 128;
+    for (int i = frame->nb_samples; i < size * 8; i++) data[i] = 0;
+
+    au_compress(state, size, packet->data, data);
+    av_free(data);
+
+    if (got_packet) *got_packet = 1;
+    return 0;
+}
+
+const AVCodec ff_dfpwm_encoder = {
+    .name            = "dfpwm",
+    .long_name       = NULL_IF_CONFIG_SMALL("DFPWM1a audio"),
+    .type            = AVMEDIA_TYPE_AUDIO,
+    .id              = AV_CODEC_ID_DFPWM,
+    .priv_data_size  = sizeof(DFPWMState),
+    .init            = dfpwm_enc_init,
+    .close           = dfpwm_enc_end,
+    .encode2         = dfpwm_enc_frame,
+    .sample_fmts     = (const enum AVSampleFormat[]){AV_SAMPLE_FMT_U8, 
AV_SAMPLE_FMT_NONE},
+    .channel_layouts = (const uint64_t[]){AV_CH_LAYOUT_MONO, 0},
+    .capabilities    = AV_CODEC_CAP_VARIABLE_FRAME_SIZE,
+    .caps_internal   = FF_CODEC_CAP_INIT_THREADSAFE,
+};
diff --git a/libavcodec/utils.c b/libavcodec/utils.c
index 6f9d90a..066da76 100644
--- a/libavcodec/utils.c
+++ b/libavcodec/utils.c
@@ -577,6 +577,8 @@  enum AVCodecID av_get_pcm_codec(enum AVSampleFormat 
fmt, int be)
  int av_get_bits_per_sample(enum AVCodecID codec_id)
  {
      switch (codec_id) {
+    case AV_CODEC_ID_DFPWM:
+        return 1;
      case AV_CODEC_ID_ADPCM_SBPRO_2:
          return 2;
      case AV_CODEC_ID_ADPCM_SBPRO_3:
diff --git a/libavcodec/version.h b/libavcodec/version.h
index d900503..84f3979 100644
--- a/libavcodec/version.h
+++ b/libavcodec/version.h
@@ -28,7 +28,7 @@ 
  #include "libavutil/version.h"
   #define LIBAVCODEC_VERSION_MAJOR  59
-#define LIBAVCODEC_VERSION_MINOR  21
+#define LIBAVCODEC_VERSION_MINOR  22
  #define LIBAVCODEC_VERSION_MICRO 100
   #define LIBAVCODEC_VERSION_INT 
AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \
diff --git a/libavformat/Makefile b/libavformat/Makefile
index 6566e40..b89073a 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -165,6 +165,8 @@  OBJS-$(CONFIG_DAUD_MUXER)                += daudenc.o
  OBJS-$(CONFIG_DCSTR_DEMUXER)             += dcstr.o
  OBJS-$(CONFIG_DERF_DEMUXER)              += derf.o pcm.o
  OBJS-$(CONFIG_DFA_DEMUXER)               += dfa.o
+OBJS-$(CONFIG_DFPWM_DEMUXER)             += dfpwmdec.o pcm.o
+OBJS-$(CONFIG_DFPWM_MUXER)               += rawenc.o
  OBJS-$(CONFIG_DHAV_DEMUXER)              += dhav.o
  OBJS-$(CONFIG_DIRAC_DEMUXER)             += diracdec.o rawdec.o
  OBJS-$(CONFIG_DIRAC_MUXER)               += rawenc.o
diff --git a/libavformat/allformats.c b/libavformat/allformats.c
index d066a77..587ad59 100644
--- a/libavformat/allformats.c
+++ b/libavformat/allformats.c
@@ -124,6 +124,8 @@  extern const AVOutputFormat ff_daud_muxer;
  extern const AVInputFormat  ff_dcstr_demuxer;
  extern const AVInputFormat  ff_derf_demuxer;
  extern const AVInputFormat  ff_dfa_demuxer;
+extern const AVInputFormat  ff_dfpwm_demuxer;
+extern const AVOutputFormat ff_dfpwm_muxer;
  extern const AVInputFormat  ff_dhav_demuxer;
  extern const AVInputFormat  ff_dirac_demuxer;
  extern const AVOutputFormat ff_dirac_muxer;
diff --git a/libavformat/dfpwmdec.c b/libavformat/dfpwmdec.c
new file mode 100644
index 0000000..ad5bfa5
--- /dev/null
+++ b/libavformat/dfpwmdec.c
@@ -0,0 +1,107 @@ 
+/*
+ * RAW PCM demuxers
+ * Copyright (c) 2002 Fabrice Bellard
+ * Copyright (c) 2022 Jack Bruienne
+ *
+ * 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/avstring.h"
+#include "avformat.h"
+#include "internal.h"
+#include "pcm.h"
+#include "libavutil/log.h"
+#include "libavutil/opt.h"
+#include "libavutil/avassert.h"
+
+typedef struct DFPWMAudioDemuxerContext {
+    AVClass *class;
+    int sample_rate;
+} DFPWMAudioDemuxerContext;
+
+static int dfpwm_read_header(AVFormatContext *s)
+{
+    DFPWMAudioDemuxerContext *s1 = s->priv_data;
+    AVCodecParameters *par;
+    AVStream *st;
+    uint8_t *mime_type = NULL;
+
+    st = avformat_new_stream(s, NULL);
+    if (!st)
+        return AVERROR(ENOMEM);
+    par = st->codecpar;
+
+    par->codec_type  = AVMEDIA_TYPE_AUDIO;
+    par->codec_id    = s->iformat->raw_codec_id;
+    par->sample_rate = s1->sample_rate;
+    par->channels    = 1;
+
+    av_opt_get(s->pb, "mime_type", AV_OPT_SEARCH_CHILDREN, &mime_type);
+    if (mime_type && s->iformat->mime_type) {
+        int rate = 0;
+        const char *options;
+        if (av_stristart(mime_type, s->iformat->mime_type, &options)) { 
/* audio/L16 */
+            while (options = strchr(options, ';')) {
+                options++;
+                if (!rate)
+                    sscanf(options, " rate=%d",     &rate);
+            }
+            if (rate <= 0) {
+                av_log(s, AV_LOG_ERROR,
+                       "Invalid sample_rate found in mime_type \"%s\"\n",
+                       mime_type);
+                av_freep(&mime_type);
+                return AVERROR_INVALIDDATA;
+            }
+            par->sample_rate = rate;
+        }
+    }
+    av_freep(&mime_type);
+
+    par->bits_per_coded_sample = av_get_bits_per_sample(par->codec_id);
+
+    av_assert0(par->bits_per_coded_sample > 0);
+
+    par->block_align = 1;
+
+    avpriv_set_pts_info(st, 64, 1, par->sample_rate);
+    return 0;
+}
+
+static const AVOption dfpwm_options[] = {
+    { "sample_rate", "", offsetof(DFPWMAudioDemuxerContext, 
sample_rate), AV_OPT_TYPE_INT, {.i64 = 44100}, 0, INT_MAX, 
AV_OPT_FLAG_DECODING_PARAM },
+    { NULL },
+};
+static const AVClass dfpwm_demuxer_class = {
+    .class_name = "dfpwm demuxer",
+    .item_name  = av_default_item_name,
+    .option     = dfpwm_options,
+    .version    = LIBAVUTIL_VERSION_INT,
+};
+
+const AVInputFormat ff_dfpwm_demuxer = {
+    .name           = "dfpwm",
+    .long_name      = NULL_IF_CONFIG_SMALL("raw DFPWM1a data"),
+    .priv_data_size = sizeof(DFPWMAudioDemuxerContext),
+    .read_header    = dfpwm_read_header,
+    .read_packet    = ff_pcm_read_packet,
+    .read_seek      = ff_pcm_read_seek,
+    .flags          = AVFMT_GENERIC_INDEX,
+    .extensions     = "dfpwm",
+    .raw_codec_id   = AV_CODEC_ID_DFPWM,
+    .priv_class     = &dfpwm_demuxer_class,
+};
\ No newline at end of file
diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c
index 4bbae77..17b627b 100644
--- a/libavformat/rawenc.c
+++ b/libavformat/rawenc.c
@@ -192,6 +192,19 @@  const AVOutputFormat ff_data_muxer = {
  };
  #endif
  +#if CONFIG_DFPWM_MUXER
+const AVOutputFormat ff_dfpwm_muxer = {
+    .name              = "dfpwm",
+    .long_name         = NULL_IF_CONFIG_SMALL("raw DFPWM1a audio"),
+    .extensions        = "dfpwm",
+    .audio_codec       = AV_CODEC_ID_DFPWM,
+    .video_codec       = AV_CODEC_ID_NONE,
+    .init              = force_one_stream,
+    .write_packet      = ff_raw_write_packet,
+    .flags             = AVFMT_NOTIMESTAMPS,
+};
+#endif
+
  #if CONFIG_DIRAC_MUXER
  const AVOutputFormat ff_dirac_muxer = {
      .name              = "dirac",
diff --git a/libavformat/version.h b/libavformat/version.h
index 2623457..0f89af4 100644
--- a/libavformat/version.h
+++ b/libavformat/version.h
@@ -32,8 +32,8 @@ 
  // Major bumping may affect Ticket5467, 5421, 5451(compatibility with 
Chromium)
  // Also please add any ticket numbers that you believe might be 
affected here
  #define LIBAVFORMAT_VERSION_MAJOR  59
-#define LIBAVFORMAT_VERSION_MINOR  17
-#define LIBAVFORMAT_VERSION_MICRO 102
+#define LIBAVFORMAT_VERSION_MINOR  18
+#define LIBAVFORMAT_VERSION_MICRO 100
   #define LIBAVFORMAT_VERSION_INT