mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Implement Software mode paletted screen conversion for AVRecorder
This is only necessary since HWR2's framebuffer is window size (monitor resolution in fullscreen mode). Once the framebuffer is changed to be native game resolution, this commit should be reverted and a new interface should be implemented to directly copy the framebuffer's pixels into video frames.
This commit is contained in:
parent
fa0071e785
commit
840c11577e
8 changed files with 142 additions and 3 deletions
|
|
@ -7,12 +7,15 @@
|
|||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <tcb/span.hpp>
|
||||
|
||||
#include "cxxutil.hpp"
|
||||
#include "m_avrecorder.hpp"
|
||||
|
|
@ -22,7 +25,9 @@
|
|||
#include "i_sound.h"
|
||||
#include "m_avrecorder.h"
|
||||
#include "m_fixed.h"
|
||||
#include "screen.h" // vid global
|
||||
#include "screen.h" // vid global
|
||||
#include "st_stuff.h" // st_palette
|
||||
#include "v_video.h" // pLocalPalette
|
||||
|
||||
using namespace srb2::media;
|
||||
|
||||
|
|
@ -207,3 +212,24 @@ boolean M_AVRecorder_IsExpired(void)
|
|||
|
||||
return g_av_recorder->invalid();
|
||||
}
|
||||
|
||||
// TODO: remove once hwr2 twodee is finished
|
||||
void M_AVRecorder_CopySoftwareScreen(void)
|
||||
{
|
||||
SRB2_ASSERT(g_av_recorder != nullptr);
|
||||
|
||||
auto frame = g_av_recorder->new_indexed_video_frame(vid.width, vid.height);
|
||||
|
||||
if (!frame)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
tcb::span<RGBA_t> pal(&pLocalPalette[std::max(st_palette, 0) * 256], 256);
|
||||
tcb::span<uint8_t> scr(screens[0], vid.width * vid.height);
|
||||
|
||||
std::copy(pal.begin(), pal.end(), frame->palette.begin());
|
||||
std::copy(scr.begin(), scr.end(), frame->screen.begin());
|
||||
|
||||
g_av_recorder->push_indexed_video_frame(std::move(frame));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ boolean M_AVRecorder_IsExpired(void);
|
|||
|
||||
const char *M_AVRecorder_GetCurrentFormat(void);
|
||||
|
||||
// TODO: remove once hwr2 twodee is finished
|
||||
void M_AVRecorder_CopySoftwareScreen(void);
|
||||
|
||||
extern consvar_t
|
||||
cv_movie_custom_resolution,
|
||||
cv_movie_duration,
|
||||
|
|
|
|||
|
|
@ -1380,6 +1380,12 @@ void M_SaveFrame(void)
|
|||
|
||||
if (moviemode == MM_AVRECORDER)
|
||||
{
|
||||
// TODO: replace once hwr2 twodee is finished
|
||||
if (rendermode == render_soft)
|
||||
{
|
||||
M_AVRecorder_CopySoftwareScreen();
|
||||
}
|
||||
|
||||
if (M_AVRecorder_IsExpired())
|
||||
{
|
||||
M_StopMovie();
|
||||
|
|
@ -1387,6 +1393,7 @@ void M_SaveFrame(void)
|
|||
return;
|
||||
}
|
||||
|
||||
// skip interpolated frames for other modes
|
||||
if (oldtic == I_GetTime())
|
||||
return;
|
||||
else
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ target_sources(SRB2SDL2 PRIVATE
|
|||
avrecorder.cpp
|
||||
avrecorder.hpp
|
||||
avrecorder_impl.hpp
|
||||
avrecorder_indexed.cpp
|
||||
avrecorder_queue.cpp
|
||||
cfile.cpp
|
||||
cfile.hpp
|
||||
|
|
|
|||
|
|
@ -57,6 +57,22 @@ public:
|
|||
std::optional<Video> video;
|
||||
};
|
||||
|
||||
// TODO: remove once hwr2 twodee is finished
|
||||
struct IndexedVideoFrame
|
||||
{
|
||||
using instance_t = std::unique_ptr<IndexedVideoFrame>;
|
||||
|
||||
std::array<RGBA_t, 256> palette;
|
||||
std::vector<uint8_t> screen;
|
||||
uint32_t width, height;
|
||||
int pts;
|
||||
|
||||
IndexedVideoFrame(uint32_t width_, uint32_t height_, int pts_) :
|
||||
screen(width_ * height_), width(width_), height(height_), pts(pts_)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Returns the canonical file extension minus the dot.
|
||||
// E.g. "webm" (not ".webm").
|
||||
static const char* file_extension();
|
||||
|
|
@ -66,6 +82,12 @@ public:
|
|||
|
||||
void push_audio_samples(audio_buffer_t buffer);
|
||||
|
||||
// May return nullptr in case called between units of
|
||||
// Config::frame_rate
|
||||
IndexedVideoFrame::instance_t new_indexed_video_frame(uint32_t width, uint32_t height);
|
||||
|
||||
void push_indexed_video_frame(IndexedVideoFrame::instance_t frame);
|
||||
|
||||
// Proper name of the container format.
|
||||
const char* format_name() const;
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ public:
|
|||
template <typename _>
|
||||
struct Traits<VideoEncoder, _>
|
||||
{
|
||||
using frame_type = VideoFrame::instance_t;
|
||||
using frame_type = IndexedVideoFrame::instance_t;
|
||||
};
|
||||
|
||||
std::vector<typename Traits<T>::frame_type> vec_;
|
||||
|
|
@ -142,6 +142,9 @@ private:
|
|||
QueueState encode_queues();
|
||||
|
||||
void worker();
|
||||
|
||||
// TODO: remove once hwr2 twodee is finished
|
||||
VideoFrame::instance_t convert_indexed_video_frame(const IndexedVideoFrame& indexed);
|
||||
};
|
||||
|
||||
template <>
|
||||
|
|
|
|||
69
src/media/avrecorder_indexed.cpp
Normal file
69
src/media/avrecorder_indexed.cpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
// 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// TODO: remove this file once hwr2 twodee is finished
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
|
||||
#include "../cxxutil.hpp"
|
||||
#include "avrecorder_impl.hpp"
|
||||
|
||||
using namespace srb2::media;
|
||||
|
||||
using Impl = AVRecorder::Impl;
|
||||
|
||||
VideoFrame::instance_t Impl::convert_indexed_video_frame(const IndexedVideoFrame& indexed)
|
||||
{
|
||||
VideoFrame::instance_t frame = video_encoder_->new_frame(indexed.width, indexed.height, indexed.pts);
|
||||
|
||||
SRB2_ASSERT(frame != nullptr);
|
||||
|
||||
const VideoFrame::Buffer& buffer = frame->rgba_buffer();
|
||||
|
||||
const uint8_t* s = indexed.screen.data();
|
||||
uint8_t* p = buffer.plane.data();
|
||||
|
||||
for (int y = 0; y < frame->height(); ++y)
|
||||
{
|
||||
for (int x = 0; x < frame->width(); ++x)
|
||||
{
|
||||
const RGBA_t& c = indexed.palette[s[x]];
|
||||
|
||||
reinterpret_cast<uint32_t*>(p)[x] = c.rgba;
|
||||
}
|
||||
|
||||
s += indexed.width;
|
||||
p += buffer.row_stride;
|
||||
}
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
AVRecorder::IndexedVideoFrame::instance_t AVRecorder::new_indexed_video_frame(uint32_t width, uint32_t height)
|
||||
{
|
||||
std::optional<int> pts = impl_->advance_video_pts();
|
||||
|
||||
if (!pts)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<IndexedVideoFrame>(width, height, *pts);
|
||||
}
|
||||
|
||||
void AVRecorder::push_indexed_video_frame(IndexedVideoFrame::instance_t frame)
|
||||
{
|
||||
auto _ = impl_->queue_guard();
|
||||
|
||||
impl_->video_queue_.vec_.emplace_back(std::move(frame));
|
||||
impl_->wake_up_worker();
|
||||
}
|
||||
|
|
@ -119,7 +119,15 @@ Impl::QueueState Impl::encode_queues()
|
|||
};
|
||||
|
||||
auto encode_audio = [this](auto copy) { audio_encoder_->encode(copy); };
|
||||
auto encode_video = [this](auto copy) {};
|
||||
auto encode_video = [this](auto copy)
|
||||
{
|
||||
for (auto& p : copy)
|
||||
{
|
||||
auto frame = convert_indexed_video_frame(*p);
|
||||
|
||||
video_encoder_->encode(std::move(frame));
|
||||
}
|
||||
};
|
||||
|
||||
check(audio_queue_, encode_audio);
|
||||
check(video_queue_, encode_video);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue