diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 005d4e2e68..ab94d9922e 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -17,6 +17,7 @@ body: label: Media3 Version description: What version of Media3 are you using? options: + - 1.0.0-rc02 - 1.0.0-rc01 - 1.0.0-beta03 - 1.0.0-beta02 diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 38c631388e..b5a6a75e46 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,49 @@ # Release notes +### 1.0.0-rc02 (2023-03-02) + +This release corresponds to the +[ExoPlayer 2.18.4 release](https://github.com/google/ExoPlayer/releases/tag/r2.18.4). + +* Core library: + * Fix network type detection on API 33 + ([#10970](https://github.com/google/ExoPlayer/issues/10970)). + * Fix `NullPointerException` when calling `ExoPlayer.isTunnelingEnabled` + ([#10977](https://github.com/google/ExoPlayer/issues/10977)). +* Downloads: + * Make the maximum difference of the start time of two segments to be + merged configurable in `SegmentDownloader` and subclasses + ([#248](https://github.com/androidx/media/pull/248)). +* Audio: + * Fix broken gapless MP3 playback on Samsung devices + ([#8594](https://github.com/google/ExoPlayer/issues/8594)). + * Fix bug where playback speeds set immediately after disabling audio may + be overridden by a previous speed change + ([#10882](https://github.com/google/ExoPlayer/issues/10882)). +* Video: + * Map HEVC HDR10 format to `HEVCProfileMain10HDR10` instead of + `HEVCProfileMain10`. + * Add workaround for a device issue on Chromecast with Google TV and + Lenovo M10 FHD Plus that causes 60fps AVC streams to be marked as + unsupported + ([#10898](https://github.com/google/ExoPlayer/issues/10898)). + * Fix frame release performance issues when playing media with a frame + rate far higher than the screen refresh rate. +* Cast: + * Fix transient `STATE_IDLE` when transitioning between media items + ([#245](https://github.com/androidx/media/issues/245)). +* RTSP: + * Catch the IllegalArgumentException thrown in parsing of invalid RTSP + Describe response messages + ([#10971](https://github.com/google/ExoPlayer/issues/10971)). +* Session: + * Fix a bug where notification play/pause button doesn't update with + player state ([#192](https://github.com/androidx/media/issues/192)). +* IMA extension: + * Fix a bug which prevented DAI streams without any ads from starting + because the first (and in the case without ads the only) `LOADED` event + wasn't received. + ### 1.0.0-rc01 (2023-02-16) This release corresponds to the @@ -73,17 +117,12 @@ This release corresponds to the ([#233](https://github.com/androidx/media/issues/233)). * Make `QueueTimeline` more robust in case of a shady legacy session state ([#241](https://github.com/androidx/media/issues/241)). -* Metadata: - * Parse multiple null-separated values from ID3 frames, as permitted by - ID3 v2.4. - * Add `MediaMetadata.mediaType` to denote the type of content or the type - of folder described by the metadata. - * Add `MediaMetadata.isBrowsable` as a replacement for - `MediaMetadata.folderType`. The folder type will be deprecated in the - next release. * Cast extension: * Bump Cast SDK version to 21.2.0. * IMA extension: + * Map `PLAYER_STATE_LOADING` to `STATE_BUFFERING` + ([#245](\(https://github.com/androidx/media/issues/245\)). +* IMA extension * Remove player listener of the `ImaServerSideAdInsertionMediaSource` on the application thread to avoid threading issues. * Add a property `focusSkipButtonWhenAvailable` to the @@ -92,6 +131,8 @@ This release corresponds to the * Add a method `focusSkipButton()` to the `ImaServerSideAdInsertionMediaSource.AdsLoader` to programmatically request to focus the skip button. + * Fix a bug which prevented playback from starting for a DAI stream + without any ads. * Bump IMA SDK version to 3.29.0. * Demo app: * Request notification permission for download notifications at runtime diff --git a/constants.gradle b/constants.gradle index 39884d57c7..f1fa589e94 100644 --- a/constants.gradle +++ b/constants.gradle @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. project.ext { - releaseVersion = '1.0.0-rc01' - releaseVersionCode = 1_000_000_2_01 + releaseVersion = '1.0.0-rc02' + releaseVersionCode = 1_000_000_2_02 minSdkVersion = 16 appTargetSdkVersion = 33 // API version before restricting local file access. diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java index 8d2a0cbde1..dbf61395bd 100644 --- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java +++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java @@ -1237,6 +1237,7 @@ public final class CastPlayer extends BasePlayer { int receiverAppStatus = remoteMediaClient.getPlayerState(); switch (receiverAppStatus) { case MediaStatus.PLAYER_STATE_BUFFERING: + case MediaStatus.PLAYER_STATE_LOADING: return STATE_BUFFERING; case MediaStatus.PLAYER_STATE_PLAYING: case MediaStatus.PLAYER_STATE_PAUSED: @@ -1299,6 +1300,7 @@ public final class CastPlayer extends BasePlayer { return false; } + @SuppressWarnings("VisibleForTests") private static int getCastRepeatMode(@RepeatMode int repeatMode) { switch (repeatMode) { case REPEAT_MODE_ONE: diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java index ac44ebb603..112b2ea841 100644 --- a/libraries/common/src/main/java/androidx/media3/common/C.java +++ b/libraries/common/src/main/java/androidx/media3/common/C.java @@ -331,7 +331,10 @@ public final class C { */ @UnstableApi public static final int ENCODING_OPUS = AudioFormat.ENCODING_OPUS; - /** Represents the behavior affecting whether spatialization will be used. */ + /** + * Represents the behavior affecting whether spatialization will be used. One of {@link + * #SPATIALIZATION_BEHAVIOR_AUTO} or {@link #SPATIALIZATION_BEHAVIOR_NEVER}. + */ @Documented @Retention(RetentionPolicy.SOURCE) @Target(TYPE_USE) diff --git a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java index 2640a0a7e4..957ab7d219 100644 --- a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java +++ b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java @@ -28,7 +28,7 @@ import androidx.media3.common.util.UnstableApi; import java.util.List; /** - * A {@link Player} that forwards operations to another {@link Player}. Applications can use this + * A {@link Player} that forwards method calls to another {@link Player}. Applications can use this * class to suppress or modify specific operations, by overriding the respective methods. */ @UnstableApi diff --git a/libraries/common/src/main/java/androidx/media3/common/MediaLibraryInfo.java b/libraries/common/src/main/java/androidx/media3/common/MediaLibraryInfo.java index ad013f4cd3..64cdc2d996 100644 --- a/libraries/common/src/main/java/androidx/media3/common/MediaLibraryInfo.java +++ b/libraries/common/src/main/java/androidx/media3/common/MediaLibraryInfo.java @@ -29,11 +29,11 @@ public final class MediaLibraryInfo { /** The version of the library expressed as a string, for example "1.2.3" or "1.2.3-beta01". */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa. - public static final String VERSION = "1.0.0-rc01"; + public static final String VERSION = "1.0.0-rc02"; /** The version of the library expressed as {@code TAG + "/" + VERSION}. */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - public static final String VERSION_SLASHY = "AndroidXMedia3/1.0.0-rc01"; + public static final String VERSION_SLASHY = "AndroidXMedia3/1.0.0-rc02"; /** * The version of the library expressed as an integer, for example 1002003300. @@ -47,7 +47,7 @@ public final class MediaLibraryInfo { * (123-045-006-3-00). */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - public static final int VERSION_INT = 1_000_000_2_01; + public static final int VERSION_INT = 1_000_000_2_02; /** Whether the library was compiled with {@link Assertions} checks enabled. */ public static final boolean ASSERTIONS_ENABLED = true; diff --git a/libraries/common/src/main/java/androidx/media3/common/util/ColorParser.java b/libraries/common/src/main/java/androidx/media3/common/util/ColorParser.java index 3d1f7a6349..f0550b0789 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/ColorParser.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/ColorParser.java @@ -27,8 +27,12 @@ import java.util.regex.Pattern; /** * Parser for color expressions found in styling formats, e.g. TTML and CSS. * - * @see WebVTT CSS Styling - * @see Timed Text Markup Language 2 (TTML2) - 10.3.5 + *

See also: + * + *

