Commit d32d0276 authored by Thibault Saunier's avatar Thibault Saunier

Do not commit timeline during splitting section

If we commit a timeline while splitting clips we might end up commiting
"half splited" clips. This happens because while splitting, the keyframe
are moved and `KeyframeCurve.__controlSourceChangedCb` is called.

Introduce a context manager on the pipeline so that we ensure that
a section of code is commited atomically.

Fixes #2251
parent 6b78b26d
Pipeline #43137 failed with stages
in 54 minutes and 21 seconds
...@@ -1836,19 +1836,23 @@ class TimelineContainer(Gtk.Grid, Zoomable, Loggable): ...@@ -1836,19 +1836,23 @@ class TimelineContainer(Gtk.Grid, Zoomable, Loggable):
position = self._project.pipeline.getPosition() position = self._project.pipeline.getPosition()
splitted = False splitted = False
for clip in clips:
start = clip.get_start() with self._project.pipeline.commit_timeline_after():
end = start + clip.get_duration() for clip in clips:
if start < position and end > position: start = clip.get_start()
clip.get_layer().splitting_object = True end = start + clip.get_duration()
if start < position and end > position:
self.app.write_action("split-clip", layer = clip.get_layer()
clip_name=clip.get_name(), layer.splitting_object = True
position=float(position / Gst.SECOND)) try:
self.app.write_action("split-clip",
clip.split(position) clip_name=clip.get_name(),
clip.get_layer().splitting_object = False position=float(position / Gst.SECOND))
splitted = True
clip.split(position)
splitted = True
finally:
layer.splitting_object = False
if not splitted and splitting_selection: if not splitted and splitting_selection:
self._splitElements() self._splitElements()
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
# the Free Software Foundation; either version 3, or (at your option) # the Free Software Foundation; either version 3, or (at your option)
# any later version. # any later version.
"""High-level pipelines.""" """High-level pipelines."""
import contextlib
import os import os
from gi.repository import GES from gi.repository import GES
...@@ -533,6 +534,7 @@ class Pipeline(GES.Pipeline, SimplePipeline): ...@@ -533,6 +534,7 @@ class Pipeline(GES.Pipeline, SimplePipeline):
self._was_empty = False self._was_empty = False
self._commit_wanted = False self._commit_wanted = False
self._prevent_commits = 0
if "watchdog" in os.environ.get("PITIVI_UNSTABLE_FEATURES", ''): if "watchdog" in os.environ.get("PITIVI_UNSTABLE_FEATURES", ''):
watchdog = Gst.ElementFactory.make("watchdog", None) watchdog = Gst.ElementFactory.make("watchdog", None)
...@@ -623,8 +625,18 @@ class Pipeline(GES.Pipeline, SimplePipeline): ...@@ -623,8 +625,18 @@ class Pipeline(GES.Pipeline, SimplePipeline):
else: else:
SimplePipeline._busMessageCb(self, bus, message) SimplePipeline._busMessageCb(self, bus, message)
@contextlib.contextmanager
def commit_timeline_after(self):
self._prevent_commits += 1
self.info("Disabling commits during action execution")
try:
yield
finally:
self._prevent_commits -= 1
self.commit_timeline()
def commit_timeline(self): def commit_timeline(self):
if self.getState() == Gst.State.NULL: if self._prevent_commits > 0 or self.getState() == Gst.State.NULL:
# No need to commit. NLE will do it automatically when # No need to commit. NLE will do it automatically when
# changing state from READY to PAUSED. # changing state from READY to PAUSED.
return return
......
...@@ -133,3 +133,25 @@ class TestPipeline(common.TestCase): ...@@ -133,3 +133,25 @@ class TestPipeline(common.TestCase):
self.assertEqual(pipe._recovery_state, SimplePipeline.RecoveryState.NOT_RECOVERING) self.assertEqual(pipe._recovery_state, SimplePipeline.RecoveryState.NOT_RECOVERING)
self.assertFalse(pipe._busy_async) self.assertFalse(pipe._busy_async)
self.assertIsNone(pipe._next_seek) self.assertIsNone(pipe._next_seek)
def test_commit_timeline_after(self):
"""Checks the recovery mechanism."""
pipe = Pipeline(common.create_pitivi_mock())
timeline = GES.Timeline()
pipe.set_timeline(timeline)
with mock.patch.object(pipe, "getState") as get_state:
get_state.return_value = (0, Gst.State.PAUSED, 0)
with mock.patch.object(timeline, "commit") as commit:
with pipe.commit_timeline_after():
pipe.commit_timeline()
self.assertEqual(commit.call_count, 1)
with mock.patch.object(timeline, "commit") as commit:
with pipe.commit_timeline_after():
self.assertEqual(pipe._prevent_commits, 1)
with pipe.commit_timeline_after():
self.assertEqual(pipe._prevent_commits, 2)
pipe.commit_timeline()
self.assertEqual(commit.call_count, 0)
self.assertEqual(commit.call_count, 1)
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment