1# Copyright 2004-2021 Tom Rothamel <pytom@bishoujo.us> 2# 3# Permission is hereby granted, free of charge, to any person 4# obtaining a copy of this software and associated documentation files 5# (the "Software"), to deal in the Software without restriction, 6# including without limitation the rights to use, copy, modify, merge, 7# publish, distribute, sublicense, and/or sell copies of the Software, 8# and to permit persons to whom the Software is furnished to do so, 9# subject to the following conditions: 10# 11# The above copyright notice and this permission notice shall be 12# included in all copies or substantial portions of the Software. 13# 14# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20# WITH THE SOFTWARE OR 21 22from __future__ import division, absolute_import, with_statement, print_function, unicode_literals 23from renpy.compat import * 24 25import renpy 26import jnius # @UnresolvedImport 27 28from renpy.audio.audio import MusicContext 29 30VideoPlayer = jnius.autoclass("org.renpy.android.VideoPlayer") 31 32 33class AndroidVideoChannel(object): 34 35 def __init__(self, name, file_prefix="", file_suffix="", default_loop=None): 36 37 # A list of queued filenames. 38 self.queue = [ ] 39 40 # The filename that's currently playing. 41 self.filename = None 42 43 # The videoplayer that's currently playing. 44 self.player = None 45 46 # The name assigned to this channel. This is used to look up 47 # information about the channel in the MusicContext object. 48 self.name = name 49 50 # The name of the mixer this channel uses. Set below, as there's 51 # no good default. 52 self.mixer = None 53 54 # The time the music in this channel was last changed. 55 self.last_changed = 0 56 57 # The callback that is called if the queue becomes empty. 58 self.callback = None 59 60 # Ignored. 61 self.synchro_start = False 62 self.wait_stop = False 63 self.loop = [ ] 64 65 # A prefix and suffix that are used to create the full filenames. 66 self.file_prefix = file_prefix 67 self.file_suffix = file_suffix 68 69 if default_loop is None: 70 # By default, should we loop the music? 71 self.default_loop = True 72 # Was this set explicitly? 73 self.default_loop_set = False 74 75 else: 76 self.default_loop = default_loop 77 self.default_loop_set = True 78 79 def get_context(self): 80 """ 81 Returns the MusicContext corresponding to this channel, taken from 82 the context object. Allocates a MusicContext if none exists. 83 """ 84 85 mcd = renpy.game.context().music 86 87 rv = mcd.get(self.name) 88 if rv is None: 89 rv = mcd[self.name] = MusicContext() 90 91 return rv 92 93 context = property(get_context) 94 95 def copy_context(self): 96 """ 97 Copies the MusicContext associated with this channel, updates the 98 ExecutionContext to point to the copy, and returns the copy. 99 """ 100 101 mcd = renpy.game.context().music 102 103 ctx = self.get_context().copy() 104 mcd[self.name] = ctx 105 return ctx 106 107 def start(self): 108 """ 109 Starts playing the first video in the queue. 110 """ 111 112 if not self.queue: 113 return 114 115 filename = self.queue.pop(0) 116 117 print("Playing", filename) 118 119 with renpy.loader.load(filename) as f: 120 real_fn = f.name 121 base = getattr(f, "base", -1) 122 length = getattr(f, "length", -1) 123 124 self.filename = filename 125 self.player = VideoPlayer(real_fn, base, length) 126 127 def stop(self): 128 129 if self.player is not None: 130 self.player.stop() 131 self.player = None 132 133 self.filename = None 134 135 def get_playing(self): 136 137 if self.player is None: 138 return None 139 140 if self.player.isPlaying(): 141 return self.filename 142 143 def periodic(self): 144 145 # This should be set from something that checks to see if our 146 # mixer is muted. 147 force_stop = self.context.force_stop 148 149 if force_stop: 150 self.dequeue() 151 self.stop() 152 return 153 154 if self.get_playing(): 155 return 156 157 if self.queue: 158 self.start() 159 160 def dequeue(self, even_tight=False): 161 """ 162 Clears the queued music, except for a first item that has 163 not been started. 164 """ 165 166 if self.get_playing(): 167 self.queue = [ ] 168 else: 169 self.queue = self.queue[:1] 170 171 def interact(self): 172 """ 173 Called (mostly) once per interaction. 174 """ 175 176 self.periodic() 177 178 def fadeout(self, secs): 179 """ 180 Causes the playing music to be faded out for the given number 181 of seconds. Also clears any queued music. 182 """ 183 184 self.stop() 185 self.queue = [ ] 186 187 def enqueue(self, filenames, loop=True, synchro_start=False, fadein=0, tight=None, loop_only=False, relative_volume=1.0): 188 self.queue.extend(filenames) 189 190 def set_volume(self, volume): 191 pass 192 193 def get_pos(self): 194 pass 195 196 def set_pan(self, pan, delay): 197 pass 198 199 def set_secondary_volume(self, volume, delay): 200 pass 201 202 def pause(self): 203 if self.player is not None: 204 self.player.pause() 205 206 def unpause(self): 207 if self.player is not None: 208 self.player.unpause() 209 210 def reload(self): 211 return 212 213 def read_video(self): 214 return None 215 216 def video_ready(self): 217 return 1 218