• 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/aac_encoder.h"
13 
14 #include <cstddef>
15 #include <cstdint>
16 #include <memory>
17 #include <utility>
18 #include <vector>
19 
20 #include "absl/log/log.h"
21 #include "absl/status/status.h"
22 #include "absl/strings/str_cat.h"
23 #include "absl/strings/string_view.h"
24 #include "absl/synchronization/mutex.h"
25 #include "iamf/cli/audio_frame_with_data.h"
26 #include "iamf/cli/codec/aac_utils.h"
27 #include "iamf/cli/proto/codec_config.pb.h"
28 #include "iamf/common/utils/macros.h"
29 #include "iamf/common/utils/numeric_utils.h"
30 #include "iamf/common/utils/sample_processing_utils.h"
31 #include "iamf/common/utils/validation_utils.h"
32 #include "libAACenc/include/aacenc_lib.h"
33 #include "libSYS/include/FDK_audio.h"
34 #include "libSYS/include/machine_type.h"
35 
36 namespace iamf_tools {
37 
38 namespace {
39 
40 // Converts an AACENC_ERROR to an absl::Status.
AacEncErrorToAbslStatus(AACENC_ERROR aac_error_code,const absl::string_view error_message)41 absl::Status AacEncErrorToAbslStatus(AACENC_ERROR aac_error_code,
42                                      const absl::string_view error_message) {
43   absl::StatusCode status_code;
44   switch (aac_error_code) {
45     case AACENC_OK:
46       return absl::OkStatus();
47     case AACENC_INVALID_HANDLE:
48       status_code = absl::StatusCode::kInvalidArgument;
49       break;
50     case AACENC_MEMORY_ERROR:
51       status_code = absl::StatusCode::kResourceExhausted;
52       break;
53     case AACENC_UNSUPPORTED_PARAMETER:
54       status_code = absl::StatusCode::kInvalidArgument;
55       break;
56     case AACENC_INVALID_CONFIG:
57       status_code = absl::StatusCode::kFailedPrecondition;
58       break;
59     case AACENC_INIT_ERROR:
60     case AACENC_INIT_AAC_ERROR:
61     case AACENC_INIT_SBR_ERROR:
62     case AACENC_INIT_TP_ERROR:
63     case AACENC_INIT_META_ERROR:
64     case AACENC_INIT_MPS_ERROR:
65       status_code = absl::StatusCode::kInternal;
66       break;
67     case AACENC_ENCODE_EOF:
68       status_code = absl::StatusCode::kOutOfRange;
69       break;
70     case AACENC_ENCODE_ERROR:
71     default:
72       status_code = absl::StatusCode::kUnknown;
73       break;
74   }
75 
76   return absl::Status(
77       status_code,
78       absl::StrCat(error_message, " AACENC_ERROR= ", aac_error_code));
79 }
80 
ConfigureAacEncoder(const iamf_tools_cli_proto::AacEncoderMetadata & encoder_metadata,int num_channels,uint32_t output_sample_rate,AACENCODER * const encoder)81 absl::Status ConfigureAacEncoder(
82     const iamf_tools_cli_proto::AacEncoderMetadata& encoder_metadata,
83     int num_channels, uint32_t output_sample_rate, AACENCODER* const encoder) {
84   // IAMF requires metadata is not embedded in the stream.
85   RETURN_IF_NOT_OK(AacEncErrorToAbslStatus(
86       aacEncoder_SetParam(encoder, AACENC_METADATA_MODE, 0),
87       "Failed to configure encoder metadata mode."));
88 
89   RETURN_IF_NOT_OK(AacEncErrorToAbslStatus(
90       aacEncoder_SetParam(encoder, AACENC_TRANSMUX, GetAacTransportationType()),
91       "Failed to configure encoder transport type."));
92 
93   // IAMF only supports AAC-LC.
94   RETURN_IF_NOT_OK(AacEncErrorToAbslStatus(
95       aacEncoder_SetParam(encoder, AACENC_AOT, AOT_AAC_LC),
96       "Failed to configure encoder audio object type."));
97 
98   // Configure values based on the associated Codec Config OBU.
99   RETURN_IF_NOT_OK(AacEncErrorToAbslStatus(
100       aacEncoder_SetParam(encoder, AACENC_SAMPLERATE,
101                           static_cast<UINT>(output_sample_rate)),
102       "Failed to configure encoder sample rate."));
103 
104   CHANNEL_MODE aac_channel_mode;
105   switch (num_channels) {
106     case 1:
107       aac_channel_mode = MODE_1;
108 
109       break;
110     case 2:
111       aac_channel_mode = MODE_2;
112       break;
113     default:
114       return absl::InvalidArgumentError(
115           absl::StrCat("IAMF requires AAC to be used with 1 or 2 channels. Got "
116                        "num_channels= ",
117                        num_channels));
118   }
119   RETURN_IF_NOT_OK(AacEncErrorToAbslStatus(
120       aacEncoder_SetParam(encoder, AACENC_CHANNELMODE, aac_channel_mode),
121       absl::StrCat("Failed to configure encoder channel mode= ",
122                    aac_channel_mode)));
123 
124   // Let AACENC_BITRATE be configured automatically.
125 
126   // Set some arguments configured by the user-provided `encoder_metadata_`.
127   RETURN_IF_NOT_OK(AacEncErrorToAbslStatus(
128       aacEncoder_SetParam(encoder, AACENC_AFTERBURNER,
129                           encoder_metadata.enable_afterburner() ? 1 : 0),
130       absl::StrCat(
131           "Failed to configure encoder afterburner enable_afterburner= ",
132           encoder_metadata.enable_afterburner())));
133 
134   RETURN_IF_NOT_OK(AacEncErrorToAbslStatus(
135       aacEncoder_SetParam(encoder, AACENC_BITRATEMODE,
136                           encoder_metadata.bitrate_mode()),
137       absl::StrCat("Failed to configure encoder bitrate mode= ",
138                    encoder_metadata.bitrate_mode())));
139 
140   RETURN_IF_NOT_OK(AacEncErrorToAbslStatus(
141       aacEncoder_SetParam(encoder, AACENC_SIGNALING_MODE,
142                           encoder_metadata.signaling_mode()),
143       absl::StrCat("Failed to configure encoder signaling mode= ",
144                    encoder_metadata.signaling_mode())));
145 
146   return absl::OkStatus();
147 }
148 
ValidateEncoderInfo(int num_channels,uint32_t num_samples_per_frame,AACENCODER * const encoder)149 absl::Status ValidateEncoderInfo(int num_channels,
150                                  uint32_t num_samples_per_frame,
151                                  AACENCODER* const encoder) {
152   // Validate the configuration is consistent with the associated Codec Config
153   // OBU.
154   AACENC_InfoStruct enc_info;
155   RETURN_IF_NOT_OK(AacEncErrorToAbslStatus(aacEncInfo(encoder, &enc_info),
156                                            "Failed to get encoder info."));
157 
158   RETURN_IF_NOT_OK(
159       ValidateEqual(num_channels, static_cast<int>(enc_info.inputChannels),
160                     "user requested vs libFDK required `num_channels`"));
161   RETURN_IF_NOT_OK(ValidateEqual(
162       num_samples_per_frame, static_cast<uint32_t>(enc_info.frameLength),
163       "user requested vs libFDK required `num_samples_per_frame`"));
164 
165   return absl::OkStatus();
166 }
167 
168 }  // namespace
169 
InitializeEncoder()170 absl::Status AacEncoder::InitializeEncoder() {
171   if (encoder_) {
172     return absl::InvalidArgumentError(
173         "Expected `encoder_` to not be initialized yet.");
174   }
175 
176   // Open the encoder.
177   RETURN_IF_NOT_OK(
178       AacEncErrorToAbslStatus(aacEncOpen(&encoder_, 0, num_channels_),
179                               "Failed to initialize AAC encoder."));
180 
181   // Configure the encoder.
182   RETURN_IF_NOT_OK(ConfigureAacEncoder(encoder_metadata_, num_channels_,
183                                        output_sample_rate_, encoder_));
184 
185   // Call `aacEncEncode` with `nullptr` arguments to initialize the encoder.
186   RETURN_IF_NOT_OK(AacEncErrorToAbslStatus(
187       aacEncEncode(encoder_, nullptr, nullptr, nullptr, nullptr),
188       "Failed on call to `aacEncEncode`."));
189 
190   // Validate the configuration matches expected results.
191   RETURN_IF_NOT_OK(
192       ValidateEncoderInfo(num_channels_, num_samples_per_frame_, encoder_));
193 
194   return absl::OkStatus();
195 }
196 
~AacEncoder()197 AacEncoder::~AacEncoder() { aacEncClose(&encoder_); }
198 
EncodeAudioFrame(int input_bit_depth,const std::vector<std::vector<int32_t>> & samples,std::unique_ptr<AudioFrameWithData> partial_audio_frame_with_data)199 absl::Status AacEncoder::EncodeAudioFrame(
200     int input_bit_depth, const std::vector<std::vector<int32_t>>& samples,
201     std::unique_ptr<AudioFrameWithData> partial_audio_frame_with_data) {
202   if (!encoder_) {
203     LOG(ERROR) << "Expected `encoder_` to be initialized.";
204   }
205   RETURN_IF_NOT_OK(ValidateNotFinalized());
206   RETURN_IF_NOT_OK(ValidateInputSamples(samples));
207   const int num_samples_per_channel = static_cast<int>(num_samples_per_frame_);
208 
209   AACENC_InfoStruct enc_info;
210   RETURN_IF_NOT_OK(AacEncErrorToAbslStatus(aacEncInfo(encoder_, &enc_info),
211                                            "Failed to get encoder info."));
212 
213   // Convert input to the array that will be passed to `aacEncEncode`.
214   if (input_bit_depth != GetFdkAacBitDepth()) {
215     auto error_message =
216         absl::StrCat("Expected AAC to be ", GetFdkAacBitDepth(), " bits, got ",
217                      input_bit_depth);
218     return absl::InvalidArgumentError(error_message);
219   }
220 
221   // `fdk_aac` requires the native system endianness as input.
222   const bool big_endian = IsNativeBigEndian();
223   std::vector<INT_PCM> encoder_input_pcm(
224       num_samples_per_channel * num_channels_, 0);
225   size_t write_position = 0;
226   for (int t = 0; t < samples.size(); t++) {
227     for (int c = 0; c < samples[0].size(); ++c) {
228       // Convert all frames to INT_PCM samples for input for `fdk_aac` (usually
229       // 16-bit).
230       RETURN_IF_NOT_OK(WritePcmSample(
231           static_cast<uint32_t>(samples[t][c]), input_bit_depth, big_endian,
232           reinterpret_cast<uint8_t*>(encoder_input_pcm.data()),
233           write_position));
234     }
235   }
236 
237   // The `fdk_aac` interface supports multiple input buffers. Although IAMF only
238   // uses one buffer without metadata or ancillary data.
239   void* in_buffers[1] = {encoder_input_pcm.data()};
240   INT in_buffer_identifiers[1] = {IN_AUDIO_DATA};
241   INT in_buffer_sizes[1] = {
242       static_cast<INT>(encoder_input_pcm.size() * GetFdkAacBytesPerSample())};
243   INT in_buffer_element_sizes[1] = {GetFdkAacBytesPerSample()};
244   AACENC_BufDesc inBufDesc = {.numBufs = 1,
245                               .bufs = in_buffers,
246                               .bufferIdentifiers = in_buffer_identifiers,
247                               .bufSizes = in_buffer_sizes,
248                               .bufElSizes = in_buffer_element_sizes};
249   AACENC_InArgs in_args = {
250       .numInSamples = num_samples_per_channel * num_channels_,
251       .numAncBytes = 0};
252 
253   // Resize the output buffer to support the worst case size.
254   auto& audio_frame = partial_audio_frame_with_data->obu.audio_frame_;
255   audio_frame.resize(enc_info.maxOutBufBytes, 0);
256 
257   // The `fdk_aac` interface supports multiple input buffers. Although IAMF only
258   // uses one buffer without metadata or ancillary data.
259   void* out_bufs[1] = {audio_frame.data()};
260   INT out_buffer_identifiers[1] = {OUT_BITSTREAM_DATA};
261   INT out_buffer_sizes[1] = {
262       static_cast<INT>(audio_frame.size() * sizeof(uint8_t))};
263   INT out_buffer_element_sizes[1] = {sizeof(uint8_t)};
264   AACENC_BufDesc outBufDesc = {.numBufs = 1,
265                                .bufs = out_bufs,
266                                .bufferIdentifiers = out_buffer_identifiers,
267                                .bufSizes = out_buffer_sizes,
268                                .bufElSizes = out_buffer_element_sizes};
269 
270   // Encode the frame.
271   AACENC_OutArgs out_args;
272   // This implementation expects `fdk_aac` to return an entire frame and no
273   // error code.
274   RETURN_IF_NOT_OK(AacEncErrorToAbslStatus(
275       aacEncEncode(encoder_, &inBufDesc, &outBufDesc, &in_args, &out_args),
276       "Failed on call to `aacEncEncode`."));
277 
278   if (num_samples_per_channel * num_channels_ != out_args.numInSamples) {
279     return absl::UnknownError("Failed to encode an entire frame.");
280   }
281 
282   // Resize the buffer to the actual size and finalize it.
283   audio_frame.resize(out_args.numOutBytes);
284   absl::MutexLock lock(&mutex_);
285   finalized_audio_frames_.emplace_back(
286       std::move(*partial_audio_frame_with_data));
287 
288   LOG_FIRST_N(INFO, 3) << "Encoded " << num_samples_per_channel << " samples * "
289                        << num_channels_ << " channels using "
290                        << out_args.numOutBytes << " bytes";
291   return absl::OkStatus();
292 }
293 
SetNumberOfSamplesToDelayAtStart(bool)294 absl::Status AacEncoder::SetNumberOfSamplesToDelayAtStart(
295     bool /*validate_codec_delay*/) {
296   if (!encoder_) {
297     LOG(ERROR) << "Expected `encoder_` to be initialized.";
298   }
299 
300   // Validate the configuration.
301   AACENC_InfoStruct enc_info;
302   RETURN_IF_NOT_OK(AacEncErrorToAbslStatus(aacEncInfo(encoder_, &enc_info),
303                                            "Failed to get encoder info."));
304 
305   // Set the number of samples the decoder must ignore. For AAC this appears
306   // to be implementation specific. The implementation of AAC-LC in `fdk_aac`
307   // seems to usually make this 2048 samples.
308   required_samples_to_delay_at_start_ = enc_info.nDelayCore;
309   return absl::OkStatus();
310 }
311 
312 }  // namespace iamf_tools
313