/*
* Copyright 2009 Sikirulai Braheem
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.bramosystems.oss.player.core.client.ui;
import com.bramosystems.oss.player.core.client.MediaInfo.MediaInfoKey;
import com.bramosystems.oss.player.core.client.*;
import com.bramosystems.oss.player.core.client.geom.MatrixSupport;
import com.bramosystems.oss.player.core.client.geom.TransformationMatrix;
import com.bramosystems.oss.player.core.client.impl.CorePlayerProvider;
import com.bramosystems.oss.player.core.client.impl.FMPStateManager;
import com.bramosystems.oss.player.core.client.impl.FlashMediaPlayerImpl;
import com.bramosystems.oss.player.core.client.playlist.MRL;
import com.bramosystems.oss.player.core.client.spi.Player;
import com.bramosystems.oss.player.core.event.client.*;
import com.bramosystems.oss.player.util.client.RegExp;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Command;
import java.util.ArrayList;
import java.util.List;
/**
* Widget to embed Flash plugin for playback of flash-supported formats
*
* <h3>Usage Example</h3>
*
* <p>
* <code><pre>
* SimplePanel panel = new SimplePanel(); // create panel to hold the player
* Widget player = null;
* try {
* // create the player
* player = new FlashMediaPlayer("www.example.com/mediafile.flv", false, "200px", "250px");
* } catch(PluginVersionException e) {
* // catch plugin version exception and alert user to download plugin first.
* // An option is to use the utility method in PlayerUtil class.
* player = PlayerUtil.getMissingPluginNotice(Plugin.FlashMediaPlayer, "Missing Plugin",
* ".. some nice message telling the user to click and download plugin first ..",
* false);
* } catch(PluginNotFoundException e) {
* // catch PluginNotFoundException and tell user to download plugin, possibly providing
* // a link to the plugin download page.
* player = new HTML(".. another kind of message telling the user to download plugin..");
* }
*
* panel.setWidget(player); // add player to panel.
* </pre></code>
*
* <h3>M3U Playlist Support</h3>
* <p>
* This player supports M3U formatted playlists. However, each entry in the playlist MUST be
* a flash-supported media file.
* </p>
*
* @author Sikirulai Braheem
* @since 1.0
*/
@Player(name = "FlashPlayer", providerFactory = CorePlayerProvider.class, minPluginVersion = FlashMediaPlayer.reqVer)
public class FlashMediaPlayer extends AbstractMediaPlayer implements PlaylistSupport, MatrixSupport {
final static String reqVer = "10.0.0";
private PluginVersion req;
private FlashMediaPlayerImpl impl;
private String playerId;
private boolean isEmbedded, resizeToVideoSize;
private ArrayList<String> _playlistCache;
private SWFWidget swf;
private String _height, _width;
private static String DEFAULT_HEIGHT = "22px";
private FlashMediaPlayer() throws PluginNotFoundException, PluginVersionException {
try {
req = PluginVersion.get(reqVer);
PluginVersion v = PlayerUtil.getFlashPlayerVersion();
if (v.compareTo(req) < 0) {
throw new PluginVersionException(Plugin.FlashPlayer, req.toString(), v.toString());
}
} catch (RegExp.RegexException ex) {
throw new PluginNotFoundException(Plugin.FlashPlayer);
}
_playlistCache = new ArrayList<String>();
resizeToVideoSize = false;
}
/**
* Constructs <code>FlashMediaPlayer</code> with the specified {@code height} and
* {@code width} to playback media located at {@code mediaURL}. Media playback
* begins automatically if {@code autoplay} is {@code true}.
*
* <p> {@code height} and {@code width} are specified as CSS units. A value of {@code null}
* for {@code height} or {@code width} puts the player in embedded mode. When in embedded mode,
* the player is made invisible on the page and media state events are propagated to registered
* listeners only. This is desired especially when used with custom sound controls. For custom
* video-playback control, specify valid CSS values for {@code height} and {@code width} but hide the
* player controls with {@code setControllerVisible(false)}.
*
* @param mediaURL the URL of the media to playback
* @param autoplay {@code true} to start playing automatically, {@code false} otherwise
* @param height the height of the player
* @param width the width of the player.
*
* @throws PluginVersionException if the required Flash plugin version is not installed on the client.
* @throws PluginNotFoundException if the Flash plugin is not installed on the client.
*/
public FlashMediaPlayer(final String mediaURL, final boolean autoplay, String height, String width)
throws PluginNotFoundException, PluginVersionException {
this();
_height = height;
_width = width;
isEmbedded = (height == null) || (width == null);
if (isEmbedded) {
_height = "0px";
_width = "0px";
}
swf = new SWFWidget(FMPStateManager.getSWFImpl(), "100%", _height, req);
swf.setFlashVar("playerId", swf.getId());
swf.setFlashVar("autoplay", Boolean.toString(autoplay));
swf.setFlashVar("evtPfx", ((CorePlayerProvider) getWidgetFactory("core")).getFMPHandlerPrefix(swf.getId()));
swf.setFlashVar("mediaURL", URL.encodePathSegment(mediaURL));
swf.commitFlashVars();
swf.addProperty("allowScriptAccess", "always");
swf.addProperty("allowFullScreen", "true");
swf.addProperty("bgcolor", "#000000");
playerId = swf.getId();
((CorePlayerProvider) getWidgetFactory("core")).initFMPHandler(playerId, new FMPStateManager.FMPStateCallback() {
@Override
public void onInit() {
impl = FlashMediaPlayerImpl.getPlayer(playerId);
for (String url : _playlistCache) {
impl.addToPlaylist(url);
}
firePlayerStateEvent(PlayerStateEvent.State.Ready);
}
@Override
public void onMessage(int type, String message) {
if (type == 1) {
fireError(message);
} else {
fireDebug(message);
}
}
@Override
public void onProgress(double progress) {
fireLoadingProgress(progress);
}
@Override
public void onMediaInfo(String info) {
MediaInfo mi = new MediaInfo();
FMPStateManager.fillMediaInfoImpl(info, mi);
fireMediaInfoAvailable(mi);
}
@Override
public void onEvent(int type, boolean buttonDown, boolean alt, boolean ctrl,
boolean shift, boolean cmd, int stageX, int stageY) {
int button = buttonDown ? NativeEvent.BUTTON_LEFT : NativeEvent.BUTTON_RIGHT;
int screenX = -1, screenY = -1;
Document _doc = Document.get();
NativeEvent event = null;
switch (type) {
case 1: // mouse down
event = _doc.createMouseDownEvent(1, screenX, screenY, stageX, stageY,
ctrl, alt, shift, cmd, button);
break;
case 2: // mouse up
event = _doc.createMouseUpEvent(1, screenX, screenY, stageX, stageY,
ctrl, alt, shift, cmd, button);
break;
case 3: // mouse move
event = _doc.createMouseMoveEvent(1, screenX, screenY, stageX, stageY,
ctrl, alt, shift, cmd, button);
break;
case 10: // click
event = _doc.createClickEvent(1, screenX, screenY, stageX, stageY,
ctrl, alt, shift, cmd);
break;
case 11: // double click
event = _doc.createDblClickEvent(1, screenX, screenY, stageX, stageY,
ctrl, alt, shift, cmd);
break;
case 20: // key down
event = _doc.createKeyDownEvent(ctrl, alt, shift, cmd, stageX);
break;
case 21: // key press
event = _doc.createKeyPressEvent(ctrl, alt, shift, cmd, stageX);
break;
case 22: // key up
event = _doc.createKeyUpEvent(ctrl, alt, shift, cmd, stageX);
break;
}
DomEvent.fireNativeEvent(event, FlashMediaPlayer.this);
}
@Override
public void onStateChanged(int stateId, int listIndex) {
switch (stateId) {
case 1: // loading started...
//// listener.onPlayerReady();
break;
case 2: // play started...
firePlayStateEvent(PlayStateEvent.State.Started, listIndex);
break;
case 3: // play stopped...
firePlayStateEvent(PlayStateEvent.State.Stopped, listIndex);
break;
case 4: // play paused...
firePlayStateEvent(PlayStateEvent.State.Paused, listIndex);
break;
case 9: // play finished...
firePlayStateEvent(PlayStateEvent.State.Finished, listIndex);
break;
case 10: // loading complete ...
fireLoadingProgress(1.0);
break;
}
}
@Override
public void onFullScreen(boolean fullscreen) {
firePlayerStateEvent(fullscreen ? PlayerStateEvent.State.FullScreenStarted : PlayerStateEvent.State.FullScreenFinished);
}
});
if (!isEmbedded) {
addMediaInfoHandler(new MediaInfoHandler() {
@Override
public void onMediaInfoAvailable(MediaInfoEvent event) {
MediaInfo info = event.getMediaInfo();
if (info.getAvailableItems().contains(MediaInfoKey.VideoHeight)
|| info.getAvailableItems().contains(MediaInfoKey.VideoWidth)) {
checkVideoSize(Integer.parseInt(info.getItem(MediaInfoKey.VideoHeight)),
Integer.parseInt(info.getItem(MediaInfoKey.VideoWidth)));
}
}
});
}
initWidget(swf);
}
/**
* Constructs <code>FlashMediaPlayer</code> to automatically playback media located at
* {@code mediaURL}.
*
* <p> Note: This constructor hides the video display component, the player controls are
* however visible.
*
* @param mediaURL the URL of the media to playback
*
* @throws PluginVersionException if the required Flash plugin version is not installed on the client.
* @throws PluginNotFoundException if the Flash plugin is not installed on the client.
*
*/
public FlashMediaPlayer(String mediaURL) throws PluginNotFoundException,
PluginVersionException {
this(mediaURL, true, DEFAULT_HEIGHT, "100%");
}
/**
* Constructs <code>FlashMediaPlayer</code> to playback media located at {@code mediaURL}.
* Media playback begins automatically if {@code autoplay} is {@code true}.
*
* <p> Note: This constructor hides the video display component, the player controls are
* however visible.
*
* @param mediaURL the URL of the media to playback
* @param autoplay {@code true} to start playing automatically, {@code false} otherwise
*
* @throws PluginVersionException if the required Flash plugin version is not installed on the client.
* @throws PluginNotFoundException if the Flash plugin is not installed on the client.
*/
public FlashMediaPlayer(String mediaURL, boolean autoplay) throws PluginNotFoundException,
PluginVersionException {
this(mediaURL, autoplay, DEFAULT_HEIGHT, "100%");
}
private void checkVideoSize(int vidHeight, int vidWidth) {
String _h = _height, _w = _width;
if (resizeToVideoSize) {
if (vidHeight == 0) {
_h = DEFAULT_HEIGHT; // suppress SWF app height for audio files ...
} else if ((vidHeight > 0) && (vidWidth > 0)) {
fireDebug("Resizing Player : " + vidWidth + " x " + vidHeight);
_h = vidHeight + "px";
_w = vidWidth + "px";
}
}
swf.setSize("100%", _h);
setWidth(_w);
if (!_height.equals(_h) && !_width.equals(_w)) {
firePlayerStateEvent(PlayerStateEvent.State.DimensionChangedOnVideo);
}
}
private void checkAvailable() {
if (!isPlayerOnPage(playerId)) {
String message = "Player not available, create an instance";
fireDebug(message);
throw new IllegalStateException(message);
}
}
/**
* Overriden to register associated resources
*/
@Override
protected void onLoad() {
swf.setSize("100%", _height);
setWidth(_width);
}
/**
* Overriden to release associated resources
*/
@Override
protected void onUnload() {
impl.closeMedia();
((CorePlayerProvider) getWidgetFactory("core")).closeFMPHandler(playerId);
}
@Override
public long getMediaDuration() {
checkAvailable();
return (long) impl.getMediaDuration();
}
@Override
public double getPlayPosition() {
checkAvailable();
return impl.getPlayPosition();
}
@Override
public double getVolume() {
checkAvailable();
return impl.getVolume();
}
@Override
public void loadMedia(String mediaURL) throws LoadException {
checkAvailable();
impl.loadMedia(mediaURL);
}
@Override
public void pauseMedia() {
checkAvailable();
impl.pauseMedia();
}
@Override
public void playMedia() throws PlayException {
checkAvailable();
impl.playMedia();
}
@Override
public void setPlayPosition(double position) {
checkAvailable();
impl.setPlayPosition(position);
}
@Override
public void setVolume(double volume) {
checkAvailable();
impl.setVolume(volume);
}
@Override
public void stopMedia() {
checkAvailable();
impl.stopMedia();
}
/**
* Displays or hides the player controls.
*/
@Override
public void setControllerVisible(final boolean show) {
if (isPlayerOnPage(playerId)) {
impl.setControllerVisible(show);
} else {
addToPlayerReadyCommandQueue("controller", new Command() {
@Override
public void execute() {
impl.setControllerVisible(show);
}
});
}
}
/**
* Checks whether the player controls are visible.
*/
@Override
public boolean isControllerVisible() {
checkAvailable();
return impl.isControllerVisible();
}
/**
* Returns the number of times this player repeats playback before stopping.
*/
@Override
public int getLoopCount() {
checkAvailable();
return impl.getLoopCount();
}
/**
* Sets the number of times the current media file should repeat playback before stopping.
*
* <p>As of version 1.0, if this player is not available on the panel, this method
* call is added to the command-queue for later execution.
*/
@Override
public void setLoopCount(final int loop) {
if (isPlayerOnPage(playerId)) {
impl.setLoopCount(loop);
} else {
addToPlayerReadyCommandQueue("loopcount", new Command() {
@Override
public void execute() {
impl.setLoopCount(loop);
}
});
}
}
@Override
public void addToPlaylist(String mediaURL) {
if (isPlayerOnPage(playerId)) {
impl.addToPlaylist(mediaURL);
} else {
_playlistCache.add(mediaURL);
}
}
@Override
public void addToPlaylist(MRL mediaLocator) {
addToPlaylist(mediaLocator.getNextResource(true));
}
@Override
public void addToPlaylist(String... mediaURLs) {
addToPlaylist(mediaURLs[0]);
}
@Override
public void addToPlaylist(List<MRL> mediaLocators) {
for (MRL mrl : mediaLocators) {
addToPlaylist(mrl);
}
}
@Override
public boolean isShuffleEnabled() {
checkAvailable();
return impl.isShuffleEnabled();
}
@Override
public void removeFromPlaylist(int index) {
checkAvailable();
impl.removeFromPlaylist(index);
}
/**
* Enables or disables players' shuffle mode.
*
* <p>As of version 1.0, if this player is not available on the panel, this method
* call is added to the command-queue for later execution.
*/
@Override
public void setShuffleEnabled(final boolean enable) {
if (isPlayerOnPage(playerId)) {
impl.setShuffleEnabled(enable);
} else {
addToPlayerReadyCommandQueue("shuffle", new Command() {
@Override
public void execute() {
impl.setShuffleEnabled(enable);
}
});
}
}
@Override
public void clearPlaylist() {
checkAvailable();
impl.clearPlaylist();
}
@Override
public int getPlaylistSize() {
checkAvailable();
return impl.getPlaylistCount();
}
@Override
public void play(int index) throws IndexOutOfBoundsException {
checkAvailable();
if (!impl.playMedia(index)) {
throw new IndexOutOfBoundsException();
}
}
@Override
public void playNext() throws PlayException {
checkAvailable();
if (!impl.playNext()) {
throw new PlayException("No more entries in playlist");
}
}
@Override
public void playPrevious() throws PlayException {
checkAvailable();
if (!impl.playPrevious()) {
throw new PlayException("Beginning of playlist reached");
}
}
@Override
public int getVideoHeight() {
checkAvailable();
return impl.getVideoHeight();
}
@Override
public int getVideoWidth() {
checkAvailable();
return impl.getVideoWidth();
}
@Override
public void setResizeToVideoSize(boolean resize) {
resizeToVideoSize = resize;
if (isPlayerOnPage(playerId)) {
// if player is on panel now update its size, otherwise
// allow it to be handled by the MediaInfoHandler...
checkVideoSize(impl.getVideoHeight(), impl.getVideoWidth());
}
}
@Override
public boolean isResizeToVideoSize() {
return resizeToVideoSize;
}
/**
* Sets the transformation matrix of the underlying Flash player.
*
* <p>If this player is not attached to a panel, this method call is added to
* the command-queue for later execution.
*/
@Override
public void setMatrix(final TransformationMatrix matrix) {
if (isPlayerOnPage(playerId)) {
impl.setMatrix(matrix.getMatrix().getVx().getX(),
matrix.getMatrix().getVy().getX(), matrix.getMatrix().getVx().getY(),
matrix.getMatrix().getVy().getY(), matrix.getMatrix().getVx().getZ(),
matrix.getMatrix().getVy().getZ());
if (resizeToVideoSize) {
checkVideoSize(getVideoHeight(), getVideoWidth());
}
} else {
addToPlayerReadyCommandQueue("matrix", new Command() {
@Override
public void execute() {
setMatrix(matrix);
}
});
}
}
@Override
public TransformationMatrix getMatrix() {
checkAvailable();
String[] elements = impl.getMatrix().split(",");
TransformationMatrix matrix = new TransformationMatrix();
matrix.getMatrix().getVx().setX(Double.parseDouble(elements[0]));
matrix.getMatrix().getVy().setX(Double.parseDouble(elements[1]));
matrix.getMatrix().getVx().setY(Double.parseDouble(elements[2]));
matrix.getMatrix().getVy().setY(Double.parseDouble(elements[3]));
matrix.getMatrix().getVx().setZ(Double.parseDouble(elements[4]));
matrix.getMatrix().getVy().setZ(Double.parseDouble(elements[5]));
return matrix;
}
@Override
public <C extends ConfigParameter> void setConfigParameter(C param, Object value) {
super.setConfigParameter(param, value);
if (param instanceof DefaultConfigParameter) {
switch ((DefaultConfigParameter) param) {
case TransparencyMode:
if (value != null) {
swf.addProperty("wmode", ((TransparencyMode) value).name().toLowerCase());
} else {
swf.addProperty("wmode", null);
}
break;
case BackgroundColor:
swf.addProperty("bgcolor", (String) value);
}
}
}
@Override
public RepeatMode getRepeatMode() {
checkAvailable();
return impl.getRepeatMode();
}
@Override
public void setRepeatMode(final RepeatMode mode) {
if (isPlayerOnPage(playerId)) {
impl.setRepeatMode(mode);
} else {
addToPlayerReadyCommandQueue("repeatMode", new Command() {
@Override
public void execute() {
impl.setRepeatMode(mode);
}
});
}
}
/**
* Sets the player to automatically hide its controls.
*
* <p>The auto-hide effect is active IF AND ONLY IF the controller is set visible
*
* @param autohide {@code true} to auto-hide the controller, {@code false} otherwise
* @since 1.2
*/
public void setAutoHideController(final boolean autohide) {
if (isPlayerOnPage(playerId)) {
impl.setAutoHideController(autohide);
} else {
addToPlayerReadyCommandQueue("autohide", new Command() {
@Override
public void execute() {
impl.setAutoHideController(autohide);
}
});
}
}
/**
* Checks whether the player automatically hides its controls.
*
* @return {@code true} is the player autohides its controls, {@code false} otherwise
* @since 1.2
*/
public boolean isAutoHideController() {
checkAvailable();
return impl.isAutoHideController();
}
}