Generate Hypnotoad rather than streaming it.

This commit is contained in:
John Maguire 2010-07-14 11:16:56 +00:00
parent d612e8468d
commit 41ab99e62e
9 changed files with 108 additions and 24 deletions

3
dist/macdeploy.py vendored
View File

@ -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',

View File

@ -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_);

View File

@ -188,7 +188,6 @@ class Player : public QObject {
int volume_before_mute_;
static const char* kRainUrl;
static const char* kHypnotoadUrl;
};
#endif // PLAYER_H

View File

@ -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;

View File

@ -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;
}

View File

@ -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_;

View File

@ -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);

View File

@ -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:

View File

@ -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