mirror of
https://github.com/niellun/FastCarPlay.git
synced 2026-06-07 09:38:25 +02:00
Minor fixes, Alternative rendering mode
This commit is contained in:
@@ -23,7 +23,7 @@ all: debug
|
||||
|
||||
LDOPTIONS := -lSDL2 -lSDL2_ttf -lavformat -lavcodec -lavutil -lswscale -lusb-1.0 -lssl -lcrypto
|
||||
LDFLAGS :=
|
||||
CXXCOMMON := -Wall -Isrc
|
||||
CXXCOMMON := -Wall -std=c++17 -Isrc
|
||||
|
||||
debug: BUILD_TYPE := debug
|
||||
debug: CXXFLAGS := -g -O0 -DPROTOCOL_DEBUG -fsanitize=address -fno-omit-frame-pointer
|
||||
@@ -32,8 +32,8 @@ debug: TARGET := $(TARGET_NAME)-debug
|
||||
debug: prepare
|
||||
|
||||
release: BUILD_TYPE := release
|
||||
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: CXXFLAGS := -O3 -ffast-math -march=native -fno-plt -fno-rtti -flto -fdata-sections -ffunction-sections -DNDEBUG
|
||||
release: LDFLAGS += -O3 -ffast-math -march=native -Wl, -flto
|
||||
release: TARGET := $(TARGET_NAME)
|
||||
release: prepare
|
||||
|
||||
@@ -58,4 +58,4 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp
|
||||
clean:
|
||||
@rm -rf $(OUT_DIR)
|
||||
@rm -rf $(GEN_DIR)
|
||||
@echo "Clean complete"
|
||||
@echo "Clean complete"
|
||||
|
||||
+10
-3
@@ -139,6 +139,13 @@
|
||||
# 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
|
||||
@@ -165,8 +172,8 @@
|
||||
#key-siri = 115 # S
|
||||
#key-nightmode-on = 122 # Z
|
||||
#key-nightmode-off = 120 # X
|
||||
#key-left = 1073741903 # Left arrow
|
||||
#key-right = 1073741904 # Right arrow
|
||||
#key-left = 1073741904 # Left arrow
|
||||
#key-right = 1073741903 # Right arrow
|
||||
#key-enter = 13 # Enter
|
||||
# For pipe to simulate key up after key-enter press
|
||||
#key-enterup = 0 # unmapped by default
|
||||
@@ -212,4 +219,4 @@
|
||||
|
||||
# Enable FFMPEG AV_CODEC_FLAG2_FAST for HW decoder.
|
||||
# Allow non spec compliant speedup tricks.
|
||||
#decode-fast = false
|
||||
#decode-fast = false
|
||||
|
||||
+41
-17
@@ -34,7 +34,8 @@ static KeySetting<int> *keyMap[] = {
|
||||
&Settings::keyReject,
|
||||
&Settings::keyVideoFocus,
|
||||
&Settings::keyVideoRelease,
|
||||
&Settings::keyNavFocus};
|
||||
&Settings::keyNavFocus,
|
||||
&Settings::keyNavRelease};
|
||||
|
||||
static constexpr size_t keyMapSize = sizeof(keyMap) / sizeof(keyMap[0]);
|
||||
|
||||
@@ -85,10 +86,7 @@ void Application::start(const char *title)
|
||||
std::cout << "[App] Initialising" << std::endl;
|
||||
|
||||
// Create SDL window centered on screen
|
||||
if (Settings::fastScale)
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "nearest");
|
||||
else
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "best");
|
||||
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, Settings::fastScale ? "nearest" : "best");
|
||||
|
||||
// Prepare window, show it in headless to avoid blinking, otherwise hidden untill iniailised
|
||||
bool fullsize = Settings::isFullscreen() || Settings::isHeadless();
|
||||
@@ -111,10 +109,22 @@ void Application::start(const char *title)
|
||||
Uint32 flags = SDL_RENDERER_ACCELERATED;
|
||||
if (Settings::vsync)
|
||||
flags |= SDL_RENDERER_PRESENTVSYNC;
|
||||
|
||||
SDL_SetHint(SDL_HINT_RENDER_DRIVER, Settings::renderDriver.value.c_str());
|
||||
_renderer = SDL_CreateRenderer(_window, -1, flags);
|
||||
|
||||
if (!_renderer)
|
||||
throw std::runtime_error(std::string("SDL can't create renderer > ") + SDL_GetError());
|
||||
|
||||
SDL_RendererInfo rendererInfo{};
|
||||
if (SDL_GetRendererInfo(_renderer, &rendererInfo) == 0)
|
||||
{
|
||||
std::cout << "[App] Renderer: " << rendererInfo.name
|
||||
<< " (" << ((rendererInfo.flags & SDL_RENDERER_ACCELERATED) ? "accelerated" : "software")
|
||||
<< ", " << ((rendererInfo.flags & SDL_RENDERER_PRESENTVSYNC) ? "vsync" : "no-vsync")
|
||||
<< ")" << std::endl;
|
||||
}
|
||||
|
||||
// Register additional events
|
||||
_evtBase = SDL_RegisterEvents(2);
|
||||
if (_evtBase == (Uint32)-1)
|
||||
@@ -204,7 +214,7 @@ bool Application::processSystemEvent(const SDL_Event &e)
|
||||
_state.connected = e.user.code != 0;
|
||||
_state.frameRendered = false;
|
||||
_state.dirty = true;
|
||||
_state.requestFrame = -1;
|
||||
_state.requestFrame = 0;
|
||||
_state.flushBuffers = _state.connected;
|
||||
return true;
|
||||
}
|
||||
@@ -324,7 +334,8 @@ void Application::loop()
|
||||
AVFrame *frame = nullptr;
|
||||
uint32_t frameid = 0;
|
||||
uint32_t latestFrameid = 0;
|
||||
uint32_t frameTargetTime = 1000 / Settings::fps;
|
||||
uint32_t requestFrameId = 0;
|
||||
uint32_t frameTargetTime = Settings::fps > 0 ? 1000 / Settings::fps : 1000;
|
||||
int frameDelay = 0;
|
||||
while (_active)
|
||||
{
|
||||
@@ -339,18 +350,20 @@ void Application::loop()
|
||||
std::cout << "[App] Frame drop " << frameid - latestFrameid - 1 << " on " << frameid << std::endl;
|
||||
latestFrameid = frameid;
|
||||
_state.dirty = false;
|
||||
if (_state.requestFrame >= 0)
|
||||
_state.requestFrame++;
|
||||
}
|
||||
videoBuffer.consume();
|
||||
}
|
||||
|
||||
if (_state.requestFrame > 0 && Settings::forceRedraw > 0)
|
||||
if (_state.requestFrame > 0 && Settings::forceRedraw > 0 && ++_state.requestFrame > Settings::forceRedraw)
|
||||
{
|
||||
if (_state.requestFrame <= Settings::forceRedraw)
|
||||
if (latestFrameid == requestFrameId)
|
||||
{
|
||||
protocol.requestKeyframe();
|
||||
_state.requestFrame = 1;
|
||||
requestFrameId = latestFrameid;
|
||||
}
|
||||
else
|
||||
_state.requestFrame = -1;
|
||||
_state.requestFrame = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,7 +378,10 @@ void Application::loop()
|
||||
else
|
||||
{
|
||||
if (processFrameEvents(protocol, interface) && Settings::forceRedraw > 0)
|
||||
{
|
||||
_state.requestFrame = 1;
|
||||
requestFrameId = latestFrameid;
|
||||
}
|
||||
}
|
||||
|
||||
if (_state.flushBuffers)
|
||||
@@ -375,15 +391,23 @@ void Application::loop()
|
||||
videoBuffer.reset();
|
||||
}
|
||||
|
||||
if (_active)
|
||||
if (_active && !Settings::vsync)
|
||||
{
|
||||
Uint32 frameEnd = SDL_GetTicks();
|
||||
frameDelay = frameTargetTime - (frameEnd - frameStart);
|
||||
SDL_Delay(frameDelay > 0 ? frameDelay : 1);
|
||||
frameStart += frameTargetTime;
|
||||
frameDelay = frameTargetTime - (frameEnd - frameStart);
|
||||
if(latestFrameid > 0 && latestFrameid != videoBuffer.latestId())
|
||||
{
|
||||
SDL_Delay(1);
|
||||
frameStart = frameEnd;
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_Delay(frameDelay > 0 ? frameDelay : 1);
|
||||
frameStart += frameTargetTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Settings::isHeadless())
|
||||
SDL_HideWindow(_window);
|
||||
}
|
||||
}
|
||||
|
||||
+21
-1
@@ -49,6 +49,13 @@ AVCodecContext *Decoder::load_codec(AVCodecID codec_id)
|
||||
AVCodecContext *result = nullptr;
|
||||
|
||||
// Try hardware-accelerated decoders by iterating registered codecs
|
||||
// NOTE: simply opening a codec with AV_CODEC_CAP_HARDWARE is not sufficient
|
||||
// on platforms such as V4L2M2M. A proper hwdevice context and get_format
|
||||
// callback must be setup so that AVFrames reference driver buffers instead
|
||||
// of being converted to system memory. This implementation currently
|
||||
// only picks a hardware-capable codec but still operates in software mode.
|
||||
// Fixing this will eliminate an extra copy and allow true GPU‑accelerated
|
||||
// decoding on the Pi.
|
||||
while ((codec = av_codec_iterate(&iter)) && Settings::hwDecode)
|
||||
{
|
||||
if (!av_codec_is_decoder(codec) || codec->id != codec_id)
|
||||
@@ -115,7 +122,7 @@ void Decoder::runner()
|
||||
|
||||
// Load codec context
|
||||
_context = load_codec(_codecId);
|
||||
if (_status.null(_context, ("Can't find decoder for codec " + _codecId)))
|
||||
if (_status.null(_context, std::string("Can't find decoder for codec ") + avcodec_get_name(_codecId)))
|
||||
return;
|
||||
std::string codec = _context->codec->name;
|
||||
|
||||
@@ -203,4 +210,17 @@ void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPack
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// push null packet to flush decoder and drain delayed frames
|
||||
if (_context)
|
||||
{
|
||||
avcodec_send_packet(context, nullptr);
|
||||
while (avcodec_receive_frame(context, frame) == 0)
|
||||
{
|
||||
AVFrame *out = _vb->write(counter++);
|
||||
av_frame_unref(out);
|
||||
av_frame_move_ref(out, frame);
|
||||
_vb->commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
// Base interface for any one setting
|
||||
class ISetting
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "helper/functions.h"
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t paddi
|
||||
|
||||
Protocol::~Protocol()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void Protocol::start(uint32_t evtStatus, uint32_t evtPhone)
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@
|
||||
|
||||
|
||||
Recorder::Recorder(uint16_t buffSize)
|
||||
: _sender(nullptr), _active(false), _device(0), _data(buffSize)
|
||||
: _sender(nullptr), _active(false), _data(buffSize)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,6 @@ private:
|
||||
IAudioSender *_sender;
|
||||
std::atomic<bool> _active;
|
||||
std::thread _thread;
|
||||
SDL_AudioDeviceID _device;
|
||||
AtomicQueue<AudioChunk> _data;
|
||||
};
|
||||
|
||||
|
||||
+54
-12
@@ -1,4 +1,5 @@
|
||||
#include "renderer.h"
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include "settings.h"
|
||||
#include "helper/functions.h"
|
||||
@@ -140,12 +141,6 @@ SDL_Rect RendererImage::draw(SDL_Renderer *renderer, int w, int h)
|
||||
return dst;
|
||||
}
|
||||
|
||||
const Renderer::FormatMapping Renderer::_mapping[] = {
|
||||
{AV_PIX_FMT_RGB24, SDL_PIXELFORMAT_RGB24, &Renderer::rgb, "RGB24"},
|
||||
{AV_PIX_FMT_YUV420P, SDL_PIXELFORMAT_IYUV, &Renderer::yuv, "YUV420P"},
|
||||
{AV_PIX_FMT_YUVJ420P, SDL_PIXELFORMAT_IYUV, &Renderer::yuv, "YUVJ420P"},
|
||||
{AV_PIX_FMT_NV12, SDL_PIXELFORMAT_NV12, &Renderer::nv, "NV12"}};
|
||||
|
||||
Renderer::Renderer(SDL_Renderer *renderer)
|
||||
: xScale(0),
|
||||
yScale(0),
|
||||
@@ -158,6 +153,12 @@ Renderer::Renderer(SDL_Renderer *renderer)
|
||||
_sws(nullptr),
|
||||
_frame(nullptr)
|
||||
{
|
||||
if (Settings::alternativeRendering)
|
||||
{
|
||||
_mapping[1].function = &Renderer::yuvAlternative;
|
||||
_mapping[2].function = &Renderer::yuvAlternative;
|
||||
_mapping[3].function = &Renderer::nvAlternative;
|
||||
}
|
||||
}
|
||||
|
||||
Renderer::~Renderer()
|
||||
@@ -298,6 +299,25 @@ void Renderer::nv(AVFrame *frame)
|
||||
frame->data[1], frame->linesize[1]);
|
||||
}
|
||||
|
||||
void Renderer::nvAlternative(AVFrame *frame)
|
||||
{
|
||||
uint8_t *pixels = nullptr;
|
||||
int pitch = 0;
|
||||
if (SDL_LockTexture(_texture, nullptr, (void **)&pixels, &pitch) != 0)
|
||||
return;
|
||||
|
||||
// Y plane
|
||||
for (int i = 0; i < frame->height; i++)
|
||||
memcpy(pixels + i * pitch, frame->data[0] + i * frame->linesize[0], frame->width);
|
||||
|
||||
// UV interleaved plane (half height, full width)
|
||||
uint8_t *uv = pixels + pitch * frame->height;
|
||||
for (int i = 0; i < frame->height / 2; i++)
|
||||
memcpy(uv + i * pitch, frame->data[1] + i * frame->linesize[1], frame->width);
|
||||
|
||||
SDL_UnlockTexture(_texture);
|
||||
}
|
||||
|
||||
void Renderer::yuv(AVFrame *frame)
|
||||
{
|
||||
SDL_UpdateYUVTexture(
|
||||
@@ -308,6 +328,30 @@ void Renderer::yuv(AVFrame *frame)
|
||||
frame->data[2], frame->linesize[2]);
|
||||
}
|
||||
|
||||
void Renderer::yuvAlternative(AVFrame *frame)
|
||||
{
|
||||
uint8_t *pixels = nullptr;
|
||||
int pitch = 0;
|
||||
if (SDL_LockTexture(_texture, nullptr, (void **)&pixels, &pitch) != 0)
|
||||
return;
|
||||
|
||||
// Y plane
|
||||
for (int i = 0; i < frame->height; i++)
|
||||
memcpy(pixels + i * pitch, frame->data[0] + i * frame->linesize[0], frame->width);
|
||||
|
||||
// U plane
|
||||
uint8_t *u = pixels + pitch * frame->height;
|
||||
for (int i = 0; i < frame->height / 2; i++)
|
||||
memcpy(u + i * (pitch / 2), frame->data[1] + i * frame->linesize[1], frame->width / 2);
|
||||
|
||||
// V plane
|
||||
uint8_t *v = u + (pitch / 2) * (frame->height / 2);
|
||||
for (int i = 0; i < frame->height / 2; i++)
|
||||
memcpy(v + i * (pitch / 2), frame->data[2] + i * frame->linesize[2], frame->width / 2);
|
||||
|
||||
SDL_UnlockTexture(_texture);
|
||||
}
|
||||
|
||||
void Renderer::scale(AVFrame *frame)
|
||||
{
|
||||
// Scale frame to output format
|
||||
@@ -317,10 +361,8 @@ void Renderer::scale(AVFrame *frame)
|
||||
_frame->data,
|
||||
_frame->linesize);
|
||||
|
||||
SDL_UpdateYUVTexture(
|
||||
_texture,
|
||||
nullptr,
|
||||
_frame->data[0], _frame->linesize[0],
|
||||
_frame->data[1], _frame->linesize[1],
|
||||
_frame->data[2], _frame->linesize[2]);
|
||||
if (Settings::alternativeRendering)
|
||||
yuvAlternative(_frame);
|
||||
else
|
||||
yuv(_frame);
|
||||
}
|
||||
|
||||
+7
-1
@@ -75,7 +75,9 @@ private:
|
||||
|
||||
void rgb(AVFrame *frame);
|
||||
void nv(AVFrame *frame);
|
||||
void nvAlternative(AVFrame *frame);
|
||||
void yuv(AVFrame *frame);
|
||||
void yuvAlternative(AVFrame *frame);
|
||||
void scale(AVFrame *frame);
|
||||
|
||||
SDL_Texture *_texture;
|
||||
@@ -85,7 +87,11 @@ private:
|
||||
DrawFuncType _render;
|
||||
SwsContext *_sws;
|
||||
AVFrame *_frame;
|
||||
static const FormatMapping _mapping[];
|
||||
FormatMapping _mapping[4] = {
|
||||
{AV_PIX_FMT_RGB24, SDL_PIXELFORMAT_RGB24, &Renderer::rgb, "RGB24"},
|
||||
{AV_PIX_FMT_YUV420P, SDL_PIXELFORMAT_IYUV, &Renderer::yuv, "YUV420P"},
|
||||
{AV_PIX_FMT_YUVJ420P, SDL_PIXELFORMAT_IYUV, &Renderer::yuv, "YUVJ420P"},
|
||||
{AV_PIX_FMT_NV12, SDL_PIXELFORMAT_NV12, &Renderer::nv, "NV12"}};
|
||||
};
|
||||
|
||||
#endif /* SRC_RENDERER */
|
||||
|
||||
+6
-4
@@ -42,6 +42,7 @@ public:
|
||||
static inline Setting<int> forceRedraw{"force-redraw", 0};
|
||||
static inline Setting<float> aspectCorrection{"aspect-correction", 1};
|
||||
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};
|
||||
@@ -49,6 +50,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", ""};
|
||||
|
||||
@@ -56,8 +58,8 @@ public:
|
||||
static inline KeySetting<int> keySiri{"key-siri", 115, 5};
|
||||
static inline KeySetting<int> keyNightOn{"key-nightmode-on", 122, 16};
|
||||
static inline KeySetting<int> keyNightOff{"key-nightmode-off", 120, 17};
|
||||
static inline KeySetting<int> keyLeft{"key-left", 1073741903, 100};
|
||||
static inline KeySetting<int> keyRight{"key-right", 1073741904, 101};
|
||||
static inline KeySetting<int> keyLeft{"key-left", 1073741904, 100};
|
||||
static inline KeySetting<int> keyRight{"key-right", 1073741903, 101};
|
||||
static inline KeySetting<int> keyEnter{"key-enter", 13, 104};
|
||||
static inline KeySetting<int> keyEnterUp{"key-enterup", 0, 105};
|
||||
static inline KeySetting<int> keyBack{"key-back", 8, 106};
|
||||
@@ -79,8 +81,8 @@ public:
|
||||
|
||||
// Debug section
|
||||
static inline Setting<int> protocolDebug{"protocol-debug", 0};
|
||||
static inline Setting<float> codecLowDelay{"decode-low-delay", true};
|
||||
static inline Setting<float> codecFast{"decode-fast", false};
|
||||
static inline Setting<bool> codecLowDelay{"decode-low-delay", true};
|
||||
static inline Setting<bool> codecFast{"decode-fast", false};
|
||||
|
||||
static bool load(const std::string &filename);
|
||||
static void print();
|
||||
|
||||
@@ -78,8 +78,8 @@ public:
|
||||
unique_lock<std::mutex> lock(_mtx);
|
||||
|
||||
_lock.wait(lock, [&]
|
||||
{ return _count > count || !waitFlag; });
|
||||
return waitFlag;
|
||||
{ return _count > count || !waitFlag.load(); });
|
||||
return waitFlag.load();
|
||||
}
|
||||
|
||||
void clear()
|
||||
|
||||
@@ -42,6 +42,14 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t latestId()
|
||||
{
|
||||
int index = _latest.load();
|
||||
if (index < 0)
|
||||
return 0;
|
||||
return _ids[index];
|
||||
}
|
||||
|
||||
bool latest(AVFrame **frame, uint32_t *id)
|
||||
{
|
||||
_reading.store(_latest.load());
|
||||
|
||||
Reference in New Issue
Block a user