Simplify video buffer, optimise rendering timings, debug overlay

This commit is contained in:
Niellune
2026-03-24 00:16:33 +02:00
parent 868f30e606
commit 1c21326458
13 changed files with 293 additions and 269 deletions
+67 -25
View File
@@ -1,6 +1,7 @@
#include "application.h"
#include <SDL2/SDL_ttf.h>
#include <cstdio>
#include "struct/video_buffer.h"
#include "common/logger.h"
@@ -42,6 +43,8 @@ Application::Application(/* args */) : _window(nullptr),
{
log_v("Creating");
_debug = Settings::debugOverlay;
if (!setAudioDriver())
throw std::runtime_error("Unsupported audio driver " + std::string(Settings::audioDriver.value));
@@ -120,10 +123,7 @@ void Application::start(const char *title)
}
log_v("Starting");
if (Settings::doubleBuffer)
loop<VideoBufferDouble>();
else
loop<VideoBuffer>();
loop();
log_v("Stopped");
}
@@ -192,6 +192,11 @@ bool Application::processSystemEvent(const SDL_Event &e)
_active = false;
return true;
}
case SDLK_d:
{
_debug = !_debug;
return true;
}
}
}
@@ -278,7 +283,6 @@ bool Application::processFrameEvents(AtomicQueue<Message> &queue, Renderer &rend
return result;
}
template <class Buffer>
void Application::loop()
{
// Prepare home screen
@@ -298,12 +302,11 @@ void Application::loop()
SDL_ShowWindow(_window);
interface.drawHome(true, PROTOCOL_STATUS_UNKNOWN);
Buffer videoBuffer;
Connector protocol;
Decoder<Buffer> decoder;
Connection protocol;
Decoder decoder;
PcmAudio audioMain("main"), audioAux("aux");
decoder.start(&protocol.videoStream, videoBuffer, AV_CODEC_ID_H264);
decoder.start(&protocol.videoStream, AV_CODEC_ID_H264);
audioMain.start(&protocol.audioStreamMain);
audioAux.start(&protocol.audioStreamAux, &audioMain);
protocol.start(&_state.deviceStatus);
@@ -314,9 +317,14 @@ void Application::loop()
uint32_t frameid = 0;
uint32_t latestFrameid = 0;
uint32_t frameTargetTime = Settings::fps > 0 ? 1000 / Settings::fps : 1000;
uint32_t delay = 0;
uint32_t dropframes = 0;
int skipEvents = 0;
int frameTime = 0;
while (_active)
{
bool late = false;
if (_state.deviceStatus != _state.previousdeviceStatus)
{
// On connect/disconnect
@@ -330,24 +338,36 @@ void Application::loop()
if (_state.deviceStatus == PROTOCOL_STATUS_CONNECTED)
{
decoder.flush();
videoBuffer.reset();
decoder.buffer.reset();
}
_state.previousdeviceStatus = _state.deviceStatus;
}
if (_state.deviceStatus == PROTOCOL_STATUS_CONNECTED && _state.showVideo)
{
if (videoBuffer.latest(&frame, &frameid) && frame && (frameid != latestFrameid || _state.dirty))
delay = 0;
while (!_state.dirty && decoder.buffer.latestId() == latestFrameid && ++delay < frameTargetTime)
{
if (interface.render(frame))
SDL_Delay(1);
}
if (decoder.buffer.consume(&frame, &frameid))
{
bool newFrame = frameid != latestFrameid;
if (newFrame || _state.dirty)
{
_state.frameRendered = true;
if (!_state.dirty && (frameid != latestFrameid + 1))
log_d("Frame drop %d on %d", frameid - latestFrameid - 1, frameid);
latestFrameid = frameid;
_state.dirty = false;
if (interface.render(frame))
{
_state.frameRendered = true;
_state.dirty = false;
if (latestFrameid > 0 && frameid - latestFrameid > 1)
{
dropframes += frameid - latestFrameid - 1;
log_d("Frame drop %d on %d total %d", frameid - latestFrameid - 1, frameid, dropframes);
}
latestFrameid = frameid;
}
}
videoBuffer.consume();
}
if (_state.requestFrame > 0 && Settings::forceRedraw > 0)
@@ -372,29 +392,51 @@ void Application::loop()
}
else
{
if (latestFrameid < 0 || latestFrameid == videoBuffer.latestId() || ++skipEvents > Settings::eventsSkip)
late = decoder.buffer.latestId() - latestFrameid > 1;
if(!late || ++skipEvents > Settings::eventsSkip)
{
skipEvents = 0;
if (processFrameEvents(protocol.writeQueue, interface) && Settings::forceRedraw > 0)
{
_state.requestFrame = 1;
}
skipEvents = 0;
}
}
if (_debug)
{
char debugBuffer[256];
std::snprintf(debugBuffer, sizeof(debugBuffer),
"FRAME: %u / %u [%d] droped %d\n"
"TIME: %d delay %d\n"
"VIDEO: %u\n"
"AUDIO-MAIN: %u\n"
"AUDIO-AUX: %u\n"
"OUT: %u",
latestFrameid,
decoder.buffer.latestId(),
decoder.buffer.latestId() - latestFrameid, dropframes,
frameTime, delay,
protocol.videoStream.count(),
protocol.audioStreamMain.count(),
protocol.audioStreamAux.count(),
protocol.writeQueue.count());
interface.debug(debugBuffer);
}
if (_active && !Settings::vsync)
{
Uint32 frameEnd = SDL_GetTicks();
int frameDelay = frameTargetTime - (frameEnd - frameStart);
if (latestFrameid > 0 && latestFrameid != videoBuffer.latestId())
frameTime = frameEnd - frameStart;
int frameDelay = frameTargetTime - frameTime;
if (frameDelay <= 0 || decoder.buffer.latestId() - latestFrameid > 1)
{
SDL_Delay(1);
frameStart = frameEnd;
}
else
{
SDL_Delay(frameDelay > 0 ? frameDelay : 1);
frameStart += frameTargetTime;
SDL_Delay(frameDelay);
frameStart += frameDelay;
}
}
}
+2 -2
View File
@@ -5,7 +5,7 @@
#include "protocol/protocol_const.h"
#include "connector.h"
#include "protocol/connection.h"
#include "renderer.h"
#define REDRAW_REQUEST 5
@@ -36,7 +36,6 @@ private:
bool processSystemEvent(const SDL_Event &e);
bool processFrameEvents(AtomicQueue<Message> &queue, Renderer &renderer);
template <class Buffer>
void loop();
SDL_Window *_window;
@@ -46,6 +45,7 @@ private:
State _state;
int _width;
int _height;
bool _debug;
};
#endif /* SRC_APPLICATION */
+28 -30
View File
@@ -5,32 +5,32 @@
#include "common/functions.h"
#include "settings.h"
template <class Buffer>
Decoder<Buffer>::Decoder()
: _context(nullptr)
Decoder::Decoder()
: buffer(Settings::renderingBuffer),
_context(nullptr),
_active(false),
_data(nullptr)
{
}
template <class Buffer>
Decoder<Buffer>::~Decoder()
Decoder::~Decoder()
{
stop();
}
template <class Buffer>
void Decoder<Buffer>::start(AtomicQueue<Message> *data, Buffer &vb, AVCodecID codecId)
void Decoder::start(AtomicQueue<Message> *data, AVCodecID codecId)
{
if (_active)
stop();
_vb = &vb;
buffer.reset();
_data = data;
_codecId = codecId;
_active = true;
_thread = std::thread(&Decoder::runner, this);
}
template <class Buffer>
void Decoder<Buffer>::stop()
void Decoder::stop()
{
if (!_active)
return;
@@ -40,16 +40,14 @@ void Decoder<Buffer>::stop()
_thread.join();
}
template <class Buffer>
void Decoder<Buffer>::flush()
void Decoder::flush()
{
if (_context)
avcodec_flush_buffers(_context);
}
// Initialize and select the best decoder (try HW first, then SW)
template <class Buffer>
AVCodecContext *Decoder<Buffer>::load_codec(AVCodecID codec_id)
AVCodecContext *Decoder::load_codec(AVCodecID codec_id)
{
void *iter = nullptr;
const AVCodec *codec = nullptr;
@@ -122,8 +120,7 @@ AVCodecContext *Decoder<Buffer>::load_codec(AVCodecID codec_id)
return result;
}
template <class Buffer>
void Decoder<Buffer>::runner()
void Decoder::runner()
{
// Set thread name
setThreadName("video-decoder");
@@ -166,8 +163,7 @@ void Decoder<Buffer>::runner()
_context = nullptr;
}
template <class Buffer>
void Decoder<Buffer>::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPacket *packet, AVFrame *frame)
void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPacket *packet, AVFrame *frame)
{
uint32_t counter = 0;
@@ -214,14 +210,16 @@ void Decoder<Buffer>::loop(AVCodecContext *context, AVCodecParserContext *parser
log_w("Can't decode packet > %s", avErrorText(send_ret).c_str());
continue;
}
// Receive decoded frames
while (avcodec_receive_frame(context, frame) == 0 && _active)
{
AVFrame *out = _vb->write(counter++);
av_frame_unref(out);
av_frame_move_ref(out, frame);
_vb->commit();
AVFrame *out = buffer.write(counter++);
if (out)
{
av_frame_unref(out);
av_frame_move_ref(out, frame);
buffer.commit();
}
}
}
}
@@ -232,13 +230,13 @@ void Decoder<Buffer>::loop(AVCodecContext *context, AVCodecParserContext *parser
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();
AVFrame *out = buffer.write(counter++);
if (out)
{
av_frame_unref(out);
av_frame_move_ref(out, frame);
buffer.commit();
}
}
}
}
template class Decoder<VideoBuffer>;
template class Decoder<VideoBufferDouble>;
+5 -11
View File
@@ -14,18 +14,18 @@ extern "C"
#include "struct/atomic_queue.h"
#include "protocol/message.h"
template <class Buffer>
class Decoder
{
public:
Decoder();
~Decoder();
void start(AtomicQueue<Message> *data, Buffer &vb, AVCodecID codecId);
void start(AtomicQueue<Message> *data, AVCodecID codecId);
void stop();
void flush();
VideoBuffer buffer;
private:
void runner();
void loop(AVCodecContext *context, AVCodecParserContext *parser, AVPacket *packet, AVFrame *frame);
@@ -34,14 +34,8 @@ private:
std::thread _thread;
AVCodecContext* _context;
AVCodecID _codecId;
std::atomic<bool> _active = false;
AtomicQueue<Message> *_data = nullptr;
Buffer *_vb = nullptr;
std::atomic<bool> _active;
AtomicQueue<Message> *_data;
};
extern template class Decoder<VideoBuffer>;
extern template class Decoder<VideoBufferDouble>;
#endif /* SRC_DECODER */
+61 -1
View File
@@ -6,11 +6,14 @@
#include "protocol/protocol_const.h"
Interface::Interface(SDL_Renderer *renderer)
: Renderer(renderer), _state(0),
: Renderer(renderer),
_state(0),
_debug(false),
_textDongle(font, font_len, Settings::fontSize),
_textInit(font, font_len, Settings::fontSize),
_textConnect(font, font_len, Settings::fontSize),
_textLaunch(font, font_len, Settings::fontSize),
_textDebug(font, font_len, 15),
_mainImage(background, background_len)
{
}
@@ -19,6 +22,31 @@ Interface::~Interface()
{
}
bool Interface::render(AVFrame *frame)
{
if(!frame)
return false;
if (_render == nullptr || frame->width != _textureWidth || frame->height != _textureHeight)
{
clear();
if (!prepare(frame, Settings::width, Settings::height))
return false;
}
(this->*_render)(frame);
SDL_RenderCopy(_renderer, _texture, &_sourceRect, nullptr);
if (_debug)
{
drawDebug();
_debug = false;
}
SDL_RenderPresent(_renderer);
return true;
}
bool Interface::drawHome(bool force, int state)
{
if (state == _state && !force)
@@ -49,3 +77,35 @@ bool Interface::drawHome(bool force, int state)
SDL_RenderPresent(_renderer);
return true;
}
void Interface::debug(const char *text)
{
_debugText = text ? text : "";
_debug = true;
}
void Interface::drawDebug()
{
if (_debugText.empty())
return;
constexpr int padding = 8;
constexpr int lineSpacing = 2;
const SDL_Color debugColor = {255, 0, 255, 255};
size_t lineStart = 0;
int y = padding;
while (lineStart <= _debugText.size())
{
size_t lineEnd = _debugText.find('\n', lineStart);
std::string line = _debugText.substr(lineStart, lineEnd - lineStart);
if (_textDebug.prepare(_renderer, line, debugColor))
_textDebug.draw(_renderer, padding, y);
y += _textDebug.height + lineSpacing;
if (lineEnd == std::string::npos)
break;
lineStart = lineEnd + 1;
}
}
+9 -2
View File
@@ -9,15 +9,22 @@ class Interface : public Renderer
public:
Interface(SDL_Renderer *renderer);
~Interface();
bool render(AVFrame *frame);
bool drawHome(bool force, int state);
void debug(const char *text);
private:
void drawDebug();
int _state;
bool _debug;
RendererText _textDongle;
RendererText _textInit;
RendererText _textConnect;
RendererText _textLaunch;
RendererImage _mainImage;
RendererText _textLaunch;
RendererText _textDebug;
RendererImage _mainImage;
std::string _debugText;
};
#endif /* SRC_INTERFACE */
@@ -1,4 +1,4 @@
#include "connector.h"
#include "connection.h"
#include <stdexcept>
#include <iostream>
@@ -10,7 +10,7 @@
#include "common/functions.h"
#include "settings.h"
Connector::Connector()
Connection::Connection()
: writeQueue(WRITE_QUEUE_SIZE),
videoStream(Settings::videoQueue),
audioStreamMain(Settings::audioQueue),
@@ -44,10 +44,12 @@ Connector::Connector()
_cipher = nullptr;
log_w("Can't initialise cypher for encryption > Unknown error");
}
log_v("Created");
}
Connector::~Connector()
Connection::~Connection()
{
log_v("Destroying");
stop();
if (_cipher)
@@ -61,24 +63,29 @@ Connector::~Connector()
libusb_exit(_context);
_context = nullptr;
}
log_v("Destroyed");
}
void Connector::start(atomic<int8_t>* statusHandler)
void Connection::start(atomic<int8_t> *statusHandler)
{
_statusHandler = statusHandler;
if (_active)
return;
log_v("Starting");
_active = true;
_thread = std::thread(&Connector::mainLoop, this);
_thread = std::thread(&Connection::mainLoop, this);
}
void Connector::stop()
void Connection::stop()
{
if (!_active)
return;
log_v("Stopping");
_active = false;
writeQueue.notify();
state(PROTOCOL_STATUS_INITIALISING);
@@ -86,15 +93,17 @@ void Connector::stop()
if (_thread.joinable())
_thread.join();
_statusHandler = nullptr;
_statusHandler = nullptr;
}
void Connector::mainLoop()
void Connection::mainLoop()
{
// Set thread name
setThreadName("usb-write");
state(PROTOCOL_STATUS_LINKING);
log_d("USB writing thread started");
while (_active)
{
libusb_device_handle *handler = libusb_open_device_with_vid_pid(_context, Settings::vendorid, Settings::productid);
@@ -119,12 +128,12 @@ void Connector::mainLoop()
state(PROTOCOL_STATUS_ONLINE);
log_i("Device connected %d:%d speed: %d", libusb_get_bus_number(device), libusb_get_device_address(device), libusb_get_device_speed(device));
_reader.start(_context, handler, epIn, this);
onConnect();
onDeviceConnect();
writeLoop(handler, epOut);
_connected = false;
onDisconnect();
onDeviceDisconnect();
_reader.stop();
}
@@ -136,7 +145,7 @@ void Connector::mainLoop()
}
}
void Connector::writeLoop(libusb_device_handle *handler, uint8_t ep)
void Connection::writeLoop(libusb_device_handle *handler, uint8_t ep)
{
while (_active && _reader.active())
{
@@ -179,7 +188,7 @@ void Connector::writeLoop(libusb_device_handle *handler, uint8_t ep)
}
}
libusb_device *Connector::link(libusb_device_handle *handler, uint8_t *epIn, uint8_t *epOut)
libusb_device *Connection::link(libusb_device_handle *handler, uint8_t *epIn, uint8_t *epOut)
{
state(PROTOCOL_STATUS_LINKING);
@@ -210,7 +219,7 @@ libusb_device *Connector::link(libusb_device_handle *handler, uint8_t *epIn, uin
return device;
}
void Connector::setEncryption(bool enabled)
void Connection::setEncryption(bool enabled)
{
if (!enabled)
{
@@ -227,7 +236,7 @@ void Connector::setEncryption(bool enabled)
_ecnrypt = true;
}
bool Connector::fail(int status, const char *msg)
bool Connection::fail(int status, const char *msg)
{
if (status == 0)
return false;
@@ -236,7 +245,7 @@ bool Connector::fail(int status, const char *msg)
return true;
}
bool Connector::state(u_int8_t state)
bool Connection::state(u_int8_t state)
{
if (state == _state)
return false;
@@ -256,7 +265,7 @@ bool Connector::state(u_int8_t state)
return true;
}
if (state == PROTOCOL_STATUS_NO_DEVICE && (_nodeviceCount++ > 10 || _state >= PROTOCOL_STATUS_ONLINE))
if (state == PROTOCOL_STATUS_NO_DEVICE && (_nodeviceCount++ > 30 || _state >= PROTOCOL_STATUS_ONLINE))
{
_failCount = 0;
_state = state;
@@ -276,7 +285,7 @@ bool Connector::state(u_int8_t state)
return false;
}
void Connector::onConnect()
void Connection::onDeviceConnect()
{
int syncTime = std::time(nullptr);
int drivePosition = Settings::leftDrive ? 0 : 1; // 0==left, 1==right
@@ -314,7 +323,7 @@ void Connector::onConnect()
width = Settings::width * scale;
height = Settings::height * scale;
log_i("Requesting carplay %dx%d@%d, android auto %dx%d", Settings::width.value, Settings::height.value, Settings::fps.value, width, height);
log_i("Requesting carplay %dx%d@%d, android auto %dx%d", Settings::width.value, Settings::height.value, Settings::sourceFps.value, width, height);
if (Settings::encryption)
{
@@ -327,7 +336,7 @@ void Connector::onConnect()
if (Settings::dpi > 0)
send(Message::File("/tmp/screen_dpi", Settings::dpi));
send(Message::File("/etc/android_work_mode", 1));
send(Message::Init(Settings::width, Settings::height, Settings::fps));
send(Message::Init(Settings::width, Settings::height, Settings::sourceFps));
send(Message::String(
CMD_JSON_CONTROL,
"{\"syncTime\":%d,\"mediaDelay\":%d,\"drivePosition\":%d,"
@@ -347,19 +356,16 @@ void Connector::onConnect()
send(Message::Control(Settings::bluetoothAudio ? 22 : 23));
if (Settings::autoconnect)
send(Message::Control(1002));
if (Settings::onConnect.value.length() > 1)
execute(Settings::onConnect.value.c_str());
}
void Connector::onDisconnect()
void Connection::onDeviceDisconnect()
{
_recorder.stop();
if (Settings::onDisconnect.value.length() > 1)
execute(Settings::onDisconnect.value.c_str());
}
void Connector::onMessage(std::unique_ptr<Message> message)
void Connection::onMessage(std::unique_ptr<Message> message)
{
Status s = message->decrypt(_cipher);
if (s.failed())
@@ -388,12 +394,21 @@ void Connector::onMessage(std::unique_ptr<Message> message)
break;
case CMD_PLUGGED:
{
state(PROTOCOL_STATUS_CONNECTED);
if (Settings::onConnect.value.length() > 1)
execute(Settings::onConnect.value.c_str());
break;
}
case CMD_UNPLUGGED:
{
state(PROTOCOL_STATUS_ONLINE);
_recorder.stop();
if (Settings::onDisconnect.value.length() > 1)
execute(Settings::onDisconnect.value.c_str());
break;
}
case CMD_VIDEO_DATA:
{
@@ -23,12 +23,12 @@
#define WRITE_QUEUE_SIZE 256
#define ENCRYPTION_BASE "SkBRDy3gmrw1ieH0"
class Connector : public IMessageReceiver
class Connection : public IMessageReceiver
{
public:
Connector();
virtual ~Connector();
Connection();
virtual ~Connection();
void start(atomic<int8_t> *statusHandler);
void stop();
@@ -49,8 +49,8 @@ private:
void setEncryption(bool enabled);
bool fail(int status, const char *msg);
bool state(u_int8_t state);
void onConnect();
void onDisconnect();
void onDeviceConnect();
void onDeviceDisconnect();
Recorder _recorder;
AESCipher *_cipher;
-14
View File
@@ -165,20 +165,6 @@ Renderer::~Renderer()
clear();
}
bool Renderer::render(AVFrame *frame)
{
if (_render == nullptr || frame->width != _textureWidth || frame->height != _textureHeight)
{
clear();
if (!prepare(frame, Settings::width, Settings::height))
return false;
}
(this->*_render)(frame);
SDL_RenderCopy(_renderer, _texture, &_sourceRect, nullptr);
SDL_RenderPresent(_renderer);
return true;
}
bool Renderer::prepareTexture(uint32_t format, int width, int height)
{
_texture = SDL_CreateTexture(_renderer, format,
+10 -12
View File
@@ -50,16 +50,22 @@ public:
Renderer(SDL_Renderer *renderer);
~Renderer();
bool render(AVFrame *frame);
float xScale;
float yScale;
protected:
SDL_Renderer *_renderer;
private:
using DrawFuncType = void (Renderer::*)(AVFrame *);
SDL_Renderer *_renderer;
void clear();
bool prepare(AVFrame *frame, int targetWidth, int targetHeight);
SDL_Texture *_texture;
int _textureWidth;
int _textureHeight;
SDL_Rect _sourceRect;
DrawFuncType _render;
private:
struct FormatMapping
{
AVPixelFormat avFormat;
@@ -68,10 +74,7 @@ private:
std::string name;
};
void clear();
bool prepare(AVFrame *frame, int targetWidth, int targetHeight);
bool prepareTexture(uint32_t format, int width, int height);
void rgb(AVFrame *frame);
void nv(AVFrame *frame);
void nvAlternative(AVFrame *frame);
@@ -79,11 +82,6 @@ private:
void yuvAlternative(AVFrame *frame);
void scale(AVFrame *frame);
SDL_Texture *_texture;
int _textureWidth;
int _textureHeight;
SDL_Rect _sourceRect;
DrawFuncType _render;
SwsContext *_sws;
AVFrame *_frame;
FormatMapping _mapping[4] = {
+6 -5
View File
@@ -16,8 +16,8 @@ public:
static inline Setting<int> productid{"product-id", 5408};
static inline Setting<int> width{"width", 720};
static inline Setting<int> height{"height", 576};
static inline Setting<int> sourceFps{"source-fps", 50};
static inline Setting<int> fps{"fps", 50};
static inline Setting<int> sourceFps{"source-fps", 60};
static inline Setting<int> fps{"fps", 60};
static inline Setting<int> screenMode{"window-mode", 0};
static inline Setting<bool> cursor{"cursor", false};
static inline Setting<int> loglevel{"log-level", 2};
@@ -39,12 +39,12 @@ public:
static inline Setting<int> fontSize{"font-size", 30};
static inline Setting<bool> vsync{"vsync", false};
static inline Setting<bool> hwDecode{"hw-decode", true};
static inline Setting<bool> doubleBuffer{"double-buffered", true};
static inline Setting<int> renderingBuffer{"rendering-buffer", 5};
static inline Setting<int> eventsSkip{"draw-skip-events", 3};
static inline Setting<int> forceRedraw{"force-redraw", 0};
static inline Setting<int> eventsSkip{"draw-skip-events", 0};
static inline Setting<float> aspectCorrection{"aspect-correction", 1};
static inline Setting<std::string> renderDriver{"renderer-driver", ""};
static inline Setting<bool> alternativeRendering{"alternative-rendering", true};
static inline Setting<bool> alternativeRendering{"alternative-rendering", false};
static inline Setting<bool> fastScale{"fast-render-scale", false};
static inline Setting<int> usbQueue{"async-usb-calls", 16};
static inline Setting<int> usbTransferSize{"usb-buffer-size", 2048};
@@ -87,6 +87,7 @@ public:
// Debug section
static inline Setting<bool> codecLowDelay{"decode-low-delay", true};
static inline Setting<bool> codecFast{"decode-fast", true};
static inline Setting<bool> debugOverlay{"debug-overlay", false};
static bool load(const std::string &filename);
static void print();
+9 -9
View File
@@ -14,7 +14,7 @@ template <typename T>
class AtomicQueue
{
public:
AtomicQueue(size_t size)
AtomicQueue(uint16_t size)
: _size(size), _data(new unique_ptr<T>[size]), _first(0), _last(0), _count(0)
{
}
@@ -69,12 +69,12 @@ public:
return item;
}
bool has(size_t count)
bool has(uint16_t count)
{
return _count.load(std::memory_order_acquire) >= count;
}
bool wait(atomic<bool> &waitFlag, size_t count = 0)
bool wait(atomic<bool> &waitFlag, uint16_t count = 0)
{
unique_lock<std::mutex> lock(_mtx);
@@ -83,7 +83,7 @@ public:
return waitFlag.load(std::memory_order_acquire);
}
bool waitFor(atomic<bool> &waitFlag, uint32_t timeoutMs, size_t count = 0)
bool waitFor(atomic<bool> &waitFlag, uint32_t timeoutMs, uint16_t count = 0)
{
unique_lock<std::mutex> lock(_mtx);
_lock.wait_for(lock, std::chrono::milliseconds(timeoutMs), [&]
@@ -104,14 +104,14 @@ public:
_lock.notify_all();
}
size_t count() { return _count.load(std::memory_order_acquire); }
uint16_t count() { return _count.load(std::memory_order_acquire); }
private:
size_t _size;
uint16_t _size;
unique_ptr<unique_ptr<T>[]> _data;
size_t _first;
size_t _last;
atomic<size_t> _count;
uint16_t _first;
uint16_t _last;
atomic<uint16_t> _count;
mutex _mtx;
condition_variable _lock;
};
+52 -129
View File
@@ -13,17 +13,21 @@ extern "C"
class VideoBuffer
{
public:
VideoBuffer()
VideoBuffer(int8_t size) : _reading(-1), _writing(-1), _latest(-1), _size(size), _frames(nullptr), _ids(nullptr)
{
_writing = 0;
_reading.store(-1);
_latest.store(-1);
for (uint8_t i = 0; i < 3; ++i)
if (size < 3)
throw std::runtime_error("Minimum rendering buffer size is 3");
_frames = new AVFrame *[_size]();
_ids = new uint32_t[_size]();
for (uint8_t i = 0; i < _size; ++i)
{
_ids[i] = 0;
_frames[i] = av_frame_alloc();
if (!_frames[i])
{
dispose();
throw std::runtime_error("Failed to allocate AVFrame");
}
}
@@ -31,14 +35,7 @@ public:
~VideoBuffer() noexcept
{
for (uint8_t i = 0; i < 3; ++i)
{
if (_frames[i])
{
av_frame_free(&_frames[i]);
_frames[i] = nullptr;
}
}
dispose();
}
uint32_t latestId() const noexcept
@@ -49,34 +46,38 @@ public:
return _ids[static_cast<uint8_t>(index)];
}
bool latest(AVFrame **frame, uint32_t *id) noexcept
bool consume(AVFrame **frame, uint32_t *id) noexcept
{
const int8_t index = _latest.load(std::memory_order_acquire);
_reading.store(index, std::memory_order_seq_cst);
const int8_t latest = _latest.load(std::memory_order_acquire);
int8_t index = _reading.load(std::memory_order_relaxed);
if (index != latest)
{
index++;
if (index == _size)
index = 0;
_reading.store(index, std::memory_order_relaxed);
}
if (index == -1)
return false;
const uint8_t slot = static_cast<uint8_t>(index);
*frame = _frames[slot];
*id = _ids[slot];
return true;
}
void consume() noexcept
{
_reading.store(-1, std::memory_order_seq_cst);
}
AVFrame *write(uint32_t id) noexcept
{
int8_t index = _writing;
while (index == _reading.load(std::memory_order_seq_cst) ||
index == _latest.load(std::memory_order_relaxed))
int8_t index = _writing.load(std::memory_order_relaxed) + 1;
if (index == _size)
index = 0;
if (index == _reading.load(std::memory_order_relaxed))
{
++index;
if (index == 3)
index = 0;
return nullptr;
}
_writing = index;
_writing.store(index, std::memory_order_relaxed);
const uint8_t slot = static_cast<uint8_t>(index);
_ids[slot] = id;
return _frames[slot];
@@ -84,125 +85,47 @@ public:
void commit() noexcept
{
_latest.store(_writing, std::memory_order_release);
// Publish the frame contents and id written into the selected slot.
_latest.store(_writing.load(std::memory_order_relaxed), std::memory_order_release);
}
void reset() noexcept
{
_reading.store(-1);
_latest.store(-1);
_reading.store(-1, std::memory_order_relaxed);
_writing.store(-1, std::memory_order_relaxed);
_latest.store(-1, std::memory_order_release);
}
private:
std::atomic<int8_t> _latest;
std::atomic<int8_t> _reading;
int8_t _writing;
AVFrame *_frames[3];
uint32_t _ids[3];
};
class VideoBufferDouble
{
public:
VideoBufferDouble()
void dispose()
{
_writing = 0;
_oldest.store(-1);
_reading.store(-1);
_latest.store(-1);
for (uint8_t i = 0; i < 4; ++i)
if (_frames)
{
_ids[i] = 0;
_frames[i] = av_frame_alloc();
if (!_frames[i])
for (uint8_t i = 0; i < _size; ++i)
{
throw std::runtime_error("Failed to allocate AVFrame");
if (_frames[i])
{
av_frame_free(&_frames[i]);
_frames[i] = nullptr;
}
}
delete[] _frames;
_frames = nullptr;
}
}
~VideoBufferDouble() noexcept
{
for (uint8_t i = 0; i < 4; ++i)
if (_ids)
{
if (_frames[i])
{
av_frame_free(&_frames[i]);
_frames[i] = nullptr;
}
delete[] _ids;
_ids = nullptr;
}
}
uint32_t latestId() const noexcept
{
const int8_t index = _latest.load(std::memory_order_acquire);
if (index == -1)
return 0;
return _ids[static_cast<uint8_t>(index)];
}
bool latest(AVFrame **frame, uint32_t *id) noexcept
{
int8_t index = _oldest.load(std::memory_order_acquire);
_reading.store(index, std::memory_order_seq_cst);
if (index == -1)
{
index = _latest.load(std::memory_order_acquire);
_reading.store(index, std::memory_order_seq_cst);
if (index == -1)
return false;
}
const uint8_t slot = static_cast<uint8_t>(index);
*frame = _frames[slot];
*id = _ids[slot];
return true;
}
void consume() noexcept
{
const int8_t reading = _reading.load(std::memory_order_seq_cst);
if (_oldest.load(std::memory_order_relaxed) == reading)
_oldest.store(-1, std::memory_order_relaxed);
_reading.store(-1, std::memory_order_seq_cst);
}
AVFrame *write(uint32_t id) noexcept
{
int8_t index = _writing;
while (index == _reading.load(std::memory_order_seq_cst) ||
index == _latest.load(std::memory_order_relaxed) ||
index == _oldest.load(std::memory_order_relaxed))
{
++index;
if (index == 4)
index = 0;
}
_writing = index;
const uint8_t slot = static_cast<uint8_t>(index);
_ids[slot] = id;
return _frames[slot];
}
void commit() noexcept
{
_oldest.store(_latest.load(std::memory_order_relaxed), std::memory_order_release);
_latest.store(_writing, std::memory_order_release);
}
void reset() noexcept
{
_oldest.store(-1);
_reading.store(-1);
_latest.store(-1);
}
private:
std::atomic<int8_t> _oldest;
std::atomic<int8_t> _latest;
std::atomic<int8_t> _reading;
int8_t _writing;
AVFrame *_frames[4];
uint32_t _ids[4];
std::atomic<int8_t> _writing;
std::atomic<int8_t> _latest;
int8_t _size;
AVFrame **_frames;
uint32_t *_ids;
};
#endif /* SRC_STRUCT_VIDEO_BUFFER */