/* * Copyright (c) 2011, 2018, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.webkit.graphics; import com.sun.javafx.logging.PlatformLogger; import com.sun.webkit.Invoker; public abstract class WCMediaPlayer extends Ref { protected final static PlatformLogger log = PlatformLogger.getLogger("webkit.mediaplayer"); // pointer to native Player object; // read the value only on FX event thread, check that it has non-zero value; // setters (ctor && fwkDispose) are called on event thread. private long nPtr; protected WCMediaPlayer() { } void setNativePointer(long nativePointer) { if (nativePointer == 0) { throw new IllegalArgumentException("nativePointer is 0"); } if (nPtr != 0) { throw new IllegalStateException("nPtr is not 0"); } this.nPtr = nativePointer; } /** * Methods to implement */ protected abstract void load(String url, String userAgent); protected abstract void cancelLoad(); protected abstract void disposePlayer(); protected abstract void prepareToPlay(); protected abstract void play(); protected abstract void pause(); protected abstract float getCurrentTime(); // the method _must_ call notifySeeking(true)/notifySeeking(false) protected abstract void seek(float time); protected abstract void setRate(float rate); protected abstract void setVolume(float volume); protected abstract void setMute(boolean mute); protected abstract void setSize(int w, int h); protected abstract void setPreservesPitch(boolean preserve); protected abstract void renderCurrentFrame(WCGraphicsContext gc, int x, int y, int w, int h); /** * Obtains current "preserves pitch" value. */ protected boolean getPreservesPitch() { return preserve; } protected int getNetworkState() { return networkState; } protected int getReadyState() { return readyState; } protected int getPreload() { return preload; } protected boolean isPaused() { return paused; } protected boolean isSeeking() { return seeking; } /* ======================================= */ /* Methods to notify webkit about events */ /* ======================================= */ //enum NetworkState { Empty, Idle, Loading, Loaded, FormatError, NetworkError, DecodeError }; protected final static int NETWORK_STATE_EMPTY = 0; protected final static int NETWORK_STATE_IDLE = 1; protected final static int NETWORK_STATE_LOADING = 2; protected final static int NETWORK_STATE_LOADED = 3; protected final static int NETWORK_STATE_FORMAT_ERROR = 4; protected final static int NETWORK_STATE_NETWORK_ERROR = 5; protected final static int NETWORK_STATE_DECODE_ERROR = 6; //enum ReadyState { HaveNothing, HaveMetadata, HaveCurrentData, HaveFutureData, HaveEnoughData }; protected final static int READY_STATE_HAVE_NOTHING = 0; protected final static int READY_STATE_HAVE_METADATA = 1; protected final static int READY_STATE_HAVE_CURRENT_DATA = 2; protected final static int READY_STATE_HAVE_FUTURE_DATA = 3; protected final static int READY_STATE_HAVE_ENOUGH_DATA = 4; //enum Preload { None, MetaData, Auto }; protected final static int PRELOAD_NONE = 0; protected final static int PRELOAD_METADATA = 1; protected final static int PRELOAD_AUTO = 2; private int networkState = NETWORK_STATE_EMPTY; private int readyState = READY_STATE_HAVE_NOTHING; private int preload = PRELOAD_AUTO; private boolean paused = true; private boolean seeking = false; protected void notifyNetworkStateChanged(int networkState) { if (this.networkState != networkState) { this.networkState = networkState; final int _networkState = networkState; Invoker.getInvoker().invokeOnEventThread(() -> { if (nPtr != 0) { notifyNetworkStateChanged(nPtr, _networkState); } }); } } protected void notifyReadyStateChanged(int readyState) { if (this.readyState != readyState) { this.readyState = readyState; final int _readyState = readyState; Invoker.getInvoker().invokeOnEventThread(() -> { if (nPtr != 0) { notifyReadyStateChanged(nPtr, _readyState); } }); } } protected void notifyPaused(boolean paused) { log.fine("notifyPaused, {0} => {1}", new Object[]{Boolean.valueOf(this.paused), Boolean.valueOf(paused)}); if (this.paused != paused) { this.paused = paused; final boolean _paused = paused; Invoker.getInvoker().invokeOnEventThread(() -> { if (nPtr != 0) { notifyPaused(nPtr, _paused); } }); } } // pass -1 as readyState value if the state is not changed protected void notifySeeking(boolean seeking, int readyState) { log.fine("notifySeeking, {0} => {1}", new Object[]{Boolean.valueOf(this.seeking), Boolean.valueOf(seeking)}); if (this.seeking != seeking || this.readyState != readyState) { this.seeking = seeking; this.readyState = readyState; final boolean _seeking = seeking; final int _readyState = readyState; Invoker.getInvoker().invokeOnEventThread(() -> { if (nPtr != 0) { notifySeeking(nPtr, _seeking, _readyState); } }); } } protected void notifyFinished() { Invoker.getInvoker().invokeOnEventThread(() -> { if (nPtr != 0) { notifyFinished(nPtr); } }); } /** got metadata */ protected void notifyReady(boolean hasVideo, boolean hasAudio, float duration) { final boolean _hasVideo = hasVideo; final boolean _hasAudio = hasAudio; final float _duration = duration; Invoker.getInvoker().invokeOnEventThread(() -> { if (nPtr != 0) { notifyReady(nPtr, _hasVideo, _hasAudio, _duration); } }); } protected void notifyDurationChanged(float newDuration) { final float _newDuration = newDuration; Invoker.getInvoker().invokeOnEventThread(() -> { if (nPtr != 0) { notifyDurationChanged(nPtr, _newDuration); } }); } protected void notifySizeChanged(int width, int height) { // notify on event thread to ensure native object is valid (nPtr != 0) final int _width = width; final int _height = height; Invoker.getInvoker().invokeOnEventThread(() -> { if (nPtr != 0) { notifySizeChanged(nPtr, _width, _height); } }); } private Runnable newFrameNotifier = () -> { if (nPtr != 0) { notifyNewFrame(nPtr); } }; protected void notifyNewFrame() { Invoker.getInvoker().invokeOnEventThread(newFrameNotifier); } /** {@code ranges} array contains pairs [start,end] of the buffered times */ protected void notifyBufferChanged(float[] ranges, int bytesLoaded) { // notify on event thread to ensure native object is valid (nPtr != 0) final float[] _ranges = ranges; final int _bytesLoaded = bytesLoaded; Invoker.getInvoker().invokeOnEventThread(() -> { if (nPtr != 0) { notifyBufferChanged(nPtr, _ranges, _bytesLoaded); } }); } /* ======================================= */ /* Methods called from webkit */ /* ======================================= */ private void fwkLoad(String url, String userAgent) { log.fine("fwkLoad, url={0}, userAgent={1}", new Object[] {url, userAgent}); load(url, userAgent); } private void fwkCancelLoad() { log.fine("fwkCancelLoad"); cancelLoad(); } private void fwkPrepareToPlay() { log.fine("fwkPrepareToPlay"); prepareToPlay(); } private void fwkDispose() { log.fine("fwkDispose"); nPtr = 0; cancelLoad(); disposePlayer(); } private void fwkPlay() { log.fine("fwkPlay"); play(); } private void fwkPause() { log.fine("fwkPause"); pause(); } private float fwkGetCurrentTime() { float res = getCurrentTime(); log.finer("fwkGetCurrentTime(), return {0}", res); return res; } private void fwkSeek(float time) { log.fine("fwkSeek({0})", time); seek(time); } private void fwkSetRate(float rate) { log.fine("fwkSetRate({0})", rate); setRate(rate); } private void fwkSetVolume(float volume) { log.fine("fwkSetVolume({0})", volume); setVolume(volume); } private void fwkSetMute(boolean mute) { log.fine("fwkSetMute({0})", mute); setMute(mute); } private void fwkSetSize(int w, int h) { //log.fine("setSize({0} x {1})", new Object[]{w, h}); setSize(w, h); } private boolean preserve = true; private void fwkSetPreservesPitch(boolean preserve) { log.fine("setPreservesPitch({0})", preserve); // synchronized(renderLock) { this.preserve = preserve; setPreservesPitch(preserve); // } } private void fwkSetPreload(int preload) { log.fine("fwkSetPreload({0})", preload == PRELOAD_NONE ? "PRELOAD_NONE" : preload == PRELOAD_METADATA ? "PRELOAD_METADATA" : preload == PRELOAD_AUTO ? "PRELOAD_AUTO" : ("INVALID VALUE: " + preload)); this.preload = preload; } /* called from GraphicsDecoder */ void render(WCGraphicsContext gc, int x, int y, int w, int h) { log.finer("render(x={0}, y={1}, w={2}, h={3}", new Object[]{x, y, w, h}); renderCurrentFrame(gc, x, y, w, h); } /* native methods */ private native void notifyNetworkStateChanged(long nPtr, int networkState); private native void notifyReadyStateChanged(long nPtr, int readyState); private native void notifyPaused(long nPtr, boolean paused); private native void notifySeeking(long nPtr, boolean seeking, int readyState); private native void notifyFinished(long nPtr); private native void notifyReady(long nPtr, boolean hasVideo, boolean hasAudio, float duration); private native void notifyDurationChanged(long nPtr, float duration); private native void notifySizeChanged(long nPtr, int width, int height); private native void notifyNewFrame(long nPtr); private native void notifyBufferChanged(long nPtr, float[] ranges, int bytesLoaded); }