Commit 2f43e4bc authored by Max Kellermann's avatar Max Kellermann

Playlist: copy stream tags from the PlayerThread

Finally restores an important feature that has been broken for several months when the PlayerThread started working with Song copies instead of pointers to the Queue's Song instances (commit e96779de).
parent f8c23488
...@@ -40,8 +40,8 @@ struct Instance { ...@@ -40,8 +40,8 @@ struct Instance {
void DatabaseModified(); void DatabaseModified();
/** /**
* A tag in the play queue has been modified. Propagate the * A tag in the play queue has been modified by the player
* change to all subsystems. * thread. Propagate the change to all subsystems.
*/ */
void TagModified(); void TagModified();
......
...@@ -19,11 +19,16 @@ ...@@ -19,11 +19,16 @@
#include "config.h" #include "config.h"
#include "Partition.hxx" #include "Partition.hxx"
#include "Song.hxx"
void void
Partition::TagModified() Partition::TagModified()
{ {
playlist.TagChanged(); Song *song = pc.LockReadTaggedSong();
if (song != nullptr) {
playlist.TagModified(std::move(*song));
song->Free();
}
} }
void void
......
...@@ -167,8 +167,8 @@ struct Partition { ...@@ -167,8 +167,8 @@ struct Partition {
} }
/** /**
* A tag in the play queue has been modified. Propagate the * A tag in the play queue has been modified by the player
* change to all subsystems. * thread. Propagate the change to all subsystems.
*/ */
void TagModified(); void TagModified();
......
...@@ -53,6 +53,9 @@ player_control::~player_control() ...@@ -53,6 +53,9 @@ player_control::~player_control()
{ {
if (next_song != nullptr) if (next_song != nullptr)
next_song->Free(); next_song->Free();
if (tagged_song != nullptr)
tagged_song->Free();
} }
void void
...@@ -201,6 +204,25 @@ player_control::ClearError() ...@@ -201,6 +204,25 @@ player_control::ClearError()
} }
void void
player_control::LockSetTaggedSong(const Song &song)
{
Lock();
if (tagged_song != nullptr)
tagged_song->Free();
tagged_song = song.DupDetached();
Unlock();
}
void
player_control::ClearTaggedSong()
{
if (tagged_song != nullptr) {
tagged_song->Free();
tagged_song = nullptr;
}
}
void
player_control::EnqueueSong(Song *song) player_control::EnqueueSong(Song *song)
{ {
assert(song != nullptr); assert(song != nullptr);
......
...@@ -100,7 +100,7 @@ struct player_control { ...@@ -100,7 +100,7 @@ struct player_control {
Thread thread; Thread thread;
/** /**
* This lock protects #command, #state, #error. * This lock protects #command, #state, #error, #tagged_song.
*/ */
mutable Mutex mutex; mutable Mutex mutex;
...@@ -129,6 +129,18 @@ struct player_control { ...@@ -129,6 +129,18 @@ struct player_control {
*/ */
Error error; Error error;
/**
* A copy of the current #Song after its tags have been
* updated by the decoder (for example, a radio stream that
* has sent a new tag after switching to the next song). This
* shall be used by the GlobalEvents::TAG handler to update
* the current #Song in the queue.
*
* Protected by #mutex. Set by the PlayerThread and consumed
* by the main thread.
*/
Song *tagged_song;
uint16_t bit_rate; uint16_t bit_rate;
AudioFormat audio_format; AudioFormat audio_format;
float total_time; float total_time;
...@@ -356,6 +368,35 @@ public: ...@@ -356,6 +368,35 @@ public:
return error_type; return error_type;
} }
/**
* Set the #tagged_song attribute to a newly allocated copy of
* the given #Song. Locks and unlocks the object.
*/
void LockSetTaggedSong(const Song &song);
void ClearTaggedSong();
/**
* Read and clear the #tagged_song attribute.
*
* Caller must lock the object.
*/
Song *ReadTaggedSong() {
Song *result = tagged_song;
tagged_song = nullptr;
return result;
}
/**
* Like ReadTaggedSong(), but locks and unlocks the object.
*/
Song *LockReadTaggedSong() {
Lock();
Song *result = ReadTaggedSong();
Unlock();
return result;
}
void Stop(); void Stop();
void UpdateAudio(); void UpdateAudio();
......
...@@ -335,6 +335,8 @@ Player::WaitForDecoder() ...@@ -335,6 +335,8 @@ Player::WaitForDecoder()
return false; return false;
} }
pc.ClearTaggedSong();
if (song != nullptr) if (song != nullptr)
song->Free(); song->Free();
...@@ -684,7 +686,7 @@ Player::ProcessCommand() ...@@ -684,7 +686,7 @@ Player::ProcessCommand()
} }
static void static void
update_song_tag(Song *song, const Tag &new_tag) update_song_tag(player_control &pc, Song *song, const Tag &new_tag)
{ {
if (song->IsFile()) if (song->IsFile())
/* don't update tags of local files, only remote /* don't update tags of local files, only remote
...@@ -696,6 +698,8 @@ update_song_tag(Song *song, const Tag &new_tag) ...@@ -696,6 +698,8 @@ update_song_tag(Song *song, const Tag &new_tag)
delete old_tag; delete old_tag;
pc.LockSetTaggedSong(*song);
/* the main thread will update the playlist version when he /* the main thread will update the playlist version when he
receives this event */ receives this event */
GlobalEvents::Emit(GlobalEvents::TAG); GlobalEvents::Emit(GlobalEvents::TAG);
...@@ -722,7 +726,7 @@ play_chunk(player_control &pc, ...@@ -722,7 +726,7 @@ play_chunk(player_control &pc,
assert(chunk->CheckFormat(format)); assert(chunk->CheckFormat(format));
if (chunk->tag != nullptr) if (chunk->tag != nullptr)
update_song_tag(song, *chunk->tag); update_song_tag(pc, song, *chunk->tag);
if (chunk->IsEmpty()) { if (chunk->IsEmpty()) {
buffer.Return(chunk); buffer.Return(chunk);
...@@ -1077,6 +1081,8 @@ Player::Run() ...@@ -1077,6 +1081,8 @@ Player::Run()
pc.Lock(); pc.Lock();
pc.ClearTaggedSong();
if (queued) { if (queued) {
assert(pc.next_song != nullptr); assert(pc.next_song != nullptr);
pc.next_song->Free(); pc.next_song->Free();
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "PlaylistError.hxx" #include "PlaylistError.hxx"
#include "PlayerControl.hxx" #include "PlayerControl.hxx"
#include "Song.hxx" #include "Song.hxx"
#include "tag/Tag.hxx"
#include "Idle.hxx" #include "Idle.hxx"
#include "Log.hxx" #include "Log.hxx"
...@@ -35,13 +36,17 @@ playlist::FullIncrementVersions() ...@@ -35,13 +36,17 @@ playlist::FullIncrementVersions()
} }
void void
playlist::TagChanged() playlist::TagModified(Song &&song)
{ {
if (!playing) if (!playing || song.tag == nullptr)
return; return;
assert(current >= 0); assert(current >= 0);
Song &current_song = queue.GetOrder(current);
if (SongEquals(song, current_song))
current_song.ReplaceTag(std::move(*song.tag));
queue.ModifyAtOrder(current); queue.ModifyAtOrder(current);
idle_add(IDLE_PLAYLIST); idle_add(IDLE_PLAYLIST);
} }
......
...@@ -128,7 +128,12 @@ protected: ...@@ -128,7 +128,12 @@ protected:
public: public:
void Clear(player_control &pc); void Clear(player_control &pc);
void TagChanged(); /**
* A tag in the play queue has been modified by the player
* thread. Apply the given song's tag to the current song if
* the song matches.
*/
void TagModified(Song &&song);
void FullIncrementVersions(); void FullIncrementVersions();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment