try handle more edge cases

This commit is contained in:
Erica Z 2024-10-15 22:45:16 +02:00
parent bac519034d
commit e6f3dc6e7c

View file

@ -26,13 +26,16 @@ class Playbin : Object {
public PlaybinState state { get; private set; default = PlaybinState.STOPPED; } public PlaybinState state { get; private set; default = PlaybinState.STOPPED; }
// true if a timer should update the postion property
private bool update_position = false; private bool update_position = false;
public int64 position { get; private set; default = 0; }
public int64 position { get; private set; } public Subsonic api { get; set; default = null; }
public Subsonic api { get; set; }
// sent when a new song starts playing
public signal void now_playing (uint index, Song song, int64 duration); public signal void now_playing (uint index, Song song, int64 duration);
// FIXME this should be synced with the selection model, right??
private uint playing_index; private uint playing_index;
private bool next_gapless; private bool next_gapless;
@ -41,6 +44,37 @@ class Playbin : Object {
source.user_agent = "audrey/linux"; source.user_agent = "audrey/linux";
} }
// ASSUMPTION: about-to-finish will be signalled exactly once per track
// even if seeking backwards after
AsyncQueue<string> next_uri = new AsyncQueue<string> ();
private ListModel _play_queue = null;
private ulong _play_queue_items_changed;
public ListModel play_queue {
get { return _play_queue; }
set {
if (_play_queue != null) {
SignalHandler.disconnect (_play_queue, _play_queue_items_changed);
}
_play_queue = value;
_play_queue_items_changed = value.items_changed.connect (on_play_queue_items_changed);
}
}
// called when uri can be switched for gapless playback
// need async queue because this might be called from a gstreamer thread
private void about_to_finish (dynamic Gst.Element playbin) {
print ("about to finish\n");
// will block if the next uri isn't ready yet
// leaves the queue empty as per the ASSUMPTION above
string? next_uri = this.next_uri.pop ();
if (next_uri != "") {
playbin.uri = next_uri;
}
}
private void on_play_queue_items_changed (ListModel play_queue, uint position, uint removed, uint added) { private void on_play_queue_items_changed (ListModel play_queue, uint position, uint removed, uint added) {
if (this.state == PlaybinState.STOPPED) { if (this.state == PlaybinState.STOPPED) {
return; return;
@ -74,26 +108,12 @@ class Playbin : Object {
} else { } else {
// about-to-finish already triggered // about-to-finish already triggered
// we'll need to stop the new track when it starts playing // we'll need to stop the new track when it starts playing
assert (false); // TODO // but stream-start should already be able to take care of that
} }
} }
} }
private ListModel _play_queue = null;
private ulong _play_queue_items_changed;
public ListModel play_queue {
get { return _play_queue; }
set {
if (_play_queue != null) {
SignalHandler.disconnect (_play_queue, _play_queue_items_changed);
}
_play_queue = value;
_play_queue_items_changed = value.items_changed.connect (on_play_queue_items_changed);
}
}
public Playbin () { public Playbin () {
this.next_uri = new AsyncQueue<string> ();
this.next_uri.push (""); this.next_uri.push ("");
// gstreamer docs: GNOME-based applications, for example, will usually // gstreamer docs: GNOME-based applications, for example, will usually
@ -151,19 +171,19 @@ class Playbin : Object {
} }
var now_playing = (Song) play_queue.get_item (this.playing_index); var now_playing = (Song) play_queue.get_item (this.playing_index);
if (this.api.stream_uri (now_playing.id) != (string) this.playbin.current_uri) { if (this.api.stream_uri (now_playing.id) == (string) this.playbin.current_uri) {
this.now_playing (this.playing_index, now_playing, duration);
if (this.playing_index+1 < play_queue.get_n_items ()) {
Song song = (Song) play_queue.get_item (this.playing_index+1);
this.next_uri.push (this.api.stream_uri (song.id));
} else {
this.next_uri.push ("");
}
} else {
// edge case // edge case
assert (false); // TODO assert (false); // TODO
} }
this.now_playing (this.playing_index, now_playing, duration);
if (this.playing_index+1 < play_queue.get_n_items ()) {
Song song = (Song) play_queue.get_item (this.playing_index+1);
this.next_uri.push (this.api.stream_uri (song.id));
} else {
this.next_uri.push ("");
}
}); });
bus.message["state-changed"].connect ((message) => { bus.message["state-changed"].connect ((message) => {
@ -195,32 +215,11 @@ class Playbin : Object {
this.playbin.set_state (Gst.State.PLAYING); this.playbin.set_state (Gst.State.PLAYING);
this.next_gapless = false; this.next_gapless = false;
string? next_uri = this.next_uri.try_pop (); // make sure the queue is empty, so next stream-changed can fix it up
if (next_uri != null) { this.next_uri.try_pop ();
// we're in luck, about-to-finish hasn't been triggered yet // if it was already empty then uhhhh if theres any problems then
} else { // playbin.uri wont match up with the current track's stream uri and we can
// about-to-finish already triggered // fix it there
// we'll need to stop the new track when it starts playing
assert (false); // TODO
}
}
// ASSUMPTION: about-to-finish will be signalled exactly once per track
// even if seeking backwards after
AsyncQueue<string?> next_uri;
// called when uri can be switched for gapless playback
// need async queue because this might be called from a gstreamer thread
private void about_to_finish (dynamic Gst.Element playbin) {
print ("about to finish\n");
// will block if the next uri isn't ready yet
// leaves the queue empty as per the ASSUMPTION above
string? next_uri = this.next_uri.pop ();
if (next_uri != "") {
playbin.uri = next_uri;
}
} }
public void pause () { public void pause () {
@ -234,9 +233,4 @@ class Playbin : Object {
this.playbin.set_state (Gst.State.PLAYING); this.playbin.set_state (Gst.State.PLAYING);
this.state = PlaybinState.PLAYING; this.state = PlaybinState.PLAYING;
} }
public void stop_playback() {
this.playbin.set_state (Gst.State.READY);
this.state = PlaybinState.STOPPED;
}
} }