mirror of
https://github.com/niellun/FastCarPlay.git
synced 2026-06-07 09:38:25 +02:00
Simplify video buffer, optimise rendering timings, debug overlay
This commit is contained in:
+67
-25
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||
@@ -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
@@ -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
@@ -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();
|
||||
|
||||
@@ -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
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user