Improvments to gstreamer backend and settings +++

- Fixed bug not setting environment for gstreamer before initialization
- Fixed windows directsound device
- Fixed crash on failure to create gstreamer element
- Fixed crash when switching backend
- Don't stop playback if equalizer or replay gain fails in gstreamer
- Improvments to backend settings
- Fixed backend settings to work for windows
- Fixed replay gain settings not working
- Fixed right click menu for album showing in statusview even when no song was playing
- Removed redundant code
This commit is contained in:
Jonas Kvinge
2018-04-05 21:40:05 +02:00
parent 6bbc1271e4
commit 43bf7e3ca8
21 changed files with 286 additions and 328 deletions

View File

@@ -97,12 +97,9 @@ QList<DeviceFinder::Device> AlsaDeviceFinder::ListDevices() {
snd_pcm_info_get_name(pcminfo);
Device device;
device.card = card;
device.device = dev;
device.description = QString("%1 %2").arg(snd_ctl_card_info_get_name(cardinfo)).arg(snd_pcm_info_get_name(pcminfo));
device.string = QString("hw:%1,%2").arg(card).arg(dev);
device.device_property_value = QString("hw:%1,%2").arg(card).arg(dev);
device.iconname = GuessIconName("", device.description);
device.value = QString("hw:%1,%2").arg(card).arg(dev);
device.iconname = GuessIconName(device.description);
ret.append(device);
}

View File

