diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index 005d4e2e68f..ab94d9922e4 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 38c631388ea..b5a6a75e466 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 39884d57c73..f1fa589e943 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 8d2a0cbde1b..dbf61395bd6 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 @@ private static int fetchPlaybackState(RemoteMediaClient remoteMediaClient) { 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 @@ private static boolean isTrackActive(long id, long[] activeTrackIds) { 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 ac44ebb6036..112b2ea8413 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 @@ private 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 2640a0a7e46..957ab7d2194 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 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 ad013f4cd3c..64cdc2d9964 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 3d1f7a6349d..f0550b07895 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 @@ /** * 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 4e97ab796c5..7b64795466c 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 @@ private NetworkTypeObserver(Context context) { 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 464db2648d9..a079c488d97 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 static byte[] toByteArray(InputStream inputStream) throws IOException { * 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 static Intent registerReceiverNotExported( } } - /** - * 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 0d403fb1738..36c2d454296 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 @@ public void initialize(long uid) { @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 625c6090b6b..04fd1482bd8 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.IntentFilter; import android.media.AudioManager; import android.os.Handler; -import androidx.media3.common.util.Util; /* package */ final class AudioBecomingNoisyManager { @@ -47,8 +46,8 @@ public AudioBecomingNoisyManager(Context context, Handler eventHandler, EventLis */ 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 525b2df9c43..10c64a7e982 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 @@ public void setDeviceMuted(boolean muted) { @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 6c3c64075b7..e84ea1e7e09 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 @@ && shouldUseLivePlaybackSpeedControl(playbackInfo.timeline, playbackInfo.periodI 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 @@ && shouldUseLivePlaybackSpeedControl(playbackInfo.timeline, playbackInfo.periodI } } + 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 @@ private void resetRendererPosition(long periodPositionUs) throws ExoPlaybackExce private void setPlaybackParametersInternal(PlaybackParameters playbackParameters) throws ExoPlaybackException { - mediaClock.setPlaybackParameters(playbackParameters); + setMediaClockPlaybackParameters(playbackParameters); handlePlaybackParameters(mediaClock.getPlaybackParameters(), /* acknowledgeCommand= */ true); } @@ -1657,7 +1663,7 @@ private void maybeTriggerPendingMessages(long oldPeriodPositionUs, long newPerio 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 @@ private void updatePlaybackSpeedSettingsForNewPeriod( 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 @@ private long getMaxRendererReadPositionUs() { 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 @@ private void maybeUpdateLoadingPeriod() throws ExoPlaybackException { } } - 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 1fc7dc48287..c5a82301549 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 @@ public StreamVolumeManager(Context context, Handler eventHandler, Listener liste 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 6cb10f07311..9888db45a6d 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 1cfef2accc9..671ddb5aa6d 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 AudioCapabilities register() { @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 952c5fb8b6e..414cf24d403 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 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 @@ void onSystemTimeUsMismatch( /** 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 @@ void onSystemTimeUsMismatch( 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 @@ void onSystemTimeUsMismatch( * @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 @@ public void setAudioTrack( 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 @@ public void setAudioTrackPlaybackSpeed(float audioTrackPlaybackSpeed) { 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 @@ public long getCurrentPositionUs(boolean sourceEnded) { // 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 @@ public long getCurrentPositionUs(boolean sourceEnded) { // 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 @@ public long getCurrentPositionUs(boolean sourceEnded) { /** 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 @@ public boolean isPlaying() { * @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 @@ public boolean pause() { 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 @@ public void reset() { } 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 @@ private void maybeSampleSyncParams() { 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 @@ private void maybePollAndCheckTimestamp(long systemTimeUs, long playbackPosition // 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 @@ private void maybeUpdateLatency(long systemTimeUs) { // 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 @@ private void resetSyncParams() { */ private boolean forceHasPendingData() { return needsPassthroughWorkarounds - && Assertions.checkNotNull(audioTrack).getPlayState() == AudioTrack.PLAYSTATE_PAUSED + && checkNotNull(audioTrack).getPlayState() == AudioTrack.PLAYSTATE_PAUSED && getPlaybackHeadPosition() == 0; } @@ -576,34 +585,44 @@ private long getPlaybackHeadPositionUs() { * @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 @@ private long getPlaybackHeadPosition() { // 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 91aa86ca068..9377aaace6d 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 @@ public void releaseOutputBuffer(int index, long renderTimeStampNs) { @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 10f0a24e155..bba85feff94 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 @@ public void waitUntilQueueingComplete() throws InterruptedException { 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 67c6649d27c..e950d3ac73a 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 @@ private void flushInternal() { // 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 e49a9a5a3cc..d36f9eb07b9 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 @@ private static boolean needsAdaptationFlushWorkaround(String mimeType) { * @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 @@ private static boolean needsProfileExcludedWorkaround(String mimeType, int profi && ("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 @@ private static final class Api29 { 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 815b4c369ea..a752432fa04 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 @@ private static String buildCustomDiagnosticInfo(int errorCode) { */ 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 @@ private static String buildCustomDiagnosticInfo(int errorCode) { 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 @@ private static String buildCustomDiagnosticInfo(int errorCode) { 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 MediaCodecRenderer( 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 MediaCodecRenderer( 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 @@ protected final void setPendingPlaybackException(ExoPlaybackException exception) 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 @@ protected final void updateOutputFormatForTime(long presentationTimeUs) if (outputFormatChanged || (codecOutputMediaFormatChanged && outputFormat != null)) { onOutputFormatChanged(outputFormat, codecOutputMediaFormat); codecOutputMediaFormatChanged = false; + needToNotifyOutputFormatChangeAfterStreamChange = false; } } @@ -648,23 +641,17 @@ protected void onEnabled(boolean joining, boolean mayRenderStartOfStream) @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 @@ protected void onPositionReset(long positionUs, boolean joining) throws ExoPlayb // 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 void setPlaybackSpeed(float currentPlaybackSpeed, float targetPlaybackSpe @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 @@ protected void resetCodecStateForFlush() { decodeOnlyPresentationTimestamps.clear(); largestQueuedPresentationTimeUs = C.TIME_UNSET; lastBufferInStreamPresentationTimeUs = C.TIME_UNSET; + lastProcessedOutputBufferTimeUs = C.TIME_UNSET; if (c2Mp3TimestampTracker != null) { c2Mp3TimestampTracker.reset(); } @@ -1353,7 +1335,11 @@ private boolean feedInputBuffer() throws ExoPlaybackException { 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 @@ protected void onQueueInputBuffer(DecoderInputBuffer buffer) throws ExoPlaybackE */ @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 @@ protected final void setPendingOutputEndOfStream() { * 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 @@ private static boolean codecNeedsMonoChannelCountWorkaround(String name, Format && "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 e97e053084d..81af112a670 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 static Pair getCodecProfileAndLevel(Format format) { 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 @@ private static Pair getDolbyVisionProfileAndLevel( } @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 @@ private static Pair getHevcProfileAndLevel(String codec, Strin 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 437490eea38..1fee9d80cbd 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 @@ private Downloader createDownloader(DownloadRequest request, @C.ContentType int 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 d4b8ece7262..7a8ac0ba2bf 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 int compareTo(Segment other) { } } + 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 int compareTo(Segment other) { 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 int compareTo(Segment other) { 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 int compareTo(Segment other) { * @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 SegmentDownloader( cacheKeyFactory = cacheDataSourceFactory.getCacheKeyFactory(); priorityTaskManager = cacheDataSourceFactory.getUpstreamPriorityTaskManager(); activeRunnables = new ArrayList<>(); + maxMergedSegmentStartTimeDiffUs = Util.msToUs(maxMergedSegmentStartTimeDiffMs); } @Override @@ -145,7 +170,7 @@ public final void download(@Nullable ProgressListener progressListener) // 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 @@ private void removeActiveRunnable(int index) { } } - 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 @@ private static void mergeSegments(List segments, CacheKeyFactory keyFac @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 ab87aa361cb..53ad113710b 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 boolean checkRequirements(Context context) { 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 @@ private boolean isDeviceIdle(Context context) { } 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 541224221c9..d214bc95af9 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 RequirementsWatcher(Context context, Listener listener, Requirements requ 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 ca3d0fadbc9..b555b3c40fc 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 @@ * 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 1bd45fc24aa..6b9d4b567d8 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 @@ && maybeDropBuffersToKeyframe(positionUs, treatDroppedBuffersAsSkipped)) { 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 c1a57f9246c..b935a4cf89b 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 a16ac064b34..93a8ae066bc 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 6b03d0fdd64..14db1306223 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 1193607b284..28e9dcb0103 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 49d5ff23c4c..c8e8eeb7868 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 54b219e042d..1539008d72f 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 420fa00b979..af37d820785 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 c4a6c3686e7..be8ed9cdaab 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 5ca81f27803..5fe387cefb6 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 6c80546d7b9..ecb381f5349 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 f916f076e32..54a112a9ee9 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 dca960dbd42..1b15b1f605b 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 92904131e1c..848bfb2d4d6 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 e079173d120..8ef5c20f72d 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 27fb6389f8b..639a151801e 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 27fb6389f8b..639a151801e 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 27fb6389f8b..639a151801e 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 45b331f4771..545c55ac86d 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 6d13c5fd219..a7a8edeaba1 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 67df50e2675..5ac51b75cbf 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 e2e0ca7b9e8..d476d29b7bc 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 ad7b14e934a..271919fbc1b 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 6fbdf45fac4..15f1a38628c 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 9610fd2e14f..e177a5d7534 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 22f02999768..274278bf4ad 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 6764ddb4d3b..46f80721b57 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 27821e22999..5d2b5874e50 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 d839e3f3fa3..17a17adcdd2 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 031e629fd84..05d7cfa4481 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 e511a695948..d19171d9750 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 61d61e87041..4fc4e86bcb4 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 ce650eb479b..e20aac9971f 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 713dbc50b86..6c62e12ee42 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 709bb8e5b45..03a7a64d1af 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 dadb93b0167..41010d65f8e 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 b4df8db16c7..f32b0bfc75a 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 34fa93ac36b..2460d0c4b7c 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 a0e7f49af14..616fb42af59 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 d284e7b78ad..2089642a4a3 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 c311225089f..7747d1256b3 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 3bbdd2cb6c8..a87dce92cc1 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 8cf2921bc38..6ce6949e2b2 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 0b7f314f91c..1ff16ef04f1 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 afc749c88aa..c81cdd10928 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 1899c273e46..0213b5e0621 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 58a1de488ab..6e1c3aecfcd 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 5c75164da85..2e0653da43e 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 6e7aac5dc9a..95b30da3ce3 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 0d98194d6a3..0c37597ddae 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 f4d08d49d37..3033c3c93f8 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 9e308724ad4..88294129a9c 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 5d5b46b28c5..026d9be10d7 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 deff7e8e415..a1a014bf7bb 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 589296d9a65..c8eeb8553dd 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 86236f9e03d..ee5031015df 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 63fc39fac73..40633af74cd 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 6c9f639ec98..1988ac95eb1 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 c38d90d176f..59889e55d58 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 a4a7afa1a65..56355afa1f6 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 b989fd3bc1e..c2b7f7f3603 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 36b652c2cca..2b9b3289fd2 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 fec40af4e2e..4581322c61a 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 025ba4a0e68..460643ebb8e 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 836ff91e416..b3e916ef4c5 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 aad22873536..f537b8e38f0 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 34ea63ab33d..a645a70f8c3 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 f2ea0ebb2fe..e6d112427fd 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 b9dd46a955d..bd9cdbf5d4c 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 fe1f48a955d..cf0480ebb08 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 c888cd3b721..73cba4d678c 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 3b04a687a5f..4e68f6f05c3 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 2b9252ec7c2..24a75afb362 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 7fbb8955de0..2413efd778f 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 966f4062735..b9dd50dd8a4 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 85aaf73a8cd..21e3ed45d50 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 1e97ed5e5d5..3b74a5541a2 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 1d24d8cab34..4031209c29c 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 1b4279329fc..eb4c8197467 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 f696d26abfc..c95bfd95a2b 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 f1d3047458b..4e2a4172614 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 fb2ae47b596..d258b7123a2 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.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.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 void adInMovingLiveWindow_keepsContentPosition() throws Exception { @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 void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { 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 void onPlaybackParametersChanged(PlaybackParameters playbackParameters) { .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 void playerIdle_withSetPlaybackSpeed_usesPlaybackParameterSpeedWithPitchU @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 PlaybackParameters getPlaybackParameters() { ? 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 PlaybackParameters getPlaybackParameters() { 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 PlaybackParameters getPlaybackParameters() { /* 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 Loader.LoadErrorAction onLoadError( } } + 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 98707a32261..ee7026a6694 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 void dequeueInputBufferIndex_withMediaCodecError_throwsException() throws 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 void dequeueOutputBufferIndex_withMediaCodecError_throwsException() throw 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 00000000000..ac86669ee56 --- /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 d42ab38fe02..b7ad8dc6c26 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 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.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 void render_sendsVideoSizeChangeWithCurrentFormatValues() throws Exceptio /* 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 void render_sendsVideoSizeChangeWithCurrentFormatValues() throws Exceptio /* 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 e3c41727288..73a59291b3c 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 DashDownloader(MediaItem mediaItem, CacheDataSource.Factory cacheDataSour */ 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 DashDownloader( * @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 777694e9f63..7c82bee608a 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 HlsDownloader(MediaItem mediaItem, CacheDataSource.Factory cacheDataSourc */ 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 HlsDownloader( * @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 e029b74578f..d9ec2dbe37d 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 void prepareSourceInternal(@Nullable TransferListener mediaTransferListen StreamManagerLoadable streamManagerLoadable = new StreamManagerLoadable( sdkAdsLoader, - adsLoader.configuration, + /* imaServerSideAdInsertionMediaSource= */ this, streamRequest, streamPlayer, - applicationAdErrorListener, - loadVideoTimeoutMs); + applicationAdErrorListener); loader.startLoading( streamManagerLoadable, new StreamManagerLoadableCallback(), @@ -633,7 +632,6 @@ private void setStreamManager(@Nullable StreamManager streamManager) { } this.streamManager.removeAdEventListener(componentListener); this.streamManager.destroy(); - this.streamManager = null; } this.streamManager = streamManager; if (streamManager != null) { @@ -644,6 +642,12 @@ private void setStreamManager(@Nullable StreamManager streamManager) { 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 @@ private final class StreamManagerLoadableCallback @Override public void onLoadCompleted( StreamManagerLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) { - mainHandler.post(() -> setStreamManager(checkNotNull(loadable.getStreamManager()))); setContentUri(checkNotNull(loadable.getContentUri())); } @@ -983,14 +986,12 @@ private static class StreamManagerLoadable 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 @@ private static class StreamManagerLoadable /** 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 Uri getContentUri() { 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 void onAdsManagerLoaded(AdsManagerLoadedEvent event) { 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 84bcc4cd8dc..c8ac907a8df 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 @@ private void handleRtspResponse(List message) { 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 b3d79404a2e..b96d3d54b92 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 @@ * @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 @@ public int hashCode() { 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 @@ public int hashCode() { 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 @@ private static void processAacFmtpAttribute( 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 @@ private static byte[] getInitializationDataFromParameterSet(String parameterSet) 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 @@ private static void processH265FmtpAttribute( 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 f7b7ab41b81..104f6ae9c34 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 void onSessionTimelineRequestFailed( 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 38c17af3957..ccc9663ac8c 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 @@ /** * 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 6ac0a245601..4160e6b3a91 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 @@ /** * 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 4ca632271bb..6d2ded5a4c8 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 SsDownloader( .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 SsDownloader( * @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 debb1f84a8a..7c2a9712cec 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 @@ /** * 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 a1bfbbe059b..40e4b1392c3 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 @@ /** * 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 240afed6db0..68832975f18 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 CommentHeader(String vendor, String[] comments, int length) { /** * Vorbis identification header. * - * @see Vorbis - * spec/Identification header + *

See the Vorbis + * spec/Identification header */ public static final class VorbisIdHeader { @@ -135,8 +135,9 @@ public Mode(boolean blockFlag, int windowType, int transformType, int mapping) { /** * 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 static int iLog(int x) { /** * 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 static VorbisIdHeader readVorbisIdentificationHeader(ParsableByteArray he /** * 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 static CommentHeader readVorbisCommentHeader(ParsableByteArray headerData * *

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 static boolean verifyVorbisHeaderCapturePattern( * 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 @@ private static CodeBook readBook(VorbisBitArray bitArray) throws ParserException } /** - * @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 4ccca487c82..5cb4b55da91 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 @@ private static long parseTfdt(ParsableByteArray tfdt) { 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 @@ private static int parseTrun( // 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 b5f53a35fb4..f9dea101f13 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 @@ *

  • 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 623f8fd057c..e27eefdb0fe 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.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 6a0daf58930..cb74c2b6ab9 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 @@ /** * 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 78b7a4def73..c1b9a69fec5 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 void inheritInlineAttributes() throws IOException, SubtitleDecoderExcepti * 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 27c0cc4ece6..30afcb411b8 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 @@ private void updateNotificationInternal( 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 f3045d3cda1..c1cd0c6e4aa 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 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 @@ * 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 @@ * #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 @@ * *

    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 @@ *
  • *
  • {@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 @@ * 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 @@ * {@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 static final class Builder extends BuilderBaseA 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 Builder setId(String id) { * 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 static final class ControllerInfo { * @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 9d32853015e..67acd1aca68 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 @@ public ListenableFuture sendCustomCommand( 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 a93a6cf8a79..30b76997f51 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 @@ /** * 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 @@ *

    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 @@ * #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 void onCreate() { * 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 8ff1ece685b..08c05430ee2 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 167aa23d487..503a47a0b94 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 140f15247bc..78c978f9331 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 9b026dc7896..3de48f1eee7 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 aab80ab5a70..9fc56865f39 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 b01d57052af..ff390a7a97f 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 e24ec1e9bc5..b720ead2c11 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 2694309f6a8..4cb73e9a463 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 2441a1fca81..46f4b9c07d1 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 f9bf84a2639..368e7fc64b6 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 9c1f3dc49f4..9c373bd834f 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 c8236d5ab09..79c8487541f 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 f6685e59991..24fc22bb1f0 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 0d90dca7f97..255b6a74bfb 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 0bf10abd2dc..aef8b741e64 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 0bf10abd2dc..aef8b741e64 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 0bf10abd2dc..aef8b741e64 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 c3ea5dcb072..8986aeb307c 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 1a8880ecdb1..99e401b71fa 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 4bd8e64a455..a6bd4c2becc 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 f53cc746a70..c57640f6136 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 eb8a961f982..01501389914 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 a4d6637218c..2adf214fbd5 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 fe00e2480b4..bdf99bf638a 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 bda0e76cd5b..ffc27ff8a09 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 97c0b5ae91a..96f6019adc6 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 3c2e5830395..1fcceea9478 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 99a792cdd5e..4b6951be58c 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 3fa32acbfbb..fe1eae0a7f0 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 2ff84c81f99..b1abc67ce96 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 c5507fa8a15..1385a9cf42a 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 142339374a6..b818baec7c5 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 dfc6267d5a2..249da58ac22 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 0be1260e555..0ea7666b8fc 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 d5232cf89b5..a7fcd9a24d3 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 44957a4eeda..2db5cf15a34 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 44e49caf122..bf4371548ec 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 98f22c78074..e88b1cf39e5 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 b12cf6a8941..1d595590012 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 485b09c0bfa..eb709d4c596 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 015471fe5e4..5d6fae98c3d 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 350724d9e09..081cccf540c 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 337bd7be33b..6a7310245f9 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 a8c02c98af7..3a8f7ec3e0f 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 030bb7a5499..7283e50af17 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 19487a2d570..41b8e9fa041 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 0b68af763fa..8a15c5b4ca8 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 5b9c2604fcb..0efbb93de65 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 35c8acf4769..e00fdc1a4ea 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 120211affc3..5cf79880885 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 ea9920abc44..9e6f91f7618 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 fefb5ea8354..4712949ceb7 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 8d282366c80..9955dd5503f 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 f3735156e43..b451512da36 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 86653fca741..ad0de6dc75b 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 10eddbef385..00acc8b75b1 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 0be33ad385c..d341ca707bd 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 7bd7d795207..a422f4d3137 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 dd6fc779bd4..3de84449660 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 b643018ac04..ff8bc44ce29 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 48c60ddef80..706d6b07aef 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 f2d31acf055..27342a51c71 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 ab3fa88897a..bab26908b59 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 c8ebb5046d6..bd2c4b0afd6 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 e3f7665ac3e..fb872219373 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 ad53a1d6e4d..d613f5e2e5b 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 241fb50b780..4678bf1b0c8 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 e0b65856a23..1e441e48439 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 e8a3734789a..f51ad9575ca 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 683e2aae68b..f3a5ecdb6a8 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 995740a809b..8345460f875 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 2795069c690..93fa1a469c5 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 4786db3ce7e..56f0532c175 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 423908db8a2..877371bfc19 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 fed30dd6275..1356dd54939 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 9307041370b..ebb77b0a243 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 0db79165949..bb5ef13433b 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 87716dc283e..1c6da3a07f7 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 e7603e7d905..1c10d0755b8 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 4f5ee5ddddd..d50537842a2 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 ca7bbe632b7..e0dd32ef775 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 @@ * *

    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 0b6a29a68ca..dafdad02fd5 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 dd372298485..775029f1e22 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 343e45fbb8f..82802a43cd9 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 bef7a468e5d..804acfc4d65 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 86ea0522682..90653578f00 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 2acf688bca3..ae0185ea3d3 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 05ea71c4720..041453ecb2a 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 3a898ed4bde..5870895d62c 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 2235db434bf..d8e8450755e 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 3793c330c1f..1981474f164 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 ba2e8374a9f..56b646a258d 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 bb0f85369ae..48943a497dd 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 71ff36a1a3c..d7486b4b9ac 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 ff160f6a6a5..f035521b139 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 c4ed289befe..a715576c611 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 af859027975..f312573b976 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 af859027975..f312573b976 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 af859027975..f312573b976 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 0bc0f6c546d..30e3056b704 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 924d3490388..076f352c128 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 faa44e483f4..22dd590ee02 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 90441518b1a..08fc4df563f 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 c25aa14a7c4..a7963d437f4 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 f57744643c0..2ab098e46fe 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 5faeb0b5b2a..8b5a19053e7 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 cc876a8bf30..cc2386dcddd 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 932434af285..125daae7f89 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 955ccf1f93f..d912bae2919 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 fe18f2a16ad..37113b18d42 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 320e47aef83..f58e02d5307 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 7f24082f549..66801d37092 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 f77b1831189..5171913fbe9 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 b010d3c7392..9afe0208caf 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 20f603c476c..fe4d96f02c0 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 b97ae066454..c6f6c12d00a 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 c5499212c4a..0b61a2c4342 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 f8970f64ee5..c5017b3c72b 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 2ff75b72dd6..65c048bff70 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 86d41df02bb..3296c2b5575 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 3e8fd004a16..c8da4e15a96 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 45b2417f5b8..5dcf85112df 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 b6e8d231ea4..d0a0f942c50 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 29601c0253a..818f69e1ead 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 bd0f103425b..3126375a156 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 f1d221e9bf6..020629ad608 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 0d0ec51c3c6..ef264ca156b 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 a5d13326e4d..95fe201eb9c 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 4d1e4e995a8..984452145e8 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 56273ce0e51..667ef287b93 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 303f5ca52f6..ffde009be05 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 2758851781c..f051ca62645 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 89ad8e56361..eaa9531ee9c 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 075270b6dc6..e444db3ebec 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 586aea713f8..bf8d43e1d73 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 eaf36592007..2a1f32bcdc6 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 e2579cd646a..2c882f2d37e 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 ec5bfca1589..f6489e8cef8 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 6e70f79c534..76aa421cab8 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 80a7f426695..be152306b02 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 0448518b56d..d67cc4c3a9e 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 36d9b4c01e8..d08bfeb7583 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 a78f8597187..e19521ca7b2 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 75204eed520..abb31c30ce2 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 5bc58e7ae8b..77c82d17282 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 0357753ac88..b814f210a9b 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 d20d287b1cc..23ce2ca335a 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 73edb04ff01..64b401a72d3 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 015e31701de..02308a65cac 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 36172865b90..bdae9c86327 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 124b8a99bab..7485b6b4232 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 fdb108348e3..5584dcf93c3 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 0757b261da3..04c4c2fa685 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 b2fd8b6fa4f..6058a4c36f6 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 2ffcc61d1cd..7d3307db51c 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 1e410c9712e..0c49e5d4454 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 f7639dd95b6..7b173200947 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 400d321f5e5..1749d4f5ddd 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 4fedf2ae07b..d32d6a44829 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 88a0239c57f..354f58c69ab 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 27dc2bcfd61..4249568e953 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 05d78e556e6..7752fcb7722 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