Skip to content

Commit

Permalink
Move playback and pipeline to PlaybackManager (#321)
Browse files Browse the repository at this point in the history
  • Loading branch information
danirabbit committed Jan 15, 2023
1 parent 7eeb435 commit adaa34f
Show file tree
Hide file tree
Showing 8 changed files with 307 additions and 269 deletions.
1 change: 1 addition & 0 deletions po/POTFILES
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ src/Window.vala
src/Objects/Video.vala
src/Services/DirectoryMonitoring.vala
src/Services/LibraryManager.vala
src/Services/PlaybackManager.vala
src/Services/Thumbnailer.vala
src/Widgets/UnsupportedFileDialog.vala
src/Widgets/WelcomePage.vala
Expand Down
257 changes: 254 additions & 3 deletions src/Services/PlaybackManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,27 @@ public class Audience.PlaybackManager : Object {
public signal bool next ();
public signal File? get_first_item ();
public signal void clear_playlist (bool should_stop = true);
public signal void ended ();
public signal void item_added ();
public signal void next_audio ();
public signal void next_text ();
public signal void play (File file);
public signal void previous ();
public signal void queue_file (File file);
public signal void save_playlist ();
public signal void set_current (string current_file);
public signal void stop ();
public signal void uri_changed (string uri);

public string? subtitle_uri { get; set; }
public ClutterGst.Playback playback { get; private set; }
public string? subtitle_uri { get; private set; }

private unowned Gst.Pipeline pipeline {
get {
return (Gst.Pipeline) playback.get_pipeline ();
}
}

private uint inhibit_token = 0;
private ulong ready_handler_id = 0;

private static GLib.Once<PlaybackManager> instance;
public static unowned PlaybackManager get_default () {
Expand All @@ -26,6 +36,153 @@ public class Audience.PlaybackManager : Object {

private PlaybackManager () {}

construct {
unowned var default_application = (Gtk.Application) Application.get_default ();

playback = new ClutterGst.Playback ();
playback.set_seek_flags (ClutterGst.SeekFlags.ACCURATE);

default_application.action_state_changed.connect ((name, new_state) => {
if (name == Audience.App.ACTION_PLAY_PAUSE) {
playback.playing = new_state.get_boolean ();
}
});

playback.notify["playing"].connect (() => {
var play_pause_action = default_application.lookup_action (Audience.App.ACTION_PLAY_PAUSE);
((SimpleAction) play_pause_action).set_state (playback.playing);

if (playback.playing) {
if (inhibit_token != 0) {
default_application.uninhibit (inhibit_token);
}

inhibit_token = default_application.inhibit (
default_application.active_window,
Gtk.ApplicationInhibitFlags.IDLE | Gtk.ApplicationInhibitFlags.SUSPEND,
_("A video is playing")
);
} else if (inhibit_token != 0) {
default_application.uninhibit (inhibit_token);
inhibit_token = 0;
}
});

playback.eos.connect (() => {
Idle.add (() => {
playback.progress = 0;
if (!next ()) {
var repeat_action = default_application.lookup_action (Audience.App.ACTION_REPEAT);
if (repeat_action.get_state ().get_boolean ()) {
var file = get_first_item ();
((Audience.Window) default_application.active_window).open_files ({ file });
} else {
pipeline.set_state (Gst.State.NULL);
settings.set_double ("last-stopped", 0);
ended ();
}
}
return false;
});
});

/* playback.subtitle_uri does not seem to notify so connect directly to the pipeline */
pipeline.notify["suburi"].connect (() => {
if (subtitle_uri != playback.subtitle_uri) {
subtitle_uri = playback.subtitle_uri;
}
});

playback.notify ["uri"].connect (() => {
uri_changed (playback.uri);
});
}

~PlaybackManager () {
// FIXME:should find better way to decide if its end of playlist
if (playback.progress > 0.99) {
settings.set_double ("last-stopped", 0);
} else if (playback.uri != "") {
/* The progress is only valid if the uri has not been reset as the current video setting is not
* updated. The playback.uri has been reset when the window is destroyed from the Welcome page */
settings.set_double ("last-stopped", playback.progress);
}

save_playlist ();

if (inhibit_token != 0) {
((Gtk.Application) GLib.Application.get_default ()).uninhibit (inhibit_token);
inhibit_token = 0;
}
}

public void play_file (string uri, bool from_beginning = true) {
debug ("Opening %s", uri);
pipeline.set_state (Gst.State.NULL);
var file = File.new_for_uri (uri);
try {
var info = file.query_info (GLib.FileAttribute.STANDARD_CONTENT_TYPE + "," + GLib.FileAttribute.STANDARD_NAME, 0);
unowned string content_type = info.get_content_type ();

if (!GLib.ContentType.is_a (content_type, "video/*")) {
debug ("Unrecognized file format: %s", content_type);
var unsupported_file_dialog = new UnsupportedFileDialog (uri, info.get_name (), content_type);
unsupported_file_dialog.present ();

unsupported_file_dialog.response.connect (type => {
if (type == Gtk.ResponseType.CANCEL) {
// Play next video if available or else go to welcome page
if (!next ()) {
ended ();
}
}

unsupported_file_dialog.destroy ();
});
}
} catch (Error e) {
debug (e.message);
}

playback.uri = uri;

((Gtk.Application) Application.get_default ()).active_window.title = get_title (uri);

/* Set progress before subtitle uri else it gets reset to zero */
if (from_beginning) {
pipeline.seek_simple (Gst.Format.TIME, Gst.SeekFlags.FLUSH, 0);
} else {
set_progress (settings.get_double ("last-stopped"));
}

if (!from_beginning) { //We are resuming the current video - fetch the current subtitles
/* Should not bind to this setting else may cause loop */
set_subtitle (settings.get_string ("current-external-subtitles-uri"));
} else {
set_subtitle (get_subtitle_for_uri (uri));
}

pipeline.set_state (Gst.State.PLAYING);
Gtk.RecentManager.get_default ().add_item (uri);

settings.set_string ("current-video", uri);
}

public void stop () {
settings.set_double ("last-stopped", 0);
settings.set_strv ("last-played-videos", {});
settings.set_string ("current-video", "");

/* We do not want to emit an "ended" signal if already ended - it can cause premature
* ending of next video and other side-effects
*/
if (playback.playing) {
pipeline.set_state (Gst.State.NULL);
playback.progress = 1.0;
ended ();
}
}

public void append_to_playlist (File file) {
if (is_subtitle (file.get_uri ())) {
subtitle_uri = file.get_uri ();
Expand All @@ -47,4 +204,98 @@ public class Audience.PlaybackManager : Object {

return false;
}

public unowned List<string> get_audio_tracks () {
return playback.get_audio_streams ();
}

public unowned List<string> get_subtitle_tracks () {
return playback.get_subtitle_tracks ();
}

public string get_uri () {
return playback.uri;
}

public bool get_playing () {
var state = Gst.State.NULL;
var pending = Gst.State.NULL;
pipeline.get_state (out state, out pending, 300);

return state == Gst.State.PLAYING;
}

public double get_duration () {
return playback.duration;
}

public int get_audio_track () {
return playback.audio_stream;
}

public void set_audio_track (int track) {
playback.audio_stream = track;
}

public double get_progress () {
return playback.progress;
}

public void set_progress (double progress) {
playback.progress = progress;
}

public int get_subtitle_track () {
return playback.subtitle_track;
}

public void set_subtitle_track (int track) {
playback.subtitle_track = track;
}

public void set_subtitle (string uri) {
var progress = playback.progress;
var is_playing = playback.playing;

/* Temporarily connect to the ready signal so that we can restore the progress setting
* after resetting the pipeline in order to set the subtitle uri */
ready_handler_id = playback.ready.connect (() => {
playback.progress = progress;
// Pause video if it was in Paused state before adding the subtitle
if (!is_playing) {
pipeline.set_state (Gst.State.PAUSED);
}

playback.disconnect (ready_handler_id);
});

pipeline.set_state (Gst.State.NULL); // Does not work otherwise
playback.set_subtitle_uri (uri);
pipeline.set_state (Gst.State.PLAYING);

settings.set_string ("current-external-subtitles-uri", uri);
}

private string get_subtitle_for_uri (string uri) {
/* This assumes that the subtitle file has the same basename as the video file but with
* one of the subtitle extensions, and is in the same folder. */
string without_ext;
int last_dot = uri.last_index_of (".", 0);
int last_slash = uri.last_index_of ("/", 0);

if (last_dot < last_slash) {//we dont have extension
without_ext = uri;
} else {
without_ext = uri.slice (0, last_dot);
}

foreach (string ext in SUBTITLE_EXTENSIONS) {
string sub_uri = without_ext + "." + ext;
if (File.new_for_uri (sub_uri).query_exists ()) {
return sub_uri;
}
}

return "";
}
}
8 changes: 4 additions & 4 deletions src/Widgets/Player/BottomBar.vala
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ public class Audience.Widgets.BottomBar : Gtk.Revealer {
private const string PULSE_CLASS = "pulse";
private const string PULSE_TYPE = "attention";

public SettingsPopover preferences_popover { get; private set; }
public PlaylistPopover playlist_popover { get; private set; }
public Videos.SeekBar time_widget { get; private set; }

private SettingsPopover preferences_popover;
private Gtk.Button play_button;
private Gtk.MenuButton playlist_button;
private uint hiding_timer = 0;
Expand All @@ -49,7 +49,7 @@ public class Audience.Widgets.BottomBar : Gtk.Revealer {
}
}

public BottomBar (ClutterGst.Playback playback) {
public BottomBar () {
play_button = new Gtk.Button.from_icon_name ("media-playback-start-symbolic", Gtk.IconSize.BUTTON) {
action_name = App.ACTION_PREFIX + App.ACTION_PLAY_PAUSE,
tooltip_text = _("Play")
Expand All @@ -63,15 +63,15 @@ public class Audience.Widgets.BottomBar : Gtk.Revealer {
tooltip_text = _("Playlist")
};

preferences_popover = new SettingsPopover (playback);
preferences_popover = new SettingsPopover ();

var preferences_button = new Gtk.MenuButton () {
image = new Gtk.Image.from_icon_name ("open-menu-symbolic", Gtk.IconSize.SMALL_TOOLBAR),
popover = preferences_popover,
tooltip_text = _("Settings")
};

time_widget = new Videos.SeekBar (playback);
time_widget = new Videos.SeekBar ();

var main_actionbar = new Gtk.ActionBar ();
main_actionbar.pack_start (play_button);
Expand Down
Loading

0 comments on commit adaa34f

Please sign in to comment.