mirror of
https://github.com/samsonjs/media.git
synced 2026-03-26 09:35:47 +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
|
||||
description: What version of ExoPlayer are you using?
|
||||
options:
|
||||
- 2.18.1
|
||||
- 2.18.0
|
||||
- 2.17.1
|
||||
- 2.17.0
|
||||
|
|
|
|||
|
|
@ -1,5 +1,48 @@
|
|||
# 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)
|
||||
|
||||
This release corresponds to the
|
||||
|
|
@ -37,7 +80,9 @@ This release corresponds to the
|
|||
* Rename `TracksInfo` to `Tracks` and `TracksInfo.TrackGroupInfo` to
|
||||
`Tracks.Group`. `Player.getCurrentTracksInfo` and
|
||||
`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
|
||||
`DefaultTrackSelector.Parameters.buildUpon` to return
|
||||
`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
|
||||
subtitle format.
|
||||
* Extractors:
|
||||
* Add support for AVI
|
||||
([#2092](https://github.com/google/ExoPlayer/issues/2092)).
|
||||
* Matroska: Parse `DiscardPadding` for Opus tracks.
|
||||
* MP4: Parse bitrates from `esds` boxes.
|
||||
* Ogg: Allow duplicate Opus ID and comment headers
|
||||
|
|
@ -140,6 +187,8 @@ This release corresponds to the
|
|||
of `DefaultCompositeSequenceableLoaderFactory` can be passed explicitly
|
||||
if required.
|
||||
* RTSP:
|
||||
* Add RTP reader for H263
|
||||
([#63](https://github.com/androidx/media/pull/63)).
|
||||
* Add RTP reader for MPEG4
|
||||
([#35](https://github.com/androidx/media/pull/35)).
|
||||
* Add RTP reader for HEVC
|
||||
|
|
@ -172,10 +221,11 @@ This release corresponds to the
|
|||
AndroidStudio's gradle sync to fail
|
||||
([#9933](https://github.com/google/ExoPlayer/issues/9933)).
|
||||
* Remove deprecated symbols:
|
||||
* Remove `Player.Listener.onTracksChanged`. Use
|
||||
`Player.Listener.onTracksInfoChanged` instead.
|
||||
* Remove `Player.Listener.onTracksChanged(TrackGroupArray,
|
||||
TrackSelectionArray)`. Use `Player.Listener.onTracksChanged(Tracks)`
|
||||
instead.
|
||||
* Remove `Player.getCurrentTrackGroups` and
|
||||
`Player.getCurrentTrackSelections`. Use `Player.getCurrentTracksInfo`
|
||||
`Player.getCurrentTrackSelections`. Use `Player.getCurrentTracks`
|
||||
instead. You can also continue to use `ExoPlayer.getCurrentTrackGroups`
|
||||
and `ExoPlayer.getCurrentTrackSelections`, although these methods remain
|
||||
deprecated.
|
||||
|
|
@ -371,7 +421,7 @@ This release corresponds to the
|
|||
when creating `PendingIntent`s
|
||||
([#9528](https://github.com/google/ExoPlayer/issues/9528)).
|
||||
* Remove deprecated symbols:
|
||||
* Remove `Player.EventLister`. Use `Player.Listener` instead.
|
||||
* Remove `Player.EventListener`. Use `Player.Listener` instead.
|
||||
* Remove `MediaSourceFactory.setDrmSessionManager`,
|
||||
`MediaSourceFactory.setDrmHttpDataSourceFactory`, and
|
||||
`MediaSourceFactory.setDrmUserAgent`. Use
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
// limitations under the License.
|
||||
project.ext {
|
||||
// ExoPlayer version and version code.
|
||||
releaseVersion = '2.18.0'
|
||||
releaseVersionCode = 2_018_000
|
||||
releaseVersion = '2.18.1'
|
||||
releaseVersionCode = 2_018_001
|
||||
minSdkVersion = 16
|
||||
appTargetSdkVersion = 29
|
||||
// Upgrading this requires [Internal ref: b/193254928] to be fixed, or some
|
||||
|
|
|
|||
|
|
@ -78,6 +78,12 @@
|
|||
<data android:scheme="file"/>
|
||||
<data android:scheme="ssai"/>
|
||||
</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>
|
||||
<action android:name="com.google.android.exoplayer.demo.action.VIEW_LIST"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
|
|
|
|||
|
|
@ -43,6 +43,12 @@
|
|||
<data android:scheme="asset"/>
|
||||
<data android:scheme="file"/>
|
||||
</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>
|
||||
|
||||
</application>
|
||||
|
|
|
|||
|
|
@ -49,6 +49,12 @@
|
|||
<data android:scheme="asset"/>
|
||||
<data android:scheme="file"/>
|
||||
</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 android:name=".TransformerActivity"
|
||||
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/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.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/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>
|
||||
|
|
@ -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/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/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/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>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -3049,6 +3049,20 @@ static final int COMMAND_SEEK_TO_WINDOW</pre>
|
|||
</dl>
|
||||
</li>
|
||||
</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>
|
||||
|
|
@ -3203,20 +3217,6 @@ static final int COMMAND_SEEK_TO_WINDOW</pre>
|
|||
</dl>
|
||||
</li>
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -500,7 +500,8 @@ public final byte[] data</pre>
|
|||
<ul class="blockList">
|
||||
<li class="blockList">
|
||||
<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>
|
||||
<div class="block">Returns a copy of this instance with the specified data.</div>
|
||||
<dl>
|
||||
|
|
|
|||
|
|
@ -517,7 +517,8 @@ public static <a href="DrmInitData.html" title="class in com.google.android
|
|||
<ul class="blockList">
|
||||
<li class="blockList">
|
||||
<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>
|
||||
<div class="block">Returns a copy with the specified protection scheme type.</div>
|
||||
<dl>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
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 altColor = "altColor";
|
||||
var rowColor = "rowColor";
|
||||
|
|
@ -477,20 +477,25 @@ extends <a href="../../BasePlayer.html" title="class in com.google.android.exopl
|
|||
</td>
|
||||
</tr>
|
||||
<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>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlaybackParameters()">getPlaybackParameters</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">Returns the currently active playback parameters.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i29" class="rowColor">
|
||||
<tr id="i30" class="altColor">
|
||||
<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>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i30" class="altColor">
|
||||
<tr id="i31" class="rowColor">
|
||||
<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>
|
||||
<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>
|
||||
</td>
|
||||
</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>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlayerError()">getPlayerError</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">Returns the error that caused playback to fail.</div>
|
||||
</td>
|
||||
</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>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlaylistMetadata()">getPlaylistMetadata</a></span>()</code></th>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i33" class="rowColor">
|
||||
<tr id="i34" class="altColor">
|
||||
<td class="colFirst"><code>boolean</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getPlayWhenReady()">getPlayWhenReady</a></span>()</code></th>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i34" class="altColor">
|
||||
<tr id="i35" class="rowColor">
|
||||
<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>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i35" class="rowColor">
|
||||
<tr id="i36" class="altColor">
|
||||
<td class="colFirst"><code>long</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getSeekBackIncrement()">getSeekBackIncrement</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">Returns the <a href="../../Player.html#seekBack()"><code>Player.seekBack()</code></a> increment.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i36" class="altColor">
|
||||
<tr id="i37" class="rowColor">
|
||||
<td class="colFirst"><code>long</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getSeekForwardIncrement()">getSeekForwardIncrement</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">Returns the <a href="../../Player.html#seekForward()"><code>Player.seekForward()</code></a> increment.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i37" class="rowColor">
|
||||
<tr id="i38" class="altColor">
|
||||
<td class="colFirst"><code>boolean</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getShuffleModeEnabled()">getShuffleModeEnabled</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">Returns whether shuffling of media items is enabled.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i38" class="altColor">
|
||||
<tr id="i39" class="rowColor">
|
||||
<td class="colFirst"><code>long</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getTotalBufferedDuration()">getTotalBufferedDuration</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">Returns an estimate of the total buffered duration from the current position, in milliseconds.</div>
|
||||
</td>
|
||||
</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>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getTrackSelectionParameters()">getTrackSelectionParameters</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">Returns the parameters constraining the track selection.</div>
|
||||
</td>
|
||||
</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>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getVideoSize()">getVideoSize</a></span>()</code></th>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i41" class="rowColor">
|
||||
<tr id="i42" class="altColor">
|
||||
<td class="colFirst"><code>float</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#getVolume()">getVolume</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">This method is not supported and returns 1.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i42" class="altColor">
|
||||
<tr id="i43" class="rowColor">
|
||||
<td class="colFirst"><code>void</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#increaseDeviceVolume()">increaseDeviceVolume</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">This method is not supported and does nothing.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i43" class="rowColor">
|
||||
<tr id="i44" class="altColor">
|
||||
<td class="colFirst"><code>boolean</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#isCastSessionAvailable()">isCastSessionAvailable</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">Returns whether a cast session is available.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i44" class="altColor">
|
||||
<tr id="i45" class="rowColor">
|
||||
<td class="colFirst"><code>boolean</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#isDeviceMuted()">isDeviceMuted</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">This method is not supported and always returns <code>false</code>.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i45" class="rowColor">
|
||||
<tr id="i46" class="altColor">
|
||||
<td class="colFirst"><code>boolean</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#isLoading()">isLoading</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">Whether the player is currently loading the source.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i46" class="altColor">
|
||||
<tr id="i47" class="rowColor">
|
||||
<td class="colFirst"><code>boolean</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#isPlayingAd()">isPlayingAd</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">Returns whether the player is currently playing an ad.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i47" class="rowColor">
|
||||
<tr id="i48" class="altColor">
|
||||
<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,
|
||||
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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i48" class="altColor">
|
||||
<tr id="i49" class="rowColor">
|
||||
<td class="colFirst"><code>void</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#prepare()">prepare</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">Prepares the player.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i49" class="rowColor">
|
||||
<tr id="i50" class="altColor">
|
||||
<td class="colFirst"><code>void</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#release()">release</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">Releases the player.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i50" class="altColor">
|
||||
<tr id="i51" class="rowColor">
|
||||
<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>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i51" class="rowColor">
|
||||
<tr id="i52" class="altColor">
|
||||
<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,
|
||||
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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i52" class="altColor">
|
||||
<tr id="i53" class="rowColor">
|
||||
<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,
|
||||
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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i53" class="rowColor">
|
||||
<tr id="i54" class="altColor">
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<div class="block">This method is not supported and does nothing.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i54" class="altColor">
|
||||
<tr id="i55" class="rowColor">
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<div class="block">This method is not supported and does nothing.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i55" class="rowColor">
|
||||
<tr id="i56" class="altColor">
|
||||
<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,
|
||||
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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i56" class="altColor">
|
||||
<tr id="i57" class="rowColor">
|
||||
<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,
|
||||
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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i57" class="rowColor">
|
||||
<tr id="i58" class="altColor">
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<div class="block">Attempts to set the playback parameters.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i58" class="altColor">
|
||||
<tr id="i59" class="rowColor">
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<div class="block">This method is not supported and does nothing.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i59" class="rowColor">
|
||||
<tr id="i60" class="altColor">
|
||||
<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>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i60" class="altColor">
|
||||
<tr id="i61" class="rowColor">
|
||||
<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>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i61" class="rowColor">
|
||||
<tr id="i62" class="altColor">
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<div class="block">Sets a listener for updates on the cast session availability.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i62" class="altColor">
|
||||
<tr id="i63" class="rowColor">
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<div class="block">Sets whether shuffling of media items is enabled.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i63" class="rowColor">
|
||||
<tr id="i64" class="altColor">
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<div class="block">Sets the parameters constraining the track selection.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i64" class="altColor">
|
||||
<tr id="i65" class="rowColor">
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<div class="block">This method is not supported and does nothing.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i65" class="rowColor">
|
||||
<tr id="i66" class="altColor">
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<div class="block">This method is not supported and does nothing.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i66" class="altColor">
|
||||
<tr id="i67" class="rowColor">
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<div class="block">This method is not supported and does nothing.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i67" class="rowColor">
|
||||
<tr id="i68" class="altColor">
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<div class="block">This method is not supported and does nothing.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i68" class="altColor">
|
||||
<tr id="i69" class="rowColor">
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<div class="block">This method is not supported and does nothing.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i69" class="rowColor">
|
||||
<tr id="i70" class="altColor">
|
||||
<td class="colFirst"><code>void</code></td>
|
||||
<th class="colSecond" scope="row"><code><span class="memberNameLink"><a href="#stop()">stop</a></span>()</code></th>
|
||||
<td class="colLast">
|
||||
<div class="block">Stops playback without resetting the playlist.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i70" class="altColor">
|
||||
<tr id="i71" class="rowColor">
|
||||
<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>
|
||||
<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>
|
||||
</li>
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
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 altColor = "altColor";
|
||||
var rowColor = "rowColor";
|
||||
|
|
@ -216,14 +216,6 @@ extends <a href="https://developer.android.com/reference/java/lang/Object.html"
|
|||
</td>
|
||||
</tr>
|
||||
<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>
|
||||
<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">
|
||||
|
|
@ -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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i9" class="rowColor">
|
||||
<tr id="i8" class="altColor">
|
||||
<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,
|
||||
boolean expectedSleepForOffload)</code></th>
|
||||
|
|
@ -240,14 +232,14 @@ extends <a href="https://developer.android.com/reference/java/lang/Object.html"
|
|||
playback error occurs.</div>
|
||||
</td>
|
||||
</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>
|
||||
<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">
|
||||
<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>
|
||||
</tr>
|
||||
<tr id="i11" class="rowColor">
|
||||
<tr id="i10" class="altColor">
|
||||
<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,
|
||||
<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>
|
||||
</li>
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
</dl>
|
||||
<dl>
|
||||
<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>
|
||||
<hr>
|
||||
<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>
|
||||
</tr>
|
||||
<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,
|
||||
<a href="FakeTimeline.TimelineWindowDefinition.html" title="class in com.google.android.exoplayer2.testutil">FakeTimeline.TimelineWindowDefinition</a>... windowDefinitions)</code></th>
|
||||
<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>
|
||||
<ul class="blockListLast">
|
||||
<ul class="blockList">
|
||||
<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,
|
||||
|
|
@ -493,6 +501,22 @@ extends <a href="../Timeline.html" title="class in com.google.android.exoplayer2
|
|||
</dl>
|
||||
</li>
|
||||
</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>
|
||||
</ul>
|
||||
</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">
|
||||
<li class="blockList">
|
||||
<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">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
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
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 altColor = "altColor";
|
||||
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>
|
||||
</td>
|
||||
</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>
|
||||
<ul class="blockList">
|
||||
<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,
|
||||
<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
|
||||
from testing equality by not comparing period ids which may be different due to id mapping of
|
||||
child source period ids.</div>
|
||||
from testing equality by not comparing:
|
||||
|
||||
<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>
|
||||
<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>
|
||||
|
|
@ -627,6 +639,17 @@ extends <a href="https://developer.android.com/reference/java/lang/Object.html"
|
|||
</dl>
|
||||
</li>
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -800,57 +800,51 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
</td>
|
||||
</tr>
|
||||
<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>
|
||||
<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>
|
||||
from every method.</div>
|
||||
</td>
|
||||
</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>
|
||||
<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
|
||||
every method.</div>
|
||||
</td>
|
||||
</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>
|
||||
<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>
|
||||
</td>
|
||||
</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>
|
||||
<td class="colLast">
|
||||
<div class="block">Utility methods for tests.</div>
|
||||
</td>
|
||||
</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>
|
||||
<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>
|
||||
</td>
|
||||
</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>
|
||||
<td class="colLast">
|
||||
<div class="block">A <code>Dispatcher</code> for <code>MockWebServer</code> that allows per-path
|
||||
customisation of the static data served.</div>
|
||||
</td>
|
||||
</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>
|
||||
<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>
|
||||
</td>
|
||||
</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>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
<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="NoUidTimeline.html" title="class in com.google.android.exoplayer2.testutil"><span class="typeNameLink">NoUidTimeline</span></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</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>.
|
||||
|
||||
<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>
|
||||
</ul>
|
||||
</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) {
|
||||
}
|
||||
//-->
|
||||
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 altColor = "altColor";
|
||||
var rowColor = "rowColor";
|
||||
|
|
@ -131,7 +131,8 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
<hr>
|
||||
<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>
|
||||
<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>
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -154,6 +155,13 @@ extends <a href="https://developer.android.com/reference/java/lang/Object.html"
|
|||
</tr>
|
||||
<tr class="altColor">
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<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 id="i8" class="altColor">
|
||||
<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>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i9" class="rowColor">
|
||||
<tr id="i10" class="altColor">
|
||||
<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>
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
<tr id="i10" class="altColor">
|
||||
<tr id="i11" class="rowColor">
|
||||
<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,
|
||||
<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>
|
||||
</tr>
|
||||
<tr id="i11" class="rowColor">
|
||||
<tr id="i12" class="altColor">
|
||||
<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,
|
||||
<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>
|
||||
</li>
|
||||
</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>
|
||||
|
|
|
|||
|
|
@ -153,18 +153,24 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
</td>
|
||||
</tr>
|
||||
<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>
|
||||
<td class="colLast">
|
||||
<div class="block">Tracks the progression of media time.</div>
|
||||
</td>
|
||||
</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>
|
||||
<td class="colLast">
|
||||
<div class="block">A listener for network type changes.</div>
|
||||
</td>
|
||||
</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>
|
||||
<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>
|
||||
|
|
@ -295,7 +301,8 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
<tr class="altColor">
|
||||
<th class="colFirst" scope="row"><a href="Log.html" title="class in com.google.android.exoplayer2.util">Log</a></th>
|
||||
<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>
|
||||
</tr>
|
||||
<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="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="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="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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
<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 class="altColor">
|
||||
<td class="colFirst"><a id="com.google.android.exoplayer2.ExoPlayerLibraryInfo.VERSION_INT">
|
||||
<!-- -->
|
||||
</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>
|
||||
<td class="colLast"><code>2018000</code></td>
|
||||
<td class="colLast"><code>2018001</code></td>
|
||||
</tr>
|
||||
<tr class="rowColor">
|
||||
<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>
|
||||
<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>
|
||||
</tbody>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -7322,6 +7322,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
<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>
|
||||
<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>
|
||||
<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>
|
||||
|
|
@ -8022,6 +8026,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
<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>
|
||||
</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>
|
||||
<dd>
|
||||
<div class="block">Default color for ad markers.</div>
|
||||
|
|
@ -10214,6 +10222,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
<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>
|
||||
<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>
|
||||
<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>
|
||||
|
|
@ -12643,6 +12655,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
<dd>
|
||||
<div class="block">Creates a fake timeline with the given window definitions.</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.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>
|
||||
<dd>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<dd>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
|
|
@ -19189,8 +19205,6 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
<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>
|
||||
<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>
|
||||
<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>
|
||||
|
|
@ -20405,6 +20419,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
<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>
|
||||
<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>
|
||||
<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>
|
||||
|
|
@ -22798,7 +22816,8 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
</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>
|
||||
<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>
|
||||
<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>
|
||||
|
|
@ -22820,6 +22839,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
<dd>
|
||||
<div class="block">Log level to only log warning and error messages.</div>
|
||||
</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>
|
||||
<dd>
|
||||
<div class="block">Log level for ExoPlayer logcat logging.</div>
|
||||
|
|
@ -24808,14 +24831,6 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
</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>
|
||||
<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>
|
||||
<dd>
|
||||
<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
|
||||
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>
|
||||
<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>
|
||||
<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>
|
||||
|
|
@ -35457,6 +35467,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
<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>
|
||||
</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>
|
||||
<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>
|
||||
|
|
@ -40321,6 +40335,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
<dd>
|
||||
<div class="block">Creates an instance for a given <code>MediaSessionCompat</code> and maximum queue size.</div>
|
||||
</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>
|
||||
<dd>
|
||||
<div class="block">Creates a window definition with one period.</div>
|
||||
|
|
@ -42501,6 +42519,10 @@ $('.navPadding').css('padding-top', $('.fixedNav').css("height"));
|
|||
</a>
|
||||
<h2 class="title">W</h2>
|
||||
<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>
|
||||
<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>
|
||||
|
|
|
|||
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>
|
||||
<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/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>
|
||||
</ul>
|
||||
</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/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.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.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>
|
||||
|
|
|
|||
|
|
@ -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"}]
|
||||
|
||||
|
|
|
|||
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_MEDIA_ITEMS_METADATA,
|
||||
COMMAND_SET_MEDIA_ITEMS_METADATA,
|
||||
COMMAND_SET_MEDIA_ITEM,
|
||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||
COMMAND_GET_TRACKS,
|
||||
COMMAND_SET_MEDIA_ITEM)
|
||||
COMMAND_GET_TRACKS)
|
||||
.build();
|
||||
|
||||
public static final float MIN_SPEED_SUPPORTED = 0.5f;
|
||||
|
|
@ -143,6 +143,7 @@ public final class CastPlayer extends BasePlayer {
|
|||
private int pendingSeekWindowIndex;
|
||||
private long pendingSeekPositionMs;
|
||||
@Nullable private PositionInfo pendingMediaItemRemovalPosition;
|
||||
private MediaMetadata mediaMetadata;
|
||||
|
||||
/**
|
||||
* Creates a new cast player.
|
||||
|
|
@ -196,7 +197,7 @@ public final class CastPlayer extends BasePlayer {
|
|||
this.mediaItemConverter = mediaItemConverter;
|
||||
this.seekBackIncrementMs = seekBackIncrementMs;
|
||||
this.seekForwardIncrementMs = seekForwardIncrementMs;
|
||||
timelineTracker = new CastTimelineTracker();
|
||||
timelineTracker = new CastTimelineTracker(mediaItemConverter);
|
||||
period = new Timeline.Period();
|
||||
statusListener = new StatusListener();
|
||||
seekResultCallback = new SeekResultCallback();
|
||||
|
|
@ -210,6 +211,7 @@ public final class CastPlayer extends BasePlayer {
|
|||
playbackParameters = new StateHolder<>(PlaybackParameters.DEFAULT);
|
||||
playbackState = STATE_IDLE;
|
||||
currentTimeline = CastTimeline.EMPTY_CAST_TIMELINE;
|
||||
mediaMetadata = MediaMetadata.EMPTY;
|
||||
currentTracks = Tracks.EMPTY;
|
||||
availableCommands = new Commands.Builder().addAll(PERMANENT_AVAILABLE_COMMANDS).build();
|
||||
pendingSeekWindowIndex = C.INDEX_UNSET;
|
||||
|
|
@ -281,8 +283,7 @@ public final class CastPlayer extends BasePlayer {
|
|||
|
||||
@Override
|
||||
public void setMediaItems(List<MediaItem> mediaItems, int startIndex, long startPositionMs) {
|
||||
setMediaItemsInternal(
|
||||
toMediaQueueItems(mediaItems), startIndex, startPositionMs, repeatMode.value);
|
||||
setMediaItemsInternal(mediaItems, startIndex, startPositionMs, repeatMode.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -292,7 +293,7 @@ public final class CastPlayer extends BasePlayer {
|
|||
if (index < currentTimeline.getWindowCount()) {
|
||||
uid = (int) currentTimeline.getWindow(/* windowIndex= */ index, window).uid;
|
||||
}
|
||||
addMediaItemsInternal(toMediaQueueItems(mediaItems), uid);
|
||||
addMediaItemsInternal(mediaItems, uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -424,6 +425,13 @@ public final class CastPlayer extends BasePlayer {
|
|||
Player.EVENT_MEDIA_ITEM_TRANSITION,
|
||||
listener ->
|
||||
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();
|
||||
} else if (pendingSeekCount == 0) {
|
||||
|
|
@ -561,8 +569,12 @@ public final class CastPlayer extends BasePlayer {
|
|||
|
||||
@Override
|
||||
public MediaMetadata getMediaMetadata() {
|
||||
// CastPlayer does not currently support metadata.
|
||||
return MediaMetadata.EMPTY;
|
||||
return mediaMetadata;
|
||||
}
|
||||
|
||||
public MediaMetadata getMediaMetadataInternal() {
|
||||
MediaItem currentMediaItem = getCurrentMediaItem();
|
||||
return currentMediaItem != null ? currentMediaItem.mediaMetadata : MediaMetadata.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -759,6 +771,7 @@ public final class CastPlayer extends BasePlayer {
|
|||
return;
|
||||
}
|
||||
int oldWindowIndex = this.currentWindowIndex;
|
||||
MediaMetadata oldMediaMetadata = mediaMetadata;
|
||||
@Nullable
|
||||
Object oldPeriodUid =
|
||||
!getCurrentTimeline().isEmpty()
|
||||
|
|
@ -770,6 +783,7 @@ public final class CastPlayer extends BasePlayer {
|
|||
boolean playingPeriodChangedByTimelineChange = updateTimelineAndNotifyIfChanged();
|
||||
Timeline currentTimeline = getCurrentTimeline();
|
||||
currentWindowIndex = fetchCurrentWindowIndex(remoteMediaClient, currentTimeline);
|
||||
mediaMetadata = getMediaMetadataInternal();
|
||||
@Nullable
|
||||
Object currentPeriodUid =
|
||||
!currentTimeline.isEmpty()
|
||||
|
|
@ -823,6 +837,11 @@ public final class CastPlayer extends BasePlayer {
|
|||
listeners.queueEvent(
|
||||
Player.EVENT_TRACKS_CHANGED, listener -> listener.onTracksChanged(currentTracks));
|
||||
}
|
||||
if (!oldMediaMetadata.equals(mediaMetadata)) {
|
||||
listeners.queueEvent(
|
||||
Player.EVENT_MEDIA_METADATA_CHANGED,
|
||||
listener -> listener.onMediaMetadataChanged(mediaMetadata));
|
||||
}
|
||||
updateAvailableCommandsAndNotifyIfChanged();
|
||||
listeners.flushEvents();
|
||||
}
|
||||
|
|
@ -1018,14 +1037,13 @@ public final class CastPlayer extends BasePlayer {
|
|||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PendingResult<MediaChannelResult> setMediaItemsInternal(
|
||||
MediaQueueItem[] mediaQueueItems,
|
||||
private void setMediaItemsInternal(
|
||||
List<MediaItem> mediaItems,
|
||||
int startIndex,
|
||||
long startPositionMs,
|
||||
@RepeatMode int repeatMode) {
|
||||
if (remoteMediaClient == null || mediaQueueItems.length == 0) {
|
||||
return null;
|
||||
if (remoteMediaClient == null || mediaItems.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
startPositionMs = startPositionMs == C.TIME_UNSET ? 0 : startPositionMs;
|
||||
if (startIndex == C.INDEX_UNSET) {
|
||||
|
|
@ -1036,34 +1054,35 @@ public final class CastPlayer extends BasePlayer {
|
|||
if (!currentTimeline.isEmpty()) {
|
||||
pendingMediaItemRemovalPosition = getCurrentPositionInfo();
|
||||
}
|
||||
return remoteMediaClient.queueLoad(
|
||||
MediaQueueItem[] mediaQueueItems = toMediaQueueItems(mediaItems);
|
||||
timelineTracker.onMediaItemsSet(mediaItems, mediaQueueItems);
|
||||
remoteMediaClient.queueLoad(
|
||||
mediaQueueItems,
|
||||
min(startIndex, mediaQueueItems.length - 1),
|
||||
min(startIndex, mediaItems.size() - 1),
|
||||
getCastRepeatMode(repeatMode),
|
||||
startPositionMs,
|
||||
/* customData= */ null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PendingResult<MediaChannelResult> addMediaItemsInternal(MediaQueueItem[] items, int uid) {
|
||||
private void addMediaItemsInternal(List<MediaItem> mediaItems, int uid) {
|
||||
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 PendingResult<MediaChannelResult> moveMediaItemsInternal(
|
||||
int[] uids, int fromIndex, int newIndex) {
|
||||
private void moveMediaItemsInternal(int[] uids, int fromIndex, int newIndex) {
|
||||
if (remoteMediaClient == null || getMediaStatus() == null) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
int insertBeforeIndex = fromIndex < newIndex ? newIndex + uids.length : newIndex;
|
||||
int insertBeforeItemId = MediaQueueItem.INVALID_ITEM_ID;
|
||||
if (insertBeforeIndex < currentTimeline.getWindowCount()) {
|
||||
insertBeforeItemId = (int) currentTimeline.getWindow(insertBeforeIndex, window).uid;
|
||||
}
|
||||
return remoteMediaClient.queueReorderItems(uids, insertBeforeItemId, /* customData= */ null);
|
||||
remoteMediaClient.queueReorderItems(uids, insertBeforeItemId, /* customData= */ null);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
|
|
|||
|
|
@ -15,13 +15,13 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.ext.cast;
|
||||
|
||||
import android.net.Uri;
|
||||
import android.util.SparseArray;
|
||||
import android.util.SparseIntArray;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.gms.cast.MediaInfo;
|
||||
import java.util.Arrays;
|
||||
|
||||
/** 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. */
|
||||
public static final class ItemData {
|
||||
|
||||
/* package */ static final String UNKNOWN_CONTENT_ID = "UNKNOWN_CONTENT_ID";
|
||||
|
||||
/** Holds no media information. */
|
||||
public static final ItemData EMPTY =
|
||||
new ItemData(
|
||||
/* durationUs= */ 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. */
|
||||
public final long durationUs;
|
||||
|
|
@ -45,6 +49,10 @@ import java.util.Arrays;
|
|||
public final long defaultPositionUs;
|
||||
/** Whether the item is live content, or {@code false} if unknown. */
|
||||
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.
|
||||
|
|
@ -52,11 +60,20 @@ import java.util.Arrays;
|
|||
* @param durationUs See {@link #durationsUs}.
|
||||
* @param defaultPositionUs See {@link #defaultPositionUs}.
|
||||
* @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.defaultPositionUs = defaultPositionUs;
|
||||
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}
|
||||
* 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
|
||||
&& defaultPositionUs == this.defaultPositionUs
|
||||
&& isLive == this.isLive) {
|
||||
&& isLive == this.isLive
|
||||
&& contentId.equals(this.contentId)
|
||||
&& mediaItem.equals(this.mediaItem)) {
|
||||
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<>());
|
||||
|
||||
private final SparseIntArray idsToIndex;
|
||||
private final MediaItem[] mediaItems;
|
||||
private final int[] ids;
|
||||
private final long[] durationsUs;
|
||||
private final long[] defaultPositionsUs;
|
||||
|
|
@ -100,10 +127,12 @@ import java.util.Arrays;
|
|||
durationsUs = new long[itemCount];
|
||||
defaultPositionsUs = new long[itemCount];
|
||||
isLive = new boolean[itemCount];
|
||||
mediaItems = new MediaItem[itemCount];
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
int id = ids[i];
|
||||
idsToIndex.put(id, i);
|
||||
ItemData data = itemIdToData.get(id, ItemData.EMPTY);
|
||||
mediaItems[i] = data.mediaItem;
|
||||
durationsUs[i] = data.durationUs;
|
||||
defaultPositionsUs[i] = data.defaultPositionUs == C.TIME_UNSET ? 0 : data.defaultPositionUs;
|
||||
isLive[i] = data.isLive;
|
||||
|
|
@ -121,18 +150,16 @@ import java.util.Arrays;
|
|||
public Window getWindow(int windowIndex, Window window, long defaultPositionProjectionUs) {
|
||||
long durationUs = durationsUs[windowIndex];
|
||||
boolean isDynamic = durationUs == C.TIME_UNSET;
|
||||
MediaItem mediaItem =
|
||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(ids[windowIndex]).build();
|
||||
return window.set(
|
||||
/* uid= */ ids[windowIndex],
|
||||
/* mediaItem= */ mediaItem,
|
||||
/* mediaItem= */ mediaItems[windowIndex],
|
||||
/* manifest= */ null,
|
||||
/* presentationStartTimeMs= */ C.TIME_UNSET,
|
||||
/* windowStartTimeMs= */ C.TIME_UNSET,
|
||||
/* elapsedRealtimeEpochOffsetMs= */ C.TIME_UNSET,
|
||||
/* isSeekable= */ !isDynamic,
|
||||
isDynamic,
|
||||
isLive[windowIndex] ? mediaItem.liveConfiguration : null,
|
||||
isLive[windowIndex] ? mediaItems[windowIndex].liveConfiguration : null,
|
||||
defaultPositionsUs[windowIndex],
|
||||
durationUs,
|
||||
/* firstPeriodIndex= */ windowIndex,
|
||||
|
|
|
|||
|
|
@ -15,14 +15,23 @@
|
|||
*/
|
||||
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 androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
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.MediaQueueItem;
|
||||
import com.google.android.gms.cast.MediaStatus;
|
||||
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Creates {@link CastTimeline CastTimelines} from cast receiver app status updates.
|
||||
|
|
@ -33,9 +42,47 @@ import java.util.HashSet;
|
|||
/* package */ final class CastTimelineTracker {
|
||||
|
||||
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<>();
|
||||
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();
|
||||
String currentContentId = checkStateNotNull(mediaStatus.getMediaInfo()).getContentId();
|
||||
MediaItem mediaItem = mediaItemsByContentId.get(currentContentId);
|
||||
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()) {
|
||||
long defaultPositionUs = (long) (item.getStartTime() * C.MICROS_PER_SECOND);
|
||||
updateItemData(item.getItemId(), item.getMedia(), defaultPositionUs);
|
||||
for (MediaQueueItem queueItem : mediaStatus.getQueueItems()) {
|
||||
long defaultPositionUs = (long) (queueItem.getStartTime() * C.MICROS_PER_SECOND);
|
||||
@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);
|
||||
}
|
||||
|
||||
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);
|
||||
long durationUs = CastUtils.getStreamDurationUs(mediaInfo);
|
||||
if (durationUs == C.TIME_UNSET) {
|
||||
|
|
@ -87,7 +152,10 @@ import java.util.HashSet;
|
|||
if (defaultPositionUs == C.TIME_UNSET) {
|
||||
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) {
|
||||
|
|
@ -99,6 +167,8 @@ import java.util.HashSet;
|
|||
int index = 0;
|
||||
while (index < itemIdToData.size()) {
|
||||
if (!scratchItemIds.contains(itemIdToData.keyAt(index))) {
|
||||
CastTimeline.ItemData itemData = itemIdToData.valueAt(index);
|
||||
mediaItemsByContentId.remove(itemData.contentId);
|
||||
itemIdToData.removeAt(index);
|
||||
} else {
|
||||
index++;
|
||||
|
|
|
|||
|
|
@ -126,11 +126,14 @@ public final class DefaultMediaItemConverter implements MediaItemConverter {
|
|||
if (mediaItem.mediaMetadata.trackNumber != null) {
|
||||
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 =
|
||||
new MediaInfo.Builder(mediaItem.localConfiguration.uri.toString())
|
||||
new MediaInfo.Builder(contentId)
|
||||
.setStreamType(MediaInfo.STREAM_TYPE_BUFFERED)
|
||||
.setContentType(mediaItem.localConfiguration.mimeType)
|
||||
.setContentUrl(contentUrl)
|
||||
.setMetadata(metadata)
|
||||
.setCustomData(getCustomData(mediaItem))
|
||||
.build();
|
||||
|
|
|
|||
|
|
@ -64,8 +64,10 @@ import android.net.Uri;
|
|||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.MediaMetadata;
|
||||
import com.google.android.exoplayer2.PlaybackParameters;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
import com.google.android.exoplayer2.Player.Listener;
|
||||
import com.google.android.exoplayer2.Timeline;
|
||||
import com.google.android.exoplayer2.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
|
|
@ -98,6 +100,7 @@ import org.mockito.Mockito;
|
|||
public class CastPlayerTest {
|
||||
|
||||
private CastPlayer castPlayer;
|
||||
private DefaultMediaItemConverter mediaItemConverter;
|
||||
private RemoteMediaClient.Callback remoteMediaClientCallback;
|
||||
|
||||
@Mock private RemoteMediaClient mockRemoteMediaClient;
|
||||
|
|
@ -106,7 +109,7 @@ public class CastPlayerTest {
|
|||
@Mock private CastContext mockCastContext;
|
||||
@Mock private SessionManager mockSessionManager;
|
||||
@Mock private CastSession mockCastSession;
|
||||
@Mock private Player.Listener mockListener;
|
||||
@Mock private Listener mockListener;
|
||||
@Mock private PendingResult<RemoteMediaClient.MediaChannelResult> mockPendingResult;
|
||||
|
||||
@Captor
|
||||
|
|
@ -126,12 +129,14 @@ public class CastPlayerTest {
|
|||
when(mockCastSession.getRemoteMediaClient()).thenReturn(mockRemoteMediaClient);
|
||||
when(mockRemoteMediaClient.getMediaStatus()).thenReturn(mockMediaStatus);
|
||||
when(mockRemoteMediaClient.getMediaQueue()).thenReturn(mockMediaQueue);
|
||||
when(mockMediaStatus.getMediaInfo()).thenReturn(new MediaInfo.Builder("contentId").build());
|
||||
when(mockMediaQueue.getItemIds()).thenReturn(new int[0]);
|
||||
// Make the remote media client present the same default values as ExoPlayer:
|
||||
when(mockRemoteMediaClient.isPaused()).thenReturn(true);
|
||||
when(mockMediaStatus.getQueueRepeatMode()).thenReturn(MediaStatus.REPEAT_MODE_REPEAT_OFF);
|
||||
when(mockMediaStatus.getPlaybackRate()).thenReturn(1.0d);
|
||||
castPlayer = new CastPlayer(mockCastContext);
|
||||
mediaItemConverter = new DefaultMediaItemConverter();
|
||||
castPlayer = new CastPlayer(mockCastContext, mediaItemConverter);
|
||||
castPlayer.addListener(mockListener);
|
||||
verify(mockRemoteMediaClient).registerCallback(callbackArgumentCaptor.capture());
|
||||
remoteMediaClientCallback = callbackArgumentCaptor.getValue();
|
||||
|
|
@ -388,7 +393,7 @@ public class CastPlayerTest {
|
|||
mediaItems.add(
|
||||
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)
|
||||
.queueLoad(queueItemsArgumentCaptor.capture(), eq(1), anyInt(), eq(2000L), any());
|
||||
|
|
@ -434,22 +439,23 @@ public class CastPlayerTest {
|
|||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||
.build());
|
||||
|
||||
castPlayer.setMediaItems(
|
||||
firstPlaylist, /* startWindowIndex= */ 1, /* startPositionMs= */ 2000L);
|
||||
castPlayer.setMediaItems(firstPlaylist, /* startIndex= */ 1, /* startPositionMs= */ 2000L);
|
||||
updateTimeLine(
|
||||
firstPlaylist, /* mediaQueueItemIds= */ new int[] {1, 2}, /* currentItemId= */ 2);
|
||||
// Replacing existing playlist.
|
||||
castPlayer.setMediaItems(
|
||||
secondPlaylist, /* startWindowIndex= */ 0, /* startPositionMs= */ 1000L);
|
||||
castPlayer.setMediaItems(secondPlaylist, /* startIndex= */ 0, /* startPositionMs= */ 1000L);
|
||||
updateTimeLine(secondPlaylist, /* mediaQueueItemIds= */ new int[] {3}, /* currentItemId= */ 3);
|
||||
|
||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||
inOrder
|
||||
.verify(mockListener, times(2))
|
||||
.verify(mockListener)
|
||||
.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());
|
||||
assertThat(mediaItemCaptor.getAllValues().get(1).localConfiguration.tag).isEqualTo(3);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation") // Verifies deprecated callback being called correctly.
|
||||
|
|
@ -469,8 +475,7 @@ public class CastPlayerTest {
|
|||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||
.build());
|
||||
|
||||
castPlayer.setMediaItems(
|
||||
firstPlaylist, /* startWindowIndex= */ 1, /* startPositionMs= */ 2000L);
|
||||
castPlayer.setMediaItems(firstPlaylist, /* startIndex= */ 1, /* startPositionMs= */ 2000L);
|
||||
updateTimeLine(
|
||||
firstPlaylist,
|
||||
/* mediaQueueItemIds= */ new int[] {1, 2},
|
||||
|
|
@ -481,8 +486,7 @@ public class CastPlayerTest {
|
|||
/* durationsMs= */ new long[] {20_000, 20_000},
|
||||
/* positionMs= */ 2000L);
|
||||
// Replacing existing playlist.
|
||||
castPlayer.setMediaItems(
|
||||
secondPlaylist, /* startWindowIndex= */ 0, /* startPositionMs= */ 1000L);
|
||||
castPlayer.setMediaItems(secondPlaylist, /* startIndex= */ 0, /* startPositionMs= */ 1000L);
|
||||
updateTimeLine(
|
||||
secondPlaylist,
|
||||
/* mediaQueueItemIds= */ new int[] {3},
|
||||
|
|
@ -494,8 +498,8 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo oldPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 2,
|
||||
/* windowIndex= */ 1,
|
||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
||||
/* mediaItemIndex= */ 1,
|
||||
firstPlaylist.get(1),
|
||||
/* periodUid= */ 2,
|
||||
/* periodIndex= */ 1,
|
||||
/* positionMs= */ 2000,
|
||||
|
|
@ -505,8 +509,8 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo newPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 3,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(3).build(),
|
||||
/* mediaItemIndex= */ 0,
|
||||
secondPlaylist.get(0),
|
||||
/* periodUid= */ 3,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 1000,
|
||||
|
|
@ -536,34 +540,37 @@ public class CastPlayerTest {
|
|||
verify(mockRemoteMediaClient)
|
||||
.queueInsertItems(
|
||||
queueItemsArgumentCaptor.capture(), eq(MediaQueueItem.INVALID_ITEM_ID), any());
|
||||
|
||||
MediaQueueItem[] mediaQueueItems = queueItemsArgumentCaptor.getValue();
|
||||
assertThat(mediaQueueItems[0].getMedia().getContentId()).isEqualTo(uri1);
|
||||
assertThat(mediaQueueItems[1].getMedia().getContentId()).isEqualTo(uri2);
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
@Test
|
||||
public void addMediaItems_insertAtIndex_callsRemoteMediaClient() {
|
||||
int[] mediaQueueItemIds = createMediaQueueItemIds(/* numberOfIds= */ 2);
|
||||
List<MediaItem> mediaItems = createMediaItems(mediaQueueItemIds);
|
||||
// Add two items.
|
||||
addMediaItemsAndUpdateTimeline(mediaItems, mediaQueueItemIds);
|
||||
String uri = "http://www.google.com/video3";
|
||||
MediaItem anotherMediaItem =
|
||||
new MediaItem.Builder().setUri(uri).setMimeType(MimeTypes.APPLICATION_MPD).build();
|
||||
int index = 1;
|
||||
List<MediaItem> newPlaylist = Collections.singletonList(anotherMediaItem);
|
||||
|
||||
// Add another on position 1
|
||||
int index = 1;
|
||||
castPlayer.addMediaItems(index, Collections.singletonList(anotherMediaItem));
|
||||
castPlayer.addMediaItems(index, newPlaylist);
|
||||
updateTimeLine(newPlaylist, /* mediaQueueItemIds= */ new int[] {123}, /* currentItemId= */ 1);
|
||||
|
||||
verify(mockRemoteMediaClient)
|
||||
.queueInsertItems(
|
||||
queueItemsArgumentCaptor.capture(),
|
||||
eq((int) mediaItems.get(index).localConfiguration.tag),
|
||||
any());
|
||||
|
||||
MediaQueueItem[] mediaQueueItems = queueItemsArgumentCaptor.getValue();
|
||||
assertThat(mediaQueueItems[0].getMedia().getContentId()).isEqualTo(uri);
|
||||
verify(mockRemoteMediaClient, times(2))
|
||||
.queueInsertItems(queueItemsArgumentCaptor.capture(), anyInt(), any());
|
||||
assertThat(queueItemsArgumentCaptor.getAllValues().get(1)[0])
|
||||
.isEqualTo(mediaItemConverter.toMediaQueueItem(anotherMediaItem));
|
||||
Timeline.Window currentWindow =
|
||||
castPlayer
|
||||
.getCurrentTimeline()
|
||||
.getWindow(castPlayer.getCurrentMediaItemIndex(), new Timeline.Window());
|
||||
assertThat(currentWindow.uid).isEqualTo(123);
|
||||
assertThat(currentWindow.mediaItem).isEqualTo(anotherMediaItem);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -702,8 +709,8 @@ public class CastPlayerTest {
|
|||
|
||||
Timeline currentTimeline = castPlayer.getCurrentTimeline();
|
||||
for (int i = 0; i < mediaItems.size(); i++) {
|
||||
assertThat(currentTimeline.getWindow(/* windowIndex= */ i, window).uid)
|
||||
.isEqualTo(mediaItems.get(i).localConfiguration.tag);
|
||||
assertThat(currentTimeline.getWindow(/* windowIndex= */ i, window).mediaItem)
|
||||
.isEqualTo(mediaItems.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -720,10 +727,8 @@ public class CastPlayerTest {
|
|||
inOrder
|
||||
.verify(mockListener)
|
||||
.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());
|
||||
assertThat(mediaItemCaptor.getValue().localConfiguration.tag)
|
||||
.isEqualTo(mediaItem.localConfiguration.tag);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -742,7 +747,8 @@ public class CastPlayerTest {
|
|||
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||
inOrder
|
||||
.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)
|
||||
.onMediaItemTransition(
|
||||
|
|
@ -776,8 +782,8 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo oldPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 1,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
||||
/* mediaItemIndex= */ 0,
|
||||
mediaItems.get(0),
|
||||
/* periodUid= */ 1,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 1234,
|
||||
|
|
@ -787,7 +793,7 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo newPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ null,
|
||||
/* windowIndex= */ 0,
|
||||
/* mediaItemIndex= */ 0,
|
||||
/* mediaItem= */ null,
|
||||
/* periodUid= */ null,
|
||||
/* periodIndex= */ 0,
|
||||
|
|
@ -827,10 +833,8 @@ public class CastPlayerTest {
|
|||
.onMediaItemTransition(
|
||||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||
assertThat(mediaItemCaptor.getAllValues().get(0).localConfiguration.tag)
|
||||
.isEqualTo(mediaItem1.localConfiguration.tag);
|
||||
assertThat(mediaItemCaptor.getAllValues().get(1).localConfiguration.tag)
|
||||
.isEqualTo(mediaItem2.localConfiguration.tag);
|
||||
assertThat(mediaItemCaptor.getAllValues().get(0)).isEqualTo(mediaItem1);
|
||||
assertThat(mediaItemCaptor.getAllValues().get(1)).isEqualTo(mediaItem2);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -862,8 +866,8 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo oldPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 1,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
||||
/* mediaItemIndex= */ 0,
|
||||
mediaItem1,
|
||||
/* periodUid= */ 1,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 1234,
|
||||
|
|
@ -873,8 +877,8 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo newPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 2,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
||||
/* mediaItemIndex= */ 0,
|
||||
mediaItem2,
|
||||
/* periodUid= */ 2,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 0,
|
||||
|
|
@ -912,10 +916,8 @@ public class CastPlayerTest {
|
|||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED));
|
||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||
List<MediaItem> capturedMediaItems = mediaItemCaptor.getAllValues();
|
||||
assertThat(capturedMediaItems.get(0).localConfiguration.tag)
|
||||
.isEqualTo(mediaItem1.localConfiguration.tag);
|
||||
assertThat(capturedMediaItems.get(1).localConfiguration.tag)
|
||||
.isEqualTo(mediaItem2.localConfiguration.tag);
|
||||
assertThat(capturedMediaItems.get(0)).isEqualTo(mediaItem1);
|
||||
assertThat(capturedMediaItems.get(1)).isEqualTo(mediaItem2);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -945,8 +947,8 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo oldPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 1,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
||||
/* mediaItemIndex= */ 0,
|
||||
mediaItem1,
|
||||
/* periodUid= */ 1,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 0, // position at which we receive the timeline change
|
||||
|
|
@ -956,8 +958,8 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo newPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 2,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
||||
/* mediaItemIndex= */ 0,
|
||||
mediaItem2,
|
||||
/* periodUid= */ 2,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 0,
|
||||
|
|
@ -992,7 +994,8 @@ public class CastPlayerTest {
|
|||
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||
inOrder
|
||||
.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());
|
||||
}
|
||||
|
||||
|
|
@ -1027,19 +1030,21 @@ public class CastPlayerTest {
|
|||
|
||||
castPlayer.addMediaItems(mediaItems);
|
||||
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
|
||||
.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)
|
||||
.onMediaItemTransition(
|
||||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_SEEK));
|
||||
.onMediaItemTransition(eq(mediaItem2), eq(Player.MEDIA_ITEM_TRANSITION_REASON_SEEK));
|
||||
inOrder.verify(mockListener, never()).onPositionDiscontinuity(any(), any(), anyInt());
|
||||
assertThat(mediaItemCaptor.getValue().localConfiguration.tag)
|
||||
.isEqualTo(mediaItem2.localConfiguration.tag);
|
||||
assertThat(firstMediaMetadata).isEqualTo(mediaItem1.mediaMetadata);
|
||||
assertThat(secondMediaMetadata).isEqualTo(mediaItem2.mediaMetadata);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1054,13 +1059,13 @@ public class CastPlayerTest {
|
|||
|
||||
castPlayer.addMediaItems(mediaItems);
|
||||
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
||||
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 1234);
|
||||
castPlayer.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 1234);
|
||||
|
||||
Player.PositionInfo oldPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 1,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
||||
/* mediaItemIndex= */ 0,
|
||||
mediaItem1,
|
||||
/* periodUid= */ 1,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 0,
|
||||
|
|
@ -1070,8 +1075,8 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo newPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 2,
|
||||
/* windowIndex= */ 1,
|
||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
||||
/* mediaItemIndex= */ 1,
|
||||
mediaItem2,
|
||||
/* periodUid= */ 2,
|
||||
/* periodIndex= */ 1,
|
||||
/* positionMs= */ 1234,
|
||||
|
|
@ -1097,12 +1102,13 @@ public class CastPlayerTest {
|
|||
|
||||
castPlayer.addMediaItems(mediaItems);
|
||||
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 1234);
|
||||
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 1234);
|
||||
|
||||
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||
inOrder
|
||||
.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());
|
||||
}
|
||||
|
||||
|
|
@ -1115,14 +1121,13 @@ public class CastPlayerTest {
|
|||
|
||||
castPlayer.addMediaItems(mediaItems);
|
||||
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 =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 1,
|
||||
/* windowIndex= */ 0,
|
||||
mediaItem,
|
||||
/* mediaItemIndex= */ 0,
|
||||
mediaItems.get(0),
|
||||
/* periodUid= */ 1,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 0,
|
||||
|
|
@ -1132,8 +1137,8 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo newPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 1,
|
||||
/* windowIndex= */ 0,
|
||||
mediaItem,
|
||||
/* mediaItemIndex= */ 0,
|
||||
mediaItems.get(0),
|
||||
/* periodUid= */ 1,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 1234,
|
||||
|
|
@ -1164,13 +1169,12 @@ public class CastPlayerTest {
|
|||
InOrder inOrder = Mockito.inOrder(mockListener);
|
||||
inOrder
|
||||
.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)
|
||||
.onMediaItemTransition(
|
||||
mediaItemCaptor.capture(), eq(Player.MEDIA_ITEM_TRANSITION_REASON_AUTO));
|
||||
.onMediaItemTransition(eq(mediaItems.get(1)), eq(Player.MEDIA_ITEM_TRANSITION_REASON_AUTO));
|
||||
inOrder.verify(mockListener, never()).onMediaItemTransition(any(), anyInt());
|
||||
assertThat(mediaItemCaptor.getValue().localConfiguration.tag).isEqualTo(2);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1203,8 +1207,8 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo oldPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 1,
|
||||
/* windowIndex= */ 0,
|
||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build(),
|
||||
/* mediaItemIndex= */ 0,
|
||||
mediaItems.get(0),
|
||||
/* periodUid= */ 1,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 12500,
|
||||
|
|
@ -1214,8 +1218,8 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo newPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 2,
|
||||
/* windowIndex= */ 1,
|
||||
new MediaItem.Builder().setUri(Uri.EMPTY).setTag(2).build(),
|
||||
/* mediaItemIndex= */ 1,
|
||||
mediaItems.get(1),
|
||||
/* periodUid= */ 2,
|
||||
/* periodIndex= */ 1,
|
||||
/* positionMs= */ 0,
|
||||
|
|
@ -1250,12 +1254,11 @@ public class CastPlayerTest {
|
|||
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
|
||||
castPlayer.seekBack();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build();
|
||||
Player.PositionInfo oldPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 1,
|
||||
/* windowIndex= */ 0,
|
||||
mediaItem,
|
||||
/* mediaItemIndex= */ 0,
|
||||
mediaItems.get(0),
|
||||
/* periodUid= */ 1,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 2 * C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
||||
|
|
@ -1265,8 +1268,8 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo newPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 1,
|
||||
/* windowIndex= */ 0,
|
||||
mediaItem,
|
||||
/* mediaItemIndex= */ 0,
|
||||
mediaItems.get(0),
|
||||
/* periodUid= */ 1,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ C.DEFAULT_SEEK_BACK_INCREMENT_MS,
|
||||
|
|
@ -1299,12 +1302,11 @@ public class CastPlayerTest {
|
|||
mediaItems, mediaQueueItemIds, currentItemId, streamTypes, durationsMs, positionMs);
|
||||
castPlayer.seekForward();
|
||||
|
||||
MediaItem mediaItem = new MediaItem.Builder().setUri(Uri.EMPTY).setTag(1).build();
|
||||
Player.PositionInfo oldPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 1,
|
||||
/* windowIndex= */ 0,
|
||||
mediaItem,
|
||||
/* mediaItemIndex= */ 0,
|
||||
mediaItems.get(0),
|
||||
/* periodUid= */ 1,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ 0,
|
||||
|
|
@ -1314,8 +1316,8 @@ public class CastPlayerTest {
|
|||
Player.PositionInfo newPosition =
|
||||
new Player.PositionInfo(
|
||||
/* windowUid= */ 1,
|
||||
/* windowIndex= */ 0,
|
||||
mediaItem,
|
||||
/* mediaItemIndex= */ 0,
|
||||
mediaItems.get(0),
|
||||
/* periodUid= */ 1,
|
||||
/* periodIndex= */ 0,
|
||||
/* positionMs= */ C.DEFAULT_SEEK_FORWARD_INCREMENT_MS,
|
||||
|
|
@ -1475,14 +1477,14 @@ public class CastPlayerTest {
|
|||
// Check that there were no other calls to onAvailableCommandsChanged.
|
||||
verify(mockListener).onAvailableCommandsChanged(any());
|
||||
|
||||
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
|
||||
castPlayer.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 0);
|
||||
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
|
||||
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
||||
|
||||
castPlayer.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0);
|
||||
castPlayer.seekTo(/* mediaItemIndex= */ 2, /* positionMs= */ 0);
|
||||
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
||||
|
||||
castPlayer.seekTo(/* windowIndex= */ 3, /* positionMs= */ 0);
|
||||
castPlayer.seekTo(/* mediaItemIndex= */ 3, /* positionMs= */ 0);
|
||||
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousWindow);
|
||||
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
||||
}
|
||||
|
|
@ -1509,14 +1511,14 @@ public class CastPlayerTest {
|
|||
// Check that there were no other calls to onAvailableCommandsChanged.
|
||||
verify(mockListener).onAvailableCommandsChanged(any());
|
||||
|
||||
castPlayer.seekTo(/* windowIndex= */ 2, /* positionMs= */ 0);
|
||||
castPlayer.seekTo(/* mediaItemIndex= */ 2, /* positionMs= */ 0);
|
||||
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToPreviousAndNextWindow);
|
||||
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
||||
|
||||
castPlayer.seekTo(/* windowIndex= */ 1, /* positionMs= */ 0);
|
||||
castPlayer.seekTo(/* mediaItemIndex= */ 1, /* positionMs= */ 0);
|
||||
verify(mockListener, times(2)).onAvailableCommandsChanged(any());
|
||||
|
||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 0);
|
||||
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 0);
|
||||
verify(mockListener).onAvailableCommandsChanged(commandsWithSeekToNextWindow);
|
||||
verify(mockListener, times(3)).onAvailableCommandsChanged(any());
|
||||
}
|
||||
|
|
@ -1533,8 +1535,8 @@ public class CastPlayerTest {
|
|||
updateTimeLine(mediaItems, mediaQueueItemIds, /* currentItemId= */ 1);
|
||||
verify(mockListener).onAvailableCommandsChanged(defaultCommands);
|
||||
|
||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 200);
|
||||
castPlayer.seekTo(/* windowIndex= */ 0, /* positionMs= */ 100);
|
||||
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 200);
|
||||
castPlayer.seekTo(/* mediaItemIndex= */ 0, /* positionMs= */ 100);
|
||||
// Check that there were no other calls to onAvailableCommandsChanged.
|
||||
verify(mockListener).onAvailableCommandsChanged(any());
|
||||
}
|
||||
|
|
@ -1763,6 +1765,105 @@ public class CastPlayerTest {
|
|||
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) {
|
||||
int[] mediaQueueItemIds = new int[numberOfIds];
|
||||
for (int i = 0; i < numberOfIds; i++) {
|
||||
|
|
@ -1782,8 +1883,9 @@ public class CastPlayerTest {
|
|||
private MediaItem createMediaItem(int mediaQueueItemId) {
|
||||
return new MediaItem.Builder()
|
||||
.setUri("http://www.google.com/video" + mediaQueueItemId)
|
||||
.setMediaMetadata(
|
||||
new MediaMetadata.Builder().setArtist("Foo Bar - " + mediaQueueItemId).build())
|
||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||
.setTag(mediaQueueItemId)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
@ -1821,8 +1923,12 @@ public class CastPlayerTest {
|
|||
int mediaQueueItemId = mediaQueueItemIds[i];
|
||||
int streamType = streamTypes[i];
|
||||
long durationMs = durationsMs[i];
|
||||
String contentId =
|
||||
mediaItem.mediaId.equals(MediaItem.DEFAULT_MEDIA_ID)
|
||||
? mediaItem.localConfiguration.uri.toString()
|
||||
: mediaItem.mediaId;
|
||||
MediaInfo.Builder mediaInfoBuilder =
|
||||
new MediaInfo.Builder(mediaItem.localConfiguration.uri.toString())
|
||||
new MediaInfo.Builder(contentId)
|
||||
.setStreamType(streamType)
|
||||
.setContentType(mediaItem.localConfiguration.mimeType);
|
||||
if (durationMs != C.TIME_UNSET) {
|
||||
|
|
|
|||
|
|
@ -15,21 +15,30 @@
|
|||
*/
|
||||
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 androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
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.util.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
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.framework.media.MediaQueue;
|
||||
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.List;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
/** Tests for {@link CastTimelineTracker}. */
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
|
|
@ -40,10 +49,19 @@ public class CastTimelineTrackerTest {
|
|||
private static final long DURATION_4_MS = 4000;
|
||||
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. */
|
||||
@Test
|
||||
public void getCastTimelinePersistsDuration() {
|
||||
CastTimelineTracker tracker = new CastTimelineTracker();
|
||||
CastTimelineTracker tracker = new CastTimelineTracker(new DefaultMediaItemConverter());
|
||||
|
||||
RemoteMediaClient remoteMediaClient =
|
||||
mockRemoteMediaClient(
|
||||
|
|
@ -104,10 +122,179 @@ public class CastTimelineTrackerTest {
|
|||
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(
|
||||
int[] itemIds, int currentItemId, long currentDurationMs) {
|
||||
RemoteMediaClient remoteMediaClient = Mockito.mock(RemoteMediaClient.class);
|
||||
MediaStatus status = Mockito.mock(MediaStatus.class);
|
||||
RemoteMediaClient remoteMediaClient = mock(RemoteMediaClient.class);
|
||||
MediaStatus status = mock(MediaStatus.class);
|
||||
when(status.getQueueItems()).thenReturn(Collections.emptyList());
|
||||
when(remoteMediaClient.getMediaStatus()).thenReturn(status);
|
||||
when(status.getMediaInfo()).thenReturn(getMediaInfo(currentDurationMs));
|
||||
|
|
@ -118,7 +305,7 @@ public class CastTimelineTrackerTest {
|
|||
}
|
||||
|
||||
private static MediaQueue mockMediaQueue(int[] itemIds) {
|
||||
MediaQueue mediaQueue = Mockito.mock(MediaQueue.class);
|
||||
MediaQueue mediaQueue = mock(MediaQueue.class);
|
||||
when(mediaQueue.getItemIds()).thenReturn(itemIds);
|
||||
return mediaQueue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ public class DefaultMediaItemConverterTest {
|
|||
MediaItem.Builder builder = new MediaItem.Builder();
|
||||
MediaItem item =
|
||||
builder
|
||||
.setMediaId("fooBar")
|
||||
.setUri(Uri.parse("http://example.com"))
|
||||
.setMediaMetadata(MediaMetadata.EMPTY)
|
||||
.setMimeType(MimeTypes.APPLICATION_MPD)
|
||||
|
|
@ -66,4 +67,45 @@ public class DefaultMediaItemConverterTest {
|
|||
|
||||
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.
|
||||
|
||||
@Override
|
||||
public void onPlaybackStateChanged(@Player.State int playbackState) {
|
||||
notifyStateChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerError(PlaybackException error) {
|
||||
Callback callback = getCallback();
|
||||
|
|
@ -283,5 +278,13 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter implements Runnab
|
|||
int scaledWidth = Math.round(videoSize.width * videoSize.pixelWidthHeightRatio);
|
||||
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 'com.google.truth:truth:' + truthVersion
|
||||
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
|
||||
testImplementation project(modulePrefix + 'library-core')
|
||||
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". */
|
||||
// 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}. */
|
||||
// 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.
|
||||
|
|
@ -41,7 +41,7 @@ public final class ExoPlayerLibraryInfo {
|
|||
* integer version 123045006 (123-045-006).
|
||||
*/
|
||||
// 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. */
|
||||
public static final boolean ASSERTIONS_ENABLED = true;
|
||||
|
|
|
|||
|
|
@ -372,6 +372,7 @@ public interface Player {
|
|||
COMMAND_GET_TIMELINE,
|
||||
COMMAND_GET_MEDIA_ITEMS_METADATA,
|
||||
COMMAND_SET_MEDIA_ITEMS_METADATA,
|
||||
COMMAND_SET_MEDIA_ITEM,
|
||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||
COMMAND_GET_AUDIO_ATTRIBUTES,
|
||||
COMMAND_GET_VOLUME,
|
||||
|
|
@ -383,7 +384,6 @@ public interface Player {
|
|||
COMMAND_GET_TEXT,
|
||||
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
|
||||
COMMAND_GET_TRACKS,
|
||||
COMMAND_SET_MEDIA_ITEM,
|
||||
};
|
||||
|
||||
private final FlagSet.Builder flagsBuilder;
|
||||
|
|
@ -1422,6 +1422,7 @@ public interface Player {
|
|||
COMMAND_GET_TIMELINE,
|
||||
COMMAND_GET_MEDIA_ITEMS_METADATA,
|
||||
COMMAND_SET_MEDIA_ITEMS_METADATA,
|
||||
COMMAND_SET_MEDIA_ITEM,
|
||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||
COMMAND_GET_AUDIO_ATTRIBUTES,
|
||||
COMMAND_GET_VOLUME,
|
||||
|
|
@ -1433,7 +1434,6 @@ public interface Player {
|
|||
COMMAND_GET_TEXT,
|
||||
COMMAND_SET_TRACK_SELECTION_PARAMETERS,
|
||||
COMMAND_GET_TRACKS,
|
||||
COMMAND_SET_MEDIA_ITEM,
|
||||
})
|
||||
@interface Command {}
|
||||
/** Command to start, pause or resume playback. */
|
||||
|
|
@ -1490,6 +1490,8 @@ public interface Player {
|
|||
int COMMAND_GET_MEDIA_ITEMS_METADATA = 18;
|
||||
/** Command to set the {@link MediaItem MediaItems} metadata. */
|
||||
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. */
|
||||
int COMMAND_CHANGE_MEDIA_ITEMS = 20;
|
||||
/** Command to get the player current {@link AudioAttributes}. */
|
||||
|
|
@ -1512,8 +1514,6 @@ public interface Player {
|
|||
int COMMAND_SET_TRACK_SELECTION_PARAMETERS = 29;
|
||||
/** Command to get details of the current track selection. */
|
||||
int COMMAND_GET_TRACKS = 30;
|
||||
/** Command to set a {@link MediaItem MediaItem}. */
|
||||
int COMMAND_SET_MEDIA_ITEM = 31;
|
||||
|
||||
/** Represents an invalid {@link Command}. */
|
||||
int COMMAND_INVALID = -1;
|
||||
|
|
|
|||
|
|
@ -1340,6 +1340,27 @@ public abstract class Timeline implements Bundleable {
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
@ -1356,6 +1377,13 @@ public abstract class Timeline implements Bundleable {
|
|||
for (int i = 0; i < getPeriodCount(); i++) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.drm;
|
|||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.CheckResult;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
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.
|
||||
* @return A copy with the specified protection scheme type.
|
||||
*/
|
||||
@CheckResult
|
||||
public DrmInitData copyWithSchemeType(@Nullable String schemeType) {
|
||||
if (Util.areEqual(this.schemeType, schemeType)) {
|
||||
return this;
|
||||
|
|
@ -332,6 +334,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
|
|||
* @param data The data to include in the copy.
|
||||
* @return The new instance.
|
||||
*/
|
||||
@CheckResult
|
||||
public SchemeData copyWithData(@Nullable byte[] 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 android.text.TextUtils;
|
||||
import androidx.annotation.GuardedBy;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.Size;
|
||||
|
|
@ -28,7 +29,10 @@ import java.lang.annotation.Target;
|
|||
import java.net.UnknownHostException;
|
||||
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 {
|
||||
|
||||
/**
|
||||
|
|
@ -51,15 +55,89 @@ public final class Log {
|
|||
/** Log level to disable all logging. */
|
||||
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;
|
||||
|
||||
@GuardedBy("lock")
|
||||
private static boolean logStackTraces = true;
|
||||
|
||||
@GuardedBy("lock")
|
||||
private static Logger logger = Logger.DEFAULT;
|
||||
|
||||
private Log() {}
|
||||
|
||||
/** Returns current {@link LogLevel} for ExoPlayer logcat logging. */
|
||||
@Pure
|
||||
public static @LogLevel int getLogLevel() {
|
||||
return logLevel;
|
||||
synchronized (lock) {
|
||||
return logLevel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -68,7 +146,9 @@ public final class Log {
|
|||
* @param logLevel The new {@link LogLevel}.
|
||||
*/
|
||||
public static void setLogLevel(@LogLevel int logLevel) {
|
||||
Log.logLevel = logLevel;
|
||||
synchronized (lock) {
|
||||
Log.logLevel = logLevel;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -78,7 +158,20 @@ public final class Log {
|
|||
* @param logStackTraces Whether stack traces will be logged.
|
||||
*/
|
||||
public static void setLogStackTraces(boolean logStackTraces) {
|
||||
Log.logStackTraces = logStackTraces;
|
||||
synchronized (lock) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -86,8 +179,10 @@ public final class Log {
|
|||
*/
|
||||
@Pure
|
||||
public static void d(@Size(max = 23) String tag, String message) {
|
||||
if (logLevel == LOG_LEVEL_ALL) {
|
||||
android.util.Log.d(tag, message);
|
||||
synchronized (lock) {
|
||||
if (logLevel == LOG_LEVEL_ALL) {
|
||||
logger.d(tag, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -104,8 +199,10 @@ public final class Log {
|
|||
*/
|
||||
@Pure
|
||||
public static void i(@Size(max = 23) String tag, String message) {
|
||||
if (logLevel <= LOG_LEVEL_INFO) {
|
||||
android.util.Log.i(tag, message);
|
||||
synchronized (lock) {
|
||||
if (logLevel <= LOG_LEVEL_INFO) {
|
||||
logger.i(tag, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -122,8 +219,10 @@ public final class Log {
|
|||
*/
|
||||
@Pure
|
||||
public static void w(@Size(max = 23) String tag, String message) {
|
||||
if (logLevel <= LOG_LEVEL_WARNING) {
|
||||
android.util.Log.w(tag, message);
|
||||
synchronized (lock) {
|
||||
if (logLevel <= LOG_LEVEL_WARNING) {
|
||||
logger.w(tag, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -140,8 +239,10 @@ public final class Log {
|
|||
*/
|
||||
@Pure
|
||||
public static void e(@Size(max = 23) String tag, String message) {
|
||||
if (logLevel <= LOG_LEVEL_ERROR) {
|
||||
android.util.Log.e(tag, message);
|
||||
synchronized (lock) {
|
||||
if (logLevel <= LOG_LEVEL_ERROR) {
|
||||
logger.e(tag, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -167,20 +268,23 @@ public final class Log {
|
|||
@Nullable
|
||||
@Pure
|
||||
public static String getThrowableString(@Nullable Throwable throwable) {
|
||||
if (throwable == null) {
|
||||
return null;
|
||||
} else if (isCausedByUnknownHostException(throwable)) {
|
||||
// UnknownHostException implies the device doesn't have network connectivity.
|
||||
// UnknownHostException.getMessage() may return a string that's more verbose than desired for
|
||||
// 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
|
||||
// indicate the failure mode at all. Hence we special case this exception to always return a
|
||||
// concise but useful message.
|
||||
return "UnknownHostException (no network)";
|
||||
} else if (!logStackTraces) {
|
||||
return throwable.getMessage();
|
||||
} else {
|
||||
return android.util.Log.getStackTraceString(throwable).trim().replace("\t", " ");
|
||||
synchronized (lock) {
|
||||
if (throwable == null) {
|
||||
return null;
|
||||
} else if (isCausedByUnknownHostException(throwable)) {
|
||||
// UnknownHostException implies the device doesn't have network connectivity.
|
||||
// UnknownHostException.getMessage() may return a string that's more verbose than desired
|
||||
// for
|
||||
// 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
|
||||
// indicate the failure mode at all. Hence we special case this exception to always return a
|
||||
// concise but useful message.
|
||||
return "UnknownHostException (no network)";
|
||||
} else if (!logStackTraces) {
|
||||
return throwable.getMessage();
|
||||
} else {
|
||||
return android.util.Log.getStackTraceString(throwable).trim().replace("\t", " ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ import android.os.Handler;
|
|||
import android.os.Looper;
|
||||
import android.os.Parcel;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.MediaStore;
|
||||
import android.security.NetworkSecurityPolicy;
|
||||
import android.telephony.TelephonyManager;
|
||||
import android.text.TextUtils;
|
||||
|
|
@ -195,7 +196,7 @@ public final class Util {
|
|||
*/
|
||||
@Nullable
|
||||
public static ComponentName startForegroundService(Context context, Intent intent) {
|
||||
if (Util.SDK_INT >= 26) {
|
||||
if (SDK_INT >= 26) {
|
||||
return context.startForegroundService(intent);
|
||||
} else {
|
||||
return context.startService(intent);
|
||||
|
|
@ -211,12 +212,12 @@ public final class Util {
|
|||
* @return Whether a permission request was made.
|
||||
*/
|
||||
public static boolean maybeRequestReadExternalStoragePermission(Activity activity, Uri... uris) {
|
||||
if (Util.SDK_INT < 23) {
|
||||
if (SDK_INT < 23) {
|
||||
return false;
|
||||
}
|
||||
for (Uri uri : uris) {
|
||||
if (isLocalFileUri(uri)) {
|
||||
return requestExternalStoragePermission(activity);
|
||||
if (maybeRequestReadExternalStoragePermission(activity, uri)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
@ -234,25 +235,46 @@ public final class Util {
|
|||
*/
|
||||
public static boolean maybeRequestReadExternalStoragePermission(
|
||||
Activity activity, MediaItem... mediaItems) {
|
||||
if (Util.SDK_INT < 23) {
|
||||
if (SDK_INT < 23) {
|
||||
return false;
|
||||
}
|
||||
for (MediaItem mediaItem : mediaItems) {
|
||||
if (mediaItem.localConfiguration == null) {
|
||||
continue;
|
||||
}
|
||||
if (isLocalFileUri(mediaItem.localConfiguration.uri)) {
|
||||
return requestExternalStoragePermission(activity);
|
||||
if (maybeRequestReadExternalStoragePermission(activity, mediaItem.localConfiguration.uri)) {
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < mediaItem.localConfiguration.subtitleConfigurations.size(); i++) {
|
||||
if (isLocalFileUri(mediaItem.localConfiguration.subtitleConfigurations.get(i).uri)) {
|
||||
return requestExternalStoragePermission(activity);
|
||||
List<MediaItem.SubtitleConfiguration> subtitleConfigs =
|
||||
mediaItem.localConfiguration.subtitleConfigurations;
|
||||
for (int i = 0; i < subtitleConfigs.size(); i++) {
|
||||
if (maybeRequestReadExternalStoragePermission(activity, subtitleConfigs.get(i).uri)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
* 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.
|
||||
*/
|
||||
public static boolean checkCleartextTrafficPermitted(MediaItem... mediaItems) {
|
||||
if (Util.SDK_INT < 24) {
|
||||
if (SDK_INT < 24) {
|
||||
// We assume cleartext traffic is permitted.
|
||||
return true;
|
||||
}
|
||||
|
|
@ -622,7 +644,7 @@ public final class Util {
|
|||
normalizedTag = language;
|
||||
}
|
||||
normalizedTag = Ascii.toLowerCase(normalizedTag);
|
||||
String mainLanguage = Util.splitAtFirst(normalizedTag, "-")[0];
|
||||
String mainLanguage = splitAtFirst(normalizedTag, "-")[0];
|
||||
if (languageTagReplacementMap == null) {
|
||||
languageTagReplacementMap = createIsoLanguageReplacementMap();
|
||||
}
|
||||
|
|
@ -1633,9 +1655,9 @@ public final class Util {
|
|||
case 7:
|
||||
return AudioFormat.CHANNEL_OUT_5POINT1 | AudioFormat.CHANNEL_OUT_BACK_CENTER;
|
||||
case 8:
|
||||
if (Util.SDK_INT >= 23) {
|
||||
if (SDK_INT >= 23) {
|
||||
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.
|
||||
return AudioFormat.CHANNEL_OUT_5POINT1
|
||||
| AudioFormat.CHANNEL_OUT_SIDE_LEFT
|
||||
|
|
@ -1918,7 +1940,7 @@ public final class Util {
|
|||
public static @ContentType int inferContentTypeForUriAndMimeType(
|
||||
Uri uri, @Nullable String mimeType) {
|
||||
if (mimeType == null) {
|
||||
return Util.inferContentType(uri);
|
||||
return inferContentType(uri);
|
||||
}
|
||||
switch (mimeType) {
|
||||
case MimeTypes.APPLICATION_MPD:
|
||||
|
|
@ -2242,7 +2264,7 @@ public final class Util {
|
|||
|
||||
/** Returns the default {@link Locale.Category#DISPLAY DISPLAY} {@link Locale}. */
|
||||
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.
|
||||
*/
|
||||
public static boolean isAutomotive(Context context) {
|
||||
return Util.SDK_INT >= 23
|
||||
return SDK_INT >= 23
|
||||
&& context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
|
||||
}
|
||||
|
||||
|
|
@ -2332,7 +2354,7 @@ public final class Util {
|
|||
*/
|
||||
public static Point getCurrentDisplayModeSize(Context context) {
|
||||
@Nullable Display defaultDisplay = null;
|
||||
if (Util.SDK_INT >= 17) {
|
||||
if (SDK_INT >= 17) {
|
||||
@Nullable
|
||||
DisplayManager displayManager =
|
||||
(DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
|
||||
|
|
@ -2380,7 +2402,7 @@ public final class Util {
|
|||
// vendor.display-size instead.
|
||||
@Nullable
|
||||
String displaySize =
|
||||
Util.SDK_INT < 28
|
||||
SDK_INT < 28
|
||||
? getSystemProperty("sys.display-size")
|
||||
: getSystemProperty("vendor.display-size");
|
||||
// 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.
|
||||
if ("Sony".equals(Util.MANUFACTURER)
|
||||
&& Util.MODEL.startsWith("BRAVIA")
|
||||
if ("Sony".equals(MANUFACTURER)
|
||||
&& MODEL.startsWith("BRAVIA")
|
||||
&& context.getPackageManager().hasSystemFeature("com.sony.dtv.hardware.panel.qfhd")) {
|
||||
return new Point(3840, 2160);
|
||||
}
|
||||
}
|
||||
|
||||
Point displaySize = new Point();
|
||||
if (Util.SDK_INT >= 23) {
|
||||
if (SDK_INT >= 23) {
|
||||
getDisplaySizeV23(display, displaySize);
|
||||
} else if (Util.SDK_INT >= 17) {
|
||||
} else if (SDK_INT >= 17) {
|
||||
getDisplaySizeV17(display, displaySize);
|
||||
} else {
|
||||
getDisplaySizeV16(display, displaySize);
|
||||
|
|
@ -2629,7 +2651,7 @@ public final class Util {
|
|||
|
||||
@RequiresApi(24)
|
||||
private static String[] getSystemLocalesV24(Configuration config) {
|
||||
return Util.split(config.getLocales().toLanguageTags(), ",");
|
||||
return split(config.getLocales().toLanguageTags(), ",");
|
||||
}
|
||||
|
||||
@RequiresApi(21)
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import static com.google.common.truth.Truth.assertThat;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
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.testutil.FakeTimeline;
|
||||
import com.google.android.exoplayer2.testutil.FakeTimeline.TimelineWindowDefinition;
|
||||
|
|
@ -65,6 +66,50 @@ public class TimelineTest {
|
|||
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
|
||||
public void windowEquals() {
|
||||
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_MEDIA_ITEMS_METADATA,
|
||||
COMMAND_SET_MEDIA_ITEMS_METADATA,
|
||||
COMMAND_SET_MEDIA_ITEM,
|
||||
COMMAND_CHANGE_MEDIA_ITEMS,
|
||||
COMMAND_GET_TRACKS,
|
||||
COMMAND_GET_AUDIO_ATTRIBUTES,
|
||||
|
|
@ -292,8 +293,7 @@ import java.util.concurrent.TimeoutException;
|
|||
COMMAND_SET_DEVICE_VOLUME,
|
||||
COMMAND_ADJUST_DEVICE_VOLUME,
|
||||
COMMAND_SET_VIDEO_SURFACE,
|
||||
COMMAND_GET_TEXT,
|
||||
COMMAND_SET_MEDIA_ITEM)
|
||||
COMMAND_GET_TEXT)
|
||||
.addIf(
|
||||
COMMAND_SET_TRACK_SELECTION_PARAMETERS, trackSelector.isSetParametersSupported())
|
||||
.build();
|
||||
|
|
@ -422,6 +422,9 @@ import java.util.concurrent.TimeoutException;
|
|||
public void experimentalSetOffloadSchedulingEnabled(boolean offloadSchedulingEnabled) {
|
||||
verifyApplicationThread();
|
||||
internalPlayer.experimentalSetOffloadSchedulingEnabled(offloadSchedulingEnabled);
|
||||
for (AudioOffloadListener listener : audioOffloadListeners) {
|
||||
listener.onExperimentalOffloadSchedulingEnabledChanged(offloadSchedulingEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -696,6 +699,7 @@ import java.util.concurrent.TimeoutException;
|
|||
@Override
|
||||
public void setShuffleOrder(ShuffleOrder shuffleOrder) {
|
||||
verifyApplicationThread();
|
||||
this.shuffleOrder = shuffleOrder;
|
||||
Timeline timeline = createMaskingTimeline();
|
||||
PlaybackInfo newPlaybackInfo =
|
||||
maskTimelineAndPosition(
|
||||
|
|
@ -704,7 +708,6 @@ import java.util.concurrent.TimeoutException;
|
|||
maskWindowPositionMsOrGetPeriodPositionUs(
|
||||
timeline, getCurrentMediaItemIndex(), getCurrentPosition()));
|
||||
pendingOperationAcks++;
|
||||
this.shuffleOrder = shuffleOrder;
|
||||
internalPlayer.setShuffleOrder(shuffleOrder);
|
||||
updatePlaybackInfo(
|
||||
newPlaybackInfo,
|
||||
|
|
@ -1951,12 +1954,6 @@ import java.util.concurrent.TimeoutException;
|
|||
updateAvailableCommands();
|
||||
listeners.flushEvents();
|
||||
|
||||
if (previousPlaybackInfo.offloadSchedulingEnabled != newPlaybackInfo.offloadSchedulingEnabled) {
|
||||
for (AudioOffloadListener listener : audioOffloadListeners) {
|
||||
listener.onExperimentalOffloadSchedulingEnabledChanged(
|
||||
newPlaybackInfo.offloadSchedulingEnabled);
|
||||
}
|
||||
}
|
||||
if (previousPlaybackInfo.sleepingForOffload != newPlaybackInfo.sleepingForOffload) {
|
||||
for (AudioOffloadListener listener : audioOffloadListeners) {
|
||||
listener.onExperimentalSleepingForOffloadChanged(newPlaybackInfo.sleepingForOffload);
|
||||
|
|
|
|||
|
|
@ -809,10 +809,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
return;
|
||||
}
|
||||
this.offloadSchedulingEnabled = offloadSchedulingEnabled;
|
||||
@Player.State int state = playbackInfo.playbackState;
|
||||
if (offloadSchedulingEnabled || state == Player.STATE_ENDED || state == Player.STATE_IDLE) {
|
||||
playbackInfo = playbackInfo.copyWithOffloadSchedulingEnabled(offloadSchedulingEnabled);
|
||||
} else {
|
||||
if (!offloadSchedulingEnabled && playbackInfo.sleepingForOffload) {
|
||||
// We need to wake the player up if offload scheduling is disabled and we are sleeping.
|
||||
handler.sendEmptyMessage(MSG_DO_SOME_WORK);
|
||||
}
|
||||
}
|
||||
|
|
@ -952,12 +950,14 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
|
||||
private void doSomeWork() throws ExoPlaybackException, IOException {
|
||||
long operationStartTimeMs = clock.uptimeMillis();
|
||||
// Remove other pending DO_SOME_WORK requests that are handled by this invocation.
|
||||
handler.removeMessages(MSG_DO_SOME_WORK);
|
||||
|
||||
updatePeriods();
|
||||
|
||||
if (playbackInfo.playbackState == Player.STATE_IDLE
|
||||
|| playbackInfo.playbackState == Player.STATE_ENDED) {
|
||||
// Remove all messages. Prepare (in case of IDLE) or seek (in case of ENDED) will resume.
|
||||
handler.removeMessages(MSG_DO_SOME_WORK);
|
||||
// Nothing to do. Prepare (in case of IDLE) or seek (in case of ENDED) will resume.
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1070,24 +1070,24 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
throw new IllegalStateException("Playback stuck buffering and not loading");
|
||||
}
|
||||
|
||||
if (offloadSchedulingEnabled != playbackInfo.offloadSchedulingEnabled) {
|
||||
playbackInfo = playbackInfo.copyWithOffloadSchedulingEnabled(offloadSchedulingEnabled);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
boolean isPlaying = shouldPlayWhenReady() && playbackInfo.playbackState == Player.STATE_READY;
|
||||
boolean sleepingForOffload = offloadSchedulingEnabled && requestForRendererSleep && isPlaying;
|
||||
if (playbackInfo.sleepingForOffload != sleepingForOffload) {
|
||||
playbackInfo = playbackInfo.copyWithSleepingForOffload(sleepingForOffload);
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
@ -1117,19 +1117,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
}
|
||||
|
||||
private void scheduleNextWork(long thisOperationStartTimeMs, long intervalMs) {
|
||||
handler.removeMessages(MSG_DO_SOME_WORK);
|
||||
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 {
|
||||
playbackInfoUpdate.incrementPendingOperationAcks(/* operationAcks= */ 1);
|
||||
|
||||
|
|
@ -1460,7 +1450,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
/* bufferedPositionUs= */ startPositionUs,
|
||||
/* totalBufferedDurationUs= */ 0,
|
||||
/* positionUs= */ startPositionUs,
|
||||
offloadSchedulingEnabled,
|
||||
/* sleepingForOffload= */ false);
|
||||
if (releaseMediaSourceList) {
|
||||
mediaSourceList.release();
|
||||
|
|
|
|||
|
|
@ -70,8 +70,6 @@ import java.util.List;
|
|||
public final @PlaybackSuppressionReason int playbackSuppressionReason;
|
||||
/** The playback parameters. */
|
||||
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. */
|
||||
public final boolean sleepingForOffload;
|
||||
|
||||
|
|
@ -118,7 +116,6 @@ import java.util.List;
|
|||
/* bufferedPositionUs= */ 0,
|
||||
/* totalBufferedDurationUs= */ 0,
|
||||
/* positionUs= */ 0,
|
||||
/* offloadSchedulingEnabled= */ false,
|
||||
/* sleepingForOffload= */ false);
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +138,6 @@ import java.util.List;
|
|||
* @param bufferedPositionUs See {@link #bufferedPositionUs}.
|
||||
* @param totalBufferedDurationUs See {@link #totalBufferedDurationUs}.
|
||||
* @param positionUs See {@link #positionUs}.
|
||||
* @param offloadSchedulingEnabled See {@link #offloadSchedulingEnabled}.
|
||||
* @param sleepingForOffload See {@link #sleepingForOffload}.
|
||||
*/
|
||||
public PlaybackInfo(
|
||||
|
|
@ -162,7 +158,6 @@ import java.util.List;
|
|||
long bufferedPositionUs,
|
||||
long totalBufferedDurationUs,
|
||||
long positionUs,
|
||||
boolean offloadSchedulingEnabled,
|
||||
boolean sleepingForOffload) {
|
||||
this.timeline = timeline;
|
||||
this.periodId = periodId;
|
||||
|
|
@ -181,7 +176,6 @@ import java.util.List;
|
|||
this.bufferedPositionUs = bufferedPositionUs;
|
||||
this.totalBufferedDurationUs = totalBufferedDurationUs;
|
||||
this.positionUs = positionUs;
|
||||
this.offloadSchedulingEnabled = offloadSchedulingEnabled;
|
||||
this.sleepingForOffload = sleepingForOffload;
|
||||
}
|
||||
|
||||
|
|
@ -233,7 +227,6 @@ import java.util.List;
|
|||
bufferedPositionUs,
|
||||
totalBufferedDurationUs,
|
||||
positionUs,
|
||||
offloadSchedulingEnabled,
|
||||
sleepingForOffload);
|
||||
}
|
||||
|
||||
|
|
@ -263,7 +256,6 @@ import java.util.List;
|
|||
bufferedPositionUs,
|
||||
totalBufferedDurationUs,
|
||||
positionUs,
|
||||
offloadSchedulingEnabled,
|
||||
sleepingForOffload);
|
||||
}
|
||||
|
||||
|
|
@ -293,7 +285,6 @@ import java.util.List;
|
|||
bufferedPositionUs,
|
||||
totalBufferedDurationUs,
|
||||
positionUs,
|
||||
offloadSchedulingEnabled,
|
||||
sleepingForOffload);
|
||||
}
|
||||
|
||||
|
|
@ -323,7 +314,6 @@ import java.util.List;
|
|||
bufferedPositionUs,
|
||||
totalBufferedDurationUs,
|
||||
positionUs,
|
||||
offloadSchedulingEnabled,
|
||||
sleepingForOffload);
|
||||
}
|
||||
|
||||
|
|
@ -353,7 +343,6 @@ import java.util.List;
|
|||
bufferedPositionUs,
|
||||
totalBufferedDurationUs,
|
||||
positionUs,
|
||||
offloadSchedulingEnabled,
|
||||
sleepingForOffload);
|
||||
}
|
||||
|
||||
|
|
@ -383,7 +372,6 @@ import java.util.List;
|
|||
bufferedPositionUs,
|
||||
totalBufferedDurationUs,
|
||||
positionUs,
|
||||
offloadSchedulingEnabled,
|
||||
sleepingForOffload);
|
||||
}
|
||||
|
||||
|
|
@ -417,7 +405,6 @@ import java.util.List;
|
|||
bufferedPositionUs,
|
||||
totalBufferedDurationUs,
|
||||
positionUs,
|
||||
offloadSchedulingEnabled,
|
||||
sleepingForOffload);
|
||||
}
|
||||
|
||||
|
|
@ -447,38 +434,6 @@ import java.util.List;
|
|||
bufferedPositionUs,
|
||||
totalBufferedDurationUs,
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -508,7 +463,6 @@ import java.util.List;
|
|||
bufferedPositionUs,
|
||||
totalBufferedDurationUs,
|
||||
positionUs,
|
||||
offloadSchedulingEnabled,
|
||||
sleepingForOffload);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import android.media.AudioManager;
|
|||
import android.media.AudioTrack;
|
||||
import android.media.PlaybackParams;
|
||||
import android.media.metrics.LogSessionId;
|
||||
import android.os.ConditionVariable;
|
||||
import android.os.Handler;
|
||||
import android.os.SystemClock;
|
||||
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.audio.AudioProcessor.UnhandledAudioFormatException;
|
||||
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.MimeTypes;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
|
@ -606,7 +607,8 @@ public final class DefaultAudioSink implements AudioSink {
|
|||
enableAudioTrackPlaybackParams = Util.SDK_INT >= 23 && builder.enableAudioTrackPlaybackParams;
|
||||
offloadMode = Util.SDK_INT >= 29 ? builder.offloadMode : OFFLOAD_MODE_DISABLED;
|
||||
audioTrackBufferSizeProvider = builder.audioTrackBufferSizeProvider;
|
||||
releasingConditionVariable = new ConditionVariable(true);
|
||||
releasingConditionVariable = new ConditionVariable(Clock.DEFAULT);
|
||||
releasingConditionVariable.open();
|
||||
audioTrackPositionTracker = new AudioTrackPositionTracker(new PositionTrackerListener());
|
||||
channelMappingAudioProcessor = new ChannelMappingAudioProcessor();
|
||||
trimmingAudioProcessor = new TrimmingAudioProcessor();
|
||||
|
|
@ -831,13 +833,15 @@ public final class DefaultAudioSink implements AudioSink {
|
|||
}
|
||||
}
|
||||
|
||||
private void initializeAudioTrack() throws InitializationException {
|
||||
// If we're asynchronously releasing a previous audio track then we block until it has been
|
||||
private boolean initializeAudioTrack() throws InitializationException {
|
||||
// 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
|
||||
// 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
|
||||
// initialization of the audio track to fail.
|
||||
releasingConditionVariable.block();
|
||||
if (!releasingConditionVariable.isOpen()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
audioTrack = buildAudioTrackWithRetry();
|
||||
if (isOffloadedPlayback(audioTrack)) {
|
||||
|
|
@ -865,6 +869,7 @@ public final class DefaultAudioSink implements AudioSink {
|
|||
}
|
||||
|
||||
startMediaTimeUsNeedsInit = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -921,7 +926,10 @@ public final class DefaultAudioSink implements AudioSink {
|
|||
|
||||
if (!isAudioTrackInitialized()) {
|
||||
try {
|
||||
initializeAudioTrack();
|
||||
if (!initializeAudioTrack()) {
|
||||
// Not yet ready for initialization of a new AudioTrack.
|
||||
return false;
|
||||
}
|
||||
} catch (InitializationException e) {
|
||||
if (e.isRecoverable) {
|
||||
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) {
|
||||
if (profileLevel.profile == profile && profileLevel.level >= level) {
|
||||
if (profileLevel.profile == profile
|
||||
&& profileLevel.level >= level
|
||||
&& !needsProfileExcludedWorkaround(mimeType, profile)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -829,4 +831,15 @@ public final class MediaCodecInfo {
|
|||
}
|
||||
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) {
|
||||
this.dataSourceFactory = dataSourceFactory;
|
||||
delegateFactoryLoader.setDataSourceFactory(dataSourceFactory);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -576,6 +577,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
|||
this.dataSourceFactory = dataSourceFactory;
|
||||
// TODO(b/233577470): Call MediaSource.Factory.setDataSourceFactory on each value when it
|
||||
// exists on the interface.
|
||||
mediaSourceFactorySuppliers.clear();
|
||||
mediaSourceFactories.clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -609,6 +611,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
|||
}
|
||||
|
||||
@Nullable Supplier<MediaSource.Factory> mediaSourceFactorySupplier = null;
|
||||
DataSource.Factory dataSourceFactory = checkNotNull(this.dataSourceFactory);
|
||||
try {
|
||||
Class<? extends MediaSource.Factory> clazz;
|
||||
switch (contentType) {
|
||||
|
|
@ -616,20 +619,20 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
|||
clazz =
|
||||
Class.forName("com.google.android.exoplayer2.source.dash.DashMediaSource$Factory")
|
||||
.asSubclass(MediaSource.Factory.class);
|
||||
mediaSourceFactorySupplier = () -> newInstance(clazz, checkNotNull(dataSourceFactory));
|
||||
mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
|
||||
break;
|
||||
case C.CONTENT_TYPE_SS:
|
||||
clazz =
|
||||
Class.forName(
|
||||
"com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource$Factory")
|
||||
.asSubclass(MediaSource.Factory.class);
|
||||
mediaSourceFactorySupplier = () -> newInstance(clazz, checkNotNull(dataSourceFactory));
|
||||
mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
|
||||
break;
|
||||
case C.CONTENT_TYPE_HLS:
|
||||
clazz =
|
||||
Class.forName("com.google.android.exoplayer2.source.hls.HlsMediaSource$Factory")
|
||||
.asSubclass(MediaSource.Factory.class);
|
||||
mediaSourceFactorySupplier = () -> newInstance(clazz, checkNotNull(dataSourceFactory));
|
||||
mediaSourceFactorySupplier = () -> newInstance(clazz, dataSourceFactory);
|
||||
break;
|
||||
case C.CONTENT_TYPE_RTSP:
|
||||
clazz =
|
||||
|
|
@ -639,9 +642,7 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory {
|
|||
break;
|
||||
case C.CONTENT_TYPE_OTHER:
|
||||
mediaSourceFactorySupplier =
|
||||
() ->
|
||||
new ProgressiveMediaSource.Factory(
|
||||
checkNotNull(dataSourceFactory), extractorsFactory);
|
||||
() -> new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory);
|
||||
break;
|
||||
default:
|
||||
// Do nothing.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
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.min;
|
||||
|
||||
|
|
@ -134,7 +135,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
private boolean seenFirstTrackSelection;
|
||||
private boolean notifyDiscontinuity;
|
||||
private int enabledTrackCount;
|
||||
private long length;
|
||||
private boolean isLengthKnown;
|
||||
|
||||
private long lastSeekPositionUs;
|
||||
private long pendingResetPositionUs;
|
||||
|
|
@ -192,15 +193,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
onContinueLoadingRequestedRunnable =
|
||||
() -> {
|
||||
if (!released) {
|
||||
Assertions.checkNotNull(callback)
|
||||
.onContinueLoadingRequested(ProgressiveMediaPeriod.this);
|
||||
checkNotNull(callback).onContinueLoadingRequested(ProgressiveMediaPeriod.this);
|
||||
}
|
||||
};
|
||||
handler = Util.createHandlerForCurrentLooper();
|
||||
sampleQueueTrackIds = new TrackId[0];
|
||||
sampleQueues = new SampleQueue[0];
|
||||
pendingResetPositionUs = C.TIME_UNSET;
|
||||
length = C.LENGTH_UNSET;
|
||||
durationUs = C.TIME_UNSET;
|
||||
dataType = C.DATA_TYPE_MEDIA;
|
||||
}
|
||||
|
|
@ -366,7 +365,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
|
||||
@Override
|
||||
public long getNextLoadPositionUs() {
|
||||
return enabledTrackCount == 0 ? C.TIME_END_OF_SOURCE : getBufferedPositionUs();
|
||||
return getBufferedPositionUs();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -382,8 +381,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
@Override
|
||||
public long getBufferedPositionUs() {
|
||||
assertPrepared();
|
||||
boolean[] trackIsAudioVideoFlags = trackState.trackIsAudioVideoFlags;
|
||||
if (loadingFinished) {
|
||||
if (loadingFinished || enabledTrackCount == 0) {
|
||||
return C.TIME_END_OF_SOURCE;
|
||||
} else if (isPendingReset()) {
|
||||
return pendingResetPositionUs;
|
||||
|
|
@ -393,14 +391,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
// Ignore non-AV tracks, which may be sparse or poorly interleaved.
|
||||
int trackCount = sampleQueues.length;
|
||||
for (int i = 0; i < trackCount; i++) {
|
||||
if (trackIsAudioVideoFlags[i] && !sampleQueues[i].isLastSampleQueued()) {
|
||||
if (trackState.trackIsAudioVideoFlags[i]
|
||||
&& trackState.trackEnabledStates[i]
|
||||
&& !sampleQueues[i].isLastSampleQueued()) {
|
||||
largestQueuedTimestampUs =
|
||||
min(largestQueuedTimestampUs, sampleQueues[i].getLargestQueuedTimestampUs());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (largestQueuedTimestampUs == Long.MAX_VALUE) {
|
||||
largestQueuedTimestampUs = getLargestQueuedTimestampUs();
|
||||
largestQueuedTimestampUs = getLargestQueuedTimestampUs(/* includeDisabledTracks= */ false);
|
||||
}
|
||||
return largestQueuedTimestampUs == Long.MIN_VALUE
|
||||
? lastSeekPositionUs
|
||||
|
|
@ -536,7 +536,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
for (SampleQueue sampleQueue : sampleQueues) {
|
||||
sampleQueue.reset();
|
||||
}
|
||||
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
|
||||
checkNotNull(callback).onContinueLoadingRequested(this);
|
||||
}
|
||||
|
||||
private boolean suppressRead() {
|
||||
|
|
@ -550,7 +550,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
ExtractingLoadable loadable, long elapsedRealtimeMs, long loadDurationMs) {
|
||||
if (durationUs == C.TIME_UNSET && seekMap != null) {
|
||||
boolean isSeekable = seekMap.isSeekable();
|
||||
long largestQueuedTimestampUs = getLargestQueuedTimestampUs();
|
||||
long largestQueuedTimestampUs =
|
||||
getLargestQueuedTimestampUs(/* includeDisabledTracks= */ true);
|
||||
durationUs =
|
||||
largestQueuedTimestampUs == Long.MIN_VALUE
|
||||
? 0
|
||||
|
|
@ -577,9 +578,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
/* trackSelectionData= */ null,
|
||||
/* mediaStartTimeUs= */ loadable.seekTimeUs,
|
||||
durationUs);
|
||||
copyLengthFromLoader(loadable);
|
||||
loadingFinished = true;
|
||||
Assertions.checkNotNull(callback).onContinueLoadingRequested(this);
|
||||
checkNotNull(callback).onContinueLoadingRequested(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -606,12 +606,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
/* mediaStartTimeUs= */ loadable.seekTimeUs,
|
||||
durationUs);
|
||||
if (!released) {
|
||||
copyLengthFromLoader(loadable);
|
||||
for (SampleQueue sampleQueue : sampleQueues) {
|
||||
sampleQueue.reset();
|
||||
}
|
||||
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,
|
||||
IOException error,
|
||||
int errorCount) {
|
||||
copyLengthFromLoader(loadable);
|
||||
StatsDataSource dataSource = loadable.dataSource;
|
||||
LoadEventInfo loadEventInfo =
|
||||
new LoadEventInfo(
|
||||
|
|
@ -709,6 +707,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
|
||||
// Internal methods.
|
||||
|
||||
private void onLengthKnown() {
|
||||
handler.post(() -> isLengthKnown = true);
|
||||
}
|
||||
|
||||
private TrackOutput prepareTrackOutput(TrackId id) {
|
||||
int trackCount = sampleQueues.length;
|
||||
for (int i = 0; i < trackCount; i++) {
|
||||
|
|
@ -732,7 +734,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
private void setSeekMap(SeekMap seekMap) {
|
||||
this.seekMap = icyHeaders == null ? seekMap : new Unseekable(/* durationUs= */ C.TIME_UNSET);
|
||||
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;
|
||||
listener.onSourceInfoRefreshed(durationUs, seekMap.isSeekable(), isLive);
|
||||
if (!prepared) {
|
||||
|
|
@ -754,7 +756,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
TrackGroup[] trackArray = new TrackGroup[trackCount];
|
||||
boolean[] trackIsAudioVideoFlags = new boolean[trackCount];
|
||||
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;
|
||||
boolean isAudio = MimeTypes.isAudio(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);
|
||||
prepared = true;
|
||||
Assertions.checkNotNull(callback).onPrepared(this);
|
||||
}
|
||||
|
||||
private void copyLengthFromLoader(ExtractingLoadable loadable) {
|
||||
if (length == C.LENGTH_UNSET) {
|
||||
length = loadable.length;
|
||||
}
|
||||
checkNotNull(callback).onPrepared(this);
|
||||
}
|
||||
|
||||
private void startLoading() {
|
||||
|
|
@ -806,7 +802,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
return;
|
||||
}
|
||||
loadable.setLoadPosition(
|
||||
Assertions.checkNotNull(seekMap).getSeekPoints(pendingResetPositionUs).first.position,
|
||||
checkNotNull(seekMap).getSeekPoints(pendingResetPositionUs).first.position,
|
||||
pendingResetPositionUs);
|
||||
for (SampleQueue sampleQueue : sampleQueues) {
|
||||
sampleQueue.setStartTimeUs(pendingResetPositionUs);
|
||||
|
|
@ -839,7 +835,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
* retry.
|
||||
*/
|
||||
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
|
||||
// request data starting from the point it left off.
|
||||
extractedSamplesCountAtStartOfLoad = currentExtractedSampleCount;
|
||||
|
|
@ -903,11 +899,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
return extractedSamplesCount;
|
||||
}
|
||||
|
||||
private long getLargestQueuedTimestampUs() {
|
||||
private long getLargestQueuedTimestampUs(boolean includeDisabledTracks) {
|
||||
long largestQueuedTimestampUs = Long.MIN_VALUE;
|
||||
for (SampleQueue sampleQueue : sampleQueues) {
|
||||
largestQueuedTimestampUs =
|
||||
max(largestQueuedTimestampUs, sampleQueue.getLargestQueuedTimestampUs());
|
||||
for (int i = 0; i < sampleQueues.length; i++) {
|
||||
if (includeDisabledTracks || checkNotNull(trackState).trackEnabledStates[i]) {
|
||||
largestQueuedTimestampUs =
|
||||
max(largestQueuedTimestampUs, sampleQueues[i].getLargestQueuedTimestampUs());
|
||||
}
|
||||
}
|
||||
return largestQueuedTimestampUs;
|
||||
}
|
||||
|
|
@ -919,8 +917,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
@EnsuresNonNull({"trackState", "seekMap"})
|
||||
private void assertPrepared() {
|
||||
Assertions.checkState(prepared);
|
||||
Assertions.checkNotNull(trackState);
|
||||
Assertions.checkNotNull(seekMap);
|
||||
checkNotNull(trackState);
|
||||
checkNotNull(seekMap);
|
||||
}
|
||||
|
||||
private final class SampleStreamImpl implements SampleStream {
|
||||
|
|
@ -969,7 +967,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
private boolean pendingExtractorSeek;
|
||||
private long seekTimeUs;
|
||||
private DataSpec dataSpec;
|
||||
private long length;
|
||||
@Nullable private TrackOutput icyTrackOutput;
|
||||
private boolean seenIcyMetadata;
|
||||
|
||||
|
|
@ -987,7 +984,6 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
this.loadCondition = loadCondition;
|
||||
this.positionHolder = new PositionHolder();
|
||||
this.pendingExtractorSeek = true;
|
||||
this.length = C.LENGTH_UNSET;
|
||||
loadTaskId = LoadEventInfo.getNewId();
|
||||
dataSpec = buildDataSpec(/* position= */ 0);
|
||||
}
|
||||
|
|
@ -1006,9 +1002,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
try {
|
||||
long position = positionHolder.position;
|
||||
dataSpec = buildDataSpec(position);
|
||||
length = dataSource.open(dataSpec);
|
||||
long length = dataSource.open(dataSpec);
|
||||
if (length != C.LENGTH_UNSET) {
|
||||
length += position;
|
||||
onLengthKnown();
|
||||
}
|
||||
icyHeaders = IcyHeaders.parse(dataSource.getResponseHeaders());
|
||||
DataSource extractorDataSource = dataSource;
|
||||
|
|
@ -1064,9 +1061,12 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
public void onIcyMetadata(ParsableByteArray metadata) {
|
||||
// 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.
|
||||
long timeUs = !seenIcyMetadata ? seekTimeUs : max(getLargestQueuedTimestampUs(), seekTimeUs);
|
||||
long timeUs =
|
||||
!seenIcyMetadata
|
||||
? seekTimeUs
|
||||
: max(getLargestQueuedTimestampUs(/* includeDisabledTracks= */ true), seekTimeUs);
|
||||
int length = metadata.bytesLeft();
|
||||
TrackOutput icyTrackOutput = Assertions.checkNotNull(this.icyTrackOutput);
|
||||
TrackOutput icyTrackOutput = checkNotNull(this.icyTrackOutput);
|
||||
icyTrackOutput.sampleData(metadata, length);
|
||||
icyTrackOutput.sampleMetadata(
|
||||
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}.
|
||||
*
|
||||
* <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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -626,7 +626,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
surface = placeholderSurface;
|
||||
} else {
|
||||
MediaCodecInfo codecInfo = getCodecInfo();
|
||||
if (codecInfo != null && shouldUseDummySurface(codecInfo)) {
|
||||
if (codecInfo != null && shouldUsePlaceholderSurface(codecInfo)) {
|
||||
placeholderSurface = PlaceholderSurface.newInstanceV17(context, codecInfo.secure);
|
||||
surface = placeholderSurface;
|
||||
}
|
||||
|
|
@ -672,7 +672,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
|
||||
@Override
|
||||
protected boolean shouldInitCodec(MediaCodecInfo codecInfo) {
|
||||
return surface != null || shouldUseDummySurface(codecInfo);
|
||||
return surface != null || shouldUsePlaceholderSurface(codecInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -703,7 +703,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
deviceNeedsNoPostProcessWorkaround,
|
||||
tunneling ? tunnelingAudioSessionId : C.AUDIO_SESSION_ID_UNSET);
|
||||
if (surface == null) {
|
||||
if (!shouldUseDummySurface(codecInfo)) {
|
||||
if (!shouldUsePlaceholderSurface(codecInfo)) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
if (placeholderSurface == null) {
|
||||
|
|
@ -1330,7 +1330,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
maybeNotifyRenderedFirstFrame();
|
||||
}
|
||||
|
||||
private boolean shouldUseDummySurface(MediaCodecInfo codecInfo) {
|
||||
private boolean shouldUsePlaceholderSurface(MediaCodecInfo codecInfo) {
|
||||
return Util.SDK_INT >= 23
|
||||
&& !tunneling
|
||||
&& !codecNeedsSetOutputSurfaceWorkaround(codecInfo.name)
|
||||
|
|
@ -1569,7 +1569,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
}
|
||||
if (haveUnknownDimensions) {
|
||||
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) {
|
||||
maxWidth = max(maxWidth, codecMaxSize.x);
|
||||
maxHeight = max(maxHeight, codecMaxSize.y);
|
||||
|
|
@ -1597,8 +1597,10 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
|
|||
*
|
||||
* @param codecInfo Information about the {@link MediaCodec} 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) {
|
||||
boolean 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.runUntilPlaybackState;
|
||||
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.runUntilTimelineChanged;
|
||||
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_OFFSET_IN_FIRST_PERIOD_US;
|
||||
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 org.junit.Assert.assertArrayEquals;
|
||||
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.MediaPeriodId;
|
||||
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.TrackGroup;
|
||||
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.FakeTrackSelector;
|
||||
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.trackselection.DefaultTrackSelector;
|
||||
import com.google.android.exoplayer2.upstream.Allocation;
|
||||
|
|
@ -6502,6 +6502,53 @@ public final class ExoPlayerTest {
|
|||
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
|
||||
public void setMediaSources_empty_whenEmpty_correctMaskingMediaItemIndex() throws Exception {
|
||||
final int[] currentMediaItemIndices = {C.INDEX_UNSET, C.INDEX_UNSET, C.INDEX_UNSET};
|
||||
|
|
@ -9625,47 +9672,16 @@ public final class ExoPlayerTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void enableOffloadSchedulingWhileIdle_isToggled_isReported() throws Exception {
|
||||
public void enableOffloadScheduling_isReported() throws Exception {
|
||||
ExoPlayer player = new TestExoPlayerBuilder(context).build();
|
||||
ExoPlayer.AudioOffloadListener mockListener = mock(ExoPlayer.AudioOffloadListener.class);
|
||||
player.addAudioOffloadListener(mockListener);
|
||||
|
||||
player.experimentalSetOffloadSchedulingEnabled(true);
|
||||
assertThat(runUntilReceiveOffloadSchedulingEnabledNewState(player)).isTrue();
|
||||
verify(mockListener).onExperimentalOffloadSchedulingEnabledChanged(true);
|
||||
|
||||
player.experimentalSetOffloadSchedulingEnabled(false);
|
||||
assertThat(runUntilReceiveOffloadSchedulingEnabledNewState(player)).isFalse();
|
||||
}
|
||||
|
||||
@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();
|
||||
verify(mockListener).onExperimentalOffloadSchedulingEnabledChanged(false);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -12286,6 +12302,6 @@ public final class ExoPlayerTest {
|
|||
* Returns an argument matcher for {@link Timeline} instances that ignores period and window uids.
|
||||
*/
|
||||
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,
|
||||
/* totalBufferedDurationUs= */ 0,
|
||||
/* positionUs= */ 0,
|
||||
/* offloadSchedulingEnabled= */ false,
|
||||
/* sleepingForOffload= */ false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ public class Mp4PlaybackTest {
|
|||
"sample_eac3joc.mp4",
|
||||
"sample_fragmented.mp4",
|
||||
"sample_fragmented_seekable.mp4",
|
||||
"sample_fragmented_large_bitrates.mp4",
|
||||
"sample_fragmented_sei.mp4",
|
||||
"sample_mdat_too_long.mp4",
|
||||
"sample.mp4",
|
||||
|
|
|
|||
|
|
@ -597,6 +597,9 @@ public class DashManifestParser extends DefaultHandler
|
|||
case "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed":
|
||||
uuid = C.WIDEVINE_UUID;
|
||||
break;
|
||||
case "urn:uuid:e2719d58-a985-b3c9-781a-b030af78d30e":
|
||||
uuid = C.CLEARKEY_UUID;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -604,7 +607,9 @@ public class DashManifestParser extends DefaultHandler
|
|||
|
||||
do {
|
||||
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");
|
||||
} else if (data == null
|
||||
&& XmlPullParserUtil.isStartTagIgnorePrefix(xpp, "pssh")
|
||||
|
|
@ -851,6 +856,7 @@ public class DashManifestParser extends DefaultHandler
|
|||
ArrayList<SchemeData> drmSchemeDatas = representationInfo.drmSchemeDatas;
|
||||
drmSchemeDatas.addAll(extraDrmSchemeDatas);
|
||||
if (!drmSchemeDatas.isEmpty()) {
|
||||
fillInClearKeyInformation(drmSchemeDatas);
|
||||
filterRedundantIncompleteSchemeDatas(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.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.source.dash;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
|
@ -22,9 +23,13 @@ import androidx.test.core.app.ApplicationProvider;
|
|||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
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.MediaSource;
|
||||
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import java.io.IOException;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
|
|
@ -82,4 +87,53 @@ public class DefaultMediaSourceFactoryTest {
|
|||
|
||||
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 com.google.android.exoplayer2.C;
|
||||
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.source.dash.manifest.Representation.MultiSegmentRepresentation;
|
||||
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";
|
||||
private static final String 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 = "<" + NEXT_TAG_NAME + "/>";
|
||||
|
|
@ -880,6 +883,37 @@ public class DashManifestParserTest {
|
|||
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) {
|
||||
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.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.primitives.Ints;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -1303,7 +1304,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||
}
|
||||
|
||||
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();
|
||||
|
|
@ -1609,7 +1612,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||
.setLanguage(language);
|
||||
|
||||
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();
|
||||
|
|
@ -1659,7 +1664,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||
parent.skipBytes(2);
|
||||
}
|
||||
if ((flags & 0x40 /* URL_Flag */) != 0) {
|
||||
parent.skipBytes(parent.readUnsignedShort());
|
||||
parent.skipBytes(parent.readUnsignedByte());
|
||||
}
|
||||
if ((flags & 0x20 /* OCRstreamFlag */) != 0) {
|
||||
parent.skipBytes(2);
|
||||
|
|
@ -1683,8 +1688,8 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||
}
|
||||
|
||||
parent.skipBytes(4);
|
||||
int peakBitrate = parent.readUnsignedIntToInt();
|
||||
int bitrate = parent.readUnsignedIntToInt();
|
||||
long peakBitrate = parent.readUnsignedInt();
|
||||
long bitrate = parent.readUnsignedInt();
|
||||
|
||||
// Start of the DecoderSpecificInfo.
|
||||
parent.skipBytes(1); // DecoderSpecificInfo tag
|
||||
|
|
@ -1943,14 +1948,14 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
|
|||
private static final class EsdsData {
|
||||
private final @NullableType String mimeType;
|
||||
private final byte @NullableType [] initializationData;
|
||||
private final int bitrate;
|
||||
private final int peakBitrate;
|
||||
private final long bitrate;
|
||||
private final long peakBitrate;
|
||||
|
||||
public EsdsData(
|
||||
@NullableType String mimeType,
|
||||
byte @NullableType [] initializationData,
|
||||
int bitrate,
|
||||
int peakBitrate) {
|
||||
long bitrate,
|
||||
long peakBitrate) {
|
||||
this.mimeType = mimeType;
|
||||
this.initializationData = initializationData;
|
||||
this.bitrate = bitrate;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package com.google.android.exoplayer2.util;
|
|||
import static java.lang.Math.min;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import java.nio.ByteBuffer;
|
||||
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) {
|
||||
int numShortTermRefPicSets = bitArray.readUnsignedExpGolombCodedInt();
|
||||
boolean interRefPicSetPredictionFlag = false;
|
||||
int numNegativePics;
|
||||
int numPositivePics;
|
||||
// As this method applies in a SPS, the only element of NumDeltaPocs accessed is the previous
|
||||
// one, so we just keep track of that rather than storing the whole array.
|
||||
// RefRpsIdx = stRpsIdx - (delta_idx_minus1 + 1) and delta_idx_minus1 is always zero in SPS.
|
||||
int previousNumDeltaPocs = 0;
|
||||
// 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++) {
|
||||
if (stRpsIdx != 0) {
|
||||
interRefPicSetPredictionFlag = bitArray.readBit();
|
||||
}
|
||||
int numNegativePics;
|
||||
int numPositivePics;
|
||||
int[] deltaPocS0;
|
||||
int[] deltaPocS1;
|
||||
|
||||
boolean interRefPicSetPredictionFlag = stRpsIdx != 0 && bitArray.readBit();
|
||||
if (interRefPicSetPredictionFlag) {
|
||||
bitArray.skipBit(); // delta_rps_sign
|
||||
bitArray.readUnsignedExpGolombCodedInt(); // abs_delta_rps_minus1
|
||||
int previousNumDeltaPocs = previousNumNegativePics + previousNumPositivePics;
|
||||
|
||||
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++) {
|
||||
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 {
|
||||
numNegativePics = bitArray.readUnsignedExpGolombCodedInt();
|
||||
numPositivePics = bitArray.readUnsignedExpGolombCodedInt();
|
||||
previousNumDeltaPocs = numNegativePics + numPositivePics;
|
||||
deltaPocS0 = new int[numNegativePics];
|
||||
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]
|
||||
}
|
||||
deltaPocS1 = new int[numPositivePics];
|
||||
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]
|
||||
}
|
||||
}
|
||||
previousNumNegativePics = numNegativePics;
|
||||
previousNumPositivePics = numPositivePics;
|
||||
previousDeltaPocS0 = deltaPocS0;
|
||||
previousDeltaPocS1 = deltaPocS1;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -122,6 +122,15 @@ public final class FragmentedMp4ExtractorTest {
|
|||
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) {
|
||||
return () ->
|
||||
new FragmentedMp4Extractor(
|
||||
|
|
|
|||
|
|
@ -168,6 +168,32 @@ public final class NalUnitUtilTest {
|
|||
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() {
|
||||
byte[] data = new byte[20];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.source.hls;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
|
@ -22,9 +23,13 @@ import androidx.test.core.app.ApplicationProvider;
|
|||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
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.MediaSource;
|
||||
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import java.io.IOException;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
|
|
@ -82,4 +87,53 @@ public class DefaultMediaSourceFactoryTest {
|
|||
|
||||
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;
|
||||
|
||||
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 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. */
|
||||
private int fragmentedSampleSizeBytes;
|
||||
|
||||
private long fragmentedSampleTimeUs;
|
||||
|
||||
private long startTimeOffsetUs;
|
||||
/**
|
||||
* 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;
|
||||
previousSequenceNumber = C.INDEX_UNSET;
|
||||
fragmentedSampleSizeBytes = C.LENGTH_UNSET;
|
||||
fragmentedSampleTimeUs = C.TIME_UNSET;
|
||||
// The start time offset must be 0 until the first seek.
|
||||
startTimeOffsetUs = 0;
|
||||
gotFirstPacketOfVp8Frame = false;
|
||||
|
|
@ -81,7 +86,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onReceivingFirstPacket(long timestamp, int sequenceNumber) {}
|
||||
public void onReceivingFirstPacket(long timestamp, int sequenceNumber) {
|
||||
checkState(firstReceivedTimestamp == C.TIME_UNSET);
|
||||
firstReceivedTimestamp = timestamp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void consume(
|
||||
|
|
@ -113,21 +121,16 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
|
||||
int fragmentSize = data.bytesLeft();
|
||||
trackOutput.sampleData(data, fragmentSize);
|
||||
fragmentedSampleSizeBytes += fragmentSize;
|
||||
if (fragmentedSampleSizeBytes == C.LENGTH_UNSET) {
|
||||
fragmentedSampleSizeBytes = fragmentSize;
|
||||
} else {
|
||||
fragmentedSampleSizeBytes += fragmentSize;
|
||||
}
|
||||
|
||||
fragmentedSampleTimeUs = toSampleUs(startTimeOffsetUs, timestamp, firstReceivedTimestamp);
|
||||
|
||||
if (rtpMarker) {
|
||||
if (firstReceivedTimestamp == C.TIME_UNSET) {
|
||||
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;
|
||||
outputSampleMetadataForFragmentedPackets();
|
||||
}
|
||||
previousSequenceNumber = sequenceNumber;
|
||||
}
|
||||
|
|
@ -147,18 +150,18 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
private boolean validateVp8Descriptor(ParsableByteArray payload, int packetSequenceNumber) {
|
||||
// VP8 Payload Descriptor is defined in RFC7741 Section 4.2.
|
||||
int header = payload.readUnsignedByte();
|
||||
if (!gotFirstPacketOfVp8Frame) {
|
||||
// TODO(b/198620566) Consider using ParsableBitArray.
|
||||
// For start of VP8 partition S=1 and PID=0 as per RFC7741 Section 4.2.
|
||||
if ((header & 0x10) != 0x1 || (header & 0x07) != 0) {
|
||||
Log.w(TAG, "RTP packet is not the start of a new VP8 partition, skipping.");
|
||||
return false;
|
||||
// TODO(b/198620566) Consider using ParsableBitArray.
|
||||
// For start of VP8 partition S=1 and PID=0 as per RFC7741 Section 4.2.
|
||||
if ((header & 0x10) == 0x10 && (header & 0x07) == 0) {
|
||||
if (gotFirstPacketOfVp8Frame && fragmentedSampleSizeBytes > 0) {
|
||||
// Received new VP8 fragment, output data of previous fragment to decoder.
|
||||
outputSampleMetadataForFragmentedPackets();
|
||||
}
|
||||
gotFirstPacketOfVp8Frame = true;
|
||||
} else {
|
||||
} else if (gotFirstPacketOfVp8Frame) {
|
||||
// Check that this packet is in the sequence of the previous packet.
|
||||
int expectedSequenceNumber = RtpPacket.getNextSequenceNumber(previousSequenceNumber);
|
||||
if (packetSequenceNumber != expectedSequenceNumber) {
|
||||
if (packetSequenceNumber < expectedSequenceNumber) {
|
||||
Log.w(
|
||||
TAG,
|
||||
Util.formatInvariant(
|
||||
|
|
@ -167,6 +170,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
expectedSequenceNumber, packetSequenceNumber));
|
||||
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.
|
||||
|
|
@ -195,6 +201,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
|||
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(
|
||||
long startTimeOffsetUs, long rtpTimestamp, long firstReceivedRtpTimestamp) {
|
||||
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.jetbrains.kotlin:kotlin-annotations-jvm:' + kotlinAnnotationsVersion
|
||||
implementation 'androidx.annotation:annotation:' + androidxAnnotationVersion
|
||||
testImplementation project(modulePrefix + 'robolectricutils')
|
||||
testImplementation project(modulePrefix + 'testutils')
|
||||
testImplementation 'org.robolectric:robolectric:' + robolectricVersion
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package com.google.android.exoplayer2.source.smoothstreaming;
|
||||
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.content.Context;
|
||||
|
|
@ -22,9 +23,13 @@ import androidx.test.core.app.ApplicationProvider;
|
|||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
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.MediaSource;
|
||||
import com.google.android.exoplayer2.testutil.FakeDataSource;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import java.io.IOException;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
|
|
@ -93,4 +98,53 @@ public class DefaultMediaSourceFactoryTest {
|
|||
|
||||
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) {
|
||||
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(
|
||||
v -> {
|
||||
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());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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;
|
||||
|
||||
import static com.google.android.exoplayer2.testutil.TestUtil.timelinesAreSame;
|
||||
|
||||
import android.os.Looper;
|
||||
import android.view.Surface;
|
||||
import androidx.annotation.Nullable;
|
||||
|
|
@ -763,7 +765,7 @@ public abstract class Action {
|
|||
@Nullable Timeline expectedTimeline,
|
||||
@Player.TimelineChangeReason int expectedReason) {
|
||||
super(tag, "WaitForTimelineChanged");
|
||||
this.expectedTimeline = expectedTimeline != null ? new NoUidTimeline(expectedTimeline) : null;
|
||||
this.expectedTimeline = expectedTimeline;
|
||||
this.ignoreExpectedReason = false;
|
||||
this.expectedReason = expectedReason;
|
||||
}
|
||||
|
|
@ -795,7 +797,7 @@ public abstract class Action {
|
|||
@Override
|
||||
public void onTimelineChanged(
|
||||
Timeline timeline, @Player.TimelineChangeReason int reason) {
|
||||
if ((expectedTimeline == null || new NoUidTimeline(timeline).equals(expectedTimeline))
|
||||
if ((expectedTimeline == null || timelinesAreSame(timeline, expectedTimeline))
|
||||
&& (ignoreExpectedReason || expectedReason == reason)) {
|
||||
player.removeListener(this);
|
||||
nextAction.schedule(player, trackSelector, surface, handler);
|
||||
|
|
@ -803,8 +805,8 @@ public abstract class Action {
|
|||
}
|
||||
};
|
||||
player.addListener(listener);
|
||||
Timeline currentTimeline = new NoUidTimeline(player.getCurrentTimeline());
|
||||
if (currentTimeline.equals(expectedTimeline)) {
|
||||
if (expectedTimeline != null
|
||||
&& timelinesAreSame(player.getCurrentTimeline(), expectedTimeline)) {
|
||||
player.removeListener(listener);
|
||||
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.HandlerWrapper;
|
||||
import com.google.android.exoplayer2.util.MimeTypes;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
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.
|
||||
*/
|
||||
public void assertTimelinesSame(Timeline... timelines) {
|
||||
assertThat(this.timelines).hasSize(timelines.length);
|
||||
for (int i = 0; i < timelines.length; i++) {
|
||||
assertThat(new NoUidTimeline(timelines[i]))
|
||||
.isEqualTo(new NoUidTimeline(this.timelines.get(i)));
|
||||
}
|
||||
TestUtil.assertTimelinesSame(
|
||||
ImmutableList.copyOf(this.timelines), ImmutableList.copyOf(timelines));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import com.google.android.exoplayer2.C;
|
|||
import com.google.android.exoplayer2.MediaItem;
|
||||
import com.google.android.exoplayer2.Player;
|
||||
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.util.Assertions;
|
||||
import com.google.android.exoplayer2.util.Util;
|
||||
|
|
@ -273,7 +274,7 @@ public final class FakeTimeline extends Timeline {
|
|||
private final TimelineWindowDefinition[] windowDefinitions;
|
||||
private final Object[] manifests;
|
||||
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
|
||||
|
|
@ -393,6 +394,19 @@ public final class FakeTimeline extends Timeline {
|
|||
* @param windowDefinitions A list of {@link TimelineWindowDefinition}s.
|
||||
*/
|
||||
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];
|
||||
System.arraycopy(manifests, 0, this.manifests, 0, min(this.manifests.length, manifests.length));
|
||||
this.windowDefinitions = windowDefinitions;
|
||||
|
|
@ -401,7 +415,7 @@ public final class FakeTimeline extends Timeline {
|
|||
for (int i = 0; i < windowDefinitions.length; i++) {
|
||||
periodOffsets[i + 1] = periodOffsets[i] + windowDefinitions[i].periodCount;
|
||||
}
|
||||
fakeShuffleOrder = new FakeShuffleOrder(windowDefinitions.length);
|
||||
this.shuffleOrder = shuffleOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -420,7 +434,7 @@ public final class FakeTimeline extends Timeline {
|
|||
? getFirstWindowIndex(shuffleModeEnabled)
|
||||
: C.INDEX_UNSET;
|
||||
}
|
||||
return shuffleModeEnabled ? fakeShuffleOrder.getNextIndex(windowIndex) : windowIndex + 1;
|
||||
return shuffleModeEnabled ? shuffleOrder.getNextIndex(windowIndex) : windowIndex + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -434,20 +448,20 @@ public final class FakeTimeline extends Timeline {
|
|||
? getLastWindowIndex(shuffleModeEnabled)
|
||||
: C.INDEX_UNSET;
|
||||
}
|
||||
return shuffleModeEnabled ? fakeShuffleOrder.getPreviousIndex(windowIndex) : windowIndex - 1;
|
||||
return shuffleModeEnabled ? shuffleOrder.getPreviousIndex(windowIndex) : windowIndex - 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLastWindowIndex(boolean shuffleModeEnabled) {
|
||||
return shuffleModeEnabled
|
||||
? fakeShuffleOrder.getLastIndex()
|
||||
? shuffleOrder.getLastIndex()
|
||||
: super.getLastWindowIndex(/* shuffleModeEnabled= */ false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFirstWindowIndex(boolean shuffleModeEnabled) {
|
||||
return shuffleModeEnabled
|
||||
? fakeShuffleOrder.getFirstIndex()
|
||||
? shuffleOrder.getFirstIndex()
|
||||
: 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
|
||||
@Nullable
|
||||
public PlaybackException getPlayerError() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ import com.google.android.exoplayer2.util.Assertions;
|
|||
import com.google.android.exoplayer2.util.Util;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.primitives.Bytes;
|
||||
import com.google.common.truth.Correspondence;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
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
|
||||
* from testing equality by not comparing period ids which may be different due to id mapping of
|
||||
* child source period ids.
|
||||
* from testing equality by not comparing:
|
||||
*
|
||||
* <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 expectedTimelines A list of expected {@link Timeline timelines}.
|
||||
*/
|
||||
public static void assertTimelinesSame(
|
||||
List<Timeline> actualTimelines, List<Timeline> expectedTimelines) {
|
||||
assertThat(actualTimelines).hasSize(expectedTimelines.size());
|
||||
for (int i = 0; i < actualTimelines.size(); i++) {
|
||||
assertThat(new NoUidTimeline(actualTimelines.get(i)))
|
||||
.isEqualTo(new NoUidTimeline(expectedTimelines.get(i)));
|
||||
}
|
||||
assertThat(actualTimelines)
|
||||
.comparingElementsUsing(
|
||||
Correspondence.from(
|
||||
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;
|
||||
}
|
||||
|
||||
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