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
fea43767
authored
Apr 19, 2020
by
olly
Committed by
Oliver Woodman
Apr 20, 2020
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Merge trick play tracks into main track groups
Issue: #6054 PiperOrigin-RevId: 307285068
parent
8ea33e23
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
276 additions
and
60 deletions
RELEASENOTES.md
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java
library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaPeriodTest.java
testutils/src/main/java/com/google/android/exoplayer2/testutil/MediaPeriodAsserts.java
RELEASENOTES.md
View file @
fea43767
...
@@ -97,6 +97,11 @@
...
@@ -97,6 +97,11 @@
*
Remove generics from DRM components.
*
Remove generics from DRM components.
*
Downloads: Merge downloads in
`SegmentDownloader`
to improve overall
*
Downloads: Merge downloads in
`SegmentDownloader`
to improve overall
download speed (
[
#5978
](
https://github.com/google/ExoPlayer/issues/5978
)
).
download speed (
[
#5978
](
https://github.com/google/ExoPlayer/issues/5978
)
).
*
DASH:
*
Merge trick play adaptation sets (i.e., adaptation sets marked with
`http://dashif.org/guidelines/trickmode`
) into the same
`TrackGroup`
as
the main adaptation sets to which they refer. Trick play tracks are
marked with the
`C.ROLE_FLAG_TRICK_PLAY`
flag.
*
MP3: Add
`IndexSeeker`
for accurate seeks in VBR streams
*
MP3: Add
`IndexSeeker`
for accurate seeks in VBR streams
(
[
#6787
](
https://github.com/google/ExoPlayer/issues/6787
)
). This seeker is
(
[
#6787
](
https://github.com/google/ExoPlayer/issues/6787
)
). This seeker is
enabled by passing
`FLAG_ENABLE_INDEX_SEEKING`
to the
`Mp3Extractor`
. It may
enabled by passing
`FLAG_ENABLE_INDEX_SEEKING`
to the
`Mp3Extractor`
. It may
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java
View file @
fea43767
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
source
.
dash
;
package
com
.
google
.
android
.
exoplayer2
.
source
.
dash
;
import
android.util.Pair
;
import
android.util.Pair
;
import
android.util.SparseArray
;
import
android.util.SparseIntArray
;
import
android.util.SparseIntArray
;
import
androidx.annotation.IntDef
;
import
androidx.annotation.IntDef
;
import
androidx.annotation.Nullable
;
import
androidx.annotation.Nullable
;
...
@@ -516,51 +517,94 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
...
@@ -516,51 +517,94 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
return
Pair
.
create
(
new
TrackGroupArray
(
trackGroups
),
trackGroupInfos
);
return
Pair
.
create
(
new
TrackGroupArray
(
trackGroups
),
trackGroupInfos
);
}
}
/**
* Groups adaptation sets. Two adaptations sets belong to the same group if either:
*
* <ul>
* <li>One is a trick-play adaptation set and uses a {@code
* http://dashif.org/guidelines/trickmode} essential or supplemental property to indicate
* that the other is the main adaptation set to which it corresponds.
* <li>The two adaptation sets are marked as safe for switching using {@code
* urn:mpeg:dash:adaptation-set-switching:2016} supplemental properties.
* </ul>
*
* @param adaptationSets The adaptation sets to merge.
* @return An array of groups, where each group is an array of adaptation set indices.
*/
private
static
int
[][]
getGroupedAdaptationSetIndices
(
List
<
AdaptationSet
>
adaptationSets
)
{
private
static
int
[][]
getGroupedAdaptationSetIndices
(
List
<
AdaptationSet
>
adaptationSets
)
{
int
adaptationSetCount
=
adaptationSets
.
size
();
int
adaptationSetCount
=
adaptationSets
.
size
();
SparseIntArray
idToIndexMap
=
new
SparseIntArray
(
adaptationSetCount
);
SparseIntArray
adaptationSetIdToIndex
=
new
SparseIntArray
(
adaptationSetCount
);
List
<
List
<
Integer
>>
adaptationSetGroupedIndices
=
new
ArrayList
<>(
adaptationSetCount
);
SparseArray
<
List
<
Integer
>>
adaptationSetIndexToGroupedIndices
=
new
SparseArray
<>(
adaptationSetCount
);
// Initially make each adaptation set belong to its own group. Also build the
// adaptationSetIdToIndex map.
for
(
int
i
=
0
;
i
<
adaptationSetCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
adaptationSetCount
;
i
++)
{
idToIndexMap
.
put
(
adaptationSets
.
get
(
i
).
id
,
i
);
adaptationSetIdToIndex
.
put
(
adaptationSets
.
get
(
i
).
id
,
i
);
List
<
Integer
>
initialGroup
=
new
ArrayList
<>();
initialGroup
.
add
(
i
);
adaptationSetGroupedIndices
.
add
(
initialGroup
);
adaptationSetIndexToGroupedIndices
.
put
(
i
,
initialGroup
);
}
}
int
[][]
groupedAdaptationSetIndices
=
new
int
[
adaptationSetCount
][];
// Merge adaptation set groups.
boolean
[]
adaptationSetUsedFlags
=
new
boolean
[
adaptationSetCount
];
int
groupCount
=
0
;
for
(
int
i
=
0
;
i
<
adaptationSetCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
adaptationSetCount
;
i
++)
{
if
(
adaptationSetUsedFlags
[
i
])
{
int
mergedGroupIndex
=
i
;
// This adaptation set has already been included in a group.
AdaptationSet
adaptationSet
=
adaptationSets
.
get
(
i
);
continue
;
// Trick-play adaptation sets are merged with their corresponding main adaptation sets.
@Nullable
Descriptor
trickPlayProperty
=
findTrickPlayProperty
(
adaptationSet
.
essentialProperties
);
if
(
trickPlayProperty
==
null
)
{
// Trick-play can also be specified using a supplemental property.
trickPlayProperty
=
findTrickPlayProperty
(
adaptationSet
.
supplementalProperties
);
}
if
(
trickPlayProperty
!=
null
)
{
int
mainAdaptationSetId
=
Integer
.
parseInt
(
trickPlayProperty
.
value
);
int
mainAdaptationSetIndex
=
adaptationSetIdToIndex
.
get
(
mainAdaptationSetId
,
/* valueIfKeyNotFound= */
-
1
);
if
(
mainAdaptationSetIndex
!=
-
1
)
{
mergedGroupIndex
=
mainAdaptationSetIndex
;
}
}
}
adaptationSetUsedFlags
[
i
]
=
true
;
// Adaptation sets that are safe for switching are merged, using the smallest index for the
// merged group.
if
(
mergedGroupIndex
==
i
)
{
@Nullable
@Nullable
Descriptor
adaptationSetSwitchingProperty
=
Descriptor
adaptationSetSwitchingProperty
=
findAdaptationSetSwitchingProperty
(
adaptationSets
.
get
(
i
).
supplementalProperties
);
findAdaptationSetSwitchingProperty
(
adaptationSet
.
supplementalProperties
);
if
(
adaptationSetSwitchingProperty
==
null
)
{
if
(
adaptationSetSwitchingProperty
!=
null
)
{
groupedAdaptationSetIndices
[
groupCount
++]
=
new
int
[]
{
i
};
String
[]
otherAdaptationSetIds
=
Util
.
split
(
adaptationSetSwitchingProperty
.
value
,
","
);
}
else
{
for
(
String
adaptationSetId
:
otherAdaptationSetIds
)
{
String
[]
extraAdaptationSetIds
=
Util
.
split
(
adaptationSetSwitchingProperty
.
value
,
","
);
int
otherAdaptationSetId
=
int
[]
adaptationSetIndices
=
new
int
[
1
+
extraAdaptationSetIds
.
length
];
adaptationSetIdToIndex
.
get
(
adaptationSetIndices
[
0
]
=
i
;
Integer
.
parseInt
(
adaptationSetId
),
/* valueIfKeyNotFound= */
-
1
);
int
outputIndex
=
1
;
if
(
otherAdaptationSetId
!=
-
1
)
{
for
(
String
adaptationSetId
:
extraAdaptationSetIds
)
{
mergedGroupIndex
=
Math
.
min
(
mergedGroupIndex
,
otherAdaptationSetId
);
int
extraIndex
=
}
idToIndexMap
.
get
(
Integer
.
parseInt
(
adaptationSetId
),
/* valueIfKeyNotFound= */
-
1
);
if
(
extraIndex
!=
-
1
)
{
adaptationSetUsedFlags
[
extraIndex
]
=
true
;
adaptationSetIndices
[
outputIndex
]
=
extraIndex
;
outputIndex
++;
}
}
}
}
if
(
outputIndex
<
adaptationSetIndices
.
length
)
{
adaptationSetIndices
=
Arrays
.
copyOf
(
adaptationSetIndices
,
outputIndex
);
}
}
groupedAdaptationSetIndices
[
groupCount
++]
=
adaptationSetIndices
;
// Merge the groups if necessary.
if
(
mergedGroupIndex
!=
i
)
{
List
<
Integer
>
thisGroup
=
adaptationSetIndexToGroupedIndices
.
get
(
i
);
List
<
Integer
>
mergedGroup
=
adaptationSetIndexToGroupedIndices
.
get
(
mergedGroupIndex
);
mergedGroup
.
addAll
(
thisGroup
);
adaptationSetIndexToGroupedIndices
.
put
(
i
,
mergedGroup
);
adaptationSetGroupedIndices
.
remove
(
thisGroup
);
}
}
}
}
return
groupCount
<
adaptationSetCount
int
[][]
groupedAdaptationSetIndices
=
new
int
[
adaptationSetGroupedIndices
.
size
()][];
?
Arrays
.
copyOf
(
groupedAdaptationSetIndices
,
groupCount
)
:
groupedAdaptationSetIndices
;
for
(
int
i
=
0
;
i
<
groupedAdaptationSetIndices
.
length
;
i
++)
{
groupedAdaptationSetIndices
[
i
]
=
Util
.
toArray
(
adaptationSetGroupedIndices
.
get
(
i
));
// Restore the original adaptation set order within each group.
Arrays
.
sort
(
groupedAdaptationSetIndices
[
i
]);
}
return
groupedAdaptationSetIndices
;
}
}
/**
/**
...
@@ -747,9 +791,19 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
...
@@ -747,9 +791,19 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
@Nullable
@Nullable
private
static
Descriptor
findAdaptationSetSwitchingProperty
(
List
<
Descriptor
>
descriptors
)
{
private
static
Descriptor
findAdaptationSetSwitchingProperty
(
List
<
Descriptor
>
descriptors
)
{
return
findDescriptor
(
descriptors
,
"urn:mpeg:dash:adaptation-set-switching:2016"
);
}
@Nullable
private
static
Descriptor
findTrickPlayProperty
(
List
<
Descriptor
>
descriptors
)
{
return
findDescriptor
(
descriptors
,
"http://dashif.org/guidelines/trickmode"
);
}
@Nullable
private
static
Descriptor
findDescriptor
(
List
<
Descriptor
>
descriptors
,
String
schemeIdUri
)
{
for
(
int
i
=
0
;
i
<
descriptors
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
descriptors
.
size
();
i
++)
{
Descriptor
descriptor
=
descriptors
.
get
(
i
);
Descriptor
descriptor
=
descriptors
.
get
(
i
);
if
(
"urn:mpeg:dash:adaptation-set-switching:2016"
.
equals
(
descriptor
.
schemeIdUri
))
{
if
(
schemeIdUri
.
equals
(
descriptor
.
schemeIdUri
))
{
return
descriptor
;
return
descriptor
;
}
}
}
}
...
...
library/dash/src/test/java/com/google/android/exoplayer2/source/dash/DashMediaPeriodTest.java
View file @
fea43767
...
@@ -26,6 +26,8 @@ import com.google.android.exoplayer2.drm.DrmSessionManager;
...
@@ -26,6 +26,8 @@ import com.google.android.exoplayer2.drm.DrmSessionManager;
import
com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory
;
import
com.google.android.exoplayer2.source.CompositeSequenceableLoaderFactory
;
import
com.google.android.exoplayer2.source.MediaSource.MediaPeriodId
;
import
com.google.android.exoplayer2.source.MediaSource.MediaPeriodId
;
import
com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher
;
import
com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher
;
import
com.google.android.exoplayer2.source.TrackGroup
;
import
com.google.android.exoplayer2.source.TrackGroupArray
;
import
com.google.android.exoplayer2.source.dash.PlayerEmsgHandler.PlayerEmsgCallback
;
import
com.google.android.exoplayer2.source.dash.PlayerEmsgHandler.PlayerEmsgCallback
;
import
com.google.android.exoplayer2.source.dash.manifest.AdaptationSet
;
import
com.google.android.exoplayer2.source.dash.manifest.AdaptationSet
;
import
com.google.android.exoplayer2.source.dash.manifest.DashManifest
;
import
com.google.android.exoplayer2.source.dash.manifest.DashManifest
;
...
@@ -35,7 +37,6 @@ import com.google.android.exoplayer2.source.dash.manifest.Representation;
...
@@ -35,7 +37,6 @@ import com.google.android.exoplayer2.source.dash.manifest.Representation;
import
com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase
;
import
com.google.android.exoplayer2.source.dash.manifest.SegmentBase.SingleSegmentBase
;
import
com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement
;
import
com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement
;
import
com.google.android.exoplayer2.testutil.MediaPeriodAsserts
;
import
com.google.android.exoplayer2.testutil.MediaPeriodAsserts
;
import
com.google.android.exoplayer2.testutil.MediaPeriodAsserts.FilterableManifestMediaPeriodFactory
;
import
com.google.android.exoplayer2.upstream.Allocator
;
import
com.google.android.exoplayer2.upstream.Allocator
;
import
com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy
;
import
com.google.android.exoplayer2.upstream.LoadErrorHandlingPolicy
;
import
com.google.android.exoplayer2.upstream.LoaderErrorThrower
;
import
com.google.android.exoplayer2.upstream.LoaderErrorThrower
;
...
@@ -43,6 +44,7 @@ import com.google.android.exoplayer2.upstream.TransferListener;
...
@@ -43,6 +44,7 @@ import com.google.android.exoplayer2.upstream.TransferListener;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.List
;
import
org.junit.Test
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.junit.runner.RunWith
;
import
org.robolectric.annotation.LooperMode
;
import
org.robolectric.annotation.LooperMode
;
...
@@ -53,7 +55,7 @@ import org.robolectric.annotation.LooperMode;
...
@@ -53,7 +55,7 @@ import org.robolectric.annotation.LooperMode;
public
final
class
DashMediaPeriodTest
{
public
final
class
DashMediaPeriodTest
{
@Test
@Test
public
void
getSteamKeys_isCompatibleWithDashManifestFilter
()
{
public
void
getSt
r
eamKeys_isCompatibleWithDashManifestFilter
()
{
// Test manifest which covers various edge cases:
// Test manifest which covers various edge cases:
// - Multiple periods.
// - Multiple periods.
// - Single and multiple representations per adaptation set.
// - Single and multiple representations per adaptation set.
...
@@ -61,57 +63,201 @@ public final class DashMediaPeriodTest {
...
@@ -61,57 +63,201 @@ public final class DashMediaPeriodTest {
// - Embedded track groups.
// - Embedded track groups.
// All cases are deliberately combined in one test to catch potential indexing problems which
// All cases are deliberately combined in one test to catch potential indexing problems which
// only occur in combination.
// only occur in combination.
DashManifest
testM
anifest
=
DashManifest
m
anifest
=
createDashManifest
(
createDashManifest
(
createPeriod
(
createPeriod
(
createAdaptationSet
(
createAdaptationSet
(
/* id= */
0
,
/* id= */
0
,
/* trackType= */
C
.
TRACK_TYPE_VIDEO
,
C
.
TRACK_TYPE_VIDEO
,
/* descriptor= */
null
,
/* descriptor= */
null
,
createVideoRepresentation
(
/* bitrate= */
1000000
))),
createVideoRepresentation
(
/* bitrate= */
1000000
))),
createPeriod
(
createPeriod
(
createAdaptationSet
(
createAdaptationSet
(
/* id= */
100
,
/* id= */
100
,
/* trackType= */
C
.
TRACK_TYPE_VIDEO
,
C
.
TRACK_TYPE_VIDEO
,
/* descriptor= */
createSwitchDescriptor
(
/* ids...= */
103
,
104
),
createSwitchDescriptor
(
/* ids...= */
103
,
104
),
createVideoRepresentationWithInbandEventStream
(
/* bitrate= */
200000
),
createVideoRepresentationWithInbandEventStream
(
/* bitrate= */
200000
),
createVideoRepresentationWithInbandEventStream
(
/* bitrate= */
400000
),
createVideoRepresentationWithInbandEventStream
(
/* bitrate= */
400000
),
createVideoRepresentationWithInbandEventStream
(
/* bitrate= */
600000
)),
createVideoRepresentationWithInbandEventStream
(
/* bitrate= */
600000
)),
createAdaptationSet
(
createAdaptationSet
(
/* id= */
101
,
/* id= */
101
,
/* trackType= */
C
.
TRACK_TYPE_AUDIO
,
C
.
TRACK_TYPE_AUDIO
,
/* descriptor= */
createSwitchDescriptor
(
/* ids...= */
102
),
createSwitchDescriptor
(
/* ids...= */
102
),
createAudioRepresentation
(
/* bitrate= */
48000
),
createAudioRepresentation
(
/* bitrate= */
48000
),
createAudioRepresentation
(
/* bitrate= */
96000
)),
createAudioRepresentation
(
/* bitrate= */
96000
)),
createAdaptationSet
(
createAdaptationSet
(
/* id= */
102
,
/* id= */
102
,
/* trackType= */
C
.
TRACK_TYPE_AUDIO
,
C
.
TRACK_TYPE_AUDIO
,
/* descriptor= */
createSwitchDescriptor
(
/* ids...= */
101
),
createSwitchDescriptor
(
/* ids...= */
101
),
createAudioRepresentation
(
/* bitrate= */
256000
)),
createAudioRepresentation
(
/* bitrate= */
256000
)),
createAdaptationSet
(
createAdaptationSet
(
/* id= */
103
,
/* id= */
103
,
/* trackType= */
C
.
TRACK_TYPE_VIDEO
,
C
.
TRACK_TYPE_VIDEO
,
/* descriptor= */
createSwitchDescriptor
(
/* ids...= */
100
,
104
),
createSwitchDescriptor
(
/* ids...= */
100
,
104
),
createVideoRepresentationWithInbandEventStream
(
/* bitrate= */
800000
),
createVideoRepresentationWithInbandEventStream
(
/* bitrate= */
800000
),
createVideoRepresentationWithInbandEventStream
(
/* bitrate= */
1000000
)),
createVideoRepresentationWithInbandEventStream
(
/* bitrate= */
1000000
)),
createAdaptationSet
(
createAdaptationSet
(
/* id= */
104
,
/* id= */
104
,
/* trackType= */
C
.
TRACK_TYPE_VIDEO
,
C
.
TRACK_TYPE_VIDEO
,
/* descriptor= */
createSwitchDescriptor
(
/* ids...= */
100
,
103
),
createSwitchDescriptor
(
/* ids...= */
100
,
103
),
createVideoRepresentationWithInbandEventStream
(
/* bitrate= */
2000000
)),
createVideoRepresentationWithInbandEventStream
(
/* bitrate= */
2000000
)),
createAdaptationSet
(
createAdaptationSet
(
/* id= */
105
,
/* id= */
105
,
/* trackType= */
C
.
TRACK_TYPE_TEXT
,
C
.
TRACK_TYPE_TEXT
,
/* descriptor= */
null
,
/* descriptor= */
null
,
createTextRepresentation
(
/* language= */
"eng"
)),
createTextRepresentation
(
/* language= */
"eng"
)),
createAdaptationSet
(
createAdaptationSet
(
/* id= */
105
,
/* id= */
105
,
/* trackType= */
C
.
TRACK_TYPE_TEXT
,
C
.
TRACK_TYPE_TEXT
,
/* descriptor= */
null
,
/* descriptor= */
null
,
createTextRepresentation
(
/* language= */
"ger"
))));
createTextRepresentation
(
/* language= */
"ger"
))));
FilterableManifestMediaPeriodFactory
<
DashManifest
>
mediaPeriodFactory
=
(
manifest
,
periodIndex
)
->
// Ignore embedded metadata as we don't want to select primary group just to get embedded track.
new
DashMediaPeriod
(
MediaPeriodAsserts
.
assertGetStreamKeysAndManifestFilterIntegration
(
DashMediaPeriodTest:
:
createDashMediaPeriod
,
manifest
,
/* periodIndex= */
1
,
/* ignoredMimeType= */
"application/x-emsg"
);
}
@Test
public
void
adaptationSetSwitchingProperty_mergesTrackGroups
()
{
DashManifest
manifest
=
createDashManifest
(
createPeriod
(
createAdaptationSet
(
/* id= */
0
,
C
.
TRACK_TYPE_VIDEO
,
createSwitchDescriptor
(
/* ids...= */
1
,
2
),
createVideoRepresentation
(
/* bitrate= */
0
),
createVideoRepresentation
(
/* bitrate= */
1
)),
createAdaptationSet
(
/* id= */
3
,
C
.
TRACK_TYPE_VIDEO
,
/* descriptor= */
null
,
createVideoRepresentation
(
/* bitrate= */
300
)),
createAdaptationSet
(
/* id= */
2
,
C
.
TRACK_TYPE_VIDEO
,
createSwitchDescriptor
(
/* ids...= */
0
,
1
),
createVideoRepresentation
(
/* bitrate= */
200
),
createVideoRepresentation
(
/* bitrate= */
201
)),
createAdaptationSet
(
/* id= */
1
,
C
.
TRACK_TYPE_VIDEO
,
createSwitchDescriptor
(
/* ids...= */
0
,
2
),
createVideoRepresentation
(
/* bitrate= */
100
))));
DashMediaPeriod
dashMediaPeriod
=
createDashMediaPeriod
(
manifest
,
0
);
List
<
AdaptationSet
>
adaptationSets
=
manifest
.
getPeriod
(
0
).
adaptationSets
;
// We expect the three adaptation sets with the switch descriptor to be merged, retaining the
// representations in their original order.
TrackGroupArray
expectedTrackGroups
=
new
TrackGroupArray
(
new
TrackGroup
(
adaptationSets
.
get
(
0
).
representations
.
get
(
0
).
format
,
adaptationSets
.
get
(
0
).
representations
.
get
(
1
).
format
,
adaptationSets
.
get
(
2
).
representations
.
get
(
0
).
format
,
adaptationSets
.
get
(
2
).
representations
.
get
(
1
).
format
,
adaptationSets
.
get
(
3
).
representations
.
get
(
0
).
format
),
new
TrackGroup
(
adaptationSets
.
get
(
1
).
representations
.
get
(
0
).
format
));
MediaPeriodAsserts
.
assertTrackGroups
(
dashMediaPeriod
,
expectedTrackGroups
);
}
@Test
public
void
trickPlayProperty_mergesTrackGroups
()
{
DashManifest
manifest
=
createDashManifest
(
createPeriod
(
createAdaptationSet
(
/* id= */
0
,
C
.
TRACK_TYPE_VIDEO
,
createTrickPlayDescriptor
(
/* mainAdaptationSetId= */
1
),
createVideoRepresentation
(
/* bitrate= */
0
),
createVideoRepresentation
(
/* bitrate= */
1
)),
createAdaptationSet
(
/* id= */
1
,
C
.
TRACK_TYPE_VIDEO
,
/* descriptor= */
null
,
createVideoRepresentation
(
/* bitrate= */
100
)),
createAdaptationSet
(
/* id= */
2
,
C
.
TRACK_TYPE_VIDEO
,
/* descriptor= */
null
,
createVideoRepresentation
(
/* bitrate= */
200
),
createVideoRepresentation
(
/* bitrate= */
201
)),
createAdaptationSet
(
/* id= */
3
,
C
.
TRACK_TYPE_VIDEO
,
createTrickPlayDescriptor
(
/* mainAdaptationSetId= */
2
),
createVideoRepresentation
(
/* bitrate= */
300
))));
DashMediaPeriod
dashMediaPeriod
=
createDashMediaPeriod
(
manifest
,
0
);
List
<
AdaptationSet
>
adaptationSets
=
manifest
.
getPeriod
(
0
).
adaptationSets
;
// We expect the trick play adaptation sets to be merged with the ones to which they refer,
// retaining representations in their original order.
TrackGroupArray
expectedTrackGroups
=
new
TrackGroupArray
(
new
TrackGroup
(
adaptationSets
.
get
(
0
).
representations
.
get
(
0
).
format
,
adaptationSets
.
get
(
0
).
representations
.
get
(
1
).
format
,
adaptationSets
.
get
(
1
).
representations
.
get
(
0
).
format
),
new
TrackGroup
(
adaptationSets
.
get
(
2
).
representations
.
get
(
0
).
format
,
adaptationSets
.
get
(
2
).
representations
.
get
(
1
).
format
,
adaptationSets
.
get
(
3
).
representations
.
get
(
0
).
format
));
MediaPeriodAsserts
.
assertTrackGroups
(
dashMediaPeriod
,
expectedTrackGroups
);
}
@Test
public
void
adaptationSetSwitchingProperty_andTrickPlayProperty_mergesTrackGroups
()
{
DashManifest
manifest
=
createDashManifest
(
createPeriod
(
createAdaptationSet
(
/* id= */
0
,
C
.
TRACK_TYPE_VIDEO
,
createTrickPlayDescriptor
(
/* mainAdaptationSetId= */
1
),
createVideoRepresentation
(
/* bitrate= */
0
),
createVideoRepresentation
(
/* bitrate= */
1
)),
createAdaptationSet
(
/* id= */
1
,
C
.
TRACK_TYPE_VIDEO
,
createSwitchDescriptor
(
/* ids...= */
2
),
createVideoRepresentation
(
/* bitrate= */
100
)),
createAdaptationSet
(
/* id= */
2
,
C
.
TRACK_TYPE_VIDEO
,
createSwitchDescriptor
(
/* ids...= */
1
),
createVideoRepresentation
(
/* bitrate= */
200
),
createVideoRepresentation
(
/* bitrate= */
201
)),
createAdaptationSet
(
/* id= */
3
,
C
.
TRACK_TYPE_VIDEO
,
createTrickPlayDescriptor
(
/* mainAdaptationSetId= */
2
),
createVideoRepresentation
(
/* bitrate= */
300
))));
DashMediaPeriod
dashMediaPeriod
=
createDashMediaPeriod
(
manifest
,
0
);
List
<
AdaptationSet
>
adaptationSets
=
manifest
.
getPeriod
(
0
).
adaptationSets
;
// We expect all adaptation sets to be merged into one group, retaining representations in their
// original order.
TrackGroupArray
expectedTrackGroups
=
new
TrackGroupArray
(
new
TrackGroup
(
adaptationSets
.
get
(
0
).
representations
.
get
(
0
).
format
,
adaptationSets
.
get
(
0
).
representations
.
get
(
1
).
format
,
adaptationSets
.
get
(
1
).
representations
.
get
(
0
).
format
,
adaptationSets
.
get
(
2
).
representations
.
get
(
0
).
format
,
adaptationSets
.
get
(
2
).
representations
.
get
(
1
).
format
,
adaptationSets
.
get
(
3
).
representations
.
get
(
0
).
format
));
MediaPeriodAsserts
.
assertTrackGroups
(
dashMediaPeriod
,
expectedTrackGroups
);
}
private
static
DashMediaPeriod
createDashMediaPeriod
(
DashManifest
manifest
,
int
periodIndex
)
{
return
new
DashMediaPeriod
(
/* id= */
periodIndex
,
/* id= */
periodIndex
,
manifest
,
manifest
,
periodIndex
,
periodIndex
,
...
@@ -129,13 +275,6 @@ public final class DashMediaPeriodTest {
...
@@ -129,13 +275,6 @@ public final class DashMediaPeriodTest {
mock
(
Allocator
.
class
),
mock
(
Allocator
.
class
),
mock
(
CompositeSequenceableLoaderFactory
.
class
),
mock
(
CompositeSequenceableLoaderFactory
.
class
),
mock
(
PlayerEmsgCallback
.
class
));
mock
(
PlayerEmsgCallback
.
class
));
// Ignore embedded metadata as we don't want to select primary group just to get embedded track.
MediaPeriodAsserts
.
assertGetStreamKeysAndManifestFilterIntegration
(
mediaPeriodFactory
,
testManifest
,
/* periodIndex= */
1
,
/* ignoredMimeType= */
"application/x-emsg"
);
}
}
private
static
DashManifest
createDashManifest
(
Period
...
periods
)
{
private
static
DashManifest
createDashManifest
(
Period
...
periods
)
{
...
@@ -228,6 +367,13 @@ public final class DashMediaPeriodTest {
...
@@ -228,6 +367,13 @@ public final class DashMediaPeriodTest {
/* id= */
null
);
/* id= */
null
);
}
}
private
static
Descriptor
createTrickPlayDescriptor
(
int
mainAdaptationSetId
)
{
return
new
Descriptor
(
/* schemeIdUri= */
"http://dashif.org/guidelines/trickmode"
,
/* value= */
Integer
.
toString
(
mainAdaptationSetId
),
/* id= */
null
);
}
private
static
Descriptor
getInbandEventDescriptor
()
{
private
static
Descriptor
getInbandEventDescriptor
()
{
return
new
Descriptor
(
return
new
Descriptor
(
/* schemeIdUri= */
"inBandSchemeIdUri"
,
/* value= */
"inBandValue"
,
/* id= */
"inBandId"
);
/* schemeIdUri= */
"inBandSchemeIdUri"
,
/* value= */
"inBandValue"
,
/* id= */
"inBandId"
);
...
...
testutils/src/main/java/com/google/android/exoplayer2/testutil/MediaPeriodAsserts.java
View file @
fea43767
...
@@ -55,6 +55,17 @@ public final class MediaPeriodAsserts {
...
@@ -55,6 +55,17 @@ public final class MediaPeriodAsserts {
private
MediaPeriodAsserts
()
{}
private
MediaPeriodAsserts
()
{}
/**
/**
* Prepares the {@link MediaPeriod} and asserts that it provides the specified track groups.
*
* @param mediaPeriod The {@link MediaPeriod} to test.
* @param expectedGroups The expected track groups.
*/
public
static
void
assertTrackGroups
(
MediaPeriod
mediaPeriod
,
TrackGroupArray
expectedGroups
)
{
TrackGroupArray
actualGroups
=
prepareAndGetTrackGroups
(
mediaPeriod
);
assertThat
(
actualGroups
).
isEqualTo
(
expectedGroups
);
}
/**
* Asserts that the values returns by {@link MediaPeriod#getStreamKeys(List)} are compatible with
* Asserts that the values returns by {@link MediaPeriod#getStreamKeys(List)} are compatible with
* a {@link FilterableManifest} using these stream keys.
* a {@link FilterableManifest} using these stream keys.
*
*
...
@@ -85,7 +96,7 @@ public final class MediaPeriodAsserts {
...
@@ -85,7 +96,7 @@ public final class MediaPeriodAsserts {
int
periodIndex
,
int
periodIndex
,
@Nullable
String
ignoredMimeType
)
{
@Nullable
String
ignoredMimeType
)
{
MediaPeriod
mediaPeriod
=
mediaPeriodFactory
.
createMediaPeriod
(
manifest
,
periodIndex
);
MediaPeriod
mediaPeriod
=
mediaPeriodFactory
.
createMediaPeriod
(
manifest
,
periodIndex
);
TrackGroupArray
trackGroupArray
=
g
etTrackGroups
(
mediaPeriod
);
TrackGroupArray
trackGroupArray
=
prepareAndG
etTrackGroups
(
mediaPeriod
);
// Create test vector of query test selections:
// Create test vector of query test selections:
// - One selection with one track per group, two tracks or all tracks.
// - One selection with one track per group, two tracks or all tracks.
...
@@ -146,7 +157,7 @@ public final class MediaPeriodAsserts {
...
@@ -146,7 +157,7 @@ public final class MediaPeriodAsserts {
// The filtered manifest should only have one period left.
// The filtered manifest should only have one period left.
MediaPeriod
filteredMediaPeriod
=
MediaPeriod
filteredMediaPeriod
=
mediaPeriodFactory
.
createMediaPeriod
(
filteredManifest
,
/* periodIndex= */
0
);
mediaPeriodFactory
.
createMediaPeriod
(
filteredManifest
,
/* periodIndex= */
0
);
TrackGroupArray
filteredTrackGroupArray
=
g
etTrackGroups
(
filteredMediaPeriod
);
TrackGroupArray
filteredTrackGroupArray
=
prepareAndG
etTrackGroups
(
filteredMediaPeriod
);
for
(
TrackSelection
trackSelection
:
testSelection
)
{
for
(
TrackSelection
trackSelection
:
testSelection
)
{
if
(
ignoredMimeType
!=
null
if
(
ignoredMimeType
!=
null
&&
ignoredMimeType
.
equals
(
trackSelection
.
getFormat
(
0
).
sampleMimeType
))
{
&&
ignoredMimeType
.
equals
(
trackSelection
.
getFormat
(
0
).
sampleMimeType
))
{
...
@@ -186,7 +197,7 @@ public final class MediaPeriodAsserts {
...
@@ -186,7 +197,7 @@ public final class MediaPeriodAsserts {
return
true
;
return
true
;
}
}
private
static
TrackGroupArray
g
etTrackGroups
(
MediaPeriod
mediaPeriod
)
{
private
static
TrackGroupArray
prepareAndG
etTrackGroups
(
MediaPeriod
mediaPeriod
)
{
AtomicReference
<
TrackGroupArray
>
trackGroupArray
=
new
AtomicReference
<>();
AtomicReference
<
TrackGroupArray
>
trackGroupArray
=
new
AtomicReference
<>();
DummyMainThread
dummyMainThread
=
new
DummyMainThread
();
DummyMainThread
dummyMainThread
=
new
DummyMainThread
();
ConditionVariable
preparedCondition
=
new
ConditionVariable
();
ConditionVariable
preparedCondition
=
new
ConditionVariable
();
...
...
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