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
f6297f4f
authored
Jun 23, 2019
by
Oliver Woodman
Committed by
GitHub
Jun 23, 2019
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #5986 from google/dev-v2-r2.10.2
r2.10.2
parents
7e407089
b9c8861b
Hide whitespace changes
Inline
Side-by-side
Showing
65 changed files
with
1427 additions
and
487 deletions
RELEASENOTES.md
build.gradle
constants.gradle
demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/DefaultReceiverPlayerManager.java
extensions/cast/build.gradle
extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java
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
extensions/vp9/README.md
extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxOutputBuffer.java
extensions/vp9/src/main/jni/Application.mk
extensions/vp9/src/main/jni/generate_libvpx_android_configs.sh
extensions/vp9/src/main/jni/vpx_jni.cc
library/core/src/main/java/com/google/android/exoplayer2/C.java
library/core/src/main/java/com/google/android/exoplayer2/DefaultRenderersFactory.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/MediaPeriodHolder.java
library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java
library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java
library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java
library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java
library/core/src/main/java/com/google/android/exoplayer2/scheduler/Requirements.java
library/core/src/main/java/com/google/android/exoplayer2/scheduler/RequirementsWatcher.java
library/core/src/main/java/com/google/android/exoplayer2/source/SilenceMediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlNode.java
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRegion.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/WebvttDecoder.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultDataSource.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/ResolvingDataSource.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/util/MimeTypes.java
library/core/src/main/java/com/google/android/exoplayer2/util/Util.java
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
library/core/src/test/assets/webvtt/with_css_styles
library/core/src/test/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java
library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/CssParserTest.java
library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java
library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java
library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadManagerDashTest.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/SphericalSurfaceView.java
library/ui/src/main/res/layout/exo_playback_control_view.xml
library/ui/src/main/res/values/attrs.xml
library/ui/src/main/res/values/ids.xml
testutils/src/main/java/com/google/android/exoplayer2/testutil/DebugRenderersFactory.java
testutils/src/main/java/com/google/android/exoplayer2/testutil/HostActivity.java
testutils/src/main/res/layout/host_activity.xml → testutils/src/main/res/layout/exo_testutils_host_activity.xml
testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java
RELEASENOTES.md
View file @
f6297f4f
# Release notes #
# Release notes #
### 2.10.2 ###
*
Add
`ResolvingDataSource`
for just-in-time resolution of
`DataSpec`
s
(
[
#5779
](
https://github.com/google/ExoPlayer/issues/5779
)
).
*
Add
`SilenceMediaSource`
that can be used to play silence of a given
duration (
[
#5735
](
https://github.com/google/ExoPlayer/issues/5735
)
).
*
Offline:
*
Prevent unexpected
`DownloadHelper.Callback.onPrepared`
callbacks after
preparation of a
`DownloadHelper`
fails
(
[
#5915
](
https://github.com/google/ExoPlayer/issues/5915
)
).
*
Fix
`CacheUtil.cache()`
downloading too much data
(
[
#5927
](
https://github.com/google/ExoPlayer/issues/5927
)
).
*
Fix misreporting cached bytes when caching is paused
(
[
#5573
](
https://github.com/google/ExoPlayer/issues/5573
)
).
*
UI:
*
Allow setting
`DefaultTimeBar`
attributes on
`PlayerView`
and
`PlayerControlView`
.
*
Change playback controls toggle from touch down to touch up events
(
[
#5784
](
https://github.com/google/ExoPlayer/issues/5784
)
).
*
Fix issue where playback controls were not kept visible on key presses
(
[
#5963
](
https://github.com/google/ExoPlayer/issues/5963
)
).
*
Subtitles:
*
CEA-608: Handle XDS and TEXT modes
(
[
#5807
](
https://github.com/google/ExoPlayer/pull/5807
)
).
*
TTML: Fix bitmap rendering
(
[
#5633
](
https://github.com/google/ExoPlayer/pull/5633
)
).
*
IMA: Fix ad pod index offset calculation without preroll
(
[
#5928
](
https://github.com/google/ExoPlayer/issues/5928
)
).
*
Add a
`playWhenReady`
flag to MediaSessionConnector.PlaybackPreparer methods
to indicate whether a controller sent a play or only a prepare command. This
allows to take advantage of decoder reuse with the MediaSessionConnector
(
[
#5891
](
https://github.com/google/ExoPlayer/issues/5891
)
).
*
Add
`ProgressUpdateListener`
to
`PlayerControlView`
(
[
#5834
](
https://github.com/google/ExoPlayer/issues/5834
)
).
*
Add support for auto-detecting UDP streams in
`DefaultDataSource`
(
[
#6036
](
https://github.com/google/ExoPlayer/pull/6036
)
).
*
Allow enabling decoder fallback with
`DefaultRenderersFactory`
(
[
#5942
](
https://github.com/google/ExoPlayer/issues/5942
)
).
*
Gracefully handle revoked
`ACCESS_NETWORK_STATE`
permission
(
[
#6019
](
https://github.com/google/ExoPlayer/issues/6019
)
).
*
Fix decoding problems when seeking back after seeking beyond a mid-roll ad
(
[
#6009
](
https://github.com/google/ExoPlayer/issues/6009
)
).
*
Fix application of
`maxAudioBitrate`
for adaptive audio track groups
(
[
#6006
](
https://github.com/google/ExoPlayer/issues/6006
)
).
*
Fix bug caused by parallel adaptive track selection using
`Format`
s without
bitrate information
(
[
#5971
](
https://github.com/google/ExoPlayer/issues/5971
)
).
*
Fix bug in
`CastPlayer.getCurrentWindowIndex()`
(
[
#5955
](
https://github.com/google/ExoPlayer/issues/5955
)
).
### 2.10.1 ###
### 2.10.1 ###
*
Offline: Add option to remove all downloads.
*
Offline: Add option to remove all downloads.
...
...
build.gradle
View file @
f6297f4f
...
@@ -36,7 +36,7 @@ allprojects {
...
@@ -36,7 +36,7 @@ allprojects {
jcenter
()
jcenter
()
}
}
project
.
ext
{
project
.
ext
{
exoplayerPublishEnabled
=
tru
e
exoplayerPublishEnabled
=
fals
e
}
}
if
(
it
.
hasProperty
(
'externalBuildDir'
))
{
if
(
it
.
hasProperty
(
'externalBuildDir'
))
{
if
(!
new
File
(
externalBuildDir
).
isAbsolute
())
{
if
(!
new
File
(
externalBuildDir
).
isAbsolute
())
{
...
...
constants.gradle
View file @
f6297f4f
...
@@ -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.10.
1
'
releaseVersion
=
'2.10.
2
'
releaseVersionCode
=
201000
1
releaseVersionCode
=
201000
2
minSdkVersion
=
16
minSdkVersion
=
16
targetSdkVersion
=
28
targetSdkVersion
=
28
compileSdkVersion
=
28
compileSdkVersion
=
28
...
...
demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/DefaultReceiverPlayerManager.java
View file @
f6297f4f
...
@@ -66,7 +66,6 @@ import java.util.ArrayList;
...
@@ -66,7 +66,6 @@ import java.util.ArrayList;
private
final
Listener
listener
;
private
final
Listener
listener
;
private
final
ConcatenatingMediaSource
concatenatingMediaSource
;
private
final
ConcatenatingMediaSource
concatenatingMediaSource
;
private
boolean
castMediaQueueCreationPending
;
private
int
currentItemIndex
;
private
int
currentItemIndex
;
private
Player
currentPlayer
;
private
Player
currentPlayer
;
...
@@ -268,9 +267,6 @@ import java.util.ArrayList;
...
@@ -268,9 +267,6 @@ import java.util.ArrayList;
public
void
onTimelineChanged
(
public
void
onTimelineChanged
(
Timeline
timeline
,
@Nullable
Object
manifest
,
@TimelineChangeReason
int
reason
)
{
Timeline
timeline
,
@Nullable
Object
manifest
,
@TimelineChangeReason
int
reason
)
{
updateCurrentItemIndex
();
updateCurrentItemIndex
();
if
(
currentPlayer
==
castPlayer
&&
timeline
.
isEmpty
())
{
castMediaQueueCreationPending
=
true
;
}
}
}
// CastPlayer.SessionAvailabilityListener implementation.
// CastPlayer.SessionAvailabilityListener implementation.
...
@@ -332,7 +328,6 @@ import java.util.ArrayList;
...
@@ -332,7 +328,6 @@ import java.util.ArrayList;
this
.
currentPlayer
=
currentPlayer
;
this
.
currentPlayer
=
currentPlayer
;
// Media queue management.
// Media queue management.
castMediaQueueCreationPending
=
currentPlayer
==
castPlayer
;
if
(
currentPlayer
==
exoPlayer
)
{
if
(
currentPlayer
==
exoPlayer
)
{
exoPlayer
.
prepare
(
concatenatingMediaSource
);
exoPlayer
.
prepare
(
concatenatingMediaSource
);
}
}
...
@@ -352,12 +347,11 @@ import java.util.ArrayList;
...
@@ -352,12 +347,11 @@ import java.util.ArrayList;
*/
*/
private
void
setCurrentItem
(
int
itemIndex
,
long
positionMs
,
boolean
playWhenReady
)
{
private
void
setCurrentItem
(
int
itemIndex
,
long
positionMs
,
boolean
playWhenReady
)
{
maybeSetCurrentItemAndNotify
(
itemIndex
);
maybeSetCurrentItemAndNotify
(
itemIndex
);
if
(
c
astMediaQueueCreationPending
)
{
if
(
c
urrentPlayer
==
castPlayer
&&
castPlayer
.
getCurrentTimeline
().
isEmpty
()
)
{
MediaQueueItem
[]
items
=
new
MediaQueueItem
[
mediaQueue
.
size
()];
MediaQueueItem
[]
items
=
new
MediaQueueItem
[
mediaQueue
.
size
()];
for
(
int
i
=
0
;
i
<
items
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
items
.
length
;
i
++)
{
items
[
i
]
=
buildMediaQueueItem
(
mediaQueue
.
get
(
i
));
items
[
i
]
=
buildMediaQueueItem
(
mediaQueue
.
get
(
i
));
}
}
castMediaQueueCreationPending
=
false
;
castPlayer
.
loadItems
(
items
,
itemIndex
,
positionMs
,
Player
.
REPEAT_MODE_OFF
);
castPlayer
.
loadItems
(
items
,
itemIndex
,
positionMs
,
Player
.
REPEAT_MODE_OFF
);
}
else
{
}
else
{
currentPlayer
.
seekTo
(
itemIndex
,
positionMs
);
currentPlayer
.
seekTo
(
itemIndex
,
positionMs
);
...
...
extensions/cast/build.gradle
View file @
f6297f4f
...
@@ -31,7 +31,7 @@ android {
...
@@ -31,7 +31,7 @@ android {
}
}
dependencies
{
dependencies
{
api
'com.google.android.gms:play-services-cast-framework:16.
1.2
'
api
'com.google.android.gms:play-services-cast-framework:16.
2.0
'
implementation
'androidx.annotation:annotation:1.0.2'
implementation
'androidx.annotation:annotation:1.0.2'
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
project
(
modulePrefix
+
'library-ui'
)
implementation
project
(
modulePrefix
+
'library-ui'
)
...
...
extensions/cast/src/main/java/com/google/android/exoplayer2/ext/cast/CastPlayer.java
View file @
f6297f4f
...
@@ -45,8 +45,11 @@ import com.google.android.gms.cast.framework.media.RemoteMediaClient;
...
@@ -45,8 +45,11 @@ import com.google.android.gms.cast.framework.media.RemoteMediaClient;
import
com.google.android.gms.cast.framework.media.RemoteMediaClient.MediaChannelResult
;
import
com.google.android.gms.cast.framework.media.RemoteMediaClient.MediaChannelResult
;
import
com.google.android.gms.common.api.PendingResult
;
import
com.google.android.gms.common.api.PendingResult
;
import
com.google.android.gms.common.api.ResultCallback
;
import
com.google.android.gms.common.api.ResultCallback
;
import
java.util.ArrayDeque
;
import
java.util.ArrayList
;
import
java.util.Iterator
;
import
java.util.List
;
import
java.util.List
;
import
java.util.concurrent.CopyOnWriteArray
Se
t
;
import
java.util.concurrent.CopyOnWriteArray
Lis
t
;
/**
/**
* {@link Player} implementation that communicates with a Cast receiver app.
* {@link Player} implementation that communicates with a Cast receiver app.
...
@@ -86,8 +89,10 @@ public final class CastPlayer extends BasePlayer {
...
@@ -86,8 +89,10 @@ public final class CastPlayer extends BasePlayer {
private
final
StatusListener
statusListener
;
private
final
StatusListener
statusListener
;
private
final
SeekResultCallback
seekResultCallback
;
private
final
SeekResultCallback
seekResultCallback
;
// Listeners.
// Listeners and notification.
private
final
CopyOnWriteArraySet
<
EventListener
>
listeners
;
private
final
CopyOnWriteArrayList
<
ListenerHolder
>
listeners
;
private
final
ArrayList
<
ListenerNotificationTask
>
notificationsBatch
;
private
final
ArrayDeque
<
ListenerNotificationTask
>
ongoingNotificationsTasks
;
private
SessionAvailabilityListener
sessionAvailabilityListener
;
private
SessionAvailabilityListener
sessionAvailabilityListener
;
// Internal state.
// Internal state.
...
@@ -113,7 +118,9 @@ public final class CastPlayer extends BasePlayer {
...
@@ -113,7 +118,9 @@ public final class CastPlayer extends BasePlayer {
period
=
new
Timeline
.
Period
();
period
=
new
Timeline
.
Period
();
statusListener
=
new
StatusListener
();
statusListener
=
new
StatusListener
();
seekResultCallback
=
new
SeekResultCallback
();
seekResultCallback
=
new
SeekResultCallback
();
listeners
=
new
CopyOnWriteArraySet
<>();
listeners
=
new
CopyOnWriteArrayList
<>();
notificationsBatch
=
new
ArrayList
<>();
ongoingNotificationsTasks
=
new
ArrayDeque
<>();
SessionManager
sessionManager
=
castContext
.
getSessionManager
();
SessionManager
sessionManager
=
castContext
.
getSessionManager
();
sessionManager
.
addSessionManagerListener
(
statusListener
,
CastSession
.
class
);
sessionManager
.
addSessionManagerListener
(
statusListener
,
CastSession
.
class
);
...
@@ -296,12 +303,17 @@ public final class CastPlayer extends BasePlayer {
...
@@ -296,12 +303,17 @@ public final class CastPlayer extends BasePlayer {
@Override
@Override
public
void
addListener
(
EventListener
listener
)
{
public
void
addListener
(
EventListener
listener
)
{
listeners
.
add
(
listener
);
listeners
.
add
IfAbsent
(
new
ListenerHolder
(
listener
)
);
}
}
@Override
@Override
public
void
removeListener
(
EventListener
listener
)
{
public
void
removeListener
(
EventListener
listener
)
{
listeners
.
remove
(
listener
);
for
(
ListenerHolder
listenerHolder
:
listeners
)
{
if
(
listenerHolder
.
listener
.
equals
(
listener
))
{
listenerHolder
.
release
();
listeners
.
remove
(
listenerHolder
);
}
}
}
}
@Override
@Override
...
@@ -347,14 +359,13 @@ public final class CastPlayer extends BasePlayer {
...
@@ -347,14 +359,13 @@ public final class CastPlayer extends BasePlayer {
pendingSeekCount
++;
pendingSeekCount
++;
pendingSeekWindowIndex
=
windowIndex
;
pendingSeekWindowIndex
=
windowIndex
;
pendingSeekPositionMs
=
positionMs
;
pendingSeekPositionMs
=
positionMs
;
for
(
EventListener
listener
:
listeners
)
{
notificationsBatch
.
add
(
listener
.
onPositionDiscontinuity
(
Player
.
DISCONTINUITY_REASON_SEEK
);
new
ListenerNotificationTask
(
}
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_SEEK
)));
}
else
if
(
pendingSeekCount
==
0
)
{
}
else
if
(
pendingSeekCount
==
0
)
{
for
(
EventListener
listener
:
listeners
)
{
notificationsBatch
.
add
(
new
ListenerNotificationTask
(
EventListener:
:
onSeekProcessed
));
listener
.
onSeekProcessed
();
}
}
}
flushNotifications
();
}
}
@Override
@Override
...
@@ -530,30 +541,40 @@ public final class CastPlayer extends BasePlayer {
...
@@ -530,30 +541,40 @@ public final class CastPlayer extends BasePlayer {
||
this
.
playWhenReady
!=
playWhenReady
)
{
||
this
.
playWhenReady
!=
playWhenReady
)
{
this
.
playbackState
=
playbackState
;
this
.
playbackState
=
playbackState
;
this
.
playWhenReady
=
playWhenReady
;
this
.
playWhenReady
=
playWhenReady
;
for
(
EventListener
listener
:
listeners
)
{
notificationsBatch
.
add
(
listener
.
onPlayerStateChanged
(
this
.
playWhenReady
,
this
.
playbackState
);
new
ListenerNotificationTask
(
}
listener
->
listener
.
onPlayerStateChanged
(
this
.
playWhenReady
,
this
.
playbackState
)));
}
}
@RepeatMode
int
repeatMode
=
fetchRepeatMode
(
remoteMediaClient
);
@RepeatMode
int
repeatMode
=
fetchRepeatMode
(
remoteMediaClient
);
if
(
this
.
repeatMode
!=
repeatMode
)
{
if
(
this
.
repeatMode
!=
repeatMode
)
{
this
.
repeatMode
=
repeatMode
;
this
.
repeatMode
=
repeatMode
;
for
(
EventListener
listener
:
listeners
)
{
notificationsBatch
.
add
(
listener
.
onRepeatModeChanged
(
repeatMode
);
new
ListenerNotificationTask
(
listener
->
listener
.
onRepeatModeChanged
(
this
.
repeatMode
)));
}
}
maybeUpdateTimelineAndNotify
();
int
currentWindowIndex
=
C
.
INDEX_UNSET
;
MediaQueueItem
currentItem
=
remoteMediaClient
.
getCurrentItem
();
if
(
currentItem
!=
null
)
{
currentWindowIndex
=
currentTimeline
.
getIndexOfPeriod
(
currentItem
.
getItemId
());
}
if
(
currentWindowIndex
==
C
.
INDEX_UNSET
)
{
// The timeline is empty. Fall back to index 0, which is what ExoPlayer would do.
currentWindowIndex
=
0
;
}
}
int
currentWindowIndex
=
fetchCurrentWindowIndex
(
getMediaStatus
());
if
(
this
.
currentWindowIndex
!=
currentWindowIndex
&&
pendingSeekCount
==
0
)
{
if
(
this
.
currentWindowIndex
!=
currentWindowIndex
&&
pendingSeekCount
==
0
)
{
this
.
currentWindowIndex
=
currentWindowIndex
;
this
.
currentWindowIndex
=
currentWindowIndex
;
for
(
EventListener
listener
:
listeners
)
{
notificationsBatch
.
add
(
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_PERIOD_TRANSITION
);
new
ListenerNotificationTask
(
}
listener
->
listener
.
onPositionDiscontinuity
(
DISCONTINUITY_REASON_PERIOD_TRANSITION
)));
}
}
if
(
updateTracksAndSelections
())
{
if
(
updateTracksAndSelections
())
{
for
(
EventListener
listener
:
listeners
)
{
notificationsBatch
.
add
(
listener
.
onTracksChanged
(
currentTrackGroups
,
currentTrackSelection
);
new
ListenerNotificationTask
(
}
listener
->
listener
.
onTracksChanged
(
currentTrackGroups
,
currentTrackSelection
)));
}
}
maybeUpdateTimelineAndNotify
();
flushNotifications
();
}
}
private
void
maybeUpdateTimelineAndNotify
()
{
private
void
maybeUpdateTimelineAndNotify
()
{
...
@@ -561,9 +582,10 @@ public final class CastPlayer extends BasePlayer {
...
@@ -561,9 +582,10 @@ public final class CastPlayer extends BasePlayer {
@Player
.
TimelineChangeReason
int
reason
=
waitingForInitialTimeline
@Player
.
TimelineChangeReason
int
reason
=
waitingForInitialTimeline
?
Player
.
TIMELINE_CHANGE_REASON_PREPARED
:
Player
.
TIMELINE_CHANGE_REASON_DYNAMIC
;
?
Player
.
TIMELINE_CHANGE_REASON_PREPARED
:
Player
.
TIMELINE_CHANGE_REASON_DYNAMIC
;
waitingForInitialTimeline
=
false
;
waitingForInitialTimeline
=
false
;
for
(
EventListener
listener
:
listeners
)
{
notificationsBatch
.
add
(
listener
.
onTimelineChanged
(
currentTimeline
,
null
,
reason
);
new
ListenerNotificationTask
(
}
listener
->
listener
.
onTimelineChanged
(
currentTimeline
,
/* manifest= */
null
,
reason
)));
}
}
}
}
...
@@ -701,16 +723,6 @@ public final class CastPlayer extends BasePlayer {
...
@@ -701,16 +723,6 @@ public final class CastPlayer extends BasePlayer {
}
}
}
}
/**
* Retrieves the current item index from {@code mediaStatus} and maps it into a window index. If
* there is no media session, returns 0.
*/
private
static
int
fetchCurrentWindowIndex
(
@Nullable
MediaStatus
mediaStatus
)
{
Integer
currentItemId
=
mediaStatus
!=
null
?
mediaStatus
.
getIndexById
(
mediaStatus
.
getCurrentItemId
())
:
null
;
return
currentItemId
!=
null
?
currentItemId
:
0
;
}
private
static
boolean
isTrackActive
(
long
id
,
long
[]
activeTrackIds
)
{
private
static
boolean
isTrackActive
(
long
id
,
long
[]
activeTrackIds
)
{
for
(
long
activeTrackId
:
activeTrackIds
)
{
for
(
long
activeTrackId
:
activeTrackIds
)
{
if
(
activeTrackId
==
id
)
{
if
(
activeTrackId
==
id
)
{
...
@@ -826,7 +838,23 @@ public final class CastPlayer extends BasePlayer {
...
@@ -826,7 +838,23 @@ public final class CastPlayer extends BasePlayer {
}
}
// Result callbacks hooks.
// Internal methods.
private
void
flushNotifications
()
{
boolean
recursiveNotification
=
!
ongoingNotificationsTasks
.
isEmpty
();
ongoingNotificationsTasks
.
addAll
(
notificationsBatch
);
notificationsBatch
.
clear
();
if
(
recursiveNotification
)
{
// This will be handled once the current notification task is finished.
return
;
}
while
(!
ongoingNotificationsTasks
.
isEmpty
())
{
ongoingNotificationsTasks
.
peekFirst
().
execute
();
ongoingNotificationsTasks
.
removeFirst
();
}
}
// Internal classes.
private
final
class
SeekResultCallback
implements
ResultCallback
<
MediaChannelResult
>
{
private
final
class
SeekResultCallback
implements
ResultCallback
<
MediaChannelResult
>
{
...
@@ -840,9 +868,25 @@ public final class CastPlayer extends BasePlayer {
...
@@ -840,9 +868,25 @@ public final class CastPlayer extends BasePlayer {
if
(--
pendingSeekCount
==
0
)
{
if
(--
pendingSeekCount
==
0
)
{
pendingSeekWindowIndex
=
C
.
INDEX_UNSET
;
pendingSeekWindowIndex
=
C
.
INDEX_UNSET
;
pendingSeekPositionMs
=
C
.
TIME_UNSET
;
pendingSeekPositionMs
=
C
.
TIME_UNSET
;
for
(
EventListener
listener
:
listeners
)
{
notificationsBatch
.
add
(
new
ListenerNotificationTask
(
EventListener:
:
onSeekProcessed
));
listener
.
onSeekProcessed
();
flushNotifications
();
}
}
}
}
private
final
class
ListenerNotificationTask
{
private
final
Iterator
<
ListenerHolder
>
listenersSnapshot
;
private
final
ListenerInvocation
listenerInvocation
;
private
ListenerNotificationTask
(
ListenerInvocation
listenerInvocation
)
{
this
.
listenersSnapshot
=
listeners
.
iterator
();
this
.
listenerInvocation
=
listenerInvocation
;
}
public
void
execute
()
{
while
(
listenersSnapshot
.
hasNext
())
{
listenersSnapshot
.
next
().
invoke
(
listenerInvocation
);
}
}
}
}
}
}
...
...
extensions/ima/build.gradle
View file @
f6297f4f
...
@@ -34,6 +34,7 @@ android {
...
@@ -34,6 +34,7 @@ android {
dependencies
{
dependencies
{
api
'com.google.ads.interactivemedia.v3:interactivemedia:3.11.2'
api
'com.google.ads.interactivemedia.v3:interactivemedia:3.11.2'
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
'androidx.annotation:annotation:1.0.2'
implementation
'com.google.android.gms:play-services-ads-identifier:16.0.0'
implementation
'com.google.android.gms:play-services-ads-identifier:16.0.0'
testImplementation
project
(
modulePrefix
+
'testutils-robolectric'
)
testImplementation
project
(
modulePrefix
+
'testutils-robolectric'
)
}
}
...
...
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
View file @
f6297f4f
...
@@ -1054,13 +1054,8 @@ public final class ImaAdsLoader
...
@@ -1054,13 +1054,8 @@ public final class ImaAdsLoader
long
contentPositionMs
=
player
.
getCurrentPosition
();
long
contentPositionMs
=
player
.
getCurrentPosition
();
int
adGroupIndexForPosition
=
int
adGroupIndexForPosition
=
adPlaybackState
.
getAdGroupIndexForPositionUs
(
C
.
msToUs
(
contentPositionMs
));
adPlaybackState
.
getAdGroupIndexForPositionUs
(
C
.
msToUs
(
contentPositionMs
));
if
(
adGroupIndexForPosition
==
0
)
{
if
(
adGroupIndexForPosition
>
0
&&
adGroupIndexForPosition
!=
C
.
INDEX_UNSET
)
{
podIndexOffset
=
0
;
// Skip any ad groups before the one at or immediately before the playback position.
}
else
if
(
adGroupIndexForPosition
==
C
.
INDEX_UNSET
)
{
// There is no preroll and midroll pod indices start at 1.
podIndexOffset
=
-
1
;
}
else
/* adGroupIndexForPosition > 0 */
{
// Skip ad groups before the one at or immediately before the playback position.
for
(
int
i
=
0
;
i
<
adGroupIndexForPosition
;
i
++)
{
for
(
int
i
=
0
;
i
<
adGroupIndexForPosition
;
i
++)
{
adPlaybackState
=
adPlaybackState
.
withSkippedAdGroup
(
i
);
adPlaybackState
=
adPlaybackState
.
withSkippedAdGroup
(
i
);
}
}
...
@@ -1070,9 +1065,18 @@ public final class ImaAdsLoader
...
@@ -1070,9 +1065,18 @@ public final class ImaAdsLoader
long
adGroupBeforeTimeUs
=
adGroupTimesUs
[
adGroupIndexForPosition
-
1
];
long
adGroupBeforeTimeUs
=
adGroupTimesUs
[
adGroupIndexForPosition
-
1
];
double
midpointTimeUs
=
(
adGroupForPositionTimeUs
+
adGroupBeforeTimeUs
)
/
2
d
;
double
midpointTimeUs
=
(
adGroupForPositionTimeUs
+
adGroupBeforeTimeUs
)
/
2
d
;
adsRenderingSettings
.
setPlayAdsAfterTime
(
midpointTimeUs
/
C
.
MICROS_PER_SECOND
);
adsRenderingSettings
.
setPlayAdsAfterTime
(
midpointTimeUs
/
C
.
MICROS_PER_SECOND
);
}
// We're removing one or more ads, which means that the earliest ad (if any) will be a
// IMA indexes any remaining midroll ad pods from 1. A preroll (if present) has index 0.
// midroll/postroll. Midroll pod indices start at 1.
// Store an index offset as we want to index all ads (including skipped ones) from 0.
if
(
adGroupIndexForPosition
==
0
&&
adGroupTimesUs
[
0
]
==
0
)
{
// We are playing a preroll.
podIndexOffset
=
0
;
}
else
if
(
adGroupIndexForPosition
==
C
.
INDEX_UNSET
)
{
// There's no ad to play which means there's no preroll.
podIndexOffset
=
-
1
;
}
else
{
// We are playing a midroll and any ads before it were skipped.
podIndexOffset
=
adGroupIndexForPosition
-
1
;
podIndexOffset
=
adGroupIndexForPosition
-
1
;
}
}
...
...
extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java
View file @
f6297f4f
...
@@ -172,7 +172,7 @@ public final class MediaSessionConnector {
...
@@ -172,7 +172,7 @@ public final class MediaSessionConnector {
ResultReceiver
cb
);
ResultReceiver
cb
);
}
}
/** Interface to which playback preparation actions are delegated. */
/** Interface to which playback preparation a
nd play a
ctions are delegated. */
public
interface
PlaybackPreparer
extends
CommandReceiver
{
public
interface
PlaybackPreparer
extends
CommandReceiver
{
long
ACTIONS
=
long
ACTIONS
=
...
@@ -197,14 +197,36 @@ public final class MediaSessionConnector {
...
@@ -197,14 +197,36 @@ public final class MediaSessionConnector {
* @return The bitmask of the supported media actions.
* @return The bitmask of the supported media actions.
*/
*/
long
getSupportedPrepareActions
();
long
getSupportedPrepareActions
();
/** See {@link MediaSessionCompat.Callback#onPrepare()}. */
/**
void
onPrepare
();
* See {@link MediaSessionCompat.Callback#onPrepare()}.
/** See {@link MediaSessionCompat.Callback#onPrepareFromMediaId(String, Bundle)}. */
*
void
onPrepareFromMediaId
(
String
mediaId
,
Bundle
extras
);
* @param playWhenReady Whether playback should be started after preparation.
/** See {@link MediaSessionCompat.Callback#onPrepareFromSearch(String, Bundle)}. */
*/
void
onPrepareFromSearch
(
String
query
,
Bundle
extras
);
void
onPrepare
(
boolean
playWhenReady
);
/** See {@link MediaSessionCompat.Callback#onPrepareFromUri(Uri, Bundle)}. */
/**
void
onPrepareFromUri
(
Uri
uri
,
Bundle
extras
);
* See {@link MediaSessionCompat.Callback#onPrepareFromMediaId(String, Bundle)}.
*
* @param mediaId The media id of the media item to be prepared.
* @param playWhenReady Whether playback should be started after preparation.
* @param extras A {@link Bundle} of extras passed by the media controller.
*/
void
onPrepareFromMediaId
(
String
mediaId
,
boolean
playWhenReady
,
Bundle
extras
);
/**
* See {@link MediaSessionCompat.Callback#onPrepareFromSearch(String, Bundle)}.
*
* @param query The search query.
* @param playWhenReady Whether playback should be started after preparation.
* @param extras A {@link Bundle} of extras passed by the media controller.
*/
void
onPrepareFromSearch
(
String
query
,
boolean
playWhenReady
,
Bundle
extras
);
/**
* See {@link MediaSessionCompat.Callback#onPrepareFromUri(Uri, Bundle)}.
*
* @param uri The {@link Uri} of the media item to be prepared.
* @param playWhenReady Whether playback should be started after preparation.
* @param extras A {@link Bundle} of extras passed by the media controller.
*/
void
onPrepareFromUri
(
Uri
uri
,
boolean
playWhenReady
,
Bundle
extras
);
}
}
/**
/**
...
@@ -834,13 +856,6 @@ public final class MediaSessionConnector {
...
@@ -834,13 +856,6 @@ public final class MediaSessionConnector {
return
player
!=
null
&&
mediaButtonEventHandler
!=
null
;
return
player
!=
null
&&
mediaButtonEventHandler
!=
null
;
}
}
private
void
stopPlayerForPrepare
(
boolean
playWhenReady
)
{
if
(
player
!=
null
)
{
player
.
stop
();
player
.
setPlayWhenReady
(
playWhenReady
);
}
}
private
void
rewind
(
Player
player
)
{
private
void
rewind
(
Player
player
)
{
if
(
player
.
isCurrentWindowSeekable
()
&&
rewindMs
>
0
)
{
if
(
player
.
isCurrentWindowSeekable
()
&&
rewindMs
>
0
)
{
seekTo
(
player
,
player
.
getCurrentPosition
()
-
rewindMs
);
seekTo
(
player
,
player
.
getCurrentPosition
()
-
rewindMs
);
...
@@ -1047,12 +1062,12 @@ public final class MediaSessionConnector {
...
@@ -1047,12 +1062,12 @@ public final class MediaSessionConnector {
if
(
canDispatchPlaybackAction
(
PlaybackStateCompat
.
ACTION_PLAY
))
{
if
(
canDispatchPlaybackAction
(
PlaybackStateCompat
.
ACTION_PLAY
))
{
if
(
player
.
getPlaybackState
()
==
Player
.
STATE_IDLE
)
{
if
(
player
.
getPlaybackState
()
==
Player
.
STATE_IDLE
)
{
if
(
playbackPreparer
!=
null
)
{
if
(
playbackPreparer
!=
null
)
{
playbackPreparer
.
onPrepare
();
playbackPreparer
.
onPrepare
(
/* playWhenReady= */
true
);
}
}
}
else
if
(
player
.
getPlaybackState
()
==
Player
.
STATE_ENDED
)
{
}
else
if
(
player
.
getPlaybackState
()
==
Player
.
STATE_ENDED
)
{
controlDispatcher
.
dispatchSeekTo
(
player
,
player
.
getCurrentWindowIndex
(),
C
.
TIME_UNSET
);
controlDispatcher
.
dispatchSeekTo
(
player
,
player
.
getCurrentWindowIndex
(),
C
.
TIME_UNSET
);
controlDispatcher
.
dispatchSetPlayWhenReady
(
player
,
/* playWhenReady= */
true
);
}
}
controlDispatcher
.
dispatchSetPlayWhenReady
(
player
,
/* playWhenReady= */
true
);
}
}
}
}
...
@@ -1182,56 +1197,49 @@ public final class MediaSessionConnector {
...
@@ -1182,56 +1197,49 @@ public final class MediaSessionConnector {
@Override
@Override
public
void
onPrepare
()
{
public
void
onPrepare
()
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PREPARE
))
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PREPARE
))
{
stopPlayerForPrepare
(
/* playWhenReady= */
false
);
playbackPreparer
.
onPrepare
(
/* playWhenReady= */
false
);
playbackPreparer
.
onPrepare
();
}
}
}
}
@Override
@Override
public
void
onPrepareFromMediaId
(
String
mediaId
,
Bundle
extras
)
{
public
void
onPrepareFromMediaId
(
String
mediaId
,
Bundle
extras
)
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PREPARE_FROM_MEDIA_ID
))
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PREPARE_FROM_MEDIA_ID
))
{
stopPlayerForPrepare
(
/* playWhenReady= */
false
);
playbackPreparer
.
onPrepareFromMediaId
(
mediaId
,
/* playWhenReady= */
false
,
extras
);
playbackPreparer
.
onPrepareFromMediaId
(
mediaId
,
extras
);
}
}
}
}
@Override
@Override
public
void
onPrepareFromSearch
(
String
query
,
Bundle
extras
)
{
public
void
onPrepareFromSearch
(
String
query
,
Bundle
extras
)
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PREPARE_FROM_SEARCH
))
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PREPARE_FROM_SEARCH
))
{
stopPlayerForPrepare
(
/* playWhenReady= */
false
);
playbackPreparer
.
onPrepareFromSearch
(
query
,
/* playWhenReady= */
false
,
extras
);
playbackPreparer
.
onPrepareFromSearch
(
query
,
extras
);
}
}
}
}
@Override
@Override
public
void
onPrepareFromUri
(
Uri
uri
,
Bundle
extras
)
{
public
void
onPrepareFromUri
(
Uri
uri
,
Bundle
extras
)
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PREPARE_FROM_URI
))
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PREPARE_FROM_URI
))
{
stopPlayerForPrepare
(
/* playWhenReady= */
false
);
playbackPreparer
.
onPrepareFromUri
(
uri
,
/* playWhenReady= */
false
,
extras
);
playbackPreparer
.
onPrepareFromUri
(
uri
,
extras
);
}
}
}
}
@Override
@Override
public
void
onPlayFromMediaId
(
String
mediaId
,
Bundle
extras
)
{
public
void
onPlayFromMediaId
(
String
mediaId
,
Bundle
extras
)
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PLAY_FROM_MEDIA_ID
))
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PLAY_FROM_MEDIA_ID
))
{
stopPlayerForPrepare
(
/* playWhenReady= */
true
);
playbackPreparer
.
onPrepareFromMediaId
(
mediaId
,
/* playWhenReady= */
true
,
extras
);
playbackPreparer
.
onPrepareFromMediaId
(
mediaId
,
extras
);
}
}
}
}
@Override
@Override
public
void
onPlayFromSearch
(
String
query
,
Bundle
extras
)
{
public
void
onPlayFromSearch
(
String
query
,
Bundle
extras
)
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PLAY_FROM_SEARCH
))
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PLAY_FROM_SEARCH
))
{
stopPlayerForPrepare
(
/* playWhenReady= */
true
);
playbackPreparer
.
onPrepareFromSearch
(
query
,
/* playWhenReady= */
true
,
extras
);
playbackPreparer
.
onPrepareFromSearch
(
query
,
extras
);
}
}
}
}
@Override
@Override
public
void
onPlayFromUri
(
Uri
uri
,
Bundle
extras
)
{
public
void
onPlayFromUri
(
Uri
uri
,
Bundle
extras
)
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PLAY_FROM_URI
))
{
if
(
canDispatchToPlaybackPreparer
(
PlaybackStateCompat
.
ACTION_PLAY_FROM_URI
))
{
stopPlayerForPrepare
(
/* playWhenReady= */
true
);
playbackPreparer
.
onPrepareFromUri
(
uri
,
/* playWhenReady= */
true
,
extras
);
playbackPreparer
.
onPrepareFromUri
(
uri
,
extras
);
}
}
}
}
...
...
extensions/vp9/README.md
View file @
f6297f4f
...
@@ -29,6 +29,7 @@ VP9_EXT_PATH="${EXOPLAYER_ROOT}/extensions/vp9/src/main"
...
@@ -29,6 +29,7 @@ VP9_EXT_PATH="${EXOPLAYER_ROOT}/extensions/vp9/src/main"
```
```
*
Download the
[
Android NDK
][]
and set its location in an environment variable.
*
Download the
[
Android NDK
][]
and set its location in an environment variable.
The build configuration has been tested with Android NDK r19c.
```
```
NDK_PATH="<path to Android NDK>"
NDK_PATH="<path to Android NDK>"
...
@@ -54,7 +55,7 @@ git checkout tags/v1.8.0 -b v1.8.0
...
@@ -54,7 +55,7 @@ git checkout tags/v1.8.0 -b v1.8.0
```
```
cd ${VP9_EXT_PATH}/jni && \
cd ${VP9_EXT_PATH}/jni && \
./generate_libvpx_android_configs.sh
"${NDK_PATH}"
./generate_libvpx_android_configs.sh
```
```
*
Build the JNI native libraries from the command line:
*
Build the JNI native libraries from the command line:
...
@@ -66,7 +67,6 @@ ${NDK_PATH}/ndk-build APP_ABI=all -j4
...
@@ -66,7 +67,6 @@ ${NDK_PATH}/ndk-build APP_ABI=all -j4
[
top level README
]:
https://github.com/google/ExoPlayer/blob/release-v2/README.md
[
top level README
]:
https://github.com/google/ExoPlayer/blob/release-v2/README.md
[
Android NDK
]:
https://developer.android.com/tools/sdk/ndk/index.html
[
Android NDK
]:
https://developer.android.com/tools/sdk/ndk/index.html
[
#3520
]:
https://github.com/google/ExoPlayer/issues/3520
## Notes ##
## Notes ##
...
...
extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/VpxOutputBuffer.java
View file @
f6297f4f
...
@@ -60,8 +60,8 @@ public final class VpxOutputBuffer extends OutputBuffer {
...
@@ -60,8 +60,8 @@ public final class VpxOutputBuffer extends OutputBuffer {
* Initializes the buffer.
* Initializes the buffer.
*
*
* @param timeUs The presentation timestamp for the buffer, in microseconds.
* @param timeUs The presentation timestamp for the buffer, in microseconds.
* @param mode The output mode. One of {@link VpxDecoder#OUTPUT_MODE_NONE}
and
{@link
* @param mode The output mode. One of {@link VpxDecoder#OUTPUT_MODE_NONE}
,
{@link
* VpxDecoder#OUTPUT_MODE_YUV}.
* VpxDecoder#OUTPUT_MODE_YUV}
and {@link VpxDecoder#OUTPUT_MODE_SURFACE_YUV}
.
*/
*/
public
void
init
(
long
timeUs
,
int
mode
)
{
public
void
init
(
long
timeUs
,
int
mode
)
{
this
.
timeUs
=
timeUs
;
this
.
timeUs
=
timeUs
;
...
@@ -110,6 +110,15 @@ public final class VpxOutputBuffer extends OutputBuffer {
...
@@ -110,6 +110,15 @@ public final class VpxOutputBuffer extends OutputBuffer {
return
true
;
return
true
;
}
}
/**
* Configures the buffer for the given frame dimensions when passing actual frame data via {@link
* #decoderPrivate}. Called via JNI after decoding completes.
*/
public
void
initForPrivateFrame
(
int
width
,
int
height
)
{
this
.
width
=
width
;
this
.
height
=
height
;
}
private
void
initData
(
int
size
)
{
private
void
initData
(
int
size
)
{
if
(
data
==
null
||
data
.
capacity
()
<
size
)
{
if
(
data
==
null
||
data
.
capacity
()
<
size
)
{
data
=
ByteBuffer
.
allocateDirect
(
size
);
data
=
ByteBuffer
.
allocateDirect
(
size
);
...
...
extensions/vp9/src/main/jni/Application.mk
View file @
f6297f4f
...
@@ -15,6 +15,6 @@
...
@@ -15,6 +15,6 @@
#
#
APP_OPTIM := release
APP_OPTIM := release
APP_STL :=
gnustl
_static
APP_STL :=
c++
_static
APP_CPPFLAGS := -frtti
APP_CPPFLAGS := -frtti
APP_PLATFORM := android-
9
APP_PLATFORM := android-
16
extensions/vp9/src/main/jni/generate_libvpx_android_configs.sh
View file @
f6297f4f
...
@@ -20,46 +20,33 @@
...
@@ -20,46 +20,33 @@
set
-e
set
-e
if
[
$#
-ne
1
]
;
then
if
[
$#
-ne
0
]
;
then
echo
"Usage:
${
0
}
<path_to_android_ndk>
"
echo
"Usage:
${
0
}
"
exit
exit
fi
fi
ndk
=
"
${
1
}
"
shift
1
# configuration parameters common to all architectures
# configuration parameters common to all architectures
common_params
=
"--disable-examples --disable-docs --enable-realtime-only"
common_params
=
"--disable-examples --disable-docs --enable-realtime-only"
common_params+
=
" --disable-vp8 --disable-vp9-encoder --disable-webm-io"
common_params+
=
" --disable-vp8 --disable-vp9-encoder --disable-webm-io"
common_params+
=
" --disable-libyuv --disable-runtime-cpu-detect"
common_params+
=
" --disable-libyuv --disable-runtime-cpu-detect"
common_params+
=
" --enable-external-build"
# configuration parameters for various architectures
# configuration parameters for various architectures
arch[0]
=
"armeabi-v7a"
arch[0]
=
"armeabi-v7a"
config[0]
=
"--target=armv7-android-gcc --sdk-path=
$ndk
--enable-neon"
config[0]
=
"--target=armv7-android-gcc --enable-neon --enable-neon-asm"
config[0]+
=
" --enable-neon-asm"
arch[1]
=
"armeabi"
arch[1]
=
"x86"
config[1]
=
"--target=armv7-android-gcc --sdk-path=
$ndk
--disable-neon"
config[1]
=
"--force-target=x86-android-gcc --disable-sse2"
config[1]+
=
" --disable-neon-asm"
config[1]+
=
" --disable-sse3 --disable-ssse3 --disable-sse4_1 --disable-avx"
config[1]+
=
" --disable-avx2 --enable-pic"
arch[2]
=
"
mips
"
arch[2]
=
"
arm64-v8a
"
config[2]
=
"--force-target=
mips32-android-gcc --sdk-path=
$ndk
"
config[2]
=
"--force-target=
armv8-android-gcc --enable-neon
"
arch[3]
=
"x86"
arch[3]
=
"x86
_64
"
config[3]
=
"--force-target=x86
-android-gcc --sdk-path=
$ndk
--disable-sse2"
config[3]
=
"--force-target=x86
_64-android-gcc
--disable-sse2"
config[3]+
=
" --disable-sse3 --disable-ssse3 --disable-sse4_1 --disable-avx"
config[3]+
=
" --disable-sse3 --disable-ssse3 --disable-sse4_1 --disable-avx"
config[3]+
=
" --disable-avx2 --enable-pic"
config[3]+
=
" --disable-avx2 --enable-pic --disable-neon --disable-neon-asm"
arch[4]
=
"arm64-v8a"
config[4]
=
"--force-target=armv8-android-gcc --sdk-path=
$ndk
--enable-neon"
arch[5]
=
"x86_64"
config[5]
=
"--force-target=x86_64-android-gcc --sdk-path=
$ndk
--disable-sse2"
config[5]+
=
" --disable-sse3 --disable-ssse3 --disable-sse4_1 --disable-avx"
config[5]+
=
" --disable-avx2 --enable-pic --disable-neon --disable-neon-asm"
arch[6]
=
"mips64"
config[6]
=
"--force-target=mips64-android-gcc --sdk-path=
$ndk
"
limit
=
$((${#
arch
[@]
}
-
1
))
limit
=
$((${#
arch
[@]
}
-
1
))
...
@@ -102,10 +89,7 @@ for i in $(seq 0 ${limit}); do
...
@@ -102,10 +89,7 @@ for i in $(seq 0 ${limit}); do
# configure and make
# configure and make
echo
"build_android_configs: "
echo
"build_android_configs: "
echo
"configure
${
config
[
${
i
}
]
}
${
common_params
}
"
echo
"configure
${
config
[
${
i
}
]
}
${
common_params
}
"
../../libvpx/configure
${
config
[
${
i
}
]
}
${
common_params
}
--extra-cflags
=
"
\
../../libvpx/configure
${
config
[
${
i
}
]
}
${
common_params
}
-isystem
$ndk
/sysroot/usr/include/arm-linux-androideabi
\
-isystem
$ndk
/sysroot/usr/include
\
"
rm
-f
libvpx_srcs.txt
rm
-f
libvpx_srcs.txt
for
f
in
${
allowed_files
}
;
do
for
f
in
${
allowed_files
}
;
do
# the build system supports multiple different configurations. avoid
# the build system supports multiple different configurations. avoid
...
...
extensions/vp9/src/main/jni/vpx_jni.cc
View file @
f6297f4f
...
@@ -60,6 +60,7 @@
...
@@ -60,6 +60,7 @@
// JNI references for VpxOutputBuffer class.
// JNI references for VpxOutputBuffer class.
static
jmethodID
initForYuvFrame
;
static
jmethodID
initForYuvFrame
;
static
jmethodID
initForPrivateFrame
;
static
jfieldID
dataField
;
static
jfieldID
dataField
;
static
jfieldID
outputModeField
;
static
jfieldID
outputModeField
;
static
jfieldID
decoderPrivateField
;
static
jfieldID
decoderPrivateField
;
...
@@ -481,6 +482,8 @@ DECODER_FUNC(jlong, vpxInit, jboolean disableLoopFilter,
...
@@ -481,6 +482,8 @@ DECODER_FUNC(jlong, vpxInit, jboolean disableLoopFilter,
"com/google/android/exoplayer2/ext/vp9/VpxOutputBuffer"
);
"com/google/android/exoplayer2/ext/vp9/VpxOutputBuffer"
);
initForYuvFrame
=
env
->
GetMethodID
(
outputBufferClass
,
"initForYuvFrame"
,
initForYuvFrame
=
env
->
GetMethodID
(
outputBufferClass
,
"initForYuvFrame"
,
"(IIIII)Z"
);
"(IIIII)Z"
);
initForPrivateFrame
=
env
->
GetMethodID
(
outputBufferClass
,
"initForPrivateFrame"
,
"(II)V"
);
dataField
=
env
->
GetFieldID
(
outputBufferClass
,
"data"
,
dataField
=
env
->
GetFieldID
(
outputBufferClass
,
"data"
,
"Ljava/nio/ByteBuffer;"
);
"Ljava/nio/ByteBuffer;"
);
outputModeField
=
env
->
GetFieldID
(
outputBufferClass
,
"mode"
,
"I"
);
outputModeField
=
env
->
GetFieldID
(
outputBufferClass
,
"mode"
,
"I"
);
...
@@ -602,6 +605,10 @@ DECODER_FUNC(jint, vpxGetFrame, jlong jContext, jobject jOutputBuffer) {
...
@@ -602,6 +605,10 @@ DECODER_FUNC(jint, vpxGetFrame, jlong jContext, jobject jOutputBuffer) {
}
}
jfb
->
d_w
=
img
->
d_w
;
jfb
->
d_w
=
img
->
d_w
;
jfb
->
d_h
=
img
->
d_h
;
jfb
->
d_h
=
img
->
d_h
;
env
->
CallVoidMethod
(
jOutputBuffer
,
initForPrivateFrame
,
img
->
d_w
,
img
->
d_h
);
if
(
env
->
ExceptionCheck
())
{
return
-
1
;
}
env
->
SetIntField
(
jOutputBuffer
,
decoderPrivateField
,
env
->
SetIntField
(
jOutputBuffer
,
decoderPrivateField
,
id
+
kDecoderPrivateBase
);
id
+
kDecoderPrivateBase
);
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/C.java
View file @
f6297f4f
...
@@ -146,8 +146,8 @@ public final class C {
...
@@ -146,8 +146,8 @@ public final class C {
* {@link #ENCODING_INVALID}, {@link #ENCODING_PCM_8BIT}, {@link #ENCODING_PCM_16BIT}, {@link
* {@link #ENCODING_INVALID}, {@link #ENCODING_PCM_8BIT}, {@link #ENCODING_PCM_16BIT}, {@link
* #ENCODING_PCM_24BIT}, {@link #ENCODING_PCM_32BIT}, {@link #ENCODING_PCM_FLOAT}, {@link
* #ENCODING_PCM_24BIT}, {@link #ENCODING_PCM_32BIT}, {@link #ENCODING_PCM_FLOAT}, {@link
* #ENCODING_PCM_MU_LAW}, {@link #ENCODING_PCM_A_LAW}, {@link #ENCODING_AC3}, {@link
* #ENCODING_PCM_MU_LAW}, {@link #ENCODING_PCM_A_LAW}, {@link #ENCODING_AC3}, {@link
* #ENCODING_E_AC3}, {@link #ENCODING_
AC4}, {@link #ENCODING_DTS}, {@link #ENCODING_DTS_HD} or
* #ENCODING_E_AC3}, {@link #ENCODING_
E_AC3_JOC}, {@link #ENCODING_AC4}, {@link #ENCODING_DTS},
* {@link #ENCODING_DOLBY_TRUEHD}.
* {@link #ENCODING_D
TS_HD} or {@link #ENCODING_D
OLBY_TRUEHD}.
*/
*/
@Documented
@Documented
@Retention
(
RetentionPolicy
.
SOURCE
)
@Retention
(
RetentionPolicy
.
SOURCE
)
...
@@ -163,6 +163,7 @@ public final class C {
...
@@ -163,6 +163,7 @@ public final class C {
ENCODING_PCM_A_LAW
,
ENCODING_PCM_A_LAW
,
ENCODING_AC3
,
ENCODING_AC3
,
ENCODING_E_AC3
,
ENCODING_E_AC3
,
ENCODING_E_AC3_JOC
,
ENCODING_AC4
,
ENCODING_AC4
,
ENCODING_DTS
,
ENCODING_DTS
,
ENCODING_DTS_HD
,
ENCODING_DTS_HD
,
...
@@ -210,6 +211,8 @@ public final class C {
...
@@ -210,6 +211,8 @@ public final class C {
public
static
final
int
ENCODING_AC3
=
AudioFormat
.
ENCODING_AC3
;
public
static
final
int
ENCODING_AC3
=
AudioFormat
.
ENCODING_AC3
;
/** @see AudioFormat#ENCODING_E_AC3 */
/** @see AudioFormat#ENCODING_E_AC3 */
public
static
final
int
ENCODING_E_AC3
=
AudioFormat
.
ENCODING_E_AC3
;
public
static
final
int
ENCODING_E_AC3
=
AudioFormat
.
ENCODING_E_AC3
;
/** @see AudioFormat#ENCODING_E_AC3_JOC */
public
static
final
int
ENCODING_E_AC3_JOC
=
AudioFormat
.
ENCODING_E_AC3_JOC
;
/** @see AudioFormat#ENCODING_AC4 */
/** @see AudioFormat#ENCODING_AC4 */
public
static
final
int
ENCODING_AC4
=
AudioFormat
.
ENCODING_AC4
;
public
static
final
int
ENCODING_AC4
=
AudioFormat
.
ENCODING_AC4
;
/** @see AudioFormat#ENCODING_DTS */
/** @see AudioFormat#ENCODING_DTS */
...
...
library/core/src/main/java/com/google/android/exoplayer2/DefaultRenderersFactory.java
View file @
f6297f4f
...
@@ -24,6 +24,7 @@ import androidx.annotation.Nullable;
...
@@ -24,6 +24,7 @@ import androidx.annotation.Nullable;
import
com.google.android.exoplayer2.audio.AudioCapabilities
;
import
com.google.android.exoplayer2.audio.AudioCapabilities
;
import
com.google.android.exoplayer2.audio.AudioProcessor
;
import
com.google.android.exoplayer2.audio.AudioProcessor
;
import
com.google.android.exoplayer2.audio.AudioRendererEventListener
;
import
com.google.android.exoplayer2.audio.AudioRendererEventListener
;
import
com.google.android.exoplayer2.audio.DefaultAudioSink
;
import
com.google.android.exoplayer2.audio.MediaCodecAudioRenderer
;
import
com.google.android.exoplayer2.audio.MediaCodecAudioRenderer
;
import
com.google.android.exoplayer2.drm.DrmSessionManager
;
import
com.google.android.exoplayer2.drm.DrmSessionManager
;
import
com.google.android.exoplayer2.drm.FrameworkMediaCrypto
;
import
com.google.android.exoplayer2.drm.FrameworkMediaCrypto
;
...
@@ -90,6 +91,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -90,6 +91,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
@ExtensionRendererMode
private
int
extensionRendererMode
;
@ExtensionRendererMode
private
int
extensionRendererMode
;
private
long
allowedVideoJoiningTimeMs
;
private
long
allowedVideoJoiningTimeMs
;
private
boolean
playClearSamplesWithoutKeys
;
private
boolean
playClearSamplesWithoutKeys
;
private
boolean
enableDecoderFallback
;
private
MediaCodecSelector
mediaCodecSelector
;
private
MediaCodecSelector
mediaCodecSelector
;
/** @param context A {@link Context}. */
/** @param context A {@link Context}. */
...
@@ -203,6 +205,19 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -203,6 +205,19 @@ public class DefaultRenderersFactory implements RenderersFactory {
}
}
/**
/**
* Sets whether to enable fallback to lower-priority decoders if decoder initialization fails.
* This may result in using a decoder that is less efficient or slower than the primary decoder.
*
* @param enableDecoderFallback Whether to enable fallback to lower-priority decoders if decoder
* initialization fails.
* @return This factory, for convenience.
*/
public
DefaultRenderersFactory
setEnableDecoderFallback
(
boolean
enableDecoderFallback
)
{
this
.
enableDecoderFallback
=
enableDecoderFallback
;
return
this
;
}
/**
* Sets a {@link MediaCodecSelector} for use by {@link MediaCodec} based renderers.
* Sets a {@link MediaCodecSelector} for use by {@link MediaCodec} based renderers.
*
*
* <p>The default value is {@link MediaCodecSelector#DEFAULT}.
* <p>The default value is {@link MediaCodecSelector#DEFAULT}.
...
@@ -248,6 +263,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -248,6 +263,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
mediaCodecSelector
,
mediaCodecSelector
,
drmSessionManager
,
drmSessionManager
,
playClearSamplesWithoutKeys
,
playClearSamplesWithoutKeys
,
enableDecoderFallback
,
eventHandler
,
eventHandler
,
videoRendererEventListener
,
videoRendererEventListener
,
allowedVideoJoiningTimeMs
,
allowedVideoJoiningTimeMs
,
...
@@ -258,6 +274,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -258,6 +274,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
mediaCodecSelector
,
mediaCodecSelector
,
drmSessionManager
,
drmSessionManager
,
playClearSamplesWithoutKeys
,
playClearSamplesWithoutKeys
,
enableDecoderFallback
,
buildAudioProcessors
(),
buildAudioProcessors
(),
eventHandler
,
eventHandler
,
audioRendererEventListener
,
audioRendererEventListener
,
...
@@ -282,6 +299,9 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -282,6 +299,9 @@ public class DefaultRenderersFactory implements RenderersFactory {
* @param playClearSamplesWithoutKeys Whether renderers are permitted to play clear regions of
* @param playClearSamplesWithoutKeys Whether renderers are permitted to play clear regions of
* encrypted media prior to having obtained the keys necessary to decrypt encrypted regions of
* encrypted media prior to having obtained the keys necessary to decrypt encrypted regions of
* the media.
* the media.
* @param enableDecoderFallback Whether to enable fallback to lower-priority decoders if decoder
* initialization fails. This may result in using a decoder that is slower/less efficient than
* the primary decoder.
* @param eventHandler A handler associated with the main thread's looper.
* @param eventHandler A handler associated with the main thread's looper.
* @param eventListener An event listener.
* @param eventListener An event listener.
* @param allowedVideoJoiningTimeMs The maximum duration for which video renderers can attempt to
* @param allowedVideoJoiningTimeMs The maximum duration for which video renderers can attempt to
...
@@ -294,6 +314,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -294,6 +314,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
MediaCodecSelector
mediaCodecSelector
,
MediaCodecSelector
mediaCodecSelector
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
boolean
playClearSamplesWithoutKeys
,
boolean
playClearSamplesWithoutKeys
,
boolean
enableDecoderFallback
,
Handler
eventHandler
,
Handler
eventHandler
,
VideoRendererEventListener
eventListener
,
VideoRendererEventListener
eventListener
,
long
allowedVideoJoiningTimeMs
,
long
allowedVideoJoiningTimeMs
,
...
@@ -305,6 +326,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -305,6 +326,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
allowedVideoJoiningTimeMs
,
allowedVideoJoiningTimeMs
,
drmSessionManager
,
drmSessionManager
,
playClearSamplesWithoutKeys
,
playClearSamplesWithoutKeys
,
enableDecoderFallback
,
eventHandler
,
eventHandler
,
eventListener
,
eventListener
,
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY
));
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY
));
...
@@ -356,6 +378,9 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -356,6 +378,9 @@ public class DefaultRenderersFactory implements RenderersFactory {
* @param playClearSamplesWithoutKeys Whether renderers are permitted to play clear regions of
* @param playClearSamplesWithoutKeys Whether renderers are permitted to play clear regions of
* encrypted media prior to having obtained the keys necessary to decrypt encrypted regions of
* encrypted media prior to having obtained the keys necessary to decrypt encrypted regions of
* the media.
* the media.
* @param enableDecoderFallback Whether to enable fallback to lower-priority decoders if decoder
* initialization fails. This may result in using a decoder that is slower/less efficient than
* the primary decoder.
* @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio buffers
* @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio buffers
* before output. May be empty.
* before output. May be empty.
* @param eventHandler A handler to use when invoking event listeners and outputs.
* @param eventHandler A handler to use when invoking event listeners and outputs.
...
@@ -368,6 +393,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -368,6 +393,7 @@ public class DefaultRenderersFactory implements RenderersFactory {
MediaCodecSelector
mediaCodecSelector
,
MediaCodecSelector
mediaCodecSelector
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
boolean
playClearSamplesWithoutKeys
,
boolean
playClearSamplesWithoutKeys
,
boolean
enableDecoderFallback
,
AudioProcessor
[]
audioProcessors
,
AudioProcessor
[]
audioProcessors
,
Handler
eventHandler
,
Handler
eventHandler
,
AudioRendererEventListener
eventListener
,
AudioRendererEventListener
eventListener
,
...
@@ -378,10 +404,10 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -378,10 +404,10 @@ public class DefaultRenderersFactory implements RenderersFactory {
mediaCodecSelector
,
mediaCodecSelector
,
drmSessionManager
,
drmSessionManager
,
playClearSamplesWithoutKeys
,
playClearSamplesWithoutKeys
,
enableDecoderFallback
,
eventHandler
,
eventHandler
,
eventListener
,
eventListener
,
AudioCapabilities
.
getCapabilities
(
context
),
new
DefaultAudioSink
(
AudioCapabilities
.
getCapabilities
(
context
),
audioProcessors
)));
audioProcessors
));
if
(
extensionRendererMode
==
EXTENSION_RENDERER_MODE_OFF
)
{
if
(
extensionRendererMode
==
EXTENSION_RENDERER_MODE_OFF
)
{
return
;
return
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
View file @
f6297f4f
...
@@ -510,7 +510,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
...
@@ -510,7 +510,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
@Override
@Override
public
long
getTotalBufferedDuration
()
{
public
long
getTotalBufferedDuration
()
{
return
Math
.
max
(
0
,
C
.
usToMs
(
playbackInfo
.
totalBufferedDurationUs
)
);
return
C
.
usToMs
(
playbackInfo
.
totalBufferedDurationUs
);
}
}
@Override
@Override
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
View file @
f6297f4f
...
@@ -729,13 +729,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -729,13 +729,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
newPlayingPeriodHolder
=
queue
.
advancePlayingPeriod
();
newPlayingPeriodHolder
=
queue
.
advancePlayingPeriod
();
}
}
// Disable all the renderers if the period being played is changing, or if forced.
// Disable all renderers if the period being played is changing, if the seek results in negative
if
(
oldPlayingPeriodHolder
!=
newPlayingPeriodHolder
||
forceDisableRenderers
)
{
// renderer timestamps, or if forced.
if
(
forceDisableRenderers
||
oldPlayingPeriodHolder
!=
newPlayingPeriodHolder
||
(
newPlayingPeriodHolder
!=
null
&&
newPlayingPeriodHolder
.
toRendererTime
(
periodPositionUs
)
<
0
))
{
for
(
Renderer
renderer
:
enabledRenderers
)
{
for
(
Renderer
renderer
:
enabledRenderers
)
{
disableRenderer
(
renderer
);
disableRenderer
(
renderer
);
}
}
enabledRenderers
=
new
Renderer
[
0
];
enabledRenderers
=
new
Renderer
[
0
];
oldPlayingPeriodHolder
=
null
;
oldPlayingPeriodHolder
=
null
;
if
(
newPlayingPeriodHolder
!=
null
)
{
newPlayingPeriodHolder
.
setRendererOffset
(
/* rendererPositionOffsetUs= */
0
);
}
}
}
// Update the holders.
// Update the holders.
...
@@ -1798,9 +1805,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
...
@@ -1798,9 +1805,12 @@ import java.util.concurrent.atomic.AtomicBoolean;
private
long
getTotalBufferedDurationUs
(
long
bufferedPositionInLoadingPeriodUs
)
{
private
long
getTotalBufferedDurationUs
(
long
bufferedPositionInLoadingPeriodUs
)
{
MediaPeriodHolder
loadingPeriodHolder
=
queue
.
getLoadingPeriod
();
MediaPeriodHolder
loadingPeriodHolder
=
queue
.
getLoadingPeriod
();
return
loadingPeriodHolder
==
null
if
(
loadingPeriodHolder
==
null
)
{
?
0
return
0
;
:
bufferedPositionInLoadingPeriodUs
-
loadingPeriodHolder
.
toPeriodTime
(
rendererPositionUs
);
}
long
totalBufferedDurationUs
=
bufferedPositionInLoadingPeriodUs
-
loadingPeriodHolder
.
toPeriodTime
(
rendererPositionUs
);
return
Math
.
max
(
0
,
totalBufferedDurationUs
);
}
}
private
void
updateLoadControlTrackSelection
(
private
void
updateLoadControlTrackSelection
(
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
View file @
f6297f4f
...
@@ -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.10.
1
"
;
public
static
final
String
VERSION
=
"2.10.
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.10.
1
"
;
public
static
final
String
VERSION_SLASHY
=
"ExoPlayerLib/2.10.
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
=
201000
1
;
public
static
final
int
VERSION_INT
=
201000
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/MediaPeriodHolder.java
View file @
f6297f4f
...
@@ -67,8 +67,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
...
@@ -67,8 +67,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
* Creates a new holder with information required to play it as part of a timeline.
* Creates a new holder with information required to play it as part of a timeline.
*
*
* @param rendererCapabilities The renderer capabilities.
* @param rendererCapabilities The renderer capabilities.
* @param rendererPositionOffsetUs The time offset of the start of the media period to provide to
* @param rendererPositionOffsetUs The renderer time of the start of the period, in microseconds.
* renderers.
* @param trackSelector The track selector.
* @param trackSelector The track selector.
* @param allocator The allocator.
* @param allocator The allocator.
* @param mediaSource The media source that produced the media period.
* @param mediaSource The media source that produced the media period.
...
@@ -82,7 +81,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
...
@@ -82,7 +81,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
MediaSource
mediaSource
,
MediaSource
mediaSource
,
MediaPeriodInfo
info
)
{
MediaPeriodInfo
info
)
{
this
.
rendererCapabilities
=
rendererCapabilities
;
this
.
rendererCapabilities
=
rendererCapabilities
;
this
.
rendererPositionOffsetUs
=
rendererPositionOffsetUs
-
info
.
startPositionUs
;
this
.
rendererPositionOffsetUs
=
rendererPositionOffsetUs
;
this
.
trackSelector
=
trackSelector
;
this
.
trackSelector
=
trackSelector
;
this
.
mediaSource
=
mediaSource
;
this
.
mediaSource
=
mediaSource
;
this
.
uid
=
info
.
id
.
periodUid
;
this
.
uid
=
info
.
id
.
periodUid
;
...
@@ -115,6 +114,15 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
...
@@ -115,6 +114,15 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
return
rendererPositionOffsetUs
;
return
rendererPositionOffsetUs
;
}
}
/**
* Sets the renderer time of the start of the period, in microseconds.
*
* @param rendererPositionOffsetUs The new renderer position offset, in microseconds.
*/
public
void
setRendererOffset
(
long
rendererPositionOffsetUs
)
{
this
.
rendererPositionOffsetUs
=
rendererPositionOffsetUs
;
}
/** Returns start position of period in renderer time. */
/** Returns start position of period in renderer time. */
public
long
getStartPositionRendererTime
()
{
public
long
getStartPositionRendererTime
()
{
return
info
.
startPositionUs
+
rendererPositionOffsetUs
;
return
info
.
startPositionUs
+
rendererPositionOffsetUs
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java
View file @
f6297f4f
...
@@ -144,8 +144,8 @@ import com.google.android.exoplayer2.util.Assertions;
...
@@ -144,8 +144,8 @@ import com.google.android.exoplayer2.util.Assertions;
MediaPeriodInfo
info
)
{
MediaPeriodInfo
info
)
{
long
rendererPositionOffsetUs
=
long
rendererPositionOffsetUs
=
loading
==
null
loading
==
null
?
info
.
startPositionUs
?
(
info
.
id
.
isAd
()
?
info
.
contentPositionUs
:
0
)
:
(
loading
.
getRendererOffset
()
+
loading
.
info
.
durationUs
);
:
(
loading
.
getRendererOffset
()
+
loading
.
info
.
durationUs
-
info
.
startPositionUs
);
MediaPeriodHolder
newPeriodHolder
=
MediaPeriodHolder
newPeriodHolder
=
new
MediaPeriodHolder
(
new
MediaPeriodHolder
(
rendererCapabilities
,
rendererCapabilities
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java
View file @
f6297f4f
...
@@ -1125,6 +1125,7 @@ public final class DefaultAudioSink implements AudioSink {
...
@@ -1125,6 +1125,7 @@ public final class DefaultAudioSink implements AudioSink {
case
C
.
ENCODING_AC3
:
case
C
.
ENCODING_AC3
:
return
640
*
1000
/
8
;
return
640
*
1000
/
8
;
case
C
.
ENCODING_E_AC3
:
case
C
.
ENCODING_E_AC3
:
case
C
.
ENCODING_E_AC3_JOC
:
return
6144
*
1000
/
8
;
return
6144
*
1000
/
8
;
case
C
.
ENCODING_AC4
:
case
C
.
ENCODING_AC4
:
return
2688
*
1000
/
8
;
return
2688
*
1000
/
8
;
...
@@ -1154,7 +1155,7 @@ public final class DefaultAudioSink implements AudioSink {
...
@@ -1154,7 +1155,7 @@ public final class DefaultAudioSink implements AudioSink {
return
DtsUtil
.
parseDtsAudioSampleCount
(
buffer
);
return
DtsUtil
.
parseDtsAudioSampleCount
(
buffer
);
}
else
if
(
encoding
==
C
.
ENCODING_AC3
)
{
}
else
if
(
encoding
==
C
.
ENCODING_AC3
)
{
return
Ac3Util
.
getAc3SyncframeAudioSampleCount
();
return
Ac3Util
.
getAc3SyncframeAudioSampleCount
();
}
else
if
(
encoding
==
C
.
ENCODING_E_AC3
)
{
}
else
if
(
encoding
==
C
.
ENCODING_E_AC3
||
encoding
==
C
.
ENCODING_E_AC3_JOC
)
{
return
Ac3Util
.
parseEAc3SyncframeAudioSampleCount
(
buffer
);
return
Ac3Util
.
parseEAc3SyncframeAudioSampleCount
(
buffer
);
}
else
if
(
encoding
==
C
.
ENCODING_AC4
)
{
}
else
if
(
encoding
==
C
.
ENCODING_AC4
)
{
return
Ac4Util
.
parseAc4SyncframeAudioSampleCount
(
buffer
);
return
Ac4Util
.
parseAc4SyncframeAudioSampleCount
(
buffer
);
...
@@ -1177,11 +1178,10 @@ public final class DefaultAudioSink implements AudioSink {
...
@@ -1177,11 +1178,10 @@ public final class DefaultAudioSink implements AudioSink {
@TargetApi
(
21
)
@TargetApi
(
21
)
private
int
writeNonBlockingWithAvSyncV21
(
AudioTrack
audioTrack
,
ByteBuffer
buffer
,
int
size
,
private
int
writeNonBlockingWithAvSyncV21
(
AudioTrack
audioTrack
,
ByteBuffer
buffer
,
int
size
,
long
presentationTimeUs
)
{
long
presentationTimeUs
)
{
// TODO: Uncomment this when [Internal ref: b/33627517] is clarified or fixed.
if
(
Util
.
SDK_INT
>=
26
)
{
// if (Util.SDK_INT >= 23) {
// The underlying platform AudioTrack writes AV sync headers directly.
// // The underlying platform AudioTrack writes AV sync headers directly.
return
audioTrack
.
write
(
buffer
,
size
,
WRITE_NON_BLOCKING
,
presentationTimeUs
*
1000
);
// return audioTrack.write(buffer, size, WRITE_NON_BLOCKING, presentationTimeUs * 1000);
}
// }
if
(
avSyncHeader
==
null
)
{
if
(
avSyncHeader
==
null
)
{
avSyncHeader
=
ByteBuffer
.
allocate
(
16
);
avSyncHeader
=
ByteBuffer
.
allocate
(
16
);
avSyncHeader
.
order
(
ByteOrder
.
BIG_ENDIAN
);
avSyncHeader
.
order
(
ByteOrder
.
BIG_ENDIAN
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
View file @
f6297f4f
...
@@ -245,12 +245,50 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
...
@@ -245,12 +245,50 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@Nullable
Handler
eventHandler
,
@Nullable
Handler
eventHandler
,
@Nullable
AudioRendererEventListener
eventListener
,
@Nullable
AudioRendererEventListener
eventListener
,
AudioSink
audioSink
)
{
AudioSink
audioSink
)
{
this
(
context
,
mediaCodecSelector
,
drmSessionManager
,
playClearSamplesWithoutKeys
,
/* enableDecoderFallback= */
false
,
eventHandler
,
eventListener
,
audioSink
);
}
/**
* @param context A context.
* @param mediaCodecSelector A decoder selector.
* @param drmSessionManager For use with encrypted content. May be null if support for encrypted
* content is not required.
* @param playClearSamplesWithoutKeys Encrypted media may contain clear (un-encrypted) regions.
* For example a media file may start with a short clear region so as to allow playback to
* begin in parallel with key acquisition. This parameter specifies whether the renderer is
* permitted to play clear regions of encrypted media files before {@code drmSessionManager}
* has obtained the keys necessary to decrypt encrypted regions of the media.
* @param enableDecoderFallback Whether to enable fallback to lower-priority decoders if decoder
* initialization fails. This may result in using a decoder that is slower/less efficient than
* the primary decoder.
* @param eventHandler A handler to use when delivering events to {@code eventListener}. May be
* null if delivery of events is not required.
* @param eventListener A listener of events. May be null if delivery of events is not required.
* @param audioSink The sink to which audio will be output.
*/
public
MediaCodecAudioRenderer
(
Context
context
,
MediaCodecSelector
mediaCodecSelector
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
boolean
playClearSamplesWithoutKeys
,
boolean
enableDecoderFallback
,
@Nullable
Handler
eventHandler
,
@Nullable
AudioRendererEventListener
eventListener
,
AudioSink
audioSink
)
{
super
(
super
(
C
.
TRACK_TYPE_AUDIO
,
C
.
TRACK_TYPE_AUDIO
,
mediaCodecSelector
,
mediaCodecSelector
,
drmSessionManager
,
drmSessionManager
,
playClearSamplesWithoutKeys
,
playClearSamplesWithoutKeys
,
/* enableDecoderFallback= */
false
,
enableDecoderFallback
,
/* assumedMinimumCodecOperatingRate= */
44100
);
/* assumedMinimumCodecOperatingRate= */
44100
);
this
.
context
=
context
.
getApplicationContext
();
this
.
context
=
context
.
getApplicationContext
();
this
.
audioSink
=
audioSink
;
this
.
audioSink
=
audioSink
;
...
@@ -341,7 +379,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
...
@@ -341,7 +379,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
* @return Whether passthrough playback is supported.
* @return Whether passthrough playback is supported.
*/
*/
protected
boolean
allowPassthrough
(
int
channelCount
,
String
mimeType
)
{
protected
boolean
allowPassthrough
(
int
channelCount
,
String
mimeType
)
{
return
audioSink
.
supportsOutput
(
channelCount
,
MimeTypes
.
getEncoding
(
mimeType
))
;
return
getPassthroughEncoding
(
channelCount
,
mimeType
)
!=
C
.
ENCODING_INVALID
;
}
}
@Override
@Override
...
@@ -437,11 +475,14 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
...
@@ -437,11 +475,14 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
@C
.
Encoding
int
encoding
;
@C
.
Encoding
int
encoding
;
MediaFormat
format
;
MediaFormat
format
;
if
(
passthroughMediaFormat
!=
null
)
{
if
(
passthroughMediaFormat
!=
null
)
{
encoding
=
MimeTypes
.
getEncoding
(
passthroughMediaFormat
.
getString
(
MediaFormat
.
KEY_MIME
));
format
=
passthroughMediaFormat
;
format
=
passthroughMediaFormat
;
encoding
=
getPassthroughEncoding
(
format
.
getInteger
(
MediaFormat
.
KEY_CHANNEL_COUNT
),
format
.
getString
(
MediaFormat
.
KEY_MIME
));
}
else
{
}
else
{
encoding
=
pcmEncoding
;
format
=
outputFormat
;
format
=
outputFormat
;
encoding
=
pcmEncoding
;
}
}
int
channelCount
=
format
.
getInteger
(
MediaFormat
.
KEY_CHANNEL_COUNT
);
int
channelCount
=
format
.
getInteger
(
MediaFormat
.
KEY_CHANNEL_COUNT
);
int
sampleRate
=
format
.
getInteger
(
MediaFormat
.
KEY_SAMPLE_RATE
);
int
sampleRate
=
format
.
getInteger
(
MediaFormat
.
KEY_SAMPLE_RATE
);
...
@@ -464,6 +505,28 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
...
@@ -464,6 +505,28 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
}
}
/**
/**
* Returns the {@link C.Encoding} constant to use for passthrough of the given format, or {@link
* C#ENCODING_INVALID} if passthrough is not possible.
*/
@C
.
Encoding
protected
int
getPassthroughEncoding
(
int
channelCount
,
String
mimeType
)
{
if
(
MimeTypes
.
AUDIO_E_AC3_JOC
.
equals
(
mimeType
))
{
if
(
audioSink
.
supportsOutput
(
channelCount
,
C
.
ENCODING_E_AC3_JOC
))
{
return
MimeTypes
.
getEncoding
(
MimeTypes
.
AUDIO_E_AC3_JOC
);
}
// E-AC3 receivers can decode JOC streams, but in 2-D rather than 3-D, so try to fall back.
mimeType
=
MimeTypes
.
AUDIO_E_AC3
;
}
@C
.
Encoding
int
encoding
=
MimeTypes
.
getEncoding
(
mimeType
);
if
(
audioSink
.
supportsOutput
(
channelCount
,
encoding
))
{
return
encoding
;
}
else
{
return
C
.
ENCODING_INVALID
;
}
}
/**
* Called when the audio session id becomes known. The default implementation is a no-op. One
* Called when the audio session id becomes known. The default implementation is a no-op. One
* reason for overriding this method would be to instantiate and enable a {@link Virtualizer} in
* reason for overriding this method would be to instantiate and enable a {@link Virtualizer} in
* order to spatialize the audio channels. For this use case, any {@link Virtualizer} instances
* order to spatialize the audio channels. For this use case, any {@link Virtualizer} instances
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
View file @
f6297f4f
...
@@ -53,7 +53,6 @@ import java.lang.annotation.RetentionPolicy;
...
@@ -53,7 +53,6 @@ import java.lang.annotation.RetentionPolicy;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
import
java.util.ArrayDeque
;
import
java.util.ArrayDeque
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.List
;
/**
/**
...
@@ -454,15 +453,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
...
@@ -454,15 +453,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
* @param crypto For drm protected playbacks, a {@link MediaCrypto} to use for decryption.
* @param crypto For drm protected playbacks, a {@link MediaCrypto} to use for decryption.
* @param codecOperatingRate The codec operating rate, or {@link #CODEC_OPERATING_RATE_UNSET} if
* @param codecOperatingRate The codec operating rate, or {@link #CODEC_OPERATING_RATE_UNSET} if
* no codec operating rate should be set.
* no codec operating rate should be set.
* @throws DecoderQueryException If an error occurs querying {@code codecInfo}.
*/
*/
protected
abstract
void
configureCodec
(
protected
abstract
void
configureCodec
(
MediaCodecInfo
codecInfo
,
MediaCodecInfo
codecInfo
,
MediaCodec
codec
,
MediaCodec
codec
,
Format
format
,
Format
format
,
MediaCrypto
crypto
,
MediaCrypto
crypto
,
float
codecOperatingRate
)
float
codecOperatingRate
);
throws
DecoderQueryException
;
protected
final
void
maybeInitCodec
()
throws
ExoPlaybackException
{
protected
final
void
maybeInitCodec
()
throws
ExoPlaybackException
{
if
(
codec
!=
null
||
inputFormat
==
null
)
{
if
(
codec
!=
null
||
inputFormat
==
null
)
{
...
@@ -742,11 +739,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
...
@@ -742,11 +739,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
try
{
try
{
List
<
MediaCodecInfo
>
allAvailableCodecInfos
=
List
<
MediaCodecInfo
>
allAvailableCodecInfos
=
getAvailableCodecInfos
(
mediaCryptoRequiresSecureDecoder
);
getAvailableCodecInfos
(
mediaCryptoRequiresSecureDecoder
);
availableCodecInfos
=
new
ArrayDeque
<>();
if
(
enableDecoderFallback
)
{
if
(
enableDecoderFallback
)
{
availableCodecInfos
=
new
ArrayDeque
<>(
allAvailableCodecInfos
);
availableCodecInfos
.
addAll
(
allAvailableCodecInfos
);
}
else
{
}
else
if
(!
allAvailableCodecInfos
.
isEmpty
())
{
availableCodecInfos
=
availableCodecInfos
.
add
(
allAvailableCodecInfos
.
get
(
0
));
new
ArrayDeque
<>(
Collections
.
singletonList
(
allAvailableCodecInfos
.
get
(
0
)));
}
}
preferredDecoderInitializationException
=
null
;
preferredDecoderInitializationException
=
null
;
}
catch
(
DecoderQueryException
e
)
{
}
catch
(
DecoderQueryException
e
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java
View file @
f6297f4f
...
@@ -176,7 +176,7 @@ public final class MediaCodecUtil {
...
@@ -176,7 +176,7 @@ public final class MediaCodecUtil {
// E-AC3 decoders can decode JOC streams, but in 2-D rather than 3-D.
// E-AC3 decoders can decode JOC streams, but in 2-D rather than 3-D.
CodecKey
eac3Key
=
new
CodecKey
(
MimeTypes
.
AUDIO_E_AC3
,
key
.
secure
,
key
.
tunneling
);
CodecKey
eac3Key
=
new
CodecKey
(
MimeTypes
.
AUDIO_E_AC3
,
key
.
secure
,
key
.
tunneling
);
ArrayList
<
MediaCodecInfo
>
eac3DecoderInfos
=
ArrayList
<
MediaCodecInfo
>
eac3DecoderInfos
=
getDecoderInfosInternal
(
eac3Key
,
mediaCodecList
,
mimeType
);
getDecoderInfosInternal
(
eac3Key
,
mediaCodecList
,
MimeTypes
.
AUDIO_E_AC3
);
decoderInfos
.
addAll
(
eac3DecoderInfos
);
decoderInfos
.
addAll
(
eac3DecoderInfos
);
}
}
applyWorkarounds
(
mimeType
,
decoderInfos
);
applyWorkarounds
(
mimeType
,
decoderInfos
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadHelper.java
View file @
f6297f4f
...
@@ -951,6 +951,7 @@ public final class DownloadHelper {
...
@@ -951,6 +951,7 @@ public final class DownloadHelper {
downloadHelper
.
onMediaPrepared
();
downloadHelper
.
onMediaPrepared
();
return
true
;
return
true
;
case
DOWNLOAD_HELPER_CALLBACK_MESSAGE_FAILED:
case
DOWNLOAD_HELPER_CALLBACK_MESSAGE_FAILED:
release
();
downloadHelper
.
onMediaPreparationFailed
((
IOException
)
Util
.
castNonNull
(
msg
.
obj
));
downloadHelper
.
onMediaPreparationFailed
((
IOException
)
Util
.
castNonNull
(
msg
.
obj
));
return
true
;
return
true
;
default
:
default
:
...
...
library/core/src/main/java/com/google/android/exoplayer2/scheduler/Requirements.java
View file @
f6297f4f
...
@@ -27,6 +27,7 @@ import android.os.Parcel;
...
@@ -27,6 +27,7 @@ import android.os.Parcel;
import
android.os.Parcelable
;
import
android.os.Parcelable
;
import
android.os.PowerManager
;
import
android.os.PowerManager
;
import
androidx.annotation.IntDef
;
import
androidx.annotation.IntDef
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.android.exoplayer2.util.Util
;
import
java.lang.annotation.Documented
;
import
java.lang.annotation.Documented
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.Retention
;
...
@@ -128,7 +129,7 @@ public final class Requirements implements Parcelable {
...
@@ -128,7 +129,7 @@ public final class Requirements implements Parcelable {
ConnectivityManager
connectivityManager
=
ConnectivityManager
connectivityManager
=
(
ConnectivityManager
)
context
.
getSystemService
(
Context
.
CONNECTIVITY_SERVICE
);
(
ConnectivityManager
)
context
.
getSystemService
(
Context
.
CONNECTIVITY_SERVICE
);
NetworkInfo
networkInfo
=
connectivityManager
.
getActiveNetworkInfo
();
NetworkInfo
networkInfo
=
Assertions
.
checkNotNull
(
connectivityManager
)
.
getActiveNetworkInfo
();
if
(
networkInfo
==
null
if
(
networkInfo
==
null
||
!
networkInfo
.
isConnected
()
||
!
networkInfo
.
isConnected
()
||
!
isInternetConnectivityValidated
(
connectivityManager
))
{
||
!
isInternetConnectivityValidated
(
connectivityManager
))
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/scheduler/RequirementsWatcher.java
View file @
f6297f4f
...
@@ -28,6 +28,7 @@ import android.os.Handler;
...
@@ -28,6 +28,7 @@ import android.os.Handler;
import
android.os.Looper
;
import
android.os.Looper
;
import
android.os.PowerManager
;
import
android.os.PowerManager
;
import
androidx.annotation.RequiresApi
;
import
androidx.annotation.RequiresApi
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.android.exoplayer2.util.Util
;
/**
/**
...
@@ -126,7 +127,8 @@ public final class RequirementsWatcher {
...
@@ -126,7 +127,8 @@ public final class RequirementsWatcher {
@TargetApi
(
23
)
@TargetApi
(
23
)
private
void
registerNetworkCallbackV23
()
{
private
void
registerNetworkCallbackV23
()
{
ConnectivityManager
connectivityManager
=
ConnectivityManager
connectivityManager
=
(
ConnectivityManager
)
context
.
getSystemService
(
Context
.
CONNECTIVITY_SERVICE
);
Assertions
.
checkNotNull
(
(
ConnectivityManager
)
context
.
getSystemService
(
Context
.
CONNECTIVITY_SERVICE
));
NetworkRequest
request
=
NetworkRequest
request
=
new
NetworkRequest
.
Builder
()
new
NetworkRequest
.
Builder
()
.
addCapability
(
NetworkCapabilities
.
NET_CAPABILITY_VALIDATED
)
.
addCapability
(
NetworkCapabilities
.
NET_CAPABILITY_VALIDATED
)
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/SilenceMediaSource.java
0 → 100644
View file @
f6297f4f
/*
* Copyright (C) 2019 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
;
import
androidx.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.FormatHolder
;
import
com.google.android.exoplayer2.SeekParameters
;
import
com.google.android.exoplayer2.decoder.DecoderInputBuffer
;
import
com.google.android.exoplayer2.trackselection.TrackSelection
;
import
com.google.android.exoplayer2.upstream.Allocator
;
import
com.google.android.exoplayer2.upstream.TransferListener
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.Util
;
import
java.util.ArrayList
;
import
org.checkerframework.checker.nullness.compatqual.NullableType
;
/** Media source with a single period consisting of silent raw audio of a given duration. */
public
final
class
SilenceMediaSource
extends
BaseMediaSource
{
private
static
final
int
SAMPLE_RATE_HZ
=
44100
;
@C
.
PcmEncoding
private
static
final
int
ENCODING
=
C
.
ENCODING_PCM_16BIT
;
private
static
final
int
CHANNEL_COUNT
=
2
;
private
static
final
Format
FORMAT
=
Format
.
createAudioSampleFormat
(
/* id=*/
null
,
MimeTypes
.
AUDIO_RAW
,
/* codecs= */
null
,
/* bitrate= */
Format
.
NO_VALUE
,
/* maxInputSize= */
Format
.
NO_VALUE
,
CHANNEL_COUNT
,
SAMPLE_RATE_HZ
,
ENCODING
,
/* initializationData= */
null
,
/* drmInitData= */
null
,
/* selectionFlags= */
0
,
/* language= */
null
);
private
static
final
byte
[]
SILENCE_SAMPLE
=
new
byte
[
Util
.
getPcmFrameSize
(
ENCODING
,
CHANNEL_COUNT
)
*
1024
];
private
final
long
durationUs
;
/**
* Creates a new media source providing silent audio of the given duration.
*
* @param durationUs The duration of silent audio to output, in microseconds.
*/
public
SilenceMediaSource
(
long
durationUs
)
{
Assertions
.
checkArgument
(
durationUs
>=
0
);
this
.
durationUs
=
durationUs
;
}
@Override
public
void
prepareSourceInternal
(
@Nullable
TransferListener
mediaTransferListener
)
{
refreshSourceInfo
(
new
SinglePeriodTimeline
(
durationUs
,
/* isSeekable= */
true
,
/* isDynamic= */
false
),
/* manifest= */
null
);
}
@Override
public
void
maybeThrowSourceInfoRefreshError
()
{}
@Override
public
MediaPeriod
createPeriod
(
MediaPeriodId
id
,
Allocator
allocator
,
long
startPositionUs
)
{
return
new
SilenceMediaPeriod
(
durationUs
);
}
@Override
public
void
releasePeriod
(
MediaPeriod
mediaPeriod
)
{}
@Override
public
void
releaseSourceInternal
()
{}
private
static
final
class
SilenceMediaPeriod
implements
MediaPeriod
{
private
static
final
TrackGroupArray
TRACKS
=
new
TrackGroupArray
(
new
TrackGroup
(
FORMAT
));
private
final
long
durationUs
;
private
final
ArrayList
<
SampleStream
>
sampleStreams
;
public
SilenceMediaPeriod
(
long
durationUs
)
{
this
.
durationUs
=
durationUs
;
sampleStreams
=
new
ArrayList
<>();
}
@Override
public
void
prepare
(
Callback
callback
,
long
positionUs
)
{
callback
.
onPrepared
(
/* mediaPeriod= */
this
);
}
@Override
public
void
maybeThrowPrepareError
()
{}
@Override
public
TrackGroupArray
getTrackGroups
()
{
return
TRACKS
;
}
@Override
public
long
selectTracks
(
@NullableType
TrackSelection
[]
selections
,
boolean
[]
mayRetainStreamFlags
,
@NullableType
SampleStream
[]
streams
,
boolean
[]
streamResetFlags
,
long
positionUs
)
{
for
(
int
i
=
0
;
i
<
selections
.
length
;
i
++)
{
if
(
streams
[
i
]
!=
null
&&
(
selections
[
i
]
==
null
||
!
mayRetainStreamFlags
[
i
]))
{
sampleStreams
.
remove
(
streams
[
i
]);
streams
[
i
]
=
null
;
}
if
(
streams
[
i
]
==
null
&&
selections
[
i
]
!=
null
)
{
SilenceSampleStream
stream
=
new
SilenceSampleStream
(
durationUs
);
stream
.
seekTo
(
positionUs
);
sampleStreams
.
add
(
stream
);
streams
[
i
]
=
stream
;
streamResetFlags
[
i
]
=
true
;
}
}
return
positionUs
;
}
@Override
public
void
discardBuffer
(
long
positionUs
,
boolean
toKeyframe
)
{}
@Override
public
long
readDiscontinuity
()
{
return
C
.
TIME_UNSET
;
}
@Override
public
long
seekToUs
(
long
positionUs
)
{
for
(
int
i
=
0
;
i
<
sampleStreams
.
size
();
i
++)
{
((
SilenceSampleStream
)
sampleStreams
.
get
(
i
)).
seekTo
(
positionUs
);
}
return
positionUs
;
}
@Override
public
long
getAdjustedSeekPositionUs
(
long
positionUs
,
SeekParameters
seekParameters
)
{
return
positionUs
;
}
@Override
public
long
getBufferedPositionUs
()
{
return
C
.
TIME_END_OF_SOURCE
;
}
@Override
public
long
getNextLoadPositionUs
()
{
return
C
.
TIME_END_OF_SOURCE
;
}
@Override
public
boolean
continueLoading
(
long
positionUs
)
{
return
false
;
}
@Override
public
void
reevaluateBuffer
(
long
positionUs
)
{}
}
private
static
final
class
SilenceSampleStream
implements
SampleStream
{
private
final
long
durationBytes
;
private
boolean
sentFormat
;
private
long
positionBytes
;
public
SilenceSampleStream
(
long
durationUs
)
{
durationBytes
=
getAudioByteCount
(
durationUs
);
seekTo
(
0
);
}
public
void
seekTo
(
long
positionUs
)
{
positionBytes
=
getAudioByteCount
(
positionUs
);
}
@Override
public
boolean
isReady
()
{
return
true
;
}
@Override
public
void
maybeThrowError
()
{}
@Override
public
int
readData
(
FormatHolder
formatHolder
,
DecoderInputBuffer
buffer
,
boolean
formatRequired
)
{
if
(!
sentFormat
||
formatRequired
)
{
formatHolder
.
format
=
FORMAT
;
sentFormat
=
true
;
return
C
.
RESULT_FORMAT_READ
;
}
long
bytesRemaining
=
durationBytes
-
positionBytes
;
if
(
bytesRemaining
==
0
)
{
buffer
.
addFlag
(
C
.
BUFFER_FLAG_END_OF_STREAM
);
return
C
.
RESULT_BUFFER_READ
;
}
int
bytesToWrite
=
(
int
)
Math
.
min
(
SILENCE_SAMPLE
.
length
,
bytesRemaining
);
buffer
.
ensureSpaceForWrite
(
bytesToWrite
);
buffer
.
addFlag
(
C
.
BUFFER_FLAG_KEY_FRAME
);
buffer
.
data
.
put
(
SILENCE_SAMPLE
,
/* offset= */
0
,
bytesToWrite
);
buffer
.
timeUs
=
getAudioPositionUs
(
positionBytes
);
positionBytes
+=
bytesToWrite
;
return
C
.
RESULT_BUFFER_READ
;
}
@Override
public
int
skipData
(
long
positionUs
)
{
long
oldPositionBytes
=
positionBytes
;
seekTo
(
positionUs
);
return
(
int
)
((
positionBytes
-
oldPositionBytes
)
/
SILENCE_SAMPLE
.
length
);
}
}
private
static
long
getAudioByteCount
(
long
durationUs
)
{
long
audioSampleCount
=
durationUs
*
SAMPLE_RATE_HZ
/
C
.
MICROS_PER_SECOND
;
return
Util
.
getPcmFrameSize
(
ENCODING
,
CHANNEL_COUNT
)
*
audioSampleCount
;
}
private
static
long
getAudioPositionUs
(
long
bytes
)
{
long
audioSampleCount
=
bytes
/
Util
.
getPcmFrameSize
(
ENCODING
,
CHANNEL_COUNT
);
return
audioSampleCount
*
C
.
MICROS_PER_SECOND
/
SAMPLE_RATE_HZ
;
}
}
library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java
View file @
f6297f4f
...
@@ -80,6 +80,11 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -80,6 +80,11 @@ public final class Cea608Decoder extends CeaDecoder {
* at which point the non-displayed memory becomes the displayed memory (and vice versa).
* at which point the non-displayed memory becomes the displayed memory (and vice versa).
*/
*/
private
static
final
byte
CTRL_RESUME_CAPTION_LOADING
=
0x20
;
private
static
final
byte
CTRL_RESUME_CAPTION_LOADING
=
0x20
;
private
static
final
byte
CTRL_BACKSPACE
=
0x21
;
private
static
final
byte
CTRL_DELETE_TO_END_OF_ROW
=
0x24
;
/**
/**
* Command initiating roll-up style captioning, with the maximum of 2 rows displayed
* Command initiating roll-up style captioning, with the maximum of 2 rows displayed
* simultaneously.
* simultaneously.
...
@@ -95,25 +100,31 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -95,25 +100,31 @@ public final class Cea608Decoder extends CeaDecoder {
* simultaneously.
* simultaneously.
*/
*/
private
static
final
byte
CTRL_ROLL_UP_CAPTIONS_4_ROWS
=
0x27
;
private
static
final
byte
CTRL_ROLL_UP_CAPTIONS_4_ROWS
=
0x27
;
/**
/**
* Command initiating paint-on style captioning. Subsequent data should be addressed immediately
* Command initiating paint-on style captioning. Subsequent data should be addressed immediately
* to displayed memory without need for the {@link #CTRL_RESUME_CAPTION_LOADING} command.
* to displayed memory without need for the {@link #CTRL_RESUME_CAPTION_LOADING} command.
*/
*/
private
static
final
byte
CTRL_RESUME_DIRECT_CAPTIONING
=
0x29
;
private
static
final
byte
CTRL_RESUME_DIRECT_CAPTIONING
=
0x29
;
/**
/**
* Command indicating the end of a pop-on style caption. At this point the caption loaded in
* TEXT commands are switching to TEXT service. All consecutive incoming data must be filtered out
* non-displayed memory should be swapped with the one in displayed memory. If no
* until a command is received that switches back to the CAPTION service.
* {@link #CTRL_RESUME_CAPTION_LOADING} command has been received, this command forces the
* receiver into pop-on style.
*/
*/
private
static
final
byte
CTRL_END_OF_CAPTION
=
0x2F
;
private
static
final
byte
CTRL_TEXT_RESTART
=
0x2A
;
private
static
final
byte
CTRL_RESUME_TEXT_DISPLAY
=
0x2B
;
private
static
final
byte
CTRL_ERASE_DISPLAYED_MEMORY
=
0x2C
;
private
static
final
byte
CTRL_ERASE_DISPLAYED_MEMORY
=
0x2C
;
private
static
final
byte
CTRL_CARRIAGE_RETURN
=
0x2D
;
private
static
final
byte
CTRL_CARRIAGE_RETURN
=
0x2D
;
private
static
final
byte
CTRL_ERASE_NON_DISPLAYED_MEMORY
=
0x2E
;
private
static
final
byte
CTRL_ERASE_NON_DISPLAYED_MEMORY
=
0x2E
;
private
static
final
byte
CTRL_DELETE_TO_END_OF_ROW
=
0x24
;
private
static
final
byte
CTRL_BACKSPACE
=
0x21
;
/**
* Command indicating the end of a pop-on style caption. At this point the caption loaded in
* non-displayed memory should be swapped with the one in displayed memory. If no {@link
* #CTRL_RESUME_CAPTION_LOADING} command has been received, this command forces the receiver into
* pop-on style.
*/
private
static
final
byte
CTRL_END_OF_CAPTION
=
0x2F
;
// Basic North American 608 CC char set, mostly ASCII. Indexed by (char-0x20).
// Basic North American 608 CC char set, mostly ASCII. Indexed by (char-0x20).
private
static
final
int
[]
BASIC_CHARACTER_SET
=
new
int
[]
{
private
static
final
int
[]
BASIC_CHARACTER_SET
=
new
int
[]
{
...
@@ -237,6 +248,11 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -237,6 +248,11 @@ public final class Cea608Decoder extends CeaDecoder {
private
byte
repeatableControlCc2
;
private
byte
repeatableControlCc2
;
private
int
currentChannel
;
private
int
currentChannel
;
// The incoming characters may belong to 3 different services based on the last received control
// codes. The 3 services are Captioning, Text and XDS. The decoder only processes Captioning
// service bytes and drops the rest.
private
boolean
isInCaptionService
;
public
Cea608Decoder
(
String
mimeType
,
int
accessibilityChannel
)
{
public
Cea608Decoder
(
String
mimeType
,
int
accessibilityChannel
)
{
ccData
=
new
ParsableByteArray
();
ccData
=
new
ParsableByteArray
();
cueBuilders
=
new
ArrayList
<>();
cueBuilders
=
new
ArrayList
<>();
...
@@ -268,6 +284,7 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -268,6 +284,7 @@ public final class Cea608Decoder extends CeaDecoder {
setCaptionMode
(
CC_MODE_UNKNOWN
);
setCaptionMode
(
CC_MODE_UNKNOWN
);
resetCueBuilders
();
resetCueBuilders
();
isInCaptionService
=
true
;
}
}
@Override
@Override
...
@@ -288,6 +305,7 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -288,6 +305,7 @@ public final class Cea608Decoder extends CeaDecoder {
repeatableControlCc1
=
0
;
repeatableControlCc1
=
0
;
repeatableControlCc2
=
0
;
repeatableControlCc2
=
0
;
currentChannel
=
NTSC_CC_CHANNEL_1
;
currentChannel
=
NTSC_CC_CHANNEL_1
;
isInCaptionService
=
true
;
}
}
@Override
@Override
...
@@ -363,6 +381,12 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -363,6 +381,12 @@ public final class Cea608Decoder extends CeaDecoder {
continue
;
continue
;
}
}
maybeUpdateIsInCaptionService
(
ccData1
,
ccData2
);
if
(!
isInCaptionService
)
{
// Only the Captioning service is supported. Drop all other bytes.
continue
;
}
// Special North American character set.
// Special North American character set.
// ccData1 - 0|0|0|1|C|0|0|1
// ccData1 - 0|0|0|1|C|0|0|1
// ccData2 - 0|0|1|1|X|X|X|X
// ccData2 - 0|0|1|1|X|X|X|X
...
@@ -629,6 +653,29 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -629,6 +653,29 @@ public final class Cea608Decoder extends CeaDecoder {
cueBuilders
.
add
(
currentCueBuilder
);
cueBuilders
.
add
(
currentCueBuilder
);
}
}
private
void
maybeUpdateIsInCaptionService
(
byte
cc1
,
byte
cc2
)
{
if
(
isXdsControlCode
(
cc1
))
{
isInCaptionService
=
false
;
}
else
if
(
isServiceSwitchCommand
(
cc1
))
{
switch
(
cc2
)
{
case
CTRL_TEXT_RESTART:
case
CTRL_RESUME_TEXT_DISPLAY:
isInCaptionService
=
false
;
break
;
case
CTRL_END_OF_CAPTION:
case
CTRL_RESUME_CAPTION_LOADING:
case
CTRL_RESUME_DIRECT_CAPTIONING:
case
CTRL_ROLL_UP_CAPTIONS_2_ROWS:
case
CTRL_ROLL_UP_CAPTIONS_3_ROWS:
case
CTRL_ROLL_UP_CAPTIONS_4_ROWS:
isInCaptionService
=
true
;
break
;
default
:
// No update.
}
}
}
private
static
char
getChar
(
byte
ccData
)
{
private
static
char
getChar
(
byte
ccData
)
{
int
index
=
(
ccData
&
0x7F
)
-
0x20
;
int
index
=
(
ccData
&
0x7F
)
-
0x20
;
return
(
char
)
BASIC_CHARACTER_SET
[
index
];
return
(
char
)
BASIC_CHARACTER_SET
[
index
];
...
@@ -683,6 +730,15 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -683,6 +730,15 @@ public final class Cea608Decoder extends CeaDecoder {
return
(
cc1
&
0xF0
)
==
0x10
;
return
(
cc1
&
0xF0
)
==
0x10
;
}
}
private
static
boolean
isXdsControlCode
(
byte
cc1
)
{
return
0x01
<=
cc1
&&
cc1
<=
0x0F
;
}
private
static
boolean
isServiceSwitchCommand
(
byte
cc1
)
{
// cc1 - 0|0|0|1|C|1|0|0
return
(
cc1
&
0xF7
)
==
0x14
;
}
private
static
class
CueBuilder
{
private
static
class
CueBuilder
{
// 608 captions define a 15 row by 32 column screen grid. These constants convert from 608
// 608 captions define a 15 row by 32 column screen grid. These constants convert from 608
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java
View file @
f6297f4f
...
@@ -429,6 +429,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
...
@@ -429,6 +429,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
/* lineType= */
Cue
.
LINE_TYPE_FRACTION
,
/* lineType= */
Cue
.
LINE_TYPE_FRACTION
,
lineAnchor
,
lineAnchor
,
width
,
width
,
height
,
/* textSizeType= */
Cue
.
TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING
,
/* textSizeType= */
Cue
.
TEXT_SIZE_TYPE_FRACTIONAL_IGNORE_PADDING
,
/* textSize= */
regionTextHeight
);
/* textSize= */
regionTextHeight
);
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlNode.java
View file @
f6297f4f
...
@@ -231,11 +231,11 @@ import java.util.TreeSet;
...
@@ -231,11 +231,11 @@ import java.util.TreeSet;
new
Cue
(
new
Cue
(
bitmap
,
bitmap
,
region
.
position
,
region
.
position
,
Cue
.
ANCHOR_TYPE_
MIDDLE
,
Cue
.
ANCHOR_TYPE_
START
,
region
.
line
,
region
.
line
,
region
.
lineAnchor
,
region
.
lineAnchor
,
region
.
width
,
region
.
width
,
/* height= */
Cue
.
DIMEN_UNSET
));
region
.
height
));
}
}
// Create text based cues.
// Create text based cues.
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRegion.java
View file @
f6297f4f
...
@@ -28,6 +28,7 @@ import com.google.android.exoplayer2.text.Cue;
...
@@ -28,6 +28,7 @@ import com.google.android.exoplayer2.text.Cue;
public
final
@Cue
.
LineType
int
lineType
;
public
final
@Cue
.
LineType
int
lineType
;
public
final
@Cue
.
AnchorType
int
lineAnchor
;
public
final
@Cue
.
AnchorType
int
lineAnchor
;
public
final
float
width
;
public
final
float
width
;
public
final
float
height
;
public
final
@Cue
.
TextSizeType
int
textSizeType
;
public
final
@Cue
.
TextSizeType
int
textSizeType
;
public
final
float
textSize
;
public
final
float
textSize
;
...
@@ -39,6 +40,7 @@ import com.google.android.exoplayer2.text.Cue;
...
@@ -39,6 +40,7 @@ import com.google.android.exoplayer2.text.Cue;
/* lineType= */
Cue
.
TYPE_UNSET
,
/* lineType= */
Cue
.
TYPE_UNSET
,
/* lineAnchor= */
Cue
.
TYPE_UNSET
,
/* lineAnchor= */
Cue
.
TYPE_UNSET
,
/* width= */
Cue
.
DIMEN_UNSET
,
/* width= */
Cue
.
DIMEN_UNSET
,
/* height= */
Cue
.
DIMEN_UNSET
,
/* textSizeType= */
Cue
.
TYPE_UNSET
,
/* textSizeType= */
Cue
.
TYPE_UNSET
,
/* textSize= */
Cue
.
DIMEN_UNSET
);
/* textSize= */
Cue
.
DIMEN_UNSET
);
}
}
...
@@ -50,6 +52,7 @@ import com.google.android.exoplayer2.text.Cue;
...
@@ -50,6 +52,7 @@ import com.google.android.exoplayer2.text.Cue;
@Cue
.
LineType
int
lineType
,
@Cue
.
LineType
int
lineType
,
@Cue
.
AnchorType
int
lineAnchor
,
@Cue
.
AnchorType
int
lineAnchor
,
float
width
,
float
width
,
float
height
,
int
textSizeType
,
int
textSizeType
,
float
textSize
)
{
float
textSize
)
{
this
.
id
=
id
;
this
.
id
=
id
;
...
@@ -58,6 +61,7 @@ import com.google.android.exoplayer2.text.Cue;
...
@@ -58,6 +61,7 @@ import com.google.android.exoplayer2.text.Cue;
this
.
lineType
=
lineType
;
this
.
lineType
=
lineType
;
this
.
lineAnchor
=
lineAnchor
;
this
.
lineAnchor
=
lineAnchor
;
this
.
width
=
width
;
this
.
width
=
width
;
this
.
height
=
height
;
this
.
textSizeType
=
textSizeType
;
this
.
textSizeType
=
textSizeType
;
this
.
textSize
=
textSize
;
this
.
textSize
=
textSize
;
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/CssParser.java
View file @
f6297f4f
...
@@ -20,6 +20,8 @@ import com.google.android.exoplayer2.util.ColorParser;
...
@@ -20,6 +20,8 @@ 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
com.google.android.exoplayer2.util.Util
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.regex.Matcher
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
java.util.regex.Pattern
;
...
@@ -35,8 +37,8 @@ import java.util.regex.Pattern;
...
@@ -35,8 +37,8 @@ import java.util.regex.Pattern;
private
static
final
String
PROPERTY_TEXT_DECORATION
=
"text-decoration"
;
private
static
final
String
PROPERTY_TEXT_DECORATION
=
"text-decoration"
;
private
static
final
String
VALUE_BOLD
=
"bold"
;
private
static
final
String
VALUE_BOLD
=
"bold"
;
private
static
final
String
VALUE_UNDERLINE
=
"underline"
;
private
static
final
String
VALUE_UNDERLINE
=
"underline"
;
private
static
final
String
BLOCK
_START
=
"{"
;
private
static
final
String
RULE
_START
=
"{"
;
private
static
final
String
BLOCK
_END
=
"}"
;
private
static
final
String
RULE
_END
=
"}"
;
private
static
final
String
PROPERTY_FONT_STYLE
=
"font-style"
;
private
static
final
String
PROPERTY_FONT_STYLE
=
"font-style"
;
private
static
final
String
VALUE_ITALIC
=
"italic"
;
private
static
final
String
VALUE_ITALIC
=
"italic"
;
...
@@ -52,37 +54,47 @@ import java.util.regex.Pattern;
...
@@ -52,37 +54,47 @@ import java.util.regex.Pattern;
}
}
/**
/**
* Takes a CSS style block and consumes up to the first empty line found. Attempts to parse the
* Takes a CSS style block and consumes up to the first empty line. Attempts to parse the contents
* contents of the style block and returns a {@link WebvttCssStyle} instance if successful, or
* of the style block and returns a list of {@link WebvttCssStyle} instances if successful. If
* {@code null} otherwise.
* parsing fails, it returns a list including only the styles which have been successfully parsed
* up to the style rule which was malformed.
*
*
* @param input The input from which the style block should be read.
* @param input The input from which the style block should be read.
* @return A {@link WebvttCssStyle} that represents the parsed block.
* @return A list of {@link WebvttCssStyle}s that represents the parsed block, or a list
* containing the styles up to the parsing failure.
*/
*/
public
WebvttCssStyle
parseBlock
(
ParsableByteArray
input
)
{
public
List
<
WebvttCssStyle
>
parseBlock
(
ParsableByteArray
input
)
{
stringBuilder
.
setLength
(
0
);
stringBuilder
.
setLength
(
0
);
int
initialInputPosition
=
input
.
getPosition
();
int
initialInputPosition
=
input
.
getPosition
();
skipStyleBlock
(
input
);
skipStyleBlock
(
input
);
styleInput
.
reset
(
input
.
data
,
input
.
getPosition
());
styleInput
.
reset
(
input
.
data
,
input
.
getPosition
());
styleInput
.
setPosition
(
initialInputPosition
);
styleInput
.
setPosition
(
initialInputPosition
);
String
selector
=
parseSelector
(
styleInput
,
stringBuilder
);
if
(
selector
==
null
||
!
BLOCK_START
.
equals
(
parseNextToken
(
styleInput
,
stringBuilder
)))
{
List
<
WebvttCssStyle
>
styles
=
new
ArrayList
<>();
return
null
;
String
selector
;
}
while
((
selector
=
parseSelector
(
styleInput
,
stringBuilder
))
!=
null
)
{
WebvttCssStyle
style
=
new
WebvttCssStyle
();
if
(!
RULE_START
.
equals
(
parseNextToken
(
styleInput
,
stringBuilder
)))
{
applySelectorToStyle
(
style
,
selector
);
return
styles
;
String
token
=
null
;
}
boolean
blockEndFound
=
false
;
WebvttCssStyle
style
=
new
WebvttCssStyle
();
while
(!
blockEndFound
)
{
applySelectorToStyle
(
style
,
selector
);
int
position
=
styleInput
.
getPosition
();
String
token
=
null
;
token
=
parseNextToken
(
styleInput
,
stringBuilder
);
boolean
blockEndFound
=
false
;
blockEndFound
=
token
==
null
||
BLOCK_END
.
equals
(
token
);
while
(!
blockEndFound
)
{
if
(!
blockEndFound
)
{
int
position
=
styleInput
.
getPosition
();
styleInput
.
setPosition
(
position
);
token
=
parseNextToken
(
styleInput
,
stringBuilder
);
parseStyleDeclaration
(
styleInput
,
style
,
stringBuilder
);
blockEndFound
=
token
==
null
||
RULE_END
.
equals
(
token
);
if
(!
blockEndFound
)
{
styleInput
.
setPosition
(
position
);
parseStyleDeclaration
(
styleInput
,
style
,
stringBuilder
);
}
}
// Check that the style rule ended correctly.
if
(
RULE_END
.
equals
(
token
))
{
styles
.
add
(
style
);
}
}
}
}
return
BLOCK_END
.
equals
(
token
)
?
style
:
null
;
// Check that the style block ended correctly.
return
styles
;
}
}
/**
/**
...
@@ -107,7 +119,7 @@ import java.util.regex.Pattern;
...
@@ -107,7 +119,7 @@ import java.util.regex.Pattern;
if
(
token
==
null
)
{
if
(
token
==
null
)
{
return
null
;
return
null
;
}
}
if
(
BLOCK
_START
.
equals
(
token
))
{
if
(
RULE
_START
.
equals
(
token
))
{
input
.
setPosition
(
position
);
input
.
setPosition
(
position
);
return
""
;
return
""
;
}
}
...
@@ -156,7 +168,7 @@ import java.util.regex.Pattern;
...
@@ -156,7 +168,7 @@ import java.util.regex.Pattern;
String
token
=
parseNextToken
(
input
,
stringBuilder
);
String
token
=
parseNextToken
(
input
,
stringBuilder
);
if
(
";"
.
equals
(
token
))
{
if
(
";"
.
equals
(
token
))
{
// The style declaration is well formed.
// The style declaration is well formed.
}
else
if
(
BLOCK
_END
.
equals
(
token
))
{
}
else
if
(
RULE
_END
.
equals
(
token
))
{
// The style declaration is well formed and we can go on, but the closing bracket had to be
// The style declaration is well formed and we can go on, but the closing bracket had to be
// fed back.
// fed back.
input
.
setPosition
(
position
);
input
.
setPosition
(
position
);
...
@@ -250,7 +262,7 @@ import java.util.regex.Pattern;
...
@@ -250,7 +262,7 @@ import java.util.regex.Pattern;
// Syntax error.
// Syntax error.
return
null
;
return
null
;
}
}
if
(
BLOCK
_END
.
equals
(
token
)
||
";"
.
equals
(
token
))
{
if
(
RULE
_END
.
equals
(
token
)
||
";"
.
equals
(
token
))
{
input
.
setPosition
(
position
);
input
.
setPosition
(
position
);
expressionEndFound
=
true
;
expressionEndFound
=
true
;
}
else
{
}
else
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttDecoder.java
View file @
f6297f4f
...
@@ -80,10 +80,7 @@ public final class WebvttDecoder extends SimpleSubtitleDecoder {
...
@@ -80,10 +80,7 @@ public final class WebvttDecoder extends SimpleSubtitleDecoder {
throw
new
SubtitleDecoderException
(
"A style block was found after the first cue."
);
throw
new
SubtitleDecoderException
(
"A style block was found after the first cue."
);
}
}
parsableWebvttData
.
readLine
();
// Consume the "STYLE" header.
parsableWebvttData
.
readLine
();
// Consume the "STYLE" header.
WebvttCssStyle
styleBlock
=
cssParser
.
parseBlock
(
parsableWebvttData
);
definedStyles
.
addAll
(
cssParser
.
parseBlock
(
parsableWebvttData
));
if
(
styleBlock
!=
null
)
{
definedStyles
.
add
(
styleBlock
);
}
}
else
if
(
event
==
EVENT_CUE
)
{
}
else
if
(
event
==
EVENT_CUE
)
{
if
(
cueParser
.
parseCue
(
parsableWebvttData
,
webvttCueBuilder
,
definedStyles
))
{
if
(
cueParser
.
parseCue
(
parsableWebvttData
,
webvttCueBuilder
,
definedStyles
))
{
subtitles
.
add
(
webvttCueBuilder
.
build
());
subtitles
.
add
(
webvttCueBuilder
.
build
());
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/AdaptiveTrackSelection.java
View file @
f6297f4f
...
@@ -757,7 +757,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
...
@@ -757,7 +757,7 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
for
(
int
i
=
0
;
i
<
values
.
length
;
i
++)
{
for
(
int
i
=
0
;
i
<
values
.
length
;
i
++)
{
logValues
[
i
]
=
new
double
[
values
[
i
].
length
];
logValues
[
i
]
=
new
double
[
values
[
i
].
length
];
for
(
int
j
=
0
;
j
<
values
[
i
].
length
;
j
++)
{
for
(
int
j
=
0
;
j
<
values
[
i
].
length
;
j
++)
{
logValues
[
i
][
j
]
=
Math
.
log
(
values
[
i
][
j
]);
logValues
[
i
][
j
]
=
values
[
i
][
j
]
==
Format
.
NO_VALUE
?
0
:
Math
.
log
(
values
[
i
][
j
]);
}
}
}
}
return
logValues
;
return
logValues
;
...
@@ -779,7 +779,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
...
@@ -779,7 +779,8 @@ public class AdaptiveTrackSelection extends BaseTrackSelection {
double
totalBitrateDiff
=
logBitrates
[
i
][
logBitrates
[
i
].
length
-
1
]
-
logBitrates
[
i
][
0
];
double
totalBitrateDiff
=
logBitrates
[
i
][
logBitrates
[
i
].
length
-
1
]
-
logBitrates
[
i
][
0
];
for
(
int
j
=
0
;
j
<
logBitrates
[
i
].
length
-
1
;
j
++)
{
for
(
int
j
=
0
;
j
<
logBitrates
[
i
].
length
-
1
;
j
++)
{
double
switchBitrate
=
0.5
*
(
logBitrates
[
i
][
j
]
+
logBitrates
[
i
][
j
+
1
]);
double
switchBitrate
=
0.5
*
(
logBitrates
[
i
][
j
]
+
logBitrates
[
i
][
j
+
1
]);
switchPoints
[
i
][
j
]
=
(
switchBitrate
-
logBitrates
[
i
][
0
])
/
totalBitrateDiff
;
switchPoints
[
i
][
j
]
=
totalBitrateDiff
==
0.0
?
1.0
:
(
switchBitrate
-
logBitrates
[
i
][
0
])
/
totalBitrateDiff
;
}
}
}
}
return
switchPoints
;
return
switchPoints
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java
View file @
f6297f4f
...
@@ -1934,6 +1934,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1934,6 +1934,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
getAdaptiveAudioTracks
(
getAdaptiveAudioTracks
(
selectedGroup
,
selectedGroup
,
formatSupports
[
selectedGroupIndex
],
formatSupports
[
selectedGroupIndex
],
params
.
maxAudioBitrate
,
params
.
allowAudioMixedMimeTypeAdaptiveness
,
params
.
allowAudioMixedMimeTypeAdaptiveness
,
params
.
allowAudioMixedSampleRateAdaptiveness
);
params
.
allowAudioMixedSampleRateAdaptiveness
);
if
(
adaptiveTracks
.
length
>
0
)
{
if
(
adaptiveTracks
.
length
>
0
)
{
...
@@ -1951,6 +1952,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1951,6 +1952,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private
static
int
[]
getAdaptiveAudioTracks
(
private
static
int
[]
getAdaptiveAudioTracks
(
TrackGroup
group
,
TrackGroup
group
,
int
[]
formatSupport
,
int
[]
formatSupport
,
int
maxAudioBitrate
,
boolean
allowMixedMimeTypeAdaptiveness
,
boolean
allowMixedMimeTypeAdaptiveness
,
boolean
allowMixedSampleRateAdaptiveness
)
{
boolean
allowMixedSampleRateAdaptiveness
)
{
int
selectedConfigurationTrackCount
=
0
;
int
selectedConfigurationTrackCount
=
0
;
...
@@ -1967,6 +1969,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1967,6 +1969,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
group
,
group
,
formatSupport
,
formatSupport
,
configuration
,
configuration
,
maxAudioBitrate
,
allowMixedMimeTypeAdaptiveness
,
allowMixedMimeTypeAdaptiveness
,
allowMixedSampleRateAdaptiveness
);
allowMixedSampleRateAdaptiveness
);
if
(
configurationCount
>
selectedConfigurationTrackCount
)
{
if
(
configurationCount
>
selectedConfigurationTrackCount
)
{
...
@@ -1977,13 +1980,16 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1977,13 +1980,16 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
}
if
(
selectedConfigurationTrackCount
>
1
)
{
if
(
selectedConfigurationTrackCount
>
1
)
{
Assertions
.
checkNotNull
(
selectedConfiguration
);
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
++)
{
Format
format
=
group
.
getFormat
(
i
);
if
(
isSupportedAdaptiveAudioTrack
(
if
(
isSupportedAdaptiveAudioTrack
(
group
.
getFormat
(
i
)
,
format
,
formatSupport
[
i
],
formatSupport
[
i
],
Assertions
.
checkNotNull
(
selectedConfiguration
),
selectedConfiguration
,
maxAudioBitrate
,
allowMixedMimeTypeAdaptiveness
,
allowMixedMimeTypeAdaptiveness
,
allowMixedSampleRateAdaptiveness
))
{
allowMixedSampleRateAdaptiveness
))
{
adaptiveIndices
[
index
++]
=
i
;
adaptiveIndices
[
index
++]
=
i
;
...
@@ -1998,6 +2004,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -1998,6 +2004,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
TrackGroup
group
,
TrackGroup
group
,
int
[]
formatSupport
,
int
[]
formatSupport
,
AudioConfigurationTuple
configuration
,
AudioConfigurationTuple
configuration
,
int
maxAudioBitrate
,
boolean
allowMixedMimeTypeAdaptiveness
,
boolean
allowMixedMimeTypeAdaptiveness
,
boolean
allowMixedSampleRateAdaptiveness
)
{
boolean
allowMixedSampleRateAdaptiveness
)
{
int
count
=
0
;
int
count
=
0
;
...
@@ -2006,6 +2013,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -2006,6 +2013,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
group
.
getFormat
(
i
),
group
.
getFormat
(
i
),
formatSupport
[
i
],
formatSupport
[
i
],
configuration
,
configuration
,
maxAudioBitrate
,
allowMixedMimeTypeAdaptiveness
,
allowMixedMimeTypeAdaptiveness
,
allowMixedSampleRateAdaptiveness
))
{
allowMixedSampleRateAdaptiveness
))
{
count
++;
count
++;
...
@@ -2018,9 +2026,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -2018,9 +2026,11 @@ public class DefaultTrackSelector extends MappingTrackSelector {
Format
format
,
Format
format
,
int
formatSupport
,
int
formatSupport
,
AudioConfigurationTuple
configuration
,
AudioConfigurationTuple
configuration
,
int
maxAudioBitrate
,
boolean
allowMixedMimeTypeAdaptiveness
,
boolean
allowMixedMimeTypeAdaptiveness
,
boolean
allowMixedSampleRateAdaptiveness
)
{
boolean
allowMixedSampleRateAdaptiveness
)
{
return
isSupported
(
formatSupport
,
false
)
return
isSupported
(
formatSupport
,
false
)
&&
(
format
.
bitrate
==
Format
.
NO_VALUE
||
format
.
bitrate
<=
maxAudioBitrate
)
&&
(
format
.
channelCount
!=
Format
.
NO_VALUE
&&
(
format
.
channelCount
!=
Format
.
NO_VALUE
&&
format
.
channelCount
==
configuration
.
channelCount
)
&&
format
.
channelCount
==
configuration
.
channelCount
)
&&
(
allowMixedMimeTypeAdaptiveness
&&
(
allowMixedMimeTypeAdaptiveness
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/DefaultDataSource.java
View file @
f6297f4f
...
@@ -42,6 +42,7 @@ import java.util.Map;
...
@@ -42,6 +42,7 @@ import java.util.Map;
* <li>rtmp: For fetching data over RTMP. Only supported if the project using ExoPlayer has an
* <li>rtmp: For fetching data over RTMP. Only supported if the project using ExoPlayer has an
* explicit dependency on ExoPlayer's RTMP extension.
* explicit dependency on ExoPlayer's RTMP extension.
* <li>data: For parsing data inlined in the URI as defined in RFC 2397.
* <li>data: For parsing data inlined in the URI as defined in RFC 2397.
* <li>udp: For fetching data over UDP (e.g. udp://something.com/media).
* <li>http(s): For fetching data over HTTP and HTTPS (e.g. https://www.something.com/media.mp4),
* <li>http(s): For fetching data over HTTP and HTTPS (e.g. https://www.something.com/media.mp4),
* if constructed using {@link #DefaultDataSource(Context, TransferListener, String,
* if constructed using {@link #DefaultDataSource(Context, TransferListener, String,
* boolean)}, or any other schemes supported by a base data source if constructed using {@link
* boolean)}, or any other schemes supported by a base data source if constructed using {@link
...
@@ -55,6 +56,7 @@ public final class DefaultDataSource implements DataSource {
...
@@ -55,6 +56,7 @@ public final class DefaultDataSource implements DataSource {
private
static
final
String
SCHEME_ASSET
=
"asset"
;
private
static
final
String
SCHEME_ASSET
=
"asset"
;
private
static
final
String
SCHEME_CONTENT
=
"content"
;
private
static
final
String
SCHEME_CONTENT
=
"content"
;
private
static
final
String
SCHEME_RTMP
=
"rtmp"
;
private
static
final
String
SCHEME_RTMP
=
"rtmp"
;
private
static
final
String
SCHEME_UDP
=
"udp"
;
private
static
final
String
SCHEME_RAW
=
RawResourceDataSource
.
RAW_RESOURCE_SCHEME
;
private
static
final
String
SCHEME_RAW
=
RawResourceDataSource
.
RAW_RESOURCE_SCHEME
;
private
final
Context
context
;
private
final
Context
context
;
...
@@ -62,12 +64,13 @@ public final class DefaultDataSource implements DataSource {
...
@@ -62,12 +64,13 @@ public final class DefaultDataSource implements DataSource {
private
final
DataSource
baseDataSource
;
private
final
DataSource
baseDataSource
;
// Lazily initialized.
// Lazily initialized.
private
@Nullable
DataSource
fileDataSource
;
@Nullable
private
DataSource
fileDataSource
;
private
@Nullable
DataSource
assetDataSource
;
@Nullable
private
DataSource
assetDataSource
;
private
@Nullable
DataSource
contentDataSource
;
@Nullable
private
DataSource
contentDataSource
;
private
@Nullable
DataSource
rtmpDataSource
;
@Nullable
private
DataSource
rtmpDataSource
;
private
@Nullable
DataSource
dataSchemeDataSource
;
@Nullable
private
DataSource
udpDataSource
;
private
@Nullable
DataSource
rawResourceDataSource
;
@Nullable
private
DataSource
dataSchemeDataSource
;
@Nullable
private
DataSource
rawResourceDataSource
;
private
@Nullable
DataSource
dataSource
;
private
@Nullable
DataSource
dataSource
;
...
@@ -218,6 +221,7 @@ public final class DefaultDataSource implements DataSource {
...
@@ -218,6 +221,7 @@ public final class DefaultDataSource implements DataSource {
maybeAddListenerToDataSource
(
assetDataSource
,
transferListener
);
maybeAddListenerToDataSource
(
assetDataSource
,
transferListener
);
maybeAddListenerToDataSource
(
contentDataSource
,
transferListener
);
maybeAddListenerToDataSource
(
contentDataSource
,
transferListener
);
maybeAddListenerToDataSource
(
rtmpDataSource
,
transferListener
);
maybeAddListenerToDataSource
(
rtmpDataSource
,
transferListener
);
maybeAddListenerToDataSource
(
udpDataSource
,
transferListener
);
maybeAddListenerToDataSource
(
dataSchemeDataSource
,
transferListener
);
maybeAddListenerToDataSource
(
dataSchemeDataSource
,
transferListener
);
maybeAddListenerToDataSource
(
rawResourceDataSource
,
transferListener
);
maybeAddListenerToDataSource
(
rawResourceDataSource
,
transferListener
);
}
}
...
@@ -240,6 +244,8 @@ public final class DefaultDataSource implements DataSource {
...
@@ -240,6 +244,8 @@ public final class DefaultDataSource implements DataSource {
dataSource
=
getContentDataSource
();
dataSource
=
getContentDataSource
();
}
else
if
(
SCHEME_RTMP
.
equals
(
scheme
))
{
}
else
if
(
SCHEME_RTMP
.
equals
(
scheme
))
{
dataSource
=
getRtmpDataSource
();
dataSource
=
getRtmpDataSource
();
}
else
if
(
SCHEME_UDP
.
equals
(
scheme
))
{
dataSource
=
getUdpDataSource
();
}
else
if
(
DataSchemeDataSource
.
SCHEME_DATA
.
equals
(
scheme
))
{
}
else
if
(
DataSchemeDataSource
.
SCHEME_DATA
.
equals
(
scheme
))
{
dataSource
=
getDataSchemeDataSource
();
dataSource
=
getDataSchemeDataSource
();
}
else
if
(
SCHEME_RAW
.
equals
(
scheme
))
{
}
else
if
(
SCHEME_RAW
.
equals
(
scheme
))
{
...
@@ -277,6 +283,14 @@ public final class DefaultDataSource implements DataSource {
...
@@ -277,6 +283,14 @@ public final class DefaultDataSource implements DataSource {
}
}
}
}
private
DataSource
getUdpDataSource
()
{
if
(
udpDataSource
==
null
)
{
udpDataSource
=
new
UdpDataSource
();
addListenersToDataSource
(
udpDataSource
);
}
return
udpDataSource
;
}
private
DataSource
getFileDataSource
()
{
private
DataSource
getFileDataSource
()
{
if
(
fileDataSource
==
null
)
{
if
(
fileDataSource
==
null
)
{
fileDataSource
=
new
FileDataSource
();
fileDataSource
=
new
FileDataSource
();
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/ResolvingDataSource.java
0 → 100644
View file @
f6297f4f
/*
* Copyright (C) 2019 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
.
upstream
;
import
android.net.Uri
;
import
androidx.annotation.Nullable
;
import
java.io.IOException
;
import
java.util.List
;
import
java.util.Map
;
/** {@link DataSource} wrapper allowing just-in-time resolution of {@link DataSpec DataSpecs}. */
public
final
class
ResolvingDataSource
implements
DataSource
{
/** Resolves {@link DataSpec DataSpecs}. */
public
interface
Resolver
{
/**
* Resolves a {@link DataSpec} before forwarding it to the wrapped {@link DataSource}. This
* method is allowed to block until the {@link DataSpec} has been resolved.
*
* <p>Note that this method is called for every new connection, so caching of results is
* recommended, especially if network operations are involved.
*
* @param dataSpec The original {@link DataSpec}.
* @return The resolved {@link DataSpec}.
* @throws IOException If an {@link IOException} occurred while resolving the {@link DataSpec}.
*/
DataSpec
resolveDataSpec
(
DataSpec
dataSpec
)
throws
IOException
;
/**
* Resolves a URI reported by {@link DataSource#getUri()} for event reporting and caching
* purposes.
*
* <p>Implementations do not need to overwrite this method unless they want to change the
* reported URI.
*
* <p>This method is <em>not</em> allowed to block.
*
* @param uri The URI as reported by {@link DataSource#getUri()}.
* @return The resolved URI used for event reporting and caching.
*/
default
Uri
resolveReportedUri
(
Uri
uri
)
{
return
uri
;
}
}
/** {@link DataSource.Factory} for {@link ResolvingDataSource} instances. */
public
static
final
class
Factory
implements
DataSource
.
Factory
{
private
final
DataSource
.
Factory
upstreamFactory
;
private
final
Resolver
resolver
;
/**
* Creates factory for {@link ResolvingDataSource} instances.
*
* @param upstreamFactory The wrapped {@link DataSource.Factory} handling the resolved {@link
* DataSpec DataSpecs}.
* @param resolver The {@link Resolver} to resolve the {@link DataSpec DataSpecs}.
*/
public
Factory
(
DataSource
.
Factory
upstreamFactory
,
Resolver
resolver
)
{
this
.
upstreamFactory
=
upstreamFactory
;
this
.
resolver
=
resolver
;
}
@Override
public
DataSource
createDataSource
()
{
return
new
ResolvingDataSource
(
upstreamFactory
.
createDataSource
(),
resolver
);
}
}
private
final
DataSource
upstreamDataSource
;
private
final
Resolver
resolver
;
private
boolean
upstreamOpened
;
/**
* @param upstreamDataSource The wrapped {@link DataSource}.
* @param resolver The {@link Resolver} to resolve the {@link DataSpec DataSpecs}.
*/
public
ResolvingDataSource
(
DataSource
upstreamDataSource
,
Resolver
resolver
)
{
this
.
upstreamDataSource
=
upstreamDataSource
;
this
.
resolver
=
resolver
;
}
@Override
public
void
addTransferListener
(
TransferListener
transferListener
)
{
upstreamDataSource
.
addTransferListener
(
transferListener
);
}
@Override
public
long
open
(
DataSpec
dataSpec
)
throws
IOException
{
DataSpec
resolvedDataSpec
=
resolver
.
resolveDataSpec
(
dataSpec
);
upstreamOpened
=
true
;
return
upstreamDataSource
.
open
(
resolvedDataSpec
);
}
@Override
public
int
read
(
byte
[]
buffer
,
int
offset
,
int
readLength
)
throws
IOException
{
return
upstreamDataSource
.
read
(
buffer
,
offset
,
readLength
);
}
@Nullable
@Override
public
Uri
getUri
()
{
Uri
reportedUri
=
upstreamDataSource
.
getUri
();
return
reportedUri
==
null
?
null
:
resolver
.
resolveReportedUri
(
reportedUri
);
}
@Override
public
Map
<
String
,
List
<
String
>>
getResponseHeaders
()
{
return
upstreamDataSource
.
getResponseHeaders
();
}
@Override
public
void
close
()
throws
IOException
{
if
(
upstreamOpened
)
{
upstreamOpened
=
false
;
upstreamDataSource
.
close
();
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java
View file @
f6297f4f
...
@@ -134,9 +134,9 @@ public final class CacheDataSource implements DataSource {
...
@@ -134,9 +134,9 @@ public final class CacheDataSource implements DataSource {
private
@Nullable
DataSource
currentDataSource
;
private
@Nullable
DataSource
currentDataSource
;
private
boolean
currentDataSpecLengthUnset
;
private
boolean
currentDataSpecLengthUnset
;
private
@Nullabl
e
Uri
uri
;
@Nullable
privat
e
Uri
uri
;
private
@Nullabl
e
Uri
actualUri
;
@Nullable
privat
e
Uri
actualUri
;
private
@HttpMethod
int
httpMethod
;
@HttpMethod
private
int
httpMethod
;
private
int
flags
;
private
int
flags
;
private
@Nullable
String
key
;
private
@Nullable
String
key
;
private
long
readPosition
;
private
long
readPosition
;
...
@@ -319,7 +319,7 @@ public final class CacheDataSource implements DataSource {
...
@@ -319,7 +319,7 @@ public final class CacheDataSource implements DataSource {
}
}
return
bytesRead
;
return
bytesRead
;
}
catch
(
IOException
e
)
{
}
catch
(
IOException
e
)
{
if
(
currentDataSpecLengthUnset
&&
isCausedByPositionOutOfRange
(
e
))
{
if
(
currentDataSpecLengthUnset
&&
CacheUtil
.
isCausedByPositionOutOfRange
(
e
))
{
setNoBytesRemainingAndMaybeStoreLength
();
setNoBytesRemainingAndMaybeStoreLength
();
return
C
.
RESULT_END_OF_INPUT
;
return
C
.
RESULT_END_OF_INPUT
;
}
}
...
@@ -484,20 +484,6 @@ public final class CacheDataSource implements DataSource {
...
@@ -484,20 +484,6 @@ public final class CacheDataSource implements DataSource {
return
redirectedUri
!=
null
?
redirectedUri
:
defaultUri
;
return
redirectedUri
!=
null
?
redirectedUri
:
defaultUri
;
}
}
private
static
boolean
isCausedByPositionOutOfRange
(
IOException
e
)
{
Throwable
cause
=
e
;
while
(
cause
!=
null
)
{
if
(
cause
instanceof
DataSourceException
)
{
int
reason
=
((
DataSourceException
)
cause
).
reason
;
if
(
reason
==
DataSourceException
.
POSITION_OUT_OF_RANGE
)
{
return
true
;
}
}
cause
=
cause
.
getCause
();
}
return
false
;
}
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 @
f6297f4f
...
@@ -20,6 +20,7 @@ import androidx.annotation.Nullable;
...
@@ -20,6 +20,7 @@ import androidx.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.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.DataSourceException
;
import
com.google.android.exoplayer2.upstream.DataSpec
;
import
com.google.android.exoplayer2.upstream.DataSpec
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.PriorityTaskManager
;
import
com.google.android.exoplayer2.util.PriorityTaskManager
;
...
@@ -78,13 +79,7 @@ public final class CacheUtil {
...
@@ -78,13 +79,7 @@ public final class CacheUtil {
DataSpec
dataSpec
,
Cache
cache
,
@Nullable
CacheKeyFactory
cacheKeyFactory
)
{
DataSpec
dataSpec
,
Cache
cache
,
@Nullable
CacheKeyFactory
cacheKeyFactory
)
{
String
key
=
buildCacheKey
(
dataSpec
,
cacheKeyFactory
);
String
key
=
buildCacheKey
(
dataSpec
,
cacheKeyFactory
);
long
position
=
dataSpec
.
absoluteStreamPosition
;
long
position
=
dataSpec
.
absoluteStreamPosition
;
long
requestLength
;
long
requestLength
=
getRequestLength
(
dataSpec
,
cache
,
key
);
if
(
dataSpec
.
length
!=
C
.
LENGTH_UNSET
)
{
requestLength
=
dataSpec
.
length
;
}
else
{
long
contentLength
=
ContentMetadata
.
getContentLength
(
cache
.
getContentMetadata
(
key
));
requestLength
=
contentLength
==
C
.
LENGTH_UNSET
?
C
.
LENGTH_UNSET
:
contentLength
-
position
;
}
long
bytesAlreadyCached
=
0
;
long
bytesAlreadyCached
=
0
;
long
bytesLeft
=
requestLength
;
long
bytesLeft
=
requestLength
;
while
(
bytesLeft
!=
0
)
{
while
(
bytesLeft
!=
0
)
{
...
@@ -179,53 +174,66 @@ public final class CacheUtil {
...
@@ -179,53 +174,66 @@ public final class CacheUtil {
Assertions
.
checkNotNull
(
dataSource
);
Assertions
.
checkNotNull
(
dataSource
);
Assertions
.
checkNotNull
(
buffer
);
Assertions
.
checkNotNull
(
buffer
);
String
key
=
buildCacheKey
(
dataSpec
,
cacheKeyFactory
);
long
bytesLeft
;
ProgressNotifier
progressNotifier
=
null
;
ProgressNotifier
progressNotifier
=
null
;
if
(
progressListener
!=
null
)
{
if
(
progressListener
!=
null
)
{
progressNotifier
=
new
ProgressNotifier
(
progressListener
);
progressNotifier
=
new
ProgressNotifier
(
progressListener
);
Pair
<
Long
,
Long
>
lengthAndBytesAlreadyCached
=
getCached
(
dataSpec
,
cache
,
cacheKeyFactory
);
Pair
<
Long
,
Long
>
lengthAndBytesAlreadyCached
=
getCached
(
dataSpec
,
cache
,
cacheKeyFactory
);
progressNotifier
.
init
(
lengthAndBytesAlreadyCached
.
first
,
lengthAndBytesAlreadyCached
.
second
);
progressNotifier
.
init
(
lengthAndBytesAlreadyCached
.
first
,
lengthAndBytesAlreadyCached
.
second
);
bytesLeft
=
lengthAndBytesAlreadyCached
.
first
;
}
else
{
bytesLeft
=
getRequestLength
(
dataSpec
,
cache
,
key
);
}
}
String
key
=
buildCacheKey
(
dataSpec
,
cacheKeyFactory
);
long
position
=
dataSpec
.
absoluteStreamPosition
;
long
position
=
dataSpec
.
absoluteStreamPosition
;
long
bytesLeft
;
boolean
lengthUnset
=
bytesLeft
==
C
.
LENGTH_UNSET
;
if
(
dataSpec
.
length
!=
C
.
LENGTH_UNSET
)
{
bytesLeft
=
dataSpec
.
length
;
}
else
{
long
contentLength
=
ContentMetadata
.
getContentLength
(
cache
.
getContentMetadata
(
key
));
bytesLeft
=
contentLength
==
C
.
LENGTH_UNSET
?
C
.
LENGTH_UNSET
:
contentLength
-
position
;
}
while
(
bytesLeft
!=
0
)
{
while
(
bytesLeft
!=
0
)
{
throwExceptionIfInterruptedOrCancelled
(
isCanceled
);
throwExceptionIfInterruptedOrCancelled
(
isCanceled
);
long
blockLength
=
long
blockLength
=
cache
.
getCachedLength
(
cache
.
getCachedLength
(
key
,
position
,
lengthUnset
?
Long
.
MAX_VALUE
:
bytesLeft
);
key
,
position
,
bytesLeft
!=
C
.
LENGTH_UNSET
?
bytesLeft
:
Long
.
MAX_VALUE
);
if
(
blockLength
>
0
)
{
if
(
blockLength
>
0
)
{
// Skip already cached data.
// Skip already cached data.
}
else
{
}
else
{
// There is a hole in the cache which is at least "-blockLength" long.
// There is a hole in the cache which is at least "-blockLength" long.
blockLength
=
-
blockLength
;
blockLength
=
-
blockLength
;
long
length
=
blockLength
==
Long
.
MAX_VALUE
?
C
.
LENGTH_UNSET
:
blockLength
;
boolean
isLastBlock
=
length
==
bytesLeft
;
long
read
=
long
read
=
readAndDiscard
(
readAndDiscard
(
dataSpec
,
dataSpec
,
position
,
position
,
blockL
ength
,
l
ength
,
dataSource
,
dataSource
,
buffer
,
buffer
,
priorityTaskManager
,
priorityTaskManager
,
priority
,
priority
,
progressNotifier
,
progressNotifier
,
isLastBlock
,
isCanceled
);
isCanceled
);
if
(
read
<
blockLength
)
{
if
(
read
<
blockLength
)
{
// Reached to the end of the data.
// Reached to the end of the data.
if
(
enableEOFException
&&
bytesLeft
!=
C
.
LENGTH_UNSET
)
{
if
(
enableEOFException
&&
!
lengthUnset
)
{
throw
new
EOFException
();
throw
new
EOFException
();
}
}
break
;
break
;
}
}
}
}
position
+=
blockLength
;
position
+=
blockLength
;
bytesLeft
-=
bytesLeft
==
C
.
LENGTH_UNSET
?
0
:
blockLength
;
if
(!
lengthUnset
)
{
bytesLeft
-=
blockLength
;
}
}
}
private
static
long
getRequestLength
(
DataSpec
dataSpec
,
Cache
cache
,
String
key
)
{
if
(
dataSpec
.
length
!=
C
.
LENGTH_UNSET
)
{
return
dataSpec
.
length
;
}
else
{
long
contentLength
=
ContentMetadata
.
getContentLength
(
cache
.
getContentMetadata
(
key
));
return
contentLength
==
C
.
LENGTH_UNSET
?
C
.
LENGTH_UNSET
:
contentLength
-
dataSpec
.
absoluteStreamPosition
;
}
}
}
}
...
@@ -242,6 +250,7 @@ public final class CacheUtil {
...
@@ -242,6 +250,7 @@ public final class CacheUtil {
* caching.
* caching.
* @param priority The priority of this task.
* @param priority The priority of this task.
* @param progressNotifier A notifier through which to report progress updates, or {@code null}.
* @param progressNotifier A notifier through which to report progress updates, or {@code null}.
* @param isLastBlock Whether this read block is the last block of the content.
* @param isCanceled An optional flag that will interrupt caching if set to true.
* @param isCanceled An optional flag that will interrupt caching if set to true.
* @return Number of read bytes, or 0 if no data is available because the end of the opened range
* @return Number of read bytes, or 0 if no data is available because the end of the opened range
* has been reached.
* has been reached.
...
@@ -255,54 +264,64 @@ public final class CacheUtil {
...
@@ -255,54 +264,64 @@ public final class CacheUtil {
PriorityTaskManager
priorityTaskManager
,
PriorityTaskManager
priorityTaskManager
,
int
priority
,
int
priority
,
@Nullable
ProgressNotifier
progressNotifier
,
@Nullable
ProgressNotifier
progressNotifier
,
boolean
isLastBlock
,
AtomicBoolean
isCanceled
)
AtomicBoolean
isCanceled
)
throws
IOException
,
InterruptedException
{
throws
IOException
,
InterruptedException
{
long
positionOffset
=
absoluteStreamPosition
-
dataSpec
.
absoluteStreamPosition
;
long
positionOffset
=
absoluteStreamPosition
-
dataSpec
.
absoluteStreamPosition
;
long
initialPositionOffset
=
positionOffset
;
long
endOffset
=
length
!=
C
.
LENGTH_UNSET
?
positionOffset
+
length
:
C
.
POSITION_UNSET
;
while
(
true
)
{
while
(
true
)
{
if
(
priorityTaskManager
!=
null
)
{
if
(
priorityTaskManager
!=
null
)
{
// Wait for any other thread with higher priority to finish its job.
// Wait for any other thread with higher priority to finish its job.
priorityTaskManager
.
proceed
(
priority
);
priorityTaskManager
.
proceed
(
priority
);
}
}
throwExceptionIfInterruptedOrCancelled
(
isCanceled
);
try
{
try
{
throwExceptionIfInterruptedOrCancelled
(
isCanceled
);
long
resolvedLength
=
C
.
LENGTH_UNSET
;
// Create a new dataSpec setting length to C.LENGTH_UNSET to prevent getting an error in
boolean
isDataSourceOpen
=
false
;
// case the given length exceeds the end of input.
if
(
endOffset
!=
C
.
POSITION_UNSET
)
{
dataSpec
=
// If a specific length is given, first try to open the data source for that length to
new
DataSpec
(
// avoid more data then required to be requested. If the given length exceeds the end of
dataSpec
.
uri
,
// input we will get a "position out of range" error. In that case try to open the source
dataSpec
.
httpMethod
,
// again with unset length.
dataSpec
.
httpBody
,
try
{
absoluteStreamPosition
,
resolvedLength
=
/* position= */
dataSpec
.
position
+
positionOffset
,
dataSource
.
open
(
dataSpec
.
subrange
(
positionOffset
,
endOffset
-
positionOffset
));
C
.
LENGTH_UNSET
,
isDataSourceOpen
=
true
;
dataSpec
.
key
,
}
catch
(
IOException
exception
)
{
dataSpec
.
flags
);
if
(!
isLastBlock
||
!
isCausedByPositionOutOfRange
(
exception
))
{
long
resolvedLength
=
dataSource
.
open
(
dataSpec
);
throw
exception
;
if
(
progressNotifier
!=
null
&&
resolvedLength
!=
C
.
LENGTH_UNSET
)
{
}
Util
.
closeQuietly
(
dataSource
);
}
}
if
(!
isDataSourceOpen
)
{
resolvedLength
=
dataSource
.
open
(
dataSpec
.
subrange
(
positionOffset
,
C
.
LENGTH_UNSET
));
}
if
(
isLastBlock
&&
progressNotifier
!=
null
&&
resolvedLength
!=
C
.
LENGTH_UNSET
)
{
progressNotifier
.
onRequestLengthResolved
(
positionOffset
+
resolvedLength
);
progressNotifier
.
onRequestLengthResolved
(
positionOffset
+
resolvedLength
);
}
}
long
totalBytesRead
=
0
;
while
(
positionOffset
!=
endOffset
)
{
while
(
totalBytesRead
!=
length
)
{
throwExceptionIfInterruptedOrCancelled
(
isCanceled
);
throwExceptionIfInterruptedOrCancelled
(
isCanceled
);
int
bytesRead
=
int
bytesRead
=
dataSource
.
read
(
dataSource
.
read
(
buffer
,
buffer
,
0
,
0
,
length
!=
C
.
LENGTH
_UNSET
endOffset
!=
C
.
POSITION
_UNSET
?
(
int
)
Math
.
min
(
buffer
.
length
,
length
-
totalBytesRead
)
?
(
int
)
Math
.
min
(
buffer
.
length
,
endOffset
-
positionOffset
)
:
buffer
.
length
);
:
buffer
.
length
);
if
(
bytesRead
==
C
.
RESULT_END_OF_INPUT
)
{
if
(
bytesRead
==
C
.
RESULT_END_OF_INPUT
)
{
if
(
progressNotifier
!=
null
)
{
if
(
progressNotifier
!=
null
)
{
progressNotifier
.
onRequestLengthResolved
(
positionOffset
+
totalBytesRead
);
progressNotifier
.
onRequestLengthResolved
(
positionOffset
);
}
}
break
;
break
;
}
}
totalBytesRead
+=
bytesRead
;
positionOffset
+=
bytesRead
;
if
(
progressNotifier
!=
null
)
{
if
(
progressNotifier
!=
null
)
{
progressNotifier
.
onBytesCached
(
bytesRead
);
progressNotifier
.
onBytesCached
(
bytesRead
);
}
}
}
}
return
totalBytesRead
;
return
positionOffset
-
initialPositionOffset
;
}
catch
(
PriorityTaskManager
.
PriorityTooLowException
exception
)
{
}
catch
(
PriorityTaskManager
.
PriorityTooLowException
exception
)
{
// catch and try again
// catch and try again
}
finally
{
}
finally
{
...
@@ -340,6 +359,20 @@ public final class CacheUtil {
...
@@ -340,6 +359,20 @@ public final class CacheUtil {
}
}
}
}
/*package*/
static
boolean
isCausedByPositionOutOfRange
(
IOException
e
)
{
Throwable
cause
=
e
;
while
(
cause
!=
null
)
{
if
(
cause
instanceof
DataSourceException
)
{
int
reason
=
((
DataSourceException
)
cause
).
reason
;
if
(
reason
==
DataSourceException
.
POSITION_OUT_OF_RANGE
)
{
return
true
;
}
}
cause
=
cause
.
getCause
();
}
return
false
;
}
private
static
String
buildCacheKey
(
private
static
String
buildCacheKey
(
DataSpec
dataSpec
,
@Nullable
CacheKeyFactory
cacheKeyFactory
)
{
DataSpec
dataSpec
,
@Nullable
CacheKeyFactory
cacheKeyFactory
)
{
return
(
cacheKeyFactory
!=
null
?
cacheKeyFactory
:
DEFAULT_CACHE_KEY_FACTORY
)
return
(
cacheKeyFactory
!=
null
?
cacheKeyFactory
:
DEFAULT_CACHE_KEY_FACTORY
)
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java
View file @
f6297f4f
...
@@ -348,8 +348,9 @@ public final class MimeTypes {
...
@@ -348,8 +348,9 @@ public final class MimeTypes {
case
MimeTypes
.
AUDIO_AC3
:
case
MimeTypes
.
AUDIO_AC3
:
return
C
.
ENCODING_AC3
;
return
C
.
ENCODING_AC3
;
case
MimeTypes
.
AUDIO_E_AC3
:
case
MimeTypes
.
AUDIO_E_AC3
:
case
MimeTypes
.
AUDIO_E_AC3_JOC
:
return
C
.
ENCODING_E_AC3
;
return
C
.
ENCODING_E_AC3
;
case
MimeTypes
.
AUDIO_E_AC3_JOC
:
return
C
.
ENCODING_E_AC3_JOC
;
case
MimeTypes
.
AUDIO_AC4
:
case
MimeTypes
.
AUDIO_AC4
:
return
C
.
ENCODING_AC4
;
return
C
.
ENCODING_AC4
;
case
MimeTypes
.
AUDIO_DTS
:
case
MimeTypes
.
AUDIO_DTS
:
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/Util.java
View file @
f6297f4f
...
@@ -1713,7 +1713,12 @@ public final class Util {
...
@@ -1713,7 +1713,12 @@ public final class Util {
if
(
connectivityManager
==
null
)
{
if
(
connectivityManager
==
null
)
{
return
C
.
NETWORK_TYPE_UNKNOWN
;
return
C
.
NETWORK_TYPE_UNKNOWN
;
}
}
networkInfo
=
connectivityManager
.
getActiveNetworkInfo
();
try
{
networkInfo
=
connectivityManager
.
getActiveNetworkInfo
();
}
catch
(
SecurityException
e
)
{
// Expected if permission was revoked.
return
C
.
NETWORK_TYPE_UNKNOWN
;
}
if
(
networkInfo
==
null
||
!
networkInfo
.
isConnected
())
{
if
(
networkInfo
==
null
||
!
networkInfo
.
isConnected
())
{
return
C
.
NETWORK_TYPE_OFFLINE
;
return
C
.
NETWORK_TYPE_OFFLINE
;
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
View file @
f6297f4f
...
@@ -550,8 +550,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -550,8 +550,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
MediaCodec
codec
,
MediaCodec
codec
,
Format
format
,
Format
format
,
MediaCrypto
crypto
,
MediaCrypto
crypto
,
float
codecOperatingRate
)
float
codecOperatingRate
)
{
throws
DecoderQueryException
{
codecMaxValues
=
getCodecMaxValues
(
codecInfo
,
format
,
getStreamFormats
());
codecMaxValues
=
getCodecMaxValues
(
codecInfo
,
format
,
getStreamFormats
());
MediaFormat
mediaFormat
=
MediaFormat
mediaFormat
=
getMediaFormat
(
getMediaFormat
(
...
@@ -1173,11 +1172,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1173,11 +1172,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
* @param format The format for which the codec is being configured.
* @param format The format for which the codec is being configured.
* @param streamFormats The possible stream formats.
* @param streamFormats The possible stream formats.
* @return Suitable {@link CodecMaxValues}.
* @return Suitable {@link CodecMaxValues}.
* @throws DecoderQueryException If an error occurs querying {@code codecInfo}.
*/
*/
protected
CodecMaxValues
getCodecMaxValues
(
protected
CodecMaxValues
getCodecMaxValues
(
MediaCodecInfo
codecInfo
,
Format
format
,
Format
[]
streamFormats
)
MediaCodecInfo
codecInfo
,
Format
format
,
Format
[]
streamFormats
)
{
throws
DecoderQueryException
{
int
maxWidth
=
format
.
width
;
int
maxWidth
=
format
.
width
;
int
maxHeight
=
format
.
height
;
int
maxHeight
=
format
.
height
;
int
maxInputSize
=
getMaxInputSize
(
codecInfo
,
format
);
int
maxInputSize
=
getMaxInputSize
(
codecInfo
,
format
);
...
@@ -1227,17 +1224,15 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1227,17 +1224,15 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
}
/**
/**
* Returns a maximum video size to use when configuring a codec for {@code format} in a way
* Returns a maximum video size to use when configuring a codec for {@code format} in a way
that
*
that will allow possible adaptation to other compatible formats that are expected to have th
e
*
will allow possible adaptation to other compatible formats that are expected to have the sam
e
*
same
aspect ratio, but whose sizes are unknown.
* aspect ratio, but whose sizes are unknown.
*
*
* @param codecInfo Information about the {@link MediaCodec} being configured.
* @param codecInfo Information about the {@link MediaCodec} being configured.
* @param format The format for which the codec is being configured.
* @param format The format for which the codec is being configured.
* @return The maximum video size to use, or null if the size of {@code format} should be used.
* @return The maximum video size to use, or null if the size of {@code format} should be used.
* @throws DecoderQueryException If an error occurs querying {@code codecInfo}.
*/
*/
private
static
Point
getCodecMaxSize
(
MediaCodecInfo
codecInfo
,
Format
format
)
private
static
Point
getCodecMaxSize
(
MediaCodecInfo
codecInfo
,
Format
format
)
{
throws
DecoderQueryException
{
boolean
isVerticalVideo
=
format
.
height
>
format
.
width
;
boolean
isVerticalVideo
=
format
.
height
>
format
.
width
;
int
formatLongEdgePx
=
isVerticalVideo
?
format
.
height
:
format
.
width
;
int
formatLongEdgePx
=
isVerticalVideo
?
format
.
height
:
format
.
width
;
int
formatShortEdgePx
=
isVerticalVideo
?
format
.
width
:
format
.
height
;
int
formatShortEdgePx
=
isVerticalVideo
?
format
.
width
:
format
.
height
;
...
@@ -1255,12 +1250,18 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1255,12 +1250,18 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
return
alignedSize
;
return
alignedSize
;
}
}
}
else
{
}
else
{
// Conservatively assume the codec requires 16px width and height alignment.
try
{
longEdgePx
=
Util
.
ceilDivide
(
longEdgePx
,
16
)
*
16
;
// Conservatively assume the codec requires 16px width and height alignment.
shortEdgePx
=
Util
.
ceilDivide
(
shortEdgePx
,
16
)
*
16
;
longEdgePx
=
Util
.
ceilDivide
(
longEdgePx
,
16
)
*
16
;
if
(
longEdgePx
*
shortEdgePx
<=
MediaCodecUtil
.
maxH264DecodableFrameSize
())
{
shortEdgePx
=
Util
.
ceilDivide
(
shortEdgePx
,
16
)
*
16
;
return
new
Point
(
isVerticalVideo
?
shortEdgePx
:
longEdgePx
,
if
(
longEdgePx
*
shortEdgePx
<=
MediaCodecUtil
.
maxH264DecodableFrameSize
())
{
isVerticalVideo
?
longEdgePx
:
shortEdgePx
);
return
new
Point
(
isVerticalVideo
?
shortEdgePx
:
longEdgePx
,
isVerticalVideo
?
longEdgePx
:
shortEdgePx
);
}
}
catch
(
DecoderQueryException
e
)
{
// We tried our best. Give up!
return
null
;
}
}
}
}
}
}
...
...
library/core/src/test/assets/webvtt/with_css_styles
View file @
f6297f4f
...
@@ -13,8 +13,6 @@ STYLE
...
@@ -13,8 +13,6 @@ STYLE
::cue(#id2) {
::cue(#id2) {
color: peachpuff;
color: peachpuff;
}
}
STYLE
::cue(v[voice="LaGord"]) { background-color: lime }
::cue(v[voice="LaGord"]) { background-color: lime }
STYLE
STYLE
...
...
library/core/src/test/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java
View file @
f6297f4f
...
@@ -514,7 +514,7 @@ public final class TtmlDecoderTest {
...
@@ -514,7 +514,7 @@ public final class TtmlDecoderTest {
assertThat
(
cue
.
position
).
isEqualTo
(
24
f
/
100
f
);
assertThat
(
cue
.
position
).
isEqualTo
(
24
f
/
100
f
);
assertThat
(
cue
.
line
).
isEqualTo
(
28
f
/
100
f
);
assertThat
(
cue
.
line
).
isEqualTo
(
28
f
/
100
f
);
assertThat
(
cue
.
size
).
isEqualTo
(
51
f
/
100
f
);
assertThat
(
cue
.
size
).
isEqualTo
(
51
f
/
100
f
);
assertThat
(
cue
.
bitmapHeight
).
isEqualTo
(
Cue
.
DIMEN_UNSET
);
assertThat
(
cue
.
bitmapHeight
).
isEqualTo
(
12
f
/
100
f
);
cues
=
subtitle
.
getCues
(
4000000
);
cues
=
subtitle
.
getCues
(
4000000
);
assertThat
(
cues
).
hasSize
(
1
);
assertThat
(
cues
).
hasSize
(
1
);
...
@@ -524,7 +524,7 @@ public final class TtmlDecoderTest {
...
@@ -524,7 +524,7 @@ public final class TtmlDecoderTest {
assertThat
(
cue
.
position
).
isEqualTo
(
21
f
/
100
f
);
assertThat
(
cue
.
position
).
isEqualTo
(
21
f
/
100
f
);
assertThat
(
cue
.
line
).
isEqualTo
(
35
f
/
100
f
);
assertThat
(
cue
.
line
).
isEqualTo
(
35
f
/
100
f
);
assertThat
(
cue
.
size
).
isEqualTo
(
57
f
/
100
f
);
assertThat
(
cue
.
size
).
isEqualTo
(
57
f
/
100
f
);
assertThat
(
cue
.
bitmapHeight
).
isEqualTo
(
Cue
.
DIMEN_UNSET
);
assertThat
(
cue
.
bitmapHeight
).
isEqualTo
(
6
f
/
100
f
);
cues
=
subtitle
.
getCues
(
7500000
);
cues
=
subtitle
.
getCues
(
7500000
);
assertThat
(
cues
).
hasSize
(
1
);
assertThat
(
cues
).
hasSize
(
1
);
...
@@ -534,7 +534,7 @@ public final class TtmlDecoderTest {
...
@@ -534,7 +534,7 @@ public final class TtmlDecoderTest {
assertThat
(
cue
.
position
).
isEqualTo
(
24
f
/
100
f
);
assertThat
(
cue
.
position
).
isEqualTo
(
24
f
/
100
f
);
assertThat
(
cue
.
line
).
isEqualTo
(
28
f
/
100
f
);
assertThat
(
cue
.
line
).
isEqualTo
(
28
f
/
100
f
);
assertThat
(
cue
.
size
).
isEqualTo
(
51
f
/
100
f
);
assertThat
(
cue
.
size
).
isEqualTo
(
51
f
/
100
f
);
assertThat
(
cue
.
bitmapHeight
).
isEqualTo
(
Cue
.
DIMEN_UNSET
);
assertThat
(
cue
.
bitmapHeight
).
isEqualTo
(
12
f
/
100
f
);
}
}
@Test
@Test
...
@@ -549,7 +549,7 @@ public final class TtmlDecoderTest {
...
@@ -549,7 +549,7 @@ public final class TtmlDecoderTest {
assertThat
(
cue
.
position
).
isEqualTo
(
307
f
/
1280
f
);
assertThat
(
cue
.
position
).
isEqualTo
(
307
f
/
1280
f
);
assertThat
(
cue
.
line
).
isEqualTo
(
562
f
/
720
f
);
assertThat
(
cue
.
line
).
isEqualTo
(
562
f
/
720
f
);
assertThat
(
cue
.
size
).
isEqualTo
(
653
f
/
1280
f
);
assertThat
(
cue
.
size
).
isEqualTo
(
653
f
/
1280
f
);
assertThat
(
cue
.
bitmapHeight
).
isEqualTo
(
Cue
.
DIMEN_UNSET
);
assertThat
(
cue
.
bitmapHeight
).
isEqualTo
(
86
f
/
720
f
);
cues
=
subtitle
.
getCues
(
4000000
);
cues
=
subtitle
.
getCues
(
4000000
);
assertThat
(
cues
).
hasSize
(
1
);
assertThat
(
cues
).
hasSize
(
1
);
...
@@ -559,7 +559,7 @@ public final class TtmlDecoderTest {
...
@@ -559,7 +559,7 @@ public final class TtmlDecoderTest {
assertThat
(
cue
.
position
).
isEqualTo
(
269
f
/
1280
f
);
assertThat
(
cue
.
position
).
isEqualTo
(
269
f
/
1280
f
);
assertThat
(
cue
.
line
).
isEqualTo
(
612
f
/
720
f
);
assertThat
(
cue
.
line
).
isEqualTo
(
612
f
/
720
f
);
assertThat
(
cue
.
size
).
isEqualTo
(
730
f
/
1280
f
);
assertThat
(
cue
.
size
).
isEqualTo
(
730
f
/
1280
f
);
assertThat
(
cue
.
bitmapHeight
).
isEqualTo
(
Cue
.
DIMEN_UNSET
);
assertThat
(
cue
.
bitmapHeight
).
isEqualTo
(
43
f
/
720
f
);
}
}
@Test
@Test
...
...
library/core/src/test/java/com/google/android/exoplayer2/text/webvtt/CssParserTest.java
View file @
f6297f4f
...
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
...
@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertThat;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
androidx.test.ext.junit.runners.AndroidJUnit4
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.android.exoplayer2.util.Util
;
import
java.util.List
;
import
org.junit.Before
;
import
org.junit.Before
;
import
org.junit.Test
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.junit.runner.RunWith
;
...
@@ -87,21 +88,32 @@ public final class CssParserTest {
...
@@ -87,21 +88,32 @@ public final class CssParserTest {
@Test
@Test
public
void
testParseMethodSimpleInput
()
{
public
void
testParseMethodSimpleInput
()
{
String
styleBlock1
=
" ::cue { color : black; background-color: PapayaWhip }"
;
WebvttCssStyle
expectedStyle
=
new
WebvttCssStyle
();
WebvttCssStyle
expectedStyle
=
new
WebvttCssStyle
();
String
styleBlock1
=
" ::cue { color : black; background-color: PapayaWhip }"
;
expectedStyle
.
setFontColor
(
0xFF000000
);
expectedStyle
.
setFontColor
(
0xFF000000
);
expectedStyle
.
setBackgroundColor
(
0xFFFFEFD5
);
expectedStyle
.
setBackgroundColor
(
0xFFFFEFD5
);
assertParserProduces
(
expectedStyle
,
styleBlock1
);
assertParserProduces
(
styleBlock1
,
expectedStyle
);
String
styleBlock2
=
" ::cue { color : black }\n\n::cue { color : invalid }"
;
String
styleBlock2
=
" ::cue { color : black }\n\n::cue { color : invalid }"
;
expectedStyle
=
new
WebvttCssStyle
();
expectedStyle
=
new
WebvttCssStyle
();
expectedStyle
.
setFontColor
(
0xFF000000
);
expectedStyle
.
setFontColor
(
0xFF000000
);
assertParserProduces
(
expectedStyle
,
styleBlock2
);
assertParserProduces
(
styleBlock2
,
expectedStyle
);
String
styleBlock3
=
"
\n
::cue {\n background-color\n:#00fFFe}"
;
String
styleBlock3
=
"::cue {\n background-color\n:#00fFFe}"
;
expectedStyle
=
new
WebvttCssStyle
();
expectedStyle
=
new
WebvttCssStyle
();
expectedStyle
.
setBackgroundColor
(
0xFF00FFFE
);
expectedStyle
.
setBackgroundColor
(
0xFF00FFFE
);
assertParserProduces
(
expectedStyle
,
styleBlock3
);
assertParserProduces
(
styleBlock3
,
expectedStyle
);
}
@Test
public
void
testParseMethodMultipleRulesInBlockInput
()
{
String
styleBlock
=
"::cue {\n background-color\n:#00fFFe} \n::cue {\n background-color\n:#00000000}\n"
;
WebvttCssStyle
expectedStyle
=
new
WebvttCssStyle
();
expectedStyle
.
setBackgroundColor
(
0xFF00FFFE
);
WebvttCssStyle
secondExpectedStyle
=
new
WebvttCssStyle
();
secondExpectedStyle
.
setBackgroundColor
(
0x000000
);
assertParserProduces
(
styleBlock
,
expectedStyle
,
secondExpectedStyle
);
}
}
@Test
@Test
...
@@ -116,7 +128,7 @@ public final class CssParserTest {
...
@@ -116,7 +128,7 @@ public final class CssParserTest {
expectedStyle
.
setFontFamily
(
"courier"
);
expectedStyle
.
setFontFamily
(
"courier"
);
expectedStyle
.
setBold
(
true
);
expectedStyle
.
setBold
(
true
);
assertParserProduces
(
expectedStyle
,
styleBlock
);
assertParserProduces
(
styleBlock
,
expectedStyle
);
}
}
@Test
@Test
...
@@ -128,7 +140,7 @@ public final class CssParserTest {
...
@@ -128,7 +140,7 @@ public final class CssParserTest {
expectedStyle
.
setBackgroundColor
(
0x190A0B0C
);
expectedStyle
.
setBackgroundColor
(
0x190A0B0C
);
expectedStyle
.
setFontColor
(
0xFF010101
);
expectedStyle
.
setFontColor
(
0xFF010101
);
assertParserProduces
(
expectedStyle
,
styleBlock
);
assertParserProduces
(
styleBlock
,
expectedStyle
);
}
}
@Test
@Test
...
@@ -203,25 +215,29 @@ public final class CssParserTest {
...
@@ -203,25 +215,29 @@ public final class CssParserTest {
assertThat
(
input
.
readLine
()).
isEqualTo
(
expectedLine
);
assertThat
(
input
.
readLine
()).
isEqualTo
(
expectedLine
);
}
}
private
void
assertParserProduces
(
WebvttCssStyle
expected
,
private
void
assertParserProduces
(
String
styleBlock
,
WebvttCssStyle
...
expectedStyles
)
{
String
styleBlock
){
ParsableByteArray
input
=
new
ParsableByteArray
(
Util
.
getUtf8Bytes
(
styleBlock
));
ParsableByteArray
input
=
new
ParsableByteArray
(
Util
.
getUtf8Bytes
(
styleBlock
));
WebvttCssStyle
actualElem
=
parser
.
parseBlock
(
input
);
List
<
WebvttCssStyle
>
styles
=
parser
.
parseBlock
(
input
);
assertThat
(
actualElem
.
hasBackgroundColor
()).
isEqualTo
(
expected
.
hasBackgroundColor
());
assertThat
(
styles
.
size
()).
isEqualTo
(
expectedStyles
.
length
);
if
(
expected
.
hasBackgroundColor
())
{
for
(
int
i
=
0
;
i
<
expectedStyles
.
length
;
i
++)
{
assertThat
(
actualElem
.
getBackgroundColor
()).
isEqualTo
(
expected
.
getBackgroundColor
());
WebvttCssStyle
expected
=
expectedStyles
[
i
];
}
WebvttCssStyle
actualElem
=
styles
.
get
(
i
);
assertThat
(
actualElem
.
hasFontColor
()).
isEqualTo
(
expected
.
hasFontColor
());
assertThat
(
actualElem
.
hasBackgroundColor
()).
isEqualTo
(
expected
.
hasBackgroundColor
());
if
(
expected
.
hasFontColor
())
{
if
(
expected
.
hasBackgroundColor
())
{
assertThat
(
actualElem
.
getFontColor
()).
isEqualTo
(
expected
.
getFontColor
());
assertThat
(
actualElem
.
getBackgroundColor
()).
isEqualTo
(
expected
.
getBackgroundColor
());
}
assertThat
(
actualElem
.
hasFontColor
()).
isEqualTo
(
expected
.
hasFontColor
());
if
(
expected
.
hasFontColor
())
{
assertThat
(
actualElem
.
getFontColor
()).
isEqualTo
(
expected
.
getFontColor
());
}
assertThat
(
actualElem
.
getFontFamily
()).
isEqualTo
(
expected
.
getFontFamily
());
assertThat
(
actualElem
.
getFontSize
()).
isEqualTo
(
expected
.
getFontSize
());
assertThat
(
actualElem
.
getFontSizeUnit
()).
isEqualTo
(
expected
.
getFontSizeUnit
());
assertThat
(
actualElem
.
getStyle
()).
isEqualTo
(
expected
.
getStyle
());
assertThat
(
actualElem
.
isLinethrough
()).
isEqualTo
(
expected
.
isLinethrough
());
assertThat
(
actualElem
.
isUnderline
()).
isEqualTo
(
expected
.
isUnderline
());
assertThat
(
actualElem
.
getTextAlign
()).
isEqualTo
(
expected
.
getTextAlign
());
}
}
assertThat
(
actualElem
.
getFontFamily
()).
isEqualTo
(
expected
.
getFontFamily
());
assertThat
(
actualElem
.
getFontSize
()).
isEqualTo
(
expected
.
getFontSize
());
assertThat
(
actualElem
.
getFontSizeUnit
()).
isEqualTo
(
expected
.
getFontSizeUnit
());
assertThat
(
actualElem
.
getStyle
()).
isEqualTo
(
expected
.
getStyle
());
assertThat
(
actualElem
.
isLinethrough
()).
isEqualTo
(
expected
.
isLinethrough
());
assertThat
(
actualElem
.
isUnderline
()).
isEqualTo
(
expected
.
isUnderline
());
assertThat
(
actualElem
.
getTextAlign
()).
isEqualTo
(
expected
.
getTextAlign
());
}
}
}
}
library/core/src/test/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelectorTest.java
View file @
f6297f4f
...
@@ -341,6 +341,76 @@ public final class DefaultTrackSelectorTest {
...
@@ -341,6 +341,76 @@ public final class DefaultTrackSelectorTest {
assertFixedSelection
(
result
.
selections
.
get
(
0
),
trackGroups
,
formatWithSelectionFlag
);
assertFixedSelection
(
result
.
selections
.
get
(
0
),
trackGroups
,
formatWithSelectionFlag
);
}
}
/** Tests that adaptive audio track selections respect the maximum audio bitrate. */
public
void
testSelectAdaptiveAudioTrackGroupWithMaxBitrate
()
throws
ExoPlaybackException
{
Format
format128k
=
Format
.
createAudioSampleFormat
(
/* id= */
"128"
,
/* sampleMimeType= */
MimeTypes
.
AUDIO_AAC
,
/* codecs= */
"mp4a.40.2"
,
/* bitrate= */
128
*
1024
,
/* maxInputSize= */
Format
.
NO_VALUE
,
/* channelCount= */
2
,
/* sampleRate= */
44100
,
/* initializationData= */
null
,
/* drmInitData= */
null
,
/* selectionFlags= */
0
,
/* language= */
null
);
Format
format192k
=
Format
.
createAudioSampleFormat
(
/* id= */
"192"
,
/* sampleMimeType= */
MimeTypes
.
AUDIO_AAC
,
/* codecs= */
"mp4a.40.2"
,
/* bitrate= */
192
*
1024
,
/* maxInputSize= */
Format
.
NO_VALUE
,
/* channelCount= */
2
,
/* sampleRate= */
44100
,
/* initializationData= */
null
,
/* drmInitData= */
null
,
/* selectionFlags= */
0
,
/* language= */
null
);
Format
format256k
=
Format
.
createAudioSampleFormat
(
/* id= */
"256"
,
/* sampleMimeType= */
MimeTypes
.
AUDIO_AAC
,
/* codecs= */
"mp4a.40.2"
,
/* bitrate= */
256
*
1024
,
/* maxInputSize= */
Format
.
NO_VALUE
,
/* channelCount= */
2
,
/* sampleRate= */
44100
,
/* initializationData= */
null
,
/* drmInitData= */
null
,
/* selectionFlags= */
0
,
/* language= */
null
);
RendererCapabilities
[]
rendererCapabilities
=
{
ALL_AUDIO_FORMAT_SUPPORTED_RENDERER_CAPABILITIES
};
TrackGroupArray
trackGroups
=
new
TrackGroupArray
(
new
TrackGroup
(
format192k
,
format128k
,
format256k
));
TrackSelectorResult
result
=
trackSelector
.
selectTracks
(
rendererCapabilities
,
trackGroups
,
periodId
,
TIMELINE
);
assertAdaptiveSelection
(
result
.
selections
.
get
(
0
),
trackGroups
.
get
(
0
),
0
,
1
,
2
);
trackSelector
.
setParameters
(
trackSelector
.
buildUponParameters
().
setMaxAudioBitrate
(
256
*
1024
-
1
));
result
=
trackSelector
.
selectTracks
(
rendererCapabilities
,
trackGroups
,
periodId
,
TIMELINE
);
assertAdaptiveSelection
(
result
.
selections
.
get
(
0
),
trackGroups
.
get
(
0
),
0
,
1
);
trackSelector
.
setParameters
(
trackSelector
.
buildUponParameters
().
setMaxAudioBitrate
(
192
*
1024
));
result
=
trackSelector
.
selectTracks
(
rendererCapabilities
,
trackGroups
,
periodId
,
TIMELINE
);
assertAdaptiveSelection
(
result
.
selections
.
get
(
0
),
trackGroups
.
get
(
0
),
0
,
1
);
trackSelector
.
setParameters
(
trackSelector
.
buildUponParameters
().
setMaxAudioBitrate
(
192
*
1024
-
1
));
result
=
trackSelector
.
selectTracks
(
rendererCapabilities
,
trackGroups
,
periodId
,
TIMELINE
);
assertAdaptiveSelection
(
result
.
selections
.
get
(
0
),
trackGroups
.
get
(
0
),
1
);
trackSelector
.
setParameters
(
trackSelector
.
buildUponParameters
().
setMaxAudioBitrate
(
10
));
result
=
trackSelector
.
selectTracks
(
rendererCapabilities
,
trackGroups
,
periodId
,
TIMELINE
);
assertAdaptiveSelection
(
result
.
selections
.
get
(
0
),
trackGroups
.
get
(
0
),
1
);
}
/**
/**
* Tests that track selector will select audio track with language that match preferred language
* Tests that track selector will select audio track with language that match preferred language
* given by {@link Parameters}.
* given by {@link Parameters}.
...
@@ -893,7 +963,6 @@ public final class DefaultTrackSelectorTest {
...
@@ -893,7 +963,6 @@ public final class DefaultTrackSelectorTest {
Format
forcedDefault
=
Format
forcedDefault
=
buildTextFormat
(
"forcedDefault"
,
"eng"
,
C
.
SELECTION_FLAG_FORCED
|
C
.
SELECTION_FLAG_DEFAULT
);
buildTextFormat
(
"forcedDefault"
,
"eng"
,
C
.
SELECTION_FLAG_FORCED
|
C
.
SELECTION_FLAG_DEFAULT
);
Format
defaultOnly
=
buildTextFormat
(
"defaultOnly"
,
"eng"
,
C
.
SELECTION_FLAG_DEFAULT
);
Format
defaultOnly
=
buildTextFormat
(
"defaultOnly"
,
"eng"
,
C
.
SELECTION_FLAG_DEFAULT
);
Format
forcedOnlySpanish
=
buildTextFormat
(
"forcedOnlySpanish"
,
"spa"
,
C
.
SELECTION_FLAG_FORCED
);
Format
noFlag
=
buildTextFormat
(
"noFlag"
,
"eng"
);
Format
noFlag
=
buildTextFormat
(
"noFlag"
,
"eng"
);
RendererCapabilities
[]
textRendererCapabilities
=
RendererCapabilities
[]
textRendererCapabilities
=
...
...
library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DashDownloaderTest.java
View file @
f6297f4f
...
@@ -35,6 +35,7 @@ import com.google.android.exoplayer2.offline.Downloader;
...
@@ -35,6 +35,7 @@ import com.google.android.exoplayer2.offline.Downloader;
import
com.google.android.exoplayer2.offline.DownloaderConstructorHelper
;
import
com.google.android.exoplayer2.offline.DownloaderConstructorHelper
;
import
com.google.android.exoplayer2.offline.DownloaderFactory
;
import
com.google.android.exoplayer2.offline.DownloaderFactory
;
import
com.google.android.exoplayer2.offline.StreamKey
;
import
com.google.android.exoplayer2.offline.StreamKey
;
import
com.google.android.exoplayer2.testutil.CacheAsserts.RequestSet
;
import
com.google.android.exoplayer2.testutil.FakeDataSet
;
import
com.google.android.exoplayer2.testutil.FakeDataSet
;
import
com.google.android.exoplayer2.testutil.FakeDataSource
;
import
com.google.android.exoplayer2.testutil.FakeDataSource
;
import
com.google.android.exoplayer2.testutil.FakeDataSource.Factory
;
import
com.google.android.exoplayer2.testutil.FakeDataSource.Factory
;
...
@@ -108,7 +109,7 @@ public class DashDownloaderTest {
...
@@ -108,7 +109,7 @@ public class DashDownloaderTest {
DashDownloader
dashDownloader
=
getDashDownloader
(
fakeDataSet
,
new
StreamKey
(
0
,
0
,
0
));
DashDownloader
dashDownloader
=
getDashDownloader
(
fakeDataSet
,
new
StreamKey
(
0
,
0
,
0
));
dashDownloader
.
download
(
progressListener
);
dashDownloader
.
download
(
progressListener
);
assertCachedData
(
cache
,
fakeDataSet
);
assertCachedData
(
cache
,
new
RequestSet
(
fakeDataSet
).
useBoundedDataSpecFor
(
"audio_init_data"
)
);
}
}
@Test
@Test
...
@@ -127,7 +128,7 @@ public class DashDownloaderTest {
...
@@ -127,7 +128,7 @@ public class DashDownloaderTest {
DashDownloader
dashDownloader
=
getDashDownloader
(
fakeDataSet
,
new
StreamKey
(
0
,
0
,
0
));
DashDownloader
dashDownloader
=
getDashDownloader
(
fakeDataSet
,
new
StreamKey
(
0
,
0
,
0
));
dashDownloader
.
download
(
progressListener
);
dashDownloader
.
download
(
progressListener
);
assertCachedData
(
cache
,
fakeDataSet
);
assertCachedData
(
cache
,
new
RequestSet
(
fakeDataSet
).
useBoundedDataSpecFor
(
"audio_init_data"
)
);
}
}
@Test
@Test
...
@@ -146,7 +147,7 @@ public class DashDownloaderTest {
...
@@ -146,7 +147,7 @@ public class DashDownloaderTest {
DashDownloader
dashDownloader
=
DashDownloader
dashDownloader
=
getDashDownloader
(
fakeDataSet
,
new
StreamKey
(
0
,
0
,
0
),
new
StreamKey
(
0
,
1
,
0
));
getDashDownloader
(
fakeDataSet
,
new
StreamKey
(
0
,
0
,
0
),
new
StreamKey
(
0
,
1
,
0
));
dashDownloader
.
download
(
progressListener
);
dashDownloader
.
download
(
progressListener
);
assertCachedData
(
cache
,
fakeDataSet
);
assertCachedData
(
cache
,
new
RequestSet
(
fakeDataSet
).
useBoundedDataSpecFor
(
"audio_init_data"
)
);
}
}
@Test
@Test
...
@@ -167,7 +168,7 @@ public class DashDownloaderTest {
...
@@ -167,7 +168,7 @@ public class DashDownloaderTest {
DashDownloader
dashDownloader
=
getDashDownloader
(
fakeDataSet
);
DashDownloader
dashDownloader
=
getDashDownloader
(
fakeDataSet
);
dashDownloader
.
download
(
progressListener
);
dashDownloader
.
download
(
progressListener
);
assertCachedData
(
cache
,
fakeDataSet
);
assertCachedData
(
cache
,
new
RequestSet
(
fakeDataSet
).
useBoundedDataSpecFor
(
"audio_init_data"
)
);
}
}
@Test
@Test
...
@@ -256,7 +257,7 @@ public class DashDownloaderTest {
...
@@ -256,7 +257,7 @@ public class DashDownloaderTest {
// Expected.
// Expected.
}
}
dashDownloader
.
download
(
progressListener
);
dashDownloader
.
download
(
progressListener
);
assertCachedData
(
cache
,
fakeDataSet
);
assertCachedData
(
cache
,
new
RequestSet
(
fakeDataSet
).
useBoundedDataSpecFor
(
"audio_init_data"
)
);
}
}
@Test
@Test
...
...
library/dash/src/test/java/com/google/android/exoplayer2/source/dash/offline/DownloadManagerDashTest.java
View file @
f6297f4f
...
@@ -33,6 +33,7 @@ import com.google.android.exoplayer2.offline.DownloadRequest;
...
@@ -33,6 +33,7 @@ import com.google.android.exoplayer2.offline.DownloadRequest;
import
com.google.android.exoplayer2.offline.DownloaderConstructorHelper
;
import
com.google.android.exoplayer2.offline.DownloaderConstructorHelper
;
import
com.google.android.exoplayer2.offline.StreamKey
;
import
com.google.android.exoplayer2.offline.StreamKey
;
import
com.google.android.exoplayer2.scheduler.Requirements
;
import
com.google.android.exoplayer2.scheduler.Requirements
;
import
com.google.android.exoplayer2.testutil.CacheAsserts.RequestSet
;
import
com.google.android.exoplayer2.testutil.DummyMainThread
;
import
com.google.android.exoplayer2.testutil.DummyMainThread
;
import
com.google.android.exoplayer2.testutil.DummyMainThread.TestRunnable
;
import
com.google.android.exoplayer2.testutil.DummyMainThread.TestRunnable
;
import
com.google.android.exoplayer2.testutil.FakeDataSet
;
import
com.google.android.exoplayer2.testutil.FakeDataSet
;
...
@@ -154,7 +155,7 @@ public class DownloadManagerDashTest {
...
@@ -154,7 +155,7 @@ public class DownloadManagerDashTest {
public
void
testHandleDownloadRequest
()
throws
Throwable
{
public
void
testHandleDownloadRequest
()
throws
Throwable
{
handleDownloadRequest
(
fakeStreamKey1
,
fakeStreamKey2
);
handleDownloadRequest
(
fakeStreamKey1
,
fakeStreamKey2
);
blockUntilTasksCompleteAndThrowAnyDownloadError
();
blockUntilTasksCompleteAndThrowAnyDownloadError
();
assertCachedData
(
cache
,
fakeDataSet
);
assertCachedData
(
cache
,
new
RequestSet
(
fakeDataSet
).
useBoundedDataSpecFor
(
"audio_init_data"
)
);
}
}
@Test
@Test
...
@@ -162,7 +163,7 @@ public class DownloadManagerDashTest {
...
@@ -162,7 +163,7 @@ public class DownloadManagerDashTest {
handleDownloadRequest
(
fakeStreamKey1
);
handleDownloadRequest
(
fakeStreamKey1
);
handleDownloadRequest
(
fakeStreamKey2
);
handleDownloadRequest
(
fakeStreamKey2
);
blockUntilTasksCompleteAndThrowAnyDownloadError
();
blockUntilTasksCompleteAndThrowAnyDownloadError
();
assertCachedData
(
cache
,
fakeDataSet
);
assertCachedData
(
cache
,
new
RequestSet
(
fakeDataSet
).
useBoundedDataSpecFor
(
"audio_init_data"
)
);
}
}
@Test
@Test
...
@@ -176,7 +177,7 @@ public class DownloadManagerDashTest {
...
@@ -176,7 +177,7 @@ public class DownloadManagerDashTest {
handleDownloadRequest
(
fakeStreamKey1
);
handleDownloadRequest
(
fakeStreamKey1
);
blockUntilTasksCompleteAndThrowAnyDownloadError
();
blockUntilTasksCompleteAndThrowAnyDownloadError
();
assertCachedData
(
cache
,
fakeDataSet
);
assertCachedData
(
cache
,
new
RequestSet
(
fakeDataSet
).
useBoundedDataSpecFor
(
"audio_init_data"
)
);
}
}
@Test
@Test
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java
View file @
f6297f4f
...
@@ -322,6 +322,7 @@ import java.util.Map;
...
@@ -322,6 +322,7 @@ import java.util.Map;
if
(
enabledTrackGroupCount
==
0
)
{
if
(
enabledTrackGroupCount
==
0
)
{
chunkSource
.
reset
();
chunkSource
.
reset
();
downstreamTrackFormat
=
null
;
downstreamTrackFormat
=
null
;
pendingResetUpstreamFormats
=
true
;
mediaChunks
.
clear
();
mediaChunks
.
clear
();
if
(
loader
.
isLoading
())
{
if
(
loader
.
isLoading
())
{
if
(
sampleQueuesBuilt
)
{
if
(
sampleQueuesBuilt
)
{
...
...
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/offline/HlsDownloaderTest.java
View file @
f6297f4f
...
@@ -44,6 +44,7 @@ import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
...
@@ -44,6 +44,7 @@ import com.google.android.exoplayer2.offline.DownloaderConstructorHelper;
import
com.google.android.exoplayer2.offline.DownloaderFactory
;
import
com.google.android.exoplayer2.offline.DownloaderFactory
;
import
com.google.android.exoplayer2.offline.StreamKey
;
import
com.google.android.exoplayer2.offline.StreamKey
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist
;
import
com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist
;
import
com.google.android.exoplayer2.testutil.CacheAsserts.RequestSet
;
import
com.google.android.exoplayer2.testutil.FakeDataSet
;
import
com.google.android.exoplayer2.testutil.FakeDataSet
;
import
com.google.android.exoplayer2.testutil.FakeDataSource.Factory
;
import
com.google.android.exoplayer2.testutil.FakeDataSource.Factory
;
import
com.google.android.exoplayer2.upstream.DummyDataSource
;
import
com.google.android.exoplayer2.upstream.DummyDataSource
;
...
@@ -129,12 +130,13 @@ public class HlsDownloaderTest {
...
@@ -129,12 +130,13 @@ public class HlsDownloaderTest {
assertCachedData
(
assertCachedData
(
cache
,
cache
,
fakeDataSet
,
new
RequestSet
(
fakeDataSet
)
MASTER_PLAYLIST_URI
,
.
subset
(
MEDIA_PLAYLIST_1_URI
,
MASTER_PLAYLIST_URI
,
MEDIA_PLAYLIST_1_DIR
+
"fileSequence0.ts"
,
MEDIA_PLAYLIST_1_URI
,
MEDIA_PLAYLIST_1_DIR
+
"fileSequence1.ts"
,
MEDIA_PLAYLIST_1_DIR
+
"fileSequence0.ts"
,
MEDIA_PLAYLIST_1_DIR
+
"fileSequence2.ts"
);
MEDIA_PLAYLIST_1_DIR
+
"fileSequence1.ts"
,
MEDIA_PLAYLIST_1_DIR
+
"fileSequence2.ts"
));
}
}
@Test
@Test
...
@@ -186,11 +188,12 @@ public class HlsDownloaderTest {
...
@@ -186,11 +188,12 @@ public class HlsDownloaderTest {
assertCachedData
(
assertCachedData
(
cache
,
cache
,
fakeDataSet
,
new
RequestSet
(
fakeDataSet
)
MEDIA_PLAYLIST_1_URI
,
.
subset
(
MEDIA_PLAYLIST_1_DIR
+
"fileSequence0.ts"
,
MEDIA_PLAYLIST_1_URI
,
MEDIA_PLAYLIST_1_DIR
+
"fileSequence1.ts"
,
MEDIA_PLAYLIST_1_DIR
+
"fileSequence0.ts"
,
MEDIA_PLAYLIST_1_DIR
+
"fileSequence2.ts"
);
MEDIA_PLAYLIST_1_DIR
+
"fileSequence1.ts"
,
MEDIA_PLAYLIST_1_DIR
+
"fileSequence2.ts"
));
}
}
@Test
@Test
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java
View file @
f6297f4f
...
@@ -220,11 +220,26 @@ public class DefaultTimeBar extends View implements TimeBar {
...
@@ -220,11 +220,26 @@ public class DefaultTimeBar extends View implements TimeBar {
private
@Nullable
long
[]
adGroupTimesMs
;
private
@Nullable
long
[]
adGroupTimesMs
;
private
@Nullable
boolean
[]
playedAdGroups
;
private
@Nullable
boolean
[]
playedAdGroups
;
/** Creates a new time bar. */
public
DefaultTimeBar
(
Context
context
)
{
this
(
context
,
null
);
}
public
DefaultTimeBar
(
Context
context
,
@Nullable
AttributeSet
attrs
)
{
this
(
context
,
attrs
,
0
);
}
public
DefaultTimeBar
(
Context
context
,
@Nullable
AttributeSet
attrs
,
int
defStyleAttr
)
{
this
(
context
,
attrs
,
defStyleAttr
,
attrs
);
}
// Suppress warnings due to usage of View methods in the constructor.
// Suppress warnings due to usage of View methods in the constructor.
@SuppressWarnings
(
"nullness:method.invocation.invalid"
)
@SuppressWarnings
(
"nullness:method.invocation.invalid"
)
public
DefaultTimeBar
(
Context
context
,
AttributeSet
attrs
)
{
public
DefaultTimeBar
(
super
(
context
,
attrs
);
Context
context
,
@Nullable
AttributeSet
attrs
,
int
defStyleAttr
,
@Nullable
AttributeSet
timebarAttrs
)
{
super
(
context
,
attrs
,
defStyleAttr
);
seekBounds
=
new
Rect
();
seekBounds
=
new
Rect
();
progressBar
=
new
Rect
();
progressBar
=
new
Rect
();
bufferedBar
=
new
Rect
();
bufferedBar
=
new
Rect
();
...
@@ -251,9 +266,9 @@ public class DefaultTimeBar extends View implements TimeBar {
...
@@ -251,9 +266,9 @@ public class DefaultTimeBar extends View implements TimeBar {
int
defaultScrubberEnabledSize
=
dpToPx
(
density
,
DEFAULT_SCRUBBER_ENABLED_SIZE_DP
);
int
defaultScrubberEnabledSize
=
dpToPx
(
density
,
DEFAULT_SCRUBBER_ENABLED_SIZE_DP
);
int
defaultScrubberDisabledSize
=
dpToPx
(
density
,
DEFAULT_SCRUBBER_DISABLED_SIZE_DP
);
int
defaultScrubberDisabledSize
=
dpToPx
(
density
,
DEFAULT_SCRUBBER_DISABLED_SIZE_DP
);
int
defaultScrubberDraggedSize
=
dpToPx
(
density
,
DEFAULT_SCRUBBER_DRAGGED_SIZE_DP
);
int
defaultScrubberDraggedSize
=
dpToPx
(
density
,
DEFAULT_SCRUBBER_DRAGGED_SIZE_DP
);
if
(
a
ttrs
!=
null
)
{
if
(
timebarA
ttrs
!=
null
)
{
TypedArray
a
=
context
.
getTheme
().
obtainStyledAttributes
(
attrs
,
R
.
styleable
.
DefaultTimeBar
,
0
,
TypedArray
a
=
0
);
context
.
getTheme
().
obtainStyledAttributes
(
timebarAttrs
,
R
.
styleable
.
DefaultTimeBar
,
0
,
0
);
try
{
try
{
scrubberDrawable
=
a
.
getDrawable
(
R
.
styleable
.
DefaultTimeBar_scrubber_drawable
);
scrubberDrawable
=
a
.
getDrawable
(
R
.
styleable
.
DefaultTimeBar_scrubber_drawable
);
if
(
scrubberDrawable
!=
null
)
{
if
(
scrubberDrawable
!=
null
)
{
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java
View file @
f6297f4f
...
@@ -28,6 +28,7 @@ import android.view.KeyEvent;
...
@@ -28,6 +28,7 @@ import android.view.KeyEvent;
import
android.view.LayoutInflater
;
import
android.view.LayoutInflater
;
import
android.view.MotionEvent
;
import
android.view.MotionEvent
;
import
android.view.View
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.FrameLayout
;
import
android.widget.FrameLayout
;
import
android.widget.ImageView
;
import
android.widget.ImageView
;
import
android.widget.TextView
;
import
android.widget.TextView
;
...
@@ -97,6 +98,9 @@ import java.util.Locale;
...
@@ -97,6 +98,9 @@ import java.util.Locale;
* <li>Corresponding method: None
* <li>Corresponding method: None
* <li>Default: {@code R.layout.exo_player_control_view}
* <li>Default: {@code R.layout.exo_player_control_view}
* </ul>
* </ul>
* <li>All attributes that can be set on {@link DefaultTimeBar} can also be set on a
* PlayerControlView, and will be propagated to the inflated {@link DefaultTimeBar} unless the
* layout is overridden to specify a custom {@code exo_progress} (see below).
* </ul>
* </ul>
*
*
* <h3>Overriding the layout file</h3>
* <h3>Overriding the layout file</h3>
...
@@ -154,7 +158,15 @@ import java.util.Locale;
...
@@ -154,7 +158,15 @@ import java.util.Locale;
* <ul>
* <ul>
* <li>Type: {@link TextView}
* <li>Type: {@link TextView}
* </ul>
* </ul>
* <li><b>{@code exo_progress_placeholder}</b> - A placeholder that's replaced with the inflated
* {@link DefaultTimeBar}. Ignored if an {@code exo_progress} view exists.
* <ul>
* <li>Type: {@link View}
* </ul>
* <li><b>{@code exo_progress}</b> - Time bar that's updated during playback and allows seeking.
* <li><b>{@code exo_progress}</b> - Time bar that's updated during playback and allows seeking.
* {@link DefaultTimeBar} attributes set on the PlayerControlView will not be automatically
* propagated through to this instance. If a view exists with this id, any {@code
* exo_progress_placeholder} view will be ignored.
* <ul>
* <ul>
* <li>Type: {@link TimeBar}
* <li>Type: {@link TimeBar}
* </ul>
* </ul>
...
@@ -188,6 +200,18 @@ public class PlayerControlView extends FrameLayout {
...
@@ -188,6 +200,18 @@ public class PlayerControlView extends FrameLayout {
void
onVisibilityChange
(
int
visibility
);
void
onVisibilityChange
(
int
visibility
);
}
}
/** Listener to be notified when progress has been updated. */
public
interface
ProgressUpdateListener
{
/**
* Called when progress needs to be updated.
*
* @param position The current position.
* @param bufferedPosition The current buffered position.
*/
void
onProgressUpdate
(
long
position
,
long
bufferedPosition
);
}
/** The default fast forward increment, in milliseconds. */
/** The default fast forward increment, in milliseconds. */
public
static
final
int
DEFAULT_FAST_FORWARD_MS
=
15000
;
public
static
final
int
DEFAULT_FAST_FORWARD_MS
=
15000
;
/** The default rewind increment, in milliseconds. */
/** The default rewind increment, in milliseconds. */
...
@@ -235,7 +259,8 @@ public class PlayerControlView extends FrameLayout {
...
@@ -235,7 +259,8 @@ public class PlayerControlView extends FrameLayout {
@Nullable
private
Player
player
;
@Nullable
private
Player
player
;
private
com
.
google
.
android
.
exoplayer2
.
ControlDispatcher
controlDispatcher
;
private
com
.
google
.
android
.
exoplayer2
.
ControlDispatcher
controlDispatcher
;
private
VisibilityListener
visibilityListener
;
@Nullable
private
VisibilityListener
visibilityListener
;
@Nullable
private
ProgressUpdateListener
progressUpdateListener
;
@Nullable
private
PlaybackPreparer
playbackPreparer
;
@Nullable
private
PlaybackPreparer
playbackPreparer
;
private
boolean
isAttachedToWindow
;
private
boolean
isAttachedToWindow
;
...
@@ -317,9 +342,27 @@ public class PlayerControlView extends FrameLayout {
...
@@ -317,9 +342,27 @@ public class PlayerControlView extends FrameLayout {
LayoutInflater
.
from
(
context
).
inflate
(
controllerLayoutId
,
this
);
LayoutInflater
.
from
(
context
).
inflate
(
controllerLayoutId
,
this
);
setDescendantFocusability
(
FOCUS_AFTER_DESCENDANTS
);
setDescendantFocusability
(
FOCUS_AFTER_DESCENDANTS
);
TimeBar
customTimeBar
=
findViewById
(
R
.
id
.
exo_progress
);
View
timeBarPlaceholder
=
findViewById
(
R
.
id
.
exo_progress_placeholder
);
if
(
customTimeBar
!=
null
)
{
timeBar
=
customTimeBar
;
}
else
if
(
timeBarPlaceholder
!=
null
)
{
// Propagate attrs as timebarAttrs so that DefaultTimeBar's custom attributes are transferred,
// but standard attributes (e.g. background) are not.
DefaultTimeBar
defaultTimeBar
=
new
DefaultTimeBar
(
context
,
null
,
0
,
playbackAttrs
);
defaultTimeBar
.
setId
(
R
.
id
.
exo_progress
);
defaultTimeBar
.
setLayoutParams
(
timeBarPlaceholder
.
getLayoutParams
());
ViewGroup
parent
=
((
ViewGroup
)
timeBarPlaceholder
.
getParent
());
int
timeBarIndex
=
parent
.
indexOfChild
(
timeBarPlaceholder
);
parent
.
removeView
(
timeBarPlaceholder
);
parent
.
addView
(
defaultTimeBar
,
timeBarIndex
);
timeBar
=
defaultTimeBar
;
}
else
{
timeBar
=
null
;
}
durationView
=
findViewById
(
R
.
id
.
exo_duration
);
durationView
=
findViewById
(
R
.
id
.
exo_duration
);
positionView
=
findViewById
(
R
.
id
.
exo_position
);
positionView
=
findViewById
(
R
.
id
.
exo_position
);
timeBar
=
findViewById
(
R
.
id
.
exo_progress
);
if
(
timeBar
!=
null
)
{
if
(
timeBar
!=
null
)
{
timeBar
.
addListener
(
componentListener
);
timeBar
.
addListener
(
componentListener
);
}
}
...
@@ -455,6 +498,15 @@ public class PlayerControlView extends FrameLayout {
...
@@ -455,6 +498,15 @@ public class PlayerControlView extends FrameLayout {
}
}
/**
/**
* Sets the {@link ProgressUpdateListener}.
*
* @param listener The listener to be notified about when progress is updated.
*/
public
void
setProgressUpdateListener
(
@Nullable
ProgressUpdateListener
listener
)
{
this
.
progressUpdateListener
=
listener
;
}
/**
* Sets the {@link PlaybackPreparer}.
* Sets the {@link PlaybackPreparer}.
*
*
* @param playbackPreparer The {@link PlaybackPreparer}.
* @param playbackPreparer The {@link PlaybackPreparer}.
...
@@ -855,6 +907,9 @@ public class PlayerControlView extends FrameLayout {
...
@@ -855,6 +907,9 @@ public class PlayerControlView extends FrameLayout {
timeBar
.
setPosition
(
position
);
timeBar
.
setPosition
(
position
);
timeBar
.
setBufferedPosition
(
bufferedPosition
);
timeBar
.
setBufferedPosition
(
bufferedPosition
);
}
}
if
(
progressUpdateListener
!=
null
)
{
progressUpdateListener
.
onProgressUpdate
(
position
,
bufferedPosition
);
}
// Cancel any pending updates and schedule a new one if necessary.
// Cancel any pending updates and schedule a new one if necessary.
removeCallbacks
(
updateProgressAction
);
removeCallbacks
(
updateProgressAction
);
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java
View file @
f6297f4f
...
@@ -966,7 +966,8 @@ public class PlayerNotificationManager {
...
@@ -966,7 +966,8 @@ public class PlayerNotificationManager {
@Nullable
NotificationCompat
.
Builder
builder
,
@Nullable
NotificationCompat
.
Builder
builder
,
boolean
ongoing
,
boolean
ongoing
,
@Nullable
Bitmap
largeIcon
)
{
@Nullable
Bitmap
largeIcon
)
{
if
(
player
.
getPlaybackState
()
==
Player
.
STATE_IDLE
)
{
if
(
player
.
getPlaybackState
()
==
Player
.
STATE_IDLE
&&
(
player
.
getCurrentTimeline
().
isEmpty
()
||
playbackPreparer
==
null
))
{
builderActions
=
null
;
builderActions
=
null
;
return
null
;
return
null
;
}
}
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
View file @
f6297f4f
...
@@ -35,7 +35,6 @@ import android.util.AttributeSet;
...
@@ -35,7 +35,6 @@ import android.util.AttributeSet;
import
android.view.KeyEvent
;
import
android.view.KeyEvent
;
import
android.view.LayoutInflater
;
import
android.view.LayoutInflater
;
import
android.view.MotionEvent
;
import
android.view.MotionEvent
;
import
android.view.Surface
;
import
android.view.SurfaceView
;
import
android.view.SurfaceView
;
import
android.view.TextureView
;
import
android.view.TextureView
;
import
android.view.View
;
import
android.view.View
;
...
@@ -50,7 +49,6 @@ import com.google.android.exoplayer2.ExoPlaybackException;
...
@@ -50,7 +49,6 @@ import com.google.android.exoplayer2.ExoPlaybackException;
import
com.google.android.exoplayer2.PlaybackPreparer
;
import
com.google.android.exoplayer2.PlaybackPreparer
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.Player.DiscontinuityReason
;
import
com.google.android.exoplayer2.Player.DiscontinuityReason
;
import
com.google.android.exoplayer2.Player.VideoComponent
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.metadata.id3.ApicFrame
;
import
com.google.android.exoplayer2.metadata.id3.ApicFrame
;
import
com.google.android.exoplayer2.source.TrackGroupArray
;
import
com.google.android.exoplayer2.source.TrackGroupArray
;
...
@@ -165,9 +163,10 @@ import java.util.List;
...
@@ -165,9 +163,10 @@ import java.util.List;
* <li>Corresponding method: None
* <li>Corresponding method: None
* <li>Default: {@code R.layout.exo_player_control_view}
* <li>Default: {@code R.layout.exo_player_control_view}
* </ul>
* </ul>
* <li>All attributes that can be set on a {@link PlayerControlView} can also be set on a
* <li>All attributes that can be set on {@link PlayerControlView} and {@link DefaultTimeBar} can
* PlayerView, and will be propagated to the inflated {@link PlayerControlView} unless the
* also be set on a PlayerView, and will be propagated to the inflated {@link
* layout is overridden to specify a custom {@code exo_controller} (see below).
* PlayerControlView} unless the layout is overridden to specify a custom {@code
* exo_controller} (see below).
* </ul>
* </ul>
*
*
* <h3>Overriding the layout file</h3>
* <h3>Overriding the layout file</h3>
...
@@ -217,9 +216,10 @@ import java.util.List;
...
@@ -217,9 +216,10 @@ import java.util.List;
* <li>Type: {@link View}
* <li>Type: {@link View}
* </ul>
* </ul>
* <li><b>{@code exo_controller}</b> - An already inflated {@link PlayerControlView}. Allows use
* <li><b>{@code exo_controller}</b> - An already inflated {@link PlayerControlView}. Allows use
* of a custom extension of {@link PlayerControlView}. Note that attributes such as {@code
* of a custom extension of {@link PlayerControlView}. {@link PlayerControlView} and {@link
* rewind_increment} will not be automatically propagated through to this instance. If a view
* DefaultTimeBar} attributes set on the PlayerView will not be automatically propagated
* exists with this id, any {@code exo_controller_placeholder} view will be ignored.
* through to this instance. If a view exists with this id, any {@code
* exo_controller_placeholder} view will be ignored.
* <ul>
* <ul>
* <li>Type: {@link PlayerControlView}
* <li>Type: {@link PlayerControlView}
* </ul>
* </ul>
...
@@ -303,6 +303,7 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
...
@@ -303,6 +303,7 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
private
boolean
controllerHideDuringAds
;
private
boolean
controllerHideDuringAds
;
private
boolean
controllerHideOnTouch
;
private
boolean
controllerHideOnTouch
;
private
int
textureViewRotation
;
private
int
textureViewRotation
;
private
boolean
isTouching
;
public
PlayerView
(
Context
context
)
{
public
PlayerView
(
Context
context
)
{
this
(
context
,
null
);
this
(
context
,
null
);
...
@@ -405,7 +406,6 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
...
@@ -405,7 +406,6 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
break
;
break
;
case
SURFACE_TYPE_MONO360_VIEW:
case
SURFACE_TYPE_MONO360_VIEW:
SphericalSurfaceView
sphericalSurfaceView
=
new
SphericalSurfaceView
(
context
);
SphericalSurfaceView
sphericalSurfaceView
=
new
SphericalSurfaceView
(
context
);
sphericalSurfaceView
.
setSurfaceListener
(
componentListener
);
sphericalSurfaceView
.
setSingleTapListener
(
componentListener
);
sphericalSurfaceView
.
setSingleTapListener
(
componentListener
);
surfaceView
=
sphericalSurfaceView
;
surfaceView
=
sphericalSurfaceView
;
break
;
break
;
...
@@ -459,8 +459,9 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
...
@@ -459,8 +459,9 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
this
.
controller
=
customController
;
this
.
controller
=
customController
;
}
else
if
(
controllerPlaceholder
!=
null
)
{
}
else
if
(
controllerPlaceholder
!=
null
)
{
// Propagate attrs as playbackAttrs so that PlayerControlView's custom attributes are
// Propagate attrs as playbackAttrs so that PlayerControlView's custom attributes are
// transferred, but standard
FrameLayout
attributes (e.g. background) are not.
// transferred, but standard attributes (e.g. background) are not.
this
.
controller
=
new
PlayerControlView
(
context
,
null
,
0
,
attrs
);
this
.
controller
=
new
PlayerControlView
(
context
,
null
,
0
,
attrs
);
controller
.
setId
(
R
.
id
.
exo_controller
);
controller
.
setLayoutParams
(
controllerPlaceholder
.
getLayoutParams
());
controller
.
setLayoutParams
(
controllerPlaceholder
.
getLayoutParams
());
ViewGroup
parent
=
((
ViewGroup
)
controllerPlaceholder
.
getParent
());
ViewGroup
parent
=
((
ViewGroup
)
controllerPlaceholder
.
getParent
());
int
controllerIndex
=
parent
.
indexOfChild
(
controllerPlaceholder
);
int
controllerIndex
=
parent
.
indexOfChild
(
controllerPlaceholder
);
...
@@ -771,11 +772,20 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
...
@@ -771,11 +772,20 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
if
(
player
!=
null
&&
player
.
isPlayingAd
())
{
if
(
player
!=
null
&&
player
.
isPlayingAd
())
{
return
super
.
dispatchKeyEvent
(
event
);
return
super
.
dispatchKeyEvent
(
event
);
}
}
boolean
isDpadWhenControlHidden
=
isDpadKey
(
event
.
getKeyCode
())
&&
useController
&&
!
controller
.
isVisible
();
boolean
isDpadAndUseController
=
isDpadKey
(
event
.
getKeyCode
())
&&
useController
;
boolean
handled
=
boolean
handled
=
false
;
isDpadWhenControlHidden
||
dispatchMediaKeyEvent
(
event
)
||
super
.
dispatchKeyEvent
(
event
);
if
(
isDpadAndUseController
&&
!
controller
.
isVisible
())
{
if
(
handled
)
{
// Handle the key event by showing the controller.
maybeShowController
(
true
);
handled
=
true
;
}
else
if
(
dispatchMediaKeyEvent
(
event
)
||
super
.
dispatchKeyEvent
(
event
))
{
// The key event was handled as a media key or by the super class. We should also show the
// controller, or extend its show timeout if already visible.
maybeShowController
(
true
);
handled
=
true
;
}
else
if
(
isDpadAndUseController
)
{
// The key event wasn't handled, but we should extend the controller's show timeout.
maybeShowController
(
true
);
maybeShowController
(
true
);
}
}
return
handled
;
return
handled
;
...
@@ -1039,11 +1049,21 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
...
@@ -1039,11 +1049,21 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
}
}
@Override
@Override
public
boolean
onTouchEvent
(
MotionEvent
ev
)
{
public
boolean
onTouchEvent
(
MotionEvent
event
)
{
if
(
ev
.
getActionMasked
()
!=
MotionEvent
.
ACTION_DOWN
)
{
switch
(
event
.
getAction
())
{
return
false
;
case
MotionEvent
.
ACTION_DOWN
:
isTouching
=
true
;
return
true
;
case
MotionEvent
.
ACTION_UP
:
if
(
isTouching
)
{
isTouching
=
false
;
performClick
();
return
true
;
}
return
false
;
default
:
return
false
;
}
}
return
performClick
();
}
}
@Override
@Override
...
@@ -1359,7 +1379,6 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
...
@@ -1359,7 +1379,6 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
TextOutput
,
TextOutput
,
VideoListener
,
VideoListener
,
OnLayoutChangeListener
,
OnLayoutChangeListener
,
SphericalSurfaceView
.
SurfaceListener
,
SingleTapListener
{
SingleTapListener
{
// TextOutput implementation
// TextOutput implementation
...
@@ -1449,18 +1468,6 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
...
@@ -1449,18 +1468,6 @@ public class PlayerView extends FrameLayout implements AdsLoader.AdViewProvider
applyTextureViewRotation
((
TextureView
)
view
,
textureViewRotation
);
applyTextureViewRotation
((
TextureView
)
view
,
textureViewRotation
);
}
}
// SphericalSurfaceView.SurfaceTextureListener implementation
@Override
public
void
surfaceChanged
(
@Nullable
Surface
surface
)
{
if
(
player
!=
null
)
{
VideoComponent
videoComponent
=
player
.
getVideoComponent
();
if
(
videoComponent
!=
null
)
{
videoComponent
.
setVideoSurface
(
surface
);
}
}
}
// SingleTapListener implementation
// SingleTapListener implementation
@Override
@Override
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java
View file @
f6297f4f
...
@@ -362,10 +362,16 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -362,10 +362,16 @@ import com.google.android.exoplayer2.util.Util;
int
width
=
Math
.
round
(
parentWidth
*
cueSize
);
int
width
=
Math
.
round
(
parentWidth
*
cueSize
);
int
height
=
cueBitmapHeight
!=
Cue
.
DIMEN_UNSET
?
Math
.
round
(
parentHeight
*
cueBitmapHeight
)
int
height
=
cueBitmapHeight
!=
Cue
.
DIMEN_UNSET
?
Math
.
round
(
parentHeight
*
cueBitmapHeight
)
:
Math
.
round
(
width
*
((
float
)
cueBitmap
.
getHeight
()
/
cueBitmap
.
getWidth
()));
:
Math
.
round
(
width
*
((
float
)
cueBitmap
.
getHeight
()
/
cueBitmap
.
getWidth
()));
int
x
=
Math
.
round
(
cueLineAnchor
==
Cue
.
ANCHOR_TYPE_END
?
(
anchorX
-
width
)
int
x
=
:
cueLineAnchor
==
Cue
.
ANCHOR_TYPE_MIDDLE
?
(
anchorX
-
(
width
/
2
))
:
anchorX
);
Math
.
round
(
int
y
=
Math
.
round
(
cuePositionAnchor
==
Cue
.
ANCHOR_TYPE_END
?
(
anchorY
-
height
)
cuePositionAnchor
==
Cue
.
ANCHOR_TYPE_END
:
cuePositionAnchor
==
Cue
.
ANCHOR_TYPE_MIDDLE
?
(
anchorY
-
(
height
/
2
))
:
anchorY
);
?
(
anchorX
-
width
)
:
cuePositionAnchor
==
Cue
.
ANCHOR_TYPE_MIDDLE
?
(
anchorX
-
(
width
/
2
))
:
anchorX
);
int
y
=
Math
.
round
(
cueLineAnchor
==
Cue
.
ANCHOR_TYPE_END
?
(
anchorY
-
height
)
:
cueLineAnchor
==
Cue
.
ANCHOR_TYPE_MIDDLE
?
(
anchorY
-
(
height
/
2
))
:
anchorY
);
bitmapRect
=
new
Rect
(
x
,
y
,
x
+
width
,
y
+
height
);
bitmapRect
=
new
Rect
(
x
,
y
,
x
+
width
,
y
+
height
);
}
}
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/SphericalSurfaceView.java
View file @
f6297f4f
...
@@ -53,20 +53,6 @@ import javax.microedition.khronos.opengles.GL10;
...
@@ -53,20 +53,6 @@ import javax.microedition.khronos.opengles.GL10;
*/
*/
public
final
class
SphericalSurfaceView
extends
GLSurfaceView
{
public
final
class
SphericalSurfaceView
extends
GLSurfaceView
{
/**
* This listener can be used to be notified when the {@link Surface} associated with this view is
* changed.
*/
public
interface
SurfaceListener
{
/**
* Invoked when the surface is changed or there isn't one anymore. Any previous surface
* shouldn't be used after this call.
*
* @param surface The new surface or null if there isn't one anymore.
*/
void
surfaceChanged
(
@Nullable
Surface
surface
);
}
// Arbitrary vertical field of view.
// Arbitrary vertical field of view.
private
static
final
int
FIELD_OF_VIEW_DEGREES
=
90
;
private
static
final
int
FIELD_OF_VIEW_DEGREES
=
90
;
private
static
final
float
Z_NEAR
=
.
1
f
;
private
static
final
float
Z_NEAR
=
.
1
f
;
...
@@ -84,7 +70,6 @@ public final class SphericalSurfaceView extends GLSurfaceView {
...
@@ -84,7 +70,6 @@ public final class SphericalSurfaceView extends GLSurfaceView {
private
final
Handler
mainHandler
;
private
final
Handler
mainHandler
;
private
final
TouchTracker
touchTracker
;
private
final
TouchTracker
touchTracker
;
private
final
SceneRenderer
scene
;
private
final
SceneRenderer
scene
;
private
@Nullable
SurfaceListener
surfaceListener
;
private
@Nullable
SurfaceTexture
surfaceTexture
;
private
@Nullable
SurfaceTexture
surfaceTexture
;
private
@Nullable
Surface
surface
;
private
@Nullable
Surface
surface
;
private
@Nullable
Player
.
VideoComponent
videoComponent
;
private
@Nullable
Player
.
VideoComponent
videoComponent
;
...
@@ -156,15 +141,6 @@ public final class SphericalSurfaceView extends GLSurfaceView {
...
@@ -156,15 +141,6 @@ public final class SphericalSurfaceView extends GLSurfaceView {
}
}
}
}
/**
* Sets the {@link SurfaceListener} used to listen to surface events.
*
* @param listener The listener for surface events.
*/
public
void
setSurfaceListener
(
@Nullable
SurfaceListener
listener
)
{
surfaceListener
=
listener
;
}
/** Sets the {@link SingleTapListener} used to listen to single tap events on this view. */
/** Sets the {@link SingleTapListener} used to listen to single tap events on this view. */
public
void
setSingleTapListener
(
@Nullable
SingleTapListener
listener
)
{
public
void
setSingleTapListener
(
@Nullable
SingleTapListener
listener
)
{
touchTracker
.
setSingleTapListener
(
listener
);
touchTracker
.
setSingleTapListener
(
listener
);
...
@@ -196,8 +172,8 @@ public final class SphericalSurfaceView extends GLSurfaceView {
...
@@ -196,8 +172,8 @@ public final class SphericalSurfaceView extends GLSurfaceView {
mainHandler
.
post
(
mainHandler
.
post
(
()
->
{
()
->
{
if
(
surface
!=
null
)
{
if
(
surface
!=
null
)
{
if
(
surfaceListener
!=
null
)
{
if
(
videoComponent
!=
null
)
{
surfaceListener
.
surfaceChanged
(
null
);
videoComponent
.
clearVideoSurface
(
surface
);
}
}
releaseSurface
(
surfaceTexture
,
surface
);
releaseSurface
(
surfaceTexture
,
surface
);
surfaceTexture
=
null
;
surfaceTexture
=
null
;
...
@@ -214,8 +190,8 @@ public final class SphericalSurfaceView extends GLSurfaceView {
...
@@ -214,8 +190,8 @@ public final class SphericalSurfaceView extends GLSurfaceView {
Surface
oldSurface
=
this
.
surface
;
Surface
oldSurface
=
this
.
surface
;
this
.
surfaceTexture
=
surfaceTexture
;
this
.
surfaceTexture
=
surfaceTexture
;
this
.
surface
=
new
Surface
(
surfaceTexture
);
this
.
surface
=
new
Surface
(
surfaceTexture
);
if
(
surfaceListener
!=
null
)
{
if
(
videoComponent
!=
null
)
{
surfaceListener
.
surfaceChanged
(
surface
);
videoComponent
.
setVideoSurface
(
surface
);
}
}
releaseSurface
(
oldSurfaceTexture
,
oldSurface
);
releaseSurface
(
oldSurfaceTexture
,
oldSurface
);
});
});
...
...
library/ui/src/main/res/layout/exo_playback_control_view.xml
View file @
f6297f4f
...
@@ -76,8 +76,7 @@
...
@@ -76,8 +76,7 @@
android:includeFontPadding=
"false"
android:includeFontPadding=
"false"
android:textColor=
"#FFBEBEBE"
/>
android:textColor=
"#FFBEBEBE"
/>
<com.google.android.exoplayer2.ui.DefaultTimeBar
<View
android:id=
"@id/exo_progress_placeholder"
android:id=
"@id/exo_progress"
android:layout_width=
"0dp"
android:layout_width=
"0dp"
android:layout_weight=
"1"
android:layout_weight=
"1"
android:layout_height=
"26dp"
/>
android:layout_height=
"26dp"
/>
...
...
library/ui/src/main/res/values/attrs.xml
View file @
f6297f4f
...
@@ -24,25 +24,43 @@
...
@@ -24,25 +24,43 @@
<enum
name=
"zoom"
value=
"4"
/>
<enum
name=
"zoom"
value=
"4"
/>
</attr>
</attr>
<!-- Must be kept in sync with
SimpleExo
PlayerView -->
<!-- Must be kept in sync with PlayerView -->
<attr
name=
"surface_type"
format=
"enum"
>
<attr
name=
"surface_type"
format=
"enum"
>
<enum
name=
"none"
value=
"0"
/>
<enum
name=
"none"
value=
"0"
/>
<enum
name=
"surface_view"
value=
"1"
/>
<enum
name=
"surface_view"
value=
"1"
/>
<enum
name=
"texture_view"
value=
"2"
/>
<enum
name=
"texture_view"
value=
"2"
/>
<enum
name=
"spherical_view"
value=
"3"
/>
<enum
name=
"spherical_view"
value=
"3"
/>
</attr>
</attr>
<attr
name=
"show_timeout"
format=
"integer"
/>
<attr
name=
"rewind_increment"
format=
"integer"
/>
<!-- Must be kept in sync with RepeatModeUtil -->
<attr
name=
"fastforward_increment"
format=
"integer"
/>
<attr
name=
"player_layout_id"
format=
"reference"
/>
<attr
name=
"controller_layout_id"
format=
"reference"
/>
<attr
name=
"repeat_toggle_modes"
>
<attr
name=
"repeat_toggle_modes"
>
<flag
name=
"none"
value=
"0"
/>
<flag
name=
"none"
value=
"0"
/>
<flag
name=
"one"
value=
"1"
/>
<flag
name=
"one"
value=
"1"
/>
<flag
name=
"all"
value=
"2"
/>
<flag
name=
"all"
value=
"2"
/>
</attr>
</attr>
<!-- PlayerControlView attributes -->
<attr
name=
"show_timeout"
format=
"integer"
/>
<attr
name=
"rewind_increment"
format=
"integer"
/>
<attr
name=
"fastforward_increment"
format=
"integer"
/>
<attr
name=
"show_shuffle_button"
format=
"boolean"
/>
<attr
name=
"show_shuffle_button"
format=
"boolean"
/>
<attr
name=
"time_bar_min_update_interval"
format=
"integer"
/>
<attr
name=
"time_bar_min_update_interval"
format=
"integer"
/>
<attr
name=
"controller_layout_id"
format=
"reference"
/>
<!-- DefaultTimeBar attributes -->
<attr
name=
"bar_height"
format=
"dimension"
/>
<attr
name=
"touch_target_height"
format=
"dimension"
/>
<attr
name=
"ad_marker_width"
format=
"dimension"
/>
<attr
name=
"scrubber_enabled_size"
format=
"dimension"
/>
<attr
name=
"scrubber_disabled_size"
format=
"dimension"
/>
<attr
name=
"scrubber_dragged_size"
format=
"dimension"
/>
<attr
name=
"scrubber_drawable"
format=
"reference"
/>
<attr
name=
"played_color"
format=
"color"
/>
<attr
name=
"scrubber_color"
format=
"color"
/>
<attr
name=
"buffered_color"
format=
"color"
/>
<attr
name=
"unplayed_color"
format=
"color"
/>
<attr
name=
"ad_marker_color"
format=
"color"
/>
<attr
name=
"played_ad_marker_color"
format=
"color"
/>
<declare-styleable
name=
"PlayerView"
>
<declare-styleable
name=
"PlayerView"
>
<attr
name=
"use_artwork"
format=
"boolean"
/>
<attr
name=
"use_artwork"
format=
"boolean"
/>
...
@@ -58,9 +76,11 @@
...
@@ -58,9 +76,11 @@
<enum
name=
"always"
value=
"2"
/>
<enum
name=
"always"
value=
"2"
/>
</attr>
</attr>
<attr
name=
"keep_content_on_player_reset"
format=
"boolean"
/>
<attr
name=
"keep_content_on_player_reset"
format=
"boolean"
/>
<attr
name=
"resize_mode"
/>
<attr
name=
"player_layout_id"
format=
"reference"
/>
<attr
name=
"surface_type"
/>
<attr
name=
"surface_type"
/>
<attr
name=
"player_layout_id"
/>
<!-- AspectRatioFrameLayout attributes -->
<attr
name=
"resize_mode"
/>
<!-- PlayerControlView attributes -->
<!-- PlayerControlView attributes -->
<attr
name=
"show_timeout"
/>
<attr
name=
"show_timeout"
/>
<attr
name=
"rewind_increment"
/>
<attr
name=
"rewind_increment"
/>
...
@@ -69,6 +89,20 @@
...
@@ -69,6 +89,20 @@
<attr
name=
"show_shuffle_button"
/>
<attr
name=
"show_shuffle_button"
/>
<attr
name=
"time_bar_min_update_interval"
/>
<attr
name=
"time_bar_min_update_interval"
/>
<attr
name=
"controller_layout_id"
/>
<attr
name=
"controller_layout_id"
/>
<!-- DefaultTimeBar attributes -->
<attr
name=
"bar_height"
/>
<attr
name=
"touch_target_height"
/>
<attr
name=
"ad_marker_width"
/>
<attr
name=
"scrubber_enabled_size"
/>
<attr
name=
"scrubber_disabled_size"
/>
<attr
name=
"scrubber_dragged_size"
/>
<attr
name=
"scrubber_drawable"
/>
<attr
name=
"played_color"
/>
<attr
name=
"scrubber_color"
/>
<attr
name=
"buffered_color"
/>
<attr
name=
"unplayed_color"
/>
<attr
name=
"ad_marker_color"
/>
<attr
name=
"played_ad_marker_color"
/>
</declare-styleable>
</declare-styleable>
<declare-styleable
name=
"AspectRatioFrameLayout"
>
<declare-styleable
name=
"AspectRatioFrameLayout"
>
...
@@ -83,22 +117,36 @@
...
@@ -83,22 +117,36 @@
<attr
name=
"show_shuffle_button"
/>
<attr
name=
"show_shuffle_button"
/>
<attr
name=
"time_bar_min_update_interval"
/>
<attr
name=
"time_bar_min_update_interval"
/>
<attr
name=
"controller_layout_id"
/>
<attr
name=
"controller_layout_id"
/>
<!-- DefaultTimeBar attributes -->
<attr
name=
"bar_height"
/>
<attr
name=
"touch_target_height"
/>
<attr
name=
"ad_marker_width"
/>
<attr
name=
"scrubber_enabled_size"
/>
<attr
name=
"scrubber_disabled_size"
/>
<attr
name=
"scrubber_dragged_size"
/>
<attr
name=
"scrubber_drawable"
/>
<attr
name=
"played_color"
/>
<attr
name=
"scrubber_color"
/>
<attr
name=
"buffered_color"
/>
<attr
name=
"unplayed_color"
/>
<attr
name=
"ad_marker_color"
/>
<attr
name=
"played_ad_marker_color"
/>
</declare-styleable>
</declare-styleable>
<declare-styleable
name=
"DefaultTimeBar"
>
<declare-styleable
name=
"DefaultTimeBar"
>
<attr
name=
"bar_height"
format=
"dimension"
/>
<attr
name=
"bar_height"
/>
<attr
name=
"touch_target_height"
format=
"dimension"
/>
<attr
name=
"touch_target_height"
/>
<attr
name=
"ad_marker_width"
format=
"dimension"
/>
<attr
name=
"ad_marker_width"
/>
<attr
name=
"scrubber_enabled_size"
format=
"dimension"
/>
<attr
name=
"scrubber_enabled_size"
/>
<attr
name=
"scrubber_disabled_size"
format=
"dimension"
/>
<attr
name=
"scrubber_disabled_size"
/>
<attr
name=
"scrubber_dragged_size"
format=
"dimension"
/>
<attr
name=
"scrubber_dragged_size"
/>
<attr
name=
"scrubber_drawable"
format=
"reference"
/>
<attr
name=
"scrubber_drawable"
/>
<attr
name=
"played_color"
format=
"color"
/>
<attr
name=
"played_color"
/>
<attr
name=
"scrubber_color"
format=
"color"
/>
<attr
name=
"scrubber_color"
/>
<attr
name=
"buffered_color"
format=
"color"
/>
<attr
name=
"buffered_color"
/>
<attr
name=
"unplayed_color"
format=
"color"
/>
<attr
name=
"unplayed_color"
/>
<attr
name=
"ad_marker_color"
format=
"color"
/>
<attr
name=
"ad_marker_color"
/>
<attr
name=
"played_ad_marker_color"
format=
"color"
/>
<attr
name=
"played_ad_marker_color"
/>
</declare-styleable>
</declare-styleable>
</resources>
</resources>
library/ui/src/main/res/values/ids.xml
View file @
f6297f4f
...
@@ -33,6 +33,7 @@
...
@@ -33,6 +33,7 @@
<item
name=
"exo_repeat_toggle"
type=
"id"
/>
<item
name=
"exo_repeat_toggle"
type=
"id"
/>
<item
name=
"exo_duration"
type=
"id"
/>
<item
name=
"exo_duration"
type=
"id"
/>
<item
name=
"exo_position"
type=
"id"
/>
<item
name=
"exo_position"
type=
"id"
/>
<item
name=
"exo_progress_placeholder"
type=
"id"
/>
<item
name=
"exo_progress"
type=
"id"
/>
<item
name=
"exo_progress"
type=
"id"
/>
<item
name=
"exo_buffering"
type=
"id"
/>
<item
name=
"exo_buffering"
type=
"id"
/>
<item
name=
"exo_error_message"
type=
"id"
/>
<item
name=
"exo_error_message"
type=
"id"
/>
...
...
testutils/src/main/java/com/google/android/exoplayer2/testutil/DebugRenderersFactory.java
View file @
f6297f4f
...
@@ -30,7 +30,6 @@ import com.google.android.exoplayer2.drm.DrmSessionManager;
...
@@ -30,7 +30,6 @@ import com.google.android.exoplayer2.drm.DrmSessionManager;
import
com.google.android.exoplayer2.drm.FrameworkMediaCrypto
;
import
com.google.android.exoplayer2.drm.FrameworkMediaCrypto
;
import
com.google.android.exoplayer2.mediacodec.MediaCodecInfo
;
import
com.google.android.exoplayer2.mediacodec.MediaCodecInfo
;
import
com.google.android.exoplayer2.mediacodec.MediaCodecSelector
;
import
com.google.android.exoplayer2.mediacodec.MediaCodecSelector
;
import
com.google.android.exoplayer2.mediacodec.MediaCodecUtil.DecoderQueryException
;
import
com.google.android.exoplayer2.video.MediaCodecVideoRenderer
;
import
com.google.android.exoplayer2.video.MediaCodecVideoRenderer
;
import
com.google.android.exoplayer2.video.VideoRendererEventListener
;
import
com.google.android.exoplayer2.video.VideoRendererEventListener
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteBuffer
;
...
@@ -55,6 +54,7 @@ public class DebugRenderersFactory extends DefaultRenderersFactory {
...
@@ -55,6 +54,7 @@ public class DebugRenderersFactory extends DefaultRenderersFactory {
MediaCodecSelector
mediaCodecSelector
,
MediaCodecSelector
mediaCodecSelector
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
boolean
playClearSamplesWithoutKeys
,
boolean
playClearSamplesWithoutKeys
,
boolean
enableDecoderFallback
,
Handler
eventHandler
,
Handler
eventHandler
,
VideoRendererEventListener
eventListener
,
VideoRendererEventListener
eventListener
,
long
allowedVideoJoiningTimeMs
,
long
allowedVideoJoiningTimeMs
,
...
@@ -113,8 +113,7 @@ public class DebugRenderersFactory extends DefaultRenderersFactory {
...
@@ -113,8 +113,7 @@ public class DebugRenderersFactory extends DefaultRenderersFactory {
MediaCodec
codec
,
MediaCodec
codec
,
Format
format
,
Format
format
,
MediaCrypto
crypto
,
MediaCrypto
crypto
,
float
operatingRate
)
float
operatingRate
)
{
throws
DecoderQueryException
{
// If the codec is being initialized whilst the renderer is started, default behavior is to
// If the codec is being initialized whilst the renderer is started, default behavior is to
// render the first frame (i.e. the keyframe before the current position), then drop frames up
// render the first frame (i.e. the keyframe before the current position), then drop frames up
// to the current playback position. For test runs that place a maximum limit on the number of
// to the current playback position. For test runs that place a maximum limit on the number of
...
...
testutils/src/main/java/com/google/android/exoplayer2/testutil/HostActivity.java
View file @
f6297f4f
...
@@ -166,7 +166,8 @@ public final class HostActivity extends Activity implements SurfaceHolder.Callba
...
@@ -166,7 +166,8 @@ public final class HostActivity extends Activity implements SurfaceHolder.Callba
public
void
onCreate
(
Bundle
savedInstanceState
)
{
public
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
super
.
onCreate
(
savedInstanceState
);
requestWindowFeature
(
Window
.
FEATURE_NO_TITLE
);
requestWindowFeature
(
Window
.
FEATURE_NO_TITLE
);
setContentView
(
getResources
().
getIdentifier
(
"host_activity"
,
"layout"
,
getPackageName
()));
setContentView
(
getResources
().
getIdentifier
(
"exo_testutils_host_activity"
,
"layout"
,
getPackageName
()));
surfaceView
=
findViewById
(
surfaceView
=
findViewById
(
getResources
().
getIdentifier
(
"surface_view"
,
"id"
,
getPackageName
()));
getResources
().
getIdentifier
(
"surface_view"
,
"id"
,
getPackageName
()));
surfaceView
.
getHolder
().
addCallback
(
this
);
surfaceView
.
getHolder
().
addCallback
(
this
);
...
...
testutils/src/main/res/layout/host_activity.xml
→
testutils/src/main/res/layout/
exo_testutils_
host_activity.xml
View file @
f6297f4f
File moved
testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/CacheAsserts.java
View file @
f6297f4f
...
@@ -33,58 +33,89 @@ import java.util.ArrayList;
...
@@ -33,58 +33,89 @@ import java.util.ArrayList;
/** Assertion methods for {@link Cache}. */
/** Assertion methods for {@link Cache}. */
public
final
class
CacheAsserts
{
public
final
class
CacheAsserts
{
/**
/** Defines a set of data requests. */
* Asserts that the cache content is equal to the data in the {@code fakeDataSet}.
public
static
final
class
RequestSet
{
*
* @throws IOException If an error occurred reading from the Cache.
private
final
FakeDataSet
fakeDataSet
;
*/
private
DataSpec
[]
dataSpecs
;
public
static
void
assertCachedData
(
Cache
cache
,
FakeDataSet
fakeDataSet
)
throws
IOException
{
ArrayList
<
FakeData
>
allData
=
fakeDataSet
.
getAllData
();
public
RequestSet
(
FakeDataSet
fakeDataSet
)
{
Uri
[]
uris
=
new
Uri
[
allData
.
size
()];
this
.
fakeDataSet
=
fakeDataSet
;
for
(
int
i
=
0
;
i
<
allData
.
size
();
i
++)
{
ArrayList
<
FakeData
>
allData
=
fakeDataSet
.
getAllData
();
uris
[
i
]
=
allData
.
get
(
i
).
uri
;
dataSpecs
=
new
DataSpec
[
allData
.
size
()];
for
(
int
i
=
0
;
i
<
dataSpecs
.
length
;
i
++)
{
dataSpecs
[
i
]
=
new
DataSpec
(
allData
.
get
(
i
).
uri
);
}
}
}
assertCachedData
(
cache
,
fakeDataSet
,
uris
);
}
/**
public
RequestSet
subset
(
String
...
uriStrings
)
{
* Asserts that the cache content is equal to the given subset of data in the {@code fakeDataSet}.
dataSpecs
=
new
DataSpec
[
uriStrings
.
length
];
*
for
(
int
i
=
0
;
i
<
dataSpecs
.
length
;
i
++)
{
* @throws IOException If an error occurred reading from the Cache.
dataSpecs
[
i
]
=
new
DataSpec
(
Uri
.
parse
(
uriStrings
[
i
]));
*/
}
public
static
void
assertCachedData
(
Cache
cache
,
FakeDataSet
fakeDataSet
,
String
...
uriStrings
)
return
this
;
throws
IOException
{
}
Uri
[]
uris
=
new
Uri
[
uriStrings
.
length
];
for
(
int
i
=
0
;
i
<
uriStrings
.
length
;
i
++)
{
public
RequestSet
subset
(
Uri
...
uris
)
{
uris
[
i
]
=
Uri
.
parse
(
uriStrings
[
i
]);
dataSpecs
=
new
DataSpec
[
uris
.
length
];
for
(
int
i
=
0
;
i
<
dataSpecs
.
length
;
i
++)
{
dataSpecs
[
i
]
=
new
DataSpec
(
uris
[
i
]);
}
return
this
;
}
public
RequestSet
subset
(
DataSpec
...
dataSpecs
)
{
this
.
dataSpecs
=
dataSpecs
;
return
this
;
}
public
int
getCount
()
{
return
dataSpecs
.
length
;
}
public
byte
[]
getData
(
int
i
)
{
return
fakeDataSet
.
getData
(
dataSpecs
[
i
].
uri
).
getData
();
}
public
DataSpec
getDataSpec
(
int
i
)
{
return
dataSpecs
[
i
];
}
public
RequestSet
useBoundedDataSpecFor
(
String
uriString
)
{
FakeData
data
=
fakeDataSet
.
getData
(
uriString
);
for
(
int
i
=
0
;
i
<
dataSpecs
.
length
;
i
++)
{
DataSpec
spec
=
dataSpecs
[
i
];
if
(
spec
.
uri
.
getPath
().
equals
(
uriString
))
{
dataSpecs
[
i
]
=
spec
.
subrange
(
0
,
data
.
getData
().
length
);
return
this
;
}
}
throw
new
IllegalStateException
();
}
}
assertCachedData
(
cache
,
fakeDataSet
,
uris
);
}
}
/**
/**
* Asserts that the cache cont
ent is equal to the given subset of data in the {@code fakeData
Set}.
* Asserts that the cache cont
ains necessary data for the {@code request
Set}.
*
*
* @throws IOException If an error occurred reading from the Cache.
* @throws IOException If an error occurred reading from the Cache.
*/
*/
public
static
void
assertCachedData
(
Cache
cache
,
FakeDataSet
fakeDataSet
,
Uri
...
uris
)
public
static
void
assertCachedData
(
Cache
cache
,
RequestSet
requestSet
)
throws
IOException
{
throws
IOException
{
int
totalLength
=
0
;
int
totalLength
=
0
;
for
(
Uri
uri
:
uris
)
{
for
(
int
i
=
0
;
i
<
requestSet
.
getCount
();
i
++
)
{
byte
[]
data
=
fakeDataSet
.
getData
(
uri
).
getData
(
);
byte
[]
data
=
requestSet
.
getData
(
i
);
assertDataCached
(
cache
,
uri
,
data
);
assertDataCached
(
cache
,
requestSet
.
getDataSpec
(
i
)
,
data
);
totalLength
+=
data
.
length
;
totalLength
+=
data
.
length
;
}
}
assertThat
(
cache
.
getCacheSpace
()).
isEqualTo
(
totalLength
);
assertThat
(
cache
.
getCacheSpace
()).
isEqualTo
(
totalLength
);
}
}
/**
/**
* Asserts that the cache cont
ains the given data for {@code uriString
}.
* Asserts that the cache cont
ent is equal to the data in the {@code fakeDataSet
}.
*
*
* @throws IOException If an error occurred reading from the Cache.
* @throws IOException If an error occurred reading from the Cache.
*/
*/
public
static
void
assertDataCached
(
Cache
cache
,
Uri
uri
,
byte
[]
expected
)
throws
IOException
{
public
static
void
assertCachedData
(
Cache
cache
,
FakeDataSet
fakeDataSet
)
throws
IOException
{
DataSpec
dataSpec
=
new
DataSpec
(
uri
);
assertCachedData
(
cache
,
new
RequestSet
(
fakeDataSet
));
assertDataCached
(
cache
,
dataSpec
,
expected
);
}
}
/**
/**
...
@@ -95,15 +126,18 @@ public final class CacheAsserts {
...
@@ -95,15 +126,18 @@ public final class CacheAsserts {
public
static
void
assertDataCached
(
Cache
cache
,
DataSpec
dataSpec
,
byte
[]
expected
)
public
static
void
assertDataCached
(
Cache
cache
,
DataSpec
dataSpec
,
byte
[]
expected
)
throws
IOException
{
throws
IOException
{
DataSource
dataSource
=
new
CacheDataSource
(
cache
,
DummyDataSource
.
INSTANCE
,
0
);
DataSource
dataSource
=
new
CacheDataSource
(
cache
,
DummyDataSource
.
INSTANCE
,
0
);
dataSource
.
open
(
dataSpec
)
;
byte
[]
bytes
;
try
{
try
{
byte
[]
bytes
=
TestUtil
.
readToEnd
(
dataSource
);
dataSource
.
open
(
dataSpec
);
assertWithMessage
(
"Cached data doesn't match expected for '"
+
dataSpec
.
uri
+
"',"
)
bytes
=
TestUtil
.
readToEnd
(
dataSource
);
.
that
(
bytes
)
}
catch
(
IOException
e
)
{
.
isEqualTo
(
expected
);
throw
new
IOException
(
"Opening/reading cache failed: "
+
dataSpec
,
e
);
}
finally
{
}
finally
{
dataSource
.
close
();
dataSource
.
close
();
}
}
assertWithMessage
(
"Cached data doesn't match expected for '"
+
dataSpec
.
uri
+
"',"
)
.
that
(
bytes
)
.
isEqualTo
(
expected
);
}
}
/**
/**
...
...
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