video.py (8503B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\eve\client\script\ui\heronotification\video.py 2 import abc 3 import math 4 import eveui 5 import gametime 6 import signals 7 import trinity 8 import uthread2 9 from carbonui import uiconst 10 from carbonui.primitives.sprite import StreamingVideoSprite 11 from carbonui.uianimations import animations 12 from eve.client.script.ui.heronotification.cancel import Cancelled 13 from stacklesslib.locks import NLCondition 14 15 class LoopPolicy(object): 16 __metaclass__ = abc.ABCMeta 17 18 @abc.abstractmethod 19 def should_stop(self): 20 pass 21 22 def on_start_looping(self): 23 pass 24 25 def on_loop_finished(self): 26 pass 27 28 def dispose(self): 29 pass 30 31 32 class LoopCountPolicy(LoopPolicy): 33 34 def __init__(self, loop_count): 35 self._loop_count = loop_count 36 self._finished_loops = 0 37 38 def should_stop(self): 39 return self._finished_loops >= self._loop_count 40 41 def on_loop_finished(self): 42 self._finished_loops += 1 43 44 45 class LoopTimerPolicy(LoopPolicy): 46 47 def __init__(self, duration): 48 self._duration = duration 49 self._loop_start = None 50 51 def should_stop(self): 52 return self._loop_start is not None and gametime.now() - self._loop_start >= self._duration 53 54 def on_start_looping(self): 55 self._loop_start = gametime.now() 56 57 58 class SegmentedVideoHeroNotification(object): 59 60 def __init__(self, video_width, video_height, video_intro_path, video_loop_path, video_outro_path, loop_policy, allow_cancel_intro = False, allow_cancel_loop = False): 61 self._allow_cancel_intro = allow_cancel_intro 62 self._allow_cancel_loop = allow_cancel_loop 63 self._cancellation_token = None 64 self._loop_policy = loop_policy 65 self._state = State.INIT 66 self._video_height = video_height 67 self._video_intro_path = video_intro_path 68 self._video_loop_path = video_loop_path 69 self._video_outro_path = video_outro_path 70 self._video_sprite_intro = None 71 self._video_sprite_loop = None 72 self._video_sprite_outro = None 73 self._video_width = video_width 74 self.on_intro_start = signals.Signal() 75 self.on_loop_start = signals.Signal() 76 self.on_outro_start = signals.Signal() 77 self._on_intro_start_condition = NLCondition() 78 self._on_loop_start_condition = NLCondition() 79 self._on_outro_start_condition = NLCondition() 80 self._is_killed_gracefully = False 81 82 def play(self, parent, cancellation_token): 83 if self._state != State.INIT: 84 raise RuntimeError('The video is already playing') 85 self._cancellation_token = cancellation_token 86 try: 87 self._setup(parent) 88 self._wait_for_intro() 89 except Cancelled: 90 self._hard_outro() 91 else: 92 self._wait_for_loops() 93 self._play_outro() 94 self._wait_for_outro() 95 finally: 96 self._cleanup() 97 98 def wait_for_intro_start(self): 99 if self._state < State.INTRO: 100 self._on_intro_start_condition.wait() 101 102 def wait_for_loop_start(self): 103 if self._state < State.LOOP: 104 self._on_loop_start_condition.wait() 105 106 def wait_for_outro_start(self): 107 if self._state < State.OUTRO: 108 self._on_outro_start_condition.wait() 109 110 def _setup(self, parent): 111 self._state = State.SETUP 112 self._video_sprite_intro = self._create_video_sprite(parent=parent, video_path=self._video_intro_path, auto_play=True, on_finished=self._on_intro_finished) 113 self._video_sprite_loop = self._create_video_sprite(parent=parent, video_path=self._video_loop_path, loop=True, display=False) 114 self._video_sprite_outro = self._create_video_sprite(parent=parent, video_path=self._video_outro_path, display=False) 115 self._state = State.INTRO 116 self.on_intro_start() 117 self._on_intro_start_condition.notify_all() 118 119 def _create_video_sprite(self, parent, video_path, loop = False, auto_play = False, on_finished = None, display = True): 120 sprite = StreamingVideoSprite(parent=parent, align=uiconst.CENTER, state=uiconst.UI_DISABLED, width=self._video_width, height=self._video_height, videoPath=video_path, disableAudio=True, videoLoop=loop, videoAutoPlay=auto_play, blendMode=trinity.TR2_SBM_ADD, spriteEffect=trinity.TR2_SFX_COPY) 121 sprite.display = display 122 if on_finished is not None: 123 sprite.OnVideoFinished = on_finished 124 return sprite 125 126 def _on_intro_finished(self): 127 if self._state == State.INTRO: 128 self._play_loop() 129 130 def _wait_for_intro(self): 131 while self._state == State.INTRO: 132 should_continue = self._wait_for_next_frame() 133 if not should_continue: 134 return 135 if self._allow_cancel_intro: 136 self._cancellation_token.check_cancelled() 137 138 def _play_loop(self): 139 self._state = State.LOOP 140 self._video_sprite_loop.display = True 141 self._video_sprite_intro.display = False 142 self._video_sprite_loop.Play() 143 self._loop_policy.on_start_looping() 144 self.on_loop_start() 145 self._on_loop_start_condition.notify_all() 146 147 def _wait_for_loops(self): 148 last_loop = self._get_video_loops() 149 while not self._loop_policy.should_stop(): 150 if self._allow_cancel_loop and self._cancellation_token.cancelled: 151 while self._get_video_loops() == last_loop: 152 should_continue = self._wait_for_next_frame() 153 if not should_continue: 154 return 155 return 156 157 should_continue = self._wait_for_next_frame() 158 if not should_continue: 159 return 160 loop = self._get_video_loops() 161 while last_loop < loop: 162 last_loop += 1 163 self._loop_policy.on_loop_finished() 164 165 def _get_video_loops(self): 166 if not self._video_sprite_loop.duration: 167 return 0 168 return int(math.floor(self._video_sprite_loop.mediaTime / float(self._video_sprite_loop.duration))) 169 170 def _play_outro(self): 171 self._state = State.OUTRO 172 self._video_sprite_outro.display = True 173 self._video_sprite_loop.display = False 174 self._video_sprite_loop.Pause() 175 self._video_sprite_outro.Play() 176 self.on_outro_start() 177 self._on_outro_start_condition.notify_all() 178 179 def _wait_for_outro(self): 180 while self._state == State.OUTRO: 181 if self._video_sprite_outro.isFinished: 182 break 183 should_continue = self._wait_for_next_frame() 184 if not should_continue: 185 return 186 187 def _hard_outro(self): 188 for sprite in (self._video_sprite_intro, self._video_sprite_loop, self._video_sprite_outro): 189 if sprite is not None and not sprite.destroyed: 190 animations.FadeOut(sprite, duration=0.5) 191 192 uthread2.sleep(0.5) 193 self._state = State.FINISHED 194 195 def _wait_for_next_frame(self): 196 eveui.wait_for_next_frame() 197 if self.is_killed_gracefully: 198 return False 199 self._check_destroyed() 200 return True 201 202 def _check_destroyed(self): 203 for element in (self._video_sprite_intro, self._video_sprite_loop, self._video_sprite_outro): 204 if element is None or element.destroyed: 205 raise HeroNotificationDestroyed() 206 207 @property 208 def is_killed_gracefully(self): 209 return self._is_killed_gracefully 210 211 def kill(self): 212 self._is_killed_gracefully = True 213 214 def _cleanup(self): 215 self._loop_policy.dispose() 216 for element in (self._video_sprite_intro, self._video_sprite_loop, self._video_sprite_outro): 217 if element is not None: 218 element.Close() 219 220 self._video_sprite_intro = None 221 self._video_sprite_loop = None 222 self._video_sprite_outro = None 223 self._cancellation_token = None 224 self._state = State.FINISHED 225 self._on_intro_start_condition.notify_all() 226 self._on_loop_start_condition.notify_all() 227 self._on_outro_start_condition.notify_all() 228 229 def __call__(self, parent, cancellation_token): 230 self.play(parent, cancellation_token) 231 232 233 class State(object): 234 INIT = 1 235 SETUP = 2 236 INTRO = 3 237 LOOP = 4 238 OUTRO = 5 239 FINISHED = 6 240 241 242 class HeroNotificationDestroyed(Exception): 243 pass