A game framework written with osu! in mind.
1// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
2// See the LICENCE file in the repository root for full licence text.
3
4using NUnit.Framework;
5using osu.Framework.Allocation;
6using osu.Framework.Extensions.Color4Extensions;
7using osu.Framework.Graphics;
8using osu.Framework.Graphics.Containers;
9using osu.Framework.Graphics.Video;
10using osu.Framework.Testing;
11using osu.Framework.Timing;
12
13namespace osu.Framework.Tests.Visual.Sprites
14{
15 public class TestSceneVideo : FrameworkTestScene
16 {
17 private Container videoContainer;
18 private TextFlowContainer timeText;
19
20 private ManualClock clock;
21
22 private TestVideo video;
23
24 private bool didDecode;
25
26 [BackgroundDependencyLoader]
27 private void load()
28 {
29 Children = new Drawable[]
30 {
31 videoContainer = new Container
32 {
33 RelativeSizeAxes = Axes.Both,
34 Clock = new FramedClock(clock = new ManualClock()),
35 },
36 timeText = new TextFlowContainer(f => f.Font = FrameworkFont.Condensed)
37 {
38 RelativeSizeAxes = Axes.Both,
39 Text = "Video is loading...",
40 }
41 };
42 }
43
44 [SetUpSteps]
45 public void SetUpSteps()
46 {
47 AddStep("Reset clock", () =>
48 {
49 clock.CurrentTime = 0;
50 didDecode = false;
51 });
52 loadNewVideo();
53 AddUntilStep("Wait for video to load", () => video.IsLoaded);
54 AddStep("Reset clock", () => clock.CurrentTime = 0);
55 }
56
57 private void loadNewVideo()
58 {
59 AddStep("load video", () =>
60 {
61 videoContainer.Child = video = new TestVideo
62 {
63 Loop = false,
64 };
65 });
66 }
67
68 [Test]
69 public void TestStartFromCurrentTime()
70 {
71 AddAssert("Video is near start", () => video.PlaybackPosition < 1000);
72
73 AddWaitStep("Wait some", 20);
74
75 loadNewVideo();
76
77 AddAssert("Video is near start", () => video.PlaybackPosition < 1000);
78 }
79
80 [Test]
81 public void TestDecodingStopsWhenNotPresent()
82 {
83 AddStep("make video hidden", () => video.Hide());
84
85 AddWaitStep("wait a bit", 10);
86
87 AddUntilStep("decoding stopped", () => video.State == VideoDecoder.DecoderState.Ready);
88
89 AddStep("reset decode state", () => didDecode = false);
90
91 AddWaitStep("wait a bit", 10);
92 AddAssert("decoding didn't run", () => !didDecode);
93
94 AddStep("make video visible", () => video.Show());
95 AddUntilStep("decoding ran", () => didDecode);
96 }
97
98 [TestCase(false)]
99 [TestCase(true)]
100 public void TestDecodingStopsBeforeStartTime(bool looping)
101 {
102 AddStep("Set looping", () => video.Loop = looping);
103
104 AddStep("Jump back to before start time", () => clock.CurrentTime = -30000);
105
106 AddUntilStep("decoding stopped", () => video.State == VideoDecoder.DecoderState.Ready);
107
108 AddStep("reset decode state", () => didDecode = false);
109
110 AddWaitStep("wait a bit", 10);
111 AddAssert("decoding didn't run", () => !didDecode);
112
113 AddStep("seek close to start", () => clock.CurrentTime = -500);
114 AddUntilStep("decoding ran", () => didDecode);
115 }
116
117 [Test]
118 public void TestJumpForward()
119 {
120 AddStep("Jump ahead by 10 seconds", () => clock.CurrentTime += 10000);
121 AddUntilStep("Video seeked", () => video.PlaybackPosition >= 10000);
122 }
123
124 [Test]
125 public void TestJumpBack()
126 {
127 AddStep("Jump ahead by 30 seconds", () => clock.CurrentTime += 30000);
128 AddUntilStep("Video seeked", () => video.PlaybackPosition >= 30000);
129 AddStep("Jump back by 10 seconds", () => clock.CurrentTime -= 10000);
130 AddUntilStep("Video seeked", () => video.PlaybackPosition < 30000);
131 }
132
133 [Test]
134 public void TestJumpBackAfterEndOfPlayback()
135 {
136 AddStep("Jump ahead by 60 seconds", () => clock.CurrentTime += 60000);
137
138 AddUntilStep("Video seeked", () => video.PlaybackPosition >= 30000);
139 AddUntilStep("Reached end", () => video.State == VideoDecoder.DecoderState.EndOfStream);
140 AddStep("reset decode state", () => didDecode = false);
141
142 AddStep("Jump back to valid time", () => clock.CurrentTime = 20000);
143 AddUntilStep("decoding ran", () => didDecode);
144 }
145
146 [Test]
147 public void TestVideoDoesNotLoopIfDisabled()
148 {
149 AddStep("Seek to end", () => clock.CurrentTime = video.Duration);
150 AddUntilStep("Video seeked", () => video.PlaybackPosition >= video.Duration - 1000);
151 AddWaitStep("Wait for playback", 10);
152 AddAssert("Not looped", () => video.PlaybackPosition >= video.Duration - 1000);
153 }
154
155 [Test]
156 public void TestVideoLoopsIfEnabled()
157 {
158 AddStep("Set looping", () => video.Loop = true);
159 AddStep("Seek to end", () => clock.CurrentTime = video.Duration);
160 AddWaitStep("Wait for playback", 10);
161 AddUntilStep("Looped", () => video.PlaybackPosition < video.Duration - 1000);
162 }
163
164 [Test]
165 public void TestShader()
166 {
167 AddStep("Set colour", () => video.Colour = Color4Extensions.FromHex("#ea7948").Opacity(0.75f));
168 AddStep("Use normal shader", () => video.UseRoundedShader = false);
169 AddStep("Use rounded shader", () => video.UseRoundedShader = true);
170 }
171
172 private int currentSecond;
173 private int fps;
174 private int lastFramesProcessed;
175
176 protected override void Update()
177 {
178 base.Update();
179
180 if (clock != null)
181 clock.CurrentTime += Clock.ElapsedFrameTime;
182
183 if (video != null)
184 {
185 var newSecond = (int)(video.PlaybackPosition / 1000.0);
186
187 if (newSecond != currentSecond)
188 {
189 currentSecond = newSecond;
190 fps = video.FramesProcessed - lastFramesProcessed;
191 lastFramesProcessed = video.FramesProcessed;
192 }
193
194 if (timeText != null)
195 {
196 timeText.Text = $"aim time: {video.PlaybackPosition:N2}\n"
197 + $"video time: {video.CurrentFrameTime:N2}\n"
198 + $"duration: {video.Duration:N2}\n"
199 + $"buffered {video.AvailableFrames}\n"
200 + $"FPS: {fps}\n"
201 + $"State: {video.State}";
202 }
203
204 didDecode |= video.State == VideoDecoder.DecoderState.Running;
205 }
206 }
207 }
208}