mirror of
https://github.com/niellun/FastCarPlay.git
synced 2026-06-07 09:38:25 +02:00
Single and Double buffered drawing
This commit is contained in:
@@ -108,6 +108,9 @@
|
||||
# This is happening or Raspberry Pi Zero 2W. Disable this to use SW decoding for that case.
|
||||
#hw-decode = true
|
||||
|
||||
# Use double buffering for drawing video. May smooth rendering but increase CPU usage
|
||||
#double-buffered = true;
|
||||
|
||||
# Request extra frames onevery key press. Usefull if you do not see last updates after key press
|
||||
# Enable for RPI hardware decoding and other systems where hardware decoder tends to buffer frames
|
||||
# 0 - disabled, enter number of frames to request (find minimal value that get all screen updates)
|
||||
|
||||
+8
-4
@@ -120,7 +120,10 @@ void Application::start(const char *title)
|
||||
}
|
||||
|
||||
log_v("Starting");
|
||||
loop();
|
||||
if (Settings::doubleBuffer)
|
||||
loop<VideoBufferDouble>();
|
||||
else
|
||||
loop<VideoBuffer>();
|
||||
log_v("Stopped");
|
||||
}
|
||||
|
||||
@@ -275,6 +278,7 @@ bool Application::processFrameEvents(AtomicQueue<Message> &queue, Renderer &rend
|
||||
return result;
|
||||
}
|
||||
|
||||
template <class Buffer>
|
||||
void Application::loop()
|
||||
{
|
||||
// Prepare home screen
|
||||
@@ -294,12 +298,12 @@ void Application::loop()
|
||||
SDL_ShowWindow(_window);
|
||||
interface.drawHome(true, PROTOCOL_STATUS_UNKNOWN);
|
||||
|
||||
VideoBuffer videoBuffer;
|
||||
Buffer videoBuffer;
|
||||
Connector protocol;
|
||||
Decoder decoder;
|
||||
Decoder<Buffer> decoder;
|
||||
PcmAudio audioMain("main"), audioAux("aux");
|
||||
|
||||
decoder.start(&protocol.videoStream, &videoBuffer, AV_CODEC_ID_H264);
|
||||
decoder.start(&protocol.videoStream, videoBuffer, AV_CODEC_ID_H264);
|
||||
audioMain.start(&protocol.audioStreamMain);
|
||||
audioAux.start(&protocol.audioStreamAux, &audioMain);
|
||||
protocol.start(&_state.deviceStatus);
|
||||
|
||||
@@ -36,6 +36,7 @@ private:
|
||||
bool processSystemEvent(const SDL_Event &e);
|
||||
bool processFrameEvents(AtomicQueue<Message> &queue, Renderer &renderer);
|
||||
|
||||
template <class Buffer>
|
||||
void loop();
|
||||
|
||||
SDL_Window *_window;
|
||||
|
||||
+20
-9
@@ -5,28 +5,32 @@
|
||||
#include "common/functions.h"
|
||||
#include "settings.h"
|
||||
|
||||
Decoder::Decoder()
|
||||
template <class Buffer>
|
||||
Decoder<Buffer>::Decoder()
|
||||
: _context(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
Decoder::~Decoder()
|
||||
template <class Buffer>
|
||||
Decoder<Buffer>::~Decoder()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void Decoder::start(AtomicQueue<Message> *data, VideoBuffer *vb, AVCodecID codecId)
|
||||
template <class Buffer>
|
||||
void Decoder<Buffer>::start(AtomicQueue<Message> *data, Buffer &vb, AVCodecID codecId)
|
||||
{
|
||||
if (_active)
|
||||
stop();
|
||||
|
||||
_vb = vb;
|
||||
_vb = &vb;
|
||||
_data = data;
|
||||
_codecId = codecId;
|
||||
_active = true;
|
||||
_thread = std::thread(&Decoder::runner, this);
|
||||
}
|
||||
void Decoder::stop()
|
||||
template <class Buffer>
|
||||
void Decoder<Buffer>::stop()
|
||||
{
|
||||
if (!_active)
|
||||
return;
|
||||
@@ -36,14 +40,16 @@ void Decoder::stop()
|
||||
_thread.join();
|
||||
}
|
||||
|
||||
void Decoder::flush()
|
||||
template <class Buffer>
|
||||
void Decoder<Buffer>::flush()
|
||||
{
|
||||
if (_context)
|
||||
avcodec_flush_buffers(_context);
|
||||
}
|
||||
|
||||
// Initialize and select the best decoder (try HW first, then SW)
|
||||
AVCodecContext *Decoder::load_codec(AVCodecID codec_id)
|
||||
template <class Buffer>
|
||||
AVCodecContext *Decoder<Buffer>::load_codec(AVCodecID codec_id)
|
||||
{
|
||||
void *iter = nullptr;
|
||||
const AVCodec *codec = nullptr;
|
||||
@@ -116,7 +122,8 @@ AVCodecContext *Decoder::load_codec(AVCodecID codec_id)
|
||||
return result;
|
||||
}
|
||||
|
||||
void Decoder::runner()
|
||||
template <class Buffer>
|
||||
void Decoder<Buffer>::runner()
|
||||
{
|
||||
// Set thread name
|
||||
setThreadName("video-decoder");
|
||||
@@ -159,7 +166,8 @@ void Decoder::runner()
|
||||
_context = nullptr;
|
||||
}
|
||||
|
||||
void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPacket *packet, AVFrame *frame)
|
||||
template <class Buffer>
|
||||
void Decoder<Buffer>::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPacket *packet, AVFrame *frame)
|
||||
{
|
||||
uint32_t counter = 0;
|
||||
|
||||
@@ -231,3 +239,6 @@ void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPack
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template class Decoder<VideoBuffer>;
|
||||
template class Decoder<VideoBufferDouble>;
|
||||
|
||||
+7
-3
@@ -14,6 +14,7 @@ extern "C"
|
||||
#include "struct/atomic_queue.h"
|
||||
#include "protocol/message.h"
|
||||
|
||||
template <class Buffer>
|
||||
class Decoder
|
||||
{
|
||||
|
||||
@@ -21,7 +22,7 @@ public:
|
||||
Decoder();
|
||||
~Decoder();
|
||||
|
||||
void start(AtomicQueue<Message> *data, VideoBuffer *vb, AVCodecID codecId);
|
||||
void start(AtomicQueue<Message> *data, Buffer &vb, AVCodecID codecId);
|
||||
void stop();
|
||||
void flush();
|
||||
|
||||
@@ -31,13 +32,16 @@ private:
|
||||
static AVCodecContext *load_codec(AVCodecID codec_id);
|
||||
|
||||
std::thread _thread;
|
||||
AVCodecContext* _context;
|
||||
AVCodecContext* _context;
|
||||
AVCodecID _codecId;
|
||||
|
||||
std::atomic<bool> _active = false;
|
||||
|
||||
AtomicQueue<Message> *_data = nullptr;
|
||||
VideoBuffer *_vb = nullptr;
|
||||
Buffer *_vb = nullptr;
|
||||
};
|
||||
|
||||
extern template class Decoder<VideoBuffer>;
|
||||
extern template class Decoder<VideoBufferDouble>;
|
||||
|
||||
#endif /* SRC_DECODER */
|
||||
|
||||
@@ -39,6 +39,7 @@ 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> forceRedraw{"force-redraw", 0};
|
||||
static inline Setting<int> eventsSkip{"draw-skip-events", 0};
|
||||
static inline Setting<float> aspectCorrection{"aspect-correction", 1};
|
||||
|
||||
+141
-45
@@ -1,5 +1,5 @@
|
||||
#ifndef SRC_STRUCT_VIDER_BUFFER2
|
||||
#define SRC_STRUCT_VIDER_BUFFER2
|
||||
#ifndef SRC_STRUCT_VIDEO_BUFFER
|
||||
#define SRC_STRUCT_VIDEO_BUFFER
|
||||
|
||||
extern "C"
|
||||
{
|
||||
@@ -7,20 +7,18 @@ extern "C"
|
||||
}
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <stdexcept>
|
||||
|
||||
#define BUFFER_VIDEO_FRAMES 4
|
||||
|
||||
class VideoBuffer
|
||||
{
|
||||
public:
|
||||
VideoBuffer()
|
||||
{
|
||||
_writing.store(0);
|
||||
_oldest.store(-1);
|
||||
_writing = 0;
|
||||
_reading.store(-1);
|
||||
_latest.store(-1);
|
||||
for (uint8_t i = 0; i < BUFFER_VIDEO_FRAMES; ++i)
|
||||
for (uint8_t i = 0; i < 3; ++i)
|
||||
{
|
||||
_ids[i] = 0;
|
||||
_frames[i] = av_frame_alloc();
|
||||
@@ -31,9 +29,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
~VideoBuffer()
|
||||
~VideoBuffer() noexcept
|
||||
{
|
||||
for (uint8_t i = 0; i < BUFFER_VIDEO_FRAMES; ++i)
|
||||
for (uint8_t i = 0; i < 3; ++i)
|
||||
{
|
||||
if (_frames[i])
|
||||
{
|
||||
@@ -43,58 +41,156 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t latestId()
|
||||
uint32_t latestId() const noexcept
|
||||
{
|
||||
int index = _latest.load();
|
||||
if (index < 0)
|
||||
const int8_t index = _latest.load(std::memory_order_acquire);
|
||||
if (index == -1)
|
||||
return 0;
|
||||
return _ids[index];
|
||||
return _ids[static_cast<uint8_t>(index)];
|
||||
}
|
||||
|
||||
bool latest(AVFrame **frame, uint32_t *id)
|
||||
bool latest(AVFrame **frame, uint32_t *id) noexcept
|
||||
{
|
||||
_reading.store(_oldest.load());
|
||||
int index = _reading.load();
|
||||
if (index < 0)
|
||||
{
|
||||
_reading.store(_latest.load());
|
||||
index = _reading.load();
|
||||
if (index < 0)
|
||||
return false;
|
||||
}
|
||||
*frame = _frames[index];
|
||||
*id = _ids[index];
|
||||
const int8_t 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()
|
||||
void consume() noexcept
|
||||
{
|
||||
if(_oldest.load() == _reading.load())
|
||||
_oldest.store(-1);
|
||||
_reading.store(-1);
|
||||
_reading.store(-1, std::memory_order_seq_cst);
|
||||
}
|
||||
|
||||
AVFrame *write(uint32_t id)
|
||||
AVFrame *write(uint32_t id) noexcept
|
||||
{
|
||||
int index = _writing.load();
|
||||
while (index == _reading.load() || index == _latest.load() || index == _oldest.load())
|
||||
int8_t index = _writing;
|
||||
while (index == _reading.load(std::memory_order_seq_cst) ||
|
||||
index == _latest.load(std::memory_order_relaxed))
|
||||
{
|
||||
index = (index + 1) % BUFFER_VIDEO_FRAMES;
|
||||
++index;
|
||||
if (index == 3)
|
||||
index = 0;
|
||||
}
|
||||
_writing.store(index);
|
||||
_ids[index] = id;
|
||||
return _frames[index];
|
||||
_writing = index;
|
||||
const uint8_t slot = static_cast<uint8_t>(index);
|
||||
_ids[slot] = id;
|
||||
return _frames[slot];
|
||||
}
|
||||
|
||||
void commit()
|
||||
void commit() noexcept
|
||||
{
|
||||
_oldest.store(_latest.load());
|
||||
_latest.store(_writing.load());
|
||||
_latest.store(_writing, std::memory_order_release);
|
||||
}
|
||||
|
||||
void reset()
|
||||
void reset() noexcept
|
||||
{
|
||||
_reading.store(-1);
|
||||
_latest.store(-1);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
_writing = 0;
|
||||
_oldest.store(-1);
|
||||
_reading.store(-1);
|
||||
_latest.store(-1);
|
||||
for (uint8_t i = 0; i < 4; ++i)
|
||||
{
|
||||
_ids[i] = 0;
|
||||
_frames[i] = av_frame_alloc();
|
||||
if (!_frames[i])
|
||||
{
|
||||
throw std::runtime_error("Failed to allocate AVFrame");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~VideoBufferDouble() noexcept
|
||||
{
|
||||
for (uint8_t i = 0; i < 4; ++i)
|
||||
{
|
||||
if (_frames[i])
|
||||
{
|
||||
av_frame_free(&_frames[i]);
|
||||
_frames[i] = 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
|
||||
{
|
||||
_writing.store(0);
|
||||
_oldest.store(-1);
|
||||
_reading.store(-1);
|
||||
_latest.store(-1);
|
||||
@@ -104,9 +200,9 @@ private:
|
||||
std::atomic<int8_t> _oldest;
|
||||
std::atomic<int8_t> _latest;
|
||||
std::atomic<int8_t> _reading;
|
||||
std::atomic<int8_t> _writing;
|
||||
AVFrame *_frames[BUFFER_VIDEO_FRAMES] = {nullptr, nullptr, nullptr};
|
||||
uint32_t _ids[BUFFER_VIDEO_FRAMES];
|
||||
int8_t _writing;
|
||||
AVFrame *_frames[4];
|
||||
uint32_t _ids[4];
|
||||
};
|
||||
|
||||
#endif /* SRC_STRUCT_VIDER_BUFFER2 */
|
||||
#endif /* SRC_STRUCT_VIDEO_BUFFER */
|
||||
|
||||
Reference in New Issue
Block a user