@@ -23,13 +23,10 @@
#include "core/logging.h"
DeviceFinder::DeviceFinder(const QString &output): output_(output) {
//qLog(Debug) << __PRETTY_FUNCTION__ << output;
DeviceFinder::DeviceFinder(const QString &name): name_(name) {
}
QString DeviceFinder::GuessIconName(const QString &name, const QString &description) {
//qLog(Debug) << __PRETTY_FUNCTION__ << name << description;
QString DeviceFinder::GuessIconName(const QString &description) {
QString description_lower = description.toLower();

View File

@@ -31,18 +31,15 @@ class DeviceFinder {
public:
struct Device {
int card;
int device;
QVariant device_property_value;
QString string;
QString description;
QVariant value;
QString iconname;
};
virtual ~DeviceFinder() {}
// The name of the gstreamer sink element that devices found by this class can be used with.
QString output() const { return output_; }
QString name() const { return name_; }
// Does any necessary setup, returning false if this DeviceFinder cannot be used.
virtual bool Initialise() = 0;
@@ -51,12 +48,12 @@ class DeviceFinder {
virtual QList<Device> ListDevices() = 0;
protected:
explicit DeviceFinder(const QString &output);
explicit DeviceFinder(const QString &name);
static QString GuessIconName(const QString &, const QString &);
static QString GuessIconName(const QString &description);
private:
QString output_;
QString name_;
};

View File

@@ -44,15 +44,13 @@ BOOL DirectSoundDeviceFinder::EnumerateCallback(LPGUID guid, LPCSTR description,
State *state = reinterpret_cast<State*>(state_voidptr);
if (guid) {
Device dev;
dev.description = QString::fromUtf8(description);
dev.device_property_value = QUuid(*guid).toByteArray();
dev.iconname = GuessIconName(dev.string, dev.description);
state->devices.append(dev);
}
Device dev;
dev.description = QString::fromUtf8(description);
if (guid) dev.value = QUuid(*guid).toByteArray();
else dev.value = QVariant();
dev.iconname = GuessIconName(dev.description);
state->devices.append(dev);
return 1;
}

View File

@@ -90,19 +90,11 @@ class Base : public QObject {
bool crossfade_same_album() const { return crossfade_same_album_; }
static const int kScopeSize = 1024;
struct PluginDetails {
QString name;
QString description;
QString iconname;
};
typedef QList<PluginDetails> PluginDetailsList;
struct OutputDetails {
QString name;
QString description;
QString iconname;
QVariant device_property_value;
};
typedef QList<OutputDetails> OutputDetailsList;
@@ -159,6 +151,13 @@ signals:
int next_background_stream_id_;
bool fadeout_pause_enabled_;
qint64 fadeout_pause_duration_nanosec_;
struct PluginDetails {
QString name;
QString description;
QString iconname;
};
typedef QList<PluginDetails> PluginDetailsList;
private:
bool about_to_end_emitted_;

View File

@@ -81,7 +81,7 @@ void EngineDevice::Init() {
for (DeviceFinder *finder : device_finders) {
if (!finder->Initialise()) {
qLog(Warning) << "Failed to initialise DeviceFinder for" << finder->output();
qLog(Warning) << "Failed to initialise DeviceFinder for" << finder->name();
delete finder;
continue;
}

View File

@@ -41,9 +41,6 @@ class EngineDevice : public QObject {
void Init();
QList<DeviceFinder*> device_finders_;
protected:
//static QString GuessIconName(const QString &, const QString &);
private:
QString output_;

View File

@@ -149,10 +149,8 @@ bool GstEngine::Init() {
void GstEngine::InitialiseGStreamer() {
#if 0
gst_init(nullptr, nullptr);
gst_pb_utils_init();
#endif
}
@@ -435,7 +433,7 @@ bool GstEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool forc
SetVolume(volume_);
SetEqualizerEnabled(equalizer_enabled_);
SetEqualizerParameters(equalizer_preamp_, equalizer_gains_);
if (equalizer_preamp_) SetEqualizerParameters(equalizer_preamp_, equalizer_gains_);
SetStereoBalance(stereo_balance_);
// Maybe fade in this track
@@ -757,7 +755,7 @@ GstElement *GstEngine::CreateElement(const QString &factoryName, GstElement *bin
// Make a unique name
QString name = factoryName + "-" + QString::number(next_element_id_++);
GstElement *element = gst_element_factory_make(factoryName.toLatin1().constData(), name.toLatin1().constData());
GstElement *element = gst_element_factory_make(factoryName.toUtf8().constData(), name.toUtf8().constData());
if (!element) {
emit Error(QString("GStreamer could not create the element: %1. Please make sure that you have installed all necessary GStreamer plugins").arg(factoryName));
@@ -889,18 +887,29 @@ EngineBase::OutputDetailsList GstEngine::GetOutputsList() const {
EngineBase::OutputDetailsList ret;
PluginDetailsList plugins = GetPluginList("Sink/Audio");
for (const PluginDetails &plugin : plugins) {
//if (plugins.count() > 0) {
for (const PluginDetails &plugin : plugins) {
OutputDetails output;
output.name = plugin.name;
output.description = plugin.description;
if (plugin.name == kAutoSink) output.iconname = "soundcard";
else if ((plugin.name == kALSASink) || (plugin.name == kOSS4Sink) || (plugin.name == kOSS4Sink)) output.iconname = "alsa";
else if (plugin.name== kJackAudioSink) output.iconname = "jack";
else if (plugin.name == kPulseSink) output.iconname = "pulseaudio";
else if ((plugin.name == kA2DPSink) || (plugin.name == kAVDTPSink)) output.iconname = "bluetooth";
else output.iconname = "soundcard";
ret.append(output);
}
#if 0
}
else {
OutputDetails output;
output.name = plugin.name;
output.description = plugin.description;
if (plugin.name == kAutoSink) output.iconname = "soundcard";
else if ((plugin.name == kALSASink) || (plugin.name == kOSS4Sink) || (plugin.name == kOSS4Sink)) output.iconname = "alsa";
else if (plugin.name== kJackAudioSink) output.iconname = "jack";
else if (plugin.name == kPulseSink) output.iconname = "pulseaudio";
else if ((plugin.name == kA2DPSink) || (plugin.name == kAVDTPSink)) output.iconname = "bluetooth";
else output.iconname = "soundcard";
output.name = kAutoSink;
output.description = "Auto";
output.iconname = "soundcard";
ret.append(output);
}
#endif
return ret;

View File

@@ -189,29 +189,20 @@ bool GstEnginePipeline::InitAudioBin() {
// Audio bin
audiobin_ = gst_bin_new("audiobin");
//audiobin_ = gst_bin_new("playbackbin");
//gst_bin_add(GST_BIN(pipeline_), audiobin_);
// Create the sink
if (!(audiosink_ = engine_->CreateElement(sink_, audiobin_))) return false;
//if (GstEngine::SinkDeviceSupport(sink_) && !device_.isEmpty())
//g_object_set(G_OBJECT(audiosink_), "device", device_.toUtf8().constData(), NULL);
if (g_object_class_find_property(G_OBJECT_GET_CLASS(audiosink_), "device") && !device_.toString().isEmpty()) {
audiosink_ = engine_->CreateElement(sink_, audiobin_);
if (!audiosink_) return false;
if (g_object_class_find_property(G_OBJECT_GET_CLASS(audiosink_), "device") && device_.isValid()) {
switch (device_.type()) {
case QVariant::Int:
g_object_set(G_OBJECT(audiosink_), "device", device_.toInt(), nullptr);
break;
case QVariant::String:
//qLog(Info) << "g_object_set: " << device_.toString().toUtf8().constData();
//g_object_set(G_OBJECT(audiosink_), "device", device_.toString().toUtf8().constData(), nullptr);
g_object_set(audiosink_, "device", device_.toString().toUtf8().constData(), nullptr);
//g_object_set(G_OBJECT(audiosink_), "device", "hw:0,0", nullptr);
if (device_.toString().isEmpty()) break;
g_object_set(G_OBJECT(audiosink_), "device", device_.toString().toUtf8().constData(), nullptr);
break;
#ifdef Q_OS_WIN32
case QVariant::ByteArray: {
GUID guid = QUuid(device_.toByteArray());
@@ -219,7 +210,6 @@ bool GstEnginePipeline::InitAudioBin() {
break;
}
#endif // Q_OS_WIN32
default:
qLog(Warning) << "Unknown device type" << device_;
break;
@@ -245,7 +235,7 @@ bool GstEnginePipeline::InitAudioBin() {
audioscale_ = engine_->CreateElement("audioresample", audiobin_);
convert = engine_->CreateElement("audioconvert", audiobin_);
if (!queue_ || !audioconvert_ || !tee || !probe_queue || !probe_converter || !probe_sink || !audio_queue || !equalizer_preamp_ || !equalizer_ || !stereo_panorama_ || !volume_ || !audioscale_ || !convert) {
if (!queue_ || !audioconvert_ || !tee || !probe_queue || !probe_converter || !probe_sink || !audio_queue || !volume_ || !audioscale_ || !convert) {
return false;
}
@@ -258,17 +248,14 @@ bool GstEnginePipeline::InitAudioBin() {
rgvolume_ = engine_->CreateElement("rgvolume", audiobin_);
rglimiter_ = engine_->CreateElement("rglimiter", audiobin_);
audioconvert2_ = engine_->CreateElement("audioconvert", audiobin_);
event_probe = audioconvert2_;
convert_sink = rgvolume_;
if (!rgvolume_ || !rglimiter_ || !audioconvert2_) {
return false;
if (rgvolume_ && rglimiter_ && audioconvert2_) {
event_probe = audioconvert2_;
convert_sink = rgvolume_;
// Set replaygain settings
g_object_set(G_OBJECT(rgvolume_), "album-mode", rg_mode_, nullptr);
g_object_set(G_OBJECT(rgvolume_), "pre-amp", double(rg_preamp_), nullptr);
g_object_set(G_OBJECT(rglimiter_), "enabled", int(rg_compression_), nullptr);
}
// Set replaygain settings
g_object_set(G_OBJECT(rgvolume_), "album-mode", rg_mode_, nullptr);
g_object_set(G_OBJECT(rgvolume_), "pre-amp", double(rg_preamp_), nullptr);
g_object_set(G_OBJECT(rglimiter_), "enabled", int(rg_compression_), nullptr);
}
// Create a pad on the outside of the audiobin and connect it to the pad of the first element.
@@ -292,33 +279,35 @@ bool GstEnginePipeline::InitAudioBin() {
// As a workaround, we create two dummy bands at both ends of the spectrum.
// This causes the actual first and last adjustable bands to be implemented using band-pass filters.
g_object_set(G_OBJECT(equalizer_), "num-bands", 10 + 2, nullptr);
if (equalizer_) {
g_object_set(G_OBJECT(equalizer_), "num-bands", 10 + 2, nullptr);
// Dummy first band (bandwidth 0, cutting below 20Hz):
GstObject *first_band = GST_OBJECT(gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(equalizer_), 0));
g_object_set(G_OBJECT(first_band), "freq", 20.0, "bandwidth", 0, "gain", 0.0f, nullptr);
g_object_unref(G_OBJECT(first_band));
// Dummy first band (bandwidth 0, cutting below 20Hz):
GstObject *first_band = GST_OBJECT(gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(equalizer_), 0));
g_object_set(G_OBJECT(first_band), "freq", 20.0, "bandwidth", 0, "gain", 0.0f, nullptr);
g_object_unref(G_OBJECT(first_band));
// Dummy last band (bandwidth 0, cutting over 20KHz):
GstObject *last_band = GST_OBJECT(gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(equalizer_), kEqBandCount + 1));
g_object_set(G_OBJECT(last_band), "freq", 20000.0, "bandwidth", 0, "gain", 0.0f, nullptr);
g_object_unref(G_OBJECT(last_band));
// Dummy last band (bandwidth 0, cutting over 20KHz):
GstObject *last_band = GST_OBJECT(gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(equalizer_), kEqBandCount + 1));
g_object_set(G_OBJECT(last_band), "freq", 20000.0, "bandwidth", 0, "gain", 0.0f, nullptr);
g_object_unref(G_OBJECT(last_band));
int last_band_frequency = 0;
for (int i = 0; i < kEqBandCount; ++i) {
const int index_in_eq = i + 1;
GstObject *band = GST_OBJECT(gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(equalizer_), index_in_eq));
int last_band_frequency = 0;
for (int i = 0; i < kEqBandCount; ++i) {
const int index_in_eq = i + 1;
GstObject *band = GST_OBJECT(gst_child_proxy_get_child_by_index(GST_CHILD_PROXY(equalizer_), index_in_eq));
const float frequency = kEqBandFrequencies[i];
const float bandwidth = frequency - last_band_frequency;
last_band_frequency = frequency;
const float frequency = kEqBandFrequencies[i];
const float bandwidth = frequency - last_band_frequency;
last_band_frequency = frequency;
g_object_set(G_OBJECT(band), "freq", frequency, "bandwidth", bandwidth, "gain", 0.0f, nullptr);
g_object_unref(G_OBJECT(band));
g_object_set(G_OBJECT(band), "freq", frequency, "bandwidth", bandwidth, "gain", 0.0f, nullptr);
g_object_unref(G_OBJECT(band));
}
}
// Set the stereo balance.
g_object_set(G_OBJECT(stereo_panorama_), "panorama", stereo_balance_, nullptr);
if (stereo_panorama_) g_object_set(G_OBJECT(stereo_panorama_), "panorama", stereo_balance_, nullptr);
// Set the buffer duration. We set this on this queue instead of the decode bin (in ReplaceDecodeBin()) because setting it on the decode bin only affects network sources.
// Disable the default buffer and byte limits, so we only buffer based on time.
@@ -340,7 +329,7 @@ bool GstEnginePipeline::InitAudioBin() {
gst_pad_link(gst_element_get_request_pad(tee, "src_%u"), gst_element_get_static_pad(audio_queue, "sink"));
// Link replaygain elements if enabled.
if (rg_enabled_) {
if (rg_enabled_ && rgvolume_ && rglimiter_ && audioconvert2_) {
gst_element_link_many(rgvolume_, rglimiter_, audioconvert2_, tee, nullptr);
}
@@ -352,7 +341,7 @@ bool GstEnginePipeline::InitAudioBin() {
// Don't force 16 bit.
gst_element_link(probe_queue, probe_converter);
if (engine_->IsEqualizerEnabled()) gst_element_link_many(audio_queue, equalizer_preamp_, equalizer_, stereo_panorama_, volume_, audioscale_, convert, nullptr);
if (engine_->IsEqualizerEnabled() && equalizer_ && equalizer_preamp_ && stereo_panorama_) gst_element_link_many(audio_queue, equalizer_preamp_, equalizer_, stereo_panorama_, volume_, audioscale_, convert, nullptr);
else gst_element_link_many(audio_queue, volume_, audioscale_, convert, nullptr);
// Let the audio output of the tee autonegotiate the bit depth and format.
@@ -383,6 +372,8 @@ bool GstEnginePipeline::InitFromUrl(const QByteArray &url, qint64 end_nanosec) {
end_offset_nanosec_ = end_nanosec;
pipeline_ = engine_->CreateElement("playbin");
if (pipeline_ == nullptr) return false;
g_object_set(G_OBJECT(pipeline_), "uri", url.constData(), nullptr);
CHECKED_GCONNECT(G_OBJECT(pipeline_), "about-to-finish", &AboutToFinishCallback, this);
@@ -939,6 +930,8 @@ void GstEnginePipeline::SetStereoBalance(float value) {
}
void GstEnginePipeline::UpdateEqualizer() {
if (!equalizer_ || !equalizer_preamp_) return;
// Update band gains
for (int i = 0; i < kEqBandCount; ++i) {
@@ -957,8 +950,7 @@ void GstEnginePipeline::UpdateEqualizer() {
// Update preamp
float preamp = 1.0;
if (eq_enabled_)
preamp = float(eq_preamp_ + 100) * 0.01; // To scale from 0.0 to 2.0
if (eq_enabled_) preamp = float(eq_preamp_ + 100) * 0.01; // To scale from 0.0 to 2.0
g_object_set(G_OBJECT(equalizer_preamp_), "volume", preamp, nullptr);

View File

@@ -101,8 +101,8 @@ QList<DeviceFinder::Device> OsxDeviceFinder::ListDevices() {
Device dev;
dev.description = QString::fromUtf8(CFStringGetCStringPtr(*device_name, CFStringGetSystemEncoding()));
dev.device_property_value = id;
dev.icon_name = GuessIconName(dev.description);
dev.value = id;
dev.iconname = GuessIconName(dev.description);
ret.append(dev);
}
return ret;

View File

@@ -116,9 +116,9 @@ void PulseDeviceFinder::GetSinkInfoCallback(pa_context *c, const pa_sink_info *i
if (info) {
Device dev;
dev.device_property_value = QString::fromUtf8(info->name);
dev.description = QString::fromUtf8(info->description);
dev.iconname = QString::fromUtf8(pa_proplist_gets(info->proplist, "device.icon_name"));
dev.value = QString::fromUtf8(info->name);
dev.iconname = QString::fromUtf8(pa_proplist_gets(info->proplist, "device.iconname"));
state->devices.append(dev);
}