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
8cbcfd66
authored
Aug 07, 2018
by
ojw28
Committed by
GitHub
Aug 07, 2018
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge pull request #4556 from google/dev-v2-r2.8.3
r2.8.3
parents
f7ed789f
563f13a8
Hide whitespace changes
Inline
Side-by-side
Showing
59 changed files
with
1954 additions
and
707 deletions
.idea/codeStyleSettings.xml
RELEASENOTES.md
constants.gradle
demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
extensions/leanback/src/main/java/com/google/android/exoplayer2/ext/leanback/LeanbackPlayerAdapter.java
extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
library/core/src/main/java/com/google/android/exoplayer2/Format.java
library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java
library/core/src/main/java/com/google/android/exoplayer2/Player.java
library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java
library/core/src/main/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessor.java
library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java
library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java
library/core/src/main/java/com/google/android/exoplayer2/drm/DrmInitData.java
library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaDrm.java
library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java
library/core/src/main/java/com/google/android/exoplayer2/drm/LocalMediaDrmCallback.java
library/core/src/main/java/com/google/android/exoplayer2/drm/MediaDrmCallback.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/GaplessInfoHolder.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/MetadataUtil.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackSampleTable.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
library/core/src/main/java/com/google/android/exoplayer2/metadata/id3/InternalFrame.java
library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java
library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdPlaybackState.java
library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java
library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java
library/core/src/main/java/com/google/android/exoplayer2/util/TimestampAdjuster.java
library/core/src/main/java/com/google/android/exoplayer2/util/XmlPullParserUtil.java
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
library/core/src/test/java/com/google/android/exoplayer2/source/ads/AdPlaybackStateTest.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.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/EventSampleStream.java
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/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/test/java/com/google/android/exoplayer2/source/hls/Aes128DataSourceTest.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java
library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java
library/ui/src/main/res/drawable-ldpi/exo_controls_fullscreen_enter.png
library/ui/src/main/res/drawable-ldpi/exo_controls_fullscreen_exit.png
testutils/src/main/java/com/google/android/exoplayer2/testutil/Action.java
testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java
.idea/codeStyleSettings.xml
0 → 100644
View file @
8cbcfd66
<?xml version="1.0" encoding="UTF-8"?>
<project
version=
"4"
>
<component
name=
"ProjectCodeStyleSettingsManager"
>
<option
name=
"PER_PROJECT_SETTINGS"
>
<value>
<option
name=
"OTHER_INDENT_OPTIONS"
>
<value>
<option
name=
"INDENT_SIZE"
value=
"2"
/>
<option
name=
"CONTINUATION_INDENT_SIZE"
value=
"4"
/>
<option
name=
"TAB_SIZE"
value=
"2"
/>
<option
name=
"USE_TAB_CHARACTER"
value=
"false"
/>
<option
name=
"SMART_TABS"
value=
"false"
/>
<option
name=
"LABEL_INDENT_SIZE"
value=
"0"
/>
<option
name=
"LABEL_INDENT_ABSOLUTE"
value=
"false"
/>
<option
name=
"USE_RELATIVE_INDENTS"
value=
"false"
/>
</value>
</option>
<option
name=
"CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND"
value=
"999"
/>
<option
name=
"NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND"
value=
"999"
/>
<option
name=
"PACKAGES_TO_USE_IMPORT_ON_DEMAND"
>
<value
/>
</option>
<option
name=
"IMPORT_LAYOUT_TABLE"
>
<value>
<package
name=
""
withSubpackages=
"true"
static=
"true"
/>
<emptyLine
/>
<package
name=
""
withSubpackages=
"true"
static=
"false"
/>
</value>
</option>
<option
name=
"RIGHT_MARGIN"
value=
"100"
/>
<option
name=
"JD_ALIGN_PARAM_COMMENTS"
value=
"false"
/>
<option
name=
"JD_ALIGN_EXCEPTION_COMMENTS"
value=
"false"
/>
<option
name=
"JD_P_AT_EMPTY_LINES"
value=
"false"
/>
<option
name=
"JD_KEEP_EMPTY_PARAMETER"
value=
"false"
/>
<option
name=
"JD_KEEP_EMPTY_EXCEPTION"
value=
"false"
/>
<option
name=
"JD_KEEP_EMPTY_RETURN"
value=
"false"
/>
<option
name=
"KEEP_CONTROL_STATEMENT_IN_ONE_LINE"
value=
"false"
/>
<option
name=
"KEEP_BLANK_LINES_IN_CODE"
value=
"1"
/>
<option
name=
"KEEP_BLANK_LINES_BEFORE_RBRACE"
value=
"0"
/>
<option
name=
"ALIGN_MULTILINE_PARAMETERS"
value=
"false"
/>
<option
name=
"ALIGN_MULTILINE_FOR"
value=
"false"
/>
<option
name=
"SPACE_BEFORE_ARRAY_INITIALIZER_LBRACE"
value=
"true"
/>
<option
name=
"CALL_PARAMETERS_WRAP"
value=
"1"
/>
<option
name=
"METHOD_PARAMETERS_WRAP"
value=
"1"
/>
<option
name=
"EXTENDS_LIST_WRAP"
value=
"1"
/>
<option
name=
"THROWS_KEYWORD_WRAP"
value=
"1"
/>
<option
name=
"METHOD_CALL_CHAIN_WRAP"
value=
"1"
/>
<option
name=
"BINARY_OPERATION_WRAP"
value=
"1"
/>
<option
name=
"BINARY_OPERATION_SIGN_ON_NEXT_LINE"
value=
"true"
/>
<option
name=
"TERNARY_OPERATION_WRAP"
value=
"1"
/>
<option
name=
"TERNARY_OPERATION_SIGNS_ON_NEXT_LINE"
value=
"true"
/>
<option
name=
"FOR_STATEMENT_WRAP"
value=
"1"
/>
<option
name=
"ARRAY_INITIALIZER_WRAP"
value=
"1"
/>
<option
name=
"WRAP_COMMENTS"
value=
"true"
/>
<option
name=
"IF_BRACE_FORCE"
value=
"3"
/>
<option
name=
"DOWHILE_BRACE_FORCE"
value=
"3"
/>
<option
name=
"WHILE_BRACE_FORCE"
value=
"3"
/>
<option
name=
"FOR_BRACE_FORCE"
value=
"3"
/>
<AndroidXmlCodeStyleSettings>
<option
name=
"USE_CUSTOM_SETTINGS"
value=
"true"
/>
<option
name=
"LAYOUT_SETTINGS"
>
<value>
<option
name=
"INSERT_BLANK_LINE_BEFORE_TAG"
value=
"false"
/>
</value>
</option>
</AndroidXmlCodeStyleSettings>
<Objective-C>
<option
name=
"INDENT_NAMESPACE_MEMBERS"
value=
"0"
/>
<option
name=
"INDENT_C_STRUCT_MEMBERS"
value=
"2"
/>
<option
name=
"INDENT_CLASS_MEMBERS"
value=
"2"
/>
<option
name=
"INDENT_VISIBILITY_KEYWORDS"
value=
"1"
/>
<option
name=
"INDENT_INSIDE_CODE_BLOCK"
value=
"2"
/>
<option
name=
"KEEP_STRUCTURES_IN_ONE_LINE"
value=
"true"
/>
<option
name=
"FUNCTION_PARAMETERS_WRAP"
value=
"5"
/>
<option
name=
"FUNCTION_CALL_ARGUMENTS_WRAP"
value=
"5"
/>
<option
name=
"TEMPLATE_CALL_ARGUMENTS_WRAP"
value=
"5"
/>
<option
name=
"TEMPLATE_CALL_ARGUMENTS_ALIGN_MULTILINE"
value=
"true"
/>
<option
name=
"ALIGN_INIT_LIST_IN_COLUMNS"
value=
"false"
/>
<option
name=
"SPACE_BEFORE_SUPERCLASS_COLON"
value=
"false"
/>
</Objective-C>
<Objective-C-extensions>
<file>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"Import"
/>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"Macro"
/>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"Typedef"
/>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"Enum"
/>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"Constant"
/>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"Global"
/>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"Struct"
/>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"FunctionPredecl"
/>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"Function"
/>
</file>
<class>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"Property"
/>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"Synthesize"
/>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"InitMethod"
/>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"StaticMethod"
/>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"InstanceMethod"
/>
<option
name=
"com.jetbrains.cidr.lang.util.OCDeclarationKind"
value=
"DeallocMethod"
/>
</class>
<extensions>
<pair
source=
"cc"
header=
"h"
/>
<pair
source=
"c"
header=
"h"
/>
</extensions>
</Objective-C-extensions>
<XML>
<option
name=
"XML_ALIGN_ATTRIBUTES"
value=
"false"
/>
<option
name=
"XML_LEGACY_SETTINGS_IMPORTED"
value=
"true"
/>
</XML>
<codeStyleSettings
language=
"HTML"
>
<indentOptions>
<option
name=
"INDENT_SIZE"
value=
"2"
/>
<option
name=
"CONTINUATION_INDENT_SIZE"
value=
"4"
/>
<option
name=
"TAB_SIZE"
value=
"2"
/>
</indentOptions>
</codeStyleSettings>
<codeStyleSettings
language=
"JAVA"
>
<option
name=
"KEEP_CONTROL_STATEMENT_IN_ONE_LINE"
value=
"false"
/>
<option
name=
"KEEP_BLANK_LINES_IN_CODE"
value=
"1"
/>
<option
name=
"BLANK_LINES_AFTER_CLASS_HEADER"
value=
"1"
/>
<option
name=
"ALIGN_MULTILINE_PARAMETERS"
value=
"false"
/>
<option
name=
"ALIGN_MULTILINE_RESOURCES"
value=
"false"
/>
<option
name=
"ALIGN_MULTILINE_FOR"
value=
"false"
/>
<option
name=
"CALL_PARAMETERS_WRAP"
value=
"1"
/>
<option
name=
"METHOD_PARAMETERS_WRAP"
value=
"1"
/>
<option
name=
"EXTENDS_LIST_WRAP"
value=
"1"
/>
<option
name=
"THROWS_KEYWORD_WRAP"
value=
"1"
/>
<option
name=
"METHOD_CALL_CHAIN_WRAP"
value=
"1"
/>
<option
name=
"BINARY_OPERATION_WRAP"
value=
"1"
/>
<option
name=
"BINARY_OPERATION_SIGN_ON_NEXT_LINE"
value=
"true"
/>
<option
name=
"TERNARY_OPERATION_WRAP"
value=
"1"
/>
<option
name=
"TERNARY_OPERATION_SIGNS_ON_NEXT_LINE"
value=
"true"
/>
<option
name=
"FOR_STATEMENT_WRAP"
value=
"1"
/>
<option
name=
"ARRAY_INITIALIZER_WRAP"
value=
"1"
/>
<option
name=
"IF_BRACE_FORCE"
value=
"3"
/>
<option
name=
"DOWHILE_BRACE_FORCE"
value=
"3"
/>
<option
name=
"WHILE_BRACE_FORCE"
value=
"3"
/>
<option
name=
"FOR_BRACE_FORCE"
value=
"3"
/>
<option
name=
"PARENT_SETTINGS_INSTALLED"
value=
"true"
/>
<indentOptions>
<option
name=
"INDENT_SIZE"
value=
"2"
/>
<option
name=
"CONTINUATION_INDENT_SIZE"
value=
"4"
/>
<option
name=
"TAB_SIZE"
value=
"2"
/>
</indentOptions>
</codeStyleSettings>
<codeStyleSettings
language=
"JSON"
>
<indentOptions>
<option
name=
"CONTINUATION_INDENT_SIZE"
value=
"4"
/>
<option
name=
"TAB_SIZE"
value=
"2"
/>
</indentOptions>
</codeStyleSettings>
<codeStyleSettings
language=
"ObjectiveC"
>
<option
name=
"KEEP_BLANK_LINES_BEFORE_RBRACE"
value=
"1"
/>
<option
name=
"BLANK_LINES_BEFORE_IMPORTS"
value=
"0"
/>
<option
name=
"BLANK_LINES_AFTER_IMPORTS"
value=
"0"
/>
<option
name=
"BLANK_LINES_AROUND_CLASS"
value=
"0"
/>
<option
name=
"BLANK_LINES_AROUND_METHOD"
value=
"0"
/>
<option
name=
"BLANK_LINES_AROUND_METHOD_IN_INTERFACE"
value=
"0"
/>
<option
name=
"ALIGN_MULTILINE_BINARY_OPERATION"
value=
"false"
/>
<option
name=
"BINARY_OPERATION_SIGN_ON_NEXT_LINE"
value=
"true"
/>
<option
name=
"FOR_STATEMENT_WRAP"
value=
"1"
/>
<option
name=
"ASSIGNMENT_WRAP"
value=
"1"
/>
<indentOptions>
<option
name=
"INDENT_SIZE"
value=
"2"
/>
<option
name=
"CONTINUATION_INDENT_SIZE"
value=
"4"
/>
</indentOptions>
</codeStyleSettings>
<codeStyleSettings
language=
"XML"
>
<indentOptions>
<option
name=
"INDENT_SIZE"
value=
"2"
/>
<option
name=
"CONTINUATION_INDENT_SIZE"
value=
"2"
/>
<option
name=
"TAB_SIZE"
value=
"2"
/>
</indentOptions>
<arrangement>
<rules>
<section>
<rule>
<match>
<AND>
<NAME>
xmlns:android
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
^$
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
xmlns:.*
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
^$
</XML_NAMESPACE>
</AND>
</match>
<order>
BY_NAME
</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:id
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
style
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
^$
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
^$
</XML_NAMESPACE>
</AND>
</match>
<order>
BY_NAME
</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:.*Style
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
<order>
BY_NAME
</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:layout_width
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:layout_height
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:layout_weight
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:layout_margin
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:layout_marginTop
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:layout_marginBottom
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:layout_marginStart
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:layout_marginEnd
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:layout_marginLeft
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:layout_marginRight
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:layout_.*
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
<order>
BY_NAME
</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:padding
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:paddingTop
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:paddingBottom
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:paddingStart
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:paddingEnd
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:paddingLeft
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*:paddingRight
</NAME>
<XML_ATTRIBUTE
/>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*
</NAME>
<XML_NAMESPACE>
http://schemas.android.com/apk/res/android
</XML_NAMESPACE>
</AND>
</match>
<order>
BY_NAME
</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*
</NAME>
<XML_NAMESPACE>
http://schemas.android.com/apk/res-auto
</XML_NAMESPACE>
</AND>
</match>
<order>
BY_NAME
</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*
</NAME>
<XML_NAMESPACE>
http://schemas.android.com/tools
</XML_NAMESPACE>
</AND>
</match>
<order>
BY_NAME
</order>
</rule>
</section>
<section>
<rule>
<match>
<AND>
<NAME>
.*
</NAME>
<XML_NAMESPACE>
.*
</XML_NAMESPACE>
</AND>
</match>
<order>
BY_NAME
</order>
</rule>
</section>
</rules>
</arrangement>
</codeStyleSettings>
</value>
</option>
<option
name=
"USE_PER_PROJECT_SETTINGS"
value=
"true"
/>
</component>
</project>
RELEASENOTES.md
View file @
8cbcfd66
# Release notes #
# Release notes #
### 2.8.3 ###
*
IMA:
*
Fix behavior when creating/releasing the player then releasing
`ImaAdsLoader`
(
[
#3879
](
https://github.com/google/ExoPlayer/issues/3879
)
).
*
Add support for setting slots for companion ads.
*
Captions:
*
TTML: Fix an issue with TTML using font size as % of cell resolution that
makes
`SubtitleView.setApplyEmbeddedFontSizes()`
not work correctly.
(
[
#4491
](
https://github.com/google/ExoPlayer/issues/4491
)
).
*
CEA-608: Improve handling of embedded styles
(
[
#4321
](
https://github.com/google/ExoPlayer/issues/4321
)
).
*
DASH:
*
Exclude text streams from duration calculations
(
[
#4029
](
https://github.com/google/ExoPlayer/issues/4029
)
).
*
Fix freezing when playing multi-period manifests with
`EventStream`
s
(
[
#4492
](
https://github.com/google/ExoPlayer/issues/4492
)
).
*
DRM: Allow DrmInitData to carry a license server URL
(
[
#3393
](
https://github.com/google/ExoPlayer/issues/3393
)
).
*
MPEG-TS: Fix bug preventing SCTE-35 cues from being output
(
[
#4573
](
https://github.com/google/ExoPlayer/issues/4573
)
).
*
Expose all internal ID3 data stored in MP4 udta boxes, and switch from using
CommentFrame to InternalFrame for frames with gapless metadata in MP4.
*
Add
`PlayerView.isControllerVisible`
(
[
#4385
](
https://github.com/google/ExoPlayer/issues/4385
)
).
*
Fix issue playing DRM protected streams on Asus Zenfone 2
(
[
#4403
](
https://github.com/google/ExoPlayer/issues/4413
)
).
*
Add support for multiple audio and video tracks in MPEG-PS streams
(
[
#4406
](
https://github.com/google/ExoPlayer/issues/4406
)
).
*
Add workaround for track index mismatches between trex and tkhd boxes in
fragmented MP4 files
(
[
#4477
](
https://github.com/google/ExoPlayer/issues/4477
)
).
*
Add workaround for track index mismatches between tfhd and tkhd boxes in
fragmented MP4 files
(
[
#4083
](
https://github.com/google/ExoPlayer/issues/4083
)
).
*
Ignore all MP4 edit lists if one edit list couldn't be handled
(
[
#4348
](
https://github.com/google/ExoPlayer/issues/4348
)
).
*
Fix issue when switching track selection from an embedded track to a primary
track in DASH (
[
#4477
](
https://github.com/google/ExoPlayer/issues/4477
)
).
*
Fix accessibility class name for
`DefaultTimeBar`
(
[
#4611
](
https://github.com/google/ExoPlayer/issues/4611
)
).
*
Improved compatibility with FireOS devices.
### 2.8.2 ###
### 2.8.2 ###
*
IMA: Don't advertise support for video/mpeg ad media, as we don't have an
*
IMA: Don't advertise support for video/mpeg ad media, as we don't have an
...
...
constants.gradle
View file @
8cbcfd66
...
@@ -13,8 +13,8 @@
...
@@ -13,8 +13,8 @@
// limitations under the License.
// limitations under the License.
project
.
ext
{
project
.
ext
{
// ExoPlayer version and version code.
// ExoPlayer version and version code.
releaseVersion
=
'2.8.
2
'
releaseVersion
=
'2.8.
3
'
releaseVersionCode
=
280
2
releaseVersionCode
=
280
3
// Important: ExoPlayer specifies a minSdkVersion of 14 because various
// Important: ExoPlayer specifies a minSdkVersion of 14 because various
// components provided by the library may be of use on older devices.
// components provided by the library may be of use on older devices.
// However, please note that the core media playback functionality provided
// However, please note that the core media playback functionality provided
...
...
demos/cast/src/main/java/com/google/android/exoplayer2/castdemo/PlayerManager.java
View file @
8cbcfd66
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.castdemo;
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.castdemo;
import
android.content.Context
;
import
android.content.Context
;
import
android.net.Uri
;
import
android.net.Uri
;
import
android.support.annotation.Nullable
;
import
android.view.KeyEvent
;
import
android.view.KeyEvent
;
import
android.view.View
;
import
android.view.View
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
...
@@ -282,7 +283,7 @@ import java.util.ArrayList;
...
@@ -282,7 +283,7 @@ import java.util.ArrayList;
@Override
@Override
public
void
onTimelineChanged
(
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
@TimelineChangeReason
int
reason
)
{
Timeline
timeline
,
@Nullable
Object
manifest
,
@TimelineChangeReason
int
reason
)
{
updateCurrentItemIndex
();
updateCurrentItemIndex
();
if
(
timeline
.
isEmpty
())
{
if
(
timeline
.
isEmpty
())
{
castMediaQueueCreationPending
=
true
;
castMediaQueueCreationPending
=
true
;
...
...
extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java
View file @
8cbcfd66
...
@@ -38,6 +38,7 @@ import com.google.ads.interactivemedia.v3.api.AdsManager;
...
@@ -38,6 +38,7 @@ import com.google.ads.interactivemedia.v3.api.AdsManager;
import
com.google.ads.interactivemedia.v3.api.AdsManagerLoadedEvent
;
import
com.google.ads.interactivemedia.v3.api.AdsManagerLoadedEvent
;
import
com.google.ads.interactivemedia.v3.api.AdsRenderingSettings
;
import
com.google.ads.interactivemedia.v3.api.AdsRenderingSettings
;
import
com.google.ads.interactivemedia.v3.api.AdsRequest
;
import
com.google.ads.interactivemedia.v3.api.AdsRequest
;
import
com.google.ads.interactivemedia.v3.api.CompanionAdSlot
;
import
com.google.ads.interactivemedia.v3.api.ImaSdkFactory
;
import
com.google.ads.interactivemedia.v3.api.ImaSdkFactory
;
import
com.google.ads.interactivemedia.v3.api.ImaSdkSettings
;
import
com.google.ads.interactivemedia.v3.api.ImaSdkSettings
;
import
com.google.ads.interactivemedia.v3.api.player.ContentProgressProvider
;
import
com.google.ads.interactivemedia.v3.api.player.ContentProgressProvider
;
...
@@ -62,6 +63,7 @@ import java.lang.annotation.Retention;
...
@@ -62,6 +63,7 @@ import java.lang.annotation.Retention;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.RetentionPolicy
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Arrays
;
import
java.util.Collection
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map
;
...
@@ -267,13 +269,9 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -267,13 +269,9 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
/** The expected ad group index that IMA should load next. */
/** The expected ad group index that IMA should load next. */
private
int
expectedAdGroupIndex
;
private
int
expectedAdGroupIndex
;
/**
/** The index of the current ad group that IMA is loading. */
* The index of the current ad group that IMA is loading.
*/
private
int
adGroupIndex
;
private
int
adGroupIndex
;
/**
/** Whether IMA has sent an ad event to pause content since the last resume content event. */
* Whether IMA has sent an ad event to pause content since the last resume content event.
*/
private
boolean
imaPausedContent
;
private
boolean
imaPausedContent
;
/** The current ad playback state. */
/** The current ad playback state. */
private
@ImaAdState
int
imaAdState
;
private
@ImaAdState
int
imaAdState
;
...
@@ -285,9 +283,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -285,9 +283,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
// Fields tracking the player/loader state.
// Fields tracking the player/loader state.
/**
/** Whether the player is playing an ad. */
* Whether the player is playing an ad.
*/
private
boolean
playingAd
;
private
boolean
playingAd
;
/**
/**
* If the player is playing an ad, stores the ad index in its ad group. {@link C#INDEX_UNSET}
* If the player is playing an ad, stores the ad index in its ad group. {@link C#INDEX_UNSET}
...
@@ -310,13 +306,9 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -310,13 +306,9 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
* content progress should increase. {@link C#TIME_UNSET} otherwise.
* content progress should increase. {@link C#TIME_UNSET} otherwise.
*/
*/
private
long
fakeContentProgressOffsetMs
;
private
long
fakeContentProgressOffsetMs
;
/**
/** Stores the pending content position when a seek operation was intercepted to play an ad. */
* Stores the pending content position when a seek operation was intercepted to play an ad.
*/
private
long
pendingContentPositionMs
;
private
long
pendingContentPositionMs
;
/**
/** Whether {@link #getContentProgress()} has sent {@link #pendingContentPositionMs} to IMA. */
* Whether {@link #getContentProgress()} has sent {@link #pendingContentPositionMs} to IMA.
*/
private
boolean
sentPendingContentPositionMs
;
private
boolean
sentPendingContentPositionMs
;
/**
/**
...
@@ -406,6 +398,17 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -406,6 +398,17 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
}
}
/**
/**
* Sets the slots for displaying companion ads. Individual slots can be created using {@link
* ImaSdkFactory#createCompanionAdSlot()}.
*
* @param companionSlots Slots for displaying companion ads.
* @see AdDisplayContainer#setCompanionSlots(Collection)
*/
public
void
setCompanionSlots
(
Collection
<
CompanionAdSlot
>
companionSlots
)
{
adDisplayContainer
.
setCompanionSlots
(
companionSlots
);
}
/**
* Requests ads, if they have not already been requested. Must be called on the main thread.
* Requests ads, if they have not already been requested. Must be called on the main thread.
*
*
* <p>Ads will be requested automatically when the player is prepared if this method has not been
* <p>Ads will be requested automatically when the player is prepared if this method has not been
...
@@ -509,6 +512,11 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -509,6 +512,11 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
adsManager
.
destroy
();
adsManager
.
destroy
();
adsManager
=
null
;
adsManager
=
null
;
}
}
imaPausedContent
=
false
;
imaAdState
=
IMA_AD_STATE_NONE
;
pendingAdLoadError
=
null
;
adPlaybackState
=
AdPlaybackState
.
NONE
;
updateAdPlaybackState
();
}
}
@Override
@Override
...
@@ -558,7 +566,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -558,7 +566,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
Log
.
d
(
TAG
,
"onAdEvent: "
+
adEventType
);
Log
.
d
(
TAG
,
"onAdEvent: "
+
adEventType
);
}
}
if
(
adsManager
==
null
)
{
if
(
adsManager
==
null
)
{
Log
.
w
(
TAG
,
"
Dropping ad e
vent after release: "
+
adEvent
);
Log
.
w
(
TAG
,
"
Ignoring AdE
vent after release: "
+
adEvent
);
return
;
return
;
}
}
try
{
try
{
...
@@ -654,6 +662,13 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -654,6 +662,13 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
@Override
@Override
public
void
loadAd
(
String
adUriString
)
{
public
void
loadAd
(
String
adUriString
)
{
try
{
try
{
if
(
DEBUG
)
{
Log
.
d
(
TAG
,
"loadAd in ad group "
+
adGroupIndex
);
}
if
(
adsManager
==
null
)
{
Log
.
w
(
TAG
,
"Ignoring loadAd after release"
);
return
;
}
if
(
adGroupIndex
==
C
.
INDEX_UNSET
)
{
if
(
adGroupIndex
==
C
.
INDEX_UNSET
)
{
Log
.
w
(
Log
.
w
(
TAG
,
TAG
,
...
@@ -662,9 +677,6 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -662,9 +677,6 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
adGroupIndex
=
expectedAdGroupIndex
;
adGroupIndex
=
expectedAdGroupIndex
;
adsManager
.
start
();
adsManager
.
start
();
}
}
if
(
DEBUG
)
{
Log
.
d
(
TAG
,
"loadAd in ad group "
+
adGroupIndex
);
}
int
adIndexInAdGroup
=
getAdIndexInAdGroupToLoad
(
adGroupIndex
);
int
adIndexInAdGroup
=
getAdIndexInAdGroupToLoad
(
adGroupIndex
);
if
(
adIndexInAdGroup
==
C
.
INDEX_UNSET
)
{
if
(
adIndexInAdGroup
==
C
.
INDEX_UNSET
)
{
Log
.
w
(
TAG
,
"Unexpected loadAd in an ad group with no remaining unavailable ads"
);
Log
.
w
(
TAG
,
"Unexpected loadAd in an ad group with no remaining unavailable ads"
);
...
@@ -693,6 +705,10 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -693,6 +705,10 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
if
(
DEBUG
)
{
if
(
DEBUG
)
{
Log
.
d
(
TAG
,
"playAd"
);
Log
.
d
(
TAG
,
"playAd"
);
}
}
if
(
adsManager
==
null
)
{
Log
.
w
(
TAG
,
"Ignoring playAd after release"
);
return
;
}
switch
(
imaAdState
)
{
switch
(
imaAdState
)
{
case
IMA_AD_STATE_PLAYING:
case
IMA_AD_STATE_PLAYING:
// IMA does not always call stopAd before resuming content.
// IMA does not always call stopAd before resuming content.
...
@@ -736,6 +752,10 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -736,6 +752,10 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
if
(
DEBUG
)
{
if
(
DEBUG
)
{
Log
.
d
(
TAG
,
"stopAd"
);
Log
.
d
(
TAG
,
"stopAd"
);
}
}
if
(
adsManager
==
null
)
{
Log
.
w
(
TAG
,
"Ignoring stopAd after release"
);
return
;
}
if
(
player
==
null
)
{
if
(
player
==
null
)
{
// Sometimes messages from IMA arrive after detaching the player. See [Internal: b/63801642].
// Sometimes messages from IMA arrive after detaching the player. See [Internal: b/63801642].
Log
.
w
(
TAG
,
"Unexpected stopAd while detached"
);
Log
.
w
(
TAG
,
"Unexpected stopAd while detached"
);
...
@@ -775,8 +795,8 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -775,8 +795,8 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
// Player.EventListener implementation.
// Player.EventListener implementation.
@Override
@Override
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
public
void
onTimelineChanged
(
@Player
.
TimelineChangeReason
int
reason
)
{
Timeline
timeline
,
@Nullable
Object
manifest
,
@Player
.
TimelineChangeReason
int
reason
)
{
if
(
reason
==
Player
.
TIMELINE_CHANGE_REASON_RESET
)
{
if
(
reason
==
Player
.
TIMELINE_CHANGE_REASON_RESET
)
{
// The player is being reset and this source will be released.
// The player is being reset and this source will be released.
return
;
return
;
...
@@ -1083,6 +1103,10 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -1083,6 +1103,10 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
Log
.
d
(
Log
.
d
(
TAG
,
"Prepare error for ad "
+
adIndexInAdGroup
+
" in group "
+
adGroupIndex
,
exception
);
TAG
,
"Prepare error for ad "
+
adIndexInAdGroup
+
" in group "
+
adGroupIndex
,
exception
);
}
}
if
(
adsManager
==
null
)
{
Log
.
w
(
TAG
,
"Ignoring ad prepare error after release"
);
return
;
}
if
(
imaAdState
==
IMA_AD_STATE_NONE
)
{
if
(
imaAdState
==
IMA_AD_STATE_NONE
)
{
// Send IMA a content position at the ad group so that it will try to play it, at which point
// Send IMA a content position at the ad group so that it will try to play it, at which point
// we can notify that it failed to load.
// we can notify that it failed to load.
...
@@ -1165,7 +1189,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
...
@@ -1165,7 +1189,7 @@ public final class ImaAdsLoader extends Player.DefaultEventListener implements A
Log
.
e
(
TAG
,
message
,
cause
);
Log
.
e
(
TAG
,
message
,
cause
);
// We can't recover from an unexpected error in general, so skip all remaining ads.
// We can't recover from an unexpected error in general, so skip all remaining ads.
if
(
adPlaybackState
==
null
)
{
if
(
adPlaybackState
==
null
)
{
adPlaybackState
=
new
AdPlaybackState
()
;
adPlaybackState
=
AdPlaybackState
.
NONE
;
}
else
{
}
else
{
for
(
int
i
=
0
;
i
<
adPlaybackState
.
adGroupCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
adPlaybackState
.
adGroupCount
;
i
++)
{
adPlaybackState
=
adPlaybackState
.
withSkippedAdGroup
(
i
);
adPlaybackState
=
adPlaybackState
.
withSkippedAdGroup
(
i
);
...
...
extensions/leanback/src/main/java/com/google/android/exoplayer2/ext/leanback/LeanbackPlayerAdapter.java
View file @
8cbcfd66
...
@@ -281,8 +281,8 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter {
...
@@ -281,8 +281,8 @@ public final class LeanbackPlayerAdapter extends PlayerAdapter {
}
}
@Override
@Override
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
public
void
onTimelineChanged
(
@TimelineChangeReason
int
reason
)
{
Timeline
timeline
,
@Nullable
Object
manifest
,
@TimelineChangeReason
int
reason
)
{
Callback
callback
=
getCallback
();
Callback
callback
=
getCallback
();
callback
.
onDurationChanged
(
LeanbackPlayerAdapter
.
this
);
callback
.
onDurationChanged
(
LeanbackPlayerAdapter
.
this
);
callback
.
onCurrentPositionChanged
(
LeanbackPlayerAdapter
.
this
);
callback
.
onCurrentPositionChanged
(
LeanbackPlayerAdapter
.
this
);
...
...
extensions/mediasession/src/main/java/com/google/android/exoplayer2/ext/mediasession/MediaSessionConnector.java
View file @
8cbcfd66
...
@@ -674,8 +674,8 @@ public final class MediaSessionConnector {
...
@@ -674,8 +674,8 @@ public final class MediaSessionConnector {
private
int
currentWindowCount
;
private
int
currentWindowCount
;
@Override
@Override
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
public
void
onTimelineChanged
(
@Player
.
TimelineChangeReason
int
reason
)
{
Timeline
timeline
,
@Nullable
Object
manifest
,
@Player
.
TimelineChangeReason
int
reason
)
{
int
windowCount
=
player
.
getCurrentTimeline
().
getWindowCount
();
int
windowCount
=
player
.
getCurrentTimeline
().
getWindowCount
();
int
windowIndex
=
player
.
getCurrentWindowIndex
();
int
windowIndex
=
player
.
getCurrentWindowIndex
();
if
(
queueNavigator
!=
null
)
{
if
(
queueNavigator
!=
null
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java
View file @
8cbcfd66
...
@@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo {
...
@@ -29,11 +29,11 @@ public final class ExoPlayerLibraryInfo {
/** The version of the library expressed as a string, for example "1.2.3". */
/** The version of the library expressed as a string, for example "1.2.3". */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa.
public
static
final
String
VERSION
=
"2.8.
2
"
;
public
static
final
String
VERSION
=
"2.8.
3
"
;
/** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */
/** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public
static
final
String
VERSION_SLASHY
=
"ExoPlayerLib/2.8.
2
"
;
public
static
final
String
VERSION_SLASHY
=
"ExoPlayerLib/2.8.
3
"
;
/**
/**
* The version of the library expressed as an integer, for example 1002003.
* The version of the library expressed as an integer, for example 1002003.
...
@@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo {
...
@@ -43,7 +43,7 @@ public final class ExoPlayerLibraryInfo {
* integer version 123045006 (123-045-006).
* integer version 123045006 (123-045-006).
*/
*/
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
// Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa.
public
static
final
int
VERSION_INT
=
200800
2
;
public
static
final
int
VERSION_INT
=
200800
3
;
/**
/**
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
* Whether the library was compiled with {@link com.google.android.exoplayer2.util.Assertions}
...
...
library/core/src/main/java/com/google/android/exoplayer2/Format.java
View file @
8cbcfd66
...
@@ -80,6 +80,13 @@ public final class Format implements Parcelable {
...
@@ -80,6 +80,13 @@ public final class Format implements Parcelable {
/** DRM initialization data if the stream is protected, or null otherwise. */
/** DRM initialization data if the stream is protected, or null otherwise. */
public
final
@Nullable
DrmInitData
drmInitData
;
public
final
@Nullable
DrmInitData
drmInitData
;
/**
* For samples that contain subsamples, this is an offset that should be added to subsample
* timestamps. A value of {@link #OFFSET_SAMPLE_RELATIVE} indicates that subsample timestamps are
* relative to the timestamps of their parent samples.
*/
public
final
long
subsampleOffsetUs
;
// Video specific.
// Video specific.
/**
/**
...
@@ -141,15 +148,6 @@ public final class Format implements Parcelable {
...
@@ -141,15 +148,6 @@ public final class Format implements Parcelable {
*/
*/
public
final
int
encoderPadding
;
public
final
int
encoderPadding
;
// Text specific.
/**
* For samples that contain subsamples, this is an offset that should be added to subsample
* timestamps. A value of {@link #OFFSET_SAMPLE_RELATIVE} indicates that subsample timestamps are
* relative to the timestamps of their parent samples.
*/
public
final
long
subsampleOffsetUs
;
// Audio and text specific.
// Audio and text specific.
/**
/**
...
...
library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java
View file @
8cbcfd66
...
@@ -228,11 +228,13 @@ import com.google.android.exoplayer2.util.Assertions;
...
@@ -228,11 +228,13 @@ import com.google.android.exoplayer2.util.Assertions;
reading
=
playing
.
next
;
reading
=
playing
.
next
;
}
}
playing
.
release
();
playing
.
release
();
playing
=
playing
.
next
;
length
--;
length
--;
if
(
length
==
0
)
{
if
(
length
==
0
)
{
loading
=
null
;
loading
=
null
;
oldFrontPeriodUid
=
playing
.
uid
;
oldFrontPeriodWindowSequenceNumber
=
playing
.
info
.
id
.
windowSequenceNumber
;
}
}
playing
=
playing
.
next
;
}
else
{
}
else
{
playing
=
loading
;
playing
=
loading
;
reading
=
loading
;
reading
=
loading
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/Player.java
View file @
8cbcfd66
...
@@ -191,7 +191,8 @@ public interface Player {
...
@@ -191,7 +191,8 @@ public interface Player {
* @param manifest The latest manifest. May be null.
* @param manifest The latest manifest. May be null.
* @param reason The {@link TimelineChangeReason} responsible for this timeline change.
* @param reason The {@link TimelineChangeReason} responsible for this timeline change.
*/
*/
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
@TimelineChangeReason
int
reason
);
void
onTimelineChanged
(
Timeline
timeline
,
@Nullable
Object
manifest
,
@TimelineChangeReason
int
reason
);
/**
/**
* Called when the available or selected tracks change.
* Called when the available or selected tracks change.
...
@@ -281,8 +282,8 @@ public interface Player {
...
@@ -281,8 +282,8 @@ public interface Player {
abstract
class
DefaultEventListener
implements
EventListener
{
abstract
class
DefaultEventListener
implements
EventListener
{
@Override
@Override
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
public
void
onTimelineChanged
(
@TimelineChangeReason
int
reason
)
{
Timeline
timeline
,
@Nullable
Object
manifest
,
@TimelineChangeReason
int
reason
)
{
// Call deprecated version. Otherwise, do nothing.
// Call deprecated version. Otherwise, do nothing.
onTimelineChanged
(
timeline
,
manifest
);
onTimelineChanged
(
timeline
,
manifest
);
}
}
...
@@ -337,7 +338,7 @@ public interface Player {
...
@@ -337,7 +338,7 @@ public interface Player {
* instead.
* instead.
*/
*/
@Deprecated
@Deprecated
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
)
{
public
void
onTimelineChanged
(
Timeline
timeline
,
@Nullable
Object
manifest
)
{
// Do nothing.
// Do nothing.
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/analytics/AnalyticsCollector.java
View file @
8cbcfd66
...
@@ -420,7 +420,7 @@ public class AnalyticsCollector
...
@@ -420,7 +420,7 @@ public class AnalyticsCollector
@Override
@Override
public
final
void
onTimelineChanged
(
public
final
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
@Player
.
TimelineChangeReason
int
reason
)
{
Timeline
timeline
,
@Nullable
Object
manifest
,
@Player
.
TimelineChangeReason
int
reason
)
{
mediaPeriodQueueTracker
.
onTimelineChanged
(
timeline
);
mediaPeriodQueueTracker
.
onTimelineChanged
(
timeline
);
EventTime
eventTime
=
generatePlayingMediaPeriodEventTime
();
EventTime
eventTime
=
generatePlayingMediaPeriodEventTime
();
for
(
AnalyticsListener
listener
:
listeners
)
{
for
(
AnalyticsListener
listener
:
listeners
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/audio/SilenceSkippingAudioProcessor.java
View file @
8cbcfd66
...
@@ -33,12 +33,12 @@ public final class SilenceSkippingAudioProcessor implements AudioProcessor {
...
@@ -33,12 +33,12 @@ public final class SilenceSkippingAudioProcessor implements AudioProcessor {
* The minimum duration of audio that must be below {@link #SILENCE_THRESHOLD_LEVEL} to classify
* The minimum duration of audio that must be below {@link #SILENCE_THRESHOLD_LEVEL} to classify
* that part of audio as silent, in microseconds.
* that part of audio as silent, in microseconds.
*/
*/
private
static
final
long
MINIMUM_SILENCE_DURATION_US
=
1
0
0_000
;
private
static
final
long
MINIMUM_SILENCE_DURATION_US
=
1
5
0_000
;
/**
/**
* The duration of silence by which to extend non-silent sections, in microseconds. The value must
* The duration of silence by which to extend non-silent sections, in microseconds. The value must
* not exceed {@link #MINIMUM_SILENCE_DURATION_US}.
* not exceed {@link #MINIMUM_SILENCE_DURATION_US}.
*/
*/
private
static
final
long
PADDING_SILENCE_US
=
1
0_000
;
private
static
final
long
PADDING_SILENCE_US
=
2
0_000
;
/**
/**
* The absolute level below which an individual PCM sample is classified as silent. Note: the
* The absolute level below which an individual PCM sample is classified as silent. Note: the
* specified value will be rounded so that the threshold check only depends on the more
* specified value will be rounded so that the threshold check only depends on the more
...
...
library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSession.java
View file @
8cbcfd66
...
@@ -22,11 +22,12 @@ import android.os.Handler;
...
@@ -22,11 +22,12 @@ import android.os.Handler;
import
android.os.HandlerThread
;
import
android.os.HandlerThread
;
import
android.os.Looper
;
import
android.os.Looper
;
import
android.os.Message
;
import
android.os.Message
;
import
android.support.annotation.Nullable
;
import
android.util.Log
;
import
android.util.Log
;
import
android.util.Pair
;
import
android.util.Pair
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.drm.DefaultDrmSessionEventListener.EventDispatcher
;
import
com.google.android.exoplayer2.drm.DefaultDrmSessionEventListener.EventDispatcher
;
import
com.google.android.exoplayer2.drm.
ExoMediaDrm.DefaultKeyRequest
;
import
com.google.android.exoplayer2.drm.
DrmInitData.SchemeData
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest
;
import
java.util.Arrays
;
import
java.util.Arrays
;
...
@@ -77,8 +78,7 @@ import java.util.UUID;
...
@@ -77,8 +78,7 @@ import java.util.UUID;
private
final
ExoMediaDrm
<
T
>
mediaDrm
;
private
final
ExoMediaDrm
<
T
>
mediaDrm
;
private
final
ProvisioningManager
<
T
>
provisioningManager
;
private
final
ProvisioningManager
<
T
>
provisioningManager
;
private
final
byte
[]
initData
;
private
final
SchemeData
schemeData
;
private
final
String
mimeType
;
private
final
@DefaultDrmSessionManager
.
Mode
int
mode
;
private
final
@DefaultDrmSessionManager
.
Mode
int
mode
;
private
final
HashMap
<
String
,
String
>
optionalKeyRequestParameters
;
private
final
HashMap
<
String
,
String
>
optionalKeyRequestParameters
;
private
final
EventDispatcher
eventDispatcher
;
private
final
EventDispatcher
eventDispatcher
;
...
@@ -97,15 +97,20 @@ import java.util.UUID;
...
@@ -97,15 +97,20 @@ import java.util.UUID;
private
byte
[]
sessionId
;
private
byte
[]
sessionId
;
private
byte
[]
offlineLicenseKeySetId
;
private
byte
[]
offlineLicenseKeySetId
;
private
Object
currentKeyRequest
;
private
Object
currentProvisionRequest
;
/**
/**
* Instantiates a new DRM session.
* Instantiates a new DRM session.
*
*
* @param uuid The UUID of the drm scheme.
* @param uuid The UUID of the drm scheme.
* @param mediaDrm The media DRM.
* @param mediaDrm The media DRM.
* @param provisioningManager The manager for provisioning.
* @param provisioningManager The manager for provisioning.
* @param initData The DRM init data.
* @param schemeData The DRM data for this session, or null if a {@code offlineLicenseKeySetId} is
* provided.
* @param mode The DRM mode.
* @param mode The DRM mode.
* @param offlineLicenseKeySetId The offlineLicense KeySetId.
* @param offlineLicenseKeySetId The offline license key set identifier, or null when not using
* offline keys.
* @param optionalKeyRequestParameters The optional key request parameters.
* @param optionalKeyRequestParameters The optional key request parameters.
* @param callback The media DRM callback.
* @param callback The media DRM callback.
* @param playbackLooper The playback looper.
* @param playbackLooper The playback looper.
...
@@ -117,10 +122,9 @@ import java.util.UUID;
...
@@ -117,10 +122,9 @@ import java.util.UUID;
UUID
uuid
,
UUID
uuid
,
ExoMediaDrm
<
T
>
mediaDrm
,
ExoMediaDrm
<
T
>
mediaDrm
,
ProvisioningManager
<
T
>
provisioningManager
,
ProvisioningManager
<
T
>
provisioningManager
,
byte
[]
initData
,
@Nullable
SchemeData
schemeData
,
String
mimeType
,
@DefaultDrmSessionManager
.
Mode
int
mode
,
@DefaultDrmSessionManager
.
Mode
int
mode
,
byte
[]
offlineLicenseKeySetId
,
@Nullable
byte
[]
offlineLicenseKeySetId
,
HashMap
<
String
,
String
>
optionalKeyRequestParameters
,
HashMap
<
String
,
String
>
optionalKeyRequestParameters
,
MediaDrmCallback
callback
,
MediaDrmCallback
callback
,
Looper
playbackLooper
,
Looper
playbackLooper
,
...
@@ -131,6 +135,7 @@ import java.util.UUID;
...
@@ -131,6 +135,7 @@ import java.util.UUID;
this
.
mediaDrm
=
mediaDrm
;
this
.
mediaDrm
=
mediaDrm
;
this
.
mode
=
mode
;
this
.
mode
=
mode
;
this
.
offlineLicenseKeySetId
=
offlineLicenseKeySetId
;
this
.
offlineLicenseKeySetId
=
offlineLicenseKeySetId
;
this
.
schemeData
=
offlineLicenseKeySetId
==
null
?
schemeData
:
null
;
this
.
optionalKeyRequestParameters
=
optionalKeyRequestParameters
;
this
.
optionalKeyRequestParameters
=
optionalKeyRequestParameters
;
this
.
callback
=
callback
;
this
.
callback
=
callback
;
this
.
initialDrmRequestRetryCount
=
initialDrmRequestRetryCount
;
this
.
initialDrmRequestRetryCount
=
initialDrmRequestRetryCount
;
...
@@ -141,14 +146,6 @@ import java.util.UUID;
...
@@ -141,14 +146,6 @@ import java.util.UUID;
requestHandlerThread
=
new
HandlerThread
(
"DrmRequestHandler"
);
requestHandlerThread
=
new
HandlerThread
(
"DrmRequestHandler"
);
requestHandlerThread
.
start
();
requestHandlerThread
.
start
();
postRequestHandler
=
new
PostRequestHandler
(
requestHandlerThread
.
getLooper
());
postRequestHandler
=
new
PostRequestHandler
(
requestHandlerThread
.
getLooper
());
if
(
offlineLicenseKeySetId
==
null
)
{
this
.
initData
=
initData
;
this
.
mimeType
=
mimeType
;
}
else
{
this
.
initData
=
null
;
this
.
mimeType
=
null
;
}
}
}
// Life cycle.
// Life cycle.
...
@@ -177,6 +174,8 @@ import java.util.UUID;
...
@@ -177,6 +174,8 @@ import java.util.UUID;
requestHandlerThread
=
null
;
requestHandlerThread
=
null
;
mediaCrypto
=
null
;
mediaCrypto
=
null
;
lastException
=
null
;
lastException
=
null
;
currentKeyRequest
=
null
;
currentProvisionRequest
=
null
;
if
(
sessionId
!=
null
)
{
if
(
sessionId
!=
null
)
{
mediaDrm
.
closeSession
(
sessionId
);
mediaDrm
.
closeSession
(
sessionId
);
sessionId
=
null
;
sessionId
=
null
;
...
@@ -187,18 +186,42 @@ import java.util.UUID;
...
@@ -187,18 +186,42 @@ import java.util.UUID;
}
}
public
boolean
hasInitData
(
byte
[]
initData
)
{
public
boolean
hasInitData
(
byte
[]
initData
)
{
return
Arrays
.
equals
(
this
.
initData
,
initData
);
return
Arrays
.
equals
(
schemeData
!=
null
?
schemeData
.
data
:
null
,
initData
);
}
}
public
boolean
hasSessionId
(
byte
[]
sessionId
)
{
public
boolean
hasSessionId
(
byte
[]
sessionId
)
{
return
Arrays
.
equals
(
this
.
sessionId
,
sessionId
);
return
Arrays
.
equals
(
this
.
sessionId
,
sessionId
);
}
}
@SuppressWarnings
(
"deprecation"
)
public
void
onMediaDrmEvent
(
int
what
)
{
if
(!
isOpen
())
{
return
;
}
switch
(
what
)
{
case
ExoMediaDrm
.
EVENT_KEY_REQUIRED
:
doLicense
(
false
);
break
;
case
ExoMediaDrm
.
EVENT_KEY_EXPIRED
:
// When an already expired key is loaded MediaDrm sends this event immediately. Ignore
// this event if the state isn't STATE_OPENED_WITH_KEYS yet which means we're still
// waiting for key response.
onKeysExpired
();
break
;
case
ExoMediaDrm
.
EVENT_PROVISION_REQUIRED
:
state
=
STATE_OPENED
;
provisioningManager
.
provisionRequired
(
this
);
break
;
default
:
break
;
}
}
// Provisioning implementation.
// Provisioning implementation.
public
void
provision
()
{
public
void
provision
()
{
ProvisionRequest
r
equest
=
mediaDrm
.
getProvisionRequest
();
currentProvisionR
equest
=
mediaDrm
.
getProvisionRequest
();
postRequestHandler
.
obtainMessage
(
MSG_PROVISION
,
request
,
true
).
sendToTarget
(
);
postRequestHandler
.
post
(
MSG_PROVISION
,
currentProvisionRequest
,
/* allowRetry= */
true
);
}
}
public
void
onProvisionCompleted
()
{
public
void
onProvisionCompleted
()
{
...
@@ -271,11 +294,12 @@ import java.util.UUID;
...
@@ -271,11 +294,12 @@ import java.util.UUID;
return
false
;
return
false
;
}
}
private
void
onProvisionResponse
(
Object
response
)
{
private
void
onProvisionResponse
(
Object
re
quest
,
Object
re
sponse
)
{
if
(
state
!=
STATE_OPENING
&&
!
isOpen
(
))
{
if
(
request
!=
currentProvisionRequest
||
(
state
!=
STATE_OPENING
&&
!
isOpen
()
))
{
// This event is stale.
// This event is stale.
return
;
return
;
}
}
currentProvisionRequest
=
null
;
if
(
response
instanceof
Exception
)
{
if
(
response
instanceof
Exception
)
{
provisioningManager
.
onProvisionError
((
Exception
)
response
);
provisioningManager
.
onProvisionError
((
Exception
)
response
);
...
@@ -356,24 +380,30 @@ import java.util.UUID;
...
@@ -356,24 +380,30 @@ import java.util.UUID;
private
void
postKeyRequest
(
int
type
,
boolean
allowRetry
)
{
private
void
postKeyRequest
(
int
type
,
boolean
allowRetry
)
{
byte
[]
scope
=
type
==
ExoMediaDrm
.
KEY_TYPE_RELEASE
?
offlineLicenseKeySetId
:
sessionId
;
byte
[]
scope
=
type
==
ExoMediaDrm
.
KEY_TYPE_RELEASE
?
offlineLicenseKeySetId
:
sessionId
;
byte
[]
initData
=
null
;
String
mimeType
=
null
;
String
licenseServerUrl
=
null
;
if
(
schemeData
!=
null
)
{
initData
=
schemeData
.
data
;
mimeType
=
schemeData
.
mimeType
;
licenseServerUrl
=
schemeData
.
licenseServerUrl
;
}
try
{
try
{
KeyRequest
request
=
mediaDrm
.
getKeyRequest
(
scope
,
initData
,
mimeType
,
type
,
KeyRequest
mediaDrmKeyRequest
=
optionalKeyRequestParameters
);
mediaDrm
.
getKeyRequest
(
scope
,
initData
,
mimeType
,
type
,
optionalKeyRequestParameters
);
if
(
C
.
CLEARKEY_UUID
.
equals
(
uuid
))
{
currentKeyRequest
=
Pair
.
create
(
mediaDrmKeyRequest
,
licenseServerUrl
);
request
=
new
DefaultKeyRequest
(
ClearKeyUtil
.
adjustRequestData
(
request
.
getData
()),
postRequestHandler
.
post
(
MSG_KEYS
,
currentKeyRequest
,
allowRetry
);
request
.
getDefaultUrl
());
}
postRequestHandler
.
obtainMessage
(
MSG_KEYS
,
request
,
allowRetry
).
sendToTarget
();
}
catch
(
Exception
e
)
{
}
catch
(
Exception
e
)
{
onKeysError
(
e
);
onKeysError
(
e
);
}
}
}
}
private
void
onKeyResponse
(
Object
response
)
{
private
void
onKeyResponse
(
Object
re
quest
,
Object
re
sponse
)
{
if
(!
isOpen
())
{
if
(
request
!=
currentKeyRequest
||
!
isOpen
())
{
// This event is stale.
// This event is stale.
return
;
return
;
}
}
currentKeyRequest
=
null
;
if
(
response
instanceof
Exception
)
{
if
(
response
instanceof
Exception
)
{
onKeysError
((
Exception
)
response
);
onKeysError
((
Exception
)
response
);
...
@@ -382,9 +412,6 @@ import java.util.UUID;
...
@@ -382,9 +412,6 @@ import java.util.UUID;
try
{
try
{
byte
[]
responseData
=
(
byte
[])
response
;
byte
[]
responseData
=
(
byte
[])
response
;
if
(
C
.
CLEARKEY_UUID
.
equals
(
uuid
))
{
responseData
=
ClearKeyUtil
.
adjustResponseData
(
responseData
);
}
if
(
mode
==
DefaultDrmSessionManager
.
MODE_RELEASE
)
{
if
(
mode
==
DefaultDrmSessionManager
.
MODE_RELEASE
)
{
mediaDrm
.
provideKeyResponse
(
offlineLicenseKeySetId
,
responseData
);
mediaDrm
.
provideKeyResponse
(
offlineLicenseKeySetId
,
responseData
);
eventDispatcher
.
drmKeysRemoved
();
eventDispatcher
.
drmKeysRemoved
();
...
@@ -430,30 +457,7 @@ import java.util.UUID;
...
@@ -430,30 +457,7 @@ import java.util.UUID;
return
state
==
STATE_OPENED
||
state
==
STATE_OPENED_WITH_KEYS
;
return
state
==
STATE_OPENED
||
state
==
STATE_OPENED_WITH_KEYS
;
}
}
@SuppressWarnings
(
"deprecation"
)
// Internal classes.
public
void
onMediaDrmEvent
(
int
what
)
{
if
(!
isOpen
())
{
return
;
}
switch
(
what
)
{
case
ExoMediaDrm
.
EVENT_KEY_REQUIRED
:
doLicense
(
false
);
break
;
case
ExoMediaDrm
.
EVENT_KEY_EXPIRED
:
// When an already expired key is loaded MediaDrm sends this event immediately. Ignore
// this event if the state isn't STATE_OPENED_WITH_KEYS yet which means we're still
// waiting for key response.
onKeysExpired
();
break
;
case
ExoMediaDrm
.
EVENT_PROVISION_REQUIRED
:
state
=
STATE_OPENED
;
provisioningManager
.
provisionRequired
(
this
);
break
;
default
:
break
;
}
}
@SuppressLint
(
"HandlerLeak"
)
@SuppressLint
(
"HandlerLeak"
)
private
class
PostResponseHandler
extends
Handler
{
private
class
PostResponseHandler
extends
Handler
{
...
@@ -464,12 +468,15 @@ import java.util.UUID;
...
@@ -464,12 +468,15 @@ import java.util.UUID;
@Override
@Override
public
void
handleMessage
(
Message
msg
)
{
public
void
handleMessage
(
Message
msg
)
{
Pair
<?,
?>
requestAndResponse
=
(
Pair
<?,
?>)
msg
.
obj
;
Object
request
=
requestAndResponse
.
first
;
Object
response
=
requestAndResponse
.
second
;
switch
(
msg
.
what
)
{
switch
(
msg
.
what
)
{
case
MSG_PROVISION:
case
MSG_PROVISION:
onProvisionResponse
(
msg
.
obj
);
onProvisionResponse
(
request
,
response
);
break
;
break
;
case
MSG_KEYS:
case
MSG_KEYS:
onKeyResponse
(
msg
.
obj
);
onKeyResponse
(
request
,
response
);
break
;
break
;
default
:
default
:
break
;
break
;
...
@@ -486,21 +493,27 @@ import java.util.UUID;
...
@@ -486,21 +493,27 @@ import java.util.UUID;
super
(
backgroundLooper
);
super
(
backgroundLooper
);
}
}
Message
obtainMessage
(
int
what
,
Object
object
,
boolean
allowRetry
)
{
void
post
(
int
what
,
Object
request
,
boolean
allowRetry
)
{
return
obtainMessage
(
what
,
allowRetry
?
1
:
0
/* allow retry*/
,
0
/* error count */
,
int
allowRetryInt
=
allowRetry
?
1
:
0
;
object
);
int
errorCount
=
0
;
obtainMessage
(
what
,
allowRetryInt
,
errorCount
,
request
).
sendToTarget
();
}
}
@Override
@Override
@SuppressWarnings
(
"unchecked"
)
public
void
handleMessage
(
Message
msg
)
{
public
void
handleMessage
(
Message
msg
)
{
Object
request
=
msg
.
obj
;
Object
response
;
Object
response
;
try
{
try
{
switch
(
msg
.
what
)
{
switch
(
msg
.
what
)
{
case
MSG_PROVISION:
case
MSG_PROVISION:
response
=
callback
.
executeProvisionRequest
(
uuid
,
(
ProvisionRequest
)
msg
.
obj
);
response
=
callback
.
executeProvisionRequest
(
uuid
,
(
ProvisionRequest
)
request
);
break
;
break
;
case
MSG_KEYS:
case
MSG_KEYS:
response
=
callback
.
executeKeyRequest
(
uuid
,
(
KeyRequest
)
msg
.
obj
);
Pair
<
KeyRequest
,
String
>
keyRequest
=
(
Pair
<
KeyRequest
,
String
>)
request
;
KeyRequest
mediaDrmKeyRequest
=
keyRequest
.
first
;
String
licenseServerUrl
=
keyRequest
.
second
;
response
=
callback
.
executeKeyRequest
(
uuid
,
mediaDrmKeyRequest
,
licenseServerUrl
);
break
;
break
;
default
:
default
:
throw
new
RuntimeException
();
throw
new
RuntimeException
();
...
@@ -511,7 +524,7 @@ import java.util.UUID;
...
@@ -511,7 +524,7 @@ import java.util.UUID;
}
}
response
=
e
;
response
=
e
;
}
}
postResponseHandler
.
obtainMessage
(
msg
.
what
,
response
).
sendToTarget
();
postResponseHandler
.
obtainMessage
(
msg
.
what
,
Pair
.
create
(
request
,
response
)
).
sendToTarget
();
}
}
private
boolean
maybeRetryRequest
(
Message
originalMsg
)
{
private
boolean
maybeRetryRequest
(
Message
originalMsg
)
{
...
@@ -534,5 +547,4 @@ import java.util.UUID;
...
@@ -534,5 +547,4 @@ import java.util.UUID;
}
}
}
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/drm/DefaultDrmSessionManager.java
View file @
8cbcfd66
...
@@ -32,7 +32,6 @@ import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
...
@@ -32,7 +32,6 @@ import com.google.android.exoplayer2.drm.DrmSession.DrmSessionException;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.OnEventListener
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.OnEventListener
;
import
com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil
;
import
com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.android.exoplayer2.util.Util
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.Retention
;
import
java.lang.annotation.RetentionPolicy
;
import
java.lang.annotation.RetentionPolicy
;
...
@@ -89,7 +88,6 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
...
@@ -89,7 +88,6 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
public
static
final
int
INITIAL_DRM_REQUEST_RETRY_COUNT
=
3
;
public
static
final
int
INITIAL_DRM_REQUEST_RETRY_COUNT
=
3
;
private
static
final
String
TAG
=
"DefaultDrmSessionMgr"
;
private
static
final
String
TAG
=
"DefaultDrmSessionMgr"
;
private
static
final
String
CENC_SCHEME_MIME_TYPE
=
"cenc"
;
private
final
UUID
uuid
;
private
final
UUID
uuid
;
private
final
ExoMediaDrm
<
T
>
mediaDrm
;
private
final
ExoMediaDrm
<
T
>
mediaDrm
;
...
@@ -509,17 +507,14 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
...
@@ -509,17 +507,14 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
}
}
}
}
byte
[]
initData
=
null
;
SchemeData
schemeData
=
null
;
String
mimeType
=
null
;
if
(
offlineLicenseKeySetId
==
null
)
{
if
(
offlineLicenseKeySetId
==
null
)
{
SchemeData
d
ata
=
getSchemeData
(
drmInitData
,
uuid
,
false
);
schemeD
ata
=
getSchemeData
(
drmInitData
,
uuid
,
false
);
if
(
d
ata
==
null
)
{
if
(
schemeD
ata
==
null
)
{
final
MissingSchemeDataException
error
=
new
MissingSchemeDataException
(
uuid
);
final
MissingSchemeDataException
error
=
new
MissingSchemeDataException
(
uuid
);
eventDispatcher
.
drmSessionManagerError
(
error
);
eventDispatcher
.
drmSessionManagerError
(
error
);
return
new
ErrorStateDrmSession
<>(
new
DrmSessionException
(
error
));
return
new
ErrorStateDrmSession
<>(
new
DrmSessionException
(
error
));
}
}
initData
=
getSchemeInitData
(
data
,
uuid
);
mimeType
=
getSchemeMimeType
(
data
,
uuid
);
}
}
DefaultDrmSession
<
T
>
session
;
DefaultDrmSession
<
T
>
session
;
...
@@ -528,6 +523,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
...
@@ -528,6 +523,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
}
else
{
}
else
{
// Only use an existing session if it has matching init data.
// Only use an existing session if it has matching init data.
session
=
null
;
session
=
null
;
byte
[]
initData
=
schemeData
!=
null
?
schemeData
.
data
:
null
;
for
(
DefaultDrmSession
<
T
>
existingSession
:
sessions
)
{
for
(
DefaultDrmSession
<
T
>
existingSession
:
sessions
)
{
if
(
existingSession
.
hasInitData
(
initData
))
{
if
(
existingSession
.
hasInitData
(
initData
))
{
session
=
existingSession
;
session
=
existingSession
;
...
@@ -543,8 +539,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
...
@@ -543,8 +539,7 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
uuid
,
uuid
,
mediaDrm
,
mediaDrm
,
this
,
this
,
initData
,
schemeData
,
mimeType
,
mode
,
mode
,
offlineLicenseKeySetId
,
offlineLicenseKeySetId
,
optionalKeyRequestParameters
,
optionalKeyRequestParameters
,
...
@@ -650,31 +645,6 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
...
@@ -650,31 +645,6 @@ public class DefaultDrmSessionManager<T extends ExoMediaCrypto> implements DrmSe
return
matchingSchemeDatas
.
get
(
0
);
return
matchingSchemeDatas
.
get
(
0
);
}
}
private
static
byte
[]
getSchemeInitData
(
SchemeData
data
,
UUID
uuid
)
{
byte
[]
schemeInitData
=
data
.
data
;
if
(
Util
.
SDK_INT
<
21
)
{
// Prior to L the Widevine CDM required data to be extracted from the PSSH atom.
byte
[]
psshData
=
PsshAtomUtil
.
parseSchemeSpecificData
(
schemeInitData
,
uuid
);
if
(
psshData
==
null
)
{
// Extraction failed. schemeData isn't a Widevine PSSH atom, so leave it unchanged.
}
else
{
schemeInitData
=
psshData
;
}
}
return
schemeInitData
;
}
private
static
String
getSchemeMimeType
(
SchemeData
data
,
UUID
uuid
)
{
String
schemeMimeType
=
data
.
mimeType
;
if
(
Util
.
SDK_INT
<
26
&&
C
.
CLEARKEY_UUID
.
equals
(
uuid
)
&&
(
MimeTypes
.
VIDEO_MP4
.
equals
(
schemeMimeType
)
||
MimeTypes
.
AUDIO_MP4
.
equals
(
schemeMimeType
)))
{
// Prior to API level 26 the ClearKey CDM only accepted "cenc" as the scheme for MP4.
schemeMimeType
=
CENC_SCHEME_MIME_TYPE
;
}
return
schemeMimeType
;
}
@SuppressLint
(
"HandlerLeak"
)
@SuppressLint
(
"HandlerLeak"
)
private
class
MediaDrmHandler
extends
Handler
{
private
class
MediaDrmHandler
extends
Handler
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/drm/DrmInitData.java
View file @
8cbcfd66
...
@@ -266,9 +266,9 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
...
@@ -266,9 +266,9 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
* applies to all schemes).
* applies to all schemes).
*/
*/
private
final
UUID
uuid
;
private
final
UUID
uuid
;
/**
/**
The URL of the server to which license requests should be made. May be null if unknown. */
* The mimeType of {@link #data}.
public
final
@Nullable
String
licenseServerUrl
;
*/
/** The mimeType of {@link #data}.
*/
public
final
String
mimeType
;
public
final
String
mimeType
;
/**
/**
* The initialization data. May be null for scheme support checks only.
* The initialization data. May be null for scheme support checks only.
...
@@ -297,7 +297,25 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
...
@@ -297,7 +297,25 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
* @param requiresSecureDecryption See {@link #requiresSecureDecryption}.
* @param requiresSecureDecryption See {@link #requiresSecureDecryption}.
*/
*/
public
SchemeData
(
UUID
uuid
,
String
mimeType
,
byte
[]
data
,
boolean
requiresSecureDecryption
)
{
public
SchemeData
(
UUID
uuid
,
String
mimeType
,
byte
[]
data
,
boolean
requiresSecureDecryption
)
{
this
(
uuid
,
/* licenseServerUrl= */
null
,
mimeType
,
data
,
requiresSecureDecryption
);
}
/**
* @param uuid The {@link UUID} of the DRM scheme, or {@link C#UUID_NIL} if the data is
* universal (i.e. applies to all schemes).
* @param licenseServerUrl See {@link #licenseServerUrl}.
* @param mimeType See {@link #mimeType}.
* @param data See {@link #data}.
* @param requiresSecureDecryption See {@link #requiresSecureDecryption}.
*/
public
SchemeData
(
UUID
uuid
,
@Nullable
String
licenseServerUrl
,
String
mimeType
,
byte
[]
data
,
boolean
requiresSecureDecryption
)
{
this
.
uuid
=
Assertions
.
checkNotNull
(
uuid
);
this
.
uuid
=
Assertions
.
checkNotNull
(
uuid
);
this
.
licenseServerUrl
=
licenseServerUrl
;
this
.
mimeType
=
Assertions
.
checkNotNull
(
mimeType
);
this
.
mimeType
=
Assertions
.
checkNotNull
(
mimeType
);
this
.
data
=
data
;
this
.
data
=
data
;
this
.
requiresSecureDecryption
=
requiresSecureDecryption
;
this
.
requiresSecureDecryption
=
requiresSecureDecryption
;
...
@@ -305,6 +323,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
...
@@ -305,6 +323,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
/* package */
SchemeData
(
Parcel
in
)
{
/* package */
SchemeData
(
Parcel
in
)
{
uuid
=
new
UUID
(
in
.
readLong
(),
in
.
readLong
());
uuid
=
new
UUID
(
in
.
readLong
(),
in
.
readLong
());
licenseServerUrl
=
in
.
readString
();
mimeType
=
in
.
readString
();
mimeType
=
in
.
readString
();
data
=
in
.
createByteArray
();
data
=
in
.
createByteArray
();
requiresSecureDecryption
=
in
.
readByte
()
!=
0
;
requiresSecureDecryption
=
in
.
readByte
()
!=
0
;
...
@@ -346,7 +365,9 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
...
@@ -346,7 +365,9 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
return
true
;
return
true
;
}
}
SchemeData
other
=
(
SchemeData
)
obj
;
SchemeData
other
=
(
SchemeData
)
obj
;
return
mimeType
.
equals
(
other
.
mimeType
)
&&
Util
.
areEqual
(
uuid
,
other
.
uuid
)
return
Util
.
areEqual
(
licenseServerUrl
,
other
.
licenseServerUrl
)
&&
Util
.
areEqual
(
mimeType
,
other
.
mimeType
)
&&
Util
.
areEqual
(
uuid
,
other
.
uuid
)
&&
Arrays
.
equals
(
data
,
other
.
data
);
&&
Arrays
.
equals
(
data
,
other
.
data
);
}
}
...
@@ -354,6 +375,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
...
@@ -354,6 +375,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
public
int
hashCode
()
{
public
int
hashCode
()
{
if
(
hashCode
==
0
)
{
if
(
hashCode
==
0
)
{
int
result
=
uuid
.
hashCode
();
int
result
=
uuid
.
hashCode
();
result
=
31
*
result
+
(
licenseServerUrl
==
null
?
0
:
licenseServerUrl
.
hashCode
());
result
=
31
*
result
+
mimeType
.
hashCode
();
result
=
31
*
result
+
mimeType
.
hashCode
();
result
=
31
*
result
+
Arrays
.
hashCode
(
data
);
result
=
31
*
result
+
Arrays
.
hashCode
(
data
);
hashCode
=
result
;
hashCode
=
result
;
...
@@ -372,6 +394,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
...
@@ -372,6 +394,7 @@ public final class DrmInitData implements Comparator<SchemeData>, Parcelable {
public
void
writeToParcel
(
Parcel
dest
,
int
flags
)
{
public
void
writeToParcel
(
Parcel
dest
,
int
flags
)
{
dest
.
writeLong
(
uuid
.
getMostSignificantBits
());
dest
.
writeLong
(
uuid
.
getMostSignificantBits
());
dest
.
writeLong
(
uuid
.
getLeastSignificantBits
());
dest
.
writeLong
(
uuid
.
getLeastSignificantBits
());
dest
.
writeString
(
licenseServerUrl
);
dest
.
writeString
(
mimeType
);
dest
.
writeString
(
mimeType
);
dest
.
writeByteArray
(
data
);
dest
.
writeByteArray
(
data
);
dest
.
writeByte
((
byte
)
(
requiresSecureDecryption
?
1
:
0
));
dest
.
writeByte
((
byte
)
(
requiresSecureDecryption
?
1
:
0
));
...
...
library/core/src/main/java/com/google/android/exoplayer2/drm/FrameworkMediaDrm.java
View file @
8cbcfd66
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
.
drm
;
package
com
.
google
.
android
.
exoplayer2
.
drm
;
import
android.annotation.SuppressLint
;
import
android.annotation.TargetApi
;
import
android.annotation.TargetApi
;
import
android.media.DeniedByServerException
;
import
android.media.DeniedByServerException
;
import
android.media.MediaCrypto
;
import
android.media.MediaCrypto
;
...
@@ -26,7 +27,9 @@ import android.media.UnsupportedSchemeException;
...
@@ -26,7 +27,9 @@ import android.media.UnsupportedSchemeException;
import
android.support.annotation.NonNull
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.extractor.mp4.PsshAtomUtil
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.MimeTypes
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.android.exoplayer2.util.Util
;
import
java.util.ArrayList
;
import
java.util.ArrayList
;
import
java.util.HashMap
;
import
java.util.HashMap
;
...
@@ -40,6 +43,8 @@ import java.util.UUID;
...
@@ -40,6 +43,8 @@ import java.util.UUID;
@TargetApi
(
23
)
@TargetApi
(
23
)
public
final
class
FrameworkMediaDrm
implements
ExoMediaDrm
<
FrameworkMediaCrypto
>
{
public
final
class
FrameworkMediaDrm
implements
ExoMediaDrm
<
FrameworkMediaCrypto
>
{
private
static
final
String
CENC_SCHEME_MIME_TYPE
=
"cenc"
;
private
final
UUID
uuid
;
private
final
UUID
uuid
;
private
final
MediaDrm
mediaDrm
;
private
final
MediaDrm
mediaDrm
;
...
@@ -60,6 +65,7 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
...
@@ -60,6 +65,7 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
}
}
}
}
@SuppressLint
(
"WrongConstant"
)
private
FrameworkMediaDrm
(
UUID
uuid
)
throws
UnsupportedSchemeException
{
private
FrameworkMediaDrm
(
UUID
uuid
)
throws
UnsupportedSchemeException
{
Assertions
.
checkNotNull
(
uuid
);
Assertions
.
checkNotNull
(
uuid
);
Assertions
.
checkArgument
(!
C
.
COMMON_PSSH_UUID
.
equals
(
uuid
),
"Use C.CLEARKEY_UUID instead"
);
Assertions
.
checkArgument
(!
C
.
COMMON_PSSH_UUID
.
equals
(
uuid
),
"Use C.CLEARKEY_UUID instead"
);
...
@@ -67,6 +73,9 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
...
@@ -67,6 +73,9 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
uuid
=
Util
.
SDK_INT
<
27
&&
C
.
CLEARKEY_UUID
.
equals
(
uuid
)
?
C
.
COMMON_PSSH_UUID
:
uuid
;
uuid
=
Util
.
SDK_INT
<
27
&&
C
.
CLEARKEY_UUID
.
equals
(
uuid
)
?
C
.
COMMON_PSSH_UUID
:
uuid
;
this
.
uuid
=
uuid
;
this
.
uuid
=
uuid
;
this
.
mediaDrm
=
new
MediaDrm
(
uuid
);
this
.
mediaDrm
=
new
MediaDrm
(
uuid
);
if
(
C
.
WIDEVINE_UUID
.
equals
(
uuid
)
&&
needsForceL3Workaround
())
{
mediaDrm
.
setPropertyString
(
"securityLevel"
,
"L3"
);
}
}
}
@Override
@Override
...
@@ -116,14 +125,49 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
...
@@ -116,14 +125,49 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
@Override
@Override
public
KeyRequest
getKeyRequest
(
byte
[]
scope
,
byte
[]
init
,
String
mimeType
,
int
keyType
,
public
KeyRequest
getKeyRequest
(
byte
[]
scope
,
byte
[]
init
,
String
mimeType
,
int
keyType
,
HashMap
<
String
,
String
>
optionalParameters
)
throws
NotProvisionedException
{
HashMap
<
String
,
String
>
optionalParameters
)
throws
NotProvisionedException
{
// 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.
if
((
Util
.
SDK_INT
<
21
&&
C
.
WIDEVINE_UUID
.
equals
(
uuid
))
||
(
C
.
PLAYREADY_UUID
.
equals
(
uuid
)
&&
"Amazon"
.
equals
(
Util
.
MANUFACTURER
)
&&
(
"AFTB"
.
equals
(
Util
.
MODEL
)
// Fire TV Gen 1
||
"AFTS"
.
equals
(
Util
.
MODEL
)
// Fire TV Gen 2
||
"AFTM"
.
equals
(
Util
.
MODEL
))))
{
// Fire TV Stick Gen 1
byte
[]
psshData
=
PsshAtomUtil
.
parseSchemeSpecificData
(
init
,
uuid
);
if
(
psshData
==
null
)
{
// Extraction failed. schemeData isn't a PSSH atom, so leave it unchanged.
}
else
{
init
=
psshData
;
}
}
// Prior to API level 26 the ClearKey CDM only accepted "cenc" as the scheme for MP4.
if
(
Util
.
SDK_INT
<
26
&&
C
.
CLEARKEY_UUID
.
equals
(
uuid
)
&&
(
MimeTypes
.
VIDEO_MP4
.
equals
(
mimeType
)
||
MimeTypes
.
AUDIO_MP4
.
equals
(
mimeType
)))
{
mimeType
=
CENC_SCHEME_MIME_TYPE
;
}
final
MediaDrm
.
KeyRequest
request
=
mediaDrm
.
getKeyRequest
(
scope
,
init
,
mimeType
,
keyType
,
final
MediaDrm
.
KeyRequest
request
=
mediaDrm
.
getKeyRequest
(
scope
,
init
,
mimeType
,
keyType
,
optionalParameters
);
optionalParameters
);
return
new
DefaultKeyRequest
(
request
.
getData
(),
request
.
getDefaultUrl
());
byte
[]
requestData
=
request
.
getData
();
if
(
C
.
CLEARKEY_UUID
.
equals
(
uuid
))
{
requestData
=
ClearKeyUtil
.
adjustRequestData
(
requestData
);
}
return
new
DefaultKeyRequest
(
requestData
,
request
.
getDefaultUrl
());
}
}
@Override
@Override
public
byte
[]
provideKeyResponse
(
byte
[]
scope
,
byte
[]
response
)
public
byte
[]
provideKeyResponse
(
byte
[]
scope
,
byte
[]
response
)
throws
NotProvisionedException
,
DeniedByServerException
{
throws
NotProvisionedException
,
DeniedByServerException
{
if
(
C
.
CLEARKEY_UUID
.
equals
(
uuid
))
{
response
=
ClearKeyUtil
.
adjustResponseData
(
response
);
}
return
mediaDrm
.
provideKeyResponse
(
scope
,
response
);
return
mediaDrm
.
provideKeyResponse
(
scope
,
response
);
}
}
...
@@ -183,4 +227,12 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
...
@@ -183,4 +227,12 @@ public final class FrameworkMediaDrm implements ExoMediaDrm<FrameworkMediaCrypto
forceAllowInsecureDecoderComponents
);
forceAllowInsecureDecoderComponents
);
}
}
/**
* Returns whether the device codec is known to fail if security level L1 is used.
*
* <p>See <a href="https://github.com/google/ExoPlayer/issues/4413">GitHub issue #4413</a>.
*/
private
static
boolean
needsForceL3Workaround
()
{
return
"ASUS_Z00AD"
.
equals
(
Util
.
MODEL
);
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/drm/HttpMediaDrmCallback.java
View file @
8cbcfd66
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.drm;
...
@@ -17,6 +17,7 @@ package com.google.android.exoplayer2.drm;
import
android.annotation.TargetApi
;
import
android.annotation.TargetApi
;
import
android.net.Uri
;
import
android.net.Uri
;
import
android.support.annotation.Nullable
;
import
android.text.TextUtils
;
import
android.text.TextUtils
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest
;
...
@@ -114,8 +115,13 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
...
@@ -114,8 +115,13 @@ public final class HttpMediaDrmCallback implements MediaDrmCallback {
}
}
@Override
@Override
public
byte
[]
executeKeyRequest
(
UUID
uuid
,
KeyRequest
request
)
throws
Exception
{
public
byte
[]
executeKeyRequest
(
UUID
uuid
,
KeyRequest
request
,
@Nullable
String
mediaProvidedLicenseServerUrl
)
throws
Exception
{
String
url
=
request
.
getDefaultUrl
();
String
url
=
request
.
getDefaultUrl
();
if
(
TextUtils
.
isEmpty
(
url
))
{
url
=
mediaProvidedLicenseServerUrl
;
}
if
(
forceDefaultLicenseUrl
||
TextUtils
.
isEmpty
(
url
))
{
if
(
forceDefaultLicenseUrl
||
TextUtils
.
isEmpty
(
url
))
{
url
=
defaultLicenseUrl
;
url
=
defaultLicenseUrl
;
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/drm/LocalMediaDrmCallback.java
View file @
8cbcfd66
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
.
drm
;
package
com
.
google
.
android
.
exoplayer2
.
drm
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Assertions
;
...
@@ -44,7 +45,9 @@ public final class LocalMediaDrmCallback implements MediaDrmCallback {
...
@@ -44,7 +45,9 @@ public final class LocalMediaDrmCallback implements MediaDrmCallback {
}
}
@Override
@Override
public
byte
[]
executeKeyRequest
(
UUID
uuid
,
KeyRequest
request
)
throws
Exception
{
public
byte
[]
executeKeyRequest
(
UUID
uuid
,
KeyRequest
request
,
@Nullable
String
mediaProvidedLicenseServerUrl
)
throws
Exception
{
return
keyResponse
;
return
keyResponse
;
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/drm/MediaDrmCallback.java
View file @
8cbcfd66
...
@@ -15,6 +15,7 @@
...
@@ -15,6 +15,7 @@
*/
*/
package
com
.
google
.
android
.
exoplayer2
.
drm
;
package
com
.
google
.
android
.
exoplayer2
.
drm
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.KeyRequest
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest
;
import
com.google.android.exoplayer2.drm.ExoMediaDrm.ProvisionRequest
;
import
java.util.UUID
;
import
java.util.UUID
;
...
@@ -38,10 +39,13 @@ public interface MediaDrmCallback {
...
@@ -38,10 +39,13 @@ public interface MediaDrmCallback {
* Executes a key request.
* Executes a key request.
*
*
* @param uuid The UUID of the content protection scheme.
* @param uuid The UUID of the content protection scheme.
* @param request The request.
* @param request The request generated by the content decryption module.
* @param mediaProvidedLicenseServerUrl A license server URL provided by the media, or null if the
* media does not include any license server URL.
* @return The response data.
* @return The response data.
* @throws Exception If an error occurred executing the request.
* @throws Exception If an error occurred executing the request.
*/
*/
byte
[]
executeKeyRequest
(
UUID
uuid
,
KeyRequest
request
)
throws
Exception
;
byte
[]
executeKeyRequest
(
UUID
uuid
,
KeyRequest
request
,
@Nullable
String
mediaProvidedLicenseServerUrl
)
throws
Exception
;
}
}
library/core/src/main/java/com/google/android/exoplayer2/extractor/GaplessInfoHolder.java
View file @
8cbcfd66
...
@@ -19,6 +19,7 @@ import com.google.android.exoplayer2.Format;
...
@@ -19,6 +19,7 @@ import com.google.android.exoplayer2.Format;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.metadata.Metadata
;
import
com.google.android.exoplayer2.metadata.id3.CommentFrame
;
import
com.google.android.exoplayer2.metadata.id3.CommentFrame
;
import
com.google.android.exoplayer2.metadata.id3.Id3Decoder.FramePredicate
;
import
com.google.android.exoplayer2.metadata.id3.Id3Decoder.FramePredicate
;
import
com.google.android.exoplayer2.metadata.id3.InternalFrame
;
import
java.util.regex.Matcher
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
import
java.util.regex.Pattern
;
...
@@ -39,7 +40,8 @@ public final class GaplessInfoHolder {
...
@@ -39,7 +40,8 @@ public final class GaplessInfoHolder {
}
}
};
};
private
static
final
String
GAPLESS_COMMENT_ID
=
"iTunSMPB"
;
private
static
final
String
GAPLESS_DOMAIN
=
"com.apple.iTunes"
;
private
static
final
String
GAPLESS_DESCRIPTION
=
"iTunSMPB"
;
private
static
final
Pattern
GAPLESS_COMMENT_PATTERN
=
private
static
final
Pattern
GAPLESS_COMMENT_PATTERN
=
Pattern
.
compile
(
"^ [0-9a-fA-F]{8} ([0-9a-fA-F]{8}) ([0-9a-fA-F]{8})"
);
Pattern
.
compile
(
"^ [0-9a-fA-F]{8} ([0-9a-fA-F]{8}) ([0-9a-fA-F]{8})"
);
...
@@ -91,7 +93,15 @@ public final class GaplessInfoHolder {
...
@@ -91,7 +93,15 @@ public final class GaplessInfoHolder {
Metadata
.
Entry
entry
=
metadata
.
get
(
i
);
Metadata
.
Entry
entry
=
metadata
.
get
(
i
);
if
(
entry
instanceof
CommentFrame
)
{
if
(
entry
instanceof
CommentFrame
)
{
CommentFrame
commentFrame
=
(
CommentFrame
)
entry
;
CommentFrame
commentFrame
=
(
CommentFrame
)
entry
;
if
(
setFromComment
(
commentFrame
.
description
,
commentFrame
.
text
))
{
if
(
GAPLESS_DESCRIPTION
.
equals
(
commentFrame
.
description
)
&&
setFromComment
(
commentFrame
.
text
))
{
return
true
;
}
}
else
if
(
entry
instanceof
InternalFrame
)
{
InternalFrame
internalFrame
=
(
InternalFrame
)
entry
;
if
(
GAPLESS_DOMAIN
.
equals
(
internalFrame
.
domain
)
&&
GAPLESS_DESCRIPTION
.
equals
(
internalFrame
.
description
)
&&
setFromComment
(
internalFrame
.
text
))
{
return
true
;
return
true
;
}
}
}
}
...
@@ -103,14 +113,10 @@ public final class GaplessInfoHolder {
...
@@ -103,14 +113,10 @@ public final class GaplessInfoHolder {
* Populates the holder with data parsed from a gapless playback comment (stored in an ID3 header
* Populates the holder with data parsed from a gapless playback comment (stored in an ID3 header
* or MPEG 4 user data), if valid and non-zero.
* or MPEG 4 user data), if valid and non-zero.
*
*
* @param name The comment's identifier.
* @param data The comment's payload data.
* @param data The comment's payload data.
* @return Whether the holder was populated.
* @return Whether the holder was populated.
*/
*/
private
boolean
setFromComment
(
String
name
,
String
data
)
{
private
boolean
setFromComment
(
String
data
)
{
if
(!
GAPLESS_COMMENT_ID
.
equals
(
name
))
{
return
false
;
}
Matcher
matcher
=
GAPLESS_COMMENT_PATTERN
.
matcher
(
data
);
Matcher
matcher
=
GAPLESS_COMMENT_PATTERN
.
matcher
(
data
);
if
(
matcher
.
find
())
{
if
(
matcher
.
find
())
{
try
{
try
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java
View file @
8cbcfd66
...
@@ -616,10 +616,10 @@ public final class MatroskaExtractor implements Extractor {
...
@@ -616,10 +616,10 @@ public final class MatroskaExtractor implements Extractor {
currentTrack
.
number
=
(
int
)
value
;
currentTrack
.
number
=
(
int
)
value
;
break
;
break
;
case
ID_FLAG_DEFAULT:
case
ID_FLAG_DEFAULT:
currentTrack
.
flag
Forced
=
value
==
1
;
currentTrack
.
flag
Default
=
value
==
1
;
break
;
break
;
case
ID_FLAG_FORCED:
case
ID_FLAG_FORCED:
currentTrack
.
flag
Default
=
value
==
1
;
currentTrack
.
flag
Forced
=
value
==
1
;
break
;
break
;
case
ID_TRACK_TYPE:
case
ID_TRACK_TYPE:
currentTrack
.
type
=
(
int
)
value
;
currentTrack
.
type
=
(
int
)
value
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/AtomParsers.java
View file @
8cbcfd66
...
@@ -43,6 +43,9 @@ import java.util.List;
...
@@ -43,6 +43,9 @@ import java.util.List;
*/
*/
/* package */
final
class
AtomParsers
{
/* package */
final
class
AtomParsers
{
/** Thrown if an edit list couldn't be applied. */
public
static
final
class
UnhandledEditListException
extends
ParserException
{}
private
static
final
String
TAG
=
"AtomParsers"
;
private
static
final
String
TAG
=
"AtomParsers"
;
private
static
final
int
TYPE_vide
=
Util
.
getIntegerCodeForString
(
"vide"
);
private
static
final
int
TYPE_vide
=
Util
.
getIntegerCodeForString
(
"vide"
);
...
@@ -117,10 +120,12 @@ import java.util.List;
...
@@ -117,10 +120,12 @@ import java.util.List;
* @param stblAtom stbl (sample table) atom to decode.
* @param stblAtom stbl (sample table) atom to decode.
* @param gaplessInfoHolder Holder to populate with gapless playback information.
* @param gaplessInfoHolder Holder to populate with gapless playback information.
* @return Sample table described by the stbl atom.
* @return Sample table described by the stbl atom.
* @throws ParserException If the resulting sample sequence does not contain a sync sample.
* @throws UnhandledEditListException Thrown if the edit list can't be applied.
* @throws ParserException Thrown if the stbl atom can't be parsed.
*/
*/
public
static
TrackSampleTable
parseStbl
(
Track
track
,
Atom
.
ContainerAtom
stblAtom
,
public
static
TrackSampleTable
parseStbl
(
GaplessInfoHolder
gaplessInfoHolder
)
throws
ParserException
{
Track
track
,
Atom
.
ContainerAtom
stblAtom
,
GaplessInfoHolder
gaplessInfoHolder
)
throws
ParserException
{
SampleSizeBox
sampleSizeBox
;
SampleSizeBox
sampleSizeBox
;
Atom
.
LeafAtom
stszAtom
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_stsz
);
Atom
.
LeafAtom
stszAtom
=
stblAtom
.
getLeafAtomOfType
(
Atom
.
TYPE_stsz
);
if
(
stszAtom
!=
null
)
{
if
(
stszAtom
!=
null
)
{
...
@@ -136,7 +141,13 @@ import java.util.List;
...
@@ -136,7 +141,13 @@ import java.util.List;
int
sampleCount
=
sampleSizeBox
.
getSampleCount
();
int
sampleCount
=
sampleSizeBox
.
getSampleCount
();
if
(
sampleCount
==
0
)
{
if
(
sampleCount
==
0
)
{
return
new
TrackSampleTable
(
return
new
TrackSampleTable
(
new
long
[
0
],
new
int
[
0
],
0
,
new
long
[
0
],
new
int
[
0
],
C
.
TIME_UNSET
);
track
,
/* offsets= */
new
long
[
0
],
/* sizes= */
new
int
[
0
],
/* maximumSize= */
0
,
/* timestampsUs= */
new
long
[
0
],
/* flags= */
new
int
[
0
],
/* durationUs= */
C
.
TIME_UNSET
);
}
}
// Entries are byte offsets of chunks.
// Entries are byte offsets of chunks.
...
@@ -315,7 +326,8 @@ import java.util.List;
...
@@ -315,7 +326,8 @@ import java.util.List;
// There is no edit list, or we are ignoring it as we already have gapless metadata to apply.
// There is no edit list, or we are ignoring it as we already have gapless metadata to apply.
// This implementation does not support applying both gapless metadata and an edit list.
// This implementation does not support applying both gapless metadata and an edit list.
Util
.
scaleLargeTimestampsInPlace
(
timestamps
,
C
.
MICROS_PER_SECOND
,
track
.
timescale
);
Util
.
scaleLargeTimestampsInPlace
(
timestamps
,
C
.
MICROS_PER_SECOND
,
track
.
timescale
);
return
new
TrackSampleTable
(
offsets
,
sizes
,
maximumSize
,
timestamps
,
flags
,
durationUs
);
return
new
TrackSampleTable
(
track
,
offsets
,
sizes
,
maximumSize
,
timestamps
,
flags
,
durationUs
);
}
}
// See the BMFF spec (ISO 14496-12) subsection 8.6.6. Edit lists that require prerolling from a
// See the BMFF spec (ISO 14496-12) subsection 8.6.6. Edit lists that require prerolling from a
...
@@ -342,7 +354,8 @@ import java.util.List;
...
@@ -342,7 +354,8 @@ import java.util.List;
gaplessInfoHolder
.
encoderDelay
=
(
int
)
encoderDelay
;
gaplessInfoHolder
.
encoderDelay
=
(
int
)
encoderDelay
;
gaplessInfoHolder
.
encoderPadding
=
(
int
)
encoderPadding
;
gaplessInfoHolder
.
encoderPadding
=
(
int
)
encoderPadding
;
Util
.
scaleLargeTimestampsInPlace
(
timestamps
,
C
.
MICROS_PER_SECOND
,
track
.
timescale
);
Util
.
scaleLargeTimestampsInPlace
(
timestamps
,
C
.
MICROS_PER_SECOND
,
track
.
timescale
);
return
new
TrackSampleTable
(
offsets
,
sizes
,
maximumSize
,
timestamps
,
flags
,
durationUs
);
return
new
TrackSampleTable
(
track
,
offsets
,
sizes
,
maximumSize
,
timestamps
,
flags
,
durationUs
);
}
}
}
}
}
}
...
@@ -359,7 +372,8 @@ import java.util.List;
...
@@ -359,7 +372,8 @@ import java.util.List;
}
}
durationUs
=
durationUs
=
Util
.
scaleLargeTimestamp
(
duration
-
editStartTime
,
C
.
MICROS_PER_SECOND
,
track
.
timescale
);
Util
.
scaleLargeTimestamp
(
duration
-
editStartTime
,
C
.
MICROS_PER_SECOND
,
track
.
timescale
);
return
new
TrackSampleTable
(
offsets
,
sizes
,
maximumSize
,
timestamps
,
flags
,
durationUs
);
return
new
TrackSampleTable
(
track
,
offsets
,
sizes
,
maximumSize
,
timestamps
,
flags
,
durationUs
);
}
}
// Omit any sample at the end point of an edit for audio tracks.
// Omit any sample at the end point of an edit for audio tracks.
...
@@ -409,6 +423,11 @@ import java.util.List;
...
@@ -409,6 +423,11 @@ import java.util.List;
System
.
arraycopy
(
sizes
,
startIndex
,
editedSizes
,
sampleIndex
,
count
);
System
.
arraycopy
(
sizes
,
startIndex
,
editedSizes
,
sampleIndex
,
count
);
System
.
arraycopy
(
flags
,
startIndex
,
editedFlags
,
sampleIndex
,
count
);
System
.
arraycopy
(
flags
,
startIndex
,
editedFlags
,
sampleIndex
,
count
);
}
}
if
(
startIndex
<
endIndex
&&
(
editedFlags
[
sampleIndex
]
&
C
.
BUFFER_FLAG_KEY_FRAME
)
==
0
)
{
// Applying the edit list would require prerolling from a sync sample.
Log
.
w
(
TAG
,
"Ignoring edit list: edit does not start with a sync sample."
);
throw
new
UnhandledEditListException
();
}
for
(
int
j
=
startIndex
;
j
<
endIndex
;
j
++)
{
for
(
int
j
=
startIndex
;
j
<
endIndex
;
j
++)
{
long
ptsUs
=
Util
.
scaleLargeTimestamp
(
pts
,
C
.
MICROS_PER_SECOND
,
track
.
movieTimescale
);
long
ptsUs
=
Util
.
scaleLargeTimestamp
(
pts
,
C
.
MICROS_PER_SECOND
,
track
.
movieTimescale
);
long
timeInSegmentUs
=
long
timeInSegmentUs
=
...
@@ -424,20 +443,8 @@ import java.util.List;
...
@@ -424,20 +443,8 @@ import java.util.List;
pts
+=
editDuration
;
pts
+=
editDuration
;
}
}
long
editedDurationUs
=
Util
.
scaleLargeTimestamp
(
pts
,
C
.
MICROS_PER_SECOND
,
track
.
timescale
);
long
editedDurationUs
=
Util
.
scaleLargeTimestamp
(
pts
,
C
.
MICROS_PER_SECOND
,
track
.
timescale
);
boolean
hasSyncSample
=
false
;
for
(
int
i
=
0
;
i
<
editedFlags
.
length
&&
!
hasSyncSample
;
i
++)
{
hasSyncSample
|=
(
editedFlags
[
i
]
&
C
.
BUFFER_FLAG_KEY_FRAME
)
!=
0
;
}
if
(!
hasSyncSample
)
{
// We don't support edit lists where the edited sample sequence doesn't contain a sync sample.
// Such edit lists are often (although not always) broken, so we ignore it and continue.
Log
.
w
(
TAG
,
"Ignoring edit list: Edited sample sequence does not contain a sync sample."
);
Util
.
scaleLargeTimestampsInPlace
(
timestamps
,
C
.
MICROS_PER_SECOND
,
track
.
timescale
);
return
new
TrackSampleTable
(
offsets
,
sizes
,
maximumSize
,
timestamps
,
flags
,
durationUs
);
}
return
new
TrackSampleTable
(
return
new
TrackSampleTable
(
track
,
editedOffsets
,
editedOffsets
,
editedSizes
,
editedSizes
,
editedMaximumSize
,
editedMaximumSize
,
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/FragmentedMp4Extractor.java
View file @
8cbcfd66
...
@@ -499,7 +499,7 @@ public final class FragmentedMp4Extractor implements Extractor {
...
@@ -499,7 +499,7 @@ public final class FragmentedMp4Extractor implements Extractor {
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
Track
track
=
tracks
.
valueAt
(
i
);
Track
track
=
tracks
.
valueAt
(
i
);
TrackBundle
trackBundle
=
new
TrackBundle
(
extractorOutput
.
track
(
i
,
track
.
type
));
TrackBundle
trackBundle
=
new
TrackBundle
(
extractorOutput
.
track
(
i
,
track
.
type
));
trackBundle
.
init
(
track
,
defaultSampleValuesArray
.
get
(
track
.
id
));
trackBundle
.
init
(
track
,
getDefaultSampleValues
(
defaultSampleValuesArray
,
track
.
id
));
trackBundles
.
put
(
track
.
id
,
trackBundle
);
trackBundles
.
put
(
track
.
id
,
trackBundle
);
durationUs
=
Math
.
max
(
durationUs
,
track
.
durationUs
);
durationUs
=
Math
.
max
(
durationUs
,
track
.
durationUs
);
}
}
...
@@ -509,11 +509,23 @@ public final class FragmentedMp4Extractor implements Extractor {
...
@@ -509,11 +509,23 @@ public final class FragmentedMp4Extractor implements Extractor {
Assertions
.
checkState
(
trackBundles
.
size
()
==
trackCount
);
Assertions
.
checkState
(
trackBundles
.
size
()
==
trackCount
);
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
Track
track
=
tracks
.
valueAt
(
i
);
Track
track
=
tracks
.
valueAt
(
i
);
trackBundles
.
get
(
track
.
id
).
init
(
track
,
defaultSampleValuesArray
.
get
(
track
.
id
));
trackBundles
.
get
(
track
.
id
)
.
init
(
track
,
getDefaultSampleValues
(
defaultSampleValuesArray
,
track
.
id
));
}
}
}
}
}
}
private
DefaultSampleValues
getDefaultSampleValues
(
SparseArray
<
DefaultSampleValues
>
defaultSampleValuesArray
,
int
trackId
)
{
if
(
defaultSampleValuesArray
.
size
()
==
1
)
{
// Ignore track id if there is only one track to cope with non-matching track indices.
// See https://github.com/google/ExoPlayer/issues/4477.
return
defaultSampleValuesArray
.
valueAt
(
/* index= */
0
);
}
return
Assertions
.
checkNotNull
(
defaultSampleValuesArray
.
get
(
trackId
));
}
private
void
onMoofContainerAtomRead
(
ContainerAtom
moof
)
throws
ParserException
{
private
void
onMoofContainerAtomRead
(
ContainerAtom
moof
)
throws
ParserException
{
parseMoof
(
moof
,
trackBundles
,
flags
,
extendedTypeScratch
);
parseMoof
(
moof
,
trackBundles
,
flags
,
extendedTypeScratch
);
// If drm init data is sideloaded, we ignore pssh boxes.
// If drm init data is sideloaded, we ignore pssh boxes.
...
@@ -642,7 +654,7 @@ public final class FragmentedMp4Extractor implements Extractor {
...
@@ -642,7 +654,7 @@ public final class FragmentedMp4Extractor implements Extractor {
private
static
void
parseTraf
(
ContainerAtom
traf
,
SparseArray
<
TrackBundle
>
trackBundleArray
,
private
static
void
parseTraf
(
ContainerAtom
traf
,
SparseArray
<
TrackBundle
>
trackBundleArray
,
@Flags
int
flags
,
byte
[]
extendedTypeScratch
)
throws
ParserException
{
@Flags
int
flags
,
byte
[]
extendedTypeScratch
)
throws
ParserException
{
LeafAtom
tfhd
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_tfhd
);
LeafAtom
tfhd
=
traf
.
getLeafAtomOfType
(
Atom
.
TYPE_tfhd
);
TrackBundle
trackBundle
=
parseTfhd
(
tfhd
.
data
,
trackBundleArray
,
flags
);
TrackBundle
trackBundle
=
parseTfhd
(
tfhd
.
data
,
trackBundleArray
);
if
(
trackBundle
==
null
)
{
if
(
trackBundle
==
null
)
{
return
;
return
;
}
}
...
@@ -793,13 +805,13 @@ public final class FragmentedMp4Extractor implements Extractor {
...
@@ -793,13 +805,13 @@ public final class FragmentedMp4Extractor implements Extractor {
* @return The {@link TrackBundle} to which the {@link TrackFragment} belongs, or null if the tfhd
* @return The {@link TrackBundle} to which the {@link TrackFragment} belongs, or null if the tfhd
* does not refer to any {@link TrackBundle}.
* does not refer to any {@link TrackBundle}.
*/
*/
private
static
TrackBundle
parseTfhd
(
ParsableByteArray
tfhd
,
private
static
TrackBundle
parseTfhd
(
SparseArray
<
TrackBundle
>
trackBundles
,
int
flag
s
)
{
ParsableByteArray
tfhd
,
SparseArray
<
TrackBundle
>
trackBundle
s
)
{
tfhd
.
setPosition
(
Atom
.
HEADER_SIZE
);
tfhd
.
setPosition
(
Atom
.
HEADER_SIZE
);
int
fullAtom
=
tfhd
.
readInt
();
int
fullAtom
=
tfhd
.
readInt
();
int
atomFlags
=
Atom
.
parseFullAtomFlags
(
fullAtom
);
int
atomFlags
=
Atom
.
parseFullAtomFlags
(
fullAtom
);
int
trackId
=
tfhd
.
readInt
();
int
trackId
=
tfhd
.
readInt
();
TrackBundle
trackBundle
=
trackBundles
.
get
((
flags
&
FLAG_SIDELOADED
)
==
0
?
trackId
:
0
);
TrackBundle
trackBundle
=
getTrackBundle
(
trackBundles
,
trackId
);
if
(
trackBundle
==
null
)
{
if
(
trackBundle
==
null
)
{
return
null
;
return
null
;
}
}
...
@@ -824,6 +836,17 @@ public final class FragmentedMp4Extractor implements Extractor {
...
@@ -824,6 +836,17 @@ public final class FragmentedMp4Extractor implements Extractor {
return
trackBundle
;
return
trackBundle
;
}
}
private
static
@Nullable
TrackBundle
getTrackBundle
(
SparseArray
<
TrackBundle
>
trackBundles
,
int
trackId
)
{
if
(
trackBundles
.
size
()
==
1
)
{
// Ignore track id if there is only one track. This is either because we have a side-loaded
// track (flag FLAG_SIDELOADED) or to cope with non-matching track indices (see
// https://github.com/google/ExoPlayer/issues/4083).
return
trackBundles
.
valueAt
(
/* index= */
0
);
}
return
trackBundles
.
get
(
trackId
);
}
/**
/**
* Parses a tfdt atom (defined in 14496-12).
* Parses a tfdt atom (defined in 14496-12).
*
*
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/MetadataUtil.java
View file @
8cbcfd66
...
@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.metadata.Metadata;
...
@@ -20,6 +20,7 @@ import com.google.android.exoplayer2.metadata.Metadata;
import
com.google.android.exoplayer2.metadata.id3.ApicFrame
;
import
com.google.android.exoplayer2.metadata.id3.ApicFrame
;
import
com.google.android.exoplayer2.metadata.id3.CommentFrame
;
import
com.google.android.exoplayer2.metadata.id3.CommentFrame
;
import
com.google.android.exoplayer2.metadata.id3.Id3Frame
;
import
com.google.android.exoplayer2.metadata.id3.Id3Frame
;
import
com.google.android.exoplayer2.metadata.id3.InternalFrame
;
import
com.google.android.exoplayer2.metadata.id3.TextInformationFrame
;
import
com.google.android.exoplayer2.metadata.id3.TextInformationFrame
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
com.google.android.exoplayer2.util.Util
;
import
com.google.android.exoplayer2.util.Util
;
...
@@ -293,14 +294,13 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -293,14 +294,13 @@ import com.google.android.exoplayer2.util.Util;
data
.
skipBytes
(
atomSize
-
12
);
data
.
skipBytes
(
atomSize
-
12
);
}
}
}
}
if
(!
"com.apple.iTunes"
.
equals
(
domain
)
||
!
"iTunSMPB"
.
equals
(
name
)
||
dataAtomPosition
==
-
1
)
{
if
(
domain
==
null
||
name
==
null
||
dataAtomPosition
==
-
1
)
{
// We're only interested in iTunSMPB.
return
null
;
return
null
;
}
}
data
.
setPosition
(
dataAtomPosition
);
data
.
setPosition
(
dataAtomPosition
);
data
.
skipBytes
(
16
);
// size (4), type (4), version (1), flags (3), empty (4)
data
.
skipBytes
(
16
);
// size (4), type (4), version (1), flags (3), empty (4)
String
value
=
data
.
readNullTerminatedString
(
dataAtomSize
-
16
);
String
value
=
data
.
readNullTerminatedString
(
dataAtomSize
-
16
);
return
new
CommentFrame
(
LANGUAGE_UNDEFINED
,
name
,
value
);
return
new
InternalFrame
(
domain
,
name
,
value
);
}
}
private
static
int
parseUint8AttributeValue
(
ParsableByteArray
data
)
{
private
static
int
parseUint8AttributeValue
(
ParsableByteArray
data
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/Mp4Extractor.java
View file @
8cbcfd66
...
@@ -391,25 +391,21 @@ public final class Mp4Extractor implements Extractor, SeekMap {
...
@@ -391,25 +391,21 @@ public final class Mp4Extractor implements Extractor, SeekMap {
}
}
}
}
for
(
int
i
=
0
;
i
<
moov
.
containerChildren
.
size
();
i
++)
{
boolean
ignoreEditLists
=
(
flags
&
FLAG_WORKAROUND_IGNORE_EDIT_LISTS
)
!=
0
;
Atom
.
ContainerAtom
atom
=
moov
.
containerChildren
.
get
(
i
);
ArrayList
<
TrackSampleTable
>
trackSampleTables
;
if
(
atom
.
type
!=
Atom
.
TYPE_trak
)
{
try
{
continue
;
trackSampleTables
=
getTrackSampleTables
(
moov
,
gaplessInfoHolder
,
ignoreEditLists
);
}
}
catch
(
AtomParsers
.
UnhandledEditListException
e
)
{
// Discard gapless info as we aren't able to handle corresponding edits.
Track
track
=
AtomParsers
.
parseTrak
(
atom
,
moov
.
getLeafAtomOfType
(
Atom
.
TYPE_mvhd
),
gaplessInfoHolder
=
new
GaplessInfoHolder
();
C
.
TIME_UNSET
,
null
,
(
flags
&
FLAG_WORKAROUND_IGNORE_EDIT_LISTS
)
!=
0
,
isQuickTime
);
trackSampleTables
=
if
(
track
==
null
)
{
getTrackSampleTables
(
moov
,
gaplessInfoHolder
,
/* ignoreEditLists= */
true
);
continue
;
}
}
Atom
.
ContainerAtom
stblAtom
=
atom
.
getContainerAtomOfType
(
Atom
.
TYPE_mdia
)
.
getContainerAtomOfType
(
Atom
.
TYPE_minf
).
getContainerAtomOfType
(
Atom
.
TYPE_stbl
);
TrackSampleTable
trackSampleTable
=
AtomParsers
.
parseStbl
(
track
,
stblAtom
,
gaplessInfoHolder
);
if
(
trackSampleTable
.
sampleCount
==
0
)
{
continue
;
}
int
trackCount
=
trackSampleTables
.
size
();
for
(
int
i
=
0
;
i
<
trackCount
;
i
++)
{
TrackSampleTable
trackSampleTable
=
trackSampleTables
.
get
(
i
);
Track
track
=
trackSampleTable
.
track
;
Mp4Track
mp4Track
=
new
Mp4Track
(
track
,
trackSampleTable
,
Mp4Track
mp4Track
=
new
Mp4Track
(
track
,
trackSampleTable
,
extractorOutput
.
track
(
i
,
track
.
type
));
extractorOutput
.
track
(
i
,
track
.
type
));
// Each sample has up to three bytes of overhead for the start code that replaces its length.
// Each sample has up to three bytes of overhead for the start code that replaces its length.
...
@@ -445,6 +441,39 @@ public final class Mp4Extractor implements Extractor, SeekMap {
...
@@ -445,6 +441,39 @@ public final class Mp4Extractor implements Extractor, SeekMap {
extractorOutput
.
seekMap
(
this
);
extractorOutput
.
seekMap
(
this
);
}
}
private
ArrayList
<
TrackSampleTable
>
getTrackSampleTables
(
ContainerAtom
moov
,
GaplessInfoHolder
gaplessInfoHolder
,
boolean
ignoreEditLists
)
throws
ParserException
{
ArrayList
<
TrackSampleTable
>
trackSampleTables
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
moov
.
containerChildren
.
size
();
i
++)
{
Atom
.
ContainerAtom
atom
=
moov
.
containerChildren
.
get
(
i
);
if
(
atom
.
type
!=
Atom
.
TYPE_trak
)
{
continue
;
}
Track
track
=
AtomParsers
.
parseTrak
(
atom
,
moov
.
getLeafAtomOfType
(
Atom
.
TYPE_mvhd
),
/* duration= */
C
.
TIME_UNSET
,
/* drmInitData= */
null
,
ignoreEditLists
,
isQuickTime
);
if
(
track
==
null
)
{
continue
;
}
Atom
.
ContainerAtom
stblAtom
=
atom
.
getContainerAtomOfType
(
Atom
.
TYPE_mdia
)
.
getContainerAtomOfType
(
Atom
.
TYPE_minf
)
.
getContainerAtomOfType
(
Atom
.
TYPE_stbl
);
TrackSampleTable
trackSampleTable
=
AtomParsers
.
parseStbl
(
track
,
stblAtom
,
gaplessInfoHolder
);
if
(
trackSampleTable
.
sampleCount
==
0
)
{
continue
;
}
trackSampleTables
.
add
(
trackSampleTable
);
}
return
trackSampleTables
;
}
/**
/**
* Attempts to extract the next sample in the current mdat atom for the specified track.
* Attempts to extract the next sample in the current mdat atom for the specified track.
* <p>
* <p>
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mp4/TrackSampleTable.java
View file @
8cbcfd66
...
@@ -24,29 +24,19 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -24,29 +24,19 @@ import com.google.android.exoplayer2.util.Util;
*/
*/
/* package */
final
class
TrackSampleTable
{
/* package */
final
class
TrackSampleTable
{
/**
/**
The track corresponding to this sample table. */
* Number of samples.
public
final
Track
track
;
*/
/** Number of samples.
*/
public
final
int
sampleCount
;
public
final
int
sampleCount
;
/**
/** Sample offsets in bytes. */
* Sample offsets in bytes.
*/
public
final
long
[]
offsets
;
public
final
long
[]
offsets
;
/**
/** Sample sizes in bytes. */
* Sample sizes in bytes.
*/
public
final
int
[]
sizes
;
public
final
int
[]
sizes
;
/**
/** Maximum sample size in {@link #sizes}. */
* Maximum sample size in {@link #sizes}.
*/
public
final
int
maximumSize
;
public
final
int
maximumSize
;
/**
/** Sample timestamps in microseconds. */
* Sample timestamps in microseconds.
*/
public
final
long
[]
timestampsUs
;
public
final
long
[]
timestampsUs
;
/**
/** Sample flags. */
* Sample flags.
*/
public
final
int
[]
flags
;
public
final
int
[]
flags
;
/**
/**
* The duration of the track sample table in microseconds, or {@link C#TIME_UNSET} if the sample
* The duration of the track sample table in microseconds, or {@link C#TIME_UNSET} if the sample
...
@@ -55,6 +45,7 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -55,6 +45,7 @@ import com.google.android.exoplayer2.util.Util;
public
final
long
durationUs
;
public
final
long
durationUs
;
public
TrackSampleTable
(
public
TrackSampleTable
(
Track
track
,
long
[]
offsets
,
long
[]
offsets
,
int
[]
sizes
,
int
[]
sizes
,
int
maximumSize
,
int
maximumSize
,
...
@@ -65,6 +56,7 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -65,6 +56,7 @@ import com.google.android.exoplayer2.util.Util;
Assertions
.
checkArgument
(
offsets
.
length
==
timestampsUs
.
length
);
Assertions
.
checkArgument
(
offsets
.
length
==
timestampsUs
.
length
);
Assertions
.
checkArgument
(
flags
.
length
==
timestampsUs
.
length
);
Assertions
.
checkArgument
(
flags
.
length
==
timestampsUs
.
length
);
this
.
track
=
track
;
this
.
offsets
=
offsets
;
this
.
offsets
=
offsets
;
this
.
sizes
=
sizes
;
this
.
sizes
=
sizes
;
this
.
maximumSize
=
maximumSize
;
this
.
maximumSize
=
maximumSize
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/ts/PsExtractor.java
View file @
8cbcfd66
...
@@ -52,7 +52,12 @@ public final class PsExtractor implements Extractor {
...
@@ -52,7 +52,12 @@ public final class PsExtractor implements Extractor {
private
static
final
int
PACKET_START_CODE_PREFIX
=
0x000001
;
private
static
final
int
PACKET_START_CODE_PREFIX
=
0x000001
;
private
static
final
int
MPEG_PROGRAM_END_CODE
=
0x000001B9
;
private
static
final
int
MPEG_PROGRAM_END_CODE
=
0x000001B9
;
private
static
final
int
MAX_STREAM_ID_PLUS_ONE
=
0x100
;
private
static
final
int
MAX_STREAM_ID_PLUS_ONE
=
0x100
;
// Max search length for first audio and video track in input data.
private
static
final
long
MAX_SEARCH_LENGTH
=
1024
*
1024
;
private
static
final
long
MAX_SEARCH_LENGTH
=
1024
*
1024
;
// Max search length for additional audio and video tracks in input data after at least one audio
// and video track has been found.
private
static
final
long
MAX_SEARCH_LENGTH_AFTER_AUDIO_AND_VIDEO_FOUND
=
8
*
1024
;
public
static
final
int
PRIVATE_STREAM_1
=
0xBD
;
public
static
final
int
PRIVATE_STREAM_1
=
0xBD
;
public
static
final
int
AUDIO_STREAM
=
0xC0
;
public
static
final
int
AUDIO_STREAM
=
0xC0
;
...
@@ -66,6 +71,7 @@ public final class PsExtractor implements Extractor {
...
@@ -66,6 +71,7 @@ public final class PsExtractor implements Extractor {
private
boolean
foundAllTracks
;
private
boolean
foundAllTracks
;
private
boolean
foundAudioTrack
;
private
boolean
foundAudioTrack
;
private
boolean
foundVideoTrack
;
private
boolean
foundVideoTrack
;
private
long
lastTrackPosition
;
// Accessed only by the loading thread.
// Accessed only by the loading thread.
private
ExtractorOutput
output
;
private
ExtractorOutput
output
;
...
@@ -188,18 +194,21 @@ public final class PsExtractor implements Extractor {
...
@@ -188,18 +194,21 @@ public final class PsExtractor implements Extractor {
if
(!
foundAllTracks
)
{
if
(!
foundAllTracks
)
{
if
(
payloadReader
==
null
)
{
if
(
payloadReader
==
null
)
{
ElementaryStreamReader
elementaryStreamReader
=
null
;
ElementaryStreamReader
elementaryStreamReader
=
null
;
if
(
!
foundAudioTrack
&&
streamId
==
PRIVATE_STREAM_1
)
{
if
(
streamId
==
PRIVATE_STREAM_1
)
{
// Private stream, used for AC3 audio.
// Private stream, used for AC3 audio.
// NOTE: This may need further parsing to determine if its DTS, but that's likely only
// NOTE: This may need further parsing to determine if its DTS, but that's likely only
// valid for DVDs.
// valid for DVDs.
elementaryStreamReader
=
new
Ac3Reader
();
elementaryStreamReader
=
new
Ac3Reader
();
foundAudioTrack
=
true
;
foundAudioTrack
=
true
;
}
else
if
(!
foundAudioTrack
&&
(
streamId
&
AUDIO_STREAM_MASK
)
==
AUDIO_STREAM
)
{
lastTrackPosition
=
input
.
getPosition
();
}
else
if
((
streamId
&
AUDIO_STREAM_MASK
)
==
AUDIO_STREAM
)
{
elementaryStreamReader
=
new
MpegAudioReader
();
elementaryStreamReader
=
new
MpegAudioReader
();
foundAudioTrack
=
true
;
foundAudioTrack
=
true
;
}
else
if
(!
foundVideoTrack
&&
(
streamId
&
VIDEO_STREAM_MASK
)
==
VIDEO_STREAM
)
{
lastTrackPosition
=
input
.
getPosition
();
}
else
if
((
streamId
&
VIDEO_STREAM_MASK
)
==
VIDEO_STREAM
)
{
elementaryStreamReader
=
new
H262Reader
();
elementaryStreamReader
=
new
H262Reader
();
foundVideoTrack
=
true
;
foundVideoTrack
=
true
;
lastTrackPosition
=
input
.
getPosition
();
}
}
if
(
elementaryStreamReader
!=
null
)
{
if
(
elementaryStreamReader
!=
null
)
{
TrackIdGenerator
idGenerator
=
new
TrackIdGenerator
(
streamId
,
MAX_STREAM_ID_PLUS_ONE
);
TrackIdGenerator
idGenerator
=
new
TrackIdGenerator
(
streamId
,
MAX_STREAM_ID_PLUS_ONE
);
...
@@ -208,7 +217,11 @@ public final class PsExtractor implements Extractor {
...
@@ -208,7 +217,11 @@ public final class PsExtractor implements Extractor {
psPayloadReaders
.
put
(
streamId
,
payloadReader
);
psPayloadReaders
.
put
(
streamId
,
payloadReader
);
}
}
}
}
if
((
foundAudioTrack
&&
foundVideoTrack
)
||
input
.
getPosition
()
>
MAX_SEARCH_LENGTH
)
{
long
maxSearchPosition
=
foundAudioTrack
&&
foundVideoTrack
?
lastTrackPosition
+
MAX_SEARCH_LENGTH_AFTER_AUDIO_AND_VIDEO_FOUND
:
MAX_SEARCH_LENGTH
;
if
(
input
.
getPosition
()
>
maxSearchPosition
)
{
foundAllTracks
=
true
;
foundAllTracks
=
true
;
output
.
endTracks
();
output
.
endTracks
();
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java
View file @
8cbcfd66
...
@@ -369,6 +369,15 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
...
@@ -369,6 +369,15 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
wrappedMediaCrypto
=
mediaCrypto
.
getWrappedMediaCrypto
();
wrappedMediaCrypto
=
mediaCrypto
.
getWrappedMediaCrypto
();
drmSessionRequiresSecureDecoder
=
mediaCrypto
.
requiresSecureDecoderComponent
(
mimeType
);
drmSessionRequiresSecureDecoder
=
mediaCrypto
.
requiresSecureDecoderComponent
(
mimeType
);
}
}
if
(
deviceNeedsDrmKeysToConfigureCodecWorkaround
())
{
@DrmSession
.
State
int
drmSessionState
=
drmSession
.
getState
();
if
(
drmSessionState
==
DrmSession
.
STATE_ERROR
)
{
throw
ExoPlaybackException
.
createForRenderer
(
drmSession
.
getError
(),
getIndex
());
}
else
if
(
drmSessionState
!=
DrmSession
.
STATE_OPENED_WITH_KEYS
)
{
// Wait for keys.
return
;
}
}
}
}
if
(
codecInfo
==
null
)
{
if
(
codecInfo
==
null
)
{
...
@@ -405,7 +414,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
...
@@ -405,7 +414,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
codecAdaptationWorkaroundMode
=
codecAdaptationWorkaroundMode
(
codecName
);
codecAdaptationWorkaroundMode
=
codecAdaptationWorkaroundMode
(
codecName
);
codecNeedsDiscardToSpsWorkaround
=
codecNeedsDiscardToSpsWorkaround
(
codecName
,
format
);
codecNeedsDiscardToSpsWorkaround
=
codecNeedsDiscardToSpsWorkaround
(
codecName
,
format
);
codecNeedsFlushWorkaround
=
codecNeedsFlushWorkaround
(
codecName
);
codecNeedsFlushWorkaround
=
codecNeedsFlushWorkaround
(
codecName
);
codecNeedsEosPropagationWorkaround
=
codecNeedsEosPropagationWorkaround
(
codec
Name
);
codecNeedsEosPropagationWorkaround
=
codecNeedsEosPropagationWorkaround
(
codec
Info
);
codecNeedsEosFlushWorkaround
=
codecNeedsEosFlushWorkaround
(
codecName
);
codecNeedsEosFlushWorkaround
=
codecNeedsEosFlushWorkaround
(
codecName
);
codecNeedsEosOutputExceptionWorkaround
=
codecNeedsEosOutputExceptionWorkaround
(
codecName
);
codecNeedsEosOutputExceptionWorkaround
=
codecNeedsEosOutputExceptionWorkaround
(
codecName
);
codecNeedsMonoChannelCountWorkaround
=
codecNeedsMonoChannelCountWorkaround
(
codecName
,
format
);
codecNeedsMonoChannelCountWorkaround
=
codecNeedsMonoChannelCountWorkaround
(
codecName
,
format
);
...
@@ -1210,6 +1219,16 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
...
@@ -1210,6 +1219,16 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
}
}
/**
/**
* Returns whether the device needs keys to have been loaded into the {@link DrmSession} before
* codec configuration.
*/
private
boolean
deviceNeedsDrmKeysToConfigureCodecWorkaround
()
{
return
"Amazon"
.
equals
(
Util
.
MANUFACTURER
)
&&
(
"AFTM"
.
equals
(
Util
.
MODEL
)
// Fire TV Stick Gen 1
||
"AFTB"
.
equals
(
Util
.
MODEL
));
// Fire TV Gen 1
}
/**
* Returns whether the decoder is known to fail when flushed.
* Returns whether the decoder is known to fail when flushed.
* <p>
* <p>
* If true is returned, the renderer will work around the issue by releasing the decoder and
* If true is returned, the renderer will work around the issue by releasing the decoder and
...
@@ -1272,20 +1291,23 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
...
@@ -1272,20 +1291,23 @@ public abstract class MediaCodecRenderer extends BaseRenderer {
}
}
/**
/**
* Returns whether the decoder is known to handle the propagation of the
* Returns whether the decoder is known to handle the propagation of the
{@link
*
{@link
MediaCodec#BUFFER_FLAG_END_OF_STREAM} flag incorrectly on the host device.
* MediaCodec#BUFFER_FLAG_END_OF_STREAM} flag incorrectly on the host device.
*
<p>
*
* If true is returned, the renderer will work around the issue by approximating end of stream
*
<p>
If true is returned, the renderer will work around the issue by approximating end of stream
* behavior without relying on the flag being propagated through to an output buffer by the
* behavior without relying on the flag being propagated through to an output buffer by the
* underlying decoder.
* underlying decoder.
*
*
* @param
name The name of the decoder
.
* @param
codecInfo Information about the {@link MediaCodec}
.
* @return True if the decoder is known to handle {@link MediaCodec#BUFFER_FLAG_END_OF_STREAM}
* @return True if the decoder is known to handle {@link MediaCodec#BUFFER_FLAG_END_OF_STREAM}
* propagation incorrectly on the host device. False otherwise.
* propagation incorrectly on the host device. False otherwise.
*/
*/
private
static
boolean
codecNeedsEosPropagationWorkaround
(
String
name
)
{
private
static
boolean
codecNeedsEosPropagationWorkaround
(
MediaCodecInfo
codecInfo
)
{
return
Util
.
SDK_INT
<=
17
&&
(
"OMX.rk.video_decoder.avc"
.
equals
(
name
)
String
name
=
codecInfo
.
name
;
||
"OMX.allwinner.video.decoder.avc"
.
equals
(
name
));
return
(
Util
.
SDK_INT
<=
17
&&
(
"OMX.rk.video_decoder.avc"
.
equals
(
name
)
||
"OMX.allwinner.video.decoder.avc"
.
equals
(
name
)))
||
(
"Amazon"
.
equals
(
Util
.
MANUFACTURER
)
&&
"AFTS"
.
equals
(
Util
.
MODEL
)
&&
codecInfo
.
secure
);
}
}
/**
/**
...
...
library/core/src/main/java/com/google/android/exoplayer2/metadata/id3/InternalFrame.java
0 → 100644
View file @
8cbcfd66
/*
* 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
.
metadata
.
id3
;
import
android.os.Parcel
;
import
android.support.annotation.Nullable
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Util
;
/** Internal ID3 frame that is intended for use by the player. */
public
final
class
InternalFrame
extends
Id3Frame
{
public
static
final
String
ID
=
"----"
;
public
final
String
domain
;
public
final
String
description
;
public
final
String
text
;
public
InternalFrame
(
String
domain
,
String
description
,
String
text
)
{
super
(
ID
);
this
.
domain
=
domain
;
this
.
description
=
description
;
this
.
text
=
text
;
}
/* package */
InternalFrame
(
Parcel
in
)
{
super
(
ID
);
domain
=
Assertions
.
checkNotNull
(
in
.
readString
());
description
=
Assertions
.
checkNotNull
(
in
.
readString
());
text
=
Assertions
.
checkNotNull
(
in
.
readString
());
}
@Override
public
boolean
equals
(
@Nullable
Object
obj
)
{
if
(
this
==
obj
)
{
return
true
;
}
if
(
obj
==
null
||
getClass
()
!=
obj
.
getClass
())
{
return
false
;
}
InternalFrame
other
=
(
InternalFrame
)
obj
;
return
Util
.
areEqual
(
description
,
other
.
description
)
&&
Util
.
areEqual
(
domain
,
other
.
domain
)
&&
Util
.
areEqual
(
text
,
other
.
text
);
}
@Override
public
int
hashCode
()
{
int
result
=
17
;
result
=
31
*
result
+
(
domain
!=
null
?
domain
.
hashCode
()
:
0
);
result
=
31
*
result
+
(
description
!=
null
?
description
.
hashCode
()
:
0
);
result
=
31
*
result
+
(
text
!=
null
?
text
.
hashCode
()
:
0
);
return
result
;
}
@Override
public
String
toString
()
{
return
id
+
": domain="
+
domain
+
", description="
+
description
;
}
// Parcelable implementation.
@Override
public
void
writeToParcel
(
Parcel
dest
,
int
flags
)
{
dest
.
writeString
(
id
);
dest
.
writeString
(
domain
);
dest
.
writeString
(
text
);
}
public
static
final
Creator
<
InternalFrame
>
CREATOR
=
new
Creator
<
InternalFrame
>()
{
@Override
public
InternalFrame
createFromParcel
(
Parcel
in
)
{
return
new
InternalFrame
(
in
);
}
@Override
public
InternalFrame
[]
newArray
(
int
size
)
{
return
new
InternalFrame
[
size
];
}
};
}
library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadService.java
View file @
8cbcfd66
...
@@ -86,6 +86,7 @@ public abstract class DownloadService extends Service {
...
@@ -86,6 +86,7 @@ public abstract class DownloadService extends Service {
private
DownloadManagerListener
downloadManagerListener
;
private
DownloadManagerListener
downloadManagerListener
;
private
int
lastStartId
;
private
int
lastStartId
;
private
boolean
startedInForeground
;
private
boolean
startedInForeground
;
private
boolean
taskRemoved
;
/**
/**
* Creates a DownloadService with {@link #DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL}.
* Creates a DownloadService with {@link #DEFAULT_FOREGROUND_NOTIFICATION_UPDATE_INTERVAL}.
...
@@ -219,12 +220,17 @@ public abstract class DownloadService extends Service {
...
@@ -219,12 +220,17 @@ public abstract class DownloadService extends Service {
@Override
@Override
public
int
onStartCommand
(
Intent
intent
,
int
flags
,
int
startId
)
{
public
int
onStartCommand
(
Intent
intent
,
int
flags
,
int
startId
)
{
lastStartId
=
startId
;
lastStartId
=
startId
;
taskRemoved
=
false
;
String
intentAction
=
null
;
String
intentAction
=
null
;
if
(
intent
!=
null
)
{
if
(
intent
!=
null
)
{
intentAction
=
intent
.
getAction
();
intentAction
=
intent
.
getAction
();
startedInForeground
|=
startedInForeground
|=
intent
.
getBooleanExtra
(
KEY_FOREGROUND
,
false
)
||
ACTION_RESTART
.
equals
(
intentAction
);
intent
.
getBooleanExtra
(
KEY_FOREGROUND
,
false
)
||
ACTION_RESTART
.
equals
(
intentAction
);
}
}
// intentAction is null if the service is restarted or no action is specified.
if
(
intentAction
==
null
)
{
intentAction
=
ACTION_INIT
;
}
logd
(
"onStartCommand action: "
+
intentAction
+
" startId: "
+
startId
);
logd
(
"onStartCommand action: "
+
intentAction
+
" startId: "
+
startId
);
switch
(
intentAction
)
{
switch
(
intentAction
)
{
case
ACTION_INIT:
case
ACTION_INIT:
...
@@ -261,6 +267,12 @@ public abstract class DownloadService extends Service {
...
@@ -261,6 +267,12 @@ public abstract class DownloadService extends Service {
}
}
@Override
@Override
public
void
onTaskRemoved
(
Intent
rootIntent
)
{
logd
(
"onTaskRemoved rootIntent: "
+
rootIntent
);
taskRemoved
=
true
;
}
@Override
public
void
onDestroy
()
{
public
void
onDestroy
()
{
logd
(
"onDestroy"
);
logd
(
"onDestroy"
);
foregroundNotificationUpdater
.
stopPeriodicUpdates
();
foregroundNotificationUpdater
.
stopPeriodicUpdates
();
...
@@ -353,8 +365,13 @@ public abstract class DownloadService extends Service {
...
@@ -353,8 +365,13 @@ public abstract class DownloadService extends Service {
if
(
startedInForeground
&&
Util
.
SDK_INT
>=
26
)
{
if
(
startedInForeground
&&
Util
.
SDK_INT
>=
26
)
{
foregroundNotificationUpdater
.
showNotificationIfNotAlready
();
foregroundNotificationUpdater
.
showNotificationIfNotAlready
();
}
}
boolean
stopSelfResult
=
stopSelfResult
(
lastStartId
);
if
(
Util
.
SDK_INT
<
28
&&
taskRemoved
)
{
// See [Internal: b/74248644].
logd
(
"stopSelf("
+
lastStartId
+
") result: "
+
stopSelfResult
);
stopSelf
();
logd
(
"stopSelf()"
);
}
else
{
boolean
stopSelfResult
=
stopSelfResult
(
lastStartId
);
logd
(
"stopSelf("
+
lastStartId
+
") result: "
+
stopSelfResult
);
}
}
}
private
void
logd
(
String
message
)
{
private
void
logd
(
String
message
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdPlaybackState.java
View file @
8cbcfd66
...
@@ -344,6 +344,14 @@ public final class AdPlaybackState {
...
@@ -344,6 +344,14 @@ public final class AdPlaybackState {
return
new
AdPlaybackState
(
adGroupTimesUs
,
adGroups
,
adResumePositionUs
,
contentDurationUs
);
return
new
AdPlaybackState
(
adGroupTimesUs
,
adGroups
,
adResumePositionUs
,
contentDurationUs
);
}
}
/** Returns an instance with the specified ad marked as skipped. */
@CheckResult
public
AdPlaybackState
withSkippedAd
(
int
adGroupIndex
,
int
adIndexInAdGroup
)
{
AdGroup
[]
adGroups
=
Arrays
.
copyOf
(
this
.
adGroups
,
this
.
adGroups
.
length
);
adGroups
[
adGroupIndex
]
=
adGroups
[
adGroupIndex
].
withAdState
(
AD_STATE_SKIPPED
,
adIndexInAdGroup
);
return
new
AdPlaybackState
(
adGroupTimesUs
,
adGroups
,
adResumePositionUs
,
contentDurationUs
);
}
/** Returns an instance with the specified ad marked as having a load error. */
/** Returns an instance with the specified ad marked as having a load error. */
@CheckResult
@CheckResult
public
AdPlaybackState
withAdLoadError
(
int
adGroupIndex
,
int
adIndexInAdGroup
)
{
public
AdPlaybackState
withAdLoadError
(
int
adGroupIndex
,
int
adIndexInAdGroup
)
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea608Decoder.java
View file @
8cbcfd66
...
@@ -21,10 +21,10 @@ import android.text.Layout.Alignment;
...
@@ -21,10 +21,10 @@ import android.text.Layout.Alignment;
import
android.text.SpannableString
;
import
android.text.SpannableString
;
import
android.text.SpannableStringBuilder
;
import
android.text.SpannableStringBuilder
;
import
android.text.Spanned
;
import
android.text.Spanned
;
import
android.text.style.CharacterStyle
;
import
android.text.style.ForegroundColorSpan
;
import
android.text.style.ForegroundColorSpan
;
import
android.text.style.StyleSpan
;
import
android.text.style.StyleSpan
;
import
android.text.style.UnderlineSpan
;
import
android.text.style.UnderlineSpan
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.text.Cue
;
import
com.google.android.exoplayer2.text.Cue
;
import
com.google.android.exoplayer2.text.Subtitle
;
import
com.google.android.exoplayer2.text.Subtitle
;
...
@@ -55,15 +55,13 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -55,15 +55,13 @@ public final class Cea608Decoder extends CeaDecoder {
private
static
final
int
[]
ROW_INDICES
=
new
int
[]
{
11
,
1
,
3
,
12
,
14
,
5
,
7
,
9
};
private
static
final
int
[]
ROW_INDICES
=
new
int
[]
{
11
,
1
,
3
,
12
,
14
,
5
,
7
,
9
};
private
static
final
int
[]
COLUMN_INDICES
=
new
int
[]
{
0
,
4
,
8
,
12
,
16
,
20
,
24
,
28
};
private
static
final
int
[]
COLUMN_INDICES
=
new
int
[]
{
0
,
4
,
8
,
12
,
16
,
20
,
24
,
28
};
private
static
final
int
[]
COLORS
=
new
int
[]
{
Color
.
WHITE
,
private
static
final
int
[]
STYLE_COLORS
=
Color
.
GREEN
,
new
int
[]
{
Color
.
BLUE
,
Color
.
WHITE
,
Color
.
GREEN
,
Color
.
BLUE
,
Color
.
CYAN
,
Color
.
RED
,
Color
.
YELLOW
,
Color
.
MAGENTA
Color
.
CYAN
,
};
Color
.
RED
,
private
static
final
int
STYLE_ITALICS
=
0x07
;
Color
.
YELLOW
,
private
static
final
int
STYLE_UNCHANGED
=
0x08
;
Color
.
MAGENTA
,
};
// The default number of rows to display in roll-up captions mode.
// The default number of rows to display in roll-up captions mode.
private
static
final
int
DEFAULT_CAPTIONS_ROW_COUNT
=
4
;
private
static
final
int
DEFAULT_CAPTIONS_ROW_COUNT
=
4
;
...
@@ -377,18 +375,10 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -377,18 +375,10 @@ public final class Cea608Decoder extends CeaDecoder {
// A midrow control code advances the cursor.
// A midrow control code advances the cursor.
currentCueBuilder
.
append
(
' '
);
currentCueBuilder
.
append
(
' '
);
// cc2 - 0|0|1|0|ATRBT|U
// cc2 - 0|0|1|0|STYLE|U
// ATRBT is the 3-byte encoded attribute, and U is the underline toggle
boolean
underline
=
(
cc2
&
0x01
)
==
0x01
;
boolean
isUnderlined
=
(
cc2
&
0x01
)
==
0x01
;
int
style
=
(
cc2
>>
1
)
&
0x07
;
currentCueBuilder
.
setUnderline
(
isUnderlined
);
currentCueBuilder
.
setStyle
(
style
,
underline
);
int
attribute
=
(
cc2
>>
1
)
&
0x0F
;
if
(
attribute
==
0x07
)
{
currentCueBuilder
.
setMidrowStyle
(
new
StyleSpan
(
Typeface
.
ITALIC
),
2
);
currentCueBuilder
.
setMidrowStyle
(
new
ForegroundColorSpan
(
Color
.
WHITE
),
1
);
}
else
{
currentCueBuilder
.
setMidrowStyle
(
new
ForegroundColorSpan
(
COLORS
[
attribute
]),
1
);
}
}
}
private
void
handlePreambleAddressCode
(
byte
cc1
,
byte
cc2
)
{
private
void
handlePreambleAddressCode
(
byte
cc1
,
byte
cc2
)
{
...
@@ -414,22 +404,18 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -414,22 +404,18 @@ public final class Cea608Decoder extends CeaDecoder {
currentCueBuilder
.
setRow
(
row
);
currentCueBuilder
.
setRow
(
row
);
}
}
if
((
cc2
&
0x01
)
==
0x01
)
{
currentCueBuilder
.
setPreambleStyle
(
new
UnderlineSpan
());
}
// cc2 - 0|1|N|0|STYLE|U
// cc2 - 0|1|N|0|STYLE|U
// cc2 - 0|1|N|1|CURSR|U
// cc2 - 0|1|N|1|CURSR|U
int
attribute
=
cc2
>>
1
&
0x0F
;
boolean
isCursor
=
(
cc2
&
0x10
)
==
0x10
;
if
(
attribute
<=
0x07
)
{
boolean
underline
=
(
cc2
&
0x01
)
==
0x01
;
if
(
attribute
==
0x07
)
{
int
cursorOrStyle
=
(
cc2
>>
1
)
&
0x07
;
currentCueBuilder
.
setPreambleStyle
(
new
StyleSpan
(
Typeface
.
ITALIC
));
currentCueBuilder
.
setPreambleStyle
(
new
ForegroundColorSpan
(
Color
.
WHITE
));
// We need to call setStyle even for the isCursor case, to update the underline bit.
}
else
{
// STYLE_UNCHANGED is used for this case.
currentCueBuilder
.
setPreambleStyle
(
new
ForegroundColorSpan
(
COLORS
[
attribute
])
);
currentCueBuilder
.
setStyle
(
isCursor
?
STYLE_UNCHANGED
:
cursorOrStyle
,
underline
);
}
}
else
{
if
(
isCursor
)
{
currentCueBuilder
.
setIndent
(
COLUMN_INDICES
[
attribute
&
0x07
]);
currentCueBuilder
.
setIndent
(
COLUMN_INDICES
[
cursorOrStyle
]);
}
}
}
}
...
@@ -585,44 +571,37 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -585,44 +571,37 @@ public final class Cea608Decoder extends CeaDecoder {
private
static
class
CueBuilder
{
private
static
class
CueBuilder
{
private
static
final
int
POSITION_UNSET
=
-
1
;
// 608 captions define a 15 row by 32 column screen grid. These constants convert from 608
// 608 captions define a 15 row by 32 column screen grid. These constants convert from 608
// positions to normalized screen position.
// positions to normalized screen position.
private
static
final
int
SCREEN_CHARWIDTH
=
32
;
private
static
final
int
SCREEN_CHARWIDTH
=
32
;
private
static
final
int
BASE_ROW
=
15
;
private
static
final
int
BASE_ROW
=
15
;
private
final
List
<
CharacterStyle
>
preambleStyles
;
private
final
List
<
CueStyle
>
cueStyles
;
private
final
List
<
CueStyle
>
midrowStyles
;
private
final
List
<
SpannableString
>
rolledUpCaptions
;
private
final
List
<
SpannableString
>
rolledUpCaptions
;
private
final
S
pannableS
tringBuilder
captionStringBuilder
;
private
final
StringBuilder
captionStringBuilder
;
private
int
row
;
private
int
row
;
private
int
indent
;
private
int
indent
;
private
int
tabOffset
;
private
int
tabOffset
;
private
int
captionMode
;
private
int
captionMode
;
private
int
captionRowCount
;
private
int
captionRowCount
;
private
int
underlineStartPosition
;
public
CueBuilder
(
int
captionMode
,
int
captionRowCount
)
{
public
CueBuilder
(
int
captionMode
,
int
captionRowCount
)
{
preambleStyles
=
new
ArrayList
<>();
cueStyles
=
new
ArrayList
<>();
midrowStyles
=
new
ArrayList
<>();
rolledUpCaptions
=
new
ArrayList
<>();
rolledUpCaptions
=
new
ArrayList
<>();
captionStringBuilder
=
new
S
pannableS
tringBuilder
();
captionStringBuilder
=
new
StringBuilder
();
reset
(
captionMode
);
reset
(
captionMode
);
setCaptionRowCount
(
captionRowCount
);
setCaptionRowCount
(
captionRowCount
);
}
}
public
void
reset
(
int
captionMode
)
{
public
void
reset
(
int
captionMode
)
{
this
.
captionMode
=
captionMode
;
this
.
captionMode
=
captionMode
;
preambleStyles
.
clear
();
cueStyles
.
clear
();
midrowStyles
.
clear
();
rolledUpCaptions
.
clear
();
rolledUpCaptions
.
clear
();
captionStringBuilder
.
clear
(
);
captionStringBuilder
.
setLength
(
0
);
row
=
BASE_ROW
;
row
=
BASE_ROW
;
indent
=
0
;
indent
=
0
;
tabOffset
=
0
;
tabOffset
=
0
;
underlineStartPosition
=
POSITION_UNSET
;
}
}
public
void
setCaptionRowCount
(
int
captionRowCount
)
{
public
void
setCaptionRowCount
(
int
captionRowCount
)
{
...
@@ -630,7 +609,8 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -630,7 +609,8 @@ public final class Cea608Decoder extends CeaDecoder {
}
}
public
boolean
isEmpty
()
{
public
boolean
isEmpty
()
{
return
preambleStyles
.
isEmpty
()
&&
midrowStyles
.
isEmpty
()
&&
rolledUpCaptions
.
isEmpty
()
return
cueStyles
.
isEmpty
()
&&
rolledUpCaptions
.
isEmpty
()
&&
captionStringBuilder
.
length
()
==
0
;
&&
captionStringBuilder
.
length
()
==
0
;
}
}
...
@@ -638,6 +618,16 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -638,6 +618,16 @@ public final class Cea608Decoder extends CeaDecoder {
int
length
=
captionStringBuilder
.
length
();
int
length
=
captionStringBuilder
.
length
();
if
(
length
>
0
)
{
if
(
length
>
0
)
{
captionStringBuilder
.
delete
(
length
-
1
,
length
);
captionStringBuilder
.
delete
(
length
-
1
,
length
);
// Decrement style start positions if necessary.
for
(
int
i
=
cueStyles
.
size
()
-
1
;
i
>=
0
;
i
--)
{
CueStyle
style
=
cueStyles
.
get
(
i
);
if
(
style
.
start
==
length
)
{
style
.
start
--;
}
else
{
// All earlier cues must have style.start < length.
break
;
}
}
}
}
}
}
...
@@ -651,11 +641,8 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -651,11 +641,8 @@ public final class Cea608Decoder extends CeaDecoder {
public
void
rollUp
()
{
public
void
rollUp
()
{
rolledUpCaptions
.
add
(
buildSpannableString
());
rolledUpCaptions
.
add
(
buildSpannableString
());
captionStringBuilder
.
clear
();
captionStringBuilder
.
setLength
(
0
);
preambleStyles
.
clear
();
cueStyles
.
clear
();
midrowStyles
.
clear
();
underlineStartPosition
=
POSITION_UNSET
;
int
numRows
=
Math
.
min
(
captionRowCount
,
row
);
int
numRows
=
Math
.
min
(
captionRowCount
,
row
);
while
(
rolledUpCaptions
.
size
()
>=
numRows
)
{
while
(
rolledUpCaptions
.
size
()
>=
numRows
)
{
rolledUpCaptions
.
remove
(
0
);
rolledUpCaptions
.
remove
(
0
);
...
@@ -670,23 +657,8 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -670,23 +657,8 @@ public final class Cea608Decoder extends CeaDecoder {
tabOffset
=
tabs
;
tabOffset
=
tabs
;
}
}
public
void
setPreambleStyle
(
CharacterStyle
style
)
{
public
void
setStyle
(
int
style
,
boolean
underline
)
{
preambleStyles
.
add
(
style
);
cueStyles
.
add
(
new
CueStyle
(
style
,
underline
,
captionStringBuilder
.
length
()));
}
public
void
setMidrowStyle
(
CharacterStyle
style
,
int
nextStyleIncrement
)
{
midrowStyles
.
add
(
new
CueStyle
(
style
,
captionStringBuilder
.
length
(),
nextStyleIncrement
));
}
public
void
setUnderline
(
boolean
enabled
)
{
if
(
enabled
)
{
underlineStartPosition
=
captionStringBuilder
.
length
();
}
else
if
(
underlineStartPosition
!=
POSITION_UNSET
)
{
// underline spans won't overlap, so it's safe to modify the builder directly with them
captionStringBuilder
.
setSpan
(
new
UnderlineSpan
(),
underlineStartPosition
,
captionStringBuilder
.
length
(),
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
underlineStartPosition
=
POSITION_UNSET
;
}
}
}
public
void
append
(
char
text
)
{
public
void
append
(
char
text
)
{
...
@@ -694,31 +666,69 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -694,31 +666,69 @@ public final class Cea608Decoder extends CeaDecoder {
}
}
public
SpannableString
buildSpannableString
()
{
public
SpannableString
buildSpannableString
()
{
int
length
=
captionStringBuilder
.
length
();
SpannableStringBuilder
builder
=
new
SpannableStringBuilder
(
captionStringBuilder
);
int
length
=
builder
.
length
();
int
underlineStartPosition
=
C
.
INDEX_UNSET
;
int
italicStartPosition
=
C
.
INDEX_UNSET
;
int
colorStartPosition
=
0
;
int
color
=
Color
.
WHITE
;
boolean
nextItalic
=
false
;
int
nextColor
=
Color
.
WHITE
;
for
(
int
i
=
0
;
i
<
cueStyles
.
size
();
i
++)
{
CueStyle
cueStyle
=
cueStyles
.
get
(
i
);
boolean
underline
=
cueStyle
.
underline
;
int
style
=
cueStyle
.
style
;
if
(
style
!=
STYLE_UNCHANGED
)
{
// If the style is a color then italic is cleared.
nextItalic
=
style
==
STYLE_ITALICS
;
// If the style is italic then the color is left unchanged.
nextColor
=
style
==
STYLE_ITALICS
?
nextColor
:
STYLE_COLORS
[
style
];
}
// preamble styles apply to the entire cue
int
position
=
cueStyle
.
start
;
for
(
int
i
=
0
;
i
<
preambleStyles
.
size
();
i
++)
{
int
nextPosition
=
(
i
+
1
)
<
cueStyles
.
size
()
?
cueStyles
.
get
(
i
+
1
).
start
:
length
;
captionStringBuilder
.
setSpan
(
preambleStyles
.
get
(
i
),
0
,
length
,
if
(
position
==
nextPosition
)
{
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
// There are more cueStyles to process at the current position.
}
continue
;
}
// midrow styles only apply to part of the cue, and after preamble styles
// Process changes to underline up to the current position.
for
(
int
i
=
0
;
i
<
midrowStyles
.
size
();
i
++)
{
if
(
underlineStartPosition
!=
C
.
INDEX_UNSET
&&
!
underline
)
{
CueStyle
cueStyle
=
midrowStyles
.
get
(
i
);
setUnderlineSpan
(
builder
,
underlineStartPosition
,
position
);
int
end
=
(
i
<
midrowStyles
.
size
()
-
cueStyle
.
nextStyleIncrement
)
underlineStartPosition
=
C
.
INDEX_UNSET
;
?
midrowStyles
.
get
(
i
+
cueStyle
.
nextStyleIncrement
).
start
}
else
if
(
underlineStartPosition
==
C
.
INDEX_UNSET
&&
underline
)
{
:
length
;
underlineStartPosition
=
position
;
captionStringBuilder
.
setSpan
(
cueStyle
.
style
,
cueStyle
.
start
,
end
,
}
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
// Process changes to italic up to the current position.
if
(
italicStartPosition
!=
C
.
INDEX_UNSET
&&
!
nextItalic
)
{
setItalicSpan
(
builder
,
italicStartPosition
,
position
);
italicStartPosition
=
C
.
INDEX_UNSET
;
}
else
if
(
italicStartPosition
==
C
.
INDEX_UNSET
&&
nextItalic
)
{
italicStartPosition
=
position
;
}
// Process changes to color up to the current position.
if
(
nextColor
!=
color
)
{
setColorSpan
(
builder
,
colorStartPosition
,
position
,
color
);
color
=
nextColor
;
colorStartPosition
=
position
;
}
}
}
// special case for midrow underlines that went to the end of the cue
// Add any final spans.
if
(
underlineStartPosition
!=
POSITION_UNSET
)
{
if
(
underlineStartPosition
!=
C
.
INDEX_UNSET
&&
underlineStartPosition
!=
length
)
{
captionStringBuilder
.
setSpan
(
new
UnderlineSpan
(),
underlineStartPosition
,
length
,
setUnderlineSpan
(
builder
,
underlineStartPosition
,
length
);
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
if
(
italicStartPosition
!=
C
.
INDEX_UNSET
&&
italicStartPosition
!=
length
)
{
setItalicSpan
(
builder
,
italicStartPosition
,
length
);
}
if
(
colorStartPosition
!=
length
)
{
setColorSpan
(
builder
,
colorStartPosition
,
length
,
color
);
}
}
return
new
SpannableString
(
captionStringB
uilder
);
return
new
SpannableString
(
b
uilder
);
}
}
public
Cue
build
()
{
public
Cue
build
()
{
...
@@ -788,16 +798,34 @@ public final class Cea608Decoder extends CeaDecoder {
...
@@ -788,16 +798,34 @@ public final class Cea608Decoder extends CeaDecoder {
return
captionStringBuilder
.
toString
();
return
captionStringBuilder
.
toString
();
}
}
private
static
void
setUnderlineSpan
(
SpannableStringBuilder
builder
,
int
start
,
int
end
)
{
builder
.
setSpan
(
new
UnderlineSpan
(),
start
,
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
private
static
void
setItalicSpan
(
SpannableStringBuilder
builder
,
int
start
,
int
end
)
{
builder
.
setSpan
(
new
StyleSpan
(
Typeface
.
ITALIC
),
start
,
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
private
static
void
setColorSpan
(
SpannableStringBuilder
builder
,
int
start
,
int
end
,
int
color
)
{
if
(
color
==
Color
.
WHITE
)
{
// White is treated as the default color (i.e. no span is attached).
return
;
}
builder
.
setSpan
(
new
ForegroundColorSpan
(
color
),
start
,
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
private
static
class
CueStyle
{
private
static
class
CueStyle
{
public
final
CharacterStyle
style
;
public
final
int
style
;
public
final
int
start
;
public
final
boolean
underline
;
public
final
int
nextStyleIncrement
;
public
int
start
;
public
CueStyle
(
CharacterStyle
style
,
int
start
,
int
nextStyleIncremen
t
)
{
public
CueStyle
(
int
style
,
boolean
underline
,
int
star
t
)
{
this
.
style
=
style
;
this
.
style
=
style
;
this
.
underline
=
underline
;
this
.
start
=
start
;
this
.
start
=
start
;
this
.
nextStyleIncrement
=
nextStyleIncrement
;
}
}
}
}
...
...
library/core/src/main/java/com/google/android/exoplayer2/trackselection/DefaultTrackSelector.java
View file @
8cbcfd66
...
@@ -153,7 +153,8 @@ import java.util.concurrent.atomic.AtomicReference;
...
@@ -153,7 +153,8 @@ import java.util.concurrent.atomic.AtomicReference;
public
class
DefaultTrackSelector
extends
MappingTrackSelector
{
public
class
DefaultTrackSelector
extends
MappingTrackSelector
{
/**
/**
* A builder for {@link Parameters}.
* A builder for {@link Parameters}. See the {@link Parameters} documentation for explanations of
* the parameters that can be configured using this builder.
*/
*/
public
static
final
class
ParametersBuilder
{
public
static
final
class
ParametersBuilder
{
...
@@ -177,9 +178,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -177,9 +178,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
private
boolean
viewportOrientationMayChange
;
private
boolean
viewportOrientationMayChange
;
private
int
tunnelingAudioSessionId
;
private
int
tunnelingAudioSessionId
;
/**
/** Creates a builder with default initial values. */
* Creates a builder obtaining the initial values from {@link Parameters#DEFAULT}.
*/
public
ParametersBuilder
()
{
public
ParametersBuilder
()
{
this
(
Parameters
.
DEFAULT
);
this
(
Parameters
.
DEFAULT
);
}
}
...
@@ -343,15 +342,15 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -343,15 +342,15 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
}
/**
/**
* Equivalent to
invoking {@link #setViewportSize} with the viewport size obtained from
* Equivalent to
calling {@link #setViewportSize(int, int, boolean)} with the viewport size
* {@link Util#getPhysicalDisplaySize(Context)}.
*
obtained from
{@link Util#getPhysicalDisplaySize(Context)}.
*
*
* @param context
The context to obtain the viewport size from
.
* @param context
Any context
.
* @param viewportOrientationMayChange See {@link #viewportOrientationMayChange}.
* @param viewportOrientationMayChange See {@link
Parameters
#viewportOrientationMayChange}.
* @return This builder.
* @return This builder.
*/
*/
public
ParametersBuilder
setViewportSizeToPhysicalDisplaySize
(
Context
context
,
public
ParametersBuilder
setViewportSizeToPhysicalDisplaySize
(
boolean
viewportOrientationMayChange
)
{
Context
context
,
boolean
viewportOrientationMayChange
)
{
// Assume the viewport is fullscreen.
// Assume the viewport is fullscreen.
Point
viewportSize
=
Util
.
getPhysicalDisplaySize
(
context
);
Point
viewportSize
=
Util
.
getPhysicalDisplaySize
(
context
);
return
setViewportSize
(
viewportSize
.
x
,
viewportSize
.
y
,
viewportOrientationMayChange
);
return
setViewportSize
(
viewportSize
.
x
,
viewportSize
.
y
,
viewportOrientationMayChange
);
...
@@ -368,13 +367,16 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -368,13 +367,16 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
}
/**
/**
* See {@link Parameters#viewportWidth}, {@link Parameters#maxVideoHeight} and
* See {@link Parameters#viewportWidth}, {@link Parameters#maxVideoHeight} and
{@link
*
{@link
Parameters#viewportOrientationMayChange}.
* Parameters#viewportOrientationMayChange}.
*
*
* @param viewportWidth See {@link Parameters#viewportWidth}.
* @param viewportHeight See {@link Parameters#viewportHeight}.
* @param viewportOrientationMayChange See {@link Parameters#viewportOrientationMayChange}.
* @return This builder.
* @return This builder.
*/
*/
public
ParametersBuilder
setViewportSize
(
int
viewportWidth
,
int
viewportHeight
,
public
ParametersBuilder
setViewportSize
(
boolean
viewportOrientationMayChange
)
{
int
viewportWidth
,
int
viewportHeight
,
boolean
viewportOrientationMayChange
)
{
this
.
viewportWidth
=
viewportWidth
;
this
.
viewportWidth
=
viewportWidth
;
this
.
viewportHeight
=
viewportHeight
;
this
.
viewportHeight
=
viewportHeight
;
this
.
viewportOrientationMayChange
=
viewportOrientationMayChange
;
this
.
viewportOrientationMayChange
=
viewportOrientationMayChange
;
...
@@ -485,8 +487,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -485,8 +487,10 @@ public class DefaultTrackSelector extends MappingTrackSelector {
}
}
/**
/**
* Enables or disables tunneling. To enable tunneling, pass an audio session id to use when in
* See {@link Parameters#tunnelingAudioSessionId}.
* tunneling mode. Session ids can be generated using {@link
*
* <p>Enables or disables tunneling. To enable tunneling, pass an audio session id to use when
* in tunneling mode. Session ids can be generated using {@link
* C#generateAudioSessionIdV21(Context)}. To disable tunneling pass {@link
* C#generateAudioSessionIdV21(Context)}. To disable tunneling pass {@link
* C#AUDIO_SESSION_ID_UNSET}. Tunneling will only be activated if it's both enabled and
* C#AUDIO_SESSION_ID_UNSET}. Tunneling will only be activated if it's both enabled and
* supported by the audio and video renderers for the selected tracks.
* supported by the audio and video renderers for the selected tracks.
...
@@ -540,25 +544,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -540,25 +544,7 @@ public class DefaultTrackSelector extends MappingTrackSelector {
/** Constraint parameters for {@link DefaultTrackSelector}. */
/** Constraint parameters for {@link DefaultTrackSelector}. */
public
static
final
class
Parameters
implements
Parcelable
{
public
static
final
class
Parameters
implements
Parcelable
{
/**
/** An instance with default values. */
* An instance with default values:
*
* <ul>
* <li>No preferred audio language.
* <li>No preferred text language.
* <li>Text tracks with undetermined language are not selected if no track with {@link
* #preferredTextLanguage} is available.
* <li>All selection flags are considered for text track selections.
* <li>Lowest bitrate track selections are not forced.
* <li>Adaptation between different mime types is not allowed.
* <li>Non seamless adaptation is allowed.
* <li>No max limit for video width/height.
* <li>No max video bitrate.
* <li>Video constraints are exceeded if no supported selection can be made otherwise.
* <li>Renderer capabilities are exceeded if no supported selection can be made.
* <li>No viewport constraints.
* </ul>
*/
public
static
final
Parameters
DEFAULT
=
new
Parameters
();
public
static
final
Parameters
DEFAULT
=
new
Parameters
();
// Per renderer overrides.
// Per renderer overrides.
...
@@ -568,105 +554,131 @@ public class DefaultTrackSelector extends MappingTrackSelector {
...
@@ -568,105 +554,131 @@ public class DefaultTrackSelector extends MappingTrackSelector {
// Audio
// Audio
/**
/**
* The preferred language for audio, as well as for forced text tracks, as an ISO 639-2/T tag.
* The preferred language for audio and forced text tracks, as an ISO 639-2/T tag. {@code null}
* {@code null} selects the default track, or the first track if there's no default.
* selects the default track, or the first track if there's no default. The default value is
* {@code null}.
*/
*/
public
final
@Nullable
String
preferredAudioLanguage
;
public
final
@Nullable
String
preferredAudioLanguage
;
// Text
// Text
/**
/**
* The preferred language for text tracks as an ISO 639-2/T tag. {@code null} selects the
* The preferred language for text tracks as an ISO 639-2/T tag. {@code null} selects the
* default track if there is one, or no track otherwise.
* default track if there is one, or no track otherwise.
The default value is {@code null}.
*/
*/
public
final
@Nullable
String
preferredTextLanguage
;
public
final
@Nullable
String
preferredTextLanguage
;
/**
/**
* Whether a text track with undetermined language should be selected if no track with
* Whether a text track with undetermined language should be selected if no track with {@link
* {@link #preferredTextLanguage} is available, or if {@link #preferredTextLanguage} is unset.
* #preferredTextLanguage} is available, or if {@link #preferredTextLanguage} is unset. The
* default value is {@code false}.
*/
*/
public
final
boolean
selectUndeterminedTextLanguage
;
public
final
boolean
selectUndeterminedTextLanguage
;
/**
/**
* Bitmask of selection flags that are disabled for text track selections. See {@link
* Bitmask of selection flags that are disabled for text track selections. See {@link
* C.SelectionFlags}.
* C.SelectionFlags}.
The default value is {@code 0} (i.e. no flags).
*/
*/
public
final
int
disabledTextTrackSelectionFlags
;
public
final
int
disabledTextTrackSelectionFlags
;
// Video
// Video
/**
/**
* Maximum allowed video width.
* Maximum allowed video width. The default value is {@link Integer#MAX_VALUE} (i.e. no
* constraint).
*
* <p>To constrain adaptive video track selections to be suitable for a given viewport (the
* region of the display within which video will be played), use ({@link #viewportWidth}, {@link
* #viewportHeight} and {@link #viewportOrientationMayChange}) instead.
*/
*/
public
final
int
maxVideoWidth
;
public
final
int
maxVideoWidth
;
/**
/**
* Maximum allowed video height.
* Maximum allowed video height. The default value is {@link Integer#MAX_VALUE} (i.e. no
* constraint).
*
* <p>To constrain adaptive video track selections to be suitable for a given viewport (the
* region of the display within which video will be played), use ({@link #viewportWidth}, {@link
* #viewportHeight} and {@link #viewportOrientationMayChange}) instead.
*/
*/
public
final
int
maxVideoHeight
;
public
final
int
maxVideoHeight
;
/**
/**
* Maximum video bitrate.
* Maximum video bitrate.
The default value is {@link Integer#MAX_VALUE} (i.e. no constraint).
*/
*/
public
final
int
maxVideoBitrate
;
public
final
int
maxVideoBitrate
;
/**
/**
* Whether to exceed video constraints when no selection can be made otherwise.
* Whether to exceed the {@link #maxVideoWidth}, {@link #maxVideoHeight} and {@link
* #maxVideoBitrate} constraints when no selection can be made otherwise. The default value is
* {@code true}.
*/
*/
public
final
boolean
exceedVideoConstraintsIfNecessary
;
public
final
boolean
exceedVideoConstraintsIfNecessary
;
/**
/**
* Viewport width in pixels. Constrains video tracks selections for adaptive playbacks so that
* Viewport width in pixels. Constrains video track selections for adaptive content so that only
* only tracks suitable for the viewport are selected.
* tracks suitable for the viewport are selected. The default value is {@link Integer#MAX_VALUE}
* (i.e. no constraint).
*/
*/
public
final
int
viewportWidth
;
public
final
int
viewportWidth
;
/**
/**
* Viewport height in pixels. Constrains video tracks selections for adaptive playbacks so that
* Viewport height in pixels. Constrains video track selections for adaptive content so that
* only tracks suitable for the viewport are selected.
* only tracks suitable for the viewport are selected. The default value is {@link
* Integer#MAX_VALUE} (i.e. no constraint).
*/
*/
public
final
int
viewportHeight
;
public
final
int
viewportHeight
;
/**
/**
* Whether the viewport orientation may change during playback. Constrains video tracks
* Whether the viewport orientation may change during playback. Constrains video track
* selections for adaptive playbacks so that only tracks suitable for the viewport are selected.
* selections for adaptive content so that only tracks suitable for the viewport are selected.
* The default value is {@code true}.
*/
*/
public
final
boolean
viewportOrientationMayChange
;
public
final
boolean
viewportOrientationMayChange
;
// General
// General
/**
/**
* Whether to force selection of the single lowest bitrate audio and video tracks that comply
* Whether to force selection of the single lowest bitrate audio and video tracks that comply
* with all other constraints.
* with all other constraints.
The default value is {@code false}.
*/
*/
public
final
boolean
forceLowestBitrate
;
public
final
boolean
forceLowestBitrate
;
/**
/**
* Whether to allow adaptive selections containing mixed mime types.
* Whether to allow adaptive selections containing mixed mime types. The default value is {@code
* false}.
*/
*/
public
final
boolean
allowMixedMimeAdaptiveness
;
public
final
boolean
allowMixedMimeAdaptiveness
;
/**
/**
* Whether to allow adaptive selections where adaptation may not be completely seamless.
* Whether to allow adaptive selections where adaptation may not be completely seamless. The
* default value is {@code true}.
*/
*/
public
final
boolean
allowNonSeamlessAdaptiveness
;
public
final
boolean
allowNonSeamlessAdaptiveness
;
/**
/**
* Whether to exceed renderer capabilities when no selection can be made otherwise.
* Whether to exceed renderer capabilities when no selection can be made otherwise.
*
* <p>This parameter applies when all of the tracks available for a renderer exceed the
* renderer's reported capabilities. If the parameter is {@code true} then the lowest quality
* track will still be selected. Playback may succeed if the renderer has under-reported its
* true capabilities. If {@code false} then no track will be selected. The default value is
* {@code true}.
*/
*/
public
final
boolean
exceedRendererCapabilitiesIfNecessary
;
public
final
boolean
exceedRendererCapabilitiesIfNecessary
;
/**
/**
* The audio session id to use when tunneling, or {@link C#AUDIO_SESSION_ID_UNSET} if tunneling
* The audio session id to use when tunneling, or {@link C#AUDIO_SESSION_ID_UNSET} if tunneling
* is not to be enabled.
* is disabled. The default value is {@link C#AUDIO_SESSION_ID_UNSET} (i.e. tunneling is
* disabled).
*/
*/
public
final
int
tunnelingAudioSessionId
;
public
final
int
tunnelingAudioSessionId
;
private
Parameters
()
{
private
Parameters
()
{
this
(
this
(
new
SparseArray
<
Map
<
TrackGroupArray
,
SelectionOverride
>>(),
/* selectionOverrides= */
new
SparseArray
<
Map
<
TrackGroupArray
,
SelectionOverride
>>(),
new
SparseBooleanArray
(),
/* rendererDisabledFlags= */
new
SparseBooleanArray
(),
null
,
/* preferredAudioLanguage= */
null
,
null
,
/* preferredTextLanguage= */
null
,
false
,
/* selectUndeterminedTextLanguage= */
false
,
0
,
/* disabledTextTrackSelectionFlags= */
0
,
false
,
/* forceLowestBitrate= */
false
,
false
,
/* allowMixedMimeAdaptiveness= */
false
,
true
,
/* allowNonSeamlessAdaptiveness= */
true
,
Integer
.
MAX_VALUE
,
/* maxVideoWidth= */
Integer
.
MAX_VALUE
,
Integer
.
MAX_VALUE
,
/* maxVideoHeight= */
Integer
.
MAX_VALUE
,
Integer
.
MAX_VALUE
,
/* maxVideoBitrate= */
Integer
.
MAX_VALUE
,
true
,
/* exceedVideoConstraintsIfNecessary= */
true
,
true
,
/* exceedRendererCapabilitiesIfNecessary= */
true
,
Integer
.
MAX_VALUE
,
/* viewportWidth= */
Integer
.
MAX_VALUE
,
Integer
.
MAX_VALUE
,
/* viewportHeight= */
Integer
.
MAX_VALUE
,
true
,
/* viewportOrientationMayChange= */
true
,
C
.
AUDIO_SESSION_ID_UNSET
);
/* tunnelingAudioSessionId= */
C
.
AUDIO_SESSION_ID_UNSET
);
}
}
/* package */
Parameters
(
/* package */
Parameters
(
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/TimestampAdjuster.java
View file @
8cbcfd66
...
@@ -30,7 +30,8 @@ public final class TimestampAdjuster {
...
@@ -30,7 +30,8 @@ public final class TimestampAdjuster {
public
static
final
long
DO_NOT_OFFSET
=
Long
.
MAX_VALUE
;
public
static
final
long
DO_NOT_OFFSET
=
Long
.
MAX_VALUE
;
/**
/**
* The value one greater than the largest representable (33 bit) MPEG-2 TS presentation timestamp.
* The value one greater than the largest representable (33 bit) MPEG-2 TS 90 kHz clock
* presentation timestamp.
*/
*/
private
static
final
long
MAX_PTS_PLUS_ONE
=
0x200000000
L
;
private
static
final
long
MAX_PTS_PLUS_ONE
=
0x200000000
L
;
...
@@ -38,13 +39,13 @@ public final class TimestampAdjuster {
...
@@ -38,13 +39,13 @@ public final class TimestampAdjuster {
private
long
timestampOffsetUs
;
private
long
timestampOffsetUs
;
// Volatile to allow isInitialized to be called on a different thread to adjustSampleTimestamp.
// Volatile to allow isInitialized to be called on a different thread to adjustSampleTimestamp.
private
volatile
long
lastSampleTimestamp
;
private
volatile
long
lastSampleTimestamp
Us
;
/**
/**
* @param firstSampleTimestampUs See {@link #setFirstSampleTimestampUs(long)}.
* @param firstSampleTimestampUs See {@link #setFirstSampleTimestampUs(long)}.
*/
*/
public
TimestampAdjuster
(
long
firstSampleTimestampUs
)
{
public
TimestampAdjuster
(
long
firstSampleTimestampUs
)
{
lastSampleTimestamp
=
C
.
TIME_UNSET
;
lastSampleTimestamp
Us
=
C
.
TIME_UNSET
;
setFirstSampleTimestampUs
(
firstSampleTimestampUs
);
setFirstSampleTimestampUs
(
firstSampleTimestampUs
);
}
}
...
@@ -56,30 +57,24 @@ public final class TimestampAdjuster {
...
@@ -56,30 +57,24 @@ public final class TimestampAdjuster {
* {@link #DO_NOT_OFFSET} if presentation timestamps should not be offset.
* {@link #DO_NOT_OFFSET} if presentation timestamps should not be offset.
*/
*/
public
synchronized
void
setFirstSampleTimestampUs
(
long
firstSampleTimestampUs
)
{
public
synchronized
void
setFirstSampleTimestampUs
(
long
firstSampleTimestampUs
)
{
Assertions
.
checkState
(
lastSampleTimestamp
==
C
.
TIME_UNSET
);
Assertions
.
checkState
(
lastSampleTimestamp
Us
==
C
.
TIME_UNSET
);
this
.
firstSampleTimestampUs
=
firstSampleTimestampUs
;
this
.
firstSampleTimestampUs
=
firstSampleTimestampUs
;
}
}
/**
/** Returns the last value passed to {@link #setFirstSampleTimestampUs(long)}. */
* Returns the first adjusted sample timestamp in microseconds.
*
* @return The first adjusted sample timestamp in microseconds.
*/
public
long
getFirstSampleTimestampUs
()
{
public
long
getFirstSampleTimestampUs
()
{
return
firstSampleTimestampUs
;
return
firstSampleTimestampUs
;
}
}
/**
/**
* Returns the last adjusted timestamp. If no timestamp has been adjusted, returns
* Returns the last value obtained from {@link #adjustSampleTimestamp}. If {@link
* {@code firstSampleTimestampUs} as provided to the constructor. If this value is
* #adjustSampleTimestamp} has not been called, returns the result of calling {@link
* {@link #DO_NOT_OFFSET}, returns {@link C#TIME_UNSET}.
* #getFirstSampleTimestampUs()}. If this value is {@link #DO_NOT_OFFSET}, returns {@link
*
* C#TIME_UNSET}.
* @return The last adjusted timestamp. If not present, {@code firstSampleTimestampUs} is
* returned unless equal to {@link #DO_NOT_OFFSET}, in which case {@link C#TIME_UNSET} is
* returned.
*/
*/
public
long
getLastAdjustedTimestampUs
()
{
public
long
getLastAdjustedTimestampUs
()
{
return
lastSampleTimestamp
!=
C
.
TIME_UNSET
?
lastSampleTimestamp
return
lastSampleTimestampUs
!=
C
.
TIME_UNSET
?
(
lastSampleTimestampUs
+
timestampOffsetUs
)
:
firstSampleTimestampUs
!=
DO_NOT_OFFSET
?
firstSampleTimestampUs
:
C
.
TIME_UNSET
;
:
firstSampleTimestampUs
!=
DO_NOT_OFFSET
?
firstSampleTimestampUs
:
C
.
TIME_UNSET
;
}
}
...
@@ -93,44 +88,47 @@ public final class TimestampAdjuster {
...
@@ -93,44 +88,47 @@ public final class TimestampAdjuster {
* be offset.
* be offset.
*/
*/
public
long
getTimestampOffsetUs
()
{
public
long
getTimestampOffsetUs
()
{
return
firstSampleTimestampUs
==
DO_NOT_OFFSET
?
0
return
firstSampleTimestampUs
==
DO_NOT_OFFSET
:
lastSampleTimestamp
==
C
.
TIME_UNSET
?
C
.
TIME_UNSET
:
timestampOffsetUs
;
?
0
:
lastSampleTimestampUs
==
C
.
TIME_UNSET
?
C
.
TIME_UNSET
:
timestampOffsetUs
;
}
}
/**
/**
* Resets the instance to its initial state.
* Resets the instance to its initial state.
*/
*/
public
void
reset
()
{
public
void
reset
()
{
lastSampleTimestamp
=
C
.
TIME_UNSET
;
lastSampleTimestamp
Us
=
C
.
TIME_UNSET
;
}
}
/**
/**
* Scales and offsets an MPEG-2 TS presentation timestamp considering wraparound.
* Scales and offsets an MPEG-2 TS presentation timestamp considering wraparound.
*
*
* @param pts
The
MPEG-2 TS presentation timestamp.
* @param pts
90Khz A 90 kHz clock
MPEG-2 TS presentation timestamp.
* @return The adjusted timestamp in microseconds.
* @return The adjusted timestamp in microseconds.
*/
*/
public
long
adjustTsTimestamp
(
long
pts
)
{
public
long
adjustTsTimestamp
(
long
pts
90Khz
)
{
if
(
pts
==
C
.
TIME_UNSET
)
{
if
(
pts
90Khz
==
C
.
TIME_UNSET
)
{
return
C
.
TIME_UNSET
;
return
C
.
TIME_UNSET
;
}
}
if
(
lastSampleTimestamp
!=
C
.
TIME_UNSET
)
{
if
(
lastSampleTimestamp
Us
!=
C
.
TIME_UNSET
)
{
// The wrap count for the current PTS may be closestWrapCount or (closestWrapCount - 1),
// The wrap count for the current PTS may be closestWrapCount or (closestWrapCount - 1),
// and we need to snap to the one closest to lastSampleTimestamp.
// and we need to snap to the one closest to lastSampleTimestamp
Us
.
long
lastPts
=
usToPts
(
lastSampleTimestamp
);
long
lastPts
=
usToPts
(
lastSampleTimestamp
Us
);
long
closestWrapCount
=
(
lastPts
+
(
MAX_PTS_PLUS_ONE
/
2
))
/
MAX_PTS_PLUS_ONE
;
long
closestWrapCount
=
(
lastPts
+
(
MAX_PTS_PLUS_ONE
/
2
))
/
MAX_PTS_PLUS_ONE
;
long
ptsWrapBelow
=
pts
+
(
MAX_PTS_PLUS_ONE
*
(
closestWrapCount
-
1
));
long
ptsWrapBelow
=
pts90Khz
+
(
MAX_PTS_PLUS_ONE
*
(
closestWrapCount
-
1
));
long
ptsWrapAbove
=
pts
+
(
MAX_PTS_PLUS_ONE
*
closestWrapCount
);
long
ptsWrapAbove
=
pts90Khz
+
(
MAX_PTS_PLUS_ONE
*
closestWrapCount
);
pts
=
Math
.
abs
(
ptsWrapBelow
-
lastPts
)
<
Math
.
abs
(
ptsWrapAbove
-
lastPts
)
pts90Khz
=
?
ptsWrapBelow
:
ptsWrapAbove
;
Math
.
abs
(
ptsWrapBelow
-
lastPts
)
<
Math
.
abs
(
ptsWrapAbove
-
lastPts
)
?
ptsWrapBelow
:
ptsWrapAbove
;
}
}
return
adjustSampleTimestamp
(
ptsToUs
(
pts
));
return
adjustSampleTimestamp
(
ptsToUs
(
pts
90Khz
));
}
}
/**
/**
* Offsets a
sample
timestamp in microseconds.
* Offsets a timestamp in microseconds.
*
*
* @param timeUs The timestamp
of a sample to adjust
.
* @param timeUs The timestamp
to adjust in microseconds
.
* @return The adjusted timestamp in microseconds.
* @return The adjusted timestamp in microseconds.
*/
*/
public
long
adjustSampleTimestamp
(
long
timeUs
)
{
public
long
adjustSampleTimestamp
(
long
timeUs
)
{
...
@@ -138,15 +136,15 @@ public final class TimestampAdjuster {
...
@@ -138,15 +136,15 @@ public final class TimestampAdjuster {
return
C
.
TIME_UNSET
;
return
C
.
TIME_UNSET
;
}
}
// Record the adjusted PTS to adjust for wraparound next time.
// Record the adjusted PTS to adjust for wraparound next time.
if
(
lastSampleTimestamp
!=
C
.
TIME_UNSET
)
{
if
(
lastSampleTimestamp
Us
!=
C
.
TIME_UNSET
)
{
lastSampleTimestamp
=
timeUs
;
lastSampleTimestamp
Us
=
timeUs
;
}
else
{
}
else
{
if
(
firstSampleTimestampUs
!=
DO_NOT_OFFSET
)
{
if
(
firstSampleTimestampUs
!=
DO_NOT_OFFSET
)
{
// Calculate the timestamp offset.
// Calculate the timestamp offset.
timestampOffsetUs
=
firstSampleTimestampUs
-
timeUs
;
timestampOffsetUs
=
firstSampleTimestampUs
-
timeUs
;
}
}
synchronized
(
this
)
{
synchronized
(
this
)
{
lastSampleTimestamp
=
timeUs
;
lastSampleTimestamp
Us
=
timeUs
;
// Notify threads waiting for this adjuster to be initialized.
// Notify threads waiting for this adjuster to be initialized.
notifyAll
();
notifyAll
();
}
}
...
@@ -160,15 +158,15 @@ public final class TimestampAdjuster {
...
@@ -160,15 +158,15 @@ public final class TimestampAdjuster {
* @throws InterruptedException If the thread was interrupted.
* @throws InterruptedException If the thread was interrupted.
*/
*/
public
synchronized
void
waitUntilInitialized
()
throws
InterruptedException
{
public
synchronized
void
waitUntilInitialized
()
throws
InterruptedException
{
while
(
lastSampleTimestamp
==
C
.
TIME_UNSET
)
{
while
(
lastSampleTimestamp
Us
==
C
.
TIME_UNSET
)
{
wait
();
wait
();
}
}
}
}
/**
/**
* Converts a
value in MPEG-2 timestamp units to the corresponding value
in microseconds.
* Converts a
90 kHz clock timestamp to a timestamp
in microseconds.
*
*
* @param pts A
value in MPEG-2 timestamp units
.
* @param pts A
90 kHz clock timestamp
.
* @return The corresponding value in microseconds.
* @return The corresponding value in microseconds.
*/
*/
public
static
long
ptsToUs
(
long
pts
)
{
public
static
long
ptsToUs
(
long
pts
)
{
...
@@ -176,10 +174,10 @@ public final class TimestampAdjuster {
...
@@ -176,10 +174,10 @@ public final class TimestampAdjuster {
}
}
/**
/**
* Converts a
value in microseconds to the corresponding values in MPEG-2 timestamp units
.
* Converts a
timestamp in microseconds to a 90 kHz clock timestamp
.
*
*
* @param us A value in microseconds.
* @param us A value in microseconds.
* @return The corresponding value
in MPEG-2 timestamp units
.
* @return The corresponding value
as a 90 kHz clock timestamp
.
*/
*/
public
static
long
usToPts
(
long
us
)
{
public
static
long
usToPts
(
long
us
)
{
return
(
us
*
90000
)
/
C
.
MICROS_PER_SECOND
;
return
(
us
*
90000
)
/
C
.
MICROS_PER_SECOND
;
...
...
library/core/src/main/java/com/google/android/exoplayer2/util/XmlPullParserUtil.java
View file @
8cbcfd66
...
@@ -56,8 +56,7 @@ public final class XmlPullParserUtil {
...
@@ -56,8 +56,7 @@ public final class XmlPullParserUtil {
* @return Whether the current event is a start tag with the specified name.
* @return Whether the current event is a start tag with the specified name.
* @throws XmlPullParserException If an error occurs querying the parser.
* @throws XmlPullParserException If an error occurs querying the parser.
*/
*/
public
static
boolean
isStartTag
(
XmlPullParser
xpp
,
String
name
)
public
static
boolean
isStartTag
(
XmlPullParser
xpp
,
String
name
)
throws
XmlPullParserException
{
throws
XmlPullParserException
{
return
isStartTag
(
xpp
)
&&
xpp
.
getName
().
equals
(
name
);
return
isStartTag
(
xpp
)
&&
xpp
.
getName
().
equals
(
name
);
}
}
...
@@ -73,21 +72,58 @@ public final class XmlPullParserUtil {
...
@@ -73,21 +72,58 @@ public final class XmlPullParserUtil {
}
}
/**
/**
* Returns whether the current event is a start tag with the specified name. If the current event
* has a raw name then its prefix is stripped before matching.
*
* @param xpp The {@link XmlPullParser} to query.
* @param name The specified name.
* @return Whether the current event is a start tag with the specified name.
* @throws XmlPullParserException If an error occurs querying the parser.
*/
public
static
boolean
isStartTagIgnorePrefix
(
XmlPullParser
xpp
,
String
name
)
throws
XmlPullParserException
{
return
isStartTag
(
xpp
)
&&
stripPrefix
(
xpp
.
getName
()).
equals
(
name
);
}
/**
* Returns the value of an attribute of the current start tag.
* Returns the value of an attribute of the current start tag.
*
*
* @param xpp The {@link XmlPullParser} to query.
* @param xpp The {@link XmlPullParser} to query.
* @param attributeName The name of the attribute.
* @param attributeName The name of the attribute.
* @return The value of the attribute, or null if the current event is not a start tag or if no
* @return The value of the attribute, or null if the current event is not a start tag or if no
*
no
such attribute was found.
* such attribute was found.
*/
*/
public
static
String
getAttributeValue
(
XmlPullParser
xpp
,
String
attributeName
)
{
public
static
String
getAttributeValue
(
XmlPullParser
xpp
,
String
attributeName
)
{
int
attributeCount
=
xpp
.
getAttributeCount
();
int
attributeCount
=
xpp
.
getAttributeCount
();
for
(
int
i
=
0
;
i
<
attributeCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
attributeCount
;
i
++)
{
if
(
attributeName
.
equals
(
xpp
.
getAttributeName
(
i
)))
{
if
(
xpp
.
getAttributeName
(
i
).
equals
(
attributeName
))
{
return
xpp
.
getAttributeValue
(
i
);
}
}
return
null
;
}
/**
* Returns the value of an attribute of the current start tag. Any raw attribute names in the
* current start tag have their prefixes stripped before matching.
*
* @param xpp The {@link XmlPullParser} to query.
* @param attributeName The name of the attribute.
* @return The value of the attribute, or null if the current event is not a start tag or if no
* such attribute was found.
*/
public
static
String
getAttributeValueIgnorePrefix
(
XmlPullParser
xpp
,
String
attributeName
)
{
int
attributeCount
=
xpp
.
getAttributeCount
();
for
(
int
i
=
0
;
i
<
attributeCount
;
i
++)
{
if
(
stripPrefix
(
xpp
.
getAttributeName
(
i
)).
equals
(
attributeName
))
{
return
xpp
.
getAttributeValue
(
i
);
return
xpp
.
getAttributeValue
(
i
);
}
}
}
}
return
null
;
return
null
;
}
}
private
static
String
stripPrefix
(
String
name
)
{
int
prefixSeparatorIndex
=
name
.
indexOf
(
':'
);
return
prefixSeparatorIndex
==
-
1
?
name
:
name
.
substring
(
prefixSeparatorIndex
+
1
);
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java
View file @
8cbcfd66
...
@@ -84,6 +84,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -84,6 +84,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
// pending output streams that have fewer frames than the codec latency.
// pending output streams that have fewer frames than the codec latency.
private
static
final
int
MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT
=
10
;
private
static
final
int
MAX_PENDING_OUTPUT_STREAM_OFFSET_COUNT
=
10
;
private
static
boolean
evaluatedDeviceNeedsSetOutputSurfaceWorkaround
;
private
static
boolean
deviceNeedsSetOutputSurfaceWorkaround
;
private
final
Context
context
;
private
final
Context
context
;
private
final
VideoFrameReleaseTimeHelper
frameReleaseTimeHelper
;
private
final
VideoFrameReleaseTimeHelper
frameReleaseTimeHelper
;
private
final
EventDispatcher
eventDispatcher
;
private
final
EventDispatcher
eventDispatcher
;
...
@@ -459,7 +462,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -459,7 +462,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
if
(
areAdaptationCompatible
(
codecInfo
.
adaptive
,
oldFormat
,
newFormat
)
if
(
areAdaptationCompatible
(
codecInfo
.
adaptive
,
oldFormat
,
newFormat
)
&&
newFormat
.
width
<=
codecMaxValues
.
width
&&
newFormat
.
width
<=
codecMaxValues
.
width
&&
newFormat
.
height
<=
codecMaxValues
.
height
&&
newFormat
.
height
<=
codecMaxValues
.
height
&&
getMaxInputSize
(
newFormat
)
<=
codecMaxValues
.
inputSize
)
{
&&
getMaxInputSize
(
codecInfo
,
newFormat
)
<=
codecMaxValues
.
inputSize
)
{
return
oldFormat
.
initializationDataEquals
(
newFormat
)
return
oldFormat
.
initializationDataEquals
(
newFormat
)
?
KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION
?
KEEP_CODEC_RESULT_YES_WITHOUT_RECONFIGURATION
:
KEEP_CODEC_RESULT_YES_WITH_RECONFIGURATION
;
:
KEEP_CODEC_RESULT_YES_WITH_RECONFIGURATION
;
...
@@ -981,7 +984,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -981,7 +984,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
throws
DecoderQueryException
{
throws
DecoderQueryException
{
int
maxWidth
=
format
.
width
;
int
maxWidth
=
format
.
width
;
int
maxHeight
=
format
.
height
;
int
maxHeight
=
format
.
height
;
int
maxInputSize
=
getMaxInputSize
(
format
);
int
maxInputSize
=
getMaxInputSize
(
codecInfo
,
format
);
if
(
streamFormats
.
length
==
1
)
{
if
(
streamFormats
.
length
==
1
)
{
// The single entry in streamFormats must correspond to the format for which the codec is
// The single entry in streamFormats must correspond to the format for which the codec is
// being configured.
// being configured.
...
@@ -994,7 +997,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -994,7 +997,7 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
(
streamFormat
.
width
==
Format
.
NO_VALUE
||
streamFormat
.
height
==
Format
.
NO_VALUE
);
(
streamFormat
.
width
==
Format
.
NO_VALUE
||
streamFormat
.
height
==
Format
.
NO_VALUE
);
maxWidth
=
Math
.
max
(
maxWidth
,
streamFormat
.
width
);
maxWidth
=
Math
.
max
(
maxWidth
,
streamFormat
.
width
);
maxHeight
=
Math
.
max
(
maxHeight
,
streamFormat
.
height
);
maxHeight
=
Math
.
max
(
maxHeight
,
streamFormat
.
height
);
maxInputSize
=
Math
.
max
(
maxInputSize
,
getMaxInputSize
(
streamFormat
));
maxInputSize
=
Math
.
max
(
maxInputSize
,
getMaxInputSize
(
codecInfo
,
streamFormat
));
}
}
}
}
if
(
haveUnknownDimensions
)
{
if
(
haveUnknownDimensions
)
{
...
@@ -1004,7 +1007,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1004,7 +1007,9 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
maxWidth
=
Math
.
max
(
maxWidth
,
codecMaxSize
.
x
);
maxWidth
=
Math
.
max
(
maxWidth
,
codecMaxSize
.
x
);
maxHeight
=
Math
.
max
(
maxHeight
,
codecMaxSize
.
y
);
maxHeight
=
Math
.
max
(
maxHeight
,
codecMaxSize
.
y
);
maxInputSize
=
maxInputSize
=
Math
.
max
(
maxInputSize
,
getMaxInputSize
(
format
.
sampleMimeType
,
maxWidth
,
maxHeight
));
Math
.
max
(
maxInputSize
,
getMaxInputSize
(
codecInfo
,
format
.
sampleMimeType
,
maxWidth
,
maxHeight
));
Log
.
w
(
TAG
,
"Codec max resolution adjusted to: "
+
maxWidth
+
"x"
+
maxHeight
);
Log
.
w
(
TAG
,
"Codec max resolution adjusted to: "
+
maxWidth
+
"x"
+
maxHeight
);
}
}
}
}
...
@@ -1053,13 +1058,14 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1053,13 +1058,14 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
}
/**
/**
* Returns a maximum input buffer size for a given format.
* Returns a maximum input buffer size for a given
codec and
format.
*
*
* @param codecInfo Information about the {@link MediaCodec} being configured.
* @param format The format.
* @param format The format.
* @return A maximum input buffer size in bytes, or {@link Format#NO_VALUE} if a maximum could not
* @return A maximum input buffer size in bytes, or {@link Format#NO_VALUE} if a maximum could not
* be determined.
* be determined.
*/
*/
private
static
int
getMaxInputSize
(
Format
format
)
{
private
static
int
getMaxInputSize
(
MediaCodecInfo
codecInfo
,
Format
format
)
{
if
(
format
.
maxInputSize
!=
Format
.
NO_VALUE
)
{
if
(
format
.
maxInputSize
!=
Format
.
NO_VALUE
)
{
// The format defines an explicit maximum input size. Add the total size of initialization
// The format defines an explicit maximum input size. Add the total size of initialization
// data buffers, as they may need to be queued in the same input buffer as the largest sample.
// data buffers, as they may need to be queued in the same input buffer as the largest sample.
...
@@ -1072,20 +1078,22 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1072,20 +1078,22 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
}
else
{
}
else
{
// Calculated maximum input sizes are overestimates, so it's not necessary to add the size of
// Calculated maximum input sizes are overestimates, so it's not necessary to add the size of
// initialization data.
// initialization data.
return
getMaxInputSize
(
format
.
sampleMimeType
,
format
.
width
,
format
.
height
);
return
getMaxInputSize
(
codecInfo
,
format
.
sampleMimeType
,
format
.
width
,
format
.
height
);
}
}
}
}
/**
/**
* Returns a maximum input size for a given mime type, width and height.
* Returns a maximum input size for a given
codec,
mime type, width and height.
*
*
* @param codecInfo Information about the {@link MediaCodec} being configured.
* @param sampleMimeType The format mime type.
* @param sampleMimeType The format mime type.
* @param width The width in pixels.
* @param width The width in pixels.
* @param height The height in pixels.
* @param height The height in pixels.
* @return A maximum input size in bytes, or {@link Format#NO_VALUE} if a maximum could not be
* @return A maximum input size in bytes, or {@link Format#NO_VALUE} if a maximum could not be
* determined.
* determined.
*/
*/
private
static
int
getMaxInputSize
(
String
sampleMimeType
,
int
width
,
int
height
)
{
private
static
int
getMaxInputSize
(
MediaCodecInfo
codecInfo
,
String
sampleMimeType
,
int
width
,
int
height
)
{
if
(
width
==
Format
.
NO_VALUE
||
height
==
Format
.
NO_VALUE
)
{
if
(
width
==
Format
.
NO_VALUE
||
height
==
Format
.
NO_VALUE
)
{
// We can't infer a maximum input size without video dimensions.
// We can't infer a maximum input size without video dimensions.
return
Format
.
NO_VALUE
;
return
Format
.
NO_VALUE
;
...
@@ -1101,9 +1109,12 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1101,9 +1109,12 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
minCompressionRatio
=
2
;
minCompressionRatio
=
2
;
break
;
break
;
case
MimeTypes
.
VIDEO_H264
:
case
MimeTypes
.
VIDEO_H264
:
if
(
"BRAVIA 4K 2015"
.
equals
(
Util
.
MODEL
))
{
if
(
"BRAVIA 4K 2015"
.
equals
(
Util
.
MODEL
)
// Sony Bravia 4K
// The Sony BRAVIA 4k TV has input buffers that are too small for the calculated 4k video
||
(
"Amazon"
.
equals
(
Util
.
MANUFACTURER
)
// maximum input size, so use the default value.
&&
(
"KFSOWI"
.
equals
(
Util
.
MODEL
)
// Kindle Soho
||
(
"AFTS"
.
equals
(
Util
.
MODEL
)
&&
codecInfo
.
secure
))))
{
// Fire TV Gen 2
// Use the default value for cases where platform limitations may prevent buffers of the
// calculated maximum input size from being allocated.
return
Format
.
NO_VALUE
;
return
Format
.
NO_VALUE
;
}
}
// Round up width/height to an integer number of macroblocks.
// Round up width/height to an integer number of macroblocks.
...
@@ -1163,14 +1174,36 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1163,14 +1174,36 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
return
Util
.
SDK_INT
<=
22
&&
"foster"
.
equals
(
Util
.
DEVICE
)
&&
"NVIDIA"
.
equals
(
Util
.
MANUFACTURER
);
return
Util
.
SDK_INT
<=
22
&&
"foster"
.
equals
(
Util
.
DEVICE
)
&&
"NVIDIA"
.
equals
(
Util
.
MANUFACTURER
);
}
}
/*
* TODO:
*
* 1. Validate that Android device certification now ensures correct behavior, and add a
* corresponding SDK_INT upper bound for applying the workaround (probably SDK_INT < 26).
* 2. Determine a complete list of affected devices.
* 3. Some of the devices in this list only fail to support setOutputSurface when switching from
* a SurfaceView provided Surface to a Surface of another type (e.g. TextureView/DummySurface),
* and vice versa. One hypothesis is that setOutputSurface fails when the surfaces have
* different pixel formats. If we can find a way to query the Surface instances to determine
* whether this case applies, then we'll be able to provide a more targeted workaround.
*/
/**
/**
* Returns whether the
device
is known to implement {@link MediaCodec#setOutputSurface(Surface)}
* Returns whether the
codec
is known to implement {@link MediaCodec#setOutputSurface(Surface)}
* incorrectly.
* incorrectly.
* <p>
*
* If true is returned then we fall back to releasing and re-instantiating the codec instead.
* <p>If true is returned then we fall back to releasing and re-instantiating the codec instead.
*
* @param name The name of the codec.
* @return True if the device is known to implement {@link MediaCodec#setOutputSurface(Surface)}
* incorrectly.
*/
*/
private
static
boolean
codecNeedsSetOutputSurfaceWorkaround
(
String
name
)
{
protected
boolean
codecNeedsSetOutputSurfaceWorkaround
(
String
name
)
{
// Work around https://github.com/google/ExoPlayer/issues/3236,
if
(
Util
.
SDK_INT
>=
27
||
name
.
startsWith
(
"OMX.google"
))
{
// Devices running API level 27 or later should also be unaffected. Google OMX decoders are
// not known to have this issue on any API level.
return
false
;
}
// Work around:
// https://github.com/google/ExoPlayer/issues/3236,
// https://github.com/google/ExoPlayer/issues/3355,
// https://github.com/google/ExoPlayer/issues/3355,
// https://github.com/google/ExoPlayer/issues/3439,
// https://github.com/google/ExoPlayer/issues/3439,
// https://github.com/google/ExoPlayer/issues/3724,
// https://github.com/google/ExoPlayer/issues/3724,
...
@@ -1179,28 +1212,150 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
...
@@ -1179,28 +1212,150 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer {
// https://github.com/google/ExoPlayer/issues/4084,
// https://github.com/google/ExoPlayer/issues/4084,
// https://github.com/google/ExoPlayer/issues/4104,
// https://github.com/google/ExoPlayer/issues/4104,
// https://github.com/google/ExoPlayer/issues/4134,
// https://github.com/google/ExoPlayer/issues/4134,
// https://github.com/google/ExoPlayer/issues/4315.
// https://github.com/google/ExoPlayer/issues/4315,
return
((
"deb"
.
equals
(
Util
.
DEVICE
)
// Nexus 7 (2013)
// https://github.com/google/ExoPlayer/issues/4419,
||
"flo"
.
equals
(
Util
.
DEVICE
)
// Nexus 7 (2013)
// https://github.com/google/ExoPlayer/issues/4460,
||
"mido"
.
equals
(
Util
.
DEVICE
)
// Redmi Note 4
// https://github.com/google/ExoPlayer/issues/4468.
||
"santoni"
.
equals
(
Util
.
DEVICE
))
// Redmi 4X
synchronized
(
MediaCodecVideoRenderer
.
class
)
{
&&
"OMX.qcom.video.decoder.avc"
.
equals
(
name
))
if
(!
evaluatedDeviceNeedsSetOutputSurfaceWorkaround
)
{
||
((
"tcl_eu"
.
equals
(
Util
.
DEVICE
)
// TCL Percee TV
switch
(
Util
.
DEVICE
)
{
||
"SVP-DTV15"
.
equals
(
Util
.
DEVICE
)
// Sony Bravia 4K 2015
case
"1601"
:
||
"BRAVIA_ATV2"
.
equals
(
Util
.
DEVICE
)
// Sony Bravia 4K GB
case
"1713"
:
||
Util
.
DEVICE
.
startsWith
(
"panell_"
)
// Motorola Moto C Plus
case
"1714"
:
||
"F3311"
.
equals
(
Util
.
DEVICE
)
// Sony Xperia E5
case
"A10-70F"
:
||
"M5c"
.
equals
(
Util
.
DEVICE
)
// Meizu M5C
case
"A1601"
:
||
"QM16XE_U"
.
equals
(
Util
.
DEVICE
)
// Philips QM163E
case
"A2016a40"
:
||
"A7010a48"
.
equals
(
Util
.
DEVICE
)
// Lenovo K4 Note
case
"A7000-a"
:
||
"woods_f"
.
equals
(
Util
.
MODEL
)
// Moto E (4)
case
"A7000plus"
:
||
"watson"
.
equals
(
Util
.
DEVICE
))
// Moto C
case
"A7010a48"
:
&&
"OMX.MTK.VIDEO.DECODER.AVC"
.
equals
(
name
))
case
"A7020a48"
:
||
((
"ALE-L21"
.
equals
(
Util
.
MODEL
)
// Huawei P8 Lite
case
"AquaPowerM"
:
||
"CAM-L21"
.
equals
(
Util
.
MODEL
))
// Huawei Y6II
case
"Aura_Note_2"
:
&&
"OMX.k3.video.decoder.avc"
.
equals
(
name
))
case
"BLACK-1X"
:
||
((
"HUAWEI VNS-L21"
.
equals
(
Util
.
MODEL
))
// Huawei P9 Lite
case
"BRAVIA_ATV2"
:
&&
"OMX.IMG.MSVDX.Decoder.AVC"
.
equals
(
name
));
case
"C1"
:
case
"ComioS1"
:
case
"CP8676_I02"
:
case
"CPH1609"
:
case
"CPY83_I00"
:
case
"cv1"
:
case
"cv3"
:
case
"deb"
:
case
"E5643"
:
case
"ELUGA_A3_Pro"
:
case
"ELUGA_Note"
:
case
"ELUGA_Prim"
:
case
"ELUGA_Ray_X"
:
case
"EverStar_S"
:
case
"F3111"
:
case
"F3113"
:
case
"F3116"
:
case
"F3211"
:
case
"F3213"
:
case
"F3215"
:
case
"F3311"
:
case
"flo"
:
case
"GiONEE_CBL7513"
:
case
"GiONEE_GBL7319"
:
case
"GIONEE_GBL7360"
:
case
"GIONEE_SWW1609"
:
case
"GIONEE_SWW1627"
:
case
"GIONEE_SWW1631"
:
case
"GIONEE_WBL5708"
:
case
"GIONEE_WBL7365"
:
case
"GIONEE_WBL7519"
:
case
"griffin"
:
case
"htc_e56ml_dtul"
:
case
"hwALE-H"
:
case
"HWBLN-H"
:
case
"HWCAM-H"
:
case
"HWVNS-H"
:
case
"iball8735_9806"
:
case
"Infinix-X572"
:
case
"iris60"
:
case
"itel_S41"
:
case
"j2xlteins"
:
case
"JGZ"
:
case
"K50a40"
:
case
"le_x6"
:
case
"LS-5017"
:
case
"M5c"
:
case
"manning"
:
case
"marino_f"
:
case
"MEIZU_M5"
:
case
"mh"
:
case
"mido"
:
case
"MX6"
:
case
"namath"
:
case
"nicklaus_f"
:
case
"NX541J"
:
case
"NX573J"
:
case
"OnePlus5T"
:
case
"p212"
:
case
"P681"
:
case
"P85"
:
case
"panell_d"
:
case
"panell_dl"
:
case
"panell_ds"
:
case
"panell_dt"
:
case
"PB2-670M"
:
case
"PGN528"
:
case
"PGN610"
:
case
"PGN611"
:
case
"Phantom6"
:
case
"Pixi4-7_3G"
:
case
"Pixi5-10_4G"
:
case
"PLE"
:
case
"PRO7S"
:
case
"Q350"
:
case
"Q4260"
:
case
"Q427"
:
case
"Q4310"
:
case
"Q5"
:
case
"QM16XE_U"
:
case
"QX1"
:
case
"santoni"
:
case
"Slate_Pro"
:
case
"SVP-DTV15"
:
case
"s905x018"
:
case
"taido_row"
:
case
"TB3-730F"
:
case
"TB3-730X"
:
case
"TB3-850F"
:
case
"TB3-850M"
:
case
"tcl_eu"
:
case
"V1"
:
case
"V23GB"
:
case
"V5"
:
case
"vernee_M5"
:
case
"watson"
:
case
"whyred"
:
case
"woods_f"
:
case
"woods_fn"
:
case
"X3_HK"
:
case
"XE2X"
:
case
"XT1663"
:
case
"Z12_PRO"
:
case
"Z80"
:
deviceNeedsSetOutputSurfaceWorkaround
=
true
;
break
;
default
:
// Do nothing.
break
;
}
switch
(
Util
.
MODEL
)
{
case
"AFTA"
:
case
"AFTN"
:
deviceNeedsSetOutputSurfaceWorkaround
=
true
;
break
;
default
:
// Do nothing.
break
;
}
evaluatedDeviceNeedsSetOutputSurfaceWorkaround
=
true
;
}
}
return
deviceNeedsSetOutputSurfaceWorkaround
;
}
}
protected
static
final
class
CodecMaxValues
{
protected
static
final
class
CodecMaxValues
{
...
...
library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java
View file @
8cbcfd66
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2;
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
com
.
google
.
common
.
truth
.
Truth
.
assertThat
;
import
static
org
.
junit
.
Assert
.
fail
;
import
static
org
.
junit
.
Assert
.
fail
;
import
android.support.annotation.Nullable
;
import
android.view.Surface
;
import
android.view.Surface
;
import
com.google.android.exoplayer2.Player.DefaultEventListener
;
import
com.google.android.exoplayer2.Player.DefaultEventListener
;
import
com.google.android.exoplayer2.Player.EventListener
;
import
com.google.android.exoplayer2.Player.EventListener
;
...
@@ -1981,6 +1982,44 @@ public final class ExoPlayerTest {
...
@@ -1981,6 +1982,44 @@ public final class ExoPlayerTest {
}
}
@Test
@Test
public
void
testRepeatedSeeksToUnpreparedPeriodInSameWindowKeepsWindowSequenceNumber
()
throws
Exception
{
Timeline
timeline
=
new
FakeTimeline
(
new
TimelineWindowDefinition
(
/* periodCount= */
2
,
/* id= */
0
,
/* isSeekable= */
true
,
/* isDynamic= */
false
,
/* durationUs= */
10
*
C
.
MICROS_PER_SECOND
));
FakeMediaSource
mediaSource
=
new
FakeMediaSource
(
timeline
,
/* manifest= */
null
);
ActionSchedule
actionSchedule
=
new
ActionSchedule
.
Builder
(
"testSeekToUnpreparedPeriod"
)
.
pause
()
.
waitForPlaybackState
(
Player
.
STATE_READY
)
.
seek
(
/* windowIndex= */
0
,
/* positionMs= */
9999
)
.
seek
(
/* windowIndex= */
0
,
/* positionMs= */
1
)
.
seek
(
/* windowIndex= */
0
,
/* positionMs= */
9999
)
.
play
()
.
build
();
ExoPlayerTestRunner
testRunner
=
new
ExoPlayerTestRunner
.
Builder
()
.
setMediaSource
(
mediaSource
)
.
setActionSchedule
(
actionSchedule
)
.
build
()
.
start
()
.
blockUntilEnded
(
TIMEOUT_MS
);
testRunner
.
assertPlayedPeriodIndices
(
0
,
1
,
0
,
1
);
assertThat
(
mediaSource
.
getCreatedMediaPeriods
())
.
containsAllOf
(
new
MediaPeriodId
(
/* periodIndex= */
0
,
/* windowSequenceNumber= */
0
),
new
MediaPeriodId
(
/* periodIndex= */
1
,
/* windowSequenceNumber= */
0
));
assertThat
(
mediaSource
.
getCreatedMediaPeriods
())
.
doesNotContain
(
new
MediaPeriodId
(
/* periodIndex= */
1
,
/* windowSequenceNumber= */
1
));
}
@Test
public
void
testRecursivePlayerChangesReportConsistentValuesForAllListeners
()
throws
Exception
{
public
void
testRecursivePlayerChangesReportConsistentValuesForAllListeners
()
throws
Exception
{
// We add two listeners to the player. The first stops the player as soon as it's ready and both
// We add two listeners to the player. The first stops the player as soon as it's ready and both
// record the state change events they receive.
// record the state change events they receive.
...
@@ -2040,7 +2079,7 @@ public final class ExoPlayerTest {
...
@@ -2040,7 +2079,7 @@ public final class ExoPlayerTest {
final
EventListener
eventListener
=
final
EventListener
eventListener
=
new
DefaultEventListener
()
{
new
DefaultEventListener
()
{
@Override
@Override
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
int
reason
)
{
public
void
onTimelineChanged
(
Timeline
timeline
,
@Nullable
Object
manifest
,
int
reason
)
{
if
(
timeline
.
isEmpty
())
{
if
(
timeline
.
isEmpty
())
{
playerReference
.
get
().
setPlayWhenReady
(
/* playWhenReady= */
false
);
playerReference
.
get
().
setPlayWhenReady
(
/* playWhenReady= */
false
);
}
}
...
...
library/core/src/test/java/com/google/android/exoplayer2/source/ads/AdPlaybackStateTest.java
View file @
8cbcfd66
...
@@ -90,6 +90,19 @@ public final class AdPlaybackStateTest {
...
@@ -90,6 +90,19 @@ public final class AdPlaybackStateTest {
}
}
@Test
@Test
public
void
testGetFirstAdIndexToPlaySkipsSkippedAd
()
{
state
=
state
.
withAdCount
(
/* adGroupIndex= */
0
,
/* adCount= */
3
);
state
=
state
.
withAdUri
(
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
,
TEST_URI
);
state
=
state
.
withAdUri
(
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
2
,
TEST_URI
);
state
=
state
.
withSkippedAd
(
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
);
assertThat
(
state
.
adGroups
[
0
].
getFirstAdIndexToPlay
()).
isEqualTo
(
1
);
assertThat
(
state
.
adGroups
[
0
].
states
[
1
]).
isEqualTo
(
AdPlaybackState
.
AD_STATE_UNAVAILABLE
);
assertThat
(
state
.
adGroups
[
0
].
states
[
2
]).
isEqualTo
(
AdPlaybackState
.
AD_STATE_AVAILABLE
);
}
@Test
public
void
testGetFirstAdIndexToPlaySkipsErrorAds
()
{
public
void
testGetFirstAdIndexToPlaySkipsErrorAds
()
{
state
=
state
.
withAdCount
(
/* adGroupIndex= */
0
,
/* adCount= */
3
);
state
=
state
.
withAdCount
(
/* adGroupIndex= */
0
,
/* adCount= */
3
);
state
=
state
.
withAdUri
(
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
,
TEST_URI
);
state
=
state
.
withAdUri
(
/* adGroupIndex= */
0
,
/* adIndexInAdGroup= */
0
,
TEST_URI
);
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaPeriod.java
View file @
8cbcfd66
...
@@ -18,7 +18,6 @@ package com.google.android.exoplayer2.source.dash;
...
@@ -18,7 +18,6 @@ package com.google.android.exoplayer2.source.dash;
import
android.support.annotation.IntDef
;
import
android.support.annotation.IntDef
;
import
android.support.annotation.Nullable
;
import
android.support.annotation.Nullable
;
import
android.util.Pair
;
import
android.util.Pair
;
import
android.util.SparseArray
;
import
android.util.SparseIntArray
;
import
android.util.SparseIntArray
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.Format
;
import
com.google.android.exoplayer2.Format
;
...
@@ -62,7 +61,6 @@ import java.util.List;
...
@@ -62,7 +61,6 @@ import java.util.List;
/* package */
final
int
id
;
/* package */
final
int
id
;
private
final
DashChunkSource
.
Factory
chunkSourceFactory
;
private
final
DashChunkSource
.
Factory
chunkSourceFactory
;
private
final
int
minLoadableRetryCount
;
private
final
int
minLoadableRetryCount
;
private
final
EventDispatcher
eventDispatcher
;
private
final
long
elapsedRealtimeOffset
;
private
final
long
elapsedRealtimeOffset
;
private
final
LoaderErrorThrower
manifestLoaderErrorThrower
;
private
final
LoaderErrorThrower
manifestLoaderErrorThrower
;
private
final
Allocator
allocator
;
private
final
Allocator
allocator
;
...
@@ -73,6 +71,7 @@ import java.util.List;
...
@@ -73,6 +71,7 @@ import java.util.List;
private
final
IdentityHashMap
<
ChunkSampleStream
<
DashChunkSource
>,
PlayerTrackEmsgHandler
>
private
final
IdentityHashMap
<
ChunkSampleStream
<
DashChunkSource
>,
PlayerTrackEmsgHandler
>
trackEmsgHandlerBySampleStream
;
trackEmsgHandlerBySampleStream
;
private
EventDispatcher
eventDispatcher
;
private
@Nullable
Callback
callback
;
private
@Nullable
Callback
callback
;
private
ChunkSampleStream
<
DashChunkSource
>[]
sampleStreams
;
private
ChunkSampleStream
<
DashChunkSource
>[]
sampleStreams
;
private
EventSampleStream
[]
eventSampleStreams
;
private
EventSampleStream
[]
eventSampleStreams
;
...
@@ -127,6 +126,13 @@ import java.util.List;
...
@@ -127,6 +126,13 @@ import java.util.List;
*/
*/
public
void
updateManifest
(
DashManifest
manifest
,
int
periodIndex
)
{
public
void
updateManifest
(
DashManifest
manifest
,
int
periodIndex
)
{
this
.
manifest
=
manifest
;
this
.
manifest
=
manifest
;
if
(
this
.
periodIndex
!=
periodIndex
)
{
eventDispatcher
=
eventDispatcher
.
withParameters
(
/* windowIndex= */
0
,
eventDispatcher
.
mediaPeriodId
.
copyWithPeriodIndex
(
periodIndex
),
manifest
.
getPeriod
(
periodIndex
).
startMs
);
}
this
.
periodIndex
=
periodIndex
;
this
.
periodIndex
=
periodIndex
;
playerEmsgHandler
.
updateManifest
(
manifest
);
playerEmsgHandler
.
updateManifest
(
manifest
);
if
(
sampleStreams
!=
null
)
{
if
(
sampleStreams
!=
null
)
{
...
@@ -139,7 +145,10 @@ import java.util.List;
...
@@ -139,7 +145,10 @@ import java.util.List;
for
(
EventSampleStream
eventSampleStream
:
eventSampleStreams
)
{
for
(
EventSampleStream
eventSampleStream
:
eventSampleStreams
)
{
for
(
EventStream
eventStream
:
eventStreams
)
{
for
(
EventStream
eventStream
:
eventStreams
)
{
if
(
eventStream
.
id
().
equals
(
eventSampleStream
.
eventStreamId
()))
{
if
(
eventStream
.
id
().
equals
(
eventSampleStream
.
eventStreamId
()))
{
eventSampleStream
.
updateEventStream
(
eventStream
,
manifest
.
dynamic
);
int
lastPeriodIndex
=
manifest
.
getPeriodCount
()
-
1
;
eventSampleStream
.
updateEventStream
(
eventStream
,
/* eventStreamAppendable= */
manifest
.
dynamic
&&
periodIndex
==
lastPeriodIndex
);
break
;
break
;
}
}
}
}
...
@@ -186,126 +195,34 @@ import java.util.List;
...
@@ -186,126 +195,34 @@ import java.util.List;
@Override
@Override
public
long
selectTracks
(
TrackSelection
[]
selections
,
boolean
[]
mayRetainStreamFlags
,
public
long
selectTracks
(
TrackSelection
[]
selections
,
boolean
[]
mayRetainStreamFlags
,
SampleStream
[]
streams
,
boolean
[]
streamResetFlags
,
long
positionUs
)
{
SampleStream
[]
streams
,
boolean
[]
streamResetFlags
,
long
positionUs
)
{
SparseArray
<
ChunkSampleStream
<
DashChunkSource
>>
primarySampleStreams
=
new
SparseArray
<>();
int
[]
streamIndexToTrackGroupIndex
=
getStreamIndexToTrackGroupIndex
(
selections
);
List
<
EventSampleStream
>
eventSampleStreamList
=
new
ArrayList
<>();
releaseDisabledStreams
(
selections
,
mayRetainStreamFlags
,
streams
);
releaseOrphanEmbeddedStreams
(
selections
,
streams
,
streamIndexToTrackGroupIndex
);
selectPrimarySampleStreams
(
selections
,
mayRetainStreamFlags
,
streams
,
streamResetFlags
,
selectNewStreams
(
positionUs
,
primarySampleStreams
);
selections
,
streams
,
streamResetFlags
,
positionUs
,
streamIndexToTrackGroupIndex
);
selectEventSampleStreams
(
selections
,
mayRetainStreamFlags
,
streams
,
streamResetFlags
,
eventSampleStreamList
);
ArrayList
<
ChunkSampleStream
<
DashChunkSource
>>
sampleStreamList
=
new
ArrayList
<>();
selectEmbeddedSampleStreams
(
selections
,
mayRetainStreamFlags
,
streams
,
streamResetFlags
,
ArrayList
<
EventSampleStream
>
eventSampleStreamList
=
new
ArrayList
<>();
positionUs
,
primarySampleStreams
);
for
(
SampleStream
sampleStream
:
streams
)
{
if
(
sampleStream
instanceof
ChunkSampleStream
)
{
sampleStreams
=
newSampleStreamArray
(
primarySampleStreams
.
size
());
@SuppressWarnings
(
"unchecked"
)
for
(
int
i
=
0
;
i
<
sampleStreams
.
length
;
i
++)
{
ChunkSampleStream
<
DashChunkSource
>
stream
=
sampleStreams
[
i
]
=
primarySampleStreams
.
valueAt
(
i
);
(
ChunkSampleStream
<
DashChunkSource
>)
sampleStream
;
sampleStreamList
.
add
(
stream
);
}
else
if
(
sampleStream
instanceof
EventSampleStream
)
{
eventSampleStreamList
.
add
((
EventSampleStream
)
sampleStream
);
}
}
}
sampleStreams
=
newSampleStreamArray
(
sampleStreamList
.
size
());
sampleStreamList
.
toArray
(
sampleStreams
);
eventSampleStreams
=
new
EventSampleStream
[
eventSampleStreamList
.
size
()];
eventSampleStreams
=
new
EventSampleStream
[
eventSampleStreamList
.
size
()];
eventSampleStreamList
.
toArray
(
eventSampleStreams
);
eventSampleStreamList
.
toArray
(
eventSampleStreams
);
compositeSequenceableLoader
=
compositeSequenceableLoader
=
compositeSequenceableLoaderFactory
.
createCompositeSequenceableLoader
(
sampleStreams
);
compositeSequenceableLoaderFactory
.
createCompositeSequenceableLoader
(
sampleStreams
);
return
positionUs
;
return
positionUs
;
}
}
private
void
selectPrimarySampleStreams
(
TrackSelection
[]
selections
,
boolean
[]
mayRetainStreamFlags
,
SampleStream
[]
streams
,
boolean
[]
streamResetFlags
,
long
positionUs
,
SparseArray
<
ChunkSampleStream
<
DashChunkSource
>>
primarySampleStreams
)
{
for
(
int
i
=
0
;
i
<
selections
.
length
;
i
++)
{
if
(
streams
[
i
]
instanceof
ChunkSampleStream
)
{
@SuppressWarnings
(
"unchecked"
)
ChunkSampleStream
<
DashChunkSource
>
stream
=
(
ChunkSampleStream
<
DashChunkSource
>)
streams
[
i
];
if
(
selections
[
i
]
==
null
||
!
mayRetainStreamFlags
[
i
])
{
stream
.
release
(
this
);
streams
[
i
]
=
null
;
}
else
{
int
trackGroupIndex
=
trackGroups
.
indexOf
(
selections
[
i
].
getTrackGroup
());
primarySampleStreams
.
put
(
trackGroupIndex
,
stream
);
}
}
if
(
streams
[
i
]
==
null
&&
selections
[
i
]
!=
null
)
{
int
trackGroupIndex
=
trackGroups
.
indexOf
(
selections
[
i
].
getTrackGroup
());
TrackGroupInfo
trackGroupInfo
=
trackGroupInfos
[
trackGroupIndex
];
if
(
trackGroupInfo
.
trackGroupCategory
==
TrackGroupInfo
.
CATEGORY_PRIMARY
)
{
ChunkSampleStream
<
DashChunkSource
>
stream
=
buildSampleStream
(
trackGroupInfo
,
selections
[
i
],
positionUs
);
primarySampleStreams
.
put
(
trackGroupIndex
,
stream
);
streams
[
i
]
=
stream
;
streamResetFlags
[
i
]
=
true
;
}
}
}
}
private
void
selectEventSampleStreams
(
TrackSelection
[]
selections
,
boolean
[]
mayRetainStreamFlags
,
SampleStream
[]
streams
,
boolean
[]
streamResetFlags
,
List
<
EventSampleStream
>
eventSampleStreamsList
)
{
for
(
int
i
=
0
;
i
<
selections
.
length
;
i
++)
{
if
(
streams
[
i
]
instanceof
EventSampleStream
)
{
EventSampleStream
stream
=
(
EventSampleStream
)
streams
[
i
];
if
(
selections
[
i
]
==
null
||
!
mayRetainStreamFlags
[
i
])
{
streams
[
i
]
=
null
;
}
else
{
eventSampleStreamsList
.
add
(
stream
);
}
}
if
(
streams
[
i
]
==
null
&&
selections
[
i
]
!=
null
)
{
int
trackGroupIndex
=
trackGroups
.
indexOf
(
selections
[
i
].
getTrackGroup
());
TrackGroupInfo
trackGroupInfo
=
trackGroupInfos
[
trackGroupIndex
];
if
(
trackGroupInfo
.
trackGroupCategory
==
TrackGroupInfo
.
CATEGORY_MANIFEST_EVENTS
)
{
EventStream
eventStream
=
eventStreams
.
get
(
trackGroupInfo
.
eventStreamGroupIndex
);
Format
format
=
selections
[
i
].
getTrackGroup
().
getFormat
(
0
);
EventSampleStream
stream
=
new
EventSampleStream
(
eventStream
,
format
,
manifest
.
dynamic
);
streams
[
i
]
=
stream
;
streamResetFlags
[
i
]
=
true
;
eventSampleStreamsList
.
add
(
stream
);
}
}
}
}
private
void
selectEmbeddedSampleStreams
(
TrackSelection
[]
selections
,
boolean
[]
mayRetainStreamFlags
,
SampleStream
[]
streams
,
boolean
[]
streamResetFlags
,
long
positionUs
,
SparseArray
<
ChunkSampleStream
<
DashChunkSource
>>
primarySampleStreams
)
{
for
(
int
i
=
0
;
i
<
selections
.
length
;
i
++)
{
if
((
streams
[
i
]
instanceof
EmbeddedSampleStream
||
streams
[
i
]
instanceof
EmptySampleStream
)
&&
(
selections
[
i
]
==
null
||
!
mayRetainStreamFlags
[
i
]))
{
// The stream is for an embedded track and is either no longer selected or needs replacing.
releaseIfEmbeddedSampleStream
(
streams
[
i
]);
streams
[
i
]
=
null
;
}
// We need to consider replacing the stream even if it's non-null because the primary stream
// may have been replaced, selected or deselected.
if
(
selections
[
i
]
!=
null
)
{
int
trackGroupIndex
=
trackGroups
.
indexOf
(
selections
[
i
].
getTrackGroup
());
TrackGroupInfo
trackGroupInfo
=
trackGroupInfos
[
trackGroupIndex
];
if
(
trackGroupInfo
.
trackGroupCategory
==
TrackGroupInfo
.
CATEGORY_EMBEDDED
)
{
ChunkSampleStream
<?>
primaryStream
=
primarySampleStreams
.
get
(
trackGroupInfo
.
primaryTrackGroupIndex
);
SampleStream
stream
=
streams
[
i
];
boolean
mayRetainStream
=
primaryStream
==
null
?
stream
instanceof
EmptySampleStream
:
(
stream
instanceof
EmbeddedSampleStream
&&
((
EmbeddedSampleStream
)
stream
).
parent
==
primaryStream
);
if
(!
mayRetainStream
)
{
releaseIfEmbeddedSampleStream
(
stream
);
streams
[
i
]
=
primaryStream
==
null
?
new
EmptySampleStream
()
:
primaryStream
.
selectEmbeddedTrack
(
positionUs
,
trackGroupInfo
.
trackType
);
streamResetFlags
[
i
]
=
true
;
}
}
}
}
}
@Override
@Override
public
void
discardBuffer
(
long
positionUs
,
boolean
toKeyframe
)
{
public
void
discardBuffer
(
long
positionUs
,
boolean
toKeyframe
)
{
for
(
ChunkSampleStream
<
DashChunkSource
>
sampleStream
:
sampleStreams
)
{
for
(
ChunkSampleStream
<
DashChunkSource
>
sampleStream
:
sampleStreams
)
{
...
@@ -372,6 +289,124 @@ import java.util.List;
...
@@ -372,6 +289,124 @@ import java.util.List;
// Internal methods.
// Internal methods.
private
int
[]
getStreamIndexToTrackGroupIndex
(
TrackSelection
[]
selections
)
{
int
[]
streamIndexToTrackGroupIndex
=
new
int
[
selections
.
length
];
for
(
int
i
=
0
;
i
<
selections
.
length
;
i
++)
{
if
(
selections
[
i
]
!=
null
)
{
streamIndexToTrackGroupIndex
[
i
]
=
trackGroups
.
indexOf
(
selections
[
i
].
getTrackGroup
());
}
else
{
streamIndexToTrackGroupIndex
[
i
]
=
C
.
INDEX_UNSET
;
}
}
return
streamIndexToTrackGroupIndex
;
}
private
void
releaseDisabledStreams
(
TrackSelection
[]
selections
,
boolean
[]
mayRetainStreamFlags
,
SampleStream
[]
streams
)
{
for
(
int
i
=
0
;
i
<
selections
.
length
;
i
++)
{
if
(
selections
[
i
]
==
null
||
!
mayRetainStreamFlags
[
i
])
{
if
(
streams
[
i
]
instanceof
ChunkSampleStream
)
{
@SuppressWarnings
(
"unchecked"
)
ChunkSampleStream
<
DashChunkSource
>
stream
=
(
ChunkSampleStream
<
DashChunkSource
>)
streams
[
i
];
stream
.
release
(
this
);
}
else
if
(
streams
[
i
]
instanceof
EmbeddedSampleStream
)
{
((
EmbeddedSampleStream
)
streams
[
i
]).
release
();
}
streams
[
i
]
=
null
;
}
}
}
private
void
releaseOrphanEmbeddedStreams
(
TrackSelection
[]
selections
,
SampleStream
[]
streams
,
int
[]
streamIndexToTrackGroupIndex
)
{
for
(
int
i
=
0
;
i
<
selections
.
length
;
i
++)
{
if
(
streams
[
i
]
instanceof
EmptySampleStream
||
streams
[
i
]
instanceof
EmbeddedSampleStream
)
{
// We need to release an embedded stream if the corresponding primary stream is released.
int
primaryStreamIndex
=
getPrimaryStreamIndex
(
i
,
streamIndexToTrackGroupIndex
);
boolean
mayRetainStream
;
if
(
primaryStreamIndex
==
C
.
INDEX_UNSET
)
{
// If the corresponding primary stream is not selected, we may retain an existing
// EmptySampleStream.
mayRetainStream
=
streams
[
i
]
instanceof
EmptySampleStream
;
}
else
{
// If the corresponding primary stream is selected, we may retain the embedded stream if
// the stream's parent still matches.
mayRetainStream
=
(
streams
[
i
]
instanceof
EmbeddedSampleStream
)
&&
((
EmbeddedSampleStream
)
streams
[
i
]).
parent
==
streams
[
primaryStreamIndex
];
}
if
(!
mayRetainStream
)
{
if
(
streams
[
i
]
instanceof
EmbeddedSampleStream
)
{
((
EmbeddedSampleStream
)
streams
[
i
]).
release
();
}
streams
[
i
]
=
null
;
}
}
}
}
private
void
selectNewStreams
(
TrackSelection
[]
selections
,
SampleStream
[]
streams
,
boolean
[]
streamResetFlags
,
long
positionUs
,
int
[]
streamIndexToTrackGroupIndex
)
{
// Create newly selected primary and event streams.
for
(
int
i
=
0
;
i
<
selections
.
length
;
i
++)
{
if
(
streams
[
i
]
==
null
&&
selections
[
i
]
!=
null
)
{
streamResetFlags
[
i
]
=
true
;
int
trackGroupIndex
=
streamIndexToTrackGroupIndex
[
i
];
TrackGroupInfo
trackGroupInfo
=
trackGroupInfos
[
trackGroupIndex
];
if
(
trackGroupInfo
.
trackGroupCategory
==
TrackGroupInfo
.
CATEGORY_PRIMARY
)
{
streams
[
i
]
=
buildSampleStream
(
trackGroupInfo
,
selections
[
i
],
positionUs
);
}
else
if
(
trackGroupInfo
.
trackGroupCategory
==
TrackGroupInfo
.
CATEGORY_MANIFEST_EVENTS
)
{
EventStream
eventStream
=
eventStreams
.
get
(
trackGroupInfo
.
eventStreamGroupIndex
);
Format
format
=
selections
[
i
].
getTrackGroup
().
getFormat
(
0
);
streams
[
i
]
=
new
EventSampleStream
(
eventStream
,
format
,
manifest
.
dynamic
);
}
}
}
// Create newly selected embedded streams from the corresponding primary stream. Note that this
// second pass is needed because the primary stream may not have been created yet in a first
// pass if the index of the primary stream is greater than the index of the embedded stream.
for
(
int
i
=
0
;
i
<
selections
.
length
;
i
++)
{
if
(
streams
[
i
]
==
null
&&
selections
[
i
]
!=
null
)
{
int
trackGroupIndex
=
streamIndexToTrackGroupIndex
[
i
];
TrackGroupInfo
trackGroupInfo
=
trackGroupInfos
[
trackGroupIndex
];
if
(
trackGroupInfo
.
trackGroupCategory
==
TrackGroupInfo
.
CATEGORY_EMBEDDED
)
{
int
primaryStreamIndex
=
getPrimaryStreamIndex
(
i
,
streamIndexToTrackGroupIndex
);
if
(
primaryStreamIndex
==
C
.
INDEX_UNSET
)
{
// If an embedded track is selected without the corresponding primary track, create an
// empty sample stream instead.
streams
[
i
]
=
new
EmptySampleStream
();
}
else
{
streams
[
i
]
=
((
ChunkSampleStream
)
streams
[
primaryStreamIndex
])
.
selectEmbeddedTrack
(
positionUs
,
trackGroupInfo
.
trackType
);
}
}
}
}
}
private
int
getPrimaryStreamIndex
(
int
embeddedStreamIndex
,
int
[]
streamIndexToTrackGroupIndex
)
{
int
embeddedTrackGroupIndex
=
streamIndexToTrackGroupIndex
[
embeddedStreamIndex
];
if
(
embeddedTrackGroupIndex
==
C
.
INDEX_UNSET
)
{
return
C
.
INDEX_UNSET
;
}
int
primaryTrackGroupIndex
=
trackGroupInfos
[
embeddedTrackGroupIndex
].
primaryTrackGroupIndex
;
for
(
int
i
=
0
;
i
<
streamIndexToTrackGroupIndex
.
length
;
i
++)
{
int
trackGroupIndex
=
streamIndexToTrackGroupIndex
[
i
];
if
(
trackGroupIndex
==
primaryTrackGroupIndex
&&
trackGroupInfos
[
trackGroupIndex
].
trackGroupCategory
==
TrackGroupInfo
.
CATEGORY_PRIMARY
)
{
return
i
;
}
}
return
C
.
INDEX_UNSET
;
}
private
static
Pair
<
TrackGroupArray
,
TrackGroupInfo
[]>
buildTrackGroups
(
private
static
Pair
<
TrackGroupArray
,
TrackGroupInfo
[]>
buildTrackGroups
(
List
<
AdaptationSet
>
adaptationSets
,
List
<
EventStream
>
eventStreams
)
{
List
<
AdaptationSet
>
adaptationSets
,
List
<
EventStream
>
eventStreams
)
{
int
[][]
groupedAdaptationSetIndices
=
getGroupedAdaptationSetIndices
(
adaptationSets
);
int
[][]
groupedAdaptationSetIndices
=
getGroupedAdaptationSetIndices
(
adaptationSets
);
...
@@ -624,12 +659,6 @@ import java.util.List;
...
@@ -624,12 +659,6 @@ import java.util.List;
return
new
ChunkSampleStream
[
length
];
return
new
ChunkSampleStream
[
length
];
}
}
private
static
void
releaseIfEmbeddedSampleStream
(
SampleStream
sampleStream
)
{
if
(
sampleStream
instanceof
EmbeddedSampleStream
)
{
((
EmbeddedSampleStream
)
sampleStream
).
release
();
}
}
private
static
final
class
TrackGroupInfo
{
private
static
final
class
TrackGroupInfo
{
@Retention
(
RetentionPolicy
.
SOURCE
)
@Retention
(
RetentionPolicy
.
SOURCE
)
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/DashMediaSource.java
View file @
8cbcfd66
...
@@ -37,6 +37,7 @@ import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispat
...
@@ -37,6 +37,7 @@ import com.google.android.exoplayer2.source.MediaSourceEventListener.EventDispat
import
com.google.android.exoplayer2.source.SequenceableLoader
;
import
com.google.android.exoplayer2.source.SequenceableLoader
;
import
com.google.android.exoplayer2.source.ads.AdsMediaSource
;
import
com.google.android.exoplayer2.source.ads.AdsMediaSource
;
import
com.google.android.exoplayer2.source.dash.PlayerEmsgHandler.PlayerEmsgCallback
;
import
com.google.android.exoplayer2.source.dash.PlayerEmsgHandler.PlayerEmsgCallback
;
import
com.google.android.exoplayer2.source.dash.manifest.AdaptationSet
;
import
com.google.android.exoplayer2.source.dash.manifest.DashManifest
;
import
com.google.android.exoplayer2.source.dash.manifest.DashManifest
;
import
com.google.android.exoplayer2.source.dash.manifest.DashManifestParser
;
import
com.google.android.exoplayer2.source.dash.manifest.DashManifestParser
;
import
com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement
;
import
com.google.android.exoplayer2.source.dash.manifest.UtcTimingElement
;
...
@@ -974,8 +975,25 @@ public final class DashMediaSource extends BaseMediaSource {
...
@@ -974,8 +975,25 @@ public final class DashMediaSource extends BaseMediaSource {
long
availableEndTimeUs
=
Long
.
MAX_VALUE
;
long
availableEndTimeUs
=
Long
.
MAX_VALUE
;
boolean
isIndexExplicit
=
false
;
boolean
isIndexExplicit
=
false
;
boolean
seenEmptyIndex
=
false
;
boolean
seenEmptyIndex
=
false
;
boolean
haveAudioVideoAdaptationSets
=
false
;
for
(
int
i
=
0
;
i
<
adaptationSetCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
adaptationSetCount
;
i
++)
{
DashSegmentIndex
index
=
period
.
adaptationSets
.
get
(
i
).
representations
.
get
(
0
).
getIndex
();
int
type
=
period
.
adaptationSets
.
get
(
i
).
type
;
if
(
type
==
C
.
TRACK_TYPE_AUDIO
||
type
==
C
.
TRACK_TYPE_VIDEO
)
{
haveAudioVideoAdaptationSets
=
true
;
break
;
}
}
for
(
int
i
=
0
;
i
<
adaptationSetCount
;
i
++)
{
AdaptationSet
adaptationSet
=
period
.
adaptationSets
.
get
(
i
);
// Exclude text adaptation sets from duration calculations, if we have at least one audio
// or video adaptation set. See: https://github.com/google/ExoPlayer/issues/4029
if
(
haveAudioVideoAdaptationSets
&&
adaptationSet
.
type
==
C
.
TRACK_TYPE_TEXT
)
{
continue
;
}
DashSegmentIndex
index
=
adaptationSet
.
representations
.
get
(
0
).
getIndex
();
if
(
index
==
null
)
{
if
(
index
==
null
)
{
return
new
PeriodSeekInfo
(
true
,
0
,
durationUs
);
return
new
PeriodSeekInfo
(
true
,
0
,
durationUs
);
}
}
...
...
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/EventSampleStream.java
View file @
8cbcfd66
...
@@ -36,37 +36,53 @@ import java.io.IOException;
...
@@ -36,37 +36,53 @@ import java.io.IOException;
private
final
EventMessageEncoder
eventMessageEncoder
;
private
final
EventMessageEncoder
eventMessageEncoder
;
private
long
[]
eventTimesUs
;
private
long
[]
eventTimesUs
;
private
boolean
eventStream
Updat
able
;
private
boolean
eventStream
Append
able
;
private
EventStream
eventStream
;
private
EventStream
eventStream
;
private
boolean
isFormatSentDownstream
;
private
boolean
isFormatSentDownstream
;
private
int
currentIndex
;
private
int
currentIndex
;
private
long
pendingSeekPositionUs
;
private
long
pendingSeekPositionUs
;
EventSampleStream
(
EventStream
eventStream
,
Format
upstreamFormat
,
boolean
eventStreamUpdatable
)
{
public
EventSampleStream
(
EventStream
eventStream
,
Format
upstreamFormat
,
boolean
eventStreamAppendable
)
{
this
.
upstreamFormat
=
upstreamFormat
;
this
.
upstreamFormat
=
upstreamFormat
;
this
.
eventStream
=
eventStream
;
this
.
eventStream
=
eventStream
;
eventMessageEncoder
=
new
EventMessageEncoder
();
eventMessageEncoder
=
new
EventMessageEncoder
();
pendingSeekPositionUs
=
C
.
TIME_UNSET
;
pendingSeekPositionUs
=
C
.
TIME_UNSET
;
eventTimesUs
=
eventStream
.
presentationTimesUs
;
eventTimesUs
=
eventStream
.
presentationTimesUs
;
updateEventStream
(
eventStream
,
eventStream
Updat
able
);
updateEventStream
(
eventStream
,
eventStream
Append
able
);
}
}
void
updateEventStream
(
EventStream
eventStream
,
boolean
eventStreamUpdatable
)
{
public
String
eventStreamId
()
{
return
eventStream
.
id
();
}
public
void
updateEventStream
(
EventStream
eventStream
,
boolean
eventStreamAppendable
)
{
long
lastReadPositionUs
=
currentIndex
==
0
?
C
.
TIME_UNSET
:
eventTimesUs
[
currentIndex
-
1
];
long
lastReadPositionUs
=
currentIndex
==
0
?
C
.
TIME_UNSET
:
eventTimesUs
[
currentIndex
-
1
];
this
.
eventStream
Updatable
=
eventStreamUpdat
able
;
this
.
eventStream
Appendable
=
eventStreamAppend
able
;
this
.
eventStream
=
eventStream
;
this
.
eventStream
=
eventStream
;
this
.
eventTimesUs
=
eventStream
.
presentationTimesUs
;
this
.
eventTimesUs
=
eventStream
.
presentationTimesUs
;
if
(
pendingSeekPositionUs
!=
C
.
TIME_UNSET
)
{
if
(
pendingSeekPositionUs
!=
C
.
TIME_UNSET
)
{
seekToUs
(
pendingSeekPositionUs
);
seekToUs
(
pendingSeekPositionUs
);
}
else
if
(
lastReadPositionUs
!=
C
.
TIME_UNSET
)
{
}
else
if
(
lastReadPositionUs
!=
C
.
TIME_UNSET
)
{
currentIndex
=
Util
.
binarySearchCeil
(
eventTimesUs
,
lastReadPositionUs
,
false
,
false
);
currentIndex
=
Util
.
binarySearchCeil
(
eventTimesUs
,
lastReadPositionUs
,
/* inclusive= */
false
,
/* stayInBounds= */
false
);
}
}
}
}
String
eventStreamId
()
{
/**
return
eventStream
.
id
();
* Seeks to the specified position in microseconds.
*
* @param positionUs The seek position in microseconds.
*/
public
void
seekToUs
(
long
positionUs
)
{
currentIndex
=
Util
.
binarySearchCeil
(
eventTimesUs
,
positionUs
,
/* inclusive= */
true
,
/* stayInBounds= */
false
);
boolean
isPendingSeek
=
eventStreamAppendable
&&
currentIndex
==
eventTimesUs
.
length
;
pendingSeekPositionUs
=
isPendingSeek
?
positionUs
:
C
.
TIME_UNSET
;
}
}
@Override
@Override
...
@@ -88,7 +104,7 @@ import java.io.IOException;
...
@@ -88,7 +104,7 @@ import java.io.IOException;
return
C
.
RESULT_FORMAT_READ
;
return
C
.
RESULT_FORMAT_READ
;
}
}
if
(
currentIndex
==
eventTimesUs
.
length
)
{
if
(
currentIndex
==
eventTimesUs
.
length
)
{
if
(!
eventStream
Updat
able
)
{
if
(!
eventStream
Append
able
)
{
buffer
.
setFlags
(
C
.
BUFFER_FLAG_END_OF_STREAM
);
buffer
.
setFlags
(
C
.
BUFFER_FLAG_END_OF_STREAM
);
return
C
.
RESULT_BUFFER_READ
;
return
C
.
RESULT_BUFFER_READ
;
}
else
{
}
else
{
...
@@ -118,15 +134,4 @@ import java.io.IOException;
...
@@ -118,15 +134,4 @@ import java.io.IOException;
return
skipped
;
return
skipped
;
}
}
/**
* Seeks to the specified position in microseconds.
*
* @param positionUs The seek position in microseconds.
*/
public
void
seekToUs
(
long
positionUs
)
{
currentIndex
=
Util
.
binarySearchCeil
(
eventTimesUs
,
positionUs
,
true
,
false
);
boolean
isPendingSeek
=
eventStreamUpdatable
&&
currentIndex
==
eventTimesUs
.
length
;
pendingSeekPositionUs
=
isPendingSeek
?
positionUs
:
C
.
TIME_UNSET
;
}
}
}
library/dash/src/main/java/com/google/android/exoplayer2/source/dash/manifest/DashManifestParser.java
View file @
8cbcfd66
...
@@ -355,6 +355,7 @@ public class DashManifestParser extends DefaultHandler
...
@@ -355,6 +355,7 @@ public class DashManifestParser extends DefaultHandler
protected
Pair
<
String
,
SchemeData
>
parseContentProtection
(
XmlPullParser
xpp
)
protected
Pair
<
String
,
SchemeData
>
parseContentProtection
(
XmlPullParser
xpp
)
throws
XmlPullParserException
,
IOException
{
throws
XmlPullParserException
,
IOException
{
String
schemeType
=
null
;
String
schemeType
=
null
;
String
licenseServerUrl
=
null
;
byte
[]
data
=
null
;
byte
[]
data
=
null
;
UUID
uuid
=
null
;
UUID
uuid
=
null
;
boolean
requiresSecureDecoder
=
false
;
boolean
requiresSecureDecoder
=
false
;
...
@@ -364,7 +365,7 @@ public class DashManifestParser extends DefaultHandler
...
@@ -364,7 +365,7 @@ public class DashManifestParser extends DefaultHandler
switch
(
Util
.
toLowerInvariant
(
schemeIdUri
))
{
switch
(
Util
.
toLowerInvariant
(
schemeIdUri
))
{
case
"urn:mpeg:dash:mp4protection:2011"
:
case
"urn:mpeg:dash:mp4protection:2011"
:
schemeType
=
xpp
.
getAttributeValue
(
null
,
"value"
);
schemeType
=
xpp
.
getAttributeValue
(
null
,
"value"
);
String
defaultKid
=
xpp
.
getAttributeValue
(
null
,
"cenc:
default_KID"
);
String
defaultKid
=
XmlPullParserUtil
.
getAttributeValueIgnorePrefix
(
xpp
,
"
default_KID"
);
if
(!
TextUtils
.
isEmpty
(
defaultKid
)
if
(!
TextUtils
.
isEmpty
(
defaultKid
)
&&
!
"00000000-0000-0000-0000-000000000000"
.
equals
(
defaultKid
))
{
&&
!
"00000000-0000-0000-0000-000000000000"
.
equals
(
defaultKid
))
{
String
[]
defaultKidStrings
=
defaultKid
.
split
(
"\\s+"
);
String
[]
defaultKidStrings
=
defaultKid
.
split
(
"\\s+"
);
...
@@ -389,11 +390,14 @@ public class DashManifestParser extends DefaultHandler
...
@@ -389,11 +390,14 @@ public class DashManifestParser extends DefaultHandler
do
{
do
{
xpp
.
next
();
xpp
.
next
();
if
(
XmlPullParserUtil
.
isStartTag
(
xpp
,
"widevine:license"
))
{
if
(
XmlPullParserUtil
.
isStartTag
(
xpp
,
"ms:laurl"
))
{
licenseServerUrl
=
xpp
.
getAttributeValue
(
null
,
"licenseUrl"
);
}
else
if
(
XmlPullParserUtil
.
isStartTag
(
xpp
,
"widevine:license"
))
{
String
robustnessLevel
=
xpp
.
getAttributeValue
(
null
,
"robustness_level"
);
String
robustnessLevel
=
xpp
.
getAttributeValue
(
null
,
"robustness_level"
);
requiresSecureDecoder
=
robustnessLevel
!=
null
&&
robustnessLevel
.
startsWith
(
"HW"
);
requiresSecureDecoder
=
robustnessLevel
!=
null
&&
robustnessLevel
.
startsWith
(
"HW"
);
}
else
if
(
data
==
null
)
{
}
else
if
(
data
==
null
)
{
if
(
XmlPullParserUtil
.
isStartTag
(
xpp
,
"cenc:pssh"
)
&&
xpp
.
next
()
==
XmlPullParser
.
TEXT
)
{
if
(
XmlPullParserUtil
.
isStartTagIgnorePrefix
(
xpp
,
"pssh"
)
&&
xpp
.
next
()
==
XmlPullParser
.
TEXT
)
{
// The cenc:pssh element is defined in 23001-7:2015.
// The cenc:pssh element is defined in 23001-7:2015.
data
=
Base64
.
decode
(
xpp
.
getText
(),
Base64
.
DEFAULT
);
data
=
Base64
.
decode
(
xpp
.
getText
(),
Base64
.
DEFAULT
);
uuid
=
PsshAtomUtil
.
parseUuid
(
data
);
uuid
=
PsshAtomUtil
.
parseUuid
(
data
);
...
@@ -409,8 +413,11 @@ public class DashManifestParser extends DefaultHandler
...
@@ -409,8 +413,11 @@ public class DashManifestParser extends DefaultHandler
}
}
}
}
}
while
(!
XmlPullParserUtil
.
isEndTag
(
xpp
,
"ContentProtection"
));
}
while
(!
XmlPullParserUtil
.
isEndTag
(
xpp
,
"ContentProtection"
));
SchemeData
schemeData
=
uuid
!=
null
SchemeData
schemeData
=
?
new
SchemeData
(
uuid
,
MimeTypes
.
VIDEO_MP4
,
data
,
requiresSecureDecoder
)
:
null
;
uuid
!=
null
?
new
SchemeData
(
uuid
,
licenseServerUrl
,
MimeTypes
.
VIDEO_MP4
,
data
,
requiresSecureDecoder
)
:
null
;
return
Pair
.
create
(
schemeType
,
schemeData
);
return
Pair
.
create
(
schemeType
,
schemeData
);
}
}
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaSource.java
View file @
8cbcfd66
...
@@ -401,7 +401,7 @@ public final class HlsMediaSource extends BaseMediaSource
...
@@ -401,7 +401,7 @@ public final class HlsMediaSource extends BaseMediaSource
@Override
@Override
public
void
releaseSourceInternal
()
{
public
void
releaseSourceInternal
()
{
if
(
playlistTracker
!=
null
)
{
if
(
playlistTracker
!=
null
)
{
playlistTracker
.
release
();
playlistTracker
.
stop
();
}
}
}
}
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsSampleStreamWrapper.java
View file @
8cbcfd66
...
@@ -136,6 +136,7 @@ import java.util.Arrays;
...
@@ -136,6 +136,7 @@ import java.util.Arrays;
// Accessed only by the loading thread.
// Accessed only by the loading thread.
private
boolean
tracksEnded
;
private
boolean
tracksEnded
;
private
long
sampleOffsetUs
;
private
long
sampleOffsetUs
;
private
int
chunkUid
;
/**
/**
* @param trackType The type of the track. One of the {@link C} {@code TRACK_TYPE_*} constants.
* @param trackType The type of the track. One of the {@link C} {@code TRACK_TYPE_*} constants.
...
@@ -650,6 +651,7 @@ import java.util.Arrays;
...
@@ -650,6 +651,7 @@ import java.util.Arrays;
audioSampleQueueMappingDone
=
false
;
audioSampleQueueMappingDone
=
false
;
videoSampleQueueMappingDone
=
false
;
videoSampleQueueMappingDone
=
false
;
}
}
this
.
chunkUid
=
chunkUid
;
for
(
SampleQueue
sampleQueue
:
sampleQueues
)
{
for
(
SampleQueue
sampleQueue
:
sampleQueues
)
{
sampleQueue
.
sourceId
(
chunkUid
);
sampleQueue
.
sourceId
(
chunkUid
);
}
}
...
@@ -704,6 +706,7 @@ import java.util.Arrays;
...
@@ -704,6 +706,7 @@ import java.util.Arrays;
}
}
}
}
SampleQueue
trackOutput
=
new
SampleQueue
(
allocator
);
SampleQueue
trackOutput
=
new
SampleQueue
(
allocator
);
trackOutput
.
sourceId
(
chunkUid
);
trackOutput
.
setSampleOffsetUs
(
sampleOffsetUs
);
trackOutput
.
setSampleOffsetUs
(
sampleOffsetUs
);
trackOutput
.
setUpstreamFormatChangeListener
(
this
);
trackOutput
.
setUpstreamFormatChangeListener
(
this
);
sampleQueueTrackIds
=
Arrays
.
copyOf
(
sampleQueueTrackIds
,
trackCount
+
1
);
sampleQueueTrackIds
=
Arrays
.
copyOf
(
sampleQueueTrackIds
,
trackCount
+
1
);
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/DefaultHlsPlaylistTracker.java
View file @
8cbcfd66
...
@@ -105,7 +105,7 @@ public final class DefaultHlsPlaylistTracker
...
@@ -105,7 +105,7 @@ public final class DefaultHlsPlaylistTracker
}
}
@Override
@Override
public
void
release
()
{
public
void
stop
()
{
primaryHlsUrl
=
null
;
primaryHlsUrl
=
null
;
primaryUrlSnapshot
=
null
;
primaryUrlSnapshot
=
null
;
masterPlaylist
=
null
;
masterPlaylist
=
null
;
...
...
library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistTracker.java
View file @
8cbcfd66
...
@@ -100,8 +100,8 @@ public interface HlsPlaylistTracker {
...
@@ -100,8 +100,8 @@ public interface HlsPlaylistTracker {
/**
/**
* Starts the playlist tracker.
* Starts the playlist tracker.
*
*
* <p>Must be called from the playback thread. A tracker may be restarted after a {@link
* <p>Must be called from the playback thread. A tracker may be restarted after a {@link
#stop()}
*
#release()}
call.
* call.
*
*
* @param initialPlaylistUri Uri of the HLS stream. Can point to a media playlist or a master
* @param initialPlaylistUri Uri of the HLS stream. Can point to a media playlist or a master
* playlist.
* playlist.
...
@@ -111,8 +111,12 @@ public interface HlsPlaylistTracker {
...
@@ -111,8 +111,12 @@ public interface HlsPlaylistTracker {
void
start
(
void
start
(
Uri
initialPlaylistUri
,
EventDispatcher
eventDispatcher
,
PrimaryPlaylistListener
listener
);
Uri
initialPlaylistUri
,
EventDispatcher
eventDispatcher
,
PrimaryPlaylistListener
listener
);
/** Releases all acquired resources. Must be called once per {@link #start} call. */
/**
void
release
();
* Stops the playlist tracker and releases any acquired resources.
*
* <p>Must be called once per {@link #start} call.
*/
void
stop
();
/**
/**
* Registers a listener to receive events from the playlist tracker.
* Registers a listener to receive events from the playlist tracker.
...
...
library/hls/src/test/java/com/google/android/exoplayer2/source/hls/Aes128DataSourceTest.java
View file @
8cbcfd66
...
@@ -22,6 +22,8 @@ import com.google.android.exoplayer2.C;
...
@@ -22,6 +22,8 @@ import com.google.android.exoplayer2.C;
import
com.google.android.exoplayer2.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.DataSource
;
import
com.google.android.exoplayer2.upstream.DataSpec
;
import
com.google.android.exoplayer2.upstream.DataSpec
;
import
java.io.IOException
;
import
java.io.IOException
;
import
org.junit.Ignore
;
import
org.junit.Test
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.junit.runner.RunWith
;
import
org.robolectric.RobolectricTestRunner
;
import
org.robolectric.RobolectricTestRunner
;
...
@@ -30,6 +32,7 @@ import org.robolectric.RobolectricTestRunner;
...
@@ -30,6 +32,7 @@ import org.robolectric.RobolectricTestRunner;
@RunWith
(
RobolectricTestRunner
.
class
)
@RunWith
(
RobolectricTestRunner
.
class
)
public
class
Aes128DataSourceTest
{
public
class
Aes128DataSourceTest
{
@Ignore
@Test
@Test
public
void
test_OpenCallsUpstreamOpen_CloseCallsUpstreamClose
()
throws
IOException
{
public
void
test_OpenCallsUpstreamOpen_CloseCallsUpstreamClose
()
throws
IOException
{
UpstreamDataSource
upstream
=
new
UpstreamDataSource
();
UpstreamDataSource
upstream
=
new
UpstreamDataSource
();
...
@@ -44,6 +47,7 @@ public class Aes128DataSourceTest {
...
@@ -44,6 +47,7 @@ public class Aes128DataSourceTest {
assertThat
(
upstream
.
opened
).
isFalse
();
assertThat
(
upstream
.
opened
).
isFalse
();
}
}
@Ignore
@Test
@Test
public
void
test_OpenCallsUpstreamThrowingOpen_CloseCallsUpstreamClose
()
throws
IOException
{
public
void
test_OpenCallsUpstreamThrowingOpen_CloseCallsUpstreamClose
()
throws
IOException
{
UpstreamDataSource
upstream
=
UpstreamDataSource
upstream
=
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/DefaultTimeBar.java
View file @
8cbcfd66
...
@@ -174,6 +174,12 @@ public class DefaultTimeBar extends View implements TimeBar {
...
@@ -174,6 +174,12 @@ public class DefaultTimeBar extends View implements TimeBar {
private
static
final
long
STOP_SCRUBBING_TIMEOUT_MS
=
1000
;
private
static
final
long
STOP_SCRUBBING_TIMEOUT_MS
=
1000
;
private
static
final
int
DEFAULT_INCREMENT_COUNT
=
20
;
private
static
final
int
DEFAULT_INCREMENT_COUNT
=
20
;
/**
* The name of the Android SDK view that most closely resembles this custom view. Used as the
* class name for accessibility.
*/
private
static
final
String
ACCESSIBILITY_CLASS_NAME
=
"android.widget.SeekBar"
;
private
final
Rect
seekBounds
;
private
final
Rect
seekBounds
;
private
final
Rect
progressBar
;
private
final
Rect
progressBar
;
private
final
Rect
bufferedBar
;
private
final
Rect
bufferedBar
;
...
@@ -184,7 +190,7 @@ public class DefaultTimeBar extends View implements TimeBar {
...
@@ -184,7 +190,7 @@ public class DefaultTimeBar extends View implements TimeBar {
private
final
Paint
adMarkerPaint
;
private
final
Paint
adMarkerPaint
;
private
final
Paint
playedAdMarkerPaint
;
private
final
Paint
playedAdMarkerPaint
;
private
final
Paint
scrubberPaint
;
private
final
Paint
scrubberPaint
;
private
final
Drawable
scrubberDrawable
;
private
final
@Nullable
Drawable
scrubberDrawable
;
private
final
int
barHeight
;
private
final
int
barHeight
;
private
final
int
touchTargetHeight
;
private
final
int
touchTargetHeight
;
private
final
int
adMarkerWidth
;
private
final
int
adMarkerWidth
;
...
@@ -197,12 +203,12 @@ public class DefaultTimeBar extends View implements TimeBar {
...
@@ -197,12 +203,12 @@ public class DefaultTimeBar extends View implements TimeBar {
private
final
Formatter
formatter
;
private
final
Formatter
formatter
;
private
final
Runnable
stopScrubbingRunnable
;
private
final
Runnable
stopScrubbingRunnable
;
private
final
CopyOnWriteArraySet
<
OnScrubListener
>
listeners
;
private
final
CopyOnWriteArraySet
<
OnScrubListener
>
listeners
;
private
final
int
[]
locationOnScreen
;
private
final
Point
touchPosition
;
private
int
keyCountIncrement
;
private
int
keyCountIncrement
;
private
long
keyTimeIncrement
;
private
long
keyTimeIncrement
;
private
int
lastCoarseScrubXPosition
;
private
int
lastCoarseScrubXPosition
;
private
int
[]
locationOnScreen
;
private
Point
touchPosition
;
private
boolean
scrubbing
;
private
boolean
scrubbing
;
private
long
scrubPosition
;
private
long
scrubPosition
;
...
@@ -210,12 +216,10 @@ public class DefaultTimeBar extends View implements TimeBar {
...
@@ -210,12 +216,10 @@ public class DefaultTimeBar extends View implements TimeBar {
private
long
position
;
private
long
position
;
private
long
bufferedPosition
;
private
long
bufferedPosition
;
private
int
adGroupCount
;
private
int
adGroupCount
;
private
long
[]
adGroupTimesMs
;
private
@Nullable
long
[]
adGroupTimesMs
;
private
boolean
[]
playedAdGroups
;
private
@Nullable
boolean
[]
playedAdGroups
;
/**
/** Creates a new time bar. */
* Creates a new time bar.
*/
public
DefaultTimeBar
(
Context
context
,
AttributeSet
attrs
)
{
public
DefaultTimeBar
(
Context
context
,
AttributeSet
attrs
)
{
super
(
context
,
attrs
);
super
(
context
,
attrs
);
seekBounds
=
new
Rect
();
seekBounds
=
new
Rect
();
...
@@ -230,6 +234,8 @@ public class DefaultTimeBar extends View implements TimeBar {
...
@@ -230,6 +234,8 @@ public class DefaultTimeBar extends View implements TimeBar {
scrubberPaint
=
new
Paint
();
scrubberPaint
=
new
Paint
();
scrubberPaint
.
setAntiAlias
(
true
);
scrubberPaint
.
setAntiAlias
(
true
);
listeners
=
new
CopyOnWriteArraySet
<>();
listeners
=
new
CopyOnWriteArraySet
<>();
locationOnScreen
=
new
int
[
2
];
touchPosition
=
new
Point
();
// Calculate the dimensions and paints for drawn elements.
// Calculate the dimensions and paints for drawn elements.
Resources
res
=
context
.
getResources
();
Resources
res
=
context
.
getResources
();
...
@@ -593,14 +599,14 @@ public class DefaultTimeBar extends View implements TimeBar {
...
@@ -593,14 +599,14 @@ public class DefaultTimeBar extends View implements TimeBar {
if
(
event
.
getEventType
()
==
AccessibilityEvent
.
TYPE_VIEW_SELECTED
)
{
if
(
event
.
getEventType
()
==
AccessibilityEvent
.
TYPE_VIEW_SELECTED
)
{
event
.
getText
().
add
(
getProgressText
());
event
.
getText
().
add
(
getProgressText
());
}
}
event
.
setClassName
(
DefaultTimeBar
.
class
.
getName
()
);
event
.
setClassName
(
ACCESSIBILITY_CLASS_NAME
);
}
}
@TargetApi
(
21
)
@TargetApi
(
21
)
@Override
@Override
public
void
onInitializeAccessibilityNodeInfo
(
AccessibilityNodeInfo
info
)
{
public
void
onInitializeAccessibilityNodeInfo
(
AccessibilityNodeInfo
info
)
{
super
.
onInitializeAccessibilityNodeInfo
(
info
);
super
.
onInitializeAccessibilityNodeInfo
(
info
);
info
.
setClassName
(
DefaultTimeBar
.
class
.
getCanonicalName
()
);
info
.
setClassName
(
ACCESSIBILITY_CLASS_NAME
);
info
.
setContentDescription
(
getProgressText
());
info
.
setContentDescription
(
getProgressText
());
if
(
duration
<=
0
)
{
if
(
duration
<=
0
)
{
return
;
return
;
...
@@ -616,7 +622,7 @@ public class DefaultTimeBar extends View implements TimeBar {
...
@@ -616,7 +622,7 @@ public class DefaultTimeBar extends View implements TimeBar {
@TargetApi
(
16
)
@TargetApi
(
16
)
@Override
@Override
public
boolean
performAccessibilityAction
(
int
action
,
Bundle
args
)
{
public
boolean
performAccessibilityAction
(
int
action
,
@Nullable
Bundle
args
)
{
if
(
super
.
performAccessibilityAction
(
action
,
args
))
{
if
(
super
.
performAccessibilityAction
(
action
,
args
))
{
return
true
;
return
true
;
}
}
...
@@ -693,10 +699,6 @@ public class DefaultTimeBar extends View implements TimeBar {
...
@@ -693,10 +699,6 @@ public class DefaultTimeBar extends View implements TimeBar {
}
}
private
Point
resolveRelativeTouchPosition
(
MotionEvent
motionEvent
)
{
private
Point
resolveRelativeTouchPosition
(
MotionEvent
motionEvent
)
{
if
(
locationOnScreen
==
null
)
{
locationOnScreen
=
new
int
[
2
];
touchPosition
=
new
Point
();
}
getLocationOnScreen
(
locationOnScreen
);
getLocationOnScreen
(
locationOnScreen
);
touchPosition
.
set
(
touchPosition
.
set
(
((
int
)
motionEvent
.
getRawX
())
-
locationOnScreen
[
0
],
((
int
)
motionEvent
.
getRawX
())
-
locationOnScreen
[
0
],
...
@@ -736,6 +738,11 @@ public class DefaultTimeBar extends View implements TimeBar {
...
@@ -736,6 +738,11 @@ public class DefaultTimeBar extends View implements TimeBar {
if
(
scrubberBar
.
width
()
>
0
)
{
if
(
scrubberBar
.
width
()
>
0
)
{
canvas
.
drawRect
(
scrubberBar
.
left
,
barTop
,
scrubberBar
.
right
,
barBottom
,
playedPaint
);
canvas
.
drawRect
(
scrubberBar
.
left
,
barTop
,
scrubberBar
.
right
,
barBottom
,
playedPaint
);
}
}
if
(
adGroupCount
==
0
)
{
return
;
}
long
[]
adGroupTimesMs
=
Assertions
.
checkNotNull
(
this
.
adGroupTimesMs
);
boolean
[]
playedAdGroups
=
Assertions
.
checkNotNull
(
this
.
playedAdGroups
);
int
adMarkerOffset
=
adMarkerWidth
/
2
;
int
adMarkerOffset
=
adMarkerWidth
/
2
;
for
(
int
i
=
0
;
i
<
adGroupCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
adGroupCount
;
i
++)
{
long
adGroupTimeMs
=
Util
.
constrainValue
(
adGroupTimesMs
[
i
],
0
,
duration
);
long
adGroupTimeMs
=
Util
.
constrainValue
(
adGroupTimesMs
[
i
],
0
,
duration
);
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/DownloadNotificationUtil.java
View file @
8cbcfd66
...
@@ -55,10 +55,18 @@ public final class DownloadNotificationUtil {
...
@@ -55,10 +55,18 @@ public final class DownloadNotificationUtil {
int
downloadTaskCount
=
0
;
int
downloadTaskCount
=
0
;
boolean
allDownloadPercentagesUnknown
=
true
;
boolean
allDownloadPercentagesUnknown
=
true
;
boolean
haveDownloadedBytes
=
false
;
boolean
haveDownloadedBytes
=
false
;
boolean
haveDownloadTasks
=
false
;
boolean
haveRemoveTasks
=
false
;
for
(
TaskState
taskState
:
taskStates
)
{
for
(
TaskState
taskState
:
taskStates
)
{
if
(
taskState
.
action
.
isRemoveAction
||
taskState
.
state
!=
TaskState
.
STATE_STARTED
)
{
if
(
taskState
.
state
!=
TaskState
.
STATE_STARTED
&&
taskState
.
state
!=
TaskState
.
STATE_COMPLETED
)
{
continue
;
continue
;
}
}
if
(
taskState
.
action
.
isRemoveAction
)
{
haveRemoveTasks
=
true
;
continue
;
}
haveDownloadTasks
=
true
;
if
(
taskState
.
downloadPercentage
!=
C
.
PERCENTAGE_UNSET
)
{
if
(
taskState
.
downloadPercentage
!=
C
.
PERCENTAGE_UNSET
)
{
allDownloadPercentagesUnknown
=
false
;
allDownloadPercentagesUnknown
=
false
;
totalPercentage
+=
taskState
.
downloadPercentage
;
totalPercentage
+=
taskState
.
downloadPercentage
;
...
@@ -67,18 +75,20 @@ public final class DownloadNotificationUtil {
...
@@ -67,18 +75,20 @@ public final class DownloadNotificationUtil {
downloadTaskCount
++;
downloadTaskCount
++;
}
}
boolean
haveDownloadTasks
=
downloadTaskCount
>
0
;
int
titleStringId
=
int
titleStringId
=
haveDownloadTasks
haveDownloadTasks
?
R
.
string
.
exo_download_downloading
?
R
.
string
.
exo_download_downloading
:
(
taskStates
.
length
>
0
?
R
.
string
.
exo_download_removing
:
NULL_STRING_ID
);
:
(
haveRemoveTasks
?
R
.
string
.
exo_download_removing
:
NULL_STRING_ID
);
NotificationCompat
.
Builder
notificationBuilder
=
NotificationCompat
.
Builder
notificationBuilder
=
newNotificationBuilder
(
newNotificationBuilder
(
context
,
smallIcon
,
channelId
,
contentIntent
,
message
,
titleStringId
);
context
,
smallIcon
,
channelId
,
contentIntent
,
message
,
titleStringId
);
int
progress
=
haveDownloadTasks
?
(
int
)
(
totalPercentage
/
downloadTaskCount
)
:
0
;
int
progress
=
0
;
boolean
indeterminate
=
boolean
indeterminate
=
true
;
!
haveDownloadTasks
||
(
allDownloadPercentagesUnknown
&&
haveDownloadedBytes
);
if
(
haveDownloadTasks
)
{
progress
=
(
int
)
(
totalPercentage
/
downloadTaskCount
);
indeterminate
=
allDownloadPercentagesUnknown
&&
haveDownloadedBytes
;
}
notificationBuilder
.
setProgress
(
/* max= */
100
,
progress
,
indeterminate
);
notificationBuilder
.
setProgress
(
/* max= */
100
,
progress
,
indeterminate
);
notificationBuilder
.
setOngoing
(
true
);
notificationBuilder
.
setOngoing
(
true
);
notificationBuilder
.
setShowWhen
(
false
);
notificationBuilder
.
setShowWhen
(
false
);
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerControlView.java
View file @
8cbcfd66
...
@@ -1088,7 +1088,7 @@ public class PlayerControlView extends FrameLayout {
...
@@ -1088,7 +1088,7 @@ public class PlayerControlView extends FrameLayout {
@Override
@Override
public
void
onTimelineChanged
(
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
@Player
.
TimelineChangeReason
int
reason
)
{
Timeline
timeline
,
@Nullable
Object
manifest
,
@Player
.
TimelineChangeReason
int
reason
)
{
updateNavigation
();
updateNavigation
();
updateTimeBarMode
();
updateTimeBarMode
();
updateProgress
();
updateProgress
();
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerNotificationManager.java
View file @
8cbcfd66
...
@@ -949,7 +949,7 @@ public class PlayerNotificationManager {
...
@@ -949,7 +949,7 @@ public class PlayerNotificationManager {
}
}
@Override
@Override
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
int
reason
)
{
public
void
onTimelineChanged
(
Timeline
timeline
,
@Nullable
Object
manifest
,
int
reason
)
{
if
(
player
==
null
||
player
.
getPlaybackState
()
==
Player
.
STATE_IDLE
)
{
if
(
player
==
null
||
player
.
getPlaybackState
()
==
Player
.
STATE_IDLE
)
{
return
;
return
;
}
}
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/PlayerView.java
View file @
8cbcfd66
...
@@ -696,6 +696,11 @@ public class PlayerView extends FrameLayout {
...
@@ -696,6 +696,11 @@ public class PlayerView extends FrameLayout {
return
useController
&&
controller
.
dispatchMediaKeyEvent
(
event
);
return
useController
&&
controller
.
dispatchMediaKeyEvent
(
event
);
}
}
/** Returns whether the controller is currently visible. */
public
boolean
isControllerVisible
()
{
return
controller
!=
null
&&
controller
.
isVisible
();
}
/**
/**
* Shows the playback controls. Does nothing if playback controls are disabled.
* Shows the playback controls. Does nothing if playback controls are disabled.
*
*
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java
View file @
8cbcfd66
...
@@ -28,6 +28,7 @@ import android.graphics.Rect;
...
@@ -28,6 +28,7 @@ import android.graphics.Rect;
import
android.graphics.RectF
;
import
android.graphics.RectF
;
import
android.text.Layout.Alignment
;
import
android.text.Layout.Alignment
;
import
android.text.SpannableStringBuilder
;
import
android.text.SpannableStringBuilder
;
import
android.text.Spanned
;
import
android.text.StaticLayout
;
import
android.text.StaticLayout
;
import
android.text.TextPaint
;
import
android.text.TextPaint
;
import
android.text.TextUtils
;
import
android.text.TextUtils
;
...
@@ -89,7 +90,8 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -89,7 +90,8 @@ import com.google.android.exoplayer2.util.Util;
private
int
edgeColor
;
private
int
edgeColor
;
@CaptionStyleCompat
.
EdgeType
@CaptionStyleCompat
.
EdgeType
private
int
edgeType
;
private
int
edgeType
;
private
float
textSizePx
;
private
float
defaultTextSizePx
;
private
float
cueTextSizePx
;
private
float
bottomPaddingFraction
;
private
float
bottomPaddingFraction
;
private
int
parentLeft
;
private
int
parentLeft
;
private
int
parentTop
;
private
int
parentTop
;
...
@@ -130,8 +132,8 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -130,8 +132,8 @@ import com.google.android.exoplayer2.util.Util;
/**
/**
* Draws the provided {@link Cue} into a canvas with the specified styling.
* Draws the provided {@link Cue} into a canvas with the specified styling.
*
<p>
*
* A call to this method is able to use cached results of calculations made during the previous
*
<p>
A call to this method is able to use cached results of calculations made during the previous
* call, and so an instance of this class is able to optimize repeated calls to this method in
* call, and so an instance of this class is able to optimize repeated calls to this method in
* which the same parameters are passed.
* which the same parameters are passed.
*
*
...
@@ -140,7 +142,8 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -140,7 +142,8 @@ import com.google.android.exoplayer2.util.Util;
* @param applyEmbeddedFontSizes If {@code applyEmbeddedStyles} is true, defines whether font
* @param applyEmbeddedFontSizes If {@code applyEmbeddedStyles} is true, defines whether font
* sizes embedded within the cue should be applied. Otherwise, it is ignored.
* sizes embedded within the cue should be applied. Otherwise, it is ignored.
* @param style The style to use when drawing the cue text.
* @param style The style to use when drawing the cue text.
* @param textSizePx The text size to use when drawing the cue text, in pixels.
* @param defaultTextSizePx The default text size to use when drawing the text, in pixels.
* @param cueTextSizePx The embedded text size of this cue, in pixels.
* @param bottomPaddingFraction The bottom padding fraction to apply when {@link Cue#line} is
* @param bottomPaddingFraction The bottom padding fraction to apply when {@link Cue#line} is
* {@link Cue#DIMEN_UNSET}, as a fraction of the viewport height
* {@link Cue#DIMEN_UNSET}, as a fraction of the viewport height
* @param canvas The canvas into which to draw.
* @param canvas The canvas into which to draw.
...
@@ -149,9 +152,19 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -149,9 +152,19 @@ import com.google.android.exoplayer2.util.Util;
* @param cueBoxRight The right position of the enclosing cue box.
* @param cueBoxRight The right position of the enclosing cue box.
* @param cueBoxBottom The bottom position of the enclosing cue box.
* @param cueBoxBottom The bottom position of the enclosing cue box.
*/
*/
public
void
draw
(
Cue
cue
,
boolean
applyEmbeddedStyles
,
boolean
applyEmbeddedFontSizes
,
public
void
draw
(
CaptionStyleCompat
style
,
float
textSizePx
,
float
bottomPaddingFraction
,
Canvas
canvas
,
Cue
cue
,
int
cueBoxLeft
,
int
cueBoxTop
,
int
cueBoxRight
,
int
cueBoxBottom
)
{
boolean
applyEmbeddedStyles
,
boolean
applyEmbeddedFontSizes
,
CaptionStyleCompat
style
,
float
defaultTextSizePx
,
float
cueTextSizePx
,
float
bottomPaddingFraction
,
Canvas
canvas
,
int
cueBoxLeft
,
int
cueBoxTop
,
int
cueBoxRight
,
int
cueBoxBottom
)
{
boolean
isTextCue
=
cue
.
bitmap
==
null
;
boolean
isTextCue
=
cue
.
bitmap
==
null
;
int
windowColor
=
Color
.
BLACK
;
int
windowColor
=
Color
.
BLACK
;
if
(
isTextCue
)
{
if
(
isTextCue
)
{
...
@@ -180,7 +193,8 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -180,7 +193,8 @@ import com.google.android.exoplayer2.util.Util;
&&
this
.
edgeType
==
style
.
edgeType
&&
this
.
edgeType
==
style
.
edgeType
&&
this
.
edgeColor
==
style
.
edgeColor
&&
this
.
edgeColor
==
style
.
edgeColor
&&
Util
.
areEqual
(
this
.
textPaint
.
getTypeface
(),
style
.
typeface
)
&&
Util
.
areEqual
(
this
.
textPaint
.
getTypeface
(),
style
.
typeface
)
&&
this
.
textSizePx
==
textSizePx
&&
this
.
defaultTextSizePx
==
defaultTextSizePx
&&
this
.
cueTextSizePx
==
cueTextSizePx
&&
this
.
bottomPaddingFraction
==
bottomPaddingFraction
&&
this
.
bottomPaddingFraction
==
bottomPaddingFraction
&&
this
.
parentLeft
==
cueBoxLeft
&&
this
.
parentLeft
==
cueBoxLeft
&&
this
.
parentTop
==
cueBoxTop
&&
this
.
parentTop
==
cueBoxTop
...
@@ -209,7 +223,8 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -209,7 +223,8 @@ import com.google.android.exoplayer2.util.Util;
this
.
edgeType
=
style
.
edgeType
;
this
.
edgeType
=
style
.
edgeType
;
this
.
edgeColor
=
style
.
edgeColor
;
this
.
edgeColor
=
style
.
edgeColor
;
this
.
textPaint
.
setTypeface
(
style
.
typeface
);
this
.
textPaint
.
setTypeface
(
style
.
typeface
);
this
.
textSizePx
=
textSizePx
;
this
.
defaultTextSizePx
=
defaultTextSizePx
;
this
.
cueTextSizePx
=
cueTextSizePx
;
this
.
bottomPaddingFraction
=
bottomPaddingFraction
;
this
.
bottomPaddingFraction
=
bottomPaddingFraction
;
this
.
parentLeft
=
cueBoxLeft
;
this
.
parentLeft
=
cueBoxLeft
;
this
.
parentTop
=
cueBoxTop
;
this
.
parentTop
=
cueBoxTop
;
...
@@ -228,8 +243,8 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -228,8 +243,8 @@ import com.google.android.exoplayer2.util.Util;
int
parentWidth
=
parentRight
-
parentLeft
;
int
parentWidth
=
parentRight
-
parentLeft
;
int
parentHeight
=
parentBottom
-
parentTop
;
int
parentHeight
=
parentBottom
-
parentTop
;
textPaint
.
setTextSize
(
t
extSizePx
);
textPaint
.
setTextSize
(
defaultT
extSizePx
);
int
textPaddingX
=
(
int
)
(
t
extSizePx
*
INNER_PADDING_RATIO
+
0.5f
);
int
textPaddingX
=
(
int
)
(
defaultT
extSizePx
*
INNER_PADDING_RATIO
+
0.5f
);
int
availableWidth
=
parentWidth
-
textPaddingX
*
2
;
int
availableWidth
=
parentWidth
-
textPaddingX
*
2
;
if
(
cueSize
!=
Cue
.
DIMEN_UNSET
)
{
if
(
cueSize
!=
Cue
.
DIMEN_UNSET
)
{
...
@@ -240,14 +255,12 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -240,14 +255,12 @@ import com.google.android.exoplayer2.util.Util;
return
;
return
;
}
}
CharSequence
cueText
=
this
.
cueText
;
// Remove embedded styling or font size if requested.
// Remove embedded styling or font size if requested.
CharSequence
cueText
;
if
(!
applyEmbeddedStyles
)
{
if
(
applyEmbeddedFontSizes
&&
applyEmbeddedStyles
)
{
cueText
=
cueText
.
toString
();
// Equivalent to erasing all spans.
cueText
=
this
.
cueText
;
}
else
if
(!
applyEmbeddedFontSizes
)
{
}
else
if
(!
applyEmbeddedStyles
)
{
SpannableStringBuilder
newCueText
=
new
SpannableStringBuilder
(
cueText
);
cueText
=
this
.
cueText
.
toString
();
// Equivalent to erasing all spans.
}
else
{
SpannableStringBuilder
newCueText
=
new
SpannableStringBuilder
(
this
.
cueText
);
int
cueLength
=
newCueText
.
length
();
int
cueLength
=
newCueText
.
length
();
AbsoluteSizeSpan
[]
absSpans
=
newCueText
.
getSpans
(
0
,
cueLength
,
AbsoluteSizeSpan
.
class
);
AbsoluteSizeSpan
[]
absSpans
=
newCueText
.
getSpans
(
0
,
cueLength
,
AbsoluteSizeSpan
.
class
);
RelativeSizeSpan
[]
relSpans
=
newCueText
.
getSpans
(
0
,
cueLength
,
RelativeSizeSpan
.
class
);
RelativeSizeSpan
[]
relSpans
=
newCueText
.
getSpans
(
0
,
cueLength
,
RelativeSizeSpan
.
class
);
...
@@ -258,6 +271,19 @@ import com.google.android.exoplayer2.util.Util;
...
@@ -258,6 +271,19 @@ import com.google.android.exoplayer2.util.Util;
newCueText
.
removeSpan
(
relSpan
);
newCueText
.
removeSpan
(
relSpan
);
}
}
cueText
=
newCueText
;
cueText
=
newCueText
;
}
else
{
// Apply embedded styles & font size.
if
(
cueTextSizePx
>
0
)
{
// Use a SpannableStringBuilder encompassing the whole cue text to apply the default
// cueTextSizePx.
SpannableStringBuilder
newCueText
=
new
SpannableStringBuilder
(
cueText
);
newCueText
.
setSpan
(
new
AbsoluteSizeSpan
((
int
)
cueTextSizePx
),
/* start= */
0
,
/* end= */
newCueText
.
length
(),
Spanned
.
SPAN_PRIORITY
);
cueText
=
newCueText
;
}
}
}
Alignment
textAlignment
=
cueTextAlignment
==
null
?
Alignment
.
ALIGN_CENTER
:
cueTextAlignment
;
Alignment
textAlignment
=
cueTextAlignment
==
null
?
Alignment
.
ALIGN_CENTER
:
cueTextAlignment
;
...
...
library/ui/src/main/java/com/google/android/exoplayer2/ui/SubtitleView.java
View file @
8cbcfd66
...
@@ -269,15 +269,15 @@ public final class SubtitleView extends View implements TextOutput {
...
@@ -269,15 +269,15 @@ public final class SubtitleView extends View implements TextOutput {
for
(
int
i
=
0
;
i
<
cueCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
cueCount
;
i
++)
{
Cue
cue
=
cues
.
get
(
i
);
Cue
cue
=
cues
.
get
(
i
);
float
textSizePx
=
float
cueTextSizePx
=
resolveCueTextSize
(
cue
,
rawViewHeight
,
viewHeightMinusPadding
);
resolveTextSizeForCue
(
cue
,
rawViewHeight
,
viewHeightMinusPadding
,
defaultViewTextSizePx
);
SubtitlePainter
painter
=
painters
.
get
(
i
);
SubtitlePainter
painter
=
painters
.
get
(
i
);
painter
.
draw
(
painter
.
draw
(
cue
,
cue
,
applyEmbeddedStyles
,
applyEmbeddedStyles
,
applyEmbeddedFontSizes
,
applyEmbeddedFontSizes
,
style
,
style
,
textSizePx
,
defaultViewTextSizePx
,
cueTextSizePx
,
bottomPaddingFraction
,
bottomPaddingFraction
,
canvas
,
canvas
,
left
,
left
,
...
@@ -287,14 +287,13 @@ public final class SubtitleView extends View implements TextOutput {
...
@@ -287,14 +287,13 @@ public final class SubtitleView extends View implements TextOutput {
}
}
}
}
private
float
resolveTextSizeForCue
(
private
float
resolveCueTextSize
(
Cue
cue
,
int
rawViewHeight
,
int
viewHeightMinusPadding
)
{
Cue
cue
,
int
rawViewHeight
,
int
viewHeightMinusPadding
,
float
defaultViewTextSizePx
)
{
if
(
cue
.
textSizeType
==
Cue
.
TYPE_UNSET
||
cue
.
textSize
==
Cue
.
DIMEN_UNSET
)
{
if
(
cue
.
textSizeType
==
Cue
.
TYPE_UNSET
||
cue
.
textSize
==
Cue
.
DIMEN_UNSET
)
{
return
defaultViewTextSizePx
;
return
0
;
}
}
float
defaultCueTextSizePx
=
float
defaultCueTextSizePx
=
resolveTextSize
(
cue
.
textSizeType
,
cue
.
textSize
,
rawViewHeight
,
viewHeightMinusPadding
);
resolveTextSize
(
cue
.
textSizeType
,
cue
.
textSize
,
rawViewHeight
,
viewHeightMinusPadding
);
return
defaultCueTextSizePx
>
0
?
defaultCueTextSizePx
:
defaultViewTextSizePx
;
return
Math
.
max
(
defaultCueTextSizePx
,
0
)
;
}
}
private
float
resolveTextSize
(
private
float
resolveTextSize
(
...
...
library/ui/src/main/res/drawable-ldpi/exo_controls_fullscreen_enter.png
View file @
8cbcfd66
107 Bytes
|
W:
|
H:
139 Bytes
|
W:
|
H:
2-up
Swipe
Onion skin
library/ui/src/main/res/drawable-ldpi/exo_controls_fullscreen_exit.png
View file @
8cbcfd66
105 Bytes
|
W:
|
H:
146 Bytes
|
W:
|
H:
2-up
Swipe
Onion skin
testutils/src/main/java/com/google/android/exoplayer2/testutil/Action.java
View file @
8cbcfd66
...
@@ -575,7 +575,9 @@ public abstract class Action {
...
@@ -575,7 +575,9 @@ public abstract class Action {
new
Player
.
DefaultEventListener
()
{
new
Player
.
DefaultEventListener
()
{
@Override
@Override
public
void
onTimelineChanged
(
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
@Player
.
TimelineChangeReason
int
reason
)
{
Timeline
timeline
,
@Nullable
Object
manifest
,
@Player
.
TimelineChangeReason
int
reason
)
{
if
(
expectedTimeline
==
null
||
timeline
.
equals
(
expectedTimeline
))
{
if
(
expectedTimeline
==
null
||
timeline
.
equals
(
expectedTimeline
))
{
player
.
removeListener
(
this
);
player
.
removeListener
(
this
);
nextAction
.
schedule
(
player
,
trackSelector
,
surface
,
handler
);
nextAction
.
schedule
(
player
,
trackSelector
,
surface
,
handler
);
...
...
testutils/src/main/java/com/google/android/exoplayer2/testutil/ExoPlayerTestRunner.java
View file @
8cbcfd66
...
@@ -601,8 +601,8 @@ public final class ExoPlayerTestRunner extends Player.DefaultEventListener
...
@@ -601,8 +601,8 @@ public final class ExoPlayerTestRunner extends Player.DefaultEventListener
// Player.EventListener
// Player.EventListener
@Override
@Override
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
public
void
onTimelineChanged
(
@Player
.
TimelineChangeReason
int
reason
)
{
Timeline
timeline
,
@Nullable
Object
manifest
,
@Player
.
TimelineChangeReason
int
reason
)
{
timelines
.
add
(
timeline
);
timelines
.
add
(
timeline
);
manifests
.
add
(
manifest
);
manifests
.
add
(
manifest
);
timelineChangeReasons
.
add
(
reason
);
timelineChangeReasons
.
add
(
reason
);
...
...
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