Compare commits

...

2 commits

Author SHA1 Message Date
faa5d15e1e add warning 2024-10-26 09:11:58 +02:00
9f6bc7b10b Revert "use pipe for mpv wakeup callback"
This reverts commit 72d4e63249.

more trouble than it's worth
2024-10-26 09:10:55 +02:00
2 changed files with 122 additions and 149 deletions

View file

@ -57,6 +57,9 @@ public class Playbin : GLib.Object {
public ListStore _play_queue; public ListStore _play_queue;
public ListModel play_queue { get { return this._play_queue; } } public ListModel play_queue { get { return this._play_queue; } }
// try to prevent wait_event to be called twice
private bool is_handling_event = false;
private async Mpv.Error mpv_command_async (string[] args) { private async Mpv.Error mpv_command_async (string[] args) {
CommandCallback cc = {}; CommandCallback cc = {};
@ -67,9 +70,6 @@ public class Playbin : GLib.Object {
return cc.error; return cc.error;
} }
// should be Mpv.WakeupCallback, but i think there's a vala bug here
private SourceOnceFunc wakeup_callback; // anchor reference here, mpv won't remind us
public Playbin () { public Playbin () {
this._play_queue = new ListStore (typeof (Subsonic.Song)); this._play_queue = new ListStore (typeof (Subsonic.Song));
@ -83,153 +83,126 @@ public class Playbin : GLib.Object {
assert (this.mpv.observe_property (2, "playlist-pos", Mpv.Format.INT64) >= 0); assert (this.mpv.observe_property (2, "playlist-pos", Mpv.Format.INT64) >= 0);
assert (this.mpv.observe_property (3, "pause", Mpv.Format.FLAG) >= 0); assert (this.mpv.observe_property (3, "pause", Mpv.Format.FLAG) >= 0);
int wakeup_fds[2]; this.mpv.wakeup_callback = () => {
try { Idle.add (() => {
assert (Unix.open_pipe (wakeup_fds, 0)); if (this.is_handling_event) {
} catch (Error e) { warning ("main thread mpv wakeup callback called twice");
error (@"could not open pipe for mpv wakeup: $(e.message)"); return false;
} }
this.is_handling_event = true;
IOChannel wakeup_read = new IOChannel.unix_new (wakeup_fds[0]);
IOChannel wakeup_write = new IOChannel.unix_new (wakeup_fds[1]); while (true) {
var event = this.mpv.wait_event (0.0);
wakeup_read.set_close_on_unref (true); if (event.event_id == Mpv.EventId.NONE) break;
wakeup_write.set_close_on_unref (true);
switch (event.event_id) {
try { case Mpv.EventId.PROPERTY_CHANGE:
wakeup_read.set_encoding (null); var data = event.parse_property ();
wakeup_write.set_encoding (null); switch (event.reply_userdata) {
wakeup_write.set_buffered (false); case 0:
} catch (Error e) { assert (data.name == "time-pos");
error (@"could not set up pipes for mpv wakeup: $(e.message)"); if (data.format == Mpv.Format.NONE) {
} this.position = 0.0;
} else {
this.wakeup_callback = () => { this.position = data.parse_double ();
try { }
wakeup_write.write_chars ({0}, null); break;
} catch (Error e) {
error (@"could not write to mpv wakeup pipe: $(e.message)"); case 1:
} assert (data.name == "duration");
}; if (data.format == Mpv.Format.NONE) {
this.mpv.wakeup_callback = this.wakeup_callback; // this.duration = 0.0; i think this prevents the fallback below from working
} else {
assert (0 < wakeup_read.add_watch (IOCondition.IN, (source, condition) => { this.duration = data.parse_double ();
try { }
wakeup_read.read_chars ({0}, null); break;
} catch (Error e) {
error (@"could not read from mpv wakeup pipe: $(e.message)"); case 2:
} // here as a sanity check
// should always match our own play_queu_position/state
while (true) { assert (data.name == "playlist-pos");
var event = this.mpv.wait_event (0.0); int64 playlist_pos = data.parse_int64 ();
if (event.event_id == Mpv.EventId.NONE) break; if (playlist_pos < 0) {
if (this.state != PlaybinState.STOPPED) {
switch (event.event_id) { error ("mpv has no current playlist entry, but we think it's index %u", this.play_queue_position);
case Mpv.EventId.PROPERTY_CHANGE: }
var data = event.parse_property (); assert (this.play_queue_position == this.play_queue.get_n_items ());
switch (event.reply_userdata) { } else {
case 0: if (this.state == PlaybinState.STOPPED) {
assert (data.name == "time-pos"); error ("mpv is at playlist entry %u, but we're stopped", (uint) playlist_pos);
if (data.format == Mpv.Format.NONE) { }
this.position = 0.0; if (this.play_queue_position != (uint) playlist_pos) {
} else { error ("mpv is at playlist entry %u, but we think it's %u", (uint) playlist_pos, this.play_queue_position);
this.position = data.parse_double (); }
} }
break; break;
case 1: case 3:
assert (data.name == "duration"); // also here as a sanity check
if (data.format == Mpv.Format.NONE) { // should always match our own state
// this.duration = 0.0; i think this prevents the fallback below from working assert (data.name == "pause");
} else { bool pause = data.parse_flag ();
this.duration = data.parse_double (); if (pause && this.state != PlaybinState.PAUSED) {
} error (@"mpv is paused, but we are @(this.state)");
break; }
if (!pause && this.state == PlaybinState.PAUSED) {
case 2: error ("mpv is not paused, but we are paused");
// here as a sanity check }
// should always match our own play_queu_position/state break;
assert (data.name == "playlist-pos");
int64 playlist_pos = data.parse_int64 (); default:
if (playlist_pos < 0) { assert (false);
if (this.state != PlaybinState.STOPPED) { break;
error ("mpv has no current playlist entry, but we think it's index %u", this.play_queue_position); }
} break;
assert (this.play_queue_position == this.play_queue.get_n_items ());
} else { case Mpv.EventId.START_FILE:
if (this.state == PlaybinState.STOPPED) { debug ("START_FILE received");
error ("mpv is at playlist entry %u, but we're stopped", (uint) playlist_pos);
} // estimate duration from api data
if (this.play_queue_position != (uint) playlist_pos) { // while mpv doesn't know it
error ("mpv is at playlist entry %u, but we think it's %u", (uint) playlist_pos, this.play_queue_position); this.duration = ((Subsonic.Song) this._play_queue.get_item (this.play_queue_position)).duration;
}
} this.new_track ();
break; break;
case 3: case Mpv.EventId.END_FILE:
// also here as a sanity check var data = event.parse_end_file ();
// should always match our own state debug (@"END_FILE received (reason: $(data.reason))");
assert (data.name == "pause");
bool pause = data.parse_flag (); if (data.error < 0) {
if (pause && this.state != PlaybinState.PAUSED) { warning ("playback of track aborted: %s", data.error.to_string ());
error (@"mpv is paused, but we are @(this.state)"); }
}
if (!pause && this.state == PlaybinState.PAUSED) { if (data.reason == Mpv.EndFileReason.EOF) {
error ("mpv is not paused, but we are paused"); // assume this is a proper transition
} this.play_queue_position += 1;
break;
if (this.play_queue_position == this._play_queue.get_n_items ()) {
default: // reached the end (?)
assert (false); this.state = PlaybinState.STOPPED;
break; this.stopped ();
} }
break; }
case Mpv.EventId.START_FILE: break;
debug ("START_FILE received");
case Mpv.EventId.COMMAND_REPLY:
// estimate duration from api data unowned CommandCallback *cc = (CommandCallback *) event.reply_userdata;
// while mpv doesn't know it cc.error = event.error;
this.duration = ((Subsonic.Song) this._play_queue.get_item (this.play_queue_position)).duration; cc.callback ();
break;
this.new_track ();
break; default:
// ignore by default
case Mpv.EventId.END_FILE: break;
var data = event.parse_end_file (); }
debug (@"END_FILE received (reason: $(data.reason))");
if (data.error < 0) {
warning ("playback of track aborted: %s", data.error.to_string ());
}
if (data.reason == Mpv.EndFileReason.EOF) {
// assume this is a proper transition
this.play_queue_position += 1;
if (this.play_queue_position == this._play_queue.get_n_items ()) {
// reached the end (?)
this.state = PlaybinState.STOPPED;
this.stopped ();
}
}
break;
case Mpv.EventId.COMMAND_REPLY:
unowned CommandCallback *cc = (CommandCallback *) event.reply_userdata;
cc.error = event.error;
cc.callback ();
break;
default:
// ignore by default
break;
} }
}
return true; this.is_handling_event = false;
})); return false;
});
};
} }
public void seek (double position) { public void seek (double position) {

View file

@ -43,7 +43,7 @@ namespace Mpv {
[CCode (cname = "mpv_wait_event")] [CCode (cname = "mpv_wait_event")]
public unowned Event? wait_event (double timeout); public unowned Event? wait_event (double timeout);
public unowned WakeupCallback wakeup_callback { public WakeupCallback wakeup_callback {
[CCode (cname = "mpv_set_wakeup_callback")] set; [CCode (cname = "mpv_set_wakeup_callback")] set;
} }