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
6b0e1758
authored
Dec 01, 2018
by
Oliver Woodman
Committed by
GitHub
Dec 01, 2018
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #5164 from google/dev-v2-r2.9.2
r2.9.2
parents
b5beb326
a94fa330
Hide whitespace changes
Inline
Side-by-side
Showing
66 changed files
with
640 additions
and
389 deletions
CONTRIBUTING.md
RELEASENOTES.md
constants.gradle
demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/DemoUtil.java
demos/ima/src/main/res/values/strings.xml
extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java
extensions/ffmpeg/README.md
extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java
extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/LibflacAudioRenderer.java
extensions/ima/build.gradle
extensions/ima/src/main/AndroidManifest.xml
extensions/ima/src/main/proguard-rules.txt
extensions/mediasession/build.gradle
extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java
extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java
extensions/rtmp/build.gradle
extensions/rtmp/src/test/AndroidManifest.xml
extensions/rtmp/src/test/java/com/google/android/exoplayer2/ext/rtmp/DefaultDataSourceTest.java
library/core/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
library/core/src/main/java/com/google/android/exoplayer2/Timeline.java
library/core/src/main/java/com/google/android/exoplayer2/audio/AudioCapabilities.java
library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java
library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java
library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaDrm.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/BinarySearchSeeker.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsBinarySearchSeeker.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsDurationReader.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsBinarySearchSeeker.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsDurationReader.java
library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java
library/core/src/main/java/com/google/android/exoplayer2/source/ShuffleOrder.java
library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java
library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttDecoder.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/BaseTrackSelection.java
library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java
library/core/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java
library/core/src/main/java/com/google/android/exoplayer2/util/RepeatModeUtil.java
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
library/core/src/test/assets/ts/sample.ts.1.dump
library/core/src/test/assets/ts/sample.ts.2.dump
library/core/src/test/java/com/google/android/exoplayer2/DefaultLoadControlTest.java
library/core/src/test/java/com/google/android/exoplayer2/source/ShuffleOrderTest.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java
library/dash/src/main/proguard-rules.txt
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/HlsSampleStreamWrapper.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/DefaultHlsPlaylistTracker.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java
library/hls/src/main/proguard-rules.txt
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParser.java
library/smoothstreaming/src/main/proguard-rules.txt
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/SphericalSurfaceView.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/TouchTracker.java
library/ui/src/main/res/values/attrs.xml
testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeShuffleOrder.java
CONTRIBUTING.md
View file @
6b0e1758
...
...
@@ -16,9 +16,8 @@ all of the information requested in the issue template.
## Pull requests ##
We will also consider high quality pull requests. These should normally merge
into the
`dev-vX`
branch with the highest major version number. Bug fixes may
be suitable for merging into older
`dev-vX`
branches. Before a pull request can
be accepted you must submit a Contributor License Agreement, as described below.
into the
`dev-v2`
branch. Before a pull request can be accepted you must submit
a Contributor License Agreement, as described below.
[
dev
]:
https://github.com/google/ExoPlayer/tree/dev
...
...
RELEASENOTES.md
View file @
6b0e1758
# Release notes #
### 2.9.2 ###
*
HLS:
*
Fix issue causing unnecessary media playlist requests when playing live
streams (
[
#5059
](
https://github.com/google/ExoPlayer/issues/5059
)
).
*
Fix decoder re-instantiation issue for packed audio streams
(
[
#5063
](
https://github.com/google/ExoPlayer/issues/5063
)
).
*
MP4: Support Opus and FLAC in the MP4 container, and in DASH
(
[
#4883
](
https://github.com/google/ExoPlayer/issues/4883
)
).
*
DASH: Fix detecting the end of live events
(
[
#4780
](
https://github.com/google/ExoPlayer/issues/4780
)
).
*
Spherical video: Fall back to
`TYPE_ROTATION_VECTOR`
if
`TYPE_GAME_ROTATION_VECTOR`
is unavailable
(
[
#5119
](
https://github.com/google/ExoPlayer/issues/5119
)
).
*
Support seeking for a wider range of MPEG-TS streams
(
[
#5097
](
https://github.com/google/ExoPlayer/issues/5097
)
).
*
Include channel count in audio capabilities check
(
[
#4690
](
https://github.com/google/ExoPlayer/issues/4690
)
).
*
Fix issue with applying the
`show_buffering`
attribute in
`PlayerView`
(
[
#5139
](
https://github.com/google/ExoPlayer/issues/5139
)
).
*
Fix issue where null
`Metadata`
was output when it failed to decode
(
[
#5149
](
https://github.com/google/ExoPlayer/issues/5149
)
).
*
Fix playback of some invalid but playable MP4 streams by replacing assertions
with logged warnings in sample table parsing code
(
[
#5162
](
https://github.com/google/ExoPlayer/issues/5162
)
).
*
Fix UUID passed to
`MediaCrypto`
when using
`C.CLEARKEY_UUID`
before API 27.
### 2.9.1 ###
*
Add convenience methods
`Player.next`
,
`Player.previous`
,
`Player.hasNext`
...
...
@@ -20,7 +47,7 @@
*
DASH: Parse ProgramInformation element if present in the manifest.
*
HLS:
*
Add constructor to
`DefaultHlsExtractorFactory`
for adding TS payload
reader factory flags
reader factory flags
.
*
Fix bug in segment sniffing
(
[
#5039
](
https://github.com/google/ExoPlayer/issues/5039
)
).
(
[
#4861
](
https://github.com/google/ExoPlayer/issues/4861
)
).
...
...
constants.gradle
View file @
6b0e1758
...
...
@@ -13,8 +13,8 @@
// limitations under the License.
project
.
ext
{
// ExoPlayer version and version code.
releaseVersion
=
'2.9.
1
'
releaseVersionCode
=
200900
1
releaseVersion
=
'2.9.
2
'
releaseVersionCode
=
200900
2
// Important: ExoPlayer specifies a minSdkVersion of 14 because various
// components provided by the library may be of use on older devices.
// However, please note that the core media playback functionality provided
...
...
demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/DemoUtil.java
View file @
6b0e1758
...
...
@@ -81,8 +81,6 @@ import java.util.List;
+
"hls/TearsOfSteel.m3u8"
,
"Tears of Steel (HLS)"
,
MIME_TYPE_HLS
));
samples
.
add
(
new
Sample
(
"https://html5demos.com/assets/dizzy.mp4"
,
"Dizzy (MP4)"
,
MIME_TYPE_VIDEO_MP4
));
SAMPLES
=
Collections
.
unmodifiableList
(
samples
);
}
...
...
demos/ima/src/main/res/values/strings.xml
View file @
6b0e1758
...
...
@@ -17,7 +17,7 @@
<string
name=
"application_name"
>
Exo IMA Demo
</string>
<string
name=
"content_url"
>
<![CDATA[http
://rmcdn.2mdn.net/MotifFiles/html/1248596/android_1330378998288.mp4
]]>
</string>
<string
name=
"content_url"
>
<![CDATA[http
s://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv
]]>
</string>
<string
name=
"ad_tag_url"
>
<![CDATA[https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dlinear&correlator=]]>
</string>
...
...
extensions/cronet/src/main/java/com/google/android/exoplayer2/ext/cronet/CronetDataSource.java
View file @
6b0e1758
...
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
ext
.
cronet
;
import
android.net.Uri
;
import
android.support.annotation.Nullable
;
import
android.text.TextUtils
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.ExoPlayerLibraryInfo
;
...
...
@@ -455,6 +456,18 @@ public class CronetDataSource extends BaseDataSource implements HttpDataSource {
}
}
/** Returns current {@link UrlRequest}. May be null if the data source is not opened. */
@Nullable
protected
UrlRequest
getCurrentUrlRequest
()
{
return
currentUrlRequest
;
}
/** Returns current {@link UrlResponseInfo}. May be null if the data source is not opened. */
@Nullable
protected
UrlResponseInfo
getCurrentUrlResponseInfo
()
{
return
responseInfo
;
}
// Internal methods.
private
UrlRequest
.
Builder
buildRequestBuilder
(
DataSpec
dataSpec
)
throws
IOException
{
...
...
extensions/ffmpeg/README.md
View file @
6b0e1758
...
...
@@ -46,7 +46,7 @@ HOST_PLATFORM="linux-x86_64"
be supported. See the
[
Supported formats
][]
page for more details of the
available flags.
For example, to fetch and build for armeabi-v7a,
For example, to fetch and build
FFmpeg release 4.0
for armeabi-v7a,
arm64-v8a and x86 on Linux x86_64:
```
...
...
@@ -71,7 +71,7 @@ COMMON_OPTIONS="\
" && \
cd "${FFMPEG_EXT_PATH}/jni" && \
(git -C ffmpeg pull || git clone git://source.ffmpeg.org/ffmpeg ffmpeg) && \
cd ffmpeg && \
cd ffmpeg &&
git checkout release/4.0 &&
\
./configure \
--libdir=android-libs/armeabi-v7a \
--arch=arm \
...
...
extensions/ffmpeg/src/main/java/com/google/android/exoplayer2/ext/ffmpeg/FfmpegAudioRenderer.java
View file @
6b0e1758
...
...
@@ -145,12 +145,13 @@ public final class FfmpegAudioRenderer extends SimpleDecoderAudioRenderer {
}
private
boolean
isOutputSupported
(
Format
inputFormat
)
{
return
shouldUseFloatOutput
(
inputFormat
)
||
supportsOutputEncoding
(
C
.
ENCODING_PCM_16BIT
);
return
shouldUseFloatOutput
(
inputFormat
)
||
supportsOutput
(
inputFormat
.
channelCount
,
C
.
ENCODING_PCM_16BIT
);
}
private
boolean
shouldUseFloatOutput
(
Format
inputFormat
)
{
Assertions
.
checkNotNull
(
inputFormat
.
sampleMimeType
);
if
(!
enableFloatOutput
||
!
supportsOutput
Encoding
(
C
.
ENCODING_PCM_FLOAT
))
{
if
(!
enableFloatOutput
||
!
supportsOutput
(
inputFormat
.
channelCount
,
C
.
ENCODING_PCM_FLOAT
))
{
return
false
;
}
switch
(
inputFormat
.
sampleMimeType
)
{
...
...
extensions/flac/src/main/java/com/google/android/exoplayer2/ext/flac/LibflacAudioRenderer.java
View file @
6b0e1758
...
...
@@ -53,7 +53,7 @@ public class LibflacAudioRenderer extends SimpleDecoderAudioRenderer {
if
(!
FlacLibrary
.
isAvailable
()
||
!
MimeTypes
.
AUDIO_FLAC
.
equalsIgnoreCase
(
format
.
sampleMimeType
))
{
return
FORMAT_UNSUPPORTED_TYPE
;
}
else
if
(!
supportsOutput
Encoding
(
C
.
ENCODING_PCM_16BIT
))
{
}
else
if
(!
supportsOutput
(
format
.
channelCount
,
C
.
ENCODING_PCM_16BIT
))
{
return
FORMAT_UNSUPPORTED_SUBTYPE
;
}
else
if
(!
supportsFormatDrm
(
drmSessionManager
,
format
.
drmInitData
))
{
return
FORMAT_UNSUPPORTED_DRM
;
...
...
extensions/ima/build.gradle
View file @
6b0e1758
...
...
@@ -31,13 +31,13 @@ android {
}
dependencies
{
api
'com.google.ads.interactivemedia.v3:interactivemedia:3.
9.4
'
api
'com.google.ads.interactivemedia.v3:interactivemedia:3.
10.2
'
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
'com.google.android.gms:play-services-ads:1
5.0
.1'
implementation
'com.google.android.gms:play-services-ads:1
7.1
.1'
// These dependencies are necessary to force the supportLibraryVersion of
// com.android.support:support-v4 and com.android.support:customtabs to be
// used. Else older versions are used, for example via:
// com.google.android.gms:play-services-ads:1
5.0
.1
// com.google.android.gms:play-services-ads:1
7.1
.1
// |-- com.android.support:customtabs:26.1.0
implementation
'com.android.support:support-v4:'
+
supportLibraryVersion
implementation
'com.android.support:customtabs:'
+
supportLibraryVersion
...
...
extensions/ima/src/main/AndroidManifest.xml
View file @
6b0e1758
...
...
@@ -15,6 +15,10 @@
-->
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
package=
"com.google.android.exoplayer2.ext.ima"
>
<meta-data
android:name=
"com.google.android.gms.version"
android:value=
"@integer/google_play_services_version"
/>
<application>
<meta-data
android:name=
"com.google.android.gms.ads.AD_MANAGER_APP"
android:value=
"true"
/>
<meta-data
android:name=
"com.google.android.gms.version"
android:value=
"@integer/google_play_services_version"
/>
</application>
</manifest>
extensions/ima/src/main/proguard-rules.txt
deleted
100644 → 0
View file @
b5beb326
# Proguard rules specific to the IMA extension.
-keep class com.google.ads.interactivemedia.** { *; }
-keep interface com.google.ads.interactivemedia.** { *; }
-keep class com.google.obf.** { *; }
-keep interface com.google.obf.** { *; }
extensions/mediasession/build.gradle
View file @
6b0e1758
...
...
@@ -31,7 +31,7 @@ android {
dependencies
{
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
'com.android.support:support-media-compat:'
+
supportLibraryVersion
api
'com.android.support:support-media-compat:'
+
supportLibraryVersion
}
ext
{
...
...
extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java
View file @
6b0e1758
...
...
@@ -49,6 +49,11 @@ import java.util.Map;
/**
* Connects a {@link MediaSessionCompat} to a {@link Player}.
*
* <p>This connector does <em>not</em> call {@link MediaSessionCompat#setActive(boolean)}, and so
* application code is responsible for making the session active when desired. A session must be
* active for transport controls to be displayed (e.g. on the lock screen) and for it to receive
* media button events.
*
* <p>The connector listens for actions sent by the media session's controller and implements these
* actions by calling appropriate player methods. The playback state of the media session is
* automatically synced with the player. The connector can also be optionally extended by providing
...
...
extensions/opus/src/main/java/com/google/android/exoplayer2/ext/opus/LibopusAudioRenderer.java
View file @
6b0e1758
...
...
@@ -78,7 +78,7 @@ public final class LibopusAudioRenderer extends SimpleDecoderAudioRenderer {
if
(!
OpusLibrary
.
isAvailable
()
||
!
MimeTypes
.
AUDIO_OPUS
.
equalsIgnoreCase
(
format
.
sampleMimeType
))
{
return
FORMAT_UNSUPPORTED_TYPE
;
}
else
if
(!
supportsOutput
Encoding
(
C
.
ENCODING_PCM_16BIT
))
{
}
else
if
(!
supportsOutput
(
format
.
channelCount
,
C
.
ENCODING_PCM_16BIT
))
{
return
FORMAT_UNSUPPORTED_SUBTYPE
;
}
else
if
(!
supportsFormatDrm
(
drmSessionManager
,
format
.
drmInitData
))
{
return
FORMAT_UNSUPPORTED_DRM
;
...
...
extensions/rtmp/build.gradle
View file @
6b0e1758
...
...
@@ -33,6 +33,8 @@ dependencies {
implementation
project
(
modulePrefix
+
'library-core'
)
implementation
'net.butterflytv.utils:rtmp-client:3.0.1'
implementation
'com.android.support:support-annotations:'
+
supportLibraryVersion
testImplementation
'junit:junit:'
+
junitVersion
testImplementation
'org.robolectric:robolectric:'
+
robolectricVersion
}
ext
{
...
...
extensions/rtmp/src/test/AndroidManifest.xml
0 → 100644
View file @
6b0e1758
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2018 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<manifest
package=
"com.google.android.exoplayer2.ext.rtmp"
/>
extensions/rtmp/src/test/java/com/google/android/exoplayer2/ext/rtmp/DefaultDataSourceTest.java
0 → 100644
View file @
6b0e1758
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer2
.
ext
.
rtmp
;
import
android.net.Uri
;
import
com.google.android.exoplayer2.upstream.DataSpec
;
import
com.google.android.exoplayer2.upstream.DefaultDataSource
;
import
java.io.IOException
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.robolectric.RobolectricTestRunner
;
import
org.robolectric.RuntimeEnvironment
;
/** Unit test for {@link DefaultDataSource} with RTMP URIs. */
@RunWith
(
RobolectricTestRunner
.
class
)
public
final
class
DefaultDataSourceTest
{
@Test
public
void
openRtmpDataSpec_instantiatesRtmpDataSourceViaReflection
()
throws
IOException
{
DefaultDataSource
dataSource
=
new
DefaultDataSource
(
RuntimeEnvironment
.
application
,
"userAgent"
,
/* allowCrossProtocolRedirects= */
false
);
DataSpec
dataSpec
=
new
DataSpec
(
Uri
.
parse
(
"rtmp://test.com/stream"
));
try
{
dataSource
.
open
(
dataSpec
);
}
catch
(
UnsatisfiedLinkError
e
)
{
// RtmpDataSource was successfully instantiated (test run using Gradle).
}
catch
(
UnsupportedOperationException
e
)
{
// RtmpDataSource was successfully instantiated (test run using Blaze).
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/DefaultLoadControl.java
View file @
6b0e1758
...
...
@@ -79,6 +79,7 @@ public class DefaultLoadControl implements LoadControl {
private
PriorityTaskManager
priorityTaskManager
;
private
int
backBufferDurationMs
;
private
boolean
retainBackBufferFromKeyframe
;
private
boolean
createDefaultLoadControlCalled
;
/** Constructs a new instance. */
public
Builder
()
{
...
...
@@ -99,8 +100,10 @@ public class DefaultLoadControl implements LoadControl {
*
* @param allocator The {@link DefaultAllocator}.
* @return This builder, for convenience.
* @throws IllegalStateException If {@link #createDefaultLoadControl()} has already been called.
*/
public
Builder
setAllocator
(
DefaultAllocator
allocator
)
{
Assertions
.
checkState
(!
createDefaultLoadControlCalled
);
this
.
allocator
=
allocator
;
return
this
;
}
...
...
@@ -118,12 +121,14 @@ public class DefaultLoadControl implements LoadControl {
* for playback to resume after a rebuffer, in milliseconds. A rebuffer is defined to be
* caused by buffer depletion rather than a user action.
* @return This builder, for convenience.
* @throws IllegalStateException If {@link #createDefaultLoadControl()} has already been called.
*/
public
Builder
setBufferDurationsMs
(
int
minBufferMs
,
int
maxBufferMs
,
int
bufferForPlaybackMs
,
int
bufferForPlaybackAfterRebufferMs
)
{
Assertions
.
checkState
(!
createDefaultLoadControlCalled
);
this
.
minBufferMs
=
minBufferMs
;
this
.
maxBufferMs
=
maxBufferMs
;
this
.
bufferForPlaybackMs
=
bufferForPlaybackMs
;
...
...
@@ -137,8 +142,10 @@ public class DefaultLoadControl implements LoadControl {
*
* @param targetBufferBytes The target buffer size in bytes.
* @return This builder, for convenience.
* @throws IllegalStateException If {@link #createDefaultLoadControl()} has already been called.
*/
public
Builder
setTargetBufferBytes
(
int
targetBufferBytes
)
{
Assertions
.
checkState
(!
createDefaultLoadControlCalled
);
this
.
targetBufferBytes
=
targetBufferBytes
;
return
this
;
}
...
...
@@ -150,8 +157,10 @@ public class DefaultLoadControl implements LoadControl {
* @param prioritizeTimeOverSizeThresholds Whether the load control prioritizes buffer time
* constraints over buffer size constraints.
* @return This builder, for convenience.
* @throws IllegalStateException If {@link #createDefaultLoadControl()} has already been called.
*/
public
Builder
setPrioritizeTimeOverSizeThresholds
(
boolean
prioritizeTimeOverSizeThresholds
)
{
Assertions
.
checkState
(!
createDefaultLoadControlCalled
);
this
.
prioritizeTimeOverSizeThresholds
=
prioritizeTimeOverSizeThresholds
;
return
this
;
}
...
...
@@ -161,8 +170,10 @@ public class DefaultLoadControl implements LoadControl {
*
* @param priorityTaskManager The {@link PriorityTaskManager} to use.
* @return This builder, for convenience.
* @throws IllegalStateException If {@link #createDefaultLoadControl()} has already been called.
*/
public
Builder
setPriorityTaskManager
(
PriorityTaskManager
priorityTaskManager
)
{
Assertions
.
checkState
(!
createDefaultLoadControlCalled
);
this
.
priorityTaskManager
=
priorityTaskManager
;
return
this
;
}
...
...
@@ -175,8 +186,10 @@ public class DefaultLoadControl implements LoadControl {
* @param retainBackBufferFromKeyframe Whether the back buffer is retained from the previous
* keyframe.
* @return This builder, for convenience.
* @throws IllegalStateException If {@link #createDefaultLoadControl()} has already been called.
*/
public
Builder
setBackBuffer
(
int
backBufferDurationMs
,
boolean
retainBackBufferFromKeyframe
)
{
Assertions
.
checkState
(!
createDefaultLoadControlCalled
);
this
.
backBufferDurationMs
=
backBufferDurationMs
;
this
.
retainBackBufferFromKeyframe
=
retainBackBufferFromKeyframe
;
return
this
;
...
...
@@ -184,6 +197,7 @@ public class DefaultLoadControl implements LoadControl {
/** Creates a {@link DefaultLoadControl}. */
public
DefaultLoadControl
createDefaultLoadControl
()
{
createDefaultLoadControlCalled
=
true
;
if
(
allocator
==
null
)
{
allocator
=
new
DefaultAllocator
(
true
,
C
.
DEFAULT_BUFFER_SEGMENT_SIZE
);
}
...
...
@@ -371,7 +385,7 @@ public class DefaultLoadControl implements LoadControl {
}
if
(
bufferedDurationUs
<
minBufferUs
)
{
isBuffering
=
prioritizeTimeOverSizeThresholds
||
!
targetBufferSizeReached
;
}
else
if
(
bufferedDurationUs
>
maxBufferUs
||
targetBufferSizeReached
)
{
}
else
if
(
bufferedDurationUs
>
=
maxBufferUs
||
targetBufferSizeReached
)
{
isBuffering
=
false
;
}
// Else don't change the buffering state
if
(
priorityTaskManager
!=
null
&&
isBuffering
!=
wasBuffering
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
View file @
6b0e1758
...
...
@@ -29,11 +29,11 @@ public final class 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.
public
static
final
String
VERSION
=
"2.9.
1
"
;
public
static
final
String
VERSION
=
"2.9.
2
"
;
/** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public
static
final
String
VERSION_SLASHY
=
"ExoPlayerLib/2.9.
1
"
;
public
static
final
String
VERSION_SLASHY
=
"ExoPlayerLib/2.9.
2
"
;
/**
* The version of the library expressed as an integer, for example 1002003.
...
...
@@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo {
* integer version 123045006 (123-045-006).
*/
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public
static
final
int
VERSION_INT
=
200900
1
;
public
static
final
int
VERSION_INT
=
200900
2
;
/**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
...
...
library/core/src/main/java/com/google/android/exoplayer2/Timeline.java
View file @
6b0e1758
...
...
@@ -139,9 +139,12 @@ public abstract class Timeline {
*/
public
boolean
isSeekable
;
/**
* Whether this window may change when the timeline is updated.
*/
// TODO: Split this to better describe which parts of the window might change. For example it
// should be possible to individually determine whether the start and end positions of the
// window may change relative to the underlying periods. For an example of where it's useful to
// know that the end position is fixed whilst the start position may still change, see:
// https://github.com/google/ExoPlayer/issues/4780.
/** Whether this window may change when the timeline is updated. */
public
boolean
isDynamic
;
/**
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/AudioCapabilities.java
View file @
6b0e1758
...
...
@@ -29,11 +29,11 @@ import java.util.Arrays;
@TargetApi
(
21
)
public
final
class
AudioCapabilities
{
/**
* The minimum audio capabilities supported by all devices.
*/
private
static
final
int
DEFAULT_MAX_CHANNEL_COUNT
=
8
;
/** The minimum audio capabilities supported by all devices.
*/
public
static
final
AudioCapabilities
DEFAULT_AUDIO_CAPABILITIES
=
new
AudioCapabilities
(
new
int
[]
{
AudioFormat
.
ENCODING_PCM_16BIT
},
2
);
new
AudioCapabilities
(
new
int
[]
{
AudioFormat
.
ENCODING_PCM_16BIT
},
DEFAULT_MAX_CHANNEL_COUNT
);
/**
* Returns the current audio capabilities for the device.
...
...
@@ -52,8 +52,10 @@ public final class AudioCapabilities {
if
(
intent
==
null
||
intent
.
getIntExtra
(
AudioManager
.
EXTRA_AUDIO_PLUG_STATE
,
0
)
==
0
)
{
return
DEFAULT_AUDIO_CAPABILITIES
;
}
return
new
AudioCapabilities
(
intent
.
getIntArrayExtra
(
AudioManager
.
EXTRA_ENCODINGS
),
intent
.
getIntExtra
(
AudioManager
.
EXTRA_MAX_CHANNEL_COUNT
,
0
));
return
new
AudioCapabilities
(
intent
.
getIntArrayExtra
(
AudioManager
.
EXTRA_ENCODINGS
),
intent
.
getIntExtra
(
AudioManager
.
EXTRA_MAX_CHANNEL_COUNT
,
/* defaultValue= */
DEFAULT_MAX_CHANNEL_COUNT
));
}
private
final
int
[]
supportedEncodings
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/AudioSink.java
View file @
6b0e1758
...
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.audio;
import
android.media.AudioTrack
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.PlaybackParameters
;
import
java.nio.ByteBuffer
;
...
...
@@ -165,12 +166,13 @@ public interface AudioSink {
void
setListener
(
Listener
listener
);
/**
* Returns whether
it's possible to play audio in the specified encoding
.
* Returns whether
the sink supports the audio format
.
*
* @param encoding The audio encoding.
* @return Whether it's possible to play audio in the specified encoding.
* @param channelCount The number of channels, or {@link Format#NO_VALUE} if not known.
* @param encoding The audio encoding, or {@link Format#NO_VALUE} if not known.
* @return Whether the sink supports the audio format.
*/
boolean
isEncodingSupported
(
@C
.
Encoding
int
encoding
);
boolean
supportsOutput
(
int
channelCount
,
@C
.
Encoding
int
encoding
);
/**
* Returns the playback position in the stream starting at zero, in microseconds, or
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java
View file @
6b0e1758
...
...
@@ -377,14 +377,18 @@ public final class DefaultAudioSink implements AudioSink {
}
@Override
public
boolean
isEncodingSupported
(
@C
.
Encoding
int
encoding
)
{
public
boolean
supportsOutput
(
int
channelCount
,
@C
.
Encoding
int
encoding
)
{
if
(
Util
.
isEncodingLinearPcm
(
encoding
))
{
// AudioTrack supports 16-bit integer PCM output in all platform API versions, and float
// output from platform API version 21 only. Other integer PCM encodings are resampled by this
// sink to 16-bit PCM.
// sink to 16-bit PCM. We assume that the audio framework will downsample any number of
// channels to the output device's required number of channels.
return
encoding
!=
C
.
ENCODING_PCM_FLOAT
||
Util
.
SDK_INT
>=
21
;
}
else
{
return
audioCapabilities
!=
null
&&
audioCapabilities
.
supportsEncoding
(
encoding
);
return
audioCapabilities
!=
null
&&
audioCapabilities
.
supportsEncoding
(
encoding
)
&&
(
channelCount
==
Format
.
NO_VALUE
||
channelCount
<=
audioCapabilities
.
getMaxChannelCount
());
}
}
...
...
@@ -415,7 +419,7 @@ public final class DefaultAudioSink implements AudioSink {
isInputPcm
=
Util
.
isEncodingLinearPcm
(
inputEncoding
);
shouldConvertHighResIntPcmToFloat
=
enableConvertHighResIntPcmToFloat
&&
isEncodingSupported
(
C
.
ENCODING_PCM_32BIT
)
&&
supportsOutput
(
channelCount
,
C
.
ENCODING_PCM_32BIT
)
&&
Util
.
isEncodingHighResolutionIntegerPcm
(
inputEncoding
);
if
(
isInputPcm
)
{
pcmFrameSize
=
Util
.
getPcmFrameSize
(
inputEncoding
,
channelCount
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/MediaCodecAudioRenderer.java
View file @
6b0e1758
...
...
@@ -272,12 +272,14 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
}
int
tunnelingSupport
=
Util
.
SDK_INT
>=
21
?
TUNNELING_SUPPORTED
:
TUNNELING_NOT_SUPPORTED
;
boolean
supportsFormatDrm
=
supportsFormatDrm
(
drmSessionManager
,
format
.
drmInitData
);
if
(
supportsFormatDrm
&&
allowPassthrough
(
mimeType
)
if
(
supportsFormatDrm
&&
allowPassthrough
(
format
.
channelCount
,
mimeType
)
&&
mediaCodecSelector
.
getPassthroughDecoderInfo
()
!=
null
)
{
return
ADAPTIVE_NOT_SEAMLESS
|
tunnelingSupport
|
FORMAT_HANDLED
;
}
if
((
MimeTypes
.
AUDIO_RAW
.
equals
(
mimeType
)
&&
!
audioSink
.
isEncodingSupported
(
format
.
pcmEncoding
))
||
!
audioSink
.
isEncodingSupported
(
C
.
ENCODING_PCM_16BIT
))
{
if
((
MimeTypes
.
AUDIO_RAW
.
equals
(
mimeType
)
&&
!
audioSink
.
supportsOutput
(
format
.
channelCount
,
format
.
pcmEncoding
))
||
!
audioSink
.
supportsOutput
(
format
.
channelCount
,
C
.
ENCODING_PCM_16BIT
))
{
// Assume the decoder outputs 16-bit PCM, unless the input is raw.
return
FORMAT_UNSUPPORTED_SUBTYPE
;
}
...
...
@@ -316,7 +318,7 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
protected
List
<
MediaCodecInfo
>
getDecoderInfos
(
MediaCodecSelector
mediaCodecSelector
,
Format
format
,
boolean
requiresSecureDecoder
)
throws
DecoderQueryException
{
if
(
allowPassthrough
(
format
.
sampleMimeType
))
{
if
(
allowPassthrough
(
format
.
channelCount
,
format
.
sampleMimeType
))
{
MediaCodecInfo
passthroughDecoderInfo
=
mediaCodecSelector
.
getPassthroughDecoderInfo
();
if
(
passthroughDecoderInfo
!=
null
)
{
return
Collections
.
singletonList
(
passthroughDecoderInfo
);
...
...
@@ -330,12 +332,13 @@ public class MediaCodecAudioRenderer extends MediaCodecRenderer implements Media
* This implementation returns true if the {@link AudioSink} indicates that encoded audio output
* is supported.
*
* @param channelCount The number of channels in the input media, or {@link Format#NO_VALUE} if
* not known.
* @param mimeType The type of input media.
* @return Whether passthrough playback is supported.
*/
protected
boolean
allowPassthrough
(
String
mimeType
)
{
@C
.
Encoding
int
encoding
=
MimeTypes
.
getEncoding
(
mimeType
);
return
encoding
!=
C
.
ENCODING_INVALID
&&
audioSink
.
isEncodingSupported
(
encoding
);
protected
boolean
allowPassthrough
(
int
channelCount
,
String
mimeType
)
{
return
audioSink
.
supportsOutput
(
channelCount
,
MimeTypes
.
getEncoding
(
mimeType
));
}
@Override
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/SimpleDecoderAudioRenderer.java
View file @
6b0e1758
...
...
@@ -249,13 +249,12 @@ public abstract class SimpleDecoderAudioRenderer extends BaseRenderer implements
DrmSessionManager
<
ExoMediaCrypto
>
drmSessionManager
,
Format
format
);
/**
* Returns whether the
audio sink can accept audio in the specified encoding
.
* Returns whether the
sink supports the audio format
.
*
* @param encoding The audio encoding.
* @return Whether the audio sink can accept audio in the specified encoding.
* @see AudioSink#supportsOutput(int, int)
*/
protected
final
boolean
supportsOutput
Encoding
(
@C
.
Encoding
int
encoding
)
{
return
audioSink
.
isEncodingSupported
(
encoding
);
protected
final
boolean
supportsOutput
(
int
channelCount
,
@C
.
Encoding
int
encoding
)
{
return
audioSink
.
supportsOutput
(
channelCount
,
encoding
);
}
@Override
...
...
library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaDrm.java
View file @
6b0e1758
...
...
@@ -70,9 +70,7 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
Assertions
.
checkNotNull
(
uuid
);
Assertions
.
checkArgument
(!
C
.
COMMON_PSSH_UUID
.
equals
(
uuid
),
"Use C.CLEARKEY_UUID instead"
);
this
.
uuid
=
uuid
;
// ClearKey had to be accessed using the Common PSSH UUID prior to API level 27.
this
.
mediaDrm
=
new
MediaDrm
(
Util
.
SDK_INT
<
27
&&
C
.
CLEARKEY_UUID
.
equals
(
uuid
)
?
C
.
COMMON_PSSH_UUID
:
uuid
);
this
.
mediaDrm
=
new
MediaDrm
(
adjustUuid
(
uuid
));
if
(
C
.
WIDEVINE_UUID
.
equals
(
uuid
)
&&
needsForceWidevineL3Workaround
())
{
forceWidevineL3
(
mediaDrm
);
}
...
...
@@ -152,7 +150,6 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
@Override
public
byte
[]
provideKeyResponse
(
byte
[]
scope
,
byte
[]
response
)
throws
NotProvisionedException
,
DeniedByServerException
{
if
(
C
.
CLEARKEY_UUID
.
equals
(
uuid
))
{
response
=
ClearKeyUtil
.
adjustResponseData
(
response
);
}
...
...
@@ -212,8 +209,8 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
// indicate that it required secure video decoders [Internal ref: b/11428937].
boolean
forceAllowInsecureDecoderComponents
=
Util
.
SDK_INT
<
21
&&
C
.
WIDEVINE_UUID
.
equals
(
uuid
)
&&
"L3"
.
equals
(
getPropertyString
(
"securityLevel"
));
return
new
FrameworkMediaCrypto
(
new
MediaCrypto
(
uuid
,
initData
),
forceAllowInsecureDecoderComponents
);
return
new
FrameworkMediaCrypto
(
new
MediaCrypto
(
adjustUuid
(
uuid
),
initData
),
forceAllowInsecureDecoderComponents
);
}
private
static
SchemeData
getSchemeData
(
UUID
uuid
,
List
<
SchemeData
>
schemeDatas
)
{
...
...
@@ -269,6 +266,11 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
return
schemeDatas
.
get
(
0
);
}
private
static
UUID
adjustUuid
(
UUID
uuid
)
{
// ClearKey had to be accessed using the Common PSSH UUID prior to API level 27.
return
Util
.
SDK_INT
<
27
&&
C
.
CLEARKEY_UUID
.
equals
(
uuid
)
?
C
.
COMMON_PSSH_UUID
:
uuid
;
}
private
static
byte
[]
adjustRequestInitData
(
UUID
uuid
,
byte
[]
initData
)
{
// Prior to L the Widevine CDM required data to be extracted from the PSSH atom. Some Amazon
// devices also required data to be extracted from the PSSH atom for PlayReady.
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/BinarySearchSeeker.java
View file @
6b0e1758
...
...
@@ -42,37 +42,25 @@ public abstract class BinarySearchSeeker {
protected
interface
TimestampSeeker
{
/**
* Searches for a given timestamp from the input.
* Searches a limited window of the provided input for a target timestamp. The size of the
* window is implementation specific, but should be small enough such that it's reasonable for
* multiple such reads to occur during a seek operation.
*
* <p>Given a target timestamp and an input stream, this seeker will try to read up to a range
* of {@code searchRangeBytes} bytes from that input, look for all available timestamps from all
* frames in that range, compare those with the target timestamp, and return one of the {@link
* TimestampSearchResult}.
*
* @param input The {@link ExtractorInput} from which data should be read.
* @param targetTimestamp The target timestamp that we are looking for.
* @param outputFrameHolder If {@link TimestampSearchResult#RESULT_TARGET_TIMESTAMP_FOUND} is
* @param input The {@link ExtractorInput} from which data should be peeked.
* @param targetTimestamp The target timestamp.
* @param outputFrameHolder If {@link TimestampSearchResult#TYPE_TARGET_TIMESTAMP_FOUND} is
* returned, this holder may be updated to hold the extracted frame that contains the target
* frame/sample associated with the target timestamp.
* @return A {@link TimestampSearchResult}, that includes a {@link TimestampSearchResult#result}
* value, and other necessary info:
* <ul>
* <li>{@link TimestampSearchResult#RESULT_NO_TIMESTAMP} is returned if there is no
* timestamp in the reading range.
* <li>{@link TimestampSearchResult#RESULT_POSITION_UNDERESTIMATED} is returned if all
* timestamps in the range are smaller than the target timestamp.
* <li>{@link TimestampSearchResult#RESULT_POSITION_OVERESTIMATED} is returned if all
* timestamps in the range are larger than the target timestamp.
* <li>{@link TimestampSearchResult#RESULT_TARGET_TIMESTAMP_FOUND} is returned if this
* seeker can find a timestamp that it deems close enough to the given target.
* </ul>
*
* @return A {@link TimestampSearchResult} that describes the result of the search.
* @throws IOException If an error occurred reading from the input.
* @throws InterruptedException If the thread was interrupted.
*/
TimestampSearchResult
searchForTimestamp
(
ExtractorInput
input
,
long
targetTimestamp
,
OutputFrameHolder
outputFrameHolder
)
throws
IOException
,
InterruptedException
;
/** Called when a seek operation finishes. */
default
void
onSeekFinished
()
{}
}
/**
...
...
@@ -231,22 +219,22 @@ public abstract class BinarySearchSeeker {
timestampSeeker
.
searchForTimestamp
(
input
,
seekOperationParams
.
getTargetTimePosition
(),
outputFrameHolder
);
switch
(
timestampSearchResult
.
result
)
{
case
TimestampSearchResult
.
RESULT
_POSITION_OVERESTIMATED
:
switch
(
timestampSearchResult
.
type
)
{
case
TimestampSearchResult
.
TYPE
_POSITION_OVERESTIMATED
:
seekOperationParams
.
updateSeekCeiling
(
timestampSearchResult
.
timestampToUpdate
,
timestampSearchResult
.
bytePositionToUpdate
);
break
;
case
TimestampSearchResult
.
RESULT
_POSITION_UNDERESTIMATED
:
case
TimestampSearchResult
.
TYPE
_POSITION_UNDERESTIMATED
:
seekOperationParams
.
updateSeekFloor
(
timestampSearchResult
.
timestampToUpdate
,
timestampSearchResult
.
bytePositionToUpdate
);
break
;
case
TimestampSearchResult
.
RESULT
_TARGET_TIMESTAMP_FOUND
:
case
TimestampSearchResult
.
TYPE
_TARGET_TIMESTAMP_FOUND
:
markSeekOperationFinished
(
/* foundTargetFrame= */
true
,
timestampSearchResult
.
bytePositionToUpdate
);
skipInputUntilPosition
(
input
,
timestampSearchResult
.
bytePositionToUpdate
);
return
seekToPosition
(
input
,
timestampSearchResult
.
bytePositionToUpdate
,
seekPositionHolder
);
case
TimestampSearchResult
.
RESULT
_NO_TIMESTAMP
:
case
TimestampSearchResult
.
TYPE
_NO_TIMESTAMP
:
// We can't find any timestamp in the search range from the search position.
// Give up, and just continue reading from the last search position in this case.
markSeekOperationFinished
(
/* foundTargetFrame= */
false
,
searchPosition
);
...
...
@@ -270,6 +258,7 @@ public abstract class BinarySearchSeeker {
protected
final
void
markSeekOperationFinished
(
boolean
foundTargetFrame
,
long
resultPosition
)
{
seekOperationParams
=
null
;
timestampSeeker
.
onSeekFinished
();
onSeekOperationFinished
(
foundTargetFrame
,
resultPosition
);
}
...
...
@@ -433,45 +422,49 @@ public abstract class BinarySearchSeeker {
*/
public
static
final
class
TimestampSearchResult
{
public
static
final
int
RESULT_TARGET_TIMESTAMP_FOUND
=
0
;
public
static
final
int
RESULT_POSITION_OVERESTIMATED
=
-
1
;
public
static
final
int
RESULT_POSITION_UNDERESTIMATED
=
-
2
;
public
static
final
int
RESULT_NO_TIMESTAMP
=
-
3
;
/** The search found a timestamp that it deems close enough to the given target. */
public
static
final
int
TYPE_TARGET_TIMESTAMP_FOUND
=
0
;
/** The search found only timestamps larger than the target timestamp. */
public
static
final
int
TYPE_POSITION_OVERESTIMATED
=
-
1
;
/** The search found only timestamps smaller than the target timestamp. */
public
static
final
int
TYPE_POSITION_UNDERESTIMATED
=
-
2
;
/** The search didn't find any timestamps. */
public
static
final
int
TYPE_NO_TIMESTAMP
=
-
3
;
@Documented
@Retention
(
RetentionPolicy
.
SOURCE
)
@IntDef
({
RESULT
_TARGET_TIMESTAMP_FOUND
,
RESULT
_POSITION_OVERESTIMATED
,
RESULT
_POSITION_UNDERESTIMATED
,
RESULT
_NO_TIMESTAMP
TYPE
_TARGET_TIMESTAMP_FOUND
,
TYPE
_POSITION_OVERESTIMATED
,
TYPE
_POSITION_UNDERESTIMATED
,
TYPE
_NO_TIMESTAMP
})
@interface
SearchResult
{}
@interface
Type
{}
public
static
final
TimestampSearchResult
NO_TIMESTAMP_IN_RANGE_RESULT
=
new
TimestampSearchResult
(
RESULT
_NO_TIMESTAMP
,
C
.
TIME_UNSET
,
C
.
POSITION_UNSET
);
new
TimestampSearchResult
(
TYPE
_NO_TIMESTAMP
,
C
.
TIME_UNSET
,
C
.
POSITION_UNSET
);
/**
@see TimestampSeeker
*/
private
final
@SearchResult
int
result
;
/**
The type of the result.
*/
@Type
private
final
int
type
;
/**
* When {@
code result} is {@link #RESULT
_POSITION_OVERESTIMATED}, the {@link
* SeekOperationParams#ceilingTimePosition} should be updated with this value. When {@
code
*
result} is {@link #RESULT
_POSITION_UNDERESTIMATED}, the {@link
* When {@
link #type} is {@link #TYPE
_POSITION_OVERESTIMATED}, the {@link
* SeekOperationParams#ceilingTimePosition} should be updated with this value. When {@
link
*
#type} is {@link #TYPE
_POSITION_UNDERESTIMATED}, the {@link
* SeekOperationParams#floorTimePosition} should be updated with this value.
*/
private
final
long
timestampToUpdate
;
/**
* When {@
code result} is {@link #RESULT
_POSITION_OVERESTIMATED}, the {@link
* SeekOperationParams#ceilingBytePosition} should be updated with this value. When {@
code
*
result} is {@link #RESULT
_POSITION_UNDERESTIMATED}, the {@link
* When {@
link #type} is {@link #TYPE
_POSITION_OVERESTIMATED}, the {@link
* SeekOperationParams#ceilingBytePosition} should be updated with this value. When {@
link
*
#type} is {@link #TYPE
_POSITION_UNDERESTIMATED}, the {@link
* SeekOperationParams#floorBytePosition} should be updated with this value.
*/
private
final
long
bytePositionToUpdate
;
private
TimestampSearchResult
(
@
SearchResult
int
result
,
long
timestampToUpdate
,
long
bytePositionToUpdate
)
{
this
.
result
=
result
;
@
Type
int
type
,
long
timestampToUpdate
,
long
bytePositionToUpdate
)
{
this
.
type
=
type
;
this
.
timestampToUpdate
=
timestampToUpdate
;
this
.
bytePositionToUpdate
=
bytePositionToUpdate
;
}
...
...
@@ -484,7 +477,7 @@ public abstract class BinarySearchSeeker {
public
static
TimestampSearchResult
overestimatedResult
(
long
newCeilingTimestamp
,
long
newCeilingBytePosition
)
{
return
new
TimestampSearchResult
(
RESULT
_POSITION_OVERESTIMATED
,
newCeilingTimestamp
,
newCeilingBytePosition
);
TYPE
_POSITION_OVERESTIMATED
,
newCeilingTimestamp
,
newCeilingBytePosition
);
}
/**
...
...
@@ -495,11 +488,11 @@ public abstract class BinarySearchSeeker {
public
static
TimestampSearchResult
underestimatedResult
(
long
newFloorTimestamp
,
long
newCeilingBytePosition
)
{
return
new
TimestampSearchResult
(
RESULT
_POSITION_UNDERESTIMATED
,
newFloorTimestamp
,
newCeilingBytePosition
);
TYPE
_POSITION_UNDERESTIMATED
,
newFloorTimestamp
,
newCeilingBytePosition
);
}
/**
* Returns a result to signal that the target timestamp has been found at
the
{@code
* Returns a result to signal that the target timestamp has been found at {@code
* resultBytePosition}, and the seek operation can stop.
*
* <p>Note that when this value is returned from {@link
...
...
@@ -508,7 +501,7 @@ public abstract class BinarySearchSeeker {
*/
public
static
TimestampSearchResult
targetFoundResult
(
long
resultBytePosition
)
{
return
new
TimestampSearchResult
(
RESULT
_TARGET_TIMESTAMP_FOUND
,
C
.
TIME_UNSET
,
resultBytePosition
);
TYPE
_TARGET_TIMESTAMP_FOUND
,
C
.
TIME_UNSET
,
resultBytePosition
);
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Atom.java
View file @
6b0e1758
...
...
@@ -145,6 +145,10 @@ import java.util.List;
public
static
final
int
TYPE_alac
=
Util
.
getIntegerCodeForString
(
"alac"
);
public
static
final
int
TYPE_alaw
=
Util
.
getIntegerCodeForString
(
"alaw"
);
public
static
final
int
TYPE_ulaw
=
Util
.
getIntegerCodeForString
(
"ulaw"
);
public
static
final
int
TYPE_Opus
=
Util
.
getIntegerCodeForString
(
"Opus"
);
public
static
final
int
TYPE_dOps
=
Util
.
getIntegerCodeForString
(
"dOps"
);
public
static
final
int
TYPE_fLaC
=
Util
.
getIntegerCodeForString
(
"fLaC"
);
public
static
final
int
TYPE_dfLa
=
Util
.
getIntegerCodeForString
(
"dfLa"
);
public
final
int
type
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
View file @
6b0e1758
...
...
@@ -58,6 +58,9 @@ import java.util.List;
*/
private
static
final
int
MAX_GAPLESS_TRIM_SIZE_SAMPLES
=
3
;
/** The magic signature for an Opus Identification header, as defined in RFC-7845. */
private
static
final
byte
[]
opusMagic
=
Util
.
getUtf8Bytes
(
"OpusHead"
);
/**
* Parses a trak atom (defined in 14496-12).
*
...
...
@@ -233,8 +236,6 @@ import java.util.List;
sizes
=
Arrays
.
copyOf
(
sizes
,
sampleCount
);
timestamps
=
Arrays
.
copyOf
(
timestamps
,
sampleCount
);
flags
=
Arrays
.
copyOf
(
flags
,
sampleCount
);
remainingSamplesAtTimestampOffset
=
0
;
remainingTimestampOffsetChanges
=
0
;
break
;
}
...
...
@@ -290,23 +291,38 @@ import java.util.List;
}
duration
=
timestampTimeUnits
+
timestampOffset
;
Assertions
.
checkArgument
(
remainingSamplesAtTimestampOffset
==
0
);
// Remove trailing ctts entries with 0-valued sample counts.
// If the stbl's child boxes are not consistent the container is malformed, but the stream may
// still be playable.
boolean
isCttsValid
=
true
;
while
(
remainingTimestampOffsetChanges
>
0
)
{
Assertions
.
checkArgument
(
ctts
.
readUnsignedIntToInt
()
==
0
);
if
(
ctts
.
readUnsignedIntToInt
()
!=
0
)
{
isCttsValid
=
false
;
break
;
}
ctts
.
readInt
();
// Ignore offset.
remainingTimestampOffsetChanges
--;
}
// If the stbl's child boxes are not consistent the container is malformed, but the stream may
// still be playable.
if
(
remainingSynchronizationSamples
!=
0
||
remainingSamplesAtTimestampDelta
!=
0
||
remainingSamplesInChunk
!=
0
||
remainingTimestampDeltaChanges
!=
0
)
{
Log
.
w
(
TAG
,
"Inconsistent stbl box for track "
+
track
.
id
+
": remainingSynchronizationSamples "
+
remainingSynchronizationSamples
+
", remainingSamplesAtTimestampDelta "
+
remainingSamplesAtTimestampDelta
+
", remainingSamplesInChunk "
+
remainingSamplesInChunk
+
", remainingTimestampDeltaChanges "
+
remainingTimestampDeltaChanges
);
if
(
remainingSynchronizationSamples
!=
0
||
remainingSamplesAtTimestampDelta
!=
0
||
remainingSamplesInChunk
!=
0
||
remainingTimestampDeltaChanges
!=
0
||
remainingSamplesAtTimestampOffset
!=
0
||
!
isCttsValid
)
{
Log
.
w
(
TAG
,
"Inconsistent stbl box for track "
+
track
.
id
+
": remainingSynchronizationSamples "
+
remainingSynchronizationSamples
+
", remainingSamplesAtTimestampDelta "
+
remainingSamplesAtTimestampDelta
+
", remainingSamplesInChunk "
+
remainingSamplesInChunk
+
", remainingTimestampDeltaChanges "
+
remainingTimestampDeltaChanges
+
", remainingSamplesAtTimestampOffset "
+
remainingSamplesAtTimestampOffset
+
(!
isCttsValid
?
", ctts invalid"
:
""
));
}
}
else
{
long
[]
chunkOffsetsBytes
=
new
long
[
chunkIterator
.
length
];
...
...
@@ -679,7 +695,9 @@ import java.util.List;
||
childAtomType
==
Atom
.
TYPE__mp3
||
childAtomType
==
Atom
.
TYPE_alac
||
childAtomType
==
Atom
.
TYPE_alaw
||
childAtomType
==
Atom
.
TYPE_ulaw
)
{
||
childAtomType
==
Atom
.
TYPE_ulaw
||
childAtomType
==
Atom
.
TYPE_Opus
||
childAtomType
==
Atom
.
TYPE_fLaC
)
{
parseAudioSampleEntry
(
stsd
,
childAtomType
,
childStartPosition
,
childAtomSize
,
trackId
,
language
,
isQuickTime
,
drmInitData
,
out
,
i
);
}
else
if
(
childAtomType
==
Atom
.
TYPE_TTML
||
childAtomType
==
Atom
.
TYPE_tx3g
...
...
@@ -976,6 +994,10 @@ import java.util.List;
mimeType
=
MimeTypes
.
AUDIO_ALAW
;
}
else
if
(
atomType
==
Atom
.
TYPE_ulaw
)
{
mimeType
=
MimeTypes
.
AUDIO_MLAW
;
}
else
if
(
atomType
==
Atom
.
TYPE_Opus
)
{
mimeType
=
MimeTypes
.
AUDIO_OPUS
;
}
else
if
(
atomType
==
Atom
.
TYPE_fLaC
)
{
mimeType
=
MimeTypes
.
AUDIO_FLAC
;
}
byte
[]
initializationData
=
null
;
...
...
@@ -1016,7 +1038,20 @@ import java.util.List;
}
else
if
(
childAtomType
==
Atom
.
TYPE_alac
)
{
initializationData
=
new
byte
[
childAtomSize
];
parent
.
setPosition
(
childPosition
);
parent
.
readBytes
(
initializationData
,
0
,
childAtomSize
);
parent
.
readBytes
(
initializationData
,
/* offset= */
0
,
childAtomSize
);
}
else
if
(
childAtomType
==
Atom
.
TYPE_dOps
)
{
// Build an Opus Identification Header (defined in RFC-7845) by concatenating the Opus Magic
// Signature and the body of the dOps atom.
int
childAtomBodySize
=
childAtomSize
-
Atom
.
HEADER_SIZE
;
initializationData
=
new
byte
[
opusMagic
.
length
+
childAtomBodySize
];
System
.
arraycopy
(
opusMagic
,
0
,
initializationData
,
0
,
opusMagic
.
length
);
parent
.
setPosition
(
childPosition
+
Atom
.
HEADER_SIZE
);
parent
.
readBytes
(
initializationData
,
opusMagic
.
length
,
childAtomBodySize
);
}
else
if
(
childAtomSize
==
Atom
.
TYPE_dfLa
)
{
int
childAtomBodySize
=
childAtomSize
-
Atom
.
FULL_HEADER_SIZE
;
initializationData
=
new
byte
[
childAtomBodySize
];
parent
.
setPosition
(
childPosition
+
Atom
.
FULL_HEADER_SIZE
);
parent
.
readBytes
(
initializationData
,
/* offset= */
0
,
childAtomBodySize
);
}
childPosition
+=
childAtomSize
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsBinarySearchSeeker.java
View file @
6b0e1758
...
...
@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.extractor.BinarySearchSeeker;
import
com.google.android.exoplayer2.extractor.ExtractorInput
;
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
;
/**
...
...
@@ -53,10 +54,9 @@ import java.io.IOException;
/**
* A seeker that looks for a given SCR timestamp at a given position in a PS stream.
*
* <p>Given a SCR timestamp, and a position within a PS stream, this seeker will try to read a
* range of up to {@link #TIMESTAMP_SEARCH_BYTES} bytes from that stream position, look for all
* packs in that range, and then compare the SCR timestamps (if available) of these packets vs the
* target timestamp.
* <p>Given a SCR timestamp, and a position within a PS stream, this seeker will peek up to {@link
* #TIMESTAMP_SEARCH_BYTES} bytes from that stream position, look for all packs in that range, and
* then compare the SCR timestamps (if available) of these packets to the target timestamp.
*/
private
static
final
class
PsScrSeeker
implements
TimestampSeeker
{
...
...
@@ -65,7 +65,7 @@ import java.io.IOException;
private
PsScrSeeker
(
TimestampAdjuster
scrTimestampAdjuster
)
{
this
.
scrTimestampAdjuster
=
scrTimestampAdjuster
;
packetBuffer
=
new
ParsableByteArray
(
TIMESTAMP_SEARCH_BYTES
);
packetBuffer
=
new
ParsableByteArray
();
}
@Override
...
...
@@ -73,14 +73,19 @@ import java.io.IOException;
ExtractorInput
input
,
long
targetTimestamp
,
OutputFrameHolder
outputFrameHolder
)
throws
IOException
,
InterruptedException
{
long
inputPosition
=
input
.
getPosition
();
int
bytesTo
Read
=
(
int
)
Math
.
min
(
TIMESTAMP_SEARCH_BYTES
,
input
.
getLength
()
-
input
.
getPosition
());
packetBuffer
.
reset
(
bytesTo
Read
);
input
.
peekFully
(
packetBuffer
.
data
,
/* offset= */
0
,
bytesTo
Read
);
int
bytesTo
Search
=
(
int
)
Math
.
min
(
TIMESTAMP_SEARCH_BYTES
,
input
.
getLength
()
-
inputPosition
);
packetBuffer
.
reset
(
bytesTo
Search
);
input
.
peekFully
(
packetBuffer
.
data
,
/* offset= */
0
,
bytesTo
Search
);
return
searchForScrValueInBuffer
(
packetBuffer
,
targetTimestamp
,
inputPosition
);
}
@Override
public
void
onSeekFinished
()
{
packetBuffer
.
reset
(
Util
.
EMPTY_BYTE_ARRAY
);
}
private
TimestampSearchResult
searchForScrValueInBuffer
(
ParsableByteArray
packetBuffer
,
long
targetScrTimeUs
,
long
bufferStartOffset
)
{
int
startOfLastPacketPosition
=
C
.
POSITION_UNSET
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsDurationReader.java
View file @
6b0e1758
...
...
@@ -21,6 +21,7 @@ import com.google.android.exoplayer2.extractor.ExtractorInput;
import
com.google.android.exoplayer2.extractor.PositionHolder
;
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
;
/**
...
...
@@ -38,7 +39,7 @@ import java.io.IOException;
*/
/* package */
final
class
PsDurationReader
{
private
static
final
int
DURATION_READ
_BYTES
=
20000
;
private
static
final
int
TIMESTAMP_SEARCH
_BYTES
=
20000
;
private
final
TimestampAdjuster
scrTimestampAdjuster
;
private
final
ParsableByteArray
packetBuffer
;
...
...
@@ -56,7 +57,7 @@ import java.io.IOException;
firstScrValue
=
C
.
TIME_UNSET
;
lastScrValue
=
C
.
TIME_UNSET
;
durationUs
=
C
.
TIME_UNSET
;
packetBuffer
=
new
ParsableByteArray
(
DURATION_READ_BYTES
);
packetBuffer
=
new
ParsableByteArray
();
}
/** Returns true if a PS duration has been read. */
...
...
@@ -129,6 +130,7 @@ import java.io.IOException;
}
private
int
finishReadDuration
(
ExtractorInput
input
)
{
packetBuffer
.
reset
(
Util
.
EMPTY_BYTE_ARRAY
);
isDurationRead
=
true
;
input
.
resetPeekPosition
();
return
Extractor
.
RESULT_CONTINUE
;
...
...
@@ -136,16 +138,16 @@ import java.io.IOException;
private
int
readFirstScrValue
(
ExtractorInput
input
,
PositionHolder
seekPositionHolder
)
throws
IOException
,
InterruptedException
{
if
(
input
.
getPosition
()
!=
0
)
{
seekPositionHolder
.
position
=
0
;
int
bytesToSearch
=
(
int
)
Math
.
min
(
TIMESTAMP_SEARCH_BYTES
,
input
.
getLength
());
int
searchStartPosition
=
0
;
if
(
input
.
getPosition
()
!=
searchStartPosition
)
{
seekPositionHolder
.
position
=
searchStartPosition
;
return
Extractor
.
RESULT_SEEK
;
}
int
bytesToRead
=
(
int
)
Math
.
min
(
DURATION_READ_BYTES
,
input
.
getLength
()
);
packetBuffer
.
reset
(
bytesToSearch
);
input
.
resetPeekPosition
();
input
.
peekFully
(
packetBuffer
.
data
,
/* offset= */
0
,
bytesToRead
);
packetBuffer
.
setPosition
(
0
);
packetBuffer
.
setLimit
(
bytesToRead
);
input
.
peekFully
(
packetBuffer
.
data
,
/* offset= */
0
,
bytesToSearch
);
firstScrValue
=
readFirstScrValueFromBuffer
(
packetBuffer
);
isFirstScrValueRead
=
true
;
...
...
@@ -172,17 +174,17 @@ import java.io.IOException;
private
int
readLastScrValue
(
ExtractorInput
input
,
PositionHolder
seekPositionHolder
)
throws
IOException
,
InterruptedException
{
int
bytesToRead
=
(
int
)
Math
.
min
(
DURATION_READ_BYTES
,
input
.
getLength
());
long
bufferStartStreamPosition
=
input
.
getLength
()
-
bytesToRead
;
if
(
input
.
getPosition
()
!=
bufferStartStreamPosition
)
{
seekPositionHolder
.
position
=
bufferStartStreamPosition
;
long
inputLength
=
input
.
getLength
();
int
bytesToSearch
=
(
int
)
Math
.
min
(
TIMESTAMP_SEARCH_BYTES
,
inputLength
);
long
searchStartPosition
=
inputLength
-
bytesToSearch
;
if
(
input
.
getPosition
()
!=
searchStartPosition
)
{
seekPositionHolder
.
position
=
searchStartPosition
;
return
Extractor
.
RESULT_SEEK
;
}
packetBuffer
.
reset
(
bytesToSearch
);
input
.
resetPeekPosition
();
input
.
peekFully
(
packetBuffer
.
data
,
/* offset= */
0
,
bytesToRead
);
packetBuffer
.
setPosition
(
0
);
packetBuffer
.
setLimit
(
bytesToRead
);
input
.
peekFully
(
packetBuffer
.
data
,
/* offset= */
0
,
bytesToSearch
);
lastScrValue
=
readLastScrValueFromBuffer
(
packetBuffer
);
isLastScrValueRead
=
true
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsBinarySearchSeeker.java
View file @
6b0e1758
...
...
@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.extractor.BinarySearchSeeker;
import
com.google.android.exoplayer2.extractor.ExtractorInput
;
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
;
/**
...
...
@@ -33,10 +34,8 @@ import java.io.IOException;
/* package */
final
class
TsBinarySearchSeeker
extends
BinarySearchSeeker
{
private
static
final
long
SEEK_TOLERANCE_US
=
100_000
;
private
static
final
int
MINIMUM_SEARCH_RANGE_BYTES
=
TsExtractor
.
TS_PACKET_SIZE
*
5
;
private
static
final
int
TIMESTAMP_SEARCH_PACKETS
=
200
;
private
static
final
int
TIMESTAMP_SEARCH_BYTES
=
TsExtractor
.
TS_PACKET_SIZE
*
TIMESTAMP_SEARCH_PACKETS
;
private
static
final
int
MINIMUM_SEARCH_RANGE_BYTES
=
5
*
TsExtractor
.
TS_PACKET_SIZE
;
private
static
final
int
TIMESTAMP_SEARCH_BYTES
=
600
*
TsExtractor
.
TS_PACKET_SIZE
;
public
TsBinarySearchSeeker
(
TimestampAdjuster
pcrTimestampAdjuster
,
long
streamDurationUs
,
long
inputLength
,
int
pcrPid
)
{
...
...
@@ -56,10 +55,10 @@ import java.io.IOException;
* A {@link TimestampSeeker} implementation that looks for a given PCR timestamp at a given
* position in a TS stream.
*
* <p>Given a PCR timestamp, and a position within a TS stream, this seeker will
try to read up to
*
{@link #TIMESTAMP_SEARCH_PACKETS} TS packets from that stream position, look for all packet
*
with PID equals to PCR_PID, and then compare the PCR timestamps (if available) of these packets
*
vs the target
timestamp.
* <p>Given a PCR timestamp, and a position within a TS stream, this seeker will
peek up to {@link
*
#TIMESTAMP_SEARCH_BYTES} from that stream position, look for all packets with PID equal to
*
PCR_PID, and then compare the PCR timestamps (if available) of these packets to the target
* timestamp.
*/
private
static
final
class
TsPcrSeeker
implements
TimestampSeeker
{
...
...
@@ -70,7 +69,7 @@ import java.io.IOException;
public
TsPcrSeeker
(
int
pcrPid
,
TimestampAdjuster
pcrTimestampAdjuster
)
{
this
.
pcrPid
=
pcrPid
;
this
.
pcrTimestampAdjuster
=
pcrTimestampAdjuster
;
packetBuffer
=
new
ParsableByteArray
(
TIMESTAMP_SEARCH_BYTES
);
packetBuffer
=
new
ParsableByteArray
();
}
@Override
...
...
@@ -78,10 +77,10 @@ import java.io.IOException;
ExtractorInput
input
,
long
targetTimestamp
,
OutputFrameHolder
outputFrameHolder
)
throws
IOException
,
InterruptedException
{
long
inputPosition
=
input
.
getPosition
();
int
bytesTo
Read
=
(
int
)
Math
.
min
(
TIMESTAMP_SEARCH_BYTES
,
input
.
getLength
()
-
input
.
getPosition
());
packetBuffer
.
reset
(
bytesTo
Read
);
input
.
peekFully
(
packetBuffer
.
data
,
/* offset= */
0
,
bytesTo
Read
);
int
bytesTo
Search
=
(
int
)
Math
.
min
(
TIMESTAMP_SEARCH_BYTES
,
input
.
getLength
()
-
inputPosition
);
packetBuffer
.
reset
(
bytesTo
Search
);
input
.
peekFully
(
packetBuffer
.
data
,
/* offset= */
0
,
bytesTo
Search
);
return
searchForPcrValueInBuffer
(
packetBuffer
,
targetTimestamp
,
inputPosition
);
}
...
...
@@ -133,5 +132,10 @@ import java.io.IOException;
return
TimestampSearchResult
.
NO_TIMESTAMP_IN_RANGE_RESULT
;
}
}
@Override
public
void
onSeekFinished
()
{
packetBuffer
.
reset
(
Util
.
EMPTY_BYTE_ARRAY
);
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/TsDurationReader.java
View file @
6b0e1758
...
...
@@ -21,6 +21,7 @@ import com.google.android.exoplayer2.extractor.ExtractorInput;
import
com.google.android.exoplayer2.extractor.PositionHolder
;
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
;
/**
...
...
@@ -35,8 +36,7 @@ import java.io.IOException;
*/
/* package */
final
class
TsDurationReader
{
private
static
final
int
DURATION_READ_PACKETS
=
200
;
private
static
final
int
DURATION_READ_BYTES
=
TsExtractor
.
TS_PACKET_SIZE
*
DURATION_READ_PACKETS
;
private
static
final
int
TIMESTAMP_SEARCH_BYTES
=
600
*
TsExtractor
.
TS_PACKET_SIZE
;
private
final
TimestampAdjuster
pcrTimestampAdjuster
;
private
final
ParsableByteArray
packetBuffer
;
...
...
@@ -54,7 +54,7 @@ import java.io.IOException;
firstPcrValue
=
C
.
TIME_UNSET
;
lastPcrValue
=
C
.
TIME_UNSET
;
durationUs
=
C
.
TIME_UNSET
;
packetBuffer
=
new
ParsableByteArray
(
DURATION_READ_BYTES
);
packetBuffer
=
new
ParsableByteArray
();
}
/** Returns true if a TS duration has been read. */
...
...
@@ -117,6 +117,7 @@ import java.io.IOException;
}
private
int
finishReadDuration
(
ExtractorInput
input
)
{
packetBuffer
.
reset
(
Util
.
EMPTY_BYTE_ARRAY
);
isDurationRead
=
true
;
input
.
resetPeekPosition
();
return
Extractor
.
RESULT_CONTINUE
;
...
...
@@ -124,16 +125,16 @@ import java.io.IOException;
private
int
readFirstPcrValue
(
ExtractorInput
input
,
PositionHolder
seekPositionHolder
,
int
pcrPid
)
throws
IOException
,
InterruptedException
{
if
(
input
.
getPosition
()
!=
0
)
{
seekPositionHolder
.
position
=
0
;
int
bytesToSearch
=
(
int
)
Math
.
min
(
TIMESTAMP_SEARCH_BYTES
,
input
.
getLength
());
int
searchStartPosition
=
0
;
if
(
input
.
getPosition
()
!=
searchStartPosition
)
{
seekPositionHolder
.
position
=
searchStartPosition
;
return
Extractor
.
RESULT_SEEK
;
}
int
bytesToRead
=
(
int
)
Math
.
min
(
DURATION_READ_BYTES
,
input
.
getLength
()
);
packetBuffer
.
reset
(
bytesToSearch
);
input
.
resetPeekPosition
();
input
.
peekFully
(
packetBuffer
.
data
,
/* offset= */
0
,
bytesToRead
);
packetBuffer
.
setPosition
(
0
);
packetBuffer
.
setLimit
(
bytesToRead
);
input
.
peekFully
(
packetBuffer
.
data
,
/* offset= */
0
,
bytesToSearch
);
firstPcrValue
=
readFirstPcrValueFromBuffer
(
packetBuffer
,
pcrPid
);
isFirstPcrValueRead
=
true
;
...
...
@@ -159,17 +160,17 @@ import java.io.IOException;
private
int
readLastPcrValue
(
ExtractorInput
input
,
PositionHolder
seekPositionHolder
,
int
pcrPid
)
throws
IOException
,
InterruptedException
{
int
bytesToRead
=
(
int
)
Math
.
min
(
DURATION_READ_BYTES
,
input
.
getLength
());
long
bufferStartStreamPosition
=
input
.
getLength
()
-
bytesToRead
;
if
(
input
.
getPosition
()
!=
bufferStartStreamPosition
)
{
seekPositionHolder
.
position
=
bufferStartStreamPosition
;
long
inputLength
=
input
.
getLength
();
int
bytesToSearch
=
(
int
)
Math
.
min
(
TIMESTAMP_SEARCH_BYTES
,
inputLength
);
long
searchStartPosition
=
inputLength
-
bytesToSearch
;
if
(
input
.
getPosition
()
!=
searchStartPosition
)
{
seekPositionHolder
.
position
=
searchStartPosition
;
return
Extractor
.
RESULT_SEEK
;
}
packetBuffer
.
reset
(
bytesToSearch
);
input
.
resetPeekPosition
();
input
.
peekFully
(
packetBuffer
.
data
,
/* offset= */
0
,
bytesToRead
);
packetBuffer
.
setPosition
(
0
);
packetBuffer
.
setLimit
(
bytesToRead
);
input
.
peekFully
(
packetBuffer
.
data
,
/* offset= */
0
,
bytesToSearch
);
lastPcrValue
=
readLastPcrValueFromBuffer
(
packetBuffer
,
pcrPid
);
isLastPcrValueRead
=
true
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/metadata/MetadataRenderer.java
View file @
6b0e1758
...
...
@@ -129,9 +129,12 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
buffer
.
subsampleOffsetUs
=
formatHolder
.
format
.
subsampleOffsetUs
;
buffer
.
flip
();
int
index
=
(
pendingMetadataIndex
+
pendingMetadataCount
)
%
MAX_PENDING_METADATA_COUNT
;
pendingMetadata
[
index
]
=
decoder
.
decode
(
buffer
);
pendingMetadataTimestamps
[
index
]
=
buffer
.
timeUs
;
pendingMetadataCount
++;
Metadata
metadata
=
decoder
.
decode
(
buffer
);
if
(
metadata
!=
null
)
{
pendingMetadata
[
index
]
=
metadata
;
pendingMetadataTimestamps
[
index
]
=
buffer
.
timeUs
;
pendingMetadataCount
++;
}
}
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/ConcatenatingMediaSource.java
View file @
6b0e1758
...
...
@@ -365,6 +365,9 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
public
final
synchronized
void
moveMediaSource
(
int
currentIndex
,
int
newIndex
,
@Nullable
Runnable
actionOnCompletion
)
{
if
(
currentIndex
==
newIndex
)
{
if
(
actionOnCompletion
!=
null
)
{
actionOnCompletion
.
run
();
}
return
;
}
mediaSourcesPublic
.
add
(
newIndex
,
mediaSourcesPublic
.
remove
(
currentIndex
));
...
...
@@ -570,9 +573,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
if
(
fromIndex
==
0
&&
toIndex
==
shuffleOrder
.
getLength
())
{
shuffleOrder
=
shuffleOrder
.
cloneAndClear
();
}
else
{
for
(
int
index
=
toIndex
-
1
;
index
>=
fromIndex
;
index
--)
{
shuffleOrder
=
shuffleOrder
.
cloneAndRemove
(
index
);
}
shuffleOrder
=
shuffleOrder
.
cloneAndRemove
(
fromIndex
,
toIndex
);
}
for
(
int
index
=
toIndex
-
1
;
index
>=
fromIndex
;
index
--)
{
removeMediaSourceInternal
(
index
);
...
...
@@ -581,7 +582,7 @@ public class ConcatenatingMediaSource extends CompositeMediaSource<MediaSourceHo
break
;
case
MSG_MOVE:
MessageData
<
Integer
>
moveMessage
=
(
MessageData
<
Integer
>)
Util
.
castNonNull
(
message
);
shuffleOrder
=
shuffleOrder
.
cloneAndRemove
(
moveMessage
.
index
);
shuffleOrder
=
shuffleOrder
.
cloneAndRemove
(
moveMessage
.
index
,
moveMessage
.
index
+
1
);
shuffleOrder
=
shuffleOrder
.
cloneAndInsert
(
moveMessage
.
customData
,
1
);
moveMediaSourceInternal
(
moveMessage
.
index
,
moveMessage
.
customData
);
scheduleListenerNotification
(
moveMessage
.
actionOnCompletion
);
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/SampleQueue.java
View file @
6b0e1758
...
...
@@ -30,10 +30,8 @@ import java.io.EOFException;
import
java.io.IOException
;
import
java.nio.ByteBuffer
;
/**
* A queue of media samples.
*/
public
final
class
SampleQueue
implements
TrackOutput
{
/** A queue of media samples. */
public
class
SampleQueue
implements
TrackOutput
{
/**
* A listener for changes to the upstream format.
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/ShuffleOrder.java
View file @
6b0e1758
...
...
@@ -55,6 +55,17 @@ public interface ShuffleOrder {
this
(
length
,
new
Random
(
randomSeed
));
}
/**
* Creates an instance with a specified shuffle order and the specified random seed. The random
* seed is used for {@link #cloneAndInsert(int, int)} invocations.
*
* @param shuffledIndices The shuffled indices to use as order.
* @param randomSeed A random seed.
*/
public
DefaultShuffleOrder
(
int
[]
shuffledIndices
,
long
randomSeed
)
{
this
(
Arrays
.
copyOf
(
shuffledIndices
,
shuffledIndices
.
length
),
new
Random
(
randomSeed
));
}
private
DefaultShuffleOrder
(
int
length
,
Random
random
)
{
this
(
createShuffledList
(
length
,
random
),
random
);
}
...
...
@@ -124,15 +135,16 @@ public interface ShuffleOrder {
}
@Override
public
ShuffleOrder
cloneAndRemove
(
int
removalIndex
)
{
int
[]
newShuffled
=
new
int
[
shuffled
.
length
-
1
];
boolean
foundRemovedElement
=
false
;
public
ShuffleOrder
cloneAndRemove
(
int
indexFrom
,
int
indexToExclusive
)
{
int
numberOfElementsToRemove
=
indexToExclusive
-
indexFrom
;
int
[]
newShuffled
=
new
int
[
shuffled
.
length
-
numberOfElementsToRemove
];
int
foundElementsCount
=
0
;
for
(
int
i
=
0
;
i
<
shuffled
.
length
;
i
++)
{
if
(
shuffled
[
i
]
==
removalIndex
)
{
found
RemovedElement
=
true
;
if
(
shuffled
[
i
]
>=
indexFrom
&&
shuffled
[
i
]
<
indexToExclusive
)
{
found
ElementsCount
++
;
}
else
{
newShuffled
[
foundRemovedElement
?
i
-
1
:
i
]
=
shuffled
[
i
]
>
removalIndex
?
shuffled
[
i
]
-
1
:
shuffled
[
i
];
newShuffled
[
i
-
foundElementsCount
]
=
shuffled
[
i
]
>=
indexFrom
?
shuffled
[
i
]
-
numberOfElementsToRemove
:
shuffled
[
i
];
}
}
return
new
DefaultShuffleOrder
(
newShuffled
,
new
Random
(
random
.
nextLong
()));
...
...
@@ -202,8 +214,8 @@ public interface ShuffleOrder {
}
@Override
public
ShuffleOrder
cloneAndRemove
(
int
removalIndex
)
{
return
new
UnshuffledShuffleOrder
(
length
-
1
);
public
ShuffleOrder
cloneAndRemove
(
int
indexFrom
,
int
indexToExclusive
)
{
return
new
UnshuffledShuffleOrder
(
length
-
indexToExclusive
+
indexFrom
);
}
@Override
...
...
@@ -257,12 +269,14 @@ public interface ShuffleOrder {
ShuffleOrder
cloneAndInsert
(
int
insertionIndex
,
int
insertionCount
);
/**
* Returns a copy of the shuffle order with
one element
removed.
* Returns a copy of the shuffle order with
a range of elements
removed.
*
* @param removalIndex The index of the element in the unshuffled order which is to be removed.
* @return A copy of this {@link ShuffleOrder} without the removed element.
* @param indexFrom The starting index in the unshuffled order of the range to remove.
* @param indexToExclusive The smallest index (must be greater or equal to {@code indexFrom}) that
* will not be removed.
* @return A copy of this {@link ShuffleOrder} without the elements in the removed range.
*/
ShuffleOrder
cloneAndRemove
(
int
removalIndex
);
ShuffleOrder
cloneAndRemove
(
int
indexFrom
,
int
indexToExclusive
);
/** Returns a copy of the shuffle order with all elements removed. */
ShuffleOrder
cloneAndClear
();
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java
View file @
6b0e1758
...
...
@@ -33,6 +33,7 @@ import com.google.android.exoplayer2.text.SubtitleInputBuffer;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
java.util.ArrayList
;
import
java.util.Collections
;
import
java.util.List
;
/**
...
...
@@ -451,7 +452,7 @@ public final class Cea608Decoder extends CeaDecoder {
switch
(
cc2
)
{
case
CTRL_ERASE_DISPLAYED_MEMORY:
cues
=
null
;
cues
=
Collections
.
emptyList
()
;
if
(
captionMode
==
CC_MODE_ROLL_UP
||
captionMode
==
CC_MODE_PAINT_ON
)
{
resetCueBuilders
();
}
...
...
@@ -506,7 +507,7 @@ public final class Cea608Decoder extends CeaDecoder {
if
(
oldCaptionMode
==
CC_MODE_PAINT_ON
||
captionMode
==
CC_MODE_ROLL_UP
||
captionMode
==
CC_MODE_UNKNOWN
)
{
// When switching from paint-on or to roll-up or unknown, we also need to clear the caption.
cues
=
null
;
cues
=
Collections
.
emptyList
()
;
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/webvtt/WebvttDecoder.java
View file @
6b0e1758
...
...
@@ -110,7 +110,7 @@ public final class WebvttDecoder extends SimpleSubtitleDecoder {
foundEvent
=
EVENT_END_OF_FILE
;
}
else
if
(
STYLE_START
.
equals
(
line
))
{
foundEvent
=
EVENT_STYLE_BLOCK
;
}
else
if
(
COMMENT_START
.
startsWith
(
line
))
{
}
else
if
(
line
.
startsWith
(
COMMENT_START
))
{
foundEvent
=
EVENT_COMMENT
;
}
else
{
foundEvent
=
EVENT_CUE
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/BaseTrackSelection.java
View file @
6b0e1758
...
...
@@ -22,6 +22,7 @@ import com.google.android.exoplayer2.Format;
import
com.google.android.exoplayer2.source.TrackGroup
;
import
com.google.android.exoplayer2.source.chunk.MediaChunk
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Util
;
import
java.util.Arrays
;
import
java.util.Comparator
;
import
java.util.List
;
...
...
@@ -160,7 +161,10 @@ public abstract class BaseTrackSelection implements TrackSelection {
if
(!
canBlacklist
)
{
return
false
;
}
blacklistUntilTimes
[
index
]
=
Math
.
max
(
blacklistUntilTimes
[
index
],
nowMs
+
blacklistDurationMs
);
blacklistUntilTimes
[
index
]
=
Math
.
max
(
blacklistUntilTimes
[
index
],
Util
.
addWithOverflowDefault
(
nowMs
,
blacklistDurationMs
,
Long
.
MAX_VALUE
));
return
true
;
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java
View file @
6b0e1758
...
...
@@ -207,7 +207,7 @@ public final class MimeTypes {
if
(
codec
==
null
)
{
return
null
;
}
codec
=
codec
.
trim
(
);
codec
=
Util
.
toLowerInvariant
(
codec
.
trim
()
);
if
(
codec
.
startsWith
(
"avc1"
)
||
codec
.
startsWith
(
"avc3"
))
{
return
MimeTypes
.
VIDEO_H264
;
}
else
if
(
codec
.
startsWith
(
"hev1"
)
||
codec
.
startsWith
(
"hvc1"
))
{
...
...
@@ -245,6 +245,8 @@ public final class MimeTypes {
return
MimeTypes
.
AUDIO_OPUS
;
}
else
if
(
codec
.
startsWith
(
"vorbis"
))
{
return
MimeTypes
.
AUDIO_VORBIS
;
}
else
if
(
codec
.
startsWith
(
"flac"
))
{
return
MimeTypes
.
AUDIO_FLAC
;
}
else
{
return
getCustomMimeTypeForCodec
(
codec
);
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/ParsableByteArray.java
View file @
6b0e1758
...
...
@@ -67,6 +67,12 @@ public final class ParsableByteArray {
this
.
limit
=
limit
;
}
/** Sets the position and limit to zero. */
public
void
reset
()
{
position
=
0
;
limit
=
0
;
}
/**
* Resets the position to zero and the limit to the specified value. If the limit exceeds the
* capacity, {@code data} is replaced with a new array of sufficient size.
...
...
@@ -78,6 +84,16 @@ public final class ParsableByteArray {
}
/**
* Updates the instance to wrap {@code data}, and resets the position to zero and the limit to
* {@code data.length}.
*
* @param data The array to wrap.
*/
public
void
reset
(
byte
[]
data
)
{
reset
(
data
,
data
.
length
);
}
/**
* Updates the instance to wrap {@code data}, and resets the position to zero.
*
* @param data The array to wrap.
...
...
@@ -90,14 +106,6 @@ public final class ParsableByteArray {
}
/**
* Sets the position and limit to zero.
*/
public
void
reset
()
{
position
=
0
;
limit
=
0
;
}
/**
* Returns the number of bytes yet to be read.
*/
public
int
bytesLeft
()
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/RepeatModeUtil.java
View file @
6b0e1758
...
...
@@ -26,6 +26,7 @@ import java.lang.annotation.RetentionPolicy;
*/
public
final
class
RepeatModeUtil
{
// LINT.IfChange
/**
* Set of repeat toggle modes. Can be combined using bit-wise operations. Possible flag values are
* {@link #REPEAT_TOGGLE_MODE_NONE}, {@link #REPEAT_TOGGLE_MODE_ONE} and {@link
...
...
@@ -47,6 +48,7 @@ public final class RepeatModeUtil {
public
static
final
int
REPEAT_TOGGLE_MODE_ONE
=
1
;
/** "Repeat All" button enabled. */
public
static
final
int
REPEAT_TOGGLE_MODE_ALL
=
1
<<
1
;
// 2
// LINT.ThenChange(../../../../../../../../../ui/src/main/res/values/attrs.xml)
private
RepeatModeUtil
()
{
// Prevent instantiation.
...
...
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
View file @
6b0e1758
...
...
@@ -1329,6 +1329,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
case
"A7010a48"
:
case
"A7020a48"
:
case
"AquaPowerM"
:
case
"ASUS_X00AD_2"
:
case
"Aura_Note_2"
:
case
"BLACK-1X"
:
case
"BRAVIA_ATV2"
:
...
...
@@ -1369,6 +1370,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
case
"HWBLN-H"
:
case
"HWCAM-H"
:
case
"HWVNS-H"
:
case
"i9031"
:
case
"iball8735_9806"
:
case
"Infinix-X572"
:
case
"iris60"
:
...
...
@@ -1376,6 +1378,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
case
"j2xlteins"
:
case
"JGZ"
:
case
"K50a40"
:
case
"kate"
:
case
"le_x6"
:
case
"LS-5017"
:
case
"M5c"
:
...
...
library/core/src/test/assets/ts/sample.ts.1.dump
View file @
6b0e1758
...
...
@@ -26,10 +26,14 @@ track 256:
drmInitData = -
initializationData:
data = length 22, hash CE183139
total output bytes =
24315
sample count =
1
total output bytes =
45026
sample count =
2
sample 0:
time = 55611
time = 55610
flags = 1
data = length 20711, hash 34341E8
sample 1:
time = 88977
flags = 0
data = length 18112, hash EC44B35B
track 257:
...
...
@@ -57,19 +61,19 @@ track 257:
total output bytes = 5015
sample count = 4
sample 0:
time =
11333
time =
44699
flags = 1
data = length 1253, hash 727FD1C6
sample 1:
time =
37455
time =
70821
flags = 1
data = length 1254, hash 73FB07B8
sample 2:
time =
63578
time =
96944
flags = 1
data = length 1254, hash 73FB07B8
sample 3:
time =
89700
time =
123066
flags = 1
data = length 1254, hash 73FB07B8
track 8448:
...
...
library/core/src/test/assets/ts/sample.ts.2.dump
View file @
6b0e1758
...
...
@@ -26,10 +26,14 @@ track 256:
drmInitData = -
initializationData:
data = length 22, hash CE183139
total output bytes =
24315
sample count =
1
total output bytes =
45026
sample count =
2
sample 0:
time = 77855
time = 77854
flags = 1
data = length 20711, hash 34341E8
sample 1:
time = 111221
flags = 0
data = length 18112, hash EC44B35B
track 257:
...
...
@@ -57,19 +61,19 @@ track 257:
total output bytes = 5015
sample count = 4
sample 0:
time =
33577
time =
66943
flags = 1
data = length 1253, hash 727FD1C6
sample 1:
time =
59699
time =
93065
flags = 1
data = length 1254, hash 73FB07B8
sample 2:
time =
85822
time =
119188
flags = 1
data = length 1254, hash 73FB07B8
sample 3:
time = 1
11944
time = 1
45310
flags = 1
data = length 1254, hash 73FB07B8
track 8448:
...
...
library/core/src/test/java/com/google/android/exoplayer2/DefaultLoadControlTest.java
View file @
6b0e1758
...
...
@@ -48,15 +48,15 @@ public class DefaultLoadControlTest {
createDefaultLoadControl
();
assertThat
(
loadControl
.
shouldContinueLoading
(
/* bufferedDurationUs= */
0
,
SPEED
)).
isTrue
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MIN_BUFFER_US
,
SPEED
)).
isTrue
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MAX_BUFFER_US
,
SPEED
)).
isTrue
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MAX_BUFFER_US
+
1
,
SPEED
)).
isFalse
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MAX_BUFFER_US
-
1
,
SPEED
)).
isTrue
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MAX_BUFFER_US
,
SPEED
)).
isFalse
();
}
@Test
public
void
testShouldNotContinueLoadingOnceBufferingStopped_untilBelowMinBuffer
()
{
createDefaultLoadControl
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MAX_BUFFER_US
+
1
,
SPEED
)).
isFalse
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MAX_BUFFER_US
,
SPEED
)).
isFalse
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MAX_BUFFER_US
-
1
,
SPEED
)).
isFalse
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MIN_BUFFER_US
,
SPEED
)).
isFalse
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MIN_BUFFER_US
-
1
,
SPEED
)).
isTrue
();
}
...
...
@@ -69,7 +69,7 @@ public class DefaultLoadControlTest {
assertThat
(
loadControl
.
shouldContinueLoading
(
/* bufferedDurationUs= */
0
,
SPEED
)).
isTrue
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MIN_BUFFER_US
-
1
,
SPEED
)).
isTrue
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MIN_BUFFER_US
,
SPEED
)).
isFalse
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MAX_BUFFER_US
+
1
,
SPEED
)).
isFalse
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MAX_BUFFER_US
,
SPEED
)).
isFalse
();
}
@Test
...
...
@@ -82,7 +82,7 @@ public class DefaultLoadControlTest {
assertThat
(
loadControl
.
shouldContinueLoading
(
/* bufferedDurationUs= */
0
,
SPEED
)).
isFalse
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MIN_BUFFER_US
,
SPEED
)).
isFalse
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MAX_BUFFER_US
+
1
,
SPEED
)).
isFalse
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MAX_BUFFER_US
,
SPEED
)).
isFalse
();
}
@Test
...
...
@@ -100,7 +100,7 @@ public class DefaultLoadControlTest {
public
void
testShouldNotContinueLoadingWithMaxBufferReached_inFastPlayback
()
{
createDefaultLoadControl
();
assertThat
(
loadControl
.
shouldContinueLoading
(
MAX_BUFFER_US
+
1
,
/* playbackSpeed= */
100
f
))
assertThat
(
loadControl
.
shouldContinueLoading
(
MAX_BUFFER_US
,
/* playbackSpeed= */
100
f
))
.
isFalse
();
}
...
...
library/core/src/test/java/com/google/android/exoplayer2/source/ShuffleOrderTest.java
View file @
6b0e1758
...
...
@@ -45,10 +45,32 @@ public final class ShuffleOrderTest {
testCloneAndInsert
(
new
DefaultShuffleOrder
(
initialLength
,
RANDOM_SEED
),
insertionPoint
,
5
);
}
}
testCloneAndRemove
(
new
DefaultShuffleOrder
(
5
,
RANDOM_SEED
),
0
);
testCloneAndRemove
(
new
DefaultShuffleOrder
(
5
,
RANDOM_SEED
),
2
);
testCloneAndRemove
(
new
DefaultShuffleOrder
(
5
,
RANDOM_SEED
),
4
);
testCloneAndRemove
(
new
DefaultShuffleOrder
(
1
,
RANDOM_SEED
),
0
);
testCloneAndRemove
(
new
DefaultShuffleOrder
(
5
,
RANDOM_SEED
),
0
,
1
);
testCloneAndRemove
(
new
DefaultShuffleOrder
(
5
,
RANDOM_SEED
),
2
,
3
);
testCloneAndRemove
(
new
DefaultShuffleOrder
(
5
,
RANDOM_SEED
),
4
,
5
);
testCloneAndRemove
(
new
DefaultShuffleOrder
(
1
,
RANDOM_SEED
),
0
,
1
);
testCloneAndRemove
(
new
DefaultShuffleOrder
(
1000
,
RANDOM_SEED
),
0
,
1000
);
testCloneAndRemove
(
new
DefaultShuffleOrder
(
1000
,
RANDOM_SEED
),
0
,
999
);
testCloneAndRemove
(
new
DefaultShuffleOrder
(
1000
,
RANDOM_SEED
),
0
,
500
);
testCloneAndRemove
(
new
DefaultShuffleOrder
(
1000
,
RANDOM_SEED
),
100
,
600
);
testCloneAndRemove
(
new
DefaultShuffleOrder
(
1000
,
RANDOM_SEED
),
500
,
1000
);
}
@Test
public
void
testDefaultShuffleOrderSideloaded
()
{
int
[]
shuffledIndices
=
new
int
[]
{
2
,
1
,
0
,
4
,
3
};
ShuffleOrder
shuffleOrder
=
new
DefaultShuffleOrder
(
shuffledIndices
,
RANDOM_SEED
);
assertThat
(
shuffleOrder
.
getFirstIndex
()).
isEqualTo
(
2
);
assertThat
(
shuffleOrder
.
getLastIndex
()).
isEqualTo
(
3
);
for
(
int
i
=
0
;
i
<
4
;
i
++)
{
assertThat
(
shuffleOrder
.
getNextIndex
(
shuffledIndices
[
i
])).
isEqualTo
(
shuffledIndices
[
i
+
1
]);
}
assertThat
(
shuffleOrder
.
getNextIndex
(
3
)).
isEqualTo
(
C
.
INDEX_UNSET
);
for
(
int
i
=
4
;
i
>
0
;
i
--)
{
assertThat
(
shuffleOrder
.
getPreviousIndex
(
shuffledIndices
[
i
]))
.
isEqualTo
(
shuffledIndices
[
i
-
1
]);
}
assertThat
(
shuffleOrder
.
getPreviousIndex
(
2
)).
isEqualTo
(
C
.
INDEX_UNSET
);
}
@Test
...
...
@@ -63,10 +85,15 @@ public final class ShuffleOrderTest {
testCloneAndInsert
(
new
UnshuffledShuffleOrder
(
initialLength
),
insertionPoint
,
5
);
}
}
testCloneAndRemove
(
new
UnshuffledShuffleOrder
(
5
),
0
);
testCloneAndRemove
(
new
UnshuffledShuffleOrder
(
5
),
2
);
testCloneAndRemove
(
new
UnshuffledShuffleOrder
(
5
),
4
);
testCloneAndRemove
(
new
UnshuffledShuffleOrder
(
1
),
0
);
testCloneAndRemove
(
new
UnshuffledShuffleOrder
(
5
),
0
,
1
);
testCloneAndRemove
(
new
UnshuffledShuffleOrder
(
5
),
2
,
3
);
testCloneAndRemove
(
new
UnshuffledShuffleOrder
(
5
),
4
,
5
);
testCloneAndRemove
(
new
UnshuffledShuffleOrder
(
1
),
0
,
1
);
testCloneAndRemove
(
new
UnshuffledShuffleOrder
(
1000
),
0
,
1000
);
testCloneAndRemove
(
new
UnshuffledShuffleOrder
(
1000
),
0
,
999
);
testCloneAndRemove
(
new
UnshuffledShuffleOrder
(
1000
),
0
,
500
);
testCloneAndRemove
(
new
UnshuffledShuffleOrder
(
1000
),
100
,
600
);
testCloneAndRemove
(
new
UnshuffledShuffleOrder
(
1000
),
500
,
1000
);
}
@Test
...
...
@@ -120,22 +147,24 @@ public final class ShuffleOrderTest {
}
}
private
static
void
testCloneAndRemove
(
ShuffleOrder
shuffleOrder
,
int
position
)
{
ShuffleOrder
newOrder
=
shuffleOrder
.
cloneAndRemove
(
position
);
assertShuffleOrderCorrectness
(
newOrder
,
shuffleOrder
.
getLength
()
-
1
);
private
static
void
testCloneAndRemove
(
ShuffleOrder
shuffleOrder
,
int
indexFrom
,
int
indexToExclusive
)
{
int
numberOfElementsToRemove
=
indexToExclusive
-
indexFrom
;
ShuffleOrder
newOrder
=
shuffleOrder
.
cloneAndRemove
(
indexFrom
,
indexToExclusive
);
assertShuffleOrderCorrectness
(
newOrder
,
shuffleOrder
.
getLength
()
-
numberOfElementsToRemove
);
// Assert all elements still have the relative same order
for
(
int
i
=
0
;
i
<
shuffleOrder
.
getLength
();
i
++)
{
if
(
i
==
position
)
{
if
(
i
>=
indexFrom
&&
i
<
indexToExclusive
)
{
continue
;
}
int
expectedNextIndex
=
shuffleOrder
.
getNextIndex
(
i
);
if
(
expectedNextIndex
==
position
)
{
while
(
expectedNextIndex
>=
indexFrom
&&
expectedNextIndex
<
indexToExclusive
)
{
expectedNextIndex
=
shuffleOrder
.
getNextIndex
(
expectedNextIndex
);
}
if
(
expectedNextIndex
!=
C
.
INDEX_UNSET
&&
expectedNextIndex
>=
position
)
{
expectedNextIndex
--
;
if
(
expectedNextIndex
!=
C
.
INDEX_UNSET
&&
expectedNextIndex
>=
indexFrom
)
{
expectedNextIndex
-=
numberOfElementsToRemove
;
}
int
newNextIndex
=
newOrder
.
getNextIndex
(
i
<
position
?
i
:
i
-
1
);
int
newNextIndex
=
newOrder
.
getNextIndex
(
i
<
indexFrom
?
i
:
i
-
numberOfElementsToRemove
);
assertThat
(
newNextIndex
).
isEqualTo
(
expectedNextIndex
);
}
}
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
View file @
6b0e1758
...
...
@@ -376,7 +376,6 @@ public final class DashMediaSource extends BaseMediaSource {
private
int
staleManifestReloadAttempt
;
private
long
expiredManifestPublishTimeUs
;
private
boolean
dynamicMediaPresentationEnded
;
private
int
firstPeriodId
;
...
...
@@ -679,7 +678,6 @@ public final class DashMediaSource extends BaseMediaSource {
elapsedRealtimeOffsetMs
=
0
;
staleManifestReloadAttempt
=
0
;
expiredManifestPublishTimeUs
=
C
.
TIME_UNSET
;
dynamicMediaPresentationEnded
=
false
;
firstPeriodId
=
0
;
periodsById
.
clear
();
}
...
...
@@ -691,10 +689,6 @@ public final class DashMediaSource extends BaseMediaSource {
startLoadingManifest
();
}
/* package */
void
onDashLiveMediaPresentationEndSignalEncountered
()
{
this
.
dynamicMediaPresentationEnded
=
true
;
}
/* package */
void
onDashManifestPublishTimeExpired
(
long
expiredManifestPublishTimeUs
)
{
if
(
this
.
expiredManifestPublishTimeUs
==
C
.
TIME_UNSET
||
this
.
expiredManifestPublishTimeUs
<
expiredManifestPublishTimeUs
)
{
...
...
@@ -734,9 +728,8 @@ public final class DashMediaSource extends BaseMediaSource {
// behind.
Log
.
w
(
TAG
,
"Loaded out of sync manifest"
);
isManifestStale
=
true
;
}
else
if
(
dynamicMediaPresentationEnded
||
(
expiredManifestPublishTimeUs
!=
C
.
TIME_UNSET
&&
newManifest
.
publishTimeMs
*
1000
<=
expiredManifestPublishTimeUs
))
{
}
else
if
(
expiredManifestPublishTimeUs
!=
C
.
TIME_UNSET
&&
newManifest
.
publishTimeMs
*
1000
<=
expiredManifestPublishTimeUs
)
{
// If we receive a dynamic manifest that's older than expected (i.e. its publish time has
// expired, or it's dynamic and we know the presentation has ended), then this manifest is
// stale.
...
...
@@ -745,8 +738,6 @@ public final class DashMediaSource extends BaseMediaSource {
"Loaded stale dynamic manifest: "
+
newManifest
.
publishTimeMs
+
", "
+
dynamicMediaPresentationEnded
+
", "
+
expiredManifestPublishTimeUs
);
isManifestStale
=
true
;
}
...
...
@@ -763,7 +754,6 @@ public final class DashMediaSource extends BaseMediaSource {
staleManifestReloadAttempt
=
0
;
}
manifest
=
newManifest
;
manifestLoadPending
&=
manifest
.
dynamic
;
manifestLoadStartTimestampMs
=
elapsedRealtimeMs
-
loadDurationMs
;
...
...
@@ -1170,12 +1160,16 @@ public final class DashMediaSource extends BaseMediaSource {
long
windowDefaultStartPositionUs
=
getAdjustedWindowDefaultStartPositionUs
(
defaultPositionProjectionUs
);
Object
tag
=
setTag
?
windowTag
:
null
;
boolean
isDynamic
=
manifest
.
dynamic
&&
manifest
.
minUpdatePeriodMs
!=
C
.
TIME_UNSET
&&
manifest
.
durationMs
==
C
.
TIME_UNSET
;
return
window
.
set
(
tag
,
presentationStartTimeMs
,
windowStartTimeMs
,
/* isSeekable= */
true
,
manifest
.
d
ynamic
,
isD
ynamic
,
windowDefaultStartPositionUs
,
windowDurationUs
,
/* firstPeriodIndex= */
0
,
...
...
@@ -1253,11 +1247,6 @@ public final class DashMediaSource extends BaseMediaSource {
public
void
onDashManifestPublishTimeExpired
(
long
expiredManifestPublishTimeUs
)
{
DashMediaSource
.
this
.
onDashManifestPublishTimeExpired
(
expiredManifestPublishTimeUs
);
}
@Override
public
void
onDashLiveMediaPresentationEndSignalEncountered
()
{
DashMediaSource
.
this
.
onDashLiveMediaPresentationEndSignalEncountered
();
}
}
private
final
class
ManifestCallback
implements
Loader
.
Callback
<
ParsingLoadable
<
DashManifest
>>
{
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DefaultDashChunkSource.java
View file @
6b0e1758
...
...
@@ -318,9 +318,12 @@ public class DefaultDashChunkSource implements DashChunkSource {
}
}
long
periodDurationUs
=
representationHolder
.
periodDurationUs
;
boolean
periodEnded
=
periodDurationUs
!=
C
.
TIME_UNSET
;
if
(
representationHolder
.
getSegmentCount
()
==
0
)
{
// The index doesn't define any segments.
out
.
endOfStream
=
!
manifest
.
dynamic
||
(
periodIndex
<
manifest
.
getPeriodCount
()
-
1
)
;
out
.
endOfStream
=
periodEnded
;
return
;
}
...
...
@@ -343,17 +346,15 @@ public class DefaultDashChunkSource implements DashChunkSource {
fatalError
=
new
BehindLiveWindowException
();
return
;
}
if
(
segmentNum
>
lastAvailableSegmentNum
||
(
missingLastSegment
&&
segmentNum
>=
lastAvailableSegmentNum
))
{
// The segment is beyond the end of the period. We know the period will not be extended if the
// manifest is static, or if there's a period after this one.
out
.
endOfStream
=
!
manifest
.
dynamic
||
(
periodIndex
<
manifest
.
getPeriodCount
()
-
1
);
// The segment is beyond the end of the period.
out
.
endOfStream
=
periodEnded
;
return
;
}
long
periodDurationUs
=
representationHolder
.
periodDurationUs
;
if
(
periodDurationUs
!=
C
.
TIME_UNSET
&&
representationHolder
.
getSegmentStartTimeUs
(
segmentNum
)
>=
periodDurationUs
)
{
if
(
periodEnded
&&
representationHolder
.
getSegmentStartTimeUs
(
segmentNum
)
>=
periodDurationUs
)
{
// The period duration clips the period to a position before the segment.
out
.
endOfStream
=
true
;
return
;
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/PlayerEmsgHandler.java
View file @
6b0e1758
...
...
@@ -60,8 +60,7 @@ import java.util.TreeMap;
*/
public
final
class
PlayerEmsgHandler
implements
Handler
.
Callback
{
private
static
final
int
EMSG_MEDIA_PRESENTATION_ENDED
=
1
;
private
static
final
int
EMSG_MANIFEST_EXPIRED
=
2
;
private
static
final
int
EMSG_MANIFEST_EXPIRED
=
1
;
/** Callbacks for player emsg events encountered during DASH live stream. */
public
interface
PlayerEmsgCallback
{
...
...
@@ -75,9 +74,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
* @param expiredManifestPublishTimeUs The manifest publish time that has been expired.
*/
void
onDashManifestPublishTimeExpired
(
long
expiredManifestPublishTimeUs
);
/** Called when a media presentation end signal is encountered during live stream. * */
void
onDashLiveMediaPresentationEndSignalEncountered
();
}
private
final
Allocator
allocator
;
...
...
@@ -88,7 +84,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
private
DashManifest
manifest
;
private
boolean
dynamicMediaPresentationEnded
;
private
long
expiredManifestPublishTimeUs
;
private
long
lastLoadedChunkEndTimeUs
;
private
long
lastLoadedChunkEndTimeBeforeRefreshUs
;
...
...
@@ -134,21 +129,15 @@ public final class PlayerEmsgHandler implements Handler.Callback {
return
true
;
}
boolean
manifestRefreshNeeded
=
false
;
if
(
dynamicMediaPresentationEnded
)
{
// The manifest we have is dynamic, but we know a non-dynamic one representing the final state
// should be available.
manifestRefreshNeeded
=
true
;
}
else
{
// Find the smallest publishTime (greater than or equal to the current manifest's publish
// time) that has a corresponding expiry time.
Map
.
Entry
<
Long
,
Long
>
expiredEntry
=
ceilingExpiryEntryForPublishTime
(
manifest
.
publishTimeMs
);
if
(
expiredEntry
!=
null
)
{
long
expiredPointUs
=
expiredEntry
.
getValue
();
if
(
expiredPointUs
<
presentationPositionUs
)
{
expiredManifestPublishTimeUs
=
expiredEntry
.
getKey
();
notifyManifestPublishTimeExpired
();
manifestRefreshNeeded
=
true
;
}
// Find the smallest publishTime (greater than or equal to the current manifest's publish time)
// that has a corresponding expiry time.
Map
.
Entry
<
Long
,
Long
>
expiredEntry
=
ceilingExpiryEntryForPublishTime
(
manifest
.
publishTimeMs
);
if
(
expiredEntry
!=
null
)
{
long
expiredPointUs
=
expiredEntry
.
getValue
();
if
(
expiredPointUs
<
presentationPositionUs
)
{
expiredManifestPublishTimeUs
=
expiredEntry
.
getKey
();
notifyManifestPublishTimeExpired
();
manifestRefreshNeeded
=
true
;
}
}
if
(
manifestRefreshNeeded
)
{
...
...
@@ -221,9 +210,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
return
true
;
}
switch
(
message
.
what
)
{
case
(
EMSG_MEDIA_PRESENTATION_ENDED
):
handleMediaPresentationEndedMessageEncountered
();
return
true
;
case
(
EMSG_MANIFEST_EXPIRED
):
ManifestExpiryEventInfo
messageObj
=
(
ManifestExpiryEventInfo
)
message
.
obj
;
handleManifestExpiredMessage
(
...
...
@@ -248,11 +234,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
}
}
private
void
handleMediaPresentationEndedMessageEncountered
()
{
dynamicMediaPresentationEnded
=
true
;
notifySourceMediaPresentationEnded
();
}
private
@Nullable
Map
.
Entry
<
Long
,
Long
>
ceilingExpiryEntryForPublishTime
(
long
publishTimeMs
)
{
return
manifestPublishTimeToExpiryTimeUs
.
ceilingEntry
(
publishTimeMs
);
}
...
...
@@ -273,10 +254,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
playerEmsgCallback
.
onDashManifestPublishTimeExpired
(
expiredManifestPublishTimeUs
);
}
private
void
notifySourceMediaPresentationEnded
()
{
playerEmsgCallback
.
onDashLiveMediaPresentationEndSignalEncountered
();
}
/** Requests DASH media manifest to be refreshed if necessary. */
private
void
maybeNotifyDashManifestRefreshNeeded
()
{
if
(
lastLoadedChunkEndTimeBeforeRefreshUs
!=
C
.
TIME_UNSET
...
...
@@ -298,12 +275,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
}
}
private
static
boolean
isMessageSignalingMediaPresentationEnded
(
EventMessage
eventMessage
)
{
// According to section 4.5.2.1 DASH-IF IOP, if both presentation time delta and event duration
// are zero, the media presentation is ended.
return
eventMessage
.
presentationTimeUs
==
0
&&
eventMessage
.
durationMs
==
0
;
}
/** Handles emsg messages for a specific track for the player. */
public
final
class
PlayerTrackEmsgHandler
implements
TrackOutput
{
...
...
@@ -413,16 +384,7 @@ public final class PlayerEmsgHandler implements Handler.Callback {
if
(
manifestPublishTimeMsInEmsg
==
C
.
TIME_UNSET
)
{
return
;
}
if
(
isMessageSignalingMediaPresentationEnded
(
eventMessage
))
{
onMediaPresentationEndedMessageEncountered
();
}
else
{
onManifestExpiredMessageEncountered
(
eventTimeUs
,
manifestPublishTimeMsInEmsg
);
}
}
private
void
onMediaPresentationEndedMessageEncountered
()
{
handler
.
sendMessage
(
handler
.
obtainMessage
(
EMSG_MEDIA_PRESENTATION_ENDED
));
onManifestExpiredMessageEncountered
(
eventTimeUs
,
manifestPublishTimeMsInEmsg
);
}
private
void
onManifestExpiredMessageEncountered
(
...
...
library/dash/src/main/proguard-rules.txt
deleted
100644 → 0
View file @
b5beb326
# Proguard rules specific to the dash module.
# Constructors accessed via reflection in SegmentDownloadAction
-dontnote com.google.android.exoplayer2.source.dash.offline.DashDownloadAction
-keepclassmembers class com.google.android.exoplayer2.source.dash.offline.DashDownloadAction {
static ** DESERIALIZER;
}
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsChunkSource.java
View file @
6b0e1758
...
...
@@ -262,7 +262,8 @@ import java.util.List;
// Retry when playlist is refreshed.
return
;
}
HlsMediaPlaylist
mediaPlaylist
=
playlistTracker
.
getPlaylistSnapshot
(
selectedUrl
);
HlsMediaPlaylist
mediaPlaylist
=
playlistTracker
.
getPlaylistSnapshot
(
selectedUrl
,
/* isForPlayback= */
true
);
independentSegments
=
mediaPlaylist
.
hasIndependentSegments
;
updateLiveEdgeTimeUs
(
mediaPlaylist
);
...
...
@@ -279,7 +280,7 @@ import java.util.List;
// behind the live window.
selectedVariantIndex
=
oldVariantIndex
;
selectedUrl
=
variants
[
selectedVariantIndex
];
mediaPlaylist
=
playlistTracker
.
getPlaylistSnapshot
(
selectedUrl
);
mediaPlaylist
=
playlistTracker
.
getPlaylistSnapshot
(
selectedUrl
,
/* isForPlayback= */
true
);
startOfPlaylistInPeriodUs
=
mediaPlaylist
.
startTimeUs
-
playlistTracker
.
getInitialStartTimeUs
();
chunkMediaSequence
=
previous
.
getNextChunkIndex
();
...
...
@@ -435,7 +436,8 @@ import java.util.List;
chunkIterators
[
i
]
=
MediaChunkIterator
.
EMPTY
;
continue
;
}
HlsMediaPlaylist
playlist
=
playlistTracker
.
getPlaylistSnapshot
(
variantUrl
);
HlsMediaPlaylist
playlist
=
playlistTracker
.
getPlaylistSnapshot
(
variantUrl
,
/* isForPlayback= */
false
);
long
startOfPlaylistInPeriodUs
=
playlist
.
startTimeUs
-
playlistTracker
.
getInitialStartTimeUs
();
boolean
switchingVariant
=
variantIndex
!=
oldVariantIndex
;
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java
View file @
6b0e1758
...
...
@@ -32,6 +32,7 @@ import com.google.android.exoplayer2.upstream.DataSpec;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.TimestampAdjuster
;
import
com.google.android.exoplayer2.util.Util
;
import
java.io.EOFException
;
import
java.io.IOException
;
import
java.util.List
;
import
java.util.concurrent.atomic.AtomicInteger
;
...
...
@@ -41,8 +42,7 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
/* package */
final
class
HlsMediaChunk
extends
MediaChunk
{
private
static
final
String
PRIV_TIMESTAMP_FRAME_OWNER
=
public
static
final
String
PRIV_TIMESTAMP_FRAME_OWNER
=
"com.apple.streaming.transportStreamTimestamp"
;
private
static
final
AtomicInteger
uidSource
=
new
AtomicInteger
();
...
...
@@ -313,8 +313,10 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
private
long
peekId3PrivTimestamp
(
ExtractorInput
input
)
throws
IOException
,
InterruptedException
{
input
.
resetPeekPosition
();
if
(
input
.
getLength
()
<
Id3Decoder
.
ID3_HEADER_LENGTH
||
!
input
.
peekFully
(
id3Data
.
data
,
0
,
Id3Decoder
.
ID3_HEADER_LENGTH
,
true
))
{
try
{
input
.
peekFully
(
id3Data
.
data
,
0
,
Id3Decoder
.
ID3_HEADER_LENGTH
);
}
catch
(
EOFException
e
)
{
// The input isn't long enough for there to be any ID3 data.
return
C
.
TIME_UNSET
;
}
id3Data
.
reset
(
Id3Decoder
.
ID3_HEADER_LENGTH
);
...
...
@@ -330,9 +332,7 @@ import java.util.concurrent.atomic.AtomicInteger;
id3Data
.
reset
(
requiredCapacity
);
System
.
arraycopy
(
data
,
0
,
id3Data
.
data
,
0
,
Id3Decoder
.
ID3_HEADER_LENGTH
);
}
if
(!
input
.
peekFully
(
id3Data
.
data
,
Id3Decoder
.
ID3_HEADER_LENGTH
,
id3Size
,
true
))
{
return
C
.
TIME_UNSET
;
}
input
.
peekFully
(
id3Data
.
data
,
Id3Decoder
.
ID3_HEADER_LENGTH
,
id3Size
);
Metadata
metadata
=
id3Decoder
.
decode
(
id3Data
.
data
,
id3Size
);
if
(
metadata
==
null
)
{
return
C
.
TIME_UNSET
;
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java
View file @
6b0e1758
...
...
@@ -16,6 +16,7 @@
package
com
.
google
.
android
.
exoplayer2
.
source
.
hls
;
import
android.os.Handler
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.FormatHolder
;
...
...
@@ -24,6 +25,8 @@ import com.google.android.exoplayer2.extractor.DummyTrackOutput;
import
com.google.android.exoplayer2.extractor.ExtractorOutput
;
import
com.google.android.exoplayer2.extractor.SeekMap
;
import
com.google.android.exoplayer2.extractor.TrackOutput
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.metadata.id3.PrivFrame
;
import
com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispatcher
;
import
com.google.android.exoplayer2.source.SampleQueue
;
import
com.google.android.exoplayer2.source.SampleQueue.UpstreamFormatChangedListener
;
...
...
@@ -791,7 +794,7 @@ import java.util.List;
return
createDummyTrackOutput
(
id
,
type
);
}
}
SampleQueue
trackOutput
=
new
SampleQueue
(
allocator
);
SampleQueue
trackOutput
=
new
PrivTimestampStripping
SampleQueue
(
allocator
);
trackOutput
.
setSampleOffsetUs
(
sampleOffsetUs
);
trackOutput
.
sourceId
(
chunkUid
);
trackOutput
.
setUpstreamFormatChangeListener
(
this
);
...
...
@@ -1126,4 +1129,53 @@ import java.util.List;
Log
.
w
(
TAG
,
"Unmapped track with id "
+
id
+
" of type "
+
type
);
return
new
DummyTrackOutput
();
}
private
static
final
class
PrivTimestampStrippingSampleQueue
extends
SampleQueue
{
public
PrivTimestampStrippingSampleQueue
(
Allocator
allocator
)
{
super
(
allocator
);
}
@Override
public
void
format
(
Format
format
)
{
super
.
format
(
format
.
copyWithMetadata
(
getAdjustedMetadata
(
format
.
metadata
)));
}
/**
* Strips the private timestamp frame from metadata, if present. See:
* https://github.com/google/ExoPlayer/issues/5063
*/
@Nullable
private
Metadata
getAdjustedMetadata
(
@Nullable
Metadata
metadata
)
{
if
(
metadata
==
null
)
{
return
null
;
}
int
length
=
metadata
.
length
();
int
transportStreamTimestampMetadataIndex
=
C
.
INDEX_UNSET
;
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
Metadata
.
Entry
metadataEntry
=
metadata
.
get
(
i
);
if
(
metadataEntry
instanceof
PrivFrame
)
{
PrivFrame
privFrame
=
(
PrivFrame
)
metadataEntry
;
if
(
HlsMediaChunk
.
PRIV_TIMESTAMP_FRAME_OWNER
.
equals
(
privFrame
.
owner
))
{
transportStreamTimestampMetadataIndex
=
i
;
break
;
}
}
}
if
(
transportStreamTimestampMetadataIndex
==
C
.
INDEX_UNSET
)
{
return
metadata
;
}
if
(
length
==
1
)
{
return
null
;
}
Metadata
.
Entry
[]
newMetadataEntries
=
new
Metadata
.
Entry
[
length
-
1
];
for
(
int
i
=
0
;
i
<
length
;
i
++)
{
if
(
i
!=
transportStreamTimestampMetadataIndex
)
{
int
newIndex
=
i
<
transportStreamTimestampMetadataIndex
?
i
:
i
-
1
;
newMetadataEntries
[
newIndex
]
=
metadata
.
get
(
i
);
}
}
return
new
Metadata
(
newMetadataEntries
);
}
}
}
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/DefaultHlsPlaylistTracker.java
View file @
6b0e1758
...
...
@@ -162,9 +162,9 @@ public final class DefaultHlsPlaylistTracker
}
@Override
public
HlsMediaPlaylist
getPlaylistSnapshot
(
HlsUrl
url
)
{
public
HlsMediaPlaylist
getPlaylistSnapshot
(
HlsUrl
url
,
boolean
isForPlayback
)
{
HlsMediaPlaylist
snapshot
=
playlistBundles
.
get
(
url
).
getPlaylistSnapshot
();
if
(
snapshot
!=
null
)
{
if
(
snapshot
!=
null
&&
isForPlayback
)
{
maybeSetPrimaryUrl
(
url
);
}
return
snapshot
;
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java
View file @
6b0e1758
...
...
@@ -167,11 +167,13 @@ public interface HlsPlaylistTracker {
* HlsUrl}.
*
* @param url The {@link HlsUrl} corresponding to the requested media playlist.
* @param isForPlayback Whether the caller might use the snapshot to request media segments for
* playback. If true, the primary playlist may be updated to the one requested.
* @return The most recent snapshot of the playlist referenced by the provided {@link HlsUrl}. May
* be null if no snapshot has been loaded yet.
*/
@Nullable
HlsMediaPlaylist
getPlaylistSnapshot
(
HlsUrl
url
);
HlsMediaPlaylist
getPlaylistSnapshot
(
HlsUrl
url
,
boolean
isForPlayback
);
/**
* Returns the start time of the first loaded primary playlist, or {@link C#TIME_UNSET} if no
...
...
library/hls/src/main/proguard-rules.txt
deleted
100644 → 0
View file @
b5beb326
# Proguard rules specific to the hls module.
# Constructors accessed via reflection in SegmentDownloadAction
-dontnote com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction
-keepclassmembers class com.google.android.exoplayer2.source.hls.offline.HlsDownloadAction {
static ** DESERIALIZER;
}
library/smoothstreaming/src/main/java/com/google/android/exoplayer2/source/smoothstreaming/manifest/SsManifestParser.java
View file @
6b0e1758
...
...
@@ -378,8 +378,12 @@ public class SsManifestParser implements ParsingLoadable.Parser<SsManifest> {
DrmInitData
drmInitData
=
new
DrmInitData
(
new
SchemeData
(
protectionElement
.
uuid
,
MimeTypes
.
VIDEO_MP4
,
protectionElement
.
data
));
for
(
StreamElement
streamElement
:
streamElementArray
)
{
for
(
int
i
=
0
;
i
<
streamElement
.
formats
.
length
;
i
++)
{
streamElement
.
formats
[
i
]
=
streamElement
.
formats
[
i
].
copyWithDrmInitData
(
drmInitData
);
int
type
=
streamElement
.
type
;
if
(
type
==
C
.
TRACK_TYPE_VIDEO
||
type
==
C
.
TRACK_TYPE_AUDIO
)
{
Format
[]
formats
=
streamElement
.
formats
;
for
(
int
i
=
0
;
i
<
formats
.
length
;
i
++)
{
formats
[
i
]
=
formats
[
i
].
copyWithDrmInitData
(
drmInitData
);
}
}
}
}
...
...
library/smoothstreaming/src/main/proguard-rules.txt
deleted
100644 → 0
View file @
b5beb326
# Proguard rules specific to the smoothstreaming module.
# Constructors accessed via reflection in SegmentDownloadAction
-dontnote com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction
-keepclassmembers class com.google.android.exoplayer2.source.smoothstreaming.offline.SsDownloadAction {
static ** DESERIALIZER;
}
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
View file @
6b0e1758
...
...
@@ -241,11 +241,7 @@ import java.util.List;
*/
public
class
PlayerView
extends
FrameLayout
{
private
static
final
int
SURFACE_TYPE_NONE
=
0
;
private
static
final
int
SURFACE_TYPE_SURFACE_VIEW
=
1
;
private
static
final
int
SURFACE_TYPE_TEXTURE_VIEW
=
2
;
private
static
final
int
SURFACE_TYPE_MONO360_VIEW
=
3
;
// LINT.IfChange
/**
* Determines when the buffering view is shown. One of {@link #SHOW_BUFFERING_NEVER}, {@link
* #SHOW_BUFFERING_WHEN_PLAYING} or {@link #SHOW_BUFFERING_ALWAYS}.
...
...
@@ -266,6 +262,14 @@ public class PlayerView extends FrameLayout {
* buffering} state.
*/
public
static
final
int
SHOW_BUFFERING_ALWAYS
=
2
;
// LINT.ThenChange(../../../../../../res/values/attrs.xml)
// LINT.IfChange
private
static
final
int
SURFACE_TYPE_NONE
=
0
;
private
static
final
int
SURFACE_TYPE_SURFACE_VIEW
=
1
;
private
static
final
int
SURFACE_TYPE_TEXTURE_VIEW
=
2
;
private
static
final
int
SURFACE_TYPE_MONO360_VIEW
=
3
;
// LINT.ThenChange(../../../../../../res/values/attrs.xml)
private
final
AspectRatioFrameLayout
contentFrame
;
private
final
View
shutterView
;
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/SphericalSurfaceView.java
View file @
6b0e1758
...
...
@@ -104,12 +104,18 @@ public final class SphericalSurfaceView extends GLSurfaceView {
// Configure sensors and touch.
sensorManager
=
(
SensorManager
)
Assertions
.
checkNotNull
(
context
.
getSystemService
(
Context
.
SENSOR_SERVICE
));
// TYPE_GAME_ROTATION_VECTOR is the easiest sensor since it handles all the complex math for
// fusion. It's used instead of TYPE_ROTATION_VECTOR since the latter uses the magnetometer on
// devices. When used indoors, the magnetometer can take some time to settle depending on the
// device and amount of metal in the environment.
int
type
=
Util
.
SDK_INT
>=
18
?
Sensor
.
TYPE_GAME_ROTATION_VECTOR
:
Sensor
.
TYPE_ROTATION_VECTOR
;
orientationSensor
=
sensorManager
.
getDefaultSensor
(
type
);
Sensor
orientationSensor
=
null
;
if
(
Util
.
SDK_INT
>=
18
)
{
// TYPE_GAME_ROTATION_VECTOR is the easiest sensor since it handles all the complex math for
// fusion. It's used instead of TYPE_ROTATION_VECTOR since the latter uses the magnetometer on
// devices. When used indoors, the magnetometer can take some time to settle depending on the
// device and amount of metal in the environment.
orientationSensor
=
sensorManager
.
getDefaultSensor
(
Sensor
.
TYPE_GAME_ROTATION_VECTOR
);
}
if
(
orientationSensor
==
null
)
{
orientationSensor
=
sensorManager
.
getDefaultSensor
(
Sensor
.
TYPE_ROTATION_VECTOR
);
}
this
.
orientationSensor
=
orientationSensor
;
scene
=
new
SceneRenderer
();
renderer
=
new
Renderer
(
scene
);
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/spherical/TouchTracker.java
View file @
6b0e1758
...
...
@@ -44,11 +44,10 @@ import android.view.View;
* a nicer UI. An even more advanced UI would reproject the user's touch point into 3D and drag the
* Mesh as the user moves their finger. However, that requires quaternion interpolation.
*/
// @VisibleForTesting
/*package*/
class
TouchTracker
extends
GestureDetector
.
SimpleOnGestureListener
/* package */
class
TouchTracker
extends
GestureDetector
.
SimpleOnGestureListener
implements
View
.
OnTouchListener
{
/*
package
*/
interface
Listener
{
/*
package
*/
interface
Listener
{
void
onScrollChange
(
PointF
scrollOffsetDegrees
);
}
...
...
library/ui/src/main/res/values/attrs.xml
View file @
6b0e1758
...
...
@@ -53,8 +53,8 @@
<attr
name=
"auto_show"
format=
"boolean"
/>
<attr
name=
"show_buffering"
format=
"enum"
>
<enum
name=
"never"
value=
"0"
/>
<enum
name=
"
always
"
value=
"1"
/>
<enum
name=
"
when_playing
"
value=
"2"
/>
<enum
name=
"
when_playing
"
value=
"1"
/>
<enum
name=
"
always
"
value=
"2"
/>
</attr>
<attr
name=
"keep_content_on_player_reset"
format=
"boolean"
/>
<attr
name=
"resize_mode"
/>
...
...
testutils_robolectric/src/main/java/com/google/android/exoplayer2/testutil/FakeShuffleOrder.java
View file @
6b0e1758
...
...
@@ -61,8 +61,8 @@ public final class FakeShuffleOrder implements ShuffleOrder {
}
@Override
public
ShuffleOrder
cloneAndRemove
(
int
removalIndex
)
{
return
new
FakeShuffleOrder
(
length
-
1
);
public
ShuffleOrder
cloneAndRemove
(
int
indexFrom
,
int
indexToExclusive
)
{
return
new
FakeShuffleOrder
(
length
-
indexToExclusive
+
indexFrom
);
}
@Override
...
...
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