diff --git a/settings.txt b/settings.txt index 9e5b818..fee4fc4 100644 --- a/settings.txt +++ b/settings.txt @@ -114,19 +114,23 @@ #renderer-driver = # Use alternative video rendering method. May reduce CPU load and increase smoothness. -#alternative-rendering = false +#alternative-rendering = true # Select faster method of scaling image to window size (nearest) or better quality (linear) #fast-render-scale = false +# USB reading number of async requests and buffer size +#async-usb-calls = 8 +#usb-buffer-size = 131072 + # Size of video and audio buffers. Increase if you see artifacts -#video-buffer-size = 32 -#audio-buffer-size = 32 +#video-buffer-size = 64 +#audio-buffer-size = 64 # Audio delay for music and calls. Fill the buffer to this value before start playing. # Increase if you hear audio artifacts or tearing. Should be less than audio-buffer-size #audio-buffer-wait = 2 -#audio-buffer-wait-call = 8 +#audio-buffer-wait-call = 6 # Reduce volume of main audio source when there is higher priority audio stream. # This will reduce volume of the music if there is navigation instructions @@ -137,7 +141,7 @@ # This controls the size of the audio buffer used by the audio device. # The buffer time is ~ 1/10 buffer size in ms, depending on sample rate e.g. 2048 = ~200ms # Preferably use power of 2 for the values or check your audio driver documentation -#audio-buffer-samples = 2048 +#audio-buffer-samples = 512 # Force application to use following audio driver as audio output, if empty use default driver # See SDL documentation for options. Some of them are @@ -219,4 +223,4 @@ # Enable FFMPEG AV_CODEC_FLAG2_FAST for HW decoder. # Allow non spec compliant speedup tricks. -#decode-fast = false +#decode-fast = true diff --git a/src/application.cpp b/src/application.cpp index 041859c..9a1e21c 100644 --- a/src/application.cpp +++ b/src/application.cpp @@ -320,7 +320,7 @@ void Application::loop() interface.drawHome(true, PROTOCOL_STATUS_UNKNOWN); VideoBuffer videoBuffer; - Protocol protocol(Settings::width, Settings::height, Settings::sourceFps, AV_INPUT_BUFFER_PADDING_SIZE); + Protocol protocol(Settings::width, Settings::height, Settings::sourceFps); Decoder decoder; PcmAudio audioMain("Main"), audioAux("Aux"); diff --git a/src/connector.cpp b/src/connector.cpp index 5118d9d..167b35e 100644 --- a/src/connector.cpp +++ b/src/connector.cpp @@ -9,8 +9,7 @@ #include "helper/functions.h" #include "settings.h" -Connector::Connector(uint16_t videoPadding) - : _videoPadding(videoPadding) +Connector::Connector() { try { @@ -25,22 +24,58 @@ Connector::Connector(uint16_t videoPadding) int result = libusb_init(&_context); if (result < 0) throw std::runtime_error(std::string("Can't initialise USB: ") + libusb_error_name(result)); + + _usbTransfers = Settings::usbQueue; + if(_usbTransfers > MAX_USB_REQUESTS) + _usbTransfers = MAX_USB_REQUESTS; + + for (int i = 0; i < _usbTransfers; i++) + { + _usbTransfer[i] = libusb_alloc_transfer(0); + uint8_t *buf = static_cast(malloc(Settings::usbBufferSize)); + libusb_fill_bulk_transfer(_usbTransfer[i], _device, _endpoint_in, buf, Settings::usbBufferSize, Connector::onUsbRead, this, READ_TIMEOUT); + } } Connector::~Connector() { _active = false; _queue.notify(); + libusb_interrupt_event_handler(_context); if (_write_thread.joinable()) _write_thread.join(); + if (_read_thread.joinable()) + _read_thread.join(); + if (_cipher) { delete _cipher; _cipher = nullptr; } + for (int i = 0; i < _usbTransfers; i++) + { + if (_usbTransfer[i]) + { + timeval timeout{0, 100000}; + libusb_cancel_transfer(_usbTransfer[i]); + libusb_handle_events_timeout_completed(_context, &timeout, nullptr); + } + } + + for (int i = 0; i < _usbTransfers; i++) + { + if (_usbTransfer[i]) + { + if (_usbTransfer[i]->buffer) + free(_usbTransfer[i]->buffer); + libusb_free_transfer(_usbTransfer[i]); + _usbTransfer[i] = nullptr; + } + } + if (_device) { libusb_release_interface(_device, 0); @@ -71,11 +106,15 @@ void Connector::stop() _active = false; _queue.notify(); + libusb_interrupt_event_handler(_context); state(PROTOCOL_STATUS_INITIALISING); if (_write_thread.joinable()) _write_thread.join(); + + if (_read_thread.joinable()) + _read_thread.join(); } bool Connector::connect(uint16_t vendor_id, uint16_t product_id) @@ -138,7 +177,7 @@ void Connector::release() } } -bool Connector::nextState(u_int8_t state) +bool Connector::state(u_int8_t state) { if (state == _state) return false; @@ -154,6 +193,7 @@ bool Connector::nextState(u_int8_t state) _nodeviceCount = 0; _failCount = 0; _state = state; + onStatus(state); return true; } @@ -161,18 +201,13 @@ bool Connector::nextState(u_int8_t state) { _failCount = 0; _state = state; + onStatus(state); return true; } return false; } -void Connector::state(u_int8_t state) -{ - if (nextState(state)) - onStatus(state); -} - bool Connector::linkFail(int status, const char *msg) { if (status == 0) @@ -183,6 +218,14 @@ bool Connector::linkFail(int status, const char *msg) return true; } +void Connector::onDisconnect() +{ + if (!_connected) + return; + std::cout << "[Connection] Device disconnected" << std::endl; + _connected = false; +} + bool Connector::send(std::unique_ptr packet) { if (!_connected || !packet) @@ -323,68 +366,50 @@ void Connector::printMessage(uint32_t cmd, uint32_t length, uint8_t *data, bool std::cout << oss.str() << std::endl; } +void Connector::onUsbRead(libusb_transfer *transfer) +{ + Connector *c = static_cast(transfer->user_data); + + if (!c->_active) + return; + + if (transfer->status == LIBUSB_TRANSFER_NO_DEVICE) + { + c->onDisconnect(); + return; + } + + if (c->_active && transfer->status == LIBUSB_TRANSFER_COMPLETED && transfer->actual_length > 0) + c->onData(transfer->buffer, transfer->actual_length); + + if (c->_active && (libusb_submit_transfer(transfer) != LIBUSB_SUCCESS)) + { + std::cout << "[Connection] USB transfer re-submit failed" << std::endl; + c->onDisconnect(); + } +} + void Connector::readLoop() { - Header header; - int transferred = 0; - uint8_t *data = nullptr; - - // Set thread name setThreadName("protocol-reader"); + timeval timeout{0, 100000}; + + for (int i = 0; i < _usbTransfers; i++) + { + _usbTransfer[i]->dev_handle = _device; + _usbTransfer[i]->endpoint = _endpoint_in; + int status = libusb_submit_transfer(_usbTransfer[i]); + if (status != LIBUSB_SUCCESS) + { + std::cout << "[Connection] USB transfer submit " << i << " failed: " << status << std::endl; + onDisconnect(); + return; + } + } + while (_active && _connected) { - int result = libusb_bulk_transfer(_device, _endpoint_in, reinterpret_cast(&header), sizeof(Header), &transferred, READ_TIMEOUT); - - if (result == LIBUSB_ERROR_NO_DEVICE) - { - std::cout << "[Connection] Device disconnected" << std::endl; - _connected = false; - continue; - } - - if (result != LIBUSB_SUCCESS || transferred != sizeof(Header)) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } - - int padding = 0; - if (header.type == 6) - padding = _videoPadding; - - if (header.length > 0) - { - data = (uint8_t *)malloc(header.length + padding); - if (!data) - continue; - libusb_bulk_transfer(_device, _endpoint_in, data, header.length, &transferred, READ_TIMEOUT); - } - - if (header.magic == MAGIC_ENC) - { - if (!_cipher) - { - std::cout << "[Connection] Received encrypted command " << header.type << " but cipher is not initialised" << std::endl; - if (data) - free(data); - continue; - } - if (!_cipher->Decrypt(data, header.length)) - { - std::cout << "[Connection] Can't decrypt command " << header.type << std::endl; - if (data) - free(data); - continue; - } - } - -#ifdef PROTOCOL_DEBUG - printMessage(header.type, header.length, data, header.magic == MAGIC_ENC, false); -#endif - - if (padding > 0) - std::fill(data + header.length, data + header.length + padding, 0); - onData(header.type, header.length, data); + libusb_handle_events_timeout_completed(_context, &timeout, nullptr); } } diff --git a/src/connector.h b/src/connector.h index fb555b2..4181143 100644 --- a/src/connector.h +++ b/src/connector.h @@ -13,7 +13,9 @@ #include "struct/atomic_queue.h" #include "struct/command.h" -#define READ_TIMEOUT 3000 +#define READ_TIMEOUT 10000 +#define MAX_USB_REQUESTS 64 +#define COMMAND_QUEUE_SIZE 256 #define ENCRYPTION_BASE "SkBRDy3gmrw1ieH0" #define PROTOCOL_DEBUG_NONE 0 @@ -22,21 +24,12 @@ #define PROTOCOL_DEBUG_OUT 3 #define PROTOCOL_DEBUG_ALL 4 -#pragma pack(push, 1) -struct Header -{ - uint32_t magic; - int32_t length; - uint32_t type; - uint32_t typecheck; -}; -#pragma pack(pop) class Connector : public ISender { public: - Connector(uint16_t videoPadding); + Connector(); virtual ~Connector(); void start(); @@ -44,7 +37,7 @@ public: bool send(std::unique_ptr packet) override; protected: - virtual void onData(uint32_t cmd, uint32_t length, uint8_t *data) = 0; + virtual void onData(uint8_t *data, uint32_t length) = 0; virtual void onStatus(u_int8_t status) = 0; virtual void onDevice(bool connected) = 0; @@ -58,15 +51,16 @@ protected: AESCipher *_cipher = nullptr; private: + static void onUsbRead(libusb_transfer *transfer); + void readLoop(); void writeLoop(); - + void onDisconnect(); bool connect(uint16_t vendor_id, uint16_t product_id); bool link(); void release(); - void state(u_int8_t state); - bool nextState(u_int8_t state); + bool state(u_int8_t state); bool linkFail(int status, const char *msg); int write(int cmd, bool encrypt, uint8_t *data, uint32_t size); @@ -74,6 +68,7 @@ private: libusb_device_handle *_device = nullptr; uint8_t _endpoint_in; uint8_t _endpoint_out; + uint8_t _usbTransfers; std::atomic _connected = false; std::atomic _ecnrypt = false; @@ -86,9 +81,8 @@ private: std::thread _write_thread; std::mutex _write_mutex; std::atomic _active = false; - AtomicQueue _queue{256}; - - u_int16_t _videoPadding; + AtomicQueue _queue{COMMAND_QUEUE_SIZE}; + libusb_transfer *_usbTransfer[MAX_USB_REQUESTS] = {}; }; #endif /* SRC_CONNECTOR */ diff --git a/src/helper/protocol_const.h b/src/helper/protocol_const.h index b9d8d73..8817b04 100644 --- a/src/helper/protocol_const.h +++ b/src/helper/protocol_const.h @@ -58,10 +58,11 @@ #define BTN_300 300 #define BTN_301 301 - #define AUDIO_BUFFER_SIZE 2560 #define AUDIO_BUFFER_OFFSET 12 +#define OFFSET_AUDIO_FORMAT 0 + struct ProtocolCmdEntry { int cmd; diff --git a/src/pcm_audio.cpp b/src/pcm_audio.cpp index 799812c..93b62d1 100644 --- a/src/pcm_audio.cpp +++ b/src/pcm_audio.cpp @@ -1,5 +1,6 @@ #include "pcm_audio.h" #include "helper/functions.h" +#include "helper/protocol_const.h" #include "settings.h" // Add sample size (buffer size in samples) to ChannelConfig diff --git a/src/protocol.cpp b/src/protocol.cpp index 2adb6fa..2af3d92 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -1,14 +1,14 @@ #include "protocol.h" #include "helper/protocol_const.h" #include "helper/functions.h" +#include "settings.h" -#include -#include -#include +#include +#include +#include -Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t padding) - : Connector(padding), - videoData(Settings::videoQueue), +Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps) + : videoData(Settings::videoQueue), audioStreamMain(Settings::audioQueue), audioStreamAux(Settings::audioQueue), _recorder(Settings::audioQueue), @@ -17,6 +17,7 @@ Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t paddi _fps(fps), _phoneConnected(false) { + } Protocol::~Protocol() @@ -161,19 +162,52 @@ void Protocol::onControl(int cmd) } } -void Protocol::onData(uint32_t cmd, uint32_t length, uint8_t *data) +void Protocol::onData(uint8_t *data, uint32_t length) { - bool dispose = true; - switch (cmd) + uint32_t offset = 0; + while (offset < length) + { + if (_message == nullptr) + _message = std::make_unique(); + offset += _message->parse(data + offset, length - offset); + + if (!_message->ready()) + continue; + + if (!_message->valid()) + { + std::cout << "[Connection] Can't allocate message " << _message->type() << " with size " << _message->length() << std::endl; + _message = nullptr; + continue; + } + + if (_message->encrypted() && !_message->decrypt(_cipher)) + { + if (!_cipher) + std::cout << "[Connection] Received encrypted command " << _message->type() << " but cipher is not initialised" << std::endl; + else + std::cout << "[Connection] Can't decrypt command " << _message->type() << std::endl; + _message = nullptr; + continue; + } + +#ifdef PROTOCOL_DEBUG + printMessage(_message->type(), _message->length(), _message->data(), _message->encrypted(), false); +#endif + + dispatch(std::move(_message)); + _message = nullptr; + } +} + +void Protocol::dispatch(std::unique_ptr msg) +{ + switch (msg->type()) { case CMD_CONTROL: - if (length == 4) - { - int cmd = 0; - memcpy(&cmd, data, sizeof(int)); - onControl(cmd); - } + if (msg->length() == 4) + onControl(msg->getInt(0)); break; case CMD_PLUGGED: @@ -186,40 +220,25 @@ void Protocol::onData(uint32_t cmd, uint32_t length, uint8_t *data) case CMD_VIDEO_DATA: { - if (length <= 20) - break; - videoData.pushDiscard(std::make_unique(data, length, 20)); - dispose = false; + if(msg->setOffset(20)) + videoData.pushDiscard(std::move(msg)); break; } case CMD_AUDIO_DATA: { - if (length <= 16) - { + if (msg->length() <= 16) break; - } - int channel = 0; - memcpy(&channel, data + 8, sizeof(int)); + int channel = msg->getInt(8); + msg->setOffset(12); if (channel == 1) - { - audioStreamMain.pushDiscard(std::make_unique(data, length, 12)); - dispose = false; - break; - } + audioStreamMain.pushDiscard(std::move(msg)); if (channel == 2) - { - audioStreamAux.pushDiscard(std::make_unique(data, length, 12)); - dispose = false; - break; - } + audioStreamAux.pushDiscard(std::move(msg)); break; } case CMD_ENCRYPTION: - if (length == 0) + if (msg->length() == 0) setEncryption(true); break; } - - if (dispose && length > 0 && data) - free(data); } diff --git a/src/protocol.h b/src/protocol.h index 45b8c4c..056e323 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -3,8 +3,6 @@ #include "struct/atomic_queue.h" #include "struct/message.h" -#include "struct/multitouch.h" -#include "settings.h" #include "connector.h" #include "recorder.h" @@ -12,7 +10,7 @@ class Protocol : public Connector { public: - Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t padding); + Protocol(uint16_t width, uint16_t height, uint16_t fps); ~Protocol() override; Protocol(const Protocol &) = delete; @@ -30,11 +28,11 @@ private: void onStatus(uint8_t status) override; void onDevice(bool connected) override; - void onControl(int cmd); - void onData(uint32_t cmd, uint32_t length, uint8_t *data) override; - void onPhone(bool connected); + void onData(uint8_t *data, uint32_t length) override; - static const char *cmdString(int cmd); + void dispatch(std::unique_ptr msg); + void onControl(int cmd); + void onPhone(bool connected); Recorder _recorder; uint16_t _width; @@ -44,6 +42,8 @@ private: uint32_t _evtStatusId = (uint32_t)-1; uint32_t _evtPhoneId = (uint32_t)-1; + + std::unique_ptr _message; }; #endif /* SRC_PROTOCOL */ diff --git a/src/settings.cpp b/src/settings.cpp index 9f0ea82..672913d 100644 --- a/src/settings.cpp +++ b/src/settings.cpp @@ -8,7 +8,7 @@ bool Settings::load(const std::string &filename) std::ifstream file(filename); if (!file.is_open()) { - std::cerr << "[Settings] Cannot open “" << filename << "”" << std::endl; + std::cerr << "[Settings] Cannot open file: " << filename << std::endl; return false; } diff --git a/src/settings.h b/src/settings.h index 34fc377..5320c8e 100644 --- a/src/settings.h +++ b/src/settings.h @@ -42,14 +42,16 @@ public: static inline Setting forceRedraw{"force-redraw", 0}; static inline Setting aspectCorrection{"aspect-correction", 1}; static inline Setting renderDriver{"renderer-driver", ""}; - static inline Setting alternativeRendering{"alternative-rendering", false}; + static inline Setting alternativeRendering{"alternative-rendering", true}; 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 usbQueue{"async-usb-calls", 8}; + static inline Setting usbBufferSize{"usb-buffer-size", 131072}; + static inline Setting videoQueue{"video-buffer-size", 64}; + static inline Setting audioQueue{"audio-buffer-size", 64}; static inline Setting audioDelay{"audio-buffer-wait", 2}; - static inline Setting audioDelayCall{"audio-buffer-wait-call", 8}; + static inline Setting audioDelayCall{"audio-buffer-wait-call", 6}; static inline Setting audioFade{"audio-fade", 0.3}; - static inline Setting audioBuffer{"audio-buffer-samples", 2048}; + static inline Setting audioBuffer{"audio-buffer-samples", 512}; static inline Setting audioDriver{"audio-driver", ""}; static inline Setting onConnect{"on-connect-script", ""}; @@ -83,7 +85,7 @@ public: // Debug section static inline Setting protocolDebug{"protocol-debug", 0}; static inline Setting codecLowDelay{"decode-low-delay", true}; - static inline Setting codecFast{"decode-fast", false}; + static inline Setting codecFast{"decode-fast", true}; static bool load(const std::string &filename); static void print(); diff --git a/src/struct/message.h b/src/struct/message.h index 6b58683..e4fa2fe 100644 --- a/src/struct/message.h +++ b/src/struct/message.h @@ -1,17 +1,30 @@ #ifndef SRC_STRUCT_MESSAGE #define SRC_STRUCT_MESSAGE +#include "libavcodec/defs.h" +#include "helper/protocol_const.h" + #include #include #include #include -#define OFFSET_AUDIO_FORMAT 0 +#include "aes_cipher.h" + +#pragma pack(push, 1) +struct Header +{ + uint32_t magic; + int32_t length; + uint32_t type; + uint32_t typecheck; +}; +#pragma pack(pop) class Message { public: - Message(uint8_t *data, uint32_t data_length, uint32_t offset) : _data(data), _length(data_length), _offset(offset) + Message() : _header({0, 0, 0, 0}), _data(nullptr), _offset(0), _headerLegth(0), _dataLength(0) { } @@ -24,21 +37,84 @@ public: } } + uint32_t parse(uint8_t *data, uint32_t data_length) + { + uint32_t result = 0; + + if (!hasHeader()) + { + uint8_t copy = sizeof(Header) - _headerLegth; + if (copy > data_length) + copy = data_length; + memcpy(reinterpret_cast(&_header) + _headerLegth, data, copy); + _headerLegth += copy; + result += copy; + } + + if (!hasHeader() || result >= data_length || _header.length <= 0) + return result; + + if (_data == nullptr) + { + uint32_t padding = _header.type == CMD_VIDEO_DATA ? AV_INPUT_BUFFER_PADDING_SIZE : 0; + _data = (uint8_t *)malloc(_header.length + padding); + if (padding > 0 && _data) + std::fill(_data + _header.length, _data + _header.length + padding, 0); + } + + uint32_t copy = _header.length - _dataLength; + if (copy > data_length - result) + copy = data_length - result; + if (_data) + memcpy(_data + _dataLength, data + result, copy); + _dataLength += copy; + result += copy; + + return result; + } + int getInt(uint32_t offset) const { int result = 0; - if (_length - sizeof(int) >= offset) + if (_data && _dataLength - sizeof(int) >= offset) memcpy(&result, _data + offset, sizeof(int)); return result; } + bool ready() const { return _headerLegth == sizeof(Header) && (_header.length <= 0 || _dataLength >= _header.length); } + + bool valid() const { return _header.length <= 0 || _data; } + + bool setOffset(uint32_t offset) + { + if (offset >= _dataLength) + return false; + _offset = offset; + return true; + } + + bool encrypted() const { return _header.magic == MAGIC_ENC; } + + bool decrypt(AESCipher *cipher) + { + if (!cipher) + return false; + return cipher->Decrypt(_data, _dataLength); + } + uint8_t *data() const { return _data + _offset; } - uint32_t length() const { return _length - _offset; } + uint32_t length() const { return _dataLength - _offset; } + uint32_t type() const { return _header.type; } private: + bool hasHeader() const { return _headerLegth == sizeof(Header); } + + Header _header; uint8_t *_data; - uint32_t _length; uint32_t _offset; + + u_int8_t _headerLegth; + uint32_t _dataLength; }; #endif /* SRC_STRUCT_MESSAGE */