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=[
|
||||
# Core plugins
|
||||
'libgstaudioconvert.so',
|
||||
'libgstaudiofx.so',
|
||||
'libgstaudiotestsrc.so',
|
||||
'libgstaudioresample.so',
|
||||
'libgstautodetect.so',
|
||||
'libgstcoreelements.so',
|
||||
@ -103,6 +105,7 @@ GSTREAMER_SEARCH_PATH=[
|
||||
|
||||
QT_PLUGINS = [
|
||||
'accessible/libqtaccessiblewidgets.dylib',
|
||||
'bearer/libqcorewlanbearer.dylib',
|
||||
'codecs/libqcncodecs.dylib',
|
||||
'codecs/libqjpcodecs.dylib',
|
||||
'codecs/libqkrcodecs.dylib',
|
||||
|
@ -49,7 +49,6 @@
|
||||
using boost::shared_ptr;
|
||||
|
||||
const char* Player::kRainUrl = "http://data.clementine-player.org/rainymood";
|
||||
const char* Player::kHypnotoadUrl = "http://data.clementine-player.org/hypnotoad";
|
||||
|
||||
#ifdef Q_WS_X11
|
||||
QDBusArgument& operator<< (QDBusArgument& arg, const DBusStatus& status) {
|
||||
@ -621,7 +620,7 @@ void Player::MakeItRain(bool rain) {
|
||||
void Player::AllHail(bool hypnotoad) {
|
||||
const bool is_hailing = toad_stream_ != -1;
|
||||
if (hypnotoad && !is_hailing) {
|
||||
toad_stream_ = engine_->AddBackgroundStream(QUrl(kHypnotoadUrl));
|
||||
toad_stream_ = engine_->AllGloryToTheHypnotoad();
|
||||
}
|
||||
if (!hypnotoad && is_hailing) {
|
||||
engine_->StopBackgroundStream(toad_stream_);
|
||||
|
@ -188,7 +188,6 @@ class Player : public QObject {
|
||||
int volume_before_mute_;
|
||||
|
||||
static const char* kRainUrl;
|
||||
static const char* kHypnotoadUrl;
|
||||
};
|
||||
|
||||
#endif // PLAYER_H
|
||||
|
@ -53,6 +53,7 @@ class Base : public QObject, boost::noncopyable {
|
||||
|
||||
virtual int AddBackgroundStream(const QUrl& url);
|
||||
virtual void StopBackgroundStream(int id) {}
|
||||
virtual int AllGloryToTheHypnotoad() { return -1; }
|
||||
|
||||
virtual State state() const = 0;
|
||||
virtual uint position() const = 0;
|
||||
|
@ -48,6 +48,13 @@ using boost::shared_ptr;
|
||||
|
||||
const char* GstEngine::kSettingsGroup = "GstEngine";
|
||||
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()
|
||||
@ -690,7 +697,7 @@ GstEngine::PluginDetailsList
|
||||
return ret;
|
||||
}
|
||||
|
||||
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QUrl& url) {
|
||||
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline() {
|
||||
shared_ptr<GstEnginePipeline> ret(new GstEnginePipeline(this));
|
||||
ret->set_output_device(sink_, device_);
|
||||
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)));
|
||||
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();
|
||||
|
||||
return ret;
|
||||
@ -798,3 +810,24 @@ void GstEngine::BackgroundStreamFinished() {
|
||||
GstEnginePipeline* pipeline = qobject_cast<GstEnginePipeline*>(sender());
|
||||
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);
|
||||
void StopBackgroundStream(int id);
|
||||
int AllGloryToTheHypnotoad();
|
||||
|
||||
uint position() const;
|
||||
uint length() const;
|
||||
@ -133,6 +134,7 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
||||
void StartTimers();
|
||||
void StopTimers();
|
||||
|
||||
boost::shared_ptr<GstEnginePipeline> CreatePipeline();
|
||||
boost::shared_ptr<GstEnginePipeline> CreatePipeline(const QUrl& url);
|
||||
|
||||
void UpdateScope();
|
||||
@ -143,6 +145,8 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
||||
static const int kPreloadGap = 1000; // msec
|
||||
static const int kSeekDelay = 100; // msec
|
||||
|
||||
static const char* kHypnotoadPipeline;
|
||||
|
||||
QString sink_;
|
||||
QString device_;
|
||||
|
||||
|
@ -77,8 +77,7 @@ bool GstEnginePipeline::StopUriDecodeBin(gpointer bin) {
|
||||
return false; // So it doesn't get called again
|
||||
}
|
||||
|
||||
bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) {
|
||||
GstElement* new_bin = engine_->CreateElement("uridecodebin");
|
||||
bool GstEnginePipeline::ReplaceDecodeBin(GstElement* new_bin) {
|
||||
if (!new_bin) return false;
|
||||
|
||||
// Destroy the old one, if any
|
||||
@ -94,17 +93,33 @@ bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) {
|
||||
segment_start_received_ = false;
|
||||
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;
|
||||
}
|
||||
|
||||
bool GstEnginePipeline::Init(const QUrl &url) {
|
||||
pipeline_ = gst_pipeline_new("pipeline");
|
||||
url_ = url;
|
||||
bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) {
|
||||
GstElement* new_bin = engine_->CreateElement("uridecodebin");
|
||||
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
|
||||
// to the sink. The parts of the pipeline are split up into bins:
|
||||
// uri decode bin -> audio bin
|
||||
@ -114,9 +129,6 @@ bool GstEnginePipeline::Init(const QUrl &url) {
|
||||
// audioconvert -> rgvolume -> rglimiter -> equalizer_preamp -> equalizer ->
|
||||
// volume -> audioscale -> audioconvert -> audiosink
|
||||
|
||||
// Decode bin
|
||||
if (!ReplaceDecodeBin(url)) return false;
|
||||
|
||||
// Audio bin
|
||||
audiobin_ = gst_bin_new("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);
|
||||
bus_cb_id_ = gst_bus_add_watch(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), BusCallback, this);
|
||||
|
||||
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() {
|
||||
if (pipeline_) {
|
||||
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);
|
||||
|
||||
// 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.
|
||||
void AddBufferConsumer(BufferConsumer* consumer);
|
||||
@ -103,8 +104,12 @@ class GstEnginePipeline : public QObject {
|
||||
void ElementMessageReceived(GstMessage*);
|
||||
QString ParseTag(GstTagList* list, const char* tag) const;
|
||||
|
||||
bool Init();
|
||||
GstElement* CreateDecodeBinFromString(const char* pipeline);
|
||||
|
||||
void UpdateVolume();
|
||||
void UpdateEqualizer();
|
||||
bool ReplaceDecodeBin(GstElement* new_bin);
|
||||
bool ReplaceDecodeBin(const QUrl& url);
|
||||
|
||||
private slots:
|
||||
|
@ -247,13 +247,12 @@ void NowPlayingWidget::paintEvent(QPaintEvent *e) {
|
||||
}
|
||||
|
||||
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_) {
|
||||
case SmallSongDetails:
|
||||
if (hypnotoad_) {
|
||||
p->drawPixmap(0, 0, small_ideal_height_, small_ideal_height_, hypnotoad_->currentPixmap());
|
||||
return;
|
||||
}
|
||||
// Draw the cover
|
||||
p->drawPixmap(0, 0, small_ideal_height_, small_ideal_height_, cover_);
|
||||
|
||||
@ -264,6 +263,11 @@ void NowPlayingWidget::DrawContents(QPainter *p) {
|
||||
break;
|
||||
|
||||
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;
|
||||
|
||||
// Draw the black background
|
||||
|
Loading…
x
Reference in New Issue
Block a user