diff --git a/src/application.cpp b/src/application.cpp index 90d4cad..1bc73cd 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -1,6 +1,7 @@ #include "application.h" #include +#include #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(); - else - loop(); + 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 &queue, Renderer &rend return result; } -template 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 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; } } } diff --git a/src/application.h b/src/application.h index c774f8a..95e6d7f 100644 --- a/src/application.h +++ b/src/application.h @@ -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 &queue, Renderer &renderer); - template void loop(); SDL_Window *_window; @@ -46,6 +45,7 @@ private: State _state; int _width; int _height; + bool _debug; }; #endif /* SRC_APPLICATION */ diff --git a/src/decoder.cpp b/src/decoder.cpp index 009fd8b..4c2b5a6 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -5,32 +5,32 @@ #include "common/functions.h" #include "settings.h" -template -Decoder::Decoder() - : _context(nullptr) +Decoder::Decoder() + : buffer(Settings::renderingBuffer), + _context(nullptr), + _active(false), + _data(nullptr) { } -template -Decoder::~Decoder() +Decoder::~Decoder() { stop(); } -template -void Decoder::start(AtomicQueue *data, Buffer &vb, AVCodecID codecId) +void Decoder::start(AtomicQueue *data, AVCodecID codecId) { if (_active) stop(); - _vb = &vb; + buffer.reset(); _data = data; _codecId = codecId; _active = true; _thread = std::thread(&Decoder::runner, this); } -template -void Decoder::stop() + +void Decoder::stop() { if (!_active) return; @@ -40,16 +40,14 @@ void Decoder::stop() _thread.join(); } -template -void Decoder::flush() +void Decoder::flush() { if (_context) avcodec_flush_buffers(_context); } // Initialize and select the best decoder (try HW first, then SW) -template -AVCodecContext *Decoder::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::load_codec(AVCodecID codec_id) return result; } -template -void Decoder::runner() +void Decoder::runner() { // Set thread name setThreadName("video-decoder"); @@ -166,8 +163,7 @@ void Decoder::runner() _context = nullptr; } -template -void Decoder::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::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::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; -template class Decoder; diff --git a/src/decoder.h b/src/decoder.h index bdbeb64..0367d04 100644 --- a/src/decoder.h +++ b/src/decoder.h @@ -14,18 +14,18 @@ extern "C" #include "struct/atomic_queue.h" #include "protocol/message.h" -template class Decoder { - public: Decoder(); ~Decoder(); - void start(AtomicQueue *data, Buffer &vb, AVCodecID codecId); + void start(AtomicQueue *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 _active = false; - - AtomicQueue *_data = nullptr; - Buffer *_vb = nullptr; + std::atomic _active; + AtomicQueue *_data; }; -extern template class Decoder; -extern template class Decoder; - #endif /* SRC_DECODER */ diff --git a/src/interface.cpp b/src/interface.cpp index 489838c..60127b0 100644 --- a/src/interface.cpp +++ b/src/interface.cpp @@ -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; + } +} diff --git a/src/interface.h b/src/interface.h index 2aafc25..1e44ee2 100644 --- a/src/interface.h +++ b/src/interface.h @@ -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 */ diff --git a/src/connector.cpp b/src/protocol/connection.cpp similarity index 88% rename from src/connector.cpp rename to src/protocol/connection.cpp index 4fa8708..53f4b2a 100644 --- a/src/connector.cpp +++ b/src/protocol/connection.cpp @@ -1,4 +1,4 @@ -#include "connector.h" +#include "connection.h" #include #include @@ -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* statusHandler) +void Connection::start(atomic *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) +void Connection::onMessage(std::unique_ptr message) { Status s = message->decrypt(_cipher); if (s.failed()) @@ -388,12 +394,21 @@ void Connector::onMessage(std::unique_ptr 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: { diff --git a/src/connector.h b/src/protocol/connection.h similarity index 91% rename from src/connector.h rename to src/protocol/connection.h index 0b87edf..6eaa4f1 100644 --- a/src/connector.h +++ b/src/protocol/connection.h @@ -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 *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; diff --git a/src/renderer.cpp b/src/renderer.cpp index f6b1b63..c454bb5 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -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, diff --git a/src/renderer.h b/src/renderer.h index d19b85f..67969b4 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -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] = { diff --git a/src/settings.h b/src/settings.h index d3bc43c..4dcf5fa 100644 --- a/src/settings.h +++ b/src/settings.h @@ -16,8 +16,8 @@ public: static inline Setting productid{"product-id", 5408}; static inline Setting width{"width", 720}; static inline Setting height{"height", 576}; - static inline Setting sourceFps{"source-fps", 50}; - static inline Setting fps{"fps", 50}; + static inline Setting sourceFps{"source-fps", 60}; + static inline Setting fps{"fps", 60}; static inline Setting screenMode{"window-mode", 0}; static inline Setting cursor{"cursor", false}; static inline Setting loglevel{"log-level", 2}; @@ -39,12 +39,12 @@ public: static inline Setting fontSize{"font-size", 30}; static inline Setting vsync{"vsync", false}; static inline Setting hwDecode{"hw-decode", true}; - static inline Setting doubleBuffer{"double-buffered", true}; + static inline Setting renderingBuffer{"rendering-buffer", 5}; + static inline Setting eventsSkip{"draw-skip-events", 3}; static inline Setting forceRedraw{"force-redraw", 0}; - static inline Setting eventsSkip{"draw-skip-events", 0}; static inline Setting aspectCorrection{"aspect-correction", 1}; static inline Setting renderDriver{"renderer-driver", ""}; - static inline Setting alternativeRendering{"alternative-rendering", true}; + static inline Setting alternativeRendering{"alternative-rendering", false}; static inline Setting fastScale{"fast-render-scale", false}; static inline Setting usbQueue{"async-usb-calls", 16}; static inline Setting usbTransferSize{"usb-buffer-size", 2048}; @@ -87,6 +87,7 @@ public: // Debug section static inline Setting codecLowDelay{"decode-low-delay", true}; static inline Setting codecFast{"decode-fast", true}; + static inline Setting debugOverlay{"debug-overlay", false}; static bool load(const std::string &filename); static void print(); diff --git a/src/struct/atomic_queue.h b/src/struct/atomic_queue.h index b70f7ca..b270451 100644 --- a/src/struct/atomic_queue.h +++ b/src/struct/atomic_queue.h @@ -14,7 +14,7 @@ template class AtomicQueue { public: - AtomicQueue(size_t size) + AtomicQueue(uint16_t size) : _size(size), _data(new unique_ptr[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 &waitFlag, size_t count = 0) + bool wait(atomic &waitFlag, uint16_t count = 0) { unique_lock lock(_mtx); @@ -83,7 +83,7 @@ public: return waitFlag.load(std::memory_order_acquire); } - bool waitFor(atomic &waitFlag, uint32_t timeoutMs, size_t count = 0) + bool waitFor(atomic &waitFlag, uint32_t timeoutMs, uint16_t count = 0) { unique_lock 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[]> _data; - size_t _first; - size_t _last; - atomic _count; + uint16_t _first; + uint16_t _last; + atomic _count; mutex _mtx; condition_variable _lock; }; diff --git a/src/struct/video_buffer.h b/src/struct/video_buffer.h index b9194c2..3684a3f 100644 --- a/src/struct/video_buffer.h +++ b/src/struct/video_buffer.h @@ -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(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(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(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 _latest; - std::atomic _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(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(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(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 _oldest; - std::atomic _latest; std::atomic _reading; - int8_t _writing; - AVFrame *_frames[4]; - uint32_t _ids[4]; + std::atomic _writing; + std::atomic _latest; + int8_t _size; + AVFrame **_frames; + uint32_t *_ids; }; #endif /* SRC_STRUCT_VIDEO_BUFFER */