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
de39925c
authored
Feb 10, 2019
by
Oliver Woodman
Committed by
GitHub
Feb 10, 2019
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #5455 from google/dev-v2-r2.9.5
r2.9.5
parents
1c4ea26f
4d843da6
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
624 additions
and
306 deletions
RELEASENOTES.md
constants.gradle
extensions/cronet/build.gradle
extensions/ima/build.gradle
extensions/okhttp/build.gradle
library/core/src/main/java/com/google/android/exoplayer2/DefaultRenderersFactory.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultTsPayloadReaderFactory.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java
library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java
library/core/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
testutils/src/main/java/com/google/android/exoplayer2/testutil/DebugRenderersFactory.java
testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/RobolectricUtil.java
RELEASENOTES.md
View file @
de39925c
# Release notes #
# Release notes #
### 2.9.5 ###
*
HLS: Parse
`CHANNELS`
attribute from
`EXT-X-MEDIA`
tag.
*
ConcatenatingMediaSource:
*
Add
`Handler`
parameter to methods that take a callback
`Runnable`
.
*
Fix issue with dropped messages when releasing the source
(
[
#5464
](
https://github.com/google/ExoPlayer/issues/5464
)
).
*
ExtractorMediaSource: Fix issue that could cause the player to get stuck
buffering at the end of the media.
*
PlayerView: Fix issue preventing
`OnClickListener`
from receiving events
(
[
#5433
](
https://github.com/google/ExoPlayer/issues/5433
)
).
*
IMA extension: Upgrade IMA dependency to 3.10.6.
*
Cronet extension: Upgrade Cronet dependency to 71.3578.98.
*
OkHttp extension: Upgrade OkHttp dependency to 3.12.1.
*
MP3: Wider fix for issue where streams would play twice on some Samsung
devices (
[
#4519
](
https://github.com/google/ExoPlayer/issues/4519
)
).
### 2.9.4 ###
### 2.9.4 ###
*
IMA extension: Clear ads loader listeners on release
*
IMA extension: Clear ads loader listeners on release
...
@@ -1160,7 +1177,7 @@
...
@@ -1160,7 +1177,7 @@
[
here
](
https://medium.com/google-exoplayer/customizing-exoplayers-ui-components-728cf55ee07a#.9ewjg7avi
)
.
[
here
](
https://medium.com/google-exoplayer/customizing-exoplayers-ui-components-728cf55ee07a#.9ewjg7avi
)
.
*
Robustness improvements when handling MediaSource timeline changes and
*
Robustness improvements when handling MediaSource timeline changes and
MediaPeriod transitions.
MediaPeriod transitions.
*
EIA
608: Support for caption styling and positioning.
*
CEA-
608: Support for caption styling and positioning.
*
MPEG-TS: Improved support:
*
MPEG-TS: Improved support:
*
Support injection of custom TS payload readers.
*
Support injection of custom TS payload readers.
*
Support injection of custom section payload readers.
*
Support injection of custom section payload readers.
...
@@ -1404,8 +1421,8 @@ V2 release.
...
@@ -1404,8 +1421,8 @@ V2 release.
(#801).
(#801).
*
MP3: Fix playback of some streams when stream length is unknown.
*
MP3: Fix playback of some streams when stream length is unknown.
*
ID3: Support multiple frames of the same type in a single tag.
*
ID3: Support multiple frames of the same type in a single tag.
*
EIA608: Correctly handle repeated control characters, fixing an issue in which
*
CEA-608: Correctly handle repeated control characters, fixing an issue in
captions would immediately disappear.
which
captions would immediately disappear.
*
AVC3: Fix decoder failures on some MediaTek devices in the case where the
*
AVC3: Fix decoder failures on some MediaTek devices in the case where the
first buffer fed to the decoder does not start with SPS/PPS NAL units.
first buffer fed to the decoder does not start with SPS/PPS NAL units.
*
Misc bug fixes.
*
Misc bug fixes.
...
...
constants.gradle
View file @
de39925c
...
@@ -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.9.
4
'
releaseVersion
=
'2.9.
5
'
releaseVersionCode
=
200900
4
releaseVersionCode
=
200900
5
// Important: ExoPlayer specifies a minSdkVersion of 14 because various
// Important: ExoPlayer specifies a minSdkVersion of 14 because various
// components provided by the library may be of use on older devices.
// components provided by the library may be of use on older devices.
// However, please note that the core media playback functionality provided
// However, please note that the core media playback functionality provided
...
...
extensions/cronet/build.gradle
View file @
de39925c
...
@@ -30,7 +30,7 @@ android {
...
@@ -30,7 +30,7 @@ android {
}
}
dependencies
{
dependencies
{
api
'org.chromium.net:cronet-embedded:
66.3359.15
8'
api
'org.chromium.net:cronet-embedded:
71.3578.9
8'
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
'com.android.support:support-annotations:'
+
supportLibraryVersion
implementation
'com.android.support:support-annotations:'
+
supportLibraryVersion
testImplementation
project
(
modulePrefix
+
'library'
)
testImplementation
project
(
modulePrefix
+
'library'
)
...
...
extensions/ima/build.gradle
View file @
de39925c
...
@@ -31,13 +31,13 @@ android {
...
@@ -31,13 +31,13 @@ android {
}
}
dependencies
{
dependencies
{
api
'com.google.ads.interactivemedia.v3:interactivemedia:3.10.
2
'
api
'com.google.ads.interactivemedia.v3:interactivemedia:3.10.
6
'
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
'com.google.android.gms:play-services-ads:17.1.
1
'
implementation
'com.google.android.gms:play-services-ads:17.1.
2
'
// These dependencies are necessary to force the supportLibraryVersion of
// These dependencies are necessary to force the supportLibraryVersion of
// com.android.support:support-v4 and com.android.support:customtabs to be
// com.android.support:support-v4 and com.android.support:customtabs to be
// used. Else older versions are used, for example via:
// used. Else older versions are used, for example via:
// com.google.android.gms:play-services-ads:17.1.
1
// com.google.android.gms:play-services-ads:17.1.
2
// |-- com.android.support:customtabs:26.1.0
// |-- com.android.support:customtabs:26.1.0
implementation
'com.android.support:support-v4:'
+
supportLibraryVersion
implementation
'com.android.support:support-v4:'
+
supportLibraryVersion
implementation
'com.android.support:customtabs:'
+
supportLibraryVersion
implementation
'com.android.support:customtabs:'
+
supportLibraryVersion
...
...
extensions/okhttp/build.gradle
View file @
de39925c
...
@@ -34,7 +34,7 @@ dependencies {
...
@@ -34,7 +34,7 @@ dependencies {
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
'com.android.support:support-annotations:'
+
supportLibraryVersion
implementation
'com.android.support:support-annotations:'
+
supportLibraryVersion
compileOnly
'org.checkerframework:checker-qual:'
+
checkerframeworkVersion
compileOnly
'org.checkerframework:checker-qual:'
+
checkerframeworkVersion
api
'com.squareup.okhttp3:okhttp:3.1
1.0
'
api
'com.squareup.okhttp3:okhttp:3.1
2.1
'
}
}
ext
{
ext
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/DefaultRenderersFactory.java
View file @
de39925c
...
@@ -16,6 +16,7 @@
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
;
package
com
.
google
.
android
.
exoplayer2
;
import
android.content.Context
;
import
android.content.Context
;
import
android.media.MediaCodec
;
import
android.os.Handler
;
import
android.os.Handler
;
import
android.os.Looper
;
import
android.os.Looper
;
import
android.support.annotation.IntDef
;
import
android.support.annotation.IntDef
;
...
@@ -85,15 +86,18 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -85,15 +86,18 @@ public class DefaultRenderersFactory implements RenderersFactory {
protected
static
final
int
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY
=
50
;
protected
static
final
int
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY
=
50
;
private
final
Context
context
;
private
final
Context
context
;
private
final
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
;
@Nullable
private
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
;
private
final
@ExtensionRendererMode
int
extensionRendererMode
;
@ExtensionRendererMode
private
int
extensionRendererMode
;
private
final
long
allowedVideoJoiningTimeMs
;
private
long
allowedVideoJoiningTimeMs
;
private
boolean
playClearSamplesWithoutKeys
;
private
MediaCodecSelector
mediaCodecSelector
;
/**
/** @param context A {@link Context}. */
* @param context A {@link Context}.
*/
public
DefaultRenderersFactory
(
Context
context
)
{
public
DefaultRenderersFactory
(
Context
context
)
{
this
(
context
,
EXTENSION_RENDERER_MODE_OFF
);
this
.
context
=
context
;
extensionRendererMode
=
EXTENSION_RENDERER_MODE_OFF
;
allowedVideoJoiningTimeMs
=
DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS
;
mediaCodecSelector
=
MediaCodecSelector
.
DEFAULT
;
}
}
/**
/**
...
@@ -108,19 +112,20 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -108,19 +112,20 @@ public class DefaultRenderersFactory implements RenderersFactory {
}
}
/**
/**
* @param context A {@link Context}.
* @deprecated Use {@link #DefaultRenderersFactory(Context)} and {@link
* @param extensionRendererMode The extension renderer mode, which determines if and how available
* #setExtensionRendererMode(int)}.
* extension renderers are used. Note that extensions must be included in the application
* build for them to be considered available.
*/
*/
@Deprecated
@SuppressWarnings
(
"deprecation"
)
public
DefaultRenderersFactory
(
public
DefaultRenderersFactory
(
Context
context
,
@ExtensionRendererMode
int
extensionRendererMode
)
{
Context
context
,
@ExtensionRendererMode
int
extensionRendererMode
)
{
this
(
context
,
extensionRendererMode
,
DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS
);
this
(
context
,
extensionRendererMode
,
DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS
);
}
}
/**
/**
* @deprecated Use {@link #DefaultRenderersFactory(Context, int)} and pass {@link
* @deprecated Use {@link #DefaultRenderersFactory(Context)} and {@link
* DrmSessionManager} directly to {@link SimpleExoPlayer} or {@link ExoPlayerFactory}.
* #setExtensionRendererMode(int)}, and pass {@link DrmSessionManager} directly to {@link
* SimpleExoPlayer} or {@link ExoPlayerFactory}.
*/
*/
@Deprecated
@Deprecated
@SuppressWarnings
(
"deprecation"
)
@SuppressWarnings
(
"deprecation"
)
...
@@ -132,26 +137,22 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -132,26 +137,22 @@ public class DefaultRenderersFactory implements RenderersFactory {
}
}
/**
/**
* @param context A {@link Context}.
* @deprecated Use {@link #DefaultRenderersFactory(Context)}, {@link
* @param extensionRendererMode The extension renderer mode, which determines if and how available
* #setExtensionRendererMode(int)} and {@link #setAllowedVideoJoiningTimeMs(long)}.
* extension renderers are used. Note that extensions must be included in the application
* build for them to be considered available.
* @param allowedVideoJoiningTimeMs The maximum duration for which video renderers can attempt to
* seamlessly join an ongoing playback.
*/
*/
@Deprecated
@SuppressWarnings
(
"deprecation"
)
public
DefaultRenderersFactory
(
public
DefaultRenderersFactory
(
Context
context
,
Context
context
,
@ExtensionRendererMode
int
extensionRendererMode
,
@ExtensionRendererMode
int
extensionRendererMode
,
long
allowedVideoJoiningTimeMs
)
{
long
allowedVideoJoiningTimeMs
)
{
this
.
context
=
context
;
this
(
context
,
null
,
extensionRendererMode
,
allowedVideoJoiningTimeMs
);
this
.
extensionRendererMode
=
extensionRendererMode
;
this
.
allowedVideoJoiningTimeMs
=
allowedVideoJoiningTimeMs
;
this
.
drmSessionManager
=
null
;
}
}
/**
/**
* @deprecated Use {@link #DefaultRenderersFactory(Context, int, long)} and pass {@link
* @deprecated Use {@link #DefaultRenderersFactory(Context)}, {@link
* DrmSessionManager} directly to {@link SimpleExoPlayer} or {@link ExoPlayerFactory}.
* #setExtensionRendererMode(int)} and {@link #setAllowedVideoJoiningTimeMs(long)}, and pass
* {@link DrmSessionManager} directly to {@link SimpleExoPlayer} or {@link ExoPlayerFactory}.
*/
*/
@Deprecated
@Deprecated
public
DefaultRenderersFactory
(
public
DefaultRenderersFactory
(
...
@@ -163,6 +164,70 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -163,6 +164,70 @@ public class DefaultRenderersFactory implements RenderersFactory {
this
.
extensionRendererMode
=
extensionRendererMode
;
this
.
extensionRendererMode
=
extensionRendererMode
;
this
.
allowedVideoJoiningTimeMs
=
allowedVideoJoiningTimeMs
;
this
.
allowedVideoJoiningTimeMs
=
allowedVideoJoiningTimeMs
;
this
.
drmSessionManager
=
drmSessionManager
;
this
.
drmSessionManager
=
drmSessionManager
;
mediaCodecSelector
=
MediaCodecSelector
.
DEFAULT
;
}
/**
* Sets the extension renderer mode, which determines if and how available extension renderers are
* used. Note that extensions must be included in the application build for them to be considered
* available.
*
* <p>The default value is {@link #EXTENSION_RENDERER_MODE_OFF}.
*
* @param extensionRendererMode The extension renderer mode.
* @return This factory, for convenience.
*/
public
DefaultRenderersFactory
setExtensionRendererMode
(
@ExtensionRendererMode
int
extensionRendererMode
)
{
this
.
extensionRendererMode
=
extensionRendererMode
;
return
this
;
}
/**
* Sets whether renderers are permitted to play clear regions of encrypted media prior to having
* obtained the keys necessary to decrypt encrypted regions of the media. For encrypted media that
* starts with a short clear region, this allows playback to begin in parallel with key
* acquisition, which can reduce startup latency.
*
* <p>The default value is {@code false}.
*
* @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
* the media.
* @return This factory, for convenience.
*/
public
DefaultRenderersFactory
setPlayClearSamplesWithoutKeys
(
boolean
playClearSamplesWithoutKeys
)
{
this
.
playClearSamplesWithoutKeys
=
playClearSamplesWithoutKeys
;
return
this
;
}
/**
* Sets a {@link MediaCodecSelector} for use by {@link MediaCodec} based renderers.
*
* <p>The default value is {@link MediaCodecSelector#DEFAULT}.
*
* @param mediaCodecSelector The {@link MediaCodecSelector}.
* @return This factory, for convenience.
*/
public
DefaultRenderersFactory
setMediaCodecSelector
(
MediaCodecSelector
mediaCodecSelector
)
{
this
.
mediaCodecSelector
=
mediaCodecSelector
;
return
this
;
}
/**
* Sets the maximum duration for which video renderers can attempt to seamlessly join an ongoing
* playback.
*
* <p>The default value is {@link #DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS}.
*
* @param allowedVideoJoiningTimeMs The maximum duration for which video renderers can attempt to
* seamlessly join an ongoing playback, in milliseconds.
* @return This factory, for convenience.
*/
public
DefaultRenderersFactory
setAllowedVideoJoiningTimeMs
(
long
allowedVideoJoiningTimeMs
)
{
this
.
allowedVideoJoiningTimeMs
=
allowedVideoJoiningTimeMs
;
return
this
;
}
}
@Override
@Override
...
@@ -177,10 +242,26 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -177,10 +242,26 @@ public class DefaultRenderersFactory implements RenderersFactory {
drmSessionManager
=
this
.
drmSessionManager
;
drmSessionManager
=
this
.
drmSessionManager
;
}
}
ArrayList
<
Renderer
>
renderersList
=
new
ArrayList
<>();
ArrayList
<
Renderer
>
renderersList
=
new
ArrayList
<>();
buildVideoRenderers
(
context
,
drmSessionManager
,
allowedVideoJoiningTimeMs
,
buildVideoRenderers
(
eventHandler
,
videoRendererEventListener
,
extensionRendererMode
,
renderersList
);
context
,
buildAudioRenderers
(
context
,
drmSessionManager
,
buildAudioProcessors
(),
extensionRendererMode
,
eventHandler
,
audioRendererEventListener
,
extensionRendererMode
,
renderersList
);
mediaCodecSelector
,
drmSessionManager
,
playClearSamplesWithoutKeys
,
eventHandler
,
videoRendererEventListener
,
allowedVideoJoiningTimeMs
,
renderersList
);
buildAudioRenderers
(
context
,
extensionRendererMode
,
mediaCodecSelector
,
drmSessionManager
,
playClearSamplesWithoutKeys
,
buildAudioProcessors
(),
eventHandler
,
audioRendererEventListener
,
renderersList
);
buildTextRenderers
(
context
,
textRendererOutput
,
eventHandler
.
getLooper
(),
buildTextRenderers
(
context
,
textRendererOutput
,
eventHandler
.
getLooper
(),
extensionRendererMode
,
renderersList
);
extensionRendererMode
,
renderersList
);
buildMetadataRenderers
(
context
,
metadataRendererOutput
,
eventHandler
.
getLooper
(),
buildMetadataRenderers
(
context
,
metadataRendererOutput
,
eventHandler
.
getLooper
(),
...
@@ -194,27 +275,36 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -194,27 +275,36 @@ public class DefaultRenderersFactory implements RenderersFactory {
* Builds video renderers for use by the player.
* Builds video renderers for use by the player.
*
*
* @param context The {@link Context} associated with the player.
* @param context The {@link Context} associated with the player.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player
* @param extensionRendererMode The extension renderer mode.
* will not be used for DRM protected playbacks.
* @param mediaCodecSelector A decoder selector.
* @param allowedVideoJoiningTimeMs The maximum duration in milliseconds for which video
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player will
* renderers can attempt to seamlessly join an ongoing playback.
* not be used for DRM protected playbacks.
* @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
* the media.
* @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 extensionRendererMode The extension renderer mode.
* @param allowedVideoJoiningTimeMs The maximum duration for which video renderers can attempt to
* seamlessly join an ongoing playback, in milliseconds.
* @param out An array to which the built renderers should be appended.
* @param out An array to which the built renderers should be appended.
*/
*/
protected
void
buildVideoRenderers
(
Context
context
,
protected
void
buildVideoRenderers
(
Context
context
,
@ExtensionRendererMode
int
extensionRendererMode
,
MediaCodecSelector
mediaCodecSelector
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
long
allowedVideoJoiningTimeMs
,
Handler
eventHandler
,
boolean
playClearSamplesWithoutKeys
,
VideoRendererEventListener
eventListener
,
@ExtensionRendererMode
int
extensionRendererMode
,
Handler
eventHandler
,
VideoRendererEventListener
eventListener
,
long
allowedVideoJoiningTimeMs
,
ArrayList
<
Renderer
>
out
)
{
ArrayList
<
Renderer
>
out
)
{
out
.
add
(
out
.
add
(
new
MediaCodecVideoRenderer
(
new
MediaCodecVideoRenderer
(
context
,
context
,
MediaCodecSelector
.
DEFAULT
,
mediaCodecSelector
,
allowedVideoJoiningTimeMs
,
allowedVideoJoiningTimeMs
,
drmSessionManager
,
drmSessionManager
,
/* playClearSamplesWithoutKeys= */
false
,
playClearSamplesWithoutKeys
,
eventHandler
,
eventHandler
,
eventListener
,
eventListener
,
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY
));
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY
));
...
@@ -261,26 +351,35 @@ public class DefaultRenderersFactory implements RenderersFactory {
...
@@ -261,26 +351,35 @@ public class DefaultRenderersFactory implements RenderersFactory {
* Builds audio renderers for use by the player.
* Builds audio renderers for use by the player.
*
*
* @param context The {@link Context} associated with the player.
* @param context The {@link Context} associated with the player.
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player
* @param extensionRendererMode The extension renderer mode.
* will not be used for DRM protected playbacks.
* @param mediaCodecSelector A decoder selector.
* @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the player will
* buffers before output. May be empty.
* not be used for DRM protected playbacks.
* @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
* the media.
* @param audioProcessors An array of {@link AudioProcessor}s that will process PCM audio buffers
* 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.
* @param eventListener An event listener.
* @param eventListener An event listener.
* @param extensionRendererMode The extension renderer mode.
* @param out An array to which the built renderers should be appended.
* @param out An array to which the built renderers should be appended.
*/
*/
protected
void
buildAudioRenderers
(
Context
context
,
protected
void
buildAudioRenderers
(
Context
context
,
@ExtensionRendererMode
int
extensionRendererMode
,
MediaCodecSelector
mediaCodecSelector
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
AudioProcessor
[]
audioProcessors
,
Handler
eventHandler
,
boolean
playClearSamplesWithoutKeys
,
AudioRendererEventListener
eventListener
,
@ExtensionRendererMode
int
extensionRendererMode
,
AudioProcessor
[]
audioProcessors
,
Handler
eventHandler
,
AudioRendererEventListener
eventListener
,
ArrayList
<
Renderer
>
out
)
{
ArrayList
<
Renderer
>
out
)
{
out
.
add
(
out
.
add
(
new
MediaCodecAudioRenderer
(
new
MediaCodecAudioRenderer
(
context
,
context
,
MediaCodecSelector
.
DEFAULT
,
mediaCodecSelector
,
drmSessionManager
,
drmSessionManager
,
/* playClearSamplesWithoutKeys= */
false
,
playClearSamplesWithoutKeys
,
eventHandler
,
eventHandler
,
eventListener
,
eventListener
,
AudioCapabilities
.
getCapabilities
(
context
),
AudioCapabilities
.
getCapabilities
(
context
),
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java
View file @
de39925c
...
@@ -97,7 +97,8 @@ public final class ExoPlayerFactory {
...
@@ -97,7 +97,8 @@ public final class ExoPlayerFactory {
LoadControl
loadControl
,
LoadControl
loadControl
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
@DefaultRenderersFactory
.
ExtensionRendererMode
int
extensionRendererMode
)
{
@DefaultRenderersFactory
.
ExtensionRendererMode
int
extensionRendererMode
)
{
RenderersFactory
renderersFactory
=
new
DefaultRenderersFactory
(
context
,
extensionRendererMode
);
RenderersFactory
renderersFactory
=
new
DefaultRenderersFactory
(
context
).
setExtensionRendererMode
(
extensionRendererMode
);
return
newSimpleInstance
(
return
newSimpleInstance
(
context
,
renderersFactory
,
trackSelector
,
loadControl
,
drmSessionManager
);
context
,
renderersFactory
,
trackSelector
,
loadControl
,
drmSessionManager
);
}
}
...
@@ -127,7 +128,9 @@ public final class ExoPlayerFactory {
...
@@ -127,7 +128,9 @@ public final class ExoPlayerFactory {
@DefaultRenderersFactory
.
ExtensionRendererMode
int
extensionRendererMode
,
@DefaultRenderersFactory
.
ExtensionRendererMode
int
extensionRendererMode
,
long
allowedVideoJoiningTimeMs
)
{
long
allowedVideoJoiningTimeMs
)
{
RenderersFactory
renderersFactory
=
RenderersFactory
renderersFactory
=
new
DefaultRenderersFactory
(
context
,
extensionRendererMode
,
allowedVideoJoiningTimeMs
);
new
DefaultRenderersFactory
(
context
)
.
setExtensionRendererMode
(
extensionRendererMode
)
.
setAllowedVideoJoiningTimeMs
(
allowedVideoJoiningTimeMs
);
return
newSimpleInstance
(
return
newSimpleInstance
(
context
,
renderersFactory
,
trackSelector
,
loadControl
,
drmSessionManager
);
context
,
renderersFactory
,
trackSelector
,
loadControl
,
drmSessionManager
);
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
View file @
de39925c
...
@@ -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.9.
4
"
;
public
static
final
String
VERSION
=
"2.9.
5
"
;
/** 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.9.
4
"
;
public
static
final
String
VERSION_SLASHY
=
"ExoPlayerLib/2.9.
5
"
;
/**
/**
* 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
=
200900
4
;
public
static
final
int
VERSION_INT
=
200900
5
;
/**
/**
* 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/audio/DefaultAudioSink.java
View file @
de39925c
...
@@ -419,7 +419,7 @@ public final class DefaultAudioSink implements AudioSink {
...
@@ -419,7 +419,7 @@ public final class DefaultAudioSink implements AudioSink {
isInputPcm
=
Util
.
isEncodingLinearPcm
(
inputEncoding
);
isInputPcm
=
Util
.
isEncodingLinearPcm
(
inputEncoding
);
shouldConvertHighResIntPcmToFloat
=
shouldConvertHighResIntPcmToFloat
=
enableConvertHighResIntPcmToFloat
enableConvertHighResIntPcmToFloat
&&
supportsOutput
(
channelCount
,
C
.
ENCODING_PCM_
32BI
T
)
&&
supportsOutput
(
channelCount
,
C
.
ENCODING_PCM_
FLOA
T
)
&&
Util
.
isEncodingHighResolutionIntegerPcm
(
inputEncoding
);
&&
Util
.
isEncodingHighResolutionIntegerPcm
(
inputEncoding
);
if
(
isInputPcm
)
{
if
(
isInputPcm
)
{
pcmFrameSize
=
Util
.
getPcmFrameSize
(
inputEncoding
,
channelCount
);
pcmFrameSize
=
Util
.
getPcmFrameSize
(
inputEncoding
,
channelCount
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/DefaultTsPayloadReaderFactory.java
View file @
de39925c
...
@@ -50,7 +50,8 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
...
@@ -50,7 +50,8 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
FLAG_IGNORE_H264_STREAM
,
FLAG_IGNORE_H264_STREAM
,
FLAG_DETECT_ACCESS_UNITS
,
FLAG_DETECT_ACCESS_UNITS
,
FLAG_IGNORE_SPLICE_INFO_STREAM
,
FLAG_IGNORE_SPLICE_INFO_STREAM
,
FLAG_OVERRIDE_CAPTION_DESCRIPTORS
FLAG_OVERRIDE_CAPTION_DESCRIPTORS
,
FLAG_IGNORE_HDMV_DTS_STREAM
})
})
public
@interface
Flags
{}
public
@interface
Flags
{}
...
@@ -86,6 +87,12 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
...
@@ -86,6 +87,12 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
* closedCaptionFormats} will be ignored if the PMT contains closed captions service descriptors.
* closedCaptionFormats} will be ignored if the PMT contains closed captions service descriptors.
*/
*/
public
static
final
int
FLAG_OVERRIDE_CAPTION_DESCRIPTORS
=
1
<<
5
;
public
static
final
int
FLAG_OVERRIDE_CAPTION_DESCRIPTORS
=
1
<<
5
;
/**
* Prevents the creation of {@link DtsReader} instances when receiving {@link
* TsExtractor#TS_STREAM_TYPE_HDMV_DTS} as stream type. Enabling this flag prevents a stream type
* collision between HDMV DTS audio and SCTE-35 subtitles.
*/
public
static
final
int
FLAG_IGNORE_HDMV_DTS_STREAM
=
1
<<
6
;
private
static
final
int
DESCRIPTOR_TAG_CAPTION_SERVICE
=
0x86
;
private
static
final
int
DESCRIPTOR_TAG_CAPTION_SERVICE
=
0x86
;
...
@@ -142,8 +149,12 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
...
@@ -142,8 +149,12 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
case
TsExtractor
.
TS_STREAM_TYPE_AC3
:
case
TsExtractor
.
TS_STREAM_TYPE_AC3
:
case
TsExtractor
.
TS_STREAM_TYPE_E_AC3
:
case
TsExtractor
.
TS_STREAM_TYPE_E_AC3
:
return
new
PesReader
(
new
Ac3Reader
(
esInfo
.
language
));
return
new
PesReader
(
new
Ac3Reader
(
esInfo
.
language
));
case
TsExtractor
.
TS_STREAM_TYPE_DTS
:
case
TsExtractor
.
TS_STREAM_TYPE_HDMV_DTS
:
case
TsExtractor
.
TS_STREAM_TYPE_HDMV_DTS
:
if
(
isSet
(
FLAG_IGNORE_HDMV_DTS_STREAM
))
{
return
null
;
}
// Fall through.
case
TsExtractor
.
TS_STREAM_TYPE_DTS
:
return
new
PesReader
(
new
DtsReader
(
esInfo
.
language
));
return
new
PesReader
(
new
DtsReader
(
esInfo
.
language
));
case
TsExtractor
.
TS_STREAM_TYPE_H262
:
case
TsExtractor
.
TS_STREAM_TYPE_H262
:
return
new
PesReader
(
new
H262Reader
(
buildUserDataReader
(
esInfo
)));
return
new
PesReader
(
new
H262Reader
(
buildUserDataReader
(
esInfo
)));
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java
View file @
de39925c
...
@@ -59,8 +59,6 @@ public final class MediaCodecUtil {
...
@@ -59,8 +59,6 @@ public final class MediaCodecUtil {
private
static
final
String
TAG
=
"MediaCodecUtil"
;
private
static
final
String
TAG
=
"MediaCodecUtil"
;
private
static
final
Pattern
PROFILE_PATTERN
=
Pattern
.
compile
(
"^\\D?(\\d+)$"
);
private
static
final
Pattern
PROFILE_PATTERN
=
Pattern
.
compile
(
"^\\D?(\\d+)$"
);
private
static
final
RawAudioCodecComparator
RAW_AUDIO_CODEC_COMPARATOR
=
new
RawAudioCodecComparator
();
private
static
final
HashMap
<
CodecKey
,
List
<
MediaCodecInfo
>>
decoderInfosCache
=
new
HashMap
<>();
private
static
final
HashMap
<
CodecKey
,
List
<
MediaCodecInfo
>>
decoderInfosCache
=
new
HashMap
<>();
...
@@ -312,32 +310,6 @@ public final class MediaCodecUtil {
...
@@ -312,32 +310,6 @@ public final class MediaCodecUtil {
return
false
;
return
false
;
}
}
// Work around https://github.com/google/ExoPlayer/issues/398.
if
(
Util
.
SDK_INT
<
18
&&
"OMX.SEC.MP3.Decoder"
.
equals
(
name
))
{
return
false
;
}
// Work around https://github.com/google/ExoPlayer/issues/4519.
if
(
"OMX.SEC.mp3.dec"
.
equals
(
name
)
&&
(
Util
.
MODEL
.
startsWith
(
"GT-I9152"
)
||
Util
.
MODEL
.
startsWith
(
"GT-I9515"
)
||
Util
.
MODEL
.
startsWith
(
"GT-P5220"
)
||
Util
.
MODEL
.
startsWith
(
"GT-S7580"
)
||
Util
.
MODEL
.
startsWith
(
"SM-G350"
)
||
Util
.
MODEL
.
startsWith
(
"SM-G386"
)
||
Util
.
MODEL
.
startsWith
(
"SM-T231"
)
||
Util
.
MODEL
.
startsWith
(
"SM-T530"
)
||
Util
.
MODEL
.
startsWith
(
"SCH-I535"
)
||
Util
.
MODEL
.
startsWith
(
"SPH-L710"
)))
{
return
false
;
}
if
(
"OMX.brcm.audio.mp3.decoder"
.
equals
(
name
)
&&
(
Util
.
MODEL
.
startsWith
(
"GT-I9152"
)
||
Util
.
MODEL
.
startsWith
(
"GT-S7580"
)
||
Util
.
MODEL
.
startsWith
(
"SM-G350"
)))
{
return
false
;
}
// Work around https://github.com/google/ExoPlayer/issues/1528 and
// Work around https://github.com/google/ExoPlayer/issues/1528 and
// https://github.com/google/ExoPlayer/issues/3171.
// https://github.com/google/ExoPlayer/issues/3171.
if
(
Util
.
SDK_INT
<
18
&&
"OMX.MTK.AUDIO.DECODER.AAC"
.
equals
(
name
)
if
(
Util
.
SDK_INT
<
18
&&
"OMX.MTK.AUDIO.DECODER.AAC"
.
equals
(
name
)
...
@@ -424,7 +396,18 @@ public final class MediaCodecUtil {
...
@@ -424,7 +396,18 @@ public final class MediaCodecUtil {
*/
*/
private
static
void
applyWorkarounds
(
String
mimeType
,
List
<
MediaCodecInfo
>
decoderInfos
)
{
private
static
void
applyWorkarounds
(
String
mimeType
,
List
<
MediaCodecInfo
>
decoderInfos
)
{
if
(
MimeTypes
.
AUDIO_RAW
.
equals
(
mimeType
))
{
if
(
MimeTypes
.
AUDIO_RAW
.
equals
(
mimeType
))
{
Collections
.
sort
(
decoderInfos
,
RAW_AUDIO_CODEC_COMPARATOR
);
Collections
.
sort
(
decoderInfos
,
new
RawAudioCodecComparator
());
}
else
if
(
Util
.
SDK_INT
<
21
&&
decoderInfos
.
size
()
>
1
)
{
String
firstCodecName
=
decoderInfos
.
get
(
0
).
name
;
if
(
"OMX.SEC.mp3.dec"
.
equals
(
firstCodecName
)
||
"OMX.SEC.MP3.Decoder"
.
equals
(
firstCodecName
)
||
"OMX.brcm.audio.mp3.decoder"
.
equals
(
firstCodecName
))
{
// Prefer OMX.google codecs over OMX.SEC.mp3.dec, OMX.SEC.MP3.Decoder and
// OMX.brcm.audio.mp3.decoder on older devices. See:
// https://github.com/google/ExoPlayer/issues/398 and
// https://github.com/google/ExoPlayer/issues/4519.
Collections
.
sort
(
decoderInfos
,
new
PreferOmxGoogleCodecComparator
());
}
}
}
}
}
...
@@ -730,6 +713,18 @@ public final class MediaCodecUtil {
...
@@ -730,6 +713,18 @@ public final class MediaCodecUtil {
}
}
}
}
/** Comparator for preferring OMX.google media codecs. */
private
static
final
class
PreferOmxGoogleCodecComparator
implements
Comparator
<
MediaCodecInfo
>
{
@Override
public
int
compare
(
MediaCodecInfo
a
,
MediaCodecInfo
b
)
{
return
scoreMediaCodecInfo
(
a
)
-
scoreMediaCodecInfo
(
b
);
}
private
static
int
scoreMediaCodecInfo
(
MediaCodecInfo
mediaCodecInfo
)
{
return
mediaCodecInfo
.
name
.
startsWith
(
"OMX.google"
)
?
-
1
:
0
;
}
}
static
{
static
{
AVC_PROFILE_NUMBER_TO_CONST
=
new
SparseIntArray
();
AVC_PROFILE_NUMBER_TO_CONST
=
new
SparseIntArray
();
AVC_PROFILE_NUMBER_TO_CONST
.
put
(
66
,
CodecProfileLevel
.
AVCProfileBaseline
);
AVC_PROFILE_NUMBER_TO_CONST
.
put
(
66
,
CodecProfileLevel
.
AVCProfileBaseline
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
View file @
de39925c
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source;
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.source;
import
android.os.Handler
;
import
android.os.Handler
;
import
android.os.Message
;
import
android.os.Message
;
import
android.support.annotation.GuardedBy
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.support.annotation.Nullable
;
import
android.util.Pair
;
import
android.util.Pair
;
...
@@ -35,9 +36,11 @@ import java.util.Arrays;
...
@@ -35,9 +36,11 @@ import java.util.Arrays;
import
java.util.Collection
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.HashMap
;
import
java.util.HashSet
;
import
java.util.IdentityHashMap
;
import
java.util.IdentityHashMap
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map
;
import
java.util.Set
;
/**
/**
* Concatenates multiple {@link MediaSource}s. The list of {@link MediaSource}s can be modified
* Concatenates multiple {@link MediaSource}s. The list of {@link MediaSource}s can be modified
...
@@ -50,25 +53,31 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -50,25 +53,31 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
private
static
final
int
MSG_REMOVE
=
1
;
private
static
final
int
MSG_REMOVE
=
1
;
private
static
final
int
MSG_MOVE
=
2
;
private
static
final
int
MSG_MOVE
=
2
;
private
static
final
int
MSG_SET_SHUFFLE_ORDER
=
3
;
private
static
final
int
MSG_SET_SHUFFLE_ORDER
=
3
;
private
static
final
int
MSG_
NOTIFY_LISTENER
=
4
;
private
static
final
int
MSG_
UPDATE_TIMELINE
=
4
;
private
static
final
int
MSG_ON_COMPLETION
=
5
;
private
static
final
int
MSG_ON_COMPLETION
=
5
;
// Accessed on the app thread.
// Accessed on any thread.
@GuardedBy
(
"this"
)
private
final
List
<
MediaSourceHolder
>
mediaSourcesPublic
;
private
final
List
<
MediaSourceHolder
>
mediaSourcesPublic
;
// Accessed on the playback thread.
@GuardedBy
(
"this"
)
private
final
Set
<
HandlerAndRunnable
>
pendingOnCompletionActions
;
@GuardedBy
(
"this"
)
@Nullable
private
Handler
playbackThreadHandler
;
// Accessed on the playback thread only.
private
final
List
<
MediaSourceHolder
>
mediaSourceHolders
;
private
final
List
<
MediaSourceHolder
>
mediaSourceHolders
;
private
final
Map
<
MediaPeriod
,
MediaSourceHolder
>
mediaSourceByMediaPeriod
;
private
final
Map
<
MediaPeriod
,
MediaSourceHolder
>
mediaSourceByMediaPeriod
;
private
final
Map
<
Object
,
MediaSourceHolder
>
mediaSourceByUid
;
private
final
Map
<
Object
,
MediaSourceHolder
>
mediaSourceByUid
;
private
final
List
<
Runnable
>
pendingOnCompletionActions
;
private
final
boolean
isAtomic
;
private
final
boolean
isAtomic
;
private
final
boolean
useLazyPreparation
;
private
final
boolean
useLazyPreparation
;
private
final
Timeline
.
Window
window
;
private
final
Timeline
.
Window
window
;
private
final
Timeline
.
Period
period
;
private
final
Timeline
.
Period
period
;
@Nullable
private
Handler
playbackThreadHandler
;
private
boolean
timelineUpdateScheduled
;
@Nullable
private
Handler
applicationThreadHandler
;
private
Set
<
HandlerAndRunnable
>
nextTimelineUpdateOnCompletionActions
;
private
boolean
listenerNotificationScheduled
;
private
ShuffleOrder
shuffleOrder
;
private
ShuffleOrder
shuffleOrder
;
private
int
windowCount
;
private
int
windowCount
;
private
int
periodCount
;
private
int
periodCount
;
...
@@ -127,7 +136,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -127,7 +136,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
this
.
mediaSourceByUid
=
new
HashMap
<>();
this
.
mediaSourceByUid
=
new
HashMap
<>();
this
.
mediaSourcesPublic
=
new
ArrayList
<>();
this
.
mediaSourcesPublic
=
new
ArrayList
<>();
this
.
mediaSourceHolders
=
new
ArrayList
<>();
this
.
mediaSourceHolders
=
new
ArrayList
<>();
this
.
pendingOnCompletionActions
=
new
ArrayList
<>();
this
.
nextTimelineUpdateOnCompletionActions
=
new
HashSet
<>();
this
.
pendingOnCompletionActions
=
new
HashSet
<>();
this
.
isAtomic
=
isAtomic
;
this
.
isAtomic
=
isAtomic
;
this
.
useLazyPreparation
=
useLazyPreparation
;
this
.
useLazyPreparation
=
useLazyPreparation
;
window
=
new
Timeline
.
Window
();
window
=
new
Timeline
.
Window
();
...
@@ -141,19 +151,20 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -141,19 +151,20 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
* @param mediaSource The {@link MediaSource} to be added to the list.
* @param mediaSource The {@link MediaSource} to be added to the list.
*/
*/
public
final
synchronized
void
addMediaSource
(
MediaSource
mediaSource
)
{
public
final
synchronized
void
addMediaSource
(
MediaSource
mediaSource
)
{
addMediaSource
(
mediaSourcesPublic
.
size
(),
mediaSource
,
null
);
addMediaSource
(
mediaSourcesPublic
.
size
(),
mediaSource
);
}
}
/**
/**
* Appends a {@link MediaSource} to the playlist and executes a custom action on completion.
* Appends a {@link MediaSource} to the playlist and executes a custom action on completion.
*
*
* @param mediaSource The {@link MediaSource} to be added to the list.
* @param mediaSource The {@link MediaSource} to be added to the list.
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
* @param handler The {@link Handler} to run {@code onCompletionAction}.
* @param onCompletionAction A {@link Runnable} which is executed immediately after the media
* source has been added to the playlist.
* source has been added to the playlist.
*/
*/
public
final
synchronized
void
addMediaSource
(
public
final
synchronized
void
addMediaSource
(
MediaSource
mediaSource
,
@Nullable
Runnable
actionOnComple
tion
)
{
MediaSource
mediaSource
,
Handler
handler
,
Runnable
onCompletionAc
tion
)
{
addMediaSource
(
mediaSourcesPublic
.
size
(),
mediaSource
,
actionOnComple
tion
);
addMediaSource
(
mediaSourcesPublic
.
size
(),
mediaSource
,
handler
,
onCompletionAc
tion
);
}
}
/**
/**
...
@@ -164,7 +175,11 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -164,7 +175,11 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
* @param mediaSource The {@link MediaSource} to be added to the list.
* @param mediaSource The {@link MediaSource} to be added to the list.
*/
*/
public
final
synchronized
void
addMediaSource
(
int
index
,
MediaSource
mediaSource
)
{
public
final
synchronized
void
addMediaSource
(
int
index
,
MediaSource
mediaSource
)
{
addMediaSource
(
index
,
mediaSource
,
null
);
addPublicMediaSources
(
index
,
Collections
.
singletonList
(
mediaSource
),
/* handler= */
null
,
/* onCompletionAction= */
null
);
}
}
/**
/**
...
@@ -173,12 +188,14 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -173,12 +188,14 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
* @param index The index at which the new {@link MediaSource} will be inserted. This index must
* @param index The index at which the new {@link MediaSource} will be inserted. This index must
* be in the range of 0 <= index <= {@link #getSize()}.
* be in the range of 0 <= index <= {@link #getSize()}.
* @param mediaSource The {@link MediaSource} to be added to the list.
* @param mediaSource The {@link MediaSource} to be added to the list.
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
* @param handler The {@link Handler} to run {@code onCompletionAction}.
* @param onCompletionAction A {@link Runnable} which is executed immediately after the media
* source has been added to the playlist.
* source has been added to the playlist.
*/
*/
public
final
synchronized
void
addMediaSource
(
public
final
synchronized
void
addMediaSource
(
int
index
,
MediaSource
mediaSource
,
@Nullable
Runnable
actionOnCompletion
)
{
int
index
,
MediaSource
mediaSource
,
Handler
handler
,
Runnable
onCompletionAction
)
{
addMediaSources
(
index
,
Collections
.
singletonList
(
mediaSource
),
actionOnCompletion
);
addPublicMediaSources
(
index
,
Collections
.
singletonList
(
mediaSource
),
handler
,
onCompletionAction
);
}
}
/**
/**
...
@@ -188,7 +205,11 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -188,7 +205,11 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
* sources are added in the order in which they appear in this collection.
* sources are added in the order in which they appear in this collection.
*/
*/
public
final
synchronized
void
addMediaSources
(
Collection
<
MediaSource
>
mediaSources
)
{
public
final
synchronized
void
addMediaSources
(
Collection
<
MediaSource
>
mediaSources
)
{
addMediaSources
(
mediaSourcesPublic
.
size
(),
mediaSources
,
null
);
addPublicMediaSources
(
mediaSourcesPublic
.
size
(),
mediaSources
,
/* handler= */
null
,
/* onCompletionAction= */
null
);
}
}
/**
/**
...
@@ -197,12 +218,13 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -197,12 +218,13 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
*
*
* @param mediaSources A collection of {@link MediaSource}s to be added to the list. The media
* @param mediaSources A collection of {@link MediaSource}s to be added to the list. The media
* sources are added in the order in which they appear in this collection.
* sources are added in the order in which they appear in this collection.
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
* @param handler The {@link Handler} to run {@code onCompletionAction}.
* @param onCompletionAction A {@link Runnable} which is executed immediately after the media
* sources have been added to the playlist.
* sources have been added to the playlist.
*/
*/
public
final
synchronized
void
addMediaSources
(
public
final
synchronized
void
addMediaSources
(
Collection
<
MediaSource
>
mediaSources
,
@Nullable
Runnable
actionOnComple
tion
)
{
Collection
<
MediaSource
>
mediaSources
,
Handler
handler
,
Runnable
onCompletionAc
tion
)
{
add
MediaSources
(
mediaSourcesPublic
.
size
(),
mediaSources
,
actionOnComple
tion
);
add
PublicMediaSources
(
mediaSourcesPublic
.
size
(),
mediaSources
,
handler
,
onCompletionAc
tion
);
}
}
/**
/**
...
@@ -214,7 +236,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -214,7 +236,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
* sources are added in the order in which they appear in this collection.
* sources are added in the order in which they appear in this collection.
*/
*/
public
final
synchronized
void
addMediaSources
(
int
index
,
Collection
<
MediaSource
>
mediaSources
)
{
public
final
synchronized
void
addMediaSources
(
int
index
,
Collection
<
MediaSource
>
mediaSources
)
{
add
MediaSources
(
index
,
mediaSources
,
null
);
add
PublicMediaSources
(
index
,
mediaSources
,
/* handler= */
null
,
/* onCompletionAction= */
null
);
}
}
/**
/**
...
@@ -224,26 +246,16 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -224,26 +246,16 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
* be in the range of 0 <= index <= {@link #getSize()}.
* be in the range of 0 <= index <= {@link #getSize()}.
* @param mediaSources A collection of {@link MediaSource}s to be added to the list. The media
* @param mediaSources A collection of {@link MediaSource}s to be added to the list. The media
* sources are added in the order in which they appear in this collection.
* sources are added in the order in which they appear in this collection.
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
* @param handler The {@link Handler} to run {@code onCompletionAction}.
* @param onCompletionAction A {@link Runnable} which is executed immediately after the media
* sources have been added to the playlist.
* sources have been added to the playlist.
*/
*/
public
final
synchronized
void
addMediaSources
(
public
final
synchronized
void
addMediaSources
(
int
index
,
Collection
<
MediaSource
>
mediaSources
,
@Nullable
Runnable
actionOnCompletion
)
{
int
index
,
for
(
MediaSource
mediaSource
:
mediaSources
)
{
Collection
<
MediaSource
>
mediaSources
,
Assertions
.
checkNotNull
(
mediaSource
);
Handler
handler
,
}
Runnable
onCompletionAction
)
{
List
<
MediaSourceHolder
>
mediaSourceHolders
=
new
ArrayList
<>(
mediaSources
.
size
());
addPublicMediaSources
(
index
,
mediaSources
,
handler
,
onCompletionAction
);
for
(
MediaSource
mediaSource
:
mediaSources
)
{
mediaSourceHolders
.
add
(
new
MediaSourceHolder
(
mediaSource
));
}
mediaSourcesPublic
.
addAll
(
index
,
mediaSourceHolders
);
if
(
playbackThreadHandler
!=
null
&&
!
mediaSources
.
isEmpty
())
{
playbackThreadHandler
.
obtainMessage
(
MSG_ADD
,
new
MessageData
<>(
index
,
mediaSourceHolders
,
actionOnCompletion
))
.
sendToTarget
();
}
else
if
(
actionOnCompletion
!=
null
)
{
actionOnCompletion
.
run
();
}
}
}
/**
/**
...
@@ -259,26 +271,27 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -259,26 +271,27 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
* range of 0 <= index < {@link #getSize()}.
* range of 0 <= index < {@link #getSize()}.
*/
*/
public
final
synchronized
void
removeMediaSource
(
int
index
)
{
public
final
synchronized
void
removeMediaSource
(
int
index
)
{
remove
MediaSource
(
index
,
null
);
remove
PublicMediaSources
(
index
,
index
+
1
,
/* handler= */
null
,
/* onCompletionAction= */
null
);
}
}
/**
/**
* Removes a {@link MediaSource} from the playlist and executes a custom action on completion.
* Removes a {@link MediaSource} from the playlist and executes a custom action on completion.
*
*
* <p>Note: If you want to move the instance, it's preferable to use {@link #moveMediaSource(int,
* <p>Note: If you want to move the instance, it's preferable to use {@link #moveMediaSource(int,
* int, Runnable)} instead.
* int,
Handler,
Runnable)} instead.
*
*
* <p>Note: If you want to remove a set of contiguous sources, it's preferable to use {@link
* <p>Note: If you want to remove a set of contiguous sources, it's preferable to use {@link
* #removeMediaSourceRange(int, int, Runnable)} instead.
* #removeMediaSourceRange(int, int,
Handler,
Runnable)} instead.
*
*
* @param index The index at which the media source will be removed. This index must be in the
* @param index The index at which the media source will be removed. This index must be in the
* range of 0 <= index < {@link #getSize()}.
* range of 0 <= index < {@link #getSize()}.
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
* @param handler The {@link Handler} to run {@code onCompletionAction}.
* @param onCompletionAction A {@link Runnable} which is executed immediately after the media
* source has been removed from the playlist.
* source has been removed from the playlist.
*/
*/
public
final
synchronized
void
removeMediaSource
(
public
final
synchronized
void
removeMediaSource
(
int
index
,
@Nullable
Runnable
actionOnComple
tion
)
{
int
index
,
Handler
handler
,
Runnable
onCompletionAc
tion
)
{
remove
MediaSourceRange
(
index
,
index
+
1
,
actionOnComple
tion
);
remove
PublicMediaSources
(
index
,
index
+
1
,
handler
,
onCompletionAc
tion
);
}
}
/**
/**
...
@@ -296,7 +309,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -296,7 +309,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
* {@code toIndex} > {@link #getSize()}, {@code fromIndex} > {@code toIndex}
* {@code toIndex} > {@link #getSize()}, {@code fromIndex} > {@code toIndex}
*/
*/
public
final
synchronized
void
removeMediaSourceRange
(
int
fromIndex
,
int
toIndex
)
{
public
final
synchronized
void
removeMediaSourceRange
(
int
fromIndex
,
int
toIndex
)
{
removeMediaSourceRange
(
fromIndex
,
toIndex
,
null
);
removePublicMediaSources
(
fromIndex
,
toIndex
,
/* handler= */
null
,
/* onCompletionAction= */
null
);
}
}
/**
/**
...
@@ -310,27 +324,15 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -310,27 +324,15 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
* removed. This index must be in the range of 0 <= index <= {@link #getSize()}.
* removed. This index must be in the range of 0 <= index <= {@link #getSize()}.
* @param toIndex The final range index, pointing to the first media source that will be left
* @param toIndex The final range index, pointing to the first media source that will be left
* untouched. This index must be in the range of 0 <= index <= {@link #getSize()}.
* untouched. This index must be in the range of 0 <= index <= {@link #getSize()}.
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
* @param handler The {@link Handler} to run {@code onCompletionAction}.
* @param onCompletionAction A {@link Runnable} which is executed immediately after the media
* source range has been removed from the playlist.
* source range has been removed from the playlist.
* @throws IllegalArgumentException When the range is malformed, i.e. {@code fromIndex} < 0,
* @throws IllegalArgumentException When the range is malformed, i.e. {@code fromIndex} < 0,
* {@code toIndex} > {@link #getSize()}, {@code fromIndex} > {@code toIndex}
* {@code toIndex} > {@link #getSize()}, {@code fromIndex} > {@code toIndex}
*/
*/
public
final
synchronized
void
removeMediaSourceRange
(
public
final
synchronized
void
removeMediaSourceRange
(
int
fromIndex
,
int
toIndex
,
@Nullable
Runnable
actionOnCompletion
)
{
int
fromIndex
,
int
toIndex
,
Handler
handler
,
Runnable
onCompletionAction
)
{
Util
.
removeRange
(
mediaSourcesPublic
,
fromIndex
,
toIndex
);
removePublicMediaSources
(
fromIndex
,
toIndex
,
handler
,
onCompletionAction
);
if
(
fromIndex
==
toIndex
)
{
if
(
actionOnCompletion
!=
null
)
{
actionOnCompletion
.
run
();
}
return
;
}
if
(
playbackThreadHandler
!=
null
)
{
playbackThreadHandler
.
obtainMessage
(
MSG_REMOVE
,
new
MessageData
<>(
fromIndex
,
toIndex
,
actionOnCompletion
))
.
sendToTarget
();
}
else
if
(
actionOnCompletion
!=
null
)
{
actionOnCompletion
.
run
();
}
}
}
/**
/**
...
@@ -342,7 +344,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -342,7 +344,8 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
* range of 0 <= index < {@link #getSize()}.
* range of 0 <= index < {@link #getSize()}.
*/
*/
public
final
synchronized
void
moveMediaSource
(
int
currentIndex
,
int
newIndex
)
{
public
final
synchronized
void
moveMediaSource
(
int
currentIndex
,
int
newIndex
)
{
moveMediaSource
(
currentIndex
,
newIndex
,
null
);
movePublicMediaSource
(
currentIndex
,
newIndex
,
/* handler= */
null
,
/* onCompletionAction= */
null
);
}
}
/**
/**
...
@@ -353,40 +356,29 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -353,40 +356,29 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
* in the range of 0 <= index < {@link #getSize()}.
* in the range of 0 <= index < {@link #getSize()}.
* @param newIndex The target index of the media source in the playlist. This index must be in the
* @param newIndex The target index of the media source in the playlist. This index must be in the
* range of 0 <= index < {@link #getSize()}.
* range of 0 <= index < {@link #getSize()}.
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the media
* @param handler The {@link Handler} to run {@code onCompletionAction}.
* @param onCompletionAction A {@link Runnable} which is executed immediately after the media
* source has been moved.
* source has been moved.
*/
*/
public
final
synchronized
void
moveMediaSource
(
public
final
synchronized
void
moveMediaSource
(
int
currentIndex
,
int
newIndex
,
@Nullable
Runnable
actionOnCompletion
)
{
int
currentIndex
,
int
newIndex
,
Handler
handler
,
Runnable
onCompletionAction
)
{
if
(
currentIndex
==
newIndex
)
{
movePublicMediaSource
(
currentIndex
,
newIndex
,
handler
,
onCompletionAction
);
if
(
actionOnCompletion
!=
null
)
{
actionOnCompletion
.
run
();
}
return
;
}
mediaSourcesPublic
.
add
(
newIndex
,
mediaSourcesPublic
.
remove
(
currentIndex
));
if
(
playbackThreadHandler
!=
null
)
{
playbackThreadHandler
.
obtainMessage
(
MSG_MOVE
,
new
MessageData
<>(
currentIndex
,
newIndex
,
actionOnCompletion
))
.
sendToTarget
();
}
else
if
(
actionOnCompletion
!=
null
)
{
actionOnCompletion
.
run
();
}
}
}
/** Clears the playlist. */
/** Clears the playlist. */
public
final
synchronized
void
clear
()
{
public
final
synchronized
void
clear
()
{
clear
(
/* actionOnCompletion= */
null
);
removeMediaSourceRange
(
0
,
getSize
()
);
}
}
/**
/**
* Clears the playlist and executes a custom action on completion.
* Clears the playlist and executes a custom action on completion.
*
*
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the playlist
* @param handler The {@link Handler} to run {@code onCompletionAction}.
* @param onCompletionAction A {@link Runnable} which is executed immediately after the playlist
* has been cleared.
* has been cleared.
*/
*/
public
final
synchronized
void
clear
(
@Nullable
Runnable
actionOnComple
tion
)
{
public
final
synchronized
void
clear
(
Handler
handler
,
Runnable
onCompletionAc
tion
)
{
removeMediaSourceRange
(
0
,
getSize
(),
actionOnComple
tion
);
removeMediaSourceRange
(
0
,
getSize
(),
handler
,
onCompletionAc
tion
);
}
}
/** Returns the number of media sources in the playlist. */
/** Returns the number of media sources in the playlist. */
...
@@ -410,41 +402,24 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -410,41 +402,24 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
* @param shuffleOrder A {@link ShuffleOrder}.
* @param shuffleOrder A {@link ShuffleOrder}.
*/
*/
public
final
synchronized
void
setShuffleOrder
(
ShuffleOrder
shuffleOrder
)
{
public
final
synchronized
void
setShuffleOrder
(
ShuffleOrder
shuffleOrder
)
{
set
ShuffleOrder
(
shuffleOrder
,
/* actionOnComple
tion= */
null
);
set
PublicShuffleOrder
(
shuffleOrder
,
/* handler= */
null
,
/* onCompletionAc
tion= */
null
);
}
}
/**
/**
* Sets a new shuffle order to use when shuffling the child media sources.
* Sets a new shuffle order to use when shuffling the child media sources.
*
*
* @param shuffleOrder A {@link ShuffleOrder}.
* @param shuffleOrder A {@link ShuffleOrder}.
* @param actionOnCompletion A {@link Runnable} which is executed immediately after the shuffle
* @param handler The {@link Handler} to run {@code onCompletionAction}.
* @param onCompletionAction A {@link Runnable} which is executed immediately after the shuffle
* order has been changed.
* order has been changed.
*/
*/
public
final
synchronized
void
setShuffleOrder
(
public
final
synchronized
void
setShuffleOrder
(
ShuffleOrder
shuffleOrder
,
@Nullable
Runnable
actionOnCompletion
)
{
ShuffleOrder
shuffleOrder
,
Handler
handler
,
Runnable
onCompletionAction
)
{
Handler
playbackThreadHandler
=
this
.
playbackThreadHandler
;
setPublicShuffleOrder
(
shuffleOrder
,
handler
,
onCompletionAction
);
if
(
playbackThreadHandler
!=
null
)
{
int
size
=
getSize
();
if
(
shuffleOrder
.
getLength
()
!=
size
)
{
shuffleOrder
=
shuffleOrder
.
cloneAndClear
()
.
cloneAndInsert
(
/* insertionIndex= */
0
,
/* insertionCount= */
size
);
}
playbackThreadHandler
.
obtainMessage
(
MSG_SET_SHUFFLE_ORDER
,
new
MessageData
<>(
/* index= */
0
,
shuffleOrder
,
actionOnCompletion
))
.
sendToTarget
();
}
else
{
this
.
shuffleOrder
=
shuffleOrder
.
getLength
()
>
0
?
shuffleOrder
.
cloneAndClear
()
:
shuffleOrder
;
if
(
actionOnCompletion
!=
null
)
{
actionOnCompletion
.
run
();
}
}
}
}
// CompositeMediaSource implementation.
@Override
@Override
@Nullable
@Nullable
public
Object
getTag
()
{
public
Object
getTag
()
{
...
@@ -458,13 +433,12 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -458,13 +433,12 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
@Nullable
TransferListener
mediaTransferListener
)
{
@Nullable
TransferListener
mediaTransferListener
)
{
super
.
prepareSourceInternal
(
player
,
isTopLevelSource
,
mediaTransferListener
);
super
.
prepareSourceInternal
(
player
,
isTopLevelSource
,
mediaTransferListener
);
playbackThreadHandler
=
new
Handler
(
/* callback= */
this
::
handleMessage
);
playbackThreadHandler
=
new
Handler
(
/* callback= */
this
::
handleMessage
);
applicationThreadHandler
=
new
Handler
(
player
.
getApplicationLooper
());
if
(
mediaSourcesPublic
.
isEmpty
())
{
if
(
mediaSourcesPublic
.
isEmpty
())
{
notifyListener
();
updateTimelineAndScheduleOnCompletionActions
();
}
else
{
}
else
{
shuffleOrder
=
shuffleOrder
.
cloneAndInsert
(
0
,
mediaSourcesPublic
.
size
());
shuffleOrder
=
shuffleOrder
.
cloneAndInsert
(
0
,
mediaSourcesPublic
.
size
());
addMediaSourcesInternal
(
0
,
mediaSourcesPublic
);
addMediaSourcesInternal
(
0
,
mediaSourcesPublic
);
schedule
ListenerNotification
(
/* actionOnCompletion= */
null
);
schedule
TimelineUpdate
(
);
}
}
}
}
...
@@ -509,15 +483,20 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -509,15 +483,20 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
}
}
@Override
@Override
public
final
void
releaseSourceInternal
()
{
public
final
synchronized
void
releaseSourceInternal
()
{
super
.
releaseSourceInternal
();
super
.
releaseSourceInternal
();
mediaSourceHolders
.
clear
();
mediaSourceHolders
.
clear
();
mediaSourceByUid
.
clear
();
mediaSourceByUid
.
clear
();
playbackThreadHandler
=
null
;
applicationThreadHandler
=
null
;
shuffleOrder
=
shuffleOrder
.
cloneAndClear
();
shuffleOrder
=
shuffleOrder
.
cloneAndClear
();
windowCount
=
0
;
windowCount
=
0
;
periodCount
=
0
;
periodCount
=
0
;
if
(
playbackThreadHandler
!=
null
)
{
playbackThreadHandler
.
removeCallbacksAndMessages
(
null
);
playbackThreadHandler
=
null
;
}
timelineUpdateScheduled
=
false
;
nextTimelineUpdateOnCompletionActions
.
clear
();
dispatchOnCompletionActions
(
pendingOnCompletionActions
);
}
}
@Override
@Override
...
@@ -550,19 +529,123 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -550,19 +529,123 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
return
windowIndex
+
mediaSourceHolder
.
firstWindowIndexInChild
;
return
windowIndex
+
mediaSourceHolder
.
firstWindowIndexInChild
;
}
}
// Internal methods. Called from any thread.
@GuardedBy
(
"this"
)
private
void
addPublicMediaSources
(
int
index
,
Collection
<
MediaSource
>
mediaSources
,
@Nullable
Handler
handler
,
@Nullable
Runnable
onCompletionAction
)
{
Assertions
.
checkArgument
((
handler
==
null
)
==
(
onCompletionAction
==
null
));
Handler
playbackThreadHandler
=
this
.
playbackThreadHandler
;
for
(
MediaSource
mediaSource
:
mediaSources
)
{
Assertions
.
checkNotNull
(
mediaSource
);
}
List
<
MediaSourceHolder
>
mediaSourceHolders
=
new
ArrayList
<>(
mediaSources
.
size
());
for
(
MediaSource
mediaSource
:
mediaSources
)
{
mediaSourceHolders
.
add
(
new
MediaSourceHolder
(
mediaSource
));
}
mediaSourcesPublic
.
addAll
(
index
,
mediaSourceHolders
);
if
(
playbackThreadHandler
!=
null
&&
!
mediaSources
.
isEmpty
())
{
HandlerAndRunnable
callbackAction
=
createOnCompletionAction
(
handler
,
onCompletionAction
);
playbackThreadHandler
.
obtainMessage
(
MSG_ADD
,
new
MessageData
<>(
index
,
mediaSourceHolders
,
callbackAction
))
.
sendToTarget
();
}
else
if
(
onCompletionAction
!=
null
&&
handler
!=
null
)
{
handler
.
post
(
onCompletionAction
);
}
}
@GuardedBy
(
"this"
)
private
void
removePublicMediaSources
(
int
fromIndex
,
int
toIndex
,
@Nullable
Handler
handler
,
@Nullable
Runnable
onCompletionAction
)
{
Assertions
.
checkArgument
((
handler
==
null
)
==
(
onCompletionAction
==
null
));
Handler
playbackThreadHandler
=
this
.
playbackThreadHandler
;
Util
.
removeRange
(
mediaSourcesPublic
,
fromIndex
,
toIndex
);
if
(
playbackThreadHandler
!=
null
)
{
HandlerAndRunnable
callbackAction
=
createOnCompletionAction
(
handler
,
onCompletionAction
);
playbackThreadHandler
.
obtainMessage
(
MSG_REMOVE
,
new
MessageData
<>(
fromIndex
,
toIndex
,
callbackAction
))
.
sendToTarget
();
}
else
if
(
onCompletionAction
!=
null
&&
handler
!=
null
)
{
handler
.
post
(
onCompletionAction
);
}
}
@GuardedBy
(
"this"
)
private
void
movePublicMediaSource
(
int
currentIndex
,
int
newIndex
,
@Nullable
Handler
handler
,
@Nullable
Runnable
onCompletionAction
)
{
Assertions
.
checkArgument
((
handler
==
null
)
==
(
onCompletionAction
==
null
));
Handler
playbackThreadHandler
=
this
.
playbackThreadHandler
;
mediaSourcesPublic
.
add
(
newIndex
,
mediaSourcesPublic
.
remove
(
currentIndex
));
if
(
playbackThreadHandler
!=
null
)
{
HandlerAndRunnable
callbackAction
=
createOnCompletionAction
(
handler
,
onCompletionAction
);
playbackThreadHandler
.
obtainMessage
(
MSG_MOVE
,
new
MessageData
<>(
currentIndex
,
newIndex
,
callbackAction
))
.
sendToTarget
();
}
else
if
(
onCompletionAction
!=
null
&&
handler
!=
null
)
{
handler
.
post
(
onCompletionAction
);
}
}
@GuardedBy
(
"this"
)
private
void
setPublicShuffleOrder
(
ShuffleOrder
shuffleOrder
,
@Nullable
Handler
handler
,
@Nullable
Runnable
onCompletionAction
)
{
Assertions
.
checkArgument
((
handler
==
null
)
==
(
onCompletionAction
==
null
));
Handler
playbackThreadHandler
=
this
.
playbackThreadHandler
;
if
(
playbackThreadHandler
!=
null
)
{
int
size
=
getSize
();
if
(
shuffleOrder
.
getLength
()
!=
size
)
{
shuffleOrder
=
shuffleOrder
.
cloneAndClear
()
.
cloneAndInsert
(
/* insertionIndex= */
0
,
/* insertionCount= */
size
);
}
HandlerAndRunnable
callbackAction
=
createOnCompletionAction
(
handler
,
onCompletionAction
);
playbackThreadHandler
.
obtainMessage
(
MSG_SET_SHUFFLE_ORDER
,
new
MessageData
<>(
/* index= */
0
,
shuffleOrder
,
callbackAction
))
.
sendToTarget
();
}
else
{
this
.
shuffleOrder
=
shuffleOrder
.
getLength
()
>
0
?
shuffleOrder
.
cloneAndClear
()
:
shuffleOrder
;
if
(
onCompletionAction
!=
null
&&
handler
!=
null
)
{
handler
.
post
(
onCompletionAction
);
}
}
}
@GuardedBy
(
"this"
)
@Nullable
private
HandlerAndRunnable
createOnCompletionAction
(
@Nullable
Handler
handler
,
@Nullable
Runnable
runnable
)
{
if
(
handler
==
null
||
runnable
==
null
)
{
return
null
;
}
HandlerAndRunnable
handlerAndRunnable
=
new
HandlerAndRunnable
(
handler
,
runnable
);
pendingOnCompletionActions
.
add
(
handlerAndRunnable
);
return
handlerAndRunnable
;
}
// Internal methods. Called on the playback thread.
@SuppressWarnings
(
"unchecked"
)
@SuppressWarnings
(
"unchecked"
)
private
boolean
handleMessage
(
Message
msg
)
{
private
boolean
handleMessage
(
Message
msg
)
{
if
(
playbackThreadHandler
==
null
)
{
// Stale event.
return
false
;
}
switch
(
msg
.
what
)
{
switch
(
msg
.
what
)
{
case
MSG_ADD:
case
MSG_ADD:
MessageData
<
Collection
<
MediaSourceHolder
>>
addMessage
=
MessageData
<
Collection
<
MediaSourceHolder
>>
addMessage
=
(
MessageData
<
Collection
<
MediaSourceHolder
>>)
Util
.
castNonNull
(
msg
.
obj
);
(
MessageData
<
Collection
<
MediaSourceHolder
>>)
Util
.
castNonNull
(
msg
.
obj
);
shuffleOrder
=
shuffleOrder
.
cloneAndInsert
(
addMessage
.
index
,
addMessage
.
customData
.
size
());
shuffleOrder
=
shuffleOrder
.
cloneAndInsert
(
addMessage
.
index
,
addMessage
.
customData
.
size
());
addMediaSourcesInternal
(
addMessage
.
index
,
addMessage
.
customData
);
addMediaSourcesInternal
(
addMessage
.
index
,
addMessage
.
customData
);
schedule
ListenerNotification
(
addMessage
.
actionOnComple
tion
);
schedule
TimelineUpdate
(
addMessage
.
onCompletionAc
tion
);
break
;
break
;
case
MSG_REMOVE:
case
MSG_REMOVE:
MessageData
<
Integer
>
removeMessage
=
(
MessageData
<
Integer
>)
Util
.
castNonNull
(
msg
.
obj
);
MessageData
<
Integer
>
removeMessage
=
(
MessageData
<
Integer
>)
Util
.
castNonNull
(
msg
.
obj
);
...
@@ -576,30 +659,27 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -576,30 +659,27 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
for
(
int
index
=
toIndex
-
1
;
index
>=
fromIndex
;
index
--)
{
for
(
int
index
=
toIndex
-
1
;
index
>=
fromIndex
;
index
--)
{
removeMediaSourceInternal
(
index
);
removeMediaSourceInternal
(
index
);
}
}
schedule
ListenerNotification
(
removeMessage
.
actionOnComple
tion
);
schedule
TimelineUpdate
(
removeMessage
.
onCompletionAc
tion
);
break
;
break
;
case
MSG_MOVE:
case
MSG_MOVE:
MessageData
<
Integer
>
moveMessage
=
(
MessageData
<
Integer
>)
Util
.
castNonNull
(
msg
.
obj
);
MessageData
<
Integer
>
moveMessage
=
(
MessageData
<
Integer
>)
Util
.
castNonNull
(
msg
.
obj
);
shuffleOrder
=
shuffleOrder
.
cloneAndRemove
(
moveMessage
.
index
,
moveMessage
.
index
+
1
);
shuffleOrder
=
shuffleOrder
.
cloneAndRemove
(
moveMessage
.
index
,
moveMessage
.
index
+
1
);
shuffleOrder
=
shuffleOrder
.
cloneAndInsert
(
moveMessage
.
customData
,
1
);
shuffleOrder
=
shuffleOrder
.
cloneAndInsert
(
moveMessage
.
customData
,
1
);
moveMediaSourceInternal
(
moveMessage
.
index
,
moveMessage
.
customData
);
moveMediaSourceInternal
(
moveMessage
.
index
,
moveMessage
.
customData
);
schedule
ListenerNotification
(
moveMessage
.
actionOnComple
tion
);
schedule
TimelineUpdate
(
moveMessage
.
onCompletionAc
tion
);
break
;
break
;
case
MSG_SET_SHUFFLE_ORDER:
case
MSG_SET_SHUFFLE_ORDER:
MessageData
<
ShuffleOrder
>
shuffleOrderMessage
=
MessageData
<
ShuffleOrder
>
shuffleOrderMessage
=
(
MessageData
<
ShuffleOrder
>)
Util
.
castNonNull
(
msg
.
obj
);
(
MessageData
<
ShuffleOrder
>)
Util
.
castNonNull
(
msg
.
obj
);
shuffleOrder
=
shuffleOrderMessage
.
customData
;
shuffleOrder
=
shuffleOrderMessage
.
customData
;
schedule
ListenerNotification
(
shuffleOrderMessage
.
actionOnComple
tion
);
schedule
TimelineUpdate
(
shuffleOrderMessage
.
onCompletionAc
tion
);
break
;
break
;
case
MSG_
NOTIFY_LISTENER
:
case
MSG_
UPDATE_TIMELINE
:
notifyListener
();
updateTimelineAndScheduleOnCompletionActions
();
break
;
break
;
case
MSG_ON_COMPLETION:
case
MSG_ON_COMPLETION:
List
<
Runnable
>
actionsOnCompletion
=
(
List
<
Runnable
>)
Util
.
castNonNull
(
msg
.
obj
);
Set
<
HandlerAndRunnable
>
actions
=
(
Set
<
HandlerAndRunnable
>)
Util
.
castNonNull
(
msg
.
obj
);
Handler
handler
=
Assertions
.
checkNotNull
(
applicationThreadHandler
);
dispatchOnCompletionActions
(
actions
);
for
(
int
i
=
0
;
i
<
actionsOnCompletion
.
size
();
i
++)
{
handler
.
post
(
actionsOnCompletion
.
get
(
i
));
}
break
;
break
;
default
:
default
:
throw
new
IllegalStateException
();
throw
new
IllegalStateException
();
...
@@ -607,34 +687,46 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -607,34 +687,46 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
return
true
;
return
true
;
}
}
private
void
scheduleListenerNotification
(
@Nullable
Runnable
actionOnCompletion
)
{
private
void
scheduleTimelineUpdate
()
{
if
(!
listenerNotificationScheduled
)
{
scheduleTimelineUpdate
(
/* onCompletionAction= */
null
);
Assertions
.
checkNotNull
(
playbackThreadHandler
)
}
.
obtainMessage
(
MSG_NOTIFY_LISTENER
)
.
sendToTarget
();
private
void
scheduleTimelineUpdate
(
@Nullable
HandlerAndRunnable
onCompletionAction
)
{
listenerNotificationScheduled
=
true
;
if
(!
timelineUpdateScheduled
)
{
getPlaybackThreadHandlerOnPlaybackThread
().
obtainMessage
(
MSG_UPDATE_TIMELINE
).
sendToTarget
();
timelineUpdateScheduled
=
true
;
}
}
if
(
actionOnComple
tion
!=
null
)
{
if
(
onCompletionAc
tion
!=
null
)
{
pendingOnCompletionActions
.
add
(
actionOnComple
tion
);
nextTimelineUpdateOnCompletionActions
.
add
(
onCompletionAc
tion
);
}
}
}
}
private
void
notifyListener
()
{
private
void
updateTimelineAndScheduleOnCompletionActions
()
{
listenerNotificationScheduled
=
false
;
timelineUpdateScheduled
=
false
;
List
<
Runnable
>
actionsOnCompletion
=
Set
<
HandlerAndRunnable
>
onCompletionActions
=
nextTimelineUpdateOnCompletionActions
;
pendingOnCompletionActions
.
isEmpty
()
nextTimelineUpdateOnCompletionActions
=
new
HashSet
<>();
?
Collections
.
emptyList
()
:
new
ArrayList
<>(
pendingOnCompletionActions
);
pendingOnCompletionActions
.
clear
();
refreshSourceInfo
(
refreshSourceInfo
(
new
ConcatenatedTimeline
(
new
ConcatenatedTimeline
(
mediaSourceHolders
,
windowCount
,
periodCount
,
shuffleOrder
,
isAtomic
),
mediaSourceHolders
,
windowCount
,
periodCount
,
shuffleOrder
,
isAtomic
),
/* manifest= */
null
);
/* manifest= */
null
);
if
(!
actionsOnCompletion
.
isEmpty
())
{
getPlaybackThreadHandlerOnPlaybackThread
()
Assertions
.
checkNotNull
(
playbackThreadHandler
)
.
obtainMessage
(
MSG_ON_COMPLETION
,
onCompletionActions
)
.
obtainMessage
(
MSG_ON_COMPLETION
,
actionsOnCompletion
)
.
sendToTarget
();
.
sendToTarget
();
}
@SuppressWarnings
(
"GuardedBy"
)
private
Handler
getPlaybackThreadHandlerOnPlaybackThread
()
{
// Write access to this value happens on the playback thread only, so playback thread reads
// don't need to be synchronized.
return
Assertions
.
checkNotNull
(
playbackThreadHandler
);
}
private
synchronized
void
dispatchOnCompletionActions
(
Set
<
HandlerAndRunnable
>
onCompletionActions
)
{
for
(
HandlerAndRunnable
pendingAction
:
onCompletionActions
)
{
pendingAction
.
dispatch
();
}
}
pendingOnCompletionActions
.
removeAll
(
onCompletionActions
);
}
}
private
void
addMediaSourcesInternal
(
private
void
addMediaSourcesInternal
(
...
@@ -733,7 +825,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -733,7 +825,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
}
}
}
}
mediaSourceHolder
.
isPrepared
=
true
;
mediaSourceHolder
.
isPrepared
=
true
;
schedule
ListenerNotification
(
/* actionOnCompletion= */
null
);
schedule
TimelineUpdate
(
);
}
}
private
void
removeMediaSourceInternal
(
int
index
)
{
private
void
removeMediaSourceInternal
(
int
index
)
{
...
@@ -846,12 +938,12 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -846,12 +938,12 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
public
final
int
index
;
public
final
int
index
;
public
final
T
customData
;
public
final
T
customData
;
public
final
@Nullable
Runnable
actionOnComple
tion
;
@Nullable
public
final
HandlerAndRunnable
onCompletionAc
tion
;
public
MessageData
(
int
index
,
T
customData
,
@Nullable
Runnable
actionOnComple
tion
)
{
public
MessageData
(
int
index
,
T
customData
,
@Nullable
HandlerAndRunnable
onCompletionAc
tion
)
{
this
.
index
=
index
;
this
.
index
=
index
;
this
.
actionOnCompletion
=
actionOnCompletion
;
this
.
customData
=
customData
;
this
.
customData
=
customData
;
this
.
onCompletionAction
=
onCompletionAction
;
}
}
}
}
...
@@ -1104,5 +1196,20 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
...
@@ -1104,5 +1196,20 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
// Do nothing.
// Do nothing.
}
}
}
}
private
static
final
class
HandlerAndRunnable
{
private
final
Handler
handler
;
private
final
Runnable
runnable
;
public
HandlerAndRunnable
(
Handler
handler
,
Runnable
runnable
)
{
this
.
handler
=
handler
;
this
.
runnable
=
runnable
;
}
public
void
dispatch
()
{
handler
.
post
(
runnable
);
}
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/source/ExtractorMediaPeriod.java
View file @
de39925c
...
@@ -346,10 +346,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
...
@@ -346,10 +346,9 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
}
else
if
(
isPendingReset
())
{
}
else
if
(
isPendingReset
())
{
return
pendingResetPositionUs
;
return
pendingResetPositionUs
;
}
}
long
largestQueuedTimestampUs
=
C
.
TIME_UNSET
;
long
largestQueuedTimestampUs
=
Long
.
MAX_VALUE
;
if
(
haveAudioVideoTracks
)
{
if
(
haveAudioVideoTracks
)
{
// Ignore non-AV tracks, which may be sparse or poorly interleaved.
// Ignore non-AV tracks, which may be sparse or poorly interleaved.
largestQueuedTimestampUs
=
Long
.
MAX_VALUE
;
int
trackCount
=
sampleQueues
.
length
;
int
trackCount
=
sampleQueues
.
length
;
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
if
(
trackIsAudioVideoFlags
[
i
]
&&
!
sampleQueues
[
i
].
isLastSampleQueued
())
{
if
(
trackIsAudioVideoFlags
[
i
]
&&
!
sampleQueues
[
i
].
isLastSampleQueued
())
{
...
@@ -358,7 +357,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
...
@@ -358,7 +357,7 @@ import org.checkerframework.checker.nullness.compatqual.NullableType;
}
}
}
}
}
}
if
(
largestQueuedTimestampUs
==
C
.
TIME_UNSET
)
{
if
(
largestQueuedTimestampUs
==
Long
.
MAX_VALUE
)
{
largestQueuedTimestampUs
=
getLargestQueuedTimestampUs
();
largestQueuedTimestampUs
=
getLargestQueuedTimestampUs
();
}
}
return
largestQueuedTimestampUs
==
Long
.
MIN_VALUE
?
lastSeekPositionUs
return
largestQueuedTimestampUs
==
Long
.
MIN_VALUE
?
lastSeekPositionUs
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/LoopingMediaSource.java
View file @
de39925c
...
@@ -31,7 +31,7 @@ import java.util.Map;
...
@@ -31,7 +31,7 @@ import java.util.Map;
* Loops a {@link MediaSource} a specified number of times.
* Loops a {@link MediaSource} a specified number of times.
*
*
* <p>Note: To loop a {@link MediaSource} indefinitely, it is usually better to use {@link
* <p>Note: To loop a {@link MediaSource} indefinitely, it is usually better to use {@link
* ExoPlayer#setRepeatMode(int)}.
* ExoPlayer#setRepeatMode(int)}
instead of this class
.
*/
*/
public
final
class
LoopingMediaSource
extends
CompositeMediaSource
<
Void
>
{
public
final
class
LoopingMediaSource
extends
CompositeMediaSource
<
Void
>
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
View file @
de39925c
...
@@ -1319,8 +1319,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1319,8 +1319,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
}
synchronized
(
MediaCodecVideoRenderer
.
class
)
{
synchronized
(
MediaCodecVideoRenderer
.
class
)
{
if
(!
evaluatedDeviceNeedsSetOutputSurfaceWorkaround
)
{
if
(!
evaluatedDeviceNeedsSetOutputSurfaceWorkaround
)
{
if
(
Util
.
SDK_INT
<=
27
&&
"dangal"
.
equals
(
Util
.
DEVICE
))
{
if
(
Util
.
SDK_INT
<=
27
&&
(
"dangal"
.
equals
(
Util
.
DEVICE
)
||
"HWEML"
.
equals
(
Util
.
DEVICE
)))
{
// Dangal is affected on API level 27: https://github.com/google/ExoPlayer/issues/5169.
// A small number of devices are affected on API level 27:
// https://github.com/google/ExoPlayer/issues/5169.
deviceNeedsSetOutputSurfaceWorkaround
=
true
;
deviceNeedsSetOutputSurfaceWorkaround
=
true
;
}
else
if
(
Util
.
SDK_INT
>=
27
)
{
}
else
if
(
Util
.
SDK_INT
>=
27
)
{
// In general, devices running API level 27 or later should be unaffected. Do nothing.
// In general, devices running API level 27 or later should be unaffected. Do nothing.
...
...
library/core/src/test/java/com/google/android/exoplayer2/source/ConcatenatingMediaSourceTest.java
View file @
de39925c
...
@@ -17,13 +17,16 @@ package com.google.android.exoplayer2.source;
...
@@ -17,13 +17,16 @@ package com.google.android.exoplayer2.source;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
org
.
junit
.
Assert
.
fail
;
import
static
org
.
junit
.
Assert
.
fail
;
import
static
org
.
mockito
.
Mockito
.
mock
;
import
static
org
.
mockito
.
Mockito
.
verify
;
import
static
org
.
mockito
.
Mockito
.
verify
;
import
android.os.ConditionVariable
;
import
android.os.ConditionVariable
;
import
android.os.Handler
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.Timeline
;
import
com.google.android.exoplayer2.Timeline
;
import
com.google.android.exoplayer2.source.MediaSource.MediaPeriodId
;
import
com.google.android.exoplayer2.source.MediaSource.MediaPeriodId
;
import
com.google.android.exoplayer2.source.MediaSource.SourceInfoRefreshListener
;
import
com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder
;
import
com.google.android.exoplayer2.source.ShuffleOrder.DefaultShuffleOrder
;
import
com.google.android.exoplayer2.testutil.DummyMainThread
;
import
com.google.android.exoplayer2.testutil.DummyMainThread
;
import
com.google.android.exoplayer2.testutil.FakeMediaSource
;
import
com.google.android.exoplayer2.testutil.FakeMediaSource
;
...
@@ -41,7 +44,6 @@ import org.junit.After;
...
@@ -41,7 +44,6 @@ import org.junit.After;
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
;
import
org.mockito.Mockito
;
import
org.robolectric.RobolectricTestRunner
;
import
org.robolectric.RobolectricTestRunner
;
import
org.robolectric.annotation.Config
;
import
org.robolectric.annotation.Config
;
...
@@ -415,57 +417,59 @@ public final class ConcatenatingMediaSourceTest {
...
@@ -415,57 +417,59 @@ public final class ConcatenatingMediaSourceTest {
@Test
@Test
public
void
testCustomCallbackBeforePreparationAddSingle
()
{
public
void
testCustomCallbackBeforePreparationAddSingle
()
{
Runnable
runnable
=
Mockito
.
mock
(
Runnable
.
class
);
Runnable
runnable
=
mock
(
Runnable
.
class
);
mediaSource
.
addMediaSource
(
createFakeMediaSource
(),
runnable
);
mediaSource
.
addMediaSource
(
createFakeMediaSource
(),
new
Handler
(),
runnable
);
verify
(
runnable
).
run
();
verify
(
runnable
).
run
();
}
}
@Test
@Test
public
void
testCustomCallbackBeforePreparationAddMultiple
()
{
public
void
testCustomCallbackBeforePreparationAddMultiple
()
{
Runnable
runnable
=
Mockito
.
mock
(
Runnable
.
class
);
Runnable
runnable
=
mock
(
Runnable
.
class
);
mediaSource
.
addMediaSources
(
mediaSource
.
addMediaSources
(
Arrays
.
asList
(
new
MediaSource
[]
{
createFakeMediaSource
(),
createFakeMediaSource
()}),
Arrays
.
asList
(
new
MediaSource
[]
{
createFakeMediaSource
(),
createFakeMediaSource
()}),
new
Handler
(),
runnable
);
runnable
);
verify
(
runnable
).
run
();
verify
(
runnable
).
run
();
}
}
@Test
@Test
public
void
testCustomCallbackBeforePreparationAddSingleWithIndex
()
{
public
void
testCustomCallbackBeforePreparationAddSingleWithIndex
()
{
Runnable
runnable
=
Mockito
.
mock
(
Runnable
.
class
);
Runnable
runnable
=
mock
(
Runnable
.
class
);
mediaSource
.
addMediaSource
(
/* index */
0
,
createFakeMediaSource
(),
runnable
);
mediaSource
.
addMediaSource
(
/* index */
0
,
createFakeMediaSource
(),
new
Handler
(),
runnable
);
verify
(
runnable
).
run
();
verify
(
runnable
).
run
();
}
}
@Test
@Test
public
void
testCustomCallbackBeforePreparationAddMultipleWithIndex
()
{
public
void
testCustomCallbackBeforePreparationAddMultipleWithIndex
()
{
Runnable
runnable
=
Mockito
.
mock
(
Runnable
.
class
);
Runnable
runnable
=
mock
(
Runnable
.
class
);
mediaSource
.
addMediaSources
(
mediaSource
.
addMediaSources
(
/* index */
0
,
/* index */
0
,
Arrays
.
asList
(
new
MediaSource
[]
{
createFakeMediaSource
(),
createFakeMediaSource
()}),
Arrays
.
asList
(
new
MediaSource
[]
{
createFakeMediaSource
(),
createFakeMediaSource
()}),
new
Handler
(),
runnable
);
runnable
);
verify
(
runnable
).
run
();
verify
(
runnable
).
run
();
}
}
@Test
@Test
public
void
testCustomCallbackBeforePreparationRemove
()
{
public
void
testCustomCallbackBeforePreparationRemove
()
{
Runnable
runnable
=
Mockito
.
mock
(
Runnable
.
class
);
Runnable
runnable
=
mock
(
Runnable
.
class
);
mediaSource
.
addMediaSource
(
createFakeMediaSource
());
mediaSource
.
addMediaSource
(
createFakeMediaSource
());
mediaSource
.
removeMediaSource
(
/* index */
0
,
runnable
);
mediaSource
.
removeMediaSource
(
/* index */
0
,
new
Handler
(),
runnable
);
verify
(
runnable
).
run
();
verify
(
runnable
).
run
();
}
}
@Test
@Test
public
void
testCustomCallbackBeforePreparationMove
()
{
public
void
testCustomCallbackBeforePreparationMove
()
{
Runnable
runnable
=
Mockito
.
mock
(
Runnable
.
class
);
Runnable
runnable
=
mock
(
Runnable
.
class
);
mediaSource
.
addMediaSources
(
mediaSource
.
addMediaSources
(
Arrays
.
asList
(
new
MediaSource
[]
{
createFakeMediaSource
(),
createFakeMediaSource
()}));
Arrays
.
asList
(
new
MediaSource
[]
{
createFakeMediaSource
(),
createFakeMediaSource
()}));
mediaSource
.
moveMediaSource
(
/* fromIndex */
1
,
/* toIndex */
0
,
runnable
);
mediaSource
.
moveMediaSource
(
/* fromIndex */
1
,
/* toIndex */
0
,
new
Handler
(),
runnable
);
verify
(
runnable
).
run
();
verify
(
runnable
).
run
();
}
}
...
@@ -476,7 +480,8 @@ public final class ConcatenatingMediaSourceTest {
...
@@ -476,7 +480,8 @@ public final class ConcatenatingMediaSourceTest {
testRunner
.
prepareSource
();
testRunner
.
prepareSource
();
final
TimelineGrabber
timelineGrabber
=
new
TimelineGrabber
(
testRunner
);
final
TimelineGrabber
timelineGrabber
=
new
TimelineGrabber
(
testRunner
);
dummyMainThread
.
runOnMainThread
(
dummyMainThread
.
runOnMainThread
(
()
->
mediaSource
.
addMediaSource
(
createFakeMediaSource
(),
timelineGrabber
));
()
->
mediaSource
.
addMediaSource
(
createFakeMediaSource
(),
new
Handler
(),
timelineGrabber
));
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
assertThat
(
timeline
.
getWindowCount
()).
isEqualTo
(
1
);
assertThat
(
timeline
.
getWindowCount
()).
isEqualTo
(
1
);
}
finally
{
}
finally
{
...
@@ -495,6 +500,7 @@ public final class ConcatenatingMediaSourceTest {
...
@@ -495,6 +500,7 @@ public final class ConcatenatingMediaSourceTest {
mediaSource
.
addMediaSources
(
mediaSource
.
addMediaSources
(
Arrays
.
asList
(
Arrays
.
asList
(
new
MediaSource
[]
{
createFakeMediaSource
(),
createFakeMediaSource
()}),
new
MediaSource
[]
{
createFakeMediaSource
(),
createFakeMediaSource
()}),
new
Handler
(),
timelineGrabber
));
timelineGrabber
));
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
assertThat
(
timeline
.
getWindowCount
()).
isEqualTo
(
2
);
assertThat
(
timeline
.
getWindowCount
()).
isEqualTo
(
2
);
...
@@ -511,7 +517,8 @@ public final class ConcatenatingMediaSourceTest {
...
@@ -511,7 +517,8 @@ public final class ConcatenatingMediaSourceTest {
final
TimelineGrabber
timelineGrabber
=
new
TimelineGrabber
(
testRunner
);
final
TimelineGrabber
timelineGrabber
=
new
TimelineGrabber
(
testRunner
);
dummyMainThread
.
runOnMainThread
(
dummyMainThread
.
runOnMainThread
(
()
->
()
->
mediaSource
.
addMediaSource
(
/* index */
0
,
createFakeMediaSource
(),
timelineGrabber
));
mediaSource
.
addMediaSource
(
/* index */
0
,
createFakeMediaSource
(),
new
Handler
(),
timelineGrabber
));
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
assertThat
(
timeline
.
getWindowCount
()).
isEqualTo
(
1
);
assertThat
(
timeline
.
getWindowCount
()).
isEqualTo
(
1
);
}
finally
{
}
finally
{
...
@@ -531,6 +538,7 @@ public final class ConcatenatingMediaSourceTest {
...
@@ -531,6 +538,7 @@ public final class ConcatenatingMediaSourceTest {
/* index */
0
,
/* index */
0
,
Arrays
.
asList
(
Arrays
.
asList
(
new
MediaSource
[]
{
createFakeMediaSource
(),
createFakeMediaSource
()}),
new
MediaSource
[]
{
createFakeMediaSource
(),
createFakeMediaSource
()}),
new
Handler
(),
timelineGrabber
));
timelineGrabber
));
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
assertThat
(
timeline
.
getWindowCount
()).
isEqualTo
(
2
);
assertThat
(
timeline
.
getWindowCount
()).
isEqualTo
(
2
);
...
@@ -549,7 +557,7 @@ public final class ConcatenatingMediaSourceTest {
...
@@ -549,7 +557,7 @@ public final class ConcatenatingMediaSourceTest {
final
TimelineGrabber
timelineGrabber
=
new
TimelineGrabber
(
testRunner
);
final
TimelineGrabber
timelineGrabber
=
new
TimelineGrabber
(
testRunner
);
dummyMainThread
.
runOnMainThread
(
dummyMainThread
.
runOnMainThread
(
()
->
mediaSource
.
removeMediaSource
(
/* index */
0
,
timelineGrabber
));
()
->
mediaSource
.
removeMediaSource
(
/* index */
0
,
new
Handler
(),
timelineGrabber
));
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
assertThat
(
timeline
.
getWindowCount
()).
isEqualTo
(
0
);
assertThat
(
timeline
.
getWindowCount
()).
isEqualTo
(
0
);
}
finally
{
}
finally
{
...
@@ -571,7 +579,9 @@ public final class ConcatenatingMediaSourceTest {
...
@@ -571,7 +579,9 @@ public final class ConcatenatingMediaSourceTest {
final
TimelineGrabber
timelineGrabber
=
new
TimelineGrabber
(
testRunner
);
final
TimelineGrabber
timelineGrabber
=
new
TimelineGrabber
(
testRunner
);
dummyMainThread
.
runOnMainThread
(
dummyMainThread
.
runOnMainThread
(
()
->
mediaSource
.
moveMediaSource
(
/* fromIndex */
1
,
/* toIndex */
0
,
timelineGrabber
));
()
->
mediaSource
.
moveMediaSource
(
/* fromIndex */
1
,
/* toIndex */
0
,
new
Handler
(),
timelineGrabber
));
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
assertThat
(
timeline
.
getWindowCount
()).
isEqualTo
(
2
);
assertThat
(
timeline
.
getWindowCount
()).
isEqualTo
(
2
);
}
finally
{
}
finally
{
...
@@ -819,7 +829,7 @@ public final class ConcatenatingMediaSourceTest {
...
@@ -819,7 +829,7 @@ public final class ConcatenatingMediaSourceTest {
testRunner
.
prepareSource
();
testRunner
.
prepareSource
();
final
TimelineGrabber
timelineGrabber
=
new
TimelineGrabber
(
testRunner
);
final
TimelineGrabber
timelineGrabber
=
new
TimelineGrabber
(
testRunner
);
dummyMainThread
.
runOnMainThread
(()
->
mediaSource
.
clear
(
timelineGrabber
));
dummyMainThread
.
runOnMainThread
(()
->
mediaSource
.
clear
(
new
Handler
(),
timelineGrabber
));
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
assertThat
(
timeline
.
isEmpty
()).
isTrue
();
assertThat
(
timeline
.
isEmpty
()).
isTrue
();
...
@@ -964,8 +974,9 @@ public final class ConcatenatingMediaSourceTest {
...
@@ -964,8 +974,9 @@ public final class ConcatenatingMediaSourceTest {
@Test
@Test
public
void
testCustomCallbackBeforePreparationSetShuffleOrder
()
throws
Exception
{
public
void
testCustomCallbackBeforePreparationSetShuffleOrder
()
throws
Exception
{
Runnable
runnable
=
Mockito
.
mock
(
Runnable
.
class
);
Runnable
runnable
=
mock
(
Runnable
.
class
);
mediaSource
.
setShuffleOrder
(
new
ShuffleOrder
.
UnshuffledShuffleOrder
(
/* length= */
0
),
runnable
);
mediaSource
.
setShuffleOrder
(
new
ShuffleOrder
.
UnshuffledShuffleOrder
(
/* length= */
0
),
new
Handler
(),
runnable
);
verify
(
runnable
).
run
();
verify
(
runnable
).
run
();
}
}
...
@@ -981,7 +992,9 @@ public final class ConcatenatingMediaSourceTest {
...
@@ -981,7 +992,9 @@ public final class ConcatenatingMediaSourceTest {
dummyMainThread
.
runOnMainThread
(
dummyMainThread
.
runOnMainThread
(
()
->
()
->
mediaSource
.
setShuffleOrder
(
mediaSource
.
setShuffleOrder
(
new
ShuffleOrder
.
UnshuffledShuffleOrder
(
/* length= */
3
),
timelineGrabber
));
new
ShuffleOrder
.
UnshuffledShuffleOrder
(
/* length= */
3
),
new
Handler
(),
timelineGrabber
));
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
Timeline
timeline
=
timelineGrabber
.
assertTimelineChangeBlocking
();
assertThat
(
timeline
.
getFirstWindowIndex
(
/* shuffleModeEnabled= */
true
)).
isEqualTo
(
0
);
assertThat
(
timeline
.
getFirstWindowIndex
(
/* shuffleModeEnabled= */
true
)).
isEqualTo
(
0
);
}
finally
{
}
finally
{
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java
View file @
de39925c
...
@@ -101,6 +101,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
...
@@ -101,6 +101,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
Pattern
.
compile
(
"AVERAGE-BANDWIDTH=(\\d+)\\b"
);
Pattern
.
compile
(
"AVERAGE-BANDWIDTH=(\\d+)\\b"
);
private
static
final
Pattern
REGEX_AUDIO
=
Pattern
.
compile
(
"AUDIO=\"(.+?)\""
);
private
static
final
Pattern
REGEX_AUDIO
=
Pattern
.
compile
(
"AUDIO=\"(.+?)\""
);
private
static
final
Pattern
REGEX_BANDWIDTH
=
Pattern
.
compile
(
"[^-]BANDWIDTH=(\\d+)\\b"
);
private
static
final
Pattern
REGEX_BANDWIDTH
=
Pattern
.
compile
(
"[^-]BANDWIDTH=(\\d+)\\b"
);
private
static
final
Pattern
REGEX_CHANNELS
=
Pattern
.
compile
(
"CHANNELS=\"(.+?)\""
);
private
static
final
Pattern
REGEX_CODECS
=
Pattern
.
compile
(
"CODECS=\"(.+?)\""
);
private
static
final
Pattern
REGEX_CODECS
=
Pattern
.
compile
(
"CODECS=\"(.+?)\""
);
private
static
final
Pattern
REGEX_RESOLUTION
=
Pattern
.
compile
(
"RESOLUTION=(\\d+x\\d+)"
);
private
static
final
Pattern
REGEX_RESOLUTION
=
Pattern
.
compile
(
"RESOLUTION=(\\d+x\\d+)"
);
private
static
final
Pattern
REGEX_FRAME_RATE
=
Pattern
.
compile
(
"FRAME-RATE=([\\d\\.]+)\\b"
);
private
static
final
Pattern
REGEX_FRAME_RATE
=
Pattern
.
compile
(
"FRAME-RATE=([\\d\\.]+)\\b"
);
...
@@ -346,6 +347,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
...
@@ -346,6 +347,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
switch
(
parseStringAttr
(
line
,
REGEX_TYPE
,
variableDefinitions
))
{
switch
(
parseStringAttr
(
line
,
REGEX_TYPE
,
variableDefinitions
))
{
case
TYPE_AUDIO:
case
TYPE_AUDIO:
String
codecs
=
audioGroupIdToCodecs
.
get
(
groupId
);
String
codecs
=
audioGroupIdToCodecs
.
get
(
groupId
);
int
channelCount
=
parseChannelsAttribute
(
line
,
variableDefinitions
);
String
sampleMimeType
=
codecs
!=
null
?
MimeTypes
.
getMediaMimeType
(
codecs
)
:
null
;
String
sampleMimeType
=
codecs
!=
null
?
MimeTypes
.
getMediaMimeType
(
codecs
)
:
null
;
format
=
format
=
Format
.
createAudioContainerFormat
(
Format
.
createAudioContainerFormat
(
...
@@ -355,7 +357,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
...
@@ -355,7 +357,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
sampleMimeType
,
sampleMimeType
,
codecs
,
codecs
,
/* bitrate= */
Format
.
NO_VALUE
,
/* bitrate= */
Format
.
NO_VALUE
,
/* channelCount= */
Format
.
NO_VALUE
,
channelCount
,
/* sampleRate= */
Format
.
NO_VALUE
,
/* sampleRate= */
Format
.
NO_VALUE
,
/* initializationData= */
null
,
/* initializationData= */
null
,
selectionFlags
,
selectionFlags
,
...
@@ -426,21 +428,6 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
...
@@ -426,21 +428,6 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
variableDefinitions
);
variableDefinitions
);
}
}
@C
.
SelectionFlags
private
static
int
parseSelectionFlags
(
String
line
)
{
int
flags
=
0
;
if
(
parseOptionalBooleanAttribute
(
line
,
REGEX_DEFAULT
,
false
))
{
flags
|=
C
.
SELECTION_FLAG_DEFAULT
;
}
if
(
parseOptionalBooleanAttribute
(
line
,
REGEX_FORCED
,
false
))
{
flags
|=
C
.
SELECTION_FLAG_FORCED
;
}
if
(
parseOptionalBooleanAttribute
(
line
,
REGEX_AUTOSELECT
,
false
))
{
flags
|=
C
.
SELECTION_FLAG_AUTOSELECT
;
}
return
flags
;
}
private
static
HlsMediaPlaylist
parseMediaPlaylist
(
private
static
HlsMediaPlaylist
parseMediaPlaylist
(
HlsMasterPlaylist
masterPlaylist
,
LineIterator
iterator
,
String
baseUri
)
throws
IOException
{
HlsMasterPlaylist
masterPlaylist
,
LineIterator
iterator
,
String
baseUri
)
throws
IOException
{
@HlsMediaPlaylist
.
PlaylistType
int
playlistType
=
HlsMediaPlaylist
.
PLAYLIST_TYPE_UNKNOWN
;
@HlsMediaPlaylist
.
PlaylistType
int
playlistType
=
HlsMediaPlaylist
.
PLAYLIST_TYPE_UNKNOWN
;
...
@@ -661,6 +648,28 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
...
@@ -661,6 +648,28 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
segments
);
segments
);
}
}
@C
.
SelectionFlags
private
static
int
parseSelectionFlags
(
String
line
)
{
int
flags
=
0
;
if
(
parseOptionalBooleanAttribute
(
line
,
REGEX_DEFAULT
,
false
))
{
flags
|=
C
.
SELECTION_FLAG_DEFAULT
;
}
if
(
parseOptionalBooleanAttribute
(
line
,
REGEX_FORCED
,
false
))
{
flags
|=
C
.
SELECTION_FLAG_FORCED
;
}
if
(
parseOptionalBooleanAttribute
(
line
,
REGEX_AUTOSELECT
,
false
))
{
flags
|=
C
.
SELECTION_FLAG_AUTOSELECT
;
}
return
flags
;
}
private
static
int
parseChannelsAttribute
(
String
line
,
Map
<
String
,
String
>
variableDefinitions
)
{
String
channelsString
=
parseOptionalStringAttr
(
line
,
REGEX_CHANNELS
,
variableDefinitions
);
return
channelsString
!=
null
?
Integer
.
parseInt
(
Util
.
splitAtFirst
(
channelsString
,
"/"
)[
0
])
:
Format
.
NO_VALUE
;
}
private
static
@Nullable
SchemeData
parsePlayReadySchemeData
(
private
static
@Nullable
SchemeData
parsePlayReadySchemeData
(
String
line
,
Map
<
String
,
String
>
variableDefinitions
)
throws
ParserException
{
String
line
,
Map
<
String
,
String
>
variableDefinitions
)
throws
ParserException
{
String
keyFormatVersions
=
String
keyFormatVersions
=
...
...
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java
View file @
de39925c
...
@@ -81,6 +81,18 @@ public class HlsMasterPlaylistParserTest {
...
@@ -81,6 +81,18 @@ public class HlsMasterPlaylistParserTest {
+
"CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+
"CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+
"http://example.com/low.m3u8\n"
;
+
"http://example.com/low.m3u8\n"
;
private
static
final
String
PLAYLIST_WITH_CHANNELS_ATTRIBUTE
=
" #EXTM3U \n"
+
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio\",CHANNELS=\"6\",NAME=\"Eng6\","
+
"URI=\"something.m3u8\"\n"
+
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio\",CHANNELS=\"2/6\",NAME=\"Eng26\","
+
"URI=\"something2.m3u8\"\n"
+
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"audio\",NAME=\"Eng\","
+
"URI=\"something3.m3u8\"\n"
+
"#EXT-X-STREAM-INF:BANDWIDTH=1280000,"
+
"CODECS=\"mp4a.40.2,avc1.66.30\",AUDIO=\"audio\",RESOLUTION=304x128\n"
+
"http://example.com/low.m3u8\n"
;
private
static
final
String
PLAYLIST_WITHOUT_CC
=
private
static
final
String
PLAYLIST_WITHOUT_CC
=
" #EXTM3U \n"
" #EXTM3U \n"
+
"#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,"
+
"#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,"
...
@@ -217,6 +229,17 @@ public class HlsMasterPlaylistParserTest {
...
@@ -217,6 +229,17 @@ public class HlsMasterPlaylistParserTest {
}
}
@Test
@Test
public
void
testPlaylistWithChannelsAttribute
()
throws
IOException
{
HlsMasterPlaylist
playlist
=
parseMasterPlaylist
(
PLAYLIST_URI
,
PLAYLIST_WITH_CHANNELS_ATTRIBUTE
);
List
<
HlsMasterPlaylist
.
HlsUrl
>
audios
=
playlist
.
audios
;
assertThat
(
audios
).
hasSize
(
3
);
assertThat
(
audios
.
get
(
0
).
format
.
channelCount
).
isEqualTo
(
6
);
assertThat
(
audios
.
get
(
1
).
format
.
channelCount
).
isEqualTo
(
2
);
assertThat
(
audios
.
get
(
2
).
format
.
channelCount
).
isEqualTo
(
Format
.
NO_VALUE
);
}
@Test
public
void
testPlaylistWithoutClosedCaptions
()
throws
IOException
{
public
void
testPlaylistWithoutClosedCaptions
()
throws
IOException
{
HlsMasterPlaylist
playlist
=
parseMasterPlaylist
(
PLAYLIST_URI
,
PLAYLIST_WITHOUT_CC
);
HlsMasterPlaylist
playlist
=
parseMasterPlaylist
(
PLAYLIST_URI
,
PLAYLIST_WITHOUT_CC
);
assertThat
(
playlist
.
muxedCaptionFormats
).
isEmpty
();
assertThat
(
playlist
.
muxedCaptionFormats
).
isEmpty
();
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
View file @
de39925c
...
@@ -758,10 +758,6 @@ public class PlayerView extends FrameLayout {
...
@@ -758,10 +758,6 @@ public class PlayerView extends FrameLayout {
@Override
@Override
public
boolean
dispatchKeyEvent
(
KeyEvent
event
)
{
public
boolean
dispatchKeyEvent
(
KeyEvent
event
)
{
if
(
player
!=
null
&&
player
.
isPlayingAd
())
{
if
(
player
!=
null
&&
player
.
isPlayingAd
())
{
// Focus any overlay UI now, in case it's provided by a WebView whose contents may update
// dynamically. This is needed to make the "Skip ad" button focused on Android TV when using
// IMA [Internal: b/62371030].
overlayFrameLayout
.
requestFocus
();
return
super
.
dispatchKeyEvent
(
event
);
return
super
.
dispatchKeyEvent
(
event
);
}
}
boolean
isDpadWhenControlHidden
=
boolean
isDpadWhenControlHidden
=
...
@@ -1035,6 +1031,12 @@ public class PlayerView extends FrameLayout {
...
@@ -1035,6 +1031,12 @@ public class PlayerView extends FrameLayout {
if
(
ev
.
getActionMasked
()
!=
MotionEvent
.
ACTION_DOWN
)
{
if
(
ev
.
getActionMasked
()
!=
MotionEvent
.
ACTION_DOWN
)
{
return
false
;
return
false
;
}
}
return
performClick
();
}
@Override
public
boolean
performClick
()
{
super
.
performClick
();
return
toggleControllerVisibility
();
return
toggleControllerVisibility
();
}
}
...
...
testutils/src/main/java/com/google/android/exoplayer2/testutil/DebugRenderersFactory.java
View file @
de39925c
...
@@ -20,6 +20,7 @@ import android.content.Context;
...
@@ -20,6 +20,7 @@ import android.content.Context;
import
android.media.MediaCodec
;
import
android.media.MediaCodec
;
import
android.media.MediaCrypto
;
import
android.media.MediaCrypto
;
import
android.os.Handler
;
import
android.os.Handler
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.DefaultRenderersFactory
;
import
com.google.android.exoplayer2.DefaultRenderersFactory
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.Format
;
...
@@ -37,23 +38,38 @@ import java.util.ArrayList;
...
@@ -37,23 +38,38 @@ import java.util.ArrayList;
/**
/**
* A debug extension of {@link DefaultRenderersFactory}. Provides a video renderer that performs
* A debug extension of {@link DefaultRenderersFactory}. Provides a video renderer that performs
* video buffer timestamp assertions.
* video buffer timestamp assertions, and modifies the default value for {@link
* #setAllowedVideoJoiningTimeMs(long)} to be {@code 0}.
*/
*/
@TargetApi
(
16
)
@TargetApi
(
16
)
public
class
DebugRenderersFactory
extends
DefaultRenderersFactory
{
public
class
DebugRenderersFactory
extends
DefaultRenderersFactory
{
public
DebugRenderersFactory
(
Context
context
)
{
public
DebugRenderersFactory
(
Context
context
)
{
super
(
context
,
DefaultRenderersFactory
.
EXTENSION_RENDERER_MODE_OFF
,
0
);
super
(
context
);
setAllowedVideoJoiningTimeMs
(
0
);
}
}
@Override
@Override
protected
void
buildVideoRenderers
(
Context
context
,
protected
void
buildVideoRenderers
(
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
long
allowedVideoJoiningTimeMs
,
Context
context
,
Handler
eventHandler
,
VideoRendererEventListener
eventListener
,
@ExtensionRendererMode
int
extensionRendererMode
,
@ExtensionRendererMode
int
extensionRendererMode
,
ArrayList
<
Renderer
>
out
)
{
MediaCodecSelector
mediaCodecSelector
,
out
.
add
(
new
DebugMediaCodecVideoRenderer
(
context
,
MediaCodecSelector
.
DEFAULT
,
@Nullable
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
allowedVideoJoiningTimeMs
,
drmSessionManager
,
eventHandler
,
eventListener
,
boolean
playClearSamplesWithoutKeys
,
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY
));
Handler
eventHandler
,
VideoRendererEventListener
eventListener
,
long
allowedVideoJoiningTimeMs
,
ArrayList
<
Renderer
>
out
)
{
out
.
add
(
new
DebugMediaCodecVideoRenderer
(
context
,
mediaCodecSelector
,
allowedVideoJoiningTimeMs
,
drmSessionManager
,
playClearSamplesWithoutKeys
,
eventHandler
,
eventListener
,
MAX_DROPPED_VIDEO_FRAME_COUNT_TO_NOTIFY
));
}
}
/**
/**
...
@@ -72,12 +88,24 @@ public class DebugRenderersFactory extends DefaultRenderersFactory {
...
@@ -72,12 +88,24 @@ public class DebugRenderersFactory extends DefaultRenderersFactory {
private
int
minimumInsertIndex
;
private
int
minimumInsertIndex
;
private
boolean
skipToPositionBeforeRenderingFirstFrame
;
private
boolean
skipToPositionBeforeRenderingFirstFrame
;
public
DebugMediaCodecVideoRenderer
(
Context
context
,
MediaCodecSelector
mediaCodecSelector
,
public
DebugMediaCodecVideoRenderer
(
long
allowedJoiningTimeMs
,
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
Context
context
,
Handler
eventHandler
,
VideoRendererEventListener
eventListener
,
MediaCodecSelector
mediaCodecSelector
,
long
allowedJoiningTimeMs
,
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
boolean
playClearSamplesWithoutKeys
,
Handler
eventHandler
,
VideoRendererEventListener
eventListener
,
int
maxDroppedFrameCountToNotify
)
{
int
maxDroppedFrameCountToNotify
)
{
super
(
context
,
mediaCodecSelector
,
allowedJoiningTimeMs
,
drmSessionManager
,
false
,
super
(
eventHandler
,
eventListener
,
maxDroppedFrameCountToNotify
);
context
,
mediaCodecSelector
,
allowedJoiningTimeMs
,
drmSessionManager
,
playClearSamplesWithoutKeys
,
eventHandler
,
eventListener
,
maxDroppedFrameCountToNotify
);
}
}
@Override
@Override
...
...
testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/RobolectricUtil.java
View file @
de39925c
...
@@ -37,6 +37,7 @@ import org.robolectric.shadows.ShadowMessageQueue;
...
@@ -37,6 +37,7 @@ import org.robolectric.shadows.ShadowMessageQueue;
public
final
class
RobolectricUtil
{
public
final
class
RobolectricUtil
{
private
static
final
AtomicLong
sequenceNumberGenerator
=
new
AtomicLong
(
0
);
private
static
final
AtomicLong
sequenceNumberGenerator
=
new
AtomicLong
(
0
);
private
static
final
int
ANY_MESSAGE
=
Integer
.
MIN_VALUE
;
private
RobolectricUtil
()
{}
private
RobolectricUtil
()
{}
...
@@ -110,7 +111,8 @@ public final class RobolectricUtil {
...
@@ -110,7 +111,8 @@ public final class RobolectricUtil {
boolean
isRemoved
=
false
;
boolean
isRemoved
=
false
;
for
(
RemovedMessage
removedMessage
:
removedMessages
)
{
for
(
RemovedMessage
removedMessage
:
removedMessages
)
{
if
(
removedMessage
.
handler
==
target
if
(
removedMessage
.
handler
==
target
&&
removedMessage
.
what
==
pendingMessage
.
message
.
what
&&
(
removedMessage
.
what
==
ANY_MESSAGE
||
removedMessage
.
what
==
pendingMessage
.
message
.
what
)
&&
(
removedMessage
.
object
==
null
&&
(
removedMessage
.
object
==
null
||
removedMessage
.
object
==
pendingMessage
.
message
.
obj
)
||
removedMessage
.
object
==
pendingMessage
.
message
.
obj
)
&&
pendingMessage
.
sequenceNumber
<
removedMessage
.
sequenceNumber
)
{
&&
pendingMessage
.
sequenceNumber
<
removedMessage
.
sequenceNumber
)
{
...
@@ -179,6 +181,15 @@ public final class RobolectricUtil {
...
@@ -179,6 +181,15 @@ public final class RobolectricUtil {
((
CustomLooper
)
shadowOf
(
looper
)).
removeMessages
(
handler
,
what
,
object
);
((
CustomLooper
)
shadowOf
(
looper
)).
removeMessages
(
handler
,
what
,
object
);
}
}
}
}
@Implementation
public
void
removeCallbacksAndMessages
(
Handler
handler
,
Object
object
)
{
Looper
looper
=
ShadowLooper
.
getLooperForThread
(
looperThread
);
if
(
shadowOf
(
looper
)
instanceof
CustomLooper
&&
shadowOf
(
looper
)
!=
ShadowLooper
.
getShadowMainLooper
())
{
((
CustomLooper
)
shadowOf
(
looper
)).
removeMessages
(
handler
,
ANY_MESSAGE
,
object
);
}
}
}
}
private
static
final
class
PendingMessage
implements
Comparable
<
PendingMessage
>
{
private
static
final
class
PendingMessage
implements
Comparable
<
PendingMessage
>
{
...
...
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