parent
ca8877ad47
commit
95ac85f642
|
@ -31,6 +31,7 @@
|
|||
#include <string>
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QFuture>
|
||||
|
@ -53,6 +54,7 @@
|
|||
#include "core/logging.h"
|
||||
#include "core/taskmanager.h"
|
||||
#include "core/timeconstants.h"
|
||||
#include "core/signalchecker.h"
|
||||
#include "enginebase.h"
|
||||
#include "enginetype.h"
|
||||
#include "gstengine.h"
|
||||
|
@ -71,9 +73,12 @@ const char *GstEngine::kAVDTPSink = "avdtpsink";
|
|||
const char *GstEngine::InterAudiosink = "interaudiosink";
|
||||
const char *GstEngine::kDirectSoundSink = "directsoundsink";
|
||||
const char *GstEngine::kOSXAudioSink = "osxaudiosink";
|
||||
const int GstEngine::kDiscoveryTimeoutS = 10;
|
||||
|
||||
GstEngine::GstEngine(TaskManager *task_manager)
|
||||
: task_manager_(task_manager),
|
||||
gst_startup_(nullptr),
|
||||
discoverer_(nullptr),
|
||||
buffering_task_id_(-1),
|
||||
latest_buffer_(nullptr),
|
||||
stereo_balancer_enabled_(false),
|
||||
|
@ -86,7 +91,10 @@ GstEngine::GstEngine(TaskManager *task_manager)
|
|||
is_fading_out_to_pause_(false),
|
||||
has_faded_out_(false),
|
||||
scope_chunk_(0),
|
||||
have_new_buffer_(false) {
|
||||
have_new_buffer_(false),
|
||||
discovery_finished_cb_id_(-1),
|
||||
discovery_discovered_cb_id_(-1)
|
||||
{
|
||||
|
||||
type_ = Engine::GStreamer;
|
||||
seek_timer_->setSingleShot(true);
|
||||
|
@ -107,6 +115,19 @@ GstEngine::~GstEngine() {
|
|||
latest_buffer_ = nullptr;
|
||||
}
|
||||
|
||||
if (discoverer_) {
|
||||
|
||||
if (discovery_discovered_cb_id_ != -1)
|
||||
g_signal_handler_disconnect(G_OBJECT(discoverer_), discovery_discovered_cb_id_);
|
||||
if (discovery_finished_cb_id_ != -1)
|
||||
g_signal_handler_disconnect(G_OBJECT(discoverer_), discovery_finished_cb_id_);
|
||||
|
||||
gst_discoverer_stop(discoverer_);
|
||||
g_object_unref(discoverer_);
|
||||
discoverer_ = nullptr;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool GstEngine::Init() {
|
||||
|
@ -141,8 +162,17 @@ void GstEngine::StartPreloading(const QUrl &stream_url, const QUrl &original_url
|
|||
QByteArray gst_url = FixupUrl(stream_url);
|
||||
|
||||
// No crossfading, so we can just queue the new URL in the existing pipeline and get gapless playback (hopefully)
|
||||
if (current_pipeline_)
|
||||
if (current_pipeline_) {
|
||||
current_pipeline_->SetNextUrl(gst_url, original_url, beginning_nanosec, force_stop_at_end ? end_nanosec : 0);
|
||||
// Add request to discover the stream
|
||||
#ifdef Q_OS_LINUX
|
||||
if (discoverer_) {
|
||||
if (!gst_discoverer_discover_uri_async(discoverer_, gst_url.toStdString().c_str())) {
|
||||
qLog(Error) << "Failed to start stream discovery for" << gst_url;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -180,6 +210,27 @@ bool GstEngine::Load(const QUrl &stream_url, const QUrl &original_url, Engine::T
|
|||
if (crossfade)
|
||||
current_pipeline_->StartFader(fadeout_duration_nanosec_, QTimeLine::Forward);
|
||||
|
||||
// Setting up stream discoverer
|
||||
#ifdef Q_OS_LINUX
|
||||
if (!discoverer_) {
|
||||
discoverer_ = gst_discoverer_new(kDiscoveryTimeoutS * GST_SECOND, nullptr);
|
||||
if (discoverer_) {
|
||||
discovery_discovered_cb_id_ = CHECKED_GCONNECT(G_OBJECT(discoverer_), "discovered", &StreamDiscovered, this);
|
||||
discovery_finished_cb_id_ = CHECKED_GCONNECT(G_OBJECT(discoverer_), "finished", &StreamDiscoveryFinished, this);
|
||||
gst_discoverer_start(discoverer_);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Add request to discover the stream
|
||||
#ifdef Q_OS_LINUX
|
||||
if (discoverer_) {
|
||||
if (!gst_discoverer_discover_uri_async(discoverer_, gst_url.toStdString().c_str())) {
|
||||
qLog(Error) << "Failed to start stream discovery for" << gst_url;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
@ -845,3 +896,69 @@ void GstEngine::UpdateScope(const int chunk_length) {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
void GstEngine::StreamDiscovered(GstDiscoverer*, GstDiscovererInfo *info, GError*, gpointer self) {
|
||||
|
||||
GstEngine *instance = reinterpret_cast<GstEngine*>(self);
|
||||
if (!instance->current_pipeline_) return;
|
||||
|
||||
QString discovered_url(gst_discoverer_info_get_uri(info));
|
||||
|
||||
GstDiscovererResult result = gst_discoverer_info_get_result(info);
|
||||
if (result != GST_DISCOVERER_OK) {
|
||||
QString error_message = GSTdiscovererErrorMessage(result);
|
||||
qLog(Error) << QString("Stream discovery for %1 failed: %2").arg(discovered_url).arg(error_message);
|
||||
return;
|
||||
}
|
||||
|
||||
GList *audio_streams = gst_discoverer_info_get_audio_streams(info);
|
||||
if (audio_streams) {
|
||||
|
||||
GstDiscovererStreamInfo *stream_info = reinterpret_cast<GstDiscovererStreamInfo*>(g_list_first(audio_streams)->data);
|
||||
|
||||
Engine::SimpleMetaBundle bundle;
|
||||
if (discovered_url == instance->current_pipeline_->stream_url()) {
|
||||
bundle.url = instance->current_pipeline_->original_url();
|
||||
}
|
||||
else if (discovered_url == instance->current_pipeline_->next_stream_url()) {
|
||||
bundle.url = instance->current_pipeline_->next_original_url();
|
||||
}
|
||||
bundle.stream_url = QUrl(discovered_url);
|
||||
bundle.samplerate = gst_discoverer_audio_info_get_sample_rate(GST_DISCOVERER_AUDIO_INFO(stream_info));
|
||||
bundle.bitdepth = gst_discoverer_audio_info_get_depth(GST_DISCOVERER_AUDIO_INFO(stream_info));
|
||||
bundle.bitrate = gst_discoverer_audio_info_get_bitrate(GST_DISCOVERER_AUDIO_INFO(stream_info)) / 1000;
|
||||
|
||||
GstCaps *caps = gst_discoverer_stream_info_get_caps(stream_info);
|
||||
gchar *codec_description = gst_pb_utils_get_codec_description(caps);
|
||||
QString filetype_description = (codec_description ? QString(codec_description) : QString("Unknown"));
|
||||
g_free(codec_description);
|
||||
|
||||
gst_caps_unref(caps);
|
||||
gst_discoverer_stream_info_list_free(audio_streams);
|
||||
|
||||
bundle.filetype = Song::FiletypeByDescription(filetype_description);
|
||||
qLog(Info) << "Got stream info for" << discovered_url + ":" << filetype_description;
|
||||
|
||||
emit instance->MetaData(bundle);
|
||||
|
||||
}
|
||||
else {
|
||||
qLog(Error) << "Could not detect an audio stream in" << discovered_url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GstEngine::StreamDiscoveryFinished(GstDiscoverer*, gpointer) {}
|
||||
|
||||
QString GstEngine::GSTdiscovererErrorMessage(GstDiscovererResult result) {
|
||||
|
||||
switch (result) {
|
||||
case GST_DISCOVERER_URI_INVALID: return "The URI is invalid";
|
||||
case GST_DISCOVERER_TIMEOUT: return "The discovery timed-out";
|
||||
case GST_DISCOVERER_BUSY: return "The discoverer was already discovering a file";
|
||||
case GST_DISCOVERER_MISSING_PLUGINS: return "Some plugins are missing for full discovery";
|
||||
case GST_DISCOVERER_ERROR:
|
||||
default: return "An error happened and the GError is set";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <memory>
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
|
@ -126,19 +127,6 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
|
|||
void BufferingFinished();
|
||||
|
||||
private:
|
||||
static const char *kAutoSink;
|
||||
static const char *kALSASink;
|
||||
static const char *kOpenALSASink;
|
||||
static const char *kOSSSink;
|
||||
static const char *kOSS4Sink;
|
||||
static const char *kJackAudioSink;
|
||||
static const char *kPulseSink;
|
||||
static const char *kA2DPSink;
|
||||
static const char *kAVDTPSink;
|
||||
static const char *InterAudiosink;
|
||||
static const char *kDirectSoundSink;
|
||||
static const char *kOSXAudioSink;
|
||||
|
||||
PluginDetailsList GetPluginList(const QString &classname) const;
|
||||
QByteArray FixupUrl(const QUrl &url);
|
||||
|
||||
|
@ -153,13 +141,32 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
|
|||
|
||||
void UpdateScope(int chunk_length);
|
||||
|
||||
static void StreamDiscovered(GstDiscoverer*, GstDiscovererInfo *info, GError*, gpointer self);
|
||||
static void StreamDiscoveryFinished(GstDiscoverer*, gpointer);
|
||||
static QString GSTdiscovererErrorMessage(GstDiscovererResult result);
|
||||
|
||||
private:
|
||||
static const char *kAutoSink;
|
||||
static const char *kALSASink;
|
||||
static const char *kOpenALSASink;
|
||||
static const char *kOSSSink;
|
||||
static const char *kOSS4Sink;
|
||||
static const char *kJackAudioSink;
|
||||
static const char *kPulseSink;
|
||||
static const char *kA2DPSink;
|
||||
static const char *kAVDTPSink;
|
||||
static const char *InterAudiosink;
|
||||
static const char *kDirectSoundSink;
|
||||
static const char *kOSXAudioSink;
|
||||
static const int kDiscoveryTimeoutS;
|
||||
static const qint64 kTimerIntervalNanosec = 1000 * kNsecPerMsec; // 1s
|
||||
static const qint64 kPreloadGapNanosec = 5000 * kNsecPerMsec; // 5s
|
||||
static const qint64 kSeekDelayNanosec = 100 * kNsecPerMsec; // 100msec
|
||||
|
||||
TaskManager *task_manager_;
|
||||
GstStartup *gst_startup_;
|
||||
GstDiscoverer *discoverer_;
|
||||
|
||||
int buffering_task_id_;
|
||||
|
||||
std::shared_ptr<GstEnginePipeline> current_pipeline_;
|
||||
|
@ -197,6 +204,9 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
|
|||
int scope_chunks_;
|
||||
QString buffer_format_;
|
||||
|
||||
int discovery_finished_cb_id_;
|
||||
int discovery_discovered_cb_id_;
|
||||
|
||||
};
|
||||
|
||||
#endif /* GSTENGINE_H */
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include <glib-object.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/audio/audio.h>
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
|
@ -59,7 +58,6 @@
|
|||
|
||||
const int GstEnginePipeline::kGstStateTimeoutNanosecs = 10000000;
|
||||
const int GstEnginePipeline::kFaderFudgeMsec = 2000;
|
||||
const int GstEnginePipeline::kDiscoveryTimeoutS = 10;
|
||||
|
||||
const int GstEnginePipeline::kEqBandCount = 10;
|
||||
const int GstEnginePipeline::kEqBandFrequencies[] = { 60, 170, 310, 600, 1000, 3000, 6000, 12000, 14000, 16000 };
|
||||
|
@ -107,13 +105,10 @@ GstEnginePipeline::GstEnginePipeline(GstEngine *engine)
|
|||
audiopanorama_(nullptr),
|
||||
equalizer_(nullptr),
|
||||
equalizer_preamp_(nullptr),
|
||||
discoverer_(nullptr),
|
||||
pad_added_cb_id_(-1),
|
||||
notify_source_cb_id_(-1),
|
||||
about_to_finish_cb_id_(-1),
|
||||
bus_cb_id_(-1),
|
||||
discovery_finished_cb_id_(-1),
|
||||
discovery_discovered_cb_id_(-1),
|
||||
unsupported_analyzer_(false)
|
||||
{
|
||||
|
||||
|
@ -127,18 +122,6 @@ GstEnginePipeline::GstEnginePipeline(GstEngine *engine)
|
|||
|
||||
GstEnginePipeline::~GstEnginePipeline() {
|
||||
|
||||
if (discoverer_) {
|
||||
|
||||
if (discovery_discovered_cb_id_ != -1)
|
||||
g_signal_handler_disconnect(G_OBJECT(discoverer_), discovery_discovered_cb_id_);
|
||||
if (discovery_finished_cb_id_ != -1)
|
||||
g_signal_handler_disconnect(G_OBJECT(discoverer_), discovery_finished_cb_id_);
|
||||
|
||||
g_object_unref(discoverer_);
|
||||
discoverer_ = nullptr;
|
||||
|
||||
}
|
||||
|
||||
if (pipeline_) {
|
||||
|
||||
if (pad_added_cb_id_ != -1)
|
||||
|
@ -234,16 +217,6 @@ bool GstEnginePipeline::InitFromUrl(const QByteArray &stream_url, const QUrl ori
|
|||
notify_source_cb_id_ = CHECKED_GCONNECT(G_OBJECT(pipeline_), "notify::source", &SourceSetupCallback, this);
|
||||
about_to_finish_cb_id_ = CHECKED_GCONNECT(G_OBJECT(pipeline_), "about-to-finish", &AboutToFinishCallback, this);
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// Setting up a discoverer
|
||||
discoverer_ = gst_discoverer_new(kDiscoveryTimeoutS * GST_SECOND, nullptr);
|
||||
if (discoverer_) {
|
||||
discovery_discovered_cb_id_ = CHECKED_GCONNECT(G_OBJECT(discoverer_), "discovered", &StreamDiscovered, this);
|
||||
discovery_finished_cb_id_ = CHECKED_GCONNECT(G_OBJECT(discoverer_), "finished", &StreamDiscoveryFinished, this);
|
||||
gst_discoverer_start(discoverer_);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!InitAudioBin()) return false;
|
||||
|
||||
// Set playbin's sink to be our custom audio-sink.
|
||||
|
@ -442,15 +415,6 @@ bool GstEnginePipeline::InitAudioBin() {
|
|||
bus_cb_id_ = gst_bus_add_watch(bus, BusCallback, this);
|
||||
gst_object_unref(bus);
|
||||
|
||||
// Add request to discover the stream
|
||||
#ifdef Q_OS_LINUX
|
||||
if (discoverer_) {
|
||||
if (!gst_discoverer_discover_uri_async(discoverer_, stream_url_.toStdString().c_str())) {
|
||||
qLog(Error) << "Failed to start stream discovery for" << stream_url_;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
unsupported_analyzer_ = false;
|
||||
|
||||
return true;
|
||||
|
@ -1005,16 +969,6 @@ void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) {
|
|||
next_uri_set_ = false;
|
||||
g_object_set(G_OBJECT(pipeline_), "uri", stream_url_.constData(), nullptr);
|
||||
SetState(GST_STATE_PLAYING);
|
||||
|
||||
// Add request to discover the stream
|
||||
#ifdef Q_OS_LINUX
|
||||
if (discoverer_) {
|
||||
if (!gst_discoverer_discover_uri_async(discoverer_, stream_url_.toStdString().c_str())) {
|
||||
qLog(Error) << "Failed to start stream discovery for" << stream_url_;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1263,78 +1217,4 @@ void GstEnginePipeline::SetNextUrl(const QByteArray &stream_url, const QUrl &ori
|
|||
next_beginning_offset_nanosec_ = beginning_nanosec;
|
||||
next_end_offset_nanosec_ = end_nanosec;
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// Add request to discover the stream
|
||||
if (discoverer_) {
|
||||
if (!gst_discoverer_discover_uri_async(discoverer_, next_stream_url_.toStdString().c_str())) {
|
||||
qLog(Error) << "Failed to start stream discovery for" << next_stream_url_;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void GstEnginePipeline::StreamDiscovered(GstDiscoverer*, GstDiscovererInfo *info, GError*, gpointer self) {
|
||||
|
||||
GstEnginePipeline *instance = reinterpret_cast<GstEnginePipeline*>(self);
|
||||
|
||||
QString discovered_url(gst_discoverer_info_get_uri(info));
|
||||
|
||||
GstDiscovererResult result = gst_discoverer_info_get_result(info);
|
||||
if (result != GST_DISCOVERER_OK) {
|
||||
QString error_message = GSTdiscovererErrorMessage(result);
|
||||
qLog(Error) << QString("Stream discovery for %1 failed: %2").arg(discovered_url).arg(error_message);
|
||||
return;
|
||||
}
|
||||
|
||||
GList *audio_streams = gst_discoverer_info_get_audio_streams(info);
|
||||
if (audio_streams) {
|
||||
|
||||
GstDiscovererStreamInfo *stream_info = reinterpret_cast<GstDiscovererStreamInfo*>(g_list_first(audio_streams)->data);
|
||||
|
||||
Engine::SimpleMetaBundle bundle;
|
||||
if (discovered_url == instance->stream_url_) {
|
||||
bundle.url = instance->original_url_;
|
||||
}
|
||||
else if (discovered_url == instance->next_stream_url_) {
|
||||
bundle.url = instance->next_original_url_;
|
||||
}
|
||||
bundle.stream_url = QUrl(discovered_url);
|
||||
bundle.samplerate = gst_discoverer_audio_info_get_sample_rate(GST_DISCOVERER_AUDIO_INFO(stream_info));
|
||||
bundle.bitdepth = gst_discoverer_audio_info_get_depth(GST_DISCOVERER_AUDIO_INFO(stream_info));
|
||||
bundle.bitrate = gst_discoverer_audio_info_get_bitrate(GST_DISCOVERER_AUDIO_INFO(stream_info)) / 1000;
|
||||
|
||||
GstCaps *caps = gst_discoverer_stream_info_get_caps(stream_info);
|
||||
gchar *codec_description = gst_pb_utils_get_codec_description(caps);
|
||||
QString filetype_description = (codec_description ? QString(codec_description) : QString("Unknown"));
|
||||
g_free(codec_description);
|
||||
|
||||
gst_caps_unref(caps);
|
||||
gst_discoverer_stream_info_list_free(audio_streams);
|
||||
|
||||
bundle.filetype = Song::FiletypeByDescription(filetype_description);
|
||||
qLog(Info) << "Got stream info for" << discovered_url + ":" << filetype_description;
|
||||
|
||||
emit instance->MetadataFound(instance->id(), bundle);
|
||||
|
||||
}
|
||||
else {
|
||||
qLog(Error) << "Could not detect an audio stream in" << discovered_url;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void GstEnginePipeline::StreamDiscoveryFinished(GstDiscoverer*, gpointer) {}
|
||||
|
||||
QString GstEnginePipeline::GSTdiscovererErrorMessage(GstDiscovererResult result) {
|
||||
|
||||
switch (result) {
|
||||
case GST_DISCOVERER_URI_INVALID: return "The URI is invalid";
|
||||
case GST_DISCOVERER_TIMEOUT: return "The discovery timed-out";
|
||||
case GST_DISCOVERER_BUSY: return "The discoverer was already discovering a file";
|
||||
case GST_DISCOVERER_MISSING_PLUGINS: return "Some plugins are missing for full discovery";
|
||||
case GST_DISCOVERER_ERROR:
|
||||
default: return "An error happened and the GError is set";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,7 +29,6 @@
|
|||
#include <glib-object.h>
|
||||
#include <glib/gtypes.h>
|
||||
#include <gst/gst.h>
|
||||
#include <gst/pbutils/pbutils.h>
|
||||
|
||||
#include <QtGlobal>
|
||||
#include <QObject>
|
||||
|
@ -100,7 +99,9 @@ class GstEnginePipeline : public QObject {
|
|||
|
||||
// Get information about the music playback
|
||||
QByteArray stream_url() const { return stream_url_; }
|
||||
QByteArray next_stream_url() const { return next_stream_url_; }
|
||||
QUrl original_url() const { return original_url_; }
|
||||
QUrl next_original_url() const { return next_original_url_; }
|
||||
bool is_valid() const { return valid_; }
|
||||
|
||||
// Please note that this method (unlike GstEngine's.position()) is multiple-section media unaware.
|
||||
|
@ -149,9 +150,6 @@ class GstEnginePipeline : public QObject {
|
|||
static GstBusSyncReply BusCallbackSync(GstBus*, GstMessage*, gpointer);
|
||||
static gboolean BusCallback(GstBus*, GstMessage*, gpointer);
|
||||
static void TaskEnterCallback(GstTask*, GThread*, gpointer);
|
||||
static void StreamDiscovered(GstDiscoverer*, GstDiscovererInfo *info, GError*, gpointer self);
|
||||
static void StreamDiscoveryFinished(GstDiscoverer*, gpointer);
|
||||
static QString GSTdiscovererErrorMessage(GstDiscovererResult result);
|
||||
|
||||
void TagMessageReceived(GstMessage*);
|
||||
void ErrorMessageReceived(GstMessage*);
|
||||
|
@ -174,7 +172,6 @@ class GstEnginePipeline : public QObject {
|
|||
private:
|
||||
static const int kGstStateTimeoutNanosecs;
|
||||
static const int kFaderFudgeMsec;
|
||||
static const int kDiscoveryTimeoutS;
|
||||
static const int kEqBandCount;
|
||||
static const int kEqBandFrequencies[];
|
||||
|
||||
|
@ -275,14 +272,11 @@ class GstEnginePipeline : public QObject {
|
|||
GstElement *audiopanorama_;
|
||||
GstElement *equalizer_;
|
||||
GstElement *equalizer_preamp_;
|
||||
GstDiscoverer *discoverer_;
|
||||
|
||||
int pad_added_cb_id_;
|
||||
int notify_source_cb_id_;
|
||||
int about_to_finish_cb_id_;
|
||||
int bus_cb_id_;
|
||||
int discovery_finished_cb_id_;
|
||||
int discovery_discovered_cb_id_;
|
||||
|
||||
QThreadPool set_state_threadpool_;
|
||||
|
||||
|
|
Loading…
Reference in New Issue