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 |
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 |
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".
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 --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