Compare commits

...

2 commits

Author SHA1 Message Date
d17c538ccb vapi changes 2024-10-17 22:32:51 +02:00
d053e68744 just use the sync mpv api for everything
seems to be fast enough
2024-10-17 22:08:21 +02:00
2 changed files with 97 additions and 162 deletions

View file

@ -4,16 +4,6 @@ enum PlaybinState {
PLAYING, PLAYING,
} }
errordomain PlaybinError {
MPV,
}
private void check_mpv_error (int ec) throws PlaybinError {
if (ec < 0) {
throw new PlaybinError.MPV ("%s", Mpv.error_string (ec));
}
}
private class SourceFuncWrapper { private class SourceFuncWrapper {
public SourceFunc inner; public SourceFunc inner;
@ -32,7 +22,7 @@ class Playbin : GLib.Object {
get { return _volume; } get { return _volume; }
set { set {
_volume = value; _volume = value;
mpv_set_property_int64.begin ("volume", value); assert (mpv.set_property_int64 ("volume", value) >= 0);
} }
} }
@ -41,7 +31,7 @@ class Playbin : GLib.Object {
get { return _mute; } get { return _mute; }
set { set {
_mute = value; _mute = value;
mpv_set_property_flag.begin ("mute", value); assert (mpv.set_property_flag ("mute", value) >= 0);
} }
} }
@ -65,87 +55,32 @@ class Playbin : GLib.Object {
} }
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) {
// FIXME: these should prolly be chained
for (uint i = 0; i < removed; i += 1) { for (uint i = 0; i < removed; i += 1) {
this.mpv_command.begin ({"playlist-remove", position.to_string ()}); assert (this.mpv.command ({
"playlist-remove",
position.to_string (),
}) >= 0);
} }
for (uint i = 0; i < added; i += 1) { for (uint i = 0; i < added; i += 1) {
this.mpv_command.begin ({"loadfile", this.api.stream_uri (((Subsonic.Song) play_queue.get_item (position+i)).id), "insert-at-play", (position+i).to_string ()}); assert (this.mpv.command ({
"loadfile",
this.api.stream_uri (((Subsonic.Song) play_queue.get_item (position+i)).id),
"insert-at-play",
(position+i).to_string (),
}) >= 0);
} }
} }
private SourceFuncWrapper[] mpv_command_callbacks = {};
private int[] mpv_command_error = {};
private async void mpv_command (string[] args) throws Error {
int userdata = -1;
for (int i = 0; i < this.mpv_command_callbacks.length; i += 1) {
if (this.mpv_command_callbacks[i].inner == null) {
userdata = i;
break;
}
}
if (userdata == -1) {
userdata = this.mpv_command_callbacks.length;
this.mpv_command_callbacks += new SourceFuncWrapper ();
this.mpv_command_error += 0;
}
check_mpv_error (this.mpv.command_async ((uint64) userdata, args));
this.mpv_command_callbacks[userdata].inner = this.mpv_command.callback;
yield;
check_mpv_error (this.mpv_command_error[userdata]);
}
private SourceFuncWrapper[] mpv_set_property_callbacks = {};
private int[] mpv_set_property_error = {};
private async void mpv_set_property (string name, Mpv.Format format, void *data) throws Error {
int userdata = -1;
for (int i = 0; i < this.mpv_set_property_callbacks.length; i += 1) {
if (this.mpv_set_property_callbacks[i].inner == null) {
userdata = i;
break;
}
}
if (userdata == -1) {
userdata = this.mpv_set_property_callbacks.length;
this.mpv_set_property_callbacks += new SourceFuncWrapper ();
this.mpv_set_property_error += 0;
}
check_mpv_error (this.mpv.set_property_async ((uint64) userdata, name, format, data));
this.mpv_set_property_callbacks[userdata].inner = this.mpv_set_property.callback;
yield;
check_mpv_error (this.mpv_set_property_error[userdata]);
}
private async void mpv_set_property_int64 (string name, int64 value) throws Error {
yield this.mpv_set_property (name, Mpv.Format.INT64, &value);
}
private async void mpv_set_property_flag (string name, bool value) throws Error {
int flag = value ? 1 : 0;
yield this.mpv_set_property (name, Mpv.Format.FLAG, &flag);
}
public Playbin () { public Playbin () {
try { assert (this.mpv.initialize () >= 0);
check_mpv_error (this.mpv.initialize ()); assert (this.mpv.set_property_string ("user-agent", Audrey.Const.user_agent) >= 0);
check_mpv_error (this.mpv.set_property_string ("user-agent", Audrey.Const.user_agent)); assert (this.mpv.set_property_string ("video", "no") >= 0);
check_mpv_error (this.mpv.set_property_string ("video", "no")); assert (this.mpv.set_property_string ("prefetch-playlist", "yes") >= 0);
check_mpv_error (this.mpv.set_property_string ("prefetch-playlist", "yes")); assert (this.mpv.set_property_string ("gapless-audio", "yes") >= 0);
check_mpv_error (this.mpv.set_property_string ("gapless-audio", "yes")); assert (this.mpv.observe_property (0, "time-pos", Mpv.Format.DOUBLE) >= 0);
check_mpv_error (this.mpv.observe_property (0, "time-pos", Mpv.Format.DOUBLE)); assert (this.mpv.observe_property (1, "duration", Mpv.Format.DOUBLE) >= 0);
check_mpv_error (this.mpv.observe_property (1, "duration", Mpv.Format.DOUBLE)); assert (this.mpv.observe_property (2, "playlist-pos", Mpv.Format.INT64) >= 0);
check_mpv_error (this.mpv.observe_property (2, "playlist-pos", Mpv.Format.INT64));
} catch (Error e) {
error ("could not initialize mpv: %s", e.message);
}
this.mpv.wakeup_callback = () => { this.mpv.wakeup_callback = () => {
Idle.add (() => { Idle.add (() => {
@ -154,50 +89,33 @@ class Playbin : GLib.Object {
if (event.event_id == Mpv.EventId.NONE) break; if (event.event_id == Mpv.EventId.NONE) break;
switch (event.event_id) { switch (event.event_id) {
case Mpv.EventId.COMMAND_REPLY:
this.mpv_command_error[event.reply_userdata] = event.error;
var cb = (owned) this.mpv_command_callbacks[event.reply_userdata].inner;
cb ();
break;
case Mpv.EventId.SET_PROPERTY_REPLY:
this.mpv_set_property_error[event.reply_userdata] = event.error;
var cb = (owned) this.mpv_set_property_callbacks[event.reply_userdata].inner;
cb ();
break;
case Mpv.EventId.PROPERTY_CHANGE: case Mpv.EventId.PROPERTY_CHANGE:
var data = event.parse_property ();
switch (event.reply_userdata) { switch (event.reply_userdata) {
case 0: case 0:
var data = (Mpv.EventProperty *) event.data;
assert (data.name == "time-pos"); assert (data.name == "time-pos");
if (data.format == Mpv.Format.NONE) { if (data.format == Mpv.Format.NONE) {
this.position = 0.0; this.position = 0.0;
} else { } else {
assert (data.format == Mpv.Format.DOUBLE); this.position = data.parse_double ();
this.position = * (double *) data.data;
} }
break; break;
case 1: case 1:
var data = (Mpv.EventProperty *) event.data;
assert (data.name == "duration"); assert (data.name == "duration");
if (data.format == Mpv.Format.NONE) { if (data.format == Mpv.Format.NONE) {
this.duration = 0.0; this.duration = 0.0;
} else { } else {
assert (data.format == Mpv.Format.DOUBLE); this.duration = data.parse_double ();
this.duration = * (double *) data.data;
} }
break; break;
case 2: case 2:
var data = (Mpv.EventProperty *) event.data;
assert (data.name == "playlist-pos"); assert (data.name == "playlist-pos");
if (data.format == Mpv.Format.NONE) { if (data.format == Mpv.Format.NONE) {
this.play_queue_position = 0; this.play_queue_position = 0;
} else { } else {
assert (data.format == Mpv.Format.INT64); this.play_queue_position = (uint) data.parse_int64 ();
this.play_queue_position = (uint) * (int64 *) data.data;
} }
break; break;
@ -207,36 +125,13 @@ class Playbin : GLib.Object {
} }
break; break;
case Mpv.EventId.START_FILE:
// ignore
break;
case Mpv.EventId.FILE_LOADED: case Mpv.EventId.FILE_LOADED:
this.song = (Subsonic.Song) this.play_queue.get_item (this.play_queue_position); this.song = (Subsonic.Song) this.play_queue.get_item (this.play_queue_position);
this.now_playing (); this.now_playing ();
break; break;
case Mpv.EventId.PLAYBACK_RESTART:
// ignore
break;
case Mpv.EventId.SEEK:
// ignore
break;
case Mpv.EventId.END_FILE:
// ignore
break;
// deprecated, ignore
case Mpv.EventId.IDLE:
case Mpv.EventId.TICK:
// uninteresting, ignore
case Mpv.EventId.AUDIO_RECONFIG:
break;
default: default:
print ("got unimplemented %s\n", event.event_id.to_string ()); // ignore by default
break; break;
} }
} }
@ -248,38 +143,38 @@ class Playbin : GLib.Object {
public void seek (double position) { public void seek (double position) {
this.position = position; this.position = position;
this.mpv_command.begin ({"seek", position.to_string (), "absolute"}); assert (this.mpv.command ({"seek", position.to_string (), "absolute"}) >= 0);
} }
// manually changes which track in the play queue to play // manually changes which track in the play queue to play
public void select_track (uint position) public void select_track (uint position)
requires (position < this.play_queue.get_n_items ()) requires (position < this.play_queue.get_n_items ())
{ {
this.mpv_command.begin ({"playlist-play-index", position.to_string ()}); assert (this.mpv.command ({"playlist-play-index", position.to_string ()}) >= 0);
this.state = PlaybinState.PLAYING; this.state = PlaybinState.PLAYING;
} }
public void pause () { public void pause () {
assert (this.state != PlaybinState.STOPPED); assert (this.state != PlaybinState.STOPPED);
this.state = PlaybinState.PAUSED; this.state = PlaybinState.PAUSED;
this.mpv_command.begin ({"pause"}); assert (this.mpv.command ({"pause"}) >= 0);
} }
public void play () { public void play () {
assert (this.state != PlaybinState.STOPPED); assert (this.state != PlaybinState.STOPPED);
this.state = PlaybinState.PLAYING; this.state = PlaybinState.PLAYING;
this.mpv_command.begin ({"play"}); assert (this.mpv.command ({"play"}) >= 0);
} }
public void next_track () { public void next_track () {
assert (this.state != PlaybinState.STOPPED); assert (this.state != PlaybinState.STOPPED);
this.state = PlaybinState.PLAYING; this.state = PlaybinState.PLAYING;
this.mpv_command.begin ({"playlist-next-playlist"}); assert (this.mpv.command ({"playlist-next-playlist"}) >= 0);
} }
public void prev_track () { public void prev_track () {
assert (this.state != PlaybinState.STOPPED); assert (this.state != PlaybinState.STOPPED);
this.state = PlaybinState.PLAYING; this.state = PlaybinState.PLAYING;
this.mpv_command.begin ({"playlist-prev-playlist"}); assert (this.mpv.command ({"playlist-prev-playlist"}) >= 0);
} }
} }

View file

@ -1,8 +1,33 @@
[CCode (cheader_filename = "mpv/client.h")] [CCode (cheader_filename = "mpv/client.h")]
namespace Mpv { namespace Mpv {
[CCode (cname = "mpv_error_string")] [CCode (cname = "mpv_error", cprefix = "MPV_ERROR_", has_type_id = false)]
public unowned string error_string (int error); public enum Error {
SUCCESS,
EVENT_QUEUE_FULL,
NOMEM,
UNINITIALIZED,
INVALID_PARAMETER,
OPTION_NOT_FOUND,
OPTION_FORMAT,
OPTION_ERROR,
PROPERTY_NOT_FOUND,
PROPERTY_FORMAT,
PROPERTY_UNAVAILABLE,
PROPERTY_ERROR,
COMMAND,
LOADING_FAILED,
AO_INIT_FAILED,
VO_INIT_FAILED,
NOTHING_TO_PLAY,
UNKNOWN_FORMAT,
UNSUPPORTED,
NOT_IMPLEMENTED,
GENERIC;
[CCode (cname = "mpv_error_string")]
public unowned string to_string ();
}
public delegate void WakeupCallback (); public delegate void WakeupCallback ();
@ -13,29 +38,35 @@ namespace Mpv {
public Handle (); public Handle ();
[CCode (cname = "mpv_initialize")] [CCode (cname = "mpv_initialize")]
public int initialize (); public Error initialize ();
[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 WakeupCallback wakeup_callback { public WakeupCallback wakeup_callback {
[CCode (cname = "mpv_set_wakeup_callback")] set; [CCode (cname = "mpv_set_wakeup_callback")] set;
} }
[CCode (cname = "mpv_set_property")]
public Error set_property (string name, Format format, void *data);
[CCode (cname = "mpv_set_property_string")] [CCode (cname = "mpv_set_property_string")]
public int set_property_string (string name, string data); public Error set_property_string (string name, string value);
[CCode (cname = "mpv_set_property_async")] public Error set_property_int64 (string name, int64 value) {
public int set_property_async (uint64 reply_userdata, string name, Format format, void *data); return this.set_property (name, Format.INT64, &value);
}
[CCode (cname = "mpv_command_async")] public Error set_property_flag (string name, bool value) {
public int command_async ( int flag = value ? 1 : 0;
uint64 reply_userdata, return this.set_property (name, Format.INT64, &flag);
[CCode (array_length = false)] }
string[] args);
[CCode (cname = "mpv_command")]
public Error command ([CCode (array_length = false)] string[] args);
[CCode (cname = "mpv_observe_property")] [CCode (cname = "mpv_observe_property")]
public int observe_property (uint64 reply_userdata, string name, Format format); public Error observe_property (uint64 reply_userdata, string name, Format format);
} }
[CCode (cname = "mpv_format", cprefix = "MPV_FORMAT_", has_type_id = false)] [CCode (cname = "mpv_format", cprefix = "MPV_FORMAT_", has_type_id = false)]
@ -71,31 +102,40 @@ namespace Mpv {
PROPERTY_CHANGE, PROPERTY_CHANGE,
QUEUE_OVERFLOW, QUEUE_OVERFLOW,
HOOK, HOOK,
// deprecated
IDLE,
TICK,
} }
[CCode (cname = "mpv_event")] [CCode (cname = "mpv_event", destroy_function = "", has_type_id = false, has_copy_function = false)]
public struct Event { public struct Event {
EventId event_id; EventId event_id;
int error; Error error;
uint64 reply_userdata; uint64 reply_userdata;
void *data; void *data;
public unowned EventProperty? parse_property ()
requires (event_id == EventId.PROPERTY_CHANGE)
requires (error >= 0)
{
return (Mpv.EventProperty?) data;
}
} }
[CCode (cname = "mpv_event_start_file")] [CCode (cname = "mpv_event_property", destroy_function = "", has_type_id = false, has_copy_function = false)]
public struct EventStartFile {
int64 playlist_entry_id;
}
[CCode (cname = "mpv_event_property")]
public struct EventProperty { public struct EventProperty {
string name; string name;
Format format; Format format;
void *data; void *data;
public int64 parse_int64 ()
requires (format == Format.INT64)
{
return * (int64 *) data;
}
public double parse_double ()
requires (format == Format.DOUBLE)
{
return * (double *) data;
}
} }
} }