Compare commits
No commits in common. "d17c538ccbec5fe4bb365be7eb5a2a3ef52ae9c2" and "9dc3559c2693028bea4623478d5a2a562d715bfa" have entirely different histories.
d17c538ccb
...
9dc3559c26
2 changed files with 162 additions and 97 deletions
167
src/playbin.vala
167
src/playbin.vala
|
@ -4,6 +4,16 @@ enum PlaybinState {
|
|||
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 {
|
||||
public SourceFunc inner;
|
||||
|
||||
|
@ -22,7 +32,7 @@ class Playbin : GLib.Object {
|
|||
get { return _volume; }
|
||||
set {
|
||||
_volume = value;
|
||||
assert (mpv.set_property_int64 ("volume", value) >= 0);
|
||||
mpv_set_property_int64.begin ("volume", value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,7 +41,7 @@ class Playbin : GLib.Object {
|
|||
get { return _mute; }
|
||||
set {
|
||||
_mute = value;
|
||||
assert (mpv.set_property_flag ("mute", value) >= 0);
|
||||
mpv_set_property_flag.begin ("mute", value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,32 +65,87 @@ class Playbin : GLib.Object {
|
|||
}
|
||||
|
||||
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) {
|
||||
assert (this.mpv.command ({
|
||||
"playlist-remove",
|
||||
position.to_string (),
|
||||
}) >= 0);
|
||||
this.mpv_command.begin ({"playlist-remove", position.to_string ()});
|
||||
}
|
||||
|
||||
for (uint i = 0; i < added; i += 1) {
|
||||
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);
|
||||
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 ()});
|
||||
}
|
||||
}
|
||||
|
||||
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 () {
|
||||
assert (this.mpv.initialize () >= 0);
|
||||
assert (this.mpv.set_property_string ("user-agent", Audrey.Const.user_agent) >= 0);
|
||||
assert (this.mpv.set_property_string ("video", "no") >= 0);
|
||||
assert (this.mpv.set_property_string ("prefetch-playlist", "yes") >= 0);
|
||||
assert (this.mpv.set_property_string ("gapless-audio", "yes") >= 0);
|
||||
assert (this.mpv.observe_property (0, "time-pos", Mpv.Format.DOUBLE) >= 0);
|
||||
assert (this.mpv.observe_property (1, "duration", Mpv.Format.DOUBLE) >= 0);
|
||||
assert (this.mpv.observe_property (2, "playlist-pos", Mpv.Format.INT64) >= 0);
|
||||
try {
|
||||
check_mpv_error (this.mpv.initialize ());
|
||||
check_mpv_error (this.mpv.set_property_string ("user-agent", Audrey.Const.user_agent));
|
||||
check_mpv_error (this.mpv.set_property_string ("video", "no"));
|
||||
check_mpv_error (this.mpv.set_property_string ("prefetch-playlist", "yes"));
|
||||
check_mpv_error (this.mpv.set_property_string ("gapless-audio", "yes"));
|
||||
check_mpv_error (this.mpv.observe_property (0, "time-pos", Mpv.Format.DOUBLE));
|
||||
check_mpv_error (this.mpv.observe_property (1, "duration", Mpv.Format.DOUBLE));
|
||||
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 = () => {
|
||||
Idle.add (() => {
|
||||
|
@ -89,33 +154,50 @@ class Playbin : GLib.Object {
|
|||
if (event.event_id == Mpv.EventId.NONE) break;
|
||||
|
||||
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:
|
||||
var data = event.parse_property ();
|
||||
switch (event.reply_userdata) {
|
||||
case 0:
|
||||
var data = (Mpv.EventProperty *) event.data;
|
||||
assert (data.name == "time-pos");
|
||||
if (data.format == Mpv.Format.NONE) {
|
||||
this.position = 0.0;
|
||||
} else {
|
||||
this.position = data.parse_double ();
|
||||
assert (data.format == Mpv.Format.DOUBLE);
|
||||
this.position = * (double *) data.data;
|
||||
}
|
||||
break;
|
||||
|
||||
case 1:
|
||||
var data = (Mpv.EventProperty *) event.data;
|
||||
assert (data.name == "duration");
|
||||
if (data.format == Mpv.Format.NONE) {
|
||||
this.duration = 0.0;
|
||||
} else {
|
||||
this.duration = data.parse_double ();
|
||||
assert (data.format == Mpv.Format.DOUBLE);
|
||||
this.duration = * (double *) data.data;
|
||||
}
|
||||
break;
|
||||
|
||||
case 2:
|
||||
var data = (Mpv.EventProperty *) event.data;
|
||||
assert (data.name == "playlist-pos");
|
||||
if (data.format == Mpv.Format.NONE) {
|
||||
this.play_queue_position = 0;
|
||||
} else {
|
||||
this.play_queue_position = (uint) data.parse_int64 ();
|
||||
assert (data.format == Mpv.Format.INT64);
|
||||
this.play_queue_position = (uint) * (int64 *) data.data;
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -125,13 +207,36 @@ class Playbin : GLib.Object {
|
|||
}
|
||||
break;
|
||||
|
||||
case Mpv.EventId.START_FILE:
|
||||
// ignore
|
||||
break;
|
||||
|
||||
case Mpv.EventId.FILE_LOADED:
|
||||
this.song = (Subsonic.Song) this.play_queue.get_item (this.play_queue_position);
|
||||
this.now_playing ();
|
||||
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:
|
||||
// ignore by default
|
||||
print ("got unimplemented %s\n", event.event_id.to_string ());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -143,38 +248,38 @@ class Playbin : GLib.Object {
|
|||
|
||||
public void seek (double position) {
|
||||
this.position = position;
|
||||
assert (this.mpv.command ({"seek", position.to_string (), "absolute"}) >= 0);
|
||||
this.mpv_command.begin ({"seek", position.to_string (), "absolute"});
|
||||
}
|
||||
|
||||
// manually changes which track in the play queue to play
|
||||
public void select_track (uint position)
|
||||
requires (position < this.play_queue.get_n_items ())
|
||||
{
|
||||
assert (this.mpv.command ({"playlist-play-index", position.to_string ()}) >= 0);
|
||||
this.mpv_command.begin ({"playlist-play-index", position.to_string ()});
|
||||
this.state = PlaybinState.PLAYING;
|
||||
}
|
||||
|
||||
public void pause () {
|
||||
assert (this.state != PlaybinState.STOPPED);
|
||||
this.state = PlaybinState.PAUSED;
|
||||
assert (this.mpv.command ({"pause"}) >= 0);
|
||||
this.mpv_command.begin ({"pause"});
|
||||
}
|
||||
|
||||
public void play () {
|
||||
assert (this.state != PlaybinState.STOPPED);
|
||||
this.state = PlaybinState.PLAYING;
|
||||
assert (this.mpv.command ({"play"}) >= 0);
|
||||
this.mpv_command.begin ({"play"});
|
||||
}
|
||||
|
||||
public void next_track () {
|
||||
assert (this.state != PlaybinState.STOPPED);
|
||||
this.state = PlaybinState.PLAYING;
|
||||
assert (this.mpv.command ({"playlist-next-playlist"}) >= 0);
|
||||
this.mpv_command.begin ({"playlist-next-playlist"});
|
||||
}
|
||||
|
||||
public void prev_track () {
|
||||
assert (this.state != PlaybinState.STOPPED);
|
||||
this.state = PlaybinState.PLAYING;
|
||||
assert (this.mpv.command ({"playlist-prev-playlist"}) >= 0);
|
||||
this.mpv_command.begin ({"playlist-prev-playlist"});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,33 +1,8 @@
|
|||
[CCode (cheader_filename = "mpv/client.h")]
|
||||
namespace Mpv {
|
||||
|
||||
[CCode (cname = "mpv_error", cprefix = "MPV_ERROR_", has_type_id = false)]
|
||||
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 unowned string error_string (int error);
|
||||
|
||||
public delegate void WakeupCallback ();
|
||||
|
||||
|
@ -38,35 +13,29 @@ namespace Mpv {
|
|||
public Handle ();
|
||||
|
||||
[CCode (cname = "mpv_initialize")]
|
||||
public Error initialize ();
|
||||
public int initialize ();
|
||||
|
||||
[CCode (cname = "mpv_wait_event")]
|
||||
public unowned Event? wait_event (double timeout);
|
||||
public unowned Event *wait_event (double timeout);
|
||||
|
||||
public WakeupCallback wakeup_callback {
|
||||
[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")]
|
||||
public Error set_property_string (string name, string value);
|
||||
public int set_property_string (string name, string data);
|
||||
|
||||
public Error set_property_int64 (string name, int64 value) {
|
||||
return this.set_property (name, Format.INT64, &value);
|
||||
}
|
||||
[CCode (cname = "mpv_set_property_async")]
|
||||
public int set_property_async (uint64 reply_userdata, string name, Format format, void *data);
|
||||
|
||||
public Error set_property_flag (string name, bool value) {
|
||||
int flag = value ? 1 : 0;
|
||||
return this.set_property (name, Format.INT64, &flag);
|
||||
}
|
||||
|
||||
[CCode (cname = "mpv_command")]
|
||||
public Error command ([CCode (array_length = false)] string[] args);
|
||||
[CCode (cname = "mpv_command_async")]
|
||||
public int command_async (
|
||||
uint64 reply_userdata,
|
||||
[CCode (array_length = false)]
|
||||
string[] args);
|
||||
|
||||
[CCode (cname = "mpv_observe_property")]
|
||||
public Error observe_property (uint64 reply_userdata, string name, Format format);
|
||||
public int observe_property (uint64 reply_userdata, string name, Format format);
|
||||
}
|
||||
|
||||
[CCode (cname = "mpv_format", cprefix = "MPV_FORMAT_", has_type_id = false)]
|
||||
|
@ -102,40 +71,31 @@ namespace Mpv {
|
|||
PROPERTY_CHANGE,
|
||||
QUEUE_OVERFLOW,
|
||||
HOOK,
|
||||
|
||||
// deprecated
|
||||
IDLE,
|
||||
TICK,
|
||||
}
|
||||
|
||||
[CCode (cname = "mpv_event", destroy_function = "", has_type_id = false, has_copy_function = false)]
|
||||
[CCode (cname = "mpv_event")]
|
||||
public struct Event {
|
||||
EventId event_id;
|
||||
Error error;
|
||||
int error;
|
||||
uint64 reply_userdata;
|
||||
void *data;
|
||||
|
||||
public unowned EventProperty? parse_property ()
|
||||
requires (event_id == EventId.PROPERTY_CHANGE)
|
||||
requires (error >= 0)
|
||||
{
|
||||
return (Mpv.EventProperty?) data;
|
||||
}
|
||||
}
|
||||
|
||||
[CCode (cname = "mpv_event_property", destroy_function = "", has_type_id = false, has_copy_function = false)]
|
||||
[CCode (cname = "mpv_event_start_file")]
|
||||
public struct EventStartFile {
|
||||
int64 playlist_entry_id;
|
||||
}
|
||||
|
||||
[CCode (cname = "mpv_event_property")]
|
||||
public struct EventProperty {
|
||||
string name;
|
||||
Format format;
|
||||
void *data;
|
||||
|
||||
public int64 parse_int64 ()
|
||||
requires (format == Format.INT64)
|
||||
{
|
||||
return * (int64 *) data;
|
||||
}
|
||||
|
||||
public double parse_double ()
|
||||
requires (format == Format.DOUBLE)
|
||||
{
|
||||
return * (double *) data;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue