Message ID | CAPYw7P713o_j6cim4kuRmdVsQXDafVeWQ5J2gp-sVue5Vsyu7g@mail.gmail.com |
---|---|
State | New |
Headers | show |
Series | [FFmpeg-devel] FTR decoder | expand |
Context | Check | Description |
---|---|---|
yinshiyou/configure_loongarch64 | warning | Failed to apply patch |
andriy/make_x86 | fail | Make failed |
Paul B Mahol: > Patch attached. > + union { > + uint64_t u64; > + uint8_t u8[8 + AV_INPUT_BUFFER_PADDING_SIZE]; > + } tmp; > + > + *poutbuf_size = 0; > + *poutbuf = NULL; > + > + if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) { > + next = buf_size; > + } else { > + for (int i = 0; i < buf_size; i++) { > + if (ftr->skip > 0) { > + ftr->skip--; > + if (ftr->skip == 0 && ftr->split) { > + ftr->split = 0; > + next = i; > + break; > + } else if (ftr->skip > 0) { > + continue; > + } > + } > + > + state = (state << 8) | buf[i]; > + tmp.u64 = av_be2ne64(state); It is simpler to just use an uint8_t buf[8 + AV_INPUT_BUFFER_PADDING_SIZE] that is set via AV_RB64(buf, state). > + init_get_bits(&bits, tmp.u8 + 8 - AV_AAC_ADTS_HEADER_SIZE, > + AV_AAC_ADTS_HEADER_SIZE * 8); > +
Paul B Mahol: > diff --git a/libavcodec/ftr.c b/libavcodec/ftr.c > new file mode 100644 > index 0000000000..03d490a0c9 > --- /dev/null > +++ b/libavcodec/ftr.c > @@ -0,0 +1,217 @@ > +/* > + * 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 "adts_header.h" > +#include "avcodec.h" > +#include "codec_internal.h" > +#include "get_bits.h" > +#include "internal.h" You seem to not have rebased your patch upon master: ff_get_buffer() is now in decode.h and this won't compile; including internal.h seems superfluous now. > + > +typedef struct FTRContext { > + AVCodecContext *aac_avctx[64]; // wrapper context for AAC > + int nb_context; > + AVPacket *packet; > +} FTRContext; > + > +static av_cold int ftr_init(AVCodecContext *avctx) > +{ > + FTRContext *s = avctx->priv_data; > + const AVCodec *codec; > + int ret; > + > + if (avctx->ch_layout.nb_channels > 64 || > + avctx->ch_layout.nb_channels <= 0) > + return AVERROR_BUG; I don't see what is supposed to limit nb_channels to 64. If it isn't checked somewhere else, you need to return something else then AVERROR_BUG. EINVAL, ENOSYS or ENOTSUP. > + > + s->packet = av_packet_alloc(); > + if (!s->packet) > + return AVERROR(ENOMEM); > + > + s->nb_context = avctx->ch_layout.nb_channels; > + > + codec = avcodec_find_decoder(AV_CODEC_ID_AAC); This may return the libfdk-aac decoder if the native ones are disabled. It uses AV_SAMPLE_FMT_S16, whereas the native ones use a planar format, namely AV_SAMPLE_FMT_FLTP or . The way you are forwarding the data only works with planar formats. IMO you should just add a configure dependency on the native decoder and force it by using ff_aac_decoder instead of avcodec_find_decoder(). Or maybe use ff_aac_fixed_decoder to make this codec easily testable? > + if (!codec) > + return AVERROR_BUG; > + > + for (int i = 0; i < s->nb_context; i++) { > + s->aac_avctx[i] = avcodec_alloc_context3(codec); > + if (!s->aac_avctx[i]) > + return AVERROR(ENOMEM); > + ret = avcodec_open2(s->aac_avctx[i], codec, NULL); > + if (ret < 0) > + return ret; > + } > + > + avctx->sample_fmt = s->aac_avctx[0]->sample_fmt; > + > + return 0; > +} > + > +static int ftr_decode_frame(AVCodecContext *avctx, AVFrame *frame, > + int *got_frame, AVPacket *avpkt) > +{ > + FTRContext *s = avctx->priv_data; > + GetBitContext gb; > + int ret, ch_offset = 0; > + > + ret = init_get_bits8(&gb, avpkt->data, avpkt->size); > + if (ret < 0) > + return ret; > + > + frame->nb_samples = 0; > + > + for (int i = 0; i < s->nb_context; i++) { > + AVCodecContext *codec_avctx = s->aac_avctx[i]; > + GetBitContext gb2 = gb; > + AACADTSHeaderInfo hdr_info; > + AVFrame *iframe = NULL; > + int size; > + > + if (get_bits_left(&gb) < 64) > + return AVERROR_INVALIDDATA; > + > + memset(&hdr_info, 0, sizeof(hdr_info)); > + > + size = ff_adts_header_parse(&gb2, &hdr_info); > + if (size <= 0 || size * 8 > get_bits_left(&gb)) > + return AVERROR_INVALIDDATA; > + > + if (size > s->packet->size) { > + if (s->packet->size == 0) { > + ret = av_new_packet(s->packet, size); > + } else { > + ret = av_grow_packet(s->packet, size - s->packet->size); > + } This branch seems superfluous: av_grow_packet() can handle blank packets just fine. > + if (ret < 0) > + return ret; > + } > + > + ret = av_packet_make_writable(s->packet); > + if (ret < 0) > + return ret; > + > + memcpy(s->packet->data, avpkt->data + (get_bits_count(&gb) >> 3), size); > + s->packet->size = size; > + > + if (size > 12) { > + uint8_t *buf = s->packet->data; > + > + if (buf[3] & 0x20) { Does this happen often? If not, then you can just reuse the given data (you just need to set pkt->data and size). > + int tmp = buf[8]; > + buf[ 9] = ~buf[9]; > + buf[11] = ~buf[11]; > + buf[12] = ~buf[12]; > + buf[ 8] = ~buf[10]; > + buf[10] = ~tmp; > + } > + } > + > + ret = avcodec_send_packet(codec_avctx, s->packet); > + if (ret < 0) { > + av_log(avctx, AV_LOG_ERROR, "Error submitting a packet for decoding\n"); > + return ret; > + } > + > + iframe = av_frame_alloc(); There is no reason to allocate this temp frame in a loop; it can be allocated during init just like the temp packet. > + if (!iframe) > + return AVERROR(ENOMEM); > + > + ret = avcodec_receive_frame(codec_avctx, iframe); > + if (ret < 0) { > + av_frame_free(&iframe); > + return ret; > + } > + > + if (!avctx->sample_rate) { > + avctx->sample_rate = codec_avctx->sample_rate; > + } else { > + if (avctx->sample_rate != codec_avctx->sample_rate) { > + av_frame_free(&iframe); > + return AVERROR_INVALIDDATA; > + } > + } > + > + if (!frame->nb_samples) { > + frame->nb_samples = iframe->nb_samples; > + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) { > + av_frame_free(&iframe); > + return ret; > + } > + } else { > + if (frame->nb_samples != iframe->nb_samples) { > + av_frame_free(&iframe); > + return AVERROR_INVALIDDATA; > + } > + } > + > + skip_bits_long(&gb, size * 8); > + > + if (ch_offset + iframe->ch_layout.nb_channels > avctx->ch_layout.nb_channels) { > + av_frame_free(&iframe); > + return AVERROR_INVALIDDATA; > + } > + > + for (int ch = 0; ch < iframe->ch_layout.nb_channels; ch++) { > + memcpy(frame->extended_data[ch_offset + ch], iframe->extended_data[ch], sizeof(float) * iframe->nb_samples); One could ref the corresponding buffers; but this would cause problems with the DR1 flag. I wonder whether we can simply forward get_buffer2 to the child contexts and keep DR1. (This presumes that the used AAC decoder has the DR1 flag set, which is true for the native one.) > + } > + > + ch_offset += iframe->ch_layout.nb_channels; > + > + av_frame_free(&iframe); > + > + if (ch_offset >= avctx->ch_layout.nb_channels) > + break; > + } > + > + *got_frame = 1; > + > + return get_bits_count(&gb) >> 3; > +} > + > +static void ftr_flush(AVCodecContext *avctx) > +{ > + FTRContext *s = avctx->priv_data; > + > + for (int i = 0; i < s->nb_context; i++) > + avcodec_flush_buffers(s->aac_avctx[i]); > +} > + > +static av_cold int ftr_close(AVCodecContext *avctx) > +{ > + FTRContext *s = avctx->priv_data; > + > + for (int i = 0; i < s->nb_context; i++) > + avcodec_free_context(&s->aac_avctx[i]); > + av_packet_free(&s->packet); > + > + return 0; > +} > + > +const FFCodec ff_ftr_decoder = { > + .p.name = "ftr", > + .p.long_name = NULL_IF_CONFIG_SMALL("FTR Voice"), > + .p.type = AVMEDIA_TYPE_AUDIO, > + .p.id = AV_CODEC_ID_FTR, > + .init = ftr_init, > + FF_CODEC_DECODE_CB(ftr_decode_frame), > + .close = ftr_close, > + .flush = ftr_flush, > + .priv_data_size = sizeof(FTRContext), > + .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, > + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, > +};
On Wed, 31 Aug 2022, at 18:42, Paul B Mahol wrote:
> Patch attached.
gg
New patch updated from feedback received.
On Wed, Aug 31, 2022 at 9:15 PM Andreas Rheinhardt < andreas.rheinhardt@outlook.com> wrote: > Paul B Mahol: > > diff --git a/libavcodec/ftr.c b/libavcodec/ftr.c > > new file mode 100644 > > index 0000000000..03d490a0c9 > > --- /dev/null > > +++ b/libavcodec/ftr.c > > @@ -0,0 +1,217 @@ > > +/* > > + * 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 "adts_header.h" > > +#include "avcodec.h" > > +#include "codec_internal.h" > > +#include "get_bits.h" > > +#include "internal.h" > > You seem to not have rebased your patch upon master: ff_get_buffer() is > now in decode.h and this won't compile; including internal.h seems > superfluous now. > > > + > > +typedef struct FTRContext { > > + AVCodecContext *aac_avctx[64]; // wrapper context for AAC > > + int nb_context; > > + AVPacket *packet; > > +} FTRContext; > > + > > +static av_cold int ftr_init(AVCodecContext *avctx) > > +{ > > + FTRContext *s = avctx->priv_data; > > + const AVCodec *codec; > > + int ret; > > + > > + if (avctx->ch_layout.nb_channels > 64 || > > + avctx->ch_layout.nb_channels <= 0) > > + return AVERROR_BUG; > > I don't see what is supposed to limit nb_channels to 64. If it isn't > checked somewhere else, you need to return something else then > AVERROR_BUG. EINVAL, ENOSYS or ENOTSUP. > > > + > > + s->packet = av_packet_alloc(); > > + if (!s->packet) > > + return AVERROR(ENOMEM); > > + > > + s->nb_context = avctx->ch_layout.nb_channels; > > + > > + codec = avcodec_find_decoder(AV_CODEC_ID_AAC); > > This may return the libfdk-aac decoder if the native ones are disabled. > It uses AV_SAMPLE_FMT_S16, whereas the native ones use a planar format, > namely AV_SAMPLE_FMT_FLTP or . The way you are forwarding the data only > works with planar formats. > IMO you should just add a configure dependency on the native decoder and > force it by using ff_aac_decoder instead of avcodec_find_decoder(). Or > maybe use ff_aac_fixed_decoder to make this codec easily testable? > > > + if (!codec) > > + return AVERROR_BUG; > > + > > + for (int i = 0; i < s->nb_context; i++) { > > + s->aac_avctx[i] = avcodec_alloc_context3(codec); > > + if (!s->aac_avctx[i]) > > + return AVERROR(ENOMEM); > > + ret = avcodec_open2(s->aac_avctx[i], codec, NULL); > > + if (ret < 0) > > + return ret; > > + } > > + > > + avctx->sample_fmt = s->aac_avctx[0]->sample_fmt; > > + > > + return 0; > > +} > > + > > +static int ftr_decode_frame(AVCodecContext *avctx, AVFrame *frame, > > + int *got_frame, AVPacket *avpkt) > > +{ > > + FTRContext *s = avctx->priv_data; > > + GetBitContext gb; > > + int ret, ch_offset = 0; > > + > > + ret = init_get_bits8(&gb, avpkt->data, avpkt->size); > > + if (ret < 0) > > + return ret; > > + > > + frame->nb_samples = 0; > > + > > + for (int i = 0; i < s->nb_context; i++) { > > + AVCodecContext *codec_avctx = s->aac_avctx[i]; > > + GetBitContext gb2 = gb; > > + AACADTSHeaderInfo hdr_info; > > + AVFrame *iframe = NULL; > > + int size; > > + > > + if (get_bits_left(&gb) < 64) > > + return AVERROR_INVALIDDATA; > > + > > + memset(&hdr_info, 0, sizeof(hdr_info)); > > + > > + size = ff_adts_header_parse(&gb2, &hdr_info); > > + if (size <= 0 || size * 8 > get_bits_left(&gb)) > > + return AVERROR_INVALIDDATA; > > + > > + if (size > s->packet->size) { > > + if (s->packet->size == 0) { > > + ret = av_new_packet(s->packet, size); > > + } else { > > + ret = av_grow_packet(s->packet, size - s->packet->size); > > + } > > This branch seems superfluous: av_grow_packet() can handle blank packets > just fine. > > > + if (ret < 0) > > + return ret; > > + } > > + > > + ret = av_packet_make_writable(s->packet); > > + if (ret < 0) > > + return ret; > > + > > + memcpy(s->packet->data, avpkt->data + (get_bits_count(&gb) >> > 3), size); > > + s->packet->size = size; > > + > > + if (size > 12) { > > + uint8_t *buf = s->packet->data; > > + > > + if (buf[3] & 0x20) { > > Does this happen often? If not, then you can just reuse the given data > (you just need to set pkt->data and size). > It happens almost always. > > > + int tmp = buf[8]; > > + buf[ 9] = ~buf[9]; > > + buf[11] = ~buf[11]; > > + buf[12] = ~buf[12]; > > + buf[ 8] = ~buf[10]; > > + buf[10] = ~tmp; > > + } > > + } > > + > > + ret = avcodec_send_packet(codec_avctx, s->packet); > > + if (ret < 0) { > > + av_log(avctx, AV_LOG_ERROR, "Error submitting a packet for > decoding\n"); > > + return ret; > > + } > > + > > + iframe = av_frame_alloc(); > > There is no reason to allocate this temp frame in a loop; it can be > allocated during init just like the temp packet. > > > + if (!iframe) > > + return AVERROR(ENOMEM); > > + > > + ret = avcodec_receive_frame(codec_avctx, iframe); > > + if (ret < 0) { > > + av_frame_free(&iframe); > > + return ret; > > + } > > + > > + if (!avctx->sample_rate) { > > + avctx->sample_rate = codec_avctx->sample_rate; > > + } else { > > + if (avctx->sample_rate != codec_avctx->sample_rate) { > > + av_frame_free(&iframe); > > + return AVERROR_INVALIDDATA; > > + } > > + } > > + > > + if (!frame->nb_samples) { > > + frame->nb_samples = iframe->nb_samples; > > + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) { > > + av_frame_free(&iframe); > > + return ret; > > + } > > + } else { > > + if (frame->nb_samples != iframe->nb_samples) { > > + av_frame_free(&iframe); > > + return AVERROR_INVALIDDATA; > > + } > > + } > > + > > + skip_bits_long(&gb, size * 8); > > + > > + if (ch_offset + iframe->ch_layout.nb_channels > > avctx->ch_layout.nb_channels) { > > + av_frame_free(&iframe); > > + return AVERROR_INVALIDDATA; > > + } > > + > > + for (int ch = 0; ch < iframe->ch_layout.nb_channels; ch++) { > > + memcpy(frame->extended_data[ch_offset + ch], > iframe->extended_data[ch], sizeof(float) * iframe->nb_samples); > > One could ref the corresponding buffers; but this would cause problems > with the DR1 flag. I wonder whether we can simply forward get_buffer2 to > the child contexts and keep DR1. (This presumes that the used AAC > decoder has the DR1 flag set, which is true for the native one.) > > > + } > > + > > + ch_offset += iframe->ch_layout.nb_channels; > > + > > + av_frame_free(&iframe); > > + > > + if (ch_offset >= avctx->ch_layout.nb_channels) > > + break; > > + } > > + > > + *got_frame = 1; > > + > > + return get_bits_count(&gb) >> 3; > > +} > > + > > +static void ftr_flush(AVCodecContext *avctx) > > +{ > > + FTRContext *s = avctx->priv_data; > > + > > + for (int i = 0; i < s->nb_context; i++) > > + avcodec_flush_buffers(s->aac_avctx[i]); > > +} > > + > > +static av_cold int ftr_close(AVCodecContext *avctx) > > +{ > > + FTRContext *s = avctx->priv_data; > > + > > + for (int i = 0; i < s->nb_context; i++) > > + avcodec_free_context(&s->aac_avctx[i]); > > + av_packet_free(&s->packet); > > + > > + return 0; > > +} > > + > > +const FFCodec ff_ftr_decoder = { > > + .p.name = "ftr", > > + .p.long_name = NULL_IF_CONFIG_SMALL("FTR Voice"), > > + .p.type = AVMEDIA_TYPE_AUDIO, > > + .p.id = AV_CODEC_ID_FTR, > > + .init = ftr_init, > > + FF_CODEC_DECODE_CB(ftr_decode_frame), > > + .close = ftr_close, > > + .flush = ftr_flush, > > + .priv_data_size = sizeof(FTRContext), > > + .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, > > + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, > > +}; > > _______________________________________________ > 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". >
On 8/31/22, Paul B Mahol <onemda@gmail.com> wrote: > New patch updated from feedback received. > Will apply soon.
Quoting Paul B Mahol (2022-09-22 11:09:03) > On 8/31/22, Paul B Mahol <onemda@gmail.com> wrote: > > New patch updated from feedback received. > > > > Will apply soon. This needs tests.
From d56c7b938f5a1a291f7a46b0d06ed45f6e723b82 Mon Sep 17 00:00:00 2001 From: Paul B Mahol <onemda@gmail.com> Date: Tue, 30 Aug 2022 17:14:46 +0200 Subject: [PATCH] avcodec: add FTR audio decoder Signed-off-by: Paul B Mahol <onemda@gmail.com> --- libavcodec/Makefile | 2 + libavcodec/allcodecs.c | 1 + libavcodec/codec_desc.c | 7 ++ libavcodec/codec_id.h | 1 + libavcodec/ftr.c | 217 ++++++++++++++++++++++++++++++++++++++++ libavcodec/ftr_parser.c | 108 ++++++++++++++++++++ libavcodec/parsers.c | 1 + libavcodec/utils.c | 1 + libavformat/avidec.c | 5 +- libavformat/riff.c | 3 + 10 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 libavcodec/ftr.c create mode 100644 libavcodec/ftr_parser.c diff --git a/libavcodec/Makefile b/libavcodec/Makefile index cb80f73d99..8ff9588013 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -359,6 +359,7 @@ OBJS-$(CONFIG_FMVC_DECODER) += fmvc.o OBJS-$(CONFIG_FOURXM_DECODER) += 4xm.o OBJS-$(CONFIG_FRAPS_DECODER) += fraps.o OBJS-$(CONFIG_FRWU_DECODER) += frwu.o +OBJS-$(CONFIG_FTR_DECODER) += ftr.o OBJS-$(CONFIG_G2M_DECODER) += g2meet.o elsdec.o mjpegdec_common.o OBJS-$(CONFIG_G723_1_DECODER) += g723_1dec.o g723_1.o \ acelp_vectors.o celp_filters.o celp_math.o @@ -1130,6 +1131,7 @@ OBJS-$(CONFIG_DVBSUB_PARSER) += dvbsub_parser.o OBJS-$(CONFIG_DVD_NAV_PARSER) += dvd_nav_parser.o OBJS-$(CONFIG_DVDSUB_PARSER) += dvdsub_parser.o OBJS-$(CONFIG_FLAC_PARSER) += flac_parser.o flacdata.o flac.o +OBJS-$(CONFIG_FTR_PARSER) += ftr_parser.o OBJS-$(CONFIG_G723_1_PARSER) += g723_1_parser.o OBJS-$(CONFIG_G729_PARSER) += g729_parser.o OBJS-$(CONFIG_GIF_PARSER) += gif_parser.o diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 6939a4e25f..f7631cd497 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -466,6 +466,7 @@ extern const FFCodec ff_fastaudio_decoder; extern const FFCodec ff_ffwavesynth_decoder; extern const FFCodec ff_flac_encoder; extern const FFCodec ff_flac_decoder; +extern const FFCodec ff_ftr_decoder; extern const FFCodec ff_g723_1_encoder; extern const FFCodec ff_g723_1_decoder; extern const FFCodec ff_g729_decoder; diff --git a/libavcodec/codec_desc.c b/libavcodec/codec_desc.c index 06dfe55d0f..d6523845ea 100644 --- a/libavcodec/codec_desc.c +++ b/libavcodec/codec_desc.c @@ -3290,6 +3290,13 @@ static const AVCodecDescriptor codec_descriptors[] = { .long_name = NULL_IF_CONFIG_SMALL("DFPWM (Dynamic Filter Pulse Width Modulation)"), .props = AV_CODEC_PROP_LOSSY, }, + { + .id = AV_CODEC_ID_FTR, + .type = AVMEDIA_TYPE_AUDIO, + .name = "ftr", + .long_name = NULL_IF_CONFIG_SMALL("FTR Voice"), + .props = AV_CODEC_PROP_INTRA_ONLY | AV_CODEC_PROP_LOSSY, + }, /* subtitle codecs */ { diff --git a/libavcodec/codec_id.h b/libavcodec/codec_id.h index 2247bc0309..dc8b30eb93 100644 --- a/libavcodec/codec_id.h +++ b/libavcodec/codec_id.h @@ -527,6 +527,7 @@ enum AVCodecID { AV_CODEC_ID_FASTAUDIO, AV_CODEC_ID_MSNSIREN, AV_CODEC_ID_DFPWM, + AV_CODEC_ID_FTR, /* subtitle codecs */ AV_CODEC_ID_FIRST_SUBTITLE = 0x17000, ///< A dummy ID pointing at the start of subtitle codecs. diff --git a/libavcodec/ftr.c b/libavcodec/ftr.c new file mode 100644 index 0000000000..03d490a0c9 --- /dev/null +++ b/libavcodec/ftr.c @@ -0,0 +1,217 @@ +/* + * 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 "adts_header.h" +#include "avcodec.h" +#include "codec_internal.h" +#include "get_bits.h" +#include "internal.h" + +typedef struct FTRContext { + AVCodecContext *aac_avctx[64]; // wrapper context for AAC + int nb_context; + AVPacket *packet; +} FTRContext; + +static av_cold int ftr_init(AVCodecContext *avctx) +{ + FTRContext *s = avctx->priv_data; + const AVCodec *codec; + int ret; + + if (avctx->ch_layout.nb_channels > 64 || + avctx->ch_layout.nb_channels <= 0) + return AVERROR_BUG; + + s->packet = av_packet_alloc(); + if (!s->packet) + return AVERROR(ENOMEM); + + s->nb_context = avctx->ch_layout.nb_channels; + + codec = avcodec_find_decoder(AV_CODEC_ID_AAC); + if (!codec) + return AVERROR_BUG; + + for (int i = 0; i < s->nb_context; i++) { + s->aac_avctx[i] = avcodec_alloc_context3(codec); + if (!s->aac_avctx[i]) + return AVERROR(ENOMEM); + ret = avcodec_open2(s->aac_avctx[i], codec, NULL); + if (ret < 0) + return ret; + } + + avctx->sample_fmt = s->aac_avctx[0]->sample_fmt; + + return 0; +} + +static int ftr_decode_frame(AVCodecContext *avctx, AVFrame *frame, + int *got_frame, AVPacket *avpkt) +{ + FTRContext *s = avctx->priv_data; + GetBitContext gb; + int ret, ch_offset = 0; + + ret = init_get_bits8(&gb, avpkt->data, avpkt->size); + if (ret < 0) + return ret; + + frame->nb_samples = 0; + + for (int i = 0; i < s->nb_context; i++) { + AVCodecContext *codec_avctx = s->aac_avctx[i]; + GetBitContext gb2 = gb; + AACADTSHeaderInfo hdr_info; + AVFrame *iframe = NULL; + int size; + + if (get_bits_left(&gb) < 64) + return AVERROR_INVALIDDATA; + + memset(&hdr_info, 0, sizeof(hdr_info)); + + size = ff_adts_header_parse(&gb2, &hdr_info); + if (size <= 0 || size * 8 > get_bits_left(&gb)) + return AVERROR_INVALIDDATA; + + if (size > s->packet->size) { + if (s->packet->size == 0) { + ret = av_new_packet(s->packet, size); + } else { + ret = av_grow_packet(s->packet, size - s->packet->size); + } + if (ret < 0) + return ret; + } + + ret = av_packet_make_writable(s->packet); + if (ret < 0) + return ret; + + memcpy(s->packet->data, avpkt->data + (get_bits_count(&gb) >> 3), size); + s->packet->size = size; + + if (size > 12) { + uint8_t *buf = s->packet->data; + + if (buf[3] & 0x20) { + int tmp = buf[8]; + buf[ 9] = ~buf[9]; + buf[11] = ~buf[11]; + buf[12] = ~buf[12]; + buf[ 8] = ~buf[10]; + buf[10] = ~tmp; + } + } + + ret = avcodec_send_packet(codec_avctx, s->packet); + if (ret < 0) { + av_log(avctx, AV_LOG_ERROR, "Error submitting a packet for decoding\n"); + return ret; + } + + iframe = av_frame_alloc(); + if (!iframe) + return AVERROR(ENOMEM); + + ret = avcodec_receive_frame(codec_avctx, iframe); + if (ret < 0) { + av_frame_free(&iframe); + return ret; + } + + if (!avctx->sample_rate) { + avctx->sample_rate = codec_avctx->sample_rate; + } else { + if (avctx->sample_rate != codec_avctx->sample_rate) { + av_frame_free(&iframe); + return AVERROR_INVALIDDATA; + } + } + + if (!frame->nb_samples) { + frame->nb_samples = iframe->nb_samples; + if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) { + av_frame_free(&iframe); + return ret; + } + } else { + if (frame->nb_samples != iframe->nb_samples) { + av_frame_free(&iframe); + return AVERROR_INVALIDDATA; + } + } + + skip_bits_long(&gb, size * 8); + + if (ch_offset + iframe->ch_layout.nb_channels > avctx->ch_layout.nb_channels) { + av_frame_free(&iframe); + return AVERROR_INVALIDDATA; + } + + for (int ch = 0; ch < iframe->ch_layout.nb_channels; ch++) { + memcpy(frame->extended_data[ch_offset + ch], iframe->extended_data[ch], sizeof(float) * iframe->nb_samples); + } + + ch_offset += iframe->ch_layout.nb_channels; + + av_frame_free(&iframe); + + if (ch_offset >= avctx->ch_layout.nb_channels) + break; + } + + *got_frame = 1; + + return get_bits_count(&gb) >> 3; +} + +static void ftr_flush(AVCodecContext *avctx) +{ + FTRContext *s = avctx->priv_data; + + for (int i = 0; i < s->nb_context; i++) + avcodec_flush_buffers(s->aac_avctx[i]); +} + +static av_cold int ftr_close(AVCodecContext *avctx) +{ + FTRContext *s = avctx->priv_data; + + for (int i = 0; i < s->nb_context; i++) + avcodec_free_context(&s->aac_avctx[i]); + av_packet_free(&s->packet); + + return 0; +} + +const FFCodec ff_ftr_decoder = { + .p.name = "ftr", + .p.long_name = NULL_IF_CONFIG_SMALL("FTR Voice"), + .p.type = AVMEDIA_TYPE_AUDIO, + .p.id = AV_CODEC_ID_FTR, + .init = ftr_init, + FF_CODEC_DECODE_CB(ftr_decode_frame), + .close = ftr_close, + .flush = ftr_flush, + .priv_data_size = sizeof(FTRContext), + .p.capabilities = AV_CODEC_CAP_SUBFRAMES | AV_CODEC_CAP_DR1, + .caps_internal = FF_CODEC_CAP_INIT_CLEANUP, +}; diff --git a/libavcodec/ftr_parser.c b/libavcodec/ftr_parser.c new file mode 100644 index 0000000000..58cc7d6421 --- /dev/null +++ b/libavcodec/ftr_parser.c @@ -0,0 +1,108 @@ +/* + * FTR parser + * 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 + */ + +/** + * @file + * FTR parser + */ + +#include "parser.h" +#include "get_bits.h" +#include "adts_header.h" +#include "adts_parser.h" +#include "mpeg4audio.h" + +typedef struct FTRParseContext { + ParseContext pc; + int skip; + int split; + int frame_index; +} FTRParseContext; + +static int ftr_parse(AVCodecParserContext *s, AVCodecContext *avctx, + const uint8_t **poutbuf, int *poutbuf_size, + const uint8_t *buf, int buf_size) +{ + FTRParseContext *ftr = s->priv_data; + uint64_t state = ftr->pc.state64; + int next = END_NOT_FOUND; + GetBitContext bits; + AACADTSHeaderInfo hdr; + int size; + union { + uint64_t u64; + uint8_t u8[8 + AV_INPUT_BUFFER_PADDING_SIZE]; + } tmp; + + *poutbuf_size = 0; + *poutbuf = NULL; + + if (s->flags & PARSER_FLAG_COMPLETE_FRAMES) { + next = buf_size; + } else { + for (int i = 0; i < buf_size; i++) { + if (ftr->skip > 0) { + ftr->skip--; + if (ftr->skip == 0 && ftr->split) { + ftr->split = 0; + next = i; + break; + } else if (ftr->skip > 0) { + continue; + } + } + + state = (state << 8) | buf[i]; + tmp.u64 = av_be2ne64(state); + init_get_bits(&bits, tmp.u8 + 8 - AV_AAC_ADTS_HEADER_SIZE, + AV_AAC_ADTS_HEADER_SIZE * 8); + + if ((size = ff_adts_header_parse(&bits, &hdr)) > 0) { + ftr->skip = size - 6; + ftr->frame_index += ff_mpeg4audio_channels[hdr.chan_config]; + if (ftr->frame_index >= avctx->ch_layout.nb_channels) { + ftr->frame_index = 0; + ftr->split = 1; + } + } + } + + ftr->pc.state64 = state; + + if (ff_combine_frame(&ftr->pc, next, &buf, &buf_size) < 0) { + *poutbuf = NULL; + *poutbuf_size = 0; + return buf_size; + } + } + + *poutbuf = buf; + *poutbuf_size = buf_size; + + return next; +} + +const AVCodecParser ff_ftr_parser = { + .codec_ids = { AV_CODEC_ID_FTR }, + .priv_data_size = sizeof(FTRParseContext), + .parser_parse = ftr_parse, + .parser_close = ff_parse_close, +}; diff --git a/libavcodec/parsers.c b/libavcodec/parsers.c index a8d52af6cb..ad72e147fd 100644 --- a/libavcodec/parsers.c +++ b/libavcodec/parsers.c @@ -42,6 +42,7 @@ extern const AVCodecParser ff_dvbsub_parser; extern const AVCodecParser ff_dvdsub_parser; extern const AVCodecParser ff_dvd_nav_parser; extern const AVCodecParser ff_flac_parser; +extern const AVCodecParser ff_ftr_parser; extern const AVCodecParser ff_g723_1_parser; extern const AVCodecParser ff_g729_parser; extern const AVCodecParser ff_gif_parser; diff --git a/libavcodec/utils.c b/libavcodec/utils.c index e73e3a7d08..0536174f1e 100644 --- a/libavcodec/utils.c +++ b/libavcodec/utils.c @@ -638,6 +638,7 @@ static int get_audio_frame_duration(enum AVCodecID id, int sr, int ch, int ba, case AV_CODEC_ID_MP2: case AV_CODEC_ID_MUSEPACK7: return 1152; case AV_CODEC_ID_AC3: return 1536; + case AV_CODEC_ID_FTR: return 1024; } if (sr > 0) { diff --git a/libavformat/avidec.c b/libavformat/avidec.c index 937d9e6ffb..212b154d80 100644 --- a/libavformat/avidec.c +++ b/libavformat/avidec.c @@ -1555,7 +1555,10 @@ resync: } else { pkt->flags |= AV_PKT_FLAG_KEY; } - ast->frame_offset += get_duration(ast, pkt->size); + if (st->codecpar->codec_id == AV_CODEC_ID_FTR) + ast->frame_offset++; + else + ast->frame_offset += get_duration(ast, pkt->size); } ast->remaining -= err; if (!ast->remaining) { diff --git a/libavformat/riff.c b/libavformat/riff.c index 6c06ad2d60..59fc7abcbd 100644 --- a/libavformat/riff.c +++ b/libavformat/riff.c @@ -558,6 +558,7 @@ const AVCodecTag ff_codec_wav_tags[] = { { AV_CODEC_ID_WMALOSSLESS, 0x0163 }, { AV_CODEC_ID_XMA1, 0x0165 }, { AV_CODEC_ID_XMA2, 0x0166 }, + { AV_CODEC_ID_FTR, 0x0180 }, { AV_CODEC_ID_ADPCM_CT, 0x0200 }, { AV_CODEC_ID_DVAUDIO, 0x0215 }, { AV_CODEC_ID_DVAUDIO, 0x0216 }, @@ -583,8 +584,10 @@ const AVCodecTag ff_codec_wav_tags[] = { { AV_CODEC_ID_PCM_MULAW, 0x6c75 }, { AV_CODEC_ID_AAC, 0x706d }, { AV_CODEC_ID_AAC, 0x4143 }, + { AV_CODEC_ID_FTR, 0x4180 }, { AV_CODEC_ID_XAN_DPCM, 0x594a }, { AV_CODEC_ID_G729, 0x729A }, + { AV_CODEC_ID_FTR, 0x8180 }, { AV_CODEC_ID_G723_1, 0xA100 }, /* Comverse Infosys Ltd. G723 1 */ { AV_CODEC_ID_AAC, 0xA106 }, { AV_CODEC_ID_SPEEX, 0xA109 }, -- 2.37.2