mirror of
https://github.com/moonlight-stream/moonlight-android.git
synced 2026-04-06 07:56:07 +00:00
Remove FFMPEG decoding and supporting code
This commit is contained in:
@@ -1,294 +0,0 @@
|
||||
package com.limelight.binding.video;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import android.graphics.PixelFormat;
|
||||
import android.os.Build;
|
||||
import android.view.SurfaceHolder;
|
||||
|
||||
import com.limelight.LimeLog;
|
||||
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.VideoDepacketizer;
|
||||
import com.limelight.nvstream.av.video.cpu.AvcDecoder;
|
||||
|
||||
@SuppressWarnings("EmptyCatchBlock")
|
||||
public class AndroidCpuDecoderRenderer extends EnhancedDecoderRenderer {
|
||||
|
||||
private Thread rendererThread, decoderThread;
|
||||
private int targetFps;
|
||||
|
||||
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 = 5;
|
||||
|
||||
private static final int LOW_PERF = 1;
|
||||
private static final int MED_PERF = 2;
|
||||
private static final int HIGH_PERF = 3;
|
||||
|
||||
private int totalFrames;
|
||||
private long totalTimeMs;
|
||||
|
||||
private final int cpuCount = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
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 (only even kind of works on ARM)
|
||||
if (Build.FINGERPRINT.contains("generic")) {
|
||||
// Emulator
|
||||
return LOW_PERF;
|
||||
}
|
||||
else 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 boolean setup(VideoFormat format, int width, int height, int redrawRate, Object renderTarget, int drFlags) {
|
||||
this.targetFps = redrawRate;
|
||||
|
||||
// We should never make it here with H265
|
||||
if (format != VideoFormat.H264) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int perfLevel = LOW_PERF; //findOptimalPerformanceLevel();
|
||||
int threadCount;
|
||||
|
||||
int avcFlags = 0;
|
||||
switch (perfLevel) {
|
||||
case HIGH_PERF:
|
||||
// Single threaded low latency decode is ideal but hard to acheive
|
||||
avcFlags = AvcDecoder.LOW_LATENCY_DECODE;
|
||||
threadCount = 1;
|
||||
break;
|
||||
|
||||
case LOW_PERF:
|
||||
// Disable the loop filter for performance reasons
|
||||
avcFlags = AvcDecoder.FAST_BILINEAR_FILTERING;
|
||||
|
||||
// Use plenty of threads to try to utilize the CPU as best we can
|
||||
threadCount = cpuCount - 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
case MED_PERF:
|
||||
avcFlags = AvcDecoder.BILINEAR_FILTERING;
|
||||
|
||||
// Only use 2 threads to minimize frame processing latency
|
||||
threadCount = 2;
|
||||
break;
|
||||
}
|
||||
|
||||
// If the user wants quality, we'll remove the low IQ flags
|
||||
if ((drFlags & VideoDecoderRenderer.FLAG_PREFER_QUALITY) != 0) {
|
||||
// Make sure the loop filter is enabled
|
||||
avcFlags &= ~AvcDecoder.DISABLE_LOOP_FILTER;
|
||||
|
||||
// Disable the non-compliant speed optimizations
|
||||
avcFlags &= ~AvcDecoder.FAST_DECODE;
|
||||
|
||||
LimeLog.info("Using high quality decoding");
|
||||
}
|
||||
|
||||
SurfaceHolder sh = (SurfaceHolder)renderTarget;
|
||||
sh.setFormat(PixelFormat.RGBX_8888);
|
||||
|
||||
int err = AvcDecoder.init(width, height, avcFlags, threadCount);
|
||||
if (err != 0) {
|
||||
throw new IllegalStateException("AVC decoder initialization failure: "+err);
|
||||
}
|
||||
|
||||
if (!AvcDecoder.setRenderTarget(sh.getSurface())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
decoderBuffer = ByteBuffer.allocate(DECODER_BUFFER_SIZE + AvcDecoder.getInputPaddingSize());
|
||||
|
||||
LimeLog.info("Using software decoding (performance level: "+perfLevel+")");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start(final VideoDepacketizer depacketizer) {
|
||||
decoderThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
DecodeUnit du;
|
||||
while (!isInterrupted()) {
|
||||
try {
|
||||
du = depacketizer.takeNextDecodeUnit();
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
|
||||
submitDecodeUnit(du);
|
||||
depacketizer.freeDecodeUnit(du);
|
||||
}
|
||||
}
|
||||
};
|
||||
decoderThread.setName("Video - Decoder (CPU)");
|
||||
decoderThread.setPriority(Thread.MAX_PRIORITY - 1);
|
||||
decoderThread.start();
|
||||
|
||||
rendererThread = new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
long nextFrameTime = MediaCodecHelper.getMonotonicMillis();
|
||||
while (!isInterrupted())
|
||||
{
|
||||
long diff = nextFrameTime - MediaCodecHelper.getMonotonicMillis();
|
||||
|
||||
if (diff > WAIT_CEILING_MS) {
|
||||
try {
|
||||
Thread.sleep(diff - WAIT_CEILING_MS);
|
||||
} catch (InterruptedException e) {
|
||||
return;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
nextFrameTime = computePresentationTimeMs(targetFps);
|
||||
AvcDecoder.redraw();
|
||||
}
|
||||
}
|
||||
};
|
||||
rendererThread.setName("Video - Renderer (CPU)");
|
||||
rendererThread.setPriority(Thread.MAX_PRIORITY);
|
||||
rendererThread.start();
|
||||
return true;
|
||||
}
|
||||
|
||||
private long computePresentationTimeMs(int frameRate) {
|
||||
return MediaCodecHelper.getMonotonicMillis() + (1000 / frameRate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
rendererThread.interrupt();
|
||||
decoderThread.interrupt();
|
||||
|
||||
try {
|
||||
rendererThread.join();
|
||||
} catch (InterruptedException e) { }
|
||||
try {
|
||||
decoderThread.join();
|
||||
} catch (InterruptedException e) { }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
AvcDecoder.destroy();
|
||||
}
|
||||
|
||||
private 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.getBufferHead();
|
||||
bbd != null; bbd = bbd.nextDescriptor) {
|
||||
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.getBufferHead();
|
||||
bbd != null; bbd = bbd.nextDescriptor) {
|
||||
System.arraycopy(bbd.data, bbd.offset, data, offset, bbd.length);
|
||||
offset += bbd.length;
|
||||
}
|
||||
}
|
||||
|
||||
boolean success = (AvcDecoder.decode(data, 0, decodeUnit.getDataLength()) == 0);
|
||||
if (success) {
|
||||
long timeAfterDecode = MediaCodecHelper.getMonotonicMillis();
|
||||
|
||||
// Add delta time to the totals (excluding probable outliers)
|
||||
long delta = timeAfterDecode - decodeUnit.getReceiveTimestamp();
|
||||
if (delta >= 0 && delta < 1000) {
|
||||
totalTimeMs += delta;
|
||||
totalFrames++;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCapabilities() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAverageDecoderLatency() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAverageEndToEndLatency() {
|
||||
if (totalFrames == 0) {
|
||||
return 0;
|
||||
}
|
||||
return (int)(totalTimeMs / totalFrames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHevcSupported() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
package com.limelight.binding.video;
|
||||
|
||||
import android.media.MediaCodecInfo;
|
||||
|
||||
import com.limelight.nvstream.av.DecodeUnit;
|
||||
import com.limelight.nvstream.av.video.VideoDecoderRenderer;
|
||||
import com.limelight.nvstream.av.video.VideoDepacketizer;
|
||||
|
||||
public class ConfigurableDecoderRenderer extends EnhancedDecoderRenderer {
|
||||
|
||||
private EnhancedDecoderRenderer decoderRenderer;
|
||||
|
||||
@Override
|
||||
public void release() {
|
||||
if (decoderRenderer != null) {
|
||||
decoderRenderer.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setup(VideoFormat format, int width, int height, int redrawRate, Object renderTarget, int drFlags) {
|
||||
if (decoderRenderer == null) {
|
||||
throw new IllegalStateException("ConfigurableDecoderRenderer not initialized");
|
||||
}
|
||||
return decoderRenderer.setup(format, width, height, redrawRate, renderTarget, drFlags);
|
||||
}
|
||||
|
||||
public void initializeWithFlags(int drFlags, int videoFormat) {
|
||||
if ((drFlags & VideoDecoderRenderer.FLAG_FORCE_HARDWARE_DECODING) != 0 ||
|
||||
((drFlags & VideoDecoderRenderer.FLAG_FORCE_SOFTWARE_DECODING) == 0 &&
|
||||
MediaCodecHelper.findProbableSafeDecoder("video/avc", MediaCodecInfo.CodecProfileLevel.AVCProfileHigh) != null)) {
|
||||
decoderRenderer = new MediaCodecDecoderRenderer(videoFormat);
|
||||
}
|
||||
else {
|
||||
decoderRenderer = new AndroidCpuDecoderRenderer();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isHardwareAccelerated() {
|
||||
if (decoderRenderer == null) {
|
||||
throw new IllegalStateException("ConfigurableDecoderRenderer not initialized");
|
||||
}
|
||||
return (decoderRenderer instanceof MediaCodecDecoderRenderer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean start(VideoDepacketizer depacketizer) {
|
||||
return decoderRenderer.start(depacketizer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
decoderRenderer.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCapabilities() {
|
||||
return decoderRenderer.getCapabilities();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void directSubmitDecodeUnit(DecodeUnit du) {
|
||||
decoderRenderer.directSubmitDecodeUnit(du);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAverageDecoderLatency() {
|
||||
if (decoderRenderer != null) {
|
||||
return decoderRenderer.getAverageDecoderLatency();
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAverageEndToEndLatency() {
|
||||
if (decoderRenderer != null) {
|
||||
return decoderRenderer.getAverageEndToEndLatency();
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isHevcSupported() {
|
||||
if (decoderRenderer != null) {
|
||||
return decoderRenderer.isHevcSupported();
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user