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
### 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)
This release corresponds to the

View File

@ -26,7 +26,7 @@ project.ext {
// https://cs.android.com/android/platform/superproject/+/master:external/guava/METADATA
guavaVersion = '31.0.1-android'
mockitoVersion = '3.12.4'
robolectricVersion = '4.6.1'
robolectricVersion = '4.8-alpha-1'
// Keep this in sync with Google's internal Checker Framework version.
checkerframeworkVersion = '3.13.0'
checkerframeworkCompatVersion = '2.5.5'

View File

@ -119,8 +119,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
// Run the shader program.
GlProgram program = checkNotNull(this.program);
program.setSamplerTexIdUniform("uTexSampler0", frameTexture, /* unit= */ 0);
program.setSamplerTexIdUniform("uTexSampler1", textures[0], /* unit= */ 1);
program.setSamplerTexIdUniform("uTexSampler0", frameTexture, /* texUnitIndex= */ 0);
program.setSamplerTexIdUniform("uTexSampler1", textures[0], /* texUnitIndex= */ 1);
program.setFloatUniform("uScaleX", bitmapScaleX);
program.setFloatUniform("uScaleY", bitmapScaleY);
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.DefaultDataSource;
import androidx.media3.datasource.DefaultHttpDataSource;
import androidx.media3.datasource.HttpDataSource;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.dash.DashMediaSource;
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 drmLicenseUrl = Assertions.checkNotNull(intent.getStringExtra(DRM_LICENSE_URL_EXTRA));
UUID drmSchemeUuid = Assertions.checkNotNull(Util.getDrmUuid(drmScheme));
HttpDataSource.Factory licenseDataSourceFactory = new DefaultHttpDataSource.Factory();
DataSource.Factory licenseDataSourceFactory = new DefaultHttpDataSource.Factory();
HttpMediaDrmCallback drmCallback =
new HttpMediaDrmCallback(drmLicenseUrl, licenseDataSourceFactory);
drmSessionManager =

View File

@ -21,7 +21,6 @@ import androidx.media3.database.StandaloneDatabaseProvider;
import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.DefaultDataSource;
import androidx.media3.datasource.DefaultHttpDataSource;
import androidx.media3.datasource.HttpDataSource;
import androidx.media3.datasource.cache.Cache;
import androidx.media3.datasource.cache.CacheDataSource;
import androidx.media3.datasource.cache.NoOpCacheEvictor;
@ -59,7 +58,7 @@ public final class DemoUtil {
private static final String DOWNLOAD_CONTENT_DIRECTORY = "downloads";
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 File downloadDirectory;
private static @MonotonicNonNull Cache downloadCache;
@ -85,7 +84,7 @@ public final class DemoUtil {
.setExtensionRendererMode(extensionRendererMode);
}
public static synchronized HttpDataSource.Factory getHttpDataSourceFactory(Context context) {
public static synchronized DataSource.Factory getHttpDataSourceFactory(Context context) {
if (httpDataSourceFactory == null) {
if (USE_CRONET_FOR_NETWORKING) {
context = context.getApplicationContext();

View File

@ -15,8 +15,7 @@
*/
package androidx.media3.demo.main;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkStateNotNull;
import static com.google.common.base.Preconditions.checkNotNull;
import android.content.Context;
import android.content.DialogInterface;
@ -30,12 +29,11 @@ import androidx.media3.common.DrmInitData;
import androidx.media3.common.Format;
import androidx.media3.common.MediaItem;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.TracksInfo;
import androidx.media3.common.util.Log;
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.drm.DrmSession;
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.DownloadRequest;
import androidx.media3.exoplayer.offline.DownloadService;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.trackselection.MappingTrackSelector.MappedTrackInfo;
import java.io.IOException;
import java.util.HashMap;
@ -66,7 +65,7 @@ public class DownloadTracker {
private static final String TAG = "DownloadTracker";
private final Context context;
private final HttpDataSource.Factory httpDataSourceFactory;
private final DataSource.Factory dataSourceFactory;
private final CopyOnWriteArraySet<Listener> listeners;
private final HashMap<Uri, Download> downloads;
private final DownloadIndex downloadIndex;
@ -74,11 +73,9 @@ public class DownloadTracker {
@Nullable private StartDownloadDialogHelper startDownloadDialogHelper;
public DownloadTracker(
Context context,
HttpDataSource.Factory httpDataSourceFactory,
DownloadManager downloadManager) {
Context context, DataSource.Factory dataSourceFactory, DownloadManager downloadManager) {
this.context = context.getApplicationContext();
this.httpDataSourceFactory = httpDataSourceFactory;
this.dataSourceFactory = dataSourceFactory;
listeners = new CopyOnWriteArraySet<>();
downloads = new HashMap<>();
downloadIndex = downloadManager.getDownloadIndex();
@ -87,8 +84,7 @@ public class DownloadTracker {
}
public void addListener(Listener listener) {
checkNotNull(listener);
listeners.add(listener);
listeners.add(checkNotNull(listener));
}
public void removeListener(Listener listener) {
@ -119,8 +115,7 @@ public class DownloadTracker {
startDownloadDialogHelper =
new StartDownloadDialogHelper(
fragmentManager,
DownloadHelper.forMediaItem(
context, mediaItem, renderersFactory, httpDataSourceFactory),
DownloadHelper.forMediaItem(context, mediaItem, renderersFactory, dataSourceFactory),
mediaItem);
}
}
@ -218,7 +213,7 @@ public class DownloadTracker {
new WidevineOfflineLicenseFetchTask(
format,
mediaItem.localConfiguration.drmConfiguration,
httpDataSourceFactory,
dataSourceFactory,
/* dialogHelper= */ this,
helper);
widevineOfflineLicenseFetchTask.execute();
@ -361,7 +356,7 @@ public class DownloadTracker {
private final Format format;
private final MediaItem.DrmConfiguration drmConfiguration;
private final HttpDataSource.Factory httpDataSourceFactory;
private final DataSource.Factory dataSourceFactory;
private final StartDownloadDialogHelper dialogHelper;
private final DownloadHelper downloadHelper;
@ -371,12 +366,12 @@ public class DownloadTracker {
public WidevineOfflineLicenseFetchTask(
Format format,
MediaItem.DrmConfiguration drmConfiguration,
HttpDataSource.Factory httpDataSourceFactory,
DataSource.Factory dataSourceFactory,
StartDownloadDialogHelper dialogHelper,
DownloadHelper downloadHelper) {
this.format = format;
this.drmConfiguration = drmConfiguration;
this.httpDataSourceFactory = httpDataSourceFactory;
this.dataSourceFactory = dataSourceFactory;
this.dialogHelper = dialogHelper;
this.downloadHelper = downloadHelper;
}
@ -387,7 +382,7 @@ public class DownloadTracker {
OfflineLicenseHelper.newWidevineInstance(
drmConfiguration.licenseUri.toString(),
drmConfiguration.forceDefaultLicenseUri,
httpDataSourceFactory,
dataSourceFactory,
drmConfiguration.licenseRequestHeaders,
new DrmSessionEventListener.EventDispatcher());
try {
@ -405,7 +400,7 @@ public class DownloadTracker {
if (drmSessionException != null) {
dialogHelper.onOfflineLicenseFetchedError(drmSessionException);
} else {
dialogHelper.onOfflineLicenseFetched(downloadHelper, checkStateNotNull(keySetId));
dialogHelper.onOfflineLicenseFetched(downloadHelper, checkNotNull(keySetId));
}
}
}

View File

@ -15,8 +15,9 @@
*/
package androidx.media3.demo.main;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import android.content.Intent;
import android.net.Uri;
@ -26,7 +27,6 @@ import androidx.media3.common.MediaItem;
import androidx.media3.common.MediaItem.ClippingConfiguration;
import androidx.media3.common.MediaItem.SubtitleConfiguration;
import androidx.media3.common.MediaMetadata;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Util;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
@ -86,7 +86,7 @@ public class IntentUtil {
/** Populates the intent with the given list of {@link MediaItem media items}. */
public static void addToIntent(List<MediaItem> mediaItems, Intent intent) {
Assertions.checkArgument(!mediaItems.isEmpty());
checkArgument(!mediaItems.isEmpty());
if (mediaItems.size() == 1) {
MediaItem mediaItem = mediaItems.get(0);
MediaItem.LocalConfiguration localConfiguration = checkNotNull(mediaItem.localConfiguration);
@ -241,7 +241,7 @@ public class IntentUtil {
drmConfiguration.forcedSessionTrackTypes;
if (!forcedDrmSessionTrackTypes.isEmpty()) {
// Only video and audio together are supported.
Assertions.checkState(
checkState(
forcedDrmSessionTrackTypes.size() == 2
&& forcedDrmSessionTrackTypes.contains(C.TRACK_TYPE_VIDEO)
&& forcedDrmSessionTrackTypes.contains(C.TRACK_TYPE_AUDIO));

View File

@ -15,9 +15,9 @@
*/
package androidx.media3.demo.main;
import static androidx.media3.common.util.Assertions.checkArgument;
import static androidx.media3.common.util.Assertions.checkNotNull;
import static androidx.media3.common.util.Assertions.checkState;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import android.content.Context;
import android.content.Intent;
@ -116,8 +116,11 @@ public class SampleChooserActivity extends AppCompatActivity
useExtensionRenderers = DemoUtil.useExtensionRenderers();
downloadTracker = DemoUtil.getDownloadTracker(/* context= */ this);
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
// action. Starting it in the background throws an exception if the app is in the background too
// (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.DefaultDataSource;
import androidx.media3.datasource.DefaultHttpDataSource;
import androidx.media3.datasource.HttpDataSource;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.dash.DashMediaSource;
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 drmLicenseUrl = Assertions.checkNotNull(intent.getStringExtra(DRM_LICENSE_URL_EXTRA));
UUID drmSchemeUuid = Assertions.checkNotNull(Util.getDrmUuid(drmScheme));
HttpDataSource.Factory licenseDataSourceFactory = new DefaultHttpDataSource.Factory();
DataSource.Factory licenseDataSourceFactory = new DefaultHttpDataSource.Factory();
HttpMediaDrmCallback drmCallback =
new HttpMediaDrmCallback(drmLicenseUrl, licenseDataSourceFactory);
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 VIDEO_MIME_TYPE = "video_mime_type";
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_Y = "scale_y";
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 videoMimeSpinner;
private @MonotonicNonNull Spinner resolutionHeightSpinner;
private @MonotonicNonNull Spinner translateSpinner;
private @MonotonicNonNull Spinner scaleSpinner;
private @MonotonicNonNull Spinner rotateSpinner;
private @MonotonicNonNull CheckBox enableFallbackCheckBox;
@ -136,14 +133,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
resolutionHeightAdapter.addAll(
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 =
new ArrayAdapter<>(/* context= */ this, R.layout.spinner_item);
scaleAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
@ -185,7 +174,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
"audioMimeSpinner",
"videoMimeSpinner",
"resolutionHeightSpinner",
"translateSpinner",
"scaleSpinner",
"rotateSpinner",
"enableFallbackCheckBox",
@ -209,13 +197,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
if (!SAME_AS_INPUT_OPTION.equals(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());
if (!SAME_AS_INPUT_OPTION.equals(selectedScale)) {
List<String> scaleXY = Arrays.asList(selectedScale.split(", "));
@ -258,7 +239,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
"audioMimeSpinner",
"videoMimeSpinner",
"resolutionHeightSpinner",
"translateSpinner",
"scaleSpinner",
"rotateSpinner",
"enableHdrEditingCheckBox"
@ -277,7 +257,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
"audioMimeSpinner",
"videoMimeSpinner",
"resolutionHeightSpinner",
"translateSpinner",
"scaleSpinner",
"rotateSpinner",
"enableHdrEditingCheckBox"
@ -295,7 +274,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
"audioMimeSpinner",
"videoMimeSpinner",
"resolutionHeightSpinner",
"translateSpinner",
"scaleSpinner",
"rotateSpinner",
"enableHdrEditingCheckBox"
@ -304,7 +282,6 @@ public final class ConfigurationActivity extends AppCompatActivity {
audioMimeSpinner.setEnabled(isAudioEnabled);
videoMimeSpinner.setEnabled(isVideoEnabled);
resolutionHeightSpinner.setEnabled(isVideoEnabled);
translateSpinner.setEnabled(isVideoEnabled);
scaleSpinner.setEnabled(isVideoEnabled);
rotateSpinner.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.video_mime_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.rotate).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.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Matrix;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@ -217,10 +216,15 @@ public final class TransformerActivity extends AppCompatActivity {
if (resolutionHeight != C.LENGTH_UNSET) {
requestBuilder.setResolution(resolutionHeight);
}
Matrix transformationMatrix = getTransformationMatrix(bundle);
if (!transformationMatrix.isIdentity()) {
requestBuilder.setTransformationMatrix(transformationMatrix);
}
float scaleX = bundle.getFloat(ConfigurationActivity.SCALE_X, /* defaultValue= */ 1);
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(
bundle.getBoolean(ConfigurationActivity.ENABLE_HDR_EDITING));
transformerBuilder
@ -251,27 +255,6 @@ public final class TransformerActivity extends AppCompatActivity {
.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({
"informationTextView",
"progressViewGroup",

View File

@ -137,17 +137,6 @@
android:layout_gravity="right|center_vertical"
android:gravity="right" />
</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
android:layout_weight="1"
android:gravity="center_vertical" >

View File

@ -24,7 +24,6 @@
<string name="audio_mime" translatable="false">Output audio 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="translate" translatable="false">Translate video</string>
<string name="scale" translatable="false">Scale video</string>
<string name="rotate" translatable="false">Rotate video (degrees)</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 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[] EMPTY_TRACK_ID_ARRAY = new long[0];

View File

@ -683,11 +683,12 @@ public interface Player {
/**
* Called when the combined {@link MediaMetadata} changes.
*
* <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
* selections' formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in
* the {@link MediaItem#mediaMetadata}, it will be prioritised above the same field coming from
* static or dynamic metadata.
* <p>The provided {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata
* MediaItem metadata}, the static metadata in the media's {@link Format#metadata Format}, and
* any timed metadata that has been parsed from the media and output via {@link
* Listener#onMetadata(Metadata)}. If a field is populated in the {@link
* 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.
*
@ -2123,11 +2124,11 @@ public interface Player {
* Returns the current combined {@link MediaMetadata}, or {@link MediaMetadata#EMPTY} if not
* supported.
*
* <p>This {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata} and the
* static and dynamic metadata from the {@link TrackSelection#getFormat(int) track selections'
* formats} and {@link Listener#onMetadata(Metadata)}. If a field is populated in the {@link
* MediaItem#mediaMetadata}, it will be prioritised above the same field coming from static or
* dynamic metadata.
* <p>This {@link MediaMetadata} is a combination of the {@link MediaItem#mediaMetadata MediaItem
* metadata}, the static metadata in the media's {@link Format#metadata Format}, and any timed
* metadata that has been parsed from the media and output via {@link
* Listener#onMetadata(Metadata)}. If a field is populated in the {@link MediaItem#mediaMetadata},
* it will be prioritised above the same field coming from static or timed metadata.
*/
MediaMetadata getMediaMetadata();

View File

@ -34,7 +34,21 @@ import java.lang.annotation.Target;
import java.util.Arrays;
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 {
private static final String TAG = "TrackGroup";

View File

@ -31,16 +31,21 @@ import java.lang.annotation.RetentionPolicy;
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
* depending on the player capabilities.
* <p>A track selection override is applied during playback if the media being played contains a
* {@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
* similar to {@link TrackSelectionParameters#disabledTrackTypes}, except it will only affect the
* playback of the associated {@link TrackGroup}. For example, if the only {@link
* C#TRACK_TYPE_VIDEO} {@link TrackGroup} is associated with no tracks, no video will play until the
* next video starts.
* <p>If {@link #trackIndices} is empty then the override specifies that no tracks should be
* selected. Adding an empty override to a {@link TrackSelectionParameters} is similar to {@link
* TrackSelectionParameters.Builder#setTrackTypeDisabled disabling a track type}, except that an
* empty override will only be applied if the media being played contains a {@link TrackGroup} equal
* 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 {

View File

@ -47,10 +47,11 @@ import org.checkerframework.checker.initialization.qual.UnknownInitialization;
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
* SD, and to select a German audio track if there is one:
* <p>Parameters can be queried and set on a {@link Player}. For example the following code modifies
* the parameters to restrict video track selections to SD, and to select a German audio track if
* there is one:
*
* <pre>{@code
* // Build on the current parameters.
@ -656,28 +657,26 @@ public class TrackSelectionParameters implements Bundleable {
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) {
overrides.put(override.trackGroup, override);
return this;
}
/** Removes the override associated with the provided {@link TrackGroup} if present. */
public Builder clearOverride(TrackGroup trackGroup) {
overrides.remove(trackGroup);
return this;
}
/** Set the override for the type of the provided {@link TrackGroup}. */
/** Sets an override, replacing all existing overrides with the same track type. */
public Builder setOverrideForType(TrackSelectionOverride override) {
clearOverridesOfType(override.getTrackType());
overrides.put(override.trackGroup, override);
return this;
}
/**
* Remove any override associated with {@link TrackGroup TrackGroups} of type {@code trackType}.
*/
/** Removes the override for the provided {@link TrackGroup}, if there is one. */
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) {
Iterator<TrackSelectionOverride> it = overrides.values().iterator();
while (it.hasNext()) {
@ -689,7 +688,7 @@ public class TrackSelectionParameters implements Bundleable {
return this;
}
/** Removes all track overrides. */
/** Removes all overrides. */
public Builder clearOverrides() {
overrides.clear();
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
* {@link C.TrackType type} of tracks it contains, and the level to which each track is supported
* by the player.
* level to which each track is supported by the player, and whether any of the tracks are
* selected.
*/
public static final class TrackGroupInfo implements Bundleable {
@ -55,26 +55,26 @@ public final class TracksInfo implements Bundleable {
private final boolean[] trackSelected;
/**
* Constructs a TrackGroupInfo.
* Constructs an instance.
*
* @param trackGroup The {@link TrackGroup} described.
* @param adaptiveSupported Whether adaptive selections containing more than one track in the
* {@code trackGroup} are supported.
* @param trackSupport The {@link C.FormatSupport} of each track in the {@code trackGroup}.
* @param tracksSelected Whether each track in the {@code trackGroup} is selected.
* @param trackGroup The underlying {@link TrackGroup}.
* @param adaptiveSupported Whether the player supports adaptive selections containing more than
* one track in the group.
* @param trackSupport The {@link C.FormatSupport} of each track in the group.
* @param trackSelected Whether each track in the {@code trackGroup} is selected.
*/
@UnstableApi
public TrackGroupInfo(
TrackGroup trackGroup,
boolean adaptiveSupported,
@C.FormatSupport int[] trackSupport,
boolean[] tracksSelected) {
boolean[] trackSelected) {
length = trackGroup.length;
checkArgument(length == trackSupport.length && length == tracksSelected.length);
checkArgument(length == trackSupport.length && length == trackSelected.length);
this.trackGroup = trackGroup;
this.adaptiveSupported = adaptiveSupported && length > 1;
this.trackSupport = trackSupport.clone();
this.trackSelected = tracksSelected.clone();
this.trackSelected = trackSelected.clone();
}
/** 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. */
@UnstableApi public static final TracksInfo EMPTY = new TracksInfo(ImmutableList.of());
private final ImmutableList<TrackGroupInfo> trackGroupInfos;
/**
* 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.
*/
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);
GlUtil.checkGlError();
}
@ -175,9 +173,16 @@ public final class GlProgram {
checkNotNull(attributeByName.get(name)).setBuffer(values, size);
}
/** Sets a texture sampler type uniform. */
public void setSamplerTexIdUniform(String name, int texId, int unit) {
checkNotNull(uniformByName.get(name)).setSamplerTexId(texId, unit);
/**
* Sets a texture sampler type uniform.
*
* @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. */
@ -322,7 +327,7 @@ public final class GlProgram {
private final float[] value;
private int texId;
private int unit;
private int texUnitIndex;
private Uniform(String name, int location, int type) {
this.name = name;
@ -335,11 +340,11 @@ public final class GlProgram {
* Configures {@link #bind()} to use the specified {@code texId} for this sampler uniform.
*
* @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.unit = unit;
this.texUnitIndex = texUnitIndex;
}
/** Configures {@link #bind()} to use the specified float {@code value} for this uniform. */
@ -382,7 +387,7 @@ public final class GlProgram {
if (texId == 0) {
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) {
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texId);
} else if (type == GLES20.GL_SAMPLER_2D) {
@ -390,7 +395,7 @@ public final class GlProgram {
} else {
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_MIN_FILTER, GLES20.GL_LINEAR);
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.
* 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) {
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
* {@code height} pixels.
@ -320,6 +345,7 @@ public final class GlUtil {
* @param height of the new texture in pixels
*/
public static int createTexture(int width, int height) {
assertValidTextureSize(width, height);
int texId = generateAndBindTexture(GLES20.GL_TEXTURE_2D);
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(width * height * 4);
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)
private static final class Api17 {
private Api17() {}
@ -438,12 +469,15 @@ public final class GlUtil {
Object surface,
int[] configAttributes,
int[] windowSurfaceAttributes) {
return EGL14.eglCreateWindowSurface(
eglDisplay,
getEglConfig(eglDisplay, configAttributes),
surface,
windowSurfaceAttributes,
/* offset= */ 0);
EGLSurface eglSurface =
EGL14.eglCreateWindowSurface(
eglDisplay,
getEglConfig(eglDisplay, configAttributes),
surface,
windowSurfaceAttributes,
/* offset= */ 0);
checkEglException("Error creating surface");
return eglSurface;
}
@DoNotInline
@ -459,8 +493,11 @@ public final class GlUtil {
if (boundFramebuffer[0] != framebuffer) {
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, framebuffer);
}
checkGlError();
EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
checkEglException("Error making context current");
GLES20.glViewport(/* x= */ 0, /* y= */ 0, width, height);
checkGlError();
}
@DoNotInline
@ -471,19 +508,15 @@ public final class GlUtil {
}
EGL14.eglMakeCurrent(
eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
int error = EGL14.eglGetError();
checkEglException(error == EGL14.EGL_SUCCESS, "Error releasing context: " + error);
checkEglException("Error releasing context");
if (eglContext != null) {
EGL14.eglDestroyContext(eglDisplay, eglContext);
error = EGL14.eglGetError();
checkEglException(error == EGL14.EGL_SUCCESS, "Error destroying context: " + error);
checkEglException("Error destroying context");
}
EGL14.eglReleaseThread();
error = EGL14.eglGetError();
checkEglException(error == EGL14.EGL_SUCCESS, "Error releasing thread: " + error);
checkEglException("Error releasing thread");
EGL14.eglTerminate(eglDisplay);
error = EGL14.eglGetError();
checkEglException(error == EGL14.EGL_SUCCESS, "Error terminating display: " + error);
checkEglException("Error terminating display");
}
@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);
}
/**
* 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.
*

View File

@ -22,6 +22,7 @@ import static android.graphics.Color.argb;
import static android.graphics.Color.parseColor;
import static androidx.media3.common.util.ColorParser.parseTtmlColor;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import android.graphics.Color;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@ -34,24 +35,26 @@ public final class ColorParserTest {
// Negative tests.
@Test(expected = IllegalArgumentException.class)
@Test
public void parseUnknownColor() {
ColorParser.parseTtmlColor("colorOfAnElectron");
assertThrows(
IllegalArgumentException.class, () -> ColorParser.parseTtmlColor("colorOfAnElectron"));
}
@Test(expected = IllegalArgumentException.class)
@Test
public void parseNull() {
ColorParser.parseTtmlColor(null);
assertThrows(IllegalArgumentException.class, () -> ColorParser.parseTtmlColor(null));
}
@Test(expected = IllegalArgumentException.class)
@Test
public void parseEmpty() {
ColorParser.parseTtmlColor("");
assertThrows(IllegalArgumentException.class, () -> ColorParser.parseTtmlColor(""));
}
@Test(expected = IllegalArgumentException.class)
@Test
public void rgbColorParsingRgbValuesNegative() {
ColorParser.parseTtmlColor("rgb(-4, 55, 209)");
assertThrows(
IllegalArgumentException.class, () -> ColorParser.parseTtmlColor("rgb(-4, 55, 209)"));
}
// Positive tests.

View File

@ -48,6 +48,7 @@ public abstract class BaseDataSource implements DataSource {
this.listeners = new ArrayList<>(/* initialCapacity= */ 1);
}
@UnstableApi
@Override
public final void addTransferListener(TransferListener transferListener) {
checkNotNull(transferListener);

View File

@ -26,13 +26,13 @@ import java.util.List;
import java.util.Map;
/** Reads data from URI-identified resources. */
@UnstableApi
public interface DataSource extends DataReader {
/** A factory for {@link DataSource} instances. */
interface Factory {
/** Creates a {@link DataSource} instance. */
@UnstableApi
DataSource createDataSource();
}
@ -41,6 +41,7 @@ public interface DataSource extends DataReader {
*
* @param transferListener A {@link TransferListener}.
*/
@UnstableApi
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
* {@link DataSpec#length}.
*/
@UnstableApi
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.
*/
@UnstableApi
@Nullable
Uri getUri();
@ -91,6 +94,7 @@ public interface DataSource extends DataReader {
*
* <p>Key look-up in the returned map is case-insensitive.
*/
@UnstableApi
default Map<String, List<String>> getResponseHeaders() {
return Collections.emptyMap();
}
@ -101,5 +105,6 @@ public interface DataSource extends DataReader {
*
* @throws IOException If an error occurs closing the source.
*/
@UnstableApi
void close() throws IOException;
}

View File

@ -54,7 +54,6 @@ import java.util.Map;
* #DefaultDataSource(Context, DataSource)}.
* </ul>
*/
@UnstableApi
public final class DefaultDataSource implements DataSource {
/** {@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.
* @return This factory.
*/
@UnstableApi
public Factory setTransferListener(@Nullable TransferListener transferListener) {
this.transferListener = transferListener;
return this;
}
@UnstableApi
@Override
public DefaultDataSource createDataSource() {
DefaultDataSource dataSource =
@ -144,6 +145,7 @@ public final class DefaultDataSource implements DataSource {
*
* @param context A context.
*/
@UnstableApi
public DefaultDataSource(Context context, boolean allowCrossProtocolRedirects) {
this(
context,
@ -162,6 +164,7 @@ public final class DefaultDataSource implements DataSource {
* @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
* to HTTPS and vice versa) are enabled when fetching remote data.
*/
@UnstableApi
public DefaultDataSource(
Context context, @Nullable String userAgent, boolean allowCrossProtocolRedirects) {
this(
@ -185,6 +188,7 @@ public final class DefaultDataSource implements DataSource {
* @param allowCrossProtocolRedirects Whether cross-protocol redirects (i.e. redirects from HTTP
* to HTTPS and vice versa) are enabled when fetching remote data.
*/
@UnstableApi
public DefaultDataSource(
Context context,
@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
* content. This {@link DataSource} should normally support at least http(s).
*/
@UnstableApi
public DefaultDataSource(Context context, DataSource baseDataSource) {
this.context = context.getApplicationContext();
this.baseDataSource = Assertions.checkNotNull(baseDataSource);
transferListeners = new ArrayList<>();
}
@UnstableApi
@Override
public void addTransferListener(TransferListener transferListener) {
Assertions.checkNotNull(transferListener);
@ -229,6 +235,7 @@ public final class DefaultDataSource implements DataSource {
maybeAddListenerToDataSource(rawResourceDataSource, transferListener);
}
@UnstableApi
@Override
public long open(DataSpec dataSpec) throws IOException {
Assertions.checkState(dataSource == null);
@ -260,22 +267,26 @@ public final class DefaultDataSource implements DataSource {
return dataSource.open(dataSpec);
}
@UnstableApi
@Override
public int read(byte[] buffer, int offset, int length) throws IOException {
return Assertions.checkNotNull(dataSource).read(buffer, offset, length);
}
@UnstableApi
@Override
@Nullable
public Uri getUri() {
return dataSource == null ? null : dataSource.getUri();
}
@UnstableApi
@Override
public Map<String, List<String>> getResponseHeaders() {
return dataSource == null ? Collections.emptyMap() : dataSource.getResponseHeaders();
}
@UnstableApi
@Override
public void close() throws IOException {
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
* be passed to {@link HttpDataSource.Factory#setDefaultRequestProperties(Map)}.
*/
@UnstableApi
public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSource {
/** {@link DataSource.Factory} for {@link DefaultHttpDataSource} instances. */
@ -83,6 +82,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS;
}
@UnstableApi
@Override
public final Factory setDefaultRequestProperties(Map<String, String> defaultRequestProperties) {
this.defaultRequestProperties.clearAndSet(defaultRequestProperties);
@ -99,6 +99,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
* agent of the underlying platform.
* @return This factory.
*/
@UnstableApi
public Factory setUserAgent(@Nullable String userAgent) {
this.userAgent = userAgent;
return this;
@ -112,6 +113,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
* @param connectTimeoutMs The connect timeout, in milliseconds, that will be used.
* @return This factory.
*/
@UnstableApi
public Factory setConnectTimeoutMs(int connectTimeoutMs) {
this.connectTimeoutMs = connectTimeoutMs;
return this;
@ -125,6 +127,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
* @param readTimeoutMs The connect timeout, in milliseconds, that will be used.
* @return This factory.
*/
@UnstableApi
public Factory setReadTimeoutMs(int readTimeoutMs) {
this.readTimeoutMs = readTimeoutMs;
return this;
@ -138,6 +141,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
* @param allowCrossProtocolRedirects Whether to allow cross protocol redirects.
* @return This factory.
*/
@UnstableApi
public Factory setAllowCrossProtocolRedirects(boolean allowCrossProtocolRedirects) {
this.allowCrossProtocolRedirects = allowCrossProtocolRedirects;
return this;
@ -154,6 +158,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
* predicate that was previously set.
* @return This factory.
*/
@UnstableApi
public Factory setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
this.contentTypePredicate = contentTypePredicate;
return this;
@ -169,6 +174,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
* @param transferListener The listener that will be used.
* @return This factory.
*/
@UnstableApi
public Factory setTransferListener(@Nullable TransferListener transferListener) {
this.transferListener = transferListener;
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
* POST request.
*/
@UnstableApi
public Factory setKeepPostFor302Redirects(boolean keepPostFor302Redirects) {
this.keepPostFor302Redirects = keepPostFor302Redirects;
return this;
}
@UnstableApi
@Override
public DefaultHttpDataSource createDataSource() {
DefaultHttpDataSource dataSource =
@ -202,9 +210,9 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
}
/** 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. */
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 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.
*/
@UnstableApi
@SuppressWarnings("deprecation")
@Deprecated
public DefaultHttpDataSource() {
@ -241,6 +250,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
/**
* @deprecated Use {@link DefaultHttpDataSource.Factory} instead.
*/
@UnstableApi
@SuppressWarnings("deprecation")
@Deprecated
public DefaultHttpDataSource(@Nullable String userAgent) {
@ -250,6 +260,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
/**
* @deprecated Use {@link DefaultHttpDataSource.Factory} instead.
*/
@UnstableApi
@SuppressWarnings("deprecation")
@Deprecated
public DefaultHttpDataSource(
@ -265,6 +276,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
/**
* @deprecated Use {@link DefaultHttpDataSource.Factory} instead.
*/
@UnstableApi
@Deprecated
public DefaultHttpDataSource(
@Nullable String userAgent,
@ -305,22 +317,26 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
* @deprecated Use {@link DefaultHttpDataSource.Factory#setContentTypePredicate(Predicate)}
* instead.
*/
@UnstableApi
@Deprecated
public void setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
this.contentTypePredicate = contentTypePredicate;
}
@UnstableApi
@Override
@Nullable
public Uri getUri() {
return connection == null ? null : Uri.parse(connection.getURL().toString());
}
@UnstableApi
@Override
public int getResponseCode() {
return connection == null || responseCode <= 0 ? -1 : responseCode;
}
@UnstableApi
@Override
public Map<String, List<String>> getResponseHeaders() {
if (connection == null) {
@ -337,6 +353,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
return new NullFilteringHeadersMap(connection.getHeaderFields());
}
@UnstableApi
@Override
public void setRequestProperty(String name, String value) {
checkNotNull(name);
@ -344,18 +361,21 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
requestProperties.set(name, value);
}
@UnstableApi
@Override
public void clearRequestProperty(String name) {
checkNotNull(name);
requestProperties.remove(name);
}
@UnstableApi
@Override
public void clearAllRequestProperties() {
requestProperties.clear();
}
/** Opens the source to read the specified data. */
@UnstableApi
@Override
public long open(DataSpec dataSpec) throws HttpDataSourceException {
this.dataSpec = dataSpec;
@ -474,6 +494,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
return bytesToRead;
}
@UnstableApi
@Override
public int read(byte[] buffer, int offset, int length) throws HttpDataSourceException {
try {
@ -484,6 +505,7 @@ public class DefaultHttpDataSource extends BaseDataSource implements HttpDataSou
}
}
@UnstableApi
@Override
public void close() throws HttpDataSourceException {
try {

View File

@ -172,6 +172,7 @@ public interface HttpDataSource extends DataSource {
}
/** A {@link Predicate} that rejects content types often used for pay-walls. */
@UnstableApi
Predicate<String> REJECT_PAYWALL_TYPES =
contentType -> {
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
application code to use `CronetDataSource.Factory`. If your application also
needs to play non-http(s) content such as local files, use:
```
new DefaultDataSourceFactory(
new DefaultDataSource.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
* construct the instance.
*/
@UnstableApi
public class CronetDataSource extends BaseDataSource implements HttpDataSource {
static {
@ -132,6 +131,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
* CronetEngine}, or {@link DefaultHttpDataSource} for cases where {@link
* CronetEngineWrapper#getCronetEngine()} would have returned {@code null}.
*/
@UnstableApi
@Deprecated
public Factory(CronetEngineWrapper cronetEngineWrapper, Executor executor) {
this.cronetEngine = cronetEngineWrapper.getCronetEngine();
@ -142,6 +142,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS;
}
@UnstableApi
@Override
public final Factory setDefaultRequestProperties(Map<String, String> defaultRequestProperties) {
this.defaultRequestProperties.clearAndSet(defaultRequestProperties);
@ -161,6 +162,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
* agent of the underlying {@link CronetEngine}.
* @return This factory.
*/
@UnstableApi
public Factory setUserAgent(@Nullable String userAgent) {
this.userAgent = userAgent;
if (internalFallbackFactory != null) {
@ -179,6 +181,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
* UrlRequest.Builder#REQUEST_PRIORITY_*} constants.
* @return This factory.
*/
@UnstableApi
public Factory setRequestPriority(int requestPriority) {
this.requestPriority = requestPriority;
return this;
@ -192,6 +195,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
* @param connectTimeoutMs The connect timeout, in milliseconds, that will be used.
* @return This factory.
*/
@UnstableApi
public Factory setConnectionTimeoutMs(int connectTimeoutMs) {
this.connectTimeoutMs = connectTimeoutMs;
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.
* @return This factory.
*/
@UnstableApi
public Factory setResetTimeoutOnRedirects(boolean resetTimeoutOnRedirects) {
this.resetTimeoutOnRedirects = resetTimeoutOnRedirects;
return this;
@ -223,6 +228,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
* to the redirect url in the "Cookie" header.
* @return This factory.
*/
@UnstableApi
public Factory setHandleSetCookieRequests(boolean handleSetCookieRequests) {
this.handleSetCookieRequests = handleSetCookieRequests;
return this;
@ -236,6 +242,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
* @param readTimeoutMs The connect timeout, in milliseconds, that will be used.
* @return This factory.
*/
@UnstableApi
public Factory setReadTimeoutMs(int readTimeoutMs) {
this.readTimeoutMs = readTimeoutMs;
if (internalFallbackFactory != null) {
@ -254,6 +261,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
* predicate that was previously set.
* @return This factory.
*/
@UnstableApi
public Factory setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
this.contentTypePredicate = contentTypePredicate;
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
* POST request.
*/
@UnstableApi
public Factory setKeepPostFor302Redirects(boolean keepPostFor302Redirects) {
this.keepPostFor302Redirects = keepPostFor302Redirects;
if (internalFallbackFactory != null) {
@ -284,6 +293,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
* @param transferListener The listener that will be used.
* @return This factory.
*/
@UnstableApi
public Factory setTransferListener(@Nullable TransferListener transferListener) {
this.transferListener = transferListener;
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
* {@link CronetEngine} is not available. Use the fallback factory directly in such cases.
*/
@UnstableApi
@Deprecated
public Factory setFallbackFactory(@Nullable HttpDataSource.Factory fallbackFactory) {
this.fallbackFactory = fallbackFactory;
return this;
}
@UnstableApi
@Override
public HttpDataSource createDataSource() {
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}. */
@UnstableApi
public static final class OpenException extends HttpDataSourceException {
/**
@ -389,9 +402,9 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
}
/** 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. */
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;
@ -436,6 +449,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
private volatile long currentConnectTimeoutMs;
@UnstableApi
protected CronetDataSource(
CronetEngine cronetEngine,
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
* predicate that was previously set.
*/
@UnstableApi
@Deprecated
public void setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
this.contentTypePredicate = contentTypePredicate;
@ -480,21 +495,25 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
// HttpDataSource implementation.
@UnstableApi
@Override
public void setRequestProperty(String name, String value) {
requestProperties.set(name, value);
}
@UnstableApi
@Override
public void clearRequestProperty(String name) {
requestProperties.remove(name);
}
@UnstableApi
@Override
public void clearAllRequestProperties() {
requestProperties.clear();
}
@UnstableApi
@Override
public int getResponseCode() {
return responseInfo == null || responseInfo.getHttpStatusCode() <= 0
@ -502,17 +521,20 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
: responseInfo.getHttpStatusCode();
}
@UnstableApi
@Override
public Map<String, List<String>> getResponseHeaders() {
return responseInfo == null ? Collections.emptyMap() : responseInfo.getAllHeaders();
}
@UnstableApi
@Override
@Nullable
public Uri getUri() {
return responseInfo == null ? null : Uri.parse(responseInfo.getUrl());
}
@UnstableApi
@Override
public long open(DataSpec dataSpec) throws HttpDataSourceException {
Assertions.checkNotNull(dataSpec);
@ -644,6 +666,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
return bytesRemaining;
}
@UnstableApi
@Override
public int read(byte[] buffer, int offset, int length) throws HttpDataSourceException {
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 IllegalArgumentException If {@code buffer} is not a direct ByteBuffer.
*/
@UnstableApi
public int read(ByteBuffer buffer) throws HttpDataSourceException {
Assertions.checkState(opened);
@ -759,6 +783,7 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
return bytesRead;
}
@UnstableApi
@Override
public synchronized void close() {
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. */
@UnstableApi
@Nullable
protected UrlRequest getCurrentUrlRequest() {
return currentUrlRequest;
}
/** Returns current {@link UrlResponseInfo}. May be null if the data source is not opened. */
@UnstableApi
@Nullable
protected UrlResponseInfo getCurrentUrlResponseInfo() {
return responseInfo;
}
@UnstableApi
protected UrlRequest.Builder buildRequestBuilder(DataSpec dataSpec) throws IOException {
UrlRequest.Builder requestBuilder =
cronetEngine

View File

@ -31,7 +31,6 @@ import org.chromium.net.CronetEngine;
import org.chromium.net.CronetProvider;
/** Cronet utility methods. */
@UnstableApi
public final class CronetUtil {
private static final String TAG = "CronetUtil";
@ -77,6 +76,7 @@ public final class CronetUtil {
* over Cronet Embedded, if both are available.
* @return The {@link CronetEngine}, or {@code null} if no suitable engine could be built.
*/
@UnstableApi
@Nullable
public static CronetEngine buildCronetEngine(
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
* construct the instance.
*/
@UnstableApi
public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
static {
@ -89,6 +88,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
defaultRequestProperties = new RequestProperties();
}
@UnstableApi
@Override
public final Factory setDefaultRequestProperties(Map<String, String> defaultRequestProperties) {
this.defaultRequestProperties.clearAndSet(defaultRequestProperties);
@ -105,6 +105,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
* agent of the underlying {@link OkHttpClient}.
* @return This factory.
*/
@UnstableApi
public Factory setUserAgent(@Nullable String userAgent) {
this.userAgent = userAgent;
return this;
@ -118,6 +119,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
* @param cacheControl The cache control that will be used.
* @return This factory.
*/
@UnstableApi
public Factory setCacheControl(@Nullable CacheControl cacheControl) {
this.cacheControl = cacheControl;
return this;
@ -134,6 +136,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
* predicate that was previously set.
* @return This factory.
*/
@UnstableApi
public Factory setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
this.contentTypePredicate = contentTypePredicate;
return this;
@ -149,11 +152,13 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
* @param transferListener The listener that will be used.
* @return This factory.
*/
@UnstableApi
public Factory setTransferListener(@Nullable TransferListener transferListener) {
this.transferListener = transferListener;
return this;
}
@UnstableApi
@Override
public OkHttpDataSource createDataSource() {
OkHttpDataSource dataSource =
@ -185,6 +190,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
* @deprecated Use {@link OkHttpDataSource.Factory} instead.
*/
@SuppressWarnings("deprecation")
@UnstableApi
@Deprecated
public OkHttpDataSource(Call.Factory callFactory) {
this(callFactory, /* userAgent= */ null);
@ -194,6 +200,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
* @deprecated Use {@link OkHttpDataSource.Factory} instead.
*/
@SuppressWarnings("deprecation")
@UnstableApi
@Deprecated
public OkHttpDataSource(Call.Factory callFactory, @Nullable String userAgent) {
this(callFactory, userAgent, /* cacheControl= */ null, /* defaultRequestProperties= */ null);
@ -202,6 +209,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
/**
* @deprecated Use {@link OkHttpDataSource.Factory} instead.
*/
@UnstableApi
@Deprecated
public OkHttpDataSource(
Call.Factory callFactory,
@ -234,27 +242,32 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
/**
* @deprecated Use {@link OkHttpDataSource.Factory#setContentTypePredicate(Predicate)} instead.
*/
@UnstableApi
@Deprecated
public void setContentTypePredicate(@Nullable Predicate<String> contentTypePredicate) {
this.contentTypePredicate = contentTypePredicate;
}
@UnstableApi
@Override
@Nullable
public Uri getUri() {
return response == null ? null : Uri.parse(response.request().url().toString());
}
@UnstableApi
@Override
public int getResponseCode() {
return response == null ? -1 : response.code();
}
@UnstableApi
@Override
public Map<String, List<String>> getResponseHeaders() {
return response == null ? Collections.emptyMap() : response.headers().toMultimap();
}
@UnstableApi
@Override
public void setRequestProperty(String name, String value) {
Assertions.checkNotNull(name);
@ -262,17 +275,20 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
requestProperties.set(name, value);
}
@UnstableApi
@Override
public void clearRequestProperty(String name) {
Assertions.checkNotNull(name);
requestProperties.remove(name);
}
@UnstableApi
@Override
public void clearAllRequestProperties() {
requestProperties.clear();
}
@UnstableApi
@Override
public long open(DataSpec dataSpec) throws HttpDataSourceException {
this.dataSpec = dataSpec;
@ -358,6 +374,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
return bytesToRead;
}
@UnstableApi
@Override
public int read(byte[] buffer, int offset, int length) throws HttpDataSourceException {
try {
@ -368,6 +385,7 @@ public class OkHttpDataSource extends BaseDataSource implements HttpDataSource {
}
}
@UnstableApi
@Override
public void close() {
if (opened) {

View File

@ -39,7 +39,7 @@ injected from application code.
`DefaultDataSource` will automatically use the RTMP extension whenever it's
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
application code are required. Alternatively, if you know that your application
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.
if (project.file('src/main/jni/ffmpeg').exists()) {
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 {

View File

@ -14,7 +14,7 @@
# 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.
set(CMAKE_CXX_STANDARD 11)

View File

@ -21,11 +21,11 @@ import static java.lang.Math.min;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
import androidx.media3.exoplayer.upstream.Allocator;
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.PriorityTaskManager;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.TrackSelectionArray;
import androidx.media3.common.TracksInfo;
import androidx.media3.common.VideoSize;
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.MediaSource;
import androidx.media3.exoplayer.source.ShuffleOrder;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.text.TextRenderer;
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
import androidx.media3.exoplayer.trackselection.TrackSelectionArray;
import androidx.media3.exoplayer.trackselection.TrackSelector;
import androidx.media3.exoplayer.upstream.BandwidthMeter;
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.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.TrackSelectionArray;
import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.TracksInfo;
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.MediaPeriodId;
import androidx.media3.exoplayer.source.ShuffleOrder;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.text.TextOutput;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
import androidx.media3.exoplayer.trackselection.TrackSelectionArray;
import androidx.media3.exoplayer.trackselection.TrackSelector;
import androidx.media3.exoplayer.trackselection.TrackSelectorResult;
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.RepeatMode;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Clock;
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.SampleStream;
import androidx.media3.exoplayer.source.ShuffleOrder;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.text.TextRenderer;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
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.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
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.Format;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Log;
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.MediaSource.MediaPeriodId;
import androidx.media3.exoplayer.source.SampleStream;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
import androidx.media3.exoplayer.trackselection.TrackSelector;
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.MediaItem;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Clock;
import androidx.media3.common.util.HandlerWrapper;
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.MediaPeriod;
import androidx.media3.exoplayer.source.MediaSource;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.upstream.Allocator;
import androidx.media3.exoplayer.upstream.DefaultAllocator;
import androidx.media3.extractor.DefaultExtractorsFactory;
@ -157,7 +157,7 @@ public final class MetadataRetriever {
mediaPeriod.maybeThrowPrepareError();
}
mediaSourceHandler.sendEmptyMessageDelayed(
MESSAGE_CHECK_FOR_FAILURE, /* delayMillis= */ ERROR_POLL_INTERVAL_MS);
MESSAGE_CHECK_FOR_FAILURE, /* delayMs= */ ERROR_POLL_INTERVAL_MS);
} catch (Exception e) {
trackGroupsFuture.setException(e);
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.PlaybackSuppressionReason;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.trackselection.TrackSelectorResult;
import com.google.common.collect.ImmutableList;
import java.util.List;

View File

@ -35,8 +35,6 @@ import androidx.media3.common.MediaMetadata;
import androidx.media3.common.PlaybackParameters;
import androidx.media3.common.PriorityTaskManager;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.TrackSelectionArray;
import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.TracksInfo;
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.MediaSource;
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.upstream.BandwidthMeter;
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.TimelineChangeReason;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackSelection;
import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.TracksInfo;
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.MediaLoadData;
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
import androidx.media3.exoplayer.trackselection.TrackSelection;
import androidx.media3.exoplayer.video.VideoDecoderOutputBufferRenderer;
import com.google.common.base.Objects;
import java.io.IOException;

View File

@ -24,8 +24,8 @@ import androidx.annotation.RequiresApi;
import androidx.media3.common.MediaItem;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.DefaultHttpDataSource;
import androidx.media3.datasource.HttpDataSource;
import com.google.common.primitives.Ints;
import java.util.Map;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@ -42,7 +42,7 @@ public final class DefaultDrmSessionManagerProvider implements DrmSessionManager
@GuardedBy("lock")
private @MonotonicNonNull DrmSessionManager manager;
@Nullable private HttpDataSource.Factory drmHttpDataSourceFactory;
@Nullable private DataSource.Factory drmHttpDataSourceFactory;
@Nullable private String userAgent;
public DefaultDrmSessionManagerProvider() {
@ -50,26 +50,22 @@ public final class DefaultDrmSessionManagerProvider implements DrmSessionManager
}
/**
* Sets the {@link HttpDataSource.Factory} to be used for creating {@link HttpMediaDrmCallback
* HttpMediaDrmCallbacks} which executes key and provisioning requests over HTTP. If {@code null}
* is passed the {@link DefaultHttpDataSource.Factory} is used.
* Sets the {@link DataSource.Factory} which is used to create {@link HttpMediaDrmCallback}
* instances. If {@code null} is passed a {@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}.
*/
public void setDrmHttpDataSourceFactory(
@Nullable HttpDataSource.Factory drmHttpDataSourceFactory) {
this.drmHttpDataSourceFactory = drmHttpDataSourceFactory;
public void setDrmHttpDataSourceFactory(@Nullable DataSource.Factory drmDataSourceFactory) {
this.drmHttpDataSourceFactory = drmDataSourceFactory;
}
/**
* Sets the optional user agent to be used for DRM requests.
*
* <p>In case a factory has been set by {@link
* #setDrmHttpDataSourceFactory(HttpDataSource.Factory)}, this user agent is ignored.
*
* @param userAgent The 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
* outgoing requests.
*/
@Deprecated
public void setDrmUserAgent(@Nullable String userAgent) {
this.userAgent = userAgent;
}
@ -94,7 +90,7 @@ public final class DefaultDrmSessionManagerProvider implements DrmSessionManager
@RequiresApi(18)
private DrmSessionManager createManager(MediaItem.DrmConfiguration drmConfiguration) {
HttpDataSource.Factory dataSourceFactory =
DataSource.Factory dataSourceFactory =
drmHttpDataSourceFactory != null
? drmHttpDataSourceFactory
: new DefaultHttpDataSource.Factory().setUserAgent(userAgent);

View File

@ -15,6 +15,8 @@
*/
package androidx.media3.exoplayer.drm;
import static androidx.media3.common.util.Assertions.checkNotNull;
import android.annotation.SuppressLint;
import android.media.DeniedByServerException;
import android.media.MediaCrypto;
@ -23,6 +25,7 @@ import android.media.MediaDrm;
import android.media.MediaDrmException;
import android.media.NotProvisionedException;
import android.media.UnsupportedSchemeException;
import android.media.metrics.LogSessionId;
import android.os.PersistableBundle;
import android.text.TextUtils;
import androidx.annotation.DoNotInline;
@ -188,7 +191,13 @@ public final class FrameworkMediaDrm implements ExoMediaDrm {
@Override
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.
@ -518,5 +527,16 @@ public final class FrameworkMediaDrm implements ExoMediaDrm {
public static boolean requiresSecureDecoder(MediaDrm mediaDrm, String 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.UnstableApi;
import androidx.media3.common.util.Util;
import androidx.media3.datasource.DataSource;
import androidx.media3.datasource.DataSourceInputStream;
import androidx.media3.datasource.DataSpec;
import androidx.media3.datasource.HttpDataSource;
import androidx.media3.datasource.HttpDataSource.InvalidResponseCodeException;
import androidx.media3.datasource.StatsDataSource;
import androidx.media3.exoplayer.drm.ExoMediaDrm.KeyRequest;
@ -36,41 +36,47 @@ import java.util.List;
import java.util.Map;
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
public final class HttpMediaDrmCallback implements MediaDrmCallback {
private static final int MAX_MANUAL_REDIRECTS = 5;
private final HttpDataSource.Factory dataSourceFactory;
private final DataSource.Factory dataSourceFactory;
@Nullable private final String defaultLicenseUrl;
private final boolean forceDefaultLicenseUrl;
private final Map<String, String> keyRequestProperties;
/**
* Constructs an instance.
*
* @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 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(
@Nullable String defaultLicenseUrl, HttpDataSource.Factory dataSourceFactory) {
@Nullable String defaultLicenseUrl, DataSource.Factory dataSourceFactory) {
this(defaultLicenseUrl, /* forceDefaultLicenseUrl= */ false, dataSourceFactory);
}
/**
* Constructs an instance.
*
* @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
* 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.
* @param forceDefaultLicenseUrl Whether to force use of {@code defaultLicenseUrl} for key
* 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(
@Nullable String defaultLicenseUrl,
boolean forceDefaultLicenseUrl,
HttpDataSource.Factory dataSourceFactory) {
DataSource.Factory dataSourceFactory) {
Assertions.checkArgument(!(forceDefaultLicenseUrl && TextUtils.isEmpty(defaultLicenseUrl)));
this.dataSourceFactory = dataSourceFactory;
this.defaultLicenseUrl = defaultLicenseUrl;
@ -156,7 +162,7 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
}
private static byte[] executePost(
HttpDataSource.Factory dataSourceFactory,
DataSource.Factory dataSourceFactory,
String url,
@Nullable byte[] httpBody,
Map<String, String> requestProperties)

View File

@ -26,7 +26,7 @@ import androidx.media3.common.DrmInitData;
import androidx.media3.common.Format;
import androidx.media3.common.util.Assertions;
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.drm.DefaultDrmSessionManager.Mode;
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
* 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
* DRM-related events.
* @return A new instance which uses Widevine CDM.
*/
public static OfflineLicenseHelper newWidevineInstance(
String defaultLicenseUrl,
HttpDataSource.Factory httpDataSourceFactory,
DataSource.Factory dataSourceFactory,
DrmSessionEventListener.EventDispatcher eventDispatcher) {
return newWidevineInstance(
defaultLicenseUrl,
/* forceDefaultLicenseUrl= */ false,
httpDataSourceFactory,
eventDispatcher);
defaultLicenseUrl, /* forceDefaultLicenseUrl= */ false, dataSourceFactory, eventDispatcher);
}
/**
@ -77,7 +74,7 @@ public final class OfflineLicenseHelper {
* their own license URL.
* @param forceDefaultLicenseUrl Whether to use {@code defaultLicenseUrl} for key requests that
* 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
* DRM-related events.
* @return A new instance which uses Widevine CDM.
@ -85,12 +82,12 @@ public final class OfflineLicenseHelper {
public static OfflineLicenseHelper newWidevineInstance(
String defaultLicenseUrl,
boolean forceDefaultLicenseUrl,
HttpDataSource.Factory httpDataSourceFactory,
DataSource.Factory dataSourceFactory,
DrmSessionEventListener.EventDispatcher eventDispatcher) {
return newWidevineInstance(
defaultLicenseUrl,
forceDefaultLicenseUrl,
httpDataSourceFactory,
dataSourceFactory,
/* optionalKeyRequestParameters= */ null,
eventDispatcher);
}
@ -113,7 +110,7 @@ public final class OfflineLicenseHelper {
public static OfflineLicenseHelper newWidevineInstance(
String defaultLicenseUrl,
boolean forceDefaultLicenseUrl,
HttpDataSource.Factory httpDataSourceFactory,
DataSource.Factory dataSourceFactory,
@Nullable Map<String, String> optionalKeyRequestParameters,
DrmSessionEventListener.EventDispatcher eventDispatcher) {
return new OfflineLicenseHelper(
@ -121,7 +118,7 @@ public final class OfflineLicenseHelper {
.setKeyRequestParameters(optionalKeyRequestParameters)
.build(
new HttpMediaDrmCallback(
defaultLicenseUrl, forceDefaultLicenseUrl, httpDataSourceFactory)),
defaultLicenseUrl, forceDefaultLicenseUrl, dataSourceFactory)),
eventDispatcher);
}

View File

@ -96,8 +96,9 @@ public final class DefaultMediaCodecAdapterFactory implements MediaCodecAdapter.
@Override
public MediaCodecAdapter createAdapter(MediaCodecAdapter.Configuration configuration)
throws IOException {
if ((asynchronousMode == MODE_ENABLED && Util.SDK_INT >= 23)
|| (asynchronousMode == MODE_DEFAULT && Util.SDK_INT >= 31)) {
if (Util.SDK_INT >= 23
&& (asynchronousMode == MODE_ENABLED
|| (asynchronousMode == MODE_DEFAULT && Util.SDK_INT >= 31))) {
int trackType = MimeTypes.getTrackType(configuration.format.sampleMimeType);
Log.i(
TAG,

View File

@ -443,6 +443,8 @@ public final class MediaCodecUtil {
return "audio/x-lg-alac";
} else if (mimeType.equals(MimeTypes.AUDIO_FLAC) && "OMX.lge.flac.decoder".equals(name)) {
return "audio/x-lg-flac";
} else if (mimeType.equals(MimeTypes.AUDIO_AC3) && "OMX.lge.ac3.decoder".equals(name)) {
return "audio/lg-ac3";
}
return null;

View File

@ -31,7 +31,6 @@ import androidx.media3.common.MimeTypes;
import androidx.media3.common.StreamKey;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.TrackSelectionOverride;
import androidx.media3.common.TrackSelectionParameters;
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.MediaPeriodId;
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.MediaChunkIterator;
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.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.UnstableApi;
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
* for requesting media data.
*/
@UnstableApi
public DefaultMediaSourceFactory(DataSource.Factory dataSourceFactory) {
this(dataSourceFactory, new DefaultExtractorsFactory());
}

View File

@ -21,7 +21,6 @@ import static androidx.media3.common.util.Util.castNonNull;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.SeekParameters;
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.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.ExoPlayer;
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.StreamKey;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Assertions;
import androidx.media3.decoder.DecoderInputBuffer;
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.ParserException;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.ConditionVariable;
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.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.UnstableApi;
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.MimeTypes;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.Util;

View File

@ -13,13 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.media3.common;
package androidx.media3.exoplayer.source;
import static java.lang.annotation.ElementType.TYPE_USE;
import android.os.Bundle;
import androidx.annotation.IntDef;
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.Log;
import androidx.media3.common.util.UnstableApi;
@ -30,7 +33,16 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
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
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.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.common.util.Util;
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.MediaSourceEventListener;
import androidx.media3.exoplayer.source.SampleStream;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
import androidx.media3.exoplayer.upstream.Allocator;
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.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackSelection;
import androidx.media3.common.util.Clock;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.UnstableApi;
@ -781,7 +780,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
}
double[] logBitrates = new double[trackBitrates[i].length];
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];
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.Format;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackSelection;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.UnstableApi;
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.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.TrackSelection;
import androidx.media3.common.TrackSelectionOverride;
import androidx.media3.common.TrackSelectionParameters;
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.RendererConfiguration;
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
import androidx.media3.exoplayer.source.TrackGroupArray;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
@ -86,11 +85,10 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
* .setMaxVideoSizeSd()
* .setPreferredAudioLanguage("de")
* .build());
*
* }</pre>
*
* 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
* defaultTrackSelector.setParameters(
@ -98,7 +96,6 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
* .buildUpon()
* .setTunnelingEnabled(true)
* .build());
*
* }</pre>
*/
@UnstableApi

View File

@ -20,7 +20,6 @@ import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackSelection;
import androidx.media3.common.util.Log;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;

View File

@ -18,7 +18,6 @@ package androidx.media3.exoplayer.trackselection;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackSelection;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.source.chunk.MediaChunk;
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.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.TracksInfo;
import androidx.media3.common.util.UnstableApi;
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.RendererConfiguration;
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
import androidx.media3.exoplayer.source.TrackGroupArray;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

View File

@ -13,11 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.media3.common;
package androidx.media3.exoplayer.trackselection;
import static java.lang.annotation.ElementType.TYPE_USE;
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 java.lang.annotation.Documented;
import java.lang.annotation.Retention;

View File

@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.media3.common;
package androidx.media3.exoplayer.trackselection;
import androidx.annotation.Nullable;
import androidx.media3.common.util.UnstableApi;

View File

@ -19,12 +19,11 @@ import android.os.SystemClock;
import androidx.annotation.Nullable;
import androidx.media3.common.C;
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.TrackGroupInfo;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.RendererCapabilities;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector.SelectionOverride;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection.Definition;
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;

View File

@ -17,8 +17,6 @@ package androidx.media3.exoplayer.trackselection;
import androidx.annotation.Nullable;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.TrackSelection;
import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.UnstableApi;
@ -28,6 +26,7 @@ import androidx.media3.exoplayer.Renderer;
import androidx.media3.exoplayer.RendererCapabilities;
import androidx.media3.exoplayer.RendererConfiguration;
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
import androidx.media3.exoplayer.source.TrackGroupArray;
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.
// 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);
TrackGroup trackGroup = trackGroupInfo.getTrackGroup();
for (int trackIndex = 0; trackIndex < trackGroup.length; trackIndex++) {
if (!trackGroupInfo.isTrackSelected(trackIndex)) {
continue;
}
@Nullable Metadata metadata = trackGroup.getFormat(trackIndex).metadata;
if (metadata != null) {
logd(" Metadata [");
printMetadata(metadata, " ");
logd(" ]");
break;
for (int trackIndex = 0; !loggedMetadata && trackIndex < trackGroup.length; trackIndex++) {
if (trackGroupInfo.isTrackSelected(trackIndex)) {
@Nullable Metadata metadata = trackGroup.getFormat(trackIndex).metadata;
if (metadata != null && metadata.length() > 0) {
logd(" Metadata [");
printMetadata(metadata, " ");
logd(" ]");
loggedMetadata = true;
}
}
}
}

View File

@ -18,9 +18,9 @@ package androidx.media3.exoplayer;
import static com.google.common.truth.Truth.assertThat;
import androidx.media3.common.C;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.DefaultLoadControl.Builder;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
import androidx.media3.exoplayer.upstream.DefaultAllocator;
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.Window;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.TracksInfo;
import androidx.media3.common.TracksInfo.TrackGroupInfo;
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.MediaSourceEventListener;
import androidx.media3.exoplayer.source.SinglePeriodTimeline;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.source.ads.ServerSideAdInsertionMediaSource;
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
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.MediaItem;
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.MotionPhotoMetadata;
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.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.TrackSelectionOverride;
import androidx.media3.common.TrackSelectionParameters;
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.source.MediaPeriod;
import androidx.media3.exoplayer.source.MediaSourceEventListener.EventDispatcher;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
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.Format;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.decoder.DecoderInputBuffer;
import androidx.media3.exoplayer.FormatHolder;
import androidx.media3.exoplayer.drm.DrmSessionEventListener;

View File

@ -13,10 +13,13 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.media3.common;
package androidx.media3.exoplayer.source;
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 org.junit.Test;
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.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackSelection;
import androidx.media3.datasource.DataSpec;
import androidx.media3.exoplayer.source.MediaSource;
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.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.TrackSelection;
import androidx.media3.common.TrackSelectionOverride;
import androidx.media3.common.TracksInfo;
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.RendererConfiguration;
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.ParametersBuilder;
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.Timeline;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.ExoPlaybackException;
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.RendererConfiguration;
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.test.utils.FakeTimeline;
import androidx.test.ext.junit.runners.AndroidJUnit4;
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.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.TrackSelection;
import androidx.media3.common.TracksInfo;
import androidx.media3.common.TracksInfo.TrackGroupInfo;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.google.common.collect.ImmutableList;
import java.util.List;

View File

@ -20,10 +20,10 @@ import static org.junit.Assert.fail;
import androidx.annotation.Nullable;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.exoplayer.ExoPlaybackException;
import androidx.media3.exoplayer.RendererCapabilities;
import androidx.media3.exoplayer.source.MediaSource.MediaPeriodId;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.trackselection.TrackSelector.InvalidationListener;
import androidx.media3.exoplayer.upstream.BandwidthMeter;
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.StreamKey;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Util;
import androidx.media3.datasource.TransferListener;
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.SampleStream;
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.EmbeddedSampleStream;
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.datasource.DataSource;
import androidx.media3.datasource.DataSpec;
import androidx.media3.datasource.HttpDataSource;
import androidx.media3.exoplayer.dash.manifest.DashManifest;
import androidx.media3.exoplayer.dash.manifest.DashManifestParser;
import androidx.media3.exoplayer.dash.manifest.Period;
@ -85,7 +84,7 @@ public final class DashUtil {
/**
* 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.
* @return An instance of {@link DashManifest}.
* @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.
*
* @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}.
* @return The loaded {@link Format}, or null if none is defined.
* @throws IOException Thrown when there is an error while loading.

View File

@ -1189,41 +1189,41 @@ public class DashManifestParser extends DefaultHandler
xpp.nextToken();
while (!XmlPullParserUtil.isEndTag(xpp, "Event")) {
switch (xpp.getEventType()) {
case (XmlPullParser.START_DOCUMENT):
case XmlPullParser.START_DOCUMENT:
xmlSerializer.startDocument(null, false);
break;
case (XmlPullParser.END_DOCUMENT):
case XmlPullParser.END_DOCUMENT:
xmlSerializer.endDocument();
break;
case (XmlPullParser.START_TAG):
case XmlPullParser.START_TAG:
xmlSerializer.startTag(xpp.getNamespace(), xpp.getName());
for (int i = 0; i < xpp.getAttributeCount(); i++) {
xmlSerializer.attribute(
xpp.getAttributeNamespace(i), xpp.getAttributeName(i), xpp.getAttributeValue(i));
}
break;
case (XmlPullParser.END_TAG):
case XmlPullParser.END_TAG:
xmlSerializer.endTag(xpp.getNamespace(), xpp.getName());
break;
case (XmlPullParser.TEXT):
case XmlPullParser.TEXT:
xmlSerializer.text(xpp.getText());
break;
case (XmlPullParser.CDSECT):
case XmlPullParser.CDSECT:
xmlSerializer.cdsect(xpp.getText());
break;
case (XmlPullParser.ENTITY_REF):
case XmlPullParser.ENTITY_REF:
xmlSerializer.entityRef(xpp.getText());
break;
case (XmlPullParser.IGNORABLE_WHITESPACE):
case XmlPullParser.IGNORABLE_WHITESPACE:
xmlSerializer.ignorableWhitespace(xpp.getText());
break;
case (XmlPullParser.PROCESSING_INSTRUCTION):
case XmlPullParser.PROCESSING_INSTRUCTION:
xmlSerializer.processingInstruction(xpp.getText());
break;
case (XmlPullParser.COMMENT):
case XmlPullParser.COMMENT:
xmlSerializer.comment(xpp.getText());
break;
case (XmlPullParser.DOCDECL):
case XmlPullParser.DOCDECL:
xmlSerializer.docdecl(xpp.getText());
break;
default: // fall out

View File

@ -21,7 +21,6 @@ import android.net.Uri;
import androidx.media3.common.Format;
import androidx.media3.common.MimeTypes;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.datasource.TransferListener;
import androidx.media3.exoplayer.analytics.PlayerId;
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.MediaSource.MediaPeriodId;
import androidx.media3.exoplayer.source.MediaSourceEventListener;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.upstream.Allocator;
import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy;
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.StreamKey;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.UnstableApi;
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.SampleStream;
import androidx.media3.exoplayer.source.SequenceableLoader;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
import androidx.media3.exoplayer.upstream.Allocator;
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.ParserException;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.common.util.Assertions;
import androidx.media3.common.util.Log;
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.ReadFlags;
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.MediaChunkIterator;
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.checkState;
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.usToMs;
import static androidx.media3.exoplayer.ima.ImaUtil.expandAdGroupPlaceholder;
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.updateAdDurationAndPropagate;
import static androidx.media3.exoplayer.ima.ImaUtil.updateAdDurationInAdGroup;
@ -661,19 +662,29 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
private static AdPlaybackState setVodAdGroupPlaceholders(
List<CuePoint> cuePoints, AdPlaybackState adPlaybackState) {
// TODO(b/192231683) Use getEndTimeMs()/getStartTimeMs() after jar target was removed
for (int i = 0; i < cuePoints.size(); i++) {
CuePoint cuePoint = cuePoints.get(i);
long fromPositionUs = msToUs(secToMsRounded(cuePoint.getStartTime()));
adPlaybackState =
addAdGroupToAdPlaybackState(
adPlaybackState,
/* fromPositionUs= */ secToUs(cuePoint.getStartTime()),
/* fromPositionUs= */ fromPositionUs,
/* contentResumeOffsetUs= */ 0,
// TODO(b/192231683) Use getEndTimeMs()/getStartTimeMs() after jar target was removed
/* adDurationsUs...= */ secToUs(cuePoint.getEndTime() - cuePoint.getStartTime()));
/* adDurationsUs...= */ getAdDuration(
/* startTimeSeconds= */ cuePoint.getStartTime(),
/* endTimeSeconds= */ cuePoint.getEndTime()));
}
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) {
AdPodInfo adPodInfo = ad.getAdPodInfo();
// Handle post rolls that have a podIndex of -1.
@ -685,9 +696,9 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
adPlaybackState =
expandAdGroupPlaceholder(
adGroupIndex,
/* adGroupDurationUs= */ secToUs(adPodInfo.getMaxDuration()),
/* adGroupDurationUs= */ msToUs(secToMsRounded(adPodInfo.getMaxDuration())),
adIndexInAdGroup,
/* adDurationUs= */ secToUs(ad.getDuration()),
/* adDurationUs= */ msToUs(secToMsRounded(ad.getDuration())),
/* adsInAdGroupCount= */ adPodInfo.getTotalAds(),
adPlaybackState);
} else if (adIndexInAdGroup < adGroup.count - 1) {
@ -695,7 +706,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
updateAdDurationInAdGroup(
adGroupIndex,
adIndexInAdGroup,
/* adDurationUs= */ secToUs(ad.getDuration()),
/* adDurationUs= */ msToUs(secToMsRounded(ad.getDuration())),
adPlaybackState);
}
return adPlaybackState;
@ -704,7 +715,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
private AdPlaybackState addLiveAdBreak(
Ad ad, long currentPeriodPositionUs, AdPlaybackState adPlaybackState) {
AdPodInfo adPodInfo = ad.getAdPodInfo();
long adDurationUs = secToUs(ad.getDuration());
long adDurationUs = secToUsRounded(ad.getDuration());
int adIndexInAdGroup = adPodInfo.getAdPosition() - 1;
// TODO(b/208398934) Support seeking backwards.
if (adIndexInAdGroup == 0 || adPlaybackState.adGroupCount == 1) {
@ -718,7 +729,7 @@ public final class ImaServerSideAdInsertionMediaSource extends CompositeMediaSou
new long[adCount],
adIndexInAdGroup,
adDurationUs,
secToUs(adPodInfo.getMaxDuration()));
msToUs(secToMsRounded(adPodInfo.getMaxDuration())));
adPlaybackState =
addAdGroupToAdPlaybackState(
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.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.math.DoubleMath;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
@ -398,17 +401,13 @@ import java.util.Set;
long elapsedAdGroupAdDurationUs = 0;
for (int j = periodIndex; j < contentTimeline.getPeriodCount(); j++) {
contentTimeline.getPeriod(j, period, /* setIds= */ true);
// TODO(b/192231683) Remove subtracted US from ad group time when we can upgrade the SDK.
// Subtract one microsecond to work around rounding errors with adGroup.timeUs.
if (totalElapsedContentDurationUs < adGroup.timeUs - 1) {
if (totalElapsedContentDurationUs < adGroup.timeUs) {
// Period starts before the ad group, so it is a content period.
adPlaybackStates.put(checkNotNull(period.uid), contentOnlyAdPlaybackState);
totalElapsedContentDurationUs += period.durationUs;
} else {
long periodStartUs = totalElapsedContentDurationUs + elapsedAdGroupAdDurationUs;
// TODO(b/192231683) Remove additional US when we can upgrade the SDK.
// Add one microsecond to work around rounding errors with adGroup.timeUs.
if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs + 1) {
if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs) {
// 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).
adPlaybackStates.put(
@ -490,16 +489,12 @@ import java.util.Set;
long elapsedAdGroupAdDurationUs = 0;
for (int j = periodIndex; j < contentTimeline.getPeriodCount(); j++) {
contentTimeline.getPeriod(j, period, /* setIds= */ true);
// TODO(b/192231683) Remove subtracted US from ad group time when we can upgrade the SDK.
// Subtract one microsecond to work around rounding errors with adGroup.timeUs.
if (totalElapsedContentDurationUs < adGroup.timeUs - 1) {
if (totalElapsedContentDurationUs < adGroup.timeUs) {
// Period starts before the ad group, so it is a content period.
totalElapsedContentDurationUs += period.durationUs;
} else {
long periodStartUs = totalElapsedContentDurationUs + elapsedAdGroupAdDurationUs;
// TODO(b/192231683) Remove additional US when we can upgrade the SDK.
// Add one microsecond to work around rounding errors with adGroup.timeUs.
if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs + 1) {
if (periodStartUs + period.durationUs <= adGroup.timeUs + adGroupDurationUs) {
// The period ends before the end of the ad group, so it is an ad period.
if (j == adPeriodIndex) {
return new Pair<>(/* adGroupIndex= */ i, adIndexInAdGroup);
@ -518,5 +513,31 @@ import java.util.Set;
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() {}
}

View File

@ -23,13 +23,13 @@ import androidx.media3.common.MediaItem;
import androidx.media3.common.PlaybackException;
import androidx.media3.common.Player;
import androidx.media3.common.Timeline;
import androidx.media3.common.TrackSelectionArray;
import androidx.media3.common.TrackSelectionParameters;
import androidx.media3.common.TracksInfo;
import androidx.media3.common.util.Clock;
import androidx.media3.common.util.ListenerSet;
import androidx.media3.common.util.Util;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.trackselection.TrackSelectionArray;
import androidx.media3.test.utils.StubExoPlayer;
/** A fake {@link ExoPlayer} for testing content/ad playback. */
@ -79,7 +79,7 @@ import androidx.media3.test.utils.StubExoPlayer;
PositionInfo oldPosition =
new PositionInfo(
windowUid,
/* windowIndex= */ 0,
/* mediaItemIndex= */ 0,
mediaItem,
periodUid,
/* periodIndex= */ 0,
@ -97,7 +97,7 @@ import androidx.media3.test.utils.StubExoPlayer;
PositionInfo newPosition =
new PositionInfo(
windowUid,
/* windowIndex= */ 0,
/* mediaItemIndex= */ 0,
mediaItem,
periodUid,
/* periodIndex= */ 0,
@ -128,7 +128,7 @@ import androidx.media3.test.utils.StubExoPlayer;
PositionInfo oldPosition =
new PositionInfo(
windowUid,
/* windowIndex= */ 0,
/* mediaItemIndex= */ 0,
mediaItem,
periodUid,
/* periodIndex= */ 0,
@ -146,7 +146,7 @@ import androidx.media3.test.utils.StubExoPlayer;
PositionInfo newPosition =
new PositionInfo(
windowUid,
/* windowIndex= */ 0,
/* mediaItemIndex= */ 0,
mediaItem,
periodUid,
/* periodIndex= */ 0,

View File

@ -28,8 +28,6 @@ import androidx.media3.common.C;
import androidx.media3.common.Format;
import androidx.media3.common.StreamKey;
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.Util;
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.ReadDataResult;
import androidx.media3.exoplayer.source.SampleStream.ReadFlags;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
import androidx.media3.exoplayer.trackselection.TrackSelection;
import androidx.media3.exoplayer.upstream.Allocator;
import androidx.media3.exoplayer.upstream.Loader;
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.StreamKey;
import androidx.media3.common.TrackGroup;
import androidx.media3.common.TrackGroupArray;
import androidx.media3.datasource.TransferListener;
import androidx.media3.exoplayer.SeekParameters;
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.SampleStream;
import androidx.media3.exoplayer.source.SequenceableLoader;
import androidx.media3.exoplayer.source.TrackGroupArray;
import androidx.media3.exoplayer.source.chunk.ChunkSampleStream;
import androidx.media3.exoplayer.trackselection.ExoTrackSelection;
import androidx.media3.exoplayer.upstream.Allocator;

View File

@ -31,6 +31,21 @@ public final class 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. */
public static final class SpsData {
@ -38,6 +53,7 @@ public final class NalUnitUtil {
public final int constraintsFlagsAndReservedZero2Bits;
public final int levelIdc;
public final int seqParameterSetId;
public final int maxNumRefFrames;
public final int width;
public final int height;
public final float pixelWidthHeightRatio;
@ -53,6 +69,7 @@ public final class NalUnitUtil {
int constraintsFlagsAndReservedZero2Bits,
int levelIdc,
int seqParameterSetId,
int maxNumRefFrames,
int width,
int height,
float pixelWidthHeightRatio,
@ -66,6 +83,7 @@ public final class NalUnitUtil {
this.constraintsFlagsAndReservedZero2Bits = constraintsFlagsAndReservedZero2Bits;
this.levelIdc = levelIdc;
this.seqParameterSetId = seqParameterSetId;
this.maxNumRefFrames = maxNumRefFrames;
this.width = width;
this.height = height;
this.pixelWidthHeightRatio = pixelWidthHeightRatio;
@ -372,7 +390,7 @@ public final class NalUnitUtil {
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
int picWidthInMbs = data.readUnsignedExpGolombCodedInt() + 1;
@ -432,6 +450,7 @@ public final class NalUnitUtil {
constraintsFlagsAndReservedZero2Bits,
levelIdc,
seqParameterSetId,
maxNumRefFrames,
frameWidth,
frameHeight,
pixelWidthHeightRatio,

View File

@ -15,7 +15,6 @@
*/
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.extractor.mp4.AtomParsers.parseTraks;
import static androidx.media3.extractor.mp4.Sniffer.BRAND_HEIC;
@ -165,8 +164,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
private int sampleCurrentNalBytesRemaining;
// Extractor outputs.
private @MonotonicNonNull ExtractorOutput extractorOutput;
private Mp4Track @MonotonicNonNull [] tracks;
private ExtractorOutput extractorOutput;
private Mp4Track[] tracks;
private long @MonotonicNonNull [][] accumulatedSampleSizes;
private int firstVideoTrackIndex;
@ -197,6 +196,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
nalLength = new ParsableByteArray(4);
scratch = new ParsableByteArray();
sampleTrackIndex = C.INDEX_UNSET;
extractorOutput = ExtractorOutput.PLACEHOLDER;
tracks = new Mp4Track[0];
}
@Override
@ -227,7 +228,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
sefReader.reset();
slowMotionMetadataEntries.clear();
}
} else if (tracks != null) {
} else {
for (Mp4Track track : tracks) {
updateSampleIndex(track, timeUs);
if (track.trueHdSampleRechunker != null) {
@ -280,7 +281,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
@Override
public SeekPoints getSeekPoints(long timeUs) {
if (checkNotNull(tracks).length == 0) {
if (tracks.length == 0) {
return new SeekPoints(SeekPoint.START);
}
@ -502,7 +503,6 @@ public final class Mp4Extractor implements Extractor, SeekMap {
isQuickTime,
/* modifyTrackFunction= */ track -> track);
ExtractorOutput extractorOutput = checkNotNull(this.extractorOutput);
int trackCount = trackSampleTables.size();
for (int i = 0; i < trackCount; i++) {
TrackSampleTable trackSampleTable = trackSampleTables.get(i);
@ -582,7 +582,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
return RESULT_END_OF_INPUT;
}
}
Mp4Track track = castNonNull(tracks)[sampleTrackIndex];
Mp4Track track = tracks[sampleTrackIndex];
TrackOutput trackOutput = track.trackOutput;
int sampleIndex = track.sampleIndex;
long position = track.sampleTable.offsets[sampleIndex];
@ -699,7 +699,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
long minAccumulatedBytes = Long.MAX_VALUE;
boolean minAccumulatedBytesRequiresReload = true;
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];
int sampleIndex = track.sampleIndex;
if (sampleIndex == track.sampleTable.sampleCount) {
@ -744,7 +744,6 @@ public final class Mp4Extractor implements Extractor, SeekMap {
private void processEndOfStreamReadingAtomHeader() {
if (fileType == FILE_TYPE_HEIC && (flags & FLAG_READ_MOTION_PHOTO_METADATA) != 0) {
// Add image track and prepare media.
ExtractorOutput extractorOutput = checkNotNull(this.extractorOutput);
TrackOutput trackOutput = extractorOutput.track(/* id= */ 0, C.TRACK_TYPE_IMAGE);
@Nullable
Metadata metadata = motionPhotoMetadata == null ? null : new Metadata(motionPhotoMetadata);

View File

@ -44,10 +44,6 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull;
@UnstableApi
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 boolean allowNonIdrKeyframes;
private final boolean detectAccessUnits;
@ -85,9 +81,9 @@ public final class H264Reader implements ElementaryStreamReader {
this.allowNonIdrKeyframes = allowNonIdrKeyframes;
this.detectAccessUnits = detectAccessUnits;
prefixFlags = new boolean[3];
sps = new NalUnitTargetBuffer(NAL_UNIT_TYPE_SPS, 128);
pps = new NalUnitTargetBuffer(NAL_UNIT_TYPE_PPS, 128);
sei = new NalUnitTargetBuffer(NAL_UNIT_TYPE_SEI, 128);
sps = new NalUnitTargetBuffer(NalUnitUtil.NAL_UNIT_TYPE_SPS, 128);
pps = new NalUnitTargetBuffer(NalUnitUtil.NAL_UNIT_TYPE_PPS, 128);
sei = new NalUnitTargetBuffer(NalUnitUtil.NAL_UNIT_TYPE_SEI, 128);
pesTimeUs = C.TIME_UNSET;
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 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 boolean allowNonIdrKeyframes;
private final boolean detectAccessUnits;
@ -331,11 +322,11 @@ public final class H264Reader implements ElementaryStreamReader {
nalUnitType = type;
nalUnitTimeUs = pesTimeUs;
nalUnitStartPosition = position;
if ((allowNonIdrKeyframes && nalUnitType == NAL_UNIT_TYPE_NON_IDR)
if ((allowNonIdrKeyframes && nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_NON_IDR)
|| (detectAccessUnits
&& (nalUnitType == NAL_UNIT_TYPE_IDR
|| nalUnitType == NAL_UNIT_TYPE_NON_IDR
|| nalUnitType == NAL_UNIT_TYPE_PARTITION_A))) {
&& (nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_IDR
|| nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_NON_IDR
|| nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_PARTITION_A))) {
// Store the previous header and prepare to populate the new one.
SliceHeaderData newSliceHeader = previousSliceHeader;
previousSliceHeader = sliceHeader;
@ -425,7 +416,7 @@ public final class H264Reader implements ElementaryStreamReader {
bottomFieldFlagPresent = true;
}
}
boolean idrPicFlag = nalUnitType == NAL_UNIT_TYPE_IDR;
boolean idrPicFlag = nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_IDR;
int idrPicId = 0;
if (idrPicFlag) {
if (!bitArray.canReadExpGolombCodedNum()) {
@ -480,7 +471,7 @@ public final class H264Reader implements ElementaryStreamReader {
public boolean endNalUnit(
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))) {
// If the NAL unit ending is the start of a new sample, output the previous one.
if (hasOutputFormat && readingSample) {
@ -495,8 +486,8 @@ public final class H264Reader implements ElementaryStreamReader {
boolean treatIFrameAsKeyframe =
allowNonIdrKeyframes ? sliceHeader.isISlice() : randomAccessIndicator;
sampleIsKeyframe |=
nalUnitType == NAL_UNIT_TYPE_IDR
|| (treatIFrameAsKeyframe && nalUnitType == NAL_UNIT_TYPE_NON_IDR);
nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_IDR
|| (treatIFrameAsKeyframe && nalUnitType == NalUnitUtil.NAL_UNIT_TYPE_NON_IDR);
return sampleIsKeyframe;
}

View File

@ -126,6 +126,7 @@ public final class NalUnitUtilTest {
public void parseSpsNalUnit() {
NalUnitUtil.SpsData data =
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.height).isEqualTo(360);
assertThat(data.deltaPicOrderAlwaysZeroFlag).isFalse();

View File

@ -15,6 +15,8 @@
*/
package androidx.media3.session;
import static androidx.media3.common.util.Assertions.checkStateNotNull;
import android.app.Notification;
import android.content.Intent;
import android.os.Bundle;
@ -25,6 +27,7 @@ import androidx.core.app.NotificationManagerCompat;
import androidx.core.content.ContextCompat;
import androidx.media3.common.Player;
import androidx.media3.common.util.Util;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.HashMap;
import java.util.List;
@ -50,6 +53,7 @@ import java.util.concurrent.TimeoutException;
private final Map<MediaSession, ListenableFuture<MediaController>> controllerMap;
private int totalNotificationCount;
@Nullable private MediaNotification mediaNotification;
public MediaNotificationManager(
MediaSessionService mediaSessionService,
@ -122,13 +126,13 @@ import java.util.concurrent.TimeoutException;
MediaController mediaController;
try {
mediaController = controllerFuture.get(0, TimeUnit.MILLISECONDS);
} catch (ExecutionException | InterruptedException | TimeoutException e) {
mediaController = checkStateNotNull(Futures.getDone(controllerFuture));
} catch (ExecutionException e) {
// We should never reach this point.
throw new IllegalStateException(e);
}
int notificationSequence = ++this.totalNotificationCount;
int notificationSequence = ++totalNotificationCount;
MediaNotification.Provider.Callback callback =
notification ->
mainExecutor.execute(
@ -141,45 +145,68 @@ import java.util.concurrent.TimeoutException;
private void onNotificationUpdated(
int notificationSequence, MediaSession session, MediaNotification mediaNotification) {
if (notificationSequence == this.totalNotificationCount) {
if (notificationSequence == totalNotificationCount) {
updateNotification(session, mediaNotification);
}
}
private void updateNotification(MediaSession session, MediaNotification mediaNotification) {
int id = mediaNotification.notificationId;
Notification notification = mediaNotification.notification;
if (Util.SDK_INT >= 21) {
// Call Notification.MediaStyle#setMediaSession() indirectly.
android.media.session.MediaSession.Token fwkToken =
(android.media.session.MediaSession.Token)
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();
if (player.getPlayWhenReady()) {
if (player.getPlayWhenReady() && canStartPlayback(player)) {
ContextCompat.startForegroundService(mediaSessionService, startSelfIntent);
mediaSessionService.startForeground(id, notification);
mediaSessionService.startForeground(
mediaNotification.notificationId, mediaNotification.notification);
} else {
stopForegroundServiceIfNeeded();
notificationManagerCompat.notify(id, notification);
maybeStopForegroundService(/* removeNotifications= */ false);
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();
for (int i = 0; i < sessions.size(); i++) {
Player player = sessions.get(i).getPlayer();
if (player.getPlayWhenReady()) {
if (player.getPlayWhenReady() && canStartPlayback(player)) {
return;
}
}
// Calling stopForeground(true) is a workaround for pre-L devices which prevents
// the media notification from being undismissable.
boolean shouldRemoveNotification = Util.SDK_INT < 21;
mediaSessionService.stopForeground(shouldRemoveNotification);
// To hide the notification on all API levels, we need to call both Service.stopForeground(true)
// and notificationManagerCompat.cancelAll(). For pre-L devices, we must also call
// Service.stopForeground(true) anyway as a workaround that prevents the media notification from
// 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 {
@ -190,11 +217,20 @@ import java.util.concurrent.TimeoutException;
}
public void onConnected() {
updateNotification(session);
if (canStartPlayback(session.getPlayer())) {
updateNotification(session);
}
}
@Override
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(
Player.EVENT_PLAYBACK_STATE_CHANGED,
Player.EVENT_PLAY_WHEN_READY_CHANGED,
@ -206,7 +242,7 @@ import java.util.concurrent.TimeoutException;
@Override
public void onDisconnected(MediaController controller) {
mediaSessionService.removeSession(session);
stopForegroundServiceIfNeeded();
maybeStopForegroundService(/* removeNotifications= */ true);
}
}
}

Some files were not shown because too many files have changed in this diff Show More