Path: blob/main/samples/ayunami2000/VideoMapPacketCodec.java
8641 views
package ayunami2000;12import java.io.ByteArrayOutputStream;3import java.io.DataOutputStream;4import java.io.IOException;56public class VideoMapPacketCodec {78public final int[][] mapIds;9private boolean loop;10private String url;11private int duration;12private long timestamp;13private long pauseTimestamp;14private double posX;15private double posY;16private double posZ;17private float volume;18private int frameRate;19private boolean requiresFullResetPacket;20private boolean requiresPositionPacket;21private boolean isDisabled;2223/**24* @param mapIds 2D grid of map IDs that make up the screen (mapIds[y][x])25* @param posX audio playback X coord26* @param posY audio playback Y coord27* @param posZ audio playback Z coord28* @param volume the volume of the clip29*/30public VideoMapPacketCodec(int[][] mapIds, double posX, double posY, double posZ, float volume) {31this.mapIds = mapIds;32this.url = null;33this.posX = posX;34this.posY = posY;35this.posZ = posZ;36this.volume = volume;37this.frameRate = 30;38this.requiresPositionPacket = true;39this.requiresFullResetPacket = true;40this.isDisabled = true;41}4243/**44* @param mapIds 2D grid of map IDs that make up the screen (mapIds[y][x])45* @param posX audio playback X coord46* @param posY audio playback Y coord47* @param posZ audio playback Z coord48*/49public VideoMapPacketCodec(int[][] mapIds, double posX, double posY, double posZ) {50this(mapIds, posX, posY, posZ, 0.5f);51}5253/**54* @param mapIds 2D grid of map IDs that make up the screen (mapIds[y][x])55*/56public VideoMapPacketCodec(int[][] mapIds) {57this(mapIds, 0, 100, 0, 0.5f);58}5960/**61* @param posX audio playback X coord62* @param posY audio playback Y coord63* @param posZ audio playback Z coord64* @param volume the volume of the clip65* @return packet to send to players66*/67public byte[] moveAudioSource(double posX, double posY, double posZ, float volume) {68this.posX = posX;69this.posY = posY;70this.posZ = posZ;71this.volume = volume;72this.requiresPositionPacket = true;73return syncPlaybackWithPlayers();74}7576/**77* unloads video and resets all map object to vanilla renderer78* @return packet to send to players79*/80public byte[] disableVideo() {81isDisabled = true;82return syncPlaybackWithPlayers();83}8485/**86* syncs the server side video timestamp with players87* @return packet to send to players88*/89public byte[] syncPlaybackWithPlayers() {90try {91ByteArrayOutputStream bao = new ByteArrayOutputStream();92DataOutputStream str = new DataOutputStream(bao);9394if(isDisabled) {95str.write(0);96int x = mapIds[0].length;97int y = mapIds.length;98str.write((x << 4) | y);99for(int yy = 0; yy < y; ++yy) {100for(int xx = 0; xx < x; ++xx) {101str.writeShort(mapIds[yy][xx]);102}103}104return bao.toByteArray();105}106107int packetType = 1;108if(requiresFullResetPacket) {109packetType = packetType | 2;110}111if(requiresFullResetPacket || requiresPositionPacket) {112packetType = packetType | 4;113}114115str.write(packetType);116117if(requiresFullResetPacket) {118int x = mapIds[0].length;119int y = mapIds.length;120str.write((x << 4) | y);121for(int yy = 0; yy < y; ++yy) {122for(int xx = 0; xx < x; ++xx) {123str.writeShort(mapIds[yy][xx]);124}125}126str.write(frameRate);127str.writeInt(duration);128str.writeUTF(url);129}130131if(requiresFullResetPacket || requiresPositionPacket) {132str.writeFloat(volume);133str.writeDouble(posX);134str.writeDouble(posY);135str.writeDouble(posZ);136}137138str.writeInt(getElapsedMillis());139str.writeBoolean(loop);140str.writeBoolean(pauseTimestamp > 0l);141142requiresFullResetPacket = false;143requiresPositionPacket = false;144145return bao.toByteArray();146}catch(IOException e) {147throw new RuntimeException("serialization error", e);148}149}150151/**152* this is dual purpose, it calculates elapsed time but also loops or pauses the video if it is finished playing153*/154private int getElapsedMillis() {155if(pauseTimestamp > 0l) {156return (int)(pauseTimestamp - timestamp);157}158int t = (int)(System.currentTimeMillis() - timestamp);159if(loop) {160while(t > duration) {161t -= duration;162timestamp += duration;163}164}else {165if(t > duration) {166timestamp = (int)(System.currentTimeMillis() - duration);167return duration;168}169}170return t;171}172173/**174* @param url URL to an MP4 or other HTML5 supported video file175* @param loop If the video file should loop176* @param duration duration of the video in seconds177* @return packet to send to players178*/179public byte[] beginPlayback(String url, boolean loop, float duration) {180this.url = url;181this.loop = loop;182this.duration = (int)(duration * 1000.0f);183this.pauseTimestamp = 0l;184this.timestamp = 0l;185this.requiresFullResetPacket = true;186this.isDisabled = false;187return syncPlaybackWithPlayers();188}189190/**191* Tells the browser to pre-load a URL to a video to be played in the future192* @param url the URL of the video193* @param ttl the amount of time the video should stay loaded194* @return packet to send to players195*/196public static byte[] bufferVideo(String url, int ttl) {197try {198ByteArrayOutputStream bao = new ByteArrayOutputStream();199DataOutputStream str = new DataOutputStream(bao);200str.write(8);201str.writeInt(ttl);202str.writeUTF(url);203return bao.toByteArray();204}catch(IOException e) {205throw new RuntimeException("serialization error", e);206}207}208209/**210* @return the duration of the current clip211*/212public float getDuration() {213return duration * 0.001f;214}215216/**217* @return the URL of the current clip218*/219public String getURL() {220return url;221}222223/**224* @return the server's current timestamp225*/226public float getPlaybackTime() {227return getElapsedMillis() * 0.001f;228}229230/**231* @param time time in seconds to seek the video to232*/233public byte[] setPlaybackTime(float time) {234timestamp = System.currentTimeMillis() - (int)(time * 1000.0f);235return syncPlaybackWithPlayers();236}237238/**239* @return if playback is complete (false if loop)240*/241public boolean isPlaybackFinished() {242return !loop && getElapsedMillis() == duration;243}244245/**246* @param loop video should loop247*/248public byte[] setLoopEnable(boolean loop) {249this.loop = loop;250return syncPlaybackWithPlayers();251}252253/**254* @return if loop is enabled255*/256public boolean isLoopEnable() {257return loop;258}259260/**261* @param pause set if video should pause262* @return packet to send to players263*/264public byte[] setPaused(boolean pause) {265getElapsedMillis();266if(pause && pauseTimestamp <= 0l) {267pauseTimestamp = System.currentTimeMillis();268}else if(!pause && pauseTimestamp > 0l) {269timestamp = System.currentTimeMillis() - (pauseTimestamp - timestamp);270pauseTimestamp = 0l;271}272return syncPlaybackWithPlayers();273}274275/**276* @return if video is currently paused277*/278public boolean isPaused() {279return pauseTimestamp > 0l;280}281282/**283* @return current server-side volume284*/285public float getVolume() {286return volume;287}288289}290291292