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 }