media: add WebM Vorbis and VP8 encoders

This commit is contained in:
James R 2023-02-12 02:15:23 -08:00
parent 60899133c1
commit 654f97fa72
5 changed files with 237 additions and 0 deletions

View file

@ -15,8 +15,12 @@ target_sources(SRB2SDL2 PRIVATE
vp8.hpp
vpx_error.hpp
webm.hpp
webm_encoder.hpp
webm_container.cpp
webm_container.hpp
webm_vorbis.hpp
webm_vorbis_lace.cpp
webm_vp8.hpp
webm_writer.hpp
yuv420p.cpp
yuv420p.hpp

View file

@ -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_WEBM_ENCODER_HPP__
#define __SRB2_MEDIA_WEBM_ENCODER_HPP__
#include <mkvmuxer/mkvmuxer.h>
#include "encoder.hpp"
#include "webm_container.hpp"
namespace srb2::media
{
template <typename T = mkvmuxer::Track>
class WebmEncoder : virtual public MediaEncoder
{
public:
WebmEncoder(WebmContainer& container, webm::track trackid) : container_(container), trackid_(trackid)
{
container_.init_queue(trackid_);
}
protected:
WebmContainer& container_;
webm::track trackid_;
std::size_t size() const { return container_.track_size(trackid_); }
time_unit_t duration() const { return container_.track_duration(trackid_); }
static T* get_track(const WebmContainer& container, webm::track trackid) { return container.get_track<T>(trackid); }
T* track() const { return get_track(container_, trackid_); }
virtual void write_frame(frame_buffer_t p, time_unit_t ts, bool is_key_frame) override final
{
const auto ts_nano = std::chrono::duration_cast<webm::duration>(ts);
container_.queue_frame(p, trackid_, ts_nano.count(), is_key_frame);
}
};
}; // namespace srb2::media
#endif // __SRB2_MEDIA_WEBM_ENCODER_HPP__

59
src/media/webm_vorbis.hpp Normal file
View file

@ -0,0 +1,59 @@
// 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_WEBM_VORBIS_HPP__
#define __SRB2_MEDIA_WEBM_VORBIS_HPP__
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <vector>
#include "../cxxutil.hpp"
#include "vorbis.hpp"
#include "webm_encoder.hpp"
namespace srb2::media
{
class WebmVorbisEncoder : public WebmEncoder<mkvmuxer::AudioTrack>, public VorbisEncoder
{
public:
WebmVorbisEncoder(WebmContainer& container, webm::track trackid, AudioEncoder::Config cfg) :
WebmEncoder(container, trackid), VorbisEncoder(cfg)
{
// write Vorbis extra data
const auto p = make_vorbis_private_data();
SRB2_ASSERT(track()->SetCodecPrivate(reinterpret_cast<const uint8_t*>(p.data()), p.size()) == true);
}
virtual BitRate estimated_bit_rate() const override final
{
auto _ = container_.queue_guard();
const std::chrono::duration<float> t = duration();
if (t <= t.zero())
{
return {};
}
using namespace std::chrono_literals;
return {static_cast<std::size_t>((size() * 8) / t.count()), 1s};
}
private:
std::vector<std::byte> make_vorbis_private_data();
};
}; // namespace srb2::media
#endif // __SRB2_MEDIA_WEBM_VORBIS_HPP__

View file

@ -0,0 +1,79 @@
// 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 <algorithm>
#include <cstddef>
#include <iterator>
#include <vector>
#include <tcb/span.hpp>
#include "webm_vorbis.hpp"
// https://www.matroska.org/technical/notes.html#xiph-lacing
// https://www.matroska.org/technical/codec_specs.html#a_vorbis
using namespace srb2::media;
static std::size_t lace_length(const ogg_packet& op)
{
return (op.bytes / 255) + 1;
}
static void lace(std::vector<std::byte>& v, const ogg_packet& op)
{
// The lacing size is encoded in at least one byte. If
// the value is 255, add the value of the next byte in
// sequence. This ends with a byte that is less than 255.
std::fill_n(std::back_inserter(v), lace_length(op) - 1, std::byte {255});
const unsigned char n = (op.bytes % 255);
v.emplace_back(std::byte {n});
}
std::vector<std::byte> WebmVorbisEncoder::make_vorbis_private_data()
{
const headers_t packets = generate_headers();
std::vector<std::byte> v;
// There are three Vorbis header packets. The lacing for
// these packets in Matroska does not count the final
// packet.
// clang-format off
v.reserve(
1
+ lace_length(packets[0])
+ lace_length(packets[1])
+ packets[0].bytes
+ packets[1].bytes
+ packets[2].bytes);
// clang-format on
// The first byte is the number of packets. Once again,
// the last packet is not counted.
v.emplace_back(std::byte {2});
// Then the laced sizes for each packet.
lace(v, packets[0]);
lace(v, packets[1]);
// Then each packet's data. The last packet's data
// actually is written here.
for (auto op : packets)
{
tcb::span<const std::byte> p(reinterpret_cast<const std::byte*>(op.packet), op.bytes);
std::copy(p.begin(), p.end(), std::back_inserter(v));
}
return v;
}

44
src/media/webm_vp8.hpp Normal file
View file

@ -0,0 +1,44 @@
// 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_WEBM_VP8_HPP__
#define __SRB2_MEDIA_WEBM_VP8_HPP__
#include "vp8.hpp"
#include "webm_encoder.hpp"
namespace srb2::media
{
class WebmVP8Encoder : public WebmEncoder<mkvmuxer::VideoTrack>, public VP8Encoder
{
public:
WebmVP8Encoder(WebmContainer& container, webm::track trackid, VideoEncoder::Config cfg) :
WebmEncoder(container, trackid), VP8Encoder(cfg)
{
}
virtual BitRate estimated_bit_rate() const override final
{
auto _ = container_.queue_guard();
const int frames = frame_count().frames;
if (frames <= 0)
{
return {};
}
return {(size() * 8) / frames, std::chrono::duration<float>(1.f / frame_rate())};
}
};
}; // namespace srb2::media
#endif // __SRB2_MEDIA_WEBM_VP8_HPP__