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
41b58d50
authored
Oct 14, 2020
by
christosts
Committed by
Oliver Woodman
Oct 17, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Use low latency properties in DashMediaSource
Issue: #4904 PiperOrigin-RevId: 337046645
parent
6f66e7d0
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
585 additions
and
71 deletions
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaSourceTest.java
testdata/src/test/assets/media/mpd/sample_mpd_live_with_complete_service_description
testdata/src/test/assets/media/mpd/sample_mpd_live_with_offset_inside_window
testdata/src/test/assets/media/mpd/sample_mpd_live_with_offset_too_long
testdata/src/test/assets/media/mpd/sample_mpd_live_with_offset_too_short
testdata/src/test/assets/media/mpd/sample_mpd_live_with_suggested_presentation_delay_2s
testdata/src/test/assets/media/mpd/sample_mpd_live_without_live_configuration
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
View file @
41b58d50
...
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
source
.
dash
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Assertions
.
checkNotNull
;
import
static
com
.
google
.
android
.
exoplayer2
.
util
.
Util
.
castNonNull
;
import
static
java
.
lang
.
Math
.
max
;
import
static
java
.
lang
.
Math
.
min
;
...
...
@@ -29,6 +30,7 @@ import com.google.android.exoplayer2.C;
import
com.google.android.exoplayer2.ExoPlayerLibraryInfo
;
import
com.google.android.exoplayer2.MediaItem
;
import
com.google.android.exoplayer2.ParserException
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.Timeline
;
import
com.google.android.exoplayer2.drm.DrmSessionEventListener
;
import
com.google.android.exoplayer2.drm.DrmSessionManager
;
...
...
@@ -102,8 +104,8 @@ public final class DashMediaSource extends BaseMediaSource {
@Nullable
private
DrmSessionManager
drmSessionManager
;
private
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
LoadErrorHandlingPolicy
loadErrorHandlingPolicy
;
private
long
livePresentationDelay
Ms
;
private
boolean
livePresentationDelayOverridesManifest
;
private
long
targetLiveOffsetOverride
Ms
;
private
long
fallbackTargetLiveOffsetMs
;
@Nullable
private
ParsingLoadable
.
Parser
<?
extends
DashManifest
>
manifestParser
;
private
List
<
StreamKey
>
streamKeys
;
@Nullable
private
Object
tag
;
...
...
@@ -134,7 +136,8 @@ public final class DashMediaSource extends BaseMediaSource {
this
.
manifestDataSourceFactory
=
manifestDataSourceFactory
;
mediaSourceDrmHelper
=
new
MediaSourceDrmHelper
();
loadErrorHandlingPolicy
=
new
DefaultLoadErrorHandlingPolicy
();
livePresentationDelayMs
=
DEFAULT_LIVE_PRESENTATION_DELAY_MS
;
targetLiveOffsetOverrideMs
=
C
.
TIME_UNSET
;
fallbackTargetLiveOffsetMs
=
DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS
;
compositeSequenceableLoaderFactory
=
new
DefaultCompositeSequenceableLoaderFactory
();
streamKeys
=
Collections
.
emptyList
();
}
...
...
@@ -204,34 +207,31 @@ public final class DashMediaSource extends BaseMediaSource {
return
this
;
}
/** @deprecated Use {@link #setLivePresentationDelayMs(long, boolean)} instead. */
/**
* @deprecated Use {@link MediaItem.Builder#setLiveTargetOffsetMs(long)} to override the
* manifest, or {@link #setFallbackTargetLiveOffsetMs(long)} to provide a fallback value.
*/
@Deprecated
@SuppressWarnings
(
"deprecation"
)
public
Factory
setLivePresentationDelayMs
(
long
livePresentationDelayMs
)
{
if
(
livePresentationDelayMs
==
DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS
)
{
return
setLivePresentationDelayMs
(
DEFAULT_LIVE_PRESENTATION_DELAY_MS
,
false
);
}
else
{
return
setLivePresentationDelayMs
(
livePresentationDelayMs
,
true
);
public
Factory
setLivePresentationDelayMs
(
long
livePresentationDelayMs
,
boolean
overridesManifest
)
{
targetLiveOffsetOverrideMs
=
overridesManifest
?
livePresentationDelayMs
:
C
.
TIME_UNSET
;
if
(!
overridesManifest
)
{
setFallbackTargetLiveOffsetMs
(
livePresentationDelayMs
);
}
return
this
;
}
/**
* Sets the duration in milliseconds by which the default start position should precede the end
* of the live window for live playbacks. The {@code overridesManifest} parameter specifies
* whether the value is used in preference to one in the manifest, if present. The default value
* is {@link #DEFAULT_LIVE_PRESENTATION_DELAY_MS}, and by default {@code overridesManifest} is
* false.
* Sets the {@link Player#getCurrentLiveOffset() target offset for live streams} that is used if
* no value is defined in the {@link MediaItem} or the manifest.
*
* <p>The default value is {@link #DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS}.
*
* @param livePresentationDelayMs For live playbacks, the duration in milliseconds by which the
* default start position should precede the end of the live window.
* @param overridesManifest Whether the value is used in preference to one in the manifest, if
* present.
* @param fallbackTargetLiveOffsetMs The fallback live target offset in milliseconds.
* @return This factory, for convenience.
*/
public
Factory
setLivePresentationDelayMs
(
long
livePresentationDelayMs
,
boolean
overridesManifest
)
{
this
.
livePresentationDelayMs
=
livePresentationDelayMs
;
this
.
livePresentationDelayOverridesManifest
=
overridesManifest
;
public
Factory
setFallbackTargetLiveOffsetMs
(
long
fallbackTargetLiveOffsetMs
)
{
this
.
fallbackTargetLiveOffsetMs
=
fallbackTargetLiveOffsetMs
;
return
this
;
}
...
...
@@ -306,12 +306,17 @@ public final class DashMediaSource extends BaseMediaSource {
}
boolean
hasUri
=
mediaItem
.
playbackProperties
!=
null
;
boolean
hasTag
=
hasUri
&&
mediaItem
.
playbackProperties
.
tag
!=
null
;
boolean
hasTargetLiveOffset
=
mediaItem
.
liveConfiguration
.
targetLiveOffsetMs
!=
C
.
TIME_UNSET
;
mediaItem
=
mediaItem
.
buildUpon
()
.
setMimeType
(
MimeTypes
.
APPLICATION_MPD
)
.
setUri
(
hasUri
?
mediaItem
.
playbackProperties
.
uri
:
Uri
.
EMPTY
)
.
setTag
(
hasTag
?
mediaItem
.
playbackProperties
.
tag
:
tag
)
.
setLiveTargetOffsetMs
(
hasTargetLiveOffset
?
mediaItem
.
liveConfiguration
.
targetLiveOffsetMs
:
targetLiveOffsetOverrideMs
)
.
setStreamKeys
(
streamKeys
)
.
build
();
return
new
DashMediaSource
(
...
...
@@ -323,8 +328,7 @@ public final class DashMediaSource extends BaseMediaSource {
compositeSequenceableLoaderFactory
,
drmSessionManager
!=
null
?
drmSessionManager
:
mediaSourceDrmHelper
.
create
(
mediaItem
),
loadErrorHandlingPolicy
,
livePresentationDelayMs
,
livePresentationDelayOverridesManifest
);
fallbackTargetLiveOffsetMs
);
}
/** @deprecated Use {@link #createMediaSource(MediaItem)} instead. */
...
...
@@ -365,12 +369,21 @@ public final class DashMediaSource extends BaseMediaSource {
boolean
needsTag
=
mediaItem
.
playbackProperties
.
tag
==
null
&&
tag
!=
null
;
boolean
needsStreamKeys
=
mediaItem
.
playbackProperties
.
streamKeys
.
isEmpty
()
&&
!
streamKeys
.
isEmpty
();
if
(
needsTag
&&
needsStreamKeys
)
{
mediaItem
=
mediaItem
.
buildUpon
().
setTag
(
tag
).
setStreamKeys
(
streamKeys
).
build
();
}
else
if
(
needsTag
)
{
mediaItem
=
mediaItem
.
buildUpon
().
setTag
(
tag
).
build
();
}
else
if
(
needsStreamKeys
)
{
mediaItem
=
mediaItem
.
buildUpon
().
setStreamKeys
(
streamKeys
).
build
();
boolean
needsTargetLiveOffset
=
mediaItem
.
liveConfiguration
.
targetLiveOffsetMs
==
C
.
TIME_UNSET
&&
targetLiveOffsetOverrideMs
!=
C
.
TIME_UNSET
;
if
(
needsTag
||
needsStreamKeys
||
needsTargetLiveOffset
)
{
MediaItem
.
Builder
builder
=
mediaItem
.
buildUpon
();
if
(
needsTag
)
{
builder
.
setTag
(
tag
);
}
if
(
needsStreamKeys
)
{
builder
.
setStreamKeys
(
streamKeys
);
}
if
(
needsTargetLiveOffset
)
{
builder
.
setLiveTargetOffsetMs
(
targetLiveOffsetOverrideMs
);
}
mediaItem
=
builder
.
build
();
}
return
new
DashMediaSource
(
mediaItem
,
...
...
@@ -381,8 +394,7 @@ public final class DashMediaSource extends BaseMediaSource {
compositeSequenceableLoaderFactory
,
drmSessionManager
!=
null
?
drmSessionManager
:
mediaSourceDrmHelper
.
create
(
mediaItem
),
loadErrorHandlingPolicy
,
livePresentationDelayMs
,
livePresentationDelayOverridesManifest
);
fallbackTargetLiveOffsetMs
);
}
@Override
...
...
@@ -392,17 +404,12 @@ public final class DashMediaSource extends BaseMediaSource {
}
/**
* The default
presentation delay for live streams. The presentation delay is the duration by
*
which the default start position precedes the end of the live window
.
* The default
target {@link Player#getCurrentLiveOffset() offset for live streams} that is used
*
if no value is defined in the {@link MediaItem} or the manifest
.
*/
public
static
final
long
DEFAULT_LIVE_PRESENTATION_DELAY_MS
=
30_000
;
/** @deprecated Use {@link #DEFAULT_LIVE_PRESENTATION_DELAY_MS}. */
@Deprecated
public
static
final
long
DEFAULT_LIVE_PRESENTATION_DELAY_FIXED_MS
=
DEFAULT_LIVE_PRESENTATION_DELAY_MS
;
/** @deprecated Use of this parameter is no longer necessary. */
@Deprecated
public
static
final
long
DEFAULT_LIVE_PRESENTATION_DELAY_PREFER_MANIFEST_MS
=
-
1
;
public
static
final
long
DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS
=
30_000
;
/** @deprecated Use {@link #DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS} instead. */
@Deprecated
public
static
final
long
DEFAULT_LIVE_PRESENTATION_DELAY_MS
=
30_000
;
/** The media id used by media items of dash media sources without a manifest URI. */
public
static
final
String
DUMMY_MEDIA_ID
=
"com.google.android.exoplayer2.source.dash.DashMediaSource"
;
...
...
@@ -426,8 +433,7 @@ public final class DashMediaSource extends BaseMediaSource {
private
final
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
final
DrmSessionManager
drmSessionManager
;
private
final
LoadErrorHandlingPolicy
loadErrorHandlingPolicy
;
private
final
long
livePresentationDelayMs
;
private
final
boolean
livePresentationDelayOverridesManifest
;
private
final
long
fallbackTargetLiveOffsetMs
;
private
final
EventDispatcher
manifestEventDispatcher
;
private
final
ParsingLoadable
.
Parser
<?
extends
DashManifest
>
manifestParser
;
private
final
ManifestCallback
manifestCallback
;
...
...
@@ -437,8 +443,6 @@ public final class DashMediaSource extends BaseMediaSource {
private
final
Runnable
simulateManifestRefreshRunnable
;
private
final
PlayerEmsgCallback
playerEmsgCallback
;
private
final
LoaderErrorThrower
manifestLoadErrorThrower
;
private
final
MediaItem
mediaItem
;
private
final
MediaItem
.
PlaybackProperties
playbackProperties
;
private
DataSource
dataSource
;
private
Loader
loader
;
...
...
@@ -447,6 +451,8 @@ public final class DashMediaSource extends BaseMediaSource {
private
IOException
manifestFatalError
;
private
Handler
handler
;
private
MediaItem
mediaItem
;
private
MediaItem
.
PlaybackProperties
playbackProperties
;
private
Uri
manifestUri
;
private
Uri
initialManifestUri
;
private
DashManifest
manifest
;
...
...
@@ -469,8 +475,7 @@ public final class DashMediaSource extends BaseMediaSource {
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
,
DrmSessionManager
drmSessionManager
,
LoadErrorHandlingPolicy
loadErrorHandlingPolicy
,
long
livePresentationDelayMs
,
boolean
livePresentationDelayOverridesManifest
)
{
long
fallbackTargetLiveOffsetMs
)
{
this
.
mediaItem
=
mediaItem
;
this
.
playbackProperties
=
checkNotNull
(
mediaItem
.
playbackProperties
);
this
.
manifestUri
=
playbackProperties
.
uri
;
...
...
@@ -481,8 +486,7 @@ public final class DashMediaSource extends BaseMediaSource {
this
.
chunkSourceFactory
=
chunkSourceFactory
;
this
.
drmSessionManager
=
drmSessionManager
;
this
.
loadErrorHandlingPolicy
=
loadErrorHandlingPolicy
;
this
.
livePresentationDelayMs
=
livePresentationDelayMs
;
this
.
livePresentationDelayOverridesManifest
=
livePresentationDelayOverridesManifest
;
this
.
fallbackTargetLiveOffsetMs
=
fallbackTargetLiveOffsetMs
;
this
.
compositeSequenceableLoaderFactory
=
compositeSequenceableLoaderFactory
;
sideloadedManifest
=
manifest
!=
null
;
manifestEventDispatcher
=
createEventDispatcher
(
/* mediaPeriodId= */
null
);
...
...
@@ -688,6 +692,9 @@ public final class DashMediaSource extends BaseMediaSource {
staleManifestReloadAttempt
=
0
;
}
mediaItem
=
mergeLiveConfiguration
(
mediaItem
,
fallbackTargetLiveOffsetMs
,
newManifest
);
playbackProperties
=
castNonNull
(
mediaItem
.
playbackProperties
);
manifest
=
newManifest
;
manifestLoadPending
&=
manifest
.
dynamic
;
manifestLoadStartTimestampMs
=
elapsedRealtimeMs
-
loadDurationMs
;
...
...
@@ -921,23 +928,7 @@ public final class DashMediaSource extends BaseMediaSource {
for
(
int
i
=
0
;
i
<
manifest
.
getPeriodCount
()
-
1
;
i
++)
{
windowDurationUs
+=
manifest
.
getPeriodDurationUs
(
i
);
}
long
windowDefaultStartPositionUs
=
0
;
if
(
manifest
.
dynamic
)
{
long
presentationDelayForManifestMs
=
livePresentationDelayMs
;
if
(!
livePresentationDelayOverridesManifest
&&
manifest
.
suggestedPresentationDelayMs
!=
C
.
TIME_UNSET
)
{
presentationDelayForManifestMs
=
manifest
.
suggestedPresentationDelayMs
;
}
// Snap the default position to the start of the segment containing it.
windowDefaultStartPositionUs
=
windowDurationUs
-
C
.
msToUs
(
presentationDelayForManifestMs
);
if
(
windowDefaultStartPositionUs
<
MIN_LIVE_DEFAULT_START_POSITION_US
)
{
// The default start position is too close to the start of the live window. Set it to the
// minimum default start position provided the window is at least twice as big. Else set
// it to the middle of the window.
windowDefaultStartPositionUs
=
min
(
MIN_LIVE_DEFAULT_START_POSITION_US
,
windowDurationUs
/
2
);
}
}
long
windowStartTimeMs
=
C
.
TIME_UNSET
;
if
(
manifest
.
availabilityStartTimeMs
!=
C
.
TIME_UNSET
)
{
windowStartTimeMs
=
...
...
@@ -945,6 +936,25 @@ public final class DashMediaSource extends BaseMediaSource {
+
manifest
.
getPeriod
(
0
).
startMs
+
C
.
usToMs
(
currentStartTimeUs
);
}
long
windowDefaultStartPositionUs
=
0
;
if
(
manifest
.
dynamic
)
{
ensureTargetLiveOffsetIsInLiveWindow
(
/* nowPeriodTimeUs= */
currentStartTimeUs
+
nowUnixTimeUs
-
C
.
msToUs
(
windowStartTimeMs
),
/* windowStartPeriodTimeUs= */
currentStartTimeUs
,
/* windowEndPeriodTimeUs= */
currentEndTimeUs
);
windowDefaultStartPositionUs
=
nowUnixTimeUs
-
C
.
msToUs
(
windowStartTimeMs
+
mediaItem
.
liveConfiguration
.
targetLiveOffsetMs
);
long
minimumDefaultStartPositionUs
=
min
(
MIN_LIVE_DEFAULT_START_POSITION_US
,
windowDurationUs
/
2
);
if
(
windowDefaultStartPositionUs
<
minimumDefaultStartPositionUs
)
{
// The default start position is too close to the start of the live window. Set it to the
// minimum default start position provided the window is at least twice as big. Else set
// it to the middle of the window.
windowDefaultStartPositionUs
=
minimumDefaultStartPositionUs
;
}
}
DashTimeline
timeline
=
new
DashTimeline
(
manifest
.
availabilityStartTimeMs
,
...
...
@@ -989,6 +999,26 @@ public final class DashMediaSource extends BaseMediaSource {
}
}
private
void
ensureTargetLiveOffsetIsInLiveWindow
(
long
nowPeriodTimeUs
,
long
windowStartPeriodTimeUs
,
long
windowEndPeriodTimeUs
)
{
long
targetLiveOffsetUs
=
C
.
msToUs
(
mediaItem
.
liveConfiguration
.
targetLiveOffsetMs
);
long
minOffsetUs
=
nowPeriodTimeUs
-
windowEndPeriodTimeUs
;
if
(
targetLiveOffsetUs
<
minOffsetUs
)
{
targetLiveOffsetUs
=
minOffsetUs
;
}
long
maxOffsetUs
=
nowPeriodTimeUs
-
windowStartPeriodTimeUs
;
if
(
targetLiveOffsetUs
>
maxOffsetUs
)
{
long
windowDurationUs
=
windowEndPeriodTimeUs
-
windowStartPeriodTimeUs
;
targetLiveOffsetUs
=
maxOffsetUs
-
min
(
MIN_LIVE_DEFAULT_START_POSITION_US
,
windowDurationUs
/
2
);
}
long
targetLiveOffsetMs
=
C
.
usToMs
(
targetLiveOffsetUs
);
if
(
mediaItem
.
liveConfiguration
.
targetLiveOffsetMs
!=
targetLiveOffsetMs
)
{
mediaItem
=
mediaItem
.
buildUpon
().
setLiveTargetOffsetMs
(
targetLiveOffsetMs
).
build
();
playbackProperties
=
castNonNull
(
mediaItem
.
playbackProperties
);
}
}
private
void
scheduleManifestRefresh
(
long
delayUntilNextLoadMs
)
{
handler
.
postDelayed
(
refreshManifestRunnable
,
delayUntilNextLoadMs
);
}
...
...
@@ -1057,6 +1087,41 @@ public final class DashMediaSource extends BaseMediaSource {
return
LongMath
.
divide
(
intervalUs
,
1000
,
RoundingMode
.
CEILING
);
}
private
static
MediaItem
mergeLiveConfiguration
(
MediaItem
mediaItem
,
long
fallbackTargetLiveOffsetMs
,
DashManifest
manifest
)
{
// Evaluate live config properties from media item and manifest according to precedence.
long
liveTargetOffsetMs
;
if
(
mediaItem
.
liveConfiguration
.
targetLiveOffsetMs
!=
C
.
TIME_UNSET
)
{
liveTargetOffsetMs
=
mediaItem
.
liveConfiguration
.
targetLiveOffsetMs
;
}
else
if
(
manifest
.
serviceDescription
!=
null
&&
manifest
.
serviceDescription
.
targetOffsetMs
!=
C
.
TIME_UNSET
)
{
liveTargetOffsetMs
=
manifest
.
serviceDescription
.
targetOffsetMs
;
}
else
if
(
manifest
.
suggestedPresentationDelayMs
!=
C
.
TIME_UNSET
)
{
liveTargetOffsetMs
=
manifest
.
suggestedPresentationDelayMs
;
}
else
{
liveTargetOffsetMs
=
fallbackTargetLiveOffsetMs
;
}
float
liveMinPlaybackSpeed
=
C
.
RATE_UNSET
;
if
(
mediaItem
.
liveConfiguration
.
minPlaybackSpeed
!=
C
.
RATE_UNSET
)
{
liveMinPlaybackSpeed
=
mediaItem
.
liveConfiguration
.
minPlaybackSpeed
;
}
else
if
(
manifest
.
serviceDescription
!=
null
)
{
liveMinPlaybackSpeed
=
manifest
.
serviceDescription
.
minPlaybackSpeed
;
}
float
liveMaxPlaybackSpeed
=
C
.
RATE_UNSET
;
if
(
mediaItem
.
liveConfiguration
.
maxPlaybackSpeed
!=
C
.
RATE_UNSET
)
{
liveMaxPlaybackSpeed
=
mediaItem
.
liveConfiguration
.
maxPlaybackSpeed
;
}
else
if
(
manifest
.
serviceDescription
!=
null
)
{
liveMaxPlaybackSpeed
=
manifest
.
serviceDescription
.
maxPlaybackSpeed
;
}
// Update live configuration in the media item.
return
mediaItem
.
buildUpon
()
.
setLiveTargetOffsetMs
(
liveTargetOffsetMs
)
.
setLiveMinPlaybackSpeed
(
liveMinPlaybackSpeed
)
.
setLiveMaxPlaybackSpeed
(
liveMaxPlaybackSpeed
)
.
build
();
}
private
static
final
class
PeriodSeekInfo
{
public
static
PeriodSeekInfo
createPeriodSeekInfo
(
...
...
@@ -1244,8 +1309,9 @@ public final class DashMediaSource extends BaseMediaSource {
}
// If there are multiple video adaptation sets with unaligned segments, the initial time may
// not correspond to the start of a segment in both, but this is an edge case.
DashSegmentIndex
snapIndex
=
period
.
adaptationSets
.
get
(
videoAdaptationSetIndex
)
.
representations
.
get
(
0
).
getIndex
();
@Nullable
DashSegmentIndex
snapIndex
=
period
.
adaptationSets
.
get
(
videoAdaptationSetIndex
).
representations
.
get
(
0
).
getIndex
();
if
(
snapIndex
==
null
||
snapIndex
.
getSegmentCount
(
periodDurationUs
)
==
0
)
{
// Video adaptation set does not include a non-empty index for snapping.
return
windowDefaultStartPositionUs
;
...
...
library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaSourceTest.java
View file @
41b58d50
...
...
@@ -16,27 +16,56 @@
package
com
.
google
.
android
.
exoplayer2
.
source
.
dash
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
java
.
util
.
concurrent
.
TimeUnit
.
MILLISECONDS
;
import
static
org
.
junit
.
Assert
.
fail
;
import
static
org
.
robolectric
.
annotation
.
LooperMode
.
Mode
.
PAUSED
;
import
android.net.Uri
;
import
androidx.annotation.Nullable
;
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.ParserException
;
import
com.google.android.exoplayer2.Timeline
;
import
com.google.android.exoplayer2.Timeline.Window
;
import
com.google.android.exoplayer2.offline.StreamKey
;
import
com.google.android.exoplayer2.source.MediaSource
;
import
com.google.android.exoplayer2.source.MediaSource.MediaSourceCaller
;
import
com.google.android.exoplayer2.testutil.TestUtil
;
import
com.google.android.exoplayer2.upstream.ByteArrayDataSource
;
import
com.google.android.exoplayer2.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.FileDataSource
;
import
com.google.android.exoplayer2.upstream.ParsingLoadable
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.common.collect.ImmutableList
;
import
java.io.ByteArrayInputStream
;
import
java.io.IOException
;
import
java.util.concurrent.CountDownLatch
;
import
java.util.concurrent.atomic.AtomicReference
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.robolectric.annotation.LooperMode
;
import
org.robolectric.shadows.ShadowLooper
;
/** Unit test for {@link DashMediaSource}. */
@RunWith
(
AndroidJUnit4
.
class
)
@LooperMode
(
PAUSED
)
public
final
class
DashMediaSourceTest
{
private
static
final
String
SAMPLE_MPD_LIVE_WITHOUT_LIVE_CONFIGURATION
=
"media/mpd/sample_mpd_live_without_live_configuration"
;
private
static
final
String
SAMPLE_MPD_LIVE_WITH_SUGGESTED_PRESENTATION_DELAY_2S
=
"media/mpd/sample_mpd_live_with_suggested_presentation_delay_2s"
;
private
static
final
String
SAMPLE_MPD_LIVE_WITH_COMPLETE_SERVICE_DESCRIPTION
=
"media/mpd/sample_mpd_live_with_complete_service_description"
;
private
static
final
String
SAMPLE_MPD_LIVE_WITH_OFFSET_INSIDE_WINDOW
=
"media/mpd/sample_mpd_live_with_offset_inside_window"
;
private
static
final
String
SAMPLE_MPD_LIVE_WITH_OFFSET_TOO_SHORT
=
"media/mpd/sample_mpd_live_with_offset_too_short"
;
private
static
final
String
SAMPLE_MPD_LIVE_WITH_OFFSET_TOO_LONG
=
"media/mpd/sample_mpd_live_with_offset_too_long"
;
@Test
public
void
iso8601ParserParse
()
throws
IOException
{
DashMediaSource
.
Iso8601Parser
parser
=
new
DashMediaSource
.
Iso8601Parser
();
...
...
@@ -157,7 +186,7 @@ public final class DashMediaSourceTest {
// Tests backwards compatibility
@SuppressWarnings
(
"deprecation"
)
@Test
public
void
factorySetStreamKeys_withMediaItemStreamKeys_doesNot
s
OverrideMediaItemStreamKeys
()
{
public
void
factorySetStreamKeys_withMediaItemStreamKeys_doesNotOverrideMediaItemStreamKeys
()
{
StreamKey
mediaItemStreamKey
=
new
StreamKey
(
/* groupIndex= */
0
,
/* trackIndex= */
1
);
MediaItem
mediaItem
=
new
MediaItem
.
Builder
()
...
...
@@ -187,6 +216,280 @@ public final class DashMediaSourceTest {
assertThat
(
mediaSource
.
getMediaItem
()).
isEqualTo
(
mediaItem
);
}
@Test
public
void
factorySetFallbackTargetLiveOffsetMs_withMediaLiveTargetOffsetMs_usesMediaOffset
()
{
MediaItem
mediaItem
=
new
MediaItem
.
Builder
().
setUri
(
Uri
.
EMPTY
).
setLiveTargetOffsetMs
(
2L
).
build
();
DashMediaSource
.
Factory
factory
=
new
DashMediaSource
.
Factory
(
new
FileDataSource
.
Factory
())
.
setFallbackTargetLiveOffsetMs
(
1234L
);
MediaItem
dashMediaItem
=
factory
.
createMediaSource
(
mediaItem
).
getMediaItem
();
assertThat
(
dashMediaItem
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
2L
);
}
@Test
public
void
factorySetLivePresentationDelayMs_withMediaLiveTargetOffset_usesMediaOffset
()
{
MediaItem
mediaItem
=
new
MediaItem
.
Builder
().
setUri
(
Uri
.
EMPTY
).
setLiveTargetOffsetMs
(
2L
).
build
();
DashMediaSource
.
Factory
factory
=
new
DashMediaSource
.
Factory
(
new
FileDataSource
.
Factory
())
.
setLivePresentationDelayMs
(
1234L
,
/* overridesManifest= */
true
);
MediaItem
dashMediaItem
=
factory
.
createMediaSource
(
mediaItem
).
getMediaItem
();
assertThat
(
dashMediaItem
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
2L
);
}
@Test
public
void
factorySetLivePresentationDelayMs_overridingManifest_mixedIntoMediaItem
()
{
MediaItem
mediaItem
=
new
MediaItem
.
Builder
().
setUri
(
Uri
.
EMPTY
).
build
();
DashMediaSource
.
Factory
factory
=
new
DashMediaSource
.
Factory
(
new
FileDataSource
.
Factory
())
.
setLivePresentationDelayMs
(
2000L
,
/* overridesManifest= */
true
);
MediaItem
dashMediaItem
=
factory
.
createMediaSource
(
mediaItem
).
getMediaItem
();
assertThat
(
dashMediaItem
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
2000L
);
}
@Test
public
void
factorySetLivePresentationDelayMs_notOverridingManifest_unsetInMediaItem
()
{
MediaItem
mediaItem
=
new
MediaItem
.
Builder
().
setUri
(
Uri
.
EMPTY
).
build
();
DashMediaSource
.
Factory
factory
=
new
DashMediaSource
.
Factory
(
new
FileDataSource
.
Factory
())
.
setLivePresentationDelayMs
(
2000L
,
/* overridesManifest= */
false
);
MediaItem
dashMediaItem
=
factory
.
createMediaSource
(
mediaItem
).
getMediaItem
();
assertThat
(
dashMediaItem
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
C
.
TIME_UNSET
);
}
@Test
public
void
factorySetFallbackTargetLiveOffsetMs_doesNotChangeMediaItem
()
{
DashMediaSource
.
Factory
factory
=
new
DashMediaSource
.
Factory
(
new
FileDataSource
.
Factory
())
.
setFallbackTargetLiveOffsetMs
(
2000L
);
MediaItem
dashMediaItem
=
factory
.
createMediaSource
(
MediaItem
.
fromUri
(
Uri
.
EMPTY
)).
getMediaItem
();
assertThat
(
dashMediaItem
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
C
.
TIME_UNSET
);
}
@Test
public
void
prepare_withoutLiveConfiguration_withoutMediaItemLiveProperties_usesDefaultFallback
()
throws
InterruptedException
{
DashMediaSource
mediaSource
=
new
DashMediaSource
.
Factory
(
()
->
createSampleMpdDataSource
(
SAMPLE_MPD_LIVE_WITHOUT_LIVE_CONFIGURATION
))
.
createMediaSource
(
MediaItem
.
fromUri
(
Uri
.
EMPTY
));
MediaItem
mediaItemFromSource
=
prepareAndWaitForTimelineRefresh
(
mediaSource
).
mediaItem
;
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
targetLiveOffsetMs
)
.
isEqualTo
(
DashMediaSource
.
DEFAULT_FALLBACK_TARGET_LIVE_OFFSET_MS
);
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
minPlaybackSpeed
).
isEqualTo
(
C
.
RATE_UNSET
);
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
maxPlaybackSpeed
).
isEqualTo
(
C
.
RATE_UNSET
);
}
@Test
public
void
prepare_withoutLiveConfiguration_withoutMediaItemLiveProperties_usesFallback
()
throws
InterruptedException
{
DashMediaSource
mediaSource
=
new
DashMediaSource
.
Factory
(
()
->
createSampleMpdDataSource
(
SAMPLE_MPD_LIVE_WITHOUT_LIVE_CONFIGURATION
))
.
setFallbackTargetLiveOffsetMs
(
1234L
)
.
createMediaSource
(
MediaItem
.
fromUri
(
Uri
.
EMPTY
));
MediaItem
mediaItemFromSource
=
prepareAndWaitForTimelineRefresh
(
mediaSource
).
mediaItem
;
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
1234L
);
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
minPlaybackSpeed
).
isEqualTo
(
C
.
RATE_UNSET
);
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
maxPlaybackSpeed
).
isEqualTo
(
C
.
RATE_UNSET
);
}
@Test
public
void
prepare_withoutLiveConfiguration_withMediaItemLiveProperties_usesMediaItem
()
throws
InterruptedException
{
MediaItem
mediaItem
=
new
MediaItem
.
Builder
()
.
setUri
(
Uri
.
EMPTY
)
.
setLiveTargetOffsetMs
(
876L
)
.
setLiveMinPlaybackSpeed
(
23
f
)
.
setLiveMaxPlaybackSpeed
(
42
f
)
.
build
();
DashMediaSource
mediaSource
=
new
DashMediaSource
.
Factory
(
()
->
createSampleMpdDataSource
(
SAMPLE_MPD_LIVE_WITHOUT_LIVE_CONFIGURATION
))
.
setFallbackTargetLiveOffsetMs
(
1234L
)
.
createMediaSource
(
mediaItem
);
MediaItem
mediaItemFromSource
=
prepareAndWaitForTimelineRefresh
(
mediaSource
).
mediaItem
;
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
876L
);
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
minPlaybackSpeed
).
isEqualTo
(
23
f
);
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
maxPlaybackSpeed
).
isEqualTo
(
42
f
);
}
@Test
public
void
prepare_withSuggestedPresentationDelay_usesManifestValue
()
throws
InterruptedException
{
DashMediaSource
mediaSource
=
new
DashMediaSource
.
Factory
(
()
->
createSampleMpdDataSource
(
SAMPLE_MPD_LIVE_WITH_SUGGESTED_PRESENTATION_DELAY_2S
))
.
setFallbackTargetLiveOffsetMs
(
1234L
)
.
createMediaSource
(
MediaItem
.
fromUri
(
Uri
.
EMPTY
));
MediaItem
mediaItem
=
prepareAndWaitForTimelineRefresh
(
mediaSource
).
mediaItem
;
assertThat
(
mediaItem
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
2_000L
);
assertThat
(
mediaItem
.
liveConfiguration
.
minPlaybackSpeed
).
isEqualTo
(
C
.
RATE_UNSET
);
assertThat
(
mediaItem
.
liveConfiguration
.
maxPlaybackSpeed
).
isEqualTo
(
C
.
RATE_UNSET
);
}
@Test
public
void
prepare_withSuggestedPresentationDelay_withMediaItemLiveProperties_usesMediaItem
()
throws
InterruptedException
{
MediaItem
mediaItem
=
new
MediaItem
.
Builder
()
.
setUri
(
Uri
.
EMPTY
)
.
setLiveTargetOffsetMs
(
876L
)
.
setLiveMinPlaybackSpeed
(
23
f
)
.
setLiveMaxPlaybackSpeed
(
42
f
)
.
build
();
DashMediaSource
mediaSource
=
new
DashMediaSource
.
Factory
(
()
->
createSampleMpdDataSource
(
SAMPLE_MPD_LIVE_WITH_SUGGESTED_PRESENTATION_DELAY_2S
))
.
setFallbackTargetLiveOffsetMs
(
1234L
)
.
createMediaSource
(
mediaItem
);
MediaItem
mediaItemFromSource
=
prepareAndWaitForTimelineRefresh
(
mediaSource
).
mediaItem
;
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
876L
);
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
minPlaybackSpeed
).
isEqualTo
(
23
f
);
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
maxPlaybackSpeed
).
isEqualTo
(
42
f
);
}
@Test
public
void
prepare_withCompleteServiceDescription_usesManifestValue
()
throws
InterruptedException
{
DashMediaSource
mediaSource
=
new
DashMediaSource
.
Factory
(
()
->
createSampleMpdDataSource
(
SAMPLE_MPD_LIVE_WITH_COMPLETE_SERVICE_DESCRIPTION
))
.
setFallbackTargetLiveOffsetMs
(
1234L
)
.
createMediaSource
(
MediaItem
.
fromUri
(
Uri
.
EMPTY
));
MediaItem
mediaItem
=
prepareAndWaitForTimelineRefresh
(
mediaSource
).
mediaItem
;
assertThat
(
mediaItem
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
4_000L
);
assertThat
(
mediaItem
.
liveConfiguration
.
minPlaybackSpeed
).
isEqualTo
(
0.96f
);
assertThat
(
mediaItem
.
liveConfiguration
.
maxPlaybackSpeed
).
isEqualTo
(
1.04f
);
}
@Test
public
void
prepare_withCompleteServiceDescription_withMediaItemLiveProperties_usesMediaItem
()
throws
InterruptedException
{
MediaItem
mediaItem
=
new
MediaItem
.
Builder
()
.
setUri
(
Uri
.
EMPTY
)
.
setLiveTargetOffsetMs
(
876L
)
.
setLiveMinPlaybackSpeed
(
23
f
)
.
setLiveMaxPlaybackSpeed
(
42
f
)
.
build
();
DashMediaSource
mediaSource
=
new
DashMediaSource
.
Factory
(
()
->
createSampleMpdDataSource
(
SAMPLE_MPD_LIVE_WITH_COMPLETE_SERVICE_DESCRIPTION
))
.
setFallbackTargetLiveOffsetMs
(
1234L
)
.
createMediaSource
(
mediaItem
);
MediaItem
mediaItemFromSource
=
prepareAndWaitForTimelineRefresh
(
mediaSource
).
mediaItem
;
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
876L
);
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
minPlaybackSpeed
).
isEqualTo
(
23
f
);
assertThat
(
mediaItemFromSource
.
liveConfiguration
.
maxPlaybackSpeed
).
isEqualTo
(
42
f
);
}
@Test
public
void
prepare_targetLiveOffsetInWindow_manifestTargetOffsetAndAlignedWindowStartPosition
()
throws
InterruptedException
{
DashMediaSource
mediaSource
=
new
DashMediaSource
.
Factory
(
()
->
createSampleMpdDataSource
(
SAMPLE_MPD_LIVE_WITH_OFFSET_INSIDE_WINDOW
))
.
createMediaSource
(
MediaItem
.
fromUri
(
Uri
.
EMPTY
));
Window
window
=
prepareAndWaitForTimelineRefresh
(
mediaSource
);
// Expect the target live offset as defined in the manifest.
assertThat
(
window
.
mediaItem
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
3000
);
// Expect the default position at the first segment start before the live edge.
assertThat
(
window
.
getDefaultPositionMs
()).
isEqualTo
(
2_000
);
}
@Test
public
void
prepare_targetLiveOffsetTooLong_correctedTargetOffsetAndAlignedWindowStartPosition
()
throws
InterruptedException
{
DashMediaSource
mediaSource
=
new
DashMediaSource
.
Factory
(
()
->
createSampleMpdDataSource
(
SAMPLE_MPD_LIVE_WITH_OFFSET_TOO_LONG
))
.
createMediaSource
(
MediaItem
.
fromUri
(
Uri
.
EMPTY
));
Window
window
=
prepareAndWaitForTimelineRefresh
(
mediaSource
);
// Expect the default position at the first segment start below the minimum live start position.
assertThat
(
window
.
getDefaultPositionMs
()).
isEqualTo
(
4_000
);
// Expect the target live offset reaching from now time to the minimum live start position.
assertThat
(
window
.
mediaItem
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
9000
);
}
@Test
public
void
prepare_targetLiveOffsetTooShort_correctedTargetOffsetAndAlignedWindowStartPosition
()
throws
InterruptedException
{
// Load manifest with now time far behind the start of the window.
DashMediaSource
mediaSource
=
new
DashMediaSource
.
Factory
(
()
->
createSampleMpdDataSource
(
SAMPLE_MPD_LIVE_WITH_OFFSET_TOO_SHORT
))
.
createMediaSource
(
MediaItem
.
fromUri
(
Uri
.
EMPTY
));
Window
window
=
prepareAndWaitForTimelineRefresh
(
mediaSource
);
// Expect the default position at the start of the last segment.
assertThat
(
window
.
getDefaultPositionMs
()).
isEqualTo
(
12_000
);
// Expect the target live offset reaching from now time to the end of the window.
assertThat
(
window
.
mediaItem
.
liveConfiguration
.
targetLiveOffsetMs
).
isEqualTo
(
60_000
-
16_000
);
}
private
static
Window
prepareAndWaitForTimelineRefresh
(
MediaSource
mediaSource
)
throws
InterruptedException
{
AtomicReference
<
Window
>
windowReference
=
new
AtomicReference
<>();
CountDownLatch
countDownLatch
=
new
CountDownLatch
(
/* count= */
1
);
MediaSourceCaller
caller
=
(
MediaSource
source
,
Timeline
timeline
)
->
{
if
(
windowReference
.
get
()
==
null
)
{
windowReference
.
set
(
timeline
.
getWindow
(
0
,
new
Timeline
.
Window
()));
countDownLatch
.
countDown
();
}
};
mediaSource
.
prepareSource
(
caller
,
/* mediaTransferListener= */
null
);
while
(!
countDownLatch
.
await
(
/* timeout= */
10
,
MILLISECONDS
))
{
ShadowLooper
.
idleMainLooper
();
}
return
windowReference
.
get
();
}
private
static
DataSource
createSampleMpdDataSource
(
String
fileName
)
{
byte
[]
manifestData
=
new
byte
[
0
];
try
{
manifestData
=
TestUtil
.
getByteArray
(
ApplicationProvider
.
getApplicationContext
(),
fileName
);
}
catch
(
IOException
e
)
{
fail
(
e
.
getMessage
());
}
return
new
ByteArrayDataSource
(
manifestData
);
}
private
static
void
assertParseStringToLong
(
long
expected
,
ParsingLoadable
.
Parser
<
Long
>
parser
,
String
data
)
throws
IOException
{
long
actual
=
parser
.
parse
(
null
,
new
ByteArrayInputStream
(
Util
.
getUtf8Bytes
(
data
)));
...
...
testdata/src/test/assets/media/mpd/sample_mpd_live_with_complete_service_description
0 → 100644
View file @
41b58d50
<?xml version="1.0" encoding="utf-8"?>
<MPD
type=
"dynamic"
suggestedPresentationDelay=
"PT2S"
availabilityStartTime=
"2020-01-01T00:00:00Z"
minimumUpdatePeriod=
"PT4M"
timeShiftBufferDepth=
"PT6.0S"
>
<UTCTiming
schemeIdUri=
"urn:mpeg:dash:utc:direct:2014"
value=
"2020-01-01T01:00:00Z"
/>
<ServiceDescription
id=
"0"
>
<Latency
target=
"4000"
/>
<PlaybackRate
max=
"1.04"
min=
"0.96"
/>
</ServiceDescription>
<Period
start=
"PT0.0S"
>
<AdaptationSet
contentType=
"video"
>
<Representation
id=
"0"
mimeType=
"video/mp4"
>
<SegmentTemplate
timescale=
"1000000"
duration=
"2000000"
availabilityTimeOffset=
"2"
startNumber=
"1"
>
</SegmentTemplate>
</Representation>
</AdaptationSet>
</Period>
</MPD>
testdata/src/test/assets/media/mpd/sample_mpd_live_with_offset_inside_window
0 → 100644
View file @
41b58d50
<?xml version="1.0" encoding="utf-8"?>
<MPD
type=
"dynamic"
minimumUpdatePeriod=
"PT4M"
availabilityStartTime=
"2020-01-01T00:00:00Z"
timeShiftBufferDepth=
"PT6.0S"
>
<UTCTiming
schemeIdUri=
"urn:mpeg:dash:utc:direct:2014"
value=
"2020-01-01T01:00:00Z"
/>
<ServiceDescription
id=
"0"
>
<Latency
target=
"3000"
/>
</ServiceDescription>
<Period
start=
"PT0.0S"
>
<AdaptationSet
contentType=
"video"
>
<Representation
id=
"0"
mimeType=
"video/mp4"
>
<SegmentTemplate
timescale=
"1000000"
duration=
"2000000"
availabilityTimeOffset=
"2"
startNumber=
"1"
/>
</Representation>
</AdaptationSet>
</Period>
</MPD>
testdata/src/test/assets/media/mpd/sample_mpd_live_with_offset_too_long
0 → 100644
View file @
41b58d50
<?xml version="1.0" encoding="utf-8"?>
<MPD
type=
"dynamic"
minimumUpdatePeriod=
"PT4M"
availabilityStartTime=
"2020-01-01T00:00:00Z"
timeShiftBufferDepth=
"PT16.0S"
>
<UTCTiming
schemeIdUri=
"urn:mpeg:dash:utc:direct:2014"
value=
"2020-01-01T00:00:20Z"
/>
<ServiceDescription
id=
"0"
>
<Latency
target=
"30000"
/>
</ServiceDescription>
<Period
start=
"PT0.0S"
>
<AdaptationSet
contentType=
"video"
>
<Representation
id=
"0"
mimeType=
"video/mp4"
>
<SegmentTemplate
timescale=
"1000000"
duration=
"2000000"
availabilityTimeOffset=
"2"
startNumber=
"1"
/>
</Representation>
</AdaptationSet>
</Period>
</MPD>
testdata/src/test/assets/media/mpd/sample_mpd_live_with_offset_too_short
0 → 100644
View file @
41b58d50
<?xml version="1.0" encoding="UTF-8"?>
<MPD
type=
"dynamic"
timeShiftBufferDepth=
"PT16S"
minimumUpdatePeriod=
"PT4M"
availabilityStartTime=
"1970-01-01T00:00:00Z"
>
<!-- Now is 60 seconds after the start of the window. -->
<UTCTiming
schemeIdUri=
"urn:mpeg:dash:utc:direct:2014"
value=
"2020-01-01T00:01:00Z"
/>
<ServiceDescription
id=
"0"
>
<Latency
target=
"4000"
/>
</ServiceDescription>
<Period
id=
"1"
start=
"PT0S"
>
<AdaptationSet
id=
"0"
contentType=
"video"
>
<SegmentTemplate
presentationTimeOffset=
"0"
timescale=
"1000"
startNumber=
"1"
>
<SegmentTimeline>
<!-- t = 2020-01-01T00:00:00Z (UTC) -->
<S
t=
"1577836800000"
d=
"4000"
r=
"3"
/>
</SegmentTimeline>
</SegmentTemplate>
<Representation
id=
"0"
/>
</AdaptationSet>
</Period>
</MPD>
testdata/src/test/assets/media/mpd/sample_mpd_live_with_suggested_presentation_delay_2s
0 → 100644
View file @
41b58d50
<?xml version="1.0" encoding="utf-8"?>
<MPD
type=
"dynamic"
suggestedPresentationDelay=
"PT2S"
minimumUpdatePeriod=
"PT4M"
availabilityStartTime=
"2020-01-01T00:00:00Z"
timeShiftBufferDepth=
"PT6.0S"
>
<UTCTiming
schemeIdUri=
"urn:mpeg:dash:utc:direct:2014"
value=
"2020-01-01T01:00:00Z"
/>
<Period
start=
"PT0.0S"
>
<AdaptationSet
contentType=
"video"
>
<Representation
id=
"0"
mimeType=
"video/mp4"
>
<SegmentTemplate
timescale=
"1000000"
duration=
"2000000"
availabilityTimeOffset=
"2"
startNumber=
"1"
>
</SegmentTemplate>
</Representation>
</AdaptationSet>
</Period>
</MPD>
testdata/src/test/assets/media/mpd/sample_mpd_live_without_live_configuration
0 → 100644
View file @
41b58d50
<?xml version="1.0" encoding="utf-8"?>
<MPD
type=
"dynamic"
minimumUpdatePeriod=
"PT4M"
availabilityStartTime=
"2020-01-01T00:00:00Z"
timeShiftBufferDepth=
"PT1M"
>
<UTCTiming
schemeIdUri=
"urn:mpeg:dash:utc:direct:2014"
value=
"2020-01-01T01:00:00Z"
/>
<Period
start=
"PT0.0S"
>
<AdaptationSet
contentType=
"video"
>
<Representation
id=
"0"
mimeType=
"video/mp4"
>
<SegmentTemplate
timescale=
"1000000"
duration=
"2000000"
availabilityTimeOffset=
"2"
startNumber=
"1"
>
</SegmentTemplate>
</Representation>
</AdaptationSet>
</Period>
</MPD>
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