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/flac_encoder.h"
13
14 #include <cstddef>
15 #include <cstdint>
16 #include <cstring>
17 #include <list>
18 #include <memory>
19 #include <utility>
20 #include <vector>
21
22 #include "absl/functional/any_invocable.h"
23 #include "absl/log/log.h"
24 #include "absl/status/status.h"
25 #include "absl/strings/str_cat.h"
26 #include "absl/synchronization/mutex.h"
27 #include "absl/types/span.h"
28 #include "iamf/cli/audio_frame_with_data.h"
29 #include "iamf/cli/proto/codec_config.pb.h"
30 #include "iamf/common/utils/macros.h"
31 #include "iamf/common/utils/sample_processing_utils.h"
32 #include "iamf/obu/decoder_config/flac_decoder_config.h"
33 #include "include/FLAC/format.h"
34 #include "include/FLAC/ordinals.h"
35 #include "include/FLAC/stream_encoder.h"
36
37 namespace iamf_tools {
38
39 namespace {
40
Configure(const iamf_tools_cli_proto::FlacEncoderMetadata & encoder_metadata,const FlacDecoderConfig & decoder_config,int num_channels,uint32_t num_samples_per_frame,uint32_t output_sample_rate,uint8_t input_pcm_bit_depth_,FLAC__StreamEncoder * const encoder)41 absl::Status Configure(
42 const iamf_tools_cli_proto::FlacEncoderMetadata& encoder_metadata,
43 const FlacDecoderConfig& decoder_config, int num_channels,
44 uint32_t num_samples_per_frame, uint32_t output_sample_rate,
45 uint8_t input_pcm_bit_depth_, FLAC__StreamEncoder* const encoder) {
46 FLAC__bool ok = true;
47 // Configure values based on the associated Codec Config OBU.
48
49 ok &= FLAC__stream_encoder_set_channels(encoder, num_channels);
50
51 ok &= FLAC__stream_encoder_set_bits_per_sample(
52 encoder, static_cast<uint32_t>(input_pcm_bit_depth_));
53
54 ok &= FLAC__stream_encoder_set_sample_rate(encoder, output_sample_rate);
55
56 // IAMF requires a constant block size.
57 ok &= FLAC__stream_encoder_set_blocksize(encoder, num_samples_per_frame);
58
59 uint64_t total_samples_in_stream;
60 RETURN_IF_NOT_OK(
61 decoder_config.GetTotalSamplesInStream(total_samples_in_stream));
62
63 ok &= FLAC__stream_encoder_set_total_samples_estimate(
64 encoder, total_samples_in_stream);
65
66 // Set arguments configured by the user-provided `encoder_metadata_`.
67 ok &= FLAC__stream_encoder_set_compression_level(
68 encoder, encoder_metadata.compression_level());
69
70 ok &= FLAC__stream_encoder_set_verify(encoder, true);
71
72 if (!ok) {
73 return absl::UnknownError("Failed to configure Flac encoder.");
74 }
75
76 return absl::OkStatus();
77 }
78 } // namespace
79
LibFlacWriteCallback(const FLAC__StreamEncoder *,const FLAC__byte buffer[],size_t bytes,unsigned int samples,unsigned int current_frame,void * client_data)80 FLAC__StreamEncoderWriteStatus LibFlacWriteCallback(
81 const FLAC__StreamEncoder* /*encoder*/, const FLAC__byte buffer[],
82 size_t bytes, unsigned int samples, unsigned int current_frame,
83 void* client_data) {
84 const unsigned int kLibFlacMetadataSentinel = 0;
85 if (samples == kLibFlacMetadataSentinel) {
86 // `libflac` uses a value of `0` to indicate this callback is for metadata.
87 LOG(INFO) << "`iamf_tools` currently ignores all additional FLAC metadata.";
88 return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
89 }
90
91 auto flac_encoder = static_cast<FlacEncoder*>(client_data);
92
93 absl::MutexLock lock(&flac_encoder->mutex_);
94
95 auto flac_frame_iter =
96 flac_encoder->frame_index_to_frame_.find(current_frame);
97 if (flac_frame_iter == flac_encoder->frame_index_to_frame_.end()) {
98 LOG(ERROR) << "Failed to find a frame with index " << current_frame
99 << " in Flac encoder. Data may be lost or corrupted.";
100 return FLAC__STREAM_ENCODER_WRITE_STATUS_FATAL_ERROR;
101 }
102
103 // Append to `audio_frame_` and track how many samples it represents. It will
104 // be finalized later to ensure frames are finalized in chronological order.
105 FlacFrame& flac_frame = flac_frame_iter->second;
106 flac_frame.audio_frame_with_data->obu.audio_frame_.insert(
107 flac_frame.audio_frame_with_data->obu.audio_frame_.end(), buffer,
108 buffer + bytes);
109 flac_frame.num_samples += samples;
110
111 if (flac_frame.num_samples == flac_encoder->num_samples_per_frame_) {
112 // A frame has been completed; move to the finalized frames.
113 flac_encoder->finalized_audio_frames_.emplace_back(
114 std::move(*flac_frame_iter->second.audio_frame_with_data));
115
116 // The frame is fully processed and no longer needed.
117 flac_encoder->frame_index_to_frame_.erase(flac_frame_iter);
118 }
119
120 return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
121 }
122
LibFlacMetadataCallback(const FLAC__StreamEncoder *,const FLAC__StreamMetadata * metadata,void * client_data)123 void LibFlacMetadataCallback(const FLAC__StreamEncoder* /*encoder*/,
124 const FLAC__StreamMetadata* metadata,
125 void* client_data) {
126 LOG_FIRST_N(INFO, 1) << "Begin `LibFlacMetadataCallback`.";
127
128 if (metadata->type == FLAC__METADATA_TYPE_STREAMINFO) {
129 LOG(INFO) << "Received `STREAMINFO` metadata.";
130 // Just validate we got the `STREAMINFO` metadata at some point. IAMF
131 // requires some fields to be set constant and different from what will be
132 // returned by `libflac`.
133 auto flac_encoder = static_cast<FlacEncoder*>(client_data);
134
135 absl::MutexLock lock(&flac_encoder->mutex_);
136 flac_encoder->finished_ = true;
137 }
138 }
139
~FlacEncoder()140 FlacEncoder::~FlacEncoder() {
141 FLAC__stream_encoder_delete(encoder_);
142
143 absl::MutexLock lock(&mutex_);
144 if (!frame_index_to_frame_.empty()) {
145 LOG(ERROR) << "Some frames were not fully processed. Maybe `Finalize()` "
146 "was not called.";
147 }
148 }
149
EncodeAudioFrame(int input_bit_depth,const std::vector<std::vector<int32_t>> & samples,std::unique_ptr<AudioFrameWithData> partial_audio_frame_with_data)150 absl::Status FlacEncoder::EncodeAudioFrame(
151 int input_bit_depth, const std::vector<std::vector<int32_t>>& samples,
152 std::unique_ptr<AudioFrameWithData> partial_audio_frame_with_data) {
153 RETURN_IF_NOT_OK(ValidateNotFinalized());
154 RETURN_IF_NOT_OK(ValidateInputSamples(samples));
155 const int num_samples_per_channel = static_cast<int>(num_samples_per_frame_);
156
157 LOG_FIRST_N(INFO, 1) << "num_samples_per_channel: "
158 << num_samples_per_channel;
159 LOG_FIRST_N(INFO, 1) << "num_channels: " << num_channels_;
160
161 // FLAC requires a right-justified sign extended value. Calculate what the
162 // mask is to sign extend a `input_bit_depth`-bit value.
163 uint32_t base_sign_extension_mask = 0;
164 for (int i = 31; i > input_bit_depth - 1; --i) {
165 base_sign_extension_mask |= 1 << i;
166 }
167
168 const absl::AnyInvocable<absl::Status(int32_t, int32_t&) const>
169 kLeftJustifiedToRightJustified =
170 [base_sign_extension_mask, input_bit_depth](int32_t input,
171 int32_t& output) {
172 // Only apply the sign extension mask when the left-justified value
173 // has '1' in the MSB.
174 const uint32_t sign_extension_mask =
175 (input & 0x80000000) ? base_sign_extension_mask : 0;
176 // Shift the input value to be right-justified.
177 output = static_cast<uint32_t>(input) >> (32 - input_bit_depth) |
178 sign_extension_mask;
179 return absl::OkStatus();
180 };
181
182 // Convert input to the array that will be passed to `flac_encode`.
183 std::vector<FLAC__int32> encoder_input_pcm;
184 RETURN_IF_NOT_OK(ConvertTimeChannelToInterleaved(
185 absl::MakeConstSpan(samples), kLeftJustifiedToRightJustified,
186 encoder_input_pcm));
187
188 LOG_FIRST_N(INFO, 1) << "Encoding " << encoder_input_pcm.size() * 4
189 << " bytes representing " << num_samples_per_channel
190 << " x " << num_channels_ << " samples.";
191
192 if (!FLAC__stream_encoder_process_interleaved(
193 encoder_, encoder_input_pcm.data(), num_samples_per_channel)) {
194 return absl::UnknownError("Flac failed to encode.");
195 }
196
197 absl::MutexLock lock(&mutex_);
198
199 // Transfer ownership of the partial audio frame so it can be finalized later.
200 frame_index_to_frame_[next_frame_index_++].audio_frame_with_data =
201 std::move(partial_audio_frame_with_data);
202 return absl::OkStatus();
203 }
204
Finalize()205 absl::Status FlacEncoder::Finalize() {
206 // Signal to `libflac` the encoder is finished.
207 if (!FLAC__stream_encoder_finish(encoder_)) {
208 return absl::UnknownError("Failed to finalize Flac encoder.");
209 }
210
211 return absl::OkStatus();
212 }
213
InitializeEncoder()214 absl::Status FlacEncoder::InitializeEncoder() {
215 // Initialize the encoder.
216 encoder_ = FLAC__stream_encoder_new();
217 if (encoder_ == nullptr) {
218 return absl::UnknownError("Failed to initialize Flac encoder.");
219 }
220
221 // Configure the FLAC encoder based on user input data.
222 RETURN_IF_NOT_OK(Configure(encoder_metadata_, decoder_config_, num_channels_,
223 num_samples_per_frame_, output_sample_rate_,
224 input_pcm_bit_depth_, encoder_));
225
226 // Initialize the FLAC encoder.
227 FLAC__StreamEncoderInitStatus init_status = FLAC__stream_encoder_init_stream(
228 encoder_, LibFlacWriteCallback, /*seek_callback=*/nullptr,
229 /*tell_callback=*/nullptr, LibFlacMetadataCallback,
230 static_cast<void*>(this));
231
232 if (init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
233 return absl::UnknownError(
234 absl::StrCat("Failed to initialize Flac stream: ", init_status));
235 }
236
237 return absl::OkStatus();
238 }
239
240 } // namespace iamf_tools
241