mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Merge branch 'main' of github.com:ittiam-systems/media into rtp_wav
This commit is contained in:
commit
bfc1fb9aa7
@ -1,5 +1,61 @@
|
|||||||
# Release notes
|
# Release notes
|
||||||
|
|
||||||
|
### Unreleased changes
|
||||||
|
|
||||||
|
* Core library:
|
||||||
|
* Enable support for Android platform diagnostics via
|
||||||
|
`MediaMetricsManager`. ExoPlayer will forward playback events and
|
||||||
|
performance data to the platform, which helps to provide system
|
||||||
|
performance and debugging information on the device. This data may also
|
||||||
|
be collected by Google
|
||||||
|
[if sharing usage and diagnostics data is enabled](https://support.google.com/accounts/answer/6078260)
|
||||||
|
by the user of the device. Apps can opt-out of contributing to platform
|
||||||
|
diagnostics for ExoPlayer with
|
||||||
|
`ExoPlayer.Builder.setUsePlatformDiagnostics(false)`.
|
||||||
|
* Track selection:
|
||||||
|
* Flatten `TrackSelectionOverrides` class into `TrackSelectionParameters`,
|
||||||
|
and promote `TrackSelectionOverride` to a top level class.
|
||||||
|
* Audio:
|
||||||
|
* Use LG AC3 audio decoder advertising non-standard MIME type.
|
||||||
|
* Extractors:
|
||||||
|
* Matroska: Parse `DiscardPadding` for Opus tracks.
|
||||||
|
* UI:
|
||||||
|
* Fix delivery of events to `OnClickListener`s set on `PlayerView` and
|
||||||
|
`LegacyPlayerView`, in the case that `useController=false`
|
||||||
|
([#9605](https://github.com/google/ExoPlayer/issues/9605)). Also fix
|
||||||
|
delivery of events to `OnLongClickListener` for all view configurations.
|
||||||
|
* Fix incorrectly treating a sequence of touch events that exit the bounds
|
||||||
|
of `PlayerView` and `LegacyPlayerView` before `ACTION_UP` as a click
|
||||||
|
([#9861](https://github.com/google/ExoPlayer/issues/9861)).
|
||||||
|
* Fix `PlayerView` accessibility issue where it was not possible to
|
||||||
|
tapping would toggle playback rather than hiding the controls
|
||||||
|
([#8627](https://github.com/google/ExoPlayer/issues/8627)).
|
||||||
|
* Rewrite `TrackSelectionView` and `TrackSelectionDialogBuilder` to work
|
||||||
|
with the `Player` interface rather than `ExoPlayer`. This allows the
|
||||||
|
views to be used with other `Player` implementations, and removes the
|
||||||
|
dependency from the UI module to the ExoPlayer module. This is a
|
||||||
|
breaking change.
|
||||||
|
* RTSP:
|
||||||
|
* Add RTP reader for HEVC
|
||||||
|
([#36](https://github.com/androidx/media/pull/36)).
|
||||||
|
* Remove deprecated symbols:
|
||||||
|
* Remove `Player.Listener.onTracksChanged`. Use
|
||||||
|
`Player.Listener.onTracksInfoChanged` instead.
|
||||||
|
* Remove `Player.getCurrentTrackGroups` and
|
||||||
|
`Player.getCurrentTrackSelections`. Use `Player.getCurrentTracksInfo`
|
||||||
|
instead. You can also continue to use `ExoPlayer.getCurrentTrackGroups`
|
||||||
|
and `ExoPlayer.getCurrentTrackSelections`, although these methods remain
|
||||||
|
deprecated.
|
||||||
|
* Remove `DownloadHelper`
|
||||||
|
`DEFAULT_TRACK_SELECTOR_PARAMETERS_WITHOUT_VIEWPORT` and
|
||||||
|
`DEFAULT_TRACK_SELECTOR_PARAMETERS` constants. Use
|
||||||
|
`getDefaultTrackSelectorParameters(Context)` instead when possible, and
|
||||||
|
`DEFAULT_TRACK_SELECTOR_PARAMETERS_WITHOUT_CONTEXT` otherwise.
|
||||||
|
* FFmpeg extension:
|
||||||
|
* Update CMake version to `3.21.0+` to avoid a CMake bug causing
|
||||||
|
AndroidStudio's gradle sync to fail
|
||||||
|
([#9933](https://github.com/google/ExoPlayer/issues/9933)).
|
||||||
|
|
||||||
### 1.0.0-alpha03 (2022-03-14)
|
### 1.0.0-alpha03 (2022-03-14)
|
||||||
|
|
||||||
This release corresponds to the
|
This release corresponds to the
|
||||||
|
@ -26,7 +26,7 @@ project.ext {
|
|||||||
// https://cs.android.com/android/platform/superproject/+/master:external/guava/METADATA
|
// https://cs.android.com/android/platform/superproject/+/master:external/guava/METADATA
|
||||||
guavaVersion = '31.0.1-android'
|
guavaVersion = '31.0.1-android'
|
||||||
mockitoVersion = '3.12.4'
|
mockitoVersion = '3.12.4'
|
||||||
robolectricVersion = '4.6.1'
|
robolectricVersion = '4.8-alpha-1'
|
||||||
// Keep this in sync with Google's internal Checker Framework version.
|
// Keep this in sync with Google's internal Checker Framework version.
|
||||||
checkerframeworkVersion = '3.13.0'
|
checkerframeworkVersion = '3.13.0'
|
||||||
checkerframeworkCompatVersion = '2.5.5'
|
checkerframeworkCompatVersion = '2.5.5'
|
||||||
|
@ -119,8 +119,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||||||
|
|
||||||
// Run the shader program.
|
// Run the shader program.
|
||||||
GlProgram program = checkNotNull(this.program);
|
GlProgram program = checkNotNull(this.program);
|
||||||
program.setSamplerTexIdUniform("uTexSampler0", frameTexture, /* unit= */ 0);
|
program.setSamplerTexIdUniform("uTexSampler0", frameTexture, /* texUnitIndex= */ 0);
|
||||||
program.setSamplerTexIdUniform("uTexSampler1", textures[0], /* unit= */ 1);
|
program.setSamplerTexIdUniform("uTexSampler1", textures[0], /* texUnitIndex= */ 1);
|
||||||
program.setFloatUniform("uScaleX", bitmapScaleX);
|
program.setFloatUniform("uScaleX", bitmapScaleX);
|
||||||
program.setFloatUniform("uScaleY", bitmapScaleY);
|
program.setFloatUniform("uScaleY", bitmapScaleY);
|
||||||
program.setFloatsUniform("uTexTransform", transformMatrix);
|
program.setFloatsUniform("uTexTransform", transformMatrix);
|
||||||
|
@ -32,7 +32,6 @@ import androidx.media3.common.util.Util;
|
|||||||
import androidx.media3.datasource.DataSource;
|
import androidx.media3.datasource.DataSource;
|
||||||
import androidx.media3.datasource.DefaultDataSource;
|
import androidx.media3.datasource.DefaultDataSource;
|
||||||
import androidx.media3.datasource.DefaultHttpDataSource;
|
import androidx.media3.datasource.DefaultHttpDataSource;
|
||||||
import androidx.media3.datasource.HttpDataSource;
|
|
||||||
import androidx.media3.exoplayer.ExoPlayer;
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
import androidx.media3.exoplayer.dash.DashMediaSource;
|
import androidx.media3.exoplayer.dash.DashMediaSource;
|
||||||
import androidx.media3.exoplayer.drm.DefaultDrmSessionManager;
|
import androidx.media3.exoplayer.drm.DefaultDrmSessionManager;
|
||||||
@ -144,7 +143,7 @@ public final class MainActivity extends Activity {
|
|||||||
String drmScheme = Assertions.checkNotNull(intent.getStringExtra(DRM_SCHEME_EXTRA));
|
String drmScheme = Assertions.checkNotNull(intent.getStringExtra(DRM_SCHEME_EXTRA));
|
||||||
String drmLicenseUrl = Assertions.checkNotNull(intent.getStringExtra(DRM_LICENSE_URL_EXTRA));
|
String drmLicenseUrl = Assertions.checkNotNull(intent.getStringExtra(DRM_LICENSE_URL_EXTRA));
|
||||||
UUID drmSchemeUuid = Assertions.checkNotNull(Util.getDrmUuid(drmScheme));
|
UUID drmSchemeUuid = Assertions.checkNotNull(Util.getDrmUuid(drmScheme));
|
||||||
HttpDataSource.Factory licenseDataSourceFactory = new DefaultHttpDataSource.Factory();
|
DataSource.Factory licenseDataSourceFactory = new DefaultHttpDataSource.Factory();
|
||||||
HttpMediaDrmCallback drmCallback =
|
HttpMediaDrmCallback drmCallback =
|
||||||
new HttpMediaDrmCallback(drmLicenseUrl, licenseDataSourceFactory);
|
new HttpMediaDrmCallback(drmLicenseUrl, licenseDataSourceFactory);
|
||||||
drmSessionManager =
|
drmSessionManager =
|
||||||
|
@ -21,7 +21,6 @@ import androidx.media3.database.StandaloneDatabaseProvider;
|
|||||||
import androidx.media3.datasource.DataSource;
|
import androidx.media3.datasource.DataSource;
|
||||||
import androidx.media3.datasource.DefaultDataSource;
|
import androidx.media3.datasource.DefaultDataSource;
|
||||||
import androidx.media3.datasource.DefaultHttpDataSource;
|
import androidx.media3.datasource.DefaultHttpDataSource;
|
||||||
import androidx.media3.datasource.HttpDataSource;
|
|
||||||
import androidx.media3.datasource.cache.Cache;
|
import androidx.media3.datasource.cache.Cache;
|
||||||
import androidx.media3.datasource.cache.CacheDataSource;
|
import androidx.media3.datasource.cache.CacheDataSource;
|
||||||
import androidx.media3.datasource.cache.NoOpCacheEvictor;
|
import androidx.media3.datasource.cache.NoOpCacheEvictor;
|
||||||
@ -59,7 +58,7 @@ public final class DemoUtil {
|
|||||||
private static final String DOWNLOAD_CONTENT_DIRECTORY = "downloads";
|
private static final String DOWNLOAD_CONTENT_DIRECTORY = "downloads";
|
||||||
|
|
||||||
private static DataSource.@MonotonicNonNull Factory dataSourceFactory;
|
private static DataSource.@MonotonicNonNull Factory dataSourceFactory;
|
||||||
private static HttpDataSource.@MonotonicNonNull Factory httpDataSourceFactory;
|
private static DataSource.@MonotonicNonNull Factory httpDataSourceFactory;
|
||||||
private static @MonotonicNonNull DatabaseProvider databaseProvider;
|
private static @MonotonicNonNull DatabaseProvider databaseProvider;
|
||||||
private static @MonotonicNonNull File downloadDirectory;
|
private static @MonotonicNonNull File downloadDirectory;
|
||||||
private static @MonotonicNonNull Cache downloadCache;
|
private static @MonotonicNonNull Cache downloadCache;
|
||||||
@ -85,7 +84,7 @@ public final class DemoUtil {
|
|||||||
.setExtensionRendererMode(extensionRendererMode);
|
.setExtensionRendererMode(extensionRendererMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static synchronized HttpDataSource.Factory getHttpDataSourceFactory(Context context) {
|
public static synchronized DataSource.Factory getHttpDataSourceFactory(Context context) {
|
||||||
if (httpDataSourceFactory == null) {
|
if (httpDataSourceFactory == null) {
|
||||||
if (USE_CRONET_FOR_NETWORKING) {
|
if (USE_CRONET_FOR_NETWORKING) {
|
||||||
context = context.getApplicationContext();
|
context = context.getApplicationContext();
|
||||||
|
@ -15,8 +15,7 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.demo.main;
|
package androidx.media3.demo.main;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
@ -30,12 +29,11 @@ import androidx.media3.common.DrmInitData;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TrackSelectionParameters;
|
import androidx.media3.common.TrackSelectionParameters;
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.datasource.HttpDataSource;
|
import androidx.media3.datasource.DataSource;
|
||||||
import androidx.media3.exoplayer.RenderersFactory;
|
import androidx.media3.exoplayer.RenderersFactory;
|
||||||
import androidx.media3.exoplayer.drm.DrmSession;
|
import androidx.media3.exoplayer.drm.DrmSession;
|
||||||
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
|
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
|
||||||
@ -48,6 +46,7 @@ import androidx.media3.exoplayer.offline.DownloadIndex;
|
|||||||
import androidx.media3.exoplayer.offline.DownloadManager;
|
import androidx.media3.exoplayer.offline.DownloadManager;
|
||||||
import androidx.media3.exoplayer.offline.DownloadRequest;
|
import androidx.media3.exoplayer.offline.DownloadRequest;
|
||||||
import androidx.media3.exoplayer.offline.DownloadService;
|
import androidx.media3.exoplayer.offline.DownloadService;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.MappingTrackSelector.MappedTrackInfo;
|
import androidx.media3.exoplayer.trackselection.MappingTrackSelector.MappedTrackInfo;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -66,7 +65,7 @@ public class DownloadTracker {
|
|||||||
private static final String TAG = "DownloadTracker";
|
private static final String TAG = "DownloadTracker";
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final HttpDataSource.Factory httpDataSourceFactory;
|
private final DataSource.Factory dataSourceFactory;
|
||||||
private final CopyOnWriteArraySet<Listener> listeners;
|
private final CopyOnWriteArraySet<Listener> listeners;
|
||||||
private final HashMap<Uri, Download> downloads;
|
private final HashMap<Uri, Download> downloads;
|
||||||
private final DownloadIndex downloadIndex;
|
private final DownloadIndex downloadIndex;
|
||||||
@ -74,11 +73,9 @@ public class DownloadTracker {
|
|||||||
@Nullable private StartDownloadDialogHelper startDownloadDialogHelper;
|
@Nullable private StartDownloadDialogHelper startDownloadDialogHelper;
|
||||||
|
|
||||||
public DownloadTracker(
|
public DownloadTracker(
|
||||||
Context context,
|
Context context, DataSource.Factory dataSourceFactory, DownloadManager downloadManager) {
|
||||||
HttpDataSource.Factory httpDataSourceFactory,
|
|
||||||
DownloadManager downloadManager) {
|
|
||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
this.httpDataSourceFactory = httpDataSourceFactory;
|
this.dataSourceFactory = dataSourceFactory;
|
||||||
listeners = new CopyOnWriteArraySet<>();
|
listeners = new CopyOnWriteArraySet<>();
|
||||||
downloads = new HashMap<>();
|
downloads = new HashMap<>();
|
||||||
downloadIndex = downloadManager.getDownloadIndex();
|
downloadIndex = downloadManager.getDownloadIndex();
|
||||||
@ -87,8 +84,7 @@ public class DownloadTracker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addListener(Listener listener) {
|
public void addListener(Listener listener) {
|
||||||
checkNotNull(listener);
|
listeners.add(checkNotNull(listener));
|
||||||
listeners.add(listener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeListener(Listener listener) {
|
public void removeListener(Listener listener) {
|
||||||
@ -119,8 +115,7 @@ public class DownloadTracker {
|
|||||||
startDownloadDialogHelper =
|
startDownloadDialogHelper =
|
||||||
new StartDownloadDialogHelper(
|
new StartDownloadDialogHelper(
|
||||||
fragmentManager,
|
fragmentManager,
|
||||||
DownloadHelper.forMediaItem(
|
DownloadHelper.forMediaItem(context, mediaItem, renderersFactory, dataSourceFactory),
|
||||||
context, mediaItem, renderersFactory, httpDataSourceFactory),
|
|
||||||
mediaItem);
|
mediaItem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -218,7 +213,7 @@ public class DownloadTracker {
|
|||||||
new WidevineOfflineLicenseFetchTask(
|
new WidevineOfflineLicenseFetchTask(
|
||||||
format,
|
format,
|
||||||
mediaItem.localConfiguration.drmConfiguration,
|
mediaItem.localConfiguration.drmConfiguration,
|
||||||
httpDataSourceFactory,
|
dataSourceFactory,
|
||||||
/* dialogHelper= */ this,
|
/* dialogHelper= */ this,
|
||||||
helper);
|
helper);
|
||||||
widevineOfflineLicenseFetchTask.execute();
|
widevineOfflineLicenseFetchTask.execute();
|
||||||
@ -361,7 +356,7 @@ public class DownloadTracker {
|
|||||||
|
|
||||||
private final Format format;
|
private final Format format;
|
||||||
private final MediaItem.DrmConfiguration drmConfiguration;
|
private final MediaItem.DrmConfiguration drmConfiguration;
|
||||||
private final HttpDataSource.Factory httpDataSourceFactory;
|
private final DataSource.Factory dataSourceFactory;
|
||||||
private final StartDownloadDialogHelper dialogHelper;
|
private final StartDownloadDialogHelper dialogHelper;
|
||||||
private final DownloadHelper downloadHelper;
|
private final DownloadHelper downloadHelper;
|
||||||
|
|
||||||
@ -371,12 +366,12 @@ public class DownloadTracker {
|
|||||||
public WidevineOfflineLicenseFetchTask(
|
public WidevineOfflineLicenseFetchTask(
|
||||||
Format format,
|
Format format,
|
||||||
MediaItem.DrmConfiguration drmConfiguration,
|
MediaItem.DrmConfiguration drmConfiguration,
|
||||||
HttpDataSource.Factory httpDataSourceFactory,
|
DataSource.Factory dataSourceFactory,
|
||||||
StartDownloadDialogHelper dialogHelper,
|
StartDownloadDialogHelper dialogHelper,
|
||||||
DownloadHelper downloadHelper) {
|
DownloadHelper downloadHelper) {
|
||||||
this.format = format;
|
this.format = format;
|
||||||
this.drmConfiguration = drmConfiguration;
|
this.drmConfiguration = drmConfiguration;
|
||||||
this.httpDataSourceFactory = httpDataSourceFactory;
|
this.dataSourceFactory = dataSourceFactory;
|
||||||
this.dialogHelper = dialogHelper;
|
this.dialogHelper = dialogHelper;
|
||||||
this.downloadHelper = downloadHelper;
|
this.downloadHelper = downloadHelper;
|
||||||
}
|
}
|
||||||
@ -387,7 +382,7 @@ public class DownloadTracker {
|
|||||||
OfflineLicenseHelper.newWidevineInstance(
|
OfflineLicenseHelper.newWidevineInstance(
|
||||||
drmConfiguration.licenseUri.toString(),
|
drmConfiguration.licenseUri.toString(),
|
||||||
drmConfiguration.forceDefaultLicenseUri,
|
drmConfiguration.forceDefaultLicenseUri,
|
||||||
httpDataSourceFactory,
|
dataSourceFactory,
|
||||||
drmConfiguration.licenseRequestHeaders,
|
drmConfiguration.licenseRequestHeaders,
|
||||||
new DrmSessionEventListener.EventDispatcher());
|
new DrmSessionEventListener.EventDispatcher());
|
||||||
try {
|
try {
|
||||||
@ -405,7 +400,7 @@ public class DownloadTracker {
|
|||||||
if (drmSessionException != null) {
|
if (drmSessionException != null) {
|
||||||
dialogHelper.onOfflineLicenseFetchedError(drmSessionException);
|
dialogHelper.onOfflineLicenseFetchedError(drmSessionException);
|
||||||
} else {
|
} else {
|
||||||
dialogHelper.onOfflineLicenseFetched(downloadHelper, checkStateNotNull(keySetId));
|
dialogHelper.onOfflineLicenseFetched(downloadHelper, checkNotNull(keySetId));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.demo.main;
|
package androidx.media3.demo.main;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
@ -26,7 +27,6 @@ import androidx.media3.common.MediaItem;
|
|||||||
import androidx.media3.common.MediaItem.ClippingConfiguration;
|
import androidx.media3.common.MediaItem.ClippingConfiguration;
|
||||||
import androidx.media3.common.MediaItem.SubtitleConfiguration;
|
import androidx.media3.common.MediaItem.SubtitleConfiguration;
|
||||||
import androidx.media3.common.MediaMetadata;
|
import androidx.media3.common.MediaMetadata;
|
||||||
import androidx.media3.common.util.Assertions;
|
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -86,7 +86,7 @@ public class IntentUtil {
|
|||||||
|
|
||||||
/** Populates the intent with the given list of {@link MediaItem media items}. */
|
/** Populates the intent with the given list of {@link MediaItem media items}. */
|
||||||
public static void addToIntent(List<MediaItem> mediaItems, Intent intent) {
|
public static void addToIntent(List<MediaItem> mediaItems, Intent intent) {
|
||||||
Assertions.checkArgument(!mediaItems.isEmpty());
|
checkArgument(!mediaItems.isEmpty());
|
||||||
if (mediaItems.size() == 1) {
|
if (mediaItems.size() == 1) {
|
||||||
MediaItem mediaItem = mediaItems.get(0);
|
MediaItem mediaItem = mediaItems.get(0);
|
||||||
MediaItem.LocalConfiguration localConfiguration = checkNotNull(mediaItem.localConfiguration);
|
MediaItem.LocalConfiguration localConfiguration = checkNotNull(mediaItem.localConfiguration);
|
||||||
@ -241,7 +241,7 @@ public class IntentUtil {
|
|||||||
drmConfiguration.forcedSessionTrackTypes;
|
drmConfiguration.forcedSessionTrackTypes;
|
||||||
if (!forcedDrmSessionTrackTypes.isEmpty()) {
|
if (!forcedDrmSessionTrackTypes.isEmpty()) {
|
||||||
// Only video and audio together are supported.
|
// Only video and audio together are supported.
|
||||||
Assertions.checkState(
|
checkState(
|
||||||
forcedDrmSessionTrackTypes.size() == 2
|
forcedDrmSessionTrackTypes.size() == 2
|
||||||
&& forcedDrmSessionTrackTypes.contains(C.TRACK_TYPE_VIDEO)
|
&& forcedDrmSessionTrackTypes.contains(C.TRACK_TYPE_VIDEO)
|
||||||
&& forcedDrmSessionTrackTypes.contains(C.TRACK_TYPE_AUDIO));
|
&& forcedDrmSessionTrackTypes.contains(C.TRACK_TYPE_AUDIO));
|
||||||
|
@ -15,9 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.demo.main;
|
package androidx.media3.demo.main;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static com.google.common.base.Preconditions.checkState;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -116,8 +116,11 @@ public class SampleChooserActivity extends AppCompatActivity
|
|||||||
useExtensionRenderers = DemoUtil.useExtensionRenderers();
|
useExtensionRenderers = DemoUtil.useExtensionRenderers();
|
||||||
downloadTracker = DemoUtil.getDownloadTracker(/* context= */ this);
|
downloadTracker = DemoUtil.getDownloadTracker(/* context= */ this);
|
||||||
loadSample();
|
loadSample();
|
||||||
|
startDownloadService();
|
||||||
|
}
|
||||||
|
|
||||||
// Start the download service if it should be running but it's not currently.
|
/** Start the download service if it should be running but it's not currently. */
|
||||||
|
private void startDownloadService() {
|
||||||
// Starting the service in the foreground causes notification flicker if there is no scheduled
|
// Starting the service in the foreground causes notification flicker if there is no scheduled
|
||||||
// action. Starting it in the background throws an exception if the app is in the background too
|
// action. Starting it in the background throws an exception if the app is in the background too
|
||||||
// (e.g. if device screen is locked).
|
// (e.g. if device screen is locked).
|
||||||
|
@ -35,7 +35,6 @@ import androidx.media3.common.util.Util;
|
|||||||
import androidx.media3.datasource.DataSource;
|
import androidx.media3.datasource.DataSource;
|
||||||
import androidx.media3.datasource.DefaultDataSource;
|
import androidx.media3.datasource.DefaultDataSource;
|
||||||
import androidx.media3.datasource.DefaultHttpDataSource;
|
import androidx.media3.datasource.DefaultHttpDataSource;
|
||||||
import androidx.media3.datasource.HttpDataSource;
|
|
||||||
import androidx.media3.exoplayer.ExoPlayer;
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
import androidx.media3.exoplayer.dash.DashMediaSource;
|
import androidx.media3.exoplayer.dash.DashMediaSource;
|
||||||
import androidx.media3.exoplayer.drm.DefaultDrmSessionManager;
|
import androidx.media3.exoplayer.drm.DefaultDrmSessionManager;
|
||||||
@ -189,7 +188,7 @@ public final class MainActivity extends Activity {
|
|||||||
String drmScheme = Assertions.checkNotNull(intent.getStringExtra(DRM_SCHEME_EXTRA));
|
String drmScheme = Assertions.checkNotNull(intent.getStringExtra(DRM_SCHEME_EXTRA));
|
||||||
String drmLicenseUrl = Assertions.checkNotNull(intent.getStringExtra(DRM_LICENSE_URL_EXTRA));
|
String drmLicenseUrl = Assertions.checkNotNull(intent.getStringExtra(DRM_LICENSE_URL_EXTRA));
|
||||||
UUID drmSchemeUuid = Assertions.checkNotNull(Util.getDrmUuid(drmScheme));
|
UUID drmSchemeUuid = Assertions.checkNotNull(Util.getDrmUuid(drmScheme));
|
||||||
HttpDataSource.Factory licenseDataSourceFactory = new DefaultHttpDataSource.Factory();
|
DataSource.Factory licenseDataSourceFactory = new DefaultHttpDataSource.Factory();
|
||||||
HttpMediaDrmCallback drmCallback =
|
HttpMediaDrmCallback drmCallback =
|
||||||
new HttpMediaDrmCallback(drmLicenseUrl, licenseDataSourceFactory);
|
new HttpMediaDrmCallback(drmLicenseUrl, licenseDataSourceFactory);
|
||||||
drmSessionManager =
|
drmSessionManager =
|
||||||
|
@ -50,8 +50,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
public static final String AUDIO_MIME_TYPE = "audio_mime_type";
|
public static final String AUDIO_MIME_TYPE = "audio_mime_type";
|
||||||
public static final String VIDEO_MIME_TYPE = "video_mime_type";
|
public static final String VIDEO_MIME_TYPE = "video_mime_type";
|
||||||
public static final String RESOLUTION_HEIGHT = "resolution_height";
|
public static final String RESOLUTION_HEIGHT = "resolution_height";
|
||||||
public static final String TRANSLATE_X = "translate_x";
|
|
||||||
public static final String TRANSLATE_Y = "translate_y";
|
|
||||||
public static final String SCALE_X = "scale_x";
|
public static final String SCALE_X = "scale_x";
|
||||||
public static final String SCALE_Y = "scale_y";
|
public static final String SCALE_Y = "scale_y";
|
||||||
public static final String ROTATE_DEGREES = "rotate_degrees";
|
public static final String ROTATE_DEGREES = "rotate_degrees";
|
||||||
@ -81,7 +79,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
private @MonotonicNonNull Spinner audioMimeSpinner;
|
private @MonotonicNonNull Spinner audioMimeSpinner;
|
||||||
private @MonotonicNonNull Spinner videoMimeSpinner;
|
private @MonotonicNonNull Spinner videoMimeSpinner;
|
||||||
private @MonotonicNonNull Spinner resolutionHeightSpinner;
|
private @MonotonicNonNull Spinner resolutionHeightSpinner;
|
||||||
private @MonotonicNonNull Spinner translateSpinner;
|
|
||||||
private @MonotonicNonNull Spinner scaleSpinner;
|
private @MonotonicNonNull Spinner scaleSpinner;
|
||||||
private @MonotonicNonNull Spinner rotateSpinner;
|
private @MonotonicNonNull Spinner rotateSpinner;
|
||||||
private @MonotonicNonNull CheckBox enableFallbackCheckBox;
|
private @MonotonicNonNull CheckBox enableFallbackCheckBox;
|
||||||
@ -136,14 +133,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
resolutionHeightAdapter.addAll(
|
resolutionHeightAdapter.addAll(
|
||||||
SAME_AS_INPUT_OPTION, "144", "240", "360", "480", "720", "1080", "1440", "2160");
|
SAME_AS_INPUT_OPTION, "144", "240", "360", "480", "720", "1080", "1440", "2160");
|
||||||
|
|
||||||
ArrayAdapter<String> translateAdapter =
|
|
||||||
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
|
|
||||||
translateAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
|
||||||
translateSpinner = findViewById(R.id.translate_spinner);
|
|
||||||
translateSpinner.setAdapter(translateAdapter);
|
|
||||||
translateAdapter.addAll(
|
|
||||||
SAME_AS_INPUT_OPTION, "-.1, -.1", "0, 0", ".5, 0", "0, .5", "1, 1", "1.9, 0", "0, 1.9");
|
|
||||||
|
|
||||||
ArrayAdapter<String> scaleAdapter =
|
ArrayAdapter<String> scaleAdapter =
|
||||||
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
|
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
|
||||||
scaleAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
scaleAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
@ -185,7 +174,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
"audioMimeSpinner",
|
"audioMimeSpinner",
|
||||||
"videoMimeSpinner",
|
"videoMimeSpinner",
|
||||||
"resolutionHeightSpinner",
|
"resolutionHeightSpinner",
|
||||||
"translateSpinner",
|
|
||||||
"scaleSpinner",
|
"scaleSpinner",
|
||||||
"rotateSpinner",
|
"rotateSpinner",
|
||||||
"enableFallbackCheckBox",
|
"enableFallbackCheckBox",
|
||||||
@ -209,13 +197,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
if (!SAME_AS_INPUT_OPTION.equals(selectedResolutionHeight)) {
|
if (!SAME_AS_INPUT_OPTION.equals(selectedResolutionHeight)) {
|
||||||
bundle.putInt(RESOLUTION_HEIGHT, Integer.parseInt(selectedResolutionHeight));
|
bundle.putInt(RESOLUTION_HEIGHT, Integer.parseInt(selectedResolutionHeight));
|
||||||
}
|
}
|
||||||
String selectedTranslate = String.valueOf(translateSpinner.getSelectedItem());
|
|
||||||
if (!SAME_AS_INPUT_OPTION.equals(selectedTranslate)) {
|
|
||||||
List<String> translateXY = Arrays.asList(selectedTranslate.split(", "));
|
|
||||||
checkState(translateXY.size() == 2);
|
|
||||||
bundle.putFloat(TRANSLATE_X, Float.parseFloat(translateXY.get(0)));
|
|
||||||
bundle.putFloat(TRANSLATE_Y, Float.parseFloat(translateXY.get(1)));
|
|
||||||
}
|
|
||||||
String selectedScale = String.valueOf(scaleSpinner.getSelectedItem());
|
String selectedScale = String.valueOf(scaleSpinner.getSelectedItem());
|
||||||
if (!SAME_AS_INPUT_OPTION.equals(selectedScale)) {
|
if (!SAME_AS_INPUT_OPTION.equals(selectedScale)) {
|
||||||
List<String> scaleXY = Arrays.asList(selectedScale.split(", "));
|
List<String> scaleXY = Arrays.asList(selectedScale.split(", "));
|
||||||
@ -258,7 +239,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
"audioMimeSpinner",
|
"audioMimeSpinner",
|
||||||
"videoMimeSpinner",
|
"videoMimeSpinner",
|
||||||
"resolutionHeightSpinner",
|
"resolutionHeightSpinner",
|
||||||
"translateSpinner",
|
|
||||||
"scaleSpinner",
|
"scaleSpinner",
|
||||||
"rotateSpinner",
|
"rotateSpinner",
|
||||||
"enableHdrEditingCheckBox"
|
"enableHdrEditingCheckBox"
|
||||||
@ -277,7 +257,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
"audioMimeSpinner",
|
"audioMimeSpinner",
|
||||||
"videoMimeSpinner",
|
"videoMimeSpinner",
|
||||||
"resolutionHeightSpinner",
|
"resolutionHeightSpinner",
|
||||||
"translateSpinner",
|
|
||||||
"scaleSpinner",
|
"scaleSpinner",
|
||||||
"rotateSpinner",
|
"rotateSpinner",
|
||||||
"enableHdrEditingCheckBox"
|
"enableHdrEditingCheckBox"
|
||||||
@ -295,7 +274,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
"audioMimeSpinner",
|
"audioMimeSpinner",
|
||||||
"videoMimeSpinner",
|
"videoMimeSpinner",
|
||||||
"resolutionHeightSpinner",
|
"resolutionHeightSpinner",
|
||||||
"translateSpinner",
|
|
||||||
"scaleSpinner",
|
"scaleSpinner",
|
||||||
"rotateSpinner",
|
"rotateSpinner",
|
||||||
"enableHdrEditingCheckBox"
|
"enableHdrEditingCheckBox"
|
||||||
@ -304,7 +282,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
audioMimeSpinner.setEnabled(isAudioEnabled);
|
audioMimeSpinner.setEnabled(isAudioEnabled);
|
||||||
videoMimeSpinner.setEnabled(isVideoEnabled);
|
videoMimeSpinner.setEnabled(isVideoEnabled);
|
||||||
resolutionHeightSpinner.setEnabled(isVideoEnabled);
|
resolutionHeightSpinner.setEnabled(isVideoEnabled);
|
||||||
translateSpinner.setEnabled(isVideoEnabled);
|
|
||||||
scaleSpinner.setEnabled(isVideoEnabled);
|
scaleSpinner.setEnabled(isVideoEnabled);
|
||||||
rotateSpinner.setEnabled(isVideoEnabled);
|
rotateSpinner.setEnabled(isVideoEnabled);
|
||||||
enableHdrEditingCheckBox.setEnabled(isVideoEnabled);
|
enableHdrEditingCheckBox.setEnabled(isVideoEnabled);
|
||||||
@ -312,7 +289,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
|
|||||||
findViewById(R.id.audio_mime_text_view).setEnabled(isAudioEnabled);
|
findViewById(R.id.audio_mime_text_view).setEnabled(isAudioEnabled);
|
||||||
findViewById(R.id.video_mime_text_view).setEnabled(isVideoEnabled);
|
findViewById(R.id.video_mime_text_view).setEnabled(isVideoEnabled);
|
||||||
findViewById(R.id.resolution_height_text_view).setEnabled(isVideoEnabled);
|
findViewById(R.id.resolution_height_text_view).setEnabled(isVideoEnabled);
|
||||||
findViewById(R.id.translate).setEnabled(isVideoEnabled);
|
|
||||||
findViewById(R.id.scale).setEnabled(isVideoEnabled);
|
findViewById(R.id.scale).setEnabled(isVideoEnabled);
|
||||||
findViewById(R.id.rotate).setEnabled(isVideoEnabled);
|
findViewById(R.id.rotate).setEnabled(isVideoEnabled);
|
||||||
findViewById(R.id.hdr_editing).setEnabled(isVideoEnabled);
|
findViewById(R.id.hdr_editing).setEnabled(isVideoEnabled);
|
||||||
|
@ -21,7 +21,6 @@ import static androidx.media3.common.util.Assertions.checkNotNull;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Matrix;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
@ -217,10 +216,15 @@ public final class TransformerActivity extends AppCompatActivity {
|
|||||||
if (resolutionHeight != C.LENGTH_UNSET) {
|
if (resolutionHeight != C.LENGTH_UNSET) {
|
||||||
requestBuilder.setResolution(resolutionHeight);
|
requestBuilder.setResolution(resolutionHeight);
|
||||||
}
|
}
|
||||||
Matrix transformationMatrix = getTransformationMatrix(bundle);
|
|
||||||
if (!transformationMatrix.isIdentity()) {
|
float scaleX = bundle.getFloat(ConfigurationActivity.SCALE_X, /* defaultValue= */ 1);
|
||||||
requestBuilder.setTransformationMatrix(transformationMatrix);
|
float scaleY = bundle.getFloat(ConfigurationActivity.SCALE_Y, /* defaultValue= */ 1);
|
||||||
}
|
requestBuilder.setScale(scaleX, scaleY);
|
||||||
|
|
||||||
|
float rotateDegrees =
|
||||||
|
bundle.getFloat(ConfigurationActivity.ROTATE_DEGREES, /* defaultValue= */ 0);
|
||||||
|
requestBuilder.setRotationDegrees(rotateDegrees);
|
||||||
|
|
||||||
requestBuilder.experimental_setEnableHdrEditing(
|
requestBuilder.experimental_setEnableHdrEditing(
|
||||||
bundle.getBoolean(ConfigurationActivity.ENABLE_HDR_EDITING));
|
bundle.getBoolean(ConfigurationActivity.ENABLE_HDR_EDITING));
|
||||||
transformerBuilder
|
transformerBuilder
|
||||||
@ -251,27 +255,6 @@ public final class TransformerActivity extends AppCompatActivity {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Matrix getTransformationMatrix(Bundle bundle) {
|
|
||||||
Matrix transformationMatrix = new Matrix();
|
|
||||||
|
|
||||||
float translateX = bundle.getFloat(ConfigurationActivity.TRANSLATE_X, /* defaultValue= */ 0);
|
|
||||||
float translateY = bundle.getFloat(ConfigurationActivity.TRANSLATE_Y, /* defaultValue= */ 0);
|
|
||||||
// TODO(b/201293185): Implement an AdvancedFrameEditor to handle translation, as the current
|
|
||||||
// transformationMatrix is automatically adjusted to focus on the original pixels and
|
|
||||||
// effectively undo translations.
|
|
||||||
transformationMatrix.postTranslate(translateX, translateY);
|
|
||||||
|
|
||||||
float scaleX = bundle.getFloat(ConfigurationActivity.SCALE_X, /* defaultValue= */ 1);
|
|
||||||
float scaleY = bundle.getFloat(ConfigurationActivity.SCALE_Y, /* defaultValue= */ 1);
|
|
||||||
transformationMatrix.postScale(scaleX, scaleY);
|
|
||||||
|
|
||||||
float rotateDegrees =
|
|
||||||
bundle.getFloat(ConfigurationActivity.ROTATE_DEGREES, /* defaultValue= */ 0);
|
|
||||||
transformationMatrix.postRotate(rotateDegrees);
|
|
||||||
|
|
||||||
return transformationMatrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
@RequiresNonNull({
|
@RequiresNonNull({
|
||||||
"informationTextView",
|
"informationTextView",
|
||||||
"progressViewGroup",
|
"progressViewGroup",
|
||||||
|
@ -137,17 +137,6 @@
|
|||||||
android:layout_gravity="right|center_vertical"
|
android:layout_gravity="right|center_vertical"
|
||||||
android:gravity="right" />
|
android:gravity="right" />
|
||||||
</TableRow>
|
</TableRow>
|
||||||
<TableRow
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="center_vertical" >
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/translate"
|
|
||||||
android:text="@string/translate"/>
|
|
||||||
<Spinner
|
|
||||||
android:id="@+id/translate_spinner"
|
|
||||||
android:layout_gravity="right|center_vertical"
|
|
||||||
android:gravity="right" />
|
|
||||||
</TableRow>
|
|
||||||
<TableRow
|
<TableRow
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:gravity="center_vertical" >
|
android:gravity="center_vertical" >
|
||||||
|
@ -24,7 +24,6 @@
|
|||||||
<string name="audio_mime" translatable="false">Output audio MIME type</string>
|
<string name="audio_mime" translatable="false">Output audio MIME type</string>
|
||||||
<string name="video_mime" translatable="false">Output video MIME type</string>
|
<string name="video_mime" translatable="false">Output video MIME type</string>
|
||||||
<string name="resolution_height" translatable="false">Output video resolution</string>
|
<string name="resolution_height" translatable="false">Output video resolution</string>
|
||||||
<string name="translate" translatable="false">Translate video</string>
|
|
||||||
<string name="scale" translatable="false">Scale video</string>
|
<string name="scale" translatable="false">Scale video</string>
|
||||||
<string name="rotate" translatable="false">Rotate video (degrees)</string>
|
<string name="rotate" translatable="false">Rotate video (degrees)</string>
|
||||||
<string name="enable_fallback" translatable="false">Enable fallback</string>
|
<string name="enable_fallback" translatable="false">Enable fallback</string>
|
||||||
|
@ -111,9 +111,6 @@ public final class CastPlayer extends BasePlayer {
|
|||||||
|
|
||||||
private static final String TAG = "CastPlayer";
|
private static final String TAG = "CastPlayer";
|
||||||
|
|
||||||
private static final int RENDERER_INDEX_VIDEO = 0;
|
|
||||||
private static final int RENDERER_INDEX_AUDIO = 1;
|
|
||||||
private static final int RENDERER_INDEX_TEXT = 2;
|
|
||||||
private static final long PROGRESS_REPORT_PERIOD_MS = 1000;
|
private static final long PROGRESS_REPORT_PERIOD_MS = 1000;
|
||||||
private static final long[] EMPTY_TRACK_ID_ARRAY = new long[0];
|
private static final long[] EMPTY_TRACK_ID_ARRAY = new long[0];
|
||||||
|
|
||||||
|
@ -683,11 +683,12 @@ public interface Player {
|
|||||||
/**
|
/**
|
||||||
* Called when the combined {@link MediaMetadata} changes.
|
* Called when the combined {@link MediaMetadata} changes.
|
||||||
*
|
*
|
||||||
* <p>The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata}
|
* <p>The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata
|
||||||
* and the static and dynamic metadata from the {@link TrackSelection#getFormat(int) track
|
* MediaItem metadata}, the static metadata in the media's {@link Format#metadata Format}, and
|
||||||
* selections' formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in
|
* any timed metadata that has been parsed from the media and output via {@link
|
||||||
* the {@link MediaItem#mediaMetadata}, it will be prioritised above the same field coming from
|
* Listener#onMetadata(Metadata)}. If a field is populated in the {@link
|
||||||
* static or dynamic metadata.
|
* MediaItem#mediaMetadata}, it will be prioritised above the same field coming from static or
|
||||||
|
* timed metadata.
|
||||||
*
|
*
|
||||||
* <p>This method may be called multiple times in quick succession.
|
* <p>This method may be called multiple times in quick succession.
|
||||||
*
|
*
|
||||||
@ -2123,11 +2124,11 @@ public interface Player {
|
|||||||
* Returns the current combined {@link MediaMetadata}, or {@link MediaMetadata#EMPTY} if not
|
* Returns the current combined {@link MediaMetadata}, or {@link MediaMetadata#EMPTY} if not
|
||||||
* supported.
|
* supported.
|
||||||
*
|
*
|
||||||
* <p>This {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata} and the
|
* <p>This {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata MediaItem
|
||||||
* static and dynamic metadata from the {@link TrackSelection#getFormat(int) track selections'
|
* metadata}, the static metadata in the media's {@link Format#metadata Format}, and any timed
|
||||||
* formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in the {@link
|
* metadata that has been parsed from the media and output via {@link
|
||||||
* MediaItem#mediaMetadata}, it will be prioritised above the same field coming from static or
|
* Listener#onMetadata(Metadata)}. If a field is populated in the {@link MediaItem#mediaMetadata},
|
||||||
* dynamic metadata.
|
* it will be prioritised above the same field coming from static or timed metadata.
|
||||||
*/
|
*/
|
||||||
MediaMetadata getMediaMetadata();
|
MediaMetadata getMediaMetadata();
|
||||||
|
|
||||||
|
@ -34,7 +34,21 @@ import java.lang.annotation.Target;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Defines an immutable group of tracks identified by their format identity. */
|
/**
|
||||||
|
* An immutable group of tracks. All tracks in a group present the same content, but their formats
|
||||||
|
* may differ.
|
||||||
|
*
|
||||||
|
* <p>As an example of how tracks can be grouped, consider an adaptive playback where a main video
|
||||||
|
* feed is provided in five resolutions, and an alternative video feed (e.g., a different camera
|
||||||
|
* angle in a sports match) is provided in two resolutions. In this case there will be two video
|
||||||
|
* track groups, one corresponding to the main video feed containing five tracks, and a second for
|
||||||
|
* the alternative video feed containing two tracks.
|
||||||
|
*
|
||||||
|
* <p>Note that audio tracks whose languages differ are not grouped, because content in different
|
||||||
|
* languages is not considered to be the same. Conversely, audio tracks in the same language that
|
||||||
|
* only differ in properties such as bitrate, sampling rate, channel count and so on can be grouped.
|
||||||
|
* This also applies to text tracks.
|
||||||
|
*/
|
||||||
public final class TrackGroup implements Bundleable {
|
public final class TrackGroup implements Bundleable {
|
||||||
|
|
||||||
private static final String TAG = "TrackGroup";
|
private static final String TAG = "TrackGroup";
|
||||||
|
@ -31,16 +31,21 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Forces the selection of {@link #trackIndices} for a {@link TrackGroup}.
|
* A track selection override, consisting of a {@link TrackGroup} and the indices of the tracks
|
||||||
|
* within the group that should be selected.
|
||||||
*
|
*
|
||||||
* <p>If multiple tracks in {@link #trackGroup} are overridden, as many as possible will be selected
|
* <p>A track selection override is applied during playback if the media being played contains a
|
||||||
* depending on the player capabilities.
|
* {@link TrackGroup} equal to the one in the override. If a {@link TrackSelectionParameters}
|
||||||
|
* contains only one override of a given track type that applies to the media, this override will be
|
||||||
|
* used to control the track selection for that type. If multiple overrides of a given track type
|
||||||
|
* apply then the player will apply only one of them.
|
||||||
*
|
*
|
||||||
* <p>If {@link #trackIndices} is empty, no tracks from {@link #trackGroup} will be played. This is
|
* <p>If {@link #trackIndices} is empty then the override specifies that no tracks should be
|
||||||
* similar to {@link TrackSelectionParameters#disabledTrackTypes}, except it will only affect the
|
* selected. Adding an empty override to a {@link TrackSelectionParameters} is similar to {@link
|
||||||
* playback of the associated {@link TrackGroup}. For example, if the only {@link
|
* TrackSelectionParameters.Builder#setTrackTypeDisabled disabling a track type}, except that an
|
||||||
* C#TRACK_TYPE_VIDEO} {@link TrackGroup} is associated with no tracks, no video will play until the
|
* empty override will only be applied if the media being played contains a {@link TrackGroup} equal
|
||||||
* next video starts.
|
* to the one in the override. Conversely, disabling a track type will prevent selection of tracks
|
||||||
|
* of that type for all media.
|
||||||
*/
|
*/
|
||||||
public final class TrackSelectionOverride implements Bundleable {
|
public final class TrackSelectionOverride implements Bundleable {
|
||||||
|
|
||||||
|
@ -47,10 +47,11 @@ import org.checkerframework.checker.initialization.qual.UnknownInitialization;
|
|||||||
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constraint parameters for track selection.
|
* Parameters for controlling track selection.
|
||||||
*
|
*
|
||||||
* <p>For example the following code modifies the parameters to restrict video track selections to
|
* <p>Parameters can be queried and set on a {@link Player}. For example the following code modifies
|
||||||
* SD, and to select a German audio track if there is one:
|
* the parameters to restrict video track selections to SD, and to select a German audio track if
|
||||||
|
* there is one:
|
||||||
*
|
*
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* // Build on the current parameters.
|
* // Build on the current parameters.
|
||||||
@ -656,28 +657,26 @@ public class TrackSelectionParameters implements Bundleable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Adds an override for the provided {@link TrackGroup}. */
|
/** Adds an override, replacing any override for the same {@link TrackGroup}. */
|
||||||
public Builder addOverride(TrackSelectionOverride override) {
|
public Builder addOverride(TrackSelectionOverride override) {
|
||||||
overrides.put(override.trackGroup, override);
|
overrides.put(override.trackGroup, override);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Removes the override associated with the provided {@link TrackGroup} if present. */
|
/** Sets an override, replacing all existing overrides with the same track type. */
|
||||||
public Builder clearOverride(TrackGroup trackGroup) {
|
|
||||||
overrides.remove(trackGroup);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Set the override for the type of the provided {@link TrackGroup}. */
|
|
||||||
public Builder setOverrideForType(TrackSelectionOverride override) {
|
public Builder setOverrideForType(TrackSelectionOverride override) {
|
||||||
clearOverridesOfType(override.getTrackType());
|
clearOverridesOfType(override.getTrackType());
|
||||||
overrides.put(override.trackGroup, override);
|
overrides.put(override.trackGroup, override);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Removes the override for the provided {@link TrackGroup}, if there is one. */
|
||||||
* Remove any override associated with {@link TrackGroup TrackGroups} of type {@code trackType}.
|
public Builder clearOverride(TrackGroup trackGroup) {
|
||||||
*/
|
overrides.remove(trackGroup);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Removes all overrides of the provided track type. */
|
||||||
public Builder clearOverridesOfType(@C.TrackType int trackType) {
|
public Builder clearOverridesOfType(@C.TrackType int trackType) {
|
||||||
Iterator<TrackSelectionOverride> it = overrides.values().iterator();
|
Iterator<TrackSelectionOverride> it = overrides.values().iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
@ -689,7 +688,7 @@ public class TrackSelectionParameters implements Bundleable {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Removes all track overrides. */
|
/** Removes all overrides. */
|
||||||
public Builder clearOverrides() {
|
public Builder clearOverrides() {
|
||||||
overrides.clear();
|
overrides.clear();
|
||||||
return this;
|
return this;
|
||||||
|
@ -41,8 +41,8 @@ public final class TracksInfo implements Bundleable {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Information about a single group of tracks, including the underlying {@link TrackGroup}, the
|
* Information about a single group of tracks, including the underlying {@link TrackGroup}, the
|
||||||
* {@link C.TrackType type} of tracks it contains, and the level to which each track is supported
|
* level to which each track is supported by the player, and whether any of the tracks are
|
||||||
* by the player.
|
* selected.
|
||||||
*/
|
*/
|
||||||
public static final class TrackGroupInfo implements Bundleable {
|
public static final class TrackGroupInfo implements Bundleable {
|
||||||
|
|
||||||
@ -55,26 +55,26 @@ public final class TracksInfo implements Bundleable {
|
|||||||
private final boolean[] trackSelected;
|
private final boolean[] trackSelected;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a TrackGroupInfo.
|
* Constructs an instance.
|
||||||
*
|
*
|
||||||
* @param trackGroup The {@link TrackGroup} described.
|
* @param trackGroup The underlying {@link TrackGroup}.
|
||||||
* @param adaptiveSupported Whether adaptive selections containing more than one track in the
|
* @param adaptiveSupported Whether the player supports adaptive selections containing more than
|
||||||
* {@code trackGroup} are supported.
|
* one track in the group.
|
||||||
* @param trackSupport The {@link C.FormatSupport} of each track in the {@code trackGroup}.
|
* @param trackSupport The {@link C.FormatSupport} of each track in the group.
|
||||||
* @param tracksSelected Whether each track in the {@code trackGroup} is selected.
|
* @param trackSelected Whether each track in the {@code trackGroup} is selected.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public TrackGroupInfo(
|
public TrackGroupInfo(
|
||||||
TrackGroup trackGroup,
|
TrackGroup trackGroup,
|
||||||
boolean adaptiveSupported,
|
boolean adaptiveSupported,
|
||||||
@C.FormatSupport int[] trackSupport,
|
@C.FormatSupport int[] trackSupport,
|
||||||
boolean[] tracksSelected) {
|
boolean[] trackSelected) {
|
||||||
length = trackGroup.length;
|
length = trackGroup.length;
|
||||||
checkArgument(length == trackSupport.length && length == tracksSelected.length);
|
checkArgument(length == trackSupport.length && length == trackSelected.length);
|
||||||
this.trackGroup = trackGroup;
|
this.trackGroup = trackGroup;
|
||||||
this.adaptiveSupported = adaptiveSupported && length > 1;
|
this.adaptiveSupported = adaptiveSupported && length > 1;
|
||||||
this.trackSupport = trackSupport.clone();
|
this.trackSupport = trackSupport.clone();
|
||||||
this.trackSelected = tracksSelected.clone();
|
this.trackSelected = trackSelected.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the underlying {@link TrackGroup}. */
|
/** Returns the underlying {@link TrackGroup}. */
|
||||||
@ -266,11 +266,11 @@ public final class TracksInfo implements Bundleable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final ImmutableList<TrackGroupInfo> trackGroupInfos;
|
|
||||||
|
|
||||||
/** An {@code TrackInfo} that contains no tracks. */
|
/** An {@code TrackInfo} that contains no tracks. */
|
||||||
@UnstableApi public static final TracksInfo EMPTY = new TracksInfo(ImmutableList.of());
|
@UnstableApi public static final TracksInfo EMPTY = new TracksInfo(ImmutableList.of());
|
||||||
|
|
||||||
|
private final ImmutableList<TrackGroupInfo> trackGroupInfos;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs an instance.
|
* Constructs an instance.
|
||||||
*
|
*
|
||||||
|
@ -147,8 +147,6 @@ public final class GlProgram {
|
|||||||
* <p>Call this in the rendering loop to switch between different programs.
|
* <p>Call this in the rendering loop to switch between different programs.
|
||||||
*/
|
*/
|
||||||
public void use() {
|
public void use() {
|
||||||
// TODO(b/214975934): When multiple GL programs are supported by Transformer, make sure
|
|
||||||
// to call use() to switch between programs.
|
|
||||||
GLES20.glUseProgram(programId);
|
GLES20.glUseProgram(programId);
|
||||||
GlUtil.checkGlError();
|
GlUtil.checkGlError();
|
||||||
}
|
}
|
||||||
@ -175,9 +173,16 @@ public final class GlProgram {
|
|||||||
checkNotNull(attributeByName.get(name)).setBuffer(values, size);
|
checkNotNull(attributeByName.get(name)).setBuffer(values, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets a texture sampler type uniform. */
|
/**
|
||||||
public void setSamplerTexIdUniform(String name, int texId, int unit) {
|
* Sets a texture sampler type uniform.
|
||||||
checkNotNull(uniformByName.get(name)).setSamplerTexId(texId, unit);
|
*
|
||||||
|
* @param name The uniform's name.
|
||||||
|
* @param texId The texture identifier.
|
||||||
|
* @param texUnitIndex The texture unit index. Use a different index (0, 1, 2, ...) for each
|
||||||
|
* texture sampler in the program.
|
||||||
|
*/
|
||||||
|
public void setSamplerTexIdUniform(String name, int texId, int texUnitIndex) {
|
||||||
|
checkNotNull(uniformByName.get(name)).setSamplerTexId(texId, texUnitIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets a float type uniform. */
|
/** Sets a float type uniform. */
|
||||||
@ -322,7 +327,7 @@ public final class GlProgram {
|
|||||||
private final float[] value;
|
private final float[] value;
|
||||||
|
|
||||||
private int texId;
|
private int texId;
|
||||||
private int unit;
|
private int texUnitIndex;
|
||||||
|
|
||||||
private Uniform(String name, int location, int type) {
|
private Uniform(String name, int location, int type) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
@ -335,11 +340,11 @@ public final class GlProgram {
|
|||||||
* Configures {@link #bind()} to use the specified {@code texId} for this sampler uniform.
|
* Configures {@link #bind()} to use the specified {@code texId} for this sampler uniform.
|
||||||
*
|
*
|
||||||
* @param texId The GL texture identifier from which to sample.
|
* @param texId The GL texture identifier from which to sample.
|
||||||
* @param unit The GL texture unit index.
|
* @param texUnitIndex The GL texture unit index.
|
||||||
*/
|
*/
|
||||||
public void setSamplerTexId(int texId, int unit) {
|
public void setSamplerTexId(int texId, int texUnitIndex) {
|
||||||
this.texId = texId;
|
this.texId = texId;
|
||||||
this.unit = unit;
|
this.texUnitIndex = texUnitIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Configures {@link #bind()} to use the specified float {@code value} for this uniform. */
|
/** Configures {@link #bind()} to use the specified float {@code value} for this uniform. */
|
||||||
@ -382,7 +387,7 @@ public final class GlProgram {
|
|||||||
if (texId == 0) {
|
if (texId == 0) {
|
||||||
throw new IllegalStateException("No call to setSamplerTexId() before bind.");
|
throw new IllegalStateException("No call to setSamplerTexId() before bind.");
|
||||||
}
|
}
|
||||||
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + unit);
|
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + texUnitIndex);
|
||||||
if (type == GLES11Ext.GL_SAMPLER_EXTERNAL_OES || type == GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT) {
|
if (type == GLES11Ext.GL_SAMPLER_EXTERNAL_OES || type == GL_SAMPLER_EXTERNAL_2D_Y2Y_EXT) {
|
||||||
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
|
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
|
||||||
} else if (type == GLES20.GL_SAMPLER_2D) {
|
} else if (type == GLES20.GL_SAMPLER_2D) {
|
||||||
@ -390,7 +395,7 @@ public final class GlProgram {
|
|||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Unexpected uniform type: " + type);
|
throw new IllegalStateException("Unexpected uniform type: " + type);
|
||||||
}
|
}
|
||||||
GLES20.glUniform1i(location, unit);
|
GLES20.glUniform1i(location, texUnitIndex);
|
||||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
|
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
|
||||||
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
|
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
|
||||||
GLES20.glTexParameteri(
|
GLES20.glTexParameteri(
|
||||||
|
@ -119,7 +119,8 @@ public final class GlUtil {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether creating a GL context with {@value #EXTENSION_PROTECTED_CONTENT} is possible.
|
* Returns whether creating a GL context with {@value #EXTENSION_PROTECTED_CONTENT} is possible.
|
||||||
* If {@code true}, the device supports a protected output path for DRM content when using GL.
|
*
|
||||||
|
* <p>If {@code true}, the device supports a protected output path for DRM content when using GL.
|
||||||
*/
|
*/
|
||||||
public static boolean isProtectedContentExtensionSupported(Context context) {
|
public static boolean isProtectedContentExtensionSupported(Context context) {
|
||||||
if (Util.SDK_INT < 24) {
|
if (Util.SDK_INT < 24) {
|
||||||
@ -222,6 +223,30 @@ public final class GlUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts the texture size is valid.
|
||||||
|
*
|
||||||
|
* @param width The width for a texture.
|
||||||
|
* @param height The height for a texture.
|
||||||
|
* @throws GlException If the texture width or height is invalid.
|
||||||
|
*/
|
||||||
|
public static void assertValidTextureSize(int width, int height) {
|
||||||
|
// TODO(b/201293185): Consider handling adjustments for sizes > GL_MAX_TEXTURE_SIZE
|
||||||
|
// (ex. downscaling appropriately) in a FrameProcessor instead of asserting incorrect values.
|
||||||
|
|
||||||
|
// For valid GL sizes, see:
|
||||||
|
// https://www.khronos.org/registry/OpenGL-Refpages/es2.0/xhtml/glTexImage2D.xml
|
||||||
|
int[] maxTextureSizeBuffer = new int[1];
|
||||||
|
GLES20.glGetIntegerv(GLES20.GL_MAX_TEXTURE_SIZE, maxTextureSizeBuffer, 0);
|
||||||
|
int maxTextureSize = maxTextureSizeBuffer[0];
|
||||||
|
if (width < 0 || height < 0) {
|
||||||
|
throwGlException("width or height is less than 0");
|
||||||
|
}
|
||||||
|
if (width > maxTextureSize || height > maxTextureSize) {
|
||||||
|
throwGlException("width or height is greater than GL_MAX_TEXTURE_SIZE " + maxTextureSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes the specified {@code eglSurface} the render target, using a viewport of {@code width} by
|
* Makes the specified {@code eglSurface} the render target, using a viewport of {@code width} by
|
||||||
* {@code height} pixels.
|
* {@code height} pixels.
|
||||||
@ -320,6 +345,7 @@ public final class GlUtil {
|
|||||||
* @param height of the new texture in pixels
|
* @param height of the new texture in pixels
|
||||||
*/
|
*/
|
||||||
public static int createTexture(int width, int height) {
|
public static int createTexture(int width, int height) {
|
||||||
|
assertValidTextureSize(width, height);
|
||||||
int texId = generateAndBindTexture(GLES20.GL_TEXTURE_2D);
|
int texId = generateAndBindTexture(GLES20.GL_TEXTURE_2D);
|
||||||
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(width * height * 4);
|
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(width * height * 4);
|
||||||
GLES20.glTexImage2D(
|
GLES20.glTexImage2D(
|
||||||
@ -390,6 +416,11 @@ public final class GlUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void checkEglException(String errorMessage) {
|
||||||
|
int error = EGL14.eglGetError();
|
||||||
|
checkEglException(error == EGL14.EGL_SUCCESS, errorMessage + ", error code: " + error);
|
||||||
|
}
|
||||||
|
|
||||||
@RequiresApi(17)
|
@RequiresApi(17)
|
||||||
private static final class Api17 {
|
private static final class Api17 {
|
||||||
private Api17() {}
|
private Api17() {}
|
||||||
@ -438,12 +469,15 @@ public final class GlUtil {
|
|||||||
Object surface,
|
Object surface,
|
||||||
int[] configAttributes,
|
int[] configAttributes,
|
||||||
int[] windowSurfaceAttributes) {
|
int[] windowSurfaceAttributes) {
|
||||||
return EGL14.eglCreateWindowSurface(
|
EGLSurface eglSurface =
|
||||||
|
EGL14.eglCreateWindowSurface(
|
||||||
eglDisplay,
|
eglDisplay,
|
||||||
getEglConfig(eglDisplay, configAttributes),
|
getEglConfig(eglDisplay, configAttributes),
|
||||||
surface,
|
surface,
|
||||||
windowSurfaceAttributes,
|
windowSurfaceAttributes,
|
||||||
/* offset= */ 0);
|
/* offset= */ 0);
|
||||||
|
checkEglException("Error creating surface");
|
||||||
|
return eglSurface;
|
||||||
}
|
}
|
||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
@ -459,8 +493,11 @@ public final class GlUtil {
|
|||||||
if (boundFramebuffer[0] != framebuffer) {
|
if (boundFramebuffer[0] != framebuffer) {
|
||||||
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffer);
|
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffer);
|
||||||
}
|
}
|
||||||
|
checkGlError();
|
||||||
EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
|
EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
|
||||||
|
checkEglException("Error making context current");
|
||||||
GLES20.glViewport(/* x= */ 0, /* y= */ 0, width, height);
|
GLES20.glViewport(/* x= */ 0, /* y= */ 0, width, height);
|
||||||
|
checkGlError();
|
||||||
}
|
}
|
||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
@ -471,19 +508,15 @@ public final class GlUtil {
|
|||||||
}
|
}
|
||||||
EGL14.eglMakeCurrent(
|
EGL14.eglMakeCurrent(
|
||||||
eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
|
eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
|
||||||
int error = EGL14.eglGetError();
|
checkEglException("Error releasing context");
|
||||||
checkEglException(error == EGL14.EGL_SUCCESS, "Error releasing context: " + error);
|
|
||||||
if (eglContext != null) {
|
if (eglContext != null) {
|
||||||
EGL14.eglDestroyContext(eglDisplay, eglContext);
|
EGL14.eglDestroyContext(eglDisplay, eglContext);
|
||||||
error = EGL14.eglGetError();
|
checkEglException("Error destroying context");
|
||||||
checkEglException(error == EGL14.EGL_SUCCESS, "Error destroying context: " + error);
|
|
||||||
}
|
}
|
||||||
EGL14.eglReleaseThread();
|
EGL14.eglReleaseThread();
|
||||||
error = EGL14.eglGetError();
|
checkEglException("Error releasing thread");
|
||||||
checkEglException(error == EGL14.EGL_SUCCESS, "Error releasing thread: " + error);
|
|
||||||
EGL14.eglTerminate(eglDisplay);
|
EGL14.eglTerminate(eglDisplay);
|
||||||
error = EGL14.eglGetError();
|
checkEglException("Error terminating display");
|
||||||
checkEglException(error == EGL14.EGL_SUCCESS, "Error terminating display: " + error);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DoNotInline
|
@DoNotInline
|
||||||
|
@ -1143,18 +1143,6 @@ public final class Util {
|
|||||||
return (timeMs == C.TIME_UNSET || timeMs == C.TIME_END_OF_SOURCE) ? timeMs : (timeMs * 1000);
|
return (timeMs == C.TIME_UNSET || timeMs == C.TIME_END_OF_SOURCE) ? timeMs : (timeMs * 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a time in seconds to the corresponding time in microseconds.
|
|
||||||
*
|
|
||||||
* @param timeSec The time in seconds.
|
|
||||||
* @return The corresponding time in microseconds.
|
|
||||||
*/
|
|
||||||
public static long secToUs(double timeSec) {
|
|
||||||
return BigDecimal.valueOf(timeSec)
|
|
||||||
.multiply(BigDecimal.valueOf(C.MICROS_PER_SECOND))
|
|
||||||
.longValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses an xs:duration attribute value, returning the parsed duration in milliseconds.
|
* Parses an xs:duration attribute value, returning the parsed duration in milliseconds.
|
||||||
*
|
*
|
||||||
|
@ -22,6 +22,7 @@ import static android.graphics.Color.argb;
|
|||||||
import static android.graphics.Color.parseColor;
|
import static android.graphics.Color.parseColor;
|
||||||
import static androidx.media3.common.util.ColorParser.parseTtmlColor;
|
import static androidx.media3.common.util.ColorParser.parseTtmlColor;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
@ -34,24 +35,26 @@ public final class ColorParserTest {
|
|||||||
|
|
||||||
// Negative tests.
|
// Negative tests.
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test
|
||||||
public void parseUnknownColor() {
|
public void parseUnknownColor() {
|
||||||
ColorParser.parseTtmlColor("colorOfAnElectron");
|
assertThrows(
|
||||||
|
IllegalArgumentException.class, () -> ColorParser.parseTtmlColor("colorOfAnElectron"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test
|
||||||
public void parseNull() {
|
public void parseNull() {
|
||||||
ColorParser.parseTtmlColor(null);
|
assertThrows(IllegalArgumentException.class, () -> ColorParser.parseTtmlColor(null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test
|
||||||
public void parseEmpty() {
|
public void parseEmpty() {
|
||||||
ColorParser.parseTtmlColor("");
|
assertThrows(IllegalArgumentException.class, () -> ColorParser.parseTtmlColor(""));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test
|
||||||
public void rgbColorParsingRgbValuesNegative() {
|
public void rgbColorParsingRgbValuesNegative() {
|
||||||
ColorParser.parseTtmlColor("rgb(-4, 55, 209)");
|
assertThrows(
|
||||||
|
IllegalArgumentException.class, () -> ColorParser.parseTtmlColor("rgb(-4, 55, 209)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Positive tests.
|
// Positive tests.
|
||||||
|
@ -48,6 +48,7 @@ public abstract class BaseDataSource implements DataSource {
|
|||||||
this.listeners = new ArrayList<>(/* initialCapacity= */ 1);
|
this.listeners = new ArrayList<>(/* initialCapacity= */ 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public final void addTransferListener(TransferListener transferListener) {
|
public final void addTransferListener(TransferListener transferListener) {
|
||||||
checkNotNull(transferListener);
|
checkNotNull(transferListener);
|
||||||
|
@ -26,13 +26,13 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
/** Reads data from URI-identified resources. */
|
/** Reads data from URI-identified resources. */
|
||||||
@UnstableApi
|
|
||||||
public interface DataSource extends DataReader {
|
public interface DataSource extends DataReader {
|
||||||
|
|
||||||
/** A factory for {@link DataSource} instances. */
|
/** A factory for {@link DataSource} instances. */
|
||||||
interface Factory {
|
interface Factory {
|
||||||
|
|
||||||
/** Creates a {@link DataSource} instance. */
|
/** Creates a {@link DataSource} instance. */
|
||||||
|
@UnstableApi
|
||||||
DataSource createDataSource();
|
DataSource createDataSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -41,6 +41,7 @@ public interface DataSource extends DataReader {
|
|||||||
*
|
*
|
||||||
* @param transferListener A {@link TransferListener}.
|
* @param transferListener A {@link TransferListener}.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
void addTransferListener(TransferListener transferListener);
|
void addTransferListener(TransferListener transferListener);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,6 +73,7 @@ public interface DataSource extends DataReader {
|
|||||||
* unresolved. For all other requests, the value returned will be equal to the request's
|
* unresolved. For all other requests, the value returned will be equal to the request's
|
||||||
* {@link DataSpec#length}.
|
* {@link DataSpec#length}.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
long open(DataSpec dataSpec) throws IOException;
|
long open(DataSpec dataSpec) throws IOException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -82,6 +84,7 @@ public interface DataSource extends DataReader {
|
|||||||
*
|
*
|
||||||
* @return The {@link Uri} from which data is being read, or null if the source is not open.
|
* @return The {@link Uri} from which data is being read, or null if the source is not open.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
@Nullable
|
@Nullable
|
||||||
Uri getUri();
|
Uri getUri();
|
||||||
|
|
||||||
@ -91,6 +94,7 @@ public interface DataSource extends DataReader {
|
|||||||
*
|
*
|
||||||
* <p>Key look-up in the returned map is case-insensitive.
|
* <p>Key look-up in the returned map is case-insensitive.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
default Map<String, List<String>> getResponseHeaders() {
|
default Map<String, List<String>> getResponseHeaders() {
|
||||||
return Collections.emptyMap();
|
return Collections.emptyMap();
|
||||||
}
|
}
|
||||||
@ -101,5 +105,6 @@ public interface DataSource extends DataReader {
|
|||||||
*
|
*
|
||||||
* @throws IOException If an error occurs closing the source.
|
* @throws IOException If an error occurs closing the source.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
void close() throws IOException;
|
void close() throws IOException;
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,6 @@ import java.util.Map;
|
|||||||
* #DefaultDataSource(Context, DataSource)}.
|
* #DefaultDataSource(Context, DataSource)}.
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
|
||||||
public final class DefaultDataSource implements DataSource {
|
public final class DefaultDataSource implements DataSource {
|
||||||
|
|
||||||
/** {@link DataSource.Factory} for {@link DefaultDataSource} instances. */
|
/** {@link DataSource.Factory} for {@link DefaultDataSource} instances. */
|
||||||
@ -98,11 +97,13 @@ public final class DefaultDataSource implements DataSource {
|
|||||||
* @param transferListener The listener that will be used.
|
* @param transferListener The listener that will be used.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setTransferListener(@Nullable TransferListener transferListener) {
|
public Factory setTransferListener(@Nullable TransferListener transferListener) {
|
||||||
this.transferListener = transferListener;
|
this.transferListener = transferListener;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public DefaultDataSource createDataSource() {
|
public DefaultDataSource createDataSource() {
|
||||||
DefaultDataSource dataSource =
|
DefaultDataSource dataSource =
|
||||||
@ -144,6 +145,7 @@ public final class DefaultDataSource implements DataSource {
|
|||||||
*
|
*
|
||||||
* @param context A context.
|
* @param context A context.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public DefaultDataSource(Context context, boolean allowCrossProtocolRedirects) {
|
public DefaultDataSource(Context context, boolean allowCrossProtocolRedirects) {
|
||||||
this(
|
this(
|
||||||
context,
|
context,
|
||||||
@ -162,6 +164,7 @@ public final class DefaultDataSource implements DataSource {
|
|||||||
* @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
|
* @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
|
||||||
* to HTTPS and vice versa) are enabled when fetching remote data.
|
* to HTTPS and vice versa) are enabled when fetching remote data.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public DefaultDataSource(
|
public DefaultDataSource(
|
||||||
Context context, @Nullable String userAgent, boolean allowCrossProtocolRedirects) {
|
Context context, @Nullable String userAgent, boolean allowCrossProtocolRedirects) {
|
||||||
this(
|
this(
|
||||||
@ -185,6 +188,7 @@ public final class DefaultDataSource implements DataSource {
|
|||||||
* @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
|
* @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
|
||||||
* to HTTPS and vice versa) are enabled when fetching remote data.
|
* to HTTPS and vice versa) are enabled when fetching remote data.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public DefaultDataSource(
|
public DefaultDataSource(
|
||||||
Context context,
|
Context context,
|
||||||
@Nullable String userAgent,
|
@Nullable String userAgent,
|
||||||
@ -209,12 +213,14 @@ public final class DefaultDataSource implements DataSource {
|
|||||||
* @param baseDataSource A {@link DataSource} to use for URI schemes other than file, asset and
|
* @param baseDataSource A {@link DataSource} to use for URI schemes other than file, asset and
|
||||||
* content. This {@link DataSource} should normally support at least http(s).
|
* content. This {@link DataSource} should normally support at least http(s).
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public DefaultDataSource(Context context, DataSource baseDataSource) {
|
public DefaultDataSource(Context context, DataSource baseDataSource) {
|
||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
this.baseDataSource = Assertions.checkNotNull(baseDataSource);
|
this.baseDataSource = Assertions.checkNotNull(baseDataSource);
|
||||||
transferListeners = new ArrayList<>();
|
transferListeners = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void addTransferListener(TransferListener transferListener) {
|
public void addTransferListener(TransferListener transferListener) {
|
||||||
Assertions.checkNotNull(transferListener);
|
Assertions.checkNotNull(transferListener);
|
||||||
@ -229,6 +235,7 @@ public final class DefaultDataSource implements DataSource {
|
|||||||
maybeAddListenerToDataSource(rawResourceDataSource, transferListener);
|
maybeAddListenerToDataSource(rawResourceDataSource, transferListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public long open(DataSpec dataSpec) throws IOException {
|
public long open(DataSpec dataSpec) throws IOException {
|
||||||
Assertions.checkState(dataSource == null);
|
Assertions.checkState(dataSource == null);
|
||||||
@ -260,22 +267,26 @@ public final class DefaultDataSource implements DataSource {
|
|||||||
return dataSource.open(dataSpec);
|
return dataSource.open(dataSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] buffer, int offset, int length) throws IOException {
|
public int read(byte[] buffer, int offset, int length) throws IOException {
|
||||||
return Assertions.checkNotNull(dataSource).read(buffer, offset, length);
|
return Assertions.checkNotNull(dataSource).read(buffer, offset, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Uri getUri() {
|
public Uri getUri() {
|
||||||
return dataSource == null ? null : dataSource.getUri();
|
return dataSource == null ? null : dataSource.getUri();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> getResponseHeaders() {
|
public Map<String, List<String>> getResponseHeaders() {
|
||||||
return dataSource == null ? Collections.emptyMap() : dataSource.getResponseHeaders();
|
return dataSource == null ? Collections.emptyMap() : dataSource.getResponseHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
if (dataSource != null) {
|
if (dataSource != null) {
|
||||||
|
@ -60,7 +60,6 @@ import java.util.zip.GZIPInputStream;
|
|||||||
* priority) the {@code dataSpec}, {@link #setRequestProperty} and the default properties that can
|
* priority) the {@code dataSpec}, {@link #setRequestProperty} and the default properties that can
|
||||||
* be passed to {@link HttpDataSource.Factory#setDefaultRequestProperties(Map)}.
|
* be passed to {@link HttpDataSource.Factory#setDefaultRequestProperties(Map)}.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
|
||||||
public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSource {
|
public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSource {
|
||||||
|
|
||||||
/** {@link DataSource.Factory} for {@link DefaultHttpDataSource} instances. */
|
/** {@link DataSource.Factory} for {@link DefaultHttpDataSource} instances. */
|
||||||
@ -83,6 +82,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS;
|
readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public final Factory setDefaultRequestProperties(Map<String, String> defaultRequestProperties) {
|
public final Factory setDefaultRequestProperties(Map<String, String> defaultRequestProperties) {
|
||||||
this.defaultRequestProperties.clearAndSet(defaultRequestProperties);
|
this.defaultRequestProperties.clearAndSet(defaultRequestProperties);
|
||||||
@ -99,6 +99,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
* agent of the underlying platform.
|
* agent of the underlying platform.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setUserAgent(@Nullable String userAgent) {
|
public Factory setUserAgent(@Nullable String userAgent) {
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
return this;
|
return this;
|
||||||
@ -112,6 +113,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
* @param connectTimeoutMs The connect timeout, in milliseconds, that will be used.
|
* @param connectTimeoutMs The connect timeout, in milliseconds, that will be used.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setConnectTimeoutMs(int connectTimeoutMs) {
|
public Factory setConnectTimeoutMs(int connectTimeoutMs) {
|
||||||
this.connectTimeoutMs = connectTimeoutMs;
|
this.connectTimeoutMs = connectTimeoutMs;
|
||||||
return this;
|
return this;
|
||||||
@ -125,6 +127,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
* @param readTimeoutMs The connect timeout, in milliseconds, that will be used.
|
* @param readTimeoutMs The connect timeout, in milliseconds, that will be used.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setReadTimeoutMs(int readTimeoutMs) {
|
public Factory setReadTimeoutMs(int readTimeoutMs) {
|
||||||
this.readTimeoutMs = readTimeoutMs;
|
this.readTimeoutMs = readTimeoutMs;
|
||||||
return this;
|
return this;
|
||||||
@ -138,6 +141,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
* @param allowCrossProtocolRedirects Whether to allow cross protocol redirects.
|
* @param allowCrossProtocolRedirects Whether to allow cross protocol redirects.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setAllowCrossProtocolRedirects(boolean allowCrossProtocolRedirects) {
|
public Factory setAllowCrossProtocolRedirects(boolean allowCrossProtocolRedirects) {
|
||||||
this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
|
this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
|
||||||
return this;
|
return this;
|
||||||
@ -154,6 +158,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
* predicate that was previously set.
|
* predicate that was previously set.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
|
public Factory setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
|
||||||
this.contentTypePredicate = contentTypePredicate;
|
this.contentTypePredicate = contentTypePredicate;
|
||||||
return this;
|
return this;
|
||||||
@ -169,6 +174,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
* @param transferListener The listener that will be used.
|
* @param transferListener The listener that will be used.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setTransferListener(@Nullable TransferListener transferListener) {
|
public Factory setTransferListener(@Nullable TransferListener transferListener) {
|
||||||
this.transferListener = transferListener;
|
this.transferListener = transferListener;
|
||||||
return this;
|
return this;
|
||||||
@ -178,11 +184,13 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
* Sets whether we should keep the POST method and body when we have HTTP 302 redirects for a
|
* Sets whether we should keep the POST method and body when we have HTTP 302 redirects for a
|
||||||
* POST request.
|
* POST request.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setKeepPostFor302Redirects(boolean keepPostFor302Redirects) {
|
public Factory setKeepPostFor302Redirects(boolean keepPostFor302Redirects) {
|
||||||
this.keepPostFor302Redirects = keepPostFor302Redirects;
|
this.keepPostFor302Redirects = keepPostFor302Redirects;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public DefaultHttpDataSource createDataSource() {
|
public DefaultHttpDataSource createDataSource() {
|
||||||
DefaultHttpDataSource dataSource =
|
DefaultHttpDataSource dataSource =
|
||||||
@ -202,9 +210,9 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** The default connection timeout, in milliseconds. */
|
/** The default connection timeout, in milliseconds. */
|
||||||
public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 8 * 1000;
|
@UnstableApi public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 8 * 1000;
|
||||||
/** The default read timeout, in milliseconds. */
|
/** The default read timeout, in milliseconds. */
|
||||||
public static final int DEFAULT_READ_TIMEOUT_MILLIS = 8 * 1000;
|
@UnstableApi public static final int DEFAULT_READ_TIMEOUT_MILLIS = 8 * 1000;
|
||||||
|
|
||||||
private static final String TAG = "DefaultHttpDataSource";
|
private static final String TAG = "DefaultHttpDataSource";
|
||||||
private static final int MAX_REDIRECTS = 20; // Same limit as okhttp.
|
private static final int MAX_REDIRECTS = 20; // Same limit as okhttp.
|
||||||
@ -232,6 +240,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
/**
|
/**
|
||||||
* @deprecated Use {@link DefaultHttpDataSource.Factory} instead.
|
* @deprecated Use {@link DefaultHttpDataSource.Factory} instead.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public DefaultHttpDataSource() {
|
public DefaultHttpDataSource() {
|
||||||
@ -241,6 +250,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
/**
|
/**
|
||||||
* @deprecated Use {@link DefaultHttpDataSource.Factory} instead.
|
* @deprecated Use {@link DefaultHttpDataSource.Factory} instead.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public DefaultHttpDataSource(@Nullable String userAgent) {
|
public DefaultHttpDataSource(@Nullable String userAgent) {
|
||||||
@ -250,6 +260,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
/**
|
/**
|
||||||
* @deprecated Use {@link DefaultHttpDataSource.Factory} instead.
|
* @deprecated Use {@link DefaultHttpDataSource.Factory} instead.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public DefaultHttpDataSource(
|
public DefaultHttpDataSource(
|
||||||
@ -265,6 +276,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
/**
|
/**
|
||||||
* @deprecated Use {@link DefaultHttpDataSource.Factory} instead.
|
* @deprecated Use {@link DefaultHttpDataSource.Factory} instead.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public DefaultHttpDataSource(
|
public DefaultHttpDataSource(
|
||||||
@Nullable String userAgent,
|
@Nullable String userAgent,
|
||||||
@ -305,22 +317,26 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
* @deprecated Use {@link DefaultHttpDataSource.Factory#setContentTypePredicate(Predicate)}
|
* @deprecated Use {@link DefaultHttpDataSource.Factory#setContentTypePredicate(Predicate)}
|
||||||
* instead.
|
* instead.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
|
public void setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
|
||||||
this.contentTypePredicate = contentTypePredicate;
|
this.contentTypePredicate = contentTypePredicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Uri getUri() {
|
public Uri getUri() {
|
||||||
return connection == null ? null : Uri.parse(connection.getURL().toString());
|
return connection == null ? null : Uri.parse(connection.getURL().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public int getResponseCode() {
|
public int getResponseCode() {
|
||||||
return connection == null || responseCode <= 0 ? -1 : responseCode;
|
return connection == null || responseCode <= 0 ? -1 : responseCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> getResponseHeaders() {
|
public Map<String, List<String>> getResponseHeaders() {
|
||||||
if (connection == null) {
|
if (connection == null) {
|
||||||
@ -337,6 +353,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
return new NullFilteringHeadersMap(connection.getHeaderFields());
|
return new NullFilteringHeadersMap(connection.getHeaderFields());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void setRequestProperty(String name, String value) {
|
public void setRequestProperty(String name, String value) {
|
||||||
checkNotNull(name);
|
checkNotNull(name);
|
||||||
@ -344,18 +361,21 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
requestProperties.set(name, value);
|
requestProperties.set(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void clearRequestProperty(String name) {
|
public void clearRequestProperty(String name) {
|
||||||
checkNotNull(name);
|
checkNotNull(name);
|
||||||
requestProperties.remove(name);
|
requestProperties.remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void clearAllRequestProperties() {
|
public void clearAllRequestProperties() {
|
||||||
requestProperties.clear();
|
requestProperties.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Opens the source to read the specified data. */
|
/** Opens the source to read the specified data. */
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public long open(DataSpec dataSpec) throws HttpDataSourceException {
|
public long open(DataSpec dataSpec) throws HttpDataSourceException {
|
||||||
this.dataSpec = dataSpec;
|
this.dataSpec = dataSpec;
|
||||||
@ -474,6 +494,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
return bytesToRead;
|
return bytesToRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] buffer, int offset, int length) throws HttpDataSourceException {
|
public int read(byte[] buffer, int offset, int length) throws HttpDataSourceException {
|
||||||
try {
|
try {
|
||||||
@ -484,6 +505,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void close() throws HttpDataSourceException {
|
public void close() throws HttpDataSourceException {
|
||||||
try {
|
try {
|
||||||
|
@ -172,6 +172,7 @@ public interface HttpDataSource extends DataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** A {@link Predicate} that rejects content types often used for pay-walls. */
|
/** A {@link Predicate} that rejects content types often used for pay-walls. */
|
||||||
|
@UnstableApi
|
||||||
Predicate<String> REJECT_PAYWALL_TYPES =
|
Predicate<String> REJECT_PAYWALL_TYPES =
|
||||||
contentType -> {
|
contentType -> {
|
||||||
if (contentType == null) {
|
if (contentType == null) {
|
||||||
|
@ -38,8 +38,9 @@ If your application only needs to play http(s) content, using the Cronet
|
|||||||
extension is as simple as updating `DataSource.Factory` instantiations in your
|
extension is as simple as updating `DataSource.Factory` instantiations in your
|
||||||
application code to use `CronetDataSource.Factory`. If your application also
|
application code to use `CronetDataSource.Factory`. If your application also
|
||||||
needs to play non-http(s) content such as local files, use:
|
needs to play non-http(s) content such as local files, use:
|
||||||
|
|
||||||
```
|
```
|
||||||
new DefaultDataSourceFactory(
|
new DefaultDataSource.Factory(
|
||||||
...
|
...
|
||||||
/* baseDataSourceFactory= */ new CronetDataSource.Factory(...) );
|
/* baseDataSourceFactory= */ new CronetDataSource.Factory(...) );
|
||||||
```
|
```
|
||||||
|
@ -68,7 +68,6 @@ import org.chromium.net.UrlResponseInfo;
|
|||||||
* priority) the {@code dataSpec}, {@link #setRequestProperty} and the default parameters used to
|
* priority) the {@code dataSpec}, {@link #setRequestProperty} and the default parameters used to
|
||||||
* construct the instance.
|
* construct the instance.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
|
||||||
public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@ -132,6 +131,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* CronetEngine}, or {@link DefaultHttpDataSource} for cases where {@link
|
* CronetEngine}, or {@link DefaultHttpDataSource} for cases where {@link
|
||||||
* CronetEngineWrapper#getCronetEngine()} would have returned {@code null}.
|
* CronetEngineWrapper#getCronetEngine()} would have returned {@code null}.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Factory(CronetEngineWrapper cronetEngineWrapper, Executor executor) {
|
public Factory(CronetEngineWrapper cronetEngineWrapper, Executor executor) {
|
||||||
this.cronetEngine = cronetEngineWrapper.getCronetEngine();
|
this.cronetEngine = cronetEngineWrapper.getCronetEngine();
|
||||||
@ -142,6 +142,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS;
|
readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public final Factory setDefaultRequestProperties(Map<String, String> defaultRequestProperties) {
|
public final Factory setDefaultRequestProperties(Map<String, String> defaultRequestProperties) {
|
||||||
this.defaultRequestProperties.clearAndSet(defaultRequestProperties);
|
this.defaultRequestProperties.clearAndSet(defaultRequestProperties);
|
||||||
@ -161,6 +162,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* agent of the underlying {@link CronetEngine}.
|
* agent of the underlying {@link CronetEngine}.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setUserAgent(@Nullable String userAgent) {
|
public Factory setUserAgent(@Nullable String userAgent) {
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
if (internalFallbackFactory != null) {
|
if (internalFallbackFactory != null) {
|
||||||
@ -179,6 +181,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* UrlRequest.Builder#REQUEST_PRIORITY_*} constants.
|
* UrlRequest.Builder#REQUEST_PRIORITY_*} constants.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setRequestPriority(int requestPriority) {
|
public Factory setRequestPriority(int requestPriority) {
|
||||||
this.requestPriority = requestPriority;
|
this.requestPriority = requestPriority;
|
||||||
return this;
|
return this;
|
||||||
@ -192,6 +195,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* @param connectTimeoutMs The connect timeout, in milliseconds, that will be used.
|
* @param connectTimeoutMs The connect timeout, in milliseconds, that will be used.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setConnectionTimeoutMs(int connectTimeoutMs) {
|
public Factory setConnectionTimeoutMs(int connectTimeoutMs) {
|
||||||
this.connectTimeoutMs = connectTimeoutMs;
|
this.connectTimeoutMs = connectTimeoutMs;
|
||||||
if (internalFallbackFactory != null) {
|
if (internalFallbackFactory != null) {
|
||||||
@ -208,6 +212,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* @param resetTimeoutOnRedirects Whether the connect timeout is reset when a redirect occurs.
|
* @param resetTimeoutOnRedirects Whether the connect timeout is reset when a redirect occurs.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setResetTimeoutOnRedirects(boolean resetTimeoutOnRedirects) {
|
public Factory setResetTimeoutOnRedirects(boolean resetTimeoutOnRedirects) {
|
||||||
this.resetTimeoutOnRedirects = resetTimeoutOnRedirects;
|
this.resetTimeoutOnRedirects = resetTimeoutOnRedirects;
|
||||||
return this;
|
return this;
|
||||||
@ -223,6 +228,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* to the redirect url in the "Cookie" header.
|
* to the redirect url in the "Cookie" header.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setHandleSetCookieRequests(boolean handleSetCookieRequests) {
|
public Factory setHandleSetCookieRequests(boolean handleSetCookieRequests) {
|
||||||
this.handleSetCookieRequests = handleSetCookieRequests;
|
this.handleSetCookieRequests = handleSetCookieRequests;
|
||||||
return this;
|
return this;
|
||||||
@ -236,6 +242,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* @param readTimeoutMs The connect timeout, in milliseconds, that will be used.
|
* @param readTimeoutMs The connect timeout, in milliseconds, that will be used.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setReadTimeoutMs(int readTimeoutMs) {
|
public Factory setReadTimeoutMs(int readTimeoutMs) {
|
||||||
this.readTimeoutMs = readTimeoutMs;
|
this.readTimeoutMs = readTimeoutMs;
|
||||||
if (internalFallbackFactory != null) {
|
if (internalFallbackFactory != null) {
|
||||||
@ -254,6 +261,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* predicate that was previously set.
|
* predicate that was previously set.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
|
public Factory setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
|
||||||
this.contentTypePredicate = contentTypePredicate;
|
this.contentTypePredicate = contentTypePredicate;
|
||||||
if (internalFallbackFactory != null) {
|
if (internalFallbackFactory != null) {
|
||||||
@ -266,6 +274,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* Sets whether we should keep the POST method and body when we have HTTP 302 redirects for a
|
* Sets whether we should keep the POST method and body when we have HTTP 302 redirects for a
|
||||||
* POST request.
|
* POST request.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setKeepPostFor302Redirects(boolean keepPostFor302Redirects) {
|
public Factory setKeepPostFor302Redirects(boolean keepPostFor302Redirects) {
|
||||||
this.keepPostFor302Redirects = keepPostFor302Redirects;
|
this.keepPostFor302Redirects = keepPostFor302Redirects;
|
||||||
if (internalFallbackFactory != null) {
|
if (internalFallbackFactory != null) {
|
||||||
@ -284,6 +293,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* @param transferListener The listener that will be used.
|
* @param transferListener The listener that will be used.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setTransferListener(@Nullable TransferListener transferListener) {
|
public Factory setTransferListener(@Nullable TransferListener transferListener) {
|
||||||
this.transferListener = transferListener;
|
this.transferListener = transferListener;
|
||||||
if (internalFallbackFactory != null) {
|
if (internalFallbackFactory != null) {
|
||||||
@ -303,12 +313,14 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* @deprecated Do not use {@link CronetDataSource} or its factory in cases where a suitable
|
* @deprecated Do not use {@link CronetDataSource} or its factory in cases where a suitable
|
||||||
* {@link CronetEngine} is not available. Use the fallback factory directly in such cases.
|
* {@link CronetEngine} is not available. Use the fallback factory directly in such cases.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Factory setFallbackFactory(@Nullable HttpDataSource.Factory fallbackFactory) {
|
public Factory setFallbackFactory(@Nullable HttpDataSource.Factory fallbackFactory) {
|
||||||
this.fallbackFactory = fallbackFactory;
|
this.fallbackFactory = fallbackFactory;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public HttpDataSource createDataSource() {
|
public HttpDataSource createDataSource() {
|
||||||
if (cronetEngine == null) {
|
if (cronetEngine == null) {
|
||||||
@ -337,6 +349,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Thrown when an error is encountered when trying to open a {@link CronetDataSource}. */
|
/** Thrown when an error is encountered when trying to open a {@link CronetDataSource}. */
|
||||||
|
@UnstableApi
|
||||||
public static final class OpenException extends HttpDataSourceException {
|
public static final class OpenException extends HttpDataSourceException {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -389,9 +402,9 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** The default connection timeout, in milliseconds. */
|
/** The default connection timeout, in milliseconds. */
|
||||||
public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 8 * 1000;
|
@UnstableApi public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 8 * 1000;
|
||||||
/** The default read timeout, in milliseconds. */
|
/** The default read timeout, in milliseconds. */
|
||||||
public static final int DEFAULT_READ_TIMEOUT_MILLIS = 8 * 1000;
|
@UnstableApi public static final int DEFAULT_READ_TIMEOUT_MILLIS = 8 * 1000;
|
||||||
|
|
||||||
/* package */ final UrlRequest.Callback urlRequestCallback;
|
/* package */ final UrlRequest.Callback urlRequestCallback;
|
||||||
|
|
||||||
@ -436,6 +449,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
|
|
||||||
private volatile long currentConnectTimeoutMs;
|
private volatile long currentConnectTimeoutMs;
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
protected CronetDataSource(
|
protected CronetDataSource(
|
||||||
CronetEngine cronetEngine,
|
CronetEngine cronetEngine,
|
||||||
Executor executor,
|
Executor executor,
|
||||||
@ -473,6 +487,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* @param contentTypePredicate The content type {@link Predicate}, or {@code null} to clear a
|
* @param contentTypePredicate The content type {@link Predicate}, or {@code null} to clear a
|
||||||
* predicate that was previously set.
|
* predicate that was previously set.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
|
public void setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
|
||||||
this.contentTypePredicate = contentTypePredicate;
|
this.contentTypePredicate = contentTypePredicate;
|
||||||
@ -480,21 +495,25 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
|
|
||||||
// HttpDataSource implementation.
|
// HttpDataSource implementation.
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void setRequestProperty(String name, String value) {
|
public void setRequestProperty(String name, String value) {
|
||||||
requestProperties.set(name, value);
|
requestProperties.set(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void clearRequestProperty(String name) {
|
public void clearRequestProperty(String name) {
|
||||||
requestProperties.remove(name);
|
requestProperties.remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void clearAllRequestProperties() {
|
public void clearAllRequestProperties() {
|
||||||
requestProperties.clear();
|
requestProperties.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public int getResponseCode() {
|
public int getResponseCode() {
|
||||||
return responseInfo == null || responseInfo.getHttpStatusCode() <= 0
|
return responseInfo == null || responseInfo.getHttpStatusCode() <= 0
|
||||||
@ -502,17 +521,20 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
: responseInfo.getHttpStatusCode();
|
: responseInfo.getHttpStatusCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> getResponseHeaders() {
|
public Map<String, List<String>> getResponseHeaders() {
|
||||||
return responseInfo == null ? Collections.emptyMap() : responseInfo.getAllHeaders();
|
return responseInfo == null ? Collections.emptyMap() : responseInfo.getAllHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Uri getUri() {
|
public Uri getUri() {
|
||||||
return responseInfo == null ? null : Uri.parse(responseInfo.getUrl());
|
return responseInfo == null ? null : Uri.parse(responseInfo.getUrl());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public long open(DataSpec dataSpec) throws HttpDataSourceException {
|
public long open(DataSpec dataSpec) throws HttpDataSourceException {
|
||||||
Assertions.checkNotNull(dataSpec);
|
Assertions.checkNotNull(dataSpec);
|
||||||
@ -644,6 +666,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
return bytesRemaining;
|
return bytesRemaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] buffer, int offset, int length) throws HttpDataSourceException {
|
public int read(byte[] buffer, int offset, int length) throws HttpDataSourceException {
|
||||||
Assertions.checkState(opened);
|
Assertions.checkState(opened);
|
||||||
@ -715,6 +738,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* @throws HttpDataSourceException If an error occurs reading from the source.
|
* @throws HttpDataSourceException If an error occurs reading from the source.
|
||||||
* @throws IllegalArgumentException If {@code buffer} is not a direct ByteBuffer.
|
* @throws IllegalArgumentException If {@code buffer} is not a direct ByteBuffer.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public int read(ByteBuffer buffer) throws HttpDataSourceException {
|
public int read(ByteBuffer buffer) throws HttpDataSourceException {
|
||||||
Assertions.checkState(opened);
|
Assertions.checkState(opened);
|
||||||
|
|
||||||
@ -759,6 +783,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
return bytesRead;
|
return bytesRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public synchronized void close() {
|
public synchronized void close() {
|
||||||
if (currentUrlRequest != null) {
|
if (currentUrlRequest != null) {
|
||||||
@ -779,17 +804,20 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Returns current {@link UrlRequest}. May be null if the data source is not opened. */
|
/** Returns current {@link UrlRequest}. May be null if the data source is not opened. */
|
||||||
|
@UnstableApi
|
||||||
@Nullable
|
@Nullable
|
||||||
protected UrlRequest getCurrentUrlRequest() {
|
protected UrlRequest getCurrentUrlRequest() {
|
||||||
return currentUrlRequest;
|
return currentUrlRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns current {@link UrlResponseInfo}. May be null if the data source is not opened. */
|
/** Returns current {@link UrlResponseInfo}. May be null if the data source is not opened. */
|
||||||
|
@UnstableApi
|
||||||
@Nullable
|
@Nullable
|
||||||
protected UrlResponseInfo getCurrentUrlResponseInfo() {
|
protected UrlResponseInfo getCurrentUrlResponseInfo() {
|
||||||
return responseInfo;
|
return responseInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
protected UrlRequest.Builder buildRequestBuilder(DataSpec dataSpec) throws IOException {
|
protected UrlRequest.Builder buildRequestBuilder(DataSpec dataSpec) throws IOException {
|
||||||
UrlRequest.Builder requestBuilder =
|
UrlRequest.Builder requestBuilder =
|
||||||
cronetEngine
|
cronetEngine
|
||||||
|
@ -31,7 +31,6 @@ import org.chromium.net.CronetEngine;
|
|||||||
import org.chromium.net.CronetProvider;
|
import org.chromium.net.CronetProvider;
|
||||||
|
|
||||||
/** Cronet utility methods. */
|
/** Cronet utility methods. */
|
||||||
@UnstableApi
|
|
||||||
public final class CronetUtil {
|
public final class CronetUtil {
|
||||||
|
|
||||||
private static final String TAG = "CronetUtil";
|
private static final String TAG = "CronetUtil";
|
||||||
@ -77,6 +76,7 @@ public final class CronetUtil {
|
|||||||
* over Cronet Embedded, if both are available.
|
* over Cronet Embedded, if both are available.
|
||||||
* @return The {@link CronetEngine}, or {@code null} if no suitable engine could be built.
|
* @return The {@link CronetEngine}, or {@code null} if no suitable engine could be built.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
@Nullable
|
@Nullable
|
||||||
public static CronetEngine buildCronetEngine(
|
public static CronetEngine buildCronetEngine(
|
||||||
Context context, @Nullable String userAgent, boolean preferGooglePlayServices) {
|
Context context, @Nullable String userAgent, boolean preferGooglePlayServices) {
|
||||||
|
@ -60,7 +60,6 @@ import okhttp3.ResponseBody;
|
|||||||
* priority) the {@code dataSpec}, {@link #setRequestProperty} and the default parameters used to
|
* priority) the {@code dataSpec}, {@link #setRequestProperty} and the default parameters used to
|
||||||
* construct the instance.
|
* construct the instance.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
|
||||||
public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@ -89,6 +88,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
defaultRequestProperties = new RequestProperties();
|
defaultRequestProperties = new RequestProperties();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public final Factory setDefaultRequestProperties(Map<String, String> defaultRequestProperties) {
|
public final Factory setDefaultRequestProperties(Map<String, String> defaultRequestProperties) {
|
||||||
this.defaultRequestProperties.clearAndSet(defaultRequestProperties);
|
this.defaultRequestProperties.clearAndSet(defaultRequestProperties);
|
||||||
@ -105,6 +105,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* agent of the underlying {@link OkHttpClient}.
|
* agent of the underlying {@link OkHttpClient}.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setUserAgent(@Nullable String userAgent) {
|
public Factory setUserAgent(@Nullable String userAgent) {
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
return this;
|
return this;
|
||||||
@ -118,6 +119,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* @param cacheControl The cache control that will be used.
|
* @param cacheControl The cache control that will be used.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setCacheControl(@Nullable CacheControl cacheControl) {
|
public Factory setCacheControl(@Nullable CacheControl cacheControl) {
|
||||||
this.cacheControl = cacheControl;
|
this.cacheControl = cacheControl;
|
||||||
return this;
|
return this;
|
||||||
@ -134,6 +136,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* predicate that was previously set.
|
* predicate that was previously set.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
|
public Factory setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
|
||||||
this.contentTypePredicate = contentTypePredicate;
|
this.contentTypePredicate = contentTypePredicate;
|
||||||
return this;
|
return this;
|
||||||
@ -149,11 +152,13 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* @param transferListener The listener that will be used.
|
* @param transferListener The listener that will be used.
|
||||||
* @return This factory.
|
* @return This factory.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
public Factory setTransferListener(@Nullable TransferListener transferListener) {
|
public Factory setTransferListener(@Nullable TransferListener transferListener) {
|
||||||
this.transferListener = transferListener;
|
this.transferListener = transferListener;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public OkHttpDataSource createDataSource() {
|
public OkHttpDataSource createDataSource() {
|
||||||
OkHttpDataSource dataSource =
|
OkHttpDataSource dataSource =
|
||||||
@ -185,6 +190,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* @deprecated Use {@link OkHttpDataSource.Factory} instead.
|
* @deprecated Use {@link OkHttpDataSource.Factory} instead.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
|
@UnstableApi
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public OkHttpDataSource(Call.Factory callFactory) {
|
public OkHttpDataSource(Call.Factory callFactory) {
|
||||||
this(callFactory, /* userAgent= */ null);
|
this(callFactory, /* userAgent= */ null);
|
||||||
@ -194,6 +200,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
* @deprecated Use {@link OkHttpDataSource.Factory} instead.
|
* @deprecated Use {@link OkHttpDataSource.Factory} instead.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
|
@UnstableApi
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public OkHttpDataSource(Call.Factory callFactory, @Nullable String userAgent) {
|
public OkHttpDataSource(Call.Factory callFactory, @Nullable String userAgent) {
|
||||||
this(callFactory, userAgent, /* cacheControl= */ null, /* defaultRequestProperties= */ null);
|
this(callFactory, userAgent, /* cacheControl= */ null, /* defaultRequestProperties= */ null);
|
||||||
@ -202,6 +209,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
/**
|
/**
|
||||||
* @deprecated Use {@link OkHttpDataSource.Factory} instead.
|
* @deprecated Use {@link OkHttpDataSource.Factory} instead.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public OkHttpDataSource(
|
public OkHttpDataSource(
|
||||||
Call.Factory callFactory,
|
Call.Factory callFactory,
|
||||||
@ -234,27 +242,32 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
/**
|
/**
|
||||||
* @deprecated Use {@link OkHttpDataSource.Factory#setContentTypePredicate(Predicate)} instead.
|
* @deprecated Use {@link OkHttpDataSource.Factory#setContentTypePredicate(Predicate)} instead.
|
||||||
*/
|
*/
|
||||||
|
@UnstableApi
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public void setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
|
public void setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
|
||||||
this.contentTypePredicate = contentTypePredicate;
|
this.contentTypePredicate = contentTypePredicate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Uri getUri() {
|
public Uri getUri() {
|
||||||
return response == null ? null : Uri.parse(response.request().url().toString());
|
return response == null ? null : Uri.parse(response.request().url().toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public int getResponseCode() {
|
public int getResponseCode() {
|
||||||
return response == null ? -1 : response.code();
|
return response == null ? -1 : response.code();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> getResponseHeaders() {
|
public Map<String, List<String>> getResponseHeaders() {
|
||||||
return response == null ? Collections.emptyMap() : response.headers().toMultimap();
|
return response == null ? Collections.emptyMap() : response.headers().toMultimap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void setRequestProperty(String name, String value) {
|
public void setRequestProperty(String name, String value) {
|
||||||
Assertions.checkNotNull(name);
|
Assertions.checkNotNull(name);
|
||||||
@ -262,17 +275,20 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
requestProperties.set(name, value);
|
requestProperties.set(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void clearRequestProperty(String name) {
|
public void clearRequestProperty(String name) {
|
||||||
Assertions.checkNotNull(name);
|
Assertions.checkNotNull(name);
|
||||||
requestProperties.remove(name);
|
requestProperties.remove(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void clearAllRequestProperties() {
|
public void clearAllRequestProperties() {
|
||||||
requestProperties.clear();
|
requestProperties.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public long open(DataSpec dataSpec) throws HttpDataSourceException {
|
public long open(DataSpec dataSpec) throws HttpDataSourceException {
|
||||||
this.dataSpec = dataSpec;
|
this.dataSpec = dataSpec;
|
||||||
@ -358,6 +374,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
return bytesToRead;
|
return bytesToRead;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public int read(byte[] buffer, int offset, int length) throws HttpDataSourceException {
|
public int read(byte[] buffer, int offset, int length) throws HttpDataSourceException {
|
||||||
try {
|
try {
|
||||||
@ -368,6 +385,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UnstableApi
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
if (opened) {
|
if (opened) {
|
||||||
|
@ -39,7 +39,7 @@ injected from application code.
|
|||||||
|
|
||||||
`DefaultDataSource` will automatically use the RTMP extension whenever it's
|
`DefaultDataSource` will automatically use the RTMP extension whenever it's
|
||||||
available. Hence if your application is using `DefaultDataSource` or
|
available. Hence if your application is using `DefaultDataSource` or
|
||||||
`DefaultDataSourceFactory`, adding support for RTMP streams is as simple as
|
`DefaultDataSource.Factory`, adding support for RTMP streams is as simple as
|
||||||
adding a dependency to the RTMP extension as described above. No changes to your
|
adding a dependency to the RTMP extension as described above. No changes to your
|
||||||
application code are required. Alternatively, if you know that your application
|
application code are required. Alternatively, if you know that your application
|
||||||
doesn't need to handle any other protocols, you can update any
|
doesn't need to handle any other protocols, you can update any
|
||||||
|
@ -17,7 +17,8 @@ apply from: "$gradle.ext.androidxMediaSettingsDir/common_library_config.gradle"
|
|||||||
// failures if ffmpeg hasn't been built according to the README instructions.
|
// failures if ffmpeg hasn't been built according to the README instructions.
|
||||||
if (project.file('src/main/jni/ffmpeg').exists()) {
|
if (project.file('src/main/jni/ffmpeg').exists()) {
|
||||||
android.externalNativeBuild.cmake.path = 'src/main/jni/CMakeLists.txt'
|
android.externalNativeBuild.cmake.path = 'src/main/jni/CMakeLists.txt'
|
||||||
android.externalNativeBuild.cmake.version = '3.7.1+'
|
// Should match cmake_minimum_required.
|
||||||
|
android.externalNativeBuild.cmake.version = '3.21.0+'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.7.1 FATAL_ERROR)
|
cmake_minimum_required(VERSION 3.21.0 FATAL_ERROR)
|
||||||
|
|
||||||
# Enable C++11 features.
|
# Enable C++11 features.
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
@ -21,11 +21,11 @@ import static java.lang.Math.min;
|
|||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
import androidx.media3.exoplayer.upstream.Allocator;
|
import androidx.media3.exoplayer.upstream.Allocator;
|
||||||
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
||||||
|
@ -38,8 +38,6 @@ import androidx.media3.common.MediaItem;
|
|||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
import androidx.media3.common.PriorityTaskManager;
|
import androidx.media3.common.PriorityTaskManager;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TrackSelectionArray;
|
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
import androidx.media3.common.VideoSize;
|
import androidx.media3.common.VideoSize;
|
||||||
import androidx.media3.common.text.Cue;
|
import androidx.media3.common.text.Cue;
|
||||||
@ -57,8 +55,10 @@ import androidx.media3.exoplayer.metadata.MetadataRenderer;
|
|||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
import androidx.media3.exoplayer.source.ShuffleOrder;
|
import androidx.media3.exoplayer.source.ShuffleOrder;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.text.TextRenderer;
|
import androidx.media3.exoplayer.text.TextRenderer;
|
||||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
|
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
|
||||||
|
import androidx.media3.exoplayer.trackselection.TrackSelectionArray;
|
||||||
import androidx.media3.exoplayer.trackselection.TrackSelector;
|
import androidx.media3.exoplayer.trackselection.TrackSelector;
|
||||||
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
||||||
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
|
import androidx.media3.exoplayer.upstream.DefaultBandwidthMeter;
|
||||||
|
@ -69,8 +69,6 @@ import androidx.media3.common.Player;
|
|||||||
import androidx.media3.common.PriorityTaskManager;
|
import androidx.media3.common.PriorityTaskManager;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TrackSelectionArray;
|
|
||||||
import androidx.media3.common.TrackSelectionParameters;
|
import androidx.media3.common.TrackSelectionParameters;
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
import androidx.media3.common.VideoSize;
|
import androidx.media3.common.VideoSize;
|
||||||
@ -93,8 +91,10 @@ import androidx.media3.exoplayer.metadata.MetadataOutput;
|
|||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
import androidx.media3.exoplayer.source.ShuffleOrder;
|
import androidx.media3.exoplayer.source.ShuffleOrder;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.text.TextOutput;
|
import androidx.media3.exoplayer.text.TextOutput;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
|
import androidx.media3.exoplayer.trackselection.TrackSelectionArray;
|
||||||
import androidx.media3.exoplayer.trackselection.TrackSelector;
|
import androidx.media3.exoplayer.trackselection.TrackSelector;
|
||||||
import androidx.media3.exoplayer.trackselection.TrackSelectorResult;
|
import androidx.media3.exoplayer.trackselection.TrackSelectorResult;
|
||||||
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
||||||
|
@ -44,7 +44,6 @@ import androidx.media3.common.Player.PlayWhenReadyChangeReason;
|
|||||||
import androidx.media3.common.Player.PlaybackSuppressionReason;
|
import androidx.media3.common.Player.PlaybackSuppressionReason;
|
||||||
import androidx.media3.common.Player.RepeatMode;
|
import androidx.media3.common.Player.RepeatMode;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.Clock;
|
import androidx.media3.common.util.Clock;
|
||||||
import androidx.media3.common.util.HandlerWrapper;
|
import androidx.media3.common.util.HandlerWrapper;
|
||||||
@ -62,6 +61,7 @@ import androidx.media3.exoplayer.source.MediaPeriod;
|
|||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
import androidx.media3.exoplayer.source.SampleStream;
|
import androidx.media3.exoplayer.source.SampleStream;
|
||||||
import androidx.media3.exoplayer.source.ShuffleOrder;
|
import androidx.media3.exoplayer.source.ShuffleOrder;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.text.TextRenderer;
|
import androidx.media3.exoplayer.text.TextRenderer;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
import androidx.media3.exoplayer.trackselection.TrackSelector;
|
import androidx.media3.exoplayer.trackselection.TrackSelector;
|
||||||
|
@ -18,8 +18,8 @@ package androidx.media3.exoplayer;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
import androidx.media3.exoplayer.upstream.Allocator;
|
import androidx.media3.exoplayer.upstream.Allocator;
|
||||||
|
|
||||||
|
@ -21,7 +21,6 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.exoplayer.source.ClippingMediaPeriod;
|
import androidx.media3.exoplayer.source.ClippingMediaPeriod;
|
||||||
@ -29,6 +28,7 @@ import androidx.media3.exoplayer.source.EmptySampleStream;
|
|||||||
import androidx.media3.exoplayer.source.MediaPeriod;
|
import androidx.media3.exoplayer.source.MediaPeriod;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
import androidx.media3.exoplayer.source.SampleStream;
|
import androidx.media3.exoplayer.source.SampleStream;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
import androidx.media3.exoplayer.trackselection.TrackSelector;
|
import androidx.media3.exoplayer.trackselection.TrackSelector;
|
||||||
import androidx.media3.exoplayer.trackselection.TrackSelectorResult;
|
import androidx.media3.exoplayer.trackselection.TrackSelectorResult;
|
||||||
|
@ -26,7 +26,6 @@ import androidx.annotation.VisibleForTesting;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Clock;
|
import androidx.media3.common.util.Clock;
|
||||||
import androidx.media3.common.util.HandlerWrapper;
|
import androidx.media3.common.util.HandlerWrapper;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
@ -34,6 +33,7 @@ import androidx.media3.exoplayer.analytics.PlayerId;
|
|||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||||
import androidx.media3.exoplayer.source.MediaPeriod;
|
import androidx.media3.exoplayer.source.MediaPeriod;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.upstream.Allocator;
|
import androidx.media3.exoplayer.upstream.Allocator;
|
||||||
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
||||||
import androidx.media3.extractor.DefaultExtractorsFactory;
|
import androidx.media3.extractor.DefaultExtractorsFactory;
|
||||||
@ -157,7 +157,7 @@ public final class MetadataRetriever {
|
|||||||
mediaPeriod.maybeThrowPrepareError();
|
mediaPeriod.maybeThrowPrepareError();
|
||||||
}
|
}
|
||||||
mediaSourceHandler.sendEmptyMessageDelayed(
|
mediaSourceHandler.sendEmptyMessageDelayed(
|
||||||
MESSAGE_CHECK_FOR_FAILURE, /* delayMillis= */ ERROR_POLL_INTERVAL_MS);
|
MESSAGE_CHECK_FOR_FAILURE, /* delayMs= */ ERROR_POLL_INTERVAL_MS);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
trackGroupsFuture.setException(e);
|
trackGroupsFuture.setException(e);
|
||||||
mediaSourceHandler.obtainMessage(MESSAGE_RELEASE).sendToTarget();
|
mediaSourceHandler.obtainMessage(MESSAGE_RELEASE).sendToTarget();
|
||||||
|
@ -23,8 +23,8 @@ import androidx.media3.common.PlaybackParameters;
|
|||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
import androidx.media3.common.Player.PlaybackSuppressionReason;
|
import androidx.media3.common.Player.PlaybackSuppressionReason;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.TrackSelectorResult;
|
import androidx.media3.exoplayer.trackselection.TrackSelectorResult;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -35,8 +35,6 @@ import androidx.media3.common.MediaMetadata;
|
|||||||
import androidx.media3.common.PlaybackParameters;
|
import androidx.media3.common.PlaybackParameters;
|
||||||
import androidx.media3.common.PriorityTaskManager;
|
import androidx.media3.common.PriorityTaskManager;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TrackSelectionArray;
|
|
||||||
import androidx.media3.common.TrackSelectionParameters;
|
import androidx.media3.common.TrackSelectionParameters;
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
import androidx.media3.common.VideoSize;
|
import androidx.media3.common.VideoSize;
|
||||||
@ -49,6 +47,8 @@ import androidx.media3.exoplayer.analytics.AnalyticsListener;
|
|||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
import androidx.media3.exoplayer.source.ShuffleOrder;
|
import androidx.media3.exoplayer.source.ShuffleOrder;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
|
import androidx.media3.exoplayer.trackselection.TrackSelectionArray;
|
||||||
import androidx.media3.exoplayer.trackselection.TrackSelector;
|
import androidx.media3.exoplayer.trackselection.TrackSelector;
|
||||||
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
||||||
import androidx.media3.exoplayer.video.VideoFrameMetadataListener;
|
import androidx.media3.exoplayer.video.VideoFrameMetadataListener;
|
||||||
|
@ -45,7 +45,6 @@ import androidx.media3.common.Player.DiscontinuityReason;
|
|||||||
import androidx.media3.common.Player.PlaybackSuppressionReason;
|
import androidx.media3.common.Player.PlaybackSuppressionReason;
|
||||||
import androidx.media3.common.Player.TimelineChangeReason;
|
import androidx.media3.common.Player.TimelineChangeReason;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackSelection;
|
|
||||||
import androidx.media3.common.TrackSelectionParameters;
|
import androidx.media3.common.TrackSelectionParameters;
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
import androidx.media3.common.VideoSize;
|
import androidx.media3.common.VideoSize;
|
||||||
@ -60,6 +59,7 @@ import androidx.media3.exoplayer.metadata.MetadataOutput;
|
|||||||
import androidx.media3.exoplayer.source.LoadEventInfo;
|
import androidx.media3.exoplayer.source.LoadEventInfo;
|
||||||
import androidx.media3.exoplayer.source.MediaLoadData;
|
import androidx.media3.exoplayer.source.MediaLoadData;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
|
import androidx.media3.exoplayer.trackselection.TrackSelection;
|
||||||
import androidx.media3.exoplayer.video.VideoDecoderOutputBufferRenderer;
|
import androidx.media3.exoplayer.video.VideoDecoderOutputBufferRenderer;
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -24,8 +24,8 @@ import androidx.annotation.RequiresApi;
|
|||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
import androidx.media3.datasource.DataSource;
|
||||||
import androidx.media3.datasource.DefaultHttpDataSource;
|
import androidx.media3.datasource.DefaultHttpDataSource;
|
||||||
import androidx.media3.datasource.HttpDataSource;
|
|
||||||
import com.google.common.primitives.Ints;
|
import com.google.common.primitives.Ints;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
@ -42,7 +42,7 @@ public final class DefaultDrmSessionManagerProvider implements DrmSessionManager
|
|||||||
@GuardedBy("lock")
|
@GuardedBy("lock")
|
||||||
private @MonotonicNonNull DrmSessionManager manager;
|
private @MonotonicNonNull DrmSessionManager manager;
|
||||||
|
|
||||||
@Nullable private HttpDataSource.Factory drmHttpDataSourceFactory;
|
@Nullable private DataSource.Factory drmHttpDataSourceFactory;
|
||||||
@Nullable private String userAgent;
|
@Nullable private String userAgent;
|
||||||
|
|
||||||
public DefaultDrmSessionManagerProvider() {
|
public DefaultDrmSessionManagerProvider() {
|
||||||
@ -50,26 +50,22 @@ public final class DefaultDrmSessionManagerProvider implements DrmSessionManager
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link HttpDataSource.Factory} to be used for creating {@link HttpMediaDrmCallback
|
* Sets the {@link DataSource.Factory} which is used to create {@link HttpMediaDrmCallback}
|
||||||
* HttpMediaDrmCallbacks} which executes key and provisioning requests over HTTP. If {@code null}
|
* instances. If {@code null} is passed a {@link DefaultHttpDataSource.Factory} is used.
|
||||||
* is passed the {@link DefaultHttpDataSource.Factory} is used.
|
|
||||||
*
|
*
|
||||||
* @param drmHttpDataSourceFactory The HTTP data source factory or {@code null} to use {@link
|
* @param drmDataSourceFactory The data source factory or {@code null} to use {@link
|
||||||
* DefaultHttpDataSource.Factory}.
|
* DefaultHttpDataSource.Factory}.
|
||||||
*/
|
*/
|
||||||
public void setDrmHttpDataSourceFactory(
|
public void setDrmHttpDataSourceFactory(@Nullable DataSource.Factory drmDataSourceFactory) {
|
||||||
@Nullable HttpDataSource.Factory drmHttpDataSourceFactory) {
|
this.drmHttpDataSourceFactory = drmDataSourceFactory;
|
||||||
this.drmHttpDataSourceFactory = drmHttpDataSourceFactory;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the optional user agent to be used for DRM requests.
|
* @deprecated Pass a custom {@link DataSource.Factory} to {@link
|
||||||
*
|
* #setDrmHttpDataSourceFactory(DataSource.Factory)} which sets the desired user agent on
|
||||||
* <p>In case a factory has been set by {@link
|
* outgoing requests.
|
||||||
* #setDrmHttpDataSourceFactory(HttpDataSource.Factory)}, this user agent is ignored.
|
|
||||||
*
|
|
||||||
* @param userAgent The user agent to be used for DRM requests.
|
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setDrmUserAgent(@Nullable String userAgent) {
|
public void setDrmUserAgent(@Nullable String userAgent) {
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
}
|
}
|
||||||
@ -94,7 +90,7 @@ public final class DefaultDrmSessionManagerProvider implements DrmSessionManager
|
|||||||
|
|
||||||
@RequiresApi(18)
|
@RequiresApi(18)
|
||||||
private DrmSessionManager createManager(MediaItem.DrmConfiguration drmConfiguration) {
|
private DrmSessionManager createManager(MediaItem.DrmConfiguration drmConfiguration) {
|
||||||
HttpDataSource.Factory dataSourceFactory =
|
DataSource.Factory dataSourceFactory =
|
||||||
drmHttpDataSourceFactory != null
|
drmHttpDataSourceFactory != null
|
||||||
? drmHttpDataSourceFactory
|
? drmHttpDataSourceFactory
|
||||||
: new DefaultHttpDataSource.Factory().setUserAgent(userAgent);
|
: new DefaultHttpDataSource.Factory().setUserAgent(userAgent);
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.exoplayer.drm;
|
package androidx.media3.exoplayer.drm;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.media.DeniedByServerException;
|
import android.media.DeniedByServerException;
|
||||||
import android.media.MediaCrypto;
|
import android.media.MediaCrypto;
|
||||||
@ -23,6 +25,7 @@ import android.media.MediaDrm;
|
|||||||
import android.media.MediaDrmException;
|
import android.media.MediaDrmException;
|
||||||
import android.media.NotProvisionedException;
|
import android.media.NotProvisionedException;
|
||||||
import android.media.UnsupportedSchemeException;
|
import android.media.UnsupportedSchemeException;
|
||||||
|
import android.media.metrics.LogSessionId;
|
||||||
import android.os.PersistableBundle;
|
import android.os.PersistableBundle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import androidx.annotation.DoNotInline;
|
import androidx.annotation.DoNotInline;
|
||||||
@ -188,7 +191,13 @@ public final class FrameworkMediaDrm implements ExoMediaDrm {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPlayerIdForSession(byte[] sessionId, PlayerId playerId) {
|
public void setPlayerIdForSession(byte[] sessionId, PlayerId playerId) {
|
||||||
// TODO(b/221032172): Implement this when CDM compatibility issues are resolved.
|
if (Util.SDK_INT >= 31) {
|
||||||
|
try {
|
||||||
|
Api31.setLogSessionIdOnMediaDrmSession(mediaDrm, sessionId, playerId);
|
||||||
|
} catch (UnsupportedOperationException e) {
|
||||||
|
Log.w(TAG, "setLogSessionId failed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return values of MediaDrm.KeyRequest.getRequestType are equal to KeyRequest.RequestType.
|
// Return values of MediaDrm.KeyRequest.getRequestType are equal to KeyRequest.RequestType.
|
||||||
@ -518,5 +527,16 @@ public final class FrameworkMediaDrm implements ExoMediaDrm {
|
|||||||
public static boolean requiresSecureDecoder(MediaDrm mediaDrm, String mimeType) {
|
public static boolean requiresSecureDecoder(MediaDrm mediaDrm, String mimeType) {
|
||||||
return mediaDrm.requiresSecureDecoder(mimeType);
|
return mediaDrm.requiresSecureDecoder(mimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DoNotInline
|
||||||
|
public static void setLogSessionIdOnMediaDrmSession(
|
||||||
|
MediaDrm mediaDrm, byte[] drmSessionId, PlayerId playerId) {
|
||||||
|
LogSessionId logSessionId = playerId.getLogSessionId();
|
||||||
|
if (!logSessionId.equals(LogSessionId.LOG_SESSION_ID_NONE)) {
|
||||||
|
MediaDrm.PlaybackComponent playbackComponent =
|
||||||
|
checkNotNull(mediaDrm.getPlaybackComponent(drmSessionId));
|
||||||
|
playbackComponent.setLogSessionId(logSessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,9 +22,9 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
import androidx.media3.datasource.DataSource;
|
||||||
import androidx.media3.datasource.DataSourceInputStream;
|
import androidx.media3.datasource.DataSourceInputStream;
|
||||||
import androidx.media3.datasource.DataSpec;
|
import androidx.media3.datasource.DataSpec;
|
||||||
import androidx.media3.datasource.HttpDataSource;
|
|
||||||
import androidx.media3.datasource.HttpDataSource.InvalidResponseCodeException;
|
import androidx.media3.datasource.HttpDataSource.InvalidResponseCodeException;
|
||||||
import androidx.media3.datasource.StatsDataSource;
|
import androidx.media3.datasource.StatsDataSource;
|
||||||
import androidx.media3.exoplayer.drm.ExoMediaDrm.KeyRequest;
|
import androidx.media3.exoplayer.drm.ExoMediaDrm.KeyRequest;
|
||||||
@ -36,41 +36,47 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/** A {@link MediaDrmCallback} that makes requests using {@link HttpDataSource} instances. */
|
/** A {@link MediaDrmCallback} that makes requests using {@link DataSource} instances. */
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class HttpMediaDrmCallback implements MediaDrmCallback {
|
public final class HttpMediaDrmCallback implements MediaDrmCallback {
|
||||||
|
|
||||||
private static final int MAX_MANUAL_REDIRECTS = 5;
|
private static final int MAX_MANUAL_REDIRECTS = 5;
|
||||||
|
|
||||||
private final HttpDataSource.Factory dataSourceFactory;
|
private final DataSource.Factory dataSourceFactory;
|
||||||
@Nullable private final String defaultLicenseUrl;
|
@Nullable private final String defaultLicenseUrl;
|
||||||
private final boolean forceDefaultLicenseUrl;
|
private final boolean forceDefaultLicenseUrl;
|
||||||
private final Map<String, String> keyRequestProperties;
|
private final Map<String, String> keyRequestProperties;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Constructs an instance.
|
||||||
|
*
|
||||||
* @param defaultLicenseUrl The default license URL. Used for key requests that do not specify
|
* @param defaultLicenseUrl The default license URL. Used for key requests that do not specify
|
||||||
* their own license URL. May be {@code null} if it's known that all key requests will specify
|
* their own license URL. May be {@code null} if it's known that all key requests will specify
|
||||||
* their own URLs.
|
* their own URLs.
|
||||||
* @param dataSourceFactory A factory from which to obtain {@link HttpDataSource} instances.
|
* @param dataSourceFactory A factory from which to obtain {@link DataSource} instances. This will
|
||||||
|
* usually be an HTTP-based {@link DataSource}.
|
||||||
*/
|
*/
|
||||||
public HttpMediaDrmCallback(
|
public HttpMediaDrmCallback(
|
||||||
@Nullable String defaultLicenseUrl, HttpDataSource.Factory dataSourceFactory) {
|
@Nullable String defaultLicenseUrl, DataSource.Factory dataSourceFactory) {
|
||||||
this(defaultLicenseUrl, /* forceDefaultLicenseUrl= */ false, dataSourceFactory);
|
this(defaultLicenseUrl, /* forceDefaultLicenseUrl= */ false, dataSourceFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Constructs an instance.
|
||||||
|
*
|
||||||
* @param defaultLicenseUrl The default license URL. Used for key requests that do not specify
|
* @param defaultLicenseUrl The default license URL. Used for key requests that do not specify
|
||||||
* their own license URL, or for all key requests if {@code forceDefaultLicenseUrl} is set to
|
* their own license URL, or for all key requests if {@code forceDefaultLicenseUrl} is set to
|
||||||
* true. May be {@code null} if {@code forceDefaultLicenseUrl} is {@code false} and if it's
|
* true. May be {@code null} if {@code forceDefaultLicenseUrl} is {@code false} and if it's
|
||||||
* known that all key requests will specify their own URLs.
|
* known that all key requests will specify their own URLs.
|
||||||
* @param forceDefaultLicenseUrl Whether to force use of {@code defaultLicenseUrl} for key
|
* @param forceDefaultLicenseUrl Whether to force use of {@code defaultLicenseUrl} for key
|
||||||
* requests that include their own license URL.
|
* requests that include their own license URL.
|
||||||
* @param dataSourceFactory A factory from which to obtain {@link HttpDataSource} instances.
|
* @param dataSourceFactory A factory from which to obtain {@link DataSource} instances. This will
|
||||||
|
* * usually be an HTTP-based {@link DataSource}.
|
||||||
*/
|
*/
|
||||||
public HttpMediaDrmCallback(
|
public HttpMediaDrmCallback(
|
||||||
@Nullable String defaultLicenseUrl,
|
@Nullable String defaultLicenseUrl,
|
||||||
boolean forceDefaultLicenseUrl,
|
boolean forceDefaultLicenseUrl,
|
||||||
HttpDataSource.Factory dataSourceFactory) {
|
DataSource.Factory dataSourceFactory) {
|
||||||
Assertions.checkArgument(!(forceDefaultLicenseUrl && TextUtils.isEmpty(defaultLicenseUrl)));
|
Assertions.checkArgument(!(forceDefaultLicenseUrl && TextUtils.isEmpty(defaultLicenseUrl)));
|
||||||
this.dataSourceFactory = dataSourceFactory;
|
this.dataSourceFactory = dataSourceFactory;
|
||||||
this.defaultLicenseUrl = defaultLicenseUrl;
|
this.defaultLicenseUrl = defaultLicenseUrl;
|
||||||
@ -156,7 +162,7 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] executePost(
|
private static byte[] executePost(
|
||||||
HttpDataSource.Factory dataSourceFactory,
|
DataSource.Factory dataSourceFactory,
|
||||||
String url,
|
String url,
|
||||||
@Nullable byte[] httpBody,
|
@Nullable byte[] httpBody,
|
||||||
Map<String, String> requestProperties)
|
Map<String, String> requestProperties)
|
||||||
|
@ -26,7 +26,7 @@ import androidx.media3.common.DrmInitData;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.datasource.HttpDataSource;
|
import androidx.media3.datasource.DataSource;
|
||||||
import androidx.media3.exoplayer.analytics.PlayerId;
|
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||||
import androidx.media3.exoplayer.drm.DefaultDrmSessionManager.Mode;
|
import androidx.media3.exoplayer.drm.DefaultDrmSessionManager.Mode;
|
||||||
import androidx.media3.exoplayer.drm.DrmSession.DrmSessionException;
|
import androidx.media3.exoplayer.drm.DrmSession.DrmSessionException;
|
||||||
@ -53,20 +53,17 @@ public final class OfflineLicenseHelper {
|
|||||||
*
|
*
|
||||||
* @param defaultLicenseUrl The default license URL. Used for key requests that do not specify
|
* @param defaultLicenseUrl The default license URL. Used for key requests that do not specify
|
||||||
* their own license URL.
|
* their own license URL.
|
||||||
* @param httpDataSourceFactory A factory from which to obtain {@link HttpDataSource} instances.
|
* @param dataSourceFactory A factory from which to obtain {@link DataSource} instances.
|
||||||
* @param eventDispatcher A {@link DrmSessionEventListener.EventDispatcher} used to distribute
|
* @param eventDispatcher A {@link DrmSessionEventListener.EventDispatcher} used to distribute
|
||||||
* DRM-related events.
|
* DRM-related events.
|
||||||
* @return A new instance which uses Widevine CDM.
|
* @return A new instance which uses Widevine CDM.
|
||||||
*/
|
*/
|
||||||
public static OfflineLicenseHelper newWidevineInstance(
|
public static OfflineLicenseHelper newWidevineInstance(
|
||||||
String defaultLicenseUrl,
|
String defaultLicenseUrl,
|
||||||
HttpDataSource.Factory httpDataSourceFactory,
|
DataSource.Factory dataSourceFactory,
|
||||||
DrmSessionEventListener.EventDispatcher eventDispatcher) {
|
DrmSessionEventListener.EventDispatcher eventDispatcher) {
|
||||||
return newWidevineInstance(
|
return newWidevineInstance(
|
||||||
defaultLicenseUrl,
|
defaultLicenseUrl, /* forceDefaultLicenseUrl= */ false, dataSourceFactory, eventDispatcher);
|
||||||
/* forceDefaultLicenseUrl= */ false,
|
|
||||||
httpDataSourceFactory,
|
|
||||||
eventDispatcher);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -77,7 +74,7 @@ public final class OfflineLicenseHelper {
|
|||||||
* their own license URL.
|
* their own license URL.
|
||||||
* @param forceDefaultLicenseUrl Whether to use {@code defaultLicenseUrl} for key requests that
|
* @param forceDefaultLicenseUrl Whether to use {@code defaultLicenseUrl} for key requests that
|
||||||
* include their own license URL.
|
* include their own license URL.
|
||||||
* @param httpDataSourceFactory A factory from which to obtain {@link HttpDataSource} instances.
|
* @param dataSourceFactory A factory from which to obtain {@link DataSource} instances.
|
||||||
* @param eventDispatcher A {@link DrmSessionEventListener.EventDispatcher} used to distribute
|
* @param eventDispatcher A {@link DrmSessionEventListener.EventDispatcher} used to distribute
|
||||||
* DRM-related events.
|
* DRM-related events.
|
||||||
* @return A new instance which uses Widevine CDM.
|
* @return A new instance which uses Widevine CDM.
|
||||||
@ -85,12 +82,12 @@ public final class OfflineLicenseHelper {
|
|||||||
public static OfflineLicenseHelper newWidevineInstance(
|
public static OfflineLicenseHelper newWidevineInstance(
|
||||||
String defaultLicenseUrl,
|
String defaultLicenseUrl,
|
||||||
boolean forceDefaultLicenseUrl,
|
boolean forceDefaultLicenseUrl,
|
||||||
HttpDataSource.Factory httpDataSourceFactory,
|
DataSource.Factory dataSourceFactory,
|
||||||
DrmSessionEventListener.EventDispatcher eventDispatcher) {
|
DrmSessionEventListener.EventDispatcher eventDispatcher) {
|
||||||
return newWidevineInstance(
|
return newWidevineInstance(
|
||||||
defaultLicenseUrl,
|
defaultLicenseUrl,
|
||||||
forceDefaultLicenseUrl,
|
forceDefaultLicenseUrl,
|
||||||
httpDataSourceFactory,
|
dataSourceFactory,
|
||||||
/* optionalKeyRequestParameters= */ null,
|
/* optionalKeyRequestParameters= */ null,
|
||||||
eventDispatcher);
|
eventDispatcher);
|
||||||
}
|
}
|
||||||
@ -113,7 +110,7 @@ public final class OfflineLicenseHelper {
|
|||||||
public static OfflineLicenseHelper newWidevineInstance(
|
public static OfflineLicenseHelper newWidevineInstance(
|
||||||
String defaultLicenseUrl,
|
String defaultLicenseUrl,
|
||||||
boolean forceDefaultLicenseUrl,
|
boolean forceDefaultLicenseUrl,
|
||||||
HttpDataSource.Factory httpDataSourceFactory,
|
DataSource.Factory dataSourceFactory,
|
||||||
@Nullable Map<String, String> optionalKeyRequestParameters,
|
@Nullable Map<String, String> optionalKeyRequestParameters,
|
||||||
DrmSessionEventListener.EventDispatcher eventDispatcher) {
|
DrmSessionEventListener.EventDispatcher eventDispatcher) {
|
||||||
return new OfflineLicenseHelper(
|
return new OfflineLicenseHelper(
|
||||||
@ -121,7 +118,7 @@ public final class OfflineLicenseHelper {
|
|||||||
.setKeyRequestParameters(optionalKeyRequestParameters)
|
.setKeyRequestParameters(optionalKeyRequestParameters)
|
||||||
.build(
|
.build(
|
||||||
new HttpMediaDrmCallback(
|
new HttpMediaDrmCallback(
|
||||||
defaultLicenseUrl, forceDefaultLicenseUrl, httpDataSourceFactory)),
|
defaultLicenseUrl, forceDefaultLicenseUrl, dataSourceFactory)),
|
||||||
eventDispatcher);
|
eventDispatcher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,8 +96,9 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter.
|
|||||||
@Override
|
@Override
|
||||||
public MediaCodecAdapter createAdapter(MediaCodecAdapter.Configuration configuration)
|
public MediaCodecAdapter createAdapter(MediaCodecAdapter.Configuration configuration)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if ((asynchronousMode == MODE_ENABLED && Util.SDK_INT >= 23)
|
if (Util.SDK_INT >= 23
|
||||||
|| (asynchronousMode == MODE_DEFAULT && Util.SDK_INT >= 31)) {
|
&& (asynchronousMode == MODE_ENABLED
|
||||||
|
|| (asynchronousMode == MODE_DEFAULT && Util.SDK_INT >= 31))) {
|
||||||
int trackType = MimeTypes.getTrackType(configuration.format.sampleMimeType);
|
int trackType = MimeTypes.getTrackType(configuration.format.sampleMimeType);
|
||||||
Log.i(
|
Log.i(
|
||||||
TAG,
|
TAG,
|
||||||
|
@ -443,6 +443,8 @@ public final class MediaCodecUtil {
|
|||||||
return "audio/x-lg-alac";
|
return "audio/x-lg-alac";
|
||||||
} else if (mimeType.equals(MimeTypes.AUDIO_FLAC) && "OMX.lge.flac.decoder".equals(name)) {
|
} else if (mimeType.equals(MimeTypes.AUDIO_FLAC) && "OMX.lge.flac.decoder".equals(name)) {
|
||||||
return "audio/x-lg-flac";
|
return "audio/x-lg-flac";
|
||||||
|
} else if (mimeType.equals(MimeTypes.AUDIO_AC3) && "OMX.lge.ac3.decoder".equals(name)) {
|
||||||
|
return "audio/lg-ac3";
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -31,7 +31,6 @@ import androidx.media3.common.MimeTypes;
|
|||||||
import androidx.media3.common.StreamKey;
|
import androidx.media3.common.StreamKey;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TrackSelectionOverride;
|
import androidx.media3.common.TrackSelectionOverride;
|
||||||
import androidx.media3.common.TrackSelectionParameters;
|
import androidx.media3.common.TrackSelectionParameters;
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
@ -52,6 +51,7 @@ import androidx.media3.exoplayer.source.MediaPeriod;
|
|||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaSourceCaller;
|
import androidx.media3.exoplayer.source.MediaSource.MediaSourceCaller;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.source.chunk.MediaChunk;
|
import androidx.media3.exoplayer.source.chunk.MediaChunk;
|
||||||
import androidx.media3.exoplayer.source.chunk.MediaChunkIterator;
|
import androidx.media3.exoplayer.source.chunk.MediaChunkIterator;
|
||||||
import androidx.media3.exoplayer.trackselection.BaseTrackSelection;
|
import androidx.media3.exoplayer.trackselection.BaseTrackSelection;
|
||||||
|
@ -19,7 +19,6 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
@ -147,7 +147,6 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
|||||||
* @param dataSourceFactory A {@link DataSource.Factory} to create {@link DataSource} instances
|
* @param dataSourceFactory A {@link DataSource.Factory} to create {@link DataSource} instances
|
||||||
* for requesting media data.
|
* for requesting media data.
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
|
||||||
public DefaultMediaSourceFactory(DataSource.Factory dataSourceFactory) {
|
public DefaultMediaSourceFactory(DataSource.Factory dataSourceFactory) {
|
||||||
this(dataSourceFactory, new DefaultExtractorsFactory());
|
this(dataSourceFactory, new DefaultExtractorsFactory());
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,6 @@ import static androidx.media3.common.util.Util.castNonNull;
|
|||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.exoplayer.SeekParameters;
|
import androidx.media3.exoplayer.SeekParameters;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
|
@ -19,7 +19,6 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.StreamKey;
|
import androidx.media3.common.StreamKey;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.exoplayer.ExoPlayer;
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
import androidx.media3.exoplayer.SeekParameters;
|
import androidx.media3.exoplayer.SeekParameters;
|
||||||
|
@ -23,7 +23,6 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.StreamKey;
|
import androidx.media3.common.StreamKey;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.decoder.DecoderInputBuffer;
|
import androidx.media3.decoder.DecoderInputBuffer;
|
||||||
import androidx.media3.exoplayer.FormatHolder;
|
import androidx.media3.exoplayer.FormatHolder;
|
||||||
|
@ -28,7 +28,6 @@ import androidx.media3.common.Metadata;
|
|||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.ParserException;
|
import androidx.media3.common.ParserException;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.ConditionVariable;
|
import androidx.media3.common.util.ConditionVariable;
|
||||||
import androidx.media3.common.util.ParsableByteArray;
|
import androidx.media3.common.util.ParsableByteArray;
|
||||||
|
@ -26,7 +26,6 @@ import androidx.media3.common.MediaItem;
|
|||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
@ -20,7 +20,6 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
@ -13,13 +13,16 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package androidx.media3.common;
|
package androidx.media3.exoplayer.source;
|
||||||
|
|
||||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.media3.common.Bundleable;
|
||||||
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.util.BundleableUtil;
|
import androidx.media3.common.util.BundleableUtil;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
@ -30,7 +33,16 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
import java.lang.annotation.Target;
|
import java.lang.annotation.Target;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** An immutable array of {@link TrackGroup}s. */
|
/**
|
||||||
|
* An immutable array of {@link TrackGroup}s.
|
||||||
|
*
|
||||||
|
* <p>This class is typically used to represent all of the tracks available in a piece of media.
|
||||||
|
* Tracks that are known to present the same content are grouped together (e.g., the same video feed
|
||||||
|
* provided at different resolutions in an adaptive stream). Tracks that are known to present
|
||||||
|
* different content are in separate track groups (e.g., an audio track will not be in the same
|
||||||
|
* group as a video track, and an audio track in one language will be in a different group to an
|
||||||
|
* audio track in another language).
|
||||||
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class TrackGroupArray implements Bundleable {
|
public final class TrackGroupArray implements Bundleable {
|
||||||
|
|
@ -36,7 +36,6 @@ import androidx.media3.common.MediaItem;
|
|||||||
import androidx.media3.common.StreamKey;
|
import androidx.media3.common.StreamKey;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.datasource.TransferListener;
|
import androidx.media3.datasource.TransferListener;
|
||||||
@ -54,6 +53,7 @@ import androidx.media3.exoplayer.source.MediaPeriod;
|
|||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
import androidx.media3.exoplayer.source.MediaSourceEventListener;
|
import androidx.media3.exoplayer.source.MediaSourceEventListener;
|
||||||
import androidx.media3.exoplayer.source.SampleStream;
|
import androidx.media3.exoplayer.source.SampleStream;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
import androidx.media3.exoplayer.upstream.Allocator;
|
import androidx.media3.exoplayer.upstream.Allocator;
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
|
@ -24,7 +24,6 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackSelection;
|
|
||||||
import androidx.media3.common.util.Clock;
|
import androidx.media3.common.util.Clock;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
@ -781,7 +780,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
|
|||||||
}
|
}
|
||||||
double[] logBitrates = new double[trackBitrates[i].length];
|
double[] logBitrates = new double[trackBitrates[i].length];
|
||||||
for (int j = 0; j < trackBitrates[i].length; j++) {
|
for (int j = 0; j < trackBitrates[i].length; j++) {
|
||||||
logBitrates[j] = trackBitrates[i][j] == Format.NO_VALUE ? 0 : Math.log(trackBitrates[i][j]);
|
logBitrates[j] =
|
||||||
|
trackBitrates[i][j] == Format.NO_VALUE ? 0 : Math.log((double) trackBitrates[i][j]);
|
||||||
}
|
}
|
||||||
double totalBitrateDiff = logBitrates[logBitrates.length - 1] - logBitrates[0];
|
double totalBitrateDiff = logBitrates[logBitrates.length - 1] - logBitrates[0];
|
||||||
for (int j = 0; j < logBitrates.length - 1; j++) {
|
for (int j = 0; j < logBitrates.length - 1; j++) {
|
||||||
|
@ -22,7 +22,6 @@ import androidx.annotation.Nullable;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackSelection;
|
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
@ -36,8 +36,6 @@ import androidx.media3.common.Format;
|
|||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TrackSelection;
|
|
||||||
import androidx.media3.common.TrackSelectionOverride;
|
import androidx.media3.common.TrackSelectionOverride;
|
||||||
import androidx.media3.common.TrackSelectionParameters;
|
import androidx.media3.common.TrackSelectionParameters;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
@ -51,6 +49,7 @@ import androidx.media3.exoplayer.RendererCapabilities.AdaptiveSupport;
|
|||||||
import androidx.media3.exoplayer.RendererCapabilities.Capabilities;
|
import androidx.media3.exoplayer.RendererCapabilities.Capabilities;
|
||||||
import androidx.media3.exoplayer.RendererConfiguration;
|
import androidx.media3.exoplayer.RendererConfiguration;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import com.google.common.collect.ComparisonChain;
|
import com.google.common.collect.ComparisonChain;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Ordering;
|
import com.google.common.collect.Ordering;
|
||||||
@ -86,11 +85,10 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
* .setMaxVideoSizeSd()
|
* .setMaxVideoSizeSd()
|
||||||
* .setPreferredAudioLanguage("de")
|
* .setPreferredAudioLanguage("de")
|
||||||
* .build());
|
* .build());
|
||||||
*
|
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*
|
*
|
||||||
* Some specialized parameters are only available in the extended {@link Parameters} class, which
|
* Some specialized parameters are only available in the extended {@link Parameters} class, which
|
||||||
* can be retrieved and modified in a similar way in this track selector:
|
* can be retrieved and modified in a similar way by calling methods directly on this class:
|
||||||
*
|
*
|
||||||
* <pre>{@code
|
* <pre>{@code
|
||||||
* defaultTrackSelector.setParameters(
|
* defaultTrackSelector.setParameters(
|
||||||
@ -98,7 +96,6 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||||||
* .buildUpon()
|
* .buildUpon()
|
||||||
* .setTunnelingEnabled(true)
|
* .setTunnelingEnabled(true)
|
||||||
* .build());
|
* .build());
|
||||||
*
|
|
||||||
* }</pre>
|
* }</pre>
|
||||||
*/
|
*/
|
||||||
@UnstableApi
|
@UnstableApi
|
||||||
|
@ -20,7 +20,6 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackSelection;
|
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
|
@ -18,7 +18,6 @@ package androidx.media3.exoplayer.trackselection;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackSelection;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.exoplayer.source.chunk.MediaChunk;
|
import androidx.media3.exoplayer.source.chunk.MediaChunk;
|
||||||
import androidx.media3.exoplayer.source.chunk.MediaChunkIterator;
|
import androidx.media3.exoplayer.source.chunk.MediaChunkIterator;
|
||||||
|
@ -31,7 +31,6 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.C.FormatSupport;
|
import androidx.media3.common.C.FormatSupport;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
@ -42,6 +41,7 @@ import androidx.media3.exoplayer.RendererCapabilities.AdaptiveSupport;
|
|||||||
import androidx.media3.exoplayer.RendererCapabilities.Capabilities;
|
import androidx.media3.exoplayer.RendererCapabilities.Capabilities;
|
||||||
import androidx.media3.exoplayer.RendererConfiguration;
|
import androidx.media3.exoplayer.RendererConfiguration;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
@ -13,11 +13,14 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package androidx.media3.common;
|
package androidx.media3.exoplayer.trackselection;
|
||||||
|
|
||||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||||
|
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
|
import androidx.media3.common.C;
|
||||||
|
import androidx.media3.common.Format;
|
||||||
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import java.lang.annotation.Documented;
|
import java.lang.annotation.Documented;
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package androidx.media3.common;
|
package androidx.media3.exoplayer.trackselection;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
@ -19,12 +19,11 @@ import android.os.SystemClock;
|
|||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TrackSelection;
|
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
import androidx.media3.common.TracksInfo.TrackGroupInfo;
|
import androidx.media3.common.TracksInfo.TrackGroupInfo;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.exoplayer.RendererCapabilities;
|
import androidx.media3.exoplayer.RendererCapabilities;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector.SelectionOverride;
|
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector.SelectionOverride;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection.Definition;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection.Definition;
|
||||||
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||||
|
@ -17,8 +17,6 @@ package androidx.media3.exoplayer.trackselection;
|
|||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TrackSelection;
|
|
||||||
import androidx.media3.common.TrackSelectionParameters;
|
import androidx.media3.common.TrackSelectionParameters;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
@ -28,6 +26,7 @@ import androidx.media3.exoplayer.Renderer;
|
|||||||
import androidx.media3.exoplayer.RendererCapabilities;
|
import androidx.media3.exoplayer.RendererCapabilities;
|
||||||
import androidx.media3.exoplayer.RendererConfiguration;
|
import androidx.media3.exoplayer.RendererConfiguration;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -286,19 +286,19 @@ public class EventLogger implements AnalyticsListener {
|
|||||||
}
|
}
|
||||||
// TODO: Replace this with an override of onMediaMetadataChanged.
|
// TODO: Replace this with an override of onMediaMetadataChanged.
|
||||||
// Log metadata for at most one of the selected tracks.
|
// Log metadata for at most one of the selected tracks.
|
||||||
for (int groupIndex = 0; groupIndex < trackGroupInfos.size(); groupIndex++) {
|
boolean loggedMetadata = false;
|
||||||
|
for (int groupIndex = 0; !loggedMetadata && groupIndex < trackGroupInfos.size(); groupIndex++) {
|
||||||
TracksInfo.TrackGroupInfo trackGroupInfo = trackGroupInfos.get(groupIndex);
|
TracksInfo.TrackGroupInfo trackGroupInfo = trackGroupInfos.get(groupIndex);
|
||||||
TrackGroup trackGroup = trackGroupInfo.getTrackGroup();
|
TrackGroup trackGroup = trackGroupInfo.getTrackGroup();
|
||||||
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
|
for (int trackIndex = 0; !loggedMetadata && trackIndex < trackGroup.length; trackIndex++) {
|
||||||
if (!trackGroupInfo.isTrackSelected(trackIndex)) {
|
if (trackGroupInfo.isTrackSelected(trackIndex)) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
@Nullable Metadata metadata = trackGroup.getFormat(trackIndex).metadata;
|
@Nullable Metadata metadata = trackGroup.getFormat(trackIndex).metadata;
|
||||||
if (metadata != null) {
|
if (metadata != null && metadata.length() > 0) {
|
||||||
logd(" Metadata [");
|
logd(" Metadata [");
|
||||||
printMetadata(metadata, " ");
|
printMetadata(metadata, " ");
|
||||||
logd(" ]");
|
logd(" ]");
|
||||||
break;
|
loggedMetadata = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,9 +18,9 @@ package androidx.media3.exoplayer;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.exoplayer.DefaultLoadControl.Builder;
|
import androidx.media3.exoplayer.DefaultLoadControl.Builder;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
import androidx.media3.exoplayer.upstream.DefaultAllocator;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
@ -108,7 +108,6 @@ import androidx.media3.common.Player.PositionInfo;
|
|||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.Timeline.Window;
|
import androidx.media3.common.Timeline.Window;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
import androidx.media3.common.TracksInfo.TrackGroupInfo;
|
import androidx.media3.common.TracksInfo.TrackGroupInfo;
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
@ -127,6 +126,7 @@ import androidx.media3.exoplayer.source.MediaSource;
|
|||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
import androidx.media3.exoplayer.source.MediaSourceEventListener;
|
import androidx.media3.exoplayer.source.MediaSourceEventListener;
|
||||||
import androidx.media3.exoplayer.source.SinglePeriodTimeline;
|
import androidx.media3.exoplayer.source.SinglePeriodTimeline;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.source.ads.ServerSideAdInsertionMediaSource;
|
import androidx.media3.exoplayer.source.ads.ServerSideAdInsertionMediaSource;
|
||||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
|
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
|
||||||
import androidx.media3.exoplayer.upstream.Allocation;
|
import androidx.media3.exoplayer.upstream.Allocation;
|
||||||
|
@ -26,7 +26,7 @@ import android.net.Uri;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.MediaItem;
|
import androidx.media3.common.MediaItem;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.extractor.metadata.mp4.MdtaMetadataEntry;
|
import androidx.media3.extractor.metadata.mp4.MdtaMetadataEntry;
|
||||||
import androidx.media3.extractor.metadata.mp4.MotionPhotoMetadata;
|
import androidx.media3.extractor.metadata.mp4.MotionPhotoMetadata;
|
||||||
import androidx.media3.extractor.metadata.mp4.SlowMotionData;
|
import androidx.media3.extractor.metadata.mp4.SlowMotionData;
|
||||||
|
@ -27,7 +27,6 @@ import androidx.media3.common.MimeTypes;
|
|||||||
import androidx.media3.common.StreamKey;
|
import androidx.media3.common.StreamKey;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TrackSelectionOverride;
|
import androidx.media3.common.TrackSelectionOverride;
|
||||||
import androidx.media3.common.TrackSelectionParameters;
|
import androidx.media3.common.TrackSelectionParameters;
|
||||||
import androidx.media3.exoplayer.Renderer;
|
import androidx.media3.exoplayer.Renderer;
|
||||||
@ -35,6 +34,7 @@ import androidx.media3.exoplayer.RenderersFactory;
|
|||||||
import androidx.media3.exoplayer.offline.DownloadHelper.Callback;
|
import androidx.media3.exoplayer.offline.DownloadHelper.Callback;
|
||||||
import androidx.media3.exoplayer.source.MediaPeriod;
|
import androidx.media3.exoplayer.source.MediaPeriod;
|
||||||
import androidx.media3.exoplayer.source.MediaSourceEventListener.EventDispatcher;
|
import androidx.media3.exoplayer.source.MediaSourceEventListener.EventDispatcher;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
|
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
import androidx.media3.exoplayer.trackselection.MappingTrackSelector.MappedTrackInfo;
|
import androidx.media3.exoplayer.trackselection.MappingTrackSelector.MappedTrackInfo;
|
||||||
|
@ -23,7 +23,6 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
import androidx.media3.common.C;
|
import androidx.media3.common.C;
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.decoder.DecoderInputBuffer;
|
import androidx.media3.decoder.DecoderInputBuffer;
|
||||||
import androidx.media3.exoplayer.FormatHolder;
|
import androidx.media3.exoplayer.FormatHolder;
|
||||||
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
|
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
|
||||||
|
@ -13,10 +13,13 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package androidx.media3.common;
|
package androidx.media3.exoplayer.source;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import androidx.media3.common.Format;
|
||||||
|
import androidx.media3.common.MimeTypes;
|
||||||
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
@ -24,7 +24,6 @@ import androidx.media3.common.Format;
|
|||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackSelection;
|
|
||||||
import androidx.media3.datasource.DataSpec;
|
import androidx.media3.datasource.DataSpec;
|
||||||
import androidx.media3.exoplayer.source.MediaSource;
|
import androidx.media3.exoplayer.source.MediaSource;
|
||||||
import androidx.media3.exoplayer.source.chunk.BaseMediaChunkIterator;
|
import androidx.media3.exoplayer.source.chunk.BaseMediaChunkIterator;
|
||||||
|
@ -40,8 +40,6 @@ import androidx.media3.common.Format;
|
|||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TrackSelection;
|
|
||||||
import androidx.media3.common.TrackSelectionOverride;
|
import androidx.media3.common.TrackSelectionOverride;
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
@ -50,6 +48,7 @@ import androidx.media3.exoplayer.RendererCapabilities;
|
|||||||
import androidx.media3.exoplayer.RendererCapabilities.Capabilities;
|
import androidx.media3.exoplayer.RendererCapabilities.Capabilities;
|
||||||
import androidx.media3.exoplayer.RendererConfiguration;
|
import androidx.media3.exoplayer.RendererConfiguration;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector.Parameters;
|
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector.Parameters;
|
||||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector.ParametersBuilder;
|
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector.ParametersBuilder;
|
||||||
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector.SelectionOverride;
|
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector.SelectionOverride;
|
||||||
|
@ -24,7 +24,6 @@ import androidx.media3.common.Format;
|
|||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.exoplayer.ExoPlaybackException;
|
import androidx.media3.exoplayer.ExoPlaybackException;
|
||||||
import androidx.media3.exoplayer.RendererCapabilities;
|
import androidx.media3.exoplayer.RendererCapabilities;
|
||||||
@ -32,6 +31,7 @@ import androidx.media3.exoplayer.RendererCapabilities.AdaptiveSupport;
|
|||||||
import androidx.media3.exoplayer.RendererCapabilities.Capabilities;
|
import androidx.media3.exoplayer.RendererCapabilities.Capabilities;
|
||||||
import androidx.media3.exoplayer.RendererConfiguration;
|
import androidx.media3.exoplayer.RendererConfiguration;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.test.utils.FakeTimeline;
|
import androidx.media3.test.utils.FakeTimeline;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
@ -32,10 +32,9 @@ import static com.google.common.truth.Truth.assertThat;
|
|||||||
|
|
||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TrackSelection;
|
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
import androidx.media3.common.TracksInfo.TrackGroupInfo;
|
import androidx.media3.common.TracksInfo.TrackGroupInfo;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -20,10 +20,10 @@ import static org.junit.Assert.fail;
|
|||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.exoplayer.ExoPlaybackException;
|
import androidx.media3.exoplayer.ExoPlaybackException;
|
||||||
import androidx.media3.exoplayer.RendererCapabilities;
|
import androidx.media3.exoplayer.RendererCapabilities;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.TrackSelector.InvalidationListener;
|
import androidx.media3.exoplayer.trackselection.TrackSelector.InvalidationListener;
|
||||||
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
import androidx.media3.exoplayer.upstream.BandwidthMeter;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
@ -28,7 +28,6 @@ import androidx.media3.common.Format;
|
|||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.StreamKey;
|
import androidx.media3.common.StreamKey;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.datasource.TransferListener;
|
import androidx.media3.datasource.TransferListener;
|
||||||
import androidx.media3.exoplayer.SeekParameters;
|
import androidx.media3.exoplayer.SeekParameters;
|
||||||
@ -50,6 +49,7 @@ import androidx.media3.exoplayer.source.MediaSourceEventListener;
|
|||||||
import androidx.media3.exoplayer.source.MediaSourceEventListener.EventDispatcher;
|
import androidx.media3.exoplayer.source.MediaSourceEventListener.EventDispatcher;
|
||||||
import androidx.media3.exoplayer.source.SampleStream;
|
import androidx.media3.exoplayer.source.SampleStream;
|
||||||
import androidx.media3.exoplayer.source.SequenceableLoader;
|
import androidx.media3.exoplayer.source.SequenceableLoader;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.source.chunk.ChunkSampleStream;
|
import androidx.media3.exoplayer.source.chunk.ChunkSampleStream;
|
||||||
import androidx.media3.exoplayer.source.chunk.ChunkSampleStream.EmbeddedSampleStream;
|
import androidx.media3.exoplayer.source.chunk.ChunkSampleStream.EmbeddedSampleStream;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
|
@ -24,7 +24,6 @@ import androidx.media3.common.util.Assertions;
|
|||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.datasource.DataSource;
|
import androidx.media3.datasource.DataSource;
|
||||||
import androidx.media3.datasource.DataSpec;
|
import androidx.media3.datasource.DataSpec;
|
||||||
import androidx.media3.datasource.HttpDataSource;
|
|
||||||
import androidx.media3.exoplayer.dash.manifest.DashManifest;
|
import androidx.media3.exoplayer.dash.manifest.DashManifest;
|
||||||
import androidx.media3.exoplayer.dash.manifest.DashManifestParser;
|
import androidx.media3.exoplayer.dash.manifest.DashManifestParser;
|
||||||
import androidx.media3.exoplayer.dash.manifest.Period;
|
import androidx.media3.exoplayer.dash.manifest.Period;
|
||||||
@ -85,7 +84,7 @@ public final class DashUtil {
|
|||||||
/**
|
/**
|
||||||
* Loads a DASH manifest.
|
* Loads a DASH manifest.
|
||||||
*
|
*
|
||||||
* @param dataSource The {@link HttpDataSource} from which the manifest should be read.
|
* @param dataSource The {@link DataSource} from which the manifest should be read.
|
||||||
* @param uri The {@link Uri} of the manifest to be read.
|
* @param uri The {@link Uri} of the manifest to be read.
|
||||||
* @return An instance of {@link DashManifest}.
|
* @return An instance of {@link DashManifest}.
|
||||||
* @throws IOException Thrown when there is an error while loading.
|
* @throws IOException Thrown when there is an error while loading.
|
||||||
@ -97,7 +96,7 @@ public final class DashUtil {
|
|||||||
/**
|
/**
|
||||||
* Loads a {@link Format} for acquiring keys for a given period in a DASH manifest.
|
* Loads a {@link Format} for acquiring keys for a given period in a DASH manifest.
|
||||||
*
|
*
|
||||||
* @param dataSource The {@link HttpDataSource} from which data should be loaded.
|
* @param dataSource The {@link DataSource} from which data should be loaded.
|
||||||
* @param period The {@link Period}.
|
* @param period The {@link Period}.
|
||||||
* @return The loaded {@link Format}, or null if none is defined.
|
* @return The loaded {@link Format}, or null if none is defined.
|
||||||
* @throws IOException Thrown when there is an error while loading.
|
* @throws IOException Thrown when there is an error while loading.
|
||||||
|
@ -1189,41 +1189,41 @@ public class DashManifestParser extends DefaultHandler
|
|||||||
xpp.nextToken();
|
xpp.nextToken();
|
||||||
while (!XmlPullParserUtil.isEndTag(xpp, "Event")) {
|
while (!XmlPullParserUtil.isEndTag(xpp, "Event")) {
|
||||||
switch (xpp.getEventType()) {
|
switch (xpp.getEventType()) {
|
||||||
case (XmlPullParser.START_DOCUMENT):
|
case XmlPullParser.START_DOCUMENT:
|
||||||
xmlSerializer.startDocument(null, false);
|
xmlSerializer.startDocument(null, false);
|
||||||
break;
|
break;
|
||||||
case (XmlPullParser.END_DOCUMENT):
|
case XmlPullParser.END_DOCUMENT:
|
||||||
xmlSerializer.endDocument();
|
xmlSerializer.endDocument();
|
||||||
break;
|
break;
|
||||||
case (XmlPullParser.START_TAG):
|
case XmlPullParser.START_TAG:
|
||||||
xmlSerializer.startTag(xpp.getNamespace(), xpp.getName());
|
xmlSerializer.startTag(xpp.getNamespace(), xpp.getName());
|
||||||
for (int i = 0; i < xpp.getAttributeCount(); i++) {
|
for (int i = 0; i < xpp.getAttributeCount(); i++) {
|
||||||
xmlSerializer.attribute(
|
xmlSerializer.attribute(
|
||||||
xpp.getAttributeNamespace(i), xpp.getAttributeName(i), xpp.getAttributeValue(i));
|
xpp.getAttributeNamespace(i), xpp.getAttributeName(i), xpp.getAttributeValue(i));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case (XmlPullParser.END_TAG):
|
case XmlPullParser.END_TAG:
|
||||||
xmlSerializer.endTag(xpp.getNamespace(), xpp.getName());
|
xmlSerializer.endTag(xpp.getNamespace(), xpp.getName());
|
||||||
break;
|
break;
|
||||||
case (XmlPullParser.TEXT):
|
case XmlPullParser.TEXT:
|
||||||
xmlSerializer.text(xpp.getText());
|
xmlSerializer.text(xpp.getText());
|
||||||
break;
|
break;
|
||||||
case (XmlPullParser.CDSECT):
|
case XmlPullParser.CDSECT:
|
||||||
xmlSerializer.cdsect(xpp.getText());
|
xmlSerializer.cdsect(xpp.getText());
|
||||||
break;
|
break;
|
||||||
case (XmlPullParser.ENTITY_REF):
|
case XmlPullParser.ENTITY_REF:
|
||||||
xmlSerializer.entityRef(xpp.getText());
|
xmlSerializer.entityRef(xpp.getText());
|
||||||
break;
|
break;
|
||||||
case (XmlPullParser.IGNORABLE_WHITESPACE):
|
case XmlPullParser.IGNORABLE_WHITESPACE:
|
||||||
xmlSerializer.ignorableWhitespace(xpp.getText());
|
xmlSerializer.ignorableWhitespace(xpp.getText());
|
||||||
break;
|
break;
|
||||||
case (XmlPullParser.PROCESSING_INSTRUCTION):
|
case XmlPullParser.PROCESSING_INSTRUCTION:
|
||||||
xmlSerializer.processingInstruction(xpp.getText());
|
xmlSerializer.processingInstruction(xpp.getText());
|
||||||
break;
|
break;
|
||||||
case (XmlPullParser.COMMENT):
|
case XmlPullParser.COMMENT:
|
||||||
xmlSerializer.comment(xpp.getText());
|
xmlSerializer.comment(xpp.getText());
|
||||||
break;
|
break;
|
||||||
case (XmlPullParser.DOCDECL):
|
case XmlPullParser.DOCDECL:
|
||||||
xmlSerializer.docdecl(xpp.getText());
|
xmlSerializer.docdecl(xpp.getText());
|
||||||
break;
|
break;
|
||||||
default: // fall out
|
default: // fall out
|
||||||
|
@ -21,7 +21,6 @@ import android.net.Uri;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.datasource.TransferListener;
|
import androidx.media3.datasource.TransferListener;
|
||||||
import androidx.media3.exoplayer.analytics.PlayerId;
|
import androidx.media3.exoplayer.analytics.PlayerId;
|
||||||
import androidx.media3.exoplayer.dash.PlayerEmsgHandler.PlayerEmsgCallback;
|
import androidx.media3.exoplayer.dash.PlayerEmsgHandler.PlayerEmsgCallback;
|
||||||
@ -33,6 +32,7 @@ import androidx.media3.exoplayer.drm.DrmSessionManager;
|
|||||||
import androidx.media3.exoplayer.source.CompositeSequenceableLoaderFactory;
|
import androidx.media3.exoplayer.source.CompositeSequenceableLoaderFactory;
|
||||||
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
|
||||||
import androidx.media3.exoplayer.source.MediaSourceEventListener;
|
import androidx.media3.exoplayer.source.MediaSourceEventListener;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.upstream.Allocator;
|
import androidx.media3.exoplayer.upstream.Allocator;
|
||||||
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||||
import androidx.media3.exoplayer.upstream.LoaderErrorThrower;
|
import androidx.media3.exoplayer.upstream.LoaderErrorThrower;
|
||||||
|
@ -25,7 +25,6 @@ import androidx.media3.common.Metadata;
|
|||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.StreamKey;
|
import androidx.media3.common.StreamKey;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
@ -45,6 +44,7 @@ import androidx.media3.exoplayer.source.MediaPeriod;
|
|||||||
import androidx.media3.exoplayer.source.MediaSourceEventListener.EventDispatcher;
|
import androidx.media3.exoplayer.source.MediaSourceEventListener.EventDispatcher;
|
||||||
import androidx.media3.exoplayer.source.SampleStream;
|
import androidx.media3.exoplayer.source.SampleStream;
|
||||||
import androidx.media3.exoplayer.source.SequenceableLoader;
|
import androidx.media3.exoplayer.source.SequenceableLoader;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
import androidx.media3.exoplayer.upstream.Allocator;
|
import androidx.media3.exoplayer.upstream.Allocator;
|
||||||
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
|
||||||
|
@ -33,7 +33,6 @@ import androidx.media3.common.Metadata;
|
|||||||
import androidx.media3.common.MimeTypes;
|
import androidx.media3.common.MimeTypes;
|
||||||
import androidx.media3.common.ParserException;
|
import androidx.media3.common.ParserException;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.util.Assertions;
|
import androidx.media3.common.util.Assertions;
|
||||||
import androidx.media3.common.util.Log;
|
import androidx.media3.common.util.Log;
|
||||||
import androidx.media3.common.util.ParsableByteArray;
|
import androidx.media3.common.util.ParsableByteArray;
|
||||||
@ -53,6 +52,7 @@ import androidx.media3.exoplayer.source.SampleQueue.UpstreamFormatChangedListene
|
|||||||
import androidx.media3.exoplayer.source.SampleStream;
|
import androidx.media3.exoplayer.source.SampleStream;
|
||||||
import androidx.media3.exoplayer.source.SampleStream.ReadFlags;
|
import androidx.media3.exoplayer.source.SampleStream.ReadFlags;
|
||||||
import androidx.media3.exoplayer.source.SequenceableLoader;
|
import androidx.media3.exoplayer.source.SequenceableLoader;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.source.chunk.Chunk;
|
import androidx.media3.exoplayer.source.chunk.Chunk;
|
||||||
import androidx.media3.exoplayer.source.chunk.MediaChunkIterator;
|
import androidx.media3.exoplayer.source.chunk.MediaChunkIterator;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
|
@ -18,11 +18,12 @@ package androidx.media3.exoplayer.ima;
|
|||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
import static androidx.media3.common.util.Assertions.checkNotNull;
|
||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
import static androidx.media3.common.util.Util.msToUs;
|
import static androidx.media3.common.util.Util.msToUs;
|
||||||
import static androidx.media3.common.util.Util.secToUs;
|
|
||||||
import static androidx.media3.common.util.Util.sum;
|
import static androidx.media3.common.util.Util.sum;
|
||||||
import static androidx.media3.common.util.Util.usToMs;
|
import static androidx.media3.common.util.Util.usToMs;
|
||||||
import static androidx.media3.exoplayer.ima.ImaUtil.expandAdGroupPlaceholder;
|
import static androidx.media3.exoplayer.ima.ImaUtil.expandAdGroupPlaceholder;
|
||||||
import static androidx.media3.exoplayer.ima.ImaUtil.getAdGroupAndIndexInMultiPeriodWindow;
|
import static androidx.media3.exoplayer.ima.ImaUtil.getAdGroupAndIndexInMultiPeriodWindow;
|
||||||
|
import static androidx.media3.exoplayer.ima.ImaUtil.secToMsRounded;
|
||||||
|
import static androidx.media3.exoplayer.ima.ImaUtil.secToUsRounded;
|
||||||
import static androidx.media3.exoplayer.ima.ImaUtil.splitAdPlaybackStateForPeriods;
|
import static androidx.media3.exoplayer.ima.ImaUtil.splitAdPlaybackStateForPeriods;
|
||||||
import static androidx.media3.exoplayer.ima.ImaUtil.updateAdDurationAndPropagate;
|
import static androidx.media3.exoplayer.ima.ImaUtil.updateAdDurationAndPropagate;
|
||||||
import static androidx.media3.exoplayer.ima.ImaUtil.updateAdDurationInAdGroup;
|
import static androidx.media3.exoplayer.ima.ImaUtil.updateAdDurationInAdGroup;
|
||||||
@ -661,19 +662,29 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
|
|||||||
|
|
||||||
private static AdPlaybackState setVodAdGroupPlaceholders(
|
private static AdPlaybackState setVodAdGroupPlaceholders(
|
||||||
List<CuePoint> cuePoints, AdPlaybackState adPlaybackState) {
|
List<CuePoint> cuePoints, AdPlaybackState adPlaybackState) {
|
||||||
|
// TODO(b/192231683) Use getEndTimeMs()/getStartTimeMs() after jar target was removed
|
||||||
for (int i = 0; i < cuePoints.size(); i++) {
|
for (int i = 0; i < cuePoints.size(); i++) {
|
||||||
CuePoint cuePoint = cuePoints.get(i);
|
CuePoint cuePoint = cuePoints.get(i);
|
||||||
|
long fromPositionUs = msToUs(secToMsRounded(cuePoint.getStartTime()));
|
||||||
adPlaybackState =
|
adPlaybackState =
|
||||||
addAdGroupToAdPlaybackState(
|
addAdGroupToAdPlaybackState(
|
||||||
adPlaybackState,
|
adPlaybackState,
|
||||||
/* fromPositionUs= */ secToUs(cuePoint.getStartTime()),
|
/* fromPositionUs= */ fromPositionUs,
|
||||||
/* contentResumeOffsetUs= */ 0,
|
/* contentResumeOffsetUs= */ 0,
|
||||||
// TODO(b/192231683) Use getEndTimeMs()/getStartTimeMs() after jar target was removed
|
/* adDurationsUs...= */ getAdDuration(
|
||||||
/* adDurationsUs...= */ secToUs(cuePoint.getEndTime() - cuePoint.getStartTime()));
|
/* startTimeSeconds= */ cuePoint.getStartTime(),
|
||||||
|
/* endTimeSeconds= */ cuePoint.getEndTime()));
|
||||||
}
|
}
|
||||||
return adPlaybackState;
|
return adPlaybackState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static long getAdDuration(double startTimeSeconds, double endTimeSeconds) {
|
||||||
|
// startTimeSeconds and endTimeSeconds that are coming from the SDK, only have a precision of
|
||||||
|
// milliseconds so everything that is below a millisecond can be safely considered as coming
|
||||||
|
// from rounding issues.
|
||||||
|
return msToUs(secToMsRounded(endTimeSeconds - startTimeSeconds));
|
||||||
|
}
|
||||||
|
|
||||||
private static AdPlaybackState setVodAdInPlaceholder(Ad ad, AdPlaybackState adPlaybackState) {
|
private static AdPlaybackState setVodAdInPlaceholder(Ad ad, AdPlaybackState adPlaybackState) {
|
||||||
AdPodInfo adPodInfo = ad.getAdPodInfo();
|
AdPodInfo adPodInfo = ad.getAdPodInfo();
|
||||||
// Handle post rolls that have a podIndex of -1.
|
// Handle post rolls that have a podIndex of -1.
|
||||||
@ -685,9 +696,9 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
|
|||||||
adPlaybackState =
|
adPlaybackState =
|
||||||
expandAdGroupPlaceholder(
|
expandAdGroupPlaceholder(
|
||||||
adGroupIndex,
|
adGroupIndex,
|
||||||
/* adGroupDurationUs= */ secToUs(adPodInfo.getMaxDuration()),
|
/* adGroupDurationUs= */ msToUs(secToMsRounded(adPodInfo.getMaxDuration())),
|
||||||
adIndexInAdGroup,
|
adIndexInAdGroup,
|
||||||
/* adDurationUs= */ secToUs(ad.getDuration()),
|
/* adDurationUs= */ msToUs(secToMsRounded(ad.getDuration())),
|
||||||
/* adsInAdGroupCount= */ adPodInfo.getTotalAds(),
|
/* adsInAdGroupCount= */ adPodInfo.getTotalAds(),
|
||||||
adPlaybackState);
|
adPlaybackState);
|
||||||
} else if (adIndexInAdGroup < adGroup.count - 1) {
|
} else if (adIndexInAdGroup < adGroup.count - 1) {
|
||||||
@ -695,7 +706,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
|
|||||||
updateAdDurationInAdGroup(
|
updateAdDurationInAdGroup(
|
||||||
adGroupIndex,
|
adGroupIndex,
|
||||||
adIndexInAdGroup,
|
adIndexInAdGroup,
|
||||||
/* adDurationUs= */ secToUs(ad.getDuration()),
|
/* adDurationUs= */ msToUs(secToMsRounded(ad.getDuration())),
|
||||||
adPlaybackState);
|
adPlaybackState);
|
||||||
}
|
}
|
||||||
return adPlaybackState;
|
return adPlaybackState;
|
||||||
@ -704,7 +715,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
|
|||||||
private AdPlaybackState addLiveAdBreak(
|
private AdPlaybackState addLiveAdBreak(
|
||||||
Ad ad, long currentPeriodPositionUs, AdPlaybackState adPlaybackState) {
|
Ad ad, long currentPeriodPositionUs, AdPlaybackState adPlaybackState) {
|
||||||
AdPodInfo adPodInfo = ad.getAdPodInfo();
|
AdPodInfo adPodInfo = ad.getAdPodInfo();
|
||||||
long adDurationUs = secToUs(ad.getDuration());
|
long adDurationUs = secToUsRounded(ad.getDuration());
|
||||||
int adIndexInAdGroup = adPodInfo.getAdPosition() - 1;
|
int adIndexInAdGroup = adPodInfo.getAdPosition() - 1;
|
||||||
// TODO(b/208398934) Support seeking backwards.
|
// TODO(b/208398934) Support seeking backwards.
|
||||||
if (adIndexInAdGroup == 0 || adPlaybackState.adGroupCount == 1) {
|
if (adIndexInAdGroup == 0 || adPlaybackState.adGroupCount == 1) {
|
||||||
@ -718,7 +729,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
|
|||||||
new long[adCount],
|
new long[adCount],
|
||||||
adIndexInAdGroup,
|
adIndexInAdGroup,
|
||||||
adDurationUs,
|
adDurationUs,
|
||||||
secToUs(adPodInfo.getMaxDuration()));
|
msToUs(secToMsRounded(adPodInfo.getMaxDuration())));
|
||||||
adPlaybackState =
|
adPlaybackState =
|
||||||
addAdGroupToAdPlaybackState(
|
addAdGroupToAdPlaybackState(
|
||||||
adPlaybackState,
|
adPlaybackState,
|
||||||
|
@ -54,7 +54,10 @@ import com.google.ads.interactivemedia.v3.api.player.VideoAdPlayer;
|
|||||||
import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate;
|
import com.google.ads.interactivemedia.v3.api.player.VideoProgressUpdate;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.math.DoubleMath;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -398,17 +401,13 @@ import java.util.Set;
|
|||||||
long elapsedAdGroupAdDurationUs = 0;
|
long elapsedAdGroupAdDurationUs = 0;
|
||||||
for (int j = periodIndex; j < contentTimeline.getPeriodCount(); j++) {
|
for (int j = periodIndex; j < contentTimeline.getPeriodCount(); j++) {
|
||||||
contentTimeline.getPeriod(j, period, /* setIds= */ true);
|
contentTimeline.getPeriod(j, period, /* setIds= */ true);
|
||||||
// TODO(b/192231683) Remove subtracted US from ad group time when we can upgrade the SDK.
|
if (totalElapsedContentDurationUs < adGroup.timeUs) {
|
||||||
// Subtract one microsecond to work around rounding errors with adGroup.timeUs.
|
|
||||||
if (totalElapsedContentDurationUs < adGroup.timeUs - 1) {
|
|
||||||
// Period starts before the ad group, so it is a content period.
|
// Period starts before the ad group, so it is a content period.
|
||||||
adPlaybackStates.put(checkNotNull(period.uid), contentOnlyAdPlaybackState);
|
adPlaybackStates.put(checkNotNull(period.uid), contentOnlyAdPlaybackState);
|
||||||
totalElapsedContentDurationUs += period.durationUs;
|
totalElapsedContentDurationUs += period.durationUs;
|
||||||
} else {
|
} else {
|
||||||
long periodStartUs = totalElapsedContentDurationUs + elapsedAdGroupAdDurationUs;
|
long periodStartUs = totalElapsedContentDurationUs + elapsedAdGroupAdDurationUs;
|
||||||
// TODO(b/192231683) Remove additional US when we can upgrade the SDK.
|
if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs) {
|
||||||
// Add one microsecond to work around rounding errors with adGroup.timeUs.
|
|
||||||
if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs + 1) {
|
|
||||||
// The period ends before the end of the ad group, so it is an ad period (Note: A VOD ad
|
// The period ends before the end of the ad group, so it is an ad period (Note: A VOD ad
|
||||||
// reported by the IMA SDK spans multiple periods before the LOADED event arrives).
|
// reported by the IMA SDK spans multiple periods before the LOADED event arrives).
|
||||||
adPlaybackStates.put(
|
adPlaybackStates.put(
|
||||||
@ -490,16 +489,12 @@ import java.util.Set;
|
|||||||
long elapsedAdGroupAdDurationUs = 0;
|
long elapsedAdGroupAdDurationUs = 0;
|
||||||
for (int j = periodIndex; j < contentTimeline.getPeriodCount(); j++) {
|
for (int j = periodIndex; j < contentTimeline.getPeriodCount(); j++) {
|
||||||
contentTimeline.getPeriod(j, period, /* setIds= */ true);
|
contentTimeline.getPeriod(j, period, /* setIds= */ true);
|
||||||
// TODO(b/192231683) Remove subtracted US from ad group time when we can upgrade the SDK.
|
if (totalElapsedContentDurationUs < adGroup.timeUs) {
|
||||||
// Subtract one microsecond to work around rounding errors with adGroup.timeUs.
|
|
||||||
if (totalElapsedContentDurationUs < adGroup.timeUs - 1) {
|
|
||||||
// Period starts before the ad group, so it is a content period.
|
// Period starts before the ad group, so it is a content period.
|
||||||
totalElapsedContentDurationUs += period.durationUs;
|
totalElapsedContentDurationUs += period.durationUs;
|
||||||
} else {
|
} else {
|
||||||
long periodStartUs = totalElapsedContentDurationUs + elapsedAdGroupAdDurationUs;
|
long periodStartUs = totalElapsedContentDurationUs + elapsedAdGroupAdDurationUs;
|
||||||
// TODO(b/192231683) Remove additional US when we can upgrade the SDK.
|
if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs) {
|
||||||
// Add one microsecond to work around rounding errors with adGroup.timeUs.
|
|
||||||
if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs + 1) {
|
|
||||||
// The period ends before the end of the ad group, so it is an ad period.
|
// The period ends before the end of the ad group, so it is an ad period.
|
||||||
if (j == adPeriodIndex) {
|
if (j == adPeriodIndex) {
|
||||||
return new Pair<>(/* adGroupIndex= */ i, adIndexInAdGroup);
|
return new Pair<>(/* adGroupIndex= */ i, adIndexInAdGroup);
|
||||||
@ -518,5 +513,31 @@ import java.util.Set;
|
|||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a time in seconds to the corresponding time in microseconds.
|
||||||
|
*
|
||||||
|
* <p>Fractional values are rounded to the nearest microsecond using {@link RoundingMode#HALF_UP}.
|
||||||
|
*
|
||||||
|
* @param timeSec The time in seconds.
|
||||||
|
* @return The corresponding time in microseconds.
|
||||||
|
*/
|
||||||
|
public static long secToUsRounded(double timeSec) {
|
||||||
|
return DoubleMath.roundToLong(
|
||||||
|
BigDecimal.valueOf(timeSec).scaleByPowerOfTen(6).doubleValue(), RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts a time in seconds to the corresponding time in milliseconds.
|
||||||
|
*
|
||||||
|
* <p>Fractional values are rounded to the nearest millisecond using {@link RoundingMode#HALF_UP}.
|
||||||
|
*
|
||||||
|
* @param timeSec The time in seconds.
|
||||||
|
* @return The corresponding time in milliseconds.
|
||||||
|
*/
|
||||||
|
public static long secToMsRounded(double timeSec) {
|
||||||
|
return DoubleMath.roundToLong(
|
||||||
|
BigDecimal.valueOf(timeSec).scaleByPowerOfTen(3).doubleValue(), RoundingMode.HALF_UP);
|
||||||
|
}
|
||||||
|
|
||||||
private ImaUtil() {}
|
private ImaUtil() {}
|
||||||
}
|
}
|
||||||
|
@ -23,13 +23,13 @@ import androidx.media3.common.MediaItem;
|
|||||||
import androidx.media3.common.PlaybackException;
|
import androidx.media3.common.PlaybackException;
|
||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
import androidx.media3.common.Timeline;
|
import androidx.media3.common.Timeline;
|
||||||
import androidx.media3.common.TrackSelectionArray;
|
|
||||||
import androidx.media3.common.TrackSelectionParameters;
|
import androidx.media3.common.TrackSelectionParameters;
|
||||||
import androidx.media3.common.TracksInfo;
|
import androidx.media3.common.TracksInfo;
|
||||||
import androidx.media3.common.util.Clock;
|
import androidx.media3.common.util.Clock;
|
||||||
import androidx.media3.common.util.ListenerSet;
|
import androidx.media3.common.util.ListenerSet;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.exoplayer.ExoPlayer;
|
import androidx.media3.exoplayer.ExoPlayer;
|
||||||
|
import androidx.media3.exoplayer.trackselection.TrackSelectionArray;
|
||||||
import androidx.media3.test.utils.StubExoPlayer;
|
import androidx.media3.test.utils.StubExoPlayer;
|
||||||
|
|
||||||
/** A fake {@link ExoPlayer} for testing content/ad playback. */
|
/** A fake {@link ExoPlayer} for testing content/ad playback. */
|
||||||
@ -79,7 +79,7 @@ import androidx.media3.test.utils.StubExoPlayer;
|
|||||||
PositionInfo oldPosition =
|
PositionInfo oldPosition =
|
||||||
new PositionInfo(
|
new PositionInfo(
|
||||||
windowUid,
|
windowUid,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItem,
|
||||||
periodUid,
|
periodUid,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
@ -97,7 +97,7 @@ import androidx.media3.test.utils.StubExoPlayer;
|
|||||||
PositionInfo newPosition =
|
PositionInfo newPosition =
|
||||||
new PositionInfo(
|
new PositionInfo(
|
||||||
windowUid,
|
windowUid,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItem,
|
||||||
periodUid,
|
periodUid,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
@ -128,7 +128,7 @@ import androidx.media3.test.utils.StubExoPlayer;
|
|||||||
PositionInfo oldPosition =
|
PositionInfo oldPosition =
|
||||||
new PositionInfo(
|
new PositionInfo(
|
||||||
windowUid,
|
windowUid,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItem,
|
||||||
periodUid,
|
periodUid,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
@ -146,7 +146,7 @@ import androidx.media3.test.utils.StubExoPlayer;
|
|||||||
PositionInfo newPosition =
|
PositionInfo newPosition =
|
||||||
new PositionInfo(
|
new PositionInfo(
|
||||||
windowUid,
|
windowUid,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItem,
|
||||||
periodUid,
|
periodUid,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
|
@ -28,8 +28,6 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.StreamKey;
|
import androidx.media3.common.StreamKey;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.common.TrackSelection;
|
|
||||||
import androidx.media3.common.util.UnstableApi;
|
import androidx.media3.common.util.UnstableApi;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
import androidx.media3.decoder.DecoderInputBuffer;
|
import androidx.media3.decoder.DecoderInputBuffer;
|
||||||
@ -44,7 +42,9 @@ import androidx.media3.exoplayer.source.SampleQueue.UpstreamFormatChangedListene
|
|||||||
import androidx.media3.exoplayer.source.SampleStream;
|
import androidx.media3.exoplayer.source.SampleStream;
|
||||||
import androidx.media3.exoplayer.source.SampleStream.ReadDataResult;
|
import androidx.media3.exoplayer.source.SampleStream.ReadDataResult;
|
||||||
import androidx.media3.exoplayer.source.SampleStream.ReadFlags;
|
import androidx.media3.exoplayer.source.SampleStream.ReadFlags;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
|
import androidx.media3.exoplayer.trackselection.TrackSelection;
|
||||||
import androidx.media3.exoplayer.upstream.Allocator;
|
import androidx.media3.exoplayer.upstream.Allocator;
|
||||||
import androidx.media3.exoplayer.upstream.Loader;
|
import androidx.media3.exoplayer.upstream.Loader;
|
||||||
import androidx.media3.exoplayer.upstream.Loader.Loadable;
|
import androidx.media3.exoplayer.upstream.Loader.Loadable;
|
||||||
|
@ -20,7 +20,6 @@ import androidx.media3.common.C;
|
|||||||
import androidx.media3.common.Format;
|
import androidx.media3.common.Format;
|
||||||
import androidx.media3.common.StreamKey;
|
import androidx.media3.common.StreamKey;
|
||||||
import androidx.media3.common.TrackGroup;
|
import androidx.media3.common.TrackGroup;
|
||||||
import androidx.media3.common.TrackGroupArray;
|
|
||||||
import androidx.media3.datasource.TransferListener;
|
import androidx.media3.datasource.TransferListener;
|
||||||
import androidx.media3.exoplayer.SeekParameters;
|
import androidx.media3.exoplayer.SeekParameters;
|
||||||
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
|
import androidx.media3.exoplayer.drm.DrmSessionEventListener;
|
||||||
@ -31,6 +30,7 @@ import androidx.media3.exoplayer.source.MediaPeriod;
|
|||||||
import androidx.media3.exoplayer.source.MediaSourceEventListener;
|
import androidx.media3.exoplayer.source.MediaSourceEventListener;
|
||||||
import androidx.media3.exoplayer.source.SampleStream;
|
import androidx.media3.exoplayer.source.SampleStream;
|
||||||
import androidx.media3.exoplayer.source.SequenceableLoader;
|
import androidx.media3.exoplayer.source.SequenceableLoader;
|
||||||
|
import androidx.media3.exoplayer.source.TrackGroupArray;
|
||||||
import androidx.media3.exoplayer.source.chunk.ChunkSampleStream;
|
import androidx.media3.exoplayer.source.chunk.ChunkSampleStream;
|
||||||
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
|
||||||
import androidx.media3.exoplayer.upstream.Allocator;
|
import androidx.media3.exoplayer.upstream.Allocator;
|
||||||
|
@ -31,6 +31,21 @@ public final class NalUnitUtil {
|
|||||||
|
|
||||||
private static final String TAG = "NalUnitUtil";
|
private static final String TAG = "NalUnitUtil";
|
||||||
|
|
||||||
|
/** Coded slice of a non-IDR picture. */
|
||||||
|
public static final int NAL_UNIT_TYPE_NON_IDR = 1;
|
||||||
|
/** Coded slice data partition A. */
|
||||||
|
public static final int NAL_UNIT_TYPE_PARTITION_A = 2;
|
||||||
|
/** Coded slice of an IDR picture. */
|
||||||
|
public static final int NAL_UNIT_TYPE_IDR = 5;
|
||||||
|
/** Supplemental enhancement information. */
|
||||||
|
public static final int NAL_UNIT_TYPE_SEI = 6;
|
||||||
|
/** Sequence parameter set. */
|
||||||
|
public static final int NAL_UNIT_TYPE_SPS = 7;
|
||||||
|
/** Picture parameter set. */
|
||||||
|
public static final int NAL_UNIT_TYPE_PPS = 8;
|
||||||
|
/** Access unit delimiter. */
|
||||||
|
public static final int NAL_UNIT_TYPE_AUD = 9;
|
||||||
|
|
||||||
/** Holds data parsed from a H.264 sequence parameter set NAL unit. */
|
/** Holds data parsed from a H.264 sequence parameter set NAL unit. */
|
||||||
public static final class SpsData {
|
public static final class SpsData {
|
||||||
|
|
||||||
@ -38,6 +53,7 @@ public final class NalUnitUtil {
|
|||||||
public final int constraintsFlagsAndReservedZero2Bits;
|
public final int constraintsFlagsAndReservedZero2Bits;
|
||||||
public final int levelIdc;
|
public final int levelIdc;
|
||||||
public final int seqParameterSetId;
|
public final int seqParameterSetId;
|
||||||
|
public final int maxNumRefFrames;
|
||||||
public final int width;
|
public final int width;
|
||||||
public final int height;
|
public final int height;
|
||||||
public final float pixelWidthHeightRatio;
|
public final float pixelWidthHeightRatio;
|
||||||
@ -53,6 +69,7 @@ public final class NalUnitUtil {
|
|||||||
int constraintsFlagsAndReservedZero2Bits,
|
int constraintsFlagsAndReservedZero2Bits,
|
||||||
int levelIdc,
|
int levelIdc,
|
||||||
int seqParameterSetId,
|
int seqParameterSetId,
|
||||||
|
int maxNumRefFrames,
|
||||||
int width,
|
int width,
|
||||||
int height,
|
int height,
|
||||||
float pixelWidthHeightRatio,
|
float pixelWidthHeightRatio,
|
||||||
@ -66,6 +83,7 @@ public final class NalUnitUtil {
|
|||||||
this.constraintsFlagsAndReservedZero2Bits = constraintsFlagsAndReservedZero2Bits;
|
this.constraintsFlagsAndReservedZero2Bits = constraintsFlagsAndReservedZero2Bits;
|
||||||
this.levelIdc = levelIdc;
|
this.levelIdc = levelIdc;
|
||||||
this.seqParameterSetId = seqParameterSetId;
|
this.seqParameterSetId = seqParameterSetId;
|
||||||
|
this.maxNumRefFrames = maxNumRefFrames;
|
||||||
this.width = width;
|
this.width = width;
|
||||||
this.height = height;
|
this.height = height;
|
||||||
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
|
||||||
@ -372,7 +390,7 @@ public final class NalUnitUtil {
|
|||||||
data.readUnsignedExpGolombCodedInt(); // offset_for_ref_frame[i]
|
data.readUnsignedExpGolombCodedInt(); // offset_for_ref_frame[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.readUnsignedExpGolombCodedInt(); // max_num_ref_frames
|
int maxNumRefFrames = data.readUnsignedExpGolombCodedInt(); // max_num_ref_frames
|
||||||
data.skipBit(); // gaps_in_frame_num_value_allowed_flag
|
data.skipBit(); // gaps_in_frame_num_value_allowed_flag
|
||||||
|
|
||||||
int picWidthInMbs = data.readUnsignedExpGolombCodedInt() + 1;
|
int picWidthInMbs = data.readUnsignedExpGolombCodedInt() + 1;
|
||||||
@ -432,6 +450,7 @@ public final class NalUnitUtil {
|
|||||||
constraintsFlagsAndReservedZero2Bits,
|
constraintsFlagsAndReservedZero2Bits,
|
||||||
levelIdc,
|
levelIdc,
|
||||||
seqParameterSetId,
|
seqParameterSetId,
|
||||||
|
maxNumRefFrames,
|
||||||
frameWidth,
|
frameWidth,
|
||||||
frameHeight,
|
frameHeight,
|
||||||
pixelWidthHeightRatio,
|
pixelWidthHeightRatio,
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.extractor.mp4;
|
package androidx.media3.extractor.mp4;
|
||||||
|
|
||||||
import static androidx.media3.common.util.Assertions.checkNotNull;
|
|
||||||
import static androidx.media3.common.util.Util.castNonNull;
|
import static androidx.media3.common.util.Util.castNonNull;
|
||||||
import static androidx.media3.extractor.mp4.AtomParsers.parseTraks;
|
import static androidx.media3.extractor.mp4.AtomParsers.parseTraks;
|
||||||
import static androidx.media3.extractor.mp4.Sniffer.BRAND_HEIC;
|
import static androidx.media3.extractor.mp4.Sniffer.BRAND_HEIC;
|
||||||
@ -165,8 +164,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
|||||||
private int sampleCurrentNalBytesRemaining;
|
private int sampleCurrentNalBytesRemaining;
|
||||||
|
|
||||||
// Extractor outputs.
|
// Extractor outputs.
|
||||||
private @MonotonicNonNull ExtractorOutput extractorOutput;
|
private ExtractorOutput extractorOutput;
|
||||||
private Mp4Track @MonotonicNonNull [] tracks;
|
private Mp4Track[] tracks;
|
||||||
|
|
||||||
private long @MonotonicNonNull [][] accumulatedSampleSizes;
|
private long @MonotonicNonNull [][] accumulatedSampleSizes;
|
||||||
private int firstVideoTrackIndex;
|
private int firstVideoTrackIndex;
|
||||||
@ -197,6 +196,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
|||||||
nalLength = new ParsableByteArray(4);
|
nalLength = new ParsableByteArray(4);
|
||||||
scratch = new ParsableByteArray();
|
scratch = new ParsableByteArray();
|
||||||
sampleTrackIndex = C.INDEX_UNSET;
|
sampleTrackIndex = C.INDEX_UNSET;
|
||||||
|
extractorOutput = ExtractorOutput.PLACEHOLDER;
|
||||||
|
tracks = new Mp4Track[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -227,7 +228,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
|||||||
sefReader.reset();
|
sefReader.reset();
|
||||||
slowMotionMetadataEntries.clear();
|
slowMotionMetadataEntries.clear();
|
||||||
}
|
}
|
||||||
} else if (tracks != null) {
|
} else {
|
||||||
for (Mp4Track track : tracks) {
|
for (Mp4Track track : tracks) {
|
||||||
updateSampleIndex(track, timeUs);
|
updateSampleIndex(track, timeUs);
|
||||||
if (track.trueHdSampleRechunker != null) {
|
if (track.trueHdSampleRechunker != null) {
|
||||||
@ -280,7 +281,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SeekPoints getSeekPoints(long timeUs) {
|
public SeekPoints getSeekPoints(long timeUs) {
|
||||||
if (checkNotNull(tracks).length == 0) {
|
if (tracks.length == 0) {
|
||||||
return new SeekPoints(SeekPoint.START);
|
return new SeekPoints(SeekPoint.START);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,7 +503,6 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
|||||||
isQuickTime,
|
isQuickTime,
|
||||||
/* modifyTrackFunction= */ track -> track);
|
/* modifyTrackFunction= */ track -> track);
|
||||||
|
|
||||||
ExtractorOutput extractorOutput = checkNotNull(this.extractorOutput);
|
|
||||||
int trackCount = trackSampleTables.size();
|
int trackCount = trackSampleTables.size();
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
TrackSampleTable trackSampleTable = trackSampleTables.get(i);
|
TrackSampleTable trackSampleTable = trackSampleTables.get(i);
|
||||||
@ -582,7 +582,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
|||||||
return RESULT_END_OF_INPUT;
|
return RESULT_END_OF_INPUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Mp4Track track = castNonNull(tracks)[sampleTrackIndex];
|
Mp4Track track = tracks[sampleTrackIndex];
|
||||||
TrackOutput trackOutput = track.trackOutput;
|
TrackOutput trackOutput = track.trackOutput;
|
||||||
int sampleIndex = track.sampleIndex;
|
int sampleIndex = track.sampleIndex;
|
||||||
long position = track.sampleTable.offsets[sampleIndex];
|
long position = track.sampleTable.offsets[sampleIndex];
|
||||||
@ -699,7 +699,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
|||||||
long minAccumulatedBytes = Long.MAX_VALUE;
|
long minAccumulatedBytes = Long.MAX_VALUE;
|
||||||
boolean minAccumulatedBytesRequiresReload = true;
|
boolean minAccumulatedBytesRequiresReload = true;
|
||||||
int minAccumulatedBytesTrackIndex = C.INDEX_UNSET;
|
int minAccumulatedBytesTrackIndex = C.INDEX_UNSET;
|
||||||
for (int trackIndex = 0; trackIndex < castNonNull(tracks).length; trackIndex++) {
|
for (int trackIndex = 0; trackIndex < tracks.length; trackIndex++) {
|
||||||
Mp4Track track = tracks[trackIndex];
|
Mp4Track track = tracks[trackIndex];
|
||||||
int sampleIndex = track.sampleIndex;
|
int sampleIndex = track.sampleIndex;
|
||||||
if (sampleIndex == track.sampleTable.sampleCount) {
|
if (sampleIndex == track.sampleTable.sampleCount) {
|
||||||
@ -744,7 +744,6 @@ public final class Mp4Extractor implements Extractor, SeekMap {
|
|||||||
private void processEndOfStreamReadingAtomHeader() {
|
private void processEndOfStreamReadingAtomHeader() {
|
||||||
if (fileType == FILE_TYPE_HEIC && (flags & FLAG_READ_MOTION_PHOTO_METADATA) != 0) {
|
if (fileType == FILE_TYPE_HEIC && (flags & FLAG_READ_MOTION_PHOTO_METADATA) != 0) {
|
||||||
// Add image track and prepare media.
|
// Add image track and prepare media.
|
||||||
ExtractorOutput extractorOutput = checkNotNull(this.extractorOutput);
|
|
||||||
TrackOutput trackOutput = extractorOutput.track(/* id= */ 0, C.TRACK_TYPE_IMAGE);
|
TrackOutput trackOutput = extractorOutput.track(/* id= */ 0, C.TRACK_TYPE_IMAGE);
|
||||||
@Nullable
|
@Nullable
|
||||||
Metadata metadata = motionPhotoMetadata == null ? null : new Metadata(motionPhotoMetadata);
|
Metadata metadata = motionPhotoMetadata == null ? null : new Metadata(motionPhotoMetadata);
|
||||||
|
@ -44,10 +44,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
|
|||||||
@UnstableApi
|
@UnstableApi
|
||||||
public final class H264Reader implements ElementaryStreamReader {
|
public final class H264Reader implements ElementaryStreamReader {
|
||||||
|
|
||||||
private static final int NAL_UNIT_TYPE_SEI = 6; // Supplemental enhancement information
|
|
||||||
private static final int NAL_UNIT_TYPE_SPS = 7; // Sequence parameter set
|
|
||||||
private static final int NAL_UNIT_TYPE_PPS = 8; // Picture parameter set
|
|
||||||
|
|
||||||
private final SeiReader seiReader;
|
private final SeiReader seiReader;
|
||||||
private final boolean allowNonIdrKeyframes;
|
private final boolean allowNonIdrKeyframes;
|
||||||
private final boolean detectAccessUnits;
|
private final boolean detectAccessUnits;
|
||||||
@ -85,9 +81,9 @@ public final class H264Reader implements ElementaryStreamReader {
|
|||||||
this.allowNonIdrKeyframes = allowNonIdrKeyframes;
|
this.allowNonIdrKeyframes = allowNonIdrKeyframes;
|
||||||
this.detectAccessUnits = detectAccessUnits;
|
this.detectAccessUnits = detectAccessUnits;
|
||||||
prefixFlags = new boolean[3];
|
prefixFlags = new boolean[3];
|
||||||
sps = new NalUnitTargetBuffer(NAL_UNIT_TYPE_SPS, 128);
|
sps = new NalUnitTargetBuffer(NalUnitUtil.NAL_UNIT_TYPE_SPS, 128);
|
||||||
pps = new NalUnitTargetBuffer(NAL_UNIT_TYPE_PPS, 128);
|
pps = new NalUnitTargetBuffer(NalUnitUtil.NAL_UNIT_TYPE_PPS, 128);
|
||||||
sei = new NalUnitTargetBuffer(NAL_UNIT_TYPE_SEI, 128);
|
sei = new NalUnitTargetBuffer(NalUnitUtil.NAL_UNIT_TYPE_SEI, 128);
|
||||||
pesTimeUs = C.TIME_UNSET;
|
pesTimeUs = C.TIME_UNSET;
|
||||||
seiWrapper = new ParsableByteArray();
|
seiWrapper = new ParsableByteArray();
|
||||||
}
|
}
|
||||||
@ -266,11 +262,6 @@ public final class H264Reader implements ElementaryStreamReader {
|
|||||||
|
|
||||||
private static final int DEFAULT_BUFFER_SIZE = 128;
|
private static final int DEFAULT_BUFFER_SIZE = 128;
|
||||||
|
|
||||||
private static final int NAL_UNIT_TYPE_NON_IDR = 1; // Coded slice of a non-IDR picture
|
|
||||||
private static final int NAL_UNIT_TYPE_PARTITION_A = 2; // Coded slice data partition A
|
|
||||||
private static final int NAL_UNIT_TYPE_IDR = 5; // Coded slice of an IDR picture
|
|
||||||
private static final int NAL_UNIT_TYPE_AUD = 9; // Access unit delimiter
|
|
||||||
|
|
||||||
private final TrackOutput output;
|
private final TrackOutput output;
|
||||||
private final boolean allowNonIdrKeyframes;
|
private final boolean allowNonIdrKeyframes;
|
||||||
private final boolean detectAccessUnits;
|
private final boolean detectAccessUnits;
|
||||||
@ -331,11 +322,11 @@ public final class H264Reader implements ElementaryStreamReader {
|
|||||||
nalUnitType = type;
|
nalUnitType = type;
|
||||||
nalUnitTimeUs = pesTimeUs;
|
nalUnitTimeUs = pesTimeUs;
|
||||||
nalUnitStartPosition = position;
|
nalUnitStartPosition = position;
|
||||||
if ((allowNonIdrKeyframes && nalUnitType == NAL_UNIT_TYPE_NON_IDR)
|
if ((allowNonIdrKeyframes && nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_NON_IDR)
|
||||||
|| (detectAccessUnits
|
|| (detectAccessUnits
|
||||||
&& (nalUnitType == NAL_UNIT_TYPE_IDR
|
&& (nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_IDR
|
||||||
|| nalUnitType == NAL_UNIT_TYPE_NON_IDR
|
|| nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_NON_IDR
|
||||||
|| nalUnitType == NAL_UNIT_TYPE_PARTITION_A))) {
|
|| nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_PARTITION_A))) {
|
||||||
// Store the previous header and prepare to populate the new one.
|
// Store the previous header and prepare to populate the new one.
|
||||||
SliceHeaderData newSliceHeader = previousSliceHeader;
|
SliceHeaderData newSliceHeader = previousSliceHeader;
|
||||||
previousSliceHeader = sliceHeader;
|
previousSliceHeader = sliceHeader;
|
||||||
@ -425,7 +416,7 @@ public final class H264Reader implements ElementaryStreamReader {
|
|||||||
bottomFieldFlagPresent = true;
|
bottomFieldFlagPresent = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
boolean idrPicFlag = nalUnitType == NAL_UNIT_TYPE_IDR;
|
boolean idrPicFlag = nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_IDR;
|
||||||
int idrPicId = 0;
|
int idrPicId = 0;
|
||||||
if (idrPicFlag) {
|
if (idrPicFlag) {
|
||||||
if (!bitArray.canReadExpGolombCodedNum()) {
|
if (!bitArray.canReadExpGolombCodedNum()) {
|
||||||
@ -480,7 +471,7 @@ public final class H264Reader implements ElementaryStreamReader {
|
|||||||
|
|
||||||
public boolean endNalUnit(
|
public boolean endNalUnit(
|
||||||
long position, int offset, boolean hasOutputFormat, boolean randomAccessIndicator) {
|
long position, int offset, boolean hasOutputFormat, boolean randomAccessIndicator) {
|
||||||
if (nalUnitType == NAL_UNIT_TYPE_AUD
|
if (nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_AUD
|
||||||
|| (detectAccessUnits && sliceHeader.isFirstVclNalUnitOfPicture(previousSliceHeader))) {
|
|| (detectAccessUnits && sliceHeader.isFirstVclNalUnitOfPicture(previousSliceHeader))) {
|
||||||
// If the NAL unit ending is the start of a new sample, output the previous one.
|
// If the NAL unit ending is the start of a new sample, output the previous one.
|
||||||
if (hasOutputFormat && readingSample) {
|
if (hasOutputFormat && readingSample) {
|
||||||
@ -495,8 +486,8 @@ public final class H264Reader implements ElementaryStreamReader {
|
|||||||
boolean treatIFrameAsKeyframe =
|
boolean treatIFrameAsKeyframe =
|
||||||
allowNonIdrKeyframes ? sliceHeader.isISlice() : randomAccessIndicator;
|
allowNonIdrKeyframes ? sliceHeader.isISlice() : randomAccessIndicator;
|
||||||
sampleIsKeyframe |=
|
sampleIsKeyframe |=
|
||||||
nalUnitType == NAL_UNIT_TYPE_IDR
|
nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_IDR
|
||||||
|| (treatIFrameAsKeyframe && nalUnitType == NAL_UNIT_TYPE_NON_IDR);
|
|| (treatIFrameAsKeyframe && nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_NON_IDR);
|
||||||
return sampleIsKeyframe;
|
return sampleIsKeyframe;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,6 +126,7 @@ public final class NalUnitUtilTest {
|
|||||||
public void parseSpsNalUnit() {
|
public void parseSpsNalUnit() {
|
||||||
NalUnitUtil.SpsData data =
|
NalUnitUtil.SpsData data =
|
||||||
NalUnitUtil.parseSpsNalUnit(SPS_TEST_DATA, SPS_TEST_DATA_OFFSET, SPS_TEST_DATA.length);
|
NalUnitUtil.parseSpsNalUnit(SPS_TEST_DATA, SPS_TEST_DATA_OFFSET, SPS_TEST_DATA.length);
|
||||||
|
assertThat(data.maxNumRefFrames).isEqualTo(4);
|
||||||
assertThat(data.width).isEqualTo(640);
|
assertThat(data.width).isEqualTo(640);
|
||||||
assertThat(data.height).isEqualTo(360);
|
assertThat(data.height).isEqualTo(360);
|
||||||
assertThat(data.deltaPicOrderAlwaysZeroFlag).isFalse();
|
assertThat(data.deltaPicOrderAlwaysZeroFlag).isFalse();
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package androidx.media3.session;
|
package androidx.media3.session;
|
||||||
|
|
||||||
|
import static androidx.media3.common.util.Assertions.checkStateNotNull;
|
||||||
|
|
||||||
import android.app.Notification;
|
import android.app.Notification;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -25,6 +27,7 @@ import androidx.core.app.NotificationManagerCompat;
|
|||||||
import androidx.core.content.ContextCompat;
|
import androidx.core.content.ContextCompat;
|
||||||
import androidx.media3.common.Player;
|
import androidx.media3.common.Player;
|
||||||
import androidx.media3.common.util.Util;
|
import androidx.media3.common.util.Util;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -50,6 +53,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
private final Map<MediaSession, ListenableFuture<MediaController>> controllerMap;
|
private final Map<MediaSession, ListenableFuture<MediaController>> controllerMap;
|
||||||
|
|
||||||
private int totalNotificationCount;
|
private int totalNotificationCount;
|
||||||
|
@Nullable private MediaNotification mediaNotification;
|
||||||
|
|
||||||
public MediaNotificationManager(
|
public MediaNotificationManager(
|
||||||
MediaSessionService mediaSessionService,
|
MediaSessionService mediaSessionService,
|
||||||
@ -122,13 +126,13 @@ import java.util.concurrent.TimeoutException;
|
|||||||
|
|
||||||
MediaController mediaController;
|
MediaController mediaController;
|
||||||
try {
|
try {
|
||||||
mediaController = controllerFuture.get(0, TimeUnit.MILLISECONDS);
|
mediaController = checkStateNotNull(Futures.getDone(controllerFuture));
|
||||||
} catch (ExecutionException | InterruptedException | TimeoutException e) {
|
} catch (ExecutionException e) {
|
||||||
// We should never reach this point.
|
// We should never reach this point.
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
int notificationSequence = ++this.totalNotificationCount;
|
int notificationSequence = ++totalNotificationCount;
|
||||||
MediaNotification.Provider.Callback callback =
|
MediaNotification.Provider.Callback callback =
|
||||||
notification ->
|
notification ->
|
||||||
mainExecutor.execute(
|
mainExecutor.execute(
|
||||||
@ -141,45 +145,68 @@ import java.util.concurrent.TimeoutException;
|
|||||||
|
|
||||||
private void onNotificationUpdated(
|
private void onNotificationUpdated(
|
||||||
int notificationSequence, MediaSession session, MediaNotification mediaNotification) {
|
int notificationSequence, MediaSession session, MediaNotification mediaNotification) {
|
||||||
if (notificationSequence == this.totalNotificationCount) {
|
if (notificationSequence == totalNotificationCount) {
|
||||||
updateNotification(session, mediaNotification);
|
updateNotification(session, mediaNotification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateNotification(MediaSession session, MediaNotification mediaNotification) {
|
private void updateNotification(MediaSession session, MediaNotification mediaNotification) {
|
||||||
int id = mediaNotification.notificationId;
|
|
||||||
Notification notification = mediaNotification.notification;
|
|
||||||
|
|
||||||
if (Util.SDK_INT >= 21) {
|
if (Util.SDK_INT >= 21) {
|
||||||
// Call Notification.MediaStyle#setMediaSession() indirectly.
|
// Call Notification.MediaStyle#setMediaSession() indirectly.
|
||||||
android.media.session.MediaSession.Token fwkToken =
|
android.media.session.MediaSession.Token fwkToken =
|
||||||
(android.media.session.MediaSession.Token)
|
(android.media.session.MediaSession.Token)
|
||||||
session.getSessionCompat().getSessionToken().getToken();
|
session.getSessionCompat().getSessionToken().getToken();
|
||||||
notification.extras.putParcelable(Notification.EXTRA_MEDIA_SESSION, fwkToken);
|
mediaNotification.notification.extras.putParcelable(
|
||||||
|
Notification.EXTRA_MEDIA_SESSION, fwkToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.mediaNotification = mediaNotification;
|
||||||
Player player = session.getPlayer();
|
Player player = session.getPlayer();
|
||||||
if (player.getPlayWhenReady()) {
|
if (player.getPlayWhenReady() && canStartPlayback(player)) {
|
||||||
ContextCompat.startForegroundService(mediaSessionService, startSelfIntent);
|
ContextCompat.startForegroundService(mediaSessionService, startSelfIntent);
|
||||||
mediaSessionService.startForeground(id, notification);
|
mediaSessionService.startForeground(
|
||||||
|
mediaNotification.notificationId, mediaNotification.notification);
|
||||||
} else {
|
} else {
|
||||||
stopForegroundServiceIfNeeded();
|
maybeStopForegroundService(/* removeNotifications= */ false);
|
||||||
notificationManagerCompat.notify(id, notification);
|
notificationManagerCompat.notify(
|
||||||
|
mediaNotification.notificationId, mediaNotification.notification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopForegroundServiceIfNeeded() {
|
/**
|
||||||
|
* Stops the service from the foreground, if no player is actively playing content.
|
||||||
|
*
|
||||||
|
* @param removeNotifications Whether to remove notifications, if the service is stopped from the
|
||||||
|
* foreground.
|
||||||
|
*/
|
||||||
|
private void maybeStopForegroundService(boolean removeNotifications) {
|
||||||
List<MediaSession> sessions = mediaSessionService.getSessions();
|
List<MediaSession> sessions = mediaSessionService.getSessions();
|
||||||
for (int i = 0; i < sessions.size(); i++) {
|
for (int i = 0; i < sessions.size(); i++) {
|
||||||
Player player = sessions.get(i).getPlayer();
|
Player player = sessions.get(i).getPlayer();
|
||||||
if (player.getPlayWhenReady()) {
|
if (player.getPlayWhenReady() && canStartPlayback(player)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Calling stopForeground(true) is a workaround for pre-L devices which prevents
|
// To hide the notification on all API levels, we need to call both Service.stopForeground(true)
|
||||||
// the media notification from being undismissable.
|
// and notificationManagerCompat.cancelAll(). For pre-L devices, we must also call
|
||||||
boolean shouldRemoveNotification = Util.SDK_INT < 21;
|
// Service.stopForeground(true) anyway as a workaround that prevents the media notification from
|
||||||
mediaSessionService.stopForeground(shouldRemoveNotification);
|
// being undismissable.
|
||||||
|
mediaSessionService.stopForeground(removeNotifications || Util.SDK_INT < 21);
|
||||||
|
if (removeNotifications && mediaNotification != null) {
|
||||||
|
notificationManagerCompat.cancel(mediaNotification.notificationId);
|
||||||
|
// Update the notification count so that if a pending notification callback arrives (e.g., a
|
||||||
|
// bitmap is loaded), we don't show the notification.
|
||||||
|
totalNotificationCount++;
|
||||||
|
mediaNotification = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether {@code player} can start playback and therefore we should present a
|
||||||
|
* notification for this player.
|
||||||
|
*/
|
||||||
|
private static boolean canStartPlayback(Player player) {
|
||||||
|
return player.getPlaybackState() != Player.STATE_IDLE && !player.getCurrentTimeline().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class MediaControllerListener implements MediaController.Listener, Player.Listener {
|
private final class MediaControllerListener implements MediaController.Listener, Player.Listener {
|
||||||
@ -190,11 +217,20 @@ import java.util.concurrent.TimeoutException;
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onConnected() {
|
public void onConnected() {
|
||||||
|
if (canStartPlayback(session.getPlayer())) {
|
||||||
updateNotification(session);
|
updateNotification(session);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEvents(Player player, Player.Events events) {
|
public void onEvents(Player player, Player.Events events) {
|
||||||
|
if (!canStartPlayback(player)) {
|
||||||
|
maybeStopForegroundService(/* removeNotifications= */ true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Limit the events on which we may update the notification to ensure we don't update the
|
||||||
|
// notification too frequently, otherwise the system may suppress notifications.
|
||||||
if (events.containsAny(
|
if (events.containsAny(
|
||||||
Player.EVENT_PLAYBACK_STATE_CHANGED,
|
Player.EVENT_PLAYBACK_STATE_CHANGED,
|
||||||
Player.EVENT_PLAY_WHEN_READY_CHANGED,
|
Player.EVENT_PLAY_WHEN_READY_CHANGED,
|
||||||
@ -206,7 +242,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
@Override
|
@Override
|
||||||
public void onDisconnected(MediaController controller) {
|
public void onDisconnected(MediaController controller) {
|
||||||
mediaSessionService.removeSession(session);
|
mediaSessionService.removeSession(session);
|
||||||
stopForegroundServiceIfNeeded();
|
maybeStopForegroundService(/* removeNotifications= */ true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user