• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023, Alliance for Open Media. All rights reserved
3  *
4  * This source code is subject to the terms of the BSD 3-Clause Clear License
5  * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear
6  * License was not distributed with this source code in the LICENSE file, you
7  * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the
8  * Alliance for Open Media Patent License 1.0 was not distributed with this
9  * source code in the PATENTS file, you can obtain it at
10  * www.aomedia.org/license/patent.
11  */
12 #include "iamf/cli/rendering_mix_presentation_finalizer.h"
13 
14 #include <algorithm>
15 #include <cmath>
16 #include <cstddef>
17 #include <cstdint>
18 #include <cstring>
19 #include <functional>
20 #include <list>
21 #include <memory>
22 #include <string>
23 #include <utility>
24 #include <vector>
25 
26 #include "absl/base/nullability.h"
27 #include "absl/container/flat_hash_map.h"
28 #include "absl/container/flat_hash_set.h"
29 #include "absl/functional/any_invocable.h"
30 #include "absl/log/check.h"
31 #include "absl/log/log.h"
32 #include "absl/status/status.h"
33 #include "absl/strings/str_cat.h"
34 #include "absl/strings/string_view.h"
35 #include "absl/time/clock.h"
36 #include "absl/time/time.h"
37 #include "absl/types/span.h"
38 #include "iamf/cli/audio_element_with_data.h"
39 #include "iamf/cli/cli_util.h"
40 #include "iamf/cli/demixing_module.h"
41 #include "iamf/cli/loudness_calculator_base.h"
42 #include "iamf/cli/loudness_calculator_factory_base.h"
43 #include "iamf/cli/parameter_block_with_data.h"
44 #include "iamf/cli/renderer/audio_element_renderer_base.h"
45 #include "iamf/cli/renderer_factory.h"
46 #include "iamf/cli/sample_processor_base.h"
47 #include "iamf/common/utils/macros.h"
48 #include "iamf/common/utils/numeric_utils.h"
49 #include "iamf/common/utils/sample_processing_utils.h"
50 #include "iamf/common/utils/validation_utils.h"
51 #include "iamf/obu/audio_element.h"
52 #include "iamf/obu/codec_config.h"
53 #include "iamf/obu/mix_presentation.h"
54 #include "iamf/obu/param_definitions.h"
55 #include "iamf/obu/parameter_block.h"
56 #include "iamf/obu/types.h"
57 
58 namespace iamf_tools {
59 
60 namespace {
61 
62 using LayoutRenderingMetadata =
63     RenderingMixPresentationFinalizer::LayoutRenderingMetadata;
64 using SubmixRenderingMetadata =
65     RenderingMixPresentationFinalizer::SubmixRenderingMetadata;
66 
CanRenderAnyLayout(const std::vector<SubmixRenderingMetadata> & rendering_metadata)67 bool CanRenderAnyLayout(
68     const std::vector<SubmixRenderingMetadata>& rendering_metadata) {
69   for (auto& submix_rendering_metadata : rendering_metadata) {
70     for (auto& layout_rendering_metadata :
71          submix_rendering_metadata.layout_rendering_metadata) {
72       if (layout_rendering_metadata.can_render) {
73         return true;
74       }
75     }
76   }
77   return false;
78 }
79 
CollectAudioElementsInSubMix(const absl::flat_hash_map<uint32_t,AudioElementWithData> & audio_elements,const std::vector<SubMixAudioElement> & sub_mix_audio_elements,std::vector<const AudioElementWithData * > & audio_elements_in_sub_mix)80 absl::Status CollectAudioElementsInSubMix(
81     const absl::flat_hash_map<uint32_t, AudioElementWithData>& audio_elements,
82     const std::vector<SubMixAudioElement>& sub_mix_audio_elements,
83     std::vector<const AudioElementWithData*>& audio_elements_in_sub_mix) {
84   audio_elements_in_sub_mix.reserve(sub_mix_audio_elements.size());
85   for (const auto& audio_element : sub_mix_audio_elements) {
86     auto iter = audio_elements.find(audio_element.audio_element_id);
87     if (iter == audio_elements.end()) {
88       return absl::InvalidArgumentError(absl::StrCat(
89           "Audio Element with ID= ", audio_element.audio_element_id,
90           " not found"));
91     }
92     audio_elements_in_sub_mix.push_back(&iter->second);
93   }
94 
95   return absl::OkStatus();
96 }
97 
GetCommonCodecConfigPropertiesFromAudioElementIds(const std::vector<const AudioElementWithData * > & audio_elements_in_sub_mix,uint32_t & common_sample_rate,uint8_t & common_bit_depth,uint32_t & common_num_samples_per_frame,bool & requires_resampling)98 absl::Status GetCommonCodecConfigPropertiesFromAudioElementIds(
99     const std::vector<const AudioElementWithData*>& audio_elements_in_sub_mix,
100     uint32_t& common_sample_rate, uint8_t& common_bit_depth,
101     uint32_t& common_num_samples_per_frame, bool& requires_resampling) {
102   absl::flat_hash_set<uint32_t> sample_rates;
103   absl::flat_hash_set<uint32_t> num_samples_per_frame;
104   absl::flat_hash_set<uint8_t> bit_depths;
105 
106   // Get all the bit-depths and sample_rates from each Audio Element.
107   for (const auto* audio_element : audio_elements_in_sub_mix) {
108     num_samples_per_frame.insert(
109         audio_element->codec_config->GetNumSamplesPerFrame());
110     sample_rates.insert(audio_element->codec_config->GetOutputSampleRate());
111     bit_depths.insert(
112         audio_element->codec_config->GetBitDepthToMeasureLoudness());
113   }
114 
115   RETURN_IF_NOT_OK(GetCommonSampleRateAndBitDepth(
116       sample_rates, bit_depths, common_sample_rate, common_bit_depth,
117       requires_resampling));
118   if (num_samples_per_frame.size() != 1) {
119     return absl::InvalidArgumentError(
120         "Audio elements in a submix must have the same number of samples per "
121         "frame.");
122   }
123   common_num_samples_per_frame = *num_samples_per_frame.begin();
124 
125   return absl::OkStatus();
126 }
127 
128 using AudioElementRenderingMetadata =
129     RenderingMixPresentationFinalizer::AudioElementRenderingMetadata;
130 
InitializeRenderingMetadata(const RendererFactoryBase & renderer_factory,const std::vector<const AudioElementWithData * > & audio_elements_in_sub_mix,const std::vector<SubMixAudioElement> & sub_mix_audio_elements,const Layout & loudness_layout,const uint32_t common_sample_rate,std::vector<AudioElementRenderingMetadata> & rendering_metadata_array)131 absl::Status InitializeRenderingMetadata(
132     const RendererFactoryBase& renderer_factory,
133     const std::vector<const AudioElementWithData*>& audio_elements_in_sub_mix,
134     const std::vector<SubMixAudioElement>& sub_mix_audio_elements,
135     const Layout& loudness_layout, const uint32_t common_sample_rate,
136     std::vector<AudioElementRenderingMetadata>& rendering_metadata_array) {
137   rendering_metadata_array.resize(audio_elements_in_sub_mix.size());
138 
139   for (int i = 0; i < audio_elements_in_sub_mix.size(); i++) {
140     const auto& sub_mix_audio_element = *audio_elements_in_sub_mix[i];
141     auto& rendering_metadata = rendering_metadata_array[i];
142     rendering_metadata.audio_element = &(sub_mix_audio_element.obu);
143     rendering_metadata.codec_config = sub_mix_audio_element.codec_config;
144     rendering_metadata.renderer = renderer_factory.CreateRendererForLayout(
145         sub_mix_audio_element.obu.audio_substream_ids_,
146         sub_mix_audio_element.substream_id_to_labels,
147         rendering_metadata.audio_element->GetAudioElementType(),
148         sub_mix_audio_element.obu.config_,
149         sub_mix_audio_elements[i].rendering_config, loudness_layout,
150         static_cast<size_t>(
151             rendering_metadata.codec_config->GetNumSamplesPerFrame()));
152 
153     if (rendering_metadata.renderer == nullptr) {
154       return absl::UnknownError("Unable to create renderer.");
155     }
156 
157     const uint32_t output_sample_rate =
158         sub_mix_audio_element.codec_config->GetOutputSampleRate();
159     if (common_sample_rate != output_sample_rate) {
160       // Theoretically, we would have to resample this audio element to the
161       // common sample rate. However, as of IAMF v1.1.0, the spec forbids
162       // multiple Codec Config OBUs. This case is not possible to occur with a
163       // single Codec Config OBU.
164       return absl::UnimplementedError(
165           absl::StrCat("OBUs with different sample rates not supported yet: (",
166                        common_sample_rate, " != ", output_sample_rate, ")."));
167     }
168   }
169 
170   return absl::OkStatus();
171 }
172 
FlushUntilNonEmptyOrTimeout(AudioElementRendererBase & audio_element_renderer,std::vector<InternalSampleType> & rendered_samples)173 absl::Status FlushUntilNonEmptyOrTimeout(
174     AudioElementRendererBase& audio_element_renderer,
175     std::vector<InternalSampleType>& rendered_samples) {
176   static const int kMaxNumTries = 500;
177   for (int i = 0; i < kMaxNumTries; i++) {
178     RETURN_IF_NOT_OK(audio_element_renderer.Flush(rendered_samples));
179     if (!rendered_samples.empty()) {
180       // Usually samples will be ready right away. So avoid sleeping.
181       return absl::OkStatus();
182     }
183     absl::SleepFor(absl::Milliseconds(10));
184   }
185   return absl::DeadlineExceededError("Timed out waiting for samples.");
186 }
187 
RenderLabeledFrameToLayout(const LabeledFrame & labeled_frame,const AudioElementRenderingMetadata & rendering_metadata,std::vector<InternalSampleType> & rendered_samples)188 absl::Status RenderLabeledFrameToLayout(
189     const LabeledFrame& labeled_frame,
190     const AudioElementRenderingMetadata& rendering_metadata,
191     std::vector<InternalSampleType>& rendered_samples) {
192   const auto num_time_ticks =
193       rendering_metadata.renderer->RenderLabeledFrame(labeled_frame);
194   if (!num_time_ticks.ok()) {
195     return num_time_ticks.status();
196   } else if (*num_time_ticks >
197              static_cast<size_t>(
198                  rendering_metadata.codec_config->GetNumSamplesPerFrame())) {
199     return absl::InvalidArgumentError("Too many samples in this frame");
200   } else if (*num_time_ticks == 0) {
201     // This was an empty frame.
202     return absl::OkStatus();
203   }
204 
205   return FlushUntilNonEmptyOrTimeout(*rendering_metadata.renderer,
206                                      rendered_samples);
207 }
208 
GetParameterBlockLinearMixGainsPerTick(uint32_t common_sample_rate,const ParameterBlockWithData & parameter_block,const MixGainParamDefinition & mix_gain,std::vector<float> & linear_mix_gain_per_tick)209 absl::Status GetParameterBlockLinearMixGainsPerTick(
210     uint32_t common_sample_rate, const ParameterBlockWithData& parameter_block,
211     const MixGainParamDefinition& mix_gain,
212     std::vector<float>& linear_mix_gain_per_tick) {
213   if (mix_gain.parameter_rate_ != common_sample_rate) {
214     // TODO(b/283281856): Support resampling parameter blocks.
215     return absl::UnimplementedError(
216         "Parameter blocks that require resampling are not supported yet.");
217   }
218 
219   const int16_t default_mix_gain = mix_gain.default_mix_gain_;
220   // Initialize to the default gain value.
221   std::fill(linear_mix_gain_per_tick.begin(), linear_mix_gain_per_tick.end(),
222             std::pow(10.0f, Q7_8ToFloat(default_mix_gain) / 20.0f));
223 
224   InternalTimestamp cur_tick = parameter_block.start_timestamp;
225   // Process as many ticks as possible until all are found or the parameter
226   // block ends.
227   while (cur_tick < parameter_block.end_timestamp &&
228          (cur_tick - parameter_block.start_timestamp) <
229              linear_mix_gain_per_tick.size()) {
230     RETURN_IF_NOT_OK(parameter_block.obu->GetLinearMixGain(
231         cur_tick - parameter_block.start_timestamp,
232         linear_mix_gain_per_tick[cur_tick - parameter_block.start_timestamp]));
233     cur_tick++;
234   }
235   return absl::OkStatus();
236 }
237 
238 // Fills in the output `mix_gains` with the gain in Q7.8 format to apply at each
239 // tick.
240 // TODO(b/288073842): Consider improving computational efficiency instead of
241 //                    searching through all parameter blocks for each frame.
242 // TODO(b/379961928): Remove this function once the new
243 //                    `GetParameterBlockLinearMixGainsPerTick()` is in use.
GetParameterBlockLinearMixGainsPerTick(uint32_t common_sample_rate,InternalTimestamp start_timestamp,InternalTimestamp end_timestamp,const std::list<ParameterBlockWithData> & parameter_blocks,const MixGainParamDefinition & mix_gain,std::vector<float> & linear_mix_gain_per_tick)244 absl::Status GetParameterBlockLinearMixGainsPerTick(
245     uint32_t common_sample_rate, InternalTimestamp start_timestamp,
246     InternalTimestamp end_timestamp,
247     const std::list<ParameterBlockWithData>& parameter_blocks,
248     const MixGainParamDefinition& mix_gain,
249     std::vector<float>& linear_mix_gain_per_tick) {
250   if (mix_gain.parameter_rate_ != common_sample_rate) {
251     // TODO(b/283281856): Support resampling parameter blocks.
252     return absl::UnimplementedError(
253         "Parameter blocks that require resampling are not supported yet.");
254   }
255 
256   const auto parameter_id = mix_gain.parameter_id_;
257   const int16_t default_mix_gain = mix_gain.default_mix_gain_;
258 
259   // Initialize to the default gain value.
260   std::fill(linear_mix_gain_per_tick.begin(), linear_mix_gain_per_tick.end(),
261             std::pow(10.0f, Q7_8ToFloat(default_mix_gain) / 20.0f));
262 
263   InternalTimestamp cur_tick = start_timestamp;
264 
265   // Find the mix gain at each tick. May terminate early if there are samples to
266   // trim at the end.
267   while (cur_tick < end_timestamp &&
268          (cur_tick - start_timestamp) < linear_mix_gain_per_tick.size()) {
269     // Find the parameter block that this tick occurs during.
270     const auto parameter_block_iter = std::find_if(
271         parameter_blocks.begin(), parameter_blocks.end(),
272         [cur_tick, parameter_id](const auto& parameter_block) {
273           return parameter_block.obu->parameter_id_ == parameter_id &&
274                  parameter_block.start_timestamp <= cur_tick &&
275                  cur_tick < parameter_block.end_timestamp;
276         });
277     if (parameter_block_iter == parameter_blocks.end()) {
278       // Default mix gain will be used for this frame. Logic elsewhere validates
279       // the rest of the audio frames have consistent coverage.
280       break;
281     }
282 
283     // Process as many ticks as possible until all are found or the parameter
284     // block ends.
285     while (cur_tick < end_timestamp &&
286            cur_tick < parameter_block_iter->end_timestamp &&
287            (cur_tick - start_timestamp) < linear_mix_gain_per_tick.size()) {
288       RETURN_IF_NOT_OK(parameter_block_iter->obu->GetLinearMixGain(
289           cur_tick - parameter_block_iter->start_timestamp,
290           linear_mix_gain_per_tick[cur_tick - start_timestamp]));
291       cur_tick++;
292     }
293   }
294 
295   return absl::OkStatus();
296 }
297 
GetAndApplyMixGain(uint32_t common_sample_rate,const ParameterBlockWithData & parameter_block,const MixGainParamDefinition & mix_gain,int32_t num_channels,std::vector<InternalSampleType> & rendered_samples)298 absl::Status GetAndApplyMixGain(  // NOLINT
299     uint32_t common_sample_rate, const ParameterBlockWithData& parameter_block,
300     const MixGainParamDefinition& mix_gain, int32_t num_channels,
301     std::vector<InternalSampleType>& rendered_samples) {
302   if (rendered_samples.size() % num_channels != 0) {
303     return absl::InvalidArgumentError(absl::StrCat(
304         "Expected an integer number of interlaced channels. "
305         "renderered_samples.size()= ",
306         rendered_samples.size(), ", num_channels= ", num_channels));
307   }
308 
309   // Get the mix gain on a per tick basis from the parameter block.
310   std::vector<float> linear_mix_gain_per_tick(rendered_samples.size() /
311                                               num_channels);
312   RETURN_IF_NOT_OK(GetParameterBlockLinearMixGainsPerTick(
313       common_sample_rate, parameter_block, mix_gain, linear_mix_gain_per_tick));
314 
315   if (!linear_mix_gain_per_tick.empty()) {
316     LOG_FIRST_N(INFO, 6) << " First tick in this frame has gain: "
317                          << linear_mix_gain_per_tick.front();
318   }
319 
320   for (int tick = 0; tick < linear_mix_gain_per_tick.size(); tick++) {
321     for (int channel = 0; channel < num_channels; channel++) {
322       // Apply the same mix gain to all `num_channels` associated with this
323       // tick.
324       rendered_samples[tick * num_channels + channel] *=
325           linear_mix_gain_per_tick[tick];
326     }
327   }
328 
329   return absl::OkStatus();
330 }
331 
332 // TODO(b/379961928): Remove once the new GetAndApplyMixGain is in use.
GetAndApplyMixGain(uint32_t common_sample_rate,InternalTimestamp start_timestamp,InternalTimestamp end_timestamp,const std::list<ParameterBlockWithData> & parameter_blocks,const MixGainParamDefinition & mix_gain,int32_t num_channels,std::vector<float> & linear_mix_gain_per_tick,std::vector<InternalSampleType> & rendered_samples)333 absl::Status GetAndApplyMixGain(
334     uint32_t common_sample_rate, InternalTimestamp start_timestamp,
335     InternalTimestamp end_timestamp,
336     const std::list<ParameterBlockWithData>& parameter_blocks,
337     const MixGainParamDefinition& mix_gain, int32_t num_channels,
338     std::vector<float>& linear_mix_gain_per_tick,
339     std::vector<InternalSampleType>& rendered_samples) {
340   if (rendered_samples.size() % num_channels != 0) {
341     return absl::InvalidArgumentError(absl::StrCat(
342         "Expected an integer number of interlaced channels. "
343         "renderered_samples.size()= ",
344         rendered_samples.size(), ", num_channels= ", num_channels));
345   }
346 
347   // Get the mix gain on a per tick basis from the parameter block.
348   linear_mix_gain_per_tick.resize(rendered_samples.size() / num_channels, 0.0f);
349   RETURN_IF_NOT_OK(GetParameterBlockLinearMixGainsPerTick(
350       common_sample_rate, start_timestamp, end_timestamp, parameter_blocks,
351       mix_gain, linear_mix_gain_per_tick));
352 
353   if (!linear_mix_gain_per_tick.empty()) {
354     LOG_FIRST_N(INFO, 6) << " First tick in this frame has gain: "
355                          << linear_mix_gain_per_tick.front();
356   }
357 
358   for (int tick = 0; tick < linear_mix_gain_per_tick.size(); tick++) {
359     for (int channel = 0; channel < num_channels; channel++) {
360       // Apply the same mix gain to all `num_channels` associated with this
361       // tick.
362       rendered_samples[tick * num_channels + channel] *=
363           linear_mix_gain_per_tick[tick];
364     }
365   }
366 
367   return absl::OkStatus();
368 }
369 
MixAudioElements(std::vector<std::vector<InternalSampleType>> & rendered_audio_elements,std::vector<InternalSampleType> & rendered_samples)370 absl::Status MixAudioElements(
371     std::vector<std::vector<InternalSampleType>>& rendered_audio_elements,
372     std::vector<InternalSampleType>& rendered_samples) {
373   const size_t num_samples = rendered_audio_elements.empty()
374                                  ? 0
375                                  : rendered_audio_elements.front().size();
376   rendered_samples.reserve(num_samples);
377 
378   for (const auto& rendered_audio_element : rendered_audio_elements) {
379     if (rendered_audio_element.size() != num_samples) {
380       return absl::UnknownError(
381           "Expected all frames to have the same number of samples.");
382     }
383   }
384 
385   for (int i = 0; i < num_samples; i++) {
386     InternalSampleType mixed_sample = 0;
387     // Sum all audio elements for this tick.
388     for (const auto& rendered_audio_element : rendered_audio_elements) {
389       mixed_sample += rendered_audio_element[i];
390     }
391     // Push the clipped result.
392     rendered_samples.push_back(mixed_sample);
393   }
394 
395   return absl::OkStatus();
396 }
397 
398 // Fills in `valid_rendered_samples` which is a view backed by
399 // `rendered_samples` of the ticks actually rendered.
RenderAllFramesForLayout(int32_t num_channels,const std::vector<SubMixAudioElement> sub_mix_audio_elements,const MixGainParamDefinition & output_mix_gain,const IdLabeledFrameMap & id_to_labeled_frame,const std::vector<AudioElementRenderingMetadata> & rendering_metadata_array,InternalTimestamp start_timestamp,InternalTimestamp end_timestamp,const std::list<ParameterBlockWithData> & parameter_blocks,const uint32_t common_sample_rate,std::vector<std::vector<int32_t>> & rendered_samples,absl::Span<const std::vector<int32_t>> & valid_rendered_samples)400 absl::Status RenderAllFramesForLayout(
401     int32_t num_channels,
402     const std::vector<SubMixAudioElement> sub_mix_audio_elements,
403     const MixGainParamDefinition& output_mix_gain,
404     const IdLabeledFrameMap& id_to_labeled_frame,
405     const std::vector<AudioElementRenderingMetadata>& rendering_metadata_array,
406     InternalTimestamp start_timestamp, InternalTimestamp end_timestamp,
407     const std::list<ParameterBlockWithData>& parameter_blocks,
408     const uint32_t common_sample_rate,
409     std::vector<std::vector<int32_t>>& rendered_samples,
410     absl::Span<const std::vector<int32_t>>& valid_rendered_samples) {
411   // Each audio element rendered individually with `element_mix_gain` applied.
412   std::vector<std::vector<InternalSampleType>> rendered_audio_elements(
413       sub_mix_audio_elements.size());
414   std::vector<float> linear_mix_gain_per_tick;
415   for (int i = 0; i < sub_mix_audio_elements.size(); i++) {
416     const SubMixAudioElement& sub_mix_audio_element = sub_mix_audio_elements[i];
417     const auto audio_element_id = sub_mix_audio_element.audio_element_id;
418     const auto& rendering_metadata = rendering_metadata_array[i];
419 
420     if (id_to_labeled_frame.find(audio_element_id) !=
421         id_to_labeled_frame.end()) {
422       const auto& labeled_frame = id_to_labeled_frame.at(audio_element_id);
423       // Render the frame to the specified `loudness_layout` and apply element
424       // mix gain.
425       RETURN_IF_NOT_OK(RenderLabeledFrameToLayout(
426           labeled_frame, rendering_metadata, rendered_audio_elements[i]));
427     }
428     RETURN_IF_NOT_OK(GetAndApplyMixGain(
429         common_sample_rate, start_timestamp, end_timestamp, parameter_blocks,
430         sub_mix_audio_element.element_mix_gain, num_channels,
431         linear_mix_gain_per_tick, rendered_audio_elements[i]));
432   }
433 
434   // Mix the audio elements.
435   std::vector<InternalSampleType> rendered_samples_internal;
436   RETURN_IF_NOT_OK(
437       MixAudioElements(rendered_audio_elements, rendered_samples_internal));
438 
439   LOG_FIRST_N(INFO, 1) << "    Applying output_mix_gain.default_mix_gain= "
440                        << output_mix_gain.default_mix_gain_;
441 
442   RETURN_IF_NOT_OK(
443       GetAndApplyMixGain(common_sample_rate, start_timestamp, end_timestamp,
444                          parameter_blocks, output_mix_gain, num_channels,
445                          linear_mix_gain_per_tick, rendered_samples_internal));
446 
447   // Convert the rendered samples to int32, clipping if needed.
448   size_t num_ticks = 0;
449   RETURN_IF_NOT_OK(ConvertInterleavedToTimeChannel(
450       absl::MakeConstSpan(rendered_samples_internal), num_channels,
451       absl::AnyInvocable<absl::Status(InternalSampleType, int32_t&) const>(
452           NormalizedFloatingPointToInt32<InternalSampleType>),
453       rendered_samples, num_ticks));
454   valid_rendered_samples =
455       absl::MakeConstSpan(rendered_samples).first(num_ticks);
456   return absl::OkStatus();
457 }
458 
ValidateUserLoudness(const LoudnessInfo & user_loudness,const uint32_t mix_presentation_id,const int sub_mix_index,const int layout_index,const LoudnessInfo & output_loudness,bool & loudness_matches_user_data)459 absl::Status ValidateUserLoudness(const LoudnessInfo& user_loudness,
460                                   const uint32_t mix_presentation_id,
461                                   const int sub_mix_index,
462                                   const int layout_index,
463                                   const LoudnessInfo& output_loudness,
464                                   bool& loudness_matches_user_data) {
465   const std::string mix_presentation_sub_mix_layout_index =
466       absl::StrCat("Mix Presentation(ID ", mix_presentation_id, ")->sub_mixes[",
467                    sub_mix_index, "]->layouts[", layout_index, "]: ");
468   if (output_loudness.integrated_loudness !=
469       user_loudness.integrated_loudness) {
470     LOG(ERROR) << mix_presentation_sub_mix_layout_index
471                << "Computed integrated loudness different from "
472                << "user specification: " << output_loudness.integrated_loudness
473                << " vs " << user_loudness.integrated_loudness;
474     loudness_matches_user_data = false;
475   }
476 
477   if (output_loudness.digital_peak != user_loudness.digital_peak) {
478     LOG(ERROR) << mix_presentation_sub_mix_layout_index
479                << "Computed digital peak different from "
480                << "user specification: " << output_loudness.digital_peak
481                << " vs " << user_loudness.digital_peak;
482     loudness_matches_user_data = false;
483   }
484 
485   if (output_loudness.info_type & LoudnessInfo::kTruePeak) {
486     if (output_loudness.true_peak != user_loudness.true_peak) {
487       LOG(ERROR) << mix_presentation_sub_mix_layout_index
488                  << "Computed true peak different from "
489                  << "user specification: " << output_loudness.true_peak
490                  << " vs " << user_loudness.true_peak;
491       loudness_matches_user_data = false;
492     }
493   }
494 
495   // Anchored loudness and layout extension are copied from the user input
496   // and do not need to be validated.
497 
498   return absl::OkStatus();
499 }
500 
501 // Calculates the loudness of the rendered samples. These rendered samples are
502 // for a specific timestamp for a given submix and layout. If
503 // `validate_loudness` is true, then the user provided loudness values are
504 // validated against the computed values.
UpdateLoudnessInfoForLayout(bool validate_loudness,const LoudnessInfo & input_loudness,const uint32_t mix_presentation_id,const int sub_mix_index,const int layout_index,bool & loudness_matches_user_data,std::unique_ptr<LoudnessCalculatorBase> loudness_calculator,LoudnessInfo & output_calculated_loudness)505 absl::Status UpdateLoudnessInfoForLayout(
506     bool validate_loudness, const LoudnessInfo& input_loudness,
507     const uint32_t mix_presentation_id, const int sub_mix_index,
508     const int layout_index, bool& loudness_matches_user_data,
509     std::unique_ptr<LoudnessCalculatorBase> loudness_calculator,
510     LoudnessInfo& output_calculated_loudness) {
511   // Copy the final loudness values back to the output OBU.
512   auto calculated_loudness_info = loudness_calculator->QueryLoudness();
513   if (!calculated_loudness_info.ok()) {
514     return calculated_loudness_info.status();
515   }
516 
517   if (validate_loudness) {
518     // Validate any user provided loudness values match computed values.
519     RETURN_IF_NOT_OK(ValidateUserLoudness(
520         input_loudness, mix_presentation_id, sub_mix_index, layout_index,
521         *calculated_loudness_info, loudness_matches_user_data));
522   }
523   output_calculated_loudness = *calculated_loudness_info;
524   return absl::OkStatus();
525 }
526 
527 // Generates rendering metadata for all layouts within a submix. This includes
528 // optionally creating a sample processor and/or a loudness calculator for each
529 // layout.
GenerateRenderingMetadataForLayouts(const RendererFactoryBase & renderer_factory,const LoudnessCalculatorFactoryBase * loudness_calculator_factory,const RenderingMixPresentationFinalizer::SampleProcessorFactory & sample_processor_factory,const DecodedUleb128 mix_presentation_id,const MixPresentationSubMix & sub_mix,int sub_mix_index,const std::vector<const AudioElementWithData * > & audio_elements_in_sub_mix,uint32_t common_sample_rate,uint8_t rendering_bit_depth,uint32_t common_num_samples_per_frame,std::vector<LayoutRenderingMetadata> & output_layout_rendering_metadata)530 absl::Status GenerateRenderingMetadataForLayouts(
531     const RendererFactoryBase& renderer_factory,
532     const LoudnessCalculatorFactoryBase* loudness_calculator_factory,
533     const RenderingMixPresentationFinalizer::SampleProcessorFactory&
534         sample_processor_factory,
535     const DecodedUleb128 mix_presentation_id,
536     const MixPresentationSubMix& sub_mix, int sub_mix_index,
537     const std::vector<const AudioElementWithData*>& audio_elements_in_sub_mix,
538     uint32_t common_sample_rate, uint8_t rendering_bit_depth,
539     uint32_t common_num_samples_per_frame,
540     std::vector<LayoutRenderingMetadata>& output_layout_rendering_metadata) {
541   output_layout_rendering_metadata.resize(sub_mix.layouts.size());
542   for (int layout_index = 0; layout_index < sub_mix.layouts.size();
543        layout_index++) {
544     LayoutRenderingMetadata& layout_rendering_metadata =
545         output_layout_rendering_metadata[layout_index];
546     const auto& layout = sub_mix.layouts[layout_index];
547 
548     int32_t num_channels = 0;
549     auto can_render_status = MixPresentationObu::GetNumChannelsFromLayout(
550         layout.loudness_layout, num_channels);
551     layout_rendering_metadata.num_channels = num_channels;
552 
553     can_render_status.Update(InitializeRenderingMetadata(
554         renderer_factory, audio_elements_in_sub_mix, sub_mix.audio_elements,
555         layout.loudness_layout, common_sample_rate,
556         layout_rendering_metadata.audio_element_rendering_metadata));
557 
558     if (!can_render_status.ok()) {
559       layout_rendering_metadata.can_render = false;
560       continue;
561     } else {
562       layout_rendering_metadata.can_render = true;
563     }
564     if (loudness_calculator_factory != nullptr) {
565       // Optionally create a loudness calculator.
566       layout_rendering_metadata.loudness_calculator =
567           loudness_calculator_factory->CreateLoudnessCalculator(
568               layout, common_num_samples_per_frame, common_sample_rate,
569               rendering_bit_depth);
570     }
571     // Optionally create a post-processor.
572     layout_rendering_metadata.sample_processor = sample_processor_factory(
573         mix_presentation_id, sub_mix_index, layout_index,
574         layout.loudness_layout, num_channels, common_sample_rate,
575         rendering_bit_depth, common_num_samples_per_frame);
576 
577     // Pre-allocate a buffer to store a frame's worth of rendered samples.
578     layout_rendering_metadata.rendered_samples.resize(
579         common_num_samples_per_frame, std::vector<int32_t>(num_channels, 0));
580   }
581 
582   return absl::OkStatus();
583 }
584 
585 // We generate one rendering metadata object for each submix. Once this
586 // metadata is generated, we will loop through it to render all submixes
587 // for a given timestamp. Within a submix, there can be many different audio
588 // elements and layouts that need to be rendered as well. Not all of these
589 // need to be rendered; only the ones that either have a wav writer or a
590 // loudness calculator.
GenerateRenderingMetadataForSubmixes(const RendererFactoryBase & renderer_factory,absl::Nullable<const LoudnessCalculatorFactoryBase * > loudness_calculator_factory,const RenderingMixPresentationFinalizer::SampleProcessorFactory & sample_processor_factory,const absl::flat_hash_map<uint32_t,AudioElementWithData> & audio_elements,const MixPresentationObu & mix_presentation_obu,std::vector<SubmixRenderingMetadata> & output_rendering_metadata)591 absl::Status GenerateRenderingMetadataForSubmixes(
592     const RendererFactoryBase& renderer_factory,
593     absl::Nullable<const LoudnessCalculatorFactoryBase*>
594         loudness_calculator_factory,
595     const RenderingMixPresentationFinalizer::SampleProcessorFactory&
596         sample_processor_factory,
597     const absl::flat_hash_map<uint32_t, AudioElementWithData>& audio_elements,
598     const MixPresentationObu& mix_presentation_obu,
599     std::vector<SubmixRenderingMetadata>& output_rendering_metadata) {
600   const auto mix_presentation_id = mix_presentation_obu.GetMixPresentationId();
601   output_rendering_metadata.resize(mix_presentation_obu.sub_mixes_.size());
602   for (int sub_mix_index = 0;
603        sub_mix_index < mix_presentation_obu.sub_mixes_.size();
604        ++sub_mix_index) {
605     SubmixRenderingMetadata& submix_rendering_metadata =
606         output_rendering_metadata[sub_mix_index];
607     const auto& sub_mix = mix_presentation_obu.sub_mixes_[sub_mix_index];
608 
609     // Pointers to audio elements in this sub mix; useful later.
610     std::vector<const AudioElementWithData*> audio_elements_in_sub_mix;
611     RETURN_IF_NOT_OK(CollectAudioElementsInSubMix(
612         audio_elements, sub_mix.audio_elements, audio_elements_in_sub_mix));
613 
614     submix_rendering_metadata.audio_elements_in_sub_mix =
615         sub_mix.audio_elements;
616     submix_rendering_metadata.mix_gain =
617         std::make_unique<MixGainParamDefinition>(sub_mix.output_mix_gain);
618 
619     // Data common to all audio elements and layouts.
620     bool requires_resampling;
621     uint32_t common_num_samples_per_frame;
622     uint8_t rendering_bit_depth;
623     RETURN_IF_NOT_OK(GetCommonCodecConfigPropertiesFromAudioElementIds(
624         audio_elements_in_sub_mix, submix_rendering_metadata.common_sample_rate,
625         rendering_bit_depth, common_num_samples_per_frame,
626         requires_resampling));
627     if (requires_resampling) {
628       // Detected multiple Codec Config OBUs with different sample rates or
629       // bit-depths. As of IAMF v1.1.0, multiple Codec  Config OBUs in the same
630       // IA sequence are never permitted. The spec implies we would have to
631       // resample to a common sample rate and/or bit-depth.
632       return absl::UnimplementedError(
633           "This implementation does not support mixing Codec Config OBUs with "
634           "different sample rates or bit-depths.");
635     }
636     RETURN_IF_NOT_OK(GenerateRenderingMetadataForLayouts(
637         renderer_factory, loudness_calculator_factory, sample_processor_factory,
638         mix_presentation_id, sub_mix, sub_mix_index, audio_elements_in_sub_mix,
639         submix_rendering_metadata.common_sample_rate, rendering_bit_depth,
640         common_num_samples_per_frame,
641         submix_rendering_metadata.layout_rendering_metadata));
642   }
643   return absl::OkStatus();
644 }
645 
FlushPostProcessors(std::vector<SubmixRenderingMetadata> & rendering_metadata)646 absl::Status FlushPostProcessors(
647     std::vector<SubmixRenderingMetadata>& rendering_metadata) {
648   for (auto& submix_rendering_metadata : rendering_metadata) {
649     for (auto& layout_rendering_metadata :
650          submix_rendering_metadata.layout_rendering_metadata) {
651       if (layout_rendering_metadata.sample_processor != nullptr) {
652         RETURN_IF_NOT_OK(layout_rendering_metadata.sample_processor->Flush());
653       }
654     }
655   }
656 
657   return absl::OkStatus();
658 }
659 
FillLoudnessForMixPresentation(bool validate_loudness,std::vector<SubmixRenderingMetadata> & rendering_metadata,MixPresentationObu & mix_presentation_obu)660 absl::Status FillLoudnessForMixPresentation(
661     bool validate_loudness,
662     std::vector<SubmixRenderingMetadata>& rendering_metadata,
663     MixPresentationObu& mix_presentation_obu) {
664   bool loudness_matches_user_data = true;
665   int submix_index = 0;
666   for (auto& submix_rendering_metadata : rendering_metadata) {
667     int layout_index = 0;
668     for (auto& layout_rendering_metadata :
669          submix_rendering_metadata.layout_rendering_metadata) {
670       if (layout_rendering_metadata.loudness_calculator != nullptr) {
671         RETURN_IF_NOT_OK(UpdateLoudnessInfoForLayout(
672             validate_loudness,
673             mix_presentation_obu.sub_mixes_[submix_index]
674                 .layouts[layout_index]
675                 .loudness,
676             mix_presentation_obu.GetMixPresentationId(), submix_index,
677             layout_index, loudness_matches_user_data,
678             std::move(layout_rendering_metadata.loudness_calculator),
679             mix_presentation_obu.sub_mixes_[submix_index]
680                 .layouts[layout_index]
681                 .loudness));
682       }
683       layout_index++;
684     }
685     submix_index++;
686   }
687   if (!loudness_matches_user_data) {
688     return absl::InvalidArgumentError("Loudness does not match user data.");
689   }
690   return absl::OkStatus();
691 }
692 
693 // Renders all submixes, layouts, and audio elements for a temporal unit. It
694 // then optionally writes the rendered samples to a wav file and/or calculates
695 // the loudness of the rendered samples.
RenderWriteAndCalculateLoudnessForTemporalUnit(const IdLabeledFrameMap & id_to_labeled_frame,InternalTimestamp start_timestamp,InternalTimestamp end_timestamp,const std::list<ParameterBlockWithData> & parameter_blocks,std::vector<SubmixRenderingMetadata> & rendering_metadata)696 absl::Status RenderWriteAndCalculateLoudnessForTemporalUnit(
697     const IdLabeledFrameMap& id_to_labeled_frame,
698     InternalTimestamp start_timestamp, InternalTimestamp end_timestamp,
699     const std::list<ParameterBlockWithData>& parameter_blocks,
700     std::vector<SubmixRenderingMetadata>& rendering_metadata) {
701   for (auto& submix_rendering_metadata : rendering_metadata) {
702     for (auto& layout_rendering_metadata :
703          submix_rendering_metadata.layout_rendering_metadata) {
704       if (!layout_rendering_metadata.can_render) {
705         continue;
706       }
707       if (submix_rendering_metadata.mix_gain == nullptr) {
708         return absl::InvalidArgumentError("Submix mix gain is null");
709       }
710 
711       RETURN_IF_NOT_OK(RenderAllFramesForLayout(
712           layout_rendering_metadata.num_channels,
713           submix_rendering_metadata.audio_elements_in_sub_mix,
714           *submix_rendering_metadata.mix_gain, id_to_labeled_frame,
715           layout_rendering_metadata.audio_element_rendering_metadata,
716           start_timestamp, end_timestamp, parameter_blocks,
717           submix_rendering_metadata.common_sample_rate,
718           layout_rendering_metadata.rendered_samples,
719           layout_rendering_metadata.valid_rendered_samples));
720 
721       // Calculate loudness based on the original rendered samples; we do not
722       // know what post-processing the end user will have.
723       if (layout_rendering_metadata.loudness_calculator != nullptr) {
724         RETURN_IF_NOT_OK(
725             layout_rendering_metadata.loudness_calculator
726                 ->AccumulateLoudnessForSamples(
727                     layout_rendering_metadata.valid_rendered_samples));
728       }
729 
730       // Perform any post-processing.
731       if (layout_rendering_metadata.sample_processor != nullptr) {
732         RETURN_IF_NOT_OK(layout_rendering_metadata.sample_processor->PushFrame(
733             layout_rendering_metadata.valid_rendered_samples));
734       }
735     }
736   }
737   return absl::OkStatus();
738 }
739 
740 absl::StatusOr<const LayoutRenderingMetadata*>
GetRenderedSamplesAndPostProcessor(const absl::flat_hash_map<DecodedUleb128,std::vector<SubmixRenderingMetadata>> & mix_presentation_id_to_sub_mix_rendering_metadata,DecodedUleb128 mix_presentation_id,size_t sub_mix_index,size_t layout_index)741 GetRenderedSamplesAndPostProcessor(
742     const absl::flat_hash_map<DecodedUleb128,
743                               std::vector<SubmixRenderingMetadata>>&
744         mix_presentation_id_to_sub_mix_rendering_metadata,
745     DecodedUleb128 mix_presentation_id, size_t sub_mix_index,
746     size_t layout_index) {
747   // Lookup the requested layout in the requested mix presentation.
748   const auto sub_mix_rendering_metadata_it =
749       mix_presentation_id_to_sub_mix_rendering_metadata.find(
750           mix_presentation_id);
751   const auto mix_presentation_id_error_message =
752       absl::StrCat(" Mix Presentation ID ", mix_presentation_id);
753   if (sub_mix_rendering_metadata_it ==
754       mix_presentation_id_to_sub_mix_rendering_metadata.end()) {
755     return absl::NotFoundError(
756         absl::StrCat(mix_presentation_id_error_message,
757                      " not found in rendering metadata."));
758   }
759 
760   // Validate the sub mix and layout are in bounds, then retrieve it.
761   const auto& [unused_mix_presentation_id, sub_mix_rendering_metadatas] =
762       *sub_mix_rendering_metadata_it;
763   RETURN_IF_NOT_OK(Validate(
764       sub_mix_index, std::less<size_t>(), sub_mix_rendering_metadatas.size(),
765       absl::StrCat(mix_presentation_id_error_message, "  sub_mix_index <")));
766   RETURN_IF_NOT_OK(Validate(
767       layout_index, std::less<size_t>(),
768       sub_mix_rendering_metadatas[sub_mix_index]
769           .layout_rendering_metadata.size(),
770       absl::StrCat(mix_presentation_id_error_message, "  layout_index <")));
771   return &sub_mix_rendering_metadatas[sub_mix_index]
772               .layout_rendering_metadata[layout_index];
773 }
774 
775 }  // namespace
776 
777 absl::StatusOr<RenderingMixPresentationFinalizer>
Create(absl::Nullable<const RendererFactoryBase * > renderer_factory,absl::Nullable<const LoudnessCalculatorFactoryBase * > loudness_calculator_factory,const absl::flat_hash_map<uint32_t,AudioElementWithData> & audio_elements,const SampleProcessorFactory & sample_processor_factory,const std::list<MixPresentationObu> & mix_presentation_obus)778 RenderingMixPresentationFinalizer::Create(
779     absl::Nullable<const RendererFactoryBase*> renderer_factory,
780     absl::Nullable<const LoudnessCalculatorFactoryBase*>
781         loudness_calculator_factory,
782     const absl::flat_hash_map<uint32_t, AudioElementWithData>& audio_elements,
783     const SampleProcessorFactory& sample_processor_factory,
784     const std::list<MixPresentationObu>& mix_presentation_obus) {
785   const bool rendering_enabled = renderer_factory != nullptr;
786   if (!rendering_enabled) {
787     LOG(INFO) << "Rendering is safely disabled.";
788   }
789   if (loudness_calculator_factory == nullptr) {
790     LOG(INFO) << "Loudness calculator factory is null so loudness will not be "
791                  "calculated.";
792   }
793   absl::flat_hash_map<DecodedUleb128, std::vector<SubmixRenderingMetadata>>
794       mix_presentation_id_to_rendering_metadata;
795   std::list<MixPresentationObu> mix_presentation_obus_to_render;
796   for (const auto& mix_presentation_obu : mix_presentation_obus) {
797     // Copy all mix presentation OBUs, so they can be echoed back, even when
798     // rendering is disabled.
799     mix_presentation_obus_to_render.emplace_back(mix_presentation_obu);
800 
801     // Fill in rendering metadata if rendering is enabled, and at least one
802     // layout can be rendered.
803     if (rendering_enabled) {
804       std::vector<SubmixRenderingMetadata> temp_sub_mix_rendering_metadata;
805       RETURN_IF_NOT_OK(GenerateRenderingMetadataForSubmixes(
806           *renderer_factory, loudness_calculator_factory,
807           sample_processor_factory, audio_elements, mix_presentation_obu,
808           temp_sub_mix_rendering_metadata));
809       if (CanRenderAnyLayout(temp_sub_mix_rendering_metadata)) {
810         mix_presentation_id_to_rendering_metadata.emplace(
811             mix_presentation_obu.GetMixPresentationId(),
812             std::move(temp_sub_mix_rendering_metadata));
813       }
814     }
815   }
816 
817   return RenderingMixPresentationFinalizer(
818       std::move(mix_presentation_id_to_rendering_metadata),
819       std::move(mix_presentation_obus_to_render));
820 }
821 
PushTemporalUnit(const IdLabeledFrameMap & id_to_labeled_frame,InternalTimestamp start_timestamp,InternalTimestamp end_timestamp,const std::list<ParameterBlockWithData> & parameter_blocks)822 absl::Status RenderingMixPresentationFinalizer::PushTemporalUnit(
823     const IdLabeledFrameMap& id_to_labeled_frame,
824     InternalTimestamp start_timestamp, InternalTimestamp end_timestamp,
825     const std::list<ParameterBlockWithData>& parameter_blocks) {
826   switch (state_) {
827     case kAcceptingTemporalUnits:
828       // Ok to push.
829       break;
830     case kFinalizePushTemporalUnitCalled:
831       return absl::FailedPreconditionError(
832           "PushTemporalUnit() should not be called after "
833           "FinalizePushingTemporalUnits() has been called.");
834     case kFlushedFinalizedMixPresentationObus:
835       return absl::FailedPreconditionError(
836           "PushTemporalUnit() should not be called after "
837           "GetFinalizedMixPresentationOBUs() has been called.");
838   }
839   for (auto& [mix_presentation_ids, sub_mix_rendering_metadata] :
840        mix_presentation_id_to_sub_mix_rendering_metadata_) {
841     RETURN_IF_NOT_OK(RenderWriteAndCalculateLoudnessForTemporalUnit(
842         id_to_labeled_frame, start_timestamp, end_timestamp, parameter_blocks,
843         sub_mix_rendering_metadata));
844   }
845   return absl::OkStatus();
846 }
847 
848 absl::StatusOr<absl::Span<const std::vector<int32_t>>>
GetPostProcessedSamplesAsSpan(DecodedUleb128 mix_presentation_id,size_t sub_mix_index,size_t layout_index) const849 RenderingMixPresentationFinalizer::GetPostProcessedSamplesAsSpan(
850     DecodedUleb128 mix_presentation_id, size_t sub_mix_index,
851     size_t layout_index) const {
852   const auto layout_rendering_metadata = GetRenderedSamplesAndPostProcessor(
853       mix_presentation_id_to_sub_mix_rendering_metadata_, mix_presentation_id,
854       sub_mix_index, layout_index);
855   if (!layout_rendering_metadata.ok()) {
856     return layout_rendering_metadata.status();
857   }
858   // `absl::StatusOr<const T*> cannot hold a nullptr.
859   CHECK_NE(*layout_rendering_metadata, nullptr);
860 
861   // Prioritize returning the post-processed samples if a post-processor is
862   // available. Otherwise, return the rendered samples.
863   return (*layout_rendering_metadata)->sample_processor != nullptr
864              ? (*layout_rendering_metadata)
865                    ->sample_processor->GetOutputSamplesAsSpan()
866              : (*layout_rendering_metadata)->valid_rendered_samples;
867 }
868 
FinalizePushingTemporalUnits()869 absl::Status RenderingMixPresentationFinalizer::FinalizePushingTemporalUnits() {
870   switch (state_) {
871     case kAcceptingTemporalUnits:
872       state_ = kFinalizePushTemporalUnitCalled;
873       break;
874     case kFinalizePushTemporalUnitCalled:
875     case kFlushedFinalizedMixPresentationObus:
876       return absl::FailedPreconditionError(
877           "FinalizePushingTemporalUnits() should not be called twice.");
878   }
879 
880   for (auto& [mix_presentation_id, sub_mix_rendering_metadata] :
881        mix_presentation_id_to_sub_mix_rendering_metadata_) {
882     RETURN_IF_NOT_OK(FlushPostProcessors(sub_mix_rendering_metadata));
883   }
884   return absl::OkStatus();
885 }
886 
887 absl::StatusOr<std::list<MixPresentationObu>>
GetFinalizedMixPresentationObus(bool validate_loudness)888 RenderingMixPresentationFinalizer::GetFinalizedMixPresentationObus(
889     bool validate_loudness) {
890   switch (state_) {
891     case kAcceptingTemporalUnits:
892       return absl::FailedPreconditionError(
893           "FinalizePushingTemporalUnits() should be called before "
894           "GetFinalizedMixPresentationOBUs().");
895     case kFinalizePushTemporalUnitCalled:
896       // Ok to finalize.
897       break;
898     case kFlushedFinalizedMixPresentationObus:
899       return absl::FailedPreconditionError(
900           "GetFinalizedMixPresentationOBUs() should not be called twice.");
901   }
902 
903   // Finalize the OBUs in place.
904   for (auto& mix_presentation_obu : mix_presentation_obus_) {
905     const auto sub_mix_rendering_metadata_it =
906         mix_presentation_id_to_sub_mix_rendering_metadata_.find(
907             mix_presentation_obu.GetMixPresentationId());
908     if (sub_mix_rendering_metadata_it ==
909         mix_presentation_id_to_sub_mix_rendering_metadata_.end()) {
910       LOG(INFO) << "Rendering was disabled for Mix Presentation ID= "
911                 << mix_presentation_obu.GetMixPresentationId()
912                 << " echoing the input OBU.";
913       continue;
914     }
915 
916     RETURN_IF_NOT_OK(FillLoudnessForMixPresentation(
917         validate_loudness, sub_mix_rendering_metadata_it->second,
918         mix_presentation_obu));
919     mix_presentation_obu.PrintObu();
920   }
921 
922   // Flush the finalized OBUs and mark that this class should not use them
923   // again.
924   state_ = kFlushedFinalizedMixPresentationObus;
925   return std::move(mix_presentation_obus_);
926 }
927 
928 }  // namespace iamf_tools
929