diff --git a/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java b/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java
index 035c9e70..25ed08fe 100644
--- a/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java
+++ b/app/src/main/java/com/limelight/binding/video/MediaCodecDecoderRenderer.java
@@ -1292,7 +1292,8 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
@SuppressWarnings("deprecation")
@Override
public int submitDecodeUnit(byte[] decodeUnitData, int decodeUnitLength, int decodeUnitType,
- int frameNumber, int frameType, long receiveTimeMs, long enqueueTimeMs) {
+ int frameNumber, int frameType, char frameHostProcessingLatency,
+ long receiveTimeMs, long enqueueTimeMs) {
if (stopping) {
// Don't bother if we're stopping
return MoonBridge.DR_OK;
@@ -1334,6 +1335,10 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
sb.append(context.getString(R.string.perf_overlay_decoder, decoder)).append('\n');
sb.append(context.getString(R.string.perf_overlay_incomingfps, fps.receivedFps)).append('\n');
sb.append(context.getString(R.string.perf_overlay_renderingfps, fps.renderedFps)).append('\n');
+ sb.append(context.getString(R.string.perf_overlay_hostprocessinglatency,
+ (float)lastTwo.minHostProcessingLatency / 10,
+ (float)lastTwo.maxHostProcessingLatency / 10,
+ (float)lastTwo.totalHostProcessingLatency / 10 / Math.max(lastTwo.framesWithHostProcessingLatency, 1))).append('\n');
sb.append(context.getString(R.string.perf_overlay_netdrops,
(float)lastTwo.framesLost / lastTwo.totalFrames * 100)).append('\n');
sb.append(context.getString(R.string.perf_overlay_netlatency,
@@ -1533,6 +1538,17 @@ public class MediaCodecDecoderRenderer extends VideoDecoderRenderer implements C
}
}
else {
+ if (frameHostProcessingLatency != 0) {
+ if (activeWindowVideoStats.minHostProcessingLatency != 0) {
+ activeWindowVideoStats.minHostProcessingLatency = (char) Math.min(activeWindowVideoStats.minHostProcessingLatency, frameHostProcessingLatency);
+ } else {
+ activeWindowVideoStats.minHostProcessingLatency = frameHostProcessingLatency;
+ }
+ activeWindowVideoStats.framesWithHostProcessingLatency += 1;
+ }
+ activeWindowVideoStats.maxHostProcessingLatency = (char) Math.max(activeWindowVideoStats.maxHostProcessingLatency, frameHostProcessingLatency);
+ activeWindowVideoStats.totalHostProcessingLatency += frameHostProcessingLatency;
+
activeWindowVideoStats.totalFramesReceived++;
activeWindowVideoStats.totalFrames++;
diff --git a/app/src/main/java/com/limelight/binding/video/VideoStats.java b/app/src/main/java/com/limelight/binding/video/VideoStats.java
index b90a14c9..b65b897e 100644
--- a/app/src/main/java/com/limelight/binding/video/VideoStats.java
+++ b/app/src/main/java/com/limelight/binding/video/VideoStats.java
@@ -11,6 +11,10 @@ class VideoStats {
int totalFramesRendered;
int frameLossEvents;
int framesLost;
+ char minHostProcessingLatency;
+ char maxHostProcessingLatency;
+ int totalHostProcessingLatency;
+ int framesWithHostProcessingLatency;
long measurementStartTimestamp;
void add(VideoStats other) {
@@ -22,6 +26,15 @@ class VideoStats {
this.frameLossEvents += other.frameLossEvents;
this.framesLost += other.framesLost;
+ if (this.minHostProcessingLatency == 0) {
+ this.minHostProcessingLatency = other.minHostProcessingLatency;
+ } else {
+ this.minHostProcessingLatency = (char) Math.min(this.minHostProcessingLatency, other.minHostProcessingLatency);
+ }
+ this.maxHostProcessingLatency = (char) Math.max(this.maxHostProcessingLatency, other.maxHostProcessingLatency);
+ this.totalHostProcessingLatency += other.totalHostProcessingLatency;
+ this.framesWithHostProcessingLatency += other.framesWithHostProcessingLatency;
+
if (this.measurementStartTimestamp == 0) {
this.measurementStartTimestamp = other.measurementStartTimestamp;
}
@@ -37,6 +50,10 @@ class VideoStats {
this.totalFramesRendered = other.totalFramesRendered;
this.frameLossEvents = other.frameLossEvents;
this.framesLost = other.framesLost;
+ this.minHostProcessingLatency = other.minHostProcessingLatency;
+ this.maxHostProcessingLatency = other.maxHostProcessingLatency;
+ this.totalHostProcessingLatency = other.totalHostProcessingLatency;
+ this.framesWithHostProcessingLatency = other.framesWithHostProcessingLatency;
this.measurementStartTimestamp = other.measurementStartTimestamp;
}
@@ -48,6 +65,10 @@ class VideoStats {
this.totalFramesRendered = 0;
this.frameLossEvents = 0;
this.framesLost = 0;
+ this.minHostProcessingLatency = 0;
+ this.maxHostProcessingLatency = 0;
+ this.totalHostProcessingLatency = 0;
+ this.framesWithHostProcessingLatency = 0;
this.measurementStartTimestamp = 0;
}
diff --git a/app/src/main/java/com/limelight/nvstream/av/video/VideoDecoderRenderer.java b/app/src/main/java/com/limelight/nvstream/av/video/VideoDecoderRenderer.java
index 397d8f4e..8c222eee 100644
--- a/app/src/main/java/com/limelight/nvstream/av/video/VideoDecoderRenderer.java
+++ b/app/src/main/java/com/limelight/nvstream/av/video/VideoDecoderRenderer.java
@@ -10,7 +10,8 @@ public abstract class VideoDecoderRenderer {
// This is called once for each frame-start NALU. This means it will be called several times
// for an IDR frame which contains several parameter sets and the I-frame data.
public abstract int submitDecodeUnit(byte[] decodeUnitData, int decodeUnitLength, int decodeUnitType,
- int frameNumber, int frameType, long receiveTimeMs, long enqueueTimeMs);
+ int frameNumber, int frameType, char frameHostProcessingLatency,
+ long receiveTimeMs, long enqueueTimeMs);
public abstract void cleanup();
diff --git a/app/src/main/java/com/limelight/nvstream/jni/MoonBridge.java b/app/src/main/java/com/limelight/nvstream/jni/MoonBridge.java
index ca08002c..0a4b6e5f 100644
--- a/app/src/main/java/com/limelight/nvstream/jni/MoonBridge.java
+++ b/app/src/main/java/com/limelight/nvstream/jni/MoonBridge.java
@@ -167,11 +167,11 @@ public class MoonBridge {
}
public static int bridgeDrSubmitDecodeUnit(byte[] decodeUnitData, int decodeUnitLength, int decodeUnitType,
- int frameNumber, int frameType,
+ int frameNumber, int frameType, char frameHostProcessingLatency,
long receiveTimeMs, long enqueueTimeMs) {
if (videoRenderer != null) {
return videoRenderer.submitDecodeUnit(decodeUnitData, decodeUnitLength,
- decodeUnitType, frameNumber, frameType, receiveTimeMs, enqueueTimeMs);
+ decodeUnitType, frameNumber, frameType, frameHostProcessingLatency, receiveTimeMs, enqueueTimeMs);
}
else {
return DR_OK;
diff --git a/app/src/main/jni/moonlight-core/callbacks.c b/app/src/main/jni/moonlight-core/callbacks.c
index 6edc32c8..ce455fcd 100644
--- a/app/src/main/jni/moonlight-core/callbacks.c
+++ b/app/src/main/jni/moonlight-core/callbacks.c
@@ -80,7 +80,7 @@ Java_com_limelight_nvstream_jni_MoonBridge_init(JNIEnv *env, jclass clazz) {
BridgeDrStartMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeDrStart", "()V");
BridgeDrStopMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeDrStop", "()V");
BridgeDrCleanupMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeDrCleanup", "()V");
- BridgeDrSubmitDecodeUnitMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeDrSubmitDecodeUnit", "([BIIIIJJ)I");
+ BridgeDrSubmitDecodeUnitMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeDrSubmitDecodeUnit", "([BIIIICJJ)I");
BridgeArInitMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeArInit", "(III)I");
BridgeArStartMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeArStart", "()V");
BridgeArStopMethod = (*env)->GetStaticMethodID(env, clazz, "bridgeArStop", "()V");
@@ -159,7 +159,7 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
ret = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod,
DecodedFrameBuffer, currentEntry->length, currentEntry->bufferType,
- decodeUnit->frameNumber, decodeUnit->frameType,
+ decodeUnit->frameNumber, decodeUnit->frameType, (jchar)decodeUnit->frameHostProcessingLatency,
(jlong)decodeUnit->receiveTimeMs, (jlong)decodeUnit->enqueueTimeMs);
if ((*env)->ExceptionCheck(env)) {
// We will crash here
@@ -180,7 +180,7 @@ int BridgeDrSubmitDecodeUnit(PDECODE_UNIT decodeUnit) {
ret = (*env)->CallStaticIntMethod(env, GlobalBridgeClass, BridgeDrSubmitDecodeUnitMethod,
DecodedFrameBuffer, offset, BUFFER_TYPE_PICDATA,
- decodeUnit->frameNumber, decodeUnit->frameType,
+ decodeUnit->frameNumber, decodeUnit->frameType, (jchar)decodeUnit->frameHostProcessingLatency,
(jlong)decodeUnit->receiveTimeMs, (jlong)decodeUnit->enqueueTimeMs);
if ((*env)->ExceptionCheck(env)) {
// We will crash here
diff --git a/app/src/main/jni/moonlight-core/moonlight-common-c b/app/src/main/jni/moonlight-core/moonlight-common-c
index b77072d3..284840bd 160000
--- a/app/src/main/jni/moonlight-core/moonlight-common-c
+++ b/app/src/main/jni/moonlight-core/moonlight-common-c
@@ -1 +1 @@
-Subproject commit b77072d39984019b4234d9f2adc3b673b6d57812
+Subproject commit 284840bde75c0e30dd72aaa5e4e136dedf084ee1
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index cad10e3a..8d77846c 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -108,6 +108,7 @@
Decoder: %1$s
Incoming frame rate from network: %1$.2f FPS
Rendering frame rate: %1$.2f FPS
+ Host processing latency min/max/average: %1$.1f/%2$.1f/%3$.1f ms
Frames dropped by your network connection: %1$.2f%%
Average network latency: %1$d ms (variance: %2$d ms)
Average decoding time: %1$.2f ms