-
Notifications
You must be signed in to change notification settings - Fork 476
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allows exact live sync for HLS #703
Conversation
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET, | ||
SntpClient.getElapsedRealtimeOffsetMs(), | ||
periodDurationUs, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the SntpClient is not initialized this change done nothing, so you could consider making the changes to DefaultHlsPlaylistTracker
surrounded by a feature flag from the HlsPlaylistTracker.Factory
(if it was not private), or simply a PSFB flag.
} | ||
|
||
private void requestMasterPlaylist( | ||
EventDispatcher eventDispatcher, ParsingLoadable<HlsPlaylist> multivariantPlaylistLoadable) { | ||
long elapsedRealtime = | ||
initialPlaylistLoader.startLoading( | ||
multivariantPlaylistLoadable, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The requestMasterPlaylist
was simply a refactoring (extract method), then call the method in the SntpClient
callback. Most times this is a no-op as the SntpClient
offset is up to date.
} | ||
} | ||
|
||
private void loadAfterTimeSync(Uri playlistRequestUri) { | ||
ParsingLoadable.Parser<HlsPlaylist> mediaPlaylistParser = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same refactor lift method to create loadAfterTimeSync()
. Here only the primary playlist load needs to possibly refresh SntpClient
as the master playlist is only loaded once
@stevemayhew I think you should rebase this against |
This method allows customizing the maximum position when using `Player.seekToPrevious()`. This commit also adds two new methods to `TestExoPlayerBuilder`: - `setMaxSeekToPreviousPosition(long)` - `getMaxSeekToPreviousPosition()`
…n the correct value
In muxer only 32-bit sample duration is supported. Earlier the duration was type casted at different places. PiperOrigin-RevId: 643954226
PiperOrigin-RevId: 643984158
…eekToPreviousPosition PiperOrigin-RevId: 643987403
PiperOrigin-RevId: 644002147
`Objects.equals` has been available since API 19. PiperOrigin-RevId: 644026884
This is an internal refactor with no logic changed. There is a duplication in information and a lack of consistency around the use of test assets in transformer androidTest. This change refactors each asset to be defined as its own AssetInfo, with the other relevant parts stored alongside the uri. Once this is in place, we can consider other useful functionality, such as having boolean flags for what tracks an asset has, helper methods for whether an asset is local or remote, and more. This will reduce the manual overhead necessary to use more assets in tests, and in particular leads towards easily using new & existing assets in parameterized tests. PiperOrigin-RevId: 644040595
PiperOrigin-RevId: 644058273
These calls have no effect because the VideoFrameReleaseControl of CompositionPlayer is created with allowedJoiningTimeMs set to 0. PiperOrigin-RevId: 644274524
Also adds basic support for canDecode receiving an image Format. PiperOrigin-RevId: 644300521
There are a lot of tests for AudioFocusManager in isolation, but almost none for the handling in ExoPlayer. Add test coverage for all the common cases, including some currently broken behavior that is indicated by TODOs. PiperOrigin-RevId: 644319251
#cherrypick PiperOrigin-RevId: 644351958
#cherrypick PiperOrigin-RevId: 644352776
PiperOrigin-RevId: 644363298
PiperOrigin-RevId: 644373231
PiperOrigin-RevId: 644402109
The 'player commands' returned to ExoPlayerImpl instruct the player on how to treat the current audio focus state. The current return value when playWhenReady==false is misleading because it implies we are definitely not allowed to play as if we've lost focus. Instead, we should return the actual player command corresponding to the focus state we are in. This has no practical effect in ExoPlayerImpl as we already ignore the 'player command' completely when playWhenReady=false. To facilitate this change, we also introduce a new internal state for FOCUS_NOT_REQUESTED to distinguish it from the state in which we lost focus. #cherrypick PiperOrigin-RevId: 644416586
PiperOrigin-RevId: 644659699
Includes adjusting how effects are defined, to make it clearer in a test that a given ItemConfig has effects associated with it. PiperOrigin-RevId: 644684308
The Javadoc was indicating that the duration should always be set, but it doesn't need to be set in most cases for export. PiperOrigin-RevId: 644685827
#cherrypick PiperOrigin-RevId: 644693533
In the case of a legacy session app providing an error code different to `ERROR_UNKNOWN` without an error message, a localized fallback message is provided instead of ignoring the error. #cherrypick PiperOrigin-RevId: 644704680
PiperOrigin-RevId: 653206245
----- Context: * Each sequence is wrapped as a single MediaSource, each being played by an underlying ExoPlayer. * Repeat mode is typically implemented in Players as a seek to the next item in the playlist. ----- This CL: Repeat mode is triggered by listening for when the main input player sees a play when ready change due to the end of the media item. There is a slight delay at the end of the playback, before it repeats. Setting repeat mode on the underlying players addresses this, but means that the players will seek without waiting for the CompositionPlayer, and as such previewAudioPipeline does not get the correct signals around blocking/flushing. PreviewAudioPipeline - The seek position can validly be C.TIME_UNSET, however preview pipeline did not handle this case. CompositionPlayer getContentPosition is given (through a lambda) as a supplier to the State object, which means any comparisons between previous/new state for this value does not work. In SimpleBasePlayer, there is logic to use the positionDiscontinuityPositionUs for the position change (see getPositionInfo called from updateStateAndInformListeners), however this logic is not considered in getMediaItemTransitionReason, so a condition needed to be added for this case. ----- Tests: * Dump files clearly show the position and data is repeated. * Assertions on the reasons for transitions or position discontinuities. PiperOrigin-RevId: 653210278
The change is purely video releated. PiperOrigin-RevId: 653229546
#cherrypick PiperOrigin-RevId: 653261278
Implement dOpsBox to provide support for Opus audio codec PiperOrigin-RevId: 653288049
Boxes.moov() simply wraps the subboxes and this logic can be put into main method which has all the logic of creating moov box. This is to eventually move Mp4MoovStucture.moov() into Boxes.java where all other box creation methods are already present. PiperOrigin-RevId: 653319292
This replaces the existing PlaceholderSurface mode with a more efficient solution that doesn't require a GL texture or a new thread. PiperOrigin-RevId: 653537596
PiperOrigin-RevId: 653569415
This test is flaky at p4head, because CompositionPlayer has no logic to ensure a specific input is used to configure the audio graph. Depending on which sequence registers input first, it changes the output format of the media. By setting effects on the 2nd sequence, both inputs are the same format and this flakiness is avoided in the test. PiperOrigin-RevId: 653582441
Creating a moov box is same as creating any other box so there is no particular need to have a separate class for this. In a follow up CL the method will be moved into Boxes.java along with other box creation methods. Made nit changes in the final fields ordering to match with constructor parameter ordering. PiperOrigin-RevId: 653602558
This controller connects the audio output to the MediaCodec so that it can automatically propagate CTA-2075 loudness metadata. PiperOrigin-RevId: 653628503
PiperOrigin-RevId: 653629763
#cherrypick PiperOrigin-RevId: 653640574
PiperOrigin-RevId: 653644998
#cherrypick PiperOrigin-RevId: 653654999
PiperOrigin-RevId: 653667116
Introduced three `setDataSource` APIs in `MediaExtractorCompat`, enabling the use of `AssetFileDescriptor` and `FileDescriptor` to set the data source. PiperOrigin-RevId: 653957035
All methods check if the player is currently handling the ad source by calling isCurrentAdPlaying(). This method was missing a check for empty timelines that throws an exception when trying to access a non-existent period. Also add this check to two methods that assume the current item is the ads source, but didn't check it yet. PiperOrigin-RevId: 653963557
According to b/292763081 the build failure `unresolved reference: addLast` in the session-demo is caused by a bug in the Kotlin compiler 1.9.0 to which we depend. Bumping the Kotlin plugin version to 1.9.10 which is the next above 1.9.0 as listed in [1], the build problem is resolved. Further, the Kotlin extension compiler version is set to 1.5.3 to make compose work with Kotlin 1.9.10 (see [2] for the Kotlin/Compose compatibility map). [1] https://kotlinlang.org/docs/releases.html#release-details [2] https://developer.android.com/jetpack/androidx/releases/compose-kotlin PiperOrigin-RevId: 654030537
We currently wait until a previous AudioTrack from the same DefaultAudioSink is released on a background thread before attempting to initialize a new AudioTrack. This is done to avoid issues where the releasing track blocks some shared audio memory, preventing a new track from being created. The current solution has two main shortcomings: - In most cases, the system can easily handle multiple AudioTracks and waiting for the release just causes unnecessary delays (e.g. when seeking). - It only waits for a previous track from the same DefaultAudioSink, not accounting for any other tracks that may be in the process of being released from other players. To improve on both shortcomings, we can (1) move the check for "is releasing tracks and thus may block shared memory" to the static release infrastructure to be shared across all player instances. (2) optimistically create a new AudioTrack immediately without waiting for the previous one to be fully released. (3) extend the existing retry logic that already retries failed attempts for 100ms to only start the timer when ongoing releases are done. This ensures we really waited until we have all shared resources we can get before giving up completely. This also acts as a replacement for change (2) to handle situations where creating a second track is genuinely not possible. Also increase threshold to 200ms as the new unit test is falky on a real device with just 100ms (highlighting that the device needed more than 100ms to clean up internal resources). PiperOrigin-RevId: 654053123
This was used in media1 `MediaItemDescription` to indicate the download status of a media item. When connected to a legacy `MediaBrowserServiceCompat` the Media3 browsers converts the legacy media item to a Media3 `MediaItem` and converts the extras of `MediaDescriptionCompat.extras` to `MediaMetadata.extras`. #cherrypick PiperOrigin-RevId: 654625502
Thanks @BogdanLivadariu no one is giving it any attention, we've already shipped it ;-) |
This commit addresses the issue of inaccurate live position measurement in HLS by introducing NTP time synchronization. Unlike DASH, HLS lacks origin time synchronization logic, resulting in potential discrepancies in wall-clock time when using `System.currentTimeMillis()`. The commit utilizes the `SntpClient` in ExoPlayer to determine the clock offset with the default NTP server (`time.android.com`) and applies this information to the `HlsMediaSource`. With the implementation of this change, multiple devices can now synchronize playback to a common time source, as long as the origin server also synchronizes to an NTP time source.
Enables testing the Live Offset settings with the main demo PlayerActivity. Using an intent like: ``` adb shell am start -n androidx.media3.demo.main/.PlayerActivity -a androidx.media3.demo.main.action.VIEW --ei live_offset_target 30 --ef live_offset_adjust_speed 35.0 ``` Will start playback synced to a 30 second offset with agressive speed adjustment to achive it, note this requires audio codec that supports speed changes (AAC for example)
9f96065
to
a4979a8
Compare
Closing this mess and re-opening with base against main |
This pull request satisfies the enhancement request #702. For playback longer than a few hours the fix for SntpClient is also required, see pull request #697