mirror of
https://github.com/moonlight-stream/moonlight-embedded.git
synced 2026-02-16 10:30:47 +00:00
142 lines
3.7 KiB
Java
142 lines
3.7 KiB
Java
package com.limelight.binding.video;
|
|
|
|
import java.awt.Graphics;
|
|
import java.awt.image.BufferedImage;
|
|
import java.awt.image.DataBufferInt;
|
|
import java.nio.ByteBuffer;
|
|
|
|
import javax.swing.JFrame;
|
|
|
|
import com.limelight.nvstream.av.ByteBufferDescriptor;
|
|
import com.limelight.nvstream.av.DecodeUnit;
|
|
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
|
import com.limelight.nvstream.av.video.cpu.AvcDecoder;
|
|
|
|
public class SwingCpuDecoderRenderer implements VideoDecoderRenderer {
|
|
|
|
private Thread rendererThread;
|
|
private int targetFps;
|
|
private int width, height;
|
|
|
|
private Graphics graphics;
|
|
private BufferedImage image;
|
|
|
|
private static final int DECODER_BUFFER_SIZE = 92*1024;
|
|
private ByteBuffer decoderBuffer;
|
|
|
|
// Only sleep if the difference is above this value
|
|
private static final int WAIT_CEILING_MS = 8;
|
|
|
|
@Override
|
|
public void setup(int width, int height, Object renderTarget, int drFlags) {
|
|
this.targetFps = 30;
|
|
this.width = width;
|
|
this.height = height;
|
|
|
|
// Single threaded low latency decode is ideal
|
|
int avcFlags = AvcDecoder.LOW_LATENCY_DECODE;
|
|
int threadCount = 1;
|
|
|
|
// Hack to work around the bad Java native library loader
|
|
// which can't resolve native library dependencies
|
|
if (System.getProperty("os.name").contains("Windows")) {
|
|
System.loadLibrary("avutil-52");
|
|
System.loadLibrary("postproc-52");
|
|
System.loadLibrary("pthreadVC2");
|
|
}
|
|
|
|
int err = AvcDecoder.init(width, height, avcFlags, threadCount);
|
|
if (err != 0) {
|
|
throw new IllegalStateException("AVC decoder initialization failure: "+err);
|
|
}
|
|
|
|
graphics = ((JFrame)renderTarget).getGraphics();
|
|
|
|
image = new BufferedImage(width, height,
|
|
BufferedImage.TYPE_INT_BGR);
|
|
|
|
decoderBuffer = ByteBuffer.allocate(DECODER_BUFFER_SIZE + AvcDecoder.getInputPaddingSize());
|
|
|
|
System.out.println("Using software decoding");
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
rendererThread = new Thread() {
|
|
@Override
|
|
public void run() {
|
|
long nextFrameTime = System.currentTimeMillis();
|
|
int[] imageBuffer = ((DataBufferInt)image.getRaster().getDataBuffer()).getData();
|
|
|
|
while (!isInterrupted())
|
|
{
|
|
long diff = nextFrameTime - System.currentTimeMillis();
|
|
|
|
if (diff < WAIT_CEILING_MS) {
|
|
// We must call Thread.sleep in order to be interruptable
|
|
diff = 0;
|
|
}
|
|
|
|
try {
|
|
Thread.sleep(diff);
|
|
} catch (InterruptedException e) {
|
|
return;
|
|
}
|
|
|
|
nextFrameTime = computePresentationTimeMs(targetFps);
|
|
if (AvcDecoder.getRgbFrameInt(imageBuffer, imageBuffer.length)) {
|
|
graphics.drawImage(image, 0, 0, width, height, null);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
rendererThread.setName("Video - Renderer (CPU)");
|
|
rendererThread.start();
|
|
}
|
|
|
|
private long computePresentationTimeMs(int frameRate) {
|
|
return System.currentTimeMillis() + (1000 / frameRate);
|
|
}
|
|
|
|
@Override
|
|
public void stop() {
|
|
rendererThread.interrupt();
|
|
|
|
try {
|
|
rendererThread.join();
|
|
} catch (InterruptedException e) { }
|
|
}
|
|
|
|
@Override
|
|
public void release() {
|
|
AvcDecoder.destroy();
|
|
}
|
|
|
|
@Override
|
|
public boolean submitDecodeUnit(DecodeUnit decodeUnit) {
|
|
byte[] data;
|
|
|
|
// Use the reserved decoder buffer if this decode unit will fit
|
|
if (decodeUnit.getDataLength() <= DECODER_BUFFER_SIZE) {
|
|
decoderBuffer.clear();
|
|
|
|
for (ByteBufferDescriptor bbd : decodeUnit.getBufferList()) {
|
|
decoderBuffer.put(bbd.data, bbd.offset, bbd.length);
|
|
}
|
|
|
|
data = decoderBuffer.array();
|
|
}
|
|
else {
|
|
data = new byte[decodeUnit.getDataLength()+AvcDecoder.getInputPaddingSize()];
|
|
|
|
int offset = 0;
|
|
for (ByteBufferDescriptor bbd : decodeUnit.getBufferList()) {
|
|
System.arraycopy(bbd.data, bbd.offset, data, offset, bbd.length);
|
|
offset += bbd.length;
|
|
}
|
|
}
|
|
|
|
return (AvcDecoder.decode(data, 0, decodeUnit.getDataLength()) == 0);
|
|
}
|
|
}
|