mirror of
https://github.com/niellun/FastCarPlay.git
synced 2026-06-07 09:38:25 +02:00
Audio fade on notifications and optimisations
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
# Generated files
|
||||
out/
|
||||
conf/
|
||||
headers/
|
||||
src/autogen
|
||||
|
||||
|
||||
@@ -26,14 +26,14 @@ LDFLAGS :=
|
||||
CXXCOMMON := -Wall -Isrc
|
||||
|
||||
debug: BUILD_TYPE := debug
|
||||
debug: CXXFLAGS := -g -O0 -fsanitize=address -fno-omit-frame-pointer -DPROTOCOL_DEBUG
|
||||
debug: CXXFLAGS := -g -O0 -DPROTOCOL_DEBUG -fsanitize=address -fno-omit-frame-pointer
|
||||
debug: LDFLAGS += -fsanitize=address -fno-omit-frame-pointer
|
||||
debug: TARGET := $(TARGET_NAME)-debug
|
||||
debug: prepare
|
||||
|
||||
release: BUILD_TYPE := release
|
||||
release: CXXFLAGS := -O2 -march=native -flto -fno-plt -fdata-sections -ffunction-sections -DNDEBUG
|
||||
release: LDFLAGS += -Wl,--gc-sections -flto
|
||||
release: CXXFLAGS := -Ofast -march=native -fno-plt -fno-rtti -flto -fdata-sections -ffunction-sections -DNDEBUG
|
||||
release: LDFLAGS += -Ofast -march=native -Wl,--gc-sections -flto
|
||||
release: TARGET := $(TARGET_NAME)
|
||||
release: prepare
|
||||
|
||||
|
||||
@@ -1,32 +1,23 @@
|
||||
# FastCarPlay
|
||||
This is C++ implementation of carplay receiver for "Autobox" dongles.
|
||||
The purpose of the project was to make application lightweight to run on Raspberry PI Zero 2W and use hardware decoding.
|
||||
The purpose of the project is to make application lightweight to run on Raspberry PI Zero 2W using hardware decoding.
|
||||
|
||||
# Dongles
|
||||
## Dongles
|
||||
The dongles are readily available from Amazon or Aliexpress labeled by Carlinkit. They also seems to have official web site https://www.carlinkit.com/.
|
||||
Devices might have different vendor and product id's. Check your with lsusb and update settings if necessary.
|
||||
|
||||
# Setup
|
||||
## Dependencies
|
||||
## Setup
|
||||
### 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 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 libssl3
|
||||
sudo apt install ffmpeg libsdl2-2.0-0 libsdl2-ttf-2.0-0 libusb-1.0-0 libssl3
|
||||
```
|
||||
|
||||
## Build and run
|
||||
The application can be started with settings file. Sample of the settings file can be found in settings.txt
|
||||
The project is using make. From the repository root run following
|
||||
```
|
||||
make clean
|
||||
make release
|
||||
./out/app ./settings.txt
|
||||
```
|
||||
|
||||
## USB Permissions and device id
|
||||
### USB Permissions and device id
|
||||
On linux app may not have permisiions to read USB device. You need to create udev rule to grant persmissions for dongle.
|
||||
First you need to figure out your idVendor and idProduct.
|
||||
```
|
||||
@@ -41,13 +32,23 @@ Create udev rules, replace <__Vendor__> <__Product__> and <__Your user__> with y
|
||||
```
|
||||
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"
|
||||
## SUBSYSTEM=="usb", ATTRS{idVendor}=="1314", ATTRS{idProduct}=="1520", GROUP="linux", MODE="0660"
|
||||
sudo udevadm control --reload-rules
|
||||
sudo udevadm trigger
|
||||
```
|
||||
|
||||
### Build and run
|
||||
The application can be started with settings file. Sample of the settings file can be found in settings.txt. Make sure that you put proper device id in settings and app have access to usb (see previous step)
|
||||
The project is using make. You can edit ./conf/settings.txt after copy if needed. From the repository root run following
|
||||
```
|
||||
make clean
|
||||
make release
|
||||
mkdir ./conf
|
||||
cp ./settings.txt /conf
|
||||
./out/app ./conf/settings.txt
|
||||
```
|
||||
|
||||
## Customisation
|
||||
### Customisation
|
||||
You can change font and background images by replacing files in ./src/resource
|
||||
- background.bmp for background image. Use BMP format only.
|
||||
- font.ttf for font. Use TTF fdrmat only
|
||||
@@ -58,7 +59,7 @@ make clean
|
||||
make release
|
||||
```
|
||||
|
||||
## Keys
|
||||
### Keys
|
||||
The following keys have been mapped:
|
||||
- Left - navigate left
|
||||
- Right - navigate right
|
||||
@@ -66,7 +67,7 @@ The following keys have been mapped:
|
||||
- Backspace - Go back
|
||||
- f - toggle fullscreen mode
|
||||
|
||||
# Status
|
||||
## Status
|
||||
What is working:
|
||||
- Video
|
||||
- Audio (multiple channels)
|
||||
@@ -78,7 +79,7 @@ What is not working:
|
||||
- Microphone - that's next step for me to figure out how to feed sound
|
||||
- Telephone - the listening part will work, but because there is no mic implementation you can't speak
|
||||
|
||||
## Notes
|
||||
### Notes
|
||||
Regardless the resolution there are 2 types of settings
|
||||
- source-width source-height source-fps - defines what video parameters will be requested from device
|
||||
- width height fps - defines video drawing resolution and fps
|
||||
@@ -89,13 +90,22 @@ You can set the 'scaler' in setting to define scaling algorithm. On my Raspbery
|
||||
|
||||
Increasing FPS above Source-FPS will cause app to run UI loop with less delays and do more event polling. This can increase responsivenes of the system, but also will make X11 to use more resources.
|
||||
|
||||
## Next plans
|
||||
- Implement direct buffer transfer from video decoder to renderer (should reduce amount of memory copies and CPU load)
|
||||
- Control audio buffers better (now system use 3 decoding threads but in reality only 2 required)
|
||||
- Reduce music volume when there is navigation messages
|
||||
- Add abilities to run script on device connect and device disconnect
|
||||
### Progress and plans
|
||||
Done
|
||||
- ✓ Implement direct buffer transfer from video decoder to renderer (should reduce amount of memory copies and CPU load)
|
||||
- ✓ Control audio buffers better (now system use 3 decoding threads but in reality only 2 required)
|
||||
- ✓ Reduce music volume when there is navigation messages
|
||||
- ✓ Add abilities to run script on device connect and device disconnect
|
||||
- ✓ Add encrypted USB communication option with magic code 0x55bb55bb for new firmware
|
||||
- ✓ Improve touch responsiveness
|
||||
- ✓ Protocol debugging option
|
||||
|
||||
# Acknowledgement
|
||||
Next
|
||||
- Add microphone support (Calls, Siri)
|
||||
- Support Android Auto (message me if you have idea how it suppose to work with this dongle)
|
||||
|
||||
|
||||
## Acknowledgement
|
||||
The project is inspired and based on great work done by other developers:
|
||||
- 
|
||||
- 
|
||||
@@ -103,7 +113,7 @@ The project is inspired and based on great work done by other developers:
|
||||
- 
|
||||
|
||||
The project is licenced under GPL-3 licence. See LICENCE for details.
|
||||
The project is using  font. See FONT_LICENCE for details.
|
||||
The project is using Open Sans font (https://fonts.google.com/specimen/Open+Sans). See FONT_LICENCE for details.
|
||||
|
||||
# Finally
|
||||
## Finally
|
||||
If you have any questions, suggestions or you find problems running this feel free to open issue.
|
||||
|
||||
+15
-2
@@ -10,7 +10,10 @@
|
||||
# Application drawing settings widthxheight
|
||||
#width = 720
|
||||
#height = 576
|
||||
#fps = 60
|
||||
#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
|
||||
@@ -18,11 +21,13 @@
|
||||
# Requested image from phone
|
||||
#source-width = 720
|
||||
#source-height = 576
|
||||
#source-fps = 30
|
||||
#source-fps = 50
|
||||
|
||||
# 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
|
||||
# The image will still be scaled to window size using ginternal GL scaling.
|
||||
# See fast-render-scale for that
|
||||
# options
|
||||
# SWS_FAST_BILINEAR 1
|
||||
# SWS_BILINEAR 2
|
||||
@@ -37,6 +42,9 @@
|
||||
# SWS_SPLINE 1024
|
||||
#scaler = 2
|
||||
|
||||
# Select faster method of scaling image to window size (nearest) or better quality (linear)
|
||||
#fast-render-scale = false
|
||||
|
||||
# Enable logging
|
||||
#logging = false
|
||||
|
||||
@@ -48,6 +56,11 @@
|
||||
# Should be less than audio-buffer-size
|
||||
#audio-buffer-wait = 2
|
||||
|
||||
# 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
|
||||
# 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
|
||||
|
||||
|
||||
+18
-16
@@ -30,6 +30,12 @@ Connector::~Connector()
|
||||
{
|
||||
stop();
|
||||
|
||||
if (_cipher)
|
||||
{
|
||||
delete _cipher;
|
||||
_cipher = nullptr;
|
||||
}
|
||||
|
||||
if (_device)
|
||||
{
|
||||
libusb_release_interface(_device, 0);
|
||||
@@ -53,7 +59,6 @@ void Connector::start(IProtocol *protocol)
|
||||
|
||||
_active = true;
|
||||
_write_thread = std::thread(&Connector::write_loop, this);
|
||||
_read_thread = std::thread(&Connector::read_loop, this);
|
||||
}
|
||||
|
||||
void Connector::stop()
|
||||
@@ -234,7 +239,7 @@ void Connector::printBytes(uint32_t length, uint8_t *data, uint16_t max)
|
||||
std::cout << " > ";
|
||||
for (size_t i = 0; (i < length) & (i < max); ++i)
|
||||
{
|
||||
std::cout << data[i];
|
||||
std::cout << std::setw(4) << (uint32_t)(data[i]);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
@@ -299,16 +304,8 @@ void Connector::read_loop()
|
||||
|
||||
// Set thread name
|
||||
setThreadName("protocol-reader");
|
||||
while (_active)
|
||||
while (_active && _connected)
|
||||
{
|
||||
if (!_connected)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(mtx);
|
||||
cv.wait_for(lock, std::chrono::seconds(1), [&]()
|
||||
{ return !_active.load(); });
|
||||
continue;
|
||||
}
|
||||
|
||||
int result = libusb_bulk_transfer(_device, _endpoint_in, reinterpret_cast<uint8_t *>(&header), sizeof(Header), &transferred, READ_TIMEOUT);
|
||||
|
||||
if (result == LIBUSB_ERROR_NO_DEVICE)
|
||||
@@ -322,6 +319,7 @@ void Connector::read_loop()
|
||||
|
||||
if (result != LIBUSB_SUCCESS || transferred != sizeof(Header))
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -337,10 +335,6 @@ void Connector::read_loop()
|
||||
libusb_bulk_transfer(_device, _endpoint_in, data, header.length, &transferred, READ_TIMEOUT);
|
||||
}
|
||||
|
||||
#ifdef PROTOCOL_DEBUG
|
||||
printMessage(header.type, header.length, data, header.magic == MAGIC_ENC, false);
|
||||
#endif
|
||||
|
||||
if (!_protocol)
|
||||
{
|
||||
free(data);
|
||||
@@ -349,7 +343,6 @@ void Connector::read_loop()
|
||||
|
||||
if (header.magic == MAGIC_ENC)
|
||||
{
|
||||
|
||||
if (!_cipher)
|
||||
{
|
||||
std::cout << "[Connection] Received encrypted command " << header.type << " but cipher is not initialised" << std::endl;
|
||||
@@ -362,6 +355,13 @@ 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)
|
||||
std::fill(data + header.length, data + header.length + padding, 0);
|
||||
_protocol->onData(header.type, header.length, data);
|
||||
@@ -383,6 +383,8 @@ void Connector::write_loop()
|
||||
{
|
||||
status("Initialising dongle");
|
||||
std::cout << "[Connection] Device connected" << std::endl;
|
||||
|
||||
_read_thread = std::thread(&Connector::read_loop, this);
|
||||
if (_protocol)
|
||||
_protocol->onDevice(true);
|
||||
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@
|
||||
struct Header
|
||||
{
|
||||
uint32_t magic;
|
||||
uint32_t length;
|
||||
int32_t length;
|
||||
uint32_t type;
|
||||
uint32_t typecheck;
|
||||
};
|
||||
|
||||
+21
-15
@@ -179,16 +179,16 @@ void processEvents(Protocol &protocol, bool processMouse)
|
||||
}
|
||||
}
|
||||
|
||||
if(processMouse && (downX>=0 || upX>=0 || motionX>=0))
|
||||
if (processMouse && (downX >= 0 || upX >= 0 || motionX >= 0))
|
||||
{
|
||||
int window_width, window_height;
|
||||
SDL_GetWindowSize(window, &window_width, &window_height);
|
||||
if(downX>=0)
|
||||
if (downX >= 0)
|
||||
protocol.sendClick(1.0 * downX / window_width, 1.0 * downY / window_height, true);
|
||||
if(motionX>=0)
|
||||
if (motionX >= 0)
|
||||
protocol.sendMove(1.0 * motionX / window_width, 1.0 * motionY / window_height);
|
||||
if(upX>=0)
|
||||
protocol.sendClick(1.0 * upX / window_width, 1.0 * upY / window_height, false);
|
||||
if (upX >= 0)
|
||||
protocol.sendClick(1.0 * upX / window_width, 1.0 * upY / window_height, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,11 +208,10 @@ void application()
|
||||
VideoBuffer videoBuffer;
|
||||
Protocol protocol(Settings::sourceWidth, Settings::sourceHeight, Settings::sourceFps, AV_INPUT_BUFFER_PADDING_SIZE);
|
||||
Decoder decoder;
|
||||
PcmAudio audio0, audio1, audio2;
|
||||
PcmAudio audioMain("Main"), audioAux("Aux");
|
||||
decoder.start(&protocol.videoData, &videoBuffer, AV_CODEC_ID_H264);
|
||||
audio0.start(&protocol.audioStream0);
|
||||
audio1.start(&protocol.audioStream1);
|
||||
audio2.start(&protocol.audioStream2);
|
||||
audioMain.start(&protocol.audioStreamMain);
|
||||
audioAux.start(&protocol.audioStreamAux, &audioMain);
|
||||
protocol.start(onStatus);
|
||||
|
||||
UFont textFont(font, font_len, Settings::fontSize);
|
||||
@@ -236,9 +235,9 @@ void application()
|
||||
uint32_t frameDelay = inactiveDelay;
|
||||
active = true;
|
||||
uint32_t latestid = 0;
|
||||
Uint32 frameStart = SDL_GetTicks();
|
||||
while (active)
|
||||
{
|
||||
Uint32 frameStart = SDL_GetTicks();
|
||||
processEvents(protocol, videoPrepared);
|
||||
|
||||
if (connected != protocol.phoneConnected)
|
||||
@@ -266,8 +265,8 @@ void application()
|
||||
SDL_RenderCopy(renderer, videoRenderer.texture, nullptr, nullptr);
|
||||
SDL_RenderPresent(renderer);
|
||||
}
|
||||
if(frameid!=latestid+1)
|
||||
std::cout << "[Main] Fram drop from " << frameid - latestid - 1 << std::endl;
|
||||
if (frameid != latestid + 1)
|
||||
std::cout << "[Main] Frame drop " << frameid - latestid - 1 << " on " << frameid << std::endl;
|
||||
latestid = frameid;
|
||||
videoBuffer.consume();
|
||||
}
|
||||
@@ -291,11 +290,15 @@ void application()
|
||||
}
|
||||
}
|
||||
|
||||
Uint32 frameTime = SDL_GetTicks() - frameStart;
|
||||
Uint32 frameEnd = SDL_GetTicks();
|
||||
Uint32 frameTime = frameEnd - frameStart;
|
||||
if (frameTime < frameDelay)
|
||||
{
|
||||
SDL_Delay(frameDelay - frameTime); // Sleep only the remaining time
|
||||
frameStart = frameStart + frameDelay;
|
||||
}
|
||||
else
|
||||
frameStart = frameEnd;
|
||||
}
|
||||
std::cout << "[Main] Stopping" << std::endl;
|
||||
SDL_HideWindow(window);
|
||||
@@ -344,7 +347,10 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
// Create SDL window centered on screen
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||
if (Settings::fastScale)
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
|
||||
else
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||
window = SDL_CreateWindow(title,
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
SDL_WINDOWPOS_CENTERED,
|
||||
@@ -361,7 +367,7 @@ int main(int argc, char **argv)
|
||||
}
|
||||
|
||||
// Create accelerated renderer for the window
|
||||
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||
renderer = SDL_CreateRenderer(window, -1, (Settings::vsync ? (SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC) : SDL_RENDERER_ACCELERATED));
|
||||
if (renderer)
|
||||
{
|
||||
std::cout << "[Main] Started" << std::endl;
|
||||
|
||||
+68
-27
@@ -11,11 +11,52 @@ ChannelConfig PcmAudio::_configTable[] = {
|
||||
};
|
||||
|
||||
// Implementation
|
||||
PcmAudio::PcmAudio() {}
|
||||
PcmAudio::PcmAudio(const char *name)
|
||||
{
|
||||
if (!name || strlen(name) < 1)
|
||||
{
|
||||
_name = "[Audio]";
|
||||
return;
|
||||
}
|
||||
_name = "[Audio ";
|
||||
_name = _name + name + "]";
|
||||
_fade = false;
|
||||
_faded = false;
|
||||
_volume = 1.0;
|
||||
_fadeVolume = Settings::audioFade;
|
||||
if (_fadeVolume < 0)
|
||||
_fadeVolume = 0;
|
||||
if (_fadeVolume > 1)
|
||||
_fadeVolume = 1;
|
||||
}
|
||||
|
||||
PcmAudio::~PcmAudio()
|
||||
{
|
||||
stop();
|
||||
if (_thread.joinable())
|
||||
_thread.join();
|
||||
}
|
||||
|
||||
void PcmAudio::fadecpy(uint8_t *target, uint8_t *source, size_t len)
|
||||
{
|
||||
int16_t *src = reinterpret_cast<int16_t *>(source);
|
||||
int16_t *dst = reinterpret_cast<int16_t *>(target);
|
||||
_faded = true;
|
||||
for (size_t i = 0; i < len / 2; i++)
|
||||
{
|
||||
if (_fade)
|
||||
{
|
||||
if (_volume - FADE_OUT_SPEED >= _fadeVolume)
|
||||
_volume = _volume - FADE_OUT_SPEED;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_volume + FADE_IN_SPEED <= 1)
|
||||
_volume = _volume + FADE_IN_SPEED;
|
||||
}
|
||||
dst[i] = src[i] * _volume;
|
||||
}
|
||||
_faded = _volume + FADE_IN_SPEED <= 1;
|
||||
}
|
||||
|
||||
void PcmAudio::callback(void *userdata, Uint8 *stream, int len)
|
||||
@@ -32,26 +73,31 @@ void PcmAudio::callback(void *userdata, Uint8 *stream, int len)
|
||||
if (segment == nullptr || self->_reconfig)
|
||||
{
|
||||
std::fill_n(stream, len, 0);
|
||||
if (self->_nodata++ > NO_DATA_FRAMES)
|
||||
{
|
||||
self->_reconfig = true;
|
||||
self->_cv.notify_one();
|
||||
}
|
||||
self->_faded = self->_fade;
|
||||
self->_volume = self->_faded ? Settings::audioFade : 1;
|
||||
self->_reconfig = true;
|
||||
self->_cv.notify_one();
|
||||
return;
|
||||
}
|
||||
|
||||
self->_nodata = 0;
|
||||
int remain = segment->length() - self->_offset;
|
||||
uint8_t *data = segment->data() + self->_offset;
|
||||
|
||||
if (remain > len)
|
||||
{
|
||||
std::memcpy(stream, data, len);
|
||||
if (self->_fade || self->_faded)
|
||||
self->fadecpy(stream, data, len);
|
||||
else
|
||||
std::memcpy(stream, data, len);
|
||||
self->_offset = self->_offset + len;
|
||||
return;
|
||||
}
|
||||
|
||||
std::memcpy(stream, data, remain);
|
||||
if (self->_fade || self->_faded)
|
||||
self->fadecpy(stream, data, remain);
|
||||
else
|
||||
std::memcpy(stream, data, remain);
|
||||
|
||||
len = len - remain;
|
||||
stream = stream + remain;
|
||||
self->_data->pop();
|
||||
@@ -72,25 +118,16 @@ ChannelConfig PcmAudio::getConfig(int type)
|
||||
return {44100, 2};
|
||||
}
|
||||
|
||||
void PcmAudio::setVolume(float vol)
|
||||
void PcmAudio::Fade(bool enable)
|
||||
{
|
||||
if (vol < 0)
|
||||
{
|
||||
_volume = 0;
|
||||
return;
|
||||
}
|
||||
if (vol > 1)
|
||||
{
|
||||
_volume = 1;
|
||||
return;
|
||||
}
|
||||
_volume = vol;
|
||||
_fade = enable;
|
||||
}
|
||||
|
||||
void PcmAudio::start(AtomicQueue<Message> *data)
|
||||
void PcmAudio::start(AtomicQueue<Message> *data, PcmAudio *fader)
|
||||
{
|
||||
if (_active)
|
||||
stop();
|
||||
_fader = fader;
|
||||
_data = data;
|
||||
_active = true;
|
||||
_thread = std::thread(&PcmAudio::runner, this);
|
||||
@@ -114,13 +151,11 @@ void PcmAudio::runner()
|
||||
|
||||
while (_active)
|
||||
{
|
||||
printf("AUDIO - WAIT BUFFER\n");
|
||||
_data->wait(_active, Settings::audioDelay);
|
||||
const Message *segment = _data->peek();
|
||||
if (!segment)
|
||||
continue;
|
||||
|
||||
printf("AUDIO - START\n");
|
||||
_config = getConfig(segment->getInt(OFFSET_AUDIO_FORMAT));
|
||||
if (device != 0)
|
||||
{
|
||||
@@ -140,21 +175,27 @@ void PcmAudio::runner()
|
||||
|
||||
_reconfig = false;
|
||||
_offset = 0;
|
||||
_nodata = 0;
|
||||
|
||||
device = SDL_OpenAudioDevice(nullptr, 0, &spec, nullptr, 0);
|
||||
if (device == 0)
|
||||
{
|
||||
std::cerr << "[Audio] Failed to open audio: " << SDL_GetError() << std::endl;
|
||||
std::cerr << _name << " Failed to open audio: " << SDL_GetError() << std::endl;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
continue;
|
||||
}
|
||||
SDL_PauseAudioDevice(device, 0);
|
||||
std::cout << _name << " Start playing " << _config.rate << "kHz " << (_config.channels == 2 ? "stereo" : "mono") << std::endl;
|
||||
if (_fader)
|
||||
_fader->Fade(true);
|
||||
|
||||
printf("AUDIO - SLEED %b\n", _reconfig.load() || !_active.load());
|
||||
std::unique_lock<std::mutex> lock(_mtx);
|
||||
_cv.wait(lock, [&]
|
||||
{ return _reconfig.load() || !_active.load(); });
|
||||
|
||||
SDL_PauseAudioDevice(device, 1);
|
||||
std::cout << _name << " Stop playing" << std::endl;
|
||||
if (_fader)
|
||||
_fader->Fade(false);
|
||||
}
|
||||
|
||||
if (device)
|
||||
|
||||
+12
-7
@@ -11,7 +11,8 @@
|
||||
#include "struct/message.h"
|
||||
#include "helper/error.h"
|
||||
|
||||
#define NO_DATA_FRAMES 20
|
||||
#define FADE_IN_SPEED 0.00001
|
||||
#define FADE_OUT_SPEED 0.0001
|
||||
|
||||
struct ChannelConfig
|
||||
{
|
||||
@@ -32,35 +33,39 @@ struct ChannelConfig
|
||||
class PcmAudio
|
||||
{
|
||||
public:
|
||||
PcmAudio();
|
||||
PcmAudio(const char *name = "");
|
||||
~PcmAudio();
|
||||
|
||||
// Start playing raw PCM data from queue
|
||||
void start(AtomicQueue<Message> *data);
|
||||
void start(AtomicQueue<Message> *data, PcmAudio *fader = nullptr);
|
||||
void stop();
|
||||
|
||||
void setVolume(float vol);
|
||||
void Fade(bool enble);
|
||||
|
||||
private:
|
||||
void runner();
|
||||
void loop(SDL_AudioDeviceID device);
|
||||
void fadecpy(uint8_t *target, uint8_t *source, size_t len);
|
||||
|
||||
static void callback(void *userdata, Uint8 *stream, int len);
|
||||
static ChannelConfig _configTable[];
|
||||
static ChannelConfig getConfig(int type);
|
||||
|
||||
float _volume = 1.0f;
|
||||
|
||||
AtomicQueue<Message> *_data;
|
||||
ChannelConfig _config;
|
||||
int _offset;
|
||||
int _nodata;
|
||||
PcmAudio *_fader;
|
||||
bool _fade;
|
||||
bool _faded;
|
||||
float _volume;
|
||||
float _fadeVolume;
|
||||
|
||||
std::thread _thread;
|
||||
std::mutex _mtx;
|
||||
std::condition_variable _cv;
|
||||
std::atomic<bool> _reconfig{false};
|
||||
std::atomic<bool> _active{false};
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
#endif /* SRC_PCM_AUDIO */
|
||||
|
||||
+5
-14
@@ -9,9 +9,8 @@
|
||||
Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t padding)
|
||||
: connector(padding),
|
||||
videoData(Settings::videoQueue),
|
||||
audioStream0(Settings::audioQueue),
|
||||
audioStream1(Settings::audioQueue),
|
||||
audioStream2(Settings::audioQueue),
|
||||
audioStreamMain(Settings::audioQueue),
|
||||
audioStreamAux(Settings::audioQueue),
|
||||
phoneConnected(false),
|
||||
_width(width),
|
||||
_height(height),
|
||||
@@ -51,8 +50,6 @@ void Protocol::sendInit(int width, int height, int fps)
|
||||
|
||||
void Protocol::sendKey(int key)
|
||||
{
|
||||
printf("Send key %d", key);
|
||||
|
||||
uint8_t buf[4];
|
||||
write_uint32_le(&buf[0], key);
|
||||
|
||||
@@ -211,27 +208,21 @@ void Protocol::onData(uint32_t cmd, uint32_t length, uint8_t *data)
|
||||
}
|
||||
case CMD_AUDIO_DATA:
|
||||
{
|
||||
if (length <= 13)
|
||||
if (length <= 16)
|
||||
{
|
||||
break;
|
||||
}
|
||||
int channel = 0;
|
||||
memcpy(&channel, data + 8, sizeof(int));
|
||||
if (channel == 0)
|
||||
{
|
||||
audioStream0.pushDiscard(std::make_unique<Message>(data, length, 12));
|
||||
dispose = false;
|
||||
break;
|
||||
}
|
||||
if (channel == 1)
|
||||
{
|
||||
audioStream1.pushDiscard(std::make_unique<Message>(data, length, 12));
|
||||
audioStreamMain.pushDiscard(std::make_unique<Message>(data, length, 12));
|
||||
dispose = false;
|
||||
break;
|
||||
}
|
||||
if (channel == 2)
|
||||
{
|
||||
audioStream2.pushDiscard(std::make_unique<Message>(data, length, 12));
|
||||
audioStreamAux.pushDiscard(std::make_unique<Message>(data, length, 12));
|
||||
dispose = false;
|
||||
break;
|
||||
}
|
||||
|
||||
+2
-3
@@ -32,9 +32,8 @@ public:
|
||||
|
||||
Connector connector;
|
||||
AtomicQueue<Message> videoData;
|
||||
AtomicQueue<Message> audioStream0;
|
||||
AtomicQueue<Message> audioStream1;
|
||||
AtomicQueue<Message> audioStream2;
|
||||
AtomicQueue<Message> audioStreamMain;
|
||||
AtomicQueue<Message> audioStreamAux;
|
||||
bool phoneConnected;
|
||||
|
||||
private:
|
||||
|
||||
+8
-4
@@ -13,21 +13,25 @@ public:
|
||||
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};
|
||||
static inline Setting<int> fps{"fps", 50};
|
||||
static inline Setting<bool> vsync{"vsync", false};
|
||||
static inline Setting<int> sourceWidth{"source-width", 720};
|
||||
static inline Setting<int> sourceHeight{"source-height", 576};
|
||||
static inline Setting<int> sourceFps{"source-fps", 30};
|
||||
static inline Setting<int> sourceFps{"source-fps", 50};
|
||||
static inline Setting<bool> logging{"logging", false};
|
||||
static inline Setting<int> scaler{"scaler", 2};
|
||||
static inline Setting<bool> fastScale{"fast-render-scale", false};
|
||||
static inline Setting<int> videoQueue{"video-buffer-size", 32};
|
||||
static inline Setting<int> audioQueue{"audio-buffer-size", 16};
|
||||
static inline Setting<int> audioDelay{"audio-buffer-wait", 2};
|
||||
static inline Setting<int> audioDelay{"audio-buffer-wait", 2};
|
||||
static inline Setting<float> audioFade{"audio-fade", 0.3};
|
||||
static inline Setting<int> fontSize{"font-size", 30};
|
||||
static inline Setting<bool> encryption{"encryption", false};
|
||||
static inline Setting<bool> autoconnect{"autoconnect", true};
|
||||
static inline Setting<std::string> onConnect{"on-connect-script", ""};
|
||||
static inline Setting<std::string> onDisconnect{"on-disconnect-script", ""};
|
||||
static inline Setting<int> protocolDebug{"protocol-debug", 0};
|
||||
static inline Setting<int> protocolDebug{"protocol-debug", 0};
|
||||
|
||||
|
||||
static void load(const std::string &filename);
|
||||
static void print();
|
||||
|
||||
Reference in New Issue
Block a user