h264 splash

This commit is contained in:
wrapper 2025-09-25 10:07:03 +07:00
parent 1f7b09285b
commit fdab954040
5 changed files with 213 additions and 34 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
.gradle
build

9
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,9 @@
{
"java.configuration.runtimes": [
{
"name": "JavaSE-1.8",
"path": "/usr/local/jdk8u462-b08",
"default": true
},
]
}

View file

@ -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
View file

View 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;
}