mirror of
https://github.com/niellun/FastCarPlay.git
synced 2026-06-07 09:38:25 +02:00
Encrypted protocol, autoconnect, logging refactor
This commit is contained in:
+2
-1
@@ -5,4 +5,5 @@ src/autogen
|
||||
|
||||
# General files
|
||||
.*/
|
||||
.DS*
|
||||
.DS*
|
||||
.*settings.txt
|
||||
@@ -21,7 +21,7 @@ TARGET_NAME := app
|
||||
|
||||
all: debug
|
||||
|
||||
LDOPTIONS := -lSDL2 -lSDL2_ttf -lavformat -lavcodec -lavutil -lswscale -lusb-1.0
|
||||
LDOPTIONS := -lSDL2 -lSDL2_ttf -lavformat -lavcodec -lavutil -lswscale -lusb-1.0 -lssl -lcrypto
|
||||
LDFLAGS :=
|
||||
CXXCOMMON := -Wall -Isrc
|
||||
|
||||
|
||||
@@ -10,11 +10,11 @@ Devices might have different vendor and product id's. Check your with lsusb and
|
||||
## Dependencies
|
||||
The project is based on SDL2, FFMPEG, LIBUSB. It use XXD for resource embedding.
|
||||
```
|
||||
sudo apt install build-essential xxd libsdl2-dev libsdl2-ttf-dev libavformat-dev libavcodec-dev libavutil-dev libswscale-dev libusb-1.0-0-dev
|
||||
sudo apt install build-essential xxd libsdl2-dev libsdl2-ttf-dev libavformat-dev libavcodec-dev libavutil-dev libswscale-dev libusb-1.0-0-dev libssl-dev
|
||||
```
|
||||
To run the application you also need to install runtime
|
||||
```
|
||||
sudo apt install ffmpeg libsdl2-2.0-0 libsdl2-ttf-2.0-0 libavformat59 libavcodec61 libavutil57 libswscale7 libusb-1.0-0
|
||||
sudo apt install ffmpeg libsdl2-2.0-0 libsdl2-ttf-2.0-0 libavformat59 libavcodec61 libavutil57 libswscale7 libusb-1.0-0 libssl3
|
||||
```
|
||||
|
||||
## Build and run
|
||||
@@ -39,7 +39,7 @@ Bus 003 Device 066: ID 1314:1520 Magic Communication Tec. Auto Box
|
||||
So in my case ID 1314:1520 shows idVendor 1314 and idProduct 1520. We need to use those to create udev rules. If yours are different you also need to put them in settings.txt and run application with settings.txt as argument. Remember that in settings.txt values are in decimal, so you need to convert hex values to base 10 first.
|
||||
Create udev rules, replace <__Vendor__> <__Product__> and <__Your user__> with your informations
|
||||
```
|
||||
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="<__Vendor__>", ATTRS{idProduct}=="<__Product__>", GROUP="<__Your user__>", MODE="0660"' | sudo tee /etc/udev/rules.d/50-carlinkit.rules
|
||||
echo 'SUBSYSTEM=="usb", ATTRS{idVendor}=="<Vendor>", ATTRS{idProduct}=="<Product>", GROUP="<Your user>", MODE="0660"' | sudo tee /etc/udev/rules.d/50-carlinkit.rules
|
||||
## example
|
||||
## UBSYSTEM=="usb", ATTRS{idVendor}=="1314", ATTRS{idProduct}=="1520", GROUP="linux", MODE="0660"
|
||||
sudo udevadm control --reload-rules
|
||||
|
||||
+15
-2
@@ -6,11 +6,15 @@
|
||||
# Application starts in full screen
|
||||
#fullscreen = true
|
||||
|
||||
|
||||
# Application drawing settings widthxheight
|
||||
#width = 720
|
||||
#height = 576
|
||||
#fps = 60
|
||||
|
||||
# Target DPI reported to device. Set 0 for default
|
||||
#dpi = 0
|
||||
|
||||
# Requested image from phone
|
||||
#source-width = 720
|
||||
#source-height = 576
|
||||
@@ -34,10 +38,19 @@
|
||||
#scaler = 2
|
||||
|
||||
# Enable logging
|
||||
logging = true
|
||||
#logging = false
|
||||
|
||||
# Size of video and audio buffers. Increase if you see artifacts
|
||||
#queue-size = 32
|
||||
|
||||
# Font size for messgaes on screen
|
||||
#font-size = 30
|
||||
#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
|
||||
@@ -0,0 +1,110 @@
|
||||
#include "aes_cipher.h"
|
||||
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <ctime>
|
||||
|
||||
AESCipher::AESCipher(const std::string &baseKey)
|
||||
: _baseKey(baseKey)
|
||||
{
|
||||
std::srand(std::time(nullptr));
|
||||
_seed = static_cast<uint32_t>(std::rand());
|
||||
|
||||
if (_baseKey.size() != keyLength)
|
||||
{
|
||||
std::cerr << "[Cypher] Error: base key must be exactly 16 bytes" << std::endl;
|
||||
throw std::invalid_argument("Base key must be exactly 16 bytes");
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < keyLength; ++i)
|
||||
{
|
||||
_encKey[i] = static_cast<uint8_t>(_baseKey[(_seed + i) % keyLength]);
|
||||
}
|
||||
|
||||
_initVec.fill(0);
|
||||
_initVec[1] = static_cast<uint8_t>(_seed);
|
||||
_initVec[4] = static_cast<uint8_t>(_seed >> 8);
|
||||
_initVec[9] = static_cast<uint8_t>(_seed >> 16);
|
||||
_initVec[12] = static_cast<uint8_t>(_seed >> 24);
|
||||
}
|
||||
|
||||
bool AESCipher::Encrypt(uint8_t *data, uint16_t length) const
|
||||
{
|
||||
if (!data || length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
|
||||
if (!ctx)
|
||||
{
|
||||
std::cerr << "[Cypher] Failed to create cipher context" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_cfb(), nullptr, _encKey.data(), _initVec.data()) != 1)
|
||||
{
|
||||
std::cerr << "[Cypher] Encryption initialization failed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> temp(new uint8_t[length + AES_BLOCK_SIZE]);
|
||||
int out_len = 0;
|
||||
if (EVP_EncryptUpdate(ctx.get(), temp.get(), &out_len, data, length) != 1)
|
||||
{
|
||||
std::cerr << "[Cypher] Encryption failed during update" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
int final_len = 0;
|
||||
if (EVP_EncryptFinal_ex(ctx.get(), temp.get() + out_len, &final_len) != 1)
|
||||
{
|
||||
std::cerr << "[Cypher] Encryption failed during final" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::copy_n(temp.get(), length, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AESCipher::Decrypt(uint8_t *data, uint16_t length) const
|
||||
{
|
||||
if (!data || length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
|
||||
if (!ctx)
|
||||
{
|
||||
std::cerr << "[Cypher] Failed to create cipher context" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_cfb(), nullptr, _encKey.data(), _initVec.data()) != 1)
|
||||
{
|
||||
std::cerr << "[Cypher] Decryption initialization failed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> temp(new uint8_t[length + AES_BLOCK_SIZE]);
|
||||
int out_len = 0;
|
||||
if (EVP_DecryptUpdate(ctx.get(), temp.get(), &out_len, data, length) != 1)
|
||||
{
|
||||
std::cerr << "[Cypher] Decryption failed during update" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
int final_len = 0;
|
||||
if (EVP_DecryptFinal_ex(ctx.get(), temp.get() + out_len, &final_len) != 1)
|
||||
{
|
||||
std::cerr << "[Cypher] Decryption failed during final" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::copy_n(temp.get(), length, data);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef SRC_AES_CIPHER
|
||||
#define SRC_AES_CIPHER
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
class AESCipher
|
||||
{
|
||||
public:
|
||||
static constexpr size_t keyLength = 16;
|
||||
|
||||
AESCipher(const std::string &base_key);
|
||||
~AESCipher() = default;
|
||||
|
||||
bool Encrypt(uint8_t *data, uint16_t length) const;
|
||||
bool Decrypt(uint8_t *data, uint16_t length) const;
|
||||
|
||||
uint32_t Seed() const { return _seed; }
|
||||
const std::string& Key() const { return _baseKey; }
|
||||
|
||||
private:
|
||||
std::string _baseKey;
|
||||
uint32_t _seed;
|
||||
std::array<uint8_t, keyLength> _encKey;
|
||||
std::array<uint8_t, keyLength> _initVec;
|
||||
};
|
||||
|
||||
#endif /* SRC_AES_CIPHER */
|
||||
+87
-54
@@ -4,12 +4,22 @@
|
||||
#include <iostream>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "helper/protocol_const.h"
|
||||
#include "helper/functions.h"
|
||||
#include "settings.h"
|
||||
|
||||
Connector::Connector(uint16_t videoPadding)
|
||||
: _videoPadding(videoPadding)
|
||||
{
|
||||
try
|
||||
{
|
||||
_cipher = new AESCipher(ENCRYPTION_BASE);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_cipher = nullptr;
|
||||
}
|
||||
|
||||
int result = libusb_init(&_context);
|
||||
if (result < 0)
|
||||
throw std::runtime_error(std::string("Can't initialise USB: ") + libusb_error_name(result));
|
||||
@@ -60,13 +70,13 @@ void Connector::stop()
|
||||
|
||||
bool Connector::connect(uint16_t vendor_id, uint16_t product_id)
|
||||
{
|
||||
status("Searching for device");
|
||||
status("Searching for dongle");
|
||||
|
||||
std::cout << "Creating device handle" << std::endl;
|
||||
_device = libusb_open_device_with_vid_pid(_context, vendor_id, product_id);
|
||||
if (!_device)
|
||||
{
|
||||
status("Can't find device");
|
||||
std::cout << "[Connection] Failed to create device handle - no device" << std::endl;
|
||||
status("Can't find dongle");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -81,26 +91,38 @@ bool Connector::connect(uint16_t vendor_id, uint16_t product_id)
|
||||
|
||||
bool Connector::link()
|
||||
{
|
||||
status("Linking device");
|
||||
int usbres = 0;
|
||||
status("Linking dongle");
|
||||
|
||||
std::cout << "Reset device" << std::endl;
|
||||
if (libusb_reset_device(_device) < 0)
|
||||
usbres = libusb_reset_device(_device);
|
||||
if (usbres < 0)
|
||||
{
|
||||
std::cout << "[Connection] Can't reset device: " << libusb_error_name(usbres) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "Set configuration" << std::endl;
|
||||
if (libusb_set_configuration(_device, 1) < 0)
|
||||
usbres = libusb_set_configuration(_device, 1);
|
||||
if (usbres < 0)
|
||||
{
|
||||
std::cout << "[Connection] Can't set configuration: " << libusb_error_name(usbres) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "Claim interface" << std::endl;
|
||||
if (libusb_claim_interface(_device, 0) < 0)
|
||||
usbres = libusb_claim_interface(_device, 0);
|
||||
if (usbres < 0)
|
||||
{
|
||||
std::cout << "[Connection] Can't claim interface: " << libusb_error_name(usbres) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "Get config descriptor" << std::endl;
|
||||
libusb_device *dev = libusb_get_device(_device);
|
||||
struct libusb_config_descriptor *config = nullptr;
|
||||
|
||||
if (libusb_get_active_config_descriptor(dev, &config) < 0)
|
||||
usbres = libusb_get_active_config_descriptor(dev, &config);
|
||||
if (usbres < 0)
|
||||
{
|
||||
std::cout << "[Connection] Can't get config: " << libusb_error_name(usbres) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < config->interface[0].altsetting[0].bNumEndpoints; i++)
|
||||
{
|
||||
@@ -108,18 +130,14 @@ bool Connector::link()
|
||||
if ((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
|
||||
{
|
||||
_endpoint_in = ep->bEndpointAddress;
|
||||
std::cout << "Found input endpoint" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
_endpoint_out = ep->bEndpointAddress;
|
||||
std::cout << "Found output endpoint" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Free config descriptor" << std::endl;
|
||||
libusb_free_config_descriptor(config);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -135,58 +153,56 @@ void Connector::release()
|
||||
|
||||
void Connector::status(const char *status)
|
||||
{
|
||||
std::cout << status << std::endl;
|
||||
if (_protocol)
|
||||
_protocol->onStatus(status);
|
||||
}
|
||||
|
||||
void Connector::write_uint32_le(uint8_t *dst, uint32_t value)
|
||||
{
|
||||
dst[0] = value & 0xFF;
|
||||
dst[1] = (value >> 8) & 0xFF;
|
||||
dst[2] = (value >> 16) & 0xFF;
|
||||
dst[3] = (value >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
int Connector::send(int cmd)
|
||||
int Connector::send(int cmd, bool encrypt, uint8_t *data, uint32_t size)
|
||||
{
|
||||
if (!_connected)
|
||||
return 0;
|
||||
|
||||
int transferred;
|
||||
uint8_t buffer[16];
|
||||
uint32_t magic = MAGIC;
|
||||
encrypt = encrypt && _ecnrypt;
|
||||
|
||||
write_uint32_le(&buffer[0], 0x55AA55AA);
|
||||
write_uint32_le(&buffer[4], 0);
|
||||
write_uint32_le(&buffer[8], cmd);
|
||||
write_uint32_le(&buffer[12], ~cmd);
|
||||
if (encrypt && data && size > 0)
|
||||
{
|
||||
if (_cipher->Encrypt(data, size))
|
||||
magic = MAGIC_ENC;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(_write_mutex);
|
||||
libusb_bulk_transfer(_device, _endpoint_out, buffer, 16, &transferred, 0);
|
||||
|
||||
return transferred;
|
||||
}
|
||||
|
||||
int Connector::send(int cmd, uint8_t *data, uint32_t size)
|
||||
{
|
||||
if (!_connected)
|
||||
return 0;
|
||||
|
||||
int transferred;
|
||||
uint8_t buffer[16];
|
||||
|
||||
write_uint32_le(&buffer[0], 0x55AA55AA);
|
||||
write_uint32_le(&buffer[0], magic);
|
||||
write_uint32_le(&buffer[4], size);
|
||||
write_uint32_le(&buffer[8], cmd);
|
||||
write_uint32_le(&buffer[12], ~cmd);
|
||||
|
||||
std::unique_lock<std::mutex> lock(_write_mutex);
|
||||
libusb_bulk_transfer(_device, _endpoint_out, buffer, 16, &transferred, 0);
|
||||
libusb_bulk_transfer(_device, _endpoint_out, data, size, &transferred, 0);
|
||||
if (data && size > 0)
|
||||
libusb_bulk_transfer(_device, _endpoint_out, data, size, &transferred, 0);
|
||||
|
||||
return transferred;
|
||||
}
|
||||
|
||||
void Connector::setEncryption(bool enabled)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
_ecnrypt = false;
|
||||
return;
|
||||
}
|
||||
if (!_cipher)
|
||||
{
|
||||
std::cout << "[Connection] Can't enable encryption: cypher initialisation failed" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << "[Connection] Encryption enabled" << std::endl;
|
||||
_ecnrypt = true;
|
||||
}
|
||||
|
||||
void Connector::read_loop()
|
||||
{
|
||||
std::mutex mtx;
|
||||
@@ -196,7 +212,7 @@ void Connector::read_loop()
|
||||
uint8_t *data;
|
||||
|
||||
// Set thread name
|
||||
setThreadName( "protocol-reader");
|
||||
setThreadName("protocol-reader");
|
||||
while (_active)
|
||||
{
|
||||
if (!_connected)
|
||||
@@ -211,6 +227,7 @@ void Connector::read_loop()
|
||||
|
||||
if (result == LIBUSB_ERROR_NO_DEVICE)
|
||||
{
|
||||
std::cout << "[Connection] Device disconnected" << std::endl;
|
||||
if (_protocol)
|
||||
_protocol->onDevice(false);
|
||||
_connected = false;
|
||||
@@ -235,13 +252,29 @@ void Connector::read_loop()
|
||||
}
|
||||
|
||||
if (!_protocol)
|
||||
free(data);
|
||||
else
|
||||
{
|
||||
if (padding > 0)
|
||||
std::fill(data + header.length, data + header.length + padding, 0);
|
||||
_protocol->onData(header.type, header.length, data);
|
||||
free(data);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (header.magic == MAGIC_ENC)
|
||||
{
|
||||
|
||||
if (!_cipher)
|
||||
{
|
||||
std::cout << "[Connection] Received encrypted command " << header.type <<" but cipher is not initialised" << std::endl;
|
||||
continue;
|
||||
}
|
||||
if (!_cipher->Decrypt(data, header.length))
|
||||
{
|
||||
std::cout << "[Connection] Can't decrypt command " << header.type << std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (padding > 0)
|
||||
std::fill(data + header.length, data + header.length + padding, 0);
|
||||
_protocol->onData(header.type, header.length, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +291,7 @@ void Connector::write_loop()
|
||||
_connected = connect(Settings::vendorid, Settings::productid);
|
||||
if (_connected)
|
||||
{
|
||||
status("Starting device");
|
||||
status("Initialising dongle");
|
||||
if (_protocol)
|
||||
_protocol->onDevice(true);
|
||||
|
||||
|
||||
+12
-7
@@ -9,11 +9,14 @@
|
||||
#include <string>
|
||||
|
||||
#include "helper/iprotocol.h"
|
||||
#include "aes_cipher.h"
|
||||
|
||||
#define READ_TIMEOUT 5000
|
||||
#define ENCRYPTION_BASE "SkBRDy3gmrw1ieH0"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct Header {
|
||||
struct Header
|
||||
{
|
||||
uint32_t magic;
|
||||
uint32_t length;
|
||||
uint32_t type;
|
||||
@@ -31,10 +34,10 @@ public:
|
||||
void start(IProtocol *protocol);
|
||||
void stop();
|
||||
|
||||
int send(int cmd, uint8_t *data, uint32_t size);
|
||||
int send(int cmd);
|
||||
int send(int cmd, bool encrypt = true, uint8_t *data = nullptr, uint32_t size = 0);
|
||||
void setEncryption(bool enabled);
|
||||
|
||||
static void write_uint32_le(uint8_t *dst, uint32_t value);
|
||||
AESCipher *Cypher() const { return _cipher; };
|
||||
|
||||
private:
|
||||
void read_loop();
|
||||
@@ -43,14 +46,15 @@ private:
|
||||
bool connect(uint16_t vendor_id, uint16_t product_id);
|
||||
bool link();
|
||||
void release();
|
||||
|
||||
void status(const char* status);
|
||||
|
||||
void status(const char *status);
|
||||
|
||||
libusb_context *_context = nullptr;
|
||||
libusb_device_handle *_device = nullptr;
|
||||
uint8_t _endpoint_in;
|
||||
uint8_t _endpoint_out;
|
||||
bool _connected;
|
||||
std::atomic<bool> _ecnrypt = false;
|
||||
|
||||
std::thread _read_thread;
|
||||
std::thread _write_thread;
|
||||
@@ -59,7 +63,8 @@ private:
|
||||
|
||||
u_int16_t _videoPadding;
|
||||
|
||||
IProtocol* _protocol = nullptr;
|
||||
IProtocol *_protocol = nullptr;
|
||||
AESCipher *_cipher = nullptr;
|
||||
};
|
||||
|
||||
#endif /* SRC_CONNECTOR */
|
||||
|
||||
+11
-13
@@ -52,18 +52,18 @@ AVCodecContext *Decoder::load_codec(AVCodecID codec_id)
|
||||
result = avcodec_alloc_context3(codec);
|
||||
if (!result)
|
||||
{
|
||||
std::cout << "Can't load HW codec " << codec->name << ": out of memory" << std::endl;
|
||||
std::cout << "[Video] Can't load HW codec " << codec->name << ": out of memory" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
int ret = avcodec_open2(result, codec, nullptr);
|
||||
if (ret == 0)
|
||||
{
|
||||
std::cout << "Using HW decoder: " << codec->name << std::endl;
|
||||
std::cout << "[Video] Using HW decoder: " << codec->name << std::endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::cout << "Can't load HW codec " << codec->name << ": " << Error::avErrorText(ret) << std::endl;
|
||||
std::cout << "[Video] Can't load HW codec " << codec->name << ": " << Error::avErrorText(ret) << std::endl;
|
||||
avcodec_free_context(&result);
|
||||
}
|
||||
|
||||
@@ -71,33 +71,33 @@ AVCodecContext *Decoder::load_codec(AVCodecID codec_id)
|
||||
codec = avcodec_find_decoder(codec_id);
|
||||
if (!codec)
|
||||
{
|
||||
std::cout << "Decoder not found for codec id " << codec_id << std::endl;
|
||||
std::cout << "[Video] Decoder not found for codec id " << codec_id << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
result = avcodec_alloc_context3(codec);
|
||||
if (!result)
|
||||
{
|
||||
std::cout << "Failed to allocate context for codec id " << codec_id << std::endl;
|
||||
std::cout << "[Video] Failed to allocate context for codec id " << codec_id << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int ret = avcodec_open2(result, codec, nullptr);
|
||||
if (ret < 0)
|
||||
{
|
||||
std::cout << "Failed to open software decoder " << codec->name << ": " << Error::avErrorText(ret) << std::endl;
|
||||
std::cout << "[Video] Failed to open software decoder " << codec->name << ": " << Error::avErrorText(ret) << std::endl;
|
||||
avcodec_free_context(&result);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::cout << "Using SW decoder " << codec->name << std::endl;
|
||||
std::cout << "[Video] Using SW decoder " << codec->name << std::endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Decoder::runner()
|
||||
{
|
||||
// Set thread name
|
||||
setThreadName( "video-decoding");
|
||||
setThreadName("video-decoding");
|
||||
|
||||
// Load codec context
|
||||
AVCodecContext *context = load_codec(_codecId);
|
||||
@@ -127,15 +127,13 @@ void Decoder::runner()
|
||||
avcodec_free_context(&context);
|
||||
|
||||
if (_status.error())
|
||||
std::cout << "Video decoder error: " << _status.message() << std::endl;
|
||||
std::cout << "[Video] Decoder error: " << _status.message() << std::endl;
|
||||
}
|
||||
|
||||
void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPacket *packet, AVFrame *frame)
|
||||
{
|
||||
uint32_t counter = 0;
|
||||
|
||||
std::cout << "Video decoder loop started" << std::endl;
|
||||
|
||||
// Main decoding loop; runs until global_quit flag is set
|
||||
while (_active)
|
||||
{
|
||||
@@ -182,14 +180,14 @@ void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPack
|
||||
int send_ret = avcodec_send_packet(context, packet);
|
||||
if (send_ret != 0)
|
||||
{
|
||||
std::cout << "Error decoding packet: " << Error::avErrorText(send_ret) << std::endl;
|
||||
std::cout << "[Video] Can't decode packet: " << Error::avErrorText(send_ret) << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Receive decoded frames
|
||||
while (avcodec_receive_frame(context, frame) == 0 && _active)
|
||||
{
|
||||
AVFrame* out = _vb->write(counter++);
|
||||
AVFrame *out = _vb->write(counter++);
|
||||
av_frame_unref(out);
|
||||
av_frame_move_ref(out, frame);
|
||||
_vb->commit();
|
||||
|
||||
@@ -21,4 +21,12 @@ inline void disable_cout()
|
||||
std::cout.setstate(std::ios_base::failbit);
|
||||
}
|
||||
|
||||
inline void write_uint32_le(uint8_t *dst, uint32_t value)
|
||||
{
|
||||
dst[0] = value & 0xFF;
|
||||
dst[1] = (value >> 8) & 0xFF;
|
||||
dst[2] = (value >> 16) & 0xFF;
|
||||
dst[3] = (value >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
#endif /* SRC_HELPER_FUNCTIONS */
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef SRC_HELPER_PROTOCOL_CONST
|
||||
#define SRC_HELPER_PROTOCOL_CONST
|
||||
|
||||
#define MAGIC 0x55aa55aa
|
||||
#define MAGIC_ENC 0x55bb55bb
|
||||
|
||||
#define CMD_OPEN 1
|
||||
#define CMD_PLUGGED 2
|
||||
#define CMD_STATE 3
|
||||
#define CMD_UNPLUGGED 4
|
||||
#define CMD_TOUCH 5
|
||||
#define CMD_VIDEO_DATA 6
|
||||
#define CMD_AUDIO_DATA 7
|
||||
#define CMD_CONTROL 8
|
||||
#define CMD_UNKNOWN_9 9
|
||||
#define CMD_APP_INFO 10
|
||||
#define CMD_BLUETOOTH_INFO 13
|
||||
#define CMD_WIFI_INFO 14
|
||||
#define CMD_DEVICE_LIST 18
|
||||
#define CMD_JSON_CONTROL 25
|
||||
#define CMD_MANUFACTURER 20
|
||||
#define CMD_UNKNOWN_38 38
|
||||
#define CMD_SEND_FILE 153
|
||||
#define CMD_UNKNOWN_136 136
|
||||
#define CMD_DAYNIGHT 162
|
||||
#define CMD_HEARTBEAT 170
|
||||
#define CMD_VERSION 204
|
||||
#define CMD_ENCRYPTION 240
|
||||
|
||||
struct ProtocolCmdEntry
|
||||
{
|
||||
int cmd;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
const ProtocolCmdEntry protocolCmdList[] = {
|
||||
{CMD_OPEN, "Open"},
|
||||
{CMD_PLUGGED, "Plugged"},
|
||||
{CMD_STATE, "State"},
|
||||
{CMD_UNPLUGGED, "Unplugged"},
|
||||
{CMD_TOUCH, "Touch"},
|
||||
{CMD_VIDEO_DATA, "Video"},
|
||||
{CMD_AUDIO_DATA, "Audio"},
|
||||
{CMD_CONTROL, "Control Bin"},
|
||||
{CMD_APP_INFO, "AppInfo"},
|
||||
{CMD_BLUETOOTH_INFO, "Bluetooth Info"},
|
||||
{CMD_WIFI_INFO, "WiFi Info"},
|
||||
{CMD_DEVICE_LIST, "Device List"},
|
||||
{CMD_JSON_CONTROL, "Control JSON"},
|
||||
{CMD_MANUFACTURER, "Manufacturer"},
|
||||
{CMD_SEND_FILE, "File"},
|
||||
{CMD_DAYNIGHT, "DeyNight Mode"},
|
||||
{CMD_HEARTBEAT, "Heartbeat"},
|
||||
{CMD_VERSION, "Version"},
|
||||
{CMD_ENCRYPTION, "Encryption"}};
|
||||
|
||||
#endif /* SRC_HELPER_PROTOCOL_CONST */
|
||||
+4
-4
@@ -207,7 +207,7 @@ void application()
|
||||
DrawText(textFont, status);
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
std::cout << " > Application loop" << std::endl;
|
||||
std::cout << "[Main] Loop" << std::endl;
|
||||
Renderer videoRenderer(renderer);
|
||||
bool dirty = true;
|
||||
bool connected = false;
|
||||
@@ -276,7 +276,7 @@ void application()
|
||||
SDL_Delay(frameDelay - frameTime); // Sleep only the remaining time
|
||||
}
|
||||
}
|
||||
std::cout << " > Application stopping" << std::endl;
|
||||
std::cout << "[Main] Stopping" << std::endl;
|
||||
SDL_HideWindow(window);
|
||||
}
|
||||
|
||||
@@ -343,9 +343,9 @@ int main(int argc, char **argv)
|
||||
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||
if (renderer)
|
||||
{
|
||||
std::cout << " > Application started" << std::endl;
|
||||
std::cout << "[Main] Started" << std::endl;
|
||||
application();
|
||||
std::cout << " > Application finish" << std::endl;
|
||||
std::cout << "[Main] Finish" << std::endl;
|
||||
SDL_DestroyRenderer(renderer);
|
||||
}
|
||||
else
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ void PcmAudio::runner()
|
||||
device = SDL_OpenAudioDevice(nullptr, 0, &spec, nullptr, 0);
|
||||
if (device == 0)
|
||||
{
|
||||
std::cerr << "Failed to open audio: " << SDL_GetError() << std::endl;
|
||||
std::cerr << "[Audio] Failed to open audio: " << SDL_GetError() << std::endl;
|
||||
continue;
|
||||
}
|
||||
// Calculate new buffer target: 0.5s
|
||||
|
||||
+88
-55
@@ -1,6 +1,10 @@
|
||||
#include "protocol.h"
|
||||
#include "helper/protocol_const.h"
|
||||
#include "helper/functions.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <cctype>
|
||||
|
||||
Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t padding)
|
||||
: connector(padding),
|
||||
@@ -22,25 +26,12 @@ Protocol::~Protocol()
|
||||
|
||||
const char *Protocol::cmdString(int cmd)
|
||||
{
|
||||
switch (cmd)
|
||||
for (const ProtocolCmdEntry &entry : protocolCmdList)
|
||||
{
|
||||
case CMD_OPEN:
|
||||
return "Open";
|
||||
case CMD_PLUGGED:
|
||||
return "Plugged";
|
||||
case CMD_UNPLUGGED:
|
||||
return "Unplugged";
|
||||
case CMD_TOUCH:
|
||||
return "Touch";
|
||||
case CMD_VIDEO_DATA:
|
||||
return "Video";
|
||||
case CMD_AUDIO_DATA:
|
||||
return "Audio";
|
||||
case CMD_SEND_FILE:
|
||||
return "File";
|
||||
default:
|
||||
return "Unknown";
|
||||
if (entry.cmd == cmd)
|
||||
return entry.name;
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
void Protocol::start(StatusCallback onStatus)
|
||||
@@ -57,15 +48,15 @@ void Protocol::stop()
|
||||
void Protocol::sendInit(int width, int height, int fps)
|
||||
{
|
||||
uint8_t buf[28];
|
||||
Connector::write_uint32_le(&buf[0], width);
|
||||
Connector::write_uint32_le(&buf[4], height);
|
||||
Connector::write_uint32_le(&buf[8], fps);
|
||||
Connector::write_uint32_le(&buf[12], 5);
|
||||
Connector::write_uint32_le(&buf[16], 49152);
|
||||
Connector::write_uint32_le(&buf[20], 2);
|
||||
Connector::write_uint32_le(&buf[24], 2);
|
||||
write_uint32_le(&buf[0], width);
|
||||
write_uint32_le(&buf[4], height);
|
||||
write_uint32_le(&buf[8], fps);
|
||||
write_uint32_le(&buf[12], 5);
|
||||
write_uint32_le(&buf[16], 49152);
|
||||
write_uint32_le(&buf[20], 2);
|
||||
write_uint32_le(&buf[24], 2);
|
||||
|
||||
connector.send(1, buf, 28);
|
||||
connector.send(1, true, buf, 28);
|
||||
}
|
||||
|
||||
void Protocol::sendKey(int key)
|
||||
@@ -73,9 +64,9 @@ void Protocol::sendKey(int key)
|
||||
printf("Send key %d", key);
|
||||
|
||||
uint8_t buf[4];
|
||||
Connector::write_uint32_le(&buf[0], key);
|
||||
write_uint32_le(&buf[0], key);
|
||||
|
||||
connector.send(8, buf, 4);
|
||||
connector.send(8, false, buf, 4);
|
||||
}
|
||||
|
||||
void Protocol::sendFile(const char *filename, const char *value)
|
||||
@@ -96,28 +87,28 @@ void Protocol::sendFile(const char *filename, const char *value)
|
||||
void Protocol::sendFile(const char *filename, int value)
|
||||
{
|
||||
uint8_t buf[4];
|
||||
Connector::write_uint32_le(buf, value);
|
||||
write_uint32_le(buf, value);
|
||||
sendFile(filename, buf, 4);
|
||||
}
|
||||
|
||||
void Protocol::sendClick(float x, float y, bool down)
|
||||
{
|
||||
uint8_t buf[16];
|
||||
Connector::write_uint32_le(buf, down ? 14 : 16);
|
||||
Connector::write_uint32_le(buf + 4, int(10000 * x));
|
||||
Connector::write_uint32_le(buf + 8, int(10000 * y));
|
||||
Connector::write_uint32_le(buf + 12, 0);
|
||||
connector.send(5, buf, 16);
|
||||
write_uint32_le(buf, down ? 14 : 16);
|
||||
write_uint32_le(buf + 4, int(10000 * x));
|
||||
write_uint32_le(buf + 8, int(10000 * y));
|
||||
write_uint32_le(buf + 12, 0);
|
||||
connector.send(5, false, buf, 16);
|
||||
}
|
||||
|
||||
void Protocol::sendMove(float dx, float dy)
|
||||
{
|
||||
uint8_t buf[16];
|
||||
Connector::write_uint32_le(buf, 15);
|
||||
Connector::write_uint32_le(buf + 4, int(10000 * dx));
|
||||
Connector::write_uint32_le(buf + 8, int(10000 * dy));
|
||||
Connector::write_uint32_le(buf + 12, 0);
|
||||
connector.send(5, buf, 16);
|
||||
write_uint32_le(buf, 15);
|
||||
write_uint32_le(buf + 4, int(10000 * dx));
|
||||
write_uint32_le(buf + 8, int(10000 * dy));
|
||||
write_uint32_le(buf + 12, 0);
|
||||
connector.send(5, false, buf, 16);
|
||||
}
|
||||
|
||||
void Protocol::sendFile(const char *filename, const uint8_t *data, uint32_t length)
|
||||
@@ -131,7 +122,7 @@ void Protocol::sendFile(const char *filename, const uint8_t *data, uint32_t leng
|
||||
uint8_t *buf = result.data();
|
||||
|
||||
// 1) filename length (LE)
|
||||
Connector::write_uint32_le(buf, fn_len);
|
||||
write_uint32_le(buf, fn_len);
|
||||
buf += 4;
|
||||
|
||||
// 2) filename bytes (including the '\0')
|
||||
@@ -139,13 +130,33 @@ void Protocol::sendFile(const char *filename, const uint8_t *data, uint32_t leng
|
||||
buf += fn_len;
|
||||
|
||||
// 3) content length (LE)
|
||||
Connector::write_uint32_le(buf, length);
|
||||
write_uint32_le(buf, length);
|
||||
buf += 4;
|
||||
|
||||
// 4) content bytes
|
||||
std::memcpy(buf, data, length);
|
||||
|
||||
connector.send(CMD_SEND_FILE, result.data(), total);
|
||||
connector.send(CMD_SEND_FILE, true, result.data(), total);
|
||||
}
|
||||
|
||||
void Protocol::sendInt(uint32_t cmd, uint32_t value, bool encryption)
|
||||
{
|
||||
uint8_t buf[4];
|
||||
write_uint32_le(buf, value);
|
||||
connector.send(cmd, encryption, buf, 4);
|
||||
}
|
||||
|
||||
void Protocol::sendEncryption()
|
||||
{
|
||||
AESCipher *cypher = connector.Cypher();
|
||||
if (!cypher)
|
||||
{
|
||||
std::cout << "[Protocol] Can't enable encryption: cypher is not initalised";
|
||||
return;
|
||||
}
|
||||
uint8_t buf[4];
|
||||
write_uint32_le(buf, cypher->Seed());
|
||||
connector.send(CMD_ENCRYPTION, false, buf, 4);
|
||||
}
|
||||
|
||||
void Protocol::onStatus(const char *status)
|
||||
@@ -158,14 +169,24 @@ void Protocol::onDevice(bool connected)
|
||||
{
|
||||
if (connected)
|
||||
{
|
||||
if (Settings::encryption)
|
||||
sendEncryption();
|
||||
sendInit(_width, _height, _fps);
|
||||
sendFile("/tmp/night_mode", 0); // 0==day, 1==night
|
||||
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);
|
||||
sendFile("/etc/box_name", "CarPlay");
|
||||
if (Settings::autoconnect)
|
||||
sendInt(CMD_CONTROL, 1002);
|
||||
if (Settings::encryption)
|
||||
sendEncryption();
|
||||
}
|
||||
else
|
||||
{
|
||||
connector.setEncryption(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +199,7 @@ void Protocol::onData(uint32_t cmd, uint32_t length, uint8_t *data)
|
||||
{
|
||||
if (length <= 20)
|
||||
break;
|
||||
videoData.pushDiscard( std::make_unique<Message>(data, length, 20));
|
||||
videoData.pushDiscard(std::make_unique<Message>(data, length, 20));
|
||||
dispose = false;
|
||||
break;
|
||||
}
|
||||
@@ -222,6 +243,13 @@ void Protocol::onData(uint32_t cmd, uint32_t length, uint8_t *data)
|
||||
phoneConnected = false;
|
||||
break;
|
||||
}
|
||||
case CMD_ENCRYPTION:
|
||||
{
|
||||
if (length == 0)
|
||||
connector.setEncryption(true);
|
||||
print_message(cmd, length, data);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
print_message(cmd, length, data);
|
||||
@@ -236,7 +264,7 @@ void Protocol::print_ints(uint32_t length, uint8_t *data, uint16_t max)
|
||||
{
|
||||
if (data && length >= 4)
|
||||
{
|
||||
printf(" > ");
|
||||
std::cout << " > ";
|
||||
size_t count = length / 4;
|
||||
for (size_t i = 0; (i < count) & (i < max); ++i)
|
||||
{
|
||||
@@ -245,9 +273,9 @@ void Protocol::print_ints(uint32_t length, uint8_t *data, uint16_t max)
|
||||
((uint32_t)data[i * 4 + 1] << 8) |
|
||||
((uint32_t)data[i * 4 + 2] << 16) |
|
||||
((uint32_t)data[i * 4 + 3] << 24);
|
||||
printf("%u ", val);
|
||||
std::cout << val;
|
||||
}
|
||||
printf("\n");
|
||||
std::cout << endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,25 +283,30 @@ void Protocol::print_bytes(uint32_t length, uint8_t *data, uint16_t max)
|
||||
{
|
||||
if (data && length >= 4)
|
||||
{
|
||||
printf(" > ");
|
||||
std::cout << " > ";
|
||||
for (size_t i = 0; (i < length) & (i < max); ++i)
|
||||
{
|
||||
printf("%d ", data[i]);
|
||||
std::cout << data[i];
|
||||
}
|
||||
printf("\n");
|
||||
std::cout << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Protocol::print_message(uint32_t cmd, uint32_t length, uint8_t *data)
|
||||
{
|
||||
printf("Cmd: %-3u %-10s Size: %-6u > ", cmd, cmdString(cmd), length);
|
||||
std::cout << "> "
|
||||
<< std::setw(3) << std::right << static_cast<unsigned>(cmd)
|
||||
<< std::setw(8) << std::left << ("[" + std::to_string(length) + "]")
|
||||
<< std::setw(15) << std::left << cmdString(cmd);
|
||||
|
||||
if (data && length > 0)
|
||||
for (size_t i = 0; i < 40 && i < length; ++i)
|
||||
{
|
||||
for (size_t i = 0; i < 50 && i < length; ++i)
|
||||
{
|
||||
char ch = static_cast<char>(data[i]);
|
||||
printf("%c", isprint(ch) ? ch : '.');
|
||||
std::cout << (std::isprint(static_cast<unsigned char>(ch)) ? ch : '.');
|
||||
}
|
||||
// for (int i = 0; i < length && i < 10; i++)
|
||||
// printf("%-4u", data[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
+3
-9
@@ -7,15 +7,6 @@
|
||||
#include "settings.h"
|
||||
#include "connector.h"
|
||||
|
||||
#define MAGIC 0x55aa55aa
|
||||
|
||||
#define CMD_OPEN 1
|
||||
#define CMD_PLUGGED 2
|
||||
#define CMD_UNPLUGGED 4
|
||||
#define CMD_TOUCH 5
|
||||
#define CMD_VIDEO_DATA 6
|
||||
#define CMD_AUDIO_DATA 7
|
||||
#define CMD_SEND_FILE 153
|
||||
|
||||
class Protocol : public IProtocol
|
||||
{
|
||||
@@ -48,6 +39,9 @@ public:
|
||||
bool phoneConnected;
|
||||
|
||||
private:
|
||||
void sendInt(uint32_t cmd, uint32_t value, bool encryption = true);
|
||||
void sendEncryption();
|
||||
|
||||
void onStatus(const char *status) override;
|
||||
void onDevice(bool connected) override;
|
||||
void onData(uint32_t cmd, uint32_t length, uint8_t *data) override;
|
||||
|
||||
+5
-2
@@ -10,6 +10,7 @@ public:
|
||||
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", 60};
|
||||
@@ -18,8 +19,10 @@ public:
|
||||
static inline Setting<int> sourceFps{"source-fps", 30};
|
||||
static inline Setting<bool> logging{"logging", false};
|
||||
static inline Setting<int> scaler{"scaler", 2};
|
||||
static inline Setting<int> queue{"queue-size", 32};
|
||||
static inline Setting<int> fontSize{"font-size", 30};
|
||||
static inline Setting<int> queue{"queue-size", 32};
|
||||
static inline Setting<int> fontSize{"font-size", 30};
|
||||
static inline Setting<bool> encryption{"encryption", false};
|
||||
static inline Setting<bool> autoconnect{"autoconnect", true};
|
||||
|
||||
static void load(const std::string &filename);
|
||||
static void print();
|
||||
|
||||
Reference in New Issue
Block a user