mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2025-07-20 11:33:06 +00:00
157 lines
3.6 KiB
Java
157 lines
3.6 KiB
Java
package com.limelight.nvstream.av.video;
|
|
|
|
import java.io.BufferedReader;
|
|
import java.io.File;
|
|
import java.io.FileReader;
|
|
import java.io.IOException;
|
|
import java.nio.ByteBuffer;
|
|
import android.view.Surface;
|
|
|
|
import com.limelight.nvstream.av.AvByteBufferDescriptor;
|
|
import com.limelight.nvstream.av.AvDecodeUnit;
|
|
|
|
public class CpuDecoderRenderer implements DecoderRenderer {
|
|
|
|
private Surface renderTarget;
|
|
private ByteBuffer decoderBuffer;
|
|
private Thread rendererThread;
|
|
private int perfLevel;
|
|
|
|
private static final int LOW_PERF = 1;
|
|
private static final int MED_PERF = 2;
|
|
private static final int HIGH_PERF = 3;
|
|
|
|
private int findOptimalPerformanceLevel() {
|
|
StringBuilder cpuInfo = new StringBuilder();
|
|
BufferedReader br = null;
|
|
try {
|
|
br = new BufferedReader(new FileReader(new File("/proc/cpuinfo")));
|
|
for (;;) {
|
|
int ch = br.read();
|
|
if (ch == -1)
|
|
break;
|
|
cpuInfo.append((char)ch);
|
|
}
|
|
|
|
// Here we're doing very simple heuristics based on CPU model
|
|
String cpuInfoStr = cpuInfo.toString();
|
|
|
|
// We order them from greatest to least for proper detection
|
|
// of devices with multiple sets of cores (like Exynos 5 Octa)
|
|
// TODO Make this better
|
|
if (cpuInfoStr.contains("0xc0f")) {
|
|
// Cortex-A15
|
|
return MED_PERF;
|
|
}
|
|
else if (cpuInfoStr.contains("0xc09")) {
|
|
// Cortex-A9
|
|
return LOW_PERF;
|
|
}
|
|
else if (cpuInfoStr.contains("0xc07")) {
|
|
// Cortex-A7
|
|
return LOW_PERF;
|
|
}
|
|
else {
|
|
// Didn't have anything we're looking for
|
|
return MED_PERF;
|
|
}
|
|
} catch (IOException e) {
|
|
} finally {
|
|
if (br != null) {
|
|
try {
|
|
br.close();
|
|
} catch (IOException e) {}
|
|
}
|
|
}
|
|
|
|
// Couldn't read cpuinfo, so assume medium
|
|
return MED_PERF;
|
|
}
|
|
|
|
@Override
|
|
public void setup(int width, int height, Surface renderTarget) {
|
|
this.renderTarget = renderTarget;
|
|
this.perfLevel = findOptimalPerformanceLevel();
|
|
|
|
int err = AvcDecoder.init(width, height, MED_PERF);
|
|
if (err != 0) {
|
|
throw new IllegalStateException("AVC decoder initialization failure: "+err);
|
|
}
|
|
|
|
decoderBuffer = ByteBuffer.allocate(92*1024);
|
|
|
|
System.out.println("Using software decoding (performance level: "+perfLevel+")");
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
rendererThread = new Thread() {
|
|
@Override
|
|
public void run() {
|
|
int frameRateTarget;
|
|
long nextFrameTime = System.currentTimeMillis();
|
|
|
|
switch (perfLevel) {
|
|
case HIGH_PERF:
|
|
frameRateTarget = 45;
|
|
break;
|
|
case MED_PERF:
|
|
frameRateTarget = 30;
|
|
break;
|
|
case LOW_PERF:
|
|
default:
|
|
frameRateTarget = 15;
|
|
break;
|
|
}
|
|
|
|
while (!isInterrupted())
|
|
{
|
|
long diff = nextFrameTime - System.currentTimeMillis();
|
|
|
|
if (diff > 0) {
|
|
// Sleep until the frame should be rendered
|
|
try {
|
|
Thread.sleep(diff);
|
|
} catch (InterruptedException e) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
nextFrameTime = computePresentationTimeMs(frameRateTarget);
|
|
AvcDecoder.redraw(renderTarget);
|
|
}
|
|
}
|
|
};
|
|
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 void submitDecodeUnit(AvDecodeUnit decodeUnit) {
|
|
decoderBuffer.clear();
|
|
|
|
for (AvByteBufferDescriptor bbd : decodeUnit.getBufferList()) {
|
|
decoderBuffer.put(bbd.data, bbd.offset, bbd.length);
|
|
}
|
|
|
|
AvcDecoder.decode(decoderBuffer.array(), 0, decodeUnit.getDataLength());
|
|
}
|
|
}
|