diff --git a/.github/AUTOROLL b/.github/AUTOROLL index 8e9439e0e5d2..49c9e21e9eb7 100644 --- a/.github/AUTOROLL +++ b/.github/AUTOROLL @@ -1 +1 @@ -5f4930b006c0b13e4610d565da281c05a08a42cf +00d5d7bd741b67377b635a199fb366a14cdce7c3 diff --git a/cobalt/android/apk/app/src/main/java/dev/cobalt/coat/BaseStarboardBridge.java b/cobalt/android/apk/app/src/main/java/dev/cobalt/coat/BaseStarboardBridge.java index a67df1e2ee29..03193f39fd12 100644 --- a/cobalt/android/apk/app/src/main/java/dev/cobalt/coat/BaseStarboardBridge.java +++ b/cobalt/android/apk/app/src/main/java/dev/cobalt/coat/BaseStarboardBridge.java @@ -208,9 +208,10 @@ protected void onActivityDestroy(Activity activity) { if (mApplicationStopped) { // We can't restart the starboard app, so kill the process for a clean start next time. Log.i(TAG, "Activity destroyed after shutdown; killing app."); + BaseStarboardBridgeJni.get().closeNativeStarboard(mNativeApp); mTtsHelper.shutdown(); mAdvertisingId.shutdown(); - BaseStarboardBridgeJni.get().closeNativeStarboard(mNativeApp); + System.exit(0); } else { Log.i(TAG, "Activity destroyed without shutdown; app suspended in background."); } diff --git a/cobalt/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java b/cobalt/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java index 5155d1823ae8..d7527a9336b0 100644 --- a/cobalt/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java +++ b/cobalt/android/apk/app/src/main/java/dev/cobalt/media/MediaCodecBridge.java @@ -41,8 +41,8 @@ import dev.cobalt.util.SynchronizedHolder; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.Locale; import java.util.concurrent.atomic.AtomicInteger; +import java.util.Locale; import org.jni_zero.CalledByNative; import org.jni_zero.JNINamespace; import org.jni_zero.NativeMethods; @@ -88,17 +88,14 @@ private boolean shouldSkipVideoFrame(long presentationTimeUs, boolean isDecodeOn return true; } // |mOperatingRate| won't be 0, but to be cautious we still add a check here. - if (!mSkipVideoFramesOver60Fps - || mOperatingRate <= kMaxAcceptedOperatingRate - || mOperatingRate == 0) { + if (!mSkipVideoFramesOver60Fps || mOperatingRate <= kMaxAcceptedOperatingRate || mOperatingRate == 0) { return false; } // Deterministically downsample to 60fps by picking one frame per 1/60s interval. // Some visual jitter may occur briefly when the playback rate changes. double frameIntervalUs = 1_000_000.0 / mOperatingRate; if (Math.floor(presentationTimeUs * kMaxAcceptedOperatingRate / 1_000_000.0) - == Math.floor( - (presentationTimeUs - frameIntervalUs) * kMaxAcceptedOperatingRate / 1_000_000.0)) { + == Math.floor((presentationTimeUs - frameIntervalUs) * kMaxAcceptedOperatingRate / 1_000_000.0)) { return true; } return false; @@ -127,17 +124,17 @@ int sizeOfActiveOutputBuffers() { return mActiveOutputBuffers.get(); } - /** Wraps a {@link MediaFormat} object to expose its properties to native code */ - // Copied from Chromium's MediaCodecBridge.java - // https://source.chromium.org/chromium/chromium/src/+/main:media/base/android/java/src/org/chromium/media/MediaCodecBridge.java;l=294-350;drc=6ac17d9d1b844a695209e865137466925fa1214f - // Here are changes made. - // - Exposes formatHasCropValues() to the native layer, which needs to call it. - // - Removes the methods that Cobalt do not use (e.g. colorStandrd). - // - Add @Nonnul annotation to mFormat. since it is not null. - // - Add safety checks for width() and height() to prevent a crash. Cobalt's native code - // accesses the format immediately in the onOutputFormatChanged callback, which can be - // before the dimension keys are available. - private static class MediaFormatWrapper { + /** Wraps a {@link MediaFormat} object to expose its properties to native code */ + // Copied from Chromium's MediaCodecBridge.java + // https://source.chromium.org/chromium/chromium/src/+/main:media/base/android/java/src/org/chromium/media/MediaCodecBridge.java;l=294-350;drc=6ac17d9d1b844a695209e865137466925fa1214f + // Here are changes made. + // - Exposes formatHasCropValues() to the native layer, which needs to call it. + // - Removes the methods that Cobalt do not use (e.g. colorStandrd). + // - Add @Nonnul annotation to mFormat. since it is not null. + // - Add safety checks for width() and height() to prevent a crash. Cobalt's native code + // accesses the format immediately in the onOutputFormatChanged callback, which can be + // before the dimension keys are available. + private static class MediaFormatWrapper { @NonNull private final MediaFormat mFormat; private MediaFormatWrapper(MediaFormat format) { @@ -147,9 +144,9 @@ private MediaFormatWrapper(MediaFormat format) { @CalledByNative("MediaFormatWrapper") private boolean formatHasCropValues() { return mFormat.containsKey(KEY_CROP_RIGHT) - && mFormat.containsKey(KEY_CROP_LEFT) - && mFormat.containsKey(KEY_CROP_BOTTOM) - && mFormat.containsKey(KEY_CROP_TOP); + && mFormat.containsKey(KEY_CROP_LEFT) + && mFormat.containsKey(KEY_CROP_BOTTOM) + && mFormat.containsKey(KEY_CROP_TOP); } @CalledByNative("MediaFormatWrapper") @@ -720,12 +717,11 @@ private int flush() { mFrameRateEstimator.reset(); } if (mEnableIgnoreCallbacksDuringFlushing) { - mMainHandler.post( - () -> { - synchronized (mNativeBridgeLock) { - mIsFlushing = false; - } - }); + mMainHandler.post(() -> { + synchronized (mNativeBridgeLock) { + mIsFlushing = false; + } + }); } } return MediaCodecStatus.OK; @@ -774,8 +770,8 @@ private MediaFormatWrapper getOutputFormat() { MediaFormat format = null; try { format = mMediaCodec.get().getOutputFormat(); - // Catches `RuntimeException` to handle any undocumented exceptions. - // See http://b/445694177#comment4 for details. + // Catches `RuntimeException` to handle any undocumented exceptions. + // See http://b/445694177#comment4 for details. } catch (RuntimeException e) { Log.e(TAG, "Failed to get output format", e); return null; @@ -785,7 +781,7 @@ private MediaFormatWrapper getOutputFormat() { // If the format is null, we crash the app for dev builds to enforce the API contract. // On release builds, we log the error and return null. if (format == null) { - assert (false); + assert(false); Log.e(TAG, "MediaCodec.getOutputFormat() returned null"); return null; } @@ -1091,9 +1087,7 @@ public void onFirstTunnelFrameReady(MediaCodec codec) { } }; if (mEnableIgnoreCallbacksDuringFlushing) { - mMediaCodec - .get() - .setOnFirstTunnelFrameReadyListener(mMainHandler, mFirstTunnelFrameReadyListener); + mMediaCodec.get().setOnFirstTunnelFrameReadyListener(mMainHandler, mFirstTunnelFrameReadyListener); } else { mMediaCodec.get().setOnFirstTunnelFrameReadyListener(null, mFirstTunnelFrameReadyListener); } diff --git a/cobalt/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java b/cobalt/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java index 46eea5d734e8..224abd963595 100644 --- a/cobalt/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java +++ b/cobalt/android/apk/app/src/main/java/dev/cobalt/media/MediaDrmBridge.java @@ -125,14 +125,11 @@ public static OperationResult operationFailed(String errorMessage) { } public static OperationResult operationFailed(String errorMessage, Throwable e) { - return operationFailed( - String.format("%s StackTrace: %s", errorMessage, Log.getStackTraceString(e))); + return operationFailed(String.format("%s StackTrace: %s", errorMessage, Log.getStackTraceString(e))); } public static OperationResult notProvisioned(Throwable e) { - return new OperationResult( - DrmOperationStatus.NOT_PROVISIONED, - String.format("Device is not provisioned. StackTrace: %s", Log.getStackTraceString(e))); + return new OperationResult(DrmOperationStatus.NOT_PROVISIONED, String.format("Device is not provisioned. StackTrace: %s", Log.getStackTraceString(e))); } @CalledByNative("OperationResult") @@ -156,8 +153,7 @@ public boolean isSuccess() { * @param nativeMediaDrmBridge The native owner of this class. */ @CalledByNative - static MediaDrmBridge create( - String keySystem, boolean enableAppProvisioning, long nativeMediaDrmBridge) { + static MediaDrmBridge create(String keySystem, boolean enableAppProvisioning, long nativeMediaDrmBridge) { UUID cryptoScheme = WIDEVINE_UUID; if (!MediaDrm.isCryptoSchemeSupported(cryptoScheme)) { return null; @@ -165,8 +161,7 @@ static MediaDrmBridge create( MediaDrmBridge mediaDrmBridge = null; try { - mediaDrmBridge = - new MediaDrmBridge(keySystem, cryptoScheme, enableAppProvisioning, nativeMediaDrmBridge); + mediaDrmBridge = new MediaDrmBridge(keySystem, cryptoScheme, enableAppProvisioning, nativeMediaDrmBridge); Log.d(TAG, "MediaDrmBridge successfully created."); } catch (UnsupportedSchemeException e) { Log.e(TAG, "Unsupported DRM scheme", e); @@ -273,8 +268,7 @@ OperationResult createSessionWithAppProvisioning(int ticket, byte[] initData, St assert mEnableAppProvisioning; if (mMediaDrm == null) { Log.e(TAG, "createSessionWithAppProvisioning() called when MediaDrm is null."); - return OperationResult.operationFailed( - "createSessionWithAppProvisioning() called when MediaDrm is null."); + return OperationResult.operationFailed("createSessionWithAppProvisioning() called when MediaDrm is null."); } OperationResult result = createMediaCryptoSessionWithAppProvisioning(); @@ -333,12 +327,14 @@ OperationResult updateSession(int ticket, byte[] sessionId, byte[] response) { Log.d(TAG, "updateSession()"); if (mMediaDrm == null) { Log.e(TAG, "updateSession() called when MediaDrm is null."); - return OperationResult.operationFailed("Null MediaDrm object when calling updateSession()."); + return OperationResult.operationFailed( + "Null MediaDrm object when calling updateSession()."); } if (!sessionExists(sessionId)) { Log.e(TAG, "updateSession tried to update a session that does not exist."); - return OperationResult.operationFailed("Failed to update session because it does not exist."); + return OperationResult.operationFailed( + "Failed to update session because it does not exist."); } try { @@ -429,8 +425,7 @@ MediaCrypto getMediaCrypto() { return mMediaCrypto; } - private MediaDrmBridge( - String keySystem, UUID schemeUUID, boolean enableAppProvisioning, long nativeMediaDrmBridge) + private MediaDrmBridge(String keySystem, UUID schemeUUID, boolean enableAppProvisioning, long nativeMediaDrmBridge) throws android.media.UnsupportedSchemeException { mSchemeUUID = schemeUUID; mMediaDrm = new MediaDrm(schemeUUID); @@ -518,15 +513,12 @@ public void onKeyStatusChange( byte[] sessionId, List keyInformation, boolean hasNewUsableKey) { - MediaDrmBridgeJni.get() - .onKeyStatusChange( - mNativeMediaDrmBridge, - sessionId, - keyInformation.stream() - .map( - keyStatus -> - new KeyStatus(keyStatus.getKeyId(), keyStatus.getStatusCode())) - .toArray(KeyStatus[]::new)); + MediaDrmBridgeJni.get().onKeyStatusChange( + mNativeMediaDrmBridge, + sessionId, + keyInformation.stream() + .map(keyStatus -> new KeyStatus(keyStatus.getKeyId(), keyStatus.getStatusCode())) + .toArray(KeyStatus[]::new)); } }, null); @@ -568,10 +560,7 @@ private void handleKeyRequiredEventWithAppProvisioning(byte[] sessionId, byte[] } ByteBuffer sessionIdByteBuffer = ByteBuffer.wrap(sessionId); if (!mSessionIds.containsKey(sessionIdByteBuffer)) { - Log.e( - TAG, - "HandleKeyRequiredEventWithAppProvisioning failed: invalid session id=" - + bytesToString(sessionId)); + Log.e(TAG, "HandleKeyRequiredEventWithAppProvisioning failed: invalid session id=" + bytesToString(sessionId)); return; } @@ -599,8 +588,8 @@ private void onSessionMessage( int requestType = request.getRequestType(); - MediaDrmBridgeJni.get() - .onSessionMessage(mNativeMediaDrmBridge, ticket, sessionId, requestType, request.getData()); + MediaDrmBridgeJni.get().onSessionMessage( + mNativeMediaDrmBridge, ticket, sessionId, requestType, request.getData()); } /** @@ -692,6 +681,8 @@ private byte[] openSession() throws android.media.NotProvisionedException { // Throw NotProvisionedException so that we can attemptProvisioning(). throw e; } catch (MediaDrmException e) { + // Other MediaDrmExceptions (e.g. ResourceBusyException) are not + // recoverable. Log.e(TAG, "Cannot open a new session", e); release(); return null; @@ -968,7 +959,11 @@ private int getStatusCode() { @NativeMethods interface Natives { void onSessionMessage( - long nativeMediaDrmBridge, int ticket, byte[] sessionId, int requestType, byte[] message); + long nativeMediaDrmBridge, + int ticket, + byte[] sessionId, + int requestType, + byte[] message); void onKeyStatusChange( long nativeMediaDrmBridge, diff --git a/cobalt/android/apk/app/src/main/java/dev/cobalt/util/JavaSwitches.java b/cobalt/android/apk/app/src/main/java/dev/cobalt/util/JavaSwitches.java index 2b3e36611eee..5f1eef52aefa 100644 --- a/cobalt/android/apk/app/src/main/java/dev/cobalt/util/JavaSwitches.java +++ b/cobalt/android/apk/app/src/main/java/dev/cobalt/util/JavaSwitches.java @@ -19,7 +19,9 @@ import java.util.Map; import java.util.StringJoiner; -/** Defines the constant names for feature switches used in Kimono. */ +/** + * Defines the constant names for feature switches used in Kimono. + */ public class JavaSwitches { public static final String ENABLE_QUIC = "EnableQUIC"; public static final String DISABLE_STARTUP_GUARD = "DisableStartupGuard"; @@ -53,15 +55,13 @@ public class JavaSwitches { public static final String RECLAIM_DELAY_IN_SECONDS = "ReclaimDelayInSeconds"; /** flag to disable GPU memory buffer compositor resources. */ - public static final String DISABLE_GPU_MEMORY_BUFFER_COMPOSITOR_RESOURCES = - "DisableGpuMemoryBufferCompositorResources"; + public static final String DISABLE_GPU_MEMORY_BUFFER_COMPOSITOR_RESOURCES = "DisableGpuMemoryBufferCompositorResources"; /** flag to limit GPU image cache items */ public static final String GPU_IMAGE_CACHE_LIMIT_ITEMS = "GpuImageCacheLimitItems"; /** flag to limit GPU image cache working set budget bytes */ - public static final String DECODED_IMAGE_WORKING_SET_BUDGET_BYTES = - "DecodedImageWorkingSetBudgetBytes"; + public static final String DECODED_IMAGE_WORKING_SET_BUDGET_BYTES = "DecodedImageWorkingSetBudgetBytes"; /** flag to allow scaling clipped images in GpuImageDecodeCache */ public static final String ENABLE_SCALING_CLIPPED_IMAGES = "EnableScalingClippedImages"; @@ -73,15 +73,14 @@ public class JavaSwitches { public static final String REDUCE_ANDROID_THREAD_STACK_SIZE = "ReduceAndroidThreadStackSize"; /** flag to enable dynamic mojo pipe sizing. */ - public static final String ENABLE_COBALT_DYNAMIC_MOJO_PIPE_SIZING = - "EnableCobaltDynamicMojoPipeSizing"; + public static final String ENABLE_COBALT_DYNAMIC_MOJO_PIPE_SIZING = "EnableCobaltDynamicMojoPipeSizing"; /** flag to tune cobalt dynamic mojo pipe sizing subresource size in bytes. */ - public static final String COBALT_DYNAMIC_MOJO_PIPE_SUBRESOURCE_SIZE = - "CobaltDynamicMojoPipeSubresourceSize"; + public static final String COBALT_DYNAMIC_MOJO_PIPE_SUBRESOURCE_SIZE = "CobaltDynamicMojoPipeSubresourceSize"; /** flag to tune cobalt dynamic mojo pipe sizing media size in bytes. */ - public static final String COBALT_DYNAMIC_MOJO_PIPE_MEDIA_SIZE = "CobaltDynamicMojoPipeMediaSize"; + public static final String COBALT_DYNAMIC_MOJO_PIPE_MEDIA_SIZE = + "CobaltDynamicMojoPipeMediaSize"; /** flag to disable FontSrcLocalMatching lookup table. */ public static final String DISABLE_FONT_SRC_LOCAL_MATCHING = "DisableFontSrcLocalMatching"; @@ -93,25 +92,19 @@ public class JavaSwitches { public static final String COBALT_BYPASS_BUFFERING_BYTES_CONSUMER = "CobaltBypassBufferingBytesConsumer"; - /** flag to wait for media resources during shutdown. */ - public static final String WAIT_FOR_MEDIA_RESOURCES_ON_SHUTDOWN = - "WaitForMediaResourcesOnShutdown"; - /** flag to aggressively flush v8 bytecode after a configurable old time. */ public static final String V8_SET_BYTECODE_OLD_TIME = "V8SetBytecodeOldTime"; public static List getExtraCommandLineArgs(Map javaSwitches) { List extraCommandLineArgs = new ArrayList<>(); StringJoiner jsFlags = new StringJoiner(";"); - StringJoiner enabledFeatures = new StringJoiner(","); - StringJoiner disabledFeatures = new StringJoiner(","); if (javaSwitches.containsKey(JavaSwitches.USE_IPV4_FOR_DNS)) { - enabledFeatures.add("UseIPv4ForDNS"); + extraCommandLineArgs.add("--enable-features=UseIPv4ForDNS"); } if (javaSwitches.containsKey(JavaSwitches.LOCAL_STORAGE_DELETE_LOCK_FILE)) { - enabledFeatures.add("LocalStorageDeleteLockFile"); + extraCommandLineArgs.add("--enable-features=LocalStorageDeleteLockFile"); } if (!javaSwitches.containsKey(JavaSwitches.ENABLE_QUIC)) { @@ -137,15 +130,11 @@ public static List getExtraCommandLineArgs(Map javaSwitc } if (javaSwitches.containsKey(JavaSwitches.GPU_IMAGE_CACHE_LIMIT_ITEMS)) { - String limit = - javaSwitches.get(JavaSwitches.GPU_IMAGE_CACHE_LIMIT_ITEMS).replaceAll("[^0-9]", ""); + String limit = javaSwitches.get(JavaSwitches.GPU_IMAGE_CACHE_LIMIT_ITEMS).replaceAll("[^0-9]", ""); extraCommandLineArgs.add("--cc-image-cache-limit-items=" + limit); } if (javaSwitches.containsKey(JavaSwitches.DECODED_IMAGE_WORKING_SET_BUDGET_BYTES)) { - String budget = - javaSwitches - .get(JavaSwitches.DECODED_IMAGE_WORKING_SET_BUDGET_BYTES) - .replaceAll("[^0-9]", ""); + String budget = javaSwitches.get(JavaSwitches.DECODED_IMAGE_WORKING_SET_BUDGET_BYTES).replaceAll("[^0-9]", ""); extraCommandLineArgs.add("--decoded-image-working-set-budget-bytes=" + budget); } @@ -154,8 +143,7 @@ public static List getExtraCommandLineArgs(Map javaSwitc } StringJoiner mojoPipeParams = new StringJoiner("/"); - String subresourceSize = - javaSwitches.get(JavaSwitches.COBALT_DYNAMIC_MOJO_PIPE_SUBRESOURCE_SIZE); + String subresourceSize = javaSwitches.get(JavaSwitches.COBALT_DYNAMIC_MOJO_PIPE_SUBRESOURCE_SIZE); if (subresourceSize != null) { String size = subresourceSize.replaceAll("[^0-9]", ""); if (!size.isEmpty()) { @@ -171,34 +159,32 @@ public static List getExtraCommandLineArgs(Map javaSwitc } } - if (javaSwitches.containsKey(JavaSwitches.ENABLE_COBALT_DYNAMIC_MOJO_PIPE_SIZING) - || mojoPipeParams.length() > 0) { + if (javaSwitches.containsKey(JavaSwitches.ENABLE_COBALT_DYNAMIC_MOJO_PIPE_SIZING) || mojoPipeParams.length() > 0) { if (mojoPipeParams.length() > 0) { - enabledFeatures.add("CobaltDynamicMojoPipeSizing:" + mojoPipeParams.toString()); + extraCommandLineArgs.add("--enable-features=CobaltDynamicMojoPipeSizing:" + mojoPipeParams.toString()); } else { - enabledFeatures.add("CobaltDynamicMojoPipeSizing"); + extraCommandLineArgs.add("--enable-features=CobaltDynamicMojoPipeSizing"); } } StringJoiner featureParams = new StringJoiner("/"); if (javaSwitches.containsKey(JavaSwitches.INTEREST_AREA_SIZE_IN_PIXELS)) { - String size = - javaSwitches.get(JavaSwitches.INTEREST_AREA_SIZE_IN_PIXELS).replaceAll("[^0-9]", ""); + String size = javaSwitches.get(JavaSwitches.INTEREST_AREA_SIZE_IN_PIXELS).replaceAll("[^0-9]", ""); featureParams.add("size_in_pixels/" + size); } if (javaSwitches.containsKey(JavaSwitches.RECLAIM_DELAY_IN_SECONDS)) { - String delay = - javaSwitches.get(JavaSwitches.RECLAIM_DELAY_IN_SECONDS).replaceAll("[^0-9]", ""); + String delay = javaSwitches.get(JavaSwitches.RECLAIM_DELAY_IN_SECONDS).replaceAll("[^0-9]", ""); featureParams.add("reclaim_delay_s/" + delay); } if (featureParams.length() > 0) { - enabledFeatures.add("SmallerInterestArea:" + featureParams.toString()); + extraCommandLineArgs.add( + "--enable-features=SmallerInterestArea:" + featureParams.toString()); } if (javaSwitches.containsKey(JavaSwitches.DISABLE_FONT_SRC_LOCAL_MATCHING)) { - disabledFeatures.add("FontSrcLocalMatching"); + extraCommandLineArgs.add("--disable-features=FontSrcLocalMatching"); } if (javaSwitches.containsKey(JavaSwitches.ENABLE_OPTIMIZED_FONT_LOADING)) { @@ -214,11 +200,11 @@ public static List getExtraCommandLineArgs(Map javaSwitc } if (javaSwitches.containsKey(JavaSwitches.REDUCE_STARBOARD_THREAD_STACK_SIZE)) { - enabledFeatures.add(JavaSwitches.REDUCE_STARBOARD_THREAD_STACK_SIZE); + extraCommandLineArgs.add("--enable-features=" + JavaSwitches.REDUCE_STARBOARD_THREAD_STACK_SIZE); } if (javaSwitches.containsKey(JavaSwitches.REDUCE_ANDROID_THREAD_STACK_SIZE)) { - enabledFeatures.add(JavaSwitches.REDUCE_ANDROID_THREAD_STACK_SIZE); + extraCommandLineArgs.add("--enable-features=" + JavaSwitches.REDUCE_ANDROID_THREAD_STACK_SIZE); } if (javaSwitches.containsKey(JavaSwitches.AVOID_CC_REUSE_RESOURCE)) { @@ -226,19 +212,8 @@ public static List getExtraCommandLineArgs(Map javaSwitc } if (javaSwitches.containsKey(JavaSwitches.COBALT_BYPASS_BUFFERING_BYTES_CONSUMER)) { - enabledFeatures.add(JavaSwitches.COBALT_BYPASS_BUFFERING_BYTES_CONSUMER); - } - - if (javaSwitches.containsKey(JavaSwitches.WAIT_FOR_MEDIA_RESOURCES_ON_SHUTDOWN)) { - enabledFeatures.add(JavaSwitches.WAIT_FOR_MEDIA_RESOURCES_ON_SHUTDOWN); - } - - if (enabledFeatures.length() > 0) { - extraCommandLineArgs.add("--enable-features=" + enabledFeatures.toString()); - } - - if (disabledFeatures.length() > 0) { - extraCommandLineArgs.add("--disable-features=" + disabledFeatures.toString()); + extraCommandLineArgs.add( + "--enable-features=" + JavaSwitches.COBALT_BYPASS_BUFFERING_BYTES_CONSUMER); } return extraCommandLineArgs; diff --git a/cobalt/shell/android/java/src/dev/cobalt/shell/ShellManager.java b/cobalt/shell/android/java/src/dev/cobalt/shell/ShellManager.java index 21eeb017b43f..c8b6cd198047 100644 --- a/cobalt/shell/android/java/src/dev/cobalt/shell/ShellManager.java +++ b/cobalt/shell/android/java/src/dev/cobalt/shell/ShellManager.java @@ -15,6 +15,7 @@ package dev.cobalt.shell; import android.content.Context; +import dev.cobalt.shell.ContentViewRenderView; import org.chromium.base.ThreadUtils; import org.chromium.build.annotations.Initializer; import org.chromium.build.annotations.NullMarked; @@ -27,188 +28,189 @@ import org.jni_zero.JNINamespace; import org.jni_zero.NativeMethods; -/** Copied from org.chromium.content_shell.ShellManager. Container and generator of ShellViews. */ +/** + * Copied from org.chromium.content_shell.ShellManager. + * Container and generator of ShellViews. + */ @JNINamespace("content") @NullMarked public class ShellManager { - private static final String TAG = "cobalt"; - private WindowAndroid mWindow; - private @Nullable Shell mActiveShell; + private static final String TAG = "cobalt"; + private WindowAndroid mWindow; + private @Nullable Shell mActiveShell; - private Shell.@Nullable OnWebContentsReadyListener mNextWebContentsReadyListener; + private Shell.@Nullable OnWebContentsReadyListener mNextWebContentsReadyListener; - // The target for all content rendering. - private @Nullable ContentViewRenderView mContentViewRenderView; + // The target for all content rendering. + private @Nullable ContentViewRenderView mContentViewRenderView; - private Context mContext; + private Context mContext; - private boolean mIsActivityVisible; + private boolean mIsActivityVisible; - /** Constructor for inflating via XML. */ - public ShellManager(final Context context) { - mContext = context; - if (sNatives == null) { - sNatives = ShellManagerJni.get(); + /** + * Constructor for inflating via XML. + */ + public ShellManager(final Context context) { + mContext = context; + if (sNatives == null) { + sNatives = ShellManagerJni.get(); + } + sNatives.init(this); } - sNatives.init(this); - } - public void onActivityVisible(boolean visible) { - mIsActivityVisible = visible; - if (mActiveShell != null) { - mActiveShell.onActivityVisible(visible); + public void onActivityVisible(boolean visible) { + mIsActivityVisible = visible; + if (mActiveShell != null) { + mActiveShell.onActivityVisible(visible); + } } - } - - public Context getContext() { - return mContext; - } - - /** - * @param window The window used to generate all shells. - */ - @Initializer - public void setWindow(WindowAndroid window) { - assert window != null; - mWindow = window; - mContentViewRenderView = new ContentViewRenderView(getContext()); - mContentViewRenderView.onNativeLibraryLoaded(window); - } - - /** - * @return The window used to generate all shells. - */ - public WindowAndroid getWindow() { - return mWindow; - } - - /** Get the ContentViewRenderView. */ - public @Nullable ContentViewRenderView getContentViewRenderView() { - return mContentViewRenderView; - } - - /** - * @return The currently visible shell view or null if one is not showing. - */ - public @Nullable Shell getActiveShell() { - return mActiveShell; - } - - /** - * Creates a new shell pointing to the specified URL. - * - * @param url The URL the shell should load upon creation. - */ - public void launchShell(String url) { - // Calls the overloaded method with a null listener. - launchShell(url, /* deepLinkUrl= */ "", /* listener= */ null); - } - - /** - * Creates a new shell pointing to the specified URL. - * - * @param url The URL the shell should load upon creation. - * @param deepLinkUrl The URL from the DeepLink URL. - * @param listener The listener to be notified when WebContents is ready. - */ - public void launchShell( - String url, String deepLinkUrl, Shell.OnWebContentsReadyListener listener) { - ThreadUtils.assertOnUiThread(); - mNextWebContentsReadyListener = listener; - Shell previousShell = mActiveShell; - sNatives.launchShell(url, deepLinkUrl); - if (previousShell != null) previousShell.close(); - } - - @CalledByNative - private Object createShell(long nativeShellPtr) { - if (mContentViewRenderView == null) { - mContentViewRenderView = new ContentViewRenderView(getContext()); - mContentViewRenderView.onNativeLibraryLoaded(mWindow); + + public Context getContext() { + return mContext; } - Shell shellView = new Shell(getContext()); - shellView.initialize(nativeShellPtr, mWindow); - shellView.onActivityVisible(mIsActivityVisible); - shellView.setWebContentsReadyListener(mNextWebContentsReadyListener); - mNextWebContentsReadyListener = null; + /** + * @param window The window used to generate all shells. + */ + @Initializer + public void setWindow(WindowAndroid window) { + assert window != null; + mWindow = window; + mContentViewRenderView = new ContentViewRenderView(getContext()); + mContentViewRenderView.onNativeLibraryLoaded(window); + } - // TODO(tedchoc): Allow switching back to these inactive shells. - if (mActiveShell != null) removeShell(mActiveShell); + /** + * @return The window used to generate all shells. + */ + public WindowAndroid getWindow() { + return mWindow; + } - showShell(shellView); - return shellView; - } + /** + * Get the ContentViewRenderView. + */ + public @Nullable ContentViewRenderView getContentViewRenderView() { + return mContentViewRenderView; + } - @RequiresNonNull("mContentViewRenderView") - private void showShell(Shell shellView) { - if (mActiveShell != null) { - mActiveShell.setContentViewRenderView(null); + /** + * @return The currently visible shell view or null if one is not showing. + */ + public @Nullable Shell getActiveShell() { + return mActiveShell; } - shellView.setContentViewRenderView(mContentViewRenderView); - mActiveShell = shellView; - WebContents webContents = mActiveShell.getWebContents(); - if (webContents != null) { - mContentViewRenderView.setCurrentWebContents(webContents); - if (mIsActivityVisible) { - webContents.updateWebContentsVisibility(Visibility.VISIBLE); - } + + /** + * Creates a new shell pointing to the specified URL. + * @param url The URL the shell should load upon creation. + */ + public void launchShell(String url) { + // Calls the overloaded method with a null listener. + launchShell(url, /* deepLinkUrl= */ "", /* listener= */ null); } - } - - @CalledByNative - private void removeShell(Shell shellView) { - if (shellView == mActiveShell) mActiveShell = null; - shellView.setContentViewRenderView(null); - } - - /** - * Destroys the Shell manager and associated components. Always called at activity exit, and - * potentially called by native in cases where we need to control the timing of - * mContentViewRenderView destruction. Must handle being called twice. - */ - @CalledByNative - public void destroy() { - // Remove active shell (Currently single shell support only available). - if (mActiveShell != null) { - removeShell(mActiveShell); + + /** + * Creates a new shell pointing to the specified URL. + * @param url The URL the shell should load upon creation. + * @param deepLinkUrl The URL from the DeepLink URL. + * @param listener The listener to be notified when WebContents is ready. + */ + public void launchShell(String url, String deepLinkUrl, Shell.OnWebContentsReadyListener listener) { + ThreadUtils.assertOnUiThread(); + mNextWebContentsReadyListener = listener; + Shell previousShell = mActiveShell; + sNatives.launchShell(url, deepLinkUrl); + if (previousShell != null) previousShell.close(); } - if (mContentViewRenderView != null) { - mContentViewRenderView.destroy(); - mContentViewRenderView = null; + + @CalledByNative + private Object createShell(long nativeShellPtr) { + if (mContentViewRenderView == null) { + mContentViewRenderView = new ContentViewRenderView(getContext()); + mContentViewRenderView.onNativeLibraryLoaded(mWindow); + } + + Shell shellView = new Shell(getContext()); + shellView.initialize(nativeShellPtr, mWindow); + shellView.onActivityVisible(mIsActivityVisible); + shellView.setWebContentsReadyListener(mNextWebContentsReadyListener); + mNextWebContentsReadyListener = null; + + // TODO(tedchoc): Allow switching back to these inactive shells. + if (mActiveShell != null) removeShell(mActiveShell); + + showShell(shellView); + return shellView; } - } - private static Natives sNatives; + @RequiresNonNull("mContentViewRenderView") + private void showShell(Shell shellView) { + if (mActiveShell != null) { + mActiveShell.setContentViewRenderView(null); + } + shellView.setContentViewRenderView(mContentViewRenderView); + mActiveShell = shellView; + WebContents webContents = mActiveShell.getWebContents(); + if (webContents != null) { + mContentViewRenderView.setCurrentWebContents(webContents); + if (mIsActivityVisible) { + webContents.updateWebContentsVisibility(Visibility.VISIBLE); + } + } + } - public static void setNativesForTesting(Natives natives) { - sNatives = natives; - } + @CalledByNative + private void removeShell(Shell shellView) { + if (shellView == mActiveShell) mActiveShell = null; + shellView.setContentViewRenderView(null); + } - /** Interface for the native implementation of ShellManager. */ - @NativeMethods - public interface Natives { /** - * Creates the native ShellManager object. - * - * @param shellManagerInstance The Java instance of the ShellManager. + * Destroys the Shell manager and associated components. + * Always called at activity exit, and potentially called by native in cases where we need to + * control the timing of mContentViewRenderView destruction. Must handle being called twice. */ - void init(Object shellManagerInstance); + @CalledByNative + public void destroy() { + // Remove active shell (Currently single shell support only available). + if (mActiveShell != null) { + removeShell(mActiveShell); + } + if (mContentViewRenderView != null) { + mContentViewRenderView.destroy(); + mContentViewRenderView = null; + } + } - /** - * Creates a new shell pointing to the specified URL. - * - * @param url The URL the shell should load upon creation. - * @param deepLinkUrl The topic URL from the DeepLink URL. - */ - void launchShell(String url, String deepLinkUrl); + private static Natives sNatives; + + public static void setNativesForTesting(Natives natives) { + sNatives = natives; + } /** - * Appends the migration status parameter to the given URL. - * - * @param url The URL to append the migration status to. - * @return The updated URL. + * Interface for the native implementation of ShellManager. */ - String appendMigrationStatus(String url); - } + @NativeMethods + public interface Natives { + /** + * Creates the native ShellManager object. + * @param shellManagerInstance The Java instance of the ShellManager. + */ + void init(Object shellManagerInstance); + /** + * Creates a new shell pointing to the specified URL. + * @param url The URL the shell should load upon creation. + * @param deepLinkUrl The topic URL from the DeepLink URL. + */ + void launchShell(String url, String deepLinkUrl); + /** + * Appends the migration status parameter to the given URL. + * @param url The URL to append the migration status to. + * @return The updated URL. + */ + String appendMigrationStatus(String url); + } } diff --git a/media/starboard/bidirectional_fit_decoder_buffer_allocator_strategy.h b/media/starboard/bidirectional_fit_decoder_buffer_allocator_strategy.h index 7678881bea44..9b45f38147ce 100644 --- a/media/starboard/bidirectional_fit_decoder_buffer_allocator_strategy.h +++ b/media/starboard/bidirectional_fit_decoder_buffer_allocator_strategy.h @@ -30,7 +30,8 @@ class BidirectionalFitDecoderBufferAllocatorStrategy public: BidirectionalFitDecoderBufferAllocatorStrategy(size_t initial_capacity, size_t allocation_increment) - : fallback_allocator_(/*enable_decommit_on_idle=*/false), + : fallback_allocator_(/*enable_decommit_on_idle=*/false, + /*enable_page_alignment=*/false), bidirectional_fit_allocator_(&fallback_allocator_, initial_capacity, kSmallAllocationThreshold, @@ -50,8 +51,10 @@ class BidirectionalFitDecoderBufferAllocatorStrategy bool enable_decommit_on_idle, size_t retain_blocks, size_t conservative_decommit_blocks, - bool aggressive_decommit_on_suspend = false) - : fallback_allocator_(enable_decommit_on_idle), + bool aggressive_decommit_on_suspend = false, + bool allocate_with_page_alignment = true) + : fallback_allocator_(enable_decommit_on_idle, + allocate_with_page_alignment), bidirectional_fit_allocator_(&fallback_allocator_, initial_capacity, kSmallAllocationThreshold, diff --git a/media/starboard/decoder_buffer_allocator.cc b/media/starboard/decoder_buffer_allocator.cc index 6ef18a033b02..2b7cb6c2848c 100644 --- a/media/starboard/decoder_buffer_allocator.cc +++ b/media/starboard/decoder_buffer_allocator.cc @@ -107,6 +107,9 @@ void DecoderBufferAllocator::ReleaseIdleMemory() { } void DecoderBufferAllocator::DecommitAllDecommitableBlocks() { + if (!decommit_on_suspend_enabled_.load(std::memory_order_relaxed)) { + return; + } base::AutoLock scoped_lock(mutex_); if (strategy_) { strategy_->DecommitAllDecommitableBlocks(); @@ -238,13 +241,19 @@ void DecoderBufferAllocator::EnableConfigurableDecommitStrategy( int block_size, int retain_blocks, int conservative_decommit_blocks, - bool aggressive_decommit_on_suspend) { + bool aggressive_decommit_on_suspend, + bool allocate_with_page_alignment) { auto* allocator = Get(); CHECK(allocator); + if (aggressive_decommit_on_suspend) { + // This optimization is enable only, and isn't expected to be disabled. + allocator->decommit_on_suspend_enabled_.store(true, + std::memory_order_relaxed); + } allocator->UpdateAllocatorStrategy(base::BindRepeating( [](int block_size, int retain_blocks, int conservative_decommit_blocks, - bool aggressive_decommit_on_suspend, int initial_capacity, - int allocation_unit) + bool aggressive_decommit_on_suspend, bool allocate_with_page_alignment, + int initial_capacity, int allocation_unit) -> std::unique_ptr { LOG(INFO) << "DecoderBufferAllocator is using " @@ -256,15 +265,17 @@ void DecoderBufferAllocator::EnableConfigurableDecommitStrategy( << "retain_blocks: " << retain_blocks << ", " << "conservative_decommit_blocks: " << conservative_decommit_blocks << ", aggressive_decommit_on_suspend: " - << ToString(aggressive_decommit_on_suspend); + << ToString(aggressive_decommit_on_suspend) + << ", allocate_with_page_alignment: " + << ToString(allocate_with_page_alignment); return std::make_unique( block_size, block_size, /*enable_decommit_on_idle=*/true, retain_blocks, conservative_decommit_blocks, - aggressive_decommit_on_suspend); + aggressive_decommit_on_suspend, allocate_with_page_alignment); }, block_size, retain_blocks, conservative_decommit_blocks, - aggressive_decommit_on_suspend)); + aggressive_decommit_on_suspend, allocate_with_page_alignment)); } // static diff --git a/media/starboard/decoder_buffer_allocator.h b/media/starboard/decoder_buffer_allocator.h index d28a7edb0ffa..1a3c2b3ef288 100644 --- a/media/starboard/decoder_buffer_allocator.h +++ b/media/starboard/decoder_buffer_allocator.h @@ -15,6 +15,7 @@ #ifndef MEDIA_STARBOARD_DECODER_BUFFER_ALLOCATOR_H_ #define MEDIA_STARBOARD_DECODER_BUFFER_ALLOCATOR_H_ +#include #include #include @@ -86,7 +87,8 @@ class DecoderBufferAllocator : public DecoderBuffer::Allocator, int block_size, int retain_blocks, int conservative_decommit_blocks, - bool aggressive_decommit_on_suspend); + bool aggressive_decommit_on_suspend, + bool allocate_with_page_alignment); static void EnableMediaBufferPoolStrategy(); static void EnableReleaseIdleMemory(); @@ -118,6 +120,7 @@ class DecoderBufferAllocator : public DecoderBuffer::Allocator, int pending_allocation_operations_count_ GUARDED_BY(mutex_) = 0; int allocation_operation_index_ GUARDED_BY(mutex_) = 0; #endif // !BUILDFLAG(COBALT_IS_RELEASE_BUILD) + std::atomic decommit_on_suspend_enabled_{false}; }; } // namespace media diff --git a/media/starboard/media_buffer_pool_decoder_buffer_allocator_strategy.cc b/media/starboard/media_buffer_pool_decoder_buffer_allocator_strategy.cc index 147bd43edf5b..350602a70b65 100644 --- a/media/starboard/media_buffer_pool_decoder_buffer_allocator_strategy.cc +++ b/media/starboard/media_buffer_pool_decoder_buffer_allocator_strategy.cc @@ -29,7 +29,8 @@ MediaBufferPoolDecoderBufferAllocatorStrategy:: : media_buffer_pool_(media_buffer_pool), video_buffer_initial_capacity_(video_buffer_initial_capacity), video_buffer_allocation_increment_(video_buffer_allocation_increment), - audio_fallback_allocator_(/*enable_decommit_on_idle=*/false), + audio_fallback_allocator_(/*enable_decommit_on_idle=*/false, + /*enable_page_alignment=*/false), audio_allocator_( &audio_fallback_allocator_, // Pre-allocate sufficient capacity for audio buffers to diff --git a/media/starboard/starboard_memory_allocator.h b/media/starboard/starboard_memory_allocator.h index 1442bb40e919..af29c10bc4e7 100644 --- a/media/starboard/starboard_memory_allocator.h +++ b/media/starboard/starboard_memory_allocator.h @@ -41,11 +41,14 @@ using starboard::AlignUp; // using posix_memalign() and free(). class StarboardMemoryAllocator : public starboard::Allocator { public: - explicit StarboardMemoryAllocator(bool enable_decommit) + StarboardMemoryAllocator(bool enable_decommit, bool enable_page_alignment) : enable_decommit_(enable_decommit), + enable_page_alignment_(enable_page_alignment), page_size_(static_cast(sysconf(_SC_PAGESIZE))) { LOG(INFO) << "StarboardMemoryAllocator: decommit is " - << (enable_decommit_ ? "enabled" : "disabled") << "."; + << (enable_decommit_ ? "enabled" : "disabled") + << ", page alignment is " + << (enable_page_alignment_ ? "enabled" : "disabled") << "."; } void* Allocate(size_t size) override { @@ -57,7 +60,7 @@ class StarboardMemoryAllocator : public starboard::Allocator { } void* AllocateForAlignment(size_t* size, size_t alignment) override { - if (enable_decommit_) { + if (enable_page_alignment_) { alignment = std::max(alignment, page_size_); *size = AlignUp(*size, page_size_); } @@ -69,23 +72,23 @@ class StarboardMemoryAllocator : public starboard::Allocator { void Decommit(void* memory, size_t size, bool conservative) override { #if !BUILDFLAG(COBALT_IS_RELEASE_BUILD) - uintptr_t start = reinterpret_cast(memory); CHECK(enable_decommit_); - CHECK_EQ(start % page_size_, 0U); - CHECK_EQ(size % page_size_, 0U); #endif // !BUILDFLAG(COBALT_IS_RELEASE_BUILD) - // Align down the size to prevent decommitting past the end just in case - // the user explicitly passes an altered, unaligned size block. - size_t aligned_size = AlignDown(size, page_size_); + uint8_t* start = static_cast(memory); + uint8_t* end = start + size; + uint8_t* aligned_start = AlignUp(start, page_size_); + uint8_t* aligned_end = AlignDown(end, page_size_); - if (aligned_size > 0) { + if (aligned_start < aligned_end) { + size_t aligned_size = aligned_end - aligned_start; // MADV_FREE is not supported on all kernel versions/configurations. If it // fails, fallback to MADV_DONTNEED to ensure memory is still decommitted. - if (conservative && madvise(memory, aligned_size, MADV_FREE) == 0) { + if (conservative && + madvise(aligned_start, aligned_size, MADV_FREE) == 0) { return; } - madvise(memory, aligned_size, MADV_DONTNEED); + madvise(aligned_start, aligned_size, MADV_DONTNEED); } } @@ -102,6 +105,7 @@ class StarboardMemoryAllocator : public starboard::Allocator { private: const bool enable_decommit_; + const bool enable_page_alignment_; const size_t page_size_; }; diff --git a/media/starboard/starboard_memory_allocator_test.cc b/media/starboard/starboard_memory_allocator_test.cc index 29ee21b9aea3..add5e6de4714 100644 --- a/media/starboard/starboard_memory_allocator_test.cc +++ b/media/starboard/starboard_memory_allocator_test.cc @@ -24,7 +24,7 @@ namespace { class StarboardMemoryAllocatorTest : public ::testing::TestWithParam { protected: - StarboardMemoryAllocatorTest() : allocator_(GetParam()) {} + StarboardMemoryAllocatorTest() : allocator_(GetParam(), GetParam()) {} StarboardMemoryAllocator allocator_; }; @@ -78,7 +78,8 @@ INSTANTIATE_TEST_SUITE_P(EnableDecommit, TEST(StarboardMemoryAllocatorDecommitSpecificTest, AllocateForAlignmentUpdatesSizeWhenDecommitEnabled) { - StarboardMemoryAllocator allocator(/*enable_decommit=*/true); + StarboardMemoryAllocator allocator(/*enable_decommit=*/true, + /*enable_page_alignment=*/true); const size_t page_size = sysconf(_SC_PAGESIZE); const size_t initial_size = page_size / 2; // A size smaller than page size @@ -95,7 +96,8 @@ TEST(StarboardMemoryAllocatorDecommitSpecificTest, TEST(StarboardMemoryAllocatorDecommitSpecificTest, AllocateForAlignmentDoesNotUpdateSizeWhenDecommitDisabled) { - StarboardMemoryAllocator allocator(/*enable_decommit=*/false); + StarboardMemoryAllocator allocator(/*enable_decommit=*/false, + /*enable_page_alignment=*/false); const size_t page_size = sysconf(_SC_PAGESIZE); const size_t initial_size = page_size / 2; diff --git a/starboard/android/shared/BUILD.gn b/starboard/android/shared/BUILD.gn index d47a0ef66939..985b38a75c6d 100644 --- a/starboard/android/shared/BUILD.gn +++ b/starboard/android/shared/BUILD.gn @@ -195,8 +195,6 @@ static_library("starboard_platform") { "media_is_buffer_pool_allocate_on_demand.cc", "media_is_key_system_supported.cc", "media_is_video_supported.cc", - "media_resource_tracker.cc", - "media_resource_tracker.h", "memfd_media_buffer_pool.cc", "memfd_media_buffer_pool.h", "microphone_impl.cc", @@ -445,7 +443,6 @@ test("starboard_platform_tests") { "media_codec_decoder_test.cc", "media_codec_video_decoder_helpers_test.cc", "media_codec_video_decoder_test.cc", - "media_resource_tracker_test.cc", "memfd_media_buffer_pool_test.cc", "mock_media_capabilities_cache.cc", "mock_media_capabilities_cache.h", diff --git a/starboard/android/shared/audio_track_audio_sink_type.cc b/starboard/android/shared/audio_track_audio_sink_type.cc index d9d180478b44..5f62b3b76d66 100644 --- a/starboard/android/shared/audio_track_audio_sink_type.cc +++ b/starboard/android/shared/audio_track_audio_sink_type.cc @@ -261,10 +261,10 @@ void AudioTrackAudioSink::AudioThreadFunc() { SB_LOG(INFO) << "AudioTrackAudioSink thread started."; - int accumulated_written_frames = 0; + int64_t accumulated_written_frames = 0; int64_t last_playback_head_event_at = -1; // microseconds - int last_playback_head_position = 0; + int64_t last_playback_head_position = 0; bool release_frames_after_audio_starts = features::FeatureList::IsEnabled( features::kReleaseVideoFramesAfterAudioStarts); @@ -284,7 +284,7 @@ void AudioTrackAudioSink::AudioThreadFunc() { continue; } - int playback_head_position = 0; + int64_t playback_head_position = 0; int64_t frames_consumed_at = 0; if (audio_track_->GetAndResetHasAudioDeviceChanged()) { SB_LOG(INFO) << "Audio device changed, raising a capability changed " @@ -515,7 +515,7 @@ void AudioTrackAudioSink::ReportError(bool capability_changed, } } -int64_t AudioTrackAudioSink::GetFramesDurationUs(int frames) const { +int64_t AudioTrackAudioSink::GetFramesDurationUs(int64_t frames) const { return frames * 1'000'000LL / sampling_frequency_hz_; } diff --git a/starboard/android/shared/audio_track_audio_sink_type.h b/starboard/android/shared/audio_track_audio_sink_type.h index 5bfb2ccfe692..2344278fef83 100644 --- a/starboard/android/shared/audio_track_audio_sink_type.h +++ b/starboard/android/shared/audio_track_audio_sink_type.h @@ -169,7 +169,7 @@ class AudioTrackAudioSink : public SbAudioSinkImpl { void ReportError(bool capability_changed, const std::string& error_message); - int64_t GetFramesDurationUs(int frames) const; + int64_t GetFramesDurationUs(int64_t frames) const; const raw_ptr type_; const int channels_; diff --git a/starboard/android/shared/audio_track_bridge.cc b/starboard/android/shared/audio_track_bridge.cc index 1132010c54ed..3d21b4004169 100644 --- a/starboard/android/shared/audio_track_bridge.cc +++ b/starboard/android/shared/audio_track_bridge.cc @@ -19,7 +19,6 @@ #include "starboard/android/shared/audio_output_manager.h" #include "starboard/android/shared/media_common.h" -#include "starboard/android/shared/media_resource_tracker.h" #include "starboard/android/shared/safe_jni.h" #include "starboard/audio_sink.h" #include "starboard/common/check_op.h" @@ -124,9 +123,7 @@ AudioTrackBridge::AudioTrackBridge( const DataArray& j_audio_data) : max_samples_per_write_(max_samples_per_write), j_audio_track_bridge_(j_audio_track_bridge), - j_audio_data_(j_audio_data) { - MediaResourceTracker::GetInstance()->Increment(); -} + j_audio_data_(j_audio_data) {} AudioTrackBridge::~AudioTrackBridge() { if (!j_audio_track_bridge_.is_null()) { @@ -138,7 +135,6 @@ AudioTrackBridge::~AudioTrackBridge() { AudioOutputManager::GetInstance()->DestroyAudioTrackBridge( env, audio_track_bridge); } - MediaResourceTracker::GetInstance()->Decrement(); } void AudioTrackBridge::Play() { diff --git a/starboard/android/shared/base_starboard_bridge.cc b/starboard/android/shared/base_starboard_bridge.cc index 27b43935702b..348798dfa3f8 100644 --- a/starboard/android/shared/base_starboard_bridge.cc +++ b/starboard/android/shared/base_starboard_bridge.cc @@ -12,8 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include - #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/strings/string_number_conversions.h" @@ -21,13 +19,11 @@ #include "starboard/android/shared/application_android.h" #include "starboard/android/shared/file_internal.h" #include "starboard/android/shared/log_internal.h" -#include "starboard/android/shared/media_resource_tracker.h" #include "starboard/android/shared/starboard_bridge.h" #include "starboard/common/command_line.h" #include "starboard/common/log.h" #include "starboard/common/time.h" #include "starboard/shared/starboard/audio_sink/audio_sink_internal.h" -#include "starboard/shared/starboard/features.h" #include "third_party/jni_zero/jni_zero.h" // TODO(b/492704919): enable on AOSP when the layering violation is fixed. @@ -110,24 +106,6 @@ void JNI_BaseStarboardBridge_CloseNativeStarboard(JNIEnv* env, jlong nativeApp) { auto* app = reinterpret_cast(nativeApp); delete app; - - if (features::FeatureList::IsEnabled( - features::kWaitForMediaResourcesOnShutdown)) { - std::thread([]() { - constexpr int kTimeoutMs = 2'000; - int remaining_count = - MediaResourceTracker::GetInstance()->WaitUntilZero(kTimeoutMs); - if (remaining_count > 0) { - SB_LOG(WARNING) << "Timed out waiting for all media resources to be " - "destroyed. Active count: " - << remaining_count; - } - exit(0); - }).detach(); - return; - } - - exit(0); } void JNI_BaseStarboardBridge_InitializePlatformAudioSink(JNIEnv* env) { diff --git a/starboard/android/shared/media_drm_bridge.cc b/starboard/android/shared/media_drm_bridge.cc index f11de4692e3e..38e990d6aa7d 100644 --- a/starboard/android/shared/media_drm_bridge.cc +++ b/starboard/android/shared/media_drm_bridge.cc @@ -22,7 +22,6 @@ #include "base/android/jni_array.h" #include "base/android/jni_string.h" #include "base/memory/raw_ref.h" -#include "starboard/android/shared/media_resource_tracker.h" #include "starboard/common/check_op.h" #include "starboard/common/log.h" #include "third_party/jni_zero/jni_zero.h" @@ -152,15 +151,12 @@ std::unique_ptr MediaDrmBridge::Create( MediaDrmBridge::MediaDrmBridge(PassKey, base::raw_ref host) - : host_(host) { - MediaResourceTracker::GetInstance()->Increment(); -} + : host_(host) {} MediaDrmBridge::~MediaDrmBridge() { if (!j_media_drm_bridge_.is_null()) { Java_MediaDrmBridge_destroy(AttachCurrentThread(), j_media_drm_bridge_); } - MediaResourceTracker::GetInstance()->Decrement(); } void MediaDrmBridge::CreateSession(int ticket, diff --git a/starboard/android/shared/media_resource_tracker.cc b/starboard/android/shared/media_resource_tracker.cc deleted file mode 100644 index cd0fa0663186..000000000000 --- a/starboard/android/shared/media_resource_tracker.cc +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2026 The Cobalt Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "starboard/android/shared/media_resource_tracker.h" - -#include -#include - -#include "starboard/common/log.h" - -namespace starboard { - -// static -MediaResourceTracker* MediaResourceTracker::GetInstance() { - static starboard::NoDestructor instance; - return instance.get(); -} - -void MediaResourceTracker::Increment() { - active_media_resource_count_.fetch_add(1, std::memory_order_relaxed); -} - -void MediaResourceTracker::Decrement() { - int prev_count = - active_media_resource_count_.fetch_sub(1, std::memory_order_release); - if (prev_count <= 0) { - SB_LOG(WARNING) << "MediaResourceTracker::Decrement called when active " - "resource count was " - << prev_count << "."; - return; - } - if (prev_count == 1) { - SB_LOG(INFO) << "All media resources destroyed."; - std::lock_guard lock(mutex_); - cv_.notify_all(); - } -} - -int MediaResourceTracker::WaitUntilZero(int timeout_ms) { - if (active_media_resource_count_.load(std::memory_order_acquire) <= 0) { - SB_LOG(INFO) << "Active media resource count is already 0."; - return 0; - } - std::unique_lock lock(mutex_); - cv_.wait_for(lock, std::chrono::milliseconds(timeout_ms), [this] { - return active_media_resource_count_.load(std::memory_order_acquire) <= 0; - }); - int remaining = active_media_resource_count_.load(std::memory_order_acquire); - if (remaining <= 0) { - SB_LOG(INFO) << "Finished waiting for all media resources to be destroyed."; - } - return remaining; -} - -} // namespace starboard diff --git a/starboard/android/shared/media_resource_tracker.h b/starboard/android/shared/media_resource_tracker.h deleted file mode 100644 index 183878cafeef..000000000000 --- a/starboard/android/shared/media_resource_tracker.h +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2026 The Cobalt Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef STARBOARD_ANDROID_SHARED_MEDIA_RESOURCE_TRACKER_H_ -#define STARBOARD_ANDROID_SHARED_MEDIA_RESOURCE_TRACKER_H_ - -#include -#include -#include -#include - -#include "starboard/common/no_destructor.h" - -namespace starboard { - -// MediaResourceTracker provides thread-safe accounting of active native media -// components (SbPlayer decoders, AudioTrack audio sinks, and MediaDrm crypto -// sessions) on Android. -class MediaResourceTracker { - public: - static MediaResourceTracker* GetInstance(); - - MediaResourceTracker(const MediaResourceTracker&) = delete; - MediaResourceTracker& operator=(const MediaResourceTracker&) = delete; - - void Increment(); - void Decrement(); - int WaitUntilZero(int timeout_ms); - - private: - friend class starboard::NoDestructor; - - MediaResourceTracker() = default; - ~MediaResourceTracker() = default; - - std::mutex mutex_; - std::condition_variable cv_; - std::atomic active_media_resource_count_ = 0; -}; - -} // namespace starboard - -#endif // STARBOARD_ANDROID_SHARED_MEDIA_RESOURCE_TRACKER_H_ diff --git a/starboard/android/shared/media_resource_tracker_test.cc b/starboard/android/shared/media_resource_tracker_test.cc deleted file mode 100644 index 83b268389cdd..000000000000 --- a/starboard/android/shared/media_resource_tracker_test.cc +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2026 The Cobalt Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "starboard/android/shared/media_resource_tracker.h" - -#include -#include - -#include "testing/gtest/include/gtest/gtest.h" - -namespace starboard { -namespace { - -TEST(MediaResourceTrackerTest, InitialCountIsZero) { - auto* tracker = MediaResourceTracker::GetInstance(); - EXPECT_EQ(0, tracker->WaitUntilZero(0)); -} - -TEST(MediaResourceTrackerTest, IncrementAndDecrement) { - auto* tracker = MediaResourceTracker::GetInstance(); - tracker->Increment(); - tracker->Increment(); - - std::thread worker([tracker]() { - std::this_thread::sleep_for(std::chrono::milliseconds(20)); - tracker->Decrement(); - std::this_thread::sleep_for(std::chrono::milliseconds(20)); - tracker->Decrement(); - }); - - EXPECT_EQ(0, tracker->WaitUntilZero(1000)); - worker.join(); -} - -TEST(MediaResourceTrackerTest, TimeoutWhenNotZero) { - auto* tracker = MediaResourceTracker::GetInstance(); - tracker->Increment(); - - // Should timeout and return remaining count of 1 - EXPECT_EQ(1, tracker->WaitUntilZero(50)); - - // Clean up counter state - tracker->Decrement(); - EXPECT_EQ(0, tracker->WaitUntilZero(0)); -} - -} // namespace -} // namespace starboard diff --git a/starboard/android/shared/player_create.cc b/starboard/android/shared/player_create.cc index f0af4ca0ebe1..000a71e06011 100644 --- a/starboard/android/shared/player_create.cc +++ b/starboard/android/shared/player_create.cc @@ -19,7 +19,6 @@ #include #include -#include "starboard/android/shared/media_resource_tracker.h" #include "starboard/android/shared/video_max_video_input_size.h" #include "starboard/android/shared/video_surface_view.h" #include "starboard/android/shared/video_window.h" @@ -223,6 +222,5 @@ SbPlayer SbPlayerCreate(SbWindow /*window*/, // don't matter. SbPlayerSetBounds(player.get(), 0, 0, 0, 0, 0); } - starboard::MediaResourceTracker::GetInstance()->Increment(); return player.release(); } diff --git a/starboard/android/shared/player_destroy.cc b/starboard/android/shared/player_destroy.cc index f94a29cfe7c7..6409862d1d8e 100644 --- a/starboard/android/shared/player_destroy.cc +++ b/starboard/android/shared/player_destroy.cc @@ -16,7 +16,6 @@ #include "starboard/player.h" // clang-format on -#include "starboard/android/shared/media_resource_tracker.h" #include "starboard/shared/starboard/player/player_internal.h" void SbPlayerDestroy(SbPlayer player) { @@ -25,5 +24,4 @@ void SbPlayerDestroy(SbPlayer player) { } delete player; - starboard::MediaResourceTracker::GetInstance()->Decrement(); } diff --git a/starboard/extension/feature_config.h b/starboard/extension/feature_config.h index 6075aeb75c2b..703bb6db41be 100644 --- a/starboard/extension/feature_config.h +++ b/starboard/extension/feature_config.h @@ -161,12 +161,6 @@ STARBOARD_FEATURE(kUseStubVideoDecoder, "UseStubVideoDecoder", false) STARBOARD_FEATURE(kVideoDecoderDelayUsecOverride, "VideoDecoderDelayUsecOverride", false) - -// Set the following variable to true to wait for media resources (MediaCodec, -// AudioTrack, MediaDrm) during shutdown before deleting the application. -STARBOARD_FEATURE(kWaitForMediaResourcesOnShutdown, - "WaitForMediaResourcesOnShutdown", - false) // keep-sorted end #endif // BUILDFLAG(IS_ANDROID) && (SB_API_VERSION >= 17) diff --git a/third_party/blink/renderer/modules/cobalt/h5vcc_settings/h_5_vcc_settings.cc b/third_party/blink/renderer/modules/cobalt/h5vcc_settings/h_5_vcc_settings.cc index 6bd719736f83..71d98bebde47 100644 --- a/third_party/blink/renderer/modules/cobalt/h5vcc_settings/h_5_vcc_settings.cc +++ b/third_party/blink/renderer/modules/cobalt/h5vcc_settings/h_5_vcc_settings.cc @@ -149,23 +149,27 @@ ScriptPromise H5vccSettings::set( if (name == "DecoderBuffer.EnableConfigurableDecommitStrategy") { // The value is a 32-bit integer encoding four parts: - // aggressive_decommit_on_suspend (flag), block_size (in MB), retain_blocks - // (count), and conservative_decommit_blocks (count). For example, + // flags (8 bits), block_size (in MB), retain_blocks (count), and + // conservative_decommit_blocks (count). For example, // 0x01040402 sets aggressive_decommit_on_suspend to true, 4 MB block size, // 4 retain blocks, and 2 conservative decommit blocks respectively. // Passing multiple parameters encoded within a single integer is not // ideal, but it simplifies experiment setup in the current framework. // - // - aggressive_decommit_on_suspend: When non-zero, enables aggressive - // MADV_DONTNEED decommit on all idle - // blocks when app suspends/hides. - // - block_size: Specifies both the initial pool capacity and the fallback - // allocation increment. - // - retain_blocks: The first `retain_blocks` blocks of the pool are kept - // fully committed. - // - conservative_decommit_blocks: The next `conservative_decommit_blocks` - // blocks are conservatively decommitted - // (e.g. using MADV_FREE). + // - flags (8 bits): + // - aggressive_decommit_on_suspend (bit 24): When set (LSB is non-zero), + // enables aggressive MADV_DONTNEED decommit on all idle blocks when + // app suspends/hides. + // - allocate_with_page_alignment (bit 25): When set (second bit is + // non-zero), forces new OS block allocations to be page-aligned and + // rounded up to page sizes. Default is true. + // - block_size (8 bits): Specifies both the initial pool capacity and the + // fallback allocation increment. + // - retain_blocks (8 bits): Specifies the first `retain_blocks` blocks of + // the pool are kept fully committed. + // - conservative_decommit_blocks (8 bits): Specifies the next + // `conservative_decommit_blocks` blocks are conservatively decommitted + // (e.g. using MADV_FREE). // Any remaining memory beyond these two windows is aggressively decommitted // (e.g. MADV_DONTNEED). // For example, if 128 MB is allocated for the memory pool, and @@ -179,18 +183,25 @@ ScriptPromise H5vccSettings::set( // (returned // to the OS immediately, with virtual memory address range retained). // - // Note: The values passed in are assumed to be valid positive integers. A - // value of 0 will not enable this feature as it is not a positive integer. - return ProcessSettingAsPositiveInt( + // Note: A value of 0 or less will not enable this feature (handled as a + // placebo). + return ProcessSettingAs( script_state, exception_context, name, *value, [](int value) { - bool aggressive_decommit_on_suspend = ((value >> 24) & 0xFF) != 0; + if (value <= 0) { + // Explicitly allow non-positive values as placebo. + return base::ok(); + } + + bool aggressive_decommit_on_suspend = ((value >> 24) & 0x01) != 0; + bool allocate_with_page_alignment = ((value >> 24) & 0x02) != 0; int block_size_mb = (value >> 16) & 0xFF; int retain_blocks = (value >> 8) & 0xFF; int conservative_decommit_blocks = value & 0xFF; ::media::DecoderBufferAllocator::EnableConfigurableDecommitStrategy( block_size_mb * 1024 * 1024, retain_blocks, - conservative_decommit_blocks, aggressive_decommit_on_suspend); - return true; + conservative_decommit_blocks, aggressive_decommit_on_suspend, + allocate_with_page_alignment); + return base::ok(); }); } if (name == "DecoderBuffer.EnableMediaBufferPoolAllocatorStrategy") {