1 /*
   2  * Copyright (c) 2011, 2018, 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 }