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
f7ed789f
authored
Jun 26, 2018
by
ojw28
Committed by
GitHub
Jun 26, 2018
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #4434 from google/dev-v2-r2.8.2
r2.8.2
parents
2b55c91a
d880fac5
Hide whitespace changes
Inline
Side-by-side
Showing
93 changed files
with
1628 additions
and
1014 deletions
RELEASENOTES.md
constants.gradle
demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java
demos/main/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java
extensions/cast/build.gradle
extensions/ima/build.gradle
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.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/analytics/AnalyticsCollector.java
library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java
library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/Sniffer.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FixedSampleSizeRechunker.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/PsshAtomUtil.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OpusReader.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisUtil.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java
library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java
library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java
library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloader.java
library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloader.java
library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java
library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java
library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/BaseMediaChunk.java
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/BaseMediaChunkOutput.java
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ContainerMediaChunk.java
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/DataChunk.java
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/MediaChunk.java
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/SingleSampleMediaChunk.java
library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java
library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java
library/core/src/main/java/com/google/android/exoplayer2/text/cea/CeaDecoder.java
library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java
library/core/src/main/java/com/google/android/exoplayer2/text/tx3g/Tx3gDecoder.java
library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/CssParser.java
library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoder.java
library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java
library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttParserUtil.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/BaseTrackSelection.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/FixedTrackSelection.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/RandomTrackSelection.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionArray.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectorResult.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/DataSchemeDataSource.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/Loader.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/ParsingLoadable.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadataInternal.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/crypto/AesFlushingCipher.java
library/core/src/main/java/com/google/android/exoplayer2/util/ColorParser.java
library/core/src/main/java/com/google/android/exoplayer2/util/EGLSurfaceTexture.java
library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java
library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java
library/core/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java
library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java
library/core/src/main/java/com/google/android/exoplayer2/util/Util.java
library/core/src/main/java/com/google/android/exoplayer2/video/DummySurface.java
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/video/VideoFrameReleaseTimeHelper.java
library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashWrappingSegmentIndex.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.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/offline/HlsDownloadHelper.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/DefaultHlsPlaylistTracker.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/TrackSelectionView.java
RELEASENOTES.md
View file @
f7ed789f
# Release notes #
# Release notes #
### 2.8.2 ###
*
IMA: Don't advertise support for video/mpeg ad media, as we don't have an
extractor for this (
[
#4297
](
https://github.com/google/ExoPlayer/issues/4297
)
).
*
DASH: Fix playback getting stuck when playing representations that have both
sidx atoms and non-zero presentationTimeOffset values.
*
HLS:
*
Allow injection of custom playlist trackers.
*
Fix adaptation in live playlists with EXT-X-PROGRAM-DATE-TIME tags.
*
Mitigate memory leaks when
`MediaSource`
loads are slow to cancel
(
[
#4249
](
https://github.com/google/ExoPlayer/issues/4249
)
).
*
Fix inconsistent
`Player.EventListener`
invocations for recursive player state
changes (
[
#4276
](
https://github.com/google/ExoPlayer/issues/4276
)
).
*
Fix
`MediaCodec.native_setSurface`
crash on Moto C
(
[
#4315
](
https://github.com/google/ExoPlayer/issues/4315
)
).
*
Fix missing whitespace in CEA-608
(
[
#3906
](
https://github.com/google/ExoPlayer/issues/3906
)
).
*
Fix crash downloading HLS media playlists
(
[
#4396
](
https://github.com/google/ExoPlayer/issues/4396
)
).
*
Fix a bug where download cancellation was ignored
(
[
#4403
](
https://github.com/google/ExoPlayer/issues/4403
)
).
*
Set
`METADATA_KEY_TITLE`
on media descriptions
(
[
#4292
](
https://github.com/google/ExoPlayer/issues/4292
)
).
*
Allow apps to register custom MIME types
(
[
#4264
](
https://github.com/google/ExoPlayer/issues/4264
)
).
### 2.8.1 ###
### 2.8.1 ###
*
HLS:
*
HLS:
...
@@ -59,7 +85,7 @@
...
@@ -59,7 +85,7 @@
periods are created, released and being read from.
periods are created, released and being read from.
*
Support live stream clipping with
`ClippingMediaSource`
.
*
Support live stream clipping with
`ClippingMediaSource`
.
*
Allow setting tags for all media sources in their factories. The tag of the
*
Allow setting tags for all media sources in their factories. The tag of the
current window can be retrieved with
`
Exo
Player.getCurrentTag`
.
current window can be retrieved with
`Player.getCurrentTag`
.
*
UI components:
*
UI components:
*
Add support for displaying error messages and a buffering spinner in
*
Add support for displaying error messages and a buffering spinner in
`PlayerView`
.
`PlayerView`
.
...
...
constants.gradle
View file @
f7ed789f
...
@@ -13,8 +13,8 @@
...
@@ -13,8 +13,8 @@
// limitations under the License.
// limitations under the License.
project
.
ext
{
project
.
ext
{
// ExoPlayer version and version code.
// ExoPlayer version and version code.
releaseVersion
=
'2.8.
1
'
releaseVersion
=
'2.8.
2
'
releaseVersionCode
=
280
1
releaseVersionCode
=
280
2
// Important: ExoPlayer specifies a minSdkVersion of 14 because various
// Important: ExoPlayer specifies a minSdkVersion of 14 because various
// components provided by the library may be of use on older devices.
// components provided by the library may be of use on older devices.
// However, please note that the core media playback functionality provided
// However, please note that the core media playback functionality provided
...
@@ -25,7 +25,7 @@ project.ext {
...
@@ -25,7 +25,7 @@ project.ext {
buildToolsVersion
=
'27.0.3'
buildToolsVersion
=
'27.0.3'
testSupportLibraryVersion
=
'0.5'
testSupportLibraryVersion
=
'0.5'
supportLibraryVersion
=
'27.0.0'
supportLibraryVersion
=
'27.0.0'
playServicesLibraryVersion
=
'1
2.0.0
'
playServicesLibraryVersion
=
'1
5.0.1
'
dexmakerVersion
=
'1.2'
dexmakerVersion
=
'1.2'
mockitoVersion
=
'1.9.5'
mockitoVersion
=
'1.9.5'
junitVersion
=
'4.12'
junitVersion
=
'4.12'
...
...
demos/main/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java
View file @
f7ed789f
...
@@ -136,6 +136,7 @@ public class PlayerActivity extends Activity
...
@@ -136,6 +136,7 @@ public class PlayerActivity extends Activity
private
DataSource
.
Factory
mediaDataSourceFactory
;
private
DataSource
.
Factory
mediaDataSourceFactory
;
private
SimpleExoPlayer
player
;
private
SimpleExoPlayer
player
;
private
FrameworkMediaDrm
mediaDrm
;
private
MediaSource
mediaSource
;
private
MediaSource
mediaSource
;
private
DefaultTrackSelector
trackSelector
;
private
DefaultTrackSelector
trackSelector
;
private
DefaultTrackSelector
.
Parameters
trackSelectorParameters
;
private
DefaultTrackSelector
.
Parameters
trackSelectorParameters
;
...
@@ -487,8 +488,9 @@ public class PlayerActivity extends Activity
...
@@ -487,8 +488,9 @@ public class PlayerActivity extends Activity
keyRequestPropertiesArray
[
i
+
1
]);
keyRequestPropertiesArray
[
i
+
1
]);
}
}
}
}
return
new
DefaultDrmSessionManager
<>(
releaseMediaDrm
();
uuid
,
FrameworkMediaDrm
.
newInstance
(
uuid
),
drmCallback
,
null
,
multiSession
);
mediaDrm
=
FrameworkMediaDrm
.
newInstance
(
uuid
);
return
new
DefaultDrmSessionManager
<>(
uuid
,
mediaDrm
,
drmCallback
,
null
,
multiSession
);
}
}
private
void
releasePlayer
()
{
private
void
releasePlayer
()
{
...
@@ -502,6 +504,23 @@ public class PlayerActivity extends Activity
...
@@ -502,6 +504,23 @@ public class PlayerActivity extends Activity
mediaSource
=
null
;
mediaSource
=
null
;
trackSelector
=
null
;
trackSelector
=
null
;
}
}
releaseMediaDrm
();
}
private
void
releaseMediaDrm
()
{
if
(
mediaDrm
!=
null
)
{
mediaDrm
.
release
();
mediaDrm
=
null
;
}
}
private
void
releaseAdsLoader
()
{
if
(
adsLoader
!=
null
)
{
adsLoader
.
release
();
adsLoader
=
null
;
loadedAdTagUri
=
null
;
playerView
.
getOverlayFrameLayout
().
removeAllViews
();
}
}
}
private
void
updateTrackSelectorParameters
()
{
private
void
updateTrackSelectorParameters
()
{
...
@@ -576,15 +595,6 @@ public class PlayerActivity extends Activity
...
@@ -576,15 +595,6 @@ public class PlayerActivity extends Activity
}
}
}
}
private
void
releaseAdsLoader
()
{
if
(
adsLoader
!=
null
)
{
adsLoader
.
release
();
adsLoader
=
null
;
loadedAdTagUri
=
null
;
playerView
.
getOverlayFrameLayout
().
removeAllViews
();
}
}
// User controls
// User controls
private
void
updateButtonVisibilities
()
{
private
void
updateButtonVisibilities
()
{
...
...
demos/main/src/main/java/com/google/android/exoplayer2/demo/SampleChooserActivity.java
View file @
f7ed789f
...
@@ -94,9 +94,15 @@ public class SampleChooserActivity extends Activity
...
@@ -94,9 +94,15 @@ public class SampleChooserActivity extends Activity
SampleListLoader
loaderTask
=
new
SampleListLoader
();
SampleListLoader
loaderTask
=
new
SampleListLoader
();
loaderTask
.
execute
(
uris
);
loaderTask
.
execute
(
uris
);
// Ping the download service in case it's not running (but should be).
// Start the download service if it should be running but it's not currently.
startService
(
// Starting the service in the foreground causes notification flicker if there is no scheduled
new
Intent
(
this
,
DemoDownloadService
.
class
).
setAction
(
DownloadService
.
ACTION_INIT
));
// action. Starting it in the background throws an exception if the app is in the background too
// (e.g. if device screen is locked).
try
{
DownloadService
.
start
(
this
,
DemoDownloadService
.
class
);
}
catch
(
IllegalStateException
e
)
{
DownloadService
.
startForeground
(
this
,
DemoDownloadService
.
class
);
}
}
}
@Override
@Override
...
...
extensions/cast/build.gradle
View file @
f7ed789f
...
@@ -26,16 +26,6 @@ android {
...
@@ -26,16 +26,6 @@ android {
}
}
dependencies
{
dependencies
{
// These dependencies are necessary to force the supportLibraryVersion of
// com.android.support:support-v4, com.android.support:appcompat-v7 and
// com.android.support:mediarouter-v7 to be used. Else older versions are
// used, for example:
// com.google.android.gms:play-services-cast-framework:12.0.0
// |-- com.google.android.gms:play-services-basement:12.0.0
// |-- com.android.support:support-v4:26.1.0
api
'com.android.support:support-v4:'
+
supportLibraryVersion
api
'com.android.support:appcompat-v7:'
+
supportLibraryVersion
api
'com.android.support:mediarouter-v7:'
+
supportLibraryVersion
api
'com.google.android.gms:play-services-cast-framework:'
+
playServicesLibraryVersion
api
'com.google.android.gms:play-services-cast-framework:'
+
playServicesLibraryVersion
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
project
(
modulePrefix
+
'library-ui'
)
implementation
project
(
modulePrefix
+
'library-ui'
)
...
@@ -44,6 +34,15 @@ dependencies {
...
@@ -44,6 +34,15 @@ dependencies {
testImplementation
'org.mockito:mockito-core:'
+
mockitoVersion
testImplementation
'org.mockito:mockito-core:'
+
mockitoVersion
testImplementation
'org.robolectric:robolectric:'
+
robolectricVersion
testImplementation
'org.robolectric:robolectric:'
+
robolectricVersion
testImplementation
project
(
modulePrefix
+
'testutils-robolectric'
)
testImplementation
project
(
modulePrefix
+
'testutils-robolectric'
)
// These dependencies are necessary to force the supportLibraryVersion of
// com.android.support:support-v4, com.android.support:appcompat-v7 and
// com.android.support:mediarouter-v7 to be used. Else older versions are
// used, for example via:
// com.google.android.gms:play-services-cast-framework:15.0.1
// |-- com.android.support:mediarouter-v7:26.1.0
api
'com.android.support:support-v4:'
+
supportLibraryVersion
api
'com.android.support:mediarouter-v7:'
+
supportLibraryVersion
api
'com.android.support:recyclerview-v7:'
+
supportLibraryVersion
}
}
ext
{
ext
{
...
...
extensions/ima/build.gradle
View file @
f7ed789f
...
@@ -26,17 +26,16 @@ android {
...
@@ -26,17 +26,16 @@ android {
}
}
dependencies
{
dependencies
{
// This dependency is necessary to force the supportLibraryVersion of
api
'com.google.ads.interactivemedia.v3:interactivemedia:3.8.7'
// com.android.support:support-v4 to be used. Else an older version (25.2.0)
// is included via:
// com.google.android.gms:play-services-ads:12.0.0
// |-- com.google.android.gms:play-services-ads-lite:12.0.0
// |-- com.google.android.gms:play-services-basement:12.0.0
// |-- com.android.support:support-v4:26.1.0
api
'com.android.support:support-v4:'
+
supportLibraryVersion
api
'com.google.ads.interactivemedia.v3:interactivemedia:3.8.5'
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
'com.google.android.gms:play-services-ads:'
+
playServicesLibraryVersion
implementation
'com.google.android.gms:play-services-ads:'
+
playServicesLibraryVersion
// These dependencies are necessary to force the supportLibraryVersion of
// com.android.support:support-v4 and com.android.support:customtabs to be
// used. Else older versions are used, for example via:
// com.google.android.gms:play-services-ads:15.0.1
// |-- com.android.support:customtabs:26.1.0
implementation
'com.android.support:support-v4:'
+
supportLibraryVersion
implementation
'com.android.support:customtabs:'
+
supportLibraryVersion
}
}
ext
{
ext
{
...
...
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
View file @
f7ed789f
...
@@ -447,9 +447,13 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -447,9 +447,13 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
}
else
if
(
contentType
==
C
.
TYPE_HLS
)
{
}
else
if
(
contentType
==
C
.
TYPE_HLS
)
{
supportedMimeTypes
.
add
(
MimeTypes
.
APPLICATION_M3U8
);
supportedMimeTypes
.
add
(
MimeTypes
.
APPLICATION_M3U8
);
}
else
if
(
contentType
==
C
.
TYPE_OTHER
)
{
}
else
if
(
contentType
==
C
.
TYPE_OTHER
)
{
supportedMimeTypes
.
addAll
(
Arrays
.
asList
(
supportedMimeTypes
.
addAll
(
MimeTypes
.
VIDEO_MP4
,
MimeTypes
.
VIDEO_WEBM
,
MimeTypes
.
VIDEO_H263
,
MimeTypes
.
VIDEO_MPEG
,
Arrays
.
asList
(
MimeTypes
.
AUDIO_MP4
,
MimeTypes
.
AUDIO_MPEG
));
MimeTypes
.
VIDEO_MP4
,
MimeTypes
.
VIDEO_WEBM
,
MimeTypes
.
VIDEO_H263
,
MimeTypes
.
AUDIO_MP4
,
MimeTypes
.
AUDIO_MPEG
));
}
else
if
(
contentType
==
C
.
TYPE_SS
)
{
}
else
if
(
contentType
==
C
.
TYPE_SS
)
{
// IMA does not support Smooth Streaming ad media.
// IMA does not support Smooth Streaming ad media.
}
}
...
...
extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java
View file @
f7ed789f
...
@@ -600,8 +600,9 @@ public final class MediaSessionConnector {
...
@@ -600,8 +600,9 @@ public final class MediaSessionConnector {
}
}
}
}
if
(
description
.
getTitle
()
!=
null
)
{
if
(
description
.
getTitle
()
!=
null
)
{
builder
.
putString
(
MediaMetadataCompat
.
METADATA_KEY_DISPLAY_TITLE
,
String
title
=
String
.
valueOf
(
description
.
getTitle
());
String
.
valueOf
(
description
.
getTitle
()));
builder
.
putString
(
MediaMetadataCompat
.
METADATA_KEY_TITLE
,
title
);
builder
.
putString
(
MediaMetadataCompat
.
METADATA_KEY_DISPLAY_TITLE
,
title
);
}
}
if
(
description
.
getSubtitle
()
!=
null
)
{
if
(
description
.
getSubtitle
()
!=
null
)
{
builder
.
putString
(
MediaMetadataCompat
.
METADATA_KEY_DISPLAY_SUBTITLE
,
builder
.
putString
(
MediaMetadataCompat
.
METADATA_KEY_DISPLAY_SUBTITLE
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java
View file @
f7ed789f
...
@@ -89,12 +89,12 @@ import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
...
@@ -89,12 +89,12 @@ import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
* model">
* model">
*
*
* <ul>
* <ul>
* <li>
It is strongly recommended that ExoPlayer instances are created and accessed from a singl
e
* <li>
ExoPlayer instances must be accessed from a single application thread. This must be th
e
*
application thread. The application's main thread is ideal. Accessing an instance from
*
thread the player is created on if that thread has a {@link Looper}, or the application's
* m
ultiple threads is discouraged as it may cause synchronization problems
.
* m
ain thread otherwise
.
* <li>Registered listeners are called on the thread th
at created the ExoPlayer instance, unless
* <li>Registered listeners are called on the thread th
e player is created on if that thread has a
*
the thread that created the ExoPlayer instance does not have a {@link Looper}. In that
*
{@link Looper}, or the application's main thread otherwise. Note that this means registered
*
case, registered listeners will be called on the application's main thread
.
*
listeners are called on the same thread which must be used to access the player
.
* <li>An internal playback thread is responsible for playback. Injected player components such as
* <li>An internal playback thread is responsible for playback. Injected player components such as
* Renderers, MediaSources, TrackSelectors and LoadControls are called by the player on this
* Renderers, MediaSources, TrackSelectors and LoadControls are called by the player on this
* thread.
* thread.
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
View file @
f7ed789f
...
@@ -33,8 +33,10 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
...
@@ -33,8 +33,10 @@ import com.google.android.exoplayer2.trackselection.TrackSelectorResult;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Clock
;
import
com.google.android.exoplayer2.util.Clock
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.android.exoplayer2.util.Util
;
import
java.util.ArrayDeque
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Set
;
import
java.util.concurrent.CopyOnWriteArraySet
;
import
java.util.concurrent.CopyOnWriteArraySet
;
/**
/**
...
@@ -53,6 +55,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -53,6 +55,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
private
final
CopyOnWriteArraySet
<
Player
.
EventListener
>
listeners
;
private
final
CopyOnWriteArraySet
<
Player
.
EventListener
>
listeners
;
private
final
Timeline
.
Window
window
;
private
final
Timeline
.
Window
window
;
private
final
Timeline
.
Period
period
;
private
final
Timeline
.
Period
period
;
private
final
ArrayDeque
<
PlaybackInfoUpdate
>
pendingPlaybackInfoUpdates
;
private
boolean
playWhenReady
;
private
boolean
playWhenReady
;
private
@RepeatMode
int
repeatMode
;
private
@RepeatMode
int
repeatMode
;
...
@@ -112,6 +115,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -112,6 +115,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
/* startPositionUs= */
0
,
/* startPositionUs= */
0
,
TrackGroupArray
.
EMPTY
,
TrackGroupArray
.
EMPTY
,
emptyTrackSelectorResult
);
emptyTrackSelectorResult
);
pendingPlaybackInfoUpdates
=
new
ArrayDeque
<>();
internalPlayer
=
internalPlayer
=
new
ExoPlayerImplInternal
(
new
ExoPlayerImplInternal
(
renderers
,
renderers
,
...
@@ -185,7 +189,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -185,7 +189,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
/* positionDiscontinuity= */
false
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
TIMELINE_CHANGE_REASON_RESET
,
TIMELINE_CHANGE_REASON_RESET
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
/* playWhenReadyChanged= */
false
);
}
}
@Override
@Override
...
@@ -193,10 +198,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -193,10 +198,13 @@ import java.util.concurrent.CopyOnWriteArraySet;
if
(
this
.
playWhenReady
!=
playWhenReady
)
{
if
(
this
.
playWhenReady
!=
playWhenReady
)
{
this
.
playWhenReady
=
playWhenReady
;
this
.
playWhenReady
=
playWhenReady
;
internalPlayer
.
setPlayWhenReady
(
playWhenReady
);
internalPlayer
.
setPlayWhenReady
(
playWhenReady
);
PlaybackInfo
playbackInfo
=
this
.
playbackInfo
;
updatePlaybackInfo
(
for
(
Player
.
EventListener
listener
:
listeners
)
{
playbackInfo
,
listener
.
onPlayerStateChanged
(
playWhenReady
,
playbackInfo
.
playbackState
);
/* positionDiscontinuity= */
false
,
}
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
TIMELINE_CHANGE_REASON_RESET
,
/* seekProcessed= */
false
,
/* playWhenReadyChanged= */
true
);
}
}
}
}
...
@@ -352,7 +360,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -352,7 +360,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
/* positionDiscontinuity= */
false
,
/* positionDiscontinuity= */
false
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
/* ignored */
DISCONTINUITY_REASON_INTERNAL
,
TIMELINE_CHANGE_REASON_RESET
,
TIMELINE_CHANGE_REASON_RESET
,
/* seekProcessed= */
false
);
/* seekProcessed= */
false
,
/* playWhenReadyChanged= */
false
);
}
}
@Override
@Override
...
@@ -615,7 +624,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -615,7 +624,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
positionDiscontinuity
,
positionDiscontinuity
,
positionDiscontinuityReason
,
positionDiscontinuityReason
,
timelineChangeReason
,
timelineChangeReason
,
seekProcessed
);
seekProcessed
,
/* playWhenReadyChanged= */
false
);
}
}
}
}
...
@@ -643,51 +653,33 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -643,51 +653,33 @@ import java.util.concurrent.CopyOnWriteArraySet;
}
}
private
void
updatePlaybackInfo
(
private
void
updatePlaybackInfo
(
PlaybackInfo
newP
laybackInfo
,
PlaybackInfo
p
laybackInfo
,
boolean
positionDiscontinuity
,
boolean
positionDiscontinuity
,
@Player
.
DiscontinuityReason
int
positionDiscontinuityReason
,
@Player
.
DiscontinuityReason
int
positionDiscontinuityReason
,
@Player
.
TimelineChangeReason
int
timelineChangeReason
,
@Player
.
TimelineChangeReason
int
timelineChangeReason
,
boolean
seekProcessed
)
{
boolean
seekProcessed
,
boolean
timelineOrManifestChanged
=
boolean
playWhenReadyChanged
)
{
playbackInfo
.
timeline
!=
newPlaybackInfo
.
timeline
boolean
isRunningRecursiveListenerNotification
=
!
pendingPlaybackInfoUpdates
.
isEmpty
();
||
playbackInfo
.
manifest
!=
newPlaybackInfo
.
manifest
;
pendingPlaybackInfoUpdates
.
addLast
(
boolean
playbackStateChanged
=
playbackInfo
.
playbackState
!=
newPlaybackInfo
.
playbackState
;
new
PlaybackInfoUpdate
(
boolean
isLoadingChanged
=
playbackInfo
.
isLoading
!=
newPlaybackInfo
.
isLoading
;
playbackInfo
,
boolean
trackSelectorResultChanged
=
/* previousPlaybackInfo= */
this
.
playbackInfo
,
playbackInfo
.
trackSelectorResult
!=
newPlaybackInfo
.
trackSelectorResult
;
listeners
,
playbackInfo
=
newPlaybackInfo
;
trackSelector
,
if
(
timelineOrManifestChanged
||
timelineChangeReason
==
TIMELINE_CHANGE_REASON_PREPARED
)
{
positionDiscontinuity
,
for
(
Player
.
EventListener
listener
:
listeners
)
{
positionDiscontinuityReason
,
listener
.
onTimelineChanged
(
timelineChangeReason
,
playbackInfo
.
timeline
,
playbackInfo
.
manifest
,
timelineChangeReason
);
seekProcessed
,
}
playWhenReady
,
}
playWhenReadyChanged
));
if
(
positionDiscontinuity
)
{
// Assign playback info immediately such that all getters return the right values.
for
(
Player
.
EventListener
listener
:
listeners
)
{
this
.
playbackInfo
=
playbackInfo
;
listener
.
onPositionDiscontinuity
(
positionDiscontinuityReason
);
if
(
isRunningRecursiveListenerNotification
)
{
}
return
;
}
if
(
trackSelectorResultChanged
)
{
trackSelector
.
onSelectionActivated
(
playbackInfo
.
trackSelectorResult
.
info
);
for
(
Player
.
EventListener
listener
:
listeners
)
{
listener
.
onTracksChanged
(
playbackInfo
.
trackGroups
,
playbackInfo
.
trackSelectorResult
.
selections
);
}
}
if
(
isLoadingChanged
)
{
for
(
Player
.
EventListener
listener
:
listeners
)
{
listener
.
onLoadingChanged
(
playbackInfo
.
isLoading
);
}
}
if
(
playbackStateChanged
)
{
for
(
Player
.
EventListener
listener
:
listeners
)
{
listener
.
onPlayerStateChanged
(
playWhenReady
,
playbackInfo
.
playbackState
);
}
}
}
if
(
seekProcessed
)
{
while
(!
pendingPlaybackInfoUpdates
.
isEmpty
())
{
for
(
Player
.
EventListener
listener
:
listeners
)
{
pendingPlaybackInfoUpdates
.
peekFirst
().
notifyListeners
();
listener
.
onSeekProcessed
();
pendingPlaybackInfoUpdates
.
removeFirst
();
}
}
}
}
}
...
@@ -703,4 +695,85 @@ import java.util.concurrent.CopyOnWriteArraySet;
...
@@ -703,4 +695,85 @@ import java.util.concurrent.CopyOnWriteArraySet;
private
boolean
shouldMaskPosition
()
{
private
boolean
shouldMaskPosition
()
{
return
playbackInfo
.
timeline
.
isEmpty
()
||
pendingOperationAcks
>
0
;
return
playbackInfo
.
timeline
.
isEmpty
()
||
pendingOperationAcks
>
0
;
}
}
private
static
final
class
PlaybackInfoUpdate
{
private
final
PlaybackInfo
playbackInfo
;
private
final
Set
<
Player
.
EventListener
>
listeners
;
private
final
TrackSelector
trackSelector
;
private
final
boolean
positionDiscontinuity
;
private
final
@Player
.
DiscontinuityReason
int
positionDiscontinuityReason
;
private
final
@Player
.
TimelineChangeReason
int
timelineChangeReason
;
private
final
boolean
seekProcessed
;
private
final
boolean
playWhenReady
;
private
final
boolean
playbackStateOrPlayWhenReadyChanged
;
private
final
boolean
timelineOrManifestChanged
;
private
final
boolean
isLoadingChanged
;
private
final
boolean
trackSelectorResultChanged
;
public
PlaybackInfoUpdate
(
PlaybackInfo
playbackInfo
,
PlaybackInfo
previousPlaybackInfo
,
Set
<
Player
.
EventListener
>
listeners
,
TrackSelector
trackSelector
,
boolean
positionDiscontinuity
,
@Player
.
DiscontinuityReason
int
positionDiscontinuityReason
,
@Player
.
TimelineChangeReason
int
timelineChangeReason
,
boolean
seekProcessed
,
boolean
playWhenReady
,
boolean
playWhenReadyChanged
)
{
this
.
playbackInfo
=
playbackInfo
;
this
.
listeners
=
listeners
;
this
.
trackSelector
=
trackSelector
;
this
.
positionDiscontinuity
=
positionDiscontinuity
;
this
.
positionDiscontinuityReason
=
positionDiscontinuityReason
;
this
.
timelineChangeReason
=
timelineChangeReason
;
this
.
seekProcessed
=
seekProcessed
;
this
.
playWhenReady
=
playWhenReady
;
playbackStateOrPlayWhenReadyChanged
=
playWhenReadyChanged
||
previousPlaybackInfo
.
playbackState
!=
playbackInfo
.
playbackState
;
timelineOrManifestChanged
=
previousPlaybackInfo
.
timeline
!=
playbackInfo
.
timeline
||
previousPlaybackInfo
.
manifest
!=
playbackInfo
.
manifest
;
isLoadingChanged
=
previousPlaybackInfo
.
isLoading
!=
playbackInfo
.
isLoading
;
trackSelectorResultChanged
=
previousPlaybackInfo
.
trackSelectorResult
!=
playbackInfo
.
trackSelectorResult
;
}
public
void
notifyListeners
()
{
if
(
timelineOrManifestChanged
||
timelineChangeReason
==
TIMELINE_CHANGE_REASON_PREPARED
)
{
for
(
Player
.
EventListener
listener
:
listeners
)
{
listener
.
onTimelineChanged
(
playbackInfo
.
timeline
,
playbackInfo
.
manifest
,
timelineChangeReason
);
}
}
if
(
positionDiscontinuity
)
{
for
(
Player
.
EventListener
listener
:
listeners
)
{
listener
.
onPositionDiscontinuity
(
positionDiscontinuityReason
);
}
}
if
(
trackSelectorResultChanged
)
{
trackSelector
.
onSelectionActivated
(
playbackInfo
.
trackSelectorResult
.
info
);
for
(
Player
.
EventListener
listener
:
listeners
)
{
listener
.
onTracksChanged
(
playbackInfo
.
trackGroups
,
playbackInfo
.
trackSelectorResult
.
selections
);
}
}
if
(
isLoadingChanged
)
{
for
(
Player
.
EventListener
listener
:
listeners
)
{
listener
.
onLoadingChanged
(
playbackInfo
.
isLoading
);
}
}
if
(
playbackStateOrPlayWhenReadyChanged
)
{
for
(
Player
.
EventListener
listener
:
listeners
)
{
listener
.
onPlayerStateChanged
(
playWhenReady
,
playbackInfo
.
playbackState
);
}
}
if
(
seekProcessed
)
{
for
(
Player
.
EventListener
listener
:
listeners
)
{
listener
.
onSeekProcessed
();
}
}
}
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
View file @
f7ed789f
...
@@ -1543,6 +1543,7 @@ import java.util.Collections;
...
@@ -1543,6 +1543,7 @@ import java.util.Collections;
}
}
}
}
@SuppressWarnings
(
"ParameterNotNullable"
)
private
void
updatePlayingPeriodRenderers
(
@Nullable
MediaPeriodHolder
oldPlayingPeriodHolder
)
private
void
updatePlayingPeriodRenderers
(
@Nullable
MediaPeriodHolder
oldPlayingPeriodHolder
)
throws
ExoPlaybackException
{
throws
ExoPlaybackException
{
MediaPeriodHolder
newPlayingPeriodHolder
=
queue
.
getPlayingPeriod
();
MediaPeriodHolder
newPlayingPeriodHolder
=
queue
.
getPlayingPeriod
();
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
View file @
f7ed789f
...
@@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo {
...
@@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo {
/** The version of the library expressed as a string, for example "1.2.3". */
/** 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.
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
public
static
final
String
VERSION
=
"2.8.
1
"
;
public
static
final
String
VERSION
=
"2.8.
2
"
;
/** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */
/** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public
static
final
String
VERSION_SLASHY
=
"ExoPlayerLib/2.8.
1
"
;
public
static
final
String
VERSION_SLASHY
=
"ExoPlayerLib/2.8.
2
"
;
/**
/**
* The version of the library expressed as an integer, for example 1002003.
* The version of the library expressed as an integer, for example 1002003.
...
@@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo {
...
@@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo {
* integer version 123045006 (123-045-006).
* integer version 123045006 (123-045-006).
*/
*/
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public
static
final
int
VERSION_INT
=
200800
1
;
public
static
final
int
VERSION_INT
=
200800
2
;
/**
/**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
...
...
library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java
View file @
f7ed789f
...
@@ -46,6 +46,7 @@ import java.util.Collections;
...
@@ -46,6 +46,7 @@ import java.util.Collections;
import
java.util.List
;
import
java.util.List
;
import
java.util.Set
;
import
java.util.Set
;
import
java.util.concurrent.CopyOnWriteArraySet
;
import
java.util.concurrent.CopyOnWriteArraySet
;
import
org.checkerframework.checker.nullness.qual.MonotonicNonNull
;
/**
/**
* Data collector which is able to forward analytics events to {@link AnalyticsListener}s by
* Data collector which is able to forward analytics events to {@link AnalyticsListener}s by
...
@@ -66,29 +67,34 @@ public class AnalyticsCollector
...
@@ -66,29 +67,34 @@ public class AnalyticsCollector
/**
/**
* Creates an analytics collector for the specified player.
* Creates an analytics collector for the specified player.
*
*
* @param player The {@link Player} for which data will be collected.
* @param player The {@link Player} for which data will be collected. Can be null, if the player
* is set by calling {@link AnalyticsCollector#setPlayer(Player)} before using the analytics
* collector.
* @param clock A {@link Clock} used to generate timestamps.
* @param clock A {@link Clock} used to generate timestamps.
* @return An analytics collector.
* @return An analytics collector.
*/
*/
public
AnalyticsCollector
createAnalyticsCollector
(
Player
player
,
Clock
clock
)
{
public
AnalyticsCollector
createAnalyticsCollector
(
@Nullable
Player
player
,
Clock
clock
)
{
return
new
AnalyticsCollector
(
player
,
clock
);
return
new
AnalyticsCollector
(
player
,
clock
);
}
}
}
}
private
final
CopyOnWriteArraySet
<
AnalyticsListener
>
listeners
;
private
final
CopyOnWriteArraySet
<
AnalyticsListener
>
listeners
;
private
final
Player
player
;
private
final
Clock
clock
;
private
final
Clock
clock
;
private
final
Window
window
;
private
final
Window
window
;
private
final
MediaPeriodQueueTracker
mediaPeriodQueueTracker
;
private
final
MediaPeriodQueueTracker
mediaPeriodQueueTracker
;
private
@MonotonicNonNull
Player
player
;
/**
/**
* Creates an analytics collector for the specified player.
* Creates an analytics collector for the specified player.
*
*
* @param player The {@link Player} for which data will be collected.
* @param player The {@link Player} for which data will be collected. Can be null, if the player
* is set by calling {@link AnalyticsCollector#setPlayer(Player)} before using the analytics
* collector.
* @param clock A {@link Clock} used to generate timestamps.
* @param clock A {@link Clock} used to generate timestamps.
*/
*/
protected
AnalyticsCollector
(
Player
player
,
Clock
clock
)
{
protected
AnalyticsCollector
(
@Nullable
Player
player
,
Clock
clock
)
{
this
.
player
=
Assertions
.
checkNotNull
(
player
)
;
this
.
player
=
player
;
this
.
clock
=
Assertions
.
checkNotNull
(
clock
);
this
.
clock
=
Assertions
.
checkNotNull
(
clock
);
listeners
=
new
CopyOnWriteArraySet
<>();
listeners
=
new
CopyOnWriteArraySet
<>();
mediaPeriodQueueTracker
=
new
MediaPeriodQueueTracker
();
mediaPeriodQueueTracker
=
new
MediaPeriodQueueTracker
();
...
@@ -113,6 +119,17 @@ public class AnalyticsCollector
...
@@ -113,6 +119,17 @@ public class AnalyticsCollector
listeners
.
remove
(
listener
);
listeners
.
remove
(
listener
);
}
}
/**
* Sets the player for which data will be collected. Must only be called if no player has been set
* yet.
*
* @param player The {@link Player} for which data will be collected.
*/
public
void
setPlayer
(
Player
player
)
{
Assertions
.
checkState
(
this
.
player
==
null
);
this
.
player
=
Assertions
.
checkNotNull
(
player
);
}
// External events.
// External events.
/**
/**
...
@@ -541,6 +558,7 @@ public class AnalyticsCollector
...
@@ -541,6 +558,7 @@ public class AnalyticsCollector
/** Returns a new {@link EventTime} for the specified window index and media period id. */
/** Returns a new {@link EventTime} for the specified window index and media period id. */
protected
EventTime
generateEventTime
(
int
windowIndex
,
@Nullable
MediaPeriodId
mediaPeriodId
)
{
protected
EventTime
generateEventTime
(
int
windowIndex
,
@Nullable
MediaPeriodId
mediaPeriodId
)
{
Assertions
.
checkNotNull
(
player
);
long
realtimeMs
=
clock
.
elapsedRealtime
();
long
realtimeMs
=
clock
.
elapsedRealtime
();
Timeline
timeline
=
player
.
getCurrentTimeline
();
Timeline
timeline
=
player
.
getCurrentTimeline
();
long
eventPositionMs
;
long
eventPositionMs
;
...
@@ -579,7 +597,7 @@ public class AnalyticsCollector
...
@@ -579,7 +597,7 @@ public class AnalyticsCollector
private
EventTime
generateEventTime
(
@Nullable
WindowAndMediaPeriodId
mediaPeriod
)
{
private
EventTime
generateEventTime
(
@Nullable
WindowAndMediaPeriodId
mediaPeriod
)
{
if
(
mediaPeriod
==
null
)
{
if
(
mediaPeriod
==
null
)
{
int
windowIndex
=
player
.
getCurrentWindowIndex
();
int
windowIndex
=
Assertions
.
checkNotNull
(
player
)
.
getCurrentWindowIndex
();
MediaPeriodId
mediaPeriodId
=
mediaPeriodQueueTracker
.
tryResolveWindowIndex
(
windowIndex
);
MediaPeriodId
mediaPeriodId
=
mediaPeriodQueueTracker
.
tryResolveWindowIndex
(
windowIndex
);
return
generateEventTime
(
windowIndex
,
mediaPeriodId
);
return
generateEventTime
(
windowIndex
,
mediaPeriodId
);
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleDecoder.java
View file @
f7ed789f
...
@@ -17,7 +17,7 @@ package com.google.android.exoplayer2.decoder;
...
@@ -17,7 +17,7 @@ package com.google.android.exoplayer2.decoder;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
import
java.util.
LinkedList
;
import
java.util.
ArrayDeque
;
/**
/**
* Base class for {@link Decoder}s that use their own decode thread.
* Base class for {@link Decoder}s that use their own decode thread.
...
@@ -28,8 +28,8 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
...
@@ -28,8 +28,8 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
private
final
Thread
decodeThread
;
private
final
Thread
decodeThread
;
private
final
Object
lock
;
private
final
Object
lock
;
private
final
LinkedList
<
I
>
queuedInputBuffers
;
private
final
ArrayDeque
<
I
>
queuedInputBuffers
;
private
final
LinkedList
<
O
>
queuedOutputBuffers
;
private
final
ArrayDeque
<
O
>
queuedOutputBuffers
;
private
final
I
[]
availableInputBuffers
;
private
final
I
[]
availableInputBuffers
;
private
final
O
[]
availableOutputBuffers
;
private
final
O
[]
availableOutputBuffers
;
...
@@ -48,8 +48,8 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
...
@@ -48,8 +48,8 @@ public abstract class SimpleDecoder<I extends DecoderInputBuffer, O extends Outp
*/
*/
protected
SimpleDecoder
(
I
[]
inputBuffers
,
O
[]
outputBuffers
)
{
protected
SimpleDecoder
(
I
[]
inputBuffers
,
O
[]
outputBuffers
)
{
lock
=
new
Object
();
lock
=
new
Object
();
queuedInputBuffers
=
new
LinkedList
<>();
queuedInputBuffers
=
new
ArrayDeque
<>();
queuedOutputBuffers
=
new
LinkedList
<>();
queuedOutputBuffers
=
new
ArrayDeque
<>();
availableInputBuffers
=
inputBuffers
;
availableInputBuffers
=
inputBuffers
;
availableInputBufferCount
=
inputBuffers
.
length
;
availableInputBufferCount
=
inputBuffers
.
length
;
for
(
int
i
=
0
;
i
<
availableInputBufferCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
availableInputBufferCount
;
i
++)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java
View file @
f7ed789f
...
@@ -108,7 +108,8 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
...
@@ -108,7 +108,8 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
@Override
@Override
public
byte
[]
executeProvisionRequest
(
UUID
uuid
,
ProvisionRequest
request
)
throws
IOException
{
public
byte
[]
executeProvisionRequest
(
UUID
uuid
,
ProvisionRequest
request
)
throws
IOException
{
String
url
=
request
.
getDefaultUrl
()
+
"&signedRequest="
+
new
String
(
request
.
getData
());
String
url
=
request
.
getDefaultUrl
()
+
"&signedRequest="
+
Util
.
fromUtf8Bytes
(
request
.
getData
());
return
executePost
(
dataSourceFactory
,
url
,
new
byte
[
0
],
null
);
return
executePost
(
dataSourceFactory
,
url
,
new
byte
[
0
],
null
);
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/DefaultEbmlReader.java
View file @
f7ed789f
...
@@ -24,7 +24,7 @@ import java.io.EOFException;
...
@@ -24,7 +24,7 @@ import java.io.EOFException;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.RetentionPolicy
;
import
java.util.
Stack
;
import
java.util.
ArrayDeque
;
/**
/**
* Default implementation of {@link EbmlReader}.
* Default implementation of {@link EbmlReader}.
...
@@ -46,15 +46,21 @@ import java.util.Stack;
...
@@ -46,15 +46,21 @@ import java.util.Stack;
private
static
final
int
VALID_FLOAT32_ELEMENT_SIZE_BYTES
=
4
;
private
static
final
int
VALID_FLOAT32_ELEMENT_SIZE_BYTES
=
4
;
private
static
final
int
VALID_FLOAT64_ELEMENT_SIZE_BYTES
=
8
;
private
static
final
int
VALID_FLOAT64_ELEMENT_SIZE_BYTES
=
8
;
private
final
byte
[]
scratch
=
new
byte
[
8
]
;
private
final
byte
[]
scratch
;
private
final
Stack
<
MasterElement
>
masterElementsStack
=
new
Stack
<>()
;
private
final
ArrayDeque
<
MasterElement
>
masterElementsStack
;
private
final
VarintReader
varintReader
=
new
VarintReader
()
;
private
final
VarintReader
varintReader
;
private
EbmlReaderOutput
output
;
private
EbmlReaderOutput
output
;
private
@ElementState
int
elementState
;
private
@ElementState
int
elementState
;
private
int
elementId
;
private
int
elementId
;
private
long
elementContentSize
;
private
long
elementContentSize
;
public
DefaultEbmlReader
()
{
scratch
=
new
byte
[
8
];
masterElementsStack
=
new
ArrayDeque
<>();
varintReader
=
new
VarintReader
();
}
@Override
@Override
public
void
init
(
EbmlReaderOutput
eventHandler
)
{
public
void
init
(
EbmlReaderOutput
eventHandler
)
{
this
.
output
=
eventHandler
;
this
.
output
=
eventHandler
;
...
@@ -100,7 +106,7 @@ import java.util.Stack;
...
@@ -100,7 +106,7 @@ import java.util.Stack;
case
EbmlReaderOutput
.
TYPE_MASTER
:
case
EbmlReaderOutput
.
TYPE_MASTER
:
long
elementContentPosition
=
input
.
getPosition
();
long
elementContentPosition
=
input
.
getPosition
();
long
elementEndPosition
=
elementContentPosition
+
elementContentSize
;
long
elementEndPosition
=
elementContentPosition
+
elementContentSize
;
masterElementsStack
.
add
(
new
MasterElement
(
elementId
,
elementEndPosition
));
masterElementsStack
.
push
(
new
MasterElement
(
elementId
,
elementEndPosition
));
output
.
startMasterElement
(
elementId
,
elementContentPosition
,
elementContentSize
);
output
.
startMasterElement
(
elementId
,
elementContentPosition
,
elementContentSize
);
elementState
=
ELEMENT_STATE_READ_ID
;
elementState
=
ELEMENT_STATE_READ_ID
;
return
true
;
return
true
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/Sniffer.java
View file @
f7ed789f
...
@@ -78,8 +78,9 @@ import java.io.IOException;
...
@@ -78,8 +78,9 @@ import java.io.IOException;
return
false
;
return
false
;
}
}
if
(
size
!=
0
)
{
if
(
size
!=
0
)
{
input
.
advancePeekPosition
((
int
)
size
);
int
sizeInt
=
(
int
)
size
;
peekLength
+=
size
;
input
.
advancePeekPosition
(
sizeInt
);
peekLength
+=
sizeInt
;
}
}
}
}
return
peekLength
==
headerStart
+
headerSize
;
return
peekLength
==
headerStart
+
headerSize
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FixedSampleSizeRechunker.java
View file @
f7ed789f
...
@@ -108,4 +108,7 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -108,4 +108,7 @@ import com.google.android.exoplayer2.util.Util;
return
new
Results
(
offsets
,
sizes
,
maximumSize
,
timestamps
,
flags
,
duration
);
return
new
Results
(
offsets
,
sizes
,
maximumSize
,
timestamps
,
flags
,
duration
);
}
}
private
FixedSampleSizeRechunker
()
{
// Prevent instantiation.
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
View file @
f7ed789f
...
@@ -50,7 +50,6 @@ import java.util.ArrayList;
...
@@ -50,7 +50,6 @@ import java.util.ArrayList;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Stack
;
import
java.util.UUID
;
import
java.util.UUID
;
/**
/**
...
@@ -141,7 +140,7 @@ public final class FragmentedMp4Extractor implements Extractor {
...
@@ -141,7 +140,7 @@ public final class FragmentedMp4Extractor implements Extractor {
// Parser state.
// Parser state.
private
final
ParsableByteArray
atomHeader
;
private
final
ParsableByteArray
atomHeader
;
private
final
byte
[]
extendedTypeScratch
;
private
final
byte
[]
extendedTypeScratch
;
private
final
Stack
<
ContainerAtom
>
containerAtoms
;
private
final
ArrayDeque
<
ContainerAtom
>
containerAtoms
;
private
final
ArrayDeque
<
MetadataSampleInfo
>
pendingMetadataSampleInfos
;
private
final
ArrayDeque
<
MetadataSampleInfo
>
pendingMetadataSampleInfos
;
private
final
@Nullable
TrackOutput
additionalEmsgTrackOutput
;
private
final
@Nullable
TrackOutput
additionalEmsgTrackOutput
;
...
@@ -257,7 +256,7 @@ public final class FragmentedMp4Extractor implements Extractor {
...
@@ -257,7 +256,7 @@ public final class FragmentedMp4Extractor implements Extractor {
nalPrefix
=
new
ParsableByteArray
(
5
);
nalPrefix
=
new
ParsableByteArray
(
5
);
nalBuffer
=
new
ParsableByteArray
();
nalBuffer
=
new
ParsableByteArray
();
extendedTypeScratch
=
new
byte
[
16
];
extendedTypeScratch
=
new
byte
[
16
];
containerAtoms
=
new
Stack
<>();
containerAtoms
=
new
ArrayDeque
<>();
pendingMetadataSampleInfos
=
new
ArrayDeque
<>();
pendingMetadataSampleInfos
=
new
ArrayDeque
<>();
trackBundles
=
new
SparseArray
<>();
trackBundles
=
new
SparseArray
<>();
durationUs
=
C
.
TIME_UNSET
;
durationUs
=
C
.
TIME_UNSET
;
...
@@ -390,7 +389,7 @@ public final class FragmentedMp4Extractor implements Extractor {
...
@@ -390,7 +389,7 @@ public final class FragmentedMp4Extractor implements Extractor {
if
(
shouldParseContainerAtom
(
atomType
))
{
if
(
shouldParseContainerAtom
(
atomType
))
{
long
endPosition
=
input
.
getPosition
()
+
atomSize
-
Atom
.
HEADER_SIZE
;
long
endPosition
=
input
.
getPosition
()
+
atomSize
-
Atom
.
HEADER_SIZE
;
containerAtoms
.
add
(
new
ContainerAtom
(
atomType
,
endPosition
));
containerAtoms
.
push
(
new
ContainerAtom
(
atomType
,
endPosition
));
if
(
atomSize
==
atomHeaderBytesRead
)
{
if
(
atomSize
==
atomHeaderBytesRead
)
{
processAtomEnded
(
endPosition
);
processAtomEnded
(
endPosition
);
}
else
{
}
else
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
View file @
f7ed789f
...
@@ -37,9 +37,9 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -37,9 +37,9 @@ import com.google.android.exoplayer2.util.Util;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.RetentionPolicy
;
import
java.util.ArrayDeque
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Stack
;
/**
/**
* Extracts data from the MP4 container format.
* Extracts data from the MP4 container format.
...
@@ -101,7 +101,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
...
@@ -101,7 +101,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
private
final
ParsableByteArray
nalLength
;
private
final
ParsableByteArray
nalLength
;
private
final
ParsableByteArray
atomHeader
;
private
final
ParsableByteArray
atomHeader
;
private
final
Stack
<
ContainerAtom
>
containerAtoms
;
private
final
ArrayDeque
<
ContainerAtom
>
containerAtoms
;
@State
private
int
parserState
;
@State
private
int
parserState
;
private
int
atomType
;
private
int
atomType
;
...
@@ -137,7 +137,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
...
@@ -137,7 +137,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
public
Mp4Extractor
(
@Flags
int
flags
)
{
public
Mp4Extractor
(
@Flags
int
flags
)
{
this
.
flags
=
flags
;
this
.
flags
=
flags
;
atomHeader
=
new
ParsableByteArray
(
Atom
.
LONG_HEADER_SIZE
);
atomHeader
=
new
ParsableByteArray
(
Atom
.
LONG_HEADER_SIZE
);
containerAtoms
=
new
Stack
<>();
containerAtoms
=
new
ArrayDeque
<>();
nalStartCode
=
new
ParsableByteArray
(
NalUnitUtil
.
NAL_START_CODE
);
nalStartCode
=
new
ParsableByteArray
(
NalUnitUtil
.
NAL_START_CODE
);
nalLength
=
new
ParsableByteArray
(
4
);
nalLength
=
new
ParsableByteArray
(
4
);
sampleTrackIndex
=
C
.
INDEX_UNSET
;
sampleTrackIndex
=
C
.
INDEX_UNSET
;
...
@@ -303,7 +303,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
...
@@ -303,7 +303,7 @@ public final class Mp4Extractor implements Extractor, SeekMap {
if
(
shouldParseContainerAtom
(
atomType
))
{
if
(
shouldParseContainerAtom
(
atomType
))
{
long
endPosition
=
input
.
getPosition
()
+
atomSize
-
atomHeaderBytesRead
;
long
endPosition
=
input
.
getPosition
()
+
atomSize
-
atomHeaderBytesRead
;
containerAtoms
.
add
(
new
ContainerAtom
(
atomType
,
endPosition
));
containerAtoms
.
push
(
new
ContainerAtom
(
atomType
,
endPosition
));
if
(
atomSize
==
atomHeaderBytesRead
)
{
if
(
atomSize
==
atomHeaderBytesRead
)
{
processAtomEnded
(
endPosition
);
processAtomEnded
(
endPosition
);
}
else
{
}
else
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/PsshAtomUtil.java
View file @
f7ed789f
...
@@ -49,6 +49,7 @@ public final class PsshAtomUtil {
...
@@ -49,6 +49,7 @@ public final class PsshAtomUtil {
* @param data The scheme specific data.
* @param data The scheme specific data.
* @return The PSSH atom.
* @return The PSSH atom.
*/
*/
@SuppressWarnings
(
"ParameterNotNullable"
)
public
static
byte
[]
buildPsshAtom
(
public
static
byte
[]
buildPsshAtom
(
UUID
systemId
,
@Nullable
UUID
[]
keyIds
,
@Nullable
byte
[]
data
)
{
UUID
systemId
,
@Nullable
UUID
[]
keyIds
,
@Nullable
byte
[]
data
)
{
boolean
buildV1Atom
=
keyIds
!=
null
;
boolean
buildV1Atom
=
keyIds
!=
null
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OpusReader.java
View file @
f7ed789f
...
@@ -130,6 +130,6 @@ import java.util.List;
...
@@ -130,6 +130,6 @@ import java.util.List;
}
else
{
}
else
{
length
=
10000
<<
length
;
length
=
10000
<<
length
;
}
}
return
frames
*
length
;
return
(
long
)
frames
*
length
;
}
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisUtil.java
View file @
f7ed789f
...
@@ -357,12 +357,12 @@ import java.util.Arrays;
...
@@ -357,12 +357,12 @@ import java.util.Arrays;
for
(
int
i
=
0
;
i
<
lengthMap
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
lengthMap
.
length
;
i
++)
{
if
(
isSparse
)
{
if
(
isSparse
)
{
if
(
bitArray
.
readBit
())
{
if
(
bitArray
.
readBit
())
{
lengthMap
[
i
]
=
bitArray
.
readBits
(
5
)
+
1
;
lengthMap
[
i
]
=
(
long
)
(
bitArray
.
readBits
(
5
)
+
1
)
;
}
else
{
// entry unused
}
else
{
// entry unused
lengthMap
[
i
]
=
0
;
lengthMap
[
i
]
=
0
;
}
}
}
else
{
// not sparse
}
else
{
// not sparse
lengthMap
[
i
]
=
bitArray
.
readBits
(
5
)
+
1
;
lengthMap
[
i
]
=
(
long
)
(
bitArray
.
readBits
(
5
)
+
1
)
;
}
}
}
}
}
else
{
}
else
{
...
@@ -392,7 +392,7 @@ import java.util.Arrays;
...
@@ -392,7 +392,7 @@ import java.util.Arrays;
lookupValuesCount
=
0
;
lookupValuesCount
=
0
;
}
}
}
else
{
}
else
{
lookupValuesCount
=
entries
*
dimensions
;
lookupValuesCount
=
(
long
)
entries
*
dimensions
;
}
}
// discard (no decoding required yet)
// discard (no decoding required yet)
bitArray
.
skipBits
((
int
)
(
lookupValuesCount
*
valueBits
));
bitArray
.
skipBits
((
int
)
(
lookupValuesCount
*
valueBits
));
...
@@ -407,6 +407,10 @@ import java.util.Arrays;
...
@@ -407,6 +407,10 @@ import java.util.Arrays;
return
(
long
)
Math
.
floor
(
Math
.
pow
(
entries
,
1
.
d
/
dimension
));
return
(
long
)
Math
.
floor
(
Math
.
pow
(
entries
,
1
.
d
/
dimension
));
}
}
private
VorbisUtil
()
{
// Prevent instantiation.
}
public
static
final
class
CodeBook
{
public
static
final
class
CodeBook
{
public
final
int
dimensions
;
public
final
int
dimensions
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/wav/WavHeaderReader.java
View file @
f7ed789f
...
@@ -25,7 +25,7 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -25,7 +25,7 @@ import com.google.android.exoplayer2.util.Util;
import
java.io.IOException
;
import
java.io.IOException
;
/** Reads a {@code WavHeader} from an input stream; supports resuming from input failures. */
/** Reads a {@code WavHeader} from an input stream; supports resuming from input failures. */
/*
package
*/
final
class
WavHeaderReader
{
/*
package
*/
final
class
WavHeaderReader
{
private
static
final
String
TAG
=
"WavHeaderReader"
;
private
static
final
String
TAG
=
"WavHeaderReader"
;
...
@@ -158,6 +158,10 @@ import java.io.IOException;
...
@@ -158,6 +158,10 @@ import java.io.IOException;
wavHeader
.
setDataBounds
(
input
.
getPosition
(),
chunkHeader
.
size
);
wavHeader
.
setDataBounds
(
input
.
getPosition
(),
chunkHeader
.
size
);
}
}
private
WavHeaderReader
()
{
// Prevent instantiation.
}
/** Container for a WAV chunk header. */
/** Container for a WAV chunk header. */
private
static
final
class
ChunkHeader
{
private
static
final
class
ChunkHeader
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java
View file @
f7ed789f
...
@@ -262,12 +262,23 @@ public final class DownloadManager {
...
@@ -262,12 +262,23 @@ public final class DownloadManager {
return
task
.
id
;
return
task
.
id
;
}
}
/** Returns the
current
number of tasks. */
/** Returns the number of tasks. */
public
int
getTaskCount
()
{
public
int
getTaskCount
()
{
Assertions
.
checkState
(!
released
);
Assertions
.
checkState
(!
released
);
return
tasks
.
size
();
return
tasks
.
size
();
}
}
/** Returns the number of download tasks. */
public
int
getDownloadCount
()
{
int
count
=
0
;
for
(
int
i
=
0
;
i
<
tasks
.
size
();
i
++)
{
if
(!
tasks
.
get
(
i
).
action
.
isRemoveAction
)
{
count
++;
}
}
return
count
;
}
/** Returns the state of a task, or null if no such task exists */
/** Returns the state of a task, or null if no such task exists */
public
@Nullable
TaskState
getTaskState
(
int
taskId
)
{
public
@Nullable
TaskState
getTaskState
(
int
taskId
)
{
Assertions
.
checkState
(!
released
);
Assertions
.
checkState
(!
released
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java
View file @
f7ed789f
...
@@ -160,9 +160,9 @@ public abstract class DownloadService extends Service {
...
@@ -160,9 +160,9 @@ public abstract class DownloadService extends Service {
* Starts the service, adding an action to be executed.
* Starts the service, adding an action to be executed.
*
*
* @param context A {@link Context}.
* @param context A {@link Context}.
* @param clazz The concrete download service
being targeted by the intent
.
* @param clazz The concrete download service
to be started
.
* @param downloadAction The action to be executed.
* @param downloadAction The action to be executed.
* @param foreground Whether th
is intent will be used to start the service
in the foreground.
* @param foreground Whether th
e service is started
in the foreground.
*/
*/
public
static
void
startWithAction
(
public
static
void
startWithAction
(
Context
context
,
Context
context
,
...
@@ -177,6 +177,33 @@ public abstract class DownloadService extends Service {
...
@@ -177,6 +177,33 @@ public abstract class DownloadService extends Service {
}
}
}
}
/**
* Starts the service without adding a new action. If there are any not finished actions and the
* requirements are met, the service resumes executing actions. Otherwise it stops immediately.
*
* @param context A {@link Context}.
* @param clazz The concrete download service to be started.
* @see #startForeground(Context, Class)
*/
public
static
void
start
(
Context
context
,
Class
<?
extends
DownloadService
>
clazz
)
{
context
.
startService
(
new
Intent
(
context
,
clazz
).
setAction
(
ACTION_INIT
));
}
/**
* Starts the service in the foreground without adding a new action. If there are any not finished
* actions and the requirements are met, the service resumes executing actions. Otherwise it stops
* immediately.
*
* @param context A {@link Context}.
* @param clazz The concrete download service to be started.
* @see #start(Context, Class)
*/
public
static
void
startForeground
(
Context
context
,
Class
<?
extends
DownloadService
>
clazz
)
{
Intent
intent
=
new
Intent
(
context
,
clazz
).
setAction
(
ACTION_INIT
).
putExtra
(
KEY_FOREGROUND
,
true
);
Util
.
startForegroundService
(
context
,
intent
);
}
@Override
@Override
public
void
onCreate
()
{
public
void
onCreate
()
{
logd
(
"onCreate"
);
logd
(
"onCreate"
);
...
@@ -187,17 +214,6 @@ public abstract class DownloadService extends Service {
...
@@ -187,17 +214,6 @@ public abstract class DownloadService extends Service {
downloadManager
=
getDownloadManager
();
downloadManager
=
getDownloadManager
();
downloadManagerListener
=
new
DownloadManagerListener
();
downloadManagerListener
=
new
DownloadManagerListener
();
downloadManager
.
addListener
(
downloadManagerListener
);
downloadManager
.
addListener
(
downloadManagerListener
);
RequirementsHelper
requirementsHelper
;
synchronized
(
requirementsHelpers
)
{
Class
<?
extends
DownloadService
>
clazz
=
getClass
();
requirementsHelper
=
requirementsHelpers
.
get
(
clazz
);
if
(
requirementsHelper
==
null
)
{
requirementsHelper
=
new
RequirementsHelper
(
this
,
getRequirements
(),
getScheduler
(),
clazz
);
requirementsHelpers
.
put
(
clazz
,
requirementsHelper
);
}
}
requirementsHelper
.
start
();
}
}
@Override
@Override
...
@@ -237,6 +253,7 @@ public abstract class DownloadService extends Service {
...
@@ -237,6 +253,7 @@ public abstract class DownloadService extends Service {
Log
.
e
(
TAG
,
"Ignoring unrecognized action: "
+
intentAction
);
Log
.
e
(
TAG
,
"Ignoring unrecognized action: "
+
intentAction
);
break
;
break
;
}
}
maybeStartWatchingRequirements
();
if
(
downloadManager
.
isIdle
())
{
if
(
downloadManager
.
isIdle
())
{
stop
();
stop
();
}
}
...
@@ -248,14 +265,7 @@ public abstract class DownloadService extends Service {
...
@@ -248,14 +265,7 @@ public abstract class DownloadService extends Service {
logd
(
"onDestroy"
);
logd
(
"onDestroy"
);
foregroundNotificationUpdater
.
stopPeriodicUpdates
();
foregroundNotificationUpdater
.
stopPeriodicUpdates
();
downloadManager
.
removeListener
(
downloadManagerListener
);
downloadManager
.
removeListener
(
downloadManagerListener
);
if
(
downloadManager
.
getTaskCount
()
==
0
)
{
maybeStopWatchingRequirements
();
synchronized
(
requirementsHelpers
)
{
RequirementsHelper
requirementsHelper
=
requirementsHelpers
.
remove
(
getClass
());
if
(
requirementsHelper
!=
null
)
{
requirementsHelper
.
stop
();
}
}
}
}
}
@Nullable
@Nullable
...
@@ -312,6 +322,31 @@ public abstract class DownloadService extends Service {
...
@@ -312,6 +322,31 @@ public abstract class DownloadService extends Service {
// Do nothing.
// Do nothing.
}
}
private
void
maybeStartWatchingRequirements
()
{
if
(
downloadManager
.
getDownloadCount
()
==
0
)
{
return
;
}
Class
<?
extends
DownloadService
>
clazz
=
getClass
();
RequirementsHelper
requirementsHelper
=
requirementsHelpers
.
get
(
clazz
);
if
(
requirementsHelper
==
null
)
{
requirementsHelper
=
new
RequirementsHelper
(
this
,
getRequirements
(),
getScheduler
(),
clazz
);
requirementsHelpers
.
put
(
clazz
,
requirementsHelper
);
requirementsHelper
.
start
();
logd
(
"started watching requirements"
);
}
}
private
void
maybeStopWatchingRequirements
()
{
if
(
downloadManager
.
getDownloadCount
()
>
0
)
{
return
;
}
RequirementsHelper
requirementsHelper
=
requirementsHelpers
.
remove
(
getClass
());
if
(
requirementsHelper
!=
null
)
{
requirementsHelper
.
stop
();
logd
(
"stopped watching requirements"
);
}
}
private
void
stop
()
{
private
void
stop
()
{
foregroundNotificationUpdater
.
stopPeriodicUpdates
();
foregroundNotificationUpdater
.
stopPeriodicUpdates
();
// Make sure startForeground is called before stopping. Workaround for [Internal: b/69424260].
// Make sure startForeground is called before stopping. Workaround for [Internal: b/69424260].
...
@@ -331,7 +366,7 @@ public abstract class DownloadService extends Service {
...
@@ -331,7 +366,7 @@ public abstract class DownloadService extends Service {
private
final
class
DownloadManagerListener
implements
DownloadManager
.
Listener
{
private
final
class
DownloadManagerListener
implements
DownloadManager
.
Listener
{
@Override
@Override
public
void
onInitialized
(
DownloadManager
downloadManager
)
{
public
void
onInitialized
(
DownloadManager
downloadManager
)
{
// Do nothing.
maybeStartWatchingRequirements
();
}
}
@Override
@Override
...
...
library/core/src/main/java/com/google/android/exoplayer2/offline/ProgressiveDownloader.java
View file @
f7ed789f
...
@@ -13,7 +13,7 @@
...
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* See the License for the specific language governing permissions and
* limitations under the License.
* limitations under the License.
*/
*/
package
com
.
google
.
android
.
exoplayer2
.
offline
;
package
com
.
google
.
android
.
exoplayer2
.
offline
;
import
android.net.Uri
;
import
android.net.Uri
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/offline/SegmentDownloader.java
View file @
f7ed789f
...
@@ -201,6 +201,8 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M, K>, K>
...
@@ -201,6 +201,8 @@ public abstract class SegmentDownloader<M extends FilterableManifest<M, K>, K>
throws
InterruptedException
,
IOException
;
throws
InterruptedException
,
IOException
;
/** Initializes the download, returning a list of {@link Segment}s that need to be downloaded. */
/** Initializes the download, returning a list of {@link Segment}s that need to be downloaded. */
// Writes to downloadedSegments and downloadedBytes are safe. See the comment on download().
@SuppressWarnings
(
"NonAtomicVolatileUpdate"
)
private
List
<
Segment
>
initDownload
()
throws
IOException
,
InterruptedException
{
private
List
<
Segment
>
initDownload
()
throws
IOException
,
InterruptedException
{
M
manifest
=
getManifest
(
dataSource
,
manifestUri
);
M
manifest
=
getManifest
(
dataSource
,
manifestUri
);
if
(!
streamKeys
.
isEmpty
())
{
if
(!
streamKeys
.
isEmpty
())
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
View file @
f7ed789f
...
@@ -19,7 +19,6 @@ import android.os.Handler;
...
@@ -19,7 +19,6 @@ import android.os.Handler;
import
android.os.Looper
;
import
android.os.Looper
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.support.annotation.Nullable
;
import
android.util.SparseIntArray
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
import
com.google.android.exoplayer2.ExoPlayer
;
import
com.google.android.exoplayer2.ExoPlayer
;
...
@@ -34,6 +33,7 @@ import java.util.ArrayList;
...
@@ -34,6 +33,7 @@ import java.util.ArrayList;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.IdentityHashMap
;
import
java.util.IdentityHashMap
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map
;
...
@@ -656,7 +656,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -656,7 +656,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
/* package */
static
final
class
MediaSourceHolder
implements
Comparable
<
MediaSourceHolder
>
{
/* package */
static
final
class
MediaSourceHolder
implements
Comparable
<
MediaSourceHolder
>
{
public
final
MediaSource
mediaSource
;
public
final
MediaSource
mediaSource
;
public
final
in
t
uid
;
public
final
Objec
t
uid
;
public
DeferredTimeline
timeline
;
public
DeferredTimeline
timeline
;
public
int
childIndex
;
public
int
childIndex
;
...
@@ -668,9 +668,9 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -668,9 +668,9 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
public
MediaSourceHolder
(
MediaSource
mediaSource
)
{
public
MediaSourceHolder
(
MediaSource
mediaSource
)
{
this
.
mediaSource
=
mediaSource
;
this
.
mediaSource
=
mediaSource
;
this
.
uid
=
System
.
identityHashCode
(
this
);
this
.
timeline
=
new
DeferredTimeline
();
this
.
timeline
=
new
DeferredTimeline
();
this
.
activeMediaPeriods
=
new
ArrayList
<>();
this
.
activeMediaPeriods
=
new
ArrayList
<>();
this
.
uid
=
new
Object
();
}
}
public
void
reset
(
int
childIndex
,
int
firstWindowIndexInChild
,
int
firstPeriodIndexInChild
)
{
public
void
reset
(
int
childIndex
,
int
firstWindowIndexInChild
,
int
firstPeriodIndexInChild
)
{
...
@@ -728,8 +728,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -728,8 +728,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
private
final
int
[]
firstPeriodInChildIndices
;
private
final
int
[]
firstPeriodInChildIndices
;
private
final
int
[]
firstWindowInChildIndices
;
private
final
int
[]
firstWindowInChildIndices
;
private
final
Timeline
[]
timelines
;
private
final
Timeline
[]
timelines
;
private
final
in
t
[]
uids
;
private
final
Objec
t
[]
uids
;
private
final
SparseIntArray
childIndexByUid
;
private
final
HashMap
<
Object
,
Integer
>
childIndexByUid
;
public
ConcatenatedTimeline
(
public
ConcatenatedTimeline
(
Collection
<
MediaSourceHolder
>
mediaSourceHolders
,
Collection
<
MediaSourceHolder
>
mediaSourceHolders
,
...
@@ -744,8 +744,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -744,8 +744,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
firstPeriodInChildIndices
=
new
int
[
childCount
];
firstPeriodInChildIndices
=
new
int
[
childCount
];
firstWindowInChildIndices
=
new
int
[
childCount
];
firstWindowInChildIndices
=
new
int
[
childCount
];
timelines
=
new
Timeline
[
childCount
];
timelines
=
new
Timeline
[
childCount
];
uids
=
new
in
t
[
childCount
];
uids
=
new
Objec
t
[
childCount
];
childIndexByUid
=
new
SparseIntArray
();
childIndexByUid
=
new
HashMap
<>
();
int
index
=
0
;
int
index
=
0
;
for
(
MediaSourceHolder
mediaSourceHolder
:
mediaSourceHolders
)
{
for
(
MediaSourceHolder
mediaSourceHolder
:
mediaSourceHolders
)
{
timelines
[
index
]
=
mediaSourceHolder
.
timeline
;
timelines
[
index
]
=
mediaSourceHolder
.
timeline
;
...
@@ -768,11 +768,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -768,11 +768,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
@Override
@Override
protected
int
getChildIndexByChildUid
(
Object
childUid
)
{
protected
int
getChildIndexByChildUid
(
Object
childUid
)
{
if
(!(
childUid
instanceof
Integer
))
{
Integer
index
=
childIndexByUid
.
get
(
childUid
);
return
C
.
INDEX_UNSET
;
return
index
==
null
?
C
.
INDEX_UNSET
:
index
;
}
int
index
=
childIndexByUid
.
get
((
int
)
childUid
,
-
1
);
return
index
==
-
1
?
C
.
INDEX_UNSET
:
index
;
}
}
@Override
@Override
...
@@ -804,7 +801,6 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -804,7 +801,6 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
public
int
getPeriodCount
()
{
public
int
getPeriodCount
()
{
return
periodCount
;
return
periodCount
;
}
}
}
}
/**
/**
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/DynamicConcatenatingMediaSource.java
View file @
f7ed789f
...
@@ -19,19 +19,25 @@ package com.google.android.exoplayer2.source;
...
@@ -19,19 +19,25 @@ package com.google.android.exoplayer2.source;
@Deprecated
@Deprecated
public
final
class
DynamicConcatenatingMediaSource
extends
ConcatenatingMediaSource
{
public
final
class
DynamicConcatenatingMediaSource
extends
ConcatenatingMediaSource
{
/** @deprecated Use {@link ConcatenatingMediaSource#ConcatenatingMediaSource()} instead. */
/**
* @deprecated Use {@link ConcatenatingMediaSource#ConcatenatingMediaSource(MediaSource...)}
* instead.
*/
@Deprecated
@Deprecated
public
DynamicConcatenatingMediaSource
()
{}
public
DynamicConcatenatingMediaSource
()
{}
/** @deprecated Use {@link ConcatenatingMediaSource#ConcatenatingMediaSource(boolean)} instead. */
/**
* @deprecated Use {@link ConcatenatingMediaSource#ConcatenatingMediaSource(boolean,
* MediaSource...)} instead.
*/
@Deprecated
@Deprecated
public
DynamicConcatenatingMediaSource
(
boolean
isAtomic
)
{
public
DynamicConcatenatingMediaSource
(
boolean
isAtomic
)
{
super
(
isAtomic
);
super
(
isAtomic
);
}
}
/**
/**
* @deprecated Use {@link ConcatenatingMediaSource#ConcatenatingMediaSource(boolean,
* @deprecated Use {@link ConcatenatingMediaSource#ConcatenatingMediaSource(boolean,
ShuffleOrder,
*
ShuffleOrder
)} instead.
*
MediaSource...
)} instead.
*/
*/
@Deprecated
@Deprecated
public
DynamicConcatenatingMediaSource
(
boolean
isAtomic
,
ShuffleOrder
shuffleOrder
)
{
public
DynamicConcatenatingMediaSource
(
boolean
isAtomic
,
ShuffleOrder
shuffleOrder
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java
View file @
f7ed789f
...
@@ -90,7 +90,7 @@ import java.util.Arrays;
...
@@ -90,7 +90,7 @@ import java.util.Arrays;
private
final
Runnable
onContinueLoadingRequestedRunnable
;
private
final
Runnable
onContinueLoadingRequestedRunnable
;
private
final
Handler
handler
;
private
final
Handler
handler
;
private
Callback
callback
;
private
@Nullable
Callback
callback
;
private
SeekMap
seekMap
;
private
SeekMap
seekMap
;
private
SampleQueue
[]
sampleQueues
;
private
SampleQueue
[]
sampleQueues
;
private
int
[]
sampleQueueTrackIds
;
private
int
[]
sampleQueueTrackIds
;
...
@@ -190,6 +190,7 @@ import java.util.Arrays;
...
@@ -190,6 +190,7 @@ import java.util.Arrays;
}
}
loader
.
release
(
this
);
loader
.
release
(
this
);
handler
.
removeCallbacksAndMessages
(
null
);
handler
.
removeCallbacksAndMessages
(
null
);
callback
=
null
;
released
=
true
;
released
=
true
;
eventDispatcher
.
mediaPeriodReleased
();
eventDispatcher
.
mediaPeriodReleased
();
}
}
...
@@ -833,11 +834,6 @@ import java.util.Arrays;
...
@@ -833,11 +834,6 @@ import java.util.Arrays;
}
}
@Override
@Override
public
boolean
isLoadCanceled
()
{
return
loadCanceled
;
}
@Override
public
void
load
()
throws
IOException
,
InterruptedException
{
public
void
load
()
throws
IOException
,
InterruptedException
{
int
result
=
Extractor
.
RESULT_CONTINUE
;
int
result
=
Extractor
.
RESULT_CONTINUE
;
while
(
result
==
Extractor
.
RESULT_CONTINUE
&&
!
loadCanceled
)
{
while
(
result
==
Extractor
.
RESULT_CONTINUE
&&
!
loadCanceled
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/SingleSampleMediaPeriod.java
View file @
f7ed789f
...
@@ -349,11 +349,6 @@ import java.util.Arrays;
...
@@ -349,11 +349,6 @@ import java.util.Arrays;
}
}
@Override
@Override
public
boolean
isLoadCanceled
()
{
return
false
;
}
@Override
public
void
load
()
throws
IOException
,
InterruptedException
{
public
void
load
()
throws
IOException
,
InterruptedException
{
// We always load from the beginning, so reset the sampleSize to 0.
// We always load from the beginning, so reset the sampleSize to 0.
sampleSize
=
0
;
sampleSize
=
0
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/TrackGroup.java
View file @
f7ed789f
...
@@ -72,11 +72,14 @@ public final class TrackGroup implements Parcelable {
...
@@ -72,11 +72,14 @@ public final class TrackGroup implements Parcelable {
}
}
/**
/**
* Returns the index of the track with the given format in the group.
* Returns the index of the track with the given format in the group. The format is located by
* identity so, for example, {@code group.indexOf(group.getFormat(index)) == index} even if
* multiple tracks have formats that contain the same values.
*
*
* @param format The format.
* @param format The format.
* @return The index of the track, or {@link C#INDEX_UNSET} if no such track exists.
* @return The index of the track, or {@link C#INDEX_UNSET} if no such track exists.
*/
*/
@SuppressWarnings
(
"ReferenceEquality"
)
public
int
indexOf
(
Format
format
)
{
public
int
indexOf
(
Format
format
)
{
for
(
int
i
=
0
;
i
<
formats
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
formats
.
length
;
i
++)
{
if
(
format
==
formats
[
i
])
{
if
(
format
==
formats
[
i
])
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/BaseMediaChunk.java
View file @
f7ed789f
...
@@ -44,7 +44,7 @@ public abstract class BaseMediaChunk extends MediaChunk {
...
@@ -44,7 +44,7 @@ public abstract class BaseMediaChunk extends MediaChunk {
* @param endTimeUs The end time of the media contained by the chunk, in microseconds.
* @param endTimeUs The end time of the media contained by the chunk, in microseconds.
* @param seekTimeUs The media time from which output will begin, or {@link C#TIME_UNSET} if the
* @param seekTimeUs The media time from which output will begin, or {@link C#TIME_UNSET} if the
* whole chunk should be output.
* whole chunk should be output.
* @param chunkIndex The index of the chunk.
* @param chunkIndex The index of the chunk
, or {@link C#INDEX_UNSET} if it is not known
.
*/
*/
public
BaseMediaChunk
(
public
BaseMediaChunk
(
DataSource
dataSource
,
DataSource
dataSource
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/BaseMediaChunkOutput.java
View file @
f7ed789f
...
@@ -21,10 +21,8 @@ import com.google.android.exoplayer2.extractor.TrackOutput;
...
@@ -21,10 +21,8 @@ import com.google.android.exoplayer2.extractor.TrackOutput;
import
com.google.android.exoplayer2.source.SampleQueue
;
import
com.google.android.exoplayer2.source.SampleQueue
;
import
com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper.TrackOutputProvider
;
import
com.google.android.exoplayer2.source.chunk.ChunkExtractorWrapper.TrackOutputProvider
;
/**
/** An output for {@link BaseMediaChunk}s. */
* An output for {@link BaseMediaChunk}s.
public
final
class
BaseMediaChunkOutput
implements
TrackOutputProvider
{
*/
/* package */
final
class
BaseMediaChunkOutput
implements
TrackOutputProvider
{
private
static
final
String
TAG
=
"BaseMediaChunkOutput"
;
private
static
final
String
TAG
=
"BaseMediaChunkOutput"
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/ContainerMediaChunk.java
View file @
f7ed789f
...
@@ -49,7 +49,7 @@ public class ContainerMediaChunk extends BaseMediaChunk {
...
@@ -49,7 +49,7 @@ public class ContainerMediaChunk extends BaseMediaChunk {
* @param endTimeUs The end time of the media contained by the chunk, in microseconds.
* @param endTimeUs The end time of the media contained by the chunk, in microseconds.
* @param seekTimeUs The media time from which output will begin, or {@link C#TIME_UNSET} if the
* @param seekTimeUs The media time from which output will begin, or {@link C#TIME_UNSET} if the
* whole chunk should be output.
* whole chunk should be output.
* @param chunkIndex The index of the chunk.
* @param chunkIndex The index of the chunk
, or {@link C#INDEX_UNSET} if it is not known
.
* @param chunkCount The number of chunks in the underlying media that are spanned by this
* @param chunkCount The number of chunks in the underlying media that are spanned by this
* instance. Normally equal to one, but may be larger if multiple chunks as defined by the
* instance. Normally equal to one, but may be larger if multiple chunks as defined by the
* underlying media are being merged into a single load.
* underlying media are being merged into a single load.
...
@@ -106,11 +106,6 @@ public class ContainerMediaChunk extends BaseMediaChunk {
...
@@ -106,11 +106,6 @@ public class ContainerMediaChunk extends BaseMediaChunk {
loadCanceled
=
true
;
loadCanceled
=
true
;
}
}
@Override
public
final
boolean
isLoadCanceled
()
{
return
loadCanceled
;
}
@SuppressWarnings
(
"NonAtomicVolatileUpdate"
)
@SuppressWarnings
(
"NonAtomicVolatileUpdate"
)
@Override
@Override
public
final
void
load
()
throws
IOException
,
InterruptedException
{
public
final
void
load
()
throws
IOException
,
InterruptedException
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/DataChunk.java
View file @
f7ed789f
...
@@ -76,11 +76,6 @@ public abstract class DataChunk extends Chunk {
...
@@ -76,11 +76,6 @@ public abstract class DataChunk extends Chunk {
}
}
@Override
@Override
public
final
boolean
isLoadCanceled
()
{
return
loadCanceled
;
}
@Override
public
final
void
load
()
throws
IOException
,
InterruptedException
{
public
final
void
load
()
throws
IOException
,
InterruptedException
{
try
{
try
{
dataSource
.
open
(
dataSpec
);
dataSource
.
open
(
dataSpec
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/InitializationChunk.java
View file @
f7ed789f
...
@@ -69,11 +69,6 @@ public final class InitializationChunk extends Chunk {
...
@@ -69,11 +69,6 @@ public final class InitializationChunk extends Chunk {
loadCanceled
=
true
;
loadCanceled
=
true
;
}
}
@Override
public
boolean
isLoadCanceled
()
{
return
loadCanceled
;
}
@SuppressWarnings
(
"NonAtomicVolatileUpdate"
)
@SuppressWarnings
(
"NonAtomicVolatileUpdate"
)
@Override
@Override
public
void
load
()
throws
IOException
,
InterruptedException
{
public
void
load
()
throws
IOException
,
InterruptedException
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/MediaChunk.java
View file @
f7ed789f
...
@@ -26,7 +26,7 @@ import com.google.android.exoplayer2.util.Assertions;
...
@@ -26,7 +26,7 @@ import com.google.android.exoplayer2.util.Assertions;
*/
*/
public
abstract
class
MediaChunk
extends
Chunk
{
public
abstract
class
MediaChunk
extends
Chunk
{
/** The chunk index. */
/** The chunk index
, or {@link C#INDEX_UNSET} if it is not known
. */
public
final
long
chunkIndex
;
public
final
long
chunkIndex
;
/**
/**
...
@@ -37,7 +37,7 @@ public abstract class MediaChunk extends Chunk {
...
@@ -37,7 +37,7 @@ public abstract class MediaChunk extends Chunk {
* @param trackSelectionData See {@link #trackSelectionData}.
* @param trackSelectionData See {@link #trackSelectionData}.
* @param startTimeUs The start time of the media contained by the chunk, in microseconds.
* @param startTimeUs The start time of the media contained by the chunk, in microseconds.
* @param endTimeUs The end time of the media contained by the chunk, in microseconds.
* @param endTimeUs The end time of the media contained by the chunk, in microseconds.
* @param chunkIndex The index of the chunk.
* @param chunkIndex The index of the chunk
, or {@link C#INDEX_UNSET} if it is not known
.
*/
*/
public
MediaChunk
(
public
MediaChunk
(
DataSource
dataSource
,
DataSource
dataSource
,
...
@@ -54,9 +54,9 @@ public abstract class MediaChunk extends Chunk {
...
@@ -54,9 +54,9 @@ public abstract class MediaChunk extends Chunk {
this
.
chunkIndex
=
chunkIndex
;
this
.
chunkIndex
=
chunkIndex
;
}
}
/** Returns the next chunk index. */
/** Returns the next chunk index
or {@link C#INDEX_UNSET} if it is not known
. */
public
long
getNextChunkIndex
()
{
public
long
getNextChunkIndex
()
{
return
chunkIndex
+
1
;
return
chunkIndex
!=
C
.
INDEX_UNSET
?
chunkIndex
+
1
:
C
.
INDEX_UNSET
;
}
}
/**
/**
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/chunk/SingleSampleMediaChunk.java
View file @
f7ed789f
...
@@ -34,7 +34,6 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
...
@@ -34,7 +34,6 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
private
final
Format
sampleFormat
;
private
final
Format
sampleFormat
;
private
volatile
int
bytesLoaded
;
private
volatile
int
bytesLoaded
;
private
volatile
boolean
loadCanceled
;
private
volatile
boolean
loadCompleted
;
private
volatile
boolean
loadCompleted
;
/**
/**
...
@@ -45,7 +44,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
...
@@ -45,7 +44,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
* @param trackSelectionData See {@link #trackSelectionData}.
* @param trackSelectionData See {@link #trackSelectionData}.
* @param startTimeUs The start time of the media contained by the chunk, in microseconds.
* @param startTimeUs The start time of the media contained by the chunk, in microseconds.
* @param endTimeUs The end time of the media contained by the chunk, in microseconds.
* @param endTimeUs The end time of the media contained by the chunk, in microseconds.
* @param chunkIndex The index of the chunk.
* @param chunkIndex The index of the chunk
, or {@link C#INDEX_UNSET} if it is not known
.
* @param trackType The type of the chunk. Typically one of the {@link C} {@code TRACK_TYPE_*}
* @param trackType The type of the chunk. Typically one of the {@link C} {@code TRACK_TYPE_*}
* constants.
* constants.
* @param sampleFormat The {@link Format} of the sample in the chunk.
* @param sampleFormat The {@link Format} of the sample in the chunk.
...
@@ -90,12 +89,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
...
@@ -90,12 +89,7 @@ public final class SingleSampleMediaChunk extends BaseMediaChunk {
@Override
@Override
public
void
cancelLoad
()
{
public
void
cancelLoad
()
{
loadCanceled
=
true
;
// Do nothing.
}
@Override
public
boolean
isLoadCanceled
()
{
return
loadCanceled
;
}
}
@SuppressWarnings
(
"NonAtomicVolatileUpdate"
)
@SuppressWarnings
(
"NonAtomicVolatileUpdate"
)
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java
View file @
f7ed789f
...
@@ -374,6 +374,9 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -374,6 +374,9 @@ public final class Cea608Decoder extends CeaDecoder {
private
void
handleMidrowCtrl
(
byte
cc2
)
{
private
void
handleMidrowCtrl
(
byte
cc2
)
{
// TODO: support the extended styles (i.e. backgrounds and transparencies)
// TODO: support the extended styles (i.e. backgrounds and transparencies)
// A midrow control code advances the cursor.
currentCueBuilder
.
append
(
' '
);
// cc2 - 0|0|1|0|ATRBT|U
// cc2 - 0|0|1|0|ATRBT|U
// ATRBT is the 3-byte encoded attribute, and U is the underline toggle
// ATRBT is the 3-byte encoded attribute, and U is the underline toggle
boolean
isUnderlined
=
(
cc2
&
0x01
)
==
0x01
;
boolean
isUnderlined
=
(
cc2
&
0x01
)
==
0x01
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java
View file @
f7ed789f
...
@@ -38,7 +38,6 @@ import com.google.android.exoplayer2.util.ParsableBitArray;
...
@@ -38,7 +38,6 @@ import com.google.android.exoplayer2.util.ParsableBitArray;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.List
;
/**
/**
...
@@ -196,7 +195,10 @@ public final class Cea708Decoder extends CeaDecoder {
...
@@ -196,7 +195,10 @@ public final class Cea708Decoder extends CeaDecoder {
@Override
@Override
protected
void
decode
(
SubtitleInputBuffer
inputBuffer
)
{
protected
void
decode
(
SubtitleInputBuffer
inputBuffer
)
{
ccData
.
reset
(
inputBuffer
.
data
.
array
(),
inputBuffer
.
data
.
limit
());
// Subtitle input buffers are non-direct and the position is zero, so calling array() is safe.
@SuppressWarnings
(
"ByteBufferBackingArray"
)
byte
[]
inputBufferData
=
inputBuffer
.
data
.
array
();
ccData
.
reset
(
inputBufferData
,
inputBuffer
.
data
.
limit
());
while
(
ccData
.
bytesLeft
()
>=
3
)
{
while
(
ccData
.
bytesLeft
()
>=
3
)
{
int
ccTypeAndValid
=
(
ccData
.
readUnsignedByte
()
&
0x07
);
int
ccTypeAndValid
=
(
ccData
.
readUnsignedByte
()
&
0x07
);
...
@@ -879,7 +881,7 @@ public final class Cea708Decoder extends CeaDecoder {
...
@@ -879,7 +881,7 @@ public final class Cea708Decoder extends CeaDecoder {
private
int
row
;
private
int
row
;
public
CueBuilder
()
{
public
CueBuilder
()
{
rolledUpCaptions
=
new
Linked
List
<>();
rolledUpCaptions
=
new
Array
List
<>();
captionStringBuilder
=
new
SpannableStringBuilder
();
captionStringBuilder
=
new
SpannableStringBuilder
();
reset
();
reset
();
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/cea/CeaDecoder.java
View file @
f7ed789f
...
@@ -24,7 +24,7 @@ import com.google.android.exoplayer2.text.SubtitleDecoderException;
...
@@ -24,7 +24,7 @@ import com.google.android.exoplayer2.text.SubtitleDecoderException;
import
com.google.android.exoplayer2.text.SubtitleInputBuffer
;
import
com.google.android.exoplayer2.text.SubtitleInputBuffer
;
import
com.google.android.exoplayer2.text.SubtitleOutputBuffer
;
import
com.google.android.exoplayer2.text.SubtitleOutputBuffer
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
import
java.util.
LinkedList
;
import
java.util.
ArrayDeque
;
import
java.util.PriorityQueue
;
import
java.util.PriorityQueue
;
/**
/**
...
@@ -35,8 +35,8 @@ import java.util.PriorityQueue;
...
@@ -35,8 +35,8 @@ import java.util.PriorityQueue;
private
static
final
int
NUM_INPUT_BUFFERS
=
10
;
private
static
final
int
NUM_INPUT_BUFFERS
=
10
;
private
static
final
int
NUM_OUTPUT_BUFFERS
=
2
;
private
static
final
int
NUM_OUTPUT_BUFFERS
=
2
;
private
final
LinkedList
<
CeaInputBuffer
>
availableInputBuffers
;
private
final
ArrayDeque
<
CeaInputBuffer
>
availableInputBuffers
;
private
final
LinkedList
<
SubtitleOutputBuffer
>
availableOutputBuffers
;
private
final
ArrayDeque
<
SubtitleOutputBuffer
>
availableOutputBuffers
;
private
final
PriorityQueue
<
CeaInputBuffer
>
queuedInputBuffers
;
private
final
PriorityQueue
<
CeaInputBuffer
>
queuedInputBuffers
;
private
CeaInputBuffer
dequeuedInputBuffer
;
private
CeaInputBuffer
dequeuedInputBuffer
;
...
@@ -44,11 +44,11 @@ import java.util.PriorityQueue;
...
@@ -44,11 +44,11 @@ import java.util.PriorityQueue;
private
long
queuedInputBufferCount
;
private
long
queuedInputBufferCount
;
public
CeaDecoder
()
{
public
CeaDecoder
()
{
availableInputBuffers
=
new
LinkedList
<>();
availableInputBuffers
=
new
ArrayDeque
<>();
for
(
int
i
=
0
;
i
<
NUM_INPUT_BUFFERS
;
i
++)
{
for
(
int
i
=
0
;
i
<
NUM_INPUT_BUFFERS
;
i
++)
{
availableInputBuffers
.
add
(
new
CeaInputBuffer
());
availableInputBuffers
.
add
(
new
CeaInputBuffer
());
}
}
availableOutputBuffers
=
new
LinkedList
<>();
availableOutputBuffers
=
new
ArrayDeque
<>();
for
(
int
i
=
0
;
i
<
NUM_OUTPUT_BUFFERS
;
i
++)
{
for
(
int
i
=
0
;
i
<
NUM_OUTPUT_BUFFERS
;
i
++)
{
availableOutputBuffers
.
add
(
new
CeaOutputBuffer
());
availableOutputBuffers
.
add
(
new
CeaOutputBuffer
());
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java
View file @
f7ed789f
...
@@ -62,7 +62,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
...
@@ -62,7 +62,7 @@ public final class SsaDecoder extends SimpleSubtitleDecoder {
super
(
"SsaDecoder"
);
super
(
"SsaDecoder"
);
if
(
initializationData
!=
null
&&
!
initializationData
.
isEmpty
())
{
if
(
initializationData
!=
null
&&
!
initializationData
.
isEmpty
())
{
haveInitializationData
=
true
;
haveInitializationData
=
true
;
String
formatLine
=
new
String
(
initializationData
.
get
(
0
));
String
formatLine
=
Util
.
fromUtf8Bytes
(
initializationData
.
get
(
0
));
Assertions
.
checkArgument
(
formatLine
.
startsWith
(
FORMAT_LINE_PREFIX
));
Assertions
.
checkArgument
(
formatLine
.
startsWith
(
FORMAT_LINE_PREFIX
));
parseFormatLine
(
formatLine
);
parseFormatLine
(
formatLine
);
parseHeader
(
new
ParsableByteArray
(
initializationData
.
get
(
1
)));
parseHeader
(
new
ParsableByteArray
(
initializationData
.
get
(
1
)));
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java
View file @
f7ed789f
...
@@ -26,8 +26,8 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -26,8 +26,8 @@ import com.google.android.exoplayer2.util.Util;
import
com.google.android.exoplayer2.util.XmlPullParserUtil
;
import
com.google.android.exoplayer2.util.XmlPullParserUtil
;
import
java.io.ByteArrayInputStream
;
import
java.io.ByteArrayInputStream
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.util.ArrayDeque
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.LinkedList
;
import
java.util.Map
;
import
java.util.Map
;
import
java.util.regex.Matcher
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
java.util.regex.Pattern
;
...
@@ -109,13 +109,13 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
...
@@ -109,13 +109,13 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
ByteArrayInputStream
inputStream
=
new
ByteArrayInputStream
(
bytes
,
0
,
length
);
ByteArrayInputStream
inputStream
=
new
ByteArrayInputStream
(
bytes
,
0
,
length
);
xmlParser
.
setInput
(
inputStream
,
null
);
xmlParser
.
setInput
(
inputStream
,
null
);
TtmlSubtitle
ttmlSubtitle
=
null
;
TtmlSubtitle
ttmlSubtitle
=
null
;
LinkedList
<
TtmlNode
>
nodeStack
=
new
LinkedList
<>();
ArrayDeque
<
TtmlNode
>
nodeStack
=
new
ArrayDeque
<>();
int
unsupportedNodeDepth
=
0
;
int
unsupportedNodeDepth
=
0
;
int
eventType
=
xmlParser
.
getEventType
();
int
eventType
=
xmlParser
.
getEventType
();
FrameAndTickRate
frameAndTickRate
=
DEFAULT_FRAME_AND_TICK_RATE
;
FrameAndTickRate
frameAndTickRate
=
DEFAULT_FRAME_AND_TICK_RATE
;
CellResolution
cellResolution
=
DEFAULT_CELL_RESOLUTION
;
CellResolution
cellResolution
=
DEFAULT_CELL_RESOLUTION
;
while
(
eventType
!=
XmlPullParser
.
END_DOCUMENT
)
{
while
(
eventType
!=
XmlPullParser
.
END_DOCUMENT
)
{
TtmlNode
parent
=
nodeStack
.
peek
Last
();
TtmlNode
parent
=
nodeStack
.
peek
();
if
(
unsupportedNodeDepth
==
0
)
{
if
(
unsupportedNodeDepth
==
0
)
{
String
name
=
xmlParser
.
getName
();
String
name
=
xmlParser
.
getName
();
if
(
eventType
==
XmlPullParser
.
START_TAG
)
{
if
(
eventType
==
XmlPullParser
.
START_TAG
)
{
...
@@ -131,7 +131,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
...
@@ -131,7 +131,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
}
else
{
}
else
{
try
{
try
{
TtmlNode
node
=
parseNode
(
xmlParser
,
parent
,
regionMap
,
frameAndTickRate
);
TtmlNode
node
=
parseNode
(
xmlParser
,
parent
,
regionMap
,
frameAndTickRate
);
nodeStack
.
addLast
(
node
);
nodeStack
.
push
(
node
);
if
(
parent
!=
null
)
{
if
(
parent
!=
null
)
{
parent
.
addChild
(
node
);
parent
.
addChild
(
node
);
}
}
...
@@ -145,9 +145,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
...
@@ -145,9 +145,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
parent
.
addChild
(
TtmlNode
.
buildTextNode
(
xmlParser
.
getText
()));
parent
.
addChild
(
TtmlNode
.
buildTextNode
(
xmlParser
.
getText
()));
}
else
if
(
eventType
==
XmlPullParser
.
END_TAG
)
{
}
else
if
(
eventType
==
XmlPullParser
.
END_TAG
)
{
if
(
xmlParser
.
getName
().
equals
(
TtmlNode
.
TAG_TT
))
{
if
(
xmlParser
.
getName
().
equals
(
TtmlNode
.
TAG_TT
))
{
ttmlSubtitle
=
new
TtmlSubtitle
(
nodeStack
.
getLast
(),
globalStyles
,
regionMap
);
ttmlSubtitle
=
new
TtmlSubtitle
(
nodeStack
.
peek
(),
globalStyles
,
regionMap
);
}
}
nodeStack
.
removeLast
();
nodeStack
.
pop
();
}
}
}
else
{
}
else
{
if
(
eventType
==
XmlPullParser
.
START_TAG
)
{
if
(
eventType
==
XmlPullParser
.
START_TAG
)
{
...
@@ -178,7 +178,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
...
@@ -178,7 +178,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
float
frameRateMultiplier
=
1
;
float
frameRateMultiplier
=
1
;
String
frameRateMultiplierString
=
xmlParser
.
getAttributeValue
(
TTP
,
"frameRateMultiplier"
);
String
frameRateMultiplierString
=
xmlParser
.
getAttributeValue
(
TTP
,
"frameRateMultiplier"
);
if
(
frameRateMultiplierString
!=
null
)
{
if
(
frameRateMultiplierString
!=
null
)
{
String
[]
parts
=
frameRateMultiplierString
.
split
(
" "
);
String
[]
parts
=
Util
.
split
(
frameRateMultiplierString
,
" "
);
if
(
parts
.
length
!=
2
)
{
if
(
parts
.
length
!=
2
)
{
throw
new
SubtitleDecoderException
(
"frameRateMultiplier doesn't have 2 parts"
);
throw
new
SubtitleDecoderException
(
"frameRateMultiplier doesn't have 2 parts"
);
}
}
...
@@ -354,7 +354,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
...
@@ -354,7 +354,8 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
}
}
private
String
[]
parseStyleIds
(
String
parentStyleIds
)
{
private
String
[]
parseStyleIds
(
String
parentStyleIds
)
{
return
parentStyleIds
.
split
(
"\\s+"
);
parentStyleIds
=
parentStyleIds
.
trim
();
return
parentStyleIds
.
isEmpty
()
?
new
String
[
0
]
:
Util
.
split
(
parentStyleIds
,
"\\s+"
);
}
}
private
TtmlStyle
parseStyleAttributes
(
XmlPullParser
parser
,
TtmlStyle
style
)
{
private
TtmlStyle
parseStyleAttributes
(
XmlPullParser
parser
,
TtmlStyle
style
)
{
...
@@ -531,7 +532,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
...
@@ -531,7 +532,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
private
static
void
parseFontSize
(
String
expression
,
TtmlStyle
out
)
throws
private
static
void
parseFontSize
(
String
expression
,
TtmlStyle
out
)
throws
SubtitleDecoderException
{
SubtitleDecoderException
{
String
[]
expressions
=
expression
.
split
(
"\\s+"
);
String
[]
expressions
=
Util
.
split
(
expression
,
"\\s+"
);
Matcher
matcher
;
Matcher
matcher
;
if
(
expressions
.
length
==
1
)
{
if
(
expressions
.
length
==
1
)
{
matcher
=
FONT_SIZE
.
matcher
(
expression
);
matcher
=
FONT_SIZE
.
matcher
(
expression
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/tx3g/Tx3gDecoder.java
View file @
f7ed789f
...
@@ -92,7 +92,8 @@ public final class Tx3gDecoder extends SimpleSubtitleDecoder {
...
@@ -92,7 +92,8 @@ public final class Tx3gDecoder extends SimpleSubtitleDecoder {
|
((
initializationBytes
[
27
]
&
0xFF
)
<<
16
)
|
((
initializationBytes
[
27
]
&
0xFF
)
<<
16
)
|
((
initializationBytes
[
28
]
&
0xFF
)
<<
8
)
|
((
initializationBytes
[
28
]
&
0xFF
)
<<
8
)
|
(
initializationBytes
[
29
]
&
0xFF
);
|
(
initializationBytes
[
29
]
&
0xFF
);
String
fontFamily
=
new
String
(
initializationBytes
,
43
,
initializationBytes
.
length
-
43
);
String
fontFamily
=
Util
.
fromUtf8Bytes
(
initializationBytes
,
43
,
initializationBytes
.
length
-
43
);
defaultFontFamily
=
TX3G_SERIF
.
equals
(
fontFamily
)
?
C
.
SERIF_NAME
:
C
.
SANS_SERIF_NAME
;
defaultFontFamily
=
TX3G_SERIF
.
equals
(
fontFamily
)
?
C
.
SERIF_NAME
:
C
.
SANS_SERIF_NAME
;
//font size (initializationBytes[25]) is 5% of video height
//font size (initializationBytes[25]) is 5% of video height
calculatedVideoTrackHeight
=
20
*
initializationBytes
[
25
];
calculatedVideoTrackHeight
=
20
*
initializationBytes
[
25
];
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/CssParser.java
View file @
f7ed789f
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.text.webvtt;
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.text.webvtt;
import
android.text.TextUtils
;
import
android.text.TextUtils
;
import
com.google.android.exoplayer2.util.ColorParser
;
import
com.google.android.exoplayer2.util.ColorParser
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.Util
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.regex.Matcher
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
java.util.regex.Pattern
;
...
@@ -314,7 +315,7 @@ import java.util.regex.Pattern;
...
@@ -314,7 +315,7 @@ import java.util.regex.Pattern;
}
}
selector
=
selector
.
substring
(
0
,
voiceStartIndex
);
selector
=
selector
.
substring
(
0
,
voiceStartIndex
);
}
}
String
[]
classDivision
=
selector
.
split
(
"\\."
);
String
[]
classDivision
=
Util
.
split
(
selector
,
"\\."
);
String
tagAndIdDivision
=
classDivision
[
0
];
String
tagAndIdDivision
=
classDivision
[
0
];
int
idPrefixIndex
=
tagAndIdDivision
.
indexOf
(
'#'
);
int
idPrefixIndex
=
tagAndIdDivision
.
indexOf
(
'#'
);
if
(
idPrefixIndex
!=
-
1
)
{
if
(
idPrefixIndex
!=
-
1
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/Mp4WebvttDecoder.java
View file @
f7ed789f
...
@@ -78,7 +78,8 @@ public final class Mp4WebvttDecoder extends SimpleSubtitleDecoder {
...
@@ -78,7 +78,8 @@ public final class Mp4WebvttDecoder extends SimpleSubtitleDecoder {
int
boxType
=
sampleData
.
readInt
();
int
boxType
=
sampleData
.
readInt
();
remainingCueBoxBytes
-=
BOX_HEADER_SIZE
;
remainingCueBoxBytes
-=
BOX_HEADER_SIZE
;
int
payloadLength
=
boxSize
-
BOX_HEADER_SIZE
;
int
payloadLength
=
boxSize
-
BOX_HEADER_SIZE
;
String
boxPayload
=
new
String
(
sampleData
.
data
,
sampleData
.
getPosition
(),
payloadLength
);
String
boxPayload
=
Util
.
fromUtf8Bytes
(
sampleData
.
data
,
sampleData
.
getPosition
(),
payloadLength
);
sampleData
.
skipBytes
(
payloadLength
);
sampleData
.
skipBytes
(
payloadLength
);
remainingCueBoxBytes
-=
payloadLength
;
remainingCueBoxBytes
-=
payloadLength
;
if
(
boxType
==
TYPE_sttg
)
{
if
(
boxType
==
TYPE_sttg
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttCueParser.java
View file @
f7ed789f
...
@@ -34,11 +34,12 @@ import android.text.style.UnderlineSpan;
...
@@ -34,11 +34,12 @@ import android.text.style.UnderlineSpan;
import
android.util.Log
;
import
android.util.Log
;
import
com.google.android.exoplayer2.text.Cue
;
import
com.google.android.exoplayer2.text.Cue
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.Util
;
import
java.util.ArrayDeque
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Stack
;
import
java.util.regex.Matcher
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
java.util.regex.Pattern
;
...
@@ -157,7 +158,7 @@ public final class WebvttCueParser {
...
@@ -157,7 +158,7 @@ public final class WebvttCueParser {
/* package */
static
void
parseCueText
(
String
id
,
String
markup
,
WebvttCue
.
Builder
builder
,
/* package */
static
void
parseCueText
(
String
id
,
String
markup
,
WebvttCue
.
Builder
builder
,
List
<
WebvttCssStyle
>
styles
)
{
List
<
WebvttCssStyle
>
styles
)
{
SpannableStringBuilder
spannedText
=
new
SpannableStringBuilder
();
SpannableStringBuilder
spannedText
=
new
SpannableStringBuilder
();
Stack
<
StartTag
>
startTagStack
=
new
Stack
<>();
ArrayDeque
<
StartTag
>
startTagStack
=
new
ArrayDeque
<>();
List
<
StyleMatch
>
scratchStyleMatches
=
new
ArrayList
<>();
List
<
StyleMatch
>
scratchStyleMatches
=
new
ArrayList
<>();
int
pos
=
0
;
int
pos
=
0
;
while
(
pos
<
markup
.
length
())
{
while
(
pos
<
markup
.
length
())
{
...
@@ -456,7 +457,7 @@ public final class WebvttCueParser {
...
@@ -456,7 +457,7 @@ public final class WebvttCueParser {
if
(
tagExpression
.
isEmpty
())
{
if
(
tagExpression
.
isEmpty
())
{
return
null
;
return
null
;
}
}
return
tagExpression
.
split
(
"[ \\.]"
)[
0
];
return
Util
.
splitAtFirst
(
tagExpression
,
"[ \\.]"
)[
0
];
}
}
private
static
void
getApplicableStyles
(
List
<
WebvttCssStyle
>
declaredStyles
,
String
id
,
private
static
void
getApplicableStyles
(
List
<
WebvttCssStyle
>
declaredStyles
,
String
id
,
...
@@ -518,7 +519,7 @@ public final class WebvttCueParser {
...
@@ -518,7 +519,7 @@ public final class WebvttCueParser {
voice
=
fullTagExpression
.
substring
(
voiceStartIndex
).
trim
();
voice
=
fullTagExpression
.
substring
(
voiceStartIndex
).
trim
();
fullTagExpression
=
fullTagExpression
.
substring
(
0
,
voiceStartIndex
);
fullTagExpression
=
fullTagExpression
.
substring
(
0
,
voiceStartIndex
);
}
}
String
[]
nameAndClasses
=
fullTagExpression
.
split
(
"\\."
);
String
[]
nameAndClasses
=
Util
.
split
(
fullTagExpression
,
"\\."
);
String
name
=
nameAndClasses
[
0
];
String
name
=
nameAndClasses
[
0
];
String
[]
classes
;
String
[]
classes
;
if
(
nameAndClasses
.
length
>
1
)
{
if
(
nameAndClasses
.
length
>
1
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttParserUtil.java
View file @
f7ed789f
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.text.webvtt;
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.text.webvtt;
import
com.google.android.exoplayer2.text.SubtitleDecoderException
;
import
com.google.android.exoplayer2.text.SubtitleDecoderException
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.Util
;
import
java.util.regex.Matcher
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
java.util.regex.Pattern
;
...
@@ -53,8 +54,8 @@ public final class WebvttParserUtil {
...
@@ -53,8 +54,8 @@ public final class WebvttParserUtil {
*/
*/
public
static
long
parseTimestampUs
(
String
timestamp
)
throws
NumberFormatException
{
public
static
long
parseTimestampUs
(
String
timestamp
)
throws
NumberFormatException
{
long
value
=
0
;
long
value
=
0
;
String
[]
parts
=
timestamp
.
split
(
"\\."
,
2
);
String
[]
parts
=
Util
.
splitAtFirst
(
timestamp
,
"\\."
);
String
[]
subparts
=
parts
[
0
].
split
(
":"
);
String
[]
subparts
=
Util
.
split
(
parts
[
0
],
":"
);
for
(
String
subpart
:
subparts
)
{
for
(
String
subpart
:
subparts
)
{
value
=
(
value
*
60
)
+
Long
.
parseLong
(
subpart
);
value
=
(
value
*
60
)
+
Long
.
parseLong
(
subpart
);
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java
View file @
f7ed789f
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
.
trackselection
;
package
com
.
google
.
android
.
exoplayer2
.
trackselection
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.source.TrackGroup
;
import
com.google.android.exoplayer2.source.TrackGroup
;
...
@@ -242,9 +243,11 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
...
@@ -242,9 +243,11 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
this
.
minTimeBetweenBufferReevaluationMs
=
minTimeBetweenBufferReevaluationMs
;
this
.
minTimeBetweenBufferReevaluationMs
=
minTimeBetweenBufferReevaluationMs
;
this
.
clock
=
clock
;
this
.
clock
=
clock
;
playbackSpeed
=
1
f
;
playbackSpeed
=
1
f
;
selectedIndex
=
determineIdealSelectedIndex
(
Long
.
MIN_VALUE
);
reason
=
C
.
SELECTION_REASON_INITIAL
;
reason
=
C
.
SELECTION_REASON_INITIAL
;
lastBufferEvaluationMs
=
C
.
TIME_UNSET
;
lastBufferEvaluationMs
=
C
.
TIME_UNSET
;
@SuppressWarnings
(
"nullness:method.invocation.invalid"
)
int
selectedIndex
=
determineIdealSelectedIndex
(
Long
.
MIN_VALUE
);
this
.
selectedIndex
=
selectedIndex
;
}
}
@Override
@Override
...
@@ -301,7 +304,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
...
@@ -301,7 +304,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
}
}
@Override
@Override
public
Object
getSelectionData
()
{
public
@Nullable
Object
getSelectionData
()
{
return
null
;
return
null
;
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/BaseTrackSelection.java
View file @
f7ed789f
...
@@ -110,6 +110,7 @@ public abstract class BaseTrackSelection implements TrackSelection {
...
@@ -110,6 +110,7 @@ public abstract class BaseTrackSelection implements TrackSelection {
}
}
@Override
@Override
@SuppressWarnings
(
"ReferenceEquality"
)
public
final
int
indexOf
(
Format
format
)
{
public
final
int
indexOf
(
Format
format
)
{
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
if
(
formats
[
i
]
==
format
)
{
if
(
formats
[
i
]
==
format
)
{
...
@@ -183,7 +184,9 @@ public abstract class BaseTrackSelection implements TrackSelection {
...
@@ -183,7 +184,9 @@ public abstract class BaseTrackSelection implements TrackSelection {
return
hashCode
;
return
hashCode
;
}
}
// Track groups are compared by identity not value, as distinct groups may have the same value.
@Override
@Override
@SuppressWarnings
(
"ReferenceEquality"
)
public
boolean
equals
(
@Nullable
Object
obj
)
{
public
boolean
equals
(
@Nullable
Object
obj
)
{
if
(
this
==
obj
)
{
if
(
this
==
obj
)
{
return
true
;
return
true
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java
View file @
f7ed789f
...
@@ -19,7 +19,6 @@ import android.content.Context;
...
@@ -19,7 +19,6 @@ import android.content.Context;
import
android.graphics.Point
;
import
android.graphics.Point
;
import
android.os.Parcel
;
import
android.os.Parcel
;
import
android.os.Parcelable
;
import
android.os.Parcelable
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.support.annotation.Nullable
;
import
android.text.TextUtils
;
import
android.text.TextUtils
;
import
android.util.Pair
;
import
android.util.Pair
;
...
@@ -161,8 +160,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -161,8 +160,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private
final
SparseArray
<
Map
<
TrackGroupArray
,
SelectionOverride
>>
selectionOverrides
;
private
final
SparseArray
<
Map
<
TrackGroupArray
,
SelectionOverride
>>
selectionOverrides
;
private
final
SparseBooleanArray
rendererDisabledFlags
;
private
final
SparseBooleanArray
rendererDisabledFlags
;
private
String
preferredAudioLanguage
;
private
@Nullable
String
preferredAudioLanguage
;
private
String
preferredTextLanguage
;
private
@Nullable
String
preferredTextLanguage
;
private
boolean
selectUndeterminedTextLanguage
;
private
boolean
selectUndeterminedTextLanguage
;
private
int
disabledTextTrackSelectionFlags
;
private
int
disabledTextTrackSelectionFlags
;
private
boolean
forceLowestBitrate
;
private
boolean
forceLowestBitrate
;
...
@@ -572,14 +571,14 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -572,14 +571,14 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* The preferred language for audio, as well as for forced text tracks, as an ISO 639-2/T tag.
* The preferred language for audio, as well as for forced text tracks, as an ISO 639-2/T tag.
* {@code null} selects the default track, or the first track if there's no default.
* {@code null} selects the default track, or the first track if there's no default.
*/
*/
public
final
String
preferredAudioLanguage
;
public
final
@Nullable
String
preferredAudioLanguage
;
// Text
// Text
/**
/**
* The preferred language for text tracks as an ISO 639-2/T tag. {@code null} selects the
* The preferred language for text tracks as an ISO 639-2/T tag. {@code null} selects the
* default track if there is one, or no track otherwise.
* default track if there is one, or no track otherwise.
*/
*/
public
final
String
preferredTextLanguage
;
public
final
@Nullable
String
preferredTextLanguage
;
/**
/**
* Whether a text track with undetermined language should be selected if no track with
* Whether a text track with undetermined language should be selected if no track with
* {@link #preferredTextLanguage} is available, or if {@link #preferredTextLanguage} is unset.
* {@link #preferredTextLanguage} is available, or if {@link #preferredTextLanguage} is unset.
...
@@ -673,8 +672,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -673,8 +672,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
/* package */
Parameters
(
/* package */
Parameters
(
SparseArray
<
Map
<
TrackGroupArray
,
SelectionOverride
>>
selectionOverrides
,
SparseArray
<
Map
<
TrackGroupArray
,
SelectionOverride
>>
selectionOverrides
,
SparseBooleanArray
rendererDisabledFlags
,
SparseBooleanArray
rendererDisabledFlags
,
String
preferredAudioLanguage
,
@Nullable
String
preferredAudioLanguage
,
String
preferredTextLanguage
,
@Nullable
String
preferredTextLanguage
,
boolean
selectUndeterminedTextLanguage
,
boolean
selectUndeterminedTextLanguage
,
int
disabledTextTrackSelectionFlags
,
int
disabledTextTrackSelectionFlags
,
boolean
forceLowestBitrate
,
boolean
forceLowestBitrate
,
...
@@ -759,7 +758,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -759,7 +758,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* @param groups The {@link TrackGroupArray}.
* @param groups The {@link TrackGroupArray}.
* @return The override, or null if no override exists.
* @return The override, or null if no override exists.
*/
*/
public
final
SelectionOverride
getSelectionOverride
(
int
rendererIndex
,
TrackGroupArray
groups
)
{
public
final
@Nullable
SelectionOverride
getSelectionOverride
(
int
rendererIndex
,
TrackGroupArray
groups
)
{
Map
<
TrackGroupArray
,
SelectionOverride
>
overrides
=
selectionOverrides
.
get
(
rendererIndex
);
Map
<
TrackGroupArray
,
SelectionOverride
>
overrides
=
selectionOverrides
.
get
(
rendererIndex
);
return
overrides
!=
null
?
overrides
.
get
(
groups
)
:
null
;
return
overrides
!=
null
?
overrides
.
get
(
groups
)
:
null
;
}
}
...
@@ -816,8 +816,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -816,8 +816,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
result
=
31
*
result
+
viewportHeight
;
result
=
31
*
result
+
viewportHeight
;
result
=
31
*
result
+
maxVideoBitrate
;
result
=
31
*
result
+
maxVideoBitrate
;
result
=
31
*
result
+
tunnelingAudioSessionId
;
result
=
31
*
result
+
tunnelingAudioSessionId
;
result
=
31
*
result
+
preferredAudioLanguage
.
hashCode
();
result
=
result
=
31
*
result
+
preferredTextLanguage
.
hashCode
();
31
*
result
+
(
preferredAudioLanguage
==
null
?
0
:
preferredAudioLanguage
.
hashCode
());
result
=
31
*
result
+
(
preferredTextLanguage
==
null
?
0
:
preferredTextLanguage
.
hashCode
());
return
result
;
return
result
;
}
}
...
@@ -1042,7 +1043,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1042,7 +1043,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private
static
final
int
[]
NO_TRACKS
=
new
int
[
0
];
private
static
final
int
[]
NO_TRACKS
=
new
int
[
0
];
private
static
final
int
WITHIN_RENDERER_CAPABILITIES_BONUS
=
1000
;
private
static
final
int
WITHIN_RENDERER_CAPABILITIES_BONUS
=
1000
;
private
final
TrackSelection
.
Factory
adaptiveTrackSelectionFactory
;
private
final
@Nullable
TrackSelection
.
Factory
adaptiveTrackSelectionFactory
;
private
final
AtomicReference
<
Parameters
>
parametersReference
;
private
final
AtomicReference
<
Parameters
>
parametersReference
;
/**
/**
...
@@ -1069,7 +1070,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1069,7 +1070,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* @param adaptiveTrackSelectionFactory A factory for adaptive {@link TrackSelection}s, or null if
* @param adaptiveTrackSelectionFactory A factory for adaptive {@link TrackSelection}s, or null if
* the selector should not support adaptive tracks.
* the selector should not support adaptive tracks.
*/
*/
public
DefaultTrackSelector
(
TrackSelection
.
Factory
adaptiveTrackSelectionFactory
)
{
public
DefaultTrackSelector
(
@Nullable
TrackSelection
.
Factory
adaptiveTrackSelectionFactory
)
{
this
.
adaptiveTrackSelectionFactory
=
adaptiveTrackSelectionFactory
;
this
.
adaptiveTrackSelectionFactory
=
adaptiveTrackSelectionFactory
;
parametersReference
=
new
AtomicReference
<>(
Parameters
.
DEFAULT
);
parametersReference
=
new
AtomicReference
<>(
Parameters
.
DEFAULT
);
}
}
...
@@ -1139,7 +1140,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1139,7 +1140,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
/** @deprecated Use {@link Parameters#getSelectionOverride(int, TrackGroupArray)}. */
/** @deprecated Use {@link Parameters#getSelectionOverride(int, TrackGroupArray)}. */
@Deprecated
@Deprecated
public
final
SelectionOverride
getSelectionOverride
(
int
rendererIndex
,
TrackGroupArray
groups
)
{
public
final
@Nullable
SelectionOverride
getSelectionOverride
(
int
rendererIndex
,
TrackGroupArray
groups
)
{
return
getParameters
().
getSelectionOverride
(
rendererIndex
,
groups
);
return
getParameters
().
getSelectionOverride
(
rendererIndex
,
groups
);
}
}
...
@@ -1170,11 +1172,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1170,11 +1172,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// MappingTrackSelector implementation.
// MappingTrackSelector implementation.
@Override
@Override
protected
final
Pair
<
RendererConfiguration
[],
TrackSelection
[]>
selectTracks
(
protected
final
Pair
<
RendererConfiguration
[],
TrackSelection
[]>
MappedTrackInfo
mappedTrackInfo
,
selectTracks
(
int
[][][]
rendererFormatSupports
,
MappedTrackInfo
mappedTrackInfo
,
int
[]
rendererMixedMimeTypeAdaptationSupports
)
int
[][][]
rendererFormatSupports
,
throws
ExoPlaybackException
{
int
[]
rendererMixedMimeTypeAdaptationSupports
)
throws
ExoPlaybackException
{
Parameters
params
=
parametersReference
.
get
();
Parameters
params
=
parametersReference
.
get
();
int
rendererCount
=
mappedTrackInfo
.
getRendererCount
();
int
rendererCount
=
mappedTrackInfo
.
getRendererCount
();
TrackSelection
[]
rendererTrackSelections
=
TrackSelection
[]
rendererTrackSelections
=
...
@@ -1200,8 +1203,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1200,8 +1203,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
rendererTrackGroups
.
get
(
override
.
groupIndex
),
override
.
tracks
[
0
]);
rendererTrackGroups
.
get
(
override
.
groupIndex
),
override
.
tracks
[
0
]);
}
else
{
}
else
{
rendererTrackSelections
[
i
]
=
rendererTrackSelections
[
i
]
=
adaptiveTrackSelectionFactory
.
createTrackSelection
(
Assertions
.
checkNotNull
(
adaptiveTrackSelectionFactory
)
rendererTrackGroups
.
get
(
override
.
groupIndex
),
override
.
tracks
);
.
createTrackSelection
(
rendererTrackGroups
.
get
(
override
.
groupIndex
),
override
.
tracks
);
}
}
}
}
}
}
...
@@ -1209,7 +1213,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1209,7 +1213,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// Initialize the renderer configurations to the default configuration for all renderers with
// Initialize the renderer configurations to the default configuration for all renderers with
// selections, and null otherwise.
// selections, and null otherwise.
RendererConfiguration
[]
rendererConfigurations
=
new
RendererConfiguration
[
rendererCount
];
RendererConfiguration
[]
rendererConfigurations
=
new
RendererConfiguration
[
rendererCount
];
for
(
int
i
=
0
;
i
<
rendererCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
rendererCount
;
i
++)
{
boolean
forceRendererDisabled
=
params
.
getRendererDisabled
(
i
);
boolean
forceRendererDisabled
=
params
.
getRendererDisabled
(
i
);
boolean
rendererEnabled
=
boolean
rendererEnabled
=
...
@@ -1331,12 +1336,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1331,12 +1336,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* @return The {@link TrackSelection} for the renderer, or null if no selection was made.
* @return The {@link TrackSelection} for the renderer, or null if no selection was made.
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
*/
*/
protected
TrackSelection
selectVideoTrack
(
protected
@Nullable
TrackSelection
selectVideoTrack
(
TrackGroupArray
groups
,
TrackGroupArray
groups
,
int
[][]
formatSupports
,
int
[][]
formatSupports
,
int
mixedMimeTypeAdaptationSupports
,
int
mixedMimeTypeAdaptationSupports
,
Parameters
params
,
Parameters
params
,
TrackSelection
.
Factory
adaptiveTrackSelectionFactory
)
@Nullable
TrackSelection
.
Factory
adaptiveTrackSelectionFactory
)
throws
ExoPlaybackException
{
throws
ExoPlaybackException
{
TrackSelection
selection
=
null
;
TrackSelection
selection
=
null
;
if
(!
params
.
forceLowestBitrate
&&
adaptiveTrackSelectionFactory
!=
null
)
{
if
(!
params
.
forceLowestBitrate
&&
adaptiveTrackSelectionFactory
!=
null
)
{
...
@@ -1354,7 +1359,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1354,7 +1359,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return
selection
;
return
selection
;
}
}
private
static
TrackSelection
selectAdaptiveVideoTrack
(
private
static
@Nullable
TrackSelection
selectAdaptiveVideoTrack
(
TrackGroupArray
groups
,
TrackGroupArray
groups
,
int
[][]
formatSupport
,
int
[][]
formatSupport
,
int
mixedMimeTypeAdaptationSupports
,
int
mixedMimeTypeAdaptationSupports
,
...
@@ -1374,7 +1379,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1374,7 +1379,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
params
.
maxVideoBitrate
,
params
.
viewportWidth
,
params
.
viewportHeight
,
params
.
maxVideoBitrate
,
params
.
viewportWidth
,
params
.
viewportHeight
,
params
.
viewportOrientationMayChange
);
params
.
viewportOrientationMayChange
);
if
(
adaptiveTracks
.
length
>
0
)
{
if
(
adaptiveTracks
.
length
>
0
)
{
return
adaptiveTrackSelectionFactory
.
createTrackSelection
(
group
,
adaptiveTracks
);
return
Assertions
.
checkNotNull
(
adaptiveTrackSelectionFactory
)
.
createTrackSelection
(
group
,
adaptiveTracks
);
}
}
}
}
return
null
;
return
null
;
...
@@ -1421,9 +1427,15 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1421,9 +1427,15 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return
selectedTrackIndices
.
size
()
<
2
?
NO_TRACKS
:
Util
.
toArray
(
selectedTrackIndices
);
return
selectedTrackIndices
.
size
()
<
2
?
NO_TRACKS
:
Util
.
toArray
(
selectedTrackIndices
);
}
}
private
static
int
getAdaptiveVideoTrackCountForMimeType
(
TrackGroup
group
,
int
[]
formatSupport
,
private
static
int
getAdaptiveVideoTrackCountForMimeType
(
int
requiredAdaptiveSupport
,
String
mimeType
,
int
maxVideoWidth
,
int
maxVideoHeight
,
TrackGroup
group
,
int
maxVideoBitrate
,
List
<
Integer
>
selectedTrackIndices
)
{
int
[]
formatSupport
,
int
requiredAdaptiveSupport
,
@Nullable
String
mimeType
,
int
maxVideoWidth
,
int
maxVideoHeight
,
int
maxVideoBitrate
,
List
<
Integer
>
selectedTrackIndices
)
{
int
adaptiveTrackCount
=
0
;
int
adaptiveTrackCount
=
0
;
for
(
int
i
=
0
;
i
<
selectedTrackIndices
.
size
();
i
++)
{
for
(
int
i
=
0
;
i
<
selectedTrackIndices
.
size
();
i
++)
{
int
trackIndex
=
selectedTrackIndices
.
get
(
i
);
int
trackIndex
=
selectedTrackIndices
.
get
(
i
);
...
@@ -1436,9 +1448,15 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1436,9 +1448,15 @@ public class DefaultTrackSelector extends MappingTrackSelector {
return
adaptiveTrackCount
;
return
adaptiveTrackCount
;
}
}
private
static
void
filterAdaptiveVideoTrackCountForMimeType
(
TrackGroup
group
,
private
static
void
filterAdaptiveVideoTrackCountForMimeType
(
int
[]
formatSupport
,
int
requiredAdaptiveSupport
,
String
mimeType
,
int
maxVideoWidth
,
TrackGroup
group
,
int
maxVideoHeight
,
int
maxVideoBitrate
,
List
<
Integer
>
selectedTrackIndices
)
{
int
[]
formatSupport
,
int
requiredAdaptiveSupport
,
@Nullable
String
mimeType
,
int
maxVideoWidth
,
int
maxVideoHeight
,
int
maxVideoBitrate
,
List
<
Integer
>
selectedTrackIndices
)
{
for
(
int
i
=
selectedTrackIndices
.
size
()
-
1
;
i
>=
0
;
i
--)
{
for
(
int
i
=
selectedTrackIndices
.
size
()
-
1
;
i
>=
0
;
i
--)
{
int
trackIndex
=
selectedTrackIndices
.
get
(
i
);
int
trackIndex
=
selectedTrackIndices
.
get
(
i
);
if
(!
isSupportedAdaptiveVideoTrack
(
group
.
getFormat
(
trackIndex
),
mimeType
,
if
(!
isSupportedAdaptiveVideoTrack
(
group
.
getFormat
(
trackIndex
),
mimeType
,
...
@@ -1449,8 +1467,13 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1449,8 +1467,13 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
}
}
}
private
static
boolean
isSupportedAdaptiveVideoTrack
(
Format
format
,
String
mimeType
,
private
static
boolean
isSupportedAdaptiveVideoTrack
(
int
formatSupport
,
int
requiredAdaptiveSupport
,
int
maxVideoWidth
,
int
maxVideoHeight
,
Format
format
,
@Nullable
String
mimeType
,
int
formatSupport
,
int
requiredAdaptiveSupport
,
int
maxVideoWidth
,
int
maxVideoHeight
,
int
maxVideoBitrate
)
{
int
maxVideoBitrate
)
{
return
isSupported
(
formatSupport
,
false
)
&&
((
formatSupport
&
requiredAdaptiveSupport
)
!=
0
)
return
isSupported
(
formatSupport
,
false
)
&&
((
formatSupport
&
requiredAdaptiveSupport
)
!=
0
)
&&
(
mimeType
==
null
||
Util
.
areEqual
(
format
.
sampleMimeType
,
mimeType
))
&&
(
mimeType
==
null
||
Util
.
areEqual
(
format
.
sampleMimeType
,
mimeType
))
...
@@ -1459,7 +1482,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1459,7 +1482,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
&&
(
format
.
bitrate
==
Format
.
NO_VALUE
||
format
.
bitrate
<=
maxVideoBitrate
);
&&
(
format
.
bitrate
==
Format
.
NO_VALUE
||
format
.
bitrate
<=
maxVideoBitrate
);
}
}
private
static
TrackSelection
selectFixedVideoTrack
(
private
static
@Nullable
TrackSelection
selectFixedVideoTrack
(
TrackGroupArray
groups
,
int
[][]
formatSupports
,
Parameters
params
)
{
TrackGroupArray
groups
,
int
[][]
formatSupports
,
Parameters
params
)
{
TrackGroup
selectedGroup
=
null
;
TrackGroup
selectedGroup
=
null
;
int
selectedTrackIndex
=
0
;
int
selectedTrackIndex
=
0
;
...
@@ -1537,12 +1560,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1537,12 +1560,12 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* @return The {@link TrackSelection} for the renderer, or null if no selection was made.
* @return The {@link TrackSelection} for the renderer, or null if no selection was made.
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
*/
*/
protected
TrackSelection
selectAudioTrack
(
protected
@Nullable
TrackSelection
selectAudioTrack
(
TrackGroupArray
groups
,
TrackGroupArray
groups
,
int
[][]
formatSupports
,
int
[][]
formatSupports
,
int
mixedMimeTypeAdaptationSupports
,
int
mixedMimeTypeAdaptationSupports
,
Parameters
params
,
Parameters
params
,
TrackSelection
.
Factory
adaptiveTrackSelectionFactory
)
@Nullable
TrackSelection
.
Factory
adaptiveTrackSelectionFactory
)
throws
ExoPlaybackException
{
throws
ExoPlaybackException
{
int
selectedTrackIndex
=
C
.
INDEX_UNSET
;
int
selectedTrackIndex
=
C
.
INDEX_UNSET
;
int
selectedGroupIndex
=
C
.
INDEX_UNSET
;
int
selectedGroupIndex
=
C
.
INDEX_UNSET
;
...
@@ -1606,8 +1629,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1606,8 +1629,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
int
[]
adaptiveIndices
=
new
int
[
selectedConfigurationTrackCount
];
int
[]
adaptiveIndices
=
new
int
[
selectedConfigurationTrackCount
];
int
index
=
0
;
int
index
=
0
;
for
(
int
i
=
0
;
i
<
group
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
group
.
length
;
i
++)
{
if
(
isSupportedAdaptiveAudioTrack
(
group
.
getFormat
(
i
),
formatSupport
[
i
],
if
(
isSupportedAdaptiveAudioTrack
(
selectedConfiguration
))
{
group
.
getFormat
(
i
),
formatSupport
[
i
],
Assertions
.
checkNotNull
(
selectedConfiguration
)
))
{
adaptiveIndices
[
index
++]
=
i
;
adaptiveIndices
[
index
++]
=
i
;
}
}
}
}
...
@@ -1648,7 +1671,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1648,7 +1671,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* @return The {@link TrackSelection} for the renderer, or null if no selection was made.
* @return The {@link TrackSelection} for the renderer, or null if no selection was made.
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
*/
*/
protected
TrackSelection
selectTextTrack
(
protected
@Nullable
TrackSelection
selectTextTrack
(
TrackGroupArray
groups
,
int
[][]
formatSupport
,
Parameters
params
)
TrackGroupArray
groups
,
int
[][]
formatSupport
,
Parameters
params
)
throws
ExoPlaybackException
{
throws
ExoPlaybackException
{
TrackGroup
selectedGroup
=
null
;
TrackGroup
selectedGroup
=
null
;
...
@@ -1721,7 +1744,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1721,7 +1744,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* @return The {@link TrackSelection} for the renderer, or null if no selection was made.
* @return The {@link TrackSelection} for the renderer, or null if no selection was made.
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
*/
*/
protected
TrackSelection
selectOtherTrack
(
protected
@Nullable
TrackSelection
selectOtherTrack
(
int
trackType
,
TrackGroupArray
groups
,
int
[][]
formatSupport
,
Parameters
params
)
int
trackType
,
TrackGroupArray
groups
,
int
[][]
formatSupport
,
Parameters
params
)
throws
ExoPlaybackException
{
throws
ExoPlaybackException
{
TrackGroup
selectedGroup
=
null
;
TrackGroup
selectedGroup
=
null
;
...
@@ -1883,15 +1906,15 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1883,15 +1906,15 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
}
/**
/**
* Returns whether a {@link Format} specifies a particular language, or {@code false} if
* Returns whether a {@link Format} specifies a particular language, or {@code false} if
{@code
*
{@code
language} is null.
* language} is null.
*
*
* @param format The {@link Format}.
* @param format The {@link Format}.
* @param language The language.
* @param language The language.
* @return Whether the format specifies the language, or {@code false} if {@code language} is
* @return Whether the format specifies the language, or {@code false} if {@code language} is
* null.
* null.
*/
*/
protected
static
boolean
formatHasLanguage
(
Format
format
,
String
language
)
{
protected
static
boolean
formatHasLanguage
(
Format
format
,
@Nullable
String
language
)
{
return
language
!=
null
return
language
!=
null
&&
TextUtils
.
equals
(
language
,
Util
.
normalizeLanguageCode
(
format
.
language
));
&&
TextUtils
.
equals
(
language
,
Util
.
normalizeLanguageCode
(
format
.
language
));
}
}
...
@@ -1997,7 +2020,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1997,7 +2020,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
* negative integer if this score is worse than the other.
* negative integer if this score is worse than the other.
*/
*/
@Override
@Override
public
int
compareTo
(
@NonNull
AudioTrackScore
other
)
{
public
int
compareTo
(
AudioTrackScore
other
)
{
if
(
this
.
withinRendererCapabilitiesScore
!=
other
.
withinRendererCapabilitiesScore
)
{
if
(
this
.
withinRendererCapabilitiesScore
!=
other
.
withinRendererCapabilitiesScore
)
{
return
compareInts
(
this
.
withinRendererCapabilitiesScore
,
return
compareInts
(
this
.
withinRendererCapabilitiesScore
,
other
.
withinRendererCapabilitiesScore
);
other
.
withinRendererCapabilitiesScore
);
...
@@ -2066,9 +2089,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -2066,9 +2089,9 @@ public class DefaultTrackSelector extends MappingTrackSelector {
public
final
int
channelCount
;
public
final
int
channelCount
;
public
final
int
sampleRate
;
public
final
int
sampleRate
;
public
final
String
mimeType
;
public
final
@Nullable
String
mimeType
;
public
AudioConfigurationTuple
(
int
channelCount
,
int
sampleRate
,
String
mimeType
)
{
public
AudioConfigurationTuple
(
int
channelCount
,
int
sampleRate
,
@Nullable
String
mimeType
)
{
this
.
channelCount
=
channelCount
;
this
.
channelCount
=
channelCount
;
this
.
sampleRate
=
sampleRate
;
this
.
sampleRate
=
sampleRate
;
this
.
mimeType
=
mimeType
;
this
.
mimeType
=
mimeType
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/FixedTrackSelection.java
View file @
f7ed789f
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
.
trackselection
;
package
com
.
google
.
android
.
exoplayer2
.
trackselection
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.source.TrackGroup
;
import
com.google.android.exoplayer2.source.TrackGroup
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
...
@@ -30,7 +31,7 @@ public final class FixedTrackSelection extends BaseTrackSelection {
...
@@ -30,7 +31,7 @@ public final class FixedTrackSelection extends BaseTrackSelection {
public
static
final
class
Factory
implements
TrackSelection
.
Factory
{
public
static
final
class
Factory
implements
TrackSelection
.
Factory
{
private
final
int
reason
;
private
final
int
reason
;
private
final
Object
data
;
private
final
@Nullable
Object
data
;
public
Factory
()
{
public
Factory
()
{
this
.
reason
=
C
.
SELECTION_REASON_UNKNOWN
;
this
.
reason
=
C
.
SELECTION_REASON_UNKNOWN
;
...
@@ -41,7 +42,7 @@ public final class FixedTrackSelection extends BaseTrackSelection {
...
@@ -41,7 +42,7 @@ public final class FixedTrackSelection extends BaseTrackSelection {
* @param reason A reason for the track selection.
* @param reason A reason for the track selection.
* @param data Optional data associated with the track selection.
* @param data Optional data associated with the track selection.
*/
*/
public
Factory
(
int
reason
,
Object
data
)
{
public
Factory
(
int
reason
,
@Nullable
Object
data
)
{
this
.
reason
=
reason
;
this
.
reason
=
reason
;
this
.
data
=
data
;
this
.
data
=
data
;
}
}
...
@@ -51,11 +52,10 @@ public final class FixedTrackSelection extends BaseTrackSelection {
...
@@ -51,11 +52,10 @@ public final class FixedTrackSelection extends BaseTrackSelection {
Assertions
.
checkArgument
(
tracks
.
length
==
1
);
Assertions
.
checkArgument
(
tracks
.
length
==
1
);
return
new
FixedTrackSelection
(
group
,
tracks
[
0
],
reason
,
data
);
return
new
FixedTrackSelection
(
group
,
tracks
[
0
],
reason
,
data
);
}
}
}
}
private
final
int
reason
;
private
final
int
reason
;
private
final
Object
data
;
private
final
@Nullable
Object
data
;
/**
/**
* @param group The {@link TrackGroup}. Must not be null.
* @param group The {@link TrackGroup}. Must not be null.
...
@@ -71,7 +71,7 @@ public final class FixedTrackSelection extends BaseTrackSelection {
...
@@ -71,7 +71,7 @@ public final class FixedTrackSelection extends BaseTrackSelection {
* @param reason A reason for the track selection.
* @param reason A reason for the track selection.
* @param data Optional data associated with the track selection.
* @param data Optional data associated with the track selection.
*/
*/
public
FixedTrackSelection
(
TrackGroup
group
,
int
track
,
int
reason
,
Object
data
)
{
public
FixedTrackSelection
(
TrackGroup
group
,
int
track
,
int
reason
,
@Nullable
Object
data
)
{
super
(
group
,
track
);
super
(
group
,
track
);
this
.
reason
=
reason
;
this
.
reason
=
reason
;
this
.
data
=
data
;
this
.
data
=
data
;
...
@@ -94,7 +94,7 @@ public final class FixedTrackSelection extends BaseTrackSelection {
...
@@ -94,7 +94,7 @@ public final class FixedTrackSelection extends BaseTrackSelection {
}
}
@Override
@Override
public
Object
getSelectionData
()
{
public
@Nullable
Object
getSelectionData
()
{
return
data
;
return
data
;
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/MappingTrackSelector.java
View file @
f7ed789f
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
trackselection
;
package
com
.
google
.
android
.
exoplayer2
.
trackselection
;
import
android.support.annotation.IntDef
;
import
android.support.annotation.IntDef
;
import
android.support.annotation.Nullable
;
import
android.util.Pair
;
import
android.util.Pair
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
...
@@ -301,13 +302,13 @@ public abstract class MappingTrackSelector extends TrackSelector {
...
@@ -301,13 +302,13 @@ public abstract class MappingTrackSelector extends TrackSelector {
}
}
private
MappedTrackInfo
currentMappedTrackInfo
;
private
@Nullable
MappedTrackInfo
currentMappedTrackInfo
;
/**
/**
* Returns the mapping information for the currently active track selection, or null if no
* Returns the mapping information for the currently active track selection, or null if no
* selection is currently active.
* selection is currently active.
*/
*/
public
final
MappedTrackInfo
getCurrentMappedTrackInfo
()
{
public
final
@Nullable
MappedTrackInfo
getCurrentMappedTrackInfo
()
{
return
currentMappedTrackInfo
;
return
currentMappedTrackInfo
;
}
}
...
@@ -357,9 +358,11 @@ public abstract class MappingTrackSelector extends TrackSelector {
...
@@ -357,9 +358,11 @@ public abstract class MappingTrackSelector extends TrackSelector {
int
[]
rendererTrackTypes
=
new
int
[
rendererCapabilities
.
length
];
int
[]
rendererTrackTypes
=
new
int
[
rendererCapabilities
.
length
];
for
(
int
i
=
0
;
i
<
rendererCapabilities
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
rendererCapabilities
.
length
;
i
++)
{
int
rendererTrackGroupCount
=
rendererTrackGroupCounts
[
i
];
int
rendererTrackGroupCount
=
rendererTrackGroupCounts
[
i
];
rendererTrackGroupArrays
[
i
]
=
new
TrackGroupArray
(
rendererTrackGroupArrays
[
i
]
=
Arrays
.
copyOf
(
rendererTrackGroups
[
i
],
rendererTrackGroupCount
));
new
TrackGroupArray
(
rendererFormatSupports
[
i
]
=
Arrays
.
copyOf
(
rendererFormatSupports
[
i
],
rendererTrackGroupCount
);
Util
.
nullSafeArrayCopy
(
rendererTrackGroups
[
i
],
rendererTrackGroupCount
));
rendererFormatSupports
[
i
]
=
Util
.
nullSafeArrayCopy
(
rendererFormatSupports
[
i
],
rendererTrackGroupCount
);
rendererTrackTypes
[
i
]
=
rendererCapabilities
[
i
].
getTrackType
();
rendererTrackTypes
[
i
]
=
rendererCapabilities
[
i
].
getTrackType
();
}
}
...
@@ -367,7 +370,7 @@ public abstract class MappingTrackSelector extends TrackSelector {
...
@@ -367,7 +370,7 @@ public abstract class MappingTrackSelector extends TrackSelector {
int
unmappedTrackGroupCount
=
rendererTrackGroupCounts
[
rendererCapabilities
.
length
];
int
unmappedTrackGroupCount
=
rendererTrackGroupCounts
[
rendererCapabilities
.
length
];
TrackGroupArray
unmappedTrackGroupArray
=
TrackGroupArray
unmappedTrackGroupArray
=
new
TrackGroupArray
(
new
TrackGroupArray
(
Arrays
.
copyOf
(
Util
.
nullSafeArrayCopy
(
rendererTrackGroups
[
rendererCapabilities
.
length
],
unmappedTrackGroupCount
));
rendererTrackGroups
[
rendererCapabilities
.
length
],
unmappedTrackGroupCount
));
// Package up the track information and selections.
// Package up the track information and selections.
...
@@ -399,11 +402,12 @@ public abstract class MappingTrackSelector extends TrackSelector {
...
@@ -399,11 +402,12 @@ public abstract class MappingTrackSelector extends TrackSelector {
* RendererCapabilities#getTrackType()} is {@link C#TRACK_TYPE_NONE}.
* RendererCapabilities#getTrackType()} is {@link C#TRACK_TYPE_NONE}.
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
* @throws ExoPlaybackException If an error occurs while selecting the tracks.
*/
*/
protected
abstract
Pair
<
RendererConfiguration
[],
TrackSelection
[]>
selectTracks
(
protected
abstract
Pair
<
RendererConfiguration
[],
TrackSelection
[]>
MappedTrackInfo
mappedTrackInfo
,
selectTracks
(
int
[][][]
rendererFormatSupports
,
MappedTrackInfo
mappedTrackInfo
,
int
[]
rendererMixedMimeTypeAdaptationSupport
)
int
[][][]
rendererFormatSupports
,
throws
ExoPlaybackException
;
int
[]
rendererMixedMimeTypeAdaptationSupport
)
throws
ExoPlaybackException
;
/**
/**
* Finds the renderer to which the provided {@link TrackGroup} should be mapped.
* Finds the renderer to which the provided {@link TrackGroup} should be mapped.
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/RandomTrackSelection.java
View file @
f7ed789f
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
trackselection
;
package
com
.
google
.
android
.
exoplayer2
.
trackselection
;
import
android.os.SystemClock
;
import
android.os.SystemClock
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.source.TrackGroup
;
import
com.google.android.exoplayer2.source.TrackGroup
;
import
java.util.Random
;
import
java.util.Random
;
...
@@ -47,7 +48,6 @@ public final class RandomTrackSelection extends BaseTrackSelection {
...
@@ -47,7 +48,6 @@ public final class RandomTrackSelection extends BaseTrackSelection {
public
RandomTrackSelection
createTrackSelection
(
TrackGroup
group
,
int
...
tracks
)
{
public
RandomTrackSelection
createTrackSelection
(
TrackGroup
group
,
int
...
tracks
)
{
return
new
RandomTrackSelection
(
group
,
tracks
,
random
);
return
new
RandomTrackSelection
(
group
,
tracks
,
random
);
}
}
}
}
private
final
Random
random
;
private
final
Random
random
;
...
@@ -123,7 +123,7 @@ public final class RandomTrackSelection extends BaseTrackSelection {
...
@@ -123,7 +123,7 @@ public final class RandomTrackSelection extends BaseTrackSelection {
}
}
@Override
@Override
public
Object
getSelectionData
()
{
public
@Nullable
Object
getSelectionData
()
{
return
null
;
return
null
;
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelection.java
View file @
f7ed789f
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
.
trackselection
;
package
com
.
google
.
android
.
exoplayer2
.
trackselection
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.source.TrackGroup
;
import
com.google.android.exoplayer2.source.TrackGroup
;
...
@@ -90,7 +91,9 @@ public interface TrackSelection {
...
@@ -90,7 +91,9 @@ public interface TrackSelection {
int
getIndexInTrackGroup
(
int
index
);
int
getIndexInTrackGroup
(
int
index
);
/**
/**
* Returns the index in the selection of the track with the specified format.
* Returns the index in the selection of the track with the specified format. The format is
* located by identity so, for example, {@code selection.indexOf(selection.getFormat(index)) ==
* index} even if multiple selected tracks have formats that contain the same values.
*
*
* @param format The format.
* @param format The format.
* @return The index in the selection, or {@link C#INDEX_UNSET} if the track with the specified
* @return The index in the selection, or {@link C#INDEX_UNSET} if the track with the specified
...
@@ -129,10 +132,8 @@ public interface TrackSelection {
...
@@ -129,10 +132,8 @@ public interface TrackSelection {
*/
*/
int
getSelectionReason
();
int
getSelectionReason
();
/**
/** Returns optional data associated with the current track selection. */
* Returns optional data associated with the current track selection.
@Nullable
Object
getSelectionData
();
*/
Object
getSelectionData
();
// Adaptation.
// Adaptation.
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectionArray.java
View file @
f7ed789f
...
@@ -29,9 +29,7 @@ public final class TrackSelectionArray {
...
@@ -29,9 +29,7 @@ public final class TrackSelectionArray {
// Lazily initialized hashcode.
// Lazily initialized hashcode.
private
int
hashCode
;
private
int
hashCode
;
/**
/** @param trackSelections The selections. Must not be null, but may contain null elements. */
* @param trackSelections The selections. Must not be null, but may contain null elements.
*/
public
TrackSelectionArray
(
TrackSelection
...
trackSelections
)
{
public
TrackSelectionArray
(
TrackSelection
...
trackSelections
)
{
this
.
trackSelections
=
trackSelections
;
this
.
trackSelections
=
trackSelections
;
this
.
length
=
trackSelections
.
length
;
this
.
length
=
trackSelections
.
length
;
...
@@ -43,13 +41,11 @@ public final class TrackSelectionArray {
...
@@ -43,13 +41,11 @@ public final class TrackSelectionArray {
* @param index The index of the selection.
* @param index The index of the selection.
* @return The selection.
* @return The selection.
*/
*/
public
TrackSelection
get
(
int
index
)
{
public
@Nullable
TrackSelection
get
(
int
index
)
{
return
trackSelections
[
index
];
return
trackSelections
[
index
];
}
}
/**
/** Returns the selections in a newly allocated array. */
* Returns the selections in a newly allocated array.
*/
public
TrackSelection
[]
getAll
()
{
public
TrackSelection
[]
getAll
()
{
return
trackSelections
.
clone
();
return
trackSelections
.
clone
();
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelector.java
View file @
f7ed789f
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
.
trackselection
;
package
com
.
google
.
android
.
exoplayer2
.
trackselection
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
import
com.google.android.exoplayer2.ExoPlayer
;
import
com.google.android.exoplayer2.ExoPlayer
;
import
com.google.android.exoplayer2.Renderer
;
import
com.google.android.exoplayer2.Renderer
;
...
@@ -89,7 +90,7 @@ public abstract class TrackSelector {
...
@@ -89,7 +90,7 @@ public abstract class TrackSelector {
}
}
private
InvalidationListener
listener
;
private
@Nullable
InvalidationListener
listener
;
/**
/**
* Called by the player to initialize the selector.
* Called by the player to initialize the selector.
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/TrackSelectorResult.java
View file @
f7ed789f
...
@@ -48,7 +48,9 @@ public final class TrackSelectorResult {
...
@@ -48,7 +48,9 @@ public final class TrackSelectorResult {
* TrackSelector#onSelectionActivated(Object)} should the selection be activated.
* TrackSelector#onSelectionActivated(Object)} should the selection be activated.
*/
*/
public
TrackSelectorResult
(
public
TrackSelectorResult
(
RendererConfiguration
[]
rendererConfigurations
,
TrackSelection
[]
selections
,
Object
info
)
{
RendererConfiguration
[]
rendererConfigurations
,
TrackSelection
[]
selections
,
Object
info
)
{
this
.
rendererConfigurations
=
rendererConfigurations
;
this
.
rendererConfigurations
=
rendererConfigurations
;
this
.
selections
=
new
TrackSelectionArray
(
selections
);
this
.
selections
=
new
TrackSelectionArray
(
selections
);
this
.
info
=
info
;
this
.
info
=
info
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/DataSchemeDataSource.java
View file @
f7ed789f
...
@@ -19,6 +19,7 @@ import android.net.Uri;
...
@@ -19,6 +19,7 @@ import android.net.Uri;
import
android.util.Base64
;
import
android.util.Base64
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.ParserException
;
import
com.google.android.exoplayer2.ParserException
;
import
com.google.android.exoplayer2.util.Util
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.net.URLDecoder
;
import
java.net.URLDecoder
;
...
@@ -41,8 +42,8 @@ public final class DataSchemeDataSource implements DataSource {
...
@@ -41,8 +42,8 @@ public final class DataSchemeDataSource implements DataSource {
if
(!
SCHEME_DATA
.
equals
(
scheme
))
{
if
(!
SCHEME_DATA
.
equals
(
scheme
))
{
throw
new
ParserException
(
"Unsupported scheme: "
+
scheme
);
throw
new
ParserException
(
"Unsupported scheme: "
+
scheme
);
}
}
String
[]
uriParts
=
uri
.
getSchemeSpecificPart
().
split
(
","
);
String
[]
uriParts
=
Util
.
split
(
uri
.
getSchemeSpecificPart
(),
","
);
if
(
uriParts
.
length
>
2
)
{
if
(
uriParts
.
length
!=
2
)
{
throw
new
ParserException
(
"Unexpected URI format: "
+
uri
);
throw
new
ParserException
(
"Unexpected URI format: "
+
uri
);
}
}
String
dataString
=
uriParts
[
1
];
String
dataString
=
uriParts
[
1
];
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/Loader.java
View file @
f7ed789f
...
@@ -58,11 +58,6 @@ public final class Loader implements LoaderErrorThrower {
...
@@ -58,11 +58,6 @@ public final class Loader implements LoaderErrorThrower {
void
cancelLoad
();
void
cancelLoad
();
/**
/**
* Returns whether the load has been canceled.
*/
boolean
isLoadCanceled
();
/**
* Performs the load, returning on completion or cancellation.
* Performs the load, returning on completion or cancellation.
*
*
* @throws IOException If the input could not be loaded.
* @throws IOException If the input could not be loaded.
...
@@ -250,15 +245,17 @@ public final class Loader implements LoaderErrorThrower {
...
@@ -250,15 +245,17 @@ public final class Loader implements LoaderErrorThrower {
private
static
final
int
MSG_IO_EXCEPTION
=
3
;
private
static
final
int
MSG_IO_EXCEPTION
=
3
;
private
static
final
int
MSG_FATAL_ERROR
=
4
;
private
static
final
int
MSG_FATAL_ERROR
=
4
;
private
final
T
loadable
;
private
final
Loader
.
Callback
<
T
>
callback
;
public
final
int
defaultMinRetryCount
;
public
final
int
defaultMinRetryCount
;
private
final
T
loadable
;
private
final
long
startTimeMs
;
private
final
long
startTimeMs
;
private
@Nullable
Loader
.
Callback
<
T
>
callback
;
private
IOException
currentError
;
private
IOException
currentError
;
private
int
errorCount
;
private
int
errorCount
;
private
volatile
Thread
executorThread
;
private
volatile
Thread
executorThread
;
private
volatile
boolean
canceled
;
private
volatile
boolean
released
;
private
volatile
boolean
released
;
public
LoadTask
(
Looper
looper
,
T
loadable
,
Loader
.
Callback
<
T
>
callback
,
public
LoadTask
(
Looper
looper
,
T
loadable
,
Loader
.
Callback
<
T
>
callback
,
...
@@ -295,6 +292,7 @@ public final class Loader implements LoaderErrorThrower {
...
@@ -295,6 +292,7 @@ public final class Loader implements LoaderErrorThrower {
sendEmptyMessage
(
MSG_CANCEL
);
sendEmptyMessage
(
MSG_CANCEL
);
}
}
}
else
{
}
else
{
canceled
=
true
;
loadable
.
cancelLoad
();
loadable
.
cancelLoad
();
if
(
executorThread
!=
null
)
{
if
(
executorThread
!=
null
)
{
executorThread
.
interrupt
();
executorThread
.
interrupt
();
...
@@ -304,6 +302,11 @@ public final class Loader implements LoaderErrorThrower {
...
@@ -304,6 +302,11 @@ public final class Loader implements LoaderErrorThrower {
finish
();
finish
();
long
nowMs
=
SystemClock
.
elapsedRealtime
();
long
nowMs
=
SystemClock
.
elapsedRealtime
();
callback
.
onLoadCanceled
(
loadable
,
nowMs
,
nowMs
-
startTimeMs
,
true
);
callback
.
onLoadCanceled
(
loadable
,
nowMs
,
nowMs
-
startTimeMs
,
true
);
// If loading, this task will be referenced from a GC root (the loading thread) until
// cancellation completes. The time taken for cancellation to complete depends on the
// implementation of the Loadable that the task is loading. We null the callback reference
// here so that it doesn't prevent garbage collection whilst cancellation is ongoing.
callback
=
null
;
}
}
}
}
...
@@ -311,7 +314,7 @@ public final class Loader implements LoaderErrorThrower {
...
@@ -311,7 +314,7 @@ public final class Loader implements LoaderErrorThrower {
public
void
run
()
{
public
void
run
()
{
try
{
try
{
executorThread
=
Thread
.
currentThread
();
executorThread
=
Thread
.
currentThread
();
if
(!
loadable
.
isLoadCanceled
()
)
{
if
(!
canceled
)
{
TraceUtil
.
beginSection
(
"load:"
+
loadable
.
getClass
().
getSimpleName
());
TraceUtil
.
beginSection
(
"load:"
+
loadable
.
getClass
().
getSimpleName
());
try
{
try
{
loadable
.
load
();
loadable
.
load
();
...
@@ -328,7 +331,7 @@ public final class Loader implements LoaderErrorThrower {
...
@@ -328,7 +331,7 @@ public final class Loader implements LoaderErrorThrower {
}
}
}
catch
(
InterruptedException
e
)
{
}
catch
(
InterruptedException
e
)
{
// The load was canceled.
// The load was canceled.
Assertions
.
checkState
(
loadable
.
isLoadCanceled
()
);
Assertions
.
checkState
(
canceled
);
if
(!
released
)
{
if
(!
released
)
{
sendEmptyMessage
(
MSG_END_OF_SOURCE
);
sendEmptyMessage
(
MSG_END_OF_SOURCE
);
}
}
...
@@ -373,7 +376,7 @@ public final class Loader implements LoaderErrorThrower {
...
@@ -373,7 +376,7 @@ public final class Loader implements LoaderErrorThrower {
finish
();
finish
();
long
nowMs
=
SystemClock
.
elapsedRealtime
();
long
nowMs
=
SystemClock
.
elapsedRealtime
();
long
durationMs
=
nowMs
-
startTimeMs
;
long
durationMs
=
nowMs
-
startTimeMs
;
if
(
loadable
.
isLoadCanceled
()
)
{
if
(
canceled
)
{
callback
.
onLoadCanceled
(
loadable
,
nowMs
,
durationMs
,
false
);
callback
.
onLoadCanceled
(
loadable
,
nowMs
,
durationMs
,
false
);
return
;
return
;
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/ParsingLoadable.java
View file @
f7ed789f
...
@@ -78,7 +78,6 @@ public final class ParsingLoadable<T> implements Loadable {
...
@@ -78,7 +78,6 @@ public final class ParsingLoadable<T> implements Loadable {
private
final
Parser
<?
extends
T
>
parser
;
private
final
Parser
<?
extends
T
>
parser
;
private
volatile
T
result
;
private
volatile
T
result
;
private
volatile
boolean
isCanceled
;
private
volatile
long
bytesLoaded
;
private
volatile
long
bytesLoaded
;
/**
/**
...
@@ -128,14 +127,7 @@ public final class ParsingLoadable<T> implements Loadable {
...
@@ -128,14 +127,7 @@ public final class ParsingLoadable<T> implements Loadable {
@Override
@Override
public
final
void
cancelLoad
()
{
public
final
void
cancelLoad
()
{
// We don't actually cancel anything, but we need to record the cancellation so that
// Do nothing.
// isLoadCanceled can return the correct value.
isCanceled
=
true
;
}
@Override
public
final
boolean
isLoadCanceled
()
{
return
isCanceled
;
}
}
@Override
@Override
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java
View file @
f7ed789f
...
@@ -18,7 +18,6 @@ package com.google.android.exoplayer2.upstream.cache;
...
@@ -18,7 +18,6 @@ package com.google.android.exoplayer2.upstream.cache;
import
android.net.Uri
;
import
android.net.Uri
;
import
android.support.annotation.IntDef
;
import
android.support.annotation.IntDef
;
import
android.support.annotation.Nullable
;
import
android.support.annotation.Nullable
;
import
android.util.Log
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.upstream.DataSink
;
import
com.google.android.exoplayer2.upstream.DataSink
;
import
com.google.android.exoplayer2.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.DataSource
;
...
@@ -52,8 +51,6 @@ public final class CacheDataSource implements DataSource {
...
@@ -52,8 +51,6 @@ public final class CacheDataSource implements DataSource {
*/
*/
public
static
final
long
DEFAULT_MAX_CACHE_FILE_SIZE
=
2
*
1024
*
1024
;
public
static
final
long
DEFAULT_MAX_CACHE_FILE_SIZE
=
2
*
1024
*
1024
;
private
static
final
String
TAG
=
"CacheDataSource"
;
/**
/**
* Flags controlling the cache's behavior.
* Flags controlling the cache's behavior.
*/
*/
...
@@ -221,7 +218,7 @@ public final class CacheDataSource implements DataSource {
...
@@ -221,7 +218,7 @@ public final class CacheDataSource implements DataSource {
try
{
try
{
key
=
CacheUtil
.
getKey
(
dataSpec
);
key
=
CacheUtil
.
getKey
(
dataSpec
);
uri
=
dataSpec
.
uri
;
uri
=
dataSpec
.
uri
;
actualUri
=
loadRedirectedUriOrReturnGivenUri
(
cache
,
key
,
uri
);
actualUri
=
getRedirectedUriOrDefault
(
cache
,
key
,
/* defaultUri= */
uri
);
flags
=
dataSpec
.
flags
;
flags
=
dataSpec
.
flags
;
readPosition
=
dataSpec
.
position
;
readPosition
=
dataSpec
.
position
;
...
@@ -272,7 +269,7 @@ public final class CacheDataSource implements DataSource {
...
@@ -272,7 +269,7 @@ public final class CacheDataSource implements DataSource {
bytesRemaining
-=
bytesRead
;
bytesRemaining
-=
bytesRead
;
}
}
}
else
if
(
currentDataSpecLengthUnset
)
{
}
else
if
(
currentDataSpecLengthUnset
)
{
set
BytesRemainingAndMaybeStoreLength
(
0
);
set
NoBytesRemainingAndMaybeStoreLength
(
);
}
else
if
(
bytesRemaining
>
0
||
bytesRemaining
==
C
.
LENGTH_UNSET
)
{
}
else
if
(
bytesRemaining
>
0
||
bytesRemaining
==
C
.
LENGTH_UNSET
)
{
closeCurrentSource
();
closeCurrentSource
();
openNextSource
(
false
);
openNextSource
(
false
);
...
@@ -281,7 +278,7 @@ public final class CacheDataSource implements DataSource {
...
@@ -281,7 +278,7 @@ public final class CacheDataSource implements DataSource {
return
bytesRead
;
return
bytesRead
;
}
catch
(
IOException
e
)
{
}
catch
(
IOException
e
)
{
if
(
currentDataSpecLengthUnset
&&
isCausedByPositionOutOfRange
(
e
))
{
if
(
currentDataSpecLengthUnset
&&
isCausedByPositionOutOfRange
(
e
))
{
set
BytesRemainingAndMaybeStoreLength
(
0
);
set
NoBytesRemainingAndMaybeStoreLength
(
);
return
C
.
RESULT_END_OF_INPUT
;
return
C
.
RESULT_END_OF_INPUT
;
}
}
handleBeforeThrow
(
e
);
handleBeforeThrow
(
e
);
...
@@ -402,46 +399,38 @@ public final class CacheDataSource implements DataSource {
...
@@ -402,46 +399,38 @@ public final class CacheDataSource implements DataSource {
currentDataSource
=
nextDataSource
;
currentDataSource
=
nextDataSource
;
currentDataSpecLengthUnset
=
nextDataSpec
.
length
==
C
.
LENGTH_UNSET
;
currentDataSpecLengthUnset
=
nextDataSpec
.
length
==
C
.
LENGTH_UNSET
;
long
resolvedLength
=
nextDataSource
.
open
(
nextDataSpec
);
long
resolvedLength
=
nextDataSource
.
open
(
nextDataSpec
);
// Update bytesRemaining, actualUri and (if writing to cache) the cache metadata.
ContentMetadataMutations
mutations
=
new
ContentMetadataMutations
();
if
(
currentDataSpecLengthUnset
&&
resolvedLength
!=
C
.
LENGTH_UNSET
)
{
if
(
currentDataSpecLengthUnset
&&
resolvedLength
!=
C
.
LENGTH_UNSET
)
{
setBytesRemainingAndMaybeStoreLength
(
resolvedLength
);
bytesRemaining
=
resolvedLength
;
ContentMetadataInternal
.
setContentLength
(
mutations
,
readPosition
+
bytesRemaining
);
}
}
// TODO find a way to store length and redirected uri in one metadata mutation.
if
(
isReadingFromUpstream
())
{
maybeUpdateActualUriFieldAndRedirectedUriMetadata
();
actualUri
=
currentDataSource
.
getUri
();
}
boolean
isRedirected
=
!
uri
.
equals
(
actualUri
);
if
(
isRedirected
)
{
private
void
maybeUpdateActualUriFieldAndRedirectedUriMetadata
()
{
ContentMetadataInternal
.
setRedirectedUri
(
mutations
,
actualUri
);
if
(!
isReadingFromUpstream
())
{
}
else
{
return
;
ContentMetadataInternal
.
removeRedirectedUri
(
mutations
);
}
}
if
(
isWritingToCache
())
{
cache
.
applyContentMetadataMutations
(
key
,
mutations
);
}
}
actualUri
=
currentDataSource
.
getUri
();
maybeUpdateRedirectedUriMetadata
();
}
}
private
void
maybeUpdateRedirectedUriMetadata
()
{
private
void
setNoBytesRemainingAndMaybeStoreLength
()
throws
IOException
{
if
(!
isWritingToCache
())
{
bytesRemaining
=
0
;
return
;
if
(
isWritingToCache
())
{
}
cache
.
setContentLength
(
key
,
readPosition
);
ContentMetadataMutations
mutations
=
new
ContentMetadataMutations
();
boolean
isRedirected
=
!
uri
.
equals
(
actualUri
);
if
(
isRedirected
)
{
ContentMetadataInternal
.
setRedirectedUri
(
mutations
,
actualUri
);
}
else
{
ContentMetadataInternal
.
removeRedirectedUri
(
mutations
);
}
try
{
cache
.
applyContentMetadataMutations
(
key
,
mutations
);
}
catch
(
CacheException
e
)
{
String
message
=
"Couldn't update redirected URI. "
+
"This might cause relative URIs get resolved incorrectly."
;
Log
.
w
(
TAG
,
message
,
e
);
}
}
}
}
private
static
Uri
loadRedirectedUriOrReturnGivenUri
(
Cache
cache
,
String
key
,
Uri
u
ri
)
{
private
static
Uri
getRedirectedUriOrDefault
(
Cache
cache
,
String
key
,
Uri
defaultU
ri
)
{
ContentMetadata
contentMetadata
=
cache
.
getContentMetadata
(
key
);
ContentMetadata
contentMetadata
=
cache
.
getContentMetadata
(
key
);
Uri
redirectedUri
=
ContentMetadataInternal
.
getRedirectedUri
(
contentMetadata
);
Uri
redirectedUri
=
ContentMetadataInternal
.
getRedirectedUri
(
contentMetadata
);
return
redirectedUri
==
null
?
u
ri
:
redirectedUri
;
return
redirectedUri
==
null
?
defaultU
ri
:
redirectedUri
;
}
}
private
static
boolean
isCausedByPositionOutOfRange
(
IOException
e
)
{
private
static
boolean
isCausedByPositionOutOfRange
(
IOException
e
)
{
...
@@ -458,13 +447,6 @@ public final class CacheDataSource implements DataSource {
...
@@ -458,13 +447,6 @@ public final class CacheDataSource implements DataSource {
return
false
;
return
false
;
}
}
private
void
setBytesRemainingAndMaybeStoreLength
(
long
bytesRemaining
)
throws
IOException
{
this
.
bytesRemaining
=
bytesRemaining
;
if
(
isWritingToCache
())
{
cache
.
setContentLength
(
key
,
readPosition
+
bytesRemaining
);
}
}
private
boolean
isReadingFromUpstream
()
{
private
boolean
isReadingFromUpstream
()
{
return
!
isReadingFromCache
();
return
!
isReadingFromCache
();
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java
View file @
f7ed789f
...
@@ -129,11 +129,11 @@ public final class CacheUtil {
...
@@ -129,11 +129,11 @@ public final class CacheUtil {
cache
,
cache
,
new
CacheDataSource
(
cache
,
upstream
),
new
CacheDataSource
(
cache
,
upstream
),
new
byte
[
DEFAULT_BUFFER_SIZE_BYTES
],
new
byte
[
DEFAULT_BUFFER_SIZE_BYTES
],
null
,
/* priorityTaskManager= */
null
,
0
,
/* priority= */
0
,
counters
,
counters
,
null
,
isCanceled
,
false
);
/* enableEOFException= */
false
);
}
}
/**
/**
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/ContentMetadataInternal.java
View file @
f7ed789f
...
@@ -20,7 +20,7 @@ import android.support.annotation.Nullable;
...
@@ -20,7 +20,7 @@ import android.support.annotation.Nullable;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
/** Helper classes to easily access and modify internal metadata values. */
/** Helper classes to easily access and modify internal metadata values. */
/*
package
*/
final
class
ContentMetadataInternal
{
/*
package
*/
final
class
ContentMetadataInternal
{
private
static
final
String
PREFIX
=
ContentMetadata
.
INTERNAL_METADATA_NAME_PREFIX
;
private
static
final
String
PREFIX
=
ContentMetadata
.
INTERNAL_METADATA_NAME_PREFIX
;
private
static
final
String
METADATA_NAME_REDIRECTED_URI
=
PREFIX
+
"redir"
;
private
static
final
String
METADATA_NAME_REDIRECTED_URI
=
PREFIX
+
"redir"
;
...
@@ -59,4 +59,8 @@ import com.google.android.exoplayer2.C;
...
@@ -59,4 +59,8 @@ import com.google.android.exoplayer2.C;
public
static
void
removeRedirectedUri
(
ContentMetadataMutations
mutations
)
{
public
static
void
removeRedirectedUri
(
ContentMetadataMutations
mutations
)
{
mutations
.
remove
(
METADATA_NAME_REDIRECTED_URI
);
mutations
.
remove
(
METADATA_NAME_REDIRECTED_URI
);
}
}
private
ContentMetadataInternal
()
{
// Prevent instantiation.
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/upstream/crypto/AesFlushingCipher.java
View file @
f7ed789f
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
upstream
.
crypto
;
package
com
.
google
.
android
.
exoplayer2
.
upstream
.
crypto
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Util
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
import
java.security.InvalidAlgorithmParameterException
;
import
java.security.InvalidAlgorithmParameterException
;
import
java.security.InvalidKeyException
;
import
java.security.InvalidKeyException
;
...
@@ -49,7 +50,9 @@ public final class AesFlushingCipher {
...
@@ -49,7 +50,9 @@ public final class AesFlushingCipher {
flushedBlock
=
new
byte
[
blockSize
];
flushedBlock
=
new
byte
[
blockSize
];
long
counter
=
offset
/
blockSize
;
long
counter
=
offset
/
blockSize
;
int
startPadding
=
(
int
)
(
offset
%
blockSize
);
int
startPadding
=
(
int
)
(
offset
%
blockSize
);
cipher
.
init
(
mode
,
new
SecretKeySpec
(
secretKey
,
cipher
.
getAlgorithm
().
split
(
"/"
)[
0
]),
cipher
.
init
(
mode
,
new
SecretKeySpec
(
secretKey
,
Util
.
splitAtFirst
(
cipher
.
getAlgorithm
(),
"/"
)[
0
]),
new
IvParameterSpec
(
getInitializationVector
(
nonce
,
counter
)));
new
IvParameterSpec
(
getInitializationVector
(
nonce
,
counter
)));
if
(
startPadding
!=
0
)
{
if
(
startPadding
!=
0
)
{
updateInPlace
(
new
byte
[
startPadding
],
0
,
startPadding
);
updateInPlace
(
new
byte
[
startPadding
],
0
,
startPadding
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/ColorParser.java
View file @
f7ed789f
...
@@ -26,7 +26,7 @@ import java.util.regex.Pattern;
...
@@ -26,7 +26,7 @@ import java.util.regex.Pattern;
*
*
* @see <a href="https://w3c.github.io/webvtt/#styling">WebVTT CSS Styling</a>
* @see <a href="https://w3c.github.io/webvtt/#styling">WebVTT CSS Styling</a>
* @see <a href="https://www.w3.org/TR/ttml2/">Timed Text Markup Language 2 (TTML2) - 10.3.5</a>
* @see <a href="https://www.w3.org/TR/ttml2/">Timed Text Markup Language 2 (TTML2) - 10.3.5</a>
*
*
/
*/
public
final
class
ColorParser
{
public
final
class
ColorParser
{
private
static
final
String
RGB
=
"rgb"
;
private
static
final
String
RGB
=
"rgb"
;
...
@@ -271,4 +271,7 @@ public final class ColorParser {
...
@@ -271,4 +271,7 @@ public final class ColorParser {
COLOR_MAP
.
put
(
"yellowgreen"
,
0xFF9ACD32
);
COLOR_MAP
.
put
(
"yellowgreen"
,
0xFF9ACD32
);
}
}
private
ColorParser
()
{
// Prevent instantiation.
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/util/EGLSurfaceTexture.java
View file @
f7ed789f
...
@@ -111,12 +111,20 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL
...
@@ -111,12 +111,20 @@ public final class EGLSurfaceTexture implements SurfaceTexture.OnFrameAvailableL
GLES20
.
glDeleteTextures
(
1
,
textureIdHolder
,
0
);
GLES20
.
glDeleteTextures
(
1
,
textureIdHolder
,
0
);
}
}
}
finally
{
}
finally
{
if
(
display
!=
null
&&
!
display
.
equals
(
EGL14
.
EGL_NO_DISPLAY
))
{
EGL14
.
eglMakeCurrent
(
display
,
EGL14
.
EGL_NO_SURFACE
,
EGL14
.
EGL_NO_SURFACE
,
EGL14
.
EGL_NO_CONTEXT
);
}
if
(
surface
!=
null
&&
!
surface
.
equals
(
EGL14
.
EGL_NO_SURFACE
))
{
if
(
surface
!=
null
&&
!
surface
.
equals
(
EGL14
.
EGL_NO_SURFACE
))
{
EGL14
.
eglDestroySurface
(
display
,
surface
);
EGL14
.
eglDestroySurface
(
display
,
surface
);
}
}
if
(
context
!=
null
)
{
if
(
context
!=
null
)
{
EGL14
.
eglDestroyContext
(
display
,
context
);
EGL14
.
eglDestroyContext
(
display
,
context
);
}
}
// EGL14.eglReleaseThread could crash before Android K (see [internal: b/11327779]).
if
(
Util
.
SDK_INT
>=
19
)
{
EGL14
.
eglReleaseThread
();
}
display
=
null
;
display
=
null
;
context
=
null
;
context
=
null
;
surface
=
null
;
surface
=
null
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java
View file @
f7ed789f
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.util;
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.util;
import
android.support.annotation.Nullable
;
import
android.support.annotation.Nullable
;
import
android.text.TextUtils
;
import
android.text.TextUtils
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
java.util.ArrayList
;
/**
/**
* Defines common MIME types and helper methods.
* Defines common MIME types and helper methods.
...
@@ -92,7 +93,29 @@ public final class MimeTypes {
...
@@ -92,7 +93,29 @@ public final class MimeTypes {
public
static
final
String
APPLICATION_DVBSUBS
=
BASE_TYPE_APPLICATION
+
"/dvbsubs"
;
public
static
final
String
APPLICATION_DVBSUBS
=
BASE_TYPE_APPLICATION
+
"/dvbsubs"
;
public
static
final
String
APPLICATION_EXIF
=
BASE_TYPE_APPLICATION
+
"/x-exif"
;
public
static
final
String
APPLICATION_EXIF
=
BASE_TYPE_APPLICATION
+
"/x-exif"
;
private
MimeTypes
()
{}
private
static
final
ArrayList
<
CustomMimeType
>
customMimeTypes
=
new
ArrayList
<>();
/**
* Registers a custom MIME type. Most applications do not need to call this method, as handling of
* standard MIME types is built in. These built-in MIME types take precedence over any registered
* via this method. If this method is used, it must be called before creating any player(s).
*
* @param mimeType The custom MIME type to register.
* @param codecPrefix The RFC 6381-style codec string prefix associated with the MIME type.
* @param trackType The {@link C}{@code .TRACK_TYPE_*} constant associated with the MIME type.
* This value is ignored if the top-level type of {@code mimeType} is audio, video or text.
*/
public
static
void
registerCustomMimeType
(
String
mimeType
,
String
codecPrefix
,
int
trackType
)
{
CustomMimeType
customMimeType
=
new
CustomMimeType
(
mimeType
,
codecPrefix
,
trackType
);
int
customMimeTypeCount
=
customMimeTypes
.
size
();
for
(
int
i
=
0
;
i
<
customMimeTypeCount
;
i
++)
{
if
(
mimeType
.
equals
(
customMimeTypes
.
get
(
i
).
mimeType
))
{
customMimeTypes
.
remove
(
i
);
break
;
}
}
customMimeTypes
.
add
(
customMimeType
);
}
/**
/**
* Whether the top-level type of {@code mimeType} is audio.
* Whether the top-level type of {@code mimeType} is audio.
...
@@ -144,7 +167,7 @@ public final class MimeTypes {
...
@@ -144,7 +167,7 @@ public final class MimeTypes {
if
(
codecs
==
null
)
{
if
(
codecs
==
null
)
{
return
null
;
return
null
;
}
}
String
[]
codecList
=
codecs
.
split
(
","
);
String
[]
codecList
=
Util
.
split
(
codecs
,
","
);
for
(
String
codec
:
codecList
)
{
for
(
String
codec
:
codecList
)
{
String
mimeType
=
getMediaMimeType
(
codec
);
String
mimeType
=
getMediaMimeType
(
codec
);
if
(
mimeType
!=
null
&&
isVideo
(
mimeType
))
{
if
(
mimeType
!=
null
&&
isVideo
(
mimeType
))
{
...
@@ -164,7 +187,7 @@ public final class MimeTypes {
...
@@ -164,7 +187,7 @@ public final class MimeTypes {
if
(
codecs
==
null
)
{
if
(
codecs
==
null
)
{
return
null
;
return
null
;
}
}
String
[]
codecList
=
codecs
.
split
(
","
);
String
[]
codecList
=
Util
.
split
(
codecs
,
","
);
for
(
String
codec
:
codecList
)
{
for
(
String
codec
:
codecList
)
{
String
mimeType
=
getMediaMimeType
(
codec
);
String
mimeType
=
getMediaMimeType
(
codec
);
if
(
mimeType
!=
null
&&
isAudio
(
mimeType
))
{
if
(
mimeType
!=
null
&&
isAudio
(
mimeType
))
{
...
@@ -222,8 +245,9 @@ public final class MimeTypes {
...
@@ -222,8 +245,9 @@ public final class MimeTypes {
return
MimeTypes
.
AUDIO_OPUS
;
return
MimeTypes
.
AUDIO_OPUS
;
}
else
if
(
codec
.
startsWith
(
"vorbis"
))
{
}
else
if
(
codec
.
startsWith
(
"vorbis"
))
{
return
MimeTypes
.
AUDIO_VORBIS
;
return
MimeTypes
.
AUDIO_VORBIS
;
}
else
{
return
getCustomMimeTypeForCodec
(
codec
);
}
}
return
null
;
}
}
/**
/**
...
@@ -236,18 +260,28 @@ public final class MimeTypes {
...
@@ -236,18 +260,28 @@ public final class MimeTypes {
@Nullable
@Nullable
public
static
String
getMimeTypeFromMp4ObjectType
(
int
objectType
)
{
public
static
String
getMimeTypeFromMp4ObjectType
(
int
objectType
)
{
switch
(
objectType
)
{
switch
(
objectType
)
{
case
0x60
:
case
0x61
:
return
MimeTypes
.
VIDEO_MPEG2
;
case
0x20
:
case
0x20
:
return
MimeTypes
.
VIDEO_MP4V
;
return
MimeTypes
.
VIDEO_MP4V
;
case
0x21
:
case
0x21
:
return
MimeTypes
.
VIDEO_H264
;
return
MimeTypes
.
VIDEO_H264
;
case
0x23
:
case
0x23
:
return
MimeTypes
.
VIDEO_H265
;
return
MimeTypes
.
VIDEO_H265
;
case
0x60
:
case
0x61
:
case
0x62
:
case
0x63
:
case
0x64
:
case
0x65
:
return
MimeTypes
.
VIDEO_MPEG2
;
case
0x6A
:
return
MimeTypes
.
VIDEO_MPEG
;
case
0x69
:
case
0x69
:
case
0x6B
:
case
0x6B
:
return
MimeTypes
.
AUDIO_MPEG
;
return
MimeTypes
.
AUDIO_MPEG
;
case
0xA3
:
return
MimeTypes
.
VIDEO_VC1
;
case
0xB1
:
return
MimeTypes
.
VIDEO_VP9
;
case
0x40
:
case
0x40
:
case
0x66
:
case
0x66
:
case
0x67
:
case
0x67
:
...
@@ -298,7 +332,7 @@ public final class MimeTypes {
...
@@ -298,7 +332,7 @@ public final class MimeTypes {
||
APPLICATION_CAMERA_MOTION
.
equals
(
mimeType
))
{
||
APPLICATION_CAMERA_MOTION
.
equals
(
mimeType
))
{
return
C
.
TRACK_TYPE_METADATA
;
return
C
.
TRACK_TYPE_METADATA
;
}
else
{
}
else
{
return
C
.
TRACK_TYPE_UNKNOWN
;
return
getTrackTypeForCustomMimeType
(
mimeType
)
;
}
}
}
}
...
@@ -355,4 +389,41 @@ public final class MimeTypes {
...
@@ -355,4 +389,41 @@ public final class MimeTypes {
return
mimeType
.
substring
(
0
,
indexOfSlash
);
return
mimeType
.
substring
(
0
,
indexOfSlash
);
}
}
private
static
@Nullable
String
getCustomMimeTypeForCodec
(
String
codec
)
{
int
customMimeTypeCount
=
customMimeTypes
.
size
();
for
(
int
i
=
0
;
i
<
customMimeTypeCount
;
i
++)
{
CustomMimeType
customMimeType
=
customMimeTypes
.
get
(
i
);
if
(
codec
.
startsWith
(
customMimeType
.
codecPrefix
))
{
return
customMimeType
.
mimeType
;
}
}
return
null
;
}
private
static
int
getTrackTypeForCustomMimeType
(
String
mimeType
)
{
int
customMimeTypeCount
=
customMimeTypes
.
size
();
for
(
int
i
=
0
;
i
<
customMimeTypeCount
;
i
++)
{
CustomMimeType
customMimeType
=
customMimeTypes
.
get
(
i
);
if
(
mimeType
.
equals
(
customMimeType
.
mimeType
))
{
return
customMimeType
.
trackType
;
}
}
return
C
.
TRACK_TYPE_UNKNOWN
;
}
private
MimeTypes
()
{
// Prevent instantiation.
}
private
static
final
class
CustomMimeType
{
public
final
String
mimeType
;
public
final
String
codecPrefix
;
public
final
int
trackType
;
public
CustomMimeType
(
String
mimeType
,
String
codecPrefix
,
int
trackType
)
{
this
.
mimeType
=
mimeType
;
this
.
codecPrefix
=
codecPrefix
;
this
.
trackType
=
trackType
;
}
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/util/ParsableBitArray.java
View file @
f7ed789f
...
@@ -175,7 +175,7 @@ public final class ParsableBitArray {
...
@@ -175,7 +175,7 @@ public final class ParsableBitArray {
bitOffset
-=
8
;
bitOffset
-=
8
;
returnValue
|=
(
data
[
byteOffset
++]
&
0xFF
)
<<
bitOffset
;
returnValue
|=
(
data
[
byteOffset
++]
&
0xFF
)
<<
bitOffset
;
}
}
returnValue
|=
(
data
[
byteOffset
]
&
0xFF
)
>>
8
-
bitOffset
;
returnValue
|=
(
data
[
byteOffset
]
&
0xFF
)
>>
(
8
-
bitOffset
)
;
returnValue
&=
0xFFFFFFFF
>>>
(
32
-
numBits
);
returnValue
&=
0xFFFFFFFF
>>>
(
32
-
numBits
);
if
(
bitOffset
==
8
)
{
if
(
bitOffset
==
8
)
{
bitOffset
=
0
;
bitOffset
=
0
;
...
@@ -199,17 +199,18 @@ public final class ParsableBitArray {
...
@@ -199,17 +199,18 @@ public final class ParsableBitArray {
int
to
=
offset
+
(
numBits
>>
3
)
/* numBits / 8 */
;
int
to
=
offset
+
(
numBits
>>
3
)
/* numBits / 8 */
;
for
(
int
i
=
offset
;
i
<
to
;
i
++)
{
for
(
int
i
=
offset
;
i
<
to
;
i
++)
{
buffer
[
i
]
=
(
byte
)
(
data
[
byteOffset
++]
<<
bitOffset
);
buffer
[
i
]
=
(
byte
)
(
data
[
byteOffset
++]
<<
bitOffset
);
buffer
[
i
]
|=
(
data
[
byteOffset
]
&
0xFF
)
>>
(
8
-
bitOffset
);
buffer
[
i
]
=
(
byte
)
(
buffer
[
i
]
|
((
data
[
byteOffset
]
&
0xFF
)
>>
(
8
-
bitOffset
))
);
}
}
// Trailing bits.
// Trailing bits.
int
bitsLeft
=
numBits
&
7
/* numBits % 8 */
;
int
bitsLeft
=
numBits
&
7
/* numBits % 8 */
;
if
(
bitsLeft
==
0
)
{
if
(
bitsLeft
==
0
)
{
return
;
return
;
}
}
buffer
[
to
]
&=
0xFF
>>
bitsLeft
;
// Set to 0 the bits that are going to be overwritten.
// Set bits that are going to be overwritten to 0.
buffer
[
to
]
=
(
byte
)
(
buffer
[
to
]
&
(
0xFF
>>
bitsLeft
));
if
(
bitOffset
+
bitsLeft
>
8
)
{
if
(
bitOffset
+
bitsLeft
>
8
)
{
// We read the rest of data[byteOffset] and increase byteOffset.
// We read the rest of data[byteOffset] and increase byteOffset.
buffer
[
to
]
|=
(
byte
)
((
data
[
byteOffset
++]
&
0xFF
)
<<
bitOffset
);
buffer
[
to
]
=
(
byte
)
(
buffer
[
to
]
|
((
data
[
byteOffset
++]
&
0xFF
)
<<
bitOffset
)
);
bitOffset
-=
8
;
bitOffset
-=
8
;
}
}
bitOffset
+=
bitsLeft
;
bitOffset
+=
bitsLeft
;
...
@@ -280,9 +281,10 @@ public final class ParsableBitArray {
...
@@ -280,9 +281,10 @@ public final class ParsableBitArray {
int
firstByteReadSize
=
Math
.
min
(
8
-
bitOffset
,
numBits
);
int
firstByteReadSize
=
Math
.
min
(
8
-
bitOffset
,
numBits
);
int
firstByteRightPaddingSize
=
8
-
bitOffset
-
firstByteReadSize
;
int
firstByteRightPaddingSize
=
8
-
bitOffset
-
firstByteReadSize
;
int
firstByteBitmask
=
(
0xFF00
>>
bitOffset
)
|
((
1
<<
firstByteRightPaddingSize
)
-
1
);
int
firstByteBitmask
=
(
0xFF00
>>
bitOffset
)
|
((
1
<<
firstByteRightPaddingSize
)
-
1
);
data
[
byteOffset
]
&=
firstByteBitmask
;
data
[
byteOffset
]
=
(
byte
)
(
data
[
byteOffset
]
&
firstByteBitmask
)
;
int
firstByteInputBits
=
value
>>>
(
numBits
-
firstByteReadSize
);
int
firstByteInputBits
=
value
>>>
(
numBits
-
firstByteReadSize
);
data
[
byteOffset
]
|=
firstByteInputBits
<<
firstByteRightPaddingSize
;
data
[
byteOffset
]
=
(
byte
)
(
data
[
byteOffset
]
|
(
firstByteInputBits
<<
firstByteRightPaddingSize
));
remainingBitsToRead
-=
firstByteReadSize
;
remainingBitsToRead
-=
firstByteReadSize
;
int
currentByteIndex
=
byteOffset
+
1
;
int
currentByteIndex
=
byteOffset
+
1
;
while
(
remainingBitsToRead
>
8
)
{
while
(
remainingBitsToRead
>
8
)
{
...
@@ -290,9 +292,11 @@ public final class ParsableBitArray {
...
@@ -290,9 +292,11 @@ public final class ParsableBitArray {
remainingBitsToRead
-=
8
;
remainingBitsToRead
-=
8
;
}
}
int
lastByteRightPaddingSize
=
8
-
remainingBitsToRead
;
int
lastByteRightPaddingSize
=
8
-
remainingBitsToRead
;
data
[
currentByteIndex
]
&=
(
1
<<
lastByteRightPaddingSize
)
-
1
;
data
[
currentByteIndex
]
=
(
byte
)
(
data
[
currentByteIndex
]
&
((
1
<<
lastByteRightPaddingSize
)
-
1
));
int
lastByteInput
=
value
&
((
1
<<
remainingBitsToRead
)
-
1
);
int
lastByteInput
=
value
&
((
1
<<
remainingBitsToRead
)
-
1
);
data
[
currentByteIndex
]
|=
lastByteInput
<<
lastByteRightPaddingSize
;
data
[
currentByteIndex
]
=
(
byte
)
(
data
[
currentByteIndex
]
|
(
lastByteInput
<<
lastByteRightPaddingSize
));
skipBits
(
numBits
);
skipBits
(
numBits
);
assertValidOffset
();
assertValidOffset
();
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java
View file @
f7ed789f
...
@@ -470,7 +470,7 @@ public final class ParsableByteArray {
...
@@ -470,7 +470,7 @@ public final class ParsableByteArray {
if
(
lastIndex
<
limit
&&
data
[
lastIndex
]
==
0
)
{
if
(
lastIndex
<
limit
&&
data
[
lastIndex
]
==
0
)
{
stringLength
--;
stringLength
--;
}
}
String
result
=
new
String
(
data
,
position
,
stringLength
);
String
result
=
Util
.
fromUtf8Bytes
(
data
,
position
,
stringLength
);
position
+=
length
;
position
+=
length
;
return
result
;
return
result
;
}
}
...
@@ -489,7 +489,7 @@ public final class ParsableByteArray {
...
@@ -489,7 +489,7 @@ public final class ParsableByteArray {
while
(
stringLimit
<
limit
&&
data
[
stringLimit
]
!=
0
)
{
while
(
stringLimit
<
limit
&&
data
[
stringLimit
]
!=
0
)
{
stringLimit
++;
stringLimit
++;
}
}
String
string
=
new
String
(
data
,
position
,
stringLimit
-
position
);
String
string
=
Util
.
fromUtf8Bytes
(
data
,
position
,
stringLimit
-
position
);
position
=
stringLimit
;
position
=
stringLimit
;
if
(
position
<
limit
)
{
if
(
position
<
limit
)
{
position
++;
position
++;
...
@@ -520,7 +520,7 @@ public final class ParsableByteArray {
...
@@ -520,7 +520,7 @@ public final class ParsableByteArray {
// There's a byte order mark at the start of the line. Discard it.
// There's a byte order mark at the start of the line. Discard it.
position
+=
3
;
position
+=
3
;
}
}
String
line
=
new
String
(
data
,
position
,
lineLimit
-
position
);
String
line
=
Util
.
fromUtf8Bytes
(
data
,
position
,
lineLimit
-
position
);
position
=
lineLimit
;
position
=
lineLimit
;
if
(
position
==
limit
)
{
if
(
position
==
limit
)
{
return
line
;
return
line
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/ParsableNalUnitBitArray.java
View file @
f7ed789f
...
@@ -140,7 +140,7 @@ public final class ParsableNalUnitBitArray {
...
@@ -140,7 +140,7 @@ public final class ParsableNalUnitBitArray {
returnValue
|=
(
data
[
byteOffset
]
&
0xFF
)
<<
bitOffset
;
returnValue
|=
(
data
[
byteOffset
]
&
0xFF
)
<<
bitOffset
;
byteOffset
+=
shouldSkipByte
(
byteOffset
+
1
)
?
2
:
1
;
byteOffset
+=
shouldSkipByte
(
byteOffset
+
1
)
?
2
:
1
;
}
}
returnValue
|=
(
data
[
byteOffset
]
&
0xFF
)
>>
8
-
bitOffset
;
returnValue
|=
(
data
[
byteOffset
]
&
0xFF
)
>>
(
8
-
bitOffset
)
;
returnValue
&=
0xFFFFFFFF
>>>
(
32
-
numBits
);
returnValue
&=
0xFFFFFFFF
>>>
(
32
-
numBits
);
if
(
bitOffset
==
8
)
{
if
(
bitOffset
==
8
)
{
bitOffset
=
0
;
bitOffset
=
0
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/Util.java
View file @
f7ed789f
...
@@ -311,10 +311,10 @@ public final class Util {
...
@@ -311,10 +311,10 @@ public final class Util {
* Returns a normalized RFC 639-2/T code for {@code language}.
* Returns a normalized RFC 639-2/T code for {@code language}.
*
*
* @param language A case-insensitive ISO 639 alpha-2 or alpha-3 language code.
* @param language A case-insensitive ISO 639 alpha-2 or alpha-3 language code.
* @return The all-lowercase normalized code, or null if the input was null, or
* @return The all-lowercase normalized code, or null if the input was null, or
{@code
*
{@code
language.toLowerCase()} if the language could not be normalized.
* language.toLowerCase()} if the language could not be normalized.
*/
*/
public
static
String
normalizeLanguageCode
(
String
language
)
{
public
static
@Nullable
String
normalizeLanguageCode
(
@Nullable
String
language
)
{
try
{
try
{
return
language
==
null
?
null
:
new
Locale
(
language
).
getISO3Language
();
return
language
==
null
?
null
:
new
Locale
(
language
).
getISO3Language
();
}
catch
(
MissingResourceException
e
)
{
}
catch
(
MissingResourceException
e
)
{
...
@@ -333,6 +333,18 @@ public final class Util {
...
@@ -333,6 +333,18 @@ public final class Util {
}
}
/**
/**
* Returns a new {@link String} constructed by decoding UTF-8 encoded bytes in a subarray.
*
* @param bytes The UTF-8 encoded bytes to decode.
* @param offset The index of the first byte to decode.
* @param length The number of bytes to decode.
* @return The string.
*/
public
static
String
fromUtf8Bytes
(
byte
[]
bytes
,
int
offset
,
int
length
)
{
return
new
String
(
bytes
,
offset
,
length
,
Charset
.
forName
(
C
.
UTF8_NAME
));
}
/**
* Returns a new byte array containing the code points of a {@link String} encoded using UTF-8.
* Returns a new byte array containing the code points of a {@link String} encoded using UTF-8.
*
*
* @param value The {@link String} whose bytes should be obtained.
* @param value The {@link String} whose bytes should be obtained.
...
@@ -343,6 +355,33 @@ public final class Util {
...
@@ -343,6 +355,33 @@ public final class Util {
}
}
/**
/**
* Splits a string using {@code value.split(regex, -1}). Note: this is is similar to {@link
* String#split(String)} but empty matches at the end of the string will not be omitted from the
* returned array.
*
* @param value The string to split.
* @param regex A delimiting regular expression.
* @return The array of strings resulting from splitting the string.
*/
public
static
String
[]
split
(
String
value
,
String
regex
)
{
return
value
.
split
(
regex
,
/* limit= */
-
1
);
}
/**
* Splits the string at the first occurrence of the delimiter {@code regex}. If the delimiter does
* not match, returns an array with one element which is the input string. If the delimiter does
* match, returns an array with the portion of the string before the delimiter and the rest of the
* string.
*
* @param value The string.
* @param regex A delimiting regular expression.
* @return The string split by the first occurrence of the delimiter.
*/
public
static
String
[]
splitAtFirst
(
String
value
,
String
regex
)
{
return
value
.
split
(
regex
,
/* limit= */
2
);
}
/**
* Returns whether the given character is a carriage return ('\r') or a line feed ('\n').
* Returns whether the given character is a carriage return ('\r') or a line feed ('\n').
*
*
* @param c The character.
* @param c The character.
...
@@ -978,7 +1017,7 @@ public final class Util {
...
@@ -978,7 +1017,7 @@ public final class Util {
if
(
TextUtils
.
isEmpty
(
codecs
))
{
if
(
TextUtils
.
isEmpty
(
codecs
))
{
return
null
;
return
null
;
}
}
String
[]
codecArray
=
codecs
.
trim
().
split
(
"(\\s*,\\s*)"
);
String
[]
codecArray
=
split
(
codecs
.
trim
(),
"(\\s*,\\s*)"
);
StringBuilder
builder
=
new
StringBuilder
();
StringBuilder
builder
=
new
StringBuilder
();
for
(
String
codec
:
codecArray
)
{
for
(
String
codec
:
codecArray
)
{
if
(
trackType
==
MimeTypes
.
getTrackTypeOfCodec
(
codec
))
{
if
(
trackType
==
MimeTypes
.
getTrackTypeOfCodec
(
codec
))
{
...
@@ -1454,7 +1493,7 @@ public final class Util {
...
@@ -1454,7 +1493,7 @@ public final class Util {
// If we managed to read sys.display-size, attempt to parse it.
// If we managed to read sys.display-size, attempt to parse it.
if
(!
TextUtils
.
isEmpty
(
sysDisplaySize
))
{
if
(!
TextUtils
.
isEmpty
(
sysDisplaySize
))
{
try
{
try
{
String
[]
sysDisplaySizeParts
=
s
ysDisplaySize
.
trim
().
split
(
"x"
);
String
[]
sysDisplaySizeParts
=
s
plit
(
sysDisplaySize
.
trim
(),
"x"
);
if
(
sysDisplaySizeParts
.
length
==
2
)
{
if
(
sysDisplaySizeParts
.
length
==
2
)
{
int
width
=
Integer
.
parseInt
(
sysDisplaySizeParts
[
0
]);
int
width
=
Integer
.
parseInt
(
sysDisplaySizeParts
[
0
]);
int
height
=
Integer
.
parseInt
(
sysDisplaySizeParts
[
1
]);
int
height
=
Integer
.
parseInt
(
sysDisplaySizeParts
[
1
]);
...
...
library/core/src/main/java/com/google/android/exoplayer2/video/DummySurface.java
View file @
f7ed789f
...
@@ -156,7 +156,7 @@ public final class DummySurface extends Surface {
...
@@ -156,7 +156,7 @@ public final class DummySurface extends Surface {
private
static
final
int
MSG_INIT
=
1
;
private
static
final
int
MSG_INIT
=
1
;
private
static
final
int
MSG_RELEASE
=
2
;
private
static
final
int
MSG_RELEASE
=
2
;
private
@MonotonicNonNull
EGLSurfaceTexture
eglSurfaceTexure
;
private
@MonotonicNonNull
EGLSurfaceTexture
eglSurfaceTex
t
ure
;
private
@MonotonicNonNull
Handler
handler
;
private
@MonotonicNonNull
Handler
handler
;
private
@Nullable
Error
initError
;
private
@Nullable
Error
initError
;
private
@Nullable
RuntimeException
initException
;
private
@Nullable
RuntimeException
initException
;
...
@@ -169,7 +169,7 @@ public final class DummySurface extends Surface {
...
@@ -169,7 +169,7 @@ public final class DummySurface extends Surface {
public
DummySurface
init
(
@SecureMode
int
secureMode
)
{
public
DummySurface
init
(
@SecureMode
int
secureMode
)
{
start
();
start
();
handler
=
new
Handler
(
getLooper
(),
/* callback= */
this
);
handler
=
new
Handler
(
getLooper
(),
/* callback= */
this
);
eglSurfaceTexure
=
new
EGLSurfaceTexture
(
handler
);
eglSurfaceTex
t
ure
=
new
EGLSurfaceTexture
(
handler
);
boolean
wasInterrupted
=
false
;
boolean
wasInterrupted
=
false
;
synchronized
(
this
)
{
synchronized
(
this
)
{
handler
.
obtainMessage
(
MSG_INIT
,
secureMode
,
0
).
sendToTarget
();
handler
.
obtainMessage
(
MSG_INIT
,
secureMode
,
0
).
sendToTarget
();
...
@@ -232,16 +232,16 @@ public final class DummySurface extends Surface {
...
@@ -232,16 +232,16 @@ public final class DummySurface extends Surface {
}
}
private
void
initInternal
(
@SecureMode
int
secureMode
)
{
private
void
initInternal
(
@SecureMode
int
secureMode
)
{
Assertions
.
checkNotNull
(
eglSurfaceTexure
);
Assertions
.
checkNotNull
(
eglSurfaceTex
t
ure
);
eglSurfaceTexure
.
init
(
secureMode
);
eglSurfaceTex
t
ure
.
init
(
secureMode
);
this
.
surface
=
this
.
surface
=
new
DummySurface
(
new
DummySurface
(
this
,
eglSurfaceTexure
.
getSurfaceTexture
(),
secureMode
!=
SECURE_MODE_NONE
);
this
,
eglSurfaceTex
t
ure
.
getSurfaceTexture
(),
secureMode
!=
SECURE_MODE_NONE
);
}
}
private
void
releaseInternal
()
{
private
void
releaseInternal
()
{
Assertions
.
checkNotNull
(
eglSurfaceTexure
);
Assertions
.
checkNotNull
(
eglSurfaceTex
t
ure
);
eglSurfaceTexure
.
release
();
eglSurfaceTex
t
ure
.
release
();
}
}
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
View file @
f7ed789f
...
@@ -205,7 +205,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -205,7 +205,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
this
.
allowedJoiningTimeMs
=
allowedJoiningTimeMs
;
this
.
allowedJoiningTimeMs
=
allowedJoiningTimeMs
;
this
.
maxDroppedFramesToNotify
=
maxDroppedFramesToNotify
;
this
.
maxDroppedFramesToNotify
=
maxDroppedFramesToNotify
;
this
.
context
=
context
.
getApplicationContext
();
this
.
context
=
context
.
getApplicationContext
();
frameReleaseTimeHelper
=
new
VideoFrameReleaseTimeHelper
(
context
);
frameReleaseTimeHelper
=
new
VideoFrameReleaseTimeHelper
(
this
.
context
);
eventDispatcher
=
new
EventDispatcher
(
eventHandler
,
eventListener
);
eventDispatcher
=
new
EventDispatcher
(
eventHandler
,
eventListener
);
deviceNeedsAutoFrcWorkaround
=
deviceNeedsAutoFrcWorkaround
();
deviceNeedsAutoFrcWorkaround
=
deviceNeedsAutoFrcWorkaround
();
pendingOutputStreamOffsetsUs
=
new
long
[
MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT
];
pendingOutputStreamOffsetsUs
=
new
long
[
MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT
];
...
@@ -1177,8 +1177,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1177,8 +1177,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
// https://github.com/google/ExoPlayer/issues/3835,
// https://github.com/google/ExoPlayer/issues/3835,
// https://github.com/google/ExoPlayer/issues/4006,
// https://github.com/google/ExoPlayer/issues/4006,
// https://github.com/google/ExoPlayer/issues/4084,
// https://github.com/google/ExoPlayer/issues/4084,
// https://github.com/google/ExoPlayer/issues/4104.
// https://github.com/google/ExoPlayer/issues/4104,
// https://github.com/google/ExoPlayer/issues/4134.
// https://github.com/google/ExoPlayer/issues/4134,
// https://github.com/google/ExoPlayer/issues/4315.
return
((
"deb"
.
equals
(
Util
.
DEVICE
)
// Nexus 7 (2013)
return
((
"deb"
.
equals
(
Util
.
DEVICE
)
// Nexus 7 (2013)
||
"flo"
.
equals
(
Util
.
DEVICE
)
// Nexus 7 (2013)
||
"flo"
.
equals
(
Util
.
DEVICE
)
// Nexus 7 (2013)
||
"mido"
.
equals
(
Util
.
DEVICE
)
// Redmi Note 4
||
"mido"
.
equals
(
Util
.
DEVICE
)
// Redmi Note 4
...
@@ -1192,7 +1193,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1192,7 +1193,8 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
||
"M5c"
.
equals
(
Util
.
DEVICE
)
// Meizu M5C
||
"M5c"
.
equals
(
Util
.
DEVICE
)
// Meizu M5C
||
"QM16XE_U"
.
equals
(
Util
.
DEVICE
)
// Philips QM163E
||
"QM16XE_U"
.
equals
(
Util
.
DEVICE
)
// Philips QM163E
||
"A7010a48"
.
equals
(
Util
.
DEVICE
)
// Lenovo K4 Note
||
"A7010a48"
.
equals
(
Util
.
DEVICE
)
// Lenovo K4 Note
||
"woods_f"
.
equals
(
Util
.
MODEL
))
// Moto E (4)
||
"woods_f"
.
equals
(
Util
.
MODEL
)
// Moto E (4)
||
"watson"
.
equals
(
Util
.
DEVICE
))
// Moto C
&&
"OMX.MTK.VIDEO.DECODER.AVC"
.
equals
(
name
))
&&
"OMX.MTK.VIDEO.DECODER.AVC"
.
equals
(
name
))
||
((
"ALE-L21"
.
equals
(
Util
.
MODEL
)
// Huawei P8 Lite
||
((
"ALE-L21"
.
equals
(
Util
.
MODEL
)
// Huawei P8 Lite
||
"CAM-L21"
.
equals
(
Util
.
MODEL
))
// Huawei Y6II
||
"CAM-L21"
.
equals
(
Util
.
MODEL
))
// Huawei Y6II
...
...
library/core/src/main/java/com/google/android/exoplayer2/video/VideoFrameReleaseTimeHelper.java
View file @
f7ed789f
...
@@ -72,8 +72,12 @@ public final class VideoFrameReleaseTimeHelper {
...
@@ -72,8 +72,12 @@ public final class VideoFrameReleaseTimeHelper {
* @param context A context from which information about the default display can be retrieved.
* @param context A context from which information about the default display can be retrieved.
*/
*/
public
VideoFrameReleaseTimeHelper
(
@Nullable
Context
context
)
{
public
VideoFrameReleaseTimeHelper
(
@Nullable
Context
context
)
{
windowManager
=
context
==
null
?
null
if
(
context
!=
null
)
{
:
(
WindowManager
)
context
.
getSystemService
(
Context
.
WINDOW_SERVICE
);
context
=
context
.
getApplicationContext
();
windowManager
=
(
WindowManager
)
context
.
getSystemService
(
Context
.
WINDOW_SERVICE
);
}
else
{
windowManager
=
null
;
}
if
(
windowManager
!=
null
)
{
if
(
windowManager
!=
null
)
{
displayListener
=
Util
.
SDK_INT
>=
17
?
maybeBuildDefaultDisplayListenerV17
(
context
)
:
null
;
displayListener
=
Util
.
SDK_INT
>=
17
?
maybeBuildDefaultDisplayListenerV17
(
context
)
:
null
;
vsyncSampler
=
VSyncSampler
.
getInstance
();
vsyncSampler
=
VSyncSampler
.
getInstance
();
...
...
library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
View file @
f7ed789f
...
@@ -1980,6 +1980,105 @@ public final class ExoPlayerTest {
...
@@ -1980,6 +1980,105 @@ public final class ExoPlayerTest {
.
inOrder
();
.
inOrder
();
}
}
@Test
public
void
testRecursivePlayerChangesReportConsistentValuesForAllListeners
()
throws
Exception
{
// We add two listeners to the player. The first stops the player as soon as it's ready and both
// record the state change events they receive.
final
AtomicReference
<
Player
>
playerReference
=
new
AtomicReference
<>();
final
List
<
Integer
>
eventListener1States
=
new
ArrayList
<>();
final
List
<
Integer
>
eventListener2States
=
new
ArrayList
<>();
final
EventListener
eventListener1
=
new
DefaultEventListener
()
{
@Override
public
void
onPlayerStateChanged
(
boolean
playWhenReady
,
int
playbackState
)
{
eventListener1States
.
add
(
playbackState
);
if
(
playbackState
==
Player
.
STATE_READY
)
{
playerReference
.
get
().
stop
(
/* reset= */
true
);
}
}
};
final
EventListener
eventListener2
=
new
DefaultEventListener
()
{
@Override
public
void
onPlayerStateChanged
(
boolean
playWhenReady
,
int
playbackState
)
{
eventListener2States
.
add
(
playbackState
);
}
};
ActionSchedule
actionSchedule
=
new
ActionSchedule
.
Builder
(
"testRecursivePlayerChanges"
)
.
executeRunnable
(
new
PlayerRunnable
()
{
@Override
public
void
run
(
SimpleExoPlayer
player
)
{
playerReference
.
set
(
player
);
player
.
addListener
(
eventListener1
);
player
.
addListener
(
eventListener2
);
}
})
.
build
();
new
ExoPlayerTestRunner
.
Builder
()
.
setActionSchedule
(
actionSchedule
)
.
build
()
.
start
()
.
blockUntilEnded
(
TIMEOUT_MS
);
assertThat
(
eventListener1States
)
.
containsExactly
(
Player
.
STATE_BUFFERING
,
Player
.
STATE_READY
,
Player
.
STATE_IDLE
)
.
inOrder
();
assertThat
(
eventListener2States
)
.
containsExactly
(
Player
.
STATE_BUFFERING
,
Player
.
STATE_READY
,
Player
.
STATE_IDLE
)
.
inOrder
();
}
@Test
public
void
testRecursivePlayerChangesAreReportedInCorrectOrder
()
throws
Exception
{
// The listener stops the player as soon as it's ready (which should report a timeline and state
// change) and sets playWhenReady to false when the timeline callback is received.
final
AtomicReference
<
Player
>
playerReference
=
new
AtomicReference
<>();
final
List
<
Boolean
>
eventListenerPlayWhenReady
=
new
ArrayList
<>();
final
List
<
Integer
>
eventListenerStates
=
new
ArrayList
<>();
final
EventListener
eventListener
=
new
DefaultEventListener
()
{
@Override
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
int
reason
)
{
if
(
timeline
.
isEmpty
())
{
playerReference
.
get
().
setPlayWhenReady
(
/* playWhenReady= */
false
);
}
}
@Override
public
void
onPlayerStateChanged
(
boolean
playWhenReady
,
int
playbackState
)
{
eventListenerPlayWhenReady
.
add
(
playWhenReady
);
eventListenerStates
.
add
(
playbackState
);
if
(
playbackState
==
Player
.
STATE_READY
)
{
playerReference
.
get
().
stop
(
/* reset= */
true
);
}
}
};
ActionSchedule
actionSchedule
=
new
ActionSchedule
.
Builder
(
"testRecursivePlayerChanges"
)
.
executeRunnable
(
new
PlayerRunnable
()
{
@Override
public
void
run
(
SimpleExoPlayer
player
)
{
playerReference
.
set
(
player
);
player
.
addListener
(
eventListener
);
}
})
.
build
();
new
ExoPlayerTestRunner
.
Builder
()
.
setActionSchedule
(
actionSchedule
)
.
build
()
.
start
()
.
blockUntilEnded
(
TIMEOUT_MS
);
assertThat
(
eventListenerStates
)
.
containsExactly
(
Player
.
STATE_BUFFERING
,
Player
.
STATE_READY
,
Player
.
STATE_IDLE
,
Player
.
STATE_IDLE
)
.
inOrder
();
assertThat
(
eventListenerPlayWhenReady
).
containsExactly
(
true
,
true
,
true
,
false
).
inOrder
();
}
// Internal methods.
// Internal methods.
private
static
ActionSchedule
.
Builder
addSurfaceSwitch
(
ActionSchedule
.
Builder
builder
)
{
private
static
ActionSchedule
.
Builder
addSurfaceSwitch
(
ActionSchedule
.
Builder
builder
)
{
...
...
library/core/src/test/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelectionTest.java
View file @
f7ed789f
...
@@ -392,11 +392,6 @@ public final class AdaptiveTrackSelectionTest {
...
@@ -392,11 +392,6 @@ public final class AdaptiveTrackSelectionTest {
}
}
@Override
@Override
public
boolean
isLoadCanceled
()
{
return
false
;
}
@Override
public
void
load
()
throws
IOException
,
InterruptedException
{
public
void
load
()
throws
IOException
,
InterruptedException
{
// Do nothing.
// Do nothing.
}
}
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java
View file @
f7ed789f
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
source
.
dash
;
package
com
.
google
.
android
.
exoplayer2
.
source
.
dash
;
import
android.support.annotation.IntDef
;
import
android.support.annotation.IntDef
;
import
android.support.annotation.Nullable
;
import
android.util.Pair
;
import
android.util.Pair
;
import
android.util.SparseArray
;
import
android.util.SparseArray
;
import
android.util.SparseIntArray
;
import
android.util.SparseIntArray
;
...
@@ -72,7 +73,7 @@ import java.util.List;
...
@@ -72,7 +73,7 @@ import java.util.List;
private
final
IdentityHashMap
<
ChunkSampleStream
<
DashChunkSource
>,
PlayerTrackEmsgHandler
>
private
final
IdentityHashMap
<
ChunkSampleStream
<
DashChunkSource
>,
PlayerTrackEmsgHandler
>
trackEmsgHandlerBySampleStream
;
trackEmsgHandlerBySampleStream
;
private
Callback
callback
;
private
@Nullable
Callback
callback
;
private
ChunkSampleStream
<
DashChunkSource
>[]
sampleStreams
;
private
ChunkSampleStream
<
DashChunkSource
>[]
sampleStreams
;
private
EventSampleStream
[]
eventSampleStreams
;
private
EventSampleStream
[]
eventSampleStreams
;
private
SequenceableLoader
compositeSequenceableLoader
;
private
SequenceableLoader
compositeSequenceableLoader
;
...
@@ -150,6 +151,7 @@ import java.util.List;
...
@@ -150,6 +151,7 @@ import java.util.List;
for
(
ChunkSampleStream
<
DashChunkSource
>
sampleStream
:
sampleStreams
)
{
for
(
ChunkSampleStream
<
DashChunkSource
>
sampleStream
:
sampleStreams
)
{
sampleStream
.
release
(
this
);
sampleStream
.
release
(
this
);
}
}
callback
=
null
;
eventDispatcher
.
mediaPeriodReleased
();
eventDispatcher
.
mediaPeriodReleased
();
}
}
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashWrappingSegmentIndex.java
View file @
f7ed789f
...
@@ -25,12 +25,15 @@ import com.google.android.exoplayer2.source.dash.manifest.RangedUri;
...
@@ -25,12 +25,15 @@ import com.google.android.exoplayer2.source.dash.manifest.RangedUri;
public
final
class
DashWrappingSegmentIndex
implements
DashSegmentIndex
{
public
final
class
DashWrappingSegmentIndex
implements
DashSegmentIndex
{
private
final
ChunkIndex
chunkIndex
;
private
final
ChunkIndex
chunkIndex
;
private
final
long
timeOffsetUs
;
/**
/**
* @param chunkIndex The {@link ChunkIndex} to wrap.
* @param chunkIndex The {@link ChunkIndex} to wrap.
* @param timeOffsetUs An offset to subtract from the times in the wrapped index, in microseconds.
*/
*/
public
DashWrappingSegmentIndex
(
ChunkIndex
chunkIndex
)
{
public
DashWrappingSegmentIndex
(
ChunkIndex
chunkIndex
,
long
timeOffsetUs
)
{
this
.
chunkIndex
=
chunkIndex
;
this
.
chunkIndex
=
chunkIndex
;
this
.
timeOffsetUs
=
timeOffsetUs
;
}
}
@Override
@Override
...
@@ -45,7 +48,7 @@ public final class DashWrappingSegmentIndex implements DashSegmentIndex {
...
@@ -45,7 +48,7 @@ public final class DashWrappingSegmentIndex implements DashSegmentIndex {
@Override
@Override
public
long
getTimeUs
(
long
segmentNum
)
{
public
long
getTimeUs
(
long
segmentNum
)
{
return
chunkIndex
.
timesUs
[(
int
)
segmentNum
];
return
chunkIndex
.
timesUs
[(
int
)
segmentNum
]
-
timeOffsetUs
;
}
}
@Override
@Override
...
@@ -61,7 +64,7 @@ public final class DashWrappingSegmentIndex implements DashSegmentIndex {
...
@@ -61,7 +64,7 @@ public final class DashWrappingSegmentIndex implements DashSegmentIndex {
@Override
@Override
public
long
getSegmentNum
(
long
timeUs
,
long
periodDurationUs
)
{
public
long
getSegmentNum
(
long
timeUs
,
long
periodDurationUs
)
{
return
chunkIndex
.
getChunkIndex
(
timeUs
);
return
chunkIndex
.
getChunkIndex
(
timeUs
+
timeOffsetUs
);
}
}
@Override
@Override
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java
View file @
f7ed789f
...
@@ -354,7 +354,10 @@ public class DefaultDashChunkSource implements DashChunkSource {
...
@@ -354,7 +354,10 @@ public class DefaultDashChunkSource implements DashChunkSource {
if
(
representationHolder
.
segmentIndex
==
null
)
{
if
(
representationHolder
.
segmentIndex
==
null
)
{
SeekMap
seekMap
=
representationHolder
.
extractorWrapper
.
getSeekMap
();
SeekMap
seekMap
=
representationHolder
.
extractorWrapper
.
getSeekMap
();
if
(
seekMap
!=
null
)
{
if
(
seekMap
!=
null
)
{
representationHolder
.
segmentIndex
=
new
DashWrappingSegmentIndex
((
ChunkIndex
)
seekMap
);
representationHolder
.
segmentIndex
=
new
DashWrappingSegmentIndex
(
(
ChunkIndex
)
seekMap
,
representationHolder
.
representation
.
presentationTimeOffsetUs
);
}
}
}
}
}
}
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/offline/DashDownloader.java
View file @
f7ed789f
...
@@ -167,7 +167,9 @@ public final class DashDownloader extends SegmentDownloader<DashManifest, Repres
...
@@ -167,7 +167,9 @@ public final class DashDownloader extends SegmentDownloader<DashManifest, Repres
return
index
;
return
index
;
}
}
ChunkIndex
seekMap
=
DashUtil
.
loadChunkIndex
(
dataSource
,
trackType
,
representation
);
ChunkIndex
seekMap
=
DashUtil
.
loadChunkIndex
(
dataSource
,
trackType
,
representation
);
return
seekMap
==
null
?
null
:
new
DashWrappingSegmentIndex
(
seekMap
);
return
seekMap
==
null
?
null
:
new
DashWrappingSegmentIndex
(
seekMap
,
representation
.
presentationTimeOffsetUs
);
}
}
}
}
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java
View file @
f7ed789f
...
@@ -104,7 +104,7 @@ import java.util.List;
...
@@ -104,7 +104,7 @@ import java.util.List;
// the way in which HlsSampleStreamWrapper generates track groups. Use only index based methods
// the way in which HlsSampleStreamWrapper generates track groups. Use only index based methods
// in TrackSelection to avoid unexpected behavior.
// in TrackSelection to avoid unexpected behavior.
private
TrackSelection
trackSelection
;
private
TrackSelection
trackSelection
;
private
long
liveEdgeTimeUs
;
private
long
liveEdge
InPeriod
TimeUs
;
private
boolean
seenExpectedPlaylistError
;
private
boolean
seenExpectedPlaylistError
;
/**
/**
...
@@ -128,7 +128,7 @@ import java.util.List;
...
@@ -128,7 +128,7 @@ import java.util.List;
this
.
variants
=
variants
;
this
.
variants
=
variants
;
this
.
timestampAdjusterProvider
=
timestampAdjusterProvider
;
this
.
timestampAdjusterProvider
=
timestampAdjusterProvider
;
this
.
muxedCaptionFormats
=
muxedCaptionFormats
;
this
.
muxedCaptionFormats
=
muxedCaptionFormats
;
liveEdgeTimeUs
=
C
.
TIME_UNSET
;
liveEdge
InPeriod
TimeUs
=
C
.
TIME_UNSET
;
Format
[]
variantFormats
=
new
Format
[
variants
.
length
];
Format
[]
variantFormats
=
new
Format
[
variants
.
length
];
int
[]
initialTrackSelection
=
new
int
[
variants
.
length
];
int
[]
initialTrackSelection
=
new
int
[
variants
.
length
];
for
(
int
i
=
0
;
i
<
variants
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
variants
.
length
;
i
++)
{
...
@@ -254,16 +254,17 @@ import java.util.List;
...
@@ -254,16 +254,17 @@ import java.util.List;
// Select the chunk.
// Select the chunk.
long
chunkMediaSequence
;
long
chunkMediaSequence
;
long
startOfPlaylistInPeriodUs
=
mediaPlaylist
.
startTimeUs
-
playlistTracker
.
getInitialStartTimeUs
();
if
(
previous
==
null
||
switchingVariant
)
{
if
(
previous
==
null
||
switchingVariant
)
{
long
targetPositionUs
=
(
previous
==
null
||
independentSegments
)
?
loadPositionUs
long
endOfPlaylistInPeriodUs
=
startOfPlaylistInPeriodUs
+
mediaPlaylist
.
durationUs
;
:
previous
.
startTimeUs
;
long
targetPositionInPeriodUs
=
if
(!
mediaPlaylist
.
hasEndTag
&&
targetPositionUs
>=
mediaPlaylist
.
getEndTimeUs
())
{
(
previous
==
null
||
independentSegments
)
?
loadPositionUs
:
previous
.
startTimeUs
;
if
(!
mediaPlaylist
.
hasEndTag
&&
targetPositionInPeriodUs
>=
endOfPlaylistInPeriodUs
)
{
// If the playlist is too old to contain the chunk, we need to refresh it.
// If the playlist is too old to contain the chunk, we need to refresh it.
chunkMediaSequence
=
mediaPlaylist
.
mediaSequence
+
mediaPlaylist
.
segments
.
size
();
chunkMediaSequence
=
mediaPlaylist
.
mediaSequence
+
mediaPlaylist
.
segments
.
size
();
}
else
{
}
else
{
long
positionOfPlaylistInPeriodUs
=
long
targetPositionInPlaylistUs
=
targetPositionInPeriodUs
-
startOfPlaylistInPeriodUs
;
mediaPlaylist
.
startTimeUs
-
playlistTracker
.
getInitialStartTimeUs
();
long
targetPositionInPlaylistUs
=
targetPositionUs
-
positionOfPlaylistInPeriodUs
;
chunkMediaSequence
=
chunkMediaSequence
=
Util
.
binarySearchFloor
(
Util
.
binarySearchFloor
(
mediaPlaylist
.
segments
,
mediaPlaylist
.
segments
,
...
@@ -277,6 +278,8 @@ import java.util.List;
...
@@ -277,6 +278,8 @@ import java.util.List;
selectedVariantIndex
=
oldVariantIndex
;
selectedVariantIndex
=
oldVariantIndex
;
selectedUrl
=
variants
[
selectedVariantIndex
];
selectedUrl
=
variants
[
selectedVariantIndex
];
mediaPlaylist
=
playlistTracker
.
getPlaylistSnapshot
(
selectedUrl
);
mediaPlaylist
=
playlistTracker
.
getPlaylistSnapshot
(
selectedUrl
);
startOfPlaylistInPeriodUs
=
mediaPlaylist
.
startTimeUs
-
playlistTracker
.
getInitialStartTimeUs
();
chunkMediaSequence
=
previous
.
getNextChunkIndex
();
chunkMediaSequence
=
previous
.
getNextChunkIndex
();
}
}
}
}
...
@@ -331,9 +334,7 @@ import java.util.List;
...
@@ -331,9 +334,7 @@ import java.util.List;
}
}
// Compute start time of the next chunk.
// Compute start time of the next chunk.
long
positionOfPlaylistInPeriodUs
=
long
segmentStartTimeInPeriodUs
=
startOfPlaylistInPeriodUs
+
segment
.
relativeStartTimeUs
;
mediaPlaylist
.
startTimeUs
-
playlistTracker
.
getInitialStartTimeUs
();
long
segmentStartTimeInPeriodUs
=
positionOfPlaylistInPeriodUs
+
segment
.
relativeStartTimeUs
;
int
discontinuitySequence
=
mediaPlaylist
.
discontinuitySequence
int
discontinuitySequence
=
mediaPlaylist
.
discontinuitySequence
+
segment
.
relativeDiscontinuitySequence
;
+
segment
.
relativeDiscontinuitySequence
;
TimestampAdjuster
timestampAdjuster
=
timestampAdjusterProvider
.
getAdjuster
(
TimestampAdjuster
timestampAdjuster
=
timestampAdjusterProvider
.
getAdjuster
(
...
@@ -420,12 +421,17 @@ import java.util.List;
...
@@ -420,12 +421,17 @@ import java.util.List;
// Private methods.
// Private methods.
private
long
resolveTimeToLiveEdgeUs
(
long
playbackPositionUs
)
{
private
long
resolveTimeToLiveEdgeUs
(
long
playbackPositionUs
)
{
final
boolean
resolveTimeToLiveEdgePossible
=
liveEdgeTimeUs
!=
C
.
TIME_UNSET
;
final
boolean
resolveTimeToLiveEdgePossible
=
liveEdgeInPeriodTimeUs
!=
C
.
TIME_UNSET
;
return
resolveTimeToLiveEdgePossible
?
liveEdgeTimeUs
-
playbackPositionUs
:
C
.
TIME_UNSET
;
return
resolveTimeToLiveEdgePossible
?
liveEdgeInPeriodTimeUs
-
playbackPositionUs
:
C
.
TIME_UNSET
;
}
}
private
void
updateLiveEdgeTimeUs
(
HlsMediaPlaylist
mediaPlaylist
)
{
private
void
updateLiveEdgeTimeUs
(
HlsMediaPlaylist
mediaPlaylist
)
{
liveEdgeTimeUs
=
mediaPlaylist
.
hasEndTag
?
C
.
TIME_UNSET
:
mediaPlaylist
.
getEndTimeUs
();
liveEdgeInPeriodTimeUs
=
mediaPlaylist
.
hasEndTag
?
C
.
TIME_UNSET
:
(
mediaPlaylist
.
getEndTimeUs
()
-
playlistTracker
.
getInitialStartTimeUs
());
}
}
private
EncryptionKeyChunk
newEncryptionKeyChunk
(
Uri
keyUri
,
String
iv
,
int
variantIndex
,
private
EncryptionKeyChunk
newEncryptionKeyChunk
(
Uri
keyUri
,
String
iv
,
int
variantIndex
,
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java
View file @
f7ed789f
...
@@ -207,11 +207,6 @@ import java.util.concurrent.atomic.AtomicInteger;
...
@@ -207,11 +207,6 @@ import java.util.concurrent.atomic.AtomicInteger;
}
}
@Override
@Override
public
boolean
isLoadCanceled
()
{
return
loadCanceled
;
}
@Override
public
void
load
()
throws
IOException
,
InterruptedException
{
public
void
load
()
throws
IOException
,
InterruptedException
{
maybeLoadInitData
();
maybeLoadInitData
();
if
(!
loadCanceled
)
{
if
(!
loadCanceled
)
{
...
@@ -242,7 +237,7 @@ import java.util.concurrent.atomic.AtomicInteger;
...
@@ -242,7 +237,7 @@ import java.util.concurrent.atomic.AtomicInteger;
initSegmentBytesLoaded
=
(
int
)
(
input
.
getPosition
()
-
initDataSpec
.
absoluteStreamPosition
);
initSegmentBytesLoaded
=
(
int
)
(
input
.
getPosition
()
-
initDataSpec
.
absoluteStreamPosition
);
}
}
}
finally
{
}
finally
{
Util
.
closeQuietly
(
d
ataSource
);
Util
.
closeQuietly
(
initD
ataSource
);
}
}
initLoadCompleted
=
true
;
initLoadCompleted
=
true
;
}
}
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java
View file @
f7ed789f
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
.
source
.
hls
;
package
com
.
google
.
android
.
exoplayer2
.
source
.
hls
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.SeekParameters
;
import
com.google.android.exoplayer2.SeekParameters
;
...
@@ -57,7 +58,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
...
@@ -57,7 +58,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
private
final
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
final
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
final
boolean
allowChunklessPreparation
;
private
final
boolean
allowChunklessPreparation
;
private
Callback
callback
;
private
@Nullable
Callback
callback
;
private
int
pendingPrepareCount
;
private
int
pendingPrepareCount
;
private
TrackGroupArray
trackGroups
;
private
TrackGroupArray
trackGroups
;
private
HlsSampleStreamWrapper
[]
sampleStreamWrappers
;
private
HlsSampleStreamWrapper
[]
sampleStreamWrappers
;
...
@@ -96,6 +97,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
...
@@ -96,6 +97,7 @@ public final class HlsMediaPeriod implements MediaPeriod, HlsSampleStreamWrapper
for
(
HlsSampleStreamWrapper
sampleStreamWrapper
:
sampleStreamWrappers
)
{
for
(
HlsSampleStreamWrapper
sampleStreamWrapper
:
sampleStreamWrappers
)
{
sampleStreamWrapper
.
release
();
sampleStreamWrapper
.
release
();
}
}
callback
=
null
;
eventDispatcher
.
mediaPeriodReleased
();
eventDispatcher
.
mediaPeriodReleased
();
}
}
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
View file @
f7ed789f
...
@@ -32,6 +32,7 @@ import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispat
...
@@ -32,6 +32,7 @@ import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispat
import
com.google.android.exoplayer2.source.SequenceableLoader
;
import
com.google.android.exoplayer2.source.SequenceableLoader
;
import
com.google.android.exoplayer2.source.SinglePeriodTimeline
;
import
com.google.android.exoplayer2.source.SinglePeriodTimeline
;
import
com.google.android.exoplayer2.source.ads.AdsMediaSource
;
import
com.google.android.exoplayer2.source.ads.AdsMediaSource
;
import
com.google.android.exoplayer2.source.hls.playlist.DefaultHlsPlaylistTracker
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist
;
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.HlsPlaylist
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsPlaylistParser
;
...
@@ -58,6 +59,7 @@ public final class HlsMediaSource extends BaseMediaSource
...
@@ -58,6 +59,7 @@ public final class HlsMediaSource extends BaseMediaSource
private
HlsExtractorFactory
extractorFactory
;
private
HlsExtractorFactory
extractorFactory
;
private
@Nullable
ParsingLoadable
.
Parser
<
HlsPlaylist
>
playlistParser
;
private
@Nullable
ParsingLoadable
.
Parser
<
HlsPlaylist
>
playlistParser
;
private
@Nullable
HlsPlaylistTracker
playlistTracker
;
private
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
int
minLoadableRetryCount
;
private
int
minLoadableRetryCount
;
private
boolean
allowChunklessPreparation
;
private
boolean
allowChunklessPreparation
;
...
@@ -136,17 +138,38 @@ public final class HlsMediaSource extends BaseMediaSource
...
@@ -136,17 +138,38 @@ public final class HlsMediaSource extends BaseMediaSource
* Sets the parser to parse HLS playlists. The default is an instance of {@link
* Sets the parser to parse HLS playlists. The default is an instance of {@link
* HlsPlaylistParser}.
* HlsPlaylistParser}.
*
*
* <p>Must not be called after calling {@link #setPlaylistTracker} on the same builder.
*
* @param playlistParser A {@link ParsingLoadable.Parser} for HLS playlists.
* @param playlistParser A {@link ParsingLoadable.Parser} for HLS playlists.
* @return This factory, for convenience.
* @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called.
* @throws IllegalStateException If one of the {@code create} methods has already been called.
*/
*/
public
Factory
setPlaylistParser
(
ParsingLoadable
.
Parser
<
HlsPlaylist
>
playlistParser
)
{
public
Factory
setPlaylistParser
(
ParsingLoadable
.
Parser
<
HlsPlaylist
>
playlistParser
)
{
Assertions
.
checkState
(!
isCreateCalled
);
Assertions
.
checkState
(!
isCreateCalled
);
Assertions
.
checkState
(
playlistTracker
==
null
,
"A playlist tracker has already been set."
);
this
.
playlistParser
=
Assertions
.
checkNotNull
(
playlistParser
);
this
.
playlistParser
=
Assertions
.
checkNotNull
(
playlistParser
);
return
this
;
return
this
;
}
}
/**
/**
* Sets the HLS playlist tracker. The default is an instance of {@link
* DefaultHlsPlaylistTracker}. Playlist trackers must not be shared by {@link HlsMediaSource}
* instances.
*
* <p>Must not be called after calling {@link #setPlaylistParser} on the same builder.
*
* @param playlistTracker A tracker for HLS playlists.
* @return This factory, for convenience.
* @throws IllegalStateException If one of the {@code create} methods has already been called.
*/
public
Factory
setPlaylistTracker
(
HlsPlaylistTracker
playlistTracker
)
{
Assertions
.
checkState
(!
isCreateCalled
);
Assertions
.
checkState
(
playlistParser
==
null
,
"A playlist parser has already been set."
);
this
.
playlistTracker
=
Assertions
.
checkNotNull
(
playlistTracker
);
return
this
;
}
/**
* Sets the factory to create composite {@link SequenceableLoader}s for when this media source
* Sets the factory to create composite {@link SequenceableLoader}s for when this media source
* loads data from multiple streams (video, audio etc...). The default is an instance of {@link
* loads data from multiple streams (video, audio etc...). The default is an instance of {@link
* DefaultCompositeSequenceableLoaderFactory}.
* DefaultCompositeSequenceableLoaderFactory}.
...
@@ -187,8 +210,12 @@ public final class HlsMediaSource extends BaseMediaSource
...
@@ -187,8 +210,12 @@ public final class HlsMediaSource extends BaseMediaSource
@Override
@Override
public
HlsMediaSource
createMediaSource
(
Uri
playlistUri
)
{
public
HlsMediaSource
createMediaSource
(
Uri
playlistUri
)
{
isCreateCalled
=
true
;
isCreateCalled
=
true
;
if
(
playlistParser
==
null
)
{
if
(
playlistTracker
==
null
)
{
playlistParser
=
new
HlsPlaylistParser
();
playlistTracker
=
new
DefaultHlsPlaylistTracker
(
hlsDataSourceFactory
,
minLoadableRetryCount
,
playlistParser
!=
null
?
playlistParser
:
new
HlsPlaylistParser
());
}
}
return
new
HlsMediaSource
(
return
new
HlsMediaSource
(
playlistUri
,
playlistUri
,
...
@@ -196,7 +223,7 @@ public final class HlsMediaSource extends BaseMediaSource
...
@@ -196,7 +223,7 @@ public final class HlsMediaSource extends BaseMediaSource
extractorFactory
,
extractorFactory
,
compositeSequenceableLoaderFactory
,
compositeSequenceableLoaderFactory
,
minLoadableRetryCount
,
minLoadableRetryCount
,
playlist
Pars
er
,
playlist
Track
er
,
allowChunklessPreparation
,
allowChunklessPreparation
,
tag
);
tag
);
}
}
...
@@ -233,12 +260,10 @@ public final class HlsMediaSource extends BaseMediaSource
...
@@ -233,12 +260,10 @@ public final class HlsMediaSource extends BaseMediaSource
private
final
HlsDataSourceFactory
dataSourceFactory
;
private
final
HlsDataSourceFactory
dataSourceFactory
;
private
final
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
final
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
final
int
minLoadableRetryCount
;
private
final
int
minLoadableRetryCount
;
private
final
ParsingLoadable
.
Parser
<
HlsPlaylist
>
playlistParser
;
private
final
boolean
allowChunklessPreparation
;
private
final
boolean
allowChunklessPreparation
;
private
final
HlsPlaylistTracker
playlistTracker
;
private
final
@Nullable
Object
tag
;
private
final
@Nullable
Object
tag
;
private
HlsPlaylistTracker
playlistTracker
;
/**
/**
* @param manifestUri The {@link Uri} of the HLS manifest.
* @param manifestUri The {@link Uri} of the HLS manifest.
* @param dataSourceFactory An {@link HlsDataSourceFactory} for {@link DataSource}s for manifests,
* @param dataSourceFactory An {@link HlsDataSourceFactory} for {@link DataSource}s for manifests,
...
@@ -276,8 +301,13 @@ public final class HlsMediaSource extends BaseMediaSource
...
@@ -276,8 +301,13 @@ public final class HlsMediaSource extends BaseMediaSource
int
minLoadableRetryCount
,
int
minLoadableRetryCount
,
Handler
eventHandler
,
Handler
eventHandler
,
MediaSourceEventListener
eventListener
)
{
MediaSourceEventListener
eventListener
)
{
this
(
manifestUri
,
new
DefaultHlsDataSourceFactory
(
dataSourceFactory
),
this
(
HlsExtractorFactory
.
DEFAULT
,
minLoadableRetryCount
,
eventHandler
,
eventListener
,
manifestUri
,
new
DefaultHlsDataSourceFactory
(
dataSourceFactory
),
HlsExtractorFactory
.
DEFAULT
,
minLoadableRetryCount
,
eventHandler
,
eventListener
,
new
HlsPlaylistParser
());
new
HlsPlaylistParser
());
}
}
...
@@ -309,7 +339,8 @@ public final class HlsMediaSource extends BaseMediaSource
...
@@ -309,7 +339,8 @@ public final class HlsMediaSource extends BaseMediaSource
extractorFactory
,
extractorFactory
,
new
DefaultCompositeSequenceableLoaderFactory
(),
new
DefaultCompositeSequenceableLoaderFactory
(),
minLoadableRetryCount
,
minLoadableRetryCount
,
playlistParser
,
new
DefaultHlsPlaylistTracker
(
dataSourceFactory
,
minLoadableRetryCount
,
new
HlsPlaylistParser
()),
/* allowChunklessPreparation= */
false
,
/* allowChunklessPreparation= */
false
,
/* tag= */
null
);
/* tag= */
null
);
if
(
eventHandler
!=
null
&&
eventListener
!=
null
)
{
if
(
eventHandler
!=
null
&&
eventListener
!=
null
)
{
...
@@ -323,7 +354,7 @@ public final class HlsMediaSource extends BaseMediaSource
...
@@ -323,7 +354,7 @@ public final class HlsMediaSource extends BaseMediaSource
HlsExtractorFactory
extractorFactory
,
HlsExtractorFactory
extractorFactory
,
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
,
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
,
int
minLoadableRetryCount
,
int
minLoadableRetryCount
,
ParsingLoadable
.
Parser
<
HlsPlaylist
>
playlistPars
er
,
HlsPlaylistTracker
playlistTrack
er
,
boolean
allowChunklessPreparation
,
boolean
allowChunklessPreparation
,
@Nullable
Object
tag
)
{
@Nullable
Object
tag
)
{
this
.
manifestUri
=
manifestUri
;
this
.
manifestUri
=
manifestUri
;
...
@@ -331,7 +362,7 @@ public final class HlsMediaSource extends BaseMediaSource
...
@@ -331,7 +362,7 @@ public final class HlsMediaSource extends BaseMediaSource
this
.
extractorFactory
=
extractorFactory
;
this
.
extractorFactory
=
extractorFactory
;
this
.
compositeSequenceableLoaderFactory
=
compositeSequenceableLoaderFactory
;
this
.
compositeSequenceableLoaderFactory
=
compositeSequenceableLoaderFactory
;
this
.
minLoadableRetryCount
=
minLoadableRetryCount
;
this
.
minLoadableRetryCount
=
minLoadableRetryCount
;
this
.
playlist
Parser
=
playlistPars
er
;
this
.
playlist
Tracker
=
playlistTrack
er
;
this
.
allowChunklessPreparation
=
allowChunklessPreparation
;
this
.
allowChunklessPreparation
=
allowChunklessPreparation
;
this
.
tag
=
tag
;
this
.
tag
=
tag
;
}
}
...
@@ -339,9 +370,7 @@ public final class HlsMediaSource extends BaseMediaSource
...
@@ -339,9 +370,7 @@ public final class HlsMediaSource extends BaseMediaSource
@Override
@Override
public
void
prepareSourceInternal
(
ExoPlayer
player
,
boolean
isTopLevelSource
)
{
public
void
prepareSourceInternal
(
ExoPlayer
player
,
boolean
isTopLevelSource
)
{
EventDispatcher
eventDispatcher
=
createEventDispatcher
(
/* mediaPeriodId= */
null
);
EventDispatcher
eventDispatcher
=
createEventDispatcher
(
/* mediaPeriodId= */
null
);
playlistTracker
=
new
HlsPlaylistTracker
(
manifestUri
,
dataSourceFactory
,
eventDispatcher
,
playlistTracker
.
start
(
manifestUri
,
eventDispatcher
,
/* listener= */
this
);
minLoadableRetryCount
,
this
,
playlistParser
);
playlistTracker
.
start
();
}
}
@Override
@Override
...
@@ -373,7 +402,6 @@ public final class HlsMediaSource extends BaseMediaSource
...
@@ -373,7 +402,6 @@ public final class HlsMediaSource extends BaseMediaSource
public
void
releaseSourceInternal
()
{
public
void
releaseSourceInternal
()
{
if
(
playlistTracker
!=
null
)
{
if
(
playlistTracker
!=
null
)
{
playlistTracker
.
release
();
playlistTracker
.
release
();
playlistTracker
=
null
;
}
}
}
}
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloadHelper.java
View file @
f7ed789f
...
@@ -73,6 +73,7 @@ public final class HlsDownloadHelper extends DownloadHelper {
...
@@ -73,6 +73,7 @@ public final class HlsDownloadHelper extends DownloadHelper {
public
TrackGroupArray
getTrackGroups
(
int
periodIndex
)
{
public
TrackGroupArray
getTrackGroups
(
int
periodIndex
)
{
Assertions
.
checkNotNull
(
playlist
);
Assertions
.
checkNotNull
(
playlist
);
if
(
playlist
instanceof
HlsMediaPlaylist
)
{
if
(
playlist
instanceof
HlsMediaPlaylist
)
{
renditionTypes
=
new
int
[
0
];
return
TrackGroupArray
.
EMPTY
;
return
TrackGroupArray
.
EMPTY
;
}
}
// TODO: Generate track groups as in playback. Reverse the mapping in getDownloadAction.
// TODO: Generate track groups as in playback. Reverse the mapping in getDownloadAction.
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/DefaultHlsPlaylistTracker.java
0 → 100644
View file @
f7ed789f
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer2
.
source
.
hls
.
playlist
;
import
android.net.Uri
;
import
android.os.Handler
;
import
android.os.SystemClock
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.ParserException
;
import
com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher
;
import
com.google.android.exoplayer2.source.chunk.ChunkedTrackBlacklistUtil
;
import
com.google.android.exoplayer2.source.hls.HlsDataSourceFactory
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment
;
import
com.google.android.exoplayer2.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.Loader
;
import
com.google.android.exoplayer2.upstream.ParsingLoadable
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.UriUtil
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.IdentityHashMap
;
import
java.util.List
;
/** Default implementation for {@link HlsPlaylistTracker}. */
public
final
class
DefaultHlsPlaylistTracker
implements
HlsPlaylistTracker
,
Loader
.
Callback
<
ParsingLoadable
<
HlsPlaylist
>>
{
/**
* Coefficient applied on the target duration of a playlist to determine the amount of time after
* which an unchanging playlist is considered stuck.
*/
private
static
final
double
PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT
=
3.5
;
private
final
HlsDataSourceFactory
dataSourceFactory
;
private
final
ParsingLoadable
.
Parser
<
HlsPlaylist
>
playlistParser
;
private
final
int
minRetryCount
;
private
final
IdentityHashMap
<
HlsUrl
,
MediaPlaylistBundle
>
playlistBundles
;
private
final
List
<
PlaylistEventListener
>
listeners
;
private
EventDispatcher
eventDispatcher
;
private
Loader
initialPlaylistLoader
;
private
Handler
playlistRefreshHandler
;
private
PrimaryPlaylistListener
primaryPlaylistListener
;
private
HlsMasterPlaylist
masterPlaylist
;
private
HlsUrl
primaryHlsUrl
;
private
HlsMediaPlaylist
primaryUrlSnapshot
;
private
boolean
isLive
;
private
long
initialStartTimeUs
;
/**
* @param dataSourceFactory A factory for {@link DataSource} instances.
* @param minRetryCount The minimum number of times loads must be retried before {@link
* #maybeThrowPlaylistRefreshError(HlsUrl)} and {@link
* #maybeThrowPrimaryPlaylistRefreshError()} propagate any loading errors.
* @param playlistParser A {@link ParsingLoadable.Parser} for HLS playlists.
*/
public
DefaultHlsPlaylistTracker
(
HlsDataSourceFactory
dataSourceFactory
,
int
minRetryCount
,
ParsingLoadable
.
Parser
<
HlsPlaylist
>
playlistParser
)
{
this
.
dataSourceFactory
=
dataSourceFactory
;
this
.
minRetryCount
=
minRetryCount
;
this
.
playlistParser
=
playlistParser
;
listeners
=
new
ArrayList
<>();
playlistBundles
=
new
IdentityHashMap
<>();
initialStartTimeUs
=
C
.
TIME_UNSET
;
}
// HlsPlaylistTracker implementation.
@Override
public
void
start
(
Uri
initialPlaylistUri
,
EventDispatcher
eventDispatcher
,
PrimaryPlaylistListener
primaryPlaylistListener
)
{
this
.
playlistRefreshHandler
=
new
Handler
();
this
.
eventDispatcher
=
eventDispatcher
;
this
.
primaryPlaylistListener
=
primaryPlaylistListener
;
ParsingLoadable
<
HlsPlaylist
>
masterPlaylistLoadable
=
new
ParsingLoadable
<>(
dataSourceFactory
.
createDataSource
(
C
.
DATA_TYPE_MANIFEST
),
initialPlaylistUri
,
C
.
DATA_TYPE_MANIFEST
,
playlistParser
);
Assertions
.
checkState
(
initialPlaylistLoader
==
null
);
initialPlaylistLoader
=
new
Loader
(
"DefaultHlsPlaylistTracker:MasterPlaylist"
);
long
elapsedRealtime
=
initialPlaylistLoader
.
startLoading
(
masterPlaylistLoadable
,
this
,
minRetryCount
);
eventDispatcher
.
loadStarted
(
masterPlaylistLoadable
.
dataSpec
,
masterPlaylistLoadable
.
type
,
elapsedRealtime
);
}
@Override
public
void
release
()
{
primaryHlsUrl
=
null
;
primaryUrlSnapshot
=
null
;
masterPlaylist
=
null
;
initialStartTimeUs
=
C
.
TIME_UNSET
;
initialPlaylistLoader
.
release
();
initialPlaylistLoader
=
null
;
for
(
MediaPlaylistBundle
bundle
:
playlistBundles
.
values
())
{
bundle
.
release
();
}
playlistRefreshHandler
.
removeCallbacksAndMessages
(
null
);
playlistRefreshHandler
=
null
;
playlistBundles
.
clear
();
}
@Override
public
void
addListener
(
PlaylistEventListener
listener
)
{
listeners
.
add
(
listener
);
}
@Override
public
void
removeListener
(
PlaylistEventListener
listener
)
{
listeners
.
remove
(
listener
);
}
@Override
public
HlsMasterPlaylist
getMasterPlaylist
()
{
return
masterPlaylist
;
}
@Override
public
HlsMediaPlaylist
getPlaylistSnapshot
(
HlsUrl
url
)
{
HlsMediaPlaylist
snapshot
=
playlistBundles
.
get
(
url
).
getPlaylistSnapshot
();
if
(
snapshot
!=
null
)
{
maybeSetPrimaryUrl
(
url
);
}
return
snapshot
;
}
@Override
public
long
getInitialStartTimeUs
()
{
return
initialStartTimeUs
;
}
@Override
public
boolean
isSnapshotValid
(
HlsUrl
url
)
{
return
playlistBundles
.
get
(
url
).
isSnapshotValid
();
}
@Override
public
void
maybeThrowPrimaryPlaylistRefreshError
()
throws
IOException
{
if
(
initialPlaylistLoader
!=
null
)
{
initialPlaylistLoader
.
maybeThrowError
();
}
if
(
primaryHlsUrl
!=
null
)
{
maybeThrowPlaylistRefreshError
(
primaryHlsUrl
);
}
}
@Override
public
void
maybeThrowPlaylistRefreshError
(
HlsUrl
url
)
throws
IOException
{
playlistBundles
.
get
(
url
).
maybeThrowPlaylistRefreshError
();
}
@Override
public
void
refreshPlaylist
(
HlsUrl
url
)
{
playlistBundles
.
get
(
url
).
loadPlaylist
();
}
@Override
public
boolean
isLive
()
{
return
isLive
;
}
// Loader.Callback implementation.
@Override
public
void
onLoadCompleted
(
ParsingLoadable
<
HlsPlaylist
>
loadable
,
long
elapsedRealtimeMs
,
long
loadDurationMs
)
{
HlsPlaylist
result
=
loadable
.
getResult
();
HlsMasterPlaylist
masterPlaylist
;
boolean
isMediaPlaylist
=
result
instanceof
HlsMediaPlaylist
;
if
(
isMediaPlaylist
)
{
masterPlaylist
=
HlsMasterPlaylist
.
createSingleVariantMasterPlaylist
(
result
.
baseUri
);
}
else
/* result instanceof HlsMasterPlaylist */
{
masterPlaylist
=
(
HlsMasterPlaylist
)
result
;
}
this
.
masterPlaylist
=
masterPlaylist
;
primaryHlsUrl
=
masterPlaylist
.
variants
.
get
(
0
);
ArrayList
<
HlsUrl
>
urls
=
new
ArrayList
<>();
urls
.
addAll
(
masterPlaylist
.
variants
);
urls
.
addAll
(
masterPlaylist
.
audios
);
urls
.
addAll
(
masterPlaylist
.
subtitles
);
createBundles
(
urls
);
MediaPlaylistBundle
primaryBundle
=
playlistBundles
.
get
(
primaryHlsUrl
);
if
(
isMediaPlaylist
)
{
// We don't need to load the playlist again. We can use the same result.
primaryBundle
.
processLoadedPlaylist
((
HlsMediaPlaylist
)
result
);
}
else
{
primaryBundle
.
loadPlaylist
();
}
eventDispatcher
.
loadCompleted
(
loadable
.
dataSpec
,
C
.
DATA_TYPE_MANIFEST
,
elapsedRealtimeMs
,
loadDurationMs
,
loadable
.
bytesLoaded
());
}
@Override
public
void
onLoadCanceled
(
ParsingLoadable
<
HlsPlaylist
>
loadable
,
long
elapsedRealtimeMs
,
long
loadDurationMs
,
boolean
released
)
{
eventDispatcher
.
loadCanceled
(
loadable
.
dataSpec
,
C
.
DATA_TYPE_MANIFEST
,
elapsedRealtimeMs
,
loadDurationMs
,
loadable
.
bytesLoaded
());
}
@Override
public
@Loader
.
RetryAction
int
onLoadError
(
ParsingLoadable
<
HlsPlaylist
>
loadable
,
long
elapsedRealtimeMs
,
long
loadDurationMs
,
IOException
error
)
{
boolean
isFatal
=
error
instanceof
ParserException
;
eventDispatcher
.
loadError
(
loadable
.
dataSpec
,
C
.
DATA_TYPE_MANIFEST
,
elapsedRealtimeMs
,
loadDurationMs
,
loadable
.
bytesLoaded
(),
error
,
isFatal
);
return
isFatal
?
Loader
.
DONT_RETRY_FATAL
:
Loader
.
RETRY
;
}
// Internal methods.
private
boolean
maybeSelectNewPrimaryUrl
()
{
List
<
HlsUrl
>
variants
=
masterPlaylist
.
variants
;
int
variantsSize
=
variants
.
size
();
long
currentTimeMs
=
SystemClock
.
elapsedRealtime
();
for
(
int
i
=
0
;
i
<
variantsSize
;
i
++)
{
MediaPlaylistBundle
bundle
=
playlistBundles
.
get
(
variants
.
get
(
i
));
if
(
currentTimeMs
>
bundle
.
blacklistUntilMs
)
{
primaryHlsUrl
=
bundle
.
playlistUrl
;
bundle
.
loadPlaylist
();
return
true
;
}
}
return
false
;
}
private
void
maybeSetPrimaryUrl
(
HlsUrl
url
)
{
if
(
url
==
primaryHlsUrl
||
!
masterPlaylist
.
variants
.
contains
(
url
)
||
(
primaryUrlSnapshot
!=
null
&&
primaryUrlSnapshot
.
hasEndTag
))
{
// Ignore if the primary url is unchanged, if the url is not a variant url, or if the last
// primary snapshot contains an end tag.
return
;
}
primaryHlsUrl
=
url
;
playlistBundles
.
get
(
primaryHlsUrl
).
loadPlaylist
();
}
private
void
createBundles
(
List
<
HlsUrl
>
urls
)
{
int
listSize
=
urls
.
size
();
for
(
int
i
=
0
;
i
<
listSize
;
i
++)
{
HlsUrl
url
=
urls
.
get
(
i
);
MediaPlaylistBundle
bundle
=
new
MediaPlaylistBundle
(
url
);
playlistBundles
.
put
(
url
,
bundle
);
}
}
/**
* Called by the bundles when a snapshot changes.
*
* @param url The url of the playlist.
* @param newSnapshot The new snapshot.
*/
private
void
onPlaylistUpdated
(
HlsUrl
url
,
HlsMediaPlaylist
newSnapshot
)
{
if
(
url
==
primaryHlsUrl
)
{
if
(
primaryUrlSnapshot
==
null
)
{
// This is the first primary url snapshot.
isLive
=
!
newSnapshot
.
hasEndTag
;
initialStartTimeUs
=
newSnapshot
.
startTimeUs
;
}
primaryUrlSnapshot
=
newSnapshot
;
primaryPlaylistListener
.
onPrimaryPlaylistRefreshed
(
newSnapshot
);
}
int
listenersSize
=
listeners
.
size
();
for
(
int
i
=
0
;
i
<
listenersSize
;
i
++)
{
listeners
.
get
(
i
).
onPlaylistChanged
();
}
}
private
boolean
notifyPlaylistError
(
HlsUrl
playlistUrl
,
boolean
shouldBlacklist
)
{
int
listenersSize
=
listeners
.
size
();
boolean
anyBlacklistingFailed
=
false
;
for
(
int
i
=
0
;
i
<
listenersSize
;
i
++)
{
anyBlacklistingFailed
|=
!
listeners
.
get
(
i
).
onPlaylistError
(
playlistUrl
,
shouldBlacklist
);
}
return
anyBlacklistingFailed
;
}
private
HlsMediaPlaylist
getLatestPlaylistSnapshot
(
HlsMediaPlaylist
oldPlaylist
,
HlsMediaPlaylist
loadedPlaylist
)
{
if
(!
loadedPlaylist
.
isNewerThan
(
oldPlaylist
))
{
if
(
loadedPlaylist
.
hasEndTag
)
{
// If the loaded playlist has an end tag but is not newer than the old playlist then we have
// an inconsistent state. This is typically caused by the server incorrectly resetting the
// media sequence when appending the end tag. We resolve this case as best we can by
// returning the old playlist with the end tag appended.
return
oldPlaylist
.
copyWithEndTag
();
}
else
{
return
oldPlaylist
;
}
}
long
startTimeUs
=
getLoadedPlaylistStartTimeUs
(
oldPlaylist
,
loadedPlaylist
);
int
discontinuitySequence
=
getLoadedPlaylistDiscontinuitySequence
(
oldPlaylist
,
loadedPlaylist
);
return
loadedPlaylist
.
copyWith
(
startTimeUs
,
discontinuitySequence
);
}
private
long
getLoadedPlaylistStartTimeUs
(
HlsMediaPlaylist
oldPlaylist
,
HlsMediaPlaylist
loadedPlaylist
)
{
if
(
loadedPlaylist
.
hasProgramDateTime
)
{
return
loadedPlaylist
.
startTimeUs
;
}
long
primarySnapshotStartTimeUs
=
primaryUrlSnapshot
!=
null
?
primaryUrlSnapshot
.
startTimeUs
:
0
;
if
(
oldPlaylist
==
null
)
{
return
primarySnapshotStartTimeUs
;
}
int
oldPlaylistSize
=
oldPlaylist
.
segments
.
size
();
Segment
firstOldOverlappingSegment
=
getFirstOldOverlappingSegment
(
oldPlaylist
,
loadedPlaylist
);
if
(
firstOldOverlappingSegment
!=
null
)
{
return
oldPlaylist
.
startTimeUs
+
firstOldOverlappingSegment
.
relativeStartTimeUs
;
}
else
if
(
oldPlaylistSize
==
loadedPlaylist
.
mediaSequence
-
oldPlaylist
.
mediaSequence
)
{
return
oldPlaylist
.
getEndTimeUs
();
}
else
{
// No segments overlap, we assume the new playlist start coincides with the primary playlist.
return
primarySnapshotStartTimeUs
;
}
}
private
int
getLoadedPlaylistDiscontinuitySequence
(
HlsMediaPlaylist
oldPlaylist
,
HlsMediaPlaylist
loadedPlaylist
)
{
if
(
loadedPlaylist
.
hasDiscontinuitySequence
)
{
return
loadedPlaylist
.
discontinuitySequence
;
}
// TODO: Improve cross-playlist discontinuity adjustment.
int
primaryUrlDiscontinuitySequence
=
primaryUrlSnapshot
!=
null
?
primaryUrlSnapshot
.
discontinuitySequence
:
0
;
if
(
oldPlaylist
==
null
)
{
return
primaryUrlDiscontinuitySequence
;
}
Segment
firstOldOverlappingSegment
=
getFirstOldOverlappingSegment
(
oldPlaylist
,
loadedPlaylist
);
if
(
firstOldOverlappingSegment
!=
null
)
{
return
oldPlaylist
.
discontinuitySequence
+
firstOldOverlappingSegment
.
relativeDiscontinuitySequence
-
loadedPlaylist
.
segments
.
get
(
0
).
relativeDiscontinuitySequence
;
}
return
primaryUrlDiscontinuitySequence
;
}
private
static
Segment
getFirstOldOverlappingSegment
(
HlsMediaPlaylist
oldPlaylist
,
HlsMediaPlaylist
loadedPlaylist
)
{
int
mediaSequenceOffset
=
(
int
)
(
loadedPlaylist
.
mediaSequence
-
oldPlaylist
.
mediaSequence
);
List
<
Segment
>
oldSegments
=
oldPlaylist
.
segments
;
return
mediaSequenceOffset
<
oldSegments
.
size
()
?
oldSegments
.
get
(
mediaSequenceOffset
)
:
null
;
}
/** Holds all information related to a specific Media Playlist. */
private
final
class
MediaPlaylistBundle
implements
Loader
.
Callback
<
ParsingLoadable
<
HlsPlaylist
>>,
Runnable
{
private
final
HlsUrl
playlistUrl
;
private
final
Loader
mediaPlaylistLoader
;
private
final
ParsingLoadable
<
HlsPlaylist
>
mediaPlaylistLoadable
;
private
HlsMediaPlaylist
playlistSnapshot
;
private
long
lastSnapshotLoadMs
;
private
long
lastSnapshotChangeMs
;
private
long
earliestNextLoadTimeMs
;
private
long
blacklistUntilMs
;
private
boolean
loadPending
;
private
IOException
playlistError
;
public
MediaPlaylistBundle
(
HlsUrl
playlistUrl
)
{
this
.
playlistUrl
=
playlistUrl
;
mediaPlaylistLoader
=
new
Loader
(
"DefaultHlsPlaylistTracker:MediaPlaylist"
);
mediaPlaylistLoadable
=
new
ParsingLoadable
<>(
dataSourceFactory
.
createDataSource
(
C
.
DATA_TYPE_MANIFEST
),
UriUtil
.
resolveToUri
(
masterPlaylist
.
baseUri
,
playlistUrl
.
url
),
C
.
DATA_TYPE_MANIFEST
,
playlistParser
);
}
public
HlsMediaPlaylist
getPlaylistSnapshot
()
{
return
playlistSnapshot
;
}
public
boolean
isSnapshotValid
()
{
if
(
playlistSnapshot
==
null
)
{
return
false
;
}
long
currentTimeMs
=
SystemClock
.
elapsedRealtime
();
long
snapshotValidityDurationMs
=
Math
.
max
(
30000
,
C
.
usToMs
(
playlistSnapshot
.
durationUs
));
return
playlistSnapshot
.
hasEndTag
||
playlistSnapshot
.
playlistType
==
HlsMediaPlaylist
.
PLAYLIST_TYPE_EVENT
||
playlistSnapshot
.
playlistType
==
HlsMediaPlaylist
.
PLAYLIST_TYPE_VOD
||
lastSnapshotLoadMs
+
snapshotValidityDurationMs
>
currentTimeMs
;
}
public
void
release
()
{
mediaPlaylistLoader
.
release
();
}
public
void
loadPlaylist
()
{
blacklistUntilMs
=
0
;
if
(
loadPending
||
mediaPlaylistLoader
.
isLoading
())
{
// Load already pending or in progress. Do nothing.
return
;
}
long
currentTimeMs
=
SystemClock
.
elapsedRealtime
();
if
(
currentTimeMs
<
earliestNextLoadTimeMs
)
{
loadPending
=
true
;
playlistRefreshHandler
.
postDelayed
(
this
,
earliestNextLoadTimeMs
-
currentTimeMs
);
}
else
{
loadPlaylistImmediately
();
}
}
public
void
maybeThrowPlaylistRefreshError
()
throws
IOException
{
mediaPlaylistLoader
.
maybeThrowError
();
if
(
playlistError
!=
null
)
{
throw
playlistError
;
}
}
// Loader.Callback implementation.
@Override
public
void
onLoadCompleted
(
ParsingLoadable
<
HlsPlaylist
>
loadable
,
long
elapsedRealtimeMs
,
long
loadDurationMs
)
{
HlsPlaylist
result
=
loadable
.
getResult
();
if
(
result
instanceof
HlsMediaPlaylist
)
{
processLoadedPlaylist
((
HlsMediaPlaylist
)
result
);
eventDispatcher
.
loadCompleted
(
loadable
.
dataSpec
,
C
.
DATA_TYPE_MANIFEST
,
elapsedRealtimeMs
,
loadDurationMs
,
loadable
.
bytesLoaded
());
}
else
{
playlistError
=
new
ParserException
(
"Loaded playlist has unexpected type."
);
}
}
@Override
public
void
onLoadCanceled
(
ParsingLoadable
<
HlsPlaylist
>
loadable
,
long
elapsedRealtimeMs
,
long
loadDurationMs
,
boolean
released
)
{
eventDispatcher
.
loadCanceled
(
loadable
.
dataSpec
,
C
.
DATA_TYPE_MANIFEST
,
elapsedRealtimeMs
,
loadDurationMs
,
loadable
.
bytesLoaded
());
}
@Override
public
@Loader
.
RetryAction
int
onLoadError
(
ParsingLoadable
<
HlsPlaylist
>
loadable
,
long
elapsedRealtimeMs
,
long
loadDurationMs
,
IOException
error
)
{
boolean
isFatal
=
error
instanceof
ParserException
;
eventDispatcher
.
loadError
(
loadable
.
dataSpec
,
C
.
DATA_TYPE_MANIFEST
,
elapsedRealtimeMs
,
loadDurationMs
,
loadable
.
bytesLoaded
(),
error
,
isFatal
);
boolean
shouldBlacklist
=
ChunkedTrackBlacklistUtil
.
shouldBlacklist
(
error
);
boolean
shouldRetryIfNotFatal
=
notifyPlaylistError
(
playlistUrl
,
shouldBlacklist
)
||
!
shouldBlacklist
;
if
(
isFatal
)
{
return
Loader
.
DONT_RETRY_FATAL
;
}
if
(
shouldBlacklist
)
{
shouldRetryIfNotFatal
|=
blacklistPlaylist
();
}
return
shouldRetryIfNotFatal
?
Loader
.
RETRY
:
Loader
.
DONT_RETRY
;
}
// Runnable implementation.
@Override
public
void
run
()
{
loadPending
=
false
;
loadPlaylistImmediately
();
}
// Internal methods.
private
void
loadPlaylistImmediately
()
{
long
elapsedRealtime
=
mediaPlaylistLoader
.
startLoading
(
mediaPlaylistLoadable
,
this
,
minRetryCount
);
eventDispatcher
.
loadStarted
(
mediaPlaylistLoadable
.
dataSpec
,
mediaPlaylistLoadable
.
type
,
elapsedRealtime
);
}
private
void
processLoadedPlaylist
(
HlsMediaPlaylist
loadedPlaylist
)
{
HlsMediaPlaylist
oldPlaylist
=
playlistSnapshot
;
long
currentTimeMs
=
SystemClock
.
elapsedRealtime
();
lastSnapshotLoadMs
=
currentTimeMs
;
playlistSnapshot
=
getLatestPlaylistSnapshot
(
oldPlaylist
,
loadedPlaylist
);
if
(
playlistSnapshot
!=
oldPlaylist
)
{
playlistError
=
null
;
lastSnapshotChangeMs
=
currentTimeMs
;
onPlaylistUpdated
(
playlistUrl
,
playlistSnapshot
);
}
else
if
(!
playlistSnapshot
.
hasEndTag
)
{
if
(
loadedPlaylist
.
mediaSequence
+
loadedPlaylist
.
segments
.
size
()
<
playlistSnapshot
.
mediaSequence
)
{
// The media sequence jumped backwards. The server has probably reset.
playlistError
=
new
PlaylistResetException
(
playlistUrl
.
url
);
notifyPlaylistError
(
playlistUrl
,
false
);
}
else
if
(
currentTimeMs
-
lastSnapshotChangeMs
>
C
.
usToMs
(
playlistSnapshot
.
targetDurationUs
)
*
PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT
)
{
// The playlist seems to be stuck. Blacklist it.
playlistError
=
new
PlaylistStuckException
(
playlistUrl
.
url
);
notifyPlaylistError
(
playlistUrl
,
true
);
blacklistPlaylist
();
}
}
// Do not allow the playlist to load again within the target duration if we obtained a new
// snapshot, or half the target duration otherwise.
earliestNextLoadTimeMs
=
currentTimeMs
+
C
.
usToMs
(
playlistSnapshot
!=
oldPlaylist
?
playlistSnapshot
.
targetDurationUs
:
(
playlistSnapshot
.
targetDurationUs
/
2
));
// Schedule a load if this is the primary playlist and it doesn't have an end tag. Else the
// next load will be scheduled when refreshPlaylist is called, or when this playlist becomes
// the primary.
if
(
playlistUrl
==
primaryHlsUrl
&&
!
playlistSnapshot
.
hasEndTag
)
{
loadPlaylist
();
}
}
/**
* Blacklists the playlist.
*
* @return Whether the playlist is the primary, despite being blacklisted.
*/
private
boolean
blacklistPlaylist
()
{
blacklistUntilMs
=
SystemClock
.
elapsedRealtime
()
+
ChunkedTrackBlacklistUtil
.
DEFAULT_TRACK_BLACKLIST_MS
;
return
primaryHlsUrl
==
playlistUrl
&&
!
maybeSelectNewPrimaryUrl
();
}
}
}
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java
View file @
f7ed789f
/*
/*
* Copyright (C) 201
6
The Android Open Source Project
* Copyright (C) 201
8
The Android Open Source Project
*
*
* Licensed under the Apache License, Version 2.0 (the "License");
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* you may not use this file except in compliance with the License.
...
@@ -16,66 +16,28 @@
...
@@ -16,66 +16,28 @@
package
com
.
google
.
android
.
exoplayer2
.
source
.
hls
.
playlist
;
package
com
.
google
.
android
.
exoplayer2
.
source
.
hls
.
playlist
;
import
android.net.Uri
;
import
android.net.Uri
;
import
android.os.Handler
;
import
android.support.annotation.Nullable
;
import
android.os.SystemClock
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.ParserException
;
import
com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher
;
import
com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher
;
import
com.google.android.exoplayer2.source.chunk.ChunkedTrackBlacklistUtil
;
import
com.google.android.exoplayer2.source.hls.HlsDataSourceFactory
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.HlsUrl
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment
;
import
com.google.android.exoplayer2.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.Loader
;
import
com.google.android.exoplayer2.upstream.ParsingLoadable
;
import
com.google.android.exoplayer2.util.UriUtil
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.util.ArrayList
;
import
java.util.IdentityHashMap
;
import
java.util.List
;
/**
/**
* Tracks playlists linked to a provided playlist url. The provided url might reference an HLS
* Tracks playlists associated to an HLS stream and provides snapshots.
* master playlist or a media playlist.
*
* <p>The playlist tracker is responsible for exposing the seeking window, which is defined by the
* segments that one of the playlists exposes. This playlist is called primary and needs to be
* periodically refreshed in the case of live streams. Note that the primary playlist is one of the
* media playlists while the master playlist is an optional kind of playlist defined by the HLS
* specification (RFC 8216).
*
* <p>Playlist loads might encounter errors. The tracker may choose to blacklist them to ensure a
* primary playlist is always available.
*/
*/
public
final
class
HlsPlaylistTracker
implements
Loader
.
Callback
<
ParsingLoadable
<
HlsPlaylist
>>
{
public
interface
HlsPlaylistTracker
{
/**
/** Listener for primary playlist changes. */
* Thrown when a playlist is considered to be stuck due to a server side error.
interface
PrimaryPlaylistListener
{
*/
public
static
final
class
PlaylistStuckException
extends
IOException
{
/**
* The url of the stuck playlist.
*/
public
final
String
url
;
private
PlaylistStuckException
(
String
url
)
{
this
.
url
=
url
;
}
}
/**
* Thrown when the media sequence of a new snapshot indicates the server has reset.
*/
public
static
final
class
PlaylistResetException
extends
IOException
{
/**
* The url of the reset playlist.
*/
public
final
String
url
;
private
PlaylistResetException
(
String
url
)
{
this
.
url
=
url
;
}
}
/**
* Listener for primary playlist changes.
*/
public
interface
PrimaryPlaylistListener
{
/**
/**
* Called when the primary playlist changes.
* Called when the primary playlist changes.
...
@@ -85,10 +47,8 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
...
@@ -85,10 +47,8 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
void
onPrimaryPlaylistRefreshed
(
HlsMediaPlaylist
mediaPlaylist
);
void
onPrimaryPlaylistRefreshed
(
HlsMediaPlaylist
mediaPlaylist
);
}
}
/**
/** Called on playlist loading events. */
* Called on playlist loading events.
interface
PlaylistEventListener
{
*/
public
interface
PlaylistEventListener
{
/**
/**
* Called a playlist changes.
* Called a playlist changes.
...
@@ -105,141 +65,107 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
...
@@ -105,141 +65,107 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
boolean
onPlaylistError
(
HlsUrl
url
,
boolean
shouldBlacklist
);
boolean
onPlaylistError
(
HlsUrl
url
,
boolean
shouldBlacklist
);
}
}
/**
/**
Thrown when a playlist is considered to be stuck due to a server side error. */
* Coefficient applied on the target duration of a playlist to determine the amount of time after
final
class
PlaylistStuckException
extends
IOException
{
* which an unchanging playlist is considered stuck.
*/
/** The url of the stuck playlist.
*/
private
static
final
double
PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT
=
3.5
;
public
final
String
url
;
private
final
Uri
initialPlaylistUri
;
/**
private
final
HlsDataSourceFactory
dataSourceFactory
;
* Creates an instance.
private
final
ParsingLoadable
.
Parser
<
HlsPlaylist
>
playlistParser
;
*
private
final
int
minRetryCount
;
* @param url See {@link #url}.
private
final
IdentityHashMap
<
HlsUrl
,
MediaPlaylistBundle
>
playlistBundles
;
*/
private
final
Handler
playlistRefreshHandler
;
public
PlaylistStuckException
(
String
url
)
{
private
final
PrimaryPlaylistListener
primaryPlaylistListener
;
this
.
url
=
url
;
private
final
List
<
PlaylistEventListener
>
listeners
;
}
private
final
Loader
initialPlaylistLoader
;
}
private
final
EventDispatcher
eventDispatcher
;
private
HlsMasterPlaylist
masterPlaylist
;
/** Thrown when the media sequence of a new snapshot indicates the server has reset. */
private
HlsUrl
primaryHlsUrl
;
final
class
PlaylistResetException
extends
IOException
{
private
HlsMediaPlaylist
primaryUrlSnapshot
;
private
boolean
isLive
;
/** The url of the reset playlist. */
private
long
initialStartTimeUs
;
public
final
String
url
;
/**
* Creates an instance.
*
* @param url See {@link #url}.
*/
public
PlaylistResetException
(
String
url
)
{
this
.
url
=
url
;
}
}
/**
/**
* @param initialPlaylistUri Uri for the initial playlist of the stream. Can refer a media
* Starts the playlist tracker.
* playlist or a master playlist.
*
* @param dataSourceFactory A factory for {@link DataSource} instances.
* <p>Must be called from the playback thread. A tracker may be restarted after a {@link
* #release()} call.
*
* @param initialPlaylistUri Uri of the HLS stream. Can point to a media playlist or a master
* playlist.
* @param eventDispatcher A dispatcher to notify of events.
* @param eventDispatcher A dispatcher to notify of events.
* @param minRetryCount The minimum number of times loads must be retried before
* @param listener A callback for the primary playlist change events.
* {@link #maybeThrowPlaylistRefreshError(HlsUrl)} and
* {@link #maybeThrowPrimaryPlaylistRefreshError()} propagate any loading errors.
* @param primaryPlaylistListener A callback for the primary playlist change events.
*/
*/
public
HlsPlaylistTracker
(
Uri
initialPlaylistUri
,
HlsDataSourceFactory
dataSourceFactory
,
void
start
(
EventDispatcher
eventDispatcher
,
int
minRetryCount
,
Uri
initialPlaylistUri
,
EventDispatcher
eventDispatcher
,
PrimaryPlaylistListener
listener
);
PrimaryPlaylistListener
primaryPlaylistListener
,
ParsingLoadable
.
Parser
<
HlsPlaylist
>
playlistParser
)
{
/** Releases all acquired resources. Must be called once per {@link #start} call. */
this
.
initialPlaylistUri
=
initialPlaylistUri
;
void
release
();
this
.
dataSourceFactory
=
dataSourceFactory
;
this
.
eventDispatcher
=
eventDispatcher
;
this
.
minRetryCount
=
minRetryCount
;
this
.
primaryPlaylistListener
=
primaryPlaylistListener
;
this
.
playlistParser
=
playlistParser
;
listeners
=
new
ArrayList
<>();
initialPlaylistLoader
=
new
Loader
(
"HlsPlaylistTracker:MasterPlaylist"
);
playlistBundles
=
new
IdentityHashMap
<>();
playlistRefreshHandler
=
new
Handler
();
initialStartTimeUs
=
C
.
TIME_UNSET
;
}
/**
/**
* Registers a listener to receive events from the playlist tracker.
* Registers a listener to receive events from the playlist tracker.
*
*
* @param listener The listener.
* @param listener The listener.
*/
*/
public
void
addListener
(
PlaylistEventListener
listener
)
{
void
addListener
(
PlaylistEventListener
listener
);
listeners
.
add
(
listener
);
}
/**
/**
* Unregisters a listener.
* Unregisters a listener.
*
*
* @param listener The listener to unregister.
* @param listener The listener to unregister.
*/
*/
public
void
removeListener
(
PlaylistEventListener
listener
)
{
void
removeListener
(
PlaylistEventListener
listener
);
listeners
.
remove
(
listener
);
}
/**
* Starts tracking all the playlists related to the provided Uri.
*/
public
void
start
()
{
ParsingLoadable
<
HlsPlaylist
>
masterPlaylistLoadable
=
new
ParsingLoadable
<>(
dataSourceFactory
.
createDataSource
(
C
.
DATA_TYPE_MANIFEST
),
initialPlaylistUri
,
C
.
DATA_TYPE_MANIFEST
,
playlistParser
);
initialPlaylistLoader
.
startLoading
(
masterPlaylistLoadable
,
this
,
minRetryCount
);
}
/**
/**
* Returns the master playlist.
* Returns the master playlist.
*
*
* <p>If the uri passed to {@link #start} points to a media playlist, an {@link HlsMasterPlaylist}
* with a single variant for said media playlist is returned.
*
* @return The master playlist. Null if the initial playlist has yet to be loaded.
* @return The master playlist. Null if the initial playlist has yet to be loaded.
*/
*/
public
HlsMasterPlaylist
getMasterPlaylist
()
{
@Nullable
return
masterPlaylist
;
HlsMasterPlaylist
getMasterPlaylist
();
}
/**
/**
* Returns the most recent snapshot available of the playlist referenced by the provided
* Returns the most recent snapshot available of the playlist referenced by the provided
{@link
*
{@link
HlsUrl}.
* HlsUrl}.
*
*
* @param url The {@link HlsUrl} corresponding to the requested media playlist.
* @param url The {@link HlsUrl} corresponding to the requested media playlist.
* @return The most recent snapshot of the playlist referenced by the provided {@link HlsUrl}. May
* @return The most recent snapshot of the playlist referenced by the provided {@link HlsUrl}. May
* be null if no snapshot has been loaded yet.
* be null if no snapshot has been loaded yet.
*/
*/
public
HlsMediaPlaylist
getPlaylistSnapshot
(
HlsUrl
url
)
{
@Nullable
HlsMediaPlaylist
snapshot
=
playlistBundles
.
get
(
url
).
getPlaylistSnapshot
();
HlsMediaPlaylist
getPlaylistSnapshot
(
HlsUrl
url
);
if
(
snapshot
!=
null
)
{
maybeSetPrimaryUrl
(
url
);
}
return
snapshot
;
}
/**
/**
* Returns the start time of the first loaded primary playlist, or {@link C#TIME_UNSET} if no
* Returns the start time of the first loaded primary playlist, or {@link C#TIME_UNSET} if no
* media playlist has been loaded.
* media playlist has been loaded.
*/
*/
public
long
getInitialStartTimeUs
()
{
long
getInitialStartTimeUs
();
return
initialStartTimeUs
;
}
/**
/**
* Returns whether the snapshot of the playlist referenced by the provided {@link HlsUrl} is
* Returns whether the snapshot of the playlist referenced by the provided {@link HlsUrl} is
* valid, meaning all the segments referenced by the playlist are expected to be available. If the
* valid, meaning all the segments referenced by the playlist are expected to be available. If the
* playlist is not valid then some of the segments may no longer be available.
* playlist is not valid then some of the segments may no longer be available.
*
* @param url The {@link HlsUrl}.
* @param url The {@link HlsUrl}.
* @return Whether the snapshot of the playlist referenced by the provided {@link HlsUrl} is
* @return Whether the snapshot of the playlist referenced by the provided {@link HlsUrl} is
* valid.
* valid.
*/
*/
public
boolean
isSnapshotValid
(
HlsUrl
url
)
{
boolean
isSnapshotValid
(
HlsUrl
url
);
return
playlistBundles
.
get
(
url
).
isSnapshotValid
();
}
/**
* Releases the playlist tracker.
*/
public
void
release
()
{
initialPlaylistLoader
.
release
();
for
(
MediaPlaylistBundle
bundle
:
playlistBundles
.
values
())
{
bundle
.
release
();
}
playlistRefreshHandler
.
removeCallbacksAndMessages
(
null
);
playlistBundles
.
clear
();
}
/**
/**
* If the tracker is having trouble refreshing the master playlist or the primary playlist, this
* If the tracker is having trouble refreshing the master playlist or the primary playlist, this
...
@@ -247,401 +173,31 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
...
@@ -247,401 +173,31 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
*
*
* @throws IOException The underlying error.
* @throws IOException The underlying error.
*/
*/
public
void
maybeThrowPrimaryPlaylistRefreshError
()
throws
IOException
{
void
maybeThrowPrimaryPlaylistRefreshError
()
throws
IOException
;
initialPlaylistLoader
.
maybeThrowError
();
if
(
primaryHlsUrl
!=
null
)
{
maybeThrowPlaylistRefreshError
(
primaryHlsUrl
);
}
}
/**
/**
* If the playlist is having trouble refreshing the playlist referenced by the given
* If the playlist is having trouble refreshing the playlist referenced by the given
{@link
*
{@link
HlsUrl}, this method throws the underlying error.
* HlsUrl}, this method throws the underlying error.
*
*
* @param url The {@link HlsUrl}.
* @param url The {@link HlsUrl}.
* @throws IOException The underyling error.
* @throws IOException The underyling error.
*/
*/
public
void
maybeThrowPlaylistRefreshError
(
HlsUrl
url
)
throws
IOException
{
void
maybeThrowPlaylistRefreshError
(
HlsUrl
url
)
throws
IOException
;
playlistBundles
.
get
(
url
).
maybeThrowPlaylistRefreshError
();
}
/**
/**
* Triggers a playlist refresh and whitelists it.
* Requests a playlist refresh and whitelists it.
*
* <p>The playlist tracker may choose the delay the playlist refresh. The request is discarded if
* a refresh was already pending.
*
*
* @param url The {@link HlsUrl} of the playlist to be refreshed.
* @param url The {@link HlsUrl} of the playlist to be refreshed.
*/
*/
public
void
refreshPlaylist
(
HlsUrl
url
)
{
void
refreshPlaylist
(
HlsUrl
url
);
playlistBundles
.
get
(
url
).
loadPlaylist
();
}
/**
/**
* Returns whether th
is is live content
.
* Returns whether th
e tracked playlists describe a live stream
.
*
*
* @return True if the content is live. False otherwise.
* @return True if the content is live. False otherwise.
*/
*/
public
boolean
isLive
()
{
boolean
isLive
();
return
isLive
;
}
// Loader.Callback implementation.
@Override
public
void
onLoadCompleted
(
ParsingLoadable
<
HlsPlaylist
>
loadable
,
long
elapsedRealtimeMs
,
long
loadDurationMs
)
{
HlsPlaylist
result
=
loadable
.
getResult
();
HlsMasterPlaylist
masterPlaylist
;
boolean
isMediaPlaylist
=
result
instanceof
HlsMediaPlaylist
;
if
(
isMediaPlaylist
)
{
masterPlaylist
=
HlsMasterPlaylist
.
createSingleVariantMasterPlaylist
(
result
.
baseUri
);
}
else
/* result instanceof HlsMasterPlaylist */
{
masterPlaylist
=
(
HlsMasterPlaylist
)
result
;
}
this
.
masterPlaylist
=
masterPlaylist
;
primaryHlsUrl
=
masterPlaylist
.
variants
.
get
(
0
);
ArrayList
<
HlsUrl
>
urls
=
new
ArrayList
<>();
urls
.
addAll
(
masterPlaylist
.
variants
);
urls
.
addAll
(
masterPlaylist
.
audios
);
urls
.
addAll
(
masterPlaylist
.
subtitles
);
createBundles
(
urls
);
MediaPlaylistBundle
primaryBundle
=
playlistBundles
.
get
(
primaryHlsUrl
);
if
(
isMediaPlaylist
)
{
// We don't need to load the playlist again. We can use the same result.
primaryBundle
.
processLoadedPlaylist
((
HlsMediaPlaylist
)
result
);
}
else
{
primaryBundle
.
loadPlaylist
();
}
eventDispatcher
.
loadCompleted
(
loadable
.
dataSpec
,
C
.
DATA_TYPE_MANIFEST
,
elapsedRealtimeMs
,
loadDurationMs
,
loadable
.
bytesLoaded
());
}
@Override
public
void
onLoadCanceled
(
ParsingLoadable
<
HlsPlaylist
>
loadable
,
long
elapsedRealtimeMs
,
long
loadDurationMs
,
boolean
released
)
{
eventDispatcher
.
loadCanceled
(
loadable
.
dataSpec
,
C
.
DATA_TYPE_MANIFEST
,
elapsedRealtimeMs
,
loadDurationMs
,
loadable
.
bytesLoaded
());
}
@Override
public
@Loader
.
RetryAction
int
onLoadError
(
ParsingLoadable
<
HlsPlaylist
>
loadable
,
long
elapsedRealtimeMs
,
long
loadDurationMs
,
IOException
error
)
{
boolean
isFatal
=
error
instanceof
ParserException
;
eventDispatcher
.
loadError
(
loadable
.
dataSpec
,
C
.
DATA_TYPE_MANIFEST
,
elapsedRealtimeMs
,
loadDurationMs
,
loadable
.
bytesLoaded
(),
error
,
isFatal
);
return
isFatal
?
Loader
.
DONT_RETRY_FATAL
:
Loader
.
RETRY
;
}
// Internal methods.
private
boolean
maybeSelectNewPrimaryUrl
()
{
List
<
HlsUrl
>
variants
=
masterPlaylist
.
variants
;
int
variantsSize
=
variants
.
size
();
long
currentTimeMs
=
SystemClock
.
elapsedRealtime
();
for
(
int
i
=
0
;
i
<
variantsSize
;
i
++)
{
MediaPlaylistBundle
bundle
=
playlistBundles
.
get
(
variants
.
get
(
i
));
if
(
currentTimeMs
>
bundle
.
blacklistUntilMs
)
{
primaryHlsUrl
=
bundle
.
playlistUrl
;
bundle
.
loadPlaylist
();
return
true
;
}
}
return
false
;
}
private
void
maybeSetPrimaryUrl
(
HlsUrl
url
)
{
if
(
url
==
primaryHlsUrl
||
!
masterPlaylist
.
variants
.
contains
(
url
)
||
(
primaryUrlSnapshot
!=
null
&&
primaryUrlSnapshot
.
hasEndTag
))
{
// Ignore if the primary url is unchanged, if the url is not a variant url, or if the last
// primary snapshot contains an end tag.
return
;
}
primaryHlsUrl
=
url
;
playlistBundles
.
get
(
primaryHlsUrl
).
loadPlaylist
();
}
private
void
createBundles
(
List
<
HlsUrl
>
urls
)
{
int
listSize
=
urls
.
size
();
for
(
int
i
=
0
;
i
<
listSize
;
i
++)
{
HlsUrl
url
=
urls
.
get
(
i
);
MediaPlaylistBundle
bundle
=
new
MediaPlaylistBundle
(
url
);
playlistBundles
.
put
(
url
,
bundle
);
}
}
/**
* Called by the bundles when a snapshot changes.
*
* @param url The url of the playlist.
* @param newSnapshot The new snapshot.
*/
private
void
onPlaylistUpdated
(
HlsUrl
url
,
HlsMediaPlaylist
newSnapshot
)
{
if
(
url
==
primaryHlsUrl
)
{
if
(
primaryUrlSnapshot
==
null
)
{
// This is the first primary url snapshot.
isLive
=
!
newSnapshot
.
hasEndTag
;
initialStartTimeUs
=
newSnapshot
.
startTimeUs
;
}
primaryUrlSnapshot
=
newSnapshot
;
primaryPlaylistListener
.
onPrimaryPlaylistRefreshed
(
newSnapshot
);
}
int
listenersSize
=
listeners
.
size
();
for
(
int
i
=
0
;
i
<
listenersSize
;
i
++)
{
listeners
.
get
(
i
).
onPlaylistChanged
();
}
}
private
boolean
notifyPlaylistError
(
HlsUrl
playlistUrl
,
boolean
shouldBlacklist
)
{
int
listenersSize
=
listeners
.
size
();
boolean
anyBlacklistingFailed
=
false
;
for
(
int
i
=
0
;
i
<
listenersSize
;
i
++)
{
anyBlacklistingFailed
|=
!
listeners
.
get
(
i
).
onPlaylistError
(
playlistUrl
,
shouldBlacklist
);
}
return
anyBlacklistingFailed
;
}
private
HlsMediaPlaylist
getLatestPlaylistSnapshot
(
HlsMediaPlaylist
oldPlaylist
,
HlsMediaPlaylist
loadedPlaylist
)
{
if
(!
loadedPlaylist
.
isNewerThan
(
oldPlaylist
))
{
if
(
loadedPlaylist
.
hasEndTag
)
{
// If the loaded playlist has an end tag but is not newer than the old playlist then we have
// an inconsistent state. This is typically caused by the server incorrectly resetting the
// media sequence when appending the end tag. We resolve this case as best we can by
// returning the old playlist with the end tag appended.
return
oldPlaylist
.
copyWithEndTag
();
}
else
{
return
oldPlaylist
;
}
}
long
startTimeUs
=
getLoadedPlaylistStartTimeUs
(
oldPlaylist
,
loadedPlaylist
);
int
discontinuitySequence
=
getLoadedPlaylistDiscontinuitySequence
(
oldPlaylist
,
loadedPlaylist
);
return
loadedPlaylist
.
copyWith
(
startTimeUs
,
discontinuitySequence
);
}
private
long
getLoadedPlaylistStartTimeUs
(
HlsMediaPlaylist
oldPlaylist
,
HlsMediaPlaylist
loadedPlaylist
)
{
if
(
loadedPlaylist
.
hasProgramDateTime
)
{
return
loadedPlaylist
.
startTimeUs
;
}
long
primarySnapshotStartTimeUs
=
primaryUrlSnapshot
!=
null
?
primaryUrlSnapshot
.
startTimeUs
:
0
;
if
(
oldPlaylist
==
null
)
{
return
primarySnapshotStartTimeUs
;
}
int
oldPlaylistSize
=
oldPlaylist
.
segments
.
size
();
Segment
firstOldOverlappingSegment
=
getFirstOldOverlappingSegment
(
oldPlaylist
,
loadedPlaylist
);
if
(
firstOldOverlappingSegment
!=
null
)
{
return
oldPlaylist
.
startTimeUs
+
firstOldOverlappingSegment
.
relativeStartTimeUs
;
}
else
if
(
oldPlaylistSize
==
loadedPlaylist
.
mediaSequence
-
oldPlaylist
.
mediaSequence
)
{
return
oldPlaylist
.
getEndTimeUs
();
}
else
{
// No segments overlap, we assume the new playlist start coincides with the primary playlist.
return
primarySnapshotStartTimeUs
;
}
}
private
int
getLoadedPlaylistDiscontinuitySequence
(
HlsMediaPlaylist
oldPlaylist
,
HlsMediaPlaylist
loadedPlaylist
)
{
if
(
loadedPlaylist
.
hasDiscontinuitySequence
)
{
return
loadedPlaylist
.
discontinuitySequence
;
}
// TODO: Improve cross-playlist discontinuity adjustment.
int
primaryUrlDiscontinuitySequence
=
primaryUrlSnapshot
!=
null
?
primaryUrlSnapshot
.
discontinuitySequence
:
0
;
if
(
oldPlaylist
==
null
)
{
return
primaryUrlDiscontinuitySequence
;
}
Segment
firstOldOverlappingSegment
=
getFirstOldOverlappingSegment
(
oldPlaylist
,
loadedPlaylist
);
if
(
firstOldOverlappingSegment
!=
null
)
{
return
oldPlaylist
.
discontinuitySequence
+
firstOldOverlappingSegment
.
relativeDiscontinuitySequence
-
loadedPlaylist
.
segments
.
get
(
0
).
relativeDiscontinuitySequence
;
}
return
primaryUrlDiscontinuitySequence
;
}
private
static
Segment
getFirstOldOverlappingSegment
(
HlsMediaPlaylist
oldPlaylist
,
HlsMediaPlaylist
loadedPlaylist
)
{
int
mediaSequenceOffset
=
(
int
)
(
loadedPlaylist
.
mediaSequence
-
oldPlaylist
.
mediaSequence
);
List
<
Segment
>
oldSegments
=
oldPlaylist
.
segments
;
return
mediaSequenceOffset
<
oldSegments
.
size
()
?
oldSegments
.
get
(
mediaSequenceOffset
)
:
null
;
}
/**
* Holds all information related to a specific Media Playlist.
*/
private
final
class
MediaPlaylistBundle
implements
Loader
.
Callback
<
ParsingLoadable
<
HlsPlaylist
>>,
Runnable
{
private
final
HlsUrl
playlistUrl
;
private
final
Loader
mediaPlaylistLoader
;
private
final
ParsingLoadable
<
HlsPlaylist
>
mediaPlaylistLoadable
;
private
HlsMediaPlaylist
playlistSnapshot
;
private
long
lastSnapshotLoadMs
;
private
long
lastSnapshotChangeMs
;
private
long
earliestNextLoadTimeMs
;
private
long
blacklistUntilMs
;
private
boolean
loadPending
;
private
IOException
playlistError
;
public
MediaPlaylistBundle
(
HlsUrl
playlistUrl
)
{
this
.
playlistUrl
=
playlistUrl
;
mediaPlaylistLoader
=
new
Loader
(
"HlsPlaylistTracker:MediaPlaylist"
);
mediaPlaylistLoadable
=
new
ParsingLoadable
<>(
dataSourceFactory
.
createDataSource
(
C
.
DATA_TYPE_MANIFEST
),
UriUtil
.
resolveToUri
(
masterPlaylist
.
baseUri
,
playlistUrl
.
url
),
C
.
DATA_TYPE_MANIFEST
,
playlistParser
);
}
public
HlsMediaPlaylist
getPlaylistSnapshot
()
{
return
playlistSnapshot
;
}
public
boolean
isSnapshotValid
()
{
if
(
playlistSnapshot
==
null
)
{
return
false
;
}
long
currentTimeMs
=
SystemClock
.
elapsedRealtime
();
long
snapshotValidityDurationMs
=
Math
.
max
(
30000
,
C
.
usToMs
(
playlistSnapshot
.
durationUs
));
return
playlistSnapshot
.
hasEndTag
||
playlistSnapshot
.
playlistType
==
HlsMediaPlaylist
.
PLAYLIST_TYPE_EVENT
||
playlistSnapshot
.
playlistType
==
HlsMediaPlaylist
.
PLAYLIST_TYPE_VOD
||
lastSnapshotLoadMs
+
snapshotValidityDurationMs
>
currentTimeMs
;
}
public
void
release
()
{
mediaPlaylistLoader
.
release
();
}
public
void
loadPlaylist
()
{
blacklistUntilMs
=
0
;
if
(
loadPending
||
mediaPlaylistLoader
.
isLoading
())
{
// Load already pending or in progress. Do nothing.
return
;
}
long
currentTimeMs
=
SystemClock
.
elapsedRealtime
();
if
(
currentTimeMs
<
earliestNextLoadTimeMs
)
{
loadPending
=
true
;
playlistRefreshHandler
.
postDelayed
(
this
,
earliestNextLoadTimeMs
-
currentTimeMs
);
}
else
{
loadPlaylistImmediately
();
}
}
public
void
maybeThrowPlaylistRefreshError
()
throws
IOException
{
mediaPlaylistLoader
.
maybeThrowError
();
if
(
playlistError
!=
null
)
{
throw
playlistError
;
}
}
// Loader.Callback implementation.
@Override
public
void
onLoadCompleted
(
ParsingLoadable
<
HlsPlaylist
>
loadable
,
long
elapsedRealtimeMs
,
long
loadDurationMs
)
{
HlsPlaylist
result
=
loadable
.
getResult
();
if
(
result
instanceof
HlsMediaPlaylist
)
{
processLoadedPlaylist
((
HlsMediaPlaylist
)
result
);
eventDispatcher
.
loadCompleted
(
loadable
.
dataSpec
,
C
.
DATA_TYPE_MANIFEST
,
elapsedRealtimeMs
,
loadDurationMs
,
loadable
.
bytesLoaded
());
}
else
{
playlistError
=
new
ParserException
(
"Loaded playlist has unexpected type."
);
}
}
@Override
public
void
onLoadCanceled
(
ParsingLoadable
<
HlsPlaylist
>
loadable
,
long
elapsedRealtimeMs
,
long
loadDurationMs
,
boolean
released
)
{
eventDispatcher
.
loadCanceled
(
loadable
.
dataSpec
,
C
.
DATA_TYPE_MANIFEST
,
elapsedRealtimeMs
,
loadDurationMs
,
loadable
.
bytesLoaded
());
}
@Override
public
@Loader
.
RetryAction
int
onLoadError
(
ParsingLoadable
<
HlsPlaylist
>
loadable
,
long
elapsedRealtimeMs
,
long
loadDurationMs
,
IOException
error
)
{
boolean
isFatal
=
error
instanceof
ParserException
;
eventDispatcher
.
loadError
(
loadable
.
dataSpec
,
C
.
DATA_TYPE_MANIFEST
,
elapsedRealtimeMs
,
loadDurationMs
,
loadable
.
bytesLoaded
(),
error
,
isFatal
);
boolean
shouldBlacklist
=
ChunkedTrackBlacklistUtil
.
shouldBlacklist
(
error
);
boolean
shouldRetryIfNotFatal
=
notifyPlaylistError
(
playlistUrl
,
shouldBlacklist
)
||
!
shouldBlacklist
;
if
(
isFatal
)
{
return
Loader
.
DONT_RETRY_FATAL
;
}
if
(
shouldBlacklist
)
{
shouldRetryIfNotFatal
|=
blacklistPlaylist
();
}
return
shouldRetryIfNotFatal
?
Loader
.
RETRY
:
Loader
.
DONT_RETRY
;
}
// Runnable implementation.
@Override
public
void
run
()
{
loadPending
=
false
;
loadPlaylistImmediately
();
}
// Internal methods.
private
void
loadPlaylistImmediately
()
{
mediaPlaylistLoader
.
startLoading
(
mediaPlaylistLoadable
,
this
,
minRetryCount
);
}
private
void
processLoadedPlaylist
(
HlsMediaPlaylist
loadedPlaylist
)
{
HlsMediaPlaylist
oldPlaylist
=
playlistSnapshot
;
long
currentTimeMs
=
SystemClock
.
elapsedRealtime
();
lastSnapshotLoadMs
=
currentTimeMs
;
playlistSnapshot
=
getLatestPlaylistSnapshot
(
oldPlaylist
,
loadedPlaylist
);
if
(
playlistSnapshot
!=
oldPlaylist
)
{
playlistError
=
null
;
lastSnapshotChangeMs
=
currentTimeMs
;
onPlaylistUpdated
(
playlistUrl
,
playlistSnapshot
);
}
else
if
(!
playlistSnapshot
.
hasEndTag
)
{
if
(
loadedPlaylist
.
mediaSequence
+
loadedPlaylist
.
segments
.
size
()
<
playlistSnapshot
.
mediaSequence
)
{
// The media sequence jumped backwards. The server has probably reset.
playlistError
=
new
PlaylistResetException
(
playlistUrl
.
url
);
notifyPlaylistError
(
playlistUrl
,
false
);
}
else
if
(
currentTimeMs
-
lastSnapshotChangeMs
>
C
.
usToMs
(
playlistSnapshot
.
targetDurationUs
)
*
PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT
)
{
// The playlist seems to be stuck. Blacklist it.
playlistError
=
new
PlaylistStuckException
(
playlistUrl
.
url
);
notifyPlaylistError
(
playlistUrl
,
true
);
blacklistPlaylist
();
}
}
// Do not allow the playlist to load again within the target duration if we obtained a new
// snapshot, or half the target duration otherwise.
earliestNextLoadTimeMs
=
currentTimeMs
+
C
.
usToMs
(
playlistSnapshot
!=
oldPlaylist
?
playlistSnapshot
.
targetDurationUs
:
(
playlistSnapshot
.
targetDurationUs
/
2
));
// Schedule a load if this is the primary playlist and it doesn't have an end tag. Else the
// next load will be scheduled when refreshPlaylist is called, or when this playlist becomes
// the primary.
if
(
playlistUrl
==
primaryHlsUrl
&&
!
playlistSnapshot
.
hasEndTag
)
{
loadPlaylist
();
}
}
/**
* Blacklists the playlist.
*
* @return Whether the playlist is the primary, despite being blacklisted.
*/
private
boolean
blacklistPlaylist
()
{
blacklistUntilMs
=
SystemClock
.
elapsedRealtime
()
+
ChunkedTrackBlacklistUtil
.
DEFAULT_TRACK_BLACKLIST_MS
;
return
primaryHlsUrl
==
playlistUrl
&&
!
maybeSelectNewPrimaryUrl
();
}
}
}
}
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/SsMediaPeriod.java
View file @
f7ed789f
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
.
source
.
smoothstreaming
;
package
com
.
google
.
android
.
exoplayer2
.
source
.
smoothstreaming
;
import
android.support.annotation.Nullable
;
import
android.util.Base64
;
import
android.util.Base64
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.SeekParameters
;
import
com.google.android.exoplayer2.SeekParameters
;
...
@@ -52,7 +53,7 @@ import java.util.ArrayList;
...
@@ -52,7 +53,7 @@ import java.util.ArrayList;
private
final
TrackEncryptionBox
[]
trackEncryptionBoxes
;
private
final
TrackEncryptionBox
[]
trackEncryptionBoxes
;
private
final
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
final
CompositeSequenceableLoaderFactory
compositeSequenceableLoaderFactory
;
private
Callback
callback
;
private
@Nullable
Callback
callback
;
private
SsManifest
manifest
;
private
SsManifest
manifest
;
private
ChunkSampleStream
<
SsChunkSource
>[]
sampleStreams
;
private
ChunkSampleStream
<
SsChunkSource
>[]
sampleStreams
;
private
SequenceableLoader
compositeSequenceableLoader
;
private
SequenceableLoader
compositeSequenceableLoader
;
...
@@ -98,6 +99,7 @@ import java.util.ArrayList;
...
@@ -98,6 +99,7 @@ import java.util.ArrayList;
for
(
ChunkSampleStream
<
SsChunkSource
>
sampleStream
:
sampleStreams
)
{
for
(
ChunkSampleStream
<
SsChunkSource
>
sampleStream
:
sampleStreams
)
{
sampleStream
.
release
();
sampleStream
.
release
();
}
}
callback
=
null
;
eventDispatcher
.
mediaPeriodReleased
();
eventDispatcher
.
mediaPeriodReleased
();
}
}
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java
View file @
f7ed789f
...
@@ -251,7 +251,7 @@ public final class SubtitleView extends View implements TextOutput {
...
@@ -251,7 +251,7 @@ public final class SubtitleView extends View implements TextOutput {
// Calculate the bounds after padding is taken into account.
// Calculate the bounds after padding is taken into account.
int
left
=
getLeft
()
+
getPaddingLeft
();
int
left
=
getLeft
()
+
getPaddingLeft
();
int
top
=
rawTop
+
getPaddingTop
();
int
top
=
rawTop
+
getPaddingTop
();
int
right
=
getRight
()
+
getPaddingRight
();
int
right
=
getRight
()
-
getPaddingRight
();
int
bottom
=
rawBottom
-
getPaddingBottom
();
int
bottom
=
rawBottom
-
getPaddingBottom
();
if
(
bottom
<=
top
||
right
<=
left
)
{
if
(
bottom
<=
top
||
right
<=
left
)
{
// No space to draw subtitles.
// No space to draw subtitles.
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/TrackSelectionView.java
View file @
f7ed789f
...
@@ -203,7 +203,9 @@ public class TrackSelectionView extends LinearLayout {
...
@@ -203,7 +203,9 @@ public class TrackSelectionView extends LinearLayout {
removeViewAt
(
i
);
removeViewAt
(
i
);
}
}
if
(
trackSelector
==
null
)
{
MappingTrackSelector
.
MappedTrackInfo
trackInfo
=
trackSelector
==
null
?
null
:
trackSelector
.
getCurrentMappedTrackInfo
();
if
(
trackSelector
==
null
||
trackInfo
==
null
)
{
// The view is not initialized.
// The view is not initialized.
disableView
.
setEnabled
(
false
);
disableView
.
setEnabled
(
false
);
defaultView
.
setEnabled
(
false
);
defaultView
.
setEnabled
(
false
);
...
@@ -212,7 +214,6 @@ public class TrackSelectionView extends LinearLayout {
...
@@ -212,7 +214,6 @@ public class TrackSelectionView extends LinearLayout {
disableView
.
setEnabled
(
true
);
disableView
.
setEnabled
(
true
);
defaultView
.
setEnabled
(
true
);
defaultView
.
setEnabled
(
true
);
MappingTrackSelector
.
MappedTrackInfo
trackInfo
=
trackSelector
.
getCurrentMappedTrackInfo
();
trackGroups
=
trackInfo
.
getTrackGroups
(
rendererIndex
);
trackGroups
=
trackInfo
.
getTrackGroups
(
rendererIndex
);
DefaultTrackSelector
.
Parameters
parameters
=
trackSelector
.
getParameters
();
DefaultTrackSelector
.
Parameters
parameters
=
trackSelector
.
getParameters
();
...
...
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