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