Minor fixes, Alternative rendering mode

This commit is contained in:
Niellune
2026-03-12 20:13:45 +02:00
parent 570695fbcf
commit cef813bcd6
14 changed files with 157 additions and 46 deletions
+41 -17
View File
@@ -34,7 +34,8 @@ static KeySetting<int> *keyMap[] = {
&Settings::keyReject,
&Settings::keyVideoFocus,
&Settings::keyVideoRelease,
&Settings::keyNavFocus};
&Settings::keyNavFocus,
&Settings::keyNavRelease};
static constexpr size_t keyMapSize = sizeof(keyMap) / sizeof(keyMap[0]);
@@ -85,10 +86,7 @@ void Application::start(const char *title)
std::cout << "[App] Initialising" << std::endl;
// Create SDL window centered on screen
if (Settings::fastScale)
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
else
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, Settings::fastScale ? "nearest" : "best");
// Prepare window, show it in headless to avoid blinking, otherwise hidden untill iniailised
bool fullsize = Settings::isFullscreen() || Settings::isHeadless();
@@ -111,10 +109,22 @@ void Application::start(const char *title)
Uint32 flags = SDL_RENDERER_ACCELERATED;
if (Settings::vsync)
flags |= SDL_RENDERER_PRESENTVSYNC;
SDL_SetHint(SDL_HINT_RENDER_DRIVER, Settings::renderDriver.value.c_str());
_renderer = SDL_CreateRenderer(_window, -1, flags);
if (!_renderer)
throw std::runtime_error(std::string("SDL can't create renderer > ") + SDL_GetError());
SDL_RendererInfo rendererInfo{};
if (SDL_GetRendererInfo(_renderer, &rendererInfo) == 0)
{
std::cout << "[App] Renderer: " << rendererInfo.name
<< " (" << ((rendererInfo.flags & SDL_RENDERER_ACCELERATED) ? "accelerated" : "software")
<< ", " << ((rendererInfo.flags & SDL_RENDERER_PRESENTVSYNC) ? "vsync" : "no-vsync")
<< ")" << std::endl;
}
// Register additional events
_evtBase = SDL_RegisterEvents(2);
if (_evtBase == (Uint32)-1)
@@ -204,7 +214,7 @@ bool Application::processSystemEvent(const SDL_Event &e)
_state.connected = e.user.code != 0;
_state.frameRendered = false;
_state.dirty = true;
_state.requestFrame = -1;
_state.requestFrame = 0;
_state.flushBuffers = _state.connected;
return true;
}
@@ -324,7 +334,8 @@ void Application::loop()
AVFrame *frame = nullptr;
uint32_t frameid = 0;
uint32_t latestFrameid = 0;
uint32_t frameTargetTime = 1000 / Settings::fps;
uint32_t requestFrameId = 0;
uint32_t frameTargetTime = Settings::fps > 0 ? 1000 / Settings::fps : 1000;
int frameDelay = 0;
while (_active)
{
@@ -339,18 +350,20 @@ void Application::loop()
std::cout << "[App] Frame drop " << frameid - latestFrameid - 1 << " on " << frameid << std::endl;
latestFrameid = frameid;
_state.dirty = false;
if (_state.requestFrame >= 0)
_state.requestFrame++;
}
videoBuffer.consume();
}
if (_state.requestFrame > 0 && Settings::forceRedraw > 0)
if (_state.requestFrame > 0 && Settings::forceRedraw > 0 && ++_state.requestFrame > Settings::forceRedraw)
{
if (_state.requestFrame <= Settings::forceRedraw)
if (latestFrameid == requestFrameId)
{
protocol.requestKeyframe();
_state.requestFrame = 1;
requestFrameId = latestFrameid;
}
else
_state.requestFrame = -1;
_state.requestFrame = 0;
}
}
@@ -365,7 +378,10 @@ void Application::loop()
else
{
if (processFrameEvents(protocol, interface) && Settings::forceRedraw > 0)
{
_state.requestFrame = 1;
requestFrameId = latestFrameid;
}
}
if (_state.flushBuffers)
@@ -375,15 +391,23 @@ void Application::loop()
videoBuffer.reset();
}
if (_active)
if (_active && !Settings::vsync)
{
Uint32 frameEnd = SDL_GetTicks();
frameDelay = frameTargetTime - (frameEnd - frameStart);
SDL_Delay(frameDelay > 0 ? frameDelay : 1);
frameStart += frameTargetTime;
frameDelay = frameTargetTime - (frameEnd - frameStart);
if(latestFrameid > 0 && latestFrameid != videoBuffer.latestId())
{
SDL_Delay(1);
frameStart = frameEnd;
}
else
{
SDL_Delay(frameDelay > 0 ? frameDelay : 1);
frameStart += frameTargetTime;
}
}
}
if (!Settings::isHeadless())
SDL_HideWindow(_window);
}
}
+21 -1
View File
@@ -49,6 +49,13 @@ AVCodecContext *Decoder::load_codec(AVCodecID codec_id)
AVCodecContext *result = nullptr;
// Try hardware-accelerated decoders by iterating registered codecs
// NOTE: simply opening a codec with AV_CODEC_CAP_HARDWARE is not sufficient
// on platforms such as V4L2M2M. A proper hwdevice context and get_format
// callback must be setup so that AVFrames reference driver buffers instead
// of being converted to system memory. This implementation currently
// only picks a hardware-capable codec but still operates in software mode.
// Fixing this will eliminate an extra copy and allow true GPUaccelerated
// decoding on the Pi.
while ((codec = av_codec_iterate(&iter)) && Settings::hwDecode)
{
if (!av_codec_is_decoder(codec) || codec->id != codec_id)
@@ -115,7 +122,7 @@ void Decoder::runner()
// Load codec context
_context = load_codec(_codecId);
if (_status.null(_context, ("Can't find decoder for codec " + _codecId)))
if (_status.null(_context, std::string("Can't find decoder for codec ") + avcodec_get_name(_codecId)))
return;
std::string codec = _context->codec->name;
@@ -203,4 +210,17 @@ void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPack
}
}
}
// push null packet to flush decoder and drain delayed frames
if (_context)
{
avcodec_send_packet(context, nullptr);
while (avcodec_receive_frame(context, frame) == 0)
{
AVFrame *out = _vb->write(counter++);
av_frame_unref(out);
av_frame_move_ref(out, frame);
_vb->commit();
}
}
}
+1
View File
@@ -6,6 +6,7 @@
#include <iostream>
#include <algorithm>
#include <stdexcept>
#include <type_traits>
// Base interface for any one setting
class ISetting
+1
View File
@@ -1,5 +1,6 @@
#include <string>
#include <iostream>
#include <memory>
#include "helper/functions.h"
+1
View File
@@ -22,6 +22,7 @@ Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t paddi
Protocol::~Protocol()
{
stop();
}
void Protocol::start(uint32_t evtStatus, uint32_t evtPhone)
+1 -1
View File
@@ -9,7 +9,7 @@
Recorder::Recorder(uint16_t buffSize)
: _sender(nullptr), _active(false), _device(0), _data(buffSize)
: _sender(nullptr), _active(false), _data(buffSize)
{
}
-1
View File
@@ -46,7 +46,6 @@ private:
IAudioSender *_sender;
std::atomic<bool> _active;
std::thread _thread;
SDL_AudioDeviceID _device;
AtomicQueue<AudioChunk> _data;
};
+54 -12
View File
@@ -1,4 +1,5 @@
#include "renderer.h"
#include <cstring>
#include <iostream>
#include "settings.h"
#include "helper/functions.h"
@@ -140,12 +141,6 @@ SDL_Rect RendererImage::draw(SDL_Renderer *renderer, int w, int h)
return dst;
}
const Renderer::FormatMapping Renderer::_mapping[] = {
{AV_PIX_FMT_RGB24, SDL_PIXELFORMAT_RGB24, &Renderer::rgb, "RGB24"},
{AV_PIX_FMT_YUV420P, SDL_PIXELFORMAT_IYUV, &Renderer::yuv, "YUV420P"},
{AV_PIX_FMT_YUVJ420P, SDL_PIXELFORMAT_IYUV, &Renderer::yuv, "YUVJ420P"},
{AV_PIX_FMT_NV12, SDL_PIXELFORMAT_NV12, &Renderer::nv, "NV12"}};
Renderer::Renderer(SDL_Renderer *renderer)
: xScale(0),
yScale(0),
@@ -158,6 +153,12 @@ Renderer::Renderer(SDL_Renderer *renderer)
_sws(nullptr),
_frame(nullptr)
{
if (Settings::alternativeRendering)
{
_mapping[1].function = &Renderer::yuvAlternative;
_mapping[2].function = &Renderer::yuvAlternative;
_mapping[3].function = &Renderer::nvAlternative;
}
}
Renderer::~Renderer()
@@ -298,6 +299,25 @@ void Renderer::nv(AVFrame *frame)
frame->data[1], frame->linesize[1]);
}
void Renderer::nvAlternative(AVFrame *frame)
{
uint8_t *pixels = nullptr;
int pitch = 0;
if (SDL_LockTexture(_texture, nullptr, (void **)&pixels, &pitch) != 0)
return;
// Y plane
for (int i = 0; i < frame->height; i++)
memcpy(pixels + i * pitch, frame->data[0] + i * frame->linesize[0], frame->width);
// UV interleaved plane (half height, full width)
uint8_t *uv = pixels + pitch * frame->height;
for (int i = 0; i < frame->height / 2; i++)
memcpy(uv + i * pitch, frame->data[1] + i * frame->linesize[1], frame->width);
SDL_UnlockTexture(_texture);
}
void Renderer::yuv(AVFrame *frame)
{
SDL_UpdateYUVTexture(
@@ -308,6 +328,30 @@ void Renderer::yuv(AVFrame *frame)
frame->data[2], frame->linesize[2]);
}
void Renderer::yuvAlternative(AVFrame *frame)
{
uint8_t *pixels = nullptr;
int pitch = 0;
if (SDL_LockTexture(_texture, nullptr, (void **)&pixels, &pitch) != 0)
return;
// Y plane
for (int i = 0; i < frame->height; i++)
memcpy(pixels + i * pitch, frame->data[0] + i * frame->linesize[0], frame->width);
// U plane
uint8_t *u = pixels + pitch * frame->height;
for (int i = 0; i < frame->height / 2; i++)
memcpy(u + i * (pitch / 2), frame->data[1] + i * frame->linesize[1], frame->width / 2);
// V plane
uint8_t *v = u + (pitch / 2) * (frame->height / 2);
for (int i = 0; i < frame->height / 2; i++)
memcpy(v + i * (pitch / 2), frame->data[2] + i * frame->linesize[2], frame->width / 2);
SDL_UnlockTexture(_texture);
}
void Renderer::scale(AVFrame *frame)
{
// Scale frame to output format
@@ -317,10 +361,8 @@ void Renderer::scale(AVFrame *frame)
_frame->data,
_frame->linesize);
SDL_UpdateYUVTexture(
_texture,
nullptr,
_frame->data[0], _frame->linesize[0],
_frame->data[1], _frame->linesize[1],
_frame->data[2], _frame->linesize[2]);
if (Settings::alternativeRendering)
yuvAlternative(_frame);
else
yuv(_frame);
}
+7 -1
View File
@@ -75,7 +75,9 @@ private:
void rgb(AVFrame *frame);
void nv(AVFrame *frame);
void nvAlternative(AVFrame *frame);
void yuv(AVFrame *frame);
void yuvAlternative(AVFrame *frame);
void scale(AVFrame *frame);
SDL_Texture *_texture;
@@ -85,7 +87,11 @@ private:
DrawFuncType _render;
SwsContext *_sws;
AVFrame *_frame;
static const FormatMapping _mapping[];
FormatMapping _mapping[4] = {
{AV_PIX_FMT_RGB24, SDL_PIXELFORMAT_RGB24, &Renderer::rgb, "RGB24"},
{AV_PIX_FMT_YUV420P, SDL_PIXELFORMAT_IYUV, &Renderer::yuv, "YUV420P"},
{AV_PIX_FMT_YUVJ420P, SDL_PIXELFORMAT_IYUV, &Renderer::yuv, "YUVJ420P"},
{AV_PIX_FMT_NV12, SDL_PIXELFORMAT_NV12, &Renderer::nv, "NV12"}};
};
#endif /* SRC_RENDERER */
+6 -4
View File
@@ -42,6 +42,7 @@ public:
static inline Setting<int> forceRedraw{"force-redraw", 0};
static inline Setting<float> aspectCorrection{"aspect-correction", 1};
static inline Setting<bool> fastScale{"fast-render-scale", false};
static inline Setting<bool> alternativeRendering{"alternative-rendering", false};
static inline Setting<int> videoQueue{"video-buffer-size", 32};
static inline Setting<int> audioQueue{"audio-buffer-size", 16};
static inline Setting<int> audioDelay{"audio-buffer-wait", 2};
@@ -49,6 +50,7 @@ public:
static inline Setting<float> audioFade{"audio-fade", 0.3};
static inline Setting<int> audioBuffer{"audio-buffer-samples", 2048};
static inline Setting<std::string> audioDriver{"audio-driver", ""};
static inline Setting<std::string> renderDriver{"renderer-driver", "auto"};
static inline Setting<std::string> onConnect{"on-connect-script", ""};
static inline Setting<std::string> onDisconnect{"on-disconnect-script", ""};
@@ -56,8 +58,8 @@ public:
static inline KeySetting<int> keySiri{"key-siri", 115, 5};
static inline KeySetting<int> keyNightOn{"key-nightmode-on", 122, 16};
static inline KeySetting<int> keyNightOff{"key-nightmode-off", 120, 17};
static inline KeySetting<int> keyLeft{"key-left", 1073741903, 100};
static inline KeySetting<int> keyRight{"key-right", 1073741904, 101};
static inline KeySetting<int> keyLeft{"key-left", 1073741904, 100};
static inline KeySetting<int> keyRight{"key-right", 1073741903, 101};
static inline KeySetting<int> keyEnter{"key-enter", 13, 104};
static inline KeySetting<int> keyEnterUp{"key-enterup", 0, 105};
static inline KeySetting<int> keyBack{"key-back", 8, 106};
@@ -79,8 +81,8 @@ public:
// Debug section
static inline Setting<int> protocolDebug{"protocol-debug", 0};
static inline Setting<float> codecLowDelay{"decode-low-delay", true};
static inline Setting<float> codecFast{"decode-fast", false};
static inline Setting<bool> codecLowDelay{"decode-low-delay", true};
static inline Setting<bool> codecFast{"decode-fast", false};
static bool load(const std::string &filename);
static void print();
+2 -2
View File
@@ -78,8 +78,8 @@ public:
unique_lock<std::mutex> lock(_mtx);
_lock.wait(lock, [&]
{ return _count > count || !waitFlag; });
return waitFlag;
{ return _count > count || !waitFlag.load(); });
return waitFlag.load();
}
void clear()
+8
View File
@@ -42,6 +42,14 @@ public:
}
}
uint32_t latestId()
{
int index = _latest.load();
if (index < 0)
return 0;
return _ids[index];
}
bool latest(AVFrame **frame, uint32_t *id)
{
_reading.store(_latest.load());