mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-02-17 19:11:30 +00:00
media: add libvorbis encoder
This commit is contained in:
parent
e9f5a75d4a
commit
650264ea86
4 changed files with 249 additions and 0 deletions
|
|
@ -6,4 +6,7 @@ target_sources(SRB2SDL2 PRIVATE
|
|||
options.hpp
|
||||
video_encoder.hpp
|
||||
video_frame.hpp
|
||||
vorbis.cpp
|
||||
vorbis.hpp
|
||||
vorbis_error.hpp
|
||||
)
|
||||
|
|
|
|||
138
src/media/vorbis.cpp
Normal file
138
src/media/vorbis.cpp
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <vorbis/vorbisenc.h>
|
||||
|
||||
#include "../cxxutil.hpp"
|
||||
#include "vorbis.hpp"
|
||||
#include "vorbis_error.hpp"
|
||||
|
||||
using namespace srb2::media;
|
||||
|
||||
// clang-format off
|
||||
const Options VorbisEncoder::options_("vorbis", {
|
||||
{"quality", Options::range<float>("0", -0.1f, 1.f)},
|
||||
{"max_bitrate", Options::range_min<int>("-1", -1)},
|
||||
{"nominal_bitrate", Options::range_min<int>("-1", -1)},
|
||||
{"min_bitrate", Options::range_min<int>("-1", -1)},
|
||||
});
|
||||
// clang-format on
|
||||
|
||||
VorbisEncoder::VorbisEncoder(Config cfg)
|
||||
{
|
||||
const long max_bitrate = options_.get<int>("max_bitrate");
|
||||
const long nominal_bitrate = options_.get<int>("nominal_bitrate");
|
||||
const long min_bitrate = options_.get<int>("min_bitrate");
|
||||
|
||||
vorbis_info_init(&vi_);
|
||||
|
||||
if (max_bitrate != -1 || nominal_bitrate != -1 || min_bitrate != -1)
|
||||
{
|
||||
// managed bitrate mode
|
||||
VorbisError error =
|
||||
vorbis_encode_init(&vi_, cfg.channels, cfg.sample_rate, max_bitrate, nominal_bitrate, min_bitrate);
|
||||
|
||||
if (error != 0)
|
||||
{
|
||||
throw std::invalid_argument(fmt::format(
|
||||
"vorbis_encode_init: {}, max_bitrate={}, nominal_bitrate={}, min_bitrate={}",
|
||||
error,
|
||||
max_bitrate,
|
||||
nominal_bitrate,
|
||||
min_bitrate
|
||||
));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// variable bitrate mode
|
||||
const float quality = options_.get<float>("quality");
|
||||
|
||||
VorbisError error = vorbis_encode_init_vbr(&vi_, cfg.channels, cfg.sample_rate, quality);
|
||||
|
||||
if (error != 0)
|
||||
{
|
||||
throw std::invalid_argument(fmt::format("vorbis_encode_init: {}, quality={}", error, quality));
|
||||
}
|
||||
}
|
||||
|
||||
SRB2_ASSERT(vorbis_analysis_init(&vd_, &vi_) == 0);
|
||||
SRB2_ASSERT(vorbis_block_init(&vd_, &vb_) == 0);
|
||||
}
|
||||
|
||||
VorbisEncoder::~VorbisEncoder()
|
||||
{
|
||||
vorbis_block_clear(&vb_);
|
||||
vorbis_dsp_clear(&vd_);
|
||||
vorbis_info_clear(&vi_);
|
||||
}
|
||||
|
||||
VorbisEncoder::headers_t VorbisEncoder::generate_headers()
|
||||
{
|
||||
headers_t op;
|
||||
|
||||
vorbis_comment vc;
|
||||
vorbis_comment_init(&vc);
|
||||
|
||||
VorbisError error = vorbis_analysis_headerout(&vd_, &vc, &op[0], &op[1], &op[2]);
|
||||
|
||||
if (error != 0)
|
||||
{
|
||||
throw std::invalid_argument(fmt::format("vorbis_analysis_headerout: {}", error));
|
||||
}
|
||||
|
||||
vorbis_comment_clear(&vc);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
void VorbisEncoder::analyse(sample_buffer_t in)
|
||||
{
|
||||
const int ch = channels();
|
||||
|
||||
const std::size_t n = in.size() / ch;
|
||||
float** fv = vorbis_analysis_buffer(&vd_, n);
|
||||
|
||||
for (std::size_t i = 0; i < n; ++i)
|
||||
{
|
||||
auto s = in.subspan(i * ch, ch);
|
||||
|
||||
fv[0][i] = s[0];
|
||||
fv[1][i] = s[1];
|
||||
}
|
||||
|
||||
// automatically handles end of stream if n = 0
|
||||
SRB2_ASSERT(vorbis_analysis_wrote(&vd_, n) == 0);
|
||||
|
||||
while (vorbis_analysis_blockout(&vd_, &vb_) > 0)
|
||||
{
|
||||
SRB2_ASSERT(vorbis_analysis(&vb_, nullptr) == 0);
|
||||
SRB2_ASSERT(vorbis_bitrate_addblock(&vb_) == 0);
|
||||
|
||||
ogg_packet op;
|
||||
|
||||
while (vorbis_bitrate_flushpacket(&vd_, &op) > 0)
|
||||
{
|
||||
write_packet(&op);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VorbisEncoder::write_packet(ogg_packet* op)
|
||||
{
|
||||
using T = const std::byte;
|
||||
tcb::span<T> p(reinterpret_cast<T*>(op->packet), static_cast<std::size_t>(op->bytes));
|
||||
|
||||
write_frame(p, std::chrono::duration<float>(vorbis_granule_time(&vd_, op->granulepos)), true);
|
||||
}
|
||||
54
src/media/vorbis.hpp
Normal file
54
src/media/vorbis.hpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// 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_VORBIS_HPP__
|
||||
#define __SRB2_MEDIA_VORBIS_HPP__
|
||||
|
||||
#include <array>
|
||||
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
#include "audio_encoder.hpp"
|
||||
#include "options.hpp"
|
||||
|
||||
namespace srb2::media
|
||||
{
|
||||
|
||||
class VorbisEncoder : public AudioEncoder
|
||||
{
|
||||
public:
|
||||
static const Options options_;
|
||||
|
||||
VorbisEncoder(Config config);
|
||||
~VorbisEncoder();
|
||||
|
||||
virtual void encode(sample_buffer_t samples) override final { analyse(samples); }
|
||||
virtual void flush() override final { analyse(); }
|
||||
|
||||
virtual const char* name() const override final { return "Vorbis"; }
|
||||
virtual int channels() const override final { return vi_.channels; }
|
||||
virtual int sample_rate() const override final { return vi_.rate; }
|
||||
|
||||
protected:
|
||||
using headers_t = std::array<ogg_packet, 3>;
|
||||
|
||||
headers_t generate_headers();
|
||||
|
||||
private:
|
||||
vorbis_info vi_;
|
||||
vorbis_dsp_state vd_;
|
||||
vorbis_block vb_;
|
||||
|
||||
void analyse(sample_buffer_t samples = {});
|
||||
void write_packet(ogg_packet* op);
|
||||
};
|
||||
|
||||
}; // namespace srb2::media
|
||||
|
||||
#endif // __SRB2_MEDIA_VORBIS_HPP__
|
||||
54
src/media/vorbis_error.hpp
Normal file
54
src/media/vorbis_error.hpp
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
// 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_VORBIS_ERROR_HPP__
|
||||
#define __SRB2_MEDIA_VORBIS_ERROR_HPP__
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
class VorbisError
|
||||
{
|
||||
public:
|
||||
VorbisError(int error) : error_(error) {}
|
||||
|
||||
operator int() const { return error_; }
|
||||
|
||||
std::string name() const
|
||||
{
|
||||
switch (error_)
|
||||
{
|
||||
case OV_EFAULT:
|
||||
return "Internal error (OV_EFAULT)";
|
||||
case OV_EINVAL:
|
||||
return "Invalid settings (OV_EINVAL)";
|
||||
case OV_EIMPL:
|
||||
return "Invalid settings (OV_EIMPL)";
|
||||
default:
|
||||
return fmt::format("error {}", error_);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
int error_;
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<VorbisError> : formatter<std::string>
|
||||
{
|
||||
template <typename FormatContext>
|
||||
auto format(const VorbisError& error, FormatContext& ctx) const
|
||||
{
|
||||
return formatter<std::string>::format(error.name(), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
#endif // __SRB2_MEDIA_VORBIS_ERROR_HPP__
|
||||
Loading…
Add table
Reference in a new issue