Fix Linux audio backend preference and worker footage render crash
- On Linux, automatically prefer PipeWire/JACK/PulseAudio over ALSA even when a saved ALSA device name exists in config. - Restore Footage length after deserialization so worker snapshots have valid stream lengths and video playback can advance. - Guard ResolveDecoderFromInput and ProcessAudioFootage against a null decoder cache to prevent worker crashes on direct Footage -> ViewerOutput connections. - Keep Linux signal backtrace handler in the render worker. - Add Oak project SVG logos.
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="66.455 51.542 122.65 122.65" width="122.65px" height="122.65px"><g id="object-5" transform="matrix(0.9999999999999999, 0, 0, 0.9999999999999999, 0, 2.1316282072803006e-14)"><ellipse style="stroke: rgb(0, 0, 0); stroke-opacity: 0; stroke-width: 1; fill: rgb(66, 85, 54);" cx="127.78" cy="112.867" rx="61.325" ry="61.325" id="object-0"/><path style="stroke: rgb(0, 0, 0); fill: rgb(255, 255, 255); stroke-opacity: 0;" d="M 108.938 74.01 C 108.938 74.01 113.768 68.927 118.024 82.364 C 121.327 92.79 115.453 105.155 115.682 113.196 C 115.91 121.237 118.274 123.233 120.894 118.269 C 123.514 113.3 124.882 118.57 124.173 121.487 C 122.633 129.57 124.625 140.553 129.134 142.503 C 133.29 144.299 131.944 133.101 135.159 132.349 C 138.375 131.597 140.886 147.886 148.395 150.681 C 155.904 153.473 151.817 140.929 156.62 142.54 C 161.424 144.151 164.989 149.094 164.414 142.723 C 164.141 139.7 174.057 143.197 174.711 139.18 C 175.368 135.164 170.235 133.443 167.764 132.764 C 165.294 132.08 159.277 131.997 162.836 128.218 C 166.396 124.438 170.625 127.358 169.484 124.518 C 168.345 121.682 162.321 120.361 155.396 121.455 C 148.469 122.55 147.929 116.789 155.752 114.889 C 163.58 112.991 163.743 108.958 159.69 106.402 C 151.469 101.212 144.084 104.757 144.084 104.757 C 137.838 107.27 133.22 104.676 133.22 104.676 C 130.051 101.066 137.477 99.577 137.477 99.577 C 144.088 96.205 126.636 93.773 126.103 95.792 C 126.698 98.659 133.676 123.858 162.109 138.304 C 137.825 128.994 124.241 105.31 123.109 94.564 C 121.974 83.819 122.782 83.712 120.621 77.554 C 118.463 71.401 112.057 65.149 108.576 67.735 C 105.093 70.32 107.833 73.159 108.938 74.01 Z" id="object-1"/><g transform="matrix(0.602933, 0.180211, -0.197794, 0.661764, -17.312515, -134.532112)" style="" id="object-2"><path style="fill: rgb(255, 255, 255); stroke-linejoin: round; stroke-linecap: round; stroke: rgb(0, 0, 0); stroke-opacity: 0;" d="M 270.267 278.934 C 266.312 278.408 266.571 273.461 267.985 269.688 C 269.398 265.915 272.668 262.24 278.489 261.413 C 284.311 260.586 293.348 260.551 298.552 261.732 C 303.756 262.913 306.571 267.285 307.36 269.857 C 308.765 274.433 308.05 278.22 304.634 278.474 C 307.168 274.498 267.373 273.799 270.267 278.934 Z M 292.463 263.467 C 292.138 263.811 291.897 264.226 291.758 264.682 C 291.088 266.896 293.041 268.971 295.273 268.417 C 297.506 267.861 298.345 265.095 296.781 263.434 L 292.463 263.467 Z M 278.933 263.135 C 278.606 263.479 278.365 263.894 278.227 264.35 C 277.557 266.564 279.509 268.639 281.743 268.085 C 283.976 267.529 284.814 264.763 283.25 263.102 L 278.933 263.135 Z M 273.394 269.573 C 273.068 269.917 272.827 270.333 272.689 270.788 C 272.017 273.002 273.971 275.077 276.204 274.523 C 278.437 273.968 279.275 271.201 277.712 269.54 L 273.394 269.573 Z M 285.356 268.443 C 285.03 268.787 284.788 269.202 284.651 269.658 C 283.979 271.872 285.933 273.947 288.165 273.393 C 290.399 272.837 291.236 270.071 289.674 268.41 L 285.356 268.443 Z M 297.696 269.416 C 297.37 269.76 297.129 270.175 296.991 270.631 C 296.32 272.845 298.273 274.92 300.505 274.366 C 302.739 273.81 303.577 271.044 302.014 269.383 L 297.696 269.416 Z"/><path style="stroke-linecap: round; fill: rgb(255, 255, 255); stroke: rgba(0, 0, 0, 0);" d="M 284.958 259.909 C 284.958 259.909 288.733 255.134 288.962 243.138 C 288.837 240.077 295.322 241.33 293.475 243.938 C 293.475 243.938 288.957 254.426 291.014 259.967 C 289.324 259.86 286.602 259.756 284.958 259.909 Z"/><path style="stroke-linecap: round; stroke-linejoin: round; fill: rgb(255, 255, 255); stroke: rgba(0, 0, 0, 0);" d="M 298.487 278.631 C 300.359 278.819 301.941 278.956 303.292 279.322 C 306.638 289.03 302.05 300.652 295.979 303.36 C 289.908 306.069 288.414 308.556 288.414 308.556 C 286.802 305.679 279.395 303.087 279.395 303.087 C 268.763 296.601 271.685 279.707 271.685 279.707 C 271.645 278.997 277.254 276.179 293.159 277.43 C 298.858 287.987 293.518 299.915 293.518 299.915 C 304.123 290.289 298.534 278.729 298.487 278.631 Z"/></g></g></svg>
|
||||||
|
After Width: | Height: | Size: 4.0 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 9.6 KiB |
@@ -281,12 +281,26 @@ void AudioManager::StopRecording()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
|
static bool IsPreferredLinuxAudioHostApi(const PaHostApiInfo *info)
|
||||||
|
{
|
||||||
|
if (!info) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString name = QString::fromLatin1(info->name);
|
||||||
|
return name.contains(QStringLiteral("PipeWire"), Qt::CaseInsensitive) ||
|
||||||
|
name.contains(QStringLiteral("JACK"), Qt::CaseInsensitive) ||
|
||||||
|
name.contains(QStringLiteral("PulseAudio"), Qt::CaseInsensitive);
|
||||||
|
}
|
||||||
|
|
||||||
static PaDeviceIndex GetPreferredLinuxAudioDevice(bool is_output_device)
|
static PaDeviceIndex GetPreferredLinuxAudioDevice(bool is_output_device)
|
||||||
{
|
{
|
||||||
// Prefer PipeWire, then PulseAudio. Both provide mixing; plain ALSA/JACK
|
// Prefer sound servers that provide mixing and desktop integration
|
||||||
// defaults often fail to share the device on modern Linux desktops.
|
// (PipeWire, JACK, PulseAudio) over plain ALSA defaults, which often
|
||||||
|
// fail to share the device on modern Linux desktops.
|
||||||
const QStringList preferred_host_apis = {
|
const QStringList preferred_host_apis = {
|
||||||
QStringLiteral("PipeWire"),
|
QStringLiteral("PipeWire"),
|
||||||
|
QStringLiteral("JACK"),
|
||||||
QStringLiteral("PulseAudio"),
|
QStringLiteral("PulseAudio"),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -325,21 +339,60 @@ PaDeviceIndex AudioManager::FindConfigDeviceByName(bool is_output_device)
|
|||||||
PaDeviceIndex AudioManager::FindDeviceByName(const QString &s,
|
PaDeviceIndex AudioManager::FindDeviceByName(const QString &s,
|
||||||
bool is_output_device)
|
bool is_output_device)
|
||||||
{
|
{
|
||||||
|
PaDeviceIndex exact_match = paNoDevice;
|
||||||
|
|
||||||
if (!s.isEmpty()) {
|
if (!s.isEmpty()) {
|
||||||
for (PaDeviceIndex i = 0, end = Pa_GetDeviceCount(); i < end; i++) {
|
for (PaDeviceIndex i = 0, end = Pa_GetDeviceCount(); i < end; i++) {
|
||||||
const PaDeviceInfo *device = Pa_GetDeviceInfo(i);
|
const PaDeviceInfo *device = Pa_GetDeviceInfo(i);
|
||||||
|
if (!device) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (((is_output_device && device->maxOutputChannels) ||
|
if (((is_output_device && device->maxOutputChannels) ||
|
||||||
(!is_output_device && device->maxInputChannels)) &&
|
(!is_output_device && device->maxInputChannels)) &&
|
||||||
!s.compare(device->name)) {
|
!s.compare(device->name)) {
|
||||||
return i;
|
exact_match = i;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef Q_OS_LINUX
|
#ifdef Q_OS_LINUX
|
||||||
|
// Even if the user/config picked a device by name, upgrade to a preferred
|
||||||
|
// host API (PipeWire/JACK/PulseAudio) when one is available. This avoids
|
||||||
|
// getting stuck on an ALSA device that cannot share the hardware.
|
||||||
|
if (exact_match != paNoDevice) {
|
||||||
|
const PaDeviceInfo *matched_info = Pa_GetDeviceInfo(exact_match);
|
||||||
|
if (matched_info) {
|
||||||
|
const PaHostApiInfo *host_api =
|
||||||
|
Pa_GetHostApiInfo(matched_info->hostApi);
|
||||||
|
if (IsPreferredLinuxAudioHostApi(host_api)) {
|
||||||
|
// Keep an explicit choice that already uses a preferred API.
|
||||||
|
return exact_match;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upgrade a non-preferred (e.g. ALSA) match to a preferred backend
|
||||||
|
// when one is available.
|
||||||
|
PaDeviceIndex preferred =
|
||||||
|
GetPreferredLinuxAudioDevice(is_output_device);
|
||||||
|
if (preferred != paNoDevice) {
|
||||||
|
qInfo() << "Overriding saved audio device" << s
|
||||||
|
<< "with preferred Linux audio device"
|
||||||
|
<< Pa_GetDeviceInfo(preferred)->name;
|
||||||
|
return preferred;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No preferred backend available; keep the saved device.
|
||||||
|
return exact_match;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return GetPreferredLinuxAudioDevice(is_output_device);
|
return GetPreferredLinuxAudioDevice(is_output_device);
|
||||||
#else
|
#else
|
||||||
|
if (exact_match != paNoDevice) {
|
||||||
|
return exact_match;
|
||||||
|
}
|
||||||
|
|
||||||
return is_output_device ? Pa_GetDefaultOutputDevice() :
|
return is_output_device ? Pa_GetDefaultOutputDevice() :
|
||||||
Pa_GetDefaultInputDevice();
|
Pa_GetDefaultInputDevice();
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -584,6 +584,11 @@ bool Footage::LoadCustom(QXmlStreamReader *reader, SerializedData *data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The cached lengths are not serialized. Recompute them from the stream
|
||||||
|
// parameters that were just loaded so that worker processes and any code
|
||||||
|
// that reads GetLength() before InvalidateCache() runs sees valid values.
|
||||||
|
VerifyLength();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -317,6 +317,12 @@ RenderProcessor::ResolveDecoderFromInput(const QString &decoder_id,
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!decoder_cache_) {
|
||||||
|
qWarning() << "Cannot resolve decoder for" << stream.filename()
|
||||||
|
<< "without a decoder cache";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
QMutexLocker locker(decoder_cache_->mutex());
|
QMutexLocker locker(decoder_cache_->mutex());
|
||||||
|
|
||||||
DecoderPair decoder = decoder_cache_->value(stream);
|
DecoderPair decoder = decoder_cache_->value(stream);
|
||||||
@@ -599,6 +605,12 @@ void RenderProcessor::ProcessAudioFootage(SampleBuffer &destination,
|
|||||||
const FootageJob *stream,
|
const FootageJob *stream,
|
||||||
const TimeRange &input_time)
|
const TimeRange &input_time)
|
||||||
{
|
{
|
||||||
|
// The worker process has no decoder cache and does not decode audio. Bail
|
||||||
|
// out gracefully rather than letting ResolveDecoderFromInput crash.
|
||||||
|
if (!decoder_cache_) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
DecoderPtr decoder = ResolveDecoderFromInput(
|
DecoderPtr decoder = ResolveDecoderFromInput(
|
||||||
stream->decoder(),
|
stream->decoder(),
|
||||||
Decoder::CodecStream(stream->filename(),
|
Decoder::CodecStream(stream->filename(),
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <csignal>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
|
||||||
@@ -32,6 +33,11 @@
|
|||||||
#include <QOpenGLContext>
|
#include <QOpenGLContext>
|
||||||
#include <QSurfaceFormat>
|
#include <QSurfaceFormat>
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
#include <execinfo.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "common/qtutils.h"
|
#include "common/qtutils.h"
|
||||||
#include "config/config.h"
|
#include "config/config.h"
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
@@ -59,6 +65,18 @@ void HideWorkerDockIcon();
|
|||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
void PrintBacktrace(int sig)
|
||||||
|
{
|
||||||
|
void *array[50];
|
||||||
|
size_t size = backtrace(array, 50);
|
||||||
|
fprintf(stderr, "worker: caught signal %d, backtrace:\n", sig);
|
||||||
|
backtrace_symbols_fd(array, size, STDERR_FILENO);
|
||||||
|
fflush(stderr);
|
||||||
|
_exit(128 + sig);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
constexpr int kProtocolVersion = 1;
|
constexpr int kProtocolVersion = 1;
|
||||||
constexpr int kDefaultWidth = 1920;
|
constexpr int kDefaultWidth = 1920;
|
||||||
constexpr int kDefaultHeight = 1080;
|
constexpr int kDefaultHeight = 1080;
|
||||||
@@ -606,6 +624,12 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef Q_OS_LINUX
|
||||||
|
std::signal(SIGSEGV, PrintBacktrace);
|
||||||
|
std::signal(SIGABRT, PrintBacktrace);
|
||||||
|
std::signal(SIGFPE, PrintBacktrace);
|
||||||
|
#endif
|
||||||
|
|
||||||
QFile in;
|
QFile in;
|
||||||
QFile out;
|
QFile out;
|
||||||
if (!in.open(stdin, QIODevice::ReadOnly | QIODevice::Unbuffered) ||
|
if (!in.open(stdin, QIODevice::ReadOnly | QIODevice::Unbuffered) ||
|
||||||
|
|||||||
Reference in New Issue
Block a user