[shotwell/wip/phako/external-gstreamer: 5/5] wip: Add external video metadata subprocess
- From: Jens Georg <jensgeorg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [shotwell/wip/phako/external-gstreamer: 5/5] wip: Add external video metadata subprocess
- Date: Sun, 12 May 2019 10:52:49 +0000 (UTC)
commit 19328ace10ecae384963fa1bc6959d99f1f369ea
Author: Jens Georg <mail jensge org>
Date: Sun May 12 12:51:23 2019 +0200
wip: Add external video metadata subprocess
src/AppDirs.vala | 10 ++
src/ExternalHelper.vala | 20 ++++
src/meson.build | 1 +
src/video-support/VideoMetadataInterface.vala | 4 +
src/video-support/VideoMetadataReaderProcess.vala | 118 ++++++++++++++++++++++
src/video-support/VideoReader.vala | 52 +++++++---
src/video-support/meson.build | 36 +++++++
src/video-support/util.vala | 15 +++
8 files changed, 240 insertions(+), 16 deletions(-)
---
diff --git a/src/AppDirs.vala b/src/AppDirs.vala
index 4d236d71..af17fa37 100644
--- a/src/AppDirs.vala
+++ b/src/AppDirs.vala
@@ -319,6 +319,16 @@ class AppDirs {
return f;
}
+ public static File get_metadata_helper() {
+ const string filename = "shotwell-video-metadata-handler";
+ File f = AppDirs.get_libexec_dir().get_child("video-support").get_child (filename);
+ if (!f.query_exists()) {
+ // If we're running installed.
+ f = AppDirs.get_libexec_dir () .get_child ("shotwell").get_child (filename);
+ }
+ return f;
+ }
+
public static File get_settings_migrator_bin() {
const string filename = "shotwell-settings-migrator";
File f = AppDirs.get_libexec_dir().get_child ("settings-migrator").get_child (filename);
diff --git a/src/ExternalHelper.vala b/src/ExternalHelper.vala
index ce715953..38ee27e4 100644
--- a/src/ExternalHelper.vala
+++ b/src/ExternalHelper.vala
@@ -1,3 +1,7 @@
+public errordomain LaunchError {
+ FAILED
+}
+
/*
* - Proxies interface access
*/
@@ -6,6 +10,7 @@ public class ExternalProxy<G> : Object, Initable {
private DBusServer server;
private Subprocess remote_process;
private SourceFunc saved_get_remote_callback;
+ private uint startup_timeout;
public string dbus_path { get; construct set; }
public string remote_helper_path { get; construct set; }
@@ -58,10 +63,14 @@ public class ExternalProxy<G> : Object, Initable {
private bool on_new_connection(DBusServer server, DBusConnection connection) {
bool retval = true;
+ Source.remove(startup_timeout);
+ startup_timeout = 0;
+
try {
remote = connection.get_proxy_sync(null, dbus_path, DBusProxyFlags.DO_NOT_LOAD_PROPERTIES |
DBusProxyFlags.DO_NOT_CONNECT_SIGNALS,
null);
+ print("==============> got remote!\n");
} catch (Error error) {
critical("Failed to create DBus proxy: %s", error.message);
retval = false;
@@ -78,9 +87,20 @@ public class ExternalProxy<G> : Object, Initable {
if (remote_process == null) {
remote_process = new Subprocess(SubprocessFlags.NONE, remote_helper_path, "--address=" +
server.get_client_address());
remote_process.wait_async.begin(null, on_process_exited);
+ startup_timeout = Timeout.add_seconds(2, () => {
+ startup_timeout = 0;
+ saved_get_remote_callback();
+ critical("=====> Timeout");
+
+ return false;
+ });
}
yield;
+
+ if (remote_process == null) {
+ throw new LaunchError.FAILED("Failed to launch subprocess...");
+ }
}
private void on_process_exited(Object? source, AsyncResult res) {
diff --git a/src/meson.build b/src/meson.build
index 0f90bebb..63ed1b94 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -267,6 +267,7 @@ executable(
'video-support/Video.vala',
'video-support/VideoSourceCollection.vala',
'video-support/VideoMetadata.vala',
+ 'video-support/VideoMetadataInterface.vala',
'ExternalHelper.vala'
] + shotwell_resources + face_sources,
include_directories : vapi_incdir,
diff --git a/src/video-support/VideoMetadataInterface.vala b/src/video-support/VideoMetadataInterface.vala
new file mode 100644
index 00000000..7d25a2a8
--- /dev/null
+++ b/src/video-support/VideoMetadataInterface.vala
@@ -0,0 +1,4 @@
+[DBus (name = "org.gnome.Shotwell.VideoMetadata1")]
+public interface VideoMetadataReaderInterface : Object {
+ public abstract async uint64 get_duration(string uri) throws Error;
+}
diff --git a/src/video-support/VideoMetadataReaderProcess.vala
b/src/video-support/VideoMetadataReaderProcess.vala
new file mode 100644
index 00000000..1ca9668b
--- /dev/null
+++ b/src/video-support/VideoMetadataReaderProcess.vala
@@ -0,0 +1,118 @@
+using Gst;
+using Gst.PbUtils;
+
+static string address;
+static MainLoop loop;
+
+const OptionEntry[] options = {
+ { "address", 'a', 0, OptionArg.STRING, ref address, "Address of private bus", "ADDRESS" },
+ { null }
+};
+
+
+[DBus (name = "org.gnome.Shotwell.VideoMetadata1")]
+internal class MetadataReader : GLib.Object {
+ private Gst.PbUtils.Discoverer discoverer;
+
+ public MetadataReader() throws Error {
+ setup_reader();
+ }
+
+ public async uint64 get_duration(string uri) throws Error {
+ print("=> Request for uri %s\n", uri);
+ Error error = null;
+ Gst.PbUtils.DiscovererInfo? info = null;
+
+ var id = discoverer.discovered.connect (
+ (_info, _error) => {
+ info = _info;
+ error = _error;
+ get_duration.callback ();
+ });
+
+ var file = File.new_for_uri(uri);
+ var path = file.get_path();
+
+ if (path != null) {
+ uri = Filename.to_uri(path);
+ }
+
+ print("Before discover\n");
+ discoverer.discover_uri_async (uri);
+ yield;
+ discoverer.disconnect (id);
+ print("After discover\n");
+
+ if (error != null) {
+ // Re-create discoverer, in error case it tends to get really
+ // slow.
+ setup_reader();
+
+ var result = info.get_result ();
+ if (result == Gst.PbUtils.DiscovererResult.TIMEOUT) {
+ debug ("Extraction timed out on %s", file.get_uri ());
+ } else if (result == Gst.PbUtils.DiscovererResult.MISSING_PLUGINS) {
+ debug ("Plugins are missing for extraction of file %s",
+ file.get_uri ());
+ }
+
+ throw error;
+ }
+
+ return info.get_duration();
+ }
+
+ private void setup_reader() throws Error {
+ discoverer = new Gst.PbUtils.Discoverer (Gst.SECOND * 5);
+ discoverer.start();
+ }
+
+ public async void terminate() throws Error {
+ loop.quit();
+ }
+}
+
+private bool on_authorize_peer(DBusAuthObserver observer, IOStream stream, Credentials? credentials) {
+ debug("Observer trying to authorize for %s", credentials.to_string());
+
+ if (credentials == null) {
+ return false;
+ }
+
+ try {
+ if (!credentials.is_same_user(new Credentials())) {
+ return false;
+ }
+
+ return true;
+ } catch (Error error) {
+ return false;
+ }
+}
+
+int main(string[] args) {
+ var option_context = new OptionContext("- shotwell video metadata reader helper binary");
+ option_context.set_help_enabled(true);
+ option_context.add_main_entries(options, null);
+ option_context.add_group(Gst.init_get_option_group());
+
+ try {
+ option_context.parse (ref args);
+
+ var observer = new DBusAuthObserver();
+ observer.authorize_authenticated_peer.connect(on_authorize_peer);
+ var connection = new DBusConnection.for_address_sync(address,
DBusConnectionFlags.AUTHENTICATION_CLIENT,
+ observer, null);
+ connection.register_object ("/org/gnome/Shotwell/VideoMetadata1", new MetadataReader());
+
+ loop = new MainLoop(null, false);
+ loop.run();
+
+ } catch (Error error) {
+ critical("Failed to parse options: %s", error.message);
+
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/src/video-support/VideoReader.vala b/src/video-support/VideoReader.vala
index 06232df5..97a9081c 100644
--- a/src/video-support/VideoReader.vala
+++ b/src/video-support/VideoReader.vala
@@ -12,6 +12,20 @@ public errordomain VideoError {
// malformed data, etc.)
}
+internal class VideoMetadataReader : ExternalProxy<VideoMetadataReaderInterface>,
VideoMetadataReaderInterface {
+ public VideoMetadataReader() throws Error {
+ Object(dbus_path : "/org/gnome/Shotwell/VideoMetadata1", remote_helper_path :
AppDirs.get_metadata_helper().get_path());
+ init();
+ }
+
+ public async uint64 get_duration(string uri) throws Error {
+ print("Getting the duration of %s: \n", uri);
+ var r = yield get_remote();
+
+ return yield r.get_duration(uri);
+ }
+}
+
public class VideoReader {
private const double UNKNOWN_CLIP_DURATION = -1.0;
private const uint THUMBNAILER_TIMEOUT = 10000; // In milliseconds.
@@ -28,6 +42,8 @@ public class VideoReader {
public VideoReader(File file) {
this.file = file;
}
+
+ private static VideoMetadataReader reader;
public static bool is_supported_video_file(File file) {
var mime_type = ContentType.guess(file.get_basename(), new uchar[0], null);
@@ -179,24 +195,28 @@ public class VideoReader {
if (!does_file_exist())
throw new VideoError.FILE("video file '%s' does not exist or is inaccessible".printf(
file.get_path()));
+
+ if (VideoReader.reader == null) {
+ try {
+ VideoReader.reader = new VideoMetadataReader();
+ } catch (Error error) {
+ critical("Failed to create Videometadataeader: %s", error.message);
+ }
+ }
try {
- Gst.PbUtils.Discoverer d = new Gst.PbUtils.Discoverer((Gst.ClockTime) (Gst.SECOND * 5));
- Gst.PbUtils.DiscovererInfo info = d.discover_uri(file.get_uri());
-
- clip_duration = ((double) info.get_duration()) / 1000000000.0;
-
- // Get creation time.
- // TODO: Note that TAG_DATE can be changed to TAG_DATE_TIME in the future
- // (and the corresponding output struct) in order to implement #2836.
- Date? video_date = null;
- if (info.get_tags() != null && info.get_tags().get_date(Gst.Tags.DATE, out video_date)) {
- // possible for get_date() to return true and a null Date
- if (video_date != null) {
- timestamp = new DateTime.local(video_date.get_year(), video_date.get_month(),
- video_date.get_day(), 0, 0, 0);
- }
- }
+ var context = new MainContext();
+ context.push_thread_default();
+ var loop = new MainLoop(context, false);
+ AsyncResult result = null;
+
+ reader.get_duration.begin(file.get_uri(), (obj, res) => {
+ result = res;
+ loop.quit();
+ });
+ loop.run();
+ clip_duration = ((double) reader.get_duration.end(result)) / 1000000000.0;
+ context.pop_thread_default();
} catch (Error e) {
debug("Video read error: %s", e.message);
throw new VideoError.CONTENTS("GStreamer couldn't extract clip information: %s"
diff --git a/src/video-support/meson.build b/src/video-support/meson.build
new file mode 100644
index 00000000..da3f9d73
--- /dev/null
+++ b/src/video-support/meson.build
@@ -0,0 +1,36 @@
+executable(
+ 'shotwell-video-metadata-handler',
+ [
+ 'VideoMetadataReaderProcess.vala'
+ ],
+ dependencies : [
+ gio,
+ gstreamer,
+ gstreamer_pbu
+ ],
+ c_args : '-DGST_PB_UTILS_IS_DISCOVERER_INFO=GST_IS_DISCOVERER_INFO'
+ # Work-around for wrong type-check macro generated by valac
+)
+
+libvideometadata_handling = static_library(
+ 'video_metadata_handling',
+ [
+ 'AVIChunk.vala',
+ 'AVIMetadataLoader.vala',
+ 'QuickTimeAtom.vala',
+ 'QuicktimeMetdataLoader.vala',
+ 'util.vala'
+ ],
+ vala_header : 'shotwell-internal-video-metadata-handling.h',
+ vala_vapi : 'shotwell-internal-video-metadata-handling.vapi',
+ include_directories : config_incdir,
+ dependencies : [
+ gio,
+ metadata
+ ]
+)
+
+metadata_handling = declare_dependency(
+ include_directories : include_directories('.'),
+ link_with : libvideometadata_handling
+)
diff --git a/src/video-support/util.vala b/src/video-support/util.vala
new file mode 100644
index 00000000..942d7144
--- /dev/null
+++ b/src/video-support/util.vala
@@ -0,0 +1,15 @@
+// Breaks a uint64 skip amount into several smaller skips.
+public void skip_uint64(InputStream input, uint64 skip_amount) throws GLib.Error {
+ while (skip_amount > 0) {
+ // skip() throws an error if the amount is too large, so check against ssize_t.MAX
+ if (skip_amount >= ssize_t.MAX) {
+ input.skip(ssize_t.MAX);
+ skip_amount -= ssize_t.MAX;
+ } else {
+ input.skip((size_t) skip_amount);
+ skip_amount = 0;
+ }
+ }
+}
+
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]