[pitivi] Handle proxy duration not matching its original file duration
- From: Thibault Saunier <tsaunier src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pitivi] Handle proxy duration not matching its original file duration
- Date: Mon, 9 Mar 2020 14:11:13 +0000 (UTC)
commit 13181372c12d0fe0c15898563e3e5eb7a9db81a3
Author: Thibault Saunier <tsaunier igalia com>
Date: Tue Mar 3 15:27:07 2020 -0300
Handle proxy duration not matching its original file duration
Fixes https://gitlab.gnome.org/GNOME/pitivi/issues/2324
pitivi/check.py | 15 +++++++-------
pitivi/timeline/timeline.py | 48 ++++++++++++++++++++++++---------------------
pitivi/utils/misc.py | 16 +++++++++++++++
pitivi/utils/proxy.py | 46 +++++++++++++++++++++++++++++++++----------
tests/test_medialibrary.py | 26 ++++++++++++++++++++++++
5 files changed, 112 insertions(+), 39 deletions(-)
---
diff --git a/pitivi/check.py b/pitivi/check.py
index 253cb8b9..41daec7d 100644
--- a/pitivi/check.py
+++ b/pitivi/check.py
@@ -376,13 +376,6 @@ def initialize_modules():
require_version("GstPbutils", GST_API_VERSION)
from gi.repository import GstPbutils
- from pitivi.utils.misc import video_info_get_natural_height, video_info_get_natural_width,
video_info_get_rotation
-
- # Monkey patch a helper method for retrieving the size of a video
- # when using square pixels.
- GstPbutils.DiscovererVideoInfo.get_natural_width = video_info_get_natural_width
- GstPbutils.DiscovererVideoInfo.get_natural_height = video_info_get_natural_height
- GstPbutils.DiscovererVideoInfo.get_rotation = video_info_get_rotation
if not os.environ.get("GES_DISCOVERY_TIMEOUT"):
os.environ["GES_DISCOVERY_TIMEOUT"] = "5"
@@ -394,6 +387,14 @@ def initialize_modules():
# Monkey patch deprecated methods to use the new variant by default
GES.TrackElement.list_children_properties = GES.TimelineElement.list_children_properties
+ from pitivi.utils.misc import video_info_get_natural_height, video_info_get_natural_width,
video_info_get_rotation
+
+ # Monkey patch a helper method for retrieving the size of a video
+ # when using square pixels.
+ GstPbutils.DiscovererVideoInfo.get_natural_width = video_info_get_natural_width
+ GstPbutils.DiscovererVideoInfo.get_natural_height = video_info_get_natural_height
+ GstPbutils.DiscovererVideoInfo.get_rotation = video_info_get_rotation
+
from pitivi.utils import validate
if validate.init() and "--inspect-action-type" in sys.argv:
try:
diff --git a/pitivi/timeline/timeline.py b/pitivi/timeline/timeline.py
index f561dca9..fc5455e3 100644
--- a/pitivi/timeline/timeline.py
+++ b/pitivi/timeline/timeline.py
@@ -42,6 +42,7 @@ from pitivi.timeline.previewers import Previewer
from pitivi.timeline.ruler import ScaleRuler
from pitivi.undo.timeline import CommitTimelineFinalizingAction
from pitivi.utils.loggable import Loggable
+from pitivi.utils.misc import asset_get_duration
from pitivi.utils.proxy import get_proxy_target
from pitivi.utils.timeline import EditingContext
from pitivi.utils.timeline import SELECT
@@ -915,6 +916,27 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
for ges_layer in self.ges_timeline.get_layers():
ges_layer.ui.update_position()
+ def add_clip_to_layer(self, ges_layer, asset, start):
+ if asset.is_image():
+ clip_duration = self.app.settings.imageClipLength * \
+ Gst.SECOND / 1000.0
+ max_duration = 0
+ else:
+ clip_duration = asset_get_duration(asset)
+ max_duration = clip_duration
+
+ ges_clip = ges_layer.add_asset(asset, start, 0, clip_duration,
+ asset.get_supported_formats())
+ if not ges_clip:
+ return ges_clip
+
+ # Tell GES that the max duration is our newly compute max duration
+ # so it has the proper information when doing timeline editing.
+ if max_duration and ges_clip.props.max_duration > max_duration:
+ ges_clip.props.max_duration = max_duration
+ return ges_clip
+
+
def __create_clips(self, x, y):
"""Creates the clips for an asset drag operation.
@@ -932,11 +954,6 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
ges_clips = []
for asset in assets:
- if asset.is_image():
- clip_duration = self.app.settings.imageClipLength * \
- Gst.SECOND / 1000.0
- else:
- clip_duration = asset.get_duration()
ges_layer, unused_on_sep = self.get_layer_at(y)
if not placement:
@@ -945,15 +962,11 @@ class Timeline(Gtk.EventBox, Zoomable, Loggable):
self.debug("Creating %s at %s", asset.props.id, Gst.TIME_ARGS(placement))
- ges_clip = ges_layer.add_asset(asset,
- placement,
- 0,
- clip_duration,
- asset.get_supported_formats())
+ ges_clip = self.add_clip_to_layer(ges_layer, asset, placement)
if not ges_clip:
return False
- placement += clip_duration
+ placement += ges_clip.props.duration
ges_clip.first_placement = True
self._project.pipeline.commit_timeline()
@@ -1486,17 +1499,8 @@ class TimelineContainer(Gtk.Grid, Zoomable, Loggable):
layer.add_clip(obj)
duration = obj.get_duration()
elif isinstance(obj, GES.Asset):
- if obj.is_image():
- duration = self.app.settings.imageClipLength * \
- Gst.SECOND / 1000.0
- else:
- duration = obj.get_duration()
-
- layer.add_asset(obj,
- start=clip_position,
- inpoint=0,
- duration=duration,
- track_types=obj.get_supported_formats())
+ ges_clip = self.timeline.add_clip_to_layer(layer, obj, clip_position)
+ duration = ges_clip.props.duration
else:
raise TimelineError("Cannot insert: %s" % type(obj))
clip_position += duration
diff --git a/pitivi/utils/misc.py b/pitivi/utils/misc.py
index ba844e76..84e29a50 100644
--- a/pitivi/utils/misc.py
+++ b/pitivi/utils/misc.py
@@ -26,6 +26,7 @@ from urllib.parse import urlsplit
from gi.repository import GdkPixbuf
from gi.repository import GLib
+from gi.repository import GES
from gi.repository import Gst
from gi.repository import Gtk
@@ -35,6 +36,9 @@ from pitivi.configure import APPMANUALURL_ONLINE
from pitivi.utils.threads import Thread
+ASSET_DURATION_META = "pitivi:asset-duration"
+
+
def scale_pixbuf(pixbuf, width, height):
"""Scales the given pixbuf preserving the original aspect ratio."""
pixbuf_width = pixbuf.props.width
@@ -455,3 +459,15 @@ def video_info_get_natural_height(video_info):
return _get_square_width(video_info)
return video_info.get_height()
+
+def asset_get_duration(asset):
+ assert isinstance(asset, GES.UriClipAsset)
+
+ # Use pitivi information before the information provided by GStreamer.
+ # We can't handle that kind of information in GES itself as it is
+ # related to Pitivi' own way of handling proxies.
+ res, duration = asset.get_uint64(ASSET_DURATION_META)
+ if res:
+ return duration
+
+ return asset.get_duration()
\ No newline at end of file
diff --git a/pitivi/utils/proxy.py b/pitivi/utils/proxy.py
index d80f9cf8..54dde5fd 100644
--- a/pitivi/utils/proxy.py
+++ b/pitivi/utils/proxy.py
@@ -29,6 +29,8 @@ from gi.repository import GstTranscoder
from pitivi.configure import get_gstpresets_dir
from pitivi.dialogs.prefs import PreferencesDialog
+from pitivi.utils.misc import ASSET_DURATION_META
+from pitivi.utils.misc import asset_get_duration
from pitivi.settings import GlobalSettings
from pitivi.utils.loggable import Loggable
@@ -319,7 +321,10 @@ class ProxyManager(GObject.Object, Loggable):
if cls.is_scaled_proxy(uri):
return ".".join(uri.split(".")[:-4])
- return ".".join(uri.split(".")[:-3])
+ if cls.is_proxy_asset(uri):
+ return ".".join(uri.split(".")[:-3])
+
+ return uri
def get_proxy_uri(self, asset, scaled=False):
"""Gets the URI of the corresponding proxy file for the specified asset.
@@ -457,15 +462,36 @@ class ProxyManager(GObject.Object, Loggable):
del transcoder
- if asset.get_info().get_duration() != proxy.get_info().get_duration():
- self.error(
- "Asset %s (duration=%s) and created proxy %s (duration=%s) do not"
- " have the same duration this should *never* happen, please file"
- " a bug with the media files." % (
- asset.get_id(), Gst.TIME_ARGS(asset.get_info().get_duration()),
- proxy.get_id(), Gst.TIME_ARGS(proxy.get_info().get_duration())
- )
- )
+ asset_duration = asset_get_duration(asset)
+ proxy_duration = asset_get_duration(proxy)
+ if asset_duration != proxy_duration:
+ duration = min(asset_duration, proxy_duration)
+
+ self.info("Reseting %s duration from %s to %s as"
+ " new proxy has a different duration",
+ asset.props.id, Gst.TIME_ARGS(asset_duration), Gst.TIME_ARGS(duration))
+ asset.set_uint64(ASSET_DURATION_META, duration)
+ proxy.set_uint64(ASSET_DURATION_META, duration)
+ target_uri = self.get_target_uri(asset)
+
+ for clip in self.app.project_manager.current_project.ges_timeline.iter_clips():
+ if self.get_target_uri(clip.props.uri) == target_uri:
+ if clip.props.in_point + clip.props.duration > duration:
+ new_duration = duration - clip.props.in_point
+ if new_duration > 0:
+ self.warning("%s reseting duration to %s as"
+ " new proxy has a shorter duration",
+ clip, Gst.TIME_ARGS(new_duration))
+ clip.set_duration(new_duration)
+ else:
+ new_inpoint = new_duration - clip.props.in_point
+ self.error("%s reseting duration to %s"
+ " and inpoint to %s as the proxy"
+ " is shorter",
+ clip, Gst.TIME_ARGS(new_duration), Gst.TIME_ARGS(new_inpoint))
+ clip.set_inpoint(new_inpoint)
+ clip.set_duration(duration - new_inpoint)
+ clip.set_max_duration(duration)
if shadow:
self.app.project_manager.current_project.finalize_proxy(proxy)
diff --git a/tests/test_medialibrary.py b/tests/test_medialibrary.py
index a05a38e0..b2a505d5 100644
--- a/tests/test_medialibrary.py
+++ b/tests/test_medialibrary.py
@@ -22,6 +22,7 @@ from unittest import mock
from gi.repository import Gdk
from gi.repository import GES
+from gi.repository import GObject
from gi.repository import Gst
from pitivi import medialibrary
@@ -29,6 +30,8 @@ from pitivi.project import ProjectManager
from pitivi.utils.proxy import ProxyingStrategy
from pitivi.utils.validate import create_event
from tests import common
+from pitivi.utils.misc import asset_get_duration
+from pitivi.utils.misc import ASSET_DURATION_META
class BaseTestMediaLibrary(common.TestCase):
@@ -359,6 +362,29 @@ class TestMediaLibrary(BaseTestMediaLibrary):
proxy = self.check_add_proxy(asset, check_progress=False)
self.check_disable_proxy(proxy, asset, delete=True)
+ def test_proxy_duration_mismatch(self):
+ sample_name = "30fps_numeroted_frames_red.mkv"
+ with common.cloned_sample(sample_name):
+ self.check_import([sample_name], proxying_strategy=ProxyingStrategy.NOTHING)
+ timeline = self.app.project_manager.current_project.ges_timeline
+
+ asset = self.medialibrary.storemodel[0][medialibrary.COL_ASSET]
+ clip = timeline.append_layer().add_asset(asset, 0, 0, Gst.CLOCK_TIME_NONE, GES.TrackType.VIDEO)
+
+ duration = 2.5 * Gst.SECOND
+ fake_duration = 3 * Gst.SECOND
+
+ asset.set_uint64(ASSET_DURATION_META, fake_duration)
+ clip.props.max_duration = fake_duration
+ clip.props.duration = fake_duration
+ self.assertEqual(clip.props.duration, fake_duration)
+ proxy = self.check_add_proxy(asset)
+
+ self.assertEqual(asset_get_duration(asset), duration)
+ self.assertEqual(asset_get_duration(proxy), duration)
+ self.assertEqual(clip.props.duration, duration)
+ self.assertEqual(clip.props.max_duration, duration)
+
def test_regenerate_scaled_proxy(self):
sample_name = "30fps_numeroted_frames_red.mkv"
with common.cloned_sample(sample_name):
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]