1 /* 2 * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.webkit.graphics; 27 28 import java.util.logging.Level; 29 import java.util.logging.Logger; 30 import com.sun.webkit.Invoker; 31 32 public abstract class WCMediaPlayer extends Ref { 33 34 protected final static Logger log; 35 protected final static boolean verbose; 36 37 static { 38 log = Logger.getLogger("webkit.mediaplayer"); 39 if (log.getLevel() == null) { 40 // disable logging if webkit.mediaplayer.level 41 // is not specified (explicitly) in log config file 42 verbose = false; 43 log.setLevel(Level.OFF); 44 } else { 45 verbose = true; 46 log.log(Level.CONFIG, "webkit.mediaplayer logging is ON, level: {0}", log.getLevel()); 47 } 48 } 49 50 // pointer to native Player object; 51 // read the value only on FX event thread, check that it has non-zero value; 52 // setters (ctor && fwkDispose) are called on event thread. 53 private long nPtr; 54 55 protected WCMediaPlayer() { 56 } 57 58 void setNativePointer(long nativePointer) { 59 if (nativePointer == 0) { 60 throw new IllegalArgumentException("nativePointer is 0"); 61 } 62 if (nPtr != 0) { 63 throw new IllegalStateException("nPtr is not 0"); 64 } 65 this.nPtr = nativePointer; 66 } 67 68 /** 69 * Methods to implement 70 */ 71 protected abstract void load(String url, String userAgent); 72 protected abstract void cancelLoad(); 73 protected abstract void disposePlayer(); 74 75 protected abstract void prepareToPlay(); 76 protected abstract void play(); 77 protected abstract void pause(); 78 79 protected abstract float getCurrentTime(); 80 // the method _must_ call notifySeeking(true)/notifySeeking(false) 81 protected abstract void seek(float time); 82 protected abstract void setRate(float rate); 83 protected abstract void setVolume(float volume); 84 protected abstract void setMute(boolean mute); 85 protected abstract void setSize(int w, int h); 86 protected abstract void setPreservesPitch(boolean preserve); 87 88 protected abstract void renderCurrentFrame(WCGraphicsContext gc, int x, int y, int w, int h); 89 90 /** 91 * Obtains current "preserves pitch" value. 92 */ 93 protected boolean getPreservesPitch() { 94 return preserve; 95 } 96 97 protected int getNetworkState() { 98 return networkState; 99 } 100 101 protected int getReadyState() { 102 return readyState; 103 } 104 105 protected int getPreload() { 106 return preload; 107 } 108 109 protected boolean isPaused() { 110 return paused; 111 } 112 113 protected boolean isSeeking() { 114 return seeking; 115 } 116 117 118 /* ======================================= */ 119 /* Methods to notify webkit about events */ 120 /* ======================================= */ 121 //enum NetworkState { Empty, Idle, Loading, Loaded, FormatError, NetworkError, DecodeError }; 122 protected final static int NETWORK_STATE_EMPTY = 0; 123 protected final static int NETWORK_STATE_IDLE = 1; 124 protected final static int NETWORK_STATE_LOADING = 2; 125 protected final static int NETWORK_STATE_LOADED = 3; 126 protected final static int NETWORK_STATE_FORMAT_ERROR = 4; 127 protected final static int NETWORK_STATE_NETWORK_ERROR = 5; 128 protected final static int NETWORK_STATE_DECODE_ERROR = 6; 129 //enum ReadyState { HaveNothing, HaveMetadata, HaveCurrentData, HaveFutureData, HaveEnoughData }; 130 protected final static int READY_STATE_HAVE_NOTHING = 0; 131 protected final static int READY_STATE_HAVE_METADATA = 1; 132 protected final static int READY_STATE_HAVE_CURRENT_DATA = 2; 133 protected final static int READY_STATE_HAVE_FUTURE_DATA = 3; 134 protected final static int READY_STATE_HAVE_ENOUGH_DATA = 4; 135 //enum Preload { None, MetaData, Auto }; 136 protected final static int PRELOAD_NONE = 0; 137 protected final static int PRELOAD_METADATA = 1; 138 protected final static int PRELOAD_AUTO = 2; 139 140 private int networkState = NETWORK_STATE_EMPTY; 141 private int readyState = READY_STATE_HAVE_NOTHING; 142 private int preload = PRELOAD_AUTO; 143 private boolean paused = true; 144 private boolean seeking = false; 145 146 147 protected void notifyNetworkStateChanged(int networkState) { 148 if (this.networkState != networkState) { 149 this.networkState = networkState; 150 final int _networkState = networkState; 151 Invoker.getInvoker().invokeOnEventThread(() -> { 152 if (nPtr != 0) { 153 notifyNetworkStateChanged(nPtr, _networkState); 154 } 155 }); 156 } 157 } 158 159 protected void notifyReadyStateChanged(int readyState) { 160 if (this.readyState != readyState) { 161 this.readyState = readyState; 162 final int _readyState = readyState; 163 Invoker.getInvoker().invokeOnEventThread(() -> { 164 if (nPtr != 0) { 165 notifyReadyStateChanged(nPtr, _readyState); 166 } 167 }); 168 } 169 } 170 171 protected void notifyPaused(boolean paused) { 172 if (verbose) log.log(Level.FINE, "notifyPaused, {0} => {1}", 173 new Object[]{Boolean.valueOf(this.paused), Boolean.valueOf(paused)}); 174 if (this.paused != paused) { 175 this.paused = paused; 176 final boolean _paused = paused; 177 Invoker.getInvoker().invokeOnEventThread(() -> { 178 if (nPtr != 0) { 179 notifyPaused(nPtr, _paused); 180 } 181 }); 182 } 183 } 184 185 // pass -1 as readyState value if the state is not changed 186 protected void notifySeeking(boolean seeking, int readyState) { 187 if (verbose) log.log(Level.FINE, "notifySeeking, {0} => {1}", 188 new Object[]{Boolean.valueOf(this.seeking), Boolean.valueOf(seeking)}); 189 if (this.seeking != seeking || this.readyState != readyState) { 190 this.seeking = seeking; 191 this.readyState = readyState; 192 final boolean _seeking = seeking; 193 final int _readyState = readyState; 194 Invoker.getInvoker().invokeOnEventThread(() -> { 195 if (nPtr != 0) { 196 notifySeeking(nPtr, _seeking, _readyState); 197 } 198 }); 199 } 200 } 201 202 protected void notifyFinished() { 203 Invoker.getInvoker().invokeOnEventThread(() -> { 204 if (nPtr != 0) { 205 notifyFinished(nPtr); 206 } 207 }); 208 } 209 210 /** got metadata */ 211 protected void notifyReady(boolean hasVideo, boolean hasAudio, float duration) { 212 final boolean _hasVideo = hasVideo; 213 final boolean _hasAudio = hasAudio; 214 final float _duration = duration; 215 Invoker.getInvoker().invokeOnEventThread(() -> { 216 if (nPtr != 0) { 217 notifyReady(nPtr, _hasVideo, _hasAudio, _duration); 218 } 219 }); 220 } 221 222 protected void notifyDurationChanged(float newDuration) { 223 final float _newDuration = newDuration; 224 Invoker.getInvoker().invokeOnEventThread(() -> { 225 if (nPtr != 0) { 226 notifyDurationChanged(nPtr, _newDuration); 227 } 228 }); 229 } 230 231 protected void notifySizeChanged(int width, int height) { 232 // notify on event thread to ensure native object is valid (nPtr != 0) 233 final int _width = width; 234 final int _height = height; 235 Invoker.getInvoker().invokeOnEventThread(() -> { 236 if (nPtr != 0) { 237 notifySizeChanged(nPtr, _width, _height); 238 } 239 }); 240 } 241 242 private Runnable newFrameNotifier = () -> { 243 if (nPtr != 0) { 244 notifyNewFrame(nPtr); 245 } 246 }; 247 248 protected void notifyNewFrame() { 249 Invoker.getInvoker().invokeOnEventThread(newFrameNotifier); 250 } 251 252 /** {@code ranges} array contains pairs [start,end] of the buffered times */ 253 protected void notifyBufferChanged(float[] ranges, int bytesLoaded) { 254 // notify on event thread to ensure native object is valid (nPtr != 0) 255 final float[] _ranges = ranges; 256 final int _bytesLoaded = bytesLoaded; 257 Invoker.getInvoker().invokeOnEventThread(() -> { 258 if (nPtr != 0) { 259 notifyBufferChanged(nPtr, _ranges, _bytesLoaded); 260 } 261 }); 262 } 263 264 265 /* ======================================= */ 266 /* Methods called from webkit */ 267 /* ======================================= */ 268 269 private void fwkLoad(String url, String userAgent) { 270 if (verbose) log.log(Level.FINE, "fwkLoad, url={0}, userAgent={1}", new Object[] {url, userAgent}); 271 load(url, userAgent); 272 } 273 274 private void fwkCancelLoad() { 275 if (verbose) log.log(Level.FINE, "fwkCancelLoad"); 276 cancelLoad(); 277 } 278 279 private void fwkPrepareToPlay() { 280 if (verbose) log.log(Level.FINE, "fwkPrepareToPlay"); 281 prepareToPlay(); 282 } 283 284 private void fwkDispose() { 285 if (verbose) log.log(Level.FINE, "fwkDispose"); 286 nPtr = 0; 287 cancelLoad(); 288 disposePlayer(); 289 } 290 291 private void fwkPlay() { 292 if (verbose) log.log(Level.FINE, "fwkPlay"); 293 play(); 294 } 295 296 private void fwkPause() { 297 if (verbose) log.log(Level.FINE, "fwkPause"); 298 pause(); 299 } 300 301 private float fwkGetCurrentTime() { 302 float res = getCurrentTime(); 303 if (verbose) log.log(Level.FINER, "fwkGetCurrentTime(), return {0}", res); 304 return res; 305 } 306 307 private void fwkSeek(float time) { 308 if (verbose) log.log(Level.FINE, "fwkSeek({0})", time); 309 seek(time); 310 } 311 312 private void fwkSetRate(float rate) { 313 if (verbose) log.log(Level.FINE, "fwkSetRate({0})", rate); 314 setRate(rate); 315 } 316 317 private void fwkSetVolume(float volume) { 318 if (verbose) log.log(Level.FINE, "fwkSetVolume({0})", volume); 319 setVolume(volume); 320 } 321 322 private void fwkSetMute(boolean mute) { 323 if (verbose) log.log(Level.FINE, "fwkSetMute({0})", mute); 324 setMute(mute); 325 } 326 327 private void fwkSetSize(int w, int h) { 328 //if (verbose) log.log(Level.FINE, "setSize({0} x {1})", new Object[]{w, h}); 329 setSize(w, h); 330 } 331 332 private boolean preserve = true; 333 334 private void fwkSetPreservesPitch(boolean preserve) { 335 if (verbose) log.log(Level.FINE, "setPreservesPitch({0})", preserve); 336 // synchronized(renderLock) { 337 this.preserve = preserve; 338 setPreservesPitch(preserve); 339 // } 340 } 341 342 private void fwkSetPreload(int preload) { 343 if (verbose) { 344 log.log(Level.FINE, "fwkSetPreload({0})", 345 preload == PRELOAD_NONE ? "PRELOAD_NONE" 346 : preload == PRELOAD_METADATA ? "PRELOAD_METADATA" 347 : preload == PRELOAD_AUTO ? "PRELOAD_AUTO" 348 : ("INVALID VALUE: " + preload)); 349 } 350 this.preload = preload; 351 } 352 353 /* called from GraphicsDecoder */ 354 void render(WCGraphicsContext gc, int x, int y, int w, int h) { 355 if (verbose) { 356 log.log(Level.FINER, "render(x={0}, y={1}, w={2}, h={3}", 357 new Object[]{x, y, w, h}); 358 } 359 renderCurrentFrame(gc, x, y, w, h); 360 } 361 362 363 /* native methods */ 364 private native void notifyNetworkStateChanged(long nPtr, int networkState); 365 private native void notifyReadyStateChanged(long nPtr, int readyState); 366 private native void notifyPaused(long nPtr, boolean paused); 367 private native void notifySeeking(long nPtr, boolean seeking, int readyState); 368 private native void notifyFinished(long nPtr); 369 private native void notifyReady(long nPtr, boolean hasVideo, boolean hasAudio, float duration); 370 private native void notifyDurationChanged(long nPtr, float duration); 371 private native void notifySizeChanged(long nPtr, int width, int height); 372 private native void notifyNewFrame(long nPtr); 373 private native void notifyBufferChanged(long nPtr, float[] ranges, int bytesLoaded); 374 375 }