mirror of
https://github.com/Cuteminded/jellyframe.git
synced 2026-10-18 19:00:30 +02:00
106 lines
3.5 KiB
C++
106 lines
3.5 KiB
C++
#include "render_worker.h"
|
|
#include <chrono>
|
|
#include <future>
|
|
|
|
RenderWorker::~RenderWorker() {
|
|
stop();
|
|
}
|
|
|
|
int RenderWorker::start(mpv_handle *player) {
|
|
std::promise<int> initialized;
|
|
auto result = initialized.get_future();
|
|
stopping = false;
|
|
dirty = false;
|
|
render_error = 0;
|
|
dimensions = (uint64_t(1920) << 32) | 1080;
|
|
worker = std::thread([this, player, ready = std::move(initialized)]() mutable {
|
|
mpv_render_context *renderer = nullptr;
|
|
mpv_render_param params[] = {
|
|
{MPV_RENDER_PARAM_API_TYPE, const_cast<char *>(MPV_RENDER_API_TYPE_SW)},
|
|
{MPV_RENDER_PARAM_INVALID, nullptr}
|
|
};
|
|
int error = mpv_render_context_create(&renderer, player, params);
|
|
if (error >= 0) mpv_render_context_set_update_callback(renderer, wakeup, this);
|
|
ready.set_value(error);
|
|
if (error < 0) return;
|
|
render_loop(renderer);
|
|
mpv_render_context_set_update_callback(renderer, nullptr, nullptr);
|
|
mpv_render_context_free(renderer);
|
|
});
|
|
return result.get();
|
|
}
|
|
|
|
void RenderWorker::wakeup(void *context) {
|
|
auto *self = static_cast<RenderWorker *>(context);
|
|
self->dirty = true;
|
|
self->wake.notify_one();
|
|
}
|
|
|
|
void RenderWorker::resize(int width, int height) {
|
|
dimensions = (uint64_t(width) << 32) | uint32_t(height);
|
|
wakeup(this);
|
|
}
|
|
|
|
void RenderWorker::render_loop(mpv_render_context *renderer) {
|
|
VideoFrame frame;
|
|
while (!stopping) {
|
|
{
|
|
std::unique_lock<std::mutex> lock(wake_mutex);
|
|
wake.wait_for(lock, std::chrono::milliseconds(20), [this] { return stopping || dirty; });
|
|
}
|
|
if (stopping) break;
|
|
if (!dirty.exchange(false)) continue;
|
|
if (!(mpv_render_context_update(renderer) & MPV_RENDER_UPDATE_FRAME)) continue;
|
|
auto size = dimensions.load();
|
|
frame.width = int(size >> 32);
|
|
frame.height = int(size & 0xffffffff);
|
|
if (frame.pixels.resize(int64_t(frame.width) * frame.height * 4) != 0) {
|
|
render_error = MPV_ERROR_NOMEM;
|
|
break;
|
|
}
|
|
int extent[] = {frame.width, frame.height};
|
|
size_t stride = size_t(frame.width) * 4;
|
|
mpv_render_param params[] = {
|
|
{MPV_RENDER_PARAM_SW_SIZE, extent},
|
|
{MPV_RENDER_PARAM_SW_FORMAT, const_cast<char *>("rgba")},
|
|
{MPV_RENDER_PARAM_SW_STRIDE, &stride},
|
|
{MPV_RENDER_PARAM_SW_POINTER, frame.pixels.ptrw()},
|
|
{MPV_RENDER_PARAM_INVALID, nullptr}
|
|
};
|
|
auto render_started = std::chrono::steady_clock::now();
|
|
int result = mpv_render_context_render(renderer, params);
|
|
frame.render_time_us = std::chrono::duration_cast<std::chrono::microseconds>(
|
|
std::chrono::steady_clock::now() - render_started).count();
|
|
if (result < 0) {
|
|
render_error = result;
|
|
break;
|
|
}
|
|
{
|
|
std::lock_guard<std::mutex> lock(frame_mutex);
|
|
std::swap(pending, frame);
|
|
frame_ready = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool RenderWorker::take_frame(VideoFrame &frame) {
|
|
std::lock_guard<std::mutex> lock(frame_mutex);
|
|
if (!frame_ready) return false;
|
|
std::swap(frame, pending);
|
|
frame_ready = false;
|
|
return true;
|
|
}
|
|
|
|
int RenderWorker::error() const {
|
|
return render_error.load();
|
|
}
|
|
|
|
void RenderWorker::stop() {
|
|
stopping = true;
|
|
wake.notify_one();
|
|
if (worker.joinable()) worker.join();
|
|
std::lock_guard<std::mutex> lock(frame_mutex);
|
|
pending = VideoFrame();
|
|
frame_ready = false;
|
|
}
|