mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2025-01-08 13:22:53 +02:00
790f793844
There are lots of files that don't need it: The number of object files that actually need it went down from 2011 to 884 here. Keep it for external users in order to not cause breakages. Also improve the other headers a bit while just at it. Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
1098 lines
36 KiB
C
1098 lines
36 KiB
C
/*
|
|
* Immersive Audio Model and Formats parsing
|
|
* Copyright (c) 2023 James Almer <jamrial@gmail.com>
|
|
*
|
|
* 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/iamf.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 left = len - avio_tell(pb);
|
|
|
|
if (left < 11)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
codec_config->extradata = av_malloc(left + 8);
|
|
if (!codec_config->extradata)
|
|
return AVERROR(ENOMEM);
|
|
|
|
AV_WB32(codec_config->extradata, MKBETAG('O','p','u','s'));
|
|
AV_WB32(codec_config->extradata + 4, MKBETAG('H','e','a','d'));
|
|
codec_config->extradata_size = avio_read(pb, codec_config->extradata + 8, left);
|
|
if (codec_config->extradata_size < left)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
codec_config->extradata_size += 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;
|
|
|
|
tag = avio_r8(pb);
|
|
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;
|
|
|
|
tag = avio_r8(pb);
|
|
if (tag != MP4DecSpecificDescrTag)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
left = len - avio_tell(pb);
|
|
if (left <= 0)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
codec_config->extradata = av_malloc(left);
|
|
if (!codec_config->extradata)
|
|
return AVERROR(ENOMEM);
|
|
|
|
codec_config->extradata_size = avio_read(pb, codec_config->extradata, left);
|
|
if (codec_config->extradata_size < left)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
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 left;
|
|
|
|
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);
|
|
|
|
codec_config->extradata_size = avio_read(pb, codec_config->extradata, left);
|
|
if (codec_config->extradata_size < left)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
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 > 2)
|
|
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;
|
|
FFIOContext b;
|
|
AVIOContext *pbc;
|
|
uint8_t *buf;
|
|
enum AVCodecID avcodec_id;
|
|
unsigned codec_config_id, nb_samples, codec_id;
|
|
int16_t seek_preroll;
|
|
int ret;
|
|
|
|
buf = av_malloc(len);
|
|
if (!buf)
|
|
return AVERROR(ENOMEM);
|
|
|
|
ret = avio_read(pb, buf, len);
|
|
if (ret != len) {
|
|
if (ret >= 0)
|
|
ret = AVERROR_INVALIDDATA;
|
|
goto fail;
|
|
}
|
|
|
|
ffio_init_context(&b, buf, len, 0, NULL, NULL, NULL, NULL);
|
|
pbc = &b.pub;
|
|
|
|
codec_config_id = ffio_read_leb(pbc);
|
|
codec_id = avio_rb32(pbc);
|
|
nb_samples = ffio_read_leb(pbc);
|
|
seek_preroll = 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->seek_preroll = seek_preroll;
|
|
|
|
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;
|
|
|
|
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;
|
|
}
|
|
|
|
static int update_extradata(AVCodecParameters *codecpar)
|
|
{
|
|
GetBitContext gb;
|
|
PutBitContext pb;
|
|
int ret;
|
|
|
|
switch(codecpar->codec_id) {
|
|
case AV_CODEC_ID_OPUS:
|
|
AV_WB8(codecpar->extradata + 9, codecpar->ch_layout.nb_channels);
|
|
break;
|
|
case AV_CODEC_ID_AAC: {
|
|
uint8_t buf[5];
|
|
|
|
init_put_bits(&pb, buf, sizeof(buf));
|
|
ret = init_get_bits8(&gb, codecpar->extradata, codecpar->extradata_size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = get_bits(&gb, 5);
|
|
put_bits(&pb, 5, ret);
|
|
if (ret == AOT_ESCAPE) // violates section 3.11.2, but better check for it
|
|
put_bits(&pb, 6, get_bits(&gb, 6));
|
|
ret = get_bits(&gb, 4);
|
|
put_bits(&pb, 4, ret);
|
|
if (ret == 0x0f)
|
|
put_bits(&pb, 24, get_bits(&gb, 24));
|
|
|
|
skip_bits(&gb, 4);
|
|
put_bits(&pb, 4, codecpar->ch_layout.nb_channels); // set channel config
|
|
ret = put_bits_left(&pb);
|
|
put_bits(&pb, ret, get_bits(&gb, ret));
|
|
flush_put_bits(&pb);
|
|
|
|
memcpy(codecpar->extradata, buf, sizeof(buf));
|
|
break;
|
|
}
|
|
case AV_CODEC_ID_FLAC: {
|
|
uint8_t buf[13];
|
|
|
|
init_put_bits(&pb, buf, sizeof(buf));
|
|
ret = init_get_bits8(&gb, codecpar->extradata, codecpar->extradata_size);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
put_bits32(&pb, get_bits_long(&gb, 32)); // min/max blocksize
|
|
put_bits64(&pb, 48, get_bits64(&gb, 48)); // min/max framesize
|
|
put_bits(&pb, 20, get_bits(&gb, 20)); // samplerate
|
|
skip_bits(&gb, 3);
|
|
put_bits(&pb, 3, codecpar->ch_layout.nb_channels - 1);
|
|
ret = put_bits_left(&pb);
|
|
put_bits(&pb, ret, get_bits(&gb, ret));
|
|
flush_put_bits(&pb);
|
|
|
|
memcpy(codecpar->extradata, buf, sizeof(buf));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int scalable_channel_layout_config(void *s, AVIOContext *pb,
|
|
IAMFAudioElement *audio_element,
|
|
const IAMFCodecConfig *codec_config)
|
|
{
|
|
int nb_layers, k = 0;
|
|
|
|
nb_layers = avio_r8(pb) >> 5; // get_bits(&gb, 3);
|
|
// skip_bits(&gb, 5); //reserved
|
|
|
|
if (nb_layers > 6)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
audio_element->layers = av_calloc(nb_layers, sizeof(*audio_element->layers));
|
|
if (!audio_element->layers)
|
|
return AVERROR(ENOMEM);
|
|
|
|
audio_element->nb_layers = nb_layers;
|
|
for (int i = 0; i < nb_layers; i++) {
|
|
AVIAMFLayer *layer;
|
|
int loudspeaker_layout, output_gain_is_present_flag;
|
|
int substream_count, coupled_substream_count;
|
|
int ret, byte = avio_r8(pb);
|
|
|
|
layer = av_iamf_audio_element_add_layer(audio_element->element);
|
|
if (!layer)
|
|
return AVERROR(ENOMEM);
|
|
|
|
loudspeaker_layout = byte >> 4; // get_bits(&gb, 4);
|
|
output_gain_is_present_flag = (byte >> 3) & 1; //get_bits1(&gb);
|
|
if ((byte >> 2) & 1)
|
|
layer->flags |= AV_IAMF_LAYER_FLAG_RECON_GAIN;
|
|
substream_count = avio_r8(pb);
|
|
coupled_substream_count = avio_r8(pb);
|
|
|
|
audio_element->layers[i].substream_count = substream_count;
|
|
audio_element->layers[i].coupled_substream_count = coupled_substream_count;
|
|
if (output_gain_is_present_flag) {
|
|
layer->output_gain_flags = avio_r8(pb) >> 2; // get_bits(&gb, 6);
|
|
layer->output_gain = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8);
|
|
}
|
|
|
|
if (loudspeaker_layout < 10)
|
|
av_channel_layout_copy(&layer->ch_layout, &ff_iamf_scalable_ch_layouts[loudspeaker_layout]);
|
|
else
|
|
layer->ch_layout = (AVChannelLayout){ .order = AV_CHANNEL_ORDER_UNSPEC,
|
|
.nb_channels = substream_count +
|
|
coupled_substream_count };
|
|
|
|
for (int j = 0; j < substream_count; j++) {
|
|
IAMFSubStream *substream = &audio_element->substreams[k++];
|
|
|
|
substream->codecpar->ch_layout = coupled_substream_count-- > 0 ? (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO :
|
|
(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
|
|
|
|
ret = update_extradata(substream->codecpar);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int ambisonics_config(void *s, AVIOContext *pb,
|
|
IAMFAudioElement *audio_element,
|
|
const IAMFCodecConfig *codec_config)
|
|
{
|
|
AVIAMFLayer *layer;
|
|
unsigned ambisonics_mode;
|
|
int output_channel_count, substream_count, order;
|
|
int ret;
|
|
|
|
ambisonics_mode = ffio_read_leb(pb);
|
|
if (ambisonics_mode > 1)
|
|
return 0;
|
|
|
|
output_channel_count = avio_r8(pb); // C
|
|
substream_count = avio_r8(pb); // N
|
|
if (audio_element->nb_substreams != substream_count)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
order = floor(sqrt(output_channel_count - 1));
|
|
/* incomplete order - some harmonics are missing */
|
|
if ((order + 1) * (order + 1) != output_channel_count)
|
|
return AVERROR_INVALIDDATA;
|
|
|
|
audio_element->layers = av_mallocz(sizeof(*audio_element->layers));
|
|
if (!audio_element->layers)
|
|
return AVERROR(ENOMEM);
|
|
|
|
audio_element->nb_layers = 1;
|
|
audio_element->layers->substream_count = substream_count;
|
|
|
|
layer = av_iamf_audio_element_add_layer(audio_element->element);
|
|
if (!layer)
|
|
return AVERROR(ENOMEM);
|
|
|
|
layer->ambisonics_mode = ambisonics_mode;
|
|
if (ambisonics_mode == 0) {
|
|
for (int i = 0; i < substream_count; i++) {
|
|
IAMFSubStream *substream = &audio_element->substreams[i];
|
|
|
|
substream->codecpar->ch_layout = (AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
|
|
|
|
ret = update_extradata(substream->codecpar);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
|
|
layer->ch_layout.order = AV_CHANNEL_ORDER_CUSTOM;
|
|
layer->ch_layout.nb_channels = output_channel_count;
|
|
layer->ch_layout.u.map = av_calloc(output_channel_count, sizeof(*layer->ch_layout.u.map));
|
|
if (!layer->ch_layout.u.map)
|
|
return AVERROR(ENOMEM);
|
|
|
|
for (int i = 0; i < output_channel_count; i++)
|
|
layer->ch_layout.u.map[i].id = avio_r8(pb) + AV_CHAN_AMBISONIC_BASE;
|
|
} else {
|
|
int coupled_substream_count = avio_r8(pb); // M
|
|
int nb_demixing_matrix = substream_count + coupled_substream_count;
|
|
int demixing_matrix_size = nb_demixing_matrix * output_channel_count;
|
|
|
|
audio_element->layers->coupled_substream_count = coupled_substream_count;
|
|
|
|
layer->ch_layout = (AVChannelLayout){ .order = AV_CHANNEL_ORDER_AMBISONIC, .nb_channels = output_channel_count };
|
|
layer->demixing_matrix = av_malloc_array(demixing_matrix_size, sizeof(*layer->demixing_matrix));
|
|
if (!layer->demixing_matrix)
|
|
return AVERROR(ENOMEM);
|
|
|
|
for (int i = 0; i < demixing_matrix_size; i++)
|
|
layer->demixing_matrix[i] = av_make_q(sign_extend(avio_rb16(pb), 16), 1 << 8);
|
|
|
|
for (int i = 0; i < substream_count; i++) {
|
|
IAMFSubStream *substream = &audio_element->substreams[i];
|
|
|
|
substream->codecpar->ch_layout = coupled_substream_count-- > 0 ? (AVChannelLayout)AV_CHANNEL_LAYOUT_STEREO :
|
|
(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO;
|
|
|
|
|
|
ret = update_extradata(substream->codecpar);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int param_parse(void *s, IAMFContext *c, AVIOContext *pb,
|
|
unsigned int type,
|
|
const IAMFAudioElement *audio_element,
|
|
AVIAMFParamDefinition **out_param_definition)
|
|
{
|
|
IAMFParamDefinition *param_definition = NULL;
|
|
AVIAMFParamDefinition *param;
|
|
unsigned int parameter_id, parameter_rate, mode;
|
|
unsigned int duration = 0, constant_subblock_duration = 0, nb_subblocks = 0;
|
|
size_t param_size;
|
|
|
|
parameter_id = ffio_read_leb(pb);
|
|
|
|
for (int i = 0; i < c->nb_param_definitions; i++)
|
|
if (c->param_definitions[i]->param->parameter_id == parameter_id) {
|
|
param_definition = c->param_definitions[i];
|
|
break;
|
|
}
|
|
|
|
parameter_rate = ffio_read_leb(pb);
|
|
mode = avio_r8(pb) >> 7;
|
|
|
|
if (mode == 0) {
|
|
duration = ffio_read_leb(pb);
|
|
if (!duration)
|
|
return AVERROR_INVALIDDATA;
|
|
constant_subblock_duration = ffio_read_leb(pb);
|
|
if (constant_subblock_duration == 0)
|
|
nb_subblocks = ffio_read_leb(pb);
|
|
else
|
|
nb_subblocks = duration / constant_subblock_duration;
|
|
}
|
|
|
|
param = av_iamf_param_definition_alloc(type, nb_subblocks, ¶m_size);
|
|
if (!param)
|
|
return AVERROR(ENOMEM);
|
|
|
|
for (int i = 0; i < nb_subblocks; i++) {
|
|
void *subblock = av_iamf_param_definition_get_subblock(param, i);
|
|
unsigned int subblock_duration = constant_subblock_duration;
|
|
|
|
if (constant_subblock_duration == 0)
|
|
subblock_duration = ffio_read_leb(pb);
|
|
|
|
switch (type) {
|
|
case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: {
|
|
AVIAMFMixGain *mix = subblock;
|
|
mix->subblock_duration = subblock_duration;
|
|
break;
|
|
}
|
|
case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: {
|
|
AVIAMFDemixingInfo *demix = subblock;
|
|
demix->subblock_duration = subblock_duration;
|
|
// DefaultDemixingInfoParameterData
|
|
av_assert0(audio_element);
|
|
demix->dmixp_mode = avio_r8(pb) >> 5;
|
|
audio_element->element->default_w = avio_r8(pb) >> 4;
|
|
break;
|
|
}
|
|
case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: {
|
|
AVIAMFReconGain *recon = subblock;
|
|
recon->subblock_duration = subblock_duration;
|
|
break;
|
|
}
|
|
default:
|
|
av_free(param);
|
|
return AVERROR_INVALIDDATA;
|
|
}
|
|
}
|
|
|
|
param->parameter_id = parameter_id;
|
|
param->parameter_rate = parameter_rate;
|
|
param->duration = duration;
|
|
param->constant_subblock_duration = constant_subblock_duration;
|
|
param->nb_subblocks = nb_subblocks;
|
|
|
|
if (param_definition) {
|
|
if (param_definition->param_size != param_size || memcmp(param_definition->param, param, param_size)) {
|
|
av_log(s, AV_LOG_ERROR, "Incosistent parameters for parameter_id %u\n", parameter_id);
|
|
av_free(param);
|
|
return AVERROR_INVALIDDATA;
|
|
}
|
|
} else {
|
|
IAMFParamDefinition **tmp = av_realloc_array(c->param_definitions, c->nb_param_definitions + 1,
|
|
sizeof(*c->param_definitions));
|
|
if (!tmp) {
|
|
av_free(param);
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
c->param_definitions = tmp;
|
|
|
|
param_definition = av_mallocz(sizeof(*param_definition));
|
|
if (!param_definition) {
|
|
av_free(param);
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
param_definition->param = param;
|
|
param_definition->mode = !mode;
|
|
param_definition->param_size = param_size;
|
|
param_definition->audio_element = audio_element;
|
|
|
|
c->param_definitions[c->nb_param_definitions++] = param_definition;
|
|
}
|
|
|
|
av_assert0(out_param_definition);
|
|
*out_param_definition = param;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int audio_element_obu(void *s, IAMFContext *c, AVIOContext *pb, int len)
|
|
{
|
|
const IAMFCodecConfig *codec_config;
|
|
AVIAMFAudioElement *element;
|
|
IAMFAudioElement **tmp, *audio_element = NULL;
|
|
FFIOContext b;
|
|
AVIOContext *pbc;
|
|
uint8_t *buf;
|
|
unsigned audio_element_id, codec_config_id, num_parameters;
|
|
int audio_element_type, ret;
|
|
|
|
buf = av_malloc(len);
|
|
if (!buf)
|
|
return AVERROR(ENOMEM);
|
|
|
|
ret = avio_read(pb, buf, len);
|
|
if (ret != len) {
|
|
if (ret >= 0)
|
|
ret = AVERROR_INVALIDDATA;
|
|
goto fail;
|
|
}
|
|
|
|
ffio_init_context(&b, buf, len, 0, NULL, NULL, NULL, NULL);
|
|
pbc = &b.pub;
|
|
|
|
audio_element_id = ffio_read_leb(pbc);
|
|
|
|
for (int i = 0; i < c->nb_audio_elements; i++)
|
|
if (c->audio_elements[i]->audio_element_id == audio_element_id) {
|
|
av_log(s, AV_LOG_ERROR, "Duplicate audio_element_id %d\n", audio_element_id);
|
|
ret = AVERROR_INVALIDDATA;
|
|
goto fail;
|
|
}
|
|
|
|
audio_element_type = avio_r8(pbc) >> 5;
|
|
codec_config_id = ffio_read_leb(pbc);
|
|
|
|
codec_config = ff_iamf_get_codec_config(c, codec_config_id);
|
|
if (!codec_config) {
|
|
av_log(s, AV_LOG_ERROR, "Non existant codec config id %d referenced in an audio element\n", codec_config_id);
|
|
ret = AVERROR_INVALIDDATA;
|
|
goto fail;
|
|
}
|
|
|
|
if (codec_config->codec_id == AV_CODEC_ID_NONE) {
|
|
av_log(s, AV_LOG_DEBUG, "Unknown codec id referenced in an audio element. Ignoring\n");
|
|
ret = 0;
|
|
goto fail;
|
|
}
|
|
|
|
tmp = av_realloc_array(c->audio_elements, c->nb_audio_elements + 1, sizeof(*c->audio_elements));
|
|
if (!tmp) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
c->audio_elements = tmp;
|
|
|
|
audio_element = av_mallocz(sizeof(*audio_element));
|
|
if (!audio_element) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
|
|
audio_element->nb_substreams = ffio_read_leb(pbc);
|
|
audio_element->codec_config_id = codec_config_id;
|
|
audio_element->audio_element_id = audio_element_id;
|
|
audio_element->substreams = av_calloc(audio_element->nb_substreams, sizeof(*audio_element->substreams));
|
|
if (!audio_element->substreams) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
|
|
element = audio_element->element = av_iamf_audio_element_alloc();
|
|
if (!element) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
audio_element->celement = element;
|
|
|
|
element->audio_element_type = audio_element_type;
|
|
|
|
for (int i = 0; i < audio_element->nb_substreams; i++) {
|
|
IAMFSubStream *substream = &audio_element->substreams[i];
|
|
|
|
substream->codecpar = avcodec_parameters_alloc();
|
|
if (!substream->codecpar) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
|
|
substream->audio_substream_id = ffio_read_leb(pbc);
|
|
|
|
substream->codecpar->codec_type = AVMEDIA_TYPE_AUDIO;
|
|
substream->codecpar->codec_id = codec_config->codec_id;
|
|
substream->codecpar->frame_size = codec_config->nb_samples;
|
|
substream->codecpar->sample_rate = codec_config->sample_rate;
|
|
substream->codecpar->seek_preroll = codec_config->seek_preroll;
|
|
|
|
switch(substream->codecpar->codec_id) {
|
|
case AV_CODEC_ID_AAC:
|
|
case AV_CODEC_ID_FLAC:
|
|
case AV_CODEC_ID_OPUS:
|
|
substream->codecpar->extradata = av_malloc(codec_config->extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
|
|
if (!substream->codecpar->extradata) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
memcpy(substream->codecpar->extradata, codec_config->extradata, codec_config->extradata_size);
|
|
memset(substream->codecpar->extradata + codec_config->extradata_size, 0, AV_INPUT_BUFFER_PADDING_SIZE);
|
|
substream->codecpar->extradata_size = codec_config->extradata_size;
|
|
break;
|
|
}
|
|
}
|
|
|
|
num_parameters = ffio_read_leb(pbc);
|
|
if (num_parameters && audio_element_type != 0) {
|
|
av_log(s, AV_LOG_ERROR, "Audio Element parameter count %u is invalid"
|
|
" for Scene representations\n", num_parameters);
|
|
ret = AVERROR_INVALIDDATA;
|
|
goto fail;
|
|
}
|
|
|
|
for (int i = 0; i < num_parameters; i++) {
|
|
unsigned type;
|
|
|
|
type = ffio_read_leb(pbc);
|
|
if (type == AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN)
|
|
ret = AVERROR_INVALIDDATA;
|
|
else if (type == AV_IAMF_PARAMETER_DEFINITION_DEMIXING)
|
|
ret = param_parse(s, c, pbc, type, audio_element, &element->demixing_info);
|
|
else if (type == AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN)
|
|
ret = param_parse(s, c, pbc, type, audio_element, &element->recon_gain_info);
|
|
else {
|
|
unsigned param_definition_size = ffio_read_leb(pbc);
|
|
avio_skip(pbc, param_definition_size);
|
|
}
|
|
if (ret < 0)
|
|
goto fail;
|
|
}
|
|
|
|
if (audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL) {
|
|
ret = scalable_channel_layout_config(s, pbc, audio_element, codec_config);
|
|
if (ret < 0)
|
|
goto fail;
|
|
} else if (audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_SCENE) {
|
|
ret = ambisonics_config(s, pbc, audio_element, codec_config);
|
|
if (ret < 0)
|
|
goto fail;
|
|
} else {
|
|
unsigned audio_element_config_size = ffio_read_leb(pbc);
|
|
avio_skip(pbc, audio_element_config_size);
|
|
}
|
|
|
|
c->audio_elements[c->nb_audio_elements++] = audio_element;
|
|
|
|
len -= avio_tell(pbc);
|
|
if (len)
|
|
av_log(s, AV_LOG_WARNING, "Underread in audio_element_obu. %d bytes left at the end\n", len);
|
|
|
|
ret = 0;
|
|
fail:
|
|
av_free(buf);
|
|
if (ret < 0)
|
|
ff_iamf_free_audio_element(&audio_element);
|
|
return ret;
|
|
}
|
|
|
|
static int label_string(AVIOContext *pb, char **label)
|
|
{
|
|
uint8_t buf[128];
|
|
|
|
avio_get_str(pb, sizeof(buf), buf, sizeof(buf));
|
|
|
|
if (pb->error)
|
|
return pb->error;
|
|
if (pb->eof_reached)
|
|
return AVERROR_INVALIDDATA;
|
|
*label = av_strdup(buf);
|
|
if (!*label)
|
|
return AVERROR(ENOMEM);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mix_presentation_obu(void *s, IAMFContext *c, AVIOContext *pb, int len)
|
|
{
|
|
AVIAMFMixPresentation *mix;
|
|
IAMFMixPresentation **tmp, *mix_presentation = NULL;
|
|
FFIOContext b;
|
|
AVIOContext *pbc;
|
|
uint8_t *buf;
|
|
unsigned nb_submixes, mix_presentation_id;
|
|
int ret;
|
|
|
|
buf = av_malloc(len);
|
|
if (!buf)
|
|
return AVERROR(ENOMEM);
|
|
|
|
ret = avio_read(pb, buf, len);
|
|
if (ret != len) {
|
|
if (ret >= 0)
|
|
ret = AVERROR_INVALIDDATA;
|
|
goto fail;
|
|
}
|
|
|
|
ffio_init_context(&b, buf, len, 0, NULL, NULL, NULL, NULL);
|
|
pbc = &b.pub;
|
|
|
|
mix_presentation_id = ffio_read_leb(pbc);
|
|
|
|
for (int i = 0; i < c->nb_mix_presentations; i++)
|
|
if (c->mix_presentations[i]->mix_presentation_id == mix_presentation_id) {
|
|
av_log(s, AV_LOG_ERROR, "Duplicate mix_presentation_id %d\n", mix_presentation_id);
|
|
ret = AVERROR_INVALIDDATA;
|
|
goto fail;
|
|
}
|
|
|
|
tmp = av_realloc_array(c->mix_presentations, c->nb_mix_presentations + 1, sizeof(*c->mix_presentations));
|
|
if (!tmp) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
c->mix_presentations = tmp;
|
|
|
|
mix_presentation = av_mallocz(sizeof(*mix_presentation));
|
|
if (!mix_presentation) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
|
|
mix_presentation->mix_presentation_id = mix_presentation_id;
|
|
mix = mix_presentation->mix = av_iamf_mix_presentation_alloc();
|
|
if (!mix) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
mix_presentation->cmix = mix;
|
|
|
|
mix_presentation->count_label = ffio_read_leb(pbc);
|
|
mix_presentation->language_label = av_calloc(mix_presentation->count_label,
|
|
sizeof(*mix_presentation->language_label));
|
|
if (!mix_presentation->language_label) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
|
|
for (int i = 0; i < mix_presentation->count_label; i++) {
|
|
ret = label_string(pbc, &mix_presentation->language_label[i]);
|
|
if (ret < 0)
|
|
goto fail;
|
|
}
|
|
|
|
for (int i = 0; i < mix_presentation->count_label; i++) {
|
|
char *annotation = NULL;
|
|
ret = label_string(pbc, &annotation);
|
|
if (ret < 0)
|
|
goto fail;
|
|
ret = av_dict_set(&mix->annotations, mix_presentation->language_label[i], annotation,
|
|
AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE);
|
|
if (ret < 0)
|
|
goto fail;
|
|
}
|
|
|
|
nb_submixes = ffio_read_leb(pbc);
|
|
for (int i = 0; i < nb_submixes; i++) {
|
|
AVIAMFSubmix *sub_mix;
|
|
unsigned nb_elements, nb_layouts;
|
|
|
|
sub_mix = av_iamf_mix_presentation_add_submix(mix);
|
|
if (!sub_mix) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
|
|
nb_elements = ffio_read_leb(pbc);
|
|
for (int j = 0; j < nb_elements; j++) {
|
|
AVIAMFSubmixElement *submix_element;
|
|
IAMFAudioElement *audio_element = NULL;
|
|
unsigned int rendering_config_extension_size;
|
|
|
|
submix_element = av_iamf_submix_add_element(sub_mix);
|
|
if (!submix_element) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
|
|
submix_element->audio_element_id = ffio_read_leb(pbc);
|
|
|
|
for (int k = 0; k < c->nb_audio_elements; k++)
|
|
if (c->audio_elements[k]->audio_element_id == submix_element->audio_element_id) {
|
|
audio_element = c->audio_elements[k];
|
|
break;
|
|
}
|
|
|
|
if (!audio_element) {
|
|
av_log(s, AV_LOG_ERROR, "Invalid Audio Element with id %u referenced by Mix Parameters %u\n",
|
|
submix_element->audio_element_id, mix_presentation_id);
|
|
ret = AVERROR_INVALIDDATA;
|
|
goto fail;
|
|
}
|
|
|
|
for (int k = 0; k < mix_presentation->count_label; k++) {
|
|
char *annotation = NULL;
|
|
ret = label_string(pbc, &annotation);
|
|
if (ret < 0)
|
|
goto fail;
|
|
ret = av_dict_set(&submix_element->annotations, mix_presentation->language_label[k], annotation,
|
|
AV_DICT_DONT_STRDUP_VAL | AV_DICT_DONT_OVERWRITE);
|
|
if (ret < 0)
|
|
goto fail;
|
|
}
|
|
|
|
submix_element->headphones_rendering_mode = avio_r8(pbc) >> 6;
|
|
|
|
rendering_config_extension_size = ffio_read_leb(pbc);
|
|
avio_skip(pbc, rendering_config_extension_size);
|
|
|
|
ret = param_parse(s, c, pbc, AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN,
|
|
NULL,
|
|
&submix_element->element_mix_config);
|
|
if (ret < 0)
|
|
goto fail;
|
|
submix_element->default_mix_gain = av_make_q(sign_extend(avio_rb16(pbc), 16), 1 << 8);
|
|
}
|
|
|
|
ret = param_parse(s, c, pbc, AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, NULL, &sub_mix->output_mix_config);
|
|
if (ret < 0)
|
|
goto fail;
|
|
sub_mix->default_mix_gain = av_make_q(sign_extend(avio_rb16(pbc), 16), 1 << 8);
|
|
|
|
nb_layouts = ffio_read_leb(pbc);
|
|
for (int j = 0; j < nb_layouts; j++) {
|
|
AVIAMFSubmixLayout *submix_layout;
|
|
int info_type;
|
|
int byte = avio_r8(pbc);
|
|
|
|
submix_layout = av_iamf_submix_add_layout(sub_mix);
|
|
if (!submix_layout) {
|
|
ret = AVERROR(ENOMEM);
|
|
goto fail;
|
|
}
|
|
|
|
submix_layout->layout_type = byte >> 6;
|
|
if (submix_layout->layout_type < AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS ||
|
|
submix_layout->layout_type > AV_IAMF_SUBMIX_LAYOUT_TYPE_BINAURAL) {
|
|
av_log(s, AV_LOG_ERROR, "Invalid Layout type %u in a submix from Mix Presentation %u\n",
|
|
submix_layout->layout_type, mix_presentation_id);
|
|
ret = AVERROR_INVALIDDATA;
|
|
goto fail;
|
|
}
|
|
if (submix_layout->layout_type == 2) {
|
|
int sound_system;
|
|
sound_system = (byte >> 2) & 0xF;
|
|
av_channel_layout_copy(&submix_layout->sound_system, &ff_iamf_sound_system_map[sound_system].layout);
|
|
}
|
|
|
|
info_type = avio_r8(pbc);
|
|
submix_layout->integrated_loudness = av_make_q(sign_extend(avio_rb16(pbc), 16), 1 << 8);
|
|
submix_layout->digital_peak = av_make_q(sign_extend(avio_rb16(pbc), 16), 1 << 8);
|
|
|
|
if (info_type & 1)
|
|
submix_layout->true_peak = av_make_q(sign_extend(avio_rb16(pbc), 16), 1 << 8);
|
|
if (info_type & 2) {
|
|
unsigned int num_anchored_loudness = avio_r8(pbc);
|
|
|
|
for (int k = 0; k < num_anchored_loudness; k++) {
|
|
unsigned int anchor_element = avio_r8(pbc);
|
|
AVRational anchored_loudness = av_make_q(sign_extend(avio_rb16(pbc), 16), 1 << 8);
|
|
if (anchor_element == IAMF_ANCHOR_ELEMENT_DIALOGUE)
|
|
submix_layout->dialogue_anchored_loudness = anchored_loudness;
|
|
else if (anchor_element <= IAMF_ANCHOR_ELEMENT_ALBUM)
|
|
submix_layout->album_anchored_loudness = anchored_loudness;
|
|
else
|
|
av_log(s, AV_LOG_DEBUG, "Unknown anchor_element. Ignoring\n");
|
|
}
|
|
}
|
|
|
|
if (info_type & 0xFC) {
|
|
unsigned int info_type_size = ffio_read_leb(pbc);
|
|
avio_skip(pbc, info_type_size);
|
|
}
|
|
}
|
|
}
|
|
|
|
c->mix_presentations[c->nb_mix_presentations++] = mix_presentation;
|
|
|
|
len -= avio_tell(pbc);
|
|
if (len)
|
|
av_log(s, AV_LOG_WARNING, "Underread in mix_presentation_obu. %d bytes left at the end\n", len);
|
|
|
|
ret = 0;
|
|
fail:
|
|
av_free(buf);
|
|
if (ret < 0)
|
|
ff_iamf_free_mix_presentation(&mix_presentation);
|
|
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;
|
|
|
|
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;
|
|
case IAMF_OBU_IA_AUDIO_ELEMENT:
|
|
ret = audio_element_obu(log_ctx, c, pb, obu_size);
|
|
break;
|
|
case IAMF_OBU_IA_MIX_PRESENTATION:
|
|
ret = mix_presentation_obu(log_ctx, c, pb, obu_size);
|
|
break;
|
|
case IAMF_OBU_IA_TEMPORAL_DELIMITER:
|
|
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;
|
|
}
|