1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
use scene::{
BaseTimelineElement, Element, ParamValue, ParamValues, RetimeConfig, SceneTracks, Track,
VideoElement,
};
use time::MediaTime;
use timeline::update_pipeline::{ElementPatch, ElementUpdateContext, apply_element_update};
fn build_video_element() -> Element {
Element::Video(VideoElement {
base: BaseTimelineElement {
id: "video-1".to_string(),
name: "Video 1".to_string(),
start_time: MediaTime::ZERO,
duration: MediaTime::from_ticks(10),
trim_start: MediaTime::ZERO,
trim_end: MediaTime::ZERO,
source_duration: None,
animations: None,
params: ParamValues::from([
("transform.positionX".to_string(), ParamValue::Number(0.0)),
("transform.positionY".to_string(), ParamValue::Number(0.0)),
("transform.scaleX".to_string(), ParamValue::Number(1.0)),
("transform.scaleY".to_string(), ParamValue::Number(1.0)),
("transform.rotate".to_string(), ParamValue::Number(0.0)),
("opacity".to_string(), ParamValue::Number(1.0)),
]),
},
media_id: "media-1".to_string(),
is_source_audio_enabled: None,
hidden: None,
retime: None,
effects: None,
masks: None,
})
}
fn build_tracks(element: Element) -> SceneTracks {
SceneTracks {
overlay: Vec::new(),
main: Track::Video {
id: "main-track".to_string(),
name: "Main".to_string(),
elements: vec![element],
muted: false,
hidden: false,
},
audio: Vec::new(),
}
}
#[test]
fn rounds_retimed_durations_back_to_integer_media_time() {
let element = build_video_element();
let tracks = build_tracks(element.clone());
let updated = apply_element_update(
&element,
&ElementPatch {
retime: Some(Some(RetimeConfig {
rate: 1.5,
maintain_pitch: None,
})),
..ElementPatch::default()
},
&ElementUpdateContext {
tracks: &tracks,
track_id: tracks.main.id(),
},
);
assert_eq!(updated.base().duration, MediaTime::from_ticks(7));
assert_eq!(timeline::element_retime(&updated).unwrap().rate, 1.5);
}
#[test]
fn start_time_on_main_track_snaps_to_zero_before_the_earliest_element() {
let earliest = {
let mut element = build_video_element();
element.base_mut().id = "video-2".to_string();
element.base_mut().start_time = MediaTime::from_ticks(100);
element
};
let element = build_video_element();
let tracks = build_tracks(earliest);
let tracks = SceneTracks {
main: Track::Video {
id: "main-track".to_string(),
name: "Main".to_string(),
elements: vec![tracks.main.elements()[0].clone(), element.clone()],
muted: false,
hidden: false,
},
..tracks
};
let updated = apply_element_update(
&element,
&ElementPatch {
start_time: Some(MediaTime::from_ticks(50)),
..ElementPatch::default()
},
&ElementUpdateContext {
tracks: &tracks,
track_id: "main-track",
},
);
assert_eq!(updated.base().start_time, MediaTime::ZERO);
}
#[test]
fn start_time_on_overlay_track_only_clamps_negative_values() {
let element = build_video_element();
let tracks = SceneTracks {
overlay: vec![Track::Video {
id: "overlay-track".to_string(),
name: "Overlay".to_string(),
elements: vec![element.clone()],
muted: false,
hidden: false,
}],
main: Track::Video {
id: "main-track".to_string(),
name: "Main".to_string(),
elements: Vec::new(),
muted: false,
hidden: false,
},
audio: Vec::new(),
};
let updated = apply_element_update(
&element,
&ElementPatch {
start_time: Some(MediaTime::from_ticks(-20)),
..ElementPatch::default()
},
&ElementUpdateContext {
tracks: &tracks,
track_id: "overlay-track",
},
);
assert_eq!(updated.base().start_time, MediaTime::ZERO);
}
|