Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
SDK
/
exoplayer
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Settings
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
16e6ea6e
authored
Mar 12, 2020
by
olly
Committed by
Oliver Woodman
Mar 19, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Fix spurious reset of PreparedState boolean flags
PiperOrigin-RevId: 300513930
parent
c85e5137
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
46 additions
and
41 deletions
library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java
library/core/src/main/java/com/google/android/exoplayer2/source/ProgressiveMediaPeriod.java
View file @
16e6ea6e
...
@@ -57,6 +57,7 @@ import java.util.Collections;
...
@@ -57,6 +57,7 @@ import java.util.Collections;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.Map
;
import
java.util.Map
;
import
org.checkerframework.checker.nullness.compatqual.NullableType
;
import
org.checkerframework.checker.nullness.compatqual.NullableType
;
import
org.checkerframework.checker.nullness.qual.EnsuresNonNull
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
/** A {@link MediaPeriod} that extracts data using an {@link Extractor}. */
/** A {@link MediaPeriod} that extracts data using an {@link Extractor}. */
...
@@ -111,23 +112,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -111,23 +112,24 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
private
final
Handler
handler
;
private
final
Handler
handler
;
@Nullable
private
Callback
callback
;
@Nullable
private
Callback
callback
;
@Nullable
private
SeekMap
seekMap
;
@Nullable
private
IcyHeaders
icyHeaders
;
@Nullable
private
IcyHeaders
icyHeaders
;
private
SampleQueue
[]
sampleQueues
;
private
SampleQueue
[]
sampleQueues
;
private
TrackId
[]
sampleQueueTrackIds
;
private
TrackId
[]
sampleQueueTrackIds
;
private
boolean
sampleQueuesBuilt
;
private
boolean
sampleQueuesBuilt
;
private
@MonotonicNonNull
PreparedState
preparedState
;
private
boolean
prepared
;
private
boolean
haveAudioVideoTracks
;
private
boolean
haveAudioVideoTracks
;
private
@MonotonicNonNull
TrackState
trackState
;
private
@MonotonicNonNull
SeekMap
seekMap
;
private
long
durationUs
;
private
boolean
isLive
;
private
int
dataType
;
private
int
dataType
;
private
boolean
seenFirstTrackSelection
;
private
boolean
seenFirstTrackSelection
;
private
boolean
notifyDiscontinuity
;
private
boolean
notifyDiscontinuity
;
private
boolean
notifiedReadingStarted
;
private
boolean
notifiedReadingStarted
;
private
int
enabledTrackCount
;
private
int
enabledTrackCount
;
private
long
durationUs
;
private
long
length
;
private
long
length
;
private
boolean
isLive
;
private
long
lastSeekPositionUs
;
private
long
lastSeekPositionUs
;
private
long
pendingResetPositionUs
;
private
long
pendingResetPositionUs
;
...
@@ -197,7 +199,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -197,7 +199,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
}
public
void
release
()
{
public
void
release
()
{
if
(
prepared
State
!=
null
)
{
if
(
prepared
)
{
// Discard as much as we can synchronously. We only do this if we're prepared, since otherwise
// Discard as much as we can synchronously. We only do this if we're prepared, since otherwise
// sampleQueues may still be being modified by the loading thread.
// sampleQueues may still be being modified by the loading thread.
for
(
SampleQueue
sampleQueue
:
sampleQueues
)
{
for
(
SampleQueue
sampleQueue
:
sampleQueues
)
{
...
@@ -229,14 +231,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -229,14 +231,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
@Override
public
void
maybeThrowPrepareError
()
throws
IOException
{
public
void
maybeThrowPrepareError
()
throws
IOException
{
maybeThrowError
();
maybeThrowError
();
if
(
loadingFinished
&&
preparedState
==
null
)
{
if
(
loadingFinished
&&
!
prepared
)
{
throw
new
ParserException
(
"Loading finished before preparation is complete."
);
throw
new
ParserException
(
"Loading finished before preparation is complete."
);
}
}
}
}
@Override
@Override
public
TrackGroupArray
getTrackGroups
()
{
public
TrackGroupArray
getTrackGroups
()
{
return
Assertions
.
checkNotNull
(
preparedState
).
tracks
;
assertPrepared
();
return
trackState
.
tracks
;
}
}
@Override
@Override
...
@@ -246,8 +249,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -246,8 +249,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@NullableType
SampleStream
[]
streams
,
@NullableType
SampleStream
[]
streams
,
boolean
[]
streamResetFlags
,
boolean
[]
streamResetFlags
,
long
positionUs
)
{
long
positionUs
)
{
TrackGroupArray
tracks
=
Assertions
.
checkNotNull
(
preparedState
).
tracks
;
assertPrepared
();
boolean
[]
trackEnabledStates
=
preparedState
.
trackEnabledStates
;
TrackGroupArray
tracks
=
trackState
.
tracks
;
boolean
[]
trackEnabledStates
=
trackState
.
trackEnabledStates
;
int
oldEnabledTrackCount
=
enabledTrackCount
;
int
oldEnabledTrackCount
=
enabledTrackCount
;
// Deselect old tracks.
// Deselect old tracks.
for
(
int
i
=
0
;
i
<
selections
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
selections
.
length
;
i
++)
{
...
@@ -316,10 +320,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -316,10 +320,11 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
@Override
public
void
discardBuffer
(
long
positionUs
,
boolean
toKeyframe
)
{
public
void
discardBuffer
(
long
positionUs
,
boolean
toKeyframe
)
{
assertPrepared
();
if
(
isPendingReset
())
{
if
(
isPendingReset
())
{
return
;
return
;
}
}
boolean
[]
trackEnabledStates
=
Assertions
.
checkNotNull
(
preparedState
)
.
trackEnabledStates
;
boolean
[]
trackEnabledStates
=
trackState
.
trackEnabledStates
;
int
trackCount
=
sampleQueues
.
length
;
int
trackCount
=
sampleQueues
.
length
;
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
sampleQueues
[
i
].
discardTo
(
positionUs
,
toKeyframe
,
trackEnabledStates
[
i
]);
sampleQueues
[
i
].
discardTo
(
positionUs
,
toKeyframe
,
trackEnabledStates
[
i
]);
...
@@ -336,7 +341,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -336,7 +341,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
if
(
loadingFinished
if
(
loadingFinished
||
loader
.
hasFatalError
()
||
loader
.
hasFatalError
()
||
pendingDeferredRetry
||
pendingDeferredRetry
||
(
prepared
State
!=
null
&&
enabledTrackCount
==
0
))
{
||
(
prepared
&&
enabledTrackCount
==
0
))
{
return
false
;
return
false
;
}
}
boolean
continuedLoading
=
loadCondition
.
open
();
boolean
continuedLoading
=
loadCondition
.
open
();
...
@@ -373,8 +378,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -373,8 +378,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
@Override
public
long
getBufferedPositionUs
()
{
public
long
getBufferedPositionUs
()
{
boolean
[]
trackIsAudioVideoFlags
=
assertPrepared
();
Assertions
.
checkNotNull
(
preparedState
)
.
trackIsAudioVideoFlags
;
boolean
[]
trackIsAudioVideoFlags
=
trackState
.
trackIsAudioVideoFlags
;
if
(
loadingFinished
)
{
if
(
loadingFinished
)
{
return
C
.
TIME_END_OF_SOURCE
;
return
C
.
TIME_END_OF_SOURCE
;
}
else
if
(
isPendingReset
())
{
}
else
if
(
isPendingReset
())
{
...
@@ -400,8 +405,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -400,8 +405,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
@Override
public
long
seekToUs
(
long
positionUs
)
{
public
long
seekToUs
(
long
positionUs
)
{
SeekMap
seekMap
=
Assertions
.
checkNotNull
(
preparedState
).
seekMap
;
assertPrepared
()
;
boolean
[]
trackIsAudioVideoFlags
=
prepared
State
.
trackIsAudioVideoFlags
;
boolean
[]
trackIsAudioVideoFlags
=
track
State
.
trackIsAudioVideoFlags
;
// Treat all seeks into non-seekable media as being to t=0.
// Treat all seeks into non-seekable media as being to t=0.
positionUs
=
seekMap
.
isSeekable
()
?
positionUs
:
0
;
positionUs
=
seekMap
.
isSeekable
()
?
positionUs
:
0
;
...
@@ -436,7 +441,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -436,7 +441,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
@Override
@Override
public
long
getAdjustedSeekPositionUs
(
long
positionUs
,
SeekParameters
seekParameters
)
{
public
long
getAdjustedSeekPositionUs
(
long
positionUs
,
SeekParameters
seekParameters
)
{
SeekMap
seekMap
=
Assertions
.
checkNotNull
(
preparedState
).
seekMap
;
assertPrepared
()
;
if
(!
seekMap
.
isSeekable
())
{
if
(!
seekMap
.
isSeekable
())
{
// Treat all seeks into non-seekable media as being to t=0.
// Treat all seeks into non-seekable media as being to t=0.
return
0
;
return
0
;
...
@@ -498,10 +503,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -498,10 +503,10 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
}
private
void
maybeNotifyDownstreamFormat
(
int
track
)
{
private
void
maybeNotifyDownstreamFormat
(
int
track
)
{
boolean
[]
trackNotifiedDownstreamFormats
=
assertPrepared
();
Assertions
.
checkNotNull
(
preparedState
)
.
trackNotifiedDownstreamFormats
;
boolean
[]
trackNotifiedDownstreamFormats
=
trackState
.
trackNotifiedDownstreamFormats
;
if
(!
trackNotifiedDownstreamFormats
[
track
])
{
if
(!
trackNotifiedDownstreamFormats
[
track
])
{
Format
trackFormat
=
prepared
State
.
tracks
.
get
(
track
).
getFormat
(
/* index= */
0
);
Format
trackFormat
=
track
State
.
tracks
.
get
(
track
).
getFormat
(
/* index= */
0
);
eventDispatcher
.
downstreamFormatChanged
(
eventDispatcher
.
downstreamFormatChanged
(
MimeTypes
.
getTrackType
(
trackFormat
.
sampleMimeType
),
MimeTypes
.
getTrackType
(
trackFormat
.
sampleMimeType
),
trackFormat
,
trackFormat
,
...
@@ -513,8 +518,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -513,8 +518,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
}
private
void
maybeStartDeferredRetry
(
int
track
)
{
private
void
maybeStartDeferredRetry
(
int
track
)
{
boolean
[]
trackIsAudioVideoFlags
=
assertPrepared
();
Assertions
.
checkNotNull
(
preparedState
)
.
trackIsAudioVideoFlags
;
boolean
[]
trackIsAudioVideoFlags
=
trackState
.
trackIsAudioVideoFlags
;
if
(!
pendingDeferredRetry
if
(!
pendingDeferredRetry
||
!
trackIsAudioVideoFlags
[
track
]
||
!
trackIsAudioVideoFlags
[
track
]
||
sampleQueues
[
track
].
isReady
(
/* loadingFinished= */
false
))
{
||
sampleQueues
[
track
].
isReady
(
/* loadingFinished= */
false
))
{
...
@@ -688,12 +693,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -688,12 +693,9 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
}
private
void
setSeekMap
(
SeekMap
seekMap
)
{
private
void
setSeekMap
(
SeekMap
seekMap
)
{
this
.
seekMap
=
icyHeaders
==
null
?
seekMap
:
new
Unseekable
(
/* durationUs */
C
.
TIME_UNSET
);
this
.
seekMap
=
icyHeaders
==
null
?
seekMap
:
new
Unseekable
(
/* durationUs
=
*/
C
.
TIME_UNSET
);
if
(
preparedState
==
null
)
{
if
(
!
prepared
)
{
maybeFinishPrepare
();
maybeFinishPrepare
();
}
else
{
preparedState
=
new
PreparedState
(
seekMap
,
preparedState
.
tracks
,
preparedState
.
trackIsAudioVideoFlags
);
}
}
durationUs
=
seekMap
.
getDurationUs
();
durationUs
=
seekMap
.
getDurationUs
();
isLive
=
length
==
C
.
LENGTH_UNSET
&&
seekMap
.
getDurationUs
()
==
C
.
TIME_UNSET
;
isLive
=
length
==
C
.
LENGTH_UNSET
&&
seekMap
.
getDurationUs
()
==
C
.
TIME_UNSET
;
...
@@ -702,8 +704,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -702,8 +704,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
}
private
void
maybeFinishPrepare
()
{
private
void
maybeFinishPrepare
()
{
SeekMap
seekMap
=
this
.
seekMap
;
if
(
released
||
prepared
||
!
sampleQueuesBuilt
||
seekMap
==
null
)
{
if
(
released
||
preparedState
!=
null
||
!
sampleQueuesBuilt
||
seekMap
==
null
)
{
return
;
return
;
}
}
for
(
SampleQueue
sampleQueue
:
sampleQueues
)
{
for
(
SampleQueue
sampleQueue
:
sampleQueues
)
{
...
@@ -744,8 +745,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -744,8 +745,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
}
trackArray
[
i
]
=
new
TrackGroup
(
trackFormat
);
trackArray
[
i
]
=
new
TrackGroup
(
trackFormat
);
}
}
preparedState
=
trackState
=
new
TrackState
(
new
TrackGroupArray
(
trackArray
),
trackIsAudioVideoFlags
);
new
PreparedState
(
seekMap
,
new
TrackGroupArray
(
trackArray
),
trackIsAudioVideoFlags
)
;
prepared
=
true
;
Assertions
.
checkNotNull
(
callback
).
onPrepared
(
this
);
Assertions
.
checkNotNull
(
callback
).
onPrepared
(
this
);
}
}
...
@@ -759,8 +760,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -759,8 +760,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
ExtractingLoadable
loadable
=
ExtractingLoadable
loadable
=
new
ExtractingLoadable
(
new
ExtractingLoadable
(
uri
,
dataSource
,
progressiveMediaExtractor
,
/* extractorOutput= */
this
,
loadCondition
);
uri
,
dataSource
,
progressiveMediaExtractor
,
/* extractorOutput= */
this
,
loadCondition
);
if
(
preparedState
!=
null
)
{
if
(
prepared
)
{
SeekMap
seekMap
=
preparedState
.
seekMap
;
Assertions
.
checkState
(
isPendingReset
());
Assertions
.
checkState
(
isPendingReset
());
if
(
durationUs
!=
C
.
TIME_UNSET
&&
pendingResetPositionUs
>
durationUs
)
{
if
(
durationUs
!=
C
.
TIME_UNSET
&&
pendingResetPositionUs
>
durationUs
)
{
loadingFinished
=
true
;
loadingFinished
=
true
;
...
@@ -768,7 +768,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -768,7 +768,8 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
return
;
return
;
}
}
loadable
.
setLoadPosition
(
loadable
.
setLoadPosition
(
seekMap
.
getSeekPoints
(
pendingResetPositionUs
).
first
.
position
,
pendingResetPositionUs
);
Assertions
.
checkNotNull
(
seekMap
).
getSeekPoints
(
pendingResetPositionUs
).
first
.
position
,
pendingResetPositionUs
);
pendingResetPositionUs
=
C
.
TIME_UNSET
;
pendingResetPositionUs
=
C
.
TIME_UNSET
;
}
}
extractedSamplesCountAtStartOfLoad
=
getExtractedSamplesCount
();
extractedSamplesCountAtStartOfLoad
=
getExtractedSamplesCount
();
...
@@ -803,7 +804,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -803,7 +804,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
// request data starting from the point it left off.
// request data starting from the point it left off.
extractedSamplesCountAtStartOfLoad
=
currentExtractedSampleCount
;
extractedSamplesCountAtStartOfLoad
=
currentExtractedSampleCount
;
return
true
;
return
true
;
}
else
if
(
prepared
State
!=
null
&&
!
suppressRead
())
{
}
else
if
(
prepared
&&
!
suppressRead
())
{
// We're playing a stream of unknown length and duration. Assume it's live, and therefore that
// We're playing a stream of unknown length and duration. Assume it's live, and therefore that
// the data at the uri is a continuously shifting window of the latest available media. For
// the data at the uri is a continuously shifting window of the latest available media. For
// this case there's no way to continue loading from where a previous load finished, so it's
// this case there's no way to continue loading from where a previous load finished, so it's
...
@@ -820,7 +821,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -820,7 +821,7 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
// because there's no buffered data to be read. This case also covers an on-demand stream with
// because there's no buffered data to be read. This case also covers an on-demand stream with
// unknown length that has yet to be prepared. This case cannot be disambiguated from the live
// unknown length that has yet to be prepared. This case cannot be disambiguated from the live
// stream case, so we have no option but to load from the start.
// stream case, so we have no option but to load from the start.
notifyDiscontinuity
=
prepared
State
!=
null
;
notifyDiscontinuity
=
prepared
;
lastSeekPositionUs
=
0
;
lastSeekPositionUs
=
0
;
extractedSamplesCountAtStartOfLoad
=
0
;
extractedSamplesCountAtStartOfLoad
=
0
;
for
(
SampleQueue
sampleQueue
:
sampleQueues
)
{
for
(
SampleQueue
sampleQueue
:
sampleQueues
)
{
...
@@ -875,6 +876,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -875,6 +876,13 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
return
pendingResetPositionUs
!=
C
.
TIME_UNSET
;
return
pendingResetPositionUs
!=
C
.
TIME_UNSET
;
}
}
@EnsuresNonNull
({
"trackState"
,
"seekMap"
})
private
void
assertPrepared
()
{
Assertions
.
checkState
(
prepared
);
Assertions
.
checkNotNull
(
trackState
);
Assertions
.
checkNotNull
(
seekMap
);
}
private
final
class
SampleStreamImpl
implements
SampleStream
{
private
final
class
SampleStreamImpl
implements
SampleStream
{
private
final
int
track
;
private
final
int
track
;
...
@@ -1042,18 +1050,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
...
@@ -1042,18 +1050,15 @@ import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
}
}
}
}
/** Stores
state that is initialized when preparation completes
. */
/** Stores
track state
. */
private
static
final
class
Prepared
State
{
private
static
final
class
Track
State
{
public
final
SeekMap
seekMap
;
public
final
TrackGroupArray
tracks
;
public
final
TrackGroupArray
tracks
;
public
final
boolean
[]
trackIsAudioVideoFlags
;
public
final
boolean
[]
trackIsAudioVideoFlags
;
public
final
boolean
[]
trackEnabledStates
;
public
final
boolean
[]
trackEnabledStates
;
public
final
boolean
[]
trackNotifiedDownstreamFormats
;
public
final
boolean
[]
trackNotifiedDownstreamFormats
;
public
PreparedState
(
public
TrackState
(
TrackGroupArray
tracks
,
boolean
[]
trackIsAudioVideoFlags
)
{
SeekMap
seekMap
,
TrackGroupArray
tracks
,
boolean
[]
trackIsAudioVideoFlags
)
{
this
.
seekMap
=
seekMap
;
this
.
tracks
=
tracks
;
this
.
tracks
=
tracks
;
this
.
trackIsAudioVideoFlags
=
trackIsAudioVideoFlags
;
this
.
trackIsAudioVideoFlags
=
trackIsAudioVideoFlags
;
this
.
trackEnabledStates
=
new
boolean
[
tracks
.
length
];
this
.
trackEnabledStates
=
new
boolean
[
tracks
.
length
];
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment