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:
James R 2023-02-12 05:35:12 -08:00
parent fa0071e785
commit 840c11577e
8 changed files with 142 additions and 3 deletions

View file

@ -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));
}

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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 <>

View 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();
}

View file

@ -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);