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
ab66dfa7
authored
Sep 10, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Parsing embedded TTML styling.
parent
95fcb3b4
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
1320 additions
and
69 deletions
library/src/androidTest/assets/ttml/chain_multiple_styles.xml
library/src/androidTest/assets/ttml/inherit_and_override_style.xml
library/src/androidTest/assets/ttml/inherit_global_and_parent.xml
library/src/androidTest/assets/ttml/inherit_multiple_styles.xml
library/src/androidTest/assets/ttml/inherit_style.xml
library/src/androidTest/assets/ttml/inline_style_attributes.xml
library/src/androidTest/assets/ttml/instance_creation.xml
library/src/androidTest/assets/ttml/no_underline_linethrough.xml
library/src/androidTest/assets/ttml/non_inheritable_properties.xml
library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlParserTest.java
library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlStyleTest.java
library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java
library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlNode.java
library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java
library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlStyle.java
library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlSubtitle.java
library/src/main/java/com/google/android/exoplayer/util/ParserUtil.java
library/src/androidTest/assets/ttml/chain_multiple_styles.xml
0 → 100644
View file @
ab66dfa7
<tt
xmlns:ttm=
"http://www.w3.org/2006/10/ttaf1#metadata"
xmlns:ttp=
"http://www.w3.org/2006/10/ttaf1#parameter"
xmlns:tts=
"http://www.w3.org/2006/10/ttaf1#style"
xmlns=
"http://www.w3.org/ns/ttml"
xmlns=
"http://www.w3.org/2006/10/ttaf1"
>
<head>
<styling>
<style
id=
"s0"
tts:backgroundColor=
"blue"
tts:color=
"black"
tts:fontWeight=
"bold"
/>
<style
id=
"s1"
tts:backgroundColor=
"black"
tts:color=
"red"
tts:fontFamily=
"sansSerif"
tts:fontStyle=
"italic"
tts:textDecoration=
"lineThrough"
/>
<!-- multiple ids defined -->
<style
style=
"s0 s1"
id=
"s2"
tts:fontFamily=
"serif"
tts:backgroundColor=
"red"
/>
</styling>
</head>
<body>
<div>
<p
style=
"s2"
begin=
"10s"
end=
"18s"
>
text 1
</p>
</div>
</body>
</tt>
library/src/androidTest/assets/ttml/inherit_and_override_style.xml
0 → 100644
View file @
ab66dfa7
<tt
xmlns=
"http://www.w3.org/ns/ttml"
xmlns=
"http://www.w3.org/2006/10/ttaf1"
xmlns:ttp=
"http://www.w3.org/2006/10/ttaf1#parameter"
xmlns:tts=
"http://www.w3.org/2006/10/ttaf1#style"
xmlns:ttm=
"http://www.w3.org/2006/10/ttaf1#metadata"
>
<head>
<styling>
<style
id=
"s0"
tts:fontWeight=
"bold"
tts:fontStyle=
"italic"
tts:fontFamily=
"serif"
tts:textDecoration=
"underline"
tts:backgroundColor=
"blue"
tts:color=
"yellow"
/>
</styling>
</head>
<body>
<div>
<p
style=
"s0"
begin=
"10s"
end=
"18s"
>
text 1
</p>
</div>
<div>
<p
style=
"s0"
begin=
"20s"
end=
"28s"
tts:fontWeight=
"normal"
tts:fontFamily=
"sansSerif"
tts:backgroundColor=
"red"
tts:color=
"yellow"
>
text 2
</p>
</div>
</body>
</tt>
library/src/androidTest/assets/ttml/inherit_global_and_parent.xml
0 → 100644
View file @
ab66dfa7
<tt
xmlns=
"http://www.w3.org/ns/ttml"
xmlns=
"http://www.w3.org/2006/10/ttaf1"
xmlns:ttp=
"http://www.w3.org/2006/10/ttaf1#parameter"
xmlns:tts=
"http://www.w3.org/2006/10/ttaf1#style"
xmlns:ttm=
"http://www.w3.org/2006/10/ttaf1#metadata"
>
<head>
<styling>
<style
id=
"s0"
tts:fontWeight=
"bold"
tts:fontStyle=
"italic"
tts:fontFamily=
"serif"
tts:textDecoration=
"underline"
tts:backgroundColor=
"blue"
tts:color=
"yellow"
/>
</styling>
</head>
<body
tts:textAlign=
"center"
>
<div
tts:fontWeight=
"normal"
tts:fontStyle=
"normal"
tts:fontFamily=
"sansSerif"
tts:textDecoration=
"lineThrough"
tts:backgroundColor=
"red"
tts:color=
"lime"
>
<p
begin=
"10s"
end=
"18s"
>
text 1
</p>
</div>
<div
tts:fontWeight=
"normal"
tts:fontStyle=
"normal"
tts:fontFamily=
"sansSerif"
tts:textDecoration=
"lineThrough"
tts:backgroundColor=
"red"
tts:color=
"lime"
>
<p
style=
"s0"
begin=
"20s"
end=
"28s"
>
text 2
</p>
</div>
</body>
</tt>
library/src/androidTest/assets/ttml/inherit_multiple_styles.xml
0 → 100644
View file @
ab66dfa7
<tt
xmlns:ttm=
"http://www.w3.org/2006/10/ttaf1#metadata"
xmlns:ttp=
"http://www.w3.org/2006/10/ttaf1#parameter"
xmlns:tts=
"http://www.w3.org/2006/10/ttaf1#style"
xmlns=
"http://www.w3.org/ns/ttml"
xmlns=
"http://www.w3.org/2006/10/ttaf1"
>
<head>
<styling>
<style
id=
"s0"
tts:backgroundColor=
"blue"
tts:color=
"black"
tts:fontWeight=
"bold"
/>
<style
id=
"s1"
tts:backgroundColor=
"black"
tts:color=
"red"
tts:fontFamily=
"sansSerif"
tts:fontStyle=
"italic"
tts:textDecoration=
"lineThrough"
/>
<style
id=
"s2"
tts:backgroundColor=
"red"
/>
<style
id=
"s3"
tts:backgroundColor=
"green"
tts:textDecoration=
"lineThrough"
/>
</styling>
</head>
<body>
<div>
<p
style=
"s0 s1"
begin=
"10s"
end=
"18s"
tts:color=
"yellow"
>
text 1
</p>
</div>
<div>
<p
style=
"s0 s1"
begin=
"20s"
end=
"28s"
>
text 1
</p>
</div>
<div
tts:color=
"yellow"
tts:textDecoration=
"underline"
tts:fontStyle=
"italic"
tts:fontFamily=
"sansSerifInline"
>
<p
style=
"s2 s3"
begin=
"30s"
end=
"38s"
>
text 1
</p>
</div>
<div>
<!-- empty style attribute -->
<p
style=
" "
begin=
"40s"
end=
"48s"
>
text 1
</p>
</div>
<div>
<p
style=
"not_existing"
begin=
"50s"
end=
"58s"
>
text 1
</p>
</div>
<div>
<p
style=
"not_existing s2"
begin=
"60s"
end=
"68s"
>
text 1
</p>
</div>
</body>
</tt>
library/src/androidTest/assets/ttml/inherit_style.xml
0 → 100644
View file @
ab66dfa7
<tt
xmlns=
"http://www.w3.org/ns/ttml"
xmlns=
"http://www.w3.org/2006/10/ttaf1"
xmlns:ttp=
"http://www.w3.org/2006/10/ttaf1#parameter"
xmlns:tts=
"http://www.w3.org/2006/10/ttaf1#style"
xmlns:ttm=
"http://www.w3.org/2006/10/ttaf1#metadata"
>
<head>
<styling>
<style
id=
"s0"
tts:fontWeight=
"bold"
tts:fontStyle=
"italic"
tts:fontFamily=
"serif"
tts:textDecoration=
"underline"
tts:backgroundColor=
"blue"
tts:color=
"yellow"
/>
</styling>
</head>
<body>
<div>
<p
style=
"s0"
begin=
"10s"
end=
"18s"
>
text 1
</p>
</div>
</body>
</tt>
library/src/androidTest/assets/ttml/inline_style_attributes.xml
0 → 100644
View file @
ab66dfa7
<tt
xmlns=
"http://www.w3.org/ns/ttml"
xmlns=
"http://www.w3.org/2006/10/ttaf1"
xmlns:ttp=
"http://www.w3.org/2006/10/ttaf1#parameter"
xmlns:tts=
"http://www.w3.org/2006/10/ttaf1#style"
xmlns:ttm=
"http://www.w3.org/2006/10/ttaf1#metadata"
>
<body>
<div>
<p
begin=
"10s"
end=
"18s"
tts:fontWeight=
"bold"
tts:fontStyle=
"italic"
tts:fontFamily=
"serif"
tts:textDecoration=
"underline"
tts:backgroundColor=
"blue"
tts:color=
"yellow"
>
text 1
</p>
</div>
<div
tts:fontWeight=
"normal"
tts:fontStyle=
"italic"
tts:fontFamily=
"sansSerif"
tts:textDecoration=
"lineThrough"
tts:backgroundColor=
"cyan"
tts:color=
"lime"
>
<p
begin=
"20s"
end=
"28s"
>
text 2
</p>
</div>
</body>
</tt>
library/src/androidTest/assets/ttml/instance_creation.xml
0 → 100644
View file @
ab66dfa7
<tt
xmlns:ttm=
"http://www.w3.org/2006/10/ttaf1#metadata"
xmlns:ttp=
"http://www.w3.org/2006/10/ttaf1#parameter"
xmlns:tts=
"http://www.w3.org/2006/10/ttaf1#style"
xmlns=
"http://www.w3.org/ns/ttml"
xmlns=
"http://www.w3.org/2006/10/ttaf1"
>
<head>
<styling>
<style
id=
"s0"
tts:color=
"blue"
/>
<style
id=
"s1"
tts:backgroundColor=
"red"
/>
</styling>
</head>
<body
style=
"s0"
>
<div>
<p
style=
"s0"
begin=
"10s"
end=
"18s"
>
text 1
</p>
</div>
<div>
<p
style=
"s0"
begin=
"20s"
end=
"28s"
>
text
<span
style=
"s0"
>
2
</span></p>
</div>
<div>
<p
style=
"s1"
begin=
"20s"
end=
"28s"
>
text
<span
style=
"s1"
>
3
</span></p>
</div>
</body>
</tt>
library/src/androidTest/assets/ttml/no_underline_linethrough.xml
0 → 100644
View file @
ab66dfa7
<tt
xmlns:ttm=
"http://www.w3.org/2006/10/ttaf1#metadata"
xmlns:ttp=
"http://www.w3.org/2006/10/ttaf1#parameter"
xmlns:tts=
"http://www.w3.org/2006/10/ttaf1#style"
xmlns=
"http://www.w3.org/ns/ttml"
xmlns=
"http://www.w3.org/2006/10/ttaf1"
>
<head>
<styling>
<style
id=
"s0"
tts:textDecoration=
"underline"
/>
<style
id=
"s1"
tts:textDecoration=
"lineThrough"
/>
</styling>
</head>
<body>
<div>
<p
style=
"s0"
begin=
"10s"
end=
"18s"
tts:textDecoration=
"noUnderline"
>
text 1
</p>
</div>
<div>
<p
style=
"s1"
begin=
"20s"
end=
"28s"
tts:textDecoration=
"noLineThrough"
>
text 1
</p>
</div>
</body>
</tt>
library/src/androidTest/assets/ttml/non_inheritable_properties.xml
0 → 100644
View file @
ab66dfa7
<tt
xmlns=
"http://www.w3.org/ns/ttml"
xmlns=
"http://www.w3.org/2006/10/ttaf1"
xmlns:ttp=
"http://www.w3.org/2006/10/ttaf1#parameter"
xmlns:tts=
"http://www.w3.org/2006/10/ttaf1#style"
xmlns:ttm=
"http://www.w3.org/2006/10/ttaf1#metadata"
>
<body>
<div>
<p
begin=
"10s"
end=
"18s"
tts:fontWeight=
"bold"
tts:fontStyle=
"italic"
tts:fontFamily=
"serif"
tts:textDecoration=
"underline"
tts:backgroundColor=
"blue"
tts:color=
"yellow"
>
<span>
text 1
</span>
</p>
</div>
</body>
</tt>
library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlParserTest.java
0 → 100644
View file @
ab66dfa7
/*
* Copyright (C) 2014 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
.
exoplayer
.
text
.
ttml
;
import
android.graphics.Color
;
import
android.test.InstrumentationTestCase
;
import
android.text.Layout
;
import
java.io.IOException
;
import
java.io.InputStream
;
/**
* Unit test for {@link TtmlParser}.
*/
public
final
class
TtmlParserTest
extends
InstrumentationTestCase
{
private
static
final
String
INLINE_ATTRIBUTES_TTML_FILE
=
"ttml/inline_style_attributes.xml"
;
private
static
final
String
INHERIT_STYLE_TTML_FILE
=
"ttml/inherit_style.xml"
;
private
static
final
String
INHERIT_STYLE_OVERRIDE_TTML_FILE
=
"ttml/inherit_and_override_style.xml"
;
private
static
final
String
INHERIT_GLOBAL_AND_PARENT_TTML_FILE
=
"ttml/inherit_global_and_parent.xml"
;
private
static
final
String
NON_INHERTABLE_PROPERTIES_TTML_FILE
=
"ttml/non_inheritable_properties.xml"
;
private
static
final
String
INHERIT_MULTIPLE_STYLES_TTML_FILE
=
"ttml/inherit_multiple_styles.xml"
;
private
static
final
String
CHAIN_MULTIPLE_STYLES_TTML_FILE
=
"ttml/chain_multiple_styles.xml"
;
private
static
final
String
NO_UNDERLINE_LINETHROUGH_TTML_FILE
=
"ttml/no_underline_linethrough.xml"
;
private
static
final
String
INSTANCE_CREATION_TTML_FILE
=
"ttml/instance_creation.xml"
;
public
void
testInlineAttributes
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
INLINE_ATTRIBUTES_TTML_FILE
);
assertEquals
(
4
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
firstDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
0
);
TtmlStyle
firstPStyle
=
queryChildrenForTag
(
firstDiv
,
TtmlNode
.
TAG_P
,
0
).
style
;
assertEquals
(
Color
.
parseColor
(
"yellow"
),
firstPStyle
.
getColor
());
assertEquals
(
Color
.
parseColor
(
"blue"
),
firstPStyle
.
getBackgroundColor
());
assertEquals
(
"serif"
,
firstPStyle
.
getFontFamily
());
assertEquals
(
TtmlStyle
.
STYLE_BOLD_ITALIC
,
firstPStyle
.
getStyle
());
assertTrue
(
firstPStyle
.
isUnderline
());
}
public
void
testInheritInlineAttributes
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
INLINE_ATTRIBUTES_TTML_FILE
);
assertEquals
(
4
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
// inherite inline attributes
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
secondDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
1
);
TtmlStyle
secondPStyle
=
queryChildrenForTag
(
secondDiv
,
TtmlNode
.
TAG_P
,
0
).
style
;
assertEquals
(
Color
.
parseColor
(
"lime"
),
secondPStyle
.
getColor
());
assertFalse
(
secondPStyle
.
hasBackgroundColorSpecified
());
assertEquals
(
0
,
secondPStyle
.
getBackgroundColor
());
assertEquals
(
"sansSerif"
,
secondPStyle
.
getFontFamily
());
assertEquals
(
TtmlStyle
.
STYLE_ITALIC
,
secondPStyle
.
getStyle
());
assertTrue
(
secondPStyle
.
isLinethrough
());
}
public
void
testInheritGlobalStyle
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
INHERIT_STYLE_TTML_FILE
);
assertEquals
(
2
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
firstDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
0
);
TtmlStyle
firstPStyle
=
queryChildrenForTag
(
firstDiv
,
TtmlNode
.
TAG_P
,
0
).
style
;
assertEquals
(
Color
.
parseColor
(
"yellow"
),
firstPStyle
.
getColor
());
assertEquals
(
Color
.
parseColor
(
"blue"
),
firstPStyle
.
getBackgroundColor
());
assertEquals
(
"serif"
,
firstPStyle
.
getFontFamily
());
assertEquals
(
TtmlStyle
.
STYLE_BOLD_ITALIC
,
firstPStyle
.
getStyle
());
assertTrue
(
firstPStyle
.
isUnderline
());
}
public
void
testInheritGlobalStyleOverriddenByInlineAttributes
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
INHERIT_STYLE_OVERRIDE_TTML_FILE
);
assertEquals
(
4
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
// first pNode inherits global style
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
firstDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
0
);
TtmlStyle
firstPStyle
=
queryChildrenForTag
(
firstDiv
,
TtmlNode
.
TAG_P
,
0
).
style
;
assertEquals
(
Color
.
parseColor
(
"yellow"
),
firstPStyle
.
getColor
());
assertEquals
(
Color
.
parseColor
(
"blue"
),
firstPStyle
.
getBackgroundColor
());
assertEquals
(
"serif"
,
firstPStyle
.
getFontFamily
());
assertEquals
(
TtmlStyle
.
STYLE_BOLD_ITALIC
,
firstPStyle
.
getStyle
());
assertTrue
(
firstPStyle
.
isUnderline
());
// second pNode inherits global style and overrides with attribute
TtmlNode
secondDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
1
);
TtmlStyle
secondPStyle
=
queryChildrenForTag
(
secondDiv
,
TtmlNode
.
TAG_P
,
0
).
style
;
assertEquals
(
Color
.
parseColor
(
"yellow"
),
secondPStyle
.
getColor
());
assertEquals
(
Color
.
parseColor
(
"red"
),
secondPStyle
.
getBackgroundColor
());
assertEquals
(
"sansSerif"
,
secondPStyle
.
getFontFamily
());
assertEquals
(
TtmlStyle
.
STYLE_ITALIC
,
secondPStyle
.
getStyle
());
assertTrue
(
secondPStyle
.
isUnderline
());
}
public
void
testInheritGlobalAndParent
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
INHERIT_GLOBAL_AND_PARENT_TTML_FILE
);
assertEquals
(
4
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
// first pNode inherits parent style
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
firstDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
0
);
TtmlStyle
firstPStyle
=
queryChildrenForTag
(
firstDiv
,
TtmlNode
.
TAG_P
,
0
).
style
;
assertFalse
(
firstPStyle
.
hasBackgroundColorSpecified
());
assertEquals
(
0
,
firstPStyle
.
getBackgroundColor
());
assertEquals
(
Color
.
parseColor
(
"lime"
),
firstPStyle
.
getColor
());
assertEquals
(
"sansSerif"
,
firstPStyle
.
getFontFamily
());
assertEquals
(
TtmlStyle
.
STYLE_NORMAL
,
firstPStyle
.
getStyle
());
assertTrue
(
firstPStyle
.
isLinethrough
());
// second pNode inherits parent style and overrides with global style
TtmlNode
secondDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
1
);
TtmlStyle
secondPStyle
=
queryChildrenForTag
(
secondDiv
,
TtmlNode
.
TAG_P
,
0
).
style
;
// attributes overridden by global style
assertEquals
(
Color
.
parseColor
(
"blue"
),
secondPStyle
.
getBackgroundColor
());
assertEquals
(
Color
.
parseColor
(
"yellow"
),
secondPStyle
.
getColor
());
assertEquals
(
TtmlStyle
.
STYLE_BOLD_ITALIC
,
secondPStyle
.
getStyle
());
assertEquals
(
"serif"
,
secondPStyle
.
getFontFamily
());
assertTrue
(
secondPStyle
.
isUnderline
());
assertEquals
(
Layout
.
Alignment
.
ALIGN_CENTER
,
secondPStyle
.
getTextAlign
());
}
public
void
testNonInheritablePropertiesAreNotInherited
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
NON_INHERTABLE_PROPERTIES_TTML_FILE
);
assertEquals
(
2
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
firstDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
0
);
TtmlNode
firstPStyle
=
queryChildrenForTag
(
firstDiv
,
TtmlNode
.
TAG_P
,
0
);
TtmlStyle
spanStyle
=
queryChildrenForTag
(
firstPStyle
,
TtmlNode
.
TAG_SPAN
,
0
).
style
;
assertFalse
(
"background color must not be inherited from a context node"
,
spanStyle
.
hasBackgroundColorSpecified
());
}
public
void
testInheritMultipleStyles
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
INHERIT_MULTIPLE_STYLES_TTML_FILE
);
assertEquals
(
12
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
firstDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
0
);
TtmlStyle
firstPStyle
=
queryChildrenForTag
(
firstDiv
,
TtmlNode
.
TAG_P
,
0
).
style
;
assertEquals
(
Color
.
parseColor
(
"blue"
),
firstPStyle
.
getBackgroundColor
());
assertEquals
(
Color
.
parseColor
(
"yellow"
),
firstPStyle
.
getColor
());
assertEquals
(
"sansSerif"
,
firstPStyle
.
getFontFamily
());
assertEquals
(
TtmlStyle
.
STYLE_BOLD_ITALIC
,
firstPStyle
.
getStyle
());
assertTrue
(
firstPStyle
.
isLinethrough
());
}
public
void
testInheritMultipleStylesWithoutLocalAttributes
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
INHERIT_MULTIPLE_STYLES_TTML_FILE
);
assertEquals
(
12
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
secondDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
1
);
TtmlStyle
firstPStyle
=
queryChildrenForTag
(
secondDiv
,
TtmlNode
.
TAG_P
,
0
).
style
;
assertEquals
(
Color
.
parseColor
(
"blue"
),
firstPStyle
.
getBackgroundColor
());
assertEquals
(
Color
.
parseColor
(
"black"
),
firstPStyle
.
getColor
());
assertEquals
(
"sansSerif"
,
firstPStyle
.
getFontFamily
());
assertEquals
(
TtmlStyle
.
STYLE_BOLD_ITALIC
,
firstPStyle
.
getStyle
());
assertTrue
(
firstPStyle
.
isLinethrough
());
}
public
void
testMergeMultipleStylesWithParentStyle
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
INHERIT_MULTIPLE_STYLES_TTML_FILE
);
assertEquals
(
12
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
thirdDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
2
);
TtmlStyle
firstPStyle
=
queryChildrenForTag
(
thirdDiv
,
TtmlNode
.
TAG_P
,
0
).
style
;
// inherit from first global style
assertEquals
(
Color
.
parseColor
(
"red"
),
firstPStyle
.
getBackgroundColor
());
// inherit from second global style
assertTrue
(
firstPStyle
.
isLinethrough
());
// inherited from parent node
assertEquals
(
"sansSerifInline"
,
firstPStyle
.
getFontFamily
());
assertEquals
(
TtmlStyle
.
STYLE_ITALIC
,
firstPStyle
.
getStyle
());
assertTrue
(
firstPStyle
.
isUnderline
());
assertEquals
(
Color
.
parseColor
(
"yellow"
),
firstPStyle
.
getColor
());
}
public
void
testEmptyStyleAttribute
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
INHERIT_MULTIPLE_STYLES_TTML_FILE
);
assertEquals
(
12
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
fourthDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
3
);
// no styles specified
assertNull
(
queryChildrenForTag
(
fourthDiv
,
TtmlNode
.
TAG_P
,
0
).
style
);
}
public
void
testNonexistingStyleId
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
INHERIT_MULTIPLE_STYLES_TTML_FILE
);
assertEquals
(
12
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
fifthDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
4
);
// no styles specified
assertNull
(
queryChildrenForTag
(
fifthDiv
,
TtmlNode
.
TAG_P
,
0
).
style
);
}
public
void
testNonExistingAndExistingStyleId
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
INHERIT_MULTIPLE_STYLES_TTML_FILE
);
assertEquals
(
12
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
sixthDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
5
);
// no styles specified
TtmlStyle
style
=
queryChildrenForTag
(
sixthDiv
,
TtmlNode
.
TAG_P
,
0
).
style
;
assertNotNull
(
style
);
assertEquals
(
Color
.
RED
,
style
.
getBackgroundColor
());
}
public
void
testMultipleChaining
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
CHAIN_MULTIPLE_STYLES_TTML_FILE
);
assertEquals
(
2
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
div
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
0
);
// no styles specified
TtmlStyle
style
=
queryChildrenForTag
(
div
,
TtmlNode
.
TAG_P
,
0
).
style
;
assertEquals
(
"serif"
,
style
.
getFontFamily
());
assertEquals
(
Color
.
RED
,
style
.
getBackgroundColor
());
assertEquals
(
Color
.
BLACK
,
style
.
getColor
());
assertEquals
(
TtmlStyle
.
STYLE_BOLD_ITALIC
,
style
.
getStyle
());
assertTrue
(
style
.
isLinethrough
());
}
public
void
testNoUnderline
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
NO_UNDERLINE_LINETHROUGH_TTML_FILE
);
assertEquals
(
4
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
div
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
0
);
TtmlStyle
style
=
queryChildrenForTag
(
div
,
TtmlNode
.
TAG_P
,
0
).
style
;
assertFalse
(
"noUnderline from inline attribute expected"
,
style
.
isUnderline
());
}
public
void
testNoLinethrough
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
NO_UNDERLINE_LINETHROUGH_TTML_FILE
);
assertEquals
(
4
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
div
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
1
);
TtmlStyle
style
=
queryChildrenForTag
(
div
,
TtmlNode
.
TAG_P
,
0
).
style
;
assertFalse
(
"noLineThrough from inline attribute expected in second pNode"
,
style
.
isLinethrough
());
}
public
void
testOnlySingleInstance
()
throws
IOException
{
TtmlSubtitle
subtitle
=
getSubtitle
(
INSTANCE_CREATION_TTML_FILE
);
assertEquals
(
4
,
subtitle
.
getEventTimeCount
());
TtmlNode
root
=
subtitle
.
getRoot
();
TtmlNode
body
=
queryChildrenForTag
(
root
,
TtmlNode
.
TAG_BODY
,
0
);
TtmlNode
firstDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
0
);
TtmlNode
secondDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
1
);
TtmlNode
thirdDiv
=
queryChildrenForTag
(
body
,
TtmlNode
.
TAG_DIV
,
2
);
TtmlNode
firstP
=
queryChildrenForTag
(
firstDiv
,
TtmlNode
.
TAG_P
,
0
);
TtmlNode
secondP
=
queryChildrenForTag
(
secondDiv
,
TtmlNode
.
TAG_P
,
0
);
TtmlNode
secondSpan
=
queryChildrenForTag
(
secondP
,
TtmlNode
.
TAG_SPAN
,
0
);
TtmlNode
thirdP
=
queryChildrenForTag
(
thirdDiv
,
TtmlNode
.
TAG_P
,
0
);
TtmlNode
thirdSpan
=
queryChildrenForTag
(
secondP
,
TtmlNode
.
TAG_SPAN
,
0
);
// inherit the same instance down the tree if possible
assertSame
(
body
.
style
,
firstP
.
style
);
assertSame
(
firstP
.
style
,
secondP
.
style
);
assertSame
(
secondP
.
style
,
secondSpan
.
style
);
// if a backgroundColor is involved it does not help
assertNotSame
(
thirdP
.
style
.
getInheritableStyle
(),
thirdSpan
.
style
);
}
private
TtmlNode
queryChildrenForTag
(
TtmlNode
node
,
String
tag
,
int
pos
)
{
int
count
=
0
;
for
(
int
i
=
0
;
i
<
node
.
getChildCount
();
i
++)
{
if
(
tag
.
equals
(
node
.
getChild
(
i
).
tag
))
{
if
(
pos
==
count
++)
{
return
node
.
getChild
(
i
);
}
}
}
return
null
;
}
private
TtmlSubtitle
getSubtitle
(
String
file
)
throws
IOException
{
TtmlParser
ttmlParser
=
new
TtmlParser
(
false
);
InputStream
inputStream
=
getInstrumentation
().
getContext
()
.
getResources
().
getAssets
().
open
(
file
);
return
(
TtmlSubtitle
)
ttmlParser
.
parse
(
inputStream
);
}
}
library/src/androidTest/java/com/google/android/exoplayer/text/ttml/TtmlStyleTest.java
0 → 100644
View file @
ab66dfa7
/*
* Copyright (C) 2014 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
.
exoplayer
.
text
.
ttml
;
import
android.graphics.Color
;
import
android.test.InstrumentationTestCase
;
/**
* Unit test for {@link TtmlStyle}.
*/
public
final
class
TtmlStyleTest
extends
InstrumentationTestCase
{
private
static
final
String
FONT_FAMILY
=
"serif"
;
private
static
final
String
ID
=
"id"
;
public
static
final
int
FOREGROUND_COLOR
=
Color
.
WHITE
;
public
static
final
int
BACKGROUND_COLOR
=
Color
.
BLACK
;
private
TtmlStyle
style
;
@Override
public
void
setUp
()
throws
Exception
{
super
.
setUp
();
style
=
new
TtmlStyle
();
}
public
void
testInheritStyle
()
{
style
.
inherit
(
createAncestorStyle
());
assertNull
(
"id must not be inherited"
,
style
.
getId
());
assertTrue
(
style
.
isUnderline
());
assertTrue
(
style
.
isLinethrough
());
assertEquals
(
TtmlStyle
.
STYLE_BOLD_ITALIC
,
style
.
getStyle
());
assertEquals
(
FONT_FAMILY
,
style
.
getFontFamily
());
assertEquals
(
Color
.
WHITE
,
style
.
getColor
());
assertFalse
(
"do not inherit backgroundColor"
,
style
.
hasBackgroundColorSpecified
());
}
public
void
testChainStyle
()
{
style
.
chain
(
createAncestorStyle
());
assertNull
(
"id must not be inherited"
,
style
.
getId
());
assertTrue
(
style
.
isUnderline
());
assertTrue
(
style
.
isLinethrough
());
assertEquals
(
TtmlStyle
.
STYLE_BOLD_ITALIC
,
style
.
getStyle
());
assertEquals
(
FONT_FAMILY
,
style
.
getFontFamily
());
assertEquals
(
FOREGROUND_COLOR
,
style
.
getColor
());
// do inherit backgroundColor when chaining
assertEquals
(
"do not inherit backgroundColor when chaining"
,
BACKGROUND_COLOR
,
style
.
getBackgroundColor
());
}
public
void
testGetInheritableStyle
()
{
// same instance as long as everything can be inherited
assertSame
(
style
,
style
.
getInheritableStyle
());
style
.
inherit
(
createAncestorStyle
());
assertSame
(
style
,
style
.
getInheritableStyle
());
// after setting a property which is not inheritable
// we expect the inheritable style to be another instance
style
.
setBackgroundColor
(
0
);
TtmlStyle
inheritableStyle
=
style
.
getInheritableStyle
();
assertNotSame
(
style
,
inheritableStyle
);
// and subsequent call give always the same instance
assertSame
(
inheritableStyle
,
style
.
getInheritableStyle
());
boolean
exceptionThrown
=
false
;
try
{
// setting properties after calling getInheritableStyle gives an exception
style
.
setItalic
(
true
);
}
catch
(
IllegalStateException
e
)
{
exceptionThrown
=
true
;
}
assertTrue
(
exceptionThrown
);
}
private
TtmlStyle
createAncestorStyle
()
{
TtmlStyle
ancestor
=
new
TtmlStyle
();
ancestor
.
setId
(
ID
);
ancestor
.
setItalic
(
true
);
ancestor
.
setBold
(
true
);
ancestor
.
setBackgroundColor
(
BACKGROUND_COLOR
);
ancestor
.
setColor
(
FOREGROUND_COLOR
);
ancestor
.
setLinethrough
(
true
);
ancestor
.
setUnderline
(
true
);
ancestor
.
setFontFamily
(
FONT_FAMILY
);
return
ancestor
;
}
public
void
testStyle
()
{
assertEquals
(
TtmlStyle
.
UNSPECIFIED
,
style
.
getStyle
());
style
.
setItalic
(
true
);
assertEquals
(
TtmlStyle
.
STYLE_ITALIC
,
style
.
getStyle
());
style
.
setBold
(
true
);
assertEquals
(
TtmlStyle
.
STYLE_BOLD_ITALIC
,
style
.
getStyle
());
style
.
setItalic
(
false
);
assertEquals
(
TtmlStyle
.
STYLE_BOLD
,
style
.
getStyle
());
style
.
setBold
(
false
);
assertEquals
(
TtmlStyle
.
STYLE_NORMAL
,
style
.
getStyle
());
}
public
void
testLinethrough
()
{
assertFalse
(
style
.
isLinethrough
());
style
.
setLinethrough
(
true
);
assertTrue
(
style
.
isLinethrough
());
style
.
setLinethrough
(
false
);
assertFalse
(
style
.
isLinethrough
());
}
public
void
testUnderline
()
{
assertFalse
(
style
.
isUnderline
());
style
.
setUnderline
(
true
);
assertTrue
(
style
.
isUnderline
());
style
.
setUnderline
(
false
);
assertFalse
(
style
.
isUnderline
());
}
public
void
testFontFamily
()
{
assertNull
(
style
.
getFontFamily
());
style
.
setFontFamily
(
FONT_FAMILY
);
assertEquals
(
FONT_FAMILY
,
style
.
getFontFamily
());
style
.
setFontFamily
(
null
);
assertNull
(
style
.
getFontFamily
());
}
public
void
testColor
()
{
assertFalse
(
style
.
hasColorSpecified
());
style
.
setColor
(
Color
.
BLACK
);
assertEquals
(
Color
.
BLACK
,
style
.
getColor
());
assertTrue
(
style
.
hasColorSpecified
());
}
public
void
testBackgroundColor
()
{
assertFalse
(
style
.
hasBackgroundColorSpecified
());
style
.
setBackgroundColor
(
Color
.
BLACK
);
assertEquals
(
Color
.
BLACK
,
style
.
getBackgroundColor
());
assertTrue
(
style
.
hasBackgroundColorSpecified
());
}
public
void
testId
()
{
assertNull
(
style
.
getId
());
style
.
setId
(
ID
);
assertEquals
(
ID
,
style
.
getId
());
style
.
setId
(
null
);
assertNull
(
style
.
getId
());
}
}
library/src/main/java/com/google/android/exoplayer/dash/mpd/MediaPresentationDescriptionParser.java
View file @
ab66dfa7
...
...
@@ -25,6 +25,7 @@ import com.google.android.exoplayer.extractor.mp4.PsshAtomUtil;
import
com.google.android.exoplayer.upstream.UriLoadable
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.ParserUtil
;
import
com.google.android.exoplayer.util.UriUtil
;
import
com.google.android.exoplayer.util.Util
;
...
...
@@ -120,16 +121,16 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
boolean
seenFirstBaseUrl
=
false
;
do
{
xpp
.
next
();
if
(
isStartTag
(
xpp
,
"BaseURL"
))
{
if
(
ParserUtil
.
isStartTag
(
xpp
,
"BaseURL"
))
{
if
(!
seenFirstBaseUrl
)
{
baseUrl
=
parseBaseUrl
(
xpp
,
baseUrl
);
seenFirstBaseUrl
=
true
;
}
}
else
if
(
isStartTag
(
xpp
,
"UTCTiming"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"UTCTiming"
))
{
utcTiming
=
parseUtcTiming
(
xpp
);
}
else
if
(
isStartTag
(
xpp
,
"Location"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"Location"
))
{
location
=
xpp
.
nextText
();
}
else
if
(
isStartTag
(
xpp
,
"Period"
)
&&
!
seenEarlyAccessPeriod
)
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"Period"
)
&&
!
seenEarlyAccessPeriod
)
{
Pair
<
Period
,
Long
>
periodWithDurationMs
=
parsePeriod
(
xpp
,
baseUrl
,
nextPeriodStartMs
);
Period
period
=
periodWithDurationMs
.
first
;
if
(
period
.
startMs
==
-
1
)
{
...
...
@@ -146,7 +147,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
periods
.
add
(
period
);
}
}
}
while
(!
isEndTag
(
xpp
,
"MPD"
));
}
while
(!
ParserUtil
.
isEndTag
(
xpp
,
"MPD"
));
if
(!
dynamic
&&
durationMs
==
-
1
)
{
// The manifest is static and doesn't define a duration. This is unexpected.
...
...
@@ -190,21 +191,21 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
boolean
seenFirstBaseUrl
=
false
;
do
{
xpp
.
next
();
if
(
isStartTag
(
xpp
,
"BaseURL"
))
{
if
(
ParserUtil
.
isStartTag
(
xpp
,
"BaseURL"
))
{
if
(!
seenFirstBaseUrl
)
{
baseUrl
=
parseBaseUrl
(
xpp
,
baseUrl
);
seenFirstBaseUrl
=
true
;
}
}
else
if
(
isStartTag
(
xpp
,
"AdaptationSet"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"AdaptationSet"
))
{
adaptationSets
.
add
(
parseAdaptationSet
(
xpp
,
baseUrl
,
segmentBase
));
}
else
if
(
isStartTag
(
xpp
,
"SegmentBase"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"SegmentBase"
))
{
segmentBase
=
parseSegmentBase
(
xpp
,
baseUrl
,
null
);
}
else
if
(
isStartTag
(
xpp
,
"SegmentList"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"SegmentList"
))
{
segmentBase
=
parseSegmentList
(
xpp
,
baseUrl
,
null
);
}
else
if
(
isStartTag
(
xpp
,
"SegmentTemplate"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"SegmentTemplate"
))
{
segmentBase
=
parseSegmentTemplate
(
xpp
,
baseUrl
,
null
);
}
}
while
(!
isEndTag
(
xpp
,
"Period"
));
}
while
(!
ParserUtil
.
isEndTag
(
xpp
,
"Period"
));
return
Pair
.
create
(
buildPeriod
(
id
,
startMs
,
adaptationSets
),
durationMs
);
}
...
...
@@ -234,35 +235,35 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
boolean
seenFirstBaseUrl
=
false
;
do
{
xpp
.
next
();
if
(
isStartTag
(
xpp
,
"BaseURL"
))
{
if
(
ParserUtil
.
isStartTag
(
xpp
,
"BaseURL"
))
{
if
(!
seenFirstBaseUrl
)
{
baseUrl
=
parseBaseUrl
(
xpp
,
baseUrl
);
seenFirstBaseUrl
=
true
;
}
}
else
if
(
isStartTag
(
xpp
,
"ContentProtection"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"ContentProtection"
))
{
contentProtectionsBuilder
.
addAdaptationSetProtection
(
parseContentProtection
(
xpp
));
}
else
if
(
isStartTag
(
xpp
,
"ContentComponent"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"ContentComponent"
))
{
language
=
checkLanguageConsistency
(
language
,
xpp
.
getAttributeValue
(
null
,
"lang"
));
contentType
=
checkContentTypeConsistency
(
contentType
,
parseContentType
(
xpp
));
}
else
if
(
isStartTag
(
xpp
,
"Representation"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"Representation"
))
{
Representation
representation
=
parseRepresentation
(
xpp
,
baseUrl
,
mimeType
,
codecs
,
width
,
height
,
frameRate
,
audioChannels
,
audioSamplingRate
,
language
,
segmentBase
,
contentProtectionsBuilder
);
contentProtectionsBuilder
.
endRepresentation
();
contentType
=
checkContentTypeConsistency
(
contentType
,
getContentType
(
representation
));
representations
.
add
(
representation
);
}
else
if
(
isStartTag
(
xpp
,
"AudioChannelConfiguration"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"AudioChannelConfiguration"
))
{
audioChannels
=
parseAudioChannelConfiguration
(
xpp
);
}
else
if
(
isStartTag
(
xpp
,
"SegmentBase"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"SegmentBase"
))
{
segmentBase
=
parseSegmentBase
(
xpp
,
baseUrl
,
(
SingleSegmentBase
)
segmentBase
);
}
else
if
(
isStartTag
(
xpp
,
"SegmentList"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"SegmentList"
))
{
segmentBase
=
parseSegmentList
(
xpp
,
baseUrl
,
(
SegmentList
)
segmentBase
);
}
else
if
(
isStartTag
(
xpp
,
"SegmentTemplate"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"SegmentTemplate"
))
{
segmentBase
=
parseSegmentTemplate
(
xpp
,
baseUrl
,
(
SegmentTemplate
)
segmentBase
);
}
else
if
(
isStartTag
(
xpp
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
))
{
parseAdaptationSetChild
(
xpp
);
}
}
while
(!
isEndTag
(
xpp
,
"AdaptationSet"
));
}
while
(!
ParserUtil
.
isEndTag
(
xpp
,
"AdaptationSet"
));
return
buildAdaptationSet
(
id
,
contentType
,
representations
,
contentProtectionsBuilder
.
build
());
}
...
...
@@ -316,14 +317,14 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
do
{
xpp
.
next
();
// The cenc:pssh element is defined in 23001-7:2015
if
(
isStartTag
(
xpp
,
"cenc:pssh"
)
&&
xpp
.
next
()
==
XmlPullParser
.
TEXT
)
{
if
(
ParserUtil
.
isStartTag
(
xpp
,
"cenc:pssh"
)
&&
xpp
.
next
()
==
XmlPullParser
.
TEXT
)
{
psshAtom
=
Base64
.
decode
(
xpp
.
getText
(),
Base64
.
DEFAULT
);
uuid
=
PsshAtomUtil
.
parseUuid
(
psshAtom
);
if
(
uuid
==
null
)
{
throw
new
ParserException
(
"Invalid pssh atom in cenc:pssh element"
);
}
}
}
while
(!
isEndTag
(
xpp
,
"ContentProtection"
));
}
while
(!
ParserUtil
.
isEndTag
(
xpp
,
"ContentProtection"
));
return
buildContentProtection
(
schemeIdUri
,
uuid
,
psshAtom
);
}
...
...
@@ -367,23 +368,23 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
boolean
seenFirstBaseUrl
=
false
;
do
{
xpp
.
next
();
if
(
isStartTag
(
xpp
,
"BaseURL"
))
{
if
(
ParserUtil
.
isStartTag
(
xpp
,
"BaseURL"
))
{
if
(!
seenFirstBaseUrl
)
{
baseUrl
=
parseBaseUrl
(
xpp
,
baseUrl
);
seenFirstBaseUrl
=
true
;
}
}
else
if
(
isStartTag
(
xpp
,
"AudioChannelConfiguration"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"AudioChannelConfiguration"
))
{
audioChannels
=
parseAudioChannelConfiguration
(
xpp
);
}
else
if
(
isStartTag
(
xpp
,
"SegmentBase"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"SegmentBase"
))
{
segmentBase
=
parseSegmentBase
(
xpp
,
baseUrl
,
(
SingleSegmentBase
)
segmentBase
);
}
else
if
(
isStartTag
(
xpp
,
"SegmentList"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"SegmentList"
))
{
segmentBase
=
parseSegmentList
(
xpp
,
baseUrl
,
(
SegmentList
)
segmentBase
);
}
else
if
(
isStartTag
(
xpp
,
"SegmentTemplate"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"SegmentTemplate"
))
{
segmentBase
=
parseSegmentTemplate
(
xpp
,
baseUrl
,
(
SegmentTemplate
)
segmentBase
);
}
else
if
(
isStartTag
(
xpp
,
"ContentProtection"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"ContentProtection"
))
{
contentProtectionsBuilder
.
addRepresentationProtection
(
parseContentProtection
(
xpp
));
}
}
while
(!
isEndTag
(
xpp
,
"Representation"
));
}
while
(!
ParserUtil
.
isEndTag
(
xpp
,
"Representation"
));
Format
format
=
buildFormat
(
id
,
mimeType
,
width
,
height
,
frameRate
,
audioChannels
,
audioSamplingRate
,
bandwidth
,
language
,
codecs
);
...
...
@@ -423,10 +424,10 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
RangedUri
initialization
=
parent
!=
null
?
parent
.
initialization
:
null
;
do
{
xpp
.
next
();
if
(
isStartTag
(
xpp
,
"Initialization"
))
{
if
(
ParserUtil
.
isStartTag
(
xpp
,
"Initialization"
))
{
initialization
=
parseInitialization
(
xpp
,
baseUrl
);
}
}
while
(!
isEndTag
(
xpp
,
"SegmentBase"
));
}
while
(!
ParserUtil
.
isEndTag
(
xpp
,
"SegmentBase"
));
return
buildSingleSegmentBase
(
initialization
,
timescale
,
presentationTimeOffset
,
baseUrl
,
indexStart
,
indexLength
);
...
...
@@ -453,17 +454,17 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
do
{
xpp
.
next
();
if
(
isStartTag
(
xpp
,
"Initialization"
))
{
if
(
ParserUtil
.
isStartTag
(
xpp
,
"Initialization"
))
{
initialization
=
parseInitialization
(
xpp
,
baseUrl
);
}
else
if
(
isStartTag
(
xpp
,
"SegmentTimeline"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"SegmentTimeline"
))
{
timeline
=
parseSegmentTimeline
(
xpp
);
}
else
if
(
isStartTag
(
xpp
,
"SegmentURL"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"SegmentURL"
))
{
if
(
segments
==
null
)
{
segments
=
new
ArrayList
<>();
}
segments
.
add
(
parseSegmentUrl
(
xpp
,
baseUrl
));
}
}
while
(!
isEndTag
(
xpp
,
"SegmentList"
));
}
while
(!
ParserUtil
.
isEndTag
(
xpp
,
"SegmentList"
));
if
(
parent
!=
null
)
{
initialization
=
initialization
!=
null
?
initialization
:
parent
.
initialization
;
...
...
@@ -500,12 +501,12 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
do
{
xpp
.
next
();
if
(
isStartTag
(
xpp
,
"Initialization"
))
{
if
(
ParserUtil
.
isStartTag
(
xpp
,
"Initialization"
))
{
initialization
=
parseInitialization
(
xpp
,
baseUrl
);
}
else
if
(
isStartTag
(
xpp
,
"SegmentTimeline"
))
{
}
else
if
(
ParserUtil
.
isStartTag
(
xpp
,
"SegmentTimeline"
))
{
timeline
=
parseSegmentTimeline
(
xpp
);
}
}
while
(!
isEndTag
(
xpp
,
"SegmentTemplate"
));
}
while
(!
ParserUtil
.
isEndTag
(
xpp
,
"SegmentTemplate"
));
if
(
parent
!=
null
)
{
initialization
=
initialization
!=
null
?
initialization
:
parent
.
initialization
;
...
...
@@ -530,7 +531,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
long
elapsedTime
=
0
;
do
{
xpp
.
next
();
if
(
isStartTag
(
xpp
,
"S"
))
{
if
(
ParserUtil
.
isStartTag
(
xpp
,
"S"
))
{
elapsedTime
=
parseLong
(
xpp
,
"t"
,
elapsedTime
);
long
duration
=
parseLong
(
xpp
,
"d"
);
int
count
=
1
+
parseInt
(
xpp
,
"r"
,
0
);
...
...
@@ -539,7 +540,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
elapsedTime
+=
duration
;
}
}
}
while
(!
isEndTag
(
xpp
,
"SegmentTimeline"
));
}
while
(!
ParserUtil
.
isEndTag
(
xpp
,
"SegmentTimeline"
));
return
segmentTimeline
;
}
...
...
@@ -592,7 +593,7 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
int
audioChannels
=
parseInt
(
xpp
,
"value"
);
do
{
xpp
.
next
();
}
while
(!
isEndTag
(
xpp
,
"AudioChannelConfiguration"
));
}
while
(!
ParserUtil
.
isEndTag
(
xpp
,
"AudioChannelConfiguration"
));
return
audioChannels
;
}
...
...
@@ -641,19 +642,6 @@ public class MediaPresentationDescriptionParser extends DefaultHandler
}
}
protected
static
boolean
isEndTag
(
XmlPullParser
xpp
,
String
name
)
throws
XmlPullParserException
{
return
xpp
.
getEventType
()
==
XmlPullParser
.
END_TAG
&&
name
.
equals
(
xpp
.
getName
());
}
protected
static
boolean
isStartTag
(
XmlPullParser
xpp
,
String
name
)
throws
XmlPullParserException
{
return
xpp
.
getEventType
()
==
XmlPullParser
.
START_TAG
&&
name
.
equals
(
xpp
.
getName
());
}
protected
static
boolean
isStartTag
(
XmlPullParser
xpp
)
throws
XmlPullParserException
{
return
xpp
.
getEventType
()
==
XmlPullParser
.
START_TAG
;
}
protected
static
float
parseFrameRate
(
XmlPullParser
xpp
,
float
defaultValue
)
{
float
frameRate
=
defaultValue
;
String
frameRateAttribute
=
xpp
.
getAttributeValue
(
null
,
"frameRate"
);
...
...
library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlNode.java
View file @
ab66dfa7
...
...
@@ -15,7 +15,16 @@
*/
package
com
.
google
.
android
.
exoplayer
.
text
.
ttml
;
import
android.text.Spannable
;
import
android.text.SpannableStringBuilder
;
import
android.text.Spanned
;
import
android.text.style.AlignmentSpan
;
import
android.text.style.BackgroundColorSpan
;
import
android.text.style.ForegroundColorSpan
;
import
android.text.style.StrikethroughSpan
;
import
android.text.style.StyleSpan
;
import
android.text.style.TypefaceSpan
;
import
android.text.style.UnderlineSpan
;
import
java.util.ArrayList
;
import
java.util.Iterator
;
...
...
@@ -28,7 +37,6 @@ import java.util.TreeSet;
/* package */
final
class
TtmlNode
{
public
static
final
long
UNDEFINED_TIME
=
-
1
;
public
static
final
String
TAG_TT
=
"tt"
;
public
static
final
String
TAG_HEAD
=
"head"
;
public
static
final
String
TAG_BODY
=
"body"
;
...
...
@@ -45,25 +53,51 @@ import java.util.TreeSet;
public
static
final
String
TAG_SMPTE_DATA
=
"smpte:data"
;
public
static
final
String
TAG_SMPTE_INFORMATION
=
"smpte:information"
;
public
static
final
String
ATTR_ID
=
"id"
;
public
static
final
String
ATTR_TTS_BACKGROUND_COLOR
=
"backgroundColor"
;
public
static
final
String
ATTR_TTS_FONT_STYLE
=
"fontStyle"
;
public
static
final
String
ATTR_TTS_FONT_SIZE
=
"fontSize"
;
public
static
final
String
ATTR_TTS_FONT_FAMILY
=
"fontFamily"
;
public
static
final
String
ATTR_TTS_FONT_WEIGHT
=
"fontWeight"
;
public
static
final
String
ATTR_TTS_COLOR
=
"color"
;
public
static
final
String
ATTR_TTS_TEXT_DECORATION
=
"textDecoration"
;
public
static
final
String
ATTR_TTS_TEXT_ALIGN
=
"textAlign"
;
public
static
final
String
LINETHROUGH
=
"linethrough"
;
public
static
final
String
NO_LINETHROUGH
=
"nolinethrough"
;
public
static
final
String
UNDERLINE
=
"underline"
;
public
static
final
String
NO_UNDERLINE
=
"nounderline"
;
public
static
final
String
ITALIC
=
"italic"
;
public
static
final
String
BOLD
=
"bold"
;
public
static
final
String
LEFT
=
"left"
;
public
static
final
String
CENTER
=
"center"
;
public
static
final
String
RIGHT
=
"right"
;
public
static
final
String
START
=
"start"
;
public
static
final
String
END
=
"end"
;
public
final
String
tag
;
public
final
String
text
;
public
final
boolean
isTextNode
;
public
final
long
startTimeUs
;
public
final
long
endTimeUs
;
public
final
TtmlStyle
style
;
private
List
<
TtmlNode
>
children
;
public
static
TtmlNode
buildTextNode
(
String
text
)
{
return
new
TtmlNode
(
null
,
applyTextElementSpacePolicy
(
text
),
UNDEFINED_TIME
,
UNDEFINED_TIME
);
public
static
TtmlNode
buildTextNode
(
String
text
,
TtmlStyle
style
)
{
return
new
TtmlNode
(
null
,
applyTextElementSpacePolicy
(
text
),
UNDEFINED_TIME
,
UNDEFINED_TIME
,
style
);
}
public
static
TtmlNode
buildNode
(
String
tag
,
long
startTimeUs
,
long
endTimeUs
)
{
return
new
TtmlNode
(
tag
,
null
,
startTimeUs
,
endTimeUs
);
public
static
TtmlNode
buildNode
(
String
tag
,
long
startTimeUs
,
long
endTimeUs
,
TtmlStyle
style
)
{
return
new
TtmlNode
(
tag
,
null
,
startTimeUs
,
endTimeUs
,
style
);
}
private
TtmlNode
(
String
tag
,
String
text
,
long
startTimeUs
,
long
endTimeUs
)
{
private
TtmlNode
(
String
tag
,
String
text
,
long
startTimeUs
,
long
endTimeUs
,
TtmlStyle
style
)
{
this
.
tag
=
tag
;
this
.
text
=
text
;
this
.
style
=
style
;
this
.
isTextNode
=
text
!=
null
;
this
.
startTimeUs
=
startTimeUs
;
this
.
endTimeUs
=
endTimeUs
;
...
...
@@ -168,15 +202,18 @@ import java.util.TreeSet;
// 4. Trim a trailing newline, if there is one.
if
(
builderLength
>
0
&&
builder
.
charAt
(
builderLength
-
1
)
==
'\n'
)
{
builder
.
delete
(
builderLength
-
1
,
builderLength
);
builderLength
--;
/*builderLength--;*/
}
return
builder
.
subSequence
(
0
,
builderLength
);
return
builder
;
}
private
SpannableStringBuilder
getText
(
long
timeUs
,
SpannableStringBuilder
builder
,
boolean
descendsPNode
)
{
if
(
isTextNode
&&
descendsPNode
)
{
int
start
=
builder
.
length
();
builder
.
append
(
text
);
applyStylesToSpan
(
builder
,
start
,
builder
.
length
(),
style
);
}
else
if
(
TAG_BR
.
equals
(
tag
)
&&
descendsPNode
)
{
builder
.
append
(
'\n'
);
}
else
if
(
TAG_METADATA
.
equals
(
tag
))
{
...
...
@@ -193,6 +230,37 @@ import java.util.TreeSet;
return
builder
;
}
private
static
void
applyStylesToSpan
(
SpannableStringBuilder
builder
,
int
start
,
int
end
,
TtmlStyle
style
)
{
if
(
style
.
getStyle
()
!=
TtmlStyle
.
UNSPECIFIED
)
{
builder
.
setSpan
(
new
StyleSpan
(
style
.
getStyle
()),
start
,
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
if
(
style
.
isLinethrough
())
{
builder
.
setSpan
(
new
StrikethroughSpan
(),
start
,
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
if
(
style
.
isUnderline
())
{
builder
.
setSpan
(
new
UnderlineSpan
(),
start
,
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
if
(
style
.
hasColorSpecified
())
{
builder
.
setSpan
(
new
ForegroundColorSpan
(
style
.
getColor
()),
start
,
end
,
Spannable
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
if
(
style
.
hasBackgroundColorSpecified
())
{
builder
.
setSpan
(
new
BackgroundColorSpan
(
style
.
getBackgroundColor
()),
start
,
end
,
Spannable
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
if
(
style
.
getFontFamily
()
!=
null
)
{
builder
.
setSpan
(
new
TypefaceSpan
(
style
.
getFontFamily
()),
start
,
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
if
(
style
.
getTextAlign
()
!=
null
)
{
builder
.
setSpan
(
new
AlignmentSpan
.
Standard
(
style
.
getTextAlign
()),
start
,
end
,
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
}
/**
* Invoked when the end of a paragraph is encountered. Adds a newline if there are one or more
* non-space characters since the previous newline.
...
...
library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlParser.java
View file @
ab66dfa7
...
...
@@ -20,7 +20,10 @@ import com.google.android.exoplayer.ParserException;
import
com.google.android.exoplayer.text.Subtitle
;
import
com.google.android.exoplayer.text.SubtitleParser
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.ParserUtil
;
import
android.graphics.Color
;
import
android.text.Layout
;
import
android.util.Log
;
import
org.xmlpull.v1.XmlPullParser
;
...
...
@@ -29,7 +32,9 @@ import org.xmlpull.v1.XmlPullParserFactory;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.util.HashMap
;
import
java.util.LinkedList
;
import
java.util.Map
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
...
...
@@ -61,6 +66,7 @@ public final class TtmlParser implements SubtitleParser {
private
static
final
String
ATTR_BEGIN
=
"begin"
;
private
static
final
String
ATTR_DURATION
=
"dur"
;
private
static
final
String
ATTR_END
=
"end"
;
private
static
final
String
ATTR_STYLE
=
"style"
;
private
static
final
Pattern
CLOCK_TIME
=
Pattern
.
compile
(
"^([0-9][0-9]+):([0-9][0-9]):([0-9][0-9])"
...
...
@@ -102,6 +108,7 @@ public final class TtmlParser implements SubtitleParser {
public
Subtitle
parse
(
InputStream
inputStream
)
throws
IOException
{
try
{
XmlPullParser
xmlParser
=
xmlParserFactory
.
newPullParser
();
Map
<
String
,
TtmlStyle
>
globalStyles
=
new
HashMap
<>();
xmlParser
.
setInput
(
inputStream
,
null
);
TtmlSubtitle
ttmlSubtitle
=
null
;
LinkedList
<
TtmlNode
>
nodeStack
=
new
LinkedList
<>();
...
...
@@ -115,9 +122,11 @@ public final class TtmlParser implements SubtitleParser {
if
(!
isSupportedTag
(
name
))
{
Log
.
i
(
TAG
,
"Ignoring unsupported tag: "
+
xmlParser
.
getName
());
unsupportedNodeDepth
++;
}
else
if
(
TtmlNode
.
TAG_HEAD
.
equals
(
name
))
{
parseHeader
(
xmlParser
,
globalStyles
);
}
else
{
try
{
TtmlNode
node
=
parseNode
(
xmlParser
,
parent
);
TtmlNode
node
=
parseNode
(
xmlParser
,
parent
,
globalStyles
);
nodeStack
.
addLast
(
node
);
if
(
parent
!=
null
)
{
parent
.
addChild
(
node
);
...
...
@@ -133,7 +142,7 @@ public final class TtmlParser implements SubtitleParser {
}
}
}
else
if
(
eventType
==
XmlPullParser
.
TEXT
)
{
parent
.
addChild
(
TtmlNode
.
buildTextNode
(
xmlParser
.
getText
()));
parent
.
addChild
(
TtmlNode
.
buildTextNode
(
xmlParser
.
getText
()
,
parent
.
style
));
}
else
if
(
eventType
==
XmlPullParser
.
END_TAG
)
{
if
(
xmlParser
.
getName
().
equals
(
TtmlNode
.
TAG_TT
))
{
ttmlSubtitle
=
new
TtmlSubtitle
(
nodeStack
.
getLast
());
...
...
@@ -156,19 +165,141 @@ public final class TtmlParser implements SubtitleParser {
}
}
private
Map
<
String
,
TtmlStyle
>
parseHeader
(
XmlPullParser
xmlParser
,
Map
<
String
,
TtmlStyle
>
globalStyles
)
throws
IOException
,
XmlPullParserException
{
do
{
xmlParser
.
next
();
if
(
ParserUtil
.
isStartTag
(
xmlParser
,
TtmlNode
.
TAG_STYLE
))
{
String
parentStyleId
=
xmlParser
.
getAttributeValue
(
null
,
ATTR_STYLE
);
TtmlStyle
style
=
parseStyleAttributes
(
xmlParser
,
new
TtmlStyle
());
if
(
parentStyleId
!=
null
)
{
String
[]
ids
=
parentStyleId
.
split
(
" "
);
for
(
int
i
=
0
;
i
<
ids
.
length
;
i
++)
{
style
.
chain
(
globalStyles
.
get
(
ids
[
i
]));
}
}
if
(
style
.
getId
()
!=
null
)
{
globalStyles
.
put
(
style
.
getId
(),
style
);
}
}
}
while
(!
ParserUtil
.
isEndTag
(
xmlParser
,
TtmlNode
.
TAG_HEAD
));
return
globalStyles
;
}
private
TtmlStyle
parseStyleAttributes
(
XmlPullParser
parser
,
TtmlStyle
style
)
{
int
attributeCount
=
parser
.
getAttributeCount
();
for
(
int
i
=
0
;
i
<
attributeCount
;
i
++)
{
String
attributeName
=
parser
.
getAttributeName
(
i
);
String
attributeValue
=
parser
.
getAttributeValue
(
i
);
// TODO: check if it is safe to remove the namespace prefix
switch
(
ParserUtil
.
removeNamespacePrefix
(
attributeName
))
{
case
TtmlNode
.
ATTR_ID
:
if
(
TtmlNode
.
TAG_STYLE
.
equals
(
parser
.
getName
()))
{
style
=
createIfNull
(
style
).
setId
(
attributeValue
);
}
break
;
case
TtmlNode
.
ATTR_TTS_BACKGROUND_COLOR
:
style
=
createIfNull
(
style
);
try
{
style
.
setBackgroundColor
(
Color
.
parseColor
(
attributeValue
));
}
catch
(
IllegalArgumentException
e
)
{
Log
.
w
(
TAG
,
"failed parsing background value: '"
+
attributeValue
+
"'"
);
}
break
;
case
TtmlNode
.
ATTR_TTS_COLOR
:
style
=
createIfNull
(
style
);
try
{
style
.
setColor
(
Color
.
parseColor
(
attributeValue
));
}
catch
(
IllegalArgumentException
e
)
{
Log
.
w
(
TAG
,
"failed parsing color value: '"
+
attributeValue
+
"'"
);
}
break
;
case
TtmlNode
.
ATTR_TTS_FONT_FAMILY
:
style
=
createIfNull
(
style
).
setFontFamily
(
attributeValue
);
break
;
case
TtmlNode
.
ATTR_TTS_FONT_SIZE
:
// TODO: handle size
break
;
case
TtmlNode
.
ATTR_TTS_FONT_WEIGHT
:
style
=
createIfNull
(
style
).
setBold
(
TtmlNode
.
BOLD
.
equals
(
attributeValue
.
toLowerCase
()));
break
;
case
TtmlNode
.
ATTR_TTS_FONT_STYLE
:
style
=
createIfNull
(
style
).
setItalic
(
TtmlNode
.
ITALIC
.
equals
(
attributeValue
.
toLowerCase
()));
break
;
case
TtmlNode
.
ATTR_TTS_TEXT_ALIGN
:
switch
(
attributeValue
.
toLowerCase
())
{
case
TtmlNode
.
LEFT
:
style
=
createIfNull
(
style
).
setTextAlign
(
Layout
.
Alignment
.
ALIGN_NORMAL
);
break
;
case
TtmlNode
.
START
:
style
=
createIfNull
(
style
).
setTextAlign
(
Layout
.
Alignment
.
ALIGN_NORMAL
);
break
;
case
TtmlNode
.
RIGHT
:
style
=
createIfNull
(
style
).
setTextAlign
(
Layout
.
Alignment
.
ALIGN_OPPOSITE
);
break
;
case
TtmlNode
.
END
:
style
=
createIfNull
(
style
).
setTextAlign
(
Layout
.
Alignment
.
ALIGN_OPPOSITE
);
break
;
case
TtmlNode
.
CENTER
:
style
=
createIfNull
(
style
).
setTextAlign
(
Layout
.
Alignment
.
ALIGN_CENTER
);
break
;
}
break
;
case
TtmlNode
.
ATTR_TTS_TEXT_DECORATION
:
switch
(
attributeValue
.
toLowerCase
())
{
case
TtmlNode
.
LINETHROUGH
:
style
=
createIfNull
(
style
).
setLinethrough
(
true
);
break
;
case
TtmlNode
.
NO_LINETHROUGH
:
style
=
createIfNull
(
style
).
setLinethrough
(
false
);
break
;
case
TtmlNode
.
UNDERLINE
:
style
=
createIfNull
(
style
).
setUnderline
(
true
);
break
;
case
TtmlNode
.
NO_UNDERLINE
:
style
=
createIfNull
(
style
).
setUnderline
(
false
);
break
;
}
break
;
default
:
// ignore
break
;
}
}
return
style
;
}
private
TtmlStyle
createIfNull
(
TtmlStyle
style
)
{
return
style
==
null
?
new
TtmlStyle
()
:
style
;
}
@Override
public
boolean
canParse
(
String
mimeType
)
{
return
MimeTypes
.
APPLICATION_TTML
.
equals
(
mimeType
);
}
private
TtmlNode
parseNode
(
XmlPullParser
parser
,
TtmlNode
parent
)
throws
ParserException
{
private
TtmlNode
parseNode
(
XmlPullParser
parser
,
TtmlNode
parent
,
Map
<
String
,
TtmlStyle
>
globalStyles
)
throws
ParserException
{
long
duration
=
0
;
long
startTime
=
TtmlNode
.
UNDEFINED_TIME
;
long
endTime
=
TtmlNode
.
UNDEFINED_TIME
;
int
attributeCount
=
parser
.
getAttributeCount
();
TtmlStyle
style
=
parseStyleAttributes
(
parser
,
null
);
boolean
hasInlineStyles
=
style
!=
null
;
if
(
parent
!=
null
&&
parent
.
style
!=
null
)
{
if
(
hasInlineStyles
)
{
style
.
inherit
(
parent
.
style
);
}
else
{
style
=
parent
.
style
.
getInheritableStyle
();
}
}
for
(
int
i
=
0
;
i
<
attributeCount
;
i
++)
{
// TODO: check if it
's safe to ignore the namespace of attributes as follows.
String
attr
=
parser
.
getAttributeName
(
i
).
replaceFirst
(
"^.*:"
,
""
);
// TODO: check if it
is safe to remove the namespace prefix
String
attr
=
ParserUtil
.
removeNamespacePrefix
(
parser
.
getAttributeName
(
i
)
);
String
value
=
parser
.
getAttributeValue
(
i
);
if
(
attr
.
equals
(
ATTR_BEGIN
))
{
startTime
=
parseTimeExpression
(
value
,
...
...
@@ -179,6 +310,34 @@ public final class TtmlParser implements SubtitleParser {
}
else
if
(
attr
.
equals
(
ATTR_DURATION
))
{
duration
=
parseTimeExpression
(
value
,
DEFAULT_FRAMERATE
,
DEFAULT_SUBFRAMERATE
,
DEFAULT_TICKRATE
);
}
else
if
(
attr
.
equals
(
ATTR_STYLE
))
{
// IDREFS: potentially multiple space delimited ids
String
[]
ids
=
value
.
split
(
" "
);
if
(
style
==
null
)
{
// use global style without overriding
if
(
ids
.
length
==
1
)
{
style
=
globalStyles
.
get
(
value
);
}
else
if
(
ids
.
length
>
1
){
style
=
new
TtmlStyle
();
for
(
int
j
=
0
;
j
<
ids
.
length
;
j
++)
{
style
.
chain
(
globalStyles
.
get
(
ids
[
j
]));
}
}
}
else
if
(
hasInlineStyles
)
{
// local attributes inherits from global style
for
(
int
j
=
0
;
j
<
ids
.
length
;
j
++)
{
style
.
chain
(
globalStyles
.
get
(
ids
[
j
]));
}
}
else
if
(
ids
.
length
>
1
||
(
ids
.
length
==
1
&&
style
!=
globalStyles
.
get
(
ids
[
0
])))
{
// merge global style and parent styles
TtmlStyle
inheritedStyles
=
style
;
style
=
new
TtmlStyle
();
for
(
int
j
=
0
;
j
<
ids
.
length
;
j
++)
{
style
.
chain
(
globalStyles
.
get
(
ids
[
j
]));
}
style
.
inherit
(
inheritedStyles
);
}
}
else
{
// Do nothing.
}
...
...
@@ -200,7 +359,7 @@ public final class TtmlParser implements SubtitleParser {
endTime
=
parent
.
endTimeUs
;
}
}
return
TtmlNode
.
buildNode
(
parser
.
getName
(),
startTime
,
endTime
);
return
TtmlNode
.
buildNode
(
parser
.
getName
(),
startTime
,
endTime
,
style
);
}
private
static
boolean
isSupportedTag
(
String
tag
)
{
...
...
library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlStyle.java
0 → 100644
View file @
ab66dfa7
/*
* Copyright (C) 2015 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
.
exoplayer
.
text
.
ttml
;
import
com.google.android.exoplayer.util.Assertions
;
import
android.graphics.Typeface
;
import
android.text.Layout
;
/**
* Style object of a <code>TtmlNode</code>
*/
public
final
class
TtmlStyle
{
public
static
final
short
UNSPECIFIED
=
-
1
;
public
static
final
short
STYLE_NORMAL
=
Typeface
.
NORMAL
;
public
static
final
short
STYLE_BOLD
=
Typeface
.
BOLD
;
public
static
final
short
STYLE_ITALIC
=
Typeface
.
ITALIC
;
public
static
final
short
STYLE_BOLD_ITALIC
=
Typeface
.
BOLD_ITALIC
;
private
static
final
short
OFF
=
0
;
private
static
final
short
ON
=
1
;
private
String
fontFamily
;
private
int
color
;
private
boolean
colorSpecified
;
private
int
backgroundColor
;
private
boolean
backgroundColorSpecified
;
private
short
linethrough
=
UNSPECIFIED
;
private
short
underline
=
UNSPECIFIED
;
private
short
bold
=
UNSPECIFIED
;
private
short
italic
=
UNSPECIFIED
;
private
String
id
;
private
TtmlStyle
inheritableStyle
;
private
Layout
.
Alignment
textAlign
;
/**
* Returns the style or <code>UNSPECIFIED</code> when no style information is given.
*
* @return UNSPECIFIED, STYLE_NORMAL, STYLE_BOLD, STYLE_BOLD or STYLE_BOLD_ITALIC
*/
public
short
getStyle
()
{
if
(
bold
==
UNSPECIFIED
&&
italic
==
UNSPECIFIED
)
{
return
UNSPECIFIED
;
}
short
style
=
STYLE_NORMAL
;
if
(
bold
!=
UNSPECIFIED
)
{
style
+=
bold
;
}
if
(
italic
!=
UNSPECIFIED
){
style
+=
italic
;
}
return
style
;
}
public
boolean
isLinethrough
()
{
return
linethrough
==
ON
;
}
public
TtmlStyle
setLinethrough
(
boolean
linethrough
)
{
Assertions
.
checkState
(
inheritableStyle
==
null
);
this
.
linethrough
=
linethrough
?
ON
:
OFF
;
return
this
;
}
public
boolean
isUnderline
()
{
return
underline
==
ON
;
}
public
TtmlStyle
setUnderline
(
boolean
underline
)
{
Assertions
.
checkState
(
inheritableStyle
==
null
);
this
.
underline
=
underline
?
ON
:
OFF
;
return
this
;
}
public
String
getFontFamily
()
{
return
fontFamily
;
}
public
TtmlStyle
setFontFamily
(
String
fontFamily
)
{
Assertions
.
checkState
(
inheritableStyle
==
null
);
this
.
fontFamily
=
fontFamily
;
return
this
;
}
public
int
getColor
()
{
return
color
;
}
public
TtmlStyle
setColor
(
int
color
)
{
Assertions
.
checkState
(
inheritableStyle
==
null
);
this
.
color
=
color
;
colorSpecified
=
true
;
return
this
;
}
public
boolean
hasColorSpecified
()
{
return
colorSpecified
;
}
public
int
getBackgroundColor
()
{
return
backgroundColor
;
}
public
TtmlStyle
setBackgroundColor
(
int
backgroundColor
)
{
this
.
backgroundColor
=
backgroundColor
;
backgroundColorSpecified
=
true
;
return
this
;
}
public
boolean
hasBackgroundColorSpecified
()
{
return
backgroundColorSpecified
;
}
public
TtmlStyle
setBold
(
boolean
isBold
)
{
Assertions
.
checkState
(
inheritableStyle
==
null
);
bold
=
isBold
?
STYLE_BOLD
:
STYLE_NORMAL
;
return
this
;
}
public
TtmlStyle
setItalic
(
boolean
isItalic
)
{
Assertions
.
checkState
(
inheritableStyle
==
null
);
italic
=
isItalic
?
STYLE_ITALIC
:
STYLE_NORMAL
;
return
this
;
}
public
TtmlStyle
getInheritableStyle
()
{
if
(
isFullyInheritable
())
{
return
this
;
}
else
if
(
inheritableStyle
==
null
)
{
inheritableStyle
=
new
TtmlStyle
().
inherit
(
this
);
}
return
inheritableStyle
;
}
private
boolean
isFullyInheritable
()
{
return
!
backgroundColorSpecified
;
}
/**
* Inherits from an ancestor style. Properties like <i>tts:backgroundColor</i> which
* are not inheritable are not inherited as well as properties which are already set locally
* are never overridden.
*
* @param ancestor the ancestor style to inherit from
*/
public
TtmlStyle
inherit
(
TtmlStyle
ancestor
)
{
return
inherit
(
ancestor
,
false
);
}
/**
* Chains this style to referential style. Local properties which are already set
* are never overridden.
*
* @param ancestor the referential style to inherit from
*/
public
TtmlStyle
chain
(
TtmlStyle
ancestor
)
{
return
inherit
(
ancestor
,
true
);
}
private
TtmlStyle
inherit
(
TtmlStyle
ancestor
,
boolean
chaining
)
{
if
(
ancestor
!=
null
)
{
if
(!
colorSpecified
&&
ancestor
.
colorSpecified
)
{
setColor
(
ancestor
.
color
);
}
if
(
bold
==
UNSPECIFIED
)
{
bold
=
ancestor
.
bold
;
}
if
(
italic
==
UNSPECIFIED
)
{
italic
=
ancestor
.
italic
;
}
if
(
fontFamily
==
null
)
{
fontFamily
=
ancestor
.
fontFamily
;
}
if
(
linethrough
==
UNSPECIFIED
)
{
linethrough
=
ancestor
.
linethrough
;
}
if
(
underline
==
UNSPECIFIED
)
{
underline
=
ancestor
.
underline
;
}
if
(
textAlign
==
null
)
{
textAlign
=
ancestor
.
textAlign
;
}
// attributes not inherited as of http://www.w3.org/TR/ttml1/
if
(
chaining
&&
!
backgroundColorSpecified
&&
ancestor
.
backgroundColorSpecified
)
{
setBackgroundColor
(
ancestor
.
backgroundColor
);
}
}
return
this
;
}
public
TtmlStyle
setId
(
String
id
)
{
this
.
id
=
id
;
return
this
;
}
public
String
getId
()
{
return
id
;
}
public
Layout
.
Alignment
getTextAlign
()
{
return
textAlign
;
}
public
TtmlStyle
setTextAlign
(
Layout
.
Alignment
textAlign
)
{
this
.
textAlign
=
textAlign
;
return
this
;
}
}
library/src/main/java/com/google/android/exoplayer/text/ttml/TtmlSubtitle.java
View file @
ab66dfa7
...
...
@@ -56,6 +56,11 @@ public final class TtmlSubtitle implements Subtitle {
return
(
eventTimesUs
.
length
==
0
?
-
1
:
eventTimesUs
[
eventTimesUs
.
length
-
1
]);
}
/* @VisibleForTesting */
/* package */
TtmlNode
getRoot
()
{
return
root
;
}
@Override
public
List
<
Cue
>
getCues
(
long
timeUs
)
{
CharSequence
cueText
=
root
.
getText
(
timeUs
);
...
...
library/src/main/java/com/google/android/exoplayer/util/ParserUtil.java
0 → 100644
View file @
ab66dfa7
/*
* Copyright (C) 2014 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
.
exoplayer
.
util
;
import
org.xmlpull.v1.XmlPullParser
;
import
org.xmlpull.v1.XmlPullParserException
;
/**
* Parser utility functions.
*/
public
final
class
ParserUtil
{
private
ParserUtil
()
{}
public
static
boolean
isEndTag
(
XmlPullParser
xpp
,
String
name
)
throws
XmlPullParserException
{
return
xpp
.
getEventType
()
==
XmlPullParser
.
END_TAG
&&
name
.
equals
(
xpp
.
getName
());
}
public
static
boolean
isStartTag
(
XmlPullParser
xpp
,
String
name
)
throws
XmlPullParserException
{
return
xpp
.
getEventType
()
==
XmlPullParser
.
START_TAG
&&
name
.
equals
(
xpp
.
getName
());
}
public
static
boolean
isStartTag
(
XmlPullParser
xpp
)
throws
XmlPullParserException
{
return
xpp
.
getEventType
()
==
XmlPullParser
.
START_TAG
;
}
/**
* Removes the namespace part ('^.*:') of the attributeName.
*
* @param attributeName the string to remove the namespace prefix from
* @return the name of the attribute without the prefix
*/
public
static
String
removeNamespacePrefix
(
String
attributeName
)
{
return
attributeName
.
replaceFirst
(
"^.*:"
,
""
);
}
}
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