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
bcb9eb43
authored
Mar 09, 2021
by
ibaker
Committed by
Ian Baker
Mar 12, 2021
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Switch SampleQueue to store Format in a span structure
PiperOrigin-RevId: 361813981
parent
b74bfa1e
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
294 additions
and
16 deletions
library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java
library/core/src/main/java/com/google/android/exoplayer2/source/SpannedData.java
library/core/src/test/java/com/google/android/exoplayer2/source/SpannedDataTest.java
library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java
View file @
bcb9eb43
...
...
@@ -16,11 +16,13 @@
package
com
.
google
.
android
.
exoplayer2
.
source
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkArgument
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
java
.
lang
.
Math
.
max
;
import
android.os.Looper
;
import
android.util.Log
;
import
androidx.annotation.CallSuper
;
import
androidx.annotation.GuardedBy
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.VisibleForTesting
;
import
com.google.android.exoplayer2.C
;
...
...
@@ -61,6 +63,7 @@ public class SampleQueue implements TrackOutput {
private
final
SampleDataQueue
sampleDataQueue
;
private
final
SampleExtrasHolder
extrasHolder
;
private
final
SpannedData
<
Format
>
formatSpans
;
@Nullable
private
final
DrmSessionManager
drmSessionManager
;
@Nullable
private
final
DrmSessionEventListener
.
EventDispatcher
drmEventDispatcher
;
@Nullable
private
final
Looper
playbackLooper
;
...
...
@@ -76,7 +79,6 @@ public class SampleQueue implements TrackOutput {
private
int
[]
flags
;
private
long
[]
timesUs
;
private
@NullableType
CryptoData
[]
cryptoDatas
;
private
Format
[]
formats
;
private
int
length
;
private
int
absoluteFirstIndex
;
...
...
@@ -92,7 +94,6 @@ public class SampleQueue implements TrackOutput {
private
boolean
upstreamFormatAdjustmentRequired
;
@Nullable
private
Format
unadjustedUpstreamFormat
;
@Nullable
private
Format
upstreamFormat
;
@Nullable
private
Format
upstreamCommittedFormat
;
private
int
upstreamSourceId
;
private
boolean
upstreamAllSamplesAreSyncSamples
;
private
boolean
loggedUnexpectedNonSyncSample
;
...
...
@@ -155,7 +156,7 @@ public class SampleQueue implements TrackOutput {
flags
=
new
int
[
capacity
];
sizes
=
new
int
[
capacity
];
cryptoDatas
=
new
CryptoData
[
capacity
];
format
s
=
new
Format
[
capacity
]
;
format
Spans
=
new
SpannedData
<>()
;
startTimeUs
=
Long
.
MIN_VALUE
;
largestDiscardedTimestampUs
=
Long
.
MIN_VALUE
;
largestQueuedTimestampUs
=
Long
.
MIN_VALUE
;
...
...
@@ -197,7 +198,7 @@ public class SampleQueue implements TrackOutput {
largestDiscardedTimestampUs
=
Long
.
MIN_VALUE
;
largestQueuedTimestampUs
=
Long
.
MIN_VALUE
;
isLastSampleQueued
=
false
;
upstreamCommittedFormat
=
null
;
formatSpans
.
clear
()
;
if
(
resetUpstreamFormat
)
{
unadjustedUpstreamFormat
=
null
;
upstreamFormat
=
null
;
...
...
@@ -370,12 +371,11 @@ public class SampleQueue implements TrackOutput {
||
isLastSampleQueued
||
(
upstreamFormat
!=
null
&&
upstreamFormat
!=
downstreamFormat
);
}
int
relativeReadIndex
=
getRelativeIndex
(
readPosition
);
if
(
formats
[
relativeReadIndex
]
!=
downstreamFormat
)
{
if
(
formatSpans
.
get
(
getReadIndex
())
!=
downstreamFormat
)
{
// A format can be read.
return
true
;
}
return
mayReadSample
(
relativeReadIndex
);
return
mayReadSample
(
getRelativeIndex
(
readPosition
)
);
}
/** Equivalent to {@link #read}, except it never advances the read position. */
...
...
@@ -690,12 +690,13 @@ public class SampleQueue implements TrackOutput {
}
}
int
relativeReadIndex
=
getRelativeIndex
(
readPosition
);
if
(
formatRequired
||
format
s
[
relativeReadIndex
]
!=
downstreamFormat
)
{
onFormatResult
(
format
s
[
relativeReadIndex
]
,
formatHolder
);
Format
format
=
formatSpans
.
get
(
getReadIndex
()
);
if
(
formatRequired
||
format
!=
downstreamFormat
)
{
onFormatResult
(
format
,
formatHolder
);
return
C
.
RESULT_FORMAT_READ
;
}
int
relativeReadIndex
=
getRelativeIndex
(
readPosition
);
if
(!
mayReadSample
(
relativeReadIndex
))
{
buffer
.
waitingForKeys
=
true
;
return
C
.
RESULT_NOTHING_READ
;
...
...
@@ -721,6 +722,8 @@ public class SampleQueue implements TrackOutput {
// referential quality.
return
false
;
}
@Nullable
Format
upstreamCommittedFormat
=
formatSpans
.
getEndValue
();
if
(
Util
.
areEqual
(
format
,
upstreamCommittedFormat
))
{
// The format has changed back to the format of the last committed sample. If they are
// different objects, we revert back to using upstreamCommittedFormat as the upstreamFormat
...
...
@@ -794,9 +797,11 @@ public class SampleQueue implements TrackOutput {
sizes
[
relativeEndIndex
]
=
size
;
flags
[
relativeEndIndex
]
=
sampleFlags
;
cryptoDatas
[
relativeEndIndex
]
=
cryptoData
;
formats
[
relativeEndIndex
]
=
upstreamFormat
;
sourceIds
[
relativeEndIndex
]
=
upstreamSourceId
;
upstreamCommittedFormat
=
upstreamFormat
;
if
(!
Util
.
areEqual
(
upstreamFormat
,
formatSpans
.
getEndValue
()))
{
formatSpans
.
appendSpan
(
getWriteIndex
(),
checkNotNull
(
upstreamFormat
));
}
length
++;
if
(
length
==
capacity
)
{
...
...
@@ -808,14 +813,12 @@ public class SampleQueue implements TrackOutput {
int
[]
newFlags
=
new
int
[
newCapacity
];
int
[]
newSizes
=
new
int
[
newCapacity
];
CryptoData
[]
newCryptoDatas
=
new
CryptoData
[
newCapacity
];
Format
[]
newFormats
=
new
Format
[
newCapacity
];
int
beforeWrap
=
capacity
-
relativeFirstIndex
;
System
.
arraycopy
(
offsets
,
relativeFirstIndex
,
newOffsets
,
0
,
beforeWrap
);
System
.
arraycopy
(
timesUs
,
relativeFirstIndex
,
newTimesUs
,
0
,
beforeWrap
);
System
.
arraycopy
(
flags
,
relativeFirstIndex
,
newFlags
,
0
,
beforeWrap
);
System
.
arraycopy
(
sizes
,
relativeFirstIndex
,
newSizes
,
0
,
beforeWrap
);
System
.
arraycopy
(
cryptoDatas
,
relativeFirstIndex
,
newCryptoDatas
,
0
,
beforeWrap
);
System
.
arraycopy
(
formats
,
relativeFirstIndex
,
newFormats
,
0
,
beforeWrap
);
System
.
arraycopy
(
sourceIds
,
relativeFirstIndex
,
newSourceIds
,
0
,
beforeWrap
);
int
afterWrap
=
relativeFirstIndex
;
System
.
arraycopy
(
offsets
,
0
,
newOffsets
,
beforeWrap
,
afterWrap
);
...
...
@@ -823,14 +826,12 @@ public class SampleQueue implements TrackOutput {
System
.
arraycopy
(
flags
,
0
,
newFlags
,
beforeWrap
,
afterWrap
);
System
.
arraycopy
(
sizes
,
0
,
newSizes
,
beforeWrap
,
afterWrap
);
System
.
arraycopy
(
cryptoDatas
,
0
,
newCryptoDatas
,
beforeWrap
,
afterWrap
);
System
.
arraycopy
(
formats
,
0
,
newFormats
,
beforeWrap
,
afterWrap
);
System
.
arraycopy
(
sourceIds
,
0
,
newSourceIds
,
beforeWrap
,
afterWrap
);
offsets
=
newOffsets
;
timesUs
=
newTimesUs
;
flags
=
newFlags
;
sizes
=
newSizes
;
cryptoDatas
=
newCryptoDatas
;
formats
=
newFormats
;
sourceIds
=
newSourceIds
;
relativeFirstIndex
=
0
;
capacity
=
newCapacity
;
...
...
@@ -862,6 +863,7 @@ public class SampleQueue implements TrackOutput {
length
-=
discardCount
;
largestQueuedTimestampUs
=
max
(
largestDiscardedTimestampUs
,
getLargestTimestamp
(
length
));
isLastSampleQueued
=
discardCount
==
0
&&
isLastSampleQueued
;
formatSpans
.
discardFrom
(
discardFromIndex
);
if
(
length
!=
0
)
{
int
relativeLastWriteIndex
=
getRelativeIndex
(
length
-
1
);
return
offsets
[
relativeLastWriteIndex
]
+
sizes
[
relativeLastWriteIndex
];
...
...
@@ -987,6 +989,7 @@ public class SampleQueue implements TrackOutput {
* @param discardCount The number of samples to discard.
* @return The corresponding offset up to which data should be discarded.
*/
@GuardedBy
(
"this"
)
private
long
discardSamples
(
int
discardCount
)
{
largestDiscardedTimestampUs
=
max
(
largestDiscardedTimestampUs
,
getLargestTimestamp
(
discardCount
));
...
...
@@ -1000,6 +1003,8 @@ public class SampleQueue implements TrackOutput {
if
(
readPosition
<
0
)
{
readPosition
=
0
;
}
formatSpans
.
discardTo
(
absoluteFirstIndex
);
if
(
length
==
0
)
{
int
relativeLastDiscardIndex
=
(
relativeFirstIndex
==
0
?
capacity
:
relativeFirstIndex
)
-
1
;
return
offsets
[
relativeLastDiscardIndex
]
+
sizes
[
relativeLastDiscardIndex
];
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/SpannedData.java
0 → 100644
View file @
bcb9eb43
/*
* Copyright 2021 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
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkArgument
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkState
;
import
static
java
.
lang
.
Math
.
min
;
import
android.util.SparseArray
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
/**
* Stores value objects associated with spans of integer keys.
*
* <p>This implementation is optimised for consecutive {@link #get(int)} calls with keys that are
* close to each other in value.
*
* <p>Spans are defined by their own {@code startKey} (inclusive) and the {@code startKey} of the
* next span (exclusive). The last span is open-ended.
*
* @param <V> The type of values stored in this collection.
*/
/* package */
final
class
SpannedData
<
V
>
{
private
int
memoizedReadIndex
;
private
final
SparseArray
<
V
>
spans
;
/** Constructs an empty instance. */
public
SpannedData
()
{
spans
=
new
SparseArray
<>();
}
/**
* Returns the value associated with the span covering {@code key}.
*
* <p>{@link #appendSpan(int, Object)} must have been called at least once since the last call to
* {@link #clear()}.
*
* <p>{@code key} must be greater than or equal to the previous value passed to {@link
* #discardTo(int)} (or zero after {@link #clear()} has been called).
*/
public
V
get
(
int
key
)
{
if
(
memoizedReadIndex
==
C
.
INDEX_UNSET
)
{
memoizedReadIndex
=
0
;
}
while
(
memoizedReadIndex
>
0
&&
key
<
spans
.
keyAt
(
memoizedReadIndex
))
{
memoizedReadIndex
--;
}
while
(
memoizedReadIndex
<
spans
.
size
()
-
1
&&
key
>=
spans
.
keyAt
(
memoizedReadIndex
+
1
))
{
memoizedReadIndex
++;
}
return
spans
.
valueAt
(
memoizedReadIndex
);
}
/**
* Adds a new span to the end starting at {@code startKey} and containing {@code value}.
*
* <p>{@code startKey} must be greater than or equal to the start key of the previous span. If
* they're equal, the previous span is overwritten.
*/
public
void
appendSpan
(
int
startKey
,
V
value
)
{
if
(
memoizedReadIndex
==
C
.
INDEX_UNSET
)
{
checkState
(
spans
.
size
()
==
0
);
memoizedReadIndex
=
0
;
}
checkArgument
(
spans
.
size
()
==
0
||
startKey
>=
spans
.
keyAt
(
spans
.
size
()
-
1
));
spans
.
append
(
startKey
,
value
);
}
/**
* Returns the value associated with the end span, or null if the collection is empty.
*
* <p>This is either the last value passed to {@link #appendSpan(int, Object)}, or the value of
* the span covering the index passed to {@link #discardFrom(int)}.
*/
@Nullable
public
V
getEndValue
()
{
return
spans
.
size
()
!=
0
?
spans
.
valueAt
(
spans
.
size
()
-
1
)
:
null
;
}
/**
* Discard the spans from the start up to {@code discardToKey}.
*
* <p>The span associated with {@code discardToKey} is not discarded (which means the last span is
* never discarded).
*/
public
void
discardTo
(
int
discardToKey
)
{
for
(
int
i
=
0
;
i
<
spans
.
size
()
-
1
&&
discardToKey
>=
spans
.
keyAt
(
i
+
1
);
i
++)
{
spans
.
removeAt
(
i
);
if
(
memoizedReadIndex
>
0
)
{
memoizedReadIndex
--;
}
}
}
/**
* Discard the spans from the end back to {@code discardFromKey}.
*
* <p>The span associated with {@code discardFromKey} is not discarded.
*/
public
void
discardFrom
(
int
discardFromKey
)
{
for
(
int
i
=
spans
.
size
()
-
1
;
i
>=
0
&&
discardFromKey
<
spans
.
keyAt
(
i
);
i
--)
{
spans
.
removeAt
(
i
);
}
memoizedReadIndex
=
spans
.
size
()
>
0
?
min
(
memoizedReadIndex
,
spans
.
size
()
-
1
)
:
C
.
INDEX_UNSET
;
}
/** Remove all spans. */
public
void
clear
()
{
memoizedReadIndex
=
C
.
INDEX_UNSET
;
spans
.
clear
();
}
}
library/core/src/test/java/com/google/android/exoplayer2/source/SpannedDataTest.java
0 → 100644
View file @
bcb9eb43
/*
* Copyright (C) 2021 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
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
/** Tests for {@link SpannedData}. */
@RunWith
(
AndroidJUnit4
.
class
)
public
final
class
SpannedDataTest
{
private
static
final
String
VALUE_1
=
"value 1"
;
private
static
final
String
VALUE_2
=
"value 2"
;
private
static
final
String
VALUE_3
=
"value 3"
;
@Test
public
void
appendMultipleSpansThenRead
()
{
SpannedData
<
String
>
spannedData
=
new
SpannedData
<>();
spannedData
.
appendSpan
(
/* startKey= */
0
,
VALUE_1
);
spannedData
.
appendSpan
(
/* startKey= */
2
,
VALUE_2
);
spannedData
.
appendSpan
(
/* startKey= */
4
,
VALUE_3
);
assertThat
(
spannedData
.
get
(
0
)).
isEqualTo
(
VALUE_1
);
assertThat
(
spannedData
.
get
(
1
)).
isEqualTo
(
VALUE_1
);
assertThat
(
spannedData
.
get
(
2
)).
isEqualTo
(
VALUE_2
);
assertThat
(
spannedData
.
get
(
3
)).
isEqualTo
(
VALUE_2
);
assertThat
(
spannedData
.
get
(
4
)).
isEqualTo
(
VALUE_3
);
assertThat
(
spannedData
.
get
(
5
)).
isEqualTo
(
VALUE_3
);
}
@Test
public
void
append_emptySpansDiscarded
()
{
SpannedData
<
String
>
spannedData
=
new
SpannedData
<>();
spannedData
.
appendSpan
(
/* startKey= */
0
,
VALUE_1
);
spannedData
.
appendSpan
(
/* startKey= */
2
,
VALUE_2
);
spannedData
.
appendSpan
(
/* startKey= */
2
,
VALUE_3
);
assertThat
(
spannedData
.
get
(
0
)).
isEqualTo
(
VALUE_1
);
assertThat
(
spannedData
.
get
(
1
)).
isEqualTo
(
VALUE_1
);
assertThat
(
spannedData
.
get
(
2
)).
isEqualTo
(
VALUE_3
);
assertThat
(
spannedData
.
get
(
3
)).
isEqualTo
(
VALUE_3
);
}
@Test
public
void
discardTo
()
{
SpannedData
<
String
>
spannedData
=
new
SpannedData
<>();
spannedData
.
appendSpan
(
/* startKey= */
0
,
VALUE_1
);
spannedData
.
appendSpan
(
/* startKey= */
2
,
VALUE_2
);
spannedData
.
appendSpan
(
/* startKey= */
4
,
VALUE_3
);
spannedData
.
discardTo
(
2
);
assertThat
(
spannedData
.
get
(
0
)).
isEqualTo
(
VALUE_2
);
assertThat
(
spannedData
.
get
(
2
)).
isEqualTo
(
VALUE_2
);
spannedData
.
discardTo
(
4
);
assertThat
(
spannedData
.
get
(
3
)).
isEqualTo
(
VALUE_3
);
assertThat
(
spannedData
.
get
(
4
)).
isEqualTo
(
VALUE_3
);
}
@Test
public
void
discardTo_prunesEmptySpans
()
{
SpannedData
<
String
>
spannedData
=
new
SpannedData
<>();
spannedData
.
appendSpan
(
/* startKey= */
0
,
VALUE_1
);
spannedData
.
appendSpan
(
/* startKey= */
2
,
VALUE_2
);
spannedData
.
appendSpan
(
/* startKey= */
2
,
VALUE_3
);
spannedData
.
discardTo
(
2
);
assertThat
(
spannedData
.
get
(
0
)).
isEqualTo
(
VALUE_3
);
assertThat
(
spannedData
.
get
(
2
)).
isEqualTo
(
VALUE_3
);
}
@Test
public
void
discardFromThenAppend_keepsValueIfSpanEndsUpNonEmpty
()
{
SpannedData
<
String
>
spannedData
=
new
SpannedData
<>();
spannedData
.
appendSpan
(
/* startKey= */
0
,
VALUE_1
);
spannedData
.
appendSpan
(
/* startKey= */
2
,
VALUE_2
);
spannedData
.
appendSpan
(
/* startKey= */
4
,
VALUE_3
);
spannedData
.
discardFrom
(
2
);
assertThat
(
spannedData
.
getEndValue
()).
isEqualTo
(
VALUE_2
);
spannedData
.
appendSpan
(
/* startKey= */
3
,
VALUE_3
);
assertThat
(
spannedData
.
get
(
0
)).
isEqualTo
(
VALUE_1
);
assertThat
(
spannedData
.
get
(
1
)).
isEqualTo
(
VALUE_1
);
assertThat
(
spannedData
.
get
(
2
)).
isEqualTo
(
VALUE_2
);
assertThat
(
spannedData
.
get
(
3
)).
isEqualTo
(
VALUE_3
);
}
@Test
public
void
discardFromThenAppend_prunesEmptySpan
()
{
SpannedData
<
String
>
spannedData
=
new
SpannedData
<>();
spannedData
.
appendSpan
(
/* startKey= */
0
,
VALUE_1
);
spannedData
.
appendSpan
(
/* startKey= */
2
,
VALUE_2
);
spannedData
.
discardFrom
(
2
);
spannedData
.
appendSpan
(
/* startKey= */
2
,
VALUE_3
);
assertThat
(
spannedData
.
get
(
0
)).
isEqualTo
(
VALUE_1
);
assertThat
(
spannedData
.
get
(
1
)).
isEqualTo
(
VALUE_1
);
assertThat
(
spannedData
.
get
(
2
)).
isEqualTo
(
VALUE_3
);
}
@Test
public
void
clear
()
{
SpannedData
<
String
>
spannedData
=
new
SpannedData
<>();
spannedData
.
appendSpan
(
/* startKey= */
0
,
VALUE_1
);
spannedData
.
appendSpan
(
/* startKey= */
2
,
VALUE_2
);
spannedData
.
clear
();
spannedData
.
appendSpan
(
/* startKey= */
1
,
VALUE_3
);
assertThat
(
spannedData
.
get
(
0
)).
isEqualTo
(
VALUE_3
);
assertThat
(
spannedData
.
get
(
1
)).
isEqualTo
(
VALUE_3
);
}
}
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