diff options
author | qinxialei <xialeiqin@gmail.com> | 2020-10-29 11:26:59 +0800 |
---|---|---|
committer | qinxialei <xialeiqin@gmail.com> | 2020-10-29 11:26:59 +0800 |
commit | e8d277081293b6fb2a5d469616baaa7a06f52496 (patch) | |
tree | 1179bb07d3927d1837d4a90bd81b2034c4c696a9 /examples | |
download | libgav1-e8d277081293b6fb2a5d469616baaa7a06f52496.tar.gz libgav1-e8d277081293b6fb2a5d469616baaa7a06f52496.tar.bz2 libgav1-e8d277081293b6fb2a5d469616baaa7a06f52496.zip |
Import Upstream version 0.16.0
Diffstat (limited to 'examples')
-rw-r--r-- | examples/file_reader.cc | 186 | ||||
-rw-r--r-- | examples/file_reader.h | 100 | ||||
-rw-r--r-- | examples/file_reader_constants.cc | 23 | ||||
-rw-r--r-- | examples/file_reader_constants.h | 39 | ||||
-rw-r--r-- | examples/file_reader_factory.cc | 51 | ||||
-rw-r--r-- | examples/file_reader_factory.h | 51 | ||||
-rw-r--r-- | examples/file_reader_interface.h | 63 | ||||
-rw-r--r-- | examples/file_writer.cc | 183 | ||||
-rw-r--r-- | examples/file_writer.h | 102 | ||||
-rw-r--r-- | examples/gav1_decode.cc | 452 | ||||
-rw-r--r-- | examples/gav1_decode_cv_pixel_buffer_pool.cc | 278 | ||||
-rw-r--r-- | examples/gav1_decode_cv_pixel_buffer_pool.h | 73 | ||||
-rw-r--r-- | examples/ivf_parser.cc | 96 | ||||
-rw-r--r-- | examples/ivf_parser.h | 57 | ||||
-rw-r--r-- | examples/libgav1_examples.cmake | 63 | ||||
-rw-r--r-- | examples/logging.h | 65 |
16 files changed, 1882 insertions, 0 deletions
diff --git a/examples/file_reader.cc b/examples/file_reader.cc new file mode 100644 index 0000000..b096722 --- /dev/null +++ b/examples/file_reader.cc @@ -0,0 +1,186 @@ +// Copyright 2019 The libgav1 Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "examples/file_reader.h" + +#include <algorithm> +#include <cstdint> +#include <cstdio> +#include <new> +#include <string> +#include <vector> + +#if defined(_WIN32) +#include <fcntl.h> +#include <io.h> +#endif + +#include "examples/file_reader_constants.h" +#include "examples/file_reader_factory.h" +#include "examples/file_reader_interface.h" +#include "examples/ivf_parser.h" +#include "examples/logging.h" + +namespace libgav1 { +namespace { + +FILE* SetBinaryMode(FILE* stream) { +#if defined(_WIN32) + _setmode(_fileno(stream), _O_BINARY); +#endif + return stream; +} + +} // namespace + +bool FileReader::registered_in_factory_ = + FileReaderFactory::RegisterReader(FileReader::Open); + +FileReader::~FileReader() { + if (owns_file_) fclose(file_); +} + +std::unique_ptr<FileReaderInterface> FileReader::Open( + const std::string& file_name, const bool error_tolerant) { + if (file_name.empty()) return nullptr; + + FILE* raw_file_ptr; + + bool owns_file = true; + if (file_name == "-") { + raw_file_ptr = SetBinaryMode(stdin); + owns_file = false; // stdin is owned by the Standard C Library. + } else { + raw_file_ptr = fopen(file_name.c_str(), "rb"); + } + + if (raw_file_ptr == nullptr) { + return nullptr; + } + + std::unique_ptr<FileReader> file( + new (std::nothrow) FileReader(raw_file_ptr, owns_file, error_tolerant)); + if (file == nullptr) { + LIBGAV1_EXAMPLES_LOG_ERROR("Out of memory"); + if (owns_file) fclose(raw_file_ptr); + return nullptr; + } + + if (!file->ReadIvfFileHeader()) { + LIBGAV1_EXAMPLES_LOG_ERROR("Unsupported file type"); + return nullptr; + } + + return file; +} + +// IVF Frame Header format, from https://wiki.multimedia.cx/index.php/IVF +// bytes 0-3 size of frame in bytes (not including the 12-byte header) +// bytes 4-11 64-bit presentation timestamp +// bytes 12.. frame data +bool FileReader::ReadTemporalUnit(std::vector<uint8_t>* const tu_data, + int64_t* const timestamp) { + if (tu_data == nullptr) return false; + tu_data->clear(); + + uint8_t header_buffer[kIvfFrameHeaderSize]; + const size_t num_read = fread(header_buffer, 1, kIvfFrameHeaderSize, file_); + + if (IsEndOfFile()) { + if (num_read != 0) { + LIBGAV1_EXAMPLES_LOG_ERROR( + "Cannot read IVF frame header: Not enough data available"); + return false; + } + + return true; + } + + IvfFrameHeader ivf_frame_header; + if (!ParseIvfFrameHeader(header_buffer, &ivf_frame_header)) { + LIBGAV1_EXAMPLES_LOG_ERROR("Could not parse IVF frame header"); + if (error_tolerant_) { + ivf_frame_header.frame_size = + std::min(ivf_frame_header.frame_size, size_t{kMaxTemporalUnitSize}); + } else { + return false; + } + } + + if (timestamp != nullptr) *timestamp = ivf_frame_header.timestamp; + + tu_data->resize(ivf_frame_header.frame_size); + const size_t size_read = + fread(tu_data->data(), 1, ivf_frame_header.frame_size, file_); + if (size_read != ivf_frame_header.frame_size) { + LIBGAV1_EXAMPLES_LOG_ERROR( + "Unexpected EOF or I/O error reading frame data"); + if (error_tolerant_) { + tu_data->resize(size_read); + } else { + return false; + } + } + return true; +} + +// Attempt to read an IVF file header. Returns true for success, and false for +// failure. +// +// IVF File Header format, from https://wiki.multimedia.cx/index.php/IVF +// bytes 0-3 signature: 'DKIF' +// bytes 4-5 version (should be 0) +// bytes 6-7 length of header in bytes +// bytes 8-11 codec FourCC (e.g., 'VP80') +// bytes 12-13 width in pixels +// bytes 14-15 height in pixels +// bytes 16-19 frame rate +// bytes 20-23 time scale +// bytes 24-27 number of frames in file +// bytes 28-31 unused +// +// Note: The rate and scale fields correspond to the numerator and denominator +// of frame rate (fps) or time base (the reciprocal of frame rate) as follows: +// +// bytes 16-19 frame rate timebase.den framerate.numerator +// bytes 20-23 time scale timebase.num framerate.denominator +bool FileReader::ReadIvfFileHeader() { + uint8_t header_buffer[kIvfFileHeaderSize]; + const size_t num_read = fread(header_buffer, 1, kIvfFileHeaderSize, file_); + if (num_read != kIvfFileHeaderSize) { + LIBGAV1_EXAMPLES_LOG_ERROR( + "Cannot read IVF header: Not enough data available"); + return false; + } + + IvfFileHeader ivf_file_header; + if (!ParseIvfFileHeader(header_buffer, &ivf_file_header)) { + LIBGAV1_EXAMPLES_LOG_ERROR("Could not parse IVF file header"); + if (error_tolerant_) { + ivf_file_header = {}; + } else { + return false; + } + } + + width_ = ivf_file_header.width; + height_ = ivf_file_header.height; + frame_rate_ = ivf_file_header.frame_rate_numerator; + time_scale_ = ivf_file_header.frame_rate_denominator; + type_ = kFileTypeIvf; + + return true; +} + +} // namespace libgav1 diff --git a/examples/file_reader.h b/examples/file_reader.h new file mode 100644 index 0000000..c342a20 --- /dev/null +++ b/examples/file_reader.h @@ -0,0 +1,100 @@ +/* + * Copyright 2019 The libgav1 Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBGAV1_EXAMPLES_FILE_READER_H_ +#define LIBGAV1_EXAMPLES_FILE_READER_H_ + +#include <cstddef> +#include <cstdint> +#include <cstdio> +#include <memory> +#include <string> +#include <vector> + +#include "examples/file_reader_interface.h" + +namespace libgav1 { + +// Temporal Unit based file reader class. Currently supports only IVF files. +class FileReader : public FileReaderInterface { + public: + enum FileType { + kFileTypeUnknown, + kFileTypeIvf, + }; + + // Creates and returns a FileReader that reads from |file_name|. + // If |error_tolerant| is true format and read errors are ignored, + // ReadTemporalUnit() may return truncated data. + // Returns nullptr when the file does not exist, cannot be read, or is not an + // IVF file. + static std::unique_ptr<FileReaderInterface> Open(const std::string& file_name, + bool error_tolerant = false); + + FileReader() = delete; + FileReader(const FileReader&) = delete; + FileReader& operator=(const FileReader&) = delete; + + // Closes |file_|. + ~FileReader() override; + + // Reads a temporal unit from |file_| and writes the data to |tu_data|. + // Returns true when: + // - A temporal unit is read successfully, or + // - At end of file. + // When ReadTemporalUnit() is called at the end of the file, it will return + // true without writing any data to |tu_data|. + // + // The |timestamp| pointer is optional: callers not interested in timestamps + // can pass nullptr. When |timestamp| is not a nullptr, this function returns + // the presentation timestamp from the IVF frame header. + /*LIBGAV1_MUST_USE_RESULT*/ bool ReadTemporalUnit( + std::vector<uint8_t>* tu_data, int64_t* timestamp) override; + + /*LIBGAV1_MUST_USE_RESULT*/ bool IsEndOfFile() const override { + return feof(file_) != 0; + } + + // The values returned by these accessors are strictly informative. No + // validation is performed when they are read from the IVF file header. + size_t width() const override { return width_; } + size_t height() const override { return height_; } + size_t frame_rate() const override { return frame_rate_; } + size_t time_scale() const override { return time_scale_; } + + private: + FileReader(FILE* file, bool owns_file, bool error_tolerant) + : file_(file), owns_file_(owns_file), error_tolerant_(error_tolerant) {} + + bool ReadIvfFileHeader(); + + FILE* file_ = nullptr; + size_t width_ = 0; + size_t height_ = 0; + size_t frame_rate_ = 0; + size_t time_scale_ = 0; + FileType type_ = kFileTypeUnknown; + // True if this object owns file_ and is responsible for closing it when + // done. + const bool owns_file_; + const bool error_tolerant_; + + static bool registered_in_factory_; +}; + +} // namespace libgav1 + +#endif // LIBGAV1_EXAMPLES_FILE_READER_H_ diff --git a/examples/file_reader_constants.cc b/examples/file_reader_constants.cc new file mode 100644 index 0000000..8439071 --- /dev/null +++ b/examples/file_reader_constants.cc @@ -0,0 +1,23 @@ +// Copyright 2019 The libgav1 Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "examples/file_reader_constants.h" + +namespace libgav1 { + +const char kIvfSignature[4] = {'D', 'K', 'I', 'F'}; +const char kAv1FourCcUpper[4] = {'A', 'V', '0', '1'}; +const char kAv1FourCcLower[4] = {'a', 'v', '0', '1'}; + +} // namespace libgav1 diff --git a/examples/file_reader_constants.h b/examples/file_reader_constants.h new file mode 100644 index 0000000..00922b4 --- /dev/null +++ b/examples/file_reader_constants.h @@ -0,0 +1,39 @@ +/* + * Copyright 2019 The libgav1 Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBGAV1_EXAMPLES_FILE_READER_CONSTANTS_H_ +#define LIBGAV1_EXAMPLES_FILE_READER_CONSTANTS_H_ + +namespace libgav1 { + +enum { + kIvfHeaderVersion = 0, + kIvfFrameHeaderSize = 12, + kIvfFileHeaderSize = 32, +#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION + kMaxTemporalUnitSize = 512 * 1024, +#else + kMaxTemporalUnitSize = 256 * 1024 * 1024, +#endif +}; + +extern const char kIvfSignature[4]; +extern const char kAv1FourCcUpper[4]; +extern const char kAv1FourCcLower[4]; + +} // namespace libgav1 + +#endif // LIBGAV1_EXAMPLES_FILE_READER_CONSTANTS_H_ diff --git a/examples/file_reader_factory.cc b/examples/file_reader_factory.cc new file mode 100644 index 0000000..d5260eb --- /dev/null +++ b/examples/file_reader_factory.cc @@ -0,0 +1,51 @@ +// Copyright 2019 The libgav1 Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "examples/file_reader_factory.h" + +#include <new> + +#include "examples/logging.h" + +namespace libgav1 { +namespace { + +std::vector<FileReaderFactory::OpenFunction>* GetFileReaderOpenFunctions() { + static auto* open_functions = + new (std::nothrow) std::vector<FileReaderFactory::OpenFunction>(); + return open_functions; +} + +} // namespace + +bool FileReaderFactory::RegisterReader(OpenFunction open_function) { + if (open_function == nullptr) return false; + auto* open_functions = GetFileReaderOpenFunctions(); + const size_t num_readers = open_functions->size(); + open_functions->push_back(open_function); + return open_functions->size() == num_readers + 1; +} + +std::unique_ptr<FileReaderInterface> FileReaderFactory::OpenReader( + const std::string& file_name, const bool error_tolerant /*= false*/) { + for (auto* open_function : *GetFileReaderOpenFunctions()) { + auto reader = open_function(file_name, error_tolerant); + if (reader == nullptr) continue; + return reader; + } + LIBGAV1_EXAMPLES_LOG_ERROR("No file reader able to open input"); + return nullptr; +} + +} // namespace libgav1 diff --git a/examples/file_reader_factory.h b/examples/file_reader_factory.h new file mode 100644 index 0000000..0f53484 --- /dev/null +++ b/examples/file_reader_factory.h @@ -0,0 +1,51 @@ +/* + * Copyright 2019 The libgav1 Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBGAV1_EXAMPLES_FILE_READER_FACTORY_H_ +#define LIBGAV1_EXAMPLES_FILE_READER_FACTORY_H_ + +#include <memory> +#include <string> + +#include "examples/file_reader_interface.h" + +namespace libgav1 { + +class FileReaderFactory { + public: + using OpenFunction = std::unique_ptr<FileReaderInterface> (*)( + const std::string& file_name, bool error_tolerant); + + FileReaderFactory() = delete; + FileReaderFactory(const FileReaderFactory&) = delete; + FileReaderFactory& operator=(const FileReaderFactory&) = delete; + ~FileReaderFactory() = default; + + // Registers the OpenFunction for a FileReaderInterface and returns true when + // registration succeeds. + static bool RegisterReader(OpenFunction open_function); + + // Passes |file_name| to each OpenFunction until one succeeds. Returns nullptr + // when no reader is found for |file_name|. Otherwise a FileReaderInterface is + // returned. If |error_tolerant| is true and the reader supports it, some + // format and read errors may be ignored and partial data returned. + static std::unique_ptr<FileReaderInterface> OpenReader( + const std::string& file_name, bool error_tolerant = false); +}; + +} // namespace libgav1 + +#endif // LIBGAV1_EXAMPLES_FILE_READER_FACTORY_H_ diff --git a/examples/file_reader_interface.h b/examples/file_reader_interface.h new file mode 100644 index 0000000..d8f7030 --- /dev/null +++ b/examples/file_reader_interface.h @@ -0,0 +1,63 @@ +/* + * Copyright 2019 The libgav1 Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBGAV1_EXAMPLES_FILE_READER_INTERFACE_H_ +#define LIBGAV1_EXAMPLES_FILE_READER_INTERFACE_H_ + +#include <cstddef> +#include <cstdint> +#include <vector> + +namespace libgav1 { + +class FileReaderInterface { + public: + FileReaderInterface() = default; + FileReaderInterface(const FileReaderInterface&) = delete; + FileReaderInterface& operator=(const FileReaderInterface&) = delete; + + FileReaderInterface(FileReaderInterface&&) = default; + FileReaderInterface& operator=(FileReaderInterface&&) = default; + + // Closes the file. + virtual ~FileReaderInterface() = default; + + // Reads a temporal unit from the file and writes the data to |tu_data|. + // Returns true when: + // - A temporal unit is read successfully, or + // - At end of file. + // When ReadTemporalUnit() is called at the end of the file, it will return + // true without writing any data to |tu_data|. + // + // The |timestamp| pointer is optional: callers not interested in timestamps + // can pass nullptr. When |timestamp| is not a nullptr, this function returns + // the presentation timestamp of the temporal unit. + /*LIBGAV1_MUST_USE_RESULT*/ virtual bool ReadTemporalUnit( + std::vector<uint8_t>* tu_data, int64_t* timestamp) = 0; + + /*LIBGAV1_MUST_USE_RESULT*/ virtual bool IsEndOfFile() const = 0; + + // The values returned by these accessors are strictly informative. No + // validation is performed when they are read from file. + virtual size_t width() const = 0; + virtual size_t height() const = 0; + virtual size_t frame_rate() const = 0; + virtual size_t time_scale() const = 0; +}; + +} // namespace libgav1 + +#endif // LIBGAV1_EXAMPLES_FILE_READER_INTERFACE_H_ diff --git a/examples/file_writer.cc b/examples/file_writer.cc new file mode 100644 index 0000000..54afe14 --- /dev/null +++ b/examples/file_writer.cc @@ -0,0 +1,183 @@ +// Copyright 2019 The libgav1 Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "examples/file_writer.h" + +#include <cerrno> +#include <cstdio> +#include <cstring> +#include <new> +#include <string> + +#if defined(_WIN32) +#include <fcntl.h> +#include <io.h> +#endif + +#include "examples/logging.h" + +namespace libgav1 { +namespace { + +FILE* SetBinaryMode(FILE* stream) { +#if defined(_WIN32) + _setmode(_fileno(stream), _O_BINARY); +#endif + return stream; +} + +std::string GetY4mColorSpaceString( + const FileWriter::Y4mParameters& y4m_parameters) { + std::string color_space_string; + switch (y4m_parameters.image_format) { + case kImageFormatMonochrome400: + color_space_string = "mono"; + break; + case kImageFormatYuv420: + if (y4m_parameters.bitdepth == 8) { + if (y4m_parameters.chroma_sample_position == + kChromaSamplePositionVertical) { + color_space_string = "420mpeg2"; + } else if (y4m_parameters.chroma_sample_position == + kChromaSamplePositionColocated) { + color_space_string = "420"; + } else { + color_space_string = "420jpeg"; + } + } else { + color_space_string = "420"; + } + break; + case kImageFormatYuv422: + color_space_string = "422"; + break; + case kImageFormatYuv444: + color_space_string = "444"; + break; + } + + if (y4m_parameters.bitdepth > 8) { + const bool monochrome = + y4m_parameters.image_format == kImageFormatMonochrome400; + if (!monochrome) color_space_string += "p"; + color_space_string += std::to_string(y4m_parameters.bitdepth); + } + + return color_space_string; +} + +} // namespace + +FileWriter::~FileWriter() { fclose(file_); } + +std::unique_ptr<FileWriter> FileWriter::Open( + const std::string& file_name, FileType file_type, + const Y4mParameters* const y4m_parameters) { + if (file_name.empty() || + (file_type == kFileTypeY4m && y4m_parameters == nullptr) || + (file_type != kFileTypeRaw && file_type != kFileTypeY4m)) { + LIBGAV1_EXAMPLES_LOG_ERROR("Invalid parameters"); + return nullptr; + } + + FILE* raw_file_ptr; + + if (file_name == "-") { + raw_file_ptr = SetBinaryMode(stdout); + } else { + raw_file_ptr = fopen(file_name.c_str(), "wb"); + } + + if (raw_file_ptr == nullptr) { + LIBGAV1_EXAMPLES_LOG_ERROR("Unable to open output file"); + return nullptr; + } + + std::unique_ptr<FileWriter> file(new (std::nothrow) FileWriter(raw_file_ptr)); + if (file == nullptr) { + LIBGAV1_EXAMPLES_LOG_ERROR("Out of memory"); + fclose(raw_file_ptr); + return nullptr; + } + + if (file_type == kFileTypeY4m && !file->WriteY4mFileHeader(*y4m_parameters)) { + LIBGAV1_EXAMPLES_LOG_ERROR("Error writing Y4M file header"); + return nullptr; + } + + file->file_type_ = file_type; + return file; +} + +bool FileWriter::WriteFrame(const DecoderBuffer& frame_buffer) { + if (file_type_ == kFileTypeY4m) { + const char kY4mFrameHeader[] = "FRAME\n"; + if (fwrite(kY4mFrameHeader, 1, strlen(kY4mFrameHeader), file_) != + strlen(kY4mFrameHeader)) { + LIBGAV1_EXAMPLES_LOG_ERROR("Error writing Y4M frame header"); + return false; + } + } + + const size_t pixel_size = + (frame_buffer.bitdepth == 8) ? sizeof(uint8_t) : sizeof(uint16_t); + for (int plane_index = 0; plane_index < frame_buffer.NumPlanes(); + ++plane_index) { + const int height = frame_buffer.displayed_height[plane_index]; + const int width = frame_buffer.displayed_width[plane_index]; + const int stride = frame_buffer.stride[plane_index]; + const uint8_t* const plane_pointer = frame_buffer.plane[plane_index]; + for (int row = 0; row < height; ++row) { + const uint8_t* const row_pointer = &plane_pointer[row * stride]; + if (fwrite(row_pointer, pixel_size, width, file_) != + static_cast<size_t>(width)) { + char error_string[256]; + snprintf(error_string, sizeof(error_string), + "File write failed: %s (errno=%d)", strerror(errno), errno); + LIBGAV1_EXAMPLES_LOG_ERROR(error_string); + return false; + } + } + } + + return true; +} + +// Writes Y4M file header to |file_| and returns true when successful. +// +// A Y4M file begins with a plaintext file signature of 'YUV4MPEG2 '. +// +// Following the signature is any number of optional parameters preceded by a +// space. We always write: +// +// Width: 'W' followed by image width in pixels. +// Height: 'H' followed by image height in pixels. +// Frame Rate: 'F' followed frames/second in the form numerator:denominator. +// Interlacing: 'I' followed by 'p' for progressive. +// Color space: 'C' followed by a string representation of the color space. +// +// More info here: https://wiki.multimedia.cx/index.php/YUV4MPEG2 +bool FileWriter::WriteY4mFileHeader(const Y4mParameters& y4m_parameters) { + std::string y4m_header = "YUV4MPEG2"; + y4m_header += " W" + std::to_string(y4m_parameters.width); + y4m_header += " H" + std::to_string(y4m_parameters.height); + y4m_header += " F" + std::to_string(y4m_parameters.frame_rate_numerator) + + ":" + std::to_string(y4m_parameters.frame_rate_denominator); + y4m_header += " Ip C" + GetY4mColorSpaceString(y4m_parameters); + y4m_header += "\n"; + return fwrite(y4m_header.c_str(), 1, y4m_header.length(), file_) == + y4m_header.length(); +} + +} // namespace libgav1 diff --git a/examples/file_writer.h b/examples/file_writer.h new file mode 100644 index 0000000..00f6cc3 --- /dev/null +++ b/examples/file_writer.h @@ -0,0 +1,102 @@ +/* + * Copyright 2019 The libgav1 Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBGAV1_EXAMPLES_FILE_WRITER_H_ +#define LIBGAV1_EXAMPLES_FILE_WRITER_H_ + +#include <cstddef> +#include <cstdint> +#include <cstdio> +#include <memory> +#include <string> + +#include "gav1/decoder_buffer.h" + +namespace libgav1 { + +// Frame based file writer class. Supports only Y4M (YUV4MPEG2) and RAW output. +class FileWriter { + public: + enum FileType : uint8_t { + kFileTypeRaw, + kFileTypeY4m, + }; + + struct Y4mParameters { + Y4mParameters() = default; + Y4mParameters(size_t width, size_t height, size_t frame_rate_numerator, + size_t frame_rate_denominator, + ChromaSamplePosition chroma_sample_position, + ImageFormat image_format, size_t bitdepth) + : width(width), + height(height), + frame_rate_numerator(frame_rate_numerator), + frame_rate_denominator(frame_rate_denominator), + chroma_sample_position(chroma_sample_position), + image_format(image_format), + bitdepth(bitdepth) {} + + Y4mParameters(const Y4mParameters& rhs) = default; + Y4mParameters& operator=(const Y4mParameters& rhs) = default; + Y4mParameters(Y4mParameters&& rhs) = default; + Y4mParameters& operator=(Y4mParameters&& rhs) = default; + + size_t width = 0; + size_t height = 0; + size_t frame_rate_numerator = 30; + size_t frame_rate_denominator = 1; + ChromaSamplePosition chroma_sample_position = kChromaSamplePositionUnknown; + ImageFormat image_format = kImageFormatYuv420; + size_t bitdepth = 8; + }; + + // Opens |file_name|. When |file_type| is kFileTypeY4m the Y4M file header is + // written out to |file_| before this method returns. + // + // Returns a FileWriter instance after the file is opened successfully for + // kFileTypeRaw files, and after the Y4M file header bytes are written for + // kFileTypeY4m files. Returns nullptr upon failure. + static std::unique_ptr<FileWriter> Open(const std::string& file_name, + FileType type, + const Y4mParameters* y4m_parameters); + + FileWriter() = delete; + FileWriter(const FileWriter&) = delete; + FileWriter& operator=(const FileWriter&) = delete; + + FileWriter(FileWriter&&) = default; + FileWriter& operator=(FileWriter&&) = default; + + // Closes |file_|. + ~FileWriter(); + + // Writes the frame data in |frame_buffer| to |file_|. Returns true after + // successful write of |frame_buffer| data. + /*LIBGAV1_MUST_USE_RESULT*/ bool WriteFrame( + const DecoderBuffer& frame_buffer); + + private: + explicit FileWriter(FILE* file) : file_(file) {} + + bool WriteY4mFileHeader(const Y4mParameters& y4m_parameters); + + FILE* file_ = nullptr; + FileType file_type_ = kFileTypeRaw; +}; + +} // namespace libgav1 + +#endif // LIBGAV1_EXAMPLES_FILE_WRITER_H_ diff --git a/examples/gav1_decode.cc b/examples/gav1_decode.cc new file mode 100644 index 0000000..4de0ba2 --- /dev/null +++ b/examples/gav1_decode.cc @@ -0,0 +1,452 @@ +// Copyright 2019 The libgav1 Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include <cerrno> +#include <cstddef> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <cstring> +#include <deque> +#include <memory> +#include <new> +#include <vector> + +#include "absl/strings/numbers.h" +#include "absl/time/clock.h" +#include "absl/time/time.h" +#include "examples/file_reader_factory.h" +#include "examples/file_reader_interface.h" +#include "examples/file_writer.h" +#include "gav1/decoder.h" + +#ifdef GAV1_DECODE_USE_CV_PIXEL_BUFFER_POOL +#include "examples/gav1_decode_cv_pixel_buffer_pool.h" +#endif + +namespace { + +struct Options { + const char* input_file_name = nullptr; + const char* output_file_name = nullptr; + const char* frame_timing_file_name = nullptr; + libgav1::FileWriter::FileType output_file_type = + libgav1::FileWriter::kFileTypeRaw; + uint8_t post_filter_mask = 0x1f; + int threads = 1; + bool frame_parallel = false; + bool output_all_layers = false; + int operating_point = 0; + int limit = 0; + int skip = 0; + int verbose = 0; +}; + +struct Timing { + absl::Duration input; + absl::Duration dequeue; +}; + +struct FrameTiming { + absl::Time enqueue; + absl::Time dequeue; +}; + +void PrintHelp(FILE* const fout) { + fprintf(fout, + "Usage: gav1_decode [options] <input file>" + " [-o <output file>]\n"); + fprintf(fout, "\n"); + fprintf(fout, "Options:\n"); + fprintf(fout, " -h, --help This help message.\n"); + fprintf(fout, " --threads <positive integer> (Default 1).\n"); + fprintf(fout, " --frame_parallel.\n"); + fprintf(fout, + " --limit <integer> Stop decoding after N frames (0 = all).\n"); + fprintf(fout, " --skip <integer> Skip initial N frames (Default 0).\n"); + fprintf(fout, " --version.\n"); + fprintf(fout, " --y4m (Default false).\n"); + fprintf(fout, " --raw (Default true).\n"); + fprintf(fout, " -v logging verbosity, can be used multiple times.\n"); + fprintf(fout, " --all_layers.\n"); + fprintf(fout, + " --operating_point <integer between 0 and 31> (Default 0).\n"); + fprintf(fout, + " --frame_timing <file> Output per-frame timing to <file> in tsv" + " format.\n Yields meaningful results only when frame parallel is" + " off.\n"); + fprintf(fout, "\nAdvanced settings:\n"); + fprintf(fout, " --post_filter_mask <integer> (Default 0x1f).\n"); + fprintf(fout, + " Mask indicating which post filters should be applied to the" + " reconstructed\n frame. This may be given as octal, decimal or" + " hexadecimal. From LSB:\n"); + fprintf(fout, " Bit 0: Loop filter (deblocking filter)\n"); + fprintf(fout, " Bit 1: Cdef\n"); + fprintf(fout, " Bit 2: SuperRes\n"); + fprintf(fout, " Bit 3: Loop Restoration\n"); + fprintf(fout, " Bit 4: Film Grain Synthesis\n"); +} + +void ParseOptions(int argc, char* argv[], Options* const options) { + for (int i = 1; i < argc; ++i) { + int32_t value; + if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) { + PrintHelp(stdout); + exit(EXIT_SUCCESS); + } else if (strcmp(argv[i], "-o") == 0) { + if (++i >= argc) { + fprintf(stderr, "Missing argument for '-o'\n"); + PrintHelp(stderr); + exit(EXIT_FAILURE); + } + options->output_file_name = argv[i]; + } else if (strcmp(argv[i], "--frame_timing") == 0) { + if (++i >= argc) { + fprintf(stderr, "Missing argument for '--frame_timing'\n"); + PrintHelp(stderr); + exit(EXIT_FAILURE); + } + options->frame_timing_file_name = argv[i]; + } else if (strcmp(argv[i], "--version") == 0) { + printf("gav1_decode, a libgav1 based AV1 decoder\n"); + printf("libgav1 %s\n", libgav1::GetVersionString()); + printf("max bitdepth: %d\n", libgav1::Decoder::GetMaxBitdepth()); + printf("build configuration: %s\n", libgav1::GetBuildConfiguration()); + exit(EXIT_SUCCESS); + } else if (strcmp(argv[i], "-v") == 0) { + ++options->verbose; + } else if (strcmp(argv[i], "--raw") == 0) { + options->output_file_type = libgav1::FileWriter::kFileTypeRaw; + } else if (strcmp(argv[i], "--y4m") == 0) { + options->output_file_type = libgav1::FileWriter::kFileTypeY4m; + } else if (strcmp(argv[i], "--threads") == 0) { + if (++i >= argc || !absl::SimpleAtoi(argv[i], &value)) { + fprintf(stderr, "Missing/Invalid value for --threads.\n"); + PrintHelp(stderr); + exit(EXIT_FAILURE); + } + options->threads = value; + } else if (strcmp(argv[i], "--frame_parallel") == 0) { + options->frame_parallel = true; + } else if (strcmp(argv[i], "--all_layers") == 0) { + options->output_all_layers = true; + } else if (strcmp(argv[i], "--operating_point") == 0) { + if (++i >= argc || !absl::SimpleAtoi(argv[i], &value) || value < 0 || + value >= 32) { + fprintf(stderr, "Missing/Invalid value for --operating_point.\n"); + PrintHelp(stderr); + exit(EXIT_FAILURE); + } + options->operating_point = value; + } else if (strcmp(argv[i], "--limit") == 0) { + if (++i >= argc || !absl::SimpleAtoi(argv[i], &value) || value < 0) { + fprintf(stderr, "Missing/Invalid value for --limit.\n"); + PrintHelp(stderr); + exit(EXIT_FAILURE); + } + options->limit = value; + } else if (strcmp(argv[i], "--skip") == 0) { + if (++i >= argc || !absl::SimpleAtoi(argv[i], &value) || value < 0) { + fprintf(stderr, "Missing/Invalid value for --skip.\n"); + PrintHelp(stderr); + exit(EXIT_FAILURE); + } + options->skip = value; + } else if (strcmp(argv[i], "--post_filter_mask") == 0) { + errno = 0; + char* endptr = nullptr; + value = (++i >= argc) ? -1 + // NOLINTNEXTLINE(runtime/deprecated_fn) + : static_cast<int32_t>(strtol(argv[i], &endptr, 0)); + // Only the last 5 bits of the mask can be set. + if ((value & ~31) != 0 || errno != 0 || endptr == argv[i]) { + fprintf(stderr, "Invalid value for --post_filter_mask.\n"); + PrintHelp(stderr); + exit(EXIT_FAILURE); + } + options->post_filter_mask = value; + } else if (strlen(argv[i]) > 1 && argv[i][0] == '-') { + fprintf(stderr, "Unknown option '%s'!\n", argv[i]); + exit(EXIT_FAILURE); + } else { + if (options->input_file_name == nullptr) { + options->input_file_name = argv[i]; + } else { + fprintf(stderr, "Found invalid parameter: \"%s\".\n", argv[i]); + PrintHelp(stderr); + exit(EXIT_FAILURE); + } + } + } + + if (argc < 2 || options->input_file_name == nullptr) { + fprintf(stderr, "Input file is required!\n"); + PrintHelp(stderr); + exit(EXIT_FAILURE); + } +} + +using InputBuffer = std::vector<uint8_t>; + +class InputBuffers { + public: + ~InputBuffers() { + for (auto buffer : free_buffers_) { + delete buffer; + } + } + InputBuffer* GetFreeBuffer() { + if (free_buffers_.empty()) { + auto* const buffer = new (std::nothrow) InputBuffer(); + if (buffer == nullptr) { + fprintf(stderr, "Failed to create input buffer.\n"); + return nullptr; + } + free_buffers_.push_back(buffer); + } + InputBuffer* const buffer = free_buffers_.front(); + free_buffers_.pop_front(); + return buffer; + } + + void ReleaseInputBuffer(InputBuffer* buffer) { + free_buffers_.push_back(buffer); + } + + private: + std::deque<InputBuffer*> free_buffers_; +}; + +void ReleaseInputBuffer(void* callback_private_data, + void* buffer_private_data) { + auto* const input_buffers = static_cast<InputBuffers*>(callback_private_data); + input_buffers->ReleaseInputBuffer( + static_cast<InputBuffer*>(buffer_private_data)); +} + +int CloseFile(FILE* stream) { return (stream == nullptr) ? 0 : fclose(stream); } + +} // namespace + +int main(int argc, char* argv[]) { + Options options; + ParseOptions(argc, argv, &options); + + auto file_reader = + libgav1::FileReaderFactory::OpenReader(options.input_file_name); + if (file_reader == nullptr) { + fprintf(stderr, "Cannot open input file!\n"); + return EXIT_FAILURE; + } + + std::unique_ptr<FILE, decltype(&CloseFile)> frame_timing_file(nullptr, + &CloseFile); + if (options.frame_timing_file_name != nullptr) { + frame_timing_file.reset(fopen(options.frame_timing_file_name, "wb")); + if (frame_timing_file == nullptr) { + fprintf(stderr, "Cannot open frame timing file '%s'!\n", + options.frame_timing_file_name); + return EXIT_FAILURE; + } + } + +#ifdef GAV1_DECODE_USE_CV_PIXEL_BUFFER_POOL + // Reference frames + 1 scratch frame (for either the current frame or the + // film grain frame). + constexpr int kNumBuffers = 8 + 1; + std::unique_ptr<Gav1DecodeCVPixelBufferPool> cv_pixel_buffers = + Gav1DecodeCVPixelBufferPool::Create(kNumBuffers); + if (cv_pixel_buffers == nullptr) { + fprintf(stderr, "Cannot create Gav1DecodeCVPixelBufferPool!\n"); + return EXIT_FAILURE; + } +#endif + + InputBuffers input_buffers; + libgav1::Decoder decoder; + libgav1::DecoderSettings settings; + settings.post_filter_mask = options.post_filter_mask; + settings.threads = options.threads; + settings.frame_parallel = options.frame_parallel; + settings.output_all_layers = options.output_all_layers; + settings.operating_point = options.operating_point; + settings.blocking_dequeue = true; + settings.callback_private_data = &input_buffers; + settings.release_input_buffer = ReleaseInputBuffer; +#ifdef GAV1_DECODE_USE_CV_PIXEL_BUFFER_POOL + settings.on_frame_buffer_size_changed = Gav1DecodeOnCVPixelBufferSizeChanged; + settings.get_frame_buffer = Gav1DecodeGetCVPixelBuffer; + settings.release_frame_buffer = Gav1DecodeReleaseCVPixelBuffer; + settings.callback_private_data = cv_pixel_buffers.get(); + settings.release_input_buffer = nullptr; + // TODO(vigneshv): Support frame parallel mode to be used with + // CVPixelBufferPool. + settings.frame_parallel = false; +#endif + libgav1::StatusCode status = decoder.Init(&settings); + if (status != libgav1::kStatusOk) { + fprintf(stderr, "Error initializing decoder: %s\n", + libgav1::GetErrorString(status)); + return EXIT_FAILURE; + } + + fprintf(stderr, "decoding '%s'\n", options.input_file_name); + if (options.verbose > 0 && options.skip > 0) { + fprintf(stderr, "skipping %d frame(s).\n", options.skip); + } + + int input_frames = 0; + int decoded_frames = 0; + Timing timing = {}; + std::vector<FrameTiming> frame_timing; + const bool record_frame_timing = frame_timing_file != nullptr; + std::unique_ptr<libgav1::FileWriter> file_writer; + InputBuffer* input_buffer = nullptr; + bool limit_reached = false; + bool dequeue_finished = false; + const absl::Time decode_loop_start = absl::Now(); + do { + if (input_buffer == nullptr && !file_reader->IsEndOfFile() && + !limit_reached) { + input_buffer = input_buffers.GetFreeBuffer(); + if (input_buffer == nullptr) return EXIT_FAILURE; + const absl::Time read_start = absl::Now(); + if (!file_reader->ReadTemporalUnit(input_buffer, + /*timestamp=*/nullptr)) { + fprintf(stderr, "Error reading input file.\n"); + return EXIT_FAILURE; + } + timing.input += absl::Now() - read_start; + } + + if (++input_frames <= options.skip) { + input_buffers.ReleaseInputBuffer(input_buffer); + input_buffer = nullptr; + continue; + } + + if (input_buffer != nullptr) { + if (input_buffer->empty()) { + input_buffers.ReleaseInputBuffer(input_buffer); + input_buffer = nullptr; + continue; + } + + const absl::Time enqueue_start = absl::Now(); + status = decoder.EnqueueFrame(input_buffer->data(), input_buffer->size(), + static_cast<int64_t>(frame_timing.size()), + /*buffer_private_data=*/input_buffer); + if (status == libgav1::kStatusOk) { + if (options.verbose > 1) { + fprintf(stderr, "enqueue frame (length %zu)\n", input_buffer->size()); + } + if (record_frame_timing) { + FrameTiming enqueue_time = {enqueue_start, absl::UnixEpoch()}; + frame_timing.emplace_back(enqueue_time); + } + + input_buffer = nullptr; + // Continue to enqueue frames until we get a kStatusTryAgain status. + continue; + } + if (status != libgav1::kStatusTryAgain) { + fprintf(stderr, "Unable to enqueue frame: %s\n", + libgav1::GetErrorString(status)); + return EXIT_FAILURE; + } + } + + const libgav1::DecoderBuffer* buffer; + status = decoder.DequeueFrame(&buffer); + if (status == libgav1::kStatusNothingToDequeue) { + dequeue_finished = true; + continue; + } + if (status != libgav1::kStatusOk) { + fprintf(stderr, "Unable to dequeue frame: %s\n", + libgav1::GetErrorString(status)); + return EXIT_FAILURE; + } + dequeue_finished = false; + if (buffer == nullptr) continue; + ++decoded_frames; + if (options.verbose > 1) { + fprintf(stderr, "buffer dequeued\n"); + } + + if (record_frame_timing) { + frame_timing[static_cast<int>(buffer->user_private_data)].dequeue = + absl::Now(); + } + + if (options.output_file_name != nullptr && file_writer == nullptr) { + libgav1::FileWriter::Y4mParameters y4m_parameters; + y4m_parameters.width = buffer->displayed_width[0]; + y4m_parameters.height = buffer->displayed_height[0]; + y4m_parameters.frame_rate_numerator = file_reader->frame_rate(); + y4m_parameters.frame_rate_denominator = file_reader->time_scale(); + y4m_parameters.chroma_sample_position = buffer->chroma_sample_position; + y4m_parameters.image_format = buffer->image_format; + y4m_parameters.bitdepth = static_cast<size_t>(buffer->bitdepth); + file_writer = libgav1::FileWriter::Open( + options.output_file_name, options.output_file_type, &y4m_parameters); + if (file_writer == nullptr) { + fprintf(stderr, "Cannot open output file!\n"); + return EXIT_FAILURE; + } + } + + if (!limit_reached && file_writer != nullptr && + !file_writer->WriteFrame(*buffer)) { + fprintf(stderr, "Error writing output file.\n"); + return EXIT_FAILURE; + } + if (options.limit > 0 && options.limit == decoded_frames) { + limit_reached = true; + if (input_buffer != nullptr) { + input_buffers.ReleaseInputBuffer(input_buffer); + } + input_buffer = nullptr; + } + } while (input_buffer != nullptr || + (!file_reader->IsEndOfFile() && !limit_reached) || + !dequeue_finished); + timing.dequeue = absl::Now() - decode_loop_start - timing.input; + + if (record_frame_timing) { + // Note timing for frame parallel will be skewed by the time spent queueing + // additional frames and in the output queue waiting for previous frames, + // the values reported won't be that meaningful. + fprintf(frame_timing_file.get(), "frame number\tdecode time us\n"); + for (size_t i = 0; i < frame_timing.size(); ++i) { + const int decode_time_us = static_cast<int>(absl::ToInt64Microseconds( + frame_timing[i].dequeue - frame_timing[i].enqueue)); + fprintf(frame_timing_file.get(), "%zu\t%d\n", i, decode_time_us); + } + } + + if (options.verbose > 0) { + fprintf(stderr, "time to read input: %d us\n", + static_cast<int>(absl::ToInt64Microseconds(timing.input))); + const int decode_time_us = + static_cast<int>(absl::ToInt64Microseconds(timing.dequeue)); + const double decode_fps = + (decode_time_us == 0) ? 0.0 : 1.0e6 * decoded_frames / decode_time_us; + fprintf(stderr, "time to decode input: %d us (%d frames, %.2f fps)\n", + decode_time_us, decoded_frames, decode_fps); + } + + return EXIT_SUCCESS; +} diff --git a/examples/gav1_decode_cv_pixel_buffer_pool.cc b/examples/gav1_decode_cv_pixel_buffer_pool.cc new file mode 100644 index 0000000..6aa4e61 --- /dev/null +++ b/examples/gav1_decode_cv_pixel_buffer_pool.cc @@ -0,0 +1,278 @@ +// Copyright 2020 The libgav1 Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "examples/gav1_decode_cv_pixel_buffer_pool.h" + +#include <cassert> +#include <cstdint> +#include <cstdio> +#include <cstdlib> +#include <memory> +#include <new> +#include <type_traits> + +namespace { + +struct CFTypeDeleter { + void operator()(CFTypeRef cf) const { CFRelease(cf); } +}; + +using UniqueCFNumberRef = + std::unique_ptr<std::remove_pointer<CFNumberRef>::type, CFTypeDeleter>; + +using UniqueCFDictionaryRef = + std::unique_ptr<std::remove_pointer<CFDictionaryRef>::type, CFTypeDeleter>; + +} // namespace + +extern "C" { + +libgav1::StatusCode Gav1DecodeOnCVPixelBufferSizeChanged( + void* callback_private_data, int bitdepth, + libgav1::ImageFormat image_format, int width, int height, int left_border, + int right_border, int top_border, int bottom_border, int stride_alignment) { + auto* buffer_pool = + static_cast<Gav1DecodeCVPixelBufferPool*>(callback_private_data); + return buffer_pool->OnCVPixelBufferSizeChanged( + bitdepth, image_format, width, height, left_border, right_border, + top_border, bottom_border, stride_alignment); +} + +libgav1::StatusCode Gav1DecodeGetCVPixelBuffer( + void* callback_private_data, int bitdepth, + libgav1::ImageFormat image_format, int width, int height, int left_border, + int right_border, int top_border, int bottom_border, int stride_alignment, + libgav1::FrameBuffer* frame_buffer) { + auto* buffer_pool = + static_cast<Gav1DecodeCVPixelBufferPool*>(callback_private_data); + return buffer_pool->GetCVPixelBuffer( + bitdepth, image_format, width, height, left_border, right_border, + top_border, bottom_border, stride_alignment, frame_buffer); +} + +void Gav1DecodeReleaseCVPixelBuffer(void* callback_private_data, + void* buffer_private_data) { + auto* buffer_pool = + static_cast<Gav1DecodeCVPixelBufferPool*>(callback_private_data); + buffer_pool->ReleaseCVPixelBuffer(buffer_private_data); +} + +} // extern "C" + +// static +std::unique_ptr<Gav1DecodeCVPixelBufferPool> +Gav1DecodeCVPixelBufferPool::Create(size_t num_buffers) { + std::unique_ptr<Gav1DecodeCVPixelBufferPool> buffer_pool( + new (std::nothrow) Gav1DecodeCVPixelBufferPool(num_buffers)); + return buffer_pool; +} + +Gav1DecodeCVPixelBufferPool::Gav1DecodeCVPixelBufferPool(size_t num_buffers) + : num_buffers_(static_cast<int>(num_buffers)) {} + +Gav1DecodeCVPixelBufferPool::~Gav1DecodeCVPixelBufferPool() { + CVPixelBufferPoolRelease(pool_); +} + +libgav1::StatusCode Gav1DecodeCVPixelBufferPool::OnCVPixelBufferSizeChanged( + int bitdepth, libgav1::ImageFormat image_format, int width, int height, + int left_border, int right_border, int top_border, int bottom_border, + int stride_alignment) { + if (bitdepth != 8 || (image_format != libgav1::kImageFormatYuv420 && + image_format != libgav1::kImageFormatMonochrome400)) { + fprintf(stderr, + "Only bitdepth 8, 4:2:0 videos are supported: bitdepth %d, " + "image_format: %d.\n", + bitdepth, image_format); + return libgav1::kStatusUnimplemented; + } + + // stride_alignment must be a power of 2. + assert((stride_alignment & (stride_alignment - 1)) == 0); + + // The possible keys for CVPixelBufferPool are: + // kCVPixelBufferPoolMinimumBufferCountKey + // kCVPixelBufferPoolMaximumBufferAgeKey + // kCVPixelBufferPoolAllocationThresholdKey + const void* pool_keys[] = {kCVPixelBufferPoolMinimumBufferCountKey}; + const int min_buffer_count = 10; + UniqueCFNumberRef cf_min_buffer_count( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &min_buffer_count)); + if (cf_min_buffer_count == nullptr) { + fprintf(stderr, "CFNumberCreate failed.\n"); + return libgav1::kStatusUnknownError; + } + const void* pool_values[] = {cf_min_buffer_count.get()}; + UniqueCFDictionaryRef pool_attributes(CFDictionaryCreate( + nullptr, pool_keys, pool_values, 1, &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks)); + if (pool_attributes == nullptr) { + fprintf(stderr, "CFDictionaryCreate failed.\n"); + return libgav1::kStatusUnknownError; + } + + // The pixelBufferAttributes argument to CVPixelBufferPoolCreate() cannot be + // null and must contain the pixel format, width, and height, otherwise + // CVPixelBufferPoolCreate() fails with kCVReturnInvalidPixelBufferAttributes + // (-6682). + + // I420: kCVPixelFormatType_420YpCbCr8Planar (video range). + const int pixel_format = (image_format == libgav1::kImageFormatYuv420) + ? kCVPixelFormatType_420YpCbCr8PlanarFullRange + : kCVPixelFormatType_OneComponent8; + UniqueCFNumberRef cf_pixel_format( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &pixel_format)); + UniqueCFNumberRef cf_width( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &width)); + UniqueCFNumberRef cf_height( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &height)); + UniqueCFNumberRef cf_left_border( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &left_border)); + UniqueCFNumberRef cf_right_border( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &right_border)); + UniqueCFNumberRef cf_top_border( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &top_border)); + UniqueCFNumberRef cf_bottom_border( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &bottom_border)); + UniqueCFNumberRef cf_stride_alignment( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &stride_alignment)); + + const void* buffer_keys[] = { + kCVPixelBufferPixelFormatTypeKey, + kCVPixelBufferWidthKey, + kCVPixelBufferHeightKey, + kCVPixelBufferExtendedPixelsLeftKey, + kCVPixelBufferExtendedPixelsRightKey, + kCVPixelBufferExtendedPixelsTopKey, + kCVPixelBufferExtendedPixelsBottomKey, + kCVPixelBufferBytesPerRowAlignmentKey, + }; + const void* buffer_values[] = { + cf_pixel_format.get(), cf_width.get(), + cf_height.get(), cf_left_border.get(), + cf_right_border.get(), cf_top_border.get(), + cf_bottom_border.get(), cf_stride_alignment.get(), + }; + UniqueCFDictionaryRef buffer_attributes(CFDictionaryCreate( + kCFAllocatorDefault, buffer_keys, buffer_values, 8, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + if (buffer_attributes == nullptr) { + fprintf(stderr, "CFDictionaryCreate of buffer_attributes failed.\n"); + return libgav1::kStatusUnknownError; + } + CVPixelBufferPoolRef cv_pool; + CVReturn ret = CVPixelBufferPoolCreate( + /*allocator=*/nullptr, pool_attributes.get(), buffer_attributes.get(), + &cv_pool); + if (ret != kCVReturnSuccess) { + fprintf(stderr, "CVPixelBufferPoolCreate failed: %d.\n", + static_cast<int>(ret)); + return libgav1::kStatusOutOfMemory; + } + CVPixelBufferPoolRelease(pool_); + pool_ = cv_pool; + return libgav1::kStatusOk; +} + +libgav1::StatusCode Gav1DecodeCVPixelBufferPool::GetCVPixelBuffer( + int bitdepth, libgav1::ImageFormat image_format, int /*width*/, + int /*height*/, int /*left_border*/, int /*right_border*/, + int /*top_border*/, int /*bottom_border*/, int /*stride_alignment*/, + libgav1::FrameBuffer* frame_buffer) { + static_cast<void>(bitdepth); + assert(bitdepth == 8 && (image_format == libgav1::kImageFormatYuv420 || + image_format == libgav1::kImageFormatMonochrome400)); + const bool is_monochrome = + (image_format == libgav1::kImageFormatMonochrome400); + + // The dictionary must have kCVPixelBufferPoolAllocationThresholdKey, + // otherwise CVPixelBufferPoolCreatePixelBufferWithAuxAttributes() fails with + // kCVReturnWouldExceedAllocationThreshold (-6689). + UniqueCFNumberRef cf_num_buffers( + CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &num_buffers_)); + + const void* buffer_keys[] = { + kCVPixelBufferPoolAllocationThresholdKey, + }; + const void* buffer_values[] = { + cf_num_buffers.get(), + }; + UniqueCFDictionaryRef aux_attributes(CFDictionaryCreate( + kCFAllocatorDefault, buffer_keys, buffer_values, 1, + &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); + if (aux_attributes == nullptr) { + fprintf(stderr, "CFDictionaryCreate of aux_attributes failed.\n"); + return libgav1::kStatusUnknownError; + } + + CVPixelBufferRef pixel_buffer; + CVReturn ret = CVPixelBufferPoolCreatePixelBufferWithAuxAttributes( + /*allocator=*/nullptr, pool_, aux_attributes.get(), &pixel_buffer); + if (ret != kCVReturnSuccess) { + fprintf(stderr, + "CVPixelBufferPoolCreatePixelBufferWithAuxAttributes failed: %d.\n", + static_cast<int>(ret)); + return libgav1::kStatusOutOfMemory; + } + + ret = CVPixelBufferLockBaseAddress(pixel_buffer, /*lockFlags=*/0); + if (ret != kCVReturnSuccess) { + fprintf(stderr, "CVPixelBufferLockBaseAddress failed: %d.\n", + static_cast<int>(ret)); + CFRelease(pixel_buffer); + return libgav1::kStatusUnknownError; + } + + // If the pixel format type is kCVPixelFormatType_OneComponent8, the pixel + // buffer is nonplanar (CVPixelBufferIsPlanar returns false and + // CVPixelBufferGetPlaneCount returns 0), but + // CVPixelBufferGetBytesPerRowOfPlane and CVPixelBufferGetBaseAddressOfPlane + // still work for plane index 0, even though the documentation says they + // return NULL for nonplanar pixel buffers. + frame_buffer->stride[0] = + static_cast<int>(CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, 0)); + frame_buffer->plane[0] = static_cast<uint8_t*>( + CVPixelBufferGetBaseAddressOfPlane(pixel_buffer, 0)); + if (is_monochrome) { + frame_buffer->stride[1] = 0; + frame_buffer->stride[2] = 0; + frame_buffer->plane[1] = nullptr; + frame_buffer->plane[2] = nullptr; + } else { + frame_buffer->stride[1] = + static_cast<int>(CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, 1)); + frame_buffer->stride[2] = + static_cast<int>(CVPixelBufferGetBytesPerRowOfPlane(pixel_buffer, 2)); + frame_buffer->plane[1] = static_cast<uint8_t*>( + CVPixelBufferGetBaseAddressOfPlane(pixel_buffer, 1)); + frame_buffer->plane[2] = static_cast<uint8_t*>( + CVPixelBufferGetBaseAddressOfPlane(pixel_buffer, 2)); + } + frame_buffer->private_data = pixel_buffer; + + return libgav1::kStatusOk; +} + +void Gav1DecodeCVPixelBufferPool::ReleaseCVPixelBuffer( + void* buffer_private_data) { + auto const pixel_buffer = static_cast<CVPixelBufferRef>(buffer_private_data); + CVReturn ret = + CVPixelBufferUnlockBaseAddress(pixel_buffer, /*unlockFlags=*/0); + if (ret != kCVReturnSuccess) { + fprintf(stderr, "%s:%d: CVPixelBufferUnlockBaseAddress failed: %d.\n", + __FILE__, __LINE__, static_cast<int>(ret)); + abort(); + } + CFRelease(pixel_buffer); +} diff --git a/examples/gav1_decode_cv_pixel_buffer_pool.h b/examples/gav1_decode_cv_pixel_buffer_pool.h new file mode 100644 index 0000000..7aee324 --- /dev/null +++ b/examples/gav1_decode_cv_pixel_buffer_pool.h @@ -0,0 +1,73 @@ +/* + * Copyright 2020 The libgav1 Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBGAV1_EXAMPLES_GAV1_DECODE_CV_PIXEL_BUFFER_POOL_H_ +#define LIBGAV1_EXAMPLES_GAV1_DECODE_CV_PIXEL_BUFFER_POOL_H_ + +#include <CoreVideo/CoreVideo.h> + +#include <cstddef> +#include <memory> + +#include "gav1/frame_buffer.h" + +extern "C" libgav1::StatusCode Gav1DecodeOnCVPixelBufferSizeChanged( + void* callback_private_data, int bitdepth, + libgav1::ImageFormat image_format, int width, int height, int left_border, + int right_border, int top_border, int bottom_border, int stride_alignment); + +extern "C" libgav1::StatusCode Gav1DecodeGetCVPixelBuffer( + void* callback_private_data, int bitdepth, + libgav1::ImageFormat image_format, int width, int height, int left_border, + int right_border, int top_border, int bottom_border, int stride_alignment, + libgav1::FrameBuffer* frame_buffer); + +extern "C" void Gav1DecodeReleaseCVPixelBuffer(void* callback_private_data, + void* buffer_private_data); + +class Gav1DecodeCVPixelBufferPool { + public: + static std::unique_ptr<Gav1DecodeCVPixelBufferPool> Create( + size_t num_buffers); + + // Not copyable or movable. + Gav1DecodeCVPixelBufferPool(const Gav1DecodeCVPixelBufferPool&) = delete; + Gav1DecodeCVPixelBufferPool& operator=(const Gav1DecodeCVPixelBufferPool&) = + delete; + + ~Gav1DecodeCVPixelBufferPool(); + + libgav1::StatusCode OnCVPixelBufferSizeChanged( + int bitdepth, libgav1::ImageFormat image_format, int width, int height, + int left_border, int right_border, int top_border, int bottom_border, + int stride_alignment); + + libgav1::StatusCode GetCVPixelBuffer(int bitdepth, + libgav1::ImageFormat image_format, + int width, int height, int left_border, + int right_border, int top_border, + int bottom_border, int stride_alignment, + libgav1::FrameBuffer* frame_buffer); + void ReleaseCVPixelBuffer(void* buffer_private_data); + + private: + Gav1DecodeCVPixelBufferPool(size_t num_buffers); + + CVPixelBufferPoolRef pool_ = nullptr; + const int num_buffers_; +}; + +#endif // LIBGAV1_EXAMPLES_GAV1_DECODE_CV_PIXEL_BUFFER_POOL_H_ diff --git a/examples/ivf_parser.cc b/examples/ivf_parser.cc new file mode 100644 index 0000000..f8adb14 --- /dev/null +++ b/examples/ivf_parser.cc @@ -0,0 +1,96 @@ +// Copyright 2019 The libgav1 Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "examples/ivf_parser.h" + +#include <cstdio> +#include <cstring> + +#include "examples/file_reader_constants.h" +#include "examples/logging.h" + +namespace libgav1 { +namespace { + +size_t ReadLittleEndian16(const uint8_t* const buffer) { + size_t value = buffer[1] << 8; + value |= buffer[0]; + return value; +} + +size_t ReadLittleEndian32(const uint8_t* const buffer) { + size_t value = buffer[3] << 24; + value |= buffer[2] << 16; + value |= buffer[1] << 8; + value |= buffer[0]; + return value; +} + +} // namespace + +bool ParseIvfFileHeader(const uint8_t* const header_buffer, + IvfFileHeader* const ivf_file_header) { + if (header_buffer == nullptr || ivf_file_header == nullptr) return false; + + if (memcmp(kIvfSignature, header_buffer, 4) != 0) { + return false; + } + + // Verify header version and length. + const size_t ivf_header_version = ReadLittleEndian16(&header_buffer[4]); + if (ivf_header_version != kIvfHeaderVersion) { + LIBGAV1_EXAMPLES_LOG_ERROR("Unexpected IVF version"); + } + + const size_t ivf_header_size = ReadLittleEndian16(&header_buffer[6]); + if (ivf_header_size != kIvfFileHeaderSize) { + LIBGAV1_EXAMPLES_LOG_ERROR("Invalid IVF file header size"); + return false; + } + + if (memcmp(kAv1FourCcLower, &header_buffer[8], 4) != 0 && + memcmp(kAv1FourCcUpper, &header_buffer[8], 4) != 0) { + LIBGAV1_EXAMPLES_LOG_ERROR("Unsupported codec 4CC"); + return false; + } + + ivf_file_header->width = ReadLittleEndian16(&header_buffer[12]); + ivf_file_header->height = ReadLittleEndian16(&header_buffer[14]); + ivf_file_header->frame_rate_numerator = + ReadLittleEndian32(&header_buffer[16]); + ivf_file_header->frame_rate_denominator = + ReadLittleEndian32(&header_buffer[20]); + + return true; +} + +bool ParseIvfFrameHeader(const uint8_t* const header_buffer, + IvfFrameHeader* const ivf_frame_header) { + if (header_buffer == nullptr || ivf_frame_header == nullptr) return false; + + ivf_frame_header->frame_size = ReadLittleEndian32(header_buffer); + if (ivf_frame_header->frame_size > kMaxTemporalUnitSize) { + LIBGAV1_EXAMPLES_LOG_ERROR("Temporal Unit size exceeds maximum"); + return false; + } + + ivf_frame_header->timestamp = ReadLittleEndian32(&header_buffer[4]); + const uint64_t timestamp_hi = + static_cast<uint64_t>(ReadLittleEndian32(&header_buffer[8])) << 32; + ivf_frame_header->timestamp |= timestamp_hi; + + return true; +} + +} // namespace libgav1 diff --git a/examples/ivf_parser.h b/examples/ivf_parser.h new file mode 100644 index 0000000..b6bbc59 --- /dev/null +++ b/examples/ivf_parser.h @@ -0,0 +1,57 @@ +/* + * Copyright 2019 The libgav1 Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBGAV1_EXAMPLES_IVF_PARSER_H_ +#define LIBGAV1_EXAMPLES_IVF_PARSER_H_ + +#include <cstddef> +#include <cstdint> + +namespace libgav1 { + +struct IvfFileHeader { + IvfFileHeader() = default; + IvfFileHeader(const IvfFileHeader& rhs) = default; + IvfFileHeader& operator=(const IvfFileHeader& rhs) = default; + IvfFileHeader(IvfFileHeader&& rhs) = default; + IvfFileHeader& operator=(IvfFileHeader&& rhs) = default; + + size_t width = 0; + size_t height = 0; + size_t frame_rate_numerator = 0; + size_t frame_rate_denominator = 0; +}; + +struct IvfFrameHeader { + IvfFrameHeader() = default; + IvfFrameHeader(const IvfFrameHeader& rhs) = default; + IvfFrameHeader& operator=(const IvfFrameHeader& rhs) = default; + IvfFrameHeader(IvfFrameHeader&& rhs) = default; + IvfFrameHeader& operator=(IvfFrameHeader&& rhs) = default; + + size_t frame_size = 0; + int64_t timestamp = 0; +}; + +bool ParseIvfFileHeader(const uint8_t* header_buffer, + IvfFileHeader* ivf_file_header); + +bool ParseIvfFrameHeader(const uint8_t* header_buffer, + IvfFrameHeader* ivf_frame_header); + +} // namespace libgav1 + +#endif // LIBGAV1_EXAMPLES_IVF_PARSER_H_ diff --git a/examples/libgav1_examples.cmake b/examples/libgav1_examples.cmake new file mode 100644 index 0000000..1f949f3 --- /dev/null +++ b/examples/libgav1_examples.cmake @@ -0,0 +1,63 @@ +# Copyright 2019 The libgav1 Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if(LIBGAV1_EXAMPLES_LIBGAV1_EXAMPLES_CMAKE_) + return() +endif() # LIBGAV1_EXAMPLES_LIBGAV1_EXAMPLES_CMAKE_ +set(LIBGAV1_EXAMPLES_LIBGAV1_EXAMPLES_CMAKE_ 1) + +set(libgav1_file_reader_sources "${libgav1_examples}/file_reader.cc" + "${libgav1_examples}/file_reader.h" + "${libgav1_examples}/file_reader_constants.cc" + "${libgav1_examples}/file_reader_constants.h" + "${libgav1_examples}/file_reader_factory.cc" + "${libgav1_examples}/file_reader_factory.h" + "${libgav1_examples}/file_reader_interface.h" + "${libgav1_examples}/ivf_parser.cc" + "${libgav1_examples}/ivf_parser.h" + "${libgav1_examples}/logging.h") + +set(libgav1_file_writer_sources "${libgav1_examples}/file_writer.cc" + "${libgav1_examples}/file_writer.h" + "${libgav1_examples}/logging.h") + +set(libgav1_decode_sources "${libgav1_examples}/gav1_decode.cc") + +macro(libgav1_add_examples_targets) + libgav1_add_library(NAME libgav1_file_reader TYPE OBJECT SOURCES + ${libgav1_file_reader_sources} DEFINES ${libgav1_defines} + INCLUDES ${libgav1_include_paths}) + + libgav1_add_library(NAME libgav1_file_writer TYPE OBJECT SOURCES + ${libgav1_file_writer_sources} DEFINES ${libgav1_defines} + INCLUDES ${libgav1_include_paths}) + + libgav1_add_executable(NAME + gav1_decode + SOURCES + ${libgav1_decode_sources} + DEFINES + ${libgav1_defines} + INCLUDES + ${libgav1_include_paths} + ${libgav1_gtest_include_paths} + OBJLIB_DEPS + libgav1_file_reader + libgav1_file_writer + LIB_DEPS + absl::strings + absl::str_format_internal + absl::time + ${libgav1_dependency}) +endmacro() diff --git a/examples/logging.h b/examples/logging.h new file mode 100644 index 0000000..c0bcad7 --- /dev/null +++ b/examples/logging.h @@ -0,0 +1,65 @@ +/* + * Copyright 2019 The libgav1 Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LIBGAV1_EXAMPLES_LOGGING_H_ +#define LIBGAV1_EXAMPLES_LOGGING_H_ + +#include <cstddef> +#include <cstdio> + +namespace libgav1 { +namespace examples { + +#if !defined(LIBGAV1_EXAMPLES_ENABLE_LOGGING) +#if defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) +#define LIBGAV1_EXAMPLES_ENABLE_LOGGING 0 +#else +#define LIBGAV1_EXAMPLES_ENABLE_LOGGING 1 +#endif +#endif + +#if LIBGAV1_EXAMPLES_ENABLE_LOGGING + +// Compile-time function to get the 'base' file_name, that is, the part of +// a file_name after the last '/' or '\' path separator. The search starts at +// the end of the string; the second parameter is the length of the string. +constexpr const char* Basename(const char* file_name, size_t offset) { + return (offset == 0 || file_name[offset - 1] == '/' || + file_name[offset - 1] == '\\') + ? file_name + offset + : Basename(file_name, offset - 1); +} + +#define LIBGAV1_EXAMPLES_LOG_ERROR(error_string) \ + do { \ + constexpr const char* libgav1_examples_basename = \ + ::libgav1::examples::Basename(__FILE__, sizeof(__FILE__) - 1); \ + fprintf(stderr, "%s:%d (%s): %s.\n", libgav1_examples_basename, __LINE__, \ + __func__, error_string); \ + } while (false) + +#else // !LIBGAV1_EXAMPLES_ENABLE_LOGGING + +#define LIBGAV1_EXAMPLES_LOG_ERROR(error_string) \ + do { \ + } while (false) + +#endif // LIBGAV1_EXAMPLES_ENABLE_LOGGING + +} // namespace examples +} // namespace libgav1 + +#endif // LIBGAV1_EXAMPLES_LOGGING_H_ |