Files
2026-08-24 10:03:24 +08:00

395 lines
12 KiB
C

/*
* Immersive Audio Model and Formats parsing
* Copyright (c) 2023 James Almer <[email protected]>
*
* 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/avassert.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/log.h"
#include "libavutil/mem.h"
#include "libavcodec/get_bits.h"
#include "libavcodec/flac.h"
#include "libavcodec/leb.h"
#include "libavcodec/mpeg4audio.h"
#include "libavcodec/put_bits.h"
#include "avio_internal.h"
#include "iamf_parse.h"
#include "isom.h"
static int opus_decoder_config(IAMFCodecConfig *codec_config,
AVIOContext *pb, int len)
{
int ret, left = len - avio_tell(pb);
if (left < 11 || codec_config->audio_roll_distance >= 0)
return AVERROR_INVALIDDATA;
codec_config->extradata = av_malloc(left + 8);
if (!codec_config->extradata)
return AVERROR(ENOMEM);
AV_WN32A(codec_config->extradata, MKBETAG('O','p','u','s'));
AV_WN32A(codec_config->extradata + 4, MKBETAG('H','e','a','d'));
ret = ffio_read_size(pb, codec_config->extradata + 8, left);
if (ret < 0)
return ret;
codec_config->extradata_size = left + 8;
codec_config->sample_rate = 48000;
return 0;
}
static int aac_decoder_config(IAMFCodecConfig *codec_config,
AVIOContext *pb, int len, void *logctx)
{
MPEG4AudioConfig cfg = { 0 };
int object_type_id, codec_id, stream_type;
int ret, tag, left;
if (codec_config->audio_roll_distance >= 0)
return AVERROR_INVALIDDATA;
ff_mp4_read_descr(logctx, pb, &tag);
if (tag != MP4DecConfigDescrTag)
return AVERROR_INVALIDDATA;
object_type_id = avio_r8(pb);
if (object_type_id != 0x40)
return AVERROR_INVALIDDATA;
stream_type = avio_r8(pb);
if (((stream_type >> 2) != 5) || ((stream_type >> 1) & 1))
return AVERROR_INVALIDDATA;
avio_skip(pb, 3); // buffer size db
avio_skip(pb, 4); // rc_max_rate
avio_skip(pb, 4); // avg bitrate
codec_id = ff_codec_get_id(ff_mp4_obj_type, object_type_id);
if (codec_id && codec_id != codec_config->codec_id)
return AVERROR_INVALIDDATA;
left = ff_mp4_read_descr(logctx, pb, &tag);
if (tag != MP4DecSpecificDescrTag ||
!left || left > (len - avio_tell(pb)))
return AVERROR_INVALIDDATA;
// We pad extradata here because avpriv_mpeg4audio_get_config2() needs it.
codec_config->extradata = av_malloc((size_t)left + AV_INPUT_BUFFER_PADDING_SIZE);
if (!codec_config->extradata)
return AVERROR(ENOMEM);
ret = ffio_read_size(pb, codec_config->extradata, left);
if (ret < 0)
return ret;
codec_config->extradata_size = left;
memset(codec_config->extradata + codec_config->extradata_size, 0,
AV_INPUT_BUFFER_PADDING_SIZE);
ret = avpriv_mpeg4audio_get_config2(&cfg, codec_config->extradata,
codec_config->extradata_size, 1, logctx);
if (ret < 0)
return ret;
codec_config->sample_rate = cfg.sample_rate;
return 0;
}
static int flac_decoder_config(IAMFCodecConfig *codec_config,
AVIOContext *pb, int len)
{
int ret, left;
if (codec_config->audio_roll_distance)
return AVERROR_INVALIDDATA;
avio_skip(pb, 4); // METADATA_BLOCK_HEADER
left = len - avio_tell(pb);
if (left < FLAC_STREAMINFO_SIZE)
return AVERROR_INVALIDDATA;
codec_config->extradata = av_malloc(left);
if (!codec_config->extradata)
return AVERROR(ENOMEM);
ret = ffio_read_size(pb, codec_config->extradata, left);
if (ret < 0)
return ret;
codec_config->extradata_size = left;
codec_config->sample_rate = AV_RB24(codec_config->extradata + 10) >> 4;
return 0;
}
static int ipcm_decoder_config(IAMFCodecConfig *codec_config,
AVIOContext *pb, int len)
{
static const enum AVCodecID sample_fmt[2][3] = {
{ AV_CODEC_ID_PCM_S16BE, AV_CODEC_ID_PCM_S24BE, AV_CODEC_ID_PCM_S32BE },
{ AV_CODEC_ID_PCM_S16LE, AV_CODEC_ID_PCM_S24LE, AV_CODEC_ID_PCM_S32LE },
};
int sample_format = avio_r8(pb); // 0 = BE, 1 = LE
int sample_size = (avio_r8(pb) / 8 - 2); // 16, 24, 32
if (sample_format > 1 || sample_size > 2U || codec_config->audio_roll_distance)
return AVERROR_INVALIDDATA;
codec_config->codec_id = sample_fmt[sample_format][sample_size];
codec_config->sample_rate = avio_rb32(pb);
if (len - avio_tell(pb))
return AVERROR_INVALIDDATA;
return 0;
}
static int codec_config_obu(void *s, IAMFContext *c, AVIOContext *pb, int len)
{
IAMFCodecConfig **tmp, *codec_config = NULL;
AVIOContext pbc;
uint8_t *buf;
enum AVCodecID avcodec_id;
unsigned codec_config_id, nb_samples, codec_id;
int16_t audio_roll_distance;
int ret;
buf = av_malloc(len);
if (!buf)
return AVERROR(ENOMEM);
ret = ffio_read_size(pb, buf, len);
if (ret < 0)
goto fail;
ffio_init_context(&pbc, buf, len, 0, NULL, NULL, NULL, NULL);
codec_config_id = ffio_read_leb(&pbc);
codec_id = avio_rb32(&pbc);
nb_samples = ffio_read_leb(&pbc);
audio_roll_distance = avio_rb16(&pbc);
switch (codec_id) {
case MKBETAG('O','p','u','s'):
avcodec_id = AV_CODEC_ID_OPUS;
break;
case MKBETAG('m','p','4','a'):
avcodec_id = AV_CODEC_ID_AAC;
break;
case MKBETAG('f','L','a','C'):
avcodec_id = AV_CODEC_ID_FLAC;
break;
default:
avcodec_id = AV_CODEC_ID_NONE;
break;
}
for (int i = 0; i < c->nb_codec_configs; i++)
if (c->codec_configs[i]->codec_config_id == codec_config_id) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
tmp = av_realloc_array(c->codec_configs, c->nb_codec_configs + 1, sizeof(*c->codec_configs));
if (!tmp) {
ret = AVERROR(ENOMEM);
goto fail;
}
c->codec_configs = tmp;
codec_config = av_mallocz(sizeof(*codec_config));
if (!codec_config) {
ret = AVERROR(ENOMEM);
goto fail;
}
codec_config->codec_config_id = codec_config_id;
codec_config->codec_id = avcodec_id;
codec_config->nb_samples = nb_samples;
codec_config->audio_roll_distance = audio_roll_distance;
switch (codec_id) {
case MKBETAG('O','p','u','s'):
ret = opus_decoder_config(codec_config, &pbc, len);
break;
case MKBETAG('m','p','4','a'):
ret = aac_decoder_config(codec_config, &pbc, len, s);
break;
case MKBETAG('f','L','a','C'):
ret = flac_decoder_config(codec_config, &pbc, len);
break;
case MKBETAG('i','p','c','m'):
ret = ipcm_decoder_config(codec_config, &pbc, len);
break;
default:
break;
}
if (ret < 0)
goto fail;
if ((codec_config->nb_samples > INT_MAX) || codec_config->nb_samples <= 0 ||
(-codec_config->audio_roll_distance > INT_MAX / codec_config->nb_samples)) {
ret = AVERROR_INVALIDDATA;
goto fail;
}
c->codec_configs[c->nb_codec_configs++] = codec_config;
len -= avio_tell(&pbc);
if (len)
av_log(s, AV_LOG_WARNING, "Underread in codec_config_obu. %d bytes left at the end\n", len);
ret = 0;
fail:
av_free(buf);
if (ret < 0) {
if (codec_config)
av_free(codec_config->extradata);
av_free(codec_config);
}
return ret;
}
int ff_iamf_parse_obu_header(const uint8_t *buf, int buf_size,
unsigned *obu_size, int *start_pos, enum IAMF_OBU_Type *type,
unsigned *skip_samples, unsigned *discard_padding)
{
GetBitContext gb;
int ret, extension_flag, trimming, start;
unsigned skip = 0, discard = 0;
unsigned size;
ret = init_get_bits8(&gb, buf, FFMIN(buf_size, MAX_IAMF_OBU_HEADER_SIZE));
if (ret < 0)
return ret;
*type = get_bits(&gb, 5);
/*redundant =*/ get_bits1(&gb);
trimming = get_bits1(&gb);
extension_flag = get_bits1(&gb);
*obu_size = get_leb(&gb);
if (*obu_size > INT_MAX)
return AVERROR_INVALIDDATA;
start = get_bits_count(&gb) / 8;
if (trimming) {
discard = get_leb(&gb); // num_samples_to_trim_at_end
skip = get_leb(&gb); // num_samples_to_trim_at_start
}
if (skip_samples)
*skip_samples = skip;
if (discard_padding)
*discard_padding = discard;
if (extension_flag) {
unsigned int extension_bytes;
extension_bytes = get_leb(&gb);
if (extension_bytes > INT_MAX / 8)
return AVERROR_INVALIDDATA;
skip_bits_long(&gb, extension_bytes * 8);
}
if (get_bits_left(&gb) < 0)
return AVERROR_INVALIDDATA;
size = *obu_size + start;
if (size > INT_MAX)
return AVERROR_INVALIDDATA;
*obu_size -= get_bits_count(&gb) / 8 - start;
*start_pos = size - *obu_size;
return size;
}
int ff_iamfdec_read_descriptors(IAMFContext *c, AVIOContext *pb,
int max_size, void *log_ctx)
{
uint8_t header[MAX_IAMF_OBU_HEADER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
int ret;
while (1) {
unsigned obu_size;
enum IAMF_OBU_Type type;
int start_pos, len, size;
if ((ret = ffio_ensure_seekback(pb, FFMIN(MAX_IAMF_OBU_HEADER_SIZE, max_size))) < 0)
return ret;
size = avio_read(pb, header, FFMIN(MAX_IAMF_OBU_HEADER_SIZE, max_size));
if (size < 0)
return size;
memset(header + size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
len = ff_iamf_parse_obu_header(header, size, &obu_size, &start_pos, &type, NULL, NULL);
if (len < 0 || obu_size > max_size) {
av_log(log_ctx, AV_LOG_ERROR, "Failed to read obu header\n");
avio_seek(pb, -size, SEEK_CUR);
return len;
}
if (type >= IAMF_OBU_IA_PARAMETER_BLOCK && type < IAMF_OBU_IA_SEQUENCE_HEADER) {
avio_seek(pb, -size, SEEK_CUR);
break;
}
avio_seek(pb, -(size - start_pos), SEEK_CUR);
switch (type) {
case IAMF_OBU_IA_CODEC_CONFIG:
ret = codec_config_obu(log_ctx, c, pb, obu_size);
break;
default: {
int64_t offset = avio_skip(pb, obu_size);
if (offset < 0)
ret = offset;
break;
}
}
if (ret < 0) {
av_log(log_ctx, AV_LOG_ERROR, "Failed to read obu type %d\n", type);
return ret;
}
max_size -= obu_size + start_pos;
if (max_size < 0)
return AVERROR_INVALIDDATA;
if (!max_size)
break;
}
return 0;
}
void ff_iamf_uninit_context(IAMFContext *c)
{
if (!c)
return;
for (int i = 0; i < c->nb_codec_configs; i++) {
av_free(c->codec_configs[i]->extradata);
av_free(c->codec_configs[i]);
}
av_freep(&c->codec_configs);
c->nb_codec_configs = 0;
}