1
0
mirror of https://github.com/strawberrymusicplayer/strawberry synced 2025-02-01 10:06:43 +01:00

Fix CD playback.

This commit is contained in:
Jonas Kvinge 2018-03-10 13:00:09 +01:00
parent 1aabdc9b8b
commit e5426500a8
2 changed files with 39 additions and 92 deletions

View File

@ -22,6 +22,8 @@
#include <QMutexLocker>
#include "core/logging.h"
#include "collection/collectionbackend.h"
#include "collection/collectionmodel.h"
@ -62,4 +64,3 @@ void CddaDevice::SongsLoaded(const SongList &songs) {
song_count_ = songs.size();
}

View File

@ -95,8 +95,6 @@ GstEnginePipeline::GstEnginePipeline(GstEngine *engine)
audioscale_(nullptr),
audiosink_(nullptr) {
//qLog(Info) << __PRETTY_FUNCTION__;
if (!sElementDeleter) {
sElementDeleter = new GstElementDeleter;
}
@ -107,8 +105,6 @@ GstEnginePipeline::GstEnginePipeline(GstEngine *engine)
void GstEnginePipeline::set_output_device(const QString &sink, const QVariant &device) {
//qLog(Info) << __PRETTY_FUNCTION__ << sink << device ;
sink_ = sink;
device_ = device;
@ -116,8 +112,6 @@ void GstEnginePipeline::set_output_device(const QString &sink, const QVariant &d
void GstEnginePipeline::set_replaygain(bool enabled, int mode, float preamp, bool compression) {
//qLog(Info) << __PRETTY_FUNCTION__;
rg_enabled_ = enabled;
rg_mode_ = mode;
rg_preamp_ = preamp;
@ -138,8 +132,6 @@ void GstEnginePipeline::set_mono_playback(bool enabled) {
bool GstEnginePipeline::ReplaceDecodeBin(GstElement *new_bin) {
//qLog(Info) << __PRETTY_FUNCTION__;
if (!new_bin) return false;
// Destroy the old elements if they are set
@ -156,27 +148,34 @@ bool GstEnginePipeline::ReplaceDecodeBin(GstElement *new_bin) {
gst_bin_add(GST_BIN(pipeline_), uridecodebin_);
return true;
}
bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) {
bool GstEnginePipeline::ReplaceDecodeBin(const QUrl &url) {
//qLog(Info) << __PRETTY_FUNCTION__;
QByteArray uri;
if (url.scheme() == "cdda") {
QString str = url.toString();
str.remove(str.lastIndexOf(QChar('a')), 1);
uri = str.toLocal8Bit();;
}
else {
uri = url.toEncoded();
}
GstElement *new_bin = nullptr;
new_bin = engine_->CreateElement("uridecodebin");
g_object_set(G_OBJECT(new_bin), "uri", url.toEncoded().constData(), nullptr);
g_object_set(G_OBJECT(new_bin), "uri", uri.constData(), nullptr);
CHECKED_GCONNECT(G_OBJECT(new_bin), "drained", &SourceDrainedCallback, this);
CHECKED_GCONNECT(G_OBJECT(new_bin), "pad-added", &NewPadCallback, this);
CHECKED_GCONNECT(G_OBJECT(new_bin), "notify::source", &SourceSetupCallback, this);
return ReplaceDecodeBin(new_bin);
}
GstElement *GstEnginePipeline::CreateDecodeBinFromString(const char *pipeline) {
//qLog(Info) << __PRETTY_FUNCTION__;
GError *error = nullptr;
GstElement *bin = gst_parse_bin_from_description(pipeline, TRUE, &error);
@ -194,12 +193,11 @@ GstElement *GstEnginePipeline::CreateDecodeBinFromString(const char *pipeline) {
else {
return bin;
}
}
bool GstEnginePipeline::Init() {
//qLog(Info) << __PRETTY_FUNCTION__;
// 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
// The uri decode bin is a gstreamer builtin that automatically picks the right type of source and decoder for the URI.
@ -410,9 +408,7 @@ bool GstEnginePipeline::Init() {
}
void GstEnginePipeline::MaybeLinkDecodeToAudio() {
//qLog(Info) << __PRETTY_FUNCTION__;
if (!uridecodebin_ || !audiobin_) return;
GstPad *pad = gst_element_get_static_pad(uridecodebin_, "src");
@ -420,11 +416,10 @@ void GstEnginePipeline::MaybeLinkDecodeToAudio() {
gst_object_unref(pad);
gst_element_link(uridecodebin_, audiobin_);
}
bool GstEnginePipeline::InitFromString(const QString &pipeline) {
//qLog(Info) << __PRETTY_FUNCTION__;
pipeline_ = gst_pipeline_new("pipeline");
@ -437,20 +432,17 @@ bool GstEnginePipeline::InitFromString(const QString &pipeline) {
if (!Init()) return false;
return gst_element_link(new_bin, audiobin_);
}
bool GstEnginePipeline::InitFromUrl(const QUrl &url, qint64 end_nanosec) {
//qLog(Info) << __PRETTY_FUNCTION__;
pipeline_ = gst_pipeline_new("pipeline");
if (url.scheme() == "cdda" && !url.path().isEmpty()) {
// Currently, Gstreamer can't handle input CD devices inside cdda URL. So
// we handle them ourselve: we extract the track number and re-create an
// URL with only cdda:// + the track number (which can be handled by
// Gstreamer). We keep the device in mind, and we will set it later using
// SourceSetupCallback
// Currently, Gstreamer can't handle input CD devices inside cdda URL.
// So we handle them ourselve: we extract the track number and re-create an URL with only cdda:// + the track number (which can be handled by Gstreamer).
// We keep the device in mind, and we will set it later using SourceSetupCallback
QStringList path = url.path().split('/');
url_ = QUrl(QString("cdda://%1").arg(path.takeLast()));
source_device_ = path.join("/");
@ -469,19 +461,16 @@ bool GstEnginePipeline::InitFromUrl(const QUrl &url, qint64 end_nanosec) {
GstEnginePipeline::~GstEnginePipeline() {
//qLog(Info) << __PRETTY_FUNCTION__;
if (pipeline_) {
gst_bus_set_sync_handler(gst_pipeline_get_bus(GST_PIPELINE(pipeline_)), nullptr, nullptr, nullptr);
g_source_remove(bus_cb_id_);
gst_element_set_state(pipeline_, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(pipeline_));
}
}
gboolean GstEnginePipeline::BusCallback(GstBus*, GstMessage *msg, gpointer self) {
//qLog(Info) << __PRETTY_FUNCTION__;
GstEnginePipeline *instance = reinterpret_cast<GstEnginePipeline*>(self);
@ -509,8 +498,6 @@ gboolean GstEnginePipeline::BusCallback(GstBus*, GstMessage *msg, gpointer self)
}
GstBusSyncReply GstEnginePipeline::BusCallbackSync(GstBus*, GstMessage *msg, gpointer self) {
//qLog(Info) << __PRETTY_FUNCTION__;
GstEnginePipeline *instance = reinterpret_cast<GstEnginePipeline*>(self);
@ -562,9 +549,7 @@ GstBusSyncReply GstEnginePipeline::BusCallbackSync(GstBus*, GstMessage *msg, gpo
}
void GstEnginePipeline::StreamStatusMessageReceived(GstMessage *msg) {
//qLog(Info) << __PRETTY_FUNCTION__;
GstStreamStatusType type;
GstElement *owner;
gst_message_parse_stream_status(msg, &type, &owner);
@ -581,8 +566,6 @@ void GstEnginePipeline::StreamStatusMessageReceived(GstMessage *msg) {
void GstEnginePipeline::TaskEnterCallback(GstTask*, GThread*, gpointer) {
//qLog(Info) << __PRETTY_FUNCTION__;
// Bump the priority of the thread only on OS X
#ifdef Q_OS_DARWIN
@ -596,8 +579,6 @@ void GstEnginePipeline::TaskEnterCallback(GstTask*, GThread*, gpointer) {
}
void GstEnginePipeline::ElementMessageReceived(GstMessage *msg) {
//qLog(Info) << __PRETTY_FUNCTION__;
const GstStructure *structure = gst_message_get_structure(msg);
@ -613,8 +594,6 @@ void GstEnginePipeline::ElementMessageReceived(GstMessage *msg) {
}
void GstEnginePipeline::ErrorMessageReceived(GstMessage *msg) {
//qLog(Info) << __PRETTY_FUNCTION__;
GError *error;
gchar *debugs;
@ -628,9 +607,8 @@ void GstEnginePipeline::ErrorMessageReceived(GstMessage *msg) {
free(debugs);
if (!redirect_url_.isEmpty() && debugstr.contains("A redirect message was posted on the bus and should have been handled by the application.")) {
// mmssrc posts a message on the bus *and* makes an error message when it
// wants to do a redirect. We handle the message, but now we have to
// ignore the error too.
// mmssrc posts a message on the bus *and* makes an error message when it wants to do a redirect.
// We handle the message, but now we have to ignore the error too.
return;
}
@ -641,8 +619,6 @@ void GstEnginePipeline::ErrorMessageReceived(GstMessage *msg) {
}
void GstEnginePipeline::TagMessageReceived(GstMessage *msg) {
//qLog(Info) << __PRETTY_FUNCTION__;
GstTagList *taglist = nullptr;
gst_message_parse_tag(msg, &taglist);
@ -663,8 +639,6 @@ void GstEnginePipeline::TagMessageReceived(GstMessage *msg) {
}
QString GstEnginePipeline::ParseTag(GstTagList *list, const char *tag) const {
//qLog(Info) << __PRETTY_FUNCTION__;
gchar *data = nullptr;
bool success = gst_tag_list_get_string(list, tag, &data);
@ -680,8 +654,6 @@ QString GstEnginePipeline::ParseTag(GstTagList *list, const char *tag) const {
void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) {
//qLog(Info) << __PRETTY_FUNCTION__;
if (msg->src != GST_OBJECT(pipeline_)) {
// We only care about state changes of the whole pipeline.
return;
@ -705,8 +677,6 @@ void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) {
void GstEnginePipeline::BufferingMessageReceived(GstMessage *msg) {
//qLog(Info) << __PRETTY_FUNCTION__;
// Only handle buffering messages from the queue2 element in audiobin - not
// the one that's created automatically by uridecodebin.
if (GST_ELEMENT(GST_MESSAGE_SRC(msg)) != queue_) {
@ -745,8 +715,6 @@ void GstEnginePipeline::BufferingMessageReceived(GstMessage *msg) {
void GstEnginePipeline::NewPadCallback(GstElement*, GstPad *pad, gpointer self) {
//qLog(Info) << __PRETTY_FUNCTION__;
GstEnginePipeline *instance = reinterpret_cast<GstEnginePipeline*>(self);
GstPad *const audiopad = gst_element_get_static_pad(instance->audiobin_, "sink");
@ -777,8 +745,6 @@ void GstEnginePipeline::NewPadCallback(GstElement*, GstPad *pad, gpointer self)
}
GstPadProbeReturn GstEnginePipeline::DecodebinProbe(GstPad *pad, GstPadProbeInfo *info, gpointer data) {
//qLog(Info) << __PRETTY_FUNCTION__;
GstEnginePipeline *instance = reinterpret_cast<GstEnginePipeline*>(data);
const GstPadProbeType info_type = GST_PAD_PROBE_INFO_TYPE(info);
@ -822,8 +788,6 @@ GstPadProbeReturn GstEnginePipeline::DecodebinProbe(GstPad *pad, GstPadProbeInfo
}
GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad*, GstPadProbeInfo *info, gpointer self) {
//qLog(Info) << __PRETTY_FUNCTION__;
GstEnginePipeline *instance = reinterpret_cast<GstEnginePipeline*>(self);
GstBuffer *buf = gst_pad_probe_info_get_buffer(info);
@ -888,8 +852,6 @@ GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad*, GstPadProbeInfo *i
}
GstPadProbeReturn GstEnginePipeline::EventHandoffCallback(GstPad*, GstPadProbeInfo *info, gpointer self) {
//qLog(Info) << __PRETTY_FUNCTION__;
GstEnginePipeline *instance = reinterpret_cast<GstEnginePipeline*>(self);
GstEvent *e = gst_pad_probe_info_get_event(info);
@ -918,8 +880,6 @@ GstPadProbeReturn GstEnginePipeline::EventHandoffCallback(GstPad*, GstPadProbeIn
void GstEnginePipeline::SourceDrainedCallback(GstURIDecodeBin *bin, gpointer self) {
//qLog(Info) << __PRETTY_FUNCTION__;
GstEnginePipeline *instance = reinterpret_cast<GstEnginePipeline*>(self);
if (instance->has_next_valid_url()) {
@ -929,8 +889,6 @@ void GstEnginePipeline::SourceDrainedCallback(GstURIDecodeBin *bin, gpointer sel
}
void GstEnginePipeline::SourceSetupCallback(GstURIDecodeBin *bin, GParamSpec *pspec, gpointer self) {
//qLog(Info) << __PRETTY_FUNCTION__;
GstEnginePipeline *instance = reinterpret_cast<GstEnginePipeline*>(self);
GstElement *element;
@ -940,10 +898,8 @@ void GstEnginePipeline::SourceSetupCallback(GstURIDecodeBin *bin, GParamSpec *ps
}
if (g_object_class_find_property(G_OBJECT_GET_CLASS(element), "device") && !instance->source_device().isEmpty()) {
// Gstreamer is not able to handle device in URL (refering to Gstreamer
// documentation, this might be added in the future). Despite that, for now
// we include device inside URL: we decompose it during Init and set device
// here, when this callback is called.
// Gstreamer is not able to handle device in URL (refering to Gstreamer documentation, this might be added in the future).
// Despite that, for now we include device inside URL: we decompose it during Init and set device here, when this callback is called.
g_object_set(element, "device", instance->source_device().toLocal8Bit().constData(), nullptr);
}
@ -960,8 +916,6 @@ void GstEnginePipeline::SourceSetupCallback(GstURIDecodeBin *bin, GParamSpec *ps
}
void GstEnginePipeline::TransitionToNext() {
//qLog(Info) << __PRETTY_FUNCTION__;
GstElement *old_decode_bin = uridecodebin_;
@ -977,13 +931,11 @@ void GstEnginePipeline::TransitionToNext() {
next_beginning_offset_nanosec_ = 0;
next_end_offset_nanosec_ = 0;
// This function gets called when the source has been drained, even if the
// song hasn't finished playing yet. We'll get a new stream when it really
// does finish, so emit TrackEnded then.
// This function gets called when the source has been drained, even if the song hasn't finished playing yet.
// We'll get a new stream when it really does finish, so emit TrackEnded then.
emit_track_ended_on_stream_start_ = true;
// This has to happen *after* the gst_element_set_state on the new bin to
// fix an occasional race condition deadlock.
// This has to happen *after* the gst_element_set_state on the new bin to fix an occasional race condition deadlock.
sElementDeleter->DeleteElementLater(old_decode_bin);
ignore_tags_ = false;
@ -1041,8 +993,6 @@ bool GstEnginePipeline::Seek(qint64 nanosec) {
}
void GstEnginePipeline::SetEqualizerEnabled(bool enabled) {
//qLog(Info) << __PRETTY_FUNCTION__ << enabled;
eq_enabled_ = enabled;
UpdateEqualizer();
@ -1115,16 +1065,14 @@ void GstEnginePipeline::StartFader(qint64 duration_nanosec, QTimeLine::Direction
const int duration_msec = duration_nanosec / kNsecPerMsec;
// If there's already another fader running then start from the same time
// that one was already at.
// If there's already another fader running then start from the same time that one was already at.
int start_time = direction == QTimeLine::Forward ? 0 : duration_msec;
if (fader_ && fader_->state() == QTimeLine::Running) {
if (duration_msec == fader_->duration()) {
start_time = fader_->currentTime();
}
else {
// Calculate the position in the new fader with the same value from
// the old fader, so no volume jumps appear
// Calculate the position in the new fader with the same value from the old fader, so no volume jumps appear
qreal time = qreal(duration_msec) * (qreal(fader_->currentTime()) / qreal(fader_->duration()));
start_time = qRound(time);
}
@ -1149,16 +1097,13 @@ void GstEnginePipeline::FaderTimelineFinished() {
fader_.reset();
// Wait a little while longer before emitting the finished signal (and
// probably distroying the pipeline) to account for delays in the audio
// server/driver.
// Wait a little while longer before emitting the finished signal (and probably distroying the pipeline) to account for delays in the audio server/driver.
if (use_fudge_timer_) {
fader_fudge_timer_.start(kFaderFudgeMsec, this);
}
else {
// Even here we cannot emit the signal directly, as it result in a
// stutter when resuming playback. So use a quest small time, so you
// won't notice the difference when resuming playback
// Even here we cannot emit the signal directly, as it result in a stutter when resuming playback.
// So use a quest small time, so you won't notice the difference when resuming playback
// (You get here when the pause fading is active)
fader_fudge_timer_.start(250, this);
}
@ -1191,9 +1136,10 @@ void GstEnginePipeline::RemoveAllBufferConsumers() {
buffer_consumers_.clear();
}
void GstEnginePipeline::SetNextUrl(const QUrl& url, qint64 beginning_nanosec, qint64 end_nanosec) {
void GstEnginePipeline::SetNextUrl(const QUrl &url, qint64 beginning_nanosec, qint64 end_nanosec) {
next_url_ = url;
next_beginning_offset_nanosec_ = beginning_nanosec;
next_end_offset_nanosec_ = end_nanosec;
}
}