• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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