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
9a367836
authored
Apr 20, 2021
by
olly
Committed by
Oliver Woodman
Apr 21, 2021
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
DASH: Avoid rounding error in getSegmentCount
Issue: #8804 PiperOrigin-RevId: 369484117
parent
7a2eaa96
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
73 additions
and
24 deletions
RELEASENOTES.md
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashSegmentIndex.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashWrappingSegmentIndex.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Representation.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/SegmentBase.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/SingleSegmentIndex.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java
library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/SegmentBaseTest.java
RELEASENOTES.md
View file @
9a367836
...
...
@@ -142,6 +142,10 @@
*
DASH:
*
Parse
`forced_subtitle`
role from DASH manifests
(
[
#8781
](
https://github.com/google/ExoPlayer/issues/8781
)
).
*
DASH:
*
Fix rounding error that could cause
`SegmentTemplate.getSegmentCount()`
to return incorrect values
(
[
#8804
](
https://github.com/google/ExoPlayer/issues/8804
)
).
*
HLS:
*
Fix bug of ignoring
`EXT-X-START`
when setting the live target offset
(
[
#8764
](
https://github.com/google/ExoPlayer/pull/8764
)
).
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
View file @
9a367836
...
...
@@ -1139,7 +1139,7 @@ public final class DashMediaSource extends BaseMediaSource {
if
(
index
==
null
)
{
return
periodStartTimeInManifestUs
;
}
int
availableSegmentCount
=
index
.
getAvailableSegmentCount
(
periodDurationUs
,
nowUnixTimeUs
);
long
availableSegmentCount
=
index
.
getAvailableSegmentCount
(
periodDurationUs
,
nowUnixTimeUs
);
if
(
availableSegmentCount
==
0
)
{
return
periodStartTimeInManifestUs
;
}
...
...
@@ -1171,7 +1171,7 @@ public final class DashMediaSource extends BaseMediaSource {
if
(
index
==
null
)
{
return
periodStartTimeInManifestUs
+
periodDurationUs
;
}
int
availableSegmentCount
=
index
.
getAvailableSegmentCount
(
periodDurationUs
,
nowUnixTimeUs
);
long
availableSegmentCount
=
index
.
getAvailableSegmentCount
(
periodDurationUs
,
nowUnixTimeUs
);
if
(
availableSegmentCount
==
0
)
{
return
periodStartTimeInManifestUs
;
}
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashSegmentIndex.java
View file @
9a367836
...
...
@@ -89,7 +89,7 @@ public interface DashSegmentIndex {
* C#TIME_UNSET} if the period's duration is not yet known.
* @return The number of segments in the index, or {@link #INDEX_UNBOUNDED}.
*/
int
getSegmentCount
(
long
periodDurationUs
);
long
getSegmentCount
(
long
periodDurationUs
);
/**
* Returns the number of available segments in the index.
...
...
@@ -99,7 +99,7 @@ public interface DashSegmentIndex {
* @param nowUnixTimeUs The current time in milliseconds since the Unix epoch.
* @return The number of available segments in the index.
*/
int
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
);
long
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
);
/**
* Returns the time, in microseconds, at which a new segment becomes available, or {@link
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashWrappingSegmentIndex.java
View file @
9a367836
...
...
@@ -48,12 +48,12 @@ public final class DashWrappingSegmentIndex implements DashSegmentIndex {
}
@Override
public
int
getSegmentCount
(
long
periodDurationUs
)
{
public
long
getSegmentCount
(
long
periodDurationUs
)
{
return
chunkIndex
.
length
;
}
@Override
public
int
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
public
long
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
return
chunkIndex
.
length
;
}
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java
View file @
9a367836
...
...
@@ -222,7 +222,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
if
(
representationHolder
.
segmentIndex
!=
null
)
{
long
segmentNum
=
representationHolder
.
getSegmentNum
(
positionUs
);
long
firstSyncUs
=
representationHolder
.
getSegmentStartTimeUs
(
segmentNum
);
int
segmentCount
=
representationHolder
.
getSegmentCount
();
long
segmentCount
=
representationHolder
.
getSegmentCount
();
long
secondSyncUs
=
firstSyncUs
<
positionUs
&&
(
segmentCount
==
DashSegmentIndex
.
INDEX_UNBOUNDED
...
...
@@ -465,7 +465,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
&&
((
InvalidResponseCodeException
)
e
).
responseCode
==
404
)
{
RepresentationHolder
representationHolder
=
representationHolders
[
trackSelection
.
indexOf
(
chunk
.
trackFormat
)];
int
segmentCount
=
representationHolder
.
getSegmentCount
();
long
segmentCount
=
representationHolder
.
getSegmentCount
();
if
(
segmentCount
!=
DashSegmentIndex
.
INDEX_UNBOUNDED
&&
segmentCount
!=
0
)
{
long
lastAvailableSegmentNum
=
representationHolder
.
getFirstSegmentNum
()
+
segmentCount
-
1
;
if
(((
MediaChunk
)
chunk
).
getNextChunkIndex
()
>
lastAvailableSegmentNum
)
{
...
...
@@ -723,7 +723,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
newPeriodDurationUs
,
newRepresentation
,
chunkExtractor
,
segmentNumShift
,
newIndex
);
}
int
oldIndexSegmentCount
=
oldIndex
.
getSegmentCount
(
newPeriodDurationUs
);
long
oldIndexSegmentCount
=
oldIndex
.
getSegmentCount
(
newPeriodDurationUs
);
if
(
oldIndexSegmentCount
==
0
)
{
// Segment numbers cannot shift if the old index was empty.
return
new
RepresentationHolder
(
...
...
@@ -777,7 +777,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
+
segmentNumShift
;
}
public
int
getSegmentCount
()
{
public
long
getSegmentCount
()
{
return
segmentIndex
.
getSegmentCount
(
periodDurationUs
);
}
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/Representation.java
View file @
9a367836
...
...
@@ -352,12 +352,12 @@ public abstract class Representation {
}
@Override
public
int
getSegmentCount
(
long
periodDurationUs
)
{
public
long
getSegmentCount
(
long
periodDurationUs
)
{
return
segmentBase
.
getSegmentCount
(
periodDurationUs
);
}
@Override
public
int
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
public
long
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
return
segmentBase
.
getAvailableSegmentCount
(
periodDurationUs
,
nowUnixTimeUs
);
}
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/SegmentBase.java
View file @
9a367836
...
...
@@ -24,6 +24,9 @@ import androidx.annotation.VisibleForTesting;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.source.dash.DashSegmentIndex
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.common.math.BigIntegerMath
;
import
java.math.BigInteger
;
import
java.math.RoundingMode
;
import
java.util.List
;
/**
...
...
@@ -214,7 +217,7 @@ public abstract class SegmentBase {
long
duration
=
segmentTimeline
.
get
((
int
)
(
sequenceNumber
-
startNumber
)).
duration
;
return
(
duration
*
C
.
MICROS_PER_SECOND
)
/
timescale
;
}
else
{
int
segmentCount
=
getSegmentCount
(
periodDurationUs
);
long
segmentCount
=
getSegmentCount
(
periodDurationUs
);
return
segmentCount
!=
INDEX_UNBOUNDED
&&
sequenceNumber
==
(
getFirstSegmentNum
()
+
segmentCount
-
1
)
?
(
periodDurationUs
-
getSegmentTimeUs
(
sequenceNumber
))
...
...
@@ -264,8 +267,8 @@ public abstract class SegmentBase {
}
/** See {@link DashSegmentIndex#getAvailableSegmentCount(long, long)}. */
public
int
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
int
segmentCount
=
getSegmentCount
(
periodDurationUs
);
public
long
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
long
segmentCount
=
getSegmentCount
(
periodDurationUs
);
if
(
segmentCount
!=
INDEX_UNBOUNDED
)
{
return
segmentCount
;
}
...
...
@@ -298,7 +301,7 @@ public abstract class SegmentBase {
}
/** See {@link DashSegmentIndex#getSegmentCount(long)}. */
public
abstract
int
getSegmentCount
(
long
periodDurationUs
);
public
abstract
long
getSegmentCount
(
long
periodDurationUs
);
}
/** A {@link MultiSegmentBase} that uses a SegmentList to define its segments. */
...
...
@@ -356,7 +359,7 @@ public abstract class SegmentBase {
}
@Override
public
int
getSegmentCount
(
long
periodDurationUs
)
{
public
long
getSegmentCount
(
long
periodDurationUs
)
{
return
mediaSegments
.
size
();
}
...
...
@@ -455,14 +458,17 @@ public abstract class SegmentBase {
}
@Override
public
int
getSegmentCount
(
long
periodDurationUs
)
{
public
long
getSegmentCount
(
long
periodDurationUs
)
{
if
(
segmentTimeline
!=
null
)
{
return
segmentTimeline
.
size
();
}
else
if
(
endNumber
!=
C
.
INDEX_UNSET
)
{
return
(
int
)
(
endNumber
-
startNumber
+
1
)
;
return
endNumber
-
startNumber
+
1
;
}
else
if
(
periodDurationUs
!=
C
.
TIME_UNSET
)
{
long
durationUs
=
(
duration
*
C
.
MICROS_PER_SECOND
)
/
timescale
;
return
(
int
)
Util
.
ceilDivide
(
periodDurationUs
,
durationUs
);
BigInteger
numerator
=
BigInteger
.
valueOf
(
periodDurationUs
).
multiply
(
BigInteger
.
valueOf
(
timescale
));
BigInteger
denominator
=
BigInteger
.
valueOf
(
duration
).
multiply
(
BigInteger
.
valueOf
(
C
.
MICROS_PER_SECOND
));
return
BigIntegerMath
.
divide
(
numerator
,
denominator
,
RoundingMode
.
CEILING
).
longValue
();
}
else
{
return
INDEX_UNBOUNDED
;
}
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/SingleSegmentIndex.java
View file @
9a367836
...
...
@@ -63,12 +63,12 @@ import com.google.android.exoplayer2.source.dash.DashSegmentIndex;
}
@Override
public
int
getSegmentCount
(
long
periodDurationUs
)
{
public
long
getSegmentCount
(
long
periodDurationUs
)
{
return
1
;
}
@Override
public
int
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
public
long
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
return
1
;
}
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java
View file @
9a367836
...
...
@@ -183,7 +183,7 @@ public final class DashDownloader extends SegmentDownloader<DashManifest> {
continue
;
}
int
segmentCount
=
index
.
getSegmentCount
(
periodDurationUs
);
long
segmentCount
=
index
.
getSegmentCount
(
periodDurationUs
);
if
(
segmentCount
==
DashSegmentIndex
.
INDEX_UNBOUNDED
)
{
throw
new
DownloadException
(
"Unbounded segment index"
);
}
...
...
library/dash/src/test/java/com/google/android/exoplayer2/source/dash/manifest/SegmentBaseTest.java
View file @
9a367836
...
...
@@ -182,4 +182,43 @@ public final class SegmentBaseTest {
/* nowUnixTimeUs= */
periodStartUnixTimeUs
+
17_500_000
))
.
isEqualTo
(
19_500_000
);
}
/** Regression test for https://github.com/google/ExoPlayer/issues/8804. */
@Test
public
void
getSegmentCount_withSegmentTemplate_avoidsIncorrectRounding
()
{
SegmentBase
.
SegmentTemplate
segmentTemplate
=
new
SegmentBase
.
SegmentTemplate
(
/* initialization= */
null
,
/* timescale= */
90000
,
/* presentationTimeOffset= */
0
,
/* startNumber= */
0
,
/* endNumber= */
C
.
INDEX_UNSET
,
/* duration= */
179989
,
/* segmentTimeline= */
null
,
/* availabilityTimeOffsetUs= */
C
.
TIME_UNSET
,
/* initializationTemplate= */
null
,
/* mediaTemplate= */
null
,
/* timeShiftBufferDepthUs= */
C
.
TIME_UNSET
,
/* periodStartUnixTimeUs= */
C
.
TIME_UNSET
);
assertThat
(
segmentTemplate
.
getSegmentCount
(
2931820000L
)).
isEqualTo
(
1466
);
}
@Test
public
void
getSegmentCount_withSegmentTemplate_avoidsOverflow
()
{
SegmentBase
.
SegmentTemplate
segmentTemplate
=
new
SegmentBase
.
SegmentTemplate
(
/* initialization= */
null
,
/* timescale= */
1000000
,
/* presentationTimeOffset= */
0
,
/* startNumber= */
0
,
/* endNumber= */
C
.
INDEX_UNSET
,
/* duration= */
179989
,
/* segmentTimeline= */
null
,
/* availabilityTimeOffsetUs= */
C
.
TIME_UNSET
,
/* initializationTemplate= */
null
,
/* mediaTemplate= */
null
,
/* timeShiftBufferDepthUs= */
C
.
TIME_UNSET
,
/* periodStartUnixTimeUs= */
C
.
TIME_UNSET
);
assertThat
(
segmentTemplate
.
getSegmentCount
(
1618875028000000L
)).
isEqualTo
(
8994299808L
);
}
}
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