Merge branch 'main' of github.com:ittiam-systems/media into rtp_wav

This commit is contained in:
Shraddha Basantwani 2022-03-28 11:13:21 +05:30
commit bfc1fb9aa7
155 changed files with 3599 additions and 1813 deletions

View File

@ -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

View File

@ -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'

View File

@ -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);

View File

@ -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 =

View File

@ -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();

View File

@ -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));
} }
} }
} }

View File

@ -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));

View File

@ -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).

View File

@ -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 =

View File

@ -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);

View File

@ -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",

View File

@ -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" >

View File

@ -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>

View File

@ -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];

View File

@ -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();

View File

@ -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";

View File

@ -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 {

View File

@ -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;

View File

@ -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.
* *

View File

@ -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(

View File

@ -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

View File

@ -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.
* *

View File

@ -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.

View File

@ -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);

View File

@ -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;
} }

View File

@ -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) {

View File

@ -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 {

View File

@ -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) {

View File

@ -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(...) );
``` ```

View File

@ -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

View File

@ -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) {

View File

@ -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) {

View File

@ -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

View File

@ -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 {

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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);
}
}
} }
} }

View File

@ -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)

View File

@ -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);
} }

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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());
} }

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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++) {

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
/** /**

View File

@ -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;
}
} }
} }
} }

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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.

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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() {}
} }

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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,

View File

@ -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);

View File

@ -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;
} }

View File

@ -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();

View File

@ -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