1 /*
2 * Copyright (c) 2024, 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
13 #include "iamf/cli/codec/lpcm_decoder.h"
14
15 #include <cstddef>
16 #include <cstdint>
17 #include <vector>
18
19 #include "absl/status/status.h"
20 #include "absl/strings/str_cat.h"
21 #include "absl/types/span.h"
22 #include "iamf/cli/codec/decoder_base.h"
23 #include "iamf/common/utils/macros.h"
24 #include "iamf/common/utils/numeric_utils.h"
25 #include "iamf/obu/codec_config.h"
26 #include "iamf/obu/decoder_config/lpcm_decoder_config.h"
27
28 namespace iamf_tools {
29
LpcmDecoder(const CodecConfigObu & codec_config_obu,int num_channels)30 LpcmDecoder::LpcmDecoder(const CodecConfigObu& codec_config_obu,
31 int num_channels)
32 : DecoderBase(num_channels,
33 static_cast<int>(codec_config_obu.GetNumSamplesPerFrame())),
34 decoder_config_(std::get<LpcmDecoderConfig>(
35 codec_config_obu.GetCodecConfig().decoder_config)),
36 audio_roll_distance_(
37 codec_config_obu.GetCodecConfig().audio_roll_distance) {}
38
Initialize()39 absl::Status LpcmDecoder::Initialize() {
40 RETURN_IF_NOT_OK(decoder_config_.Validate(audio_roll_distance_));
41 return absl::OkStatus();
42 }
43
DecodeAudioFrame(const std::vector<uint8_t> & encoded_frame)44 absl::Status LpcmDecoder::DecodeAudioFrame(
45 const std::vector<uint8_t>& encoded_frame) {
46 num_valid_ticks_ = 0;
47 uint8_t bit_depth;
48 auto status = decoder_config_.GetBitDepthToMeasureLoudness(bit_depth);
49 if (!status.ok()) {
50 return status;
51 }
52 // The LpcmDecoderConfig should have checked for valid values before returning
53 // the bit depth, but we defensively check that it's a multiple of 8 here.
54 if (bit_depth % 8 != 0) {
55 return absl::InvalidArgumentError(
56 absl::StrCat("LpcmDecoder::DecodeAudioFrame() failed: bit_depth (",
57 bit_depth, ") is not a multiple of 8."));
58 }
59 const size_t bytes_per_sample = bit_depth / 8;
60 // Make sure we have a valid number of bytes. There needs to be an equal
61 // number of samples for each channel.
62 if (encoded_frame.size() % bytes_per_sample != 0 ||
63 (encoded_frame.size() / bytes_per_sample) % num_channels_ != 0) {
64 return absl::InvalidArgumentError(absl::StrCat(
65 "LpcmDecoder::DecodeAudioFrame() failed: encoded_frame has ",
66 encoded_frame.size(),
67 " bytes, which is not a multiple of the bytes per sample (",
68 bytes_per_sample, ") * number of channels (", num_channels_, ")."));
69 }
70 // Each time tick has one sample for each channel.
71 const size_t num_ticks =
72 encoded_frame.size() / bytes_per_sample / num_channels_;
73 if (num_ticks > num_samples_per_channel_) {
74 return absl::InvalidArgumentError(
75 absl::StrCat("Detected num_ticks= ", num_ticks,
76 ", but the decoder is only configured for up to "
77 "num_samples_per_channel_= ",
78 num_samples_per_channel_, "."));
79 }
80 num_valid_ticks_ = num_ticks;
81
82 const bool little_endian = decoder_config_.IsLittleEndian();
83
84 int32_t sample_result;
85 for (size_t t = 0; t < num_valid_ticks_; ++t) {
86 // One sample for each channel in this time tick.
87 for (size_t c = 0; c < num_channels_; ++c) {
88 const size_t offset = (t * num_channels_ + c) * bytes_per_sample;
89 absl::Span<const uint8_t> input_bytes(encoded_frame.data() + offset,
90 bytes_per_sample);
91 if (little_endian) {
92 status = LittleEndianBytesToInt32(input_bytes, sample_result);
93 } else {
94 status = BigEndianBytesToInt32(input_bytes, sample_result);
95 }
96 RETURN_IF_NOT_OK(status);
97 decoded_samples_[t][c] = sample_result;
98 }
99 }
100 return absl::OkStatus();
101 }
102
103 } // namespace iamf_tools
104