Improve contextview and engine code
This commit is contained in:
parent
15a2ccc21e
commit
121a186160
@ -82,7 +82,8 @@ ContextView::ContextView(QWidget *parent) :
|
||||
timeline_fade_(new QTimeLine(1000, this)),
|
||||
image_strawberry_(":/pictures/strawberry.png"),
|
||||
active_(false),
|
||||
downloading_covers_(false)
|
||||
downloading_covers_(false),
|
||||
lyrics_id_(-1)
|
||||
{
|
||||
|
||||
ui_->setupUi(this);
|
||||
@ -173,10 +174,10 @@ void ContextView::Playing() {}
|
||||
void ContextView::Stopped() {
|
||||
|
||||
active_ = false;
|
||||
song_playing_ = song_empty_;
|
||||
song_ = song_empty_;
|
||||
song_playing_ = Song();
|
||||
song_ = Song();
|
||||
downloading_covers_ = false;
|
||||
prev_artist_ = QString();
|
||||
song_prev_ = Song();
|
||||
lyrics_ = QString();
|
||||
SetImage(image_strawberry_);
|
||||
|
||||
@ -190,21 +191,40 @@ void ContextView::UpdateNoSong() {
|
||||
|
||||
void ContextView::SongChanged(const Song &song) {
|
||||
|
||||
image_previous_ = image_original_;
|
||||
prev_artist_ = song_playing_.artist();
|
||||
lyrics_ = song.lyrics();
|
||||
song_playing_ = song;
|
||||
song_ = song;
|
||||
UpdateSong();
|
||||
update();
|
||||
if (action_show_lyrics_->isChecked()) lyrics_fetcher_->Search(song.artist(), song.album(), song.title());
|
||||
if (song_playing_.is_valid() && song.id() == song_playing_.id() && song.url() == song_playing_.url()) {
|
||||
UpdateSong(song);
|
||||
}
|
||||
else {
|
||||
song_prev_ = song_playing_;
|
||||
lyrics_ = song.lyrics();
|
||||
lyrics_id_ = -1;
|
||||
song_playing_ = song;
|
||||
song_ = song;
|
||||
SetSong(song);
|
||||
if (lyrics_.isEmpty() && action_show_lyrics_->isChecked()) {
|
||||
lyrics_fetcher_->Clear();
|
||||
lyrics_id_ = lyrics_fetcher_->Search(song.artist(), song.album(), song.title());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ContextView::SetText(QLabel *label, int value, const QString &suffix, const QString &def) {
|
||||
void ContextView::SetLabelEnabled(QLabel *label) {
|
||||
label->setEnabled(true);
|
||||
label->setVisible(true);
|
||||
label->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
}
|
||||
|
||||
void ContextView::SetLabelText(QLabel *label, int value, const QString &suffix, const QString &def) {
|
||||
label->setText(value <= 0 ? def : (QString::number(value) + " " + suffix));
|
||||
}
|
||||
|
||||
void ContextView::SetLabelDisabled(QLabel *label) {
|
||||
label->setEnabled(false);
|
||||
label->setVisible(false);
|
||||
label->setMaximumSize(0, 0);
|
||||
}
|
||||
|
||||
void ContextView::NoSong() {
|
||||
|
||||
ui_->label_stop_top->setStyleSheet(
|
||||
@ -232,7 +252,7 @@ void ContextView::NoSong() {
|
||||
|
||||
}
|
||||
|
||||
void ContextView::UpdateSong() {
|
||||
void ContextView::SetSong(const Song &song) {
|
||||
|
||||
QList <QLabel *> labels_play_data;
|
||||
|
||||
@ -251,85 +271,58 @@ void ContextView::UpdateSong() {
|
||||
"font: 11pt;"
|
||||
"font-weight: regular;"
|
||||
);
|
||||
ui_->label_play_top->setText( QString("<b>%1 - %2</b><br/>%3").arg(song_.PrettyTitle().toHtmlEscaped(), song_.artist().toHtmlEscaped(), song_.album().toHtmlEscaped()));
|
||||
ui_->label_play_top->setText( QString("<b>%1 - %2</b><br/>%3").arg(song.PrettyTitle().toHtmlEscaped(), song.artist().toHtmlEscaped(), song.album().toHtmlEscaped()));
|
||||
|
||||
if (action_show_data_->isChecked()) {
|
||||
for (QLabel *l : labels_play_data) {
|
||||
l->setEnabled(true);
|
||||
l->setVisible(true);
|
||||
l->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
}
|
||||
ui_->layout_play_data->setEnabled(true);
|
||||
ui_->filetype->setText(song_.TextForFiletype());
|
||||
ui_->length->setText(Utilities::PrettyTimeNanosec(song_.length_nanosec()));
|
||||
if (song_.samplerate() <= 0) {
|
||||
ui_->label_samplerate->setEnabled(false);
|
||||
ui_->label_samplerate->setVisible(false);
|
||||
ui_->label_samplerate->setMaximumSize(0, 0);
|
||||
ui_->samplerate->setEnabled(false);
|
||||
ui_->samplerate->setVisible(false);
|
||||
ui_->samplerate->setMaximumSize(0, 0);
|
||||
SetLabelEnabled(ui_->label_filetype);
|
||||
SetLabelEnabled(ui_->filetype);
|
||||
SetLabelEnabled(ui_->label_length);
|
||||
SetLabelEnabled(ui_->length);
|
||||
ui_->filetype->setText(song.TextForFiletype());
|
||||
ui_->length->setText(Utilities::PrettyTimeNanosec(song.length_nanosec()));
|
||||
if (song.samplerate() <= 0) {
|
||||
SetLabelDisabled(ui_->label_samplerate);
|
||||
SetLabelDisabled(ui_->samplerate);
|
||||
ui_->samplerate->clear();
|
||||
}
|
||||
else {
|
||||
ui_->label_samplerate->setEnabled(true);
|
||||
ui_->label_samplerate->setVisible(true);
|
||||
ui_->label_samplerate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
ui_->samplerate->setEnabled(true);
|
||||
ui_->samplerate->setVisible(true);
|
||||
ui_->samplerate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
SetText(ui_->samplerate, song_.samplerate(), "Hz");
|
||||
SetLabelEnabled(ui_->label_samplerate);
|
||||
SetLabelEnabled(ui_->samplerate);
|
||||
SetLabelText(ui_->samplerate, song.samplerate(), "Hz");
|
||||
}
|
||||
if (song_.bitdepth() <= 0) {
|
||||
ui_->label_bitdepth->setEnabled(false);
|
||||
ui_->label_bitdepth->setVisible(false);
|
||||
ui_->label_bitdepth->setMaximumSize(0, 0);
|
||||
ui_->bitdepth->setEnabled(false);
|
||||
ui_->bitdepth->setVisible(false);
|
||||
ui_->bitdepth->setMaximumSize(0, 0);
|
||||
if (song.bitdepth() <= 0) {
|
||||
SetLabelDisabled(ui_->label_bitdepth);
|
||||
SetLabelDisabled(ui_->bitdepth);
|
||||
ui_->bitdepth->clear();
|
||||
}
|
||||
else {
|
||||
ui_->label_bitdepth->setEnabled(true);
|
||||
ui_->label_bitdepth->setVisible(true);
|
||||
ui_->label_bitdepth->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
ui_->bitdepth->setEnabled(true);
|
||||
ui_->bitdepth->setVisible(true);
|
||||
ui_->bitdepth->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
SetText(ui_->bitdepth, song_.bitdepth(), "Bit");
|
||||
SetLabelEnabled(ui_->label_bitdepth);
|
||||
SetLabelEnabled(ui_->bitdepth);
|
||||
SetLabelText(ui_->bitdepth, song.bitdepth(), "Bit");
|
||||
}
|
||||
if (song_.bitrate() <= 0) {
|
||||
ui_->label_bitrate->setEnabled(false);
|
||||
ui_->label_bitrate->setVisible(false);
|
||||
ui_->label_bitrate->setMaximumSize(0, 0);
|
||||
ui_->bitrate->setEnabled(false);
|
||||
ui_->bitrate->setVisible(false);
|
||||
ui_->bitrate->setMaximumSize(0, 0);
|
||||
if (song.bitrate() <= 0) {
|
||||
SetLabelDisabled(ui_->label_bitrate);
|
||||
SetLabelDisabled(ui_->bitrate);
|
||||
ui_->bitrate->clear();
|
||||
}
|
||||
else {
|
||||
ui_->label_bitrate->setEnabled(true);
|
||||
ui_->label_bitrate->setVisible(true);
|
||||
ui_->label_bitrate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
ui_->bitrate->setEnabled(true);
|
||||
ui_->bitrate->setVisible(true);
|
||||
ui_->bitrate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
|
||||
SetText(ui_->bitrate, song_.bitrate(), tr("kbps"));
|
||||
SetLabelEnabled(ui_->label_bitrate);
|
||||
SetLabelEnabled(ui_->bitrate);
|
||||
SetLabelText(ui_->bitrate, song.bitrate(), tr("kbps"));
|
||||
}
|
||||
ui_->spacer_play_data->changeSize(20, 20, QSizePolicy::Fixed);
|
||||
}
|
||||
else {
|
||||
for (QLabel *l : labels_play_data) {
|
||||
l->setEnabled(false);
|
||||
l->setVisible(false);
|
||||
l->setMaximumSize(0, 0);
|
||||
}
|
||||
ui_->layout_play_data->setEnabled(false);
|
||||
ui_->filetype->clear();
|
||||
ui_->length->clear();
|
||||
ui_->samplerate->clear();
|
||||
ui_->bitdepth->clear();
|
||||
ui_->bitrate->clear();
|
||||
for (QLabel *l : labels_play_data) {
|
||||
SetLabelDisabled(l);
|
||||
}
|
||||
ui_->layout_play_data->setEnabled(false);
|
||||
ui_->spacer_play_data->changeSize(0, 0, QSizePolicy::Fixed);
|
||||
}
|
||||
|
||||
@ -403,18 +396,18 @@ void ContextView::UpdateSong() {
|
||||
ui_->device->setMaximumSize(0, 0);
|
||||
}
|
||||
|
||||
if (action_show_albums_->isChecked() && prev_artist_ != song_.artist()) {
|
||||
if (action_show_albums_->isChecked() && song_prev_.artist() != song.artist()) {
|
||||
const QueryOptions opt;
|
||||
CollectionBackend::AlbumList albumlist;
|
||||
ui_->widget_play_albums->albums_model()->Reset();
|
||||
albumlist = app_->collection_backend()->GetAlbumsByArtist(song_.artist(), opt);
|
||||
albumlist = app_->collection_backend()->GetAlbumsByArtist(song.artist(), opt);
|
||||
if (albumlist.count() > 1) {
|
||||
ui_->label_play_albums->setVisible(true);
|
||||
ui_->label_play_albums->setMinimumSize(0, 20);
|
||||
ui_->label_play_albums->setText(QString("<b>Albums by %1</b>").arg( song_.artist().toHtmlEscaped()));
|
||||
ui_->label_play_albums->setText(QString("<b>Albums by %1</b>").arg( song.artist().toHtmlEscaped()));
|
||||
ui_->label_play_albums->setStyleSheet("background-color: #3DADE8; color: rgb(255, 255, 255); font: 11pt;");
|
||||
for (CollectionBackend::Album album : albumlist) {
|
||||
SongList songs = app_->collection_backend()->GetSongs(song_.artist(), album.album_name, opt);
|
||||
SongList songs = app_->collection_backend()->GetSongs(song.artist(), album.album_name, opt);
|
||||
ui_->widget_play_albums->albums_model()->AddSongs(songs);
|
||||
}
|
||||
ui_->widget_play_albums->setEnabled(true);
|
||||
@ -454,9 +447,62 @@ void ContextView::UpdateSong() {
|
||||
|
||||
}
|
||||
|
||||
void ContextView::UpdateSong(const Song &song) {
|
||||
|
||||
if (song.artist() != song_playing_.artist() || song.album() != song_playing_.album() || song.title() != song_playing_.title()) {
|
||||
ui_->label_play_top->setText( QString("<b>%1 - %2</b><br/>%3").arg(song.PrettyTitle().toHtmlEscaped(), song.artist().toHtmlEscaped(), song.album().toHtmlEscaped()));
|
||||
}
|
||||
|
||||
if (action_show_data_->isChecked()) {
|
||||
if (song.filetype() != song_playing_.filetype()) ui_->filetype->setText(song.TextForFiletype());
|
||||
if (song.length_nanosec() != song_playing_.length_nanosec()) ui_->label_length->setText(Utilities::PrettyTimeNanosec(song.length_nanosec()));
|
||||
if (song.samplerate() != song_playing_.samplerate()) {
|
||||
if (song.samplerate() <= 0) {
|
||||
SetLabelDisabled(ui_->label_samplerate);
|
||||
SetLabelDisabled(ui_->samplerate);
|
||||
ui_->samplerate->clear();
|
||||
}
|
||||
else {
|
||||
SetLabelEnabled(ui_->label_samplerate);
|
||||
SetLabelEnabled(ui_->samplerate);
|
||||
SetLabelText(ui_->samplerate, song.samplerate(), "Hz");
|
||||
}
|
||||
}
|
||||
if (song.bitdepth() != song_playing_.bitdepth()) {
|
||||
if (song.bitdepth() <= 0) {
|
||||
SetLabelDisabled(ui_->label_bitdepth);
|
||||
SetLabelDisabled(ui_->bitdepth);
|
||||
ui_->bitdepth->clear();
|
||||
}
|
||||
else {
|
||||
SetLabelEnabled(ui_->label_bitdepth);
|
||||
SetLabelEnabled(ui_->bitdepth);
|
||||
SetLabelText(ui_->bitdepth, song.bitdepth(), "Bit");
|
||||
}
|
||||
}
|
||||
if (song.bitrate() != song_playing_.bitrate()) {
|
||||
if (song.bitrate() <= 0) {
|
||||
SetLabelDisabled(ui_->label_bitrate);
|
||||
SetLabelDisabled(ui_->bitrate);
|
||||
ui_->bitrate->clear();
|
||||
}
|
||||
else {
|
||||
SetLabelEnabled(ui_->label_bitrate);
|
||||
SetLabelEnabled(ui_->bitrate);
|
||||
SetLabelText(ui_->bitrate, song.bitrate(), tr("kbps"));
|
||||
}
|
||||
}
|
||||
}
|
||||
song_playing_ = song;
|
||||
song_ = song;
|
||||
|
||||
}
|
||||
|
||||
void ContextView::UpdateLyrics(quint64 id, const QString lyrics) {
|
||||
|
||||
if (id != lyrics_id_) return;
|
||||
lyrics_ = lyrics;
|
||||
lyrics_id_ = -1;
|
||||
if (action_show_lyrics_->isChecked()) {
|
||||
ui_->label_play_lyrics->setText(lyrics);
|
||||
}
|
||||
@ -551,6 +597,7 @@ void ContextView::ScaleCover() {
|
||||
|
||||
void ContextView::AlbumArtLoaded(const Song &song, const QString&, const QImage &image) {
|
||||
|
||||
if (song.id() != song_playing_.id() || song.url() != song_playing_.url()) return;
|
||||
if (song.effective_albumartist() != song_playing_.effective_albumartist() || song.effective_album() != song_playing_.effective_album() || song.title() != song_playing_.title()) return;
|
||||
if (image == image_original_) return;
|
||||
|
||||
@ -573,6 +620,7 @@ void ContextView::SetImage(const QImage &image) {
|
||||
DrawImage(&p);
|
||||
p.end();
|
||||
|
||||
image_previous_ = image_original_;
|
||||
image_original_ = image;
|
||||
|
||||
ScaleCover();
|
||||
@ -624,7 +672,7 @@ void ContextView::ActionShowData() {
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("show_data", action_show_data_->isChecked());
|
||||
s.endGroup();
|
||||
UpdateSong();
|
||||
SetSong(song_);
|
||||
}
|
||||
|
||||
void ContextView::ActionShowOutput() {
|
||||
@ -632,7 +680,7 @@ void ContextView::ActionShowOutput() {
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("show_output", action_show_output_->isChecked());
|
||||
s.endGroup();
|
||||
UpdateSong();
|
||||
SetSong(song_);
|
||||
}
|
||||
|
||||
void ContextView::ActionShowAlbums() {
|
||||
@ -640,8 +688,8 @@ void ContextView::ActionShowAlbums() {
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("show_albums", action_show_albums_->isChecked());
|
||||
s.endGroup();
|
||||
prev_artist_ = QString();
|
||||
UpdateSong();
|
||||
song_prev_ = Song();
|
||||
SetSong(song_);
|
||||
}
|
||||
|
||||
void ContextView::ActionShowLyrics() {
|
||||
@ -649,8 +697,11 @@ void ContextView::ActionShowLyrics() {
|
||||
s.beginGroup(kSettingsGroup);
|
||||
s.setValue("show_lyrics", action_show_lyrics_->isChecked());
|
||||
s.endGroup();
|
||||
UpdateSong();
|
||||
if (lyrics_.isEmpty() && action_show_lyrics_->isChecked()) lyrics_fetcher_->Search(song_.artist(), song_.album(), song_.title());
|
||||
SetSong(song_);
|
||||
if (lyrics_.isEmpty() && action_show_lyrics_->isChecked()) {
|
||||
lyrics_fetcher_->Clear();
|
||||
lyrics_id_ = lyrics_fetcher_->Search(song_.artist(), song_.album(), song_.title());
|
||||
}
|
||||
}
|
||||
|
||||
void ContextView::SearchCoverAutomatically() {
|
||||
|
@ -94,21 +94,23 @@ class ContextView : public QWidget {
|
||||
AlbumCoverLoaderOptions cover_loader_options_;
|
||||
Song song_;
|
||||
Song song_playing_;
|
||||
Song song_empty_;
|
||||
Song song_prev_;
|
||||
QImage image_original_;
|
||||
QImage image_previous_;
|
||||
QPixmap pixmap_current_;
|
||||
QPixmap pixmap_previous_;
|
||||
qreal pixmap_previous_opacity_;
|
||||
std::unique_ptr<QMovie> spinner_animation_;
|
||||
|
||||
QString prev_artist_;
|
||||
qint64 lyrics_id_;
|
||||
QString lyrics_;
|
||||
|
||||
void AddActions();
|
||||
void SetText(QLabel *label, int value, const QString &suffix, const QString &def = QString());
|
||||
void SetLabelEnabled(QLabel *label);
|
||||
void SetLabelDisabled(QLabel *label);
|
||||
void SetLabelText(QLabel *label, int value, const QString &suffix, const QString &def = QString());
|
||||
void NoSong();
|
||||
void UpdateSong();
|
||||
void SetSong(const Song &song);
|
||||
void UpdateSong(const Song &song);
|
||||
void SetImage(const QImage &image);
|
||||
void DrawImage(QPainter *p);
|
||||
void ScaleCover();
|
||||
|
@ -831,8 +831,8 @@ void MainWindow::MediaStopped() {
|
||||
tray_icon_->SetProgress(0);
|
||||
tray_icon_->SetStopped();
|
||||
|
||||
song_playing_ = song_empty_;
|
||||
song_ = song_empty_;
|
||||
song_playing_ = Song();
|
||||
song_ = Song();
|
||||
image_original_ = QImage();
|
||||
|
||||
}
|
||||
|
@ -389,7 +389,6 @@ signals:
|
||||
|
||||
Song song_;
|
||||
Song song_playing_;
|
||||
Song song_empty_;
|
||||
QImage image_original_;
|
||||
|
||||
};
|
||||
|
@ -246,7 +246,7 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
|
||||
item->SetTemporaryMetadata(song);
|
||||
app_->playlist_manager()->active()->InformOfCurrentSongChange();
|
||||
}
|
||||
engine_->Play(result.media_url_, stream_change_type_, item->Metadata().has_cue(), item->Metadata().beginning_nanosec(), item->Metadata().end_nanosec());
|
||||
engine_->Play(result.media_url_, song.url(), stream_change_type_, item->Metadata().has_cue(), item->Metadata().beginning_nanosec(), item->Metadata().end_nanosec());
|
||||
|
||||
current_item_ = item;
|
||||
loading_async_ = QUrl();
|
||||
@ -507,8 +507,7 @@ void Player::PlayAt(int index, Engine::TrackChangeFlags change, bool reshuffle)
|
||||
}
|
||||
else {
|
||||
loading_async_ = QUrl();
|
||||
engine_->Play(current_item_->Url(), change, current_item_->Metadata().has_cue(), current_item_->Metadata().beginning_nanosec(), current_item_->Metadata().end_nanosec());
|
||||
|
||||
engine_->Play(current_item_->Url(), current_item_->Url(), change, current_item_->Metadata().has_cue(), current_item_->Metadata().beginning_nanosec(), current_item_->Metadata().end_nanosec());
|
||||
}
|
||||
|
||||
}
|
||||
@ -550,7 +549,7 @@ void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle &bundle) {
|
||||
PlaylistItemPtr item = app_->playlist_manager()->active()->current_item();
|
||||
if (!item) return;
|
||||
|
||||
if (item->Metadata().url() != bundle.url) return;
|
||||
if (bundle.url != item->Metadata().url()) return;
|
||||
|
||||
Engine::SimpleMetaBundle bundle_copy = bundle;
|
||||
|
||||
@ -627,6 +626,8 @@ void Player::TogglePrettyOSD() {
|
||||
|
||||
void Player::TrackAboutToEnd() {
|
||||
|
||||
qLog(Debug) << __PRETTY_FUNCTION__;
|
||||
|
||||
// If the current track was from a URL handler then it might have special behaviour to queue up a subsequent track.
|
||||
// We don't want to preload (and scrobble) the next item in the playlist if it's just going to be stopped again immediately after.
|
||||
if (app_->playlist_manager()->active()->current_item()) {
|
||||
@ -679,7 +680,7 @@ void Player::TrackAboutToEnd() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
engine_->StartPreloading(url, next_item->Metadata().has_cue(), next_item->Metadata().beginning_nanosec(), next_item->Metadata().end_nanosec());
|
||||
engine_->StartPreloading(url, next_item->Url(), next_item->Metadata().has_cue(), next_item->Metadata().beginning_nanosec(), next_item->Metadata().end_nanosec());
|
||||
|
||||
}
|
||||
|
||||
@ -700,7 +701,7 @@ void Player::InvalidSongRequested(const QUrl &url) {
|
||||
}
|
||||
|
||||
void Player::RegisterUrlHandler(UrlHandler *handler) {
|
||||
|
||||
|
||||
const QString scheme = handler->scheme();
|
||||
|
||||
if (url_handlers_.contains(scheme)) {
|
||||
@ -716,7 +717,7 @@ void Player::RegisterUrlHandler(UrlHandler *handler) {
|
||||
}
|
||||
|
||||
void Player::UnregisterUrlHandler(UrlHandler *handler) {
|
||||
|
||||
|
||||
const QString scheme = url_handlers_.key(handler);
|
||||
if (scheme.isEmpty()) {
|
||||
qLog(Warning) << "Tried to unregister a URL handler for" << handler->scheme() << "that wasn't registered";
|
||||
|
@ -996,12 +996,12 @@ void Song::MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle &bundle) {
|
||||
if (!bundle.album.isEmpty()) d->album_ = bundle.album;
|
||||
if (!bundle.comment.isEmpty()) d->comment_ = bundle.comment;
|
||||
if (!bundle.genre.isEmpty()) d->genre_ = bundle.genre;
|
||||
if (!bundle.bitrate.isEmpty()) d->bitrate_ = bundle.bitrate.toInt();
|
||||
if (!bundle.samplerate.isEmpty()) d->samplerate_ = bundle.samplerate.toInt();
|
||||
if (!bundle.bitdepth.isEmpty()) d->samplerate_ = bundle.bitdepth.toInt();
|
||||
if (!bundle.length.isEmpty()) set_length_nanosec(bundle.length.toLongLong());
|
||||
if (!bundle.year.isEmpty()) d->year_ = bundle.year.toInt();
|
||||
if (!bundle.tracknr.isEmpty()) d->track_ = bundle.tracknr.toInt();
|
||||
if (bundle.bitrate > 0) d->bitrate_ = bundle.bitrate;
|
||||
if (bundle.samplerate > 0) d->samplerate_ = bundle.samplerate;
|
||||
if (bundle.bitdepth > 0) d->samplerate_ = bundle.bitdepth;
|
||||
if (bundle.length > 0) set_length_nanosec(bundle.length);
|
||||
if (bundle.year > 0) d->year_ = bundle.year;
|
||||
if (bundle.tracknr > 0) d->track_ = bundle.tracknr;
|
||||
|
||||
}
|
||||
|
||||
|
@ -308,7 +308,6 @@ class Song {
|
||||
|
||||
void set_image(const QImage &i);
|
||||
|
||||
|
||||
// Comparison functions
|
||||
bool IsMetadataEqual(const Song &other) const;
|
||||
bool IsOnSameAlbum(const Song &other) const;
|
||||
|
@ -60,11 +60,12 @@ Engine::Base::Base()
|
||||
|
||||
Engine::Base::~Base() {}
|
||||
|
||||
bool Engine::Base::Load(const QUrl &url, TrackChangeFlags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
bool Engine::Base::Load(const QUrl &media_url, const QUrl &original_url, TrackChangeFlags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
|
||||
Q_UNUSED(force_stop_at_end);
|
||||
|
||||
url_ = url;
|
||||
media_url_ = media_url;
|
||||
original_url_ = original_url;
|
||||
beginning_nanosec_ = beginning_nanosec;
|
||||
end_nanosec_ = end_nanosec;
|
||||
|
||||
@ -73,12 +74,13 @@ bool Engine::Base::Load(const QUrl &url, TrackChangeFlags, bool force_stop_at_en
|
||||
|
||||
}
|
||||
|
||||
bool Engine::Base::Play(const QUrl &url, TrackChangeFlags flags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
bool Engine::Base::Play(const QUrl &media_url, const QUrl &original_url, TrackChangeFlags flags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
|
||||
if (!Load(url, flags, force_stop_at_end, beginning_nanosec, end_nanosec))
|
||||
if (!Load(media_url, original_url, flags, force_stop_at_end, beginning_nanosec, end_nanosec))
|
||||
return false;
|
||||
|
||||
return Play(0);
|
||||
|
||||
}
|
||||
|
||||
void Engine::Base::SetVolume(uint value) {
|
||||
|
@ -68,8 +68,8 @@ public:
|
||||
|
||||
virtual bool Init() = 0;
|
||||
virtual State state() const = 0;
|
||||
virtual void StartPreloading(const QUrl&, bool, qint64, qint64) {}
|
||||
virtual bool Load(const QUrl &url, TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
|
||||
virtual void StartPreloading(const QUrl &media_url, const QUrl &original_url, bool, qint64, qint64) {}
|
||||
virtual bool Load(const QUrl &media_url, const QUrl &original_url, TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
|
||||
virtual bool Play(quint64 offset_nanosec) = 0;
|
||||
virtual void Stop(bool stop_after = false) = 0;
|
||||
virtual void Pause() = 0;
|
||||
@ -97,7 +97,7 @@ public:
|
||||
|
||||
// Plays a media stream represented with the URL 'u' from the given 'beginning' to the given 'end' (usually from 0 to a song's length).
|
||||
// Both markers should be passed in nanoseconds. 'end' can be negative, indicating that the real length of 'u' stream is unknown.
|
||||
bool Play(const QUrl &u, TrackChangeFlags c, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
|
||||
bool Play(const QUrl &media_url, const QUrl &original_url, TrackChangeFlags c, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
|
||||
void SetVolume(uint value);
|
||||
static uint MakeVolumeLogarithmic(uint volume);
|
||||
|
||||
@ -164,7 +164,8 @@ protected:
|
||||
uint volume_;
|
||||
quint64 beginning_nanosec_;
|
||||
qint64 end_nanosec_;
|
||||
QUrl url_;
|
||||
QUrl media_url_;
|
||||
QUrl original_url_;
|
||||
Scope scope_;
|
||||
bool buffering_;
|
||||
bool equalizer_enabled_;
|
||||
@ -209,12 +210,13 @@ struct SimpleMetaBundle {
|
||||
QString album;
|
||||
QString comment;
|
||||
QString genre;
|
||||
QString bitrate;
|
||||
QString samplerate;
|
||||
QString bitdepth;
|
||||
QString length;
|
||||
QString year;
|
||||
QString tracknr;
|
||||
qlonglong length;
|
||||
int year;
|
||||
int tracknr;
|
||||
int samplerate;
|
||||
int bitdepth;
|
||||
qlonglong bitrate;
|
||||
QString lyrics;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
@ -137,7 +137,7 @@ bool GstEngine::Init() {
|
||||
|
||||
Engine::State GstEngine::state() const {
|
||||
|
||||
if (!current_pipeline_) return url_.isEmpty() ? Engine::Empty : Engine::Idle;
|
||||
if (!current_pipeline_) return media_url_.isEmpty() ? Engine::Empty : Engine::Idle;
|
||||
|
||||
switch (current_pipeline_->state()) {
|
||||
case GST_STATE_NULL:
|
||||
@ -154,32 +154,32 @@ Engine::State GstEngine::state() const {
|
||||
|
||||
}
|
||||
|
||||
void GstEngine::StartPreloading(const QUrl &url, bool force_stop_at_end, qint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
void GstEngine::StartPreloading(const QUrl &media_url, const QUrl &original_url, bool force_stop_at_end, qint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
|
||||
EnsureInitialised();
|
||||
|
||||
QByteArray gst_url = FixupUrl(url);
|
||||
QByteArray gst_url = FixupUrl(media_url);
|
||||
|
||||
// No crossfading, so we can just queue the new URL in the existing pipeline and get gapless playback (hopefully)
|
||||
if (current_pipeline_)
|
||||
current_pipeline_->SetNextUrl(gst_url, beginning_nanosec, force_stop_at_end ? end_nanosec : 0);
|
||||
current_pipeline_->SetNextUrl(gst_url, original_url, beginning_nanosec, force_stop_at_end ? end_nanosec : 0);
|
||||
|
||||
}
|
||||
|
||||
bool GstEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
bool GstEngine::Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
|
||||
EnsureInitialised();
|
||||
|
||||
Engine::Base::Load(url, change, force_stop_at_end, beginning_nanosec, end_nanosec);
|
||||
Engine::Base::Load(media_url, original_url, change, force_stop_at_end, beginning_nanosec, end_nanosec);
|
||||
|
||||
QByteArray gst_url = FixupUrl(url);
|
||||
QByteArray gst_url = FixupUrl(media_url);
|
||||
|
||||
bool crossfade = current_pipeline_ && ((crossfade_enabled_ && change & Engine::Manual) || (autocrossfade_enabled_ && change & Engine::Auto) || ((crossfade_enabled_ || autocrossfade_enabled_) && change & Engine::Intro));
|
||||
|
||||
if (change & Engine::Auto && change & Engine::SameAlbum && !crossfade_same_album_)
|
||||
crossfade = false;
|
||||
|
||||
if (!crossfade && current_pipeline_ && current_pipeline_->url() == gst_url && change & Engine::Auto) {
|
||||
if (!crossfade && current_pipeline_ && current_pipeline_->media_url() == gst_url && change & Engine::Auto) {
|
||||
// We're not crossfading, and the pipeline is already playing the URI we want, so just do nothing.
|
||||
return true;
|
||||
}
|
||||
@ -188,7 +188,7 @@ bool GstEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool forc
|
||||
//SetEqualizerParameters(equalizer_preamp_, equalizer_gains_);
|
||||
//SetStereoBalance(stereo_balance_);
|
||||
|
||||
shared_ptr<GstEnginePipeline> pipeline = CreatePipeline(gst_url, force_stop_at_end ? end_nanosec : 0);
|
||||
shared_ptr<GstEnginePipeline> pipeline = CreatePipeline(gst_url, original_url, force_stop_at_end ? end_nanosec : 0);
|
||||
if (!pipeline) return false;
|
||||
|
||||
if (crossfade) StartFadeout();
|
||||
@ -229,7 +229,8 @@ void GstEngine::Stop(bool stop_after) {
|
||||
|
||||
StopTimers();
|
||||
|
||||
url_ = QUrl(); // To ensure we return Empty from state()
|
||||
media_url_ = QUrl(); // To ensure we return Empty from state()
|
||||
original_url_ = QUrl();
|
||||
beginning_nanosec_ = end_nanosec_ = 0;
|
||||
|
||||
// Check if we started a fade out. If it isn't finished yet and the user pressed stop, we cancel the fader and just stop the playback.
|
||||
@ -575,7 +576,7 @@ void GstEngine::HandlePipelineError(int pipeline_id, const QString &message, int
|
||||
BufferingFinished();
|
||||
emit StateChanged(Engine::Error);
|
||||
// unable to play media stream with this url
|
||||
emit InvalidSongRequested(url_);
|
||||
emit InvalidSongRequested(media_url_);
|
||||
|
||||
emit Error(message);
|
||||
|
||||
@ -649,9 +650,9 @@ void GstEngine::PlayDone(QFuture<GstStateChangeReturn> future, const quint64 off
|
||||
if (ret == GST_STATE_CHANGE_FAILURE) {
|
||||
// Failure, but we got a redirection URL - try loading that instead
|
||||
QByteArray redirect_url = current_pipeline_->redirect_url();
|
||||
if (!redirect_url.isEmpty() && redirect_url != current_pipeline_->url()) {
|
||||
if (!redirect_url.isEmpty() && redirect_url != current_pipeline_->media_url()) {
|
||||
qLog(Info) << "Redirecting to" << redirect_url;
|
||||
current_pipeline_ = CreatePipeline(redirect_url, end_nanosec_);
|
||||
current_pipeline_ = CreatePipeline(redirect_url, current_pipeline_->original_url(), end_nanosec_);
|
||||
Play(offset_nanosec);
|
||||
return;
|
||||
}
|
||||
@ -665,14 +666,14 @@ void GstEngine::PlayDone(QFuture<GstStateChangeReturn> future, const quint64 off
|
||||
|
||||
StartTimers();
|
||||
|
||||
// initial offset
|
||||
// Initial offset
|
||||
if (offset_nanosec != 0 || beginning_nanosec_ != 0) {
|
||||
Seek(offset_nanosec);
|
||||
}
|
||||
|
||||
emit StateChanged(Engine::Playing);
|
||||
// we've successfully started playing a media stream with this url
|
||||
emit ValidSongRequested(url_);
|
||||
// We've successfully started playing a media stream with this url
|
||||
emit ValidSongRequested(media_url_);
|
||||
|
||||
}
|
||||
|
||||
@ -823,25 +824,14 @@ shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline() {
|
||||
connect(ret.get(), SIGNAL(BufferingProgress(int)), SLOT(BufferingProgress(int)));
|
||||
connect(ret.get(), SIGNAL(BufferingFinished()), SLOT(BufferingFinished()));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QUrl &url, qint64 end_nanosec) {
|
||||
|
||||
shared_ptr<GstEnginePipeline> ret = CreatePipeline();
|
||||
|
||||
if (!ret->InitFromUrl(url.toEncoded(), end_nanosec)) ret.reset();
|
||||
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QByteArray &url, qint64 end_nanosec) {
|
||||
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QByteArray &gst_url, const QUrl &original_url, qint64 end_nanosec) {
|
||||
|
||||
shared_ptr<GstEnginePipeline> ret = CreatePipeline();
|
||||
|
||||
if (!ret->InitFromUrl(url, end_nanosec)) ret.reset();
|
||||
|
||||
if (!ret->InitFromUrl(gst_url, original_url, end_nanosec)) ret.reset();
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
@ -68,8 +68,8 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
|
||||
|
||||
bool Init();
|
||||
Engine::State state() const;
|
||||
void StartPreloading(const QUrl &url, bool force_stop_at_end, qint64 beginning_nanosec, qint64 end_nanosec);
|
||||
bool Load(const QUrl &, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
|
||||
void StartPreloading(const QUrl &media_url, const QUrl &original_url, bool force_stop_at_end, qint64 beginning_nanosec, qint64 end_nanosec);
|
||||
bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
|
||||
bool Play(quint64 offset_nanosec);
|
||||
void Stop(bool stop_after = false);
|
||||
void Pause();
|
||||
@ -157,8 +157,7 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
|
||||
void StopTimers();
|
||||
|
||||
std::shared_ptr<GstEnginePipeline> CreatePipeline();
|
||||
std::shared_ptr<GstEnginePipeline> CreatePipeline(const QUrl &url, qint64 end_nanosec);
|
||||
std::shared_ptr<GstEnginePipeline> CreatePipeline(const QByteArray &url, qint64 end_nanosec);
|
||||
std::shared_ptr<GstEnginePipeline> CreatePipeline(const QByteArray &gst_url, const QUrl &original_url, qint64 end_nanosec);
|
||||
|
||||
void UpdateScope(int chunk_length);
|
||||
|
||||
|
@ -371,15 +371,16 @@ bool GstEnginePipeline::InitFromString(const QString &pipeline) {
|
||||
|
||||
}
|
||||
|
||||
bool GstEnginePipeline::InitFromUrl(const QByteArray &url, qint64 end_nanosec) {
|
||||
bool GstEnginePipeline::InitFromUrl(const QByteArray &media_url, const QUrl original_url, qint64 end_nanosec) {
|
||||
|
||||
url_ = url;
|
||||
media_url_ = media_url;
|
||||
original_url_ = original_url;
|
||||
end_offset_nanosec_ = end_nanosec;
|
||||
|
||||
pipeline_ = engine_->CreateElement("playbin");
|
||||
if (pipeline_ == nullptr) return false;
|
||||
|
||||
g_object_set(G_OBJECT(pipeline_), "uri", url.constData(), nullptr);
|
||||
g_object_set(G_OBJECT(pipeline_), "uri", media_url.constData(), nullptr);
|
||||
CHECKED_GCONNECT(G_OBJECT(pipeline_), "about-to-finish", &AboutToFinishCallback, this);
|
||||
|
||||
CHECKED_GCONNECT(G_OBJECT(pipeline_), "pad-added", &NewPadCallback, this);
|
||||
@ -501,9 +502,11 @@ void GstEnginePipeline::StreamStartMessageReceived() {
|
||||
if (next_uri_set_) {
|
||||
next_uri_set_ = false;
|
||||
|
||||
url_ = next_url_;
|
||||
media_url_ = next_media_url_;
|
||||
original_url_ = next_original_url_;
|
||||
end_offset_nanosec_ = next_end_offset_nanosec_;
|
||||
next_url_ = QByteArray();
|
||||
next_media_url_ = QByteArray();
|
||||
next_original_url_ = QUrl();
|
||||
next_beginning_offset_nanosec_ = 0;
|
||||
next_end_offset_nanosec_ = 0;
|
||||
|
||||
@ -580,11 +583,18 @@ void GstEnginePipeline::TagMessageReceived(GstMessage *msg) {
|
||||
gst_message_parse_tag(msg, &taglist);
|
||||
|
||||
Engine::SimpleMetaBundle bundle;
|
||||
bundle.url = QUrl(QString(url_));
|
||||
bundle.title = ParseTag(taglist, GST_TAG_TITLE);
|
||||
bundle.artist = ParseTag(taglist, GST_TAG_ARTIST);
|
||||
bundle.comment = ParseTag(taglist, GST_TAG_COMMENT);
|
||||
bundle.album = ParseTag(taglist, GST_TAG_ALBUM);
|
||||
bundle.url = original_url_;
|
||||
bundle.title = ParseStrTag(taglist, GST_TAG_TITLE);
|
||||
bundle.artist = ParseStrTag(taglist, GST_TAG_ARTIST);
|
||||
bundle.comment = ParseStrTag(taglist, GST_TAG_COMMENT);
|
||||
bundle.album = ParseStrTag(taglist, GST_TAG_ALBUM);
|
||||
bundle.length = 0;
|
||||
bundle.year = 0;
|
||||
bundle.tracknr = 0;
|
||||
bundle.samplerate = 0;
|
||||
bundle.bitdepth = 0;
|
||||
bundle.bitrate = ParseUIntTag(taglist, GST_TAG_BITRATE) / 1000;
|
||||
bundle.lyrics = ParseStrTag(taglist, GST_TAG_LYRICS);
|
||||
|
||||
gst_tag_list_free(taglist);
|
||||
|
||||
@ -595,7 +605,7 @@ void GstEnginePipeline::TagMessageReceived(GstMessage *msg) {
|
||||
|
||||
}
|
||||
|
||||
QString GstEnginePipeline::ParseTag(GstTagList *list, const char *tag) const {
|
||||
QString GstEnginePipeline::ParseStrTag(GstTagList *list, const char *tag) const {
|
||||
|
||||
gchar *data = nullptr;
|
||||
bool success = gst_tag_list_get_string(list, tag, &data);
|
||||
@ -609,6 +619,17 @@ QString GstEnginePipeline::ParseTag(GstTagList *list, const char *tag) const {
|
||||
|
||||
}
|
||||
|
||||
guint GstEnginePipeline::ParseUIntTag(GstTagList *list, const char *tag) const {
|
||||
|
||||
guint data;
|
||||
bool success = gst_tag_list_get_uint(list, tag, &data);
|
||||
|
||||
guint ret = 0;
|
||||
if (success && data) ret = data;
|
||||
return ret;
|
||||
|
||||
}
|
||||
|
||||
void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) {
|
||||
|
||||
if (msg->src != GST_OBJECT(pipeline_)) {
|
||||
@ -632,7 +653,7 @@ void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) {
|
||||
if (next_uri_set_ && new_state == GST_STATE_READY) {
|
||||
// Revert uri and go back to PLAY state again
|
||||
next_uri_set_ = false;
|
||||
g_object_set(G_OBJECT(pipeline_), "uri", url_.constData(), nullptr);
|
||||
g_object_set(G_OBJECT(pipeline_), "uri", media_url_.constData(), nullptr);
|
||||
SetState(GST_STATE_PLAYING);
|
||||
}
|
||||
}
|
||||
@ -760,10 +781,11 @@ GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad*, GstPadProbeInfo *i
|
||||
quint64 end_time = start_time + duration;
|
||||
|
||||
if (end_time > instance->end_offset_nanosec_) {
|
||||
if (instance->has_next_valid_url() && instance->next_url_ == instance->url_ && instance->next_beginning_offset_nanosec_ == instance->end_offset_nanosec_) {
|
||||
if (instance->has_next_valid_url() && instance->next_media_url_ == instance->media_url_ && instance->next_beginning_offset_nanosec_ == instance->end_offset_nanosec_) {
|
||||
// The "next" song is actually the next segment of this file - so cheat and keep on playing, but just tell the Engine we've moved on.
|
||||
instance->end_offset_nanosec_ = instance->next_end_offset_nanosec_;
|
||||
instance->next_url_ = QByteArray();
|
||||
instance->next_media_url_ = QByteArray();
|
||||
instance->next_original_url_ = QUrl();
|
||||
instance->next_beginning_offset_nanosec_ = 0;
|
||||
instance->next_end_offset_nanosec_ = 0;
|
||||
|
||||
@ -816,7 +838,7 @@ void GstEnginePipeline::AboutToFinishCallback(GstPlayBin *bin, gpointer self) {
|
||||
// Set the next uri. When the current song ends it will be played automatically and a STREAM_START message is send to the bus.
|
||||
// When the next uri is not playable an error message is send when the pipeline goes to PLAY (or PAUSE) state or immediately if it is currently in PLAY state.
|
||||
instance->next_uri_set_ = true;
|
||||
g_object_set(G_OBJECT(instance->pipeline_), "uri", instance->next_url_.constData(), nullptr);
|
||||
g_object_set(G_OBJECT(instance->pipeline_), "uri", instance->next_media_url_.constData(), nullptr);
|
||||
}
|
||||
|
||||
}
|
||||
@ -1059,9 +1081,10 @@ void GstEnginePipeline::RemoveAllBufferConsumers() {
|
||||
buffer_consumers_.clear();
|
||||
}
|
||||
|
||||
void GstEnginePipeline::SetNextUrl(const QByteArray &url, qint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
void GstEnginePipeline::SetNextUrl(const QByteArray &media_url, const QUrl &original_url, qint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
|
||||
next_url_ = url;
|
||||
next_media_url_ = media_url;
|
||||
next_original_url_ = original_url;
|
||||
next_beginning_offset_nanosec_ = beginning_nanosec;
|
||||
next_end_offset_nanosec_ = end_nanosec;
|
||||
|
||||
|
@ -71,7 +71,7 @@ class GstEnginePipeline : public QObject {
|
||||
void set_mono_playback(bool enabled);
|
||||
|
||||
// Creates the pipeline, returns false on error
|
||||
bool InitFromUrl(const QByteArray &url, qint64 end_nanosec);
|
||||
bool InitFromUrl(const QByteArray &media_url, const QUrl original_url, qint64 end_nanosec);
|
||||
bool InitFromString(const QString &pipeline);
|
||||
|
||||
// GstBufferConsumers get fed audio data. Thread-safe.
|
||||
@ -89,13 +89,14 @@ class GstEnginePipeline : public QObject {
|
||||
void StartFader(qint64 duration_nanosec, QTimeLine::Direction direction = QTimeLine::Forward, QTimeLine::CurveShape shape = QTimeLine::LinearCurve, bool use_fudge_timer = true);
|
||||
|
||||
// If this is set then it will be loaded automatically when playback finishes for gapless playback
|
||||
void SetNextUrl(const QByteArray &url, qint64 beginning_nanosec, qint64 end_nanosec);
|
||||
bool has_next_valid_url() const { return !next_url_.isNull() && !next_url_.isEmpty(); }
|
||||
void SetNextUrl(const QByteArray &media_url, const QUrl &original_url, qint64 beginning_nanosec, qint64 end_nanosec);
|
||||
bool has_next_valid_url() const { return !next_media_url_.isNull() && !next_media_url_.isEmpty(); }
|
||||
|
||||
void SetSourceDevice(QString device) { source_device_ = device; }
|
||||
|
||||
// Get information about the music playback
|
||||
QByteArray url() const { return url_; }
|
||||
QByteArray media_url() const { return media_url_; }
|
||||
QUrl original_url() const { return original_url_; }
|
||||
bool is_valid() const { return valid_; }
|
||||
// Please note that this method (unlike GstEngine's.position()) is multiple-section media unaware.
|
||||
qint64 position() const;
|
||||
@ -150,7 +151,8 @@ signals:
|
||||
void StreamStatusMessageReceived(GstMessage*);
|
||||
void StreamStartMessageReceived();
|
||||
|
||||
QString ParseTag(GstTagList *list, const char *tag) const;
|
||||
QString ParseStrTag(GstTagList *list, const char *tag) const;
|
||||
guint ParseUIntTag(GstTagList *list, const char *tag) const;
|
||||
|
||||
bool InitDecodeBin(GstElement* new_bin);
|
||||
bool InitAudioBin();
|
||||
@ -213,8 +215,10 @@ signals:
|
||||
bool segment_start_received_;
|
||||
|
||||
// The URL that is currently playing, and the URL that is to be preloaded when the current track is close to finishing.
|
||||
QByteArray url_;
|
||||
QByteArray next_url_;
|
||||
QByteArray media_url_;
|
||||
QUrl original_url_;
|
||||
QByteArray next_media_url_;
|
||||
QUrl next_original_url_;
|
||||
|
||||
// If this is > 0 then the pipeline will be forced to stop when playback goes past this position.
|
||||
qint64 end_offset_nanosec_;
|
||||
|
@ -64,8 +64,8 @@ bool PhononEngine::CanDecode(const QUrl &url) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PhononEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
media_object_->setCurrentSource(Phonon::MediaSource(url));
|
||||
bool PhononEngine::Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
media_object_->setCurrentSource(Phonon::MediaSource(media_url));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ class PhononEngine : public Engine::Base {
|
||||
|
||||
bool CanDecode(const QUrl &url);
|
||||
|
||||
bool Load(const QUrl &, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
|
||||
bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
|
||||
bool Play(quint64 offset_nanosec);
|
||||
void Stop(bool stop_after = false);
|
||||
void Pause();
|
||||
|
@ -100,12 +100,12 @@ bool VLCEngine::Initialised() const {
|
||||
|
||||
}
|
||||
|
||||
bool VLCEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
bool VLCEngine::Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
|
||||
if (!Initialised()) return false;
|
||||
|
||||
// Create the media object
|
||||
VlcScopedRef<libvlc_media_t> media(libvlc_media_new_location(instance_, url.toEncoded().constData()));
|
||||
VlcScopedRef<libvlc_media_t> media(libvlc_media_new_location(instance_, media_url.toEncoded().constData()));
|
||||
|
||||
libvlc_media_player_set_media(player_, media);
|
||||
|
||||
|
@ -48,7 +48,7 @@ class VLCEngine : public Engine::Base {
|
||||
|
||||
bool Init();
|
||||
Engine::State state() const { return state_; }
|
||||
bool Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
|
||||
bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
|
||||
bool Play(quint64 offset_nanosec);
|
||||
void Stop(bool stop_after = false);
|
||||
void Pause();
|
||||
|
@ -180,20 +180,20 @@ Engine::State XineEngine::state() const {
|
||||
return Engine::Empty;
|
||||
case XINE_STATUS_STOP:
|
||||
default:
|
||||
return url_.isEmpty() ? Engine::Empty : Engine::Idle;
|
||||
return media_url_.isEmpty() ? Engine::Empty : Engine::Idle;
|
||||
}
|
||||
}
|
||||
|
||||
bool XineEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
bool XineEngine::Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
|
||||
|
||||
if (!EnsureStream()) return false;
|
||||
|
||||
Engine::Base::Load(url, change, force_stop_at_end, beginning_nanosec, end_nanosec);
|
||||
Engine::Base::Load(media_url_, original_url_, change, force_stop_at_end, beginning_nanosec, end_nanosec);
|
||||
|
||||
xine_close(stream_);
|
||||
|
||||
//int result = xine_open(stream_, url.path().toUtf8());
|
||||
int result = xine_open(stream_, url.toString().toUtf8());
|
||||
int result = xine_open(stream_, media_url.toString().toUtf8());
|
||||
if (result) {
|
||||
|
||||
#ifndef XINE_SAFE_MODE
|
||||
@ -375,7 +375,7 @@ uint XineEngine::length() const {
|
||||
|
||||
// Xine often delivers nonsense values for VBR files and such, so we only use the length for remote files
|
||||
|
||||
if (url_.scheme().toLower() == "file") return 0;
|
||||
if (media_url_.scheme().toLower() == "file") return 0;
|
||||
else {
|
||||
int pos = 0, time = 0, length = 0;
|
||||
|
||||
@ -477,10 +477,9 @@ bool XineEngine::MetaDataForUrl(const QUrl &url, Engine::SimpleMetaBundle &b) {
|
||||
b.artist = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_ARTIST));
|
||||
b.album = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_ALBUM));
|
||||
b.genre = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_GENRE));
|
||||
b.year = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_YEAR));
|
||||
b.tracknr = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_TRACK_NUMBER));
|
||||
if (b.tracknr.isEmpty())
|
||||
b.tracknr = QFileInfo(url.path()).fileName();
|
||||
b.year = atoi(xine_get_meta_info(tmpstream, XINE_META_INFO_YEAR));
|
||||
b.tracknr = atoi(xine_get_meta_info(tmpstream, XINE_META_INFO_TRACK_NUMBER));
|
||||
//if (b.tracknr <= 0) b.tracknr = QFileInfo(url.path()).fileName();
|
||||
}
|
||||
else {
|
||||
b.title = QString("Track %1").arg(QFileInfo(url.path()).fileName());
|
||||
@ -490,18 +489,19 @@ bool XineEngine::MetaDataForUrl(const QUrl &url, Engine::SimpleMetaBundle &b) {
|
||||
|
||||
if (audioCodec == "CDDA" || audioCodec == "WAV") {
|
||||
result = true;
|
||||
b.url = url;
|
||||
int samplerate = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_SAMPLERATE);
|
||||
|
||||
int bitdepth = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_BITS);
|
||||
int channels = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_CHANNELS);
|
||||
// Xine would provide a XINE_STREAM_INFO_AUDIO_BITRATE, but unfortunately not for CDDA or WAV so we calculate the bitrate by our own
|
||||
int bitsPerSample = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_BITS);
|
||||
int nbrChannels = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_CHANNELS);
|
||||
int bitrate = (samplerate * bitsPerSample * nbrChannels) / 1000;
|
||||
int bitrate = (samplerate * bitdepth * channels) / 1000;
|
||||
|
||||
b.bitrate = QString::number(bitrate);
|
||||
b.samplerate = QString::number(samplerate);
|
||||
b.samplerate = samplerate;
|
||||
b.bitdepth = bitdepth;
|
||||
b.bitrate = bitrate;
|
||||
int pos, time, length = 0;
|
||||
xine_get_pos_length(tmpstream, &pos, &time, &length);
|
||||
b.length = QString::number(length / 1000);
|
||||
b.length = length / 1000;
|
||||
}
|
||||
xine_close(tmpstream);
|
||||
}
|
||||
@ -750,7 +750,7 @@ bool XineEngine::event(QEvent *e) {
|
||||
return true;
|
||||
|
||||
case XineEvent::InfoMessage:
|
||||
emit InfoMessage((*message).arg(url_.toString()));
|
||||
emit InfoMessage((*message).arg(media_url_.toString()));
|
||||
delete message;
|
||||
return true;
|
||||
|
||||
@ -771,7 +771,7 @@ bool XineEngine::event(QEvent *e) {
|
||||
|
||||
case XineEvent::Redirecting:
|
||||
emit StatusText(QString("Redirecting to: ").arg(*message));
|
||||
Load(QUrl(*message), Engine::Auto, false, 0, 0);
|
||||
Load(QUrl(*message), original_url_, Engine::Auto, false, 0, 0);
|
||||
Play(0);
|
||||
delete message;
|
||||
return true;
|
||||
@ -787,15 +787,18 @@ bool XineEngine::event(QEvent *e) {
|
||||
Engine::SimpleMetaBundle XineEngine::fetchMetaData() const {
|
||||
|
||||
Engine::SimpleMetaBundle bundle;
|
||||
bundle.url = original_url;
|
||||
bundle.title = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_TITLE));
|
||||
bundle.artist = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_ARTIST));
|
||||
bundle.album = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_ALBUM));
|
||||
bundle.comment = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_COMMENT));
|
||||
bundle.genre = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_GENRE));
|
||||
bundle.bitrate = QString::number(xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_BITRATE) / 1000);
|
||||
bundle.samplerate = QString::number(xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_SAMPLERATE));
|
||||
bundle.year = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_YEAR));
|
||||
bundle.tracknr = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_TRACK_NUMBER));
|
||||
bundle.length = 0;
|
||||
bundle.year = atoi(xine_get_meta_info(stream_, XINE_META_INFO_YEAR));
|
||||
bundle.tracknr = atoi(xine_get_meta_info(stream_, XINE_META_INFO_TRACK_NUMBER));
|
||||
bundle.samplerate = xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_SAMPLERATE);
|
||||
bundle.bitdepth = xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_BITS);
|
||||
bundle.bitrate = xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_BITRATE) / 1000;
|
||||
|
||||
return bundle;
|
||||
|
||||
@ -899,7 +902,7 @@ void XineEngine::DetermineAndShowErrorMessage() {
|
||||
// xine can read the plugin but it didn't find any codec
|
||||
// THUS xine=daft for telling us it could handle the format in canDecode!
|
||||
body = "There is no available decoder.";
|
||||
QString const ext = QFileInfo(url_.path()).completeSuffix();
|
||||
QString const ext = QFileInfo(media_url_.path()).completeSuffix();
|
||||
// TODO:
|
||||
// if (ext == "mp3" && EngineController::installDistroCodec("xine-engine"))
|
||||
// return;
|
||||
|
@ -70,7 +70,7 @@ class XineEngine : public Engine::Base {
|
||||
|
||||
bool Init();
|
||||
Engine::State state() const;
|
||||
bool Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
|
||||
bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
|
||||
bool Play(quint64 offset_nanosec);
|
||||
void Stop(bool stop_after = false);
|
||||
void Pause();
|
||||
@ -116,7 +116,8 @@ class XineEngine : public Engine::Base {
|
||||
float preamp_;
|
||||
std::unique_ptr<PruneScopeThread> prune_;
|
||||
|
||||
QUrl url_;
|
||||
QUrl media_url_;
|
||||
QUrl original_url_;
|
||||
|
||||