diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml
index 005d4e2e68..ab94d9922e 100644
--- a/.github/ISSUE_TEMPLATE/bug.yml
+++ b/.github/ISSUE_TEMPLATE/bug.yml
@@ -17,6 +17,7 @@ body:
label: Media3 Version
description: What version of Media3 are you using?
options:
+ - 1.0.0-rc02
- 1.0.0-rc01
- 1.0.0-beta03
- 1.0.0-beta02
diff --git a/RELEASENOTES.md b/RELEASENOTES.md
index 38c631388e..b5a6a75e46 100644
--- a/RELEASENOTES.md
+++ b/RELEASENOTES.md
@@ -1,5 +1,49 @@
# Release notes
+### 1.0.0-rc02 (2023-03-02)
+
+This release corresponds to the
+[ExoPlayer 2.18.4 release](https://github.com/google/ExoPlayer/releases/tag/r2.18.4).
+
+* Core library:
+ * Fix network type detection on API 33
+ ([#10970](https://github.com/google/ExoPlayer/issues/10970)).
+ * Fix `NullPointerException` when calling `ExoPlayer.isTunnelingEnabled`
+ ([#10977](https://github.com/google/ExoPlayer/issues/10977)).
+* Downloads:
+ * Make the maximum difference of the start time of two segments to be
+ merged configurable in `SegmentDownloader` and subclasses
+ ([#248](https://github.com/androidx/media/pull/248)).
+* Audio:
+ * Fix broken gapless MP3 playback on Samsung devices
+ ([#8594](https://github.com/google/ExoPlayer/issues/8594)).
+ * Fix bug where playback speeds set immediately after disabling audio may
+ be overridden by a previous speed change
+ ([#10882](https://github.com/google/ExoPlayer/issues/10882)).
+* Video:
+ * Map HEVC HDR10 format to `HEVCProfileMain10HDR10` instead of
+ `HEVCProfileMain10`.
+ * Add workaround for a device issue on Chromecast with Google TV and
+ Lenovo M10 FHD Plus that causes 60fps AVC streams to be marked as
+ unsupported
+ ([#10898](https://github.com/google/ExoPlayer/issues/10898)).
+ * Fix frame release performance issues when playing media with a frame
+ rate far higher than the screen refresh rate.
+* Cast:
+ * Fix transient `STATE_IDLE` when transitioning between media items
+ ([#245](https://github.com/androidx/media/issues/245)).
+* RTSP:
+ * Catch the IllegalArgumentException thrown in parsing of invalid RTSP
+ Describe response messages
+ ([#10971](https://github.com/google/ExoPlayer/issues/10971)).
+* Session:
+ * Fix a bug where notification play/pause button doesn't update with
+ player state ([#192](https://github.com/androidx/media/issues/192)).
+* IMA extension:
+ * Fix a bug which prevented DAI streams without any ads from starting
+ because the first (and in the case without ads the only) `LOADED` event
+ wasn't received.
+
### 1.0.0-rc01 (2023-02-16)
This release corresponds to the
@@ -73,17 +117,12 @@ This release corresponds to the
([#233](https://github.com/androidx/media/issues/233)).
* Make `QueueTimeline` more robust in case of a shady legacy session state
([#241](https://github.com/androidx/media/issues/241)).
-* Metadata:
- * Parse multiple null-separated values from ID3 frames, as permitted by
- ID3 v2.4.
- * Add `MediaMetadata.mediaType` to denote the type of content or the type
- of folder described by the metadata.
- * Add `MediaMetadata.isBrowsable` as a replacement for
- `MediaMetadata.folderType`. The folder type will be deprecated in the
- next release.
* Cast extension:
* Bump Cast SDK version to 21.2.0.
* IMA extension:
+ * Map `PLAYER_STATE_LOADING` to `STATE_BUFFERING`
+ ([#245](\(https://github.com/androidx/media/issues/245\)).
+* IMA extension
* Remove player listener of the `ImaServerSideAdInsertionMediaSource` on
the application thread to avoid threading issues.
* Add a property `focusSkipButtonWhenAvailable` to the
@@ -92,6 +131,8 @@ This release corresponds to the
* Add a method `focusSkipButton()` to the
`ImaServerSideAdInsertionMediaSource.AdsLoader` to programmatically
request to focus the skip button.
+ * Fix a bug which prevented playback from starting for a DAI stream
+ without any ads.
* Bump IMA SDK version to 3.29.0.
* Demo app:
* Request notification permission for download notifications at runtime
diff --git a/constants.gradle b/constants.gradle
index 39884d57c7..f1fa589e94 100644
--- a/constants.gradle
+++ b/constants.gradle
@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
project.ext {
- releaseVersion = '1.0.0-rc01'
- releaseVersionCode = 1_000_000_2_01
+ releaseVersion = '1.0.0-rc02'
+ releaseVersionCode = 1_000_000_2_02
minSdkVersion = 16
appTargetSdkVersion = 33
// API version before restricting local file access.
diff --git a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java
index 8d2a0cbde1..dbf61395bd 100644
--- a/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java
+++ b/libraries/cast/src/main/java/androidx/media3/cast/CastPlayer.java
@@ -1237,6 +1237,7 @@ public final class CastPlayer extends BasePlayer {
int receiverAppStatus = remoteMediaClient.getPlayerState();
switch (receiverAppStatus) {
case MediaStatus.PLAYER_STATE_BUFFERING:
+ case MediaStatus.PLAYER_STATE_LOADING:
return STATE_BUFFERING;
case MediaStatus.PLAYER_STATE_PLAYING:
case MediaStatus.PLAYER_STATE_PAUSED:
@@ -1299,6 +1300,7 @@ public final class CastPlayer extends BasePlayer {
return false;
}
+ @SuppressWarnings("VisibleForTests")
private static int getCastRepeatMode(@RepeatMode int repeatMode) {
switch (repeatMode) {
case REPEAT_MODE_ONE:
diff --git a/libraries/common/src/main/java/androidx/media3/common/C.java b/libraries/common/src/main/java/androidx/media3/common/C.java
index ac44ebb603..112b2ea841 100644
--- a/libraries/common/src/main/java/androidx/media3/common/C.java
+++ b/libraries/common/src/main/java/androidx/media3/common/C.java
@@ -331,7 +331,10 @@ public final class C {
*/
@UnstableApi public static final int ENCODING_OPUS = AudioFormat.ENCODING_OPUS;
- /** Represents the behavior affecting whether spatialization will be used. */
+ /**
+ * Represents the behavior affecting whether spatialization will be used. One of {@link
+ * #SPATIALIZATION_BEHAVIOR_AUTO} or {@link #SPATIALIZATION_BEHAVIOR_NEVER}.
+ */
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
diff --git a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java
index 2640a0a7e4..957ab7d219 100644
--- a/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java
+++ b/libraries/common/src/main/java/androidx/media3/common/ForwardingPlayer.java
@@ -28,7 +28,7 @@ import androidx.media3.common.util.UnstableApi;
import java.util.List;
/**
- * A {@link Player} that forwards operations to another {@link Player}. Applications can use this
+ * A {@link Player} that forwards method calls to another {@link Player}. Applications can use this
* class to suppress or modify specific operations, by overriding the respective methods.
*/
@UnstableApi
diff --git a/libraries/common/src/main/java/androidx/media3/common/MediaLibraryInfo.java b/libraries/common/src/main/java/androidx/media3/common/MediaLibraryInfo.java
index ad013f4cd3..64cdc2d996 100644
--- a/libraries/common/src/main/java/androidx/media3/common/MediaLibraryInfo.java
+++ b/libraries/common/src/main/java/androidx/media3/common/MediaLibraryInfo.java
@@ -29,11 +29,11 @@ public final class MediaLibraryInfo {
/** The version of the library expressed as a string, for example "1.2.3" or "1.2.3-beta01". */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
- public static final String VERSION = "1.0.0-rc01";
+ public static final String VERSION = "1.0.0-rc02";
/** The version of the library expressed as {@code TAG + "/" + VERSION}. */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
- public static final String VERSION_SLASHY = "AndroidXMedia3/1.0.0-rc01";
+ public static final String VERSION_SLASHY = "AndroidXMedia3/1.0.0-rc02";
/**
* The version of the library expressed as an integer, for example 1002003300.
@@ -47,7 +47,7 @@ public final class MediaLibraryInfo {
* (123-045-006-3-00).
*/
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
- public static final int VERSION_INT = 1_000_000_2_01;
+ public static final int VERSION_INT = 1_000_000_2_02;
/** Whether the library was compiled with {@link Assertions} checks enabled. */
public static final boolean ASSERTIONS_ENABLED = true;
diff --git a/libraries/common/src/main/java/androidx/media3/common/util/ColorParser.java b/libraries/common/src/main/java/androidx/media3/common/util/ColorParser.java
index 3d1f7a6349..f0550b0789 100644
--- a/libraries/common/src/main/java/androidx/media3/common/util/ColorParser.java
+++ b/libraries/common/src/main/java/androidx/media3/common/util/ColorParser.java
@@ -27,8 +27,12 @@ import java.util.regex.Pattern;
/**
* Parser for color expressions found in styling formats, e.g. TTML and CSS.
*
- * @see WebVTT CSS Styling
- * @see Timed Text Markup Language 2 (TTML2) - 10.3.5
+ *
See also:
+ *
+ *
*/
@UnstableApi
public final class ColorParser {
diff --git a/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java b/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java
index 4e97ab796c..7b64795466 100644
--- a/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java
+++ b/libraries/common/src/main/java/androidx/media3/common/util/NetworkTypeObserver.java
@@ -94,7 +94,7 @@ public final class NetworkTypeObserver {
networkType = C.NETWORK_TYPE_UNKNOWN;
IntentFilter filter = new IntentFilter();
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- Util.registerReceiverNotExported(context, new Receiver(), filter);
+ context.registerReceiver(new Receiver(), filter);
}
/**
diff --git a/libraries/common/src/main/java/androidx/media3/common/util/Util.java b/libraries/common/src/main/java/androidx/media3/common/util/Util.java
index 464db2648d..a079c488d9 100644
--- a/libraries/common/src/main/java/androidx/media3/common/util/Util.java
+++ b/libraries/common/src/main/java/androidx/media3/common/util/Util.java
@@ -206,6 +206,10 @@ public final class Util {
* apps. This will be enforced by specifying {@link Context#RECEIVER_NOT_EXPORTED} if {@link
* #SDK_INT} is 33 or above.
*
+ * Do not use this method if registering a receiver for a protected
+ * system broadcast.
+ *
* @param context The context on which {@link Context#registerReceiver} will be called.
* @param receiver The {@link BroadcastReceiver} to register. This value may be null.
* @param filter Selects the Intent broadcasts to be received.
@@ -222,33 +226,6 @@ public final class Util {
}
}
- /**
- * Registers a {@link BroadcastReceiver} that's not intended to receive broadcasts from other
- * apps. This will be enforced by specifying {@link Context#RECEIVER_NOT_EXPORTED} if {@link
- * #SDK_INT} is 33 or above.
- *
- * @param context The context on which {@link Context#registerReceiver} will be called.
- * @param receiver The {@link BroadcastReceiver} to register. This value may be null.
- * @param filter Selects the Intent broadcasts to be received.
- * @param handler Handler identifying the thread that will receive the Intent.
- * @return The first sticky intent found that matches {@code filter}, or null if there are none.
- */
- @UnstableApi
- @Nullable
- public static Intent registerReceiverNotExported(
- Context context, BroadcastReceiver receiver, IntentFilter filter, Handler handler) {
- if (SDK_INT < 33) {
- return context.registerReceiver(receiver, filter, /* broadcastPermission= */ null, handler);
- } else {
- return context.registerReceiver(
- receiver,
- filter,
- /* broadcastPermission= */ null,
- handler,
- Context.RECEIVER_NOT_EXPORTED);
- }
- }
-
/**
* Calls {@link Context#startForegroundService(Intent)} if {@link #SDK_INT} is 26 or higher, or
* {@link Context#startService(Intent)} otherwise.
diff --git a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CachedContentIndex.java b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CachedContentIndex.java
index 0d403fb173..36c2d45429 100644
--- a/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CachedContentIndex.java
+++ b/libraries/datasource/src/main/java/androidx/media3/datasource/cache/CachedContentIndex.java
@@ -794,11 +794,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
public boolean exists() throws DatabaseIOException {
- return VersionTable.getVersion(
- databaseProvider.getReadableDatabase(),
- VersionTable.FEATURE_CACHE_CONTENT_METADATA,
- checkNotNull(hexUid))
- != VersionTable.VERSION_UNSET;
+ try {
+ return VersionTable.getVersion(
+ databaseProvider.getReadableDatabase(),
+ VersionTable.FEATURE_CACHE_CONTENT_METADATA,
+ checkNotNull(hexUid))
+ != VersionTable.VERSION_UNSET;
+ } catch (SQLException e) {
+ throw new DatabaseIOException(e);
+ }
}
@Override
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioBecomingNoisyManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioBecomingNoisyManager.java
index 625c6090b6..04fd1482bd 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioBecomingNoisyManager.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/AudioBecomingNoisyManager.java
@@ -21,7 +21,6 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
import android.os.Handler;
-import androidx.media3.common.util.Util;
/* package */ final class AudioBecomingNoisyManager {
@@ -47,8 +46,8 @@ import androidx.media3.common.util.Util;
*/
public void setEnabled(boolean enabled) {
if (enabled && !receiverRegistered) {
- Util.registerReceiverNotExported(
- context, receiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
+ context.registerReceiver(
+ receiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
receiverRegistered = true;
} else if (!enabled && receiverRegistered) {
context.unregisterReceiver(receiver);
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java
index 525b2df9c4..10c64a7e98 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImpl.java
@@ -1724,8 +1724,9 @@ import java.util.concurrent.TimeoutException;
@Override
public boolean isTunnelingEnabled() {
verifyApplicationThread();
- for (RendererConfiguration config : playbackInfo.trackSelectorResult.rendererConfigurations) {
- if (config.tunneling) {
+ for (@Nullable
+ RendererConfiguration config : playbackInfo.trackSelectorResult.rendererConfigurations) {
+ if (config != null && config.tunneling) {
return true;
}
}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java
index 6c3c64075b..e84ea1e7e0 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/ExoPlayerImplInternal.java
@@ -946,7 +946,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
livePlaybackSpeedControl.getAdjustedPlaybackSpeed(
getCurrentLiveOffsetUs(), getTotalBufferedDurationUs());
if (mediaClock.getPlaybackParameters().speed != adjustedSpeed) {
- mediaClock.setPlaybackParameters(playbackInfo.playbackParameters.withSpeed(adjustedSpeed));
+ setMediaClockPlaybackParameters(playbackInfo.playbackParameters.withSpeed(adjustedSpeed));
handlePlaybackParameters(
playbackInfo.playbackParameters,
/* currentPlaybackSpeed= */ mediaClock.getPlaybackParameters().speed,
@@ -956,6 +956,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
+ private void setMediaClockPlaybackParameters(PlaybackParameters playbackParameters) {
+ // Previously sent speed updates from the media clock now become stale.
+ handler.removeMessages(MSG_PLAYBACK_PARAMETERS_CHANGED_INTERNAL);
+ mediaClock.setPlaybackParameters(playbackParameters);
+ }
+
private void notifyTrackSelectionRebuffer() {
MediaPeriodHolder periodHolder = queue.getPlayingPeriod();
while (periodHolder != null) {
@@ -1342,7 +1348,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
private void setPlaybackParametersInternal(PlaybackParameters playbackParameters)
throws ExoPlaybackException {
- mediaClock.setPlaybackParameters(playbackParameters);
+ setMediaClockPlaybackParameters(playbackParameters);
handlePlaybackParameters(mediaClock.getPlaybackParameters(), /* acknowledgeCommand= */ true);
}
@@ -1657,7 +1663,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
nextPendingMessageIndexHint = nextPendingMessageIndex;
}
- private void ensureStopped(Renderer renderer) throws ExoPlaybackException {
+ private void ensureStopped(Renderer renderer) {
if (renderer.getState() == Renderer.STATE_STARTED) {
renderer.stop();
}
@@ -1914,14 +1920,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
MediaPeriodId newPeriodId,
Timeline oldTimeline,
MediaPeriodId oldPeriodId,
- long positionForTargetOffsetOverrideUs) {
+ long positionForTargetOffsetOverrideUs)
+ throws ExoPlaybackException {
if (!shouldUseLivePlaybackSpeedControl(newTimeline, newPeriodId)) {
// Live playback speed control is unused for the current period, reset speed to user-defined
// playback parameters or 1.0 for ad playback.
PlaybackParameters targetPlaybackParameters =
newPeriodId.isAd() ? PlaybackParameters.DEFAULT : playbackInfo.playbackParameters;
if (!mediaClock.getPlaybackParameters().equals(targetPlaybackParameters)) {
- mediaClock.setPlaybackParameters(targetPlaybackParameters);
+ setMediaClockPlaybackParameters(targetPlaybackParameters);
+ handlePlaybackParameters(
+ playbackInfo.playbackParameters,
+ targetPlaybackParameters.speed,
+ /* updatePlaybackInfo= */ false,
+ /* acknowledgeCommand= */ false);
}
return;
}
@@ -1970,7 +1982,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
return maxReadPositionUs;
}
- private void updatePeriods() throws ExoPlaybackException, IOException {
+ private void updatePeriods() throws ExoPlaybackException {
if (playbackInfo.timeline.isEmpty() || !mediaSourceList.isPrepared()) {
// No periods available.
return;
@@ -2012,7 +2024,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
}
}
- private void maybeUpdateReadingPeriod() {
+ private void maybeUpdateReadingPeriod() throws ExoPlaybackException {
@Nullable MediaPeriodHolder readingPeriodHolder = queue.getReadingPeriod();
if (readingPeriodHolder == null) {
return;
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java
index 1fc7dc4828..c5a8230154 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/StreamVolumeManager.java
@@ -75,7 +75,7 @@ import androidx.media3.common.util.Util;
VolumeChangeReceiver receiver = new VolumeChangeReceiver();
IntentFilter filter = new IntentFilter(VOLUME_CHANGED_ACTION);
try {
- Util.registerReceiverNotExported(applicationContext, receiver, filter);
+ applicationContext.registerReceiver(receiver, filter);
this.receiver = receiver;
} catch (RuntimeException e) {
Log.w(TAG, "Error registering stream volume receiver", e);
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilities.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilities.java
index 6cb10f0731..9888db45a6 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilities.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilities.java
@@ -88,8 +88,8 @@ public final class AudioCapabilities {
@SuppressWarnings("InlinedApi")
public static AudioCapabilities getCapabilities(Context context) {
Intent intent =
- Util.registerReceiverNotExported(
- context, /* receiver= */ null, new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG));
+ context.registerReceiver(
+ /* receiver= */ null, new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG));
return getCapabilities(context, intent);
}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilitiesReceiver.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilitiesReceiver.java
index 1cfef2accc..671ddb5aa6 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilitiesReceiver.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioCapabilitiesReceiver.java
@@ -93,7 +93,9 @@ public final class AudioCapabilitiesReceiver {
@Nullable Intent stickyIntent = null;
if (receiver != null) {
IntentFilter intentFilter = new IntentFilter(AudioManager.ACTION_HDMI_AUDIO_PLUG);
- stickyIntent = Util.registerReceiverNotExported(context, receiver, intentFilter, handler);
+ stickyIntent =
+ context.registerReceiver(
+ receiver, intentFilter, /* broadcastPermission= */ null, handler);
}
audioCapabilities = AudioCapabilities.getCapabilities(context, stickyIntent);
return audioCapabilities;
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java
index 952c5fb8b6..414cf24d40 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/audio/AudioTrackPositionTracker.java
@@ -15,6 +15,7 @@
*/
package androidx.media3.exoplayer.audio;
+import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Util.castNonNull;
import static java.lang.Math.max;
import static java.lang.Math.min;
@@ -26,7 +27,6 @@ import android.os.SystemClock;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
-import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Util;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
@@ -146,6 +146,9 @@ import java.lang.reflect.Method;
/** The duration of time used to smooth over an adjustment between position sampling modes. */
private static final long MODE_SWITCH_SMOOTHING_DURATION_US = C.MICROS_PER_SECOND;
+ /** Minimum update interval for getting the raw playback head position, in milliseconds. */
+ private static final long RAW_PLAYBACK_HEAD_POSITION_UPDATE_INTERVAL_MS = 5;
+
private static final long FORCE_RESET_WORKAROUND_TIMEOUT_MS = 200;
private static final int MAX_PLAYHEAD_OFFSET_COUNT = 10;
@@ -174,7 +177,8 @@ import java.lang.reflect.Method;
private boolean isOutputPcm;
private long lastLatencySampleTimeUs;
- private long lastRawPlaybackHeadPosition;
+ private long lastRawPlaybackHeadPositionSampleTimeMs;
+ private long rawPlaybackHeadPosition;
private long rawPlaybackHeadWrapCount;
private long passthroughWorkaroundPauseOffset;
private int nextPlayheadOffsetIndex;
@@ -199,7 +203,7 @@ import java.lang.reflect.Method;
* @param listener A listener for position tracking events.
*/
public AudioTrackPositionTracker(Listener listener) {
- this.listener = Assertions.checkNotNull(listener);
+ this.listener = checkNotNull(listener);
if (Util.SDK_INT >= 18) {
try {
getLatencyMethod = AudioTrack.class.getMethod("getLatency", (Class>[]) null);
@@ -235,7 +239,7 @@ import java.lang.reflect.Method;
needsPassthroughWorkarounds = isPassthrough && needsPassthroughWorkarounds(outputEncoding);
isOutputPcm = Util.isEncodingLinearPcm(outputEncoding);
bufferSizeUs = isOutputPcm ? framesToDurationUs(bufferSize / outputPcmFrameSize) : C.TIME_UNSET;
- lastRawPlaybackHeadPosition = 0;
+ rawPlaybackHeadPosition = 0;
rawPlaybackHeadWrapCount = 0;
passthroughWorkaroundPauseOffset = 0;
hasData = false;
@@ -253,10 +257,11 @@ import java.lang.reflect.Method;
if (audioTimestampPoller != null) {
audioTimestampPoller.reset();
}
+ resetSyncParams();
}
public long getCurrentPositionUs(boolean sourceEnded) {
- if (Assertions.checkNotNull(this.audioTrack).getPlayState() == PLAYSTATE_PLAYING) {
+ if (checkNotNull(this.audioTrack).getPlayState() == PLAYSTATE_PLAYING) {
maybeSampleSyncParams();
}
@@ -264,7 +269,7 @@ import java.lang.reflect.Method;
// Otherwise, derive a smoothed position by sampling the track's frame position.
long systemTimeUs = System.nanoTime() / 1000;
long positionUs;
- AudioTimestampPoller audioTimestampPoller = Assertions.checkNotNull(this.audioTimestampPoller);
+ AudioTimestampPoller audioTimestampPoller = checkNotNull(this.audioTimestampPoller);
boolean useGetTimestampMode = audioTimestampPoller.hasAdvancingTimestamp();
if (useGetTimestampMode) {
// Calculate the speed-adjusted position using the timestamp (which may be in the future).
@@ -282,7 +287,9 @@ import java.lang.reflect.Method;
// getPlaybackHeadPositionUs() only has a granularity of ~20 ms, so we base the position off
// the system clock (and a smoothed offset between it and the playhead position) so as to
// prevent jitter in the reported positions.
- positionUs = systemTimeUs + smoothedPlayheadOffsetUs;
+ positionUs =
+ Util.getMediaDurationForPlayoutDuration(
+ systemTimeUs + smoothedPlayheadOffsetUs, audioTrackPlaybackSpeed);
}
if (!sourceEnded) {
positionUs = max(0, positionUs - latencyUs);
@@ -329,12 +336,12 @@ import java.lang.reflect.Method;
/** Starts position tracking. Must be called immediately before {@link AudioTrack#play()}. */
public void start() {
- Assertions.checkNotNull(audioTimestampPoller).reset();
+ checkNotNull(audioTimestampPoller).reset();
}
/** Returns whether the audio track is in the playing state. */
public boolean isPlaying() {
- return Assertions.checkNotNull(audioTrack).getPlayState() == PLAYSTATE_PLAYING;
+ return checkNotNull(audioTrack).getPlayState() == PLAYSTATE_PLAYING;
}
/**
@@ -345,7 +352,7 @@ import java.lang.reflect.Method;
* @return Whether the caller can write data to the track.
*/
public boolean mayHandleBuffer(long writtenFrames) {
- @PlayState int playState = Assertions.checkNotNull(audioTrack).getPlayState();
+ @PlayState int playState = checkNotNull(audioTrack).getPlayState();
if (needsPassthroughWorkarounds) {
// An AC-3 audio track continues to play data written while it is paused. Stop writing so its
// buffer empties. See [Internal: b/18899620].
@@ -426,7 +433,7 @@ import java.lang.reflect.Method;
if (stopTimestampUs == C.TIME_UNSET) {
// The audio track is going to be paused, so reset the timestamp poller to ensure it doesn't
// supply an advancing position.
- Assertions.checkNotNull(audioTimestampPoller).reset();
+ checkNotNull(audioTimestampPoller).reset();
return true;
}
// We've handled the end of the stream already, so there's no need to pause the track.
@@ -444,15 +451,17 @@ import java.lang.reflect.Method;
}
private void maybeSampleSyncParams() {
- long playbackPositionUs = getPlaybackHeadPositionUs();
- if (playbackPositionUs == 0) {
- // The AudioTrack hasn't output anything yet.
- return;
- }
long systemTimeUs = System.nanoTime() / 1000;
if (systemTimeUs - lastPlayheadSampleTimeUs >= MIN_PLAYHEAD_OFFSET_SAMPLE_INTERVAL_US) {
+ long playbackPositionUs = getPlaybackHeadPositionUs();
+ if (playbackPositionUs == 0) {
+ // The AudioTrack hasn't output anything yet.
+ return;
+ }
// Take a new sample and update the smoothed offset between the system clock and the playhead.
- playheadOffsets[nextPlayheadOffsetIndex] = playbackPositionUs - systemTimeUs;
+ playheadOffsets[nextPlayheadOffsetIndex] =
+ Util.getPlayoutDurationForMediaDuration(playbackPositionUs, audioTrackPlaybackSpeed)
+ - systemTimeUs;
nextPlayheadOffsetIndex = (nextPlayheadOffsetIndex + 1) % MAX_PLAYHEAD_OFFSET_COUNT;
if (playheadOffsetCount < MAX_PLAYHEAD_OFFSET_COUNT) {
playheadOffsetCount++;
@@ -470,12 +479,12 @@ import java.lang.reflect.Method;
return;
}
- maybePollAndCheckTimestamp(systemTimeUs, playbackPositionUs);
+ maybePollAndCheckTimestamp(systemTimeUs);
maybeUpdateLatency(systemTimeUs);
}
- private void maybePollAndCheckTimestamp(long systemTimeUs, long playbackPositionUs) {
- AudioTimestampPoller audioTimestampPoller = Assertions.checkNotNull(this.audioTimestampPoller);
+ private void maybePollAndCheckTimestamp(long systemTimeUs) {
+ AudioTimestampPoller audioTimestampPoller = checkNotNull(this.audioTimestampPoller);
if (!audioTimestampPoller.maybePollTimestamp(systemTimeUs)) {
return;
}
@@ -483,6 +492,7 @@ import java.lang.reflect.Method;
// Check the timestamp and accept/reject it.
long audioTimestampSystemTimeUs = audioTimestampPoller.getTimestampSystemTimeUs();
long audioTimestampPositionFrames = audioTimestampPoller.getTimestampPositionFrames();
+ long playbackPositionUs = getPlaybackHeadPositionUs();
if (Math.abs(audioTimestampSystemTimeUs - systemTimeUs) > MAX_AUDIO_TIMESTAMP_OFFSET_US) {
listener.onSystemTimeUsMismatch(
audioTimestampPositionFrames,
@@ -511,8 +521,7 @@ import java.lang.reflect.Method;
// Compute the audio track latency, excluding the latency due to the buffer (leaving
// latency due to the mixer and audio hardware driver).
latencyUs =
- castNonNull((Integer) getLatencyMethod.invoke(Assertions.checkNotNull(audioTrack)))
- * 1000L
+ castNonNull((Integer) getLatencyMethod.invoke(checkNotNull(audioTrack))) * 1000L
- bufferSizeUs;
// Check that the latency is non-negative.
latencyUs = max(latencyUs, 0);
@@ -550,7 +559,7 @@ import java.lang.reflect.Method;
*/
private boolean forceHasPendingData() {
return needsPassthroughWorkarounds
- && Assertions.checkNotNull(audioTrack).getPlayState() == AudioTrack.PLAYSTATE_PAUSED
+ && checkNotNull(audioTrack).getPlayState() == AudioTrack.PLAYSTATE_PAUSED
&& getPlaybackHeadPosition() == 0;
}
@@ -576,34 +585,44 @@ import java.lang.reflect.Method;
* @return The playback head position, in frames.
*/
private long getPlaybackHeadPosition() {
- AudioTrack audioTrack = Assertions.checkNotNull(this.audioTrack);
+ long currentTimeMs = SystemClock.elapsedRealtime();
if (stopTimestampUs != C.TIME_UNSET) {
// Simulate the playback head position up to the total number of frames submitted.
- long elapsedTimeSinceStopUs = (SystemClock.elapsedRealtime() * 1000) - stopTimestampUs;
- long framesSinceStop = (elapsedTimeSinceStopUs * outputSampleRate) / C.MICROS_PER_SECOND;
+ long elapsedTimeSinceStopUs = (currentTimeMs * 1000) - stopTimestampUs;
+ long mediaTimeSinceStopUs =
+ Util.getMediaDurationForPlayoutDuration(elapsedTimeSinceStopUs, audioTrackPlaybackSpeed);
+ long framesSinceStop = (mediaTimeSinceStopUs * outputSampleRate) / C.MICROS_PER_SECOND;
return min(endPlaybackHeadPosition, stopPlaybackHeadPosition + framesSinceStop);
}
+ if (currentTimeMs - lastRawPlaybackHeadPositionSampleTimeMs
+ >= RAW_PLAYBACK_HEAD_POSITION_UPDATE_INTERVAL_MS) {
+ updateRawPlaybackHeadPosition(currentTimeMs);
+ lastRawPlaybackHeadPositionSampleTimeMs = currentTimeMs;
+ }
+ return rawPlaybackHeadPosition + (rawPlaybackHeadWrapCount << 32);
+ }
+ private void updateRawPlaybackHeadPosition(long currentTimeMs) {
+ AudioTrack audioTrack = checkNotNull(this.audioTrack);
int state = audioTrack.getPlayState();
if (state == PLAYSTATE_STOPPED) {
- // The audio track hasn't been started.
- return 0;
+ // The audio track hasn't been started. Keep initial zero timestamp.
+ return;
}
-
long rawPlaybackHeadPosition = 0xFFFFFFFFL & audioTrack.getPlaybackHeadPosition();
if (needsPassthroughWorkarounds) {
// Work around an issue with passthrough/direct AudioTracks on platform API versions 21/22
// where the playback head position jumps back to zero on paused passthrough/direct audio
// tracks. See [Internal: b/19187573].
if (state == PLAYSTATE_PAUSED && rawPlaybackHeadPosition == 0) {
- passthroughWorkaroundPauseOffset = lastRawPlaybackHeadPosition;
+ passthroughWorkaroundPauseOffset = this.rawPlaybackHeadPosition;
}
rawPlaybackHeadPosition += passthroughWorkaroundPauseOffset;
}
if (Util.SDK_INT <= 29) {
if (rawPlaybackHeadPosition == 0
- && lastRawPlaybackHeadPosition > 0
+ && this.rawPlaybackHeadPosition > 0
&& state == PLAYSTATE_PLAYING) {
// If connecting a Bluetooth audio device fails, the AudioTrack may be left in a state
// where its Java API is in the playing state, but the native track is stopped. When this
@@ -611,19 +630,18 @@ import java.lang.reflect.Method;
// playback head position and force the track to be reset after
// {@link #FORCE_RESET_WORKAROUND_TIMEOUT_MS} has elapsed.
if (forceResetWorkaroundTimeMs == C.TIME_UNSET) {
- forceResetWorkaroundTimeMs = SystemClock.elapsedRealtime();
+ forceResetWorkaroundTimeMs = currentTimeMs;
}
- return lastRawPlaybackHeadPosition;
+ return;
} else {
forceResetWorkaroundTimeMs = C.TIME_UNSET;
}
}
- if (lastRawPlaybackHeadPosition > rawPlaybackHeadPosition) {
+ if (this.rawPlaybackHeadPosition > rawPlaybackHeadPosition) {
// The value must have wrapped around.
rawPlaybackHeadWrapCount++;
}
- lastRawPlaybackHeadPosition = rawPlaybackHeadPosition;
- return rawPlaybackHeadPosition + (rawPlaybackHeadWrapCount << 32);
+ this.rawPlaybackHeadPosition = rawPlaybackHeadPosition;
}
}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java
index 91aa86ca06..9377aaace6 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapter.java
@@ -191,11 +191,13 @@ import java.nio.ByteBuffer;
@Override
public int dequeueInputBufferIndex() {
+ bufferEnqueuer.maybeThrowException();
return asynchronousMediaCodecCallback.dequeueInputBufferIndex();
}
@Override
public int dequeueOutputBufferIndex(MediaCodec.BufferInfo bufferInfo) {
+ bufferEnqueuer.maybeThrowException();
return asynchronousMediaCodecCallback.dequeueOutputBufferIndex(bufferInfo);
}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecBufferEnqueuer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecBufferEnqueuer.java
index 10f0a24e15..bba85feff9 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecBufferEnqueuer.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecBufferEnqueuer.java
@@ -162,7 +162,8 @@ class AsynchronousMediaCodecBufferEnqueuer {
blockUntilHandlerThreadIsIdle();
}
- private void maybeThrowException() {
+ /** Throw any exception that occurred on the enqueuer's background queueing thread. */
+ public void maybeThrowException() {
@Nullable RuntimeException exception = pendingRuntimeException.getAndSet(null);
if (exception != null) {
throw exception;
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java
index 67c6649d27..e950d3ac73 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecCallback.java
@@ -263,11 +263,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
// else, pendingOutputFormat may already be non-null following a previous flush, and remains
// set in this case.
+ // mediaCodecException is not reset to null. If the codec has raised an error, then it remains
+ // in FAILED_STATE even after flushing.
availableInputBuffers.clear();
availableOutputBuffers.clear();
bufferInfos.clear();
formats.clear();
- mediaCodecException = null;
}
@GuardedBy("lock")
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java
index e49a9a5a3c..d36f9eb07b 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecInfo.java
@@ -857,7 +857,7 @@ public final class MediaCodecInfo {
* @param name The name of the codec.
* @return Whether to enable the workaround.
*/
- private static final boolean needsRotatedVerticalResolutionWorkaround(String name) {
+ private static boolean needsRotatedVerticalResolutionWorkaround(String name) {
if ("OMX.MTK.VIDEO.DECODER.HEVC".equals(name) && "mcv5a".equals(Util.DEVICE)) {
// See https://github.com/google/ExoPlayer/issues/6612.
return false;
@@ -876,6 +876,17 @@ public final class MediaCodecInfo {
&& ("sailfish".equals(Util.DEVICE) || "marlin".equals(Util.DEVICE));
}
+ /** Whether the device is known to have wrong {@link PerformancePoint} declarations. */
+ private static boolean needsIgnorePerformancePointsWorkaround() {
+ // See https://github.com/google/ExoPlayer/issues/10898 and [internal ref: b/267324685].
+ return /* Chromecast with Google TV */ Util.DEVICE.equals("sabrina")
+ || Util.DEVICE.equals("boreal")
+ /* Lenovo Tablet M10 FHD Plus */
+ || Util.MODEL.startsWith("Lenovo TB-X605")
+ || Util.MODEL.startsWith("Lenovo TB-X606")
+ || Util.MODEL.startsWith("Lenovo TB-X616");
+ }
+
/** Possible outcomes of evaluating PerformancePoint coverage */
@Documented
@Retention(RetentionPolicy.SOURCE)
@@ -900,7 +911,9 @@ public final class MediaCodecInfo {
VideoCapabilities videoCapabilities, int width, int height, double frameRate) {
List performancePointList =
videoCapabilities.getSupportedPerformancePoints();
- if (performancePointList == null || performancePointList.isEmpty()) {
+ if (performancePointList == null
+ || performancePointList.isEmpty()
+ || needsIgnorePerformancePointsWorkaround()) {
return COVERAGE_RESULT_NO_EMPTY_LIST;
}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java
index 815b4c369e..a752432fa0 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecRenderer.java
@@ -208,10 +208,6 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
*/
private static final long MAX_CODEC_HOTSWAP_TIME_MS = 1000;
- // Generally there is zero or one pending output stream offset. We track more offsets to allow for
- // pending output streams that have fewer frames than the codec latency.
- private static final int MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT = 10;
-
@Documented
@Retention(RetentionPolicy.SOURCE)
@Target(TYPE_USE)
@@ -303,12 +299,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private final DecoderInputBuffer buffer;
private final DecoderInputBuffer bypassSampleBuffer;
private final BatchBuffer bypassBatchBuffer;
- private final TimedValueQueue formatQueue;
private final ArrayList decodeOnlyPresentationTimestamps;
private final MediaCodec.BufferInfo outputBufferInfo;
- private final long[] pendingOutputStreamStartPositionsUs;
- private final long[] pendingOutputStreamOffsetsUs;
- private final long[] pendingOutputStreamSwitchTimesUs;
+ private final ArrayDeque pendingOutputStreamChanges;
@Nullable private Format inputFormat;
@Nullable private Format outputFormat;
@@ -363,9 +356,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private boolean pendingOutputEndOfStream;
@Nullable private ExoPlaybackException pendingPlaybackException;
protected DecoderCounters decoderCounters;
- private long outputStreamStartPositionUs;
- private long outputStreamOffsetUs;
- private int pendingOutputStreamOffsetCount;
+ private OutputStreamInfo outputStreamInfo;
+ private long lastProcessedOutputBufferTimeUs;
+ private boolean needToNotifyOutputFormatChangeAfterStreamChange;
/**
* @param trackType The {@link C.TrackType track type} that the renderer handles.
@@ -392,17 +385,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
buffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DISABLED);
bypassSampleBuffer = new DecoderInputBuffer(DecoderInputBuffer.BUFFER_REPLACEMENT_MODE_DIRECT);
bypassBatchBuffer = new BatchBuffer();
- formatQueue = new TimedValueQueue<>();
decodeOnlyPresentationTimestamps = new ArrayList<>();
outputBufferInfo = new MediaCodec.BufferInfo();
currentPlaybackSpeed = 1f;
targetPlaybackSpeed = 1f;
renderTimeLimitMs = C.TIME_UNSET;
- pendingOutputStreamStartPositionsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
- pendingOutputStreamOffsetsUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
- pendingOutputStreamSwitchTimesUs = new long[MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT];
- outputStreamStartPositionUs = C.TIME_UNSET;
- setOutputStreamOffsetUs(C.TIME_UNSET);
+ pendingOutputStreamChanges = new ArrayDeque<>();
+ setOutputStreamInfo(OutputStreamInfo.UNSET);
// MediaCodec outputs audio buffers in native endian:
// https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers
// and code called from MediaCodecAudioRenderer.processOutputBuffer expects this endianness.
@@ -419,6 +408,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
codecHotswapDeadlineMs = C.TIME_UNSET;
largestQueuedPresentationTimeUs = C.TIME_UNSET;
lastBufferInStreamPresentationTimeUs = C.TIME_UNSET;
+ lastProcessedOutputBufferTimeUs = C.TIME_UNSET;
codecDrainState = DRAIN_STATE_NONE;
codecDrainAction = DRAIN_ACTION_NONE;
}
@@ -606,13 +596,15 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
protected final void updateOutputFormatForTime(long presentationTimeUs)
throws ExoPlaybackException {
boolean outputFormatChanged = false;
- @Nullable Format format = formatQueue.pollFloor(presentationTimeUs);
- if (format == null && codecOutputMediaFormatChanged) {
- // If the codec's output MediaFormat has changed then there should be a corresponding Format
- // change, which we've not found. Check the Format queue in case the corresponding
- // presentation timestamp is greater than presentationTimeUs, which can happen for some codecs
- // [Internal ref: b/162719047].
- format = formatQueue.pollFirst();
+ @Nullable Format format = outputStreamInfo.formatQueue.pollFloor(presentationTimeUs);
+ if (format == null
+ && needToNotifyOutputFormatChangeAfterStreamChange
+ && codecOutputMediaFormat != null) {
+ // After a stream change or after the initial start, there should be an input format change,
+ // which we've not found. Check the Format queue in case the corresponding presentation
+ // timestamp is greater than presentationTimeUs, which can happen for some codecs
+ // [Internal ref: b/162719047 and https://github.com/google/ExoPlayer/issues/8594].
+ format = outputStreamInfo.formatQueue.pollFirst();
}
if (format != null) {
outputFormat = format;
@@ -621,6 +613,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
if (outputFormatChanged || (codecOutputMediaFormatChanged && outputFormat != null)) {
onOutputFormatChanged(outputFormat, codecOutputMediaFormat);
codecOutputMediaFormatChanged = false;
+ needToNotifyOutputFormatChangeAfterStreamChange = false;
}
}
@@ -648,23 +641,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
@Override
protected void onStreamChanged(Format[] formats, long startPositionUs, long offsetUs)
throws ExoPlaybackException {
- if (this.outputStreamOffsetUs == C.TIME_UNSET) {
- checkState(this.outputStreamStartPositionUs == C.TIME_UNSET);
- this.outputStreamStartPositionUs = startPositionUs;
- setOutputStreamOffsetUs(offsetUs);
+ if (outputStreamInfo.streamOffsetUs == C.TIME_UNSET
+ || (pendingOutputStreamChanges.isEmpty()
+ && lastProcessedOutputBufferTimeUs != C.TIME_UNSET
+ && lastProcessedOutputBufferTimeUs >= largestQueuedPresentationTimeUs)) {
+ // This is the first stream, or the previous has been fully output already.
+ setOutputStreamInfo(
+ new OutputStreamInfo(
+ /* previousStreamLastBufferTimeUs= */ C.TIME_UNSET, startPositionUs, offsetUs));
} else {
- if (pendingOutputStreamOffsetCount == pendingOutputStreamOffsetsUs.length) {
- Log.w(
- TAG,
- "Too many stream changes, so dropping offset: "
- + pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1]);
- } else {
- pendingOutputStreamOffsetCount++;
- }
- pendingOutputStreamStartPositionsUs[pendingOutputStreamOffsetCount - 1] = startPositionUs;
- pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1] = offsetUs;
- pendingOutputStreamSwitchTimesUs[pendingOutputStreamOffsetCount - 1] =
- largestQueuedPresentationTimeUs;
+ pendingOutputStreamChanges.add(
+ new OutputStreamInfo(largestQueuedPresentationTimeUs, startPositionUs, offsetUs));
}
}
@@ -683,16 +670,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
// If there is a format change on the input side still pending propagation to the output, we
// need to queue a format next time a buffer is read. This is because we may not read a new
// input format after the position reset.
- if (formatQueue.size() > 0) {
+ if (outputStreamInfo.formatQueue.size() > 0) {
waitingForFirstSampleInFormat = true;
}
- formatQueue.clear();
- if (pendingOutputStreamOffsetCount != 0) {
- setOutputStreamOffsetUs(pendingOutputStreamOffsetsUs[pendingOutputStreamOffsetCount - 1]);
- outputStreamStartPositionUs =
- pendingOutputStreamStartPositionsUs[pendingOutputStreamOffsetCount - 1];
- pendingOutputStreamOffsetCount = 0;
- }
+ outputStreamInfo.formatQueue.clear();
+ pendingOutputStreamChanges.clear();
}
@Override
@@ -706,9 +688,8 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
@Override
protected void onDisabled() {
inputFormat = null;
- outputStreamStartPositionUs = C.TIME_UNSET;
- setOutputStreamOffsetUs(C.TIME_UNSET);
- pendingOutputStreamOffsetCount = 0;
+ setOutputStreamInfo(OutputStreamInfo.UNSET);
+ pendingOutputStreamChanges.clear();
flushOrReleaseCodec();
}
@@ -897,6 +878,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
decodeOnlyPresentationTimestamps.clear();
largestQueuedPresentationTimeUs = C.TIME_UNSET;
lastBufferInStreamPresentationTimeUs = C.TIME_UNSET;
+ lastProcessedOutputBufferTimeUs = C.TIME_UNSET;
if (c2Mp3TimestampTracker != null) {
c2Mp3TimestampTracker.reset();
}
@@ -1353,7 +1335,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
decodeOnlyPresentationTimestamps.add(presentationTimeUs);
}
if (waitingForFirstSampleInFormat) {
- formatQueue.add(presentationTimeUs, inputFormat);
+ if (!pendingOutputStreamChanges.isEmpty()) {
+ pendingOutputStreamChanges.peekLast().formatQueue.add(presentationTimeUs, inputFormat);
+ } else {
+ outputStreamInfo.formatQueue.add(presentationTimeUs, inputFormat);
+ }
waitingForFirstSampleInFormat = false;
}
largestQueuedPresentationTimeUs = max(largestQueuedPresentationTimeUs, presentationTimeUs);
@@ -1593,29 +1579,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
*/
@CallSuper
protected void onProcessedOutputBuffer(long presentationTimeUs) {
- while (pendingOutputStreamOffsetCount != 0
- && presentationTimeUs >= pendingOutputStreamSwitchTimesUs[0]) {
- outputStreamStartPositionUs = pendingOutputStreamStartPositionsUs[0];
- setOutputStreamOffsetUs(pendingOutputStreamOffsetsUs[0]);
- pendingOutputStreamOffsetCount--;
- System.arraycopy(
- pendingOutputStreamStartPositionsUs,
- /* srcPos= */ 1,
- pendingOutputStreamStartPositionsUs,
- /* destPos= */ 0,
- pendingOutputStreamOffsetCount);
- System.arraycopy(
- pendingOutputStreamOffsetsUs,
- /* srcPos= */ 1,
- pendingOutputStreamOffsetsUs,
- /* destPos= */ 0,
- pendingOutputStreamOffsetCount);
- System.arraycopy(
- pendingOutputStreamSwitchTimesUs,
- /* srcPos= */ 1,
- pendingOutputStreamSwitchTimesUs,
- /* destPos= */ 0,
- pendingOutputStreamOffsetCount);
+ lastProcessedOutputBufferTimeUs = presentationTimeUs;
+ if (!pendingOutputStreamChanges.isEmpty()
+ && presentationTimeUs >= pendingOutputStreamChanges.peek().previousStreamLastBufferTimeUs) {
+ setOutputStreamInfo(pendingOutputStreamChanges.poll());
onProcessedStreamChange();
}
}
@@ -2062,13 +2029,14 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
* boolean, Format)} to get the playback position with respect to the media.
*/
protected final long getOutputStreamOffsetUs() {
- return outputStreamOffsetUs;
+ return outputStreamInfo.streamOffsetUs;
}
- private void setOutputStreamOffsetUs(long outputStreamOffsetUs) {
- this.outputStreamOffsetUs = outputStreamOffsetUs;
- if (outputStreamOffsetUs != C.TIME_UNSET) {
- onOutputStreamOffsetUsChanged(outputStreamOffsetUs);
+ private void setOutputStreamInfo(OutputStreamInfo outputStreamInfo) {
+ this.outputStreamInfo = outputStreamInfo;
+ if (outputStreamInfo.streamOffsetUs != C.TIME_UNSET) {
+ needToNotifyOutputFormatChangeAfterStreamChange = true;
+ onOutputStreamOffsetUsChanged(outputStreamInfo.streamOffsetUs);
}
}
@@ -2515,6 +2483,28 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
&& "OMX.MTK.AUDIO.DECODER.MP3".equals(name);
}
+ private static final class OutputStreamInfo {
+
+ public static final OutputStreamInfo UNSET =
+ new OutputStreamInfo(
+ /* previousStreamLastBufferTimeUs= */ C.TIME_UNSET,
+ /* startPositionUs= */ C.TIME_UNSET,
+ /* streamOffsetUs= */ C.TIME_UNSET);
+
+ public final long previousStreamLastBufferTimeUs;
+ public final long startPositionUs;
+ public final long streamOffsetUs;
+ public final TimedValueQueue formatQueue;
+
+ public OutputStreamInfo(
+ long previousStreamLastBufferTimeUs, long startPositionUs, long streamOffsetUs) {
+ this.previousStreamLastBufferTimeUs = previousStreamLastBufferTimeUs;
+ this.startPositionUs = startPositionUs;
+ this.streamOffsetUs = streamOffsetUs;
+ this.formatQueue = new TimedValueQueue<>();
+ }
+ }
+
@RequiresApi(31)
private static final class Api31 {
private Api31() {}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java
index e97e053084..81af112a67 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/mediacodec/MediaCodecUtil.java
@@ -252,7 +252,7 @@ public final class MediaCodecUtil {
return getVp9ProfileAndLevel(format.codecs, parts);
case CODEC_ID_HEV1:
case CODEC_ID_HVC1:
- return getHevcProfileAndLevel(format.codecs, parts);
+ return getHevcProfileAndLevel(format.codecs, parts, format.colorInfo);
case CODEC_ID_AV01:
return getAv1ProfileAndLevel(format.codecs, parts, format.colorInfo);
case CODEC_ID_MP4A:
@@ -731,7 +731,8 @@ public final class MediaCodecUtil {
}
@Nullable
- private static Pair getHevcProfileAndLevel(String codec, String[] parts) {
+ private static Pair getHevcProfileAndLevel(
+ String codec, String[] parts, @Nullable ColorInfo colorInfo) {
if (parts.length < 4) {
// The codec has fewer parts than required by the HEVC codec string format.
Log.w(TAG, "Ignoring malformed HEVC codec string: " + codec);
@@ -748,7 +749,15 @@ public final class MediaCodecUtil {
if ("1".equals(profileString)) {
profile = CodecProfileLevel.HEVCProfileMain;
} else if ("2".equals(profileString)) {
- profile = CodecProfileLevel.HEVCProfileMain10;
+ if (colorInfo != null && colorInfo.colorTransfer == C.COLOR_TRANSFER_ST2084) {
+ profile = CodecProfileLevel.HEVCProfileMain10HDR10;
+ } else {
+ // For all other cases, we map to the Main10 profile. Note that this includes HLG
+ // HDR. On Android 13+, the platform guarantees that a decoder that advertises
+ // HEVCProfileMain10 will be able to decode HLG. This is not guaranteed for older
+ // Android versions, but we still map to Main10 for backwards compatibility.
+ profile = CodecProfileLevel.HEVCProfileMain10;
+ }
} else {
Log.w(TAG, "Unknown HEVC profile string: " + profileString);
return null;
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DefaultDownloaderFactory.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DefaultDownloaderFactory.java
index 437490eea3..1fee9d80cb 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DefaultDownloaderFactory.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/DefaultDownloaderFactory.java
@@ -105,7 +105,7 @@ public class DefaultDownloaderFactory implements DownloaderFactory {
return constructor.newInstance(mediaItem, cacheDataSourceFactory, executor);
} catch (Exception e) {
throw new IllegalStateException(
- "Failed to instantiate downloader for content type " + contentType);
+ "Failed to instantiate downloader for content type " + contentType, e);
}
}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/SegmentDownloader.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/SegmentDownloader.java
index d4b8ece726..7a8ac0ba2b 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/SegmentDownloader.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/offline/SegmentDownloader.java
@@ -75,8 +75,9 @@ public abstract class SegmentDownloader> impleme
}
}
+ public static final long DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS = 20 * C.MILLIS_PER_SECOND;
+
private static final int BUFFER_SIZE_BYTES = 128 * 1024;
- private static final long MAX_MERGED_SEGMENT_START_TIME_DIFF_US = 20 * C.MICROS_PER_SECOND;
private final DataSpec manifestDataSpec;
private final Parser manifestParser;
@@ -86,6 +87,7 @@ public abstract class SegmentDownloader> impleme
private final CacheKeyFactory cacheKeyFactory;
@Nullable private final PriorityTaskManager priorityTaskManager;
private final Executor executor;
+ private final long maxMergedSegmentStartTimeDiffUs;
/**
* The currently active runnables.
@@ -99,6 +101,24 @@ public abstract class SegmentDownloader> impleme
private volatile boolean isCanceled;
+ /**
+ * @deprecated Use {@link SegmentDownloader#SegmentDownloader(MediaItem, Parser,
+ * CacheDataSource.Factory, Executor, long)} instead.
+ */
+ @Deprecated
+ public SegmentDownloader(
+ MediaItem mediaItem,
+ Parser manifestParser,
+ CacheDataSource.Factory cacheDataSourceFactory,
+ Executor executor) {
+ this(
+ mediaItem,
+ manifestParser,
+ cacheDataSourceFactory,
+ executor,
+ DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS);
+ }
+
/**
* @param mediaItem The {@link MediaItem} to be downloaded.
* @param manifestParser A parser for manifests belonging to the media to be downloaded.
@@ -107,12 +127,16 @@ public abstract class SegmentDownloader> impleme
* @param executor An {@link Executor} used to make requests for the media being downloaded.
* Providing an {@link Executor} that uses multiple threads will speed up the download by
* allowing parts of it to be executed in parallel.
+ * @param maxMergedSegmentStartTimeDiffMs The maximum difference of the start time of two
+ * segments, up to which the segments (of the same URI) should be merged into a single
+ * download segment, in milliseconds.
*/
public SegmentDownloader(
MediaItem mediaItem,
Parser manifestParser,
CacheDataSource.Factory cacheDataSourceFactory,
- Executor executor) {
+ Executor executor,
+ long maxMergedSegmentStartTimeDiffMs) {
checkNotNull(mediaItem.localConfiguration);
this.manifestDataSpec = getCompressibleDataSpec(mediaItem.localConfiguration.uri);
this.manifestParser = manifestParser;
@@ -123,6 +147,7 @@ public abstract class SegmentDownloader> impleme
cacheKeyFactory = cacheDataSourceFactory.getCacheKeyFactory();
priorityTaskManager = cacheDataSourceFactory.getUpstreamPriorityTaskManager();
activeRunnables = new ArrayList<>();
+ maxMergedSegmentStartTimeDiffUs = Util.msToUs(maxMergedSegmentStartTimeDiffMs);
}
@Override
@@ -145,7 +170,7 @@ public abstract class SegmentDownloader> impleme
// Sort the segments so that we download media in the right order from the start of the
// content, and merge segments where possible to minimize the number of server round trips.
Collections.sort(segments);
- mergeSegments(segments, cacheKeyFactory);
+ mergeSegments(segments, cacheKeyFactory, maxMergedSegmentStartTimeDiffUs);
// Scan the segments, removing any that are fully downloaded.
int totalSegments = segments.size();
@@ -416,7 +441,8 @@ public abstract class SegmentDownloader> impleme
}
}
- private static void mergeSegments(List segments, CacheKeyFactory keyFactory) {
+ private static void mergeSegments(
+ List segments, CacheKeyFactory keyFactory, long maxMergedSegmentStartTimeDiffUs) {
HashMap lastIndexByCacheKey = new HashMap<>();
int nextOutIndex = 0;
for (int i = 0; i < segments.size(); i++) {
@@ -425,7 +451,7 @@ public abstract class SegmentDownloader> impleme
@Nullable Integer lastIndex = lastIndexByCacheKey.get(cacheKey);
@Nullable Segment lastSegment = lastIndex == null ? null : segments.get(lastIndex);
if (lastSegment == null
- || segment.startTimeUs > lastSegment.startTimeUs + MAX_MERGED_SEGMENT_START_TIME_DIFF_US
+ || segment.startTimeUs > lastSegment.startTimeUs + maxMergedSegmentStartTimeDiffUs
|| !canMergeSegments(lastSegment.dataSpec, segment.dataSpec)) {
lastIndexByCacheKey.put(cacheKey, nextOutIndex);
segments.set(nextOutIndex, segment);
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java
index ab87aa361c..53ad113710 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/Requirements.java
@@ -184,8 +184,8 @@ public final class Requirements implements Parcelable {
private boolean isDeviceCharging(Context context) {
@Nullable
Intent batteryStatus =
- Util.registerReceiverNotExported(
- context, /* receiver= */ null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
+ context.registerReceiver(
+ /* receiver= */ null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
if (batteryStatus == null) {
return false;
}
@@ -203,8 +203,8 @@ public final class Requirements implements Parcelable {
}
private boolean isStorageNotLow(Context context) {
- return Util.registerReceiverNotExported(
- context, /* receiver= */ null, new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW))
+ return context.registerReceiver(
+ /* receiver= */ null, new IntentFilter(Intent.ACTION_DEVICE_STORAGE_LOW))
== null;
}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java
index 541224221c..d214bc95af 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/scheduler/RequirementsWatcher.java
@@ -111,7 +111,7 @@ public final class RequirementsWatcher {
filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK);
}
receiver = new DeviceStatusChangeReceiver();
- Util.registerReceiverNotExported(context, receiver, filter, handler);
+ context.registerReceiver(receiver, filter, /* broadcastPermission= */ null, handler);
return notMetRequirements;
}
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/SlidingPercentile.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/SlidingPercentile.java
index ca3d0fadbc..b555b3c40f 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/SlidingPercentile.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/upstream/SlidingPercentile.java
@@ -30,8 +30,12 @@ import java.util.Comparator;
* rate observations. This is an alternative to sliding mean and exponential averaging which suffer
* from susceptibility to outliers and slow adaptation to step functions.
*
- * @see Wiki: Moving average
- * @see Wiki: Selection algorithm
+ * See the following Wikipedia articles:
+ *
+ *
*/
@UnstableApi
public class SlidingPercentile {
diff --git a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java
index 1bd45fc24a..6b9d4b567d 100644
--- a/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java
+++ b/libraries/exoplayer/src/main/java/androidx/media3/exoplayer/video/MediaCodecVideoRenderer.java
@@ -153,6 +153,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
private long lastRenderRealtimeUs;
private long totalVideoFrameProcessingOffsetUs;
private int videoFrameProcessingOffsetCount;
+ private long lastFrameReleaseTimeNs;
private int currentWidth;
private int currentHeight;
@@ -1128,9 +1129,18 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
if (Util.SDK_INT >= 21) {
// Let the underlying framework time the release.
if (earlyUs < 50000) {
- notifyFrameMetadataListener(presentationTimeUs, adjustedReleaseTimeNs, format);
- renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, adjustedReleaseTimeNs);
+ if (adjustedReleaseTimeNs == lastFrameReleaseTimeNs) {
+ // This frame should be displayed on the same vsync with the previous released frame. We
+ // are likely rendering frames at a rate higher than the screen refresh rate. Skip
+ // this buffer so that it's returned to MediaCodec sooner otherwise MediaCodec may not
+ // be able to keep decoding with this rate [b/263454203].
+ skipOutputBuffer(codec, bufferIndex, presentationTimeUs);
+ } else {
+ notifyFrameMetadataListener(presentationTimeUs, adjustedReleaseTimeNs, format);
+ renderOutputBufferV21(codec, bufferIndex, presentationTimeUs, adjustedReleaseTimeNs);
+ }
updateVideoFrameProcessingOffsetCounters(earlyUs);
+ lastFrameReleaseTimeNs = adjustedReleaseTimeNs;
return true;
}
} else {
diff --git a/libraries/exoplayer/src/main/res/values-af/strings.xml b/libraries/exoplayer/src/main/res/values-af/strings.xml
index c1a57f9246..b935a4cf89 100644
--- a/libraries/exoplayer/src/main/res/values-af/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-af/strings.xml
@@ -1,18 +1,4 @@
-
Aflaai
Aflaaie
diff --git a/libraries/exoplayer/src/main/res/values-am/strings.xml b/libraries/exoplayer/src/main/res/values-am/strings.xml
index a16ac064b3..93a8ae066b 100644
--- a/libraries/exoplayer/src/main/res/values-am/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-am/strings.xml
@@ -1,18 +1,4 @@
-
አውርድ
የወረዱ
diff --git a/libraries/exoplayer/src/main/res/values-ar/strings.xml b/libraries/exoplayer/src/main/res/values-ar/strings.xml
index 6b03d0fdd6..14db130622 100644
--- a/libraries/exoplayer/src/main/res/values-ar/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-ar/strings.xml
@@ -1,18 +1,4 @@
-
تنزيل
عمليات التنزيل
diff --git a/libraries/exoplayer/src/main/res/values-az/strings.xml b/libraries/exoplayer/src/main/res/values-az/strings.xml
index 1193607b28..28e9dcb010 100644
--- a/libraries/exoplayer/src/main/res/values-az/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-az/strings.xml
@@ -1,18 +1,4 @@
-
Endirin
Endirmələr
diff --git a/libraries/exoplayer/src/main/res/values-b+sr+Latn/strings.xml b/libraries/exoplayer/src/main/res/values-b+sr+Latn/strings.xml
index 49d5ff23c4..c8e8eeb786 100644
--- a/libraries/exoplayer/src/main/res/values-b+sr+Latn/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-b+sr+Latn/strings.xml
@@ -1,18 +1,4 @@
-
Preuzmi
Preuzimanja
diff --git a/libraries/exoplayer/src/main/res/values-be/strings.xml b/libraries/exoplayer/src/main/res/values-be/strings.xml
index 54b219e042..1539008d72 100644
--- a/libraries/exoplayer/src/main/res/values-be/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-be/strings.xml
@@ -1,18 +1,4 @@
-
Спампаваць
Спампоўкі
diff --git a/libraries/exoplayer/src/main/res/values-bg/strings.xml b/libraries/exoplayer/src/main/res/values-bg/strings.xml
index 420fa00b97..af37d82078 100644
--- a/libraries/exoplayer/src/main/res/values-bg/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-bg/strings.xml
@@ -1,18 +1,4 @@
-
Изтегляне
Изтегляния
diff --git a/libraries/exoplayer/src/main/res/values-bn/strings.xml b/libraries/exoplayer/src/main/res/values-bn/strings.xml
index c4a6c3686e..be8ed9cdaa 100644
--- a/libraries/exoplayer/src/main/res/values-bn/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-bn/strings.xml
@@ -1,18 +1,4 @@
-
ডাউনলোড করুন
ডাউনলোড
diff --git a/libraries/exoplayer/src/main/res/values-bs/strings.xml b/libraries/exoplayer/src/main/res/values-bs/strings.xml
index 5ca81f2780..5fe387cefb 100644
--- a/libraries/exoplayer/src/main/res/values-bs/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-bs/strings.xml
@@ -1,18 +1,4 @@
-
Preuzmi
Preuzimanja
diff --git a/libraries/exoplayer/src/main/res/values-ca/strings.xml b/libraries/exoplayer/src/main/res/values-ca/strings.xml
index 6c80546d7b..ecb381f534 100644
--- a/libraries/exoplayer/src/main/res/values-ca/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-ca/strings.xml
@@ -1,18 +1,4 @@
-
Baixa
Baixades
diff --git a/libraries/exoplayer/src/main/res/values-cs/strings.xml b/libraries/exoplayer/src/main/res/values-cs/strings.xml
index f916f076e3..54a112a9ee 100644
--- a/libraries/exoplayer/src/main/res/values-cs/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-cs/strings.xml
@@ -1,18 +1,4 @@
-
Stáhnout
Stahování
diff --git a/libraries/exoplayer/src/main/res/values-da/strings.xml b/libraries/exoplayer/src/main/res/values-da/strings.xml
index dca960dbd4..1b15b1f605 100644
--- a/libraries/exoplayer/src/main/res/values-da/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-da/strings.xml
@@ -1,18 +1,4 @@
-
Download
Downloads
diff --git a/libraries/exoplayer/src/main/res/values-de/strings.xml b/libraries/exoplayer/src/main/res/values-de/strings.xml
index 92904131e1..848bfb2d4d 100644
--- a/libraries/exoplayer/src/main/res/values-de/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-de/strings.xml
@@ -1,18 +1,4 @@
-
Herunterladen
Downloads
diff --git a/libraries/exoplayer/src/main/res/values-el/strings.xml b/libraries/exoplayer/src/main/res/values-el/strings.xml
index e079173d12..8ef5c20f72 100644
--- a/libraries/exoplayer/src/main/res/values-el/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-el/strings.xml
@@ -1,18 +1,4 @@
-
Λήψη
Λήψεις
diff --git a/libraries/exoplayer/src/main/res/values-en-rAU/strings.xml b/libraries/exoplayer/src/main/res/values-en-rAU/strings.xml
index 27fb6389f8..639a151801 100644
--- a/libraries/exoplayer/src/main/res/values-en-rAU/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-en-rAU/strings.xml
@@ -1,18 +1,4 @@
-
Download
Downloads
diff --git a/libraries/exoplayer/src/main/res/values-en-rGB/strings.xml b/libraries/exoplayer/src/main/res/values-en-rGB/strings.xml
index 27fb6389f8..639a151801 100644
--- a/libraries/exoplayer/src/main/res/values-en-rGB/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-en-rGB/strings.xml
@@ -1,18 +1,4 @@
-
Download
Downloads
diff --git a/libraries/exoplayer/src/main/res/values-en-rIN/strings.xml b/libraries/exoplayer/src/main/res/values-en-rIN/strings.xml
index 27fb6389f8..639a151801 100644
--- a/libraries/exoplayer/src/main/res/values-en-rIN/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-en-rIN/strings.xml
@@ -1,18 +1,4 @@
-
Download
Downloads
diff --git a/libraries/exoplayer/src/main/res/values-es-rUS/strings.xml b/libraries/exoplayer/src/main/res/values-es-rUS/strings.xml
index 45b331f477..545c55ac86 100644
--- a/libraries/exoplayer/src/main/res/values-es-rUS/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-es-rUS/strings.xml
@@ -1,18 +1,4 @@
-
Descargar
Descargas
diff --git a/libraries/exoplayer/src/main/res/values-es/strings.xml b/libraries/exoplayer/src/main/res/values-es/strings.xml
index 6d13c5fd21..a7a8edeaba 100644
--- a/libraries/exoplayer/src/main/res/values-es/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-es/strings.xml
@@ -1,18 +1,4 @@
-
Descargar
Descargas
diff --git a/libraries/exoplayer/src/main/res/values-et/strings.xml b/libraries/exoplayer/src/main/res/values-et/strings.xml
index 67df50e267..5ac51b75cb 100644
--- a/libraries/exoplayer/src/main/res/values-et/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-et/strings.xml
@@ -1,18 +1,4 @@
-
Allalaadimine
Allalaadimised
diff --git a/libraries/exoplayer/src/main/res/values-eu/strings.xml b/libraries/exoplayer/src/main/res/values-eu/strings.xml
index e2e0ca7b9e..d476d29b7b 100644
--- a/libraries/exoplayer/src/main/res/values-eu/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-eu/strings.xml
@@ -1,18 +1,4 @@
-
Deskargak
Deskargak
diff --git a/libraries/exoplayer/src/main/res/values-fa/strings.xml b/libraries/exoplayer/src/main/res/values-fa/strings.xml
index ad7b14e934..271919fbc1 100644
--- a/libraries/exoplayer/src/main/res/values-fa/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-fa/strings.xml
@@ -1,18 +1,4 @@
-
بارگیری
بارگیریها
diff --git a/libraries/exoplayer/src/main/res/values-fi/strings.xml b/libraries/exoplayer/src/main/res/values-fi/strings.xml
index 6fbdf45fac..15f1a38628 100644
--- a/libraries/exoplayer/src/main/res/values-fi/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-fi/strings.xml
@@ -1,18 +1,4 @@
-
Lataa
Lataukset
diff --git a/libraries/exoplayer/src/main/res/values-fr-rCA/strings.xml b/libraries/exoplayer/src/main/res/values-fr-rCA/strings.xml
index 9610fd2e14..e177a5d753 100644
--- a/libraries/exoplayer/src/main/res/values-fr-rCA/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-fr-rCA/strings.xml
@@ -1,18 +1,4 @@
-
Télécharger
Téléchargements
diff --git a/libraries/exoplayer/src/main/res/values-fr/strings.xml b/libraries/exoplayer/src/main/res/values-fr/strings.xml
index 22f0299976..274278bf4a 100644
--- a/libraries/exoplayer/src/main/res/values-fr/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-fr/strings.xml
@@ -1,18 +1,4 @@
-
Télécharger
Téléchargements
diff --git a/libraries/exoplayer/src/main/res/values-gl/strings.xml b/libraries/exoplayer/src/main/res/values-gl/strings.xml
index 6764ddb4d3..46f80721b5 100644
--- a/libraries/exoplayer/src/main/res/values-gl/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-gl/strings.xml
@@ -1,18 +1,4 @@
-
Descargar
Descargas
diff --git a/libraries/exoplayer/src/main/res/values-gu/strings.xml b/libraries/exoplayer/src/main/res/values-gu/strings.xml
index 27821e2299..5d2b5874e5 100644
--- a/libraries/exoplayer/src/main/res/values-gu/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-gu/strings.xml
@@ -1,18 +1,4 @@
-
ડાઉનલોડ કરો
ડાઉનલોડ
diff --git a/libraries/exoplayer/src/main/res/values-hi/strings.xml b/libraries/exoplayer/src/main/res/values-hi/strings.xml
index d839e3f3fa..17a17adcdd 100644
--- a/libraries/exoplayer/src/main/res/values-hi/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-hi/strings.xml
@@ -1,18 +1,4 @@
-
डाउनलोड करें
डाउनलोड की गई मीडिया फ़ाइलें
diff --git a/libraries/exoplayer/src/main/res/values-hr/strings.xml b/libraries/exoplayer/src/main/res/values-hr/strings.xml
index 031e629fd8..05d7cfa448 100644
--- a/libraries/exoplayer/src/main/res/values-hr/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-hr/strings.xml
@@ -1,18 +1,4 @@
-
Preuzmi
Preuzimanja
diff --git a/libraries/exoplayer/src/main/res/values-hu/strings.xml b/libraries/exoplayer/src/main/res/values-hu/strings.xml
index e511a69594..d19171d975 100644
--- a/libraries/exoplayer/src/main/res/values-hu/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-hu/strings.xml
@@ -1,18 +1,4 @@
-
Letöltés
Letöltések
diff --git a/libraries/exoplayer/src/main/res/values-hy/strings.xml b/libraries/exoplayer/src/main/res/values-hy/strings.xml
index 61d61e8704..4fc4e86bcb 100644
--- a/libraries/exoplayer/src/main/res/values-hy/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-hy/strings.xml
@@ -1,18 +1,4 @@
-
Ներբեռնել
Ներբեռնումներ
diff --git a/libraries/exoplayer/src/main/res/values-in/strings.xml b/libraries/exoplayer/src/main/res/values-in/strings.xml
index ce650eb479..e20aac9971 100644
--- a/libraries/exoplayer/src/main/res/values-in/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-in/strings.xml
@@ -1,18 +1,4 @@
-
Download
Download
diff --git a/libraries/exoplayer/src/main/res/values-is/strings.xml b/libraries/exoplayer/src/main/res/values-is/strings.xml
index 713dbc50b8..6c62e12ee4 100644
--- a/libraries/exoplayer/src/main/res/values-is/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-is/strings.xml
@@ -1,18 +1,4 @@
-
Sækja
Niðurhal
diff --git a/libraries/exoplayer/src/main/res/values-it/strings.xml b/libraries/exoplayer/src/main/res/values-it/strings.xml
index 709bb8e5b4..03a7a64d1a 100644
--- a/libraries/exoplayer/src/main/res/values-it/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-it/strings.xml
@@ -1,18 +1,4 @@
-
Scarica
Download
diff --git a/libraries/exoplayer/src/main/res/values-iw/strings.xml b/libraries/exoplayer/src/main/res/values-iw/strings.xml
index dadb93b016..41010d65f8 100644
--- a/libraries/exoplayer/src/main/res/values-iw/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-iw/strings.xml
@@ -1,18 +1,4 @@
-
הורדה
הורדות
diff --git a/libraries/exoplayer/src/main/res/values-ja/strings.xml b/libraries/exoplayer/src/main/res/values-ja/strings.xml
index b4df8db16c..f32b0bfc75 100644
--- a/libraries/exoplayer/src/main/res/values-ja/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-ja/strings.xml
@@ -1,18 +1,4 @@
-
ダウンロード
ダウンロード
diff --git a/libraries/exoplayer/src/main/res/values-ka/strings.xml b/libraries/exoplayer/src/main/res/values-ka/strings.xml
index 34fa93ac36..2460d0c4b7 100644
--- a/libraries/exoplayer/src/main/res/values-ka/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-ka/strings.xml
@@ -1,18 +1,4 @@
-
ჩამოტვირთვა
ჩამოტვირთვები
diff --git a/libraries/exoplayer/src/main/res/values-kk/strings.xml b/libraries/exoplayer/src/main/res/values-kk/strings.xml
index a0e7f49af1..616fb42af5 100644
--- a/libraries/exoplayer/src/main/res/values-kk/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-kk/strings.xml
@@ -1,18 +1,4 @@
-
Жүктеп алу
Жүктеп алынғандар
diff --git a/libraries/exoplayer/src/main/res/values-km/strings.xml b/libraries/exoplayer/src/main/res/values-km/strings.xml
index d284e7b78a..2089642a4a 100644
--- a/libraries/exoplayer/src/main/res/values-km/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-km/strings.xml
@@ -1,18 +1,4 @@
-
ទាញយក
ទាញយក
diff --git a/libraries/exoplayer/src/main/res/values-kn/strings.xml b/libraries/exoplayer/src/main/res/values-kn/strings.xml
index c311225089..7747d1256b 100644
--- a/libraries/exoplayer/src/main/res/values-kn/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-kn/strings.xml
@@ -1,18 +1,4 @@
-
ಡೌನ್ಲೋಡ್
ಡೌನ್ಲೋಡ್ಗಳು
diff --git a/libraries/exoplayer/src/main/res/values-ko/strings.xml b/libraries/exoplayer/src/main/res/values-ko/strings.xml
index 3bbdd2cb6c..a87dce92cc 100644
--- a/libraries/exoplayer/src/main/res/values-ko/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-ko/strings.xml
@@ -1,18 +1,4 @@
-
다운로드
다운로드
diff --git a/libraries/exoplayer/src/main/res/values-ky/strings.xml b/libraries/exoplayer/src/main/res/values-ky/strings.xml
index 8cf2921bc3..6ce6949e2b 100644
--- a/libraries/exoplayer/src/main/res/values-ky/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-ky/strings.xml
@@ -1,18 +1,4 @@
-
Жүктөп алуу
Жүктөлүп алынгандар
diff --git a/libraries/exoplayer/src/main/res/values-lo/strings.xml b/libraries/exoplayer/src/main/res/values-lo/strings.xml
index 0b7f314f91..1ff16ef04f 100644
--- a/libraries/exoplayer/src/main/res/values-lo/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-lo/strings.xml
@@ -1,18 +1,4 @@
-
ດາວໂຫລດ
ດາວໂຫລດ
diff --git a/libraries/exoplayer/src/main/res/values-lt/strings.xml b/libraries/exoplayer/src/main/res/values-lt/strings.xml
index afc749c88a..c81cdd1092 100644
--- a/libraries/exoplayer/src/main/res/values-lt/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-lt/strings.xml
@@ -1,18 +1,4 @@
-
Atsisiųsti
Atsisiuntimai
diff --git a/libraries/exoplayer/src/main/res/values-lv/strings.xml b/libraries/exoplayer/src/main/res/values-lv/strings.xml
index 1899c273e4..0213b5e062 100644
--- a/libraries/exoplayer/src/main/res/values-lv/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-lv/strings.xml
@@ -1,18 +1,4 @@
-
Lejupielādēt
Lejupielādes
diff --git a/libraries/exoplayer/src/main/res/values-mk/strings.xml b/libraries/exoplayer/src/main/res/values-mk/strings.xml
index 58a1de488a..6e1c3aecfc 100644
--- a/libraries/exoplayer/src/main/res/values-mk/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-mk/strings.xml
@@ -1,18 +1,4 @@
-
Преземи
Преземања
diff --git a/libraries/exoplayer/src/main/res/values-ml/strings.xml b/libraries/exoplayer/src/main/res/values-ml/strings.xml
index 5c75164da8..2e0653da43 100644
--- a/libraries/exoplayer/src/main/res/values-ml/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-ml/strings.xml
@@ -1,18 +1,4 @@
-
ഡൗൺലോഡ്
ഡൗൺലോഡുകൾ
diff --git a/libraries/exoplayer/src/main/res/values-mn/strings.xml b/libraries/exoplayer/src/main/res/values-mn/strings.xml
index 6e7aac5dc9..95b30da3ce 100644
--- a/libraries/exoplayer/src/main/res/values-mn/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-mn/strings.xml
@@ -1,18 +1,4 @@
-
Татах
Татaлт
diff --git a/libraries/exoplayer/src/main/res/values-mr/strings.xml b/libraries/exoplayer/src/main/res/values-mr/strings.xml
index 0d98194d6a..0c37597dda 100644
--- a/libraries/exoplayer/src/main/res/values-mr/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-mr/strings.xml
@@ -1,18 +1,4 @@
-
डाउनलोड करा
डाउनलोड
diff --git a/libraries/exoplayer/src/main/res/values-ms/strings.xml b/libraries/exoplayer/src/main/res/values-ms/strings.xml
index f4d08d49d3..3033c3c93f 100644
--- a/libraries/exoplayer/src/main/res/values-ms/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-ms/strings.xml
@@ -1,18 +1,4 @@
-
Muat turun
Muat turun
diff --git a/libraries/exoplayer/src/main/res/values-my/strings.xml b/libraries/exoplayer/src/main/res/values-my/strings.xml
index 9e308724ad..88294129a9 100644
--- a/libraries/exoplayer/src/main/res/values-my/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-my/strings.xml
@@ -1,18 +1,4 @@
-
ဒေါင်းလုဒ် လုပ်ရန်
ဒေါင်းလုဒ်များ
diff --git a/libraries/exoplayer/src/main/res/values-nb/strings.xml b/libraries/exoplayer/src/main/res/values-nb/strings.xml
index 5d5b46b28c..026d9be10d 100644
--- a/libraries/exoplayer/src/main/res/values-nb/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-nb/strings.xml
@@ -1,18 +1,4 @@
-
Last ned
Nedlastinger
diff --git a/libraries/exoplayer/src/main/res/values-ne/strings.xml b/libraries/exoplayer/src/main/res/values-ne/strings.xml
index deff7e8e41..a1a014bf7b 100644
--- a/libraries/exoplayer/src/main/res/values-ne/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-ne/strings.xml
@@ -1,18 +1,4 @@
-
डाउनलोड गर्नुहोस्
डाउनलोडहरू
diff --git a/libraries/exoplayer/src/main/res/values-nl/strings.xml b/libraries/exoplayer/src/main/res/values-nl/strings.xml
index 589296d9a6..c8eeb8553d 100644
--- a/libraries/exoplayer/src/main/res/values-nl/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-nl/strings.xml
@@ -1,18 +1,4 @@
-
Downloaden
Downloads
diff --git a/libraries/exoplayer/src/main/res/values-pa/strings.xml b/libraries/exoplayer/src/main/res/values-pa/strings.xml
index 86236f9e03..ee5031015d 100644
--- a/libraries/exoplayer/src/main/res/values-pa/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-pa/strings.xml
@@ -1,18 +1,4 @@
-
ਡਾਊਨਲੋਡ ਕਰੋ
ਡਾਊਨਲੋਡ
diff --git a/libraries/exoplayer/src/main/res/values-pl/strings.xml b/libraries/exoplayer/src/main/res/values-pl/strings.xml
index 63fc39fac7..40633af74c 100644
--- a/libraries/exoplayer/src/main/res/values-pl/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-pl/strings.xml
@@ -1,18 +1,4 @@
-
Pobierz
Pobieranie
diff --git a/libraries/exoplayer/src/main/res/values-pt-rPT/strings.xml b/libraries/exoplayer/src/main/res/values-pt-rPT/strings.xml
index 6c9f639ec9..1988ac95eb 100644
--- a/libraries/exoplayer/src/main/res/values-pt-rPT/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-pt-rPT/strings.xml
@@ -1,18 +1,4 @@
-
Transferir
Transferências
diff --git a/libraries/exoplayer/src/main/res/values-pt/strings.xml b/libraries/exoplayer/src/main/res/values-pt/strings.xml
index c38d90d176..59889e55d5 100644
--- a/libraries/exoplayer/src/main/res/values-pt/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-pt/strings.xml
@@ -1,18 +1,4 @@
-
Fazer o download
Downloads
diff --git a/libraries/exoplayer/src/main/res/values-ro/strings.xml b/libraries/exoplayer/src/main/res/values-ro/strings.xml
index a4a7afa1a6..56355afa1f 100644
--- a/libraries/exoplayer/src/main/res/values-ro/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-ro/strings.xml
@@ -1,20 +1,6 @@
-
- Descărcați
+ Descarcă
Descărcări
Se descarcă
Descărcarea a fost finalizată
diff --git a/libraries/exoplayer/src/main/res/values-ru/strings.xml b/libraries/exoplayer/src/main/res/values-ru/strings.xml
index b989fd3bc1..c2b7f7f360 100644
--- a/libraries/exoplayer/src/main/res/values-ru/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-ru/strings.xml
@@ -1,18 +1,4 @@
-
Скачать
Скачивания
diff --git a/libraries/exoplayer/src/main/res/values-si/strings.xml b/libraries/exoplayer/src/main/res/values-si/strings.xml
index 36b652c2cc..2b9b3289fd 100644
--- a/libraries/exoplayer/src/main/res/values-si/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-si/strings.xml
@@ -1,18 +1,4 @@
-
බාගන්න
බාගැනීම්
diff --git a/libraries/exoplayer/src/main/res/values-sk/strings.xml b/libraries/exoplayer/src/main/res/values-sk/strings.xml
index fec40af4e2..4581322c61 100644
--- a/libraries/exoplayer/src/main/res/values-sk/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-sk/strings.xml
@@ -1,18 +1,4 @@
-
Stiahnuť
Stiahnuté
diff --git a/libraries/exoplayer/src/main/res/values-sl/strings.xml b/libraries/exoplayer/src/main/res/values-sl/strings.xml
index 025ba4a0e6..460643ebb8 100644
--- a/libraries/exoplayer/src/main/res/values-sl/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-sl/strings.xml
@@ -1,18 +1,4 @@
-
Prenos
Prenosi
diff --git a/libraries/exoplayer/src/main/res/values-sq/strings.xml b/libraries/exoplayer/src/main/res/values-sq/strings.xml
index 836ff91e41..b3e916ef4c 100644
--- a/libraries/exoplayer/src/main/res/values-sq/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-sq/strings.xml
@@ -1,18 +1,4 @@
-
Shkarko
Shkarkimet
diff --git a/libraries/exoplayer/src/main/res/values-sr/strings.xml b/libraries/exoplayer/src/main/res/values-sr/strings.xml
index aad2287353..f537b8e38f 100644
--- a/libraries/exoplayer/src/main/res/values-sr/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-sr/strings.xml
@@ -1,18 +1,4 @@
-
Преузми
Преузимања
diff --git a/libraries/exoplayer/src/main/res/values-sv/strings.xml b/libraries/exoplayer/src/main/res/values-sv/strings.xml
index 34ea63ab33..a645a70f8c 100644
--- a/libraries/exoplayer/src/main/res/values-sv/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-sv/strings.xml
@@ -1,18 +1,4 @@
-
Ladda ned
Nedladdningar
diff --git a/libraries/exoplayer/src/main/res/values-sw/strings.xml b/libraries/exoplayer/src/main/res/values-sw/strings.xml
index f2ea0ebb2f..e6d112427f 100644
--- a/libraries/exoplayer/src/main/res/values-sw/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-sw/strings.xml
@@ -1,18 +1,4 @@
-
Pakua
Vipakuliwa
diff --git a/libraries/exoplayer/src/main/res/values-ta/strings.xml b/libraries/exoplayer/src/main/res/values-ta/strings.xml
index b9dd46a955..bd9cdbf5d4 100644
--- a/libraries/exoplayer/src/main/res/values-ta/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-ta/strings.xml
@@ -1,18 +1,4 @@
-
பதிவிறக்கும் பட்டன்
பதிவிறக்கங்கள்
diff --git a/libraries/exoplayer/src/main/res/values-te/strings.xml b/libraries/exoplayer/src/main/res/values-te/strings.xml
index fe1f48a955..cf0480ebb0 100644
--- a/libraries/exoplayer/src/main/res/values-te/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-te/strings.xml
@@ -1,18 +1,4 @@
-
డౌన్లోడ్ చేయి
డౌన్లోడ్లు
diff --git a/libraries/exoplayer/src/main/res/values-th/strings.xml b/libraries/exoplayer/src/main/res/values-th/strings.xml
index c888cd3b72..73cba4d678 100644
--- a/libraries/exoplayer/src/main/res/values-th/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-th/strings.xml
@@ -1,18 +1,4 @@
-
ดาวน์โหลด
ดาวน์โหลด
diff --git a/libraries/exoplayer/src/main/res/values-tl/strings.xml b/libraries/exoplayer/src/main/res/values-tl/strings.xml
index 3b04a687a5..4e68f6f05c 100644
--- a/libraries/exoplayer/src/main/res/values-tl/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-tl/strings.xml
@@ -1,18 +1,4 @@
-
I-download
Mga Download
diff --git a/libraries/exoplayer/src/main/res/values-tr/strings.xml b/libraries/exoplayer/src/main/res/values-tr/strings.xml
index 2b9252ec7c..24a75afb36 100644
--- a/libraries/exoplayer/src/main/res/values-tr/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-tr/strings.xml
@@ -1,18 +1,4 @@
-
İndir
İndirilenler
diff --git a/libraries/exoplayer/src/main/res/values-uk/strings.xml b/libraries/exoplayer/src/main/res/values-uk/strings.xml
index 7fbb8955de..2413efd778 100644
--- a/libraries/exoplayer/src/main/res/values-uk/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-uk/strings.xml
@@ -1,18 +1,4 @@
-
Завантажити
Завантаження
diff --git a/libraries/exoplayer/src/main/res/values-ur/strings.xml b/libraries/exoplayer/src/main/res/values-ur/strings.xml
index 966f406273..b9dd50dd8a 100644
--- a/libraries/exoplayer/src/main/res/values-ur/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-ur/strings.xml
@@ -1,18 +1,4 @@
-
ڈاؤن لوڈ کریں
ڈاؤن لوڈز
diff --git a/libraries/exoplayer/src/main/res/values-uz/strings.xml b/libraries/exoplayer/src/main/res/values-uz/strings.xml
index 85aaf73a8c..21e3ed45d5 100644
--- a/libraries/exoplayer/src/main/res/values-uz/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-uz/strings.xml
@@ -1,18 +1,4 @@
-
Yuklab olish
Yuklanmalar
diff --git a/libraries/exoplayer/src/main/res/values-vi/strings.xml b/libraries/exoplayer/src/main/res/values-vi/strings.xml
index 1e97ed5e5d..3b74a5541a 100644
--- a/libraries/exoplayer/src/main/res/values-vi/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-vi/strings.xml
@@ -1,18 +1,4 @@
-
Tải xuống
Tài nguyên đã tải xuống
diff --git a/libraries/exoplayer/src/main/res/values-zh-rCN/strings.xml b/libraries/exoplayer/src/main/res/values-zh-rCN/strings.xml
index 1d24d8cab3..4031209c29 100644
--- a/libraries/exoplayer/src/main/res/values-zh-rCN/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-zh-rCN/strings.xml
@@ -1,18 +1,4 @@
-
下载
下载内容
diff --git a/libraries/exoplayer/src/main/res/values-zh-rHK/strings.xml b/libraries/exoplayer/src/main/res/values-zh-rHK/strings.xml
index 1b4279329f..eb4c819746 100644
--- a/libraries/exoplayer/src/main/res/values-zh-rHK/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-zh-rHK/strings.xml
@@ -1,18 +1,4 @@
-
下載
下載內容
diff --git a/libraries/exoplayer/src/main/res/values-zh-rTW/strings.xml b/libraries/exoplayer/src/main/res/values-zh-rTW/strings.xml
index f696d26abf..c95bfd95a2 100644
--- a/libraries/exoplayer/src/main/res/values-zh-rTW/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-zh-rTW/strings.xml
@@ -1,18 +1,4 @@
-
下載
下載
diff --git a/libraries/exoplayer/src/main/res/values-zu/strings.xml b/libraries/exoplayer/src/main/res/values-zu/strings.xml
index f1d3047458..4e2a417261 100644
--- a/libraries/exoplayer/src/main/res/values-zu/strings.xml
+++ b/libraries/exoplayer/src/main/res/values-zu/strings.xml
@@ -1,18 +1,4 @@
-
Landa
Ukulandwa
diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java
index fb2ae47b59..d258b7123a 100644
--- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java
+++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/ExoPlayerTest.java
@@ -89,6 +89,7 @@ import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.net.Uri;
import android.os.Looper;
+import android.util.Pair;
import android.view.Surface;
import androidx.annotation.Nullable;
import androidx.media3.common.AdPlaybackState;
@@ -129,13 +130,13 @@ import androidx.media3.exoplayer.source.SinglePeriodTimeline;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.source.WrappingMediaSource;
import androidx.media3.exoplayer.source.ads.ServerSideAdInsertionMediaSource;
+import androidx.media3.exoplayer.text.TextOutput;
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
import androidx.media3.exoplayer.upstream.Allocation;
import androidx.media3.exoplayer.upstream.Allocator;
import androidx.media3.exoplayer.upstream.Loader;
import androidx.media3.extractor.metadata.id3.BinaryFrame;
import androidx.media3.extractor.metadata.id3.TextInformationFrame;
-import androidx.media3.test.utils.Action;
import androidx.media3.test.utils.ActionSchedule;
import androidx.media3.test.utils.ActionSchedule.PlayerRunnable;
import androidx.media3.test.utils.ActionSchedule.PlayerTarget;
@@ -3843,41 +3844,29 @@ public final class ExoPlayerTest {
@Test
public void setPlaybackSpeedConsecutivelyNotifiesListenerForEveryChangeOnceAndIsMasked()
throws Exception {
+ ExoPlayer player = new TestExoPlayerBuilder(context).build();
List maskedPlaybackSpeeds = new ArrayList<>();
- Action getPlaybackSpeedAction =
- new Action("getPlaybackSpeed", /* description= */ null) {
- @Override
- protected void doActionImpl(
- ExoPlayer player, DefaultTrackSelector trackSelector, @Nullable Surface surface) {
- maskedPlaybackSpeeds.add(player.getPlaybackParameters().speed);
- }
- };
- ActionSchedule actionSchedule =
- new ActionSchedule.Builder(TAG)
- .pause()
- .waitForPlaybackState(Player.STATE_READY)
- .setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.1f))
- .apply(getPlaybackSpeedAction)
- .setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.2f))
- .apply(getPlaybackSpeedAction)
- .setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.3f))
- .apply(getPlaybackSpeedAction)
- .play()
- .build();
List reportedPlaybackSpeeds = new ArrayList<>();
- Player.Listener listener =
+ player.addListener(
new Player.Listener() {
@Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
reportedPlaybackSpeeds.add(playbackParameters.speed);
}
- };
- new ExoPlayerTestRunner.Builder(context)
- .setActionSchedule(actionSchedule)
- .setPlayerListener(listener)
- .build()
- .start()
- .blockUntilEnded(TIMEOUT_MS);
+ });
+ player.setMediaSource(
+ new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.AUDIO_FORMAT));
+ player.prepare();
+ runUntilPlaybackState(player, Player.STATE_READY);
+
+ player.setPlaybackSpeed(1.1f);
+ maskedPlaybackSpeeds.add(player.getPlaybackParameters().speed);
+ player.setPlaybackSpeed(1.2f);
+ maskedPlaybackSpeeds.add(player.getPlaybackParameters().speed);
+ player.setPlaybackSpeed(1.3f);
+ maskedPlaybackSpeeds.add(player.getPlaybackParameters().speed);
+ runUntilPendingCommandsAreFullyHandled(player);
+ player.release();
assertThat(reportedPlaybackSpeeds).containsExactly(1.1f, 1.2f, 1.3f).inOrder();
assertThat(maskedPlaybackSpeeds).isEqualTo(reportedPlaybackSpeeds);
@@ -3887,46 +3876,28 @@ public final class ExoPlayerTest {
public void
setUnsupportedPlaybackSpeedConsecutivelyNotifiesListenerForEveryChangeOnceAndResetsOnceHandled()
throws Exception {
- Renderer renderer =
- new FakeMediaClockRenderer(C.TRACK_TYPE_AUDIO) {
- @Override
- public long getPositionUs() {
- return 0;
- }
-
- @Override
- public void setPlaybackParameters(PlaybackParameters playbackParameters) {}
-
- @Override
- public PlaybackParameters getPlaybackParameters() {
- return PlaybackParameters.DEFAULT;
- }
- };
- ActionSchedule actionSchedule =
- new ActionSchedule.Builder(TAG)
- .pause()
- .waitForPlaybackState(Player.STATE_READY)
- .setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.1f))
- .setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.2f))
- .setPlaybackParameters(new PlaybackParameters(/* speed= */ 1.3f))
- .play()
+ ExoPlayer player =
+ new TestExoPlayerBuilder(context)
+ .setRenderers(new AudioClockRendererWithoutSpeedChangeSupport())
.build();
List reportedPlaybackParameters = new ArrayList<>();
- Player.Listener listener =
+ player.addListener(
new Player.Listener() {
@Override
public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
reportedPlaybackParameters.add(playbackParameters);
}
- };
- new ExoPlayerTestRunner.Builder(context)
- .setSupportedFormats(ExoPlayerTestRunner.AUDIO_FORMAT)
- .setRenderers(renderer)
- .setActionSchedule(actionSchedule)
- .setPlayerListener(listener)
- .build()
- .start()
- .blockUntilEnded(TIMEOUT_MS);
+ });
+ player.setMediaSource(
+ new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.AUDIO_FORMAT));
+ player.prepare();
+ runUntilPlaybackState(player, Player.STATE_READY);
+
+ player.setPlaybackSpeed(1.1f);
+ player.setPlaybackSpeed(1.2f);
+ player.setPlaybackSpeed(1.3f);
+ runUntilPendingCommandsAreFullyHandled(player);
+ player.release();
assertThat(reportedPlaybackParameters)
.containsExactly(
@@ -3937,6 +3908,51 @@ public final class ExoPlayerTest {
.inOrder();
}
+ @Test
+ public void
+ setUnsupportedPlaybackSpeedDirectlyFollowedByDisablingTheRendererAndSupportedPlaybackSpeed_keepsCorrectFinalSpeedAndInformsListenersCorrectly()
+ throws Exception {
+ ExoPlayer player =
+ new TestExoPlayerBuilder(context)
+ .setRenderers(new AudioClockRendererWithoutSpeedChangeSupport())
+ .build();
+ List reportedPlaybackParameters = new ArrayList<>();
+ player.addListener(
+ new Player.Listener() {
+ @Override
+ public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
+ reportedPlaybackParameters.add(playbackParameters);
+ }
+ });
+ player.setMediaSource(
+ new FakeMediaSource(new FakeTimeline(), ExoPlayerTestRunner.AUDIO_FORMAT));
+ player.prepare();
+ runUntilPlaybackState(player, Player.STATE_READY);
+
+ player.setPlaybackSpeed(2f);
+ // We need to do something that reliably triggers a position sync with the renderer, but no
+ // further playback progress as we want to test what happens if the parameter reset is still
+ // pending when we disable the audio renderer below. Calling play and pause will achieve this.
+ player.play();
+ player.pause();
+ // Disabling the audio renderer and setting a new speed should work, and should not be affected
+ // by the still pending parameter reset from above.
+ player.setTrackSelectionParameters(
+ player
+ .getTrackSelectionParameters()
+ .buildUpon()
+ .setTrackTypeDisabled(C.TRACK_TYPE_AUDIO, /* disabled= */ true)
+ .build());
+ player.setPlaybackSpeed(5f);
+ runUntilPendingCommandsAreFullyHandled(player);
+ player.release();
+
+ assertThat(reportedPlaybackParameters)
+ .containsExactly(
+ new PlaybackParameters(/* speed= */ 2f), new PlaybackParameters(/* speed= */ 5f))
+ .inOrder();
+ }
+
@Test
public void simplePlaybackHasNoPlaybackSuppression() throws Exception {
ActionSchedule actionSchedule =
@@ -10071,8 +10087,10 @@ public final class ExoPlayerTest {
@Test
public void setPlaybackSpeed_withAdPlayback_onlyAppliesToContent() throws Exception {
- // Create renderer with media clock to listen to playback parameter changes.
+ // Create renderer with media clock to listen to playback parameter changes and reported speed
+ // changes.
ArrayList playbackParameters = new ArrayList<>();
+ ArrayList> speedChanges = new ArrayList<>();
FakeMediaClockRenderer audioRenderer =
new FakeMediaClockRenderer(C.TRACK_TYPE_AUDIO) {
private long positionUs;
@@ -10100,6 +10118,12 @@ public final class ExoPlayerTest {
? PlaybackParameters.DEFAULT
: Iterables.getLast(playbackParameters);
}
+
+ @Override
+ public void setPlaybackSpeed(float currentPlaybackSpeed, float targetPlaybackSpeed)
+ throws ExoPlaybackException {
+ speedChanges.add(Pair.create(currentPlaybackSpeed, targetPlaybackSpeed));
+ }
};
ExoPlayer player = new TestExoPlayerBuilder(context).setRenderers(audioRenderer).build();
AdPlaybackState adPlaybackState =
@@ -10132,7 +10156,7 @@ public final class ExoPlayerTest {
runUntilPlaybackState(player, Player.STATE_ENDED);
player.release();
- // Assert that the renderer received the playback speed updates at each ad/content boundary.
+ // Assert that the media clock received the playback parameters at each ad/content boundary.
assertThat(playbackParameters)
.containsExactly(
/* preroll ad */ new PlaybackParameters(1f),
@@ -10143,6 +10167,18 @@ public final class ExoPlayerTest {
/* content after postroll */ new PlaybackParameters(5f))
.inOrder();
+ // Assert that the renderer received the speed changes at each ad/content boundary.
+ assertThat(speedChanges)
+ .containsExactly(
+ /* initial setup */ Pair.create(5f, 5f),
+ /* preroll ad */ Pair.create(1f, 5f),
+ /* content after preroll */ Pair.create(5f, 5f),
+ /* midroll ad */ Pair.create(1f, 5f),
+ /* content after midroll */ Pair.create(5f, 5f),
+ /* postroll ad */ Pair.create(1f, 5f),
+ /* content after postroll */ Pair.create(5f, 5f))
+ .inOrder();
+
// Assert that user-set speed was reported, but none of the ad overrides.
verify(mockListener).onPlaybackParametersChanged(any());
verify(mockListener).onPlaybackParametersChanged(new PlaybackParameters(5.0f));
@@ -12436,6 +12472,46 @@ public final class ExoPlayerTest {
}
}
+ private static final class AudioClockRendererWithoutSpeedChangeSupport
+ extends FakeMediaClockRenderer {
+
+ private PlaybackParameters playbackParameters;
+ private boolean delayingPlaybackParameterReset;
+ private long positionUs;
+
+ public AudioClockRendererWithoutSpeedChangeSupport() {
+ super(C.TRACK_TYPE_AUDIO);
+ playbackParameters = PlaybackParameters.DEFAULT;
+ }
+
+ @Override
+ protected void onPositionReset(long positionUs, boolean joining) throws ExoPlaybackException {
+ super.onPositionReset(positionUs, joining);
+ this.positionUs = positionUs;
+ }
+
+ @Override
+ public long getPositionUs() {
+ return positionUs;
+ }
+
+ @Override
+ public void setPlaybackParameters(PlaybackParameters playbackParameters) {
+ this.playbackParameters = playbackParameters;
+ // Similar to a real renderer, the missing speed support is only detected with a delay.
+ delayingPlaybackParameterReset = true;
+ }
+
+ @Override
+ public PlaybackParameters getPlaybackParameters() {
+ if (delayingPlaybackParameterReset) {
+ delayingPlaybackParameterReset = false;
+ return playbackParameters;
+ }
+ return PlaybackParameters.DEFAULT;
+ }
+ }
+
/**
* Returns an argument matcher for {@link Timeline} instances that ignores period and window uids.
*/
diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapterTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapterTest.java
index 98707a3226..ee7026a669 100644
--- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapterTest.java
+++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/AsynchronousMediaCodecAdapterTest.java
@@ -80,6 +80,20 @@ public class AsynchronousMediaCodecAdapterTest {
assertThrows(IllegalStateException.class, () -> adapter.dequeueInputBufferIndex());
}
+ @Test
+ public void dequeueInputBufferIndex_withPendingQueueingError_throwsException() {
+ // Force MediaCodec to throw an error by attempting to queue input buffer -1.
+ adapter.queueInputBuffer(
+ /* index= */ -1,
+ /* offset= */ 0,
+ /* size= */ 0,
+ /* presentationTimeUs= */ 0,
+ /* flags= */ 0);
+ shadowOf(queueingThread.getLooper()).idle();
+
+ assertThrows(IllegalStateException.class, () -> adapter.dequeueInputBufferIndex());
+ }
+
@Test
public void dequeueInputBufferIndex_afterShutdown_returnsTryAgainLater() {
adapter.release();
@@ -123,6 +137,20 @@ public class AsynchronousMediaCodecAdapterTest {
assertThrows(IllegalStateException.class, () -> adapter.dequeueOutputBufferIndex(bufferInfo));
}
+ @Test
+ public void dequeueOutputBufferIndex_withPendingQueueingError_throwsException() {
+ // Force MediaCodec to throw an error by attempting to queue input buffer -1.
+ adapter.queueInputBuffer(
+ /* index= */ -1,
+ /* offset= */ 0,
+ /* size= */ 0,
+ /* presentationTimeUs= */ 0,
+ /* flags= */ 0);
+ shadowOf(queueingThread.getLooper()).idle();
+
+ assertThrows(IllegalStateException.class, () -> adapter.dequeueOutputBufferIndex(bufferInfo));
+ }
+
@Test
public void dequeueOutputBufferIndex_afterShutdown_returnsTryAgainLater() {
int index = adapter.dequeueInputBufferIndex();
diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecRendererTest.java
new file mode 100644
index 0000000000..ac86669ee5
--- /dev/null
+++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/mediacodec/MediaCodecRendererTest.java
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package androidx.media3.exoplayer.mediacodec;
+
+import static androidx.media3.exoplayer.DecoderReuseEvaluation.REUSE_RESULT_YES_WITHOUT_RECONFIGURATION;
+import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
+import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.inOrder;
+import static org.mockito.Mockito.spy;
+
+import android.media.MediaCrypto;
+import android.media.MediaFormat;
+import android.os.SystemClock;
+import androidx.annotation.Nullable;
+import androidx.media3.common.C;
+import androidx.media3.common.Format;
+import androidx.media3.common.MimeTypes;
+import androidx.media3.exoplayer.DecoderReuseEvaluation;
+import androidx.media3.exoplayer.ExoPlaybackException;
+import androidx.media3.exoplayer.RendererCapabilities;
+import androidx.media3.exoplayer.RendererConfiguration;
+import androidx.media3.exoplayer.analytics.PlayerId;
+import androidx.media3.exoplayer.drm.DrmSessionEventListener;
+import androidx.media3.exoplayer.drm.DrmSessionManager;
+import androidx.media3.exoplayer.upstream.DefaultAllocator;
+import androidx.media3.test.utils.FakeSampleStream;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.google.common.collect.ImmutableList;
+import java.nio.ByteBuffer;
+import java.util.Collections;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InOrder;
+
+/** Unit tests for {@link MediaCodecRenderer} */
+@RunWith(AndroidJUnit4.class)
+public class MediaCodecRendererTest {
+
+ @Test
+ public void render_withReplaceStream_triggersOutputCallbacksInCorrectOrder() throws Exception {
+ Format format1 =
+ new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1000).build();
+ Format format2 =
+ new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1500).build();
+ FakeSampleStream fakeSampleStream1 =
+ createFakeSampleStream(format1, /* sampleTimesUs...= */ 0, 100, 200, 300);
+ FakeSampleStream fakeSampleStream2 =
+ createFakeSampleStream(format2, /* sampleTimesUs...= */ 0, 100, 200);
+ MediaCodecRenderer renderer = spy(new TestRenderer());
+ renderer.init(/* index= */ 0, PlayerId.UNSET);
+
+ renderer.enable(
+ RendererConfiguration.DEFAULT,
+ new Format[] {format1},
+ fakeSampleStream1,
+ /* positionUs= */ 0,
+ /* joining= */ false,
+ /* mayRenderStartOfStream= */ true,
+ /* startPositionUs= */ 0,
+ /* offsetUs= */ 0);
+ renderer.start();
+ long positionUs = 0;
+ while (!renderer.hasReadStreamToEnd()) {
+ renderer.render(positionUs, SystemClock.elapsedRealtime());
+ positionUs += 100;
+ }
+ renderer.replaceStream(
+ new Format[] {format2}, fakeSampleStream2, /* startPositionUs= */ 400, /* offsetUs= */ 400);
+ renderer.setCurrentStreamFinal();
+ while (!renderer.isEnded()) {
+ renderer.render(positionUs, SystemClock.elapsedRealtime());
+ positionUs += 100;
+ }
+
+ InOrder inOrder = inOrder(renderer);
+ inOrder.verify(renderer).onOutputStreamOffsetUsChanged(0);
+ inOrder.verify(renderer).onOutputFormatChanged(eq(format1), any());
+ inOrder.verify(renderer).onProcessedOutputBuffer(0);
+ inOrder.verify(renderer).onProcessedOutputBuffer(100);
+ inOrder.verify(renderer).onProcessedOutputBuffer(200);
+ inOrder.verify(renderer).onProcessedOutputBuffer(300);
+ inOrder.verify(renderer).onOutputStreamOffsetUsChanged(400);
+ inOrder.verify(renderer).onProcessedStreamChange();
+ inOrder.verify(renderer).onOutputFormatChanged(eq(format2), any());
+ inOrder.verify(renderer).onProcessedOutputBuffer(400);
+ inOrder.verify(renderer).onProcessedOutputBuffer(500);
+ inOrder.verify(renderer).onProcessedOutputBuffer(600);
+ }
+
+ @Test
+ public void
+ render_withReplaceStreamAndBufferBeyondDuration_triggersOutputCallbacksInCorrectOrder()
+ throws Exception {
+ Format format1 =
+ new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1000).build();
+ Format format2 =
+ new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1500).build();
+ FakeSampleStream fakeSampleStream1 =
+ createFakeSampleStream(format1, /* sampleTimesUs...= */ 0, 100, 200, 300, 400, 500, 600);
+ FakeSampleStream fakeSampleStream2 =
+ createFakeSampleStream(format2, /* sampleTimesUs...= */ 0, 100, 200);
+ MediaCodecRenderer renderer = spy(new TestRenderer());
+ renderer.init(/* index= */ 0, PlayerId.UNSET);
+
+ renderer.enable(
+ RendererConfiguration.DEFAULT,
+ new Format[] {format1},
+ fakeSampleStream1,
+ /* positionUs= */ 0,
+ /* joining= */ false,
+ /* mayRenderStartOfStream= */ true,
+ /* startPositionUs= */ 0,
+ /* offsetUs= */ 0);
+ renderer.start();
+ long positionUs = 0;
+ while (!renderer.hasReadStreamToEnd()) {
+ renderer.render(positionUs, SystemClock.elapsedRealtime());
+ positionUs += 100;
+ }
+ renderer.replaceStream(
+ new Format[] {format2}, fakeSampleStream2, /* startPositionUs= */ 400, /* offsetUs= */ 400);
+ renderer.setCurrentStreamFinal();
+ while (!renderer.isEnded()) {
+ renderer.render(positionUs, SystemClock.elapsedRealtime());
+ positionUs += 100;
+ }
+
+ InOrder inOrder = inOrder(renderer);
+ inOrder.verify(renderer).onOutputStreamOffsetUsChanged(0);
+ inOrder.verify(renderer).onOutputFormatChanged(eq(format1), any());
+ inOrder.verify(renderer).onProcessedOutputBuffer(0);
+ inOrder.verify(renderer).onProcessedOutputBuffer(100);
+ inOrder.verify(renderer).onProcessedOutputBuffer(200);
+ inOrder.verify(renderer).onProcessedOutputBuffer(300);
+ inOrder.verify(renderer).onProcessedOutputBuffer(400);
+ inOrder.verify(renderer).onProcessedOutputBuffer(500);
+ inOrder.verify(renderer).onProcessedOutputBuffer(600);
+ inOrder.verify(renderer).onOutputStreamOffsetUsChanged(400);
+ inOrder.verify(renderer).onProcessedStreamChange();
+ inOrder.verify(renderer).onOutputFormatChanged(eq(format2), any());
+ inOrder.verify(renderer).onProcessedOutputBuffer(400);
+ inOrder.verify(renderer).onProcessedOutputBuffer(500);
+ inOrder.verify(renderer).onProcessedOutputBuffer(600);
+ }
+
+ @Test
+ public void
+ render_withReplaceStreamAndBufferLessThanStartPosition_triggersOutputCallbacksInCorrectOrder()
+ throws Exception {
+ Format format1 =
+ new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1000).build();
+ Format format2 =
+ new Format.Builder().setSampleMimeType(MimeTypes.AUDIO_AAC).setAverageBitrate(1500).build();
+ FakeSampleStream fakeSampleStream1 =
+ createFakeSampleStream(format1, /* sampleTimesUs...= */ 0, 100, 200, 300);
+ FakeSampleStream fakeSampleStream2 =
+ createFakeSampleStream(format2, /* sampleTimesUs...= */ 0, 100, 200, 300, 400);
+ MediaCodecRenderer renderer = spy(new TestRenderer());
+ renderer.init(/* index= */ 0, PlayerId.UNSET);
+
+ renderer.enable(
+ RendererConfiguration.DEFAULT,
+ new Format[] {format1},
+ fakeSampleStream1,
+ /* positionUs= */ 0,
+ /* joining= */ false,
+ /* mayRenderStartOfStream= */ true,
+ /* startPositionUs= */ 0,
+ /* offsetUs= */ 0);
+ renderer.start();
+ long positionUs = 0;
+ while (!renderer.hasReadStreamToEnd()) {
+ renderer.render(positionUs, SystemClock.elapsedRealtime());
+ positionUs += 100;
+ }
+ renderer.replaceStream(
+ new Format[] {format2}, fakeSampleStream2, /* startPositionUs= */ 400, /* offsetUs= */ 200);
+ renderer.setCurrentStreamFinal();
+ while (!renderer.isEnded()) {
+ renderer.render(positionUs, SystemClock.elapsedRealtime());
+ positionUs += 100;
+ }
+
+ InOrder inOrder = inOrder(renderer);
+ inOrder.verify(renderer).onOutputStreamOffsetUsChanged(0);
+ inOrder.verify(renderer).onOutputFormatChanged(eq(format1), any());
+ inOrder.verify(renderer).onProcessedOutputBuffer(0);
+ inOrder.verify(renderer).onProcessedOutputBuffer(100);
+ inOrder.verify(renderer).onProcessedOutputBuffer(200);
+ inOrder.verify(renderer).onProcessedOutputBuffer(300);
+ inOrder.verify(renderer).onOutputStreamOffsetUsChanged(200);
+ inOrder.verify(renderer).onProcessedStreamChange();
+ inOrder.verify(renderer).onOutputFormatChanged(eq(format2), any());
+ inOrder.verify(renderer).onProcessedOutputBuffer(200);
+ inOrder.verify(renderer).onProcessedOutputBuffer(300);
+ inOrder.verify(renderer).onProcessedOutputBuffer(400);
+ inOrder.verify(renderer).onProcessedOutputBuffer(500);
+ inOrder.verify(renderer).onProcessedOutputBuffer(600);
+ }
+
+ private FakeSampleStream createFakeSampleStream(Format format, long... sampleTimesUs) {
+ ImmutableList.Builder sampleListBuilder =
+ ImmutableList.builder();
+ for (long sampleTimeUs : sampleTimesUs) {
+ sampleListBuilder.add(oneByteSample(sampleTimeUs, C.BUFFER_FLAG_KEY_FRAME));
+ }
+ sampleListBuilder.add(END_OF_STREAM_ITEM);
+ FakeSampleStream sampleStream =
+ new FakeSampleStream(
+ new DefaultAllocator(/* trimOnReset= */ true, /* individualAllocationSize= */ 1024),
+ /* mediaSourceEventDispatcher= */ null,
+ DrmSessionManager.DRM_UNSUPPORTED,
+ new DrmSessionEventListener.EventDispatcher(),
+ /* initialFormat= */ format,
+ sampleListBuilder.build());
+ sampleStream.writeData(/* startPositionUs= */ 0);
+ return sampleStream;
+ }
+
+ private static class TestRenderer extends MediaCodecRenderer {
+
+ public TestRenderer() {
+ super(
+ C.TRACK_TYPE_AUDIO,
+ MediaCodecAdapter.Factory.DEFAULT,
+ /* mediaCodecSelector= */ (mimeType, requiresSecureDecoder, requiresTunnelingDecoder) ->
+ Collections.singletonList(
+ MediaCodecInfo.newInstance(
+ /* name= */ "name",
+ /* mimeType= */ mimeType,
+ /* codecMimeType= */ mimeType,
+ /* capabilities= */ null,
+ /* hardwareAccelerated= */ false,
+ /* softwareOnly= */ true,
+ /* vendor= */ false,
+ /* forceDisableAdaptive= */ false,
+ /* forceSecure= */ false)),
+ /* enableDecoderFallback= */ false,
+ /* assumedMinimumCodecOperatingRate= */ 44100);
+ }
+
+ @Override
+ public String getName() {
+ return "test";
+ }
+
+ @Override
+ protected @Capabilities int supportsFormat(MediaCodecSelector mediaCodecSelector, Format format)
+ throws MediaCodecUtil.DecoderQueryException {
+ return RendererCapabilities.create(C.FORMAT_HANDLED);
+ }
+
+ @Override
+ protected List getDecoderInfos(
+ MediaCodecSelector mediaCodecSelector, Format format, boolean requiresSecureDecoder)
+ throws MediaCodecUtil.DecoderQueryException {
+ return mediaCodecSelector.getDecoderInfos(
+ format.sampleMimeType,
+ /* requiresSecureDecoder= */ false,
+ /* requiresTunnelingDecoder= */ false);
+ }
+
+ @Override
+ protected MediaCodecAdapter.Configuration getMediaCodecConfiguration(
+ MediaCodecInfo codecInfo,
+ Format format,
+ @Nullable MediaCrypto crypto,
+ float codecOperatingRate) {
+ return MediaCodecAdapter.Configuration.createForAudioDecoding(
+ codecInfo, new MediaFormat(), format, crypto);
+ }
+
+ @Override
+ protected boolean processOutputBuffer(
+ long positionUs,
+ long elapsedRealtimeUs,
+ @Nullable MediaCodecAdapter codec,
+ @Nullable ByteBuffer buffer,
+ int bufferIndex,
+ int bufferFlags,
+ int sampleCount,
+ long bufferPresentationTimeUs,
+ boolean isDecodeOnlyBuffer,
+ boolean isLastBuffer,
+ Format format)
+ throws ExoPlaybackException {
+ if (bufferPresentationTimeUs <= positionUs) {
+ // Only release buffers when the position advances far enough for realistic behavior where
+ // input of buffers to the codec is faster than output.
+ codec.releaseOutputBuffer(bufferIndex, /* render= */ true);
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ protected DecoderReuseEvaluation canReuseCodec(
+ MediaCodecInfo codecInfo, Format oldFormat, Format newFormat) {
+ return new DecoderReuseEvaluation(
+ codecInfo.name,
+ oldFormat,
+ newFormat,
+ REUSE_RESULT_YES_WITHOUT_RECONFIGURATION,
+ /* discardReasons= */ 0);
+ }
+ }
+}
diff --git a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java
index d42ab38fe0..b7ad8dc6c2 100644
--- a/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java
+++ b/libraries/exoplayer/src/test/java/androidx/media3/exoplayer/video/MediaCodecVideoRendererTest.java
@@ -16,6 +16,7 @@
package androidx.media3.exoplayer.video;
import static android.view.Display.DEFAULT_DISPLAY;
+import static androidx.media3.common.util.Util.msToUs;
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.END_OF_STREAM_ITEM;
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.format;
import static androidx.media3.test.utils.FakeSampleStream.FakeSampleStreamItem.oneByteSample;
@@ -59,6 +60,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.junit.After;
import org.junit.Before;
@@ -72,6 +74,7 @@ import org.mockito.junit.MockitoRule;
import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowDisplay;
import org.robolectric.shadows.ShadowLooper;
+import org.robolectric.shadows.ShadowSystemClock;
/** Unit test for {@link MediaCodecVideoRenderer}. */
@RunWith(AndroidJUnit4.class)
@@ -261,7 +264,7 @@ public class MediaCodecVideoRendererTest {
/* initialFormat= */ pAsp1,
ImmutableList.of(oneByteSample(/* timeUs= */ 0, C.BUFFER_FLAG_KEY_FRAME)));
fakeSampleStream.writeData(/* startPositionUs= */ 0);
-
+ SystemClock.setCurrentTimeMillis(876_000_000);
mediaCodecVideoRenderer.enable(
RendererConfiguration.DEFAULT,
new Format[] {pAsp1, pAsp2, pAsp3},
@@ -272,25 +275,27 @@ public class MediaCodecVideoRendererTest {
/* startPositionUs= */ 0,
/* offsetUs */ 0);
mediaCodecVideoRenderer.start();
- mediaCodecVideoRenderer.render(/* positionUs= */ 0, SystemClock.elapsedRealtime() * 1000);
- mediaCodecVideoRenderer.render(/* positionUs= */ 250, SystemClock.elapsedRealtime() * 1000);
+ mediaCodecVideoRenderer.render(/* positionUs= */ 0, msToUs(SystemClock.elapsedRealtime()));
+ ShadowSystemClock.advanceBy(10, TimeUnit.MILLISECONDS);
+ mediaCodecVideoRenderer.render(/* positionUs= */ 10_000, msToUs(SystemClock.elapsedRealtime()));
fakeSampleStream.append(
ImmutableList.of(
format(pAsp2),
- oneByteSample(/* timeUs= */ 5_000),
- oneByteSample(/* timeUs= */ 10_000),
- format(pAsp3),
- oneByteSample(/* timeUs= */ 15_000),
oneByteSample(/* timeUs= */ 20_000),
+ oneByteSample(/* timeUs= */ 40_000),
+ format(pAsp3),
+ oneByteSample(/* timeUs= */ 60_000),
+ oneByteSample(/* timeUs= */ 80_000),
END_OF_STREAM_ITEM));
- fakeSampleStream.writeData(/* startPositionUs= */ 5_000);
+ fakeSampleStream.writeData(/* startPositionUs= */ 20_000);
mediaCodecVideoRenderer.setCurrentStreamFinal();
- int pos = 500;
+ int positionUs = 20_000;
do {
- mediaCodecVideoRenderer.render(/* positionUs= */ pos, SystemClock.elapsedRealtime() * 1000);
- pos += 250;
+ ShadowSystemClock.advanceBy(10, TimeUnit.MILLISECONDS);
+ mediaCodecVideoRenderer.render(positionUs, msToUs(SystemClock.elapsedRealtime()));
+ positionUs += 10_000;
} while (!mediaCodecVideoRenderer.isEnded());
shadowOf(testMainLooper).idle();
diff --git a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/offline/DashDownloader.java b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/offline/DashDownloader.java
index e3c4172728..73a59291b3 100644
--- a/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/offline/DashDownloader.java
+++ b/libraries/exoplayer_dash/src/main/java/androidx/media3/exoplayer/dash/offline/DashDownloader.java
@@ -100,7 +100,30 @@ public final class DashDownloader extends SegmentDownloader {
*/
public DashDownloader(
MediaItem mediaItem, CacheDataSource.Factory cacheDataSourceFactory, Executor executor) {
- this(mediaItem, new DashManifestParser(), cacheDataSourceFactory, executor);
+ this(
+ mediaItem,
+ new DashManifestParser(),
+ cacheDataSourceFactory,
+ executor,
+ DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS);
+ }
+
+ /**
+ * @deprecated Use {@link DashDownloader#DashDownloader(MediaItem, Parser,
+ * CacheDataSource.Factory, Executor, long)} instead.
+ */
+ @Deprecated
+ public DashDownloader(
+ MediaItem mediaItem,
+ Parser manifestParser,
+ CacheDataSource.Factory cacheDataSourceFactory,
+ Executor executor) {
+ this(
+ mediaItem,
+ manifestParser,
+ cacheDataSourceFactory,
+ executor,
+ DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS);
}
/**
@@ -113,13 +136,22 @@ public final class DashDownloader extends SegmentDownloader {
* @param executor An {@link Executor} used to make requests for the media being downloaded.
* Providing an {@link Executor} that uses multiple threads will speed up the download by
* allowing parts of it to be executed in parallel.
+ * @param maxMergedSegmentStartTimeDiffMs The maximum difference of the start time of two
+ * segments, up to which the segments (of the same URI) should be merged into a single
+ * download segment, in milliseconds.
*/
public DashDownloader(
MediaItem mediaItem,
Parser manifestParser,
CacheDataSource.Factory cacheDataSourceFactory,
- Executor executor) {
- super(mediaItem, manifestParser, cacheDataSourceFactory, executor);
+ Executor executor,
+ long maxMergedSegmentStartTimeDiffMs) {
+ super(
+ mediaItem,
+ manifestParser,
+ cacheDataSourceFactory,
+ executor,
+ maxMergedSegmentStartTimeDiffMs);
baseUrlExclusionList = new BaseUrlExclusionList();
}
diff --git a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/offline/HlsDownloader.java b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/offline/HlsDownloader.java
index 777694e9f6..7c82bee608 100644
--- a/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/offline/HlsDownloader.java
+++ b/libraries/exoplayer_hls/src/main/java/androidx/media3/exoplayer/hls/offline/HlsDownloader.java
@@ -89,7 +89,30 @@ public final class HlsDownloader extends SegmentDownloader {
*/
public HlsDownloader(
MediaItem mediaItem, CacheDataSource.Factory cacheDataSourceFactory, Executor executor) {
- this(mediaItem, new HlsPlaylistParser(), cacheDataSourceFactory, executor);
+ this(
+ mediaItem,
+ new HlsPlaylistParser(),
+ cacheDataSourceFactory,
+ executor,
+ DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS);
+ }
+
+ /**
+ * @deprecated Use {@link HlsDownloader#HlsDownloader(MediaItem, Parser, CacheDataSource.Factory,
+ * Executor, long)} instead.
+ */
+ @Deprecated
+ public HlsDownloader(
+ MediaItem mediaItem,
+ Parser manifestParser,
+ CacheDataSource.Factory cacheDataSourceFactory,
+ Executor executor) {
+ this(
+ mediaItem,
+ manifestParser,
+ cacheDataSourceFactory,
+ executor,
+ DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS);
}
/**
@@ -102,13 +125,22 @@ public final class HlsDownloader extends SegmentDownloader {
* @param executor An {@link Executor} used to make requests for the media being downloaded.
* Providing an {@link Executor} that uses multiple threads will speed up the download by
* allowing parts of it to be executed in parallel.
+ * @param maxMergedSegmentStartTimeDiffMs The maximum difference of the start time of two
+ * segments, up to which the segments (of the same URI) should be merged into a single
+ * download segment, in milliseconds.
*/
public HlsDownloader(
MediaItem mediaItem,
Parser manifestParser,
CacheDataSource.Factory cacheDataSourceFactory,
- Executor executor) {
- super(mediaItem, manifestParser, cacheDataSourceFactory, executor);
+ Executor executor,
+ long maxMergedSegmentStartTimeDiffMs) {
+ super(
+ mediaItem,
+ manifestParser,
+ cacheDataSourceFactory,
+ executor,
+ maxMergedSegmentStartTimeDiffMs);
}
@Override
diff --git a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java
index e029b74578..d9ec2dbe37 100644
--- a/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java
+++ b/libraries/exoplayer_ima/src/main/java/androidx/media3/exoplayer/ima/ImaServerSideAdInsertionMediaSource.java
@@ -554,11 +554,10 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
StreamManagerLoadable streamManagerLoadable =
new StreamManagerLoadable(
sdkAdsLoader,
- adsLoader.configuration,
+ /* imaServerSideAdInsertionMediaSource= */ this,
streamRequest,
streamPlayer,
- applicationAdErrorListener,
- loadVideoTimeoutMs);
+ applicationAdErrorListener);
loader.startLoading(
streamManagerLoadable,
new StreamManagerLoadableCallback(),
@@ -633,7 +632,6 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
}
this.streamManager.removeAdEventListener(componentListener);
this.streamManager.destroy();
- this.streamManager = null;
}
this.streamManager = streamManager;
if (streamManager != null) {
@@ -644,6 +642,12 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
if (applicationAdErrorListener != null) {
streamManager.addAdErrorListener(applicationAdErrorListener);
}
+ AdsRenderingSettings adsRenderingSettings =
+ ImaSdkFactory.getInstance().createAdsRenderingSettings();
+ adsRenderingSettings.setLoadVideoTimeout(loadVideoTimeoutMs);
+ adsRenderingSettings.setFocusSkipButtonWhenAvailable(
+ adsLoader.configuration.focusSkipButtonWhenAvailable);
+ streamManager.init(adsRenderingSettings);
}
}
@@ -952,7 +956,6 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
@Override
public void onLoadCompleted(
StreamManagerLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) {
- mainHandler.post(() -> setStreamManager(checkNotNull(loadable.getStreamManager())));
setContentUri(checkNotNull(loadable.getContentUri()));
}
@@ -983,14 +986,12 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
implements Loadable, AdsLoadedListener, AdErrorListener {
private final com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader;
- private final ServerSideAdInsertionConfiguration serverSideAdInsertionConfiguration;
+ private final ImaServerSideAdInsertionMediaSource imaServerSideAdInsertionMediaSource;
private final StreamRequest request;
private final StreamPlayer streamPlayer;
@Nullable private final AdErrorListener adErrorListener;
- private final int loadVideoTimeoutMs;
private final ConditionVariable conditionVariable;
- @Nullable private volatile StreamManager streamManager;
@Nullable private volatile Uri contentUri;
private volatile boolean cancelled;
private volatile boolean error;
@@ -1000,17 +1001,15 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
/** Creates an instance. */
private StreamManagerLoadable(
com.google.ads.interactivemedia.v3.api.AdsLoader adsLoader,
- ServerSideAdInsertionConfiguration serverSideAdInsertionConfiguration,
+ ImaServerSideAdInsertionMediaSource imaServerSideAdInsertionMediaSource,
StreamRequest request,
StreamPlayer streamPlayer,
- @Nullable AdErrorListener adErrorListener,
- int loadVideoTimeoutMs) {
+ @Nullable AdErrorListener adErrorListener) {
this.adsLoader = adsLoader;
- this.serverSideAdInsertionConfiguration = serverSideAdInsertionConfiguration;
+ this.imaServerSideAdInsertionMediaSource = imaServerSideAdInsertionMediaSource;
this.request = request;
this.streamPlayer = streamPlayer;
this.adErrorListener = adErrorListener;
- this.loadVideoTimeoutMs = loadVideoTimeoutMs;
conditionVariable = new ConditionVariable();
errorCode = -1;
}
@@ -1021,12 +1020,6 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
return contentUri;
}
- /** Returns the stream manager or null if not yet loaded. */
- @Nullable
- public StreamManager getStreamManager() {
- return streamManager;
- }
-
// Implement Loadable.
@Override
@@ -1080,14 +1073,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
conditionVariable.open();
return;
}
- AdsRenderingSettings adsRenderingSettings =
- ImaSdkFactory.getInstance().createAdsRenderingSettings();
- adsRenderingSettings.setLoadVideoTimeout(loadVideoTimeoutMs);
- adsRenderingSettings.setFocusSkipButtonWhenAvailable(
- serverSideAdInsertionConfiguration.focusSkipButtonWhenAvailable);
- // After initialization completed the streamUri will be reported to the streamPlayer.
- streamManager.init(adsRenderingSettings);
- this.streamManager = streamManager;
+ imaServerSideAdInsertionMediaSource.setStreamManager(streamManager);
}
// AdErrorEvent.AdErrorListener implementation.
diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java
index 84bcc4cd8d..c8ac907a8d 100644
--- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java
+++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspClient.java
@@ -652,7 +652,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
default:
throw new IllegalStateException();
}
- } catch (ParserException e) {
+ } catch (ParserException | IllegalArgumentException e) {
dispatchRtspError(new RtspPlaybackException(e));
}
}
diff --git a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java
index b3d79404a2..b96d3d54b9 100644
--- a/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java
+++ b/libraries/exoplayer_rtsp/src/main/java/androidx/media3/exoplayer/rtsp/RtspMediaTrack.java
@@ -159,7 +159,8 @@ import com.google.common.collect.ImmutableMap;
* @param sessionUri The {@link Uri} of the RTSP playback session.
*/
public RtspMediaTrack(MediaDescription mediaDescription, Uri sessionUri) {
- checkArgument(mediaDescription.attributes.containsKey(ATTR_CONTROL));
+ checkArgument(
+ mediaDescription.attributes.containsKey(ATTR_CONTROL), "missing attribute control");
payloadFormat = generatePayloadFormat(mediaDescription);
uri = extractTrackUri(sessionUri, castNonNull(mediaDescription.attributes.get(ATTR_CONTROL)));
}
@@ -210,7 +211,7 @@ import com.google.common.collect.ImmutableMap;
switch (mimeType) {
case MimeTypes.AUDIO_AAC:
checkArgument(channelCount != C.INDEX_UNSET);
- checkArgument(!fmtpParameters.isEmpty());
+ checkArgument(!fmtpParameters.isEmpty(), "missing attribute fmtp");
if (mediaEncoding.equals(RtpPayloadFormat.RTP_MEDIA_MPEG4_LATM_AUDIO)) {
// cpresent is defined in RFC3016 Section 5.3. cpresent=0 means the config fmtp parameter
// must exist.
@@ -259,11 +260,11 @@ import com.google.common.collect.ImmutableMap;
formatBuilder.setWidth(DEFAULT_H263_WIDTH).setHeight(DEFAULT_H263_HEIGHT);
break;
case MimeTypes.VIDEO_H264:
- checkArgument(!fmtpParameters.isEmpty());
+ checkArgument(!fmtpParameters.isEmpty(), "missing attribute fmtp");
processH264FmtpAttribute(formatBuilder, fmtpParameters);
break;
case MimeTypes.VIDEO_H265:
- checkArgument(!fmtpParameters.isEmpty());
+ checkArgument(!fmtpParameters.isEmpty(), "missing attribute fmtp");
processH265FmtpAttribute(formatBuilder, fmtpParameters);
break;
case MimeTypes.VIDEO_VP8:
@@ -312,7 +313,8 @@ import com.google.common.collect.ImmutableMap;
ImmutableMap fmtpAttributes,
int channelCount,
int sampleRate) {
- checkArgument(fmtpAttributes.containsKey(PARAMETER_PROFILE_LEVEL_ID));
+ checkArgument(
+ fmtpAttributes.containsKey(PARAMETER_PROFILE_LEVEL_ID), "missing profile-level-id param");
String profileLevel = checkNotNull(fmtpAttributes.get(PARAMETER_PROFILE_LEVEL_ID));
formatBuilder.setCodecs(AAC_CODECS_PREFIX + profileLevel);
formatBuilder.setInitializationData(
@@ -380,10 +382,10 @@ import com.google.common.collect.ImmutableMap;
private static void processH264FmtpAttribute(
Format.Builder formatBuilder, ImmutableMap fmtpAttributes) {
- checkArgument(fmtpAttributes.containsKey(PARAMETER_SPROP_PARAMS));
+ checkArgument(fmtpAttributes.containsKey(PARAMETER_SPROP_PARAMS), "missing sprop parameter");
String spropParameterSets = checkNotNull(fmtpAttributes.get(PARAMETER_SPROP_PARAMS));
String[] parameterSets = Util.split(spropParameterSets, ",");
- checkArgument(parameterSets.length == 2);
+ checkArgument(parameterSets.length == 2, "empty sprop value");
ImmutableList initializationData =
ImmutableList.of(
getInitializationDataFromParameterSet(parameterSets[0]),
@@ -418,11 +420,14 @@ import com.google.common.collect.ImmutableMap;
maxDonDiff == 0, "non-zero sprop-max-don-diff " + maxDonDiff + " is not supported");
}
- checkArgument(fmtpAttributes.containsKey(PARAMETER_H265_SPROP_VPS));
+ checkArgument(
+ fmtpAttributes.containsKey(PARAMETER_H265_SPROP_VPS), "missing sprop-vps parameter");
String spropVPS = checkNotNull(fmtpAttributes.get(PARAMETER_H265_SPROP_VPS));
- checkArgument(fmtpAttributes.containsKey(PARAMETER_H265_SPROP_SPS));
+ checkArgument(
+ fmtpAttributes.containsKey(PARAMETER_H265_SPROP_SPS), "missing sprop-sps parameter");
String spropSPS = checkNotNull(fmtpAttributes.get(PARAMETER_H265_SPROP_SPS));
- checkArgument(fmtpAttributes.containsKey(PARAMETER_H265_SPROP_PPS));
+ checkArgument(
+ fmtpAttributes.containsKey(PARAMETER_H265_SPROP_PPS), "missing sprop-pps parameter");
String spropPPS = checkNotNull(fmtpAttributes.get(PARAMETER_H265_SPROP_PPS));
ImmutableList initializationData =
ImmutableList.of(
diff --git a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtspClientTest.java b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtspClientTest.java
index f7b7ab41b8..104f6ae9c3 100644
--- a/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtspClientTest.java
+++ b/libraries/exoplayer_rtsp/src/test/java/androidx/media3/exoplayer/rtsp/RtspClientTest.java
@@ -389,4 +389,68 @@ public final class RtspClientTest {
RobolectricUtil.runMainLooperUntil(timelineRequestFailed::get);
assertThat(rtspClient.getState()).isEqualTo(RtspClient.RTSP_STATE_UNINITIALIZED);
}
+
+ @Test
+ public void connectServerAndClient_sdpInDescribeResponseHasInvalidFmtpAttr_doesNotUpdateTimeline()
+ throws Exception {
+ class ResponseProvider implements RtspServer.ResponseProvider {
+ @Override
+ public RtspResponse getOptionsResponse() {
+ return new RtspResponse(
+ /* status= */ 200,
+ new RtspHeaders.Builder().add(RtspHeaders.PUBLIC, "OPTIONS, DESCRIBE").build());
+ }
+
+ @Override
+ public RtspResponse getDescribeResponse(Uri requestedUri, RtspHeaders headers) {
+ String testMediaSdpInfo =
+ "v=0\r\n"
+ + "o=- 1600785369059721 1 IN IP4 192.168.2.176\r\n"
+ + "s=video, streamed by ExoPlayer\r\n"
+ + "i=test.mkv\r\n"
+ + "t=0 0\r\n"
+ + "a=tool:ExoPlayer\r\n"
+ + "a=type:broadcast\r\n"
+ + "a=control:*\r\n"
+ + "a=range:npt=0-30.102\r\n"
+ + "m=video 0 RTP/AVP 96\r\n"
+ + "c=IN IP4 0.0.0.0\r\n"
+ + "b=AS:500\r\n"
+ + "a=rtpmap:96 H264/90000\r\n"
+ + "a=fmtp:96"
+ + " packetization-mode=1;profile-level-id=64001F;sprop-parameter-sets=\r\n"
+ + "a=control:track1\r\n";
+ return RtspTestUtils.newDescribeResponseWithSdpMessage(
+ /* sessionDescription= */ testMediaSdpInfo,
+ // This session description has no tracks.
+ /* rtpPacketStreamDumps= */ ImmutableList.of(),
+ requestedUri);
+ }
+ }
+ rtspServer = new RtspServer(new ResponseProvider());
+
+ AtomicBoolean timelineRequestFailed = new AtomicBoolean();
+ rtspClient =
+ new RtspClient(
+ new SessionInfoListener() {
+ @Override
+ public void onSessionTimelineUpdated(
+ RtspSessionTiming timing, ImmutableList tracks) {}
+
+ @Override
+ public void onSessionTimelineRequestFailed(
+ String message, @Nullable Throwable cause) {
+ timelineRequestFailed.set(true);
+ }
+ },
+ EMPTY_PLAYBACK_LISTENER,
+ /* userAgent= */ "ExoPlayer:RtspClientTest",
+ RtspTestUtils.getTestUri(rtspServer.startAndGetPortNumber()),
+ SocketFactory.getDefault(),
+ /* debugLoggingEnabled= */ false);
+ rtspClient.start();
+
+ RobolectricUtil.runMainLooperUntil(timelineRequestFailed::get);
+ assertThat(rtspClient.getState()).isEqualTo(RtspClient.RTSP_STATE_UNINITIALIZED);
+ }
}
diff --git a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifest.java b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifest.java
index 38c17af395..ccc9663ac8 100644
--- a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifest.java
+++ b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifest.java
@@ -34,8 +34,8 @@ import java.util.UUID;
/**
* Represents a SmoothStreaming manifest.
*
- * @see IIS Smooth
- * Streaming Client Manifest Format
+ * See the IIS Smooth
+ * Streaming Client Manifest Format
*/
@UnstableApi
public class SsManifest implements FilterableManifest {
diff --git a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifestParser.java b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifestParser.java
index 6ac0a24560..4160e6b3a9 100644
--- a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifestParser.java
+++ b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/manifest/SsManifestParser.java
@@ -51,8 +51,8 @@ import org.xmlpull.v1.XmlPullParserFactory;
/**
* Parses SmoothStreaming client manifests.
*
- * @see IIS Smooth
- * Streaming Client Manifest Format
+ * See the IIS Smooth
+ * Streaming Client Manifest Format
*/
@UnstableApi
public class SsManifestParser implements ParsingLoadable.Parser {
diff --git a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/offline/SsDownloader.java b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/offline/SsDownloader.java
index 4ca632271b..6d2ded5a4c 100644
--- a/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/offline/SsDownloader.java
+++ b/libraries/exoplayer_smoothstreaming/src/main/java/androidx/media3/exoplayer/smoothstreaming/offline/SsDownloader.java
@@ -93,7 +93,26 @@ public final class SsDownloader extends SegmentDownloader {
.build(),
new SsManifestParser(),
cacheDataSourceFactory,
- executor);
+ executor,
+ DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS);
+ }
+
+ /**
+ * @deprecated Use {@link SsDownloader#SsDownloader(MediaItem, Parser, CacheDataSource.Factory,
+ * Executor, long)} instead.
+ */
+ @Deprecated
+ public SsDownloader(
+ MediaItem mediaItem,
+ Parser manifestParser,
+ CacheDataSource.Factory cacheDataSourceFactory,
+ Executor executor) {
+ this(
+ mediaItem,
+ manifestParser,
+ cacheDataSourceFactory,
+ executor,
+ DEFAULT_MAX_MERGED_SEGMENT_START_TIME_DIFF_MS);
}
/**
@@ -106,13 +125,22 @@ public final class SsDownloader extends SegmentDownloader {
* @param executor An {@link Executor} used to make requests for the media being downloaded.
* Providing an {@link Executor} that uses multiple threads will speed up the download by
* allowing parts of it to be executed in parallel.
+ * @param maxMergedSegmentStartTimeDiffMs The maximum difference of the start time of two
+ * segments, up to which the segments (of the same URI) should be merged into a single
+ * download segment, in milliseconds.
*/
public SsDownloader(
MediaItem mediaItem,
Parser manifestParser,
CacheDataSource.Factory cacheDataSourceFactory,
- Executor executor) {
- super(mediaItem, manifestParser, cacheDataSourceFactory, executor);
+ Executor executor,
+ long maxMergedSegmentStartTimeDiffMs) {
+ super(
+ mediaItem,
+ manifestParser,
+ cacheDataSourceFactory,
+ executor,
+ maxMergedSegmentStartTimeDiffMs);
}
@Override
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/FlacStreamMetadata.java b/libraries/extractor/src/main/java/androidx/media3/extractor/FlacStreamMetadata.java
index debb1f84a8..7c2a9712ce 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/FlacStreamMetadata.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/FlacStreamMetadata.java
@@ -33,14 +33,18 @@ import java.util.List;
/**
* Holder for FLAC metadata.
*
- * @see FLAC format
- * METADATA_BLOCK_STREAMINFO
- * @see FLAC format
- * METADATA_BLOCK_SEEKTABLE
- * @see FLAC format
- * METADATA_BLOCK_VORBIS_COMMENT
- * @see FLAC format
- * METADATA_BLOCK_PICTURE
+ * See the following spec references:
+ *
+ *
*/
@UnstableApi
public final class FlacStreamMetadata {
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisBitArray.java b/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisBitArray.java
index a1bfbbe059..40e4b1392c 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisBitArray.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisBitArray.java
@@ -23,8 +23,8 @@ import androidx.media3.common.util.UnstableApi;
/**
* Wraps a byte array, providing methods that allow it to be read as a Vorbis bitstream.
*
- * @see Vorbis bitpacking
- * specification
+ * See the Vorbis
+ * bitpacking specification
*/
@UnstableApi
public final class VorbisBitArray {
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisUtil.java b/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisUtil.java
index 240afed6db..68832975f1 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisUtil.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/VorbisUtil.java
@@ -52,8 +52,8 @@ public final class VorbisUtil {
/**
* Vorbis identification header.
*
- * @see Vorbis
- * spec/Identification header
+ *
See the Vorbis
+ * spec/Identification header
*/
public static final class VorbisIdHeader {
@@ -135,8 +135,9 @@ public final class VorbisUtil {
/**
* Returns ilog(x), which is the index of the highest set bit in {@code x}.
*
- * @see Vorbis
- * spec
+ *
See the Vorbis
+ * spec
+ *
* @param x the value of which the ilog should be calculated.
* @return ilog(x)
*/
@@ -152,8 +153,9 @@ public final class VorbisUtil {
/**
* Reads a Vorbis identification header from {@code headerData}.
*
- * @see Vorbis
- * spec/Identification header
+ *
See the Vorbis
+ * spec/Identification header
+ *
* @param headerData a {@link ParsableByteArray} wrapping the header data.
* @return a {@link VorbisUtil.VorbisIdHeader} with meta data.
* @throws ParserException thrown if invalid capture pattern is detected.
@@ -202,8 +204,9 @@ public final class VorbisUtil {
/**
* Reads a Vorbis comment header.
*
- * @see Vorbis
- * spec/Comment header
+ *
See the Vorbis
+ * spec/Comment header
+ *
* @param headerData A {@link ParsableByteArray} wrapping the header data.
* @return A {@link VorbisUtil.CommentHeader} with all the comments.
* @throws ParserException If an error occurs parsing the comment header.
@@ -219,8 +222,9 @@ public final class VorbisUtil {
*
*
The data provided may not contain the Vorbis metadata common header and the framing bit.
*
- * @see Vorbis
- * spec/Comment header
+ *
See the Vorbis
+ * spec/Comment header
+ *
* @param headerData A {@link ParsableByteArray} wrapping the header data.
* @param hasMetadataHeader Whether the {@code headerData} contains a Vorbis metadata common
* header preceding the comment header.
@@ -349,8 +353,9 @@ public final class VorbisUtil {
* That's why we need to partially decode or at least read the entire setup header to know where
* to start reading the modes.
*
- * @see Vorbis
- * spec/Setup header
+ *
See the Vorbis
+ * spec/Setup header
+ *
* @param headerData a {@link ParsableByteArray} containing setup header data.
* @param channels the number of channels.
* @return an array of {@link Mode}s.
@@ -592,7 +597,8 @@ public final class VorbisUtil {
}
/**
- * @see _book_maptype1_quantvals
+ * See the _book_maptype1_quantvals
*/
private static long mapType1QuantValues(long entries, long dimension) {
return (long) Math.floor(Math.pow(entries, 1.d / dimension));
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java
index 4ccca487c8..5cb4b55da9 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/mp4/FragmentedMp4Extractor.java
@@ -970,6 +970,26 @@ public class FragmentedMp4Extractor implements Extractor {
return version == 1 ? tfdt.readUnsignedLongToLong() : tfdt.readUnsignedInt();
}
+ private static boolean isEdtsListDurationForEntireMediaTimeline(Track track) {
+ // Currently we only support a single edit that moves the entire media timeline (indicated by
+ // duration == 0 or (editListDurationUs + editListMediaTimeUs) >= track duration.
+ // Other uses of edit lists are uncommon and unsupported.
+ if (track.editListDurations == null
+ || track.editListDurations.length != 1
+ || track.editListMediaTimes == null) {
+ return false;
+ }
+ if (track.editListDurations[0] == 0) {
+ return true;
+ }
+ long editListEndMediaTimeUs =
+ Util.scaleLargeTimestamp(
+ track.editListDurations[0] + track.editListMediaTimes[0],
+ C.MICROS_PER_SECOND,
+ track.movieTimescale);
+ return editListEndMediaTimeUs >= track.durationUs;
+ }
+
/**
* Parses a trun atom (defined in 14496-12).
*
@@ -1017,11 +1037,8 @@ public class FragmentedMp4Extractor implements Extractor {
// ensure that the first frame's presentation timestamp is zero.
long edtsOffset = 0;
- // Currently we only support a single edit that moves the entire media timeline (indicated by
- // duration == 0). Other uses of edit lists are uncommon and unsupported.
- if (track.editListDurations != null
- && track.editListDurations.length == 1
- && track.editListDurations[0] == 0) {
+ // Currently we only support a single edit that moves the entire media timeline.
+ if (isEdtsListDurationForEntireMediaTimeline(track)) {
edtsOffset = castNonNull(track.editListMediaTimes)[0];
}
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlDecoder.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlDecoder.java
index b5f53a35fb..f9dea101f1 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlDecoder.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/ttml/TtmlDecoder.java
@@ -65,7 +65,7 @@ import org.xmlpull.v1.XmlPullParserFactory;
*
cell-resolution
*
*
- * @see TTML specification
+ * See the TTML specification
*/
@UnstableApi
public final class TtmlDecoder extends SimpleSubtitleDecoder {
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java
index 623f8fd057..e27eefdb0f 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttCssStyle.java
@@ -36,10 +36,10 @@ import java.util.HashSet;
import java.util.Set;
/**
- * Style object of a Css style block in a Webvtt file.
+ * Style object of a CSS style block in a WebVTT file.
*
- * @see W3C specification - Apply
- * CSS properties
+ *
See the Apply CSS properties
+ * section of the W3C specification
*/
@UnstableApi
public final class WebvttCssStyle {
diff --git a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttDecoder.java b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttDecoder.java
index 6a0daf5893..cb74c2b6ab 100644
--- a/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttDecoder.java
+++ b/libraries/extractor/src/main/java/androidx/media3/extractor/text/webvtt/WebvttDecoder.java
@@ -29,7 +29,7 @@ import java.util.List;
/**
* A {@link SimpleSubtitleDecoder} for WebVTT.
*
- * @see WebVTT specification
+ *
See the WebVTT specification.
*/
@UnstableApi
public final class WebvttDecoder extends SimpleSubtitleDecoder {
diff --git a/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlDecoderTest.java b/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlDecoderTest.java
index 78b7a4def7..c1b9a69fec 100644
--- a/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlDecoderTest.java
+++ b/libraries/extractor/src/test/java/androidx/media3/extractor/text/ttml/TtmlDecoderTest.java
@@ -112,14 +112,11 @@ public final class TtmlDecoderTest {
* Regression test for devices on JellyBean where some named colors are not correctly defined on
* framework level. Tests that lime resolves to #FF00FF00
not #00FF00
*
.
- *
- * @throws IOException thrown if reading subtitle file fails.
- * @see
- * JellyBean Color
- * Kitkat Color
*/
+ // JellyBean Color:
+ // https://github.com/android/platform_frameworks_base/blob/jb-mr2-release/graphics/java/android/graphics/Color.java#L414
+ // KitKat Color:
+ // https://github.com/android/platform_frameworks_base/blob/kitkat-mr2.2-release/graphics/java/android/graphics/Color.java#L414
@Test
public void lime() throws IOException, SubtitleDecoderException {
TtmlSubtitle subtitle = getSubtitle(INLINE_ATTRIBUTES_TTML_FILE);
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java b/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java
index 27c0cc4ece..30afcb411b 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaNotificationManager.java
@@ -219,9 +219,11 @@ import java.util.concurrent.TimeoutException;
if (startInForegroundRequired) {
startForeground(mediaNotification);
} else {
- maybeStopForegroundService(/* removeNotifications= */ false);
+ // Notification manager has to be updated first to avoid missing updates
+ // (https://github.com/androidx/media/issues/192).
notificationManagerCompat.notify(
mediaNotification.notificationId, mediaNotification.notification);
+ maybeStopForegroundService(/* removeNotifications= */ false);
}
}
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java
index f3045d3cda..c1cd0c6e4a 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaSession.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaSession.java
@@ -67,14 +67,16 @@ import java.util.List;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
- * A session that allows a media app to expose its transport controls and playback information in a
- * process to other processes including the Android framework and other apps. The common use cases
- * are as follows:
+ * A session that allows a media app to expose its player functionality, information of the playlist
+ * and the media item currently being played to other processes including the Android framework and
+ * other apps. The common use cases are as follows:
*
*
- * - Bluetooth/wired headset key events support
- *
- Android Auto/Wearable support
- *
- Separating UI process and playback process
+ *
- Receiving and dispatching media key events (for instance Bluetooth/wired headset and remote
+ * control devices).
+ *
- Publish media playback information and player commands to SystemUI (media notification) and
+ * Android Auto/Wear OS.
+ *
- Separating UI process and playback process.
*
*
* A session should be created when an app wants to publish media playback information or handle
@@ -82,9 +84,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* sessions can be created to provide finer grain controls of media. See Supporting Multiple Sessions for details.
*
- *
If you want to support background playback, {@link MediaSessionService} is preferred. With the
- * service, your playback can be revived even after playback is finished. See {@link
- * MediaSessionService} for details.
+ *
If an app wants to support playback when in the background, using a {@link
+ * MediaSessionService} is the preferred approach. See {@link MediaSessionService} for details.
*
*
Topics covered here:
*
@@ -103,7 +104,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* #getToken() session token} to other processes to allow them to create a {@link MediaController}
* to interact with the session.
*
- *
When a session receives transport control commands, the session sends the commands directly to
+ *
When a session receives playback commands, the session calls corresponding methods directly on
* the underlying player set by {@link Builder#Builder(Context, Player)} or {@link
* #setPlayer(Player)}.
*
@@ -113,15 +114,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
*
*
Threading Model
*
- * The instances are thread safe, but should be used on the thread with a looper.
+ *
The instances are thread safe, but must be used on a thread with a looper.
*
- *
{@link Callback} methods will be called from the application thread associated with the {@link
+ *
{@link Callback} methods will be called on the application thread associated with the {@link
* Player#getApplicationLooper() application looper} of the underlying player. When a new player is
- * set by {@link #setPlayer}, the player should use the same application looper as the previous one.
+ * set by {@link #setPlayer}, the player must use the same application looper as the previous one.
*
- *
The session listens to events from the underlying player via {@link Player.Listener} and
- * expects the callback methods to be called from the application thread. If the player violates the
- * threading contract, {@link IllegalStateException} will be thrown.
+ *
The session listens to player events via {@link Player.Listener} and expects the callback
+ * methods to be called on the application thread. If the player violates the threading contract, an
+ * {@link IllegalStateException} will be thrown.
*
*
Media Key Events Mapping
*
@@ -160,7 +161,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
*
* {@link Player#play()} otherwise
*
- * For a double tap, {@link Player#seekToNext()}
+ * In case the media key events are coming from another package ID than the package ID of
+ * the media app (events coming for instance from Bluetooth), a double tap generating two
+ * key events within a brief amount of time, is converted to {@link Player#seekToNext()}
+ *
*
*
*
@@ -193,16 +197,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* media or remote playback, since the audio focus policy
* recommends not playing multiple audio content at the same time. Also, keep in mind that multiple
- * media sessions would make Android Auto and Bluetooth device with display to show your apps
+ * media sessions would make Android Auto and Bluetooth devices with display to show your app
* multiple times, because they list up media sessions, not media apps.
*
* Backward Compatibility with Legacy Session APIs
*
- * An active {@link MediaSessionCompat} is internally created with the session for the backward
- * compatibility. It's used to handle incoming connection and commands from {@link
- * MediaControllerCompat}, and helps to utilize existing APIs that are built with legacy media
- * session APIs. Use {@link #getSessionCompatToken} to get the legacy token of {@link
- * MediaSessionCompat}.
+ *
An active {@link MediaSessionCompat} is internally created with the session for backwards
+ * compatibility. It's used to handle incoming connections and commands from {@link
+ * MediaControllerCompat} instances, and helps to utilize existing APIs that are built with legacy
+ * media session APIs.
*
*
Backward Compatibility with Legacy Controller APIs
*
@@ -211,10 +214,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* {@linkplain MediaControllerCompat AndroidX controller compat}. However, {@link ControllerInfo}
* may not be precise for legacy controllers. See {@link ControllerInfo} for the details.
*
- * Unknown package name nor UID doesn't mean that you should disallow connection nor commands.
- * For SDK levels where such issues happen, session tokens could only be obtained by trusted
- * controllers (e.g. Bluetooth, Auto, ...), so it may be better for you to allow them as you did
- * with legacy sessions.
+ *
Neither an unknown package name nor an unknown UID mean that you should disallow a connection
+ * or commands per se. For SDK levels where such issues happen, session tokens can only be obtained
+ * by trusted controllers (e.g. Bluetooth, Auto, ...). This means only trusted controllers can
+ * connect and an app can accept such controllers in the same way as with legacy sessions.
*/
public class MediaSession {
@@ -242,7 +245,7 @@ public class MediaSession {
* Creates a builder for {@link MediaSession}.
*
* @param context The context.
- * @param player The underlying player to perform playback and handle transport controls.
+ * @param player The underlying player to perform playback and handle player commands.
* @throws IllegalArgumentException if {@link Player#canAdvertiseSession()} returns false.
*/
public Builder(Context context, Player player) {
@@ -253,13 +256,13 @@ public class MediaSession {
* Sets a {@link PendingIntent} to launch an {@link android.app.Activity} for the {@link
* MediaSession}. This can be used as a quick link to an ongoing media screen.
*
- *
A client can use this pending intent to start an activity belonging to this session. When
- * this pending intent is for instance included in the notification {@linkplain
- * NotificationCompat.Builder#setContentIntent(PendingIntent) as the content intent}, tapping
- * the notification will open this activity.
- *
- *
See 'Start an
- * Activity from a Notification' also.
+ *
A client can use this pending intent to start an activity belonging to this session. On
+ * API levels below 33 the pending intent can be used {@linkplain
+ * NotificationCompat.Builder#setContentIntent(PendingIntent) as the content intent}. Tapping
+ * the notification will then send that pending intent and open the activity (see 'Start an Activity from
+ * a Notification'). For API levels starting with 33, the media notification reads the
+ * pending intent directly from the session.
*
* @param pendingIntent The pending intent.
* @return The builder to allow chaining.
@@ -290,6 +293,13 @@ public class MediaSession {
* Sets a callback for the {@link MediaSession} to handle incoming requests from {link
* MediaController}.
*
+ *
Apps that want to allow controllers to {@linkplain MediaController#setMediaItems(List)
+ * set} or {@linkplain MediaController#addMediaItems(List) add} media items to the playlist,
+ * must use a callback and override its {@link
+ * MediaSession.Callback#onSetMediaItems(MediaSession, ControllerInfo, List, int, long)} or
+ * {@link MediaSession.Callback#onSetMediaItems(MediaSession, ControllerInfo, List, int, long)}
+ * methods.
+ *
* @param callback The callback.
* @return The builder to allow chaining.
*/
@@ -371,7 +381,8 @@ public class MediaSession {
* @param remoteUserInfo The remote user info.
* @param trusted {@code true} if trusted, {@code false} otherwise.
* @param cb ControllerCb. Can be {@code null} only when a MediaBrowserCompat connects to
- * MediaSessionService and ControllerInfo is needed for SessionCallback#onConnected().
+ * MediaSessionService and ControllerInfo is needed for {@link
+ * MediaSession.Callback#onConnect(MediaSession, ControllerInfo)}.
* @param connectionHints A session-specific argument sent from the controller for the
* connection. The contents of this bundle may affect the connection result.
*/
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java
index 9d32853015..67acd1aca6 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionImpl.java
@@ -447,7 +447,7 @@ import org.checkerframework.checker.initialization.qual.Initialized;
public MediaSession.ConnectionResult onConnectOnHandler(ControllerInfo controller) {
return checkNotNull(
- callback.onConnect(instance, controller), "onConntext must return non-null future");
+ callback.onConnect(instance, controller), "onConnect must return non-null future");
}
public void onPostConnectOnHandler(ControllerInfo controller) {
diff --git a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java
index a93a6cf8a7..30b76997f5 100644
--- a/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java
+++ b/libraries/session/src/main/java/androidx/media3/session/MediaSessionService.java
@@ -57,26 +57,27 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
/**
* Superclass to be extended by services hosting {@link MediaSession media sessions}.
*
- *
It's highly recommended for an app to use this class if they want to keep media playback in
- * the background. The service allows other apps to know that your app supports {@link MediaSession}
- * even when your app isn't running. For example, a user voice command may start your app to play
- * media.
+ *
It's highly recommended for an app to use this class if media playback should continue while
+ * in the background. The service allows other apps to know that your app supports {@link
+ * MediaSession} even when your app isn't running. This way, a user voice command may be able start
+ * your app to play media.
*
*
To extend this class, declare the intent filter in your {@code AndroidManifest.xml}:
*
*
{@code
*
+ * android:foregroundServiceType="mediaPlayback"
+ * android:exported="true">
*
*
*
*
* }
*
- * You may also declare {@code android.media.browse.MediaBrowserService} for compatibility with
- * {@link android.support.v4.media.MediaBrowserCompat}. This service can handle the case
- * automatically.
+ *
You may also declare the action {@code android.media.browse.MediaBrowserService} for
+ * compatibility with {@link android.support.v4.media.MediaBrowserCompat}. This service can handle
+ * the case automatically.
*
*
It's recommended for an app to have a single service declared in the manifest. Otherwise, your
* app might be shown twice in the list of the controller apps, or another app might fail to pick
@@ -93,10 +94,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
*
Service Lifecycle
*
* A media session service is a bound service and its foreground
- * service type must include mediaPlayback. When a {@link MediaController} is created
- * for the service, the controller binds to the service. {@link #onGetSession(ControllerInfo)} will
- * be called from {@link #onBind(Intent)}.
+ * href="https://developer.android.com/guide/topics/manifest/service-element#foregroundservicetype">
+ * foreground service type must include mediaPlayback. When a {@link MediaController}
+ * is created for the service, the controller binds to the service. {@link
+ * #onGetSession(ControllerInfo)} will be called from {@link #onBind(Intent)}.
*
*
After binding, the session's {@link MediaSession.Callback#onConnect(MediaSession,
* MediaSession.ControllerInfo)} will be called to accept or reject the connection request from the
@@ -115,8 +116,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
* #onUpdateNotification(MediaSession)}. In this case, you must also start or stop the service from
* the foreground, when playback starts or stops respectively.
*
- *
The service will be destroyed when all sessions are closed, or no controller is binding to the
- * service while the service is in the background.
+ *
The service will be destroyed when all sessions are {@linkplain MediaController#release()
+ * released}, or no controller is binding to the service while the service is in the background.
*
*
Supporting Multiple Sessions
*
@@ -247,7 +248,8 @@ public abstract class MediaSessionService extends Service {
* Adds a {@link MediaSession} to this service. This is not necessary for most media apps. See Supporting Multiple Sessions for details.
*
- * The added session will be removed automatically when it's closed.
+ *
The added session will be removed automatically {@linkplain MediaSession#release() when the
+ * session is released}.
*
* @param session A session to be added.
* @see #removeSession(MediaSession)
diff --git a/libraries/session/src/main/res/values-af/strings.xml b/libraries/session/src/main/res/values-af/strings.xml
index 8ff1ece685..08c05430ee 100755
--- a/libraries/session/src/main/res/values-af/strings.xml
+++ b/libraries/session/src/main/res/values-af/strings.xml
@@ -1,18 +1,4 @@
-
Speel tans
Speel
diff --git a/libraries/session/src/main/res/values-am/strings.xml b/libraries/session/src/main/res/values-am/strings.xml
index 167aa23d48..503a47a0b9 100755
--- a/libraries/session/src/main/res/values-am/strings.xml
+++ b/libraries/session/src/main/res/values-am/strings.xml
@@ -1,18 +1,4 @@
-
አሁን እየተጫወተ ያለ
አጫውት
diff --git a/libraries/session/src/main/res/values-ar/strings.xml b/libraries/session/src/main/res/values-ar/strings.xml
index 140f15247b..78c978f933 100755
--- a/libraries/session/src/main/res/values-ar/strings.xml
+++ b/libraries/session/src/main/res/values-ar/strings.xml
@@ -1,20 +1,6 @@
-
- التعرف التلقائي على الوسائط
+ قيد التشغيل الآن
تشغيل
إيقاف مؤقت
ترجيع إلى العنصر السابق
diff --git a/libraries/session/src/main/res/values-az/strings.xml b/libraries/session/src/main/res/values-az/strings.xml
index 9b026dc789..3de48f1eee 100755
--- a/libraries/session/src/main/res/values-az/strings.xml
+++ b/libraries/session/src/main/res/values-az/strings.xml
@@ -1,18 +1,4 @@
-
İndi oxudulur
Oxudun
diff --git a/libraries/session/src/main/res/values-b+sr+Latn/strings.xml b/libraries/session/src/main/res/values-b+sr+Latn/strings.xml
index aab80ab5a7..9fc56865f3 100755
--- a/libraries/session/src/main/res/values-b+sr+Latn/strings.xml
+++ b/libraries/session/src/main/res/values-b+sr+Latn/strings.xml
@@ -1,18 +1,4 @@
-
Trenutno svira
Pusti
diff --git a/libraries/session/src/main/res/values-be/strings.xml b/libraries/session/src/main/res/values-be/strings.xml
index b01d57052a..ff390a7a97 100755
--- a/libraries/session/src/main/res/values-be/strings.xml
+++ b/libraries/session/src/main/res/values-be/strings.xml
@@ -1,18 +1,4 @@
-
Зараз іграе
Прайграць
diff --git a/libraries/session/src/main/res/values-bg/strings.xml b/libraries/session/src/main/res/values-bg/strings.xml
index e24ec1e9bc..b720ead2c1 100755
--- a/libraries/session/src/main/res/values-bg/strings.xml
+++ b/libraries/session/src/main/res/values-bg/strings.xml
@@ -1,18 +1,4 @@
-
Възпроизвеждано сега съдържание
Пускане
diff --git a/libraries/session/src/main/res/values-bn/strings.xml b/libraries/session/src/main/res/values-bn/strings.xml
index 2694309f6a..4cb73e9a46 100755
--- a/libraries/session/src/main/res/values-bn/strings.xml
+++ b/libraries/session/src/main/res/values-bn/strings.xml
@@ -1,18 +1,4 @@
-
এখন চলছে
চালান
diff --git a/libraries/session/src/main/res/values-bs/strings.xml b/libraries/session/src/main/res/values-bs/strings.xml
index 2441a1fca8..46f4b9c07d 100755
--- a/libraries/session/src/main/res/values-bs/strings.xml
+++ b/libraries/session/src/main/res/values-bs/strings.xml
@@ -1,18 +1,4 @@
-
Trenutno se reproducira
Reproduciranje
diff --git a/libraries/session/src/main/res/values-ca/strings.xml b/libraries/session/src/main/res/values-ca/strings.xml
index f9bf84a263..368e7fc64b 100755
--- a/libraries/session/src/main/res/values-ca/strings.xml
+++ b/libraries/session/src/main/res/values-ca/strings.xml
@@ -1,18 +1,4 @@
-
S\'està reproduint
Reprodueix
diff --git a/libraries/session/src/main/res/values-cs/strings.xml b/libraries/session/src/main/res/values-cs/strings.xml
index 9c1f3dc49f..9c373bd834 100755
--- a/libraries/session/src/main/res/values-cs/strings.xml
+++ b/libraries/session/src/main/res/values-cs/strings.xml
@@ -1,18 +1,4 @@
-
Přehrává se
Přehrát
diff --git a/libraries/session/src/main/res/values-da/strings.xml b/libraries/session/src/main/res/values-da/strings.xml
index c8236d5ab0..79c8487541 100755
--- a/libraries/session/src/main/res/values-da/strings.xml
+++ b/libraries/session/src/main/res/values-da/strings.xml
@@ -1,18 +1,4 @@
-
Afspilles nu
Afspil
diff --git a/libraries/session/src/main/res/values-de/strings.xml b/libraries/session/src/main/res/values-de/strings.xml
index f6685e5999..24fc22bb1f 100755
--- a/libraries/session/src/main/res/values-de/strings.xml
+++ b/libraries/session/src/main/res/values-de/strings.xml
@@ -1,18 +1,4 @@
-
Läuft gerade
Wiedergabe
diff --git a/libraries/session/src/main/res/values-el/strings.xml b/libraries/session/src/main/res/values-el/strings.xml
index 0d90dca7f9..255b6a74bf 100755
--- a/libraries/session/src/main/res/values-el/strings.xml
+++ b/libraries/session/src/main/res/values-el/strings.xml
@@ -1,18 +1,4 @@
-
Ακούγεται τώρα
Αναπαραγωγή
diff --git a/libraries/session/src/main/res/values-en-rAU/strings.xml b/libraries/session/src/main/res/values-en-rAU/strings.xml
index 0bf10abd2d..aef8b741e6 100755
--- a/libraries/session/src/main/res/values-en-rAU/strings.xml
+++ b/libraries/session/src/main/res/values-en-rAU/strings.xml
@@ -1,18 +1,4 @@
-
Now playing
Play
diff --git a/libraries/session/src/main/res/values-en-rGB/strings.xml b/libraries/session/src/main/res/values-en-rGB/strings.xml
index 0bf10abd2d..aef8b741e6 100755
--- a/libraries/session/src/main/res/values-en-rGB/strings.xml
+++ b/libraries/session/src/main/res/values-en-rGB/strings.xml
@@ -1,18 +1,4 @@
-
Now playing
Play
diff --git a/libraries/session/src/main/res/values-en-rIN/strings.xml b/libraries/session/src/main/res/values-en-rIN/strings.xml
index 0bf10abd2d..aef8b741e6 100755
--- a/libraries/session/src/main/res/values-en-rIN/strings.xml
+++ b/libraries/session/src/main/res/values-en-rIN/strings.xml
@@ -1,18 +1,4 @@
-
Now playing
Play
diff --git a/libraries/session/src/main/res/values-es-rUS/strings.xml b/libraries/session/src/main/res/values-es-rUS/strings.xml
index c3ea5dcb07..8986aeb307 100755
--- a/libraries/session/src/main/res/values-es-rUS/strings.xml
+++ b/libraries/session/src/main/res/values-es-rUS/strings.xml
@@ -1,18 +1,4 @@
-
Está sonando
Reproducir
diff --git a/libraries/session/src/main/res/values-es/strings.xml b/libraries/session/src/main/res/values-es/strings.xml
index 1a8880ecdb..99e401b71f 100755
--- a/libraries/session/src/main/res/values-es/strings.xml
+++ b/libraries/session/src/main/res/values-es/strings.xml
@@ -1,18 +1,4 @@
-
Está sonando
Reproducir
diff --git a/libraries/session/src/main/res/values-et/strings.xml b/libraries/session/src/main/res/values-et/strings.xml
index 4bd8e64a45..a6bd4c2bec 100755
--- a/libraries/session/src/main/res/values-et/strings.xml
+++ b/libraries/session/src/main/res/values-et/strings.xml
@@ -1,18 +1,4 @@
-
Hetkel mängimas
Esitamine
diff --git a/libraries/session/src/main/res/values-eu/strings.xml b/libraries/session/src/main/res/values-eu/strings.xml
index f53cc746a7..c57640f613 100755
--- a/libraries/session/src/main/res/values-eu/strings.xml
+++ b/libraries/session/src/main/res/values-eu/strings.xml
@@ -1,18 +1,4 @@
-
Orain erreproduzitzen
Erreproduzitu
diff --git a/libraries/session/src/main/res/values-fa/strings.xml b/libraries/session/src/main/res/values-fa/strings.xml
index eb8a961f98..0150138991 100755
--- a/libraries/session/src/main/res/values-fa/strings.xml
+++ b/libraries/session/src/main/res/values-fa/strings.xml
@@ -1,18 +1,4 @@
-
درحال پخش
پخش
diff --git a/libraries/session/src/main/res/values-fi/strings.xml b/libraries/session/src/main/res/values-fi/strings.xml
index a4d6637218..2adf214fbd 100755
--- a/libraries/session/src/main/res/values-fi/strings.xml
+++ b/libraries/session/src/main/res/values-fi/strings.xml
@@ -1,18 +1,4 @@
-
Nyt toistetaan
Toista
diff --git a/libraries/session/src/main/res/values-fr-rCA/strings.xml b/libraries/session/src/main/res/values-fr-rCA/strings.xml
index fe00e2480b..bdf99bf638 100755
--- a/libraries/session/src/main/res/values-fr-rCA/strings.xml
+++ b/libraries/session/src/main/res/values-fr-rCA/strings.xml
@@ -1,18 +1,4 @@
-
En cours de lecture
Lire
diff --git a/libraries/session/src/main/res/values-fr/strings.xml b/libraries/session/src/main/res/values-fr/strings.xml
index bda0e76cd5..ffc27ff8a0 100755
--- a/libraries/session/src/main/res/values-fr/strings.xml
+++ b/libraries/session/src/main/res/values-fr/strings.xml
@@ -1,18 +1,4 @@
-
En cours de lecture
Lecture
diff --git a/libraries/session/src/main/res/values-gl/strings.xml b/libraries/session/src/main/res/values-gl/strings.xml
index 97c0b5ae91..96f6019adc 100755
--- a/libraries/session/src/main/res/values-gl/strings.xml
+++ b/libraries/session/src/main/res/values-gl/strings.xml
@@ -1,18 +1,4 @@
-
Reproducindo
Reproducir
diff --git a/libraries/session/src/main/res/values-gu/strings.xml b/libraries/session/src/main/res/values-gu/strings.xml
index 3c2e583039..1fcceea947 100755
--- a/libraries/session/src/main/res/values-gu/strings.xml
+++ b/libraries/session/src/main/res/values-gu/strings.xml
@@ -1,18 +1,4 @@
-
હાલમાં ચાલી રહી છે
ચલાવો
diff --git a/libraries/session/src/main/res/values-hi/strings.xml b/libraries/session/src/main/res/values-hi/strings.xml
index 99a792cdd5..4b6951be58 100755
--- a/libraries/session/src/main/res/values-hi/strings.xml
+++ b/libraries/session/src/main/res/values-hi/strings.xml
@@ -1,18 +1,4 @@
-
अभी चल रहा है
चलाएं
diff --git a/libraries/session/src/main/res/values-hr/strings.xml b/libraries/session/src/main/res/values-hr/strings.xml
index 3fa32acbfb..fe1eae0a7f 100755
--- a/libraries/session/src/main/res/values-hr/strings.xml
+++ b/libraries/session/src/main/res/values-hr/strings.xml
@@ -1,18 +1,4 @@
-
Upravo svira
Reproduciraj
diff --git a/libraries/session/src/main/res/values-hu/strings.xml b/libraries/session/src/main/res/values-hu/strings.xml
index 2ff84c81f9..b1abc67ce9 100755
--- a/libraries/session/src/main/res/values-hu/strings.xml
+++ b/libraries/session/src/main/res/values-hu/strings.xml
@@ -1,18 +1,4 @@
-
Most játszott tartalom
Lejátszás
diff --git a/libraries/session/src/main/res/values-hy/strings.xml b/libraries/session/src/main/res/values-hy/strings.xml
index c5507fa8a1..1385a9cf42 100755
--- a/libraries/session/src/main/res/values-hy/strings.xml
+++ b/libraries/session/src/main/res/values-hy/strings.xml
@@ -1,18 +1,4 @@
-
Այժմ նվագարկվում է
Նվագարկել
diff --git a/libraries/session/src/main/res/values-in/strings.xml b/libraries/session/src/main/res/values-in/strings.xml
index 142339374a..b818baec7c 100755
--- a/libraries/session/src/main/res/values-in/strings.xml
+++ b/libraries/session/src/main/res/values-in/strings.xml
@@ -1,18 +1,4 @@
-
Sedang diputar
Putar
diff --git a/libraries/session/src/main/res/values-is/strings.xml b/libraries/session/src/main/res/values-is/strings.xml
index dfc6267d5a..249da58ac2 100755
--- a/libraries/session/src/main/res/values-is/strings.xml
+++ b/libraries/session/src/main/res/values-is/strings.xml
@@ -1,18 +1,4 @@
-
Í spilun
Spila
diff --git a/libraries/session/src/main/res/values-it/strings.xml b/libraries/session/src/main/res/values-it/strings.xml
index 0be1260e55..0ea7666b8f 100755
--- a/libraries/session/src/main/res/values-it/strings.xml
+++ b/libraries/session/src/main/res/values-it/strings.xml
@@ -1,18 +1,4 @@
-
Ora in riproduzione
Riproduci
diff --git a/libraries/session/src/main/res/values-iw/strings.xml b/libraries/session/src/main/res/values-iw/strings.xml
index d5232cf89b..a7fcd9a24d 100755
--- a/libraries/session/src/main/res/values-iw/strings.xml
+++ b/libraries/session/src/main/res/values-iw/strings.xml
@@ -1,18 +1,4 @@
-
הפריט שמופעל עכשיו
הפעלה
diff --git a/libraries/session/src/main/res/values-ja/strings.xml b/libraries/session/src/main/res/values-ja/strings.xml
index 44957a4eed..2db5cf15a3 100755
--- a/libraries/session/src/main/res/values-ja/strings.xml
+++ b/libraries/session/src/main/res/values-ja/strings.xml
@@ -1,18 +1,4 @@
-
再生中
再生
diff --git a/libraries/session/src/main/res/values-ka/strings.xml b/libraries/session/src/main/res/values-ka/strings.xml
index 44e49caf12..bf4371548e 100755
--- a/libraries/session/src/main/res/values-ka/strings.xml
+++ b/libraries/session/src/main/res/values-ka/strings.xml
@@ -1,18 +1,4 @@
-
ამჟამად უკრავს
დაკვრა
diff --git a/libraries/session/src/main/res/values-kk/strings.xml b/libraries/session/src/main/res/values-kk/strings.xml
index 98f22c7807..e88b1cf39e 100755
--- a/libraries/session/src/main/res/values-kk/strings.xml
+++ b/libraries/session/src/main/res/values-kk/strings.xml
@@ -1,24 +1,10 @@
-
Қазір ойнап тұр
Ойнату
Кідірту
- Алдыңғы мазмұнға өту
- Келесі мазмұнға өту
+ Алдыңғы контентке өту
+ Келесі контентке өту
Артқа айналдыру
Алға айналдыру
Аутентификация қажет
diff --git a/libraries/session/src/main/res/values-km/strings.xml b/libraries/session/src/main/res/values-km/strings.xml
index b12cf6a894..1d59559001 100755
--- a/libraries/session/src/main/res/values-km/strings.xml
+++ b/libraries/session/src/main/res/values-km/strings.xml
@@ -1,18 +1,4 @@
-
កំពុងចាក់
ចាក់
diff --git a/libraries/session/src/main/res/values-kn/strings.xml b/libraries/session/src/main/res/values-kn/strings.xml
index 485b09c0bf..eb709d4c59 100755
--- a/libraries/session/src/main/res/values-kn/strings.xml
+++ b/libraries/session/src/main/res/values-kn/strings.xml
@@ -1,18 +1,4 @@
-
ಇದೀಗ ಪ್ಲೇ ಮಾಡಲಾಗುತ್ತಿದೆ
ಪ್ಲೇ ಮಾಡಿ
diff --git a/libraries/session/src/main/res/values-ko/strings.xml b/libraries/session/src/main/res/values-ko/strings.xml
index 015471fe5e..5d6fae98c3 100755
--- a/libraries/session/src/main/res/values-ko/strings.xml
+++ b/libraries/session/src/main/res/values-ko/strings.xml
@@ -1,18 +1,4 @@
-
지금 재생 중
재생
diff --git a/libraries/session/src/main/res/values-ky/strings.xml b/libraries/session/src/main/res/values-ky/strings.xml
index 350724d9e0..081cccf540 100755
--- a/libraries/session/src/main/res/values-ky/strings.xml
+++ b/libraries/session/src/main/res/values-ky/strings.xml
@@ -1,18 +1,4 @@
-
Ойноп жатат
Ойнотуу
diff --git a/libraries/session/src/main/res/values-lo/strings.xml b/libraries/session/src/main/res/values-lo/strings.xml
index 337bd7be33..6a7310245f 100755
--- a/libraries/session/src/main/res/values-lo/strings.xml
+++ b/libraries/session/src/main/res/values-lo/strings.xml
@@ -1,18 +1,4 @@
-
ກຳລັງຫຼິ້ນຕອນນີ້
ຫຼິ້ນ
diff --git a/libraries/session/src/main/res/values-lt/strings.xml b/libraries/session/src/main/res/values-lt/strings.xml
index a8c02c98af..3a8f7ec3e0 100755
--- a/libraries/session/src/main/res/values-lt/strings.xml
+++ b/libraries/session/src/main/res/values-lt/strings.xml
@@ -1,18 +1,4 @@
-
Dabar leidžiama
Paleisti
diff --git a/libraries/session/src/main/res/values-lv/strings.xml b/libraries/session/src/main/res/values-lv/strings.xml
index 030bb7a549..7283e50af1 100755
--- a/libraries/session/src/main/res/values-lv/strings.xml
+++ b/libraries/session/src/main/res/values-lv/strings.xml
@@ -1,18 +1,4 @@
-
Tagad atskaņo
Atskaņot
diff --git a/libraries/session/src/main/res/values-mk/strings.xml b/libraries/session/src/main/res/values-mk/strings.xml
index 19487a2d57..41b8e9fa04 100755
--- a/libraries/session/src/main/res/values-mk/strings.xml
+++ b/libraries/session/src/main/res/values-mk/strings.xml
@@ -1,18 +1,4 @@
-
Сега е пуштено
Пушти
diff --git a/libraries/session/src/main/res/values-ml/strings.xml b/libraries/session/src/main/res/values-ml/strings.xml
index 0b68af763f..8a15c5b4ca 100755
--- a/libraries/session/src/main/res/values-ml/strings.xml
+++ b/libraries/session/src/main/res/values-ml/strings.xml
@@ -1,18 +1,4 @@
-
ഇപ്പോൾ പ്ലേ ചെയ്യുന്നത്
പ്ലേ ചെയ്യുക
diff --git a/libraries/session/src/main/res/values-mn/strings.xml b/libraries/session/src/main/res/values-mn/strings.xml
index 5b9c2604fc..0efbb93de6 100755
--- a/libraries/session/src/main/res/values-mn/strings.xml
+++ b/libraries/session/src/main/res/values-mn/strings.xml
@@ -1,18 +1,4 @@
-
Одоо тоглуулж байна
Тоглуулах
diff --git a/libraries/session/src/main/res/values-mr/strings.xml b/libraries/session/src/main/res/values-mr/strings.xml
index 35c8acf476..e00fdc1a4e 100755
--- a/libraries/session/src/main/res/values-mr/strings.xml
+++ b/libraries/session/src/main/res/values-mr/strings.xml
@@ -1,18 +1,4 @@
-
आता प्ले करत आहे
प्ले करा
diff --git a/libraries/session/src/main/res/values-ms/strings.xml b/libraries/session/src/main/res/values-ms/strings.xml
index 120211affc..5cf7988088 100755
--- a/libraries/session/src/main/res/values-ms/strings.xml
+++ b/libraries/session/src/main/res/values-ms/strings.xml
@@ -1,18 +1,4 @@
-
Sedang dimainkan
Main
diff --git a/libraries/session/src/main/res/values-my/strings.xml b/libraries/session/src/main/res/values-my/strings.xml
index ea9920abc4..9e6f91f761 100755
--- a/libraries/session/src/main/res/values-my/strings.xml
+++ b/libraries/session/src/main/res/values-my/strings.xml
@@ -1,18 +1,4 @@
-
ယခု ဖွင့်နေသည်
ဖွင့်ရန်
diff --git a/libraries/session/src/main/res/values-nb/strings.xml b/libraries/session/src/main/res/values-nb/strings.xml
index fefb5ea835..4712949ceb 100755
--- a/libraries/session/src/main/res/values-nb/strings.xml
+++ b/libraries/session/src/main/res/values-nb/strings.xml
@@ -1,18 +1,4 @@
-
Spilles nå
Spill av
diff --git a/libraries/session/src/main/res/values-ne/strings.xml b/libraries/session/src/main/res/values-ne/strings.xml
index 8d282366c8..9955dd5503 100755
--- a/libraries/session/src/main/res/values-ne/strings.xml
+++ b/libraries/session/src/main/res/values-ne/strings.xml
@@ -1,18 +1,4 @@
-
अहिले प्ले भइरहेको
प्ले गर्नुहोस्
diff --git a/libraries/session/src/main/res/values-nl/strings.xml b/libraries/session/src/main/res/values-nl/strings.xml
index f3735156e4..b451512da3 100755
--- a/libraries/session/src/main/res/values-nl/strings.xml
+++ b/libraries/session/src/main/res/values-nl/strings.xml
@@ -1,18 +1,4 @@
-
Wordt nu afgespeeld
Afspelen
diff --git a/libraries/session/src/main/res/values-pa/strings.xml b/libraries/session/src/main/res/values-pa/strings.xml
index 86653fca74..ad0de6dc75 100755
--- a/libraries/session/src/main/res/values-pa/strings.xml
+++ b/libraries/session/src/main/res/values-pa/strings.xml
@@ -1,18 +1,4 @@
-
ਹੁਣੇ ਚੱਲ ਰਿਹਾ ਹੈ
ਚਲਾਓ
diff --git a/libraries/session/src/main/res/values-pl/strings.xml b/libraries/session/src/main/res/values-pl/strings.xml
index 10eddbef38..00acc8b75b 100755
--- a/libraries/session/src/main/res/values-pl/strings.xml
+++ b/libraries/session/src/main/res/values-pl/strings.xml
@@ -1,18 +1,4 @@
-
Odtwarzam teraz
Odtwórz
diff --git a/libraries/session/src/main/res/values-pt-rPT/strings.xml b/libraries/session/src/main/res/values-pt-rPT/strings.xml
index 0be33ad385..d341ca707b 100755
--- a/libraries/session/src/main/res/values-pt-rPT/strings.xml
+++ b/libraries/session/src/main/res/values-pt-rPT/strings.xml
@@ -1,18 +1,4 @@
-
A tocar
Reproduzir
diff --git a/libraries/session/src/main/res/values-pt/strings.xml b/libraries/session/src/main/res/values-pt/strings.xml
index 7bd7d79520..a422f4d313 100755
--- a/libraries/session/src/main/res/values-pt/strings.xml
+++ b/libraries/session/src/main/res/values-pt/strings.xml
@@ -1,18 +1,4 @@
-
Tocando agora
Tocar
diff --git a/libraries/session/src/main/res/values-ro/strings.xml b/libraries/session/src/main/res/values-ro/strings.xml
index dd6fc779bd..3de8444966 100755
--- a/libraries/session/src/main/res/values-ro/strings.xml
+++ b/libraries/session/src/main/res/values-ro/strings.xml
@@ -1,18 +1,4 @@
-
Se redă acum
Redă
diff --git a/libraries/session/src/main/res/values-ru/strings.xml b/libraries/session/src/main/res/values-ru/strings.xml
index b643018ac0..ff8bc44ce2 100755
--- a/libraries/session/src/main/res/values-ru/strings.xml
+++ b/libraries/session/src/main/res/values-ru/strings.xml
@@ -1,18 +1,4 @@
-
Играет сейчас
Воспроизвести
diff --git a/libraries/session/src/main/res/values-si/strings.xml b/libraries/session/src/main/res/values-si/strings.xml
index 48c60ddef8..706d6b07ae 100755
--- a/libraries/session/src/main/res/values-si/strings.xml
+++ b/libraries/session/src/main/res/values-si/strings.xml
@@ -1,18 +1,4 @@
-
Now playing
වාදනය කරන්න
diff --git a/libraries/session/src/main/res/values-sk/strings.xml b/libraries/session/src/main/res/values-sk/strings.xml
index f2d31acf05..27342a51c7 100755
--- a/libraries/session/src/main/res/values-sk/strings.xml
+++ b/libraries/session/src/main/res/values-sk/strings.xml
@@ -1,18 +1,4 @@
-
Prehráva sa
Prehrať
diff --git a/libraries/session/src/main/res/values-sl/strings.xml b/libraries/session/src/main/res/values-sl/strings.xml
index ab3fa88897..bab26908b5 100755
--- a/libraries/session/src/main/res/values-sl/strings.xml
+++ b/libraries/session/src/main/res/values-sl/strings.xml
@@ -1,18 +1,4 @@
-
Zdaj se predvaja
Predvajaj
diff --git a/libraries/session/src/main/res/values-sq/strings.xml b/libraries/session/src/main/res/values-sq/strings.xml
index c8ebb5046d..bd2c4b0afd 100755
--- a/libraries/session/src/main/res/values-sq/strings.xml
+++ b/libraries/session/src/main/res/values-sq/strings.xml
@@ -1,18 +1,4 @@
-
Gjej këngën
Luaj
diff --git a/libraries/session/src/main/res/values-sr/strings.xml b/libraries/session/src/main/res/values-sr/strings.xml
index e3f7665ac3..fb87221937 100755
--- a/libraries/session/src/main/res/values-sr/strings.xml
+++ b/libraries/session/src/main/res/values-sr/strings.xml
@@ -1,18 +1,4 @@
-
Тренутно свира
Пусти
diff --git a/libraries/session/src/main/res/values-sv/strings.xml b/libraries/session/src/main/res/values-sv/strings.xml
index ad53a1d6e4..d613f5e2e5 100755
--- a/libraries/session/src/main/res/values-sv/strings.xml
+++ b/libraries/session/src/main/res/values-sv/strings.xml
@@ -1,18 +1,4 @@
-
Nu spelas
Spela upp
diff --git a/libraries/session/src/main/res/values-sw/strings.xml b/libraries/session/src/main/res/values-sw/strings.xml
index 241fb50b78..4678bf1b0c 100755
--- a/libraries/session/src/main/res/values-sw/strings.xml
+++ b/libraries/session/src/main/res/values-sw/strings.xml
@@ -1,18 +1,4 @@
-
Inayocheza sasa
Cheza
diff --git a/libraries/session/src/main/res/values-ta/strings.xml b/libraries/session/src/main/res/values-ta/strings.xml
index e0b65856a2..1e441e4843 100755
--- a/libraries/session/src/main/res/values-ta/strings.xml
+++ b/libraries/session/src/main/res/values-ta/strings.xml
@@ -1,18 +1,4 @@
-
இப்போது பிளே ஆவது
பிளே செய்யும்
diff --git a/libraries/session/src/main/res/values-te/strings.xml b/libraries/session/src/main/res/values-te/strings.xml
index e8a3734789..f51ad9575c 100755
--- a/libraries/session/src/main/res/values-te/strings.xml
+++ b/libraries/session/src/main/res/values-te/strings.xml
@@ -1,18 +1,4 @@
-
ప్రస్తుతం ప్లే అవుతున్నది
ప్లే చేయండి
diff --git a/libraries/session/src/main/res/values-th/strings.xml b/libraries/session/src/main/res/values-th/strings.xml
index 683e2aae68..f3a5ecdb6a 100755
--- a/libraries/session/src/main/res/values-th/strings.xml
+++ b/libraries/session/src/main/res/values-th/strings.xml
@@ -1,18 +1,4 @@
-
กำลังเล่น
เล่น
diff --git a/libraries/session/src/main/res/values-tl/strings.xml b/libraries/session/src/main/res/values-tl/strings.xml
index 995740a809..8345460f87 100755
--- a/libraries/session/src/main/res/values-tl/strings.xml
+++ b/libraries/session/src/main/res/values-tl/strings.xml
@@ -1,18 +1,4 @@
-
Nagpi-play ngayon
I-play
diff --git a/libraries/session/src/main/res/values-tr/strings.xml b/libraries/session/src/main/res/values-tr/strings.xml
index 2795069c69..93fa1a469c 100755
--- a/libraries/session/src/main/res/values-tr/strings.xml
+++ b/libraries/session/src/main/res/values-tr/strings.xml
@@ -1,18 +1,4 @@
-
Şimdi oynatılıyor
Oynat
diff --git a/libraries/session/src/main/res/values-uk/strings.xml b/libraries/session/src/main/res/values-uk/strings.xml
index 4786db3ce7..56f0532c17 100755
--- a/libraries/session/src/main/res/values-uk/strings.xml
+++ b/libraries/session/src/main/res/values-uk/strings.xml
@@ -1,18 +1,4 @@
-
Зараз відтворюється
Відтворити
diff --git a/libraries/session/src/main/res/values-ur/strings.xml b/libraries/session/src/main/res/values-ur/strings.xml
index 423908db8a..877371bfc1 100755
--- a/libraries/session/src/main/res/values-ur/strings.xml
+++ b/libraries/session/src/main/res/values-ur/strings.xml
@@ -1,18 +1,4 @@
-
ابھی چل رہا ہے
چلائیں
diff --git a/libraries/session/src/main/res/values-uz/strings.xml b/libraries/session/src/main/res/values-uz/strings.xml
index fed30dd627..1356dd5493 100755
--- a/libraries/session/src/main/res/values-uz/strings.xml
+++ b/libraries/session/src/main/res/values-uz/strings.xml
@@ -1,18 +1,4 @@
-
Bu qaysi musiqa
Ijro
diff --git a/libraries/session/src/main/res/values-vi/strings.xml b/libraries/session/src/main/res/values-vi/strings.xml
index 9307041370..ebb77b0a24 100755
--- a/libraries/session/src/main/res/values-vi/strings.xml
+++ b/libraries/session/src/main/res/values-vi/strings.xml
@@ -1,18 +1,4 @@
-
Đang phát
Phát
diff --git a/libraries/session/src/main/res/values-zh-rCN/strings.xml b/libraries/session/src/main/res/values-zh-rCN/strings.xml
index 0db7916594..bb5ef13433 100755
--- a/libraries/session/src/main/res/values-zh-rCN/strings.xml
+++ b/libraries/session/src/main/res/values-zh-rCN/strings.xml
@@ -1,18 +1,4 @@
-
正在播放
播放
diff --git a/libraries/session/src/main/res/values-zh-rHK/strings.xml b/libraries/session/src/main/res/values-zh-rHK/strings.xml
index 87716dc283..1c6da3a07f 100755
--- a/libraries/session/src/main/res/values-zh-rHK/strings.xml
+++ b/libraries/session/src/main/res/values-zh-rHK/strings.xml
@@ -1,18 +1,4 @@
-
正在播放
播放
diff --git a/libraries/session/src/main/res/values-zh-rTW/strings.xml b/libraries/session/src/main/res/values-zh-rTW/strings.xml
index e7603e7d90..1c10d0755b 100755
--- a/libraries/session/src/main/res/values-zh-rTW/strings.xml
+++ b/libraries/session/src/main/res/values-zh-rTW/strings.xml
@@ -1,18 +1,4 @@
-
現正播放
播放
diff --git a/libraries/session/src/main/res/values-zu/strings.xml b/libraries/session/src/main/res/values-zu/strings.xml
index 4f5ee5dddd..d50537842a 100755
--- a/libraries/session/src/main/res/values-zu/strings.xml
+++ b/libraries/session/src/main/res/values-zu/strings.xml
@@ -1,18 +1,4 @@
-
Okudlala manje
Dlala
diff --git a/libraries/transformer/src/main/java/androidx/media3/transformer/MssimCalculator.java b/libraries/transformer/src/main/java/androidx/media3/transformer/MssimCalculator.java
index ca7bbe632b..e0dd32ef77 100644
--- a/libraries/transformer/src/main/java/androidx/media3/transformer/MssimCalculator.java
+++ b/libraries/transformer/src/main/java/androidx/media3/transformer/MssimCalculator.java
@@ -23,7 +23,7 @@ import static java.lang.Math.pow;
*
* MSSIM divides the image into windows, calculates SSIM of each, then returns the average.
*
- * @see The SSIM paper.
+ *
See the SSIM paper.
*/
/* package */ final class MssimCalculator {
// Referred to as 'L' in the SSIM paper, this constant defines the maximum pixel values. The
diff --git a/libraries/ui/lint-baseline.xml b/libraries/ui/lint-baseline.xml
index 0b6a29a68c..dafdad02fd 100644
--- a/libraries/ui/lint-baseline.xml
+++ b/libraries/ui/lint-baseline.xml
@@ -22,7 +22,7 @@
errorLine2=" ^">
@@ -33,7 +33,18 @@
errorLine2=" ^">
+
+
+
+
@@ -44,7 +55,7 @@
errorLine2=" ^">
@@ -55,7 +66,18 @@
errorLine2=" ^">
+
+
+
+
diff --git a/libraries/ui/src/main/res/values-af/strings.xml b/libraries/ui/src/main/res/values-af/strings.xml
index dd37229848..775029f1e2 100644
--- a/libraries/ui/src/main/res/values-af/strings.xml
+++ b/libraries/ui/src/main/res/values-af/strings.xml
@@ -1,18 +1,4 @@
-
Wys spelerkontroles
Versteek spelerkontroles
diff --git a/libraries/ui/src/main/res/values-am/strings.xml b/libraries/ui/src/main/res/values-am/strings.xml
index 343e45fbb8..82802a43cd 100644
--- a/libraries/ui/src/main/res/values-am/strings.xml
+++ b/libraries/ui/src/main/res/values-am/strings.xml
@@ -1,18 +1,4 @@
-
የተጫዋች መቆጣጠሪያዎችን አሳይ
የተጫዋች መቆጣጠሪያዎችን ደብቅ
@@ -57,7 +43,7 @@
ቪዲዮ
ኦዲዮ
- ጽሑፍ
+ ጽሁፍ
ምንም
ራስ-ሰር
ያልታወቀ
diff --git a/libraries/ui/src/main/res/values-ar/strings.xml b/libraries/ui/src/main/res/values-ar/strings.xml
index bef7a468e5..804acfc4d6 100644
--- a/libraries/ui/src/main/res/values-ar/strings.xml
+++ b/libraries/ui/src/main/res/values-ar/strings.xml
@@ -1,18 +1,4 @@
-
عرض عناصر التحكم بالمشغّل
إخفاء عناصر التحكم بالمشغّل
diff --git a/libraries/ui/src/main/res/values-az/strings.xml b/libraries/ui/src/main/res/values-az/strings.xml
index 86ea052268..90653578f0 100644
--- a/libraries/ui/src/main/res/values-az/strings.xml
+++ b/libraries/ui/src/main/res/values-az/strings.xml
@@ -1,18 +1,4 @@
-
Oyunçu nəzarətlərini göstərin
Oyunçu nəzarətlərini gizlədin
diff --git a/libraries/ui/src/main/res/values-b+sr+Latn/strings.xml b/libraries/ui/src/main/res/values-b+sr+Latn/strings.xml
index 2acf688bca..ae0185ea3d 100644
--- a/libraries/ui/src/main/res/values-b+sr+Latn/strings.xml
+++ b/libraries/ui/src/main/res/values-b+sr+Latn/strings.xml
@@ -1,18 +1,4 @@
-
Prikaži kontrole plejera
Sakrij kontrole plejera
diff --git a/libraries/ui/src/main/res/values-be/strings.xml b/libraries/ui/src/main/res/values-be/strings.xml
index 05ea71c472..041453ecb2 100644
--- a/libraries/ui/src/main/res/values-be/strings.xml
+++ b/libraries/ui/src/main/res/values-be/strings.xml
@@ -1,18 +1,4 @@
-
Паказаць элементы кіравання прайгравальніка
Схаваць элементы кіравання прайгравальніка
diff --git a/libraries/ui/src/main/res/values-bg/strings.xml b/libraries/ui/src/main/res/values-bg/strings.xml
index 3a898ed4bd..5870895d62 100644
--- a/libraries/ui/src/main/res/values-bg/strings.xml
+++ b/libraries/ui/src/main/res/values-bg/strings.xml
@@ -1,18 +1,4 @@
-
Показване на контролите на плейъра
Скриване на контролите на плейъра
diff --git a/libraries/ui/src/main/res/values-bn/strings.xml b/libraries/ui/src/main/res/values-bn/strings.xml
index 2235db434b..d8e8450755 100644
--- a/libraries/ui/src/main/res/values-bn/strings.xml
+++ b/libraries/ui/src/main/res/values-bn/strings.xml
@@ -1,18 +1,4 @@
-
প্লেয়ার নিয়ন্ত্রণগুলি দেখুন
প্লেয়ার নিয়ন্ত্রণগুলি লুকান
diff --git a/libraries/ui/src/main/res/values-bs/strings.xml b/libraries/ui/src/main/res/values-bs/strings.xml
index 3793c330c1..1981474f16 100644
--- a/libraries/ui/src/main/res/values-bs/strings.xml
+++ b/libraries/ui/src/main/res/values-bs/strings.xml
@@ -1,18 +1,4 @@
-
Prikaži kontrole plejera
Sakrij kontrole plejera
diff --git a/libraries/ui/src/main/res/values-ca/strings.xml b/libraries/ui/src/main/res/values-ca/strings.xml
index ba2e8374a9..56b646a258 100644
--- a/libraries/ui/src/main/res/values-ca/strings.xml
+++ b/libraries/ui/src/main/res/values-ca/strings.xml
@@ -1,18 +1,4 @@
-
Mostra els controls del reproductor
Amaga els controls del reproductor
diff --git a/libraries/ui/src/main/res/values-cs/strings.xml b/libraries/ui/src/main/res/values-cs/strings.xml
index bb0f85369a..48943a497d 100644
--- a/libraries/ui/src/main/res/values-cs/strings.xml
+++ b/libraries/ui/src/main/res/values-cs/strings.xml
@@ -1,18 +1,4 @@
-
Zobrazit ovládací prvky přehrávače
Skrýt ovládací prvky přehrávače
diff --git a/libraries/ui/src/main/res/values-da/strings.xml b/libraries/ui/src/main/res/values-da/strings.xml
index 71ff36a1a3..d7486b4b9a 100644
--- a/libraries/ui/src/main/res/values-da/strings.xml
+++ b/libraries/ui/src/main/res/values-da/strings.xml
@@ -1,18 +1,4 @@
-
Vis afspilningsknapper
Skjul afspilningsknapper
diff --git a/libraries/ui/src/main/res/values-de/strings.xml b/libraries/ui/src/main/res/values-de/strings.xml
index ff160f6a6a..f035521b13 100644
--- a/libraries/ui/src/main/res/values-de/strings.xml
+++ b/libraries/ui/src/main/res/values-de/strings.xml
@@ -1,18 +1,4 @@
-
Player-Steuerelemente anzeigen
Player-Steuerelemente ausblenden
diff --git a/libraries/ui/src/main/res/values-el/strings.xml b/libraries/ui/src/main/res/values-el/strings.xml
index c4ed289bef..a715576c61 100644
--- a/libraries/ui/src/main/res/values-el/strings.xml
+++ b/libraries/ui/src/main/res/values-el/strings.xml
@@ -1,18 +1,4 @@
-
Εμφάν. στοιχείων ελέγχου προγράμματος αναπαραγωγής
Απόκρ. στοιχείων ελέγχου προγράμματος αναπαραγωγής
diff --git a/libraries/ui/src/main/res/values-en-rAU/strings.xml b/libraries/ui/src/main/res/values-en-rAU/strings.xml
index af85902797..f312573b97 100644
--- a/libraries/ui/src/main/res/values-en-rAU/strings.xml
+++ b/libraries/ui/src/main/res/values-en-rAU/strings.xml
@@ -1,18 +1,4 @@
-
Show player controls
Hide player controls
diff --git a/libraries/ui/src/main/res/values-en-rGB/strings.xml b/libraries/ui/src/main/res/values-en-rGB/strings.xml
index af85902797..f312573b97 100644
--- a/libraries/ui/src/main/res/values-en-rGB/strings.xml
+++ b/libraries/ui/src/main/res/values-en-rGB/strings.xml
@@ -1,18 +1,4 @@
-
Show player controls
Hide player controls
diff --git a/libraries/ui/src/main/res/values-en-rIN/strings.xml b/libraries/ui/src/main/res/values-en-rIN/strings.xml
index af85902797..f312573b97 100644
--- a/libraries/ui/src/main/res/values-en-rIN/strings.xml
+++ b/libraries/ui/src/main/res/values-en-rIN/strings.xml
@@ -1,18 +1,4 @@
-
Show player controls
Hide player controls
diff --git a/libraries/ui/src/main/res/values-es-rUS/strings.xml b/libraries/ui/src/main/res/values-es-rUS/strings.xml
index 0bc0f6c546..30e3056b70 100644
--- a/libraries/ui/src/main/res/values-es-rUS/strings.xml
+++ b/libraries/ui/src/main/res/values-es-rUS/strings.xml
@@ -1,18 +1,4 @@
-
Mostrar controles del reproductor
Ocultar controles del reproductor
diff --git a/libraries/ui/src/main/res/values-es/strings.xml b/libraries/ui/src/main/res/values-es/strings.xml
index 924d349038..076f352c12 100644
--- a/libraries/ui/src/main/res/values-es/strings.xml
+++ b/libraries/ui/src/main/res/values-es/strings.xml
@@ -1,18 +1,4 @@
-
Mostrar controles del reproductor
Ocultar controles de jugador
@@ -47,13 +33,13 @@
Habilitar subtítulos
Velocidad
- - ×0,25
- - ×0,5
- - ×0,75
+ - 0,25x
+ - 0,5x
+ - 0,75x
- Normal
- - ×1,25
- - ×1,5
- - ×2
+ - 1,25x
+ - 1,5x
+ - 2x
Vídeo
Audio
diff --git a/libraries/ui/src/main/res/values-et/strings.xml b/libraries/ui/src/main/res/values-et/strings.xml
index faa44e483f..22dd590ee0 100644
--- a/libraries/ui/src/main/res/values-et/strings.xml
+++ b/libraries/ui/src/main/res/values-et/strings.xml
@@ -1,18 +1,4 @@
-
Kuva pleieri juhtnupud
Peida pleieri juhtnupud
diff --git a/libraries/ui/src/main/res/values-eu/strings.xml b/libraries/ui/src/main/res/values-eu/strings.xml
index 90441518b1..08fc4df563 100644
--- a/libraries/ui/src/main/res/values-eu/strings.xml
+++ b/libraries/ui/src/main/res/values-eu/strings.xml
@@ -1,18 +1,4 @@
-
Erakutsi erreproduzigailua kontrolatzeko aukerak
Ezkutatu erreproduzigailua kontrolatzeko aukerak
diff --git a/libraries/ui/src/main/res/values-fa/strings.xml b/libraries/ui/src/main/res/values-fa/strings.xml
index c25aa14a7c..a7963d437f 100644
--- a/libraries/ui/src/main/res/values-fa/strings.xml
+++ b/libraries/ui/src/main/res/values-fa/strings.xml
@@ -1,18 +1,4 @@
-
نمایش کنترلهای پخشکننده
پنهان کردن کنترلهای پخشکننده
diff --git a/libraries/ui/src/main/res/values-fi/strings.xml b/libraries/ui/src/main/res/values-fi/strings.xml
index f57744643c..2ab098e46f 100644
--- a/libraries/ui/src/main/res/values-fi/strings.xml
+++ b/libraries/ui/src/main/res/values-fi/strings.xml
@@ -1,18 +1,4 @@
-
Näytä soittimen säätimet
Näytä soittimen säätimet
diff --git a/libraries/ui/src/main/res/values-fr-rCA/strings.xml b/libraries/ui/src/main/res/values-fr-rCA/strings.xml
index 5faeb0b5b2..8b5a19053e 100644
--- a/libraries/ui/src/main/res/values-fr-rCA/strings.xml
+++ b/libraries/ui/src/main/res/values-fr-rCA/strings.xml
@@ -1,18 +1,4 @@
-
Afficher les commandes du lecteur
Masquer les commandes du lecteur
diff --git a/libraries/ui/src/main/res/values-fr/strings.xml b/libraries/ui/src/main/res/values-fr/strings.xml
index cc876a8bf3..cc2386dcdd 100644
--- a/libraries/ui/src/main/res/values-fr/strings.xml
+++ b/libraries/ui/src/main/res/values-fr/strings.xml
@@ -1,18 +1,4 @@
-
Afficher les commandes du lecteur
Masquer les commandes du lecteur
diff --git a/libraries/ui/src/main/res/values-gl/strings.xml b/libraries/ui/src/main/res/values-gl/strings.xml
index 932434af28..125daae7f8 100644
--- a/libraries/ui/src/main/res/values-gl/strings.xml
+++ b/libraries/ui/src/main/res/values-gl/strings.xml
@@ -1,18 +1,4 @@
-
Mostrar controis do reprodutor
Ocultar controis do reprodutor
@@ -71,6 +57,6 @@
Complementaria
Comentarios
Subtítulos
- %1$.2f Mbps
+ %1$.2f Mb/s
%1$s, %2$s
diff --git a/libraries/ui/src/main/res/values-gu/strings.xml b/libraries/ui/src/main/res/values-gu/strings.xml
index 955ccf1f93..d912bae291 100644
--- a/libraries/ui/src/main/res/values-gu/strings.xml
+++ b/libraries/ui/src/main/res/values-gu/strings.xml
@@ -1,18 +1,4 @@
-
પ્લેયર માટેના નિયંત્રણો બતાવો
પ્લેયર માટેના નિયંત્રણો છુપાવો
diff --git a/libraries/ui/src/main/res/values-hi/strings.xml b/libraries/ui/src/main/res/values-hi/strings.xml
index fe18f2a16a..37113b18d4 100644
--- a/libraries/ui/src/main/res/values-hi/strings.xml
+++ b/libraries/ui/src/main/res/values-hi/strings.xml
@@ -1,18 +1,4 @@
-
प्लेयर नियंत्रण दिखाएं
प्लेयर नियंत्रण छिपाएं
diff --git a/libraries/ui/src/main/res/values-hr/strings.xml b/libraries/ui/src/main/res/values-hr/strings.xml
index 320e47aef8..f58e02d530 100644
--- a/libraries/ui/src/main/res/values-hr/strings.xml
+++ b/libraries/ui/src/main/res/values-hr/strings.xml
@@ -1,18 +1,4 @@
-
Prikaži kontrole playera
Sakrij kontrole playera
diff --git a/libraries/ui/src/main/res/values-hu/strings.xml b/libraries/ui/src/main/res/values-hu/strings.xml
index 7f24082f54..66801d3709 100644
--- a/libraries/ui/src/main/res/values-hu/strings.xml
+++ b/libraries/ui/src/main/res/values-hu/strings.xml
@@ -1,18 +1,4 @@
-
Lejátszásvezérlők mutatása
Lejátszásvezérlők elrejtése
diff --git a/libraries/ui/src/main/res/values-hy/strings.xml b/libraries/ui/src/main/res/values-hy/strings.xml
index f77b183118..5171913fbe 100644
--- a/libraries/ui/src/main/res/values-hy/strings.xml
+++ b/libraries/ui/src/main/res/values-hy/strings.xml
@@ -1,18 +1,4 @@
-
Ցուցադրել նվագարկչի կառավարները
Թաքցնել նվագարկչի կառավարները
diff --git a/libraries/ui/src/main/res/values-in/strings.xml b/libraries/ui/src/main/res/values-in/strings.xml
index b010d3c739..9afe0208ca 100644
--- a/libraries/ui/src/main/res/values-in/strings.xml
+++ b/libraries/ui/src/main/res/values-in/strings.xml
@@ -1,18 +1,4 @@
-
Menampilkan kontrol pemutar
Menyembunyikan kontrol pemutar
diff --git a/libraries/ui/src/main/res/values-is/strings.xml b/libraries/ui/src/main/res/values-is/strings.xml
index 20f603c476..fe4d96f02c 100644
--- a/libraries/ui/src/main/res/values-is/strings.xml
+++ b/libraries/ui/src/main/res/values-is/strings.xml
@@ -1,18 +1,4 @@
-
Sýna spilunarstýringar
Fela spilunarstýringar
diff --git a/libraries/ui/src/main/res/values-it/strings.xml b/libraries/ui/src/main/res/values-it/strings.xml
index b97ae06645..c6f6c12d00 100644
--- a/libraries/ui/src/main/res/values-it/strings.xml
+++ b/libraries/ui/src/main/res/values-it/strings.xml
@@ -1,18 +1,4 @@
-
Mostra i controlli del player
Nascondi i controlli del player
diff --git a/libraries/ui/src/main/res/values-iw/strings.xml b/libraries/ui/src/main/res/values-iw/strings.xml
index c5499212c4..0b61a2c434 100644
--- a/libraries/ui/src/main/res/values-iw/strings.xml
+++ b/libraries/ui/src/main/res/values-iw/strings.xml
@@ -1,32 +1,4 @@
-
-
הצגת פקדי הנגן
הסתרת פקדי הנגן
@@ -43,16 +15,14 @@
הפסקה
הרצה אחורה
- - הרצה שנייה אחת (%d) אחורה
+ - הרצה %d שניות אחורה
- הרצה %d שניות אחורה
- - הרצה %d שניות אחורה
- הרצה %d שניות אחורה
הרצה קדימה
- - הרצה שנייה אחת (%d) קדימה
+ - הרצה %d שניות קדימה
- הרצה %d שניות קדימה
- - הרצה %d שניות קדימה
- הרצה %d שניות קדימה
המצב הנוכחי: ללא חזרה. לחצן להחלפת מצב החזרה.
diff --git a/libraries/ui/src/main/res/values-ja/strings.xml b/libraries/ui/src/main/res/values-ja/strings.xml
index f8970f64ee..c5017b3c72 100644
--- a/libraries/ui/src/main/res/values-ja/strings.xml
+++ b/libraries/ui/src/main/res/values-ja/strings.xml
@@ -1,18 +1,4 @@
-
プレーヤーのコントロールを表示する
プレーヤーのコントロールを非表示にする
diff --git a/libraries/ui/src/main/res/values-ka/strings.xml b/libraries/ui/src/main/res/values-ka/strings.xml
index 2ff75b72dd..65c048bff7 100644
--- a/libraries/ui/src/main/res/values-ka/strings.xml
+++ b/libraries/ui/src/main/res/values-ka/strings.xml
@@ -1,18 +1,4 @@
-
დამკვრელის სამართავი ღილაკების ჩვენება
დამკვრელის სამართავი ღილაკების დამალვა
diff --git a/libraries/ui/src/main/res/values-kk/strings.xml b/libraries/ui/src/main/res/values-kk/strings.xml
index 86d41df02b..3296c2b557 100644
--- a/libraries/ui/src/main/res/values-kk/strings.xml
+++ b/libraries/ui/src/main/res/values-kk/strings.xml
@@ -1,18 +1,4 @@
-
Ойнатқышты басқару элементтерін көрсету
Ойнатқышты басқару элементтерін жасыру
@@ -37,9 +23,9 @@
- %d секунд алға айналдыру
- %d секунд алға айналдыру
- Қазіргі режим: еш мазмұн қайталанбайды. Қайталау режимін ауыстыру.
- Қазіргі режим: бір мазмұн қайталанады. Қайталау режимін ауыстыру.
- Қазіргі режим: барлық мазмұн қайталанады Қайталау режимін ауыстыру.
+ Қазіргі режим: еш контент қайталанбайды. Қайталау режимін ауыстыру.
+ Қазіргі режим: бір контент қайталанады. Қайталау режимін ауыстыру.
+ Қазіргі режим: барлық контент қайталанады Қайталау режимін ауыстыру.
Араластыру режимін өшіру
Араластыру режимін қосу
VR режимі
diff --git a/libraries/ui/src/main/res/values-km/strings.xml b/libraries/ui/src/main/res/values-km/strings.xml
index 3e8fd004a1..c8da4e15a9 100644
--- a/libraries/ui/src/main/res/values-km/strings.xml
+++ b/libraries/ui/src/main/res/values-km/strings.xml
@@ -1,18 +1,4 @@
-
បង្ហាញការគ្រប់គ្រងកម្មវិធីចាក់ចម្រៀង
លាក់ការគ្រប់គ្រងកម្មវិធីចាក់ចម្រៀង
diff --git a/libraries/ui/src/main/res/values-kn/strings.xml b/libraries/ui/src/main/res/values-kn/strings.xml
index 45b2417f5b..5dcf85112d 100644
--- a/libraries/ui/src/main/res/values-kn/strings.xml
+++ b/libraries/ui/src/main/res/values-kn/strings.xml
@@ -1,18 +1,4 @@
-
ಪ್ಲೇಯರ್ ನಿಯಂತ್ರಣಗಳನ್ನು ತೋರಿಸಿ
ಪ್ಲೇಯರ್ ನಿಯಂತ್ರಣಗಳನ್ನು ಮರೆಮಾಡಿ
diff --git a/libraries/ui/src/main/res/values-ko/strings.xml b/libraries/ui/src/main/res/values-ko/strings.xml
index b6e8d231ea..d0a0f942c5 100644
--- a/libraries/ui/src/main/res/values-ko/strings.xml
+++ b/libraries/ui/src/main/res/values-ko/strings.xml
@@ -1,18 +1,4 @@
-