Generate Hypnotoad rather than streaming it.
This commit is contained in:
parent
d612e8468d
commit
41ab99e62e
3
dist/macdeploy.py
vendored
3
dist/macdeploy.py
vendored
@ -56,6 +56,8 @@ XINEPLUGIN_SEARCH_PATH=[
|
|||||||
GSTREAMER_PLUGINS=[
|
GSTREAMER_PLUGINS=[
|
||||||
# Core plugins
|
# Core plugins
|
||||||
'libgstaudioconvert.so',
|
'libgstaudioconvert.so',
|
||||||
|
'libgstaudiofx.so',
|
||||||
|
'libgstaudiotestsrc.so',
|
||||||
'libgstaudioresample.so',
|
'libgstaudioresample.so',
|
||||||
'libgstautodetect.so',
|
'libgstautodetect.so',
|
||||||
'libgstcoreelements.so',
|
'libgstcoreelements.so',
|
||||||
@ -103,6 +105,7 @@ GSTREAMER_SEARCH_PATH=[
|
|||||||
|
|
||||||
QT_PLUGINS = [
|
QT_PLUGINS = [
|
||||||
'accessible/libqtaccessiblewidgets.dylib',
|
'accessible/libqtaccessiblewidgets.dylib',
|
||||||
|
'bearer/libqcorewlanbearer.dylib',
|
||||||
'codecs/libqcncodecs.dylib',
|
'codecs/libqcncodecs.dylib',
|
||||||
'codecs/libqjpcodecs.dylib',
|
'codecs/libqjpcodecs.dylib',
|
||||||
'codecs/libqkrcodecs.dylib',
|
'codecs/libqkrcodecs.dylib',
|
||||||
|
@ -49,7 +49,6 @@
|
|||||||
using boost::shared_ptr;
|
using boost::shared_ptr;
|
||||||
|
|
||||||
const char* Player::kRainUrl = "http://data.clementine-player.org/rainymood";
|
const char* Player::kRainUrl = "http://data.clementine-player.org/rainymood";
|
||||||
const char* Player::kHypnotoadUrl = "http://data.clementine-player.org/hypnotoad";
|
|
||||||
|
|
||||||
#ifdef Q_WS_X11
|
#ifdef Q_WS_X11
|
||||||
QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status) {
|
QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status) {
|
||||||
@ -621,7 +620,7 @@ void Player::MakeItRain(bool rain) {
|
|||||||
void Player::AllHail(bool hypnotoad) {
|
void Player::AllHail(bool hypnotoad) {
|
||||||
const bool is_hailing = toad_stream_ != -1;
|
const bool is_hailing = toad_stream_ != -1;
|
||||||
if (hypnotoad && !is_hailing) {
|
if (hypnotoad && !is_hailing) {
|
||||||
toad_stream_ = engine_->AddBackgroundStream(QUrl(kHypnotoadUrl));
|
toad_stream_ = engine_->AllGloryToTheHypnotoad();
|
||||||
}
|
}
|
||||||
if (!hypnotoad && is_hailing) {
|
if (!hypnotoad && is_hailing) {
|
||||||
engine_->StopBackgroundStream(toad_stream_);
|
engine_->StopBackgroundStream(toad_stream_);
|
||||||
|
@ -188,7 +188,6 @@ class Player : public QObject {
|
|||||||
int volume_before_mute_;
|
int volume_before_mute_;
|
||||||
|
|
||||||
static const char* kRainUrl;
|
static const char* kRainUrl;
|
||||||
static const char* kHypnotoadUrl;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // PLAYER_H
|
#endif // PLAYER_H
|
||||||
|
@ -53,6 +53,7 @@ class Base : public QObject, boost::noncopyable {
|
|||||||
|
|
||||||
virtual int AddBackgroundStream(const QUrl& url);
|
virtual int AddBackgroundStream(const QUrl& url);
|
||||||
virtual void StopBackgroundStream(int id) {}
|
virtual void StopBackgroundStream(int id) {}
|
||||||
|
virtual int AllGloryToTheHypnotoad() { return -1; }
|
||||||
|
|
||||||
virtual State state() const = 0;
|
virtual State state() const = 0;
|
||||||
virtual uint position() const = 0;
|
virtual uint position() const = 0;
|
||||||
|
@ -48,6 +48,13 @@ using boost::shared_ptr;
|
|||||||
|
|
||||||
const char* GstEngine::kSettingsGroup = "GstEngine";
|
const char* GstEngine::kSettingsGroup = "GstEngine";
|
||||||
const char* GstEngine::kAutoSink = "autoaudiosink";
|
const char* GstEngine::kAutoSink = "autoaudiosink";
|
||||||
|
const char* GstEngine::kHypnotoadPipeline =
|
||||||
|
"audiotestsrc wave=6 ! "
|
||||||
|
"audioecho intensity=1 delay=50000000 ! "
|
||||||
|
"audioecho intensity=1 delay=25000000 ! "
|
||||||
|
"equalizer-10bands "
|
||||||
|
"band0=-24 band1=-3 band2=7.5 band3=12 band4=8 "
|
||||||
|
"band5=6 band6=5 band7=6 band8=0 band9=-24";
|
||||||
|
|
||||||
|
|
||||||
GstEngine::GstEngine()
|
GstEngine::GstEngine()
|
||||||
@ -690,7 +697,7 @@ GstEngine::PluginDetailsList
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QUrl& url) {
|
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline() {
|
||||||
shared_ptr<GstEnginePipeline> ret(new GstEnginePipeline(this));
|
shared_ptr<GstEnginePipeline> ret(new GstEnginePipeline(this));
|
||||||
ret->set_output_device(sink_, device_);
|
ret->set_output_device(sink_, device_);
|
||||||
ret->set_replaygain(rg_enabled_, rg_mode_, rg_preamp_, rg_compression_);
|
ret->set_replaygain(rg_enabled_, rg_mode_, rg_preamp_, rg_compression_);
|
||||||
@ -705,7 +712,12 @@ shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QUrl& url) {
|
|||||||
SLOT(NewMetaData(Engine::SimpleMetaBundle)));
|
SLOT(NewMetaData(Engine::SimpleMetaBundle)));
|
||||||
connect(ret.get(), SIGNAL(destroyed()), SLOT(ClearScopeBuffers()));
|
connect(ret.get(), SIGNAL(destroyed()), SLOT(ClearScopeBuffers()));
|
||||||
|
|
||||||
if (!ret->Init(url))
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QUrl& url) {
|
||||||
|
shared_ptr<GstEnginePipeline> ret = CreatePipeline();
|
||||||
|
if (!ret->InitFromUrl(url))
|
||||||
ret.reset();
|
ret.reset();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -798,3 +810,24 @@ void GstEngine::BackgroundStreamFinished() {
|
|||||||
GstEnginePipeline* pipeline = qobject_cast<GstEnginePipeline*>(sender());
|
GstEnginePipeline* pipeline = qobject_cast<GstEnginePipeline*>(sender());
|
||||||
pipeline->SetNextUrl(pipeline->url());
|
pipeline->SetNextUrl(pipeline->url());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GstEngine::AllGloryToTheHypnotoad() {
|
||||||
|
shared_ptr<GstEnginePipeline> pipeline = CreatePipeline();
|
||||||
|
pipeline->InitFromString(kHypnotoadPipeline);
|
||||||
|
if (!pipeline) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
pipeline->SetVolume(5); // Hypnotoad is *loud*.
|
||||||
|
// We don't want to get metadata messages or end notifications.
|
||||||
|
disconnect(pipeline.get(), SIGNAL(MetadataFound(Engine::SimpleMetaBundle)), this, 0);
|
||||||
|
disconnect(pipeline.get(), SIGNAL(EndOfStreamReached(bool)), this, 0);
|
||||||
|
connect(pipeline.get(), SIGNAL(EndOfStreamReached(bool)), SLOT(BackgroundStreamFinished()));
|
||||||
|
if (!pipeline->SetState(GST_STATE_PLAYING)) {
|
||||||
|
qWarning() << "Could not set thread to PLAYING.";
|
||||||
|
pipeline.reset();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int stream_id = next_background_stream_id_++;
|
||||||
|
background_streams_[stream_id] = pipeline;
|
||||||
|
return stream_id;
|
||||||
|
}
|
||||||
|
@ -68,6 +68,7 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
|||||||
|
|
||||||
int AddBackgroundStream(const QUrl& url);
|
int AddBackgroundStream(const QUrl& url);
|
||||||
void StopBackgroundStream(int id);
|
void StopBackgroundStream(int id);
|
||||||
|
int AllGloryToTheHypnotoad();
|
||||||
|
|
||||||
uint position() const;
|
uint position() const;
|
||||||
uint length() const;
|
uint length() const;
|
||||||
@ -133,6 +134,7 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
|||||||
void StartTimers();
|
void StartTimers();
|
||||||
void StopTimers();
|
void StopTimers();
|
||||||
|
|
||||||
|
boost::shared_ptr<GstEnginePipeline> CreatePipeline();
|
||||||
boost::shared_ptr<GstEnginePipeline> CreatePipeline(const QUrl& url);
|
boost::shared_ptr<GstEnginePipeline> CreatePipeline(const QUrl& url);
|
||||||
|
|
||||||
void UpdateScope();
|
void UpdateScope();
|
||||||
@ -143,6 +145,8 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
|||||||
static const int kPreloadGap = 1000; // msec
|
static const int kPreloadGap = 1000; // msec
|
||||||
static const int kSeekDelay = 100; // msec
|
static const int kSeekDelay = 100; // msec
|
||||||
|
|
||||||
|
static const char* kHypnotoadPipeline;
|
||||||
|
|
||||||
QString sink_;
|
QString sink_;
|
||||||
QString device_;
|
QString device_;
|
||||||
|
|
||||||
|
@ -77,8 +77,7 @@ bool GstEnginePipeline::StopUriDecodeBin(gpointer bin) {
|
|||||||
return false; // So it doesn't get called again
|
return false; // So it doesn't get called again
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) {
|
bool GstEnginePipeline::ReplaceDecodeBin(GstElement* new_bin) {
|
||||||
GstElement* new_bin = engine_->CreateElement("uridecodebin");
|
|
||||||
if (!new_bin) return false;
|
if (!new_bin) return false;
|
||||||
|
|
||||||
// Destroy the old one, if any
|
// Destroy the old one, if any
|
||||||
@ -94,17 +93,33 @@ bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) {
|
|||||||
segment_start_received_ = false;
|
segment_start_received_ = false;
|
||||||
gst_bin_add(GST_BIN(pipeline_), uridecodebin_);
|
gst_bin_add(GST_BIN(pipeline_), uridecodebin_);
|
||||||
|
|
||||||
g_object_set(G_OBJECT(uridecodebin_), "uri", url.toEncoded().constData(), NULL);
|
|
||||||
g_signal_connect(G_OBJECT(uridecodebin_), "pad-added", G_CALLBACK(NewPadCallback), this);
|
|
||||||
g_signal_connect(G_OBJECT(uridecodebin_), "drained", G_CALLBACK(SourceDrainedCallback), this);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GstEnginePipeline::Init(const QUrl &url) {
|
bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) {
|
||||||
pipeline_ = gst_pipeline_new("pipeline");
|
GstElement* new_bin = engine_->CreateElement("uridecodebin");
|
||||||
url_ = url;
|
g_object_set(G_OBJECT(new_bin), "uri", url.toEncoded().constData(), NULL);
|
||||||
|
g_signal_connect(G_OBJECT(new_bin), "drained", G_CALLBACK(SourceDrainedCallback), this);
|
||||||
|
g_signal_connect(G_OBJECT(new_bin), "pad-added", G_CALLBACK(NewPadCallback), this);
|
||||||
|
return ReplaceDecodeBin(new_bin);
|
||||||
|
}
|
||||||
|
|
||||||
|
GstElement* GstEnginePipeline::CreateDecodeBinFromString(const char* pipeline) {
|
||||||
|
GError* error = NULL;
|
||||||
|
GstElement* bin = gst_parse_bin_from_description(pipeline, TRUE, &error);
|
||||||
|
if (error) {
|
||||||
|
QString message = QString::fromLocal8Bit(error->message);
|
||||||
|
g_error_free(error);
|
||||||
|
|
||||||
|
qWarning() << message;
|
||||||
|
emit Error(message);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bin;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GstEnginePipeline::Init() {
|
||||||
// Here we create all the parts of the gstreamer pipeline - from the source
|
// Here we create all the parts of the gstreamer pipeline - from the source
|
||||||
// to the sink. The parts of the pipeline are split up into bins:
|
// to the sink. The parts of the pipeline are split up into bins:
|
||||||
// uri decode bin -> audio bin
|
// uri decode bin -> audio bin
|
||||||
@ -114,9 +129,6 @@ bool GstEnginePipeline::Init(const QUrl &url) {
|
|||||||
// audioconvert -> rgvolume -> rglimiter -> equalizer_preamp -> equalizer ->
|
// audioconvert -> rgvolume -> rglimiter -> equalizer_preamp -> equalizer ->
|
||||||
// volume -> audioscale -> audioconvert -> audiosink
|
// volume -> audioscale -> audioconvert -> audiosink
|
||||||
|
|
||||||
// Decode bin
|
|
||||||
if (!ReplaceDecodeBin(url)) return false;
|
|
||||||
|
|
||||||
// Audio bin
|
// Audio bin
|
||||||
audiobin_ = gst_bin_new("audiobin");
|
audiobin_ = gst_bin_new("audiobin");
|
||||||
gst_bin_add(GST_BIN(pipeline_), audiobin_);
|
gst_bin_add(GST_BIN(pipeline_), audiobin_);
|
||||||
@ -192,10 +204,34 @@ bool GstEnginePipeline::Init(const QUrl &url) {
|
|||||||
|
|
||||||
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this);
|
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallbackSync, this);
|
||||||
bus_cb_id_ = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this);
|
bus_cb_id_ = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GstEnginePipeline::InitFromString(const QString& pipeline) {
|
||||||
|
pipeline_ = gst_pipeline_new("pipeline");
|
||||||
|
|
||||||
|
GstElement* new_bin = CreateDecodeBinFromString(pipeline.toAscii().constData());
|
||||||
|
if (!new_bin) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ReplaceDecodeBin(new_bin)) return false;
|
||||||
|
|
||||||
|
if (!Init()) return false;
|
||||||
|
return gst_element_link(new_bin, audiobin_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GstEnginePipeline::InitFromUrl(const QUrl &url) {
|
||||||
|
pipeline_ = gst_pipeline_new("pipeline");
|
||||||
|
|
||||||
|
url_ = url;
|
||||||
|
|
||||||
|
// Decode bin
|
||||||
|
if (!ReplaceDecodeBin(url)) return false;
|
||||||
|
|
||||||
|
return Init();
|
||||||
|
}
|
||||||
|
|
||||||
GstEnginePipeline::~GstEnginePipeline() {
|
GstEnginePipeline::~GstEnginePipeline() {
|
||||||
if (pipeline_) {
|
if (pipeline_) {
|
||||||
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), NULL, NULL);
|
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), NULL, NULL);
|
||||||
|
@ -45,7 +45,8 @@ class GstEnginePipeline : public QObject {
|
|||||||
void set_replaygain(bool enabled, int mode, float preamp, bool compression);
|
void set_replaygain(bool enabled, int mode, float preamp, bool compression);
|
||||||
|
|
||||||
// Creates the pipeline, returns false on error
|
// Creates the pipeline, returns false on error
|
||||||
bool Init(const QUrl& url);
|
bool InitFromUrl(const QUrl& url);
|
||||||
|
bool InitFromString(const QString& pipeline);
|
||||||
|
|
||||||
// BufferConsumers get fed audio data. Thread-safe.
|
// BufferConsumers get fed audio data. Thread-safe.
|
||||||
void AddBufferConsumer(BufferConsumer* consumer);
|
void AddBufferConsumer(BufferConsumer* consumer);
|
||||||
@ -103,8 +104,12 @@ class GstEnginePipeline : public QObject {
|
|||||||
void ElementMessageReceived(GstMessage*);
|
void ElementMessageReceived(GstMessage*);
|
||||||
QString ParseTag(GstTagList* list, const char* tag) const;
|
QString ParseTag(GstTagList* list, const char* tag) const;
|
||||||
|
|
||||||
|
bool Init();
|
||||||
|
GstElement* CreateDecodeBinFromString(const char* pipeline);
|
||||||
|
|
||||||
void UpdateVolume();
|
void UpdateVolume();
|
||||||
void UpdateEqualizer();
|
void UpdateEqualizer();
|
||||||
|
bool ReplaceDecodeBin(GstElement* new_bin);
|
||||||
bool ReplaceDecodeBin(const QUrl& url);
|
bool ReplaceDecodeBin(const QUrl& url);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
@ -247,13 +247,12 @@ void NowPlayingWidget::paintEvent(QPaintEvent *e) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void NowPlayingWidget::DrawContents(QPainter *p) {
|
void NowPlayingWidget::DrawContents(QPainter *p) {
|
||||||
const int total_size = qMin(kMaxCoverSize, width());
|
|
||||||
if (hypnotoad_) {
|
|
||||||
p->drawPixmap(0, 0, total_size, total_size, hypnotoad_->currentPixmap());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
switch (mode_) {
|
switch (mode_) {
|
||||||
case SmallSongDetails:
|
case SmallSongDetails:
|
||||||
|
if (hypnotoad_) {
|
||||||
|
p->drawPixmap(0, 0, small_ideal_height_, small_ideal_height_, hypnotoad_->currentPixmap());
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Draw the cover
|
// Draw the cover
|
||||||
p->drawPixmap(0, 0, small_ideal_height_, small_ideal_height_, cover_);
|
p->drawPixmap(0, 0, small_ideal_height_, small_ideal_height_, cover_);
|
||||||
|
|
||||||
@ -264,6 +263,11 @@ void NowPlayingWidget::DrawContents(QPainter *p) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case LargeSongDetails:
|
case LargeSongDetails:
|
||||||
|
const int total_size = qMin(kMaxCoverSize, width());
|
||||||
|
if (hypnotoad_) {
|
||||||
|
p->drawPixmap(0, 0, total_size, total_size, hypnotoad_->currentPixmap());
|
||||||
|
return;
|
||||||
|
}
|
||||||
const int x_offset = (width() - cover_height_) / 2;
|
const int x_offset = (width() - cover_height_) / 2;
|
||||||
|
|
||||||
// Draw the black background
|
// Draw the black background
|
||||||
|
Loading…
x
Reference in New Issue
Block a user