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
Show 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 @@
...
@@ -142,6 +142,10 @@
*
DASH:
*
DASH:
*
Parse
`forced_subtitle`
role from DASH manifests
*
Parse
`forced_subtitle`
role from DASH manifests
(
[
#8781
](
https://github.com/google/ExoPlayer/issues/8781
)
).
(
[
#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:
*
HLS:
*
Fix bug of ignoring
`EXT-X-START`
when setting the live target offset
*
Fix bug of ignoring
`EXT-X-START`
when setting the live target offset
(
[
#8764
](
https://github.com/google/ExoPlayer/pull/8764
)
).
(
[
#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 {
...
@@ -1139,7 +1139,7 @@ public final class DashMediaSource extends BaseMediaSource {
if
(
index
==
null
)
{
if
(
index
==
null
)
{
return
periodStartTimeInManifestUs
;
return
periodStartTimeInManifestUs
;
}
}
int
availableSegmentCount
=
index
.
getAvailableSegmentCount
(
periodDurationUs
,
nowUnixTimeUs
);
long
availableSegmentCount
=
index
.
getAvailableSegmentCount
(
periodDurationUs
,
nowUnixTimeUs
);
if
(
availableSegmentCount
==
0
)
{
if
(
availableSegmentCount
==
0
)
{
return
periodStartTimeInManifestUs
;
return
periodStartTimeInManifestUs
;
}
}
...
@@ -1171,7 +1171,7 @@ public final class DashMediaSource extends BaseMediaSource {
...
@@ -1171,7 +1171,7 @@ public final class DashMediaSource extends BaseMediaSource {
if
(
index
==
null
)
{
if
(
index
==
null
)
{
return
periodStartTimeInManifestUs
+
periodDurationUs
;
return
periodStartTimeInManifestUs
+
periodDurationUs
;
}
}
int
availableSegmentCount
=
index
.
getAvailableSegmentCount
(
periodDurationUs
,
nowUnixTimeUs
);
long
availableSegmentCount
=
index
.
getAvailableSegmentCount
(
periodDurationUs
,
nowUnixTimeUs
);
if
(
availableSegmentCount
==
0
)
{
if
(
availableSegmentCount
==
0
)
{
return
periodStartTimeInManifestUs
;
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 {
...
@@ -89,7 +89,7 @@ public interface DashSegmentIndex {
* C#TIME_UNSET} if the period's duration is not yet known.
* C#TIME_UNSET} if the period's duration is not yet known.
* @return The number of segments in the index, or {@link #INDEX_UNBOUNDED}.
* @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.
* Returns the number of available segments in the index.
...
@@ -99,7 +99,7 @@ public interface DashSegmentIndex {
...
@@ -99,7 +99,7 @@ public interface DashSegmentIndex {
* @param nowUnixTimeUs The current time in milliseconds since the Unix epoch.
* @param nowUnixTimeUs The current time in milliseconds since the Unix epoch.
* @return The number of available segments in the index.
* @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
* 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 {
...
@@ -48,12 +48,12 @@ public final class DashWrappingSegmentIndex implements DashSegmentIndex {
}
}
@Override
@Override
public
int
getSegmentCount
(
long
periodDurationUs
)
{
public
long
getSegmentCount
(
long
periodDurationUs
)
{
return
chunkIndex
.
length
;
return
chunkIndex
.
length
;
}
}
@Override
@Override
public
int
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
public
long
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
return
chunkIndex
.
length
;
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 {
...
@@ -222,7 +222,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
if
(
representationHolder
.
segmentIndex
!=
null
)
{
if
(
representationHolder
.
segmentIndex
!=
null
)
{
long
segmentNum
=
representationHolder
.
getSegmentNum
(
positionUs
);
long
segmentNum
=
representationHolder
.
getSegmentNum
(
positionUs
);
long
firstSyncUs
=
representationHolder
.
getSegmentStartTimeUs
(
segmentNum
);
long
firstSyncUs
=
representationHolder
.
getSegmentStartTimeUs
(
segmentNum
);
int
segmentCount
=
representationHolder
.
getSegmentCount
();
long
segmentCount
=
representationHolder
.
getSegmentCount
();
long
secondSyncUs
=
long
secondSyncUs
=
firstSyncUs
<
positionUs
firstSyncUs
<
positionUs
&&
(
segmentCount
==
DashSegmentIndex
.
INDEX_UNBOUNDED
&&
(
segmentCount
==
DashSegmentIndex
.
INDEX_UNBOUNDED
...
@@ -465,7 +465,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
...
@@ -465,7 +465,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
&&
((
InvalidResponseCodeException
)
e
).
responseCode
==
404
)
{
&&
((
InvalidResponseCodeException
)
e
).
responseCode
==
404
)
{
RepresentationHolder
representationHolder
=
RepresentationHolder
representationHolder
=
representationHolders
[
trackSelection
.
indexOf
(
chunk
.
trackFormat
)];
representationHolders
[
trackSelection
.
indexOf
(
chunk
.
trackFormat
)];
int
segmentCount
=
representationHolder
.
getSegmentCount
();
long
segmentCount
=
representationHolder
.
getSegmentCount
();
if
(
segmentCount
!=
DashSegmentIndex
.
INDEX_UNBOUNDED
&&
segmentCount
!=
0
)
{
if
(
segmentCount
!=
DashSegmentIndex
.
INDEX_UNBOUNDED
&&
segmentCount
!=
0
)
{
long
lastAvailableSegmentNum
=
representationHolder
.
getFirstSegmentNum
()
+
segmentCount
-
1
;
long
lastAvailableSegmentNum
=
representationHolder
.
getFirstSegmentNum
()
+
segmentCount
-
1
;
if
(((
MediaChunk
)
chunk
).
getNextChunkIndex
()
>
lastAvailableSegmentNum
)
{
if
(((
MediaChunk
)
chunk
).
getNextChunkIndex
()
>
lastAvailableSegmentNum
)
{
...
@@ -723,7 +723,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
...
@@ -723,7 +723,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
newPeriodDurationUs
,
newRepresentation
,
chunkExtractor
,
segmentNumShift
,
newIndex
);
newPeriodDurationUs
,
newRepresentation
,
chunkExtractor
,
segmentNumShift
,
newIndex
);
}
}
int
oldIndexSegmentCount
=
oldIndex
.
getSegmentCount
(
newPeriodDurationUs
);
long
oldIndexSegmentCount
=
oldIndex
.
getSegmentCount
(
newPeriodDurationUs
);
if
(
oldIndexSegmentCount
==
0
)
{
if
(
oldIndexSegmentCount
==
0
)
{
// Segment numbers cannot shift if the old index was empty.
// Segment numbers cannot shift if the old index was empty.
return
new
RepresentationHolder
(
return
new
RepresentationHolder
(
...
@@ -777,7 +777,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
...
@@ -777,7 +777,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
+
segmentNumShift
;
+
segmentNumShift
;
}
}
public
int
getSegmentCount
()
{
public
long
getSegmentCount
()
{
return
segmentIndex
.
getSegmentCount
(
periodDurationUs
);
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 {
...
@@ -352,12 +352,12 @@ public abstract class Representation {
}
}
@Override
@Override
public
int
getSegmentCount
(
long
periodDurationUs
)
{
public
long
getSegmentCount
(
long
periodDurationUs
)
{
return
segmentBase
.
getSegmentCount
(
periodDurationUs
);
return
segmentBase
.
getSegmentCount
(
periodDurationUs
);
}
}
@Override
@Override
public
int
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
public
long
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
return
segmentBase
.
getAvailableSegmentCount
(
periodDurationUs
,
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;
...
@@ -24,6 +24,9 @@ import androidx.annotation.VisibleForTesting;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.source.dash.DashSegmentIndex
;
import
com.google.android.exoplayer2.source.dash.DashSegmentIndex
;
import
com.google.android.exoplayer2.util.Util
;
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
;
import
java.util.List
;
/**
/**
...
@@ -214,7 +217,7 @@ public abstract class SegmentBase {
...
@@ -214,7 +217,7 @@ public abstract class SegmentBase {
long
duration
=
segmentTimeline
.
get
((
int
)
(
sequenceNumber
-
startNumber
)).
duration
;
long
duration
=
segmentTimeline
.
get
((
int
)
(
sequenceNumber
-
startNumber
)).
duration
;
return
(
duration
*
C
.
MICROS_PER_SECOND
)
/
timescale
;
return
(
duration
*
C
.
MICROS_PER_SECOND
)
/
timescale
;
}
else
{
}
else
{
int
segmentCount
=
getSegmentCount
(
periodDurationUs
);
long
segmentCount
=
getSegmentCount
(
periodDurationUs
);
return
segmentCount
!=
INDEX_UNBOUNDED
return
segmentCount
!=
INDEX_UNBOUNDED
&&
sequenceNumber
==
(
getFirstSegmentNum
()
+
segmentCount
-
1
)
&&
sequenceNumber
==
(
getFirstSegmentNum
()
+
segmentCount
-
1
)
?
(
periodDurationUs
-
getSegmentTimeUs
(
sequenceNumber
))
?
(
periodDurationUs
-
getSegmentTimeUs
(
sequenceNumber
))
...
@@ -264,8 +267,8 @@ public abstract class SegmentBase {
...
@@ -264,8 +267,8 @@ public abstract class SegmentBase {
}
}
/** See {@link DashSegmentIndex#getAvailableSegmentCount(long, long)}. */
/** See {@link DashSegmentIndex#getAvailableSegmentCount(long, long)}. */
public
int
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
public
long
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
int
segmentCount
=
getSegmentCount
(
periodDurationUs
);
long
segmentCount
=
getSegmentCount
(
periodDurationUs
);
if
(
segmentCount
!=
INDEX_UNBOUNDED
)
{
if
(
segmentCount
!=
INDEX_UNBOUNDED
)
{
return
segmentCount
;
return
segmentCount
;
}
}
...
@@ -298,7 +301,7 @@ public abstract class SegmentBase {
...
@@ -298,7 +301,7 @@ public abstract class SegmentBase {
}
}
/** See {@link DashSegmentIndex#getSegmentCount(long)}. */
/** 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. */
/** A {@link MultiSegmentBase} that uses a SegmentList to define its segments. */
...
@@ -356,7 +359,7 @@ public abstract class SegmentBase {
...
@@ -356,7 +359,7 @@ public abstract class SegmentBase {
}
}
@Override
@Override
public
int
getSegmentCount
(
long
periodDurationUs
)
{
public
long
getSegmentCount
(
long
periodDurationUs
)
{
return
mediaSegments
.
size
();
return
mediaSegments
.
size
();
}
}
...
@@ -455,14 +458,17 @@ public abstract class SegmentBase {
...
@@ -455,14 +458,17 @@ public abstract class SegmentBase {
}
}
@Override
@Override
public
int
getSegmentCount
(
long
periodDurationUs
)
{
public
long
getSegmentCount
(
long
periodDurationUs
)
{
if
(
segmentTimeline
!=
null
)
{
if
(
segmentTimeline
!=
null
)
{
return
segmentTimeline
.
size
();
return
segmentTimeline
.
size
();
}
else
if
(
endNumber
!=
C
.
INDEX_UNSET
)
{
}
else
if
(
endNumber
!=
C
.
INDEX_UNSET
)
{
return
(
int
)
(
endNumber
-
startNumber
+
1
)
;
return
endNumber
-
startNumber
+
1
;
}
else
if
(
periodDurationUs
!=
C
.
TIME_UNSET
)
{
}
else
if
(
periodDurationUs
!=
C
.
TIME_UNSET
)
{
long
durationUs
=
(
duration
*
C
.
MICROS_PER_SECOND
)
/
timescale
;
BigInteger
numerator
=
return
(
int
)
Util
.
ceilDivide
(
periodDurationUs
,
durationUs
);
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
{
}
else
{
return
INDEX_UNBOUNDED
;
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;
...
@@ -63,12 +63,12 @@ import com.google.android.exoplayer2.source.dash.DashSegmentIndex;
}
}
@Override
@Override
public
int
getSegmentCount
(
long
periodDurationUs
)
{
public
long
getSegmentCount
(
long
periodDurationUs
)
{
return
1
;
return
1
;
}
}
@Override
@Override
public
int
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
public
long
getAvailableSegmentCount
(
long
periodDurationUs
,
long
nowUnixTimeUs
)
{
return
1
;
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> {
...
@@ -183,7 +183,7 @@ public final class DashDownloader extends SegmentDownloader<DashManifest> {
continue
;
continue
;
}
}
int
segmentCount
=
index
.
getSegmentCount
(
periodDurationUs
);
long
segmentCount
=
index
.
getSegmentCount
(
periodDurationUs
);
if
(
segmentCount
==
DashSegmentIndex
.
INDEX_UNBOUNDED
)
{
if
(
segmentCount
==
DashSegmentIndex
.
INDEX_UNBOUNDED
)
{
throw
new
DownloadException
(
"Unbounded segment index"
);
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 {
...
@@ -182,4 +182,43 @@ public final class SegmentBaseTest {
/* nowUnixTimeUs= */
periodStartUnixTimeUs
+
17_500_000
))
/* nowUnixTimeUs= */
periodStartUnixTimeUs
+
17_500_000
))
.
isEqualTo
(
19_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