[shotwell/wip/phako/external-gstreamer: 5/5] wip: Add external video metadata subprocess



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]