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
ab6f9aea
authored
Oct 20, 2017
by
ojw28
Committed by
GitHub
Oct 20, 2017
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #3381 from google/dev-v2-r2.5.4
r2.5.4
parents
1fdc11f2
04862bcc
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
23 changed files
with
290 additions
and
114 deletions
RELEASENOTES.md
constants.gradle
demo/src/main/AndroidManifest.xml
demo/src/main/assets/media.exolist.json
library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/AtomParsersTest.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaCrypto.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Track.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java
library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java
library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java
testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java
RELEASENOTES.md
View file @
ab6f9aea
# Release notes #
### r2.5.4 ###
*
Remove unnecessary media playlist fetches during playback of live HLS streams.
*
Add the ability to inject a HLS playlist parser through
`HlsMediaSource`
.
*
Fix potential
`IndexOutOfBoundsException`
when using
`ImaMediaSource`
(
[
#3334
](
https://github.com/google/ExoPlayer/issues/3334
)
).
*
Fix an issue parsing MP4 content containing non-CENC sinf boxes.
*
Fix memory leak when seeking with repeated periods.
*
Fix playback position when
`ExoPlayer.prepare`
is called with
`resetPosition`
set to false.
*
Ignore MP4 edit lists that seem invalid
(
[
#3351
](
https://github.com/google/ExoPlayer/issues/3351
)
).
*
Add extractor flag for ignoring all MP4 edit lists
(
[
#3358
](
https://github.com/google/ExoPlayer/issues/3358
)
).
*
Improve extensibility by exposing public constructors for
`FrameworkMediaCrypto`
and by making
`DefaultDashChunkSource.getNextChunk`
non-final.
### r2.5.3 ###
*
IMA extension: Support skipping of skippable ads on AndroidTV and other
...
...
constants.gradle
View file @
ab6f9aea
...
...
@@ -24,7 +24,7 @@ project.ext {
supportLibraryVersion
=
'25.4.0'
dexmakerVersion
=
'1.2'
mockitoVersion
=
'1.9.5'
releaseVersion
=
'r2.5.
3
'
releaseVersion
=
'r2.5.
4
'
modulePrefix
=
':'
if
(
gradle
.
ext
.
has
(
'exoplayerModulePrefix'
))
{
modulePrefix
+=
gradle
.
ext
.
exoplayerModulePrefix
...
...
demo/src/main/AndroidManifest.xml
View file @
ab6f9aea
...
...
@@ -16,8 +16,8 @@
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
package=
"com.google.android.exoplayer2.demo"
android:versionCode=
"250
3
"
android:versionName=
"2.5.
3
"
>
android:versionCode=
"250
4
"
android:versionName=
"2.5.
4
"
>
<uses-permission
android:name=
"android.permission.INTERNET"
/>
<uses-permission
android:name=
"android.permission.READ_EXTERNAL_STORAGE"
/>
...
...
demo/src/main/assets/media.exolist.json
View file @
ab6f9aea
...
...
@@ -344,11 +344,11 @@
"samples"
:
[
{
"name"
:
"Apple 4x3 basic stream"
,
"uri"
:
"https://dev
images.apple.com.edgekey.net
/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8"
"uri"
:
"https://dev
streaming-cdn.apple.com/videos
/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8"
},
{
"name"
:
"Apple 16x9 basic stream"
,
"uri"
:
"https://dev
images.apple.com.edgekey.net
/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
"uri"
:
"https://dev
streaming-cdn.apple.com/videos
/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8"
},
{
"name"
:
"Apple master playlist advanced (TS)"
,
...
...
@@ -360,11 +360,11 @@
},
{
"name"
:
"Apple TS media playlist"
,
"uri"
:
"https://dev
images.apple.com.edgekey.net
/streaming/examples/bipbop_4x3/gear1/prog_index.m3u8"
"uri"
:
"https://dev
streaming-cdn.apple.com/videos
/streaming/examples/bipbop_4x3/gear1/prog_index.m3u8"
},
{
"name"
:
"Apple AAC media playlist"
,
"uri"
:
"https://dev
images.apple.com.edgekey.net
/streaming/examples/bipbop_4x3/gear0/prog_index.m3u8"
"uri"
:
"https://dev
streaming-cdn.apple.com/videos
/streaming/examples/bipbop_4x3/gear0/prog_index.m3u8"
},
{
"name"
:
"Apple ID3 metadata"
,
...
...
@@ -381,11 +381,11 @@
},
{
"name"
:
"Apple AAC 10s"
,
"uri"
:
"https://dev
images.apple.com.edgekey.net
/streaming/examples/bipbop_4x3/gear0/fileSequence0.aac"
"uri"
:
"https://dev
streaming-cdn.apple.com/videos
/streaming/examples/bipbop_4x3/gear0/fileSequence0.aac"
},
{
"name"
:
"Apple TS 10s"
,
"uri"
:
"https://dev
images.apple.com.edgekey.net
/streaming/examples/bipbop_4x3/gear1/fileSequence0.ts"
"uri"
:
"https://dev
streaming-cdn.apple.com/videos
/streaming/examples/bipbop_4x3/gear1/fileSequence0.ts"
},
{
"name"
:
"Android screens (Matroska)"
,
...
...
library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/mp4/AtomParsersTest.java
View file @
ab6f9aea
...
...
@@ -34,18 +34,18 @@ public final class AtomParsersTest extends TestCase {
+
SAMPLE_COUNT
+
"0001000200030004"
);
public
void
testStz2Parsing4BitFieldSize
()
{
verifyParsing
(
new
Atom
.
LeafAtom
(
Atom
.
TYPE_stsz
,
new
ParsableByteArray
(
FOUR_BIT_STZ2
)));
verify
Stz2
Parsing
(
new
Atom
.
LeafAtom
(
Atom
.
TYPE_stsz
,
new
ParsableByteArray
(
FOUR_BIT_STZ2
)));
}
public
void
testStz2Parsing8BitFieldSize
()
{
verifyParsing
(
new
Atom
.
LeafAtom
(
Atom
.
TYPE_stsz
,
new
ParsableByteArray
(
EIGHT_BIT_STZ2
)));
verify
Stz2
Parsing
(
new
Atom
.
LeafAtom
(
Atom
.
TYPE_stsz
,
new
ParsableByteArray
(
EIGHT_BIT_STZ2
)));
}
public
void
testStz2Parsing16BitFieldSize
()
{
verifyParsing
(
new
Atom
.
LeafAtom
(
Atom
.
TYPE_stsz
,
new
ParsableByteArray
(
SIXTEEN_BIT_STZ2
)));
verify
Stz2
Parsing
(
new
Atom
.
LeafAtom
(
Atom
.
TYPE_stsz
,
new
ParsableByteArray
(
SIXTEEN_BIT_STZ2
)));
}
private
void
verify
Parsing
(
Atom
.
LeafAtom
stz2Atom
)
{
private
static
void
verifyStz2
Parsing
(
Atom
.
LeafAtom
stz2Atom
)
{
AtomParsers
.
Stz2SampleSizeBox
box
=
new
AtomParsers
.
Stz2SampleSizeBox
(
stz2Atom
);
assertEquals
(
4
,
box
.
getSampleCount
());
assertFalse
(
box
.
isFixedSampleSize
());
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
View file @
ab6f9aea
...
...
@@ -356,17 +356,17 @@ import java.util.concurrent.CopyOnWriteArraySet;
@Override
public
boolean
isPlayingAd
()
{
return
pendingSeekAcks
==
0
&&
playbackInfo
.
periodId
.
isAd
();
return
!
timeline
.
isEmpty
()
&&
pendingSeekAcks
==
0
&&
playbackInfo
.
periodId
.
isAd
();
}
@Override
public
int
getCurrentAdGroupIndex
()
{
return
pendingSeekAcks
==
0
?
playbackInfo
.
periodId
.
adGroupIndex
:
C
.
INDEX_UNSET
;
return
isPlayingAd
()
?
playbackInfo
.
periodId
.
adGroupIndex
:
C
.
INDEX_UNSET
;
}
@Override
public
int
getCurrentAdIndexInAdGroup
()
{
return
pendingSeekAcks
==
0
?
playbackInfo
.
periodId
.
adIndexInAdGroup
:
C
.
INDEX_UNSET
;
return
isPlayingAd
()
?
playbackInfo
.
periodId
.
adIndexInAdGroup
:
C
.
INDEX_UNSET
;
}
@Override
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
View file @
ab6f9aea
...
...
@@ -430,6 +430,10 @@ import java.io.IOException;
loadControl
.
onPrepared
();
if
(
resetPosition
)
{
playbackInfo
=
new
PlaybackInfo
(
0
,
C
.
TIME_UNSET
);
}
else
{
// The new start position is the current playback position.
playbackInfo
=
new
PlaybackInfo
(
playbackInfo
.
periodId
,
playbackInfo
.
positionUs
,
playbackInfo
.
contentPositionUs
);
}
this
.
mediaSource
=
mediaSource
;
mediaSource
.
prepareSource
(
player
,
true
,
this
);
...
...
@@ -719,7 +723,8 @@ import java.io.IOException;
// Clear the timeline, but keep the requested period if it is already prepared.
MediaPeriodHolder
periodHolder
=
playingPeriodHolder
;
while
(
periodHolder
!=
null
)
{
if
(
shouldKeepPeriodHolder
(
periodId
,
periodPositionUs
,
periodHolder
))
{
if
(
newPlayingPeriodHolder
==
null
&&
shouldKeepPeriodHolder
(
periodId
,
periodPositionUs
,
periodHolder
))
{
newPlayingPeriodHolder
=
periodHolder
;
}
else
{
periodHolder
.
release
();
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
View file @
ab6f9aea
...
...
@@ -31,13 +31,13 @@ public final class ExoPlayerLibraryInfo {
* The version of the library expressed as a string, for example "1.2.3".
*/
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
public
static
final
String
VERSION
=
"2.5.
3
"
;
public
static
final
String
VERSION
=
"2.5.
4
"
;
/**
* The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}.
*/
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public
static
final
String
VERSION_SLASHY
=
"ExoPlayerLib/2.5.
3
"
;
public
static
final
String
VERSION_SLASHY
=
"ExoPlayerLib/2.5.
4
"
;
/**
* The version of the library expressed as an integer, for example 1002003.
...
...
@@ -47,7 +47,7 @@ public final class ExoPlayerLibraryInfo {
* integer version 123045006 (123-045-006).
*/
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public
static
final
int
VERSION_INT
=
200500
3
;
public
static
final
int
VERSION_INT
=
200500
4
;
/**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
...
...
library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaCrypto.java
View file @
ab6f9aea
...
...
@@ -28,12 +28,29 @@ public final class FrameworkMediaCrypto implements ExoMediaCrypto {
private
final
MediaCrypto
mediaCrypto
;
private
final
boolean
forceAllowInsecureDecoderComponents
;
/* package */
FrameworkMediaCrypto
(
MediaCrypto
mediaCrypto
,
/**
* @param mediaCrypto The {@link MediaCrypto} to wrap.
*/
public
FrameworkMediaCrypto
(
MediaCrypto
mediaCrypto
)
{
this
(
mediaCrypto
,
false
);
}
/**
* @param mediaCrypto The {@link MediaCrypto} to wrap.
* @param forceAllowInsecureDecoderComponents Whether to force
* {@link #requiresSecureDecoderComponent(String)} to return {@code false}, rather than
* {@link MediaCrypto#requiresSecureDecoderComponent(String)} of the wrapped
* {@link MediaCrypto}.
*/
public
FrameworkMediaCrypto
(
MediaCrypto
mediaCrypto
,
boolean
forceAllowInsecureDecoderComponents
)
{
this
.
mediaCrypto
=
Assertions
.
checkNotNull
(
mediaCrypto
);
this
.
forceAllowInsecureDecoderComponents
=
forceAllowInsecureDecoderComponents
;
}
/**
* Returns the wrapped {@link MediaCrypto}.
*/
public
MediaCrypto
getWrappedMediaCrypto
()
{
return
mediaCrypto
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java
View file @
ab6f9aea
...
...
@@ -67,6 +67,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
}
private
@MatroskaExtractor
.
Flags
int
matroskaFlags
;
private
@Mp4Extractor
.
Flags
int
mp4Flags
;
private
@FragmentedMp4Extractor
.
Flags
int
fragmentedMp4Flags
;
private
@Mp3Extractor
.
Flags
int
mp3Flags
;
private
@TsExtractor
.
Mode
int
tsMode
;
...
...
@@ -90,6 +91,18 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
}
/**
* Sets flags for {@link Mp4Extractor} instances created by the factory.
*
* @see Mp4Extractor#Mp4Extractor(int)
* @param flags The flags to use.
* @return The factory, for convenience.
*/
public
synchronized
DefaultExtractorsFactory
setMp4ExtractorFlags
(
@Mp4Extractor
.
Flags
int
flags
)
{
this
.
mp4Flags
=
flags
;
return
this
;
}
/**
* Sets flags for {@link FragmentedMp4Extractor} instances created by the factory.
*
* @see FragmentedMp4Extractor#FragmentedMp4Extractor(int)
...
...
@@ -145,7 +158,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
Extractor
[]
extractors
=
new
Extractor
[
FLAC_EXTRACTOR_CONSTRUCTOR
==
null
?
11
:
12
];
extractors
[
0
]
=
new
MatroskaExtractor
(
matroskaFlags
);
extractors
[
1
]
=
new
FragmentedMp4Extractor
(
fragmentedMp4Flags
);
extractors
[
2
]
=
new
Mp4Extractor
();
extractors
[
2
]
=
new
Mp4Extractor
(
mp4Flags
);
extractors
[
3
]
=
new
Mp3Extractor
(
mp3Flags
);
extractors
[
4
]
=
new
AdtsExtractor
();
extractors
[
5
]
=
new
Ac3Extractor
();
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
View file @
ab6f9aea
...
...
@@ -60,11 +60,13 @@ import java.util.List;
* @param duration The duration in units of the timescale declared in the mvhd atom, or
* {@link C#TIME_UNSET} if the duration should be parsed from the tkhd atom.
* @param drmInitData {@link DrmInitData} to be included in the format.
* @param ignoreEditLists Whether to ignore any edit lists in the trak box.
* @param isQuickTime True for QuickTime media. False otherwise.
* @return A {@link Track} instance, or {@code null} if the track's type isn't supported.
*/
public
static
Track
parseTrak
(
Atom
.
ContainerAtom
trak
,
Atom
.
LeafAtom
mvhd
,
long
duration
,
DrmInitData
drmInitData
,
boolean
isQuickTime
)
throws
ParserException
{
DrmInitData
drmInitData
,
boolean
ignoreEditLists
,
boolean
isQuickTime
)
throws
ParserException
{
Atom
.
ContainerAtom
mdia
=
trak
.
getContainerAtomOfType
(
Atom
.
TYPE_mdia
);
int
trackType
=
parseHdlr
(
mdia
.
getLeafAtomOfType
(
Atom
.
TYPE_hdlr
).
data
);
if
(
trackType
==
C
.
TRACK_TYPE_UNKNOWN
)
{
...
...
@@ -88,11 +90,17 @@ import java.util.List;
Pair
<
Long
,
String
>
mdhdData
=
parseMdhd
(
mdia
.
getLeafAtomOfType
(
Atom
.
TYPE_mdhd
).
data
);
StsdData
stsdData
=
parseStsd
(
stbl
.
getLeafAtomOfType
(
Atom
.
TYPE_stsd
).
data
,
tkhdData
.
id
,
tkhdData
.
rotationDegrees
,
mdhdData
.
second
,
drmInitData
,
isQuickTime
);
Pair
<
long
[],
long
[]>
edtsData
=
parseEdts
(
trak
.
getContainerAtomOfType
(
Atom
.
TYPE_edts
));
long
[]
editListDurations
=
null
;
long
[]
editListMediaTimes
=
null
;
if
(!
ignoreEditLists
)
{
Pair
<
long
[],
long
[]>
edtsData
=
parseEdts
(
trak
.
getContainerAtomOfType
(
Atom
.
TYPE_edts
));
editListDurations
=
edtsData
.
first
;
editListMediaTimes
=
edtsData
.
second
;
}
return
stsdData
.
format
==
null
?
null
:
new
Track
(
tkhdData
.
id
,
trackType
,
mdhdData
.
first
,
movieTimescale
,
durationUs
,
stsdData
.
format
,
stsdData
.
requiredSampleTransformation
,
stsdData
.
trackEncryptionBoxes
,
stsdData
.
nalUnitLengthFieldLength
,
ed
tsData
.
first
,
edtsData
.
second
);
stsdData
.
nalUnitLengthFieldLength
,
ed
itListDurations
,
editListMediaTimes
);
}
/**
...
...
@@ -395,7 +403,11 @@ import java.util.List;
hasSyncSample
|=
(
editedFlags
[
i
]
&
C
.
BUFFER_FLAG_KEY_FRAME
)
!=
0
;
}
if
(!
hasSyncSample
)
{
throw
new
ParserException
(
"The edited sample sequence does not contain a sync sample."
);
// We don't support edit lists where the edited sample sequence doesn't contain a sync sample.
// Such edit lists are often (although not always) broken, so we ignore it and continue.
Log
.
w
(
TAG
,
"Ignoring edit list: Edited sample sequence does not contain a sync sample."
);
Util
.
scaleLargeTimestampsInPlace
(
timestamps
,
C
.
MICROS_PER_SECOND
,
track
.
timescale
);
return
new
TrackSampleTable
(
offsets
,
sizes
,
maximumSize
,
timestamps
,
flags
);
}
return
new
TrackSampleTable
(
editedOffsets
,
editedSizes
,
editedMaximumSize
,
editedTimestamps
,
...
...
@@ -779,7 +791,7 @@ import java.util.List;
*
* @param edtsAtom edts (edit box) atom to decode.
* @return Pair of edit list durations and edit list media times, or a pair of nulls if they are
* not present.
*
not present.
*/
private
static
Pair
<
long
[],
long
[]>
parseEdts
(
Atom
.
ContainerAtom
edtsAtom
)
{
Atom
.
LeafAtom
elst
;
...
...
@@ -1060,8 +1072,8 @@ import java.util.List;
Assertions
.
checkArgument
(
childAtomSize
>
0
,
"childAtomSize should be positive"
);
int
childAtomType
=
parent
.
readInt
();
if
(
childAtomType
==
Atom
.
TYPE_sinf
)
{
Pair
<
Integer
,
TrackEncryptionBox
>
result
=
parse
SinfFromParent
(
parent
,
childPosition
,
childAtomSize
);
Pair
<
Integer
,
TrackEncryptionBox
>
result
=
parse
CommonEncryptionSinfFromParent
(
parent
,
child
Position
,
child
AtomSize
);
if
(
result
!=
null
)
{
return
result
;
}
...
...
@@ -1071,8 +1083,8 @@ import java.util.List;
return
null
;
}
private
static
Pair
<
Integer
,
TrackEncryptionBox
>
parseSinfFromParent
(
ParsableByteArray
parent
,
int
position
,
int
size
)
{
/* package */
static
Pair
<
Integer
,
TrackEncryptionBox
>
parseCommonEncryptionSinfFromParent
(
ParsableByteArray
parent
,
int
position
,
int
size
)
{
int
childPosition
=
position
+
Atom
.
HEADER_SIZE
;
int
schemeInformationBoxPosition
=
C
.
POSITION_UNSET
;
int
schemeInformationBoxSize
=
0
;
...
...
@@ -1086,7 +1098,7 @@ import java.util.List;
dataFormat
=
parent
.
readInt
();
}
else
if
(
childAtomType
==
Atom
.
TYPE_schm
)
{
parent
.
skipBytes
(
4
);
//
scheme_type field. D
efined in ISO/IEC 23001-7:2016, section 4.1.
//
Common encryption scheme_type values are d
efined in ISO/IEC 23001-7:2016, section 4.1.
schemeType
=
parent
.
readString
(
4
);
}
else
if
(
childAtomType
==
Atom
.
TYPE_schi
)
{
schemeInformationBoxPosition
=
childPosition
;
...
...
@@ -1095,7 +1107,8 @@ import java.util.List;
childPosition
+=
childAtomSize
;
}
if
(
schemeType
!=
null
)
{
if
(
C
.
CENC_TYPE_cenc
.
equals
(
schemeType
)
||
C
.
CENC_TYPE_cbc1
.
equals
(
schemeType
)
||
C
.
CENC_TYPE_cens
.
equals
(
schemeType
)
||
C
.
CENC_TYPE_cbcs
.
equals
(
schemeType
))
{
Assertions
.
checkArgument
(
dataFormat
!=
null
,
"frma atom is mandatory"
);
Assertions
.
checkArgument
(
schemeInformationBoxPosition
!=
C
.
POSITION_UNSET
,
"schi atom is mandatory"
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
View file @
ab6f9aea
...
...
@@ -74,7 +74,7 @@ public final class FragmentedMp4Extractor implements Extractor {
@Retention
(
RetentionPolicy
.
SOURCE
)
@IntDef
(
flag
=
true
,
value
=
{
FLAG_WORKAROUND_EVERY_VIDEO_FRAME_IS_SYNC_FRAME
,
FLAG_WORKAROUND_IGNORE_TFDT_BOX
,
FLAG_ENABLE_EMSG_TRACK
,
FLAG_ENABLE_CEA608_TRACK
,
FLAG_SIDELOADED
})
FLAG_SIDELOADED
,
FLAG_WORKAROUND_IGNORE_EDIT_LISTS
})
public
@interface
Flags
{}
/**
* Flag to work around an issue in some video streams where every frame is marked as a sync frame.
...
...
@@ -103,6 +103,10 @@ public final class FragmentedMp4Extractor implements Extractor {
* container.
*/
private
static
final
int
FLAG_SIDELOADED
=
16
;
/**
* Flag to ignore any edit lists in the stream.
*/
public
static
final
int
FLAG_WORKAROUND_IGNORE_EDIT_LISTS
=
32
;
private
static
final
String
TAG
=
"FragmentedMp4Extractor"
;
private
static
final
int
SAMPLE_GROUP_TYPE_seig
=
Util
.
getIntegerCodeForString
(
"seig"
);
...
...
@@ -426,7 +430,7 @@ public final class FragmentedMp4Extractor implements Extractor {
Atom
.
ContainerAtom
atom
=
moov
.
containerChildren
.
get
(
i
);
if
(
atom
.
type
==
Atom
.
TYPE_trak
)
{
Track
track
=
AtomParsers
.
parseTrak
(
atom
,
moov
.
getLeafAtomOfType
(
Atom
.
TYPE_mvhd
),
duration
,
drmInitData
,
false
);
drmInitData
,
(
flags
&
FLAG_WORKAROUND_IGNORE_EDIT_LISTS
)
!=
0
,
false
);
if
(
track
!=
null
)
{
tracks
.
put
(
track
.
id
,
track
);
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
View file @
ab6f9aea
...
...
@@ -28,6 +28,7 @@ import com.google.android.exoplayer2.extractor.PositionHolder;
import
com.google.android.exoplayer2.extractor.SeekMap
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.extractor.mp4.Atom.ContainerAtom
;
import
com.google.android.exoplayer2.extractor.mp4.FragmentedMp4Extractor.Flags
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.NalUnitUtil
;
...
...
@@ -58,6 +59,17 @@ public final class Mp4Extractor implements Extractor, SeekMap {
};
/**
* Flags controlling the behavior of the extractor.
*/
@Retention
(
RetentionPolicy
.
SOURCE
)
@IntDef
(
flag
=
true
,
value
=
{
FLAG_WORKAROUND_IGNORE_EDIT_LISTS
})
public
@interface
Flags
{}
/**
* Flag to ignore any edit lists in the stream.
*/
public
static
final
int
FLAG_WORKAROUND_IGNORE_EDIT_LISTS
=
1
;
/**
* Parser states.
*/
@Retention
(
RetentionPolicy
.
SOURCE
)
...
...
@@ -76,6 +88,8 @@ public final class Mp4Extractor implements Extractor, SeekMap {
*/
private
static
final
long
RELOAD_MINIMUM_SEEK_DISTANCE
=
256
*
1024
;
private
final
@Flags
int
flags
;
// Temporary arrays.
private
final
ParsableByteArray
nalStartCode
;
private
final
ParsableByteArray
nalLength
;
...
...
@@ -98,7 +112,21 @@ public final class Mp4Extractor implements Extractor, SeekMap {
private
long
durationUs
;
private
boolean
isQuickTime
;
/**
* Creates a new extractor for unfragmented MP4 streams.
*/
public
Mp4Extractor
()
{
this
(
0
);
}
/**
* Creates a new extractor for unfragmented MP4 streams, using the specified flags to control the
* extractor's behavior.
*
* @param flags Flags that control the extractor's behavior.
*/
public
Mp4Extractor
(
@Flags
int
flags
)
{
this
.
flags
=
flags
;
atomHeader
=
new
ParsableByteArray
(
Atom
.
LONG_HEADER_SIZE
);
containerAtoms
=
new
Stack
<>();
nalStartCode
=
new
ParsableByteArray
(
NalUnitUtil
.
NAL_START_CODE
);
...
...
@@ -345,7 +373,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
}
Track
track
=
AtomParsers
.
parseTrak
(
atom
,
moov
.
getLeafAtomOfType
(
Atom
.
TYPE_mvhd
),
C
.
TIME_UNSET
,
null
,
isQuickTime
);
C
.
TIME_UNSET
,
null
,
(
flags
&
FLAG_WORKAROUND_IGNORE_EDIT_LISTS
)
!=
0
,
isQuickTime
);
if
(
track
==
null
)
{
continue
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Track.java
View file @
ab6f9aea
...
...
@@ -81,12 +81,12 @@ public final class Track {
/**
* Durations of edit list segments in the movie timescale. Null if there is no edit list.
*/
public
final
long
[]
editListDurations
;
@Nullable
public
final
long
[]
editListDurations
;
/**
* Media times for edit list segments in the track timescale. Null if there is no edit list.
*/
public
final
long
[]
editListMediaTimes
;
@Nullable
public
final
long
[]
editListMediaTimes
;
/**
* For H264 video tracks, the length in bytes of the NALUnitLength field in each sample. 0 for
...
...
@@ -99,7 +99,7 @@ public final class Track {
public
Track
(
int
id
,
int
type
,
long
timescale
,
long
movieTimescale
,
long
durationUs
,
Format
format
,
@Transformation
int
sampleTransformation
,
@Nullable
TrackEncryptionBox
[]
sampleDescriptionEncryptionBoxes
,
int
nalUnitLengthFieldLength
,
long
[]
editListDurations
,
long
[]
editListMediaTimes
)
{
@Nullable
long
[]
editListDurations
,
@Nullable
long
[]
editListMediaTimes
)
{
this
.
id
=
id
;
this
.
type
=
type
;
this
.
timescale
=
timescale
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
View file @
ab6f9aea
...
...
@@ -23,6 +23,7 @@ import android.media.MediaCrypto;
import
android.media.MediaFormat
;
import
android.os.Looper
;
import
android.os.SystemClock
;
import
android.support.annotation.IntDef
;
import
android.support.annotation.Nullable
;
import
android.util.Log
;
import
com.google.android.exoplayer2.BaseRenderer
;
...
...
@@ -43,6 +44,9 @@ import com.google.android.exoplayer2.util.Assertions;
import
com.google.android.exoplayer2.util.NalUnitUtil
;
import
com.google.android.exoplayer2.util.TraceUtil
;
import
com.google.android.exoplayer2.util.Util
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.nio.ByteBuffer
;
import
java.util.ArrayList
;
import
java.util.List
;
...
...
@@ -158,9 +162,26 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
*/
private
static
final
int
REINITIALIZATION_STATE_WAIT_END_OF_STREAM
=
2
;
@Retention
(
RetentionPolicy
.
SOURCE
)
@IntDef
({
ADAPTATION_WORKAROUND_MODE_NEVER
,
ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION
,
ADAPTATION_WORKAROUND_MODE_ALWAYS
})
private
@interface
AdaptationWorkaroundMode
{}
/**
* The adaptation workaround is never used.
*/
private
static
final
int
ADAPTATION_WORKAROUND_MODE_NEVER
=
0
;
/**
* The adaptation workaround is used when adapting between formats of the same resolution only.
*/
private
static
final
int
ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION
=
1
;
/**
* The adaptation workaround is always used when adapting between formats.
*/
private
static
final
int
ADAPTATION_WORKAROUND_MODE_ALWAYS
=
2
;
/**
* H.264/AVC buffer to queue when using the adaptation workaround (see
* {@link #codec
NeedsAdaptationWorkaround
(String)}. Consists of three NAL units with start codes:
* {@link #codec
AdaptationWorkaroundMode
(String)}. Consists of three NAL units with start codes:
* Baseline sequence/picture parameter sets and a 32 * 32 pixel IDR slice. This stream can be
* queued to force a resolution change when adapting to a new format.
*/
...
...
@@ -182,9 +203,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
private
DrmSession
<
FrameworkMediaCrypto
>
pendingDrmSession
;
private
MediaCodec
codec
;
private
MediaCodecInfo
codecInfo
;
private
@AdaptationWorkaroundMode
int
codecAdaptationWorkaroundMode
;
private
boolean
codecNeedsDiscardToSpsWorkaround
;
private
boolean
codecNeedsFlushWorkaround
;
private
boolean
codecNeedsAdaptationWorkaround
;
private
boolean
codecNeedsEosPropagationWorkaround
;
private
boolean
codecNeedsEosFlushWorkaround
;
private
boolean
codecNeedsEosOutputExceptionWorkaround
;
...
...
@@ -355,9 +376,9 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
}
String
codecName
=
codecInfo
.
name
;
codecAdaptationWorkaroundMode
=
codecAdaptationWorkaroundMode
(
codecName
);
codecNeedsDiscardToSpsWorkaround
=
codecNeedsDiscardToSpsWorkaround
(
codecName
,
format
);
codecNeedsFlushWorkaround
=
codecNeedsFlushWorkaround
(
codecName
);
codecNeedsAdaptationWorkaround
=
codecNeedsAdaptationWorkaround
(
codecName
);
codecNeedsEosPropagationWorkaround
=
codecNeedsEosPropagationWorkaround
(
codecName
);
codecNeedsEosFlushWorkaround
=
codecNeedsEosFlushWorkaround
(
codecName
);
codecNeedsEosOutputExceptionWorkaround
=
codecNeedsEosOutputExceptionWorkaround
(
codecName
);
...
...
@@ -458,7 +479,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
codecReceivedBuffers
=
false
;
codecNeedsDiscardToSpsWorkaround
=
false
;
codecNeedsFlushWorkaround
=
false
;
codec
NeedsAdaptationWorkaround
=
false
;
codec
AdaptationWorkaroundMode
=
ADAPTATION_WORKAROUND_MODE_NEVER
;
codecNeedsEosPropagationWorkaround
=
false
;
codecNeedsEosFlushWorkaround
=
false
;
codecNeedsMonoChannelCountWorkaround
=
false
;
...
...
@@ -802,8 +823,10 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
&&
canReconfigureCodec
(
codec
,
codecInfo
.
adaptive
,
oldFormat
,
format
))
{
codecReconfigured
=
true
;
codecReconfigurationState
=
RECONFIGURATION_STATE_WRITE_PENDING
;
codecNeedsAdaptationWorkaroundBuffer
=
codecNeedsAdaptationWorkaround
&&
format
.
width
==
oldFormat
.
width
&&
format
.
height
==
oldFormat
.
height
;
codecNeedsAdaptationWorkaroundBuffer
=
codecAdaptationWorkaroundMode
==
ADAPTATION_WORKAROUND_MODE_ALWAYS
||
(
codecAdaptationWorkaroundMode
==
ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION
&&
format
.
width
==
oldFormat
.
width
&&
format
.
height
==
oldFormat
.
height
);
}
else
{
if
(
codecReceivedBuffers
)
{
// Signal end of stream and wait for any final output buffers before re-initialization.
...
...
@@ -989,7 +1012,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
*/
private
void
processOutputFormat
()
throws
ExoPlaybackException
{
MediaFormat
format
=
codec
.
getOutputFormat
();
if
(
codec
NeedsAdaptationWorkaround
if
(
codec
AdaptationWorkaroundMode
!=
ADAPTATION_WORKAROUND_MODE_NEVER
&&
format
.
getInteger
(
MediaFormat
.
KEY_WIDTH
)
==
ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT
&&
format
.
getInteger
(
MediaFormat
.
KEY_HEIGHT
)
==
ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT
)
{
// We assume this format changed event was caused by the adaptation workaround.
...
...
@@ -1122,22 +1145,30 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
}
/**
* Returns whether the decoder is known to get stuck during some adaptations where the resolution
* does not change.
* Returns a mode that specifies when the adaptation workaround should be enabled.
* <p>
* If true is returned, the renderer will work around the issue by queueing and discarding a blank
* frame at a different resolution, which resets the codec's internal state.
* When enabled, the workaround queues and discards a blank frame with a resolution whose width
* and height both equal {@link #ADAPTATION_WORKAROUND_SLICE_WIDTH_HEIGHT}, to reset the codec's
* internal state when a format change occurs.
* <p>
* See [Internal: b/27807182].
* See <a href="https://github.com/google/ExoPlayer/issues/3257">GitHub issue #3257</a>.
*
* @param name The name of the decoder.
* @return T
rue if the decoder is known to get stuck during some adaptations
.
* @return T
he mode specifying when the adaptation workaround should be enabled
.
*/
private
static
boolean
codecNeedsAdaptationWorkaround
(
String
name
)
{
return
Util
.
SDK_INT
<
24
private
@AdaptationWorkaroundMode
int
codecAdaptationWorkaroundMode
(
String
name
)
{
if
(
Util
.
SDK_INT
<=
24
&&
"OMX.Exynos.avc.dec.secure"
.
equals
(
name
)
&&
(
Util
.
MODEL
.
startsWith
(
"SM-T585"
)
||
Util
.
MODEL
.
startsWith
(
"SM-A520"
)))
{
return
ADAPTATION_WORKAROUND_MODE_ALWAYS
;
}
else
if
(
Util
.
SDK_INT
<
24
&&
(
"OMX.Nvidia.h264.decode"
.
equals
(
name
)
||
"OMX.Nvidia.h264.decode.secure"
.
equals
(
name
))
&&
(
"flounder"
.
equals
(
Util
.
DEVICE
)
||
"flounder_lte"
.
equals
(
Util
.
DEVICE
)
||
"grouper"
.
equals
(
Util
.
DEVICE
)
||
"tilapia"
.
equals
(
Util
.
DEVICE
));
||
"grouper"
.
equals
(
Util
.
DEVICE
)
||
"tilapia"
.
equals
(
Util
.
DEVICE
)))
{
return
ADAPTATION_WORKAROUND_MODE_SAME_RESOLUTION
;
}
else
{
return
ADAPTATION_WORKAROUND_MODE_NEVER
;
}
}
/**
...
...
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
View file @
ab6f9aea
...
...
@@ -973,9 +973,11 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
* If true is returned then we fall back to releasing and re-instantiating the codec instead.
*/
private
static
boolean
codecNeedsSetOutputSurfaceWorkaround
(
String
name
)
{
// Work around https://github.com/google/ExoPlayer/issues/3236
return
(
"deb"
.
equals
(
Util
.
DEVICE
)
||
"flo"
.
equals
(
Util
.
DEVICE
))
&&
"OMX.qcom.video.decoder.avc"
.
equals
(
name
);
// Work around https://github.com/google/ExoPlayer/issues/3236 and
// https://github.com/google/ExoPlayer/issues/3355.
return
((
"deb"
.
equals
(
Util
.
DEVICE
)
||
"flo"
.
equals
(
Util
.
DEVICE
))
&&
"OMX.qcom.video.decoder.avc"
.
equals
(
name
))
||
(
"tcl_eu"
.
equals
(
Util
.
DEVICE
)
&&
"OMX.MTK.VIDEO.DECODER.AVC"
.
equals
(
name
));
}
/**
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java
View file @
ab6f9aea
...
...
@@ -173,7 +173,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
}
@Override
public
final
void
getNextChunk
(
MediaChunk
previous
,
long
playbackPositionUs
,
ChunkHolder
out
)
{
public
void
getNextChunk
(
MediaChunk
previous
,
long
playbackPositionUs
,
ChunkHolder
out
)
{
if
(
fatalError
!=
null
)
{
return
;
}
...
...
@@ -300,7 +300,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
trackSelection
.
indexOf
(
chunk
.
trackFormat
),
e
);
}
//
Private
methods.
//
Internal
methods.
private
ArrayList
<
Representation
>
getRepresentations
()
{
List
<
AdaptationSet
>
manifestAdapationSets
=
manifest
.
getPeriod
(
periodIndex
).
adaptationSets
;
...
...
@@ -319,7 +319,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
}
}
pr
ivate
static
Chunk
newInitializationChunk
(
RepresentationHolder
representationHolder
,
pr
otected
static
Chunk
newInitializationChunk
(
RepresentationHolder
representationHolder
,
DataSource
dataSource
,
Format
trackFormat
,
int
trackSelectionReason
,
Object
trackSelectionData
,
RangedUri
initializationUri
,
RangedUri
indexUri
)
{
RangedUri
requestUri
;
...
...
@@ -340,7 +340,7 @@ public class DefaultDashChunkSource implements DashChunkSource {
trackSelectionReason
,
trackSelectionData
,
representationHolder
.
extractorWrapper
);
}
pr
ivate
static
Chunk
newMediaChunk
(
RepresentationHolder
representationHolder
,
pr
otected
static
Chunk
newMediaChunk
(
RepresentationHolder
representationHolder
,
DataSource
dataSource
,
int
trackType
,
Format
trackFormat
,
int
trackSelectionReason
,
Object
trackSelectionData
,
int
firstSegmentNum
,
int
maxSegmentCount
)
{
Representation
representation
=
representationHolder
.
representation
;
...
...
library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java
View file @
ab6f9aea
...
...
@@ -28,7 +28,7 @@ import java.util.List;
import
junit.framework.TestCase
;
/**
* Test for {@link HlsMasterPlaylistParserTest}
* Test for {@link HlsMasterPlaylistParserTest}
.
*/
public
class
HlsMasterPlaylistParserTest
extends
TestCase
{
...
...
library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylistParserTest.java
View file @
ab6f9aea
...
...
@@ -27,7 +27,7 @@ import java.util.Locale;
import
junit.framework.TestCase
;
/**
* Test for {@link HlsMediaPlaylistParserTest}
* Test for {@link HlsMediaPlaylistParserTest}
.
*/
public
class
HlsMediaPlaylistParserTest
extends
TestCase
{
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
View file @
ab6f9aea
...
...
@@ -26,9 +26,12 @@ import com.google.android.exoplayer2.source.MediaPeriod;
import
com.google.android.exoplayer2.source.MediaSource
;
import
com.google.android.exoplayer2.source.SinglePeriodTimeline
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsPlaylist
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistTracker
;
import
com.google.android.exoplayer2.upstream.Allocator
;
import
com.google.android.exoplayer2.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.ParsingLoadable
;
import
com.google.android.exoplayer2.util.Assertions
;
import
java.io.IOException
;
import
java.util.List
;
...
...
@@ -52,6 +55,7 @@ public final class HlsMediaSource implements MediaSource,
private
final
HlsDataSourceFactory
dataSourceFactory
;
private
final
int
minLoadableRetryCount
;
private
final
EventDispatcher
eventDispatcher
;
private
final
ParsingLoadable
.
Parser
<
HlsPlaylist
>
playlistParser
;
private
HlsPlaylistTracker
playlistTracker
;
private
Listener
sourceListener
;
...
...
@@ -72,9 +76,18 @@ public final class HlsMediaSource implements MediaSource,
public
HlsMediaSource
(
Uri
manifestUri
,
HlsDataSourceFactory
dataSourceFactory
,
int
minLoadableRetryCount
,
Handler
eventHandler
,
AdaptiveMediaSourceEventListener
eventListener
)
{
this
(
manifestUri
,
dataSourceFactory
,
minLoadableRetryCount
,
eventHandler
,
eventListener
,
new
HlsPlaylistParser
());
}
public
HlsMediaSource
(
Uri
manifestUri
,
HlsDataSourceFactory
dataSourceFactory
,
int
minLoadableRetryCount
,
Handler
eventHandler
,
AdaptiveMediaSourceEventListener
eventListener
,
ParsingLoadable
.
Parser
<
HlsPlaylist
>
playlistParser
)
{
this
.
manifestUri
=
manifestUri
;
this
.
dataSourceFactory
=
dataSourceFactory
;
this
.
minLoadableRetryCount
=
minLoadableRetryCount
;
this
.
playlistParser
=
playlistParser
;
eventDispatcher
=
new
EventDispatcher
(
eventHandler
,
eventListener
);
}
...
...
@@ -82,7 +95,7 @@ public final class HlsMediaSource implements MediaSource,
public
void
prepareSource
(
ExoPlayer
player
,
boolean
isTopLevelSource
,
Listener
listener
)
{
Assertions
.
checkState
(
playlistTracker
==
null
);
playlistTracker
=
new
HlsPlaylistTracker
(
manifestUri
,
dataSourceFactory
,
eventDispatcher
,
minLoadableRetryCount
,
this
);
minLoadableRetryCount
,
this
,
playlistParser
);
sourceListener
=
listener
;
playlistTracker
.
start
();
}
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java
View file @
ab6f9aea
...
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source.hls.playlist;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
...
...
@@ -109,6 +110,20 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
}
/**
* Returns a copy of this playlist which includes only the renditions identified by the given
* urls.
*
* @param renditionUrls List of rendition urls.
* @return A copy of this playlist which includes only the renditions identified by the given
* urls.
*/
public
HlsMasterPlaylist
copy
(
List
<
String
>
renditionUrls
)
{
return
new
HlsMasterPlaylist
(
baseUri
,
tags
,
copyRenditionsList
(
variants
,
renditionUrls
),
copyRenditionsList
(
audios
,
renditionUrls
),
copyRenditionsList
(
subtitles
,
renditionUrls
),
muxedAudioFormat
,
muxedCaptionFormats
);
}
/**
* Creates a playlist with a single variant.
*
* @param variantUrl The url of the single variant.
...
...
@@ -121,4 +136,15 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
emptyList
,
null
,
null
);
}
private
static
List
<
HlsUrl
>
copyRenditionsList
(
List
<
HlsUrl
>
renditions
,
List
<
String
>
urls
)
{
List
<
HlsUrl
>
copiedRenditions
=
new
ArrayList
<>(
urls
.
size
());
for
(
int
i
=
0
;
i
<
renditions
.
size
();
i
++)
{
HlsUrl
rendition
=
renditions
.
get
(
i
);
if
(
urls
.
contains
(
rendition
.
url
))
{
copiedRenditions
.
add
(
rendition
);
}
}
return
copiedRenditions
;
}
}
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java
View file @
ab6f9aea
This diff is collapsed.
Click to expand it.
testutils/src/main/java/com/google/android/exoplayer2/testutil/FakeMediaSource.java
View file @
ab6f9aea
...
...
@@ -111,4 +111,5 @@ public class FakeMediaSource implements MediaSource {
}
return
new
TrackGroupArray
(
trackGroups
);
}
}
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