More engine fixes

This commit is contained in:
Jonas Kvinge
2018-07-01 01:29:52 +02:00
parent 67df8f2243
commit 04f1d296ea
22 changed files with 330 additions and 217 deletions

View File

@@ -35,7 +35,7 @@
#include "core/logging.h"
DirectSoundDeviceFinder::DirectSoundDeviceFinder()
: DeviceFinder("directsound", { "directsound", "dsound", "directsoundsink" }) {
: DeviceFinder("directsound", { "directsound", "dsound", "directsoundsink", "directx", "directx2" }) {
}
QList<DeviceFinder::Device> DirectSoundDeviceFinder::ListDevices() {

View File

@@ -84,7 +84,6 @@ bool Engine::Base::Play(const QUrl &url, TrackChangeFlags flags, bool force_stop
void Engine::Base::SetVolume(uint value) {
volume_ = value;
SetVolumeSW(MakeVolumeLogarithmic(value));
}
@@ -132,3 +131,9 @@ void Engine::Base::EmitAboutToEnd() {
about_to_end_emitted_ = true;
emit TrackAboutToEnd();
}
bool Engine::Base::ValidOutput(const QString &output) {
return (true);
}

View File

@@ -89,8 +89,9 @@ public:
}
virtual OutputDetailsList GetOutputsList() const = 0;
virtual bool ValidOutput(const QString &output) = 0;
virtual QString DefaultOutput() = 0;
virtual bool CustomDeviceSupport(const QString &name) = 0;
virtual bool CustomDeviceSupport(const QString &output) = 0;
// Plays a media stream represented with the URL 'u' from the given 'beginning' to the given 'end' (usually from 0 to a song's length).
// Both markers should be passed in nanoseconds. 'end' can be negative, indicating that the real length of 'u' stream is unknown.

View File

@@ -99,6 +99,7 @@ GstEngine::GstEngine(TaskManager *task_manager)
scope_chunk_(0),
have_new_buffer_(false) {
type_ = Engine::GStreamer;
seek_timer_->setSingleShot(true);
seek_timer_->setInterval(kSeekDelayNanosec / kNsecPerMsec);
connect(seek_timer_, SIGNAL(timeout()), SLOT(SeekNow()));
@@ -129,7 +130,6 @@ bool GstEngine::Init() {
SetEnvironment();
type_ = Engine::GStreamer;
initialising_ = QtConcurrent::run(this, &GstEngine::InitialiseGStreamer);
return true;
@@ -388,8 +388,19 @@ EngineBase::OutputDetailsList GstEngine::GetOutputsList() const {
}
bool GstEngine::CustomDeviceSupport(const QString &name) {
return (name == kALSASink || name == kOpenALSASink || name == kOSSSink || name == kOSS4Sink || name == kPulseSink || name == kA2DPSink || name == kAVDTPSink);
bool GstEngine::ValidOutput(const QString &output) {
PluginDetailsList plugins = GetPluginList("Sink/Audio");
for (const PluginDetails &plugin : plugins) {
if (plugin.name == output) return(true);
}
return(false);
}
bool GstEngine::CustomDeviceSupport(const QString &output) {
return (output == kALSASink || output == kOpenALSASink || output == kOSSSink || output == kOSS4Sink || output == kPulseSink || output == kA2DPSink || output == kAVDTPSink);
}
void GstEngine::ReloadSettings() {

View File

@@ -84,8 +84,9 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
const Engine::Scope &scope(int chunk_length);
OutputDetailsList GetOutputsList() const;
bool ValidOutput(const QString &output);
QString DefaultOutput() { return kAutoSink; }
bool CustomDeviceSupport(const QString &name);
bool CustomDeviceSupport(const QString &output);
void EnsureInitialised() { initialising_.waitForFinished(); }
void InitialiseGStreamer();
@@ -132,7 +133,6 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
void BufferingFinished();
private:
static const char *kAutoSink;
static const char *kALSASink;
static const char *kOpenALSASink;

View File

@@ -32,11 +32,13 @@
#include "core/logging.h"
PhononEngine::PhononEngine(TaskManager *task_manager)
: media_object_(new Phonon::MediaObject(this)),
: EngineBase(),
media_object_(new Phonon::MediaObject(this)),
audio_output_(new Phonon::AudioOutput(Phonon::MusicCategory, this)),
state_timer_(new QTimer(this)),
seek_offset_(-1)
{
seek_offset_(-1) {
type_ = Engine::Phonon;
Phonon::createPath(media_object_, audio_output_);
@@ -54,8 +56,6 @@ PhononEngine::~PhononEngine() {
}
bool PhononEngine::Init() {
//qLog(Debug) << __PRETTY_FUNCTION__;
type_ = Engine::Phonon;
return true;
}
@@ -179,6 +179,13 @@ EngineBase::OutputDetailsList PhononEngine::GetOutputsList() const {
return ret;
}
bool PhononEngine::CustomDeviceSupport(const QString &name) {
bool PhononEngine::ValidOutput(const QString &output) {
return (output == "auto" || output == "" || output == DefaultOutput);
return(false);
}
bool PhononEngine::CustomDeviceSupport(const QString &output) {
return false;
}

View File

@@ -64,7 +64,8 @@ class PhononEngine : public Engine::Base {
qint64 length_nanosec() const;
QString DefaultOutput() { return ""; }
bool CustomDeviceSupport(const QString &name);
bool ValidOutput(const QString &output);
bool CustomDeviceSupport(const QString &output);
protected:
void SetVolumeSW( uint percent );

View File

@@ -40,13 +40,14 @@
VLCEngine *VLCEngine::sInstance = nullptr;
VLCEngine::VLCEngine(TaskManager *task_manager)
: instance_(nullptr),
: EngineBase(),
instance_(nullptr),
player_(nullptr),
state_(Engine::Empty),
scope_data_(4096)
{
scope_data_(4096) {
Init();
type_ = Engine::VLC;
ReloadSettings();
}
@@ -61,8 +62,6 @@ VLCEngine::~VLCEngine() {
bool VLCEngine::Init() {
type_ = Engine::VLC;
/* FIXME: Do we need this?
static const char *const args[] = {
"-I", "dummy", // Don't use any interface
@@ -113,8 +112,15 @@ bool VLCEngine::Init() {
}
bool VLCEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
bool VLCEngine::Initialised() const {
if (instance_ && player_) return true;
return false;
}
bool VLCEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
if (!Initialised()) return false;
// Create the media object
VlcScopedRef<libvlc_media_t> media(libvlc_media_new_location(instance_, url.toEncoded().constData()));
@@ -125,7 +131,7 @@ bool VLCEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool forc
}
bool VLCEngine::Play(quint64 offset_nanosec) {
if (!Initialised()) return false;
// Set audio output
if (!output_.isEmpty() || output_ != "auto") {
int result = libvlc_audio_output_set(player_, output_.toUtf8().constData());
@@ -148,27 +154,29 @@ bool VLCEngine::Play(quint64 offset_nanosec) {
}
void VLCEngine::Stop(bool stop_after) {
if (!Initialised()) return;
libvlc_media_player_stop(player_);
HandleErrors();
}
void VLCEngine::Pause() {
if (!Initialised()) return;
libvlc_media_player_pause(player_);
HandleErrors();
}
void VLCEngine::Unpause() {
if (!Initialised()) return;
libvlc_media_player_play(player_);
HandleErrors();
}
void VLCEngine::Seek(quint64 offset_nanosec) {
if (!Initialised()) return;
int offset = (offset_nanosec / kNsecPerMsec);
@@ -183,7 +191,7 @@ void VLCEngine::Seek(quint64 offset_nanosec) {
}
void VLCEngine::SetVolumeSW(uint percent) {
if (!Initialised()) return;
libvlc_audio_set_volume(player_, percent);
HandleErrors();
}
@@ -248,12 +256,24 @@ EngineBase::OutputDetailsList VLCEngine::GetOutputsList() const {
}
bool VLCEngine::CustomDeviceSupport(const QString &name) {
return (name == "auto" ? false : true);
bool VLCEngine::ValidOutput(const QString &output) {
PluginDetailsList plugins = GetPluginList();
for (const PluginDetails &plugin : plugins) {
if (plugin.name == output) return(true);
}
return(false);
}
bool VLCEngine::CustomDeviceSupport(const QString &output) {
return (output == "auto" ? false : true);
}
uint VLCEngine::position() const {
if (!Initialised()) return (0);
bool is_playing = libvlc_media_player_is_playing(player_);
HandleErrors();
@@ -268,6 +288,8 @@ uint VLCEngine::position() const {
uint VLCEngine::length() const {
if (!Initialised()) return(0);
bool is_playing = libvlc_media_player_is_playing(player_);
HandleErrors();

View File

@@ -63,8 +63,9 @@ class VLCEngine : public Engine::Base {
const Engine::Scope& Scope();
OutputDetailsList GetOutputsList() const;
bool ValidOutput(const QString &output);
QString DefaultOutput() { return ""; }
bool CustomDeviceSupport(const QString &name);
bool CustomDeviceSupport(const QString &value);
private:
libvlc_instance_t *instance_;
@@ -75,6 +76,7 @@ class VLCEngine : public Engine::Base {
static VLCEngine *sInstance;
QMutex scope_mutex_;
bool Initialised() const;
uint position() const;
uint length() const;
bool CanDecode(const QUrl &url);

View File

@@ -65,12 +65,8 @@ using std::shared_ptr;
#define LLONG_MAX 9223372036854775807LL
#endif
//define this to use xine in a more standard way
//#ifdef Q_OS_WIN32
// Define this to use xine in a more standard way
//#define XINE_SAFE_MODE
//#endif
const char *XineEngine::kAutoOutput = "auto";
int XineEngine::last_error_ = XINE_MSG_NO_ERROR;
@@ -88,56 +84,28 @@ XineEngine::XineEngine(TaskManager *task_manager)
fadeout_running_ (false),
prune_(nullptr) {
type_ = Engine::Xine;
ReloadSettings();
}
XineEngine::~XineEngine() {
// Wait until the fader thread is done
if (s_fader_) {
stop_fader_ = true;
s_fader_->resume(); // safety call if the engine is in the pause state
s_fader_->wait();
}
// Wait until the prune scope thread is done
if (prune_) {
prune_->exit();
prune_->wait();
}
s_fader_.reset();
s_outfader_.reset();
prune_.reset();
if (fadeout_enabled_) {
bool terminateFader = false;
FadeOut(fadeout_duration_, &terminateFader, true); // true == exiting
}
if (stream_) xine_close(stream_);
if (eventqueue_) xine_event_dispose_queue(eventqueue_);
if (stream_) xine_dispose(stream_);
if (audioport_) xine_close_audio_driver(xine_, audioport_);
if (post_) xine_post_dispose(xine_, post_);
if (xine_) xine_exit(xine_);
//qLog(Debug) << "xine closed";
//qLog(Debug) << "Scope statistics:";
//qLog(Debug) << "Average list size: " << log_buffer_count_ / log_scope_call_count_;
//qLog(Debug) << "Buffer failure: " << double(log_no_suitable_buffer_*100) / log_scope_call_count_ << "%";
Cleanup();
}
bool XineEngine::Init() {
type_ = Engine::Xine;
Cleanup();
SetEnvironment();
QMutexLocker l(&init_mutex_);
xine_ = xine_new();
if (!xine_) {
emit Error("Could not initialize xine.");
@@ -147,17 +115,76 @@ bool XineEngine::Init() {
#ifdef XINE_SAFE_MODE
xine_engine_set_param(xine_, XINE_ENGINE_PARAM_VERBOSITY, 99);
#endif
xine_init(xine_);
MakeNewStream();
xine_init(xine_);
#ifndef XINE_SAFE_MODE
prune_.reset(new PruneScopeThread(this));
prune_->start();
#endif
SetDevice();
if (!ValidOutput(output_)) {
qLog(Error) << "Invalid output detected:" << output_ << " - Resetting to default.";
output_ = DefaultOutput();
}
audioport_ = xine_open_audio_driver(xine_, (output_.isEmpty() || output_ == kAutoOutput ? nullptr : output_.toUtf8().constData()), nullptr);
if (!audioport_) {
emit Error("Xine was unable to initialize any audio drivers.");
return false;
}
return true;
}
void XineEngine::Cleanup() {
// Wait until the prune scope thread is done
if (prune_) {
prune_->exit();
prune_->wait();
}
prune_.reset();
// Wait until the fader thread is done
if (s_fader_) {
stop_fader_ = true;
s_fader_->resume(); // safety call if the engine is in the pause state
s_fader_->wait();
}
s_fader_.reset();
s_outfader_.reset();
if (stream_)
xine_close(stream_);
if (eventqueue_) {
xine_event_dispose_queue(eventqueue_);
eventqueue_ = nullptr;
}
if (stream_) {
xine_dispose(stream_);
stream_ = nullptr;
}
if (audioport_) {
xine_close_audio_driver(xine_, audioport_);
audioport_ = nullptr;
}
if (post_) {
xine_post_dispose(xine_, post_);
post_ = nullptr;
}
if (xine_) xine_exit(xine_);
xine_ = nullptr;
//qLog(Debug) << "xine closed";
//qLog(Debug) << "Scope statistics:";
//qLog(Debug) << "Average list size: " << log_buffer_count_ / log_scope_call_count_;
//qLog(Debug) << "Buffer failure: " << double(log_no_suitable_buffer_*100) / log_scope_call_count_ << "%";
}
Engine::State XineEngine::state() const {
@@ -183,7 +210,7 @@ bool XineEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool for
if (s_outfader_) {
s_outfader_->finish();
if (s_outfader_) s_outfader_.reset();
s_outfader_.reset();
}
if (fade_length_ > 0 && xine_get_status(stream_) == XINE_STATUS_PLAY && url.scheme().toLower() == "file" && xine_get_param(stream_, XINE_PARAM_SPEED) != XINE_SPEED_PAUSE && (fade_next_track_ || crossfade_enabled_)) {
@@ -370,14 +397,22 @@ EngineBase::OutputDetailsList XineEngine::GetOutputsList() const {
return ret;
}
bool XineEngine::CustomDeviceSupport(const QString &name) {
return (name == "alsa" || name == "oss" || name == "jack" || name == "pulseaudio");
bool XineEngine::ValidOutput(const QString &output) {
PluginDetailsList plugins = GetPluginList();
for (const PluginDetails &plugin : plugins) {
if (plugin.name == output) return(true);
}
return(false);
}
bool XineEngine::CustomDeviceSupport(const QString &output) {
return (output == "alsa" || output == "oss" || output == "jack" || output == "pulseaudio");
}
void XineEngine::ReloadSettings() {
QSettings s;
Engine::Base::ReloadSettings();
if (output_ == "") output_ = DefaultOutput();
@@ -386,13 +421,13 @@ void XineEngine::ReloadSettings() {
void XineEngine::SetEnvironment() {
#ifdef Q_OS_WIN32
putenv(QString("XINE_PLUGIN_PATH=" + QCoreApplication::applicationDirPath() + "/xine/plugins").toLatin1().constData());
#endif // Q_OS_WIN32
#ifdef Q_OS_WIN
putenv(QString("XINE_PLUGIN_PATH=" + QCoreApplication::applicationDirPath() + "/xine-plugins").toLatin1().constData());
#endif
#ifdef Q_OS_DARWIN
setenv("XINE_PLUGIN_PATH", QString(QCoreApplication::applicationDirPath() + "/../PlugIns/xine").toLatin1().constData(), 1);
#endif // Q_OS_DARWIN
#endif
}
@@ -868,7 +903,7 @@ Engine::SimpleMetaBundle XineEngine::fetchMetaData() const {
}
bool XineEngine::MakeNewStream() {
void XineEngine::SetDevice() {
if (device_.isValid()) {
bool valid(false);
@@ -892,12 +927,11 @@ bool XineEngine::MakeNewStream() {
xine_config_update_entry(xine_, &entry);
}
}
current_device_ = device_;
audioport_ = xine_open_audio_driver(xine_, (output_.isEmpty() || output_ == kAutoOutput ? nullptr : output_.toUtf8().constData()), nullptr);
if (!audioport_) {
emit Error("Xine was unable to initialize any audio drivers.");
return false;
}
}
bool XineEngine::CreateStream() {
stream_ = xine_stream_new(xine_, audioport_, nullptr);
if (!stream_) {
@@ -908,7 +942,6 @@ bool XineEngine::MakeNewStream() {
}
if (eventqueue_) xine_event_dispose_queue(eventqueue_);
eventqueue_ = xine_event_new_queue(stream_);
xine_event_create_listener_thread(eventqueue_, &XineEngine::XineEventListener, (void*)this);
@@ -923,7 +956,7 @@ bool XineEngine::MakeNewStream() {
if (xine_check_version(1, 1, 1) && !(fade_length_ > 0)) {
// Enable gapless playback
qLog(Debug) << "gapless playback enabled.";
// xine_set_param(stream_, XINE_PARAM_EARLY_FINISHED_EVENT, 1);
xine_set_param(stream_, XINE_PARAM_EARLY_FINISHED_EVENT, 1);
}
#endif
return true;
@@ -931,7 +964,7 @@ bool XineEngine::MakeNewStream() {
bool XineEngine::EnsureStream() {
if (!stream_) return MakeNewStream();
if (!stream_) return CreateStream();
return true;
}

View File

@@ -65,9 +65,8 @@ private:
class XineEngine : public Engine::Base {
Q_OBJECT
public:
public:
XineEngine(TaskManager *task_manager);
~XineEngine();
@@ -86,38 +85,21 @@ public:
const Engine::Scope& scope(int chunk_length);
QString DefaultOutput() { return "auto"; }
OutputDetailsList GetOutputsList() const;
bool CustomDeviceSupport(const QString &name);
bool ValidOutput(const QString &output);
QString DefaultOutput() { return "auto"; }
bool CustomDeviceSupport(const QString &output);
void ReloadSettings();
void SetEnvironment();
uint length() const;
uint position() const;
bool CanDecode(const QUrl &);
bool MetaDataForUrl(const QUrl &url, Engine::SimpleMetaBundle &b);
bool GetAudioCDContents(const QString &device, QList<QUrl> &urls);
bool FlushBuffer();
bool CreateStream();
void SetEqualizerEnabled(bool enabled);
void SetEqualizerParameters(int preamp, const QList<int>&);
void FadeOut(uint fadeLength, bool* terminate, bool exiting = false);
static void XineEventListener(void*, const xine_event_t*);
bool event(QEvent*);
Engine::SimpleMetaBundle fetchMetaData() const;
bool MakeNewStream();
bool EnsureStream();
void DetermineAndShowErrorMessage(); //call after failure to load/play
// Simple accessors
xine_stream_t *stream() { return stream_; }
@@ -125,10 +107,12 @@ public:
bool stop_fader() { return stop_fader_; }
void set_stop_fader(bool stop_fader) { stop_fader_ = stop_fader; }
private:
private:
static const char *kAutoOutput;
QString current_output_;
QVariant current_device_;
xine_t *xine_;
xine_stream_t *stream_;
xine_audio_port_t *audioport_;
@@ -160,6 +144,26 @@ private:
mutable Engine::SimpleMetaBundle current_bundle_;
void SetEnvironment();
void Cleanup();
bool EnsureStream();
void SetDevice();
uint length() const;
uint position() const;
bool MetaDataForUrl(const QUrl &url, Engine::SimpleMetaBundle &b);
bool GetAudioCDContents(const QString &device, QList<QUrl> &urls);
bool FlushBuffer();
static void XineEventListener(void*, const xine_event_t*);
bool event(QEvent*);
Engine::SimpleMetaBundle fetchMetaData() const;
void DetermineAndShowErrorMessage(); //call after failure to load/play
PluginDetailsList GetPluginList() const;
private slots:

View File

@@ -38,7 +38,7 @@ XineFader::XineFader(XineEngine *engine, xine_t *xine, xine_stream_t *stream, xi
paused_(false),
terminated_(false) {
if (engine->MakeNewStream()) {
if (engine->CreateStream()) {
increase_ = stream_;
xine_set_param(increase_, XINE_PARAM_AUDIO_AMP_LEVEL, 0);
}