h264 splash
This commit is contained in:
parent
1f7b09285b
commit
fdab954040
5 changed files with 213 additions and 34 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
.gradle
|
||||
build
|
||||
9
.vscode/settings.json
vendored
Normal file
9
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"java.configuration.runtimes": [
|
||||
{
|
||||
"name": "JavaSE-1.8",
|
||||
"path": "/usr/local/jdk8u462-b08",
|
||||
"default": true
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
@ -73,6 +73,8 @@ jar {
|
|||
shadowJar {
|
||||
classifier = ''
|
||||
relocate 'com.elytradev.mini', 'pl.asie.splashanimation.core.repackage.com.elytradev.mini'
|
||||
relocate 'org.jcodec', 'pl.asie.splashanimation.core.repackage.org.jcodec'
|
||||
relocate 'net.sourceforge.jaad', 'pl.asie.splashanimation.core.repackage.net.sourceforge.jaad'
|
||||
configurations = [project.configurations.shadow]
|
||||
manifest.attributes "Manifest-Version": "1.0"
|
||||
manifest.attributes "FMLCorePlugin": "pl.asie.splashanimation.core.SplashAnimationCoremod"
|
||||
|
|
@ -110,6 +112,8 @@ artifacts {
|
|||
dependencies {
|
||||
compile 'com.elytradev:mini:0.2-SNAPSHOT'
|
||||
shadow 'com.elytradev:mini:0.2-SNAPSHOT'
|
||||
compile 'org.jcodec:jcodec:0.2.5'
|
||||
shadow 'org.jcodec:jcodec:0.2.5'
|
||||
}
|
||||
|
||||
//task signShadowJar(type: SignJar, dependsOn: shadowJar) {
|
||||
|
|
|
|||
0
gradlew
vendored
Normal file → Executable file
0
gradlew
vendored
Normal file → Executable file
|
|
@ -32,6 +32,16 @@ import java.awt.image.BufferedImage;
|
|||
import java.io.File;
|
||||
import java.nio.IntBuffer;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.jcodec.codecs.h264.H264Decoder;
|
||||
import org.jcodec.common.io.NIOUtils;
|
||||
import org.jcodec.common.model.ColorSpace;
|
||||
import org.jcodec.common.model.Packet;
|
||||
import org.jcodec.codecs.h264.BufferH264ES;
|
||||
import org.jcodec.common.model.Picture;
|
||||
import org.jcodec.scale.ColorUtil;
|
||||
import org.jcodec.scale.Transform;
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
import static org.lwjgl.opengl.GL12.GL_BGRA;
|
||||
|
|
@ -100,6 +110,128 @@ public class SplashAnimationRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
public static class H264Provider implements Runnable {
|
||||
private final H264Decoder decoder = new H264Decoder();
|
||||
private final LinkedList<BufferedImage> imageList = new LinkedList<>();
|
||||
private final int bufferSize;
|
||||
private int lastImage = 0;
|
||||
private boolean run = true;
|
||||
public boolean noFile = false;
|
||||
private final AtomicBoolean done;
|
||||
public int width;
|
||||
public int height;
|
||||
private Packet newFrame;
|
||||
private int counter = 0;
|
||||
|
||||
private BufferH264ES es;
|
||||
|
||||
public H264Provider(String file, AtomicBoolean isDone) {
|
||||
done = isDone;
|
||||
bufferSize = Math.max(5, (int) Math.ceil(1 / frameDelay));
|
||||
try {
|
||||
File f = new File(file);
|
||||
es = new BufferH264ES(NIOUtils.fetchFromFile(f));
|
||||
|
||||
handleDecode();
|
||||
|
||||
if (imageList.isEmpty()) {
|
||||
throw new Exception("first decoding failed!");
|
||||
}
|
||||
|
||||
width = imageList.peekFirst().getWidth();
|
||||
height = imageList.peekFirst().getHeight();
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
noFile = true;
|
||||
return; // Pass
|
||||
}
|
||||
}
|
||||
|
||||
public BufferedImage getImage(int pos) {
|
||||
while (lastImage < pos) {
|
||||
if (imageList.size() == 1) {
|
||||
return imageList.peekFirst();
|
||||
} else if (imageList.isEmpty()) {
|
||||
return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
|
||||
}
|
||||
|
||||
imageList.remove();
|
||||
lastImage++;
|
||||
}
|
||||
|
||||
if (imageList.isEmpty()) {
|
||||
return new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
|
||||
}
|
||||
|
||||
return imageList.peekFirst();
|
||||
}
|
||||
|
||||
public BufferedImage toBitmap(Picture src) {
|
||||
byte[] srcData = src.getPlaneData(0);
|
||||
int[] packed = new int[src.getWidth() * src.getHeight()];
|
||||
|
||||
for (int i = 0, dstOff = 0, srcOff = 0; i < src.getCroppedHeight(); i++) {
|
||||
for (int j = 0; j < src.getCroppedWidth(); j++, dstOff++, srcOff += 3) {
|
||||
packed[dstOff] = (255 << 24) | ((srcData[srcOff] + 128) << 16) | ((srcData[srcOff + 1] + 128) << 8)
|
||||
| (srcData[srcOff + 2] + 128);
|
||||
}
|
||||
srcOff += src.getWidth() - src.getCroppedWidth();
|
||||
}
|
||||
|
||||
BufferedImage dst = new BufferedImage(src.getWidth(), src.getHeight(), BufferedImage.TYPE_INT_ARGB);
|
||||
dst.setRGB(0, 0, src.getWidth(), src.getHeight(), packed, 0, src.getWidth());
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
public boolean handleDecode() {
|
||||
newFrame = es.nextFrame();
|
||||
if (newFrame != null) {
|
||||
try {
|
||||
Picture buf = Picture.create(1920, 1088, ColorSpace.YUV420);
|
||||
Picture out = decoder.decodeFrame(newFrame.getData(), buf.getData()).cropped();
|
||||
Picture pic = out.createCompatible();
|
||||
pic.copyFrom(out);
|
||||
|
||||
Transform transform = ColorUtil.getTransform(pic.getColor(), ColorSpace.RGB);
|
||||
Picture rgb = Picture.create(pic.getWidth(), pic.getHeight(), ColorSpace.RGB);
|
||||
transform.transform(pic, rgb);
|
||||
|
||||
synchronized (imageList) {
|
||||
imageList.add(toBitmap(rgb));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.err.println(String.format("decoding frame %d failed:", counter));
|
||||
e.printStackTrace();
|
||||
}
|
||||
counter++;
|
||||
return true;
|
||||
} else {
|
||||
h264DoneFlag.compareAndSet(false, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
while (run) {
|
||||
try {
|
||||
while (imageList.size() < bufferSize) {
|
||||
if (!handleDecode()) break;
|
||||
}
|
||||
|
||||
Thread.sleep(Math.round(frameDelay * 1000));
|
||||
} catch (InterruptedException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
this.run = false;
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean animationSolid = false;
|
||||
private static boolean animationScaleUp = false;
|
||||
private static boolean animationScaleDown = false;
|
||||
|
|
@ -115,9 +247,12 @@ public class SplashAnimationRenderer {
|
|||
private static int height = -1;
|
||||
|
||||
private static ImageProvider provider;
|
||||
private static H264Provider h264provider;
|
||||
private static Thread providerThread;
|
||||
private static int frameCount;
|
||||
|
||||
private static AtomicBoolean h264DoneFlag = new AtomicBoolean(false);
|
||||
|
||||
public static void run() {
|
||||
switch (stage) {
|
||||
case 0:
|
||||
|
|
@ -154,47 +289,63 @@ public class SplashAnimationRenderer {
|
|||
config.save();
|
||||
}
|
||||
|
||||
File imgDir = new File(frameStr);
|
||||
if (frameStr.endsWith(".h264") || frameStr.endsWith(".264")) {
|
||||
h264provider = new H264Provider(frameStr, h264DoneFlag);
|
||||
if (h264provider.noFile) {
|
||||
System.err.println("H264 open failed!");
|
||||
stage = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
TreeMap<Integer, File> files = new TreeMap<>();
|
||||
frameCount = -1;
|
||||
providerThread = new Thread(h264provider);
|
||||
providerThread.start();
|
||||
|
||||
width = h264provider.width;
|
||||
height = h264provider.height;
|
||||
} else {
|
||||
File imgDir = new File(frameStr);
|
||||
|
||||
if (imgDir.exists() && imgDir.isDirectory()) {
|
||||
for (File imgFile : imgDir.listFiles()) {
|
||||
String s = imgFile.getName().split("\\.")[0];
|
||||
try {
|
||||
Integer i = Integer.valueOf(s);
|
||||
if (i >= 0) {
|
||||
files.put(i, imgFile);
|
||||
TreeMap<Integer, File> files = new TreeMap<>();
|
||||
|
||||
if (imgDir.exists() && imgDir.isDirectory()) {
|
||||
for (File imgFile : imgDir.listFiles()) {
|
||||
String s = imgFile.getName().split("\\.")[0];
|
||||
try {
|
||||
Integer i = Integer.valueOf(s);
|
||||
if (i >= 0) {
|
||||
files.put(i, imgFile);
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// pass
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<File> imageFiles = new ArrayList<>();
|
||||
List<File> imageFiles = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<Integer, File> entry : files.entrySet()) {
|
||||
imageFiles.add(entry.getValue());
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(entry.getValue());
|
||||
width = image.getWidth();
|
||||
height = image.getHeight();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
for (Map.Entry<Integer, File> entry : files.entrySet()) {
|
||||
imageFiles.add(entry.getValue());
|
||||
try {
|
||||
BufferedImage image = ImageIO.read(entry.getValue());
|
||||
width = image.getWidth();
|
||||
height = image.getHeight();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (imageFiles.isEmpty() || width < 0 || height < 0) {
|
||||
System.err.println("Found no images!");
|
||||
stage = 2;
|
||||
return;
|
||||
}
|
||||
if (imageFiles.isEmpty() || width < 0 || height < 0) {
|
||||
System.err.println("Found no images!");
|
||||
stage = 2;
|
||||
return;
|
||||
}
|
||||
|
||||
frameCount = imageFiles.size();
|
||||
provider = new ImageProvider(imageFiles);
|
||||
providerThread = new Thread(provider);
|
||||
providerThread.start();
|
||||
frameCount = imageFiles.size();
|
||||
provider = new ImageProvider(imageFiles);
|
||||
providerThread = new Thread(provider);
|
||||
providerThread.start();
|
||||
}
|
||||
|
||||
animTexWidth = MathHelper.smallestEncompassingPowerOfTwo(width);
|
||||
animTexHeight = MathHelper.smallestEncompassingPowerOfTwo(height);
|
||||
|
|
@ -229,7 +380,12 @@ public class SplashAnimationRenderer {
|
|||
GL11.glBindTexture(GL11.GL_TEXTURE_2D, animTexture);
|
||||
|
||||
if (i != uploadedFrame) {
|
||||
BufferedImage img = provider.getImage(i);
|
||||
BufferedImage img;
|
||||
if (h264provider != null) {
|
||||
img = h264provider.getImage(i);
|
||||
} else {
|
||||
img = provider.getImage(i);
|
||||
}
|
||||
|
||||
int[] t = img.getRGB(0, 0, img.getWidth(), img.getHeight(), null, 0, img.getWidth());
|
||||
IntBuffer buf = BufferUtils.createIntBuffer(t.length);
|
||||
|
|
@ -265,7 +421,11 @@ public class SplashAnimationRenderer {
|
|||
private static void render() {
|
||||
float alpha = 1.0f;
|
||||
|
||||
if (renderFrameIndex >= frameCount) {
|
||||
if (h264provider != null && h264DoneFlag.get() && frameCount == -1) {
|
||||
frameCount = renderFrameIndex;
|
||||
}
|
||||
|
||||
if (renderFrameIndex >= frameCount && frameCount != -1) {
|
||||
float fadeProgress = ((renderFrameIndex - frameCount) * frameDelay) / fadeOutTime;
|
||||
alpha = 1.0f - fadeProgress;
|
||||
if (alpha <= 0.0f) {
|
||||
|
|
@ -329,7 +489,7 @@ public class SplashAnimationRenderer {
|
|||
}
|
||||
GL11.glEnd();
|
||||
|
||||
bindFrame(Math.min(renderFrameIndex, frameCount - 1));
|
||||
bindFrame(frameCount == -1 ? renderFrameIndex : Math.min(renderFrameIndex, frameCount - 1));
|
||||
|
||||
GL11.glColor4f(1, 1, 1, alpha);
|
||||
GL11.glBegin(GL11.GL_QUADS);
|
||||
|
|
@ -355,7 +515,11 @@ public class SplashAnimationRenderer {
|
|||
if (provider != null) {
|
||||
provider.stop();
|
||||
}
|
||||
if (h264provider != null) {
|
||||
h264provider.stop();
|
||||
}
|
||||
provider = null;
|
||||
h264provider = null;
|
||||
providerThread = null;
|
||||
stage = 3;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue