New UX, More settings, reorganise code

This commit is contained in:
Niellune
2025-05-30 14:35:48 +03:00
parent e16b5d4af3
commit 6f6e127095
23 changed files with 678 additions and 454 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ BUILD_DIR := $(OUT_DIR)/$(BUILD_TYPE)
SRCS := $(shell find $(SRC_DIR) -type f -name '*.cpp')
OBJS=$(patsubst $(SRC_DIR)/%.cpp,$(BUILD_DIR)/%.o,$(SRCS))
RES := $(shell find $(RES_DIR) -type f ! -name '*.h' -name '*.*')
RES := $(shell find $(RES_DIR) -type f ! -name '*.h' ! -name '.*' -name '*.*')
RES_SRC := $(patsubst $(RES_DIR)/%,$(GEN_DIR)/%.cpp,$(RES))
# Targets
+68 -33
View File
@@ -1,28 +1,73 @@
# Dongle configuration
##############################################################################
# 1.General settings
##############################################################################
# Dongle configuration (use lsusb to find yours)
# Note that vendor id and product id are in decimals, not hex here
#vendor-id = 4884 # 0x1314
#product-id = 5408 # 0x1520
# Application starts in full screen
#fullscreen = true
# Application drawing settings widthxheight
#width = 720
#height = 576
#fps = 50
# Enable vsync. This reduce tearing but can dramatically affect performance on low end systems
#vsync = false
# Target DPI reported to device. Set 0 for default
#dpi = 0
# Requested image from phone
#source-width = 720
#source-height = 576
#source-fps = 50
# Application drawing settings widthxheight
#width = 720
#height = 576
# Application drawing target framerate. This can responsiveness.
# If the setting is lower than source-fps the framse will be dropped
# If the setting is not multiple of source-fps can still cause frame drops cause out-of-sync
# source images and drawing times
#fps = 50
# Application starts in full screen
#fullscreen = true
# Enable generic console logging. Can reduce performace
#logging = false
##############################################################################
# 2.Device configurations
##############################################################################
# USB communication protocol encryption
# From 2024 Carlinkit has an optinal USB protocol encryption
# It can happened that it will become mandatory and device will not work withou it
# So if you have updated your device and it stop working try to enable encryption
#encryption = false
# Enable automatic connection to wireless devices
#autoconnect = true
# Eanble weak charging current (standart usb 0.5A).
# If you have a powerfull poswer supply you can try to disable this.
# If you have troubles with wired connection try to enable this, cause it might be cause of power lack.
#weak-charge = true
# Driving position, true for left hand drive, false for right hand drive
#left-hand-drive = true
# Nigh mode. 0 for day mode, 1 for night mode, 2 for automatic
#night-mode = 2
# Target DPI reported to device. Set 0 for default. Not sure if it affects anything
#dpi = 0
##############################################################################
# 3.Application configuration
##############################################################################
# Font size for messgaes on screen. Set to 0 is you do not want any
#font-size = 30
# Enable vsync. This reduce tearing but can dramatically affect performance on low end systems
#vsync = false
# Corrects aspect of UI
#aspect-correction = 1
# Scaler algorithm if application drawing is differen from source image
# It's recommended to keep application and source values same cause scaling
# takes a lot of CPU and can cause artifacts on slow devices
@@ -45,9 +90,6 @@
# Select faster method of scaling image to window size (nearest) or better quality (linear)
#fast-render-scale = false
# Enable logging
#logging = false
# Size of video and audio buffers. Increase if you see artifacts
#video-buffer-size = 32
#audio-buffer-size = 16
@@ -61,18 +103,6 @@
# Reduction level from 1 (no reduction) to 0 (fully silenced)
#audio-fade = 0.3
# Font size for messgaes on screen. Set to 0 is you do not want any
#font-size = 30
# USB communication protocol encryption
# From 2024 Carlinkit has an optinal USB protocol encryption
# It can happened that it will become mandatory and device will not work withou it
# So if you have updated your device and it stop working try to enable encryption
#encryption = false
# Enable automatic connection to wireless devices
#autoconnect = true
# Run script or app on phone connected and disconnected.
# This script/app should be fast, otherwise it will block system.
# If you need to start application in background use scripts with fork
@@ -90,10 +120,15 @@
#on-connect-script =
#on-disconnect-script =
# Protocol debug level. Works on debug builds only with PROTOCOL_DEBUG flag set.
##############################################################################
# 4.Debug
##############################################################################
# Protocol debug level. Works only on builds only with PROTOCOL_DEBUG flag set.
# Add -DPROTOCOL_DEBUG to CXXCOMMON to enable protocol debugging and rebuild.
# 0 - nothing
# 1 - unknown commands
# 2 - all commands except data streams
# 3 - include outgoing commands
# 4 - log everything
#protocol-debug = 0
# protocol-debug = 0
+71 -44
View File
@@ -21,6 +21,7 @@ Connector::Connector(uint16_t videoPadding)
_cipher = nullptr;
}
_state = PROTOCOL_STATUS_INITIALISING;
int result = libusb_init(&_context);
if (result < 0)
throw std::runtime_error(std::string("Can't initialise USB: ") + libusb_error_name(result));
@@ -67,8 +68,8 @@ void Connector::stop()
return;
_active = false;
if (_read_thread.joinable())
_read_thread.join();
state(PROTOCOL_STATUS_INITIALISING);
if (_write_thread.joinable())
_write_thread.join();
@@ -76,13 +77,11 @@ void Connector::stop()
bool Connector::connect(uint16_t vendor_id, uint16_t product_id)
{
status("Searching for dongle");
_device = libusb_open_device_with_vid_pid(_context, vendor_id, product_id);
if (!_device)
{
std::cout << "[Connection] Failed to create device handle - no device" << std::endl;
status("Can't find dongle");
state(PROTOCOL_STATUS_NO_DEVICE);
return false;
}
@@ -97,50 +96,29 @@ bool Connector::connect(uint16_t vendor_id, uint16_t product_id)
bool Connector::link()
{
int usbres = 0;
status("Linking dongle");
state(PROTOCOL_STATUS_LINKING);
usbres = libusb_reset_device(_device);
if (usbres < 0)
{
std::cout << "[Connection] Can't reset device: " << libusb_error_name(usbres) << std::endl;
if (linkFail(libusb_reset_device(_device), " Can't reset device"))
return false;
}
usbres = libusb_set_configuration(_device, 1);
if (usbres < 0)
{
std::cout << "[Connection] Can't set configuration: " << libusb_error_name(usbres) << std::endl;
if (linkFail(libusb_set_configuration(_device, 1), "Can't set configuration"))
return false;
}
usbres = libusb_claim_interface(_device, 0);
if (usbres < 0)
{
std::cout << "[Connection] Can't claim interface: " << libusb_error_name(usbres) << std::endl;
if (linkFail(libusb_claim_interface(_device, 0), "Can't claim interface"))
return false;
}
libusb_device *dev = libusb_get_device(_device);
struct libusb_config_descriptor *config = nullptr;
usbres = libusb_get_active_config_descriptor(dev, &config);
if (usbres < 0)
{
std::cout << "[Connection] Can't get config: " << libusb_error_name(usbres) << std::endl;
if (linkFail(libusb_get_active_config_descriptor(dev, &config), "Can't get config"))
return false;
}
for (int i = 0; i < config->interface[0].altsetting[0].bNumEndpoints; i++)
{
const struct libusb_endpoint_descriptor *ep = &config->interface[0].altsetting[0].endpoint[i];
if ((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
{
_endpoint_in = ep->bEndpointAddress;
}
else
{
_endpoint_out = ep->bEndpointAddress;
}
}
libusb_free_config_descriptor(config);
@@ -157,10 +135,57 @@ void Connector::release()
}
}
void Connector::status(const char *status)
bool Connector::nextState(u_int8_t state)
{
if (_protocol)
_protocol->onStatus(status);
if (state == _state)
return false;
if (state > _state)
{
_nodeviceCount = 0;
_failCount = 0;
_state = state;
return true;
}
switch (state)
{
case PROTOCOL_STATUS_INITIALISING:
break;
case PROTOCOL_STATUS_ERROR:
_nodeviceCount = 0;
if (_failCount++ < 10)
return false;
break;
case PROTOCOL_STATUS_NO_DEVICE:
if (_nodeviceCount++ < 10 && _state < PROTOCOL_STATUS_ONLINE)
return false;
break;
default:
return false;
}
_state = state;
return true;
}
void Connector::state(u_int8_t state)
{
if (nextState(state) && _protocol)
_protocol->onStatus(state);
}
bool Connector::linkFail(int status, const char *msg)
{
if (status == 0)
return false;
_lastError = msg;
std::cout << "[Connection] " << msg << ": " << libusb_error_name(status) << std::endl;
state(PROTOCOL_STATUS_ERROR);
return true;
}
int Connector::send(int cmd, bool encrypt, uint8_t *data, uint32_t size)
@@ -213,7 +238,7 @@ void Connector::setEncryption(bool enabled)
_ecnrypt = true;
}
void Connector::printInts(uint32_t length, uint8_t *data, uint16_t max)
void Connector::printInts(uint8_t *data, uint32_t length, uint16_t max)
{
if (data && length >= 4)
{
@@ -232,7 +257,7 @@ void Connector::printInts(uint32_t length, uint8_t *data, uint16_t max)
}
}
void Connector::printBytes(uint32_t length, uint8_t *data, uint16_t max)
void Connector::printBytes(uint8_t *data, uint32_t length, uint16_t max)
{
if (data && length >= 4)
{
@@ -311,8 +336,6 @@ void Connector::read_loop()
if (result == LIBUSB_ERROR_NO_DEVICE)
{
std::cout << "[Connection] Device disconnected" << std::endl;
if (_protocol)
_protocol->onDevice(false);
_connected = false;
continue;
}
@@ -357,9 +380,6 @@ void Connector::read_loop()
#ifdef PROTOCOL_DEBUG
printMessage(header.type, header.length, data, header.magic == MAGIC_ENC, false);
if (header.type == 7 && header.length < 100)
printBytes(header.length, data, 30);
#endif
if (padding > 0)
@@ -375,20 +395,20 @@ void Connector::write_loop()
// Set thread name
setThreadName("protocol-writer");
state(PROTOCOL_STATUS_LINKING);
while (_active)
{
_connected = connect(Settings::vendorid, Settings::productid);
if (_connected)
{
status("Initialising dongle");
std::cout << "[Connection] Device connected" << std::endl;
_read_thread = std::thread(&Connector::read_loop, this);
if (_protocol)
_protocol->onDevice(true);
status("Waiting for connecton");
state(PROTOCOL_STATUS_ONLINE);
while (_connected && _active)
{
send(170);
@@ -397,7 +417,14 @@ void Connector::write_loop()
{ return !_active.load(); });
}
if(_read_thread.joinable())
if (_active)
{
state(PROTOCOL_STATUS_NO_DEVICE);
if (_protocol)
_protocol->onDevice(false);
}
if (_read_thread.joinable())
_read_thread.join();
}
std::unique_lock<std::mutex> lock(mtx);
+14 -7
View File
@@ -11,7 +11,7 @@
#include "helper/iprotocol.h"
#include "aes_cipher.h"
#define READ_TIMEOUT 5000
#define READ_TIMEOUT 3000
#define ENCRYPTION_BASE "SkBRDy3gmrw1ieH0"
#define PROTOCOL_DEBUG_NONE 0
@@ -45,6 +45,11 @@ public:
AESCipher *Cypher() const { return _cipher; };
static void printMessage(uint32_t cmd, uint32_t length, uint8_t *data, bool encrypted, bool out);
static void printInts(uint8_t *data, uint32_t length, uint16_t max);
static void printBytes(uint8_t *data, uint32_t length, uint16_t max);
static const char *cmdString(int cmd);
private:
void read_loop();
void write_loop();
@@ -53,12 +58,9 @@ private:
bool link();
void release();
void status(const char *status);
static void printInts(uint32_t length, uint8_t *data, uint16_t max);
static void printBytes(uint32_t length, uint8_t *data, uint16_t max);
static const char *cmdString(int cmd);
static void printMessage(uint32_t cmd, uint32_t length, uint8_t *data, bool encrypted, bool out);
void state(u_int8_t state);
bool nextState(u_int8_t state);
bool linkFail(int status, const char *msg);
libusb_context *_context = nullptr;
libusb_device_handle *_device = nullptr;
@@ -67,6 +69,11 @@ private:
bool _connected;
std::atomic<bool> _ecnrypt = false;
uint8_t _state;
uint8_t _failCount;
uint8_t _nodeviceCount;
std::string _lastError;
std::thread _read_thread;
std::thread _write_thread;
std::mutex _write_mutex;
+3 -3
View File
@@ -63,7 +63,7 @@ AVCodecContext *Decoder::load_codec(AVCodecID codec_id)
return result;
}
std::cout << "[Video] Can't load HW codec " << codec->name << ": " << Error::avErrorText(ret) << std::endl;
std::cout << "[Video] Can't load HW codec " << codec->name << ": " << avErrorText(ret) << std::endl;
avcodec_free_context(&result);
}
@@ -85,7 +85,7 @@ AVCodecContext *Decoder::load_codec(AVCodecID codec_id)
int ret = avcodec_open2(result, codec, nullptr);
if (ret < 0)
{
std::cout << "[Video] Failed to open software decoder " << codec->name << ": " << Error::avErrorText(ret) << std::endl;
std::cout << "[Video] Failed to open software decoder " << codec->name << ": " << avErrorText(ret) << std::endl;
avcodec_free_context(&result);
return nullptr;
}
@@ -177,7 +177,7 @@ void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPack
int send_ret = avcodec_send_packet(context, packet);
if (send_ret != 0)
{
std::cout << "[Video] Can't decode packet: " << Error::avErrorText(send_ret) << std::endl;
std::cout << "[Video] Can't decode packet: " << avErrorText(send_ret) << std::endl;
continue;
}
+1 -8
View File
@@ -3,6 +3,7 @@
#include <string>
#include <stdexcept>
#include "helper/functions.h"
extern "C"
{
@@ -47,14 +48,6 @@ public:
return false;
}
static const std::string avErrorText(int code)
{
char buf[AV_ERROR_MAX_STRING_SIZE] = {0};
if (av_strerror(code, buf, sizeof(buf)) == 0)
return buf;
return "Unknown error";
}
bool avFail(int code, const std::string &message = "")
{
if (code == 0)
+32 -2
View File
@@ -1,10 +1,18 @@
#ifndef SRC_HELPER_FUNCTIONS
#define SRC_HELPER_FUNCTIONS
#include <iostream>
#include <SDL2/SDL.h>
#if defined(__linux__) || defined(__APPLE__)
#include <pthread.h>
#endif
extern "C"
{
#include <libavutil/error.h>
}
inline void setThreadName(const char *name)
{
#if defined(__linux__)
@@ -29,12 +37,34 @@ inline void write_uint32_le(uint8_t *dst, uint32_t value)
dst[3] = (value >> 24) & 0xFF;
}
inline void execute(const char* path) {
if (!path || *path == '\0') {
inline void execute(const char *path)
{
if (!path || *path == '\0')
{
throw std::invalid_argument("Program path cannot be empty");
}
std::system(path);
}
inline const std::string avErrorText(int code)
{
char buf[AV_ERROR_MAX_STRING_SIZE] = {0};
if (av_strerror(code, buf, sizeof(buf)) == 0)
return buf;
return "Unknown error";
}
inline void pushEvent(Uint32 evt, int code)
{
if (evt == (Uint32)-1)
return;
SDL_Event event;
SDL_memset(&event, 0, sizeof(event));
event.type = evt;
event.user.type = evt;
event.user.code = code;
SDL_PushEvent(&event);
}
#endif /* SRC_HELPER_FUNCTIONS */
+2 -4
View File
@@ -4,13 +4,11 @@
#include <cstdint>
#include <functional>
using StatusCallback = void (*)(const char* status);
class IProtocol
{
public:
virtual void onData(uint32_t cmd, uint32_t length, uint8_t* data) = 0;
virtual void onStatus(const char* status) = 0;
virtual void onData(uint32_t cmd, uint32_t length, uint8_t *data) = 0;
virtual void onStatus(u_int8_t status) = 0;
virtual void onDevice(bool connected) = 0;
};
#endif /* SRC_HELPER_IPROTOCOL */
+7
View File
@@ -1,6 +1,13 @@
#ifndef SRC_HELPER_PROTOCOL_CONST
#define SRC_HELPER_PROTOCOL_CONST
#define PROTOCOL_STATUS_INITIALISING 0 // Initialised > 1
#define PROTOCOL_STATUS_NO_DEVICE 1 // Start linking > 3
#define PROTOCOL_STATUS_ERROR 2 // Linked > 4, no device in sequence > 1
#define PROTOCOL_STATUS_LINKING 3 // Linked > 4, Failed in sequence > 2
#define PROTOCOL_STATUS_ONLINE 4 // Phone connected > 5, no device > 1
#define PROTOCOL_STATUS_CONNECTED 5 // Phone disconnected > 4, no device > 1
#define MAGIC 0x55aa55aa
#define MAGIC_ENC 0x55bb55bb
-65
View File
@@ -1,65 +0,0 @@
#ifndef SRC_HELPER_UFONT
#define SRC_HELPER_UFONT
#include <SDL2/SDL_ttf.h>
#include <iostream>
class UFont
{
public:
UFont(const void *font_data, int data_size, int ptsize)
{
if(ptsize < 1)
return;
SDL_RWops *font_rw = SDL_RWFromConstMem(font_data, data_size);
if (!font_rw)
{
std::cerr << "[UX] SDL can't open font: " << SDL_GetError() << std::endl;
return;
}
_font = TTF_OpenFontRW(font_rw, 1, ptsize);
if (!_font)
{
std::cerr << "[UX] SDL can't load font: " << TTF_GetError() << std::endl;
}
};
~UFont()
{
if (_font)
{
TTF_CloseFont(_font);
_font = nullptr;
}
};
SDL_Texture *GetText(SDL_Renderer *renderer, const char *text, SDL_Color color)
{
if (!_font)
return nullptr;
SDL_Surface *textSurface = TTF_RenderText_Blended(_font, text, color);
if (!textSurface)
{
std::cerr << "[UX] Failed to create text surface: " << TTF_GetError() << std::endl;
return nullptr;
}
SDL_Texture *textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
SDL_FreeSurface(textSurface);
if (!textTexture)
{
std::cerr << "[UX] Failed to create text texture: " << TTF_GetError() << std::endl;
return nullptr;
}
return textTexture;
}
private:
TTF_Font *_font = nullptr;
};
#endif /* SRC_HELPER_UFONT */
-52
View File
@@ -1,52 +0,0 @@
#ifndef SRC_HELPER_UIMAGE
#define SRC_HELPER_UIMAGE
#include <SDL2/SDL.h>
#include <iostream>
class UImage
{
public:
UImage(const void *img_data, int img_size)
{
SDL_RWops *img_rw = SDL_RWFromConstMem(img_data, img_size);
if (!img_rw)
{
std::cerr << "[UX] SDL can't open image: " << SDL_GetError() << std::endl;
return;
}
_surface = SDL_LoadBMP_RW(img_rw, 1);
if (!_surface)
{
std::cerr << "[UX] Failed to create image surface: " << SDL_GetError() << std::endl;
return;
}
Width = _surface->w;
Height = _surface->h;
};
~UImage()
{
if (_surface)
{
SDL_FreeSurface(_surface);
_surface = nullptr;
}
};
SDL_Texture *GetImage(SDL_Renderer *renderer)
{
return SDL_CreateTextureFromSurface(renderer, _surface);
}
int Width = 0;
int Height = 0;
private:
SDL_Surface *_surface = nullptr;
};
#endif /* SRC_HELPER_UIMAGE */
+51
View File
@@ -0,0 +1,51 @@
#include "interface.h"
#include "resource/background.h"
#include "resource/font.h"
#include "resource/colors.h"
#include "settings.h"
#include "helper/protocol_const.h"
#include <iostream>
Interface::Interface(SDL_Renderer *renderer)
: Renderer(renderer), _state(0),
_textDongle(font, font_len, Settings::fontSize),
_textInit(font, font_len, Settings::fontSize),
_textConnect(font, font_len, Settings::fontSize),
_textLaunch(font, font_len, Settings::fontSize),
_mainImage(background, background_len)
{
}
Interface::~Interface()
{
}
bool Interface::drawHome(bool force, int state)
{
if (state == _state && !force)
return false;
_state = state;
int width, height;
SDL_GetRendererOutputSize(_renderer, &width, &height);
SDL_RenderClear(_renderer);
_mainImage.draw(_renderer, width, height);
if (state == PROTOCOL_STATUS_ERROR)
{
if (_textDongle.prepare(_renderer, "Connection error", colorError))
_textDongle.draw(_renderer, 0.05 * width, 0.2 * height - _textDongle.height / 2);
}
if (_textDongle.prepare(_renderer, "Insert dongle", state == PROTOCOL_STATUS_NO_DEVICE ? color1 : color1_inactive))
_textDongle.draw(_renderer, 0.05 * width, 0.2 * height - _textDongle.height / 2);
if (_textInit.prepare(_renderer, "Initialising", state == PROTOCOL_STATUS_LINKING ? color2 : color2_inactive))
_textInit.draw(_renderer, 0.05 * width, 0.4 * height - _textInit.height / 2);
if (_textConnect.prepare(_renderer, "Connect phone", state == PROTOCOL_STATUS_ONLINE ? color3 : color3_inactive))
_textConnect.draw(_renderer, 0.05 * width, 0.6 * height - _textConnect.height / 2);
if (_textLaunch.prepare(_renderer, "Launching", state == PROTOCOL_STATUS_CONNECTED? color4 : color4_inactive))
_textLaunch.draw(_renderer, 0.05 * width, 0.8 * height - _textLaunch.height / 2);
SDL_RenderPresent(_renderer);
return true;
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef SRC_INTERFACE
#define SRC_INTERFACE
#include "renderer.h"
#include <string>
class Interface : public Renderer
{
public:
Interface(SDL_Renderer *renderer);
~Interface();
bool drawHome(bool force, int state);
private:
int _state;
RendererText _textDongle;
RendererText _textInit;
RendererText _textConnect;
RendererText _textLaunch;
RendererImage _mainImage;
};
#endif /* SRC_INTERFACE */
+117 -168
View File
@@ -12,100 +12,44 @@ extern "C"
#include <libavutil/imgutils.h> // FFmpeg utility functions for image handling
}
#include "resource/background.h"
#include "resource/font.h"
#include "helper/functions.h"
#include "helper/ufont.h"
#include "helper/uimage.h"
#include "helper/protocol_const.h"
#include "struct/video_buffer.h"
#include "protocol.h"
#include "decoder.h"
#include "pcm_audio.h"
#include "renderer.h"
#include "interface.h"
static const char *title = "Fast Car Play v0.2";
#define FRAME_DELAY_INACTIVE 200
static const char *title = "Fast Car Play v0.3";
static SDL_Window *window = nullptr;
static SDL_Renderer *renderer = nullptr;
bool active = false;
Uint32 evtStatus = (Uint32)-1;
Uint32 evtConnected = (Uint32)-1;
bool active = true;
static SDL_Texture *textTexture = nullptr;
static std::string textureText = "";
static SDL_Texture *imgTexture = nullptr;
static bool mouseDown = false;
static bool fullscreen = false;
std::mutex statusMutex;
std::string statusText;
void onStatus(const char *status)
struct RunParams
{
std::lock_guard<std::mutex> lock(statusMutex);
statusText = status;
}
bool connected;
bool videoRendered;
bool dirty;
bool fullscreen;
bool mouseDown;
uint8_t deviceStatus;
uint32_t frameDelay;
int activeDelay;
};
void DrawText(UFont &font, std::string text)
{
if (!textTexture || textureText.compare(text) != 0)
{
if (textTexture)
SDL_DestroyTexture(textTexture);
textTexture = font.GetText(renderer, text.c_str(), {255, 255, 255, 255});
textureText = text;
}
if (!textTexture)
return;
int textW, textH;
SDL_QueryTexture(textTexture, nullptr, nullptr, &textW, &textH);
int windowW, windowH;
SDL_GetRendererOutputSize(renderer, &windowW, &windowH);
// Center text
SDL_Rect dstRect = {
(windowW - textW) / 2,
(windowH - textH) * 9 / 10,
textW,
textH};
SDL_RenderCopy(renderer, textTexture, nullptr, &dstRect);
}
void DrawImage(UImage &img)
{
if (!imgTexture)
imgTexture = img.GetImage(renderer);
if (!imgTexture)
return;
int windowW, windowH;
SDL_GetRendererOutputSize(renderer, &windowW, &windowH);
// Compute destination rectangle to center image
SDL_Rect dst = {
(windowW - img.Width) / 2, // x: center horizontally
(windowH - img.Height) / 2, // y: center vertically
img.Width, // width: original image width
img.Height // height: original image height
};
SDL_RenderCopy(renderer, imgTexture, nullptr, &dst);
}
void processKey(Protocol &protocol, SDL_Keysym key)
void processKey(Protocol &protocol, SDL_Keysym key, RunParams &params)
{
switch (key.sym)
{
case SDLK_f:
fullscreen = !fullscreen; // Toggle fullscreen mode
SDL_SetWindowFullscreen(window, fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
SDL_SetWindowBordered(window, fullscreen ? SDL_FALSE : SDL_TRUE);
params.fullscreen = !params.fullscreen; // Toggle fullscreen mode
SDL_SetWindowFullscreen(window, params.fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
SDL_SetWindowBordered(window, params.fullscreen ? SDL_FALSE : SDL_TRUE);
break;
case SDLK_q:
@@ -131,7 +75,7 @@ void processKey(Protocol &protocol, SDL_Keysym key)
}
}
void processEvents(Protocol &protocol, bool processMouse)
void processEvents(Protocol &protocol, RunParams &params, VideoBuffer &vb)
{
SDL_Event e;
int motionX = -1;
@@ -140,6 +84,7 @@ void processEvents(Protocol &protocol, bool processMouse)
int downY = -1;
int upX = -1;
int upY = -1;
while (SDL_PollEvent(&e))
{
switch (e.type)
@@ -148,9 +93,16 @@ void processEvents(Protocol &protocol, bool processMouse)
active = false;
break;
case SDL_WINDOWEVENT:
if (e.window.event == SDL_WINDOWEVENT_RESIZED)
{
params.dirty = true;
}
break;
case SDL_MOUSEBUTTONDOWN:
{
mouseDown = true;
params.mouseDown = true;
downX = e.button.x;
downY = e.button.y;
break;
@@ -158,14 +110,14 @@ void processEvents(Protocol &protocol, bool processMouse)
case SDL_MOUSEBUTTONUP:
{
mouseDown = false;
params.mouseDown = false;
upX = e.button.x;
upY = e.button.y;
break;
}
case SDL_MOUSEMOTION:
{
if (!mouseDown)
if (!params.mouseDown)
break;
motionX = e.motion.x;
motionY = e.motion.y;
@@ -173,13 +125,30 @@ void processEvents(Protocol &protocol, bool processMouse)
}
case SDL_KEYDOWN:
{
processKey(protocol, e.key.keysym);
processKey(protocol, e.key.keysym, params);
break;
}
default:
{
if (e.type == evtConnected)
{
printf("\nEvt connected %d\n", e.user.code);
params.connected = e.user.code != 0;
params.dirty = true;
params.videoRendered = false;
params.frameDelay = params.connected ? params.activeDelay : FRAME_DELAY_INACTIVE;
if (!params.connected)
vb.reset();
}
else if (e.type == evtStatus)
{
params.deviceStatus = e.user.code;
}
}
}
}
if (processMouse && (downX >= 0 || upX >= 0 || motionX >= 0))
if (params.videoRendered && (downX >= 0 || upX >= 0 || motionX >= 0))
{
int window_width, window_height;
SDL_GetWindowSize(window, &window_width, &window_height);
@@ -194,8 +163,17 @@ void processEvents(Protocol &protocol, bool processMouse)
void application()
{
fullscreen = Settings::fullscreen;
if (fullscreen)
RunParams p;
p.activeDelay = 1000 / Settings::fps;
p.connected = false;
p.deviceStatus = PROTOCOL_STATUS_INITIALISING;
p.dirty = false;
p.frameDelay = FRAME_DELAY_INACTIVE;
p.videoRendered = false;
p.fullscreen = Settings::fullscreen;
p.mouseDown = false;
if (p.fullscreen)
{
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN);
SDL_SetWindowBordered(window, SDL_FALSE);
@@ -212,90 +190,46 @@ void application()
decoder.start(&protocol.videoData, &videoBuffer, AV_CODEC_ID_H264);
audioMain.start(&protocol.audioStreamMain);
audioAux.start(&protocol.audioStreamAux, &audioMain);
protocol.start(onStatus);
UFont textFont(font, font_len, Settings::fontSize);
std::string status = "Initialising";
DrawText(textFont, status);
SDL_RenderPresent(renderer);
UImage image(background, background_len);
SDL_RenderClear(renderer);
DrawImage(image);
DrawText(textFont, status);
SDL_RenderPresent(renderer);
protocol.start(evtStatus, evtConnected);
Interface interface(renderer);
std::cout << "[Main] Loop" << std::endl;
Renderer videoRenderer(renderer);
bool dirty = true;
bool connected = false;
bool videoPrepared = false;
const int activeDelay = 1000 / Settings::fps;
const int inactiveDelay = 1000 / 5; // 5FPS
uint32_t frameDelay = inactiveDelay;
active = true;
uint32_t latestid = 0;
Uint32 frameStart = SDL_GetTicks();
while (active)
{
processEvents(protocol, videoPrepared);
processEvents(protocol, p, videoBuffer);
if (connected != protocol.phoneConnected)
{
connected = protocol.phoneConnected;
SDL_RenderClear(renderer);
DrawImage(image);
SDL_RenderPresent(renderer);
dirty = true;
videoPrepared = false;
frameDelay = connected ? activeDelay : inactiveDelay;
}
if (connected)
if (p.connected)
{
AVFrame *frame = nullptr;
uint32_t frameid = 0;
if (videoBuffer.latest(&frame, &frameid) && frameid != latestid && frame)
if (videoBuffer.latest(&frame, &frameid) && (frameid != latestid || p.dirty) && frame)
{
if (!videoPrepared)
videoPrepared = videoRenderer.prepare(frame, Settings::width, Settings::height, Settings::scaler);
if (videoPrepared && videoRenderer.render(frame))
if (interface.render(frame))
{
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, videoRenderer.texture, nullptr, nullptr);
SDL_RenderPresent(renderer);
p.videoRendered = true;
if (!p.dirty && (frameid != latestid + 1))
std::cout << "[Main] Frame drop " << frameid - latestid - 1 << " on " << frameid << std::endl;
latestid = frameid;
p.dirty = false;
}
if (frameid != latestid + 1)
std::cout << "[Main] Frame drop " << frameid - latestid - 1 << " on " << frameid << std::endl;
latestid = frameid;
videoBuffer.consume();
}
}
else
if (!p.videoRendered)
{
{
std::lock_guard<std::mutex> lock(statusMutex);
if (status != statusText)
{
status = statusText;
dirty = true;
}
}
if (dirty)
{
SDL_RenderClear(renderer);
DrawImage(image);
DrawText(textFont, status);
SDL_RenderPresent(renderer);
}
interface.drawHome(p.dirty, p.connected ? PROTOCOL_STATUS_CONNECTED : p.deviceStatus);
p.dirty = false;
}
Uint32 frameEnd = SDL_GetTicks();
Uint32 frameTime = frameEnd - frameStart;
if (frameTime < frameDelay)
if (active && frameTime < p.frameDelay)
{
SDL_Delay(frameDelay - frameTime); // Sleep only the remaining time
frameStart = frameStart + frameDelay;
SDL_Delay(p.frameDelay - frameTime); // Sleep only the remaining time
frameStart = frameStart + p.frameDelay;
}
else
frameStart = frameEnd;
@@ -304,20 +238,8 @@ void application()
SDL_HideWindow(window);
}
int main(int argc, char **argv)
int start()
{
std::cout << title << std::endl;
if (argc > 2)
{
std::cerr << " Usage: " << argv[0] << " [settings_file]" << std::endl;
return 1;
}
if (argc == 2)
{
Settings::load(argv[1]);
}
if (!Settings::logging)
disable_cout();
else
@@ -370,9 +292,19 @@ int main(int argc, char **argv)
renderer = SDL_CreateRenderer(window, -1, (Settings::vsync ? (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC) : SDL_RENDERER_ACCELERATED));
if (renderer)
{
std::cout << "[Main] Started" << std::endl;
application();
std::cout << "[Main] Finish" << std::endl;
evtStatus = SDL_RegisterEvents(2);
if (evtStatus != (Uint32)-1)
{
evtConnected = evtStatus + 1;
std::cout << "[Main] Started" << std::endl;
application();
std::cout << "[Main] Finish" << std::endl;
}
else
{
std::cerr << "[Main] Can't register custom events" << std::endl;
}
SDL_DestroyRenderer(renderer);
}
else
@@ -380,12 +312,29 @@ int main(int argc, char **argv)
std::cerr << "[Main] SDL can't create renderer: " << SDL_GetError() << std::endl;
}
if (textTexture)
SDL_DestroyTexture(textTexture);
if (imgTexture)
SDL_DestroyTexture(imgTexture);
SDL_DestroyWindow(window);
TTF_Quit();
SDL_Quit();
return 0;
}
int main(int argc, char **argv)
{
std::cout << title << std::endl;
if (argc > 2)
{
std::cerr << " Usage: " << argv[0] << " [settings_file]" << std::endl;
return 0;
}
try
{
if (argc == 2)
Settings::load(argv[1]);
return start();
}
catch (const std::exception &e)
{
std::cerr << e.what() << std::endl;
return 1;
}
}
-1
View File
@@ -9,7 +9,6 @@
#include "struct/atomic_queue.h"
#include "struct/message.h"
#include "helper/error.h"
#define FADE_IN_SPEED 0.00001
#define FADE_OUT_SPEED 0.0001
+23 -14
View File
@@ -23,9 +23,10 @@ Protocol::~Protocol()
stop();
}
void Protocol::start(StatusCallback onStatus)
void Protocol::start(uint32_t evtStatus, uint32_t evtPhone)
{
_statusCallback = onStatus;
_evtStatusId = evtStatus;
_evtPhoneId = evtPhone;
connector.start(this);
}
@@ -146,10 +147,9 @@ void Protocol::sendEncryption()
connector.send(CMD_ENCRYPTION, false, buf, 4);
}
void Protocol::onStatus(const char *status)
void Protocol::onStatus(uint8_t status)
{
if (_statusCallback)
_statusCallback(status);
pushEvent(_evtStatusId, status);
}
void Protocol::onDevice(bool connected)
@@ -162,9 +162,12 @@ void Protocol::onDevice(bool connected)
if (Settings::dpi > 0)
sendFile("/tmp/screen_dpi", Settings::dpi);
sendFile("/etc/android_work_mode", 1);
sendFile("/tmp/night_mode", 2); // 0==day, 1==night, 2==???
sendFile("/tmp/hand_drive_mode", 0); // 0==left, 1==right
sendFile("/tmp/charge_mode", 0);
if (Settings::nightMode < 0 || Settings::nightMode > 2) // 0==day, 1==night, 2==auto
sendFile("/tmp/night_mode", 2);
else
sendFile("/tmp/night_mode", Settings::nightMode);
sendFile("/tmp/hand_drive_mode", Settings::leftDrive ? 0 : 1); // 0==left, 1==right
sendFile("/tmp/charge_mode", Settings::weakCharge ? 0 : 2); // Weak charge 0, other 2
sendFile("/etc/box_name", "CarPlay");
if (Settings::autoconnect)
sendInt(CMD_CONTROL, 1002);
@@ -186,6 +189,8 @@ void Protocol::onPhone(bool connected)
std::cout << (connected ? "[Protocol] Phone connected" : "[Protocol] Phone disconnected") << std::endl;
pushEvent(_evtPhoneId, connected ? 1 : 0);
if (connected && Settings::onConnect.value.length() > 1)
execute(Settings::onConnect.value.c_str());
@@ -198,6 +203,14 @@ void Protocol::onData(uint32_t cmd, uint32_t length, uint8_t *data)
bool dispose = true;
switch (cmd)
{
case CMD_PLUGGED:
onPhone(true);
break;
case CMD_UNPLUGGED:
onPhone(false);
break;
case CMD_VIDEO_DATA:
{
if (length <= 20)
@@ -228,12 +241,8 @@ void Protocol::onData(uint32_t cmd, uint32_t length, uint8_t *data)
}
break;
}
case CMD_PLUGGED:
onPhone(true);
break;
case CMD_UNPLUGGED:
onPhone(false);
case CMD_CONTROL:
Connector::printBytes(data, length, 20);
break;
case CMD_ENCRYPTION:
+4 -3
View File
@@ -19,7 +19,7 @@ public:
static const char *cmdString(int cmd);
void start(StatusCallback onStatus);
void start(uint32_t evtStatus, uint32_t evtPhone);
void stop();
void sendKey(int key);
@@ -40,7 +40,7 @@ private:
void sendInt(uint32_t cmd, uint32_t value, bool encryption = true);
void sendEncryption();
void onStatus(const char *status) override;
void onStatus(uint8_t status) override;
void onDevice(bool connected) override;
void onData(uint32_t cmd, uint32_t length, uint8_t *data) override;
@@ -50,7 +50,8 @@ private:
uint16_t _height;
uint16_t _fps;
StatusCallback _statusCallback = nullptr;
uint32_t _evtStatusId = (uint32_t) -1;
uint32_t _evtPhoneId = (uint32_t) -1;
};
#endif /* SRC_PROTOCOL */
+171 -29
View File
@@ -1,18 +1,156 @@
#include "renderer.h"
#include "helper/error.h"
#include <iostream>
#include "settings.h"
#include "helper/functions.h"
#include <SDL2/SDL_ttf.h>
RendererText::RendererText(const void *font_data, int data_size, int ptsize)
: width(0),
height(0),
_font(nullptr),
_texture(nullptr),
_text(" "),
_color({0, 0, 0, 0})
{
if (ptsize < 1)
return;
SDL_RWops *font_rw = SDL_RWFromConstMem(font_data, data_size);
if (!font_rw)
{
std::cerr << "[UX] SDL can't open font: " << SDL_GetError() << std::endl;
return;
}
_font = TTF_OpenFontRW(font_rw, 1, ptsize);
if (!_font)
{
std::cerr << "[UX] SDL can't load font: " << TTF_GetError() << std::endl;
}
};
RendererText::~RendererText()
{
if (_texture)
{
SDL_DestroyTexture(_texture);
_texture = nullptr;
}
if (_font)
{
TTF_CloseFont(_font);
_font = nullptr;
}
};
bool RendererText::prepare(SDL_Renderer *renderer, std::string text, SDL_Color color)
{
if (!_texture || _text.compare(text) != 0 || !sameColor(_color, color))
{
if (_texture)
SDL_DestroyTexture(_texture);
_texture = getText(renderer, text.c_str(), color);
_text = text;
_color = color;
SDL_QueryTexture(_texture, nullptr, nullptr, &width, &height);
}
return _texture;
}
SDL_Rect RendererText::draw(SDL_Renderer *renderer, int x, int y)
{
if (!_texture)
return {0, 0, 0, 0};
SDL_Rect dstRect = {x, y, (int)(width * Settings::aspectCorrection), height};
SDL_RenderCopy(renderer, _texture, nullptr, &dstRect);
return dstRect;
}
SDL_Texture *RendererText::getText(SDL_Renderer *renderer, const char *text, SDL_Color color)
{
if (!_font)
return nullptr;
SDL_Surface *textSurface = TTF_RenderText_Blended(_font, text, color);
if (!textSurface)
{
std::cerr << "[UX] Failed to create text surface: " << TTF_GetError() << std::endl;
return nullptr;
}
SDL_Texture *textTexture = SDL_CreateTextureFromSurface(renderer, textSurface);
SDL_FreeSurface(textSurface);
if (!textTexture)
{
std::cerr << "[UX] Failed to create text texture: " << TTF_GetError() << std::endl;
return nullptr;
}
return textTexture;
}
RendererImage::RendererImage(const void *img_data, int img_size)
: width(0), height(0), _surface(nullptr), _aspect(0)
{
SDL_RWops *img_rw = SDL_RWFromConstMem(img_data, img_size);
if (!img_rw)
{
std::cerr << "[UX] SDL can't open image: " << SDL_GetError() << std::endl;
return;
}
_surface = SDL_LoadBMP_RW(img_rw, 1);
if (!_surface)
{
std::cerr << "[UX] Failed to create image surface: " << SDL_GetError() << std::endl;
return;
}
width = _surface->w;
height = _surface->h;
};
RendererImage::~RendererImage()
{
if (_surface)
{
SDL_FreeSurface(_surface);
_surface = nullptr;
}
};
SDL_Rect RendererImage::draw(SDL_Renderer *renderer, int w, int h)
{
if (!_texture)
{
_texture = SDL_CreateTextureFromSurface(renderer, _surface);
if (!_texture)
return {0, 0, 0, 0};
SDL_GetRendererOutputSize(renderer, &width, &height);
_aspect = 1.0 * height / width;
}
float scale = 1.0 * h / height;
int imgw = width * scale * Settings::aspectCorrection;
SDL_Rect dst = {w - imgw, 0, imgw, h};
SDL_RenderCopy(renderer, _texture, nullptr, &dst);
return dst;
}
const Renderer::FormatMapping Renderer::_mapping[] = {
{AV_PIX_FMT_RGB24, SDL_PIXELFORMAT_RGB24, &Renderer::rgb, "RGB24"},
{AV_PIX_FMT_YUV420P, SDL_PIXELFORMAT_IYUV, &Renderer::yuv, "YUV420P"},
{AV_PIX_FMT_YUVJ420P, SDL_PIXELFORMAT_IYUV, &Renderer::yuv, "YUVJ420P"},
{AV_PIX_FMT_YUVJ420P, SDL_PIXELFORMAT_IYUV, &Renderer::yuv, "YUVJ420P"},
{AV_PIX_FMT_NV12, SDL_PIXELFORMAT_NV12, &Renderer::nv, "NV12"}};
Renderer::Renderer(SDL_Renderer *renderer)
: texture(nullptr),
textureWidth(0),
textureHeight(0),
_renderer(renderer),
: _renderer(renderer),
_texture(nullptr),
_textureWidth(0),
_textureHeight(0),
_render(nullptr),
_sws(nullptr),
_swsWidth(0),
@@ -23,10 +161,10 @@ Renderer::Renderer(SDL_Renderer *renderer)
Renderer::~Renderer()
{
if (texture)
if (_texture)
{
SDL_DestroyTexture(texture);
texture = nullptr;
SDL_DestroyTexture(_texture);
_texture = nullptr;
}
if (_sws)
{
@@ -43,27 +181,31 @@ Renderer::~Renderer()
bool Renderer::render(AVFrame *frame)
{
if (_render == nullptr)
return false;
if (!prepare(frame, Settings::width, Settings::height, Settings::scaler))
return false;
(this->*_render)(frame);
SDL_RenderClear(_renderer);
SDL_RenderCopy(_renderer, _texture, nullptr, nullptr);
SDL_RenderPresent(_renderer);
return true;
}
bool Renderer::prepareTexture(uint32_t format, int width, int height)
{
if (texture)
if (_texture)
{
if (textureWidth == width && textureHeight == height)
if (_textureWidth == width && _textureHeight == height)
return true;
SDL_DestroyTexture(texture);
texture = nullptr;
SDL_DestroyTexture(_texture);
_texture = nullptr;
}
texture = SDL_CreateTexture(_renderer, format,
SDL_TEXTUREACCESS_STREAMING,
width, height);
if (!texture)
_texture = SDL_CreateTexture(_renderer, format,
SDL_TEXTUREACCESS_STREAMING,
width, height);
if (!_texture)
{
std::cerr << "[Render] SDL can't create video texture: " << SDL_GetError() << std::endl;
std::cerr << "[UX] SDL can't create video texture: " << SDL_GetError() << std::endl;
return false;
}
return true;
@@ -80,7 +222,7 @@ bool Renderer::prepare(AVFrame *frame, int targetWidth, int targetHeight, uint32
{
if (prepareTexture(mapping.sdlFormat, targetWidth, targetHeight))
{
std::cout << "[Render] Direct rendering " << mapping.name << std::endl;
std::cout << "[UX] Direct rendering " << mapping.name << std::endl;
_render = mapping.function;
return true;
}
@@ -89,7 +231,7 @@ bool Renderer::prepare(AVFrame *frame, int targetWidth, int targetHeight, uint32
}
else
{
std::cout << "[Render] Scaling required from " << frame->width << "x" << frame->height << " to " << targetWidth << "x" << targetHeight << std::endl;
std::cout << "[UX] Scaling required from " << frame->width << "x" << frame->height << " to " << targetWidth << "x" << targetHeight << std::endl;
}
if (!prepareTexture(SDL_PIXELFORMAT_IYUV, targetWidth, targetHeight))
@@ -108,7 +250,7 @@ bool Renderer::prepare(AVFrame *frame, int targetWidth, int targetHeight, uint32
scaler, nullptr, nullptr, nullptr);
if (!_sws)
{
std::cerr << "[Render] Can't create sws context" << std::endl;
std::cerr << "[UX] Can't create sws context" << std::endl;
return false;
}
_swsWidth = frame->width;
@@ -120,7 +262,7 @@ bool Renderer::prepare(AVFrame *frame, int targetWidth, int targetHeight, uint32
_frame = av_frame_alloc();
if (!_frame)
{
std::cerr << "[Render] Can't allocate AVFrame" << std::endl;
std::cerr << "[UX] Can't allocate AVFrame" << std::endl;
return false;
}
_frame->format = AV_PIX_FMT_YUV420P;
@@ -130,12 +272,12 @@ bool Renderer::prepare(AVFrame *frame, int targetWidth, int targetHeight, uint32
int avRes = av_frame_get_buffer(_frame, 32);
if (avRes != 0)
{
std::cerr << "[Render] Can't allocate AVFrame buffer: " << Error::avErrorText(avRes) << std::endl;
std::cerr << "[UX] Can't allocate AVFrame buffer: " << avErrorText(avRes) << std::endl;
return false;
}
}
std::cout << "[Render] Scaling rendering source format " << frame->format << std::endl;
std::cout << "[UX] Scaling rendering source format " << frame->format << std::endl;
_render = &Renderer::scale;
return true;
}
@@ -143,7 +285,7 @@ bool Renderer::prepare(AVFrame *frame, int targetWidth, int targetHeight, uint32
void Renderer::rgb(AVFrame *frame)
{
SDL_UpdateTexture(
texture,
_texture,
nullptr,
frame->data[0], frame->linesize[0]);
}
@@ -151,7 +293,7 @@ void Renderer::rgb(AVFrame *frame)
void Renderer::nv(AVFrame *frame)
{
SDL_UpdateNVTexture(
texture,
_texture,
nullptr,
frame->data[0], frame->linesize[0],
frame->data[1], frame->linesize[1]);
@@ -159,7 +301,7 @@ void Renderer::nv(AVFrame *frame)
void Renderer::yuv(AVFrame *frame)
{
SDL_UpdateYUVTexture(
texture,
_texture,
nullptr,
frame->data[0], frame->linesize[0],
frame->data[1], frame->linesize[1],
@@ -176,7 +318,7 @@ void Renderer::scale(AVFrame *frame)
_frame->linesize);
// Update SDL texture with YUV frame data
SDL_UpdateYUVTexture(texture, nullptr,
SDL_UpdateYUVTexture(_texture, nullptr,
_frame->data[0], _frame->linesize[0],
_frame->data[1], _frame->linesize[1],
_frame->data[2], _frame->linesize[2]);
+42 -5
View File
@@ -8,20 +8,53 @@ extern "C"
}
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <string>
class RendererText
{
public:
RendererText(const void *font_data, int data_size, int ptsize);
~RendererText();
bool prepare(SDL_Renderer *renderer, std::string text, SDL_Color color);
SDL_Rect draw(SDL_Renderer *renderer, int x, int y);
int width;
int height;
private:
SDL_Texture *getText(SDL_Renderer *renderer, const char *text, SDL_Color color);
static int sameColor(SDL_Color c1, SDL_Color c2) { return (c1.r == c2.r) && (c1.g == c2.g) && (c1.b == c2.b) && (c1.a == c2.a); }
TTF_Font *_font = nullptr;
SDL_Texture *_texture = nullptr;
std::string _text;
SDL_Color _color;
};
class RendererImage
{
public:
RendererImage(const void *img_data, int img_size);
~RendererImage();
SDL_Rect draw(SDL_Renderer *renderer, int w, int h);
int width;
int height;
private:
SDL_Surface *_surface = nullptr;
SDL_Texture *_texture = nullptr;
float _aspect;
};
class Renderer
{
public:
Renderer(SDL_Renderer *renderer);
~Renderer();
bool prepare(AVFrame *frame, int targetWidth, int targetHeight, uint32_t scaler);
bool render(AVFrame *frame);
SDL_Texture *texture;
int textureWidth;
int textureHeight;
protected:
SDL_Renderer *_renderer;
private:
using DrawFuncType = void (Renderer::*)(AVFrame *);
@@ -34,6 +67,7 @@ private:
std::string name;
};
bool prepare(AVFrame *frame, int targetWidth, int targetHeight, uint32_t scaler);
bool prepareTexture(uint32_t format, int width, int height);
void rgb(AVFrame *frame);
@@ -41,7 +75,10 @@ private:
void yuv(AVFrame *frame);
void scale(AVFrame *frame);
SDL_Renderer *_renderer;
SDL_Texture *_texture;
int _textureWidth;
int _textureHeight;
DrawFuncType _render;
SwsContext *_sws;
int _swsWidth;
Binary file not shown.

Before

Width:  |  Height:  |  Size: 91 KiB

After

Width:  |  Height:  |  Size: 40 KiB

+16
View File
@@ -0,0 +1,16 @@
#ifndef SRC_RESOURCE_COLORS
#define SRC_RESOURCE_COLORS
#include <SDL2/SDL.h>
const SDL_Color color1_inactive = {255, 255, 255, 80};
const SDL_Color color2_inactive = {255, 255, 255, 80};
const SDL_Color color3_inactive = {255, 255, 255, 80};
const SDL_Color color4_inactive = {255, 255, 255, 80};
const SDL_Color color1 = {255, 87, 137, 255};
const SDL_Color color2 = {135, 98, 255, 255};
const SDL_Color color3 = {94, 228, 255, 255};
const SDL_Color color4 = {93, 255, 197, 255};
const SDL_Color colorError = {255, 83, 64, 255};
#endif /* SRC_RESOURCE_COLORS */
+25 -15
View File
@@ -7,31 +7,41 @@
class Settings
{
public:
// General section
static inline Setting<int> vendorid{"vendor-id", 4884};
static inline Setting<int> productid{"product-id ", 5408};
static inline Setting<bool> fullscreen{"fullscreen", true};
static inline Setting<int> dpi{"dpi", 0};
static inline Setting<int> width{"width", 720};
static inline Setting<int> height{"height", 576};
static inline Setting<int> fps{"fps", 50};
static inline Setting<bool> vsync{"vsync", false};
static inline Setting<int> sourceWidth{"source-width", 720};
static inline Setting<int> sourceHeight{"source-height", 576};
static inline Setting<int> sourceFps{"source-fps", 50};
static inline Setting<int> width{"width", 720};
static inline Setting<int> height{"height", 576};
static inline Setting<int> fps{"fps", 50};
static inline Setting<bool> fullscreen{"fullscreen", true};
static inline Setting<bool> logging{"logging", false};
// Device configurations section
static inline Setting<bool> encryption{"encryption", false};
static inline Setting<bool> autoconnect{"autoconnect", true};
static inline Setting<bool> weakCharge{"weak-charge", true};
static inline Setting<bool> leftDrive{"left-hand-drive", true};
static inline Setting<int> nightMode{"night-mode", 2};
static inline Setting<int> dpi{"dpi", 0};
// Application configuration section
static inline Setting<int> fontSize{"font-size", 30};
static inline Setting<bool> vsync{"vsync", false};
static inline Setting<float> aspectCorrection{"aspect-correction", 1};
static inline Setting<int> scaler{"scaler", 2};
static inline Setting<bool> fastScale{"fast-render-scale", false};
static inline Setting<int> videoQueue{"video-buffer-size", 32};
static inline Setting<int> audioQueue{"audio-buffer-size", 16};
static inline Setting<int> audioDelay{"audio-buffer-wait", 2};
static inline Setting<float> audioFade{"audio-fade", 0.3};
static inline Setting<int> fontSize{"font-size", 30};
static inline Setting<bool> encryption{"encryption", false};
static inline Setting<bool> autoconnect{"autoconnect", true};
static inline Setting<int> audioQueue{"audio-buffer-size", 16};
static inline Setting<int> audioDelay{"audio-buffer-wait", 2};
static inline Setting<float> audioFade{"audio-fade", 0.3};
static inline Setting<std::string> onConnect{"on-connect-script", ""};
static inline Setting<std::string> onDisconnect{"on-disconnect-script", ""};
static inline Setting<int> protocolDebug{"protocol-debug", 0};
static inline Setting<std::string> onDisconnect{"on-disconnect-script", ""};
// Debug section
static inline Setting<int> protocolDebug{"protocol-debug", 0};
static void load(const std::string &filename);
static void print();
+7
View File
@@ -75,6 +75,13 @@ public:
_latest.store(_writing.load());
}
void reset()
{
_writing.store(0);
_reading.store(-1);
_latest.store(-1);
}
private:
std::atomic<int8_t> _latest;
std::atomic<int8_t> _reading;