*/ @UnstableApi public final class ColorParser { diff --git a/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java b/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java index 4e97ab796c..7b64795466 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java @@ -94,7 +94,7 @@ public final class NetworkTypeObserver { networkType = C.NETWORK_TYPE_UNKNOWN; IntentFilter filter = new IntentFilter(); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - Util.registerReceiverNotExported(context, new Receiver(), filter); + context.registerReceiver(new Receiver(), filter); } /** diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Util.java b/libraries/common/src/main/java/androidx/media3/common/util/Util.java index 464db2648d..a079c488d9 100644 --- a/libraries/common/src/main/java/androidx/media3/common/util/Util.java +++ b/libraries/common/src/main/java/androidx/media3/common/util/Util.java @@ -206,6 +206,10 @@ public final class Util { * apps. This will be enforced by specifying {@link Context#RECEIVER_NOT_EXPORTED} if {@link * #SDK_INT} is 33 or above. * + *

Do not use this method if registering a receiver for a protected + * system broadcast. + * * @param context The context on which {@link Context#registerReceiver} will be called. * @param receiver The {@link BroadcastReceiver} to register. This value may be null. * @param filter Selects the Intent broadcasts to be received. @@ -222,33 +226,6 @@ public final class Util { } } - /** - * Registers a {@link BroadcastReceiver} that's not intended to receive broadcasts from other - * apps. This will be enforced by specifying {@link Context#RECEIVER_NOT_EXPORTED} if {@link - * #SDK_INT} is 33 or above. - * - * @param context The context on which {@link Context#registerReceiver} will be called. - * @param receiver The {@link BroadcastReceiver} to register. This value may be null. - * @param filter Selects the Intent broadcasts to be received. - * @param handler Handler identifying the thread that will receive the Intent. - * @return The first sticky intent found that matches {@code filter}, or null if there are none. - */ - @UnstableApi - @Nullable - public static Intent registerReceiverNotExported( - Context context, BroadcastReceiver receiver, IntentFilter filter, Handler handler) { - if (SDK_INT < 33) { - return context.registerReceiver(receiver, filter, /* broadcastPermission= */ null, handler); - } else { - return context.registerReceiver( - receiver, - filter, - /* broadcastPermission= */ null, - handler, - Context.RECEIVER_NOT_EXPORTED); - } - } - /** * Calls {@link Context#startForegroundService(Intent)} if {@link #SDK_INT} is 26 or higher, or * {@link Context#startService(Intent)} otherwise. diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CachedContentIndex.java b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CachedContentIndex.java index 0d403fb173..36c2d45429 100644 --- a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CachedContentIndex.java +++ b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CachedContentIndex.java @@ -794,11 +794,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; @Override public boolean exists() throws DatabaseIOException { - return VersionTable.getVersion( - databaseProvider.getReadableDatabase(), - VersionTable.FEATURE_CACHE_CONTENT_METADATA, - checkNotNull(hexUid)) - != VersionTable.VERSION_UNSET; + try { + return VersionTable.getVersion( + databaseProvider.getReadableDatabase(), + VersionTable.FEATURE_CACHE_CONTENT_METADATA, + checkNotNull(hexUid)) + != VersionTable.VERSION_UNSET; + } catch (SQLException e) { + throw new DatabaseIOException(e); + } } @Override diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioBecomingNoisyManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioBecomingNoisyManager.java index 625c6090b6..04fd1482bd 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioBecomingNoisyManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioBecomingNoisyManager.java @@ -21,7 +21,6 @@ import android.content.Intent; import android.content.IntentFilter; import android.media.AudioManager; import android.os.Handler; -import androidx.media3.common.util.Util; /* package */ final class AudioBecomingNoisyManager { @@ -47,8 +46,8 @@ import androidx.media3.common.util.Util; */ public void setEnabled(boolean enabled) { if (enabled && !receiverRegistered) { - Util.registerReceiverNotExported( - context, receiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); + context.registerReceiver( + receiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); receiverRegistered = true; } else if (!enabled && receiverRegistered) { context.unregisterReceiver(receiver); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java index 525b2df9c4..10c64a7e98 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java @@ -1724,8 +1724,9 @@ import java.util.concurrent.TimeoutException; @Override public boolean isTunnelingEnabled() { verifyApplicationThread(); - for (RendererConfiguration config : playbackInfo.trackSelectorResult.rendererConfigurations) { - if (config.tunneling) { + for (@Nullable + RendererConfiguration config : playbackInfo.trackSelectorResult.rendererConfigurations) { + if (config != null && config.tunneling) { return true; } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java index 6c3c64075b..e84ea1e7e0 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java @@ -946,7 +946,7 @@ import java.util.concurrent.atomic.AtomicBoolean; livePlaybackSpeedControl.getAdjustedPlaybackSpeed( getCurrentLiveOffsetUs(), getTotalBufferedDurationUs()); if (mediaClock.getPlaybackParameters().speed != adjustedSpeed) { - mediaClock.setPlaybackParameters(playbackInfo.playbackParameters.withSpeed(adjustedSpeed)); + setMediaClockPlaybackParameters(playbackInfo.playbackParameters.withSpeed(adjustedSpeed)); handlePlaybackParameters( playbackInfo.playbackParameters, /* currentPlaybackSpeed= */ mediaClock.getPlaybackParameters().speed, @@ -956,6 +956,12 @@ import java.util.concurrent.atomic.AtomicBoolean; } } + private void setMediaClockPlaybackParameters(PlaybackParameters playbackParameters) { + // Previously sent speed updates from the media clock now become stale. + handler.removeMessages(MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL); + mediaClock.setPlaybackParameters(playbackParameters); + } + private void notifyTrackSelectionRebuffer() { MediaPeriodHolder periodHolder = queue.getPlayingPeriod(); while (periodHolder != null) { @@ -1342,7 +1348,7 @@ import java.util.concurrent.atomic.AtomicBoolean; private void setPlaybackParametersInternal(PlaybackParameters playbackParameters) throws ExoPlaybackException { - mediaClock.setPlaybackParameters(playbackParameters); + setMediaClockPlaybackParameters(playbackParameters); handlePlaybackParameters(mediaClock.getPlaybackParameters(), /* acknowledgeCommand= */ true); } @@ -1657,7 +1663,7 @@ import java.util.concurrent.atomic.AtomicBoolean; nextPendingMessageIndexHint = nextPendingMessageIndex; } - private void ensureStopped(Renderer renderer) throws ExoPlaybackException { + private void ensureStopped(Renderer renderer) { if (renderer.getState() == Renderer.STATE_STARTED) { renderer.stop(); } @@ -1914,14 +1920,20 @@ import java.util.concurrent.atomic.AtomicBoolean; MediaPeriodId newPeriodId, Timeline oldTimeline, MediaPeriodId oldPeriodId, - long positionForTargetOffsetOverrideUs) { + long positionForTargetOffsetOverrideUs) + throws ExoPlaybackException { if (!shouldUseLivePlaybackSpeedControl(newTimeline, newPeriodId)) { // Live playback speed control is unused for the current period, reset speed to user-defined // playback parameters or 1.0 for ad playback. PlaybackParameters targetPlaybackParameters = newPeriodId.isAd() ? PlaybackParameters.DEFAULT : playbackInfo.playbackParameters; if (!mediaClock.getPlaybackParameters().equals(targetPlaybackParameters)) { - mediaClock.setPlaybackParameters(targetPlaybackParameters); + setMediaClockPlaybackParameters(targetPlaybackParameters); + handlePlaybackParameters( + playbackInfo.playbackParameters, + targetPlaybackParameters.speed, + /* updatePlaybackInfo= */ false, + /* acknowledgeCommand= */ false); } return; } @@ -1970,7 +1982,7 @@ import java.util.concurrent.atomic.AtomicBoolean; return maxReadPositionUs; } - private void updatePeriods() throws ExoPlaybackException, IOException { + private void updatePeriods() throws ExoPlaybackException { if (playbackInfo.timeline.isEmpty() || !mediaSourceList.isPrepared()) { // No periods available. return; @@ -2012,7 +2024,7 @@ import java.util.concurrent.atomic.AtomicBoolean; } } - private void maybeUpdateReadingPeriod() { + private void maybeUpdateReadingPeriod() throws ExoPlaybackException { @Nullable MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod(); if (readingPeriodHolder == null) { return; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java index 1fc7dc4828..c5a8230154 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java @@ -75,7 +75,7 @@ import androidx.media3.common.util.Util; VolumeChangeReceiver receiver = new VolumeChangeReceiver(); IntentFilter filter = new IntentFilter(VOLUME_CHANGED_ACTION); try { - Util.registerReceiverNotExported(applicationContext, receiver, filter); + applicationContext.registerReceiver(receiver, filter); this.receiver = receiver; } catch (RuntimeException e) { Log.w(TAG, "Error registering stream volume receiver", e); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilities.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilities.java index 6cb10f0731..9888db45a6 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilities.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilities.java @@ -88,8 +88,8 @@ public final class AudioCapabilities { @SuppressWarnings("InlinedApi") public static AudioCapabilities getCapabilities(Context context) { Intent intent = - Util.registerReceiverNotExported( - context, /* receiver= */ null, new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG)); + context.registerReceiver( + /* receiver= */ null, new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG)); return getCapabilities(context, intent); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilitiesReceiver.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilitiesReceiver.java index 1cfef2accc..671ddb5aa6 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilitiesReceiver.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilitiesReceiver.java @@ -93,7 +93,9 @@ public final class AudioCapabilitiesReceiver { @Nullable Intent stickyIntent = null; if (receiver != null) { IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG); - stickyIntent = Util.registerReceiverNotExported(context, receiver, intentFilter, handler); + stickyIntent = + context.registerReceiver( + receiver, intentFilter, /* broadcastPermission= */ null, handler); } audioCapabilities = AudioCapabilities.getCapabilities(context, stickyIntent); return audioCapabilities; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java index 952c5fb8b6..414cf24d40 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java @@ -15,6 +15,7 @@ */ package androidx.media3.exoplayer.audio; +import static androidx.media3.common.util.Assertions.checkNotNull; import static androidx.media3.common.util.Util.castNonNull; import static java.lang.Math.max; import static java.lang.Math.min; @@ -26,7 +27,6 @@ import android.os.SystemClock; import androidx.annotation.IntDef; import androidx.annotation.Nullable; import androidx.media3.common.C; -import androidx.media3.common.util.Assertions; import androidx.media3.common.util.Util; import java.lang.annotation.Documented; import java.lang.annotation.Retention; @@ -146,6 +146,9 @@ import java.lang.reflect.Method; /** The duration of time used to smooth over an adjustment between position sampling modes. */ private static final long MODE_SWITCH_SMOOTHING_DURATION_US = C.MICROS_PER_SECOND; + /** Minimum update interval for getting the raw playback head position, in milliseconds. */ + private static final long RAW_PLAYBACK_HEAD_POSITION_UPDATE_INTERVAL_MS = 5; + private static final long FORCE_RESET_WORKAROUND_TIMEOUT_MS = 200; private static final int MAX_PLAYHEAD_OFFSET_COUNT = 10; @@ -174,7 +177,8 @@ import java.lang.reflect.Method; private boolean isOutputPcm; private long lastLatencySampleTimeUs; - private long lastRawPlaybackHeadPosition; + private long lastRawPlaybackHeadPositionSampleTimeMs; + private long rawPlaybackHeadPosition; private long rawPlaybackHeadWrapCount; private long passthroughWorkaroundPauseOffset; private int nextPlayheadOffsetIndex; @@ -199,7 +203,7 @@ import java.lang.reflect.Method; * @param listener A listener for position tracking events. */ public AudioTrackPositionTracker(Listener listener) { - this.listener = Assertions.checkNotNull(listener); + this.listener = checkNotNull(listener); if (Util.SDK_INT >= 18) { try { getLatencyMethod = AudioTrack.class.getMethod("getLatency", (Class[]) null); @@ -235,7 +239,7 @@ import java.lang.reflect.Method; needsPassthroughWorkarounds = isPassthrough && needsPassthroughWorkarounds(outputEncoding); isOutputPcm = Util.isEncodingLinearPcm(outputEncoding); bufferSizeUs = isOutputPcm ? framesToDurationUs(bufferSize / outputPcmFrameSize) : C.TIME_UNSET; - lastRawPlaybackHeadPosition = 0; + rawPlaybackHeadPosition = 0; rawPlaybackHeadWrapCount = 0; passthroughWorkaroundPauseOffset = 0; hasData = false; @@ -253,10 +257,11 @@ import java.lang.reflect.Method; if (audioTimestampPoller != null) { audioTimestampPoller.reset(); } + resetSyncParams(); } public long getCurrentPositionUs(boolean sourceEnded) { - if (Assertions.checkNotNull(this.audioTrack).getPlayState() == PLAYSTATE_PLAYING) { + if (checkNotNull(this.audioTrack).getPlayState() == PLAYSTATE_PLAYING) { maybeSampleSyncParams(); } @@ -264,7 +269,7 @@ import java.lang.reflect.Method; // Otherwise, derive a smoothed position by sampling the track's frame position. long systemTimeUs = System.nanoTime() / 1000; long positionUs; - AudioTimestampPoller audioTimestampPoller = Assertions.checkNotNull(this.audioTimestampPoller); + AudioTimestampPoller audioTimestampPoller = checkNotNull(this.audioTimestampPoller); boolean useGetTimestampMode = audioTimestampPoller.hasAdvancingTimestamp(); if (useGetTimestampMode) { // Calculate the speed-adjusted position using the timestamp (which may be in the future). @@ -282,7 +287,9 @@ import java.lang.reflect.Method; // getPlaybackHeadPositionUs() only has a granularity of ~20 ms, so we base the position off // the system clock (and a smoothed offset between it and the playhead position) so as to // prevent jitter in the reported positions. - positionUs = systemTimeUs + smoothedPlayheadOffsetUs; + positionUs = + Util.getMediaDurationForPlayoutDuration( + systemTimeUs + smoothedPlayheadOffsetUs, audioTrackPlaybackSpeed); } if (!sourceEnded) { positionUs = max(0, positionUs - latencyUs); @@ -329,12 +336,12 @@ import java.lang.reflect.Method; /** Starts position tracking. Must be called immediately before {@link AudioTrack#play()}. */ public void start() { - Assertions.checkNotNull(audioTimestampPoller).reset(); + checkNotNull(audioTimestampPoller).reset(); } /** Returns whether the audio track is in the playing state. */ public boolean isPlaying() { - return Assertions.checkNotNull(audioTrack).getPlayState() == PLAYSTATE_PLAYING; + return checkNotNull(audioTrack).getPlayState() == PLAYSTATE_PLAYING; } /** @@ -345,7 +352,7 @@ import java.lang.reflect.Method; * @return Whether the caller can write data to the track. */ public boolean mayHandleBuffer(long writtenFrames) { - @PlayState int playState = Assertions.checkNotNull(audioTrack).getPlayState(); + @PlayState int playState = checkNotNull(audioTrack).getPlayState(); if (needsPassthroughWorkarounds) { // An AC-3 audio track continues to play data written while it is paused. Stop writing so its // buffer empties. See [Internal: b/18899620]. @@ -426,7 +433,7 @@ import java.lang.reflect.Method; if (stopTimestampUs == C.TIME_UNSET) { // The audio track is going to be paused, so reset the timestamp poller to ensure it doesn't // supply an advancing position. - Assertions.checkNotNull(audioTimestampPoller).reset(); + checkNotNull(audioTimestampPoller).reset(); return true; } // We've handled the end of the stream already, so there's no need to pause the track. @@ -444,15 +451,17 @@ import java.lang.reflect.Method; } private void maybeSampleSyncParams() { - long playbackPositionUs = getPlaybackHeadPositionUs(); - if (playbackPositionUs == 0) { - // The AudioTrack hasn't output anything yet. - return; - } long systemTimeUs = System.nanoTime() / 1000; if (systemTimeUs - lastPlayheadSampleTimeUs >= MIN_PLAYHEAD_OFFSET_SAMPLE_INTERVAL_US) { + long playbackPositionUs = getPlaybackHeadPositionUs(); + if (playbackPositionUs == 0) { + // The AudioTrack hasn't output anything yet. + return; + } // Take a new sample and update the smoothed offset between the system clock and the playhead. - playheadOffsets[nextPlayheadOffsetIndex] = playbackPositionUs - systemTimeUs; + playheadOffsets[nextPlayheadOffsetIndex] = + Util.getPlayoutDurationForMediaDuration(playbackPositionUs, audioTrackPlaybackSpeed) + - systemTimeUs; nextPlayheadOffsetIndex = (nextPlayheadOffsetIndex + 1) % MAX_PLAYHEAD_OFFSET_COUNT; if (playheadOffsetCount < MAX_PLAYHEAD_OFFSET_COUNT) { playheadOffsetCount++; @@ -470,12 +479,12 @@ import java.lang.reflect.Method; return; } - maybePollAndCheckTimestamp(systemTimeUs, playbackPositionUs); + maybePollAndCheckTimestamp(systemTimeUs); maybeUpdateLatency(systemTimeUs); } - private void maybePollAndCheckTimestamp(long systemTimeUs, long playbackPositionUs) { - AudioTimestampPoller audioTimestampPoller = Assertions.checkNotNull(this.audioTimestampPoller); + private void maybePollAndCheckTimestamp(long systemTimeUs) { + AudioTimestampPoller audioTimestampPoller = checkNotNull(this.audioTimestampPoller); if (!audioTimestampPoller.maybePollTimestamp(systemTimeUs)) { return; } @@ -483,6 +492,7 @@ import java.lang.reflect.Method; // Check the timestamp and accept/reject it. long audioTimestampSystemTimeUs = audioTimestampPoller.getTimestampSystemTimeUs(); long audioTimestampPositionFrames = audioTimestampPoller.getTimestampPositionFrames(); + long playbackPositionUs = getPlaybackHeadPositionUs(); if (Math.abs(audioTimestampSystemTimeUs - systemTimeUs) > MAX_AUDIO_TIMESTAMP_OFFSET_US) { listener.onSystemTimeUsMismatch( audioTimestampPositionFrames, @@ -511,8 +521,7 @@ import java.lang.reflect.Method; // Compute the audio track latency, excluding the latency due to the buffer (leaving // latency due to the mixer and audio hardware driver). latencyUs = - castNonNull((Integer) getLatencyMethod.invoke(Assertions.checkNotNull(audioTrack))) - * 1000L + castNonNull((Integer) getLatencyMethod.invoke(checkNotNull(audioTrack))) * 1000L - bufferSizeUs; // Check that the latency is non-negative. latencyUs = max(latencyUs, 0); @@ -550,7 +559,7 @@ import java.lang.reflect.Method; */ private boolean forceHasPendingData() { return needsPassthroughWorkarounds - && Assertions.checkNotNull(audioTrack).getPlayState() == AudioTrack.PLAYSTATE_PAUSED + && checkNotNull(audioTrack).getPlayState() == AudioTrack.PLAYSTATE_PAUSED && getPlaybackHeadPosition() == 0; } @@ -576,34 +585,44 @@ import java.lang.reflect.Method; * @return The playback head position, in frames. */ private long getPlaybackHeadPosition() { - AudioTrack audioTrack = Assertions.checkNotNull(this.audioTrack); + long currentTimeMs = SystemClock.elapsedRealtime(); if (stopTimestampUs != C.TIME_UNSET) { // Simulate the playback head position up to the total number of frames submitted. - long elapsedTimeSinceStopUs = (SystemClock.elapsedRealtime() * 1000) - stopTimestampUs; - long framesSinceStop = (elapsedTimeSinceStopUs * outputSampleRate) / C.MICROS_PER_SECOND; + long elapsedTimeSinceStopUs = (currentTimeMs * 1000) - stopTimestampUs; + long mediaTimeSinceStopUs = + Util.getMediaDurationForPlayoutDuration(elapsedTimeSinceStopUs, audioTrackPlaybackSpeed); + long framesSinceStop = (mediaTimeSinceStopUs * outputSampleRate) / C.MICROS_PER_SECOND; return min(endPlaybackHeadPosition, stopPlaybackHeadPosition + framesSinceStop); } + if (currentTimeMs - lastRawPlaybackHeadPositionSampleTimeMs + >= RAW_PLAYBACK_HEAD_POSITION_UPDATE_INTERVAL_MS) { + updateRawPlaybackHeadPosition(currentTimeMs); + lastRawPlaybackHeadPositionSampleTimeMs = currentTimeMs; + } + return rawPlaybackHeadPosition + (rawPlaybackHeadWrapCount << 32); + } + private void updateRawPlaybackHeadPosition(long currentTimeMs) { + AudioTrack audioTrack = checkNotNull(this.audioTrack); int state = audioTrack.getPlayState(); if (state == PLAYSTATE_STOPPED) { - // The audio track hasn't been started. - return 0; + // The audio track hasn't been started. Keep initial zero timestamp. + return; } - long rawPlaybackHeadPosition = 0xFFFFFFFFL & audioTrack.getPlaybackHeadPosition(); if (needsPassthroughWorkarounds) { // Work around an issue with passthrough/direct AudioTracks on platform API versions 21/22 // where the playback head position jumps back to zero on paused passthrough/direct audio // tracks. See [Internal: b/19187573]. if (state == PLAYSTATE_PAUSED && rawPlaybackHeadPosition == 0) { - passthroughWorkaroundPauseOffset = lastRawPlaybackHeadPosition; + passthroughWorkaroundPauseOffset = this.rawPlaybackHeadPosition; } rawPlaybackHeadPosition += passthroughWorkaroundPauseOffset; } if (Util.SDK_INT <= 29) { if (rawPlaybackHeadPosition == 0 - && lastRawPlaybackHeadPosition > 0 + && this.rawPlaybackHeadPosition > 0 && state == PLAYSTATE_PLAYING) { // If connecting a Bluetooth audio device fails, the AudioTrack may be left in a state // where its Java API is in the playing state, but the native track is stopped. When this @@ -611,19 +630,18 @@ import java.lang.reflect.Method; // playback head position and force the track to be reset after // {@link #FORCE_RESET_WORKAROUND_TIMEOUT_MS} has elapsed. if (forceResetWorkaroundTimeMs == C.TIME_UNSET) { - forceResetWorkaroundTimeMs = SystemClock.elapsedRealtime(); + forceResetWorkaroundTimeMs = currentTimeMs; } - return lastRawPlaybackHeadPosition; + return; } else { forceResetWorkaroundTimeMs = C.TIME_UNSET; } } - if (lastRawPlaybackHeadPosition > rawPlaybackHeadPosition) { + if (this.rawPlaybackHeadPosition > rawPlaybackHeadPosition) { // The value must have wrapped around. rawPlaybackHeadWrapCount++; } - lastRawPlaybackHeadPosition = rawPlaybackHeadPosition; - return rawPlaybackHeadPosition + (rawPlaybackHeadWrapCount << 32); + this.rawPlaybackHeadPosition = rawPlaybackHeadPosition; } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java index 91aa86ca06..9377aaace6 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java @@ -191,11 +191,13 @@ import java.nio.ByteBuffer; @Override public int dequeueInputBufferIndex() { + bufferEnqueuer.maybeThrowException(); return asynchronousMediaCodecCallback.dequeueInputBufferIndex(); } @Override public int dequeueOutputBufferIndex(MediaCodec.BufferInfo bufferInfo) { + bufferEnqueuer.maybeThrowException(); return asynchronousMediaCodecCallback.dequeueOutputBufferIndex(bufferInfo); } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecBufferEnqueuer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecBufferEnqueuer.java index 10f0a24e15..bba85feff9 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecBufferEnqueuer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecBufferEnqueuer.java @@ -162,7 +162,8 @@ class AsynchronousMediaCodecBufferEnqueuer { blockUntilHandlerThreadIsIdle(); } - private void maybeThrowException() { + /** Throw any exception that occurred on the enqueuer's background queueing thread. */ + public void maybeThrowException() { @Nullable RuntimeException exception = pendingRuntimeException.getAndSet(null); if (exception != null) { throw exception; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java index 67c6649d27..e950d3ac73 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java @@ -263,11 +263,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; // else, pendingOutputFormat may already be non-null following a previous flush, and remains // set in this case. + // mediaCodecException is not reset to null. If the codec has raised an error, then it remains + // in FAILED_STATE even after flushing. availableInputBuffers.clear(); availableOutputBuffers.clear(); bufferInfos.clear(); formats.clear(); - mediaCodecException = null; } @GuardedBy("lock") diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java index e49a9a5a3c..d36f9eb07b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java @@ -857,7 +857,7 @@ public final class MediaCodecInfo { * @param name The name of the codec. * @return Whether to enable the workaround. */ - private static final boolean needsRotatedVerticalResolutionWorkaround(String name) { + private static boolean needsRotatedVerticalResolutionWorkaround(String name) { if ("OMX.MTK.VIDEO.DECODER.HEVC".equals(name) && "mcv5a".equals(Util.DEVICE)) { // See https://github.com/google/ExoPlayer/issues/6612. return false; @@ -876,6 +876,17 @@ public final class MediaCodecInfo { && ("sailfish".equals(Util.DEVICE) || "marlin".equals(Util.DEVICE)); } + /** Whether the device is known to have wrong {@link PerformancePoint} declarations. */ + private static boolean needsIgnorePerformancePointsWorkaround() { + // See https://github.com/google/ExoPlayer/issues/10898 and [internal ref: b/267324685]. + return /* Chromecast with Google TV */ Util.DEVICE.equals("sabrina") + || Util.DEVICE.equals("boreal") + /* Lenovo Tablet M10 FHD Plus */ + || Util.MODEL.startsWith("Lenovo TB-X605") + || Util.MODEL.startsWith("Lenovo TB-X606") + || Util.MODEL.startsWith("Lenovo TB-X616"); + } + /** Possible outcomes of evaluating PerformancePoint coverage */ @Documented @Retention(RetentionPolicy.SOURCE) @@ -900,7 +911,9 @@ public final class MediaCodecInfo { VideoCapabilities videoCapabilities, int width, int height, double frameRate) { List performancePointList = videoCapabilities.getSupportedPerformancePoints(); - if (performancePointList == null || performancePointList.isEmpty()) { + if (performancePointList == null + || performancePointList.isEmpty() + || needsIgnorePerformancePointsWorkaround()) { return COVERAGE_RESULT_NO_EMPTY_LIST; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java index 815b4c369e..a752432fa0 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java @@ -208,10 +208,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer { */ private static final long MAX_CODEC_HOTSWAP_TIME_MS = 1000; - // Generally there is zero or one pending output stream offset. We track more offsets to allow for - // pending output streams that have fewer frames than the codec latency. - private static final int MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT = 10; - @Documented @Retention(RetentionPolicy.SOURCE) @Target(TYPE_USE) @@ -303,12 +299,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private final DecoderInputBuffer buffer; private final DecoderInputBuffer bypassSampleBuffer; private final BatchBuffer bypassBatchBuffer; - private final TimedValueQueue formatQueue; private final ArrayList decodeOnlyPresentationTimestamps; private final MediaCodec.BufferInfo outputBufferInfo; - private final long[] pendingOutputStreamStartPositionsUs; - private final long[] pendingOutputStreamOffsetsUs; - private final long[] pendingOutputStreamSwitchTimesUs; + private final ArrayDeque pendingOutputStreamChanges; @Nullable private Format inputFormat; @Nullable private Format outputFormat; @@ -363,9 +356,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer { private boolean pendingOutputEndOfStream; @Nullable private ExoPlaybackException pendingPlaybackException; protected DecoderCounters decoderCounters; - private long outputStreamStartPositionUs; - private long outputStreamOffsetUs; - private int pendingOutputStreamOffsetCount; + private OutputStreamInfo outputStreamInfo; + private long lastProcessedOutputBufferTimeUs; + private boolean needToNotifyOutputFormatChangeAfterStreamChange; /** * @param trackType The {@link C.TrackType track type} that the renderer handles. @@ -392,17 +385,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer { buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED); bypassSampleBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT); bypassBatchBuffer = new BatchBuffer(); - formatQueue = new TimedValueQueue<>(); decodeOnlyPresentationTimestamps = new ArrayList<>(); outputBufferInfo = new MediaCodec.BufferInfo(); currentPlaybackSpeed = 1f; targetPlaybackSpeed = 1f; renderTimeLimitMs = C.TIME_UNSET; - pendingOutputStreamStartPositionsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; - pendingOutputStreamOffsetsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; - pendingOutputStreamSwitchTimesUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT]; - outputStreamStartPositionUs = C.TIME_UNSET; - setOutputStreamOffsetUs(C.TIME_UNSET); + pendingOutputStreamChanges = new ArrayDeque<>(); + setOutputStreamInfo(OutputStreamInfo.UNSET); // MediaCodec outputs audio buffers in native endian: // https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers // and code called from MediaCodecAudioRenderer.processOutputBuffer expects this endianness. @@ -419,6 +408,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { codecHotswapDeadlineMs = C.TIME_UNSET; largestQueuedPresentationTimeUs = C.TIME_UNSET; lastBufferInStreamPresentationTimeUs = C.TIME_UNSET; + lastProcessedOutputBufferTimeUs = C.TIME_UNSET; codecDrainState = DRAIN_STATE_NONE; codecDrainAction = DRAIN_ACTION_NONE; } @@ -606,13 +596,15 @@ public abstract class MediaCodecRenderer extends BaseRenderer { protected final void updateOutputFormatForTime(long presentationTimeUs) throws ExoPlaybackException { boolean outputFormatChanged = false; - @Nullable Format format = formatQueue.pollFloor(presentationTimeUs); - if (format == null && codecOutputMediaFormatChanged) { - // If the codec's output MediaFormat has changed then there should be a corresponding Format - // change, which we've not found. Check the Format queue in case the corresponding - // presentation timestamp is greater than presentationTimeUs, which can happen for some codecs - // [Internal ref: b/162719047]. - format = formatQueue.pollFirst(); + @Nullable Format format = outputStreamInfo.formatQueue.pollFloor(presentationTimeUs); + if (format == null + && needToNotifyOutputFormatChangeAfterStreamChange + && codecOutputMediaFormat != null) { + // After a stream change or after the initial start, there should be an input format change, + // which we've not found. Check the Format queue in case the corresponding presentation + // timestamp is greater than presentationTimeUs, which can happen for some codecs + // [Internal ref: b/162719047 and https://github.com/google/ExoPlayer/issues/8594]. + format = outputStreamInfo.formatQueue.pollFirst(); } if (format != null) { outputFormat = format; @@ -621,6 +613,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { if (outputFormatChanged || (codecOutputMediaFormatChanged && outputFormat != null)) { onOutputFormatChanged(outputFormat, codecOutputMediaFormat); codecOutputMediaFormatChanged = false; + needToNotifyOutputFormatChangeAfterStreamChange = false; } } @@ -648,23 +641,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Override protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs) throws ExoPlaybackException { - if (this.outputStreamOffsetUs == C.TIME_UNSET) { - checkState(this.outputStreamStartPositionUs == C.TIME_UNSET); - this.outputStreamStartPositionUs = startPositionUs; - setOutputStreamOffsetUs(offsetUs); + if (outputStreamInfo.streamOffsetUs == C.TIME_UNSET + || (pendingOutputStreamChanges.isEmpty() + && lastProcessedOutputBufferTimeUs != C.TIME_UNSET + && lastProcessedOutputBufferTimeUs >= largestQueuedPresentationTimeUs)) { + // This is the first stream, or the previous has been fully output already. + setOutputStreamInfo( + new OutputStreamInfo( + /* previousStreamLastBufferTimeUs= */ C.TIME_UNSET, startPositionUs, offsetUs)); } else { - if (pendingOutputStreamOffsetCount == pendingOutputStreamOffsetsUs.length) { - Log.w( - TAG, - "Too many stream changes, so dropping offset: " - + pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1]); - } else { - pendingOutputStreamOffsetCount++; - } - pendingOutputStreamStartPositionsUs[pendingOutputStreamOffsetCount - 1] = startPositionUs; - pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1] = offsetUs; - pendingOutputStreamSwitchTimesUs[pendingOutputStreamOffsetCount - 1] = - largestQueuedPresentationTimeUs; + pendingOutputStreamChanges.add( + new OutputStreamInfo(largestQueuedPresentationTimeUs, startPositionUs, offsetUs)); } } @@ -683,16 +670,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer { // If there is a format change on the input side still pending propagation to the output, we // need to queue a format next time a buffer is read. This is because we may not read a new // input format after the position reset. - if (formatQueue.size() > 0) { + if (outputStreamInfo.formatQueue.size() > 0) { waitingForFirstSampleInFormat = true; } - formatQueue.clear(); - if (pendingOutputStreamOffsetCount != 0) { - setOutputStreamOffsetUs(pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1]); - outputStreamStartPositionUs = - pendingOutputStreamStartPositionsUs[pendingOutputStreamOffsetCount - 1]; - pendingOutputStreamOffsetCount = 0; - } + outputStreamInfo.formatQueue.clear(); + pendingOutputStreamChanges.clear(); } @Override @@ -706,9 +688,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer { @Override protected void onDisabled() { inputFormat = null; - outputStreamStartPositionUs = C.TIME_UNSET; - setOutputStreamOffsetUs(C.TIME_UNSET); - pendingOutputStreamOffsetCount = 0; + setOutputStreamInfo(OutputStreamInfo.UNSET); + pendingOutputStreamChanges.clear(); flushOrReleaseCodec(); } @@ -897,6 +878,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { decodeOnlyPresentationTimestamps.clear(); largestQueuedPresentationTimeUs = C.TIME_UNSET; lastBufferInStreamPresentationTimeUs = C.TIME_UNSET; + lastProcessedOutputBufferTimeUs = C.TIME_UNSET; if (c2Mp3TimestampTracker != null) { c2Mp3TimestampTracker.reset(); } @@ -1353,7 +1335,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer { decodeOnlyPresentationTimestamps.add(presentationTimeUs); } if (waitingForFirstSampleInFormat) { - formatQueue.add(presentationTimeUs, inputFormat); + if (!pendingOutputStreamChanges.isEmpty()) { + pendingOutputStreamChanges.peekLast().formatQueue.add(presentationTimeUs, inputFormat); + } else { + outputStreamInfo.formatQueue.add(presentationTimeUs, inputFormat); + } waitingForFirstSampleInFormat = false; } largestQueuedPresentationTimeUs = max(largestQueuedPresentationTimeUs, presentationTimeUs); @@ -1593,29 +1579,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer { */ @CallSuper protected void onProcessedOutputBuffer(long presentationTimeUs) { - while (pendingOutputStreamOffsetCount != 0 - && presentationTimeUs >= pendingOutputStreamSwitchTimesUs[0]) { - outputStreamStartPositionUs = pendingOutputStreamStartPositionsUs[0]; - setOutputStreamOffsetUs(pendingOutputStreamOffsetsUs[0]); - pendingOutputStreamOffsetCount--; - System.arraycopy( - pendingOutputStreamStartPositionsUs, - /* srcPos= */ 1, - pendingOutputStreamStartPositionsUs, - /* destPos= */ 0, - pendingOutputStreamOffsetCount); - System.arraycopy( - pendingOutputStreamOffsetsUs, - /* srcPos= */ 1, - pendingOutputStreamOffsetsUs, - /* destPos= */ 0, - pendingOutputStreamOffsetCount); - System.arraycopy( - pendingOutputStreamSwitchTimesUs, - /* srcPos= */ 1, - pendingOutputStreamSwitchTimesUs, - /* destPos= */ 0, - pendingOutputStreamOffsetCount); + lastProcessedOutputBufferTimeUs = presentationTimeUs; + if (!pendingOutputStreamChanges.isEmpty() + && presentationTimeUs >= pendingOutputStreamChanges.peek().previousStreamLastBufferTimeUs) { + setOutputStreamInfo(pendingOutputStreamChanges.poll()); onProcessedStreamChange(); } } @@ -2062,13 +2029,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer { * boolean, Format)} to get the playback position with respect to the media. */ protected final long getOutputStreamOffsetUs() { - return outputStreamOffsetUs; + return outputStreamInfo.streamOffsetUs; } - private void setOutputStreamOffsetUs(long outputStreamOffsetUs) { - this.outputStreamOffsetUs = outputStreamOffsetUs; - if (outputStreamOffsetUs != C.TIME_UNSET) { - onOutputStreamOffsetUsChanged(outputStreamOffsetUs); + private void setOutputStreamInfo(OutputStreamInfo outputStreamInfo) { + this.outputStreamInfo = outputStreamInfo; + if (outputStreamInfo.streamOffsetUs != C.TIME_UNSET) { + needToNotifyOutputFormatChangeAfterStreamChange = true; + onOutputStreamOffsetUsChanged(outputStreamInfo.streamOffsetUs); } } @@ -2515,6 +2483,28 @@ public abstract class MediaCodecRenderer extends BaseRenderer { && "OMX.MTK.AUDIO.DECODER.MP3".equals(name); } + private static final class OutputStreamInfo { + + public static final OutputStreamInfo UNSET = + new OutputStreamInfo( + /* previousStreamLastBufferTimeUs= */ C.TIME_UNSET, + /* startPositionUs= */ C.TIME_UNSET, + /* streamOffsetUs= */ C.TIME_UNSET); + + public final long previousStreamLastBufferTimeUs; + public final long startPositionUs; + public final long streamOffsetUs; + public final TimedValueQueue formatQueue; + + public OutputStreamInfo( + long previousStreamLastBufferTimeUs, long startPositionUs, long streamOffsetUs) { + this.previousStreamLastBufferTimeUs = previousStreamLastBufferTimeUs; + this.startPositionUs = startPositionUs; + this.streamOffsetUs = streamOffsetUs; + this.formatQueue = new TimedValueQueue<>(); + } + } + @RequiresApi(31) private static final class Api31 { private Api31() {} diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java index e97e053084..81af112a67 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java @@ -252,7 +252,7 @@ public final class MediaCodecUtil { return getVp9ProfileAndLevel(format.codecs, parts); case CODEC_ID_HEV1: case CODEC_ID_HVC1: - return getHevcProfileAndLevel(format.codecs, parts); + return getHevcProfileAndLevel(format.codecs, parts, format.colorInfo); case CODEC_ID_AV01: return getAv1ProfileAndLevel(format.codecs, parts, format.colorInfo); case CODEC_ID_MP4A: @@ -731,7 +731,8 @@ public final class MediaCodecUtil { } @Nullable - private static Pair getHevcProfileAndLevel(String codec, String[] parts) { + private static Pair getHevcProfileAndLevel( + String codec, String[] parts, @Nullable ColorInfo colorInfo) { if (parts.length < 4) { // The codec has fewer parts than required by the HEVC codec string format. Log.w(TAG, "Ignoring malformed HEVC codec string: " + codec); @@ -748,7 +749,15 @@ public final class MediaCodecUtil { if ("1".equals(profileString)) { profile = CodecProfileLevel.HEVCProfileMain; } else if ("2".equals(profileString)) { - profile = CodecProfileLevel.HEVCProfileMain10; + if (colorInfo != null && colorInfo.colorTransfer == C.COLOR_TRANSFER_ST2084) { + profile = CodecProfileLevel.HEVCProfileMain10HDR10; + } else { + // For all other cases, we map to the Main10 profile. Note that this includes HLG + // HDR. On Android 13+, the platform guarantees that a decoder that advertises + // HEVCProfileMain10 will be able to decode HLG. This is not guaranteed for older + // Android versions, but we still map to Main10 for backwards compatibility. + profile = CodecProfileLevel.HEVCProfileMain10; + } } else { Log.w(TAG, "Unknown HEVC profile string: " + profileString); return null; diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DefaultDownloaderFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DefaultDownloaderFactory.java index 437490eea3..1fee9d80cb 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DefaultDownloaderFactory.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DefaultDownloaderFactory.java @@ -105,7 +105,7 @@ public class DefaultDownloaderFactory implements DownloaderFactory { return constructor.newInstance(mediaItem, cacheDataSourceFactory, executor); } catch (Exception e) { throw new IllegalStateException( - "Failed to instantiate downloader for content type " + contentType); + "Failed to instantiate downloader for content type " + contentType, e); } } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/SegmentDownloader.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/SegmentDownloader.java index d4b8ece726..7a8ac0ba2b 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/SegmentDownloader.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/SegmentDownloader.java @@ -75,8 +75,9 @@ public abstract class SegmentDownloader> impleme } } + public static final long DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS = 20 * C.MILLIS_PER_SECOND; + private static final int BUFFER_SIZE_BYTES = 128 * 1024; - private static final long MAX_MERGED_SEGMENT_START_TIME_DIFF_US = 20 * C.MICROS_PER_SECOND; private final DataSpec manifestDataSpec; private final Parser manifestParser; @@ -86,6 +87,7 @@ public abstract class SegmentDownloader> impleme private final CacheKeyFactory cacheKeyFactory; @Nullable private final PriorityTaskManager priorityTaskManager; private final Executor executor; + private final long maxMergedSegmentStartTimeDiffUs; /** * The currently active runnables. @@ -99,6 +101,24 @@ public abstract class SegmentDownloader> impleme private volatile boolean isCanceled; + /** + * @deprecated Use {@link SegmentDownloader#SegmentDownloader(MediaItem, Parser, + * CacheDataSource.Factory, Executor, long)} instead. + */ + @Deprecated + public SegmentDownloader( + MediaItem mediaItem, + Parser manifestParser, + CacheDataSource.Factory cacheDataSourceFactory, + Executor executor) { + this( + mediaItem, + manifestParser, + cacheDataSourceFactory, + executor, + DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS); + } + /** * @param mediaItem The {@link MediaItem} to be downloaded. * @param manifestParser A parser for manifests belonging to the media to be downloaded. @@ -107,12 +127,16 @@ public abstract class SegmentDownloader> impleme * @param executor An {@link Executor} used to make requests for the media being downloaded. * Providing an {@link Executor} that uses multiple threads will speed up the download by * allowing parts of it to be executed in parallel. + * @param maxMergedSegmentStartTimeDiffMs The maximum difference of the start time of two + * segments, up to which the segments (of the same URI) should be merged into a single + * download segment, in milliseconds. */ public SegmentDownloader( MediaItem mediaItem, Parser manifestParser, CacheDataSource.Factory cacheDataSourceFactory, - Executor executor) { + Executor executor, + long maxMergedSegmentStartTimeDiffMs) { checkNotNull(mediaItem.localConfiguration); this.manifestDataSpec = getCompressibleDataSpec(mediaItem.localConfiguration.uri); this.manifestParser = manifestParser; @@ -123,6 +147,7 @@ public abstract class SegmentDownloader> impleme cacheKeyFactory = cacheDataSourceFactory.getCacheKeyFactory(); priorityTaskManager = cacheDataSourceFactory.getUpstreamPriorityTaskManager(); activeRunnables = new ArrayList<>(); + maxMergedSegmentStartTimeDiffUs = Util.msToUs(maxMergedSegmentStartTimeDiffMs); } @Override @@ -145,7 +170,7 @@ public abstract class SegmentDownloader> impleme // Sort the segments so that we download media in the right order from the start of the // content, and merge segments where possible to minimize the number of server round trips. Collections.sort(segments); - mergeSegments(segments, cacheKeyFactory); + mergeSegments(segments, cacheKeyFactory, maxMergedSegmentStartTimeDiffUs); // Scan the segments, removing any that are fully downloaded. int totalSegments = segments.size(); @@ -416,7 +441,8 @@ public abstract class SegmentDownloader> impleme } } - private static void mergeSegments(List segments, CacheKeyFactory keyFactory) { + private static void mergeSegments( + List segments, CacheKeyFactory keyFactory, long maxMergedSegmentStartTimeDiffUs) { HashMap lastIndexByCacheKey = new HashMap<>(); int nextOutIndex = 0; for (int i = 0; i < segments.size(); i++) { @@ -425,7 +451,7 @@ public abstract class SegmentDownloader> impleme @Nullable Integer lastIndex = lastIndexByCacheKey.get(cacheKey); @Nullable Segment lastSegment = lastIndex == null ? null : segments.get(lastIndex); if (lastSegment == null - || segment.startTimeUs > lastSegment.startTimeUs + MAX_MERGED_SEGMENT_START_TIME_DIFF_US + || segment.startTimeUs > lastSegment.startTimeUs + maxMergedSegmentStartTimeDiffUs || !canMergeSegments(lastSegment.dataSpec, segment.dataSpec)) { lastIndexByCacheKey.put(cacheKey, nextOutIndex); segments.set(nextOutIndex, segment); diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java index ab87aa361c..53ad113710 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java @@ -184,8 +184,8 @@ public final class Requirements implements Parcelable { private boolean isDeviceCharging(Context context) { @Nullable Intent batteryStatus = - Util.registerReceiverNotExported( - context, /* receiver= */ null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); + context.registerReceiver( + /* receiver= */ null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); if (batteryStatus == null) { return false; } @@ -203,8 +203,8 @@ public final class Requirements implements Parcelable { } private boolean isStorageNotLow(Context context) { - return Util.registerReceiverNotExported( - context, /* receiver= */ null, new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW)) + return context.registerReceiver( + /* receiver= */ null, new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW)) == null; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java index 541224221c..d214bc95af 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java @@ -111,7 +111,7 @@ public final class RequirementsWatcher { filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); } receiver = new DeviceStatusChangeReceiver(); - Util.registerReceiverNotExported(context, receiver, filter, handler); + context.registerReceiver(receiver, filter, /* broadcastPermission= */ null, handler); return notMetRequirements; } diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/SlidingPercentile.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/SlidingPercentile.java index ca3d0fadbc..b555b3c40f 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/SlidingPercentile.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/SlidingPercentile.java @@ -30,8 +30,12 @@ import java.util.Comparator; * rate observations. This is an alternative to sliding mean and exponential averaging which suffer * from susceptibility to outliers and slow adaptation to step functions. * - * @see Wiki: Moving average - * @see Wiki: Selection algorithm + *

See the following Wikipedia articles: + * + *

*/ @UnstableApi public class SlidingPercentile { diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java index 1bd45fc24a..6b9d4b567d 100644 --- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java +++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java @@ -153,6 +153,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { private long lastRenderRealtimeUs; private long totalVideoFrameProcessingOffsetUs; private int videoFrameProcessingOffsetCount; + private long lastFrameReleaseTimeNs; private int currentWidth; private int currentHeight; @@ -1128,9 +1129,18 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { if (Util.SDK_INT >= 21) { // Let the underlying framework time the release. if (earlyUs < 50000) { - notifyFrameMetadataListener(presentationTimeUs, adjustedReleaseTimeNs, format); - renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, adjustedReleaseTimeNs); + if (adjustedReleaseTimeNs == lastFrameReleaseTimeNs) { + // This frame should be displayed on the same vsync with the previous released frame. We + // are likely rendering frames at a rate higher than the screen refresh rate. Skip + // this buffer so that it's returned to MediaCodec sooner otherwise MediaCodec may not + // be able to keep decoding with this rate [b/263454203]. + skipOutputBuffer(codec, bufferIndex, presentationTimeUs); + } else { + notifyFrameMetadataListener(presentationTimeUs, adjustedReleaseTimeNs, format); + renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, adjustedReleaseTimeNs); + } updateVideoFrameProcessingOffsetCounters(earlyUs); + lastFrameReleaseTimeNs = adjustedReleaseTimeNs; return true; } } else { diff --git a/libraries/exoplayer/src/main/res/values-af/strings.xml b/libraries/exoplayer/src/main/res/values-af/strings.xml index c1a57f9246..b935a4cf89 100644 --- a/libraries/exoplayer/src/main/res/values-af/strings.xml +++ b/libraries/exoplayer/src/main/res/values-af/strings.xml @@ -1,18 +1,4 @@ - Aflaai Aflaaie diff --git a/libraries/exoplayer/src/main/res/values-am/strings.xml b/libraries/exoplayer/src/main/res/values-am/strings.xml index a16ac064b3..93a8ae066b 100644 --- a/libraries/exoplayer/src/main/res/values-am/strings.xml +++ b/libraries/exoplayer/src/main/res/values-am/strings.xml @@ -1,18 +1,4 @@ - አውርድ የወረዱ diff --git a/libraries/exoplayer/src/main/res/values-ar/strings.xml b/libraries/exoplayer/src/main/res/values-ar/strings.xml index 6b03d0fdd6..14db130622 100644 --- a/libraries/exoplayer/src/main/res/values-ar/strings.xml +++ b/libraries/exoplayer/src/main/res/values-ar/strings.xml @@ -1,18 +1,4 @@ - تنزيل عمليات التنزيل diff --git a/libraries/exoplayer/src/main/res/values-az/strings.xml b/libraries/exoplayer/src/main/res/values-az/strings.xml index 1193607b28..28e9dcb010 100644 --- a/libraries/exoplayer/src/main/res/values-az/strings.xml +++ b/libraries/exoplayer/src/main/res/values-az/strings.xml @@ -1,18 +1,4 @@ - Endirin Endirmələr diff --git a/libraries/exoplayer/src/main/res/values-b+sr+Latn/strings.xml b/libraries/exoplayer/src/main/res/values-b+sr+Latn/strings.xml index 49d5ff23c4..c8e8eeb786 100644 --- a/libraries/exoplayer/src/main/res/values-b+sr+Latn/strings.xml +++ b/libraries/exoplayer/src/main/res/values-b+sr+Latn/strings.xml @@ -1,18 +1,4 @@ - Preuzmi Preuzimanja diff --git a/libraries/exoplayer/src/main/res/values-be/strings.xml b/libraries/exoplayer/src/main/res/values-be/strings.xml index 54b219e042..1539008d72 100644 --- a/libraries/exoplayer/src/main/res/values-be/strings.xml +++ b/libraries/exoplayer/src/main/res/values-be/strings.xml @@ -1,18 +1,4 @@ - Спампаваць Спампоўкі diff --git a/libraries/exoplayer/src/main/res/values-bg/strings.xml b/libraries/exoplayer/src/main/res/values-bg/strings.xml index 420fa00b97..af37d82078 100644 --- a/libraries/exoplayer/src/main/res/values-bg/strings.xml +++ b/libraries/exoplayer/src/main/res/values-bg/strings.xml @@ -1,18 +1,4 @@ - Изтегляне Изтегляния diff --git a/libraries/exoplayer/src/main/res/values-bn/strings.xml b/libraries/exoplayer/src/main/res/values-bn/strings.xml index c4a6c3686e..be8ed9cdaa 100644 --- a/libraries/exoplayer/src/main/res/values-bn/strings.xml +++ b/libraries/exoplayer/src/main/res/values-bn/strings.xml @@ -1,18 +1,4 @@ - ডাউনলোড করুন ডাউনলোড diff --git a/libraries/exoplayer/src/main/res/values-bs/strings.xml b/libraries/exoplayer/src/main/res/values-bs/strings.xml index 5ca81f2780..5fe387cefb 100644 --- a/libraries/exoplayer/src/main/res/values-bs/strings.xml +++ b/libraries/exoplayer/src/main/res/values-bs/strings.xml @@ -1,18 +1,4 @@ - Preuzmi Preuzimanja diff --git a/libraries/exoplayer/src/main/res/values-ca/strings.xml b/libraries/exoplayer/src/main/res/values-ca/strings.xml index 6c80546d7b..ecb381f534 100644 --- a/libraries/exoplayer/src/main/res/values-ca/strings.xml +++ b/libraries/exoplayer/src/main/res/values-ca/strings.xml @@ -1,18 +1,4 @@ - Baixa Baixades diff --git a/libraries/exoplayer/src/main/res/values-cs/strings.xml b/libraries/exoplayer/src/main/res/values-cs/strings.xml index f916f076e3..54a112a9ee 100644 --- a/libraries/exoplayer/src/main/res/values-cs/strings.xml +++ b/libraries/exoplayer/src/main/res/values-cs/strings.xml @@ -1,18 +1,4 @@ - Stáhnout Stahování diff --git a/libraries/exoplayer/src/main/res/values-da/strings.xml b/libraries/exoplayer/src/main/res/values-da/strings.xml index dca960dbd4..1b15b1f605 100644 --- a/libraries/exoplayer/src/main/res/values-da/strings.xml +++ b/libraries/exoplayer/src/main/res/values-da/strings.xml @@ -1,18 +1,4 @@ - Download Downloads diff --git a/libraries/exoplayer/src/main/res/values-de/strings.xml b/libraries/exoplayer/src/main/res/values-de/strings.xml index 92904131e1..848bfb2d4d 100644 --- a/libraries/exoplayer/src/main/res/values-de/strings.xml +++ b/libraries/exoplayer/src/main/res/values-de/strings.xml @@ -1,18 +1,4 @@ - Herunterladen Downloads diff --git a/libraries/exoplayer/src/main/res/values-el/strings.xml b/libraries/exoplayer/src/main/res/values-el/strings.xml index e079173d12..8ef5c20f72 100644 --- a/libraries/exoplayer/src/main/res/values-el/strings.xml +++ b/libraries/exoplayer/src/main/res/values-el/strings.xml @@ -1,18 +1,4 @@ - Λήψη Λήψεις diff --git a/libraries/exoplayer/src/main/res/values-en-rAU/strings.xml b/libraries/exoplayer/src/main/res/values-en-rAU/strings.xml index 27fb6389f8..639a151801 100644 --- a/libraries/exoplayer/src/main/res/values-en-rAU/strings.xml +++ b/libraries/exoplayer/src/main/res/values-en-rAU/strings.xml @@ -1,18 +1,4 @@ - Download Downloads diff --git a/libraries/exoplayer/src/main/res/values-en-rGB/strings.xml b/libraries/exoplayer/src/main/res/values-en-rGB/strings.xml index 27fb6389f8..639a151801 100644 --- a/libraries/exoplayer/src/main/res/values-en-rGB/strings.xml +++ b/libraries/exoplayer/src/main/res/values-en-rGB/strings.xml @@ -1,18 +1,4 @@ - Download Downloads diff --git a/libraries/exoplayer/src/main/res/values-en-rIN/strings.xml b/libraries/exoplayer/src/main/res/values-en-rIN/strings.xml index 27fb6389f8..639a151801 100644 --- a/libraries/exoplayer/src/main/res/values-en-rIN/strings.xml +++ b/libraries/exoplayer/src/main/res/values-en-rIN/strings.xml @@ -1,18 +1,4 @@ - Download Downloads diff --git a/libraries/exoplayer/src/main/res/values-es-rUS/strings.xml b/libraries/exoplayer/src/main/res/values-es-rUS/strings.xml index 45b331f477..545c55ac86 100644 --- a/libraries/exoplayer/src/main/res/values-es-rUS/strings.xml +++ b/libraries/exoplayer/src/main/res/values-es-rUS/strings.xml @@ -1,18 +1,4 @@ - Descargar Descargas diff --git a/libraries/exoplayer/src/main/res/values-es/strings.xml b/libraries/exoplayer/src/main/res/values-es/strings.xml index 6d13c5fd21..a7a8edeaba 100644 --- a/libraries/exoplayer/src/main/res/values-es/strings.xml +++ b/libraries/exoplayer/src/main/res/values-es/strings.xml @@ -1,18 +1,4 @@ - Descargar Descargas diff --git a/libraries/exoplayer/src/main/res/values-et/strings.xml b/libraries/exoplayer/src/main/res/values-et/strings.xml index 67df50e267..5ac51b75cb 100644 --- a/libraries/exoplayer/src/main/res/values-et/strings.xml +++ b/libraries/exoplayer/src/main/res/values-et/strings.xml @@ -1,18 +1,4 @@ - Allalaadimine Allalaadimised diff --git a/libraries/exoplayer/src/main/res/values-eu/strings.xml b/libraries/exoplayer/src/main/res/values-eu/strings.xml index e2e0ca7b9e..d476d29b7b 100644 --- a/libraries/exoplayer/src/main/res/values-eu/strings.xml +++ b/libraries/exoplayer/src/main/res/values-eu/strings.xml @@ -1,18 +1,4 @@ - Deskargak Deskargak diff --git a/libraries/exoplayer/src/main/res/values-fa/strings.xml b/libraries/exoplayer/src/main/res/values-fa/strings.xml index ad7b14e934..271919fbc1 100644 --- a/libraries/exoplayer/src/main/res/values-fa/strings.xml +++ b/libraries/exoplayer/src/main/res/values-fa/strings.xml @@ -1,18 +1,4 @@ - بارگیری بارگیری‌ها diff --git a/libraries/exoplayer/src/main/res/values-fi/strings.xml b/libraries/exoplayer/src/main/res/values-fi/strings.xml index 6fbdf45fac..15f1a38628 100644 --- a/libraries/exoplayer/src/main/res/values-fi/strings.xml +++ b/libraries/exoplayer/src/main/res/values-fi/strings.xml @@ -1,18 +1,4 @@ - Lataa Lataukset diff --git a/libraries/exoplayer/src/main/res/values-fr-rCA/strings.xml b/libraries/exoplayer/src/main/res/values-fr-rCA/strings.xml index 9610fd2e14..e177a5d753 100644 --- a/libraries/exoplayer/src/main/res/values-fr-rCA/strings.xml +++ b/libraries/exoplayer/src/main/res/values-fr-rCA/strings.xml @@ -1,18 +1,4 @@ - Télécharger Téléchargements diff --git a/libraries/exoplayer/src/main/res/values-fr/strings.xml b/libraries/exoplayer/src/main/res/values-fr/strings.xml index 22f0299976..274278bf4a 100644 --- a/libraries/exoplayer/src/main/res/values-fr/strings.xml +++ b/libraries/exoplayer/src/main/res/values-fr/strings.xml @@ -1,18 +1,4 @@ - Télécharger Téléchargements diff --git a/libraries/exoplayer/src/main/res/values-gl/strings.xml b/libraries/exoplayer/src/main/res/values-gl/strings.xml index 6764ddb4d3..46f80721b5 100644 --- a/libraries/exoplayer/src/main/res/values-gl/strings.xml +++ b/libraries/exoplayer/src/main/res/values-gl/strings.xml @@ -1,18 +1,4 @@ - Descargar Descargas diff --git a/libraries/exoplayer/src/main/res/values-gu/strings.xml b/libraries/exoplayer/src/main/res/values-gu/strings.xml index 27821e2299..5d2b5874e5 100644 --- a/libraries/exoplayer/src/main/res/values-gu/strings.xml +++ b/libraries/exoplayer/src/main/res/values-gu/strings.xml @@ -1,18 +1,4 @@ - ડાઉનલોડ કરો ડાઉનલોડ diff --git a/libraries/exoplayer/src/main/res/values-hi/strings.xml b/libraries/exoplayer/src/main/res/values-hi/strings.xml index d839e3f3fa..17a17adcdd 100644 --- a/libraries/exoplayer/src/main/res/values-hi/strings.xml +++ b/libraries/exoplayer/src/main/res/values-hi/strings.xml @@ -1,18 +1,4 @@ - डाउनलोड करें डाउनलोड की गई मीडिया फ़ाइलें diff --git a/libraries/exoplayer/src/main/res/values-hr/strings.xml b/libraries/exoplayer/src/main/res/values-hr/strings.xml index 031e629fd8..05d7cfa448 100644 --- a/libraries/exoplayer/src/main/res/values-hr/strings.xml +++ b/libraries/exoplayer/src/main/res/values-hr/strings.xml @@ -1,18 +1,4 @@ - Preuzmi Preuzimanja diff --git a/libraries/exoplayer/src/main/res/values-hu/strings.xml b/libraries/exoplayer/src/main/res/values-hu/strings.xml index e511a69594..d19171d975 100644 --- a/libraries/exoplayer/src/main/res/values-hu/strings.xml +++ b/libraries/exoplayer/src/main/res/values-hu/strings.xml @@ -1,18 +1,4 @@ - Letöltés Letöltések diff --git a/libraries/exoplayer/src/main/res/values-hy/strings.xml b/libraries/exoplayer/src/main/res/values-hy/strings.xml index 61d61e8704..4fc4e86bcb 100644 --- a/libraries/exoplayer/src/main/res/values-hy/strings.xml +++ b/libraries/exoplayer/src/main/res/values-hy/strings.xml @@ -1,18 +1,4 @@ - Ներբեռնել Ներբեռնումներ diff --git a/libraries/exoplayer/src/main/res/values-in/strings.xml b/libraries/exoplayer/src/main/res/values-in/strings.xml index ce650eb479..e20aac9971 100644 --- a/libraries/exoplayer/src/main/res/values-in/strings.xml +++ b/libraries/exoplayer/src/main/res/values-in/strings.xml @@ -1,18 +1,4 @@ - Download Download diff --git a/libraries/exoplayer/src/main/res/values-is/strings.xml b/libraries/exoplayer/src/main/res/values-is/strings.xml index 713dbc50b8..6c62e12ee4 100644 --- a/libraries/exoplayer/src/main/res/values-is/strings.xml +++ b/libraries/exoplayer/src/main/res/values-is/strings.xml @@ -1,18 +1,4 @@ - Sækja Niðurhal diff --git a/libraries/exoplayer/src/main/res/values-it/strings.xml b/libraries/exoplayer/src/main/res/values-it/strings.xml index 709bb8e5b4..03a7a64d1a 100644 --- a/libraries/exoplayer/src/main/res/values-it/strings.xml +++ b/libraries/exoplayer/src/main/res/values-it/strings.xml @@ -1,18 +1,4 @@ - Scarica Download diff --git a/libraries/exoplayer/src/main/res/values-iw/strings.xml b/libraries/exoplayer/src/main/res/values-iw/strings.xml index dadb93b016..41010d65f8 100644 --- a/libraries/exoplayer/src/main/res/values-iw/strings.xml +++ b/libraries/exoplayer/src/main/res/values-iw/strings.xml @@ -1,18 +1,4 @@ - הורדה הורדות diff --git a/libraries/exoplayer/src/main/res/values-ja/strings.xml b/libraries/exoplayer/src/main/res/values-ja/strings.xml index b4df8db16c..f32b0bfc75 100644 --- a/libraries/exoplayer/src/main/res/values-ja/strings.xml +++ b/libraries/exoplayer/src/main/res/values-ja/strings.xml @@ -1,18 +1,4 @@ - ダウンロード ダウンロード diff --git a/libraries/exoplayer/src/main/res/values-ka/strings.xml b/libraries/exoplayer/src/main/res/values-ka/strings.xml index 34fa93ac36..2460d0c4b7 100644 --- a/libraries/exoplayer/src/main/res/values-ka/strings.xml +++ b/libraries/exoplayer/src/main/res/values-ka/strings.xml @@ -1,18 +1,4 @@ - ჩამოტვირთვა ჩამოტვირთვები diff --git a/libraries/exoplayer/src/main/res/values-kk/strings.xml b/libraries/exoplayer/src/main/res/values-kk/strings.xml index a0e7f49af1..616fb42af5 100644 --- a/libraries/exoplayer/src/main/res/values-kk/strings.xml +++ b/libraries/exoplayer/src/main/res/values-kk/strings.xml @@ -1,18 +1,4 @@ - Жүктеп алу Жүктеп алынғандар diff --git a/libraries/exoplayer/src/main/res/values-km/strings.xml b/libraries/exoplayer/src/main/res/values-km/strings.xml index d284e7b78a..2089642a4a 100644 --- a/libraries/exoplayer/src/main/res/values-km/strings.xml +++ b/libraries/exoplayer/src/main/res/values-km/strings.xml @@ -1,18 +1,4 @@ - ទាញយក ទាញយក diff --git a/libraries/exoplayer/src/main/res/values-kn/strings.xml b/libraries/exoplayer/src/main/res/values-kn/strings.xml index c311225089..7747d1256b 100644 --- a/libraries/exoplayer/src/main/res/values-kn/strings.xml +++ b/libraries/exoplayer/src/main/res/values-kn/strings.xml @@ -1,18 +1,4 @@ - ಡೌನ್‌ಲೋಡ್‌ ಡೌನ್‌ಲೋಡ್‌ಗಳು diff --git a/libraries/exoplayer/src/main/res/values-ko/strings.xml b/libraries/exoplayer/src/main/res/values-ko/strings.xml index 3bbdd2cb6c..a87dce92cc 100644 --- a/libraries/exoplayer/src/main/res/values-ko/strings.xml +++ b/libraries/exoplayer/src/main/res/values-ko/strings.xml @@ -1,18 +1,4 @@ - 다운로드 다운로드 diff --git a/libraries/exoplayer/src/main/res/values-ky/strings.xml b/libraries/exoplayer/src/main/res/values-ky/strings.xml index 8cf2921bc3..6ce6949e2b 100644 --- a/libraries/exoplayer/src/main/res/values-ky/strings.xml +++ b/libraries/exoplayer/src/main/res/values-ky/strings.xml @@ -1,18 +1,4 @@ - Жүктөп алуу Жүктөлүп алынгандар diff --git a/libraries/exoplayer/src/main/res/values-lo/strings.xml b/libraries/exoplayer/src/main/res/values-lo/strings.xml index 0b7f314f91..1ff16ef04f 100644 --- a/libraries/exoplayer/src/main/res/values-lo/strings.xml +++ b/libraries/exoplayer/src/main/res/values-lo/strings.xml @@ -1,18 +1,4 @@ - ດາວໂຫລດ ດາວໂຫລດ diff --git a/libraries/exoplayer/src/main/res/values-lt/strings.xml b/libraries/exoplayer/src/main/res/values-lt/strings.xml index afc749c88a..c81cdd1092 100644 --- a/libraries/exoplayer/src/main/res/values-lt/strings.xml +++ b/libraries/exoplayer/src/main/res/values-lt/strings.xml @@ -1,18 +1,4 @@ - Atsisiųsti Atsisiuntimai diff --git a/libraries/exoplayer/src/main/res/values-lv/strings.xml b/libraries/exoplayer/src/main/res/values-lv/strings.xml index 1899c273e4..0213b5e062 100644 --- a/libraries/exoplayer/src/main/res/values-lv/strings.xml +++ b/libraries/exoplayer/src/main/res/values-lv/strings.xml @@ -1,18 +1,4 @@ - Lejupielādēt Lejupielādes diff --git a/libraries/exoplayer/src/main/res/values-mk/strings.xml b/libraries/exoplayer/src/main/res/values-mk/strings.xml index 58a1de488a..6e1c3aecfc 100644 --- a/libraries/exoplayer/src/main/res/values-mk/strings.xml +++ b/libraries/exoplayer/src/main/res/values-mk/strings.xml @@ -1,18 +1,4 @@ - Преземи Преземања diff --git a/libraries/exoplayer/src/main/res/values-ml/strings.xml b/libraries/exoplayer/src/main/res/values-ml/strings.xml index 5c75164da8..2e0653da43 100644 --- a/libraries/exoplayer/src/main/res/values-ml/strings.xml +++ b/libraries/exoplayer/src/main/res/values-ml/strings.xml @@ -1,18 +1,4 @@ - ഡൗൺലോഡ് ഡൗൺലോഡുകൾ diff --git a/libraries/exoplayer/src/main/res/values-mn/strings.xml b/libraries/exoplayer/src/main/res/values-mn/strings.xml index 6e7aac5dc9..95b30da3ce 100644 --- a/libraries/exoplayer/src/main/res/values-mn/strings.xml +++ b/libraries/exoplayer/src/main/res/values-mn/strings.xml @@ -1,18 +1,4 @@ - Татах Татaлт diff --git a/libraries/exoplayer/src/main/res/values-mr/strings.xml b/libraries/exoplayer/src/main/res/values-mr/strings.xml index 0d98194d6a..0c37597dda 100644 --- a/libraries/exoplayer/src/main/res/values-mr/strings.xml +++ b/libraries/exoplayer/src/main/res/values-mr/strings.xml @@ -1,18 +1,4 @@ - डाउनलोड करा डाउनलोड diff --git a/libraries/exoplayer/src/main/res/values-ms/strings.xml b/libraries/exoplayer/src/main/res/values-ms/strings.xml index f4d08d49d3..3033c3c93f 100644 --- a/libraries/exoplayer/src/main/res/values-ms/strings.xml +++ b/libraries/exoplayer/src/main/res/values-ms/strings.xml @@ -1,18 +1,4 @@ - Muat turun Muat turun diff --git a/libraries/exoplayer/src/main/res/values-my/strings.xml b/libraries/exoplayer/src/main/res/values-my/strings.xml index 9e308724ad..88294129a9 100644 --- a/libraries/exoplayer/src/main/res/values-my/strings.xml +++ b/libraries/exoplayer/src/main/res/values-my/strings.xml @@ -1,18 +1,4 @@ - ဒေါင်းလုဒ် လုပ်ရန် ဒေါင်းလုဒ်များ diff --git a/libraries/exoplayer/src/main/res/values-nb/strings.xml b/libraries/exoplayer/src/main/res/values-nb/strings.xml index 5d5b46b28c..026d9be10d 100644 --- a/libraries/exoplayer/src/main/res/values-nb/strings.xml +++ b/libraries/exoplayer/src/main/res/values-nb/strings.xml @@ -1,18 +1,4 @@ - Last ned Nedlastinger diff --git a/libraries/exoplayer/src/main/res/values-ne/strings.xml b/libraries/exoplayer/src/main/res/values-ne/strings.xml index deff7e8e41..a1a014bf7b 100644 --- a/libraries/exoplayer/src/main/res/values-ne/strings.xml +++ b/libraries/exoplayer/src/main/res/values-ne/strings.xml @@ -1,18 +1,4 @@ - डाउनलोड गर्नुहोस् डाउनलोडहरू diff --git a/libraries/exoplayer/src/main/res/values-nl/strings.xml b/libraries/exoplayer/src/main/res/values-nl/strings.xml index 589296d9a6..c8eeb8553d 100644 --- a/libraries/exoplayer/src/main/res/values-nl/strings.xml +++ b/libraries/exoplayer/src/main/res/values-nl/strings.xml @@ -1,18 +1,4 @@ - Downloaden Downloads diff --git a/libraries/exoplayer/src/main/res/values-pa/strings.xml b/libraries/exoplayer/src/main/res/values-pa/strings.xml index 86236f9e03..ee5031015d 100644 --- a/libraries/exoplayer/src/main/res/values-pa/strings.xml +++ b/libraries/exoplayer/src/main/res/values-pa/strings.xml @@ -1,18 +1,4 @@ - ਡਾਊਨਲੋਡ ਕਰੋ ਡਾਊਨਲੋਡ diff --git a/libraries/exoplayer/src/main/res/values-pl/strings.xml b/libraries/exoplayer/src/main/res/values-pl/strings.xml index 63fc39fac7..40633af74c 100644 --- a/libraries/exoplayer/src/main/res/values-pl/strings.xml +++ b/libraries/exoplayer/src/main/res/values-pl/strings.xml @@ -1,18 +1,4 @@ - Pobierz Pobieranie diff --git a/libraries/exoplayer/src/main/res/values-pt-rPT/strings.xml b/libraries/exoplayer/src/main/res/values-pt-rPT/strings.xml index 6c9f639ec9..1988ac95eb 100644 --- a/libraries/exoplayer/src/main/res/values-pt-rPT/strings.xml +++ b/libraries/exoplayer/src/main/res/values-pt-rPT/strings.xml @@ -1,18 +1,4 @@ - Transferir Transferências diff --git a/libraries/exoplayer/src/main/res/values-pt/strings.xml b/libraries/exoplayer/src/main/res/values-pt/strings.xml index c38d90d176..59889e55d5 100644 --- a/libraries/exoplayer/src/main/res/values-pt/strings.xml +++ b/libraries/exoplayer/src/main/res/values-pt/strings.xml @@ -1,18 +1,4 @@ - Fazer o download Downloads diff --git a/libraries/exoplayer/src/main/res/values-ro/strings.xml b/libraries/exoplayer/src/main/res/values-ro/strings.xml index a4a7afa1a6..56355afa1f 100644 --- a/libraries/exoplayer/src/main/res/values-ro/strings.xml +++ b/libraries/exoplayer/src/main/res/values-ro/strings.xml @@ -1,20 +1,6 @@ - - Descărcați + Descarcă Descărcări Se descarcă Descărcarea a fost finalizată diff --git a/libraries/exoplayer/src/main/res/values-ru/strings.xml b/libraries/exoplayer/src/main/res/values-ru/strings.xml index b989fd3bc1..c2b7f7f360 100644 --- a/libraries/exoplayer/src/main/res/values-ru/strings.xml +++ b/libraries/exoplayer/src/main/res/values-ru/strings.xml @@ -1,18 +1,4 @@ - Скачать Скачивания diff --git a/libraries/exoplayer/src/main/res/values-si/strings.xml b/libraries/exoplayer/src/main/res/values-si/strings.xml index 36b652c2cc..2b9b3289fd 100644 --- a/libraries/exoplayer/src/main/res/values-si/strings.xml +++ b/libraries/exoplayer/src/main/res/values-si/strings.xml @@ -1,18 +1,4 @@ - බාගන්න බාගැනීම් diff --git a/libraries/exoplayer/src/main/res/values-sk/strings.xml b/libraries/exoplayer/src/main/res/values-sk/strings.xml index fec40af4e2..4581322c61 100644 --- a/libraries/exoplayer/src/main/res/values-sk/strings.xml +++ b/libraries/exoplayer/src/main/res/values-sk/strings.xml @@ -1,18 +1,4 @@ - Stiahnuť Stiahnuté diff --git a/libraries/exoplayer/src/main/res/values-sl/strings.xml b/libraries/exoplayer/src/main/res/values-sl/strings.xml index 025ba4a0e6..460643ebb8 100644 --- a/libraries/exoplayer/src/main/res/values-sl/strings.xml +++ b/libraries/exoplayer/src/main/res/values-sl/strings.xml @@ -1,18 +1,4 @@ - Prenos Prenosi diff --git a/libraries/exoplayer/src/main/res/values-sq/strings.xml b/libraries/exoplayer/src/main/res/values-sq/strings.xml index 836ff91e41..b3e916ef4c 100644 --- a/libraries/exoplayer/src/main/res/values-sq/strings.xml +++ b/libraries/exoplayer/src/main/res/values-sq/strings.xml @@ -1,18 +1,4 @@ - Shkarko Shkarkimet diff --git a/libraries/exoplayer/src/main/res/values-sr/strings.xml b/libraries/exoplayer/src/main/res/values-sr/strings.xml index aad2287353..f537b8e38f 100644 --- a/libraries/exoplayer/src/main/res/values-sr/strings.xml +++ b/libraries/exoplayer/src/main/res/values-sr/strings.xml @@ -1,18 +1,4 @@ - Преузми Преузимања diff --git a/libraries/exoplayer/src/main/res/values-sv/strings.xml b/libraries/exoplayer/src/main/res/values-sv/strings.xml index 34ea63ab33..a645a70f8c 100644 --- a/libraries/exoplayer/src/main/res/values-sv/strings.xml +++ b/libraries/exoplayer/src/main/res/values-sv/strings.xml @@ -1,18 +1,4 @@ - Ladda ned Nedladdningar diff --git a/libraries/exoplayer/src/main/res/values-sw/strings.xml b/libraries/exoplayer/src/main/res/values-sw/strings.xml index f2ea0ebb2f..e6d112427f 100644 --- a/libraries/exoplayer/src/main/res/values-sw/strings.xml +++ b/libraries/exoplayer/src/main/res/values-sw/strings.xml @@ -1,18 +1,4 @@ - Pakua Vipakuliwa diff --git a/libraries/exoplayer/src/main/res/values-ta/strings.xml b/libraries/exoplayer/src/main/res/values-ta/strings.xml index b9dd46a955..bd9cdbf5d4 100644 --- a/libraries/exoplayer/src/main/res/values-ta/strings.xml +++ b/libraries/exoplayer/src/main/res/values-ta/strings.xml @@ -1,18 +1,4 @@ - பதிவிறக்கும் பட்டன் பதிவிறக்கங்கள் diff --git a/libraries/exoplayer/src/main/res/values-te/strings.xml b/libraries/exoplayer/src/main/res/values-te/strings.xml index fe1f48a955..cf0480ebb0 100644 --- a/libraries/exoplayer/src/main/res/values-te/strings.xml +++ b/libraries/exoplayer/src/main/res/values-te/strings.xml @@ -1,18 +1,4 @@ - డౌన్‌లోడ్ చేయి డౌన్‌లోడ్‌లు diff --git a/libraries/exoplayer/src/main/res/values-th/strings.xml b/libraries/exoplayer/src/main/res/values-th/strings.xml index c888cd3b72..73cba4d678 100644 --- a/libraries/exoplayer/src/main/res/values-th/strings.xml +++ b/libraries/exoplayer/src/main/res/values-th/strings.xml @@ -1,18 +1,4 @@ - ดาวน์โหลด ดาวน์โหลด diff --git a/libraries/exoplayer/src/main/res/values-tl/strings.xml b/libraries/exoplayer/src/main/res/values-tl/strings.xml index 3b04a687a5..4e68f6f05c 100644 --- a/libraries/exoplayer/src/main/res/values-tl/strings.xml +++ b/libraries/exoplayer/src/main/res/values-tl/strings.xml @@ -1,18 +1,4 @@ - I-download Mga Download diff --git a/libraries/exoplayer/src/main/res/values-tr/strings.xml b/libraries/exoplayer/src/main/res/values-tr/strings.xml index 2b9252ec7c..24a75afb36 100644 --- a/libraries/exoplayer/src/main/res/values-tr/strings.xml +++ b/libraries/exoplayer/src/main/res/values-tr/strings.xml @@ -1,18 +1,4 @@ - İndir İndirilenler diff --git a/libraries/exoplayer/src/main/res/values-uk/strings.xml b/libraries/exoplayer/src/main/res/values-uk/strings.xml index 7fbb8955de..2413efd778 100644 --- a/libraries/exoplayer/src/main/res/values-uk/strings.xml +++ b/libraries/exoplayer/src/main/res/values-uk/strings.xml @@ -1,18 +1,4 @@ - Завантажити Завантаження diff --git a/libraries/exoplayer/src/main/res/values-ur/strings.xml b/libraries/exoplayer/src/main/res/values-ur/strings.xml index 966f406273..b9dd50dd8a 100644 --- a/libraries/exoplayer/src/main/res/values-ur/strings.xml +++ b/libraries/exoplayer/src/main/res/values-ur/strings.xml @@ -1,18 +1,4 @@ - ڈاؤن لوڈ کریں ڈاؤن لوڈز diff --git a/libraries/exoplayer/src/main/res/values-uz/strings.xml b/libraries/exoplayer/src/main/res/values-uz/strings.xml index 85aaf73a8c..21e3ed45d5 100644 --- a/libraries/exoplayer/src/main/res/values-uz/strings.xml +++ b/libraries/exoplayer/src/main/res/values-uz/strings.xml @@ -1,18 +1,4 @@ - Yuklab olish Yuklanmalar diff --git a/libraries/exoplayer/src/main/res/values-vi/strings.xml b/libraries/exoplayer/src/main/res/values-vi/strings.xml index 1e97ed5e5d..3b74a5541a 100644 --- a/libraries/exoplayer/src/main/res/values-vi/strings.xml +++ b/libraries/exoplayer/src/main/res/values-vi/strings.xml @@ -1,18 +1,4 @@ - Tải xuống Tài nguyên đã tải xuống diff --git a/libraries/exoplayer/src/main/res/values-zh-rCN/strings.xml b/libraries/exoplayer/src/main/res/values-zh-rCN/strings.xml index 1d24d8cab3..4031209c29 100644 --- a/libraries/exoplayer/src/main/res/values-zh-rCN/strings.xml +++ b/libraries/exoplayer/src/main/res/values-zh-rCN/strings.xml @@ -1,18 +1,4 @@ - 下载 下载内容 diff --git a/libraries/exoplayer/src/main/res/values-zh-rHK/strings.xml b/libraries/exoplayer/src/main/res/values-zh-rHK/strings.xml index 1b4279329f..eb4c819746 100644 --- a/libraries/exoplayer/src/main/res/values-zh-rHK/strings.xml +++ b/libraries/exoplayer/src/main/res/values-zh-rHK/strings.xml @@ -1,18 +1,4 @@ - 下載 下載內容 diff --git a/libraries/exoplayer/src/main/res/values-zh-rTW/strings.xml b/libraries/exoplayer/src/main/res/values-zh-rTW/strings.xml index f696d26abf..c95bfd95a2 100644 --- a/libraries/exoplayer/src/main/res/values-zh-rTW/strings.xml +++ b/libraries/exoplayer/src/main/res/values-zh-rTW/strings.xml @@ -1,18 +1,4 @@ - 下載 下載 diff --git a/libraries/exoplayer/src/main/res/values-zu/strings.xml b/libraries/exoplayer/src/main/res/values-zu/strings.xml index f1d3047458..4e2a417261 100644 --- a/libraries/exoplayer/src/main/res/values-zu/strings.xml +++ b/libraries/exoplayer/src/main/res/values-zu/strings.xml @@ -1,18 +1,4 @@ - Landa Ukulandwa diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java index fb2ae47b59..d258b7123a 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java @@ -89,6 +89,7 @@ import android.graphics.SurfaceTexture; import android.media.AudioManager; import android.net.Uri; import android.os.Looper; +import android.util.Pair; import android.view.Surface; import androidx.annotation.Nullable; import androidx.media3.common.AdPlaybackState; @@ -129,13 +130,13 @@ import androidx.media3.exoplayer.source.SinglePeriodTimeline; import androidx.media3.exoplayer.source.TrackGroupArray; import androidx.media3.exoplayer.source.WrappingMediaSource; import androidx.media3.exoplayer.source.ads.ServerSideAdInsertionMediaSource; +import androidx.media3.exoplayer.text.TextOutput; import androidx.media3.exoplayer.trackselection.DefaultTrackSelector; import androidx.media3.exoplayer.upstream.Allocation; import androidx.media3.exoplayer.upstream.Allocator; import androidx.media3.exoplayer.upstream.Loader; import androidx.media3.extractor.metadata.id3.BinaryFrame; import androidx.media3.extractor.metadata.id3.TextInformationFrame; -import androidx.media3.test.utils.Action; import androidx.media3.test.utils.ActionSchedule; import androidx.media3.test.utils.ActionSchedule.PlayerRunnable; import androidx.media3.test.utils.ActionSchedule.PlayerTarget; @@ -3843,41 +3844,29 @@ public final class ExoPlayerTest { @Test public void setPlaybackSpeedConsecutivelyNotifiesListenerForEveryChangeOnceAndIsMasked() throws Exception { + ExoPlayer player = new TestExoPlayerBuilder(context).build(); List maskedPlaybackSpeeds = new ArrayList<>(); - Action getPlaybackSpeedAction = - new Action("getPlaybackSpeed", /* description= */ null) { - @Override - protected void doActionImpl( - ExoPlayer player, DefaultTrackSelector trackSelector, @Nullable Surface surface) { - maskedPlaybackSpeeds.add(player.getPlaybackParameters().speed); - } - }; - ActionSchedule actionSchedule = - new ActionSchedule.Builder(TAG) - .pause() - .waitForPlaybackState(Player.STATE_READY) - .setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.1f)) - .apply(getPlaybackSpeedAction) - .setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.2f)) - .apply(getPlaybackSpeedAction) - .setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.3f)) - .apply(getPlaybackSpeedAction) - .play() - .build(); List reportedPlaybackSpeeds = new ArrayList<>(); - Player.Listener listener = + player.addListener( new Player.Listener() { @Override public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { reportedPlaybackSpeeds.add(playbackParameters.speed); } - }; - new ExoPlayerTestRunner.Builder(context) - .setActionSchedule(actionSchedule) - .setPlayerListener(listener) - .build() - .start() - .blockUntilEnded(TIMEOUT_MS); + }); + player.setMediaSource( + new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.AUDIO_FORMAT)); + player.prepare(); + runUntilPlaybackState(player, Player.STATE_READY); + + player.setPlaybackSpeed(1.1f); + maskedPlaybackSpeeds.add(player.getPlaybackParameters().speed); + player.setPlaybackSpeed(1.2f); + maskedPlaybackSpeeds.add(player.getPlaybackParameters().speed); + player.setPlaybackSpeed(1.3f); + maskedPlaybackSpeeds.add(player.getPlaybackParameters().speed); + runUntilPendingCommandsAreFullyHandled(player); + player.release(); assertThat(reportedPlaybackSpeeds).containsExactly(1.1f, 1.2f, 1.3f).inOrder(); assertThat(maskedPlaybackSpeeds).isEqualTo(reportedPlaybackSpeeds); @@ -3887,46 +3876,28 @@ public final class ExoPlayerTest { public void setUnsupportedPlaybackSpeedConsecutivelyNotifiesListenerForEveryChangeOnceAndResetsOnceHandled() throws Exception { - Renderer renderer = - new FakeMediaClockRenderer(C.TRACK_TYPE_AUDIO) { - @Override - public long getPositionUs() { - return 0; - } - - @Override - public void setPlaybackParameters(PlaybackParameters playbackParameters) {} - - @Override - public PlaybackParameters getPlaybackParameters() { - return PlaybackParameters.DEFAULT; - } - }; - ActionSchedule actionSchedule = - new ActionSchedule.Builder(TAG) - .pause() - .waitForPlaybackState(Player.STATE_READY) - .setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.1f)) - .setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.2f)) - .setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.3f)) - .play() + ExoPlayer player = + new TestExoPlayerBuilder(context) + .setRenderers(new AudioClockRendererWithoutSpeedChangeSupport()) .build(); List reportedPlaybackParameters = new ArrayList<>(); - Player.Listener listener = + player.addListener( new Player.Listener() { @Override public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { reportedPlaybackParameters.add(playbackParameters); } - }; - new ExoPlayerTestRunner.Builder(context) - .setSupportedFormats(ExoPlayerTestRunner.AUDIO_FORMAT) - .setRenderers(renderer) - .setActionSchedule(actionSchedule) - .setPlayerListener(listener) - .build() - .start() - .blockUntilEnded(TIMEOUT_MS); + }); + player.setMediaSource( + new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.AUDIO_FORMAT)); + player.prepare(); + runUntilPlaybackState(player, Player.STATE_READY); + + player.setPlaybackSpeed(1.1f); + player.setPlaybackSpeed(1.2f); + player.setPlaybackSpeed(1.3f); + runUntilPendingCommandsAreFullyHandled(player); + player.release(); assertThat(reportedPlaybackParameters) .containsExactly( @@ -3937,6 +3908,51 @@ public final class ExoPlayerTest { .inOrder(); } + @Test + public void + setUnsupportedPlaybackSpeedDirectlyFollowedByDisablingTheRendererAndSupportedPlaybackSpeed_keepsCorrectFinalSpeedAndInformsListenersCorrectly() + throws Exception { + ExoPlayer player = + new TestExoPlayerBuilder(context) + .setRenderers(new AudioClockRendererWithoutSpeedChangeSupport()) + .build(); + List reportedPlaybackParameters = new ArrayList<>(); + player.addListener( + new Player.Listener() { + @Override + public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { + reportedPlaybackParameters.add(playbackParameters); + } + }); + player.setMediaSource( + new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.AUDIO_FORMAT)); + player.prepare(); + runUntilPlaybackState(player, Player.STATE_READY); + + player.setPlaybackSpeed(2f); + // We need to do something that reliably triggers a position sync with the renderer, but no + // further playback progress as we want to test what happens if the parameter reset is still + // pending when we disable the audio renderer below. Calling play and pause will achieve this. + player.play(); + player.pause(); + // Disabling the audio renderer and setting a new speed should work, and should not be affected + // by the still pending parameter reset from above. + player.setTrackSelectionParameters( + player + .getTrackSelectionParameters() + .buildUpon() + .setTrackTypeDisabled(C.TRACK_TYPE_AUDIO, /* disabled= */ true) + .build()); + player.setPlaybackSpeed(5f); + runUntilPendingCommandsAreFullyHandled(player); + player.release(); + + assertThat(reportedPlaybackParameters) + .containsExactly( + new PlaybackParameters(/* speed= */ 2f), new PlaybackParameters(/* speed= */ 5f)) + .inOrder(); + } + @Test public void simplePlaybackHasNoPlaybackSuppression() throws Exception { ActionSchedule actionSchedule = @@ -10071,8 +10087,10 @@ public final class ExoPlayerTest { @Test public void setPlaybackSpeed_withAdPlayback_onlyAppliesToContent() throws Exception { - // Create renderer with media clock to listen to playback parameter changes. + // Create renderer with media clock to listen to playback parameter changes and reported speed + // changes. ArrayList playbackParameters = new ArrayList<>(); + ArrayList> speedChanges = new ArrayList<>(); FakeMediaClockRenderer audioRenderer = new FakeMediaClockRenderer(C.TRACK_TYPE_AUDIO) { private long positionUs; @@ -10100,6 +10118,12 @@ public final class ExoPlayerTest { ? PlaybackParameters.DEFAULT : Iterables.getLast(playbackParameters); } + + @Override + public void setPlaybackSpeed(float currentPlaybackSpeed, float targetPlaybackSpeed) + throws ExoPlaybackException { + speedChanges.add(Pair.create(currentPlaybackSpeed, targetPlaybackSpeed)); + } }; ExoPlayer player = new TestExoPlayerBuilder(context).setRenderers(audioRenderer).build(); AdPlaybackState adPlaybackState = @@ -10132,7 +10156,7 @@ public final class ExoPlayerTest { runUntilPlaybackState(player, Player.STATE_ENDED); player.release(); - // Assert that the renderer received the playback speed updates at each ad/content boundary. + // Assert that the media clock received the playback parameters at each ad/content boundary. assertThat(playbackParameters) .containsExactly( /* preroll ad */ new PlaybackParameters(1f), @@ -10143,6 +10167,18 @@ public final class ExoPlayerTest { /* content after postroll */ new PlaybackParameters(5f)) .inOrder(); + // Assert that the renderer received the speed changes at each ad/content boundary. + assertThat(speedChanges) + .containsExactly( + /* initial setup */ Pair.create(5f, 5f), + /* preroll ad */ Pair.create(1f, 5f), + /* content after preroll */ Pair.create(5f, 5f), + /* midroll ad */ Pair.create(1f, 5f), + /* content after midroll */ Pair.create(5f, 5f), + /* postroll ad */ Pair.create(1f, 5f), + /* content after postroll */ Pair.create(5f, 5f)) + .inOrder(); + // Assert that user-set speed was reported, but none of the ad overrides. verify(mockListener).onPlaybackParametersChanged(any()); verify(mockListener).onPlaybackParametersChanged(new PlaybackParameters(5.0f)); @@ -12436,6 +12472,46 @@ public final class ExoPlayerTest { } } + private static final class AudioClockRendererWithoutSpeedChangeSupport + extends FakeMediaClockRenderer { + + private PlaybackParameters playbackParameters; + private boolean delayingPlaybackParameterReset; + private long positionUs; + + public AudioClockRendererWithoutSpeedChangeSupport() { + super(C.TRACK_TYPE_AUDIO); + playbackParameters = PlaybackParameters.DEFAULT; + } + + @Override + protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException { + super.onPositionReset(positionUs, joining); + this.positionUs = positionUs; + } + + @Override + public long getPositionUs() { + return positionUs; + } + + @Override + public void setPlaybackParameters(PlaybackParameters playbackParameters) { + this.playbackParameters = playbackParameters; + // Similar to a real renderer, the missing speed support is only detected with a delay. + delayingPlaybackParameterReset = true; + } + + @Override + public PlaybackParameters getPlaybackParameters() { + if (delayingPlaybackParameterReset) { + delayingPlaybackParameterReset = false; + return playbackParameters; + } + return PlaybackParameters.DEFAULT; + } + } + /** * Returns an argument matcher for {@link Timeline} instances that ignores period and window uids. */ diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapterTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapterTest.java index 98707a3226..ee7026a669 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapterTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapterTest.java @@ -80,6 +80,20 @@ public class AsynchronousMediaCodecAdapterTest { assertThrows(IllegalStateException.class, () -> adapter.dequeueInputBufferIndex()); } + @Test + public void dequeueInputBufferIndex_withPendingQueueingError_throwsException() { + // Force MediaCodec to throw an error by attempting to queue input buffer -1. + adapter.queueInputBuffer( + /* index= */ -1, + /* offset= */ 0, + /* size= */ 0, + /* presentationTimeUs= */ 0, + /* flags= */ 0); + shadowOf(queueingThread.getLooper()).idle(); + + assertThrows(IllegalStateException.class, () -> adapter.dequeueInputBufferIndex()); + } + @Test public void dequeueInputBufferIndex_afterShutdown_returnsTryAgainLater() { adapter.release(); @@ -123,6 +137,20 @@ public class AsynchronousMediaCodecAdapterTest { assertThrows(IllegalStateException.class, () -> adapter.dequeueOutputBufferIndex(bufferInfo)); } + @Test + public void dequeueOutputBufferIndex_withPendingQueueingError_throwsException() { + // Force MediaCodec to throw an error by attempting to queue input buffer -1. + adapter.queueInputBuffer( + /* index= */ -1, + /* offset= */ 0, + /* size= */ 0, + /* presentationTimeUs= */ 0, + /* flags= */ 0); + shadowOf(queueingThread.getLooper()).idle(); + + assertThrows(IllegalStateException.class, () -> adapter.dequeueOutputBufferIndex(bufferInfo)); + } + @Test public void dequeueOutputBufferIndex_afterShutdown_returnsTryAgainLater() { int index = adapter.dequeueInputBufferIndex(); diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecRendererTest.java new file mode 100644 index 0000000000..ac86669ee5 --- /dev/null +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecRendererTest.java @@ -0,0 +1,323 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * 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. + */ +package androidx.media3.exoplayer.mediacodec; + +import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_YES_WITHOUT_RECONFIGURATION; +import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM; +import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.spy; + +import android.media.MediaCrypto; +import android.media.MediaFormat; +import android.os.SystemClock; +import androidx.annotation.Nullable; +import androidx.media3.common.C; +import androidx.media3.common.Format; +import androidx.media3.common.MimeTypes; +import androidx.media3.exoplayer.DecoderReuseEvaluation; +import androidx.media3.exoplayer.ExoPlaybackException; +import androidx.media3.exoplayer.RendererCapabilities; +import androidx.media3.exoplayer.RendererConfiguration; +import androidx.media3.exoplayer.analytics.PlayerId; +import androidx.media3.exoplayer.drm.DrmSessionEventListener; +import androidx.media3.exoplayer.drm.DrmSessionManager; +import androidx.media3.exoplayer.upstream.DefaultAllocator; +import androidx.media3.test.utils.FakeSampleStream; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import com.google.common.collect.ImmutableList; +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InOrder; + +/** Unit tests for {@link MediaCodecRenderer} */ +@RunWith(AndroidJUnit4.class) +public class MediaCodecRendererTest { + + @Test + public void render_withReplaceStream_triggersOutputCallbacksInCorrectOrder() throws Exception { + Format format1 = + new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1000).build(); + Format format2 = + new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1500).build(); + FakeSampleStream fakeSampleStream1 = + createFakeSampleStream(format1, /* sampleTimesUs...= */ 0, 100, 200, 300); + FakeSampleStream fakeSampleStream2 = + createFakeSampleStream(format2, /* sampleTimesUs...= */ 0, 100, 200); + MediaCodecRenderer renderer = spy(new TestRenderer()); + renderer.init(/* index= */ 0, PlayerId.UNSET); + + renderer.enable( + RendererConfiguration.DEFAULT, + new Format[] {format1}, + fakeSampleStream1, + /* positionUs= */ 0, + /* joining= */ false, + /* mayRenderStartOfStream= */ true, + /* startPositionUs= */ 0, + /* offsetUs= */ 0); + renderer.start(); + long positionUs = 0; + while (!renderer.hasReadStreamToEnd()) { + renderer.render(positionUs, SystemClock.elapsedRealtime()); + positionUs += 100; + } + renderer.replaceStream( + new Format[] {format2}, fakeSampleStream2, /* startPositionUs= */ 400, /* offsetUs= */ 400); + renderer.setCurrentStreamFinal(); + while (!renderer.isEnded()) { + renderer.render(positionUs, SystemClock.elapsedRealtime()); + positionUs += 100; + } + + InOrder inOrder = inOrder(renderer); + inOrder.verify(renderer).onOutputStreamOffsetUsChanged(0); + inOrder.verify(renderer).onOutputFormatChanged(eq(format1), any()); + inOrder.verify(renderer).onProcessedOutputBuffer(0); + inOrder.verify(renderer).onProcessedOutputBuffer(100); + inOrder.verify(renderer).onProcessedOutputBuffer(200); + inOrder.verify(renderer).onProcessedOutputBuffer(300); + inOrder.verify(renderer).onOutputStreamOffsetUsChanged(400); + inOrder.verify(renderer).onProcessedStreamChange(); + inOrder.verify(renderer).onOutputFormatChanged(eq(format2), any()); + inOrder.verify(renderer).onProcessedOutputBuffer(400); + inOrder.verify(renderer).onProcessedOutputBuffer(500); + inOrder.verify(renderer).onProcessedOutputBuffer(600); + } + + @Test + public void + render_withReplaceStreamAndBufferBeyondDuration_triggersOutputCallbacksInCorrectOrder() + throws Exception { + Format format1 = + new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1000).build(); + Format format2 = + new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1500).build(); + FakeSampleStream fakeSampleStream1 = + createFakeSampleStream(format1, /* sampleTimesUs...= */ 0, 100, 200, 300, 400, 500, 600); + FakeSampleStream fakeSampleStream2 = + createFakeSampleStream(format2, /* sampleTimesUs...= */ 0, 100, 200); + MediaCodecRenderer renderer = spy(new TestRenderer()); + renderer.init(/* index= */ 0, PlayerId.UNSET); + + renderer.enable( + RendererConfiguration.DEFAULT, + new Format[] {format1}, + fakeSampleStream1, + /* positionUs= */ 0, + /* joining= */ false, + /* mayRenderStartOfStream= */ true, + /* startPositionUs= */ 0, + /* offsetUs= */ 0); + renderer.start(); + long positionUs = 0; + while (!renderer.hasReadStreamToEnd()) { + renderer.render(positionUs, SystemClock.elapsedRealtime()); + positionUs += 100; + } + renderer.replaceStream( + new Format[] {format2}, fakeSampleStream2, /* startPositionUs= */ 400, /* offsetUs= */ 400); + renderer.setCurrentStreamFinal(); + while (!renderer.isEnded()) { + renderer.render(positionUs, SystemClock.elapsedRealtime()); + positionUs += 100; + } + + InOrder inOrder = inOrder(renderer); + inOrder.verify(renderer).onOutputStreamOffsetUsChanged(0); + inOrder.verify(renderer).onOutputFormatChanged(eq(format1), any()); + inOrder.verify(renderer).onProcessedOutputBuffer(0); + inOrder.verify(renderer).onProcessedOutputBuffer(100); + inOrder.verify(renderer).onProcessedOutputBuffer(200); + inOrder.verify(renderer).onProcessedOutputBuffer(300); + inOrder.verify(renderer).onProcessedOutputBuffer(400); + inOrder.verify(renderer).onProcessedOutputBuffer(500); + inOrder.verify(renderer).onProcessedOutputBuffer(600); + inOrder.verify(renderer).onOutputStreamOffsetUsChanged(400); + inOrder.verify(renderer).onProcessedStreamChange(); + inOrder.verify(renderer).onOutputFormatChanged(eq(format2), any()); + inOrder.verify(renderer).onProcessedOutputBuffer(400); + inOrder.verify(renderer).onProcessedOutputBuffer(500); + inOrder.verify(renderer).onProcessedOutputBuffer(600); + } + + @Test + public void + render_withReplaceStreamAndBufferLessThanStartPosition_triggersOutputCallbacksInCorrectOrder() + throws Exception { + Format format1 = + new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1000).build(); + Format format2 = + new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1500).build(); + FakeSampleStream fakeSampleStream1 = + createFakeSampleStream(format1, /* sampleTimesUs...= */ 0, 100, 200, 300); + FakeSampleStream fakeSampleStream2 = + createFakeSampleStream(format2, /* sampleTimesUs...= */ 0, 100, 200, 300, 400); + MediaCodecRenderer renderer = spy(new TestRenderer()); + renderer.init(/* index= */ 0, PlayerId.UNSET); + + renderer.enable( + RendererConfiguration.DEFAULT, + new Format[] {format1}, + fakeSampleStream1, + /* positionUs= */ 0, + /* joining= */ false, + /* mayRenderStartOfStream= */ true, + /* startPositionUs= */ 0, + /* offsetUs= */ 0); + renderer.start(); + long positionUs = 0; + while (!renderer.hasReadStreamToEnd()) { + renderer.render(positionUs, SystemClock.elapsedRealtime()); + positionUs += 100; + } + renderer.replaceStream( + new Format[] {format2}, fakeSampleStream2, /* startPositionUs= */ 400, /* offsetUs= */ 200); + renderer.setCurrentStreamFinal(); + while (!renderer.isEnded()) { + renderer.render(positionUs, SystemClock.elapsedRealtime()); + positionUs += 100; + } + + InOrder inOrder = inOrder(renderer); + inOrder.verify(renderer).onOutputStreamOffsetUsChanged(0); + inOrder.verify(renderer).onOutputFormatChanged(eq(format1), any()); + inOrder.verify(renderer).onProcessedOutputBuffer(0); + inOrder.verify(renderer).onProcessedOutputBuffer(100); + inOrder.verify(renderer).onProcessedOutputBuffer(200); + inOrder.verify(renderer).onProcessedOutputBuffer(300); + inOrder.verify(renderer).onOutputStreamOffsetUsChanged(200); + inOrder.verify(renderer).onProcessedStreamChange(); + inOrder.verify(renderer).onOutputFormatChanged(eq(format2), any()); + inOrder.verify(renderer).onProcessedOutputBuffer(200); + inOrder.verify(renderer).onProcessedOutputBuffer(300); + inOrder.verify(renderer).onProcessedOutputBuffer(400); + inOrder.verify(renderer).onProcessedOutputBuffer(500); + inOrder.verify(renderer).onProcessedOutputBuffer(600); + } + + private FakeSampleStream createFakeSampleStream(Format format, long... sampleTimesUs) { + ImmutableList.Builder sampleListBuilder = + ImmutableList.builder(); + for (long sampleTimeUs : sampleTimesUs) { + sampleListBuilder.add(oneByteSample(sampleTimeUs, C.BUFFER_FLAG_KEY_FRAME)); + } + sampleListBuilder.add(END_OF_STREAM_ITEM); + FakeSampleStream sampleStream = + new FakeSampleStream( + new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024), + /* mediaSourceEventDispatcher= */ null, + DrmSessionManager.DRM_UNSUPPORTED, + new DrmSessionEventListener.EventDispatcher(), + /* initialFormat= */ format, + sampleListBuilder.build()); + sampleStream.writeData(/* startPositionUs= */ 0); + return sampleStream; + } + + private static class TestRenderer extends MediaCodecRenderer { + + public TestRenderer() { + super( + C.TRACK_TYPE_AUDIO, + MediaCodecAdapter.Factory.DEFAULT, + /* mediaCodecSelector= */ (mimeType, requiresSecureDecoder, requiresTunnelingDecoder) -> + Collections.singletonList( + MediaCodecInfo.newInstance( + /* name= */ "name", + /* mimeType= */ mimeType, + /* codecMimeType= */ mimeType, + /* capabilities= */ null, + /* hardwareAccelerated= */ false, + /* softwareOnly= */ true, + /* vendor= */ false, + /* forceDisableAdaptive= */ false, + /* forceSecure= */ false)), + /* enableDecoderFallback= */ false, + /* assumedMinimumCodecOperatingRate= */ 44100); + } + + @Override + public String getName() { + return "test"; + } + + @Override + protected @Capabilities int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format) + throws MediaCodecUtil.DecoderQueryException { + return RendererCapabilities.create(C.FORMAT_HANDLED); + } + + @Override + protected List getDecoderInfos( + MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder) + throws MediaCodecUtil.DecoderQueryException { + return mediaCodecSelector.getDecoderInfos( + format.sampleMimeType, + /* requiresSecureDecoder= */ false, + /* requiresTunnelingDecoder= */ false); + } + + @Override + protected MediaCodecAdapter.Configuration getMediaCodecConfiguration( + MediaCodecInfo codecInfo, + Format format, + @Nullable MediaCrypto crypto, + float codecOperatingRate) { + return MediaCodecAdapter.Configuration.createForAudioDecoding( + codecInfo, new MediaFormat(), format, crypto); + } + + @Override + protected boolean processOutputBuffer( + long positionUs, + long elapsedRealtimeUs, + @Nullable MediaCodecAdapter codec, + @Nullable ByteBuffer buffer, + int bufferIndex, + int bufferFlags, + int sampleCount, + long bufferPresentationTimeUs, + boolean isDecodeOnlyBuffer, + boolean isLastBuffer, + Format format) + throws ExoPlaybackException { + if (bufferPresentationTimeUs <= positionUs) { + // Only release buffers when the position advances far enough for realistic behavior where + // input of buffers to the codec is faster than output. + codec.releaseOutputBuffer(bufferIndex, /* render= */ true); + return true; + } + return false; + } + + @Override + protected DecoderReuseEvaluation canReuseCodec( + MediaCodecInfo codecInfo, Format oldFormat, Format newFormat) { + return new DecoderReuseEvaluation( + codecInfo.name, + oldFormat, + newFormat, + REUSE_RESULT_YES_WITHOUT_RECONFIGURATION, + /* discardReasons= */ 0); + } + } +} diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java index d42ab38fe0..b7ad8dc6c2 100644 --- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java +++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java @@ -16,6 +16,7 @@ package androidx.media3.exoplayer.video; import static android.view.Display.DEFAULT_DISPLAY; +import static androidx.media3.common.util.Util.msToUs; import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM; import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.format; import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample; @@ -59,6 +60,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.common.collect.ImmutableList; import java.util.Collections; import java.util.List; +import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import org.junit.After; import org.junit.Before; @@ -72,6 +74,7 @@ import org.mockito.junit.MockitoRule; import org.robolectric.Shadows; import org.robolectric.shadows.ShadowDisplay; import org.robolectric.shadows.ShadowLooper; +import org.robolectric.shadows.ShadowSystemClock; /** Unit test for {@link MediaCodecVideoRenderer}. */ @RunWith(AndroidJUnit4.class) @@ -261,7 +264,7 @@ public class MediaCodecVideoRendererTest { /* initialFormat= */ pAsp1, ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME))); fakeSampleStream.writeData(/* startPositionUs= */ 0); - + SystemClock.setCurrentTimeMillis(876_000_000); mediaCodecVideoRenderer.enable( RendererConfiguration.DEFAULT, new Format[] {pAsp1, pAsp2, pAsp3}, @@ -272,25 +275,27 @@ public class MediaCodecVideoRendererTest { /* startPositionUs= */ 0, /* offsetUs */ 0); mediaCodecVideoRenderer.start(); - mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000); - mediaCodecVideoRenderer.render(/* positionUs= */ 250, SystemClock.elapsedRealtime() * 1000); + mediaCodecVideoRenderer.render(/* positionUs= */ 0, msToUs(SystemClock.elapsedRealtime())); + ShadowSystemClock.advanceBy(10, TimeUnit.MILLISECONDS); + mediaCodecVideoRenderer.render(/* positionUs= */ 10_000, msToUs(SystemClock.elapsedRealtime())); fakeSampleStream.append( ImmutableList.of( format(pAsp2), - oneByteSample(/* timeUs= */ 5_000), - oneByteSample(/* timeUs= */ 10_000), - format(pAsp3), - oneByteSample(/* timeUs= */ 15_000), oneByteSample(/* timeUs= */ 20_000), + oneByteSample(/* timeUs= */ 40_000), + format(pAsp3), + oneByteSample(/* timeUs= */ 60_000), + oneByteSample(/* timeUs= */ 80_000), END_OF_STREAM_ITEM)); - fakeSampleStream.writeData(/* startPositionUs= */ 5_000); + fakeSampleStream.writeData(/* startPositionUs= */ 20_000); mediaCodecVideoRenderer.setCurrentStreamFinal(); - int pos = 500; + int positionUs = 20_000; do { - mediaCodecVideoRenderer.render(/* positionUs= */ pos, SystemClock.elapsedRealtime() * 1000); - pos += 250; + ShadowSystemClock.advanceBy(10, TimeUnit.MILLISECONDS); + mediaCodecVideoRenderer.render(positionUs, msToUs(SystemClock.elapsedRealtime())); + positionUs += 10_000; } while (!mediaCodecVideoRenderer.isEnded()); shadowOf(testMainLooper).idle(); diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/offline/DashDownloader.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/offline/DashDownloader.java index e3c4172728..73a59291b3 100644 --- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/offline/DashDownloader.java +++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/offline/DashDownloader.java @@ -100,7 +100,30 @@ public final class DashDownloader extends SegmentDownloader { */ public DashDownloader( MediaItem mediaItem, CacheDataSource.Factory cacheDataSourceFactory, Executor executor) { - this(mediaItem, new DashManifestParser(), cacheDataSourceFactory, executor); + this( + mediaItem, + new DashManifestParser(), + cacheDataSourceFactory, + executor, + DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS); + } + + /** + * @deprecated Use {@link DashDownloader#DashDownloader(MediaItem, Parser, + * CacheDataSource.Factory, Executor, long)} instead. + */ + @Deprecated + public DashDownloader( + MediaItem mediaItem, + Parser manifestParser, + CacheDataSource.Factory cacheDataSourceFactory, + Executor executor) { + this( + mediaItem, + manifestParser, + cacheDataSourceFactory, + executor, + DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS); } /** @@ -113,13 +136,22 @@ public final class DashDownloader extends SegmentDownloader { * @param executor An {@link Executor} used to make requests for the media being downloaded. * Providing an {@link Executor} that uses multiple threads will speed up the download by * allowing parts of it to be executed in parallel. + * @param maxMergedSegmentStartTimeDiffMs The maximum difference of the start time of two + * segments, up to which the segments (of the same URI) should be merged into a single + * download segment, in milliseconds. */ public DashDownloader( MediaItem mediaItem, Parser manifestParser, CacheDataSource.Factory cacheDataSourceFactory, - Executor executor) { - super(mediaItem, manifestParser, cacheDataSourceFactory, executor); + Executor executor, + long maxMergedSegmentStartTimeDiffMs) { + super( + mediaItem, + manifestParser, + cacheDataSourceFactory, + executor, + maxMergedSegmentStartTimeDiffMs); baseUrlExclusionList = new BaseUrlExclusionList(); } diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/offline/HlsDownloader.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/offline/HlsDownloader.java index 777694e9f6..7c82bee608 100644 --- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/offline/HlsDownloader.java +++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/offline/HlsDownloader.java @@ -89,7 +89,30 @@ public final class HlsDownloader extends SegmentDownloader { */ public HlsDownloader( MediaItem mediaItem, CacheDataSource.Factory cacheDataSourceFactory, Executor executor) { - this(mediaItem, new HlsPlaylistParser(), cacheDataSourceFactory, executor); + this( + mediaItem, + new HlsPlaylistParser(), + cacheDataSourceFactory, + executor, + DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS); + } + + /** + * @deprecated Use {@link HlsDownloader#HlsDownloader(MediaItem, Parser, CacheDataSource.Factory, + * Executor, long)} instead. + */ + @Deprecated + public HlsDownloader( + MediaItem mediaItem, + Parser manifestParser, + CacheDataSource.Factory cacheDataSourceFactory, + Executor executor) { + this( + mediaItem, + manifestParser, + cacheDataSourceFactory, + executor, + DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS); } /** @@ -102,13 +125,22 @@ public final class HlsDownloader extends SegmentDownloader { * @param executor An {@link Executor} used to make requests for the media being downloaded. * Providing an {@link Executor} that uses multiple threads will speed up the download by * allowing parts of it to be executed in parallel. + * @param maxMergedSegmentStartTimeDiffMs The maximum difference of the start time of two + * segments, up to which the segments (of the same URI) should be merged into a single + * download segment, in milliseconds. */ public HlsDownloader( MediaItem mediaItem, Parser manifestParser, CacheDataSource.Factory cacheDataSourceFactory, - Executor executor) { - super(mediaItem, manifestParser, cacheDataSourceFactory, executor); + Executor executor, + long maxMergedSegmentStartTimeDiffMs) { + super( + mediaItem, + manifestParser, + cacheDataSourceFactory, + executor, + maxMergedSegmentStartTimeDiffMs); } @Override diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java index e029b74578..d9ec2dbe37 100644 --- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java +++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java @@ -554,11 +554,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou StreamManagerLoadable streamManagerLoadable = new StreamManagerLoadable( sdkAdsLoader, - adsLoader.configuration, + /* imaServerSideAdInsertionMediaSource= */ this, streamRequest, streamPlayer, - applicationAdErrorListener, - loadVideoTimeoutMs); + applicationAdErrorListener); loader.startLoading( streamManagerLoadable, new StreamManagerLoadableCallback(), @@ -633,7 +632,6 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou } this.streamManager.removeAdEventListener(componentListener); this.streamManager.destroy(); - this.streamManager = null; } this.streamManager = streamManager; if (streamManager != null) { @@ -644,6 +642,12 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou if (applicationAdErrorListener != null) { streamManager.addAdErrorListener(applicationAdErrorListener); } + AdsRenderingSettings adsRenderingSettings = + ImaSdkFactory.getInstance().createAdsRenderingSettings(); + adsRenderingSettings.setLoadVideoTimeout(loadVideoTimeoutMs); + adsRenderingSettings.setFocusSkipButtonWhenAvailable( + adsLoader.configuration.focusSkipButtonWhenAvailable); + streamManager.init(adsRenderingSettings); } } @@ -952,7 +956,6 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou @Override public void onLoadCompleted( StreamManagerLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) { - mainHandler.post(() -> setStreamManager(checkNotNull(loadable.getStreamManager()))); setContentUri(checkNotNull(loadable.getContentUri())); } @@ -983,14 +986,12 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou implements Loadable, AdsLoadedListener, AdErrorListener { private final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader; - private final ServerSideAdInsertionConfiguration serverSideAdInsertionConfiguration; + private final ImaServerSideAdInsertionMediaSource imaServerSideAdInsertionMediaSource; private final StreamRequest request; private final StreamPlayer streamPlayer; @Nullable private final AdErrorListener adErrorListener; - private final int loadVideoTimeoutMs; private final ConditionVariable conditionVariable; - @Nullable private volatile StreamManager streamManager; @Nullable private volatile Uri contentUri; private volatile boolean cancelled; private volatile boolean error; @@ -1000,17 +1001,15 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou /** Creates an instance. */ private StreamManagerLoadable( com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader, - ServerSideAdInsertionConfiguration serverSideAdInsertionConfiguration, + ImaServerSideAdInsertionMediaSource imaServerSideAdInsertionMediaSource, StreamRequest request, StreamPlayer streamPlayer, - @Nullable AdErrorListener adErrorListener, - int loadVideoTimeoutMs) { + @Nullable AdErrorListener adErrorListener) { this.adsLoader = adsLoader; - this.serverSideAdInsertionConfiguration = serverSideAdInsertionConfiguration; + this.imaServerSideAdInsertionMediaSource = imaServerSideAdInsertionMediaSource; this.request = request; this.streamPlayer = streamPlayer; this.adErrorListener = adErrorListener; - this.loadVideoTimeoutMs = loadVideoTimeoutMs; conditionVariable = new ConditionVariable(); errorCode = -1; } @@ -1021,12 +1020,6 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou return contentUri; } - /** Returns the stream manager or null if not yet loaded. */ - @Nullable - public StreamManager getStreamManager() { - return streamManager; - } - // Implement Loadable. @Override @@ -1080,14 +1073,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou conditionVariable.open(); return; } - AdsRenderingSettings adsRenderingSettings = - ImaSdkFactory.getInstance().createAdsRenderingSettings(); - adsRenderingSettings.setLoadVideoTimeout(loadVideoTimeoutMs); - adsRenderingSettings.setFocusSkipButtonWhenAvailable( - serverSideAdInsertionConfiguration.focusSkipButtonWhenAvailable); - // After initialization completed the streamUri will be reported to the streamPlayer. - streamManager.init(adsRenderingSettings); - this.streamManager = streamManager; + imaServerSideAdInsertionMediaSource.setStreamManager(streamManager); } // AdErrorEvent.AdErrorListener implementation. diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java index 84bcc4cd8d..c8ac907a8d 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java @@ -652,7 +652,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; default: throw new IllegalStateException(); } - } catch (ParserException e) { + } catch (ParserException | IllegalArgumentException e) { dispatchRtspError(new RtspPlaybackException(e)); } } diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java index b3d79404a2..b96d3d54b9 100644 --- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java +++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java @@ -159,7 +159,8 @@ import com.google.common.collect.ImmutableMap; * @param sessionUri The {@link Uri} of the RTSP playback session. */ public RtspMediaTrack(MediaDescription mediaDescription, Uri sessionUri) { - checkArgument(mediaDescription.attributes.containsKey(ATTR_CONTROL)); + checkArgument( + mediaDescription.attributes.containsKey(ATTR_CONTROL), "missing attribute control"); payloadFormat = generatePayloadFormat(mediaDescription); uri = extractTrackUri(sessionUri, castNonNull(mediaDescription.attributes.get(ATTR_CONTROL))); } @@ -210,7 +211,7 @@ import com.google.common.collect.ImmutableMap; switch (mimeType) { case MimeTypes.AUDIO_AAC: checkArgument(channelCount != C.INDEX_UNSET); - checkArgument(!fmtpParameters.isEmpty()); + checkArgument(!fmtpParameters.isEmpty(), "missing attribute fmtp"); if (mediaEncoding.equals(RtpPayloadFormat.RTP_MEDIA_MPEG4_LATM_AUDIO)) { // cpresent is defined in RFC3016 Section 5.3. cpresent=0 means the config fmtp parameter // must exist. @@ -259,11 +260,11 @@ import com.google.common.collect.ImmutableMap; formatBuilder.setWidth(DEFAULT_H263_WIDTH).setHeight(DEFAULT_H263_HEIGHT); break; case MimeTypes.VIDEO_H264: - checkArgument(!fmtpParameters.isEmpty()); + checkArgument(!fmtpParameters.isEmpty(), "missing attribute fmtp"); processH264FmtpAttribute(formatBuilder, fmtpParameters); break; case MimeTypes.VIDEO_H265: - checkArgument(!fmtpParameters.isEmpty()); + checkArgument(!fmtpParameters.isEmpty(), "missing attribute fmtp"); processH265FmtpAttribute(formatBuilder, fmtpParameters); break; case MimeTypes.VIDEO_VP8: @@ -312,7 +313,8 @@ import com.google.common.collect.ImmutableMap; ImmutableMap fmtpAttributes, int channelCount, int sampleRate) { - checkArgument(fmtpAttributes.containsKey(PARAMETER_PROFILE_LEVEL_ID)); + checkArgument( + fmtpAttributes.containsKey(PARAMETER_PROFILE_LEVEL_ID), "missing profile-level-id param"); String profileLevel = checkNotNull(fmtpAttributes.get(PARAMETER_PROFILE_LEVEL_ID)); formatBuilder.setCodecs(AAC_CODECS_PREFIX + profileLevel); formatBuilder.setInitializationData( @@ -380,10 +382,10 @@ import com.google.common.collect.ImmutableMap; private static void processH264FmtpAttribute( Format.Builder formatBuilder, ImmutableMap fmtpAttributes) { - checkArgument(fmtpAttributes.containsKey(PARAMETER_SPROP_PARAMS)); + checkArgument(fmtpAttributes.containsKey(PARAMETER_SPROP_PARAMS), "missing sprop parameter"); String spropParameterSets = checkNotNull(fmtpAttributes.get(PARAMETER_SPROP_PARAMS)); String[] parameterSets = Util.split(spropParameterSets, ","); - checkArgument(parameterSets.length == 2); + checkArgument(parameterSets.length == 2, "empty sprop value"); ImmutableList initializationData = ImmutableList.of( getInitializationDataFromParameterSet(parameterSets[0]), @@ -418,11 +420,14 @@ import com.google.common.collect.ImmutableMap; maxDonDiff == 0, "non-zero sprop-max-don-diff " + maxDonDiff + " is not supported"); } - checkArgument(fmtpAttributes.containsKey(PARAMETER_H265_SPROP_VPS)); + checkArgument( + fmtpAttributes.containsKey(PARAMETER_H265_SPROP_VPS), "missing sprop-vps parameter"); String spropVPS = checkNotNull(fmtpAttributes.get(PARAMETER_H265_SPROP_VPS)); - checkArgument(fmtpAttributes.containsKey(PARAMETER_H265_SPROP_SPS)); + checkArgument( + fmtpAttributes.containsKey(PARAMETER_H265_SPROP_SPS), "missing sprop-sps parameter"); String spropSPS = checkNotNull(fmtpAttributes.get(PARAMETER_H265_SPROP_SPS)); - checkArgument(fmtpAttributes.containsKey(PARAMETER_H265_SPROP_PPS)); + checkArgument( + fmtpAttributes.containsKey(PARAMETER_H265_SPROP_PPS), "missing sprop-pps parameter"); String spropPPS = checkNotNull(fmtpAttributes.get(PARAMETER_H265_SPROP_PPS)); ImmutableList initializationData = ImmutableList.of( diff --git a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtspClientTest.java b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtspClientTest.java index f7b7ab41b8..104f6ae9c3 100644 --- a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtspClientTest.java +++ b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtspClientTest.java @@ -389,4 +389,68 @@ public final class RtspClientTest { RobolectricUtil.runMainLooperUntil(timelineRequestFailed::get); assertThat(rtspClient.getState()).isEqualTo(RtspClient.RTSP_STATE_UNINITIALIZED); } + + @Test + public void connectServerAndClient_sdpInDescribeResponseHasInvalidFmtpAttr_doesNotUpdateTimeline() + throws Exception { + class ResponseProvider implements RtspServer.ResponseProvider { + @Override + public RtspResponse getOptionsResponse() { + return new RtspResponse( + /* status= */ 200, + new RtspHeaders.Builder().add(RtspHeaders.PUBLIC, "OPTIONS, DESCRIBE").build()); + } + + @Override + public RtspResponse getDescribeResponse(Uri requestedUri, RtspHeaders headers) { + String testMediaSdpInfo = + "v=0\r\n" + + "o=- 1600785369059721 1 IN IP4 192.168.2.176\r\n" + + "s=video, streamed by ExoPlayer\r\n" + + "i=test.mkv\r\n" + + "t=0 0\r\n" + + "a=tool:ExoPlayer\r\n" + + "a=type:broadcast\r\n" + + "a=control:*\r\n" + + "a=range:npt=0-30.102\r\n" + + "m=video 0 RTP/AVP 96\r\n" + + "c=IN IP4 0.0.0.0\r\n" + + "b=AS:500\r\n" + + "a=rtpmap:96 H264/90000\r\n" + + "a=fmtp:96" + + " packetization-mode=1;profile-level-id=64001F;sprop-parameter-sets=\r\n" + + "a=control:track1\r\n"; + return RtspTestUtils.newDescribeResponseWithSdpMessage( + /* sessionDescription= */ testMediaSdpInfo, + // This session description has no tracks. + /* rtpPacketStreamDumps= */ ImmutableList.of(), + requestedUri); + } + } + rtspServer = new RtspServer(new ResponseProvider()); + + AtomicBoolean timelineRequestFailed = new AtomicBoolean(); + rtspClient = + new RtspClient( + new SessionInfoListener() { + @Override + public void onSessionTimelineUpdated( + RtspSessionTiming timing, ImmutableList tracks) {} + + @Override + public void onSessionTimelineRequestFailed( + String message, @Nullable Throwable cause) { + timelineRequestFailed.set(true); + } + }, + EMPTY_PLAYBACK_LISTENER, + /* userAgent= */ "ExoPlayer:RtspClientTest", + RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()), + SocketFactory.getDefault(), + /* debugLoggingEnabled= */ false); + rtspClient.start(); + + RobolectricUtil.runMainLooperUntil(timelineRequestFailed::get); + assertThat(rtspClient.getState()).isEqualTo(RtspClient.RTSP_STATE_UNINITIALIZED); + } } diff --git a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifest.java b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifest.java index 38c17af395..ccc9663ac8 100644 --- a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifest.java +++ b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifest.java @@ -34,8 +34,8 @@ import java.util.UUID; /** * Represents a SmoothStreaming manifest. * - * @see IIS Smooth - * Streaming Client Manifest Format + *

See the IIS Smooth + * Streaming Client Manifest Format */ @UnstableApi public class SsManifest implements FilterableManifest { diff --git a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifestParser.java b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifestParser.java index 6ac0a24560..4160e6b3a9 100644 --- a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifestParser.java +++ b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifestParser.java @@ -51,8 +51,8 @@ import org.xmlpull.v1.XmlPullParserFactory; /** * Parses SmoothStreaming client manifests. * - * @see IIS Smooth - * Streaming Client Manifest Format + *

See the IIS Smooth + * Streaming Client Manifest Format */ @UnstableApi public class SsManifestParser implements ParsingLoadable.Parser { diff --git a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/offline/SsDownloader.java b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/offline/SsDownloader.java index 4ca632271b..6d2ded5a4c 100644 --- a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/offline/SsDownloader.java +++ b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/offline/SsDownloader.java @@ -93,7 +93,26 @@ public final class SsDownloader extends SegmentDownloader { .build(), new SsManifestParser(), cacheDataSourceFactory, - executor); + executor, + DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS); + } + + /** + * @deprecated Use {@link SsDownloader#SsDownloader(MediaItem, Parser, CacheDataSource.Factory, + * Executor, long)} instead. + */ + @Deprecated + public SsDownloader( + MediaItem mediaItem, + Parser manifestParser, + CacheDataSource.Factory cacheDataSourceFactory, + Executor executor) { + this( + mediaItem, + manifestParser, + cacheDataSourceFactory, + executor, + DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS); } /** @@ -106,13 +125,22 @@ public final class SsDownloader extends SegmentDownloader { * @param executor An {@link Executor} used to make requests for the media being downloaded. * Providing an {@link Executor} that uses multiple threads will speed up the download by * allowing parts of it to be executed in parallel. + * @param maxMergedSegmentStartTimeDiffMs The maximum difference of the start time of two + * segments, up to which the segments (of the same URI) should be merged into a single + * download segment, in milliseconds. */ public SsDownloader( MediaItem mediaItem, Parser manifestParser, CacheDataSource.Factory cacheDataSourceFactory, - Executor executor) { - super(mediaItem, manifestParser, cacheDataSourceFactory, executor); + Executor executor, + long maxMergedSegmentStartTimeDiffMs) { + super( + mediaItem, + manifestParser, + cacheDataSourceFactory, + executor, + maxMergedSegmentStartTimeDiffMs); } @Override diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/FlacStreamMetadata.java b/libraries/extractor/src/main/java/androidx/media3/extractor/FlacStreamMetadata.java index debb1f84a8..7c2a9712ce 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/FlacStreamMetadata.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/FlacStreamMetadata.java @@ -33,14 +33,18 @@ import java.util.List; /** * Holder for FLAC metadata. * - * @see FLAC format - * METADATA_BLOCK_STREAMINFO - * @see FLAC format - * METADATA_BLOCK_SEEKTABLE - * @see FLAC format - * METADATA_BLOCK_VORBIS_COMMENT - * @see FLAC format - * METADATA_BLOCK_PICTURE + *

See the following spec references: + * + *

*/ @UnstableApi public final class FlacStreamMetadata { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisBitArray.java b/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisBitArray.java index a1bfbbe059..40e4b1392c 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisBitArray.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisBitArray.java @@ -23,8 +23,8 @@ import androidx.media3.common.util.UnstableApi; /** * Wraps a byte array, providing methods that allow it to be read as a Vorbis bitstream. * - * @see Vorbis bitpacking - * specification + *

See the Vorbis + * bitpacking specification */ @UnstableApi public final class VorbisBitArray { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisUtil.java b/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisUtil.java index 240afed6db..68832975f1 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisUtil.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisUtil.java @@ -52,8 +52,8 @@ public final class VorbisUtil { /** * Vorbis identification header. * - * @see Vorbis - * spec/Identification header + *

See the Vorbis + * spec/Identification header */ public static final class VorbisIdHeader { @@ -135,8 +135,9 @@ public final class VorbisUtil { /** * Returns ilog(x), which is the index of the highest set bit in {@code x}. * - * @see Vorbis - * spec + *

See the Vorbis + * spec + * * @param x the value of which the ilog should be calculated. * @return ilog(x) */ @@ -152,8 +153,9 @@ public final class VorbisUtil { /** * Reads a Vorbis identification header from {@code headerData}. * - * @see Vorbis - * spec/Identification header + *

See the Vorbis + * spec/Identification header + * * @param headerData a {@link ParsableByteArray} wrapping the header data. * @return a {@link VorbisUtil.VorbisIdHeader} with meta data. * @throws ParserException thrown if invalid capture pattern is detected. @@ -202,8 +204,9 @@ public final class VorbisUtil { /** * Reads a Vorbis comment header. * - * @see Vorbis - * spec/Comment header + *

See the Vorbis + * spec/Comment header + * * @param headerData A {@link ParsableByteArray} wrapping the header data. * @return A {@link VorbisUtil.CommentHeader} with all the comments. * @throws ParserException If an error occurs parsing the comment header. @@ -219,8 +222,9 @@ public final class VorbisUtil { * *

The data provided may not contain the Vorbis metadata common header and the framing bit. * - * @see Vorbis - * spec/Comment header + *

See the Vorbis + * spec/Comment header + * * @param headerData A {@link ParsableByteArray} wrapping the header data. * @param hasMetadataHeader Whether the {@code headerData} contains a Vorbis metadata common * header preceding the comment header. @@ -349,8 +353,9 @@ public final class VorbisUtil { * That's why we need to partially decode or at least read the entire setup header to know where * to start reading the modes. * - * @see Vorbis - * spec/Setup header + *

See the Vorbis + * spec/Setup header + * * @param headerData a {@link ParsableByteArray} containing setup header data. * @param channels the number of channels. * @return an array of {@link Mode}s. @@ -592,7 +597,8 @@ public final class VorbisUtil { } /** - * @see _book_maptype1_quantvals + * See the _book_maptype1_quantvals */ private static long mapType1QuantValues(long entries, long dimension) { return (long) Math.floor(Math.pow(entries, 1.d / dimension)); diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java index 4ccca487c8..5cb4b55da9 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java @@ -970,6 +970,26 @@ public class FragmentedMp4Extractor implements Extractor { return version == 1 ? tfdt.readUnsignedLongToLong() : tfdt.readUnsignedInt(); } + private static boolean isEdtsListDurationForEntireMediaTimeline(Track track) { + // Currently we only support a single edit that moves the entire media timeline (indicated by + // duration == 0 or (editListDurationUs + editListMediaTimeUs) >= track duration. + // Other uses of edit lists are uncommon and unsupported. + if (track.editListDurations == null + || track.editListDurations.length != 1 + || track.editListMediaTimes == null) { + return false; + } + if (track.editListDurations[0] == 0) { + return true; + } + long editListEndMediaTimeUs = + Util.scaleLargeTimestamp( + track.editListDurations[0] + track.editListMediaTimes[0], + C.MICROS_PER_SECOND, + track.movieTimescale); + return editListEndMediaTimeUs >= track.durationUs; + } + /** * Parses a trun atom (defined in 14496-12). * @@ -1017,11 +1037,8 @@ public class FragmentedMp4Extractor implements Extractor { // ensure that the first frame's presentation timestamp is zero. long edtsOffset = 0; - // Currently we only support a single edit that moves the entire media timeline (indicated by - // duration == 0). Other uses of edit lists are uncommon and unsupported. - if (track.editListDurations != null - && track.editListDurations.length == 1 - && track.editListDurations[0] == 0) { + // Currently we only support a single edit that moves the entire media timeline. + if (isEdtsListDurationForEntireMediaTimeline(track)) { edtsOffset = castNonNull(track.editListMediaTimes)[0]; } diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlDecoder.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlDecoder.java index b5f53a35fb..f9dea101f1 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlDecoder.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlDecoder.java @@ -65,7 +65,7 @@ import org.xmlpull.v1.XmlPullParserFactory; *

  • cell-resolution * * - * @see TTML specification + *

    See the TTML specification */ @UnstableApi public final class TtmlDecoder extends SimpleSubtitleDecoder { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java index 623f8fd057..e27eefdb0f 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java @@ -36,10 +36,10 @@ import java.util.HashSet; import java.util.Set; /** - * Style object of a Css style block in a Webvtt file. + * Style object of a CSS style block in a WebVTT file. * - * @see W3C specification - Apply - * CSS properties + *

    See the Apply CSS properties + * section of the W3C specification */ @UnstableApi public final class WebvttCssStyle { diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttDecoder.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttDecoder.java index 6a0daf5893..cb74c2b6ab 100644 --- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttDecoder.java +++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttDecoder.java @@ -29,7 +29,7 @@ import java.util.List; /** * A {@link SimpleSubtitleDecoder} for WebVTT. * - * @see WebVTT specification + *

    See the WebVTT specification. */ @UnstableApi public final class WebvttDecoder extends SimpleSubtitleDecoder { diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlDecoderTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlDecoderTest.java index 78b7a4def7..c1b9a69fec 100644 --- a/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlDecoderTest.java +++ b/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlDecoderTest.java @@ -112,14 +112,11 @@ public final class TtmlDecoderTest { * Regression test for devices on JellyBean where some named colors are not correctly defined on * framework level. Tests that lime resolves to #FF00FF00 not #00FF00 * . - * - * @throws IOException thrown if reading subtitle file fails. - * @see - * JellyBean Color - * Kitkat Color */ + // JellyBean Color: + // https://github.com/android/platform_frameworks_base/blob/jb-mr2-release/graphics/java/android/graphics/Color.java#L414 + // KitKat Color: + // https://github.com/android/platform_frameworks_base/blob/kitkat-mr2.2-release/graphics/java/android/graphics/Color.java#L414 @Test public void lime() throws IOException, SubtitleDecoderException { TtmlSubtitle subtitle = getSubtitle(INLINE_ATTRIBUTES_TTML_FILE); diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java b/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java index 27c0cc4ece..30afcb411b 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java @@ -219,9 +219,11 @@ import java.util.concurrent.TimeoutException; if (startInForegroundRequired) { startForeground(mediaNotification); } else { - maybeStopForegroundService(/* removeNotifications= */ false); + // Notification manager has to be updated first to avoid missing updates + // (https://github.com/androidx/media/issues/192). notificationManagerCompat.notify( mediaNotification.notificationId, mediaNotification.notification); + maybeStopForegroundService(/* removeNotifications= */ false); } } diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java index f3045d3cda..c1cd0c6e4a 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java @@ -67,14 +67,16 @@ import java.util.List; import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** - * A session that allows a media app to expose its transport controls and playback information in a - * process to other processes including the Android framework and other apps. The common use cases - * are as follows: + * A session that allows a media app to expose its player functionality, information of the playlist + * and the media item currently being played to other processes including the Android framework and + * other apps. The common use cases are as follows: * *

      - *
    • Bluetooth/wired headset key events support - *
    • Android Auto/Wearable support - *
    • Separating UI process and playback process + *
    • Receiving and dispatching media key events (for instance Bluetooth/wired headset and remote + * control devices). + *
    • Publish media playback information and player commands to SystemUI (media notification) and + * Android Auto/Wear OS. + *
    • Separating UI process and playback process. *
    * *

    A session should be created when an app wants to publish media playback information or handle @@ -82,9 +84,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * sessions can be created to provide finer grain controls of media. See Supporting Multiple Sessions for details. * - *

    If you want to support background playback, {@link MediaSessionService} is preferred. With the - * service, your playback can be revived even after playback is finished. See {@link - * MediaSessionService} for details. + *

    If an app wants to support playback when in the background, using a {@link + * MediaSessionService} is the preferred approach. See {@link MediaSessionService} for details. * *

    Topics covered here: * @@ -103,7 +104,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * #getToken() session token} to other processes to allow them to create a {@link MediaController} * to interact with the session. * - *

    When a session receives transport control commands, the session sends the commands directly to + *

    When a session receives playback commands, the session calls corresponding methods directly on * the underlying player set by {@link Builder#Builder(Context, Player)} or {@link * #setPlayer(Player)}. * @@ -113,15 +114,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * *

    Threading Model

    * - *

    The instances are thread safe, but should be used on the thread with a looper. + *

    The instances are thread safe, but must be used on a thread with a looper. * - *

    {@link Callback} methods will be called from the application thread associated with the {@link + *

    {@link Callback} methods will be called on the application thread associated with the {@link * Player#getApplicationLooper() application looper} of the underlying player. When a new player is - * set by {@link #setPlayer}, the player should use the same application looper as the previous one. + * set by {@link #setPlayer}, the player must use the same application looper as the previous one. * - *

    The session listens to events from the underlying player via {@link Player.Listener} and - * expects the callback methods to be called from the application thread. If the player violates the - * threading contract, {@link IllegalStateException} will be thrown. + *

    The session listens to player events via {@link Player.Listener} and expects the callback + * methods to be called on the application thread. If the player violates the threading contract, an + * {@link IllegalStateException} will be thrown. * *

    Media Key Events Mapping

    * @@ -160,7 +161,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; *
  • *
  • {@link Player#play()} otherwise
  • * - *
  • For a double tap, {@link Player#seekToNext()}
  • + *
  • In case the media key events are coming from another package ID than the package ID of + * the media app (events coming for instance from Bluetooth), a double tap generating two + * key events within a brief amount of time, is converted to {@link Player#seekToNext()} + *
  • * * * @@ -193,16 +197,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * media or remote playback, since the audio focus policy * recommends not playing multiple audio content at the same time. Also, keep in mind that multiple - * media sessions would make Android Auto and Bluetooth device with display to show your apps + * media sessions would make Android Auto and Bluetooth devices with display to show your app * multiple times, because they list up media sessions, not media apps. * *

    Backward Compatibility with Legacy Session APIs

    * - *

    An active {@link MediaSessionCompat} is internally created with the session for the backward - * compatibility. It's used to handle incoming connection and commands from {@link - * MediaControllerCompat}, and helps to utilize existing APIs that are built with legacy media - * session APIs. Use {@link #getSessionCompatToken} to get the legacy token of {@link - * MediaSessionCompat}. + *

    An active {@link MediaSessionCompat} is internally created with the session for backwards + * compatibility. It's used to handle incoming connections and commands from {@link + * MediaControllerCompat} instances, and helps to utilize existing APIs that are built with legacy + * media session APIs. * *

    Backward Compatibility with Legacy Controller APIs

    * @@ -211,10 +214,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * {@linkplain MediaControllerCompat AndroidX controller compat}. However, {@link ControllerInfo} * may not be precise for legacy controllers. See {@link ControllerInfo} for the details. * - *

    Unknown package name nor UID doesn't mean that you should disallow connection nor commands. - * For SDK levels where such issues happen, session tokens could only be obtained by trusted - * controllers (e.g. Bluetooth, Auto, ...), so it may be better for you to allow them as you did - * with legacy sessions. + *

    Neither an unknown package name nor an unknown UID mean that you should disallow a connection + * or commands per se. For SDK levels where such issues happen, session tokens can only be obtained + * by trusted controllers (e.g. Bluetooth, Auto, ...). This means only trusted controllers can + * connect and an app can accept such controllers in the same way as with legacy sessions. */ public class MediaSession { @@ -242,7 +245,7 @@ public class MediaSession { * Creates a builder for {@link MediaSession}. * * @param context The context. - * @param player The underlying player to perform playback and handle transport controls. + * @param player The underlying player to perform playback and handle player commands. * @throws IllegalArgumentException if {@link Player#canAdvertiseSession()} returns false. */ public Builder(Context context, Player player) { @@ -253,13 +256,13 @@ public class MediaSession { * Sets a {@link PendingIntent} to launch an {@link android.app.Activity} for the {@link * MediaSession}. This can be used as a quick link to an ongoing media screen. * - *

    A client can use this pending intent to start an activity belonging to this session. When - * this pending intent is for instance included in the notification {@linkplain - * NotificationCompat.Builder#setContentIntent(PendingIntent) as the content intent}, tapping - * the notification will open this activity. - * - *

    See 'Start an - * Activity from a Notification' also. + *

    A client can use this pending intent to start an activity belonging to this session. On + * API levels below 33 the pending intent can be used {@linkplain + * NotificationCompat.Builder#setContentIntent(PendingIntent) as the content intent}. Tapping + * the notification will then send that pending intent and open the activity (see 'Start an Activity from + * a Notification'). For API levels starting with 33, the media notification reads the + * pending intent directly from the session. * * @param pendingIntent The pending intent. * @return The builder to allow chaining. @@ -290,6 +293,13 @@ public class MediaSession { * Sets a callback for the {@link MediaSession} to handle incoming requests from {link * MediaController}. * + *

    Apps that want to allow controllers to {@linkplain MediaController#setMediaItems(List) + * set} or {@linkplain MediaController#addMediaItems(List) add} media items to the playlist, + * must use a callback and override its {@link + * MediaSession.Callback#onSetMediaItems(MediaSession, ControllerInfo, List, int, long)} or + * {@link MediaSession.Callback#onSetMediaItems(MediaSession, ControllerInfo, List, int, long)} + * methods. + * * @param callback The callback. * @return The builder to allow chaining. */ @@ -371,7 +381,8 @@ public class MediaSession { * @param remoteUserInfo The remote user info. * @param trusted {@code true} if trusted, {@code false} otherwise. * @param cb ControllerCb. Can be {@code null} only when a MediaBrowserCompat connects to - * MediaSessionService and ControllerInfo is needed for SessionCallback#onConnected(). + * MediaSessionService and ControllerInfo is needed for {@link + * MediaSession.Callback#onConnect(MediaSession, ControllerInfo)}. * @param connectionHints A session-specific argument sent from the controller for the * connection. The contents of this bundle may affect the connection result. */ diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java index 9d32853015..67acd1aca6 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java @@ -447,7 +447,7 @@ import org.checkerframework.checker.initialization.qual.Initialized; public MediaSession.ConnectionResult onConnectOnHandler(ControllerInfo controller) { return checkNotNull( - callback.onConnect(instance, controller), "onConntext must return non-null future"); + callback.onConnect(instance, controller), "onConnect must return non-null future"); } public void onPostConnectOnHandler(ControllerInfo controller) { diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java index a93a6cf8a7..30b76997f5 100644 --- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java +++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java @@ -57,26 +57,27 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** * Superclass to be extended by services hosting {@link MediaSession media sessions}. * - *

    It's highly recommended for an app to use this class if they want to keep media playback in - * the background. The service allows other apps to know that your app supports {@link MediaSession} - * even when your app isn't running. For example, a user voice command may start your app to play - * media. + *

    It's highly recommended for an app to use this class if media playback should continue while + * in the background. The service allows other apps to know that your app supports {@link + * MediaSession} even when your app isn't running. This way, a user voice command may be able start + * your app to play media. * *

    To extend this class, declare the intent filter in your {@code AndroidManifest.xml}: * *

    {@code
      * 
    + *   android:foregroundServiceType="mediaPlayback"
    + *   android:exported="true">
      *   
      *     
      *   
      * 
      * }
    * - *

    You may also declare {@code android.media.browse.MediaBrowserService} for compatibility with - * {@link android.support.v4.media.MediaBrowserCompat}. This service can handle the case - * automatically. + *

    You may also declare the action {@code android.media.browse.MediaBrowserService} for + * compatibility with {@link android.support.v4.media.MediaBrowserCompat}. This service can handle + * the case automatically. * *

    It's recommended for an app to have a single service declared in the manifest. Otherwise, your * app might be shown twice in the list of the controller apps, or another app might fail to pick @@ -93,10 +94,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; *

    Service Lifecycle

    * *

    A media session service is a bound service and its foreground - * service type must include mediaPlayback. When a {@link MediaController} is created - * for the service, the controller binds to the service. {@link #onGetSession(ControllerInfo)} will - * be called from {@link #onBind(Intent)}. + * href="https://developer.android.com/guide/topics/manifest/service-element#foregroundservicetype"> + * foreground service type must include mediaPlayback. When a {@link MediaController} + * is created for the service, the controller binds to the service. {@link + * #onGetSession(ControllerInfo)} will be called from {@link #onBind(Intent)}. * *

    After binding, the session's {@link MediaSession.Callback#onConnect(MediaSession, * MediaSession.ControllerInfo)} will be called to accept or reject the connection request from the @@ -115,8 +116,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull; * #onUpdateNotification(MediaSession)}. In this case, you must also start or stop the service from * the foreground, when playback starts or stops respectively. * - *

    The service will be destroyed when all sessions are closed, or no controller is binding to the - * service while the service is in the background. + *

    The service will be destroyed when all sessions are {@linkplain MediaController#release() + * released}, or no controller is binding to the service while the service is in the background. * *

    Supporting Multiple Sessions

    * @@ -247,7 +248,8 @@ public abstract class MediaSessionService extends Service { * Adds a {@link MediaSession} to this service. This is not necessary for most media apps. See Supporting Multiple Sessions for details. * - *

    The added session will be removed automatically when it's closed. + *

    The added session will be removed automatically {@linkplain MediaSession#release() when the + * session is released}. * * @param session A session to be added. * @see #removeSession(MediaSession) diff --git a/libraries/session/src/main/res/values-af/strings.xml b/libraries/session/src/main/res/values-af/strings.xml index 8ff1ece685..08c05430ee 100755 --- a/libraries/session/src/main/res/values-af/strings.xml +++ b/libraries/session/src/main/res/values-af/strings.xml @@ -1,18 +1,4 @@ - Speel tans Speel diff --git a/libraries/session/src/main/res/values-am/strings.xml b/libraries/session/src/main/res/values-am/strings.xml index 167aa23d48..503a47a0b9 100755 --- a/libraries/session/src/main/res/values-am/strings.xml +++ b/libraries/session/src/main/res/values-am/strings.xml @@ -1,18 +1,4 @@ - አሁን እየተጫወተ ያለ አጫውት diff --git a/libraries/session/src/main/res/values-ar/strings.xml b/libraries/session/src/main/res/values-ar/strings.xml index 140f15247b..78c978f933 100755 --- a/libraries/session/src/main/res/values-ar/strings.xml +++ b/libraries/session/src/main/res/values-ar/strings.xml @@ -1,20 +1,6 @@ - - التعرف التلقائي على الوسائط + قيد التشغيل الآن تشغيل إيقاف مؤقت ترجيع إلى العنصر السابق diff --git a/libraries/session/src/main/res/values-az/strings.xml b/libraries/session/src/main/res/values-az/strings.xml index 9b026dc789..3de48f1eee 100755 --- a/libraries/session/src/main/res/values-az/strings.xml +++ b/libraries/session/src/main/res/values-az/strings.xml @@ -1,18 +1,4 @@ - İndi oxudulur Oxudun diff --git a/libraries/session/src/main/res/values-b+sr+Latn/strings.xml b/libraries/session/src/main/res/values-b+sr+Latn/strings.xml index aab80ab5a7..9fc56865f3 100755 --- a/libraries/session/src/main/res/values-b+sr+Latn/strings.xml +++ b/libraries/session/src/main/res/values-b+sr+Latn/strings.xml @@ -1,18 +1,4 @@ - Trenutno svira Pusti diff --git a/libraries/session/src/main/res/values-be/strings.xml b/libraries/session/src/main/res/values-be/strings.xml index b01d57052a..ff390a7a97 100755 --- a/libraries/session/src/main/res/values-be/strings.xml +++ b/libraries/session/src/main/res/values-be/strings.xml @@ -1,18 +1,4 @@ - Зараз іграе Прайграць diff --git a/libraries/session/src/main/res/values-bg/strings.xml b/libraries/session/src/main/res/values-bg/strings.xml index e24ec1e9bc..b720ead2c1 100755 --- a/libraries/session/src/main/res/values-bg/strings.xml +++ b/libraries/session/src/main/res/values-bg/strings.xml @@ -1,18 +1,4 @@ - Възпроизвеждано сега съдържание Пускане diff --git a/libraries/session/src/main/res/values-bn/strings.xml b/libraries/session/src/main/res/values-bn/strings.xml index 2694309f6a..4cb73e9a46 100755 --- a/libraries/session/src/main/res/values-bn/strings.xml +++ b/libraries/session/src/main/res/values-bn/strings.xml @@ -1,18 +1,4 @@ - এখন চলছে চালান diff --git a/libraries/session/src/main/res/values-bs/strings.xml b/libraries/session/src/main/res/values-bs/strings.xml index 2441a1fca8..46f4b9c07d 100755 --- a/libraries/session/src/main/res/values-bs/strings.xml +++ b/libraries/session/src/main/res/values-bs/strings.xml @@ -1,18 +1,4 @@ - Trenutno se reproducira Reproduciranje diff --git a/libraries/session/src/main/res/values-ca/strings.xml b/libraries/session/src/main/res/values-ca/strings.xml index f9bf84a263..368e7fc64b 100755 --- a/libraries/session/src/main/res/values-ca/strings.xml +++ b/libraries/session/src/main/res/values-ca/strings.xml @@ -1,18 +1,4 @@ - S\'està reproduint Reprodueix diff --git a/libraries/session/src/main/res/values-cs/strings.xml b/libraries/session/src/main/res/values-cs/strings.xml index 9c1f3dc49f..9c373bd834 100755 --- a/libraries/session/src/main/res/values-cs/strings.xml +++ b/libraries/session/src/main/res/values-cs/strings.xml @@ -1,18 +1,4 @@ - Přehrává se Přehrát diff --git a/libraries/session/src/main/res/values-da/strings.xml b/libraries/session/src/main/res/values-da/strings.xml index c8236d5ab0..79c8487541 100755 --- a/libraries/session/src/main/res/values-da/strings.xml +++ b/libraries/session/src/main/res/values-da/strings.xml @@ -1,18 +1,4 @@ - Afspilles nu Afspil diff --git a/libraries/session/src/main/res/values-de/strings.xml b/libraries/session/src/main/res/values-de/strings.xml index f6685e5999..24fc22bb1f 100755 --- a/libraries/session/src/main/res/values-de/strings.xml +++ b/libraries/session/src/main/res/values-de/strings.xml @@ -1,18 +1,4 @@ - Läuft gerade Wiedergabe diff --git a/libraries/session/src/main/res/values-el/strings.xml b/libraries/session/src/main/res/values-el/strings.xml index 0d90dca7f9..255b6a74bf 100755 --- a/libraries/session/src/main/res/values-el/strings.xml +++ b/libraries/session/src/main/res/values-el/strings.xml @@ -1,18 +1,4 @@ - Ακούγεται τώρα Αναπαραγωγή diff --git a/libraries/session/src/main/res/values-en-rAU/strings.xml b/libraries/session/src/main/res/values-en-rAU/strings.xml index 0bf10abd2d..aef8b741e6 100755 --- a/libraries/session/src/main/res/values-en-rAU/strings.xml +++ b/libraries/session/src/main/res/values-en-rAU/strings.xml @@ -1,18 +1,4 @@ - Now playing Play diff --git a/libraries/session/src/main/res/values-en-rGB/strings.xml b/libraries/session/src/main/res/values-en-rGB/strings.xml index 0bf10abd2d..aef8b741e6 100755 --- a/libraries/session/src/main/res/values-en-rGB/strings.xml +++ b/libraries/session/src/main/res/values-en-rGB/strings.xml @@ -1,18 +1,4 @@ - Now playing Play diff --git a/libraries/session/src/main/res/values-en-rIN/strings.xml b/libraries/session/src/main/res/values-en-rIN/strings.xml index 0bf10abd2d..aef8b741e6 100755 --- a/libraries/session/src/main/res/values-en-rIN/strings.xml +++ b/libraries/session/src/main/res/values-en-rIN/strings.xml @@ -1,18 +1,4 @@ - Now playing Play diff --git a/libraries/session/src/main/res/values-es-rUS/strings.xml b/libraries/session/src/main/res/values-es-rUS/strings.xml index c3ea5dcb07..8986aeb307 100755 --- a/libraries/session/src/main/res/values-es-rUS/strings.xml +++ b/libraries/session/src/main/res/values-es-rUS/strings.xml @@ -1,18 +1,4 @@ - Está sonando Reproducir diff --git a/libraries/session/src/main/res/values-es/strings.xml b/libraries/session/src/main/res/values-es/strings.xml index 1a8880ecdb..99e401b71f 100755 --- a/libraries/session/src/main/res/values-es/strings.xml +++ b/libraries/session/src/main/res/values-es/strings.xml @@ -1,18 +1,4 @@ - Está sonando Reproducir diff --git a/libraries/session/src/main/res/values-et/strings.xml b/libraries/session/src/main/res/values-et/strings.xml index 4bd8e64a45..a6bd4c2bec 100755 --- a/libraries/session/src/main/res/values-et/strings.xml +++ b/libraries/session/src/main/res/values-et/strings.xml @@ -1,18 +1,4 @@ - Hetkel mängimas Esitamine diff --git a/libraries/session/src/main/res/values-eu/strings.xml b/libraries/session/src/main/res/values-eu/strings.xml index f53cc746a7..c57640f613 100755 --- a/libraries/session/src/main/res/values-eu/strings.xml +++ b/libraries/session/src/main/res/values-eu/strings.xml @@ -1,18 +1,4 @@ - Orain erreproduzitzen Erreproduzitu diff --git a/libraries/session/src/main/res/values-fa/strings.xml b/libraries/session/src/main/res/values-fa/strings.xml index eb8a961f98..0150138991 100755 --- a/libraries/session/src/main/res/values-fa/strings.xml +++ b/libraries/session/src/main/res/values-fa/strings.xml @@ -1,18 +1,4 @@ - درحال پخش پخش diff --git a/libraries/session/src/main/res/values-fi/strings.xml b/libraries/session/src/main/res/values-fi/strings.xml index a4d6637218..2adf214fbd 100755 --- a/libraries/session/src/main/res/values-fi/strings.xml +++ b/libraries/session/src/main/res/values-fi/strings.xml @@ -1,18 +1,4 @@ - Nyt toistetaan Toista diff --git a/libraries/session/src/main/res/values-fr-rCA/strings.xml b/libraries/session/src/main/res/values-fr-rCA/strings.xml index fe00e2480b..bdf99bf638 100755 --- a/libraries/session/src/main/res/values-fr-rCA/strings.xml +++ b/libraries/session/src/main/res/values-fr-rCA/strings.xml @@ -1,18 +1,4 @@ - En cours de lecture Lire diff --git a/libraries/session/src/main/res/values-fr/strings.xml b/libraries/session/src/main/res/values-fr/strings.xml index bda0e76cd5..ffc27ff8a0 100755 --- a/libraries/session/src/main/res/values-fr/strings.xml +++ b/libraries/session/src/main/res/values-fr/strings.xml @@ -1,18 +1,4 @@ - En cours de lecture Lecture diff --git a/libraries/session/src/main/res/values-gl/strings.xml b/libraries/session/src/main/res/values-gl/strings.xml index 97c0b5ae91..96f6019adc 100755 --- a/libraries/session/src/main/res/values-gl/strings.xml +++ b/libraries/session/src/main/res/values-gl/strings.xml @@ -1,18 +1,4 @@ - Reproducindo Reproducir diff --git a/libraries/session/src/main/res/values-gu/strings.xml b/libraries/session/src/main/res/values-gu/strings.xml index 3c2e583039..1fcceea947 100755 --- a/libraries/session/src/main/res/values-gu/strings.xml +++ b/libraries/session/src/main/res/values-gu/strings.xml @@ -1,18 +1,4 @@ - હાલમાં ચાલી રહી છે ચલાવો diff --git a/libraries/session/src/main/res/values-hi/strings.xml b/libraries/session/src/main/res/values-hi/strings.xml index 99a792cdd5..4b6951be58 100755 --- a/libraries/session/src/main/res/values-hi/strings.xml +++ b/libraries/session/src/main/res/values-hi/strings.xml @@ -1,18 +1,4 @@ - अभी चल रहा है चलाएं diff --git a/libraries/session/src/main/res/values-hr/strings.xml b/libraries/session/src/main/res/values-hr/strings.xml index 3fa32acbfb..fe1eae0a7f 100755 --- a/libraries/session/src/main/res/values-hr/strings.xml +++ b/libraries/session/src/main/res/values-hr/strings.xml @@ -1,18 +1,4 @@ - Upravo svira Reproduciraj diff --git a/libraries/session/src/main/res/values-hu/strings.xml b/libraries/session/src/main/res/values-hu/strings.xml index 2ff84c81f9..b1abc67ce9 100755 --- a/libraries/session/src/main/res/values-hu/strings.xml +++ b/libraries/session/src/main/res/values-hu/strings.xml @@ -1,18 +1,4 @@ - Most játszott tartalom Lejátszás diff --git a/libraries/session/src/main/res/values-hy/strings.xml b/libraries/session/src/main/res/values-hy/strings.xml index c5507fa8a1..1385a9cf42 100755 --- a/libraries/session/src/main/res/values-hy/strings.xml +++ b/libraries/session/src/main/res/values-hy/strings.xml @@ -1,18 +1,4 @@ - Այժմ նվագարկվում է Նվագարկել diff --git a/libraries/session/src/main/res/values-in/strings.xml b/libraries/session/src/main/res/values-in/strings.xml index 142339374a..b818baec7c 100755 --- a/libraries/session/src/main/res/values-in/strings.xml +++ b/libraries/session/src/main/res/values-in/strings.xml @@ -1,18 +1,4 @@ - Sedang diputar Putar diff --git a/libraries/session/src/main/res/values-is/strings.xml b/libraries/session/src/main/res/values-is/strings.xml index dfc6267d5a..249da58ac2 100755 --- a/libraries/session/src/main/res/values-is/strings.xml +++ b/libraries/session/src/main/res/values-is/strings.xml @@ -1,18 +1,4 @@ - Í spilun Spila diff --git a/libraries/session/src/main/res/values-it/strings.xml b/libraries/session/src/main/res/values-it/strings.xml index 0be1260e55..0ea7666b8f 100755 --- a/libraries/session/src/main/res/values-it/strings.xml +++ b/libraries/session/src/main/res/values-it/strings.xml @@ -1,18 +1,4 @@ - Ora in riproduzione Riproduci diff --git a/libraries/session/src/main/res/values-iw/strings.xml b/libraries/session/src/main/res/values-iw/strings.xml index d5232cf89b..a7fcd9a24d 100755 --- a/libraries/session/src/main/res/values-iw/strings.xml +++ b/libraries/session/src/main/res/values-iw/strings.xml @@ -1,18 +1,4 @@ - הפריט שמופעל עכשיו הפעלה diff --git a/libraries/session/src/main/res/values-ja/strings.xml b/libraries/session/src/main/res/values-ja/strings.xml index 44957a4eed..2db5cf15a3 100755 --- a/libraries/session/src/main/res/values-ja/strings.xml +++ b/libraries/session/src/main/res/values-ja/strings.xml @@ -1,18 +1,4 @@ - 再生中 再生 diff --git a/libraries/session/src/main/res/values-ka/strings.xml b/libraries/session/src/main/res/values-ka/strings.xml index 44e49caf12..bf4371548e 100755 --- a/libraries/session/src/main/res/values-ka/strings.xml +++ b/libraries/session/src/main/res/values-ka/strings.xml @@ -1,18 +1,4 @@ - ამჟამად უკრავს დაკვრა diff --git a/libraries/session/src/main/res/values-kk/strings.xml b/libraries/session/src/main/res/values-kk/strings.xml index 98f22c7807..e88b1cf39e 100755 --- a/libraries/session/src/main/res/values-kk/strings.xml +++ b/libraries/session/src/main/res/values-kk/strings.xml @@ -1,24 +1,10 @@ - Қазір ойнап тұр Ойнату Кідірту - Алдыңғы мазмұнға өту - Келесі мазмұнға өту + Алдыңғы контентке өту + Келесі контентке өту Артқа айналдыру Алға айналдыру Аутентификация қажет diff --git a/libraries/session/src/main/res/values-km/strings.xml b/libraries/session/src/main/res/values-km/strings.xml index b12cf6a894..1d59559001 100755 --- a/libraries/session/src/main/res/values-km/strings.xml +++ b/libraries/session/src/main/res/values-km/strings.xml @@ -1,18 +1,4 @@ - កំពុងចាក់ ចាក់ diff --git a/libraries/session/src/main/res/values-kn/strings.xml b/libraries/session/src/main/res/values-kn/strings.xml index 485b09c0bf..eb709d4c59 100755 --- a/libraries/session/src/main/res/values-kn/strings.xml +++ b/libraries/session/src/main/res/values-kn/strings.xml @@ -1,18 +1,4 @@ - ಇದೀಗ ಪ್ಲೇ ಮಾಡಲಾಗುತ್ತಿದೆ ಪ್ಲೇ ಮಾಡಿ diff --git a/libraries/session/src/main/res/values-ko/strings.xml b/libraries/session/src/main/res/values-ko/strings.xml index 015471fe5e..5d6fae98c3 100755 --- a/libraries/session/src/main/res/values-ko/strings.xml +++ b/libraries/session/src/main/res/values-ko/strings.xml @@ -1,18 +1,4 @@ - 지금 재생 중 재생 diff --git a/libraries/session/src/main/res/values-ky/strings.xml b/libraries/session/src/main/res/values-ky/strings.xml index 350724d9e0..081cccf540 100755 --- a/libraries/session/src/main/res/values-ky/strings.xml +++ b/libraries/session/src/main/res/values-ky/strings.xml @@ -1,18 +1,4 @@ - Ойноп жатат Ойнотуу diff --git a/libraries/session/src/main/res/values-lo/strings.xml b/libraries/session/src/main/res/values-lo/strings.xml index 337bd7be33..6a7310245f 100755 --- a/libraries/session/src/main/res/values-lo/strings.xml +++ b/libraries/session/src/main/res/values-lo/strings.xml @@ -1,18 +1,4 @@ - ກຳລັງຫຼິ້ນຕອນນີ້ ຫຼິ້ນ diff --git a/libraries/session/src/main/res/values-lt/strings.xml b/libraries/session/src/main/res/values-lt/strings.xml index a8c02c98af..3a8f7ec3e0 100755 --- a/libraries/session/src/main/res/values-lt/strings.xml +++ b/libraries/session/src/main/res/values-lt/strings.xml @@ -1,18 +1,4 @@ - Dabar leidžiama Paleisti diff --git a/libraries/session/src/main/res/values-lv/strings.xml b/libraries/session/src/main/res/values-lv/strings.xml index 030bb7a549..7283e50af1 100755 --- a/libraries/session/src/main/res/values-lv/strings.xml +++ b/libraries/session/src/main/res/values-lv/strings.xml @@ -1,18 +1,4 @@ - Tagad atskaņo Atskaņot diff --git a/libraries/session/src/main/res/values-mk/strings.xml b/libraries/session/src/main/res/values-mk/strings.xml index 19487a2d57..41b8e9fa04 100755 --- a/libraries/session/src/main/res/values-mk/strings.xml +++ b/libraries/session/src/main/res/values-mk/strings.xml @@ -1,18 +1,4 @@ - Сега е пуштено Пушти diff --git a/libraries/session/src/main/res/values-ml/strings.xml b/libraries/session/src/main/res/values-ml/strings.xml index 0b68af763f..8a15c5b4ca 100755 --- a/libraries/session/src/main/res/values-ml/strings.xml +++ b/libraries/session/src/main/res/values-ml/strings.xml @@ -1,18 +1,4 @@ - ഇപ്പോൾ പ്ലേ ചെയ്യുന്നത് പ്ലേ ചെയ്യുക diff --git a/libraries/session/src/main/res/values-mn/strings.xml b/libraries/session/src/main/res/values-mn/strings.xml index 5b9c2604fc..0efbb93de6 100755 --- a/libraries/session/src/main/res/values-mn/strings.xml +++ b/libraries/session/src/main/res/values-mn/strings.xml @@ -1,18 +1,4 @@ - Одоо тоглуулж байна Тоглуулах diff --git a/libraries/session/src/main/res/values-mr/strings.xml b/libraries/session/src/main/res/values-mr/strings.xml index 35c8acf476..e00fdc1a4e 100755 --- a/libraries/session/src/main/res/values-mr/strings.xml +++ b/libraries/session/src/main/res/values-mr/strings.xml @@ -1,18 +1,4 @@ - आता प्ले करत आहे प्ले करा diff --git a/libraries/session/src/main/res/values-ms/strings.xml b/libraries/session/src/main/res/values-ms/strings.xml index 120211affc..5cf7988088 100755 --- a/libraries/session/src/main/res/values-ms/strings.xml +++ b/libraries/session/src/main/res/values-ms/strings.xml @@ -1,18 +1,4 @@ - Sedang dimainkan Main diff --git a/libraries/session/src/main/res/values-my/strings.xml b/libraries/session/src/main/res/values-my/strings.xml index ea9920abc4..9e6f91f761 100755 --- a/libraries/session/src/main/res/values-my/strings.xml +++ b/libraries/session/src/main/res/values-my/strings.xml @@ -1,18 +1,4 @@ - ယခု ဖွင့်နေသည် ဖွင့်ရန် diff --git a/libraries/session/src/main/res/values-nb/strings.xml b/libraries/session/src/main/res/values-nb/strings.xml index fefb5ea835..4712949ceb 100755 --- a/libraries/session/src/main/res/values-nb/strings.xml +++ b/libraries/session/src/main/res/values-nb/strings.xml @@ -1,18 +1,4 @@ - Spilles nå Spill av diff --git a/libraries/session/src/main/res/values-ne/strings.xml b/libraries/session/src/main/res/values-ne/strings.xml index 8d282366c8..9955dd5503 100755 --- a/libraries/session/src/main/res/values-ne/strings.xml +++ b/libraries/session/src/main/res/values-ne/strings.xml @@ -1,18 +1,4 @@ - अहिले प्ले भइरहेको प्ले गर्नुहोस् diff --git a/libraries/session/src/main/res/values-nl/strings.xml b/libraries/session/src/main/res/values-nl/strings.xml index f3735156e4..b451512da3 100755 --- a/libraries/session/src/main/res/values-nl/strings.xml +++ b/libraries/session/src/main/res/values-nl/strings.xml @@ -1,18 +1,4 @@ - Wordt nu afgespeeld Afspelen diff --git a/libraries/session/src/main/res/values-pa/strings.xml b/libraries/session/src/main/res/values-pa/strings.xml index 86653fca74..ad0de6dc75 100755 --- a/libraries/session/src/main/res/values-pa/strings.xml +++ b/libraries/session/src/main/res/values-pa/strings.xml @@ -1,18 +1,4 @@ - ਹੁਣੇ ਚੱਲ ਰਿਹਾ ਹੈ ਚਲਾਓ diff --git a/libraries/session/src/main/res/values-pl/strings.xml b/libraries/session/src/main/res/values-pl/strings.xml index 10eddbef38..00acc8b75b 100755 --- a/libraries/session/src/main/res/values-pl/strings.xml +++ b/libraries/session/src/main/res/values-pl/strings.xml @@ -1,18 +1,4 @@ - Odtwarzam teraz Odtwórz diff --git a/libraries/session/src/main/res/values-pt-rPT/strings.xml b/libraries/session/src/main/res/values-pt-rPT/strings.xml index 0be33ad385..d341ca707b 100755 --- a/libraries/session/src/main/res/values-pt-rPT/strings.xml +++ b/libraries/session/src/main/res/values-pt-rPT/strings.xml @@ -1,18 +1,4 @@ - A tocar Reproduzir diff --git a/libraries/session/src/main/res/values-pt/strings.xml b/libraries/session/src/main/res/values-pt/strings.xml index 7bd7d79520..a422f4d313 100755 --- a/libraries/session/src/main/res/values-pt/strings.xml +++ b/libraries/session/src/main/res/values-pt/strings.xml @@ -1,18 +1,4 @@ - Tocando agora Tocar diff --git a/libraries/session/src/main/res/values-ro/strings.xml b/libraries/session/src/main/res/values-ro/strings.xml index dd6fc779bd..3de8444966 100755 --- a/libraries/session/src/main/res/values-ro/strings.xml +++ b/libraries/session/src/main/res/values-ro/strings.xml @@ -1,18 +1,4 @@ - Se redă acum Redă diff --git a/libraries/session/src/main/res/values-ru/strings.xml b/libraries/session/src/main/res/values-ru/strings.xml index b643018ac0..ff8bc44ce2 100755 --- a/libraries/session/src/main/res/values-ru/strings.xml +++ b/libraries/session/src/main/res/values-ru/strings.xml @@ -1,18 +1,4 @@ - Играет сейчас Воспроизвести diff --git a/libraries/session/src/main/res/values-si/strings.xml b/libraries/session/src/main/res/values-si/strings.xml index 48c60ddef8..706d6b07ae 100755 --- a/libraries/session/src/main/res/values-si/strings.xml +++ b/libraries/session/src/main/res/values-si/strings.xml @@ -1,18 +1,4 @@ - Now playing වාදනය කරන්න diff --git a/libraries/session/src/main/res/values-sk/strings.xml b/libraries/session/src/main/res/values-sk/strings.xml index f2d31acf05..27342a51c7 100755 --- a/libraries/session/src/main/res/values-sk/strings.xml +++ b/libraries/session/src/main/res/values-sk/strings.xml @@ -1,18 +1,4 @@ - Prehráva sa Prehrať diff --git a/libraries/session/src/main/res/values-sl/strings.xml b/libraries/session/src/main/res/values-sl/strings.xml index ab3fa88897..bab26908b5 100755 --- a/libraries/session/src/main/res/values-sl/strings.xml +++ b/libraries/session/src/main/res/values-sl/strings.xml @@ -1,18 +1,4 @@ - Zdaj se predvaja Predvajaj diff --git a/libraries/session/src/main/res/values-sq/strings.xml b/libraries/session/src/main/res/values-sq/strings.xml index c8ebb5046d..bd2c4b0afd 100755 --- a/libraries/session/src/main/res/values-sq/strings.xml +++ b/libraries/session/src/main/res/values-sq/strings.xml @@ -1,18 +1,4 @@ - Gjej këngën Luaj diff --git a/libraries/session/src/main/res/values-sr/strings.xml b/libraries/session/src/main/res/values-sr/strings.xml index e3f7665ac3..fb87221937 100755 --- a/libraries/session/src/main/res/values-sr/strings.xml +++ b/libraries/session/src/main/res/values-sr/strings.xml @@ -1,18 +1,4 @@ - Тренутно свира Пусти diff --git a/libraries/session/src/main/res/values-sv/strings.xml b/libraries/session/src/main/res/values-sv/strings.xml index ad53a1d6e4..d613f5e2e5 100755 --- a/libraries/session/src/main/res/values-sv/strings.xml +++ b/libraries/session/src/main/res/values-sv/strings.xml @@ -1,18 +1,4 @@ - Nu spelas Spela upp diff --git a/libraries/session/src/main/res/values-sw/strings.xml b/libraries/session/src/main/res/values-sw/strings.xml index 241fb50b78..4678bf1b0c 100755 --- a/libraries/session/src/main/res/values-sw/strings.xml +++ b/libraries/session/src/main/res/values-sw/strings.xml @@ -1,18 +1,4 @@ - Inayocheza sasa Cheza diff --git a/libraries/session/src/main/res/values-ta/strings.xml b/libraries/session/src/main/res/values-ta/strings.xml index e0b65856a2..1e441e4843 100755 --- a/libraries/session/src/main/res/values-ta/strings.xml +++ b/libraries/session/src/main/res/values-ta/strings.xml @@ -1,18 +1,4 @@ - இப்போது பிளே ஆவது பிளே செய்யும் diff --git a/libraries/session/src/main/res/values-te/strings.xml b/libraries/session/src/main/res/values-te/strings.xml index e8a3734789..f51ad9575c 100755 --- a/libraries/session/src/main/res/values-te/strings.xml +++ b/libraries/session/src/main/res/values-te/strings.xml @@ -1,18 +1,4 @@ - ప్రస్తుతం ప్లే అవుతున్నది ప్లే చేయండి diff --git a/libraries/session/src/main/res/values-th/strings.xml b/libraries/session/src/main/res/values-th/strings.xml index 683e2aae68..f3a5ecdb6a 100755 --- a/libraries/session/src/main/res/values-th/strings.xml +++ b/libraries/session/src/main/res/values-th/strings.xml @@ -1,18 +1,4 @@ - กำลังเล่น เล่น diff --git a/libraries/session/src/main/res/values-tl/strings.xml b/libraries/session/src/main/res/values-tl/strings.xml index 995740a809..8345460f87 100755 --- a/libraries/session/src/main/res/values-tl/strings.xml +++ b/libraries/session/src/main/res/values-tl/strings.xml @@ -1,18 +1,4 @@ - Nagpi-play ngayon I-play diff --git a/libraries/session/src/main/res/values-tr/strings.xml b/libraries/session/src/main/res/values-tr/strings.xml index 2795069c69..93fa1a469c 100755 --- a/libraries/session/src/main/res/values-tr/strings.xml +++ b/libraries/session/src/main/res/values-tr/strings.xml @@ -1,18 +1,4 @@ - Şimdi oynatılıyor Oynat diff --git a/libraries/session/src/main/res/values-uk/strings.xml b/libraries/session/src/main/res/values-uk/strings.xml index 4786db3ce7..56f0532c17 100755 --- a/libraries/session/src/main/res/values-uk/strings.xml +++ b/libraries/session/src/main/res/values-uk/strings.xml @@ -1,18 +1,4 @@ - Зараз відтворюється Відтворити diff --git a/libraries/session/src/main/res/values-ur/strings.xml b/libraries/session/src/main/res/values-ur/strings.xml index 423908db8a..877371bfc1 100755 --- a/libraries/session/src/main/res/values-ur/strings.xml +++ b/libraries/session/src/main/res/values-ur/strings.xml @@ -1,18 +1,4 @@ - ابھی چل رہا ہے چلائیں diff --git a/libraries/session/src/main/res/values-uz/strings.xml b/libraries/session/src/main/res/values-uz/strings.xml index fed30dd627..1356dd5493 100755 --- a/libraries/session/src/main/res/values-uz/strings.xml +++ b/libraries/session/src/main/res/values-uz/strings.xml @@ -1,18 +1,4 @@ - Bu qaysi musiqa Ijro diff --git a/libraries/session/src/main/res/values-vi/strings.xml b/libraries/session/src/main/res/values-vi/strings.xml index 9307041370..ebb77b0a24 100755 --- a/libraries/session/src/main/res/values-vi/strings.xml +++ b/libraries/session/src/main/res/values-vi/strings.xml @@ -1,18 +1,4 @@ - Đang phát Phát diff --git a/libraries/session/src/main/res/values-zh-rCN/strings.xml b/libraries/session/src/main/res/values-zh-rCN/strings.xml index 0db7916594..bb5ef13433 100755 --- a/libraries/session/src/main/res/values-zh-rCN/strings.xml +++ b/libraries/session/src/main/res/values-zh-rCN/strings.xml @@ -1,18 +1,4 @@ - 正在播放 播放 diff --git a/libraries/session/src/main/res/values-zh-rHK/strings.xml b/libraries/session/src/main/res/values-zh-rHK/strings.xml index 87716dc283..1c6da3a07f 100755 --- a/libraries/session/src/main/res/values-zh-rHK/strings.xml +++ b/libraries/session/src/main/res/values-zh-rHK/strings.xml @@ -1,18 +1,4 @@ - 正在播放 播放 diff --git a/libraries/session/src/main/res/values-zh-rTW/strings.xml b/libraries/session/src/main/res/values-zh-rTW/strings.xml index e7603e7d90..1c10d0755b 100755 --- a/libraries/session/src/main/res/values-zh-rTW/strings.xml +++ b/libraries/session/src/main/res/values-zh-rTW/strings.xml @@ -1,18 +1,4 @@ - 現正播放 播放 diff --git a/libraries/session/src/main/res/values-zu/strings.xml b/libraries/session/src/main/res/values-zu/strings.xml index 4f5ee5dddd..d50537842a 100755 --- a/libraries/session/src/main/res/values-zu/strings.xml +++ b/libraries/session/src/main/res/values-zu/strings.xml @@ -1,18 +1,4 @@ - Okudlala manje Dlala diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/MssimCalculator.java b/libraries/transformer/src/main/java/androidx/media3/transformer/MssimCalculator.java index ca7bbe632b..e0dd32ef77 100644 --- a/libraries/transformer/src/main/java/androidx/media3/transformer/MssimCalculator.java +++ b/libraries/transformer/src/main/java/androidx/media3/transformer/MssimCalculator.java @@ -23,7 +23,7 @@ import static java.lang.Math.pow; * *

    MSSIM divides the image into windows, calculates SSIM of each, then returns the average. * - * @see The SSIM paper. + *

    See the SSIM paper. */ /* package */ final class MssimCalculator { // Referred to as 'L' in the SSIM paper, this constant defines the maximum pixel values. The diff --git a/libraries/ui/lint-baseline.xml b/libraries/ui/lint-baseline.xml index 0b6a29a68c..dafdad02fd 100644 --- a/libraries/ui/lint-baseline.xml +++ b/libraries/ui/lint-baseline.xml @@ -22,7 +22,7 @@ errorLine2=" ^"> @@ -33,7 +33,18 @@ errorLine2=" ^"> + + + + @@ -44,7 +55,7 @@ errorLine2=" ^"> @@ -55,7 +66,18 @@ errorLine2=" ^"> + + + + diff --git a/libraries/ui/src/main/res/values-af/strings.xml b/libraries/ui/src/main/res/values-af/strings.xml index dd37229848..775029f1e2 100644 --- a/libraries/ui/src/main/res/values-af/strings.xml +++ b/libraries/ui/src/main/res/values-af/strings.xml @@ -1,18 +1,4 @@ - Wys spelerkontroles Versteek spelerkontroles diff --git a/libraries/ui/src/main/res/values-am/strings.xml b/libraries/ui/src/main/res/values-am/strings.xml index 343e45fbb8..82802a43cd 100644 --- a/libraries/ui/src/main/res/values-am/strings.xml +++ b/libraries/ui/src/main/res/values-am/strings.xml @@ -1,18 +1,4 @@ - የተጫዋች መቆጣጠሪያዎችን አሳይ የተጫዋች መቆጣጠሪያዎችን ደብቅ @@ -57,7 +43,7 @@ ቪዲዮ ኦዲዮ - ጽሑፍ + ጽሁፍ ምንም ራስ-ሰር ያልታወቀ diff --git a/libraries/ui/src/main/res/values-ar/strings.xml b/libraries/ui/src/main/res/values-ar/strings.xml index bef7a468e5..804acfc4d6 100644 --- a/libraries/ui/src/main/res/values-ar/strings.xml +++ b/libraries/ui/src/main/res/values-ar/strings.xml @@ -1,18 +1,4 @@ - عرض عناصر التحكم بالمشغّل إخفاء عناصر التحكم بالمشغّل diff --git a/libraries/ui/src/main/res/values-az/strings.xml b/libraries/ui/src/main/res/values-az/strings.xml index 86ea052268..90653578f0 100644 --- a/libraries/ui/src/main/res/values-az/strings.xml +++ b/libraries/ui/src/main/res/values-az/strings.xml @@ -1,18 +1,4 @@ - Oyunçu nəzarətlərini göstərin Oyunçu nəzarətlərini gizlədin diff --git a/libraries/ui/src/main/res/values-b+sr+Latn/strings.xml b/libraries/ui/src/main/res/values-b+sr+Latn/strings.xml index 2acf688bca..ae0185ea3d 100644 --- a/libraries/ui/src/main/res/values-b+sr+Latn/strings.xml +++ b/libraries/ui/src/main/res/values-b+sr+Latn/strings.xml @@ -1,18 +1,4 @@ - Prikaži kontrole plejera Sakrij kontrole plejera diff --git a/libraries/ui/src/main/res/values-be/strings.xml b/libraries/ui/src/main/res/values-be/strings.xml index 05ea71c472..041453ecb2 100644 --- a/libraries/ui/src/main/res/values-be/strings.xml +++ b/libraries/ui/src/main/res/values-be/strings.xml @@ -1,18 +1,4 @@ - Паказаць элементы кіравання прайгравальніка Схаваць элементы кіравання прайгравальніка diff --git a/libraries/ui/src/main/res/values-bg/strings.xml b/libraries/ui/src/main/res/values-bg/strings.xml index 3a898ed4bd..5870895d62 100644 --- a/libraries/ui/src/main/res/values-bg/strings.xml +++ b/libraries/ui/src/main/res/values-bg/strings.xml @@ -1,18 +1,4 @@ - Показване на контролите на плейъра Скриване на контролите на плейъра diff --git a/libraries/ui/src/main/res/values-bn/strings.xml b/libraries/ui/src/main/res/values-bn/strings.xml index 2235db434b..d8e8450755 100644 --- a/libraries/ui/src/main/res/values-bn/strings.xml +++ b/libraries/ui/src/main/res/values-bn/strings.xml @@ -1,18 +1,4 @@ - প্লেয়ার নিয়ন্ত্রণগুলি দেখুন প্লেয়ার নিয়ন্ত্রণগুলি লুকান diff --git a/libraries/ui/src/main/res/values-bs/strings.xml b/libraries/ui/src/main/res/values-bs/strings.xml index 3793c330c1..1981474f16 100644 --- a/libraries/ui/src/main/res/values-bs/strings.xml +++ b/libraries/ui/src/main/res/values-bs/strings.xml @@ -1,18 +1,4 @@ - Prikaži kontrole plejera Sakrij kontrole plejera diff --git a/libraries/ui/src/main/res/values-ca/strings.xml b/libraries/ui/src/main/res/values-ca/strings.xml index ba2e8374a9..56b646a258 100644 --- a/libraries/ui/src/main/res/values-ca/strings.xml +++ b/libraries/ui/src/main/res/values-ca/strings.xml @@ -1,18 +1,4 @@ - Mostra els controls del reproductor Amaga els controls del reproductor diff --git a/libraries/ui/src/main/res/values-cs/strings.xml b/libraries/ui/src/main/res/values-cs/strings.xml index bb0f85369a..48943a497d 100644 --- a/libraries/ui/src/main/res/values-cs/strings.xml +++ b/libraries/ui/src/main/res/values-cs/strings.xml @@ -1,18 +1,4 @@ - Zobrazit ovládací prvky přehrávače Skrýt ovládací prvky přehrávače diff --git a/libraries/ui/src/main/res/values-da/strings.xml b/libraries/ui/src/main/res/values-da/strings.xml index 71ff36a1a3..d7486b4b9a 100644 --- a/libraries/ui/src/main/res/values-da/strings.xml +++ b/libraries/ui/src/main/res/values-da/strings.xml @@ -1,18 +1,4 @@ - Vis afspilningsknapper Skjul afspilningsknapper diff --git a/libraries/ui/src/main/res/values-de/strings.xml b/libraries/ui/src/main/res/values-de/strings.xml index ff160f6a6a..f035521b13 100644 --- a/libraries/ui/src/main/res/values-de/strings.xml +++ b/libraries/ui/src/main/res/values-de/strings.xml @@ -1,18 +1,4 @@ - Player-Steuerelemente anzeigen Player-Steuerelemente ausblenden diff --git a/libraries/ui/src/main/res/values-el/strings.xml b/libraries/ui/src/main/res/values-el/strings.xml index c4ed289bef..a715576c61 100644 --- a/libraries/ui/src/main/res/values-el/strings.xml +++ b/libraries/ui/src/main/res/values-el/strings.xml @@ -1,18 +1,4 @@ - Εμφάν. στοιχείων ελέγχου προγράμματος αναπαραγωγής Απόκρ. στοιχείων ελέγχου προγράμματος αναπαραγωγής diff --git a/libraries/ui/src/main/res/values-en-rAU/strings.xml b/libraries/ui/src/main/res/values-en-rAU/strings.xml index af85902797..f312573b97 100644 --- a/libraries/ui/src/main/res/values-en-rAU/strings.xml +++ b/libraries/ui/src/main/res/values-en-rAU/strings.xml @@ -1,18 +1,4 @@ - Show player controls Hide player controls diff --git a/libraries/ui/src/main/res/values-en-rGB/strings.xml b/libraries/ui/src/main/res/values-en-rGB/strings.xml index af85902797..f312573b97 100644 --- a/libraries/ui/src/main/res/values-en-rGB/strings.xml +++ b/libraries/ui/src/main/res/values-en-rGB/strings.xml @@ -1,18 +1,4 @@ - Show player controls Hide player controls diff --git a/libraries/ui/src/main/res/values-en-rIN/strings.xml b/libraries/ui/src/main/res/values-en-rIN/strings.xml index af85902797..f312573b97 100644 --- a/libraries/ui/src/main/res/values-en-rIN/strings.xml +++ b/libraries/ui/src/main/res/values-en-rIN/strings.xml @@ -1,18 +1,4 @@ - Show player controls Hide player controls diff --git a/libraries/ui/src/main/res/values-es-rUS/strings.xml b/libraries/ui/src/main/res/values-es-rUS/strings.xml index 0bc0f6c546..30e3056b70 100644 --- a/libraries/ui/src/main/res/values-es-rUS/strings.xml +++ b/libraries/ui/src/main/res/values-es-rUS/strings.xml @@ -1,18 +1,4 @@ - Mostrar controles del reproductor Ocultar controles del reproductor diff --git a/libraries/ui/src/main/res/values-es/strings.xml b/libraries/ui/src/main/res/values-es/strings.xml index 924d349038..076f352c12 100644 --- a/libraries/ui/src/main/res/values-es/strings.xml +++ b/libraries/ui/src/main/res/values-es/strings.xml @@ -1,18 +1,4 @@ - Mostrar controles del reproductor Ocultar controles de jugador @@ -47,13 +33,13 @@ Habilitar subtítulos Velocidad - ×0,25 - ×0,5 - ×0,75 + 0,25x + 0,5x + 0,75x Normal - ×1,25 - ×1,5 - ×2 + 1,25x + 1,5x + 2x Vídeo Audio diff --git a/libraries/ui/src/main/res/values-et/strings.xml b/libraries/ui/src/main/res/values-et/strings.xml index faa44e483f..22dd590ee0 100644 --- a/libraries/ui/src/main/res/values-et/strings.xml +++ b/libraries/ui/src/main/res/values-et/strings.xml @@ -1,18 +1,4 @@ - Kuva pleieri juhtnupud Peida pleieri juhtnupud diff --git a/libraries/ui/src/main/res/values-eu/strings.xml b/libraries/ui/src/main/res/values-eu/strings.xml index 90441518b1..08fc4df563 100644 --- a/libraries/ui/src/main/res/values-eu/strings.xml +++ b/libraries/ui/src/main/res/values-eu/strings.xml @@ -1,18 +1,4 @@ - Erakutsi erreproduzigailua kontrolatzeko aukerak Ezkutatu erreproduzigailua kontrolatzeko aukerak diff --git a/libraries/ui/src/main/res/values-fa/strings.xml b/libraries/ui/src/main/res/values-fa/strings.xml index c25aa14a7c..a7963d437f 100644 --- a/libraries/ui/src/main/res/values-fa/strings.xml +++ b/libraries/ui/src/main/res/values-fa/strings.xml @@ -1,18 +1,4 @@ - نمایش کنترل‌های پخش‌کننده پنهان کردن کنترل‌های پخش‌کننده diff --git a/libraries/ui/src/main/res/values-fi/strings.xml b/libraries/ui/src/main/res/values-fi/strings.xml index f57744643c..2ab098e46f 100644 --- a/libraries/ui/src/main/res/values-fi/strings.xml +++ b/libraries/ui/src/main/res/values-fi/strings.xml @@ -1,18 +1,4 @@ - Näytä soittimen säätimet Näytä soittimen säätimet diff --git a/libraries/ui/src/main/res/values-fr-rCA/strings.xml b/libraries/ui/src/main/res/values-fr-rCA/strings.xml index 5faeb0b5b2..8b5a19053e 100644 --- a/libraries/ui/src/main/res/values-fr-rCA/strings.xml +++ b/libraries/ui/src/main/res/values-fr-rCA/strings.xml @@ -1,18 +1,4 @@ - Afficher les commandes du lecteur Masquer les commandes du lecteur diff --git a/libraries/ui/src/main/res/values-fr/strings.xml b/libraries/ui/src/main/res/values-fr/strings.xml index cc876a8bf3..cc2386dcdd 100644 --- a/libraries/ui/src/main/res/values-fr/strings.xml +++ b/libraries/ui/src/main/res/values-fr/strings.xml @@ -1,18 +1,4 @@ - Afficher les commandes du lecteur Masquer les commandes du lecteur diff --git a/libraries/ui/src/main/res/values-gl/strings.xml b/libraries/ui/src/main/res/values-gl/strings.xml index 932434af28..125daae7f8 100644 --- a/libraries/ui/src/main/res/values-gl/strings.xml +++ b/libraries/ui/src/main/res/values-gl/strings.xml @@ -1,18 +1,4 @@ - Mostrar controis do reprodutor Ocultar controis do reprodutor @@ -71,6 +57,6 @@ Complementaria Comentarios Subtítulos - %1$.2f Mbps + %1$.2f Mb/s %1$s, %2$s diff --git a/libraries/ui/src/main/res/values-gu/strings.xml b/libraries/ui/src/main/res/values-gu/strings.xml index 955ccf1f93..d912bae291 100644 --- a/libraries/ui/src/main/res/values-gu/strings.xml +++ b/libraries/ui/src/main/res/values-gu/strings.xml @@ -1,18 +1,4 @@ - પ્લેયર માટેના નિયંત્રણો બતાવો પ્લેયર માટેના નિયંત્રણો છુપાવો diff --git a/libraries/ui/src/main/res/values-hi/strings.xml b/libraries/ui/src/main/res/values-hi/strings.xml index fe18f2a16a..37113b18d4 100644 --- a/libraries/ui/src/main/res/values-hi/strings.xml +++ b/libraries/ui/src/main/res/values-hi/strings.xml @@ -1,18 +1,4 @@ - प्लेयर नियंत्रण दिखाएं प्लेयर नियंत्रण छिपाएं diff --git a/libraries/ui/src/main/res/values-hr/strings.xml b/libraries/ui/src/main/res/values-hr/strings.xml index 320e47aef8..f58e02d530 100644 --- a/libraries/ui/src/main/res/values-hr/strings.xml +++ b/libraries/ui/src/main/res/values-hr/strings.xml @@ -1,18 +1,4 @@ - Prikaži kontrole playera Sakrij kontrole playera diff --git a/libraries/ui/src/main/res/values-hu/strings.xml b/libraries/ui/src/main/res/values-hu/strings.xml index 7f24082f54..66801d3709 100644 --- a/libraries/ui/src/main/res/values-hu/strings.xml +++ b/libraries/ui/src/main/res/values-hu/strings.xml @@ -1,18 +1,4 @@ - Lejátszásvezérlők mutatása Lejátszásvezérlők elrejtése diff --git a/libraries/ui/src/main/res/values-hy/strings.xml b/libraries/ui/src/main/res/values-hy/strings.xml index f77b183118..5171913fbe 100644 --- a/libraries/ui/src/main/res/values-hy/strings.xml +++ b/libraries/ui/src/main/res/values-hy/strings.xml @@ -1,18 +1,4 @@ - Ցուցադրել նվագարկչի կառավարները Թաքցնել նվագարկչի կառավարները diff --git a/libraries/ui/src/main/res/values-in/strings.xml b/libraries/ui/src/main/res/values-in/strings.xml index b010d3c739..9afe0208ca 100644 --- a/libraries/ui/src/main/res/values-in/strings.xml +++ b/libraries/ui/src/main/res/values-in/strings.xml @@ -1,18 +1,4 @@ - Menampilkan kontrol pemutar Menyembunyikan kontrol pemutar diff --git a/libraries/ui/src/main/res/values-is/strings.xml b/libraries/ui/src/main/res/values-is/strings.xml index 20f603c476..fe4d96f02c 100644 --- a/libraries/ui/src/main/res/values-is/strings.xml +++ b/libraries/ui/src/main/res/values-is/strings.xml @@ -1,18 +1,4 @@ - Sýna spilunarstýringar Fela spilunarstýringar diff --git a/libraries/ui/src/main/res/values-it/strings.xml b/libraries/ui/src/main/res/values-it/strings.xml index b97ae06645..c6f6c12d00 100644 --- a/libraries/ui/src/main/res/values-it/strings.xml +++ b/libraries/ui/src/main/res/values-it/strings.xml @@ -1,18 +1,4 @@ - Mostra i controlli del player Nascondi i controlli del player diff --git a/libraries/ui/src/main/res/values-iw/strings.xml b/libraries/ui/src/main/res/values-iw/strings.xml index c5499212c4..0b61a2c434 100644 --- a/libraries/ui/src/main/res/values-iw/strings.xml +++ b/libraries/ui/src/main/res/values-iw/strings.xml @@ -1,32 +1,4 @@ - - הצגת פקדי הנגן הסתרת פקדי הנגן @@ -43,16 +15,14 @@ הפסקה הרצה אחורה - הרצה שנייה אחת (%d) אחורה + הרצה %d שניות אחורה הרצה %d שניות אחורה - הרצה %d שניות אחורה הרצה %d שניות אחורה הרצה קדימה - הרצה שנייה אחת (%d) קדימה + הרצה %d שניות קדימה הרצה %d שניות קדימה - הרצה %d שניות קדימה הרצה %d שניות קדימה המצב הנוכחי: ללא חזרה. לחצן להחלפת מצב החזרה. diff --git a/libraries/ui/src/main/res/values-ja/strings.xml b/libraries/ui/src/main/res/values-ja/strings.xml index f8970f64ee..c5017b3c72 100644 --- a/libraries/ui/src/main/res/values-ja/strings.xml +++ b/libraries/ui/src/main/res/values-ja/strings.xml @@ -1,18 +1,4 @@ - プレーヤーのコントロールを表示する プレーヤーのコントロールを非表示にする diff --git a/libraries/ui/src/main/res/values-ka/strings.xml b/libraries/ui/src/main/res/values-ka/strings.xml index 2ff75b72dd..65c048bff7 100644 --- a/libraries/ui/src/main/res/values-ka/strings.xml +++ b/libraries/ui/src/main/res/values-ka/strings.xml @@ -1,18 +1,4 @@ - დამკვრელის სამართავი ღილაკების ჩვენება დამკვრელის სამართავი ღილაკების დამალვა diff --git a/libraries/ui/src/main/res/values-kk/strings.xml b/libraries/ui/src/main/res/values-kk/strings.xml index 86d41df02b..3296c2b557 100644 --- a/libraries/ui/src/main/res/values-kk/strings.xml +++ b/libraries/ui/src/main/res/values-kk/strings.xml @@ -1,18 +1,4 @@ - Ойнатқышты басқару элементтерін көрсету Ойнатқышты басқару элементтерін жасыру @@ -37,9 +23,9 @@ %d секунд алға айналдыру %d секунд алға айналдыру - Қазіргі режим: еш мазмұн қайталанбайды. Қайталау режимін ауыстыру. - Қазіргі режим: бір мазмұн қайталанады. Қайталау режимін ауыстыру. - Қазіргі режим: барлық мазмұн қайталанады Қайталау режимін ауыстыру. + Қазіргі режим: еш контент қайталанбайды. Қайталау режимін ауыстыру. + Қазіргі режим: бір контент қайталанады. Қайталау режимін ауыстыру. + Қазіргі режим: барлық контент қайталанады Қайталау режимін ауыстыру. Араластыру режимін өшіру Араластыру режимін қосу VR режимі diff --git a/libraries/ui/src/main/res/values-km/strings.xml b/libraries/ui/src/main/res/values-km/strings.xml index 3e8fd004a1..c8da4e15a9 100644 --- a/libraries/ui/src/main/res/values-km/strings.xml +++ b/libraries/ui/src/main/res/values-km/strings.xml @@ -1,18 +1,4 @@ - បង្ហាញការគ្រប់គ្រងកម្មវិធីចាក់ចម្រៀង លាក់ការគ្រប់គ្រងកម្មវិធីចាក់ចម្រៀង diff --git a/libraries/ui/src/main/res/values-kn/strings.xml b/libraries/ui/src/main/res/values-kn/strings.xml index 45b2417f5b..5dcf85112d 100644 --- a/libraries/ui/src/main/res/values-kn/strings.xml +++ b/libraries/ui/src/main/res/values-kn/strings.xml @@ -1,18 +1,4 @@ - ಪ್ಲೇಯರ್ ನಿಯಂತ್ರಣಗಳನ್ನು ತೋರಿಸಿ ಪ್ಲೇಯರ್ ನಿಯಂತ್ರಣಗಳನ್ನು ಮರೆಮಾಡಿ diff --git a/libraries/ui/src/main/res/values-ko/strings.xml b/libraries/ui/src/main/res/values-ko/strings.xml index b6e8d231ea..d0a0f942c5 100644 --- a/libraries/ui/src/main/res/values-ko/strings.xml +++ b/libraries/ui/src/main/res/values-ko/strings.xml @@ -1,18 +1,4 @@ - 플레이어 컨트롤 표시 플레이어 컨트롤 숨기기 diff --git a/libraries/ui/src/main/res/values-ky/strings.xml b/libraries/ui/src/main/res/values-ky/strings.xml index 29601c0253..818f69e1ea 100644 --- a/libraries/ui/src/main/res/values-ky/strings.xml +++ b/libraries/ui/src/main/res/values-ky/strings.xml @@ -1,23 +1,9 @@ - Ойноткучту башкаруу элементтерин көрсөтүү Ойноткучту башкаруу элементтерин жашыруу Ойнотуу көрсөткүчү - Жөндөөлөр + Параметрлер Кошумча жөндөөлөрдү жашыруу Кошумча жөндөөлөрдү көрсөтүү Толук экранга кирүү diff --git a/libraries/ui/src/main/res/values-lo/strings.xml b/libraries/ui/src/main/res/values-lo/strings.xml index bd0f103425..3126375a15 100644 --- a/libraries/ui/src/main/res/values-lo/strings.xml +++ b/libraries/ui/src/main/res/values-lo/strings.xml @@ -1,18 +1,4 @@ - ສະ​ແດງຕົວຄວບ​ຄຸມ​ເຄື່ອງ​ຫຼິ້ນ ເຊື່ອງຕົວຄວບ​ຄຸມ​ເຄື່ອງ​ຫຼິ້ນ diff --git a/libraries/ui/src/main/res/values-lt/strings.xml b/libraries/ui/src/main/res/values-lt/strings.xml index f1d221e9bf..020629ad60 100644 --- a/libraries/ui/src/main/res/values-lt/strings.xml +++ b/libraries/ui/src/main/res/values-lt/strings.xml @@ -1,18 +1,4 @@ - Rodyti leistuvės valdiklius Slėpti leistuvės valdiklius diff --git a/libraries/ui/src/main/res/values-lv/strings.xml b/libraries/ui/src/main/res/values-lv/strings.xml index 0d0ec51c3c..ef264ca156 100644 --- a/libraries/ui/src/main/res/values-lv/strings.xml +++ b/libraries/ui/src/main/res/values-lv/strings.xml @@ -1,18 +1,4 @@ - Rādīt atskaņotāja vadīklas Slēpt atskaņotāja vadīklas diff --git a/libraries/ui/src/main/res/values-mk/strings.xml b/libraries/ui/src/main/res/values-mk/strings.xml index a5d13326e4..95fe201eb9 100644 --- a/libraries/ui/src/main/res/values-mk/strings.xml +++ b/libraries/ui/src/main/res/values-mk/strings.xml @@ -1,18 +1,4 @@ - Прикажи ги контролите на плеерот Сокриј ги контролите на плеерот diff --git a/libraries/ui/src/main/res/values-ml/strings.xml b/libraries/ui/src/main/res/values-ml/strings.xml index 4d1e4e995a..984452145e 100644 --- a/libraries/ui/src/main/res/values-ml/strings.xml +++ b/libraries/ui/src/main/res/values-ml/strings.xml @@ -1,18 +1,4 @@ - പ്ലേയർ നിയന്ത്രണങ്ങൾ കാണിക്കുക പ്ലേയർ നിയന്ത്രണങ്ങൾ മറയ്ക്കുക @@ -59,7 +45,7 @@ ഓഡിയോ ടെക്‌സ്റ്റ് ഒന്നുമില്ല - സ്വമേധയാ + സ്വയമേവ അജ്ഞാതം %1$d × %2$d മോണോ diff --git a/libraries/ui/src/main/res/values-mn/strings.xml b/libraries/ui/src/main/res/values-mn/strings.xml index 56273ce0e5..667ef287b9 100644 --- a/libraries/ui/src/main/res/values-mn/strings.xml +++ b/libraries/ui/src/main/res/values-mn/strings.xml @@ -1,18 +1,4 @@ - Тоглуулагчийн удирдлагыг харуулах Тоглуулагчийн удирдлагыг нуух diff --git a/libraries/ui/src/main/res/values-mr/strings.xml b/libraries/ui/src/main/res/values-mr/strings.xml index 303f5ca52f..ffde009be0 100644 --- a/libraries/ui/src/main/res/values-mr/strings.xml +++ b/libraries/ui/src/main/res/values-mr/strings.xml @@ -1,18 +1,4 @@ - प्लेअर नियंत्रणे दर्शवा प्लेअर नियंत्रणे लपवा diff --git a/libraries/ui/src/main/res/values-ms/strings.xml b/libraries/ui/src/main/res/values-ms/strings.xml index 2758851781..f051ca6264 100644 --- a/libraries/ui/src/main/res/values-ms/strings.xml +++ b/libraries/ui/src/main/res/values-ms/strings.xml @@ -1,18 +1,4 @@ - Tunjukkan kawalan pemain Sembunyikan kawalan pemain diff --git a/libraries/ui/src/main/res/values-my/strings.xml b/libraries/ui/src/main/res/values-my/strings.xml index 89ad8e5636..eaa9531ee9 100644 --- a/libraries/ui/src/main/res/values-my/strings.xml +++ b/libraries/ui/src/main/res/values-my/strings.xml @@ -1,18 +1,4 @@ - ပလေယာ ခလုတ်များကို ပြရန် ပလေယာ ခလုတ်များကို ဝှက်ရန် diff --git a/libraries/ui/src/main/res/values-nb/strings.xml b/libraries/ui/src/main/res/values-nb/strings.xml index 075270b6dc..e444db3ebe 100644 --- a/libraries/ui/src/main/res/values-nb/strings.xml +++ b/libraries/ui/src/main/res/values-nb/strings.xml @@ -1,18 +1,4 @@ - Vis avspillingskontrollene Skjul avspillingskontrollene diff --git a/libraries/ui/src/main/res/values-ne/strings.xml b/libraries/ui/src/main/res/values-ne/strings.xml index 586aea713f..bf8d43e1d7 100644 --- a/libraries/ui/src/main/res/values-ne/strings.xml +++ b/libraries/ui/src/main/res/values-ne/strings.xml @@ -1,18 +1,4 @@ - प्लेयरसम्बन्धी नियन्त्रणहरू देखाउनुहोस् प्लेयरसम्बन्धी नियन्त्रणहरू लुकाउनुहोस् diff --git a/libraries/ui/src/main/res/values-nl/strings.xml b/libraries/ui/src/main/res/values-nl/strings.xml index eaf3659200..2a1f32bcdc 100644 --- a/libraries/ui/src/main/res/values-nl/strings.xml +++ b/libraries/ui/src/main/res/values-nl/strings.xml @@ -1,18 +1,4 @@ - Afspeelbediening tonen Afspeelbediening verbergen diff --git a/libraries/ui/src/main/res/values-pa/strings.xml b/libraries/ui/src/main/res/values-pa/strings.xml index e2579cd646..2c882f2d37 100644 --- a/libraries/ui/src/main/res/values-pa/strings.xml +++ b/libraries/ui/src/main/res/values-pa/strings.xml @@ -1,18 +1,4 @@ - ਪਲੇਅਰ ਕੰਟਰੋਲਾਂ ਨੂੰ ਦਿਖਾਓ ਪਲੇਅਰ ਕੰਟਰੋਲਾਂ ਨੂੰ ਲੁਕਾਓ diff --git a/libraries/ui/src/main/res/values-pl/strings.xml b/libraries/ui/src/main/res/values-pl/strings.xml index ec5bfca158..f6489e8cef 100644 --- a/libraries/ui/src/main/res/values-pl/strings.xml +++ b/libraries/ui/src/main/res/values-pl/strings.xml @@ -1,18 +1,4 @@ - Pokaż elementy sterujące odtwarzacza Ukryj elementy sterujące odtwarzacza diff --git a/libraries/ui/src/main/res/values-pt-rPT/strings.xml b/libraries/ui/src/main/res/values-pt-rPT/strings.xml index 6e70f79c53..76aa421cab 100644 --- a/libraries/ui/src/main/res/values-pt-rPT/strings.xml +++ b/libraries/ui/src/main/res/values-pt-rPT/strings.xml @@ -1,18 +1,4 @@ - Mostrar controlos do leitor Ocultar controlos do leitor diff --git a/libraries/ui/src/main/res/values-pt/strings.xml b/libraries/ui/src/main/res/values-pt/strings.xml index 80a7f42669..be152306b0 100644 --- a/libraries/ui/src/main/res/values-pt/strings.xml +++ b/libraries/ui/src/main/res/values-pt/strings.xml @@ -1,18 +1,4 @@ - Mostrar controles do player Ocultar controles do player diff --git a/libraries/ui/src/main/res/values-ro/strings.xml b/libraries/ui/src/main/res/values-ro/strings.xml index 0448518b56..d67cc4c3a9 100644 --- a/libraries/ui/src/main/res/values-ro/strings.xml +++ b/libraries/ui/src/main/res/values-ro/strings.xml @@ -1,52 +1,38 @@ - Afișează comenzile playerului Ascunde comenzile playerului Progresul redării Setări - Ascundeți setările suplimentare - Afișați setările suplimentare - Accesați în ecran complet - Ieșiți din ecranul complet + Ascunde setările suplimentare + Afișează setările suplimentare + Accesează în ecran complet + Ieși din ecranul complet Înapoi Înainte - Întrerupeți - Redați - Opriți - Derulați înapoi + Întrerupe + Redă + Oprește + Derulează înapoi Derulează înapoi cu %d secundă Derulează înapoi cu %d secunde Derulează înapoi cu %d de secunde - Derulați rapid înainte + Derulează rapid înainte Derulează rapid înainte cu %d secundă Derulează rapid înainte cu %d secunde Derulează rapid înainte cu %d de secunde - Mod actual: nu se repetă. Comutați modul repetare. - Mod actual: se repetă una. Comutați modul repetare. - Mod curent: se repetă tot. Comutați modul repetare. - Dezactivați modul sortare aleatorie - Activați modul sortare aleatorie + Mod actual: nu se repetă. Comută modul repetare. + Mod actual: se repetă una. Comută modul repetare. + Mod curent: se repetă tot. Comută modul repetare. + Dezactivează modul sortare aleatorie + Activează modul sortare aleatorie Mod RV - Dezactivați subtitrările - Activați subtitrările + Dezactivează subtitrările + Activează subtitrările Viteză 0,25x diff --git a/libraries/ui/src/main/res/values-ru/strings.xml b/libraries/ui/src/main/res/values-ru/strings.xml index 36d9b4c01e..d08bfeb758 100644 --- a/libraries/ui/src/main/res/values-ru/strings.xml +++ b/libraries/ui/src/main/res/values-ru/strings.xml @@ -1,18 +1,4 @@ - Показать панель управления Скрыть панель управления diff --git a/libraries/ui/src/main/res/values-si/strings.xml b/libraries/ui/src/main/res/values-si/strings.xml index a78f859718..e19521ca7b 100644 --- a/libraries/ui/src/main/res/values-si/strings.xml +++ b/libraries/ui/src/main/res/values-si/strings.xml @@ -1,18 +1,4 @@ - ධාවක පාලන පෙන්වන්න ධාවක පාලන සඟවන්න diff --git a/libraries/ui/src/main/res/values-sk/strings.xml b/libraries/ui/src/main/res/values-sk/strings.xml index 75204eed52..abb31c30ce 100644 --- a/libraries/ui/src/main/res/values-sk/strings.xml +++ b/libraries/ui/src/main/res/values-sk/strings.xml @@ -1,18 +1,4 @@ - Zobraziť ovládacie prvky prehrávača Skryť ovládacie prvky prehrávača diff --git a/libraries/ui/src/main/res/values-sl/strings.xml b/libraries/ui/src/main/res/values-sl/strings.xml index 5bc58e7ae8..77c82d1728 100644 --- a/libraries/ui/src/main/res/values-sl/strings.xml +++ b/libraries/ui/src/main/res/values-sl/strings.xml @@ -1,18 +1,4 @@ - Prikaz kontrolnikov predvajalnika Skrivanje kontrolnikov predvajalnika diff --git a/libraries/ui/src/main/res/values-sq/strings.xml b/libraries/ui/src/main/res/values-sq/strings.xml index 0357753ac8..b814f210a9 100644 --- a/libraries/ui/src/main/res/values-sq/strings.xml +++ b/libraries/ui/src/main/res/values-sq/strings.xml @@ -1,18 +1,4 @@ - Shfaq komandat e luajtësit Fshih komandat e luajtësit diff --git a/libraries/ui/src/main/res/values-sr/strings.xml b/libraries/ui/src/main/res/values-sr/strings.xml index d20d287b1c..23ce2ca335 100644 --- a/libraries/ui/src/main/res/values-sr/strings.xml +++ b/libraries/ui/src/main/res/values-sr/strings.xml @@ -1,18 +1,4 @@ - Прикажи контроле плејера Сакриј контроле плејера diff --git a/libraries/ui/src/main/res/values-sv/strings.xml b/libraries/ui/src/main/res/values-sv/strings.xml index 73edb04ff0..64b401a72d 100644 --- a/libraries/ui/src/main/res/values-sv/strings.xml +++ b/libraries/ui/src/main/res/values-sv/strings.xml @@ -1,18 +1,4 @@ - Visa spelarkontroller Dölj spelarkontroller diff --git a/libraries/ui/src/main/res/values-sw/strings.xml b/libraries/ui/src/main/res/values-sw/strings.xml index 015e31701d..02308a65ca 100644 --- a/libraries/ui/src/main/res/values-sw/strings.xml +++ b/libraries/ui/src/main/res/values-sw/strings.xml @@ -1,18 +1,4 @@ - Onyesha vidhibiti vya kichezaji Ficha vidhibiti vya kichezaji diff --git a/libraries/ui/src/main/res/values-ta/strings.xml b/libraries/ui/src/main/res/values-ta/strings.xml index 36172865b9..bdae9c8632 100644 --- a/libraries/ui/src/main/res/values-ta/strings.xml +++ b/libraries/ui/src/main/res/values-ta/strings.xml @@ -1,18 +1,4 @@ - பிளேயர் கட்டுப்பாடுகளைக் காட்டும் பிளேயர் கட்டுப்பாடுகளை மறைக்கும் diff --git a/libraries/ui/src/main/res/values-te/strings.xml b/libraries/ui/src/main/res/values-te/strings.xml index 124b8a99ba..7485b6b423 100644 --- a/libraries/ui/src/main/res/values-te/strings.xml +++ b/libraries/ui/src/main/res/values-te/strings.xml @@ -1,18 +1,4 @@ - ప్లేయర్ నియంత్రణలను చూపు ప్లేయర్ నియంత్రణలను దాచిపెట్టు @@ -21,7 +7,7 @@ అదనపు సెట్టింగ్‌లను దాచు అదనపు సెట్టింగ్‌లను చూపు ఫుల్ స్క్రీన్‌లోకి ప్రవేశించు - ఫుల్ స్క్రీన్ నుండి నిష్క్రమించు + ఫుల్ స్క్రీన్ నుండి నిష్క్రమించండి మునుపటి తర్వాత పాజ్ చేయండి @@ -32,14 +18,14 @@ %d సెకండ్ రివైండ్ చేయండి %d సెకన్లు రివైండ్ చేయండి - వేగంగా ఫార్వార్డ్ చేయండి + వేగంగా ఫార్వర్డ్ చేయండి %d సెకండ్ వేగంగా ఫార్వర్డ్ చేయండి %d సెకన్లు వేగంగా ఫార్వర్డ్ చేయండి - ప్రస్తుత మోడ్: ఏదీ పునరావృతం చేయవద్దు. పునరావృతం మోడ్‌ను టోగుల్ చేయండి. - ప్రస్తుత మోడ్: ఒకదానిని పునరావృతం చేయండి. పునరావృతం మోడ్‌ను టోగుల్ చేయండి. - ప్రస్తుత మోడ్: అన్నింటిని పునరావృతం చేయండి. పునరావృతం మోడ్‌ను టోగుల్ చేయండి. + ప్రస్తుత మోడ్: ఏదీ రిపీట్‌ చేయవద్దు. రిపీట్‌ మోడ్‌ను టోగుల్ చేయండి. + ప్రస్తుత మోడ్: ఒకదానిని రిపీట్‌ చేయండి. రిపీట్‌ మోడ్‌ను టోగుల్ చేయండి. + ప్రస్తుత మోడ్: అన్నింటిని రిపీట్‌ చేయండి. రిపీట్‌ మోడ్‌ను టోగుల్ చేయండి. షఫుల్ మోడ్‌ను డిజేబుల్ చేయండి షఫుల్ మోడ్‌ను ఎనేబుల్ చేయండి వర్చువల్ రియాలిటీ మోడ్ diff --git a/libraries/ui/src/main/res/values-th/strings.xml b/libraries/ui/src/main/res/values-th/strings.xml index fdb108348e..5584dcf93c 100644 --- a/libraries/ui/src/main/res/values-th/strings.xml +++ b/libraries/ui/src/main/res/values-th/strings.xml @@ -1,18 +1,4 @@ - แสดงแผงควบคุมโปรแกรมเล่น ซ่อนตัวควบคุมโปรแกรมเล่น diff --git a/libraries/ui/src/main/res/values-tl/strings.xml b/libraries/ui/src/main/res/values-tl/strings.xml index 0757b261da..04c4c2fa68 100644 --- a/libraries/ui/src/main/res/values-tl/strings.xml +++ b/libraries/ui/src/main/res/values-tl/strings.xml @@ -1,18 +1,4 @@ - Ipakita ang mga kontrol ng player Itago ang mga kontrol ng player diff --git a/libraries/ui/src/main/res/values-tr/strings.xml b/libraries/ui/src/main/res/values-tr/strings.xml index b2fd8b6fa4..6058a4c36f 100644 --- a/libraries/ui/src/main/res/values-tr/strings.xml +++ b/libraries/ui/src/main/res/values-tr/strings.xml @@ -1,18 +1,4 @@ - Oynatıcı kontrollerini göster Oynatıcı kontrollerini gizle diff --git a/libraries/ui/src/main/res/values-uk/strings.xml b/libraries/ui/src/main/res/values-uk/strings.xml index 2ffcc61d1c..7d3307db51 100644 --- a/libraries/ui/src/main/res/values-uk/strings.xml +++ b/libraries/ui/src/main/res/values-uk/strings.xml @@ -1,18 +1,4 @@ - Показувати елементи керування програвачем Сховати елементи керування програвачем diff --git a/libraries/ui/src/main/res/values-ur/strings.xml b/libraries/ui/src/main/res/values-ur/strings.xml index 1e410c9712..0c49e5d445 100644 --- a/libraries/ui/src/main/res/values-ur/strings.xml +++ b/libraries/ui/src/main/res/values-ur/strings.xml @@ -1,18 +1,4 @@ - پلیئر کنٹرولز دکھائیں پلیئر کنٹرولز چھپائیں diff --git a/libraries/ui/src/main/res/values-uz/strings.xml b/libraries/ui/src/main/res/values-uz/strings.xml index f7639dd95b..7b17320094 100644 --- a/libraries/ui/src/main/res/values-uz/strings.xml +++ b/libraries/ui/src/main/res/values-uz/strings.xml @@ -1,18 +1,4 @@ - Pleyer tugmalarini chiqarish Pleyer tugmalarini berkitish diff --git a/libraries/ui/src/main/res/values-vi/strings.xml b/libraries/ui/src/main/res/values-vi/strings.xml index 400d321f5e..1749d4f5dd 100644 --- a/libraries/ui/src/main/res/values-vi/strings.xml +++ b/libraries/ui/src/main/res/values-vi/strings.xml @@ -1,18 +1,4 @@ - Hiển thị các nút điều khiển trình phát Ẩn các nút điều khiển trình phát diff --git a/libraries/ui/src/main/res/values-zh-rCN/strings.xml b/libraries/ui/src/main/res/values-zh-rCN/strings.xml index 4fedf2ae07..d32d6a4482 100644 --- a/libraries/ui/src/main/res/values-zh-rCN/strings.xml +++ b/libraries/ui/src/main/res/values-zh-rCN/strings.xml @@ -1,18 +1,4 @@ - 显示播放器控件 隐藏播放器控件 diff --git a/libraries/ui/src/main/res/values-zh-rHK/strings.xml b/libraries/ui/src/main/res/values-zh-rHK/strings.xml index 88a0239c57..354f58c69a 100644 --- a/libraries/ui/src/main/res/values-zh-rHK/strings.xml +++ b/libraries/ui/src/main/res/values-zh-rHK/strings.xml @@ -1,18 +1,4 @@ - 顯示播放器控制項 隱藏播放器控制項 diff --git a/libraries/ui/src/main/res/values-zh-rTW/strings.xml b/libraries/ui/src/main/res/values-zh-rTW/strings.xml index 27dc2bcfd6..4249568e95 100644 --- a/libraries/ui/src/main/res/values-zh-rTW/strings.xml +++ b/libraries/ui/src/main/res/values-zh-rTW/strings.xml @@ -1,18 +1,4 @@ - 顯示播放器控制選項 隱藏播放器控制選項 diff --git a/libraries/ui/src/main/res/values-zu/strings.xml b/libraries/ui/src/main/res/values-zu/strings.xml index 05d78e556e..7752fcb772 100644 --- a/libraries/ui/src/main/res/values-zu/strings.xml +++ b/libraries/ui/src/main/res/values-zu/strings.xml @@ -1,18 +1,4 @@ - Bonisa izilawuli zesidlali Fihla izilawuli zesidlali