parent
2f9fa7e3e9
commit
a166b8a19b
11 changed files with 333 additions and 1 deletions
@ -0,0 +1,208 @@ |
||||
/*
|
||||
* 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 "decode.h" |
||||
|
||||
typedef struct FTRContext { |
||||
AVCodecContext *aac_avctx[64]; // wrapper context for AAC
|
||||
int nb_context; |
||||
AVPacket *packet; |
||||
AVFrame *frame; |
||||
} 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(ENOTSUP); |
||||
|
||||
s->packet = av_packet_alloc(); |
||||
if (!s->packet) |
||||
return AVERROR(ENOMEM); |
||||
|
||||
s->frame = av_frame_alloc(); |
||||
if (!s->frame) |
||||
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; |
||||
if (!av_sample_fmt_is_planar(avctx->sample_fmt)) |
||||
return AVERROR(EINVAL); |
||||
|
||||
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; |
||||
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) { |
||||
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; |
||||
} |
||||
|
||||
ret = avcodec_receive_frame(codec_avctx, s->frame); |
||||
if (ret < 0) |
||||
return ret; |
||||
|
||||
if (!avctx->sample_rate) { |
||||
avctx->sample_rate = codec_avctx->sample_rate; |
||||
} else { |
||||
if (avctx->sample_rate != codec_avctx->sample_rate) |
||||
return AVERROR_INVALIDDATA; |
||||
} |
||||
|
||||
if (!frame->nb_samples) { |
||||
frame->nb_samples = s->frame->nb_samples; |
||||
if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) |
||||
return ret; |
||||
} else { |
||||
if (frame->nb_samples != s->frame->nb_samples) |
||||
return AVERROR_INVALIDDATA; |
||||
} |
||||
|
||||
skip_bits_long(&gb, size * 8); |
||||
|
||||
if (ch_offset + s->frame->ch_layout.nb_channels > avctx->ch_layout.nb_channels) |
||||
return AVERROR_INVALIDDATA; |
||||
|
||||
if (avctx->sample_fmt != codec_avctx->sample_fmt) |
||||
return AVERROR_INVALIDDATA; |
||||
|
||||
for (int ch = 0; ch < s->frame->ch_layout.nb_channels; ch++) |
||||
memcpy(frame->extended_data[ch_offset + ch], |
||||
s->frame->extended_data[ch], |
||||
av_get_bytes_per_sample(codec_avctx->sample_fmt) * s->frame->nb_samples); |
||||
|
||||
ch_offset += s->frame->ch_layout.nb_channels; |
||||
|
||||
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); |
||||
av_frame_free(&s->frame); |
||||
|
||||
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, |
||||
}; |
@ -0,0 +1,107 @@ |
||||
/*
|
||||
* 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) |
||||
{ |
||||
uint8_t tmp[8 + AV_INPUT_BUFFER_PADDING_SIZE]; |
||||
FTRParseContext *ftr = s->priv_data; |
||||
uint64_t state = ftr->pc.state64; |
||||
int next = END_NOT_FOUND; |
||||
GetBitContext bits; |
||||
AACADTSHeaderInfo hdr; |
||||
int size; |
||||
|
||||
*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; |
||||
s->duration = 1024; |
||||
s->key_frame = 1; |
||||
break; |
||||
} else if (ftr->skip > 0) { |
||||
continue; |
||||
} |
||||
} |
||||
|
||||
state = (state << 8) | buf[i]; |
||||
AV_WB64(tmp, state); |
||||
init_get_bits(&bits, tmp + 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, |
||||
}; |
Loading…
Reference in new issue