mirror of
https://github.com/niellun/FastCarPlay.git
synced 2026-06-07 09:38:25 +02:00
Message queue for sending
This commit is contained in:
+7
-7
@@ -109,6 +109,13 @@
|
||||
# Corrects aspect of UI
|
||||
#aspect-correction = 1
|
||||
|
||||
# Force SDL renderer backend, leave empty for automatic
|
||||
# Examples: opengles2, opengl, software
|
||||
#renderer-driver =
|
||||
|
||||
# Use alternative video rendering method. May reduce CPU load and increase smoothness.
|
||||
#alternative-rendering = false
|
||||
|
||||
# Select faster method of scaling image to window size (nearest) or better quality (linear)
|
||||
#fast-render-scale = false
|
||||
|
||||
@@ -139,13 +146,6 @@
|
||||
# pipewire
|
||||
#audio-driver =
|
||||
|
||||
# Force SDL renderer backend, leave empty for automatic
|
||||
# Examples: opengles2, opengl, software
|
||||
#renderer-driver =
|
||||
|
||||
# Use alternative video rendering method. May reduce CPU load and increase smoothness.
|
||||
#alternative-rendering = false
|
||||
|
||||
# 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
|
||||
|
||||
+6
-6
@@ -270,7 +270,7 @@ bool Application::processFrameEvents(Protocol &protocol, Renderer &renderer)
|
||||
int key = processKey(e.key.keysym);
|
||||
if (key > 0)
|
||||
{
|
||||
protocol.sendKey(key);
|
||||
protocol.send(Command::Control(key));
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
@@ -279,7 +279,7 @@ bool Application::processFrameEvents(Protocol &protocol, Renderer &renderer)
|
||||
{
|
||||
if (e.key.keysym.sym == Settings::keyEnter)
|
||||
{
|
||||
protocol.sendKey(Settings::keyEnterUp.key);
|
||||
protocol.send(Command::Control(Settings::keyEnterUp.key));
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
@@ -290,11 +290,11 @@ bool Application::processFrameEvents(Protocol &protocol, Renderer &renderer)
|
||||
if (_state.frameRendered && (downX >= 0 || upX >= 0 || motionX >= 0))
|
||||
{
|
||||
if (downX >= 0)
|
||||
protocol.sendClick(renderer.xScale * downX / _width, renderer.yScale * downY / _height, true);
|
||||
protocol.send(Command::Click(renderer.xScale * downX / _width, renderer.yScale * downY / _height, true));
|
||||
if (motionX >= 0)
|
||||
protocol.sendMove(renderer.xScale * motionX / _width, renderer.yScale * motionY / _height);
|
||||
protocol.send(Command::Move(renderer.xScale * motionX / _width, renderer.yScale * motionY / _height));
|
||||
if (upX >= 0)
|
||||
protocol.sendClick(renderer.xScale * upX / _width, renderer.yScale * upY / _height, false);
|
||||
protocol.send(Command::Click(renderer.xScale * upX / _width, renderer.yScale * upY / _height, false));
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -358,7 +358,7 @@ void Application::loop()
|
||||
{
|
||||
if (latestFrameid == requestFrameId)
|
||||
{
|
||||
protocol.requestKeyframe();
|
||||
protocol.send(Command::Control(BTN_SCREEN_REFRESH));
|
||||
_state.requestFrame = 1;
|
||||
requestFrameId = latestFrameid;
|
||||
}
|
||||
|
||||
+27
-12
@@ -30,6 +30,7 @@ Connector::Connector(uint16_t videoPadding)
|
||||
Connector::~Connector()
|
||||
{
|
||||
_active = false;
|
||||
_queue.notify();
|
||||
|
||||
if (_write_thread.joinable())
|
||||
_write_thread.join();
|
||||
@@ -69,6 +70,7 @@ void Connector::stop()
|
||||
return;
|
||||
|
||||
_active = false;
|
||||
_queue.notify();
|
||||
|
||||
state(PROTOCOL_STATUS_INITIALISING);
|
||||
|
||||
@@ -181,7 +183,15 @@ bool Connector::linkFail(int status, const char *msg)
|
||||
return true;
|
||||
}
|
||||
|
||||
int Connector::send(int cmd, bool encrypt, uint8_t *data, uint32_t size)
|
||||
bool Connector::send(std::unique_ptr<Command> packet)
|
||||
{
|
||||
if (!_connected || !packet)
|
||||
return false;
|
||||
|
||||
return _queue.pushDiscard(std::move(packet));
|
||||
}
|
||||
|
||||
int Connector::write(int cmd, bool encrypt, uint8_t *data, uint32_t size)
|
||||
{
|
||||
if (!_connected)
|
||||
return 0;
|
||||
@@ -380,9 +390,6 @@ void Connector::readLoop()
|
||||
|
||||
void Connector::writeLoop()
|
||||
{
|
||||
std::mutex mtx;
|
||||
std::condition_variable cv;
|
||||
|
||||
// Set thread name
|
||||
setThreadName("protocol-writer");
|
||||
state(PROTOCOL_STATUS_LINKING);
|
||||
@@ -400,10 +407,18 @@ void Connector::writeLoop()
|
||||
state(PROTOCOL_STATUS_ONLINE);
|
||||
while (_connected && _active)
|
||||
{
|
||||
send(170);
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
cv.wait_for(lock, std::chrono::seconds(2), [&]()
|
||||
{ return !_active.load(); });
|
||||
std::unique_ptr<Command> message = _queue.pop();
|
||||
if (!message)
|
||||
{
|
||||
if (!_queue.waitFor(_active, 2000))
|
||||
break;
|
||||
message = _queue.pop();
|
||||
}
|
||||
|
||||
if (message)
|
||||
write(message->command, message->encryption, message->data, message->length);
|
||||
else
|
||||
write(CMD_HEARTBEAT, false, nullptr, 0);
|
||||
}
|
||||
|
||||
if (_active)
|
||||
@@ -412,11 +427,11 @@ void Connector::writeLoop()
|
||||
onDevice(false);
|
||||
}
|
||||
|
||||
_queue.clear();
|
||||
|
||||
if (_read_thread.joinable())
|
||||
_read_thread.join();
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
cv.wait_for(lock, std::chrono::seconds(1), [&]()
|
||||
{ return !_active.load(); });
|
||||
_queue.waitFor(_active, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -8,7 +8,10 @@
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "helper/isender.h"
|
||||
#include "aes_cipher.h"
|
||||
#include "struct/atomic_queue.h"
|
||||
#include "struct/command.h"
|
||||
|
||||
#define READ_TIMEOUT 3000
|
||||
#define ENCRYPTION_BASE "SkBRDy3gmrw1ieH0"
|
||||
@@ -29,7 +32,7 @@ struct Header
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
class Connector
|
||||
class Connector : public ISender
|
||||
{
|
||||
|
||||
public:
|
||||
@@ -38,10 +41,9 @@ public:
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
bool send(std::unique_ptr<Command> packet) override;
|
||||
|
||||
protected:
|
||||
|
||||
int send(int cmd, bool encrypt = true, uint8_t *data = nullptr, uint32_t size = 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;
|
||||
@@ -66,12 +68,13 @@ private:
|
||||
void state(u_int8_t state);
|
||||
bool nextState(u_int8_t state);
|
||||
bool linkFail(int status, const char *msg);
|
||||
int write(int cmd, bool encrypt, uint8_t *data, uint32_t size);
|
||||
|
||||
libusb_context *_context = nullptr;
|
||||
libusb_device_handle *_device = nullptr;
|
||||
uint8_t _endpoint_in;
|
||||
uint8_t _endpoint_out;
|
||||
bool _connected;
|
||||
std::atomic<bool> _connected = false;
|
||||
std::atomic<bool> _ecnrypt = false;
|
||||
|
||||
uint8_t _state;
|
||||
@@ -83,6 +86,7 @@ private:
|
||||
std::thread _write_thread;
|
||||
std::mutex _write_mutex;
|
||||
std::atomic<bool> _active = false;
|
||||
AtomicQueue<Command> _queue{256};
|
||||
|
||||
u_int16_t _videoPadding;
|
||||
};
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#ifndef SRC_HELPER_IAUDIO_SENDER
|
||||
#define SRC_HELPER_IAUDIO_SENDER
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
class IAudioSender {
|
||||
public:
|
||||
virtual ~IAudioSender() = default;
|
||||
virtual void sendAudio(uint8_t* data, uint32_t length) = 0;
|
||||
};
|
||||
|
||||
#endif /* SRC_HELPER_IAUDIO_SENDER */
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef SRC_HELPER_ISENDER
|
||||
#define SRC_HELPER_ISENDER
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "struct/command.h"
|
||||
|
||||
class ISender
|
||||
{
|
||||
public:
|
||||
virtual ~ISender() = default;
|
||||
virtual bool send(std::unique_ptr<Command> packet) = 0;
|
||||
};
|
||||
|
||||
#endif /* SRC_HELPER_ISENDER */
|
||||
+24
-180
@@ -15,7 +15,6 @@ Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t paddi
|
||||
_width(width),
|
||||
_height(height),
|
||||
_fps(fps),
|
||||
_keySent(false),
|
||||
_phoneConnected(false)
|
||||
{
|
||||
}
|
||||
@@ -37,20 +36,6 @@ void Protocol::stop()
|
||||
Connector::stop();
|
||||
}
|
||||
|
||||
void Protocol::sendInit(int width, int height, int fps)
|
||||
{
|
||||
uint8_t buf[28];
|
||||
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);
|
||||
|
||||
send(CMD_OPEN, true, buf, 28);
|
||||
}
|
||||
|
||||
void Protocol::sendConfig()
|
||||
{
|
||||
int syncTime = std::time(nullptr);
|
||||
@@ -91,171 +76,25 @@ void Protocol::sendConfig()
|
||||
|
||||
std::cout << "[Protocol] Request android image " << width << "x" << height << std::endl;
|
||||
|
||||
char buffer[512];
|
||||
snprintf(buffer, sizeof(buffer),
|
||||
"{\"syncTime\":%d,\"mediaDelay\":%d,\"drivePosition\":%d,"
|
||||
"\"androidAutoSizeW\":%d,\"androidAutoSizeH\":%d,\"HiCarConnectMode\":0,"
|
||||
"\"GNSSCapability\":7,\"DashboardInfo\":1,\"UseBTPhone\":0}",
|
||||
syncTime, Settings::mediaDelay.value, drivePosition, width, height);
|
||||
send(Command::String(
|
||||
CMD_JSON_CONTROL,
|
||||
"{\"syncTime\":%d,\"mediaDelay\":%d,\"drivePosition\":%d,"
|
||||
"\"androidAutoSizeW\":%d,\"androidAutoSizeH\":%d,\"HiCarConnectMode\":0,"
|
||||
"\"GNSSCapability\":7,\"DashboardInfo\":1,\"UseBTPhone\":0}",
|
||||
syncTime, Settings::mediaDelay.value, drivePosition, width, height));
|
||||
|
||||
sendString(CMD_JSON_CONTROL, buffer);
|
||||
send(Command::String(CMD_DAYNIGHT, "{\"DayNightMode\":%d}", nightMode));
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "{\"DayNightMode\":%d}", nightMode);
|
||||
sendString(CMD_DAYNIGHT, buffer);
|
||||
send(Command::File("/tmp/night_mode", nightMode));
|
||||
send(Command::File("/tmp/charge_mode", Settings::weakCharge ? 0 : 2)); // Weak charge 0, other 2
|
||||
send(Command::File("/etc/box_name", "CarPlay"));
|
||||
send(Command::File("/tmp/hand_drive_mode", drivePosition));
|
||||
|
||||
sendFile("/tmp/night_mode", nightMode);
|
||||
sendFile("/tmp/charge_mode", Settings::weakCharge ? 0 : 2); // Weak charge 0, other 2
|
||||
sendFile("/etc/box_name", "CarPlay");
|
||||
sendFile("/tmp/hand_drive_mode", drivePosition);
|
||||
|
||||
sendInt(CMD_CONTROL, mic);
|
||||
sendInt(CMD_CONTROL, Settings::wifi5 ? 25 : 24);
|
||||
sendInt(CMD_CONTROL, Settings::bluetoothAudio ? 22 : 23);
|
||||
send(Command::Control(mic));
|
||||
send(Command::Control(Settings::wifi5 ? 25 : 24));
|
||||
send(Command::Control(Settings::bluetoothAudio ? 22 : 23));
|
||||
if (Settings::autoconnect)
|
||||
sendInt(CMD_CONTROL, 1002);
|
||||
}
|
||||
|
||||
bool Protocol::checkKey()
|
||||
{
|
||||
bool result = _keySent;
|
||||
_keySent = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Protocol::sendKey(int key)
|
||||
{
|
||||
sendInt(CMD_CONTROL, key, false);
|
||||
_keySent = true;
|
||||
}
|
||||
|
||||
void Protocol::requestKeyframe()
|
||||
{
|
||||
sendInt(CMD_CONTROL, BTN_SCREEN_REFRESH, false);
|
||||
}
|
||||
|
||||
void Protocol::sendFile(const char *filename, const char *value)
|
||||
{
|
||||
uint32_t len = strlen(value);
|
||||
if (len > 16)
|
||||
{
|
||||
throw std::invalid_argument("String too long (max 16 bytes)");
|
||||
}
|
||||
|
||||
// note: we send only the ASCII bytes, no trailing '\0'
|
||||
sendFile(filename,
|
||||
reinterpret_cast<const uint8_t *>(value),
|
||||
static_cast<uint32_t>(len));
|
||||
}
|
||||
|
||||
// overload for a single 32‑bit integer
|
||||
void Protocol::sendFile(const char *filename, int value)
|
||||
{
|
||||
uint8_t buf[4];
|
||||
write_uint32_le(buf, value);
|
||||
sendFile(filename, buf, 4);
|
||||
}
|
||||
|
||||
void Protocol::sendClick(float x, float y, bool down)
|
||||
{
|
||||
uint8_t 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);
|
||||
send(CMD_TOUCH, false, buf, 16);
|
||||
}
|
||||
|
||||
void Protocol::sendMultiTouch(const Multitouch &touches)
|
||||
{
|
||||
int count = touches.size();
|
||||
if (count == 0)
|
||||
return;
|
||||
|
||||
uint8_t buf[MUTLITOUCH_MAX_TOUCH * sizeof(Multitouch::Touch)];
|
||||
uint8_t *p = buf;
|
||||
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
const Multitouch::Touch &t = touches[i];
|
||||
write_float_le(p + 0, t.x);
|
||||
write_float_le(p + 4, t.y);
|
||||
write_uint32_le(p + 8, static_cast<uint32_t>(t.state));
|
||||
write_uint32_le(p + 12, static_cast<uint32_t>(t.id));
|
||||
p += 16;
|
||||
}
|
||||
|
||||
send(CMD_MULTI_TOUCH, false, buf, 16 * count);
|
||||
}
|
||||
|
||||
void Protocol::sendMove(float dx, float dy)
|
||||
{
|
||||
uint8_t 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);
|
||||
send(CMD_TOUCH, false, buf, 16);
|
||||
}
|
||||
|
||||
void Protocol::sendAudio(uint8_t *data, uint32_t length)
|
||||
{
|
||||
write_uint32_le(data, 5);
|
||||
write_uint32_le(data + 4, 0);
|
||||
write_uint32_le(data + 8, 3);
|
||||
send(CMD_AUDIO_DATA, false, data, length);
|
||||
}
|
||||
|
||||
void Protocol::sendFile(const char *filename, const uint8_t *data, uint32_t length)
|
||||
{
|
||||
// filename is assumed null‑terminated, so strlen + 1 to include the '\0'
|
||||
uint32_t fn_len = strlen(filename) + 1;
|
||||
|
||||
// Total buffer size: 4 (fn_len) + fn_len + 4 (content_len) + content_len
|
||||
uint32_t total = 4 + fn_len + 4 + length;
|
||||
std::vector<uint8_t> result(total);
|
||||
uint8_t *buf = result.data();
|
||||
|
||||
// 1) filename length (LE)
|
||||
write_uint32_le(buf, fn_len);
|
||||
buf += 4;
|
||||
|
||||
// 2) filename bytes (including the '\0')
|
||||
std::memcpy(buf, filename, fn_len);
|
||||
buf += fn_len;
|
||||
|
||||
// 3) content length (LE)
|
||||
write_uint32_le(buf, length);
|
||||
buf += 4;
|
||||
|
||||
// 4) content bytes
|
||||
std::memcpy(buf, data, length);
|
||||
|
||||
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);
|
||||
send(cmd, encryption, buf, 4);
|
||||
}
|
||||
|
||||
void Protocol::sendString(uint32_t cmd, char *str, bool encryption)
|
||||
{
|
||||
uint32_t total = strlen(str);
|
||||
send(cmd, true, (uint8_t *)str, total);
|
||||
}
|
||||
|
||||
void Protocol::sendEncryption()
|
||||
{
|
||||
if (!_cipher)
|
||||
{
|
||||
std::cout << "[Protocol] Can't enable encryption: cypher is not initalised" << std::endl;
|
||||
return;
|
||||
}
|
||||
uint8_t buf[4];
|
||||
write_uint32_le(buf, _cipher->Seed());
|
||||
send(CMD_ENCRYPTION, false, buf, 4);
|
||||
send(Command::Control(1002));
|
||||
}
|
||||
|
||||
void Protocol::onStatus(uint8_t status)
|
||||
@@ -269,11 +108,16 @@ void Protocol::onDevice(bool connected)
|
||||
if (connected)
|
||||
{
|
||||
if (Settings::encryption)
|
||||
sendEncryption();
|
||||
{
|
||||
if (_cipher)
|
||||
send(Command::Encryption(_cipher->Seed()));
|
||||
else
|
||||
std::cout << "[Protocol] Can't enable encryption: cypher is not initalised" << std::endl;
|
||||
}
|
||||
if (Settings::dpi > 0)
|
||||
sendFile("/tmp/screen_dpi", Settings::dpi);
|
||||
sendFile("/etc/android_work_mode", 1);
|
||||
sendInit(_width, _height, _fps);
|
||||
send(Command::File("/tmp/screen_dpi", Settings::dpi));
|
||||
send(Command::File("/etc/android_work_mode", 1));
|
||||
send(Command::Init(_width, _height, _fps));
|
||||
sendConfig();
|
||||
}
|
||||
else
|
||||
|
||||
+1
-19
@@ -4,12 +4,11 @@
|
||||
#include "struct/atomic_queue.h"
|
||||
#include "struct/message.h"
|
||||
#include "struct/multitouch.h"
|
||||
#include "helper/iaudio_sender.h"
|
||||
#include "settings.h"
|
||||
#include "connector.h"
|
||||
#include "recorder.h"
|
||||
|
||||
class Protocol : private Connector, public IAudioSender
|
||||
class Protocol : public Connector
|
||||
{
|
||||
|
||||
public:
|
||||
@@ -22,27 +21,11 @@ public:
|
||||
void start(uint32_t evtStatus, uint32_t evtPhone);
|
||||
void stop();
|
||||
|
||||
|
||||
bool checkKey();
|
||||
void sendKey(int key);
|
||||
void requestKeyframe();
|
||||
void sendFile(const char *filename, const uint8_t *data, uint32_t length);
|
||||
void sendFile(const char *filename, const char *value);
|
||||
void sendFile(const char *filename, int value);
|
||||
void sendClick(float x, float y, bool down);
|
||||
void sendMultiTouch(const Multitouch &touches);
|
||||
void sendMove(float dx, float dy);
|
||||
void sendAudio(uint8_t *data, uint32_t length) override;
|
||||
|
||||
AtomicQueue<Message> videoData;
|
||||
AtomicQueue<Message> audioStreamMain;
|
||||
AtomicQueue<Message> audioStreamAux;
|
||||
|
||||
private:
|
||||
void sendInt(uint32_t cmd, uint32_t value, bool encryption = true);
|
||||
void sendString(uint32_t cmd, char *str, bool encryption = true);
|
||||
void sendEncryption();
|
||||
void sendInit(int width, int height, int fps);
|
||||
void sendConfig();
|
||||
|
||||
void onStatus(uint8_t status) override;
|
||||
@@ -57,7 +40,6 @@ private:
|
||||
uint16_t _width;
|
||||
uint16_t _height;
|
||||
uint16_t _fps;
|
||||
bool _keySent;
|
||||
bool _phoneConnected;
|
||||
|
||||
uint32_t _evtStatusId = (uint32_t)-1;
|
||||
|
||||
+3
-2
@@ -6,6 +6,7 @@
|
||||
#include "helper/protocol_const.h"
|
||||
#include "helper/functions.h"
|
||||
#include "settings.h"
|
||||
#include "struct/command.h"
|
||||
|
||||
|
||||
Recorder::Recorder(uint16_t buffSize)
|
||||
@@ -20,7 +21,7 @@ Recorder::~Recorder()
|
||||
_thread.join();
|
||||
}
|
||||
|
||||
void Recorder::start(IAudioSender *sender)
|
||||
void Recorder::start(ISender *sender)
|
||||
{
|
||||
if (_active)
|
||||
return;
|
||||
@@ -78,7 +79,7 @@ void Recorder::runner()
|
||||
{
|
||||
std::unique_ptr<AudioChunk> buffer = _data.pop();
|
||||
if (buffer && _sender)
|
||||
_sender->sendAudio(buffer.get()->data, buffer.get()->size);
|
||||
_sender->send(Command::Audio(std::move(buffer)));
|
||||
else if (_active)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
|
||||
|
||||
+4
-24
@@ -6,44 +6,24 @@
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include "helper/iaudio_sender.h"
|
||||
#include "helper/isender.h"
|
||||
#include "struct/audio_chunk.h"
|
||||
#include "struct/atomic_queue.h"
|
||||
|
||||
class AudioChunk
|
||||
{
|
||||
public:
|
||||
AudioChunk(uint16_t size)
|
||||
: data(new uint8_t[size]), size(size)
|
||||
{
|
||||
}
|
||||
|
||||
~AudioChunk()
|
||||
{
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
// Deleted copy constructor/assignment
|
||||
AudioChunk(const AudioChunk &) = delete;
|
||||
AudioChunk &operator=(const AudioChunk &) = delete;
|
||||
|
||||
uint8_t *data;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
class Recorder
|
||||
{
|
||||
public:
|
||||
Recorder(uint16_t buffSize);
|
||||
~Recorder();
|
||||
|
||||
void start(IAudioSender *sender);
|
||||
void start(ISender *sender);
|
||||
void stop();
|
||||
|
||||
private:
|
||||
static void AudioCallback(void *userdata, Uint8 *stream, int len);
|
||||
void runner();
|
||||
|
||||
IAudioSender *_sender;
|
||||
ISender *_sender;
|
||||
std::atomic<bool> _active;
|
||||
std::thread _thread;
|
||||
AtomicQueue<AudioChunk> _data;
|
||||
|
||||
+3
-2
@@ -41,8 +41,9 @@ public:
|
||||
static inline Setting<bool> hwDecode{"hw-decode", true};
|
||||
static inline Setting<int> forceRedraw{"force-redraw", 0};
|
||||
static inline Setting<float> aspectCorrection{"aspect-correction", 1};
|
||||
static inline Setting<std::string> renderDriver{"renderer-driver", ""};
|
||||
static inline Setting<bool> alternativeRendering{"alternative-rendering", false};
|
||||
static inline Setting<bool> fastScale{"fast-render-scale", false};
|
||||
static inline Setting<bool> alternativeRendering{"alternative-rendering", false};
|
||||
static inline Setting<int> videoQueue{"video-buffer-size", 32};
|
||||
static inline Setting<int> audioQueue{"audio-buffer-size", 16};
|
||||
static inline Setting<int> audioDelay{"audio-buffer-wait", 2};
|
||||
@@ -50,7 +51,7 @@ public:
|
||||
static inline Setting<float> audioFade{"audio-fade", 0.3};
|
||||
static inline Setting<int> audioBuffer{"audio-buffer-samples", 2048};
|
||||
static inline Setting<std::string> audioDriver{"audio-driver", ""};
|
||||
static inline Setting<std::string> renderDriver{"renderer-driver", "auto"};
|
||||
|
||||
static inline Setting<std::string> onConnect{"on-connect-script", ""};
|
||||
static inline Setting<std::string> onDisconnect{"on-disconnect-script", ""};
|
||||
|
||||
|
||||
@@ -82,6 +82,14 @@ public:
|
||||
return waitFlag.load();
|
||||
}
|
||||
|
||||
bool waitFor(atomic<bool> &waitFlag, uint32_t timeoutMs, uint8_t count = 0)
|
||||
{
|
||||
unique_lock<std::mutex> lock(_mtx);
|
||||
_lock.wait_for(lock, std::chrono::milliseconds(timeoutMs), [&]
|
||||
{ return _count > count || !waitFlag.load(); });
|
||||
return waitFlag.load();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
_data = std::make_unique<std::unique_ptr<T>[]>(_size);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
#ifndef SRC_STRUCT_AUDIO_CHUNK
|
||||
#define SRC_STRUCT_AUDIO_CHUNK
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
class AudioChunk
|
||||
{
|
||||
public:
|
||||
AudioChunk(uint16_t size)
|
||||
: data(nullptr), size(size)
|
||||
{
|
||||
if (size > 0)
|
||||
data = static_cast<uint8_t *>(malloc(size));
|
||||
}
|
||||
|
||||
~AudioChunk()
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
|
||||
// Deleted copy constructor/assignment
|
||||
AudioChunk(const AudioChunk &) = delete;
|
||||
AudioChunk &operator=(const AudioChunk &) = delete;
|
||||
|
||||
uint8_t *data;
|
||||
uint16_t size;
|
||||
};
|
||||
|
||||
#endif /* SRC_STRUCT_AUDIO_CHUNK */
|
||||
@@ -0,0 +1,189 @@
|
||||
#ifndef SRC_STRUCT_COMMAND
|
||||
#define SRC_STRUCT_COMMAND
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
|
||||
#include "helper/functions.h"
|
||||
#include "helper/protocol_const.h"
|
||||
#include "struct/audio_chunk.h"
|
||||
#include "struct/multitouch.h"
|
||||
|
||||
class Command
|
||||
{
|
||||
public:
|
||||
Command(const Command &) = delete;
|
||||
Command &operator=(const Command &) = delete;
|
||||
|
||||
Command(int cmd, bool encrypt = true, uint32_t size = 0)
|
||||
: command(cmd), encryption(encrypt), length(size), data(nullptr)
|
||||
{
|
||||
if (size > 0)
|
||||
data = static_cast<uint8_t *>(malloc(size));
|
||||
}
|
||||
|
||||
Command(int cmd, uint32_t value, bool encrypt = true)
|
||||
: Command(cmd, encrypt, 4)
|
||||
{
|
||||
write_uint32_le(data, value);
|
||||
}
|
||||
|
||||
Command(int cmd, bool encrypt, uint8_t *buffer, uint32_t size)
|
||||
: command(cmd), encryption(encrypt), length(size), data(buffer)
|
||||
{
|
||||
}
|
||||
|
||||
~Command()
|
||||
{
|
||||
if (data)
|
||||
{
|
||||
free(data);
|
||||
data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static std::unique_ptr<Command> Init(int width, int height, int fps)
|
||||
{
|
||||
std::unique_ptr<Command> result(new Command(CMD_OPEN, true, 28));
|
||||
write_uint32_le(result->data + 0, width);
|
||||
write_uint32_le(result->data + 4, height);
|
||||
write_uint32_le(result->data + 8, fps);
|
||||
write_uint32_le(result->data + 12, 5);
|
||||
write_uint32_le(result->data + 16, 49152);
|
||||
write_uint32_le(result->data + 20, 2);
|
||||
write_uint32_le(result->data + 24, 2);
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Command> File(const char *filename, const uint8_t *data, uint32_t length)
|
||||
{
|
||||
// filename is assumed null‑terminated, so strlen + 1 to include the '\0'
|
||||
uint32_t fn_len = strlen(filename) + 1;
|
||||
|
||||
// Total buffer size: 4 (fn_len) + fn_len + 4 (content_len) + content_len
|
||||
std::unique_ptr<Command> result(new Command(CMD_SEND_FILE, true, 4 + fn_len + 4 + length));
|
||||
uint8_t *buf = result->data;
|
||||
|
||||
// 1) filename length (LE)
|
||||
write_uint32_le(buf, fn_len);
|
||||
buf += 4;
|
||||
|
||||
// 2) filename bytes (including the '\0')
|
||||
std::memcpy(buf, filename, fn_len);
|
||||
buf += fn_len;
|
||||
|
||||
// 3) content length (LE)
|
||||
write_uint32_le(buf, length);
|
||||
buf += 4;
|
||||
|
||||
// 4) content bytes
|
||||
if (length > 0 && data)
|
||||
std::memcpy(buf, data, length);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Command> File(const char *filename, const char *value)
|
||||
{
|
||||
uint32_t len = std::strlen(value);
|
||||
if (len > 16)
|
||||
throw std::invalid_argument("String too long (max 16 bytes)");
|
||||
// note: we send only the ASCII bytes, no trailing '\0'
|
||||
return File(filename, reinterpret_cast<const uint8_t *>(value), len);
|
||||
}
|
||||
|
||||
// overload for a single 32‑bit integer
|
||||
static std::unique_ptr<Command> File(const char *filename, int value)
|
||||
{
|
||||
uint8_t buffer[4];
|
||||
write_uint32_le(buffer, value);
|
||||
return File(filename, buffer, 4);
|
||||
}
|
||||
|
||||
static std::unique_ptr<Command> Control(uint32_t value, bool encrypt = false)
|
||||
{
|
||||
return std::unique_ptr<Command>(new Command(CMD_CONTROL, value, encrypt));
|
||||
}
|
||||
|
||||
static std::unique_ptr<Command> Encryption(uint32_t seed)
|
||||
{
|
||||
return std::unique_ptr<Command>(new Command(CMD_ENCRYPTION, seed, false));
|
||||
}
|
||||
|
||||
static std::unique_ptr<Command> String(uint32_t cmd, const char *str, bool encrypt = true)
|
||||
{
|
||||
uint32_t length = std::strlen(str);
|
||||
std::unique_ptr<Command> result(new Command(cmd, encrypt, length));
|
||||
if (length > 0)
|
||||
std::memcpy(result->data, str, length);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static std::unique_ptr<Command> String(uint32_t cmd, const char *format, Args... args)
|
||||
{
|
||||
char buffer[512];
|
||||
std::snprintf(buffer, sizeof(buffer), format, args...);
|
||||
return String(cmd, buffer);
|
||||
}
|
||||
|
||||
static std::unique_ptr<Command> Touch(uint32_t action, float x, float y)
|
||||
{
|
||||
std::unique_ptr<Command> result(new Command(CMD_TOUCH, false, 16));
|
||||
write_uint32_le(result->data, action);
|
||||
write_uint32_le(result->data + 4, int(10000 * x));
|
||||
write_uint32_le(result->data + 8, int(10000 * y));
|
||||
write_uint32_le(result->data + 12, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Command> Click(float x, float y, bool down)
|
||||
{
|
||||
return Touch(down ? 14 : 16, x, y);
|
||||
}
|
||||
|
||||
static std::unique_ptr<Command> Move(float x, float y)
|
||||
{
|
||||
return Touch(15, x, y);
|
||||
}
|
||||
|
||||
static std::unique_ptr<Command> Audio(std::unique_ptr<AudioChunk> chunk)
|
||||
{
|
||||
std::unique_ptr<Command> result(new Command(CMD_AUDIO_DATA, false, chunk->data, chunk->size));
|
||||
chunk->data = nullptr;
|
||||
write_uint32_le(result->data, 5);
|
||||
write_uint32_le(result->data + 4, 0);
|
||||
write_uint32_le(result->data + 8, 3);
|
||||
return result;
|
||||
}
|
||||
|
||||
static std::unique_ptr<Command> MultiTouch(const Multitouch &touches)
|
||||
{
|
||||
int count = touches.size();
|
||||
if (count == 0)
|
||||
return nullptr;
|
||||
|
||||
std::unique_ptr<Command> result(new Command(CMD_MULTI_TOUCH, false, sizeof(Multitouch::Touch) * count));
|
||||
uint8_t *buf = result->data;
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
const Multitouch::Touch &t = touches[i];
|
||||
write_float_le(buf + 0, t.x);
|
||||
write_float_le(buf + 4, t.y);
|
||||
write_uint32_le(buf + 8, static_cast<uint32_t>(t.state));
|
||||
write_uint32_le(buf + 12, static_cast<uint32_t>(t.id));
|
||||
buf += 16;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int command;
|
||||
bool encryption;
|
||||
uint32_t length;
|
||||
uint8_t *data;
|
||||
};
|
||||
|
||||
#endif /* SRC_STRUCT_COMMAND */
|
||||
Reference in New Issue
Block a user