• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022, 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 #ifndef COMMON_UTILS_SAMPLE_PROCESSING_UTILS_H_
13 #define COMMON_UTILS_SAMPLE_PROCESSING_UTILS_H_
14 
15 #include <algorithm>
16 #include <cstddef>
17 #include <cstdint>
18 #include <vector>
19 
20 #include "absl/functional/any_invocable.h"
21 #include "absl/log/check.h"
22 #include "absl/log/log.h"
23 #include "absl/status/status.h"
24 #include "absl/strings/str_cat.h"
25 #include "absl/strings/string_view.h"
26 #include "absl/types/span.h"
27 
28 namespace iamf_tools {
29 
30 /*!\brief Writes the input PCM sample to a buffer.
31  *
32  * Writes the most significant `sample_size` bits of `sample` starting at
33  * `buffer[write_position]`. It is up to the user to ensure the buffer is valid.
34  *
35  * \param sample Sample to write the upper `sample_size` bits of.
36  * \param sample_size Sample size in bits. MUST be one of {8, 16, 24, 32}.
37  * \param big_endian `true` to write the sample as big endian. `false` to write
38  *        it as little endian.
39  * \param buffer Start of the buffer to write to.
40  * \param write_position Offset of the buffer to write to. Incremented to one
41  *        after the last byte written on success.
42  * \return `absl::OkStatus()` on success. `absl::InvalidArgumentError()` if
43  *         `sample_size` is invalid.
44  */
45 absl::Status WritePcmSample(uint32_t sample, uint8_t sample_size,
46                             bool big_endian, uint8_t* buffer,
47                             size_t& write_position);
48 
49 /*!\brief Arranges the input samples by time and channel.
50  *
51  * \param samples Interleaved samples to arrange.
52  * \param num_channels Number of channels.
53  * \param transform_samples Function to transform each sample to the output
54  *        type.
55  * \param output Output vector to write the samples to. The size is not
56  *        modified in this function even if the number of input samples do
57  *        not fill the entire output vector. In that case, only the first
58  *        `num_ticks` are filled.
59  * \param num_ticks Number of ticks (time samples) of the output vector that
60  *        are filled in this function.
61  * \return `absl::OkStatus()` on success. `absl::InvalidArgumentError()` if the
62  *         number of samples is not a multiple of the number of channels. An
63  *         error propagated from `transform_samples` if it fails.
64  */
65 template <typename InputType, typename OutputType>
ConvertInterleavedToTimeChannel(absl::Span<const InputType> samples,size_t num_channels,const absl::AnyInvocable<absl::Status (InputType,OutputType &)const> & transform_samples,std::vector<std::vector<OutputType>> & output,size_t & num_ticks)66 absl::Status ConvertInterleavedToTimeChannel(
67     absl::Span<const InputType> samples, size_t num_channels,
68     const absl::AnyInvocable<absl::Status(InputType, OutputType&) const>&
69         transform_samples,
70     std::vector<std::vector<OutputType>>& output, size_t& num_ticks) {
71   if (samples.size() % num_channels != 0) [[unlikely]] {
72     return absl::InvalidArgumentError(absl::StrCat(
73         "Number of samples must be a multiple of the number of "
74         "channels. Found ",
75         samples.size(), " samples and ", num_channels, " channels."));
76   }
77 
78   num_ticks = samples.size() / num_channels;
79   if (num_ticks > output.size()) [[unlikely]] {
80     return absl::InvalidArgumentError(absl::StrCat(
81         "Number of ticks does not fit into the output vector: (num_ticks= ",
82         num_ticks, " > output.size()= ", output.size(), ")"));
83   }
84 
85   for (int t = 0; t < num_ticks; ++t) {
86     if (output[t].size() != num_channels) [[unlikely]] {
87       return absl::InvalidArgumentError(absl::StrCat(
88           "Number of channels is not equal to the output vector at tick ", t,
89           ": (", num_channels, " != ", output[t].size(), ")"));
90     }
91     for (int c = 0; c < num_channels; ++c) {
92       const auto status =
93           transform_samples(samples[t * num_channels + c], output[t][c]);
94       if (!status.ok()) [[unlikely]] {
95         return status;
96       }
97     }
98   }
99   return absl::OkStatus();
100 }
101 
102 /*!\brief Interleaves the input samples.
103  *
104  * \param samples Samples in (time, channel) axes to arrange.
105  * \param transform_samples Function to transform each sample to the output
106  *        type.
107  * \param output Output vector to write the interleaved samples to.
108  * \return `absl::OkStatus()` on success. `absl::InvalidArgumentError()` if the
109  *         input has an inconsistent number of channels. An error propagated
110  *         from `transform_samples` if it fails.
111  */
112 template <typename InputType, typename OutputType>
ConvertTimeChannelToInterleaved(absl::Span<const std::vector<InputType>> input,const absl::AnyInvocable<absl::Status (InputType,OutputType &)const> & transform_samples,std::vector<OutputType> & output)113 absl::Status ConvertTimeChannelToInterleaved(
114     absl::Span<const std::vector<InputType>> input,
115     const absl::AnyInvocable<absl::Status(InputType, OutputType&) const>&
116         transform_samples,
117     std::vector<OutputType>& output) {
118   const size_t num_channels = input.empty() ? 0 : input[0].size();
119   if (!std::all_of(input.begin(), input.end(), [&](const auto& tick) {
120         return tick.size() == num_channels;
121       })) {
122     return absl::InvalidArgumentError(
123         "All ticks must have the same number of channels.");
124   }
125 
126   // TODO(b/382197581): avoid resizing inside this function.
127   output.clear();
128   output.reserve(input.size() * num_channels);
129   for (const auto& tick : input) {
130     for (const auto& sample : tick) {
131       OutputType transformed_sample;
132       auto status = transform_samples(sample, transformed_sample);
133       if (!status.ok()) {
134         return status;
135       }
136       output.emplace_back(transformed_sample);
137     }
138   }
139   return absl::OkStatus();
140 }
141 
142 }  // namespace iamf_tools
143 
144 #endif  // COMMON_UTILS_SAMPLE_PROCESSING_UTILS_H_
145