1
0
mirror of https://github.com/FFmpeg/FFmpeg.git synced 2024-11-26 19:01:44 +02:00
FFmpeg/libavformat/iamf_writer.c
Andreas Rheinhardt 18af922c53 avformat/iamf: Don't mix ownership and non-ownership pointers
IAMFAudioElement and IAMFMixPresentation currently contain
pointers to independently allocated objects that are sometimes
owned by said structures and sometimes not.

More precisely, upon success the demuxer transfers ownership
of these other objects newly created AVStreamGroups, but it
keeps its pointers. iamf_read_close() therefore always resets
these pointers (because the cleanup code always treats them
as ownership pointers). This leads to memory leaks in case
iamf_read_header() without having attached all of these
objects to stream groups.

The muxer has a similar issue: It also clears these pointers
(pointing to objects owned by stream groups created by the user)
in its deinit function.

This commit fixes this memleak by explicitly adding non-ownership
pointers; this also allows to remove the code to reset the
ownership pointers.

Reviewed-by: James Almer <jamrial@gmail.com>
Signed-off-by: Andreas Rheinhardt <andreas.rheinhardt@outlook.com>
2024-02-19 23:30:00 +01:00

835 lines
31 KiB
C

/*
* Immersive Audio Model and Formats muxing helpers and structs
* 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/channel_layout.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/iamf.h"
#include "libavutil/mem.h"
#include "libavcodec/get_bits.h"
#include "libavcodec/put_bits.h"
#include "avformat.h"
#include "avio_internal.h"
#include "iamf.h"
#include "iamf_writer.h"
static int update_extradata(IAMFCodecConfig *codec_config)
{
GetBitContext gb;
PutBitContext pb;
int ret;
switch(codec_config->codec_id) {
case AV_CODEC_ID_OPUS:
if (codec_config->extradata_size < 19)
return AVERROR_INVALIDDATA;
codec_config->extradata_size -= 8;
memmove(codec_config->extradata, codec_config->extradata + 8, codec_config->extradata_size);
AV_WB8(codec_config->extradata + 1, 2); // set channels to stereo
break;
case AV_CODEC_ID_FLAC: {
uint8_t buf[13];
init_put_bits(&pb, buf, sizeof(buf));
ret = init_get_bits8(&gb, codec_config->extradata, codec_config->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, 1); // set channels to stereo
ret = put_bits_left(&pb);
put_bits(&pb, ret, get_bits(&gb, ret));
flush_put_bits(&pb);
memcpy(codec_config->extradata, buf, sizeof(buf));
break;
}
default:
break;
}
return 0;
}
static int fill_codec_config(IAMFContext *iamf, const AVStreamGroup *stg,
IAMFCodecConfig *codec_config)
{
const AVStream *st = stg->streams[0];
IAMFCodecConfig **tmp;
int j, ret = 0;
codec_config->codec_id = st->codecpar->codec_id;
codec_config->sample_rate = st->codecpar->sample_rate;
codec_config->codec_tag = st->codecpar->codec_tag;
codec_config->nb_samples = st->codecpar->frame_size;
codec_config->seek_preroll = st->codecpar->seek_preroll;
if (st->codecpar->extradata_size) {
codec_config->extradata = av_memdup(st->codecpar->extradata, st->codecpar->extradata_size);
if (!codec_config->extradata)
return AVERROR(ENOMEM);
codec_config->extradata_size = st->codecpar->extradata_size;
ret = update_extradata(codec_config);
if (ret < 0)
goto fail;
}
for (j = 0; j < iamf->nb_codec_configs; j++) {
if (!memcmp(iamf->codec_configs[j], codec_config, offsetof(IAMFCodecConfig, extradata)) &&
(!codec_config->extradata_size || !memcmp(iamf->codec_configs[j]->extradata,
codec_config->extradata, codec_config->extradata_size)))
break;
}
if (j < iamf->nb_codec_configs) {
av_free(iamf->codec_configs[j]->extradata);
av_free(iamf->codec_configs[j]);
iamf->codec_configs[j] = codec_config;
return j;
}
tmp = av_realloc_array(iamf->codec_configs, iamf->nb_codec_configs + 1, sizeof(*iamf->codec_configs));
if (!tmp) {
ret = AVERROR(ENOMEM);
goto fail;
}
iamf->codec_configs = tmp;
iamf->codec_configs[iamf->nb_codec_configs] = codec_config;
codec_config->codec_config_id = iamf->nb_codec_configs;
return iamf->nb_codec_configs++;
fail:
av_freep(&codec_config->extradata);
return ret;
}
static IAMFParamDefinition *add_param_definition(IAMFContext *iamf, AVIAMFParamDefinition *param,
const IAMFAudioElement *audio_element, void *log_ctx)
{
IAMFParamDefinition **tmp, *param_definition;
IAMFCodecConfig *codec_config = NULL;
tmp = av_realloc_array(iamf->param_definitions, iamf->nb_param_definitions + 1,
sizeof(*iamf->param_definitions));
if (!tmp)
return NULL;
iamf->param_definitions = tmp;
if (audio_element)
codec_config = iamf->codec_configs[audio_element->codec_config_id];
if (!param->parameter_rate) {
if (!codec_config) {
av_log(log_ctx, AV_LOG_ERROR, "parameter_rate needed but not set for parameter_id %u\n",
param->parameter_id);
return NULL;
}
param->parameter_rate = codec_config->sample_rate;
}
if (codec_config) {
if (!param->duration)
param->duration = codec_config->nb_samples;
if (!param->constant_subblock_duration)
param->constant_subblock_duration = codec_config->nb_samples;
}
param_definition = av_mallocz(sizeof(*param_definition));
if (!param_definition)
return NULL;
param_definition->mode = !!param->duration;
param_definition->param = param;
param_definition->audio_element = audio_element;
iamf->param_definitions[iamf->nb_param_definitions++] = param_definition;
return param_definition;
}
int ff_iamf_add_audio_element(IAMFContext *iamf, const AVStreamGroup *stg, void *log_ctx)
{
const AVIAMFAudioElement *iamf_audio_element;
IAMFAudioElement **tmp, *audio_element;
IAMFCodecConfig *codec_config;
int ret;
if (stg->type != AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT)
return AVERROR(EINVAL);
iamf_audio_element = stg->params.iamf_audio_element;
if (iamf_audio_element->audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_SCENE) {
const AVIAMFLayer *layer = iamf_audio_element->layers[0];
if (iamf_audio_element->nb_layers != 1) {
av_log(log_ctx, AV_LOG_ERROR, "Invalid amount of layers for SCENE_BASED audio element. Must be 1\n");
return AVERROR(EINVAL);
}
if (layer->ch_layout.order != AV_CHANNEL_ORDER_CUSTOM &&
layer->ch_layout.order != AV_CHANNEL_ORDER_AMBISONIC) {
av_log(log_ctx, AV_LOG_ERROR, "Invalid channel layout for SCENE_BASED audio element\n");
return AVERROR(EINVAL);
}
if (layer->ambisonics_mode >= AV_IAMF_AMBISONICS_MODE_PROJECTION) {
av_log(log_ctx, AV_LOG_ERROR, "Unsuported ambisonics mode %d\n", layer->ambisonics_mode);
return AVERROR_PATCHWELCOME;
}
for (int i = 0; i < stg->nb_streams; i++) {
if (stg->streams[i]->codecpar->ch_layout.nb_channels > 1) {
av_log(log_ctx, AV_LOG_ERROR, "Invalid amount of channels in a stream for MONO mode ambisonics\n");
return AVERROR(EINVAL);
}
}
} else
for (int j, i = 0; i < iamf_audio_element->nb_layers; i++) {
const AVIAMFLayer *layer = iamf_audio_element->layers[i];
for (j = 0; j < FF_ARRAY_ELEMS(ff_iamf_scalable_ch_layouts); j++)
if (!av_channel_layout_compare(&layer->ch_layout, &ff_iamf_scalable_ch_layouts[j]))
break;
if (j >= FF_ARRAY_ELEMS(ff_iamf_scalable_ch_layouts)) {
av_log(log_ctx, AV_LOG_ERROR, "Unsupported channel layout in stream group #%d\n", i);
return AVERROR(EINVAL);
}
}
for (int i = 0; i < iamf->nb_audio_elements; i++) {
if (stg->id == iamf->audio_elements[i]->audio_element_id) {
av_log(log_ctx, AV_LOG_ERROR, "Duplicated Audio Element id %"PRId64"\n", stg->id);
return AVERROR(EINVAL);
}
}
codec_config = av_mallocz(sizeof(*codec_config));
if (!codec_config)
return AVERROR(ENOMEM);
ret = fill_codec_config(iamf, stg, codec_config);
if (ret < 0) {
av_free(codec_config);
return ret;
}
audio_element = av_mallocz(sizeof(*audio_element));
if (!audio_element)
return AVERROR(ENOMEM);
audio_element->celement = stg->params.iamf_audio_element;
audio_element->audio_element_id = stg->id;
audio_element->codec_config_id = ret;
audio_element->substreams = av_calloc(stg->nb_streams, sizeof(*audio_element->substreams));
if (!audio_element->substreams)
return AVERROR(ENOMEM);
audio_element->nb_substreams = stg->nb_streams;
audio_element->layers = av_calloc(iamf_audio_element->nb_layers, sizeof(*audio_element->layers));
if (!audio_element->layers)
return AVERROR(ENOMEM);
for (int i = 0, j = 0; i < iamf_audio_element->nb_layers; i++) {
int nb_channels = iamf_audio_element->layers[i]->ch_layout.nb_channels;
IAMFLayer *layer = &audio_element->layers[i];
if (i)
nb_channels -= iamf_audio_element->layers[i - 1]->ch_layout.nb_channels;
for (; nb_channels > 0 && j < stg->nb_streams; j++) {
const AVStream *st = stg->streams[j];
IAMFSubStream *substream = &audio_element->substreams[j];
substream->audio_substream_id = st->id;
layer->substream_count++;
layer->coupled_substream_count += st->codecpar->ch_layout.nb_channels == 2;
nb_channels -= st->codecpar->ch_layout.nb_channels;
}
if (nb_channels) {
av_log(log_ctx, AV_LOG_ERROR, "Invalid channel count across substreams in layer %u from stream group %u\n",
i, stg->index);
return AVERROR(EINVAL);
}
}
if (iamf_audio_element->demixing_info) {
AVIAMFParamDefinition *param = iamf_audio_element->demixing_info;
IAMFParamDefinition *param_definition = ff_iamf_get_param_definition(iamf, param->parameter_id);
if (param->nb_subblocks != 1) {
av_log(log_ctx, AV_LOG_ERROR, "nb_subblocks in demixing_info for stream group %u is not 1\n", stg->index);
return AVERROR(EINVAL);
}
if (!param_definition) {
param_definition = add_param_definition(iamf, param, audio_element, log_ctx);
if (!param_definition)
return AVERROR(ENOMEM);
}
}
if (iamf_audio_element->recon_gain_info) {
AVIAMFParamDefinition *param = iamf_audio_element->recon_gain_info;
IAMFParamDefinition *param_definition = ff_iamf_get_param_definition(iamf, param->parameter_id);
if (param->nb_subblocks != 1) {
av_log(log_ctx, AV_LOG_ERROR, "nb_subblocks in recon_gain_info for stream group %u is not 1\n", stg->index);
return AVERROR(EINVAL);
}
if (!param_definition) {
param_definition = add_param_definition(iamf, param, audio_element, log_ctx);
if (!param_definition)
return AVERROR(ENOMEM);
}
}
tmp = av_realloc_array(iamf->audio_elements, iamf->nb_audio_elements + 1, sizeof(*iamf->audio_elements));
if (!tmp)
return AVERROR(ENOMEM);
iamf->audio_elements = tmp;
iamf->audio_elements[iamf->nb_audio_elements++] = audio_element;
return 0;
}
int ff_iamf_add_mix_presentation(IAMFContext *iamf, const AVStreamGroup *stg, void *log_ctx)
{
IAMFMixPresentation **tmp, *mix_presentation;
if (stg->type != AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION)
return AVERROR(EINVAL);
for (int i = 0; i < iamf->nb_mix_presentations; i++) {
if (stg->id == iamf->mix_presentations[i]->mix_presentation_id) {
av_log(log_ctx, AV_LOG_ERROR, "Duplicate Mix Presentation id %"PRId64"\n", stg->id);
return AVERROR(EINVAL);
}
}
mix_presentation = av_mallocz(sizeof(*mix_presentation));
if (!mix_presentation)
return AVERROR(ENOMEM);
mix_presentation->cmix = stg->params.iamf_mix_presentation;
mix_presentation->mix_presentation_id = stg->id;
for (int i = 0; i < mix_presentation->cmix->nb_submixes; i++) {
const AVIAMFSubmix *submix = mix_presentation->cmix->submixes[i];
AVIAMFParamDefinition *param = submix->output_mix_config;
IAMFParamDefinition *param_definition;
if (!param) {
av_log(log_ctx, AV_LOG_ERROR, "output_mix_config is not present in submix %u from "
"Mix Presentation ID %"PRId64"\n", i, stg->id);
return AVERROR(EINVAL);
}
param_definition = ff_iamf_get_param_definition(iamf, param->parameter_id);
if (!param_definition) {
param_definition = add_param_definition(iamf, param, NULL, log_ctx);
if (!param_definition)
return AVERROR(ENOMEM);
}
for (int j = 0; j < submix->nb_elements; j++) {
const AVIAMFSubmixElement *element = submix->elements[j];
param = element->element_mix_config;
if (!param) {
av_log(log_ctx, AV_LOG_ERROR, "element_mix_config is not present for element %u in submix %u from "
"Mix Presentation ID %"PRId64"\n", j, i, stg->id);
return AVERROR(EINVAL);
}
param_definition = ff_iamf_get_param_definition(iamf, param->parameter_id);
if (!param_definition) {
param_definition = add_param_definition(iamf, param, NULL, log_ctx);
if (!param_definition)
return AVERROR(ENOMEM);
}
}
}
tmp = av_realloc_array(iamf->mix_presentations, iamf->nb_mix_presentations + 1, sizeof(*iamf->mix_presentations));
if (!tmp)
return AVERROR(ENOMEM);
iamf->mix_presentations = tmp;
iamf->mix_presentations[iamf->nb_mix_presentations++] = mix_presentation;
return 0;
}
static int iamf_write_codec_config(const IAMFContext *iamf,
const IAMFCodecConfig *codec_config,
AVIOContext *pb)
{
uint8_t header[MAX_IAMF_OBU_HEADER_SIZE];
AVIOContext *dyn_bc;
uint8_t *dyn_buf = NULL;
PutBitContext pbc;
int dyn_size;
int ret = avio_open_dyn_buf(&dyn_bc);
if (ret < 0)
return ret;
ffio_write_leb(dyn_bc, codec_config->codec_config_id);
avio_wl32(dyn_bc, codec_config->codec_tag);
ffio_write_leb(dyn_bc, codec_config->nb_samples);
avio_wb16(dyn_bc, codec_config->seek_preroll);
switch(codec_config->codec_id) {
case AV_CODEC_ID_OPUS:
avio_write(dyn_bc, codec_config->extradata, codec_config->extradata_size);
break;
case AV_CODEC_ID_AAC:
return AVERROR_PATCHWELCOME;
case AV_CODEC_ID_FLAC:
avio_w8(dyn_bc, 0x80);
avio_wb24(dyn_bc, codec_config->extradata_size);
avio_write(dyn_bc, codec_config->extradata, codec_config->extradata_size);
break;
case AV_CODEC_ID_PCM_S16LE:
avio_w8(dyn_bc, 0);
avio_w8(dyn_bc, 16);
avio_wb32(dyn_bc, codec_config->sample_rate);
break;
case AV_CODEC_ID_PCM_S24LE:
avio_w8(dyn_bc, 0);
avio_w8(dyn_bc, 24);
avio_wb32(dyn_bc, codec_config->sample_rate);
break;
case AV_CODEC_ID_PCM_S32LE:
avio_w8(dyn_bc, 0);
avio_w8(dyn_bc, 32);
avio_wb32(dyn_bc, codec_config->sample_rate);
break;
case AV_CODEC_ID_PCM_S16BE:
avio_w8(dyn_bc, 1);
avio_w8(dyn_bc, 16);
avio_wb32(dyn_bc, codec_config->sample_rate);
break;
case AV_CODEC_ID_PCM_S24BE:
avio_w8(dyn_bc, 1);
avio_w8(dyn_bc, 24);
avio_wb32(dyn_bc, codec_config->sample_rate);
break;
case AV_CODEC_ID_PCM_S32BE:
avio_w8(dyn_bc, 1);
avio_w8(dyn_bc, 32);
avio_wb32(dyn_bc, codec_config->sample_rate);
break;
default:
break;
}
init_put_bits(&pbc, header, sizeof(header));
put_bits(&pbc, 5, IAMF_OBU_IA_CODEC_CONFIG);
put_bits(&pbc, 3, 0);
flush_put_bits(&pbc);
dyn_size = avio_get_dyn_buf(dyn_bc, &dyn_buf);
avio_write(pb, header, put_bytes_count(&pbc, 1));
ffio_write_leb(pb, dyn_size);
avio_write(pb, dyn_buf, dyn_size);
ffio_free_dyn_buf(&dyn_bc);
return 0;
}
static inline int rescale_rational(AVRational q, int b)
{
return av_clip_int16(av_rescale(q.num, b, q.den));
}
static int scalable_channel_layout_config(const IAMFAudioElement *audio_element,
AVIOContext *dyn_bc)
{
const AVIAMFAudioElement *element = audio_element->celement;
uint8_t header[MAX_IAMF_OBU_HEADER_SIZE];
PutBitContext pb;
init_put_bits(&pb, header, sizeof(header));
put_bits(&pb, 3, element->nb_layers);
put_bits(&pb, 5, 0);
flush_put_bits(&pb);
avio_write(dyn_bc, header, put_bytes_count(&pb, 1));
for (int i = 0; i < element->nb_layers; i++) {
AVIAMFLayer *layer = element->layers[i];
int layout;
for (layout = 0; layout < FF_ARRAY_ELEMS(ff_iamf_scalable_ch_layouts); layout++) {
if (!av_channel_layout_compare(&layer->ch_layout, &ff_iamf_scalable_ch_layouts[layout]))
break;
}
init_put_bits(&pb, header, sizeof(header));
put_bits(&pb, 4, layout);
put_bits(&pb, 1, !!layer->output_gain_flags);
put_bits(&pb, 1, !!(layer->flags & AV_IAMF_LAYER_FLAG_RECON_GAIN));
put_bits(&pb, 2, 0); // reserved
put_bits(&pb, 8, audio_element->layers[i].substream_count);
put_bits(&pb, 8, audio_element->layers[i].coupled_substream_count);
if (layer->output_gain_flags) {
put_bits(&pb, 6, layer->output_gain_flags);
put_bits(&pb, 2, 0);
put_bits(&pb, 16, rescale_rational(layer->output_gain, 1 << 8));
}
flush_put_bits(&pb);
avio_write(dyn_bc, header, put_bytes_count(&pb, 1));
}
return 0;
}
static int ambisonics_config(const IAMFAudioElement *audio_element,
AVIOContext *dyn_bc)
{
const AVIAMFAudioElement *element = audio_element->celement;
AVIAMFLayer *layer = element->layers[0];
ffio_write_leb(dyn_bc, 0); // ambisonics_mode
ffio_write_leb(dyn_bc, layer->ch_layout.nb_channels); // output_channel_count
ffio_write_leb(dyn_bc, audio_element->nb_substreams); // substream_count
if (layer->ch_layout.order == AV_CHANNEL_ORDER_AMBISONIC)
for (int i = 0; i < layer->ch_layout.nb_channels; i++)
avio_w8(dyn_bc, i);
else
for (int i = 0; i < layer->ch_layout.nb_channels; i++)
avio_w8(dyn_bc, layer->ch_layout.u.map[i].id);
return 0;
}
static int param_definition(const IAMFContext *iamf,
const IAMFParamDefinition *param_def,
AVIOContext *dyn_bc, void *log_ctx)
{
const AVIAMFParamDefinition *param = param_def->param;
ffio_write_leb(dyn_bc, param->parameter_id);
ffio_write_leb(dyn_bc, param->parameter_rate);
avio_w8(dyn_bc, param->duration ? 0 : 1 << 7);
if (param->duration) {
ffio_write_leb(dyn_bc, param->duration);
ffio_write_leb(dyn_bc, param->constant_subblock_duration);
if (param->constant_subblock_duration == 0) {
ffio_write_leb(dyn_bc, param->nb_subblocks);
for (int i = 0; i < param->nb_subblocks; i++) {
const void *subblock = av_iamf_param_definition_get_subblock(param, i);
switch (param->type) {
case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: {
const AVIAMFMixGain *mix = subblock;
ffio_write_leb(dyn_bc, mix->subblock_duration);
break;
}
case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: {
const AVIAMFDemixingInfo *demix = subblock;
ffio_write_leb(dyn_bc, demix->subblock_duration);
break;
}
case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: {
const AVIAMFReconGain *recon = subblock;
ffio_write_leb(dyn_bc, recon->subblock_duration);
break;
}
}
}
}
}
return 0;
}
static int iamf_write_audio_element(const IAMFContext *iamf,
const IAMFAudioElement *audio_element,
AVIOContext *pb, void *log_ctx)
{
const AVIAMFAudioElement *element = audio_element->celement;
const IAMFCodecConfig *codec_config = iamf->codec_configs[audio_element->codec_config_id];
uint8_t header[MAX_IAMF_OBU_HEADER_SIZE];
AVIOContext *dyn_bc;
uint8_t *dyn_buf = NULL;
PutBitContext pbc;
int param_definition_types = AV_IAMF_PARAMETER_DEFINITION_DEMIXING, dyn_size;
int ret = avio_open_dyn_buf(&dyn_bc);
if (ret < 0)
return ret;
ffio_write_leb(dyn_bc, audio_element->audio_element_id);
init_put_bits(&pbc, header, sizeof(header));
put_bits(&pbc, 3, element->audio_element_type);
put_bits(&pbc, 5, 0);
flush_put_bits(&pbc);
avio_write(dyn_bc, header, put_bytes_count(&pbc, 1));
ffio_write_leb(dyn_bc, audio_element->codec_config_id);
ffio_write_leb(dyn_bc, audio_element->nb_substreams);
for (int i = 0; i < audio_element->nb_substreams; i++)
ffio_write_leb(dyn_bc, audio_element->substreams[i].audio_substream_id);
if (element->nb_layers == 1)
param_definition_types &= ~AV_IAMF_PARAMETER_DEFINITION_DEMIXING;
if (element->nb_layers > 1)
param_definition_types |= AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN;
if (codec_config->codec_tag == MKTAG('f','L','a','C') ||
codec_config->codec_tag == MKTAG('i','p','c','m'))
param_definition_types &= ~AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN;
ffio_write_leb(dyn_bc, av_popcount(param_definition_types)); // num_parameters
if (param_definition_types & 1) {
const AVIAMFParamDefinition *param = element->demixing_info;
const IAMFParamDefinition *param_def;
const AVIAMFDemixingInfo *demix;
if (!param) {
av_log(log_ctx, AV_LOG_ERROR, "demixing_info needed but not set in Stream Group #%u\n",
audio_element->audio_element_id);
return AVERROR(EINVAL);
}
demix = av_iamf_param_definition_get_subblock(param, 0);
ffio_write_leb(dyn_bc, AV_IAMF_PARAMETER_DEFINITION_DEMIXING); // type
param_def = ff_iamf_get_param_definition(iamf, param->parameter_id);
ret = param_definition(iamf, param_def, dyn_bc, log_ctx);
if (ret < 0)
return ret;
avio_w8(dyn_bc, demix->dmixp_mode << 5); // dmixp_mode
avio_w8(dyn_bc, element->default_w << 4); // default_w
}
if (param_definition_types & 2) {
const AVIAMFParamDefinition *param = element->recon_gain_info;
const IAMFParamDefinition *param_def;
if (!param) {
av_log(log_ctx, AV_LOG_ERROR, "recon_gain_info needed but not set in Stream Group #%u\n",
audio_element->audio_element_id);
return AVERROR(EINVAL);
}
ffio_write_leb(dyn_bc, AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN); // type
param_def = ff_iamf_get_param_definition(iamf, param->parameter_id);
ret = param_definition(iamf, param_def, dyn_bc, log_ctx);
if (ret < 0)
return ret;
}
if (element->audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL) {
ret = scalable_channel_layout_config(audio_element, dyn_bc);
if (ret < 0)
return ret;
} else {
ret = ambisonics_config(audio_element, dyn_bc);
if (ret < 0)
return ret;
}
init_put_bits(&pbc, header, sizeof(header));
put_bits(&pbc, 5, IAMF_OBU_IA_AUDIO_ELEMENT);
put_bits(&pbc, 3, 0);
flush_put_bits(&pbc);
dyn_size = avio_get_dyn_buf(dyn_bc, &dyn_buf);
avio_write(pb, header, put_bytes_count(&pbc, 1));
ffio_write_leb(pb, dyn_size);
avio_write(pb, dyn_buf, dyn_size);
ffio_free_dyn_buf(&dyn_bc);
return 0;
}
static int iamf_write_mixing_presentation(const IAMFContext *iamf,
const IAMFMixPresentation *mix_presentation,
AVIOContext *pb, void *log_ctx)
{
uint8_t header[MAX_IAMF_OBU_HEADER_SIZE];
const AVIAMFMixPresentation *mix = mix_presentation->cmix;
const AVDictionaryEntry *tag = NULL;
PutBitContext pbc;
AVIOContext *dyn_bc;
uint8_t *dyn_buf = NULL;
int dyn_size;
int ret = avio_open_dyn_buf(&dyn_bc);
if (ret < 0)
return ret;
ffio_write_leb(dyn_bc, mix_presentation->mix_presentation_id); // mix_presentation_id
ffio_write_leb(dyn_bc, av_dict_count(mix->annotations)); // count_label
while ((tag = av_dict_iterate(mix->annotations, tag)))
avio_put_str(dyn_bc, tag->key);
while ((tag = av_dict_iterate(mix->annotations, tag)))
avio_put_str(dyn_bc, tag->value);
ffio_write_leb(dyn_bc, mix->nb_submixes);
for (int i = 0; i < mix->nb_submixes; i++) {
const AVIAMFSubmix *sub_mix = mix->submixes[i];
const IAMFParamDefinition *param_def;
ffio_write_leb(dyn_bc, sub_mix->nb_elements);
for (int j = 0; j < sub_mix->nb_elements; j++) {
const IAMFAudioElement *audio_element = NULL;
const AVIAMFSubmixElement *submix_element = sub_mix->elements[j];
for (int k = 0; k < iamf->nb_audio_elements; k++)
if (iamf->audio_elements[k]->audio_element_id == submix_element->audio_element_id) {
audio_element = iamf->audio_elements[k];
break;
}
av_assert0(audio_element);
ffio_write_leb(dyn_bc, submix_element->audio_element_id);
if (av_dict_count(submix_element->annotations) != av_dict_count(mix->annotations)) {
av_log(log_ctx, AV_LOG_ERROR, "Inconsistent amount of labels in submix %d from Mix Presentation id #%u\n",
j, audio_element->audio_element_id);
return AVERROR(EINVAL);
}
while ((tag = av_dict_iterate(submix_element->annotations, tag)))
avio_put_str(dyn_bc, tag->value);
init_put_bits(&pbc, header, sizeof(header));
put_bits(&pbc, 2, submix_element->headphones_rendering_mode);
put_bits(&pbc, 6, 0); // reserved
flush_put_bits(&pbc);
avio_write(dyn_bc, header, put_bytes_count(&pbc, 1));
ffio_write_leb(dyn_bc, 0); // rendering_config_extension_size
param_def = ff_iamf_get_param_definition(iamf, submix_element->element_mix_config->parameter_id);
ret = param_definition(iamf, param_def, dyn_bc, log_ctx);
if (ret < 0)
return ret;
avio_wb16(dyn_bc, rescale_rational(submix_element->default_mix_gain, 1 << 8));
}
param_def = ff_iamf_get_param_definition(iamf, sub_mix->output_mix_config->parameter_id);
ret = param_definition(iamf, param_def, dyn_bc, log_ctx);
if (ret < 0)
return ret;
avio_wb16(dyn_bc, rescale_rational(sub_mix->default_mix_gain, 1 << 8));
ffio_write_leb(dyn_bc, sub_mix->nb_layouts); // nb_layouts
for (int i = 0; i < sub_mix->nb_layouts; i++) {
const AVIAMFSubmixLayout *submix_layout = sub_mix->layouts[i];
int layout, info_type;
int dialogue = submix_layout->dialogue_anchored_loudness.num &&
submix_layout->dialogue_anchored_loudness.den;
int album = submix_layout->album_anchored_loudness.num &&
submix_layout->album_anchored_loudness.den;
if (submix_layout->layout_type == AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS) {
for (layout = 0; layout < FF_ARRAY_ELEMS(ff_iamf_sound_system_map); layout++) {
if (!av_channel_layout_compare(&submix_layout->sound_system, &ff_iamf_sound_system_map[layout].layout))
break;
}
if (layout == FF_ARRAY_ELEMS(ff_iamf_sound_system_map)) {
av_log(log_ctx, AV_LOG_ERROR, "Invalid Sound System value in a submix\n");
return AVERROR(EINVAL);
}
}
init_put_bits(&pbc, header, sizeof(header));
put_bits(&pbc, 2, submix_layout->layout_type); // layout_type
if (submix_layout->layout_type == AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS) {
put_bits(&pbc, 4, ff_iamf_sound_system_map[layout].id); // sound_system
put_bits(&pbc, 2, 0); // reserved
} else
put_bits(&pbc, 6, 0); // reserved
flush_put_bits(&pbc);
avio_write(dyn_bc, header, put_bytes_count(&pbc, 1));
info_type = (submix_layout->true_peak.num && submix_layout->true_peak.den);
info_type |= (dialogue || album) << 1;
avio_w8(dyn_bc, info_type);
avio_wb16(dyn_bc, rescale_rational(submix_layout->integrated_loudness, 1 << 8));
avio_wb16(dyn_bc, rescale_rational(submix_layout->digital_peak, 1 << 8));
if (info_type & 1)
avio_wb16(dyn_bc, rescale_rational(submix_layout->true_peak, 1 << 8));
if (info_type & 2) {
avio_w8(dyn_bc, dialogue + album); // num_anchored_loudness
if (dialogue) {
avio_w8(dyn_bc, IAMF_ANCHOR_ELEMENT_DIALOGUE);
avio_wb16(dyn_bc, rescale_rational(submix_layout->dialogue_anchored_loudness, 1 << 8));
}
if (album) {
avio_w8(dyn_bc, IAMF_ANCHOR_ELEMENT_ALBUM);
avio_wb16(dyn_bc, rescale_rational(submix_layout->album_anchored_loudness, 1 << 8));
}
}
}
}
init_put_bits(&pbc, header, sizeof(header));
put_bits(&pbc, 5, IAMF_OBU_IA_MIX_PRESENTATION);
put_bits(&pbc, 3, 0);
flush_put_bits(&pbc);
dyn_size = avio_get_dyn_buf(dyn_bc, &dyn_buf);
avio_write(pb, header, put_bytes_count(&pbc, 1));
ffio_write_leb(pb, dyn_size);
avio_write(pb, dyn_buf, dyn_size);
ffio_free_dyn_buf(&dyn_bc);
return 0;
}
int ff_iamf_write_descriptors(const IAMFContext *iamf, AVIOContext *pb, void *log_ctx)
{
int ret;
// Sequence Header
avio_w8(pb, IAMF_OBU_IA_SEQUENCE_HEADER << 3);
ffio_write_leb(pb, 6);
avio_wb32(pb, MKBETAG('i','a','m','f'));
avio_w8(pb, iamf->nb_audio_elements > 1); // primary_profile
avio_w8(pb, iamf->nb_audio_elements > 1); // additional_profile
for (int i = 0; i < iamf->nb_codec_configs; i++) {
ret = iamf_write_codec_config(iamf, iamf->codec_configs[i], pb);
if (ret < 0)
return ret;
}
for (int i = 0; i < iamf->nb_audio_elements; i++) {
ret = iamf_write_audio_element(iamf, iamf->audio_elements[i], pb, log_ctx);
if (ret < 0)
return ret;
}
for (int i = 0; i < iamf->nb_mix_presentations; i++) {
ret = iamf_write_mixing_presentation(iamf, iamf->mix_presentations[i], pb, log_ctx);
if (ret < 0)
return ret;
}
return 0;
}