• 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/codec/opus_decoder.h"
13 
14 #include <algorithm>
15 #include <cstdint>
16 #include <vector>
17 
18 #include "absl/functional/any_invocable.h"
19 #include "absl/log/log.h"
20 #include "absl/status/status.h"
21 #include "absl/strings/str_cat.h"
22 #include "absl/types/span.h"
23 #include "iamf/cli/codec/decoder_base.h"
24 #include "iamf/cli/codec/opus_utils.h"
25 #include "iamf/common/utils/macros.h"
26 #include "iamf/common/utils/numeric_utils.h"
27 #include "iamf/common/utils/sample_processing_utils.h"
28 #include "iamf/obu/codec_config.h"
29 #include "iamf/obu/decoder_config/opus_decoder_config.h"
30 #include "include/opus.h"
31 #include "include/opus_types.h"
32 
33 namespace iamf_tools {
34 
35 namespace {
36 
37 // Performs validation for values that this implementation assumes are
38 // restricted because they are restricted in IAMF v1.1.0.
ValidateDecoderConfig(const OpusDecoderConfig & opus_decoder_config)39 absl::Status ValidateDecoderConfig(
40     const OpusDecoderConfig& opus_decoder_config) {
41   // Validate the input. Reject values that would need to be added to this
42   // function if they were ever supported.
43   if (opus_decoder_config.output_gain_ != 0 ||
44       opus_decoder_config.mapping_family_ != 0) {
45     const auto error_message = absl::StrCat(
46         "IAMF v1.1.0 expects output_gain: ", opus_decoder_config.output_gain_,
47         " and mapping_family: ", opus_decoder_config.mapping_family_,
48         " to be 0.");
49     return absl::InvalidArgumentError(error_message);
50   }
51 
52   return absl::OkStatus();
53 }
54 
55 }  // namespace
56 
OpusDecoder(const CodecConfigObu & codec_config_obu,int num_channels)57 OpusDecoder::OpusDecoder(const CodecConfigObu& codec_config_obu,
58                          int num_channels)
59     : DecoderBase(num_channels,
60                   static_cast<int>(codec_config_obu.GetNumSamplesPerFrame())),
61       opus_decoder_config_(std::get<OpusDecoderConfig>(
62           codec_config_obu.GetCodecConfig().decoder_config)),
63       output_sample_rate_(codec_config_obu.GetOutputSampleRate()) {}
64 
~OpusDecoder()65 OpusDecoder::~OpusDecoder() {
66   if (decoder_ != nullptr) {
67     opus_decoder_destroy(decoder_);
68   }
69 }
70 
Initialize()71 absl::Status OpusDecoder::Initialize() {
72   MAYBE_RETURN_IF_NOT_OK(ValidateDecoderConfig(opus_decoder_config_));
73 
74   // Initialize the decoder.
75   int opus_error_code;
76   decoder_ = opus_decoder_create(static_cast<opus_int32>(output_sample_rate_),
77                                  num_channels_, &opus_error_code);
78   RETURN_IF_NOT_OK(OpusErrorCodeToAbslStatus(
79       opus_error_code, "Failed to initialize Opus decoder."));
80 
81   return absl::OkStatus();
82 }
83 
DecodeAudioFrame(const std::vector<uint8_t> & encoded_frame)84 absl::Status OpusDecoder::DecodeAudioFrame(
85     const std::vector<uint8_t>& encoded_frame) {
86   num_valid_ticks_ = 0;
87 
88   // `opus_decode_float` decodes to `float` samples with channels interlaced.
89   // Typically these values are in the range of [-1, +1] (always for
90   // `iamf_tools`-encoded data). Values outside of that range will be clipped in
91   // `NormalizedFloatToInt32`.
92   std::vector<float> output_pcm_float(num_samples_per_channel_ * num_channels_);
93 
94   // Transform the data and feed it to the decoder.
95   std::vector<unsigned char> input_data(encoded_frame.size());
96   std::transform(encoded_frame.begin(), encoded_frame.end(), input_data.begin(),
97                  [](uint8_t c) { return static_cast<unsigned char>(c); });
98 
99   const int num_output_samples = opus_decode_float(
100       decoder_, input_data.data(), static_cast<opus_int32>(input_data.size()),
101       output_pcm_float.data(),
102       /*frame_size=*/num_samples_per_channel_,
103       /*decode_fec=*/0);
104   if (num_output_samples < 0) {
105     // When `num_output_samples` is negative, it is a non-OK Opus error code.
106     return OpusErrorCodeToAbslStatus(num_output_samples,
107                                      "Failed to decode Opus frame.");
108   }
109   LOG_FIRST_N(INFO, 3) << "Opus decoded " << num_output_samples
110                        << " samples per channel. With " << num_channels_
111                        << " channels.";
112   // Convert the interleaved data to (time, channel) axes.
113   return ConvertInterleavedToTimeChannel(
114       absl::MakeConstSpan(output_pcm_float)
115           .first(num_output_samples * num_channels_),
116       num_channels_,
117       absl::AnyInvocable<absl::Status(float, int32_t&) const>(
118           NormalizedFloatingPointToInt32<float>),
119       decoded_samples_, num_valid_ticks_);
120 }
121 
122 }  // namespace iamf_tools
123