diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml index c32439333a..817b4d0fb1 100644 --- a/.github/ISSUE_TEMPLATE/bug.yml +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -5,37 +5,27 @@ body: - type: markdown attributes: value: | - We can only process bug reports that are actionable. Unclear bug reports or reports with - insufficient information may not get attention. + We can only process bug reports that are actionable. Unclear bug reports or reports with insufficient information may not get attention. Before filing a bug: ------------------------- - - Search existing issues, including issues that are closed: - https://github.com/androidx/media/issues?q=is%3Aissue - - For ExoPlayer-related bugs, please also check for existing issues on the ExoPlayer - tracker: https://github.com/google/ExoPlayer/issues?q=is%3Aissue + - Search existing issues, including issues that are closed: https://github.com/androidx/media/issues?q=is%3Aissue + - For ExoPlayer-related bugs, please also check for existing issues on the ExoPlayer tracker: https://github.com/google/ExoPlayer/issues?q=is%3Aissue - type: dropdown attributes: - label: Media3 Version + label: Version description: What version of Media3 (or ExoPlayer) are you using? options: - - Media3 1.1.0-alpha01 - - Media3 1.0.2 - - Media3 1.0.1 - - Media3 1.0.0 - - Media3 1.0.0-rc02 - - Media3 1.0.0-rc01 - - Media3 1.0.0-beta03 - - Media3 1.0.0-beta02 - - Media3 1.0.0-beta01 - - Media3 1.0.0-alpha03 - - Media3 1.0.0-alpha02 - - Media3 1.0.0-alpha01 - - Media3 `main` branch - - ExoPlayer 2.18.7 - - ExoPlayer 2.18.6 - - ExoPlayer 2.18.5 + - Media3 1.2.1 + - Media3 1.2.0 + - Media3 main branch + - Media3 pre-release (alpha, beta or RC not in this list) + - Media3 1.1.1 / ExoPlayer 2.19.1 + - Media3 1.1.0 / ExoPlayer 2.19.0 + - Media3 1.0.2 / ExoPlayer 2.18.7 + - Media3 1.0.1 / ExoPlayer 2.18.6 + - Media3 1.0.0 / ExoPlayer 2.18.5 - ExoPlayer 2.18.4 - ExoPlayer 2.18.3 - ExoPlayer 2.18.2 @@ -50,10 +40,16 @@ body: - ExoPlayer 2.14.2 - ExoPlayer 2.14.1 - ExoPlayer 2.14.0 - - ExoPlayer `dev-v2` branch + - ExoPlayer dev-v2 branch - Older (unsupported) validations: required: true + - type: textarea + attributes: + label: More version details + description: > + Required if you selected `main` or `dev-v2` (please provide an exact commit SHA), + or 'pre-release' or 'older' (please provide the version). - type: textarea attributes: label: Devices that reproduce the issue @@ -114,7 +110,7 @@ body: * Attach a file here * Include a media URL * Refer to a piece of media from the demo app (e.g. `Misc > Dizzy (MP4)`) - * If you don't want to post media publicly please email the info to dev.exoplayer@gmail.com with subject 'Issue #\' after filing this issue, and note that you will do this here. + * If you don't want to post media publicly please email the info to android-media-github@google.com with subject 'Issue #\' after filing this issue, and note that you will do this here. * If you are certain the issue does not depend on the media being played, enter "Not applicable" here. For DRM-protected media please also include the scheme and license server URL. @@ -124,8 +120,8 @@ body: attributes: label: Bug Report description: | - After filing this issue please run `adb bugreport` shortly after reproducing the problem (ideally in the [demo app](https://github.com/androidx/media/tree/release/demos/main)) to capture a zip file, and email this to dev.exoplayer@gmail.com with subject 'Issue #\'. + After filing this issue please run `adb bugreport` shortly after reproducing the problem (ideally in the [demo app](https://github.com/androidx/media/tree/release/demos/main)) to capture a zip file, and email this to android-media-github@google.com with subject 'Issue #\'. **Note:** Logcat output is **not** the same as a full bug report, and is often missing information that's useful for diagnosing issues. Please ensure you're sending a full bug report zip file. options: - - label: You will email the zip file produced by `adb bugreport` to dev.exoplayer@gmail.com after filing this issue. + - label: You will email the zip file produced by `adb bugreport` to android-media-github@google.com after filing this issue. diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 386abe6bf9..3f753dbd18 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -39,6 +39,6 @@ Don't forget to check ExoPlayer's supported formats and devices, if applicable (https://developer.android.com/guide/topics/media/exoplayer/supported-formats). If there's something you don't want to post publicly, please submit the issue, -then email the link/bug report to dev.exoplayer@gmail.com using a subject in the -format "Issue #1234", where #1234 is your issue number (we don't reply to -emails). +then email the link/bug report to android-media-github@google.com using a +subject in the format "Issue #1234", where #1234 is your issue number (we don't +reply to emails). diff --git a/.idea/icon.svg b/.idea/icon.svg new file mode 100644 index 0000000000..f5071cd805 --- /dev/null +++ b/.idea/icon.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5908bd201c..04120620fe 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -38,6 +38,24 @@ you made on top of `main` using $ git diff -U0 main... | google-java-format-diff.py -p1 -i ``` +### Push access to PR branches + +Please ensure maintainers of this repository have push access to your PR branch +by ticking the `Allow edits from maintainers` checkbox when creating the PR (or +after it's created). See the +[GitHub docs](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork) +for more info. This allows us to make changes and fixes to the PR while it goes +through internal review, and ensures we don't create an +['evil' merge](https://git-scm.com/docs/gitglossary#Documentation/gitglossary.txt-aiddefevilmergeaevilmerge) +when it gets merged. + +This checkbox only appears on PRs from individual-owned forks +(https://github.com/orgs/community/discussions/5634). If you open a PR from an +organization-owned fork we will ask you to open a new one from an +individual-owned fork. If this isn't possible we can still merge the PR, but it +will result in an 'evil' merge because the changes and fixes we make during +internal review will be part of the merge commit. + ## Contributor license agreement Contributions to any Google project must be accompanied by a Contributor diff --git a/README.md b/README.md index 39a1e3e868..a57b9daa51 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,21 @@ # AndroidX Media AndroidX Media is a collection of libraries for implementing media use cases on -Android, including local playback (via ExoPlayer) and media sessions. +Android, including local playback (via ExoPlayer), video editing (via Transformer) and media sessions. ## Documentation * The [developer guide][] provides a wealth of information. * The [class reference][] documents the classes and methods. * The [release notes][] document the major changes in each release. +* The [media dev center][] provides samples and guidelines. * Follow our [developer blog][] to keep up to date with the latest developments! [developer guide]: https://developer.android.com/guide/topics/media/media3 [class reference]: https://developer.android.com/reference/androidx/media3/common/package-summary [release notes]: RELEASENOTES.md +[media dev center]: https://developer.android.com/media [developer blog]: https://medium.com/google-exoplayer ## Migration for existing ExoPlayer and MediaSession projects @@ -45,13 +47,21 @@ also possible to clone this GitHub repository and depend on the modules locally. #### 1. Add module dependencies The easiest way to get started using AndroidX Media is to add gradle -dependencies on the libraries you need in the `build.gradle` file of your app -module. +dependencies on the libraries you need in the `build.gradle.kts` file of your +app module. For example, to depend on ExoPlayer with DASH playback support and UI components you can add dependencies on the modules like this: -```gradle +```kotlin +implementation("androidx.media3:media3-exoplayer:1.X.X") +implementation("androidx.media3:media3-exoplayer-dash:1.X.X") +implementation("androidx.media3:media3-ui:1.X.X") +``` + +Or in Gradle Groovy DSL `build.gradle`: + +```groovy implementation 'androidx.media3:media3-exoplayer:1.X.X' implementation 'androidx.media3:media3-exoplayer-dash:1.X.X' implementation 'androidx.media3:media3-ui:1.X.X' @@ -73,10 +83,18 @@ details. #### 2. Turn on Java 8 support If not enabled already, you also need to turn on Java 8 support in all -`build.gradle` files depending on AndroidX Media, by adding the following to the -`android` section: +`build.gradle.kts` files depending on AndroidX Media, by adding the following to +the `android` section: + +```kotlin +compileOptions { + targetCompatibility = JavaVersion.VERSION_1_8 +} +``` + +Or in Gradle Groovy DSL `build.gradle`: -```gradle +```groovy compileOptions { targetCompatibility JavaVersion.VERSION_1_8 } @@ -101,18 +119,36 @@ git clone https://github.com/androidx/media.git cd media ``` -Next, add the following to your project's `settings.gradle` file, replacing +Next, add the following to your project's `settings.gradle.kts` file, replacing `path/to/media` with the path to your local copy: -```gradle +```kotlin +gradle.extra.apply { + set("androidxMediaModulePrefix", "media-") +} +apply(from = file("path/to/media/core_settings.gradle")) +``` + +Or in Gradle Groovy DSL `settings.gradle`: + +```groovy gradle.ext.androidxMediaModulePrefix = 'media-' apply from: file("path/to/media/core_settings.gradle") ``` You should now see the AndroidX Media modules appear as part of your project. -You can depend on them as you would on any other local module, for example: +You can depend on them from `build.gradle.kts` as you would on any other local +module, for example: + +```kotlin +implementation(project(":media-lib-exoplayer")) +implementation(project(":media-lib-exoplayer-dash")) +implementation(project(":media-lib-ui")) +``` + +Or in Gradle Groovy DSL `build.gradle`: -```gradle +```groovy implementation project(':media-lib-exoplayer') implementation project(':media-lib-exoplayer-dash') implementation project(':media-lib-ui') diff --git a/RELEASENOTES.md b/RELEASENOTES.md index fc703b1471..372e3faf53 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,738 @@ # Release notes +## 1.2 + +### 1.2.1 (2024-01-09) + +This release includes the following changes since the +[1.2.0 release](#120-2023-11-15): + +* ExoPlayer: + * Fix issue where manual seeks outside of the + `LiveConfiguration.min/maxOffset` range keep adjusting the offset back + to `min/maxOffset`. + * Fix issue that OPUS and VORBIS channel layouts are wrong for 3, 5, 6, 7 + and 8 channels + ([#8396](https://github.com/google/ExoPlayer/issues/8396)). + * Fix issue where track selections after seek to zero in a live stream + incorrectly let the stream start at its default position + ([#9347](https://github.com/google/ExoPlayer/issues/9347)). + * Fix the issue where new instances of `CmcdData.Factory` were receiving + negative values for `bufferedDurationUs` from chunk sources, resulting + in an `IllegalArgumentException` + ([#888](https://github.com/androidx/media/issues/888)). +* Transformer: + * Work around an issue where the encoder would throw at configuration time + due to setting a high operating rate. +* Extractors: + * Mark secondary (unplayable) HEVC tracks in JPEG motion photos as + `ROLE_FLAG_ALTERNATE` to prevent them being automatically selected for + playback because of their higher resolution. + * Fix wrong keyframe detection for TS H264 streams + ([#864](https://github.com/androidx/media/pull/864)). + * Fix duration estimation of TS streams that are longer than 47721 seconds + ([#855](https://github.com/androidx/media/issues/855)). +* Audio: + * Fix handling of EOS for `SilenceSkippingAudioProcessor` when called + multiple times ([#712](https://github.com/androidx/media/issues/712)). +* Video: + * Add workaround for a device issue on Galaxy Tab S7 FE, Chromecast with + Google TV, and Lenovo M10 FHD Plus that causes 60fps AVC streams to be + marked as unsupported + ([#693](https://github.com/androidx/media/issues/693)). +* Metadata: + * Fix bug where `MediaMetadata` was only populated from Vorbis comments + with upper-case keys + ([#876](https://github.com/androidx/media/issues/876)). + * Catch `OutOfMemoryError` when parsing very large ID3 frames, meaning + playback can continue without the tag info instead of playback failing + completely. +* DRM: + * Extend workaround for spurious ClearKey `https://default.url` license + URL to API 33+ (previously the workaround only applied on API 33 + exactly) ([#837](https://github.com/androidx/media/pull/837)). + * Fix `ERROR_DRM_SESSION_NOT_OPENED` when switching from encrypted to + clear content without a surface attached to the player. The error was + due to incorrectly using a secure decoder to play the clear content. +* Session: + * Put the custom keys and values in `MediaMetadataCompat` to + `MediaMetadata.extras` and `MediaMetadata.extras` to + `MediaMetadataCompat` + ([#756](https://github.com/androidx/media/issues/756), + [#802](https://github.com/androidx/media/issues/802)). + * Fix broadcasting `notifyChildrenChanged` for legacy controllers + ([#644](https://github.com/androidx/media/issues/644)). + * Fix a bug where setting a negative time for a disabled `setWhen` timer + of the notification caused a crash on some devices + ([#903](https://github.com/androidx/media/issues/903)). + * Fix `IllegalStateException` when the media notification controller + hasn't completed connecting when the first notification update is + requested ([#917](https://github.com/androidx/media/issues/917)). +* UI: + * Fix issue where forward and rewind buttons are not visible when used + with Material Design in a BottomSheetDialogFragment + ([#511](https://github.com/androidx/media/issues/511)). + * Fix issue where the numbers in the fast forward button of the + `PlayerControlView` were misaligned + ([#547](https://github.com/androidx/media/issues/547)). +* DASH Extension: + * Parse "f800" as channel count of 5 for Dolby in DASH manifest + ([#688](https://github.com/androidx/media/issues/688)). +* Decoder Extensions (FFmpeg, VP9, AV1, MIDI, etc.): + * MIDI: Fix issue where seeking forward skips the Program Change events + ([#704](https://github.com/androidx/media/issues/704)). + * Migrate to FFmpeg 6.0 and update supported NDK to `r26b` + ([#707](https://github.com/androidx/media/pull/707), + [#867](https://github.com/androidx/media/pull/867)). +* Cast Extension: + * Sanitize creation of a `Timeline` to not crash the app when loading + media fails on the cast device + ([#708](https://github.com/androidx/media/issues/708)). + +### 1.2.0 (2023-11-15) + +This release includes the following changes since the +[1.1.1 release](#111-2023-08-14): + +* Common Library: + * Add a `@Nullable Throwable` parameter to the methods in the `Log.Logger` + interface. The `message` parameter to these methods no longer contains + any information about the `Throwable` passed to the `Log.{d,i,w,e}()` + methods, so implementations will need to manually append this + information if desired (possibly using + `Logger.appendThrowableString(String, Throwable)`). + * Fix Kotlin compatibility issue where nullable generic type parameters + and nullable array element types are not detected as nullable. Examples + are `TrackSelectorResult` and `SimpleDecoder` method parameters + ([#6792](https://github.com/google/ExoPlayer/issues/6792)). + * Change default UI and notification behavior in + `Util.shouldShowPlayButton` to show a "play" button while playback is + temporarily suppressed (e.g. due to transient audio focus loss). The + legacy behavior can be maintained by using + `PlayerView.setShowPlayButtonIfPlaybackIsSuppressed(false)` or + `MediaSession.Builder.setShowPlayButtonIfPlaybackIsSuppressed(false)` + ([#11213](https://github.com/google/ExoPlayer/issues/11213)). + * Upgrade `androidx.annotation:annotation-experimental` to `1.3.1` to fix + https://issuetracker.google.com/251172715. + * Move `ExoPlayer.setAudioAttributes` to the `Player` interface. +* ExoPlayer: + * Fix seeking issues in AC4 streams caused by not identifying decode-only + samples correctly + ([#11000](https://github.com/google/ExoPlayer/issues/11000)). + * Add suppression of playback on unsuitable audio output devices (e.g. the + built-in speaker on Wear OS devices) when this feature is enabled via + `ExoPlayer.Builder.setSuppressPlaybackOnUnsuitableOutput`. The playback + suppression reason will be updated as + `Player.PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT` if playback + is attempted when no suitable audio outputs are available, or if all + suitable outputs are disconnected during playback. The suppression + reason will be removed when a suitable output is connected. + * Add `MediaSource.canUpdateMediaItem` and `MediaSource.updateMediaItem` + to accept `MediaItem` updates after creation via + `Player.replaceMediaItem(s)`. + * Allow `MediaItem` updates for all `MediaSource` classes provided by the + library via `Player.replaceMediaItem(s)` + ([#33](https://github.com/androidx/media/issues/33), + [#9978](https://github.com/google/ExoPlayer/issues/9978)). + * Rename `MimeTypes.TEXT_EXOPLAYER_CUES` to + `MimeTypes.APPLICATION_MEDIA3_CUES`. + * Add `PngExtractor` that sends and reads a whole PNG file into the + `TrackOutput` as one sample. + * Enhance `SequenceableLoader.continueLoading(long)` method in the + `SequenceableLoader` interface to + `SequenceableLoader.continueLoading(LoadingInfo loadingInfo)`. + `LoadingInfo` contains additional parameters, including `playbackSpeed` + and `lastRebufferRealtimeMs` in addition to the existing + `playbackPositionUs`. + * Enhance `ChunkSource.getNextChunk(long, long, List, ChunkHolder)` method + in the `ChunkSource` interface to `ChunkSource.getNextChunk(LoadingInfo, + long, List, ChunkHolder)`. + * Add additional fields to Common Media Client Data (CMCD) logging: buffer + starvation (`bs`), deadline (`dl`), playback rate (`pr`) and startup + (`su`) ([#8699](https://github.com/google/ExoPlayer/issues/8699)). + * Add luma and chroma bitdepth to `ColorInfo` + ([#491](https://github.com/androidx/media/pull/491)). + * Add additional fields to Common Media Client Data (CMCD) logging: next + object request (`nor`) and next range request (`nrr`) + ([#8699](https://github.com/google/ExoPlayer/issues/8699)). + * Add functionality to transmit Common Media Client Data (CMCD) data using + query parameters ([#553](https://github.com/androidx/media/issues/553)). + * Fix `ConcurrentModificationException` in `ExperimentalBandwidthMeter` + ([#612](https://github.com/androidx/media/issues/612)). + * Add `MediaPeriodId` parameter to + `CompositeMediaSource.getMediaTimeForChildMediaTime`. + * Support `ClippingMediaSource` (and other sources with period/window time + offsets) in `ConcatenatingMediaSource2` + ([#11226](https://github.com/google/ExoPlayer/issues/11226)). + * Change `BaseRenderer.onStreamChanged()` to also receive a + `MediaPeriodId` argument. +* Transformer: + * Parse EXIF rotation data for image inputs. + * Remove `TransformationRequest.HdrMode` annotation type and its + associated constants. Use `Composition.HdrMode` and its associated + constants instead. + * Simplify the `OverlaySettings` to fix rotation issues. + * Changed `frameRate` and `durationUs` parameters of + `SampleConsumer.queueInputBitmap` to `TimestampIterator`. +* Track Selection: + * Add `DefaultTrackSelector.Parameters.allowAudioNonSeamlessAdaptiveness` + to explicitly allow or disallow non-seamless adaptation. The default + stays at its current behavior of `true`. +* Extractors: + * MPEG-TS: Ensure the last frame is rendered by passing the last access + unit of a stream to the sample queue + ([#7909](https://github.com/google/ExoPlayer/issues/7909)). + * Fix typo when determining `rotationDegrees`. Changed + `projectionPosePitch` to `projectionPoseRoll` + ([#461](https://github.com/androidx/media/pull/461)). + * Remove the assumption that `Extractor` instances can be directly + inspected with `instanceof`. If you want runtime access to the + implementation details of an `Extractor` you must first call + `Extractor.getUnderlyingInstance`. + * Add `BmpExtractor`. + * Add `WebpExtractor`. + * Add `HeifExtractor`. + * Add + [QuickTime classic](https://developer.apple.com/standards/qtff-2001.pdf) + support to `Mp4Extractor`. +* Audio: + * Add support for 24/32-bit big-endian PCM in MP4 and Matroska, and parse + PCM encoding for `lpcm` in MP4. + * Add support for extracting Vorbis audio in MP4. + * Add `AudioSink.getFormatOffloadSupport(Format)` that retrieves level of + offload support the sink can provide for the format through a + `DefaultAudioOffloadSupportProvider`. It returns the new + `AudioOffloadSupport` that contains `isFormatSupported`, + `isGaplessSupported`, and `isSpeedChangeSupported`. + * Add `AudioSink.setOffloadMode()` through which the offload configuration + on the audio sink is configured. Default is + `AudioSink.OFFLOAD_MODE_DISABLED`. + * Offload can be enabled through `setAudioOffloadPreference` in + `TrackSelectionParameters`. If the set preference is to enable, the + device supports offload for the format, and the track selection is a + single audio track, then audio offload will be enabled. + * If `audioOffloadModePreference` is set to + `AUDIO_OFFLOAD_MODE_PREFERENCE_REQUIRED`, then the + `DefaultTrackSelector` will only select an audio track and only if that + track's format is supported in offload. If no audio track is supported + in offload, then no track will be selected. + * Disabling gapless support for offload when pre-API level 33 due to + playback position issue after track transition. + * Remove parameter `enableOffload` from + `DefaultRenderersFactory.buildAudioSink` method signature. + * Remove method `DefaultAudioSink.Builder.setOffloadMode`. + * Remove intdef value + `DefaultAudioSink.OffloadMode.OFFLOAD_MODE_ENABLED_GAPLESS_DISABLED`. + * Add support for Opus gapless metadata during offload playback. + * Allow renderer recovery by disabling offload if failed at first write + ([#627](https://github.com/androidx/media/issues/627)). + * Enable Offload Scheduling by default for audio-only offloaded playback. + * Delete `ExoPlayer.experimentalSetOffloadSchedulingEnabled` and + `AudioOffloadListener.onExperimentalOffloadSchedulingEnabledChanged`. + * Renamed `onExperimentalSleepingForOffloadChanged` as + `onSleepingForOffloadChanged` and `onExperimentalOffloadedPlayback` as + `onOffloadedPlayback`. + * Move audio offload mode related `TrackSelectionParameters` interfaces + and definitions to an inner `AudioOffloadPreferences` class. + * Add `onAudioTrackInitialized` and `onAudioTrackReleased` callbacks to + `AnalyticsListener`, `AudioRendererEventListener` and + `AudioSink.Listener`. + * Fix DTS Express audio buffer underflow issue + ([#650](https://github.com/androidx/media/pull/650)). + * Fix bug where the capabilities check for E-AC3-JOC throws an + `IllegalArgumentException` + ([#677](https://github.com/androidx/media/issues/677)). +* Video: + * Allow `MediaCodecVideoRenderer` to use a custom + `VideoFrameProcessor.Factory`. + * Fix bug where the first frame couldn't be rendered if the audio stream + starts with negative timestamps + ([#291](https://github.com/androidx/media/issues/291)). +* Text: + * Remove `ExoplayerCuesDecoder`. Text tracks with `sampleMimeType = + application/x-media3-cues` are now directly handled by `TextRenderer` + without needing a `SubtitleDecoder` instance. +* Metadata: + * `MetadataDecoder.decode` will no longer be called for "decode-only" + samples as the implementation must return null anyway. +* Effect: + * Add `VideoFrameProcessor.queueInputBitmap(Bitmap, Iterator)` + queuing bitmap input by timestamp. + * Change `VideoFrameProcessor.registerInputStream()` to be non-blocking. + Apps must implement + `VideoFrameProcessor.Listener#onInputStreamRegistered()`. + * Changed `frameRate` and `durationUs` parameters of + `VideoFrameProcessor.queueInputBitmap` to `TimestampIterator`. +* IMA extension: + * Fix bug where a multi-period DASH live stream that is not the first item + in a playlist can throw an exception + ([#571](https://github.com/androidx/media/issues/571)). + * Release StreamManager before calling `AdsLoader.destroy()` + * Bump IMA SDK version to 3.31.0. +* Session: + * Set the notifications foreground service behavior to + `FOREGROUND_SERVICE_IMMEDIATE` in `DefaultMediaNotificationProvider` + ([#167](https://github.com/androidx/media/issues/167)). + * Use only + `android.media.session.MediaSession.setMediaButtonBroadcastReceiver()` + above API 31 to avoid problems with deprecated API on Samsung devices + ([#167](https://github.com/androidx/media/issues/167)). + * Use the media notification controller as proxy to set available commands + and custom layout used to populate the notification and the platform + session. + * Convert media button events that are received by + `MediaSessionService.onStartCommand()` within Media3 instead of routing + them to the platform session and back to Media3. With this, the caller + controller is always the media notification controller and apps can + easily recognize calls coming from the notification in the same way on + all supported API levels. + * Fix bug where `MediaController.getCurrentPosition()` is not advancing + when connected to a legacy `MediaSessionCompat`. + * Add `MediaLibrarySession.getSubscribedControllers(mediaId)` for + convenience. + * Override `MediaLibrarySession.Callback.onSubscribe()` to assert the + availability of the parent ID for which the controller subscribes. If + successful, the subscription is accepted and `notifyChildrenChanged()` + is called immediately to inform the browser + ([#561](https://github.com/androidx/media/issues/561)). + * Add session demo module for Automotive OS and enable session demo for + Android Auto. + * Do not set the queue of the framework session when + `COMMAND_GET_TIMELINE` is not available for the media notification + controller. With Android Auto as the client controller reading from the + framework session, this has the effect that the `queue` button in the UI + of Android Auto is not displayed + ([#339](https://github.com/androidx/media/issues/339)). + * Use `DataSourceBitmapLoader` by default instead of `SimpleBitmapLoader` + ([#271](https://github.com/androidx/media/issues/271), + [#327](https://github.com/androidx/media/issues/327)). + * Add `MediaSession.Callback.onMediaButtonEvent(Intent)` that allows apps + to override the default media button event handling. +* UI: + * Add a `Player.Listener` implementation for Wear OS devices that handles + playback suppression due to + `Player.PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT` by + launching a system dialog to allow a user to connect a suitable audio + output (e.g. bluetooth headphones). The listener will auto-resume + playback if a suitable device is connected within a configurable timeout + (default is 5 minutes). +* Downloads: + * Declare "data sync" foreground service type for `DownloadService` for + Android 14 compatibility. When using this service, the app also needs to + add `dataSync` as `foregroundServiceType` in the manifest and add the + `FOREGROUND_SERVICE_DATA_SYNC` permission + ([#11239](https://github.com/google/ExoPlayer/issues/11239)). +* HLS Extension: + * Refresh the HLS live playlist with an interval calculated from the last + load start time rather than the last load completed time + ([#663](https://github.com/androidx/media/issues/663)). +* DASH Extension: + * Allow multiple of the same DASH identifier in segment template url. + * Add experimental support for parsing subtitles during extraction. This + has better support for merging overlapping subtitles, including + resolving flickering when transitioning between subtitle segments. You + can enable this using + `DashMediaSource.Factory.experimentalParseSubtitlesDuringExtraction()` + ([#288](https://github.com/androidx/media/issues/288)). +* RTSP Extension: + * Fix a race condition that could lead to `IndexOutOfBoundsException` when + falling back to TCP, or playback hanging in some situations. + * Check state in RTSP setup when returning loading state of + `RtspMediaPeriod` + ([#577](https://github.com/androidx/media/issues/577)). + * Ignore custom Rtsp request methods in Options response public header + ([#613](https://github.com/androidx/media/issues/613)). + * Use RTSP Setup Response timeout value in time interval of sending + keep-alive RTSP Options requests + ([#662](https://github.com/androidx/media/issues/662)). +* Decoder Extensions (FFmpeg, VP9, AV1, MIDI, etc.): + * Release the MIDI decoder module, which provides support for playback of + standard MIDI files using the Jsyn library to synthesize audio. + * Add `DecoderOutputBuffer.shouldBeSkipped` to directly mark output + buffers that don't need to be presented. This is preferred over + `C.BUFFER_FLAG_DECODE_ONLY` that will be deprecated. + * Add `Decoder.setOutputStartTimeUs` and + `SimpleDecoder.isAtLeastOutputStartTimeUs` to allow decoders to drop + decode-only samples before the start time. This should be preferred to + `Buffer.isDecodeOnly` that will be deprecated. + * Fix bug publishing MIDI decoder artifact to Maven repository. The + artifact is renamed to `media3-exoplayer-midi` + ([#734](https://github.com/androidx/media/issues/734)). +* Leanback extension: + * Fix bug where disabling a surface can cause an `ArithmeticException` in + Leanback code ([#617](https://github.com/androidx/media/issues/617)). +* Test Utilities: + * Make `TestExoPlayerBuilder` and `FakeClock` compatible with Espresso UI + tests and Compose UI tests. This fixes a bug where playback advances + non-deterministically during Espresso or Compose view interactions. +* Remove deprecated symbols: + * Remove + `TransformationRequest.Builder.setEnableRequestSdrToneMapping(boolean)` + and + `TransformationRequest.Builder.experimental_setEnableHdrEditing(boolean)`. + Use `Composition.Builder.setHdrMode(int)` and pass the `Composition` to + `Transformer.start(Composition, String)` instead. + * Remove deprecated `DownloadNotificationHelper.buildProgressNotification` + method, use a non deprecated method that takes a `notMetRequirements` + parameter instead. + +### 1.2.0-rc01 (2023-11-01) + +Use the 1.2.0 [stable version](#120-2023-11-15). + +### 1.2.0-beta01 (2023-10-18) + +Use the 1.2.0 [stable version](#120-2023-11-15). + +### 1.2.0-alpha02 (2023-09-29) + +Use the 1.2.0 [stable version](#120-2023-11-15). + +### 1.2.0-alpha01 (2023-08-17) + +Use the 1.2.0 [stable version](#120-2023-11-15). + +## 1.1 + +### 1.1.1 (2023-08-14) + +This release corresponds to the +[ExoPlayer 2.19.1 release](https://github.com/google/ExoPlayer/releases/tag/r2.19.1). + +This release includes the following changes since the +[1.1.0 release](#110-2023-07-05): + +* Common Library: + * Remove accidentally added `multidex` dependency from all modules + ([#499](https://github.com/androidx/media/issues/499)). +* ExoPlayer: + * Fix issue in `PlaybackStatsListener` where spurious `PlaybackStats` are + created after the playlist is cleared. + * Add additional fields to Common Media Client Data (CMCD) logging: + streaming format (sf), stream type (st), version (v), top birate (tb), + object duration (d), measured throughput (mtp) and object type (ot) + ([#8699](https://github.com/google/ExoPlayer/issues/8699)). +* Audio: + * Fix a bug where `Player.getState()` never transitioned to `STATE_ENDED` + when playing very short files + ([#538](https://github.com/androidx/media/issues/538)). +* Audio Offload: + * Prepend Ogg ID Header and Comment Header Pages to bitstream for + offloaded Opus playback in accordance with RFC 7845. +* Video: + * H.265/HEVC: Fix parsing SPS short and long term reference picture info. +* Text: + * CEA-608: Change cue truncation logic to only consider visible text. + Previously indent and tab offset were included when limiting the cue + length to 32 characters (which was technically correct by the spec) + ([#11019](https://github.com/google/ExoPlayer/issues/11019)). +* IMA extension: + * Bump IMA SDK version to 3.30.3. +* Session: + * Add custom layout to the state of the controller and provide a getter to + access it. When the custom layout changes, + `MediaController.Listener.onCustomLayoutChanged` is called. Apps that + want to send different custom layouts to different Media3 controller can + do this in `MediaSession.Callback.onConnect` by using an + `AcceptedResultBuilder` to make sure the custom layout is available to + the controller when connection completes. + * Fix cases where `MediaLibraryServiceLegacyStub` sent an error to a + `Result` that didn't support this which produced an + `UnsupportedOperationException` + ([#78](https://github.com/androidx/media/issues/78)). + * Fix the way `PlayerWrapper` creates a `VolumeProviderCompat` by + determining `volumeControlType` through both legacy commands + (`COMMAND_ADJUST_DEVICE_VOLUME` and `COMMAND_SET_DEVICE_VOLUME`) and new + commands (`COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS` and + `COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS`) + ([#554](https://github.com/androidx/media/issues/554)). + +### 1.1.0 (2023-07-05) + +This release corresponds to the +[ExoPlayer 2.19.0 release](https://github.com/google/ExoPlayer/releases/tag/r2.19.0). + +This release contains the following changes since the +[1.0.2 release](#102-2023-05-18): + +* Common Library: + * Add suppression reason for unsuitable audio route and play when ready + change reason for suppressed too long. + ([#15](https://github.com/androidx/media/issues/15)). + * Add commands to Player: + * `COMMAND_GET_METADATA` + * `COMMAND_SET_PLAYLIST_METADATA` + * `COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS` + * `COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS` + * Add overloaded methods to Player which allow users to specify volume + flags: + * `void setDeviceVolume(int, int)` + * `void increaseDeviceVolume(int)` + * `void decreaseDeviceVolume(int)` + * `void setDeviceMuted(boolean, int)` + * Add `Builder` for `DeviceInfo` and deprecate existing constructor. + * Add `DeviceInfo.routingControllerId` to specify the routing controller + ID for remote playbacks. + * Add `Player.replaceMediaItem(s)` as a shortcut to adding and removing + items at the same position + ([#8046](https://github.com/google/ExoPlayer/issues/8046)). +* ExoPlayer: + * Allow ExoPlayer to have control of device volume methods only if + explicitly opted in. Use + `ExoPlayer.Builder.setDeviceVolumeControlEnabled` to have access to: + * `getDeviceVolume()` + * `isDeviceMuted()` + * `setDeviceVolume(int)` and `setDeviceVolume(int, int)` + * `increaseDeviceVolume(int)` and `increaseDeviceVolume(int, int)` + * `decreaseDeviceVolume(int)` and `decreaseDeviceVolume(int, int)` + * Add `FilteringMediaSource` that allows to filter available track types + from a `MediaSource`. + * Add support for including Common Media Client Data (CMCD) in the + outgoing requests of adaptive streaming formats DASH, HLS, and + SmoothStreaming. The following fields, `br`, `bl`, `cid`, `rtp`, and + `sid`, have been incorporated + ([#8699](https://github.com/google/ExoPlayer/issues/8699)). API + structure and API methods: + * CMCD logging is disabled by default, use + `MediaSource.Factory.setCmcdConfigurationFactory(CmcdConfiguration.Factory + cmcdConfigurationFactory)` to enable it. + * All keys are enabled by default, override + `CmcdConfiguration.RequestConfig.isKeyAllowed(String key)` to filter + out which keys are logged. + * Override `CmcdConfiguration.RequestConfig.getCustomData()` to enable + custom key logging. + * Add additional action to manifest of main demo to make it easier to + start the demo app with a custom `*.exolist.json` file + ([#439](https://github.com/androidx/media/pull/439)). + * Add `ExoPlayer.setVideoEffects()` for using `Effect` during video + playback. + * Update `SampleQueue` to store `sourceId` as a `long` rather than an + `int`. This changes the signatures of public methods + `SampleQueue.sourceId` and `SampleQueue.peekSourceId`. + * Add parameters to `LoadControl` methods `shouldStartPlayback` and + `onTracksSelected` that allow associating these methods with the + relevant `MediaPeriod`. + * Change signature of + `ServerSideAdInsertionMediaSource.setAdPlaybackStates(Map)` by adding a timeline parameter that contains the + periods with the UIDs used as keys in the map. This is required to avoid + concurrency issues with multi-period live streams. + * Deprecate `EventDispatcher.withParameters(int windowIndex, @Nullable + MediaPeriodId mediaPeriodId, long mediaTimeOffsetMs)` and + `BaseMediaSource.createEventDispatcher(..., long mediaTimeOffsetMs)`. + The variant of the methods without the `mediaTimeOffsetUs` can be called + instead. Note that even for the deprecated variants, the offset is not + anymore added to `startTimeUs` and `endTimeUs` of the `MediaLoadData` + objects that are dispatched by the dispatcher. + * Rename `ExoTrackSelection.blacklist` to `excludeTrack` and + `isBlacklisted` to `isTrackExcluded`. + * Fix inconsistent behavior between `ExoPlayer.setMediaItem(s)` and + `addMediaItem(s)` when called on an empty playlist. +* Transformer: + * Remove `Transformer.Builder.setMediaSourceFactory(MediaSource.Factory)`. + Use `ExoPlayerAssetLoader.Factory(MediaSource.Factory)` and + `Transformer.Builder.setAssetLoaderFactory(AssetLoader.Factory)` + instead. + * Remove `Transformer.startTransformation(MediaItem, + ParcelFileDescriptor)`. + * Fix a bug where transformation could get stuck (leading to muxer + timeout) if the end of the video stream was signaled at the moment when + an input frame was pending processing. + * Query codecs via `MediaCodecList` instead of using + `findDecoder/EncoderForFormat` utilities, to expand support. + * Remove B-frame configuration in `DefaultEncoderFactory` because it + doesn't work on some devices. +* Track selection: + * Add + `DefaultTrackSelector.Parameters.allowInvalidateSelectionsForRendererCapabilitiesChange` + which is disabled by default. When enabled, the `DefaultTrackSelector` + will trigger a new track selection when the renderer capabilities + changed. +* Extractors: + * Ogg: Fix bug when seeking in files with a long duration + ([#391](https://github.com/androidx/media/issues/391)). + * FMP4: Fix issue where `TimestampAdjuster` initializes a wrong timestamp + offset with metadata sample time from emsg atom + ([#356](https://github.com/androidx/media/issues/356)). +* Audio: + * Fix bug where some playbacks fail when tunneling is enabled and + `AudioProcessors` are active, e.g. for gapless trimming + ([#10847](https://github.com/google/ExoPlayer/issues/10847)). + * Encapsulate Opus frames in Ogg packets in direct playbacks (offload). + * Extrapolate current position during sleep with offload scheduling. + * Add `Renderer.release()` and `AudioSink.release()` for releasing the + resources at the end of player's lifecycle. + * Listen to audio capabilities changes in `DefaultAudioSink`. Add a + required parameter `context` in the constructor of `DefaultAudioSink`, + with which the `DefaultAudioSink` will register as the listener to the + `AudioCapabilitiesReceiver` and update its `audioCapabilities` property + when informed with a capabilities change. + * Propagate audio capabilities changes via a new event + `onAudioCapabilitiesChanged` in `AudioSink.Listener` interface, and a + new interface `RendererCapabilities.Listener` which triggers + `onRendererCapabilitiesChanged` events. + * Add `ChannelMixingAudioProcessor` for applying scaling/mixing to audio + channels. + * Add new int value `DISCARD_REASON_AUDIO_BYPASS_POSSIBLE` to + `DecoderDiscardReasons` to discard audio decoder when bypass mode is + possible after audio capabilities change. + * Add direct playback support for DTS Express and DTS:X + ([#335](https://github.com/androidx/media/pull/335)). +* Video: + * Make `MediaCodecVideoRenderer` report a `VideoSize` with a width and + height of 0 when the renderer is disabled. + `Player.Listener.onVideoSizeChanged` is called accordingly when + `Player.getVideoSize()` changes. With this change, ExoPlayer's video + size with `MediaCodecVideoRenderer` has a width and height of 0 when + `Player.getCurrentTracks` does not support video, or the size of the + supported video track is not yet determined. +* DRM: + * Reduce the visibility of several internal-only methods on + `DefaultDrmSession` that aren't expected to be called from outside the + DRM package: + * `void onMediaDrmEvent(int)` + * `void provision()` + * `void onProvisionCompleted()` + * `onProvisionError(Exception, boolean)` +* Muxer: + * Add a new muxer library which can be used to create an MP4 container + file. +* IMA extension: + * Enable multi-period live DASH streams for DAI. Please note that the + current implementation does not yet support seeking in live streams + ([#10912](https://github.com/google/ExoPlayer/issues/10912)). + * Fix a bug where a new ad group is inserted in live streams because the + calculated content position in consecutive timelines varies slightly. +* Session: + * Add helper method `MediaSession.getControllerForCurrentRequest` to + obtain information about the controller that is currently calling + a`Player` method. + * Add `androidx.media3.session.MediaButtonReceiver` to enable apps to + implement playback resumption with media button events sent by, for + example, a Bluetooth headset + ([#167](https://github.com/androidx/media/issues/167)). + * Add default implementation to `MediaSession.Callback.onAddMediaItems` to + allow requested `MediaItems` to be passed onto `Player` if they have + `LocalConfiguration` (e.g. URI) + ([#282](https://github.com/androidx/media/issues/282)). + * Add "seek to previous" and "seek to next" command buttons on compact + media notification view by default for Android 12 and below + ([#410](https://github.com/androidx/media/issues/410)). +* UI: + * Add Util methods `shouldShowPlayButton` and + `handlePlayPauseButtonAction` to write custom UI elements with a + play/pause button. +* RTSP Extension: + * For MPEG4-LATM, use default profile-level-id value if absent in Describe + Response SDP message + ([#302](https://github.com/androidx/media/issues/302)). + * Use base Uri for relative path resolution from the RTSP session if + present in DESCRIBE response header + ([#11160](https://github.com/google/ExoPlayer/issues/11160)). +* DASH Extension: + * Remove the media time offset from `MediaLoadData.startTimeMs` and + `MediaLoadData.endTimeMs` for multi period DASH streams. + * Fix a bug where re-preparing a multi-period live Dash media source + produced a `IndexOutOfBoundsException` + ([#10838](https://github.com/google/ExoPlayer/issues/10838)). +* HLS Extension: + * Add + `HlsMediaSource.Factory.setTimestampAdjusterInitializationTimeoutMs(long)` + to set a timeout for the loading thread to wait for the + `TimestampAdjuster` to initialize. If the initialization doesn't + complete before the timeout, a `PlaybackException` is thrown to avoid + the playback endless stalling. The timeout is set to zero by default + ([#323](https://github.com/androidx/media/issues//323)). +* Test Utilities: + * Check for URI scheme case insensitivity in `DataSourceContractTest`. +* Remove deprecated symbols: + * Remove `DefaultAudioSink` constructors, use `DefaultAudioSink.Builder` + instead. + * Remove `HlsMasterPlaylist`, use `HlsMultivariantPlaylist` instead. + * Remove `Player.stop(boolean)`. Use `Player.stop()` and + `Player.clearMediaItems()` (if `reset` is `true`) instead. + * Remove two deprecated `SimpleCache` constructors, use a non-deprecated + constructor that takes a `DatabaseProvider` instead for better + performance. + * Remove `DefaultBandwidthMeter` constructor, use + `DefaultBandwidthMeter.Builder` instead. + * Remove `DefaultDrmSessionManager` constructors, use + `DefaultDrmSessionManager.Builder` instead. + * Remove two deprecated `HttpDataSource.InvalidResponseCodeException` + constructors, use a non-deprecated constructor that accepts additional + fields(`cause`, `responseBody`) to enhance error logging. + * Remove `DownloadHelper.forProgressive`, `DownloadHelper.forHls`, + `DownloadHelper.forDash`, and `DownloadHelper.forSmoothStreaming`, use + `DownloadHelper.forMediaItem` instead. + * Remove deprecated `DownloadService` constructor, use a non deprecated + constructor that includes the option to provide a + `channelDescriptionResourceId` parameter. + * Remove deprecated String constants for Charsets (`ASCII_NAME`, + `UTF8_NAME`, `ISO88591_NAME`, `UTF16_NAME` and `UTF16LE_NAME`), use + Kotlin Charsets from the `kotlin.text` package, the + `java.nio.charset.StandardCharsets` or the + `com.google.common.base.Charsets` instead. + * Remove deprecated `WorkManagerScheduler` constructor, use a non + deprecated constructor that includes the option to provide a `Context` + parameter instead. + * Remove the deprecated methods `createVideoSampleFormat`, + `createAudioSampleFormat`, `createContainerFormat`, and + `createSampleFormat`, which were used to instantiate the `Format` class. + Instead use `Format.Builder` for creating instances of `Format`. + * Remove the deprecated methods `copyWithMaxInputSize`, + `copyWithSubsampleOffsetUs`, `copyWithLabel`, + `copyWithManifestFormatInfo`, `copyWithGaplessInfo`, + `copyWithFrameRate`, `copyWithDrmInitData`, `copyWithMetadata`, + `copyWithBitrate` and `copyWithVideoSize`, use `Format.buildUpon()` and + setter methods instead. + * Remove deprecated `ExoPlayer.retry()`, use `prepare()` instead. + * Remove deprecated zero-arg `DefaultTrackSelector` constructor, use + `DefaultTrackSelector(Context)` instead. + * Remove deprecated `OfflineLicenseHelper` constructor, use + `OfflineLicenseHelper(DefaultDrmSessionManager, + DrmSessionEventListener.EventDispatcher)` instead. + * Remove deprecated `DownloadManager` constructor, use the constructor + that takes an `Executor` instead. + * Remove deprecated `Cue` constructors, use `Cue.Builder` instead. + * Remove deprecated `OfflineLicenseHelper` constructor, use + `OfflineLicenseHelper(DefaultDrmSessionManager, + DrmSessionEventListener.EventDispatcher)` instead. + * Remove four deprecated `AnalyticsListener` methods: + * `onDecoderEnabled`, use `onAudioEnabled` and/or `onVideoEnabled` + instead. + * `onDecoderInitialized`, use `onAudioDecoderInitialized` and/or + `onVideoDecoderInitialized` instead. + * `onDecoderInputFormatChanged`, use `onAudioInputFormatChanged` + and/or `onVideoInputFormatChanged` instead. + * `onDecoderDisabled`, use `onAudioDisabled` and/or `onVideoDisabled` + instead. + * Remove the deprecated `Player.Listener.onSeekProcessed` and + `AnalyticsListener.onSeekProcessed`, use `onPositionDiscontinuity` with + `DISCONTINUITY_REASON_SEEK` instead. + * Remove `ExoPlayer.setHandleWakeLock(boolean)`, use `setWakeMode(int)` + instead. + * Remove deprecated + `DefaultLoadControl.Builder.createDefaultLoadControl()`, use `build()` + instead. + * Remove deprecated `MediaItem.PlaybackProperties`, use + `MediaItem.LocalConfiguration` instead. Deprecated field + `MediaItem.playbackProperties` is now of type + `MediaItem.LocalConfiguration`. + +### 1.1.0-rc01 (2023-06-21) + +Use the 1.1.0 [stable version](#110-2023-07-05). + +### 1.1.0-beta01 (2023-06-07) + +Use the 1.1.0 [stable version](#110-2023-07-05). + +### 1.1.0-alpha01 (2023-05-10) + +Use the 1.1.0 [stable version](#110-2023-07-05). + +## 1.0 + ### 1.0.2 (2023-05-18) This release corresponds to the diff --git a/api.txt b/api.txt index c2f1a94d1a..32a5ca8539 100644 --- a/api.txt +++ b/api.txt @@ -1,4 +1,4 @@ -// Signature format: 3.0 +// Signature format: 2.0 package androidx.media3.common { public final class AdOverlayInfo { @@ -127,6 +127,11 @@ package androidx.media3.common { field public static final int USAGE_VOICE_COMMUNICATION = 2; // 0x2 field public static final int USAGE_VOICE_COMMUNICATION_SIGNALLING = 3; // 0x3 field public static final java.util.UUID UUID_NIL; + field public static final int VOLUME_FLAG_ALLOW_RINGER_MODES = 2; // 0x2 + field public static final int VOLUME_FLAG_PLAY_SOUND = 4; // 0x4 + field public static final int VOLUME_FLAG_REMOVE_SOUND_AND_VIBRATE = 8; // 0x8 + field public static final int VOLUME_FLAG_SHOW_UI = 1; // 0x1 + field public static final int VOLUME_FLAG_VIBRATE = 16; // 0x10 field public static final int WAKE_MODE_LOCAL = 1; // 0x1 field public static final int WAKE_MODE_NETWORK = 2; // 0x2 field public static final int WAKE_MODE_NONE = 0; // 0x0 @@ -163,6 +168,9 @@ package androidx.media3.common { @IntDef(open=true, value={androidx.media3.common.C.TRACK_TYPE_UNKNOWN, androidx.media3.common.C.TRACK_TYPE_DEFAULT, androidx.media3.common.C.TRACK_TYPE_AUDIO, androidx.media3.common.C.TRACK_TYPE_VIDEO, androidx.media3.common.C.TRACK_TYPE_TEXT, androidx.media3.common.C.TRACK_TYPE_IMAGE, androidx.media3.common.C.TRACK_TYPE_METADATA, androidx.media3.common.C.TRACK_TYPE_CAMERA_MOTION, androidx.media3.common.C.TRACK_TYPE_NONE}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE) public static @interface C.TrackType { } + @IntDef(flag=true, value={androidx.media3.common.C.VOLUME_FLAG_SHOW_UI, androidx.media3.common.C.VOLUME_FLAG_ALLOW_RINGER_MODES, androidx.media3.common.C.VOLUME_FLAG_PLAY_SOUND, androidx.media3.common.C.VOLUME_FLAG_REMOVE_SOUND_AND_VIBRATE, androidx.media3.common.C.VOLUME_FLAG_VIBRATE}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE_USE}) public static @interface C.VolumeFlags { + } + @IntDef({androidx.media3.common.C.WAKE_MODE_NONE, androidx.media3.common.C.WAKE_MODE_LOCAL, androidx.media3.common.C.WAKE_MODE_NETWORK}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface C.WakeMode { } @@ -170,9 +178,18 @@ package androidx.media3.common { field public static final int PLAYBACK_TYPE_LOCAL = 0; // 0x0 field public static final int PLAYBACK_TYPE_REMOTE = 1; // 0x1 field public static final androidx.media3.common.DeviceInfo UNKNOWN; - field public final int maxVolume; - field public final int minVolume; + field @IntRange(from=0) public final int maxVolume; + field @IntRange(from=0) public final int minVolume; field @androidx.media3.common.DeviceInfo.PlaybackType public final int playbackType; + field @Nullable public final String routingControllerId; + } + + public static final class DeviceInfo.Builder { + ctor public DeviceInfo.Builder(@androidx.media3.common.DeviceInfo.PlaybackType int); + method public androidx.media3.common.DeviceInfo build(); + method public androidx.media3.common.DeviceInfo.Builder setMaxVolume(@IntRange(from=0) int); + method public androidx.media3.common.DeviceInfo.Builder setMinVolume(@IntRange(from=0) int); + method public androidx.media3.common.DeviceInfo.Builder setRoutingControllerId(@Nullable String); } @IntDef({androidx.media3.common.DeviceInfo.PLAYBACK_TYPE_LOCAL, androidx.media3.common.DeviceInfo.PLAYBACK_TYPE_REMOTE}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE) public static @interface DeviceInfo.PlaybackType { @@ -209,8 +226,8 @@ package androidx.media3.common { public final class MediaItem { method public androidx.media3.common.MediaItem.Builder buildUpon(); - method public static androidx.media3.common.MediaItem fromUri(String); method public static androidx.media3.common.MediaItem fromUri(android.net.Uri); + method public static androidx.media3.common.MediaItem fromUri(String); field public static final String DEFAULT_MEDIA_ID = ""; field public static final androidx.media3.common.MediaItem EMPTY; field public final androidx.media3.common.MediaItem.ClippingConfiguration clippingConfiguration; @@ -247,8 +264,8 @@ package androidx.media3.common { method public androidx.media3.common.MediaItem.Builder setRequestMetadata(androidx.media3.common.MediaItem.RequestMetadata); method public androidx.media3.common.MediaItem.Builder setSubtitleConfigurations(java.util.List); method public androidx.media3.common.MediaItem.Builder setTag(@Nullable Object); - method public androidx.media3.common.MediaItem.Builder setUri(@Nullable String); method public androidx.media3.common.MediaItem.Builder setUri(@Nullable android.net.Uri); + method public androidx.media3.common.MediaItem.Builder setUri(@Nullable String); } public static class MediaItem.ClippingConfiguration { @@ -318,7 +335,7 @@ package androidx.media3.common { method public androidx.media3.common.MediaItem.LiveConfiguration.Builder setTargetOffsetMs(long); } - public static class MediaItem.LocalConfiguration { + public static final class MediaItem.LocalConfiguration { field @Nullable public final androidx.media3.common.MediaItem.AdsConfiguration adsConfiguration; field @Nullable public final androidx.media3.common.MediaItem.DrmConfiguration drmConfiguration; field @Nullable public final String mimeType; @@ -369,14 +386,50 @@ package androidx.media3.common { public final class MediaMetadata { method public androidx.media3.common.MediaMetadata.Builder buildUpon(); field public static final androidx.media3.common.MediaMetadata EMPTY; - field public static final int FOLDER_TYPE_ALBUMS = 2; // 0x2 - field public static final int FOLDER_TYPE_ARTISTS = 3; // 0x3 - field public static final int FOLDER_TYPE_GENRES = 4; // 0x4 - field public static final int FOLDER_TYPE_MIXED = 0; // 0x0 - field public static final int FOLDER_TYPE_NONE = -1; // 0xffffffff - field public static final int FOLDER_TYPE_PLAYLISTS = 5; // 0x5 - field public static final int FOLDER_TYPE_TITLES = 1; // 0x1 - field public static final int FOLDER_TYPE_YEARS = 6; // 0x6 + field @Deprecated public static final int FOLDER_TYPE_ALBUMS = 2; // 0x2 + field @Deprecated public static final int FOLDER_TYPE_ARTISTS = 3; // 0x3 + field @Deprecated public static final int FOLDER_TYPE_GENRES = 4; // 0x4 + field @Deprecated public static final int FOLDER_TYPE_MIXED = 0; // 0x0 + field @Deprecated public static final int FOLDER_TYPE_NONE = -1; // 0xffffffff + field @Deprecated public static final int FOLDER_TYPE_PLAYLISTS = 5; // 0x5 + field @Deprecated public static final int FOLDER_TYPE_TITLES = 1; // 0x1 + field @Deprecated public static final int FOLDER_TYPE_YEARS = 6; // 0x6 + field public static final int MEDIA_TYPE_ALBUM = 10; // 0xa + field public static final int MEDIA_TYPE_ARTIST = 11; // 0xb + field public static final int MEDIA_TYPE_AUDIO_BOOK = 15; // 0xf + field public static final int MEDIA_TYPE_AUDIO_BOOK_CHAPTER = 2; // 0x2 + field public static final int MEDIA_TYPE_FOLDER_ALBUMS = 21; // 0x15 + field public static final int MEDIA_TYPE_FOLDER_ARTISTS = 22; // 0x16 + field public static final int MEDIA_TYPE_FOLDER_AUDIO_BOOKS = 26; // 0x1a + field public static final int MEDIA_TYPE_FOLDER_GENRES = 23; // 0x17 + field public static final int MEDIA_TYPE_FOLDER_MIXED = 20; // 0x14 + field public static final int MEDIA_TYPE_FOLDER_MOVIES = 35; // 0x23 + field public static final int MEDIA_TYPE_FOLDER_NEWS = 32; // 0x20 + field public static final int MEDIA_TYPE_FOLDER_PLAYLISTS = 24; // 0x18 + field public static final int MEDIA_TYPE_FOLDER_PODCASTS = 27; // 0x1b + field public static final int MEDIA_TYPE_FOLDER_RADIO_STATIONS = 31; // 0x1f + field public static final int MEDIA_TYPE_FOLDER_TRAILERS = 34; // 0x22 + field public static final int MEDIA_TYPE_FOLDER_TV_CHANNELS = 28; // 0x1c + field public static final int MEDIA_TYPE_FOLDER_TV_SERIES = 29; // 0x1d + field public static final int MEDIA_TYPE_FOLDER_TV_SHOWS = 30; // 0x1e + field public static final int MEDIA_TYPE_FOLDER_VIDEOS = 33; // 0x21 + field public static final int MEDIA_TYPE_FOLDER_YEARS = 25; // 0x19 + field public static final int MEDIA_TYPE_GENRE = 12; // 0xc + field public static final int MEDIA_TYPE_MIXED = 0; // 0x0 + field public static final int MEDIA_TYPE_MOVIE = 8; // 0x8 + field public static final int MEDIA_TYPE_MUSIC = 1; // 0x1 + field public static final int MEDIA_TYPE_NEWS = 5; // 0x5 + field public static final int MEDIA_TYPE_PLAYLIST = 13; // 0xd + field public static final int MEDIA_TYPE_PODCAST = 16; // 0x10 + field public static final int MEDIA_TYPE_PODCAST_EPISODE = 3; // 0x3 + field public static final int MEDIA_TYPE_RADIO_STATION = 4; // 0x4 + field public static final int MEDIA_TYPE_TRAILER = 7; // 0x7 + field public static final int MEDIA_TYPE_TV_CHANNEL = 17; // 0x11 + field public static final int MEDIA_TYPE_TV_SEASON = 19; // 0x13 + field public static final int MEDIA_TYPE_TV_SERIES = 18; // 0x12 + field public static final int MEDIA_TYPE_TV_SHOW = 9; // 0x9 + field public static final int MEDIA_TYPE_VIDEO = 6; // 0x6 + field public static final int MEDIA_TYPE_YEAR = 14; // 0xe field public static final int PICTURE_TYPE_ARTIST_PERFORMER = 8; // 0x8 field public static final int PICTURE_TYPE_A_BRIGHT_COLORED_FISH = 17; // 0x11 field public static final int PICTURE_TYPE_BACK_COVER = 4; // 0x4 @@ -411,9 +464,11 @@ package androidx.media3.common { field @Nullable public final Integer discNumber; field @Nullable public final CharSequence displayTitle; field @Nullable public final android.os.Bundle extras; - field @Nullable @androidx.media3.common.MediaMetadata.FolderType public final Integer folderType; + field @Deprecated @Nullable @androidx.media3.common.MediaMetadata.FolderType public final Integer folderType; field @Nullable public final CharSequence genre; + field @Nullable public final Boolean isBrowsable; field @Nullable public final Boolean isPlayable; + field @Nullable @androidx.media3.common.MediaMetadata.MediaType public final Integer mediaType; field @Nullable public final androidx.media3.common.Rating overallRating; field @Nullable public final Integer recordingDay; field @Nullable public final Integer recordingMonth; @@ -447,9 +502,11 @@ package androidx.media3.common { method public androidx.media3.common.MediaMetadata.Builder setDiscNumber(@Nullable Integer); method public androidx.media3.common.MediaMetadata.Builder setDisplayTitle(@Nullable CharSequence); method public androidx.media3.common.MediaMetadata.Builder setExtras(@Nullable android.os.Bundle); - method public androidx.media3.common.MediaMetadata.Builder setFolderType(@Nullable @androidx.media3.common.MediaMetadata.FolderType Integer); + method @Deprecated public androidx.media3.common.MediaMetadata.Builder setFolderType(@Nullable @androidx.media3.common.MediaMetadata.FolderType Integer); method public androidx.media3.common.MediaMetadata.Builder setGenre(@Nullable CharSequence); + method public androidx.media3.common.MediaMetadata.Builder setIsBrowsable(@Nullable Boolean); method public androidx.media3.common.MediaMetadata.Builder setIsPlayable(@Nullable Boolean); + method public androidx.media3.common.MediaMetadata.Builder setMediaType(@Nullable @androidx.media3.common.MediaMetadata.MediaType Integer); method public androidx.media3.common.MediaMetadata.Builder setOverallRating(@Nullable androidx.media3.common.Rating); method public androidx.media3.common.MediaMetadata.Builder setRecordingDay(@IntRange(from=1, to=31) @Nullable Integer); method public androidx.media3.common.MediaMetadata.Builder setRecordingMonth(@IntRange(from=1, to=12) @Nullable Integer); @@ -467,7 +524,10 @@ package androidx.media3.common { method public androidx.media3.common.MediaMetadata.Builder setWriter(@Nullable CharSequence); } - @IntDef({androidx.media3.common.MediaMetadata.FOLDER_TYPE_NONE, androidx.media3.common.MediaMetadata.FOLDER_TYPE_MIXED, androidx.media3.common.MediaMetadata.FOLDER_TYPE_TITLES, androidx.media3.common.MediaMetadata.FOLDER_TYPE_ALBUMS, androidx.media3.common.MediaMetadata.FOLDER_TYPE_ARTISTS, androidx.media3.common.MediaMetadata.FOLDER_TYPE_GENRES, androidx.media3.common.MediaMetadata.FOLDER_TYPE_PLAYLISTS, androidx.media3.common.MediaMetadata.FOLDER_TYPE_YEARS}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface MediaMetadata.FolderType { + @Deprecated @IntDef({androidx.media3.common.MediaMetadata.FOLDER_TYPE_NONE, androidx.media3.common.MediaMetadata.FOLDER_TYPE_MIXED, androidx.media3.common.MediaMetadata.FOLDER_TYPE_TITLES, androidx.media3.common.MediaMetadata.FOLDER_TYPE_ALBUMS, androidx.media3.common.MediaMetadata.FOLDER_TYPE_ARTISTS, androidx.media3.common.MediaMetadata.FOLDER_TYPE_GENRES, androidx.media3.common.MediaMetadata.FOLDER_TYPE_PLAYLISTS, androidx.media3.common.MediaMetadata.FOLDER_TYPE_YEARS}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface MediaMetadata.FolderType { + } + + @IntDef({androidx.media3.common.MediaMetadata.MEDIA_TYPE_MIXED, androidx.media3.common.MediaMetadata.MEDIA_TYPE_MUSIC, androidx.media3.common.MediaMetadata.MEDIA_TYPE_AUDIO_BOOK_CHAPTER, androidx.media3.common.MediaMetadata.MEDIA_TYPE_PODCAST_EPISODE, androidx.media3.common.MediaMetadata.MEDIA_TYPE_RADIO_STATION, androidx.media3.common.MediaMetadata.MEDIA_TYPE_NEWS, androidx.media3.common.MediaMetadata.MEDIA_TYPE_VIDEO, androidx.media3.common.MediaMetadata.MEDIA_TYPE_TRAILER, androidx.media3.common.MediaMetadata.MEDIA_TYPE_MOVIE, androidx.media3.common.MediaMetadata.MEDIA_TYPE_TV_SHOW, androidx.media3.common.MediaMetadata.MEDIA_TYPE_ALBUM, androidx.media3.common.MediaMetadata.MEDIA_TYPE_ARTIST, androidx.media3.common.MediaMetadata.MEDIA_TYPE_GENRE, androidx.media3.common.MediaMetadata.MEDIA_TYPE_PLAYLIST, androidx.media3.common.MediaMetadata.MEDIA_TYPE_YEAR, androidx.media3.common.MediaMetadata.MEDIA_TYPE_AUDIO_BOOK, androidx.media3.common.MediaMetadata.MEDIA_TYPE_PODCAST, androidx.media3.common.MediaMetadata.MEDIA_TYPE_TV_CHANNEL, androidx.media3.common.MediaMetadata.MEDIA_TYPE_TV_SERIES, androidx.media3.common.MediaMetadata.MEDIA_TYPE_TV_SEASON, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_MIXED, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_ALBUMS, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_ARTISTS, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_GENRES, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_PLAYLISTS, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_YEARS, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_AUDIO_BOOKS, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_PODCASTS, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_TV_CHANNELS, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_TV_SERIES, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_TV_SHOWS, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_RADIO_STATIONS, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_NEWS, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_VIDEOS, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_TRAILERS, androidx.media3.common.MediaMetadata.MEDIA_TYPE_FOLDER_MOVIES}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target(java.lang.annotation.ElementType.TYPE_USE) public static @interface MediaMetadata.MediaType { } @IntDef({androidx.media3.common.MediaMetadata.PICTURE_TYPE_OTHER, androidx.media3.common.MediaMetadata.PICTURE_TYPE_FILE_ICON, androidx.media3.common.MediaMetadata.PICTURE_TYPE_FILE_ICON_OTHER, androidx.media3.common.MediaMetadata.PICTURE_TYPE_FRONT_COVER, androidx.media3.common.MediaMetadata.PICTURE_TYPE_BACK_COVER, androidx.media3.common.MediaMetadata.PICTURE_TYPE_LEAFLET_PAGE, androidx.media3.common.MediaMetadata.PICTURE_TYPE_MEDIA, androidx.media3.common.MediaMetadata.PICTURE_TYPE_LEAD_ARTIST_PERFORMER, androidx.media3.common.MediaMetadata.PICTURE_TYPE_ARTIST_PERFORMER, androidx.media3.common.MediaMetadata.PICTURE_TYPE_CONDUCTOR, androidx.media3.common.MediaMetadata.PICTURE_TYPE_BAND_ORCHESTRA, androidx.media3.common.MediaMetadata.PICTURE_TYPE_COMPOSER, androidx.media3.common.MediaMetadata.PICTURE_TYPE_LYRICIST, androidx.media3.common.MediaMetadata.PICTURE_TYPE_RECORDING_LOCATION, androidx.media3.common.MediaMetadata.PICTURE_TYPE_DURING_RECORDING, androidx.media3.common.MediaMetadata.PICTURE_TYPE_DURING_PERFORMANCE, androidx.media3.common.MediaMetadata.PICTURE_TYPE_MOVIE_VIDEO_SCREEN_CAPTURE, androidx.media3.common.MediaMetadata.PICTURE_TYPE_A_BRIGHT_COLORED_FISH, androidx.media3.common.MediaMetadata.PICTURE_TYPE_ILLUSTRATION, androidx.media3.common.MediaMetadata.PICTURE_TYPE_BAND_ARTIST_LOGO, androidx.media3.common.MediaMetadata.PICTURE_TYPE_PUBLISHER_STUDIO_LOGO}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface MediaMetadata.PictureType { @@ -486,7 +546,7 @@ package androidx.media3.common { field public static final String APPLICATION_MP4VTT = "application/x-mp4-vtt"; field public static final String APPLICATION_MPD = "application/dash+xml"; field public static final String APPLICATION_PGS = "application/pgs"; - field public static final String APPLICATION_RAWCC = "application/x-rawcc"; + field @Deprecated public static final String APPLICATION_RAWCC = "application/x-rawcc"; field public static final String APPLICATION_RTSP = "application/x-rtsp"; field public static final String APPLICATION_SS = "application/vnd.ms-sstr+xml"; field public static final String APPLICATION_SUBRIP = "application/x-subrip"; @@ -557,8 +617,8 @@ package androidx.media3.common { public class PlaybackException extends java.lang.Exception { method @CallSuper public boolean errorInfoEquals(@Nullable androidx.media3.common.PlaybackException); - method public static String getErrorCodeName(@androidx.media3.common.PlaybackException.ErrorCode int); method public final String getErrorCodeName(); + method public static String getErrorCodeName(@androidx.media3.common.PlaybackException.ErrorCode int); field public static final int CUSTOM_ERROR_CODE_BASE = 1000000; // 0xf4240 field public static final int ERROR_CODE_AUDIO_TRACK_INIT_FAILED = 5001; // 0x1389 field public static final int ERROR_CODE_AUDIO_TRACK_WRITE_FAILED = 5002; // 0x138a @@ -602,7 +662,7 @@ package androidx.media3.common { } public final class PlaybackParameters { - ctor public PlaybackParameters(float); + ctor public PlaybackParameters(@FloatRange(from=0, fromInclusive=false) float); ctor public PlaybackParameters(@FloatRange(from=0, fromInclusive=false) float, @FloatRange(from=0, fromInclusive=false) float); method @CheckResult public androidx.media3.common.PlaybackParameters withSpeed(@FloatRange(from=0, fromInclusive=false) float); field public static final androidx.media3.common.PlaybackParameters DEFAULT; @@ -614,8 +674,8 @@ package androidx.media3.common { method public void addListener(androidx.media3.common.Player.Listener); method public void addMediaItem(androidx.media3.common.MediaItem); method public void addMediaItem(int, androidx.media3.common.MediaItem); - method public void addMediaItems(java.util.List); method public void addMediaItems(int, java.util.List); + method public void addMediaItems(java.util.List); method public boolean canAdvertiseSession(); method public void clearMediaItems(); method public void clearVideoSurface(); @@ -623,7 +683,8 @@ package androidx.media3.common { method public void clearVideoSurfaceHolder(@Nullable android.view.SurfaceHolder); method public void clearVideoSurfaceView(@Nullable android.view.SurfaceView); method public void clearVideoTextureView(@Nullable android.view.TextureView); - method public void decreaseDeviceVolume(); + method @Deprecated public void decreaseDeviceVolume(); + method public void decreaseDeviceVolume(@androidx.media3.common.C.VolumeFlags int); method public android.os.Looper getApplicationLooper(); method public androidx.media3.common.AudioAttributes getAudioAttributes(); method public androidx.media3.common.Player.Commands getAvailableCommands(); @@ -667,7 +728,8 @@ package androidx.media3.common { method @FloatRange(from=0, to=1.0) public float getVolume(); method public boolean hasNextMediaItem(); method public boolean hasPreviousMediaItem(); - method public void increaseDeviceVolume(); + method @Deprecated public void increaseDeviceVolume(); + method public void increaseDeviceVolume(@androidx.media3.common.C.VolumeFlags int); method public boolean isCommandAvailable(@androidx.media3.common.Player.Command int); method public boolean isCurrentMediaItemDynamic(); method public boolean isCurrentMediaItemLive(); @@ -685,21 +747,26 @@ package androidx.media3.common { method public void removeListener(androidx.media3.common.Player.Listener); method public void removeMediaItem(int); method public void removeMediaItems(int, int); + method public void replaceMediaItem(int, androidx.media3.common.MediaItem); + method public void replaceMediaItems(int, int, java.util.List); method public void seekBack(); method public void seekForward(); - method public void seekTo(long); method public void seekTo(int, long); + method public void seekTo(long); method public void seekToDefaultPosition(); method public void seekToDefaultPosition(int); method public void seekToNext(); method public void seekToNextMediaItem(); method public void seekToPrevious(); method public void seekToPreviousMediaItem(); - method public void setDeviceMuted(boolean); - method public void setDeviceVolume(@IntRange(from=0) int); + method public void setAudioAttributes(androidx.media3.common.AudioAttributes, boolean); + method @Deprecated public void setDeviceMuted(boolean); + method public void setDeviceMuted(boolean, @androidx.media3.common.C.VolumeFlags int); + method @Deprecated public void setDeviceVolume(@IntRange(from=0) int); + method public void setDeviceVolume(@IntRange(from=0) int, int); method public void setMediaItem(androidx.media3.common.MediaItem); - method public void setMediaItem(androidx.media3.common.MediaItem, long); method public void setMediaItem(androidx.media3.common.MediaItem, boolean); + method public void setMediaItem(androidx.media3.common.MediaItem, long); method public void setMediaItems(java.util.List); method public void setMediaItems(java.util.List, boolean); method public void setMediaItems(java.util.List, int, long); @@ -716,12 +783,14 @@ package androidx.media3.common { method public void setVideoTextureView(@Nullable android.view.TextureView); method public void setVolume(@FloatRange(from=0, to=1.0) float); method public void stop(); - field public static final int COMMAND_ADJUST_DEVICE_VOLUME = 26; // 0x1a + field @Deprecated public static final int COMMAND_ADJUST_DEVICE_VOLUME = 26; // 0x1a + field public static final int COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS = 34; // 0x22 field public static final int COMMAND_CHANGE_MEDIA_ITEMS = 20; // 0x14 field public static final int COMMAND_GET_AUDIO_ATTRIBUTES = 21; // 0x15 field public static final int COMMAND_GET_CURRENT_MEDIA_ITEM = 16; // 0x10 field public static final int COMMAND_GET_DEVICE_VOLUME = 23; // 0x17 - field public static final int COMMAND_GET_MEDIA_ITEMS_METADATA = 18; // 0x12 + field @Deprecated public static final int COMMAND_GET_MEDIA_ITEMS_METADATA = 18; // 0x12 + field public static final int COMMAND_GET_METADATA = 18; // 0x12 field public static final int COMMAND_GET_TEXT = 28; // 0x1c field public static final int COMMAND_GET_TIMELINE = 17; // 0x11 field public static final int COMMAND_GET_TRACKS = 30; // 0x1e @@ -729,6 +798,7 @@ package androidx.media3.common { field public static final int COMMAND_INVALID = -1; // 0xffffffff field public static final int COMMAND_PLAY_PAUSE = 1; // 0x1 field public static final int COMMAND_PREPARE = 2; // 0x2 + field public static final int COMMAND_RELEASE = 32; // 0x20 field public static final int COMMAND_SEEK_BACK = 11; // 0xb field public static final int COMMAND_SEEK_FORWARD = 12; // 0xc field public static final int COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM = 5; // 0x5 @@ -738,9 +808,12 @@ package androidx.media3.common { field public static final int COMMAND_SEEK_TO_NEXT_MEDIA_ITEM = 8; // 0x8 field public static final int COMMAND_SEEK_TO_PREVIOUS = 7; // 0x7 field public static final int COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM = 6; // 0x6 - field public static final int COMMAND_SET_DEVICE_VOLUME = 25; // 0x19 + field public static final int COMMAND_SET_AUDIO_ATTRIBUTES = 35; // 0x23 + field @Deprecated public static final int COMMAND_SET_DEVICE_VOLUME = 25; // 0x19 + field public static final int COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS = 33; // 0x21 field public static final int COMMAND_SET_MEDIA_ITEM = 31; // 0x1f - field public static final int COMMAND_SET_MEDIA_ITEMS_METADATA = 19; // 0x13 + field @Deprecated public static final int COMMAND_SET_MEDIA_ITEMS_METADATA = 19; // 0x13 + field public static final int COMMAND_SET_PLAYLIST_METADATA = 19; // 0x13 field public static final int COMMAND_SET_REPEAT_MODE = 15; // 0xf field public static final int COMMAND_SET_SHUFFLE_MODE = 14; // 0xe field public static final int COMMAND_SET_SPEED_AND_PITCH = 13; // 0xd @@ -791,10 +864,13 @@ package androidx.media3.common { field public static final int MEDIA_ITEM_TRANSITION_REASON_SEEK = 2; // 0x2 field public static final int PLAYBACK_SUPPRESSION_REASON_NONE = 0; // 0x0 field public static final int PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS = 1; // 0x1 + field public static final int PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT = 3; // 0x3 + field @Deprecated public static final int PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_ROUTE = 2; // 0x2 field public static final int PLAY_WHEN_READY_CHANGE_REASON_AUDIO_BECOMING_NOISY = 3; // 0x3 field public static final int PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS = 2; // 0x2 field public static final int PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM = 5; // 0x5 field public static final int PLAY_WHEN_READY_CHANGE_REASON_REMOTE = 4; // 0x4 + field public static final int PLAY_WHEN_READY_CHANGE_REASON_SUPPRESSED_TOO_LONG = 6; // 0x6 field public static final int PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST = 1; // 0x1 field public static final int REPEAT_MODE_ALL = 2; // 0x2 field public static final int REPEAT_MODE_OFF = 0; // 0x0 @@ -807,7 +883,7 @@ package androidx.media3.common { field public static final int TIMELINE_CHANGE_REASON_SOURCE_UPDATE = 1; // 0x1 } - @IntDef({androidx.media3.common.Player.COMMAND_INVALID, androidx.media3.common.Player.COMMAND_PLAY_PAUSE, androidx.media3.common.Player.COMMAND_PREPARE, androidx.media3.common.Player.COMMAND_STOP, androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION, androidx.media3.common.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS, androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT, androidx.media3.common.Player.COMMAND_SEEK_TO_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_BACK, androidx.media3.common.Player.COMMAND_SEEK_FORWARD, androidx.media3.common.Player.COMMAND_SET_SPEED_AND_PITCH, androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE, androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE, androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_GET_TIMELINE, androidx.media3.common.Player.COMMAND_GET_MEDIA_ITEMS_METADATA, androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEMS_METADATA, androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS, androidx.media3.common.Player.COMMAND_GET_AUDIO_ATTRIBUTES, androidx.media3.common.Player.COMMAND_GET_VOLUME, androidx.media3.common.Player.COMMAND_GET_DEVICE_VOLUME, androidx.media3.common.Player.COMMAND_SET_VOLUME, androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME, androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME, androidx.media3.common.Player.COMMAND_SET_VIDEO_SURFACE, androidx.media3.common.Player.COMMAND_GET_TEXT, androidx.media3.common.Player.COMMAND_SET_TRACK_SELECTION_PARAMETERS, androidx.media3.common.Player.COMMAND_GET_TRACKS}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface Player.Command { + @IntDef({androidx.media3.common.Player.COMMAND_INVALID, androidx.media3.common.Player.COMMAND_PLAY_PAUSE, androidx.media3.common.Player.COMMAND_PREPARE, androidx.media3.common.Player.COMMAND_STOP, androidx.media3.common.Player.COMMAND_SEEK_TO_DEFAULT_POSITION, androidx.media3.common.Player.COMMAND_SEEK_IN_CURRENT_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_TO_PREVIOUS, androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_TO_NEXT, androidx.media3.common.Player.COMMAND_SEEK_TO_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_SEEK_BACK, androidx.media3.common.Player.COMMAND_SEEK_FORWARD, androidx.media3.common.Player.COMMAND_SET_SPEED_AND_PITCH, androidx.media3.common.Player.COMMAND_SET_SHUFFLE_MODE, androidx.media3.common.Player.COMMAND_SET_REPEAT_MODE, androidx.media3.common.Player.COMMAND_GET_CURRENT_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_GET_TIMELINE, androidx.media3.common.Player.COMMAND_GET_MEDIA_ITEMS_METADATA, androidx.media3.common.Player.COMMAND_GET_METADATA, androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEMS_METADATA, androidx.media3.common.Player.COMMAND_SET_PLAYLIST_METADATA, androidx.media3.common.Player.COMMAND_SET_MEDIA_ITEM, androidx.media3.common.Player.COMMAND_CHANGE_MEDIA_ITEMS, androidx.media3.common.Player.COMMAND_GET_AUDIO_ATTRIBUTES, androidx.media3.common.Player.COMMAND_GET_VOLUME, androidx.media3.common.Player.COMMAND_GET_DEVICE_VOLUME, androidx.media3.common.Player.COMMAND_SET_VOLUME, androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME, androidx.media3.common.Player.COMMAND_SET_DEVICE_VOLUME_WITH_FLAGS, androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME, androidx.media3.common.Player.COMMAND_ADJUST_DEVICE_VOLUME_WITH_FLAGS, androidx.media3.common.Player.COMMAND_SET_AUDIO_ATTRIBUTES, androidx.media3.common.Player.COMMAND_SET_VIDEO_SURFACE, androidx.media3.common.Player.COMMAND_GET_TEXT, androidx.media3.common.Player.COMMAND_SET_TRACK_SELECTION_PARAMETERS, androidx.media3.common.Player.COMMAND_GET_TRACKS, androidx.media3.common.Player.COMMAND_RELEASE}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface Player.Command { } public static final class Player.Commands { @@ -868,10 +944,10 @@ package androidx.media3.common { @IntDef({androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_REPEAT, androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_AUTO, androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_SEEK, androidx.media3.common.Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface Player.MediaItemTransitionReason { } - @IntDef({androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS, androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_BECOMING_NOISY, androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE, androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface Player.PlayWhenReadyChangeReason { + @IntDef({androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_USER_REQUEST, androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_FOCUS_LOSS, androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_AUDIO_BECOMING_NOISY, androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_REMOTE, androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_END_OF_MEDIA_ITEM, androidx.media3.common.Player.PLAY_WHEN_READY_CHANGE_REASON_SUPPRESSED_TOO_LONG}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface Player.PlayWhenReadyChangeReason { } - @IntDef({androidx.media3.common.Player.PLAYBACK_SUPPRESSION_REASON_NONE, androidx.media3.common.Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface Player.PlaybackSuppressionReason { + @IntDef({androidx.media3.common.Player.PLAYBACK_SUPPRESSION_REASON_NONE, androidx.media3.common.Player.PLAYBACK_SUPPRESSION_REASON_TRANSIENT_AUDIO_FOCUS_LOSS, androidx.media3.common.Player.PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_ROUTE, androidx.media3.common.Player.PLAYBACK_SUPPRESSION_REASON_UNSUITABLE_AUDIO_OUTPUT}) @java.lang.annotation.Documented @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.SOURCE) @java.lang.annotation.Target({java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.LOCAL_VARIABLE, java.lang.annotation.ElementType.TYPE_USE}) public static @interface Player.PlaybackSuppressionReason { } public static final class Player.PositionInfo { @@ -1110,7 +1186,7 @@ package androidx.media3.common.text { field public static final int ANCHOR_TYPE_MIDDLE = 1; // 0x1 field public static final int ANCHOR_TYPE_START = 0; // 0x0 field public static final float DIMEN_UNSET = -3.4028235E38f; - field public static final androidx.media3.common.text.Cue EMPTY; + field @Deprecated public static final androidx.media3.common.text.Cue EMPTY; field public static final int LINE_TYPE_FRACTION = 0; // 0x0 field public static final int LINE_TYPE_NUMBER = 1; // 0x1 field public static final int TEXT_SIZE_TYPE_ABSOLUTE = 2; // 0x2 @@ -1162,11 +1238,16 @@ package androidx.media3.common.util { method public static boolean checkCleartextTrafficPermitted(androidx.media3.common.MediaItem...); method @Nullable public static String getAdaptiveMimeTypeForContentType(@androidx.media3.common.C.ContentType int); method @Nullable public static java.util.UUID getDrmUuid(String); + method public static boolean handlePauseButtonAction(@Nullable androidx.media3.common.Player); + method public static boolean handlePlayButtonAction(@Nullable androidx.media3.common.Player); + method public static boolean handlePlayPauseButtonAction(@Nullable androidx.media3.common.Player); method @androidx.media3.common.C.ContentType public static int inferContentType(android.net.Uri); method @androidx.media3.common.C.ContentType public static int inferContentTypeForExtension(String); method @androidx.media3.common.C.ContentType public static int inferContentTypeForUriAndMimeType(android.net.Uri, @Nullable String); - method public static boolean maybeRequestReadExternalStoragePermission(android.app.Activity, android.net.Uri...); - method public static boolean maybeRequestReadExternalStoragePermission(android.app.Activity, androidx.media3.common.MediaItem...); + method @Deprecated public static boolean maybeRequestReadExternalStoragePermission(android.app.Activity, android.net.Uri...); + method @Deprecated public static boolean maybeRequestReadExternalStoragePermission(android.app.Activity, androidx.media3.common.MediaItem...); + method public static boolean maybeRequestReadStoragePermission(android.app.Activity, androidx.media3.common.MediaItem...); + method @org.checkerframework.checker.nullness.qual.EnsuresNonNullIf(result=false, expression="#1") public static boolean shouldShowPlayButton(@Nullable androidx.media3.common.Player); } } @@ -1264,7 +1345,6 @@ package androidx.media3.exoplayer { method public void addAnalyticsListener(androidx.media3.exoplayer.analytics.AnalyticsListener); method @Nullable public androidx.media3.exoplayer.ExoPlaybackException getPlayerError(); method public void removeAnalyticsListener(androidx.media3.exoplayer.analytics.AnalyticsListener); - method public void setAudioAttributes(androidx.media3.common.AudioAttributes, boolean); method public void setHandleAudioBecomingNoisy(boolean); method public void setWakeMode(@androidx.media3.common.C.WakeMode int); } @@ -1438,122 +1518,129 @@ package androidx.media3.session { field public static final String EXTRAS_KEY_SLOT_RESERVATION_SEEK_TO_PREV = "android.media.playback.ALWAYS_RESERVE_SPACE_FOR.ACTION_SKIP_TO_PREVIOUS"; } - public class MediaController implements androidx.media3.common.Player { - method public void addListener(androidx.media3.common.Player.Listener); - method public void addMediaItem(androidx.media3.common.MediaItem); - method public void addMediaItem(int, androidx.media3.common.MediaItem); - method public void addMediaItems(java.util.List); - method public void addMediaItems(int, java.util.List); - method public boolean canAdvertiseSession(); - method public void clearMediaItems(); - method public void clearVideoSurface(); - method public void clearVideoSurface(@Nullable android.view.Surface); - method public void clearVideoSurfaceHolder(@Nullable android.view.SurfaceHolder); - method public void clearVideoSurfaceView(@Nullable android.view.SurfaceView); - method public void clearVideoTextureView(@Nullable android.view.TextureView); - method public void decreaseDeviceVolume(); - method public android.os.Looper getApplicationLooper(); - method public androidx.media3.common.AudioAttributes getAudioAttributes(); - method public androidx.media3.common.Player.Commands getAvailableCommands(); - method public androidx.media3.session.SessionCommands getAvailableSessionCommands(); - method @IntRange(from=0, to=100) public int getBufferedPercentage(); - method public long getBufferedPosition(); - method @Nullable public androidx.media3.session.SessionToken getConnectedToken(); - method public long getContentBufferedPosition(); - method public long getContentDuration(); - method public long getContentPosition(); - method public int getCurrentAdGroupIndex(); - method public int getCurrentAdIndexInAdGroup(); - method public androidx.media3.common.text.CueGroup getCurrentCues(); - method public long getCurrentLiveOffset(); - method @Nullable public androidx.media3.common.MediaItem getCurrentMediaItem(); - method public int getCurrentMediaItemIndex(); - method public int getCurrentPeriodIndex(); - method public long getCurrentPosition(); - method public androidx.media3.common.Timeline getCurrentTimeline(); - method public androidx.media3.common.Tracks getCurrentTracks(); - method public androidx.media3.common.DeviceInfo getDeviceInfo(); - method @IntRange(from=0) public int getDeviceVolume(); - method public long getDuration(); - method public long getMaxSeekToPreviousPosition(); - method public androidx.media3.common.MediaItem getMediaItemAt(int); - method public int getMediaItemCount(); - method public androidx.media3.common.MediaMetadata getMediaMetadata(); - method public int getNextMediaItemIndex(); - method public boolean getPlayWhenReady(); - method public androidx.media3.common.PlaybackParameters getPlaybackParameters(); - method @androidx.media3.common.Player.State public int getPlaybackState(); - method @androidx.media3.common.Player.PlaybackSuppressionReason public int getPlaybackSuppressionReason(); - method @Nullable public androidx.media3.common.PlaybackException getPlayerError(); - method public androidx.media3.common.MediaMetadata getPlaylistMetadata(); - method public int getPreviousMediaItemIndex(); - method @androidx.media3.common.Player.RepeatMode public int getRepeatMode(); - method public long getSeekBackIncrement(); - method public long getSeekForwardIncrement(); - method @Nullable public android.app.PendingIntent getSessionActivity(); - method public boolean getShuffleModeEnabled(); - method public long getTotalBufferedDuration(); - method public androidx.media3.common.TrackSelectionParameters getTrackSelectionParameters(); - method public androidx.media3.common.VideoSize getVideoSize(); - method @FloatRange(from=0, to=1) public float getVolume(); - method public boolean hasNextMediaItem(); - method public boolean hasPreviousMediaItem(); - method public void increaseDeviceVolume(); - method public boolean isCommandAvailable(@androidx.media3.common.Player.Command int); - method public boolean isConnected(); - method public boolean isCurrentMediaItemDynamic(); - method public boolean isCurrentMediaItemLive(); - method public boolean isCurrentMediaItemSeekable(); - method public boolean isDeviceMuted(); - method public boolean isLoading(); - method public boolean isPlaying(); - method public boolean isPlayingAd(); - method public boolean isSessionCommandAvailable(@androidx.media3.session.SessionCommand.CommandCode int); - method public boolean isSessionCommandAvailable(androidx.media3.session.SessionCommand); - method public void moveMediaItem(int, int); - method public void moveMediaItems(int, int, int); - method public void pause(); - method public void play(); - method public void prepare(); - method public void release(); + @com.google.errorprone.annotations.DoNotMock public class MediaController implements androidx.media3.common.Player { + method public final void addListener(androidx.media3.common.Player.Listener); + method public final void addMediaItem(androidx.media3.common.MediaItem); + method public final void addMediaItem(int, androidx.media3.common.MediaItem); + method public final void addMediaItems(int, java.util.List); + method public final void addMediaItems(java.util.List); + method public final boolean canAdvertiseSession(); + method public final void clearMediaItems(); + method public final void clearVideoSurface(); + method public final void clearVideoSurface(@Nullable android.view.Surface); + method public final void clearVideoSurfaceHolder(@Nullable android.view.SurfaceHolder); + method public final void clearVideoSurfaceView(@Nullable android.view.SurfaceView); + method public final void clearVideoTextureView(@Nullable android.view.TextureView); + method @Deprecated public final void decreaseDeviceVolume(); + method public final void decreaseDeviceVolume(@androidx.media3.common.C.VolumeFlags int); + method public final android.os.Looper getApplicationLooper(); + method public final androidx.media3.common.AudioAttributes getAudioAttributes(); + method public final androidx.media3.common.Player.Commands getAvailableCommands(); + method public final androidx.media3.session.SessionCommands getAvailableSessionCommands(); + method @IntRange(from=0, to=100) public final int getBufferedPercentage(); + method public final long getBufferedPosition(); + method @Nullable public final androidx.media3.session.SessionToken getConnectedToken(); + method public final long getContentBufferedPosition(); + method public final long getContentDuration(); + method public final long getContentPosition(); + method public final int getCurrentAdGroupIndex(); + method public final int getCurrentAdIndexInAdGroup(); + method public final androidx.media3.common.text.CueGroup getCurrentCues(); + method public final long getCurrentLiveOffset(); + method @Nullable public final androidx.media3.common.MediaItem getCurrentMediaItem(); + method public final int getCurrentMediaItemIndex(); + method public final int getCurrentPeriodIndex(); + method public final long getCurrentPosition(); + method public final androidx.media3.common.Timeline getCurrentTimeline(); + method public final androidx.media3.common.Tracks getCurrentTracks(); + method public final androidx.media3.common.DeviceInfo getDeviceInfo(); + method @IntRange(from=0) public final int getDeviceVolume(); + method public final long getDuration(); + method public final long getMaxSeekToPreviousPosition(); + method public final androidx.media3.common.MediaItem getMediaItemAt(int); + method public final int getMediaItemCount(); + method public final androidx.media3.common.MediaMetadata getMediaMetadata(); + method public final int getNextMediaItemIndex(); + method public final boolean getPlayWhenReady(); + method public final androidx.media3.common.PlaybackParameters getPlaybackParameters(); + method @androidx.media3.common.Player.State public final int getPlaybackState(); + method @androidx.media3.common.Player.PlaybackSuppressionReason public final int getPlaybackSuppressionReason(); + method @Nullable public final androidx.media3.common.PlaybackException getPlayerError(); + method public final androidx.media3.common.MediaMetadata getPlaylistMetadata(); + method public final int getPreviousMediaItemIndex(); + method @androidx.media3.common.Player.RepeatMode public final int getRepeatMode(); + method public final long getSeekBackIncrement(); + method public final long getSeekForwardIncrement(); + method @Nullable public final android.app.PendingIntent getSessionActivity(); + method public final boolean getShuffleModeEnabled(); + method public final long getTotalBufferedDuration(); + method public final androidx.media3.common.TrackSelectionParameters getTrackSelectionParameters(); + method public final androidx.media3.common.VideoSize getVideoSize(); + method @FloatRange(from=0, to=1) public final float getVolume(); + method public final boolean hasNextMediaItem(); + method public final boolean hasPreviousMediaItem(); + method @Deprecated public final void increaseDeviceVolume(); + method public final void increaseDeviceVolume(@androidx.media3.common.C.VolumeFlags int); + method public final boolean isCommandAvailable(@androidx.media3.common.Player.Command int); + method public final boolean isConnected(); + method public final boolean isCurrentMediaItemDynamic(); + method public final boolean isCurrentMediaItemLive(); + method public final boolean isCurrentMediaItemSeekable(); + method public final boolean isDeviceMuted(); + method public final boolean isLoading(); + method public final boolean isPlaying(); + method public final boolean isPlayingAd(); + method public final boolean isSessionCommandAvailable(androidx.media3.session.SessionCommand); + method public final boolean isSessionCommandAvailable(@androidx.media3.session.SessionCommand.CommandCode int); + method public final void moveMediaItem(int, int); + method public final void moveMediaItems(int, int, int); + method public final void pause(); + method public final void play(); + method public final void prepare(); + method public final void release(); method public static void releaseFuture(java.util.concurrent.Future); - method public void removeListener(androidx.media3.common.Player.Listener); - method public void removeMediaItem(int); - method public void removeMediaItems(int, int); - method public void seekBack(); - method public void seekForward(); - method public void seekTo(long); - method public void seekTo(int, long); - method public void seekToDefaultPosition(); - method public void seekToDefaultPosition(int); - method public void seekToNext(); - method public void seekToNextMediaItem(); - method public void seekToPrevious(); - method public void seekToPreviousMediaItem(); - method public com.google.common.util.concurrent.ListenableFuture sendCustomCommand(androidx.media3.session.SessionCommand, android.os.Bundle); - method public void setDeviceMuted(boolean); - method public void setDeviceVolume(@IntRange(from=0) int); - method public void setMediaItem(androidx.media3.common.MediaItem); - method public void setMediaItem(androidx.media3.common.MediaItem, long); - method public void setMediaItem(androidx.media3.common.MediaItem, boolean); - method public void setMediaItems(java.util.List); - method public void setMediaItems(java.util.List, boolean); - method public void setMediaItems(java.util.List, int, long); - method public void setPlayWhenReady(boolean); - method public void setPlaybackParameters(androidx.media3.common.PlaybackParameters); - method public void setPlaybackSpeed(float); - method public void setPlaylistMetadata(androidx.media3.common.MediaMetadata); - method public com.google.common.util.concurrent.ListenableFuture setRating(String, androidx.media3.common.Rating); - method public com.google.common.util.concurrent.ListenableFuture setRating(androidx.media3.common.Rating); - method public void setRepeatMode(@androidx.media3.common.Player.RepeatMode int); - method public void setShuffleModeEnabled(boolean); - method public void setTrackSelectionParameters(androidx.media3.common.TrackSelectionParameters); - method public void setVideoSurface(@Nullable android.view.Surface); - method public void setVideoSurfaceHolder(@Nullable android.view.SurfaceHolder); - method public void setVideoSurfaceView(@Nullable android.view.SurfaceView); - method public void setVideoTextureView(@Nullable android.view.TextureView); - method public void setVolume(@FloatRange(from=0, to=1) float); - method public void stop(); + method public final void removeListener(androidx.media3.common.Player.Listener); + method public final void removeMediaItem(int); + method public final void removeMediaItems(int, int); + method public final void replaceMediaItem(int, androidx.media3.common.MediaItem); + method public final void replaceMediaItems(int, int, java.util.List); + method public final void seekBack(); + method public final void seekForward(); + method public final void seekTo(int, long); + method public final void seekTo(long); + method public final void seekToDefaultPosition(); + method public final void seekToDefaultPosition(int); + method public final void seekToNext(); + method public final void seekToNextMediaItem(); + method public final void seekToPrevious(); + method public final void seekToPreviousMediaItem(); + method public final com.google.common.util.concurrent.ListenableFuture sendCustomCommand(androidx.media3.session.SessionCommand, android.os.Bundle); + method public final void setAudioAttributes(androidx.media3.common.AudioAttributes, boolean); + method @Deprecated public final void setDeviceMuted(boolean); + method public final void setDeviceMuted(boolean, @androidx.media3.common.C.VolumeFlags int); + method @Deprecated public final void setDeviceVolume(@IntRange(from=0) int); + method public final void setDeviceVolume(@IntRange(from=0) int, @androidx.media3.common.C.VolumeFlags int); + method public final void setMediaItem(androidx.media3.common.MediaItem); + method public final void setMediaItem(androidx.media3.common.MediaItem, boolean); + method public final void setMediaItem(androidx.media3.common.MediaItem, long); + method public final void setMediaItems(java.util.List); + method public final void setMediaItems(java.util.List, boolean); + method public final void setMediaItems(java.util.List, int, long); + method public final void setPlayWhenReady(boolean); + method public final void setPlaybackParameters(androidx.media3.common.PlaybackParameters); + method public final void setPlaybackSpeed(float); + method public final void setPlaylistMetadata(androidx.media3.common.MediaMetadata); + method public final com.google.common.util.concurrent.ListenableFuture setRating(androidx.media3.common.Rating); + method public final com.google.common.util.concurrent.ListenableFuture setRating(String, androidx.media3.common.Rating); + method public final void setRepeatMode(@androidx.media3.common.Player.RepeatMode int); + method public final void setShuffleModeEnabled(boolean); + method public final void setTrackSelectionParameters(androidx.media3.common.TrackSelectionParameters); + method public final void setVideoSurface(@Nullable android.view.Surface); + method public final void setVideoSurfaceHolder(@Nullable android.view.SurfaceHolder); + method public final void setVideoSurfaceView(@Nullable android.view.SurfaceView); + method public final void setVideoTextureView(@Nullable android.view.TextureView); + method public final void setVolume(@FloatRange(from=0, to=1) float); + method public final void stop(); } public static final class MediaController.Builder { @@ -1622,21 +1709,22 @@ package androidx.media3.session { field @IntRange(from=1) public final int notificationId; } - public class MediaSession { - method public void broadcastCustomCommand(androidx.media3.session.SessionCommand, android.os.Bundle); - method public java.util.List getConnectedControllers(); - method public String getId(); - method public androidx.media3.common.Player getPlayer(); - method @Nullable public android.app.PendingIntent getSessionActivity(); - method public androidx.media3.session.SessionToken getToken(); - method public void release(); - method public com.google.common.util.concurrent.ListenableFuture sendCustomCommand(androidx.media3.session.MediaSession.ControllerInfo, androidx.media3.session.SessionCommand, android.os.Bundle); - method public void setAvailableCommands(androidx.media3.session.MediaSession.ControllerInfo, androidx.media3.session.SessionCommands, androidx.media3.common.Player.Commands); - method public com.google.common.util.concurrent.ListenableFuture setCustomLayout(androidx.media3.session.MediaSession.ControllerInfo, java.util.List); - method public void setCustomLayout(java.util.List); - method public void setPlayer(androidx.media3.common.Player); - method public void setSessionExtras(android.os.Bundle); - method public void setSessionExtras(androidx.media3.session.MediaSession.ControllerInfo, android.os.Bundle); + @com.google.errorprone.annotations.DoNotMock public class MediaSession { + method public final void broadcastCustomCommand(androidx.media3.session.SessionCommand, android.os.Bundle); + method public final java.util.List getConnectedControllers(); + method @Nullable public final androidx.media3.session.MediaSession.ControllerInfo getControllerForCurrentRequest(); + method public final String getId(); + method public final androidx.media3.common.Player getPlayer(); + method @Nullable public final android.app.PendingIntent getSessionActivity(); + method public final androidx.media3.session.SessionToken getToken(); + method public final void release(); + method public final com.google.common.util.concurrent.ListenableFuture sendCustomCommand(androidx.media3.session.MediaSession.ControllerInfo, androidx.media3.session.SessionCommand, android.os.Bundle); + method public final void setAvailableCommands(androidx.media3.session.MediaSession.ControllerInfo, androidx.media3.session.SessionCommands, androidx.media3.common.Player.Commands); + method public final com.google.common.util.concurrent.ListenableFuture setCustomLayout(androidx.media3.session.MediaSession.ControllerInfo, java.util.List); + method public final void setCustomLayout(java.util.List); + method public final void setPlayer(androidx.media3.common.Player); + method public final void setSessionExtras(android.os.Bundle); + method public final void setSessionExtras(androidx.media3.session.MediaSession.ControllerInfo, android.os.Bundle); } public static final class MediaSession.Builder { @@ -1653,10 +1741,10 @@ package androidx.media3.session { method public default androidx.media3.session.MediaSession.ConnectionResult onConnect(androidx.media3.session.MediaSession, androidx.media3.session.MediaSession.ControllerInfo); method public default com.google.common.util.concurrent.ListenableFuture onCustomCommand(androidx.media3.session.MediaSession, androidx.media3.session.MediaSession.ControllerInfo, androidx.media3.session.SessionCommand, android.os.Bundle); method public default void onDisconnected(androidx.media3.session.MediaSession, androidx.media3.session.MediaSession.ControllerInfo); - method @androidx.media3.session.SessionResult.Code public default int onPlayerCommandRequest(androidx.media3.session.MediaSession, androidx.media3.session.MediaSession.ControllerInfo, @androidx.media3.common.Player.Command int); + method @Deprecated @androidx.media3.session.SessionResult.Code public default int onPlayerCommandRequest(androidx.media3.session.MediaSession, androidx.media3.session.MediaSession.ControllerInfo, @androidx.media3.common.Player.Command int); method public default void onPostConnect(androidx.media3.session.MediaSession, androidx.media3.session.MediaSession.ControllerInfo); - method public default com.google.common.util.concurrent.ListenableFuture onSetRating(androidx.media3.session.MediaSession, androidx.media3.session.MediaSession.ControllerInfo, String, androidx.media3.common.Rating); method public default com.google.common.util.concurrent.ListenableFuture onSetRating(androidx.media3.session.MediaSession, androidx.media3.session.MediaSession.ControllerInfo, androidx.media3.common.Rating); + method public default com.google.common.util.concurrent.ListenableFuture onSetRating(androidx.media3.session.MediaSession, androidx.media3.session.MediaSession.ControllerInfo, String, androidx.media3.common.Rating); } public static final class MediaSession.ConnectionResult { @@ -1682,7 +1770,8 @@ package androidx.media3.session { method public final boolean isSessionAdded(androidx.media3.session.MediaSession); method @CallSuper @Nullable public android.os.IBinder onBind(@Nullable android.content.Intent); method @Nullable public abstract androidx.media3.session.MediaSession onGetSession(androidx.media3.session.MediaSession.ControllerInfo); - method public void onUpdateNotification(androidx.media3.session.MediaSession); + method @Deprecated public void onUpdateNotification(androidx.media3.session.MediaSession); + method public void onUpdateNotification(androidx.media3.session.MediaSession, boolean); method public final void removeSession(androidx.media3.session.MediaSession); field public static final String SERVICE_INTERFACE = "androidx.media3.session.MediaSessionService"; } diff --git a/build.gradle b/build.gradle index 0c15bce9e5..3d792da33f 100644 --- a/build.gradle +++ b/build.gradle @@ -17,15 +17,21 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.2.1' - classpath 'com.google.android.gms:strict-version-matcher-plugin:1.2.2' - classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.21' + classpath 'com.android.tools.build:gradle:8.0.1' + classpath 'com.google.android.gms:strict-version-matcher-plugin:1.2.4' + classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20' } } allprojects { repositories { google() mavenCentral() + maven { + url 'https://jitpack.io' + content { + includeGroup "com.github.philburk" + } + } } if (it.hasProperty('externalBuildDir')) { if (!new File(externalBuildDir).isAbsolute()) { @@ -35,5 +41,3 @@ allprojects { } group = 'androidx.media3' } - -apply from: 'javadoc_combined.gradle' diff --git a/common_library_config.gradle b/common_library_config.gradle index 79b388554c..6799dc1dfc 100644 --- a/common_library_config.gradle +++ b/common_library_config.gradle @@ -24,9 +24,11 @@ android { targetSdkVersion project.ext.targetSdkVersion consumerProguardFiles 'proguard-rules.txt' testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' + multiDexEnabled true } compileOptions { + coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } @@ -38,3 +40,8 @@ android { unitTests.includeAndroidResources true } } + +dependencies { + androidTestImplementation 'androidx.multidex:multidex:' + androidxMultidexVersion + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' +} diff --git a/constants.gradle b/constants.gradle index e159877c50..c4831f6854 100644 --- a/constants.gradle +++ b/constants.gradle @@ -12,50 +12,52 @@ // See the License for the specific language governing permissions and // limitations under the License. project.ext { - releaseVersion = '1.0.2' - releaseVersionCode = 1_000_002_3_00 + releaseVersion = '1.2.1' + releaseVersionCode = 1_002_001_3_00 minSdkVersion = 16 - appTargetSdkVersion = 33 - // API version before restricting local file access. - // https://developer.android.com/training/data-storage/app-specific - mainDemoAppTargetSdkVersion = 29 + // See https://developer.android.com/training/cars/media/automotive-os#automotive-module + automotiveMinSdkVersion = 28 + appTargetSdkVersion = 34 // Upgrading this requires [Internal ref: b/193254928] to be fixed, or some // additional robolectric config. targetSdkVersion = 30 - compileSdkVersion = 33 + compileSdkVersion = 34 dexmakerVersion = '2.28.3' junitVersion = '4.13.2' // Use the same Guava version as the Android repo: - // https://cs.android.com/android/platform/superproject/+/master:external/guava/METADATA - guavaVersion = '31.0.1-android' + // https://cs.android.com/android/platform/superproject/main/+/main:external/guava/METADATA + guavaVersion = '31.1-android' mockitoVersion = '3.12.4' - robolectricVersion = '4.8.1' + robolectricVersion = '4.10.3' // Keep this in sync with Google's internal Checker Framework version. checkerframeworkVersion = '3.13.0' - checkerframeworkCompatVersion = '2.5.5' - errorProneVersion = '2.10.0' + errorProneVersion = '2.18.0' jsr305Version = '3.0.2' - kotlinAnnotationsVersion = '1.5.31' + kotlinAnnotationsVersion = '1.8.20' + // Updating this to 1.4.0+ will import Kotlin stdlib [internal ref: b/277891049]. androidxAnnotationVersion = '1.3.0' - androidxAnnotationExperimentalVersion = '1.2.0' - androidxAppCompatVersion = '1.3.1' - androidxCollectionVersion = '1.1.0' - androidxConstraintLayoutVersion = '2.0.4' - androidxCoreVersion = '1.7.0' + androidxAnnotationExperimentalVersion = '1.3.1' + androidxAppCompatVersion = '1.6.1' + androidxCollectionVersion = '1.2.0' + androidxConstraintLayoutVersion = '2.1.4' + // Updating this to 1.9.0+ will import Kotlin stdlib [internal ref: b/277891049]. + androidxCoreVersion = '1.8.0' + androidxExifInterfaceVersion = '1.3.6' androidxFuturesVersion = '1.1.0' androidxMediaVersion = '1.6.0' - androidxMedia2Version = '1.2.0' + androidxMedia2Version = '1.2.1' androidxMultidexVersion = '2.0.1' - androidxRecyclerViewVersion = '1.2.1' - androidxMaterialVersion = '1.4.0' - androidxTestCoreVersion = '1.4.0' - androidxTestJUnitVersion = '1.1.3' - androidxTestRunnerVersion = '1.4.0' - androidxTestRulesVersion = '1.4.0' - androidxTestServicesStorageVersion = '1.4.0' - androidxTestTruthVersion = '1.4.0' + androidxRecyclerViewVersion = '1.3.0' + androidxMaterialVersion = '1.8.0' + androidxTestCoreVersion = '1.5.0' + androidxTestEspressoVersion = '3.5.1' + androidxTestJUnitVersion = '1.1.5' + androidxTestRunnerVersion = '1.5.2' + androidxTestRulesVersion = '1.5.0' + androidxTestServicesStorageVersion = '1.4.2' + androidxTestTruthVersion = '1.5.0' truthVersion = '1.1.3' - okhttpVersion = '4.9.2' + okhttpVersion = '4.11.0' modulePrefix = ':' if (gradle.ext.has('androidxMediaModulePrefix')) { modulePrefix += gradle.ext.androidxMediaModulePrefix diff --git a/core_settings.gradle b/core_settings.gradle index 0ef4e443c4..210e6019cc 100644 --- a/core_settings.gradle +++ b/core_settings.gradle @@ -21,11 +21,12 @@ if (gradle.ext.has('androidxMediaModulePrefix')) { modulePrefix += gradle.ext.androidxMediaModulePrefix } -rootProject.name = gradle.ext.androidxMediaProjectName - include modulePrefix + 'lib-common' project(modulePrefix + 'lib-common').projectDir = new File(rootDir, 'libraries/common') +include modulePrefix + 'lib-container' +project(modulePrefix + 'lib-container').projectDir = new File(rootDir, 'libraries/container') + include modulePrefix + 'lib-session' project(modulePrefix + 'lib-session').projectDir = new File(rootDir, 'libraries/session') @@ -69,6 +70,8 @@ include modulePrefix + 'lib-decoder-ffmpeg' project(modulePrefix + 'lib-decoder-ffmpeg').projectDir = new File(rootDir, 'libraries/decoder_ffmpeg') include modulePrefix + 'lib-decoder-flac' project(modulePrefix + 'lib-decoder-flac').projectDir = new File(rootDir, 'libraries/decoder_flac') +include modulePrefix + 'lib-decoder-midi' +project(modulePrefix + 'lib-decoder-midi').projectDir = new File(rootDir, 'libraries/decoder_midi') include modulePrefix + 'lib-decoder-opus' project(modulePrefix + 'lib-decoder-opus').projectDir = new File(rootDir, 'libraries/decoder_opus') include modulePrefix + 'lib-decoder-vp9' @@ -83,6 +86,9 @@ project(modulePrefix + 'lib-cast').projectDir = new File(rootDir, 'libraries/cas include modulePrefix + 'lib-effect' project(modulePrefix + 'lib-effect').projectDir = new File(rootDir, 'libraries/effect') +include modulePrefix + 'lib-muxer' +project(modulePrefix + 'lib-muxer').projectDir = new File(rootDir, 'libraries/muxer') + include modulePrefix + 'lib-transformer' project(modulePrefix + 'lib-transformer').projectDir = new File(rootDir, 'libraries/transformer') diff --git a/demos/cast/README.md b/demos/cast/README.md index b636d2c2e0..aae757dd62 100644 --- a/demos/cast/README.md +++ b/demos/cast/README.md @@ -1,7 +1,116 @@ # Cast demo -This app demonstrates integration with Google Cast, as well as switching between -Google Cast and local playback using ExoPlayer. +This app demonstrates switching between Google Cast and local playback by using +`CastPlayer` and `ExoPlayer`. + +## Building the demo app See the [demos README](../README.md) for instructions on how to build and run this demo. + +Test your streams by adding a `MediaItem` with URI and mime type to the +`DemoUtil` and deploy the app on a real device for casting. + +## Customization with `OptionsProvider` + +The Cast SDK behaviour in the demo app or your own app can be customized by +providing a custom `OptionsProvider` (see +[`DefaultCastOptionsProvider`](https://github.com/androidx/media/blob/release/libraries/cast/src/main/java/androidx/media3/cast/DefaultCastOptionsProvider.java) +also). + +Replace the default options provider in the `AndroidManifest.xml` with your own: + +```xml + +``` + +### Using a different Cast receiver app with the Media3 cast demo sender app + +The Media3 cast demo app is an implementation of an +[Android Cast *sender app*](https://developers.google.com/cast/docs/android_sender) +that uses a *default Cast receiver app* (running on the Cast device) that is +customized to support DRM protected streams +[by passing DRM configuration via `MediaInfo`](https://developers.google.com/cast/docs/android_sender/exoplayer). +Hence Widevine DRM credentials can also be populated with a +`MediaItem.DrmConfiguration.Builder` (see the samples in `DemoUtil` marked with +`Widevine`). + +If you test your own streams with this demo app, keep in mind that for your +production app you need to +[choose your own receiver app](https://developers.google.com/cast/docs/web_receiver#choose_a_web_receiver) +and have your own receiver app ID. + +If you have a receiver app already and want to quickly test whether it works +well together with the `CastPlayer`, then you can configure the demo app to use +your receiver: + +```java +public class MyOptionsProvider implements OptionsProvider { + @NonNull + @Override + public CastOptions getCastOptions(Context context) { + return new CastOptions.Builder() + .setReceiverApplicationId(YOUR_RECEIVER_APP_ID) + // other options + .build(); + } +} +``` + +You can also use the plain +[default Cast receiver app](https://developers.google.com/cast/docs/web_receiver#default_media_web_receiver) +by using `CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID`. + +#### Converting a Media3 `MediaItem` to a Cast `MediaQueueItem` + +This demo app uses the +[`DefaultMediaItemConverter`](https://github.com/androidx/media/blob/release/libraries/cast/src/main/java/androidx/media3/cast/DefaultMediaItemConverter.java) +to convert a Media3 `MediaItem` to a `MediaQueueItem` of the Cast API. Apps that +use a custom receiver app, can use a custom `MediaItemConverter` instance by +passing it into the constructor of `CastPlayer`. + +### Media session and notification + +This Media3 cast demo app uses the media session and notification support +provided by the Cast SDK. If your app already integrates with a `MediaSession`, +the Cast session can be disabled to avoid duplicate notifications or sessions: + +```java +public class MyOptionsProvider implements OptionsProvider { + @NonNull + @Override + public CastOptions getCastOptions(Context context) { + return new CastOptions.Builder() + .setCastMediaOptions( + new CastMediaOptions.Builder() + .setMediaSessionEnabled(false) + .setNotificationOptions(null) + .build()) + // other options + .build(); + } +} +``` + +## Supported media formats + +Whether a specific stream is supported on a Cast device largely depends on the +receiver app, the media player used by the receiver and the Cast device, rather +then the implementation of the sender that basically only provides media URI and +metadata. + +Generally, Google Cast and all Cast Web Receiver applications support the media +facilities and types listed on +[this page](https://developers.google.com/cast/docs/media). If you build a +custom receiver that uses a media player different to the media player of the +Cast receiver SDK, your app may support +[other formats or features](https://github.com/shaka-project/shaka-player) than +listed in the reference above. + +The Media3 team can't give support for building a receiver app or investigations +regarding support for certain media formats on a cast devices. Please consult +the Cast documentation around +[building a receiver application](https://developers.google.com/cast/docs/web_receiver) +for further details. diff --git a/demos/cast/build.gradle b/demos/cast/build.gradle index 641ecddedc..7f41b8e0be 100644 --- a/demos/cast/build.gradle +++ b/demos/cast/build.gradle @@ -15,6 +15,8 @@ apply from: '../../constants.gradle' apply plugin: 'com.android.application' android { + namespace 'androidx.media3.demo.cast' + compileSdkVersion project.ext.compileSdkVersion compileOptions { diff --git a/demos/cast/src/main/java/androidx/media3/demo/cast/DemoUtil.java b/demos/cast/src/main/java/androidx/media3/demo/cast/DemoUtil.java index 6539eab686..dd00cf3c3a 100644 --- a/demos/cast/src/main/java/androidx/media3/demo/cast/DemoUtil.java +++ b/demos/cast/src/main/java/androidx/media3/demo/cast/DemoUtil.java @@ -37,27 +37,90 @@ static { ArrayList samples = new ArrayList<>(); - - // Clear content. + // HLS streams. + samples.add( + new MediaItem.Builder() + .setUri( + "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8") + .setMediaMetadata( + new MediaMetadata.Builder() + .setTitle("HLS (adaptive): Apple 4x3 basic stream (TS/h264/aac)") + .build()) + .setMimeType(MIME_TYPE_HLS) + .build()); + samples.add( + new MediaItem.Builder() + .setUri( + "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8") + .setMediaMetadata( + new MediaMetadata.Builder() + .setTitle("HLS (adaptive): Apple 16x9 basic stream (TS/h264/aac)") + .build()) + .setMimeType(MIME_TYPE_HLS) + .build()); + samples.add( + new MediaItem.Builder() + .setUri( + "https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/hls/DesigningForGoogleCast.m3u8") + .setMediaMetadata( + new MediaMetadata.Builder() + .setTitle("HLS (1280x720): Designing For Google Cast (TS/h264/aac)") + .build()) + .setMimeType(MIME_TYPE_HLS) + .build()); + // DASH streams samples.add( new MediaItem.Builder() .setUri("https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd") - .setMediaMetadata(new MediaMetadata.Builder().setTitle("Clear DASH: Tears").build()) + .setMediaMetadata( + new MediaMetadata.Builder() + .setTitle("DASH (adaptive): Tears of steal (HD, MP4, H264/aac)") + .build()) .setMimeType(MIME_TYPE_DASH) .build()); samples.add( new MediaItem.Builder() - .setUri("https://storage.googleapis.com/shaka-demo-assets/angel-one-hls/hls.m3u8") - .setMediaMetadata(new MediaMetadata.Builder().setTitle("Clear HLS: Angel one").build()) - .setMimeType(MIME_TYPE_HLS) + .setUri("https://storage.googleapis.com/wvmedia/clear/h264/tears/tears_uhd.mpd") + .setMediaMetadata( + new MediaMetadata.Builder() + .setTitle("DASH (3840x1714): Tears of steal (MP4, H264/aac)") + .build()) + .setMimeType(MIME_TYPE_DASH) .build()); + // Progressive video streams samples.add( new MediaItem.Builder() .setUri("https://html5demos.com/assets/dizzy.mp4") - .setMediaMetadata(new MediaMetadata.Builder().setTitle("Clear MP4: Dizzy").build()) + .setMediaMetadata( + new MediaMetadata.Builder().setTitle("MP4 (480x360): Dizzy (H264/aac)").build()) .setMimeType(MIME_TYPE_VIDEO_MP4) .build()); - + samples.add( + new MediaItem.Builder() + .setUri( + "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv") + .setMediaMetadata( + new MediaMetadata.Builder().setTitle("MKV (1280x720): Screens (h264/aac)").build()) + .setMimeType(MIME_TYPE_VIDEO_MP4) + .build()); + // Progressive audio streams with artwork + samples.add( + new MediaItem.Builder() + .setUri("https://storage.googleapis.com/automotive-media/Keys_To_The_Kingdom.mp3") + .setMediaMetadata( + new MediaMetadata.Builder() + .setTitle("MP3: Keys To The Kingdom (44100/stereo/320kb/s)") + .setArtist("The 126ers") + .setAlbumTitle("Youtube Audio Library Rock 2") + .setGenre("Rock") + .setTrackNumber(1) + .setTotalTrackCount(4) + .setArtworkUri( + Uri.parse( + "https://storage.googleapis.com/automotive-media/album_art_3.jpg")) + .build()) + .setMimeType(MimeTypes.AUDIO_MPEG) + .build()); // DRM content. samples.add( new MediaItem.Builder() diff --git a/demos/cast/src/main/java/androidx/media3/demo/cast/package-info.java b/demos/cast/src/main/java/androidx/media3/demo/cast/package-info.java index abd0359226..b2977f6849 100644 --- a/demos/cast/src/main/java/androidx/media3/demo/cast/package-info.java +++ b/demos/cast/src/main/java/androidx/media3/demo/cast/package-info.java @@ -14,6 +14,9 @@ * limitations under the License. */ @NonNullApi +@OptIn(markerClass = UnstableApi.class) package androidx.media3.demo.cast; +import androidx.annotation.OptIn; import androidx.media3.common.util.NonNullApi; +import androidx.media3.common.util.UnstableApi; diff --git a/demos/cast/src/main/res/drawable/ic_baseline_cast_connected_400.xml b/demos/cast/src/main/res/drawable/ic_baseline_cast_connected_400.xml index e65129b686..ac7c75ab73 100644 --- a/demos/cast/src/main/res/drawable/ic_baseline_cast_connected_400.xml +++ b/demos/cast/src/main/res/drawable/ic_baseline_cast_connected_400.xml @@ -13,8 +13,11 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> + + android:width="400dp" xmlns:android="http://schemas.android.com/apk/res/android" + tools:ignore="NewApi" xmlns:tools="http://schemas.android.com/tools"> diff --git a/demos/cast/src/main/res/drawable/ic_plus.xml b/demos/cast/src/main/res/drawable/ic_plus.xml index 5a5a5154c9..d90d7f1ff9 100644 --- a/demos/cast/src/main/res/drawable/ic_plus.xml +++ b/demos/cast/src/main/res/drawable/ic_plus.xml @@ -13,11 +13,15 @@ See the License for the specific language governing permissions and limitations under the License. --> + + android:width="24.0dp" + tools:ignore="NewApi" > diff --git a/demos/gl/build.gradle b/demos/gl/build.gradle index 8666c45aa0..d66c72cf4e 100644 --- a/demos/gl/build.gradle +++ b/demos/gl/build.gradle @@ -15,6 +15,8 @@ apply from: '../../constants.gradle' apply plugin: 'com.android.application' android { + namespace 'androidx.media3.demo.gl' + compileSdkVersion project.ext.compileSdkVersion compileOptions { @@ -55,5 +57,4 @@ dependencies { implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion implementation 'androidx.multidex:multidex:' + androidxMultidexVersion compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion - compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion } diff --git a/demos/gl/src/main/java/androidx/media3/demo/gl/package-info.java b/demos/gl/src/main/java/androidx/media3/demo/gl/package-info.java index 5a97794da8..d44a6522f1 100644 --- a/demos/gl/src/main/java/androidx/media3/demo/gl/package-info.java +++ b/demos/gl/src/main/java/androidx/media3/demo/gl/package-info.java @@ -14,6 +14,9 @@ * limitations under the License. */ @NonNullApi +@OptIn(markerClass = UnstableApi.class) package androidx.media3.demo.gl; +import androidx.annotation.OptIn; import androidx.media3.common.util.NonNullApi; +import androidx.media3.common.util.UnstableApi; diff --git a/demos/main/build.gradle b/demos/main/build.gradle index a18768e75b..0aaa4388e7 100644 --- a/demos/main/build.gradle +++ b/demos/main/build.gradle @@ -14,8 +14,11 @@ apply from: '../../constants.gradle' apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' android { + namespace 'androidx.media3.demo.main' + compileSdkVersion project.ext.compileSdkVersion compileOptions { @@ -27,9 +30,7 @@ android { versionName project.ext.releaseVersion versionCode project.ext.releaseVersionCode minSdkVersion 21 - // Not using appTargetSDKVersion to allow local file access on API 29 - // and higher [Internal ref: b/191644662] - targetSdkVersion project.ext.mainDemoAppTargetSdkVersion + targetSdkVersion project.ext.appTargetSdkVersion multiDexEnabled true } @@ -95,6 +96,7 @@ dependencies { withDecoderExtensionsImplementation project(modulePrefix + 'lib-decoder-flac') withDecoderExtensionsImplementation project(modulePrefix + 'lib-decoder-opus') withDecoderExtensionsImplementation project(modulePrefix + 'lib-decoder-vp9') + withDecoderExtensionsImplementation project(modulePrefix + 'lib-decoder-midi') withDecoderExtensionsImplementation project(modulePrefix + 'lib-datasource-rtmp') } diff --git a/demos/main/src/main/AndroidManifest.xml b/demos/main/src/main/AndroidManifest.xml index 159cebf22b..8b70ede27d 100644 --- a/demos/main/src/main/AndroidManifest.xml +++ b/demos/main/src/main/AndroidManifest.xml @@ -21,8 +21,12 @@ + + + + @@ -36,7 +40,6 @@ android:networkSecurityConfig="@xml/network_security_config" android:largeHeap="true" android:allowBackup="false" - android:requestLegacyExternalStorage="true" android:supportsRtl="true" android:name="androidx.multidex.MultiDexApplication" tools:targetApi="29"> @@ -53,6 +56,7 @@ + @@ -94,7 +98,8 @@ + android:exported="false" + android:foregroundServiceType="dataSync"> diff --git a/demos/main/src/main/assets/media.exolist.json b/demos/main/src/main/assets/media.exolist.json index da0dea30a3..023d03f4b0 100644 --- a/demos/main/src/main/assets/media.exolist.json +++ b/demos/main/src/main/assets/media.exolist.json @@ -221,22 +221,22 @@ "name": "Dice Test", "samples": [ { - "name": "WWE live, sandbox", + "name": "WWE live hls clear, sandbox", "uri": "https://dummy.imggaming.com/video?id=111449&live=true&dash=&realm=dce.sandbox&usr=&pwd=" }, { - "name": "WWE live, wwe", + "name": "WWE live hls clear, wwe", "uri": "https://dummy.imggaming.com/video?id=111449&live=true&dash=&realm=dce.wwe&usr=&pwd=" }, { - "name": "WWE vod, wwe", + "name": "WWE vod hls clear, wwe", "uri": "https://dummy.imggaming.com/video?id=530182&live=&dash=&realm=dce.wwe&usr=&pwd=", "subtitle_uri": "https://dve-subtitles.imggaming.com/530182/741299/vtt/subtitle-en-US-199647-1697490152197.vtt", "subtitle_mime_type": "text/vtt", "subtitle_language": "en" }, { - "name": "450830 DRM, adjara", + "name": "450830 vod dash DRM, adjara", "uri": "https://dummy.imggaming.com/video?id=450830&live=&dash=true&realm=dce.adjara&usr=&pwd=" } ] @@ -385,6 +385,12 @@ "drm_scheme": "widevine", "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=GTS_HW_SECURE_ALL&provider=widevine_test" }, + { + "name": "20s license with renewal", + "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd", + "drm_scheme": "widevine", + "drm_license_uri": "https://proxy.uat.widevine.com/proxy?video_id=GTS_CAN_RENEW&provider=widevine_test" + }, { "name": "30s license (fails at ~30s)", "uri": "https://storage.googleapis.com/wvmedia/cenc/h264/tears/tears.mpd", @@ -667,6 +673,18 @@ "name": "DASH VOD: Tears of Steel (11 periods, pre/mid/post), 2/5/2 ads [5/10s]", "uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1" }, + { + "name": "DASH live: Tears of Steel (mid), 3 ads each [10 s]", + "uri": "ssai://dai.google.com/?assetKey=jNVjPZwzSkyeGiaNQTPqiQ&format=0&adsId=1" + }, + { + "name": "DASH live: New Tears of Steel (mid), 3 ads each [10 s]", + "uri": "ssai://dai.google.com/?assetKey=PSzZMzAkSXCmlJOWDmRj8Q&format=0&adsId=12" + }, + { + "name": "DASH live: Unencrypted stream with 30s ad breaks every minute", + "uri": "ssai://dai.google.com/?assetKey=0ndl1dJcRmKDUPxTRjvdog&format=0&adsId=21" + }, { "name": "Playlist: No ads - HLS VOD: Demo (skippable pre/post) - No ads", "playlist": [ @@ -696,13 +714,13 @@ ] }, { - "name": "Playlist: No ads - HLS Live: Big Buck Bunny (mid) - No ads", + "name": "Playlist: No ads - DASH VOD: Tears of Steel (11 periods, pre/mid/post) - No ads", "playlist": [ { "uri": "https://html5demos.com/assets/dizzy.mp4" }, { - "uri": "ssai://dai.google.com/?assetKey=sN_IYUG8STe1ZzhIIE_ksA&format=2&adsId=3" + "uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1" }, { "uri": "https://html5demos.com/assets/dizzy.mp4" @@ -710,10 +728,11 @@ ] }, { - "name": "Playlist: No ads - DASH VOD: Tears of Steel (11 periods, pre/mid/post) - No ads", + "name": "Playlist: Client-side Ads - DASH VOD: Tears of Steel (11 periods, pre/mid/post) - No ads", "playlist": [ { - "uri": "https://html5demos.com/assets/dizzy.mp4" + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv", + "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&cmsid=496&vid=short_onecue&correlator=" }, { "uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1" @@ -724,14 +743,27 @@ ] }, { - "name": "Playlist: Client-side Ads - DASH VOD: Tears of Steel (11 periods, pre/mid/post) - No ads", + "name": "Playlist: No ads - DASH live: Tears of Steel (mid) - No ads", "playlist": [ { - "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv", - "ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&cmsid=496&vid=short_onecue&correlator=" + "uri": "https://html5demos.com/assets/dizzy.mp4" }, { - "uri": "ssai://dai.google.com/?contentSourceId=2559737&videoId=tos-dash&format=0&adsId=1" + "uri": "ssai://dai.google.com/?assetKey=PSzZMzAkSXCmlJOWDmRj8Q&format=0&adsId=1" + }, + { + "uri": "https://html5demos.com/assets/dizzy.mp4" + } + ] + }, + { + "name": "Playlist: No ads - HLS live: Big Buck Bunny - No ads", + "playlist": [ + { + "uri": "https://html5demos.com/assets/dizzy.mp4" + }, + { + "uri": "ssai://dai.google.com/?assetKey=sN_IYUG8STe1ZzhIIE_ksA&format=2&adsId=3" }, { "uri": "https://html5demos.com/assets/dizzy.mp4" @@ -755,7 +787,7 @@ ] }, { - "name": "Audio -> Video -> Audio", + "name": "Audio -> Video (MKV) -> Video (MKV) -> Audio -> Video (MKV) -> Video (DASH) -> Audio", "playlist": [ { "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/audio-141.mp4" @@ -763,6 +795,18 @@ { "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv" }, + { + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv" + }, + { + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/audio-141.mp4" + }, + { + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv" + }, + { + "uri": "https://storage.googleapis.com/wvmedia/clear/h264/tears/tears.mpd" + }, { "uri": "https://storage.googleapis.com/exoplayer-test-media-1/gen-3/screens/dash-vod-single-segment/audio-141.mp4" } @@ -889,6 +933,14 @@ { "name": "MPEG-4 Timed Text", "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mp4/dizzy-with-tx3g.mp4" + }, + { + "name": "SubRip muxed into MKV", + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-with-subrip.mkv" + }, + { + "name": "Overlapping SSA muxed into MKV", + "uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-with-overlapping-ssa.mkv" } ] }, diff --git a/demos/main/src/main/java/androidx/media3/demo/main/DemoTrackNameProvider.java b/demos/main/src/main/java/androidx/media3/demo/main/DemoTrackNameProvider.java index 70422ef3a9..6f26618416 100644 --- a/demos/main/src/main/java/androidx/media3/demo/main/DemoTrackNameProvider.java +++ b/demos/main/src/main/java/androidx/media3/demo/main/DemoTrackNameProvider.java @@ -13,7 +13,7 @@ import androidx.media3.ui.TrackNameProvider; import java.util.Locale; -// Based on androidx.media3.ui.DefaultTrackNameProvider, media3:lib-ui +// Based on androidx.media3.ui.DefaultTrackNameProvider, media3:media3-ui // Modify the label name of audio tracks, also show the audio groupId. @UnstableApi public class DemoTrackNameProvider implements TrackNameProvider { diff --git a/demos/main/src/main/java/androidx/media3/demo/main/DownloadTracker.java b/demos/main/src/main/java/androidx/media3/demo/main/DownloadTracker.java index 089644cbc9..af32eb7065 100644 --- a/demos/main/src/main/java/androidx/media3/demo/main/DownloadTracker.java +++ b/demos/main/src/main/java/androidx/media3/demo/main/DownloadTracker.java @@ -26,6 +26,7 @@ import androidx.annotation.OptIn; import androidx.annotation.RequiresApi; import androidx.fragment.app.FragmentManager; +import androidx.media3.common.C; import androidx.media3.common.DrmInitData; import androidx.media3.common.Format; import androidx.media3.common.MediaItem; @@ -51,6 +52,7 @@ import androidx.media3.exoplayer.trackselection.MappingTrackSelector.MappedTrackInfo; import java.io.IOException; import java.util.HashMap; +import java.util.UUID; import java.util.concurrent.CopyOnWriteArraySet; /** Tracks media that has been downloaded. */ @@ -202,7 +204,7 @@ public void onPrepared(DownloadHelper helper) { return; } // TODO(internal b/163107948): Support cases where DrmInitData are not in the manifest. - if (!hasSchemaData(format.drmInitData)) { + if (!hasNonNullWidevineSchemaData(format.drmInitData)) { Toast.makeText(context, R.string.download_start_error_offline_license, Toast.LENGTH_LONG) .show(); Log.e( @@ -323,12 +325,14 @@ private void onDownloadPrepared(DownloadHelper helper) { } /** - * Returns whether any the {@link DrmInitData.SchemeData} contained in {@code drmInitData} has - * non-null {@link DrmInitData.SchemeData#data}. + * Returns whether any {@link DrmInitData.SchemeData} that {@linkplain + * DrmInitData.SchemeData#matches(UUID) matches} {@link C#WIDEVINE_UUID} has non-null {@link + * DrmInitData.SchemeData#data}. */ - private boolean hasSchemaData(DrmInitData drmInitData) { + private boolean hasNonNullWidevineSchemaData(DrmInitData drmInitData) { for (int i = 0; i < drmInitData.schemeDataCount; i++) { - if (drmInitData.get(i).hasData()) { + DrmInitData.SchemeData schemeData = drmInitData.get(i); + if (schemeData.matches(C.WIDEVINE_UUID) && schemeData.hasData()) { return true; } } diff --git a/demos/main/src/main/java/androidx/media3/demo/main/IntentUtil.java b/demos/main/src/main/java/androidx/media3/demo/main/IntentUtil.java index 068397e066..5369029b91 100644 --- a/demos/main/src/main/java/androidx/media3/demo/main/IntentUtil.java +++ b/demos/main/src/main/java/androidx/media3/demo/main/IntentUtil.java @@ -95,7 +95,7 @@ public static void addToIntent(List mediaItems, Intent intent) { if (mediaItem.mediaMetadata.title != null) { intent.putExtra(TITLE_EXTRA, mediaItem.mediaMetadata.title); } - addPlaybackPropertiesToIntent(localConfiguration, intent, /* extrasKeySuffix= */ ""); + addLocalConfigurationToIntent(localConfiguration, intent, /* extrasKeySuffix= */ ""); addClippingConfigurationToIntent( mediaItem.clippingConfiguration, intent, /* extrasKeySuffix= */ ""); } else { @@ -105,7 +105,7 @@ public static void addToIntent(List mediaItems, Intent intent) { MediaItem.LocalConfiguration localConfiguration = checkNotNull(mediaItem.localConfiguration); intent.putExtra(URI_EXTRA + ("_" + i), localConfiguration.uri.toString()); - addPlaybackPropertiesToIntent(localConfiguration, intent, /* extrasKeySuffix= */ "_" + i); + addLocalConfigurationToIntent(localConfiguration, intent, /* extrasKeySuffix= */ "_" + i); addClippingConfigurationToIntent( mediaItem.clippingConfiguration, intent, /* extrasKeySuffix= */ "_" + i); if (mediaItem.mediaMetadata.title != null) { @@ -200,7 +200,7 @@ private static MediaItem.Builder populateDrmPropertiesFromIntent( return builder; } - private static void addPlaybackPropertiesToIntent( + private static void addLocalConfigurationToIntent( MediaItem.LocalConfiguration localConfiguration, Intent intent, String extrasKeySuffix) { intent .putExtra(MIME_TYPE_EXTRA + extrasKeySuffix, localConfiguration.mimeType) diff --git a/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java b/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java index d812c2ffbf..857efd4ddf 100644 --- a/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java +++ b/demos/main/src/main/java/androidx/media3/demo/main/PlayerActivity.java @@ -93,11 +93,11 @@ public class PlayerActivity extends AppCompatActivity @Nullable private AdsLoader clientSideAdsLoader; - // TODO: Annotate this and serverSideAdsLoaderState below with @OptIn when it can be applied to - // fields (needs http://r.android.com/2004032 to be released into a version of - // androidx.annotation:annotation-experimental). - @Nullable private ImaServerSideAdInsertionMediaSource.AdsLoader serverSideAdsLoader; + @OptIn(markerClass = UnstableApi.class) + @Nullable + private ImaServerSideAdInsertionMediaSource.AdsLoader serverSideAdsLoader; + @OptIn(markerClass = UnstableApi.class) private ImaServerSideAdInsertionMediaSource.AdsLoader.@MonotonicNonNull State serverSideAdsLoaderState; @@ -354,7 +354,7 @@ private List createMediaItems(Intent intent) { finish(); return Collections.emptyList(); } - if (Util.maybeRequestReadExternalStoragePermission(/* activity= */ this, mediaItem)) { + if (Util.maybeRequestReadStoragePermission(/* activity= */ this, mediaItem)) { // The player will be reinitialized if the permission is granted. return Collections.emptyList(); } diff --git a/demos/main/src/main/java/androidx/media3/demo/main/SampleChooserActivity.java b/demos/main/src/main/java/androidx/media3/demo/main/SampleChooserActivity.java index 9417f7428a..0d8edc96c3 100644 --- a/demos/main/src/main/java/androidx/media3/demo/main/SampleChooserActivity.java +++ b/demos/main/src/main/java/androidx/media3/demo/main/SampleChooserActivity.java @@ -83,7 +83,6 @@ public class SampleChooserActivity extends AppCompatActivity private static final String TAG = "SampleChooserActivity"; private static final String GROUP_POSITION_PREFERENCE_KEY = "sample_chooser_group_position"; private static final String CHILD_POSITION_PREFERENCE_KEY = "sample_chooser_child_position"; - private static final int POST_NOTIFICATION_PERMISSION_REQUEST_CODE = 100; private String[] uris; private boolean useExtensionRenderers; @@ -186,14 +185,6 @@ public void onDownloadsChanged() { public void onRequestPermissionsResult( int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); - if (requestCode == POST_NOTIFICATION_PERMISSION_REQUEST_CODE) { - handlePostNotificationPermissionGrantResults(grantResults); - } else { - handleExternalStoragePermissionGrantResults(grantResults); - } - } - - private void handlePostNotificationPermissionGrantResults(int[] grantResults) { if (!notificationPermissionToastShown && (grantResults.length == 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED)) { Toast.makeText( @@ -208,30 +199,8 @@ private void handlePostNotificationPermissionGrantResults(int[] grantResults) { } } - private void handleExternalStoragePermissionGrantResults(int[] grantResults) { - if (grantResults.length == 0) { - // Empty results are triggered if a permission is requested while another request was already - // pending and can be safely ignored in this case. - return; - } else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { - loadSample(); - } else { - Toast.makeText(getApplicationContext(), R.string.sample_list_load_error, Toast.LENGTH_LONG) - .show(); - finish(); - } - } - private void loadSample() { checkNotNull(uris); - - for (int i = 0; i < uris.length; i++) { - Uri uri = Uri.parse(uris[i]); - if (Util.maybeRequestReadExternalStoragePermission(this, uri)) { - return; - } - } - SampleListLoader loaderTask = new SampleListLoader(); loaderTask.execute(uris); } @@ -311,8 +280,7 @@ && checkSelfPermission(Api33.getPostNotificationPermissionString()) != PackageManager.PERMISSION_GRANTED) { downloadMediaItemWaitingForNotificationPermission = playlistHolder.mediaItems.get(0); requestPermissions( - new String[] {Api33.getPostNotificationPermissionString()}, - /* requestCode= */ POST_NOTIFICATION_PERMISSION_REQUEST_CODE); + new String[] {Api33.getPostNotificationPermissionString()}, /* requestCode= */ 0); } else { toggleDownload(playlistHolder.mediaItems.get(0)); } diff --git a/demos/session/build.gradle b/demos/session/build.gradle index 376c69534d..c279b96f30 100644 --- a/demos/session/build.gradle +++ b/demos/session/build.gradle @@ -16,6 +16,8 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' android { + namespace 'androidx.media3.demo.session' + compileSdkVersion project.ext.compileSdkVersion compileOptions { @@ -57,13 +59,13 @@ android { } dependencies { + // For detecting and debugging leaks only. LeakCanary is not needed for demo app to work. + debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10' implementation 'androidx.core:core-ktx:' + androidxCoreVersion implementation 'androidx.appcompat:appcompat:' + androidxAppCompatVersion implementation 'androidx.multidex:multidex:' + androidxMultidexVersion implementation 'com.google.android.material:material:' + androidxMaterialVersion - implementation project(modulePrefix + 'lib-exoplayer') - implementation project(modulePrefix + 'lib-exoplayer-dash') - implementation project(modulePrefix + 'lib-exoplayer-hls') implementation project(modulePrefix + 'lib-ui') implementation project(modulePrefix + 'lib-session') + implementation project(modulePrefix + 'demo-session-service') } diff --git a/demos/session/src/main/AndroidManifest.xml b/demos/session/src/main/AndroidManifest.xml index 90557fc6c7..550b0698f0 100644 --- a/demos/session/src/main/AndroidManifest.xml +++ b/demos/session/src/main/AndroidManifest.xml @@ -20,6 +20,7 @@ + + + + @@ -40,7 +46,9 @@ + android:exported="true" + android:launchMode="singleTop" + android:theme="@style/Theme.AppCompat.NoActionBar"/> + diff --git a/demos/session/src/main/java/androidx/media3/demo/session/MainActivity.kt b/demos/session/src/main/java/androidx/media3/demo/session/MainActivity.kt index 9328a059e9..4788496f81 100644 --- a/demos/session/src/main/java/androidx/media3/demo/session/MainActivity.kt +++ b/demos/session/src/main/java/androidx/media3/demo/session/MainActivity.kt @@ -15,9 +15,11 @@ */ package androidx.media3.demo.session +import android.Manifest import android.content.ComponentName import android.content.Context -import android.content.Intent +import android.content.pm.PackageManager +import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.MenuItem @@ -26,6 +28,7 @@ import android.view.ViewGroup import android.widget.ArrayAdapter import android.widget.ListView import android.widget.TextView +import android.widget.Toast import androidx.activity.OnBackPressedCallback import androidx.appcompat.app.AppCompatActivity import androidx.core.content.ContextCompat @@ -70,18 +73,26 @@ class MainActivity : AppCompatActivity() { findViewById(R.id.open_player_floating_button) .setOnClickListener { - // display the playing media items - val intent = Intent(this, PlayerActivity::class.java) - startActivity(intent) + // Start the session activity that shows the playback activity. The System UI uses the same + // intent in the same way to start the activity from the notification. + browser?.sessionActivity?.send() } onBackPressedDispatcher.addCallback( - object : OnBackPressedCallback(/* enabled= */ true) { + object : OnBackPressedCallback(true) { override fun handleOnBackPressed() { popPathStack() } } ) + + if ( + Build.VERSION.SDK_INT >= 33 && + checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != + PackageManager.PERMISSION_GRANTED + ) { + requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), /* requestCode= */ 0) + } } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -102,6 +113,23 @@ class MainActivity : AppCompatActivity() { super.onStop() } + override fun onRequestPermissionsResult( + requestCode: Int, + permissions: Array, + grantResults: IntArray + ) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + if (grantResults.isEmpty()) { + // Empty results are triggered if a permission is requested while another request was already + // pending and can be safely ignored in this case. + return + } + if (grantResults[0] != PackageManager.PERMISSION_GRANTED) { + Toast.makeText(applicationContext, R.string.notification_permission_denied, Toast.LENGTH_LONG) + .show() + } + } + private fun initializeBrowser() { browserFuture = MediaBrowser.Builder( diff --git a/demos/session/src/main/java/androidx/media3/demo/session/PlayableFolderActivity.kt b/demos/session/src/main/java/androidx/media3/demo/session/PlayableFolderActivity.kt index 9c6f3e5b4a..97cf2b8110 100644 --- a/demos/session/src/main/java/androidx/media3/demo/session/PlayableFolderActivity.kt +++ b/demos/session/src/main/java/androidx/media3/demo/session/PlayableFolderActivity.kt @@ -43,7 +43,7 @@ import com.google.common.util.concurrent.ListenableFuture class PlayableFolderActivity : AppCompatActivity() { private lateinit var browserFuture: ListenableFuture private val browser: MediaBrowser? - get() = if (browserFuture.isDone) browserFuture.get() else null + get() = if (browserFuture.isDone && !browserFuture.isCancelled) browserFuture.get() else null private lateinit var mediaList: ListView private lateinit var mediaListAdapter: PlayableMediaItemArrayAdapter @@ -51,6 +51,7 @@ class PlayableFolderActivity : AppCompatActivity() { companion object { private const val MEDIA_ITEM_ID_KEY = "MEDIA_ITEM_ID_KEY" + fun createIntent(context: Context, mediaItemID: String): Intent { val intent = Intent(context, PlayableFolderActivity::class.java) intent.putExtra(MEDIA_ITEM_ID_KEY, mediaItemID) @@ -77,8 +78,7 @@ class PlayableFolderActivity : AppCompatActivity() { browser.shuffleModeEnabled = false browser.prepare() browser.play() - val intent = Intent(this, PlayerActivity::class.java) - startActivity(intent) + browser.sessionActivity?.send() } } @@ -88,8 +88,7 @@ class PlayableFolderActivity : AppCompatActivity() { browser.shuffleModeEnabled = true browser.prepare() browser.play() - val intent = Intent(this, PlayerActivity::class.java) - startActivity(intent) + browser.sessionActivity?.send() } findViewById