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
9f814850
authored
May 23, 2017
by
ojw28
Committed by
GitHub
May 23, 2017
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #2860 from google/dev-2.4.1-rc
r2.4.1
parents
d9794696
fe504594
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
51 changed files
with
787 additions
and
201 deletions
RELEASENOTES.md
build.gradle
demo/src/main/AndroidManifest.xml
demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java
extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java
extensions/vp9/src/main/jni/generate_libvpx_android_configs.sh
library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.0.dump
library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.1.dump
library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.2.dump
library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.3.dump
library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.unklen.dump
library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java
library/core/src/androidTest/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java
library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
library/core/src/main/java/com/google/android/exoplayer2/audio/Sonic.java
library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleOutputBuffer.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OggPacket.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/StreamReader.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisReader.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java
library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlNode.java
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRegion.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/Loader.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceFactory.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java
library/core/src/main/java/com/google/android/exoplayer2/video/DummySurface.java
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java
library/ui/src/main/res/values/attrs.xml
RELEASENOTES.md
View file @
9f814850
# Release notes #
### r2.4.1 ###
*
Stability: Avoid OutOfMemoryError in extractors when parsing malformed media
(
[
#2780
](
https://github.com/google/ExoPlayer/issues/2780
)
).
*
Stability: Avoid native crash on Galaxy Nexus. Avoid unnecessarily large codec
input buffer allocations on all devices
(
[
#2607
](
https://github.com/google/ExoPlayer/issues/2607
)
).
*
Variable speed playback: Fix interpolation for rate/pitch adjustment
(
[
#2774
](
https://github.com/google/ExoPlayer/issues/2774
)
).
*
HLS: Include EXT-X-DATERANGE tags in HlsMediaPlaylist.
*
HLS: Don't expose CEA-608 track if CLOSED-CAPTIONS=NONE
(
[
#2743
](
https://github.com/google/ExoPlayer/issues/2743
)
).
*
HLS: Correctly propagate errors loading the media playlist
(
[
#2623
](
https://github.com/google/ExoPlayer/issues/2623
)
).
*
UI: DefaultTimeBar enhancements and bug fixes
(
[
#2740
](
https://github.com/google/ExoPlayer/issues/2740
)
).
*
Ogg: Fix failure to play some Ogg files
(
[
#2782
](
https://github.com/google/ExoPlayer/issues/2782
)
).
*
Captions: Don't select text tack with no language by default.
*
Captions: TTML positioning fixes
(
[
#2824
](
https://github.com/google/ExoPlayer/issues/2824
)
).
*
Misc bugfixes.
### r2.4.0 ###
*
New modular library structure. You can read more about depending on individual
...
...
build.gradle
View file @
9f814850
...
...
@@ -16,7 +16,7 @@ buildscript {
jcenter
()
}
dependencies
{
classpath
'com.android.tools.build:gradle:2.3.
0
'
classpath
'com.android.tools.build:gradle:2.3.
1
'
classpath
'com.novoda:bintray-release:0.4.0'
}
// Workaround for the following test coverage issue. Remove when fixed:
...
...
@@ -48,7 +48,7 @@ allprojects {
releaseRepoName
=
getBintrayRepo
()
releaseUserOrg
=
'google'
releaseGroupId
=
'com.google.android.exoplayer'
releaseVersion
=
'r2.4.
0
'
releaseVersion
=
'r2.4.
1
'
releaseWebsite
=
'https://github.com/google/ExoPlayer'
}
if
(
it
.
hasProperty
(
'externalBuildDir'
))
{
...
...
demo/src/main/AndroidManifest.xml
View file @
9f814850
...
...
@@ -16,8 +16,8 @@
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
package=
"com.google.android.exoplayer2.demo"
android:versionCode=
"240
0
"
android:versionName=
"2.4.
0
"
>
android:versionCode=
"240
1
"
android:versionName=
"2.4.
1
"
>
<uses-permission
android:name=
"android.permission.INTERNET"
/>
<uses-permission
android:name=
"android.permission.READ_EXTERNAL_STORAGE"
/>
...
...
demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java
View file @
9f814850
...
...
@@ -234,7 +234,6 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
Intent
intent
=
getIntent
();
boolean
needNewPlayer
=
player
==
null
;
if
(
needNewPlayer
)
{
boolean
preferExtensionDecoders
=
intent
.
getBooleanExtra
(
PREFER_EXTENSION_DECODERS
,
false
);
UUID
drmSchemeUuid
=
intent
.
hasExtra
(
DRM_SCHEME_UUID_EXTRA
)
?
UUID
.
fromString
(
intent
.
getStringExtra
(
DRM_SCHEME_UUID_EXTRA
))
:
null
;
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
=
null
;
...
...
@@ -253,6 +252,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
}
}
boolean
preferExtensionDecoders
=
intent
.
getBooleanExtra
(
PREFER_EXTENSION_DECODERS
,
false
);
@DefaultRenderersFactory
.
ExtensionRendererMode
int
extensionRendererMode
=
((
DemoApplication
)
getApplication
()).
useExtensionRenderers
()
?
(
preferExtensionDecoders
?
DefaultRenderersFactory
.
EXTENSION_RENDERER_MODE_PREFER
...
...
@@ -261,10 +261,10 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
DefaultRenderersFactory
renderersFactory
=
new
DefaultRenderersFactory
(
this
,
drmSessionManager
,
extensionRendererMode
);
TrackSelection
.
Factory
video
TrackSelectionFactory
=
TrackSelection
.
Factory
adaptive
TrackSelectionFactory
=
new
AdaptiveTrackSelection
.
Factory
(
BANDWIDTH_METER
);
trackSelector
=
new
DefaultTrackSelector
(
video
TrackSelectionFactory
);
trackSelectionHelper
=
new
TrackSelectionHelper
(
trackSelector
,
video
TrackSelectionFactory
);
trackSelector
=
new
DefaultTrackSelector
(
adaptive
TrackSelectionFactory
);
trackSelectionHelper
=
new
TrackSelectionHelper
(
trackSelector
,
adaptive
TrackSelectionFactory
);
lastSeenTrackGroupArray
=
null
;
player
=
ExoPlayerFactory
.
newSimpleInstance
(
renderersFactory
,
trackSelector
);
...
...
extensions/vp9/src/main/java/com/google/android/exoplayer2/ext/vp9/LibvpxVideoRenderer.java
View file @
9f814850
...
...
@@ -286,7 +286,7 @@ public final class LibvpxVideoRenderer extends BaseRenderer {
*
* @param outputBufferTimeUs The timestamp of the current output buffer.
* @param nextOutputBufferTimeUs The timestamp of the next output buffer or
* {@link TIME_UNSET} if the next output buffer is unavailable.
* {@link
C#
TIME_UNSET} if the next output buffer is unavailable.
* @param positionUs The current playback position.
* @param joiningDeadlineMs The joining deadline.
* @return Returns whether to drop the current output buffer.
...
...
extensions/vp9/src/main/jni/generate_libvpx_android_configs.sh
View file @
9f814850
...
...
@@ -51,8 +51,7 @@ config[3]+=" --disable-sse3 --disable-ssse3 --disable-sse4_1 --disable-avx"
config[3]+
=
" --disable-avx2 --enable-pic"
arch[4]
=
"arm64-v8a"
config[4]
=
"--force-target=armv8-android-gcc --sdk-path=
$ndk
--disable-neon"
config[4]+
=
" --disable-neon-asm"
config[4]
=
"--force-target=armv8-android-gcc --sdk-path=
$ndk
--enable-neon"
arch[5]
=
"x86_64"
config[5]
=
"--force-target=x86_64-android-gcc --sdk-path=
$ndk
--disable-sse2"
...
...
library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.0.dump
View file @
9f814850
...
...
@@ -9,7 +9,7 @@ track 0:
id = null
containerMimeType = null
sampleMimeType = audio/vorbis
maxInputSize =
65025
maxInputSize =
-1
width = -1
height = -1
frameRate = -1.0
...
...
library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.1.dump
View file @
9f814850
...
...
@@ -9,7 +9,7 @@ track 0:
id = null
containerMimeType = null
sampleMimeType = audio/vorbis
maxInputSize =
65025
maxInputSize =
-1
width = -1
height = -1
frameRate = -1.0
...
...
library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.2.dump
View file @
9f814850
...
...
@@ -9,7 +9,7 @@ track 0:
id = null
containerMimeType = null
sampleMimeType = audio/vorbis
maxInputSize =
65025
maxInputSize =
-1
width = -1
height = -1
frameRate = -1.0
...
...
library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.3.dump
View file @
9f814850
...
...
@@ -9,7 +9,7 @@ track 0:
id = null
containerMimeType = null
sampleMimeType = audio/vorbis
maxInputSize =
65025
maxInputSize =
-1
width = -1
height = -1
frameRate = -1.0
...
...
library/core/src/androidTest/assets/ogg/bear_vorbis.ogg.unklen.dump
View file @
9f814850
...
...
@@ -9,7 +9,7 @@ track 0:
id = null
containerMimeType = null
sampleMimeType = audio/vorbis
maxInputSize =
65025
maxInputSize =
-1
width = -1
height = -1
frameRate = -1.0
...
...
library/core/src/androidTest/java/com/google/android/exoplayer2/extractor/ts/TsExtractorTest.java
View file @
9f814850
...
...
@@ -75,7 +75,7 @@ public final class TsExtractorTest extends InstrumentationTestCase {
public
void
testCustomPesReader
()
throws
Exception
{
CustomTsPayloadReaderFactory
factory
=
new
CustomTsPayloadReaderFactory
(
true
,
false
);
TsExtractor
tsExtractor
=
new
TsExtractor
(
TsExtractor
.
MODE_
NORMAL
,
new
TimestampAdjuster
(
0
),
TsExtractor
tsExtractor
=
new
TsExtractor
(
TsExtractor
.
MODE_
MULTI_PMT
,
new
TimestampAdjuster
(
0
),
factory
);
FakeExtractorInput
input
=
new
FakeExtractorInput
.
Builder
()
.
setData
(
TestUtil
.
getByteArray
(
getInstrumentation
(),
"ts/sample.ts"
))
...
...
@@ -100,7 +100,7 @@ public final class TsExtractorTest extends InstrumentationTestCase {
public
void
testCustomInitialSectionReader
()
throws
Exception
{
CustomTsPayloadReaderFactory
factory
=
new
CustomTsPayloadReaderFactory
(
false
,
true
);
TsExtractor
tsExtractor
=
new
TsExtractor
(
TsExtractor
.
MODE_
NORMAL
,
new
TimestampAdjuster
(
0
),
TsExtractor
tsExtractor
=
new
TsExtractor
(
TsExtractor
.
MODE_
MULTI_PMT
,
new
TimestampAdjuster
(
0
),
factory
);
FakeExtractorInput
input
=
new
FakeExtractorInput
.
Builder
()
.
setData
(
TestUtil
.
getByteArray
(
getInstrumentation
(),
"ts/sample_with_sdt.ts"
))
...
...
library/core/src/androidTest/java/com/google/android/exoplayer2/text/ttml/TtmlDecoderTest.java
View file @
9f814850
...
...
@@ -157,39 +157,39 @@ public final class TtmlDecoderTest extends InstrumentationTestCase {
assertEquals
(
2
,
output
.
size
());
Cue
ttmlCue
=
output
.
get
(
0
);
assertEquals
(
"lorem"
,
ttmlCue
.
text
.
toString
());
assertEquals
(
10
.
f
/
100
.
f
,
ttmlCue
.
position
);
assertEquals
(
10
.
f
/
100
.
f
,
ttmlCue
.
line
);
assertEquals
(
20
.
f
/
100
.
f
,
ttmlCue
.
size
);
assertEquals
(
10
f
/
100
f
,
ttmlCue
.
position
);
assertEquals
(
10
f
/
100
f
,
ttmlCue
.
line
);
assertEquals
(
20
f
/
100
f
,
ttmlCue
.
size
);
ttmlCue
=
output
.
get
(
1
);
assertEquals
(
"amet"
,
ttmlCue
.
text
.
toString
());
assertEquals
(
60
.
f
/
100
.
f
,
ttmlCue
.
position
);
assertEquals
(
10
.
f
/
100
.
f
,
ttmlCue
.
line
);
assertEquals
(
20
.
f
/
100
.
f
,
ttmlCue
.
size
);
assertEquals
(
60
f
/
100
f
,
ttmlCue
.
position
);
assertEquals
(
10
f
/
100
f
,
ttmlCue
.
line
);
assertEquals
(
20
f
/
100
f
,
ttmlCue
.
size
);
output
=
subtitle
.
getCues
(
5000000
);
assertEquals
(
1
,
output
.
size
());
ttmlCue
=
output
.
get
(
0
);
assertEquals
(
"ipsum"
,
ttmlCue
.
text
.
toString
());
assertEquals
(
40
.
f
/
100
.
f
,
ttmlCue
.
position
);
assertEquals
(
40
.
f
/
100
.
f
,
ttmlCue
.
line
);
assertEquals
(
20
.
f
/
100
.
f
,
ttmlCue
.
size
);
assertEquals
(
40
f
/
100
f
,
ttmlCue
.
position
);
assertEquals
(
40
f
/
100
f
,
ttmlCue
.
line
);
assertEquals
(
20
f
/
100
f
,
ttmlCue
.
size
);
output
=
subtitle
.
getCues
(
9000000
);
assertEquals
(
1
,
output
.
size
());
ttmlCue
=
output
.
get
(
0
);
assertEquals
(
"dolor"
,
ttmlCue
.
text
.
toString
());
assertEquals
(
10
.
f
/
100
.
f
,
ttmlCue
.
position
);
assertEquals
(
80
.
f
/
100
.
f
,
ttmlCue
.
line
);
assertEquals
(
Cue
.
DIMEN_UNSET
,
ttmlCue
.
size
);
assertEquals
(
10
f
/
100
f
,
ttmlCue
.
position
);
assertEquals
(
80
f
/
100
f
,
ttmlCue
.
line
);
assertEquals
(
1
f
,
ttmlCue
.
size
);
output
=
subtitle
.
getCues
(
21000000
);
assertEquals
(
1
,
output
.
size
());
ttmlCue
=
output
.
get
(
0
);
assertEquals
(
"She first said this"
,
ttmlCue
.
text
.
toString
());
assertEquals
(
45
.
f
/
100
.
f
,
ttmlCue
.
position
);
assertEquals
(
45
.
f
/
100
.
f
,
ttmlCue
.
line
);
assertEquals
(
35
.
f
/
100
.
f
,
ttmlCue
.
size
);
assertEquals
(
45
f
/
100
f
,
ttmlCue
.
position
);
assertEquals
(
45
f
/
100
f
,
ttmlCue
.
line
);
assertEquals
(
35
f
/
100
f
,
ttmlCue
.
size
);
output
=
subtitle
.
getCues
(
25000000
);
ttmlCue
=
output
.
get
(
0
);
assertEquals
(
"She first said this\nThen this"
,
ttmlCue
.
text
.
toString
());
...
...
@@ -197,8 +197,8 @@ public final class TtmlDecoderTest extends InstrumentationTestCase {
assertEquals
(
1
,
output
.
size
());
ttmlCue
=
output
.
get
(
0
);
assertEquals
(
"She first said this\nThen this\nFinally this"
,
ttmlCue
.
text
.
toString
());
assertEquals
(
45
.
f
/
100
.
f
,
ttmlCue
.
position
);
assertEquals
(
45
.
f
/
100
.
f
,
ttmlCue
.
line
);
assertEquals
(
45
f
/
100
f
,
ttmlCue
.
position
);
assertEquals
(
45
f
/
100
f
,
ttmlCue
.
line
);
}
public
void
testEmptyStyleAttribute
()
throws
IOException
,
SubtitleDecoderException
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/BaseRenderer.java
View file @
9f814850
...
...
@@ -142,9 +142,9 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
public
final
void
disable
()
{
Assertions
.
checkState
(
state
==
STATE_ENABLED
);
state
=
STATE_DISABLED
;
onDisabled
();
stream
=
null
;
streamIsFinal
=
false
;
onDisabled
();
}
// RendererCapabilities implementation.
...
...
@@ -300,8 +300,6 @@ public abstract class BaseRenderer implements Renderer, RendererCapabilities {
/**
* Returns whether the upstream source is ready.
*
* @return Whether the source is ready.
*/
protected
final
boolean
isSourceReady
()
{
return
readEndOfStream
?
streamIsFinal
:
stream
.
isReady
();
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayer.java
View file @
9f814850
...
...
@@ -15,6 +15,8 @@
*/
package
com
.
google
.
android
.
exoplayer2
;
import
android.os.Looper
;
import
android.support.annotation.IntDef
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.audio.MediaCodecAudioRenderer
;
import
com.google.android.exoplayer2.metadata.MetadataRenderer
;
...
...
@@ -88,7 +90,9 @@ import com.google.android.exoplayer2.video.MediaCodecVideoRenderer;
* thread. The application's main thread is ideal. Accessing an instance from multiple threads is
* discouraged, however if an application does wish to do this then it may do so provided that it
* ensures accesses are synchronized.</li>
* <li>Registered listeners are called on the thread that created the ExoPlayer instance.</li>
* <li>Registered listeners are called on the thread that created the ExoPlayer instance, unless
* the thread that created the ExoPlayer instance does not have a {@link Looper}. In that case,
* registered listeners will be called on the application's main thread.</li>
* <li>An internal playback thread is responsible for playback. Injected player components such as
* Renderers, MediaSources, TrackSelectors and LoadControls are called by the player on this
* thread.</li>
...
...
@@ -113,8 +117,8 @@ public interface ExoPlayer {
* Called when the timeline and/or manifest has been refreshed.
* <p>
* Note that if the timeline has changed then a position discontinuity may also have occurred.
* For example the current period index may have changed as a result of periods being added or
* removed from the timeline. Th
e
will <em>not</em> be reported via a separate call to
* For example
,
the current period index may have changed as a result of periods being added or
* removed from the timeline. Th
is
will <em>not</em> be reported via a separate call to
* {@link #onPositionDiscontinuity()}.
*
* @param timeline The latest timeline. Never null, but may be empty.
...
...
@@ -253,7 +257,8 @@ public interface ExoPlayer {
/**
* Register a listener to receive events from the player. The listener's methods will be called on
* the thread that was used to construct the player.
* the thread that was used to construct the player. However, if the thread used to construct the
* player does not have a {@link Looper}, then the listener will be called on the main thread.
*
* @param listener The listener to register.
*/
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java
View file @
9f814850
...
...
@@ -16,7 +16,6 @@
package
com
.
google
.
android
.
exoplayer2
;
import
android.content.Context
;
import
android.os.Looper
;
import
com.google.android.exoplayer2.drm.DrmSessionManager
;
import
com.google.android.exoplayer2.drm.FrameworkMediaCrypto
;
import
com.google.android.exoplayer2.trackselection.TrackSelector
;
...
...
@@ -29,8 +28,7 @@ public final class ExoPlayerFactory {
private
ExoPlayerFactory
()
{}
/**
* Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated
* {@link Looper}.
* Creates a {@link SimpleExoPlayer} instance.
*
* @param context A {@link Context}.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
...
...
@@ -45,8 +43,7 @@ public final class ExoPlayerFactory {
}
/**
* Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated
* {@link Looper}. Available extension renderers are not used.
* Creates a {@link SimpleExoPlayer} instance. Available extension renderers are not used.
*
* @param context A {@link Context}.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
...
...
@@ -63,8 +60,7 @@ public final class ExoPlayerFactory {
}
/**
* Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated
* {@link Looper}.
* Creates a {@link SimpleExoPlayer} instance.
*
* @param context A {@link Context}.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
...
...
@@ -86,8 +82,7 @@ public final class ExoPlayerFactory {
}
/**
* Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated
* {@link Looper}.
* Creates a {@link SimpleExoPlayer} instance.
*
* @param context A {@link Context}.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
...
...
@@ -112,8 +107,7 @@ public final class ExoPlayerFactory {
}
/**
* Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated
* {@link Looper}.
* Creates a {@link SimpleExoPlayer} instance.
*
* @param context A {@link Context}.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
...
...
@@ -123,8 +117,7 @@ public final class ExoPlayerFactory {
}
/**
* Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated
* {@link Looper}.
* Creates a {@link SimpleExoPlayer} instance.
*
* @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
...
...
@@ -135,8 +128,7 @@ public final class ExoPlayerFactory {
}
/**
* Creates a {@link SimpleExoPlayer} instance. Must be called from a thread that has an associated
* {@link Looper}.
* Creates a {@link SimpleExoPlayer} instance.
*
* @param renderersFactory A factory for creating {@link Renderer}s to be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
...
...
@@ -148,8 +140,7 @@ public final class ExoPlayerFactory {
}
/**
* Creates an {@link ExoPlayer} instance. Must be called from a thread that has an associated
* {@link Looper}.
* Creates an {@link ExoPlayer} instance.
*
* @param renderers The {@link Renderer}s that will be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
...
...
@@ -159,8 +150,7 @@ public final class ExoPlayerFactory {
}
/**
* Creates an {@link ExoPlayer} instance. Must be called from a thread that has an associated
* {@link Looper}.
* Creates an {@link ExoPlayer} instance.
*
* @param renderers The {@link Renderer}s that will be used by the instance.
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
View file @
9f814850
...
...
@@ -92,7 +92,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
trackGroups
=
TrackGroupArray
.
EMPTY
;
trackSelections
=
emptyTrackSelections
;
playbackParameters
=
PlaybackParameters
.
DEFAULT
;
eventHandler
=
new
Handler
()
{
Looper
eventLooper
=
Looper
.
myLooper
()
!=
null
?
Looper
.
myLooper
()
:
Looper
.
getMainLooper
();
eventHandler
=
new
Handler
(
eventLooper
)
{
@Override
public
void
handleMessage
(
Message
msg
)
{
ExoPlayerImpl
.
this
.
handleEvent
(
msg
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
View file @
9f814850
...
...
@@ -24,13 +24,13 @@ public interface ExoPlayerLibraryInfo {
* The version of the library expressed as a string, for example "1.2.3".
*/
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
String
VERSION
=
"2.4.
0
"
;
String
VERSION
=
"2.4.
1
"
;
/**
* The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}.
*/
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
String
VERSION_SLASHY
=
"ExoPlayerLib/2.4.
0
"
;
String
VERSION_SLASHY
=
"ExoPlayerLib/2.4.
1
"
;
/**
* The version of the library expressed as an integer, for example 1002003.
...
...
@@ -40,7 +40,7 @@ public interface ExoPlayerLibraryInfo {
* integer version 123045006 (123-045-006).
*/
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
int
VERSION_INT
=
200400
0
;
int
VERSION_INT
=
200400
1
;
/**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
...
...
library/core/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
View file @
9f814850
...
...
@@ -20,6 +20,7 @@ import android.graphics.SurfaceTexture;
import
android.media.MediaCodec
;
import
android.media.PlaybackParams
;
import
android.os.Handler
;
import
android.os.Looper
;
import
android.support.annotation.Nullable
;
import
android.util.Log
;
import
android.view.Surface
;
...
...
@@ -111,8 +112,10 @@ public class SimpleExoPlayer implements ExoPlayer {
protected
SimpleExoPlayer
(
RenderersFactory
renderersFactory
,
TrackSelector
trackSelector
,
LoadControl
loadControl
)
{
componentListener
=
new
ComponentListener
();
renderers
=
renderersFactory
.
createRenderers
(
new
Handler
(),
componentListener
,
componentListener
,
componentListener
,
componentListener
);
Looper
eventLooper
=
Looper
.
myLooper
()
!=
null
?
Looper
.
myLooper
()
:
Looper
.
getMainLooper
();
Handler
eventHandler
=
new
Handler
(
eventLooper
);
renderers
=
renderersFactory
.
createRenderers
(
eventHandler
,
componentListener
,
componentListener
,
componentListener
,
componentListener
);
// Obtain counts of video and audio renderers.
int
videoRendererCount
=
0
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/Sonic.java
View file @
9f814850
...
...
@@ -374,8 +374,8 @@ import java.util.Arrays;
}
private
short
interpolate
(
short
[]
in
,
int
inPos
,
int
oldSampleRate
,
int
newSampleRate
)
{
short
left
=
in
[
inPos
*
numChannels
];
short
right
=
in
[
inPos
*
numChannels
+
numChannels
];
short
left
=
in
[
inPos
];
short
right
=
in
[
inPos
+
numChannels
];
int
position
=
newRatePosition
*
oldSampleRate
;
int
leftPosition
=
oldRatePosition
*
newSampleRate
;
int
rightPosition
=
(
oldRatePosition
+
1
)
*
newSampleRate
;
...
...
@@ -402,7 +402,7 @@ import java.util.Arrays;
enlargeOutputBufferIfNeeded
(
1
);
for
(
int
i
=
0
;
i
<
numChannels
;
i
++)
{
outputBuffer
[
numOutputSamples
*
numChannels
+
i
]
=
interpolate
(
pitchBuffer
,
position
+
i
,
oldSampleRate
,
newSampleRate
);
interpolate
(
pitchBuffer
,
position
*
numChannels
+
i
,
oldSampleRate
,
newSampleRate
);
}
newRatePosition
++;
numOutputSamples
++;
...
...
library/core/src/main/java/com/google/android/exoplayer2/decoder/SimpleOutputBuffer.java
View file @
9f814850
...
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
decoder
;
import
java.nio.ByteBuffer
;
import
java.nio.ByteOrder
;
/**
* Buffer for {@link SimpleDecoder} output.
...
...
@@ -40,7 +41,7 @@ public class SimpleOutputBuffer extends OutputBuffer {
public
ByteBuffer
init
(
long
timeUs
,
int
size
)
{
this
.
timeUs
=
timeUs
;
if
(
data
==
null
||
data
.
capacity
()
<
size
)
{
data
=
ByteBuffer
.
allocateDirect
(
size
);
data
=
ByteBuffer
.
allocateDirect
(
size
)
.
order
(
ByteOrder
.
nativeOrder
())
;
}
data
.
position
(
0
);
data
.
limit
(
size
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/DefaultExtractorsFactory.java
View file @
9f814850
...
...
@@ -26,7 +26,9 @@ import com.google.android.exoplayer2.extractor.ts.AdtsExtractor;
import
com.google.android.exoplayer2.extractor.ts.DefaultTsPayloadReaderFactory
;
import
com.google.android.exoplayer2.extractor.ts.PsExtractor
;
import
com.google.android.exoplayer2.extractor.ts.TsExtractor
;
import
com.google.android.exoplayer2.extractor.ts.TsPayloadReader
;
import
com.google.android.exoplayer2.extractor.wav.WavExtractor
;
import
com.google.android.exoplayer2.util.TimestampAdjuster
;
import
java.lang.reflect.Constructor
;
/**
...
...
@@ -67,8 +69,13 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
private
@MatroskaExtractor
.
Flags
int
matroskaFlags
;
private
@FragmentedMp4Extractor
.
Flags
int
fragmentedMp4Flags
;
private
@Mp3Extractor
.
Flags
int
mp3Flags
;
private
@TsExtractor
.
Mode
int
tsMode
;
private
@DefaultTsPayloadReaderFactory
.
Flags
int
tsFlags
;
public
DefaultExtractorsFactory
()
{
tsMode
=
TsExtractor
.
MODE_SINGLE_PMT
;
}
/**
* Sets flags for {@link MatroskaExtractor} instances created by the factory.
*
...
...
@@ -108,6 +115,18 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
}
/**
* Sets the mode for {@link TsExtractor} instances created by the factory.
*
* @see TsExtractor#TsExtractor(int, TimestampAdjuster, TsPayloadReader.Factory).
* @param mode The mode to use.
* @return The factory, for convenience.
*/
public
synchronized
DefaultExtractorsFactory
setTsExtractorMode
(
@TsExtractor
.
Mode
int
mode
)
{
tsMode
=
mode
;
return
this
;
}
/**
* Sets flags for {@link DefaultTsPayloadReaderFactory}s used by {@link TsExtractor} instances
* created by the factory.
*
...
...
@@ -130,7 +149,7 @@ public final class DefaultExtractorsFactory implements ExtractorsFactory {
extractors
[
3
]
=
new
Mp3Extractor
(
mp3Flags
);
extractors
[
4
]
=
new
AdtsExtractor
();
extractors
[
5
]
=
new
Ac3Extractor
();
extractors
[
6
]
=
new
TsExtractor
(
tsFlags
);
extractors
[
6
]
=
new
TsExtractor
(
ts
Mode
,
ts
Flags
);
extractors
[
7
]
=
new
FlvExtractor
();
extractors
[
8
]
=
new
OggExtractor
();
extractors
[
9
]
=
new
PsExtractor
();
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/OggPacket.java
View file @
9f814850
...
...
@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.extractor.ExtractorInput;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
java.io.IOException
;
import
java.util.Arrays
;
/**
* OGG packet class.
...
...
@@ -27,8 +28,8 @@ import java.io.IOException;
/* package */
final
class
OggPacket
{
private
final
OggPageHeader
pageHeader
=
new
OggPageHeader
();
private
final
ParsableByteArray
packetArray
=
new
ParsableByteArray
(
new
byte
[
OggPageHeader
.
MAX_PAGE_PAYLOAD
],
0
);
private
final
ParsableByteArray
packetArray
=
new
ParsableByteArray
(
new
byte
[
OggPageHeader
.
MAX_PAGE_PAYLOAD
],
0
);
private
int
currentSegmentIndex
=
C
.
INDEX_UNSET
;
private
int
segmentCount
;
...
...
@@ -85,6 +86,9 @@ import java.io.IOException;
int
size
=
calculatePacketSize
(
currentSegmentIndex
);
int
segmentIndex
=
currentSegmentIndex
+
segmentCount
;
if
(
size
>
0
)
{
if
(
packetArray
.
capacity
()
<
packetArray
.
limit
()
+
size
)
{
packetArray
.
data
=
Arrays
.
copyOf
(
packetArray
.
data
,
packetArray
.
limit
()
+
size
);
}
input
.
readFully
(
packetArray
.
data
,
packetArray
.
limit
(),
size
);
packetArray
.
setLimit
(
packetArray
.
limit
()
+
size
);
populated
=
pageHeader
.
laces
[
segmentIndex
-
1
]
!=
255
;
...
...
@@ -119,6 +123,17 @@ import java.io.IOException;
}
/**
* Trims the packet data array.
*/
public
void
trimPayload
()
{
if
(
packetArray
.
data
.
length
==
OggPageHeader
.
MAX_PAGE_PAYLOAD
)
{
return
;
}
packetArray
.
data
=
Arrays
.
copyOf
(
packetArray
.
data
,
Math
.
max
(
OggPageHeader
.
MAX_PAGE_PAYLOAD
,
packetArray
.
limit
()));
}
/**
* Calculates the size of the packet starting from {@code startSegmentIndex}.
*
* @param startSegmentIndex the index of the first segment of the packet.
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/StreamReader.java
View file @
9f814850
...
...
@@ -103,15 +103,12 @@ import java.io.IOException;
switch
(
state
)
{
case
STATE_READ_HEADERS:
return
readHeaders
(
input
);
case
STATE_SKIP_HEADERS:
input
.
skipFully
((
int
)
payloadStartPosition
);
state
=
STATE_READ_PAYLOAD
;
return
Extractor
.
RESULT_CONTINUE
;
case
STATE_READ_PAYLOAD:
return
readPayload
(
input
,
seekPosition
);
default
:
// Never happens.
throw
new
IllegalStateException
();
...
...
@@ -152,6 +149,8 @@ import java.io.IOException;
setupData
=
null
;
state
=
STATE_READ_PAYLOAD
;
// First payload packet. Trim the payload array of the ogg packet after headers have been read.
oggPacket
.
trimPayload
();
return
Extractor
.
RESULT_CONTINUE
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/ogg/VorbisReader.java
View file @
9f814850
...
...
@@ -101,7 +101,7 @@ import java.util.ArrayList;
codecInitialisationData
.
add
(
vorbisSetup
.
setupHeaderData
);
setupData
.
format
=
Format
.
createAudioSampleFormat
(
null
,
MimeTypes
.
AUDIO_VORBIS
,
null
,
this
.
vorbisSetup
.
idHeader
.
bitrateNominal
,
OggPageHeader
.
MAX_PAGE_PAYLOAD
,
this
.
vorbisSetup
.
idHeader
.
bitrateNominal
,
Format
.
NO_VALUE
,
this
.
vorbisSetup
.
idHeader
.
channels
,
(
int
)
this
.
vorbisSetup
.
idHeader
.
sampleRate
,
codecInitialisationData
,
null
,
0
,
null
);
return
true
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsExtractor.java
View file @
9f814850
...
...
@@ -65,13 +65,13 @@ public final class TsExtractor implements Extractor {
* Modes for the extractor.
*/
@Retention
(
RetentionPolicy
.
SOURCE
)
@IntDef
({
MODE_
NORMAL
,
MODE_SINGLE_PMT
,
MODE_HLS
})
@IntDef
({
MODE_
MULTI_PMT
,
MODE_SINGLE_PMT
,
MODE_HLS
})
public
@interface
Mode
{}
/**
* Behave as defined in ISO/IEC 13818-1.
*/
public
static
final
int
MODE_
NORMAL
=
0
;
public
static
final
int
MODE_
MULTI_PMT
=
0
;
/**
* Assume only one PMT will be contained in the stream, even if more are declared by the PAT.
*/
...
...
@@ -132,12 +132,23 @@ public final class TsExtractor implements Extractor {
* {@code FLAG_*} values that control the behavior of the payload readers.
*/
public
TsExtractor
(
@Flags
int
defaultTsPayloadReaderFlags
)
{
this
(
MODE_NORMAL
,
new
TimestampAdjuster
(
0
),
this
(
MODE_SINGLE_PMT
,
defaultTsPayloadReaderFlags
);
}
/**
* @param mode Mode for the extractor. One of {@link #MODE_MULTI_PMT}, {@link #MODE_SINGLE_PMT}
* and {@link #MODE_HLS}.
* @param defaultTsPayloadReaderFlags A combination of {@link DefaultTsPayloadReaderFactory}
* {@code FLAG_*} values that control the behavior of the payload readers.
*/
public
TsExtractor
(
@Mode
int
mode
,
@Flags
int
defaultTsPayloadReaderFlags
)
{
this
(
mode
,
new
TimestampAdjuster
(
0
),
new
DefaultTsPayloadReaderFactory
(
defaultTsPayloadReaderFlags
));
}
/**
* @param mode Mode for the extractor. One of {@link #MODE_
NORMAL
}, {@link #MODE_SINGLE_PMT}
* @param mode Mode for the extractor. One of {@link #MODE_
MULTI_PMT
}, {@link #MODE_SINGLE_PMT}
* and {@link #MODE_HLS}.
* @param timestampAdjuster A timestamp adjuster for offsetting and scaling sample timestamps.
* @param payloadReaderFactory Factory for injecting a custom set of payload readers.
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
View file @
9f814850
...
...
@@ -488,7 +488,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
}
if
(
format
==
null
)
{
// We don't have a format yet, so try and read one.
b
uffer
.
clear
();
flagsOnlyB
uffer
.
clear
();
int
result
=
readSource
(
formatHolder
,
flagsOnlyBuffer
,
true
);
if
(
result
==
C
.
RESULT_FORMAT_READ
)
{
onInputFormatChanged
(
formatHolder
.
format
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecUtil.java
View file @
9f814850
...
...
@@ -429,6 +429,7 @@ public final class MediaCodecUtil {
case
CodecProfileLevel
.
AVCLevel42
:
return
8704
*
16
*
16
;
case
CodecProfileLevel
.
AVCLevel5
:
return
22080
*
16
*
16
;
case
CodecProfileLevel
.
AVCLevel51
:
return
36864
*
16
*
16
;
case
CodecProfileLevel
.
AVCLevel52
:
return
36864
*
16
*
16
;
default
:
return
-
1
;
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java
View file @
9f814850
...
...
@@ -153,7 +153,6 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
protected
void
onDisabled
()
{
flushPendingMetadata
();
decoder
=
null
;
super
.
onDisabled
();
}
@Override
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/TextRenderer.java
View file @
9f814850
...
...
@@ -254,7 +254,6 @@ public final class TextRenderer extends BaseRenderer implements Callback {
streamFormat
=
null
;
clearOutput
();
releaseDecoder
();
super
.
onDisabled
();
}
@Override
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlDecoder.java
View file @
9f814850
...
...
@@ -17,7 +17,6 @@ package com.google.android.exoplayer2.text.ttml;
import
android.text.Layout
;
import
android.util.Log
;
import
android.util.Pair
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.text.Cue
;
import
com.google.android.exoplayer2.text.SimpleSubtitleDecoder
;
...
...
@@ -100,7 +99,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
XmlPullParser
xmlParser
=
xmlParserFactory
.
newPullParser
();
Map
<
String
,
TtmlStyle
>
globalStyles
=
new
HashMap
<>();
Map
<
String
,
TtmlRegion
>
regionMap
=
new
HashMap
<>();
regionMap
.
put
(
TtmlNode
.
ANONYMOUS_REGION_ID
,
new
TtmlRegion
());
regionMap
.
put
(
TtmlNode
.
ANONYMOUS_REGION_ID
,
new
TtmlRegion
(
null
));
ByteArrayInputStream
inputStream
=
new
ByteArrayInputStream
(
bytes
,
0
,
length
);
xmlParser
.
setInput
(
inputStream
,
null
);
TtmlSubtitle
ttmlSubtitle
=
null
;
...
...
@@ -211,9 +210,9 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
globalStyles
.
put
(
style
.
getId
(),
style
);
}
}
else
if
(
XmlPullParserUtil
.
isStartTag
(
xmlParser
,
TtmlNode
.
TAG_REGION
))
{
Pair
<
String
,
TtmlRegion
>
ttmlRegionInfo
=
parseRegionAttributes
(
xmlParser
);
if
(
ttmlRegion
Info
!=
null
)
{
globalRegions
.
put
(
ttmlRegion
Info
.
first
,
ttmlRegionInfo
.
second
);
TtmlRegion
ttmlRegion
=
parseRegionAttributes
(
xmlParser
);
if
(
ttmlRegion
!=
null
)
{
globalRegions
.
put
(
ttmlRegion
.
id
,
ttmlRegion
);
}
}
}
while
(!
XmlPullParserUtil
.
isEndTag
(
xmlParser
,
TtmlNode
.
TAG_HEAD
));
...
...
@@ -221,41 +220,84 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
}
/**
* Parses a region declaration. Supports origin and extent definition but only when defined in
* terms of percentage of the viewport. Regions that do not correctly declare origin are ignored.
* Parses a region declaration.
* <p>
* If the region defines an origin and/or extent, it is required that they're defined as
* percentages of the viewport. Region declarations that define origin and/or extent in other
* formats are unsupported, and null is returned.
*/
private
Pair
<
String
,
TtmlRegion
>
parseRegionAttributes
(
XmlPullParser
xmlParser
)
{
private
TtmlRegion
parseRegionAttributes
(
XmlPullParser
xmlParser
)
{
String
regionId
=
XmlPullParserUtil
.
getAttributeValue
(
xmlParser
,
TtmlNode
.
ATTR_ID
);
String
regionOrigin
=
XmlPullParserUtil
.
getAttributeValue
(
xmlParser
,
TtmlNode
.
ATTR_TTS_ORIGIN
);
String
regionExtent
=
XmlPullParserUtil
.
getAttributeValue
(
xmlParser
,
TtmlNode
.
ATTR_TTS_EXTENT
);
if
(
regionOrigin
==
null
||
regionId
==
null
)
{
if
(
regionId
==
null
)
{
return
null
;
}
float
position
=
Cue
.
DIMEN_UNSET
;
float
line
=
Cue
.
DIMEN_UNSET
;
Matcher
originMatcher
=
PERCENTAGE_COORDINATES
.
matcher
(
regionOrigin
);
if
(
originMatcher
.
matches
())
{
try
{
position
=
Float
.
parseFloat
(
originMatcher
.
group
(
1
))
/
100
.
f
;
line
=
Float
.
parseFloat
(
originMatcher
.
group
(
2
))
/
100
.
f
;
}
catch
(
NumberFormatException
e
)
{
Log
.
w
(
TAG
,
"Ignoring region with malformed origin: '"
+
regionOrigin
+
"'"
,
e
);
position
=
Cue
.
DIMEN_UNSET
;
float
position
;
float
line
;
String
regionOrigin
=
XmlPullParserUtil
.
getAttributeValue
(
xmlParser
,
TtmlNode
.
ATTR_TTS_ORIGIN
);
if
(
regionOrigin
!=
null
)
{
Matcher
originMatcher
=
PERCENTAGE_COORDINATES
.
matcher
(
regionOrigin
);
if
(
originMatcher
.
matches
())
{
try
{
position
=
Float
.
parseFloat
(
originMatcher
.
group
(
1
))
/
100
f
;
line
=
Float
.
parseFloat
(
originMatcher
.
group
(
2
))
/
100
f
;
}
catch
(
NumberFormatException
e
)
{
Log
.
w
(
TAG
,
"Ignoring region with malformed origin: "
+
regionOrigin
);
return
null
;
}
}
else
{
Log
.
w
(
TAG
,
"Ignoring region with unsupported origin: "
+
regionOrigin
);
return
null
;
}
}
else
{
// Origin is omitted. Default to top left.
position
=
0
;
line
=
0
;
}
float
width
=
Cue
.
DIMEN_UNSET
;
float
width
;
float
height
;
String
regionExtent
=
XmlPullParserUtil
.
getAttributeValue
(
xmlParser
,
TtmlNode
.
ATTR_TTS_EXTENT
);
if
(
regionExtent
!=
null
)
{
Matcher
extentMatcher
=
PERCENTAGE_COORDINATES
.
matcher
(
regionExtent
);
if
(
extentMatcher
.
matches
())
{
try
{
width
=
Float
.
parseFloat
(
extentMatcher
.
group
(
1
))
/
100
.
f
;
width
=
Float
.
parseFloat
(
extentMatcher
.
group
(
1
))
/
100
f
;
height
=
Float
.
parseFloat
(
extentMatcher
.
group
(
2
))
/
100
f
;
}
catch
(
NumberFormatException
e
)
{
Log
.
w
(
TAG
,
"Ignoring malformed region extent: '"
+
regionExtent
+
"'"
,
e
);
Log
.
w
(
TAG
,
"Ignoring region with malformed extent: "
+
regionOrigin
);
return
null
;
}
}
else
{
Log
.
w
(
TAG
,
"Ignoring region with unsupported extent: "
+
regionOrigin
);
return
null
;
}
}
else
{
// Extent is omitted. Default to extent of parent.
width
=
1
;
height
=
1
;
}
@Cue
.
AnchorType
int
lineAnchor
=
Cue
.
ANCHOR_TYPE_START
;
String
displayAlign
=
XmlPullParserUtil
.
getAttributeValue
(
xmlParser
,
TtmlNode
.
ATTR_TTS_DISPLAY_ALIGN
);
if
(
displayAlign
!=
null
)
{
switch
(
displayAlign
.
toLowerCase
())
{
case
"center"
:
lineAnchor
=
Cue
.
ANCHOR_TYPE_MIDDLE
;
line
+=
height
/
2
;
break
;
case
"after"
:
lineAnchor
=
Cue
.
ANCHOR_TYPE_END
;
line
+=
height
;
break
;
default
:
// Default "before" case. Do nothing.
break
;
}
}
return
position
!=
Cue
.
DIMEN_UNSET
?
new
Pair
<>(
regionId
,
new
TtmlRegion
(
position
,
line
,
Cue
.
LINE_TYPE_FRACTION
,
width
))
:
null
;
return
new
TtmlRegion
(
regionId
,
position
,
line
,
Cue
.
LINE_TYPE_FRACTION
,
lineAnchor
,
width
)
;
}
private
String
[]
parseStyleIds
(
String
parentStyleIds
)
{
...
...
@@ -277,7 +319,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
try
{
style
.
setBackgroundColor
(
ColorParser
.
parseTtmlColor
(
attributeValue
));
}
catch
(
IllegalArgumentException
e
)
{
Log
.
w
(
TAG
,
"
failed parsing background value: '"
+
attributeValue
+
"'"
);
Log
.
w
(
TAG
,
"
Failed parsing background value: "
+
attributeValue
);
}
break
;
case
TtmlNode
.
ATTR_TTS_COLOR
:
...
...
@@ -285,7 +327,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
try
{
style
.
setFontColor
(
ColorParser
.
parseTtmlColor
(
attributeValue
));
}
catch
(
IllegalArgumentException
e
)
{
Log
.
w
(
TAG
,
"
failed parsing color value: '"
+
attributeValue
+
"'"
);
Log
.
w
(
TAG
,
"
Failed parsing color value: "
+
attributeValue
);
}
break
;
case
TtmlNode
.
ATTR_TTS_FONT_FAMILY
:
...
...
@@ -296,7 +338,7 @@ public final class TtmlDecoder extends SimpleSubtitleDecoder {
style
=
createIfNull
(
style
);
parseFontSize
(
attributeValue
,
style
);
}
catch
(
SubtitleDecoderException
e
)
{
Log
.
w
(
TAG
,
"
failed parsing fontSize value: '"
+
attributeValue
+
"'"
);
Log
.
w
(
TAG
,
"
Failed parsing fontSize value: "
+
attributeValue
);
}
break
;
case
TtmlNode
.
ATTR_TTS_FONT_WEIGHT
:
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlNode.java
View file @
9f814850
...
...
@@ -50,14 +50,15 @@ import java.util.TreeSet;
public
static
final
String
ANONYMOUS_REGION_ID
=
""
;
public
static
final
String
ATTR_ID
=
"id"
;
public
static
final
String
ATTR_TTS_
BACKGROUND_COLOR
=
"backgroundColor
"
;
public
static
final
String
ATTR_TTS_
ORIGIN
=
"origin
"
;
public
static
final
String
ATTR_TTS_EXTENT
=
"extent"
;
public
static
final
String
ATTR_TTS_DISPLAY_ALIGN
=
"displayAlign"
;
public
static
final
String
ATTR_TTS_BACKGROUND_COLOR
=
"backgroundColor"
;
public
static
final
String
ATTR_TTS_FONT_STYLE
=
"fontStyle"
;
public
static
final
String
ATTR_TTS_FONT_SIZE
=
"fontSize"
;
public
static
final
String
ATTR_TTS_FONT_FAMILY
=
"fontFamily"
;
public
static
final
String
ATTR_TTS_FONT_WEIGHT
=
"fontWeight"
;
public
static
final
String
ATTR_TTS_COLOR
=
"color"
;
public
static
final
String
ATTR_TTS_ORIGIN
=
"origin"
;
public
static
final
String
ATTR_TTS_TEXT_DECORATION
=
"textDecoration"
;
public
static
final
String
ATTR_TTS_TEXT_ALIGN
=
"textAlign"
;
...
...
@@ -179,7 +180,7 @@ import java.util.TreeSet;
for
(
Entry
<
String
,
SpannableStringBuilder
>
entry
:
regionOutputs
.
entrySet
())
{
TtmlRegion
region
=
regionMap
.
get
(
entry
.
getKey
());
cues
.
add
(
new
Cue
(
cleanUpText
(
entry
.
getValue
()),
null
,
region
.
line
,
region
.
lineType
,
Cue
.
TYPE_UNSET
,
region
.
position
,
Cue
.
TYPE_UNSET
,
region
.
width
));
region
.
lineAnchor
,
region
.
position
,
Cue
.
TYPE_UNSET
,
region
.
width
));
}
return
cues
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/ttml/TtmlRegion.java
View file @
9f814850
...
...
@@ -22,20 +22,24 @@ import com.google.android.exoplayer2.text.Cue;
*/
/* package */
final
class
TtmlRegion
{
public
final
String
id
;
public
final
float
position
;
public
final
float
line
;
@Cue
.
LineType
public
final
int
lineType
;
@Cue
.
LineType
public
final
int
lineType
;
@Cue
.
AnchorType
public
final
int
lineAnchor
;
public
final
float
width
;
public
TtmlRegion
()
{
this
(
Cue
.
DIMEN_UNSET
,
Cue
.
DIMEN
_UNSET
,
Cue
.
TYPE_UNSET
,
Cue
.
DIMEN_UNSET
);
public
TtmlRegion
(
String
id
)
{
this
(
id
,
Cue
.
DIMEN_UNSET
,
Cue
.
DIMEN_UNSET
,
Cue
.
TYPE
_UNSET
,
Cue
.
TYPE_UNSET
,
Cue
.
DIMEN_UNSET
);
}
public
TtmlRegion
(
float
position
,
float
line
,
@Cue
.
LineType
int
lineType
,
float
width
)
{
public
TtmlRegion
(
String
id
,
float
position
,
float
line
,
@Cue
.
LineType
int
lineType
,
@Cue
.
AnchorType
int
lineAnchor
,
float
width
)
{
this
.
id
=
id
;
this
.
position
=
position
;
this
.
line
=
line
;
this
.
lineType
=
lineType
;
this
.
lineAnchor
=
lineAnchor
;
this
.
width
=
width
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java
View file @
9f814850
...
...
@@ -24,6 +24,7 @@ import com.google.android.exoplayer2.Format;
import
com.google.android.exoplayer2.RendererCapabilities
;
import
com.google.android.exoplayer2.source.TrackGroup
;
import
com.google.android.exoplayer2.source.TrackGroupArray
;
import
com.google.android.exoplayer2.upstream.BandwidthMeter
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Util
;
import
java.util.ArrayList
;
...
...
@@ -376,10 +377,21 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private
final
AtomicReference
<
Parameters
>
paramsReference
;
/**
* Constructs an instance that does not support adaptive track
s
.
* Constructs an instance that does not support adaptive track
selection
.
*/
public
DefaultTrackSelector
()
{
this
(
null
);
this
((
TrackSelection
.
Factory
)
null
);
}
/**
* Constructs an instance that supports adaptive track selection. Adaptive track selections use
* the provided {@link BandwidthMeter} to determine which individual track should be used during
* playback.
*
* @param bandwidthMeter The {@link BandwidthMeter}.
*/
public
DefaultTrackSelector
(
BandwidthMeter
bandwidthMeter
)
{
this
(
new
AdaptiveTrackSelection
.
Factory
(
bandwidthMeter
));
}
/**
...
...
@@ -867,7 +879,8 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
protected
static
boolean
formatHasLanguage
(
Format
format
,
String
language
)
{
return
TextUtils
.
equals
(
language
,
Util
.
normalizeLanguageCode
(
format
.
language
));
return
language
!=
null
&&
TextUtils
.
equals
(
language
,
Util
.
normalizeLanguageCode
(
format
.
language
));
}
// Viewport size util methods.
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/Loader.java
View file @
9f814850
...
...
@@ -33,11 +33,11 @@ import java.util.concurrent.ExecutorService;
public
final
class
Loader
implements
LoaderErrorThrower
{
/**
* Thrown when an unexpected exception is encountered during loading.
* Thrown when an unexpected exception
or error
is encountered during loading.
*/
public
static
final
class
UnexpectedLoaderException
extends
IOException
{
public
UnexpectedLoaderException
(
Exception
cause
)
{
public
UnexpectedLoaderException
(
Throwable
cause
)
{
super
(
"Unexpected "
+
cause
.
getClass
().
getSimpleName
()
+
": "
+
cause
.
getMessage
(),
cause
);
}
...
...
@@ -316,6 +316,14 @@ public final class Loader implements LoaderErrorThrower {
if
(!
released
)
{
obtainMessage
(
MSG_IO_EXCEPTION
,
new
UnexpectedLoaderException
(
e
)).
sendToTarget
();
}
}
catch
(
OutOfMemoryError
e
)
{
// This can occur if a stream is malformed in a way that causes an extractor to think it
// needs to allocate a large amount of memory. We don't want the process to die in this
// case, but we do want the playback to fail.
Log
.
e
(
TAG
,
"OutOfMemory error loading stream"
,
e
);
if
(!
released
)
{
obtainMessage
(
MSG_IO_EXCEPTION
,
new
UnexpectedLoaderException
(
e
)).
sendToTarget
();
}
}
catch
(
Error
e
)
{
// We'd hope that the platform would kill the process if an Error is thrown here, but the
// executor may catch the error (b/20616433). Throw it here, but also pass and throw it from
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSource.java
View file @
9f814850
...
...
@@ -54,8 +54,8 @@ public final class CacheDataSource implements DataSource {
FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS
})
public
@interface
Flags
{}
/**
* A flag indicating whether we will block reads if the cache key is locked. If
this flag
is
*
set, then we will read from upstream if the cache key is lock
ed.
* A flag indicating whether we will block reads if the cache key is locked. If
unset then data
is
*
read from upstream if the cache key is locked, regardless of whether the data is cach
ed.
*/
public
static
final
int
FLAG_BLOCK_ON_CACHE
=
1
<<
0
;
...
...
@@ -110,7 +110,23 @@ public final class CacheDataSource implements DataSource {
/**
* Constructs an instance with default {@link DataSource} and {@link DataSink} instances for
* reading and writing the cache and with {@link #DEFAULT_MAX_CACHE_FILE_SIZE}.
* reading and writing the cache.
*
* @param cache The cache.
* @param upstream A {@link DataSource} for reading data not in the cache.
*/
public
CacheDataSource
(
Cache
cache
,
DataSource
upstream
)
{
this
(
cache
,
upstream
,
0
,
DEFAULT_MAX_CACHE_FILE_SIZE
);
}
/**
* Constructs an instance with default {@link DataSource} and {@link DataSink} instances for
* reading and writing the cache.
*
* @param cache The cache.
* @param upstream A {@link DataSource} for reading data not in the cache.
* @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE}, {@link #FLAG_IGNORE_CACHE_ON_ERROR}
* and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS}, or 0.
*/
public
CacheDataSource
(
Cache
cache
,
DataSource
upstream
,
@Flags
int
flags
)
{
this
(
cache
,
upstream
,
flags
,
DEFAULT_MAX_CACHE_FILE_SIZE
);
...
...
@@ -123,8 +139,8 @@ public final class CacheDataSource implements DataSource {
*
* @param cache The cache.
* @param upstream A {@link DataSource} for reading data not in the cache.
* @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE}
and {@link
*
#FLAG_IGNORE_CACHE_ON_ERROR}
or 0.
* @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE}
, {@link #FLAG_IGNORE_CACHE_ON_ERROR}
*
and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS},
or 0.
* @param maxCacheFileSize The maximum size of a cache file, in bytes. If the cached data size
* exceeds this value, then the data will be fragmented into multiple cache files. The
* finer-grained this is the finer-grained the eviction policy can be.
...
...
@@ -145,8 +161,8 @@ public final class CacheDataSource implements DataSource {
* @param cacheReadDataSource A {@link DataSource} for reading data from the cache.
* @param cacheWriteDataSink A {@link DataSink} for writing data to the cache. If null, cache is
* accessed read-only.
* @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE}
and {@link
*
#FLAG_IGNORE_CACHE_ON_ERROR}
or 0.
* @param flags A combination of {@link #FLAG_BLOCK_ON_CACHE}
, {@link #FLAG_IGNORE_CACHE_ON_ERROR}
*
and {@link #FLAG_IGNORE_CACHE_FOR_UNSET_LENGTH_REQUESTS},
or 0.
* @param eventListener An optional {@link EventListener} to receive events.
*/
public
CacheDataSource
(
Cache
cache
,
DataSource
upstream
,
DataSource
cacheReadDataSource
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheDataSourceFactory.java
View file @
9f814850
...
...
@@ -34,17 +34,25 @@ public final class CacheDataSourceFactory implements DataSource.Factory {
private
final
EventListener
eventListener
;
/**
* @see CacheDataSource#CacheDataSource(Cache, DataSource)
*/
public
CacheDataSourceFactory
(
Cache
cache
,
DataSource
.
Factory
upstreamFactory
)
{
this
(
cache
,
upstreamFactory
,
0
);
}
/**
* @see CacheDataSource#CacheDataSource(Cache, DataSource, int)
*/
public
CacheDataSourceFactory
(
Cache
cache
,
DataSource
.
Factory
upstreamFactory
,
int
flags
)
{
public
CacheDataSourceFactory
(
Cache
cache
,
DataSource
.
Factory
upstreamFactory
,
@CacheDataSource
.
Flags
int
flags
)
{
this
(
cache
,
upstreamFactory
,
flags
,
CacheDataSource
.
DEFAULT_MAX_CACHE_FILE_SIZE
);
}
/**
* @see CacheDataSource#CacheDataSource(Cache, DataSource, int, long)
*/
public
CacheDataSourceFactory
(
Cache
cache
,
DataSource
.
Factory
upstreamFactory
,
int
flags
,
long
maxCacheFileSize
)
{
public
CacheDataSourceFactory
(
Cache
cache
,
DataSource
.
Factory
upstreamFactory
,
@CacheDataSource
.
Flags
int
flags
,
long
maxCacheFileSize
)
{
this
(
cache
,
upstreamFactory
,
new
FileDataSourceFactory
(),
new
CacheDataSinkFactory
(
cache
,
maxCacheFileSize
),
flags
,
null
);
}
...
...
@@ -54,8 +62,8 @@ public final class CacheDataSourceFactory implements DataSource.Factory {
* EventListener)
*/
public
CacheDataSourceFactory
(
Cache
cache
,
Factory
upstreamFactory
,
Factory
cacheReadDataSourceFactory
,
DataSink
.
Factory
cacheWriteDataSinkFactory
,
int
flags
,
EventListener
eventListener
)
{
Factory
cacheReadDataSourceFactory
,
DataSink
.
Factory
cacheWriteDataSinkFactory
,
@CacheDataSource
.
Flags
int
flags
,
EventListener
eventListener
)
{
this
.
cache
=
cache
;
this
.
upstreamFactory
=
upstreamFactory
;
this
.
cacheReadDataSourceFactory
=
cacheReadDataSourceFactory
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/CacheUtil.java
View file @
9f814850
...
...
@@ -64,7 +64,7 @@ public final class CacheUtil {
}
/**
* Returns already cached and missing bytes in the {@cache} for the data defined by {@code
* Returns already cached and missing bytes in the {@c
ode c
ache} for the data defined by {@code
* dataSpec}.
*
* @param dataSpec Defines the data to be checked.
...
...
library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java
View file @
9f814850
...
...
@@ -286,7 +286,9 @@ public final class SimpleCache implements Cache {
private
void
removeSpan
(
CacheSpan
span
,
boolean
removeEmptyCachedContent
)
throws
CacheException
{
CachedContent
cachedContent
=
index
.
get
(
span
.
key
);
Assertions
.
checkState
(
cachedContent
.
removeSpan
(
span
));
if
(
cachedContent
==
null
||
!
cachedContent
.
removeSpan
(
span
))
{
return
;
}
totalSpace
-=
span
.
length
;
if
(
removeEmptyCachedContent
&&
cachedContent
.
isEmpty
())
{
index
.
removeEmpty
(
cachedContent
.
key
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/video/DummySurface.java
0 → 100644
View file @
9f814850
This diff is collapsed.
Click to expand it.
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
View file @
9f814850
...
...
@@ -376,7 +376,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
@Override
protected
void
onOutputFormatChanged
(
MediaCodec
codec
,
android
.
media
.
MediaFormat
outputFormat
)
{
protected
void
onOutputFormatChanged
(
MediaCodec
codec
,
MediaFormat
outputFormat
)
{
boolean
hasCrop
=
outputFormat
.
containsKey
(
KEY_CROP_RIGHT
)
&&
outputFormat
.
containsKey
(
KEY_CROP_LEFT
)
&&
outputFormat
.
containsKey
(
KEY_CROP_BOTTOM
)
&&
outputFormat
.
containsKey
(
KEY_CROP_TOP
);
...
...
@@ -408,11 +408,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
@Override
protected
boolean
canReconfigureCodec
(
MediaCodec
codec
,
boolean
codecIsAdaptive
,
Format
oldFormat
,
Format
newFormat
)
{
return
areAdaptationCompatible
(
oldFormat
,
newFormat
)
return
areAdaptationCompatible
(
codecIsAdaptive
,
oldFormat
,
newFormat
)
&&
newFormat
.
width
<=
codecMaxValues
.
width
&&
newFormat
.
height
<=
codecMaxValues
.
height
&&
newFormat
.
maxInputSize
<=
codecMaxValues
.
inputSize
&&
(
codecIsAdaptive
||
(
oldFormat
.
width
==
newFormat
.
width
&&
oldFormat
.
height
==
newFormat
.
height
));
&&
newFormat
.
maxInputSize
<=
codecMaxValues
.
inputSize
;
}
@Override
...
...
@@ -664,7 +662,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
boolean
haveUnknownDimensions
=
false
;
for
(
Format
streamFormat
:
streamFormats
)
{
if
(
areAdaptationCompatible
(
format
,
streamFormat
))
{
if
(
areAdaptationCompatible
(
codecInfo
.
adaptive
,
format
,
streamFormat
))
{
haveUnknownDimensions
|=
(
streamFormat
.
width
==
Format
.
NO_VALUE
||
streamFormat
.
height
==
Format
.
NO_VALUE
);
maxWidth
=
Math
.
max
(
maxWidth
,
streamFormat
.
width
);
...
...
@@ -817,17 +815,19 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
/**
* Returns whether a
n adaptive codec with suitable {@link CodecMaxValues} will support adaptatio
n
*
between
two {@link Format}s.
* Returns whether a
codec with suitable {@link CodecMaxValues} will support adaptation betwee
n
* two {@link Format}s.
*
* @param codecIsAdaptive Whether the codec supports seamless resolution switches.
* @param first The first format.
* @param second The second format.
* @return Whether an adaptive codec with suitable {@link CodecMaxValues} will support adaptation
* between two {@link Format}s.
* @return Whether the codec will support adaptation between the two {@link Format}s.
*/
private
static
boolean
areAdaptationCompatible
(
Format
first
,
Format
second
)
{
private
static
boolean
areAdaptationCompatible
(
boolean
codecIsAdaptive
,
Format
first
,
Format
second
)
{
return
first
.
sampleMimeType
.
equals
(
second
.
sampleMimeType
)
&&
getRotationDegrees
(
first
)
==
getRotationDegrees
(
second
);
&&
getRotationDegrees
(
first
)
==
getRotationDegrees
(
second
)
&&
(
codecIsAdaptive
||
(
first
.
width
==
second
.
width
&&
first
.
height
==
second
.
height
));
}
private
static
float
getPixelWidthHeightRatio
(
Format
format
)
{
...
...
library/hls/src/androidTest/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylistParserTest.java
View file @
9f814850
...
...
@@ -23,6 +23,7 @@ import com.google.android.exoplayer2.util.MimeTypes;
import
java.io.ByteArrayInputStream
;
import
java.io.IOException
;
import
java.nio.charset.Charset
;
import
java.util.Collections
;
import
java.util.List
;
import
junit.framework.TestCase
;
...
...
@@ -56,16 +57,22 @@ public class HlsMasterPlaylistParserTest extends TestCase {
private
static
final
String
MASTER_PLAYLIST_WITH_CC
=
" #EXTM3U \n"
+
"#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
+
"\n"
+
"#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128\n"
+
"http://example.com/low.m3u8\n"
;
private
static
final
String
MASTER_PLAYLIST_WITHOUT_CC
=
" #EXTM3U \n"
+
"#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,LANGUAGE=\"es\",NAME=\"Eng\",INSTREAM-ID=\"SERVICE4\"\n"
+
"#EXT-X-STREAM-INF:BANDWIDTH=1280000,CODECS=\"mp4a.40.2,avc1.66.30\",RESOLUTION=304x128,"
+
"CLOSED-CAPTIONS=NONE\n"
+
"http://example.com/low.m3u8\n"
;
public
void
testParseMasterPlaylist
()
throws
IOException
{
HlsMasterPlaylist
masterPlaylist
=
parseMasterPlaylist
(
PLAYLIST_URI
,
MASTER_PLAYLIST
);
List
<
HlsMasterPlaylist
.
HlsUrl
>
variants
=
masterPlaylist
.
variants
;
assertNotNull
(
variants
);
assertEquals
(
5
,
variants
.
size
());
assertNull
(
masterPlaylist
.
muxedCaptionFormats
);
assertEquals
(
1280000
,
variants
.
get
(
0
).
format
.
bitrate
);
assertNotNull
(
variants
.
get
(
0
).
format
.
codecs
);
...
...
@@ -117,6 +124,11 @@ public class HlsMasterPlaylistParserTest extends TestCase {
assertEquals
(
"es"
,
closedCaptionFormat
.
language
);
}
public
void
testPlaylistWithoutClosedCaptions
()
throws
IOException
{
HlsMasterPlaylist
playlist
=
parseMasterPlaylist
(
PLAYLIST_URI
,
MASTER_PLAYLIST_WITHOUT_CC
);
assertEquals
(
Collections
.
emptyList
(),
playlist
.
muxedCaptionFormats
);
}
private
static
HlsMasterPlaylist
parseMasterPlaylist
(
String
uri
,
String
playlistString
)
throws
IOException
{
Uri
playlistUri
=
Uri
.
parse
(
uri
);
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java
View file @
9f814850
...
...
@@ -92,6 +92,7 @@ import java.util.Locale;
private
boolean
isTimestampMaster
;
private
byte
[]
scratchSpace
;
private
IOException
fatalError
;
private
HlsUrl
expectedPlaylistUrl
;
private
Uri
encryptionKeyUri
;
private
byte
[]
encryptionKey
;
...
...
@@ -111,7 +112,8 @@ import java.util.Locale;
* @param timestampAdjusterProvider A provider of {@link TimestampAdjuster} instances. If
* multiple {@link HlsChunkSource}s are used for a single playback, they should all share the
* same provider.
* @param muxedCaptionFormats List of muxed caption {@link Format}s.
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
* information is available in the master playlist.
*/
public
HlsChunkSource
(
HlsPlaylistTracker
playlistTracker
,
HlsUrl
[]
variants
,
HlsDataSourceFactory
dataSourceFactory
,
TimestampAdjusterProvider
timestampAdjusterProvider
,
...
...
@@ -142,6 +144,9 @@ import java.util.Locale;
if
(
fatalError
!=
null
)
{
throw
fatalError
;
}
if
(
expectedPlaylistUrl
!=
null
)
{
playlistTracker
.
maybeThrowPlaylistRefreshError
(
expectedPlaylistUrl
);
}
}
/**
...
...
@@ -194,6 +199,7 @@ import java.util.Locale;
public
void
getNextChunk
(
HlsMediaChunk
previous
,
long
playbackPositionUs
,
HlsChunkHolder
out
)
{
int
oldVariantIndex
=
previous
==
null
?
C
.
INDEX_UNSET
:
trackGroup
.
indexOf
(
previous
.
trackFormat
);
expectedPlaylistUrl
=
null
;
// Use start time of the previous chunk rather than its end time because switching format will
// require downloading overlapping segments.
long
bufferedDurationUs
=
previous
==
null
?
0
...
...
@@ -207,6 +213,7 @@ import java.util.Locale;
HlsUrl
selectedUrl
=
variants
[
selectedVariantIndex
];
if
(!
playlistTracker
.
isSnapshotValid
(
selectedUrl
))
{
out
.
playlist
=
selectedUrl
;
expectedPlaylistUrl
=
selectedUrl
;
// Retry when playlist is refreshed.
return
;
}
...
...
@@ -246,6 +253,7 @@ import java.util.Locale;
out
.
endOfStream
=
true
;
}
else
/* Live */
{
out
.
playlist
=
selectedUrl
;
expectedPlaylistUrl
=
selectedUrl
;
}
return
;
}
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java
View file @
9f814850
...
...
@@ -39,6 +39,7 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
import
com.google.android.exoplayer2.util.TimestampAdjuster
;
import
com.google.android.exoplayer2.util.Util
;
import
java.io.IOException
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.concurrent.atomic.AtomicInteger
;
...
...
@@ -104,7 +105,8 @@ import java.util.concurrent.atomic.AtomicInteger;
* @param dataSpec Defines the data to be loaded.
* @param initDataSpec Defines the initialization data to be fed to new extractors. May be null.
* @param hlsUrl The url of the playlist from which this chunk was obtained.
* @param muxedCaptionFormats List of muxed caption {@link Format}s.
* @param muxedCaptionFormats List of muxed caption {@link Format}s. Null if no closed caption
* information is available in the master playlist.
* @param trackSelectionReason See {@link #trackSelectionReason}.
* @param trackSelectionData See {@link #trackSelectionData}.
* @param startTimeUs The start time of the chunk in microseconds.
...
...
@@ -356,9 +358,12 @@ import java.util.concurrent.atomic.AtomicInteger;
// This flag ensures the change of pid between streams does not affect the sample queues.
@DefaultTsPayloadReaderFactory
.
Flags
int
esReaderFactoryFlags
=
DefaultTsPayloadReaderFactory
.
FLAG_IGNORE_SPLICE_INFO_STREAM
;
if
(!
muxedCaptionFormats
.
isEmpty
())
{
List
<
Format
>
closedCaptionFormats
=
muxedCaptionFormats
;
if
(
closedCaptionFormats
!=
null
)
{
// The playlist declares closed caption renditions, we should ignore descriptors.
esReaderFactoryFlags
|=
DefaultTsPayloadReaderFactory
.
FLAG_OVERRIDE_CAPTION_DESCRIPTORS
;
}
else
{
closedCaptionFormats
=
Collections
.
emptyList
();
}
String
codecs
=
trackFormat
.
codecs
;
if
(!
TextUtils
.
isEmpty
(
codecs
))
{
...
...
@@ -373,7 +378,7 @@ import java.util.concurrent.atomic.AtomicInteger;
}
}
extractor
=
new
TsExtractor
(
TsExtractor
.
MODE_HLS
,
timestampAdjuster
,
new
DefaultTsPayloadReaderFactory
(
esReaderFactoryFlags
,
mux
edCaptionFormats
));
new
DefaultTsPayloadReaderFactory
(
esReaderFactoryFlags
,
clos
edCaptionFormats
));
}
if
(
usingNewExtractor
)
{
extractor
.
init
(
extractorOutput
);
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
View file @
9f814850
...
...
@@ -84,7 +84,7 @@ public final class HlsMediaSource implements MediaSource,
@Override
public
void
maybeThrowSourceInfoRefreshError
()
throws
IOException
{
playlistTracker
.
maybeThrowPlaylistRefreshError
();
playlistTracker
.
maybeThrowP
rimaryP
laylistRefreshError
();
}
@Override
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMasterPlaylist.java
View file @
9f814850
...
...
@@ -30,15 +30,31 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
*/
public
static
final
class
HlsUrl
{
/**
* The http url from which the media playlist can be obtained.
*/
public
final
String
url
;
/**
* Format information associated with the HLS url.
*/
public
final
Format
format
;
public
static
HlsUrl
createMediaPlaylistHlsUrl
(
String
baseUri
)
{
/**
* Creates an HLS url from a given http url.
*
* @param url The url.
* @return An HLS url.
*/
public
static
HlsUrl
createMediaPlaylistHlsUrl
(
String
url
)
{
Format
format
=
Format
.
createContainerFormat
(
"0"
,
MimeTypes
.
APPLICATION_M3U8
,
null
,
null
,
Format
.
NO_VALUE
,
0
,
null
);
return
new
HlsUrl
(
baseUri
,
format
);
return
new
HlsUrl
(
url
,
format
);
}
/**
* @param url See {@link #url}.
* @param format See {@link #format}.
*/
public
HlsUrl
(
String
url
,
Format
format
)
{
this
.
url
=
url
;
this
.
format
=
format
;
...
...
@@ -46,13 +62,39 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
}
/**
* The list of variants declared by the playlist.
*/
public
final
List
<
HlsUrl
>
variants
;
/**
* The list of demuxed audios declared by the playlist.
*/
public
final
List
<
HlsUrl
>
audios
;
/**
* The list of subtitles declared by the playlist.
*/
public
final
List
<
HlsUrl
>
subtitles
;
/**
* The format of the audio muxed in the variants. May be null if the playlist does not declare any
* muxed audio.
*/
public
final
Format
muxedAudioFormat
;
/**
* The format of the closed captions declared by the playlist. May be empty if the playlist
* explicitly declares no captions are available, or null if the playlist does not declare any
* captions information.
*/
public
final
List
<
Format
>
muxedCaptionFormats
;
/**
* @param baseUri The base uri. Used to resolve relative paths.
* @param variants See {@link #variants}.
* @param audios See {@link #audios}.
* @param subtitles See {@link #subtitles}.
* @param muxedAudioFormat See {@link #muxedAudioFormat}.
* @param muxedCaptionFormats See {@link #muxedCaptionFormats}.
*/
public
HlsMasterPlaylist
(
String
baseUri
,
List
<
HlsUrl
>
variants
,
List
<
HlsUrl
>
audios
,
List
<
HlsUrl
>
subtitles
,
Format
muxedAudioFormat
,
List
<
Format
>
muxedCaptionFormats
)
{
super
(
baseUri
);
...
...
@@ -60,14 +102,20 @@ public final class HlsMasterPlaylist extends HlsPlaylist {
this
.
audios
=
Collections
.
unmodifiableList
(
audios
);
this
.
subtitles
=
Collections
.
unmodifiableList
(
subtitles
);
this
.
muxedAudioFormat
=
muxedAudioFormat
;
this
.
muxedCaptionFormats
=
Collections
.
unmodifiableList
(
muxedCaptionFormats
);
this
.
muxedCaptionFormats
=
muxedCaptionFormats
!=
null
?
Collections
.
unmodifiableList
(
muxedCaptionFormats
)
:
null
;
}
public
static
HlsMasterPlaylist
createSingleVariantMasterPlaylist
(
String
variantUri
)
{
List
<
HlsUrl
>
variant
=
Collections
.
singletonList
(
HlsUrl
.
createMediaPlaylistHlsUrl
(
variantUri
));
/**
* Creates a playlist with a single variant.
*
* @param variantUrl The url of the single variant.
* @return A master playlist with a single variant for the provided url.
*/
public
static
HlsMasterPlaylist
createSingleVariantMasterPlaylist
(
String
variantUrl
)
{
List
<
HlsUrl
>
variant
=
Collections
.
singletonList
(
HlsUrl
.
createMediaPlaylistHlsUrl
(
variantUrl
));
List
<
HlsUrl
>
emptyList
=
Collections
.
emptyList
();
return
new
HlsMasterPlaylist
(
null
,
variant
,
emptyList
,
emptyList
,
null
,
Collections
.<
Format
>
emptyList
());
return
new
HlsMasterPlaylist
(
null
,
variant
,
emptyList
,
emptyList
,
null
,
null
);
}
}
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsMediaPlaylist.java
View file @
9f814850
...
...
@@ -91,12 +91,14 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
public
final
boolean
hasProgramDateTime
;
public
final
Segment
initializationSegment
;
public
final
List
<
Segment
>
segments
;
public
final
List
<
String
>
dateRanges
;
public
final
long
durationUs
;
public
HlsMediaPlaylist
(
@PlaylistType
int
playlistType
,
String
baseUri
,
long
startOffsetUs
,
long
startTimeUs
,
boolean
hasDiscontinuitySequence
,
int
discontinuitySequence
,
int
mediaSequence
,
int
version
,
long
targetDurationUs
,
boolean
hasEndTag
,
boolean
hasProgramDateTime
,
Segment
initializationSegment
,
List
<
Segment
>
segments
)
{
boolean
hasProgramDateTime
,
Segment
initializationSegment
,
List
<
Segment
>
segments
,
List
<
String
>
dateRanges
)
{
super
(
baseUri
);
this
.
playlistType
=
playlistType
;
this
.
startTimeUs
=
startTimeUs
;
...
...
@@ -117,6 +119,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
}
this
.
startOffsetUs
=
startOffsetUs
==
C
.
TIME_UNSET
?
C
.
TIME_UNSET
:
startOffsetUs
>=
0
?
startOffsetUs
:
durationUs
+
startOffsetUs
;
this
.
dateRanges
=
Collections
.
unmodifiableList
(
dateRanges
);
}
/**
...
...
@@ -155,7 +158,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
public
HlsMediaPlaylist
copyWith
(
long
startTimeUs
,
int
discontinuitySequence
)
{
return
new
HlsMediaPlaylist
(
playlistType
,
baseUri
,
startOffsetUs
,
startTimeUs
,
true
,
discontinuitySequence
,
mediaSequence
,
version
,
targetDurationUs
,
hasEndTag
,
hasProgramDateTime
,
initializationSegment
,
segments
);
hasProgramDateTime
,
initializationSegment
,
segments
,
dateRanges
);
}
/**
...
...
@@ -170,7 +173,7 @@ public final class HlsMediaPlaylist extends HlsPlaylist {
}
return
new
HlsMediaPlaylist
(
playlistType
,
baseUri
,
startOffsetUs
,
startTimeUs
,
hasDiscontinuitySequence
,
discontinuitySequence
,
mediaSequence
,
version
,
targetDurationUs
,
true
,
hasProgramDateTime
,
initializationSegment
,
segments
);
true
,
hasProgramDateTime
,
initializationSegment
,
segments
,
dateRanges
);
}
}
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java
View file @
9f814850
...
...
@@ -29,6 +29,7 @@ import java.io.IOException;
import
java.io.InputStream
;
import
java.io.InputStreamReader
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.LinkedList
;
import
java.util.List
;
import
java.util.Queue
;
...
...
@@ -57,6 +58,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private
static
final
String
TAG_ENDLIST
=
"#EXT-X-ENDLIST"
;
private
static
final
String
TAG_KEY
=
"#EXT-X-KEY"
;
private
static
final
String
TAG_BYTERANGE
=
"#EXT-X-BYTERANGE"
;
private
static
final
String
TAG_DATERANGE
=
"#EXT-X-DATERANGE"
;
private
static
final
String
TYPE_AUDIO
=
"AUDIO"
;
private
static
final
String
TYPE_VIDEO
=
"VIDEO"
;
...
...
@@ -69,6 +71,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
private
static
final
String
BOOLEAN_TRUE
=
"YES"
;
private
static
final
String
BOOLEAN_FALSE
=
"NO"
;
private
static
final
String
ATTR_CLOSED_CAPTIONS_NONE
=
"CLOSED-CAPTIONS=NONE"
;
private
static
final
Pattern
REGEX_BANDWIDTH
=
Pattern
.
compile
(
"BANDWIDTH=(\\d+)\\b"
);
private
static
final
Pattern
REGEX_CODECS
=
Pattern
.
compile
(
"CODECS=\"(.+?)\""
);
private
static
final
Pattern
REGEX_RESOLUTION
=
Pattern
.
compile
(
"RESOLUTION=(\\d+x\\d+)"
);
...
...
@@ -172,7 +176,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
ArrayList
<
HlsMasterPlaylist
.
HlsUrl
>
audios
=
new
ArrayList
<>();
ArrayList
<
HlsMasterPlaylist
.
HlsUrl
>
subtitles
=
new
ArrayList
<>();
Format
muxedAudioFormat
=
null
;
ArrayList
<
Format
>
muxedCaptionFormats
=
new
ArrayList
<>();
List
<
Format
>
muxedCaptionFormats
=
null
;
boolean
noClosedCaptions
=
false
;
String
line
;
while
(
iterator
.
hasNext
())
{
...
...
@@ -209,6 +214,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
mimeType
=
MimeTypes
.
APPLICATION_CEA708
;
accessibilityChannel
=
Integer
.
parseInt
(
instreamId
.
substring
(
7
));
}
if
(
muxedCaptionFormats
==
null
)
{
muxedCaptionFormats
=
new
ArrayList
<>();
}
muxedCaptionFormats
.
add
(
Format
.
createTextContainerFormat
(
id
,
null
,
mimeType
,
null
,
Format
.
NO_VALUE
,
selectionFlags
,
language
,
accessibilityChannel
));
break
;
...
...
@@ -220,6 +228,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
int
bitrate
=
parseIntAttr
(
line
,
REGEX_BANDWIDTH
);
String
codecs
=
parseOptionalStringAttr
(
line
,
REGEX_CODECS
);
String
resolutionString
=
parseOptionalStringAttr
(
line
,
REGEX_RESOLUTION
);
noClosedCaptions
|=
line
.
contains
(
ATTR_CLOSED_CAPTIONS_NONE
);
int
width
;
int
height
;
if
(
resolutionString
!=
null
)
{
...
...
@@ -242,6 +251,9 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
variants
.
add
(
new
HlsMasterPlaylist
.
HlsUrl
(
line
,
format
));
}
}
if
(
noClosedCaptions
)
{
muxedCaptionFormats
=
Collections
.
emptyList
();
}
return
new
HlsMasterPlaylist
(
baseUri
,
variants
,
audios
,
subtitles
,
muxedAudioFormat
,
muxedCaptionFormats
);
}
...
...
@@ -263,6 +275,7 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
boolean
hasEndTag
=
false
;
Segment
initializationSegment
=
null
;
List
<
Segment
>
segments
=
new
ArrayList
<>();
List
<
String
>
dateRanges
=
new
ArrayList
<>();
long
segmentDurationUs
=
0
;
boolean
hasDiscontinuitySequence
=
false
;
...
...
@@ -343,6 +356,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
C
.
msToUs
(
Util
.
parseXsDateTime
(
line
.
substring
(
line
.
indexOf
(
':'
)
+
1
)));
playlistStartTimeUs
=
programDatetimeUs
-
segmentStartTimeUs
;
}
}
else
if
(
line
.
startsWith
(
TAG_DATERANGE
))
{
dateRanges
.
add
(
line
);
}
else
if
(!
line
.
startsWith
(
"#"
))
{
String
segmentEncryptionIV
;
if
(!
isEncrypted
)
{
...
...
@@ -371,7 +386,8 @@ public final class HlsPlaylistParser implements ParsingLoadable.Parser<HlsPlayli
}
return
new
HlsMediaPlaylist
(
playlistType
,
baseUri
,
startOffsetUs
,
playlistStartTimeUs
,
hasDiscontinuitySequence
,
playlistDiscontinuitySequence
,
mediaSequence
,
version
,
targetDurationUs
,
hasEndTag
,
playlistStartTimeUs
!=
0
,
initializationSegment
,
segments
);
targetDurationUs
,
hasEndTag
,
playlistStartTimeUs
!=
0
,
initializationSegment
,
segments
,
dateRanges
);
}
private
static
String
parseStringAttr
(
String
line
,
Pattern
pattern
)
throws
ParserException
{
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java
View file @
9f814850
...
...
@@ -200,19 +200,30 @@ public final class HlsPlaylistTracker implements Loader.Callback<ParsingLoadable
}
/**
* If the tracker is having trouble refreshing the
primary playlist or loading an irreplaceable
*
playlist, this
method throws the underlying error. Otherwise, does nothing.
* If the tracker is having trouble refreshing the
master playlist or the primary playlist, this
* method throws the underlying error. Otherwise, does nothing.
*
* @throws IOException The underlying error.
*/
public
void
maybeThrowPlaylistRefreshError
()
throws
IOException
{
public
void
maybeThrowP
rimaryP
laylistRefreshError
()
throws
IOException
{
initialPlaylistLoader
.
maybeThrowError
();
if
(
primaryHlsUrl
!=
null
)
{
playlistBundles
.
get
(
primaryHlsUrl
).
mediaPlaylistLoader
.
maybeThrowError
(
);
maybeThrowPlaylistRefreshError
(
primaryHlsUrl
);
}
}
/**
* If the playlist is having trouble loading the playlist referenced by the given {@link HlsUrl},
* this method throws the underlying error.
*
* @param url The {@link HlsUrl}.
* @throws IOException The underyling error.
*/
public
void
maybeThrowPlaylistRefreshError
(
HlsUrl
url
)
throws
IOException
{
playlistBundles
.
get
(
url
).
mediaPlaylistLoader
.
maybeThrowError
();
}
/**
* Triggers a playlist refresh and whitelists it.
*
* @param url The {@link HlsUrl} of the playlist to be refreshed.
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java
View file @
9f814850
...
...
@@ -61,22 +61,21 @@ public class DefaultTimeBar extends View implements TimeBar {
private
static
final
int
DEFAULT_INCREMENT_COUNT
=
20
;
private
static
final
int
DEFAULT_BAR_HEIGHT
=
4
;
private
static
final
int
DEFAULT_TOUCH_TARGET_HEIGHT
=
26
;
private
static
final
int
DEFAULT_PLAYED_COLOR
=
0x33FFFFFF
;
private
static
final
int
DEFAULT_BUFFERED_COLOR
=
0xCCFFFFFF
;
private
static
final
int
DEFAULT_PLAYED_COLOR
=
0xFFFFFFFF
;
private
static
final
int
DEFAULT_AD_MARKER_COLOR
=
0xB2FFFF00
;
private
static
final
int
DEFAULT_AD_MARKER_WIDTH
=
4
;
private
static
final
int
DEFAULT_SCRUBBER_ENABLED_SIZE
=
12
;
private
static
final
int
DEFAULT_SCRUBBER_DISABLED_SIZE
=
0
;
private
static
final
int
DEFAULT_SCRUBBER_DRAGGED_SIZE
=
16
;
private
static
final
int
OPAQUE_COLOR
=
0xFF000000
;
private
final
Rect
seekBounds
;
private
final
Rect
progressBar
;
private
final
Rect
bufferedBar
;
private
final
Rect
scrubberBar
;
private
final
Paint
progressPaint
;
private
final
Paint
bufferedPaint
;
private
final
Paint
playedPaint
;
private
final
Paint
scrubberPaint
;
private
final
Paint
bufferedPaint
;
private
final
Paint
unplayedPaint
;
private
final
Paint
adMarkerPaint
;
private
final
int
barHeight
;
private
final
int
touchTargetHeight
;
...
...
@@ -115,9 +114,10 @@ public class DefaultTimeBar extends View implements TimeBar {
progressBar
=
new
Rect
();
bufferedBar
=
new
Rect
();
scrubberBar
=
new
Rect
();
progressPaint
=
new
Paint
();
bufferedPaint
=
new
Paint
();
playedPaint
=
new
Paint
();
scrubberPaint
=
new
Paint
();
bufferedPaint
=
new
Paint
();
unplayedPaint
=
new
Paint
();
adMarkerPaint
=
new
Paint
();
// Calculate the dimensions and paints for drawn elements.
...
...
@@ -147,13 +147,18 @@ public class DefaultTimeBar extends View implements TimeBar {
scrubberDraggedSize
=
a
.
getDimensionPixelSize
(
R
.
styleable
.
DefaultTimeBar_scrubber_dragged_size
,
defaultScrubberDraggedSize
);
int
playedColor
=
a
.
getInt
(
R
.
styleable
.
DefaultTimeBar_played_color
,
DEFAULT_PLAYED_COLOR
);
int
scrubberColor
=
a
.
getInt
(
R
.
styleable
.
DefaultTimeBar_scrubber_color
,
getDefaultScrubberColor
(
playedColor
));
int
bufferedColor
=
a
.
getInt
(
R
.
styleable
.
DefaultTimeBar_buffered_color
,
DEFAULT_BUFFERED_COLOR
);
getDefaultBufferedColor
(
playedColor
));
int
unplayedColor
=
a
.
getInt
(
R
.
styleable
.
DefaultTimeBar_unplayed_color
,
getDefaultUnplayedColor
(
playedColor
));
int
adMarkerColor
=
a
.
getInt
(
R
.
styleable
.
DefaultTimeBar_ad_marker_color
,
DEFAULT_AD_MARKER_COLOR
);
p
rogress
Paint
.
setColor
(
playedColor
);
scrubberPaint
.
setColor
(
OPAQUE_COLOR
|
played
Color
);
p
layed
Paint
.
setColor
(
playedColor
);
scrubberPaint
.
setColor
(
scrubber
Color
);
bufferedPaint
.
setColor
(
bufferedColor
);
unplayedPaint
.
setColor
(
unplayedColor
);
adMarkerPaint
.
setColor
(
adMarkerColor
);
}
finally
{
a
.
recycle
();
...
...
@@ -165,9 +170,10 @@ public class DefaultTimeBar extends View implements TimeBar {
scrubberEnabledSize
=
defaultScrubberEnabledSize
;
scrubberDisabledSize
=
defaultScrubberDisabledSize
;
scrubberDraggedSize
=
defaultScrubberDraggedSize
;
scrubberPaint
.
setColor
(
OPAQUE_COLOR
|
DEFAULT_PLAYED_COLOR
);
progressPaint
.
setColor
(
DEFAULT_PLAYED_COLOR
);
bufferedPaint
.
setColor
(
DEFAULT_BUFFERED_COLOR
);
playedPaint
.
setColor
(
DEFAULT_PLAYED_COLOR
);
scrubberPaint
.
setColor
(
getDefaultScrubberColor
(
DEFAULT_PLAYED_COLOR
));
bufferedPaint
.
setColor
(
getDefaultBufferedColor
(
DEFAULT_PLAYED_COLOR
));
unplayedPaint
.
setColor
(
getDefaultUnplayedColor
(
DEFAULT_PLAYED_COLOR
));
adMarkerPaint
.
setColor
(
DEFAULT_AD_MARKER_COLOR
);
}
formatBuilder
=
new
StringBuilder
();
...
...
@@ -337,16 +343,18 @@ public class DefaultTimeBar extends View implements TimeBar {
@Override
protected
void
onMeasure
(
int
widthMeasureSpec
,
int
heightMeasureSpec
)
{
int
measureWidth
=
MeasureSpec
.
getSize
(
widthMeasureSpec
);
int
measureHeight
=
MeasureSpec
.
getSize
(
heightMeasureSpec
);
setMeasuredDimension
(
measureWidth
,
measureHeight
);
int
heightMode
=
MeasureSpec
.
getMode
(
heightMeasureSpec
);
int
heightSize
=
MeasureSpec
.
getSize
(
heightMeasureSpec
);
int
height
=
heightMode
==
MeasureSpec
.
UNSPECIFIED
?
touchTargetHeight
:
heightMode
==
MeasureSpec
.
EXACTLY
?
heightSize
:
Math
.
min
(
touchTargetHeight
,
heightSize
);
setMeasuredDimension
(
MeasureSpec
.
getSize
(
widthMeasureSpec
),
height
);
}
@Override
protected
void
onLayout
(
boolean
changed
,
int
left
,
int
top
,
int
right
,
int
bottom
)
{
int
width
=
right
-
left
;
int
height
=
bottom
-
top
;
int
barY
=
height
-
touchTargetHeight
;
int
barY
=
(
height
-
touchTargetHeight
)
/
2
;
int
seekLeft
=
getPaddingLeft
();
int
seekRight
=
width
-
getPaddingRight
();
int
progressY
=
barY
+
(
touchTargetHeight
-
barHeight
)
/
2
;
...
...
@@ -457,12 +465,10 @@ public class DefaultTimeBar extends View implements TimeBar {
scrubberBar
.
set
(
progressBar
);
long
newScrubberTime
=
scrubbing
?
scrubPosition
:
position
;
if
(
duration
>
0
)
{
int
bufferedPixelWidth
=
(
int
)
((
progressBar
.
width
()
*
bufferedPosition
)
/
duration
);
bufferedBar
.
right
=
progressBar
.
left
+
bufferedPixelWidth
;
int
scrubberPixelPosition
=
(
int
)
((
progressBar
.
width
()
*
newScrubberTime
)
/
duration
);
scrubberBar
.
right
=
progressBar
.
left
+
scrubberPixelPosition
;
int
bufferedPixelWidth
=
(
int
)
((
progressBar
.
width
()
*
bufferedPosition
)
/
duration
);
bufferedBar
.
right
=
Math
.
min
(
progressBar
.
left
+
bufferedPixelWidth
,
progressBar
.
right
);
int
scrubberPixelPosition
=
(
int
)
((
progressBar
.
width
()
*
newScrubberTime
)
/
duration
);
scrubberBar
.
right
=
Math
.
min
(
progressBar
.
left
+
scrubberPixelPosition
,
progressBar
.
right
);
}
else
{
bufferedBar
.
right
=
progressBar
.
left
;
scrubberBar
.
right
=
progressBar
.
left
;
...
...
@@ -502,21 +508,21 @@ public class DefaultTimeBar extends View implements TimeBar {
int
barTop
=
progressBar
.
centerY
()
-
progressBarHeight
/
2
;
int
barBottom
=
barTop
+
progressBarHeight
;
if
(
duration
<=
0
)
{
canvas
.
drawRect
(
progressBar
.
left
,
barTop
,
progressBar
.
right
,
barBottom
,
progress
Paint
);
canvas
.
drawRect
(
progressBar
.
left
,
barTop
,
progressBar
.
right
,
barBottom
,
unplayed
Paint
);
return
;
}
int
bufferedLeft
=
bufferedBar
.
left
;
int
bufferedRight
=
bufferedBar
.
right
;
int
progressLeft
=
Math
.
max
(
Math
.
max
(
progressBar
.
left
,
bufferedRight
),
scrubberBar
.
right
);
if
(
progressLeft
<
progressBar
.
right
)
{
canvas
.
drawRect
(
progressLeft
,
barTop
,
progressBar
.
right
,
barBottom
,
progress
Paint
);
canvas
.
drawRect
(
progressLeft
,
barTop
,
progressBar
.
right
,
barBottom
,
unplayed
Paint
);
}
bufferedLeft
=
Math
.
max
(
bufferedLeft
,
scrubberBar
.
right
);
if
(
bufferedRight
>
bufferedLeft
)
{
canvas
.
drawRect
(
bufferedLeft
,
barTop
,
bufferedRight
,
barBottom
,
bufferedPaint
);
}
if
(
scrubberBar
.
width
()
>
0
)
{
canvas
.
drawRect
(
scrubberBar
.
left
,
barTop
,
scrubberBar
.
right
,
barBottom
,
scrubber
Paint
);
canvas
.
drawRect
(
scrubberBar
.
left
,
barTop
,
scrubberBar
.
right
,
barBottom
,
played
Paint
);
}
int
adMarkerOffset
=
adMarkerWidth
/
2
;
for
(
int
i
=
0
;
i
<
adBreakCount
;
i
++)
{
...
...
@@ -577,4 +583,16 @@ public class DefaultTimeBar extends View implements TimeBar {
return
(
int
)
(
dps
*
displayMetrics
.
density
+
0.5f
);
}
private
static
int
getDefaultScrubberColor
(
int
playedColor
)
{
return
0xFF000000
|
playedColor
;
}
private
static
int
getDefaultUnplayedColor
(
int
playedColor
)
{
return
0x33000000
|
(
playedColor
&
0x00FFFFFF
);
}
private
static
int
getDefaultBufferedColor
(
int
playedColor
)
{
return
0xCC000000
|
(
playedColor
&
0x00FFFFFF
);
}
}
library/ui/src/main/res/values/attrs.xml
View file @
9f814850
...
...
@@ -68,7 +68,9 @@
<attr
name=
"scrubber_disabled_size"
format=
"dimension"
/>
<attr
name=
"scrubber_dragged_size"
format=
"dimension"
/>
<attr
name=
"played_color"
format=
"color"
/>
<attr
name=
"scrubber_color"
format=
"color"
/>
<attr
name=
"buffered_color"
format=
"color"
/>
<attr
name=
"unplayed_color"
format=
"color"
/>
<attr
name=
"ad_marker_color"
format=
"color"
/>
</declare-styleable>
...
...
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