package com.skcraft.playblock.player;
import net.minecraft.nbt.NBTTagCompound;
import com.skcraft.playblock.PlayBlock;
import com.skcraft.playblock.media.Media;
import com.skcraft.playblock.media.MediaResolver;
import com.skcraft.playblock.media.PlayingMedia;
/**
* This class manages the client side of the media player, notably the
* activation and actual video playing. Rendering is off-loaded to
* {@link MediaRenderer}.
*/
public class MediaPlayerClient extends MediaPlayer {
private static final int MAX_VIDEO_SIZE = 850;
private static final int RESOLUTION_FACTOR = 64;
private final MediaManager mediaManager;
private PlayingMedia playing;
private MediaRenderer renderer;
/**
* Construct a new instance.
*/
public MediaPlayerClient() {
mediaManager = PlayBlock.getClientRuntime().getMediaManager();
}
/**
* Set the currently playing media clip on this client. This may refer to
* the URI property (from {@link #getUri()}), but it could also be from
* something else.
*
* <p>
* Some validation is performed on the given URI — if the given URI
* isn't considered valid according to
* {@link MediaResolver#canPlayUri(String)}, then the currently playing
* media clip will be set to null.
* </p>
*
* @param uri
* the URI, null to play nothing
* @param position
* a non-negative position to start from, in milliseconds,
* otherwise -1 to indicate that the video should not be time
* shifted
*/
private void setPlaying(String uri, long position) {
if (uri != null && MediaResolver.canPlayUri(uri)) {
playing = PlayingMedia.fromRelative(new Media(uri), position);
} else {
playing = null;
}
}
/**
* Return whether there is actually something to play.
*
* <p>
* There could be a renderer assigned but nothing to actually playing!
* </p>
*
* @return true if there's something to play
*/
public boolean hasSomethingToPlay() {
return playing != null;
}
/**
* Get the renderer assigned to this tile entity.
*
* @return the renderer, or possibly null if there's no renderer yet
*/
public MediaRenderer getRenderer() {
return renderer;
}
/**
* Acquire a renderer and start playing the video if possible.
*/
private void setupRenderer() {
int videoWidth = (int) Math.min(MAX_VIDEO_SIZE, getWidth() * RESOLUTION_FACTOR);
int videoHeight = (int) Math.min(MAX_VIDEO_SIZE, getHeight() * RESOLUTION_FACTOR);
renderer = mediaManager.acquireRenderer(videoWidth, videoHeight);
}
/**
* Tries to play the media on this projector.
*
* <p>
* A renderer will be acquired, or a new one will be setup if the width and
* height have changed.
* </p>
*/
private void playNewMedia() {
if (renderer == null) { // Create a new renderer if we don't have one
setupRenderer();
if (renderer == null) { // Stop if there is still no renderer
return;
}
} else if (renderer.getWidth() != getWidth() || renderer.getHeight() != getHeight()) {
// Width or height change? Re-make the renderer
release();
setupRenderer();
}
if (playing != null) {
String uri = playing.getMedia().getUri();
renderer.playMedia(uri, playing.getCalculatedPosition(), !inQueueMode());
} else {
renderer.stop();
}
}
/**
* Start playing (if possible).
*
* <p>
* This can be called repeatedly even if something is already playing.
* </p>
*/
public void enable() {
if (renderer == null) { // We have nothing active
if (mediaManager.hasNoRenderer()) { // Is there a free renderer?
playNewMedia(); // Yay!
}
}
}
/**
* Stop playing.
*
* <p>
* This can be called repeatedly even if nothing is playing.
* </p>
*/
public void disable() {
release();
}
/**
* Detach the renderer from this instance and also stop the video.
*/
public void release() {
if (renderer != null) {
mediaManager.release(renderer);
renderer = null;
}
}
private void setPlayingFromTag(NBTTagCompound tag) {
if (inQueueMode()) {
String playedUri = tag.getString("playedUri");
if (!playedUri.isEmpty()) {
setPlaying(playedUri, tag.getInteger("position"));
} else {
setPlaying(null, -1);
}
} else if (tag.hasKey("uri")) {
setPlaying(getUri(), -1);
}
if (renderer != null) {
playNewMedia(); // Switch streams
}
}
@Override
public void writeNetworkedNBT(NBTTagCompound tag) {
// State NBT can only come from the server
}
@Override
public void readNetworkedNBT(NBTTagCompound tag) {
if (tag.hasKey("uri")) {
fromSharedNbt(tag);
}
if (tag.hasKey("playedUri") || tag.hasKey("uri")) {
setPlayingFromTag(tag);
}
}
}