From 3b5245f9748b5afc08d5b0373329a92745b625f0 Mon Sep 17 00:00:00 2001 From: James R Date: Sun, 12 Feb 2023 02:03:23 -0800 Subject: [PATCH] Add basic multimedia container and encoder interfaces Adds the media subdirectory. --- src/CMakeLists.txt | 1 + src/media/CMakeLists.txt | 7 +++++ src/media/audio_encoder.hpp | 39 +++++++++++++++++++++++ src/media/container.hpp | 53 +++++++++++++++++++++++++++++++ src/media/encoder.hpp | 51 ++++++++++++++++++++++++++++++ src/media/video_encoder.hpp | 58 ++++++++++++++++++++++++++++++++++ src/media/video_frame.hpp | 63 +++++++++++++++++++++++++++++++++++++ 7 files changed, 272 insertions(+) create mode 100644 src/media/CMakeLists.txt create mode 100644 src/media/audio_encoder.hpp create mode 100644 src/media/container.hpp create mode 100644 src/media/encoder.hpp create mode 100644 src/media/video_encoder.hpp create mode 100644 src/media/video_frame.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8ff8942d5..ada3a3f4f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -549,6 +549,7 @@ if(SRB2_CONFIG_ENABLE_TESTS) add_subdirectory(tests) endif() add_subdirectory(menus) +add_subdirectory(media) # strip debug symbols into separate file when using gcc. # to be consistent with Makefile, don't generate for OS X. diff --git a/src/media/CMakeLists.txt b/src/media/CMakeLists.txt new file mode 100644 index 000000000..f0a7575bd --- /dev/null +++ b/src/media/CMakeLists.txt @@ -0,0 +1,7 @@ +target_sources(SRB2SDL2 PRIVATE + audio_encoder.hpp + container.hpp + encoder.hpp + video_encoder.hpp + video_frame.hpp +) diff --git a/src/media/audio_encoder.hpp b/src/media/audio_encoder.hpp new file mode 100644 index 000000000..3aa9f5cf5 --- /dev/null +++ b/src/media/audio_encoder.hpp @@ -0,0 +1,39 @@ +// RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef __SRB2_MEDIA_AUDIO_ENCODER_HPP__ +#define __SRB2_MEDIA_AUDIO_ENCODER_HPP__ + +#include + +#include "encoder.hpp" + +namespace srb2::media +{ + +class AudioEncoder : virtual public MediaEncoder +{ +public: + using sample_buffer_t = tcb::span; + + struct Config + { + int channels; + int sample_rate; + }; + + virtual void encode(sample_buffer_t samples) = 0; + + virtual int channels() const = 0; + virtual int sample_rate() const = 0; +}; + +}; // namespace srb2::media + +#endif // __SRB2_MEDIA_AUDIO_ENCODER_HPP__ diff --git a/src/media/container.hpp b/src/media/container.hpp new file mode 100644 index 000000000..590d496f7 --- /dev/null +++ b/src/media/container.hpp @@ -0,0 +1,53 @@ +// RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef __SRB2_MEDIA_CONTAINER_HPP__ +#define __SRB2_MEDIA_CONTAINER_HPP__ + +#include +#include +#include +#include + +#include "audio_encoder.hpp" +#include "video_encoder.hpp" + +namespace srb2::media +{ + +class MediaContainer +{ +public: + using dtor_cb_t = std::function; + using time_unit_t = std::chrono::duration; + + struct Config + { + std::string file_name; + dtor_cb_t destructor_callback; + }; + + virtual ~MediaContainer() = default; + + virtual std::unique_ptr make_audio_encoder(AudioEncoder::Config config) = 0; + virtual std::unique_ptr make_video_encoder(VideoEncoder::Config config) = 0; + + virtual const char* name() const = 0; + virtual const char* file_name() const = 0; + + // These are normally estimates. However, when called from + // Config::destructor_callback, these are the exact final + // values. + virtual time_unit_t duration() const = 0; + virtual std::size_t size() const = 0; +}; + +}; // namespace srb2::media + +#endif // __SRB2_MEDIA_CONTAINER_HPP__ diff --git a/src/media/encoder.hpp b/src/media/encoder.hpp new file mode 100644 index 000000000..d6fa9c049 --- /dev/null +++ b/src/media/encoder.hpp @@ -0,0 +1,51 @@ +// RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef __SRB2_MEDIA_ENCODER_HPP__ +#define __SRB2_MEDIA_ENCODER_HPP__ + +#include +#include + +#include + +namespace srb2::media +{ + +class MediaEncoder +{ +public: + using time_unit_t = std::chrono::duration; + + struct BitRate + { + std::size_t bits; // 8 bits = 1 byte :) + time_unit_t period; + }; + + virtual ~MediaEncoder() = default; + + // Should be called finally but it's optional. + virtual void flush() = 0; + + virtual const char* name() const = 0; + + // Returns an average bit rate over a constant period of + // time, assuming no frames drops. + virtual BitRate estimated_bit_rate() const = 0; + +protected: + using frame_buffer_t = tcb::span; + + virtual void write_frame(frame_buffer_t frame, time_unit_t timestamp, bool is_key_frame) = 0; +}; + +}; // namespace srb2::media + +#endif // __SRB2_MEDIA_ENCODER_HPP__ diff --git a/src/media/video_encoder.hpp b/src/media/video_encoder.hpp new file mode 100644 index 000000000..1230bd8da --- /dev/null +++ b/src/media/video_encoder.hpp @@ -0,0 +1,58 @@ +// RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef __SRB2_MEDIA_VIDEO_ENCODER_HPP__ +#define __SRB2_MEDIA_VIDEO_ENCODER_HPP__ + +#include "encoder.hpp" +#include "video_frame.hpp" + +namespace srb2::media +{ + +class VideoEncoder : virtual public MediaEncoder +{ +public: + struct Config + { + int width; + int height; + int frame_rate; + VideoFrame::BufferMethod buffer_method; + }; + + struct FrameCount + { + // Number of real frames, not counting frame skips. + int frames; + + time_unit_t duration; + }; + + // VideoFrame::width() and VideoFrame::height() should be + // used on the returned frame. + virtual VideoFrame::instance_t new_frame(int width, int height, int pts) = 0; + + virtual void encode(VideoFrame::instance_t frame) = 0; + + virtual int width() const = 0; + virtual int height() const = 0; + virtual int frame_rate() const = 0; + + // Reports the number of threads used, if the encoder is + // multithreaded. + virtual int thread_count() const = 0; + + // Number of frames fully encoded so far. + virtual FrameCount frame_count() const = 0; +}; + +}; // namespace srb2::media + +#endif // __SRB2_MEDIA_VIDEO_ENCODER_HPP__ diff --git a/src/media/video_frame.hpp b/src/media/video_frame.hpp new file mode 100644 index 000000000..acf278ff5 --- /dev/null +++ b/src/media/video_frame.hpp @@ -0,0 +1,63 @@ +// RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James Robert Roman +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef __SRB2_MEDIA_VIDEO_FRAME_HPP__ +#define __SRB2_MEDIA_VIDEO_FRAME_HPP__ + +#include +#include +#include + +#include + +namespace srb2::media +{ + +class VideoFrame +{ +public: + using instance_t = std::unique_ptr; + + enum class BufferMethod + { + // Returns an already allocated buffer for each + // frame. See VideoFrame::rgba_buffer(). The encoder + // completely manages allocating this buffer. + kEncoderAllocatedRGBA8888, + }; + + struct Buffer + { + tcb::span plane; + std::size_t row_stride; // size of each row + }; + + virtual int width() const = 0; + virtual int height() const = 0; + + int pts() const { return pts_; } + + // Returns a buffer that should be + // filled with RGBA pixels. + // + // This method may only be used if + // the encoder was configured with + // BufferMethod::kEncoderAllocatedRGBA8888. + virtual const Buffer& rgba_buffer() const = 0; + +protected: + VideoFrame(int pts) : pts_(pts) {} + +private: + int pts_; +}; + +}; // namespace srb2::media + +#endif // __SRB2_MEDIA_VIDEO_FRAME_HPP__