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 com.sun.javafx.logging.PlatformLogger; 29 import com.sun.webkit.Invoker; 30 31 public abstract class WCMediaPlayer extends Ref { 32 33 protected final static PlatformLogger log = PlatformLogger.getLogger("webkit.mediaplayer"); 34 35 // pointer to native Player object; 36 // read the value only on FX event thread, check that it has non-zero value; 37 // setters (ctor && fwkDispose) are called on event thread. 38 private long nPtr; 39 40 protected WCMediaPlayer() { 41 } 42 43 void setNativePointer(long nativePointer) { 44 if (nativePointer == 0) { 45 throw new IllegalArgumentException("nativePointer is 0"); 46 } 47 if (nPtr != 0) { 48 throw new IllegalStateException("nPtr is not 0"); 49 } 50 this.nPtr = nativePointer; 51 } 52 53 /** 54 * Methods to implement 55 */ 56 protected abstract void load(String url, String userAgent); 57 protected abstract void cancelLoad(); 58 protected abstract void disposePlayer(); 59 60 protected abstract void prepareToPlay(); 61 protected abstract void play(); 62 protected abstract void pause(); 63 64 protected abstract float getCurrentTime(); 65 // the method _must_ call notifySeeking(true)/notifySeeking(false) 66 protected abstract void seek(float time); 67 protected abstract void setRate(float rate); 68 protected abstract void setVolume(float volume); 69 protected abstract void setMute(boolean mute); 70 protected abstract void setSize(int w, int h); 71 protected abstract void setPreservesPitch(boolean preserve); 72 73 protected abstract void renderCurrentFrame(WCGraphicsContext gc, int x, int y, int w, int h); 74 75 /** 76 * Obtains current "preserves pitch" value. 77 */ 78 protected boolean getPreservesPitch() { 79 return preserve; 80 } 81 82 protected int getNetworkState() { 83 return networkState; 84 } 85 86 protected int getReadyState() { 87 return readyState; 88 } 89 90 protected int getPreload() { 91 return preload; 92 } 93 94 protected boolean isPaused() { 95 return paused; 96 } 97 98 protected boolean isSeeking() { 99 return seeking; 100 } 101 102 103 /* ======================================= */ 104 /* Methods to notify webkit about events */ 105 /* ======================================= */ 106 //enum NetworkState { Empty, Idle, Loading, Loaded, FormatError, NetworkError, DecodeError }; 107 protected final static int NETWORK_STATE_EMPTY = 0; 108 protected final static int NETWORK_STATE_IDLE = 1; 109 protected final static int NETWORK_STATE_LOADING = 2; 110 protected final static int NETWORK_STATE_LOADED = 3; 111 protected final static int NETWORK_STATE_FORMAT_ERROR = 4; 112 protected final static int NETWORK_STATE_NETWORK_ERROR = 5; 113 protected final static int NETWORK_STATE_DECODE_ERROR = 6; 114 //enum ReadyState { HaveNothing, HaveMetadata, HaveCurrentData, HaveFutureData, HaveEnoughData }; 115 protected final static int READY_STATE_HAVE_NOTHING = 0; 116 protected final static int READY_STATE_HAVE_METADATA = 1; 117 protected final static int READY_STATE_HAVE_CURRENT_DATA = 2; 118 protected final static int READY_STATE_HAVE_FUTURE_DATA = 3; 119 protected final static int READY_STATE_HAVE_ENOUGH_DATA = 4; 120 //enum Preload { None, MetaData, Auto }; 121 protected final static int PRELOAD_NONE = 0; 122 protected final static int PRELOAD_METADATA = 1; 123 protected final static int PRELOAD_AUTO = 2; 124 125 private int networkState = NETWORK_STATE_EMPTY; 126 private int readyState = READY_STATE_HAVE_NOTHING; 127 private int preload = PRELOAD_AUTO; 128 private boolean paused = true; 129 private boolean seeking = false; 130 131 132 protected void notifyNetworkStateChanged(int networkState) { 133 if (this.networkState != networkState) { 134 this.networkState = networkState; 135 final int _networkState = networkState; 136 Invoker.getInvoker().invokeOnEventThread(() -> { 137 if (nPtr != 0) { 138 notifyNetworkStateChanged(nPtr, _networkState); 139 } 140 }); 141 } 142 } 143 144 protected void notifyReadyStateChanged(int readyState) { 145 if (this.readyState != readyState) { 146 this.readyState = readyState; 147 final int _readyState = readyState; 148 Invoker.getInvoker().invokeOnEventThread(() -> { 149 if (nPtr != 0) { 150 notifyReadyStateChanged(nPtr, _readyState); 151 } 152 }); 153 } 154 } 155 156 protected void notifyPaused(boolean paused) { 157 log.fine("notifyPaused, {0} => {1}", 158 new Object[]{Boolean.valueOf(this.paused), Boolean.valueOf(paused)}); 159 if (this.paused != paused) { 160 this.paused = paused; 161 final boolean _paused = paused; 162 Invoker.getInvoker().invokeOnEventThread(() -> { 163 if (nPtr != 0) { 164 notifyPaused(nPtr, _paused); 165 } 166 }); 167 } 168 } 169 170 // pass -1 as readyState value if the state is not changed 171 protected void notifySeeking(boolean seeking, int readyState) { 172 log.fine("notifySeeking, {0} => {1}", 173 new Object[]{Boolean.valueOf(this.seeking), Boolean.valueOf(seeking)}); 174 if (this.seeking != seeking || this.readyState != readyState) { 175 this.seeking = seeking; 176 this.readyState = readyState; 177 final boolean _seeking = seeking; 178 final int _readyState = readyState; 179 Invoker.getInvoker().invokeOnEventThread(() -> { 180 if (nPtr != 0) { 181 notifySeeking(nPtr, _seeking, _readyState); 182 } 183 }); 184 } 185 } 186 187 protected void notifyFinished() { 188 Invoker.getInvoker().invokeOnEventThread(() -> { 189 if (nPtr != 0) { 190 notifyFinished(nPtr); 191 } 192 }); 193 } 194 195 /** got metadata */ 196 protected void notifyReady(boolean hasVideo, boolean hasAudio, float duration) { 197 final boolean _hasVideo = hasVideo; 198 final boolean _hasAudio = hasAudio; 199 final float _duration = duration; 200 Invoker.getInvoker().invokeOnEventThread(() -> { 201 if (nPtr != 0) { 202 notifyReady(nPtr, _hasVideo, _hasAudio, _duration); 203 } 204 }); 205 } 206 207 protected void notifyDurationChanged(float newDuration) { 208 final float _newDuration = newDuration; 209 Invoker.getInvoker().invokeOnEventThread(() -> { 210 if (nPtr != 0) { 211 notifyDurationChanged(nPtr, _newDuration); 212 } 213 }); 214 } 215 216 protected void notifySizeChanged(int width, int height) { 217 // notify on event thread to ensure native object is valid (nPtr != 0) 218 final int _width = width; 219 final int _height = height; 220 Invoker.getInvoker().invokeOnEventThread(() -> { 221 if (nPtr != 0) { 222 notifySizeChanged(nPtr, _width, _height); 223 } 224 }); 225 } 226 227 private Runnable newFrameNotifier = () -> { 228 if (nPtr != 0) { 229 notifyNewFrame(nPtr); 230 } 231 }; 232 233 protected void notifyNewFrame() { 234 Invoker.getInvoker().invokeOnEventThread(newFrameNotifier); 235 } 236 237 /** {@code ranges} array contains pairs [start,end] of the buffered times */ 238 protected void notifyBufferChanged(float[] ranges, int bytesLoaded) { 239 // notify on event thread to ensure native object is valid (nPtr != 0) 240 final float[] _ranges = ranges; 241 final int _bytesLoaded = bytesLoaded; 242 Invoker.getInvoker().invokeOnEventThread(() -> { 243 if (nPtr != 0) { 244 notifyBufferChanged(nPtr, _ranges, _bytesLoaded); 245 } 246 }); 247 } 248 249 250 /* ======================================= */ 251 /* Methods called from webkit */ 252 /* ======================================= */ 253 254 private void fwkLoad(String url, String userAgent) { 255 log.fine("fwkLoad, url={0}, userAgent={1}", new Object[] {url, userAgent}); 256 load(url, userAgent); 257 } 258 259 private void fwkCancelLoad() { 260 log.fine("fwkCancelLoad"); 261 cancelLoad(); 262 } 263 264 private void fwkPrepareToPlay() { 265 log.fine("fwkPrepareToPlay"); 266 prepareToPlay(); 267 } 268 269 private void fwkDispose() { 270 log.fine("fwkDispose"); 271 nPtr = 0; 272 cancelLoad(); 273 disposePlayer(); 274 } 275 276 private void fwkPlay() { 277 log.fine("fwkPlay"); 278 play(); 279 } 280 281 private void fwkPause() { 282 log.fine("fwkPause"); 283 pause(); 284 } 285 286 private float fwkGetCurrentTime() { 287 float res = getCurrentTime(); 288 log.finer("fwkGetCurrentTime(), return {0}", res); 289 return res; 290 } 291 292 private void fwkSeek(float time) { 293 log.fine("fwkSeek({0})", time); 294 seek(time); 295 } 296 297 private void fwkSetRate(float rate) { 298 log.fine("fwkSetRate({0})", rate); 299 setRate(rate); 300 } 301 302 private void fwkSetVolume(float volume) { 303 log.fine("fwkSetVolume({0})", volume); 304 setVolume(volume); 305 } 306 307 private void fwkSetMute(boolean mute) { 308 log.fine("fwkSetMute({0})", mute); 309 setMute(mute); 310 } 311 312 private void fwkSetSize(int w, int h) { 313 //log.fine("setSize({0} x {1})", new Object[]{w, h}); 314 setSize(w, h); 315 } 316 317 private boolean preserve = true; 318 319 private void fwkSetPreservesPitch(boolean preserve) { 320 log.fine("setPreservesPitch({0})", preserve); 321 // synchronized(renderLock) { 322 this.preserve = preserve; 323 setPreservesPitch(preserve); 324 // } 325 } 326 327 private void fwkSetPreload(int preload) { 328 log.fine("fwkSetPreload({0})", 329 preload == PRELOAD_NONE ? "PRELOAD_NONE" 330 : preload == PRELOAD_METADATA ? "PRELOAD_METADATA" 331 : preload == PRELOAD_AUTO ? "PRELOAD_AUTO" 332 : ("INVALID VALUE: " + preload)); 333 this.preload = preload; 334 } 335 336 /* called from GraphicsDecoder */ 337 void render(WCGraphicsContext gc, int x, int y, int w, int h) { 338 log.finer("render(x={0}, y={1}, w={2}, h={3}", new Object[]{x, y, w, h}); 339 renderCurrentFrame(gc, x, y, w, h); 340 } 341 342 343 /* native methods */ 344 private native void notifyNetworkStateChanged(long nPtr, int networkState); 345 private native void notifyReadyStateChanged(long nPtr, int readyState); 346 private native void notifyPaused(long nPtr, boolean paused); 347 private native void notifySeeking(long nPtr, boolean seeking, int readyState); 348 private native void notifyFinished(long nPtr); 349 private native void notifyReady(long nPtr, boolean hasVideo, boolean hasAudio, float duration); 350 private native void notifyDurationChanged(long nPtr, float duration); 351 private native void notifySizeChanged(long nPtr, int width, int height); 352 private native void notifyNewFrame(long nPtr); 353 private native void notifyBufferChanged(long nPtr, float[] ranges, int bytesLoaded); 354 355 }