diff --git a/iamf/cli/codec/BUILD b/iamf/cli/codec/BUILD index d13914d..65a89c4 100644 --- a/iamf/cli/codec/BUILD +++ b/iamf/cli/codec/BUILD @@ -193,3 +193,14 @@ cc_library( "@com_google_absl//absl/status", ], ) + +cc_library( + name = "flac_decoder_stream_callbacks", + srcs = ["flac_decoder_stream_callbacks.cc"], + hdrs = ["flac_decoder_stream_callbacks.h"], + deps = [ + ":flac_decoder", + "@com_google_absl//absl/log", + "@flac//:src", + ], +) diff --git a/iamf/cli/codec/flac_decoder.h b/iamf/cli/codec/flac_decoder.h index 859ddef..186be8b 100644 --- a/iamf/cli/codec/flac_decoder.h +++ b/iamf/cli/codec/flac_decoder.h @@ -41,7 +41,7 @@ class FlacDecoder : public DecoderBase { */ absl::Status Initialize() override; - /*!\brief Decodes a Flac audio frame. + /*!\brief Decodes a FLAC audio frame. * * \param encoded_frame Frame to decode. * \param decoded_samples Output decoded frames arranged in (time, sample) @@ -52,7 +52,22 @@ class FlacDecoder : public DecoderBase { const std::vector& encoded_frame, std::vector>& decoded_samples) override; + /*!\brief Sets an encoded FLAC frame in decoder.encoded_frame_. + * + * \param encoded_frame Encoded FLAC frame. + */ + void SetEncodedFrame(const std::vector& encoded_frame) { + encoded_frame_ = encoded_frame; + } + + /*!\brief Retrieves the encoded frame in decoder.encoded_frame_. + * + * \return Vector of encoded FLAC bytes representing a single frame. + */ + std::vector GetEncodedFrame() const { return encoded_frame_; } + private: + std::vector encoded_frame_ = {}; const FlacDecoderConfig decoder_config_; }; diff --git a/iamf/cli/codec/flac_decoder_stream_callbacks.cc b/iamf/cli/codec/flac_decoder_stream_callbacks.cc new file mode 100644 index 0000000..be07c35 --- /dev/null +++ b/iamf/cli/codec/flac_decoder_stream_callbacks.cc @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024, Alliance for Open Media. All rights reserved + * + * This source code is subject to the terms of the BSD 3-Clause Clear License + * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear + * License was not distributed with this source code in the LICENSE file, you + * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the + * Alliance for Open Media Patent License 1.0 was not distributed with this + * source code in the PATENTS file, you can obtain it at + * www.aomedia.org/license/patent. + */ + +#include "iamf/cli/codec/flac_decoder_stream_callbacks.h" + +#include + +#include "absl/log/log.h" +#include "iamf/cli/codec/flac_decoder.h" +#include "include/FLAC/ordinals.h" +#include "include/FLAC/stream_decoder.h" + +namespace iamf_tools { + +FLAC__StreamDecoderReadStatus LibFlacReadCallback( + const FLAC__StreamDecoder* /*decoder*/, FLAC__byte buffer[], size_t* bytes, + void* client_data) { + auto flac_decoder = static_cast(client_data); + const auto& encoded_frame = flac_decoder->GetEncodedFrame(); + if (encoded_frame.empty()) { + // No more data to read. + *bytes = 0; + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + } + if (encoded_frame.size() > *bytes) { + LOG(ERROR) << "Encoded frame size " << encoded_frame.size() + << " is larger than the libflac buffer size " << *bytes; + *bytes = 0; + return FLAC__STREAM_DECODER_READ_STATUS_ABORT; + } + for (int i = 0; i < encoded_frame.size(); ++i) { + buffer[i] = encoded_frame[i]; + } + *bytes = encoded_frame.size(); + return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; +} + +} // namespace iamf_tools diff --git a/iamf/cli/codec/flac_decoder_stream_callbacks.h b/iamf/cli/codec/flac_decoder_stream_callbacks.h new file mode 100644 index 0000000..1419217 --- /dev/null +++ b/iamf/cli/codec/flac_decoder_stream_callbacks.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, Alliance for Open Media. All rights reserved + * + * This source code is subject to the terms of the BSD 3-Clause Clear License + * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear + * License was not distributed with this source code in the LICENSE file, you + * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the + * Alliance for Open Media Patent License 1.0 was not distributed with this + * source code in the PATENTS file, you can obtain it at + * www.aomedia.org/license/patent. + */ + +#ifndef CLI_CODEC_FLAC_DECODER_STREAM_CALLBACKS_H_ +#define CLI_CODEC_FLAC_DECODER_STREAM_CALLBACKS_H_ + +#include + +#include "include/FLAC/ordinals.h" +#include "include/FLAC/stream_decoder.h" + +namespace iamf_tools { +/*!\brief Reads an encoded flac frame into the libflac decoder + * + * This callback function is used whenever the decoder needs more input data. + * + * \param decoder libflac stream decoder + * This parameter is not used in this implementation, but is included to + * override the libflac signature. + * \param buffer Output buffer for the encoded frame. + * \param bytes Maximum size of the buffer; in the case of a successful read, + * this will be set to the actual number of bytes read. + * \param client_data universal pointer, which in this case should point to + * FlacDecoder. + * + * \return A libflac read status indicating whether the read was successful. + */ +FLAC__StreamDecoderReadStatus LibFlacReadCallback( + const FLAC__StreamDecoder* decoder, FLAC__byte buffer[], size_t* bytes, + void* client_data); + +} // namespace iamf_tools + +#endif // CLI_CODEC_FLAC_DECODER_STREAM_CALLBACKS_H_ diff --git a/iamf/cli/codec/tests/BUILD b/iamf/cli/codec/tests/BUILD index e74c6eb..82c3e84 100644 --- a/iamf/cli/codec/tests/BUILD +++ b/iamf/cli/codec/tests/BUILD @@ -116,3 +116,17 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "flac_decoder_stream_callbacks_test", + srcs = ["flac_decoder_stream_callbacks_test.cc"], + deps = [ + "//iamf/cli/codec:flac_decoder", + "//iamf/cli/codec:flac_decoder_stream_callbacks", + "//iamf/obu:codec_config", + "//iamf/obu:obu_header", + "//iamf/obu/decoder_config:flac_decoder_config", + "@com_google_googletest//:gtest_main", + "@flac//:src", + ], +) diff --git a/iamf/cli/codec/tests/flac_decoder_stream_callbacks_test.cc b/iamf/cli/codec/tests/flac_decoder_stream_callbacks_test.cc new file mode 100644 index 0000000..693fec3 --- /dev/null +++ b/iamf/cli/codec/tests/flac_decoder_stream_callbacks_test.cc @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, Alliance for Open Media. All rights reserved + * + * This source code is subject to the terms of the BSD 3-Clause Clear License + * and the Alliance for Open Media Patent License 1.0. If the BSD 3-Clause Clear + * License was not distributed with this source code in the LICENSE file, you + * can obtain it at www.aomedia.org/license/software-license/bsd-3-c-c. If the + * Alliance for Open Media Patent License 1.0 was not distributed with this + * source code in the PATENTS file, you can obtain it at + * www.aomedia.org/license/patent. + */ + +#include "iamf/cli/codec/flac_decoder_stream_callbacks.h" + +#include +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "iamf/cli/codec/flac_decoder.h" +#include "iamf/obu/codec_config.h" +#include "iamf/obu/decoder_config/flac_decoder_config.h" +#include "iamf/obu/obu_header.h" +#include "include/FLAC/ordinals.h" +#include "include/FLAC/stream_decoder.h" + +namespace iamf_tools { +namespace { + +using ::testing::ElementsAreArray; + +class FlacDecoderStreamCallbacksTest : public testing::Test { + public: + FlacDecoderStreamCallbacksTest() + : flac_decoder_(FlacDecoder( + (CodecConfigObu(ObuHeader(), 0, + {.codec_id = CodecConfig::kCodecIdFlac, + .num_samples_per_frame = 1024, + .audio_roll_distance = -1, + .decoder_config = FlacDecoderConfig{}})), + /*num_channels=*/2)) {} + + protected: + FlacDecoder flac_decoder_; +}; + +TEST_F(FlacDecoderStreamCallbacksTest, ReadCallbackEmptyFrame) { + FLAC__byte buffer[1024]; + size_t bytes = 1024; + auto status = LibFlacReadCallback(/*stream_decoder=*/nullptr, buffer, &bytes, + &flac_decoder_); + EXPECT_EQ(status, FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM); + EXPECT_EQ(bytes, 0); +} + +TEST_F(FlacDecoderStreamCallbacksTest, ReadCallbackFrameTooLarge) { + FLAC__byte buffer[1024]; + size_t bytes = 1024; + flac_decoder_.SetEncodedFrame(std::vector(1025)); + auto status = LibFlacReadCallback(/*stream_decoder=*/nullptr, buffer, &bytes, + &flac_decoder_); + EXPECT_EQ(status, FLAC__STREAM_DECODER_READ_STATUS_ABORT); + EXPECT_EQ(bytes, 0); +} + +TEST_F(FlacDecoderStreamCallbacksTest, ReadCallbackSuccess) { + FLAC__byte buffer[1024]; + size_t bytes = 1028; + const std::vector encoded_frame(1024, 1); + flac_decoder_.SetEncodedFrame(encoded_frame); + auto status = LibFlacReadCallback(/*stream_decoder=*/nullptr, buffer, &bytes, + &flac_decoder_); + EXPECT_EQ(status, FLAC__STREAM_DECODER_READ_STATUS_CONTINUE); + EXPECT_EQ(bytes, 1024); + EXPECT_THAT(buffer, ElementsAreArray(encoded_frame)); +} + +} // namespace +} // namespace iamf_tools