diff --git a/Makefile b/Makefile index e5f8394..4cbf1bf 100644 --- a/Makefile +++ b/Makefile @@ -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 diff --git a/settings.txt b/settings.txt index 0626488..1670341 100644 --- a/settings.txt +++ b/settings.txt @@ -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 \ No newline at end of file +# protocol-debug = 0 \ No newline at end of file diff --git a/src/connector.cpp b/src/connector.cpp index 4baa275..d92874e 100644 --- a/src/connector.cpp +++ b/src/connector.cpp @@ -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 lock(mtx); diff --git a/src/connector.h b/src/connector.h index 159b128..4cf1aca 100644 --- a/src/connector.h +++ b/src/connector.h @@ -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 _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; diff --git a/src/decoder.cpp b/src/decoder.cpp index 6454007..49d6694 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -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; } diff --git a/src/helper/error.h b/src/helper/error.h index 879e42b..7882ac3 100644 --- a/src/helper/error.h +++ b/src/helper/error.h @@ -3,6 +3,7 @@ #include #include +#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) diff --git a/src/helper/functions.h b/src/helper/functions.h index df017cc..abaf5b9 100644 --- a/src/helper/functions.h +++ b/src/helper/functions.h @@ -1,10 +1,18 @@ #ifndef SRC_HELPER_FUNCTIONS #define SRC_HELPER_FUNCTIONS +#include +#include + #if defined(__linux__) || defined(__APPLE__) #include #endif +extern "C" +{ +#include +} + 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 */ diff --git a/src/helper/iprotocol.h b/src/helper/iprotocol.h index 0535772..92d00de 100644 --- a/src/helper/iprotocol.h +++ b/src/helper/iprotocol.h @@ -4,13 +4,11 @@ #include #include -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 */ diff --git a/src/helper/protocol_const.h b/src/helper/protocol_const.h index 97c2e85..23b3481 100644 --- a/src/helper/protocol_const.h +++ b/src/helper/protocol_const.h @@ -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 diff --git a/src/helper/ufont.h b/src/helper/ufont.h deleted file mode 100644 index 9a92f9c..0000000 --- a/src/helper/ufont.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef SRC_HELPER_UFONT -#define SRC_HELPER_UFONT - -#include -#include - -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 */ diff --git a/src/helper/uimage.h b/src/helper/uimage.h deleted file mode 100644 index 975c357..0000000 --- a/src/helper/uimage.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef SRC_HELPER_UIMAGE -#define SRC_HELPER_UIMAGE - -#include -#include - -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 */ diff --git a/src/interface.cpp b/src/interface.cpp new file mode 100644 index 0000000..6deef72 --- /dev/null +++ b/src/interface.cpp @@ -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 + +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; +} + diff --git a/src/interface.h b/src/interface.h new file mode 100644 index 0000000..2aafc25 --- /dev/null +++ b/src/interface.h @@ -0,0 +1,23 @@ +#ifndef SRC_INTERFACE +#define SRC_INTERFACE + +#include "renderer.h" +#include + +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 */ diff --git a/src/main.cpp b/src/main.cpp index 66c55fa..dbcca54 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -12,100 +12,44 @@ extern "C" #include // 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 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 ¶ms) { 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 ¶ms, 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 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; + } } \ No newline at end of file diff --git a/src/pcm_audio.h b/src/pcm_audio.h index 7cb3b80..03ce003 100644 --- a/src/pcm_audio.h +++ b/src/pcm_audio.h @@ -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 diff --git a/src/protocol.cpp b/src/protocol.cpp index 93d5c69..875e484 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -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: diff --git a/src/protocol.h b/src/protocol.h index 157cbc1..4ebe573 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -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 */ diff --git a/src/renderer.cpp b/src/renderer.cpp index 8929511..68c31dc 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -1,18 +1,156 @@ #include "renderer.h" -#include "helper/error.h" #include +#include "settings.h" +#include "helper/functions.h" +#include + +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]); diff --git a/src/renderer.h b/src/renderer.h index 6d0bf2a..e88da5b 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -8,20 +8,53 @@ extern "C" } #include +#include #include +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; diff --git a/src/resource/background.bmp b/src/resource/background.bmp index a720773..6d94d00 100644 Binary files a/src/resource/background.bmp and b/src/resource/background.bmp differ diff --git a/src/resource/colors.h b/src/resource/colors.h new file mode 100644 index 0000000..9c4609a --- /dev/null +++ b/src/resource/colors.h @@ -0,0 +1,16 @@ +#ifndef SRC_RESOURCE_COLORS +#define SRC_RESOURCE_COLORS + +#include + +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 */ diff --git a/src/settings.h b/src/settings.h index e79cec7..3bf720d 100644 --- a/src/settings.h +++ b/src/settings.h @@ -7,31 +7,41 @@ class Settings { public: + // General section static inline Setting vendorid{"vendor-id", 4884}; static inline Setting productid{"product-id ", 5408}; - static inline Setting fullscreen{"fullscreen", true}; - static inline Setting dpi{"dpi", 0}; - static inline Setting width{"width", 720}; - static inline Setting height{"height", 576}; - static inline Setting fps{"fps", 50}; - static inline Setting vsync{"vsync", false}; static inline Setting sourceWidth{"source-width", 720}; static inline Setting sourceHeight{"source-height", 576}; static inline Setting sourceFps{"source-fps", 50}; + static inline Setting width{"width", 720}; + static inline Setting height{"height", 576}; + static inline Setting fps{"fps", 50}; + static inline Setting fullscreen{"fullscreen", true}; static inline Setting logging{"logging", false}; + + // Device configurations section + static inline Setting encryption{"encryption", false}; + static inline Setting autoconnect{"autoconnect", true}; + static inline Setting weakCharge{"weak-charge", true}; + static inline Setting leftDrive{"left-hand-drive", true}; + static inline Setting nightMode{"night-mode", 2}; + static inline Setting dpi{"dpi", 0}; + + // Application configuration section + static inline Setting fontSize{"font-size", 30}; + static inline Setting vsync{"vsync", false}; + static inline Setting aspectCorrection{"aspect-correction", 1}; static inline Setting scaler{"scaler", 2}; static inline Setting fastScale{"fast-render-scale", false}; static inline Setting videoQueue{"video-buffer-size", 32}; - static inline Setting audioQueue{"audio-buffer-size", 16}; - static inline Setting audioDelay{"audio-buffer-wait", 2}; - static inline Setting audioFade{"audio-fade", 0.3}; - static inline Setting fontSize{"font-size", 30}; - static inline Setting encryption{"encryption", false}; - static inline Setting autoconnect{"autoconnect", true}; + static inline Setting audioQueue{"audio-buffer-size", 16}; + static inline Setting audioDelay{"audio-buffer-wait", 2}; + static inline Setting audioFade{"audio-fade", 0.3}; static inline Setting onConnect{"on-connect-script", ""}; - static inline Setting onDisconnect{"on-disconnect-script", ""}; - static inline Setting protocolDebug{"protocol-debug", 0}; - + static inline Setting onDisconnect{"on-disconnect-script", ""}; + + // Debug section + static inline Setting protocolDebug{"protocol-debug", 0}; static void load(const std::string &filename); static void print(); diff --git a/src/struct/video_buffer.h b/src/struct/video_buffer.h index bf88e93..76ba814 100644 --- a/src/struct/video_buffer.h +++ b/src/struct/video_buffer.h @@ -75,6 +75,13 @@ public: _latest.store(_writing.load()); } + void reset() + { + _writing.store(0); + _reading.store(-1); + _latest.store(-1); + } + private: std::atomic _latest; std::atomic _reading;