Lay root clips at timeline positions, not source positions
The root timeline placed each clip at its media-in (source) frame, which is
fractional (media-offset 50641.358…), so adjacent clips of a contiguous multicam
edit showed ~1-frame gaps; clicking a gap hit no segment and local->source
returned the last frame ("glitch to the end"). The OTIO timeline is gapless —
clips tile by their accumulated timeline :start. Carry :start through from-otio
and lay content-segments at [start, start+len] (src stays the source range), so
the assembled program is contiguous and maps cleanly to scattered source frames.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
0d73b204e6
commit
8ee2cdf7b9
2 changed files with 18 additions and 10 deletions
|
|
@ -170,15 +170,19 @@
|
|||
"The clip-segments that make up context `ctx` — what you draw and select
|
||||
against. An annotation's marks reference clips, so that's just `resolve`. The
|
||||
root timeline doesn't enumerate its clips (they're a parentless pool), so
|
||||
there it's the pool laid in root's identity coordinate (local = source)."
|
||||
there it's the pool laid at each clip's TIMELINE position (:start) with :src =
|
||||
its source range — so the assembled program tiles contiguously even though
|
||||
the underlying source frames are scattered (and fractional)."
|
||||
[scene ctx]
|
||||
(if (= :timeline (:type (grp scene ctx)))
|
||||
(->> (:groups scene)
|
||||
(keep (fn [[gid g]]
|
||||
(when (= :clip (:type g))
|
||||
(let [seg (first (resolve scene gid))]
|
||||
(assoc seg :mark gid :local (:src seg)))))) ; ref clip by group id
|
||||
(sort-by (comp first :src))
|
||||
(let [seg (first (resolve scene gid))
|
||||
[sa sb] (:src seg)
|
||||
st (:start g 0)]
|
||||
(assoc seg :mark gid :local [st (+ st (- sb sa))])))))
|
||||
(sort-by (comp first :local))
|
||||
vec)
|
||||
(resolve scene ctx)))
|
||||
|
||||
|
|
@ -278,6 +282,7 @@
|
|||
clips (into {} (for [t vtracks c (:clips t)]
|
||||
[(keyword (:id c))
|
||||
{:type :clip :parent nil :name (:name c)
|
||||
:start (:start c) ; timeline position (frames)
|
||||
:marks [{:id (keyword (str (:id c) "-m"))
|
||||
:start (:media-in c)
|
||||
:end (+ (:media-in c) (:duration c))
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@
|
|||
(def base
|
||||
{:tracks {:t0 {:name "A-roll"} :t1 {:name "B-roll"} :t2 {:name "C-roll"}}
|
||||
:groups {:root {:type :timeline :parent nil :marks [{:id :m/root :start 0 :end 300}]}
|
||||
:clip-a {:type :clip :parent nil :marks [{:id :m/a :start 0 :end 100 :track :t0}]}
|
||||
:clip-b {:type :clip :parent nil :marks [{:id :m/b :start 100 :end 200 :track :t1}]}
|
||||
:clip-c {:type :clip :parent nil :marks [{:id :m/c :start 200 :end 300 :track :t2}]}}})
|
||||
;; :start = timeline position; here it equals source so root is identity
|
||||
:clip-a {:type :clip :parent nil :start 0 :marks [{:id :m/a :start 0 :end 100 :track :t0}]}
|
||||
:clip-b {:type :clip :parent nil :start 100 :marks [{:id :m/b :start 100 :end 200 :track :t1}]}
|
||||
:clip-c {:type :clip :parent nil :start 200 :marks [{:id :m/c :start 200 :end 300 :track :t2}]}}})
|
||||
|
||||
(defn with-group [scene gid g] (assoc-in scene [:groups gid] g))
|
||||
(defn refm [id clip a b] {:id id :start {:ref clip :at a} :end {:ref clip :at b}})
|
||||
|
|
@ -177,10 +178,12 @@
|
|||
|
||||
(deftest seed-from-otio
|
||||
(testing "from-otio seeds tracks + clip mark-groups + root; content-segments at root tiles the clips"
|
||||
;; two clips cut back to back on the timeline (:start 0 then 100), like a
|
||||
;; multicam program, even though their source frames aren't contiguous
|
||||
(let [parsed {:fps 24 :duration 200
|
||||
:tracks [{:index 0 :kind :video :name "Wide" :clips [{:id "t0-c0" :name "a" :media-in 0 :duration 100}]}
|
||||
{:index 1 :kind :video :name "CU" :clips [{:id "t1-c0" :name "b" :media-in 100 :duration 100}]}
|
||||
{:index 2 :kind :audio :name "Aud" :clips [{:id "t2-c0" :name "x" :media-in 0 :duration 50}]}]}
|
||||
:tracks [{:index 0 :kind :video :name "Wide" :clips [{:id "t0-c0" :name "a" :start 0 :media-in 0 :duration 100}]}
|
||||
{:index 1 :kind :video :name "CU" :clips [{:id "t1-c0" :name "b" :start 100 :media-in 500 :duration 100}]}
|
||||
{:index 2 :kind :audio :name "Aud" :clips [{:id "t2-c0" :name "x" :start 0 :media-in 0 :duration 50}]}]}
|
||||
scene (s/from-otio parsed)]
|
||||
(is (= 2 (count (:tracks scene)))) ; audio track dropped
|
||||
(is (contains? (:groups scene) :t0-c0))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue