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
f4b8d949
authored
Oct 12, 2016
by
ojw28
Committed by
GitHub
Oct 12, 2016
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #1935 from google/dev-v2
Update dev-v2-id3 with dev-v2
parents
97e7fb85
996fe47f
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
303 additions
and
192 deletions
demo/build.gradle
demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java
demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java
extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java
extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java
extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceFactory.java
extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java
library/src/main/java/com/google/android/exoplayer2/ExoPlayer.java
library/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java
library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java
library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java
library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java
library/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java
library/src/main/res/values/attrs.xml
demo/build.gradle
View file @
f4b8d949
...
...
@@ -38,15 +38,15 @@ android {
}
productFlavors
{
demo
demoExt
noExtensions
withExtensions
}
}
dependencies
{
compile
project
(
':library'
)
demoExt
Compile
project
(
path:
':extension-ffmpeg'
)
demoExt
Compile
project
(
path:
':extension-flac'
)
demoExt
Compile
project
(
path:
':extension-opus'
)
demoExt
Compile
project
(
path:
':extension-vp9'
)
withExtensions
Compile
project
(
path:
':extension-ffmpeg'
)
withExtensions
Compile
project
(
path:
':extension-flac'
)
withExtensions
Compile
project
(
path:
':extension-opus'
)
withExtensions
Compile
project
(
path:
':extension-vp9'
)
}
demo/src/main/java/com/google/android/exoplayer2/demo/EventLogger.java
View file @
f4b8d949
...
...
@@ -98,6 +98,9 @@ import java.util.Locale;
@Override
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
)
{
if
(
timeline
==
null
)
{
return
;
}
int
periodCount
=
timeline
.
getPeriodCount
();
int
windowCount
=
timeline
.
getWindowCount
();
Log
.
d
(
TAG
,
"sourceInfo [periodCount="
+
periodCount
+
", windowCount="
+
windowCount
);
...
...
demo/src/main/java/com/google/android/exoplayer2/demo/PlayerActivity.java
View file @
f4b8d949
...
...
@@ -101,6 +101,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
}
private
Handler
mainHandler
;
private
Timeline
.
Window
window
;
private
EventLogger
eventLogger
;
private
SimpleExoPlayerView
simpleExoPlayerView
;
private
LinearLayout
debugRootView
;
...
...
@@ -115,7 +116,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
private
boolean
playerNeedsSource
;
private
boolean
shouldAutoPlay
;
private
boolean
shouldRestorePosition
;
private
boolean
isTimelineStatic
;
private
int
playerWindow
;
private
long
playerPosition
;
...
...
@@ -127,6 +128,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
shouldAutoPlay
=
true
;
mediaDataSourceFactory
=
buildDataSourceFactory
(
true
);
mainHandler
=
new
Handler
();
window
=
new
Timeline
.
Window
();
if
(
CookieHandler
.
getDefault
()
!=
DEFAULT_COOKIE_MANAGER
)
{
CookieHandler
.
setDefault
(
DEFAULT_COOKIE_MANAGER
);
}
...
...
@@ -147,7 +149,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
@Override
public
void
onNewIntent
(
Intent
intent
)
{
releasePlayer
();
shouldRestorePosition
=
false
;
isTimelineStatic
=
false
;
setIntent
(
intent
);
}
...
...
@@ -262,7 +264,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
player
.
setVideoDebugListener
(
eventLogger
);
player
.
setId3Output
(
eventLogger
);
simpleExoPlayerView
.
setPlayer
(
player
);
if
(
shouldRestorePosition
)
{
if
(
isTimelineStatic
)
{
if
(
playerPosition
==
C
.
TIME_UNSET
)
{
player
.
seekToDefaultPosition
(
playerWindow
);
}
else
{
...
...
@@ -305,7 +307,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
}
MediaSource
mediaSource
=
mediaSources
.
length
==
1
?
mediaSources
[
0
]
:
new
ConcatenatingMediaSource
(
mediaSources
);
player
.
prepare
(
mediaSource
,
!
shouldRestorePosition
);
player
.
prepare
(
mediaSource
,
!
isTimelineStatic
,
!
isTimelineStatic
);
playerNeedsSource
=
false
;
updateButtonVisibilities
();
}
...
...
@@ -348,15 +350,11 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
debugViewHelper
.
stop
();
debugViewHelper
=
null
;
shouldAutoPlay
=
player
.
getPlayWhenReady
();
shouldRestorePosition
=
false
;
playerWindow
=
player
.
getCurrentWindowIndex
();
playerPosition
=
C
.
TIME_UNSET
;
Timeline
timeline
=
player
.
getCurrentTimeline
();
if
(
timeline
!=
null
)
{
playerWindow
=
player
.
getCurrentWindowIndex
();
Timeline
.
Window
window
=
timeline
.
getWindow
(
playerWindow
,
new
Timeline
.
Window
());
if
(!
window
.
isDynamic
)
{
shouldRestorePosition
=
true
;
playerPosition
=
window
.
isSeekable
?
player
.
getCurrentPosition
()
:
C
.
TIME_UNSET
;
}
if
(
timeline
!=
null
&&
timeline
.
getWindow
(
playerWindow
,
window
).
isSeekable
)
{
playerPosition
=
player
.
getCurrentPosition
();
}
player
.
release
();
player
=
null
;
...
...
@@ -412,7 +410,8 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
@Override
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
)
{
// Do nothing.
isTimelineStatic
=
timeline
!=
null
&&
timeline
.
getWindowCount
()
>
0
&&
!
timeline
.
getWindow
(
timeline
.
getWindowCount
()
-
1
,
window
).
isDynamic
;
}
@Override
...
...
@@ -501,7 +500,7 @@ public class PlayerActivity extends Activity implements OnClickListener, ExoPlay
button
.
setText
(
label
);
button
.
setTag
(
i
);
button
.
setOnClickListener
(
this
);
debugRootView
.
addView
(
button
);
debugRootView
.
addView
(
button
,
debugRootView
.
getChildCount
()
-
1
);
}
}
}
...
...
extensions/cronet/src/androidTest/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceTest.java
View file @
f4b8d949
...
...
@@ -174,10 +174,7 @@ public final class CronetDataSourceTest {
@Test
(
expected
=
IllegalStateException
.
class
)
public
void
testOpeningTwiceThrows
()
throws
HttpDataSourceException
{
mockResponseStartSuccess
();
assertConnectionState
(
CronetDataSource
.
IDLE_CONNECTION
);
dataSourceUnderTest
.
open
(
testDataSpec
);
assertConnectionState
(
CronetDataSource
.
OPEN_CONNECTION
);
dataSourceUnderTest
.
open
(
testDataSpec
);
}
...
...
@@ -205,7 +202,7 @@ public final class CronetDataSourceTest {
dataSourceUnderTest
.
onFailed
(
mockUrlRequest
,
testUrlResponseInfo
,
null
);
mockUrlRequestException
);
dataSourceUnderTest
.
onResponseStarted
(
mockUrlRequest2
,
testUrlResponseInfo
);
...
...
@@ -253,13 +250,10 @@ public final class CronetDataSourceTest {
@Test
public
void
testRequestOpen
()
throws
HttpDataSourceException
{
mockResponseStartSuccess
();
assertEquals
(
TEST_CONTENT_LENGTH
,
dataSourceUnderTest
.
open
(
testDataSpec
));
assertConnectionState
(
CronetDataSource
.
OPEN_CONNECTION
);
verify
(
mockTransferListener
).
onTransferStart
(
dataSourceUnderTest
,
testDataSpec
);
}
@Test
public
void
testRequestOpenGzippedCompressedReturnsDataSpecLength
()
throws
HttpDataSourceException
{
...
...
@@ -271,7 +265,6 @@ public final class CronetDataSourceTest {
testDataSpec
=
new
DataSpec
(
Uri
.
parse
(
TEST_URL
),
1000
,
5000
,
null
);
assertEquals
(
5000
/* contentLength */
,
dataSourceUnderTest
.
open
(
testDataSpec
));
assertConnectionState
(
CronetDataSource
.
OPEN_CONNECTION
);
verify
(
mockTransferListener
).
onTransferStart
(
dataSourceUnderTest
,
testDataSpec
);
}
...
...
@@ -286,7 +279,6 @@ public final class CronetDataSourceTest {
// Check for connection not automatically closed.
assertFalse
(
e
.
getCause
()
instanceof
UnknownHostException
);
verify
(
mockUrlRequest
,
never
()).
cancel
();
assertConnectionState
(
CronetDataSource
.
OPENING_CONNECTION
);
verify
(
mockTransferListener
,
never
()).
onTransferStart
(
dataSourceUnderTest
,
testDataSpec
);
}
}
...
...
@@ -304,7 +296,6 @@ public final class CronetDataSourceTest {
// Check for connection not automatically closed.
assertTrue
(
e
.
getCause
()
instanceof
UnknownHostException
);
verify
(
mockUrlRequest
,
never
()).
cancel
();
assertConnectionState
(
CronetDataSource
.
OPENING_CONNECTION
);
verify
(
mockTransferListener
,
never
()).
onTransferStart
(
dataSourceUnderTest
,
testDataSpec
);
}
}
...
...
@@ -321,7 +312,6 @@ public final class CronetDataSourceTest {
assertTrue
(
e
instanceof
HttpDataSource
.
InvalidResponseCodeException
);
// Check for connection not automatically closed.
verify
(
mockUrlRequest
,
never
()).
cancel
();
assertConnectionState
(
CronetDataSource
.
OPENING_CONNECTION
);
verify
(
mockTransferListener
,
never
()).
onTransferStart
(
dataSourceUnderTest
,
testDataSpec
);
}
}
...
...
@@ -338,37 +328,16 @@ public final class CronetDataSourceTest {
assertTrue
(
e
instanceof
HttpDataSource
.
InvalidContentTypeException
);
// Check for connection not automatically closed.
verify
(
mockUrlRequest
,
never
()).
cancel
();
assertConnectionState
(
CronetDataSource
.
OPENING_CONNECTION
);
verify
(
mockContentTypePredicate
).
evaluate
(
TEST_CONTENT_TYPE
);
}
}
@Test
public
void
testRequestOpenValidatesContentLength
()
{
mockResponseStartSuccess
();
// Data spec's requested length, 5000. Test response's length, 16,000.
testDataSpec
=
new
DataSpec
(
Uri
.
parse
(
TEST_URL
),
1000
,
5000
,
null
);
try
{
dataSourceUnderTest
.
open
(
testDataSpec
);
fail
(
"HttpDataSource.HttpDataSourceException expected"
);
}
catch
(
HttpDataSourceException
e
)
{
verify
(
mockUrlRequest
).
addHeader
(
"Range"
,
"bytes=1000-5999"
);
// Check for connection not automatically closed.
verify
(
mockUrlRequest
,
never
()).
cancel
();
assertConnectionState
(
CronetDataSource
.
OPENING_CONNECTION
);
verify
(
mockTransferListener
,
never
()).
onTransferStart
(
dataSourceUnderTest
,
testPostDataSpec
);
}
}
@Test
public
void
testPostRequestOpen
()
throws
HttpDataSourceException
{
mockResponseStartSuccess
();
dataSourceUnderTest
.
setRequestProperty
(
"Content-Type"
,
TEST_CONTENT_TYPE
);
assertEquals
(
TEST_CONTENT_LENGTH
,
dataSourceUnderTest
.
open
(
testPostDataSpec
));
assertConnectionState
(
CronetDataSource
.
OPEN_CONNECTION
);
verify
(
mockTransferListener
).
onTransferStart
(
dataSourceUnderTest
,
testPostDataSpec
);
}
...
...
@@ -510,7 +479,6 @@ public final class CronetDataSourceTest {
dataSourceUnderTest
.
close
();
verify
(
mockTransferListener
).
onTransferEnd
(
dataSourceUnderTest
);
assertConnectionState
(
CronetDataSource
.
IDLE_CONNECTION
);
try
{
bytesRead
+=
dataSourceUnderTest
.
read
(
returnedBuffer
,
0
,
8
);
...
...
@@ -572,7 +540,6 @@ public final class CronetDataSourceTest {
verify
(
mockUrlRequest
,
times
(
1
)).
read
(
any
(
ByteBuffer
.
class
));
// Check for connection not automatically closed.
verify
(
mockUrlRequest
,
never
()).
cancel
();
assertConnectionState
(
CronetDataSource
.
OPEN_CONNECTION
);
assertEquals
(
16
,
bytesRead
);
}
...
...
@@ -603,15 +570,12 @@ public final class CronetDataSourceTest {
// We should still be trying to open.
assertFalse
(
timedOutCondition
.
block
(
50
));
assertEquals
(
CronetDataSource
.
OPENING_CONNECTION
,
dataSourceUnderTest
.
connectionState
);
// We should still be trying to open as we approach the timeout.
when
(
mockClock
.
elapsedRealtime
()).
thenReturn
((
long
)
TEST_CONNECT_TIMEOUT_MS
-
1
);
assertFalse
(
timedOutCondition
.
block
(
50
));
assertEquals
(
CronetDataSource
.
OPENING_CONNECTION
,
dataSourceUnderTest
.
connectionState
);
// Now we timeout.
when
(
mockClock
.
elapsedRealtime
()).
thenReturn
((
long
)
TEST_CONNECT_TIMEOUT_MS
);
timedOutCondition
.
block
();
assertEquals
(
CronetDataSource
.
OPENING_CONNECTION
,
dataSourceUnderTest
.
connectionState
);
verify
(
mockTransferListener
,
never
()).
onTransferStart
(
dataSourceUnderTest
,
testDataSpec
);
}
...
...
@@ -637,15 +601,12 @@ public final class CronetDataSourceTest {
// We should still be trying to open.
assertFalse
(
openCondition
.
block
(
50
));
assertEquals
(
CronetDataSource
.
OPENING_CONNECTION
,
dataSourceUnderTest
.
connectionState
);
// We should still be trying to open as we approach the timeout.
when
(
mockClock
.
elapsedRealtime
()).
thenReturn
((
long
)
TEST_CONNECT_TIMEOUT_MS
-
1
);
assertFalse
(
openCondition
.
block
(
50
));
assertEquals
(
CronetDataSource
.
OPENING_CONNECTION
,
dataSourceUnderTest
.
connectionState
);
// The response arrives just in time.
dataSourceUnderTest
.
onResponseStarted
(
mockUrlRequest
,
testUrlResponseInfo
);
openCondition
.
block
();
assertEquals
(
CronetDataSource
.
OPEN_CONNECTION
,
dataSourceUnderTest
.
connectionState
);
}
@Test
...
...
@@ -674,11 +635,9 @@ public final class CronetDataSourceTest {
// We should still be trying to open.
assertFalse
(
timedOutCondition
.
block
(
50
));
assertEquals
(
CronetDataSource
.
OPENING_CONNECTION
,
dataSourceUnderTest
.
connectionState
);
// We should still be trying to open as we approach the timeout.
when
(
mockClock
.
elapsedRealtime
()).
thenReturn
((
long
)
TEST_CONNECT_TIMEOUT_MS
-
1
);
assertFalse
(
timedOutCondition
.
block
(
50
));
assertEquals
(
CronetDataSource
.
OPENING_CONNECTION
,
dataSourceUnderTest
.
connectionState
);
// A redirect arrives just in time.
dataSourceUnderTest
.
onRedirectReceived
(
mockUrlRequest
,
testUrlResponseInfo
,
"RandomRedirectedUrl1"
);
...
...
@@ -689,7 +648,6 @@ public final class CronetDataSourceTest {
assertFalse
(
timedOutCondition
.
block
(
newTimeoutMs
));
// We should still be trying to open as we approach the new timeout.
assertFalse
(
timedOutCondition
.
block
(
50
));
assertEquals
(
CronetDataSource
.
OPENING_CONNECTION
,
dataSourceUnderTest
.
connectionState
);
// A redirect arrives just in time.
dataSourceUnderTest
.
onRedirectReceived
(
mockUrlRequest
,
testUrlResponseInfo
,
"RandomRedirectedUrl2"
);
...
...
@@ -700,11 +658,9 @@ public final class CronetDataSourceTest {
assertFalse
(
timedOutCondition
.
block
(
newTimeoutMs
));
// We should still be trying to open as we approach the new timeout.
assertFalse
(
timedOutCondition
.
block
(
50
));
assertEquals
(
CronetDataSource
.
OPENING_CONNECTION
,
dataSourceUnderTest
.
connectionState
);
// Now we timeout.
when
(
mockClock
.
elapsedRealtime
()).
thenReturn
(
newTimeoutMs
);
timedOutCondition
.
block
();
assertEquals
(
CronetDataSource
.
OPENING_CONNECTION
,
dataSourceUnderTest
.
connectionState
);
verify
(
mockTransferListener
,
never
()).
onTransferStart
(
dataSourceUnderTest
,
testDataSpec
);
assertEquals
(
1
,
openExceptions
.
get
());
...
...
@@ -818,7 +774,7 @@ public final class CronetDataSourceTest {
dataSourceUnderTest
.
onFailed
(
mockUrlRequest
,
createUrlResponseInfo
(
500
),
// statusCode
null
);
mockUrlRequestException
);
return
null
;
}
}).
when
(
mockUrlRequest
).
read
(
any
(
ByteBuffer
.
class
));
...
...
@@ -869,8 +825,4 @@ public final class CronetDataSourceTest {
return
testBuffer
;
}
private
void
assertConnectionState
(
int
state
)
{
assertEquals
(
state
,
dataSourceUnderTest
.
connectionState
);
}
}
extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java
View file @
f4b8d949
This diff is collapsed.
Click to expand it.
extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSourceFactory.java
View file @
f4b8d949
...
...
@@ -41,7 +41,7 @@ public final class CronetDataSourceFactory implements Factory {
private
final
CronetEngine
cronetEngine
;
private
final
Executor
executor
;
private
final
Predicate
<
String
>
contentTypePredicate
;
private
final
TransferListener
transferListener
;
private
final
TransferListener
<?
super
DataSource
>
transferListener
;
private
final
int
connectTimeoutMs
;
private
final
int
readTimeoutMs
;
private
final
boolean
resetTimeoutOnRedirects
;
...
...
extensions/okhttp/src/main/java/com/google/android/exoplayer2/ext/okhttp/OkHttpDataSource.java
View file @
f4b8d949
...
...
@@ -185,9 +185,12 @@ public class OkHttpDataSource implements HttpDataSource {
bytesToSkip
=
responseCode
==
200
&&
dataSpec
.
position
!=
0
?
dataSpec
.
position
:
0
;
// Determine the length of the data to be read, after skipping.
long
contentLength
=
response
.
body
().
contentLength
();
bytesToRead
=
dataSpec
.
length
!=
C
.
LENGTH_UNSET
?
dataSpec
.
length
:
(
contentLength
!=
-
1
?
(
contentLength
-
bytesToSkip
)
:
C
.
LENGTH_UNSET
);
if
(
dataSpec
.
length
!=
C
.
LENGTH_UNSET
)
{
bytesToRead
=
dataSpec
.
length
;
}
else
{
long
contentLength
=
response
.
body
().
contentLength
();
bytesToRead
=
contentLength
!=
-
1
?
(
contentLength
-
bytesToSkip
)
:
C
.
LENGTH_UNSET
;
}
opened
=
true
;
if
(
listener
!=
null
)
{
...
...
library/src/main/java/com/google/android/exoplayer2/ExoPlayer.java
View file @
f4b8d949
...
...
@@ -130,8 +130,8 @@ public interface ExoPlayer {
/**
* Called when timeline and/or manifest has been refreshed.
*
* @param timeline The latest timeline.
* @param manifest The latest manifest.
* @param timeline The latest timeline
, or null if the timeline is being cleared
.
* @param manifest The latest manifest
, or null if the manifest is being cleared
.
*/
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
);
...
...
@@ -247,7 +247,7 @@ public interface ExoPlayer {
/**
* Prepares the player to play the provided {@link MediaSource}. Equivalent to
* {@code prepare(mediaSource, true)}.
* {@code prepare(mediaSource, true
, true
)}.
*/
void
prepare
(
MediaSource
mediaSource
);
...
...
@@ -259,8 +259,11 @@ public interface ExoPlayer {
* @param resetPosition Whether the playback position should be reset to the default position in
* the first {@link Timeline.Window}. If false, playback will start from the position defined
* by {@link #getCurrentWindowIndex()} and {@link #getCurrentPosition()}.
* @param resetTimeline Whether the timeline and manifest should be reset. Should be true unless
* the player is being prepared to play the same media as it was playing previously (e.g. if
* playback failed and is being retried).
*/
void
prepare
(
MediaSource
mediaSource
,
boolean
resetPosition
);
void
prepare
(
MediaSource
mediaSource
,
boolean
resetPosition
,
boolean
resetTimeline
);
/**
* Sets whether playback should proceed when {@link #getPlaybackState()} == {@link #STATE_READY}.
...
...
library/src/main/java/com/google/android/exoplayer2/ExoPlayerFactory.java
View file @
f4b8d949
...
...
@@ -18,6 +18,7 @@ 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
;
/**
...
...
@@ -41,7 +42,7 @@ public final class ExoPlayerFactory {
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
*/
public
static
SimpleExoPlayer
newSimpleInstance
(
Context
context
,
TrackSelector
trackSelector
,
public
static
SimpleExoPlayer
newSimpleInstance
(
Context
context
,
TrackSelector
<?>
trackSelector
,
LoadControl
loadControl
)
{
return
newSimpleInstance
(
context
,
trackSelector
,
loadControl
,
null
);
}
...
...
@@ -56,8 +57,8 @@ public final class ExoPlayerFactory {
* @param drmSessionManager An optional {@link DrmSessionManager}. May be null if the instance
* will not be used for DRM protected playbacks.
*/
public
static
SimpleExoPlayer
newSimpleInstance
(
Context
context
,
TrackSelector
trackSelector
,
LoadControl
loadControl
,
DrmSessionManager
drmSessionManager
)
{
public
static
SimpleExoPlayer
newSimpleInstance
(
Context
context
,
TrackSelector
<?>
trackSelector
,
LoadControl
loadControl
,
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
)
{
return
newSimpleInstance
(
context
,
trackSelector
,
loadControl
,
drmSessionManager
,
false
);
}
...
...
@@ -74,8 +75,8 @@ public final class ExoPlayerFactory {
* available extensions over those defined in the core library. Note that extensions must be
* included in the application build for setting this flag to have any effect.
*/
public
static
SimpleExoPlayer
newSimpleInstance
(
Context
context
,
TrackSelector
trackSelector
,
LoadControl
loadControl
,
DrmSessionManager
drmSessionManager
,
public
static
SimpleExoPlayer
newSimpleInstance
(
Context
context
,
TrackSelector
<?>
trackSelector
,
LoadControl
loadControl
,
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
boolean
preferExtensionDecoders
)
{
return
newSimpleInstance
(
context
,
trackSelector
,
loadControl
,
drmSessionManager
,
preferExtensionDecoders
,
DEFAULT_ALLOWED_VIDEO_JOINING_TIME_MS
);
...
...
@@ -96,8 +97,8 @@ public final class ExoPlayerFactory {
* @param allowedVideoJoiningTimeMs The maximum duration for which a video renderer can attempt to
* seamlessly join an ongoing playback.
*/
public
static
SimpleExoPlayer
newSimpleInstance
(
Context
context
,
TrackSelector
trackSelector
,
LoadControl
loadControl
,
DrmSessionManager
drmSessionManager
,
public
static
SimpleExoPlayer
newSimpleInstance
(
Context
context
,
TrackSelector
<?>
trackSelector
,
LoadControl
loadControl
,
DrmSessionManager
<
FrameworkMediaCrypto
>
drmSessionManager
,
boolean
preferExtensionDecoders
,
long
allowedVideoJoiningTimeMs
)
{
return
new
SimpleExoPlayer
(
context
,
trackSelector
,
loadControl
,
drmSessionManager
,
preferExtensionDecoders
,
allowedVideoJoiningTimeMs
);
...
...
@@ -110,7 +111,7 @@ public final class ExoPlayerFactory {
* @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.
*/
public
static
ExoPlayer
newInstance
(
Renderer
[]
renderers
,
TrackSelector
trackSelector
)
{
public
static
ExoPlayer
newInstance
(
Renderer
[]
renderers
,
TrackSelector
<?>
trackSelector
)
{
return
newInstance
(
renderers
,
trackSelector
,
new
DefaultLoadControl
());
}
...
...
@@ -122,7 +123,7 @@ public final class ExoPlayerFactory {
* @param trackSelector The {@link TrackSelector} that will be used by the instance.
* @param loadControl The {@link LoadControl} that will be used by the instance.
*/
public
static
ExoPlayer
newInstance
(
Renderer
[]
renderers
,
TrackSelector
trackSelector
,
public
static
ExoPlayer
newInstance
(
Renderer
[]
renderers
,
TrackSelector
<?>
trackSelector
,
LoadControl
loadControl
)
{
return
new
ExoPlayerImpl
(
renderers
,
trackSelector
,
loadControl
);
}
...
...
library/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java
View file @
f4b8d949
...
...
@@ -35,7 +35,7 @@ import java.util.concurrent.CopyOnWriteArraySet;
private
static
final
String
TAG
=
"ExoPlayerImpl"
;
private
final
Handler
eventHandler
;
private
final
ExoPlayerImplInternal
internalPlayer
;
private
final
ExoPlayerImplInternal
<?>
internalPlayer
;
private
final
CopyOnWriteArraySet
<
EventListener
>
listeners
;
private
final
Timeline
.
Window
window
;
private
final
Timeline
.
Period
period
;
...
...
@@ -63,7 +63,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
* @param loadControl The {@link LoadControl} that will be used by the instance.
*/
@SuppressLint
(
"HandlerLeak"
)
public
ExoPlayerImpl
(
Renderer
[]
renderers
,
TrackSelector
trackSelector
,
LoadControl
loadControl
)
{
public
ExoPlayerImpl
(
Renderer
[]
renderers
,
TrackSelector
<?>
trackSelector
,
LoadControl
loadControl
)
{
Log
.
i
(
TAG
,
"Init "
+
ExoPlayerLibraryInfo
.
VERSION
);
Assertions
.
checkNotNull
(
renderers
);
Assertions
.
checkState
(
renderers
.
length
>
0
);
...
...
@@ -79,8 +80,8 @@ import java.util.concurrent.CopyOnWriteArraySet;
}
};
playbackInfo
=
new
ExoPlayerImplInternal
.
PlaybackInfo
(
0
,
0
);
internalPlayer
=
new
ExoPlayerImplInternal
(
renderers
,
trackSelector
,
loadControl
,
playWhenReady
,
eventHandler
,
playbackInfo
);
internalPlayer
=
new
ExoPlayerImplInternal
<>(
renderers
,
trackSelector
,
loadControl
,
playWhenReady
,
eventHandler
,
playbackInfo
);
}
@Override
...
...
@@ -100,12 +101,18 @@ import java.util.concurrent.CopyOnWriteArraySet;
@Override
public
void
prepare
(
MediaSource
mediaSource
)
{
prepare
(
mediaSource
,
true
);
prepare
(
mediaSource
,
true
,
true
);
}
@Override
public
void
prepare
(
MediaSource
mediaSource
,
boolean
resetPosition
)
{
timeline
=
null
;
public
void
prepare
(
MediaSource
mediaSource
,
boolean
resetPosition
,
boolean
resetTimeline
)
{
if
(
resetTimeline
&&
(
timeline
!=
null
||
manifest
!=
null
))
{
timeline
=
null
;
manifest
=
null
;
for
(
EventListener
listener
:
listeners
)
{
listener
.
onTimelineChanged
(
null
,
null
);
}
}
internalPlayer
.
prepare
(
mediaSource
,
resetPosition
);
}
...
...
library/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java
View file @
f4b8d949
...
...
@@ -538,16 +538,23 @@ import java.io.IOException;
periodIndex
=
C
.
INDEX_UNSET
;
}
// Clear the timeline, but keep the requested period if it is already prepared.
MediaPeriodHolder
<
T
>
periodHolder
=
playingPeriodHolder
;
MediaPeriodHolder
<
T
>
newPlayingPeriodHolder
=
null
;
while
(
periodHolder
!=
null
)
{
if
(
periodHolder
.
index
==
periodIndex
&&
periodHolder
.
prepared
)
{
newPlayingPeriodHolder
=
periodHolder
;
}
else
{
periodHolder
.
release
();
if
(
playingPeriodHolder
==
null
)
{
// We're still waiting for the first period to be prepared.
if
(
loadingPeriodHolder
!=
null
)
{
loadingPeriodHolder
.
release
();
}
}
else
{
// Clear the timeline, but keep the requested period if it is already prepared.
MediaPeriodHolder
<
T
>
periodHolder
=
playingPeriodHolder
;
while
(
periodHolder
!=
null
)
{
if
(
periodHolder
.
index
==
periodIndex
&&
periodHolder
.
prepared
)
{
newPlayingPeriodHolder
=
periodHolder
;
}
else
{
periodHolder
.
release
();
}
periodHolder
=
periodHolder
.
next
;
}
periodHolder
=
periodHolder
.
next
;
}
// Disable all the renderers if the period is changing.
...
...
@@ -892,7 +899,8 @@ import java.io.IOException;
}
// Release all loaded periods.
releasePeriodHoldersFrom
(
playingPeriodHolder
);
releasePeriodHoldersFrom
(
playingPeriodHolder
!=
null
?
playingPeriodHolder
:
loadingPeriodHolder
);
bufferAheadPeriodCount
=
0
;
playingPeriodHolder
=
null
;
readingPeriodHolder
=
null
;
...
...
library/src/main/java/com/google/android/exoplayer2/SimpleExoPlayer.java
View file @
f4b8d949
...
...
@@ -429,8 +429,8 @@ public final class SimpleExoPlayer implements ExoPlayer {
}
@Override
public
void
prepare
(
MediaSource
mediaSource
,
boolean
resetPosition
)
{
player
.
prepare
(
mediaSource
,
resetPosition
);
public
void
prepare
(
MediaSource
mediaSource
,
boolean
resetPosition
,
boolean
resetTimeline
)
{
player
.
prepare
(
mediaSource
,
resetPosition
,
resetTimeline
);
}
@Override
...
...
library/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
View file @
f4b8d949
...
...
@@ -55,6 +55,7 @@ import java.util.List;
private
static
final
int
TYPE_sbtl
=
Util
.
getIntegerCodeForString
(
"sbtl"
);
private
static
final
int
TYPE_subt
=
Util
.
getIntegerCodeForString
(
"subt"
);
private
static
final
int
TYPE_clcp
=
Util
.
getIntegerCodeForString
(
"clcp"
);
private
static
final
int
TYPE_cenc
=
Util
.
getIntegerCodeForString
(
"cenc"
);
/**
* Parses a trak atom (defined in 14496-12).
...
...
@@ -1283,7 +1284,7 @@ import java.util.List;
/**
* Parses encryption data from an audio/video sample entry, populating {@code out} and returning
* the unencrypted atom type, or 0 if no sinf atom was present.
* the unencrypted atom type, or 0 if no
common encryption
sinf atom was present.
*/
private
static
int
parseSampleEntryEncryptionData
(
ParsableByteArray
parent
,
int
position
,
int
size
,
StsdData
out
,
int
entryIndex
)
{
...
...
@@ -1296,10 +1297,10 @@ import java.util.List;
if
(
childAtomType
==
Atom
.
TYPE_sinf
)
{
Pair
<
Integer
,
TrackEncryptionBox
>
result
=
parseSinfFromParent
(
parent
,
childPosition
,
childAtomSize
);
Integer
dataFormat
=
result
.
first
;
Assertions
.
checkArgument
(
dataFormat
!=
null
,
"frma atom is mandatory"
)
;
out
.
trackEncryptionBoxes
[
entryIndex
]
=
result
.
second
;
return
dataFormat
;
if
(
result
!=
null
)
{
out
.
trackEncryptionBoxes
[
entryIndex
]
=
result
.
second
;
return
result
.
first
;
}
}
childPosition
+=
childAtomSize
;
}
...
...
@@ -1311,6 +1312,7 @@ import java.util.List;
int
position
,
int
size
)
{
int
childPosition
=
position
+
Atom
.
HEADER_SIZE
;
boolean
isCencScheme
=
false
;
TrackEncryptionBox
trackEncryptionBox
=
null
;
Integer
dataFormat
=
null
;
while
(
childPosition
-
position
<
size
)
{
...
...
@@ -1321,15 +1323,20 @@ import java.util.List;
dataFormat
=
parent
.
readInt
();
}
else
if
(
childAtomType
==
Atom
.
TYPE_schm
)
{
parent
.
skipBytes
(
4
);
parent
.
readInt
();
// schemeType. Expect cenc
parent
.
readInt
();
// schemeVersion. Expect 0x00010000
isCencScheme
=
parent
.
readInt
()
==
TYPE_cenc
;
}
else
if
(
childAtomType
==
Atom
.
TYPE_schi
)
{
trackEncryptionBox
=
parseSchiFromParent
(
parent
,
childPosition
,
childAtomSize
);
}
childPosition
+=
childAtomSize
;
}
return
Pair
.
create
(
dataFormat
,
trackEncryptionBox
);
if
(
isCencScheme
)
{
Assertions
.
checkArgument
(
dataFormat
!=
null
,
"frma atom is mandatory"
);
Assertions
.
checkArgument
(
trackEncryptionBox
!=
null
,
"schi->tenc atom is mandatory"
);
return
Pair
.
create
(
dataFormat
,
trackEncryptionBox
);
}
else
{
return
null
;
}
}
private
static
TrackEncryptionBox
parseSchiFromParent
(
ParsableByteArray
parent
,
int
position
,
...
...
library/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaPeriod.java
View file @
f4b8d949
...
...
@@ -103,8 +103,10 @@ import java.util.List;
public
void
release
()
{
continueLoadingHandler
.
removeCallbacksAndMessages
(
null
);
manifestFetcher
.
release
();
for
(
HlsSampleStreamWrapper
sampleStreamWrapper
:
sampleStreamWrappers
)
{
sampleStreamWrapper
.
release
();
if
(
sampleStreamWrappers
!=
null
)
{
for
(
HlsSampleStreamWrapper
sampleStreamWrapper
:
sampleStreamWrappers
)
{
sampleStreamWrapper
.
release
();
}
}
}
...
...
library/src/main/java/com/google/android/exoplayer2/ui/PlaybackControlView.java
View file @
f4b8d949
...
...
@@ -16,6 +16,8 @@
package
com
.
google
.
android
.
exoplayer2
.
ui
;
import
android.content.Context
;
import
android.content.res.TypedArray
;
import
android.os.SystemClock
;
import
android.util.AttributeSet
;
import
android.view.KeyEvent
;
import
android.view.LayoutInflater
;
...
...
@@ -52,7 +54,7 @@ public class PlaybackControlView extends FrameLayout {
public
static
final
int
DEFAULT_FAST_FORWARD_MS
=
15000
;
public
static
final
int
DEFAULT_REWIND_MS
=
5000
;
public
static
final
int
DEFAULT_SHOW_
DURATION
_MS
=
5000
;
public
static
final
int
DEFAULT_SHOW_
TIMEOUT
_MS
=
5000
;
private
static
final
int
PROGRESS_BAR_MAX
=
1000
;
private
static
final
long
MAX_POSITION_FOR_SEEK_TO_PREVIOUS
=
3000
;
...
...
@@ -74,9 +76,10 @@ public class PlaybackControlView extends FrameLayout {
private
VisibilityListener
visibilityListener
;
private
boolean
dragging
;
private
int
rewindMs
=
DEFAULT_REWIND_MS
;
private
int
fastForwardMs
=
DEFAULT_FAST_FORWARD_MS
;
private
int
showDurationMs
=
DEFAULT_SHOW_DURATION_MS
;
private
int
rewindMs
;
private
int
fastForwardMs
;
private
int
showTimeoutMs
;
private
long
hideAtMs
;
private
final
Runnable
updateProgressAction
=
new
Runnable
()
{
@Override
...
...
@@ -103,6 +106,22 @@ public class PlaybackControlView extends FrameLayout {
public
PlaybackControlView
(
Context
context
,
AttributeSet
attrs
,
int
defStyleAttr
)
{
super
(
context
,
attrs
,
defStyleAttr
);
rewindMs
=
DEFAULT_REWIND_MS
;
fastForwardMs
=
DEFAULT_FAST_FORWARD_MS
;
showTimeoutMs
=
DEFAULT_SHOW_TIMEOUT_MS
;
if
(
attrs
!=
null
)
{
TypedArray
a
=
context
.
getTheme
().
obtainStyledAttributes
(
attrs
,
R
.
styleable
.
PlaybackControlView
,
0
,
0
);
try
{
rewindMs
=
a
.
getInt
(
R
.
styleable
.
PlaybackControlView_rewind_increment
,
rewindMs
);
fastForwardMs
=
a
.
getInt
(
R
.
styleable
.
PlaybackControlView_fastforward_increment
,
fastForwardMs
);
showTimeoutMs
=
a
.
getInt
(
R
.
styleable
.
PlaybackControlView_show_timeout
,
showTimeoutMs
);
}
finally
{
a
.
recycle
();
}
}
currentWindow
=
new
Timeline
.
Window
();
formatBuilder
=
new
StringBuilder
();
formatter
=
new
Formatter
(
formatBuilder
,
Locale
.
getDefault
());
...
...
@@ -124,7 +143,6 @@ public class PlaybackControlView extends FrameLayout {
rewindButton
.
setOnClickListener
(
componentListener
);
fastForwardButton
=
findViewById
(
R
.
id
.
ffwd
);
fastForwardButton
.
setOnClickListener
(
componentListener
);
updateAll
();
}
/**
...
...
@@ -169,6 +187,7 @@ public class PlaybackControlView extends FrameLayout {
*/
public
void
setRewindIncrementMs
(
int
rewindMs
)
{
this
.
rewindMs
=
rewindMs
;
updateNavigation
();
}
/**
...
...
@@ -178,51 +197,60 @@ public class PlaybackControlView extends FrameLayout {
*/
public
void
setFastForwardIncrementMs
(
int
fastForwardMs
)
{
this
.
fastForwardMs
=
fastForwardMs
;
updateNavigation
();
}
/**
* Sets the duration to show the playback control in milliseconds.
* Returns the playback controls timeout. The playback controls are automatically hidden after
* this duration of time has elapsed without user input.
*
* @param showDurationMs The duration in milliseconds.
* @return The duration in milliseconds. A non-positive value indicates that the controls will
* remain visible indefinitely.
*/
public
void
setShowDurationMs
(
int
showDurationMs
)
{
this
.
showDurationMs
=
showDuration
Ms
;
public
int
getShowTimeoutMs
(
)
{
return
showTimeout
Ms
;
}
/**
* Shows the controller for the duration last passed to {@link #setShowDurationMs(int)}, or for
* {@link #DEFAULT_SHOW_DURATION_MS} if {@link #setShowDurationMs(int)} has not been called.
* Sets the playback controls timeout. The playback controls are automatically hidden after this
* duration of time has elapsed without user input.
*
* @param showTimeoutMs The duration in milliseconds. A non-positive value will cause the controls
* to remain visible indefinitely.
*/
public
void
s
how
(
)
{
show
(
showDurationMs
)
;
public
void
s
etShowTimeoutMs
(
int
showTimeoutMs
)
{
this
.
showTimeoutMs
=
showTimeoutMs
;
}
/**
* Shows the controller for the {@code durationMs}. If {@code durationMs} is 0 the controller is
* shown until {@link #hide()} is called.
*
* @param durationMs The duration in milliseconds.
* Shows the playback controls. If {@link #getShowTimeoutMs()} is positive then the controls will
* be automatically hidden after this duration of time has elapsed without user input.
*/
public
void
show
(
int
durationMs
)
{
setVisibility
(
VISIBLE
);
if
(
visibilityListener
!=
null
)
{
visibilityListener
.
onVisibilityChange
(
getVisibility
());
public
void
show
()
{
if
(!
isVisible
())
{
setVisibility
(
VISIBLE
);
if
(
visibilityListener
!=
null
)
{
visibilityListener
.
onVisibilityChange
(
getVisibility
());
}
updateAll
();
}
updateAll
();
showDurationMs
=
durationMs
;
hideDeferred
();
// Call hideAfterTimeout even if already visible to reset the timeout.
hideAfterTimeout
();
}
/**
* Hides the controller.
*/
public
void
hide
()
{
setVisibility
(
GONE
);
if
(
visibilityListener
!=
null
)
{
visibilityListener
.
onVisibilityChange
(
getVisibility
());
if
(
isVisible
())
{
setVisibility
(
GONE
);
if
(
visibilityListener
!=
null
)
{
visibilityListener
.
onVisibilityChange
(
getVisibility
());
}
removeCallbacks
(
updateProgressAction
);
removeCallbacks
(
hideAction
);
hideAtMs
=
C
.
TIME_UNSET
;
}
removeCallbacks
(
updateProgressAction
);
removeCallbacks
(
hideAction
);
}
/**
...
...
@@ -232,10 +260,15 @@ public class PlaybackControlView extends FrameLayout {
return
getVisibility
()
==
VISIBLE
;
}
private
void
hide
Deferred
()
{
private
void
hide
AfterTimeout
()
{
removeCallbacks
(
hideAction
);
if
(
showDurationMs
>
0
)
{
postDelayed
(
hideAction
,
showDurationMs
);
if
(
showTimeoutMs
>
0
)
{
hideAtMs
=
SystemClock
.
uptimeMillis
()
+
showTimeoutMs
;
if
(
isAttachedToWindow
())
{
postDelayed
(
hideAction
,
showTimeoutMs
);
}
}
else
{
hideAtMs
=
C
.
TIME_UNSET
;
}
}
...
...
@@ -246,7 +279,7 @@ public class PlaybackControlView extends FrameLayout {
}
private
void
updatePlayPauseButton
()
{
if
(!
isVisible
())
{
if
(!
isVisible
()
||
!
isAttachedToWindow
()
)
{
return
;
}
boolean
playing
=
player
!=
null
&&
player
.
getPlayWhenReady
();
...
...
@@ -258,7 +291,7 @@ public class PlaybackControlView extends FrameLayout {
}
private
void
updateNavigation
()
{
if
(!
isVisible
())
{
if
(!
isVisible
()
||
!
isAttachedToWindow
()
)
{
return
;
}
Timeline
currentTimeline
=
player
!=
null
?
player
.
getCurrentTimeline
()
:
null
;
...
...
@@ -276,13 +309,13 @@ public class PlaybackControlView extends FrameLayout {
}
setButtonEnabled
(
enablePrevious
,
previousButton
);
setButtonEnabled
(
enableNext
,
nextButton
);
setButtonEnabled
(
isSeekable
,
fastForwardButton
);
setButtonEnabled
(
isSeekable
,
rewindButton
);
setButtonEnabled
(
fastForwardMs
>
0
&&
isSeekable
,
fastForwardButton
);
setButtonEnabled
(
rewindMs
>
0
&&
isSeekable
,
rewindButton
);
progressBar
.
setEnabled
(
isSeekable
);
}
private
void
updateProgress
()
{
if
(!
isVisible
())
{
if
(!
isVisible
()
||
!
isAttachedToWindow
()
)
{
return
;
}
long
duration
=
player
==
null
?
0
:
player
.
getDuration
();
...
...
@@ -377,14 +410,41 @@ public class PlaybackControlView extends FrameLayout {
}
private
void
rewind
()
{
if
(
rewindMs
<=
0
)
{
return
;
}
player
.
seekTo
(
Math
.
max
(
player
.
getCurrentPosition
()
-
rewindMs
,
0
));
}
private
void
fastForward
()
{
if
(
fastForwardMs
<=
0
)
{
return
;
}
player
.
seekTo
(
Math
.
min
(
player
.
getCurrentPosition
()
+
fastForwardMs
,
player
.
getDuration
()));
}
@Override
public
void
onAttachedToWindow
()
{
super
.
onAttachedToWindow
();
if
(
hideAtMs
!=
C
.
TIME_UNSET
)
{
long
delayMs
=
hideAtMs
-
SystemClock
.
uptimeMillis
();
if
(
delayMs
<=
0
)
{
hide
();
}
else
{
postDelayed
(
hideAction
,
delayMs
);
}
}
updateAll
();
}
@Override
public
void
onDetachedFromWindow
()
{
super
.
onDetachedFromWindow
();
removeCallbacks
(
updateProgressAction
);
removeCallbacks
(
hideAction
);
}
@Override
public
boolean
dispatchKeyEvent
(
KeyEvent
event
)
{
if
(
player
==
null
||
event
.
getAction
()
!=
KeyEvent
.
ACTION_DOWN
)
{
return
super
.
dispatchKeyEvent
(
event
);
...
...
@@ -440,7 +500,7 @@ public class PlaybackControlView extends FrameLayout {
public
void
onStopTrackingTouch
(
SeekBar
seekBar
)
{
dragging
=
false
;
player
.
seekTo
(
positionValue
(
seekBar
.
getProgress
()));
hide
Deferred
();
hide
AfterTimeout
();
}
@Override
...
...
@@ -485,7 +545,7 @@ public class PlaybackControlView extends FrameLayout {
}
else
if
(
playButton
==
view
)
{
player
.
setPlayWhenReady
(!
player
.
getPlayWhenReady
());
}
hide
Deferred
();
hide
AfterTimeout
();
}
}
...
...
library/src/main/java/com/google/android/exoplayer2/ui/SimpleExoPlayerView.java
View file @
f4b8d949
...
...
@@ -48,8 +48,10 @@ public final class SimpleExoPlayerView extends FrameLayout {
private
final
AspectRatioFrameLayout
layout
;
private
final
PlaybackControlView
controller
;
private
final
ComponentListener
componentListener
;
private
SimpleExoPlayer
player
;
private
boolean
useController
=
true
;
private
int
controllerShowTimeoutMs
;
public
SimpleExoPlayerView
(
Context
context
)
{
this
(
context
,
null
);
...
...
@@ -64,6 +66,9 @@ public final class SimpleExoPlayerView extends FrameLayout {
boolean
useTextureView
=
false
;
int
resizeMode
=
AspectRatioFrameLayout
.
RESIZE_MODE_FIT
;
int
rewindMs
=
PlaybackControlView
.
DEFAULT_REWIND_MS
;
int
fastForwardMs
=
PlaybackControlView
.
DEFAULT_FAST_FORWARD_MS
;
int
controllerShowTimeoutMs
=
PlaybackControlView
.
DEFAULT_SHOW_TIMEOUT_MS
;
if
(
attrs
!=
null
)
{
TypedArray
a
=
context
.
getTheme
().
obtainStyledAttributes
(
attrs
,
R
.
styleable
.
SimpleExoPlayerView
,
0
,
0
);
...
...
@@ -73,6 +78,11 @@ public final class SimpleExoPlayerView extends FrameLayout {
useTextureView
);
resizeMode
=
a
.
getInt
(
R
.
styleable
.
SimpleExoPlayerView_resize_mode
,
AspectRatioFrameLayout
.
RESIZE_MODE_FIT
);
rewindMs
=
a
.
getInt
(
R
.
styleable
.
SimpleExoPlayerView_rewind_increment
,
rewindMs
);
fastForwardMs
=
a
.
getInt
(
R
.
styleable
.
SimpleExoPlayerView_fastforward_increment
,
fastForwardMs
);
controllerShowTimeoutMs
=
a
.
getInt
(
R
.
styleable
.
SimpleExoPlayerView_show_timeout
,
controllerShowTimeoutMs
);
}
finally
{
a
.
recycle
();
}
...
...
@@ -82,12 +92,17 @@ public final class SimpleExoPlayerView extends FrameLayout {
componentListener
=
new
ComponentListener
();
layout
=
(
AspectRatioFrameLayout
)
findViewById
(
R
.
id
.
video_frame
);
layout
.
setResizeMode
(
resizeMode
);
controller
=
(
PlaybackControlView
)
findViewById
(
R
.
id
.
control
);
shutterView
=
findViewById
(
R
.
id
.
shutter
);
subtitleLayout
=
(
SubtitleView
)
findViewById
(
R
.
id
.
subtitles
);
subtitleLayout
.
setUserDefaultStyle
();
subtitleLayout
.
setUserDefaultTextSize
();
controller
=
(
PlaybackControlView
)
findViewById
(
R
.
id
.
control
);
controller
.
hide
();
controller
.
setRewindIncrementMs
(
rewindMs
);
controller
.
setFastForwardIncrementMs
(
fastForwardMs
);
this
.
controllerShowTimeoutMs
=
controllerShowTimeoutMs
;
View
view
=
useTextureView
?
new
TextureView
(
context
)
:
new
SurfaceView
(
context
);
ViewGroup
.
LayoutParams
params
=
new
ViewGroup
.
LayoutParams
(
ViewGroup
.
LayoutParams
.
MATCH_PARENT
,
...
...
@@ -122,6 +137,9 @@ public final class SimpleExoPlayerView extends FrameLayout {
this
.
player
.
setVideoSurface
(
null
);
}
this
.
player
=
player
;
if
(
useController
)
{
controller
.
setPlayer
(
player
);
}
if
(
player
!=
null
)
{
if
(
surfaceView
instanceof
TextureView
)
{
player
.
setVideoTextureView
((
TextureView
)
surfaceView
);
...
...
@@ -131,20 +149,36 @@ public final class SimpleExoPlayerView extends FrameLayout {
player
.
setVideoListener
(
componentListener
);
player
.
addListener
(
componentListener
);
player
.
setTextOutput
(
componentListener
);
maybeShowController
(
false
);
}
else
{
shutterView
.
setVisibility
(
VISIBLE
);
controller
.
hide
();
}
if
(
useController
)
{
controller
.
setPlayer
(
player
);
}
}
/**
* Set the {@code useController} flag which indicates whether the playback control view should
* be used or not. If set to {@code false} the controller is never visible and is disconnected
* from the player.
* Sets the resize mode which can be of value {@link AspectRatioFrameLayout#RESIZE_MODE_FIT},
* {@link AspectRatioFrameLayout#RESIZE_MODE_FIXED_HEIGHT} or
* {@link AspectRatioFrameLayout#RESIZE_MODE_FIXED_WIDTH}.
*
* @param resizeMode The resize mode.
*/
public
void
setResizeMode
(
int
resizeMode
)
{
layout
.
setResizeMode
(
resizeMode
);
}
/**
* Returns whether the playback controls are enabled.
*/
public
boolean
getUseController
()
{
return
useController
;
}
/**
* Sets whether playback controls are enabled. If set to {@code false} the playback controls are
* never visible and are disconnected from the player.
*
* @param useController
If {@code false} the playback control is never us
ed.
* @param useController
Whether playback controls should be enabl
ed.
*/
public
void
setUseController
(
boolean
useController
)
{
if
(
this
.
useController
==
useController
)
{
...
...
@@ -160,14 +194,26 @@ public final class SimpleExoPlayerView extends FrameLayout {
}
/**
*
Sets the resize mode which can be of value {@link AspectRatioFrameLayout#RESIZE_MODE_FIT},
*
{@link AspectRatioFrameLayout#RESIZE_MODE_FIXED_HEIGHT} or
*
{@link AspectRatioFrameLayout#RESIZE_MODE_FIXED_WIDTH}
.
*
Returns the playback controls timeout. The playback controls are automatically hidden after
*
this duration of time has elapsed without user input and with playback or buffering in
*
progress
.
*
* @param resizeMode The resize mode.
* @return The timeout in milliseconds. A non-positive value will cause the controller to remain
* visible indefinitely.
*/
public
void
setResizeMode
(
int
resizeMode
)
{
layout
.
setResizeMode
(
resizeMode
);
public
int
getControllerShowTimeoutMs
()
{
return
controllerShowTimeoutMs
;
}
/**
* Sets the playback controls timeout. The playback controls are automatically hidden after this
* duration of time has elapsed without user input and with playback or buffering in progress.
*
* @param controllerShowTimeoutMs The timeout in milliseconds. A non-positive value will cause
* the controller to remain visible indefinitely.
*/
public
void
setControllerShowTimeoutMs
(
int
controllerShowTimeoutMs
)
{
this
.
controllerShowTimeoutMs
=
controllerShowTimeoutMs
;
}
/**
...
...
@@ -198,15 +244,6 @@ public final class SimpleExoPlayerView extends FrameLayout {
}
/**
* Sets the duration to show the playback control in milliseconds.
*
* @param showDurationMs The duration in milliseconds.
*/
public
void
setControlShowDurationMs
(
int
showDurationMs
)
{
controller
.
setShowDurationMs
(
showDurationMs
);
}
/**
* Get the view onto which video is rendered. This is either a {@link SurfaceView} (default)
* or a {@link TextureView} if the {@code use_texture_view} view attribute has been set to true.
*
...
...
@@ -218,21 +255,23 @@ public final class SimpleExoPlayerView extends FrameLayout {
@Override
public
boolean
onTouchEvent
(
MotionEvent
ev
)
{
if
(
useController
&&
ev
.
getActionMasked
()
==
MotionEvent
.
ACTION_DOWN
)
{
if
(
controller
.
isVisible
())
{
controller
.
hide
();
}
else
{
controller
.
show
();
}
if
(!
useController
||
player
==
null
||
ev
.
getActionMasked
()
!=
MotionEvent
.
ACTION_DOWN
)
{
return
false
;
}
if
(
controller
.
isVisible
())
{
controller
.
hide
();
}
else
{
maybeShowController
(
true
);
}
return
true
;
}
@Override
public
boolean
onTrackballEvent
(
MotionEvent
ev
)
{
if
(!
useController
)
{
if
(!
useController
||
player
==
null
)
{
return
false
;
}
controller
.
show
(
);
maybeShowController
(
true
);
return
true
;
}
...
...
@@ -241,6 +280,20 @@ public final class SimpleExoPlayerView extends FrameLayout {
return
useController
?
controller
.
dispatchKeyEvent
(
event
)
:
super
.
dispatchKeyEvent
(
event
);
}
private
void
maybeShowController
(
boolean
isForced
)
{
if
(!
useController
||
player
==
null
)
{
return
;
}
int
playbackState
=
player
.
getPlaybackState
();
boolean
showIndefinitely
=
playbackState
==
ExoPlayer
.
STATE_IDLE
||
playbackState
==
ExoPlayer
.
STATE_ENDED
||
!
player
.
getPlayWhenReady
();
boolean
wasShowingIndefinitely
=
controller
.
isVisible
()
&&
controller
.
getShowTimeoutMs
()
<=
0
;
controller
.
setShowTimeoutMs
(
showIndefinitely
?
0
:
controllerShowTimeoutMs
);
if
(
isForced
||
showIndefinitely
||
wasShowingIndefinitely
)
{
controller
.
show
();
}
}
private
final
class
ComponentListener
implements
SimpleExoPlayer
.
VideoListener
,
TextRenderer
.
Output
,
ExoPlayer
.
EventListener
{
...
...
@@ -278,9 +331,7 @@ public final class SimpleExoPlayerView extends FrameLayout {
@Override
public
void
onPlayerStateChanged
(
boolean
playWhenReady
,
int
playbackState
)
{
if
(
useController
&&
playbackState
==
ExoPlayer
.
STATE_ENDED
)
{
controller
.
show
(
0
);
}
maybeShowController
(
false
);
}
@Override
...
...
library/src/main/java/com/google/android/exoplayer2/upstream/DefaultHttpDataSource.java
View file @
f4b8d949
...
...
@@ -231,10 +231,13 @@ public class DefaultHttpDataSource implements HttpDataSource {
// Determine the length of the data to be read, after skipping.
if
((
dataSpec
.
flags
&
DataSpec
.
FLAG_ALLOW_GZIP
)
==
0
)
{
long
contentLength
=
getContentLength
(
connection
);
bytesToRead
=
dataSpec
.
length
!=
C
.
LENGTH_UNSET
?
dataSpec
.
length
:
contentLength
!=
C
.
LENGTH_UNSET
?
contentLength
-
bytesToSkip
:
C
.
LENGTH_UNSET
;
if
(
dataSpec
.
length
!=
C
.
LENGTH_UNSET
)
{
bytesToRead
=
dataSpec
.
length
;
}
else
{
long
contentLength
=
getContentLength
(
connection
);
bytesToRead
=
contentLength
!=
C
.
LENGTH_UNSET
?
(
contentLength
-
bytesToSkip
)
:
C
.
LENGTH_UNSET
;
}
}
else
{
// Gzip is enabled. If the server opts to use gzip then the content length in the response
// will be that of the compressed data, which isn't what we want. Furthermore, there isn't a
...
...
library/src/main/res/values/attrs.xml
View file @
f4b8d949
...
...
@@ -20,10 +20,16 @@
<enum
name=
"fixed_width"
value=
"1"
/>
<enum
name=
"fixed_height"
value=
"2"
/>
</attr>
<attr
name=
"show_timeout"
format=
"integer"
/>
<attr
name=
"rewind_increment"
format=
"integer"
/>
<attr
name=
"fastforward_increment"
format=
"integer"
/>
<declare-styleable
name=
"SimpleExoPlayerView"
>
<attr
name=
"use_controller"
format=
"boolean"
/>
<attr
name=
"use_texture_view"
format=
"boolean"
/>
<attr
name=
"show_timeout"
/>
<attr
name=
"rewind_increment"
/>
<attr
name=
"fastforward_increment"
/>
<attr
name=
"resize_mode"
/>
</declare-styleable>
...
...
@@ -31,4 +37,10 @@
<attr
name=
"resize_mode"
/>
</declare-styleable>
<declare-styleable
name=
"PlaybackControlView"
>
<attr
name=
"show_timeout"
/>
<attr
name=
"rewind_increment"
/>
<attr
name=
"fastforward_increment"
/>
</declare-styleable>
</resources>
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