From: Paul B Mahol <onemda@gmail.com> To: FFmpeg development discussions and patches <ffmpeg-devel@ffmpeg.org> Subject: [FFmpeg-devel] [PATCH] A-pac demuxer and decoder Date: Wed, 21 Sep 2022 09:23:33 +0200 Message-ID: <CAPYw7P6arVSmzk61pJ9U0NNQjCvbz_O64ZCfs_W82BHvDRdDUA@mail.gmail.com> (raw) [-- Attachment #1: Type: text/plain, Size: 18 bytes --] Patches attached. [-- Attachment #2: 0001-avcodec-add-APAC-decoder.patch --] [-- Type: text/x-patch, Size: 11219 bytes --] From 751526c456d09bd22a6316196219d79876ae74f1 Mon Sep 17 00:00:00 2001 From: Paul B Mahol <onemda@gmail.com> Date: Mon, 19 Sep 2022 22:14:05 +0200 Subject: [PATCH 1/2] avcodec: add APAC decoder Signed-off-by: Paul B Mahol <onemda@gmail.com> --- libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/apac.c | 269 ++++++++++++++++++++++++++++++++++++++++ libavcodec/codec_desc.c | 7 ++ libavcodec/codec_id.h | 1 + 5 files changed, 279 insertions(+) create mode 100644 libavcodec/apac.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index c836252664..b9aa6efaac 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -214,6 +214,7 @@ OBJS-$(CONFIG_AMRWB_DECODER) += amrwbdec.o celp_filters.o \ OBJS-$(CONFIG_AMV_ENCODER) += mjpegenc.o mjpegenc_common.o OBJS-$(CONFIG_ANM_DECODER) += anm.o OBJS-$(CONFIG_ANSI_DECODER) += ansi.o cga_data.o +OBJS-$(CONFIG_APAC_DECODER) += apac.o OBJS-$(CONFIG_APE_DECODER) += apedec.o OBJS-$(CONFIG_APTX_DECODER) += aptxdec.o aptx.o OBJS-$(CONFIG_APTX_ENCODER) += aptxenc.o aptx.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 447225e26b..fc88e25fda 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -432,6 +432,7 @@ extern const FFCodec ff_alac_decoder; extern const FFCodec ff_als_decoder; extern const FFCodec ff_amrnb_decoder; extern const FFCodec ff_amrwb_decoder; +extern const FFCodec ff_apac_decoder; extern const FFCodec ff_ape_decoder; extern const FFCodec ff_aptx_encoder; extern const FFCodec ff_aptx_decoder; diff --git a/libavcodec/apac.c b/libavcodec/apac.c new file mode 100644 index 0000000000..2dc9aacd44 --- /dev/null +++ b/libavcodec/apac.c @@ -0,0 +1,269 @@ +/* + * APAC audio decoder + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libavutil/audio_fifo.h" +#include "libavutil/internal.h" +#include "libavutil/intreadwrite.h" +#include "avcodec.h" +#include "codec_internal.h" +#include "decode.h" +#include "get_bits.h" + +typedef struct ChContext { + int have_code; + int last_sample; + int last_delta; + int bit_length; + int block_length; + uint8_t block[32 * 2]; + AVAudioFifo *samples; +} ChContext; + +typedef struct APACContext { + GetBitContext gb; + int skip; + + int cur_ch; + ChContext ch[2]; + + uint8_t *bitstream; + int64_t max_framesize; + int bitstream_size; + int bitstream_index; +} APACContext; + +static av_cold int apac_close(AVCodecContext *avctx) +{ + APACContext *s = avctx->priv_data; + + av_freep(&s->bitstream); + s->bitstream_size = 0; + + for (int ch = 0; ch < avctx->ch_layout.nb_channels; ch++) { + ChContext *c = &s->ch[ch]; + + av_audio_fifo_free(c->samples); + } + + return 0; +} + +static av_cold int apac_init(AVCodecContext *avctx) +{ + APACContext *s = avctx->priv_data; + + if (avctx->bits_per_coded_sample > 8) + avctx->sample_fmt = AV_SAMPLE_FMT_S16P; + else + avctx->sample_fmt = AV_SAMPLE_FMT_U8P; + + if (avctx->ch_layout.nb_channels < 1 || + avctx->ch_layout.nb_channels > 2) + return AVERROR_INVALIDDATA; + + for (int ch = 0; ch < avctx->ch_layout.nb_channels; ch++) { + ChContext *c = &s->ch[ch]; + + c->bit_length = avctx->bits_per_coded_sample; + c->block_length = 8; + c->have_code = 0; + c->samples = av_audio_fifo_alloc(avctx->sample_fmt, 1, 1024); + if (!c->samples) + return AVERROR(ENOMEM); + } + + s->max_framesize = 1024; + s->bitstream = av_realloc_f(s->bitstream, s->max_framesize + AV_INPUT_BUFFER_PADDING_SIZE, sizeof(*s->bitstream)); + if (!s->bitstream) + return AVERROR(ENOMEM); + + return 0; +} + +static int get_code(ChContext *c, GetBitContext *gb) +{ + if (get_bits1(gb)) { + int code = get_bits(gb, 2); + + switch (code) { + case 0: + c->bit_length--; + break; + case 1: + c->bit_length++; + break; + case 2: + c->bit_length = get_bits(gb, 5); + break; + case 3: + c->block_length = get_bits(gb, 4); + return 1; + } + } + + return 0; +} + +static int apac_decode(AVCodecContext *avctx, AVFrame *frame, + int *got_frame_ptr, AVPacket *pkt) +{ + APACContext *s = avctx->priv_data; + GetBitContext *gb = &s->gb; + int ret, n, buf_size, input_buf_size; + const uint8_t *buf; + int nb_samples; + + if (!pkt->size && s->bitstream_size <= 0) { + *got_frame_ptr = 0; + return 0; + } + + buf_size = pkt->size; + input_buf_size = buf_size; + + if (s->bitstream_index > 0 && s->bitstream_size > 0) { + memmove(s->bitstream, &s->bitstream[s->bitstream_index], s->bitstream_size); + s->bitstream_index = 0; + } + + if (s->bitstream_index + s->bitstream_size + buf_size > s->max_framesize) { + s->bitstream = av_realloc_f(s->bitstream, s->bitstream_index + + s->bitstream_size + + buf_size + AV_INPUT_BUFFER_PADDING_SIZE, + sizeof(*s->bitstream)); + if (!s->bitstream) + return AVERROR(ENOMEM); + s->max_framesize = s->bitstream_index + s->bitstream_size + buf_size; + } + if (pkt->data) + memcpy(&s->bitstream[s->bitstream_index + s->bitstream_size], pkt->data, buf_size); + buf = &s->bitstream[s->bitstream_index]; + buf_size += s->bitstream_size; + s->bitstream_size = buf_size; + + frame->nb_samples = s->bitstream_size * 16 * 8; + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) + return ret; + + if ((ret = init_get_bits8(gb, buf, buf_size)) < 0) + return ret; + + skip_bits(gb, s->skip); + s->skip = 0; + + while (get_bits_left(gb) > 0) { + for (int ch = s->cur_ch; ch < avctx->ch_layout.nb_channels; ch++) { + ChContext *c = &s->ch[ch]; + int16_t *dst16 = (int16_t *)c->block; + uint8_t *dst8 = (uint8_t *)c->block; + void *samples[4]; + + samples[0] = &c->block[0]; + if (get_bits_left(gb) < 16 && pkt->size) { + s->cur_ch = ch; + goto end; + } + + if (!c->have_code && get_code(c, gb)) + get_code(c, gb); + c->have_code = 0; + + if (c->block_length <= 0) + continue; + + if (c->bit_length < 0 || + c->bit_length > 17) { + c->bit_length = avctx->bits_per_coded_sample; + return AVERROR_INVALIDDATA; + } + + if (get_bits_left(gb) < c->block_length * c->bit_length && pkt->size) { + c->have_code = 1; + s->cur_ch = ch; + goto end; + } + + for (int i = 0; i < c->block_length; i++) { + int val = get_bits_long(gb, c->bit_length); + int delta = (val & 1) ? ~(val >> 1) : (val >> 1); + int sample; + + delta += c->last_delta; + sample = c->last_sample + delta; + c->last_delta = delta; + c->last_sample = sample; + + switch (avctx->sample_fmt) { + case AV_SAMPLE_FMT_S16P: + dst16[i] = sample; + break; + case AV_SAMPLE_FMT_U8P: + dst8[i] = sample; + break; + } + } + + av_audio_fifo_write(c->samples, samples, c->block_length); + } + + s->cur_ch = 0; + } +end: + nb_samples = frame->nb_samples; + for (int ch = 0; ch < avctx->ch_layout.nb_channels; ch++) + nb_samples = FFMIN(av_audio_fifo_size(s->ch[ch].samples), nb_samples); + + frame->nb_samples = nb_samples; + for (int ch = 0; ch < avctx->ch_layout.nb_channels; ch++) { + void *samples[1] = { frame->extended_data[ch] }; + av_audio_fifo_read(s->ch[ch].samples, samples, nb_samples); + } + + s->skip = get_bits_count(gb) - 8 * (get_bits_count(gb) / 8); + n = get_bits_count(gb) / 8; + + if (nb_samples > 0 || pkt->size) + *got_frame_ptr = 1; + + if (s->bitstream_size > 0) { + s->bitstream_index += n; + s->bitstream_size -= n; + return input_buf_size; + } + return n; +} + +const FFCodec ff_apac_decoder = { + .p.name = "apac", + CODEC_LONG_NAME("Marian's A-pac audio"), + .p.type = AVMEDIA_TYPE_AUDIO, + .p.id = AV_CODEC_ID_APAC, + .priv_data_size = sizeof(APACContext), + .init = apac_init, + FF_CODEC_DECODE_CB(apac_decode), + .close = apac_close, + .p.capabilities = AV_CODEC_CAP_DELAY | + AV_CODEC_CAP_DR1 | + AV_CODEC_CAP_SUBFRAMES, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, + .p.sample_fmts = (const enum AVSampleFormat[]) { AV_SAMPLE_FMT_U8P, + AV_SAMPLE_FMT_S16P, + AV_SAMPLE_FMT_NONE }, +}; diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 648c518b3c..e8e1529401 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -3304,6 +3304,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("Micronas SC-4 Audio"), .props = AV_CODEC_PROP_LOSSY | AV_CODEC_PROP_INTRA_ONLY, }, + { + .id = AV_CODEC_ID_APAC, + .type = AVMEDIA_TYPE_AUDIO, + .name = "apac", + .long_name = NULL_IF_CONFIG_SMALL("Marian's A-pac audio"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSLESS, + }, /* subtitle codecs */ { diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index bc8226ff07..9c01ea9750 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -529,6 +529,7 @@ enum AVCodecID { AV_CODEC_ID_DFPWM, AV_CODEC_ID_BONK, AV_CODEC_ID_MISC4, + AV_CODEC_ID_APAC, /* subtitle codecs */ AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs. -- 2.37.2 [-- Attachment #3: 0002-avformat-add-APAC-demuxer.patch --] [-- Type: text/x-patch, Size: 4762 bytes --] From d2d7ed7a0539424fe3dd72fdc68a95840d54123b Mon Sep 17 00:00:00 2001 From: Paul B Mahol <onemda@gmail.com> Date: Mon, 19 Sep 2022 22:05:20 +0200 Subject: [PATCH 2/2] avformat: add APAC demuxer Signed-off-by: Paul B Mahol <onemda@gmail.com> --- libavformat/Makefile | 1 + libavformat/allformats.c | 1 + libavformat/apac.c | 85 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 libavformat/apac.c diff --git a/libavformat/Makefile b/libavformat/Makefile index 19deb33c2e..d669dc8f44 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -101,6 +101,7 @@ OBJS-$(CONFIG_AMRNB_DEMUXER) += amr.o rawdec.o OBJS-$(CONFIG_AMRWB_DEMUXER) += amr.o rawdec.o OBJS-$(CONFIG_AMV_MUXER) += amvenc.o OBJS-$(CONFIG_ANM_DEMUXER) += anm.o +OBJS-$(CONFIG_APAC_DEMUXER) += apac.o rawdec.o OBJS-$(CONFIG_APC_DEMUXER) += apc.o OBJS-$(CONFIG_APE_DEMUXER) += ape.o apetag.o img2.o OBJS-$(CONFIG_APM_DEMUXER) += apm.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index a545b5ff45..47c419a009 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -52,6 +52,7 @@ extern const AVInputFormat ff_amrnb_demuxer; extern const AVInputFormat ff_amrwb_demuxer; extern const AVOutputFormat ff_amv_muxer; extern const AVInputFormat ff_anm_demuxer; +extern const AVInputFormat ff_apac_demuxer; extern const AVInputFormat ff_apc_demuxer; extern const AVInputFormat ff_ape_demuxer; extern const AVInputFormat ff_apm_demuxer; diff --git a/libavformat/apac.c b/libavformat/apac.c new file mode 100644 index 0000000000..4d484221fe --- /dev/null +++ b/libavformat/apac.c @@ -0,0 +1,85 @@ +/* + * APAC demuxer + * Copyright (c) 2022 Paul B Mahol + * + * 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/intreadwrite.h" +#include "avformat.h" +#include "demux.h" +#include "internal.h" +#include "rawdec.h" + +static int apac_probe(const AVProbeData *p) +{ + if (AV_RB32(p->buf) == MKBETAG('A','P','A','C') && + AV_RB32(p->buf + 8) == MKBETAG('P','R','O','F') && + AV_RB32(p->buf + 12) == MKBETAG('N','A','D',' ')) + return AVPROBE_SCORE_MAX; + + return 0; +} + +static int apac_read_header(AVFormatContext *s) +{ + AVIOContext *pb = s->pb; + uint32_t chunk_size; + AVStream *st; + int64_t pos; + + avio_skip(pb, 16); + chunk_size = avio_rl32(pb); + avio_skip(pb, chunk_size); + if (avio_rb32(pb) != MKBETAG('P','F','M','T')) + return AVERROR_INVALIDDATA; + chunk_size = avio_rl32(pb); + pos = avio_tell(pb); + avio_skip(pb, 2); + st = avformat_new_stream(s, NULL); + if (!st) + return AVERROR(ENOMEM); + st->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + st->codecpar->codec_id = AV_CODEC_ID_APAC; + st->codecpar->ch_layout.nb_channels = avio_rl16(pb); + st->codecpar->sample_rate = avio_rl32(pb); + if (st->codecpar->ch_layout.nb_channels <= 0 || + st->codecpar->sample_rate <= 0) + return AVERROR_INVALIDDATA; + avio_skip(pb, 2); + st->codecpar->bits_per_coded_sample = avio_rl16(pb); + avpriv_set_pts_info(st, 64, 1, st->codecpar->sample_rate); + avio_skip(pb, (chunk_size + pos) - avio_tell(pb) + (chunk_size & 1)); + if (avio_rb32(pb) != MKBETAG('P','A','D',' ')) + return AVERROR_INVALIDDATA; + avio_skip(pb, 4); + + return 0; +} + +const AVInputFormat ff_apac_demuxer = { + .name = "apac", + .long_name = NULL_IF_CONFIG_SMALL("raw APAC"), + .read_probe = apac_probe, + .read_header = apac_read_header, + .read_packet = ff_raw_read_partial_packet, + .extensions = "apc", + .flags = AVFMT_NOBINSEARCH | AVFMT_NOGENSEARCH | AVFMT_NO_BYTE_SEEK | AVFMT_NOTIMESTAMPS, + .raw_codec_id = AV_CODEC_ID_APAC, + .priv_data_size = sizeof(FFRawDemuxerContext), + .priv_class = &ff_raw_demuxer_class, +}; -- 2.37.2 [-- Attachment #4: Type: text/plain, Size: 251 bytes --] _______________________________________________ ffmpeg-devel mailing list ffmpeg-devel@ffmpeg.org https://ffmpeg.org/mailman/listinfo/ffmpeg-devel To unsubscribe, visit link above, or email ffmpeg-devel-request@ffmpeg.org with subject "unsubscribe".
next reply other threads:[~2022-09-21 7:23 UTC|newest] Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top 2022-09-21 7:23 Paul B Mahol [this message] 2022-09-23 17:48 ` Paul B Mahol 2022-09-23 18:31 ` James Almer
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=CAPYw7P6arVSmzk61pJ9U0NNQjCvbz_O64ZCfs_W82BHvDRdDUA@mail.gmail.com \ --to=onemda@gmail.com \ --cc=ffmpeg-devel@ffmpeg.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: link
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel This inbox may be cloned and mirrored by anyone: git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git # If you have public-inbox 1.1+ installed, you may # initialize and index your mirror using the following commands: public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \ ffmpegdev@gitmailbox.com public-inbox-index ffmpegdev Example config snippet for mirrors. AGPL code for this site: git clone https://public-inbox.org/public-inbox.git