mirror of
https://github.com/samsonjs/media.git
synced 2026-04-27 15:07:40 +00:00
commit
ab4d37f499
92 changed files with 4425 additions and 1282 deletions
1
.github/ISSUE_TEMPLATE/bug.yml
vendored
1
.github/ISSUE_TEMPLATE/bug.yml
vendored
|
|
@ -18,6 +18,7 @@ body:
|
||||||
label: ExoPlayer Version
|
label: ExoPlayer Version
|
||||||
description: What version of ExoPlayer are you using?
|
description: What version of ExoPlayer are you using?
|
||||||
options:
|
options:
|
||||||
|
- 2.18.1
|
||||||
- 2.18.0
|
- 2.18.0
|
||||||
- 2.17.1
|
- 2.17.1
|
||||||
- 2.17.0
|
- 2.17.0
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,48 @@
|
||||||
# Release notes
|
# Release notes
|
||||||
|
|
||||||
|
### 2.18.1 (2022-07-21)
|
||||||
|
|
||||||
|
This release corresponds to the
|
||||||
|
[AndroidX media3 1.0.0-beta02 release](https://github.com/androidx/media/releases/tag/1.0.0-beta02).
|
||||||
|
|
||||||
|
* Core library:
|
||||||
|
* Ensure that changing the `ShuffleOrder` with `ExoPlayer.setShuffleOrder`
|
||||||
|
results in a call to `Player.Listener#onTimelineChanged` with
|
||||||
|
`reason=Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED`
|
||||||
|
([#9889](https://github.com/google/ExoPlayer/issues/9889)).
|
||||||
|
* For progressive media, only include selected tracks in buffered position
|
||||||
|
([#10361](https://github.com/google/ExoPlayer/issues/10361)).
|
||||||
|
* Allow custom logger for all ExoPlayer log output
|
||||||
|
([#9752](https://github.com/google/ExoPlayer/issues/9752)).
|
||||||
|
* Fix implementation of `setDataSourceFactory` in
|
||||||
|
`DefaultMediaSourceFactory`, which was non-functional in some cases
|
||||||
|
([#116](https://github.com/androidx/media/issues/116)).
|
||||||
|
* Extractors:
|
||||||
|
* Fix parsing of H265 short term reference picture sets
|
||||||
|
([#10316](https://github.com/google/ExoPlayer/issues/10316)).
|
||||||
|
* Fix parsing of bitrates from `esds` boxes
|
||||||
|
([#10381](https://github.com/google/ExoPlayer/issues/10381)).
|
||||||
|
* DASH:
|
||||||
|
* Parse ClearKey license URL from manifests
|
||||||
|
([#10246](https://github.com/google/ExoPlayer/issues/10246)).
|
||||||
|
* UI:
|
||||||
|
* Ensure TalkBack announces the currently active speed option in the
|
||||||
|
playback controls menu
|
||||||
|
([#10298](https://github.com/google/ExoPlayer/issues/10298)).
|
||||||
|
* RTSP:
|
||||||
|
* Add VP8 fragmented packet handling
|
||||||
|
([#110](https://github.com/androidx/media/pull/110)).
|
||||||
|
* Leanback extension:
|
||||||
|
* Listen to `playWhenReady` changes in `LeanbackAdapter`
|
||||||
|
([10420](https://github.com/google/ExoPlayer/issues/10420)).
|
||||||
|
* Cast:
|
||||||
|
* Use the `MediaItem` that has been passed to the playlist methods as
|
||||||
|
`Window.mediaItem` in `CastTimeline`
|
||||||
|
([#25](https://github.com/androidx/media/issues/25),
|
||||||
|
[#8212](https://github.com/google/ExoPlayer/issues/8212)).
|
||||||
|
* Support `Player.getMetadata()` and `Listener.onMediaMetadataChanged()`
|
||||||
|
with `CastPlayer` ([#25](https://github.com/androidx/media/issues/25)).
|
||||||
|
|
||||||
### 2.18.0 (2022-06-16)
|
### 2.18.0 (2022-06-16)
|
||||||
|
|
||||||
This release corresponds to the
|
This release corresponds to the
|
||||||
|
|
@ -37,7 +80,9 @@ This release corresponds to the
|
||||||
* Rename `TracksInfo` to `Tracks` and `TracksInfo.TrackGroupInfo` to
|
* Rename `TracksInfo` to `Tracks` and `TracksInfo.TrackGroupInfo` to
|
||||||
`Tracks.Group`. `Player.getCurrentTracksInfo` and
|
`Tracks.Group`. `Player.getCurrentTracksInfo` and
|
||||||
`Player.Listener.onTracksInfoChanged` have also been renamed to
|
`Player.Listener.onTracksInfoChanged` have also been renamed to
|
||||||
`Player.getCurrentTracks` and `Player.Listener.onTracksChanged`.
|
`Player.getCurrentTracks` and `Player.Listener.onTracksChanged`. This
|
||||||
|
includes 'un-deprecating' the `Player.Listener.onTracksChanged` method
|
||||||
|
name, but with different parameter types.
|
||||||
* Change `DefaultTrackSelector.buildUponParameters` and
|
* Change `DefaultTrackSelector.buildUponParameters` and
|
||||||
`DefaultTrackSelector.Parameters.buildUpon` to return
|
`DefaultTrackSelector.Parameters.buildUpon` to return
|
||||||
`DefaultTrackSelector.Parameters.Builder` instead of the deprecated
|
`DefaultTrackSelector.Parameters.Builder` instead of the deprecated
|
||||||
|
|
@ -91,6 +136,8 @@ This release corresponds to the
|
||||||
* Remove `RawCcExtractor`, which was only used to handle a Google-internal
|
* Remove `RawCcExtractor`, which was only used to handle a Google-internal
|
||||||
subtitle format.
|
subtitle format.
|
||||||
* Extractors:
|
* Extractors:
|
||||||
|
* Add support for AVI
|
||||||
|
([#2092](https://github.com/google/ExoPlayer/issues/2092)).
|
||||||
* Matroska: Parse `DiscardPadding` for Opus tracks.
|
* Matroska: Parse `DiscardPadding` for Opus tracks.
|
||||||
* MP4: Parse bitrates from `esds` boxes.
|
* MP4: Parse bitrates from `esds` boxes.
|
||||||
* Ogg: Allow duplicate Opus ID and comment headers
|
* Ogg: Allow duplicate Opus ID and comment headers
|
||||||
|
|
@ -140,6 +187,8 @@ This release corresponds to the
|
||||||
of `DefaultCompositeSequenceableLoaderFactory` can be passed explicitly
|
of `DefaultCompositeSequenceableLoaderFactory` can be passed explicitly
|
||||||
if required.
|
if required.
|
||||||
* RTSP:
|
* RTSP:
|
||||||
|
* Add RTP reader for H263
|
||||||
|
([#63](https://github.com/androidx/media/pull/63)).
|
||||||
* Add RTP reader for MPEG4
|
* Add RTP reader for MPEG4
|
||||||
([#35](https://github.com/androidx/media/pull/35)).
|
([#35](https://github.com/androidx/media/pull/35)).
|
||||||
* Add RTP reader for HEVC
|
* Add RTP reader for HEVC
|
||||||
|
|
@ -172,10 +221,11 @@ This release corresponds to the
|
||||||
AndroidStudio's gradle sync to fail
|
AndroidStudio's gradle sync to fail
|
||||||
([#9933](https://github.com/google/ExoPlayer/issues/9933)).
|
([#9933](https://github.com/google/ExoPlayer/issues/9933)).
|
||||||
* Remove deprecated symbols:
|
* Remove deprecated symbols:
|
||||||
* Remove `Player.Listener.onTracksChanged`. Use
|
* Remove `Player.Listener.onTracksChanged(TrackGroupArray,
|
||||||
`Player.Listener.onTracksInfoChanged` instead.
|
TrackSelectionArray)`. Use `Player.Listener.onTracksChanged(Tracks)`
|
||||||
|
instead.
|
||||||
* Remove `Player.getCurrentTrackGroups` and
|
* Remove `Player.getCurrentTrackGroups` and
|
||||||
`Player.getCurrentTrackSelections`. Use `Player.getCurrentTracksInfo`
|
`Player.getCurrentTrackSelections`. Use `Player.getCurrentTracks`
|
||||||
instead. You can also continue to use `ExoPlayer.getCurrentTrackGroups`
|
instead. You can also continue to use `ExoPlayer.getCurrentTrackGroups`
|
||||||
and `ExoPlayer.getCurrentTrackSelections`, although these methods remain
|
and `ExoPlayer.getCurrentTrackSelections`, although these methods remain
|
||||||
deprecated.
|
deprecated.
|
||||||
|
|
@ -371,7 +421,7 @@ This release corresponds to the
|
||||||
when creating `PendingIntent`s
|
when creating `PendingIntent`s
|
||||||
([#9528](https://github.com/google/ExoPlayer/issues/9528)).
|
([#9528](https://github.com/google/ExoPlayer/issues/9528)).
|
||||||
* Remove deprecated symbols:
|
* Remove deprecated symbols:
|
||||||
* Remove `Player.EventLister`. Use `Player.Listener` instead.
|
* Remove `Player.EventListener`. Use `Player.Listener` instead.
|
||||||
* Remove `MediaSourceFactory.setDrmSessionManager`,
|
* Remove `MediaSourceFactory.setDrmSessionManager`,
|
||||||
`MediaSourceFactory.setDrmHttpDataSourceFactory`, and
|
`MediaSourceFactory.setDrmHttpDataSourceFactory`, and
|
||||||
`MediaSourceFactory.setDrmUserAgent`. Use
|
`MediaSourceFactory.setDrmUserAgent`. Use
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
project.ext {
|
project.ext {
|
||||||
// ExoPlayer version and version code.
|
// ExoPlayer version and version code.
|
||||||
releaseVersion = '2.18.0'
|
releaseVersion = '2.18.1'
|
||||||
releaseVersionCode = 2_018_000
|
releaseVersionCode = 2_018_001
|
||||||
minSdkVersion = 16
|
minSdkVersion = 16
|
||||||
appTargetSdkVersion = 29
|
appTargetSdkVersion = 29
|
||||||
// Upgrading this requires [Internal ref: b/193254928] to be fixed, or some
|
// Upgrading this requires [Internal ref: b/193254928] to be fixed, or some
|
||||||
|
|
|
||||||
|
|
@ -78,6 +78,12 @@
|
||||||
<data android:scheme="file"/>
|
<data android:scheme="file"/>
|
||||||
<data android:scheme="ssai"/>
|
<data android:scheme="ssai"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.google.android.exoplayer.demo.action.VIEW"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<data android:scheme="content"/>
|
||||||
|
<data android:mimeType="*/*"/>
|
||||||
|
</intent-filter>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="com.google.android.exoplayer.demo.action.VIEW_LIST"/>
|
<action android:name="com.google.android.exoplayer.demo.action.VIEW_LIST"/>
|
||||||
<category android:name="android.intent.category.DEFAULT"/>
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,12 @@
|
||||||
<data android:scheme="asset"/>
|
<data android:scheme="asset"/>
|
||||||
<data android:scheme="file"/>
|
<data android:scheme="file"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.google.android.exoplayer.surfacedemo.action.VIEW"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<data android:scheme="content"/>
|
||||||
|
<data android:mimeType="*/*"/>
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,12 @@
|
||||||
<data android:scheme="asset"/>
|
<data android:scheme="asset"/>
|
||||||
<data android:scheme="file"/>
|
<data android:scheme="file"/>
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="com.google.android.exoplayer2.transformerdemo.action.VIEW"/>
|
||||||
|
<category android:name="android.intent.category.DEFAULT"/>
|
||||||
|
<data android:scheme="content"/>
|
||||||
|
<data android:mimeType="*/*"/>
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name=".TransformerActivity"
|
<activity android:name=".TransformerActivity"
|
||||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
|
android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize|uiMode"
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -709,6 +709,7 @@
|
||||||
<li><a href="com/google/android/exoplayer2/source/LoadEventInfo.html" title="class in com.google.android.exoplayer2.source">LoadEventInfo</a></li>
|
<li><a href="com/google/android/exoplayer2/source/LoadEventInfo.html" title="class in com.google.android.exoplayer2.source">LoadEventInfo</a></li>
|
||||||
<li><a href="com/google/android/exoplayer2/drm/LocalMediaDrmCallback.html" title="class in com.google.android.exoplayer2.drm">LocalMediaDrmCallback</a></li>
|
<li><a href="com/google/android/exoplayer2/drm/LocalMediaDrmCallback.html" title="class in com.google.android.exoplayer2.drm">LocalMediaDrmCallback</a></li>
|
||||||
<li><a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></li>
|
<li><a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></li>
|
||||||
|
<li><a href="com/google/android/exoplayer2/util/Log.Logger.html" title="interface in com.google.android.exoplayer2.util"><span class="interfaceName">Log.Logger</span></a></li>
|
||||||
<li><a href="com/google/android/exoplayer2/util/Log.LogLevel.html" title="annotation in com.google.android.exoplayer2.util">Log.LogLevel</a></li>
|
<li><a href="com/google/android/exoplayer2/util/Log.LogLevel.html" title="annotation in com.google.android.exoplayer2.util">Log.LogLevel</a></li>
|
||||||
<li><a href="com/google/android/exoplayer2/util/LongArray.html" title="class in com.google.android.exoplayer2.util">LongArray</a></li>
|
<li><a href="com/google/android/exoplayer2/util/LongArray.html" title="class in com.google.android.exoplayer2.util">LongArray</a></li>
|
||||||
<li><a href="com/google/android/exoplayer2/source/LoopingMediaSource.html" title="class in com.google.android.exoplayer2.source">LoopingMediaSource</a></li>
|
<li><a href="com/google/android/exoplayer2/source/LoopingMediaSource.html" title="class in com.google.android.exoplayer2.source">LoopingMediaSource</a></li>
|
||||||
|
|
@ -833,7 +834,6 @@
|
||||||
<li><a href="com/google/android/exoplayer2/NoSampleRenderer.html" title="class in com.google.android.exoplayer2">NoSampleRenderer</a></li>
|
<li><a href="com/google/android/exoplayer2/NoSampleRenderer.html" title="class in com.google.android.exoplayer2">NoSampleRenderer</a></li>
|
||||||
<li><a href="com/google/android/exoplayer2/util/NotificationUtil.html" title="class in com.google.android.exoplayer2.util">NotificationUtil</a></li>
|
<li><a href="com/google/android/exoplayer2/util/NotificationUtil.html" title="class in com.google.android.exoplayer2.util">NotificationUtil</a></li>
|
||||||
<li><a href="com/google/android/exoplayer2/util/NotificationUtil.Importance.html" title="annotation in com.google.android.exoplayer2.util">NotificationUtil.Importance</a></li>
|
<li><a href="com/google/android/exoplayer2/util/NotificationUtil.Importance.html" title="annotation in com.google.android.exoplayer2.util">NotificationUtil.Importance</a></li>
|
||||||
<li><a href="com/google/android/exoplayer2/testutil/NoUidTimeline.html" title="class in com.google.android.exoplayer2.testutil">NoUidTimeline</a></li>
|
|
||||||
<li><a href="com/google/android/exoplayer2/drm/OfflineLicenseHelper.html" title="class in com.google.android.exoplayer2.drm">OfflineLicenseHelper</a></li>
|
<li><a href="com/google/android/exoplayer2/drm/OfflineLicenseHelper.html" title="class in com.google.android.exoplayer2.drm">OfflineLicenseHelper</a></li>
|
||||||
<li><a href="com/google/android/exoplayer2/extractor/ogg/OggExtractor.html" title="class in com.google.android.exoplayer2.extractor.ogg">OggExtractor</a></li>
|
<li><a href="com/google/android/exoplayer2/extractor/ogg/OggExtractor.html" title="class in com.google.android.exoplayer2.extractor.ogg">OggExtractor</a></li>
|
||||||
<li><a href="com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.html" title="class in com.google.android.exoplayer2.ext.okhttp">OkHttpDataSource</a></li>
|
<li><a href="com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.html" title="class in com.google.android.exoplayer2.ext.okhttp">OkHttpDataSource</a></li>
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -3049,6 +3049,20 @@ static final int COMMAND_SEEK_TO_WINDOW</pre>
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<a id="COMMAND_SET_MEDIA_ITEM">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList">
|
||||||
|
<h4>COMMAND_SET_MEDIA_ITEM</h4>
|
||||||
|
<pre>static final int COMMAND_SET_MEDIA_ITEM</pre>
|
||||||
|
<div class="block">Command to set a <a href="MediaItem.html" title="class in com.google.android.exoplayer2"><code>MediaItem</code></a>.</div>
|
||||||
|
<dl>
|
||||||
|
<dt><span class="seeLabel">See Also:</span></dt>
|
||||||
|
<dd><a href="../../../../constant-values.html#com.google.android.exoplayer2.Player.COMMAND_SET_MEDIA_ITEM">Constant Field Values</a></dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<a id="COMMAND_CHANGE_MEDIA_ITEMS">
|
<a id="COMMAND_CHANGE_MEDIA_ITEMS">
|
||||||
<!-- -->
|
<!-- -->
|
||||||
</a>
|
</a>
|
||||||
|
|
@ -3203,20 +3217,6 @@ static final int COMMAND_SEEK_TO_WINDOW</pre>
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a id="COMMAND_SET_MEDIA_ITEM">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList">
|
|
||||||
<h4>COMMAND_SET_MEDIA_ITEM</h4>
|
|
||||||
<pre>static final int COMMAND_SET_MEDIA_ITEM</pre>
|
|
||||||
<div class="block">Command to set a <a href="MediaItem.html" title="class in com.google.android.exoplayer2"><code>MediaItem</code></a>.</div>
|
|
||||||
<dl>
|
|
||||||
<dt><span class="seeLabel">See Also:</span></dt>
|
|
||||||
<dd><a href="../../../../constant-values.html#com.google.android.exoplayer2.Player.COMMAND_SET_MEDIA_ITEM">Constant Field Values</a></dd>
|
|
||||||
</dl>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<a id="COMMAND_INVALID">
|
<a id="COMMAND_INVALID">
|
||||||
<!-- -->
|
<!-- -->
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -500,7 +500,8 @@ public final byte[] data</pre>
|
||||||
<ul class="blockList">
|
<ul class="blockList">
|
||||||
<li class="blockList">
|
<li class="blockList">
|
||||||
<h4>copyWithData</h4>
|
<h4>copyWithData</h4>
|
||||||
<pre class="methodSignature">public <a href="DrmInitData.SchemeData.html" title="class in com.google.android.exoplayer2.drm">DrmInitData.SchemeData</a> copyWithData​(@Nullable
|
<pre class="methodSignature">@CheckResult
|
||||||
|
public <a href="DrmInitData.SchemeData.html" title="class in com.google.android.exoplayer2.drm">DrmInitData.SchemeData</a> copyWithData​(@Nullable
|
||||||
byte[] data)</pre>
|
byte[] data)</pre>
|
||||||
<div class="block">Returns a copy of this instance with the specified data.</div>
|
<div class="block">Returns a copy of this instance with the specified data.</div>
|
||||||
<dl>
|
<dl>
|
||||||
|
|
|
||||||
|
|
@ -517,7 +517,8 @@ public static <a href="DrmInitData.html" title="class in com.google.android
|
||||||
<ul class="blockList">
|
<ul class="blockList">
|
||||||
<li class="blockList">
|
<li class="blockList">
|
||||||
<h4>copyWithSchemeType</h4>
|
<h4>copyWithSchemeType</h4>
|
||||||
<pre class="methodSignature">public <a href="DrmInitData.html" title="class in com.google.android.exoplayer2.drm">DrmInitData</a> copyWithSchemeType​(@Nullable
|
<pre class="methodSignature">@CheckResult
|
||||||
|
public <a href="DrmInitData.html" title="class in com.google.android.exoplayer2.drm">DrmInitData</a> copyWithSchemeType​(@Nullable
|
||||||
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> schemeType)</pre>
|
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> schemeType)</pre>
|
||||||
<div class="block">Returns a copy with the specified protection scheme type.</div>
|
<div class="block">Returns a copy with the specified protection scheme type.</div>
|
||||||
<dl>
|
<dl>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
catch(err) {
|
catch(err) {
|
||||||
}
|
}
|
||||||
//-->
|
//-->
|
||||||
var data = {"i0":10,"i1":10,"i2":10,"i3":10,"i4":10,"i5":10,"i6":10,"i7":10,"i8":10,"i9":10,"i10":10,"i11":10,"i12":10,"i13":10,"i14":10,"i15":10,"i16":10,"i17":10,"i18":10,"i19":10,"i20":10,"i21":10,"i22":10,"i23":10,"i24":10,"i25":10,"i26":10,"i27":10,"i28":10,"i29":10,"i30":10,"i31":10,"i32":10,"i33":10,"i34":10,"i35":10,"i36":10,"i37":10,"i38":10,"i39":10,"i40":10,"i41":10,"i42":10,"i43":10,"i44":10,"i45":10,"i46":10,"i47":10,"i48":10,"i49":10,"i50":10,"i51":10,"i52":10,"i53":10,"i54":10,"i55":10,"i56":10,"i57":10,"i58":10,"i59":10,"i60":10,"i61":10,"i62":10,"i63":10,"i64":10,"i65":10,"i66":10,"i67":10,"i68":10,"i69":10,"i70":42};
|
var data = {"i0":10,"i1":10,"i2":10,"i3":10,"i4":10,"i5":10,"i6":10,"i7":10,"i8":10,"i9":10,"i10":10,"i11":10,"i12":10,"i13":10,"i14":10,"i15":10,"i16":10,"i17":10,"i18":10,"i19":10,"i20":10,"i21":10,"i22":10,"i23":10,"i24":10,"i25":10,"i26":10,"i27":10,"i28":10,"i29":10,"i30":10,"i31":10,"i32":10,"i33":10,"i34":10,"i35":10,"i36":10,"i37":10,"i38":10,"i39":10,"i40":10,"i41":10,"i42":10,"i43":10,"i44":10,"i45":10,"i46":10,"i47":10,"i48":10,"i49":10,"i50":10,"i51":10,"i52":10,"i53":10,"i54":10,"i55":10,"i56":10,"i57":10,"i58":10,"i59":10,"i60":10,"i61":10,"i62":10,"i63":10,"i64":10,"i65":10,"i66":10,"i67":10,"i68":10,"i69":10,"i70":10,"i71":42};
|
||||||
var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"],32:["t6","Deprecated Methods"]};
|
var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"],32:["t6","Deprecated Methods"]};
|
||||||
var altColor = "altColor";
|
var altColor = "altColor";
|
||||||
var rowColor = "rowColor";
|
var rowColor = "rowColor";
|
||||||
|
|
@ -477,20 +477,25 @@ extends <a href="../../BasePlayer.html" title="class in com.google.android.exopl
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i28" class="altColor">
|
<tr id="i28" class="altColor">
|
||||||
|
<td class="colFirst"><code><a href="../../MediaMetadata.html" title="class in com.google.android.exoplayer2">MediaMetadata</a></code></td>
|
||||||
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getMediaMetadataInternal()">getMediaMetadataInternal</a></span>()</code></th>
|
||||||
|
<td class="colLast"> </td>
|
||||||
|
</tr>
|
||||||
|
<tr id="i29" class="rowColor">
|
||||||
<td class="colFirst"><code><a href="../../PlaybackParameters.html" title="class in com.google.android.exoplayer2">PlaybackParameters</a></code></td>
|
<td class="colFirst"><code><a href="../../PlaybackParameters.html" title="class in com.google.android.exoplayer2">PlaybackParameters</a></code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlaybackParameters()">getPlaybackParameters</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlaybackParameters()">getPlaybackParameters</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Returns the currently active playback parameters.</div>
|
<div class="block">Returns the currently active playback parameters.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i29" class="rowColor">
|
<tr id="i30" class="altColor">
|
||||||
<td class="colFirst"><code>@com.google.android.exoplayer2.Player.State int</code></td>
|
<td class="colFirst"><code>@com.google.android.exoplayer2.Player.State int</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlaybackState()">getPlaybackState</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlaybackState()">getPlaybackState</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Returns the current <a href="../../Player.State.html" title="annotation in com.google.android.exoplayer2"><code>playback state</code></a> of the player.</div>
|
<div class="block">Returns the current <a href="../../Player.State.html" title="annotation in com.google.android.exoplayer2"><code>playback state</code></a> of the player.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i30" class="altColor">
|
<tr id="i31" class="rowColor">
|
||||||
<td class="colFirst"><code>@com.google.android.exoplayer2.Player.PlaybackSuppressionReason int</code></td>
|
<td class="colFirst"><code>@com.google.android.exoplayer2.Player.PlaybackSuppressionReason int</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlaybackSuppressionReason()">getPlaybackSuppressionReason</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlaybackSuppressionReason()">getPlaybackSuppressionReason</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
|
|
@ -498,119 +503,119 @@ extends <a href="../../BasePlayer.html" title="class in com.google.android.exopl
|
||||||
true</code>, or <a href="../../Player.html#PLAYBACK_SUPPRESSION_REASON_NONE"><code>Player.PLAYBACK_SUPPRESSION_REASON_NONE</code></a> if playback is not suppressed.</div>
|
true</code>, or <a href="../../Player.html#PLAYBACK_SUPPRESSION_REASON_NONE"><code>Player.PLAYBACK_SUPPRESSION_REASON_NONE</code></a> if playback is not suppressed.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i31" class="rowColor">
|
<tr id="i32" class="altColor">
|
||||||
<td class="colFirst"><code><a href="../../PlaybackException.html" title="class in com.google.android.exoplayer2">PlaybackException</a></code></td>
|
<td class="colFirst"><code><a href="../../PlaybackException.html" title="class in com.google.android.exoplayer2">PlaybackException</a></code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlayerError()">getPlayerError</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlayerError()">getPlayerError</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Returns the error that caused playback to fail.</div>
|
<div class="block">Returns the error that caused playback to fail.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i32" class="altColor">
|
<tr id="i33" class="rowColor">
|
||||||
<td class="colFirst"><code><a href="../../MediaMetadata.html" title="class in com.google.android.exoplayer2">MediaMetadata</a></code></td>
|
<td class="colFirst"><code><a href="../../MediaMetadata.html" title="class in com.google.android.exoplayer2">MediaMetadata</a></code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlaylistMetadata()">getPlaylistMetadata</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlaylistMetadata()">getPlaylistMetadata</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Returns the playlist <a href="../../MediaMetadata.html" title="class in com.google.android.exoplayer2"><code>MediaMetadata</code></a>, as set by <a href="../../Player.html#setPlaylistMetadata(com.google.android.exoplayer2.MediaMetadata)"><code>Player.setPlaylistMetadata(MediaMetadata)</code></a>, or <a href="../../MediaMetadata.html#EMPTY"><code>MediaMetadata.EMPTY</code></a> if not supported.</div>
|
<div class="block">Returns the playlist <a href="../../MediaMetadata.html" title="class in com.google.android.exoplayer2"><code>MediaMetadata</code></a>, as set by <a href="../../Player.html#setPlaylistMetadata(com.google.android.exoplayer2.MediaMetadata)"><code>Player.setPlaylistMetadata(MediaMetadata)</code></a>, or <a href="../../MediaMetadata.html#EMPTY"><code>MediaMetadata.EMPTY</code></a> if not supported.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i33" class="rowColor">
|
<tr id="i34" class="altColor">
|
||||||
<td class="colFirst"><code>boolean</code></td>
|
<td class="colFirst"><code>boolean</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlayWhenReady()">getPlayWhenReady</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlayWhenReady()">getPlayWhenReady</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Whether playback will proceed when <a href="../../Player.html#getPlaybackState()"><code>Player.getPlaybackState()</code></a> == <a href="../../Player.html#STATE_READY"><code>Player.STATE_READY</code></a>.</div>
|
<div class="block">Whether playback will proceed when <a href="../../Player.html#getPlaybackState()"><code>Player.getPlaybackState()</code></a> == <a href="../../Player.html#STATE_READY"><code>Player.STATE_READY</code></a>.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i34" class="altColor">
|
<tr id="i35" class="rowColor">
|
||||||
<td class="colFirst"><code>@com.google.android.exoplayer2.Player.RepeatMode int</code></td>
|
<td class="colFirst"><code>@com.google.android.exoplayer2.Player.RepeatMode int</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getRepeatMode()">getRepeatMode</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getRepeatMode()">getRepeatMode</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Returns the current <a href="../../Player.RepeatMode.html" title="annotation in com.google.android.exoplayer2"><code>Player.RepeatMode</code></a> used for playback.</div>
|
<div class="block">Returns the current <a href="../../Player.RepeatMode.html" title="annotation in com.google.android.exoplayer2"><code>Player.RepeatMode</code></a> used for playback.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i35" class="rowColor">
|
<tr id="i36" class="altColor">
|
||||||
<td class="colFirst"><code>long</code></td>
|
<td class="colFirst"><code>long</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getSeekBackIncrement()">getSeekBackIncrement</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getSeekBackIncrement()">getSeekBackIncrement</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Returns the <a href="../../Player.html#seekBack()"><code>Player.seekBack()</code></a> increment.</div>
|
<div class="block">Returns the <a href="../../Player.html#seekBack()"><code>Player.seekBack()</code></a> increment.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i36" class="altColor">
|
<tr id="i37" class="rowColor">
|
||||||
<td class="colFirst"><code>long</code></td>
|
<td class="colFirst"><code>long</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getSeekForwardIncrement()">getSeekForwardIncrement</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getSeekForwardIncrement()">getSeekForwardIncrement</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Returns the <a href="../../Player.html#seekForward()"><code>Player.seekForward()</code></a> increment.</div>
|
<div class="block">Returns the <a href="../../Player.html#seekForward()"><code>Player.seekForward()</code></a> increment.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i37" class="rowColor">
|
<tr id="i38" class="altColor">
|
||||||
<td class="colFirst"><code>boolean</code></td>
|
<td class="colFirst"><code>boolean</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getShuffleModeEnabled()">getShuffleModeEnabled</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getShuffleModeEnabled()">getShuffleModeEnabled</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Returns whether shuffling of media items is enabled.</div>
|
<div class="block">Returns whether shuffling of media items is enabled.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i38" class="altColor">
|
<tr id="i39" class="rowColor">
|
||||||
<td class="colFirst"><code>long</code></td>
|
<td class="colFirst"><code>long</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getTotalBufferedDuration()">getTotalBufferedDuration</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getTotalBufferedDuration()">getTotalBufferedDuration</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Returns an estimate of the total buffered duration from the current position, in milliseconds.</div>
|
<div class="block">Returns an estimate of the total buffered duration from the current position, in milliseconds.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i39" class="rowColor">
|
<tr id="i40" class="altColor">
|
||||||
<td class="colFirst"><code><a href="../../trackselection/TrackSelectionParameters.html" title="class in com.google.android.exoplayer2.trackselection">TrackSelectionParameters</a></code></td>
|
<td class="colFirst"><code><a href="../../trackselection/TrackSelectionParameters.html" title="class in com.google.android.exoplayer2.trackselection">TrackSelectionParameters</a></code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getTrackSelectionParameters()">getTrackSelectionParameters</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getTrackSelectionParameters()">getTrackSelectionParameters</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Returns the parameters constraining the track selection.</div>
|
<div class="block">Returns the parameters constraining the track selection.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i40" class="altColor">
|
<tr id="i41" class="rowColor">
|
||||||
<td class="colFirst"><code><a href="../../video/VideoSize.html" title="class in com.google.android.exoplayer2.video">VideoSize</a></code></td>
|
<td class="colFirst"><code><a href="../../video/VideoSize.html" title="class in com.google.android.exoplayer2.video">VideoSize</a></code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getVideoSize()">getVideoSize</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getVideoSize()">getVideoSize</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">This method is not supported and returns <a href="../../video/VideoSize.html#UNKNOWN"><code>VideoSize.UNKNOWN</code></a>.</div>
|
<div class="block">This method is not supported and returns <a href="../../video/VideoSize.html#UNKNOWN"><code>VideoSize.UNKNOWN</code></a>.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i41" class="rowColor">
|
<tr id="i42" class="altColor">
|
||||||
<td class="colFirst"><code>float</code></td>
|
<td class="colFirst"><code>float</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getVolume()">getVolume</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getVolume()">getVolume</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">This method is not supported and returns 1.</div>
|
<div class="block">This method is not supported and returns 1.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i42" class="altColor">
|
<tr id="i43" class="rowColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#increaseDeviceVolume()">increaseDeviceVolume</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#increaseDeviceVolume()">increaseDeviceVolume</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">This method is not supported and does nothing.</div>
|
<div class="block">This method is not supported and does nothing.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i43" class="rowColor">
|
<tr id="i44" class="altColor">
|
||||||
<td class="colFirst"><code>boolean</code></td>
|
<td class="colFirst"><code>boolean</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#isCastSessionAvailable()">isCastSessionAvailable</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#isCastSessionAvailable()">isCastSessionAvailable</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Returns whether a cast session is available.</div>
|
<div class="block">Returns whether a cast session is available.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i44" class="altColor">
|
<tr id="i45" class="rowColor">
|
||||||
<td class="colFirst"><code>boolean</code></td>
|
<td class="colFirst"><code>boolean</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#isDeviceMuted()">isDeviceMuted</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#isDeviceMuted()">isDeviceMuted</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">This method is not supported and always returns <code>false</code>.</div>
|
<div class="block">This method is not supported and always returns <code>false</code>.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i45" class="rowColor">
|
<tr id="i46" class="altColor">
|
||||||
<td class="colFirst"><code>boolean</code></td>
|
<td class="colFirst"><code>boolean</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#isLoading()">isLoading</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#isLoading()">isLoading</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Whether the player is currently loading the source.</div>
|
<div class="block">Whether the player is currently loading the source.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i46" class="altColor">
|
<tr id="i47" class="rowColor">
|
||||||
<td class="colFirst"><code>boolean</code></td>
|
<td class="colFirst"><code>boolean</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#isPlayingAd()">isPlayingAd</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#isPlayingAd()">isPlayingAd</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Returns whether the player is currently playing an ad.</div>
|
<div class="block">Returns whether the player is currently playing an ad.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i47" class="rowColor">
|
<tr id="i48" class="altColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#moveMediaItems(int,int,int)">moveMediaItems</a></span>​(int fromIndex,
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#moveMediaItems(int,int,int)">moveMediaItems</a></span>​(int fromIndex,
|
||||||
int toIndex,
|
int toIndex,
|
||||||
|
|
@ -619,28 +624,28 @@ extends <a href="../../BasePlayer.html" title="class in com.google.android.exopl
|
||||||
<div class="block">Moves the media item range to the new index.</div>
|
<div class="block">Moves the media item range to the new index.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i48" class="altColor">
|
<tr id="i49" class="rowColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#prepare()">prepare</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#prepare()">prepare</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Prepares the player.</div>
|
<div class="block">Prepares the player.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i49" class="rowColor">
|
<tr id="i50" class="altColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#release()">release</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#release()">release</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Releases the player.</div>
|
<div class="block">Releases the player.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i50" class="altColor">
|
<tr id="i51" class="rowColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#removeListener(com.google.android.exoplayer2.Player.Listener)">removeListener</a></span>​(<a href="../../Player.Listener.html" title="interface in com.google.android.exoplayer2">Player.Listener</a> listener)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#removeListener(com.google.android.exoplayer2.Player.Listener)">removeListener</a></span>​(<a href="../../Player.Listener.html" title="interface in com.google.android.exoplayer2">Player.Listener</a> listener)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Unregister a listener registered through <a href="../../Player.html#addListener(com.google.android.exoplayer2.Player.Listener)"><code>Player.addListener(Listener)</code></a>.</div>
|
<div class="block">Unregister a listener registered through <a href="../../Player.html#addListener(com.google.android.exoplayer2.Player.Listener)"><code>Player.addListener(Listener)</code></a>.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i51" class="rowColor">
|
<tr id="i52" class="altColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#removeMediaItems(int,int)">removeMediaItems</a></span>​(int fromIndex,
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#removeMediaItems(int,int)">removeMediaItems</a></span>​(int fromIndex,
|
||||||
int toIndex)</code></th>
|
int toIndex)</code></th>
|
||||||
|
|
@ -648,7 +653,7 @@ extends <a href="../../BasePlayer.html" title="class in com.google.android.exopl
|
||||||
<div class="block">Removes a range of media items from the playlist.</div>
|
<div class="block">Removes a range of media items from the playlist.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i52" class="altColor">
|
<tr id="i53" class="rowColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#seekTo(int,long)">seekTo</a></span>​(int mediaItemIndex,
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#seekTo(int,long)">seekTo</a></span>​(int mediaItemIndex,
|
||||||
long positionMs)</code></th>
|
long positionMs)</code></th>
|
||||||
|
|
@ -656,21 +661,21 @@ extends <a href="../../BasePlayer.html" title="class in com.google.android.exopl
|
||||||
<div class="block">Seeks to a position specified in milliseconds in the specified <a href="../../MediaItem.html" title="class in com.google.android.exoplayer2"><code>MediaItem</code></a>.</div>
|
<div class="block">Seeks to a position specified in milliseconds in the specified <a href="../../MediaItem.html" title="class in com.google.android.exoplayer2"><code>MediaItem</code></a>.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i53" class="rowColor">
|
<tr id="i54" class="altColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setDeviceMuted(boolean)">setDeviceMuted</a></span>​(boolean muted)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setDeviceMuted(boolean)">setDeviceMuted</a></span>​(boolean muted)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">This method is not supported and does nothing.</div>
|
<div class="block">This method is not supported and does nothing.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i54" class="altColor">
|
<tr id="i55" class="rowColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setDeviceVolume(int)">setDeviceVolume</a></span>​(int volume)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setDeviceVolume(int)">setDeviceVolume</a></span>​(int volume)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">This method is not supported and does nothing.</div>
|
<div class="block">This method is not supported and does nothing.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i55" class="rowColor">
|
<tr id="i56" class="altColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setMediaItems(java.util.List,boolean)">setMediaItems</a></span>​(<a href="https://developer.android.com/reference/java/util/List.html" title="class or interface in java.util" class="externalLink">List</a><<a href="../../MediaItem.html" title="class in com.google.android.exoplayer2" target="_top">MediaItem</a>> mediaItems,
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setMediaItems(java.util.List,boolean)">setMediaItems</a></span>​(<a href="https://developer.android.com/reference/java/util/List.html" title="class or interface in java.util" class="externalLink">List</a><<a href="../../MediaItem.html" title="class in com.google.android.exoplayer2" target="_top">MediaItem</a>> mediaItems,
|
||||||
boolean resetPosition)</code></th>
|
boolean resetPosition)</code></th>
|
||||||
|
|
@ -678,7 +683,7 @@ extends <a href="../../BasePlayer.html" title="class in com.google.android.exopl
|
||||||
<div class="block">Clears the playlist and adds the specified <a href="../../MediaItem.html" title="class in com.google.android.exoplayer2"><code>MediaItems</code></a>.</div>
|
<div class="block">Clears the playlist and adds the specified <a href="../../MediaItem.html" title="class in com.google.android.exoplayer2"><code>MediaItems</code></a>.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i56" class="altColor">
|
<tr id="i57" class="rowColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setMediaItems(java.util.List,int,long)">setMediaItems</a></span>​(<a href="https://developer.android.com/reference/java/util/List.html" title="class or interface in java.util" class="externalLink">List</a><<a href="../../MediaItem.html" title="class in com.google.android.exoplayer2" target="_top">MediaItem</a>> mediaItems,
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setMediaItems(java.util.List,int,long)">setMediaItems</a></span>​(<a href="https://developer.android.com/reference/java/util/List.html" title="class or interface in java.util" class="externalLink">List</a><<a href="../../MediaItem.html" title="class in com.google.android.exoplayer2" target="_top">MediaItem</a>> mediaItems,
|
||||||
int startIndex,
|
int startIndex,
|
||||||
|
|
@ -687,98 +692,98 @@ extends <a href="../../BasePlayer.html" title="class in com.google.android.exopl
|
||||||
<div class="block">Clears the playlist and adds the specified <a href="../../MediaItem.html" title="class in com.google.android.exoplayer2"><code>MediaItems</code></a>.</div>
|
<div class="block">Clears the playlist and adds the specified <a href="../../MediaItem.html" title="class in com.google.android.exoplayer2"><code>MediaItems</code></a>.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i57" class="rowColor">
|
<tr id="i58" class="altColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setPlaybackParameters(com.google.android.exoplayer2.PlaybackParameters)">setPlaybackParameters</a></span>​(<a href="../../PlaybackParameters.html" title="class in com.google.android.exoplayer2">PlaybackParameters</a> playbackParameters)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setPlaybackParameters(com.google.android.exoplayer2.PlaybackParameters)">setPlaybackParameters</a></span>​(<a href="../../PlaybackParameters.html" title="class in com.google.android.exoplayer2">PlaybackParameters</a> playbackParameters)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Attempts to set the playback parameters.</div>
|
<div class="block">Attempts to set the playback parameters.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i58" class="altColor">
|
<tr id="i59" class="rowColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setPlaylistMetadata(com.google.android.exoplayer2.MediaMetadata)">setPlaylistMetadata</a></span>​(<a href="../../MediaMetadata.html" title="class in com.google.android.exoplayer2">MediaMetadata</a> mediaMetadata)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setPlaylistMetadata(com.google.android.exoplayer2.MediaMetadata)">setPlaylistMetadata</a></span>​(<a href="../../MediaMetadata.html" title="class in com.google.android.exoplayer2">MediaMetadata</a> mediaMetadata)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">This method is not supported and does nothing.</div>
|
<div class="block">This method is not supported and does nothing.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i59" class="rowColor">
|
<tr id="i60" class="altColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setPlayWhenReady(boolean)">setPlayWhenReady</a></span>​(boolean playWhenReady)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setPlayWhenReady(boolean)">setPlayWhenReady</a></span>​(boolean playWhenReady)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Sets whether playback should proceed when <a href="../../Player.html#getPlaybackState()"><code>Player.getPlaybackState()</code></a> == <a href="../../Player.html#STATE_READY"><code>Player.STATE_READY</code></a>.</div>
|
<div class="block">Sets whether playback should proceed when <a href="../../Player.html#getPlaybackState()"><code>Player.getPlaybackState()</code></a> == <a href="../../Player.html#STATE_READY"><code>Player.STATE_READY</code></a>.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i60" class="altColor">
|
<tr id="i61" class="rowColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setRepeatMode(@com.google.android.exoplayer2.Player.RepeatModeint)">setRepeatMode</a></span>​(@com.google.android.exoplayer2.Player.RepeatMode int repeatMode)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setRepeatMode(@com.google.android.exoplayer2.Player.RepeatModeint)">setRepeatMode</a></span>​(@com.google.android.exoplayer2.Player.RepeatMode int repeatMode)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Sets the <a href="../../Player.RepeatMode.html" title="annotation in com.google.android.exoplayer2"><code>Player.RepeatMode</code></a> to be used for playback.</div>
|
<div class="block">Sets the <a href="../../Player.RepeatMode.html" title="annotation in com.google.android.exoplayer2"><code>Player.RepeatMode</code></a> to be used for playback.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i61" class="rowColor">
|
<tr id="i62" class="altColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setSessionAvailabilityListener(com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener)">setSessionAvailabilityListener</a></span>​(<a href="SessionAvailabilityListener.html" title="interface in com.google.android.exoplayer2.ext.cast">SessionAvailabilityListener</a> listener)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setSessionAvailabilityListener(com.google.android.exoplayer2.ext.cast.SessionAvailabilityListener)">setSessionAvailabilityListener</a></span>​(<a href="SessionAvailabilityListener.html" title="interface in com.google.android.exoplayer2.ext.cast">SessionAvailabilityListener</a> listener)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Sets a listener for updates on the cast session availability.</div>
|
<div class="block">Sets a listener for updates on the cast session availability.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i62" class="altColor">
|
<tr id="i63" class="rowColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setShuffleModeEnabled(boolean)">setShuffleModeEnabled</a></span>​(boolean shuffleModeEnabled)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setShuffleModeEnabled(boolean)">setShuffleModeEnabled</a></span>​(boolean shuffleModeEnabled)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Sets whether shuffling of media items is enabled.</div>
|
<div class="block">Sets whether shuffling of media items is enabled.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i63" class="rowColor">
|
<tr id="i64" class="altColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setTrackSelectionParameters(com.google.android.exoplayer2.trackselection.TrackSelectionParameters)">setTrackSelectionParameters</a></span>​(<a href="../../trackselection/TrackSelectionParameters.html" title="class in com.google.android.exoplayer2.trackselection">TrackSelectionParameters</a> parameters)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setTrackSelectionParameters(com.google.android.exoplayer2.trackselection.TrackSelectionParameters)">setTrackSelectionParameters</a></span>​(<a href="../../trackselection/TrackSelectionParameters.html" title="class in com.google.android.exoplayer2.trackselection">TrackSelectionParameters</a> parameters)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Sets the parameters constraining the track selection.</div>
|
<div class="block">Sets the parameters constraining the track selection.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i64" class="altColor">
|
<tr id="i65" class="rowColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setVideoSurface(android.view.Surface)">setVideoSurface</a></span>​(<a href="https://developer.android.com/reference/android/view/Surface.html" title="class or interface in android.view" class="externalLink" target="_top">Surface</a> surface)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setVideoSurface(android.view.Surface)">setVideoSurface</a></span>​(<a href="https://developer.android.com/reference/android/view/Surface.html" title="class or interface in android.view" class="externalLink" target="_top">Surface</a> surface)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">This method is not supported and does nothing.</div>
|
<div class="block">This method is not supported and does nothing.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i65" class="rowColor">
|
<tr id="i66" class="altColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setVideoSurfaceHolder(android.view.SurfaceHolder)">setVideoSurfaceHolder</a></span>​(<a href="https://developer.android.com/reference/android/view/SurfaceHolder.html" title="class or interface in android.view" class="externalLink" target="_top">SurfaceHolder</a> surfaceHolder)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setVideoSurfaceHolder(android.view.SurfaceHolder)">setVideoSurfaceHolder</a></span>​(<a href="https://developer.android.com/reference/android/view/SurfaceHolder.html" title="class or interface in android.view" class="externalLink" target="_top">SurfaceHolder</a> surfaceHolder)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">This method is not supported and does nothing.</div>
|
<div class="block">This method is not supported and does nothing.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i66" class="altColor">
|
<tr id="i67" class="rowColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setVideoSurfaceView(android.view.SurfaceView)">setVideoSurfaceView</a></span>​(<a href="https://developer.android.com/reference/android/view/SurfaceView.html" title="class or interface in android.view" class="externalLink" target="_top">SurfaceView</a> surfaceView)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setVideoSurfaceView(android.view.SurfaceView)">setVideoSurfaceView</a></span>​(<a href="https://developer.android.com/reference/android/view/SurfaceView.html" title="class or interface in android.view" class="externalLink" target="_top">SurfaceView</a> surfaceView)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">This method is not supported and does nothing.</div>
|
<div class="block">This method is not supported and does nothing.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i67" class="rowColor">
|
<tr id="i68" class="altColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setVideoTextureView(android.view.TextureView)">setVideoTextureView</a></span>​(<a href="https://developer.android.com/reference/android/view/TextureView.html" title="class or interface in android.view" class="externalLink" target="_top">TextureView</a> textureView)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setVideoTextureView(android.view.TextureView)">setVideoTextureView</a></span>​(<a href="https://developer.android.com/reference/android/view/TextureView.html" title="class or interface in android.view" class="externalLink" target="_top">TextureView</a> textureView)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">This method is not supported and does nothing.</div>
|
<div class="block">This method is not supported and does nothing.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i68" class="altColor">
|
<tr id="i69" class="rowColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setVolume(float)">setVolume</a></span>​(float volume)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setVolume(float)">setVolume</a></span>​(float volume)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">This method is not supported and does nothing.</div>
|
<div class="block">This method is not supported and does nothing.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i69" class="rowColor">
|
<tr id="i70" class="altColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#stop()">stop</a></span>()</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#stop()">stop</a></span>()</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Stops playback without resetting the playlist.</div>
|
<div class="block">Stops playback without resetting the playlist.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i70" class="altColor">
|
<tr id="i71" class="rowColor">
|
||||||
<td class="colFirst"><code>void</code></td>
|
<td class="colFirst"><code>void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#stop(boolean)">stop</a></span>​(boolean reset)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#stop(boolean)">stop</a></span>​(boolean reset)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
|
|
@ -1537,6 +1542,15 @@ public @com.google.android.exoplayer2.Player.RepeatMode int getRepeatM
|
||||||
it will be prioritised above the same field coming from static or timed metadata.</div>
|
it will be prioritised above the same field coming from static or timed metadata.</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<a id="getMediaMetadataInternal()">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList">
|
||||||
|
<h4>getMediaMetadataInternal</h4>
|
||||||
|
<pre class="methodSignature">public <a href="../../MediaMetadata.html" title="class in com.google.android.exoplayer2">MediaMetadata</a> getMediaMetadataInternal()</pre>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<a id="getPlaylistMetadata()">
|
<a id="getPlaylistMetadata()">
|
||||||
<!-- -->
|
<!-- -->
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
catch(err) {
|
catch(err) {
|
||||||
}
|
}
|
||||||
//-->
|
//-->
|
||||||
var data = {"i0":9,"i1":9,"i2":9,"i3":9,"i4":9,"i5":9,"i6":9,"i7":9,"i8":9,"i9":9,"i10":9,"i11":9};
|
var data = {"i0":9,"i1":9,"i2":9,"i3":9,"i4":9,"i5":9,"i6":9,"i7":9,"i8":9,"i9":9,"i10":9};
|
||||||
var tabs = {65535:["t0","All Methods"],1:["t1","Static Methods"],8:["t4","Concrete Methods"]};
|
var tabs = {65535:["t0","All Methods"],1:["t1","Static Methods"],8:["t4","Concrete Methods"]};
|
||||||
var altColor = "altColor";
|
var altColor = "altColor";
|
||||||
var rowColor = "rowColor";
|
var rowColor = "rowColor";
|
||||||
|
|
@ -216,14 +216,6 @@ extends <a href="https://developer.android.com/reference/java/lang/Object.html"
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i7" class="rowColor">
|
<tr id="i7" class="rowColor">
|
||||||
<td class="colFirst"><code>static boolean</code></td>
|
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#runUntilReceiveOffloadSchedulingEnabledNewState(com.google.android.exoplayer2.ExoPlayer)">runUntilReceiveOffloadSchedulingEnabledNewState</a></span>​(<a href="../ExoPlayer.html" title="interface in com.google.android.exoplayer2">ExoPlayer</a> player)</code></th>
|
|
||||||
<td class="colLast">
|
|
||||||
<div class="block">Runs tasks of the main <a href="https://developer.android.com/reference/android/os/Looper.html" title="class or interface in android.os" class="externalLink"><code>Looper</code></a> until <a href="../ExoPlayer.AudioOffloadListener.html#onExperimentalOffloadSchedulingEnabledChanged(boolean)" target="_top"><code>ExoPlayer.AudioOffloadListener.onExperimentalOffloadSchedulingEnabledChanged(boolean)</code></a> is called or a
|
|
||||||
playback error occurs.</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr id="i8" class="altColor">
|
|
||||||
<td class="colFirst"><code>static void</code></td>
|
<td class="colFirst"><code>static void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#runUntilRenderedFirstFrame(com.google.android.exoplayer2.ExoPlayer)">runUntilRenderedFirstFrame</a></span>​(<a href="../ExoPlayer.html" title="interface in com.google.android.exoplayer2">ExoPlayer</a> player)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#runUntilRenderedFirstFrame(com.google.android.exoplayer2.ExoPlayer)">runUntilRenderedFirstFrame</a></span>​(<a href="../ExoPlayer.html" title="interface in com.google.android.exoplayer2">ExoPlayer</a> player)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
|
|
@ -231,7 +223,7 @@ extends <a href="https://developer.android.com/reference/java/lang/Object.html"
|
||||||
callback is called or a playback error occurs.</div>
|
callback is called or a playback error occurs.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i9" class="rowColor">
|
<tr id="i8" class="altColor">
|
||||||
<td class="colFirst"><code>static void</code></td>
|
<td class="colFirst"><code>static void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#runUntilSleepingForOffload(com.google.android.exoplayer2.ExoPlayer,boolean)">runUntilSleepingForOffload</a></span>​(<a href="../ExoPlayer.html" title="interface in com.google.android.exoplayer2">ExoPlayer</a> player,
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#runUntilSleepingForOffload(com.google.android.exoplayer2.ExoPlayer,boolean)">runUntilSleepingForOffload</a></span>​(<a href="../ExoPlayer.html" title="interface in com.google.android.exoplayer2">ExoPlayer</a> player,
|
||||||
boolean expectedSleepForOffload)</code></th>
|
boolean expectedSleepForOffload)</code></th>
|
||||||
|
|
@ -240,14 +232,14 @@ extends <a href="https://developer.android.com/reference/java/lang/Object.html"
|
||||||
playback error occurs.</div>
|
playback error occurs.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i10" class="altColor">
|
<tr id="i9" class="rowColor">
|
||||||
<td class="colFirst"><code>static <a href="../Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a></code></td>
|
<td class="colFirst"><code>static <a href="../Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a></code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#runUntilTimelineChanged(com.google.android.exoplayer2.Player)">runUntilTimelineChanged</a></span>​(<a href="../Player.html" title="interface in com.google.android.exoplayer2">Player</a> player)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#runUntilTimelineChanged(com.google.android.exoplayer2.Player)">runUntilTimelineChanged</a></span>​(<a href="../Player.html" title="interface in com.google.android.exoplayer2">Player</a> player)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Runs tasks of the main <a href="https://developer.android.com/reference/android/os/Looper.html" title="class or interface in android.os" class="externalLink" target="_top"><code>Looper</code></a> until a timeline change or a playback error occurs.</div>
|
<div class="block">Runs tasks of the main <a href="https://developer.android.com/reference/android/os/Looper.html" title="class or interface in android.os" class="externalLink" target="_top"><code>Looper</code></a> until a timeline change or a playback error occurs.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i11" class="rowColor">
|
<tr id="i10" class="altColor">
|
||||||
<td class="colFirst"><code>static void</code></td>
|
<td class="colFirst"><code>static void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#runUntilTimelineChanged(com.google.android.exoplayer2.Player,com.google.android.exoplayer2.Timeline)">runUntilTimelineChanged</a></span>​(<a href="../Player.html" title="interface in com.google.android.exoplayer2">Player</a> player,
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#runUntilTimelineChanged(com.google.android.exoplayer2.Player,com.google.android.exoplayer2.Timeline)">runUntilTimelineChanged</a></span>​(<a href="../Player.html" title="interface in com.google.android.exoplayer2">Player</a> player,
|
||||||
<a href="../Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a> expectedTimeline)</code></th>
|
<a href="../Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a> expectedTimeline)</code></th>
|
||||||
|
|
@ -416,29 +408,6 @@ extends <a href="https://developer.android.com/reference/java/lang/Object.html"
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a id="runUntilReceiveOffloadSchedulingEnabledNewState(com.google.android.exoplayer2.ExoPlayer)">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList">
|
|
||||||
<h4>runUntilReceiveOffloadSchedulingEnabledNewState</h4>
|
|
||||||
<pre class="methodSignature">public static boolean runUntilReceiveOffloadSchedulingEnabledNewState​(<a href="../ExoPlayer.html" title="interface in com.google.android.exoplayer2">ExoPlayer</a> player)
|
|
||||||
throws <a href="https://developer.android.com/reference/java/util/concurrent/TimeoutException.html" title="class or interface in java.util.concurrent" class="externalLink" target="_top">TimeoutException</a></pre>
|
|
||||||
<div class="block">Runs tasks of the main <a href="https://developer.android.com/reference/android/os/Looper.html" title="class or interface in android.os" class="externalLink"><code>Looper</code></a> until <a href="../ExoPlayer.AudioOffloadListener.html#onExperimentalOffloadSchedulingEnabledChanged(boolean)" target="_top"><code>ExoPlayer.AudioOffloadListener.onExperimentalOffloadSchedulingEnabledChanged(boolean)</code></a> is called or a
|
|
||||||
playback error occurs.
|
|
||||||
|
|
||||||
<p>If a playback error occurs it will be thrown wrapped in an <a href="https://developer.android.com/reference/java/lang/IllegalStateException.html" title="class or interface in java.lang" class="externalLink" target="_top"><code>IllegalStateException</code></a>.</div>
|
|
||||||
<dl>
|
|
||||||
<dt><span class="paramLabel">Parameters:</span></dt>
|
|
||||||
<dd><code>player</code> - The <a href="../Player.html" title="interface in com.google.android.exoplayer2"><code>Player</code></a>.</dd>
|
|
||||||
<dt><span class="returnLabel">Returns:</span></dt>
|
|
||||||
<dd>The new offloadSchedulingEnabled state.</dd>
|
|
||||||
<dt><span class="throwsLabel">Throws:</span></dt>
|
|
||||||
<dd><code><a href="https://developer.android.com/reference/java/util/concurrent/TimeoutException.html" title="class or interface in java.util.concurrent" class="externalLink">TimeoutException</a></code> - If the <a href="RobolectricUtil.html#DEFAULT_TIMEOUT_MS" target="_top"><code>default timeout</code></a> is
|
|
||||||
exceeded.</dd>
|
|
||||||
</dl>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<a id="runUntilSleepingForOffload(com.google.android.exoplayer2.ExoPlayer,boolean)">
|
<a id="runUntilSleepingForOffload(com.google.android.exoplayer2.ExoPlayer,boolean)">
|
||||||
<!-- -->
|
<!-- -->
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -139,7 +139,7 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
</dl>
|
</dl>
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Direct Known Subclasses:</dt>
|
<dt>Direct Known Subclasses:</dt>
|
||||||
<dd><code><a href="../testutil/FakeMediaSource.InitialTimeline.html" title="class in com.google.android.exoplayer2.testutil">FakeMediaSource.InitialTimeline</a></code>, <code><a href="../testutil/NoUidTimeline.html" title="class in com.google.android.exoplayer2.testutil">NoUidTimeline</a></code>, <code><a href="ads/SinglePeriodAdTimeline.html" title="class in com.google.android.exoplayer2.source.ads">SinglePeriodAdTimeline</a></code></dd>
|
<dd><code><a href="../testutil/FakeMediaSource.InitialTimeline.html" title="class in com.google.android.exoplayer2.testutil">FakeMediaSource.InitialTimeline</a></code>, <code><a href="ads/SinglePeriodAdTimeline.html" title="class in com.google.android.exoplayer2.source.ads">SinglePeriodAdTimeline</a></code></dd>
|
||||||
</dl>
|
</dl>
|
||||||
<hr>
|
<hr>
|
||||||
<pre>public abstract class <span class="typeNameLabel">ForwardingTimeline</span>
|
<pre>public abstract class <span class="typeNameLabel">ForwardingTimeline</span>
|
||||||
|
|
|
||||||
|
|
@ -253,6 +253,14 @@ extends <a href="../Timeline.html" title="class in com.google.android.exoplayer2
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="rowColor">
|
<tr class="rowColor">
|
||||||
|
<th class="colConstructorName" scope="row"><code><span class="memberNameLink"><a href="#%3Cinit%3E(java.lang.Object%5B%5D,com.google.android.exoplayer2.source.ShuffleOrder,com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition...)">FakeTimeline</a></span>​(<a href="https://developer.android.com/reference/java/lang/Object.html" title="class or interface in java.lang" class="externalLink" target="_top">Object</a>[] manifests,
|
||||||
|
<a href="../source/ShuffleOrder.html" title="interface in com.google.android.exoplayer2.source">ShuffleOrder</a> shuffleOrder,
|
||||||
|
<a href="FakeTimeline.TimelineWindowDefinition.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline.TimelineWindowDefinition</a>... windowDefinitions)</code></th>
|
||||||
|
<td class="colLast">
|
||||||
|
<div class="block">Creates a fake timeline with the given window definitions and <a href="../source/ShuffleOrder.html" title="interface in com.google.android.exoplayer2.source"><code>ShuffleOrder</code></a>.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="altColor">
|
||||||
<th class="colConstructorName" scope="row"><code><span class="memberNameLink"><a href="#%3Cinit%3E(java.lang.Object%5B%5D,com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition...)">FakeTimeline</a></span>​(<a href="https://developer.android.com/reference/java/lang/Object.html" title="class or interface in java.lang" class="externalLink" target="_top">Object</a>[] manifests,
|
<th class="colConstructorName" scope="row"><code><span class="memberNameLink"><a href="#%3Cinit%3E(java.lang.Object%5B%5D,com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition...)">FakeTimeline</a></span>​(<a href="https://developer.android.com/reference/java/lang/Object.html" title="class or interface in java.lang" class="externalLink" target="_top">Object</a>[] manifests,
|
||||||
<a href="FakeTimeline.TimelineWindowDefinition.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline.TimelineWindowDefinition</a>... windowDefinitions)</code></th>
|
<a href="FakeTimeline.TimelineWindowDefinition.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline.TimelineWindowDefinition</a>... windowDefinitions)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
|
|
@ -481,7 +489,7 @@ extends <a href="../Timeline.html" title="class in com.google.android.exoplayer2
|
||||||
<a id="<init>(java.lang.Object[],com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition...)">
|
<a id="<init>(java.lang.Object[],com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition...)">
|
||||||
<!-- -->
|
<!-- -->
|
||||||
</a>
|
</a>
|
||||||
<ul class="blockListLast">
|
<ul class="blockList">
|
||||||
<li class="blockList">
|
<li class="blockList">
|
||||||
<h4>FakeTimeline</h4>
|
<h4>FakeTimeline</h4>
|
||||||
<pre>public FakeTimeline​(<a href="https://developer.android.com/reference/java/lang/Object.html" title="class or interface in java.lang" class="externalLink" target="_top">Object</a>[] manifests,
|
<pre>public FakeTimeline​(<a href="https://developer.android.com/reference/java/lang/Object.html" title="class or interface in java.lang" class="externalLink" target="_top">Object</a>[] manifests,
|
||||||
|
|
@ -493,6 +501,22 @@ extends <a href="../Timeline.html" title="class in com.google.android.exoplayer2
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<a id="<init>(java.lang.Object[],com.google.android.exoplayer2.source.ShuffleOrder,com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition...)">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<ul class="blockListLast">
|
||||||
|
<li class="blockList">
|
||||||
|
<h4>FakeTimeline</h4>
|
||||||
|
<pre>public FakeTimeline​(<a href="https://developer.android.com/reference/java/lang/Object.html" title="class or interface in java.lang" class="externalLink" target="_top">Object</a>[] manifests,
|
||||||
|
<a href="../source/ShuffleOrder.html" title="interface in com.google.android.exoplayer2.source">ShuffleOrder</a> shuffleOrder,
|
||||||
|
<a href="FakeTimeline.TimelineWindowDefinition.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline.TimelineWindowDefinition</a>... windowDefinitions)</pre>
|
||||||
|
<div class="block">Creates a fake timeline with the given window definitions and <a href="../source/ShuffleOrder.html" title="interface in com.google.android.exoplayer2.source"><code>ShuffleOrder</code></a>.</div>
|
||||||
|
<dl>
|
||||||
|
<dt><span class="paramLabel">Parameters:</span></dt>
|
||||||
|
<dd><code>windowDefinitions</code> - A list of <a href="FakeTimeline.TimelineWindowDefinition.html" title="class in com.google.android.exoplayer2.testutil"><code>FakeTimeline.TimelineWindowDefinition</code></a>s.</dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -1,440 +0,0 @@
|
||||||
<!DOCTYPE HTML>
|
|
||||||
<!-- NewPage -->
|
|
||||||
<html lang="en">
|
|
||||||
<head><!-- start favicons snippet, use https://realfavicongenerator.net/ --><link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16x16.png"><link rel="manifest" href="/assets/site.webmanifest"><link rel="mask-icon" href="/assets/safari-pinned-tab.svg" color="#fc4d50"><link rel="shortcut icon" href="/assets/favicon.ico"><meta name="msapplication-TileColor" content="#ffc40d"><meta name="msapplication-config" content="/assets/browserconfig.xml"><meta name="theme-color" content="#ffffff"><!-- end favicons snippet -->
|
|
||||||
<title>NoUidTimeline (ExoPlayer library)</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
|
||||||
<link rel="stylesheet" type="text/css" href="../../../../../stylesheet.css" title="Style">
|
|
||||||
<link rel="stylesheet" type="text/css" href="../../../../../jquery/jquery-ui.css" title="Style">
|
|
||||||
<script type="text/javascript" src="../../../../../script.js"></script>
|
|
||||||
<script type="text/javascript" src="../../../../../jquery/jszip/dist/jszip.min.js"></script>
|
|
||||||
<script type="text/javascript" src="../../../../../jquery/jszip-utils/dist/jszip-utils.min.js"></script>
|
|
||||||
<!--[if IE]>
|
|
||||||
<script type="text/javascript" src="../../../../../jquery/jszip-utils/dist/jszip-utils-ie.min.js"></script>
|
|
||||||
<![endif]-->
|
|
||||||
<script type="text/javascript" src="../../../../../jquery/jquery-3.5.1.js"></script>
|
|
||||||
<script type="text/javascript" src="../../../../../jquery/jquery-ui.js"></script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<script type="text/javascript"><!--
|
|
||||||
try {
|
|
||||||
if (location.href.indexOf('is-external=true') == -1) {
|
|
||||||
parent.document.title="NoUidTimeline (ExoPlayer library)";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(err) {
|
|
||||||
}
|
|
||||||
//-->
|
|
||||||
var data = {"i0":10,"i1":10};
|
|
||||||
var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],8:["t4","Concrete Methods"]};
|
|
||||||
var altColor = "altColor";
|
|
||||||
var rowColor = "rowColor";
|
|
||||||
var tableTab = "tableTab";
|
|
||||||
var activeTableTab = "activeTableTab";
|
|
||||||
var pathtoroot = "../../../../../";
|
|
||||||
var useModuleDirectories = false;
|
|
||||||
loadScripts(document, 'script');</script>
|
|
||||||
<noscript>
|
|
||||||
<div>JavaScript is disabled on your browser.</div>
|
|
||||||
</noscript>
|
|
||||||
<header role="banner">
|
|
||||||
<nav role="navigation">
|
|
||||||
<div class="fixedNav">
|
|
||||||
<!-- ========= START OF TOP NAVBAR ======= -->
|
|
||||||
<div class="topNav"><a id="navbar.top">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<div class="skipNav"><a href="#skip.navbar.top" title="Skip navigation links">Skip navigation links</a></div>
|
|
||||||
<a id="navbar.top.firstrow">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<ul class="navList" title="Navigation">
|
|
||||||
<li><a href="../../../../../index.html">Overview</a></li>
|
|
||||||
<li><a href="package-summary.html">Package</a></li>
|
|
||||||
<li class="navBarCell1Rev">Class</li>
|
|
||||||
<li><a href="package-tree.html">Tree</a></li>
|
|
||||||
<li><a href="../../../../../deprecated-list.html">Deprecated</a></li>
|
|
||||||
<li><a href="../../../../../index-all.html">Index</a></li>
|
|
||||||
<li><a href="../../../../../help-doc.html">Help</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="subNav">
|
|
||||||
<ul class="navList" id="allclasses_navbar_top">
|
|
||||||
<li><a href="../../../../../allclasses.html">All Classes</a></li>
|
|
||||||
</ul>
|
|
||||||
<ul class="navListSearch">
|
|
||||||
<li><label for="search">SEARCH:</label>
|
|
||||||
<input type="text" id="search" value="search" disabled="disabled">
|
|
||||||
<input type="reset" id="reset" value="reset" disabled="disabled">
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div>
|
|
||||||
<script type="text/javascript"><!--
|
|
||||||
allClassesLink = document.getElementById("allclasses_navbar_top");
|
|
||||||
if(window==top) {
|
|
||||||
allClassesLink.style.display = "block";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
allClassesLink.style.display = "none";
|
|
||||||
}
|
|
||||||
//-->
|
|
||||||
</script>
|
|
||||||
<noscript>
|
|
||||||
<div>JavaScript is disabled on your browser.</div>
|
|
||||||
</noscript>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<ul class="subNavList">
|
|
||||||
<li>Summary: </li>
|
|
||||||
<li><a href="#nested.class.summary">Nested</a> | </li>
|
|
||||||
<li><a href="#field.summary">Field</a> | </li>
|
|
||||||
<li><a href="#constructor.summary">Constr</a> | </li>
|
|
||||||
<li><a href="#method.summary">Method</a></li>
|
|
||||||
</ul>
|
|
||||||
<ul class="subNavList">
|
|
||||||
<li>Detail: </li>
|
|
||||||
<li>Field | </li>
|
|
||||||
<li><a href="#constructor.detail">Constr</a> | </li>
|
|
||||||
<li><a href="#method.detail">Method</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<a id="skip.navbar.top">
|
|
||||||
<!-- -->
|
|
||||||
</a></div>
|
|
||||||
<!-- ========= END OF TOP NAVBAR ========= -->
|
|
||||||
</div>
|
|
||||||
<div class="navPadding"> </div>
|
|
||||||
<script type="text/javascript"><!--
|
|
||||||
$('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|
||||||
//-->
|
|
||||||
</script>
|
|
||||||
</nav>
|
|
||||||
</header>
|
|
||||||
<!-- ======== START OF CLASS DATA ======== -->
|
|
||||||
<main role="main">
|
|
||||||
<div class="header">
|
|
||||||
<div class="subTitle"><span class="packageLabelInType">Package</span> <a href="package-summary.html">com.google.android.exoplayer2.testutil</a></div>
|
|
||||||
<h2 title="Class NoUidTimeline" class="title">Class NoUidTimeline</h2>
|
|
||||||
</div>
|
|
||||||
<div class="contentContainer">
|
|
||||||
<ul class="inheritance">
|
|
||||||
<li><a href="https://developer.android.com/reference/java/lang/Object.html" title="class or interface in java.lang" class="externalLink" target="_top">java.lang.Object</a></li>
|
|
||||||
<li>
|
|
||||||
<ul class="inheritance">
|
|
||||||
<li><a href="../Timeline.html" title="class in com.google.android.exoplayer2">com.google.android.exoplayer2.Timeline</a></li>
|
|
||||||
<li>
|
|
||||||
<ul class="inheritance">
|
|
||||||
<li><a href="../source/ForwardingTimeline.html" title="class in com.google.android.exoplayer2.source">com.google.android.exoplayer2.source.ForwardingTimeline</a></li>
|
|
||||||
<li>
|
|
||||||
<ul class="inheritance">
|
|
||||||
<li>com.google.android.exoplayer2.testutil.NoUidTimeline</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<div class="description">
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList">
|
|
||||||
<dl>
|
|
||||||
<dt>All Implemented Interfaces:</dt>
|
|
||||||
<dd><code><a href="../Bundleable.html" title="interface in com.google.android.exoplayer2">Bundleable</a></code></dd>
|
|
||||||
</dl>
|
|
||||||
<hr>
|
|
||||||
<pre>public class <span class="typeNameLabel">NoUidTimeline</span>
|
|
||||||
extends <a href="../source/ForwardingTimeline.html" title="class in com.google.android.exoplayer2.source">ForwardingTimeline</a></pre>
|
|
||||||
<div class="block">A timeline which wraps another timeline and overrides all window and period uids to 0. This is
|
|
||||||
useful for testing timeline equality without taking uids into account.</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="summary">
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList">
|
|
||||||
<!-- ======== NESTED CLASS SUMMARY ======== -->
|
|
||||||
<section role="region">
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList"><a id="nested.class.summary">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<h3>Nested Class Summary</h3>
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList"><a id="nested.classes.inherited.from.class.com.google.android.exoplayer2.Timeline">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<h3>Nested classes/interfaces inherited from class com.google.android.exoplayer2.<a href="../Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a></h3>
|
|
||||||
<code><a href="../Timeline.Period.html" title="class in com.google.android.exoplayer2">Timeline.Period</a>, <a href="../Timeline.RemotableTimeline.html" title="class in com.google.android.exoplayer2">Timeline.RemotableTimeline</a>, <a href="../Timeline.Window.html" title="class in com.google.android.exoplayer2">Timeline.Window</a></code></li>
|
|
||||||
</ul>
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList"><a id="nested.classes.inherited.from.class.com.google.android.exoplayer2.Bundleable">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<h3>Nested classes/interfaces inherited from interface com.google.android.exoplayer2.<a href="../Bundleable.html" title="interface in com.google.android.exoplayer2">Bundleable</a></h3>
|
|
||||||
<code><a href="../Bundleable.Creator.html" title="interface in com.google.android.exoplayer2">Bundleable.Creator</a><<a href="../Bundleable.Creator.html" title="type parameter in Bundleable.Creator">T</a> extends <a href="../Bundleable.html" title="interface in com.google.android.exoplayer2">Bundleable</a>></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<!-- =========== FIELD SUMMARY =========== -->
|
|
||||||
<section role="region">
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList"><a id="field.summary">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<h3>Field Summary</h3>
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList"><a id="fields.inherited.from.class.com.google.android.exoplayer2.source.ForwardingTimeline">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<h3>Fields inherited from class com.google.android.exoplayer2.source.<a href="../source/ForwardingTimeline.html" title="class in com.google.android.exoplayer2.source">ForwardingTimeline</a></h3>
|
|
||||||
<code><a href="../source/ForwardingTimeline.html#timeline">timeline</a></code></li>
|
|
||||||
</ul>
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList"><a id="fields.inherited.from.class.com.google.android.exoplayer2.Timeline">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<h3>Fields inherited from class com.google.android.exoplayer2.<a href="../Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a></h3>
|
|
||||||
<code><a href="../Timeline.html#CREATOR">CREATOR</a>, <a href="../Timeline.html#EMPTY">EMPTY</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<!-- ======== CONSTRUCTOR SUMMARY ======== -->
|
|
||||||
<section role="region">
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList"><a id="constructor.summary">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<h3>Constructor Summary</h3>
|
|
||||||
<table class="memberSummary">
|
|
||||||
<caption><span>Constructors</span><span class="tabEnd"> </span></caption>
|
|
||||||
<tr>
|
|
||||||
<th class="colFirst" scope="col">Constructor</th>
|
|
||||||
<th class="colLast" scope="col">Description</th>
|
|
||||||
</tr>
|
|
||||||
<tr class="altColor">
|
|
||||||
<th class="colConstructorName" scope="row"><code><span class="memberNameLink"><a href="#%3Cinit%3E(com.google.android.exoplayer2.Timeline)">NoUidTimeline</a></span>​(<a href="../Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a> timeline)</code></th>
|
|
||||||
<td class="colLast">
|
|
||||||
<div class="block">Creates an instance.</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<!-- ========== METHOD SUMMARY =========== -->
|
|
||||||
<section role="region">
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList"><a id="method.summary">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<h3>Method Summary</h3>
|
|
||||||
<table class="memberSummary">
|
|
||||||
<caption><span id="t0" class="activeTableTab"><span>All Methods</span><span class="tabEnd"> </span></span><span id="t2" class="tableTab"><span><a href="javascript:show(2);">Instance Methods</a></span><span class="tabEnd"> </span></span><span id="t4" class="tableTab"><span><a href="javascript:show(8);">Concrete Methods</a></span><span class="tabEnd"> </span></span></caption>
|
|
||||||
<tr>
|
|
||||||
<th class="colFirst" scope="col">Modifier and Type</th>
|
|
||||||
<th class="colSecond" scope="col">Method</th>
|
|
||||||
<th class="colLast" scope="col">Description</th>
|
|
||||||
</tr>
|
|
||||||
<tr id="i0" class="altColor">
|
|
||||||
<td class="colFirst"><code><a href="../Timeline.Period.html" title="class in com.google.android.exoplayer2">Timeline.Period</a></code></td>
|
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPeriod(int,com.google.android.exoplayer2.Timeline.Period,boolean)">getPeriod</a></span>​(int periodIndex,
|
|
||||||
<a href="../Timeline.Period.html" title="class in com.google.android.exoplayer2">Timeline.Period</a> period,
|
|
||||||
boolean setIds)</code></th>
|
|
||||||
<td class="colLast">
|
|
||||||
<div class="block">Populates a <a href="../Timeline.Period.html" title="class in com.google.android.exoplayer2"><code>Timeline.Period</code></a> with data for the period at the specified index.</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr id="i1" class="rowColor">
|
|
||||||
<td class="colFirst"><code><a href="../Timeline.Window.html" title="class in com.google.android.exoplayer2">Timeline.Window</a></code></td>
|
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getWindow(int,com.google.android.exoplayer2.Timeline.Window,long)">getWindow</a></span>​(int windowIndex,
|
|
||||||
<a href="../Timeline.Window.html" title="class in com.google.android.exoplayer2">Timeline.Window</a> window,
|
|
||||||
long defaultPositionProjectionUs)</code></th>
|
|
||||||
<td class="colLast">
|
|
||||||
<div class="block">Populates a <a href="../Timeline.Window.html" title="class in com.google.android.exoplayer2"><code>Timeline.Window</code></a> with data for the window at the specified index.</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList"><a id="methods.inherited.from.class.com.google.android.exoplayer2.source.ForwardingTimeline">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<h3>Methods inherited from class com.google.android.exoplayer2.source.<a href="../source/ForwardingTimeline.html" title="class in com.google.android.exoplayer2.source">ForwardingTimeline</a></h3>
|
|
||||||
<code><a href="../source/ForwardingTimeline.html#getFirstWindowIndex(boolean)">getFirstWindowIndex</a>, <a href="../source/ForwardingTimeline.html#getIndexOfPeriod(java.lang.Object)">getIndexOfPeriod</a>, <a href="../source/ForwardingTimeline.html#getLastWindowIndex(boolean)">getLastWindowIndex</a>, <a href="../source/ForwardingTimeline.html#getNextWindowIndex(int,@com.google.android.exoplayer2.Player.RepeatModeint,boolean)">getNextWindowIndex</a>, <a href="../source/ForwardingTimeline.html#getPeriodCount()">getPeriodCount</a>, <a href="../source/ForwardingTimeline.html#getPreviousWindowIndex(int,@com.google.android.exoplayer2.Player.RepeatModeint,boolean)">getPreviousWindowIndex</a>, <a href="../source/ForwardingTimeline.html#getUidOfPeriod(int)">getUidOfPeriod</a>, <a href="../source/ForwardingTimeline.html#getWindowCount()">getWindowCount</a></code></li>
|
|
||||||
</ul>
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList"><a id="methods.inherited.from.class.com.google.android.exoplayer2.Timeline">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<h3>Methods inherited from class com.google.android.exoplayer2.<a href="../Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a></h3>
|
|
||||||
<code><a href="../Timeline.html#equals(java.lang.Object)">equals</a>, <a href="../Timeline.html#getNextPeriodIndex(int,com.google.android.exoplayer2.Timeline.Period,com.google.android.exoplayer2.Timeline.Window,@com.google.android.exoplayer2.Player.RepeatModeint,boolean)">getNextPeriodIndex</a>, <a href="../Timeline.html#getPeriod(int,com.google.android.exoplayer2.Timeline.Period)">getPeriod</a>, <a href="../Timeline.html#getPeriodByUid(java.lang.Object,com.google.android.exoplayer2.Timeline.Period)">getPeriodByUid</a>, <a href="../Timeline.html#getPeriodPosition(com.google.android.exoplayer2.Timeline.Window,com.google.android.exoplayer2.Timeline.Period,int,long)">getPeriodPosition</a>, <a href="../Timeline.html#getPeriodPosition(com.google.android.exoplayer2.Timeline.Window,com.google.android.exoplayer2.Timeline.Period,int,long,long)">getPeriodPosition</a>, <a href="../Timeline.html#getPeriodPositionUs(com.google.android.exoplayer2.Timeline.Window,com.google.android.exoplayer2.Timeline.Period,int,long)">getPeriodPositionUs</a>, <a href="../Timeline.html#getPeriodPositionUs(com.google.android.exoplayer2.Timeline.Window,com.google.android.exoplayer2.Timeline.Period,int,long,long)">getPeriodPositionUs</a>, <a href="../Timeline.html#getWindow(int,com.google.android.exoplayer2.Timeline.Window)">getWindow</a>, <a href="../Timeline.html#hashCode()">hashCode</a>, <a href="../Timeline.html#isEmpty()">isEmpty</a>, <a href="../Timeline.html#isLastPeriod(int,com.google.android.exoplayer2.Timeline.Period,com.google.android.exoplayer2.Timeline.Window,@com.google.android.exoplayer2.Player.RepeatModeint,boolean)">isLastPeriod</a>, <a href="../Timeline.html#toBundle()">toBundle</a>, <a href="../Timeline.html#toBundle(boolean)">toBundle</a></code></li>
|
|
||||||
</ul>
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList"><a id="methods.inherited.from.class.java.lang.Object">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<h3>Methods inherited from class java.lang.<a href="https://developer.android.com/reference/java/lang/Object.html" title="class or interface in java.lang" class="externalLink" target="_top">Object</a></h3>
|
|
||||||
<code><a href="https://developer.android.com/reference/java/lang/Object.html#clone()" title="class or interface in java.lang" class="externalLink">clone</a>, <a href="https://developer.android.com/reference/java/lang/Object.html?is-external=true#finalize()" title="class or interface in java.lang" class="externalLink">finalize</a>, <a href="https://developer.android.com/reference/java/lang/Object.html?is-external=true#getClass()" title="class or interface in java.lang" class="externalLink">getClass</a>, <a href="https://developer.android.com/reference/java/lang/Object.html?is-external=true#notify()" title="class or interface in java.lang" class="externalLink">notify</a>, <a href="https://developer.android.com/reference/java/lang/Object.html?is-external=true#notifyAll()" title="class or interface in java.lang" class="externalLink">notifyAll</a>, <a href="https://developer.android.com/reference/java/lang/Object.html?is-external=true#toString()" title="class or interface in java.lang" class="externalLink">toString</a>, <a href="https://developer.android.com/reference/java/lang/Object.html?is-external=true#wait()" title="class or interface in java.lang" class="externalLink">wait</a>, <a href="https://developer.android.com/reference/java/lang/Object.html?is-external=true#wait(long)" title="class or interface in java.lang" class="externalLink">wait</a>, <a href="https://developer.android.com/reference/java/lang/Object.html?is-external=true#wait(long,int)" title="class or interface in java.lang" class="externalLink" target="_top">wait</a></code></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="details">
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList">
|
|
||||||
<!-- ========= CONSTRUCTOR DETAIL ======== -->
|
|
||||||
<section role="region">
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList"><a id="constructor.detail">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<h3>Constructor Detail</h3>
|
|
||||||
<a id="<init>(com.google.android.exoplayer2.Timeline)">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<ul class="blockListLast">
|
|
||||||
<li class="blockList">
|
|
||||||
<h4>NoUidTimeline</h4>
|
|
||||||
<pre>public NoUidTimeline​(<a href="../Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a> timeline)</pre>
|
|
||||||
<div class="block">Creates an instance.</div>
|
|
||||||
<dl>
|
|
||||||
<dt><span class="paramLabel">Parameters:</span></dt>
|
|
||||||
<dd><code>timeline</code> - The underlying timeline.</dd>
|
|
||||||
</dl>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
<!-- ============ METHOD DETAIL ========== -->
|
|
||||||
<section role="region">
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList"><a id="method.detail">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<h3>Method Detail</h3>
|
|
||||||
<a id="getWindow(int,com.google.android.exoplayer2.Timeline.Window,long)">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<ul class="blockList">
|
|
||||||
<li class="blockList">
|
|
||||||
<h4>getWindow</h4>
|
|
||||||
<pre class="methodSignature">public <a href="../Timeline.Window.html" title="class in com.google.android.exoplayer2">Timeline.Window</a> getWindow​(int windowIndex,
|
|
||||||
<a href="../Timeline.Window.html" title="class in com.google.android.exoplayer2">Timeline.Window</a> window,
|
|
||||||
long defaultPositionProjectionUs)</pre>
|
|
||||||
<div class="block"><span class="descfrmTypeLabel">Description copied from class: <code><a href="../Timeline.html#getWindow(int,com.google.android.exoplayer2.Timeline.Window,long)">Timeline</a></code></span></div>
|
|
||||||
<div class="block">Populates a <a href="../Timeline.Window.html" title="class in com.google.android.exoplayer2"><code>Timeline.Window</code></a> with data for the window at the specified index.</div>
|
|
||||||
<dl>
|
|
||||||
<dt><span class="overrideSpecifyLabel">Overrides:</span></dt>
|
|
||||||
<dd><code><a href="../source/ForwardingTimeline.html#getWindow(int,com.google.android.exoplayer2.Timeline.Window,long)">getWindow</a></code> in class <code><a href="../source/ForwardingTimeline.html" title="class in com.google.android.exoplayer2.source">ForwardingTimeline</a></code></dd>
|
|
||||||
<dt><span class="paramLabel">Parameters:</span></dt>
|
|
||||||
<dd><code>windowIndex</code> - The index of the window.</dd>
|
|
||||||
<dd><code>window</code> - The <a href="../Timeline.Window.html" title="class in com.google.android.exoplayer2"><code>Timeline.Window</code></a> to populate. Must not be null.</dd>
|
|
||||||
<dd><code>defaultPositionProjectionUs</code> - A duration into the future that the populated window's
|
|
||||||
default start position should be projected.</dd>
|
|
||||||
<dt><span class="returnLabel">Returns:</span></dt>
|
|
||||||
<dd>The populated <a href="../Timeline.Window.html" title="class in com.google.android.exoplayer2"><code>Timeline.Window</code></a>, for convenience.</dd>
|
|
||||||
</dl>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
<a id="getPeriod(int,com.google.android.exoplayer2.Timeline.Period,boolean)">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<ul class="blockListLast">
|
|
||||||
<li class="blockList">
|
|
||||||
<h4>getPeriod</h4>
|
|
||||||
<pre class="methodSignature">public <a href="../Timeline.Period.html" title="class in com.google.android.exoplayer2">Timeline.Period</a> getPeriod​(int periodIndex,
|
|
||||||
<a href="../Timeline.Period.html" title="class in com.google.android.exoplayer2">Timeline.Period</a> period,
|
|
||||||
boolean setIds)</pre>
|
|
||||||
<div class="block"><span class="descfrmTypeLabel">Description copied from class: <code><a href="../Timeline.html#getPeriod(int,com.google.android.exoplayer2.Timeline.Period,boolean)">Timeline</a></code></span></div>
|
|
||||||
<div class="block">Populates a <a href="../Timeline.Period.html" title="class in com.google.android.exoplayer2"><code>Timeline.Period</code></a> with data for the period at the specified index.</div>
|
|
||||||
<dl>
|
|
||||||
<dt><span class="overrideSpecifyLabel">Overrides:</span></dt>
|
|
||||||
<dd><code><a href="../source/ForwardingTimeline.html#getPeriod(int,com.google.android.exoplayer2.Timeline.Period,boolean)">getPeriod</a></code> in class <code><a href="../source/ForwardingTimeline.html" title="class in com.google.android.exoplayer2.source">ForwardingTimeline</a></code></dd>
|
|
||||||
<dt><span class="paramLabel">Parameters:</span></dt>
|
|
||||||
<dd><code>periodIndex</code> - The index of the period.</dd>
|
|
||||||
<dd><code>period</code> - The <a href="../Timeline.Period.html" title="class in com.google.android.exoplayer2"><code>Timeline.Period</code></a> to populate. Must not be null.</dd>
|
|
||||||
<dd><code>setIds</code> - Whether <a href="../Timeline.Period.html#id"><code>Timeline.Period.id</code></a> and <a href="../Timeline.Period.html#uid"><code>Timeline.Period.uid</code></a> should be populated. If false,
|
|
||||||
the fields will be set to null. The caller should pass false for efficiency reasons unless
|
|
||||||
the fields are required.</dd>
|
|
||||||
<dt><span class="returnLabel">Returns:</span></dt>
|
|
||||||
<dd>The populated <a href="../Timeline.Period.html" title="class in com.google.android.exoplayer2"><code>Timeline.Period</code></a>, for convenience.</dd>
|
|
||||||
</dl>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</section>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
<!-- ========= END OF CLASS DATA ========= -->
|
|
||||||
<footer role="contentinfo">
|
|
||||||
<nav role="navigation">
|
|
||||||
<!-- ======= START OF BOTTOM NAVBAR ====== -->
|
|
||||||
<div class="bottomNav"><a id="navbar.bottom">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<div class="skipNav"><a href="#skip.navbar.bottom" title="Skip navigation links">Skip navigation links</a></div>
|
|
||||||
<a id="navbar.bottom.firstrow">
|
|
||||||
<!-- -->
|
|
||||||
</a>
|
|
||||||
<ul class="navList" title="Navigation">
|
|
||||||
<li><a href="../../../../../index.html">Overview</a></li>
|
|
||||||
<li><a href="package-summary.html">Package</a></li>
|
|
||||||
<li class="navBarCell1Rev">Class</li>
|
|
||||||
<li><a href="package-tree.html">Tree</a></li>
|
|
||||||
<li><a href="../../../../../deprecated-list.html">Deprecated</a></li>
|
|
||||||
<li><a href="../../../../../index-all.html">Index</a></li>
|
|
||||||
<li><a href="../../../../../help-doc.html">Help</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="subNav">
|
|
||||||
<ul class="navList" id="allclasses_navbar_bottom">
|
|
||||||
<li><a href="../../../../../allclasses.html">All Classes</a></li>
|
|
||||||
</ul>
|
|
||||||
<div>
|
|
||||||
<script type="text/javascript"><!--
|
|
||||||
allClassesLink = document.getElementById("allclasses_navbar_bottom");
|
|
||||||
if(window==top) {
|
|
||||||
allClassesLink.style.display = "block";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
allClassesLink.style.display = "none";
|
|
||||||
}
|
|
||||||
//-->
|
|
||||||
</script>
|
|
||||||
<noscript>
|
|
||||||
<div>JavaScript is disabled on your browser.</div>
|
|
||||||
</noscript>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<ul class="subNavList">
|
|
||||||
<li>Summary: </li>
|
|
||||||
<li><a href="#nested.class.summary">Nested</a> | </li>
|
|
||||||
<li><a href="#field.summary">Field</a> | </li>
|
|
||||||
<li><a href="#constructor.summary">Constr</a> | </li>
|
|
||||||
<li><a href="#method.summary">Method</a></li>
|
|
||||||
</ul>
|
|
||||||
<ul class="subNavList">
|
|
||||||
<li>Detail: </li>
|
|
||||||
<li>Field | </li>
|
|
||||||
<li><a href="#constructor.detail">Constr</a> | </li>
|
|
||||||
<li><a href="#method.detail">Method</a></li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<a id="skip.navbar.bottom">
|
|
||||||
<!-- -->
|
|
||||||
</a></div>
|
|
||||||
<!-- ======== END OF BOTTOM NAVBAR ======= -->
|
|
||||||
</nav>
|
|
||||||
</footer>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
@ -867,7 +867,8 @@ public @com.google.android.exoplayer2.Player.PlaybackSuppressionReason int&
|
||||||
<ul class="blockList">
|
<ul class="blockList">
|
||||||
<li class="blockList">
|
<li class="blockList">
|
||||||
<h4>getPlayerError</h4>
|
<h4>getPlayerError</h4>
|
||||||
<pre class="methodSignature">public <a href="../PlaybackException.html" title="class in com.google.android.exoplayer2">PlaybackException</a> getPlayerError()</pre>
|
<pre class="methodSignature">@Nullable
|
||||||
|
public <a href="../PlaybackException.html" title="class in com.google.android.exoplayer2">PlaybackException</a> getPlayerError()</pre>
|
||||||
<div class="block"><span class="descfrmTypeLabel">Description copied from interface: <code><a href="../Player.html#getPlayerError()">Player</a></code></span></div>
|
<div class="block"><span class="descfrmTypeLabel">Description copied from interface: <code><a href="../Player.html#getPlayerError()">Player</a></code></span></div>
|
||||||
<div class="block">Returns the error that caused playback to fail. This is the same error that will have been
|
<div class="block">Returns the error that caused playback to fail. This is the same error that will have been
|
||||||
reported via <a href="../Player.Listener.html#onPlayerError(com.google.android.exoplayer2.PlaybackException)"><code>Player.Listener.onPlayerError(PlaybackException)</code></a> at the time of failure. It can
|
reported via <a href="../Player.Listener.html#onPlayerError(com.google.android.exoplayer2.PlaybackException)"><code>Player.Listener.onPlayerError(PlaybackException)</code></a> at the time of failure. It can
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
catch(err) {
|
catch(err) {
|
||||||
}
|
}
|
||||||
//-->
|
//-->
|
||||||
var data = {"i0":9,"i1":9,"i2":9,"i3":9,"i4":9,"i5":9,"i6":9,"i7":9,"i8":9,"i9":9,"i10":9,"i11":9,"i12":9,"i13":9,"i14":9,"i15":9,"i16":9,"i17":9,"i18":9,"i19":9,"i20":9,"i21":9,"i22":9,"i23":9,"i24":9};
|
var data = {"i0":9,"i1":9,"i2":9,"i3":9,"i4":9,"i5":9,"i6":9,"i7":9,"i8":9,"i9":9,"i10":9,"i11":9,"i12":9,"i13":9,"i14":9,"i15":9,"i16":9,"i17":9,"i18":9,"i19":9,"i20":9,"i21":9,"i22":9,"i23":9,"i24":9,"i25":9};
|
||||||
var tabs = {65535:["t0","All Methods"],1:["t1","Static Methods"],8:["t4","Concrete Methods"]};
|
var tabs = {65535:["t0","All Methods"],1:["t1","Static Methods"],8:["t4","Concrete Methods"]};
|
||||||
var altColor = "altColor";
|
var altColor = "altColor";
|
||||||
var rowColor = "rowColor";
|
var rowColor = "rowColor";
|
||||||
|
|
@ -360,6 +360,14 @@ extends <a href="https://developer.android.com/reference/java/lang/Object.html"
|
||||||
end-of-input is reached.</div>
|
end-of-input is reached.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr id="i25" class="rowColor">
|
||||||
|
<td class="colFirst"><code>static boolean</code></td>
|
||||||
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#timelinesAreSame(com.google.android.exoplayer2.Timeline,com.google.android.exoplayer2.Timeline)">timelinesAreSame</a></span>​(<a href="../Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a> thisTimeline,
|
||||||
|
<a href="../Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a> thatTimeline)</code></th>
|
||||||
|
<td class="colLast">
|
||||||
|
<div class="block">Returns true if <code>thisTimeline</code> is equal to <code>thatTimeline</code>, ignoring <a href="../Timeline.Window.html#uid"><code>Timeline.Window.uid</code></a> and <a href="../Timeline.Period.html#uid"><code>Timeline.Period.uid</code></a> values, and shuffle order.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
<ul class="blockList">
|
<ul class="blockList">
|
||||||
<li class="blockList"><a id="methods.inherited.from.class.java.lang.Object">
|
<li class="blockList"><a id="methods.inherited.from.class.java.lang.Object">
|
||||||
|
|
@ -618,8 +626,12 @@ extends <a href="https://developer.android.com/reference/java/lang/Object.html"
|
||||||
<pre class="methodSignature">public static void assertTimelinesSame​(<a href="https://developer.android.com/reference/java/util/List.html" title="class or interface in java.util" class="externalLink">List</a><<a href="../Timeline.html" title="class in com.google.android.exoplayer2" target="_top">Timeline</a>> actualTimelines,
|
<pre class="methodSignature">public static void assertTimelinesSame​(<a href="https://developer.android.com/reference/java/util/List.html" title="class or interface in java.util" class="externalLink">List</a><<a href="../Timeline.html" title="class in com.google.android.exoplayer2" target="_top">Timeline</a>> actualTimelines,
|
||||||
<a href="https://developer.android.com/reference/java/util/List.html" title="class or interface in java.util" class="externalLink">List</a><<a href="../Timeline.html" title="class in com.google.android.exoplayer2" target="_top">Timeline</a>> expectedTimelines)</pre>
|
<a href="https://developer.android.com/reference/java/util/List.html" title="class or interface in java.util" class="externalLink">List</a><<a href="../Timeline.html" title="class in com.google.android.exoplayer2" target="_top">Timeline</a>> expectedTimelines)</pre>
|
||||||
<div class="block">Asserts that the actual timelines are the same to the expected timelines. This assert differs
|
<div class="block">Asserts that the actual timelines are the same to the expected timelines. This assert differs
|
||||||
from testing equality by not comparing period ids which may be different due to id mapping of
|
from testing equality by not comparing:
|
||||||
child source period ids.</div>
|
|
||||||
|
<ul>
|
||||||
|
<li>Period IDs, which may be different due to ID mapping of child source period IDs.
|
||||||
|
<li>Shuffle order, which by default is random and non-deterministic.
|
||||||
|
</ul></div>
|
||||||
<dl>
|
<dl>
|
||||||
<dt><span class="paramLabel">Parameters:</span></dt>
|
<dt><span class="paramLabel">Parameters:</span></dt>
|
||||||
<dd><code>actualTimelines</code> - A list of actual <a href="../Timeline.html" title="class in com.google.android.exoplayer2"><code>timelines</code></a>.</dd>
|
<dd><code>actualTimelines</code> - A list of actual <a href="../Timeline.html" title="class in com.google.android.exoplayer2"><code>timelines</code></a>.</dd>
|
||||||
|
|
@ -627,6 +639,17 @@ extends <a href="https://developer.android.com/reference/java/lang/Object.html"
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<a id="timelinesAreSame(com.google.android.exoplayer2.Timeline,com.google.android.exoplayer2.Timeline)">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList">
|
||||||
|
<h4>timelinesAreSame</h4>
|
||||||
|
<pre class="methodSignature">public static boolean timelinesAreSame​(<a href="../Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a> thisTimeline,
|
||||||
|
<a href="../Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a> thatTimeline)</pre>
|
||||||
|
<div class="block">Returns true if <code>thisTimeline</code> is equal to <code>thatTimeline</code>, ignoring <a href="../Timeline.Window.html#uid"><code>Timeline.Window.uid</code></a> and <a href="../Timeline.Period.html#uid"><code>Timeline.Period.uid</code></a> values, and shuffle order.</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<a id="assertDataSourceContent(com.google.android.exoplayer2.upstream.DataSource,com.google.android.exoplayer2.upstream.DataSpec,byte[],boolean)">
|
<a id="assertDataSourceContent(com.google.android.exoplayer2.upstream.DataSource,com.google.android.exoplayer2.upstream.DataSpec,byte[],boolean)">
|
||||||
<!-- -->
|
<!-- -->
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -800,57 +800,51 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="altColor">
|
<tr class="altColor">
|
||||||
<th class="colFirst" scope="row"><a href="NoUidTimeline.html" title="class in com.google.android.exoplayer2.testutil">NoUidTimeline</a></th>
|
|
||||||
<td class="colLast">
|
|
||||||
<div class="block">A timeline which wraps another timeline and overrides all window and period uids to 0.</div>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="rowColor">
|
|
||||||
<th class="colFirst" scope="row"><a href="StubExoPlayer.html" title="class in com.google.android.exoplayer2.testutil">StubExoPlayer</a></th>
|
<th class="colFirst" scope="row"><a href="StubExoPlayer.html" title="class in com.google.android.exoplayer2.testutil">StubExoPlayer</a></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">An abstract <a href="../ExoPlayer.html" title="interface in com.google.android.exoplayer2"><code>ExoPlayer</code></a> implementation that throws <a href="https://developer.android.com/reference/java/lang/UnsupportedOperationException.html" title="class or interface in java.lang" class="externalLink" target="_top"><code>UnsupportedOperationException</code></a>
|
<div class="block">An abstract <a href="../ExoPlayer.html" title="interface in com.google.android.exoplayer2"><code>ExoPlayer</code></a> implementation that throws <a href="https://developer.android.com/reference/java/lang/UnsupportedOperationException.html" title="class or interface in java.lang" class="externalLink" target="_top"><code>UnsupportedOperationException</code></a>
|
||||||
from every method.</div>
|
from every method.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="altColor">
|
<tr class="rowColor">
|
||||||
<th class="colFirst" scope="row"><a href="StubPlayer.html" title="class in com.google.android.exoplayer2.testutil">StubPlayer</a></th>
|
<th class="colFirst" scope="row"><a href="StubPlayer.html" title="class in com.google.android.exoplayer2.testutil">StubPlayer</a></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">An abstract <a href="../Player.html" title="interface in com.google.android.exoplayer2"><code>Player</code></a> implementation that throws <a href="https://developer.android.com/reference/java/lang/UnsupportedOperationException.html" title="class or interface in java.lang" class="externalLink" target="_top"><code>UnsupportedOperationException</code></a> from
|
<div class="block">An abstract <a href="../Player.html" title="interface in com.google.android.exoplayer2"><code>Player</code></a> implementation that throws <a href="https://developer.android.com/reference/java/lang/UnsupportedOperationException.html" title="class or interface in java.lang" class="externalLink" target="_top"><code>UnsupportedOperationException</code></a> from
|
||||||
every method.</div>
|
every method.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="rowColor">
|
<tr class="altColor">
|
||||||
<th class="colFirst" scope="row"><a href="TestExoPlayerBuilder.html" title="class in com.google.android.exoplayer2.testutil">TestExoPlayerBuilder</a></th>
|
<th class="colFirst" scope="row"><a href="TestExoPlayerBuilder.html" title="class in com.google.android.exoplayer2.testutil">TestExoPlayerBuilder</a></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">A builder of <a href="../ExoPlayer.html" title="interface in com.google.android.exoplayer2"><code>ExoPlayer</code></a> instances for testing.</div>
|
<div class="block">A builder of <a href="../ExoPlayer.html" title="interface in com.google.android.exoplayer2"><code>ExoPlayer</code></a> instances for testing.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="altColor">
|
<tr class="rowColor">
|
||||||
<th class="colFirst" scope="row"><a href="TestUtil.html" title="class in com.google.android.exoplayer2.testutil">TestUtil</a></th>
|
<th class="colFirst" scope="row"><a href="TestUtil.html" title="class in com.google.android.exoplayer2.testutil">TestUtil</a></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Utility methods for tests.</div>
|
<div class="block">Utility methods for tests.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="rowColor">
|
<tr class="altColor">
|
||||||
<th class="colFirst" scope="row"><a href="TimelineAsserts.html" title="class in com.google.android.exoplayer2.testutil">TimelineAsserts</a></th>
|
<th class="colFirst" scope="row"><a href="TimelineAsserts.html" title="class in com.google.android.exoplayer2.testutil">TimelineAsserts</a></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Assertion methods for <a href="../Timeline.html" title="class in com.google.android.exoplayer2"><code>Timeline</code></a>.</div>
|
<div class="block">Assertion methods for <a href="../Timeline.html" title="class in com.google.android.exoplayer2"><code>Timeline</code></a>.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="altColor">
|
<tr class="rowColor">
|
||||||
<th class="colFirst" scope="row"><a href="WebServerDispatcher.html" title="class in com.google.android.exoplayer2.testutil">WebServerDispatcher</a></th>
|
<th class="colFirst" scope="row"><a href="WebServerDispatcher.html" title="class in com.google.android.exoplayer2.testutil">WebServerDispatcher</a></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">A <code>Dispatcher</code> for <code>MockWebServer</code> that allows per-path
|
<div class="block">A <code>Dispatcher</code> for <code>MockWebServer</code> that allows per-path
|
||||||
customisation of the static data served.</div>
|
customisation of the static data served.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="rowColor">
|
<tr class="altColor">
|
||||||
<th class="colFirst" scope="row"><a href="WebServerDispatcher.Resource.html" title="class in com.google.android.exoplayer2.testutil">WebServerDispatcher.Resource</a></th>
|
<th class="colFirst" scope="row"><a href="WebServerDispatcher.Resource.html" title="class in com.google.android.exoplayer2.testutil">WebServerDispatcher.Resource</a></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">A resource served by <a href="WebServerDispatcher.html" title="class in com.google.android.exoplayer2.testutil"><code>WebServerDispatcher</code></a>.</div>
|
<div class="block">A resource served by <a href="WebServerDispatcher.html" title="class in com.google.android.exoplayer2.testutil"><code>WebServerDispatcher</code></a>.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="altColor">
|
<tr class="rowColor">
|
||||||
<th class="colFirst" scope="row"><a href="WebServerDispatcher.Resource.Builder.html" title="class in com.google.android.exoplayer2.testutil">WebServerDispatcher.Resource.Builder</a></th>
|
<th class="colFirst" scope="row"><a href="WebServerDispatcher.Resource.Builder.html" title="class in com.google.android.exoplayer2.testutil">WebServerDispatcher.Resource.Builder</a></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Builder for <a href="WebServerDispatcher.Resource.html" title="class in com.google.android.exoplayer2.testutil"><code>WebServerDispatcher.Resource</code></a>.</div>
|
<div class="block">Builder for <a href="WebServerDispatcher.Resource.html" title="class in com.google.android.exoplayer2.testutil"><code>WebServerDispatcher.Resource</code></a>.</div>
|
||||||
|
|
|
||||||
|
|
@ -304,7 +304,6 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<li class="circle">com.google.android.exoplayer2.source.<a href="../source/ForwardingTimeline.html" title="class in com.google.android.exoplayer2.source"><span class="typeNameLink">ForwardingTimeline</span></a>
|
<li class="circle">com.google.android.exoplayer2.source.<a href="../source/ForwardingTimeline.html" title="class in com.google.android.exoplayer2.source"><span class="typeNameLink">ForwardingTimeline</span></a>
|
||||||
<ul>
|
<ul>
|
||||||
<li class="circle">com.google.android.exoplayer2.testutil.<a href="FakeMediaSource.InitialTimeline.html" title="class in com.google.android.exoplayer2.testutil"><span class="typeNameLink">FakeMediaSource.InitialTimeline</span></a></li>
|
<li class="circle">com.google.android.exoplayer2.testutil.<a href="FakeMediaSource.InitialTimeline.html" title="class in com.google.android.exoplayer2.testutil"><span class="typeNameLink">FakeMediaSource.InitialTimeline</span></a></li>
|
||||||
<li class="circle">com.google.android.exoplayer2.testutil.<a href="NoUidTimeline.html" title="class in com.google.android.exoplayer2.testutil"><span class="typeNameLink">NoUidTimeline</span></a></li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ default void onCues​(<a href="https://developer.android.com/refe
|
||||||
<div class="block">Called when there is a change in the <a href="CueGroup.html" title="class in com.google.android.exoplayer2.text"><code>CueGroup</code></a>.
|
<div class="block">Called when there is a change in the <a href="CueGroup.html" title="class in com.google.android.exoplayer2.text"><code>CueGroup</code></a>.
|
||||||
|
|
||||||
<p>Both <a href="#onCues(java.util.List)"><code>onCues(List)</code></a> and <a href="#onCues(com.google.android.exoplayer2.text.CueGroup)"><code>onCues(CueGroup)</code></a> are called when there is a change
|
<p>Both <a href="#onCues(java.util.List)"><code>onCues(List)</code></a> and <a href="#onCues(com.google.android.exoplayer2.text.CueGroup)"><code>onCues(CueGroup)</code></a> are called when there is a change
|
||||||
in the cues You should only implement one or the other.</div>
|
in the cues. You should only implement one or the other.</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,380 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<!-- NewPage -->
|
||||||
|
<html lang="en">
|
||||||
|
<head><!-- start favicons snippet, use https://realfavicongenerator.net/ --><link rel="apple-touch-icon" sizes="180x180" href="/assets/apple-touch-icon.png"><link rel="icon" type="image/png" sizes="32x32" href="/assets/favicon-32x32.png"><link rel="icon" type="image/png" sizes="16x16" href="/assets/favicon-16x16.png"><link rel="manifest" href="/assets/site.webmanifest"><link rel="mask-icon" href="/assets/safari-pinned-tab.svg" color="#fc4d50"><link rel="shortcut icon" href="/assets/favicon.ico"><meta name="msapplication-TileColor" content="#ffc40d"><meta name="msapplication-config" content="/assets/browserconfig.xml"><meta name="theme-color" content="#ffffff"><!-- end favicons snippet -->
|
||||||
|
<title>Log.Logger (ExoPlayer library)</title>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../../../../stylesheet.css" title="Style">
|
||||||
|
<link rel="stylesheet" type="text/css" href="../../../../../jquery/jquery-ui.css" title="Style">
|
||||||
|
<script type="text/javascript" src="../../../../../script.js"></script>
|
||||||
|
<script type="text/javascript" src="../../../../../jquery/jszip/dist/jszip.min.js"></script>
|
||||||
|
<script type="text/javascript" src="../../../../../jquery/jszip-utils/dist/jszip-utils.min.js"></script>
|
||||||
|
<!--[if IE]>
|
||||||
|
<script type="text/javascript" src="../../../../../jquery/jszip-utils/dist/jszip-utils-ie.min.js"></script>
|
||||||
|
<![endif]-->
|
||||||
|
<script type="text/javascript" src="../../../../../jquery/jquery-3.5.1.js"></script>
|
||||||
|
<script type="text/javascript" src="../../../../../jquery/jquery-ui.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<script type="text/javascript"><!--
|
||||||
|
try {
|
||||||
|
if (location.href.indexOf('is-external=true') == -1) {
|
||||||
|
parent.document.title="Log.Logger (ExoPlayer library)";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
}
|
||||||
|
//-->
|
||||||
|
var data = {"i0":6,"i1":6,"i2":6,"i3":6};
|
||||||
|
var tabs = {65535:["t0","All Methods"],2:["t2","Instance Methods"],4:["t3","Abstract Methods"]};
|
||||||
|
var altColor = "altColor";
|
||||||
|
var rowColor = "rowColor";
|
||||||
|
var tableTab = "tableTab";
|
||||||
|
var activeTableTab = "activeTableTab";
|
||||||
|
var pathtoroot = "../../../../../";
|
||||||
|
var useModuleDirectories = false;
|
||||||
|
loadScripts(document, 'script');</script>
|
||||||
|
<noscript>
|
||||||
|
<div>JavaScript is disabled on your browser.</div>
|
||||||
|
</noscript>
|
||||||
|
<header role="banner">
|
||||||
|
<nav role="navigation">
|
||||||
|
<div class="fixedNav">
|
||||||
|
<!-- ========= START OF TOP NAVBAR ======= -->
|
||||||
|
<div class="topNav"><a id="navbar.top">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<div class="skipNav"><a href="#skip.navbar.top" title="Skip navigation links">Skip navigation links</a></div>
|
||||||
|
<a id="navbar.top.firstrow">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<ul class="navList" title="Navigation">
|
||||||
|
<li><a href="../../../../../index.html">Overview</a></li>
|
||||||
|
<li><a href="package-summary.html">Package</a></li>
|
||||||
|
<li class="navBarCell1Rev">Class</li>
|
||||||
|
<li><a href="package-tree.html">Tree</a></li>
|
||||||
|
<li><a href="../../../../../deprecated-list.html">Deprecated</a></li>
|
||||||
|
<li><a href="../../../../../index-all.html">Index</a></li>
|
||||||
|
<li><a href="../../../../../help-doc.html">Help</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="subNav">
|
||||||
|
<ul class="navList" id="allclasses_navbar_top">
|
||||||
|
<li><a href="../../../../../allclasses.html">All Classes</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul class="navListSearch">
|
||||||
|
<li><label for="search">SEARCH:</label>
|
||||||
|
<input type="text" id="search" value="search" disabled="disabled">
|
||||||
|
<input type="reset" id="reset" value="reset" disabled="disabled">
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div>
|
||||||
|
<script type="text/javascript"><!--
|
||||||
|
allClassesLink = document.getElementById("allclasses_navbar_top");
|
||||||
|
if(window==top) {
|
||||||
|
allClassesLink.style.display = "block";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
allClassesLink.style.display = "none";
|
||||||
|
}
|
||||||
|
//-->
|
||||||
|
</script>
|
||||||
|
<noscript>
|
||||||
|
<div>JavaScript is disabled on your browser.</div>
|
||||||
|
</noscript>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ul class="subNavList">
|
||||||
|
<li>Summary: </li>
|
||||||
|
<li>Nested | </li>
|
||||||
|
<li><a href="#field.summary">Field</a> | </li>
|
||||||
|
<li>Constr | </li>
|
||||||
|
<li><a href="#method.summary">Method</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul class="subNavList">
|
||||||
|
<li>Detail: </li>
|
||||||
|
<li><a href="#field.detail">Field</a> | </li>
|
||||||
|
<li>Constr | </li>
|
||||||
|
<li><a href="#method.detail">Method</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<a id="skip.navbar.top">
|
||||||
|
<!-- -->
|
||||||
|
</a></div>
|
||||||
|
<!-- ========= END OF TOP NAVBAR ========= -->
|
||||||
|
</div>
|
||||||
|
<div class="navPadding"> </div>
|
||||||
|
<script type="text/javascript"><!--
|
||||||
|
$('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
|
//-->
|
||||||
|
</script>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<!-- ======== START OF CLASS DATA ======== -->
|
||||||
|
<main role="main">
|
||||||
|
<div class="header">
|
||||||
|
<div class="subTitle"><span class="packageLabelInType">Package</span> <a href="package-summary.html">com.google.android.exoplayer2.util</a></div>
|
||||||
|
<h2 title="Interface Log.Logger" class="title">Interface Log.Logger</h2>
|
||||||
|
</div>
|
||||||
|
<div class="contentContainer">
|
||||||
|
<div class="description">
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList">
|
||||||
|
<dl>
|
||||||
|
<dt>Enclosing class:</dt>
|
||||||
|
<dd><a href="Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dd>
|
||||||
|
</dl>
|
||||||
|
<hr>
|
||||||
|
<pre>public static interface <span class="typeNameLabel">Log.Logger</span></pre>
|
||||||
|
<div class="block">Interface for a logger that can output messages with a tag.
|
||||||
|
|
||||||
|
<p>Use <a href="#DEFAULT"><code>DEFAULT</code></a> to output to <a href="https://developer.android.com/reference/android/util/Log.html" title="class or interface in android.util" class="externalLink" target="_top"><code>Log</code></a>.</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="summary">
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList">
|
||||||
|
<!-- =========== FIELD SUMMARY =========== -->
|
||||||
|
<section role="region">
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList"><a id="field.summary">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<h3>Field Summary</h3>
|
||||||
|
<table class="memberSummary">
|
||||||
|
<caption><span>Fields</span><span class="tabEnd"> </span></caption>
|
||||||
|
<tr>
|
||||||
|
<th class="colFirst" scope="col">Modifier and Type</th>
|
||||||
|
<th class="colSecond" scope="col">Field</th>
|
||||||
|
<th class="colLast" scope="col">Description</th>
|
||||||
|
</tr>
|
||||||
|
<tr class="altColor">
|
||||||
|
<td class="colFirst"><code>static <a href="Log.Logger.html" title="interface in com.google.android.exoplayer2.util">Log.Logger</a></code></td>
|
||||||
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#DEFAULT">DEFAULT</a></span></code></th>
|
||||||
|
<td class="colLast">
|
||||||
|
<div class="block">The default instance logging to <a href="https://developer.android.com/reference/android/util/Log.html" title="class or interface in android.util" class="externalLink" target="_top"><code>Log</code></a>.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<!-- ========== METHOD SUMMARY =========== -->
|
||||||
|
<section role="region">
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList"><a id="method.summary">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<h3>Method Summary</h3>
|
||||||
|
<table class="memberSummary">
|
||||||
|
<caption><span id="t0" class="activeTableTab"><span>All Methods</span><span class="tabEnd"> </span></span><span id="t2" class="tableTab"><span><a href="javascript:show(2);">Instance Methods</a></span><span class="tabEnd"> </span></span><span id="t3" class="tableTab"><span><a href="javascript:show(4);">Abstract Methods</a></span><span class="tabEnd"> </span></span></caption>
|
||||||
|
<tr>
|
||||||
|
<th class="colFirst" scope="col">Modifier and Type</th>
|
||||||
|
<th class="colSecond" scope="col">Method</th>
|
||||||
|
<th class="colLast" scope="col">Description</th>
|
||||||
|
</tr>
|
||||||
|
<tr id="i0" class="altColor">
|
||||||
|
<td class="colFirst"><code>void</code></td>
|
||||||
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#d(java.lang.String,java.lang.String)">d</a></span>​(<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> tag,
|
||||||
|
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> message)</code></th>
|
||||||
|
<td class="colLast">
|
||||||
|
<div class="block">Logs a debug-level message.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id="i1" class="rowColor">
|
||||||
|
<td class="colFirst"><code>void</code></td>
|
||||||
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#e(java.lang.String,java.lang.String)">e</a></span>​(<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> tag,
|
||||||
|
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> message)</code></th>
|
||||||
|
<td class="colLast">
|
||||||
|
<div class="block">Logs an error-level message.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id="i2" class="altColor">
|
||||||
|
<td class="colFirst"><code>void</code></td>
|
||||||
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#i(java.lang.String,java.lang.String)">i</a></span>​(<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> tag,
|
||||||
|
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> message)</code></th>
|
||||||
|
<td class="colLast">
|
||||||
|
<div class="block">Logs an information-level message.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id="i3" class="rowColor">
|
||||||
|
<td class="colFirst"><code>void</code></td>
|
||||||
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#w(java.lang.String,java.lang.String)">w</a></span>​(<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> tag,
|
||||||
|
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> message)</code></th>
|
||||||
|
<td class="colLast">
|
||||||
|
<div class="block">Logs a warning-level message.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="details">
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList">
|
||||||
|
<!-- ============ FIELD DETAIL =========== -->
|
||||||
|
<section role="region">
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList"><a id="field.detail">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<h3>Field Detail</h3>
|
||||||
|
<a id="DEFAULT">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<ul class="blockListLast">
|
||||||
|
<li class="blockList">
|
||||||
|
<h4>DEFAULT</h4>
|
||||||
|
<pre>static final <a href="Log.Logger.html" title="interface in com.google.android.exoplayer2.util">Log.Logger</a> DEFAULT</pre>
|
||||||
|
<div class="block">The default instance logging to <a href="https://developer.android.com/reference/android/util/Log.html" title="class or interface in android.util" class="externalLink" target="_top"><code>Log</code></a>.</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
<!-- ============ METHOD DETAIL ========== -->
|
||||||
|
<section role="region">
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList"><a id="method.detail">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<h3>Method Detail</h3>
|
||||||
|
<a id="d(java.lang.String,java.lang.String)">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList">
|
||||||
|
<h4>d</h4>
|
||||||
|
<pre class="methodSignature">void d​(<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> tag,
|
||||||
|
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> message)</pre>
|
||||||
|
<div class="block">Logs a debug-level message.</div>
|
||||||
|
<dl>
|
||||||
|
<dt><span class="paramLabel">Parameters:</span></dt>
|
||||||
|
<dd><code>tag</code> - The tag of the message.</dd>
|
||||||
|
<dd><code>message</code> - The message.</dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<a id="i(java.lang.String,java.lang.String)">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList">
|
||||||
|
<h4>i</h4>
|
||||||
|
<pre class="methodSignature">void i​(<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> tag,
|
||||||
|
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> message)</pre>
|
||||||
|
<div class="block">Logs an information-level message.</div>
|
||||||
|
<dl>
|
||||||
|
<dt><span class="paramLabel">Parameters:</span></dt>
|
||||||
|
<dd><code>tag</code> - The tag of the message.</dd>
|
||||||
|
<dd><code>message</code> - The message.</dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<a id="w(java.lang.String,java.lang.String)">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList">
|
||||||
|
<h4>w</h4>
|
||||||
|
<pre class="methodSignature">void w​(<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> tag,
|
||||||
|
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> message)</pre>
|
||||||
|
<div class="block">Logs a warning-level message.</div>
|
||||||
|
<dl>
|
||||||
|
<dt><span class="paramLabel">Parameters:</span></dt>
|
||||||
|
<dd><code>tag</code> - The tag of the message.</dd>
|
||||||
|
<dd><code>message</code> - The message.</dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<a id="e(java.lang.String,java.lang.String)">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<ul class="blockListLast">
|
||||||
|
<li class="blockList">
|
||||||
|
<h4>e</h4>
|
||||||
|
<pre class="methodSignature">void e​(<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> tag,
|
||||||
|
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> message)</pre>
|
||||||
|
<div class="block">Logs an error-level message.</div>
|
||||||
|
<dl>
|
||||||
|
<dt><span class="paramLabel">Parameters:</span></dt>
|
||||||
|
<dd><code>tag</code> - The tag of the message.</dd>
|
||||||
|
<dd><code>message</code> - The message.</dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</section>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<!-- ========= END OF CLASS DATA ========= -->
|
||||||
|
<footer role="contentinfo">
|
||||||
|
<nav role="navigation">
|
||||||
|
<!-- ======= START OF BOTTOM NAVBAR ====== -->
|
||||||
|
<div class="bottomNav"><a id="navbar.bottom">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<div class="skipNav"><a href="#skip.navbar.bottom" title="Skip navigation links">Skip navigation links</a></div>
|
||||||
|
<a id="navbar.bottom.firstrow">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<ul class="navList" title="Navigation">
|
||||||
|
<li><a href="../../../../../index.html">Overview</a></li>
|
||||||
|
<li><a href="package-summary.html">Package</a></li>
|
||||||
|
<li class="navBarCell1Rev">Class</li>
|
||||||
|
<li><a href="package-tree.html">Tree</a></li>
|
||||||
|
<li><a href="../../../../../deprecated-list.html">Deprecated</a></li>
|
||||||
|
<li><a href="../../../../../index-all.html">Index</a></li>
|
||||||
|
<li><a href="../../../../../help-doc.html">Help</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="subNav">
|
||||||
|
<ul class="navList" id="allclasses_navbar_bottom">
|
||||||
|
<li><a href="../../../../../allclasses.html">All Classes</a></li>
|
||||||
|
</ul>
|
||||||
|
<div>
|
||||||
|
<script type="text/javascript"><!--
|
||||||
|
allClassesLink = document.getElementById("allclasses_navbar_bottom");
|
||||||
|
if(window==top) {
|
||||||
|
allClassesLink.style.display = "block";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
allClassesLink.style.display = "none";
|
||||||
|
}
|
||||||
|
//-->
|
||||||
|
</script>
|
||||||
|
<noscript>
|
||||||
|
<div>JavaScript is disabled on your browser.</div>
|
||||||
|
</noscript>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<ul class="subNavList">
|
||||||
|
<li>Summary: </li>
|
||||||
|
<li>Nested | </li>
|
||||||
|
<li><a href="#field.summary">Field</a> | </li>
|
||||||
|
<li>Constr | </li>
|
||||||
|
<li><a href="#method.summary">Method</a></li>
|
||||||
|
</ul>
|
||||||
|
<ul class="subNavList">
|
||||||
|
<li>Detail: </li>
|
||||||
|
<li><a href="#field.detail">Field</a> | </li>
|
||||||
|
<li>Constr | </li>
|
||||||
|
<li><a href="#method.detail">Method</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<a id="skip.navbar.bottom">
|
||||||
|
<!-- -->
|
||||||
|
</a></div>
|
||||||
|
<!-- ======== END OF BOTTOM NAVBAR ======= -->
|
||||||
|
</nav>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
catch(err) {
|
catch(err) {
|
||||||
}
|
}
|
||||||
//-->
|
//-->
|
||||||
var data = {"i0":9,"i1":9,"i2":9,"i3":9,"i4":9,"i5":9,"i6":9,"i7":9,"i8":9,"i9":9,"i10":9,"i11":9};
|
var data = {"i0":9,"i1":9,"i2":9,"i3":9,"i4":9,"i5":9,"i6":9,"i7":9,"i8":9,"i9":9,"i10":9,"i11":9,"i12":9};
|
||||||
var tabs = {65535:["t0","All Methods"],1:["t1","Static Methods"],8:["t4","Concrete Methods"]};
|
var tabs = {65535:["t0","All Methods"],1:["t1","Static Methods"],8:["t4","Concrete Methods"]};
|
||||||
var altColor = "altColor";
|
var altColor = "altColor";
|
||||||
var rowColor = "rowColor";
|
var rowColor = "rowColor";
|
||||||
|
|
@ -131,7 +131,8 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<hr>
|
<hr>
|
||||||
<pre>public final class <span class="typeNameLabel">Log</span>
|
<pre>public final class <span class="typeNameLabel">Log</span>
|
||||||
extends <a href="https://developer.android.com/reference/java/lang/Object.html" title="class or interface in java.lang" class="externalLink" target="_top">Object</a></pre>
|
extends <a href="https://developer.android.com/reference/java/lang/Object.html" title="class or interface in java.lang" class="externalLink" target="_top">Object</a></pre>
|
||||||
<div class="block">Wrapper around <a href="https://developer.android.com/reference/android/util/Log.html" title="class or interface in android.util" class="externalLink" target="_top"><code>Log</code></a> which allows to set the log level.</div>
|
<div class="block">Wrapper around <a href="https://developer.android.com/reference/android/util/Log.html" title="class or interface in android.util" class="externalLink" target="_top"><code>Log</code></a> which allows to set the log level and to specify a custom
|
||||||
|
log output.</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -154,6 +155,13 @@ extends <a href="https://developer.android.com/reference/java/lang/Object.html"
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="altColor">
|
<tr class="altColor">
|
||||||
<td class="colFirst"><code>static interface </code></td>
|
<td class="colFirst"><code>static interface </code></td>
|
||||||
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="Log.Logger.html" title="interface in com.google.android.exoplayer2.util">Log.Logger</a></span></code></th>
|
||||||
|
<td class="colLast">
|
||||||
|
<div class="block">Interface for a logger that can output messages with a tag.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="rowColor">
|
||||||
|
<td class="colFirst"><code>static interface </code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="Log.LogLevel.html" title="annotation in com.google.android.exoplayer2.util">Log.LogLevel</a></span></code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="Log.LogLevel.html" title="annotation in com.google.android.exoplayer2.util">Log.LogLevel</a></span></code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Log level for ExoPlayer logcat logging.</div>
|
<div class="block">Log level for ExoPlayer logcat logging.</div>
|
||||||
|
|
@ -286,25 +294,32 @@ extends <a href="https://developer.android.com/reference/java/lang/Object.html"
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i8" class="altColor">
|
<tr id="i8" class="altColor">
|
||||||
<td class="colFirst"><code>static void</code></td>
|
<td class="colFirst"><code>static void</code></td>
|
||||||
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setLogger(com.google.android.exoplayer2.util.Log.Logger)">setLogger</a></span>​(<a href="Log.Logger.html" title="interface in com.google.android.exoplayer2.util">Log.Logger</a> logger)</code></th>
|
||||||
|
<td class="colLast">
|
||||||
|
<div class="block">Sets a custom <a href="Log.Logger.html" title="interface in com.google.android.exoplayer2.util"><code>Log.Logger</code></a> as the output.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr id="i9" class="rowColor">
|
||||||
|
<td class="colFirst"><code>static void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setLogLevel(@com.google.android.exoplayer2.util.Log.LogLevelint)">setLogLevel</a></span>​(@com.google.android.exoplayer2.util.Log.LogLevel int logLevel)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setLogLevel(@com.google.android.exoplayer2.util.Log.LogLevelint)">setLogLevel</a></span>​(@com.google.android.exoplayer2.util.Log.LogLevel int logLevel)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Sets the <a href="Log.LogLevel.html" title="annotation in com.google.android.exoplayer2.util"><code>Log.LogLevel</code></a> for ExoPlayer logcat logging.</div>
|
<div class="block">Sets the <a href="Log.LogLevel.html" title="annotation in com.google.android.exoplayer2.util"><code>Log.LogLevel</code></a> for ExoPlayer logcat logging.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i9" class="rowColor">
|
<tr id="i10" class="altColor">
|
||||||
<td class="colFirst"><code>static void</code></td>
|
<td class="colFirst"><code>static void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setLogStackTraces(boolean)">setLogStackTraces</a></span>​(boolean logStackTraces)</code></th>
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#setLogStackTraces(boolean)">setLogStackTraces</a></span>​(boolean logStackTraces)</code></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Sets whether stack traces of <a href="https://developer.android.com/reference/java/lang/Throwable.html" title="class or interface in java.lang" class="externalLink" target="_top"><code>Throwable</code></a>s will be logged to logcat.</div>
|
<div class="block">Sets whether stack traces of <a href="https://developer.android.com/reference/java/lang/Throwable.html" title="class or interface in java.lang" class="externalLink" target="_top"><code>Throwable</code></a>s will be logged to logcat.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i10" class="altColor">
|
<tr id="i11" class="rowColor">
|
||||||
<td class="colFirst"><code>static void</code></td>
|
<td class="colFirst"><code>static void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#w(java.lang.String,java.lang.String)">w</a></span>​(<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> tag,
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#w(java.lang.String,java.lang.String)">w</a></span>​(<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> tag,
|
||||||
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> message)</code></th>
|
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> message)</code></th>
|
||||||
<td class="colLast"> </td>
|
<td class="colLast"> </td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr id="i11" class="rowColor">
|
<tr id="i12" class="altColor">
|
||||||
<td class="colFirst"><code>static void</code></td>
|
<td class="colFirst"><code>static void</code></td>
|
||||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#w(java.lang.String,java.lang.String,java.lang.Throwable)">w</a></span>​(<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> tag,
|
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#w(java.lang.String,java.lang.String,java.lang.Throwable)">w</a></span>​(<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> tag,
|
||||||
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> message,
|
<a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a> message,
|
||||||
|
|
@ -455,6 +470,20 @@ public static @com.google.android.exoplayer2.util.Log.LogLevel int get
|
||||||
</dl>
|
</dl>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<a id="setLogger(com.google.android.exoplayer2.util.Log.Logger)">
|
||||||
|
<!-- -->
|
||||||
|
</a>
|
||||||
|
<ul class="blockList">
|
||||||
|
<li class="blockList">
|
||||||
|
<h4>setLogger</h4>
|
||||||
|
<pre class="methodSignature">public static void setLogger​(<a href="Log.Logger.html" title="interface in com.google.android.exoplayer2.util">Log.Logger</a> logger)</pre>
|
||||||
|
<div class="block">Sets a custom <a href="Log.Logger.html" title="interface in com.google.android.exoplayer2.util"><code>Log.Logger</code></a> as the output.</div>
|
||||||
|
<dl>
|
||||||
|
<dt><span class="paramLabel">Parameters:</span></dt>
|
||||||
|
<dd><code>logger</code> - The <a href="Log.Logger.html" title="interface in com.google.android.exoplayer2.util"><code>Log.Logger</code></a>.</dd>
|
||||||
|
</dl>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
<a id="d(java.lang.String,java.lang.String)">
|
<a id="d(java.lang.String,java.lang.String)">
|
||||||
<!-- -->
|
<!-- -->
|
||||||
</a>
|
</a>
|
||||||
|
|
|
||||||
|
|
@ -153,18 +153,24 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="altColor">
|
<tr class="altColor">
|
||||||
|
<th class="colFirst" scope="row"><a href="Log.Logger.html" title="interface in com.google.android.exoplayer2.util">Log.Logger</a></th>
|
||||||
|
<td class="colLast">
|
||||||
|
<div class="block">Interface for a logger that can output messages with a tag.</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="rowColor">
|
||||||
<th class="colFirst" scope="row"><a href="MediaClock.html" title="interface in com.google.android.exoplayer2.util">MediaClock</a></th>
|
<th class="colFirst" scope="row"><a href="MediaClock.html" title="interface in com.google.android.exoplayer2.util">MediaClock</a></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Tracks the progression of media time.</div>
|
<div class="block">Tracks the progression of media time.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="rowColor">
|
<tr class="altColor">
|
||||||
<th class="colFirst" scope="row"><a href="NetworkTypeObserver.Listener.html" title="interface in com.google.android.exoplayer2.util">NetworkTypeObserver.Listener</a></th>
|
<th class="colFirst" scope="row"><a href="NetworkTypeObserver.Listener.html" title="interface in com.google.android.exoplayer2.util">NetworkTypeObserver.Listener</a></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">A listener for network type changes.</div>
|
<div class="block">A listener for network type changes.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="altColor">
|
<tr class="rowColor">
|
||||||
<th class="colFirst" scope="row"><a href="SntpClient.InitializationCallback.html" title="interface in com.google.android.exoplayer2.util">SntpClient.InitializationCallback</a></th>
|
<th class="colFirst" scope="row"><a href="SntpClient.InitializationCallback.html" title="interface in com.google.android.exoplayer2.util">SntpClient.InitializationCallback</a></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Callback for calls to <a href="SntpClient.html#initialize(com.google.android.exoplayer2.upstream.Loader,com.google.android.exoplayer2.util.SntpClient.InitializationCallback)"><code>SntpClient.initialize(Loader, InitializationCallback)</code></a>.</div>
|
<div class="block">Callback for calls to <a href="SntpClient.html#initialize(com.google.android.exoplayer2.upstream.Loader,com.google.android.exoplayer2.util.SntpClient.InitializationCallback)"><code>SntpClient.initialize(Loader, InitializationCallback)</code></a>.</div>
|
||||||
|
|
@ -295,7 +301,8 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<tr class="altColor">
|
<tr class="altColor">
|
||||||
<th class="colFirst" scope="row"><a href="Log.html" title="class in com.google.android.exoplayer2.util">Log</a></th>
|
<th class="colFirst" scope="row"><a href="Log.html" title="class in com.google.android.exoplayer2.util">Log</a></th>
|
||||||
<td class="colLast">
|
<td class="colLast">
|
||||||
<div class="block">Wrapper around <a href="https://developer.android.com/reference/android/util/Log.html" title="class or interface in android.util" class="externalLink" target="_top"><code>Log</code></a> which allows to set the log level.</div>
|
<div class="block">Wrapper around <a href="https://developer.android.com/reference/android/util/Log.html" title="class or interface in android.util" class="externalLink" target="_top"><code>Log</code></a> which allows to set the log level and to specify a custom
|
||||||
|
log output.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="rowColor">
|
<tr class="rowColor">
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,7 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<li class="circle">com.google.android.exoplayer2.util.<a href="HandlerWrapper.Message.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">HandlerWrapper.Message</span></a></li>
|
<li class="circle">com.google.android.exoplayer2.util.<a href="HandlerWrapper.Message.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">HandlerWrapper.Message</span></a></li>
|
||||||
<li class="circle">com.google.android.exoplayer2.util.<a href="ListenerSet.Event.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">ListenerSet.Event</span></a><T></li>
|
<li class="circle">com.google.android.exoplayer2.util.<a href="ListenerSet.Event.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">ListenerSet.Event</span></a><T></li>
|
||||||
<li class="circle">com.google.android.exoplayer2.util.<a href="ListenerSet.IterationFinishedEvent.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">ListenerSet.IterationFinishedEvent</span></a><T></li>
|
<li class="circle">com.google.android.exoplayer2.util.<a href="ListenerSet.IterationFinishedEvent.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">ListenerSet.IterationFinishedEvent</span></a><T></li>
|
||||||
|
<li class="circle">com.google.android.exoplayer2.util.<a href="Log.Logger.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">Log.Logger</span></a></li>
|
||||||
<li class="circle">com.google.android.exoplayer2.util.<a href="MediaClock.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">MediaClock</span></a></li>
|
<li class="circle">com.google.android.exoplayer2.util.<a href="MediaClock.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">MediaClock</span></a></li>
|
||||||
<li class="circle">com.google.android.exoplayer2.util.<a href="NetworkTypeObserver.Listener.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">NetworkTypeObserver.Listener</span></a></li>
|
<li class="circle">com.google.android.exoplayer2.util.<a href="NetworkTypeObserver.Listener.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">NetworkTypeObserver.Listener</span></a></li>
|
||||||
<li class="circle">com.google.android.exoplayer2.util.<a href="SntpClient.InitializationCallback.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">SntpClient.InitializationCallback</span></a></li>
|
<li class="circle">com.google.android.exoplayer2.util.<a href="SntpClient.InitializationCallback.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">SntpClient.InitializationCallback</span></a></li>
|
||||||
|
|
|
||||||
|
|
@ -1939,21 +1939,21 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<!-- -->
|
<!-- -->
|
||||||
</a><code>public static final <a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a></code></td>
|
</a><code>public static final <a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a></code></td>
|
||||||
<th class="colSecond" scope="row"><code><a href="com/google/android/exoplayer2/ExoPlayerLibraryInfo.html#VERSION">VERSION</a></code></th>
|
<th class="colSecond" scope="row"><code><a href="com/google/android/exoplayer2/ExoPlayerLibraryInfo.html#VERSION">VERSION</a></code></th>
|
||||||
<td class="colLast"><code>"2.18.0"</code></td>
|
<td class="colLast"><code>"2.18.1"</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="altColor">
|
<tr class="altColor">
|
||||||
<td class="colFirst"><a id="com.google.android.exoplayer2.ExoPlayerLibraryInfo.VERSION_INT">
|
<td class="colFirst"><a id="com.google.android.exoplayer2.ExoPlayerLibraryInfo.VERSION_INT">
|
||||||
<!-- -->
|
<!-- -->
|
||||||
</a><code>public static final int</code></td>
|
</a><code>public static final int</code></td>
|
||||||
<th class="colSecond" scope="row"><code><a href="com/google/android/exoplayer2/ExoPlayerLibraryInfo.html#VERSION_INT">VERSION_INT</a></code></th>
|
<th class="colSecond" scope="row"><code><a href="com/google/android/exoplayer2/ExoPlayerLibraryInfo.html#VERSION_INT">VERSION_INT</a></code></th>
|
||||||
<td class="colLast"><code>2018000</code></td>
|
<td class="colLast"><code>2018001</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="rowColor">
|
<tr class="rowColor">
|
||||||
<td class="colFirst"><a id="com.google.android.exoplayer2.ExoPlayerLibraryInfo.VERSION_SLASHY">
|
<td class="colFirst"><a id="com.google.android.exoplayer2.ExoPlayerLibraryInfo.VERSION_SLASHY">
|
||||||
<!-- -->
|
<!-- -->
|
||||||
</a><code>public static final <a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a></code></td>
|
</a><code>public static final <a href="https://developer.android.com/reference/java/lang/String.html" title="class or interface in java.lang" class="externalLink" target="_top">String</a></code></td>
|
||||||
<th class="colSecond" scope="row"><code><a href="com/google/android/exoplayer2/ExoPlayerLibraryInfo.html#VERSION_SLASHY">VERSION_SLASHY</a></code></th>
|
<th class="colSecond" scope="row"><code><a href="com/google/android/exoplayer2/ExoPlayerLibraryInfo.html#VERSION_SLASHY">VERSION_SLASHY</a></code></th>
|
||||||
<td class="colLast"><code>"ExoPlayerLib/2.18.0"</code></td>
|
<td class="colLast"><code>"ExoPlayerLib/2.18.1"</code></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
|
||||||
|
|
@ -7322,6 +7322,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<dl>
|
<dl>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#d(java.lang.String,java.lang.String)">d(String, String)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#d(java.lang.String,java.lang.String)">d(String, String)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.Logger.html#d(java.lang.String,java.lang.String)">d(String, String)</a></span> - Method in interface com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.Logger.html" title="interface in com.google.android.exoplayer2.util">Log.Logger</a></dt>
|
||||||
|
<dd>
|
||||||
|
<div class="block">Logs a debug-level message.</div>
|
||||||
|
</dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#d(java.lang.String,java.lang.String,java.lang.Throwable)">d(String, String, Throwable)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#d(java.lang.String,java.lang.String,java.lang.Throwable)">d(String, String, Throwable)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
<dt><a href="com/google/android/exoplayer2/source/dash/DashChunkSource.html" title="interface in com.google.android.exoplayer2.source.dash"><span class="typeNameLink">DashChunkSource</span></a> - Interface in <a href="com/google/android/exoplayer2/source/dash/package-summary.html">com.google.android.exoplayer2.source.dash</a></dt>
|
<dt><a href="com/google/android/exoplayer2/source/dash/DashChunkSource.html" title="interface in com.google.android.exoplayer2.source.dash"><span class="typeNameLink">DashChunkSource</span></a> - Interface in <a href="com/google/android/exoplayer2/source/dash/package-summary.html">com.google.android.exoplayer2.source.dash</a></dt>
|
||||||
|
|
@ -8022,6 +8026,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Default <a href="com/google/android/exoplayer2/util/Clock.html" title="interface in com.google.android.exoplayer2.util"><code>Clock</code></a> to use for all non-test cases.</div>
|
<div class="block">Default <a href="com/google/android/exoplayer2/util/Clock.html" title="interface in com.google.android.exoplayer2.util"><code>Clock</code></a> to use for all non-test cases.</div>
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.Logger.html#DEFAULT">DEFAULT</a></span> - Static variable in interface com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.Logger.html" title="interface in com.google.android.exoplayer2.util">Log.Logger</a></dt>
|
||||||
|
<dd>
|
||||||
|
<div class="block">The default instance logging to <a href="https://developer.android.com/reference/android/util/Log.html" title="class or interface in android.util" class="externalLink" target="_top"><code>Log</code></a>.</div>
|
||||||
|
</dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/ui/DefaultTimeBar.html#DEFAULT_AD_MARKER_COLOR">DEFAULT_AD_MARKER_COLOR</a></span> - Static variable in class com.google.android.exoplayer2.ui.<a href="com/google/android/exoplayer2/ui/DefaultTimeBar.html" title="class in com.google.android.exoplayer2.ui">DefaultTimeBar</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/ui/DefaultTimeBar.html#DEFAULT_AD_MARKER_COLOR">DEFAULT_AD_MARKER_COLOR</a></span> - Static variable in class com.google.android.exoplayer2.ui.<a href="com/google/android/exoplayer2/ui/DefaultTimeBar.html" title="class in com.google.android.exoplayer2.ui">DefaultTimeBar</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Default color for ad markers.</div>
|
<div class="block">Default color for ad markers.</div>
|
||||||
|
|
@ -10214,6 +10222,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<dl>
|
<dl>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#e(java.lang.String,java.lang.String)">e(String, String)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#e(java.lang.String,java.lang.String)">e(String, String)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.Logger.html#e(java.lang.String,java.lang.String)">e(String, String)</a></span> - Method in interface com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.Logger.html" title="interface in com.google.android.exoplayer2.util">Log.Logger</a></dt>
|
||||||
|
<dd>
|
||||||
|
<div class="block">Logs an error-level message.</div>
|
||||||
|
</dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#e(java.lang.String,java.lang.String,java.lang.Throwable)">e(String, String, Throwable)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#e(java.lang.String,java.lang.String,java.lang.Throwable)">e(String, String, Throwable)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/audio/Ac3Util.html#E_AC3_MAX_RATE_BYTES_PER_SECOND">E_AC3_MAX_RATE_BYTES_PER_SECOND</a></span> - Static variable in class com.google.android.exoplayer2.audio.<a href="com/google/android/exoplayer2/audio/Ac3Util.html" title="class in com.google.android.exoplayer2.audio">Ac3Util</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/audio/Ac3Util.html#E_AC3_MAX_RATE_BYTES_PER_SECOND">E_AC3_MAX_RATE_BYTES_PER_SECOND</a></span> - Static variable in class com.google.android.exoplayer2.audio.<a href="com/google/android/exoplayer2/audio/Ac3Util.html" title="class in com.google.android.exoplayer2.audio">Ac3Util</a></dt>
|
||||||
|
|
@ -12643,6 +12655,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Creates a fake timeline with the given window definitions.</div>
|
<div class="block">Creates a fake timeline with the given window definitions.</div>
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/FakeTimeline.html#%3Cinit%3E(java.lang.Object%5B%5D,com.google.android.exoplayer2.source.ShuffleOrder,com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition...)">FakeTimeline(Object[], ShuffleOrder, FakeTimeline.TimelineWindowDefinition...)</a></span> - Constructor for class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/FakeTimeline.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline</a></dt>
|
||||||
|
<dd>
|
||||||
|
<div class="block">Creates a fake timeline with the given window definitions and <a href="com/google/android/exoplayer2/source/ShuffleOrder.html" title="interface in com.google.android.exoplayer2.source"><code>ShuffleOrder</code></a>.</div>
|
||||||
|
</dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/FakeTimeline.html#%3Cinit%3E(java.lang.Object%5B%5D,com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition...)">FakeTimeline(Object[], FakeTimeline.TimelineWindowDefinition...)</a></span> - Constructor for class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/FakeTimeline.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/FakeTimeline.html#%3Cinit%3E(java.lang.Object%5B%5D,com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition...)">FakeTimeline(Object[], FakeTimeline.TimelineWindowDefinition...)</a></span> - Constructor for class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/FakeTimeline.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Creates a fake timeline with the given window definitions.</div>
|
<div class="block">Creates a fake timeline with the given window definitions.</div>
|
||||||
|
|
@ -16650,6 +16666,8 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
</dd>
|
</dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/StubPlayer.html#getMediaMetadata()">getMediaMetadata()</a></span> - Method in class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/StubPlayer.html" title="class in com.google.android.exoplayer2.testutil">StubPlayer</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/StubPlayer.html#getMediaMetadata()">getMediaMetadata()</a></span> - Method in class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/StubPlayer.html" title="class in com.google.android.exoplayer2.testutil">StubPlayer</a></dt>
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/ext/cast/CastPlayer.html#getMediaMetadataInternal()">getMediaMetadataInternal()</a></span> - Method in class com.google.android.exoplayer2.ext.cast.<a href="com/google/android/exoplayer2/ext/cast/CastPlayer.html" title="class in com.google.android.exoplayer2.ext.cast">CastPlayer</a></dt>
|
||||||
|
<dd> </dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/MimeTypes.html#getMediaMimeType(java.lang.String)">getMediaMimeType(String)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/MimeTypes.html" title="class in com.google.android.exoplayer2.util">MimeTypes</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/MimeTypes.html#getMediaMimeType(java.lang.String)">getMediaMimeType(String)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/MimeTypes.html" title="class in com.google.android.exoplayer2.util">MimeTypes</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Returns the MIME type corresponding to an RFC 6381 codec string, or <code>null</code> if it could
|
<div class="block">Returns the MIME type corresponding to an RFC 6381 codec string, or <code>null</code> if it could
|
||||||
|
|
@ -17196,8 +17214,6 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/FakeTimeline.html#getPeriod(int,com.google.android.exoplayer2.Timeline.Period,boolean)">getPeriod(int, Timeline.Period, boolean)</a></span> - Method in class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/FakeTimeline.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/FakeTimeline.html#getPeriod(int,com.google.android.exoplayer2.Timeline.Period,boolean)">getPeriod(int, Timeline.Period, boolean)</a></span> - Method in class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/FakeTimeline.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline</a></dt>
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/NoUidTimeline.html#getPeriod(int,com.google.android.exoplayer2.Timeline.Period,boolean)">getPeriod(int, Timeline.Period, boolean)</a></span> - Method in class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/NoUidTimeline.html" title="class in com.google.android.exoplayer2.testutil">NoUidTimeline</a></dt>
|
|
||||||
<dd> </dd>
|
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/Timeline.html#getPeriod(int,com.google.android.exoplayer2.Timeline.Period,boolean)">getPeriod(int, Timeline.Period, boolean)</a></span> - Method in class com.google.android.exoplayer2.<a href="com/google/android/exoplayer2/Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/Timeline.html#getPeriod(int,com.google.android.exoplayer2.Timeline.Period,boolean)">getPeriod(int, Timeline.Period, boolean)</a></span> - Method in class com.google.android.exoplayer2.<a href="com/google/android/exoplayer2/Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Populates a <a href="com/google/android/exoplayer2/Timeline.Period.html" title="class in com.google.android.exoplayer2"><code>Timeline.Period</code></a> with data for the period at the specified index.</div>
|
<div class="block">Populates a <a href="com/google/android/exoplayer2/Timeline.Period.html" title="class in com.google.android.exoplayer2"><code>Timeline.Period</code></a> with data for the period at the specified index.</div>
|
||||||
|
|
@ -19189,8 +19205,6 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/FakeTimeline.html#getWindow(int,com.google.android.exoplayer2.Timeline.Window,long)">getWindow(int, Timeline.Window, long)</a></span> - Method in class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/FakeTimeline.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/FakeTimeline.html#getWindow(int,com.google.android.exoplayer2.Timeline.Window,long)">getWindow(int, Timeline.Window, long)</a></span> - Method in class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/FakeTimeline.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline</a></dt>
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/NoUidTimeline.html#getWindow(int,com.google.android.exoplayer2.Timeline.Window,long)">getWindow(int, Timeline.Window, long)</a></span> - Method in class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/NoUidTimeline.html" title="class in com.google.android.exoplayer2.testutil">NoUidTimeline</a></dt>
|
|
||||||
<dd> </dd>
|
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/Timeline.html#getWindow(int,com.google.android.exoplayer2.Timeline.Window,long)">getWindow(int, Timeline.Window, long)</a></span> - Method in class com.google.android.exoplayer2.<a href="com/google/android/exoplayer2/Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/Timeline.html#getWindow(int,com.google.android.exoplayer2.Timeline.Window,long)">getWindow(int, Timeline.Window, long)</a></span> - Method in class com.google.android.exoplayer2.<a href="com/google/android/exoplayer2/Timeline.html" title="class in com.google.android.exoplayer2">Timeline</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Populates a <a href="com/google/android/exoplayer2/Timeline.Window.html" title="class in com.google.android.exoplayer2"><code>Timeline.Window</code></a> with data for the window at the specified index.</div>
|
<div class="block">Populates a <a href="com/google/android/exoplayer2/Timeline.Window.html" title="class in com.google.android.exoplayer2"><code>Timeline.Window</code></a> with data for the window at the specified index.</div>
|
||||||
|
|
@ -20405,6 +20419,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<dl>
|
<dl>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#i(java.lang.String,java.lang.String)">i(String, String)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#i(java.lang.String,java.lang.String)">i(String, String)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.Logger.html#i(java.lang.String,java.lang.String)">i(String, String)</a></span> - Method in interface com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.Logger.html" title="interface in com.google.android.exoplayer2.util">Log.Logger</a></dt>
|
||||||
|
<dd>
|
||||||
|
<div class="block">Logs an information-level message.</div>
|
||||||
|
</dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#i(java.lang.String,java.lang.String,java.lang.Throwable)">i(String, String, Throwable)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#i(java.lang.String,java.lang.String,java.lang.Throwable)">i(String, String, Throwable)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
<dt><a href="com/google/android/exoplayer2/metadata/icy/IcyDecoder.html" title="class in com.google.android.exoplayer2.metadata.icy"><span class="typeNameLink">IcyDecoder</span></a> - Class in <a href="com/google/android/exoplayer2/metadata/icy/package-summary.html">com.google.android.exoplayer2.metadata.icy</a></dt>
|
<dt><a href="com/google/android/exoplayer2/metadata/icy/IcyDecoder.html" title="class in com.google.android.exoplayer2.metadata.icy"><span class="typeNameLink">IcyDecoder</span></a> - Class in <a href="com/google/android/exoplayer2/metadata/icy/package-summary.html">com.google.android.exoplayer2.metadata.icy</a></dt>
|
||||||
|
|
@ -22798,7 +22816,8 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
</dd>
|
</dd>
|
||||||
<dt><a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util"><span class="typeNameLink">Log</span></a> - Class in <a href="com/google/android/exoplayer2/util/package-summary.html">com.google.android.exoplayer2.util</a></dt>
|
<dt><a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util"><span class="typeNameLink">Log</span></a> - Class in <a href="com/google/android/exoplayer2/util/package-summary.html">com.google.android.exoplayer2.util</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Wrapper around <a href="https://developer.android.com/reference/android/util/Log.html" title="class or interface in android.util" class="externalLink" target="_top"><code>Log</code></a> which allows to set the log level.</div>
|
<div class="block">Wrapper around <a href="https://developer.android.com/reference/android/util/Log.html" title="class or interface in android.util" class="externalLink" target="_top"><code>Log</code></a> which allows to set the log level and to specify a custom
|
||||||
|
log output.</div>
|
||||||
</dd>
|
</dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#LOG_LEVEL_ALL">LOG_LEVEL_ALL</a></span> - Static variable in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#LOG_LEVEL_ALL">LOG_LEVEL_ALL</a></span> - Static variable in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
|
|
@ -22820,6 +22839,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Log level to only log warning and error messages.</div>
|
<div class="block">Log level to only log warning and error messages.</div>
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt><a href="com/google/android/exoplayer2/util/Log.Logger.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">Log.Logger</span></a> - Interface in <a href="com/google/android/exoplayer2/util/package-summary.html">com.google.android.exoplayer2.util</a></dt>
|
||||||
|
<dd>
|
||||||
|
<div class="block">Interface for a logger that can output messages with a tag.</div>
|
||||||
|
</dd>
|
||||||
<dt><a href="com/google/android/exoplayer2/util/Log.LogLevel.html" title="annotation in com.google.android.exoplayer2.util"><span class="typeNameLink">Log.LogLevel</span></a> - Annotation Type in <a href="com/google/android/exoplayer2/util/package-summary.html">com.google.android.exoplayer2.util</a></dt>
|
<dt><a href="com/google/android/exoplayer2/util/Log.LogLevel.html" title="annotation in com.google.android.exoplayer2.util"><span class="typeNameLink">Log.LogLevel</span></a> - Annotation Type in <a href="com/google/android/exoplayer2/util/package-summary.html">com.google.android.exoplayer2.util</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Log level for ExoPlayer logcat logging.</div>
|
<div class="block">Log level for ExoPlayer logcat logging.</div>
|
||||||
|
|
@ -24808,14 +24831,6 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
</dd>
|
</dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/analytics/DefaultAnalyticsCollector.html#notifySeekStarted()">notifySeekStarted()</a></span> - Method in class com.google.android.exoplayer2.analytics.<a href="com/google/android/exoplayer2/analytics/DefaultAnalyticsCollector.html" title="class in com.google.android.exoplayer2.analytics">DefaultAnalyticsCollector</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/analytics/DefaultAnalyticsCollector.html#notifySeekStarted()">notifySeekStarted()</a></span> - Method in class com.google.android.exoplayer2.analytics.<a href="com/google/android/exoplayer2/analytics/DefaultAnalyticsCollector.html" title="class in com.google.android.exoplayer2.analytics">DefaultAnalyticsCollector</a></dt>
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
<dt><a href="com/google/android/exoplayer2/testutil/NoUidTimeline.html" title="class in com.google.android.exoplayer2.testutil"><span class="typeNameLink">NoUidTimeline</span></a> - Class in <a href="com/google/android/exoplayer2/testutil/package-summary.html">com.google.android.exoplayer2.testutil</a></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="block">A timeline which wraps another timeline and overrides all window and period uids to 0.</div>
|
|
||||||
</dd>
|
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/NoUidTimeline.html#%3Cinit%3E(com.google.android.exoplayer2.Timeline)">NoUidTimeline(Timeline)</a></span> - Constructor for class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/NoUidTimeline.html" title="class in com.google.android.exoplayer2.testutil">NoUidTimeline</a></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="block">Creates an instance.</div>
|
|
||||||
</dd>
|
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Util.html#nullSafeArrayAppend(T%5B%5D,T)">nullSafeArrayAppend(T[], T)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Util.html" title="class in com.google.android.exoplayer2.util">Util</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Util.html#nullSafeArrayAppend(T%5B%5D,T)">nullSafeArrayAppend(T[], T)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Util.html" title="class in com.google.android.exoplayer2.util">Util</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Creates a new array containing <code>original</code> with <code>newElement</code> appended.</div>
|
<div class="block">Creates a new array containing <code>original</code> with <code>newElement</code> appended.</div>
|
||||||
|
|
@ -32146,11 +32161,6 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<div class="block">Runs tasks of the main <a href="https://developer.android.com/reference/android/os/Looper.html" title="class or interface in android.os" class="externalLink"><code>Looper</code></a> until <a href="com/google/android/exoplayer2/Player.Listener.html#onPositionDiscontinuity(com.google.android.exoplayer2.Player.PositionInfo,com.google.android.exoplayer2.Player.PositionInfo,@com.google.android.exoplayer2.Player.DiscontinuityReasonint)" target="_top"><code>Player.Listener.onPositionDiscontinuity(Player.PositionInfo, Player.PositionInfo, int)</code></a> is
|
<div class="block">Runs tasks of the main <a href="https://developer.android.com/reference/android/os/Looper.html" title="class or interface in android.os" class="externalLink"><code>Looper</code></a> until <a href="com/google/android/exoplayer2/Player.Listener.html#onPositionDiscontinuity(com.google.android.exoplayer2.Player.PositionInfo,com.google.android.exoplayer2.Player.PositionInfo,@com.google.android.exoplayer2.Player.DiscontinuityReasonint)" target="_top"><code>Player.Listener.onPositionDiscontinuity(Player.PositionInfo, Player.PositionInfo, int)</code></a> is
|
||||||
called with the specified <a href="com/google/android/exoplayer2/Player.DiscontinuityReason.html" title="annotation in com.google.android.exoplayer2"><code>Player.DiscontinuityReason</code></a> or a playback error occurs.</div>
|
called with the specified <a href="com/google/android/exoplayer2/Player.DiscontinuityReason.html" title="annotation in com.google.android.exoplayer2"><code>Player.DiscontinuityReason</code></a> or a playback error occurs.</div>
|
||||||
</dd>
|
</dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.html#runUntilReceiveOffloadSchedulingEnabledNewState(com.google.android.exoplayer2.ExoPlayer)">runUntilReceiveOffloadSchedulingEnabledNewState(ExoPlayer)</a></span> - Static method in class com.google.android.exoplayer2.robolectric.<a href="com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.html" title="class in com.google.android.exoplayer2.robolectric">TestPlayerRunHelper</a></dt>
|
|
||||||
<dd>
|
|
||||||
<div class="block">Runs tasks of the main <a href="https://developer.android.com/reference/android/os/Looper.html" title="class or interface in android.os" class="externalLink"><code>Looper</code></a> until <a href="com/google/android/exoplayer2/ExoPlayer.AudioOffloadListener.html#onExperimentalOffloadSchedulingEnabledChanged(boolean)" target="_top"><code>ExoPlayer.AudioOffloadListener.onExperimentalOffloadSchedulingEnabledChanged(boolean)</code></a> is called or a
|
|
||||||
playback error occurs.</div>
|
|
||||||
</dd>
|
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.html#runUntilRenderedFirstFrame(com.google.android.exoplayer2.ExoPlayer)">runUntilRenderedFirstFrame(ExoPlayer)</a></span> - Static method in class com.google.android.exoplayer2.robolectric.<a href="com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.html" title="class in com.google.android.exoplayer2.robolectric">TestPlayerRunHelper</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.html#runUntilRenderedFirstFrame(com.google.android.exoplayer2.ExoPlayer)">runUntilRenderedFirstFrame(ExoPlayer)</a></span> - Static method in class com.google.android.exoplayer2.robolectric.<a href="com/google/android/exoplayer2/robolectric/TestPlayerRunHelper.html" title="class in com.google.android.exoplayer2.robolectric">TestPlayerRunHelper</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Runs tasks of the main <a href="https://developer.android.com/reference/android/os/Looper.html" title="class or interface in android.os" class="externalLink"><code>Looper</code></a> until the <a href="com/google/android/exoplayer2/Player.Listener.html#onRenderedFirstFrame()" target="_top"><code>Player.Listener.onRenderedFirstFrame()</code></a>
|
<div class="block">Runs tasks of the main <a href="https://developer.android.com/reference/android/os/Looper.html" title="class or interface in android.os" class="externalLink"><code>Looper</code></a> until the <a href="com/google/android/exoplayer2/Player.Listener.html#onRenderedFirstFrame()" target="_top"><code>Player.Listener.onRenderedFirstFrame()</code></a>
|
||||||
|
|
@ -35457,6 +35467,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Sets the components required for local ad insertion for media items that have <a href="com/google/android/exoplayer2/MediaItem.LocalConfiguration.html#adsConfiguration"><code>ads configurations</code></a></div>
|
<div class="block">Sets the components required for local ad insertion for media items that have <a href="com/google/android/exoplayer2/MediaItem.LocalConfiguration.html#adsConfiguration"><code>ads configurations</code></a></div>
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#setLogger(com.google.android.exoplayer2.util.Log.Logger)">setLogger(Log.Logger)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
||||||
|
<dd>
|
||||||
|
<div class="block">Sets a custom <a href="com/google/android/exoplayer2/util/Log.Logger.html" title="interface in com.google.android.exoplayer2.util"><code>Log.Logger</code></a> as the output.</div>
|
||||||
|
</dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#setLogLevel(@com.google.android.exoplayer2.util.Log.LogLevelint)">setLogLevel(@com.google.android.exoplayer2.util.Log.LogLevel int)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#setLogLevel(@com.google.android.exoplayer2.util.Log.LogLevelint)">setLogLevel(@com.google.android.exoplayer2.util.Log.LogLevel int)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Sets the <a href="com/google/android/exoplayer2/util/Log.LogLevel.html" title="annotation in com.google.android.exoplayer2.util"><code>Log.LogLevel</code></a> for ExoPlayer logcat logging.</div>
|
<div class="block">Sets the <a href="com/google/android/exoplayer2/util/Log.LogLevel.html" title="annotation in com.google.android.exoplayer2.util"><code>Log.LogLevel</code></a> for ExoPlayer logcat logging.</div>
|
||||||
|
|
@ -40321,6 +40335,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Creates an instance for a given <code>MediaSessionCompat</code> and maximum queue size.</div>
|
<div class="block">Creates an instance for a given <code>MediaSessionCompat</code> and maximum queue size.</div>
|
||||||
</dd>
|
</dd>
|
||||||
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/TestUtil.html#timelinesAreSame(com.google.android.exoplayer2.Timeline,com.google.android.exoplayer2.Timeline)">timelinesAreSame(Timeline, Timeline)</a></span> - Static method in class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/TestUtil.html" title="class in com.google.android.exoplayer2.testutil">TestUtil</a></dt>
|
||||||
|
<dd>
|
||||||
|
<div class="block">Returns true if <code>thisTimeline</code> is equal to <code>thatTimeline</code>, ignoring <a href="com/google/android/exoplayer2/Timeline.Window.html#uid"><code>Timeline.Window.uid</code></a> and <a href="com/google/android/exoplayer2/Timeline.Period.html#uid"><code>Timeline.Period.uid</code></a> values, and shuffle order.</div>
|
||||||
|
</dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/FakeTimeline.TimelineWindowDefinition.html#%3Cinit%3E(boolean,boolean,long)">TimelineWindowDefinition(boolean, boolean, long)</a></span> - Constructor for class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/FakeTimeline.TimelineWindowDefinition.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline.TimelineWindowDefinition</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/testutil/FakeTimeline.TimelineWindowDefinition.html#%3Cinit%3E(boolean,boolean,long)">TimelineWindowDefinition(boolean, boolean, long)</a></span> - Constructor for class com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/FakeTimeline.TimelineWindowDefinition.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline.TimelineWindowDefinition</a></dt>
|
||||||
<dd>
|
<dd>
|
||||||
<div class="block">Creates a window definition with one period.</div>
|
<div class="block">Creates a window definition with one period.</div>
|
||||||
|
|
@ -42501,6 +42519,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
</a>
|
</a>
|
||||||
<h2 class="title">W</h2>
|
<h2 class="title">W</h2>
|
||||||
<dl>
|
<dl>
|
||||||
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.Logger.html#w(java.lang.String,java.lang.String)">w(String, String)</a></span> - Method in interface com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.Logger.html" title="interface in com.google.android.exoplayer2.util">Log.Logger</a></dt>
|
||||||
|
<dd>
|
||||||
|
<div class="block">Logs a warning-level message.</div>
|
||||||
|
</dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#w(java.lang.String,java.lang.String)">w(String, String)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#w(java.lang.String,java.lang.String)">w(String, String)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
||||||
<dd> </dd>
|
<dd> </dd>
|
||||||
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#w(java.lang.String,java.lang.String,java.lang.Throwable)">w(String, String, Throwable)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
<dt><span class="memberNameLink"><a href="com/google/android/exoplayer2/util/Log.html#w(java.lang.String,java.lang.String,java.lang.Throwable)">w(String, String, Throwable)</a></span> - Static method in class com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.html" title="class in com.google.android.exoplayer2.util">Log</a></dt>
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
|
@ -1198,7 +1198,6 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<li class="circle">com.google.android.exoplayer2.source.<a href="com/google/android/exoplayer2/source/ForwardingTimeline.html" title="class in com.google.android.exoplayer2.source"><span class="typeNameLink">ForwardingTimeline</span></a>
|
<li class="circle">com.google.android.exoplayer2.source.<a href="com/google/android/exoplayer2/source/ForwardingTimeline.html" title="class in com.google.android.exoplayer2.source"><span class="typeNameLink">ForwardingTimeline</span></a>
|
||||||
<ul>
|
<ul>
|
||||||
<li class="circle">com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/FakeMediaSource.InitialTimeline.html" title="class in com.google.android.exoplayer2.testutil"><span class="typeNameLink">FakeMediaSource.InitialTimeline</span></a></li>
|
<li class="circle">com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/FakeMediaSource.InitialTimeline.html" title="class in com.google.android.exoplayer2.testutil"><span class="typeNameLink">FakeMediaSource.InitialTimeline</span></a></li>
|
||||||
<li class="circle">com.google.android.exoplayer2.testutil.<a href="com/google/android/exoplayer2/testutil/NoUidTimeline.html" title="class in com.google.android.exoplayer2.testutil"><span class="typeNameLink">NoUidTimeline</span></a></li>
|
|
||||||
<li class="circle">com.google.android.exoplayer2.source.ads.<a href="com/google/android/exoplayer2/source/ads/SinglePeriodAdTimeline.html" title="class in com.google.android.exoplayer2.source.ads"><span class="typeNameLink">SinglePeriodAdTimeline</span></a></li>
|
<li class="circle">com.google.android.exoplayer2.source.ads.<a href="com/google/android/exoplayer2/source/ads/SinglePeriodAdTimeline.html" title="class in com.google.android.exoplayer2.source.ads"><span class="typeNameLink">SinglePeriodAdTimeline</span></a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
@ -1506,6 +1505,7 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
||||||
<li class="circle">com.google.android.exoplayer2.upstream.<a href="com/google/android/exoplayer2/upstream/Loader.ReleaseCallback.html" title="interface in com.google.android.exoplayer2.upstream"><span class="typeNameLink">Loader.ReleaseCallback</span></a></li>
|
<li class="circle">com.google.android.exoplayer2.upstream.<a href="com/google/android/exoplayer2/upstream/Loader.ReleaseCallback.html" title="interface in com.google.android.exoplayer2.upstream"><span class="typeNameLink">Loader.ReleaseCallback</span></a></li>
|
||||||
<li class="circle">com.google.android.exoplayer2.upstream.<a href="com/google/android/exoplayer2/upstream/LoaderErrorThrower.html" title="interface in com.google.android.exoplayer2.upstream"><span class="typeNameLink">LoaderErrorThrower</span></a></li>
|
<li class="circle">com.google.android.exoplayer2.upstream.<a href="com/google/android/exoplayer2/upstream/LoaderErrorThrower.html" title="interface in com.google.android.exoplayer2.upstream"><span class="typeNameLink">LoaderErrorThrower</span></a></li>
|
||||||
<li class="circle">com.google.android.exoplayer2.upstream.<a href="com/google/android/exoplayer2/upstream/LoadErrorHandlingPolicy.html" title="interface in com.google.android.exoplayer2.upstream"><span class="typeNameLink">LoadErrorHandlingPolicy</span></a></li>
|
<li class="circle">com.google.android.exoplayer2.upstream.<a href="com/google/android/exoplayer2/upstream/LoadErrorHandlingPolicy.html" title="interface in com.google.android.exoplayer2.upstream"><span class="typeNameLink">LoadErrorHandlingPolicy</span></a></li>
|
||||||
|
<li class="circle">com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/Log.Logger.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">Log.Logger</span></a></li>
|
||||||
<li class="circle">com.google.android.exoplayer2.source.<a href="com/google/android/exoplayer2/source/MaskingMediaPeriod.PrepareListener.html" title="interface in com.google.android.exoplayer2.source"><span class="typeNameLink">MaskingMediaPeriod.PrepareListener</span></a></li>
|
<li class="circle">com.google.android.exoplayer2.source.<a href="com/google/android/exoplayer2/source/MaskingMediaPeriod.PrepareListener.html" title="interface in com.google.android.exoplayer2.source"><span class="typeNameLink">MaskingMediaPeriod.PrepareListener</span></a></li>
|
||||||
<li class="circle">com.google.android.exoplayer2.source.chunk.<a href="com/google/android/exoplayer2/source/chunk/MediaChunkIterator.html" title="interface in com.google.android.exoplayer2.source.chunk"><span class="typeNameLink">MediaChunkIterator</span></a></li>
|
<li class="circle">com.google.android.exoplayer2.source.chunk.<a href="com/google/android/exoplayer2/source/chunk/MediaChunkIterator.html" title="interface in com.google.android.exoplayer2.source.chunk"><span class="typeNameLink">MediaChunkIterator</span></a></li>
|
||||||
<li class="circle">com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/MediaClock.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">MediaClock</span></a></li>
|
<li class="circle">com.google.android.exoplayer2.util.<a href="com/google/android/exoplayer2/util/MediaClock.html" title="interface in com.google.android.exoplayer2.util"><span class="typeNameLink">MediaClock</span></a></li>
|
||||||
|
|
|
||||||
|
|
@ -1,2 +1 @@
|
||||||
packageSearchIndex = [{"l":"All Packages","url":"allpackages-index.html"},{"l":"com.google.android.exoplayer2"},{"l":"com.google.android.exoplayer2.analytics"},{"l":"com.google.android.exoplayer2.audio"},{"l":"com.google.android.exoplayer2.database"},{"l":"com.google.android.exoplayer2.decoder"},{"l":"com.google.android.exoplayer2.drm"},{"l":"com.google.android.exoplayer2.ext.av1"},{"l":"com.google.android.exoplayer2.ext.cast"},{"l":"com.google.android.exoplayer2.ext.cronet"},{"l":"com.google.android.exoplayer2.ext.ffmpeg"},{"l":"com.google.android.exoplayer2.ext.flac"},{"l":"com.google.android.exoplayer2.ext.ima"},{"l":"com.google.android.exoplayer2.ext.leanback"},{"l":"com.google.android.exoplayer2.ext.media2"},{"l":"com.google.android.exoplayer2.ext.mediasession"},{"l":"com.google.android.exoplayer2.ext.okhttp"},{"l":"com.google.android.exoplayer2.ext.opus"},{"l":"com.google.android.exoplayer2.ext.rtmp"},{"l":"com.google.android.exoplayer2.ext.vp9"},{"l":"com.google.android.exoplayer2.ext.workmanager"},{"l":"com.google.android.exoplayer2.extractor"},{"l":"com.google.android.exoplayer2.extractor.amr"},{"l":"com.google.android.exoplayer2.extractor.avi"},{"l":"com.google.android.exoplayer2.extractor.flac"},{"l":"com.google.android.exoplayer2.extractor.flv"},{"l":"com.google.android.exoplayer2.extractor.jpeg"},{"l":"com.google.android.exoplayer2.extractor.mkv"},{"l":"com.google.android.exoplayer2.extractor.mp3"},{"l":"com.google.android.exoplayer2.extractor.mp4"},{"l":"com.google.android.exoplayer2.extractor.ogg"},{"l":"com.google.android.exoplayer2.extractor.ts"},{"l":"com.google.android.exoplayer2.extractor.wav"},{"l":"com.google.android.exoplayer2.mediacodec"},{"l":"com.google.android.exoplayer2.metadata"},{"l":"com.google.android.exoplayer2.metadata.dvbsi"},{"l":"com.google.android.exoplayer2.metadata.emsg"},{"l":"com.google.android.exoplayer2.metadata.flac"},{"l":"com.google.android.exoplayer2.metadata.icy"},{"l":"com.google.android.exoplayer2.metadata.id3"},{"l":"com.google.android.exoplayer2.metadata.mp4"},{"l":"com.google.android.exoplayer2.metadata.scte35"},{"l":"com.google.android.exoplayer2.metadata.vorbis"},{"l":"com.google.android.exoplayer2.offline"},{"l":"com.google.android.exoplayer2.robolectric"},{"l":"com.google.android.exoplayer2.scheduler"},{"l":"com.google.android.exoplayer2.source"},{"l":"com.google.android.exoplayer2.source.ads"},{"l":"com.google.android.exoplayer2.source.chunk"},{"l":"com.google.android.exoplayer2.source.dash"},{"l":"com.google.android.exoplayer2.source.dash.manifest"},{"l":"com.google.android.exoplayer2.source.dash.offline"},{"l":"com.google.android.exoplayer2.source.hls"},{"l":"com.google.android.exoplayer2.source.hls.offline"},{"l":"com.google.android.exoplayer2.source.hls.playlist"},{"l":"com.google.android.exoplayer2.source.mediaparser"},{"l":"com.google.android.exoplayer2.source.rtsp"},{"l":"com.google.android.exoplayer2.source.rtsp.reader"},{"l":"com.google.android.exoplayer2.source.smoothstreaming"},{"l":"com.google.android.exoplayer2.source.smoothstreaming.manifest"},{"l":"com.google.android.exoplayer2.source.smoothstreaming.offline"},{"l":"com.google.android.exoplayer2.testutil"},{"l":"com.google.android.exoplayer2.testutil.truth"},{"l":"com.google.android.exoplayer2.text"},{"l":"com.google.android.exoplayer2.text.cea"},{"l":"com.google.android.exoplayer2.text.dvb"},{"l":"com.google.android.exoplayer2.text.pgs"},{"l":"com.google.android.exoplayer2.text.span"},{"l":"com.google.android.exoplayer2.text.ssa"},{"l":"com.google.android.exoplayer2.text.subrip"},{"l":"com.google.android.exoplayer2.text.ttml"},{"l":"com.google.android.exoplayer2.text.tx3g"},{"l":"com.google.android.exoplayer2.text.webvtt"},{"l":"com.google.android.exoplayer2.trackselection"},{"l":"com.google.android.exoplayer2.transformer"},{"l":"com.google.android.exoplayer2.ui"},{"l":"com.google.android.exoplayer2.upstream"},{"l":"com.google.android.exoplayer2.upstream.cache"},{"l":"com.google.android.exoplayer2.upstream.crypto"},{"l":"com.google.android.exoplayer2.util"},{"l":"com.google.android.exoplayer2.video"},{"l":"com.google.android.exoplayer2.video.spherical"}]
|
packageSearchIndex = [{"l":"All Packages","url":"allpackages-index.html"},{"l":"com.google.android.exoplayer2"},{"l":"com.google.android.exoplayer2.analytics"},{"l":"com.google.android.exoplayer2.audio"},{"l":"com.google.android.exoplayer2.database"},{"l":"com.google.android.exoplayer2.decoder"},{"l":"com.google.android.exoplayer2.drm"},{"l":"com.google.android.exoplayer2.ext.av1"},{"l":"com.google.android.exoplayer2.ext.cast"},{"l":"com.google.android.exoplayer2.ext.cronet"},{"l":"com.google.android.exoplayer2.ext.ffmpeg"},{"l":"com.google.android.exoplayer2.ext.flac"},{"l":"com.google.android.exoplayer2.ext.ima"},{"l":"com.google.android.exoplayer2.ext.leanback"},{"l":"com.google.android.exoplayer2.ext.media2"},{"l":"com.google.android.exoplayer2.ext.mediasession"},{"l":"com.google.android.exoplayer2.ext.okhttp"},{"l":"com.google.android.exoplayer2.ext.opus"},{"l":"com.google.android.exoplayer2.ext.rtmp"},{"l":"com.google.android.exoplayer2.ext.vp9"},{"l":"com.google.android.exoplayer2.ext.workmanager"},{"l":"com.google.android.exoplayer2.extractor"},{"l":"com.google.android.exoplayer2.extractor.amr"},{"l":"com.google.android.exoplayer2.extractor.avi"},{"l":"com.google.android.exoplayer2.extractor.flac"},{"l":"com.google.android.exoplayer2.extractor.flv"},{"l":"com.google.android.exoplayer2.extractor.jpeg"},{"l":"com.google.android.exoplayer2.extractor.mkv"},{"l":"com.google.android.exoplayer2.extractor.mp3"},{"l":"com.google.android.exoplayer2.extractor.mp4"},{"l":"com.google.android.exoplayer2.extractor.ogg"},{"l":"com.google.android.exoplayer2.extractor.ts"},{"l":"com.google.android.exoplayer2.extractor.wav"},{"l":"com.google.android.exoplayer2.mediacodec"},{"l":"com.google.android.exoplayer2.metadata"},{"l":"com.google.android.exoplayer2.metadata.dvbsi"},{"l":"com.google.android.exoplayer2.metadata.emsg"},{"l":"com.google.android.exoplayer2.metadata.flac"},{"l":"com.google.android.exoplayer2.metadata.icy"},{"l":"com.google.android.exoplayer2.metadata.id3"},{"l":"com.google.android.exoplayer2.metadata.mp4"},{"l":"com.google.android.exoplayer2.metadata.scte35"},{"l":"com.google.android.exoplayer2.metadata.vorbis"},{"l":"com.google.android.exoplayer2.offline"},{"l":"com.google.android.exoplayer2.robolectric"},{"l":"com.google.android.exoplayer2.scheduler"},{"l":"com.google.android.exoplayer2.source"},{"l":"com.google.android.exoplayer2.source.ads"},{"l":"com.google.android.exoplayer2.source.chunk"},{"l":"com.google.android.exoplayer2.source.dash"},{"l":"com.google.android.exoplayer2.source.dash.manifest"},{"l":"com.google.android.exoplayer2.source.dash.offline"},{"l":"com.google.android.exoplayer2.source.hls"},{"l":"com.google.android.exoplayer2.source.hls.offline"},{"l":"com.google.android.exoplayer2.source.hls.playlist"},{"l":"com.google.android.exoplayer2.source.mediaparser"},{"l":"com.google.android.exoplayer2.source.rtsp"},{"l":"com.google.android.exoplayer2.source.rtsp.reader"},{"l":"com.google.android.exoplayer2.source.smoothstreaming"},{"l":"com.google.android.exoplayer2.source.smoothstreaming.manifest"},{"l":"com.google.android.exoplayer2.source.smoothstreaming.offline"},{"l":"com.google.android.exoplayer2.testutil"},{"l":"com.google.android.exoplayer2.testutil.truth"},{"l":"com.google.android.exoplayer2.text"},{"l":"com.google.android.exoplayer2.text.cea"},{"l":"com.google.android.exoplayer2.text.dvb"},{"l":"com.google.android.exoplayer2.text.pgs"},{"l":"com.google.android.exoplayer2.text.span"},{"l":"com.google.android.exoplayer2.text.ssa"},{"l":"com.google.android.exoplayer2.text.subrip"},{"l":"com.google.android.exoplayer2.text.ttml"},{"l":"com.google.android.exoplayer2.text.tx3g"},{"l":"com.google.android.exoplayer2.text.webvtt"},{"l":"com.google.android.exoplayer2.trackselection"},{"l":"com.google.android.exoplayer2.transformer"},{"l":"com.google.android.exoplayer2.ui"},{"l":"com.google.android.exoplayer2.upstream"},{"l":"com.google.android.exoplayer2.upstream.cache"},{"l":"com.google.android.exoplayer2.upstream.crypto"},{"l":"com.google.android.exoplayer2.util"},{"l":"com.google.android.exoplayer2.video"},{"l":"com.google.android.exoplayer2.video.spherical"}]
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
|
|
@ -99,9 +99,9 @@ public final class CastPlayer extends BasePlayer {
|
||||||
COMMAND_GET_TIMELINE,
|
COMMAND_GET_TIMELINE,
|
||||||
COMMAND_GET_MEDIA_ITEMS_METADATA,
|
COMMAND_GET_MEDIA_ITEMS_METADATA,
|
||||||
COMMAND_SET_MEDIA_ITEMS_METADATA,
|
COMMAND_SET_MEDIA_ITEMS_METADATA,
|
||||||
|
COMMAND_SET_MEDIA_ITEM,
|
||||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||||
COMMAND_GET_TRACKS,
|
COMMAND_GET_TRACKS)
|
||||||
COMMAND_SET_MEDIA_ITEM)
|
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
public static final float MIN_SPEED_SUPPORTED = 0.5f;
|
public static final float MIN_SPEED_SUPPORTED = 0.5f;
|
||||||
|
|
@ -143,6 +143,7 @@ public final class CastPlayer extends BasePlayer {
|
||||||
private int pendingSeekWindowIndex;
|
private int pendingSeekWindowIndex;
|
||||||
private long pendingSeekPositionMs;
|
private long pendingSeekPositionMs;
|
||||||
@Nullable private PositionInfo pendingMediaItemRemovalPosition;
|
@Nullable private PositionInfo pendingMediaItemRemovalPosition;
|
||||||
|
private MediaMetadata mediaMetadata;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new cast player.
|
* Creates a new cast player.
|
||||||
|
|
@ -196,7 +197,7 @@ public final class CastPlayer extends BasePlayer {
|
||||||
this.mediaItemConverter = mediaItemConverter;
|
this.mediaItemConverter = mediaItemConverter;
|
||||||
this.seekBackIncrementMs = seekBackIncrementMs;
|
this.seekBackIncrementMs = seekBackIncrementMs;
|
||||||
this.seekForwardIncrementMs = seekForwardIncrementMs;
|
this.seekForwardIncrementMs = seekForwardIncrementMs;
|
||||||
timelineTracker = new CastTimelineTracker();
|
timelineTracker = new CastTimelineTracker(mediaItemConverter);
|
||||||
period = new Timeline.Period();
|
period = new Timeline.Period();
|
||||||
statusListener = new StatusListener();
|
statusListener = new StatusListener();
|
||||||
seekResultCallback = new SeekResultCallback();
|
seekResultCallback = new SeekResultCallback();
|
||||||
|
|
@ -210,6 +211,7 @@ public final class CastPlayer extends BasePlayer {
|
||||||
playbackParameters = new StateHolder<>(PlaybackParameters.DEFAULT);
|
playbackParameters = new StateHolder<>(PlaybackParameters.DEFAULT);
|
||||||
playbackState = STATE_IDLE;
|
playbackState = STATE_IDLE;
|
||||||
currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE;
|
currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE;
|
||||||
|
mediaMetadata = MediaMetadata.EMPTY;
|
||||||
currentTracks = Tracks.EMPTY;
|
currentTracks = Tracks.EMPTY;
|
||||||
availableCommands = new Commands.Builder().addAll(PERMANENT_AVAILABLE_COMMANDS).build();
|
availableCommands = new Commands.Builder().addAll(PERMANENT_AVAILABLE_COMMANDS).build();
|
||||||
pendingSeekWindowIndex = C.INDEX_UNSET;
|
pendingSeekWindowIndex = C.INDEX_UNSET;
|
||||||
|
|
@ -281,8 +283,7 @@ public final class CastPlayer extends BasePlayer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setMediaItems(List<MediaItem> mediaItems, int startIndex, long startPositionMs) {
|
public void setMediaItems(List<MediaItem> mediaItems, int startIndex, long startPositionMs) {
|
||||||
setMediaItemsInternal(
|
setMediaItemsInternal(mediaItems, startIndex, startPositionMs, repeatMode.value);
|
||||||
toMediaQueueItems(mediaItems), startIndex, startPositionMs, repeatMode.value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -292,7 +293,7 @@ public final class CastPlayer extends BasePlayer {
|
||||||
if (index < currentTimeline.getWindowCount()) {
|
if (index < currentTimeline.getWindowCount()) {
|
||||||
uid = (int) currentTimeline.getWindow(/* windowIndex= */ index, window).uid;
|
uid = (int) currentTimeline.getWindow(/* windowIndex= */ index, window).uid;
|
||||||
}
|
}
|
||||||
addMediaItemsInternal(toMediaQueueItems(mediaItems), uid);
|
addMediaItemsInternal(mediaItems, uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -424,6 +425,13 @@ public final class CastPlayer extends BasePlayer {
|
||||||
Player.EVENT_MEDIA_ITEM_TRANSITION,
|
Player.EVENT_MEDIA_ITEM_TRANSITION,
|
||||||
listener ->
|
listener ->
|
||||||
listener.onMediaItemTransition(mediaItem, MEDIA_ITEM_TRANSITION_REASON_SEEK));
|
listener.onMediaItemTransition(mediaItem, MEDIA_ITEM_TRANSITION_REASON_SEEK));
|
||||||
|
MediaMetadata oldMediaMetadata = mediaMetadata;
|
||||||
|
mediaMetadata = getMediaMetadataInternal();
|
||||||
|
if (!oldMediaMetadata.equals(mediaMetadata)) {
|
||||||
|
listeners.queueEvent(
|
||||||
|
Player.EVENT_MEDIA_METADATA_CHANGED,
|
||||||
|
listener -> listener.onMediaMetadataChanged(mediaMetadata));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
updateAvailableCommandsAndNotifyIfChanged();
|
updateAvailableCommandsAndNotifyIfChanged();
|
||||||
} else if (pendingSeekCount == 0) {
|
} else if (pendingSeekCount == 0) {
|
||||||
|
|
@ -561,8 +569,12 @@ public final class CastPlayer extends BasePlayer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MediaMetadata getMediaMetadata() {
|
public MediaMetadata getMediaMetadata() {
|
||||||
// CastPlayer does not currently support metadata.
|
return mediaMetadata;
|
||||||
return MediaMetadata.EMPTY;
|
}
|
||||||
|
|
||||||
|
public MediaMetadata getMediaMetadataInternal() {
|
||||||
|
MediaItem currentMediaItem = getCurrentMediaItem();
|
||||||
|
return currentMediaItem != null ? currentMediaItem.mediaMetadata : MediaMetadata.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -759,6 +771,7 @@ public final class CastPlayer extends BasePlayer {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int oldWindowIndex = this.currentWindowIndex;
|
int oldWindowIndex = this.currentWindowIndex;
|
||||||
|
MediaMetadata oldMediaMetadata = mediaMetadata;
|
||||||
@Nullable
|
@Nullable
|
||||||
Object oldPeriodUid =
|
Object oldPeriodUid =
|
||||||
!getCurrentTimeline().isEmpty()
|
!getCurrentTimeline().isEmpty()
|
||||||
|
|
@ -770,6 +783,7 @@ public final class CastPlayer extends BasePlayer {
|
||||||
boolean playingPeriodChangedByTimelineChange = updateTimelineAndNotifyIfChanged();
|
boolean playingPeriodChangedByTimelineChange = updateTimelineAndNotifyIfChanged();
|
||||||
Timeline currentTimeline = getCurrentTimeline();
|
Timeline currentTimeline = getCurrentTimeline();
|
||||||
currentWindowIndex = fetchCurrentWindowIndex(remoteMediaClient, currentTimeline);
|
currentWindowIndex = fetchCurrentWindowIndex(remoteMediaClient, currentTimeline);
|
||||||
|
mediaMetadata = getMediaMetadataInternal();
|
||||||
@Nullable
|
@Nullable
|
||||||
Object currentPeriodUid =
|
Object currentPeriodUid =
|
||||||
!currentTimeline.isEmpty()
|
!currentTimeline.isEmpty()
|
||||||
|
|
@ -823,6 +837,11 @@ public final class CastPlayer extends BasePlayer {
|
||||||
listeners.queueEvent(
|
listeners.queueEvent(
|
||||||
Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(currentTracks));
|
Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(currentTracks));
|
||||||
}
|
}
|
||||||
|
if (!oldMediaMetadata.equals(mediaMetadata)) {
|
||||||
|
listeners.queueEvent(
|
||||||
|
Player.EVENT_MEDIA_METADATA_CHANGED,
|
||||||
|
listener -> listener.onMediaMetadataChanged(mediaMetadata));
|
||||||
|
}
|
||||||
updateAvailableCommandsAndNotifyIfChanged();
|
updateAvailableCommandsAndNotifyIfChanged();
|
||||||
listeners.flushEvents();
|
listeners.flushEvents();
|
||||||
}
|
}
|
||||||
|
|
@ -1018,14 +1037,13 @@ public final class CastPlayer extends BasePlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private void setMediaItemsInternal(
|
||||||
private PendingResult<MediaChannelResult> setMediaItemsInternal(
|
List<MediaItem> mediaItems,
|
||||||
MediaQueueItem[] mediaQueueItems,
|
|
||||||
int startIndex,
|
int startIndex,
|
||||||
long startPositionMs,
|
long startPositionMs,
|
||||||
@RepeatMode int repeatMode) {
|
@RepeatMode int repeatMode) {
|
||||||
if (remoteMediaClient == null || mediaQueueItems.length == 0) {
|
if (remoteMediaClient == null || mediaItems.isEmpty()) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
startPositionMs = startPositionMs == C.TIME_UNSET ? 0 : startPositionMs;
|
startPositionMs = startPositionMs == C.TIME_UNSET ? 0 : startPositionMs;
|
||||||
if (startIndex == C.INDEX_UNSET) {
|
if (startIndex == C.INDEX_UNSET) {
|
||||||
|
|
@ -1036,34 +1054,35 @@ public final class CastPlayer extends BasePlayer {
|
||||||
if (!currentTimeline.isEmpty()) {
|
if (!currentTimeline.isEmpty()) {
|
||||||
pendingMediaItemRemovalPosition = getCurrentPositionInfo();
|
pendingMediaItemRemovalPosition = getCurrentPositionInfo();
|
||||||
}
|
}
|
||||||
return remoteMediaClient.queueLoad(
|
MediaQueueItem[] mediaQueueItems = toMediaQueueItems(mediaItems);
|
||||||
|
timelineTracker.onMediaItemsSet(mediaItems, mediaQueueItems);
|
||||||
|
remoteMediaClient.queueLoad(
|
||||||
mediaQueueItems,
|
mediaQueueItems,
|
||||||
min(startIndex, mediaQueueItems.length - 1),
|
min(startIndex, mediaItems.size() - 1),
|
||||||
getCastRepeatMode(repeatMode),
|
getCastRepeatMode(repeatMode),
|
||||||
startPositionMs,
|
startPositionMs,
|
||||||
/* customData= */ null);
|
/* customData= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private void addMediaItemsInternal(List<MediaItem> mediaItems, int uid) {
|
||||||
private PendingResult<MediaChannelResult> addMediaItemsInternal(MediaQueueItem[] items, int uid) {
|
|
||||||
if (remoteMediaClient == null || getMediaStatus() == null) {
|
if (remoteMediaClient == null || getMediaStatus() == null) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
return remoteMediaClient.queueInsertItems(items, uid, /* customData= */ null);
|
MediaQueueItem[] itemsToInsert = toMediaQueueItems(mediaItems);
|
||||||
|
timelineTracker.onMediaItemsAdded(mediaItems, itemsToInsert);
|
||||||
|
remoteMediaClient.queueInsertItems(itemsToInsert, uid, /* customData= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private void moveMediaItemsInternal(int[] uids, int fromIndex, int newIndex) {
|
||||||
private PendingResult<MediaChannelResult> moveMediaItemsInternal(
|
|
||||||
int[] uids, int fromIndex, int newIndex) {
|
|
||||||
if (remoteMediaClient == null || getMediaStatus() == null) {
|
if (remoteMediaClient == null || getMediaStatus() == null) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
int insertBeforeIndex = fromIndex < newIndex ? newIndex + uids.length : newIndex;
|
int insertBeforeIndex = fromIndex < newIndex ? newIndex + uids.length : newIndex;
|
||||||
int insertBeforeItemId = MediaQueueItem.INVALID_ITEM_ID;
|
int insertBeforeItemId = MediaQueueItem.INVALID_ITEM_ID;
|
||||||
if (insertBeforeIndex < currentTimeline.getWindowCount()) {
|
if (insertBeforeIndex < currentTimeline.getWindowCount()) {
|
||||||
insertBeforeItemId = (int) currentTimeline.getWindow(insertBeforeIndex, window).uid;
|
insertBeforeItemId = (int) currentTimeline.getWindow(insertBeforeIndex, window).uid;
|
||||||
}
|
}
|
||||||
return remoteMediaClient.queueReorderItems(uids, insertBeforeItemId, /* customData= */ null);
|
remoteMediaClient.queueReorderItems(uids, insertBeforeItemId, /* customData= */ null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,13 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.ext.cast;
|
package com.google.android.exoplayer2.ext.cast;
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.MediaItem;
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
|
import com.google.android.gms.cast.MediaInfo;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
/** A {@link Timeline} for Cast media queues. */
|
/** A {@link Timeline} for Cast media queues. */
|
||||||
|
|
@ -30,12 +30,16 @@ import java.util.Arrays;
|
||||||
/** Holds {@link Timeline} related data for a Cast media item. */
|
/** Holds {@link Timeline} related data for a Cast media item. */
|
||||||
public static final class ItemData {
|
public static final class ItemData {
|
||||||
|
|
||||||
|
/* package */ static final String UNKNOWN_CONTENT_ID = "UNKNOWN_CONTENT_ID";
|
||||||
|
|
||||||
/** Holds no media information. */
|
/** Holds no media information. */
|
||||||
public static final ItemData EMPTY =
|
public static final ItemData EMPTY =
|
||||||
new ItemData(
|
new ItemData(
|
||||||
/* durationUs= */ C.TIME_UNSET,
|
/* durationUs= */ C.TIME_UNSET,
|
||||||
/* defaultPositionUs= */ C.TIME_UNSET,
|
/* defaultPositionUs= */ C.TIME_UNSET,
|
||||||
/* isLive= */ false);
|
/* isLive= */ false,
|
||||||
|
MediaItem.EMPTY,
|
||||||
|
UNKNOWN_CONTENT_ID);
|
||||||
|
|
||||||
/** The duration of the item in microseconds, or {@link C#TIME_UNSET} if unknown. */
|
/** The duration of the item in microseconds, or {@link C#TIME_UNSET} if unknown. */
|
||||||
public final long durationUs;
|
public final long durationUs;
|
||||||
|
|
@ -45,6 +49,10 @@ import java.util.Arrays;
|
||||||
public final long defaultPositionUs;
|
public final long defaultPositionUs;
|
||||||
/** Whether the item is live content, or {@code false} if unknown. */
|
/** Whether the item is live content, or {@code false} if unknown. */
|
||||||
public final boolean isLive;
|
public final boolean isLive;
|
||||||
|
/** The original media item that has been set or added to the playlist. */
|
||||||
|
public final MediaItem mediaItem;
|
||||||
|
/** The {@linkplain MediaInfo#getContentId() content ID} of the cast media queue item. */
|
||||||
|
public final String contentId;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an instance.
|
* Creates an instance.
|
||||||
|
|
@ -52,11 +60,20 @@ import java.util.Arrays;
|
||||||
* @param durationUs See {@link #durationsUs}.
|
* @param durationUs See {@link #durationsUs}.
|
||||||
* @param defaultPositionUs See {@link #defaultPositionUs}.
|
* @param defaultPositionUs See {@link #defaultPositionUs}.
|
||||||
* @param isLive See {@link #isLive}.
|
* @param isLive See {@link #isLive}.
|
||||||
|
* @param mediaItem See {@link #mediaItem}.
|
||||||
|
* @param contentId See {@link #contentId}.
|
||||||
*/
|
*/
|
||||||
public ItemData(long durationUs, long defaultPositionUs, boolean isLive) {
|
public ItemData(
|
||||||
|
long durationUs,
|
||||||
|
long defaultPositionUs,
|
||||||
|
boolean isLive,
|
||||||
|
MediaItem mediaItem,
|
||||||
|
String contentId) {
|
||||||
this.durationUs = durationUs;
|
this.durationUs = durationUs;
|
||||||
this.defaultPositionUs = defaultPositionUs;
|
this.defaultPositionUs = defaultPositionUs;
|
||||||
this.isLive = isLive;
|
this.isLive = isLive;
|
||||||
|
this.mediaItem = mediaItem;
|
||||||
|
this.contentId = contentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -66,14 +83,23 @@ import java.util.Arrays;
|
||||||
* @param defaultPositionUs The default start position in microseconds, or {@link C#TIME_UNSET}
|
* @param defaultPositionUs The default start position in microseconds, or {@link C#TIME_UNSET}
|
||||||
* if unknown.
|
* if unknown.
|
||||||
* @param isLive Whether the item is live, or {@code false} if unknown.
|
* @param isLive Whether the item is live, or {@code false} if unknown.
|
||||||
|
* @param mediaItem The media item.
|
||||||
|
* @param contentId The content ID.
|
||||||
*/
|
*/
|
||||||
public ItemData copyWithNewValues(long durationUs, long defaultPositionUs, boolean isLive) {
|
public ItemData copyWithNewValues(
|
||||||
|
long durationUs,
|
||||||
|
long defaultPositionUs,
|
||||||
|
boolean isLive,
|
||||||
|
MediaItem mediaItem,
|
||||||
|
String contentId) {
|
||||||
if (durationUs == this.durationUs
|
if (durationUs == this.durationUs
|
||||||
&& defaultPositionUs == this.defaultPositionUs
|
&& defaultPositionUs == this.defaultPositionUs
|
||||||
&& isLive == this.isLive) {
|
&& isLive == this.isLive
|
||||||
|
&& contentId.equals(this.contentId)
|
||||||
|
&& mediaItem.equals(this.mediaItem)) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
return new ItemData(durationUs, defaultPositionUs, isLive);
|
return new ItemData(durationUs, defaultPositionUs, isLive, mediaItem, contentId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -82,6 +108,7 @@ import java.util.Arrays;
|
||||||
new CastTimeline(new int[0], new SparseArray<>());
|
new CastTimeline(new int[0], new SparseArray<>());
|
||||||
|
|
||||||
private final SparseIntArray idsToIndex;
|
private final SparseIntArray idsToIndex;
|
||||||
|
private final MediaItem[] mediaItems;
|
||||||
private final int[] ids;
|
private final int[] ids;
|
||||||
private final long[] durationsUs;
|
private final long[] durationsUs;
|
||||||
private final long[] defaultPositionsUs;
|
private final long[] defaultPositionsUs;
|
||||||
|
|
@ -100,10 +127,12 @@ import java.util.Arrays;
|
||||||
durationsUs = new long[itemCount];
|
durationsUs = new long[itemCount];
|
||||||
defaultPositionsUs = new long[itemCount];
|
defaultPositionsUs = new long[itemCount];
|
||||||
isLive = new boolean[itemCount];
|
isLive = new boolean[itemCount];
|
||||||
|
mediaItems = new MediaItem[itemCount];
|
||||||
for (int i = 0; i < ids.length; i++) {
|
for (int i = 0; i < ids.length; i++) {
|
||||||
int id = ids[i];
|
int id = ids[i];
|
||||||
idsToIndex.put(id, i);
|
idsToIndex.put(id, i);
|
||||||
ItemData data = itemIdToData.get(id, ItemData.EMPTY);
|
ItemData data = itemIdToData.get(id, ItemData.EMPTY);
|
||||||
|
mediaItems[i] = data.mediaItem;
|
||||||
durationsUs[i] = data.durationUs;
|
durationsUs[i] = data.durationUs;
|
||||||
defaultPositionsUs[i] = data.defaultPositionUs == C.TIME_UNSET ? 0 : data.defaultPositionUs;
|
defaultPositionsUs[i] = data.defaultPositionUs == C.TIME_UNSET ? 0 : data.defaultPositionUs;
|
||||||
isLive[i] = data.isLive;
|
isLive[i] = data.isLive;
|
||||||
|
|
@ -121,18 +150,16 @@ import java.util.Arrays;
|
||||||
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
|
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
|
||||||
long durationUs = durationsUs[windowIndex];
|
long durationUs = durationsUs[windowIndex];
|
||||||
boolean isDynamic = durationUs == C.TIME_UNSET;
|
boolean isDynamic = durationUs == C.TIME_UNSET;
|
||||||
MediaItem mediaItem =
|
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(ids[windowIndex]).build();
|
|
||||||
return window.set(
|
return window.set(
|
||||||
/* uid= */ ids[windowIndex],
|
/* uid= */ ids[windowIndex],
|
||||||
/* mediaItem= */ mediaItem,
|
/* mediaItem= */ mediaItems[windowIndex],
|
||||||
/* manifest= */ null,
|
/* manifest= */ null,
|
||||||
/* presentationStartTimeMs= */ C.TIME_UNSET,
|
/* presentationStartTimeMs= */ C.TIME_UNSET,
|
||||||
/* windowStartTimeMs= */ C.TIME_UNSET,
|
/* windowStartTimeMs= */ C.TIME_UNSET,
|
||||||
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
|
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
|
||||||
/* isSeekable= */ !isDynamic,
|
/* isSeekable= */ !isDynamic,
|
||||||
isDynamic,
|
isDynamic,
|
||||||
isLive[windowIndex] ? mediaItem.liveConfiguration : null,
|
isLive[windowIndex] ? mediaItems[windowIndex].liveConfiguration : null,
|
||||||
defaultPositionsUs[windowIndex],
|
defaultPositionsUs[windowIndex],
|
||||||
durationUs,
|
durationUs,
|
||||||
/* firstPeriodIndex= */ windowIndex,
|
/* firstPeriodIndex= */ windowIndex,
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,23 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.ext.cast;
|
package com.google.android.exoplayer2.ext.cast;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.ext.cast.CastTimeline.ItemData.UNKNOWN_CONTENT_ID;
|
||||||
|
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||||
|
import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
|
||||||
|
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.VisibleForTesting;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.gms.cast.MediaInfo;
|
import com.google.android.gms.cast.MediaInfo;
|
||||||
import com.google.android.gms.cast.MediaQueueItem;
|
import com.google.android.gms.cast.MediaQueueItem;
|
||||||
import com.google.android.gms.cast.MediaStatus;
|
import com.google.android.gms.cast.MediaStatus;
|
||||||
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
|
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates {@link CastTimeline CastTimelines} from cast receiver app status updates.
|
* Creates {@link CastTimeline CastTimelines} from cast receiver app status updates.
|
||||||
|
|
@ -33,9 +42,47 @@ import java.util.HashSet;
|
||||||
/* package */ final class CastTimelineTracker {
|
/* package */ final class CastTimelineTracker {
|
||||||
|
|
||||||
private final SparseArray<CastTimeline.ItemData> itemIdToData;
|
private final SparseArray<CastTimeline.ItemData> itemIdToData;
|
||||||
|
private final MediaItemConverter mediaItemConverter;
|
||||||
|
@VisibleForTesting /* package */ final HashMap<String, MediaItem> mediaItemsByContentId;
|
||||||
|
|
||||||
public CastTimelineTracker() {
|
/**
|
||||||
|
* Creates an instance.
|
||||||
|
*
|
||||||
|
* @param mediaItemConverter The converter used to convert from a {@link MediaQueueItem} to a
|
||||||
|
* {@link MediaItem}.
|
||||||
|
*/
|
||||||
|
public CastTimelineTracker(MediaItemConverter mediaItemConverter) {
|
||||||
|
this.mediaItemConverter = mediaItemConverter;
|
||||||
itemIdToData = new SparseArray<>();
|
itemIdToData = new SparseArray<>();
|
||||||
|
mediaItemsByContentId = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when media items {@linkplain Player#setMediaItems have been set to the playlist} and are
|
||||||
|
* sent to the cast playback queue. A future queue update of the {@link RemoteMediaClient} will
|
||||||
|
* reflect this addition.
|
||||||
|
*
|
||||||
|
* @param mediaItems The media items that have been set.
|
||||||
|
* @param mediaQueueItems The corresponding media queue items.
|
||||||
|
*/
|
||||||
|
public void onMediaItemsSet(List<MediaItem> mediaItems, MediaQueueItem[] mediaQueueItems) {
|
||||||
|
mediaItemsByContentId.clear();
|
||||||
|
onMediaItemsAdded(mediaItems, mediaQueueItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when media items {@linkplain Player#addMediaItems(List) have been added} and are sent to
|
||||||
|
* the cast playback queue. A future queue update of the {@link RemoteMediaClient} will reflect
|
||||||
|
* this addition.
|
||||||
|
*
|
||||||
|
* @param mediaItems The media items that have been added.
|
||||||
|
* @param mediaQueueItems The corresponding media queue items.
|
||||||
|
*/
|
||||||
|
public void onMediaItemsAdded(List<MediaItem> mediaItems, MediaQueueItem[] mediaQueueItems) {
|
||||||
|
for (int i = 0; i < mediaItems.size(); i++) {
|
||||||
|
mediaItemsByContentId.put(
|
||||||
|
checkNotNull(mediaQueueItems[i].getMedia()).getContentId(), mediaItems.get(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -63,18 +110,36 @@ import java.util.HashSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
int currentItemId = mediaStatus.getCurrentItemId();
|
int currentItemId = mediaStatus.getCurrentItemId();
|
||||||
|
String currentContentId = checkStateNotNull(mediaStatus.getMediaInfo()).getContentId();
|
||||||
|
MediaItem mediaItem = mediaItemsByContentId.get(currentContentId);
|
||||||
updateItemData(
|
updateItemData(
|
||||||
currentItemId, mediaStatus.getMediaInfo(), /* defaultPositionUs= */ C.TIME_UNSET);
|
currentItemId,
|
||||||
|
mediaItem != null ? mediaItem : MediaItem.EMPTY,
|
||||||
|
mediaStatus.getMediaInfo(),
|
||||||
|
currentContentId,
|
||||||
|
/* defaultPositionUs= */ C.TIME_UNSET);
|
||||||
|
|
||||||
for (MediaQueueItem item : mediaStatus.getQueueItems()) {
|
for (MediaQueueItem queueItem : mediaStatus.getQueueItems()) {
|
||||||
long defaultPositionUs = (long) (item.getStartTime() * C.MICROS_PER_SECOND);
|
long defaultPositionUs = (long) (queueItem.getStartTime() * C.MICROS_PER_SECOND);
|
||||||
updateItemData(item.getItemId(), item.getMedia(), defaultPositionUs);
|
@Nullable MediaInfo mediaInfo = queueItem.getMedia();
|
||||||
|
String contentId = mediaInfo != null ? mediaInfo.getContentId() : UNKNOWN_CONTENT_ID;
|
||||||
|
mediaItem = mediaItemsByContentId.get(contentId);
|
||||||
|
updateItemData(
|
||||||
|
queueItem.getItemId(),
|
||||||
|
mediaItem != null ? mediaItem : mediaItemConverter.toMediaItem(queueItem),
|
||||||
|
mediaInfo,
|
||||||
|
contentId,
|
||||||
|
defaultPositionUs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CastTimeline(itemIds, itemIdToData);
|
return new CastTimeline(itemIds, itemIdToData);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateItemData(int itemId, @Nullable MediaInfo mediaInfo, long defaultPositionUs) {
|
private void updateItemData(
|
||||||
|
int itemId,
|
||||||
|
MediaItem mediaItem,
|
||||||
|
@Nullable MediaInfo mediaInfo,
|
||||||
|
String contentId,
|
||||||
|
long defaultPositionUs) {
|
||||||
CastTimeline.ItemData previousData = itemIdToData.get(itemId, CastTimeline.ItemData.EMPTY);
|
CastTimeline.ItemData previousData = itemIdToData.get(itemId, CastTimeline.ItemData.EMPTY);
|
||||||
long durationUs = CastUtils.getStreamDurationUs(mediaInfo);
|
long durationUs = CastUtils.getStreamDurationUs(mediaInfo);
|
||||||
if (durationUs == C.TIME_UNSET) {
|
if (durationUs == C.TIME_UNSET) {
|
||||||
|
|
@ -87,7 +152,10 @@ import java.util.HashSet;
|
||||||
if (defaultPositionUs == C.TIME_UNSET) {
|
if (defaultPositionUs == C.TIME_UNSET) {
|
||||||
defaultPositionUs = previousData.defaultPositionUs;
|
defaultPositionUs = previousData.defaultPositionUs;
|
||||||
}
|
}
|
||||||
itemIdToData.put(itemId, previousData.copyWithNewValues(durationUs, defaultPositionUs, isLive));
|
itemIdToData.put(
|
||||||
|
itemId,
|
||||||
|
previousData.copyWithNewValues(
|
||||||
|
durationUs, defaultPositionUs, isLive, mediaItem, contentId));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeUnusedItemDataEntries(int[] itemIds) {
|
private void removeUnusedItemDataEntries(int[] itemIds) {
|
||||||
|
|
@ -99,6 +167,8 @@ import java.util.HashSet;
|
||||||
int index = 0;
|
int index = 0;
|
||||||
while (index < itemIdToData.size()) {
|
while (index < itemIdToData.size()) {
|
||||||
if (!scratchItemIds.contains(itemIdToData.keyAt(index))) {
|
if (!scratchItemIds.contains(itemIdToData.keyAt(index))) {
|
||||||
|
CastTimeline.ItemData itemData = itemIdToData.valueAt(index);
|
||||||
|
mediaItemsByContentId.remove(itemData.contentId);
|
||||||
itemIdToData.removeAt(index);
|
itemIdToData.removeAt(index);
|
||||||
} else {
|
} else {
|
||||||
index++;
|
index++;
|
||||||
|
|
|
||||||
|
|
@ -126,11 +126,14 @@ public final class DefaultMediaItemConverter implements MediaItemConverter {
|
||||||
if (mediaItem.mediaMetadata.trackNumber != null) {
|
if (mediaItem.mediaMetadata.trackNumber != null) {
|
||||||
metadata.putInt(MediaMetadata.KEY_TRACK_NUMBER, mediaItem.mediaMetadata.trackNumber);
|
metadata.putInt(MediaMetadata.KEY_TRACK_NUMBER, mediaItem.mediaMetadata.trackNumber);
|
||||||
}
|
}
|
||||||
|
String contentUrl = mediaItem.localConfiguration.uri.toString();
|
||||||
|
String contentId =
|
||||||
|
mediaItem.mediaId.equals(MediaItem.DEFAULT_MEDIA_ID) ? contentUrl : mediaItem.mediaId;
|
||||||
MediaInfo mediaInfo =
|
MediaInfo mediaInfo =
|
||||||
new MediaInfo.Builder(mediaItem.localConfiguration.uri.toString())
|
new MediaInfo.Builder(contentId)
|
||||||
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
||||||
.setContentType(mediaItem.localConfiguration.mimeType)
|
.setContentType(mediaItem.localConfiguration.mimeType)
|
||||||
|
.setContentUrl(contentUrl)
|
||||||
.setMetadata(metadata)
|
.setMetadata(metadata)
|
||||||
.setCustomData(getCustomData(mediaItem))
|
.setCustomData(getCustomData(mediaItem))
|
||||||
.build();
|
.build();
|
||||||
|
|
|
||||||
|
|
@ -64,8 +64,10 @@ import android.net.Uri;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.MediaItem;
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
|
import com.google.android.exoplayer2.MediaMetadata;
|
||||||
import com.google.android.exoplayer2.PlaybackParameters;
|
import com.google.android.exoplayer2.PlaybackParameters;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
|
import com.google.android.exoplayer2.Player.Listener;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
|
@ -98,6 +100,7 @@ import org.mockito.Mockito;
|
||||||
public class CastPlayerTest {
|
public class CastPlayerTest {
|
||||||
|
|
||||||
private CastPlayer castPlayer;
|
private CastPlayer castPlayer;
|
||||||
|
private DefaultMediaItemConverter mediaItemConverter;
|
||||||
private RemoteMediaClient.Callback remoteMediaClientCallback;
|
private RemoteMediaClient.Callback remoteMediaClientCallback;
|
||||||
|
|
||||||
@Mock private RemoteMediaClient mockRemoteMediaClient;
|
@Mock private RemoteMediaClient mockRemoteMediaClient;
|
||||||
|
|
@ -106,7 +109,7 @@ public class CastPlayerTest {
|
||||||
@Mock private CastContext mockCastContext;
|
@Mock private CastContext mockCastContext;
|
||||||
@Mock private SessionManager mockSessionManager;
|
@Mock private SessionManager mockSessionManager;
|
||||||
@Mock private CastSession mockCastSession;
|
@Mock private CastSession mockCastSession;
|
||||||
@Mock private Player.Listener mockListener;
|
@Mock private Listener mockListener;
|
||||||
@Mock private PendingResult<RemoteMediaClient.MediaChannelResult> mockPendingResult;
|
@Mock private PendingResult<RemoteMediaClient.MediaChannelResult> mockPendingResult;
|
||||||
|
|
||||||
@Captor
|
@Captor
|
||||||
|
|
@ -126,12 +129,14 @@ public class CastPlayerTest {
|
||||||
when(mockCastSession.getRemoteMediaClient()).thenReturn(mockRemoteMediaClient);
|
when(mockCastSession.getRemoteMediaClient()).thenReturn(mockRemoteMediaClient);
|
||||||
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(mockMediaStatus);
|
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(mockMediaStatus);
|
||||||
when(mockRemoteMediaClient.getMediaQueue()).thenReturn(mockMediaQueue);
|
when(mockRemoteMediaClient.getMediaQueue()).thenReturn(mockMediaQueue);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(new MediaInfo.Builder("contentId").build());
|
||||||
when(mockMediaQueue.getItemIds()).thenReturn(new int[0]);
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[0]);
|
||||||
// Make the remote media client present the same default values as ExoPlayer:
|
// Make the remote media client present the same default values as ExoPlayer:
|
||||||
when(mockRemoteMediaClient.isPaused()).thenReturn(true);
|
when(mockRemoteMediaClient.isPaused()).thenReturn(true);
|
||||||
when(mockMediaStatus.getQueueRepeatMode()).thenReturn(MediaStatus.REPEAT_MODE_REPEAT_OFF);
|
when(mockMediaStatus.getQueueRepeatMode()).thenReturn(MediaStatus.REPEAT_MODE_REPEAT_OFF);
|
||||||
when(mockMediaStatus.getPlaybackRate()).thenReturn(1.0d);
|
when(mockMediaStatus.getPlaybackRate()).thenReturn(1.0d);
|
||||||
castPlayer = new CastPlayer(mockCastContext);
|
mediaItemConverter = new DefaultMediaItemConverter();
|
||||||
|
castPlayer = new CastPlayer(mockCastContext, mediaItemConverter);
|
||||||
castPlayer.addListener(mockListener);
|
castPlayer.addListener(mockListener);
|
||||||
verify(mockRemoteMediaClient).registerCallback(callbackArgumentCaptor.capture());
|
verify(mockRemoteMediaClient).registerCallback(callbackArgumentCaptor.capture());
|
||||||
remoteMediaClientCallback = callbackArgumentCaptor.getValue();
|
remoteMediaClientCallback = callbackArgumentCaptor.getValue();
|
||||||
|
|
@ -388,7 +393,7 @@ public class CastPlayerTest {
|
||||||
mediaItems.add(
|
mediaItems.add(
|
||||||
new MediaItem.Builder().setUri(uri2).setMimeType(MimeTypes.APPLICATION_MP4).build());
|
new MediaItem.Builder().setUri(uri2).setMimeType(MimeTypes.APPLICATION_MP4).build());
|
||||||
|
|
||||||
castPlayer.setMediaItems(mediaItems, /* startWindowIndex= */ 1, /* startPositionMs= */ 2000L);
|
castPlayer.setMediaItems(mediaItems, /* startIndex= */ 1, /* startPositionMs= */ 2000L);
|
||||||
|
|
||||||
verify(mockRemoteMediaClient)
|
verify(mockRemoteMediaClient)
|
||||||
.queueLoad(queueItemsArgumentCaptor.capture(), eq(1), anyInt(), eq(2000L), any());
|
.queueLoad(queueItemsArgumentCaptor.capture(), eq(1), anyInt(), eq(2000L), any());
|
||||||
|
|
@ -434,22 +439,23 @@ public class CastPlayerTest {
|
||||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
castPlayer.setMediaItems(
|
castPlayer.setMediaItems(firstPlaylist, /* startIndex= */ 1, /* startPositionMs= */ 2000L);
|
||||||
firstPlaylist, /* startWindowIndex= */ 1, /* startPositionMs= */ 2000L);
|
|
||||||
updateTimeLine(
|
updateTimeLine(
|
||||||
firstPlaylist, /* mediaQueueItemIds= */ new int[] {1, 2}, /* currentItemId= */ 2);
|
firstPlaylist, /* mediaQueueItemIds= */ new int[] {1, 2}, /* currentItemId= */ 2);
|
||||||
// Replacing existing playlist.
|
// Replacing existing playlist.
|
||||||
castPlayer.setMediaItems(
|
castPlayer.setMediaItems(secondPlaylist, /* startIndex= */ 0, /* startPositionMs= */ 1000L);
|
||||||
secondPlaylist, /* startWindowIndex= */ 0, /* startPositionMs= */ 1000L);
|
|
||||||
updateTimeLine(secondPlaylist, /* mediaQueueItemIds= */ new int[] {3}, /* currentItemId= */ 3);
|
updateTimeLine(secondPlaylist, /* mediaQueueItemIds= */ new int[] {3}, /* currentItemId= */ 3);
|
||||||
|
|
||||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener, times(2))
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(
|
.onMediaItemTransition(
|
||||||
mediaItemCaptor.capture(), eq(MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
eq(firstPlaylist.get(1)), eq(MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
|
inOrder
|
||||||
|
.verify(mockListener)
|
||||||
|
.onMediaItemTransition(
|
||||||
|
eq(secondPlaylist.get(0)), eq(MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
assertThat(mediaItemCaptor.getAllValues().get(1).localConfiguration.tag).isEqualTo(3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("deprecation") // Verifies deprecated callback being called correctly.
|
@SuppressWarnings("deprecation") // Verifies deprecated callback being called correctly.
|
||||||
|
|
@ -469,8 +475,7 @@ public class CastPlayerTest {
|
||||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
.build());
|
.build());
|
||||||
|
|
||||||
castPlayer.setMediaItems(
|
castPlayer.setMediaItems(firstPlaylist, /* startIndex= */ 1, /* startPositionMs= */ 2000L);
|
||||||
firstPlaylist, /* startWindowIndex= */ 1, /* startPositionMs= */ 2000L);
|
|
||||||
updateTimeLine(
|
updateTimeLine(
|
||||||
firstPlaylist,
|
firstPlaylist,
|
||||||
/* mediaQueueItemIds= */ new int[] {1, 2},
|
/* mediaQueueItemIds= */ new int[] {1, 2},
|
||||||
|
|
@ -481,8 +486,7 @@ public class CastPlayerTest {
|
||||||
/* durationsMs= */ new long[] {20_000, 20_000},
|
/* durationsMs= */ new long[] {20_000, 20_000},
|
||||||
/* positionMs= */ 2000L);
|
/* positionMs= */ 2000L);
|
||||||
// Replacing existing playlist.
|
// Replacing existing playlist.
|
||||||
castPlayer.setMediaItems(
|
castPlayer.setMediaItems(secondPlaylist, /* startIndex= */ 0, /* startPositionMs= */ 1000L);
|
||||||
secondPlaylist, /* startWindowIndex= */ 0, /* startPositionMs= */ 1000L);
|
|
||||||
updateTimeLine(
|
updateTimeLine(
|
||||||
secondPlaylist,
|
secondPlaylist,
|
||||||
/* mediaQueueItemIds= */ new int[] {3},
|
/* mediaQueueItemIds= */ new int[] {3},
|
||||||
|
|
@ -494,8 +498,8 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 2,
|
/* windowUid= */ 2,
|
||||||
/* windowIndex= */ 1,
|
/* mediaItemIndex= */ 1,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
firstPlaylist.get(1),
|
||||||
/* periodUid= */ 2,
|
/* periodUid= */ 2,
|
||||||
/* periodIndex= */ 1,
|
/* periodIndex= */ 1,
|
||||||
/* positionMs= */ 2000,
|
/* positionMs= */ 2000,
|
||||||
|
|
@ -505,8 +509,8 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 3,
|
/* windowUid= */ 3,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(3).build(),
|
secondPlaylist.get(0),
|
||||||
/* periodUid= */ 3,
|
/* periodUid= */ 3,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 1000,
|
/* positionMs= */ 1000,
|
||||||
|
|
@ -536,34 +540,37 @@ public class CastPlayerTest {
|
||||||
verify(mockRemoteMediaClient)
|
verify(mockRemoteMediaClient)
|
||||||
.queueInsertItems(
|
.queueInsertItems(
|
||||||
queueItemsArgumentCaptor.capture(), eq(MediaQueueItem.INVALID_ITEM_ID), any());
|
queueItemsArgumentCaptor.capture(), eq(MediaQueueItem.INVALID_ITEM_ID), any());
|
||||||
|
|
||||||
MediaQueueItem[] mediaQueueItems = queueItemsArgumentCaptor.getValue();
|
MediaQueueItem[] mediaQueueItems = queueItemsArgumentCaptor.getValue();
|
||||||
assertThat(mediaQueueItems[0].getMedia().getContentId()).isEqualTo(uri1);
|
assertThat(mediaQueueItems[0].getMedia().getContentId()).isEqualTo(uri1);
|
||||||
assertThat(mediaQueueItems[1].getMedia().getContentId()).isEqualTo(uri2);
|
assertThat(mediaQueueItems[1].getMedia().getContentId()).isEqualTo(uri2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
@Test
|
@Test
|
||||||
public void addMediaItems_insertAtIndex_callsRemoteMediaClient() {
|
public void addMediaItems_insertAtIndex_callsRemoteMediaClient() {
|
||||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 2);
|
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 2);
|
||||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||||
|
// Add two items.
|
||||||
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
||||||
String uri = "http://www.google.com/video3";
|
String uri = "http://www.google.com/video3";
|
||||||
MediaItem anotherMediaItem =
|
MediaItem anotherMediaItem =
|
||||||
new MediaItem.Builder().setUri(uri).setMimeType(MimeTypes.APPLICATION_MPD).build();
|
new MediaItem.Builder().setUri(uri).setMimeType(MimeTypes.APPLICATION_MPD).build();
|
||||||
|
int index = 1;
|
||||||
|
List<MediaItem> newPlaylist = Collections.singletonList(anotherMediaItem);
|
||||||
|
|
||||||
// Add another on position 1
|
// Add another on position 1
|
||||||
int index = 1;
|
castPlayer.addMediaItems(index, newPlaylist);
|
||||||
castPlayer.addMediaItems(index, Collections.singletonList(anotherMediaItem));
|
updateTimeLine(newPlaylist, /* mediaQueueItemIds= */ new int[] {123}, /* currentItemId= */ 1);
|
||||||
|
|
||||||
verify(mockRemoteMediaClient)
|
verify(mockRemoteMediaClient, times(2))
|
||||||
.queueInsertItems(
|
.queueInsertItems(queueItemsArgumentCaptor.capture(), anyInt(), any());
|
||||||
queueItemsArgumentCaptor.capture(),
|
assertThat(queueItemsArgumentCaptor.getAllValues().get(1)[0])
|
||||||
eq((int) mediaItems.get(index).localConfiguration.tag),
|
.isEqualTo(mediaItemConverter.toMediaQueueItem(anotherMediaItem));
|
||||||
any());
|
Timeline.Window currentWindow =
|
||||||
|
castPlayer
|
||||||
MediaQueueItem[] mediaQueueItems = queueItemsArgumentCaptor.getValue();
|
.getCurrentTimeline()
|
||||||
assertThat(mediaQueueItems[0].getMedia().getContentId()).isEqualTo(uri);
|
.getWindow(castPlayer.getCurrentMediaItemIndex(), new Timeline.Window());
|
||||||
|
assertThat(currentWindow.uid).isEqualTo(123);
|
||||||
|
assertThat(currentWindow.mediaItem).isEqualTo(anotherMediaItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -702,8 +709,8 @@ public class CastPlayerTest {
|
||||||
|
|
||||||
Timeline currentTimeline = castPlayer.getCurrentTimeline();
|
Timeline currentTimeline = castPlayer.getCurrentTimeline();
|
||||||
for (int i = 0; i < mediaItems.size(); i++) {
|
for (int i = 0; i < mediaItems.size(); i++) {
|
||||||
assertThat(currentTimeline.getWindow(/* windowIndex= */ i, window).uid)
|
assertThat(currentTimeline.getWindow(/* windowIndex= */ i, window).mediaItem)
|
||||||
.isEqualTo(mediaItems.get(i).localConfiguration.tag);
|
.isEqualTo(mediaItems.get(i));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -720,10 +727,8 @@ public class CastPlayerTest {
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(
|
.onMediaItemTransition(
|
||||||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
eq(mediaItem), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
assertThat(mediaItemCaptor.getValue().localConfiguration.tag)
|
|
||||||
.isEqualTo(mediaItem.localConfiguration.tag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -742,7 +747,8 @@ public class CastPlayerTest {
|
||||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
.onMediaItemTransition(
|
||||||
|
eq(mediaItems.get(0)), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(
|
.onMediaItemTransition(
|
||||||
|
|
@ -776,8 +782,8 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 1234,
|
/* positionMs= */ 1234,
|
||||||
|
|
@ -787,7 +793,7 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ null,
|
/* windowUid= */ null,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
/* mediaItem= */ null,
|
/* mediaItem= */ null,
|
||||||
/* periodUid= */ null,
|
/* periodUid= */ null,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
|
|
@ -827,10 +833,8 @@ public class CastPlayerTest {
|
||||||
.onMediaItemTransition(
|
.onMediaItemTransition(
|
||||||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
assertThat(mediaItemCaptor.getAllValues().get(0).localConfiguration.tag)
|
assertThat(mediaItemCaptor.getAllValues().get(0)).isEqualTo(mediaItem1);
|
||||||
.isEqualTo(mediaItem1.localConfiguration.tag);
|
assertThat(mediaItemCaptor.getAllValues().get(1)).isEqualTo(mediaItem2);
|
||||||
assertThat(mediaItemCaptor.getAllValues().get(1).localConfiguration.tag)
|
|
||||||
.isEqualTo(mediaItem2.localConfiguration.tag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -862,8 +866,8 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
mediaItem1,
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 1234,
|
/* positionMs= */ 1234,
|
||||||
|
|
@ -873,8 +877,8 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 2,
|
/* windowUid= */ 2,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
mediaItem2,
|
||||||
/* periodUid= */ 2,
|
/* periodUid= */ 2,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 0,
|
/* positionMs= */ 0,
|
||||||
|
|
@ -912,10 +916,8 @@ public class CastPlayerTest {
|
||||||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
List<MediaItem> capturedMediaItems = mediaItemCaptor.getAllValues();
|
List<MediaItem> capturedMediaItems = mediaItemCaptor.getAllValues();
|
||||||
assertThat(capturedMediaItems.get(0).localConfiguration.tag)
|
assertThat(capturedMediaItems.get(0)).isEqualTo(mediaItem1);
|
||||||
.isEqualTo(mediaItem1.localConfiguration.tag);
|
assertThat(capturedMediaItems.get(1)).isEqualTo(mediaItem2);
|
||||||
assertThat(capturedMediaItems.get(1).localConfiguration.tag)
|
|
||||||
.isEqualTo(mediaItem2.localConfiguration.tag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -945,8 +947,8 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
mediaItem1,
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 0, // position at which we receive the timeline change
|
/* positionMs= */ 0, // position at which we receive the timeline change
|
||||||
|
|
@ -956,8 +958,8 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 2,
|
/* windowUid= */ 2,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
mediaItem2,
|
||||||
/* periodUid= */ 2,
|
/* periodUid= */ 2,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 0,
|
/* positionMs= */ 0,
|
||||||
|
|
@ -992,7 +994,8 @@ public class CastPlayerTest {
|
||||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
.onMediaItemTransition(
|
||||||
|
eq(mediaItem1), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1027,19 +1030,21 @@ public class CastPlayerTest {
|
||||||
|
|
||||||
castPlayer.addMediaItems(mediaItems);
|
castPlayer.addMediaItems(mediaItems);
|
||||||
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
||||||
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 1234);
|
MediaMetadata firstMediaMetadata = castPlayer.getMediaMetadata();
|
||||||
|
castPlayer.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 1234);
|
||||||
|
MediaMetadata secondMediaMetadata = castPlayer.getMediaMetadata();
|
||||||
|
|
||||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
.onMediaItemTransition(
|
||||||
|
eq(mediaItem1), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(
|
.onMediaItemTransition(eq(mediaItem2), eq(Player.MEDIA_ITEM_TRANSITION_REASON_SEEK));
|
||||||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_SEEK));
|
|
||||||
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
||||||
assertThat(mediaItemCaptor.getValue().localConfiguration.tag)
|
assertThat(firstMediaMetadata).isEqualTo(mediaItem1.mediaMetadata);
|
||||||
.isEqualTo(mediaItem2.localConfiguration.tag);
|
assertThat(secondMediaMetadata).isEqualTo(mediaItem2.mediaMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -1054,13 +1059,13 @@ public class CastPlayerTest {
|
||||||
|
|
||||||
castPlayer.addMediaItems(mediaItems);
|
castPlayer.addMediaItems(mediaItems);
|
||||||
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
||||||
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 1234);
|
castPlayer.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 1234);
|
||||||
|
|
||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
mediaItem1,
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 0,
|
/* positionMs= */ 0,
|
||||||
|
|
@ -1070,8 +1075,8 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 2,
|
/* windowUid= */ 2,
|
||||||
/* windowIndex= */ 1,
|
/* mediaItemIndex= */ 1,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
mediaItem2,
|
||||||
/* periodUid= */ 2,
|
/* periodUid= */ 2,
|
||||||
/* periodIndex= */ 1,
|
/* periodIndex= */ 1,
|
||||||
/* positionMs= */ 1234,
|
/* positionMs= */ 1234,
|
||||||
|
|
@ -1097,12 +1102,13 @@ public class CastPlayerTest {
|
||||||
|
|
||||||
castPlayer.addMediaItems(mediaItems);
|
castPlayer.addMediaItems(mediaItems);
|
||||||
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
||||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 1234);
|
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 1234);
|
||||||
|
|
||||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
.onMediaItemTransition(
|
||||||
|
eq(mediaItems.get(0)), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1115,14 +1121,13 @@ public class CastPlayerTest {
|
||||||
|
|
||||||
castPlayer.addMediaItems(mediaItems);
|
castPlayer.addMediaItems(mediaItems);
|
||||||
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
||||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 1234);
|
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 1234);
|
||||||
|
|
||||||
MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build();
|
|
||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 0,
|
/* positionMs= */ 0,
|
||||||
|
|
@ -1132,8 +1137,8 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 1234,
|
/* positionMs= */ 1234,
|
||||||
|
|
@ -1164,13 +1169,12 @@ public class CastPlayerTest {
|
||||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(any(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
.onMediaItemTransition(
|
||||||
|
eq(mediaItems.get(0)), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||||
inOrder
|
inOrder
|
||||||
.verify(mockListener)
|
.verify(mockListener)
|
||||||
.onMediaItemTransition(
|
.onMediaItemTransition(eq(mediaItems.get(1)), eq(Player.MEDIA_ITEM_TRANSITION_REASON_AUTO));
|
||||||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_AUTO));
|
|
||||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||||
assertThat(mediaItemCaptor.getValue().localConfiguration.tag).isEqualTo(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -1203,8 +1207,8 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 12500,
|
/* positionMs= */ 12500,
|
||||||
|
|
@ -1214,8 +1218,8 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 2,
|
/* windowUid= */ 2,
|
||||||
/* windowIndex= */ 1,
|
/* mediaItemIndex= */ 1,
|
||||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
mediaItems.get(1),
|
||||||
/* periodUid= */ 2,
|
/* periodUid= */ 2,
|
||||||
/* periodIndex= */ 1,
|
/* periodIndex= */ 1,
|
||||||
/* positionMs= */ 0,
|
/* positionMs= */ 0,
|
||||||
|
|
@ -1250,12 +1254,11 @@ public class CastPlayerTest {
|
||||||
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
|
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
|
||||||
castPlayer.seekBack();
|
castPlayer.seekBack();
|
||||||
|
|
||||||
MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build();
|
|
||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 2 * C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
/* positionMs= */ 2 * C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
||||||
|
|
@ -1265,8 +1268,8 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
/* positionMs= */ C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
||||||
|
|
@ -1299,12 +1302,11 @@ public class CastPlayerTest {
|
||||||
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
|
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
|
||||||
castPlayer.seekForward();
|
castPlayer.seekForward();
|
||||||
|
|
||||||
MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build();
|
|
||||||
Player.PositionInfo oldPosition =
|
Player.PositionInfo oldPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ 0,
|
/* positionMs= */ 0,
|
||||||
|
|
@ -1314,8 +1316,8 @@ public class CastPlayerTest {
|
||||||
Player.PositionInfo newPosition =
|
Player.PositionInfo newPosition =
|
||||||
new Player.PositionInfo(
|
new Player.PositionInfo(
|
||||||
/* windowUid= */ 1,
|
/* windowUid= */ 1,
|
||||||
/* windowIndex= */ 0,
|
/* mediaItemIndex= */ 0,
|
||||||
mediaItem,
|
mediaItems.get(0),
|
||||||
/* periodUid= */ 1,
|
/* periodUid= */ 1,
|
||||||
/* periodIndex= */ 0,
|
/* periodIndex= */ 0,
|
||||||
/* positionMs= */ C.DEFAULT_SEEK_FORWARD_INCREMENT_MS,
|
/* positionMs= */ C.DEFAULT_SEEK_FORWARD_INCREMENT_MS,
|
||||||
|
|
@ -1475,14 +1477,14 @@ public class CastPlayerTest {
|
||||||
// Check that there were no other calls to onAvailableCommandsChanged.
|
// Check that there were no other calls to onAvailableCommandsChanged.
|
||||||
verify(mockListener).onAvailableCommandsChanged(any());
|
verify(mockListener).onAvailableCommandsChanged(any());
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
|
castPlayer.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 0);
|
||||||
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
|
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
|
||||||
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0);
|
castPlayer.seekTo(/* mediaItemIndex= */ 2, /* positionMs= */ 0);
|
||||||
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 3, /* positionMs= */ 0);
|
castPlayer.seekTo(/* mediaItemIndex= */ 3, /* positionMs= */ 0);
|
||||||
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
|
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
|
||||||
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
||||||
}
|
}
|
||||||
|
|
@ -1509,14 +1511,14 @@ public class CastPlayerTest {
|
||||||
// Check that there were no other calls to onAvailableCommandsChanged.
|
// Check that there were no other calls to onAvailableCommandsChanged.
|
||||||
verify(mockListener).onAvailableCommandsChanged(any());
|
verify(mockListener).onAvailableCommandsChanged(any());
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0);
|
castPlayer.seekTo(/* mediaItemIndex= */ 2, /* positionMs= */ 0);
|
||||||
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
|
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
|
||||||
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
|
castPlayer.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 0);
|
||||||
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 0);
|
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 0);
|
||||||
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
|
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
|
||||||
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
||||||
}
|
}
|
||||||
|
|
@ -1533,8 +1535,8 @@ public class CastPlayerTest {
|
||||||
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
||||||
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
|
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
|
||||||
|
|
||||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 200);
|
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 200);
|
||||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 100);
|
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 100);
|
||||||
// Check that there were no other calls to onAvailableCommandsChanged.
|
// Check that there were no other calls to onAvailableCommandsChanged.
|
||||||
verify(mockListener).onAvailableCommandsChanged(any());
|
verify(mockListener).onAvailableCommandsChanged(any());
|
||||||
}
|
}
|
||||||
|
|
@ -1763,6 +1765,105 @@ public class CastPlayerTest {
|
||||||
verify(mockListener).onAvailableCommandsChanged(any());
|
verify(mockListener).onAvailableCommandsChanged(any());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setMediaItems_doesNotifyOnMetadataChanged() {
|
||||||
|
when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null)))
|
||||||
|
.thenReturn(mockPendingResult);
|
||||||
|
ArgumentCaptor<MediaMetadata> metadataCaptor = ArgumentCaptor.forClass(MediaMetadata.class);
|
||||||
|
String uri1 = "http://www.google.com/video1";
|
||||||
|
String uri2 = "http://www.google.com/video2";
|
||||||
|
ImmutableList<MediaItem> firstPlaylist =
|
||||||
|
ImmutableList.of(
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setUri(uri1)
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
|
.setMediaMetadata(new MediaMetadata.Builder().setArtist("foo").build())
|
||||||
|
.build());
|
||||||
|
ImmutableList<MediaItem> secondPlaylist =
|
||||||
|
ImmutableList.of(
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setUri(Uri.EMPTY)
|
||||||
|
.setMediaMetadata(new MediaMetadata.Builder().setArtist("bar").build())
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
|
.build(),
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setUri(uri2)
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MP4)
|
||||||
|
.setMediaMetadata(new MediaMetadata.Builder().setArtist("foobar").build())
|
||||||
|
.build());
|
||||||
|
castPlayer.addListener(mockListener);
|
||||||
|
|
||||||
|
MediaMetadata intitalMetadata = castPlayer.getMediaMetadata();
|
||||||
|
castPlayer.setMediaItems(firstPlaylist, /* startIndex= */ 0, /* startPositionMs= */ 2000L);
|
||||||
|
updateTimeLine(firstPlaylist, /* mediaQueueItemIds= */ new int[] {1}, /* currentItemId= */ 1);
|
||||||
|
MediaMetadata firstMetadata = castPlayer.getMediaMetadata();
|
||||||
|
// Replacing existing playlist.
|
||||||
|
castPlayer.setMediaItems(secondPlaylist, /* startIndex= */ 1, /* startPositionMs= */ 0L);
|
||||||
|
updateTimeLine(
|
||||||
|
secondPlaylist, /* mediaQueueItemIds= */ new int[] {2, 3}, /* currentItemId= */ 3);
|
||||||
|
MediaMetadata secondMetadata = castPlayer.getMediaMetadata();
|
||||||
|
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 0);
|
||||||
|
MediaMetadata thirdMetadata = castPlayer.getMediaMetadata();
|
||||||
|
|
||||||
|
verify(mockListener, times(3)).onMediaItemTransition(mediaItemCaptor.capture(), anyInt());
|
||||||
|
assertThat(mediaItemCaptor.getAllValues())
|
||||||
|
.containsExactly(firstPlaylist.get(0), secondPlaylist.get(1), secondPlaylist.get(0))
|
||||||
|
.inOrder();
|
||||||
|
verify(mockListener, times(3)).onMediaMetadataChanged(metadataCaptor.capture());
|
||||||
|
assertThat(metadataCaptor.getAllValues())
|
||||||
|
.containsExactly(
|
||||||
|
firstPlaylist.get(0).mediaMetadata,
|
||||||
|
secondPlaylist.get(1).mediaMetadata,
|
||||||
|
secondPlaylist.get(0).mediaMetadata)
|
||||||
|
.inOrder();
|
||||||
|
assertThat(intitalMetadata).isEqualTo(MediaMetadata.EMPTY);
|
||||||
|
assertThat(ImmutableList.of(firstMetadata, secondMetadata, thirdMetadata))
|
||||||
|
.containsExactly(
|
||||||
|
firstPlaylist.get(0).mediaMetadata,
|
||||||
|
secondPlaylist.get(1).mediaMetadata,
|
||||||
|
secondPlaylist.get(0).mediaMetadata)
|
||||||
|
.inOrder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setMediaItems_equalMetadata_doesNotNotifyOnMediaMetadataChanged() {
|
||||||
|
when(mockRemoteMediaClient.queueJumpToItem(anyInt(), anyLong(), eq(null)))
|
||||||
|
.thenReturn(mockPendingResult);
|
||||||
|
String uri1 = "http://www.google.com/video1";
|
||||||
|
String uri2 = "http://www.google.com/video2";
|
||||||
|
ImmutableList<MediaItem> firstPlaylist =
|
||||||
|
ImmutableList.of(
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setUri(uri1)
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
|
.setTag(1)
|
||||||
|
.build());
|
||||||
|
ImmutableList<MediaItem> secondPlaylist =
|
||||||
|
ImmutableList.of(
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setMediaMetadata(MediaMetadata.EMPTY)
|
||||||
|
.setUri(Uri.EMPTY)
|
||||||
|
.setTag(2)
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
|
.build(),
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setUri(uri2)
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MP4)
|
||||||
|
.setTag(3)
|
||||||
|
.build());
|
||||||
|
castPlayer.addListener(mockListener);
|
||||||
|
|
||||||
|
castPlayer.setMediaItems(firstPlaylist, /* startIndex= */ 0, /* startPositionMs= */ 2000L);
|
||||||
|
updateTimeLine(firstPlaylist, /* mediaQueueItemIds= */ new int[] {1}, /* currentItemId= */ 1);
|
||||||
|
castPlayer.setMediaItems(secondPlaylist, /* startIndex= */ 1, /* startPositionMs= */ 0L);
|
||||||
|
updateTimeLine(
|
||||||
|
secondPlaylist, /* mediaQueueItemIds= */ new int[] {2, 3}, /* currentItemId= */ 3);
|
||||||
|
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 0);
|
||||||
|
|
||||||
|
verify(mockListener, times(3)).onMediaItemTransition(any(), anyInt());
|
||||||
|
verify(mockListener, never()).onMediaMetadataChanged(any());
|
||||||
|
}
|
||||||
|
|
||||||
private int[] createMediaQueueItemIds(int numberOfIds) {
|
private int[] createMediaQueueItemIds(int numberOfIds) {
|
||||||
int[] mediaQueueItemIds = new int[numberOfIds];
|
int[] mediaQueueItemIds = new int[numberOfIds];
|
||||||
for (int i = 0; i < numberOfIds; i++) {
|
for (int i = 0; i < numberOfIds; i++) {
|
||||||
|
|
@ -1782,8 +1883,9 @@ public class CastPlayerTest {
|
||||||
private MediaItem createMediaItem(int mediaQueueItemId) {
|
private MediaItem createMediaItem(int mediaQueueItemId) {
|
||||||
return new MediaItem.Builder()
|
return new MediaItem.Builder()
|
||||||
.setUri("http://www.google.com/video" + mediaQueueItemId)
|
.setUri("http://www.google.com/video" + mediaQueueItemId)
|
||||||
|
.setMediaMetadata(
|
||||||
|
new MediaMetadata.Builder().setArtist("Foo Bar - " + mediaQueueItemId).build())
|
||||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
.setTag(mediaQueueItemId)
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1821,8 +1923,12 @@ public class CastPlayerTest {
|
||||||
int mediaQueueItemId = mediaQueueItemIds[i];
|
int mediaQueueItemId = mediaQueueItemIds[i];
|
||||||
int streamType = streamTypes[i];
|
int streamType = streamTypes[i];
|
||||||
long durationMs = durationsMs[i];
|
long durationMs = durationsMs[i];
|
||||||
|
String contentId =
|
||||||
|
mediaItem.mediaId.equals(MediaItem.DEFAULT_MEDIA_ID)
|
||||||
|
? mediaItem.localConfiguration.uri.toString()
|
||||||
|
: mediaItem.mediaId;
|
||||||
MediaInfo.Builder mediaInfoBuilder =
|
MediaInfo.Builder mediaInfoBuilder =
|
||||||
new MediaInfo.Builder(mediaItem.localConfiguration.uri.toString())
|
new MediaInfo.Builder(contentId)
|
||||||
.setStreamType(streamType)
|
.setStreamType(streamType)
|
||||||
.setContentType(mediaItem.localConfiguration.mimeType);
|
.setContentType(mediaItem.localConfiguration.mimeType);
|
||||||
if (durationMs != C.TIME_UNSET) {
|
if (durationMs != C.TIME_UNSET) {
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,30 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.ext.cast;
|
package com.google.android.exoplayer2.ext.cast;
|
||||||
|
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
|
import com.google.android.exoplayer2.Timeline.Window;
|
||||||
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
import com.google.android.exoplayer2.testutil.TimelineAsserts;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import com.google.android.gms.cast.MediaInfo;
|
import com.google.android.gms.cast.MediaInfo;
|
||||||
|
import com.google.android.gms.cast.MediaQueueItem;
|
||||||
import com.google.android.gms.cast.MediaStatus;
|
import com.google.android.gms.cast.MediaStatus;
|
||||||
import com.google.android.gms.cast.framework.media.MediaQueue;
|
import com.google.android.gms.cast.framework.media.MediaQueue;
|
||||||
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
|
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.mockito.Mockito;
|
|
||||||
|
|
||||||
/** Tests for {@link CastTimelineTracker}. */
|
/** Tests for {@link CastTimelineTracker}. */
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
|
@ -40,10 +49,19 @@ public class CastTimelineTrackerTest {
|
||||||
private static final long DURATION_4_MS = 4000;
|
private static final long DURATION_4_MS = 4000;
|
||||||
private static final long DURATION_5_MS = 5000;
|
private static final long DURATION_5_MS = 5000;
|
||||||
|
|
||||||
|
private MediaItemConverter mediaItemConverter;
|
||||||
|
private CastTimelineTracker castTimelineTracker;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
mediaItemConverter = new DefaultMediaItemConverter();
|
||||||
|
castTimelineTracker = new CastTimelineTracker(mediaItemConverter);
|
||||||
|
}
|
||||||
|
|
||||||
/** Tests that duration of the current media info is correctly propagated to the timeline. */
|
/** Tests that duration of the current media info is correctly propagated to the timeline. */
|
||||||
@Test
|
@Test
|
||||||
public void getCastTimelinePersistsDuration() {
|
public void getCastTimelinePersistsDuration() {
|
||||||
CastTimelineTracker tracker = new CastTimelineTracker();
|
CastTimelineTracker tracker = new CastTimelineTracker(new DefaultMediaItemConverter());
|
||||||
|
|
||||||
RemoteMediaClient remoteMediaClient =
|
RemoteMediaClient remoteMediaClient =
|
||||||
mockRemoteMediaClient(
|
mockRemoteMediaClient(
|
||||||
|
|
@ -104,10 +122,179 @@ public class CastTimelineTrackerTest {
|
||||||
Util.msToUs(DURATION_5_MS));
|
Util.msToUs(DURATION_5_MS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCastTimeline_onMediaItemsSet_correctMediaItemsInTimeline() {
|
||||||
|
RemoteMediaClient mockRemoteMediaClient = mock(RemoteMediaClient.class);
|
||||||
|
MediaQueue mockMediaQueue = mock(MediaQueue.class);
|
||||||
|
MediaStatus mockMediaStatus = mock(MediaStatus.class);
|
||||||
|
ImmutableList<MediaItem> playlistMediaItems =
|
||||||
|
ImmutableList.of(createMediaItem(0), createMediaItem(1));
|
||||||
|
MediaQueueItem[] playlistMediaQueueItems =
|
||||||
|
new MediaQueueItem[] {
|
||||||
|
createMediaQueueItem(playlistMediaItems.get(0), 0),
|
||||||
|
createMediaQueueItem(playlistMediaItems.get(1), 1)
|
||||||
|
};
|
||||||
|
castTimelineTracker.onMediaItemsSet(playlistMediaItems, playlistMediaQueueItems);
|
||||||
|
// Mock remote media client state after adding two items.
|
||||||
|
when(mockRemoteMediaClient.getMediaQueue()).thenReturn(mockMediaQueue);
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[] {0, 1});
|
||||||
|
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(mockMediaStatus);
|
||||||
|
when(mockMediaStatus.getCurrentItemId()).thenReturn(0);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(playlistMediaQueueItems[0].getMedia());
|
||||||
|
when(mockMediaStatus.getQueueItems()).thenReturn(Arrays.asList(playlistMediaQueueItems));
|
||||||
|
|
||||||
|
CastTimeline castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(2);
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 0, new Window()).mediaItem)
|
||||||
|
.isEqualTo(playlistMediaItems.get(0));
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 1, new Window()).mediaItem)
|
||||||
|
.isEqualTo(playlistMediaItems.get(1));
|
||||||
|
|
||||||
|
MediaItem thirdMediaItem = createMediaItem(2);
|
||||||
|
MediaQueueItem thirdMediaQueueItem = createMediaQueueItem(thirdMediaItem, 2);
|
||||||
|
castTimelineTracker.onMediaItemsSet(
|
||||||
|
ImmutableList.of(thirdMediaItem), new MediaQueueItem[] {thirdMediaQueueItem});
|
||||||
|
// Mock remote media client state after a single item overrides the previous playlist.
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[] {2});
|
||||||
|
when(mockMediaStatus.getCurrentItemId()).thenReturn(2);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(thirdMediaQueueItem.getMedia());
|
||||||
|
when(mockMediaStatus.getQueueItems()).thenReturn(ImmutableList.of(thirdMediaQueueItem));
|
||||||
|
|
||||||
|
castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(1);
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 0, new Window()).mediaItem)
|
||||||
|
.isEqualTo(thirdMediaItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCastTimeline_onMediaItemsAdded_correctMediaItemsInTimeline() {
|
||||||
|
RemoteMediaClient mockRemoteMediaClient = mock(RemoteMediaClient.class);
|
||||||
|
MediaQueue mockMediaQueue = mock(MediaQueue.class);
|
||||||
|
MediaStatus mockMediaStatus = mock(MediaStatus.class);
|
||||||
|
ImmutableList<MediaItem> playlistMediaItems =
|
||||||
|
ImmutableList.of(createMediaItem(0), createMediaItem(1));
|
||||||
|
MediaQueueItem[] playlistQueueItems =
|
||||||
|
new MediaQueueItem[] {
|
||||||
|
createMediaQueueItem(playlistMediaItems.get(0), /* uid= */ 0),
|
||||||
|
createMediaQueueItem(playlistMediaItems.get(1), /* uid= */ 1)
|
||||||
|
};
|
||||||
|
ImmutableList<MediaItem> secondPlaylistMediaItems =
|
||||||
|
new ImmutableList.Builder<MediaItem>()
|
||||||
|
.addAll(playlistMediaItems)
|
||||||
|
.add(createMediaItem(2))
|
||||||
|
.build();
|
||||||
|
castTimelineTracker.onMediaItemsAdded(playlistMediaItems, playlistQueueItems);
|
||||||
|
when(mockRemoteMediaClient.getMediaQueue()).thenReturn(mockMediaQueue);
|
||||||
|
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(mockMediaStatus);
|
||||||
|
// Mock remote media client state after two items have been added.
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[] {0, 1});
|
||||||
|
when(mockMediaStatus.getCurrentItemId()).thenReturn(0);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(playlistQueueItems[0].getMedia());
|
||||||
|
when(mockMediaStatus.getQueueItems()).thenReturn(Arrays.asList(playlistQueueItems));
|
||||||
|
|
||||||
|
CastTimeline castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(2);
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 0, new Window()).mediaItem)
|
||||||
|
.isEqualTo(playlistMediaItems.get(0));
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 1, new Window()).mediaItem)
|
||||||
|
.isEqualTo(playlistMediaItems.get(1));
|
||||||
|
|
||||||
|
// Mock remote media client state after adding a third item.
|
||||||
|
List<MediaQueueItem> playlistThreeQueueItems =
|
||||||
|
new ArrayList<>(Arrays.asList(playlistQueueItems));
|
||||||
|
playlistThreeQueueItems.add(createMediaQueueItem(secondPlaylistMediaItems.get(2), 2));
|
||||||
|
castTimelineTracker.onMediaItemsAdded(
|
||||||
|
secondPlaylistMediaItems, playlistThreeQueueItems.toArray(new MediaQueueItem[0]));
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[] {0, 1, 2});
|
||||||
|
when(mockMediaStatus.getQueueItems()).thenReturn(playlistThreeQueueItems);
|
||||||
|
|
||||||
|
castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(3);
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 0, new Window()).mediaItem)
|
||||||
|
.isEqualTo(secondPlaylistMediaItems.get(0));
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 1, new Window()).mediaItem)
|
||||||
|
.isEqualTo(secondPlaylistMediaItems.get(1));
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 2, new Window()).mediaItem)
|
||||||
|
.isEqualTo(secondPlaylistMediaItems.get(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void getCastTimeline_itemsRemoved_correctMediaItemsInTimelineAndMapCleanedUp() {
|
||||||
|
RemoteMediaClient mockRemoteMediaClient = mock(RemoteMediaClient.class);
|
||||||
|
MediaQueue mockMediaQueue = mock(MediaQueue.class);
|
||||||
|
MediaStatus mockMediaStatus = mock(MediaStatus.class);
|
||||||
|
ImmutableList<MediaItem> playlistMediaItems =
|
||||||
|
ImmutableList.of(createMediaItem(0), createMediaItem(1));
|
||||||
|
MediaQueueItem[] initialPlaylistTwoQueueItems =
|
||||||
|
new MediaQueueItem[] {
|
||||||
|
createMediaQueueItem(playlistMediaItems.get(0), 0),
|
||||||
|
createMediaQueueItem(playlistMediaItems.get(1), 1)
|
||||||
|
};
|
||||||
|
castTimelineTracker.onMediaItemsSet(playlistMediaItems, initialPlaylistTwoQueueItems);
|
||||||
|
when(mockRemoteMediaClient.getMediaQueue()).thenReturn(mockMediaQueue);
|
||||||
|
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(mockMediaStatus);
|
||||||
|
// Mock remote media client state with two items in the queue.
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[] {0, 1});
|
||||||
|
when(mockMediaStatus.getCurrentItemId()).thenReturn(0);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(initialPlaylistTwoQueueItems[0].getMedia());
|
||||||
|
when(mockMediaStatus.getQueueItems()).thenReturn(Arrays.asList(initialPlaylistTwoQueueItems));
|
||||||
|
|
||||||
|
CastTimeline castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(2);
|
||||||
|
assertThat(castTimelineTracker.mediaItemsByContentId).hasSize(2);
|
||||||
|
|
||||||
|
// Mock remote media client state after the first item has been removed.
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[] {1});
|
||||||
|
when(mockMediaStatus.getCurrentItemId()).thenReturn(1);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(initialPlaylistTwoQueueItems[1].getMedia());
|
||||||
|
when(mockMediaStatus.getQueueItems())
|
||||||
|
.thenReturn(ImmutableList.of(initialPlaylistTwoQueueItems[1]));
|
||||||
|
|
||||||
|
castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(1);
|
||||||
|
assertThat(castTimeline.getWindow(/* windowIndex= */ 0, new Window()).mediaItem)
|
||||||
|
.isEqualTo(playlistMediaItems.get(1));
|
||||||
|
// Assert that the removed item has been removed from the content ID map.
|
||||||
|
assertThat(castTimelineTracker.mediaItemsByContentId).hasSize(1);
|
||||||
|
|
||||||
|
// Mock remote media client state for empty queue.
|
||||||
|
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(null);
|
||||||
|
when(mockMediaQueue.getItemIds()).thenReturn(new int[0]);
|
||||||
|
when(mockMediaStatus.getCurrentItemId()).thenReturn(MediaQueueItem.INVALID_ITEM_ID);
|
||||||
|
when(mockMediaStatus.getMediaInfo()).thenReturn(null);
|
||||||
|
when(mockMediaStatus.getQueueItems()).thenReturn(ImmutableList.of());
|
||||||
|
|
||||||
|
castTimeline = castTimelineTracker.getCastTimeline(mockRemoteMediaClient);
|
||||||
|
|
||||||
|
assertThat(castTimeline.getWindowCount()).isEqualTo(0);
|
||||||
|
// Queue is not emptied when remote media client is empty. See [Internal ref: b/128825216].
|
||||||
|
assertThat(castTimelineTracker.mediaItemsByContentId).hasSize(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MediaItem createMediaItem(int uid) {
|
||||||
|
return new MediaItem.Builder()
|
||||||
|
.setUri("http://www.google.com/" + uid)
|
||||||
|
.setMimeType(MimeTypes.AUDIO_MPEG)
|
||||||
|
.setTag(uid)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private MediaQueueItem createMediaQueueItem(MediaItem mediaItem, int uid) {
|
||||||
|
return new MediaQueueItem.Builder(mediaItemConverter.toMediaQueueItem(mediaItem))
|
||||||
|
.setItemId(uid)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
private static RemoteMediaClient mockRemoteMediaClient(
|
private static RemoteMediaClient mockRemoteMediaClient(
|
||||||
int[] itemIds, int currentItemId, long currentDurationMs) {
|
int[] itemIds, int currentItemId, long currentDurationMs) {
|
||||||
RemoteMediaClient remoteMediaClient = Mockito.mock(RemoteMediaClient.class);
|
RemoteMediaClient remoteMediaClient = mock(RemoteMediaClient.class);
|
||||||
MediaStatus status = Mockito.mock(MediaStatus.class);
|
MediaStatus status = mock(MediaStatus.class);
|
||||||
when(status.getQueueItems()).thenReturn(Collections.emptyList());
|
when(status.getQueueItems()).thenReturn(Collections.emptyList());
|
||||||
when(remoteMediaClient.getMediaStatus()).thenReturn(status);
|
when(remoteMediaClient.getMediaStatus()).thenReturn(status);
|
||||||
when(status.getMediaInfo()).thenReturn(getMediaInfo(currentDurationMs));
|
when(status.getMediaInfo()).thenReturn(getMediaInfo(currentDurationMs));
|
||||||
|
|
@ -118,7 +305,7 @@ public class CastTimelineTrackerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MediaQueue mockMediaQueue(int[] itemIds) {
|
private static MediaQueue mockMediaQueue(int[] itemIds) {
|
||||||
MediaQueue mediaQueue = Mockito.mock(MediaQueue.class);
|
MediaQueue mediaQueue = mock(MediaQueue.class);
|
||||||
when(mediaQueue.getItemIds()).thenReturn(itemIds);
|
when(mediaQueue.getItemIds()).thenReturn(itemIds);
|
||||||
return mediaQueue;
|
return mediaQueue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ public class DefaultMediaItemConverterTest {
|
||||||
MediaItem.Builder builder = new MediaItem.Builder();
|
MediaItem.Builder builder = new MediaItem.Builder();
|
||||||
MediaItem item =
|
MediaItem item =
|
||||||
builder
|
builder
|
||||||
|
.setMediaId("fooBar")
|
||||||
.setUri(Uri.parse("http://example.com"))
|
.setUri(Uri.parse("http://example.com"))
|
||||||
.setMediaMetadata(MediaMetadata.EMPTY)
|
.setMediaMetadata(MediaMetadata.EMPTY)
|
||||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
|
|
@ -66,4 +67,45 @@ public class DefaultMediaItemConverterTest {
|
||||||
|
|
||||||
assertThat(reconstructedItem).isEqualTo(item);
|
assertThat(reconstructedItem).isEqualTo(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toMediaQueueItem_nonDefaultMediaId_usedAsContentId() {
|
||||||
|
MediaItem.Builder builder = new MediaItem.Builder();
|
||||||
|
MediaItem item =
|
||||||
|
builder
|
||||||
|
.setMediaId("fooBar")
|
||||||
|
.setUri("http://example.com")
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
DefaultMediaItemConverter converter = new DefaultMediaItemConverter();
|
||||||
|
MediaQueueItem queueItem = converter.toMediaQueueItem(item);
|
||||||
|
|
||||||
|
assertThat(queueItem.getMedia().getContentId()).isEqualTo("fooBar");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void toMediaQueueItem_defaultMediaId_uriAsContentId() {
|
||||||
|
DefaultMediaItemConverter converter = new DefaultMediaItemConverter();
|
||||||
|
MediaItem mediaItem =
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setUri("http://example.com")
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
MediaQueueItem queueItem = converter.toMediaQueueItem(mediaItem);
|
||||||
|
|
||||||
|
assertThat(queueItem.getMedia().getContentId()).isEqualTo("http://example.com");
|
||||||
|
|
||||||
|
MediaItem secondMediaItem =
|
||||||
|
new MediaItem.Builder()
|
||||||
|
.setMediaId(MediaItem.DEFAULT_MEDIA_ID)
|
||||||
|
.setUri("http://example.com")
|
||||||
|
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
MediaQueueItem secondQueueItem = converter.toMediaQueueItem(secondMediaItem);
|
||||||
|
|
||||||
|
assertThat(secondQueueItem.getMedia().getContentId()).isEqualTo("http://example.com");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -234,11 +234,6 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
|
||||||
|
|
||||||
// Player.Listener implementation.
|
// Player.Listener implementation.
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPlaybackStateChanged(@Player.State int playbackState) {
|
|
||||||
notifyStateChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPlayerError(PlaybackException error) {
|
public void onPlayerError(PlaybackException error) {
|
||||||
Callback callback = getCallback();
|
Callback callback = getCallback();
|
||||||
|
|
@ -283,5 +278,13 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
|
||||||
int scaledWidth = Math.round(videoSize.width * videoSize.pixelWidthHeightRatio);
|
int scaledWidth = Math.round(videoSize.width * videoSize.pixelWidthHeightRatio);
|
||||||
getCallback().onVideoSizeChanged(LeanbackPlayerAdapter.this, scaledWidth, videoSize.height);
|
getCallback().onVideoSizeChanged(LeanbackPlayerAdapter.this, scaledWidth, videoSize.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onEvents(Player player, Player.Events events) {
|
||||||
|
if (events.containsAny(
|
||||||
|
Player.EVENT_PLAY_WHEN_READY_CHANGED, Player.EVENT_PLAYBACK_STATE_CHANGED)) {
|
||||||
|
notifyStateChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ dependencies {
|
||||||
testImplementation 'junit:junit:' + junitVersion
|
testImplementation 'junit:junit:' + junitVersion
|
||||||
testImplementation 'com.google.truth:truth:' + truthVersion
|
testImplementation 'com.google.truth:truth:' + truthVersion
|
||||||
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
|
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
|
||||||
|
testImplementation project(modulePrefix + 'library-core')
|
||||||
testImplementation project(modulePrefix + 'testutils')
|
testImplementation project(modulePrefix + 'testutils')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,11 +27,11 @@ public final class ExoPlayerLibraryInfo {
|
||||||
|
|
||||||
/** The version of the library expressed as a string, for example "1.2.3". */
|
/** The version of the library expressed as a string, for example "1.2.3". */
|
||||||
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
|
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
|
||||||
public static final String VERSION = "2.18.0";
|
public static final String VERSION = "2.18.1";
|
||||||
|
|
||||||
/** The version of the library expressed as {@code TAG + "/" + VERSION}. */
|
/** The version of the library expressed as {@code TAG + "/" + VERSION}. */
|
||||||
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
||||||
public static final String VERSION_SLASHY = "ExoPlayerLib/2.18.0";
|
public static final String VERSION_SLASHY = "ExoPlayerLib/2.18.1";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The version of the library expressed as an integer, for example 1002003.
|
* The version of the library expressed as an integer, for example 1002003.
|
||||||
|
|
@ -41,7 +41,7 @@ public final class ExoPlayerLibraryInfo {
|
||||||
* integer version 123045006 (123-045-006).
|
* integer version 123045006 (123-045-006).
|
||||||
*/
|
*/
|
||||||
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
|
||||||
public static final int VERSION_INT = 2_018_000;
|
public static final int VERSION_INT = 2_018_001;
|
||||||
|
|
||||||
/** Whether the library was compiled with {@link Assertions} checks enabled. */
|
/** Whether the library was compiled with {@link Assertions} checks enabled. */
|
||||||
public static final boolean ASSERTIONS_ENABLED = true;
|
public static final boolean ASSERTIONS_ENABLED = true;
|
||||||
|
|
|
||||||
|
|
@ -372,6 +372,7 @@ public interface Player {
|
||||||
COMMAND_GET_TIMELINE,
|
COMMAND_GET_TIMELINE,
|
||||||
COMMAND_GET_MEDIA_ITEMS_METADATA,
|
COMMAND_GET_MEDIA_ITEMS_METADATA,
|
||||||
COMMAND_SET_MEDIA_ITEMS_METADATA,
|
COMMAND_SET_MEDIA_ITEMS_METADATA,
|
||||||
|
COMMAND_SET_MEDIA_ITEM,
|
||||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||||
COMMAND_GET_AUDIO_ATTRIBUTES,
|
COMMAND_GET_AUDIO_ATTRIBUTES,
|
||||||
COMMAND_GET_VOLUME,
|
COMMAND_GET_VOLUME,
|
||||||
|
|
@ -383,7 +384,6 @@ public interface Player {
|
||||||
COMMAND_GET_TEXT,
|
COMMAND_GET_TEXT,
|
||||||
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
|
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
|
||||||
COMMAND_GET_TRACKS,
|
COMMAND_GET_TRACKS,
|
||||||
COMMAND_SET_MEDIA_ITEM,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private final FlagSet.Builder flagsBuilder;
|
private final FlagSet.Builder flagsBuilder;
|
||||||
|
|
@ -1422,6 +1422,7 @@ public interface Player {
|
||||||
COMMAND_GET_TIMELINE,
|
COMMAND_GET_TIMELINE,
|
||||||
COMMAND_GET_MEDIA_ITEMS_METADATA,
|
COMMAND_GET_MEDIA_ITEMS_METADATA,
|
||||||
COMMAND_SET_MEDIA_ITEMS_METADATA,
|
COMMAND_SET_MEDIA_ITEMS_METADATA,
|
||||||
|
COMMAND_SET_MEDIA_ITEM,
|
||||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||||
COMMAND_GET_AUDIO_ATTRIBUTES,
|
COMMAND_GET_AUDIO_ATTRIBUTES,
|
||||||
COMMAND_GET_VOLUME,
|
COMMAND_GET_VOLUME,
|
||||||
|
|
@ -1433,7 +1434,6 @@ public interface Player {
|
||||||
COMMAND_GET_TEXT,
|
COMMAND_GET_TEXT,
|
||||||
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
|
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
|
||||||
COMMAND_GET_TRACKS,
|
COMMAND_GET_TRACKS,
|
||||||
COMMAND_SET_MEDIA_ITEM,
|
|
||||||
})
|
})
|
||||||
@interface Command {}
|
@interface Command {}
|
||||||
/** Command to start, pause or resume playback. */
|
/** Command to start, pause or resume playback. */
|
||||||
|
|
@ -1490,6 +1490,8 @@ public interface Player {
|
||||||
int COMMAND_GET_MEDIA_ITEMS_METADATA = 18;
|
int COMMAND_GET_MEDIA_ITEMS_METADATA = 18;
|
||||||
/** Command to set the {@link MediaItem MediaItems} metadata. */
|
/** Command to set the {@link MediaItem MediaItems} metadata. */
|
||||||
int COMMAND_SET_MEDIA_ITEMS_METADATA = 19;
|
int COMMAND_SET_MEDIA_ITEMS_METADATA = 19;
|
||||||
|
/** Command to set a {@link MediaItem MediaItem}. */
|
||||||
|
int COMMAND_SET_MEDIA_ITEM = 31;
|
||||||
/** Command to change the {@link MediaItem MediaItems} in the playlist. */
|
/** Command to change the {@link MediaItem MediaItems} in the playlist. */
|
||||||
int COMMAND_CHANGE_MEDIA_ITEMS = 20;
|
int COMMAND_CHANGE_MEDIA_ITEMS = 20;
|
||||||
/** Command to get the player current {@link AudioAttributes}. */
|
/** Command to get the player current {@link AudioAttributes}. */
|
||||||
|
|
@ -1512,8 +1514,6 @@ public interface Player {
|
||||||
int COMMAND_SET_TRACK_SELECTION_PARAMETERS = 29;
|
int COMMAND_SET_TRACK_SELECTION_PARAMETERS = 29;
|
||||||
/** Command to get details of the current track selection. */
|
/** Command to get details of the current track selection. */
|
||||||
int COMMAND_GET_TRACKS = 30;
|
int COMMAND_GET_TRACKS = 30;
|
||||||
/** Command to set a {@link MediaItem MediaItem}. */
|
|
||||||
int COMMAND_SET_MEDIA_ITEM = 31;
|
|
||||||
|
|
||||||
/** Represents an invalid {@link Command}. */
|
/** Represents an invalid {@link Command}. */
|
||||||
int COMMAND_INVALID = -1;
|
int COMMAND_INVALID = -1;
|
||||||
|
|
|
||||||
|
|
@ -1340,6 +1340,27 @@ public abstract class Timeline implements Bundleable {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check shuffled order
|
||||||
|
int windowIndex = getFirstWindowIndex(/* shuffleModeEnabled= */ true);
|
||||||
|
if (windowIndex != other.getFirstWindowIndex(/* shuffleModeEnabled= */ true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int lastWindowIndex = getLastWindowIndex(/* shuffleModeEnabled= */ true);
|
||||||
|
if (lastWindowIndex != other.getLastWindowIndex(/* shuffleModeEnabled= */ true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while (windowIndex != lastWindowIndex) {
|
||||||
|
int nextWindowIndex =
|
||||||
|
getNextWindowIndex(windowIndex, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ true);
|
||||||
|
if (nextWindowIndex
|
||||||
|
!= other.getNextWindowIndex(
|
||||||
|
windowIndex, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ true)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
windowIndex = nextWindowIndex;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1356,6 +1377,13 @@ public abstract class Timeline implements Bundleable {
|
||||||
for (int i = 0; i < getPeriodCount(); i++) {
|
for (int i = 0; i < getPeriodCount(); i++) {
|
||||||
result = 31 * result + getPeriod(i, period, /* setIds= */ true).hashCode();
|
result = 31 * result + getPeriod(i, period, /* setIds= */ true).hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (int windowIndex = getFirstWindowIndex(true);
|
||||||
|
windowIndex != C.INDEX_UNSET;
|
||||||
|
windowIndex = getNextWindowIndex(windowIndex, Player.REPEAT_MODE_OFF, true)) {
|
||||||
|
result = 31 * result + windowIndex;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.drm;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import androidx.annotation.CheckResult;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
|
import com.google.android.exoplayer2.drm.DrmInitData.SchemeData;
|
||||||
|
|
@ -156,6 +157,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
|
||||||
* @param schemeType A protection scheme type. May be null.
|
* @param schemeType A protection scheme type. May be null.
|
||||||
* @return A copy with the specified protection scheme type.
|
* @return A copy with the specified protection scheme type.
|
||||||
*/
|
*/
|
||||||
|
@CheckResult
|
||||||
public DrmInitData copyWithSchemeType(@Nullable String schemeType) {
|
public DrmInitData copyWithSchemeType(@Nullable String schemeType) {
|
||||||
if (Util.areEqual(this.schemeType, schemeType)) {
|
if (Util.areEqual(this.schemeType, schemeType)) {
|
||||||
return this;
|
return this;
|
||||||
|
|
@ -332,6 +334,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
|
||||||
* @param data The data to include in the copy.
|
* @param data The data to include in the copy.
|
||||||
* @return The new instance.
|
* @return The new instance.
|
||||||
*/
|
*/
|
||||||
|
@CheckResult
|
||||||
public SchemeData copyWithData(@Nullable byte[] data) {
|
public SchemeData copyWithData(@Nullable byte[] data) {
|
||||||
return new SchemeData(uuid, licenseServerUrl, mimeType, data);
|
return new SchemeData(uuid, licenseServerUrl, mimeType, data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.util;
|
||||||
import static java.lang.annotation.ElementType.TYPE_USE;
|
import static java.lang.annotation.ElementType.TYPE_USE;
|
||||||
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import androidx.annotation.GuardedBy;
|
||||||
import androidx.annotation.IntDef;
|
import androidx.annotation.IntDef;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.Size;
|
import androidx.annotation.Size;
|
||||||
|
|
@ -28,7 +29,10 @@ import java.lang.annotation.Target;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import org.checkerframework.dataflow.qual.Pure;
|
import org.checkerframework.dataflow.qual.Pure;
|
||||||
|
|
||||||
/** Wrapper around {@link android.util.Log} which allows to set the log level. */
|
/**
|
||||||
|
* Wrapper around {@link android.util.Log} which allows to set the log level and to specify a custom
|
||||||
|
* log output.
|
||||||
|
*/
|
||||||
public final class Log {
|
public final class Log {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -51,16 +55,90 @@ public final class Log {
|
||||||
/** Log level to disable all logging. */
|
/** Log level to disable all logging. */
|
||||||
public static final int LOG_LEVEL_OFF = Integer.MAX_VALUE;
|
public static final int LOG_LEVEL_OFF = Integer.MAX_VALUE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface for a logger that can output messages with a tag.
|
||||||
|
*
|
||||||
|
* <p>Use {@link #DEFAULT} to output to {@link android.util.Log}.
|
||||||
|
*/
|
||||||
|
public interface Logger {
|
||||||
|
|
||||||
|
/** The default instance logging to {@link android.util.Log}. */
|
||||||
|
Logger DEFAULT =
|
||||||
|
new Logger() {
|
||||||
|
@Override
|
||||||
|
public void d(String tag, String message) {
|
||||||
|
android.util.Log.d(tag, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void i(String tag, String message) {
|
||||||
|
android.util.Log.i(tag, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void w(String tag, String message) {
|
||||||
|
android.util.Log.w(tag, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void e(String tag, String message) {
|
||||||
|
android.util.Log.e(tag, message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a debug-level message.
|
||||||
|
*
|
||||||
|
* @param tag The tag of the message.
|
||||||
|
* @param message The message.
|
||||||
|
*/
|
||||||
|
void d(String tag, String message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs an information-level message.
|
||||||
|
*
|
||||||
|
* @param tag The tag of the message.
|
||||||
|
* @param message The message.
|
||||||
|
*/
|
||||||
|
void i(String tag, String message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs a warning-level message.
|
||||||
|
*
|
||||||
|
* @param tag The tag of the message.
|
||||||
|
* @param message The message.
|
||||||
|
*/
|
||||||
|
void w(String tag, String message);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Logs an error-level message.
|
||||||
|
*
|
||||||
|
* @param tag The tag of the message.
|
||||||
|
* @param message The message.
|
||||||
|
*/
|
||||||
|
void e(String tag, String message);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Object lock = new Object();
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
private static int logLevel = LOG_LEVEL_ALL;
|
private static int logLevel = LOG_LEVEL_ALL;
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
private static boolean logStackTraces = true;
|
private static boolean logStackTraces = true;
|
||||||
|
|
||||||
|
@GuardedBy("lock")
|
||||||
|
private static Logger logger = Logger.DEFAULT;
|
||||||
|
|
||||||
private Log() {}
|
private Log() {}
|
||||||
|
|
||||||
/** Returns current {@link LogLevel} for ExoPlayer logcat logging. */
|
/** Returns current {@link LogLevel} for ExoPlayer logcat logging. */
|
||||||
@Pure
|
@Pure
|
||||||
public static @LogLevel int getLogLevel() {
|
public static @LogLevel int getLogLevel() {
|
||||||
|
synchronized (lock) {
|
||||||
return logLevel;
|
return logLevel;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the {@link LogLevel} for ExoPlayer logcat logging.
|
* Sets the {@link LogLevel} for ExoPlayer logcat logging.
|
||||||
|
|
@ -68,8 +146,10 @@ public final class Log {
|
||||||
* @param logLevel The new {@link LogLevel}.
|
* @param logLevel The new {@link LogLevel}.
|
||||||
*/
|
*/
|
||||||
public static void setLogLevel(@LogLevel int logLevel) {
|
public static void setLogLevel(@LogLevel int logLevel) {
|
||||||
|
synchronized (lock) {
|
||||||
Log.logLevel = logLevel;
|
Log.logLevel = logLevel;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether stack traces of {@link Throwable}s will be logged to logcat. Stack trace logging
|
* Sets whether stack traces of {@link Throwable}s will be logged to logcat. Stack trace logging
|
||||||
|
|
@ -78,16 +158,31 @@ public final class Log {
|
||||||
* @param logStackTraces Whether stack traces will be logged.
|
* @param logStackTraces Whether stack traces will be logged.
|
||||||
*/
|
*/
|
||||||
public static void setLogStackTraces(boolean logStackTraces) {
|
public static void setLogStackTraces(boolean logStackTraces) {
|
||||||
|
synchronized (lock) {
|
||||||
Log.logStackTraces = logStackTraces;
|
Log.logStackTraces = logStackTraces;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a custom {@link Logger} as the output.
|
||||||
|
*
|
||||||
|
* @param logger The {@link Logger}.
|
||||||
|
*/
|
||||||
|
public static void setLogger(Logger logger) {
|
||||||
|
synchronized (lock) {
|
||||||
|
Log.logger = logger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see android.util.Log#d(String, String)
|
* @see android.util.Log#d(String, String)
|
||||||
*/
|
*/
|
||||||
@Pure
|
@Pure
|
||||||
public static void d(@Size(max = 23) String tag, String message) {
|
public static void d(@Size(max = 23) String tag, String message) {
|
||||||
|
synchronized (lock) {
|
||||||
if (logLevel == LOG_LEVEL_ALL) {
|
if (logLevel == LOG_LEVEL_ALL) {
|
||||||
android.util.Log.d(tag, message);
|
logger.d(tag, message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,8 +199,10 @@ public final class Log {
|
||||||
*/
|
*/
|
||||||
@Pure
|
@Pure
|
||||||
public static void i(@Size(max = 23) String tag, String message) {
|
public static void i(@Size(max = 23) String tag, String message) {
|
||||||
|
synchronized (lock) {
|
||||||
if (logLevel <= LOG_LEVEL_INFO) {
|
if (logLevel <= LOG_LEVEL_INFO) {
|
||||||
android.util.Log.i(tag, message);
|
logger.i(tag, message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -122,8 +219,10 @@ public final class Log {
|
||||||
*/
|
*/
|
||||||
@Pure
|
@Pure
|
||||||
public static void w(@Size(max = 23) String tag, String message) {
|
public static void w(@Size(max = 23) String tag, String message) {
|
||||||
|
synchronized (lock) {
|
||||||
if (logLevel <= LOG_LEVEL_WARNING) {
|
if (logLevel <= LOG_LEVEL_WARNING) {
|
||||||
android.util.Log.w(tag, message);
|
logger.w(tag, message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -140,8 +239,10 @@ public final class Log {
|
||||||
*/
|
*/
|
||||||
@Pure
|
@Pure
|
||||||
public static void e(@Size(max = 23) String tag, String message) {
|
public static void e(@Size(max = 23) String tag, String message) {
|
||||||
|
synchronized (lock) {
|
||||||
if (logLevel <= LOG_LEVEL_ERROR) {
|
if (logLevel <= LOG_LEVEL_ERROR) {
|
||||||
android.util.Log.e(tag, message);
|
logger.e(tag, message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,11 +268,13 @@ public final class Log {
|
||||||
@Nullable
|
@Nullable
|
||||||
@Pure
|
@Pure
|
||||||
public static String getThrowableString(@Nullable Throwable throwable) {
|
public static String getThrowableString(@Nullable Throwable throwable) {
|
||||||
|
synchronized (lock) {
|
||||||
if (throwable == null) {
|
if (throwable == null) {
|
||||||
return null;
|
return null;
|
||||||
} else if (isCausedByUnknownHostException(throwable)) {
|
} else if (isCausedByUnknownHostException(throwable)) {
|
||||||
// UnknownHostException implies the device doesn't have network connectivity.
|
// UnknownHostException implies the device doesn't have network connectivity.
|
||||||
// UnknownHostException.getMessage() may return a string that's more verbose than desired for
|
// UnknownHostException.getMessage() may return a string that's more verbose than desired
|
||||||
|
// for
|
||||||
// logging an expected failure mode. Conversely, android.util.Log.getStackTraceString has
|
// logging an expected failure mode. Conversely, android.util.Log.getStackTraceString has
|
||||||
// special handling to return the empty string, which can result in logging that doesn't
|
// special handling to return the empty string, which can result in logging that doesn't
|
||||||
// indicate the failure mode at all. Hence we special case this exception to always return a
|
// indicate the failure mode at all. Hence we special case this exception to always return a
|
||||||
|
|
@ -183,6 +286,7 @@ public final class Log {
|
||||||
return android.util.Log.getStackTraceString(throwable).trim().replace("\t", " ");
|
return android.util.Log.getStackTraceString(throwable).trim().replace("\t", " ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Pure
|
@Pure
|
||||||
private static String appendThrowableString(String message, @Nullable Throwable throwable) {
|
private static String appendThrowableString(String message, @Nullable Throwable throwable) {
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.Parcel;
|
import android.os.Parcel;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
|
import android.provider.MediaStore;
|
||||||
import android.security.NetworkSecurityPolicy;
|
import android.security.NetworkSecurityPolicy;
|
||||||
import android.telephony.TelephonyManager;
|
import android.telephony.TelephonyManager;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
@ -195,7 +196,7 @@ public final class Util {
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static ComponentName startForegroundService(Context context, Intent intent) {
|
public static ComponentName startForegroundService(Context context, Intent intent) {
|
||||||
if (Util.SDK_INT >= 26) {
|
if (SDK_INT >= 26) {
|
||||||
return context.startForegroundService(intent);
|
return context.startForegroundService(intent);
|
||||||
} else {
|
} else {
|
||||||
return context.startService(intent);
|
return context.startService(intent);
|
||||||
|
|
@ -211,12 +212,12 @@ public final class Util {
|
||||||
* @return Whether a permission request was made.
|
* @return Whether a permission request was made.
|
||||||
*/
|
*/
|
||||||
public static boolean maybeRequestReadExternalStoragePermission(Activity activity, Uri... uris) {
|
public static boolean maybeRequestReadExternalStoragePermission(Activity activity, Uri... uris) {
|
||||||
if (Util.SDK_INT < 23) {
|
if (SDK_INT < 23) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (Uri uri : uris) {
|
for (Uri uri : uris) {
|
||||||
if (isLocalFileUri(uri)) {
|
if (maybeRequestReadExternalStoragePermission(activity, uri)) {
|
||||||
return requestExternalStoragePermission(activity);
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -234,25 +235,46 @@ public final class Util {
|
||||||
*/
|
*/
|
||||||
public static boolean maybeRequestReadExternalStoragePermission(
|
public static boolean maybeRequestReadExternalStoragePermission(
|
||||||
Activity activity, MediaItem... mediaItems) {
|
Activity activity, MediaItem... mediaItems) {
|
||||||
if (Util.SDK_INT < 23) {
|
if (SDK_INT < 23) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (MediaItem mediaItem : mediaItems) {
|
for (MediaItem mediaItem : mediaItems) {
|
||||||
if (mediaItem.localConfiguration == null) {
|
if (mediaItem.localConfiguration == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (isLocalFileUri(mediaItem.localConfiguration.uri)) {
|
if (maybeRequestReadExternalStoragePermission(activity, mediaItem.localConfiguration.uri)) {
|
||||||
return requestExternalStoragePermission(activity);
|
return true;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < mediaItem.localConfiguration.subtitleConfigurations.size(); i++) {
|
List<MediaItem.SubtitleConfiguration> subtitleConfigs =
|
||||||
if (isLocalFileUri(mediaItem.localConfiguration.subtitleConfigurations.get(i).uri)) {
|
mediaItem.localConfiguration.subtitleConfigurations;
|
||||||
return requestExternalStoragePermission(activity);
|
for (int i = 0; i < subtitleConfigs.size(); i++) {
|
||||||
|
if (maybeRequestReadExternalStoragePermission(activity, subtitleConfigs.get(i).uri)) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean maybeRequestReadExternalStoragePermission(Activity activity, Uri uri) {
|
||||||
|
return SDK_INT >= 23
|
||||||
|
&& (isLocalFileUri(uri) || isMediaStoreExternalContentUri(uri))
|
||||||
|
&& requestExternalStoragePermission(activity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMediaStoreExternalContentUri(Uri uri) {
|
||||||
|
if (!"content".equals(uri.getScheme()) || !MediaStore.AUTHORITY.equals(uri.getAuthority())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
List<String> pathSegments = uri.getPathSegments();
|
||||||
|
if (pathSegments.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String firstPathSegment = pathSegments.get(0);
|
||||||
|
return MediaStore.VOLUME_EXTERNAL.equals(firstPathSegment)
|
||||||
|
|| MediaStore.VOLUME_EXTERNAL_PRIMARY.equals(firstPathSegment);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether it may be possible to load the URIs of the given media items based on the
|
* Returns whether it may be possible to load the URIs of the given media items based on the
|
||||||
* network security policy's cleartext traffic permissions.
|
* network security policy's cleartext traffic permissions.
|
||||||
|
|
@ -261,7 +283,7 @@ public final class Util {
|
||||||
* @return Whether it may be possible to load the URIs of the given media items.
|
* @return Whether it may be possible to load the URIs of the given media items.
|
||||||
*/
|
*/
|
||||||
public static boolean checkCleartextTrafficPermitted(MediaItem... mediaItems) {
|
public static boolean checkCleartextTrafficPermitted(MediaItem... mediaItems) {
|
||||||
if (Util.SDK_INT < 24) {
|
if (SDK_INT < 24) {
|
||||||
// We assume cleartext traffic is permitted.
|
// We assume cleartext traffic is permitted.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -622,7 +644,7 @@ public final class Util {
|
||||||
normalizedTag = language;
|
normalizedTag = language;
|
||||||
}
|
}
|
||||||
normalizedTag = Ascii.toLowerCase(normalizedTag);
|
normalizedTag = Ascii.toLowerCase(normalizedTag);
|
||||||
String mainLanguage = Util.splitAtFirst(normalizedTag, "-")[0];
|
String mainLanguage = splitAtFirst(normalizedTag, "-")[0];
|
||||||
if (languageTagReplacementMap == null) {
|
if (languageTagReplacementMap == null) {
|
||||||
languageTagReplacementMap = createIsoLanguageReplacementMap();
|
languageTagReplacementMap = createIsoLanguageReplacementMap();
|
||||||
}
|
}
|
||||||
|
|
@ -1633,9 +1655,9 @@ public final class Util {
|
||||||
case 7:
|
case 7:
|
||||||
return AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
|
return AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
|
||||||
case 8:
|
case 8:
|
||||||
if (Util.SDK_INT >= 23) {
|
if (SDK_INT >= 23) {
|
||||||
return AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
|
return AudioFormat.CHANNEL_OUT_7POINT1_SURROUND;
|
||||||
} else if (Util.SDK_INT >= 21) {
|
} else if (SDK_INT >= 21) {
|
||||||
// Equal to AudioFormat.CHANNEL_OUT_7POINT1_SURROUND, which is hidden before Android M.
|
// Equal to AudioFormat.CHANNEL_OUT_7POINT1_SURROUND, which is hidden before Android M.
|
||||||
return AudioFormat.CHANNEL_OUT_5POINT1
|
return AudioFormat.CHANNEL_OUT_5POINT1
|
||||||
| AudioFormat.CHANNEL_OUT_SIDE_LEFT
|
| AudioFormat.CHANNEL_OUT_SIDE_LEFT
|
||||||
|
|
@ -1918,7 +1940,7 @@ public final class Util {
|
||||||
public static @ContentType int inferContentTypeForUriAndMimeType(
|
public static @ContentType int inferContentTypeForUriAndMimeType(
|
||||||
Uri uri, @Nullable String mimeType) {
|
Uri uri, @Nullable String mimeType) {
|
||||||
if (mimeType == null) {
|
if (mimeType == null) {
|
||||||
return Util.inferContentType(uri);
|
return inferContentType(uri);
|
||||||
}
|
}
|
||||||
switch (mimeType) {
|
switch (mimeType) {
|
||||||
case MimeTypes.APPLICATION_MPD:
|
case MimeTypes.APPLICATION_MPD:
|
||||||
|
|
@ -2242,7 +2264,7 @@ public final class Util {
|
||||||
|
|
||||||
/** Returns the default {@link Locale.Category#DISPLAY DISPLAY} {@link Locale}. */
|
/** Returns the default {@link Locale.Category#DISPLAY DISPLAY} {@link Locale}. */
|
||||||
public static Locale getDefaultDisplayLocale() {
|
public static Locale getDefaultDisplayLocale() {
|
||||||
return Util.SDK_INT >= 24 ? Locale.getDefault(Locale.Category.DISPLAY) : Locale.getDefault();
|
return SDK_INT >= 24 ? Locale.getDefault(Locale.Category.DISPLAY) : Locale.getDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2314,7 +2336,7 @@ public final class Util {
|
||||||
* @return Whether the app is running on an automotive device.
|
* @return Whether the app is running on an automotive device.
|
||||||
*/
|
*/
|
||||||
public static boolean isAutomotive(Context context) {
|
public static boolean isAutomotive(Context context) {
|
||||||
return Util.SDK_INT >= 23
|
return SDK_INT >= 23
|
||||||
&& context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
|
&& context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2332,7 +2354,7 @@ public final class Util {
|
||||||
*/
|
*/
|
||||||
public static Point getCurrentDisplayModeSize(Context context) {
|
public static Point getCurrentDisplayModeSize(Context context) {
|
||||||
@Nullable Display defaultDisplay = null;
|
@Nullable Display defaultDisplay = null;
|
||||||
if (Util.SDK_INT >= 17) {
|
if (SDK_INT >= 17) {
|
||||||
@Nullable
|
@Nullable
|
||||||
DisplayManager displayManager =
|
DisplayManager displayManager =
|
||||||
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
|
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
|
||||||
|
|
@ -2380,7 +2402,7 @@ public final class Util {
|
||||||
// vendor.display-size instead.
|
// vendor.display-size instead.
|
||||||
@Nullable
|
@Nullable
|
||||||
String displaySize =
|
String displaySize =
|
||||||
Util.SDK_INT < 28
|
SDK_INT < 28
|
||||||
? getSystemProperty("sys.display-size")
|
? getSystemProperty("sys.display-size")
|
||||||
: getSystemProperty("vendor.display-size");
|
: getSystemProperty("vendor.display-size");
|
||||||
// If we managed to read the display size, attempt to parse it.
|
// If we managed to read the display size, attempt to parse it.
|
||||||
|
|
@ -2401,17 +2423,17 @@ public final class Util {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sony Android TVs advertise support for 4k output via a system feature.
|
// Sony Android TVs advertise support for 4k output via a system feature.
|
||||||
if ("Sony".equals(Util.MANUFACTURER)
|
if ("Sony".equals(MANUFACTURER)
|
||||||
&& Util.MODEL.startsWith("BRAVIA")
|
&& MODEL.startsWith("BRAVIA")
|
||||||
&& context.getPackageManager().hasSystemFeature("com.sony.dtv.hardware.panel.qfhd")) {
|
&& context.getPackageManager().hasSystemFeature("com.sony.dtv.hardware.panel.qfhd")) {
|
||||||
return new Point(3840, 2160);
|
return new Point(3840, 2160);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Point displaySize = new Point();
|
Point displaySize = new Point();
|
||||||
if (Util.SDK_INT >= 23) {
|
if (SDK_INT >= 23) {
|
||||||
getDisplaySizeV23(display, displaySize);
|
getDisplaySizeV23(display, displaySize);
|
||||||
} else if (Util.SDK_INT >= 17) {
|
} else if (SDK_INT >= 17) {
|
||||||
getDisplaySizeV17(display, displaySize);
|
getDisplaySizeV17(display, displaySize);
|
||||||
} else {
|
} else {
|
||||||
getDisplaySizeV16(display, displaySize);
|
getDisplaySizeV16(display, displaySize);
|
||||||
|
|
@ -2629,7 +2651,7 @@ public final class Util {
|
||||||
|
|
||||||
@RequiresApi(24)
|
@RequiresApi(24)
|
||||||
private static String[] getSystemLocalesV24(Configuration config) {
|
private static String[] getSystemLocalesV24(Configuration config) {
|
||||||
return Util.split(config.getLocales().toLanguageTags(), ",");
|
return split(config.getLocales().toLanguageTags(), ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(21)
|
@RequiresApi(21)
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.MediaItem.LiveConfiguration;
|
import com.google.android.exoplayer2.MediaItem.LiveConfiguration;
|
||||||
|
import com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder;
|
||||||
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
import com.google.android.exoplayer2.testutil.FakeTimeline;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||||
|
|
@ -65,6 +66,50 @@ public class TimelineTest {
|
||||||
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 0);
|
TimelineAsserts.assertNextWindowIndices(timeline, Player.REPEAT_MODE_ALL, false, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void timelineEquals() {
|
||||||
|
ImmutableList<TimelineWindowDefinition> timelineWindowDefinitions =
|
||||||
|
ImmutableList.of(
|
||||||
|
new TimelineWindowDefinition(/* periodCount= */ 1, /* id= */ 111),
|
||||||
|
new TimelineWindowDefinition(/* periodCount= */ 2, /* id= */ 222),
|
||||||
|
new TimelineWindowDefinition(/* periodCount= */ 3, /* id= */ 333));
|
||||||
|
Timeline timeline1 =
|
||||||
|
new FakeTimeline(timelineWindowDefinitions.toArray(new TimelineWindowDefinition[0]));
|
||||||
|
Timeline timeline2 =
|
||||||
|
new FakeTimeline(timelineWindowDefinitions.toArray(new TimelineWindowDefinition[0]));
|
||||||
|
|
||||||
|
assertThat(timeline1).isEqualTo(timeline2);
|
||||||
|
assertThat(timeline1.hashCode()).isEqualTo(timeline2.hashCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void timelineEquals_includesShuffleOrder() {
|
||||||
|
ImmutableList<TimelineWindowDefinition> timelineWindowDefinitions =
|
||||||
|
ImmutableList.of(
|
||||||
|
new TimelineWindowDefinition(/* periodCount= */ 1, /* id= */ 111),
|
||||||
|
new TimelineWindowDefinition(/* periodCount= */ 2, /* id= */ 222),
|
||||||
|
new TimelineWindowDefinition(/* periodCount= */ 3, /* id= */ 333));
|
||||||
|
Timeline timeline =
|
||||||
|
new FakeTimeline(
|
||||||
|
new Object[0],
|
||||||
|
new DefaultShuffleOrder(timelineWindowDefinitions.size(), /* randomSeed= */ 5),
|
||||||
|
timelineWindowDefinitions.toArray(new TimelineWindowDefinition[0]));
|
||||||
|
Timeline timelineWithEquivalentShuffleOrder =
|
||||||
|
new FakeTimeline(
|
||||||
|
new Object[0],
|
||||||
|
new DefaultShuffleOrder(timelineWindowDefinitions.size(), /* randomSeed= */ 5),
|
||||||
|
timelineWindowDefinitions.toArray(new TimelineWindowDefinition[0]));
|
||||||
|
Timeline timelineWithDifferentShuffleOrder =
|
||||||
|
new FakeTimeline(
|
||||||
|
new Object[0],
|
||||||
|
new DefaultShuffleOrder(timelineWindowDefinitions.size(), /* randomSeed= */ 3),
|
||||||
|
timelineWindowDefinitions.toArray(new TimelineWindowDefinition[0]));
|
||||||
|
|
||||||
|
assertThat(timeline).isEqualTo(timelineWithEquivalentShuffleOrder);
|
||||||
|
assertThat(timeline.hashCode()).isEqualTo(timelineWithEquivalentShuffleOrder.hashCode());
|
||||||
|
assertThat(timeline).isNotEqualTo(timelineWithDifferentShuffleOrder);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void windowEquals() {
|
public void windowEquals() {
|
||||||
MediaItem mediaItem = new MediaItem.Builder().setUri("uri").setTag(new Object()).build();
|
MediaItem mediaItem = new MediaItem.Builder().setUri("uri").setTag(new Object()).build();
|
||||||
|
|
|
||||||
|
|
@ -283,6 +283,7 @@ import java.util.concurrent.TimeoutException;
|
||||||
COMMAND_GET_TIMELINE,
|
COMMAND_GET_TIMELINE,
|
||||||
COMMAND_GET_MEDIA_ITEMS_METADATA,
|
COMMAND_GET_MEDIA_ITEMS_METADATA,
|
||||||
COMMAND_SET_MEDIA_ITEMS_METADATA,
|
COMMAND_SET_MEDIA_ITEMS_METADATA,
|
||||||
|
COMMAND_SET_MEDIA_ITEM,
|
||||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||||
COMMAND_GET_TRACKS,
|
COMMAND_GET_TRACKS,
|
||||||
COMMAND_GET_AUDIO_ATTRIBUTES,
|
COMMAND_GET_AUDIO_ATTRIBUTES,
|
||||||
|
|
@ -292,8 +293,7 @@ import java.util.concurrent.TimeoutException;
|
||||||
COMMAND_SET_DEVICE_VOLUME,
|
COMMAND_SET_DEVICE_VOLUME,
|
||||||
COMMAND_ADJUST_DEVICE_VOLUME,
|
COMMAND_ADJUST_DEVICE_VOLUME,
|
||||||
COMMAND_SET_VIDEO_SURFACE,
|
COMMAND_SET_VIDEO_SURFACE,
|
||||||
COMMAND_GET_TEXT,
|
COMMAND_GET_TEXT)
|
||||||
COMMAND_SET_MEDIA_ITEM)
|
|
||||||
.addIf(
|
.addIf(
|
||||||
COMMAND_SET_TRACK_SELECTION_PARAMETERS, trackSelector.isSetParametersSupported())
|
COMMAND_SET_TRACK_SELECTION_PARAMETERS, trackSelector.isSetParametersSupported())
|
||||||
.build();
|
.build();
|
||||||
|
|
@ -422,6 +422,9 @@ import java.util.concurrent.TimeoutException;
|
||||||
public void experimentalSetOffloadSchedulingEnabled(boolean offloadSchedulingEnabled) {
|
public void experimentalSetOffloadSchedulingEnabled(boolean offloadSchedulingEnabled) {
|
||||||
verifyApplicationThread();
|
verifyApplicationThread();
|
||||||
internalPlayer.experimentalSetOffloadSchedulingEnabled(offloadSchedulingEnabled);
|
internalPlayer.experimentalSetOffloadSchedulingEnabled(offloadSchedulingEnabled);
|
||||||
|
for (AudioOffloadListener listener : audioOffloadListeners) {
|
||||||
|
listener.onExperimentalOffloadSchedulingEnabledChanged(offloadSchedulingEnabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -696,6 +699,7 @@ import java.util.concurrent.TimeoutException;
|
||||||
@Override
|
@Override
|
||||||
public void setShuffleOrder(ShuffleOrder shuffleOrder) {
|
public void setShuffleOrder(ShuffleOrder shuffleOrder) {
|
||||||
verifyApplicationThread();
|
verifyApplicationThread();
|
||||||
|
this.shuffleOrder = shuffleOrder;
|
||||||
Timeline timeline = createMaskingTimeline();
|
Timeline timeline = createMaskingTimeline();
|
||||||
PlaybackInfo newPlaybackInfo =
|
PlaybackInfo newPlaybackInfo =
|
||||||
maskTimelineAndPosition(
|
maskTimelineAndPosition(
|
||||||
|
|
@ -704,7 +708,6 @@ import java.util.concurrent.TimeoutException;
|
||||||
maskWindowPositionMsOrGetPeriodPositionUs(
|
maskWindowPositionMsOrGetPeriodPositionUs(
|
||||||
timeline, getCurrentMediaItemIndex(), getCurrentPosition()));
|
timeline, getCurrentMediaItemIndex(), getCurrentPosition()));
|
||||||
pendingOperationAcks++;
|
pendingOperationAcks++;
|
||||||
this.shuffleOrder = shuffleOrder;
|
|
||||||
internalPlayer.setShuffleOrder(shuffleOrder);
|
internalPlayer.setShuffleOrder(shuffleOrder);
|
||||||
updatePlaybackInfo(
|
updatePlaybackInfo(
|
||||||
newPlaybackInfo,
|
newPlaybackInfo,
|
||||||
|
|
@ -1951,12 +1954,6 @@ import java.util.concurrent.TimeoutException;
|
||||||
updateAvailableCommands();
|
updateAvailableCommands();
|
||||||
listeners.flushEvents();
|
listeners.flushEvents();
|
||||||
|
|
||||||
if (previousPlaybackInfo.offloadSchedulingEnabled != newPlaybackInfo.offloadSchedulingEnabled) {
|
|
||||||
for (AudioOffloadListener listener : audioOffloadListeners) {
|
|
||||||
listener.onExperimentalOffloadSchedulingEnabledChanged(
|
|
||||||
newPlaybackInfo.offloadSchedulingEnabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (previousPlaybackInfo.sleepingForOffload != newPlaybackInfo.sleepingForOffload) {
|
if (previousPlaybackInfo.sleepingForOffload != newPlaybackInfo.sleepingForOffload) {
|
||||||
for (AudioOffloadListener listener : audioOffloadListeners) {
|
for (AudioOffloadListener listener : audioOffloadListeners) {
|
||||||
listener.onExperimentalSleepingForOffloadChanged(newPlaybackInfo.sleepingForOffload);
|
listener.onExperimentalSleepingForOffloadChanged(newPlaybackInfo.sleepingForOffload);
|
||||||
|
|
|
||||||
|
|
@ -809,10 +809,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.offloadSchedulingEnabled = offloadSchedulingEnabled;
|
this.offloadSchedulingEnabled = offloadSchedulingEnabled;
|
||||||
@Player.State int state = playbackInfo.playbackState;
|
if (!offloadSchedulingEnabled && playbackInfo.sleepingForOffload) {
|
||||||
if (offloadSchedulingEnabled || state == Player.STATE_ENDED || state == Player.STATE_IDLE) {
|
// We need to wake the player up if offload scheduling is disabled and we are sleeping.
|
||||||
playbackInfo = playbackInfo.copyWithOffloadSchedulingEnabled(offloadSchedulingEnabled);
|
|
||||||
} else {
|
|
||||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -952,12 +950,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
private void doSomeWork() throws ExoPlaybackException, IOException {
|
private void doSomeWork() throws ExoPlaybackException, IOException {
|
||||||
long operationStartTimeMs = clock.uptimeMillis();
|
long operationStartTimeMs = clock.uptimeMillis();
|
||||||
|
// Remove other pending DO_SOME_WORK requests that are handled by this invocation.
|
||||||
|
handler.removeMessages(MSG_DO_SOME_WORK);
|
||||||
|
|
||||||
updatePeriods();
|
updatePeriods();
|
||||||
|
|
||||||
if (playbackInfo.playbackState == Player.STATE_IDLE
|
if (playbackInfo.playbackState == Player.STATE_IDLE
|
||||||
|| playbackInfo.playbackState == Player.STATE_ENDED) {
|
|| playbackInfo.playbackState == Player.STATE_ENDED) {
|
||||||
// Remove all messages. Prepare (in case of IDLE) or seek (in case of ENDED) will resume.
|
// Nothing to do. Prepare (in case of IDLE) or seek (in case of ENDED) will resume.
|
||||||
handler.removeMessages(MSG_DO_SOME_WORK);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1070,24 +1070,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
throw new IllegalStateException("Playback stuck buffering and not loading");
|
throw new IllegalStateException("Playback stuck buffering and not loading");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (offloadSchedulingEnabled != playbackInfo.offloadSchedulingEnabled) {
|
boolean isPlaying = shouldPlayWhenReady() && playbackInfo.playbackState == Player.STATE_READY;
|
||||||
playbackInfo = playbackInfo.copyWithOffloadSchedulingEnabled(offloadSchedulingEnabled);
|
boolean sleepingForOffload = offloadSchedulingEnabled && requestForRendererSleep && isPlaying;
|
||||||
}
|
|
||||||
|
|
||||||
boolean sleepingForOffload = false;
|
|
||||||
if ((shouldPlayWhenReady() && playbackInfo.playbackState == Player.STATE_READY)
|
|
||||||
|| playbackInfo.playbackState == Player.STATE_BUFFERING) {
|
|
||||||
sleepingForOffload = !maybeScheduleWakeup(operationStartTimeMs, ACTIVE_INTERVAL_MS);
|
|
||||||
} else if (enabledRendererCount != 0 && playbackInfo.playbackState != Player.STATE_ENDED) {
|
|
||||||
scheduleNextWork(operationStartTimeMs, IDLE_INTERVAL_MS);
|
|
||||||
} else {
|
|
||||||
handler.removeMessages(MSG_DO_SOME_WORK);
|
|
||||||
}
|
|
||||||
if (playbackInfo.sleepingForOffload != sleepingForOffload) {
|
if (playbackInfo.sleepingForOffload != sleepingForOffload) {
|
||||||
playbackInfo = playbackInfo.copyWithSleepingForOffload(sleepingForOffload);
|
playbackInfo = playbackInfo.copyWithSleepingForOffload(sleepingForOffload);
|
||||||
}
|
}
|
||||||
requestForRendererSleep = false; // A sleep request is only valid for the current doSomeWork.
|
requestForRendererSleep = false; // A sleep request is only valid for the current doSomeWork.
|
||||||
|
|
||||||
|
if (sleepingForOffload || playbackInfo.playbackState == Player.STATE_ENDED) {
|
||||||
|
// No need to schedule next work.
|
||||||
|
return;
|
||||||
|
} else if (isPlaying || playbackInfo.playbackState == Player.STATE_BUFFERING) {
|
||||||
|
// We are actively playing or waiting for data to be ready. Schedule next work quickly.
|
||||||
|
scheduleNextWork(operationStartTimeMs, ACTIVE_INTERVAL_MS);
|
||||||
|
} else if (playbackInfo.playbackState == Player.STATE_READY && enabledRendererCount != 0) {
|
||||||
|
// We are ready, but not playing. Schedule next work less often to handle non-urgent updates.
|
||||||
|
scheduleNextWork(operationStartTimeMs, IDLE_INTERVAL_MS);
|
||||||
|
}
|
||||||
|
|
||||||
TraceUtil.endSection();
|
TraceUtil.endSection();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1117,19 +1117,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleNextWork(long thisOperationStartTimeMs, long intervalMs) {
|
private void scheduleNextWork(long thisOperationStartTimeMs, long intervalMs) {
|
||||||
handler.removeMessages(MSG_DO_SOME_WORK);
|
|
||||||
handler.sendEmptyMessageAtTime(MSG_DO_SOME_WORK, thisOperationStartTimeMs + intervalMs);
|
handler.sendEmptyMessageAtTime(MSG_DO_SOME_WORK, thisOperationStartTimeMs + intervalMs);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean maybeScheduleWakeup(long operationStartTimeMs, long intervalMs) {
|
|
||||||
if (offloadSchedulingEnabled && requestForRendererSleep) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
scheduleNextWork(operationStartTimeMs, intervalMs);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void seekToInternal(SeekPosition seekPosition) throws ExoPlaybackException {
|
private void seekToInternal(SeekPosition seekPosition) throws ExoPlaybackException {
|
||||||
playbackInfoUpdate.incrementPendingOperationAcks(/* operationAcks= */ 1);
|
playbackInfoUpdate.incrementPendingOperationAcks(/* operationAcks= */ 1);
|
||||||
|
|
||||||
|
|
@ -1460,7 +1450,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
/* bufferedPositionUs= */ startPositionUs,
|
/* bufferedPositionUs= */ startPositionUs,
|
||||||
/* totalBufferedDurationUs= */ 0,
|
/* totalBufferedDurationUs= */ 0,
|
||||||
/* positionUs= */ startPositionUs,
|
/* positionUs= */ startPositionUs,
|
||||||
offloadSchedulingEnabled,
|
|
||||||
/* sleepingForOffload= */ false);
|
/* sleepingForOffload= */ false);
|
||||||
if (releaseMediaSourceList) {
|
if (releaseMediaSourceList) {
|
||||||
mediaSourceList.release();
|
mediaSourceList.release();
|
||||||
|
|
|
||||||
|
|
@ -70,8 +70,6 @@ import java.util.List;
|
||||||
public final @PlaybackSuppressionReason int playbackSuppressionReason;
|
public final @PlaybackSuppressionReason int playbackSuppressionReason;
|
||||||
/** The playback parameters. */
|
/** The playback parameters. */
|
||||||
public final PlaybackParameters playbackParameters;
|
public final PlaybackParameters playbackParameters;
|
||||||
/** Whether offload scheduling is enabled for the main player loop. */
|
|
||||||
public final boolean offloadSchedulingEnabled;
|
|
||||||
/** Whether the main player loop is sleeping, while using offload scheduling. */
|
/** Whether the main player loop is sleeping, while using offload scheduling. */
|
||||||
public final boolean sleepingForOffload;
|
public final boolean sleepingForOffload;
|
||||||
|
|
||||||
|
|
@ -118,7 +116,6 @@ import java.util.List;
|
||||||
/* bufferedPositionUs= */ 0,
|
/* bufferedPositionUs= */ 0,
|
||||||
/* totalBufferedDurationUs= */ 0,
|
/* totalBufferedDurationUs= */ 0,
|
||||||
/* positionUs= */ 0,
|
/* positionUs= */ 0,
|
||||||
/* offloadSchedulingEnabled= */ false,
|
|
||||||
/* sleepingForOffload= */ false);
|
/* sleepingForOffload= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -141,7 +138,6 @@ import java.util.List;
|
||||||
* @param bufferedPositionUs See {@link #bufferedPositionUs}.
|
* @param bufferedPositionUs See {@link #bufferedPositionUs}.
|
||||||
* @param totalBufferedDurationUs See {@link #totalBufferedDurationUs}.
|
* @param totalBufferedDurationUs See {@link #totalBufferedDurationUs}.
|
||||||
* @param positionUs See {@link #positionUs}.
|
* @param positionUs See {@link #positionUs}.
|
||||||
* @param offloadSchedulingEnabled See {@link #offloadSchedulingEnabled}.
|
|
||||||
* @param sleepingForOffload See {@link #sleepingForOffload}.
|
* @param sleepingForOffload See {@link #sleepingForOffload}.
|
||||||
*/
|
*/
|
||||||
public PlaybackInfo(
|
public PlaybackInfo(
|
||||||
|
|
@ -162,7 +158,6 @@ import java.util.List;
|
||||||
long bufferedPositionUs,
|
long bufferedPositionUs,
|
||||||
long totalBufferedDurationUs,
|
long totalBufferedDurationUs,
|
||||||
long positionUs,
|
long positionUs,
|
||||||
boolean offloadSchedulingEnabled,
|
|
||||||
boolean sleepingForOffload) {
|
boolean sleepingForOffload) {
|
||||||
this.timeline = timeline;
|
this.timeline = timeline;
|
||||||
this.periodId = periodId;
|
this.periodId = periodId;
|
||||||
|
|
@ -181,7 +176,6 @@ import java.util.List;
|
||||||
this.bufferedPositionUs = bufferedPositionUs;
|
this.bufferedPositionUs = bufferedPositionUs;
|
||||||
this.totalBufferedDurationUs = totalBufferedDurationUs;
|
this.totalBufferedDurationUs = totalBufferedDurationUs;
|
||||||
this.positionUs = positionUs;
|
this.positionUs = positionUs;
|
||||||
this.offloadSchedulingEnabled = offloadSchedulingEnabled;
|
|
||||||
this.sleepingForOffload = sleepingForOffload;
|
this.sleepingForOffload = sleepingForOffload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -233,7 +227,6 @@ import java.util.List;
|
||||||
bufferedPositionUs,
|
bufferedPositionUs,
|
||||||
totalBufferedDurationUs,
|
totalBufferedDurationUs,
|
||||||
positionUs,
|
positionUs,
|
||||||
offloadSchedulingEnabled,
|
|
||||||
sleepingForOffload);
|
sleepingForOffload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,7 +256,6 @@ import java.util.List;
|
||||||
bufferedPositionUs,
|
bufferedPositionUs,
|
||||||
totalBufferedDurationUs,
|
totalBufferedDurationUs,
|
||||||
positionUs,
|
positionUs,
|
||||||
offloadSchedulingEnabled,
|
|
||||||
sleepingForOffload);
|
sleepingForOffload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -293,7 +285,6 @@ import java.util.List;
|
||||||
bufferedPositionUs,
|
bufferedPositionUs,
|
||||||
totalBufferedDurationUs,
|
totalBufferedDurationUs,
|
||||||
positionUs,
|
positionUs,
|
||||||
offloadSchedulingEnabled,
|
|
||||||
sleepingForOffload);
|
sleepingForOffload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -323,7 +314,6 @@ import java.util.List;
|
||||||
bufferedPositionUs,
|
bufferedPositionUs,
|
||||||
totalBufferedDurationUs,
|
totalBufferedDurationUs,
|
||||||
positionUs,
|
positionUs,
|
||||||
offloadSchedulingEnabled,
|
|
||||||
sleepingForOffload);
|
sleepingForOffload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -353,7 +343,6 @@ import java.util.List;
|
||||||
bufferedPositionUs,
|
bufferedPositionUs,
|
||||||
totalBufferedDurationUs,
|
totalBufferedDurationUs,
|
||||||
positionUs,
|
positionUs,
|
||||||
offloadSchedulingEnabled,
|
|
||||||
sleepingForOffload);
|
sleepingForOffload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -383,7 +372,6 @@ import java.util.List;
|
||||||
bufferedPositionUs,
|
bufferedPositionUs,
|
||||||
totalBufferedDurationUs,
|
totalBufferedDurationUs,
|
||||||
positionUs,
|
positionUs,
|
||||||
offloadSchedulingEnabled,
|
|
||||||
sleepingForOffload);
|
sleepingForOffload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -417,7 +405,6 @@ import java.util.List;
|
||||||
bufferedPositionUs,
|
bufferedPositionUs,
|
||||||
totalBufferedDurationUs,
|
totalBufferedDurationUs,
|
||||||
positionUs,
|
positionUs,
|
||||||
offloadSchedulingEnabled,
|
|
||||||
sleepingForOffload);
|
sleepingForOffload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -447,38 +434,6 @@ import java.util.List;
|
||||||
bufferedPositionUs,
|
bufferedPositionUs,
|
||||||
totalBufferedDurationUs,
|
totalBufferedDurationUs,
|
||||||
positionUs,
|
positionUs,
|
||||||
offloadSchedulingEnabled,
|
|
||||||
sleepingForOffload);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copies playback info with new offloadSchedulingEnabled.
|
|
||||||
*
|
|
||||||
* @param offloadSchedulingEnabled New offloadSchedulingEnabled state. See {@link
|
|
||||||
* #offloadSchedulingEnabled}.
|
|
||||||
* @return Copied playback info with new offload scheduling state.
|
|
||||||
*/
|
|
||||||
@CheckResult
|
|
||||||
public PlaybackInfo copyWithOffloadSchedulingEnabled(boolean offloadSchedulingEnabled) {
|
|
||||||
return new PlaybackInfo(
|
|
||||||
timeline,
|
|
||||||
periodId,
|
|
||||||
requestedContentPositionUs,
|
|
||||||
discontinuityStartPositionUs,
|
|
||||||
playbackState,
|
|
||||||
playbackError,
|
|
||||||
isLoading,
|
|
||||||
trackGroups,
|
|
||||||
trackSelectorResult,
|
|
||||||
staticMetadata,
|
|
||||||
loadingMediaPeriodId,
|
|
||||||
playWhenReady,
|
|
||||||
playbackSuppressionReason,
|
|
||||||
playbackParameters,
|
|
||||||
bufferedPositionUs,
|
|
||||||
totalBufferedDurationUs,
|
|
||||||
positionUs,
|
|
||||||
offloadSchedulingEnabled,
|
|
||||||
sleepingForOffload);
|
sleepingForOffload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -508,7 +463,6 @@ import java.util.List;
|
||||||
bufferedPositionUs,
|
bufferedPositionUs,
|
||||||
totalBufferedDurationUs,
|
totalBufferedDurationUs,
|
||||||
positionUs,
|
positionUs,
|
||||||
offloadSchedulingEnabled,
|
|
||||||
sleepingForOffload);
|
sleepingForOffload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ import android.media.AudioManager;
|
||||||
import android.media.AudioTrack;
|
import android.media.AudioTrack;
|
||||||
import android.media.PlaybackParams;
|
import android.media.PlaybackParams;
|
||||||
import android.media.metrics.LogSessionId;
|
import android.media.metrics.LogSessionId;
|
||||||
import android.os.ConditionVariable;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.SystemClock;
|
import android.os.SystemClock;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
@ -43,6 +42,8 @@ import com.google.android.exoplayer2.PlaybackParameters;
|
||||||
import com.google.android.exoplayer2.analytics.PlayerId;
|
import com.google.android.exoplayer2.analytics.PlayerId;
|
||||||
import com.google.android.exoplayer2.audio.AudioProcessor.UnhandledAudioFormatException;
|
import com.google.android.exoplayer2.audio.AudioProcessor.UnhandledAudioFormatException;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
import com.google.android.exoplayer2.util.Clock;
|
||||||
|
import com.google.android.exoplayer2.util.ConditionVariable;
|
||||||
import com.google.android.exoplayer2.util.Log;
|
import com.google.android.exoplayer2.util.Log;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
|
@ -606,7 +607,8 @@ public final class DefaultAudioSink implements AudioSink {
|
||||||
enableAudioTrackPlaybackParams = Util.SDK_INT >= 23 && builder.enableAudioTrackPlaybackParams;
|
enableAudioTrackPlaybackParams = Util.SDK_INT >= 23 && builder.enableAudioTrackPlaybackParams;
|
||||||
offloadMode = Util.SDK_INT >= 29 ? builder.offloadMode : OFFLOAD_MODE_DISABLED;
|
offloadMode = Util.SDK_INT >= 29 ? builder.offloadMode : OFFLOAD_MODE_DISABLED;
|
||||||
audioTrackBufferSizeProvider = builder.audioTrackBufferSizeProvider;
|
audioTrackBufferSizeProvider = builder.audioTrackBufferSizeProvider;
|
||||||
releasingConditionVariable = new ConditionVariable(true);
|
releasingConditionVariable = new ConditionVariable(Clock.DEFAULT);
|
||||||
|
releasingConditionVariable.open();
|
||||||
audioTrackPositionTracker = new AudioTrackPositionTracker(new PositionTrackerListener());
|
audioTrackPositionTracker = new AudioTrackPositionTracker(new PositionTrackerListener());
|
||||||
channelMappingAudioProcessor = new ChannelMappingAudioProcessor();
|
channelMappingAudioProcessor = new ChannelMappingAudioProcessor();
|
||||||
trimmingAudioProcessor = new TrimmingAudioProcessor();
|
trimmingAudioProcessor = new TrimmingAudioProcessor();
|
||||||
|
|
@ -831,13 +833,15 @@ public final class DefaultAudioSink implements AudioSink {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeAudioTrack() throws InitializationException {
|
private boolean initializeAudioTrack() throws InitializationException {
|
||||||
// If we're asynchronously releasing a previous audio track then we block until it has been
|
// If we're asynchronously releasing a previous audio track then we wait until it has been
|
||||||
// released. This guarantees that we cannot end up in a state where we have multiple audio
|
// released. This guarantees that we cannot end up in a state where we have multiple audio
|
||||||
// track instances. Without this guarantee it would be possible, in extreme cases, to exhaust
|
// track instances. Without this guarantee it would be possible, in extreme cases, to exhaust
|
||||||
// the shared memory that's available for audio track buffers. This would in turn cause the
|
// the shared memory that's available for audio track buffers. This would in turn cause the
|
||||||
// initialization of the audio track to fail.
|
// initialization of the audio track to fail.
|
||||||
releasingConditionVariable.block();
|
if (!releasingConditionVariable.isOpen()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
audioTrack = buildAudioTrackWithRetry();
|
audioTrack = buildAudioTrackWithRetry();
|
||||||
if (isOffloadedPlayback(audioTrack)) {
|
if (isOffloadedPlayback(audioTrack)) {
|
||||||
|
|
@ -865,6 +869,7 @@ public final class DefaultAudioSink implements AudioSink {
|
||||||
}
|
}
|
||||||
|
|
||||||
startMediaTimeUsNeedsInit = true;
|
startMediaTimeUsNeedsInit = true;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -921,7 +926,10 @@ public final class DefaultAudioSink implements AudioSink {
|
||||||
|
|
||||||
if (!isAudioTrackInitialized()) {
|
if (!isAudioTrackInitialized()) {
|
||||||
try {
|
try {
|
||||||
initializeAudioTrack();
|
if (!initializeAudioTrack()) {
|
||||||
|
// Not yet ready for initialization of a new AudioTrack.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} catch (InitializationException e) {
|
} catch (InitializationException e) {
|
||||||
if (e.isRecoverable) {
|
if (e.isRecoverable) {
|
||||||
throw e; // Do not delay the exception if it can be recovered at higher level.
|
throw e; // Do not delay the exception if it can be recovered at higher level.
|
||||||
|
|
|
||||||
|
|
@ -315,7 +315,9 @@ public final class MediaCodecInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (CodecProfileLevel profileLevel : profileLevels) {
|
for (CodecProfileLevel profileLevel : profileLevels) {
|
||||||
if (profileLevel.profile == profile && profileLevel.level >= level) {
|
if (profileLevel.profile == profile
|
||||||
|
&& profileLevel.level >= level
|
||||||
|
&& !needsProfileExcludedWorkaround(mimeType, profile)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -829,4 +831,15 @@ public final class MediaCodecInfo {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether a profile is excluded from the list of supported profiles. This may happen when a
|
||||||
|
* device declares support for a profile it doesn't actually support.
|
||||||
|
*/
|
||||||
|
private static boolean needsProfileExcludedWorkaround(String mimeType, int profile) {
|
||||||
|
// See https://github.com/google/ExoPlayer/issues/3537
|
||||||
|
return MimeTypes.VIDEO_H265.equals(mimeType)
|
||||||
|
&& CodecProfileLevel.HEVCProfileMain10 == profile
|
||||||
|
&& ("sailfish".equals(Util.DEVICE) || "marlin".equals(Util.DEVICE));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -274,6 +274,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
||||||
*/
|
*/
|
||||||
public DefaultMediaSourceFactory setDataSourceFactory(DataSource.Factory dataSourceFactory) {
|
public DefaultMediaSourceFactory setDataSourceFactory(DataSource.Factory dataSourceFactory) {
|
||||||
this.dataSourceFactory = dataSourceFactory;
|
this.dataSourceFactory = dataSourceFactory;
|
||||||
|
delegateFactoryLoader.setDataSourceFactory(dataSourceFactory);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -576,6 +577,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
||||||
this.dataSourceFactory = dataSourceFactory;
|
this.dataSourceFactory = dataSourceFactory;
|
||||||
// TODO(b/233577470): Call MediaSource.Factory.setDataSourceFactory on each value when it
|
// TODO(b/233577470): Call MediaSource.Factory.setDataSourceFactory on each value when it
|
||||||
// exists on the interface.
|
// exists on the interface.
|
||||||
|
mediaSourceFactorySuppliers.clear();
|
||||||
mediaSourceFactories.clear();
|
mediaSourceFactories.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -609,6 +611,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable Supplier<MediaSource.Factory> mediaSourceFactorySupplier = null;
|
@Nullable Supplier<MediaSource.Factory> mediaSourceFactorySupplier = null;
|
||||||
|
DataSource.Factory dataSourceFactory = checkNotNull(this.dataSourceFactory);
|
||||||
try {
|
try {
|
||||||
Class<? extends MediaSource.Factory> clazz;
|
Class<? extends MediaSource.Factory> clazz;
|
||||||
switch (contentType) {
|
switch (contentType) {
|
||||||
|
|
@ -616,20 +619,20 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
||||||
clazz =
|
clazz =
|
||||||
Class.forName("com.google.android.exoplayer2.source.dash.DashMediaSource$Factory")
|
Class.forName("com.google.android.exoplayer2.source.dash.DashMediaSource$Factory")
|
||||||
.asSubclass(MediaSource.Factory.class);
|
.asSubclass(MediaSource.Factory.class);
|
||||||
mediaSourceFactorySupplier = () -> newInstance(clazz, checkNotNull(dataSourceFactory));
|
mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
|
||||||
break;
|
break;
|
||||||
case C.CONTENT_TYPE_SS:
|
case C.CONTENT_TYPE_SS:
|
||||||
clazz =
|
clazz =
|
||||||
Class.forName(
|
Class.forName(
|
||||||
"com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource$Factory")
|
"com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource$Factory")
|
||||||
.asSubclass(MediaSource.Factory.class);
|
.asSubclass(MediaSource.Factory.class);
|
||||||
mediaSourceFactorySupplier = () -> newInstance(clazz, checkNotNull(dataSourceFactory));
|
mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
|
||||||
break;
|
break;
|
||||||
case C.CONTENT_TYPE_HLS:
|
case C.CONTENT_TYPE_HLS:
|
||||||
clazz =
|
clazz =
|
||||||
Class.forName("com.google.android.exoplayer2.source.hls.HlsMediaSource$Factory")
|
Class.forName("com.google.android.exoplayer2.source.hls.HlsMediaSource$Factory")
|
||||||
.asSubclass(MediaSource.Factory.class);
|
.asSubclass(MediaSource.Factory.class);
|
||||||
mediaSourceFactorySupplier = () -> newInstance(clazz, checkNotNull(dataSourceFactory));
|
mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
|
||||||
break;
|
break;
|
||||||
case C.CONTENT_TYPE_RTSP:
|
case C.CONTENT_TYPE_RTSP:
|
||||||
clazz =
|
clazz =
|
||||||
|
|
@ -639,9 +642,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
||||||
break;
|
break;
|
||||||
case C.CONTENT_TYPE_OTHER:
|
case C.CONTENT_TYPE_OTHER:
|
||||||
mediaSourceFactorySupplier =
|
mediaSourceFactorySupplier =
|
||||||
() ->
|
() -> new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory);
|
||||||
new ProgressiveMediaSource.Factory(
|
|
||||||
checkNotNull(dataSourceFactory), extractorsFactory);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.source;
|
package com.google.android.exoplayer2.source;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||||
import static java.lang.Math.max;
|
import static java.lang.Math.max;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
|
|
@ -134,7 +135,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
private boolean seenFirstTrackSelection;
|
private boolean seenFirstTrackSelection;
|
||||||
private boolean notifyDiscontinuity;
|
private boolean notifyDiscontinuity;
|
||||||
private int enabledTrackCount;
|
private int enabledTrackCount;
|
||||||
private long length;
|
private boolean isLengthKnown;
|
||||||
|
|
||||||
private long lastSeekPositionUs;
|
private long lastSeekPositionUs;
|
||||||
private long pendingResetPositionUs;
|
private long pendingResetPositionUs;
|
||||||
|
|
@ -192,15 +193,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
onContinueLoadingRequestedRunnable =
|
onContinueLoadingRequestedRunnable =
|
||||||
() -> {
|
() -> {
|
||||||
if (!released) {
|
if (!released) {
|
||||||
Assertions.checkNotNull(callback)
|
checkNotNull(callback).onContinueLoadingRequested(ProgressiveMediaPeriod.this);
|
||||||
.onContinueLoadingRequested(ProgressiveMediaPeriod.this);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
handler = Util.createHandlerForCurrentLooper();
|
handler = Util.createHandlerForCurrentLooper();
|
||||||
sampleQueueTrackIds = new TrackId[0];
|
sampleQueueTrackIds = new TrackId[0];
|
||||||
sampleQueues = new SampleQueue[0];
|
sampleQueues = new SampleQueue[0];
|
||||||
pendingResetPositionUs = C.TIME_UNSET;
|
pendingResetPositionUs = C.TIME_UNSET;
|
||||||
length = C.LENGTH_UNSET;
|
|
||||||
durationUs = C.TIME_UNSET;
|
durationUs = C.TIME_UNSET;
|
||||||
dataType = C.DATA_TYPE_MEDIA;
|
dataType = C.DATA_TYPE_MEDIA;
|
||||||
}
|
}
|
||||||
|
|
@ -366,7 +365,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getNextLoadPositionUs() {
|
public long getNextLoadPositionUs() {
|
||||||
return enabledTrackCount == 0 ? C.TIME_END_OF_SOURCE : getBufferedPositionUs();
|
return getBufferedPositionUs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -382,8 +381,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
@Override
|
@Override
|
||||||
public long getBufferedPositionUs() {
|
public long getBufferedPositionUs() {
|
||||||
assertPrepared();
|
assertPrepared();
|
||||||
boolean[] trackIsAudioVideoFlags = trackState.trackIsAudioVideoFlags;
|
if (loadingFinished || enabledTrackCount == 0) {
|
||||||
if (loadingFinished) {
|
|
||||||
return C.TIME_END_OF_SOURCE;
|
return C.TIME_END_OF_SOURCE;
|
||||||
} else if (isPendingReset()) {
|
} else if (isPendingReset()) {
|
||||||
return pendingResetPositionUs;
|
return pendingResetPositionUs;
|
||||||
|
|
@ -393,14 +391,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
// Ignore non-AV tracks, which may be sparse or poorly interleaved.
|
// Ignore non-AV tracks, which may be sparse or poorly interleaved.
|
||||||
int trackCount = sampleQueues.length;
|
int trackCount = sampleQueues.length;
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
if (trackIsAudioVideoFlags[i] && !sampleQueues[i].isLastSampleQueued()) {
|
if (trackState.trackIsAudioVideoFlags[i]
|
||||||
|
&& trackState.trackEnabledStates[i]
|
||||||
|
&& !sampleQueues[i].isLastSampleQueued()) {
|
||||||
largestQueuedTimestampUs =
|
largestQueuedTimestampUs =
|
||||||
min(largestQueuedTimestampUs, sampleQueues[i].getLargestQueuedTimestampUs());
|
min(largestQueuedTimestampUs, sampleQueues[i].getLargestQueuedTimestampUs());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (largestQueuedTimestampUs == Long.MAX_VALUE) {
|
if (largestQueuedTimestampUs == Long.MAX_VALUE) {
|
||||||
largestQueuedTimestampUs = getLargestQueuedTimestampUs();
|
largestQueuedTimestampUs = getLargestQueuedTimestampUs(/* includeDisabledTracks= */ false);
|
||||||
}
|
}
|
||||||
return largestQueuedTimestampUs == Long.MIN_VALUE
|
return largestQueuedTimestampUs == Long.MIN_VALUE
|
||||||
? lastSeekPositionUs
|
? lastSeekPositionUs
|
||||||
|
|
@ -536,7 +536,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
for (SampleQueue sampleQueue : sampleQueues) {
|
for (SampleQueue sampleQueue : sampleQueues) {
|
||||||
sampleQueue.reset();
|
sampleQueue.reset();
|
||||||
}
|
}
|
||||||
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
|
checkNotNull(callback).onContinueLoadingRequested(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean suppressRead() {
|
private boolean suppressRead() {
|
||||||
|
|
@ -550,7 +550,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
ExtractingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) {
|
ExtractingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) {
|
||||||
if (durationUs == C.TIME_UNSET && seekMap != null) {
|
if (durationUs == C.TIME_UNSET && seekMap != null) {
|
||||||
boolean isSeekable = seekMap.isSeekable();
|
boolean isSeekable = seekMap.isSeekable();
|
||||||
long largestQueuedTimestampUs = getLargestQueuedTimestampUs();
|
long largestQueuedTimestampUs =
|
||||||
|
getLargestQueuedTimestampUs(/* includeDisabledTracks= */ true);
|
||||||
durationUs =
|
durationUs =
|
||||||
largestQueuedTimestampUs == Long.MIN_VALUE
|
largestQueuedTimestampUs == Long.MIN_VALUE
|
||||||
? 0
|
? 0
|
||||||
|
|
@ -577,9 +578,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
/* trackSelectionData= */ null,
|
/* trackSelectionData= */ null,
|
||||||
/* mediaStartTimeUs= */ loadable.seekTimeUs,
|
/* mediaStartTimeUs= */ loadable.seekTimeUs,
|
||||||
durationUs);
|
durationUs);
|
||||||
copyLengthFromLoader(loadable);
|
|
||||||
loadingFinished = true;
|
loadingFinished = true;
|
||||||
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
|
checkNotNull(callback).onContinueLoadingRequested(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -606,12 +606,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
/* mediaStartTimeUs= */ loadable.seekTimeUs,
|
/* mediaStartTimeUs= */ loadable.seekTimeUs,
|
||||||
durationUs);
|
durationUs);
|
||||||
if (!released) {
|
if (!released) {
|
||||||
copyLengthFromLoader(loadable);
|
|
||||||
for (SampleQueue sampleQueue : sampleQueues) {
|
for (SampleQueue sampleQueue : sampleQueues) {
|
||||||
sampleQueue.reset();
|
sampleQueue.reset();
|
||||||
}
|
}
|
||||||
if (enabledTrackCount > 0) {
|
if (enabledTrackCount > 0) {
|
||||||
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
|
checkNotNull(callback).onContinueLoadingRequested(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -623,7 +622,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
long loadDurationMs,
|
long loadDurationMs,
|
||||||
IOException error,
|
IOException error,
|
||||||
int errorCount) {
|
int errorCount) {
|
||||||
copyLengthFromLoader(loadable);
|
|
||||||
StatsDataSource dataSource = loadable.dataSource;
|
StatsDataSource dataSource = loadable.dataSource;
|
||||||
LoadEventInfo loadEventInfo =
|
LoadEventInfo loadEventInfo =
|
||||||
new LoadEventInfo(
|
new LoadEventInfo(
|
||||||
|
|
@ -709,6 +707,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
// Internal methods.
|
// Internal methods.
|
||||||
|
|
||||||
|
private void onLengthKnown() {
|
||||||
|
handler.post(() -> isLengthKnown = true);
|
||||||
|
}
|
||||||
|
|
||||||
private TrackOutput prepareTrackOutput(TrackId id) {
|
private TrackOutput prepareTrackOutput(TrackId id) {
|
||||||
int trackCount = sampleQueues.length;
|
int trackCount = sampleQueues.length;
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
|
|
@ -732,7 +734,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
private void setSeekMap(SeekMap seekMap) {
|
private void setSeekMap(SeekMap seekMap) {
|
||||||
this.seekMap = icyHeaders == null ? seekMap : new Unseekable(/* durationUs= */ C.TIME_UNSET);
|
this.seekMap = icyHeaders == null ? seekMap : new Unseekable(/* durationUs= */ C.TIME_UNSET);
|
||||||
durationUs = seekMap.getDurationUs();
|
durationUs = seekMap.getDurationUs();
|
||||||
isLive = length == C.LENGTH_UNSET && seekMap.getDurationUs() == C.TIME_UNSET;
|
isLive = !isLengthKnown && seekMap.getDurationUs() == C.TIME_UNSET;
|
||||||
dataType = isLive ? C.DATA_TYPE_MEDIA_PROGRESSIVE_LIVE : C.DATA_TYPE_MEDIA;
|
dataType = isLive ? C.DATA_TYPE_MEDIA_PROGRESSIVE_LIVE : C.DATA_TYPE_MEDIA;
|
||||||
listener.onSourceInfoRefreshed(durationUs, seekMap.isSeekable(), isLive);
|
listener.onSourceInfoRefreshed(durationUs, seekMap.isSeekable(), isLive);
|
||||||
if (!prepared) {
|
if (!prepared) {
|
||||||
|
|
@ -754,7 +756,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
TrackGroup[] trackArray = new TrackGroup[trackCount];
|
TrackGroup[] trackArray = new TrackGroup[trackCount];
|
||||||
boolean[] trackIsAudioVideoFlags = new boolean[trackCount];
|
boolean[] trackIsAudioVideoFlags = new boolean[trackCount];
|
||||||
for (int i = 0; i < trackCount; i++) {
|
for (int i = 0; i < trackCount; i++) {
|
||||||
Format trackFormat = Assertions.checkNotNull(sampleQueues[i].getUpstreamFormat());
|
Format trackFormat = checkNotNull(sampleQueues[i].getUpstreamFormat());
|
||||||
@Nullable String mimeType = trackFormat.sampleMimeType;
|
@Nullable String mimeType = trackFormat.sampleMimeType;
|
||||||
boolean isAudio = MimeTypes.isAudio(mimeType);
|
boolean isAudio = MimeTypes.isAudio(mimeType);
|
||||||
boolean isAudioVideo = isAudio || MimeTypes.isVideo(mimeType);
|
boolean isAudioVideo = isAudio || MimeTypes.isVideo(mimeType);
|
||||||
|
|
@ -785,13 +787,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
}
|
}
|
||||||
trackState = new TrackState(new TrackGroupArray(trackArray), trackIsAudioVideoFlags);
|
trackState = new TrackState(new TrackGroupArray(trackArray), trackIsAudioVideoFlags);
|
||||||
prepared = true;
|
prepared = true;
|
||||||
Assertions.checkNotNull(callback).onPrepared(this);
|
checkNotNull(callback).onPrepared(this);
|
||||||
}
|
|
||||||
|
|
||||||
private void copyLengthFromLoader(ExtractingLoadable loadable) {
|
|
||||||
if (length == C.LENGTH_UNSET) {
|
|
||||||
length = loadable.length;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLoading() {
|
private void startLoading() {
|
||||||
|
|
@ -806,7 +802,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
loadable.setLoadPosition(
|
loadable.setLoadPosition(
|
||||||
Assertions.checkNotNull(seekMap).getSeekPoints(pendingResetPositionUs).first.position,
|
checkNotNull(seekMap).getSeekPoints(pendingResetPositionUs).first.position,
|
||||||
pendingResetPositionUs);
|
pendingResetPositionUs);
|
||||||
for (SampleQueue sampleQueue : sampleQueues) {
|
for (SampleQueue sampleQueue : sampleQueues) {
|
||||||
sampleQueue.setStartTimeUs(pendingResetPositionUs);
|
sampleQueue.setStartTimeUs(pendingResetPositionUs);
|
||||||
|
|
@ -839,7 +835,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
* retry.
|
* retry.
|
||||||
*/
|
*/
|
||||||
private boolean configureRetry(ExtractingLoadable loadable, int currentExtractedSampleCount) {
|
private boolean configureRetry(ExtractingLoadable loadable, int currentExtractedSampleCount) {
|
||||||
if (length != C.LENGTH_UNSET || (seekMap != null && seekMap.getDurationUs() != C.TIME_UNSET)) {
|
if (isLengthKnown || (seekMap != null && seekMap.getDurationUs() != C.TIME_UNSET)) {
|
||||||
// We're playing an on-demand stream. Resume the current loadable, which will
|
// We're playing an on-demand stream. Resume the current loadable, which will
|
||||||
// request data starting from the point it left off.
|
// request data starting from the point it left off.
|
||||||
extractedSamplesCountAtStartOfLoad = currentExtractedSampleCount;
|
extractedSamplesCountAtStartOfLoad = currentExtractedSampleCount;
|
||||||
|
|
@ -903,11 +899,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
return extractedSamplesCount;
|
return extractedSamplesCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private long getLargestQueuedTimestampUs() {
|
private long getLargestQueuedTimestampUs(boolean includeDisabledTracks) {
|
||||||
long largestQueuedTimestampUs = Long.MIN_VALUE;
|
long largestQueuedTimestampUs = Long.MIN_VALUE;
|
||||||
for (SampleQueue sampleQueue : sampleQueues) {
|
for (int i = 0; i < sampleQueues.length; i++) {
|
||||||
|
if (includeDisabledTracks || checkNotNull(trackState).trackEnabledStates[i]) {
|
||||||
largestQueuedTimestampUs =
|
largestQueuedTimestampUs =
|
||||||
max(largestQueuedTimestampUs, sampleQueue.getLargestQueuedTimestampUs());
|
max(largestQueuedTimestampUs, sampleQueues[i].getLargestQueuedTimestampUs());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return largestQueuedTimestampUs;
|
return largestQueuedTimestampUs;
|
||||||
}
|
}
|
||||||
|
|
@ -919,8 +917,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
@EnsuresNonNull({"trackState", "seekMap"})
|
@EnsuresNonNull({"trackState", "seekMap"})
|
||||||
private void assertPrepared() {
|
private void assertPrepared() {
|
||||||
Assertions.checkState(prepared);
|
Assertions.checkState(prepared);
|
||||||
Assertions.checkNotNull(trackState);
|
checkNotNull(trackState);
|
||||||
Assertions.checkNotNull(seekMap);
|
checkNotNull(seekMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class SampleStreamImpl implements SampleStream {
|
private final class SampleStreamImpl implements SampleStream {
|
||||||
|
|
@ -969,7 +967,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
private boolean pendingExtractorSeek;
|
private boolean pendingExtractorSeek;
|
||||||
private long seekTimeUs;
|
private long seekTimeUs;
|
||||||
private DataSpec dataSpec;
|
private DataSpec dataSpec;
|
||||||
private long length;
|
|
||||||
@Nullable private TrackOutput icyTrackOutput;
|
@Nullable private TrackOutput icyTrackOutput;
|
||||||
private boolean seenIcyMetadata;
|
private boolean seenIcyMetadata;
|
||||||
|
|
||||||
|
|
@ -987,7 +984,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
this.loadCondition = loadCondition;
|
this.loadCondition = loadCondition;
|
||||||
this.positionHolder = new PositionHolder();
|
this.positionHolder = new PositionHolder();
|
||||||
this.pendingExtractorSeek = true;
|
this.pendingExtractorSeek = true;
|
||||||
this.length = C.LENGTH_UNSET;
|
|
||||||
loadTaskId = LoadEventInfo.getNewId();
|
loadTaskId = LoadEventInfo.getNewId();
|
||||||
dataSpec = buildDataSpec(/* position= */ 0);
|
dataSpec = buildDataSpec(/* position= */ 0);
|
||||||
}
|
}
|
||||||
|
|
@ -1006,9 +1002,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
try {
|
try {
|
||||||
long position = positionHolder.position;
|
long position = positionHolder.position;
|
||||||
dataSpec = buildDataSpec(position);
|
dataSpec = buildDataSpec(position);
|
||||||
length = dataSource.open(dataSpec);
|
long length = dataSource.open(dataSpec);
|
||||||
if (length != C.LENGTH_UNSET) {
|
if (length != C.LENGTH_UNSET) {
|
||||||
length += position;
|
length += position;
|
||||||
|
onLengthKnown();
|
||||||
}
|
}
|
||||||
icyHeaders = IcyHeaders.parse(dataSource.getResponseHeaders());
|
icyHeaders = IcyHeaders.parse(dataSource.getResponseHeaders());
|
||||||
DataSource extractorDataSource = dataSource;
|
DataSource extractorDataSource = dataSource;
|
||||||
|
|
@ -1064,9 +1061,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
public void onIcyMetadata(ParsableByteArray metadata) {
|
public void onIcyMetadata(ParsableByteArray metadata) {
|
||||||
// Always output the first ICY metadata at the start time. This helps minimize any delay
|
// Always output the first ICY metadata at the start time. This helps minimize any delay
|
||||||
// between the start of playback and the first ICY metadata event.
|
// between the start of playback and the first ICY metadata event.
|
||||||
long timeUs = !seenIcyMetadata ? seekTimeUs : max(getLargestQueuedTimestampUs(), seekTimeUs);
|
long timeUs =
|
||||||
|
!seenIcyMetadata
|
||||||
|
? seekTimeUs
|
||||||
|
: max(getLargestQueuedTimestampUs(/* includeDisabledTracks= */ true), seekTimeUs);
|
||||||
int length = metadata.bytesLeft();
|
int length = metadata.bytesLeft();
|
||||||
TrackOutput icyTrackOutput = Assertions.checkNotNull(this.icyTrackOutput);
|
TrackOutput icyTrackOutput = checkNotNull(this.icyTrackOutput);
|
||||||
icyTrackOutput.sampleData(metadata, length);
|
icyTrackOutput.sampleData(metadata, length);
|
||||||
icyTrackOutput.sampleMetadata(
|
icyTrackOutput.sampleMetadata(
|
||||||
timeUs, C.BUFFER_FLAG_KEY_FRAME, length, /* offset= */ 0, /* cryptoData= */ null);
|
timeUs, C.BUFFER_FLAG_KEY_FRAME, length, /* offset= */ 0, /* cryptoData= */ null);
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ public interface TextOutput {
|
||||||
* Called when there is a change in the {@link CueGroup}.
|
* Called when there is a change in the {@link CueGroup}.
|
||||||
*
|
*
|
||||||
* <p>Both {@link #onCues(List)} and {@link #onCues(CueGroup)} are called when there is a change
|
* <p>Both {@link #onCues(List)} and {@link #onCues(CueGroup)} are called when there is a change
|
||||||
* in the cues You should only implement one or the other.
|
* in the cues. You should only implement one or the other.
|
||||||
*/
|
*/
|
||||||
void onCues(CueGroup cueGroup);
|
void onCues(CueGroup cueGroup);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -626,7 +626,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
surface = placeholderSurface;
|
surface = placeholderSurface;
|
||||||
} else {
|
} else {
|
||||||
MediaCodecInfo codecInfo = getCodecInfo();
|
MediaCodecInfo codecInfo = getCodecInfo();
|
||||||
if (codecInfo != null && shouldUseDummySurface(codecInfo)) {
|
if (codecInfo != null && shouldUsePlaceholderSurface(codecInfo)) {
|
||||||
placeholderSurface = PlaceholderSurface.newInstanceV17(context, codecInfo.secure);
|
placeholderSurface = PlaceholderSurface.newInstanceV17(context, codecInfo.secure);
|
||||||
surface = placeholderSurface;
|
surface = placeholderSurface;
|
||||||
}
|
}
|
||||||
|
|
@ -672,7 +672,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean shouldInitCodec(MediaCodecInfo codecInfo) {
|
protected boolean shouldInitCodec(MediaCodecInfo codecInfo) {
|
||||||
return surface != null || shouldUseDummySurface(codecInfo);
|
return surface != null || shouldUsePlaceholderSurface(codecInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -703,7 +703,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
deviceNeedsNoPostProcessWorkaround,
|
deviceNeedsNoPostProcessWorkaround,
|
||||||
tunneling ? tunnelingAudioSessionId : C.AUDIO_SESSION_ID_UNSET);
|
tunneling ? tunnelingAudioSessionId : C.AUDIO_SESSION_ID_UNSET);
|
||||||
if (surface == null) {
|
if (surface == null) {
|
||||||
if (!shouldUseDummySurface(codecInfo)) {
|
if (!shouldUsePlaceholderSurface(codecInfo)) {
|
||||||
throw new IllegalStateException();
|
throw new IllegalStateException();
|
||||||
}
|
}
|
||||||
if (placeholderSurface == null) {
|
if (placeholderSurface == null) {
|
||||||
|
|
@ -1330,7 +1330,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
maybeNotifyRenderedFirstFrame();
|
maybeNotifyRenderedFirstFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean shouldUseDummySurface(MediaCodecInfo codecInfo) {
|
private boolean shouldUsePlaceholderSurface(MediaCodecInfo codecInfo) {
|
||||||
return Util.SDK_INT >= 23
|
return Util.SDK_INT >= 23
|
||||||
&& !tunneling
|
&& !tunneling
|
||||||
&& !codecNeedsSetOutputSurfaceWorkaround(codecInfo.name)
|
&& !codecNeedsSetOutputSurfaceWorkaround(codecInfo.name)
|
||||||
|
|
@ -1569,7 +1569,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
}
|
}
|
||||||
if (haveUnknownDimensions) {
|
if (haveUnknownDimensions) {
|
||||||
Log.w(TAG, "Resolutions unknown. Codec max resolution: " + maxWidth + "x" + maxHeight);
|
Log.w(TAG, "Resolutions unknown. Codec max resolution: " + maxWidth + "x" + maxHeight);
|
||||||
Point codecMaxSize = getCodecMaxSize(codecInfo, format);
|
@Nullable Point codecMaxSize = getCodecMaxSize(codecInfo, format);
|
||||||
if (codecMaxSize != null) {
|
if (codecMaxSize != null) {
|
||||||
maxWidth = max(maxWidth, codecMaxSize.x);
|
maxWidth = max(maxWidth, codecMaxSize.x);
|
||||||
maxHeight = max(maxHeight, codecMaxSize.y);
|
maxHeight = max(maxHeight, codecMaxSize.y);
|
||||||
|
|
@ -1597,8 +1597,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
||||||
*
|
*
|
||||||
* @param codecInfo Information about the {@link MediaCodec} being configured.
|
* @param codecInfo Information about the {@link MediaCodec} being configured.
|
||||||
* @param format The {@link Format} for which the codec is being configured.
|
* @param format The {@link Format} for which the codec is being configured.
|
||||||
* @return The maximum video size to use, or null if the size of {@code format} should be used.
|
* @return The maximum video size to use, or {@code null} if the size of {@code format} should be
|
||||||
|
* used.
|
||||||
*/
|
*/
|
||||||
|
@Nullable
|
||||||
private static Point getCodecMaxSize(MediaCodecInfo codecInfo, Format format) {
|
private static Point getCodecMaxSize(MediaCodecInfo codecInfo, Format format) {
|
||||||
boolean isVerticalVideo = format.height > format.width;
|
boolean isVerticalVideo = format.height > format.width;
|
||||||
int formatLongEdgePx = isVerticalVideo ? format.height : format.width;
|
int formatLongEdgePx = isVerticalVideo ? format.height : format.width;
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,6 @@ import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.play
|
||||||
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilPendingCommandsAreFullyHandled;
|
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilPendingCommandsAreFullyHandled;
|
||||||
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilPlaybackState;
|
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilPlaybackState;
|
||||||
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilPositionDiscontinuity;
|
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilPositionDiscontinuity;
|
||||||
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilReceiveOffloadSchedulingEnabledNewState;
|
|
||||||
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilSleepingForOffload;
|
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilSleepingForOffload;
|
||||||
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilTimelineChanged;
|
import static com.google.android.exoplayer2.robolectric.TestPlayerRunHelper.runUntilTimelineChanged;
|
||||||
import static com.google.android.exoplayer2.source.ads.ServerSideAdInsertionUtil.addAdGroupToAdPlaybackState;
|
import static com.google.android.exoplayer2.source.ads.ServerSideAdInsertionUtil.addAdGroupToAdPlaybackState;
|
||||||
|
|
@ -62,6 +61,7 @@ import static com.google.android.exoplayer2.testutil.FakeSampleStream.FakeSample
|
||||||
import static com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_DURATION_US;
|
import static com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_DURATION_US;
|
||||||
import static com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
|
import static com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition.DEFAULT_WINDOW_OFFSET_IN_FIRST_PERIOD_US;
|
||||||
import static com.google.android.exoplayer2.testutil.TestUtil.assertTimelinesSame;
|
import static com.google.android.exoplayer2.testutil.TestUtil.assertTimelinesSame;
|
||||||
|
import static com.google.android.exoplayer2.testutil.TestUtil.timelinesAreSame;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
@ -113,6 +113,7 @@ import com.google.android.exoplayer2.source.MediaPeriod;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
|
||||||
import com.google.android.exoplayer2.source.MediaSourceEventListener;
|
import com.google.android.exoplayer2.source.MediaSourceEventListener;
|
||||||
|
import com.google.android.exoplayer2.source.ShuffleOrder;
|
||||||
import com.google.android.exoplayer2.source.SinglePeriodTimeline;
|
import com.google.android.exoplayer2.source.SinglePeriodTimeline;
|
||||||
import com.google.android.exoplayer2.source.TrackGroup;
|
import com.google.android.exoplayer2.source.TrackGroup;
|
||||||
import com.google.android.exoplayer2.source.TrackGroupArray;
|
import com.google.android.exoplayer2.source.TrackGroupArray;
|
||||||
|
|
@ -141,7 +142,6 @@ import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinit
|
||||||
import com.google.android.exoplayer2.testutil.FakeTrackSelection;
|
import com.google.android.exoplayer2.testutil.FakeTrackSelection;
|
||||||
import com.google.android.exoplayer2.testutil.FakeTrackSelector;
|
import com.google.android.exoplayer2.testutil.FakeTrackSelector;
|
||||||
import com.google.android.exoplayer2.testutil.FakeVideoRenderer;
|
import com.google.android.exoplayer2.testutil.FakeVideoRenderer;
|
||||||
import com.google.android.exoplayer2.testutil.NoUidTimeline;
|
|
||||||
import com.google.android.exoplayer2.testutil.TestExoPlayerBuilder;
|
import com.google.android.exoplayer2.testutil.TestExoPlayerBuilder;
|
||||||
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
|
||||||
import com.google.android.exoplayer2.upstream.Allocation;
|
import com.google.android.exoplayer2.upstream.Allocation;
|
||||||
|
|
@ -6502,6 +6502,53 @@ public final class ExoPlayerTest {
|
||||||
assertThat(positionAfterSetShuffleOrder.get()).isAtLeast(5000);
|
assertThat(positionAfterSetShuffleOrder.get()).isAtLeast(5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void setShuffleOrder_notifiesTimelineChanged() throws Exception {
|
||||||
|
ExoPlayer player =
|
||||||
|
new TestExoPlayerBuilder(context)
|
||||||
|
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||||
|
.build();
|
||||||
|
// No callback expected for this call, because the (empty) timeline doesn't change. We start
|
||||||
|
// with a deterministic shuffle order, to ensure when we call setShuffleOrder again below the
|
||||||
|
// order is definitely different (otherwise the test is flaky when the existing shuffle order
|
||||||
|
// matches the shuffle order passed in below).
|
||||||
|
player.setShuffleOrder(new FakeShuffleOrder(0));
|
||||||
|
player.setMediaSources(
|
||||||
|
ImmutableList.of(new FakeMediaSource(), new FakeMediaSource(), new FakeMediaSource()));
|
||||||
|
Player.Listener mockListener = mock(Player.Listener.class);
|
||||||
|
player.addListener(mockListener);
|
||||||
|
player.prepare();
|
||||||
|
TestPlayerRunHelper.playUntilPosition(player, /* mediaItemIndex= */ 0, /* positionMs= */ 5000);
|
||||||
|
player.play();
|
||||||
|
ShuffleOrder.DefaultShuffleOrder newShuffleOrder =
|
||||||
|
new ShuffleOrder.DefaultShuffleOrder(player.getMediaItemCount(), /* randomSeed= */ 5);
|
||||||
|
player.setShuffleOrder(newShuffleOrder);
|
||||||
|
TestPlayerRunHelper.runUntilPlaybackState(player, Player.STATE_ENDED);
|
||||||
|
player.release();
|
||||||
|
|
||||||
|
ArgumentCaptor<Timeline> timelineCaptor = ArgumentCaptor.forClass(Timeline.class);
|
||||||
|
verify(mockListener)
|
||||||
|
.onTimelineChanged(
|
||||||
|
timelineCaptor.capture(), eq(Player.TIMELINE_CHANGE_REASON_PLAYLIST_CHANGED));
|
||||||
|
|
||||||
|
Timeline capturedTimeline = Iterables.getOnlyElement(timelineCaptor.getAllValues());
|
||||||
|
List<Integer> newShuffleOrderIndexes = new ArrayList<>(newShuffleOrder.getLength());
|
||||||
|
for (int i = newShuffleOrder.getFirstIndex();
|
||||||
|
i != C.INDEX_UNSET;
|
||||||
|
i = newShuffleOrder.getNextIndex(i)) {
|
||||||
|
newShuffleOrderIndexes.add(i);
|
||||||
|
}
|
||||||
|
List<Integer> capturedTimelineShuffleIndexes = new ArrayList<>(newShuffleOrder.getLength());
|
||||||
|
for (int i = capturedTimeline.getFirstWindowIndex(/* shuffleModeEnabled= */ true);
|
||||||
|
i != C.INDEX_UNSET;
|
||||||
|
i =
|
||||||
|
capturedTimeline.getNextWindowIndex(
|
||||||
|
i, Player.REPEAT_MODE_OFF, /* shuffleModeEnabled= */ true)) {
|
||||||
|
capturedTimelineShuffleIndexes.add(i);
|
||||||
|
}
|
||||||
|
assertThat(capturedTimelineShuffleIndexes).isEqualTo(newShuffleOrderIndexes);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setMediaSources_empty_whenEmpty_correctMaskingMediaItemIndex() throws Exception {
|
public void setMediaSources_empty_whenEmpty_correctMaskingMediaItemIndex() throws Exception {
|
||||||
final int[] currentMediaItemIndices = {C.INDEX_UNSET, C.INDEX_UNSET, C.INDEX_UNSET};
|
final int[] currentMediaItemIndices = {C.INDEX_UNSET, C.INDEX_UNSET, C.INDEX_UNSET};
|
||||||
|
|
@ -9625,47 +9672,16 @@ public final class ExoPlayerTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void enableOffloadSchedulingWhileIdle_isToggled_isReported() throws Exception {
|
public void enableOffloadScheduling_isReported() throws Exception {
|
||||||
ExoPlayer player = new TestExoPlayerBuilder(context).build();
|
ExoPlayer player = new TestExoPlayerBuilder(context).build();
|
||||||
|
ExoPlayer.AudioOffloadListener mockListener = mock(ExoPlayer.AudioOffloadListener.class);
|
||||||
|
player.addAudioOffloadListener(mockListener);
|
||||||
|
|
||||||
player.experimentalSetOffloadSchedulingEnabled(true);
|
player.experimentalSetOffloadSchedulingEnabled(true);
|
||||||
assertThat(runUntilReceiveOffloadSchedulingEnabledNewState(player)).isTrue();
|
verify(mockListener).onExperimentalOffloadSchedulingEnabledChanged(true);
|
||||||
|
|
||||||
player.experimentalSetOffloadSchedulingEnabled(false);
|
player.experimentalSetOffloadSchedulingEnabled(false);
|
||||||
assertThat(runUntilReceiveOffloadSchedulingEnabledNewState(player)).isFalse();
|
verify(mockListener).onExperimentalOffloadSchedulingEnabledChanged(false);
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void enableOffloadSchedulingWhilePlaying_isToggled_isReported() throws Exception {
|
|
||||||
FakeSleepRenderer sleepRenderer = new FakeSleepRenderer(C.TRACK_TYPE_AUDIO).sleepOnNextRender();
|
|
||||||
ExoPlayer player = new TestExoPlayerBuilder(context).setRenderers(sleepRenderer).build();
|
|
||||||
Timeline timeline = new FakeTimeline();
|
|
||||||
player.setMediaSource(new FakeMediaSource(timeline, ExoPlayerTestRunner.AUDIO_FORMAT));
|
|
||||||
player.prepare();
|
|
||||||
player.play();
|
|
||||||
|
|
||||||
player.experimentalSetOffloadSchedulingEnabled(true);
|
|
||||||
assertThat(runUntilReceiveOffloadSchedulingEnabledNewState(player)).isTrue();
|
|
||||||
|
|
||||||
player.experimentalSetOffloadSchedulingEnabled(false);
|
|
||||||
assertThat(runUntilReceiveOffloadSchedulingEnabledNewState(player)).isFalse();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void enableOffloadSchedulingWhileSleepingForOffload_isDisabled_isReported()
|
|
||||||
throws Exception {
|
|
||||||
FakeSleepRenderer sleepRenderer = new FakeSleepRenderer(C.TRACK_TYPE_AUDIO).sleepOnNextRender();
|
|
||||||
ExoPlayer player = new TestExoPlayerBuilder(context).setRenderers(sleepRenderer).build();
|
|
||||||
Timeline timeline = new FakeTimeline();
|
|
||||||
player.setMediaSource(new FakeMediaSource(timeline, ExoPlayerTestRunner.AUDIO_FORMAT));
|
|
||||||
player.experimentalSetOffloadSchedulingEnabled(true);
|
|
||||||
player.prepare();
|
|
||||||
player.play();
|
|
||||||
runUntilSleepingForOffload(player, /* expectedSleepForOffload= */ true);
|
|
||||||
|
|
||||||
player.experimentalSetOffloadSchedulingEnabled(false);
|
|
||||||
|
|
||||||
assertThat(runUntilReceiveOffloadSchedulingEnabledNewState(player)).isFalse();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -12286,6 +12302,6 @@ public final class ExoPlayerTest {
|
||||||
* Returns an argument matcher for {@link Timeline} instances that ignores period and window uids.
|
* Returns an argument matcher for {@link Timeline} instances that ignores period and window uids.
|
||||||
*/
|
*/
|
||||||
private static ArgumentMatcher<Timeline> noUid(Timeline timeline) {
|
private static ArgumentMatcher<Timeline> noUid(Timeline timeline) {
|
||||||
return argument -> new NoUidTimeline(timeline).equals(new NoUidTimeline(argument));
|
return argument -> timelinesAreSame(argument, timeline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1106,7 +1106,6 @@ public final class MediaPeriodQueueTest {
|
||||||
/* bufferedPositionUs= */ 0,
|
/* bufferedPositionUs= */ 0,
|
||||||
/* totalBufferedDurationUs= */ 0,
|
/* totalBufferedDurationUs= */ 0,
|
||||||
/* positionUs= */ 0,
|
/* positionUs= */ 0,
|
||||||
/* offloadSchedulingEnabled= */ false,
|
|
||||||
/* sleepingForOffload= */ false);
|
/* sleepingForOffload= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ public class Mp4PlaybackTest {
|
||||||
"sample_eac3joc.mp4",
|
"sample_eac3joc.mp4",
|
||||||
"sample_fragmented.mp4",
|
"sample_fragmented.mp4",
|
||||||
"sample_fragmented_seekable.mp4",
|
"sample_fragmented_seekable.mp4",
|
||||||
|
"sample_fragmented_large_bitrates.mp4",
|
||||||
"sample_fragmented_sei.mp4",
|
"sample_fragmented_sei.mp4",
|
||||||
"sample_mdat_too_long.mp4",
|
"sample_mdat_too_long.mp4",
|
||||||
"sample.mp4",
|
"sample.mp4",
|
||||||
|
|
|
||||||
|
|
@ -597,6 +597,9 @@ public class DashManifestParser extends DefaultHandler
|
||||||
case "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed":
|
case "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed":
|
||||||
uuid = C.WIDEVINE_UUID;
|
uuid = C.WIDEVINE_UUID;
|
||||||
break;
|
break;
|
||||||
|
case "urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e":
|
||||||
|
uuid = C.CLEARKEY_UUID;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -604,7 +607,9 @@ public class DashManifestParser extends DefaultHandler
|
||||||
|
|
||||||
do {
|
do {
|
||||||
xpp.next();
|
xpp.next();
|
||||||
if (XmlPullParserUtil.isStartTag(xpp, "ms:laurl")) {
|
if (XmlPullParserUtil.isStartTag(xpp, "clearkey:Laurl") && xpp.next() == XmlPullParser.TEXT) {
|
||||||
|
licenseServerUrl = xpp.getText();
|
||||||
|
} else if (XmlPullParserUtil.isStartTag(xpp, "ms:laurl")) {
|
||||||
licenseServerUrl = xpp.getAttributeValue(null, "licenseUrl");
|
licenseServerUrl = xpp.getAttributeValue(null, "licenseUrl");
|
||||||
} else if (data == null
|
} else if (data == null
|
||||||
&& XmlPullParserUtil.isStartTagIgnorePrefix(xpp, "pssh")
|
&& XmlPullParserUtil.isStartTagIgnorePrefix(xpp, "pssh")
|
||||||
|
|
@ -851,6 +856,7 @@ public class DashManifestParser extends DefaultHandler
|
||||||
ArrayList<SchemeData> drmSchemeDatas = representationInfo.drmSchemeDatas;
|
ArrayList<SchemeData> drmSchemeDatas = representationInfo.drmSchemeDatas;
|
||||||
drmSchemeDatas.addAll(extraDrmSchemeDatas);
|
drmSchemeDatas.addAll(extraDrmSchemeDatas);
|
||||||
if (!drmSchemeDatas.isEmpty()) {
|
if (!drmSchemeDatas.isEmpty()) {
|
||||||
|
fillInClearKeyInformation(drmSchemeDatas);
|
||||||
filterRedundantIncompleteSchemeDatas(drmSchemeDatas);
|
filterRedundantIncompleteSchemeDatas(drmSchemeDatas);
|
||||||
formatBuilder.setDrmInitData(new DrmInitData(drmSchemeType, drmSchemeDatas));
|
formatBuilder.setDrmInitData(new DrmInitData(drmSchemeType, drmSchemeDatas));
|
||||||
}
|
}
|
||||||
|
|
@ -1658,6 +1664,32 @@ public class DashManifestParser extends DefaultHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void fillInClearKeyInformation(ArrayList<SchemeData> schemeDatas) {
|
||||||
|
// Find and remove ClearKey information.
|
||||||
|
@Nullable String clearKeyLicenseServerUrl = null;
|
||||||
|
for (int i = 0; i < schemeDatas.size(); i++) {
|
||||||
|
SchemeData schemeData = schemeDatas.get(i);
|
||||||
|
if (C.CLEARKEY_UUID.equals(schemeData.uuid) && schemeData.licenseServerUrl != null) {
|
||||||
|
clearKeyLicenseServerUrl = schemeData.licenseServerUrl;
|
||||||
|
schemeDatas.remove(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (clearKeyLicenseServerUrl == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Fill in the ClearKey information into the existing PSSH schema data if applicable.
|
||||||
|
for (int i = 0; i < schemeDatas.size(); i++) {
|
||||||
|
SchemeData schemeData = schemeDatas.get(i);
|
||||||
|
if (C.COMMON_PSSH_UUID.equals(schemeData.uuid) && schemeData.licenseServerUrl == null) {
|
||||||
|
schemeDatas.set(
|
||||||
|
i,
|
||||||
|
new SchemeData(
|
||||||
|
C.CLEARKEY_UUID, clearKeyLicenseServerUrl, schemeData.mimeType, schemeData.data));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Derives a sample mimeType from a container mimeType and codecs attribute.
|
* Derives a sample mimeType from a container mimeType and codecs attribute.
|
||||||
*
|
*
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.source.dash;
|
package com.google.android.exoplayer2.source.dash;
|
||||||
|
|
||||||
|
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
@ -22,9 +23,13 @@ import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.MediaItem;
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
|
import com.google.android.exoplayer2.analytics.PlayerId;
|
||||||
|
import com.google.android.exoplayer2.robolectric.RobolectricUtil;
|
||||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
|
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
import java.io.IOException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
|
@ -82,4 +87,53 @@ public class DefaultMediaSourceFactoryTest {
|
||||||
|
|
||||||
assertThat(supportedTypes).asList().containsExactly(C.CONTENT_TYPE_OTHER, C.CONTENT_TYPE_DASH);
|
assertThat(supportedTypes).asList().containsExactly(C.CONTENT_TYPE_OTHER, C.CONTENT_TYPE_DASH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createMediaSource_withSetDataSourceFactory_usesDataSourceFactory() throws Exception {
|
||||||
|
FakeDataSource fakeDataSource = new FakeDataSource();
|
||||||
|
DefaultMediaSourceFactory defaultMediaSourceFactory =
|
||||||
|
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext())
|
||||||
|
.setDataSourceFactory(() -> fakeDataSource);
|
||||||
|
|
||||||
|
prepareDashUrlAndWaitForPrepareError(defaultMediaSourceFactory);
|
||||||
|
|
||||||
|
assertThat(fakeDataSource.getAndClearOpenedDataSpecs()).asList().isNotEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
createMediaSource_usingDefaultDataSourceFactoryAndSetDataSourceFactory_usesUpdatesDataSourceFactory()
|
||||||
|
throws Exception {
|
||||||
|
FakeDataSource fakeDataSource = new FakeDataSource();
|
||||||
|
DefaultMediaSourceFactory defaultMediaSourceFactory =
|
||||||
|
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
|
||||||
|
|
||||||
|
// Use default DataSource.Factory first.
|
||||||
|
prepareDashUrlAndWaitForPrepareError(defaultMediaSourceFactory);
|
||||||
|
defaultMediaSourceFactory.setDataSourceFactory(() -> fakeDataSource);
|
||||||
|
prepareDashUrlAndWaitForPrepareError(defaultMediaSourceFactory);
|
||||||
|
|
||||||
|
assertThat(fakeDataSource.getAndClearOpenedDataSpecs()).asList().isNotEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void prepareDashUrlAndWaitForPrepareError(
|
||||||
|
DefaultMediaSourceFactory defaultMediaSourceFactory) throws Exception {
|
||||||
|
MediaSource mediaSource =
|
||||||
|
defaultMediaSourceFactory.createMediaSource(MediaItem.fromUri(URI_MEDIA + "/file.mpd"));
|
||||||
|
getInstrumentation()
|
||||||
|
.runOnMainSync(
|
||||||
|
() ->
|
||||||
|
mediaSource.prepareSource(
|
||||||
|
(source, timeline) -> {}, /* mediaTransferListener= */ null, PlayerId.UNSET));
|
||||||
|
// We don't expect this to prepare successfully.
|
||||||
|
RobolectricUtil.runMainLooperUntil(
|
||||||
|
() -> {
|
||||||
|
try {
|
||||||
|
mediaSource.maybeThrowSourceInfoRefreshError();
|
||||||
|
return false;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
|
import com.google.android.exoplayer2.drm.DrmInitData;
|
||||||
import com.google.android.exoplayer2.metadata.emsg.EventMessage;
|
import com.google.android.exoplayer2.metadata.emsg.EventMessage;
|
||||||
import com.google.android.exoplayer2.source.dash.manifest.Representation.MultiSegmentRepresentation;
|
import com.google.android.exoplayer2.source.dash.manifest.Representation.MultiSegmentRepresentation;
|
||||||
import com.google.android.exoplayer2.source.dash.manifest.Representation.SingleSegmentRepresentation;
|
import com.google.android.exoplayer2.source.dash.manifest.Representation.SingleSegmentRepresentation;
|
||||||
|
|
@ -79,6 +80,8 @@ public class DashManifestParserTest {
|
||||||
"media/mpd/sample_mpd_service_description_low_latency_only_playback_rates";
|
"media/mpd/sample_mpd_service_description_low_latency_only_playback_rates";
|
||||||
private static final String SAMPLE_MPD_SERVICE_DESCRIPTION_LOW_LATENCY_ONLY_TARGET_LATENCY =
|
private static final String SAMPLE_MPD_SERVICE_DESCRIPTION_LOW_LATENCY_ONLY_TARGET_LATENCY =
|
||||||
"media/mpd/sample_mpd_service_description_low_latency_only_target_latency";
|
"media/mpd/sample_mpd_service_description_low_latency_only_target_latency";
|
||||||
|
private static final String SAMPLE_MPD_CLEAR_KEY_LICENSE_URL =
|
||||||
|
"media/mpd/sample_mpd_clear_key_license_url";
|
||||||
|
|
||||||
private static final String NEXT_TAG_NAME = "Next";
|
private static final String NEXT_TAG_NAME = "Next";
|
||||||
private static final String NEXT_TAG = "<" + NEXT_TAG_NAME + "/>";
|
private static final String NEXT_TAG = "<" + NEXT_TAG_NAME + "/>";
|
||||||
|
|
@ -880,6 +883,37 @@ public class DashManifestParserTest {
|
||||||
assertThat(manifest.serviceDescription).isNull();
|
assertThat(manifest.serviceDescription).isNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void contentProtections_withClearKeyLicenseUrl() throws IOException {
|
||||||
|
DashManifestParser parser = new DashManifestParser();
|
||||||
|
|
||||||
|
DashManifest manifest =
|
||||||
|
parser.parse(
|
||||||
|
Uri.parse("https://example.com/test.mpd"),
|
||||||
|
TestUtil.getInputStream(
|
||||||
|
ApplicationProvider.getApplicationContext(), SAMPLE_MPD_CLEAR_KEY_LICENSE_URL));
|
||||||
|
|
||||||
|
assertThat(manifest.getPeriodCount()).isEqualTo(1);
|
||||||
|
Period period = manifest.getPeriod(0);
|
||||||
|
assertThat(period.adaptationSets).hasSize(2);
|
||||||
|
AdaptationSet adaptationSet0 = period.adaptationSets.get(0);
|
||||||
|
AdaptationSet adaptationSet1 = period.adaptationSets.get(1);
|
||||||
|
assertThat(adaptationSet0.representations).hasSize(1);
|
||||||
|
assertThat(adaptationSet1.representations).hasSize(1);
|
||||||
|
Representation representation0 = adaptationSet0.representations.get(0);
|
||||||
|
Representation representation1 = adaptationSet1.representations.get(0);
|
||||||
|
assertThat(representation0.format.drmInitData.schemeType).isEqualTo("cenc");
|
||||||
|
assertThat(representation1.format.drmInitData.schemeType).isEqualTo("cenc");
|
||||||
|
assertThat(representation0.format.drmInitData.schemeDataCount).isEqualTo(1);
|
||||||
|
assertThat(representation1.format.drmInitData.schemeDataCount).isEqualTo(1);
|
||||||
|
DrmInitData.SchemeData schemeData0 = representation0.format.drmInitData.get(0);
|
||||||
|
DrmInitData.SchemeData schemeData1 = representation1.format.drmInitData.get(0);
|
||||||
|
assertThat(schemeData0.uuid).isEqualTo(C.CLEARKEY_UUID);
|
||||||
|
assertThat(schemeData1.uuid).isEqualTo(C.CLEARKEY_UUID);
|
||||||
|
assertThat(schemeData0.licenseServerUrl).isEqualTo("https://testserver1.test/AcquireLicense");
|
||||||
|
assertThat(schemeData1.licenseServerUrl).isEqualTo("https://testserver2.test/AcquireLicense");
|
||||||
|
}
|
||||||
|
|
||||||
private static List<Descriptor> buildCea608AccessibilityDescriptors(String value) {
|
private static List<Descriptor> buildCea608AccessibilityDescriptors(String value) {
|
||||||
return Collections.singletonList(new Descriptor("urn:scte:dash:cc:cea-608:2015", value, null));
|
return Collections.singletonList(new Descriptor("urn:scte:dash:cc:cea-608:2015", value, null));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ import com.google.android.exoplayer2.video.DolbyVisionConfig;
|
||||||
import com.google.android.exoplayer2.video.HevcConfig;
|
import com.google.android.exoplayer2.video.HevcConfig;
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.common.primitives.Ints;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -1303,7 +1304,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (esdsData != null) {
|
if (esdsData != null) {
|
||||||
formatBuilder.setAverageBitrate(esdsData.bitrate).setPeakBitrate(esdsData.peakBitrate);
|
formatBuilder
|
||||||
|
.setAverageBitrate(Ints.saturatedCast(esdsData.bitrate))
|
||||||
|
.setPeakBitrate(Ints.saturatedCast(esdsData.peakBitrate));
|
||||||
}
|
}
|
||||||
|
|
||||||
out.format = formatBuilder.build();
|
out.format = formatBuilder.build();
|
||||||
|
|
@ -1609,7 +1612,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
.setLanguage(language);
|
.setLanguage(language);
|
||||||
|
|
||||||
if (esdsData != null) {
|
if (esdsData != null) {
|
||||||
formatBuilder.setAverageBitrate(esdsData.bitrate).setPeakBitrate(esdsData.peakBitrate);
|
formatBuilder
|
||||||
|
.setAverageBitrate(Ints.saturatedCast(esdsData.bitrate))
|
||||||
|
.setPeakBitrate(Ints.saturatedCast(esdsData.peakBitrate));
|
||||||
}
|
}
|
||||||
|
|
||||||
out.format = formatBuilder.build();
|
out.format = formatBuilder.build();
|
||||||
|
|
@ -1659,7 +1664,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
parent.skipBytes(2);
|
parent.skipBytes(2);
|
||||||
}
|
}
|
||||||
if ((flags & 0x40 /* URL_Flag */) != 0) {
|
if ((flags & 0x40 /* URL_Flag */) != 0) {
|
||||||
parent.skipBytes(parent.readUnsignedShort());
|
parent.skipBytes(parent.readUnsignedByte());
|
||||||
}
|
}
|
||||||
if ((flags & 0x20 /* OCRstreamFlag */) != 0) {
|
if ((flags & 0x20 /* OCRstreamFlag */) != 0) {
|
||||||
parent.skipBytes(2);
|
parent.skipBytes(2);
|
||||||
|
|
@ -1683,8 +1688,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
}
|
}
|
||||||
|
|
||||||
parent.skipBytes(4);
|
parent.skipBytes(4);
|
||||||
int peakBitrate = parent.readUnsignedIntToInt();
|
long peakBitrate = parent.readUnsignedInt();
|
||||||
int bitrate = parent.readUnsignedIntToInt();
|
long bitrate = parent.readUnsignedInt();
|
||||||
|
|
||||||
// Start of the DecoderSpecificInfo.
|
// Start of the DecoderSpecificInfo.
|
||||||
parent.skipBytes(1); // DecoderSpecificInfo tag
|
parent.skipBytes(1); // DecoderSpecificInfo tag
|
||||||
|
|
@ -1943,14 +1948,14 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
||||||
private static final class EsdsData {
|
private static final class EsdsData {
|
||||||
private final @NullableType String mimeType;
|
private final @NullableType String mimeType;
|
||||||
private final byte @NullableType [] initializationData;
|
private final byte @NullableType [] initializationData;
|
||||||
private final int bitrate;
|
private final long bitrate;
|
||||||
private final int peakBitrate;
|
private final long peakBitrate;
|
||||||
|
|
||||||
public EsdsData(
|
public EsdsData(
|
||||||
@NullableType String mimeType,
|
@NullableType String mimeType,
|
||||||
byte @NullableType [] initializationData,
|
byte @NullableType [] initializationData,
|
||||||
int bitrate,
|
long bitrate,
|
||||||
int peakBitrate) {
|
long peakBitrate) {
|
||||||
this.mimeType = mimeType;
|
this.mimeType = mimeType;
|
||||||
this.initializationData = initializationData;
|
this.initializationData = initializationData;
|
||||||
this.bitrate = bitrate;
|
this.bitrate = bitrate;
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.util;
|
||||||
import static java.lang.Math.min;
|
import static java.lang.Math.min;
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
|
@ -781,40 +782,105 @@ public final class NalUnitUtil {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skips any short term reference picture sets contained in a SPS.
|
||||||
|
*
|
||||||
|
* <p>Note: The st_ref_pic_set parsing in this method is simplified for the case where they're
|
||||||
|
* contained in a SPS, and would need generalizing for use elsewhere.
|
||||||
|
*/
|
||||||
private static void skipShortTermReferencePictureSets(ParsableNalUnitBitArray bitArray) {
|
private static void skipShortTermReferencePictureSets(ParsableNalUnitBitArray bitArray) {
|
||||||
int numShortTermRefPicSets = bitArray.readUnsignedExpGolombCodedInt();
|
int numShortTermRefPicSets = bitArray.readUnsignedExpGolombCodedInt();
|
||||||
boolean interRefPicSetPredictionFlag = false;
|
// As this method applies in a SPS, each short term reference picture set only accesses data
|
||||||
|
// from the previous one. This is because RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1), and
|
||||||
|
// delta_idx_minus1 is always zero in a SPS. Hence we just keep track of variables from the
|
||||||
|
// previous one as we iterate.
|
||||||
|
int previousNumNegativePics = C.INDEX_UNSET;
|
||||||
|
int previousNumPositivePics = C.INDEX_UNSET;
|
||||||
|
int[] previousDeltaPocS0 = new int[0];
|
||||||
|
int[] previousDeltaPocS1 = new int[0];
|
||||||
|
for (int stRpsIdx = 0; stRpsIdx < numShortTermRefPicSets; stRpsIdx++) {
|
||||||
int numNegativePics;
|
int numNegativePics;
|
||||||
int numPositivePics;
|
int numPositivePics;
|
||||||
// As this method applies in a SPS, the only element of NumDeltaPocs accessed is the previous
|
int[] deltaPocS0;
|
||||||
// one, so we just keep track of that rather than storing the whole array.
|
int[] deltaPocS1;
|
||||||
// RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1) and delta_idx_minus1 is always zero in SPS.
|
|
||||||
int previousNumDeltaPocs = 0;
|
boolean interRefPicSetPredictionFlag = stRpsIdx != 0 && bitArray.readBit();
|
||||||
for (int stRpsIdx = 0; stRpsIdx < numShortTermRefPicSets; stRpsIdx++) {
|
|
||||||
if (stRpsIdx != 0) {
|
|
||||||
interRefPicSetPredictionFlag = bitArray.readBit();
|
|
||||||
}
|
|
||||||
if (interRefPicSetPredictionFlag) {
|
if (interRefPicSetPredictionFlag) {
|
||||||
bitArray.skipBit(); // delta_rps_sign
|
int previousNumDeltaPocs = previousNumNegativePics + previousNumPositivePics;
|
||||||
bitArray.readUnsignedExpGolombCodedInt(); // abs_delta_rps_minus1
|
|
||||||
|
int deltaRpsSign = bitArray.readBit() ? 1 : 0;
|
||||||
|
int absDeltaRps = bitArray.readUnsignedExpGolombCodedInt() + 1;
|
||||||
|
int deltaRps = (1 - 2 * deltaRpsSign) * absDeltaRps;
|
||||||
|
|
||||||
|
boolean[] useDeltaFlags = new boolean[previousNumDeltaPocs + 1];
|
||||||
for (int j = 0; j <= previousNumDeltaPocs; j++) {
|
for (int j = 0; j <= previousNumDeltaPocs; j++) {
|
||||||
if (!bitArray.readBit()) { // used_by_curr_pic_flag[j]
|
if (!bitArray.readBit()) { // used_by_curr_pic_flag[j]
|
||||||
bitArray.skipBit(); // use_delta_flag[j]
|
useDeltaFlags[j] = bitArray.readBit();
|
||||||
|
} else {
|
||||||
|
// When use_delta_flag[j] is not present, its value is 1.
|
||||||
|
useDeltaFlags[j] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Derive numNegativePics, numPositivePics, deltaPocS0 and deltaPocS1 as per Rec. ITU-T
|
||||||
|
// H.265 v6 (06/2019) Section 7.4.8
|
||||||
|
int i = 0;
|
||||||
|
deltaPocS0 = new int[previousNumDeltaPocs + 1];
|
||||||
|
deltaPocS1 = new int[previousNumDeltaPocs + 1];
|
||||||
|
for (int j = previousNumPositivePics - 1; j >= 0; j--) {
|
||||||
|
int dPoc = previousDeltaPocS1[j] + deltaRps;
|
||||||
|
if (dPoc < 0 && useDeltaFlags[previousNumNegativePics + j]) {
|
||||||
|
deltaPocS0[i++] = dPoc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (deltaRps < 0 && useDeltaFlags[previousNumDeltaPocs]) {
|
||||||
|
deltaPocS0[i++] = deltaRps;
|
||||||
|
}
|
||||||
|
for (int j = 0; j < previousNumNegativePics; j++) {
|
||||||
|
int dPoc = previousDeltaPocS0[j] + deltaRps;
|
||||||
|
if (dPoc < 0 && useDeltaFlags[j]) {
|
||||||
|
deltaPocS0[i++] = dPoc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
numNegativePics = i;
|
||||||
|
deltaPocS0 = Arrays.copyOf(deltaPocS0, numNegativePics);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
for (int j = previousNumNegativePics - 1; j >= 0; j--) {
|
||||||
|
int dPoc = previousDeltaPocS0[j] + deltaRps;
|
||||||
|
if (dPoc > 0 && useDeltaFlags[j]) {
|
||||||
|
deltaPocS1[i++] = dPoc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (deltaRps > 0 && useDeltaFlags[previousNumDeltaPocs]) {
|
||||||
|
deltaPocS1[i++] = deltaRps;
|
||||||
|
}
|
||||||
|
for (int j = 0; j < previousNumPositivePics; j++) {
|
||||||
|
int dPoc = previousDeltaPocS1[j] + deltaRps;
|
||||||
|
if (dPoc > 0 && useDeltaFlags[previousNumNegativePics + j]) {
|
||||||
|
deltaPocS1[i++] = dPoc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
numPositivePics = i;
|
||||||
|
deltaPocS1 = Arrays.copyOf(deltaPocS1, numPositivePics);
|
||||||
} else {
|
} else {
|
||||||
numNegativePics = bitArray.readUnsignedExpGolombCodedInt();
|
numNegativePics = bitArray.readUnsignedExpGolombCodedInt();
|
||||||
numPositivePics = bitArray.readUnsignedExpGolombCodedInt();
|
numPositivePics = bitArray.readUnsignedExpGolombCodedInt();
|
||||||
previousNumDeltaPocs = numNegativePics + numPositivePics;
|
deltaPocS0 = new int[numNegativePics];
|
||||||
for (int i = 0; i < numNegativePics; i++) {
|
for (int i = 0; i < numNegativePics; i++) {
|
||||||
bitArray.readUnsignedExpGolombCodedInt(); // delta_poc_s0_minus1[i]
|
deltaPocS0[i] = bitArray.readUnsignedExpGolombCodedInt() + 1;
|
||||||
bitArray.skipBit(); // used_by_curr_pic_s0_flag[i]
|
bitArray.skipBit(); // used_by_curr_pic_s0_flag[i]
|
||||||
}
|
}
|
||||||
|
deltaPocS1 = new int[numPositivePics];
|
||||||
for (int i = 0; i < numPositivePics; i++) {
|
for (int i = 0; i < numPositivePics; i++) {
|
||||||
bitArray.readUnsignedExpGolombCodedInt(); // delta_poc_s1_minus1[i]
|
deltaPocS1[i] = bitArray.readUnsignedExpGolombCodedInt() + 1;
|
||||||
bitArray.skipBit(); // used_by_curr_pic_s1_flag[i]
|
bitArray.skipBit(); // used_by_curr_pic_s1_flag[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
previousNumNegativePics = numNegativePics;
|
||||||
|
previousNumPositivePics = numPositivePics;
|
||||||
|
previousDeltaPocS0 = deltaPocS0;
|
||||||
|
previousDeltaPocS1 = deltaPocS1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,15 @@ public final class FragmentedMp4ExtractorTest {
|
||||||
simulationConfig);
|
simulationConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** https://github.com/google/ExoPlayer/issues/10381 */
|
||||||
|
@Test
|
||||||
|
public void sampleWithLargeBitrates() throws Exception {
|
||||||
|
ExtractorAsserts.assertBehavior(
|
||||||
|
getExtractorFactory(ImmutableList.of()),
|
||||||
|
"media/mp4/sample_fragmented_large_bitrates.mp4",
|
||||||
|
simulationConfig);
|
||||||
|
}
|
||||||
|
|
||||||
private static ExtractorFactory getExtractorFactory(final List<Format> closedCaptionFormats) {
|
private static ExtractorFactory getExtractorFactory(final List<Format> closedCaptionFormats) {
|
||||||
return () ->
|
return () ->
|
||||||
new FragmentedMp4Extractor(
|
new FragmentedMp4Extractor(
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,32 @@ public final class NalUnitUtilTest {
|
||||||
assertDiscardToSpsMatchesExpected("FF00000001660000000167FF", "0000000167FF");
|
assertDiscardToSpsMatchesExpected("FF00000001660000000167FF", "0000000167FF");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Regression test for https://github.com/google/ExoPlayer/issues/10316. */
|
||||||
|
@Test
|
||||||
|
public void parseH265SpsNalUnitPayload_exoghi_10316() {
|
||||||
|
byte[] spsNalUnitPayload =
|
||||||
|
new byte[] {
|
||||||
|
1, 2, 32, 0, 0, 3, 0, -112, 0, 0, 3, 0, 0, 3, 0, -106, -96, 1, -32, 32, 2, 28, 77, -98,
|
||||||
|
87, -110, 66, -111, -123, 22, 74, -86, -53, -101, -98, -68, -28, 9, 119, -21, -103, 120,
|
||||||
|
-16, 22, -95, 34, 1, 54, -62, 0, 0, 7, -46, 0, 0, -69, -127, -12, 85, -17, 126, 0, -29,
|
||||||
|
-128, 28, 120, 1, -57, 0, 56, -15
|
||||||
|
};
|
||||||
|
|
||||||
|
NalUnitUtil.H265SpsData spsData =
|
||||||
|
NalUnitUtil.parseH265SpsNalUnitPayload(spsNalUnitPayload, 0, spsNalUnitPayload.length);
|
||||||
|
|
||||||
|
assertThat(spsData.constraintBytes).isEqualTo(new int[] {144, 0, 0, 0, 0, 0});
|
||||||
|
assertThat(spsData.generalLevelIdc).isEqualTo(150);
|
||||||
|
assertThat(spsData.generalProfileCompatibilityFlags).isEqualTo(4);
|
||||||
|
assertThat(spsData.generalProfileIdc).isEqualTo(2);
|
||||||
|
assertThat(spsData.generalProfileSpace).isEqualTo(0);
|
||||||
|
assertThat(spsData.generalTierFlag).isFalse();
|
||||||
|
assertThat(spsData.height).isEqualTo(2160);
|
||||||
|
assertThat(spsData.pixelWidthHeightRatio).isEqualTo(1);
|
||||||
|
assertThat(spsData.seqParameterSetId).isEqualTo(0);
|
||||||
|
assertThat(spsData.width).isEqualTo(3840);
|
||||||
|
}
|
||||||
|
|
||||||
private static byte[] buildTestData() {
|
private static byte[] buildTestData() {
|
||||||
byte[] data = new byte[20];
|
byte[] data = new byte[20];
|
||||||
for (int i = 0; i < data.length; i++) {
|
for (int i = 0; i < data.length; i++) {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.source.hls;
|
package com.google.android.exoplayer2.source.hls;
|
||||||
|
|
||||||
|
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
@ -22,9 +23,13 @@ import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.MediaItem;
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
|
import com.google.android.exoplayer2.analytics.PlayerId;
|
||||||
|
import com.google.android.exoplayer2.robolectric.RobolectricUtil;
|
||||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
|
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
import java.io.IOException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
|
@ -82,4 +87,53 @@ public class DefaultMediaSourceFactoryTest {
|
||||||
|
|
||||||
assertThat(supportedTypes).asList().containsExactly(C.CONTENT_TYPE_OTHER, C.CONTENT_TYPE_HLS);
|
assertThat(supportedTypes).asList().containsExactly(C.CONTENT_TYPE_OTHER, C.CONTENT_TYPE_HLS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createMediaSource_withSetDataSourceFactory_usesDataSourceFactory() throws Exception {
|
||||||
|
FakeDataSource fakeDataSource = new FakeDataSource();
|
||||||
|
DefaultMediaSourceFactory defaultMediaSourceFactory =
|
||||||
|
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext())
|
||||||
|
.setDataSourceFactory(() -> fakeDataSource);
|
||||||
|
|
||||||
|
prepareHlsUrlAndWaitForPrepareError(defaultMediaSourceFactory);
|
||||||
|
|
||||||
|
assertThat(fakeDataSource.getAndClearOpenedDataSpecs()).asList().isNotEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
createMediaSource_usingDefaultDataSourceFactoryAndSetDataSourceFactory_usesUpdatesDataSourceFactory()
|
||||||
|
throws Exception {
|
||||||
|
FakeDataSource fakeDataSource = new FakeDataSource();
|
||||||
|
DefaultMediaSourceFactory defaultMediaSourceFactory =
|
||||||
|
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
|
||||||
|
|
||||||
|
// Use default DataSource.Factory first.
|
||||||
|
prepareHlsUrlAndWaitForPrepareError(defaultMediaSourceFactory);
|
||||||
|
defaultMediaSourceFactory.setDataSourceFactory(() -> fakeDataSource);
|
||||||
|
prepareHlsUrlAndWaitForPrepareError(defaultMediaSourceFactory);
|
||||||
|
|
||||||
|
assertThat(fakeDataSource.getAndClearOpenedDataSpecs()).asList().isNotEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void prepareHlsUrlAndWaitForPrepareError(
|
||||||
|
DefaultMediaSourceFactory defaultMediaSourceFactory) throws Exception {
|
||||||
|
MediaSource mediaSource =
|
||||||
|
defaultMediaSourceFactory.createMediaSource(MediaItem.fromUri(URI_MEDIA + "/file.m3u8"));
|
||||||
|
getInstrumentation()
|
||||||
|
.runOnMainSync(
|
||||||
|
() ->
|
||||||
|
mediaSource.prepareSource(
|
||||||
|
(source, timeline) -> {}, /* mediaTransferListener= */ null, PlayerId.UNSET));
|
||||||
|
// We don't expect this to prepare successfully.
|
||||||
|
RobolectricUtil.runMainLooperUntil(
|
||||||
|
() -> {
|
||||||
|
try {
|
||||||
|
mediaSource.maybeThrowSourceInfoRefreshError();
|
||||||
|
return false;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.source.rtsp.reader;
|
package com.google.android.exoplayer2.source.rtsp.reader;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.util.Assertions.checkNotNull;
|
||||||
|
import static com.google.android.exoplayer2.util.Assertions.checkState;
|
||||||
import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
|
import static com.google.android.exoplayer2.util.Assertions.checkStateNotNull;
|
||||||
|
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
|
|
@ -51,6 +53,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
/** The combined size of a sample that is fragmented into multiple RTP packets. */
|
/** The combined size of a sample that is fragmented into multiple RTP packets. */
|
||||||
private int fragmentedSampleSizeBytes;
|
private int fragmentedSampleSizeBytes;
|
||||||
|
|
||||||
|
private long fragmentedSampleTimeUs;
|
||||||
|
|
||||||
private long startTimeOffsetUs;
|
private long startTimeOffsetUs;
|
||||||
/**
|
/**
|
||||||
* Whether the first packet of one VP8 frame is received. A VP8 frame can be split into two RTP
|
* Whether the first packet of one VP8 frame is received. A VP8 frame can be split into two RTP
|
||||||
|
|
@ -67,6 +71,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
firstReceivedTimestamp = C.TIME_UNSET;
|
firstReceivedTimestamp = C.TIME_UNSET;
|
||||||
previousSequenceNumber = C.INDEX_UNSET;
|
previousSequenceNumber = C.INDEX_UNSET;
|
||||||
fragmentedSampleSizeBytes = C.LENGTH_UNSET;
|
fragmentedSampleSizeBytes = C.LENGTH_UNSET;
|
||||||
|
fragmentedSampleTimeUs = C.TIME_UNSET;
|
||||||
// The start time offset must be 0 until the first seek.
|
// The start time offset must be 0 until the first seek.
|
||||||
startTimeOffsetUs = 0;
|
startTimeOffsetUs = 0;
|
||||||
gotFirstPacketOfVp8Frame = false;
|
gotFirstPacketOfVp8Frame = false;
|
||||||
|
|
@ -81,7 +86,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceivingFirstPacket(long timestamp, int sequenceNumber) {}
|
public void onReceivingFirstPacket(long timestamp, int sequenceNumber) {
|
||||||
|
checkState(firstReceivedTimestamp == C.TIME_UNSET);
|
||||||
|
firstReceivedTimestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void consume(
|
public void consume(
|
||||||
|
|
@ -113,21 +121,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
int fragmentSize = data.bytesLeft();
|
int fragmentSize = data.bytesLeft();
|
||||||
trackOutput.sampleData(data, fragmentSize);
|
trackOutput.sampleData(data, fragmentSize);
|
||||||
|
if (fragmentedSampleSizeBytes == C.LENGTH_UNSET) {
|
||||||
|
fragmentedSampleSizeBytes = fragmentSize;
|
||||||
|
} else {
|
||||||
fragmentedSampleSizeBytes += fragmentSize;
|
fragmentedSampleSizeBytes += fragmentSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
fragmentedSampleTimeUs = toSampleUs(startTimeOffsetUs, timestamp, firstReceivedTimestamp);
|
||||||
|
|
||||||
if (rtpMarker) {
|
if (rtpMarker) {
|
||||||
if (firstReceivedTimestamp == C.TIME_UNSET) {
|
outputSampleMetadataForFragmentedPackets();
|
||||||
firstReceivedTimestamp = timestamp;
|
|
||||||
}
|
|
||||||
long timeUs = toSampleUs(startTimeOffsetUs, timestamp, firstReceivedTimestamp);
|
|
||||||
trackOutput.sampleMetadata(
|
|
||||||
timeUs,
|
|
||||||
isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0,
|
|
||||||
fragmentedSampleSizeBytes,
|
|
||||||
/* offset= */ 0,
|
|
||||||
/* cryptoData= */ null);
|
|
||||||
fragmentedSampleSizeBytes = C.LENGTH_UNSET;
|
|
||||||
gotFirstPacketOfVp8Frame = false;
|
|
||||||
}
|
}
|
||||||
previousSequenceNumber = sequenceNumber;
|
previousSequenceNumber = sequenceNumber;
|
||||||
}
|
}
|
||||||
|
|
@ -147,18 +150,18 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
private boolean validateVp8Descriptor(ParsableByteArray payload, int packetSequenceNumber) {
|
private boolean validateVp8Descriptor(ParsableByteArray payload, int packetSequenceNumber) {
|
||||||
// VP8 Payload Descriptor is defined in RFC7741 Section 4.2.
|
// VP8 Payload Descriptor is defined in RFC7741 Section 4.2.
|
||||||
int header = payload.readUnsignedByte();
|
int header = payload.readUnsignedByte();
|
||||||
if (!gotFirstPacketOfVp8Frame) {
|
|
||||||
// TODO(b/198620566) Consider using ParsableBitArray.
|
// TODO(b/198620566) Consider using ParsableBitArray.
|
||||||
// For start of VP8 partition S=1 and PID=0 as per RFC7741 Section 4.2.
|
// For start of VP8 partition S=1 and PID=0 as per RFC7741 Section 4.2.
|
||||||
if ((header & 0x10) != 0x1 || (header & 0x07) != 0) {
|
if ((header & 0x10) == 0x10 && (header & 0x07) == 0) {
|
||||||
Log.w(TAG, "RTP packet is not the start of a new VP8 partition, skipping.");
|
if (gotFirstPacketOfVp8Frame && fragmentedSampleSizeBytes > 0) {
|
||||||
return false;
|
// Received new VP8 fragment, output data of previous fragment to decoder.
|
||||||
|
outputSampleMetadataForFragmentedPackets();
|
||||||
}
|
}
|
||||||
gotFirstPacketOfVp8Frame = true;
|
gotFirstPacketOfVp8Frame = true;
|
||||||
} else {
|
} else if (gotFirstPacketOfVp8Frame) {
|
||||||
// Check that this packet is in the sequence of the previous packet.
|
// Check that this packet is in the sequence of the previous packet.
|
||||||
int expectedSequenceNumber = RtpPacket.getNextSequenceNumber(previousSequenceNumber);
|
int expectedSequenceNumber = RtpPacket.getNextSequenceNumber(previousSequenceNumber);
|
||||||
if (packetSequenceNumber != expectedSequenceNumber) {
|
if (packetSequenceNumber < expectedSequenceNumber) {
|
||||||
Log.w(
|
Log.w(
|
||||||
TAG,
|
TAG,
|
||||||
Util.formatInvariant(
|
Util.formatInvariant(
|
||||||
|
|
@ -167,6 +170,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
expectedSequenceNumber, packetSequenceNumber));
|
expectedSequenceNumber, packetSequenceNumber));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "RTP packet is not the start of a new VP8 partition, skipping.");
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if optional X header is present.
|
// Check if optional X header is present.
|
||||||
|
|
@ -195,6 +201,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outputs sample metadata of the received fragmented packets.
|
||||||
|
*
|
||||||
|
* <p>Call this method only after receiving an end of a VP8 partition.
|
||||||
|
*/
|
||||||
|
private void outputSampleMetadataForFragmentedPackets() {
|
||||||
|
checkNotNull(trackOutput)
|
||||||
|
.sampleMetadata(
|
||||||
|
fragmentedSampleTimeUs,
|
||||||
|
isKeyFrame ? C.BUFFER_FLAG_KEY_FRAME : 0,
|
||||||
|
fragmentedSampleSizeBytes,
|
||||||
|
/* offset= */ 0,
|
||||||
|
/* cryptoData= */ null);
|
||||||
|
fragmentedSampleSizeBytes = 0;
|
||||||
|
fragmentedSampleTimeUs = C.TIME_UNSET;
|
||||||
|
gotFirstPacketOfVp8Frame = false;
|
||||||
|
}
|
||||||
|
|
||||||
private static long toSampleUs(
|
private static long toSampleUs(
|
||||||
long startTimeOffsetUs, long rtpTimestamp, long firstReceivedRtpTimestamp) {
|
long startTimeOffsetUs, long rtpTimestamp, long firstReceivedRtpTimestamp) {
|
||||||
return startTimeOffsetUs
|
return startTimeOffsetUs
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,203 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.google.android.exoplayer2.source.rtsp.reader;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.util.Util.getBytesFromHexString;
|
||||||
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
|
import com.google.android.exoplayer2.Format;
|
||||||
|
import com.google.android.exoplayer2.source.rtsp.RtpPacket;
|
||||||
|
import com.google.android.exoplayer2.source.rtsp.RtpPayloadFormat;
|
||||||
|
import com.google.android.exoplayer2.testutil.FakeExtractorOutput;
|
||||||
|
import com.google.android.exoplayer2.testutil.FakeTrackOutput;
|
||||||
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||||
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.primitives.Bytes;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/** Unit test for {@link RtpVp8Reader}. */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public final class RtpVp8ReaderTest {
|
||||||
|
|
||||||
|
/** VP9 uses a 90 KHz media clock (RFC7741 Section 4.1). */
|
||||||
|
private static final long MEDIA_CLOCK_FREQUENCY = 90_000;
|
||||||
|
|
||||||
|
private static final byte[] PARTITION_1 = getBytesFromHexString("000102030405060708090A0B0C0D0E");
|
||||||
|
// 000102030405060708090A
|
||||||
|
private static final byte[] PARTITION_1_FRAGMENT_1 =
|
||||||
|
Arrays.copyOf(PARTITION_1, /* newLength= */ 11);
|
||||||
|
// 0B0C0D0E
|
||||||
|
private static final byte[] PARTITION_1_FRAGMENT_2 =
|
||||||
|
Arrays.copyOfRange(PARTITION_1, /* from= */ 11, /* to= */ 15);
|
||||||
|
private static final long PARTITION_1_RTP_TIMESTAMP = 2599168056L;
|
||||||
|
private static final RtpPacket PACKET_PARTITION_1_FRAGMENT_1 =
|
||||||
|
new RtpPacket.Builder()
|
||||||
|
.setTimestamp(PARTITION_1_RTP_TIMESTAMP)
|
||||||
|
.setSequenceNumber(40289)
|
||||||
|
.setMarker(false)
|
||||||
|
.setPayloadData(Bytes.concat(getBytesFromHexString("10"), PARTITION_1_FRAGMENT_1))
|
||||||
|
.build();
|
||||||
|
private static final RtpPacket PACKET_PARTITION_1_FRAGMENT_2 =
|
||||||
|
new RtpPacket.Builder()
|
||||||
|
.setTimestamp(PARTITION_1_RTP_TIMESTAMP)
|
||||||
|
.setSequenceNumber(40290)
|
||||||
|
.setMarker(false)
|
||||||
|
.setPayloadData(Bytes.concat(getBytesFromHexString("00"), PARTITION_1_FRAGMENT_2))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
private static final byte[] PARTITION_2 = getBytesFromHexString("0D0C0B0A09080706050403020100");
|
||||||
|
// 0D0C0B0A090807060504
|
||||||
|
private static final byte[] PARTITION_2_FRAGMENT_1 =
|
||||||
|
Arrays.copyOf(PARTITION_2, /* newLength= */ 10);
|
||||||
|
// 03020100
|
||||||
|
private static final byte[] PARTITION_2_FRAGMENT_2 =
|
||||||
|
Arrays.copyOfRange(PARTITION_2, /* from= */ 10, /* to= */ 14);
|
||||||
|
private static final long PARTITION_2_RTP_TIMESTAMP = 2599168344L;
|
||||||
|
private static final RtpPacket PACKET_PARTITION_2_FRAGMENT_1 =
|
||||||
|
new RtpPacket.Builder()
|
||||||
|
.setTimestamp(PARTITION_2_RTP_TIMESTAMP)
|
||||||
|
.setSequenceNumber(40291)
|
||||||
|
.setMarker(false)
|
||||||
|
.setPayloadData(Bytes.concat(getBytesFromHexString("10"), PARTITION_2_FRAGMENT_1))
|
||||||
|
.build();
|
||||||
|
private static final RtpPacket PACKET_PARTITION_2_FRAGMENT_2 =
|
||||||
|
new RtpPacket.Builder()
|
||||||
|
.setTimestamp(PARTITION_2_RTP_TIMESTAMP)
|
||||||
|
.setSequenceNumber(40292)
|
||||||
|
.setMarker(true)
|
||||||
|
.setPayloadData(
|
||||||
|
Bytes.concat(
|
||||||
|
getBytesFromHexString("80"),
|
||||||
|
// Optional header.
|
||||||
|
getBytesFromHexString("D6AA953961"),
|
||||||
|
PARTITION_2_FRAGMENT_2))
|
||||||
|
.build();
|
||||||
|
private static final long PARTITION_2_PRESENTATION_TIMESTAMP_US =
|
||||||
|
Util.scaleLargeTimestamp(
|
||||||
|
(PARTITION_2_RTP_TIMESTAMP - PARTITION_1_RTP_TIMESTAMP),
|
||||||
|
/* multiplier= */ C.MICROS_PER_SECOND,
|
||||||
|
/* divisor= */ MEDIA_CLOCK_FREQUENCY);
|
||||||
|
|
||||||
|
private FakeExtractorOutput extractorOutput;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() {
|
||||||
|
extractorOutput =
|
||||||
|
new FakeExtractorOutput(
|
||||||
|
(id, type) -> new FakeTrackOutput(/* deduplicateConsecutiveFormats= */ true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void consume_validPackets() {
|
||||||
|
RtpVp8Reader vp8Reader = createVp8Reader();
|
||||||
|
|
||||||
|
vp8Reader.createTracks(extractorOutput, /* trackId= */ 0);
|
||||||
|
vp8Reader.onReceivingFirstPacket(
|
||||||
|
PACKET_PARTITION_1_FRAGMENT_1.timestamp, PACKET_PARTITION_1_FRAGMENT_1.sequenceNumber);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_1_FRAGMENT_1);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_1_FRAGMENT_2);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_2_FRAGMENT_1);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_2_FRAGMENT_2);
|
||||||
|
|
||||||
|
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
|
||||||
|
assertThat(trackOutput.getSampleCount()).isEqualTo(2);
|
||||||
|
assertThat(trackOutput.getSampleData(0)).isEqualTo(PARTITION_1);
|
||||||
|
assertThat(trackOutput.getSampleTimeUs(0)).isEqualTo(0);
|
||||||
|
assertThat(trackOutput.getSampleData(1)).isEqualTo(PARTITION_2);
|
||||||
|
assertThat(trackOutput.getSampleTimeUs(1)).isEqualTo(PARTITION_2_PRESENTATION_TIMESTAMP_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void consume_fragmentedFrameMissingFirstFragment() {
|
||||||
|
RtpVp8Reader vp8Reader = createVp8Reader();
|
||||||
|
|
||||||
|
vp8Reader.createTracks(extractorOutput, /* trackId= */ 0);
|
||||||
|
// First packet timing information is transmitted over RTSP, not RTP.
|
||||||
|
vp8Reader.onReceivingFirstPacket(
|
||||||
|
PACKET_PARTITION_1_FRAGMENT_1.timestamp, PACKET_PARTITION_1_FRAGMENT_1.sequenceNumber);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_1_FRAGMENT_2);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_2_FRAGMENT_1);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_2_FRAGMENT_2);
|
||||||
|
|
||||||
|
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
|
||||||
|
assertThat(trackOutput.getSampleCount()).isEqualTo(1);
|
||||||
|
assertThat(trackOutput.getSampleData(0)).isEqualTo(PARTITION_2);
|
||||||
|
assertThat(trackOutput.getSampleTimeUs(0)).isEqualTo(PARTITION_2_PRESENTATION_TIMESTAMP_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void consume_fragmentedFrameMissingBoundaryFragment() {
|
||||||
|
RtpVp8Reader vp8Reader = createVp8Reader();
|
||||||
|
|
||||||
|
vp8Reader.createTracks(extractorOutput, /* trackId= */ 0);
|
||||||
|
vp8Reader.onReceivingFirstPacket(
|
||||||
|
PACKET_PARTITION_1_FRAGMENT_1.timestamp, PACKET_PARTITION_1_FRAGMENT_1.sequenceNumber);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_1_FRAGMENT_1);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_2_FRAGMENT_1);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_2_FRAGMENT_2);
|
||||||
|
|
||||||
|
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
|
||||||
|
assertThat(trackOutput.getSampleCount()).isEqualTo(2);
|
||||||
|
assertThat(trackOutput.getSampleData(0)).isEqualTo(PARTITION_1_FRAGMENT_1);
|
||||||
|
assertThat(trackOutput.getSampleTimeUs(0)).isEqualTo(0);
|
||||||
|
assertThat(trackOutput.getSampleData(1)).isEqualTo(PARTITION_2);
|
||||||
|
assertThat(trackOutput.getSampleTimeUs(1)).isEqualTo(PARTITION_2_PRESENTATION_TIMESTAMP_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void consume_outOfOrderFragmentedFrame() {
|
||||||
|
RtpVp8Reader vp8Reader = createVp8Reader();
|
||||||
|
|
||||||
|
vp8Reader.createTracks(extractorOutput, /* trackId= */ 0);
|
||||||
|
vp8Reader.onReceivingFirstPacket(
|
||||||
|
PACKET_PARTITION_1_FRAGMENT_1.timestamp, PACKET_PARTITION_1_FRAGMENT_1.sequenceNumber);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_1_FRAGMENT_1);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_2_FRAGMENT_1);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_1_FRAGMENT_2);
|
||||||
|
consume(vp8Reader, PACKET_PARTITION_2_FRAGMENT_2);
|
||||||
|
|
||||||
|
FakeTrackOutput trackOutput = extractorOutput.trackOutputs.get(0);
|
||||||
|
assertThat(trackOutput.getSampleCount()).isEqualTo(2);
|
||||||
|
assertThat(trackOutput.getSampleData(0)).isEqualTo(PARTITION_1_FRAGMENT_1);
|
||||||
|
assertThat(trackOutput.getSampleTimeUs(0)).isEqualTo(0);
|
||||||
|
assertThat(trackOutput.getSampleData(1)).isEqualTo(PARTITION_2);
|
||||||
|
assertThat(trackOutput.getSampleTimeUs(1)).isEqualTo(PARTITION_2_PRESENTATION_TIMESTAMP_US);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RtpVp8Reader createVp8Reader() {
|
||||||
|
return new RtpVp8Reader(
|
||||||
|
new RtpPayloadFormat(
|
||||||
|
new Format.Builder().setSampleMimeType(MimeTypes.VIDEO_VP8).build(),
|
||||||
|
/* rtpPayloadType= */ 96,
|
||||||
|
/* clockRate= */ (int) MEDIA_CLOCK_FREQUENCY,
|
||||||
|
/* fmtpParameters= */ ImmutableMap.of()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void consume(RtpVp8Reader vp8Reader, RtpPacket rtpPacket) {
|
||||||
|
vp8Reader.consume(
|
||||||
|
new ParsableByteArray(rtpPacket.payloadData),
|
||||||
|
rtpPacket.timestamp,
|
||||||
|
rtpPacket.sequenceNumber,
|
||||||
|
rtpPacket.marker);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,6 +29,7 @@ dependencies {
|
||||||
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion
|
compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion
|
||||||
compileOnly 'org.jetbrains.kotlin:kotlin-annotations-jvm:' + kotlinAnnotationsVersion
|
compileOnly 'org.jetbrains.kotlin:kotlin-annotations-jvm:' + kotlinAnnotationsVersion
|
||||||
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
|
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
|
||||||
|
testImplementation project(modulePrefix + 'robolectricutils')
|
||||||
testImplementation project(modulePrefix + 'testutils')
|
testImplementation project(modulePrefix + 'testutils')
|
||||||
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
|
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.source.smoothstreaming;
|
package com.google.android.exoplayer2.source.smoothstreaming;
|
||||||
|
|
||||||
|
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
@ -22,9 +23,13 @@ import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
import com.google.android.exoplayer2.C;
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.MediaItem;
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
|
import com.google.android.exoplayer2.analytics.PlayerId;
|
||||||
|
import com.google.android.exoplayer2.robolectric.RobolectricUtil;
|
||||||
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
import com.google.android.exoplayer2.source.DefaultMediaSourceFactory;
|
||||||
import com.google.android.exoplayer2.source.MediaSource;
|
import com.google.android.exoplayer2.source.MediaSource;
|
||||||
|
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
import java.io.IOException;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
|
@ -93,4 +98,53 @@ public class DefaultMediaSourceFactoryTest {
|
||||||
|
|
||||||
assertThat(supportedTypes).asList().containsExactly(C.CONTENT_TYPE_OTHER, C.CONTENT_TYPE_SS);
|
assertThat(supportedTypes).asList().containsExactly(C.CONTENT_TYPE_OTHER, C.CONTENT_TYPE_SS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createMediaSource_withSetDataSourceFactory_usesDataSourceFactory() throws Exception {
|
||||||
|
FakeDataSource fakeDataSource = new FakeDataSource();
|
||||||
|
DefaultMediaSourceFactory defaultMediaSourceFactory =
|
||||||
|
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext())
|
||||||
|
.setDataSourceFactory(() -> fakeDataSource);
|
||||||
|
|
||||||
|
prepareSsUrlAndWaitForPrepareError(defaultMediaSourceFactory);
|
||||||
|
|
||||||
|
assertThat(fakeDataSource.getAndClearOpenedDataSpecs()).asList().isNotEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void
|
||||||
|
createMediaSource_usingDefaultDataSourceFactoryAndSetDataSourceFactory_usesUpdatesDataSourceFactory()
|
||||||
|
throws Exception {
|
||||||
|
FakeDataSource fakeDataSource = new FakeDataSource();
|
||||||
|
DefaultMediaSourceFactory defaultMediaSourceFactory =
|
||||||
|
new DefaultMediaSourceFactory((Context) ApplicationProvider.getApplicationContext());
|
||||||
|
|
||||||
|
// Use default DataSource.Factory first.
|
||||||
|
prepareSsUrlAndWaitForPrepareError(defaultMediaSourceFactory);
|
||||||
|
defaultMediaSourceFactory.setDataSourceFactory(() -> fakeDataSource);
|
||||||
|
prepareSsUrlAndWaitForPrepareError(defaultMediaSourceFactory);
|
||||||
|
|
||||||
|
assertThat(fakeDataSource.getAndClearOpenedDataSpecs()).asList().isNotEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void prepareSsUrlAndWaitForPrepareError(
|
||||||
|
DefaultMediaSourceFactory defaultMediaSourceFactory) throws Exception {
|
||||||
|
MediaSource mediaSource =
|
||||||
|
defaultMediaSourceFactory.createMediaSource(MediaItem.fromUri(URI_MEDIA + "/file.ism"));
|
||||||
|
getInstrumentation()
|
||||||
|
.runOnMainSync(
|
||||||
|
() ->
|
||||||
|
mediaSource.prepareSource(
|
||||||
|
(source, timeline) -> {}, /* mediaTransferListener= */ null, PlayerId.UNSET));
|
||||||
|
// We don't expect this to prepare successfully.
|
||||||
|
RobolectricUtil.runMainLooperUntil(
|
||||||
|
() -> {
|
||||||
|
try {
|
||||||
|
mediaSource.maybeThrowSourceInfoRefreshError();
|
||||||
|
return false;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1817,7 +1817,13 @@ public class StyledPlayerControlView extends FrameLayout {
|
||||||
if (position < playbackSpeedTexts.length) {
|
if (position < playbackSpeedTexts.length) {
|
||||||
holder.textView.setText(playbackSpeedTexts[position]);
|
holder.textView.setText(playbackSpeedTexts[position]);
|
||||||
}
|
}
|
||||||
holder.checkView.setVisibility(position == selectedIndex ? VISIBLE : INVISIBLE);
|
if (position == selectedIndex) {
|
||||||
|
holder.itemView.setSelected(true);
|
||||||
|
holder.checkView.setVisibility(VISIBLE);
|
||||||
|
} else {
|
||||||
|
holder.itemView.setSelected(false);
|
||||||
|
holder.checkView.setVisibility(INVISIBLE);
|
||||||
|
}
|
||||||
holder.itemView.setOnClickListener(
|
holder.itemView.setOnClickListener(
|
||||||
v -> {
|
v -> {
|
||||||
if (position != selectedIndex) {
|
if (position != selectedIndex) {
|
||||||
|
|
|
||||||
386
media3-migration.sh
Normal file
386
media3-migration.sh
Normal file
|
|
@ -0,0 +1,386 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# Copyright (C) 2022 The Android Open Source Project
|
||||||
|
#
|
||||||
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
# you may not use this file except in compliance with the License.
|
||||||
|
# You may obtain a copy of the License at
|
||||||
|
#
|
||||||
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
#
|
||||||
|
# Unless required by applicable law or agreed to in writing, software
|
||||||
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
# See the License for the specific language governing permissions and
|
||||||
|
# limitations under the License.
|
||||||
|
##
|
||||||
|
shopt -s extglob
|
||||||
|
|
||||||
|
PACKAGE_MAPPINGS='com.google.android.exoplayer2 com.google.android.exoplayer2
|
||||||
|
com.google.android.exoplayer2.analytics com.google.android.exoplayer2.analytics
|
||||||
|
com.google.android.exoplayer2.audio com.google.android.exoplayer2.audio
|
||||||
|
com.google.android.exoplayer2.castdemo com.google.android.exoplayer2.castdemo
|
||||||
|
com.google.android.exoplayer2.database com.google.android.exoplayer2.database
|
||||||
|
com.google.android.exoplayer2.decoder com.google.android.exoplayer2.decoder
|
||||||
|
com.google.android.exoplayer2.demo com.google.android.exoplayer2.demo
|
||||||
|
com.google.android.exoplayer2.drm com.google.android.exoplayer2.drm
|
||||||
|
com.google.android.exoplayer2.ext.av1 com.google.android.exoplayer2.ext.av1
|
||||||
|
com.google.android.exoplayer2.ext.cast com.google.android.exoplayer2.ext.cast
|
||||||
|
com.google.android.exoplayer2.ext.cronet com.google.android.exoplayer2.ext.cronet
|
||||||
|
com.google.android.exoplayer2.ext.ffmpeg com.google.android.exoplayer2.ext.ffmpeg
|
||||||
|
com.google.android.exoplayer2.ext.flac com.google.android.exoplayer2.ext.flac
|
||||||
|
com.google.android.exoplayer2.ext.ima com.google.android.exoplayer2.ext.ima
|
||||||
|
com.google.android.exoplayer2.ext.leanback com.google.android.exoplayer2.ext.leanback
|
||||||
|
com.google.android.exoplayer2.ext.okhttp com.google.android.exoplayer2.ext.okhttp
|
||||||
|
com.google.android.exoplayer2.ext.opus com.google.android.exoplayer2.ext.opus
|
||||||
|
com.google.android.exoplayer2.ext.rtmp com.google.android.exoplayer2.ext.rtmp
|
||||||
|
com.google.android.exoplayer2.ext.vp9 com.google.android.exoplayer2.ext.vp9
|
||||||
|
com.google.android.exoplayer2.ext.workmanager com.google.android.exoplayer2.ext.workmanager
|
||||||
|
com.google.android.exoplayer2.extractor com.google.android.exoplayer2.extractor
|
||||||
|
com.google.android.exoplayer2.gldemo com.google.android.exoplayer2.gldemo
|
||||||
|
com.google.android.exoplayer2.mediacodec com.google.android.exoplayer2.mediacodec
|
||||||
|
com.google.android.exoplayer2.metadata com.google.android.exoplayer2.metadata
|
||||||
|
com.google.android.exoplayer2.offline com.google.android.exoplayer2.offline
|
||||||
|
com.google.android.exoplayer2.playbacktests com.google.android.exoplayer2.playbacktests
|
||||||
|
com.google.android.exoplayer2.robolectric com.google.android.exoplayer2.robolectric
|
||||||
|
com.google.android.exoplayer2.scheduler com.google.android.exoplayer2.scheduler
|
||||||
|
com.google.android.exoplayer2.source com.google.android.exoplayer2.source
|
||||||
|
com.google.android.exoplayer2.source.dash com.google.android.exoplayer2.source.dash
|
||||||
|
com.google.android.exoplayer2.source.hls com.google.android.exoplayer2.source.hls
|
||||||
|
com.google.android.exoplayer2.source.rtsp com.google.android.exoplayer2.source.rtsp
|
||||||
|
com.google.android.exoplayer2.source.smoothstreaming com.google.android.exoplayer2.source.smoothstreaming
|
||||||
|
com.google.android.exoplayer2.surfacedemo com.google.android.exoplayer2.surfacedemo
|
||||||
|
com.google.android.exoplayer2.testdata com.google.android.exoplayer2.testdata
|
||||||
|
com.google.android.exoplayer2.testutil com.google.android.exoplayer2.testutil
|
||||||
|
com.google.android.exoplayer2.text com.google.android.exoplayer2.text
|
||||||
|
com.google.android.exoplayer2.trackselection com.google.android.exoplayer2.trackselection
|
||||||
|
com.google.android.exoplayer2.transformer com.google.android.exoplayer2.transformer
|
||||||
|
com.google.android.exoplayer2.transformerdemo com.google.android.exoplayer2.transformerdemo
|
||||||
|
com.google.android.exoplayer2.ui com.google.android.exoplayer2.ui
|
||||||
|
com.google.android.exoplayer2.upstream com.google.android.exoplayer2.upstream
|
||||||
|
com.google.android.exoplayer2.upstream.cache com.google.android.exoplayer2.upstream.cache
|
||||||
|
com.google.android.exoplayer2.upstream.crypto com.google.android.exoplayer2.upstream.crypto
|
||||||
|
com.google.android.exoplayer2.util com.google.android.exoplayer2.util
|
||||||
|
com.google.android.exoplayer2.util com.google.android.exoplayer2.util
|
||||||
|
com.google.android.exoplayer2.video com.google.android.exoplayer2.video'
|
||||||
|
|
||||||
|
|
||||||
|
CLASS_RENAMINGS='com.google.android.exoplayer2.ui.StyledPlayerView com.google.android.exoplayer2.ui.PlayerView
|
||||||
|
StyledPlayerView PlayerView
|
||||||
|
com.google.android.exoplayer2.ui.StyledPlayerControlView com.google.android.exoplayer2.ui.PlayerControlView
|
||||||
|
StyledPlayerControlView PlayerControlView
|
||||||
|
com.google.android.exoplayer2.ExoPlayerLibraryInfo androidx.media3.common.MediaLibraryInfo
|
||||||
|
ExoPlayerLibraryInfo MediaLibraryInfo
|
||||||
|
com.google.android.exoplayer2.SimpleExoPlayer com.google.android.exoplayer2.ExoPlayer
|
||||||
|
SimpleExoPlayer ExoPlayer'
|
||||||
|
|
||||||
|
CLASS_MAPPINGS='com.google.android.exoplayer2.text.span androidx.media3.common.text HorizontalTextInVerticalContextSpan LanguageFeatureSpan RubySpan SpanUtil TextAnnotation TextEmphasisSpan
|
||||||
|
com.google.android.exoplayer2.text androidx.media3.common.text CueGroup Cue
|
||||||
|
com.google.android.exoplayer2.text com.google.android.exoplayer2.text ExoplayerCuesDecoder SubtitleDecoderFactory TextOutput TextRenderer
|
||||||
|
com.google.android.exoplayer2.upstream.crypto com.google.android.exoplayer2.upstream AesCipherDataSource AesCipherDataSink AesFlushingCipher
|
||||||
|
com.google.android.exoplayer2.util com.google.android.exoplayer2.util AtomicFile Assertions BundleableUtil BundleUtil Clock ClosedSource CodecSpecificDataUtil ColorParser ConditionVariable Consumer CopyOnWriteMultiset EGLSurfaceTexture GlProgram GlUtil HandlerWrapper LibraryLoader ListenerSet Log LongArray MediaFormatUtil NetworkTypeObserver NonNullApi NotificationUtil ParsableBitArray ParsableByteArray RepeatModeUtil RunnableFutureTask SystemClock SystemHandlerWrapper TimedValueQueue TimestampAdjuster TraceUtil UnknownNull UnstableApi UriUtil Util XmlPullParserUtil
|
||||||
|
com.google.android.exoplayer2.util androidx.media3.common ErrorMessageProvider FlagSet FileTypes MimeTypes PriorityTaskManager
|
||||||
|
com.google.android.exoplayer2.metadata androidx.media3.common Metadata
|
||||||
|
com.google.android.exoplayer2.metadata com.google.android.exoplayer2.metadata MetadataDecoderFactory MetadataOutput MetadataRenderer
|
||||||
|
com.google.android.exoplayer2.audio androidx.media3.common AudioAttributes AuxEffectInfo
|
||||||
|
com.google.android.exoplayer2.ui androidx.media3.common AdOverlayInfo AdViewProvider
|
||||||
|
com.google.android.exoplayer2.source.ads androidx.media3.common AdPlaybackState
|
||||||
|
com.google.android.exoplayer2.source androidx.media3.common MediaPeriodId TrackGroup
|
||||||
|
com.google.android.exoplayer2.offline androidx.media3.common StreamKey
|
||||||
|
com.google.android.exoplayer2.ui com.google.android.exoplayer2.offline DownloadNotificationHelper
|
||||||
|
com.google.android.exoplayer2.trackselection androidx.media3.common TrackSelectionParameters TrackSelectionOverride
|
||||||
|
com.google.android.exoplayer2.video androidx.media3.common ColorInfo VideoSize
|
||||||
|
com.google.android.exoplayer2.upstream androidx.media3.common DataReader
|
||||||
|
com.google.android.exoplayer2.upstream com.google.android.exoplayer2.upstream Allocation Allocator BandwidthMeter CachedRegionTracker DefaultAllocator DefaultBandwidthMeter DefaultLoadErrorHandlingPolicy Loader LoaderErrorThrower ParsingLoadable SlidingPercentile TimeToFirstByteEstimator
|
||||||
|
com.google.android.exoplayer2.audio com.google.android.exoplayer2.extractor AacUtil Ac3Util Ac4Util DtsUtil MpegAudioUtil OpusUtil WavUtil
|
||||||
|
com.google.android.exoplayer2.util com.google.android.exoplayer2.extractor NalUnitUtil ParsableNalUnitBitArray
|
||||||
|
com.google.android.exoplayer2.video com.google.android.exoplayer2.extractor AvcConfig DolbyVisionConfig HevcConfig
|
||||||
|
com.google.android.exoplayer2.decoder com.google.android.exoplayer2 DecoderCounters DecoderReuseEvaluation
|
||||||
|
com.google.android.exoplayer2.util com.google.android.exoplayer2 MediaClock StandaloneMediaClock
|
||||||
|
com.google.android.exoplayer2 com.google.android.exoplayer2 FormatHolder PlayerMessage
|
||||||
|
com.google.android.exoplayer2 androidx.media3.common BasePlayer BundleListRetriever Bundleable ControlDispatcher C DefaultControlDispatcher DeviceInfo ErrorMessageProvider ExoPlayerLibraryInfo Format ForwardingPlayer HeartRating IllegalSeekPositionException MediaItem MediaMetadata ParserException PercentageRating PlaybackException PlaybackParameters Player PositionInfo Rating StarRating ThumbRating Timeline Tracks
|
||||||
|
com.google.android.exoplayer2.drm androidx.media3.common DrmInitData'
|
||||||
|
|
||||||
|
DEPENDENCY_MAPPINGS='exoplayer media3-exoplayer
|
||||||
|
exoplayer-common media3-common
|
||||||
|
exoplayer-core media3-exoplayer
|
||||||
|
exoplayer-dash media3-exoplayer-dash
|
||||||
|
exoplayer-database media3-database
|
||||||
|
exoplayer-datasource media-datasource
|
||||||
|
exoplayer-decoder media3-decoder
|
||||||
|
exoplayer-extractor media3-extractor
|
||||||
|
exoplayer-hls media3-exoplayer-hls
|
||||||
|
exoplayer-robolectricutils media3-test-utils-robolectric
|
||||||
|
exoplayer-rtsp media3-exoplayer-rtsp
|
||||||
|
exoplayer-smoothstreaming media3-exoplayer-smoothstreaming
|
||||||
|
exoplayer-testutils media3-test-utils
|
||||||
|
exoplayer-transformer media3-transformer
|
||||||
|
exoplayer-ui media3-ui
|
||||||
|
extension-cast media3-cast
|
||||||
|
extension-cronet media3-datasource-cronet
|
||||||
|
extension-ima media3-exoplayer-ima
|
||||||
|
extension-leanback media3-ui-leanback
|
||||||
|
extension-okhttp media3-datasource-okhttp
|
||||||
|
extension-rtmp media3-datasource-rtmp
|
||||||
|
extension-workmanager media3-exoplayer-workmanager'
|
||||||
|
|
||||||
|
# Rewrites classes, packages and dependencies from the legacy ExoPlayer package structure
|
||||||
|
# to androidx.media3 structure.
|
||||||
|
|
||||||
|
MEDIA3_VERSION="1.0.0-beta02"
|
||||||
|
LEGACY_PEER_VERSION="2.18.1"
|
||||||
|
|
||||||
|
function usage() {
|
||||||
|
echo "usage: $0 [-p|-c|-d|-v]|[-m|-l [-x <path>] [-f] PROJECT_ROOT]"
|
||||||
|
echo " PROJECT_ROOT: path to your project root (location of 'gradlew')"
|
||||||
|
echo " -p: list package mappings and then exit"
|
||||||
|
echo " -c: list class mappings (precedence over package mappings) and then exit"
|
||||||
|
echo " -d: list dependency mappings and then exit"
|
||||||
|
echo " -m: migrate packages, classes and dependencies to AndroidX Media3"
|
||||||
|
echo " -l: list files that will be considered for rewrite and then exit"
|
||||||
|
echo " -x: exclude the path from the list of file to be changed: 'app/src/test'"
|
||||||
|
echo " -f: force the action even when validation fails"
|
||||||
|
echo " -v: print the exoplayer2/media3 version strings of this script and exit"
|
||||||
|
echo " --noclean : Do not call './gradlew clean' in project directory."
|
||||||
|
echo " -h, --help: show this help text"
|
||||||
|
}
|
||||||
|
|
||||||
|
function print_pairs {
|
||||||
|
while read -r line;
|
||||||
|
do
|
||||||
|
IFS=' ' read -ra PAIR <<< "$line"
|
||||||
|
printf "%-55s %-30s\n" "${PAIR[0]}" "${PAIR[1]}"
|
||||||
|
done <<< "$(echo "$@")"
|
||||||
|
}
|
||||||
|
|
||||||
|
function print_class_mappings {
|
||||||
|
while read -r mapping;
|
||||||
|
do
|
||||||
|
old=$(echo "$mapping" | cut -d ' ' -f1)
|
||||||
|
new=$(echo "$mapping" | cut -d ' ' -f2)
|
||||||
|
classes=$(echo "$mapping" | cut -d ' ' -f3-)
|
||||||
|
for clazz in $classes;
|
||||||
|
do
|
||||||
|
printf "%-80s %-30s\n" "$old.$clazz" "$new.$clazz"
|
||||||
|
done
|
||||||
|
done <<< "$(echo "$CLASS_MAPPINGS" | sort)"
|
||||||
|
}
|
||||||
|
|
||||||
|
ERROR_COUNTER=0
|
||||||
|
VALIDATION_ERRORS=''
|
||||||
|
|
||||||
|
function add_validation_error {
|
||||||
|
let ERROR_COUNTER++
|
||||||
|
VALIDATION_ERRORS+="\033[31m[$ERROR_COUNTER] ->\033[0m ${1}"
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate_exoplayer_version() {
|
||||||
|
has_exoplayer_dependency=''
|
||||||
|
while read -r file;
|
||||||
|
do
|
||||||
|
local version
|
||||||
|
version=$(grep -m 1 "com\.google\.android\.exoplayer:" "$file" | cut -d ":" -f3 | tr -d \" | tr -d \')
|
||||||
|
if [[ ! -z $version ]] && [[ ! "$version" =~ $LEGACY_PEER_VERSION ]];
|
||||||
|
then
|
||||||
|
add_validation_error "The version does not match '$LEGACY_PEER_VERSION'. \
|
||||||
|
Update to '$LEGACY_PEER_VERSION' or use the migration script matching your \
|
||||||
|
current version. Current version '$version' found in\n $file\n"
|
||||||
|
fi
|
||||||
|
done <<< "$(find . -type f -name "build.gradle")"
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate_string_not_contained {
|
||||||
|
local pattern=$1 # regex
|
||||||
|
local failure_message=$2
|
||||||
|
while read -r file;
|
||||||
|
do
|
||||||
|
if grep -q -e "$pattern" "$file";
|
||||||
|
then
|
||||||
|
add_validation_error "$failure_message:\n $file\n"
|
||||||
|
fi
|
||||||
|
done <<< "$files"
|
||||||
|
}
|
||||||
|
|
||||||
|
function validate_string_patterns {
|
||||||
|
validate_string_not_contained \
|
||||||
|
'com\.google\.android\.exoplayer2\..*\*' \
|
||||||
|
'Replace wildcard import statements with fully qualified import statements';
|
||||||
|
validate_string_not_contained \
|
||||||
|
'com\.google\.android\.exoplayer2\.ui\.PlayerView' \
|
||||||
|
'Migrate PlayerView to StyledPlayerView before migrating';
|
||||||
|
validate_string_not_contained \
|
||||||
|
'LegacyPlayerView' \
|
||||||
|
'Migrate LegacyPlayerView to StyledPlayerView before migrating';
|
||||||
|
validate_string_not_contained \
|
||||||
|
'com\.google\.android\.exoplayer2\.ext\.mediasession' \
|
||||||
|
'The MediaSessionConnector is integrated in androidx.media3.session.MediaSession'
|
||||||
|
}
|
||||||
|
|
||||||
|
SED_CMD_INPLACE='sed -i '
|
||||||
|
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||||
|
SED_CMD_INPLACE="sed -i '' "
|
||||||
|
fi
|
||||||
|
|
||||||
|
MIGRATE_FILES='1'
|
||||||
|
LIST_FILES_ONLY='1'
|
||||||
|
PRINT_CLASS_MAPPINGS='1'
|
||||||
|
PRINT_PACKAGE_MAPPINGS='1'
|
||||||
|
PRINT_DEPENDENCY_MAPPINGS='1'
|
||||||
|
PRINT_VERSION='1'
|
||||||
|
NO_CLEAN='1'
|
||||||
|
FORCE='1'
|
||||||
|
IGNORE_VERSION='1'
|
||||||
|
EXCLUDED_PATHS=''
|
||||||
|
|
||||||
|
while [[ $1 =~ ^-.* ]];
|
||||||
|
do
|
||||||
|
case "$1" in
|
||||||
|
-m ) MIGRATE_FILES='';;
|
||||||
|
-l ) LIST_FILES_ONLY='';;
|
||||||
|
-c ) PRINT_CLASS_MAPPINGS='';;
|
||||||
|
-p ) PRINT_PACKAGE_MAPPINGS='';;
|
||||||
|
-d ) PRINT_DEPENDENCY_MAPPINGS='';;
|
||||||
|
-v ) PRINT_VERSION='';;
|
||||||
|
-f ) FORCE='';;
|
||||||
|
-x ) shift; EXCLUDED_PATHS="$(printf "%s\n%s" $EXCLUDED_PATHS $1)";;
|
||||||
|
--noclean ) NO_CLEAN='';;
|
||||||
|
* ) usage && exit 1;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -z $PRINT_DEPENDENCY_MAPPINGS ]];
|
||||||
|
then
|
||||||
|
print_pairs "$DEPENDENCY_MAPPINGS"
|
||||||
|
exit 0
|
||||||
|
elif [[ -z $PRINT_PACKAGE_MAPPINGS ]];
|
||||||
|
then
|
||||||
|
print_pairs "$PACKAGE_MAPPINGS"
|
||||||
|
exit 0
|
||||||
|
elif [[ -z $PRINT_CLASS_MAPPINGS ]];
|
||||||
|
then
|
||||||
|
print_class_mappings
|
||||||
|
exit 0
|
||||||
|
elif [[ -z $PRINT_VERSION ]];
|
||||||
|
then
|
||||||
|
echo "$LEGACY_PEER_VERSION -> $MEDIA3_VERSION. This script is written to migrate from ExoPlayer $LEGACY_PEER_VERSION to AndroidX Media3 $MEDIA3_VERSION"
|
||||||
|
exit 0
|
||||||
|
elif [[ -z $1 ]];
|
||||||
|
then
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f $1/gradlew ]];
|
||||||
|
then
|
||||||
|
echo "directory seems not to exist or is not a gradle project (missing 'gradlew')"
|
||||||
|
usage
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
PROJECT_ROOT=$1
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
|
||||||
|
# Create the set of files to transform
|
||||||
|
exclusion="/build/|/.idea/|/res/drawable|/res/color|/res/mipmap|/res/values|"
|
||||||
|
if [[ ! -z $EXCLUDED_PATHS ]];
|
||||||
|
then
|
||||||
|
while read -r path;
|
||||||
|
do
|
||||||
|
exclusion="$exclusion./$path|"
|
||||||
|
done <<< "$EXCLUDED_PATHS"
|
||||||
|
fi
|
||||||
|
files=$(find . -name '*\.java' -o -name '*\.kt' -o -name '*\.xml' | grep -Ev "'$exclusion'")
|
||||||
|
|
||||||
|
# Validate project and exit in case of validation errors
|
||||||
|
validate_string_patterns
|
||||||
|
validate_exoplayer_version "$PROJECT_ROOT"
|
||||||
|
if [[ ! -z $FORCE && ! -z "$VALIDATION_ERRORS" ]];
|
||||||
|
then
|
||||||
|
echo "============================================="
|
||||||
|
echo "Validation errors (use -f to force execution)"
|
||||||
|
echo "---------------------------------------------"
|
||||||
|
echo -e "$VALIDATION_ERRORS"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $LIST_FILES_ONLY ]];
|
||||||
|
then
|
||||||
|
echo "$files" | cut -c 3-
|
||||||
|
find . -type f -name 'build\.gradle' | cut -c 3-
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# start migration after successful validation or when forced to disregard validation
|
||||||
|
# errors
|
||||||
|
|
||||||
|
if [[ ! -z "$MIGRATE_FILES" ]];
|
||||||
|
then
|
||||||
|
echo "nothing to do"
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
PWD=$(pwd)
|
||||||
|
if [[ ! -z $NO_CLEAN ]];
|
||||||
|
then
|
||||||
|
cd "$PROJECT_ROOT"
|
||||||
|
./gradlew clean
|
||||||
|
cd "$PWD"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# create expressions for class renamings
|
||||||
|
renaming_expressions=''
|
||||||
|
while read -r renaming;
|
||||||
|
do
|
||||||
|
src=$(echo "$renaming" | cut -d ' ' -f1 | sed -e 's/\./\\\./g')
|
||||||
|
dest=$(echo "$renaming" | cut -d ' ' -f2)
|
||||||
|
renaming_expressions+="-e s/$src/$dest/g "
|
||||||
|
done <<< "$CLASS_RENAMINGS"
|
||||||
|
|
||||||
|
# create expressions for class mappings
|
||||||
|
classes_expressions=''
|
||||||
|
while read -r mapping;
|
||||||
|
do
|
||||||
|
src=$(echo "$mapping" | cut -d ' ' -f1 | sed -e 's/\./\\\./g')
|
||||||
|
dest=$(echo "$mapping" | cut -d ' ' -f2)
|
||||||
|
classes=$(echo "$mapping" | cut -d ' ' -f3-)
|
||||||
|
for clazz in $classes;
|
||||||
|
do
|
||||||
|
classes_expressions+="-e s/$src\.$clazz/$dest.$clazz/g "
|
||||||
|
done
|
||||||
|
done <<< "$CLASS_MAPPINGS"
|
||||||
|
|
||||||
|
# create expressions for package mappings
|
||||||
|
packages_expressions=''
|
||||||
|
while read -r mapping;
|
||||||
|
do
|
||||||
|
src=$(echo "$mapping" | cut -d ' ' -f1 | sed -e 's/\./\\\./g')
|
||||||
|
dest=$(echo "$mapping" | cut -d ' ' -f2)
|
||||||
|
packages_expressions+="-e s/$src/$dest/g "
|
||||||
|
done <<< "$PACKAGE_MAPPINGS"
|
||||||
|
|
||||||
|
# do search and replace with expressions in each selected file
|
||||||
|
while read -r file;
|
||||||
|
do
|
||||||
|
echo "migrating $file"
|
||||||
|
expr="$renaming_expressions $classes_expressions $packages_expressions"
|
||||||
|
$SED_CMD_INPLACE $expr $file
|
||||||
|
done <<< "$files"
|
||||||
|
|
||||||
|
# create expressions for dependencies in gradle files
|
||||||
|
EXOPLAYER_GROUP="com\.google\.android\.exoplayer"
|
||||||
|
MEDIA3_GROUP="androidx.media3"
|
||||||
|
dependency_expressions=""
|
||||||
|
while read -r mapping
|
||||||
|
do
|
||||||
|
OLD=$(echo "$mapping" | cut -d ' ' -f1 | sed -e 's/\./\\\./g')
|
||||||
|
NEW=$(echo "$mapping" | cut -d ' ' -f2)
|
||||||
|
dependency_expressions="$dependency_expressions -e s/$EXOPLAYER_GROUP:$OLD:.*\"/$MEDIA3_GROUP:$NEW:$MEDIA3_VERSION\"/g -e s/$EXOPLAYER_GROUP:$OLD:.*'/$MEDIA3_GROUP:$NEW:$MEDIA3_VERSION'/"
|
||||||
|
done <<< "$DEPENDENCY_MAPPINGS"
|
||||||
|
|
||||||
|
## do search and replace for dependencies in gradle files
|
||||||
|
while read -r build_file;
|
||||||
|
do
|
||||||
|
echo "migrating build file $build_file"
|
||||||
|
$SED_CMD_INPLACE $dependency_expressions $build_file
|
||||||
|
done <<< "$(find . -type f -name 'build\.gradle')"
|
||||||
|
|
@ -182,41 +182,6 @@ public class TestPlayerRunHelper {
|
||||||
return checkNotNull(player.getPlayerError());
|
return checkNotNull(player.getPlayerError());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs tasks of the main {@link Looper} until {@link
|
|
||||||
* ExoPlayer.AudioOffloadListener#onExperimentalOffloadSchedulingEnabledChanged} is called or a
|
|
||||||
* playback error occurs.
|
|
||||||
*
|
|
||||||
* <p>If a playback error occurs it will be thrown wrapped in an {@link IllegalStateException}.
|
|
||||||
*
|
|
||||||
* @param player The {@link Player}.
|
|
||||||
* @return The new offloadSchedulingEnabled state.
|
|
||||||
* @throws TimeoutException If the {@link RobolectricUtil#DEFAULT_TIMEOUT_MS default timeout} is
|
|
||||||
* exceeded.
|
|
||||||
*/
|
|
||||||
public static boolean runUntilReceiveOffloadSchedulingEnabledNewState(ExoPlayer player)
|
|
||||||
throws TimeoutException {
|
|
||||||
verifyMainTestThread(player);
|
|
||||||
AtomicReference<@NullableType Boolean> offloadSchedulingEnabledReceiver =
|
|
||||||
new AtomicReference<>();
|
|
||||||
ExoPlayer.AudioOffloadListener listener =
|
|
||||||
new ExoPlayer.AudioOffloadListener() {
|
|
||||||
@Override
|
|
||||||
public void onExperimentalOffloadSchedulingEnabledChanged(
|
|
||||||
boolean offloadSchedulingEnabled) {
|
|
||||||
offloadSchedulingEnabledReceiver.set(offloadSchedulingEnabled);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
player.addAudioOffloadListener(listener);
|
|
||||||
runMainLooperUntil(
|
|
||||||
() -> offloadSchedulingEnabledReceiver.get() != null || player.getPlayerError() != null);
|
|
||||||
player.removeAudioOffloadListener(listener);
|
|
||||||
if (player.getPlayerError() != null) {
|
|
||||||
throw new IllegalStateException(player.getPlayerError());
|
|
||||||
}
|
|
||||||
return checkNotNull(offloadSchedulingEnabledReceiver.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs tasks of the main {@link Looper} until {@link
|
* Runs tasks of the main {@link Looper} until {@link
|
||||||
* ExoPlayer.AudioOffloadListener#onExperimentalSleepingForOffloadChanged(boolean)} is called or a
|
* ExoPlayer.AudioOffloadListener#onExperimentalSleepingForOffloadChanged(boolean)} is called or a
|
||||||
|
|
|
||||||
339
testdata/src/test/assets/extractordumps/mp4/sample_fragmented_large_bitrates.mp4.0.dump
vendored
Normal file
339
testdata/src/test/assets/extractordumps/mp4/sample_fragmented_large_bitrates.mp4.0.dump
vendored
Normal file
|
|
@ -0,0 +1,339 @@
|
||||||
|
seekMap:
|
||||||
|
isSeekable = true
|
||||||
|
duration = 1067733
|
||||||
|
getPosition(0) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(1) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(533866) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(1067733) = [[timeUs=66733, position=1325]]
|
||||||
|
numberOfTracks = 2
|
||||||
|
track 0:
|
||||||
|
total output bytes = 85933
|
||||||
|
sample count = 30
|
||||||
|
format 0:
|
||||||
|
id = 1
|
||||||
|
sampleMimeType = video/avc
|
||||||
|
codecs = avc1.64001F
|
||||||
|
width = 1080
|
||||||
|
height = 720
|
||||||
|
initializationData:
|
||||||
|
data = length 29, hash 4746B5D9
|
||||||
|
data = length 10, hash 7A0D0F2B
|
||||||
|
sample 0:
|
||||||
|
time = 66733
|
||||||
|
flags = 1
|
||||||
|
data = length 38070, hash B58E1AEE
|
||||||
|
sample 1:
|
||||||
|
time = 200200
|
||||||
|
flags = 0
|
||||||
|
data = length 8340, hash 8AC449FF
|
||||||
|
sample 2:
|
||||||
|
time = 133466
|
||||||
|
flags = 0
|
||||||
|
data = length 1295, hash C0DA5090
|
||||||
|
sample 3:
|
||||||
|
time = 100100
|
||||||
|
flags = 0
|
||||||
|
data = length 469, hash D6E0A200
|
||||||
|
sample 4:
|
||||||
|
time = 166833
|
||||||
|
flags = 0
|
||||||
|
data = length 564, hash E5F56C5B
|
||||||
|
sample 5:
|
||||||
|
time = 333666
|
||||||
|
flags = 0
|
||||||
|
data = length 6075, hash 8756E49E
|
||||||
|
sample 6:
|
||||||
|
time = 266933
|
||||||
|
flags = 0
|
||||||
|
data = length 847, hash DCC2B618
|
||||||
|
sample 7:
|
||||||
|
time = 233566
|
||||||
|
flags = 0
|
||||||
|
data = length 455, hash B9CCE047
|
||||||
|
sample 8:
|
||||||
|
time = 300300
|
||||||
|
flags = 0
|
||||||
|
data = length 467, hash 69806D94
|
||||||
|
sample 9:
|
||||||
|
time = 467133
|
||||||
|
flags = 0
|
||||||
|
data = length 4549, hash 3944F501
|
||||||
|
sample 10:
|
||||||
|
time = 400400
|
||||||
|
flags = 0
|
||||||
|
data = length 1087, hash 491BF106
|
||||||
|
sample 11:
|
||||||
|
time = 367033
|
||||||
|
flags = 0
|
||||||
|
data = length 380, hash 5FED016A
|
||||||
|
sample 12:
|
||||||
|
time = 433766
|
||||||
|
flags = 0
|
||||||
|
data = length 455, hash 8A0610
|
||||||
|
sample 13:
|
||||||
|
time = 600600
|
||||||
|
flags = 0
|
||||||
|
data = length 5190, hash B9031D8
|
||||||
|
sample 14:
|
||||||
|
time = 533866
|
||||||
|
flags = 0
|
||||||
|
data = length 1071, hash 684E7DC8
|
||||||
|
sample 15:
|
||||||
|
time = 500500
|
||||||
|
flags = 0
|
||||||
|
data = length 653, hash 8494F326
|
||||||
|
sample 16:
|
||||||
|
time = 567233
|
||||||
|
flags = 0
|
||||||
|
data = length 485, hash 2CCC85F4
|
||||||
|
sample 17:
|
||||||
|
time = 734066
|
||||||
|
flags = 0
|
||||||
|
data = length 4884, hash D16B6A96
|
||||||
|
sample 18:
|
||||||
|
time = 667333
|
||||||
|
flags = 0
|
||||||
|
data = length 997, hash 164FF210
|
||||||
|
sample 19:
|
||||||
|
time = 633966
|
||||||
|
flags = 0
|
||||||
|
data = length 640, hash F664125B
|
||||||
|
sample 20:
|
||||||
|
time = 700700
|
||||||
|
flags = 0
|
||||||
|
data = length 491, hash B5930C7C
|
||||||
|
sample 21:
|
||||||
|
time = 867533
|
||||||
|
flags = 0
|
||||||
|
data = length 2989, hash 92CF4FCF
|
||||||
|
sample 22:
|
||||||
|
time = 800800
|
||||||
|
flags = 0
|
||||||
|
data = length 838, hash 294A3451
|
||||||
|
sample 23:
|
||||||
|
time = 767433
|
||||||
|
flags = 0
|
||||||
|
data = length 544, hash FCCE2DE6
|
||||||
|
sample 24:
|
||||||
|
time = 834166
|
||||||
|
flags = 0
|
||||||
|
data = length 329, hash A654FFA1
|
||||||
|
sample 25:
|
||||||
|
time = 1001000
|
||||||
|
flags = 0
|
||||||
|
data = length 1517, hash 5F7EBF8B
|
||||||
|
sample 26:
|
||||||
|
time = 934266
|
||||||
|
flags = 0
|
||||||
|
data = length 803, hash 7A5C4C1D
|
||||||
|
sample 27:
|
||||||
|
time = 900900
|
||||||
|
flags = 0
|
||||||
|
data = length 415, hash B31BBC3B
|
||||||
|
sample 28:
|
||||||
|
time = 967633
|
||||||
|
flags = 0
|
||||||
|
data = length 415, hash 850DFEA3
|
||||||
|
sample 29:
|
||||||
|
time = 1034366
|
||||||
|
flags = 0
|
||||||
|
data = length 619, hash AB5E56CA
|
||||||
|
track 1:
|
||||||
|
total output bytes = 18257
|
||||||
|
sample count = 46
|
||||||
|
format 0:
|
||||||
|
averageBitrate = 2147483647
|
||||||
|
peakBitrate = 2147483647
|
||||||
|
id = 2
|
||||||
|
sampleMimeType = audio/mp4a-latm
|
||||||
|
codecs = mp4a.40.2
|
||||||
|
channelCount = 1
|
||||||
|
sampleRate = 44100
|
||||||
|
language = und
|
||||||
|
initializationData:
|
||||||
|
data = length 5, hash 2B7623A
|
||||||
|
sample 0:
|
||||||
|
time = 0
|
||||||
|
flags = 1
|
||||||
|
data = length 18, hash 96519432
|
||||||
|
sample 1:
|
||||||
|
time = 23219
|
||||||
|
flags = 1
|
||||||
|
data = length 4, hash EE9DF
|
||||||
|
sample 2:
|
||||||
|
time = 46439
|
||||||
|
flags = 1
|
||||||
|
data = length 4, hash EEDBF
|
||||||
|
sample 3:
|
||||||
|
time = 69659
|
||||||
|
flags = 1
|
||||||
|
data = length 157, hash E2F078F4
|
||||||
|
sample 4:
|
||||||
|
time = 92879
|
||||||
|
flags = 1
|
||||||
|
data = length 371, hash B9471F94
|
||||||
|
sample 5:
|
||||||
|
time = 116099
|
||||||
|
flags = 1
|
||||||
|
data = length 373, hash 2AB265CB
|
||||||
|
sample 6:
|
||||||
|
time = 139319
|
||||||
|
flags = 1
|
||||||
|
data = length 402, hash 1295477C
|
||||||
|
sample 7:
|
||||||
|
time = 162539
|
||||||
|
flags = 1
|
||||||
|
data = length 455, hash 2D8146C8
|
||||||
|
sample 8:
|
||||||
|
time = 185759
|
||||||
|
flags = 1
|
||||||
|
data = length 434, hash F2C5D287
|
||||||
|
sample 9:
|
||||||
|
time = 208979
|
||||||
|
flags = 1
|
||||||
|
data = length 450, hash 84143FCD
|
||||||
|
sample 10:
|
||||||
|
time = 232199
|
||||||
|
flags = 1
|
||||||
|
data = length 429, hash EF769D50
|
||||||
|
sample 11:
|
||||||
|
time = 255419
|
||||||
|
flags = 1
|
||||||
|
data = length 450, hash EC3DE692
|
||||||
|
sample 12:
|
||||||
|
time = 278639
|
||||||
|
flags = 1
|
||||||
|
data = length 447, hash 3E519E13
|
||||||
|
sample 13:
|
||||||
|
time = 301859
|
||||||
|
flags = 1
|
||||||
|
data = length 457, hash 1E4F23A0
|
||||||
|
sample 14:
|
||||||
|
time = 325079
|
||||||
|
flags = 1
|
||||||
|
data = length 447, hash A439EA97
|
||||||
|
sample 15:
|
||||||
|
time = 348299
|
||||||
|
flags = 1
|
||||||
|
data = length 456, hash 1E9034C6
|
||||||
|
sample 16:
|
||||||
|
time = 371519
|
||||||
|
flags = 1
|
||||||
|
data = length 398, hash 99DB7345
|
||||||
|
sample 17:
|
||||||
|
time = 394739
|
||||||
|
flags = 1
|
||||||
|
data = length 474, hash 3F05F10A
|
||||||
|
sample 18:
|
||||||
|
time = 417959
|
||||||
|
flags = 1
|
||||||
|
data = length 416, hash C105EE09
|
||||||
|
sample 19:
|
||||||
|
time = 441179
|
||||||
|
flags = 1
|
||||||
|
data = length 454, hash 5FDBE458
|
||||||
|
sample 20:
|
||||||
|
time = 464399
|
||||||
|
flags = 1
|
||||||
|
data = length 438, hash 41A93AC3
|
||||||
|
sample 21:
|
||||||
|
time = 487619
|
||||||
|
flags = 1
|
||||||
|
data = length 443, hash 10FDA652
|
||||||
|
sample 22:
|
||||||
|
time = 510839
|
||||||
|
flags = 1
|
||||||
|
data = length 412, hash 1F791E25
|
||||||
|
sample 23:
|
||||||
|
time = 534058
|
||||||
|
flags = 1
|
||||||
|
data = length 482, hash A6D983D
|
||||||
|
sample 24:
|
||||||
|
time = 557278
|
||||||
|
flags = 1
|
||||||
|
data = length 386, hash BED7392F
|
||||||
|
sample 25:
|
||||||
|
time = 580498
|
||||||
|
flags = 1
|
||||||
|
data = length 463, hash 5309F8C9
|
||||||
|
sample 26:
|
||||||
|
time = 603718
|
||||||
|
flags = 1
|
||||||
|
data = length 394, hash 21C7321F
|
||||||
|
sample 27:
|
||||||
|
time = 626938
|
||||||
|
flags = 1
|
||||||
|
data = length 489, hash 71B4730D
|
||||||
|
sample 28:
|
||||||
|
time = 650158
|
||||||
|
flags = 1
|
||||||
|
data = length 403, hash D9C6DE89
|
||||||
|
sample 29:
|
||||||
|
time = 673378
|
||||||
|
flags = 1
|
||||||
|
data = length 447, hash 9B14B73B
|
||||||
|
sample 30:
|
||||||
|
time = 696598
|
||||||
|
flags = 1
|
||||||
|
data = length 439, hash 4760D35B
|
||||||
|
sample 31:
|
||||||
|
time = 719818
|
||||||
|
flags = 1
|
||||||
|
data = length 463, hash 1601F88D
|
||||||
|
sample 32:
|
||||||
|
time = 743038
|
||||||
|
flags = 1
|
||||||
|
data = length 423, hash D4AE6773
|
||||||
|
sample 33:
|
||||||
|
time = 766258
|
||||||
|
flags = 1
|
||||||
|
data = length 497, hash A3C674D3
|
||||||
|
sample 34:
|
||||||
|
time = 789478
|
||||||
|
flags = 1
|
||||||
|
data = length 419, hash D3734A1F
|
||||||
|
sample 35:
|
||||||
|
time = 812698
|
||||||
|
flags = 1
|
||||||
|
data = length 474, hash DFB41F9
|
||||||
|
sample 36:
|
||||||
|
time = 835918
|
||||||
|
flags = 1
|
||||||
|
data = length 413, hash 53E7CB9F
|
||||||
|
sample 37:
|
||||||
|
time = 859138
|
||||||
|
flags = 1
|
||||||
|
data = length 445, hash D15B0E39
|
||||||
|
sample 38:
|
||||||
|
time = 882358
|
||||||
|
flags = 1
|
||||||
|
data = length 453, hash 77ED81E4
|
||||||
|
sample 39:
|
||||||
|
time = 905578
|
||||||
|
flags = 1
|
||||||
|
data = length 545, hash 3321AEB9
|
||||||
|
sample 40:
|
||||||
|
time = 928798
|
||||||
|
flags = 1
|
||||||
|
data = length 317, hash F557D0E
|
||||||
|
sample 41:
|
||||||
|
time = 952018
|
||||||
|
flags = 1
|
||||||
|
data = length 537, hash ED58CF7B
|
||||||
|
sample 42:
|
||||||
|
time = 975238
|
||||||
|
flags = 1
|
||||||
|
data = length 458, hash 51CDAA10
|
||||||
|
sample 43:
|
||||||
|
time = 998458
|
||||||
|
flags = 1
|
||||||
|
data = length 465, hash CBA1EFD7
|
||||||
|
sample 44:
|
||||||
|
time = 1021678
|
||||||
|
flags = 1
|
||||||
|
data = length 446, hash D6735B8A
|
||||||
|
sample 45:
|
||||||
|
time = 1044897
|
||||||
|
flags = 1
|
||||||
|
data = length 10, hash A453EEBE
|
||||||
|
tracksEnded = true
|
||||||
279
testdata/src/test/assets/extractordumps/mp4/sample_fragmented_large_bitrates.mp4.1.dump
vendored
Normal file
279
testdata/src/test/assets/extractordumps/mp4/sample_fragmented_large_bitrates.mp4.1.dump
vendored
Normal file
|
|
@ -0,0 +1,279 @@
|
||||||
|
seekMap:
|
||||||
|
isSeekable = true
|
||||||
|
duration = 1067733
|
||||||
|
getPosition(0) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(1) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(533866) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(1067733) = [[timeUs=66733, position=1325]]
|
||||||
|
numberOfTracks = 2
|
||||||
|
track 0:
|
||||||
|
total output bytes = 85933
|
||||||
|
sample count = 30
|
||||||
|
format 0:
|
||||||
|
id = 1
|
||||||
|
sampleMimeType = video/avc
|
||||||
|
codecs = avc1.64001F
|
||||||
|
width = 1080
|
||||||
|
height = 720
|
||||||
|
initializationData:
|
||||||
|
data = length 29, hash 4746B5D9
|
||||||
|
data = length 10, hash 7A0D0F2B
|
||||||
|
sample 0:
|
||||||
|
time = 66733
|
||||||
|
flags = 1
|
||||||
|
data = length 38070, hash B58E1AEE
|
||||||
|
sample 1:
|
||||||
|
time = 200200
|
||||||
|
flags = 0
|
||||||
|
data = length 8340, hash 8AC449FF
|
||||||
|
sample 2:
|
||||||
|
time = 133466
|
||||||
|
flags = 0
|
||||||
|
data = length 1295, hash C0DA5090
|
||||||
|
sample 3:
|
||||||
|
time = 100100
|
||||||
|
flags = 0
|
||||||
|
data = length 469, hash D6E0A200
|
||||||
|
sample 4:
|
||||||
|
time = 166833
|
||||||
|
flags = 0
|
||||||
|
data = length 564, hash E5F56C5B
|
||||||
|
sample 5:
|
||||||
|
time = 333666
|
||||||
|
flags = 0
|
||||||
|
data = length 6075, hash 8756E49E
|
||||||
|
sample 6:
|
||||||
|
time = 266933
|
||||||
|
flags = 0
|
||||||
|
data = length 847, hash DCC2B618
|
||||||
|
sample 7:
|
||||||
|
time = 233566
|
||||||
|
flags = 0
|
||||||
|
data = length 455, hash B9CCE047
|
||||||
|
sample 8:
|
||||||
|
time = 300300
|
||||||
|
flags = 0
|
||||||
|
data = length 467, hash 69806D94
|
||||||
|
sample 9:
|
||||||
|
time = 467133
|
||||||
|
flags = 0
|
||||||
|
data = length 4549, hash 3944F501
|
||||||
|
sample 10:
|
||||||
|
time = 400400
|
||||||
|
flags = 0
|
||||||
|
data = length 1087, hash 491BF106
|
||||||
|
sample 11:
|
||||||
|
time = 367033
|
||||||
|
flags = 0
|
||||||
|
data = length 380, hash 5FED016A
|
||||||
|
sample 12:
|
||||||
|
time = 433766
|
||||||
|
flags = 0
|
||||||
|
data = length 455, hash 8A0610
|
||||||
|
sample 13:
|
||||||
|
time = 600600
|
||||||
|
flags = 0
|
||||||
|
data = length 5190, hash B9031D8
|
||||||
|
sample 14:
|
||||||
|
time = 533866
|
||||||
|
flags = 0
|
||||||
|
data = length 1071, hash 684E7DC8
|
||||||
|
sample 15:
|
||||||
|
time = 500500
|
||||||
|
flags = 0
|
||||||
|
data = length 653, hash 8494F326
|
||||||
|
sample 16:
|
||||||
|
time = 567233
|
||||||
|
flags = 0
|
||||||
|
data = length 485, hash 2CCC85F4
|
||||||
|
sample 17:
|
||||||
|
time = 734066
|
||||||
|
flags = 0
|
||||||
|
data = length 4884, hash D16B6A96
|
||||||
|
sample 18:
|
||||||
|
time = 667333
|
||||||
|
flags = 0
|
||||||
|
data = length 997, hash 164FF210
|
||||||
|
sample 19:
|
||||||
|
time = 633966
|
||||||
|
flags = 0
|
||||||
|
data = length 640, hash F664125B
|
||||||
|
sample 20:
|
||||||
|
time = 700700
|
||||||
|
flags = 0
|
||||||
|
data = length 491, hash B5930C7C
|
||||||
|
sample 21:
|
||||||
|
time = 867533
|
||||||
|
flags = 0
|
||||||
|
data = length 2989, hash 92CF4FCF
|
||||||
|
sample 22:
|
||||||
|
time = 800800
|
||||||
|
flags = 0
|
||||||
|
data = length 838, hash 294A3451
|
||||||
|
sample 23:
|
||||||
|
time = 767433
|
||||||
|
flags = 0
|
||||||
|
data = length 544, hash FCCE2DE6
|
||||||
|
sample 24:
|
||||||
|
time = 834166
|
||||||
|
flags = 0
|
||||||
|
data = length 329, hash A654FFA1
|
||||||
|
sample 25:
|
||||||
|
time = 1001000
|
||||||
|
flags = 0
|
||||||
|
data = length 1517, hash 5F7EBF8B
|
||||||
|
sample 26:
|
||||||
|
time = 934266
|
||||||
|
flags = 0
|
||||||
|
data = length 803, hash 7A5C4C1D
|
||||||
|
sample 27:
|
||||||
|
time = 900900
|
||||||
|
flags = 0
|
||||||
|
data = length 415, hash B31BBC3B
|
||||||
|
sample 28:
|
||||||
|
time = 967633
|
||||||
|
flags = 0
|
||||||
|
data = length 415, hash 850DFEA3
|
||||||
|
sample 29:
|
||||||
|
time = 1034366
|
||||||
|
flags = 0
|
||||||
|
data = length 619, hash AB5E56CA
|
||||||
|
track 1:
|
||||||
|
total output bytes = 13359
|
||||||
|
sample count = 31
|
||||||
|
format 0:
|
||||||
|
averageBitrate = 2147483647
|
||||||
|
peakBitrate = 2147483647
|
||||||
|
id = 2
|
||||||
|
sampleMimeType = audio/mp4a-latm
|
||||||
|
codecs = mp4a.40.2
|
||||||
|
channelCount = 1
|
||||||
|
sampleRate = 44100
|
||||||
|
language = und
|
||||||
|
initializationData:
|
||||||
|
data = length 5, hash 2B7623A
|
||||||
|
sample 0:
|
||||||
|
time = 348299
|
||||||
|
flags = 1
|
||||||
|
data = length 456, hash 1E9034C6
|
||||||
|
sample 1:
|
||||||
|
time = 371519
|
||||||
|
flags = 1
|
||||||
|
data = length 398, hash 99DB7345
|
||||||
|
sample 2:
|
||||||
|
time = 394739
|
||||||
|
flags = 1
|
||||||
|
data = length 474, hash 3F05F10A
|
||||||
|
sample 3:
|
||||||
|
time = 417959
|
||||||
|
flags = 1
|
||||||
|
data = length 416, hash C105EE09
|
||||||
|
sample 4:
|
||||||
|
time = 441179
|
||||||
|
flags = 1
|
||||||
|
data = length 454, hash 5FDBE458
|
||||||
|
sample 5:
|
||||||
|
time = 464399
|
||||||
|
flags = 1
|
||||||
|
data = length 438, hash 41A93AC3
|
||||||
|
sample 6:
|
||||||
|
time = 487619
|
||||||
|
flags = 1
|
||||||
|
data = length 443, hash 10FDA652
|
||||||
|
sample 7:
|
||||||
|
time = 510839
|
||||||
|
flags = 1
|
||||||
|
data = length 412, hash 1F791E25
|
||||||
|
sample 8:
|
||||||
|
time = 534058
|
||||||
|
flags = 1
|
||||||
|
data = length 482, hash A6D983D
|
||||||
|
sample 9:
|
||||||
|
time = 557278
|
||||||
|
flags = 1
|
||||||
|
data = length 386, hash BED7392F
|
||||||
|
sample 10:
|
||||||
|
time = 580498
|
||||||
|
flags = 1
|
||||||
|
data = length 463, hash 5309F8C9
|
||||||
|
sample 11:
|
||||||
|
time = 603718
|
||||||
|
flags = 1
|
||||||
|
data = length 394, hash 21C7321F
|
||||||
|
sample 12:
|
||||||
|
time = 626938
|
||||||
|
flags = 1
|
||||||
|
data = length 489, hash 71B4730D
|
||||||
|
sample 13:
|
||||||
|
time = 650158
|
||||||
|
flags = 1
|
||||||
|
data = length 403, hash D9C6DE89
|
||||||
|
sample 14:
|
||||||
|
time = 673378
|
||||||
|
flags = 1
|
||||||
|
data = length 447, hash 9B14B73B
|
||||||
|
sample 15:
|
||||||
|
time = 696598
|
||||||
|
flags = 1
|
||||||
|
data = length 439, hash 4760D35B
|
||||||
|
sample 16:
|
||||||
|
time = 719818
|
||||||
|
flags = 1
|
||||||
|
data = length 463, hash 1601F88D
|
||||||
|
sample 17:
|
||||||
|
time = 743038
|
||||||
|
flags = 1
|
||||||
|
data = length 423, hash D4AE6773
|
||||||
|
sample 18:
|
||||||
|
time = 766258
|
||||||
|
flags = 1
|
||||||
|
data = length 497, hash A3C674D3
|
||||||
|
sample 19:
|
||||||
|
time = 789478
|
||||||
|
flags = 1
|
||||||
|
data = length 419, hash D3734A1F
|
||||||
|
sample 20:
|
||||||
|
time = 812698
|
||||||
|
flags = 1
|
||||||
|
data = length 474, hash DFB41F9
|
||||||
|
sample 21:
|
||||||
|
time = 835918
|
||||||
|
flags = 1
|
||||||
|
data = length 413, hash 53E7CB9F
|
||||||
|
sample 22:
|
||||||
|
time = 859138
|
||||||
|
flags = 1
|
||||||
|
data = length 445, hash D15B0E39
|
||||||
|
sample 23:
|
||||||
|
time = 882358
|
||||||
|
flags = 1
|
||||||
|
data = length 453, hash 77ED81E4
|
||||||
|
sample 24:
|
||||||
|
time = 905578
|
||||||
|
flags = 1
|
||||||
|
data = length 545, hash 3321AEB9
|
||||||
|
sample 25:
|
||||||
|
time = 928798
|
||||||
|
flags = 1
|
||||||
|
data = length 317, hash F557D0E
|
||||||
|
sample 26:
|
||||||
|
time = 952018
|
||||||
|
flags = 1
|
||||||
|
data = length 537, hash ED58CF7B
|
||||||
|
sample 27:
|
||||||
|
time = 975238
|
||||||
|
flags = 1
|
||||||
|
data = length 458, hash 51CDAA10
|
||||||
|
sample 28:
|
||||||
|
time = 998458
|
||||||
|
flags = 1
|
||||||
|
data = length 465, hash CBA1EFD7
|
||||||
|
sample 29:
|
||||||
|
time = 1021678
|
||||||
|
flags = 1
|
||||||
|
data = length 446, hash D6735B8A
|
||||||
|
sample 30:
|
||||||
|
time = 1044897
|
||||||
|
flags = 1
|
||||||
|
data = length 10, hash A453EEBE
|
||||||
|
tracksEnded = true
|
||||||
219
testdata/src/test/assets/extractordumps/mp4/sample_fragmented_large_bitrates.mp4.2.dump
vendored
Normal file
219
testdata/src/test/assets/extractordumps/mp4/sample_fragmented_large_bitrates.mp4.2.dump
vendored
Normal file
|
|
@ -0,0 +1,219 @@
|
||||||
|
seekMap:
|
||||||
|
isSeekable = true
|
||||||
|
duration = 1067733
|
||||||
|
getPosition(0) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(1) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(533866) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(1067733) = [[timeUs=66733, position=1325]]
|
||||||
|
numberOfTracks = 2
|
||||||
|
track 0:
|
||||||
|
total output bytes = 85933
|
||||||
|
sample count = 30
|
||||||
|
format 0:
|
||||||
|
id = 1
|
||||||
|
sampleMimeType = video/avc
|
||||||
|
codecs = avc1.64001F
|
||||||
|
width = 1080
|
||||||
|
height = 720
|
||||||
|
initializationData:
|
||||||
|
data = length 29, hash 4746B5D9
|
||||||
|
data = length 10, hash 7A0D0F2B
|
||||||
|
sample 0:
|
||||||
|
time = 66733
|
||||||
|
flags = 1
|
||||||
|
data = length 38070, hash B58E1AEE
|
||||||
|
sample 1:
|
||||||
|
time = 200200
|
||||||
|
flags = 0
|
||||||
|
data = length 8340, hash 8AC449FF
|
||||||
|
sample 2:
|
||||||
|
time = 133466
|
||||||
|
flags = 0
|
||||||
|
data = length 1295, hash C0DA5090
|
||||||
|
sample 3:
|
||||||
|
time = 100100
|
||||||
|
flags = 0
|
||||||
|
data = length 469, hash D6E0A200
|
||||||
|
sample 4:
|
||||||
|
time = 166833
|
||||||
|
flags = 0
|
||||||
|
data = length 564, hash E5F56C5B
|
||||||
|
sample 5:
|
||||||
|
time = 333666
|
||||||
|
flags = 0
|
||||||
|
data = length 6075, hash 8756E49E
|
||||||
|
sample 6:
|
||||||
|
time = 266933
|
||||||
|
flags = 0
|
||||||
|
data = length 847, hash DCC2B618
|
||||||
|
sample 7:
|
||||||
|
time = 233566
|
||||||
|
flags = 0
|
||||||
|
data = length 455, hash B9CCE047
|
||||||
|
sample 8:
|
||||||
|
time = 300300
|
||||||
|
flags = 0
|
||||||
|
data = length 467, hash 69806D94
|
||||||
|
sample 9:
|
||||||
|
time = 467133
|
||||||
|
flags = 0
|
||||||
|
data = length 4549, hash 3944F501
|
||||||
|
sample 10:
|
||||||
|
time = 400400
|
||||||
|
flags = 0
|
||||||
|
data = length 1087, hash 491BF106
|
||||||
|
sample 11:
|
||||||
|
time = 367033
|
||||||
|
flags = 0
|
||||||
|
data = length 380, hash 5FED016A
|
||||||
|
sample 12:
|
||||||
|
time = 433766
|
||||||
|
flags = 0
|
||||||
|
data = length 455, hash 8A0610
|
||||||
|
sample 13:
|
||||||
|
time = 600600
|
||||||
|
flags = 0
|
||||||
|
data = length 5190, hash B9031D8
|
||||||
|
sample 14:
|
||||||
|
time = 533866
|
||||||
|
flags = 0
|
||||||
|
data = length 1071, hash 684E7DC8
|
||||||
|
sample 15:
|
||||||
|
time = 500500
|
||||||
|
flags = 0
|
||||||
|
data = length 653, hash 8494F326
|
||||||
|
sample 16:
|
||||||
|
time = 567233
|
||||||
|
flags = 0
|
||||||
|
data = length 485, hash 2CCC85F4
|
||||||
|
sample 17:
|
||||||
|
time = 734066
|
||||||
|
flags = 0
|
||||||
|
data = length 4884, hash D16B6A96
|
||||||
|
sample 18:
|
||||||
|
time = 667333
|
||||||
|
flags = 0
|
||||||
|
data = length 997, hash 164FF210
|
||||||
|
sample 19:
|
||||||
|
time = 633966
|
||||||
|
flags = 0
|
||||||
|
data = length 640, hash F664125B
|
||||||
|
sample 20:
|
||||||
|
time = 700700
|
||||||
|
flags = 0
|
||||||
|
data = length 491, hash B5930C7C
|
||||||
|
sample 21:
|
||||||
|
time = 867533
|
||||||
|
flags = 0
|
||||||
|
data = length 2989, hash 92CF4FCF
|
||||||
|
sample 22:
|
||||||
|
time = 800800
|
||||||
|
flags = 0
|
||||||
|
data = length 838, hash 294A3451
|
||||||
|
sample 23:
|
||||||
|
time = 767433
|
||||||
|
flags = 0
|
||||||
|
data = length 544, hash FCCE2DE6
|
||||||
|
sample 24:
|
||||||
|
time = 834166
|
||||||
|
flags = 0
|
||||||
|
data = length 329, hash A654FFA1
|
||||||
|
sample 25:
|
||||||
|
time = 1001000
|
||||||
|
flags = 0
|
||||||
|
data = length 1517, hash 5F7EBF8B
|
||||||
|
sample 26:
|
||||||
|
time = 934266
|
||||||
|
flags = 0
|
||||||
|
data = length 803, hash 7A5C4C1D
|
||||||
|
sample 27:
|
||||||
|
time = 900900
|
||||||
|
flags = 0
|
||||||
|
data = length 415, hash B31BBC3B
|
||||||
|
sample 28:
|
||||||
|
time = 967633
|
||||||
|
flags = 0
|
||||||
|
data = length 415, hash 850DFEA3
|
||||||
|
sample 29:
|
||||||
|
time = 1034366
|
||||||
|
flags = 0
|
||||||
|
data = length 619, hash AB5E56CA
|
||||||
|
track 1:
|
||||||
|
total output bytes = 6804
|
||||||
|
sample count = 16
|
||||||
|
format 0:
|
||||||
|
averageBitrate = 2147483647
|
||||||
|
peakBitrate = 2147483647
|
||||||
|
id = 2
|
||||||
|
sampleMimeType = audio/mp4a-latm
|
||||||
|
codecs = mp4a.40.2
|
||||||
|
channelCount = 1
|
||||||
|
sampleRate = 44100
|
||||||
|
language = und
|
||||||
|
initializationData:
|
||||||
|
data = length 5, hash 2B7623A
|
||||||
|
sample 0:
|
||||||
|
time = 696598
|
||||||
|
flags = 1
|
||||||
|
data = length 439, hash 4760D35B
|
||||||
|
sample 1:
|
||||||
|
time = 719818
|
||||||
|
flags = 1
|
||||||
|
data = length 463, hash 1601F88D
|
||||||
|
sample 2:
|
||||||
|
time = 743038
|
||||||
|
flags = 1
|
||||||
|
data = length 423, hash D4AE6773
|
||||||
|
sample 3:
|
||||||
|
time = 766258
|
||||||
|
flags = 1
|
||||||
|
data = length 497, hash A3C674D3
|
||||||
|
sample 4:
|
||||||
|
time = 789478
|
||||||
|
flags = 1
|
||||||
|
data = length 419, hash D3734A1F
|
||||||
|
sample 5:
|
||||||
|
time = 812698
|
||||||
|
flags = 1
|
||||||
|
data = length 474, hash DFB41F9
|
||||||
|
sample 6:
|
||||||
|
time = 835918
|
||||||
|
flags = 1
|
||||||
|
data = length 413, hash 53E7CB9F
|
||||||
|
sample 7:
|
||||||
|
time = 859138
|
||||||
|
flags = 1
|
||||||
|
data = length 445, hash D15B0E39
|
||||||
|
sample 8:
|
||||||
|
time = 882358
|
||||||
|
flags = 1
|
||||||
|
data = length 453, hash 77ED81E4
|
||||||
|
sample 9:
|
||||||
|
time = 905578
|
||||||
|
flags = 1
|
||||||
|
data = length 545, hash 3321AEB9
|
||||||
|
sample 10:
|
||||||
|
time = 928798
|
||||||
|
flags = 1
|
||||||
|
data = length 317, hash F557D0E
|
||||||
|
sample 11:
|
||||||
|
time = 952018
|
||||||
|
flags = 1
|
||||||
|
data = length 537, hash ED58CF7B
|
||||||
|
sample 12:
|
||||||
|
time = 975238
|
||||||
|
flags = 1
|
||||||
|
data = length 458, hash 51CDAA10
|
||||||
|
sample 13:
|
||||||
|
time = 998458
|
||||||
|
flags = 1
|
||||||
|
data = length 465, hash CBA1EFD7
|
||||||
|
sample 14:
|
||||||
|
time = 1021678
|
||||||
|
flags = 1
|
||||||
|
data = length 446, hash D6735B8A
|
||||||
|
sample 15:
|
||||||
|
time = 1044897
|
||||||
|
flags = 1
|
||||||
|
data = length 10, hash A453EEBE
|
||||||
|
tracksEnded = true
|
||||||
159
testdata/src/test/assets/extractordumps/mp4/sample_fragmented_large_bitrates.mp4.3.dump
vendored
Normal file
159
testdata/src/test/assets/extractordumps/mp4/sample_fragmented_large_bitrates.mp4.3.dump
vendored
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
seekMap:
|
||||||
|
isSeekable = true
|
||||||
|
duration = 1067733
|
||||||
|
getPosition(0) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(1) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(533866) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(1067733) = [[timeUs=66733, position=1325]]
|
||||||
|
numberOfTracks = 2
|
||||||
|
track 0:
|
||||||
|
total output bytes = 85933
|
||||||
|
sample count = 30
|
||||||
|
format 0:
|
||||||
|
id = 1
|
||||||
|
sampleMimeType = video/avc
|
||||||
|
codecs = avc1.64001F
|
||||||
|
width = 1080
|
||||||
|
height = 720
|
||||||
|
initializationData:
|
||||||
|
data = length 29, hash 4746B5D9
|
||||||
|
data = length 10, hash 7A0D0F2B
|
||||||
|
sample 0:
|
||||||
|
time = 66733
|
||||||
|
flags = 1
|
||||||
|
data = length 38070, hash B58E1AEE
|
||||||
|
sample 1:
|
||||||
|
time = 200200
|
||||||
|
flags = 0
|
||||||
|
data = length 8340, hash 8AC449FF
|
||||||
|
sample 2:
|
||||||
|
time = 133466
|
||||||
|
flags = 0
|
||||||
|
data = length 1295, hash C0DA5090
|
||||||
|
sample 3:
|
||||||
|
time = 100100
|
||||||
|
flags = 0
|
||||||
|
data = length 469, hash D6E0A200
|
||||||
|
sample 4:
|
||||||
|
time = 166833
|
||||||
|
flags = 0
|
||||||
|
data = length 564, hash E5F56C5B
|
||||||
|
sample 5:
|
||||||
|
time = 333666
|
||||||
|
flags = 0
|
||||||
|
data = length 6075, hash 8756E49E
|
||||||
|
sample 6:
|
||||||
|
time = 266933
|
||||||
|
flags = 0
|
||||||
|
data = length 847, hash DCC2B618
|
||||||
|
sample 7:
|
||||||
|
time = 233566
|
||||||
|
flags = 0
|
||||||
|
data = length 455, hash B9CCE047
|
||||||
|
sample 8:
|
||||||
|
time = 300300
|
||||||
|
flags = 0
|
||||||
|
data = length 467, hash 69806D94
|
||||||
|
sample 9:
|
||||||
|
time = 467133
|
||||||
|
flags = 0
|
||||||
|
data = length 4549, hash 3944F501
|
||||||
|
sample 10:
|
||||||
|
time = 400400
|
||||||
|
flags = 0
|
||||||
|
data = length 1087, hash 491BF106
|
||||||
|
sample 11:
|
||||||
|
time = 367033
|
||||||
|
flags = 0
|
||||||
|
data = length 380, hash 5FED016A
|
||||||
|
sample 12:
|
||||||
|
time = 433766
|
||||||
|
flags = 0
|
||||||
|
data = length 455, hash 8A0610
|
||||||
|
sample 13:
|
||||||
|
time = 600600
|
||||||
|
flags = 0
|
||||||
|
data = length 5190, hash B9031D8
|
||||||
|
sample 14:
|
||||||
|
time = 533866
|
||||||
|
flags = 0
|
||||||
|
data = length 1071, hash 684E7DC8
|
||||||
|
sample 15:
|
||||||
|
time = 500500
|
||||||
|
flags = 0
|
||||||
|
data = length 653, hash 8494F326
|
||||||
|
sample 16:
|
||||||
|
time = 567233
|
||||||
|
flags = 0
|
||||||
|
data = length 485, hash 2CCC85F4
|
||||||
|
sample 17:
|
||||||
|
time = 734066
|
||||||
|
flags = 0
|
||||||
|
data = length 4884, hash D16B6A96
|
||||||
|
sample 18:
|
||||||
|
time = 667333
|
||||||
|
flags = 0
|
||||||
|
data = length 997, hash 164FF210
|
||||||
|
sample 19:
|
||||||
|
time = 633966
|
||||||
|
flags = 0
|
||||||
|
data = length 640, hash F664125B
|
||||||
|
sample 20:
|
||||||
|
time = 700700
|
||||||
|
flags = 0
|
||||||
|
data = length 491, hash B5930C7C
|
||||||
|
sample 21:
|
||||||
|
time = 867533
|
||||||
|
flags = 0
|
||||||
|
data = length 2989, hash 92CF4FCF
|
||||||
|
sample 22:
|
||||||
|
time = 800800
|
||||||
|
flags = 0
|
||||||
|
data = length 838, hash 294A3451
|
||||||
|
sample 23:
|
||||||
|
time = 767433
|
||||||
|
flags = 0
|
||||||
|
data = length 544, hash FCCE2DE6
|
||||||
|
sample 24:
|
||||||
|
time = 834166
|
||||||
|
flags = 0
|
||||||
|
data = length 329, hash A654FFA1
|
||||||
|
sample 25:
|
||||||
|
time = 1001000
|
||||||
|
flags = 0
|
||||||
|
data = length 1517, hash 5F7EBF8B
|
||||||
|
sample 26:
|
||||||
|
time = 934266
|
||||||
|
flags = 0
|
||||||
|
data = length 803, hash 7A5C4C1D
|
||||||
|
sample 27:
|
||||||
|
time = 900900
|
||||||
|
flags = 0
|
||||||
|
data = length 415, hash B31BBC3B
|
||||||
|
sample 28:
|
||||||
|
time = 967633
|
||||||
|
flags = 0
|
||||||
|
data = length 415, hash 850DFEA3
|
||||||
|
sample 29:
|
||||||
|
time = 1034366
|
||||||
|
flags = 0
|
||||||
|
data = length 619, hash AB5E56CA
|
||||||
|
track 1:
|
||||||
|
total output bytes = 10
|
||||||
|
sample count = 1
|
||||||
|
format 0:
|
||||||
|
averageBitrate = 2147483647
|
||||||
|
peakBitrate = 2147483647
|
||||||
|
id = 2
|
||||||
|
sampleMimeType = audio/mp4a-latm
|
||||||
|
codecs = mp4a.40.2
|
||||||
|
channelCount = 1
|
||||||
|
sampleRate = 44100
|
||||||
|
language = und
|
||||||
|
initializationData:
|
||||||
|
data = length 5, hash 2B7623A
|
||||||
|
sample 0:
|
||||||
|
time = 1044897
|
||||||
|
flags = 1
|
||||||
|
data = length 10, hash A453EEBE
|
||||||
|
tracksEnded = true
|
||||||
339
testdata/src/test/assets/extractordumps/mp4/sample_fragmented_large_bitrates.mp4.unknown_length.dump
vendored
Normal file
339
testdata/src/test/assets/extractordumps/mp4/sample_fragmented_large_bitrates.mp4.unknown_length.dump
vendored
Normal file
|
|
@ -0,0 +1,339 @@
|
||||||
|
seekMap:
|
||||||
|
isSeekable = true
|
||||||
|
duration = 1067733
|
||||||
|
getPosition(0) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(1) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(533866) = [[timeUs=66733, position=1325]]
|
||||||
|
getPosition(1067733) = [[timeUs=66733, position=1325]]
|
||||||
|
numberOfTracks = 2
|
||||||
|
track 0:
|
||||||
|
total output bytes = 85933
|
||||||
|
sample count = 30
|
||||||
|
format 0:
|
||||||
|
id = 1
|
||||||
|
sampleMimeType = video/avc
|
||||||
|
codecs = avc1.64001F
|
||||||
|
width = 1080
|
||||||
|
height = 720
|
||||||
|
initializationData:
|
||||||
|
data = length 29, hash 4746B5D9
|
||||||
|
data = length 10, hash 7A0D0F2B
|
||||||
|
sample 0:
|
||||||
|
time = 66733
|
||||||
|
flags = 1
|
||||||
|
data = length 38070, hash B58E1AEE
|
||||||
|
sample 1:
|
||||||
|
time = 200200
|
||||||
|
flags = 0
|
||||||
|
data = length 8340, hash 8AC449FF
|
||||||
|
sample 2:
|
||||||
|
time = 133466
|
||||||
|
flags = 0
|
||||||
|
data = length 1295, hash C0DA5090
|
||||||
|
sample 3:
|
||||||
|
time = 100100
|
||||||
|
flags = 0
|
||||||
|
data = length 469, hash D6E0A200
|
||||||
|
sample 4:
|
||||||
|
time = 166833
|
||||||
|
flags = 0
|
||||||
|
data = length 564, hash E5F56C5B
|
||||||
|
sample 5:
|
||||||
|
time = 333666
|
||||||
|
flags = 0
|
||||||
|
data = length 6075, hash 8756E49E
|
||||||
|
sample 6:
|
||||||
|
time = 266933
|
||||||
|
flags = 0
|
||||||
|
data = length 847, hash DCC2B618
|
||||||
|
sample 7:
|
||||||
|
time = 233566
|
||||||
|
flags = 0
|
||||||
|
data = length 455, hash B9CCE047
|
||||||
|
sample 8:
|
||||||
|
time = 300300
|
||||||
|
flags = 0
|
||||||
|
data = length 467, hash 69806D94
|
||||||
|
sample 9:
|
||||||
|
time = 467133
|
||||||
|
flags = 0
|
||||||
|
data = length 4549, hash 3944F501
|
||||||
|
sample 10:
|
||||||
|
time = 400400
|
||||||
|
flags = 0
|
||||||
|
data = length 1087, hash 491BF106
|
||||||
|
sample 11:
|
||||||
|
time = 367033
|
||||||
|
flags = 0
|
||||||
|
data = length 380, hash 5FED016A
|
||||||
|
sample 12:
|
||||||
|
time = 433766
|
||||||
|
flags = 0
|
||||||
|
data = length 455, hash 8A0610
|
||||||
|
sample 13:
|
||||||
|
time = 600600
|
||||||
|
flags = 0
|
||||||
|
data = length 5190, hash B9031D8
|
||||||
|
sample 14:
|
||||||
|
time = 533866
|
||||||
|
flags = 0
|
||||||
|
data = length 1071, hash 684E7DC8
|
||||||
|
sample 15:
|
||||||
|
time = 500500
|
||||||
|
flags = 0
|
||||||
|
data = length 653, hash 8494F326
|
||||||
|
sample 16:
|
||||||
|
time = 567233
|
||||||
|
flags = 0
|
||||||
|
data = length 485, hash 2CCC85F4
|
||||||
|
sample 17:
|
||||||
|
time = 734066
|
||||||
|
flags = 0
|
||||||
|
data = length 4884, hash D16B6A96
|
||||||
|
sample 18:
|
||||||
|
time = 667333
|
||||||
|
flags = 0
|
||||||
|
data = length 997, hash 164FF210
|
||||||
|
sample 19:
|
||||||
|
time = 633966
|
||||||
|
flags = 0
|
||||||
|
data = length 640, hash F664125B
|
||||||
|
sample 20:
|
||||||
|
time = 700700
|
||||||
|
flags = 0
|
||||||
|
data = length 491, hash B5930C7C
|
||||||
|
sample 21:
|
||||||
|
time = 867533
|
||||||
|
flags = 0
|
||||||
|
data = length 2989, hash 92CF4FCF
|
||||||
|
sample 22:
|
||||||
|
time = 800800
|
||||||
|
flags = 0
|
||||||
|
data = length 838, hash 294A3451
|
||||||
|
sample 23:
|
||||||
|
time = 767433
|
||||||
|
flags = 0
|
||||||
|
data = length 544, hash FCCE2DE6
|
||||||
|
sample 24:
|
||||||
|
time = 834166
|
||||||
|
flags = 0
|
||||||
|
data = length 329, hash A654FFA1
|
||||||
|
sample 25:
|
||||||
|
time = 1001000
|
||||||
|
flags = 0
|
||||||
|
data = length 1517, hash 5F7EBF8B
|
||||||
|
sample 26:
|
||||||
|
time = 934266
|
||||||
|
flags = 0
|
||||||
|
data = length 803, hash 7A5C4C1D
|
||||||
|
sample 27:
|
||||||
|
time = 900900
|
||||||
|
flags = 0
|
||||||
|
data = length 415, hash B31BBC3B
|
||||||
|
sample 28:
|
||||||
|
time = 967633
|
||||||
|
flags = 0
|
||||||
|
data = length 415, hash 850DFEA3
|
||||||
|
sample 29:
|
||||||
|
time = 1034366
|
||||||
|
flags = 0
|
||||||
|
data = length 619, hash AB5E56CA
|
||||||
|
track 1:
|
||||||
|
total output bytes = 18257
|
||||||
|
sample count = 46
|
||||||
|
format 0:
|
||||||
|
averageBitrate = 2147483647
|
||||||
|
peakBitrate = 2147483647
|
||||||
|
id = 2
|
||||||
|
sampleMimeType = audio/mp4a-latm
|
||||||
|
codecs = mp4a.40.2
|
||||||
|
channelCount = 1
|
||||||
|
sampleRate = 44100
|
||||||
|
language = und
|
||||||
|
initializationData:
|
||||||
|
data = length 5, hash 2B7623A
|
||||||
|
sample 0:
|
||||||
|
time = 0
|
||||||
|
flags = 1
|
||||||
|
data = length 18, hash 96519432
|
||||||
|
sample 1:
|
||||||
|
time = 23219
|
||||||
|
flags = 1
|
||||||
|
data = length 4, hash EE9DF
|
||||||
|
sample 2:
|
||||||
|
time = 46439
|
||||||
|
flags = 1
|
||||||
|
data = length 4, hash EEDBF
|
||||||
|
sample 3:
|
||||||
|
time = 69659
|
||||||
|
flags = 1
|
||||||
|
data = length 157, hash E2F078F4
|
||||||
|
sample 4:
|
||||||
|
time = 92879
|
||||||
|
flags = 1
|
||||||
|
data = length 371, hash B9471F94
|
||||||
|
sample 5:
|
||||||
|
time = 116099
|
||||||
|
flags = 1
|
||||||
|
data = length 373, hash 2AB265CB
|
||||||
|
sample 6:
|
||||||
|
time = 139319
|
||||||
|
flags = 1
|
||||||
|
data = length 402, hash 1295477C
|
||||||
|
sample 7:
|
||||||
|
time = 162539
|
||||||
|
flags = 1
|
||||||
|
data = length 455, hash 2D8146C8
|
||||||
|
sample 8:
|
||||||
|
time = 185759
|
||||||
|
flags = 1
|
||||||
|
data = length 434, hash F2C5D287
|
||||||
|
sample 9:
|
||||||
|
time = 208979
|
||||||
|
flags = 1
|
||||||
|
data = length 450, hash 84143FCD
|
||||||
|
sample 10:
|
||||||
|
time = 232199
|
||||||
|
flags = 1
|
||||||
|
data = length 429, hash EF769D50
|
||||||
|
sample 11:
|
||||||
|
time = 255419
|
||||||
|
flags = 1
|
||||||
|
data = length 450, hash EC3DE692
|
||||||
|
sample 12:
|
||||||
|
time = 278639
|
||||||
|
flags = 1
|
||||||
|
data = length 447, hash 3E519E13
|
||||||
|
sample 13:
|
||||||
|
time = 301859
|
||||||
|
flags = 1
|
||||||
|
data = length 457, hash 1E4F23A0
|
||||||
|
sample 14:
|
||||||
|
time = 325079
|
||||||
|
flags = 1
|
||||||
|
data = length 447, hash A439EA97
|
||||||
|
sample 15:
|
||||||
|
time = 348299
|
||||||
|
flags = 1
|
||||||
|
data = length 456, hash 1E9034C6
|
||||||
|
sample 16:
|
||||||
|
time = 371519
|
||||||
|
flags = 1
|
||||||
|
data = length 398, hash 99DB7345
|
||||||
|
sample 17:
|
||||||
|
time = 394739
|
||||||
|
flags = 1
|
||||||
|
data = length 474, hash 3F05F10A
|
||||||
|
sample 18:
|
||||||
|
time = 417959
|
||||||
|
flags = 1
|
||||||
|
data = length 416, hash C105EE09
|
||||||
|
sample 19:
|
||||||
|
time = 441179
|
||||||
|
flags = 1
|
||||||
|
data = length 454, hash 5FDBE458
|
||||||
|
sample 20:
|
||||||
|
time = 464399
|
||||||
|
flags = 1
|
||||||
|
data = length 438, hash 41A93AC3
|
||||||
|
sample 21:
|
||||||
|
time = 487619
|
||||||
|
flags = 1
|
||||||
|
data = length 443, hash 10FDA652
|
||||||
|
sample 22:
|
||||||
|
time = 510839
|
||||||
|
flags = 1
|
||||||
|
data = length 412, hash 1F791E25
|
||||||
|
sample 23:
|
||||||
|
time = 534058
|
||||||
|
flags = 1
|
||||||
|
data = length 482, hash A6D983D
|
||||||
|
sample 24:
|
||||||
|
time = 557278
|
||||||
|
flags = 1
|
||||||
|
data = length 386, hash BED7392F
|
||||||
|
sample 25:
|
||||||
|
time = 580498
|
||||||
|
flags = 1
|
||||||
|
data = length 463, hash 5309F8C9
|
||||||
|
sample 26:
|
||||||
|
time = 603718
|
||||||
|
flags = 1
|
||||||
|
data = length 394, hash 21C7321F
|
||||||
|
sample 27:
|
||||||
|
time = 626938
|
||||||
|
flags = 1
|
||||||
|
data = length 489, hash 71B4730D
|
||||||
|
sample 28:
|
||||||
|
time = 650158
|
||||||
|
flags = 1
|
||||||
|
data = length 403, hash D9C6DE89
|
||||||
|
sample 29:
|
||||||
|
time = 673378
|
||||||
|
flags = 1
|
||||||
|
data = length 447, hash 9B14B73B
|
||||||
|
sample 30:
|
||||||
|
time = 696598
|
||||||
|
flags = 1
|
||||||
|
data = length 439, hash 4760D35B
|
||||||
|
sample 31:
|
||||||
|
time = 719818
|
||||||
|
flags = 1
|
||||||
|
data = length 463, hash 1601F88D
|
||||||
|
sample 32:
|
||||||
|
time = 743038
|
||||||
|
flags = 1
|
||||||
|
data = length 423, hash D4AE6773
|
||||||
|
sample 33:
|
||||||
|
time = 766258
|
||||||
|
flags = 1
|
||||||
|
data = length 497, hash A3C674D3
|
||||||
|
sample 34:
|
||||||
|
time = 789478
|
||||||
|
flags = 1
|
||||||
|
data = length 419, hash D3734A1F
|
||||||
|
sample 35:
|
||||||
|
time = 812698
|
||||||
|
flags = 1
|
||||||
|
data = length 474, hash DFB41F9
|
||||||
|
sample 36:
|
||||||
|
time = 835918
|
||||||
|
flags = 1
|
||||||
|
data = length 413, hash 53E7CB9F
|
||||||
|
sample 37:
|
||||||
|
time = 859138
|
||||||
|
flags = 1
|
||||||
|
data = length 445, hash D15B0E39
|
||||||
|
sample 38:
|
||||||
|
time = 882358
|
||||||
|
flags = 1
|
||||||
|
data = length 453, hash 77ED81E4
|
||||||
|
sample 39:
|
||||||
|
time = 905578
|
||||||
|
flags = 1
|
||||||
|
data = length 545, hash 3321AEB9
|
||||||
|
sample 40:
|
||||||
|
time = 928798
|
||||||
|
flags = 1
|
||||||
|
data = length 317, hash F557D0E
|
||||||
|
sample 41:
|
||||||
|
time = 952018
|
||||||
|
flags = 1
|
||||||
|
data = length 537, hash ED58CF7B
|
||||||
|
sample 42:
|
||||||
|
time = 975238
|
||||||
|
flags = 1
|
||||||
|
data = length 458, hash 51CDAA10
|
||||||
|
sample 43:
|
||||||
|
time = 998458
|
||||||
|
flags = 1
|
||||||
|
data = length 465, hash CBA1EFD7
|
||||||
|
sample 44:
|
||||||
|
time = 1021678
|
||||||
|
flags = 1
|
||||||
|
data = length 446, hash D6735B8A
|
||||||
|
sample 45:
|
||||||
|
time = 1044897
|
||||||
|
flags = 1
|
||||||
|
data = length 10, hash A453EEBE
|
||||||
|
tracksEnded = true
|
||||||
BIN
testdata/src/test/assets/media/mp4/sample_fragmented_large_bitrates.mp4
vendored
Normal file
BIN
testdata/src/test/assets/media/mp4/sample_fragmented_large_bitrates.mp4
vendored
Normal file
Binary file not shown.
30
testdata/src/test/assets/media/mpd/sample_mpd_clear_key_license_url
vendored
Normal file
30
testdata/src/test/assets/media/mpd/sample_mpd_clear_key_license_url
vendored
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
Includes ContentProtection elements with additional ClearKey license URLs.
|
||||||
|
Covers all possible locations (in AdaptationSet and Representation) and possible orders of these
|
||||||
|
ContentProtection elements (CENC first or ClearKey first).
|
||||||
|
-->
|
||||||
|
<MPD xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:mpeg:DASH:schema:MPD:2011" xsi:schemaLocation="urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd" minBufferTime="PT1.500S" profiles="urn:mpeg:dash:profile:isoff-main:2011" type="static" availabilityStartTime="2016-10-14T17:00:17" xmlns:cenc="urn:mpeg:cenc:2013" xmlns:clearkey="http://dashif.org/guidelines/clearKey">
|
||||||
|
<Period start="PT0.000S" duration="PT0H5M50S">
|
||||||
|
<SegmentTemplate startNumber="0" timescale="1000" media="sq/$Number$">
|
||||||
|
<SegmentTimeline>
|
||||||
|
<S d="2002" t="6009" r="2"/>
|
||||||
|
</SegmentTimeline>
|
||||||
|
</SegmentTemplate>
|
||||||
|
<AdaptationSet id="0" mimeType="audio/mp4" subsegmentAlignment="true">
|
||||||
|
<Representation id="140" codecs="mp4a.40.2" audioSamplingRate="48000" startWithSAP="1" bandwidth="144000">
|
||||||
|
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc" cenc:default_KID="9eb4050d-e44b-4802-932e-27d75083e266" />
|
||||||
|
<ContentProtection value="ClearKey1.0" schemeIdUri="urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e">
|
||||||
|
<clearkey:Laurl Lic_type="EME-1.0">https://testserver1.test/AcquireLicense</clearkey:Laurl>
|
||||||
|
</ContentProtection>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
<AdaptationSet id="1" mimeType="video/mp4" subsegmentAlignment="true">
|
||||||
|
<ContentProtection value="ClearKey1.0" schemeIdUri="urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e">
|
||||||
|
<clearkey:Laurl Lic_type="EME-1.0">https://testserver2.test/AcquireLicense</clearkey:Laurl>
|
||||||
|
</ContentProtection>
|
||||||
|
<ContentProtection schemeIdUri="urn:mpeg:dash:mp4protection:2011" value="cenc" cenc:default_KID="9eb4050d-e44b-4802-932e-27d75083e266" />
|
||||||
|
<Representation id="133" codecs="avc1.4d4015" width="426" height="240" startWithSAP="1" bandwidth="258000" frameRate="30" />
|
||||||
|
</AdaptationSet>
|
||||||
|
</Period>
|
||||||
|
</MPD>
|
||||||
82
testdata/src/test/assets/playbackdumps/mp4/sample_fragmented_large_bitrates.mp4.dump
vendored
Normal file
82
testdata/src/test/assets/playbackdumps/mp4/sample_fragmented_large_bitrates.mp4.dump
vendored
Normal file
|
|
@ -0,0 +1,82 @@
|
||||||
|
MediaCodecAdapter (exotest.audio.aac):
|
||||||
|
buffers.length = 47
|
||||||
|
buffers[0] = length 18, hash 96519432
|
||||||
|
buffers[1] = length 4, hash EE9DF
|
||||||
|
buffers[2] = length 4, hash EEDBF
|
||||||
|
buffers[3] = length 157, hash E2F078F4
|
||||||
|
buffers[4] = length 371, hash B9471F94
|
||||||
|
buffers[5] = length 373, hash 2AB265CB
|
||||||
|
buffers[6] = length 402, hash 1295477C
|
||||||
|
buffers[7] = length 455, hash 2D8146C8
|
||||||
|
buffers[8] = length 434, hash F2C5D287
|
||||||
|
buffers[9] = length 450, hash 84143FCD
|
||||||
|
buffers[10] = length 429, hash EF769D50
|
||||||
|
buffers[11] = length 450, hash EC3DE692
|
||||||
|
buffers[12] = length 447, hash 3E519E13
|
||||||
|
buffers[13] = length 457, hash 1E4F23A0
|
||||||
|
buffers[14] = length 447, hash A439EA97
|
||||||
|
buffers[15] = length 456, hash 1E9034C6
|
||||||
|
buffers[16] = length 398, hash 99DB7345
|
||||||
|
buffers[17] = length 474, hash 3F05F10A
|
||||||
|
buffers[18] = length 416, hash C105EE09
|
||||||
|
buffers[19] = length 454, hash 5FDBE458
|
||||||
|
buffers[20] = length 438, hash 41A93AC3
|
||||||
|
buffers[21] = length 443, hash 10FDA652
|
||||||
|
buffers[22] = length 412, hash 1F791E25
|
||||||
|
buffers[23] = length 482, hash A6D983D
|
||||||
|
buffers[24] = length 386, hash BED7392F
|
||||||
|
buffers[25] = length 463, hash 5309F8C9
|
||||||
|
buffers[26] = length 394, hash 21C7321F
|
||||||
|
buffers[27] = length 489, hash 71B4730D
|
||||||
|
buffers[28] = length 403, hash D9C6DE89
|
||||||
|
buffers[29] = length 447, hash 9B14B73B
|
||||||
|
buffers[30] = length 439, hash 4760D35B
|
||||||
|
buffers[31] = length 463, hash 1601F88D
|
||||||
|
buffers[32] = length 423, hash D4AE6773
|
||||||
|
buffers[33] = length 497, hash A3C674D3
|
||||||
|
buffers[34] = length 419, hash D3734A1F
|
||||||
|
buffers[35] = length 474, hash DFB41F9
|
||||||
|
buffers[36] = length 413, hash 53E7CB9F
|
||||||
|
buffers[37] = length 445, hash D15B0E39
|
||||||
|
buffers[38] = length 453, hash 77ED81E4
|
||||||
|
buffers[39] = length 545, hash 3321AEB9
|
||||||
|
buffers[40] = length 317, hash F557D0E
|
||||||
|
buffers[41] = length 537, hash ED58CF7B
|
||||||
|
buffers[42] = length 458, hash 51CDAA10
|
||||||
|
buffers[43] = length 465, hash CBA1EFD7
|
||||||
|
buffers[44] = length 446, hash D6735B8A
|
||||||
|
buffers[45] = length 10, hash A453EEBE
|
||||||
|
buffers[46] = length 0, hash 1
|
||||||
|
MediaCodecAdapter (exotest.video.avc):
|
||||||
|
buffers.length = 31
|
||||||
|
buffers[0] = length 38070, hash B58E1AEE
|
||||||
|
buffers[1] = length 8340, hash 8AC449FF
|
||||||
|
buffers[2] = length 1295, hash C0DA5090
|
||||||
|
buffers[3] = length 469, hash D6E0A200
|
||||||
|
buffers[4] = length 564, hash E5F56C5B
|
||||||
|
buffers[5] = length 6075, hash 8756E49E
|
||||||
|
buffers[6] = length 847, hash DCC2B618
|
||||||
|
buffers[7] = length 455, hash B9CCE047
|
||||||
|
buffers[8] = length 467, hash 69806D94
|
||||||
|
buffers[9] = length 4549, hash 3944F501
|
||||||
|
buffers[10] = length 1087, hash 491BF106
|
||||||
|
buffers[11] = length 380, hash 5FED016A
|
||||||
|
buffers[12] = length 455, hash 8A0610
|
||||||
|
buffers[13] = length 5190, hash B9031D8
|
||||||
|
buffers[14] = length 1071, hash 684E7DC8
|
||||||
|
buffers[15] = length 653, hash 8494F326
|
||||||
|
buffers[16] = length 485, hash 2CCC85F4
|
||||||
|
buffers[17] = length 4884, hash D16B6A96
|
||||||
|
buffers[18] = length 997, hash 164FF210
|
||||||
|
buffers[19] = length 640, hash F664125B
|
||||||
|
buffers[20] = length 491, hash B5930C7C
|
||||||
|
buffers[21] = length 2989, hash 92CF4FCF
|
||||||
|
buffers[22] = length 838, hash 294A3451
|
||||||
|
buffers[23] = length 544, hash FCCE2DE6
|
||||||
|
buffers[24] = length 329, hash A654FFA1
|
||||||
|
buffers[25] = length 1517, hash 5F7EBF8B
|
||||||
|
buffers[26] = length 803, hash 7A5C4C1D
|
||||||
|
buffers[27] = length 415, hash B31BBC3B
|
||||||
|
buffers[28] = length 415, hash 850DFEA3
|
||||||
|
buffers[29] = length 619, hash AB5E56CA
|
||||||
|
buffers[30] = length 0, hash 1
|
||||||
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.testutil;
|
package com.google.android.exoplayer2.testutil;
|
||||||
|
|
||||||
|
import static com.google.android.exoplayer2.testutil.TestUtil.timelinesAreSame;
|
||||||
|
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
@ -763,7 +765,7 @@ public abstract class Action {
|
||||||
@Nullable Timeline expectedTimeline,
|
@Nullable Timeline expectedTimeline,
|
||||||
@Player.TimelineChangeReason int expectedReason) {
|
@Player.TimelineChangeReason int expectedReason) {
|
||||||
super(tag, "WaitForTimelineChanged");
|
super(tag, "WaitForTimelineChanged");
|
||||||
this.expectedTimeline = expectedTimeline != null ? new NoUidTimeline(expectedTimeline) : null;
|
this.expectedTimeline = expectedTimeline;
|
||||||
this.ignoreExpectedReason = false;
|
this.ignoreExpectedReason = false;
|
||||||
this.expectedReason = expectedReason;
|
this.expectedReason = expectedReason;
|
||||||
}
|
}
|
||||||
|
|
@ -795,7 +797,7 @@ public abstract class Action {
|
||||||
@Override
|
@Override
|
||||||
public void onTimelineChanged(
|
public void onTimelineChanged(
|
||||||
Timeline timeline, @Player.TimelineChangeReason int reason) {
|
Timeline timeline, @Player.TimelineChangeReason int reason) {
|
||||||
if ((expectedTimeline == null || new NoUidTimeline(timeline).equals(expectedTimeline))
|
if ((expectedTimeline == null || timelinesAreSame(timeline, expectedTimeline))
|
||||||
&& (ignoreExpectedReason || expectedReason == reason)) {
|
&& (ignoreExpectedReason || expectedReason == reason)) {
|
||||||
player.removeListener(this);
|
player.removeListener(this);
|
||||||
nextAction.schedule(player, trackSelector, surface, handler);
|
nextAction.schedule(player, trackSelector, surface, handler);
|
||||||
|
|
@ -803,8 +805,8 @@ public abstract class Action {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
player.addListener(listener);
|
player.addListener(listener);
|
||||||
Timeline currentTimeline = new NoUidTimeline(player.getCurrentTimeline());
|
if (expectedTimeline != null
|
||||||
if (currentTimeline.equals(expectedTimeline)) {
|
&& timelinesAreSame(player.getCurrentTimeline(), expectedTimeline)) {
|
||||||
player.removeListener(listener);
|
player.removeListener(listener);
|
||||||
nextAction.schedule(player, trackSelector, surface, handler);
|
nextAction.schedule(player, trackSelector, surface, handler);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ import com.google.android.exoplayer2.upstream.BandwidthMeter;
|
||||||
import com.google.android.exoplayer2.util.Clock;
|
import com.google.android.exoplayer2.util.Clock;
|
||||||
import com.google.android.exoplayer2.util.HandlerWrapper;
|
import com.google.android.exoplayer2.util.HandlerWrapper;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
import com.google.android.exoplayer2.util.MimeTypes;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -534,11 +535,8 @@ public final class ExoPlayerTestRunner implements Player.Listener, ActionSchedul
|
||||||
* @param timelines A list of expected {@link Timeline}s.
|
* @param timelines A list of expected {@link Timeline}s.
|
||||||
*/
|
*/
|
||||||
public void assertTimelinesSame(Timeline... timelines) {
|
public void assertTimelinesSame(Timeline... timelines) {
|
||||||
assertThat(this.timelines).hasSize(timelines.length);
|
TestUtil.assertTimelinesSame(
|
||||||
for (int i = 0; i < timelines.length; i++) {
|
ImmutableList.copyOf(this.timelines), ImmutableList.copyOf(timelines));
|
||||||
assertThat(new NoUidTimeline(timelines[i]))
|
|
||||||
.isEqualTo(new NoUidTimeline(this.timelines.get(i)));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.MediaItem;
|
import com.google.android.exoplayer2.MediaItem;
|
||||||
import com.google.android.exoplayer2.Player;
|
import com.google.android.exoplayer2.Player;
|
||||||
import com.google.android.exoplayer2.Timeline;
|
import com.google.android.exoplayer2.Timeline;
|
||||||
|
import com.google.android.exoplayer2.source.ShuffleOrder;
|
||||||
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
import com.google.android.exoplayer2.source.ads.AdPlaybackState;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
|
|
@ -273,7 +274,7 @@ public final class FakeTimeline extends Timeline {
|
||||||
private final TimelineWindowDefinition[] windowDefinitions;
|
private final TimelineWindowDefinition[] windowDefinitions;
|
||||||
private final Object[] manifests;
|
private final Object[] manifests;
|
||||||
private final int[] periodOffsets;
|
private final int[] periodOffsets;
|
||||||
private final FakeShuffleOrder fakeShuffleOrder;
|
private final ShuffleOrder shuffleOrder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an ad playback state with the specified number of ads in each of the specified ad
|
* Returns an ad playback state with the specified number of ads in each of the specified ad
|
||||||
|
|
@ -393,6 +394,19 @@ public final class FakeTimeline extends Timeline {
|
||||||
* @param windowDefinitions A list of {@link TimelineWindowDefinition}s.
|
* @param windowDefinitions A list of {@link TimelineWindowDefinition}s.
|
||||||
*/
|
*/
|
||||||
public FakeTimeline(Object[] manifests, TimelineWindowDefinition... windowDefinitions) {
|
public FakeTimeline(Object[] manifests, TimelineWindowDefinition... windowDefinitions) {
|
||||||
|
this(manifests, new FakeShuffleOrder(windowDefinitions.length), windowDefinitions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a fake timeline with the given window definitions and {@link
|
||||||
|
* com.google.android.exoplayer2.source.ShuffleOrder}.
|
||||||
|
*
|
||||||
|
* @param windowDefinitions A list of {@link TimelineWindowDefinition}s.
|
||||||
|
*/
|
||||||
|
public FakeTimeline(
|
||||||
|
Object[] manifests,
|
||||||
|
ShuffleOrder shuffleOrder,
|
||||||
|
TimelineWindowDefinition... windowDefinitions) {
|
||||||
this.manifests = new Object[windowDefinitions.length];
|
this.manifests = new Object[windowDefinitions.length];
|
||||||
System.arraycopy(manifests, 0, this.manifests, 0, min(this.manifests.length, manifests.length));
|
System.arraycopy(manifests, 0, this.manifests, 0, min(this.manifests.length, manifests.length));
|
||||||
this.windowDefinitions = windowDefinitions;
|
this.windowDefinitions = windowDefinitions;
|
||||||
|
|
@ -401,7 +415,7 @@ public final class FakeTimeline extends Timeline {
|
||||||
for (int i = 0; i < windowDefinitions.length; i++) {
|
for (int i = 0; i < windowDefinitions.length; i++) {
|
||||||
periodOffsets[i + 1] = periodOffsets[i] + windowDefinitions[i].periodCount;
|
periodOffsets[i + 1] = periodOffsets[i] + windowDefinitions[i].periodCount;
|
||||||
}
|
}
|
||||||
fakeShuffleOrder = new FakeShuffleOrder(windowDefinitions.length);
|
this.shuffleOrder = shuffleOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -420,7 +434,7 @@ public final class FakeTimeline extends Timeline {
|
||||||
? getFirstWindowIndex(shuffleModeEnabled)
|
? getFirstWindowIndex(shuffleModeEnabled)
|
||||||
: C.INDEX_UNSET;
|
: C.INDEX_UNSET;
|
||||||
}
|
}
|
||||||
return shuffleModeEnabled ? fakeShuffleOrder.getNextIndex(windowIndex) : windowIndex + 1;
|
return shuffleModeEnabled ? shuffleOrder.getNextIndex(windowIndex) : windowIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -434,20 +448,20 @@ public final class FakeTimeline extends Timeline {
|
||||||
? getLastWindowIndex(shuffleModeEnabled)
|
? getLastWindowIndex(shuffleModeEnabled)
|
||||||
: C.INDEX_UNSET;
|
: C.INDEX_UNSET;
|
||||||
}
|
}
|
||||||
return shuffleModeEnabled ? fakeShuffleOrder.getPreviousIndex(windowIndex) : windowIndex - 1;
|
return shuffleModeEnabled ? shuffleOrder.getPreviousIndex(windowIndex) : windowIndex - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getLastWindowIndex(boolean shuffleModeEnabled) {
|
public int getLastWindowIndex(boolean shuffleModeEnabled) {
|
||||||
return shuffleModeEnabled
|
return shuffleModeEnabled
|
||||||
? fakeShuffleOrder.getLastIndex()
|
? shuffleOrder.getLastIndex()
|
||||||
: super.getLastWindowIndex(/* shuffleModeEnabled= */ false);
|
: super.getLastWindowIndex(/* shuffleModeEnabled= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getFirstWindowIndex(boolean shuffleModeEnabled) {
|
public int getFirstWindowIndex(boolean shuffleModeEnabled) {
|
||||||
return shuffleModeEnabled
|
return shuffleModeEnabled
|
||||||
? fakeShuffleOrder.getFirstIndex()
|
? shuffleOrder.getFirstIndex()
|
||||||
: super.getFirstWindowIndex(/* shuffleModeEnabled= */ false);
|
: super.getFirstWindowIndex(/* shuffleModeEnabled= */ false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (C) 2019 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package com.google.android.exoplayer2.testutil;
|
|
||||||
|
|
||||||
import com.google.android.exoplayer2.Timeline;
|
|
||||||
import com.google.android.exoplayer2.source.ForwardingTimeline;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A timeline which wraps another timeline and overrides all window and period uids to 0. This is
|
|
||||||
* useful for testing timeline equality without taking uids into account.
|
|
||||||
*/
|
|
||||||
public class NoUidTimeline extends ForwardingTimeline {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an instance.
|
|
||||||
*
|
|
||||||
* @param timeline The underlying timeline.
|
|
||||||
*/
|
|
||||||
public NoUidTimeline(Timeline timeline) {
|
|
||||||
super(timeline);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
|
|
||||||
timeline.getWindow(windowIndex, window, defaultPositionProjectionUs);
|
|
||||||
window.uid = 0;
|
|
||||||
return window;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
|
|
||||||
timeline.getPeriod(periodIndex, period, setIds);
|
|
||||||
period.uid = 0;
|
|
||||||
return period;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -68,6 +68,7 @@ public class StubPlayer extends BasePlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@Nullable
|
||||||
public PlaybackException getPlayerError() {
|
public PlaybackException getPlayerError() {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.primitives.Bytes;
|
import com.google.common.primitives.Bytes;
|
||||||
|
import com.google.common.truth.Correspondence;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
@ -197,19 +198,33 @@ public class TestUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Asserts that the actual timelines are the same to the expected timelines. This assert differs
|
* Asserts that the actual timelines are the same to the expected timelines. This assert differs
|
||||||
* from testing equality by not comparing period ids which may be different due to id mapping of
|
* from testing equality by not comparing:
|
||||||
* child source period ids.
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Period IDs, which may be different due to ID mapping of child source period IDs.
|
||||||
|
* <li>Shuffle order, which by default is random and non-deterministic.
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param actualTimelines A list of actual {@link Timeline timelines}.
|
* @param actualTimelines A list of actual {@link Timeline timelines}.
|
||||||
* @param expectedTimelines A list of expected {@link Timeline timelines}.
|
* @param expectedTimelines A list of expected {@link Timeline timelines}.
|
||||||
*/
|
*/
|
||||||
public static void assertTimelinesSame(
|
public static void assertTimelinesSame(
|
||||||
List<Timeline> actualTimelines, List<Timeline> expectedTimelines) {
|
List<Timeline> actualTimelines, List<Timeline> expectedTimelines) {
|
||||||
assertThat(actualTimelines).hasSize(expectedTimelines.size());
|
assertThat(actualTimelines)
|
||||||
for (int i = 0; i < actualTimelines.size(); i++) {
|
.comparingElementsUsing(
|
||||||
assertThat(new NoUidTimeline(actualTimelines.get(i)))
|
Correspondence.from(
|
||||||
.isEqualTo(new NoUidTimeline(expectedTimelines.get(i)));
|
TestUtil::timelinesAreSame, "is equal to (ignoring Window.uid and Period.uid)"))
|
||||||
|
.containsExactlyElementsIn(expectedTimelines)
|
||||||
|
.inOrder();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if {@code thisTimeline} is equal to {@code thatTimeline}, ignoring {@link
|
||||||
|
* Timeline.Window#uid} and {@link Timeline.Period#uid} values, and shuffle order.
|
||||||
|
*/
|
||||||
|
public static boolean timelinesAreSame(Timeline thisTimeline, Timeline thatTimeline) {
|
||||||
|
return new NoUidOrShufflingTimeline(thisTimeline)
|
||||||
|
.equals(new NoUidOrShufflingTimeline(thatTimeline));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -492,4 +507,68 @@ public class TestUtil {
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class NoUidOrShufflingTimeline extends Timeline {
|
||||||
|
|
||||||
|
private final Timeline delegate;
|
||||||
|
|
||||||
|
public NoUidOrShufflingTimeline(Timeline timeline) {
|
||||||
|
this.delegate = timeline;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getWindowCount() {
|
||||||
|
return delegate.getWindowCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getNextWindowIndex(int windowIndex, int repeatMode, boolean shuffleModeEnabled) {
|
||||||
|
return delegate.getNextWindowIndex(windowIndex, repeatMode, /* shuffleModeEnabled= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPreviousWindowIndex(int windowIndex, int repeatMode, boolean shuffleModeEnabled) {
|
||||||
|
return delegate.getPreviousWindowIndex(
|
||||||
|
windowIndex, repeatMode, /* shuffleModeEnabled= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getLastWindowIndex(boolean shuffleModeEnabled) {
|
||||||
|
return delegate.getLastWindowIndex(/* shuffleModeEnabled= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getFirstWindowIndex(boolean shuffleModeEnabled) {
|
||||||
|
return delegate.getFirstWindowIndex(/* shuffleModeEnabled= */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
|
||||||
|
delegate.getWindow(windowIndex, window, defaultPositionProjectionUs);
|
||||||
|
window.uid = 0;
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPeriodCount() {
|
||||||
|
return delegate.getPeriodCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Period getPeriod(int periodIndex, Period period, boolean setIds) {
|
||||||
|
delegate.getPeriod(periodIndex, period, setIds);
|
||||||
|
period.uid = 0;
|
||||||
|
return period;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getIndexOfPeriod(Object uid) {
|
||||||
|
return delegate.getIndexOfPeriod(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getUidOfPeriod(int periodIndex) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue