Add RenderSynchronizer class
RenderSynchronizer is used to coordinate video rendering updates to a specific frame rate target and aligned to display refresh cycles. go/meet-android-synchronized-rendering Bug: b/217863437 Change-Id: Ie329c4c2eccfb0c9aee9b90f7ddbc370919d5e86 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/324840 Reviewed-by: Ranveer Aggarwal <ranvr@webrtc.org> Reviewed-by: Sergey Silkin <ssilkin@webrtc.org> Commit-Queue: Linus Nilsson <lnilsson@webrtc.org> Cr-Commit-Position: refs/heads/main@{#41004}
This commit is contained in:
parent
9efd080fa2
commit
52ac8eccdf
@ -221,6 +221,7 @@ if (is_android) {
|
|||||||
"api/org/webrtc/GlTextureFrameBuffer.java",
|
"api/org/webrtc/GlTextureFrameBuffer.java",
|
||||||
"api/org/webrtc/GlUtil.java",
|
"api/org/webrtc/GlUtil.java",
|
||||||
"api/org/webrtc/JavaI420Buffer.java",
|
"api/org/webrtc/JavaI420Buffer.java",
|
||||||
|
"api/org/webrtc/RenderSynchronizer.java",
|
||||||
"api/org/webrtc/RendererCommon.java",
|
"api/org/webrtc/RendererCommon.java",
|
||||||
"api/org/webrtc/SurfaceTextureHelper.java",
|
"api/org/webrtc/SurfaceTextureHelper.java",
|
||||||
"api/org/webrtc/TextureBufferImpl.java",
|
"api/org/webrtc/TextureBufferImpl.java",
|
||||||
|
|||||||
116
sdk/android/api/org/webrtc/RenderSynchronizer.java
Normal file
116
sdk/android/api/org/webrtc/RenderSynchronizer.java
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2023 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.webrtc;
|
||||||
|
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.view.Choreographer;
|
||||||
|
import androidx.annotation.GuardedBy;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class to synchronize rendering updates with display refresh cycles and save power by blocking
|
||||||
|
* updates that exceeds the target frame rate.
|
||||||
|
*/
|
||||||
|
public final class RenderSynchronizer {
|
||||||
|
|
||||||
|
/** Interface for listening to render window updates. */
|
||||||
|
public interface Listener {
|
||||||
|
void onRenderWindowOpen();
|
||||||
|
|
||||||
|
void onRenderWindowClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String TAG = "RenderSynchronizer";
|
||||||
|
private static final float DEFAULT_TARGET_FPS = 30f;
|
||||||
|
private final Object lock = new Object();
|
||||||
|
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
|
||||||
|
private final long targetFrameIntervalNanos;
|
||||||
|
private final Handler mainThreadHandler;
|
||||||
|
private Choreographer choreographer;
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private boolean isListening;
|
||||||
|
|
||||||
|
private boolean renderWindowOpen;
|
||||||
|
private long lastRefreshTimeNanos;
|
||||||
|
private long lastOpenedTimeNanos;
|
||||||
|
|
||||||
|
public RenderSynchronizer(float targetFrameRateFps) {
|
||||||
|
this.targetFrameIntervalNanos = Math.round(TimeUnit.SECONDS.toNanos(1) / targetFrameRateFps);
|
||||||
|
this.mainThreadHandler = new Handler(Looper.getMainLooper());
|
||||||
|
mainThreadHandler.post(() -> this.choreographer = Choreographer.getInstance());
|
||||||
|
Logging.d(TAG, "Created");
|
||||||
|
}
|
||||||
|
|
||||||
|
public RenderSynchronizer() {
|
||||||
|
this(DEFAULT_TARGET_FPS);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void registerListener(Listener listener) {
|
||||||
|
listeners.add(listener);
|
||||||
|
|
||||||
|
synchronized (lock) {
|
||||||
|
if (!isListening) {
|
||||||
|
Logging.d(TAG, "First listener, subscribing to frame callbacks");
|
||||||
|
isListening = true;
|
||||||
|
mainThreadHandler.post(
|
||||||
|
() -> choreographer.postFrameCallback(this::onDisplayRefreshCycleBegin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeListener(Listener listener) {
|
||||||
|
listeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onDisplayRefreshCycleBegin(long refreshTimeNanos) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (listeners.isEmpty()) {
|
||||||
|
Logging.d(TAG, "No listeners, unsubscribing to frame callbacks");
|
||||||
|
isListening = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
choreographer.postFrameCallback(this::onDisplayRefreshCycleBegin);
|
||||||
|
|
||||||
|
long lastOpenDeltaNanos = refreshTimeNanos - lastOpenedTimeNanos;
|
||||||
|
long refreshDeltaNanos = refreshTimeNanos - lastRefreshTimeNanos;
|
||||||
|
lastRefreshTimeNanos = refreshTimeNanos;
|
||||||
|
|
||||||
|
// Make a greedy choice whether to open (or keep open) the render window. If the current time
|
||||||
|
// since the render window was last opened is closer to the target than what we predict it would
|
||||||
|
// be in the next refresh cycle then we open the window.
|
||||||
|
if (Math.abs(lastOpenDeltaNanos - targetFrameIntervalNanos)
|
||||||
|
< Math.abs(lastOpenDeltaNanos - targetFrameIntervalNanos + refreshDeltaNanos)) {
|
||||||
|
lastOpenedTimeNanos = refreshTimeNanos;
|
||||||
|
openRenderWindow();
|
||||||
|
} else if (renderWindowOpen) {
|
||||||
|
closeRenderWindow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openRenderWindow() {
|
||||||
|
renderWindowOpen = true;
|
||||||
|
for (Listener listener : listeners) {
|
||||||
|
listener.onRenderWindowOpen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void closeRenderWindow() {
|
||||||
|
renderWindowOpen = false;
|
||||||
|
for (Listener listener : listeners) {
|
||||||
|
listener.onRenderWindowClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user