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
e4e02f91
authored
Sep 28, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Further improve WebVTT parser according to WebVTT spec
parent
71f542f7
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
400 additions
and
325 deletions
library/src/androidTest/assets/webvtt/live_typical
library/src/androidTest/assets/webvtt/typical_with_comments
library/src/androidTest/assets/webvtt/typical_with_metadata → library/src/androidTest/assets/webvtt/with_positioning
library/src/androidTest/assets/webvtt/typical_with_tags → library/src/androidTest/assets/webvtt/with_tags
library/src/androidTest/java/com/google/android/exoplayer/text/webvtt/WebvttParserTest.java
library/src/main/java/com/google/android/exoplayer/text/Cue.java
library/src/main/java/com/google/android/exoplayer/text/CuePainter.java
library/src/main/java/com/google/android/exoplayer/text/SubtitleLayout.java
library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttCue.java
library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttParser.java
library/src/androidTest/assets/webvtt/live_typical
deleted
100644 → 0
View file @
71f542f7
WEBVTT
00:00.000 --> 00:01.234
This is the first subtitle.
00:02.345 --> 00:03.456
This is the second subtitle.
library/src/androidTest/assets/webvtt/typical_with_comments
View file @
e4e02f91
...
...
@@ -8,7 +8,9 @@ with multiple lines
00:00.000 --> 00:01.234
This is the first subtitle.
NOTE Single line comment
NOTE Single line comment with a space
NOTE Single line comment with a tab
2
00:02.345 --> 00:03.456
...
...
library/src/androidTest/assets/webvtt/
typical_with_metadata
→
library/src/androidTest/assets/webvtt/
with_positioning
View file @
e4e02f91
...
...
@@ -16,7 +16,13 @@ NOTE Line as percentage and line alignment
00:04.000 --> 00:05.000 line:45%,end align:middle size:35%
This is the third subtitle.
NOTE Line as absolute negative number and without line alignment
NOTE Line as absolute negative number and without line alignment.
00:06.000 --> 00:07.000 line:-10 align:middle
This is the fourth subtitle.
NOTE The positioning alignment should be inherited from align.
00:07.000 --> 00:08.000 position:10% align:end size:10%
This is the fifth subtitle.
00:06.000 --> 00:07.000 line:-10 align:middle size:35%
This is the forth subtitle.
library/src/androidTest/assets/webvtt/
typical_
with_tags
→
library/src/androidTest/assets/webvtt/with_tags
View file @
e4e02f91
File moved
library/src/androidTest/java/com/google/android/exoplayer/text/webvtt/WebvttParserTest.java
View file @
e4e02f91
...
...
@@ -15,32 +15,31 @@
*/
package
com
.
google
.
android
.
exoplayer
.
text
.
webvtt
;
import
android.test.InstrumentationTestCase
;
import
android.text.Layout
;
import
com.google.android.exoplayer.text.Cue
;
import
android.test.InstrumentationTestCase
;
import
android.text.Layout.Alignment
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.util.List
;
/**
* Unit test for {@link WebvttParser}.
*/
public
class
WebvttParserTest
extends
InstrumentationTestCase
{
private
static
final
String
TYPICAL_WEBVTT_FILE
=
"webvtt/typical"
;
private
static
final
String
TYPICAL_WITH_IDS_WEBVTT_FILE
=
"webvtt/typical_with_identifiers"
;
private
static
final
String
TYPICAL_WITH_TAGS_WEBVTT_FILE
=
"webvtt/typical_with_tags"
;
private
static
final
String
TYPICAL_WITH_COMMENTS_WEBVTT_FILE
=
"webvtt/typical_with_comments"
;
private
static
final
String
TYPICAL_WITH_METADATA_WEBVTT_FILE
=
"webvtt/typical_with_metadata"
;
private
static
final
String
LIVE_TYPICAL_WEBVTT_FILE
=
"webvtt/live_typical"
;
private
static
final
String
EMPTY_WEBVTT_FILE
=
"webvtt/empty"
;
private
static
final
String
TYPICAL_FILE
=
"webvtt/typical"
;
private
static
final
String
TYPICAL_WITH_IDS_FILE
=
"webvtt/typical_with_identifiers"
;
private
static
final
String
TYPICAL_WITH_COMMENTS_FILE
=
"webvtt/typical_with_comments"
;
private
static
final
String
WITH_POSITIONING_FILE
=
"webvtt/with_positioning"
;
private
static
final
String
WITH_TAGS_FILE
=
"webvtt/with_tags"
;
private
static
final
String
EMPTY_FILE
=
"webvtt/empty"
;
public
void
testParse
NullWebvttFile
()
throws
IOException
{
public
void
testParse
Empty
()
throws
IOException
{
WebvttParser
parser
=
new
WebvttParser
();
InputStream
inputStream
=
getInstrumentation
().
getContext
().
getResources
().
getAssets
().
open
(
EMPTY_WEBVTT_FILE
);
InputStream
inputStream
=
getInstrumentation
().
getContext
().
getResources
().
getAssets
()
.
open
(
EMPTY_FILE
);
try
{
parser
.
parse
(
inputStream
);
fail
(
"Expected IOException"
);
...
...
@@ -49,189 +48,113 @@ public class WebvttParserTest extends InstrumentationTestCase {
}
}
public
void
testParseTypical
WebvttFile
()
throws
IOException
{
public
void
testParseTypical
()
throws
IOException
{
WebvttParser
parser
=
new
WebvttParser
();
InputStream
inputStream
=
getInstrumentation
().
getContext
().
getResources
().
getAssets
().
open
(
TYPICAL_
WEBVTT_
FILE
);
getInstrumentation
().
getContext
().
getResources
().
getAssets
().
open
(
TYPICAL_FILE
);
WebvttSubtitle
subtitle
=
parser
.
parse
(
inputStream
);
// test event count
assertEquals
(
4
,
subtitle
.
getEventTimeCount
());
// test first cue
assertEquals
(
0
,
subtitle
.
getEventTime
(
0
));
assertEquals
(
"This is the first subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
0
)).
get
(
0
).
text
.
toString
());
assertEquals
(
1234000
,
subtitle
.
getEventTime
(
1
));
// test second cue
assertEquals
(
2345000
,
subtitle
.
getEventTime
(
2
));
assertEquals
(
"This is the second subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
2
)).
get
(
0
).
text
.
toString
());
assertEquals
(
3456000
,
subtitle
.
getEventTime
(
3
));
// test cues
assertCue
(
subtitle
,
0
,
0
,
1234000
,
"This is the first subtitle."
);
assertCue
(
subtitle
,
2
,
2345000
,
3456000
,
"This is the second subtitle."
);
}
public
void
testParseTypicalWithIds
WebvttFile
()
throws
IOException
{
public
void
testParseTypicalWithIds
()
throws
IOException
{
WebvttParser
parser
=
new
WebvttParser
();
InputStream
inputStream
=
getInstrumentation
().
getContext
().
getResources
().
getAssets
()
.
open
(
TYPICAL_WITH_IDS_WEBVTT_FILE
);
InputStream
inputStream
=
getInstrumentation
().
getContext
().
getResources
().
getAssets
()
.
open
(
TYPICAL_WITH_IDS_FILE
);
WebvttSubtitle
subtitle
=
parser
.
parse
(
inputStream
);
// test event count
assertEquals
(
4
,
subtitle
.
getEventTimeCount
());
// test first cue
assertEquals
(
0
,
subtitle
.
getEventTime
(
0
));
assertEquals
(
"This is the first subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
0
)).
get
(
0
).
text
.
toString
());
assertEquals
(
1234000
,
subtitle
.
getEventTime
(
1
));
// test second cue
assertEquals
(
2345000
,
subtitle
.
getEventTime
(
2
));
assertEquals
(
"This is the second subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
2
)).
get
(
0
).
text
.
toString
());
assertEquals
(
3456000
,
subtitle
.
getEventTime
(
3
));
// test cues
assertCue
(
subtitle
,
0
,
0
,
1234000
,
"This is the first subtitle."
);
assertCue
(
subtitle
,
2
,
2345000
,
3456000
,
"This is the second subtitle."
);
}
public
void
testParseTypicalWith
TagsWebvttFile
()
throws
IOException
{
public
void
testParseTypicalWith
Comments
()
throws
IOException
{
WebvttParser
parser
=
new
WebvttParser
();
InputStream
inputStream
=
getInstrumentation
().
getContext
().
getResources
().
getAssets
()
.
open
(
TYPICAL_WITH_TAGS_WEBVTT_FILE
);
WebvttSubtitle
subtitle
=
parser
.
parse
(
inputStream
);
// test event count
assertEquals
(
8
,
subtitle
.
getEventTimeCount
());
// test first cue
assertEquals
(
0
,
subtitle
.
getEventTime
(
0
));
assertEquals
(
"This is the first subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
0
)).
get
(
0
).
text
.
toString
());
assertEquals
(
1234000
,
subtitle
.
getEventTime
(
1
));
// test second cue
assertEquals
(
2345000
,
subtitle
.
getEventTime
(
2
));
assertEquals
(
"This is the second subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
2
)).
get
(
0
).
text
.
toString
());
assertEquals
(
3456000
,
subtitle
.
getEventTime
(
3
));
// test third cue
assertEquals
(
4000000
,
subtitle
.
getEventTime
(
4
));
assertEquals
(
"This is the third subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
4
)).
get
(
0
).
text
.
toString
());
assertEquals
(
5000000
,
subtitle
.
getEventTime
(
5
));
// test fourth cue
assertEquals
(
6000000
,
subtitle
.
getEventTime
(
6
));
assertEquals
(
"This is the <fourth> &subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
6
)).
get
(
0
).
text
.
toString
());
assertEquals
(
7000000
,
subtitle
.
getEventTime
(
7
));
}
public
void
testParseTypicalWithCommentsWebvttFile
()
throws
IOException
{
WebvttParser
parser
=
new
WebvttParser
();
InputStream
inputStream
=
getInstrumentation
().
getContext
().
getResources
().
getAssets
()
.
open
(
TYPICAL_WITH_COMMENTS_WEBVTT_FILE
);
InputStream
inputStream
=
getInstrumentation
().
getContext
().
getResources
().
getAssets
()
.
open
(
TYPICAL_WITH_COMMENTS_FILE
);
WebvttSubtitle
subtitle
=
parser
.
parse
(
inputStream
);
// test event count
assertEquals
(
4
,
subtitle
.
getEventTimeCount
());
// test first cue
assertEquals
(
0
,
subtitle
.
getEventTime
(
0
));
assertEquals
(
"This is the first subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
0
)).
get
(
0
).
text
.
toString
());
assertEquals
(
1234000
,
subtitle
.
getEventTime
(
1
));
// test second cue
assertEquals
(
2345000
,
subtitle
.
getEventTime
(
2
));
assertEquals
(
"This is the second subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
2
)).
get
(
0
).
text
.
toString
());
assertEquals
(
3456000
,
subtitle
.
getEventTime
(
3
));
// test cues
assertCue
(
subtitle
,
0
,
0
,
1234000
,
"This is the first subtitle."
);
assertCue
(
subtitle
,
2
,
2345000
,
3456000
,
"This is the second subtitle."
);
}
public
void
testParse
TypicalWithMetadataWebvttFile
()
throws
IOException
{
public
void
testParse
WithTags
()
throws
IOException
{
WebvttParser
parser
=
new
WebvttParser
();
InputStream
inputStream
=
getInstrumentation
().
getContext
().
getResources
().
getAssets
()
.
open
(
TYPICAL_WITH_METADATA_WEBVTT_FILE
);
InputStream
inputStream
=
getInstrumentation
().
getContext
().
getResources
().
getAssets
()
.
open
(
WITH_TAGS_FILE
);
WebvttSubtitle
subtitle
=
parser
.
parse
(
inputStream
);
// test event count
assertEquals
(
8
,
subtitle
.
getEventTimeCount
());
// test first cue
assertEquals
(
0
,
subtitle
.
getEventTime
(
0
));
assertEquals
(
"This is the first subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
0
)).
get
(
0
).
text
.
toString
());
assertEquals
(
10
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
0
)).
get
(
0
).
position
);
assertEquals
(
Layout
.
Alignment
.
ALIGN_NORMAL
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
0
)).
get
(
0
).
alignment
);
assertEquals
(
35
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
0
)).
get
(
0
).
size
);
assertEquals
(
1234000
,
subtitle
.
getEventTime
(
1
));
// test second cue
assertEquals
(
2345000
,
subtitle
.
getEventTime
(
2
));
assertEquals
(
"This is the second subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
2
)).
get
(
0
).
text
.
toString
());
assertEquals
(
Cue
.
UNSET_VALUE
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
2
)).
get
(
0
).
position
);
assertEquals
(
Layout
.
Alignment
.
ALIGN_OPPOSITE
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
2
)).
get
(
0
).
alignment
);
assertEquals
(
35
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
2
)).
get
(
0
).
size
);
assertEquals
(
3456000
,
subtitle
.
getEventTime
(
3
));
// test third cue
assertEquals
(
4000000
,
subtitle
.
getEventTime
(
4
));
assertEquals
(
"This is the third subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
4
)).
get
(
0
).
text
.
toString
());
assertEquals
(
45
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
4
)).
get
(
0
).
line
);
assertEquals
(
Layout
.
Alignment
.
ALIGN_CENTER
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
4
)).
get
(
0
).
alignment
);
assertEquals
(
35
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
4
)).
get
(
0
).
size
);
assertEquals
(
5000000
,
subtitle
.
getEventTime
(
5
));
// test forth cue
assertEquals
(
6000000
,
subtitle
.
getEventTime
(
6
));
assertEquals
(
"This is the forth subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
6
)).
get
(
0
).
text
.
toString
());
assertEquals
(-
10
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
6
)).
get
(
0
).
line
);
assertEquals
(
Layout
.
Alignment
.
ALIGN_CENTER
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
6
)).
get
(
0
).
alignment
);
assertEquals
(
35
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
6
)).
get
(
0
).
size
);
assertEquals
(
7000000
,
subtitle
.
getEventTime
(
7
));
// test cues
assertCue
(
subtitle
,
0
,
0
,
1234000
,
"This is the first subtitle."
);
assertCue
(
subtitle
,
2
,
2345000
,
3456000
,
"This is the second subtitle."
);
assertCue
(
subtitle
,
4
,
4000000
,
5000000
,
"This is the third subtitle."
);
assertCue
(
subtitle
,
6
,
6000000
,
7000000
,
"This is the <fourth> &subtitle."
);
}
public
void
testParse
LiveTypicalWebvttFile
()
throws
IOException
{
public
void
testParse
WithPositioning
()
throws
IOException
{
WebvttParser
parser
=
new
WebvttParser
();
InputStream
inputStream
=
getInstrumentation
().
getContext
().
getResources
().
getAssets
().
open
(
LIVE_TYPICAL_WEBVTT
_FILE
);
InputStream
inputStream
=
getInstrumentation
().
getContext
().
getResources
().
getAssets
()
.
open
(
WITH_POSITIONING
_FILE
);
WebvttSubtitle
subtitle
=
parser
.
parse
(
inputStream
);
// test event count
long
startTimeUs
=
0
;
assertEquals
(
4
,
subtitle
.
getEventTimeCount
());
assertEquals
(
10
,
subtitle
.
getEventTimeCount
());
// test cues
assertCue
(
subtitle
,
0
,
0
,
1234000
,
"This is the first subtitle."
,
Alignment
.
ALIGN_NORMAL
,
Cue
.
DIMEN_UNSET
,
Cue
.
TYPE_UNSET
,
Cue
.
TYPE_UNSET
,
0.1f
,
Cue
.
ANCHOR_TYPE_START
,
0.35f
);
assertCue
(
subtitle
,
2
,
2345000
,
3456000
,
"This is the second subtitle."
,
Alignment
.
ALIGN_OPPOSITE
,
Cue
.
DIMEN_UNSET
,
Cue
.
TYPE_UNSET
,
Cue
.
TYPE_UNSET
,
Cue
.
DIMEN_UNSET
,
Cue
.
TYPE_UNSET
,
0.35f
);
assertCue
(
subtitle
,
4
,
4000000
,
5000000
,
"This is the third subtitle."
,
Alignment
.
ALIGN_CENTER
,
0.45f
,
Cue
.
LINE_TYPE_FRACTION
,
Cue
.
ANCHOR_TYPE_END
,
Cue
.
DIMEN_UNSET
,
Cue
.
TYPE_UNSET
,
0.35f
);
assertCue
(
subtitle
,
6
,
6000000
,
7000000
,
"This is the fourth subtitle."
,
Alignment
.
ALIGN_CENTER
,
-
10
f
,
Cue
.
LINE_TYPE_NUMBER
,
Cue
.
TYPE_UNSET
,
Cue
.
DIMEN_UNSET
,
Cue
.
TYPE_UNSET
,
Cue
.
DIMEN_UNSET
);
assertCue
(
subtitle
,
8
,
7000000
,
8000000
,
"This is the fifth subtitle."
,
Alignment
.
ALIGN_OPPOSITE
,
Cue
.
DIMEN_UNSET
,
Cue
.
TYPE_UNSET
,
Cue
.
TYPE_UNSET
,
0.1f
,
Cue
.
ANCHOR_TYPE_END
,
0.1f
);
}
private
static
void
assertCue
(
WebvttSubtitle
subtitle
,
int
eventTimeIndex
,
long
startTimeUs
,
int
endTimeUs
,
String
text
)
{
assertCue
(
subtitle
,
eventTimeIndex
,
startTimeUs
,
endTimeUs
,
text
,
null
,
Cue
.
DIMEN_UNSET
,
Cue
.
TYPE_UNSET
,
Cue
.
TYPE_UNSET
,
Cue
.
DIMEN_UNSET
,
Cue
.
TYPE_UNSET
,
Cue
.
DIMEN_UNSET
);
}
// test first cue
assertEquals
(
startTimeUs
,
subtitle
.
getEventTime
(
0
));
assertEquals
(
"This is the first subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
0
)).
get
(
0
).
text
.
toString
());
assertEquals
(
startTimeUs
+
1234000
,
subtitle
.
getEventTime
(
1
));
// test second cue
assertEquals
(
startTimeUs
+
2345000
,
subtitle
.
getEventTime
(
2
));
assertEquals
(
"This is the second subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
2
)).
get
(
0
).
text
.
toString
());
assertEquals
(
startTimeUs
+
3456000
,
subtitle
.
getEventTime
(
3
));
private
static
void
assertCue
(
WebvttSubtitle
subtitle
,
int
eventTimeIndex
,
long
startTimeUs
,
int
endTimeUs
,
String
text
,
Alignment
textAlignment
,
float
line
,
int
lineType
,
int
lineAnchor
,
float
position
,
int
positionAnchor
,
float
size
)
{
assertEquals
(
startTimeUs
,
subtitle
.
getEventTime
(
eventTimeIndex
));
assertEquals
(
endTimeUs
,
subtitle
.
getEventTime
(
eventTimeIndex
+
1
));
List
<
Cue
>
cues
=
subtitle
.
getCues
(
subtitle
.
getEventTime
(
eventTimeIndex
));
assertEquals
(
1
,
cues
.
size
());
// Assert cue properties
Cue
cue
=
cues
.
get
(
0
);
assertEquals
(
text
,
cue
.
text
.
toString
());
assertEquals
(
textAlignment
,
cue
.
textAlignment
);
assertEquals
(
line
,
cue
.
line
);
assertEquals
(
lineType
,
cue
.
lineType
);
assertEquals
(
lineAnchor
,
cue
.
lineAnchor
);
assertEquals
(
position
,
cue
.
position
);
assertEquals
(
positionAnchor
,
cue
.
positionAnchor
);
assertEquals
(
size
,
cue
.
size
);
}
}
library/src/main/java/com/google/android/exoplayer/text/Cue.java
View file @
e4e02f91
...
...
@@ -23,30 +23,117 @@ import android.text.Layout.Alignment;
public
class
Cue
{
/**
*
Used by some methods to indicate that no value is set
.
*
An unset position or width
.
*/
public
static
final
int
UNSET_VALUE
=
-
1
;
public
static
final
float
DIMEN_UNSET
=
Float
.
MIN_VALUE
;
/**
* An unset anchor or line type value.
*/
public
static
final
int
TYPE_UNSET
=
Integer
.
MIN_VALUE
;
/**
* Anchors the left (for horizontal positions) or top (for vertical positions) edge of the cue
* box.
*/
public
static
final
int
ANCHOR_TYPE_START
=
0
;
/**
* Anchors the middle of the cue box.
*/
public
static
final
int
ANCHOR_TYPE_MIDDLE
=
1
;
/**
* Anchors the right (for horizontal positions) or bottom (for vertical positions) edge of the cue
* box.
*/
public
static
final
int
ANCHOR_TYPE_END
=
2
;
/**
* Value for {@link #lineType} when {@link #line} is a fractional position.
*/
public
static
final
int
LINE_TYPE_FRACTION
=
0
;
/**
* Value for {@link #lineType} when {@link #line} is a line number.
*/
public
static
final
int
LINE_TYPE_NUMBER
=
1
;
/**
* The cue text. Note the {@link CharSequence} may be decorated with styling spans.
*/
public
final
CharSequence
text
;
public
final
int
line
;
public
final
int
position
;
public
final
Alignment
alignment
;
public
final
int
size
;
/**
* The alignment of the cue text within the cue box.
*/
public
final
Alignment
textAlignment
;
/**
* The position of the {@link #lineAnchor} of the cue box within the viewport in the direction
* orthogonal to the writing direction, or {@link #DIMEN_UNSET}. When set, the interpretation of
* the value depends on the value of {@link #lineType}.
* <p>
* For horizontal text and {@link #lineType} equal to {@link #LINE_TYPE_FRACTION}, this is the
* fractional vertical position relative to the top of the viewport.
*/
public
final
float
line
;
/**
* The type of the {@link #line} value.
* <p>
* {@link #LINE_TYPE_FRACTION} indicates that {@link #line} is a fractional position within the
* viewport.
* <p>
* {@link #LINE_TYPE_NUMBER} indicates that {@link #line} is a line number, where the size of each
* line is taken to be the size of the first line of the cue. When {@link #line} is greater than
* or equal to 0, lines count from the start of the viewport (the first line is numbered 0). When
* {@link #line} is negative, lines count from the end of the viewport (the last line is numbered
* -1). For horizontal text the size of the first line of the cue is its height, and the start
* and end of the viewport are the top and bottom respectively.
*/
public
final
int
lineType
;
/**
* The cue box anchor positioned by {@link #line}. One of {@link #ANCHOR_TYPE_START},
* {@link #ANCHOR_TYPE_MIDDLE}, {@link #ANCHOR_TYPE_END} and {@link #TYPE_UNSET}.
* <p>
* For the normal case of horizontal text, {@link #ANCHOR_TYPE_START}, {@link #ANCHOR_TYPE_MIDDLE}
* and {@link #ANCHOR_TYPE_END} correspond to the top, middle and bottom of the cue box
* respectively.
*/
public
final
int
lineAnchor
;
/**
* The fractional position of the {@link #positionAnchor} of the cue box within the viewport in
* the direction orthogonal to {@link #line}, or {@link #DIMEN_UNSET}.
* <p>
* For horizontal text, this is the horizontal position relative to the left of the viewport. Note
* that positioning is relative to the left of the viewport even in the case of right-to-left
* text.
*/
public
final
float
position
;
/**
* The cue box anchor positioned by {@link #position}. One of {@link #ANCHOR_TYPE_START},
* {@link #ANCHOR_TYPE_MIDDLE}, {@link #ANCHOR_TYPE_END} and {@link #TYPE_UNSET}.
* <p>
* For the normal case of horizontal text, {@link #ANCHOR_TYPE_START}, {@link #ANCHOR_TYPE_MIDDLE}
* and {@link #ANCHOR_TYPE_END} correspond to the left, middle and right of the cue box
* respectively.
*/
public
final
int
positionAnchor
;
/**
* The size of the cue box in the writing direction specified as a fraction of the viewport size
* in that direction, or {@link #DIMEN_UNSET}.
*/
public
final
float
size
;
public
Cue
()
{
this
(
null
);
}
public
Cue
(
CharSequence
text
)
{
this
(
text
,
UNSET_VALUE
,
UNSET_VALUE
,
null
,
UNSET_VALUE
);
this
(
text
,
null
,
DIMEN_UNSET
,
TYPE_UNSET
,
TYPE_UNSET
,
DIMEN_UNSET
,
TYPE_UNSET
,
DIMEN_UNSET
);
}
public
Cue
(
CharSequence
text
,
int
line
,
int
position
,
Alignment
alignment
,
int
size
)
{
public
Cue
(
CharSequence
text
,
Alignment
textAlignment
,
float
line
,
int
lineType
,
int
lineAnchor
,
float
position
,
int
positionAnchor
,
float
size
)
{
this
.
text
=
text
;
this
.
textAlignment
=
textAlignment
;
this
.
line
=
line
;
this
.
lineType
=
lineType
;
this
.
lineAnchor
=
lineAnchor
;
this
.
position
=
position
;
this
.
alignment
=
alignment
;
this
.
positionAnchor
=
positionAnchor
;
this
.
size
=
size
;
}
...
...
library/src/main/java/com/google/android/exoplayer/text/CuePainter.java
View file @
e4e02f91
...
...
@@ -63,8 +63,13 @@ import android.util.Log;
// Previous input variables.
private
CharSequence
cueText
;
private
int
cuePosition
;
private
Alignment
cueAlignment
;
private
Alignment
cueTextAlignment
;
private
float
cueLine
;
private
int
cueLineType
;
private
int
cueLineAnchor
;
private
float
cuePosition
;
private
int
cuePositionAnchor
;
private
float
cueSize
;
private
boolean
applyEmbeddedStyles
;
private
int
foregroundColor
;
private
int
backgroundColor
;
...
...
@@ -120,7 +125,7 @@ import android.util.Log;
* @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 bottomPaddingFraction The bottom padding fraction to apply when {@link Cue#line} is
* {@link Cue#
UNSET_VALUE
}, 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 cueBoxLeft The left position of the enclosing cue box.
* @param cueBoxTop The top position of the enclosing cue box.
...
...
@@ -140,8 +145,13 @@ import android.util.Log;
cueText
=
cueText
.
toString
();
}
if
(
areCharSequencesEqual
(
this
.
cueText
,
cueText
)
&&
Util
.
areEqual
(
this
.
cueTextAlignment
,
cue
.
textAlignment
)
&&
this
.
cueLine
==
cue
.
line
&&
this
.
cueLineType
==
cue
.
lineType
&&
Util
.
areEqual
(
this
.
cueLineAnchor
,
cue
.
lineAnchor
)
&&
this
.
cuePosition
==
cue
.
position
&&
Util
.
areEqual
(
this
.
cueAlignment
,
cue
.
alignment
)
&&
Util
.
areEqual
(
this
.
cuePositionAnchor
,
cue
.
positionAnchor
)
&&
this
.
cueSize
==
cue
.
size
&&
this
.
applyEmbeddedStyles
==
applyEmbeddedStyles
&&
this
.
foregroundColor
==
style
.
foregroundColor
&&
this
.
backgroundColor
==
style
.
backgroundColor
...
...
@@ -161,8 +171,13 @@ import android.util.Log;
}
this
.
cueText
=
cueText
;
this
.
cueTextAlignment
=
cue
.
textAlignment
;
this
.
cueLine
=
cue
.
line
;
this
.
cueLineType
=
cue
.
lineType
;
this
.
cueLineAnchor
=
cue
.
lineAnchor
;
this
.
cuePosition
=
cue
.
position
;
this
.
cueAlignment
=
cue
.
alignment
;
this
.
cuePositionAnchor
=
cue
.
positionAnchor
;
this
.
cueSize
=
cue
.
size
;
this
.
applyEmbeddedStyles
=
applyEmbeddedStyles
;
this
.
foregroundColor
=
style
.
foregroundColor
;
this
.
backgroundColor
=
style
.
backgroundColor
;
...
...
@@ -182,16 +197,19 @@ import android.util.Log;
textPaint
.
setTextSize
(
textSizePx
);
int
textPaddingX
=
(
int
)
(
textSizePx
*
INNER_PADDING_RATIO
+
0.5f
);
int
availableWidth
=
parentWidth
-
textPaddingX
*
2
;
if
(
cueSize
!=
Cue
.
DIMEN_UNSET
)
{
availableWidth
=
(
int
)
(
availableWidth
*
cueSize
);
}
if
(
availableWidth
<=
0
)
{
Log
.
w
(
TAG
,
"Skipped drawing subtitle cue (insufficient space)"
);
return
;
}
Alignment
layoutAlignment
=
cueAlignment
==
null
?
Alignment
.
ALIGN_CENTER
:
cue
Alignment
;
textLayout
=
new
StaticLayout
(
cueText
,
textPaint
,
availableWidth
,
layou
tAlignment
,
spacingMult
,
Alignment
textAlignment
=
cueTextAlignment
==
null
?
Alignment
.
ALIGN_CENTER
:
cueText
Alignment
;
textLayout
=
new
StaticLayout
(
cueText
,
textPaint
,
availableWidth
,
tex
tAlignment
,
spacingMult
,
spacingAdd
,
true
);
int
textHeight
=
textLayout
.
getHeight
();
int
textWidth
=
0
;
int
lineCount
=
textLayout
.
getLineCount
();
...
...
@@ -202,14 +220,13 @@ import android.util.Log;
int
textLeft
;
int
textRight
;
if
(
cue
.
position
!=
Cue
.
UNSET_VALUE
)
{
if
(
cue
.
alignment
==
Alignment
.
ALIGN_OPPOSITE
)
{
textRight
=
(
parentWidth
*
cue
.
position
)
/
100
+
parentLeft
;
textLeft
=
Math
.
max
(
textRight
-
textWidth
,
parentLeft
);
}
else
{
textLeft
=
(
parentWidth
*
cue
.
position
)
/
100
+
parentLeft
;
textRight
=
Math
.
min
(
textLeft
+
textWidth
,
parentRight
);
}
if
(
cuePosition
!=
Cue
.
DIMEN_UNSET
)
{
int
anchorPosition
=
Math
.
round
(
parentWidth
*
cuePosition
)
+
parentLeft
;
textLeft
=
cuePositionAnchor
==
Cue
.
ANCHOR_TYPE_END
?
anchorPosition
-
textWidth
:
cuePositionAnchor
==
Cue
.
ANCHOR_TYPE_MIDDLE
?
(
anchorPosition
*
2
-
textWidth
)
/
2
:
anchorPosition
;
textLeft
=
Math
.
max
(
textLeft
,
parentLeft
);
textRight
=
Math
.
min
(
textLeft
+
textWidth
,
parentRight
);
}
else
{
textLeft
=
(
parentWidth
-
textWidth
)
/
2
;
textRight
=
textLeft
+
textWidth
;
...
...
@@ -217,12 +234,29 @@ import android.util.Log;
int
textTop
;
int
textBottom
;
if
(
cue
.
line
!=
Cue
.
UNSET_VALUE
)
{
textTop
=
(
parentHeight
*
cue
.
line
)
/
100
+
parentTop
;
if
(
cueLine
!=
Cue
.
DIMEN_UNSET
)
{
int
anchorPosition
;
if
(
cueLineType
==
Cue
.
LINE_TYPE_FRACTION
)
{
anchorPosition
=
Math
.
round
(
parentHeight
*
cueLine
)
+
parentTop
;
}
else
{
// cueLineType == Cue.LINE_TYPE_NUMBER
int
firstLineHeight
=
textLayout
.
getLineBottom
(
0
)
-
textLayout
.
getLineTop
(
0
);
if
(
cueLine
>=
0
)
{
anchorPosition
=
Math
.
round
(
cueLine
*
firstLineHeight
)
+
parentTop
;
}
else
{
anchorPosition
=
Math
.
round
(
cueLine
*
firstLineHeight
)
+
parentBottom
;
}
}
textTop
=
cueLineAnchor
==
Cue
.
ANCHOR_TYPE_END
?
anchorPosition
-
textHeight
:
cueLineAnchor
==
Cue
.
ANCHOR_TYPE_MIDDLE
?
(
anchorPosition
*
2
-
textHeight
)
/
2
:
anchorPosition
;
textBottom
=
textTop
+
textHeight
;
if
(
textBottom
>
parentBottom
)
{
textTop
=
parentBottom
-
textHeight
;
textBottom
=
parentBottom
;
}
else
if
(
textTop
<
parentTop
)
{
textTop
=
parentTop
;
textBottom
=
parentTop
+
textHeight
;
}
}
else
{
textTop
=
parentBottom
-
textHeight
-
(
int
)
(
parentHeight
*
bottomPaddingFraction
);
...
...
@@ -232,7 +266,7 @@ import android.util.Log;
textWidth
=
textRight
-
textLeft
;
// Update the derived drawing variables.
this
.
textLayout
=
new
StaticLayout
(
cueText
,
textPaint
,
textWidth
,
layou
tAlignment
,
spacingMult
,
this
.
textLayout
=
new
StaticLayout
(
cueText
,
textPaint
,
textWidth
,
tex
tAlignment
,
spacingMult
,
spacingAdd
,
true
);
this
.
textLeft
=
textLeft
;
this
.
textTop
=
textTop
;
...
...
library/src/main/java/com/google/android/exoplayer/text/SubtitleLayout.java
View file @
e4e02f91
...
...
@@ -38,7 +38,7 @@ public final class SubtitleLayout extends View {
public
static
final
float
DEFAULT_TEXT_SIZE_FRACTION
=
0.0533f
;
/**
* The default bottom padding to apply when {@link Cue#line} is {@link Cue#
UNSET_VALUE
}, as a
* The default bottom padding to apply when {@link Cue#line} is {@link Cue#
DIMEN_UNSET
}, as a
* fraction of the viewport height.
*
* @see #setBottomPaddingFraction(float)
...
...
@@ -174,7 +174,7 @@ public final class SubtitleLayout extends View {
}
/**
* Sets the bottom padding fraction to apply when {@link Cue#line} is {@link Cue#
UNSET_VALUE
},
* Sets the bottom padding fraction to apply when {@link Cue#line} is {@link Cue#
DIMEN_UNSET
},
* as a fraction of the view's remaining height after its top and bottom padding have been
* subtracted.
* <p>
...
...
library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttCue.java
View file @
e4e02f91
...
...
@@ -28,16 +28,17 @@ import android.text.Layout.Alignment;
public
final
long
endTime
;
public
WebvttCue
(
CharSequence
text
)
{
this
(
Cue
.
UNSET_VALUE
,
Cue
.
UNSET_VALUE
,
text
);
this
(
0
,
0
,
text
);
}
public
WebvttCue
(
long
startTime
,
long
endTime
,
CharSequence
text
)
{
this
(
startTime
,
endTime
,
text
,
Cue
.
UNSET_VALUE
,
Cue
.
UNSET_VALUE
,
null
,
Cue
.
UNSET_VALUE
);
this
(
startTime
,
endTime
,
text
,
null
,
Cue
.
DIMEN_UNSET
,
Cue
.
TYPE_UNSET
,
Cue
.
TYPE_UNSET
,
Cue
.
DIMEN_UNSET
,
Cue
.
TYPE_UNSET
,
Cue
.
DIMEN_UNSET
);
}
public
WebvttCue
(
long
startTime
,
long
endTime
,
CharSequence
text
,
int
line
,
int
position
,
Alignment
alignment
,
int
size
)
{
super
(
text
,
line
,
position
,
alignment
,
size
);
public
WebvttCue
(
long
startTime
,
long
endTime
,
CharSequence
text
,
Alignment
textAlignment
,
float
line
,
int
lineType
,
int
lineAnchor
,
float
position
,
int
positionAnchor
,
float
width
)
{
super
(
text
,
textAlignment
,
line
,
lineType
,
lineAnchor
,
position
,
positionAnchor
,
width
);
this
.
startTime
=
startTime
;
this
.
endTime
=
endTime
;
}
...
...
@@ -49,7 +50,7 @@ import android.text.Layout.Alignment;
* @return True if this cue should be placed in the default position; false otherwise.
*/
public
boolean
isNormalCue
()
{
return
(
line
==
UNSET_VALUE
&&
position
==
UNSET_VALUE
);
return
(
line
==
DIMEN_UNSET
&&
position
==
DIMEN_UNSET
);
}
}
library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttParser.java
View file @
e4e02f91
...
...
@@ -24,7 +24,6 @@ import com.google.android.exoplayer.util.MimeTypes;
import
android.text.Html
;
import
android.text.Layout.Alignment
;
import
android.util.Log
;
import
android.util.Pair
;
import
java.io.BufferedReader
;
import
java.io.IOException
;
...
...
@@ -43,34 +42,15 @@ public final class WebvttParser implements SubtitleParser {
private
static
final
String
TAG
=
"WebvttParser"
;
private
static
final
String
WEBVTT_FILE_HEADER_STRING
=
"^\uFEFF?WEBVTT((\u0020|\u0009).*)?$"
;
private
static
final
Pattern
WEBVTT_FILE_HEADER
=
Pattern
.
compile
(
WEBVTT_FILE_HEADER_STRING
);
private
static
final
String
WEBVTT_COMMENT_BLOCK_STRING
=
"^NOTE((\u0020|\u0009).*)?$"
;
private
static
final
Pattern
WEBVTT_COMMENT_BLOCK
=
Pattern
.
compile
(
WEBVTT_COMMENT_BLOCK_STRING
);
private
static
final
String
WEBVTT_METADATA_HEADER_STRING
=
"\\S*[:=]\\S*"
;
private
static
final
Pattern
WEBVTT_METADATA_HEADER
=
Pattern
.
compile
(
WEBVTT_METADATA_HEADER_STRING
);
private
static
final
String
WEBVTT_CUE_IDENTIFIER_STRING
=
"^(?!.*(-->)).*$"
;
private
static
final
Pattern
WEBVTT_CUE_IDENTIFIER
=
Pattern
.
compile
(
WEBVTT_CUE_IDENTIFIER_STRING
);
private
static
final
String
WEBVTT_TIMESTAMP_STRING
=
"(\\d+:)?[0-5]\\d:[0-5]\\d\\.\\d{3}"
;
private
static
final
Pattern
WEBVTT_TIMESTAMP
=
Pattern
.
compile
(
WEBVTT_TIMESTAMP_STRING
);
private
static
final
String
WEBVTT_CUE_SETTING_STRING
=
"\\S*:\\S*"
;
private
static
final
Pattern
WEBVTT_CUE_SETTING
=
Pattern
.
compile
(
WEBVTT_CUE_SETTING_STRING
);
private
static
final
String
WEBVTT_PERCENTAGE_NUMBER_STRING
=
"^([0-9]+|[0-9]+\\.[0-9]+)$"
;
private
static
final
String
NON_NUMERIC_STRING
=
".*[^0-9].*"
;
private
static
final
Pattern
HEADER
=
Pattern
.
compile
(
"^\uFEFF?WEBVTT((\u0020|\u0009).*)?$"
);
private
static
final
Pattern
COMMENT_BLOCK
=
Pattern
.
compile
(
"^NOTE((\u0020|\u0009).*)?$"
);
private
static
final
Pattern
METADATA_HEADER
=
Pattern
.
compile
(
"\\S*[:=]\\S*"
);
private
static
final
Pattern
CUE_IDENTIFIER
=
Pattern
.
compile
(
"^(?!.*(-->)).*$"
);
private
static
final
Pattern
TIMESTAMP
=
Pattern
.
compile
(
"(\\d+:)?[0-5]\\d:[0-5]\\d\\.\\d{3}"
);
private
static
final
Pattern
CUE_SETTING
=
Pattern
.
compile
(
"\\S*:\\S*"
);
private
final
PositionHolder
positionHolder
;
private
final
StringBuilder
textBuilder
;
private
final
boolean
strictParsing
;
/**
...
...
@@ -88,6 +68,7 @@ public final class WebvttParser implements SubtitleParser {
*/
public
WebvttParser
(
boolean
strictParsing
)
{
this
.
strictParsing
=
strictParsing
;
positionHolder
=
new
PositionHolder
();
textBuilder
=
new
StringBuilder
();
}
...
...
@@ -100,7 +81,7 @@ public final class WebvttParser implements SubtitleParser {
// file should start with "WEBVTT"
line
=
webvttData
.
readLine
();
if
(
line
==
null
||
!
WEBVTT_FILE_
HEADER
.
matcher
(
line
).
matches
())
{
if
(
line
==
null
||
!
HEADER
.
matcher
(
line
).
matches
())
{
throw
new
ParserException
(
"Expected WEBVTT. Got "
+
line
);
}
...
...
@@ -116,7 +97,7 @@ public final class WebvttParser implements SubtitleParser {
}
if
(
strictParsing
)
{
Matcher
matcher
=
WEBVTT_
METADATA_HEADER
.
matcher
(
line
);
Matcher
matcher
=
METADATA_HEADER
.
matcher
(
line
);
if
(!
matcher
.
find
())
{
throw
new
ParserException
(
"Unexpected line: "
+
line
);
}
...
...
@@ -126,38 +107,45 @@ public final class WebvttParser implements SubtitleParser {
// process the cues and text
while
((
line
=
webvttData
.
readLine
())
!=
null
)
{
// parse webvtt comment block in case it is present
Matcher
matcher
=
WEBVTT_
COMMENT_BLOCK
.
matcher
(
line
);
if
(
matcher
.
find
())
{
Matcher
matcher
=
COMMENT_BLOCK
.
matcher
(
line
);
if
(
matcher
.
find
())
{
// read lines until finding an empty one (webvtt line terminator: CRLF, or LF or CR)
while
((
(
line
=
webvttData
.
readLine
())
!=
null
)
&&
(!
line
.
isEmpty
()
))
{
//
just ignoring
comment text
while
((
line
=
webvttData
.
readLine
())
!=
null
&&
!
line
.
isEmpty
(
))
{
//
ignore
comment text
}
continue
;
}
// parse the cue identifier (if present) {
matcher
=
WEBVTT_
CUE_IDENTIFIER
.
matcher
(
line
);
matcher
=
CUE_IDENTIFIER
.
matcher
(
line
);
if
(
matcher
.
find
())
{
// ignore the identifier (we currently don't use it) and read the next line
line
=
webvttData
.
readLine
();
if
(
line
==
null
)
{
// end of file
break
;
}
}
long
startTime
=
Cue
.
UNSET_VALUE
;
long
endTime
=
Cue
.
UNSET_VALUE
;
CharSequence
text
=
null
;
int
lineNum
=
Cue
.
UNSET_VALUE
;
int
position
=
Cue
.
UNSET_VALUE
;
Alignment
alignment
=
null
;
int
size
=
Cue
.
UNSET_VALUE
;
long
cueStartTime
;
long
cueEndTime
;
CharSequence
cueText
;
Alignment
cueTextAlignment
=
null
;
float
cueLine
=
Cue
.
DIMEN_UNSET
;
int
cueLineType
=
Cue
.
TYPE_UNSET
;
int
cueLineAnchor
=
Cue
.
TYPE_UNSET
;
float
cuePosition
=
Cue
.
DIMEN_UNSET
;
int
cuePositionAnchor
=
Cue
.
TYPE_UNSET
;
float
cueWidth
=
Cue
.
DIMEN_UNSET
;
// parse the cue timestamps
matcher
=
WEBVTT_
TIMESTAMP
.
matcher
(
line
);
matcher
=
TIMESTAMP
.
matcher
(
line
);
// parse start timestamp
if
(!
matcher
.
find
())
{
throw
new
ParserException
(
"Expected cue start time: "
+
line
);
}
else
{
s
tartTime
=
parseTimestampUs
(
matcher
.
group
());
cueS
tartTime
=
parseTimestampUs
(
matcher
.
group
());
}
// parse end timestamp
...
...
@@ -166,12 +154,12 @@ public final class WebvttParser implements SubtitleParser {
throw
new
ParserException
(
"Expected cue end time: "
+
line
);
}
else
{
endTimeString
=
matcher
.
group
();
e
ndTime
=
parseTimestampUs
(
endTimeString
);
cueE
ndTime
=
parseTimestampUs
(
endTimeString
);
}
// parse the (optional) cue setting list
line
=
line
.
substring
(
line
.
indexOf
(
endTimeString
)
+
endTimeString
.
length
());
matcher
=
WEBVTT_
CUE_SETTING
.
matcher
(
line
);
matcher
=
CUE_SETTING
.
matcher
(
line
);
while
(
matcher
.
find
())
{
String
match
=
matcher
.
group
();
String
[]
parts
=
match
.
split
(
":"
,
2
);
...
...
@@ -180,52 +168,44 @@ public final class WebvttParser implements SubtitleParser {
try
{
if
(
"line"
.
equals
(
name
))
{
Pair
<
String
,
Alignment
>
lineMetadata
=
parseLinePositionAttributes
(
value
);
value
=
lineMetadata
.
first
;
if
(
value
.
endsWith
(
"%"
))
{
lineNum
=
parseIntPercentage
(
value
);
}
else
{
// Following WebVTT spec, line number can be a negative number
int
sign
=
1
;
if
(
value
.
startsWith
(
"-"
)
&&
value
.
length
()
>
1
)
{
sign
=
-
1
;
value
=
value
.
substring
(
1
);
}
if
(
value
.
matches
(
NON_NUMERIC_STRING
))
{
Log
.
w
(
TAG
,
"Invalid line value: "
+
value
);
}
else
{
lineNum
=
sign
*
Integer
.
parseInt
(
value
);
}
}
parseLineAttribute
(
value
,
positionHolder
);
cueLine
=
positionHolder
.
position
;
cueLineType
=
positionHolder
.
lineType
;
cueLineAnchor
=
positionHolder
.
positionAnchor
;
}
else
if
(
"align"
.
equals
(
name
))
{
// TODO: handle for RTL languages
alignment
=
parseAlignment
(
value
);
cueTextAlignment
=
parseTextAlignment
(
value
);
}
else
if
(
"position"
.
equals
(
name
))
{
Pair
<
String
,
Alignment
>
lineMetadata
=
parseLinePositionAttributes
(
value
);
value
=
lineMetadata
.
first
;
position
=
parseIntPercentage
(
value
)
;
parsePositionAttribute
(
value
,
positionHolder
);
cuePosition
=
positionHolder
.
position
;
cuePositionAnchor
=
positionHolder
.
positionAnchor
;
}
else
if
(
"size"
.
equals
(
name
))
{
size
=
parseInt
Percentage
(
value
);
cueWidth
=
parse
Percentage
(
value
);
}
else
{
Log
.
w
(
TAG
,
"Unknown cue setting "
+
name
+
":"
+
value
);
}
}
catch
(
NumberFormatException
e
)
{
Log
.
w
(
TAG
,
name
+
" contains an invalid value "
+
value
,
e
);
Log
.
w
(
TAG
,
e
.
getMessage
()
+
": "
+
match
);
}
}
if
(
cuePosition
!=
Cue
.
DIMEN_UNSET
&&
cuePositionAnchor
==
Cue
.
TYPE_UNSET
)
{
// Computed position alignment should be derived from the text alignment if it has not been
// set explicitly.
cuePositionAnchor
=
alignmentToAnchor
(
cueTextAlignment
);
}
// parse text
textBuilder
.
setLength
(
0
);
while
((
(
line
=
webvttData
.
readLine
())
!=
null
)
&&
(!
line
.
isEmpty
()
))
{
while
((
line
=
webvttData
.
readLine
())
!=
null
&&
!
line
.
isEmpty
(
))
{
if
(
textBuilder
.
length
()
>
0
)
{
textBuilder
.
append
(
"<br>"
);
}
textBuilder
.
append
(
line
.
trim
());
}
t
ext
=
Html
.
fromHtml
(
textBuilder
.
toString
());
cueT
ext
=
Html
.
fromHtml
(
textBuilder
.
toString
());
WebvttCue
cue
=
new
WebvttCue
(
startTime
,
endTime
,
text
,
lineNum
,
position
,
alignment
,
size
);
WebvttCue
cue
=
new
WebvttCue
(
cueStartTime
,
cueEndTime
,
cueText
,
cueTextAlignment
,
cueLine
,
cueLineType
,
cueLineAnchor
,
cuePosition
,
cuePositionAnchor
,
cueWidth
);
subtitles
.
add
(
cue
);
}
...
...
@@ -237,67 +217,116 @@ public final class WebvttParser implements SubtitleParser {
return
MimeTypes
.
TEXT_VTT
.
equals
(
mimeType
);
}
private
static
int
parseIntPercentage
(
String
s
)
throws
NumberFormatException
{
if
(!
s
.
endsWith
(
"%"
))
{
throw
new
NumberFormatException
(
s
+
" doesn't end with '%'"
);
private
static
long
parseTimestampUs
(
String
s
)
throws
NumberFormatException
{
long
value
=
0
;
String
[]
parts
=
s
.
split
(
"\\."
,
2
);
String
[]
subparts
=
parts
[
0
].
split
(
":"
);
for
(
int
i
=
0
;
i
<
subparts
.
length
;
i
++)
{
value
=
value
*
60
+
Long
.
parseLong
(
subparts
[
i
]);
}
return
(
value
*
1000
+
Long
.
parseLong
(
parts
[
1
]))
*
1000
;
}
s
=
s
.
substring
(
0
,
s
.
length
()
-
1
);
if
(!
s
.
matches
(
WEBVTT_PERCENTAGE_NUMBER_STRING
))
{
throw
new
NumberFormatException
(
s
+
" contains an invalid character"
);
private
static
void
parseLineAttribute
(
String
s
,
PositionHolder
out
)
throws
NumberFormatException
{
int
lineAnchor
;
int
commaPosition
=
s
.
indexOf
(
","
);
if
(
commaPosition
!=
-
1
)
{
lineAnchor
=
parsePositionAnchor
(
s
.
substring
(
commaPosition
+
1
));
s
=
s
.
substring
(
0
,
commaPosition
);
}
else
{
lineAnchor
=
Cue
.
TYPE_UNSET
;
}
int
value
=
Math
.
round
(
Float
.
parseFloat
(
s
));
if
(
value
<
0
||
value
>
100
)
{
throw
new
NumberFormatException
(
value
+
" is out of range [0-100]"
);
float
line
;
int
lineType
;
if
(
s
.
endsWith
(
"%"
))
{
line
=
parsePercentage
(
s
);
lineType
=
Cue
.
LINE_TYPE_FRACTION
;
}
else
{
line
=
Integer
.
parseInt
(
s
);
lineType
=
Cue
.
LINE_TYPE_NUMBER
;
}
return
value
;
out
.
position
=
line
;
out
.
positionAnchor
=
lineAnchor
;
out
.
lineType
=
lineType
;
}
private
static
long
parseTimestampUs
(
String
s
)
throws
NumberFormatException
{
if
(!
s
.
matches
(
WEBVTT_TIMESTAMP_STRING
))
{
throw
new
NumberFormatException
(
"has invalid format"
);
private
static
void
parsePositionAttribute
(
String
s
,
PositionHolder
out
)
throws
NumberFormatException
{
int
positionAnchor
;
int
commaPosition
=
s
.
indexOf
(
","
);
if
(
commaPosition
!=
-
1
)
{
positionAnchor
=
parsePositionAnchor
(
s
.
substring
(
commaPosition
+
1
));
s
=
s
.
substring
(
0
,
commaPosition
);
}
else
{
positionAnchor
=
Cue
.
TYPE_UNSET
;
}
out
.
position
=
parsePercentage
(
s
);
out
.
positionAnchor
=
positionAnchor
;
out
.
lineType
=
Cue
.
TYPE_UNSET
;
}
String
[]
parts
=
s
.
split
(
"\\."
,
2
);
long
value
=
0
;
for
(
String
group
:
parts
[
0
].
split
(
":"
))
{
value
=
value
*
60
+
Long
.
parseLong
(
group
);
private
static
float
parsePercentage
(
String
s
)
throws
NumberFormatException
{
if
(!
s
.
endsWith
(
"%"
))
{
throw
new
NumberFormatException
(
"Percentages must end with %"
);
}
return
(
value
*
1000
+
Long
.
parseLong
(
parts
[
1
]))
*
1000
;
s
=
s
.
substring
(
0
,
s
.
length
()
-
1
);
return
Float
.
parseFloat
(
s
)
/
100
;
}
private
static
Pair
<
String
,
Alignment
>
parseLinePositionAttributes
(
String
s
)
{
String
value
;
Alignment
alignment
=
null
;
int
commaPos
;
if
((
commaPos
=
s
.
indexOf
(
","
))
>
0
&&
commaPos
<
s
.
length
()
-
1
)
{
alignment
=
parseAlignment
(
s
.
substring
(
commaPos
+
1
));
value
=
s
.
substring
(
0
,
commaPos
);
}
else
{
value
=
s
;
private
static
int
parsePositionAnchor
(
String
s
)
{
switch
(
s
)
{
case
"start"
:
return
Cue
.
ANCHOR_TYPE_START
;
case
"middle"
:
return
Cue
.
ANCHOR_TYPE_MIDDLE
;
case
"end"
:
return
Cue
.
ANCHOR_TYPE_END
;
default
:
Log
.
w
(
TAG
,
"Invalid anchor value: "
+
s
);
return
Cue
.
TYPE_UNSET
;
}
}
return
new
Pair
<
String
,
Alignment
>(
value
,
alignment
);
private
static
Alignment
parseTextAlignment
(
String
s
)
{
switch
(
s
)
{
case
"start"
:
case
"left"
:
return
Alignment
.
ALIGN_NORMAL
;
case
"middle"
:
return
Alignment
.
ALIGN_CENTER
;
case
"end"
:
case
"right"
:
return
Alignment
.
ALIGN_OPPOSITE
;
default
:
Log
.
w
(
TAG
,
"Invalid alignment value: "
+
s
);
return
null
;
}
}
private
static
Alignment
parseAlignment
(
String
s
)
{
Alignment
alignment
=
null
;
if
(
"start"
.
equals
(
s
))
{
alignment
=
Alignment
.
ALIGN_NORMAL
;
}
else
if
(
"middle"
.
equals
(
s
))
{
alignment
=
Alignment
.
ALIGN_CENTER
;
}
else
if
(
"end"
.
equals
(
s
))
{
alignment
=
Alignment
.
ALIGN_OPPOSITE
;
}
else
if
(
"left"
.
equals
(
s
))
{
alignment
=
Alignment
.
ALIGN_NORMAL
;
}
else
if
(
"right"
.
equals
(
s
))
{
alignment
=
Alignment
.
ALIGN_OPPOSITE
;
}
else
{
Log
.
w
(
TAG
,
"Invalid align value: "
+
s
);
private
static
int
alignmentToAnchor
(
Alignment
alignment
)
{
if
(
alignment
==
null
)
{
return
Cue
.
TYPE_UNSET
;
}
return
alignment
;
switch
(
alignment
)
{
case
ALIGN_NORMAL:
return
Cue
.
ANCHOR_TYPE_START
;
case
ALIGN_CENTER:
return
Cue
.
ANCHOR_TYPE_MIDDLE
;
case
ALIGN_OPPOSITE:
return
Cue
.
ANCHOR_TYPE_END
;
default
:
Log
.
w
(
TAG
,
"Unrecognized alignment: "
+
alignment
);
return
Cue
.
ANCHOR_TYPE_START
;
}
}
private
static
final
class
PositionHolder
{
public
float
position
;
public
int
positionAnchor
;
public
int
lineType
;
}
}
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