* [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf
@ 2024-01-30 17:32 James Almer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 2/7 v2] avformat/demux: support inserting bitstream filters in demuxing scenarios James Almer
` (6 more replies)
0 siblings, 7 replies; 19+ messages in thread
From: James Almer @ 2024-01-30 17:32 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/bitstream_filters.c | 1 +
libavcodec/bsf/Makefile | 1 +
libavcodec/bsf/iamf_stream_split_bsf.c | 813 +++++++++++++++++++++++++
3 files changed, 815 insertions(+)
create mode 100644 libavcodec/bsf/iamf_stream_split_bsf.c
diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c
index 1e9a676a3d..640b821413 100644
--- a/libavcodec/bitstream_filters.c
+++ b/libavcodec/bitstream_filters.c
@@ -42,6 +42,7 @@ extern const FFBitStreamFilter ff_h264_redundant_pps_bsf;
extern const FFBitStreamFilter ff_hapqa_extract_bsf;
extern const FFBitStreamFilter ff_hevc_metadata_bsf;
extern const FFBitStreamFilter ff_hevc_mp4toannexb_bsf;
+extern const FFBitStreamFilter ff_iamf_stream_split_bsf;
extern const FFBitStreamFilter ff_imx_dump_header_bsf;
extern const FFBitStreamFilter ff_media100_to_mjpegb_bsf;
extern const FFBitStreamFilter ff_mjpeg2jpeg_bsf;
diff --git a/libavcodec/bsf/Makefile b/libavcodec/bsf/Makefile
index 7831b0f2aa..80dcdf94fb 100644
--- a/libavcodec/bsf/Makefile
+++ b/libavcodec/bsf/Makefile
@@ -20,6 +20,7 @@ OBJS-$(CONFIG_H264_REDUNDANT_PPS_BSF) += bsf/h264_redundant_pps.o
OBJS-$(CONFIG_HAPQA_EXTRACT_BSF) += bsf/hapqa_extract.o
OBJS-$(CONFIG_HEVC_METADATA_BSF) += bsf/h265_metadata.o
OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF) += bsf/hevc_mp4toannexb.o
+OBJS-$(CONFIG_IAMF_STREAM_MERGE_BSF) += bsf/iamf_stream_merge_bsf.o
OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += bsf/imx_dump_header.o
OBJS-$(CONFIG_MEDIA100_TO_MJPEGB_BSF) += bsf/media100_to_mjpegb.o
OBJS-$(CONFIG_MJPEG2JPEG_BSF) += bsf/mjpeg2jpeg.o
diff --git a/libavcodec/bsf/iamf_stream_split_bsf.c b/libavcodec/bsf/iamf_stream_split_bsf.c
new file mode 100644
index 0000000000..01193801d7
--- /dev/null
+++ b/libavcodec/bsf/iamf_stream_split_bsf.c
@@ -0,0 +1,813 @@
+/*
+ * 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 <stdint.h>
+#include <stddef.h>
+
+#include "libavutil/dict.h"
+#include "libavutil/opt.h"
+#include "libavformat/iamf.h"
+#include "bsf.h"
+#include "bsf_internal.h"
+#include "get_bits.h"
+
+typedef struct ParamDefinition {
+ AVIAMFParamDefinition *param;
+ size_t param_size;
+ int mode;
+ int recon_gain_present_bitmask;
+} ParamDefinition;
+
+typedef struct IAMFSplitContext {
+ AVClass *class;
+ AVPacket *buffer_pkt;
+
+ ParamDefinition *param_definitions;
+ unsigned int nb_param_definitions;
+
+ unsigned int *ids;
+ int nb_ids;
+
+ // AVOptions
+ int first_index;
+
+ // Packet side data
+ AVIAMFParamDefinition *mix;
+ size_t mix_size;
+ AVIAMFParamDefinition *demix;
+ size_t demix_size;
+ AVIAMFParamDefinition *recon;
+ size_t recon_size;
+} IAMFSplitContext;
+
+static int param_parse(AVBSFContext *ctx, GetBitContext *gb,
+ unsigned int type,
+ ParamDefinition **out)
+{
+ IAMFSplitContext *const c = ctx->priv_data;
+ ParamDefinition *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 = get_leb(gb);
+
+ 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 = get_leb(gb);
+ mode = get_bits(gb, 8) >> 7;
+
+ if (mode == 0) {
+ duration = get_leb(gb);
+ constant_subblock_duration = get_leb(gb);
+ if (constant_subblock_duration == 0) {
+ nb_subblocks = get_leb(gb);
+ } 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++) {
+ if (constant_subblock_duration == 0)
+ get_leb(gb); // subblock_duration
+
+ switch (type) {
+ case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN:
+ break;
+ case AV_IAMF_PARAMETER_DEFINITION_DEMIXING:
+ skip_bits(gb, 8); // dmixp_mode
+ break;
+ case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN:
+ 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(ctx, AV_LOG_ERROR, "Incosistent parameters for parameter_id %u\n", parameter_id);
+ av_free(param);
+ return AVERROR_INVALIDDATA;
+ }
+ av_freep(¶m);
+ } else {
+ ParamDefinition *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 = &c->param_definitions[c->nb_param_definitions++];
+ param_definition->param = param;
+ param_definition->mode = !mode;
+ param_definition->param_size = param_size;
+ }
+ if (out)
+ *out = param_definition;
+
+ return 0;
+}
+
+static int scalable_channel_layout_config(AVBSFContext *ctx, GetBitContext *gb,
+ ParamDefinition *recon_gain)
+{
+ int nb_layers;
+
+ nb_layers = get_bits(gb, 3);
+ skip_bits(gb, 5); //reserved
+
+ if (nb_layers > 6)
+ return AVERROR_INVALIDDATA;
+
+ for (int i = 0; i < nb_layers; i++) {
+ int output_gain_is_present_flag, recon_gain_is_present;
+
+ skip_bits(gb, 4); // loudspeaker_layout
+ output_gain_is_present_flag = get_bits1(gb);
+ recon_gain_is_present = get_bits1(gb);
+ if (recon_gain)
+ recon_gain->recon_gain_present_bitmask |= recon_gain_is_present << i;
+ skip_bits(gb, 2); // reserved
+ skip_bits(gb, 8); // substream_count
+ skip_bits(gb, 8); // coupled_substream_count
+ if (output_gain_is_present_flag) {
+ skip_bits(gb, 8); // output_gain_flags & reserved
+ skip_bits(gb, 16); // output_gain
+ }
+ }
+
+ return 0;
+}
+
+static int audio_element_obu(AVBSFContext *ctx, uint8_t *buf, unsigned size)
+{
+ IAMFSplitContext *const c = ctx->priv_data;
+ GetBitContext gb;
+ ParamDefinition *recon_gain = NULL;
+ unsigned audio_element_type;
+ unsigned num_substreams, num_parameters;
+ int ret;
+
+ ret = init_get_bits8(&gb, buf, size);
+ if (ret < 0)
+ return ret;
+
+ get_leb(&gb); // audio_element_id
+ audio_element_type = get_bits(&gb, 3);
+ skip_bits(&gb, 5); // reserved
+
+ get_leb(&gb); // codec_config_id
+ num_substreams = get_leb(&gb);
+ for (unsigned i = 0; i < num_substreams; i++) {
+ unsigned *audio_substream_id = av_dynarray2_add((void **)&c->ids, &c->nb_ids,
+ sizeof(*c->ids), NULL);
+ if (!audio_substream_id)
+ return AVERROR(ENOMEM);
+
+ *audio_substream_id = get_leb(&gb);
+ }
+
+ num_parameters = get_leb(&gb);
+ if (num_parameters && audio_element_type != 0) {
+ av_log(ctx, AV_LOG_ERROR, "Audio Element parameter count %u is invalid"
+ " for Scene representations\n", num_parameters);
+ return AVERROR_INVALIDDATA;
+ }
+
+ for (int i = 0; i < num_parameters; i++) {
+ unsigned type = get_leb(&gb);
+
+ if (type == AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN)
+ return AVERROR_INVALIDDATA;
+ else if (type == AV_IAMF_PARAMETER_DEFINITION_DEMIXING) {
+ ret = param_parse(ctx, &gb, type, NULL);
+ if (ret < 0)
+ return ret;
+ skip_bits(&gb, 8); // default_w
+ } else if (type == AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN) {
+ ret = param_parse(ctx, &gb, type, &recon_gain);
+ if (ret < 0)
+ return ret;
+ } else {
+ unsigned param_definition_size = get_leb(&gb);
+ skip_bits_long(&gb, param_definition_size * 8);
+ }
+ }
+
+ if (audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL) {
+ ret = scalable_channel_layout_config(ctx, &gb, recon_gain);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int label_string(GetBitContext *gb)
+{
+ int byte;
+
+ do {
+ byte = get_bits(gb, 8);
+ } while (byte);
+
+ return 0;
+}
+
+static int mix_presentation_obu(AVBSFContext *ctx, uint8_t *buf, unsigned size)
+{
+ GetBitContext gb;
+ unsigned mix_presentation_id, count_label;
+ unsigned nb_submixes, nb_elements;
+ int ret;
+
+ ret = init_get_bits8(&gb, buf, size);
+ if (ret < 0)
+ return ret;
+
+ mix_presentation_id = get_leb(&gb);
+ count_label = get_leb(&gb);
+
+ for (int i = 0; i < count_label; i++) {
+ ret = label_string(&gb);
+ if (ret < 0)
+ return ret;
+ }
+
+ for (int i = 0; i < count_label; i++) {
+ ret = label_string(&gb);
+ if (ret < 0)
+ return ret;
+ }
+
+ nb_submixes = get_leb(&gb);
+ for (int i = 0; i < nb_submixes; i++) {
+ unsigned nb_layouts;
+
+ nb_elements = get_leb(&gb);
+
+ for (int j = 0; j < nb_elements; j++) {
+ unsigned rendering_config_extension_size;
+
+ get_leb(&gb); // audio_element_id
+ for (int k = 0; k < count_label; k++) {
+ ret = label_string(&gb);
+ if (ret < 0)
+ return ret;
+ }
+
+ skip_bits(&gb, 8); // headphones_rendering_mode & reserved
+ rendering_config_extension_size = get_leb(&gb);
+ skip_bits_long(&gb, rendering_config_extension_size * 8);
+
+ ret = param_parse(ctx, &gb, AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, NULL);
+ if (ret < 0)
+ return ret;
+ skip_bits(&gb, 16); // default_mix_gain
+ }
+
+ ret = param_parse(ctx, &gb, AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, NULL);
+ if (ret < 0)
+ return ret;
+ get_bits(&gb, 16); // default_mix_gain
+
+ nb_layouts = get_leb(&gb);
+ for (int j = 0; j < nb_layouts; j++) {
+ int info_type, layout_type;
+ int byte = get_bits(&gb, 8);
+
+ layout_type = byte >> 6;
+ if (layout_type < AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS &&
+ layout_type > AV_IAMF_SUBMIX_LAYOUT_TYPE_BINAURAL) {
+ av_log(ctx, AV_LOG_ERROR, "Invalid Layout type %u in a submix from Mix Presentation %u\n",
+ layout_type, mix_presentation_id);
+ return AVERROR_INVALIDDATA;
+ }
+
+ info_type = get_bits(&gb, 8);
+ get_bits(&gb, 16); // integrated_loudness
+ get_bits(&gb, 16); // digital_peak
+
+ if (info_type & 1)
+ get_bits(&gb, 16); // true_peak
+
+ if (info_type & 2) {
+ unsigned int num_anchored_loudness = get_bits(&gb, 8);
+
+ for (int k = 0; k < num_anchored_loudness; k++) {
+ get_bits(&gb, 8); // anchor_element
+ get_bits(&gb, 16); // anchored_loudness
+ }
+ }
+
+ if (info_type & 0xFC) {
+ unsigned int info_type_size = get_leb(&gb);
+ skip_bits_long(&gb, info_type_size * 8);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int find_idx_by_id(AVBSFContext *ctx, unsigned id)
+{
+ IAMFSplitContext *const c = ctx->priv_data;
+
+ for (int i = 0; i < c->nb_ids; i++) {
+ unsigned audio_substream_id = c->ids[i];
+
+ if (audio_substream_id == id)
+ return i;
+ }
+
+ av_log(ctx, AV_LOG_ERROR, "Invalid id %d\n", id);
+ return AVERROR_INVALIDDATA;
+}
+
+static int audio_frame_obu(AVBSFContext *ctx, enum IAMF_OBU_Type type, int *idx,
+ uint8_t *buf, int *start_pos, unsigned *size,
+ int id_in_bitstream)
+{
+ GetBitContext gb;
+ unsigned audio_substream_id;
+ int ret;
+
+ ret = init_get_bits8(&gb, buf + *start_pos, *size);
+ if (ret < 0)
+ return ret;
+
+ if (id_in_bitstream) {
+ int pos;
+ audio_substream_id = get_leb(&gb);
+ pos = get_bits_count(&gb) / 8;
+ *start_pos += pos;
+ *size -= pos;
+ } else
+ audio_substream_id = type - IAMF_OBU_IA_AUDIO_FRAME_ID0;
+
+ ret = find_idx_by_id(ctx, audio_substream_id);
+ if (ret < 0)
+ return ret;
+
+ *idx = ret;
+
+ return 0;
+}
+
+static const ParamDefinition *get_param_definition(AVBSFContext *ctx, unsigned int parameter_id)
+{
+ const IAMFSplitContext *const c = ctx->priv_data;
+ const ParamDefinition *param_definition = NULL;
+
+ 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;
+ }
+
+ return param_definition;
+}
+
+static int parameter_block_obu(AVBSFContext *ctx, uint8_t *buf, unsigned size)
+{
+ IAMFSplitContext *const c = ctx->priv_data;
+ GetBitContext gb;
+ const ParamDefinition *param_definition;
+ const AVIAMFParamDefinition *param;
+ AVIAMFParamDefinition *out_param = NULL;
+ unsigned int duration, constant_subblock_duration;
+ unsigned int nb_subblocks;
+ unsigned int parameter_id;
+ size_t out_param_size;
+ int ret;
+
+ ret = init_get_bits8(&gb, buf, size);
+ if (ret < 0)
+ return ret;
+
+ parameter_id = get_leb(&gb);
+
+ param_definition = get_param_definition(ctx, parameter_id);
+ if (!param_definition) {
+ ret = 0;
+ goto fail;
+ }
+
+ param = param_definition->param;
+ if (!param_definition->mode) {
+ duration = get_leb(&gb);
+ constant_subblock_duration = get_leb(&gb);
+ if (constant_subblock_duration == 0)
+ nb_subblocks = get_leb(&gb);
+ else
+ nb_subblocks = duration / constant_subblock_duration;
+ } else {
+ duration = param->duration;
+ constant_subblock_duration = param->constant_subblock_duration;
+ nb_subblocks = param->nb_subblocks;
+ if (!nb_subblocks)
+ nb_subblocks = duration / constant_subblock_duration;
+ }
+
+ out_param = av_iamf_param_definition_alloc(param->type, nb_subblocks, &out_param_size);
+ if (!out_param) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ out_param->parameter_id = param->parameter_id;
+ out_param->type = param->type;
+ out_param->parameter_rate = param->parameter_rate;
+ out_param->duration = duration;
+ out_param->constant_subblock_duration = constant_subblock_duration;
+ out_param->nb_subblocks = nb_subblocks;
+
+ for (int i = 0; i < nb_subblocks; i++) {
+ void *subblock = av_iamf_param_definition_get_subblock(out_param, i);
+ unsigned int subblock_duration = constant_subblock_duration;
+
+ if (!param_definition->mode && !constant_subblock_duration)
+ subblock_duration = get_leb(&gb);
+
+ switch (param->type) {
+ case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: {
+ AVIAMFMixGain *mix = subblock;
+
+ mix->animation_type = get_leb(&gb);
+ if (mix->animation_type > AV_IAMF_ANIMATION_TYPE_BEZIER) {
+ ret = 0;
+ av_free(out_param);
+ goto fail;
+ }
+
+ mix->start_point_value = av_make_q(sign_extend(get_bits(&gb, 16), 16), 1 << 8);
+ if (mix->animation_type >= AV_IAMF_ANIMATION_TYPE_LINEAR)
+ mix->end_point_value = av_make_q(sign_extend(get_bits(&gb, 16), 16), 1 << 8);
+ if (mix->animation_type == AV_IAMF_ANIMATION_TYPE_BEZIER) {
+ mix->control_point_value = av_make_q(sign_extend(get_bits(&gb, 16), 16), 1 << 8);
+ mix->control_point_relative_time = av_make_q(get_bits(&gb, 8), 1 << 8);
+ }
+ mix->subblock_duration = subblock_duration;
+ break;
+ }
+ case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: {
+ AVIAMFDemixingInfo *demix = subblock;
+
+ demix->dmixp_mode = get_bits(&gb, 3);
+ skip_bits(&gb, 5); // reserved
+ demix->subblock_duration = subblock_duration;
+ break;
+ }
+ case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: {
+ AVIAMFReconGain *recon = subblock;
+
+ for (int i = 0; i < 6; i++) {
+ if (param_definition->recon_gain_present_bitmask & (1 << i)) {
+ unsigned int recon_gain_flags = get_leb(&gb);
+ unsigned int bitcount = 7 + 5 * !!(recon_gain_flags & 0x80);
+ recon_gain_flags = (recon_gain_flags & 0x7F) | ((recon_gain_flags & 0xFF00) >> 1);
+ for (int j = 0; j < bitcount; j++) {
+ if (recon_gain_flags & (1 << j))
+ recon->recon_gain[i][j] = get_bits(&gb, 8);
+ }
+ }
+ }
+ recon->subblock_duration = subblock_duration;
+ break;
+ }
+ default:
+ av_assert0(0);
+ }
+ }
+
+ switch (param->type) {
+ case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN:
+ av_free(c->mix);
+ c->mix = out_param;
+ c->mix_size = out_param_size;
+ break;
+ case AV_IAMF_PARAMETER_DEFINITION_DEMIXING:
+ av_free(c->demix);
+ c->demix = out_param;
+ c->demix_size = out_param_size;
+ break;
+ case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN:
+ av_free(c->recon);
+ c->recon = out_param;
+ c->recon_size = out_param_size;
+ break;
+ default:
+ av_assert0(0);
+ }
+
+ ret = 0;
+fail:
+ if (ret < 0)
+ av_free(out_param);
+
+ return ret;
+}
+
+static int 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 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) {
+ *skip_samples = get_leb(&gb); // num_samples_to_trim_at_end
+ *discard_padding = get_leb(&gb); // num_samples_to_trim_at_start
+ }
+
+ if (extension_flag) {
+ unsigned 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;
+}
+
+static int iamf_stream_split_filter(AVBSFContext *ctx, AVPacket *out)
+{
+ IAMFSplitContext *const c = ctx->priv_data;
+ int ret = 0;
+
+ if (!c->buffer_pkt->data) {
+ ret = ff_bsf_get_packet_ref(ctx, c->buffer_pkt);
+ if (ret < 0)
+ return ret;
+ }
+
+ while (1) {
+ enum IAMF_OBU_Type type;
+ unsigned skip_samples = 0, discard_padding = 0, obu_size;
+ int len, start_pos, idx;
+
+ len = iamf_parse_obu_header(c->buffer_pkt->data,
+ c->buffer_pkt->size,
+ &obu_size, &start_pos, &type,
+ &skip_samples, &discard_padding);
+ if (len < 0) {
+ av_log(ctx, AV_LOG_ERROR, "Failed to read obu\n");
+ ret = len;
+ goto fail;
+ }
+
+ if (type >= IAMF_OBU_IA_AUDIO_FRAME && type <= IAMF_OBU_IA_AUDIO_FRAME_ID17) {
+ ret = audio_frame_obu(ctx, type, &idx,
+ c->buffer_pkt->data, &start_pos,
+ &obu_size,
+ type == IAMF_OBU_IA_AUDIO_FRAME);
+ if (ret < 0)
+ goto fail;
+ } else {
+ switch (type) {
+ case IAMF_OBU_IA_AUDIO_ELEMENT:
+ ret = audio_element_obu(ctx, c->buffer_pkt->data + start_pos, obu_size);
+ if (ret < 0)
+ goto fail;
+ break;
+ case IAMF_OBU_IA_MIX_PRESENTATION:
+ ret = mix_presentation_obu(ctx, c->buffer_pkt->data + start_pos, obu_size);
+ if (ret < 0)
+ goto fail;
+ break;
+ case IAMF_OBU_IA_PARAMETER_BLOCK:
+ ret = parameter_block_obu(ctx, c->buffer_pkt->data + start_pos, obu_size);
+ if (ret < 0)
+ goto fail;
+ break;
+ case IAMF_OBU_IA_SEQUENCE_HEADER:
+ for (int i = 0; c->param_definitions && i < c->nb_param_definitions; i++)
+ av_free(c->param_definitions[i].param);
+ av_freep(&c->param_definitions);
+ av_freep(&c->ids);
+ c->nb_param_definitions = 0;
+ c->nb_ids = 0;
+ // fall-through
+ case IAMF_OBU_IA_TEMPORAL_DELIMITER:
+ av_freep(&c->mix);
+ av_freep(&c->demix);
+ av_freep(&c->recon);
+ c->mix_size = 0;
+ c->demix_size = 0;
+ c->recon_size = 0;
+ break;
+ }
+
+ c->buffer_pkt->data += len;
+ c->buffer_pkt->size -= len;
+
+ if (!c->buffer_pkt->size) {
+ av_packet_unref(c->buffer_pkt);
+ ret = ff_bsf_get_packet_ref(ctx, c->buffer_pkt);
+ if (ret < 0)
+ return ret;
+ } else if (c->buffer_pkt->size < 0) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+ continue;
+ }
+
+ if (c->buffer_pkt->size > INT_MAX - len) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ ret = av_packet_ref(out, c->buffer_pkt);
+ if (ret < 0)
+ goto fail;
+
+ if (skip_samples || discard_padding) {
+ uint8_t *side_data = av_packet_new_side_data(out, AV_PKT_DATA_SKIP_SAMPLES, 10);
+ if (!side_data)
+ return AVERROR(ENOMEM);
+ AV_WL32(side_data, skip_samples);
+ AV_WL32(side_data + 4, discard_padding);
+ }
+ if (c->mix) {
+ uint8_t *side_data = av_packet_new_side_data(out, AV_PKT_DATA_IAMF_MIX_GAIN_PARAM, c->mix_size);
+ if (!side_data)
+ return AVERROR(ENOMEM);
+ memcpy(side_data, c->mix, c->mix_size);
+ }
+ if (c->demix) {
+ uint8_t *side_data = av_packet_new_side_data(out, AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM, c->demix_size);
+ if (!side_data)
+ return AVERROR(ENOMEM);
+ memcpy(side_data, c->demix, c->demix_size);
+ }
+ if (c->recon) {
+ uint8_t *side_data = av_packet_new_side_data(out, AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM, c->recon_size);
+ if (!side_data)
+ return AVERROR(ENOMEM);
+ memcpy(side_data, c->recon, c->recon_size);
+ }
+
+ out->data += start_pos;
+ out->size = obu_size;
+ out->stream_index = idx + c->first_index;
+
+ c->buffer_pkt->data += len;
+ c->buffer_pkt->size -= len;
+
+ if (!c->buffer_pkt->size)
+ av_packet_unref(c->buffer_pkt);
+ else if (c->buffer_pkt->size < 0) {
+ ret = AVERROR_INVALIDDATA;
+ goto fail;
+ }
+
+ return 0;
+ }
+
+fail:
+ if (ret < 0) {
+ av_packet_unref(out);
+ av_packet_unref(c->buffer_pkt);
+ }
+
+ return ret;
+}
+
+static int iamf_stream_split_init(AVBSFContext *ctx)
+{
+ IAMFSplitContext *const c = ctx->priv_data;
+
+ c->buffer_pkt = av_packet_alloc();
+ if (!c->buffer_pkt)
+ return AVERROR(ENOMEM);
+
+ return 0;
+}
+
+static void iamf_stream_split_flush(AVBSFContext *ctx)
+{
+ IAMFSplitContext *const c = ctx->priv_data;
+
+ if (c->buffer_pkt)
+ av_packet_unref(c->buffer_pkt);
+
+ av_freep(&c->mix);
+ av_freep(&c->demix);
+ av_freep(&c->recon);
+ c->mix_size = 0;
+ c->demix_size = 0;
+ c->recon_size = 0;
+}
+
+static void iamf_stream_split_close(AVBSFContext *ctx)
+{
+ IAMFSplitContext *const c = ctx->priv_data;
+
+ iamf_stream_split_flush(ctx);
+ av_packet_free(&c->buffer_pkt);
+
+ for (int i = 0; c->param_definitions && i < c->nb_param_definitions; i++)
+ av_free(c->param_definitions[i].param);
+ av_freep(&c->param_definitions);
+ c->nb_param_definitions = 0;
+
+ av_freep(&c->ids);
+ c->nb_ids = 0;
+}
+
+#define OFFSET(x) offsetof(IAMFSplitContext, x)
+#define FLAGS (AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_BSF_PARAM)
+static const AVOption iamf_stream_split_options[] = {
+ { "first_index", "First index to set stream index in output packets",
+ OFFSET(first_index), AV_OPT_TYPE_INT, { 0 }, 0, INT_MAX, FLAGS },
+ { NULL }
+};
+
+static const AVClass iamf_stream_split_class = {
+ .class_name = "iamf_stream_split_bsf",
+ .item_name = av_default_item_name,
+ .option = iamf_stream_split_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static const enum AVCodecID iamf_stream_split_codec_ids[] = {
+ AV_CODEC_ID_PCM_S16LE, AV_CODEC_ID_PCM_S16BE,
+ AV_CODEC_ID_PCM_S24LE, AV_CODEC_ID_PCM_S24BE,
+ AV_CODEC_ID_PCM_S32LE, AV_CODEC_ID_PCM_S32BE,
+ AV_CODEC_ID_OPUS, AV_CODEC_ID_AAC,
+ AV_CODEC_ID_FLAC, AV_CODEC_ID_NONE,
+};
+
+const FFBitStreamFilter ff_iamf_stream_split_bsf = {
+ .p.name = "iamf_stream_split",
+ .p.codec_ids = iamf_stream_split_codec_ids,
+ .p.priv_class = &iamf_stream_split_class,
+ .priv_data_size = sizeof(IAMFSplitContext),
+ .init = iamf_stream_split_init,
+ .flush = iamf_stream_split_flush,
+ .close = iamf_stream_split_close,
+ .filter = iamf_stream_split_filter,
+};
--
2.43.0
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* [FFmpeg-devel] [PATCH 2/7 v2] avformat/demux: support inserting bitstream filters in demuxing scenarios
2024-01-30 17:32 [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
@ 2024-01-30 17:32 ` James Almer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 3/7 v2] avformat/mov: make MOVStreamContext refcounted James Almer
` (5 subsequent siblings)
6 siblings, 0 replies; 19+ messages in thread
From: James Almer @ 2024-01-30 17:32 UTC (permalink / raw)
To: ffmpeg-devel
Packets will be passed to the bsf immediately after being generated by a
demuxer, and no further data will be read from the input until all packets
have been returned by the bsf.
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavformat/avformat.c | 47 ++++++++++++
libavformat/demux.c | 162 ++++++++++++++++++++++++++++++-----------
libavformat/internal.h | 13 +++-
libavformat/mov.c | 14 +++-
libavformat/mux.c | 43 -----------
libavformat/mux.h | 11 ---
libavformat/rawenc.c | 1 +
7 files changed, 192 insertions(+), 99 deletions(-)
diff --git a/libavformat/avformat.c b/libavformat/avformat.c
index 882927f7b1..f22cb83c98 100644
--- a/libavformat/avformat.c
+++ b/libavformat/avformat.c
@@ -931,3 +931,50 @@ FF_ENABLE_DEPRECATION_WARNINGS
*pb = NULL;
return ret;
}
+
+int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args)
+{
+ int ret;
+ const AVBitStreamFilter *bsf;
+ FFStream *const sti = ffstream(st);
+ AVBSFContext *bsfc;
+
+ av_assert0(!sti->bsfc);
+
+ if (name) {
+ bsf = av_bsf_get_by_name(name);
+ if (!bsf) {
+ av_log(NULL, AV_LOG_ERROR, "Unknown bitstream filter '%s'\n", name);
+ return AVERROR_BSF_NOT_FOUND;
+ }
+ ret = av_bsf_alloc(bsf, &bsfc);
+ } else
+ ret = av_bsf_get_null_filter(&bsfc);
+ if (ret < 0)
+ return ret;
+
+ bsfc->time_base_in = st->time_base;
+ if ((ret = avcodec_parameters_copy(bsfc->par_in, st->codecpar)) < 0) {
+ av_bsf_free(&bsfc);
+ return ret;
+ }
+
+ if (args && bsfc->filter->priv_class) {
+ if ((ret = av_set_options_string(bsfc->priv_data, args, "=", ":")) < 0) {
+ av_bsf_free(&bsfc);
+ return ret;
+ }
+ }
+
+ if ((ret = av_bsf_init(bsfc)) < 0) {
+ av_bsf_free(&bsfc);
+ return ret;
+ }
+
+ sti->bsfc = bsfc;
+
+ av_log(NULL, AV_LOG_VERBOSE,
+ "Automatically inserted bitstream filter '%s'; args='%s'\n",
+ name, args ? args : "");
+ return 1;
+}
diff --git a/libavformat/demux.c b/libavformat/demux.c
index 6f640b92b1..fb9bf9e4ac 100644
--- a/libavformat/demux.c
+++ b/libavformat/demux.c
@@ -540,6 +540,109 @@ static int update_wrap_reference(AVFormatContext *s, AVStream *st, int stream_in
return 1;
}
+static void update_timestamps(AVFormatContext *s, AVStream *st, AVPacket *pkt)
+{
+ FFStream *const sti = ffstream(st);
+
+ if (update_wrap_reference(s, st, pkt->stream_index, pkt) && sti->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) {
+ // correct first time stamps to negative values
+ if (!is_relative(sti->first_dts))
+ sti->first_dts = wrap_timestamp(st, sti->first_dts);
+ if (!is_relative(st->start_time))
+ st->start_time = wrap_timestamp(st, st->start_time);
+ if (!is_relative(sti->cur_dts))
+ sti->cur_dts = wrap_timestamp(st, sti->cur_dts);
+ }
+
+ pkt->dts = wrap_timestamp(st, pkt->dts);
+ pkt->pts = wrap_timestamp(st, pkt->pts);
+
+ force_codec_ids(s, st);
+
+ /* TODO: audio: time filter; video: frame reordering (pts != dts) */
+ if (s->use_wallclock_as_timestamps)
+ pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base);
+}
+
+static int filter_packet(AVFormatContext *s, AVStream *st, AVPacket *pkt)
+{
+ FFFormatContext *const si = ffformatcontext(s);
+ FFStream *const sti = ffstream(st);
+ const AVPacket *pkt1;
+ int err;
+
+ if (!sti->bsfc) {
+ const PacketListEntry *pktl = si->raw_packet_buffer.head;
+ if (AVPACKET_IS_EMPTY(pkt))
+ return 0;
+
+ update_timestamps(s, st, pkt);
+
+ if (!pktl && sti->request_probe <= 0)
+ return 0;
+
+ err = avpriv_packet_list_put(&si->raw_packet_buffer, pkt, NULL, 0);
+ if (err < 0) {
+ av_packet_unref(pkt);
+ return err;
+ }
+
+ pkt1 = &si->raw_packet_buffer.tail->pkt;
+ si->raw_packet_buffer_size += pkt1->size;
+
+ if (sti->request_probe <= 0)
+ return 0;
+
+ return probe_codec(s, s->streams[pkt1->stream_index], pkt1);
+ }
+
+ err = av_bsf_send_packet(sti->bsfc, pkt);
+ if (err < 0) {
+ av_log(s, AV_LOG_ERROR,
+ "Failed to send packet to filter %s for stream %d\n",
+ sti->bsfc->filter->name, st->index);
+ return err;
+ }
+
+ do {
+ AVStream *out_st;
+ FFStream *out_sti;
+
+ err = av_bsf_receive_packet(sti->bsfc, pkt);
+ if (err < 0) {
+ if (err == AVERROR(EAGAIN) || err == AVERROR_EOF)
+ return 0;
+ av_log(s, AV_LOG_ERROR, "Error applying bitstream filters to an output "
+ "packet for stream #%d: %s\n", st->index, av_err2str(err));
+ if (!(s->error_recognition & AV_EF_EXPLODE) && err != AVERROR(ENOMEM))
+ continue;
+ return err;
+ }
+ out_st = s->streams[pkt->stream_index];
+ out_sti = ffstream(out_st);
+
+ update_timestamps(s, out_st, pkt);
+
+ err = avpriv_packet_list_put(&si->raw_packet_buffer, pkt, NULL, 0);
+ if (err < 0) {
+ av_packet_unref(pkt);
+ return err;
+ }
+
+ pkt1 = &si->raw_packet_buffer.tail->pkt;
+ si->raw_packet_buffer_size += pkt1->size;
+
+ if (out_sti->request_probe <= 0)
+ continue;
+
+ err = probe_codec(s, out_st, pkt1);
+ if (err < 0)
+ return err;
+ } while (1);
+
+ return 0;
+}
+
int ff_read_packet(AVFormatContext *s, AVPacket *pkt)
{
FFFormatContext *const si = ffformatcontext(s);
@@ -557,9 +660,6 @@ FF_ENABLE_DEPRECATION_WARNINGS
for (;;) {
PacketListEntry *pktl = si->raw_packet_buffer.head;
- AVStream *st;
- FFStream *sti;
- const AVPacket *pkt1;
if (pktl) {
AVStream *const st = s->streams[pktl->pkt.stream_index];
@@ -582,16 +682,27 @@ FF_ENABLE_DEPRECATION_WARNINGS
We must re-call the demuxer to get the real packet. */
if (err == FFERROR_REDO)
continue;
- if (!pktl || err == AVERROR(EAGAIN))
+ if (err == AVERROR(EAGAIN))
return err;
for (unsigned i = 0; i < s->nb_streams; i++) {
AVStream *const st = s->streams[i];
FFStream *const sti = ffstream(st);
+ int ret;
+
+ // Drain buffered packets in the bsf context on eof
+ if (err == AVERROR_EOF)
+ if ((ret = filter_packet(s, st, pkt)) < 0)
+ return ret;
+ pktl = si->raw_packet_buffer.head;
+ if (!pktl)
+ continue;
if (sti->probe_packets || sti->request_probe > 0)
- if ((err = probe_codec(s, st, NULL)) < 0)
- return err;
+ if ((ret = probe_codec(s, st, NULL)) < 0)
+ return ret;
av_assert0(sti->request_probe <= 0);
}
+ if (!pktl)
+ return err;
continue;
}
@@ -616,42 +727,11 @@ FF_ENABLE_DEPRECATION_WARNINGS
av_assert0(pkt->stream_index < (unsigned)s->nb_streams &&
"Invalid stream index.\n");
- st = s->streams[pkt->stream_index];
- sti = ffstream(st);
-
- if (update_wrap_reference(s, st, pkt->stream_index, pkt) && sti->pts_wrap_behavior == AV_PTS_WRAP_SUB_OFFSET) {
- // correct first time stamps to negative values
- if (!is_relative(sti->first_dts))
- sti->first_dts = wrap_timestamp(st, sti->first_dts);
- if (!is_relative(st->start_time))
- st->start_time = wrap_timestamp(st, st->start_time);
- if (!is_relative(sti->cur_dts))
- sti->cur_dts = wrap_timestamp(st, sti->cur_dts);
- }
-
- pkt->dts = wrap_timestamp(st, pkt->dts);
- pkt->pts = wrap_timestamp(st, pkt->pts);
-
- force_codec_ids(s, st);
-
- /* TODO: audio: time filter; video: frame reordering (pts != dts) */
- if (s->use_wallclock_as_timestamps)
- pkt->dts = pkt->pts = av_rescale_q(av_gettime(), AV_TIME_BASE_Q, st->time_base);
-
- if (!pktl && sti->request_probe <= 0)
- return 0;
-
- err = avpriv_packet_list_put(&si->raw_packet_buffer,
- pkt, NULL, 0);
- if (err < 0) {
- av_packet_unref(pkt);
- return err;
- }
- pkt1 = &si->raw_packet_buffer.tail->pkt;
- si->raw_packet_buffer_size += pkt1->size;
-
- if ((err = probe_codec(s, st, pkt1)) < 0)
+ err = filter_packet(s, s->streams[pkt->stream_index], pkt);
+ if (err < 0)
return err;
+ if (!AVPACKET_IS_EMPTY(pkt))
+ return 0;
}
}
diff --git a/libavformat/internal.h b/libavformat/internal.h
index f93832b3c4..c2738a420f 100644
--- a/libavformat/internal.h
+++ b/libavformat/internal.h
@@ -212,7 +212,7 @@ typedef struct FFStream {
/**
* bitstream filter to run on stream
* - encoding: Set by muxer using ff_stream_add_bitstream_filter
- * - decoding: unused
+ * - decoding: Set by demuxer using ff_stream_add_bitstream_filter
*/
struct AVBSFContext *bsfc;
@@ -752,4 +752,15 @@ int ff_match_url_ext(const char *url, const char *extensions);
struct FFOutputFormat;
void avpriv_register_devices(const struct FFOutputFormat * const o[], const AVInputFormat * const i[]);
+/**
+ * Add a bitstream filter to a stream.
+ *
+ * @param st output stream to add a filter to
+ * @param name the name of the filter to add
+ * @param args filter-specific argument string
+ * @return >0 on success;
+ * AVERROR code on failure
+ */
+int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args);
+
#endif /* AVFORMAT_INTERNAL_H */
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 4cffd6c7db..3e71252e46 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -4913,20 +4913,28 @@ static int mov_read_custom(MOVContext *c, AVIOContext *pb, MOVAtom atom)
static int heif_add_stream(MOVContext *c, HEIFItem *item)
{
MOVStreamContext *sc;
+ AVBufferRef *buf;
AVStream *st;
+ uint8_t *data;
st = avformat_new_stream(c->fc, NULL);
if (!st)
return AVERROR(ENOMEM);
- sc = av_mallocz(sizeof(MOVStreamContext));
- if (!sc)
+ data = av_mallocz(sizeof(MOVStreamContext));
+ if (!data)
return AVERROR(ENOMEM);
+ buf = av_buffer_create(data, sizeof(MOVStreamContext), mov_free_stream_context, c->fc, 0);
+ if (!buf) {
+ av_free(data);
+ return AVERROR(ENOMEM);
+ }
item->st = st;
st->id = item->item_id;
- st->priv_data = sc;
+ st->priv_data = buf;
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->codec_id = mov_codec_id(st, item->type);
+ sc = mov_get_stream_context(st);
sc->ffindex = st->index;
c->trak_index = st->index;
st->avg_frame_rate.num = st->avg_frame_rate.den = 1;
diff --git a/libavformat/mux.c b/libavformat/mux.c
index de10d2c008..4bc8627617 100644
--- a/libavformat/mux.c
+++ b/libavformat/mux.c
@@ -1344,49 +1344,6 @@ int av_get_output_timestamp(struct AVFormatContext *s, int stream,
return 0;
}
-int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args)
-{
- int ret;
- const AVBitStreamFilter *bsf;
- FFStream *const sti = ffstream(st);
- AVBSFContext *bsfc;
-
- av_assert0(!sti->bsfc);
-
- if (!(bsf = av_bsf_get_by_name(name))) {
- av_log(NULL, AV_LOG_ERROR, "Unknown bitstream filter '%s'\n", name);
- return AVERROR_BSF_NOT_FOUND;
- }
-
- if ((ret = av_bsf_alloc(bsf, &bsfc)) < 0)
- return ret;
-
- bsfc->time_base_in = st->time_base;
- if ((ret = avcodec_parameters_copy(bsfc->par_in, st->codecpar)) < 0) {
- av_bsf_free(&bsfc);
- return ret;
- }
-
- if (args && bsfc->filter->priv_class) {
- if ((ret = av_set_options_string(bsfc->priv_data, args, "=", ":")) < 0) {
- av_bsf_free(&bsfc);
- return ret;
- }
- }
-
- if ((ret = av_bsf_init(bsfc)) < 0) {
- av_bsf_free(&bsfc);
- return ret;
- }
-
- sti->bsfc = bsfc;
-
- av_log(NULL, AV_LOG_VERBOSE,
- "Automatically inserted bitstream filter '%s'; args='%s'\n",
- name, args ? args : "");
- return 1;
-}
-
int ff_write_chained(AVFormatContext *dst, int dst_stream, AVPacket *pkt,
AVFormatContext *src, int interleave)
{
diff --git a/libavformat/mux.h b/libavformat/mux.h
index b9ec75641d..ab3e8edd60 100644
--- a/libavformat/mux.h
+++ b/libavformat/mux.h
@@ -171,17 +171,6 @@ const AVPacket *ff_interleaved_peek(AVFormatContext *s, int stream);
int ff_get_muxer_ts_offset(AVFormatContext *s, int stream_index, int64_t *offset);
-/**
- * Add a bitstream filter to a stream.
- *
- * @param st output stream to add a filter to
- * @param name the name of the filter to add
- * @param args filter-specific argument string
- * @return >0 on success;
- * AVERROR code on failure
- */
-int ff_stream_add_bitstream_filter(AVStream *st, const char *name, const char *args);
-
/**
* Write a packet to another muxer than the one the user originally
* intended. Useful when chaining muxers, where one muxer internally
diff --git a/libavformat/rawenc.c b/libavformat/rawenc.c
index f916db13a2..ec31d76d88 100644
--- a/libavformat/rawenc.c
+++ b/libavformat/rawenc.c
@@ -25,6 +25,7 @@
#include "libavutil/intreadwrite.h"
#include "avformat.h"
+#include "internal.h"
#include "rawenc.h"
#include "mux.h"
--
2.43.0
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* [FFmpeg-devel] [PATCH 3/7 v2] avformat/mov: make MOVStreamContext refcounted
2024-01-30 17:32 [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 2/7 v2] avformat/demux: support inserting bitstream filters in demuxing scenarios James Almer
@ 2024-01-30 17:32 ` James Almer
2024-01-30 22:31 ` Andreas Rheinhardt
2024-01-31 1:58 ` Michael Niedermayer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 4/7 v2] avformat/mov: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
` (4 subsequent siblings)
6 siblings, 2 replies; 19+ messages in thread
From: James Almer @ 2024-01-30 17:32 UTC (permalink / raw)
To: ffmpeg-devel
This will be needed by the following commit.
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavformat/mov.c | 257 ++++++++++++++++++++++++++--------------------
1 file changed, 145 insertions(+), 112 deletions(-)
diff --git a/libavformat/mov.c b/libavformat/mov.c
index 3e71252e46..a0dfa22598 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -31,6 +31,7 @@
#include "libavutil/attributes.h"
#include "libavutil/bprint.h"
+#include "libavutil/buffer.h"
#include "libavutil/channel_layout.h"
#include "libavutil/dict_internal.h"
#include "libavutil/internal.h"
@@ -184,10 +185,20 @@ static int mov_read_mac_string(MOVContext *c, AVIOContext *pb, int len,
return p - dst;
}
+static void mov_free_stream_context(void *opaque, uint8_t *data);
+
+static inline MOVStreamContext *mov_get_stream_context(const AVStream *st)
+{
+ AVBufferRef *buf = st->priv_data;
+
+ return (MOVStreamContext *)buf->data;
+}
+
static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len)
{
AVStream *st;
- MOVStreamContext *sc;
+ AVBufferRef *buf;
+ uint8_t *data;
enum AVCodecID id;
int ret;
@@ -201,16 +212,22 @@ static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len)
return 0;
}
- sc = av_mallocz(sizeof(*sc));
- if (!sc)
+ data = av_mallocz(sizeof(MOVStreamContext));
+ if (!data)
+ return AVERROR(ENOMEM);
+ buf = av_buffer_create(data, sizeof(MOVStreamContext), mov_free_stream_context, c->fc, 0);
+ if (!buf) {
+ av_free(data);
return AVERROR(ENOMEM);
+ }
+
ret = ff_add_attached_pic(c->fc, NULL, pb, NULL, len);
if (ret < 0) {
- av_free(sc);
+ av_buffer_unref(&buf);
return ret;
}
st = c->fc->streams[c->fc->nb_streams - 1];
- st->priv_data = sc;
+ st->priv_data = buf;
if (st->attached_pic.size >= 8 && id != AV_CODEC_ID_BMP) {
if (AV_RB64(st->attached_pic.data) == 0x89504e470d0a1a0a) {
@@ -590,7 +607,7 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
avio_rb32(pb); // version + flags
entries = avio_rb32(pb);
@@ -1372,7 +1389,7 @@ static int64_t get_frag_time(AVFormatContext *s, AVStream *dst_st,
MOVFragmentIndex *frag_index, int index)
{
MOVFragmentStreamInfo * frag_stream_info;
- MOVStreamContext *sc = dst_st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(dst_st);
int64_t timestamp;
int i, j;
@@ -1571,7 +1588,7 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
if (sc->time_scale) {
av_log(c->fc, AV_LOG_ERROR, "Multiple mdhd?\n");
@@ -1714,7 +1731,7 @@ static int mov_read_pcmc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return AVERROR_INVALIDDATA;
st = fc->streams[fc->nb_streams - 1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
if (sc->format == MOV_MP4_FPCM_TAG) {
switch (pcm_sample_size) {
@@ -2217,7 +2234,7 @@ static int mov_read_stco(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
@@ -2555,7 +2572,7 @@ static int mov_parse_stsd_data(MOVContext *c, AVIOContext *pb,
if (ret < 0)
return ret;
if (size > 16) {
- MOVStreamContext *tmcd_ctx = st->priv_data;
+ MOVStreamContext *tmcd_ctx = mov_get_stream_context(st);
int val;
val = AV_RB32(st->codecpar->extradata + 4);
tmcd_ctx->tmcd_flags = val;
@@ -2721,7 +2738,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries)
av_assert0 (c->fc->nb_streams >= 1);
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
for (pseudo_stream_id = 0;
pseudo_stream_id < entries && !pb->eof_reached;
@@ -2819,7 +2836,7 @@ static int mov_read_stsd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams - 1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
sc->stsd_version = avio_r8(pb);
avio_rb24(pb); /* flags */
@@ -2884,7 +2901,7 @@ static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
@@ -2980,7 +2997,7 @@ static int mov_read_stps(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
avio_rb32(pb); // version + flags
@@ -3018,7 +3035,7 @@ static int mov_read_stss(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
sti = ffstream(st);
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
@@ -3069,7 +3086,7 @@ static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
@@ -3158,7 +3175,7 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
@@ -3269,7 +3286,7 @@ static int mov_read_sdtp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams - 1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
@@ -3314,7 +3331,7 @@ static int mov_read_ctts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
@@ -3384,7 +3401,7 @@ static int mov_read_sgpd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams - 1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
version = avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
@@ -3440,7 +3457,7 @@ static int mov_read_sbgp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
version = avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
@@ -3541,7 +3558,7 @@ static int find_prev_closest_index(AVStream *st,
int64_t* ctts_index,
int64_t* ctts_sample)
{
- MOVStreamContext *msc = st->priv_data;
+ MOVStreamContext *msc = mov_get_stream_context(st);
FFStream *const sti = ffstream(st);
AVIndexEntry *e_keep = sti->index_entries;
int nb_keep = sti->nb_index_entries;
@@ -3714,7 +3731,7 @@ static int64_t add_ctts_entry(MOVCtts** ctts_data, unsigned int* ctts_count, uns
#define MAX_REORDER_DELAY 16
static void mov_estimate_video_delay(MOVContext *c, AVStream* st)
{
- MOVStreamContext *msc = st->priv_data;
+ MOVStreamContext *msc = mov_get_stream_context(st);
FFStream *const sti = ffstream(st);
int ctts_ind = 0;
int ctts_sample = 0;
@@ -3822,7 +3839,7 @@ static void mov_current_sample_set(MOVStreamContext *sc, int current_sample)
*/
static void mov_fix_index(MOVContext *mov, AVStream *st)
{
- MOVStreamContext *msc = st->priv_data;
+ MOVStreamContext *msc = mov_get_stream_context(st);
FFStream *const sti = ffstream(st);
AVIndexEntry *e_old = sti->index_entries;
int nb_old = sti->nb_index_entries;
@@ -4136,7 +4153,7 @@ static int build_open_gop_key_points(AVStream *st)
int k;
int sample_id = 0;
uint32_t cra_index;
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
if (st->codecpar->codec_id != AV_CODEC_ID_HEVC || !sc->sync_group_count)
return 0;
@@ -4196,7 +4213,7 @@ static int build_open_gop_key_points(AVStream *st)
static void mov_build_index(MOVContext *mov, AVStream *st)
{
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
FFStream *const sti = ffstream(st);
int64_t current_offset;
int64_t current_dts = 0;
@@ -4636,16 +4653,24 @@ static void fix_timescale(MOVContext *c, MOVStreamContext *sc)
static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
+ AVBufferRef *buf;
MOVStreamContext *sc;
+ uint8_t *data;
int ret;
st = avformat_new_stream(c->fc, NULL);
if (!st) return AVERROR(ENOMEM);
st->id = -1;
- sc = av_mallocz(sizeof(MOVStreamContext));
- if (!sc) return AVERROR(ENOMEM);
+ data = av_mallocz(sizeof(MOVStreamContext));
+ if (!data) return AVERROR(ENOMEM);
+ buf = av_buffer_create(data, sizeof(MOVStreamContext), mov_free_stream_context, c->fc, 0);
+ if (!buf) {
+ av_free(data);
+ return AVERROR(ENOMEM);
+ }
- st->priv_data = sc;
+ st->priv_data = buf;
+ sc = mov_get_stream_context(st);
st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
sc->ffindex = st->index;
c->trak_index = st->index;
@@ -4841,7 +4866,7 @@ static int mov_read_custom(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
for (i = 0; i < 3; i++) {
uint8_t **p;
@@ -4941,7 +4966,7 @@ static int heif_add_stream(MOVContext *c, HEIFItem *item)
st->time_base.num = st->time_base.den = 1;
st->nb_frames = 1;
sc->time_scale = 1;
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
sc->pb = c->fc->pb;
sc->pb_is_copied = 1;
@@ -5013,7 +5038,7 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
// Each stream (trak) should have exactly 1 tkhd. This catches bad files and
// avoids corrupting AVStreams mapped to an earlier tkhd.
@@ -5215,7 +5240,7 @@ static int mov_read_tfdt(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_log(c->fc, AV_LOG_WARNING, "could not find corresponding track id %u\n", frag->track_id);
return 0;
}
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
if (sc->pseudo_stream_id + 1 != frag->stsd_id && sc->pseudo_stream_id != -1)
return 0;
version = avio_r8(pb);
@@ -5269,7 +5294,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_log(c->fc, AV_LOG_WARNING, "could not find corresponding track id %u\n", frag->track_id);
return 0;
}
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
if (sc->pseudo_stream_id+1 != frag->stsd_id && sc->pseudo_stream_id != -1)
return 0;
@@ -5572,7 +5597,7 @@ static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
}
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
timescale = av_make_q(1, avio_rb32(pb));
@@ -5655,14 +5680,14 @@ static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
si = &item->stream_info[j];
if (si->sidx_pts != AV_NOPTS_VALUE) {
ref_st = c->fc->streams[j];
- ref_sc = ref_st->priv_data;
+ ref_sc = mov_get_stream_context(ref_st);
break;
}
}
}
if (ref_st) for (i = 0; i < c->fc->nb_streams; i++) {
st = c->fc->streams[i];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
if (!sc->has_sidx) {
st->duration = sc->track_end = av_rescale(ref_st->duration, sc->time_scale, ref_sc->time_scale);
}
@@ -5758,7 +5783,7 @@ static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1 || c->ignore_editlist)
return 0;
- sc = c->fc->streams[c->fc->nb_streams-1]->priv_data;
+ sc = mov_get_stream_context(c->fc->streams[c->fc->nb_streams-1]);
version = avio_r8(pb); /* version */
avio_rb24(pb); /* flags */
@@ -5825,7 +5850,7 @@ static int mov_read_tmcd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return AVERROR_INVALIDDATA;
- sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data;
+ sc = mov_get_stream_context(c->fc->streams[c->fc->nb_streams - 1]);
sc->timecode_track = avio_rb32(pb);
return 0;
}
@@ -5882,7 +5907,7 @@ static int mov_read_smdm(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return AVERROR_INVALIDDATA;
- sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data;
+ sc = mov_get_stream_context(c->fc->streams[c->fc->nb_streams - 1]);
if (atom.size < 5) {
av_log(c->fc, AV_LOG_ERROR, "Empty Mastering Display Metadata box\n");
@@ -5930,7 +5955,7 @@ static int mov_read_mdcv(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return AVERROR_INVALIDDATA;
- sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data;
+ sc = mov_get_stream_context(c->fc->streams[c->fc->nb_streams - 1]);
if (atom.size < 24 || sc->mastering) {
av_log(c->fc, AV_LOG_ERROR, "Invalid Mastering Display Color Volume box\n");
@@ -5966,7 +5991,7 @@ static int mov_read_coll(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return AVERROR_INVALIDDATA;
- sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data;
+ sc = mov_get_stream_context(c->fc->streams[c->fc->nb_streams - 1]);
if (atom.size < 5) {
av_log(c->fc, AV_LOG_ERROR, "Empty Content Light Level box\n");
@@ -6002,7 +6027,7 @@ static int mov_read_clli(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return AVERROR_INVALIDDATA;
- sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data;
+ sc = mov_get_stream_context(c->fc->streams[c->fc->nb_streams - 1]);
if (atom.size < 4) {
av_log(c->fc, AV_LOG_ERROR, "Empty Content Light Level Info box\n");
@@ -6035,7 +6060,7 @@ static int mov_read_st3d(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
st = c->fc->streams[c->fc->nb_streams - 1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
if (atom.size < 5) {
av_log(c->fc, AV_LOG_ERROR, "Empty stereoscopic video box\n");
@@ -6085,7 +6110,7 @@ static int mov_read_sv3d(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
st = c->fc->streams[c->fc->nb_streams - 1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
if (atom.size < 8) {
av_log(c->fc, AV_LOG_ERROR, "Empty spherical video box\n");
@@ -6296,7 +6321,7 @@ static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams - 1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
ret = ffio_read_size(pb, uuid, AV_UUID_LEN);
if (ret < 0)
@@ -6408,7 +6433,7 @@ static int mov_read_frma(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams - 1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
switch (sc->format)
{
@@ -6461,7 +6486,7 @@ static int get_current_encryption_info(MOVContext *c, MOVEncryptionIndex **encry
}
if (i == c->fc->nb_streams)
return 0;
- *sc = st->priv_data;
+ *sc = mov_get_stream_context(st);
if (!frag_stream_info->encryption_index) {
// If this stream isn't encrypted, don't create the index.
@@ -6479,7 +6504,7 @@ static int get_current_encryption_info(MOVContext *c, MOVEncryptionIndex **encry
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams - 1];
- *sc = st->priv_data;
+ *sc = mov_get_stream_context(st);
if (!(*sc)->cenc.encryption_index) {
// If this stream isn't encrypted, don't create the index.
@@ -6972,7 +6997,7 @@ static int mov_read_schm(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
if (sc->pseudo_stream_id != 0) {
av_log(c->fc, AV_LOG_ERROR, "schm boxes are only supported in first sample descriptor\n");
@@ -7004,7 +7029,7 @@ static int mov_read_tenc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
if (c->fc->nb_streams < 1)
return 0;
st = c->fc->streams[c->fc->nb_streams-1];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
if (sc->pseudo_stream_id != 0) {
av_log(c->fc, AV_LOG_ERROR, "tenc atom are only supported in first sample descriptor\n");
@@ -8416,7 +8441,7 @@ static void mov_read_chapters(AVFormatContext *s)
}
sti = ffstream(st);
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
cur_pos = avio_tell(sc->pb);
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
@@ -8506,7 +8531,7 @@ static int parse_timecode_in_framenum_format(AVFormatContext *s, AVStream *st,
static int mov_read_rtmd_track(AVFormatContext *s, AVStream *st)
{
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
FFStream *const sti = ffstream(st);
char buf[AV_TIMECODE_STR_SIZE];
int64_t cur_pos = avio_tell(sc->pb);
@@ -8532,7 +8557,7 @@ static int mov_read_rtmd_track(AVFormatContext *s, AVStream *st)
static int mov_read_timecode_track(AVFormatContext *s, AVStream *st)
{
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
FFStream *const sti = ffstream(st);
int flags = 0;
int64_t cur_pos = avio_tell(sc->pb);
@@ -8589,6 +8614,56 @@ static void mov_free_encryption_index(MOVEncryptionIndex **index) {
av_freep(index);
}
+static void mov_free_stream_context(void *opaque, uint8_t *data)
+{
+ AVFormatContext *s = opaque;
+ MOVStreamContext *sc = (MOVStreamContext *)data;
+
+ av_freep(&sc->ctts_data);
+ for (int i = 0; i < sc->drefs_count; i++) {
+ av_freep(&sc->drefs[i].path);
+ av_freep(&sc->drefs[i].dir);
+ }
+ av_freep(&sc->drefs);
+
+ sc->drefs_count = 0;
+
+ if (!sc->pb_is_copied)
+ ff_format_io_close(s, &sc->pb);
+
+ sc->pb = NULL;
+ av_freep(&sc->chunk_offsets);
+ av_freep(&sc->stsc_data);
+ av_freep(&sc->sample_sizes);
+ av_freep(&sc->keyframes);
+ av_freep(&sc->stts_data);
+ av_freep(&sc->sdtp_data);
+ av_freep(&sc->stps_data);
+ av_freep(&sc->elst_data);
+ av_freep(&sc->rap_group);
+ av_freep(&sc->sync_group);
+ av_freep(&sc->sgpd_sync);
+ av_freep(&sc->sample_offsets);
+ av_freep(&sc->open_key_samples);
+ av_freep(&sc->display_matrix);
+ av_freep(&sc->index_ranges);
+
+ if (sc->extradata)
+ for (int i = 0; i < sc->stsd_count; i++)
+ av_free(sc->extradata[i]);
+ av_freep(&sc->extradata);
+ av_freep(&sc->extradata_size);
+
+ mov_free_encryption_index(&sc->cenc.encryption_index);
+ av_encryption_info_free(sc->cenc.default_encrypted_sample);
+ av_aes_ctr_free(sc->cenc.aes_ctr);
+
+ av_freep(&sc->stereo3d);
+ av_freep(&sc->spherical);
+ av_freep(&sc->mastering);
+ av_freep(&sc->coll);
+}
+
static int mov_read_close(AVFormatContext *s)
{
MOVContext *mov = s->priv_data;
@@ -8596,54 +8671,12 @@ static int mov_read_close(AVFormatContext *s)
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
if (!sc)
continue;
- av_freep(&sc->ctts_data);
- for (j = 0; j < sc->drefs_count; j++) {
- av_freep(&sc->drefs[j].path);
- av_freep(&sc->drefs[j].dir);
- }
- av_freep(&sc->drefs);
-
- sc->drefs_count = 0;
-
- if (!sc->pb_is_copied)
- ff_format_io_close(s, &sc->pb);
-
- sc->pb = NULL;
- av_freep(&sc->chunk_offsets);
- av_freep(&sc->stsc_data);
- av_freep(&sc->sample_sizes);
- av_freep(&sc->keyframes);
- av_freep(&sc->stts_data);
- av_freep(&sc->sdtp_data);
- av_freep(&sc->stps_data);
- av_freep(&sc->elst_data);
- av_freep(&sc->rap_group);
- av_freep(&sc->sync_group);
- av_freep(&sc->sgpd_sync);
- av_freep(&sc->sample_offsets);
- av_freep(&sc->open_key_samples);
- av_freep(&sc->display_matrix);
- av_freep(&sc->index_ranges);
-
- if (sc->extradata)
- for (j = 0; j < sc->stsd_count; j++)
- av_free(sc->extradata[j]);
- av_freep(&sc->extradata);
- av_freep(&sc->extradata_size);
-
- mov_free_encryption_index(&sc->cenc.encryption_index);
- av_encryption_info_free(sc->cenc.default_encrypted_sample);
- av_aes_ctr_free(sc->cenc.aes_ctr);
-
- av_freep(&sc->stereo3d);
- av_freep(&sc->spherical);
- av_freep(&sc->mastering);
- av_freep(&sc->coll);
+ av_buffer_unref((AVBufferRef **)&st->priv_data);
}
av_freep(&mov->dv_demux);
@@ -8682,7 +8715,7 @@ static int tmcd_is_referenced(AVFormatContext *s, int tmcd_id)
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
sc->timecode_track == tmcd_id)
@@ -8882,7 +8915,7 @@ static int mov_read_header(AVFormatContext *s)
/* copy timecode metadata from tmcd tracks to the related video streams */
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
if (sc->timecode_track > 0) {
AVDictionaryEntry *tcr;
int tmcd_st_id = -1;
@@ -8903,7 +8936,7 @@ static int mov_read_header(AVFormatContext *s)
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
FFStream *const sti = ffstream(st);
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
fix_timescale(mov, sc);
if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
st->codecpar->codec_id == AV_CODEC_ID_AAC) {
@@ -8933,7 +8966,7 @@ static int mov_read_header(AVFormatContext *s)
if (mov->trex_data) {
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
if (st->duration > 0) {
/* Akin to sc->data_size * 8 * sc->time_scale / st->duration but accounting for overflows. */
st->codecpar->bit_rate = av_rescale(sc->data_size, ((int64_t) sc->time_scale) * 8, st->duration);
@@ -8951,7 +8984,7 @@ static int mov_read_header(AVFormatContext *s)
if (mov->use_mfra_for > 0) {
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
if (sc->duration_for_fps > 0) {
/* Akin to sc->data_size * 8 * sc->time_scale / sc->duration_for_fps but accounting for overflows. */
st->codecpar->bit_rate = av_rescale(sc->data_size, ((int64_t) sc->time_scale) * 8, sc->duration_for_fps);
@@ -8976,7 +9009,7 @@ static int mov_read_header(AVFormatContext *s)
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
switch (st->codecpar->codec_type) {
case AVMEDIA_TYPE_AUDIO:
@@ -9047,7 +9080,7 @@ static AVIndexEntry *mov_find_next_sample(AVFormatContext *s, AVStream **st)
for (i = 0; i < s->nb_streams; i++) {
AVStream *avst = s->streams[i];
FFStream *const avsti = ffstream(avst);
- MOVStreamContext *msc = avst->priv_data;
+ MOVStreamContext *msc = mov_get_stream_context(avst);
if (msc->pb && msc->current_sample < avsti->nb_index_entries) {
AVIndexEntry *current_sample = &avsti->index_entries[msc->current_sample];
int64_t dts = av_rescale(current_sample->timestamp, AV_TIME_BASE, msc->time_scale);
@@ -9172,7 +9205,7 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
return ret;
goto retry;
}
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
/* must be done just before reading, to avoid infinite loop on sample */
current_index = sc->current_index;
mov_current_sample_inc(sc);
@@ -9338,7 +9371,7 @@ static int is_open_key_sample(const MOVStreamContext *sc, int sample)
*/
static int can_seek_to_key_sample(AVStream *st, int sample, int64_t requested_pts)
{
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
FFStream *const sti = ffstream(st);
int64_t key_sample_dts, key_sample_pts;
@@ -9364,7 +9397,7 @@ static int can_seek_to_key_sample(AVStream *st, int sample, int64_t requested_pt
static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, int flags)
{
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
FFStream *const sti = ffstream(st);
int sample, time_sample, ret;
unsigned int i;
@@ -9426,7 +9459,7 @@ static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp,
static int64_t mov_get_skip_samples(AVStream *st, int sample)
{
- MOVStreamContext *sc = st->priv_data;
+ MOVStreamContext *sc = mov_get_stream_context(st);
FFStream *const sti = ffstream(st);
int64_t first_ts = sti->index_entries[0].timestamp;
int64_t ts = sti->index_entries[sample].timestamp;
@@ -9480,7 +9513,7 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
for (i = 0; i < s->nb_streams; i++) {
MOVStreamContext *sc;
st = s->streams[i];
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
mov_current_sample_set(sc, 0);
}
while (1) {
@@ -9488,7 +9521,7 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
AVIndexEntry *entry = mov_find_next_sample(s, &st);
if (!entry)
return AVERROR_INVALIDDATA;
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
if (sc->ffindex == stream_index && sc->current_sample == sample)
break;
mov_current_sample_inc(sc);
--
2.43.0
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* [FFmpeg-devel] [PATCH 4/7 v2] avformat/mov: add support for Immersive Audio Model and Formats in ISOBMFF
2024-01-30 17:32 [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 2/7 v2] avformat/demux: support inserting bitstream filters in demuxing scenarios James Almer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 3/7 v2] avformat/mov: make MOVStreamContext refcounted James Almer
@ 2024-01-30 17:32 ` James Almer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 5/7] avcodec: add an Immersive Audio Model and Formats frame merge bsf James Almer
` (3 subsequent siblings)
6 siblings, 0 replies; 19+ messages in thread
From: James Almer @ 2024-01-30 17:32 UTC (permalink / raw)
To: ffmpeg-devel
The mp4 encapsulation was updated in the spec, so i'm resubmitting this.
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavformat/Makefile | 3 +-
libavformat/isom.h | 6 +
libavformat/mov.c | 281 +++++++++++++++++++++++++++++++++++++++----
3 files changed, 266 insertions(+), 24 deletions(-)
diff --git a/libavformat/Makefile b/libavformat/Makefile
index dcc99eeac4..814eda9a08 100644
--- a/libavformat/Makefile
+++ b/libavformat/Makefile
@@ -364,7 +364,8 @@ OBJS-$(CONFIG_MMF_MUXER) += mmf.o rawenc.o
OBJS-$(CONFIG_MODS_DEMUXER) += mods.o
OBJS-$(CONFIG_MOFLEX_DEMUXER) += moflex.o
OBJS-$(CONFIG_MOV_DEMUXER) += mov.o mov_chan.o mov_esds.o \
- qtpalette.o replaygain.o dovi_isom.o
+ qtpalette.o replaygain.o dovi_isom.o \
+ iamf.o
OBJS-$(CONFIG_MOV_MUXER) += movenc.o av1.o avc.o hevc.o vpcc.o \
movenchint.o mov_chan.o rtp.o \
movenccenc.o movenc_ttml.o rawutils.o \
diff --git a/libavformat/isom.h b/libavformat/isom.h
index 2cf456fee1..c0e634eca6 100644
--- a/libavformat/isom.h
+++ b/libavformat/isom.h
@@ -33,6 +33,7 @@
#include "libavutil/stereo3d.h"
#include "avio.h"
+#include "iamf.h"
#include "internal.h"
#include "dv.h"
@@ -166,6 +167,7 @@ typedef struct MOVIndexRange {
typedef struct MOVStreamContext {
AVIOContext *pb;
int pb_is_copied;
+ int id; ///< AVStream id
int ffindex; ///< AVStream index
int next_chunk;
unsigned int chunk_count;
@@ -260,6 +262,10 @@ typedef struct MOVStreamContext {
AVEncryptionInfo *default_encrypted_sample;
MOVEncryptionIndex *encryption_index;
} cenc;
+
+ IAMFContext *iamf;
+ uint8_t *iamf_descriptors;
+ int iamf_descriptors_size;
} MOVStreamContext;
typedef struct HEIFItem {
diff --git a/libavformat/mov.c b/libavformat/mov.c
index a0dfa22598..6e4f2ba657 100644
--- a/libavformat/mov.c
+++ b/libavformat/mov.c
@@ -59,6 +59,7 @@
#include "internal.h"
#include "avio_internal.h"
#include "demux.h"
+#include "iamf_parse.h"
#include "dovi_isom.h"
#include "riff.h"
#include "isom.h"
@@ -851,6 +852,169 @@ static int mov_read_dac3(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
}
+static int mov_read_iacb(MOVContext *c, AVIOContext *pb, MOVAtom atom)
+{
+ AVStream *st;
+ MOVStreamContext *sc;
+ FFIOContext b;
+ AVIOContext *descriptor_pb;
+ AVDictionary *metadata;
+ IAMFContext *iamf;
+ char args[32];
+ int64_t start_time, duration;
+ unsigned size;
+ int nb_frames, disposition;
+ int version, ret;
+
+ if (atom.size < 5)
+ return AVERROR_INVALIDDATA;
+
+ if (c->fc->nb_streams < 1)
+ return 0;
+
+ version = avio_r8(pb);
+ if (version != 1) {
+ av_log(c->fc, AV_LOG_ERROR, "%s configurationVersion %d",
+ version < 1 ? "invalid" : "unsupported", version);
+ return AVERROR_INVALIDDATA;
+ }
+
+ size = ffio_read_leb(pb);
+ if (!size)
+ return AVERROR_INVALIDDATA;
+
+ st = c->fc->streams[c->fc->nb_streams - 1];
+ sc = mov_get_stream_context(st);
+
+ iamf = sc->iamf = av_mallocz(sizeof(*iamf));
+ if (!iamf)
+ return AVERROR(ENOMEM);
+
+ sc->iamf_descriptors = av_malloc(size);
+ if (!sc->iamf_descriptors)
+ return AVERROR(ENOMEM);
+
+ sc->iamf_descriptors_size = size;
+ ret = avio_read(pb, sc->iamf_descriptors, size);
+ if (ret != size)
+ return ret < 0 ? ret : AVERROR_INVALIDDATA;
+
+ ffio_init_context(&b, sc->iamf_descriptors, size, 0, NULL, NULL, NULL, NULL);
+ descriptor_pb = &b.pub;
+
+ ret = ff_iamfdec_read_descriptors(iamf, descriptor_pb, size, c->fc);
+ if (ret < 0)
+ return ret;
+
+ metadata = st->metadata;
+ st->metadata = NULL;
+ start_time = st->start_time;
+ nb_frames = st->nb_frames;
+ duration = st->duration;
+ disposition = st->disposition;
+
+ for (int i = 0; i < iamf->nb_audio_elements; i++) {
+ IAMFAudioElement *audio_element = iamf->audio_elements[i];
+ AVStreamGroup *stg = avformat_stream_group_create(c->fc, AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT, NULL);
+
+ if (!stg) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ stg->id = audio_element->audio_element_id;
+ stg->params.iamf_audio_element = audio_element->element;
+ audio_element->element = NULL;
+
+ for (int j = 0; j < audio_element->nb_substreams; j++) {
+ IAMFSubStream *substream = &audio_element->substreams[j];
+ AVStream *stream;
+
+ if (!i && !j)
+ stream = st;
+ else
+ stream = avformat_new_stream(c->fc, NULL);
+ if (!stream) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ stream->start_time = start_time;
+ stream->nb_frames = nb_frames;
+ stream->duration = duration;
+ stream->disposition = disposition;
+ if (stream != st && !(stream->priv_data = av_buffer_ref(st->priv_data))) {
+ ret = AVERROR(ENOMEM);
+ goto fail;
+ }
+
+ ret = avcodec_parameters_copy(stream->codecpar, substream->codecpar);
+ if (ret < 0)
+ goto fail;
+
+ stream->id = substream->audio_substream_id;
+
+ avpriv_set_pts_info(st, 64, 1, sc->time_scale);
+
+ ret = avformat_stream_group_add_stream(stg, stream);
+ if (ret < 0)
+ goto fail;
+ }
+
+ ret = av_dict_copy(&stg->metadata, metadata, 0);
+ if (ret < 0)
+ goto fail;
+ }
+
+ for (int i = 0; i < iamf->nb_mix_presentations; i++) {
+ IAMFMixPresentation *mix_presentation = iamf->mix_presentations[i];
+ const AVIAMFMixPresentation *mix = mix_presentation->mix;
+ AVStreamGroup *stg = avformat_stream_group_create(c->fc, AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION, NULL);
+
+ if (!stg)
+ goto fail;
+
+ stg->id = mix_presentation->mix_presentation_id;
+ stg->params.iamf_mix_presentation = mix_presentation->mix;
+ mix_presentation->mix = NULL;
+
+ for (int j = 0; j < mix->nb_submixes; j++) {
+ const AVIAMFSubmix *submix = mix->submixes[j];
+
+ for (int k = 0; k < submix->nb_elements; k++) {
+ const AVIAMFSubmixElement *submix_element = submix->elements[k];
+ const AVStreamGroup *audio_element = NULL;
+
+ for (int l = 0; l < c->fc->nb_stream_groups; l++)
+ if (c->fc->stream_groups[l]->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT &&
+ c->fc->stream_groups[l]->id == submix_element->audio_element_id) {
+ audio_element = c->fc->stream_groups[l];
+ break;
+ }
+ av_assert0(audio_element);
+
+ for (int l = 0; l < audio_element->nb_streams; l++) {
+ ret = avformat_stream_group_add_stream(stg, audio_element->streams[l]);
+ if (ret < 0 && ret != AVERROR(EEXIST))
+ goto fail;
+ }
+ }
+ }
+
+ ret = av_dict_copy(&stg->metadata, metadata, 0);
+ if (ret < 0)
+ goto fail;
+ }
+
+ snprintf(args, sizeof(args), "first_index=%d", st->index);
+
+ ret = ff_stream_add_bitstream_filter(st, "iamf_stream_split", args);
+fail:
+ av_dict_free(&metadata);
+
+ return ret;
+}
+
static int mov_read_dec3(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
@@ -1396,7 +1560,7 @@ static int64_t get_frag_time(AVFormatContext *s, AVStream *dst_st,
// If the stream is referenced by any sidx, limit the search
// to fragments that referenced this stream in the sidx
if (sc->has_sidx) {
- frag_stream_info = get_frag_stream_info(frag_index, index, dst_st->id);
+ frag_stream_info = get_frag_stream_info(frag_index, index, sc->id);
if (frag_stream_info->sidx_pts != AV_NOPTS_VALUE)
return frag_stream_info->sidx_pts;
if (frag_stream_info->first_tfra_pts != AV_NOPTS_VALUE)
@@ -1407,9 +1571,11 @@ static int64_t get_frag_time(AVFormatContext *s, AVStream *dst_st,
for (i = 0; i < frag_index->item[index].nb_stream_info; i++) {
AVStream *frag_stream = NULL;
frag_stream_info = &frag_index->item[index].stream_info[i];
- for (j = 0; j < s->nb_streams; j++)
- if (s->streams[j]->id == frag_stream_info->id)
+ for (j = 0; j < s->nb_streams; j++) {
+ MOVStreamContext *sc2 = mov_get_stream_context(s->streams[j]);
+ if (sc2->id == frag_stream_info->id)
frag_stream = s->streams[j];
+ }
if (!frag_stream) {
av_log(s, AV_LOG_WARNING, "No stream matching sidx ID found.\n");
continue;
@@ -1475,12 +1641,13 @@ static int update_frag_index(MOVContext *c, int64_t offset)
for (i = 0; i < c->fc->nb_streams; i++) {
// Avoid building frag index if streams lack track id.
- if (c->fc->streams[i]->id < 0) {
+ MOVStreamContext *sc = mov_get_stream_context(c->fc->streams[i]);
+ if (sc->id < 0) {
av_free(frag_stream_info);
return AVERROR_INVALIDDATA;
}
- frag_stream_info[i].id = c->fc->streams[i]->id;
+ frag_stream_info[i].id = sc->id;
frag_stream_info[i].sidx_pts = AV_NOPTS_VALUE;
frag_stream_info[i].tfdt_dts = AV_NOPTS_VALUE;
frag_stream_info[i].next_trun_dts = AV_NOPTS_VALUE;
@@ -3270,7 +3437,7 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
"All samples in data stream index:id [%d:%d] have zero "
"duration, stream set to be discarded by default. Override "
"using AVStream->discard or -discard for ffmpeg command.\n",
- st->index, st->id);
+ st->index, sc->id);
st->discard = AVDISCARD_ALL;
}
sc->track_end = duration;
@@ -4650,6 +4817,50 @@ static void fix_timescale(MOVContext *c, MOVStreamContext *sc)
}
}
+static int mov_update_iamf_streams(MOVContext *c, const AVStream *st)
+{
+ const MOVStreamContext *sc = mov_get_stream_context(st);
+
+ for (int i = 0; i < sc->iamf->nb_audio_elements; i++) {
+ const AVStreamGroup *stg = NULL;
+
+ for (int j = 0; j < c->fc->nb_stream_groups; j++)
+ if (c->fc->stream_groups[j]->id == sc->iamf->audio_elements[i]->audio_element_id)
+ stg = c->fc->stream_groups[j];
+ av_assert0(stg);
+
+ for (int j = 0; j < stg->nb_streams; j++) {
+ const FFStream *sti = cffstream(st);
+ AVStream *out = stg->streams[j];
+ FFStream *out_sti = ffstream(stg->streams[j]);
+
+ out->codecpar->bit_rate = 0;
+
+ if (out == st)
+ continue;
+
+ out->time_base = st->time_base;
+ out->start_time = st->start_time;
+ out->duration = st->duration;
+ out->nb_frames = st->nb_frames;
+ out->disposition = st->disposition;
+ out->discard = st->discard;
+
+ av_assert0(!out_sti->index_entries);
+ out_sti->index_entries = av_malloc(sti->index_entries_allocated_size);
+ if (!out_sti->index_entries)
+ return AVERROR(ENOMEM);
+
+ out_sti->index_entries_allocated_size = sti->index_entries_allocated_size;
+ out_sti->nb_index_entries = sti->nb_index_entries;
+ out_sti->skip_samples = sti->skip_samples;
+ memcpy(out_sti->index_entries, sti->index_entries, sti->index_entries_allocated_size);
+ }
+ }
+
+ return 0;
+}
+
static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
AVStream *st;
@@ -4720,6 +4931,12 @@ static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
mov_build_index(c, st);
+ if (sc->iamf) {
+ ret = mov_update_iamf_streams(c, st);
+ if (ret < 0)
+ return ret;
+ }
+
if (sc->dref_id-1 < sc->drefs_count && sc->drefs[sc->dref_id-1].path) {
MOVDref *dref = &sc->drefs[sc->dref_id - 1];
if (c->enable_drefs) {
@@ -4960,6 +5177,7 @@ static int heif_add_stream(MOVContext *c, HEIFItem *item)
st->codecpar->codec_type = AVMEDIA_TYPE_VIDEO;
st->codecpar->codec_id = mov_codec_id(st, item->type);
sc = mov_get_stream_context(st);
+ sc->id = st->id;
sc->ffindex = st->index;
c->trak_index = st->index;
st->avg_frame_rate.num = st->avg_frame_rate.den = 1;
@@ -5057,6 +5275,7 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
avio_rb32(pb); /* modification time */
}
st->id = (int)avio_rb32(pb); /* track id (NOT 0 !)*/
+ sc->id = st->id;
avio_rb32(pb); /* reserved */
/* highlevel (considering edits) duration in movie timebase */
@@ -5231,7 +5450,8 @@ static int mov_read_tfdt(MOVContext *c, AVIOContext *pb, MOVAtom atom)
int64_t base_media_decode_time;
for (i = 0; i < c->fc->nb_streams; i++) {
- if (c->fc->streams[i]->id == frag->track_id) {
+ sc = mov_get_stream_context(c->fc->streams[i]);
+ if (sc->id == frag->track_id) {
st = c->fc->streams[i];
break;
}
@@ -5240,7 +5460,6 @@ static int mov_read_tfdt(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_log(c->fc, AV_LOG_WARNING, "could not find corresponding track id %u\n", frag->track_id);
return 0;
}
- sc = mov_get_stream_context(st);
if (sc->pseudo_stream_id + 1 != frag->stsd_id && sc->pseudo_stream_id != -1)
return 0;
version = avio_r8(pb);
@@ -5284,7 +5503,8 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
}
for (i = 0; i < c->fc->nb_streams; i++) {
- if (c->fc->streams[i]->id == frag->track_id) {
+ sc = mov_get_stream_context(c->fc->streams[i]);
+ if (sc->id == frag->track_id) {
st = c->fc->streams[i];
sti = ffstream(st);
break;
@@ -5294,7 +5514,6 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
av_log(c->fc, AV_LOG_WARNING, "could not find corresponding track id %u\n", frag->track_id);
return 0;
}
- sc = mov_get_stream_context(st);
if (sc->pseudo_stream_id+1 != frag->stsd_id && sc->pseudo_stream_id != -1)
return 0;
@@ -5587,7 +5806,8 @@ static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
track_id = avio_rb32(pb); // Reference ID
for (i = 0; i < c->fc->nb_streams; i++) {
- if (c->fc->streams[i]->id == track_id) {
+ sc = mov_get_stream_context(c->fc->streams[i]);
+ if (sc->id == track_id) {
st = c->fc->streams[i];
break;
}
@@ -5597,8 +5817,6 @@ static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
return 0;
}
- sc = mov_get_stream_context(st);
-
timescale = av_make_q(1, avio_rb32(pb));
if (timescale.den <= 0) {
@@ -6479,14 +6697,14 @@ static int get_current_encryption_info(MOVContext *c, MOVEncryptionIndex **encry
frag_stream_info = get_current_frag_stream_info(&c->frag_index);
if (frag_stream_info) {
for (i = 0; i < c->fc->nb_streams; i++) {
- if (c->fc->streams[i]->id == frag_stream_info->id) {
+ *sc = mov_get_stream_context(c->fc->streams[i]);
+ if ((*sc)->id == frag_stream_info->id) {
st = c->fc->streams[i];
break;
}
}
if (i == c->fc->nb_streams)
return 0;
- *sc = mov_get_stream_context(st);
if (!frag_stream_info->encryption_index) {
// If this stream isn't encrypted, don't create the index.
@@ -7423,7 +7641,7 @@ static int cenc_filter(MOVContext *mov, AVStream* st, MOVStreamContext *sc, AVPa
AVEncryptionInfo *encrypted_sample;
int encrypted_index, ret;
- frag_stream_info = get_frag_stream_info_from_pkt(&mov->frag_index, pkt, st->id);
+ frag_stream_info = get_frag_stream_info_from_pkt(&mov->frag_index, pkt, sc->id);
encrypted_index = current_index;
encryption_index = NULL;
if (frag_stream_info) {
@@ -8199,6 +8417,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = {
{ MKTAG('i','s','p','e'), mov_read_ispe },
{ MKTAG('i','p','r','p'), mov_read_iprp },
{ MKTAG('i','i','n','f'), mov_read_iinf },
+{ MKTAG('i','a','c','b'), mov_read_iacb },
{ 0, NULL }
};
@@ -8430,18 +8649,19 @@ static void mov_read_chapters(AVFormatContext *s)
AVStream *st = NULL;
FFStream *sti = NULL;
chapter_track = mov->chapter_tracks[j];
- for (i = 0; i < s->nb_streams; i++)
- if (s->streams[i]->id == chapter_track) {
+ for (i = 0; i < s->nb_streams; i++) {
+ sc = mov_get_stream_context(s->streams[i]);
+ if (sc->id == chapter_track) {
st = s->streams[i];
break;
}
+ }
if (!st) {
av_log(s, AV_LOG_ERROR, "Referenced QT chapter track not found\n");
continue;
}
sti = ffstream(st);
- sc = mov_get_stream_context(st);
cur_pos = avio_tell(sc->pb);
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
@@ -8662,6 +8882,11 @@ static void mov_free_stream_context(void *opaque, uint8_t *data)
av_freep(&sc->spherical);
av_freep(&sc->mastering);
av_freep(&sc->coll);
+
+ ff_iamf_uninit_context(sc->iamf);
+ av_freep(&sc->iamf);
+ av_freep(&sc->iamf_descriptors);
+ sc->iamf_descriptors_size = 0;
}
static int mov_read_close(AVFormatContext *s)
@@ -8891,7 +9116,7 @@ static int mov_read_header(AVFormatContext *s)
continue;
st = item->st;
- sc = st->priv_data;
+ sc = mov_get_stream_context(st);
st->codecpar->width = item->width;
st->codecpar->height = item->height;
sc->sample_sizes[0] = item->extent_length;
@@ -8920,9 +9145,11 @@ static int mov_read_header(AVFormatContext *s)
AVDictionaryEntry *tcr;
int tmcd_st_id = -1;
- for (j = 0; j < s->nb_streams; j++)
- if (s->streams[j]->id == sc->timecode_track)
+ for (j = 0; j < s->nb_streams; j++) {
+ MOVStreamContext *sc2 = mov_get_stream_context(s->streams[j]);
+ if (sc2->id == sc->timecode_track)
tmcd_st_id = j;
+ }
if (tmcd_st_id < 0 || tmcd_st_id == i)
continue;
@@ -9235,7 +9462,15 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
if (st->codecpar->codec_id == AV_CODEC_ID_EIA_608 && sample->size > 8)
ret = get_eia608_packet(sc->pb, pkt, sample->size);
- else
+ else if (sc->iamf_descriptors_size) {
+ ret = av_new_packet(pkt, sc->iamf_descriptors_size);
+ if (ret < 0)
+ return ret;
+ pkt->pos = avio_tell(sc->pb);
+ memcpy(pkt->data, sc->iamf_descriptors, sc->iamf_descriptors_size);
+ sc->iamf_descriptors_size = 0;
+ ret = av_append_packet(sc->pb, pkt, sample->size);
+ } else
ret = av_get_packet(sc->pb, pkt, sample->size);
if (ret < 0) {
if (should_retry(sc->pb, ret)) {
--
2.43.0
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* [FFmpeg-devel] [PATCH 5/7] avcodec: add an Immersive Audio Model and Formats frame merge bsf
2024-01-30 17:32 [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
` (2 preceding siblings ...)
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 4/7 v2] avformat/mov: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
@ 2024-01-30 17:32 ` James Almer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 6/7] avcodec/put_bits: add put_leb() James Almer
` (2 subsequent siblings)
6 siblings, 0 replies; 19+ messages in thread
From: James Almer @ 2024-01-30 17:32 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/bitstream_filters.c | 1 +
libavcodec/bsf/Makefile | 1 +
libavcodec/bsf/iamf_stream_merge_bsf.c | 227 +++++++++++++++++++++++++
3 files changed, 229 insertions(+)
create mode 100644 libavcodec/bsf/iamf_stream_merge_bsf.c
diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c
index 640b821413..cc3895295e 100644
--- a/libavcodec/bitstream_filters.c
+++ b/libavcodec/bitstream_filters.c
@@ -42,6 +42,7 @@ extern const FFBitStreamFilter ff_h264_redundant_pps_bsf;
extern const FFBitStreamFilter ff_hapqa_extract_bsf;
extern const FFBitStreamFilter ff_hevc_metadata_bsf;
extern const FFBitStreamFilter ff_hevc_mp4toannexb_bsf;
+extern const FFBitStreamFilter ff_iamf_stream_merge_bsf;
extern const FFBitStreamFilter ff_iamf_stream_split_bsf;
extern const FFBitStreamFilter ff_imx_dump_header_bsf;
extern const FFBitStreamFilter ff_media100_to_mjpegb_bsf;
diff --git a/libavcodec/bsf/Makefile b/libavcodec/bsf/Makefile
index 80dcdf94fb..34e26f1bc4 100644
--- a/libavcodec/bsf/Makefile
+++ b/libavcodec/bsf/Makefile
@@ -21,6 +21,7 @@ OBJS-$(CONFIG_HAPQA_EXTRACT_BSF) += bsf/hapqa_extract.o
OBJS-$(CONFIG_HEVC_METADATA_BSF) += bsf/h265_metadata.o
OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF) += bsf/hevc_mp4toannexb.o
OBJS-$(CONFIG_IAMF_STREAM_MERGE_BSF) += bsf/iamf_stream_merge_bsf.o
+OBJS-$(CONFIG_IAMF_STREAM_SPLIT_BSF) += bsf/iamf_stream_split_bsf.o
OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += bsf/imx_dump_header.o
OBJS-$(CONFIG_MEDIA100_TO_MJPEGB_BSF) += bsf/media100_to_mjpegb.o
OBJS-$(CONFIG_MJPEG2JPEG_BSF) += bsf/mjpeg2jpeg.o
diff --git a/libavcodec/bsf/iamf_stream_merge_bsf.c b/libavcodec/bsf/iamf_stream_merge_bsf.c
new file mode 100644
index 0000000000..c62698d374
--- /dev/null
+++ b/libavcodec/bsf/iamf_stream_merge_bsf.c
@@ -0,0 +1,227 @@
+/*
+ * 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 <stdint.h>
+#include <stddef.h>
+
+#include "libavutil/dict.h"
+#include "libavutil/fifo.h"
+#include "libavutil/opt.h"
+#include "libavformat/iamf.h"
+#include "bsf.h"
+#include "bsf_internal.h"
+#include "bytestream.h"
+#include "get_bits.h"
+#include "put_bits.h"
+
+typedef struct IAMFMergeContext {
+ AVClass *class;
+
+ AVFifo *fifo;
+
+ // AVOptions
+ AVDictionary *index_mapping;
+ int stream_count;
+ int out_index;
+} IAMFMergeContext;
+
+static int find_id_from_idx(AVBSFContext *ctx, int idx)
+{
+ IAMFMergeContext *const c = ctx->priv_data;
+ const AVDictionaryEntry *e = NULL;
+
+ while (e = av_dict_iterate(c->index_mapping, e)) {
+ char *endptr = NULL;
+ int id, map_idx = strtol(e->key, &endptr, 0);
+ if (!endptr || *endptr)
+ return AVERROR_INVALIDDATA;
+ endptr = NULL;
+ id = strtol(e->value, &endptr, 0);
+ if (!endptr || *endptr)
+ return AVERROR_INVALIDDATA;
+ if (map_idx == idx)
+ return id;
+ }
+
+ av_log(ctx, AV_LOG_ERROR, "Invalid stream idx %d\n", idx);
+ return AVERROR_INVALIDDATA;
+}
+
+static int iamf_stream_merge_filter(AVBSFContext *ctx, AVPacket *out)
+{
+ IAMFMergeContext *const c = ctx->priv_data;
+ AVPacket *pkt;
+ int ret;
+
+ while (av_fifo_can_write(c->fifo)) {
+ ret = ff_bsf_get_packet(ctx, &pkt);
+ if (ret < 0)
+ return ret;
+ av_fifo_write(c->fifo, &pkt, 1);
+ }
+
+ pkt = NULL;
+ while (av_fifo_can_read(c->fifo)) {
+ PutBitContext pb;
+ PutByteContext p;
+ uint8_t *side_data, header[MAX_IAMF_OBU_HEADER_SIZE], obu[8];
+ unsigned int obu_header;
+ unsigned int skip_samples = 0, discard_padding = 0;
+ size_t side_data_size;
+ int header_size, obu_size, old_out_size = out->size;
+ int id, type;
+
+ av_packet_free(&pkt);
+ av_fifo_read(c->fifo, &pkt, 1);
+ id = find_id_from_idx(ctx, pkt->stream_index);
+ if (id < 0)
+ return AVERROR_INVALIDDATA;
+
+ type = id <= 17 ? id + IAMF_OBU_IA_AUDIO_FRAME_ID0 : IAMF_OBU_IA_AUDIO_FRAME;
+
+ side_data = av_packet_get_side_data(pkt, AV_PKT_DATA_SKIP_SAMPLES,
+ &side_data_size);
+
+ if (side_data && side_data_size >= 10) {
+ skip_samples = AV_RL32(side_data);
+ discard_padding = AV_RL32(side_data + 4);
+ }
+
+ init_put_bits(&pb, (uint8_t *)&obu_header, sizeof(obu_header));
+ put_bits(&pb, 5, type);
+ put_bits(&pb, 1, 0); // obu_redundant_copy
+ put_bits(&pb, 1, skip_samples || discard_padding);
+ put_bits(&pb, 1, 0); // obu_extension_flag
+ flush_put_bits(&pb);
+
+ init_put_bits(&pb, header, sizeof(header));
+ if (skip_samples || discard_padding) {
+ put_leb(&pb, discard_padding);
+ put_leb(&pb, skip_samples);
+ }
+ if (id > 17)
+ put_leb(&pb, id);
+ flush_put_bits(&pb);
+
+ header_size = put_bytes_count(&pb, 1);
+
+ init_put_bits(&pb, obu, sizeof(obu));
+ put_leb(&pb, header_size + pkt->size);
+ flush_put_bits(&pb);
+
+ obu_size = put_bytes_count(&pb, 1);
+
+ ret = av_grow_packet(out, 1 + obu_size + header_size + pkt->size);
+ if (ret < 0)
+ goto fail;
+
+ bytestream2_init_writer(&p, out->data + old_out_size, 1 + obu_size + header_size + pkt->size);
+ bytestream2_put_byteu(&p, obu_header);
+ bytestream2_put_bufferu(&p, obu, obu_size);
+ bytestream2_put_bufferu(&p, header, header_size);
+ bytestream2_put_bufferu(&p, pkt->data, pkt->size);
+ }
+
+ ret = av_packet_copy_props(out, pkt);
+ if (ret < 0)
+ goto fail;
+ out->stream_index = c->out_index;
+
+ ret = 0;
+fail:
+ av_packet_free(&pkt);
+ if (ret < 0)
+ av_packet_free(&out);
+ return ret;
+}
+
+static int iamf_stream_merge_init(AVBSFContext *ctx)
+{
+ IAMFMergeContext *const c = ctx->priv_data;
+
+ if (!c->index_mapping) {
+ av_log(ctx, AV_LOG_ERROR, "Empty index map\n");
+ return AVERROR(EINVAL);
+ }
+
+ c->fifo = av_fifo_alloc2(av_dict_count(c->index_mapping), sizeof(AVPacket*), 0);
+ if (!c->fifo)
+ return AVERROR(ENOMEM);
+
+ return 0;
+}
+
+static void iamf_stream_merge_flush(AVBSFContext *ctx)
+{
+ IAMFMergeContext *const c = ctx->priv_data;
+
+ while (av_fifo_can_read(c->fifo)) {
+ AVPacket *pkt;
+ av_fifo_read(c->fifo, &pkt, 1);
+ av_packet_free(&pkt);
+ }
+ av_fifo_reset2(c->fifo);
+}
+
+static void iamf_stream_merge_close(AVBSFContext *ctx)
+{
+ IAMFMergeContext *const c = ctx->priv_data;
+
+ if (c->fifo)
+ iamf_stream_merge_flush(ctx);
+ av_fifo_freep2(&c->fifo);
+}
+
+#define OFFSET(x) offsetof(IAMFMergeContext, x)
+#define FLAGS (AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_BSF_PARAM)
+static const AVOption iamf_stream_merge_options[] = {
+ { "index_mapping", "a :-separated list of stream_index=audio_substream_id entries "
+ "to set stream id in output Audio Frame OBUs",
+ OFFSET(index_mapping), AV_OPT_TYPE_DICT, { .str = NULL }, 0, 0, FLAGS },
+ { "out_index", "Stream index to in output packets",
+ OFFSET(out_index), AV_OPT_TYPE_INT, { 0 }, 0, INT_MAX, FLAGS },
+ { NULL }
+};
+
+static const AVClass iamf_stream_merge_class = {
+ .class_name = "iamf_stream_merge_bsf",
+ .item_name = av_default_item_name,
+ .option = iamf_stream_merge_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
+static const enum AVCodecID iamf_stream_merge_codec_ids[] = {
+ AV_CODEC_ID_PCM_S16LE, AV_CODEC_ID_PCM_S16BE,
+ AV_CODEC_ID_PCM_S24LE, AV_CODEC_ID_PCM_S24BE,
+ AV_CODEC_ID_PCM_S32LE, AV_CODEC_ID_PCM_S32BE,
+ AV_CODEC_ID_OPUS, AV_CODEC_ID_AAC,
+ AV_CODEC_ID_FLAC, AV_CODEC_ID_NONE,
+};
+
+const FFBitStreamFilter ff_iamf_stream_merge_bsf = {
+ .p.name = "iamf_stream_merge",
+ .p.codec_ids = iamf_stream_merge_codec_ids,
+ .p.priv_class = &iamf_stream_merge_class,
+ .priv_data_size = sizeof(IAMFMergeContext),
+ .init = iamf_stream_merge_init,
+ .flush = iamf_stream_merge_flush,
+ .close = iamf_stream_merge_close,
+ .filter = iamf_stream_merge_filter,
+};
--
2.43.0
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* [FFmpeg-devel] [PATCH 6/7] avcodec/put_bits: add put_leb()
2024-01-30 17:32 [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
` (3 preceding siblings ...)
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 5/7] avcodec: add an Immersive Audio Model and Formats frame merge bsf James Almer
@ 2024-01-30 17:32 ` James Almer
2024-01-30 21:53 ` Andreas Rheinhardt
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 7/7] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
2024-01-30 21:47 ` [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf Andreas Rheinhardt
6 siblings, 1 reply; 19+ messages in thread
From: James Almer @ 2024-01-30 17:32 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavcodec/put_bits.h | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/libavcodec/put_bits.h b/libavcodec/put_bits.h
index 4561dc131a..04dedd3342 100644
--- a/libavcodec/put_bits.h
+++ b/libavcodec/put_bits.h
@@ -370,6 +370,22 @@ static inline void put_sbits63(PutBitContext *pb, int n, int64_t value)
put_bits64(pb, n, (uint64_t)(value) & (~(UINT64_MAX << n)));
}
+static inline void put_leb(PutBitContext *s, unsigned value)
+{
+ int len;
+ uint8_t byte;
+
+ len = (av_log2(value) + 7) / 7;
+
+ for (int i = 0; i < len; i++) {
+ byte = value >> (7 * i) & 0x7f;
+ if (i < len - 1)
+ byte |= 0x80;
+
+ put_bits_no_assert(s, 8, byte);
+ }
+}
+
/**
* Return the pointer to the byte where the bitstream writer will put
* the next bit.
--
2.43.0
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* [FFmpeg-devel] [PATCH 7/7] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF
2024-01-30 17:32 [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
` (4 preceding siblings ...)
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 6/7] avcodec/put_bits: add put_leb() James Almer
@ 2024-01-30 17:32 ` James Almer
2024-01-30 21:47 ` [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf Andreas Rheinhardt
6 siblings, 0 replies; 19+ messages in thread
From: James Almer @ 2024-01-30 17:32 UTC (permalink / raw)
To: ffmpeg-devel
Signed-off-by: James Almer <jamrial@gmail.com>
---
libavformat/movenc.c | 321 ++++++++++++++++++++++++++++++++++---------
libavformat/movenc.h | 7 +
2 files changed, 266 insertions(+), 62 deletions(-)
diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 8a27afbc57..76a568fba6 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -32,6 +32,7 @@
#include "dovi_isom.h"
#include "riff.h"
#include "avio.h"
+#include "iamf_writer.h"
#include "isom.h"
#include "av1.h"
#include "avc.h"
@@ -47,6 +48,7 @@
#include "libavcodec/raw.h"
#include "internal.h"
#include "libavutil/avstring.h"
+#include "libavutil/bprint.h"
#include "libavutil/channel_layout.h"
#include "libavutil/csp.h"
#include "libavutil/intfloat.h"
@@ -315,6 +317,33 @@ static int mov_write_sdtp_tag(AVIOContext *pb, MOVTrack *track)
return update_size(pb, pos);
}
+
+static int mov_write_iacb_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *track)
+{
+ AVIOContext *dyn_bc;
+ int64_t pos = avio_tell(pb);
+ uint8_t *dyn_buf = NULL;
+ int dyn_size;
+ int ret = avio_open_dyn_buf(&dyn_bc);
+ if (ret < 0)
+ return ret;
+
+ avio_wb32(pb, 0);
+ ffio_wfourcc(pb, "iacb");
+ avio_w8(pb, 1); // configurationVersion
+
+ ret = ff_iamf_write_descriptors(track->iamf, dyn_bc, s);
+ if (ret < 0)
+ return ret;
+
+ dyn_size = avio_close_dyn_buf(dyn_bc, &dyn_buf);
+ ffio_write_leb(pb, dyn_size);
+ avio_write(pb, dyn_buf, dyn_size);
+ av_free(dyn_buf);
+
+ return update_size(pb, pos);
+}
+
static int mov_write_amr_tag(AVIOContext *pb, MOVTrack *track)
{
avio_wb32(pb, 0x11); /* size */
@@ -1357,6 +1386,8 @@ static int mov_write_audio_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex
ret = mov_write_wave_tag(s, pb, track);
else if (track->tag == MKTAG('m','p','4','a'))
ret = mov_write_esds_tag(pb, track);
+ else if (track->tag == MKTAG('i','a','m','f'))
+ ret = mov_write_iacb_tag(mov->fc, pb, track);
else if (track->par->codec_id == AV_CODEC_ID_AMR_NB)
ret = mov_write_amr_tag(pb, track);
else if (track->par->codec_id == AV_CODEC_ID_AC3)
@@ -2481,7 +2512,7 @@ static int mov_write_video_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContex
if (track->mode == MODE_AVIF) {
mov_write_ccst_tag(pb);
- if (s->nb_streams > 0 && track == &mov->tracks[1])
+ if (mov->nb_streams > 0 && track == &mov->tracks[1])
mov_write_aux_tag(pb, "auxi");
}
@@ -3076,9 +3107,9 @@ static int mov_write_iloc_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte
avio_wb32(pb, 0); /* Version & flags */
avio_w8(pb, (4 << 4) + 4); /* offset_size(4) and length_size(4) */
avio_w8(pb, 0); /* base_offset_size(4) and reserved(4) */
- avio_wb16(pb, s->nb_streams); /* item_count */
+ avio_wb16(pb, mov->nb_streams); /* item_count */
- for (int i = 0; i < s->nb_streams; i++) {
+ for (int i = 0; i < mov->nb_streams; i++) {
avio_wb16(pb, i + 1); /* item_id */
avio_wb16(pb, 0); /* data_reference_index */
avio_wb16(pb, 1); /* extent_count */
@@ -3097,9 +3128,9 @@ static int mov_write_iinf_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "iinf");
avio_wb32(pb, 0); /* Version & flags */
- avio_wb16(pb, s->nb_streams); /* entry_count */
+ avio_wb16(pb, mov->nb_streams); /* entry_count */
- for (int i = 0; i < s->nb_streams; i++) {
+ for (int i = 0; i < mov->nb_streams; i++) {
int64_t infe_pos = avio_tell(pb);
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "infe");
@@ -3168,7 +3199,7 @@ static int mov_write_ipco_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte
int64_t pos = avio_tell(pb);
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "ipco");
- for (int i = 0; i < s->nb_streams; i++) {
+ for (int i = 0; i < mov->nb_streams; i++) {
mov_write_ispe_tag(pb, mov, s, i);
mov_write_pixi_tag(pb, mov, s, i);
mov_write_av1c_tag(pb, &mov->tracks[i]);
@@ -3186,9 +3217,9 @@ static int mov_write_ipma_tag(AVIOContext *pb, MOVMuxContext *mov, AVFormatConte
avio_wb32(pb, 0); /* size */
ffio_wfourcc(pb, "ipma");
avio_wb32(pb, 0); /* Version & flags */
- avio_wb32(pb, s->nb_streams); /* entry_count */
+ avio_wb32(pb, mov->nb_streams); /* entry_count */
- for (int i = 0, index = 1; i < s->nb_streams; i++) {
+ for (int i = 0, index = 1; i < mov->nb_streams; i++) {
avio_wb16(pb, i + 1); /* item_ID */
avio_w8(pb, 4); /* association_count */
@@ -4165,7 +4196,7 @@ static int mov_write_covr(AVIOContext *pb, AVFormatContext *s)
int64_t pos = 0;
int i;
- for (i = 0; i < s->nb_streams; i++) {
+ for (i = 0; i < mov->nb_streams; i++) {
MOVTrack *trk = &mov->tracks[i];
if (!is_cover_image(trk->st) || trk->cover_image->size <= 0)
@@ -4312,7 +4343,7 @@ static int mov_write_meta_tag(AVIOContext *pb, MOVMuxContext *mov,
mov_write_pitm_tag(pb, 1);
mov_write_iloc_tag(pb, mov, s);
mov_write_iinf_tag(pb, mov, s);
- if (s->nb_streams > 1)
+ if (mov->nb_streams > 1)
mov_write_iref_tag(pb, mov, s);
mov_write_iprp_tag(pb, mov, s);
} else {
@@ -4563,16 +4594,17 @@ static int mov_setup_track_ids(MOVMuxContext *mov, AVFormatContext *s)
if (mov->use_stream_ids_as_track_ids) {
int next_generated_track_id = 0;
- for (i = 0; i < s->nb_streams; i++) {
- if (s->streams[i]->id > next_generated_track_id)
- next_generated_track_id = s->streams[i]->id;
+ for (i = 0; i < mov->nb_streams; i++) {
+ AVStream *st = mov->tracks[i].st;
+ if (st->id > next_generated_track_id)
+ next_generated_track_id = st->id;
}
for (i = 0; i < mov->nb_tracks; i++) {
if (mov->tracks[i].entry <= 0 && !(mov->flags & FF_MOV_FLAG_FRAGMENT))
continue;
- mov->tracks[i].track_id = i >= s->nb_streams ? ++next_generated_track_id : s->streams[i]->id;
+ mov->tracks[i].track_id = i >= mov->nb_streams ? ++next_generated_track_id : mov->tracks[i].st->id;
}
} else {
for (i = 0; i < mov->nb_tracks; i++) {
@@ -4609,7 +4641,7 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
}
if (mov->chapter_track)
- for (i = 0; i < s->nb_streams; i++) {
+ for (i = 0; i < mov->nb_streams; i++) {
mov->tracks[i].tref_tag = MKTAG('c','h','a','p');
mov->tracks[i].tref_id = mov->tracks[mov->chapter_track].track_id;
}
@@ -4649,7 +4681,7 @@ static int mov_write_moov_tag(AVIOContext *pb, MOVMuxContext *mov,
for (i = 0; i < mov->nb_tracks; i++) {
if (mov->tracks[i].entry > 0 || mov->flags & FF_MOV_FLAG_FRAGMENT ||
mov->mode == MODE_AVIF) {
- int ret = mov_write_trak_tag(s, pb, mov, &(mov->tracks[i]), i < s->nb_streams ? s->streams[i] : NULL);
+ int ret = mov_write_trak_tag(s, pb, mov, &(mov->tracks[i]), i < mov->nb_streams ? mov->tracks[i].st : NULL);
if (ret < 0)
return ret;
}
@@ -5443,8 +5475,8 @@ static int mov_write_ftyp_tag(AVIOContext *pb, AVFormatContext *s)
int has_h264 = 0, has_av1 = 0, has_video = 0, has_dolby = 0;
int i;
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
+ for (i = 0; i < mov->nb_streams; i++) {
+ AVStream *st = mov->tracks[i].st;
if (is_cover_image(st))
continue;
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
@@ -5619,8 +5651,8 @@ static int mov_write_identification(AVIOContext *pb, AVFormatContext *s)
mov_write_ftyp_tag(pb,s);
if (mov->mode == MODE_PSP) {
int video_streams_nb = 0, audio_streams_nb = 0, other_streams_nb = 0;
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
+ for (i = 0; i < mov->nb_streams; i++) {
+ AVStream *st = mov->tracks[i].st;
if (is_cover_image(st))
continue;
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
@@ -5807,7 +5839,7 @@ static int mov_write_squashed_packets(AVFormatContext *s)
{
MOVMuxContext *mov = s->priv_data;
- for (int i = 0; i < s->nb_streams; i++) {
+ for (int i = 0; i < mov->nb_streams; i++) {
MOVTrack *track = &mov->tracks[i];
int ret = AVERROR_BUG;
@@ -5848,7 +5880,7 @@ static int mov_flush_fragment(AVFormatContext *s, int force)
// of fragments was triggered automatically by an AVPacket, we
// already have reliable info for the end of that track, but other
// tracks may need to be filled in.
- for (i = 0; i < s->nb_streams; i++) {
+ for (i = 0; i < mov->nb_streams; i++) {
MOVTrack *track = &mov->tracks[i];
if (!track->end_reliable) {
const AVPacket *pkt = ff_interleaved_peek(s, i);
@@ -6049,10 +6081,8 @@ static int mov_auto_flush_fragment(AVFormatContext *s, int force)
return ret;
}
-static int check_pkt(AVFormatContext *s, AVPacket *pkt)
+static int check_pkt(AVFormatContext *s, MOVTrack *trk, AVPacket *pkt)
{
- MOVMuxContext *mov = s->priv_data;
- MOVTrack *trk = &mov->tracks[pkt->stream_index];
int64_t ref;
uint64_t duration;
@@ -6090,15 +6120,21 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
{
MOVMuxContext *mov = s->priv_data;
AVIOContext *pb = s->pb;
- MOVTrack *trk = &mov->tracks[pkt->stream_index];
- AVCodecParameters *par = trk->par;
+ MOVTrack *trk;
+ AVCodecParameters *par;
AVProducerReferenceTime *prft;
unsigned int samples_in_chunk = 0;
int size = pkt->size, ret = 0, offset = 0;
size_t prft_size;
uint8_t *reformatted_data = NULL;
- ret = check_pkt(s, pkt);
+ if (pkt->stream_index < s->nb_streams)
+ trk = s->streams[pkt->stream_index]->priv_data;
+ else // Timecode or chapter
+ trk = &mov->tracks[pkt->stream_index];
+ par = trk->par;
+
+ ret = check_pkt(s, trk, pkt);
if (ret < 0)
return ret;
@@ -6187,7 +6223,7 @@ int ff_mov_write_packet(AVFormatContext *s, AVPacket *pkt)
if (par->codec_id == AV_CODEC_ID_AAC && pkt->size > 2 &&
(AV_RB16(pkt->data) & 0xfff0) == 0xfff0) {
- if (!s->streams[pkt->stream_index]->nb_frames) {
+ if (!trk->st->nb_frames) {
av_log(s, AV_LOG_ERROR, "Malformed AAC bitstream detected: "
"use the audio bitstream filter 'aac_adtstoasc' to fix it "
"('-bsf:a aac_adtstoasc' option with ffmpeg)\n");
@@ -6435,18 +6471,18 @@ err:
static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt)
{
MOVMuxContext *mov = s->priv_data;
- MOVTrack *trk = &mov->tracks[pkt->stream_index];
+ MOVTrack *trk = s->streams[pkt->stream_index]->priv_data;
AVCodecParameters *par = trk->par;
int64_t frag_duration = 0;
int size = pkt->size;
- int ret = check_pkt(s, pkt);
+ int ret = check_pkt(s, trk, pkt);
if (ret < 0)
return ret;
if (mov->flags & FF_MOV_FLAG_FRAG_DISCONT) {
int i;
- for (i = 0; i < s->nb_streams; i++)
+ for (i = 0; i < mov->nb_streams; i++)
mov->tracks[i].frag_discont = 1;
mov->flags &= ~FF_MOV_FLAG_FRAG_DISCONT;
}
@@ -6488,7 +6524,7 @@ static int mov_write_single_packet(AVFormatContext *s, AVPacket *pkt)
return 0; /* Discard 0 sized packets */
}
- if (trk->entry && pkt->stream_index < s->nb_streams)
+ if (trk->entry && pkt->stream_index < mov->nb_streams)
frag_duration = av_rescale_q(pkt->dts - trk->cluster[0].dts,
s->streams[pkt->stream_index]->time_base,
AV_TIME_BASE_Q);
@@ -6543,17 +6579,45 @@ static int mov_write_subtitle_end_packet(AVFormatContext *s,
return ret;
}
+static int mov_filter_packet(AVFormatContext *s, MOVTrack *track, AVPacket *pkt)
+{
+ int ret;
+
+ if (!track->bsf)
+ return 0;
+
+ ret = av_bsf_send_packet(track->bsf, pkt);
+ if (ret < 0) {
+ av_log(s, AV_LOG_ERROR,
+ "Failed to send packet to filter %s for stream %d: %s\n",
+ track->bsf->filter->name, pkt->stream_index, av_err2str(ret));
+ return ret;
+ }
+
+ return av_bsf_receive_packet(track->bsf, pkt);
+}
+
static int mov_write_packet(AVFormatContext *s, AVPacket *pkt)
{
MOVMuxContext *mov = s->priv_data;
MOVTrack *trk;
+ int ret;
if (!pkt) {
mov_flush_fragment(s, 1);
return 1;
}
- trk = &mov->tracks[pkt->stream_index];
+ trk = s->streams[pkt->stream_index]->priv_data;
+
+ ret = mov_filter_packet(s, trk, pkt);
+ if (ret < 0) {
+ if (ret == AVERROR(EAGAIN))
+ return 0;
+ av_log(s, AV_LOG_ERROR, "Error applying bitstream filters to an output "
+ "packet for stream #%d: %s\n", trk->st->index, av_err2str(ret));
+ return ret;
+ }
if (is_cover_image(trk->st)) {
int ret;
@@ -6754,12 +6818,12 @@ static int mov_create_chapter_track(AVFormatContext *s, int tracknum)
}
-static int mov_check_timecode_track(AVFormatContext *s, AVTimecode *tc, int src_index, const char *tcstr)
+static int mov_check_timecode_track(AVFormatContext *s, AVTimecode *tc, AVStream *src_st, const char *tcstr)
{
int ret;
/* compute the frame number */
- ret = av_timecode_init_from_string(tc, s->streams[src_index]->avg_frame_rate, tcstr, s);
+ ret = av_timecode_init_from_string(tc, src_st->avg_frame_rate, tcstr, s);
return ret;
}
@@ -6767,7 +6831,7 @@ static int mov_create_timecode_track(AVFormatContext *s, int index, int src_inde
{
MOVMuxContext *mov = s->priv_data;
MOVTrack *track = &mov->tracks[index];
- AVStream *src_st = s->streams[src_index];
+ AVStream *src_st = mov->tracks[src_index].st;
uint8_t data[4];
AVPacket *pkt = mov->pkt;
AVRational rate = src_st->avg_frame_rate;
@@ -6827,8 +6891,8 @@ static void enable_tracks(AVFormatContext *s)
first[i] = -1;
}
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
+ for (i = 0; i < mov->nb_streams; i++) {
+ AVStream *st = mov->tracks[i].st;
if (st->codecpar->codec_type <= AVMEDIA_TYPE_UNKNOWN ||
st->codecpar->codec_type >= AVMEDIA_TYPE_NB ||
@@ -6862,6 +6926,9 @@ static void mov_free(AVFormatContext *s)
MOVMuxContext *mov = s->priv_data;
int i;
+ for (i = 0; i < s->nb_streams; i++)
+ s->streams[i]->priv_data = NULL;
+
if (!mov->tracks)
return;
@@ -6892,6 +6959,7 @@ static void mov_free(AVFormatContext *s)
ffio_free_dyn_buf(&track->mdat_buf);
avpriv_packet_list_free(&track->squashed_packet_queue);
+ av_bsf_free(&track->bsf);
}
av_freep(&mov->tracks);
@@ -6964,6 +7032,89 @@ static int mov_create_dvd_sub_decoder_specific_info(MOVTrack *track,
return 0;
}
+static int mov_init_iamf_track(AVFormatContext *s)
+{
+ MOVMuxContext *mov = s->priv_data;
+ MOVTrack *track = &mov->tracks[0]; // IAMF if present is always the first track
+ const AVBitStreamFilter *filter;
+ AVBPrint bprint;
+ AVStream *first_st = NULL;
+ char *args;
+ int nb_audio_elements = 0, nb_mix_presentations = 0;
+ int ret;
+
+ for (int i = 0; i < s->nb_stream_groups; i++) {
+ const AVStreamGroup *stg = s->stream_groups[i];
+
+ if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT)
+ nb_audio_elements++;
+ if (stg->type == AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION)
+ nb_mix_presentations++;
+ }
+
+ if (!nb_audio_elements && !nb_mix_presentations)
+ return 0;
+
+ if ((nb_audio_elements < 1 && nb_audio_elements > 2) || nb_mix_presentations < 1) {
+ av_log(s, AV_LOG_ERROR, "There must be >= 1 and <= 2 IAMF_AUDIO_ELEMENT and at least "
+ "one IAMF_MIX_PRESENTATION stream groups to write a IMAF track\n");
+ return AVERROR(EINVAL);
+ }
+
+ track->iamf = av_mallocz(sizeof(*track->iamf));
+ if (!track->iamf)
+ return AVERROR(ENOMEM);
+
+ av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED);
+
+ for (int i = 0; i < s->nb_stream_groups; i++) {
+ const AVStreamGroup *stg = s->stream_groups[i];
+
+ switch(stg->type) {
+ case AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT:
+ if (!first_st)
+ first_st = stg->streams[0];
+
+ for (int j = 0; j < stg->nb_streams; j++) {
+ av_bprintf(&bprint, "%d=%d%s", s->streams[j]->index, s->streams[j]->id,
+ j < (stg->nb_streams - 1) ? ":" : "");
+ s->streams[j]->priv_data = track;
+ }
+
+ ret = ff_iamf_add_audio_element(track->iamf, stg, s);
+ break;
+ case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION:
+ ret = ff_iamf_add_mix_presentation(track->iamf, stg, s);
+ break;
+ default:
+ av_assert0(0);
+ }
+ if (ret < 0)
+ return ret;
+ }
+
+ av_bprint_finalize(&bprint, &args);
+
+ filter = av_bsf_get_by_name("iamf_stream_merge");
+ if (!filter)
+ return AVERROR_BUG;
+
+ ret = av_bsf_alloc(filter, &track->bsf);
+ if (ret < 0)
+ return ret;
+
+ ret = avcodec_parameters_copy(track->bsf->par_in, first_st->codecpar);
+ if (ret < 0)
+ return ret;
+
+ av_opt_set(track->bsf->priv_data, "index_mapping", args, 0);
+ av_opt_set_int(track->bsf->priv_data, "out_index", first_st->index, 0);
+
+ track->tag = MKTAG('i','a','m','f');
+
+ return av_bsf_init(track->bsf);
+}
+
static int mov_init(AVFormatContext *s)
{
MOVMuxContext *mov = s->priv_data;
@@ -7101,7 +7252,37 @@ static int mov_init(AVFormatContext *s)
s->streams[0]->disposition |= AV_DISPOSITION_DEFAULT;
}
- mov->nb_tracks = s->nb_streams;
+ for (i = 0; i < s->nb_stream_groups; i++) {
+ AVStreamGroup *stg = s->stream_groups[i];
+
+ if (stg->type != AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT)
+ continue;
+
+ for (int j = 0; j < stg->nb_streams; j++) {
+ AVStream *st = stg->streams[j];
+
+ if (st->priv_data) {
+ av_log(s, AV_LOG_ERROR, "Stream %d is present in more than one Stream Group of type "
+ "IAMF Audio Element\n", j);
+ return AVERROR(EINVAL);
+ }
+ st->priv_data = st;
+ }
+
+ if (!mov->nb_tracks) // We support one track for the entire IAMF structure
+ mov->nb_tracks++;
+ }
+
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ if (st->priv_data)
+ continue;
+ st->priv_data = st;
+ mov->nb_tracks++;
+ }
+
+ mov->nb_streams = mov->nb_tracks;
+
if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters)
mov->chapter_track = mov->nb_tracks++;
@@ -7127,7 +7308,7 @@ static int mov_init(AVFormatContext *s)
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
(t || (t=av_dict_get(st->metadata, "timecode", NULL, 0)))) {
AVTimecode tc;
- ret = mov_check_timecode_track(s, &tc, i, t->value);
+ ret = mov_check_timecode_track(s, &tc, st, t->value);
if (ret >= 0)
mov->nb_meta_tmcd++;
}
@@ -7176,18 +7357,33 @@ static int mov_init(AVFormatContext *s)
}
}
+ ret = mov_init_iamf_track(s);
+ if (ret < 0)
+ return ret;
+
+ for (int j = 0, i = 0; j < s->nb_streams; j++) {
+ AVStream *st = s->streams[j];
+
+ if (st != st->priv_data)
+ continue;
+ st->priv_data = &mov->tracks[i++];
+ }
+
for (i = 0; i < s->nb_streams; i++) {
AVStream *st= s->streams[i];
- MOVTrack *track= &mov->tracks[i];
+ MOVTrack *track = st->priv_data;
AVDictionaryEntry *lang = av_dict_get(st->metadata, "language", NULL,0);
- track->st = st;
- track->par = st->codecpar;
+ if (!track->st) {
+ track->st = st;
+ track->par = st->codecpar;
+ }
track->language = ff_mov_iso639_to_lang(lang?lang->value:"und", mov->mode!=MODE_MOV);
if (track->language < 0)
track->language = 32767; // Unspecified Macintosh language code
track->mode = mov->mode;
- track->tag = mov_find_codec_tag(s, track);
+ if (!track->tag)
+ track->tag = mov_find_codec_tag(s, track);
if (!track->tag) {
av_log(s, AV_LOG_ERROR, "Could not find tag for codec %s in stream #%d, "
"codec not currently supported in container\n",
@@ -7378,25 +7574,26 @@ static int mov_write_header(AVFormatContext *s)
{
AVIOContext *pb = s->pb;
MOVMuxContext *mov = s->priv_data;
- int i, ret, hint_track = 0, tmcd_track = 0, nb_tracks = s->nb_streams;
+ int i, ret, hint_track = 0, tmcd_track = 0, nb_tracks = mov->nb_streams;
if (mov->mode & (MODE_MP4|MODE_MOV|MODE_IPOD) && s->nb_chapters)
nb_tracks++;
if (mov->flags & FF_MOV_FLAG_RTP_HINT) {
hint_track = nb_tracks;
- for (i = 0; i < s->nb_streams; i++)
- if (rtp_hinting_needed(s->streams[i]))
+ for (i = 0; i < mov->nb_streams; i++) {
+ if (rtp_hinting_needed(mov->tracks[i].st))
nb_tracks++;
+ }
}
if (mov->nb_meta_tmcd)
tmcd_track = nb_tracks;
- for (i = 0; i < s->nb_streams; i++) {
+ for (i = 0; i < mov->nb_streams; i++) {
int j;
- AVStream *st= s->streams[i];
- MOVTrack *track= &mov->tracks[i];
+ MOVTrack *track = &mov->tracks[i];
+ AVStream *st = track->st;
/* copy extradata if it exists */
if (st->codecpar->extradata_size) {
@@ -7418,8 +7615,8 @@ static int mov_write_header(AVFormatContext *s)
&(AVChannelLayout)AV_CHANNEL_LAYOUT_MONO))
continue;
- for (j = 0; j < s->nb_streams; j++) {
- AVStream *stj= s->streams[j];
+ for (j = 0; j < mov->nb_streams; j++) {
+ AVStream *stj= mov->tracks[j].st;
MOVTrack *trackj= &mov->tracks[j];
if (j == i)
continue;
@@ -7482,8 +7679,8 @@ static int mov_write_header(AVFormatContext *s)
return ret;
if (mov->flags & FF_MOV_FLAG_RTP_HINT) {
- for (i = 0; i < s->nb_streams; i++) {
- if (rtp_hinting_needed(s->streams[i])) {
+ for (i = 0; i < mov->nb_streams; i++) {
+ if (rtp_hinting_needed(mov->tracks[i].st)) {
if ((ret = ff_mov_init_hinting(s, hint_track, i)) < 0)
return ret;
hint_track++;
@@ -7495,8 +7692,8 @@ static int mov_write_header(AVFormatContext *s)
const AVDictionaryEntry *t, *global_tcr = av_dict_get(s->metadata,
"timecode", NULL, 0);
/* Initialize the tmcd tracks */
- for (i = 0; i < s->nb_streams; i++) {
- AVStream *st = s->streams[i];
+ for (i = 0; i < mov->nb_streams; i++) {
+ AVStream *st = mov->tracks[i].st;
t = global_tcr;
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
@@ -7505,7 +7702,7 @@ static int mov_write_header(AVFormatContext *s)
t = av_dict_get(st->metadata, "timecode", NULL, 0);
if (!t)
continue;
- if (mov_check_timecode_track(s, &tc, i, t->value) < 0)
+ if (mov_check_timecode_track(s, &tc, st, t->value) < 0)
continue;
if ((ret = mov_create_timecode_track(s, tmcd_track, i, tc)) < 0)
return ret;
@@ -7626,7 +7823,7 @@ static int mov_write_trailer(AVFormatContext *s)
int64_t moov_pos;
if (mov->need_rewrite_extradata) {
- for (i = 0; i < s->nb_streams; i++) {
+ for (i = 0; i < mov->nb_streams; i++) {
MOVTrack *track = &mov->tracks[i];
AVCodecParameters *par = track->par;
@@ -7766,7 +7963,7 @@ static int avif_write_trailer(AVFormatContext *s)
if (mov->moov_written) return 0;
mov->is_animated_avif = s->streams[0]->nb_frames > 1;
- if (mov->is_animated_avif && s->nb_streams > 1) {
+ if (mov->is_animated_avif && mov->nb_streams > 1) {
// For animated avif with alpha channel, we need to write a tref tag
// with type "auxl".
mov->tracks[1].tref_tag = MKTAG('a', 'u', 'x', 'l');
@@ -7776,7 +7973,7 @@ static int avif_write_trailer(AVFormatContext *s)
mov_write_meta_tag(pb, mov, s);
moov_size = get_moov_size(s);
- for (i = 0; i < s->nb_streams; i++)
+ for (i = 0; i < mov->nb_tracks; i++)
mov->tracks[i].data_offset = avio_tell(pb) + moov_size + 8;
if (mov->is_animated_avif) {
@@ -7798,7 +7995,7 @@ static int avif_write_trailer(AVFormatContext *s)
// write extent offsets.
pos_backup = avio_tell(pb);
- for (i = 0; i < s->nb_streams; i++) {
+ for (i = 0; i < mov->nb_streams; i++) {
if (extent_offsets[i] != (uint32_t)extent_offsets[i]) {
av_log(s, AV_LOG_ERROR, "extent offset does not fit in 32 bits\n");
return AVERROR_INVALIDDATA;
diff --git a/libavformat/movenc.h b/libavformat/movenc.h
index 60363198c9..fee3e759e0 100644
--- a/libavformat/movenc.h
+++ b/libavformat/movenc.h
@@ -25,7 +25,9 @@
#define AVFORMAT_MOVENC_H
#include "avformat.h"
+#include "iamf.h"
#include "movenccenc.h"
+#include "libavcodec/bsf.h"
#include "libavcodec/packet_internal.h"
#define MOV_FRAG_INFO_ALLOC_INCREMENT 64
@@ -170,6 +172,10 @@ typedef struct MOVTrack {
unsigned int squash_fragment_samples_to_one; //< flag to note formats where all samples for a fragment are to be squashed
PacketList squashed_packet_queue;
+
+ AVBSFContext *bsf;
+
+ IAMFContext *iamf;
} MOVTrack;
typedef enum {
@@ -188,6 +194,7 @@ typedef struct MOVMuxContext {
const AVClass *av_class;
int mode;
int64_t time;
+ int nb_streams;
int nb_tracks;
int nb_meta_tmcd; ///< number of new created tmcd track based on metadata (aka not data copy)
int chapter_track; ///< qt chapter track number
--
2.43.0
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf
2024-01-30 17:32 [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
` (5 preceding siblings ...)
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 7/7] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
@ 2024-01-30 21:47 ` Andreas Rheinhardt
2024-01-30 22:07 ` James Almer
6 siblings, 1 reply; 19+ messages in thread
From: Andreas Rheinhardt @ 2024-01-30 21:47 UTC (permalink / raw)
To: ffmpeg-devel
James Almer:
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> libavcodec/bitstream_filters.c | 1 +
> libavcodec/bsf/Makefile | 1 +
> libavcodec/bsf/iamf_stream_split_bsf.c | 813 +++++++++++++++++++++++++
> 3 files changed, 815 insertions(+)
> create mode 100644 libavcodec/bsf/iamf_stream_split_bsf.c
>
> diff --git a/libavcodec/bitstream_filters.c b/libavcodec/bitstream_filters.c
> index 1e9a676a3d..640b821413 100644
> --- a/libavcodec/bitstream_filters.c
> +++ b/libavcodec/bitstream_filters.c
> @@ -42,6 +42,7 @@ extern const FFBitStreamFilter ff_h264_redundant_pps_bsf;
> extern const FFBitStreamFilter ff_hapqa_extract_bsf;
> extern const FFBitStreamFilter ff_hevc_metadata_bsf;
> extern const FFBitStreamFilter ff_hevc_mp4toannexb_bsf;
> +extern const FFBitStreamFilter ff_iamf_stream_split_bsf;
> extern const FFBitStreamFilter ff_imx_dump_header_bsf;
> extern const FFBitStreamFilter ff_media100_to_mjpegb_bsf;
> extern const FFBitStreamFilter ff_mjpeg2jpeg_bsf;
> diff --git a/libavcodec/bsf/Makefile b/libavcodec/bsf/Makefile
> index 7831b0f2aa..80dcdf94fb 100644
> --- a/libavcodec/bsf/Makefile
> +++ b/libavcodec/bsf/Makefile
> @@ -20,6 +20,7 @@ OBJS-$(CONFIG_H264_REDUNDANT_PPS_BSF) += bsf/h264_redundant_pps.o
> OBJS-$(CONFIG_HAPQA_EXTRACT_BSF) += bsf/hapqa_extract.o
> OBJS-$(CONFIG_HEVC_METADATA_BSF) += bsf/h265_metadata.o
> OBJS-$(CONFIG_HEVC_MP4TOANNEXB_BSF) += bsf/hevc_mp4toannexb.o
> +OBJS-$(CONFIG_IAMF_STREAM_MERGE_BSF) += bsf/iamf_stream_merge_bsf.o
> OBJS-$(CONFIG_IMX_DUMP_HEADER_BSF) += bsf/imx_dump_header.o
> OBJS-$(CONFIG_MEDIA100_TO_MJPEGB_BSF) += bsf/media100_to_mjpegb.o
> OBJS-$(CONFIG_MJPEG2JPEG_BSF) += bsf/mjpeg2jpeg.o
> diff --git a/libavcodec/bsf/iamf_stream_split_bsf.c b/libavcodec/bsf/iamf_stream_split_bsf.c
> new file mode 100644
> index 0000000000..01193801d7
> --- /dev/null
> +++ b/libavcodec/bsf/iamf_stream_split_bsf.c
> @@ -0,0 +1,813 @@
> +/*
> + * 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 <stdint.h>
> +#include <stddef.h>
> +
> +#include "libavutil/dict.h"
> +#include "libavutil/opt.h"
> +#include "libavformat/iamf.h"
> +#include "bsf.h"
> +#include "bsf_internal.h"
> +#include "get_bits.h"
> +
> +typedef struct ParamDefinition {
> + AVIAMFParamDefinition *param;
> + size_t param_size;
> + int mode;
> + int recon_gain_present_bitmask;
> +} ParamDefinition;
> +
> +typedef struct IAMFSplitContext {
> + AVClass *class;
> + AVPacket *buffer_pkt;
> +
> + ParamDefinition *param_definitions;
> + unsigned int nb_param_definitions;
> +
> + unsigned int *ids;
> + int nb_ids;
> +
> + // AVOptions
> + int first_index;
> +
> + // Packet side data
> + AVIAMFParamDefinition *mix;
> + size_t mix_size;
> + AVIAMFParamDefinition *demix;
> + size_t demix_size;
> + AVIAMFParamDefinition *recon;
> + size_t recon_size;
> +} IAMFSplitContext;
> +
> +static int param_parse(AVBSFContext *ctx, GetBitContext *gb,
> + unsigned int type,
> + ParamDefinition **out)
> +{
> + IAMFSplitContext *const c = ctx->priv_data;
> + ParamDefinition *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 = get_leb(gb);
> +
> + 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 = get_leb(gb);
> + mode = get_bits(gb, 8) >> 7;
> +
> + if (mode == 0) {
> + duration = get_leb(gb);
> + constant_subblock_duration = get_leb(gb);
> + if (constant_subblock_duration == 0) {
> + nb_subblocks = get_leb(gb);
> + } 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++) {
> + if (constant_subblock_duration == 0)
> + get_leb(gb); // subblock_duration
> +
> + switch (type) {
> + case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN:
> + break;
> + case AV_IAMF_PARAMETER_DEFINITION_DEMIXING:
> + skip_bits(gb, 8); // dmixp_mode
> + break;
> + case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN:
> + 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(ctx, AV_LOG_ERROR, "Incosistent parameters for parameter_id %u\n", parameter_id);
> + av_free(param);
> + return AVERROR_INVALIDDATA;
> + }
> + av_freep(¶m);
> + } else {
> + ParamDefinition *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 = &c->param_definitions[c->nb_param_definitions++];
> + param_definition->param = param;
> + param_definition->mode = !mode;
> + param_definition->param_size = param_size;
> + }
> + if (out)
> + *out = param_definition;
> +
> + return 0;
> +}
> +
> +static int scalable_channel_layout_config(AVBSFContext *ctx, GetBitContext *gb,
> + ParamDefinition *recon_gain)
> +{
> + int nb_layers;
> +
> + nb_layers = get_bits(gb, 3);
> + skip_bits(gb, 5); //reserved
> +
> + if (nb_layers > 6)
> + return AVERROR_INVALIDDATA;
> +
> + for (int i = 0; i < nb_layers; i++) {
> + int output_gain_is_present_flag, recon_gain_is_present;
> +
> + skip_bits(gb, 4); // loudspeaker_layout
> + output_gain_is_present_flag = get_bits1(gb);
> + recon_gain_is_present = get_bits1(gb);
> + if (recon_gain)
> + recon_gain->recon_gain_present_bitmask |= recon_gain_is_present << i;
> + skip_bits(gb, 2); // reserved
> + skip_bits(gb, 8); // substream_count
> + skip_bits(gb, 8); // coupled_substream_count
> + if (output_gain_is_present_flag) {
> + skip_bits(gb, 8); // output_gain_flags & reserved
> + skip_bits(gb, 16); // output_gain
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int audio_element_obu(AVBSFContext *ctx, uint8_t *buf, unsigned size)
> +{
> + IAMFSplitContext *const c = ctx->priv_data;
> + GetBitContext gb;
> + ParamDefinition *recon_gain = NULL;
> + unsigned audio_element_type;
> + unsigned num_substreams, num_parameters;
> + int ret;
> +
> + ret = init_get_bits8(&gb, buf, size);
> + if (ret < 0)
> + return ret;
> +
> + get_leb(&gb); // audio_element_id
> + audio_element_type = get_bits(&gb, 3);
> + skip_bits(&gb, 5); // reserved
> +
> + get_leb(&gb); // codec_config_id
> + num_substreams = get_leb(&gb);
> + for (unsigned i = 0; i < num_substreams; i++) {
> + unsigned *audio_substream_id = av_dynarray2_add((void **)&c->ids, &c->nb_ids,
> + sizeof(*c->ids), NULL);
> + if (!audio_substream_id)
> + return AVERROR(ENOMEM);
> +
> + *audio_substream_id = get_leb(&gb);
> + }
> +
> + num_parameters = get_leb(&gb);
> + if (num_parameters && audio_element_type != 0) {
> + av_log(ctx, AV_LOG_ERROR, "Audio Element parameter count %u is invalid"
> + " for Scene representations\n", num_parameters);
> + return AVERROR_INVALIDDATA;
> + }
> +
> + for (int i = 0; i < num_parameters; i++) {
> + unsigned type = get_leb(&gb);
> +
> + if (type == AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN)
> + return AVERROR_INVALIDDATA;
> + else if (type == AV_IAMF_PARAMETER_DEFINITION_DEMIXING) {
> + ret = param_parse(ctx, &gb, type, NULL);
> + if (ret < 0)
> + return ret;
> + skip_bits(&gb, 8); // default_w
> + } else if (type == AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN) {
> + ret = param_parse(ctx, &gb, type, &recon_gain);
> + if (ret < 0)
> + return ret;
> + } else {
> + unsigned param_definition_size = get_leb(&gb);
> + skip_bits_long(&gb, param_definition_size * 8);
> + }
> + }
> +
> + if (audio_element_type == AV_IAMF_AUDIO_ELEMENT_TYPE_CHANNEL) {
> + ret = scalable_channel_layout_config(ctx, &gb, recon_gain);
> + if (ret < 0)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +static int label_string(GetBitContext *gb)
> +{
> + int byte;
> +
> + do {
> + byte = get_bits(gb, 8);
> + } while (byte);
> +
> + return 0;
> +}
> +
> +static int mix_presentation_obu(AVBSFContext *ctx, uint8_t *buf, unsigned size)
> +{
> + GetBitContext gb;
> + unsigned mix_presentation_id, count_label;
> + unsigned nb_submixes, nb_elements;
> + int ret;
> +
> + ret = init_get_bits8(&gb, buf, size);
> + if (ret < 0)
> + return ret;
> +
> + mix_presentation_id = get_leb(&gb);
> + count_label = get_leb(&gb);
> +
> + for (int i = 0; i < count_label; i++) {
> + ret = label_string(&gb);
> + if (ret < 0)
> + return ret;
> + }
> +
> + for (int i = 0; i < count_label; i++) {
> + ret = label_string(&gb);
> + if (ret < 0)
> + return ret;
> + }
> +
> + nb_submixes = get_leb(&gb);
> + for (int i = 0; i < nb_submixes; i++) {
> + unsigned nb_layouts;
> +
> + nb_elements = get_leb(&gb);
> +
> + for (int j = 0; j < nb_elements; j++) {
> + unsigned rendering_config_extension_size;
> +
> + get_leb(&gb); // audio_element_id
> + for (int k = 0; k < count_label; k++) {
> + ret = label_string(&gb);
> + if (ret < 0)
> + return ret;
> + }
> +
> + skip_bits(&gb, 8); // headphones_rendering_mode & reserved
> + rendering_config_extension_size = get_leb(&gb);
> + skip_bits_long(&gb, rendering_config_extension_size * 8);
> +
> + ret = param_parse(ctx, &gb, AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, NULL);
> + if (ret < 0)
> + return ret;
> + skip_bits(&gb, 16); // default_mix_gain
> + }
> +
> + ret = param_parse(ctx, &gb, AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN, NULL);
> + if (ret < 0)
> + return ret;
> + get_bits(&gb, 16); // default_mix_gain
> +
> + nb_layouts = get_leb(&gb);
> + for (int j = 0; j < nb_layouts; j++) {
> + int info_type, layout_type;
> + int byte = get_bits(&gb, 8);
> +
> + layout_type = byte >> 6;
> + if (layout_type < AV_IAMF_SUBMIX_LAYOUT_TYPE_LOUDSPEAKERS &&
> + layout_type > AV_IAMF_SUBMIX_LAYOUT_TYPE_BINAURAL) {
> + av_log(ctx, AV_LOG_ERROR, "Invalid Layout type %u in a submix from Mix Presentation %u\n",
> + layout_type, mix_presentation_id);
> + return AVERROR_INVALIDDATA;
> + }
> +
> + info_type = get_bits(&gb, 8);
> + get_bits(&gb, 16); // integrated_loudness
> + get_bits(&gb, 16); // digital_peak
> +
> + if (info_type & 1)
> + get_bits(&gb, 16); // true_peak
> +
> + if (info_type & 2) {
> + unsigned int num_anchored_loudness = get_bits(&gb, 8);
> +
> + for (int k = 0; k < num_anchored_loudness; k++) {
> + get_bits(&gb, 8); // anchor_element
> + get_bits(&gb, 16); // anchored_loudness
> + }
> + }
> +
> + if (info_type & 0xFC) {
> + unsigned int info_type_size = get_leb(&gb);
> + skip_bits_long(&gb, info_type_size * 8);
> + }
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int find_idx_by_id(AVBSFContext *ctx, unsigned id)
> +{
> + IAMFSplitContext *const c = ctx->priv_data;
> +
> + for (int i = 0; i < c->nb_ids; i++) {
> + unsigned audio_substream_id = c->ids[i];
> +
> + if (audio_substream_id == id)
> + return i;
> + }
> +
> + av_log(ctx, AV_LOG_ERROR, "Invalid id %d\n", id);
> + return AVERROR_INVALIDDATA;
> +}
> +
> +static int audio_frame_obu(AVBSFContext *ctx, enum IAMF_OBU_Type type, int *idx,
> + uint8_t *buf, int *start_pos, unsigned *size,
> + int id_in_bitstream)
> +{
> + GetBitContext gb;
> + unsigned audio_substream_id;
> + int ret;
> +
> + ret = init_get_bits8(&gb, buf + *start_pos, *size);
> + if (ret < 0)
> + return ret;
> +
> + if (id_in_bitstream) {
> + int pos;
> + audio_substream_id = get_leb(&gb);
> + pos = get_bits_count(&gb) / 8;
> + *start_pos += pos;
> + *size -= pos;
> + } else
> + audio_substream_id = type - IAMF_OBU_IA_AUDIO_FRAME_ID0;
> +
> + ret = find_idx_by_id(ctx, audio_substream_id);
> + if (ret < 0)
> + return ret;
> +
> + *idx = ret;
> +
> + return 0;
> +}
> +
> +static const ParamDefinition *get_param_definition(AVBSFContext *ctx, unsigned int parameter_id)
> +{
> + const IAMFSplitContext *const c = ctx->priv_data;
> + const ParamDefinition *param_definition = NULL;
> +
> + 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;
> + }
> +
> + return param_definition;
> +}
> +
> +static int parameter_block_obu(AVBSFContext *ctx, uint8_t *buf, unsigned size)
> +{
> + IAMFSplitContext *const c = ctx->priv_data;
> + GetBitContext gb;
> + const ParamDefinition *param_definition;
> + const AVIAMFParamDefinition *param;
> + AVIAMFParamDefinition *out_param = NULL;
> + unsigned int duration, constant_subblock_duration;
> + unsigned int nb_subblocks;
> + unsigned int parameter_id;
> + size_t out_param_size;
> + int ret;
> +
> + ret = init_get_bits8(&gb, buf, size);
> + if (ret < 0)
> + return ret;
> +
> + parameter_id = get_leb(&gb);
> +
> + param_definition = get_param_definition(ctx, parameter_id);
> + if (!param_definition) {
> + ret = 0;
> + goto fail;
> + }
> +
> + param = param_definition->param;
> + if (!param_definition->mode) {
> + duration = get_leb(&gb);
> + constant_subblock_duration = get_leb(&gb);
> + if (constant_subblock_duration == 0)
> + nb_subblocks = get_leb(&gb);
> + else
> + nb_subblocks = duration / constant_subblock_duration;
> + } else {
> + duration = param->duration;
> + constant_subblock_duration = param->constant_subblock_duration;
> + nb_subblocks = param->nb_subblocks;
> + if (!nb_subblocks)
> + nb_subblocks = duration / constant_subblock_duration;
> + }
> +
> + out_param = av_iamf_param_definition_alloc(param->type, nb_subblocks, &out_param_size);
> + if (!out_param) {
> + ret = AVERROR(ENOMEM);
> + goto fail;
> + }
> +
> + out_param->parameter_id = param->parameter_id;
> + out_param->type = param->type;
> + out_param->parameter_rate = param->parameter_rate;
> + out_param->duration = duration;
> + out_param->constant_subblock_duration = constant_subblock_duration;
> + out_param->nb_subblocks = nb_subblocks;
> +
> + for (int i = 0; i < nb_subblocks; i++) {
> + void *subblock = av_iamf_param_definition_get_subblock(out_param, i);
> + unsigned int subblock_duration = constant_subblock_duration;
> +
> + if (!param_definition->mode && !constant_subblock_duration)
> + subblock_duration = get_leb(&gb);
> +
> + switch (param->type) {
> + case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN: {
> + AVIAMFMixGain *mix = subblock;
> +
> + mix->animation_type = get_leb(&gb);
> + if (mix->animation_type > AV_IAMF_ANIMATION_TYPE_BEZIER) {
> + ret = 0;
> + av_free(out_param);
> + goto fail;
> + }
> +
> + mix->start_point_value = av_make_q(sign_extend(get_bits(&gb, 16), 16), 1 << 8);
> + if (mix->animation_type >= AV_IAMF_ANIMATION_TYPE_LINEAR)
> + mix->end_point_value = av_make_q(sign_extend(get_bits(&gb, 16), 16), 1 << 8);
> + if (mix->animation_type == AV_IAMF_ANIMATION_TYPE_BEZIER) {
> + mix->control_point_value = av_make_q(sign_extend(get_bits(&gb, 16), 16), 1 << 8);
> + mix->control_point_relative_time = av_make_q(get_bits(&gb, 8), 1 << 8);
> + }
> + mix->subblock_duration = subblock_duration;
> + break;
> + }
> + case AV_IAMF_PARAMETER_DEFINITION_DEMIXING: {
> + AVIAMFDemixingInfo *demix = subblock;
> +
> + demix->dmixp_mode = get_bits(&gb, 3);
> + skip_bits(&gb, 5); // reserved
> + demix->subblock_duration = subblock_duration;
> + break;
> + }
> + case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN: {
> + AVIAMFReconGain *recon = subblock;
> +
> + for (int i = 0; i < 6; i++) {
> + if (param_definition->recon_gain_present_bitmask & (1 << i)) {
> + unsigned int recon_gain_flags = get_leb(&gb);
> + unsigned int bitcount = 7 + 5 * !!(recon_gain_flags & 0x80);
> + recon_gain_flags = (recon_gain_flags & 0x7F) | ((recon_gain_flags & 0xFF00) >> 1);
> + for (int j = 0; j < bitcount; j++) {
> + if (recon_gain_flags & (1 << j))
> + recon->recon_gain[i][j] = get_bits(&gb, 8);
> + }
> + }
> + }
> + recon->subblock_duration = subblock_duration;
> + break;
> + }
> + default:
> + av_assert0(0);
> + }
> + }
> +
> + switch (param->type) {
> + case AV_IAMF_PARAMETER_DEFINITION_MIX_GAIN:
> + av_free(c->mix);
> + c->mix = out_param;
> + c->mix_size = out_param_size;
> + break;
> + case AV_IAMF_PARAMETER_DEFINITION_DEMIXING:
> + av_free(c->demix);
> + c->demix = out_param;
> + c->demix_size = out_param_size;
> + break;
> + case AV_IAMF_PARAMETER_DEFINITION_RECON_GAIN:
> + av_free(c->recon);
> + c->recon = out_param;
> + c->recon_size = out_param_size;
> + break;
> + default:
> + av_assert0(0);
> + }
> +
> + ret = 0;
> +fail:
> + if (ret < 0)
> + av_free(out_param);
> +
> + return ret;
> +}
> +
> +static int 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 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);
This stuff here should not a GetBitContext at all, as basically
everything is byte-aligned (and the flags above are in known bits).
> + if (*obu_size > INT_MAX)
> + return AVERROR_INVALIDDATA;
> +
> + start = get_bits_count(&gb) / 8;
> +
> + if (trimming) {
> + *skip_samples = get_leb(&gb); // num_samples_to_trim_at_end
> + *discard_padding = get_leb(&gb); // num_samples_to_trim_at_start
> + }
> +
> + if (extension_flag) {
> + unsigned 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;
> +}
> +
> +static int iamf_stream_split_filter(AVBSFContext *ctx, AVPacket *out)
> +{
> + IAMFSplitContext *const c = ctx->priv_data;
> + int ret = 0;
> +
> + if (!c->buffer_pkt->data) {
> + ret = ff_bsf_get_packet_ref(ctx, c->buffer_pkt);
> + if (ret < 0)
> + return ret;
> + }
> +
> + while (1) {
> + enum IAMF_OBU_Type type;
> + unsigned skip_samples = 0, discard_padding = 0, obu_size;
> + int len, start_pos, idx;
> +
> + len = iamf_parse_obu_header(c->buffer_pkt->data,
> + c->buffer_pkt->size,
> + &obu_size, &start_pos, &type,
> + &skip_samples, &discard_padding);
> + if (len < 0) {
> + av_log(ctx, AV_LOG_ERROR, "Failed to read obu\n");
> + ret = len;
> + goto fail;
> + }
> +
> + if (type >= IAMF_OBU_IA_AUDIO_FRAME && type <= IAMF_OBU_IA_AUDIO_FRAME_ID17) {
> + ret = audio_frame_obu(ctx, type, &idx,
> + c->buffer_pkt->data, &start_pos,
> + &obu_size,
> + type == IAMF_OBU_IA_AUDIO_FRAME);
> + if (ret < 0)
> + goto fail;
> + } else {
> + switch (type) {
> + case IAMF_OBU_IA_AUDIO_ELEMENT:
> + ret = audio_element_obu(ctx, c->buffer_pkt->data + start_pos, obu_size);
> + if (ret < 0)
> + goto fail;
> + break;
> + case IAMF_OBU_IA_MIX_PRESENTATION:
> + ret = mix_presentation_obu(ctx, c->buffer_pkt->data + start_pos, obu_size);
> + if (ret < 0)
> + goto fail;
> + break;
> + case IAMF_OBU_IA_PARAMETER_BLOCK:
> + ret = parameter_block_obu(ctx, c->buffer_pkt->data + start_pos, obu_size);
> + if (ret < 0)
> + goto fail;
> + break;
> + case IAMF_OBU_IA_SEQUENCE_HEADER:
> + for (int i = 0; c->param_definitions && i < c->nb_param_definitions; i++)
> + av_free(c->param_definitions[i].param);
> + av_freep(&c->param_definitions);
> + av_freep(&c->ids);
> + c->nb_param_definitions = 0;
> + c->nb_ids = 0;
> + // fall-through
> + case IAMF_OBU_IA_TEMPORAL_DELIMITER:
> + av_freep(&c->mix);
> + av_freep(&c->demix);
> + av_freep(&c->recon);
> + c->mix_size = 0;
> + c->demix_size = 0;
> + c->recon_size = 0;
> + break;
> + }
> +
> + c->buffer_pkt->data += len;
> + c->buffer_pkt->size -= len;
> +
> + if (!c->buffer_pkt->size) {
> + av_packet_unref(c->buffer_pkt);
> + ret = ff_bsf_get_packet_ref(ctx, c->buffer_pkt);
> + if (ret < 0)
> + return ret;
> + } else if (c->buffer_pkt->size < 0) {
> + ret = AVERROR_INVALIDDATA;
> + goto fail;
> + }
> + continue;
> + }
> +
> + if (c->buffer_pkt->size > INT_MAX - len) {
> + ret = AVERROR_INVALIDDATA;
> + goto fail;
> + }
> +
> + ret = av_packet_ref(out, c->buffer_pkt);
> + if (ret < 0)
> + goto fail;
> +
> + if (skip_samples || discard_padding) {
> + uint8_t *side_data = av_packet_new_side_data(out, AV_PKT_DATA_SKIP_SAMPLES, 10);
> + if (!side_data)
> + return AVERROR(ENOMEM);
> + AV_WL32(side_data, skip_samples);
> + AV_WL32(side_data + 4, discard_padding);
> + }
> + if (c->mix) {
> + uint8_t *side_data = av_packet_new_side_data(out, AV_PKT_DATA_IAMF_MIX_GAIN_PARAM, c->mix_size);
> + if (!side_data)
> + return AVERROR(ENOMEM);
> + memcpy(side_data, c->mix, c->mix_size);
> + }
> + if (c->demix) {
> + uint8_t *side_data = av_packet_new_side_data(out, AV_PKT_DATA_IAMF_DEMIXING_INFO_PARAM, c->demix_size);
> + if (!side_data)
> + return AVERROR(ENOMEM);
> + memcpy(side_data, c->demix, c->demix_size);
> + }
> + if (c->recon) {
> + uint8_t *side_data = av_packet_new_side_data(out, AV_PKT_DATA_IAMF_RECON_GAIN_INFO_PARAM, c->recon_size);
> + if (!side_data)
> + return AVERROR(ENOMEM);
> + memcpy(side_data, c->recon, c->recon_size);
> + }
> +
> + out->data += start_pos;
> + out->size = obu_size;
> + out->stream_index = idx + c->first_index;
> +
> + c->buffer_pkt->data += len;
> + c->buffer_pkt->size -= len;
> +
> + if (!c->buffer_pkt->size)
> + av_packet_unref(c->buffer_pkt);
> + else if (c->buffer_pkt->size < 0) {
> + ret = AVERROR_INVALIDDATA;
> + goto fail;
> + }
> +
> + return 0;
> + }
> +
> +fail:
> + if (ret < 0) {
> + av_packet_unref(out);
> + av_packet_unref(c->buffer_pkt);
> + }
> +
> + return ret;
> +}
> +
> +static int iamf_stream_split_init(AVBSFContext *ctx)
> +{
> + IAMFSplitContext *const c = ctx->priv_data;
> +
> + c->buffer_pkt = av_packet_alloc();
> + if (!c->buffer_pkt)
> + return AVERROR(ENOMEM);
> +
> + return 0;
> +}
> +
> +static void iamf_stream_split_flush(AVBSFContext *ctx)
> +{
> + IAMFSplitContext *const c = ctx->priv_data;
> +
> + if (c->buffer_pkt)
> + av_packet_unref(c->buffer_pkt);
> +
> + av_freep(&c->mix);
> + av_freep(&c->demix);
> + av_freep(&c->recon);
> + c->mix_size = 0;
> + c->demix_size = 0;
> + c->recon_size = 0;
> +}
> +
> +static void iamf_stream_split_close(AVBSFContext *ctx)
> +{
> + IAMFSplitContext *const c = ctx->priv_data;
> +
> + iamf_stream_split_flush(ctx);
> + av_packet_free(&c->buffer_pkt);
> +
> + for (int i = 0; c->param_definitions && i < c->nb_param_definitions; i++)
> + av_free(c->param_definitions[i].param);
> + av_freep(&c->param_definitions);
> + c->nb_param_definitions = 0;
> +
> + av_freep(&c->ids);
> + c->nb_ids = 0;
> +}
> +
> +#define OFFSET(x) offsetof(IAMFSplitContext, x)
> +#define FLAGS (AV_OPT_FLAG_AUDIO_PARAM|AV_OPT_FLAG_BSF_PARAM)
> +static const AVOption iamf_stream_split_options[] = {
> + { "first_index", "First index to set stream index in output packets",
> + OFFSET(first_index), AV_OPT_TYPE_INT, { 0 }, 0, INT_MAX, FLAGS },
> + { NULL }
> +};
> +
> +static const AVClass iamf_stream_split_class = {
> + .class_name = "iamf_stream_split_bsf",
> + .item_name = av_default_item_name,
> + .option = iamf_stream_split_options,
> + .version = LIBAVUTIL_VERSION_INT,
> +};
> +
> +static const enum AVCodecID iamf_stream_split_codec_ids[] = {
> + AV_CODEC_ID_PCM_S16LE, AV_CODEC_ID_PCM_S16BE,
> + AV_CODEC_ID_PCM_S24LE, AV_CODEC_ID_PCM_S24BE,
> + AV_CODEC_ID_PCM_S32LE, AV_CODEC_ID_PCM_S32BE,
> + AV_CODEC_ID_OPUS, AV_CODEC_ID_AAC,
> + AV_CODEC_ID_FLAC, AV_CODEC_ID_NONE,
> +};
> +
> +const FFBitStreamFilter ff_iamf_stream_split_bsf = {
> + .p.name = "iamf_stream_split",
> + .p.codec_ids = iamf_stream_split_codec_ids,
> + .p.priv_class = &iamf_stream_split_class,
> + .priv_data_size = sizeof(IAMFSplitContext),
> + .init = iamf_stream_split_init,
> + .flush = iamf_stream_split_flush,
> + .close = iamf_stream_split_close,
> + .filter = iamf_stream_split_filter,
> +};
This needs to add documentation for what this BSF is actually supposed
to do. Right now it seems crazy: It parses the packet's data and expects
to find OBU headers, although the input data is supposed to be PCM,
Opus, AAC or Flac.
- Andreas
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [FFmpeg-devel] [PATCH 6/7] avcodec/put_bits: add put_leb()
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 6/7] avcodec/put_bits: add put_leb() James Almer
@ 2024-01-30 21:53 ` Andreas Rheinhardt
2024-01-30 21:59 ` James Almer
0 siblings, 1 reply; 19+ messages in thread
From: Andreas Rheinhardt @ 2024-01-30 21:53 UTC (permalink / raw)
To: ffmpeg-devel
James Almer:
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> libavcodec/put_bits.h | 16 ++++++++++++++++
> 1 file changed, 16 insertions(+)
>
> diff --git a/libavcodec/put_bits.h b/libavcodec/put_bits.h
> index 4561dc131a..04dedd3342 100644
> --- a/libavcodec/put_bits.h
> +++ b/libavcodec/put_bits.h
> @@ -370,6 +370,22 @@ static inline void put_sbits63(PutBitContext *pb, int n, int64_t value)
> put_bits64(pb, n, (uint64_t)(value) & (~(UINT64_MAX << n)));
> }
>
> +static inline void put_leb(PutBitContext *s, unsigned value)
> +{
> + int len;
> + uint8_t byte;
> +
> + len = (av_log2(value) + 7) / 7;
> +
> + for (int i = 0; i < len; i++) {
> + byte = value >> (7 * i) & 0x7f;
> + if (i < len - 1)
> + byte |= 0x80;
> +
> + put_bits_no_assert(s, 8, byte);
> + }
> +}
> +
> /**
> * Return the pointer to the byte where the bitstream writer will put
> * the next bit.
This function does use any internals of PutBitContext and should
therefore be in user-code. In fact, get_leb() should not exist in
get_bits.h.
- Andreas
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [FFmpeg-devel] [PATCH 6/7] avcodec/put_bits: add put_leb()
2024-01-30 21:53 ` Andreas Rheinhardt
@ 2024-01-30 21:59 ` James Almer
2024-01-30 22:10 ` Andreas Rheinhardt
0 siblings, 1 reply; 19+ messages in thread
From: James Almer @ 2024-01-30 21:59 UTC (permalink / raw)
To: ffmpeg-devel
On 1/30/2024 6:53 PM, Andreas Rheinhardt wrote:
> James Almer:
>> Signed-off-by: James Almer <jamrial@gmail.com>
>> ---
>> libavcodec/put_bits.h | 16 ++++++++++++++++
>> 1 file changed, 16 insertions(+)
>>
>> diff --git a/libavcodec/put_bits.h b/libavcodec/put_bits.h
>> index 4561dc131a..04dedd3342 100644
>> --- a/libavcodec/put_bits.h
>> +++ b/libavcodec/put_bits.h
>> @@ -370,6 +370,22 @@ static inline void put_sbits63(PutBitContext *pb, int n, int64_t value)
>> put_bits64(pb, n, (uint64_t)(value) & (~(UINT64_MAX << n)));
>> }
>>
>> +static inline void put_leb(PutBitContext *s, unsigned value)
>> +{
>> + int len;
>> + uint8_t byte;
>> +
>> + len = (av_log2(value) + 7) / 7;
>> +
>> + for (int i = 0; i < len; i++) {
>> + byte = value >> (7 * i) & 0x7f;
>> + if (i < len - 1)
>> + byte |= 0x80;
>> +
>> + put_bits_no_assert(s, 8, byte);
>> + }
>> +}
>> +
>> /**
>> * Return the pointer to the byte where the bitstream writer will put
>> * the next bit.
>
> This function does use any internals of PutBitContext and should
> therefore be in user-code. In fact, get_leb() should not exist in
> get_bits.h.
You could say the same about skip_1stop_8data_bits(), decode210(),
decode012() and get_vlc_multi().
And the idea is to have these be reusable. leb is evidently going to be
used by anything AOMedia will make.
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf
2024-01-30 21:47 ` [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf Andreas Rheinhardt
@ 2024-01-30 22:07 ` James Almer
2024-01-30 22:11 ` Andreas Rheinhardt
0 siblings, 1 reply; 19+ messages in thread
From: James Almer @ 2024-01-30 22:07 UTC (permalink / raw)
To: ffmpeg-devel
On 1/30/2024 6:47 PM, Andreas Rheinhardt wrote:
>> + *obu_size = get_leb(&gb);
> This stuff here should not a GetBitContext at all, as basically
> everything is byte-aligned (and the flags above are in known bits).
I'm not going to write yet another leb() reading function to work on raw
bytes. We have enough scattered around and in fact we should try to
remove most.
>> +static const enum AVCodecID iamf_stream_split_codec_ids[] = {
>> + AV_CODEC_ID_PCM_S16LE, AV_CODEC_ID_PCM_S16BE,
>> + AV_CODEC_ID_PCM_S24LE, AV_CODEC_ID_PCM_S24BE,
>> + AV_CODEC_ID_PCM_S32LE, AV_CODEC_ID_PCM_S32BE,
>> + AV_CODEC_ID_OPUS, AV_CODEC_ID_AAC,
>> + AV_CODEC_ID_FLAC, AV_CODEC_ID_NONE,
>> +};
>> +
>> +const FFBitStreamFilter ff_iamf_stream_split_bsf = {
>> + .p.name = "iamf_stream_split",
>> + .p.codec_ids = iamf_stream_split_codec_ids,
>> + .p.priv_class = &iamf_stream_split_class,
>> + .priv_data_size = sizeof(IAMFSplitContext),
>> + .init = iamf_stream_split_init,
>> + .flush = iamf_stream_split_flush,
>> + .close = iamf_stream_split_close,
>> + .filter = iamf_stream_split_filter,
>> +};
>
> This needs to add documentation for what this BSF is actually supposed
> to do. Right now it seems crazy: It parses the packet's data and expects
> to find OBU headers, although the input data is supposed to be PCM,
> Opus, AAC or Flac.
It's not too different than aac_adtstoasc in that it takes audio from
those codecs listed above encapsulated in one form and returns it in
another form.
In this case, it takes OBUs containing one or more audio frames, removes
the OBU encapsulation, and propagates each raw audio frame in separate
packets.
I'll write some documentation.
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [FFmpeg-devel] [PATCH 6/7] avcodec/put_bits: add put_leb()
2024-01-30 21:59 ` James Almer
@ 2024-01-30 22:10 ` Andreas Rheinhardt
2024-01-30 22:14 ` James Almer
0 siblings, 1 reply; 19+ messages in thread
From: Andreas Rheinhardt @ 2024-01-30 22:10 UTC (permalink / raw)
To: ffmpeg-devel
James Almer:
> On 1/30/2024 6:53 PM, Andreas Rheinhardt wrote:
>> James Almer:
>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>> ---
>>> libavcodec/put_bits.h | 16 ++++++++++++++++
>>> 1 file changed, 16 insertions(+)
>>>
>>> diff --git a/libavcodec/put_bits.h b/libavcodec/put_bits.h
>>> index 4561dc131a..04dedd3342 100644
>>> --- a/libavcodec/put_bits.h
>>> +++ b/libavcodec/put_bits.h
>>> @@ -370,6 +370,22 @@ static inline void put_sbits63(PutBitContext
>>> *pb, int n, int64_t value)
>>> put_bits64(pb, n, (uint64_t)(value) & (~(UINT64_MAX << n)));
>>> }
>>> +static inline void put_leb(PutBitContext *s, unsigned value)
>>> +{
>>> + int len;
>>> + uint8_t byte;
>>> +
>>> + len = (av_log2(value) + 7) / 7;
>>> +
>>> + for (int i = 0; i < len; i++) {
>>> + byte = value >> (7 * i) & 0x7f;
>>> + if (i < len - 1)
>>> + byte |= 0x80;
>>> +
>>> + put_bits_no_assert(s, 8, byte);
>>> + }
>>> +}
>>> +
>>> /**
>>> * Return the pointer to the byte where the bitstream writer will put
>>> * the next bit.
>>
>> This function does use any internals of PutBitContext and should
>> therefore be in user-code. In fact, get_leb() should not exist in
>> get_bits.h.
>
> You could say the same about skip_1stop_8data_bits(), decode210(),
> decode012() and get_vlc_multi().
Old errors do not justify new errors.
(skip_1stop_8data_bits() is mostly mpegvideodec and if it were not for
SVQ3 it would have been moved there already.)
>
> And the idea is to have these be reusable. leb is evidently going to be
> used by anything AOMedia will make.
Then it should be in some AOMedia header. If it should exist at all:
Byte-aligned lebs should not use a GetBitContext at all. And all current
users are byte-aligned.
- Andreas
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf
2024-01-30 22:07 ` James Almer
@ 2024-01-30 22:11 ` Andreas Rheinhardt
2024-01-30 22:12 ` James Almer
0 siblings, 1 reply; 19+ messages in thread
From: Andreas Rheinhardt @ 2024-01-30 22:11 UTC (permalink / raw)
To: ffmpeg-devel
James Almer:
> On 1/30/2024 6:47 PM, Andreas Rheinhardt wrote:
>>> + *obu_size = get_leb(&gb);
>> This stuff here should not a GetBitContext at all, as basically
>> everything is byte-aligned (and the flags above are in known bits).
>
> I'm not going to write yet another leb() reading function to work on raw
> bytes. We have enough scattered around and in fact we should try to
> remove most.
>
>>> +static const enum AVCodecID iamf_stream_split_codec_ids[] = {
>>> + AV_CODEC_ID_PCM_S16LE, AV_CODEC_ID_PCM_S16BE,
>>> + AV_CODEC_ID_PCM_S24LE, AV_CODEC_ID_PCM_S24BE,
>>> + AV_CODEC_ID_PCM_S32LE, AV_CODEC_ID_PCM_S32BE,
>>> + AV_CODEC_ID_OPUS, AV_CODEC_ID_AAC,
>>> + AV_CODEC_ID_FLAC, AV_CODEC_ID_NONE,
>>> +};
>>> +
>>> +const FFBitStreamFilter ff_iamf_stream_split_bsf = {
>>> + .p.name = "iamf_stream_split",
>>> + .p.codec_ids = iamf_stream_split_codec_ids,
>>> + .p.priv_class = &iamf_stream_split_class,
>>> + .priv_data_size = sizeof(IAMFSplitContext),
>>> + .init = iamf_stream_split_init,
>>> + .flush = iamf_stream_split_flush,
>>> + .close = iamf_stream_split_close,
>>> + .filter = iamf_stream_split_filter,
>>> +};
>>
>> This needs to add documentation for what this BSF is actually supposed
>> to do. Right now it seems crazy: It parses the packet's data and expects
>> to find OBU headers, although the input data is supposed to be PCM,
>> Opus, AAC or Flac.
>
> It's not too different than aac_adtstoasc in that it takes audio from
> those codecs listed above encapsulated in one form and returns it in
> another form.
> In this case, it takes OBUs containing one or more audio frames, removes
> the OBU encapsulation, and propagates each raw audio frame in separate
> packets.
>
Then these packets do not really contain PCM, Opus or Flac at all.
- Andreas
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf
2024-01-30 22:11 ` Andreas Rheinhardt
@ 2024-01-30 22:12 ` James Almer
2024-02-03 15:04 ` Andreas Rheinhardt
0 siblings, 1 reply; 19+ messages in thread
From: James Almer @ 2024-01-30 22:12 UTC (permalink / raw)
To: ffmpeg-devel
On 1/30/2024 7:11 PM, Andreas Rheinhardt wrote:
> James Almer:
>> On 1/30/2024 6:47 PM, Andreas Rheinhardt wrote:
>>>> + *obu_size = get_leb(&gb);
>>> This stuff here should not a GetBitContext at all, as basically
>>> everything is byte-aligned (and the flags above are in known bits).
>>
>> I'm not going to write yet another leb() reading function to work on raw
>> bytes. We have enough scattered around and in fact we should try to
>> remove most.
>>
>>>> +static const enum AVCodecID iamf_stream_split_codec_ids[] = {
>>>> + AV_CODEC_ID_PCM_S16LE, AV_CODEC_ID_PCM_S16BE,
>>>> + AV_CODEC_ID_PCM_S24LE, AV_CODEC_ID_PCM_S24BE,
>>>> + AV_CODEC_ID_PCM_S32LE, AV_CODEC_ID_PCM_S32BE,
>>>> + AV_CODEC_ID_OPUS, AV_CODEC_ID_AAC,
>>>> + AV_CODEC_ID_FLAC, AV_CODEC_ID_NONE,
>>>> +};
>>>> +
>>>> +const FFBitStreamFilter ff_iamf_stream_split_bsf = {
>>>> + .p.name = "iamf_stream_split",
>>>> + .p.codec_ids = iamf_stream_split_codec_ids,
>>>> + .p.priv_class = &iamf_stream_split_class,
>>>> + .priv_data_size = sizeof(IAMFSplitContext),
>>>> + .init = iamf_stream_split_init,
>>>> + .flush = iamf_stream_split_flush,
>>>> + .close = iamf_stream_split_close,
>>>> + .filter = iamf_stream_split_filter,
>>>> +};
>>>
>>> This needs to add documentation for what this BSF is actually supposed
>>> to do. Right now it seems crazy: It parses the packet's data and expects
>>> to find OBU headers, although the input data is supposed to be PCM,
>>> Opus, AAC or Flac.
>>
>> It's not too different than aac_adtstoasc in that it takes audio from
>> those codecs listed above encapsulated in one form and returns it in
>> another form.
>> In this case, it takes OBUs containing one or more audio frames, removes
>> the OBU encapsulation, and propagates each raw audio frame in separate
>> packets.
>>
>
> Then these packets do not really contain PCM, Opus or Flac at all.
It does, encapsulated in OBU. Not really different than AAC in ADTS,
like i said.
I can remove the iamf_stream_split_codec_ids[] array in any case.
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [FFmpeg-devel] [PATCH 6/7] avcodec/put_bits: add put_leb()
2024-01-30 22:10 ` Andreas Rheinhardt
@ 2024-01-30 22:14 ` James Almer
0 siblings, 0 replies; 19+ messages in thread
From: James Almer @ 2024-01-30 22:14 UTC (permalink / raw)
To: ffmpeg-devel
On 1/30/2024 7:10 PM, Andreas Rheinhardt wrote:
> James Almer:
>> On 1/30/2024 6:53 PM, Andreas Rheinhardt wrote:
>>> James Almer:
>>>> Signed-off-by: James Almer <jamrial@gmail.com>
>>>> ---
>>>> libavcodec/put_bits.h | 16 ++++++++++++++++
>>>> 1 file changed, 16 insertions(+)
>>>>
>>>> diff --git a/libavcodec/put_bits.h b/libavcodec/put_bits.h
>>>> index 4561dc131a..04dedd3342 100644
>>>> --- a/libavcodec/put_bits.h
>>>> +++ b/libavcodec/put_bits.h
>>>> @@ -370,6 +370,22 @@ static inline void put_sbits63(PutBitContext
>>>> *pb, int n, int64_t value)
>>>> put_bits64(pb, n, (uint64_t)(value) & (~(UINT64_MAX << n)));
>>>> }
>>>> +static inline void put_leb(PutBitContext *s, unsigned value)
>>>> +{
>>>> + int len;
>>>> + uint8_t byte;
>>>> +
>>>> + len = (av_log2(value) + 7) / 7;
>>>> +
>>>> + for (int i = 0; i < len; i++) {
>>>> + byte = value >> (7 * i) & 0x7f;
>>>> + if (i < len - 1)
>>>> + byte |= 0x80;
>>>> +
>>>> + put_bits_no_assert(s, 8, byte);
>>>> + }
>>>> +}
>>>> +
>>>> /**
>>>> * Return the pointer to the byte where the bitstream writer will put
>>>> * the next bit.
>>>
>>> This function does use any internals of PutBitContext and should
>>> therefore be in user-code. In fact, get_leb() should not exist in
>>> get_bits.h.
>>
>> You could say the same about skip_1stop_8data_bits(), decode210(),
>> decode012() and get_vlc_multi().
>
> Old errors do not justify new errors.
> (skip_1stop_8data_bits() is mostly mpegvideodec and if it were not for
> SVQ3 it would have been moved there already.)
>
>>
>> And the idea is to have these be reusable. leb is evidently going to be
>> used by anything AOMedia will make.
>
> Then it should be in some AOMedia header. If it should exist at all:
> Byte-aligned lebs should not use a GetBitContext at all. And all current
> users are byte-aligned.
All lebs are byte aligned. And of course we need a GetBitContext
implementation when we need to read elements as small as 1 bit followed
by a leb followed by some other form of vlc when parsing a single header.
And ok, I can move these to an AOM specific header if you prefer.
>
> - Andreas
>
> _______________________________________________
> 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".
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [FFmpeg-devel] [PATCH 3/7 v2] avformat/mov: make MOVStreamContext refcounted
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 3/7 v2] avformat/mov: make MOVStreamContext refcounted James Almer
@ 2024-01-30 22:31 ` Andreas Rheinhardt
2024-01-31 1:58 ` Michael Niedermayer
1 sibling, 0 replies; 19+ messages in thread
From: Andreas Rheinhardt @ 2024-01-30 22:31 UTC (permalink / raw)
To: ffmpeg-devel
James Almer:
> This will be needed by the following commit.
>
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> libavformat/mov.c | 257 ++++++++++++++++++++++++++--------------------
> 1 file changed, 145 insertions(+), 112 deletions(-)
>
> diff --git a/libavformat/mov.c b/libavformat/mov.c
> index 3e71252e46..a0dfa22598 100644
> --- a/libavformat/mov.c
> +++ b/libavformat/mov.c
> @@ -31,6 +31,7 @@
>
> #include "libavutil/attributes.h"
> #include "libavutil/bprint.h"
> +#include "libavutil/buffer.h"
> #include "libavutil/channel_layout.h"
> #include "libavutil/dict_internal.h"
> #include "libavutil/internal.h"
> @@ -184,10 +185,20 @@ static int mov_read_mac_string(MOVContext *c, AVIOContext *pb, int len,
> return p - dst;
> }
>
> +static void mov_free_stream_context(void *opaque, uint8_t *data);
> +
> +static inline MOVStreamContext *mov_get_stream_context(const AVStream *st)
> +{
> + AVBufferRef *buf = st->priv_data;
> +
> + return (MOVStreamContext *)buf->data;
> +}
> +
> static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len)
> {
> AVStream *st;
> - MOVStreamContext *sc;
> + AVBufferRef *buf;
> + uint8_t *data;
> enum AVCodecID id;
> int ret;
>
> @@ -201,16 +212,22 @@ static int mov_read_covr(MOVContext *c, AVIOContext *pb, int type, int len)
> return 0;
> }
>
> - sc = av_mallocz(sizeof(*sc));
> - if (!sc)
> + data = av_mallocz(sizeof(MOVStreamContext));
> + if (!data)
> + return AVERROR(ENOMEM);
> + buf = av_buffer_create(data, sizeof(MOVStreamContext), mov_free_stream_context, c->fc, 0);
> + if (!buf) {
> + av_free(data);
> return AVERROR(ENOMEM);
> + }
> +
> ret = ff_add_attached_pic(c->fc, NULL, pb, NULL, len);
> if (ret < 0) {
> - av_free(sc);
> + av_buffer_unref(&buf);
> return ret;
> }
> st = c->fc->streams[c->fc->nb_streams - 1];
> - st->priv_data = sc;
> + st->priv_data = buf;
>
> if (st->attached_pic.size >= 8 && id != AV_CODEC_ID_BMP) {
> if (AV_RB64(st->attached_pic.data) == 0x89504e470d0a1a0a) {
> @@ -590,7 +607,7 @@ static int mov_read_dref(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> avio_rb32(pb); // version + flags
> entries = avio_rb32(pb);
> @@ -1372,7 +1389,7 @@ static int64_t get_frag_time(AVFormatContext *s, AVStream *dst_st,
> MOVFragmentIndex *frag_index, int index)
> {
> MOVFragmentStreamInfo * frag_stream_info;
> - MOVStreamContext *sc = dst_st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(dst_st);
> int64_t timestamp;
> int i, j;
>
> @@ -1571,7 +1588,7 @@ static int mov_read_mdhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> if (sc->time_scale) {
> av_log(c->fc, AV_LOG_ERROR, "Multiple mdhd?\n");
> @@ -1714,7 +1731,7 @@ static int mov_read_pcmc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> return AVERROR_INVALIDDATA;
>
> st = fc->streams[fc->nb_streams - 1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> if (sc->format == MOV_MP4_FPCM_TAG) {
> switch (pcm_sample_size) {
> @@ -2217,7 +2234,7 @@ static int mov_read_stco(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> avio_r8(pb); /* version */
> avio_rb24(pb); /* flags */
> @@ -2555,7 +2572,7 @@ static int mov_parse_stsd_data(MOVContext *c, AVIOContext *pb,
> if (ret < 0)
> return ret;
> if (size > 16) {
> - MOVStreamContext *tmcd_ctx = st->priv_data;
> + MOVStreamContext *tmcd_ctx = mov_get_stream_context(st);
> int val;
> val = AV_RB32(st->codecpar->extradata + 4);
> tmcd_ctx->tmcd_flags = val;
> @@ -2721,7 +2738,7 @@ int ff_mov_read_stsd_entries(MOVContext *c, AVIOContext *pb, int entries)
>
> av_assert0 (c->fc->nb_streams >= 1);
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> for (pseudo_stream_id = 0;
> pseudo_stream_id < entries && !pb->eof_reached;
> @@ -2819,7 +2836,7 @@ static int mov_read_stsd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams - 1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> sc->stsd_version = avio_r8(pb);
> avio_rb24(pb); /* flags */
> @@ -2884,7 +2901,7 @@ static int mov_read_stsc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> avio_r8(pb); /* version */
> avio_rb24(pb); /* flags */
> @@ -2980,7 +2997,7 @@ static int mov_read_stps(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> avio_rb32(pb); // version + flags
>
> @@ -3018,7 +3035,7 @@ static int mov_read_stss(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> sti = ffstream(st);
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> avio_r8(pb); /* version */
> avio_rb24(pb); /* flags */
> @@ -3069,7 +3086,7 @@ static int mov_read_stsz(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> avio_r8(pb); /* version */
> avio_rb24(pb); /* flags */
> @@ -3158,7 +3175,7 @@ static int mov_read_stts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> avio_r8(pb); /* version */
> avio_rb24(pb); /* flags */
> @@ -3269,7 +3286,7 @@ static int mov_read_sdtp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams - 1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> avio_r8(pb); /* version */
> avio_rb24(pb); /* flags */
> @@ -3314,7 +3331,7 @@ static int mov_read_ctts(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> avio_r8(pb); /* version */
> avio_rb24(pb); /* flags */
> @@ -3384,7 +3401,7 @@ static int mov_read_sgpd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams - 1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> version = avio_r8(pb); /* version */
> avio_rb24(pb); /* flags */
> @@ -3440,7 +3457,7 @@ static int mov_read_sbgp(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> version = avio_r8(pb); /* version */
> avio_rb24(pb); /* flags */
> @@ -3541,7 +3558,7 @@ static int find_prev_closest_index(AVStream *st,
> int64_t* ctts_index,
> int64_t* ctts_sample)
> {
> - MOVStreamContext *msc = st->priv_data;
> + MOVStreamContext *msc = mov_get_stream_context(st);
> FFStream *const sti = ffstream(st);
> AVIndexEntry *e_keep = sti->index_entries;
> int nb_keep = sti->nb_index_entries;
> @@ -3714,7 +3731,7 @@ static int64_t add_ctts_entry(MOVCtts** ctts_data, unsigned int* ctts_count, uns
> #define MAX_REORDER_DELAY 16
> static void mov_estimate_video_delay(MOVContext *c, AVStream* st)
> {
> - MOVStreamContext *msc = st->priv_data;
> + MOVStreamContext *msc = mov_get_stream_context(st);
> FFStream *const sti = ffstream(st);
> int ctts_ind = 0;
> int ctts_sample = 0;
> @@ -3822,7 +3839,7 @@ static void mov_current_sample_set(MOVStreamContext *sc, int current_sample)
> */
> static void mov_fix_index(MOVContext *mov, AVStream *st)
> {
> - MOVStreamContext *msc = st->priv_data;
> + MOVStreamContext *msc = mov_get_stream_context(st);
> FFStream *const sti = ffstream(st);
> AVIndexEntry *e_old = sti->index_entries;
> int nb_old = sti->nb_index_entries;
> @@ -4136,7 +4153,7 @@ static int build_open_gop_key_points(AVStream *st)
> int k;
> int sample_id = 0;
> uint32_t cra_index;
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
>
> if (st->codecpar->codec_id != AV_CODEC_ID_HEVC || !sc->sync_group_count)
> return 0;
> @@ -4196,7 +4213,7 @@ static int build_open_gop_key_points(AVStream *st)
>
> static void mov_build_index(MOVContext *mov, AVStream *st)
> {
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
> FFStream *const sti = ffstream(st);
> int64_t current_offset;
> int64_t current_dts = 0;
> @@ -4636,16 +4653,24 @@ static void fix_timescale(MOVContext *c, MOVStreamContext *sc)
> static int mov_read_trak(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> {
> AVStream *st;
> + AVBufferRef *buf;
> MOVStreamContext *sc;
> + uint8_t *data;
> int ret;
>
> st = avformat_new_stream(c->fc, NULL);
> if (!st) return AVERROR(ENOMEM);
> st->id = -1;
> - sc = av_mallocz(sizeof(MOVStreamContext));
> - if (!sc) return AVERROR(ENOMEM);
> + data = av_mallocz(sizeof(MOVStreamContext));
> + if (!data) return AVERROR(ENOMEM);
> + buf = av_buffer_create(data, sizeof(MOVStreamContext), mov_free_stream_context, c->fc, 0);
> + if (!buf) {
> + av_free(data);
> + return AVERROR(ENOMEM);
> + }
>
> - st->priv_data = sc;
> + st->priv_data = buf;
> + sc = mov_get_stream_context(st);
> st->codecpar->codec_type = AVMEDIA_TYPE_DATA;
> sc->ffindex = st->index;
> c->trak_index = st->index;
> @@ -4841,7 +4866,7 @@ static int mov_read_custom(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> for (i = 0; i < 3; i++) {
> uint8_t **p;
> @@ -4941,7 +4966,7 @@ static int heif_add_stream(MOVContext *c, HEIFItem *item)
> st->time_base.num = st->time_base.den = 1;
> st->nb_frames = 1;
> sc->time_scale = 1;
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
> sc->pb = c->fc->pb;
> sc->pb_is_copied = 1;
>
> @@ -5013,7 +5038,7 @@ static int mov_read_tkhd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> // Each stream (trak) should have exactly 1 tkhd. This catches bad files and
> // avoids corrupting AVStreams mapped to an earlier tkhd.
> @@ -5215,7 +5240,7 @@ static int mov_read_tfdt(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> av_log(c->fc, AV_LOG_WARNING, "could not find corresponding track id %u\n", frag->track_id);
> return 0;
> }
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
> if (sc->pseudo_stream_id + 1 != frag->stsd_id && sc->pseudo_stream_id != -1)
> return 0;
> version = avio_r8(pb);
> @@ -5269,7 +5294,7 @@ static int mov_read_trun(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> av_log(c->fc, AV_LOG_WARNING, "could not find corresponding track id %u\n", frag->track_id);
> return 0;
> }
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
> if (sc->pseudo_stream_id+1 != frag->stsd_id && sc->pseudo_stream_id != -1)
> return 0;
>
> @@ -5572,7 +5597,7 @@ static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> return 0;
> }
>
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> timescale = av_make_q(1, avio_rb32(pb));
>
> @@ -5655,14 +5680,14 @@ static int mov_read_sidx(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> si = &item->stream_info[j];
> if (si->sidx_pts != AV_NOPTS_VALUE) {
> ref_st = c->fc->streams[j];
> - ref_sc = ref_st->priv_data;
> + ref_sc = mov_get_stream_context(ref_st);
> break;
> }
> }
> }
> if (ref_st) for (i = 0; i < c->fc->nb_streams; i++) {
> st = c->fc->streams[i];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
> if (!sc->has_sidx) {
> st->duration = sc->track_end = av_rescale(ref_st->duration, sc->time_scale, ref_sc->time_scale);
> }
> @@ -5758,7 +5783,7 @@ static int mov_read_elst(MOVContext *c, AVIOContext *pb, MOVAtom atom)
>
> if (c->fc->nb_streams < 1 || c->ignore_editlist)
> return 0;
> - sc = c->fc->streams[c->fc->nb_streams-1]->priv_data;
> + sc = mov_get_stream_context(c->fc->streams[c->fc->nb_streams-1]);
>
> version = avio_r8(pb); /* version */
> avio_rb24(pb); /* flags */
> @@ -5825,7 +5850,7 @@ static int mov_read_tmcd(MOVContext *c, AVIOContext *pb, MOVAtom atom)
>
> if (c->fc->nb_streams < 1)
> return AVERROR_INVALIDDATA;
> - sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data;
> + sc = mov_get_stream_context(c->fc->streams[c->fc->nb_streams - 1]);
> sc->timecode_track = avio_rb32(pb);
> return 0;
> }
> @@ -5882,7 +5907,7 @@ static int mov_read_smdm(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return AVERROR_INVALIDDATA;
>
> - sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data;
> + sc = mov_get_stream_context(c->fc->streams[c->fc->nb_streams - 1]);
>
> if (atom.size < 5) {
> av_log(c->fc, AV_LOG_ERROR, "Empty Mastering Display Metadata box\n");
> @@ -5930,7 +5955,7 @@ static int mov_read_mdcv(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return AVERROR_INVALIDDATA;
>
> - sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data;
> + sc = mov_get_stream_context(c->fc->streams[c->fc->nb_streams - 1]);
>
> if (atom.size < 24 || sc->mastering) {
> av_log(c->fc, AV_LOG_ERROR, "Invalid Mastering Display Color Volume box\n");
> @@ -5966,7 +5991,7 @@ static int mov_read_coll(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return AVERROR_INVALIDDATA;
>
> - sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data;
> + sc = mov_get_stream_context(c->fc->streams[c->fc->nb_streams - 1]);
>
> if (atom.size < 5) {
> av_log(c->fc, AV_LOG_ERROR, "Empty Content Light Level box\n");
> @@ -6002,7 +6027,7 @@ static int mov_read_clli(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return AVERROR_INVALIDDATA;
>
> - sc = c->fc->streams[c->fc->nb_streams - 1]->priv_data;
> + sc = mov_get_stream_context(c->fc->streams[c->fc->nb_streams - 1]);
>
> if (atom.size < 4) {
> av_log(c->fc, AV_LOG_ERROR, "Empty Content Light Level Info box\n");
> @@ -6035,7 +6060,7 @@ static int mov_read_st3d(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> return 0;
>
> st = c->fc->streams[c->fc->nb_streams - 1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> if (atom.size < 5) {
> av_log(c->fc, AV_LOG_ERROR, "Empty stereoscopic video box\n");
> @@ -6085,7 +6110,7 @@ static int mov_read_sv3d(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> return 0;
>
> st = c->fc->streams[c->fc->nb_streams - 1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> if (atom.size < 8) {
> av_log(c->fc, AV_LOG_ERROR, "Empty spherical video box\n");
> @@ -6296,7 +6321,7 @@ static int mov_read_uuid(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams - 1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> ret = ffio_read_size(pb, uuid, AV_UUID_LEN);
> if (ret < 0)
> @@ -6408,7 +6433,7 @@ static int mov_read_frma(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams - 1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> switch (sc->format)
> {
> @@ -6461,7 +6486,7 @@ static int get_current_encryption_info(MOVContext *c, MOVEncryptionIndex **encry
> }
> if (i == c->fc->nb_streams)
> return 0;
> - *sc = st->priv_data;
> + *sc = mov_get_stream_context(st);
>
> if (!frag_stream_info->encryption_index) {
> // If this stream isn't encrypted, don't create the index.
> @@ -6479,7 +6504,7 @@ static int get_current_encryption_info(MOVContext *c, MOVEncryptionIndex **encry
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams - 1];
> - *sc = st->priv_data;
> + *sc = mov_get_stream_context(st);
>
> if (!(*sc)->cenc.encryption_index) {
> // If this stream isn't encrypted, don't create the index.
> @@ -6972,7 +6997,7 @@ static int mov_read_schm(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> if (sc->pseudo_stream_id != 0) {
> av_log(c->fc, AV_LOG_ERROR, "schm boxes are only supported in first sample descriptor\n");
> @@ -7004,7 +7029,7 @@ static int mov_read_tenc(MOVContext *c, AVIOContext *pb, MOVAtom atom)
> if (c->fc->nb_streams < 1)
> return 0;
> st = c->fc->streams[c->fc->nb_streams-1];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
>
> if (sc->pseudo_stream_id != 0) {
> av_log(c->fc, AV_LOG_ERROR, "tenc atom are only supported in first sample descriptor\n");
> @@ -8416,7 +8441,7 @@ static void mov_read_chapters(AVFormatContext *s)
> }
> sti = ffstream(st);
>
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
> cur_pos = avio_tell(sc->pb);
>
> if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
> @@ -8506,7 +8531,7 @@ static int parse_timecode_in_framenum_format(AVFormatContext *s, AVStream *st,
>
> static int mov_read_rtmd_track(AVFormatContext *s, AVStream *st)
> {
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
> FFStream *const sti = ffstream(st);
> char buf[AV_TIMECODE_STR_SIZE];
> int64_t cur_pos = avio_tell(sc->pb);
> @@ -8532,7 +8557,7 @@ static int mov_read_rtmd_track(AVFormatContext *s, AVStream *st)
>
> static int mov_read_timecode_track(AVFormatContext *s, AVStream *st)
> {
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
> FFStream *const sti = ffstream(st);
> int flags = 0;
> int64_t cur_pos = avio_tell(sc->pb);
> @@ -8589,6 +8614,56 @@ static void mov_free_encryption_index(MOVEncryptionIndex **index) {
> av_freep(index);
> }
>
> +static void mov_free_stream_context(void *opaque, uint8_t *data)
> +{
> + AVFormatContext *s = opaque;
> + MOVStreamContext *sc = (MOVStreamContext *)data;
> +
> + av_freep(&sc->ctts_data);
> + for (int i = 0; i < sc->drefs_count; i++) {
> + av_freep(&sc->drefs[i].path);
> + av_freep(&sc->drefs[i].dir);
> + }
> + av_freep(&sc->drefs);
> +
> + sc->drefs_count = 0;
> +
> + if (!sc->pb_is_copied)
> + ff_format_io_close(s, &sc->pb);
> +
> + sc->pb = NULL;
> + av_freep(&sc->chunk_offsets);
> + av_freep(&sc->stsc_data);
> + av_freep(&sc->sample_sizes);
> + av_freep(&sc->keyframes);
> + av_freep(&sc->stts_data);
> + av_freep(&sc->sdtp_data);
> + av_freep(&sc->stps_data);
> + av_freep(&sc->elst_data);
> + av_freep(&sc->rap_group);
> + av_freep(&sc->sync_group);
> + av_freep(&sc->sgpd_sync);
> + av_freep(&sc->sample_offsets);
> + av_freep(&sc->open_key_samples);
> + av_freep(&sc->display_matrix);
> + av_freep(&sc->index_ranges);
> +
> + if (sc->extradata)
> + for (int i = 0; i < sc->stsd_count; i++)
> + av_free(sc->extradata[i]);
> + av_freep(&sc->extradata);
> + av_freep(&sc->extradata_size);
> +
> + mov_free_encryption_index(&sc->cenc.encryption_index);
> + av_encryption_info_free(sc->cenc.default_encrypted_sample);
> + av_aes_ctr_free(sc->cenc.aes_ctr);
> +
> + av_freep(&sc->stereo3d);
> + av_freep(&sc->spherical);
> + av_freep(&sc->mastering);
> + av_freep(&sc->coll);
> +}
> +
> static int mov_read_close(AVFormatContext *s)
> {
> MOVContext *mov = s->priv_data;
> @@ -8596,54 +8671,12 @@ static int mov_read_close(AVFormatContext *s)
>
> for (i = 0; i < s->nb_streams; i++) {
> AVStream *st = s->streams[i];
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
>
> if (!sc)
> continue;
>
> - av_freep(&sc->ctts_data);
> - for (j = 0; j < sc->drefs_count; j++) {
> - av_freep(&sc->drefs[j].path);
> - av_freep(&sc->drefs[j].dir);
> - }
> - av_freep(&sc->drefs);
> -
> - sc->drefs_count = 0;
> -
> - if (!sc->pb_is_copied)
> - ff_format_io_close(s, &sc->pb);
> -
> - sc->pb = NULL;
> - av_freep(&sc->chunk_offsets);
> - av_freep(&sc->stsc_data);
> - av_freep(&sc->sample_sizes);
> - av_freep(&sc->keyframes);
> - av_freep(&sc->stts_data);
> - av_freep(&sc->sdtp_data);
> - av_freep(&sc->stps_data);
> - av_freep(&sc->elst_data);
> - av_freep(&sc->rap_group);
> - av_freep(&sc->sync_group);
> - av_freep(&sc->sgpd_sync);
> - av_freep(&sc->sample_offsets);
> - av_freep(&sc->open_key_samples);
> - av_freep(&sc->display_matrix);
> - av_freep(&sc->index_ranges);
> -
> - if (sc->extradata)
> - for (j = 0; j < sc->stsd_count; j++)
> - av_free(sc->extradata[j]);
> - av_freep(&sc->extradata);
> - av_freep(&sc->extradata_size);
> -
> - mov_free_encryption_index(&sc->cenc.encryption_index);
> - av_encryption_info_free(sc->cenc.default_encrypted_sample);
> - av_aes_ctr_free(sc->cenc.aes_ctr);
> -
> - av_freep(&sc->stereo3d);
> - av_freep(&sc->spherical);
> - av_freep(&sc->mastering);
> - av_freep(&sc->coll);
> + av_buffer_unref((AVBufferRef **)&st->priv_data);
> }
>
> av_freep(&mov->dv_demux);
> @@ -8682,7 +8715,7 @@ static int tmcd_is_referenced(AVFormatContext *s, int tmcd_id)
>
> for (i = 0; i < s->nb_streams; i++) {
> AVStream *st = s->streams[i];
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
>
> if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO &&
> sc->timecode_track == tmcd_id)
> @@ -8882,7 +8915,7 @@ static int mov_read_header(AVFormatContext *s)
> /* copy timecode metadata from tmcd tracks to the related video streams */
> for (i = 0; i < s->nb_streams; i++) {
> AVStream *st = s->streams[i];
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
> if (sc->timecode_track > 0) {
> AVDictionaryEntry *tcr;
> int tmcd_st_id = -1;
> @@ -8903,7 +8936,7 @@ static int mov_read_header(AVFormatContext *s)
> for (i = 0; i < s->nb_streams; i++) {
> AVStream *st = s->streams[i];
> FFStream *const sti = ffstream(st);
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
> fix_timescale(mov, sc);
> if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO &&
> st->codecpar->codec_id == AV_CODEC_ID_AAC) {
> @@ -8933,7 +8966,7 @@ static int mov_read_header(AVFormatContext *s)
> if (mov->trex_data) {
> for (i = 0; i < s->nb_streams; i++) {
> AVStream *st = s->streams[i];
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
> if (st->duration > 0) {
> /* Akin to sc->data_size * 8 * sc->time_scale / st->duration but accounting for overflows. */
> st->codecpar->bit_rate = av_rescale(sc->data_size, ((int64_t) sc->time_scale) * 8, st->duration);
> @@ -8951,7 +8984,7 @@ static int mov_read_header(AVFormatContext *s)
> if (mov->use_mfra_for > 0) {
> for (i = 0; i < s->nb_streams; i++) {
> AVStream *st = s->streams[i];
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
> if (sc->duration_for_fps > 0) {
> /* Akin to sc->data_size * 8 * sc->time_scale / sc->duration_for_fps but accounting for overflows. */
> st->codecpar->bit_rate = av_rescale(sc->data_size, ((int64_t) sc->time_scale) * 8, sc->duration_for_fps);
> @@ -8976,7 +9009,7 @@ static int mov_read_header(AVFormatContext *s)
>
> for (i = 0; i < s->nb_streams; i++) {
> AVStream *st = s->streams[i];
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
>
> switch (st->codecpar->codec_type) {
> case AVMEDIA_TYPE_AUDIO:
> @@ -9047,7 +9080,7 @@ static AVIndexEntry *mov_find_next_sample(AVFormatContext *s, AVStream **st)
> for (i = 0; i < s->nb_streams; i++) {
> AVStream *avst = s->streams[i];
> FFStream *const avsti = ffstream(avst);
> - MOVStreamContext *msc = avst->priv_data;
> + MOVStreamContext *msc = mov_get_stream_context(avst);
> if (msc->pb && msc->current_sample < avsti->nb_index_entries) {
> AVIndexEntry *current_sample = &avsti->index_entries[msc->current_sample];
> int64_t dts = av_rescale(current_sample->timestamp, AV_TIME_BASE, msc->time_scale);
> @@ -9172,7 +9205,7 @@ static int mov_read_packet(AVFormatContext *s, AVPacket *pkt)
> return ret;
> goto retry;
> }
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
> /* must be done just before reading, to avoid infinite loop on sample */
> current_index = sc->current_index;
> mov_current_sample_inc(sc);
> @@ -9338,7 +9371,7 @@ static int is_open_key_sample(const MOVStreamContext *sc, int sample)
> */
> static int can_seek_to_key_sample(AVStream *st, int sample, int64_t requested_pts)
> {
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
> FFStream *const sti = ffstream(st);
> int64_t key_sample_dts, key_sample_pts;
>
> @@ -9364,7 +9397,7 @@ static int can_seek_to_key_sample(AVStream *st, int sample, int64_t requested_pt
>
> static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp, int flags)
> {
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
> FFStream *const sti = ffstream(st);
> int sample, time_sample, ret;
> unsigned int i;
> @@ -9426,7 +9459,7 @@ static int mov_seek_stream(AVFormatContext *s, AVStream *st, int64_t timestamp,
>
> static int64_t mov_get_skip_samples(AVStream *st, int sample)
> {
> - MOVStreamContext *sc = st->priv_data;
> + MOVStreamContext *sc = mov_get_stream_context(st);
> FFStream *const sti = ffstream(st);
> int64_t first_ts = sti->index_entries[0].timestamp;
> int64_t ts = sti->index_entries[sample].timestamp;
> @@ -9480,7 +9513,7 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
> for (i = 0; i < s->nb_streams; i++) {
> MOVStreamContext *sc;
> st = s->streams[i];
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
> mov_current_sample_set(sc, 0);
> }
> while (1) {
> @@ -9488,7 +9521,7 @@ static int mov_read_seek(AVFormatContext *s, int stream_index, int64_t sample_ti
> AVIndexEntry *entry = mov_find_next_sample(s, &st);
> if (!entry)
> return AVERROR_INVALIDDATA;
> - sc = st->priv_data;
> + sc = mov_get_stream_context(st);
> if (sc->ffindex == stream_index && sc->current_sample == sample)
> break;
> mov_current_sample_inc(sc);
This is using the AVBuffer API for stuff for which lots of simpler ways
of achieving the same aim are possible: Simply add a refcount field to
MOVStreamContext and add a ref function and an unref function that
decrements the refcount, frees the MOVStreamContext if necessary and
(always) resets st->priv_data.
- Andreas
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [FFmpeg-devel] [PATCH 3/7 v2] avformat/mov: make MOVStreamContext refcounted
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 3/7 v2] avformat/mov: make MOVStreamContext refcounted James Almer
2024-01-30 22:31 ` Andreas Rheinhardt
@ 2024-01-31 1:58 ` Michael Niedermayer
2024-01-31 2:01 ` James Almer
1 sibling, 1 reply; 19+ messages in thread
From: Michael Niedermayer @ 2024-01-31 1:58 UTC (permalink / raw)
To: FFmpeg development discussions and patches
[-- Attachment #1.1: Type: text/plain, Size: 1630 bytes --]
On Tue, Jan 30, 2024 at 02:32:14PM -0300, James Almer wrote:
> This will be needed by the following commit.
>
> Signed-off-by: James Almer <jamrial@gmail.com>
> ---
> libavformat/mov.c | 257 ++++++++++++++++++++++++++--------------------
> 1 file changed, 145 insertions(+), 112 deletions(-)
The previous commits dont seem to build alone
libavformat/mov.c: In function ‘heif_add_stream’:
libavformat/mov.c:4931:60: error: ‘mov_free_stream_context’ undeclared (first use in this function); did you mean ‘ff_find_stream_index’?
buf = av_buffer_create(data, sizeof(MOVStreamContext), mov_free_stream_context, c->fc, 0);
^~~~~~~~~~~~~~~~~~~~~~~
ff_find_stream_index
libavformat/mov.c:4931:60: note: each undeclared identifier is reported only once for each function it appears in
libavformat/mov.c:4942:10: error: implicit declaration of function ‘mov_get_stream_context’; did you mean ‘ffio_init_read_context’? [-Werror=implicit-function-declaration]
sc = mov_get_stream_context(st);
And with this commit also not:
libavcodec/libavcodec.a(bitstream_filters.o):(.data.rel.ro+0x90): undefined reference to `ff_iamf_stream_split_bsf'
collect2: error: ld returned 1 exit status
Makefile:133: recipe for target 'ffmpeg_g' failed
make: *** [ffmpeg_g] Error 1
the next builds
[...]
--
Michael GnuPG fingerprint: 9FF2128B147EF6730BADF133611EC787040B0FAB
The worst form of inequality is to try to make unequal things equal.
-- Aristotle
[-- Attachment #1.2: signature.asc --]
[-- Type: application/pgp-signature, Size: 195 bytes --]
[-- Attachment #2: Type: text/plain, Size: 251 bytes --]
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [FFmpeg-devel] [PATCH 3/7 v2] avformat/mov: make MOVStreamContext refcounted
2024-01-31 1:58 ` Michael Niedermayer
@ 2024-01-31 2:01 ` James Almer
0 siblings, 0 replies; 19+ messages in thread
From: James Almer @ 2024-01-31 2:01 UTC (permalink / raw)
To: ffmpeg-devel
On 1/30/2024 10:58 PM, Michael Niedermayer wrote:
> On Tue, Jan 30, 2024 at 02:32:14PM -0300, James Almer wrote:
>> This will be needed by the following commit.
>>
>> Signed-off-by: James Almer <jamrial@gmail.com>
>> ---
>> libavformat/mov.c | 257 ++++++++++++++++++++++++++--------------------
>> 1 file changed, 145 insertions(+), 112 deletions(-)
>
> The previous commits dont seem to build alone
>
> libavformat/mov.c: In function ‘heif_add_stream’:
> libavformat/mov.c:4931:60: error: ‘mov_free_stream_context’ undeclared (first use in this function); did you mean ‘ff_find_stream_index’?
> buf = av_buffer_create(data, sizeof(MOVStreamContext), mov_free_stream_context, c->fc, 0);
> ^~~~~~~~~~~~~~~~~~~~~~~
> ff_find_stream_index
> libavformat/mov.c:4931:60: note: each undeclared identifier is reported only once for each function it appears in
> libavformat/mov.c:4942:10: error: implicit declaration of function ‘mov_get_stream_context’; did you mean ‘ffio_init_read_context’? [-Werror=implicit-function-declaration]
> sc = mov_get_stream_context(st);
>
> And with this commit also not:
>
> libavcodec/libavcodec.a(bitstream_filters.o):(.data.rel.ro+0x90): undefined reference to `ff_iamf_stream_split_bsf'
> collect2: error: ld returned 1 exit status
> Makefile:133: recipe for target 'ffmpeg_g' failed
> make: *** [ffmpeg_g] Error 1
>
> the next builds
Yes, i fucked up a rebase and some code meant for this patch ended up in
2/7.
Will resend the set with some changes suggested by Andreas.
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
* Re: [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf
2024-01-30 22:12 ` James Almer
@ 2024-02-03 15:04 ` Andreas Rheinhardt
0 siblings, 0 replies; 19+ messages in thread
From: Andreas Rheinhardt @ 2024-02-03 15:04 UTC (permalink / raw)
To: ffmpeg-devel
James Almer:
> On 1/30/2024 7:11 PM, Andreas Rheinhardt wrote:
>> James Almer:
>>> On 1/30/2024 6:47 PM, Andreas Rheinhardt wrote:
>>>>> + *obu_size = get_leb(&gb);
>>>> This stuff here should not a GetBitContext at all, as basically
>>>> everything is byte-aligned (and the flags above are in known bits).
>>>
>>> I'm not going to write yet another leb() reading function to work on raw
>>> bytes. We have enough scattered around and in fact we should try to
>>> remove most.
>>>
>>>>> +static const enum AVCodecID iamf_stream_split_codec_ids[] = {
>>>>> + AV_CODEC_ID_PCM_S16LE, AV_CODEC_ID_PCM_S16BE,
>>>>> + AV_CODEC_ID_PCM_S24LE, AV_CODEC_ID_PCM_S24BE,
>>>>> + AV_CODEC_ID_PCM_S32LE, AV_CODEC_ID_PCM_S32BE,
>>>>> + AV_CODEC_ID_OPUS, AV_CODEC_ID_AAC,
>>>>> + AV_CODEC_ID_FLAC, AV_CODEC_ID_NONE,
>>>>> +};
>>>>> +
>>>>> +const FFBitStreamFilter ff_iamf_stream_split_bsf = {
>>>>> + .p.name = "iamf_stream_split",
>>>>> + .p.codec_ids = iamf_stream_split_codec_ids,
>>>>> + .p.priv_class = &iamf_stream_split_class,
>>>>> + .priv_data_size = sizeof(IAMFSplitContext),
>>>>> + .init = iamf_stream_split_init,
>>>>> + .flush = iamf_stream_split_flush,
>>>>> + .close = iamf_stream_split_close,
>>>>> + .filter = iamf_stream_split_filter,
>>>>> +};
>>>>
>>>> This needs to add documentation for what this BSF is actually supposed
>>>> to do. Right now it seems crazy: It parses the packet's data and
>>>> expects
>>>> to find OBU headers, although the input data is supposed to be PCM,
>>>> Opus, AAC or Flac.
>>>
>>> It's not too different than aac_adtstoasc in that it takes audio from
>>> those codecs listed above encapsulated in one form and returns it in
>>> another form.
>>> In this case, it takes OBUs containing one or more audio frames, removes
>>> the OBU encapsulation, and propagates each raw audio frame in separate
>>> packets.
>>>
>>
>> Then these packets do not really contain PCM, Opus or Flac at all.
>
> It does, encapsulated in OBU. Not really different than AAC in ADTS,
> like i said.
> I can remove the iamf_stream_split_codec_ids[] array in any case.
I disagree with this: While there are some codecs where several
different encapsulations are widely used and supported (in which case
one can actually find out information about the packetization by looking
at the extradata (H.26x) or the packets (AAC)), the aforementioned ones
are not among them. Your PCM-in-OBU is just not PCM.
- Andreas
_______________________________________________
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".
^ permalink raw reply [flat|nested] 19+ messages in thread
end of thread, other threads:[~2024-02-03 15:02 UTC | newest]
Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-01-30 17:32 [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf James Almer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 2/7 v2] avformat/demux: support inserting bitstream filters in demuxing scenarios James Almer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 3/7 v2] avformat/mov: make MOVStreamContext refcounted James Almer
2024-01-30 22:31 ` Andreas Rheinhardt
2024-01-31 1:58 ` Michael Niedermayer
2024-01-31 2:01 ` James Almer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 4/7 v2] avformat/mov: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 5/7] avcodec: add an Immersive Audio Model and Formats frame merge bsf James Almer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 6/7] avcodec/put_bits: add put_leb() James Almer
2024-01-30 21:53 ` Andreas Rheinhardt
2024-01-30 21:59 ` James Almer
2024-01-30 22:10 ` Andreas Rheinhardt
2024-01-30 22:14 ` James Almer
2024-01-30 17:32 ` [FFmpeg-devel] [PATCH 7/7] avformat/movenc: add support for Immersive Audio Model and Formats in ISOBMFF James Almer
2024-01-30 21:47 ` [FFmpeg-devel] [PATCH 1/7 v2] avcodec: add an Immersive Audio Model and Formats frame split bsf Andreas Rheinhardt
2024-01-30 22:07 ` James Almer
2024-01-30 22:11 ` Andreas Rheinhardt
2024-01-30 22:12 ` James Almer
2024-02-03 15:04 ` Andreas Rheinhardt
Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
This inbox may be cloned and mirrored by anyone:
git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git
# If you have public-inbox 1.1+ installed, you may
# initialize and index your mirror using the following commands:
public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
ffmpegdev@gitmailbox.com
public-inbox-index ffmpegdev
Example config snippet for mirrors.
AGPL code for this site: git clone https://public-inbox.org/public-inbox.git