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
fbbf3f27
authored
Jun 10, 2015
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Simplify Subrip support
parent
71252784
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
61 additions
and
150 deletions
library/src/main/java/com/google/android/exoplayer/text/subrip/SubripCue.java
library/src/main/java/com/google/android/exoplayer/text/subrip/SubripParser.java
library/src/main/java/com/google/android/exoplayer/text/subrip/SubripSubtitle.java
library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttSubtitle.java
library/src/main/java/com/google/android/exoplayer/text/subrip/SubripCue.java
View file @
fbbf3f27
...
@@ -25,24 +25,10 @@ import com.google.android.exoplayer.text.Cue;
...
@@ -25,24 +25,10 @@ import com.google.android.exoplayer.text.Cue;
public
final
long
startTime
;
public
final
long
startTime
;
public
final
long
endTime
;
public
final
long
endTime
;
public
SubripCue
(
CharSequence
text
)
{
public
SubripCue
(
long
startTime
,
long
endTime
,
CharSequence
text
)
{
this
(
Cue
.
UNSET_VALUE
,
Cue
.
UNSET_VALUE
,
Cue
.
UNSET_VALUE
,
text
);
super
(
text
);
}
public
SubripCue
(
long
startTime
,
long
endTime
,
int
position
,
CharSequence
text
)
{
super
(
text
,
Cue
.
UNSET_VALUE
,
position
,
null
,
Cue
.
UNSET_VALUE
);
this
.
startTime
=
startTime
;
this
.
startTime
=
startTime
;
this
.
endTime
=
endTime
;
this
.
endTime
=
endTime
;
}
}
/**
* Returns whether or not this cue should be placed in the default position and rolled-up with
* the other "normal" cues.
*
* @return True if this cue should be placed in the default position; false otherwise.
*/
public
boolean
isNormalCue
()
{
return
(
line
==
UNSET_VALUE
&&
position
==
UNSET_VALUE
);
}
}
}
library/src/main/java/com/google/android/exoplayer/text/subrip/SubripParser.java
View file @
fbbf3f27
...
@@ -15,14 +15,15 @@
...
@@ -15,14 +15,15 @@
*/
*/
package
com
.
google
.
android
.
exoplayer
.
text
.
subrip
;
package
com
.
google
.
android
.
exoplayer
.
text
.
subrip
;
import
android.text.Html
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.C
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.ParserException
;
import
com.google.android.exoplayer.text.Cue
;
import
com.google.android.exoplayer.text.SubtitleParser
;
import
com.google.android.exoplayer.text.SubtitleParser
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
com.google.android.exoplayer.util.MimeTypes
;
import
android.text.Html
;
import
android.text.Spanned
;
import
android.text.TextUtils
;
import
java.io.BufferedReader
;
import
java.io.BufferedReader
;
import
java.io.IOException
;
import
java.io.IOException
;
import
java.io.InputStream
;
import
java.io.InputStream
;
...
@@ -32,93 +33,66 @@ import java.util.regex.Matcher;
...
@@ -32,93 +33,66 @@ import java.util.regex.Matcher;
import
java.util.regex.Pattern
;
import
java.util.regex.Pattern
;
/**
/**
* A simple S
RT
parser.
* A simple S
ubRip
parser.
* <p/>
* <p/>
*
* @see <a href="https://en.wikipedia.org/wiki/SubRip">Wikipedia on SubRip</a>
* @see <a href="https://en.wikipedia.org/wiki/SubRip">Wikipedia on SRT</a>
*/
*/
public
final
class
SubripParser
implements
SubtitleParser
{
public
final
class
SubripParser
implements
SubtitleParser
{
private
static
final
String
TAG
=
"SubRipParser"
;
private
static
final
Pattern
SUBRIP_TIMING_LINE
=
Pattern
.
compile
(
"(.*)\\s+-->\\s+(.*)"
);
private
static
final
Pattern
SUBRIP_TIMESTAMP
=
private
static
final
String
SUBRIP_POSITION_STRING
=
"^(\\d)$"
;
Pattern
.
compile
(
"(?:(\\d+):)?(\\d+):(\\d+),(\\d+)"
);
private
static
final
Pattern
SUBRIP_POSITION
=
Pattern
.
compile
(
SUBRIP_POSITION_STRING
);
private
static
final
String
SUBRIP_CUE_IDENTIFIER_STRING
=
"^(.*)\\s-->\\s(.*)$"
;
private
static
final
Pattern
SUBRIP_CUE_IDENTIFIER
=
Pattern
.
compile
(
SUBRIP_CUE_IDENTIFIER_STRING
);
private
static
final
String
SUBRIP_TIMESTAMP_STRING
=
"(\\d+:)?[0-5]\\d:[0-5]\\d:[0-5]\\d,\\d{3}"
;
// private static final Pattern SUBRIP_TIMESTAMP = Pattern.compile(SUBRIP_TIMESTAMP_STRING);
private
final
StringBuilder
textBuilder
;
private
final
StringBuilder
textBuilder
;
private
final
boolean
strictParsing
;
public
SubripParser
()
{
public
SubripParser
()
{
this
(
true
);
}
public
SubripParser
(
boolean
strictParsing
)
{
this
.
strictParsing
=
strictParsing
;
textBuilder
=
new
StringBuilder
();
textBuilder
=
new
StringBuilder
();
}
}
@Override
@Override
public
SubripSubtitle
parse
(
InputStream
inputStream
,
String
inputEncoding
,
long
startTimeUs
)
public
SubripSubtitle
parse
(
InputStream
inputStream
,
String
inputEncoding
,
long
startTimeUs
)
throws
IOException
{
throws
IOException
{
ArrayList
<
SubripCue
>
subtitles
=
new
ArrayList
<>();
ArrayList
<
SubripCue
>
cues
=
new
ArrayList
<>();
BufferedReader
reader
=
new
BufferedReader
(
new
InputStreamReader
(
inputStream
,
C
.
UTF8_NAME
));
// file should not be empty
String
currentLine
;
if
(
inputStream
.
available
()
==
0
)
{
throw
new
ParserException
(
"File is empty?"
);
while
((
currentLine
=
reader
.
readLine
())
!=
null
)
{
}
// Parse the numeric counter as a sanity check.
try
{
BufferedReader
subripData
=
new
BufferedReader
(
new
InputStreamReader
(
inputStream
,
C
.
UTF8_NAME
));
Integer
.
parseInt
(
currentLine
);
String
line
;
}
catch
(
NumberFormatException
e
)
{
throw
new
ParserException
(
"Expected numeric counter: "
+
currentLine
);
// process the cues and text
while
((
line
=
subripData
.
readLine
())
!=
null
)
{
long
startTime
=
Cue
.
UNSET_VALUE
;
long
endTime
=
Cue
.
UNSET_VALUE
;
CharSequence
text
=
null
;
int
position
=
Cue
.
UNSET_VALUE
;
Matcher
matcher
=
SUBRIP_POSITION
.
matcher
(
line
);
if
(
matcher
.
matches
())
{
position
=
Integer
.
parseInt
(
matcher
.
group
());
}
}
line
=
subripData
.
readLine
();
// Read and parse the timing line.
long
cueStartTimeUs
;
// parse cue time
long
cueEndTimeUs
;
matcher
=
SUBRIP_CUE_IDENTIFIER
.
matcher
(
line
);
currentLine
=
reader
.
readLine
();
if
(!
matcher
.
find
())
{
Matcher
matcher
=
SUBRIP_TIMING_LINE
.
matcher
(
currentLine
);
throw
new
ParserException
(
"Expected cue start time: "
+
line
);
if
(
matcher
.
find
())
{
cueStartTimeUs
=
parseTimestampUs
(
matcher
.
group
(
1
))
+
startTimeUs
;
cueEndTimeUs
=
parseTimestampUs
(
matcher
.
group
(
2
))
+
startTimeUs
;
}
else
{
}
else
{
startTime
=
parseTimestampUs
(
matcher
.
group
(
1
))
+
startTimeUs
;
throw
new
ParserException
(
"Expected timing line: "
+
currentLine
);
endTime
=
parseTimestampUs
(
matcher
.
group
(
2
))
+
startTimeUs
;
}
}
//
parse text
//
Read and parse the text.
textBuilder
.
setLength
(
0
);
textBuilder
.
setLength
(
0
);
while
(
((
line
=
subripData
.
readLine
())
!=
null
)
&&
(!
line
.
isEmpty
()))
{
while
(
!
TextUtils
.
isEmpty
(
currentLine
=
reader
.
readLine
()))
{
if
(
textBuilder
.
length
()
>
0
)
{
if
(
textBuilder
.
length
()
>
0
)
{
textBuilder
.
append
(
"<br>"
);
textBuilder
.
append
(
"<br>"
);
}
}
textBuilder
.
append
(
l
ine
.
trim
());
textBuilder
.
append
(
currentL
ine
.
trim
());
}
}
text
=
Html
.
fromHtml
(
textBuilder
.
toString
());
SubripCue
cue
=
new
SubripCue
(
startTime
,
endTime
,
position
,
text
);
Spanned
text
=
Html
.
fromHtml
(
textBuilder
.
toString
());
subtitles
.
add
(
cue
);
SubripCue
cue
=
new
SubripCue
(
cueStartTimeUs
,
cueEndTimeUs
,
text
);
cues
.
add
(
cue
);
}
}
subripData
.
close
();
reader
.
close
();
inputStream
.
close
();
inputStream
.
close
();
SubripSubtitle
subtitle
=
new
SubripSubtitle
(
subtitl
es
,
startTimeUs
);
SubripSubtitle
subtitle
=
new
SubripSubtitle
(
cu
es
,
startTimeUs
);
return
subtitle
;
return
subtitle
;
}
}
...
@@ -127,23 +101,16 @@ public final class SubripParser implements SubtitleParser {
...
@@ -127,23 +101,16 @@ public final class SubripParser implements SubtitleParser {
return
MimeTypes
.
APPLICATION_SUBRIP
.
equals
(
mimeType
);
return
MimeTypes
.
APPLICATION_SUBRIP
.
equals
(
mimeType
);
}
}
private
void
handleNoncompliantLine
(
String
line
)
throws
ParserException
{
if
(
strictParsing
)
{
throw
new
ParserException
(
"Unexpected line: "
+
line
);
}
}
private
static
long
parseTimestampUs
(
String
s
)
throws
NumberFormatException
{
private
static
long
parseTimestampUs
(
String
s
)
throws
NumberFormatException
{
if
(!
s
.
matches
(
SUBRIP_TIMESTAMP_STRING
))
{
Matcher
matcher
=
SUBRIP_TIMESTAMP
.
matcher
(
s
);
if
(!
matcher
.
matches
())
{
throw
new
NumberFormatException
(
"has invalid format"
);
throw
new
NumberFormatException
(
"has invalid format"
);
}
}
long
timestampMs
=
Long
.
parseLong
(
matcher
.
group
(
1
))
*
60
*
60
*
1000
;
String
[]
parts
=
s
.
split
(
","
,
2
);
timestampMs
+=
Long
.
parseLong
(
matcher
.
group
(
2
))
*
60
*
1000
;
long
value
=
0
;
timestampMs
+=
Long
.
parseLong
(
matcher
.
group
(
3
))
*
1000
;
for
(
String
group
:
parts
[
0
].
split
(
":"
))
{
timestampMs
+=
Long
.
parseLong
(
matcher
.
group
(
4
));
value
=
value
*
60
+
Long
.
parseLong
(
group
);
return
timestampMs
*
1000
;
}
return
(
value
*
1000
+
Long
.
parseLong
(
parts
[
1
]))
*
1000
;
}
}
}
}
library/src/main/java/com/google/android/exoplayer/text/subrip/SubripSubtitle.java
View file @
fbbf3f27
...
@@ -15,15 +15,11 @@
...
@@ -15,15 +15,11 @@
*/
*/
package
com
.
google
.
android
.
exoplayer
.
text
.
subrip
;
package
com
.
google
.
android
.
exoplayer
.
text
.
subrip
;
import
android.text.SpannableStringBuilder
;
import
com.google.android.exoplayer.text.Cue
;
import
com.google.android.exoplayer.text.Cue
;
import
com.google.android.exoplayer.text.Subtitle
;
import
com.google.android.exoplayer.text.Subtitle
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.Assertions
;
import
com.google.android.exoplayer.util.Util
;
import
com.google.android.exoplayer.util.Util
;
import
java.util.ArrayList
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.Collections
;
import
java.util.List
;
import
java.util.List
;
...
@@ -36,7 +32,6 @@ import java.util.List;
...
@@ -36,7 +32,6 @@ import java.util.List;
private
final
int
numCues
;
private
final
int
numCues
;
private
final
long
startTimeUs
;
private
final
long
startTimeUs
;
private
final
long
[]
cueTimesUs
;
private
final
long
[]
cueTimesUs
;
private
final
long
[]
sortedCueTimesUs
;
/**
/**
* @param cues A list of the cues in this subtitle.
* @param cues A list of the cues in this subtitle.
...
@@ -44,19 +39,16 @@ import java.util.List;
...
@@ -44,19 +39,16 @@ import java.util.List;
*/
*/
public
SubripSubtitle
(
List
<
SubripCue
>
cues
,
long
startTimeUs
)
{
public
SubripSubtitle
(
List
<
SubripCue
>
cues
,
long
startTimeUs
)
{
this
.
cues
=
cues
;
this
.
cues
=
cues
;
numCues
=
cues
.
size
();
this
.
startTimeUs
=
startTimeUs
;
this
.
startTimeUs
=
startTimeUs
;
this
.
cueTimesUs
=
new
long
[
2
*
numCues
];
numCues
=
cues
.
size
();
cueTimesUs
=
new
long
[
2
*
numCues
];
for
(
int
cueIndex
=
0
;
cueIndex
<
numCues
;
cueIndex
++)
{
for
(
int
cueIndex
=
0
;
cueIndex
<
numCues
;
cueIndex
++)
{
SubripCue
cue
=
cues
.
get
(
cueIndex
);
SubripCue
cue
=
cues
.
get
(
cueIndex
);
int
arrayIndex
=
cueIndex
*
2
;
int
arrayIndex
=
cueIndex
*
2
;
cueTimesUs
[
arrayIndex
]
=
cue
.
startTime
;
cueTimesUs
[
arrayIndex
]
=
cue
.
startTime
;
cueTimesUs
[
arrayIndex
+
1
]
=
cue
.
endTime
;
cueTimesUs
[
arrayIndex
+
1
]
=
cue
.
endTime
;
}
}
this
.
sortedCueTimesUs
=
Arrays
.
copyOf
(
cueTimesUs
,
cueTimesUs
.
length
);
Arrays
.
sort
(
sortedCueTimesUs
);
}
}
@Override
@Override
...
@@ -67,20 +59,20 @@ import java.util.List;
...
@@ -67,20 +59,20 @@ import java.util.List;
@Override
@Override
public
int
getNextEventTimeIndex
(
long
timeUs
)
{
public
int
getNextEventTimeIndex
(
long
timeUs
)
{
Assertions
.
checkArgument
(
timeUs
>=
0
);
Assertions
.
checkArgument
(
timeUs
>=
0
);
int
index
=
Util
.
binarySearchCeil
(
sortedC
ueTimesUs
,
timeUs
,
false
,
false
);
int
index
=
Util
.
binarySearchCeil
(
c
ueTimesUs
,
timeUs
,
false
,
false
);
return
index
<
sortedC
ueTimesUs
.
length
?
index
:
-
1
;
return
index
<
c
ueTimesUs
.
length
?
index
:
-
1
;
}
}
@Override
@Override
public
int
getEventTimeCount
()
{
public
int
getEventTimeCount
()
{
return
sortedC
ueTimesUs
.
length
;
return
c
ueTimesUs
.
length
;
}
}
@Override
@Override
public
long
getEventTime
(
int
index
)
{
public
long
getEventTime
(
int
index
)
{
Assertions
.
checkArgument
(
index
>=
0
);
Assertions
.
checkArgument
(
index
>=
0
);
Assertions
.
checkArgument
(
index
<
sortedC
ueTimesUs
.
length
);
Assertions
.
checkArgument
(
index
<
c
ueTimesUs
.
length
);
return
sortedC
ueTimesUs
[
index
];
return
c
ueTimesUs
[
index
];
}
}
@Override
@Override
...
@@ -88,50 +80,17 @@ import java.util.List;
...
@@ -88,50 +80,17 @@ import java.util.List;
if
(
getEventTimeCount
()
==
0
)
{
if
(
getEventTimeCount
()
==
0
)
{
return
-
1
;
return
-
1
;
}
}
return
sortedCueTimesUs
[
sortedC
ueTimesUs
.
length
-
1
];
return
cueTimesUs
[
c
ueTimesUs
.
length
-
1
];
}
}
@Override
@Override
public
List
<
Cue
>
getCues
(
long
timeUs
)
{
public
List
<
Cue
>
getCues
(
long
timeUs
)
{
ArrayList
<
Cue
>
list
=
null
;
int
index
=
Util
.
binarySearchFloor
(
cueTimesUs
,
timeUs
,
true
,
false
);
SubripCue
firstNormalCue
=
null
;
if
(
index
==
-
1
||
index
%
2
==
1
)
{
SpannableStringBuilder
normalCueTextBuilder
=
null
;
// timeUs is earlier than the start of the first cue, or corresponds to a gap between cues.
for
(
int
i
=
0
;
i
<
numCues
;
i
++)
{
if
((
cueTimesUs
[
i
*
2
]
<=
timeUs
)
&&
(
timeUs
<
cueTimesUs
[
i
*
2
+
1
]))
{
if
(
list
==
null
)
{
list
=
new
ArrayList
<>();
}
SubripCue
cue
=
cues
.
get
(
i
);
if
(
cue
.
isNormalCue
())
{
// we want to merge all of the normal cues into a single cue to ensure they are drawn
// correctly (i.e. don't overlap) and to emulate roll-up, but only if there are multiple
// normal cues, otherwise we can just append the single normal cue
if
(
firstNormalCue
==
null
)
{
firstNormalCue
=
cue
;
}
else
if
(
normalCueTextBuilder
==
null
)
{
normalCueTextBuilder
=
new
SpannableStringBuilder
();
normalCueTextBuilder
.
append
(
firstNormalCue
.
text
).
append
(
"\n"
).
append
(
cue
.
text
);
}
else
{
normalCueTextBuilder
.
append
(
"\n"
).
append
(
cue
.
text
);
}
}
else
{
list
.
add
(
cue
);
}
}
}
if
(
normalCueTextBuilder
!=
null
)
{
// there were multiple normal cues, so create a new cue with all of the text
list
.
add
(
new
SubripCue
(
normalCueTextBuilder
));
}
else
if
(
firstNormalCue
!=
null
)
{
// there was only a single normal cue, so just add it to the list
list
.
add
(
firstNormalCue
);
}
if
(
list
!=
null
)
{
return
list
;
}
else
{
return
Collections
.<
Cue
>
emptyList
();
return
Collections
.<
Cue
>
emptyList
();
}
else
{
return
Collections
.
singletonList
((
Cue
)
cues
.
get
(
index
/
2
));
}
}
}
}
...
...
library/src/main/java/com/google/android/exoplayer/text/webvtt/WebvttSubtitle.java
View file @
fbbf3f27
...
@@ -44,18 +44,17 @@ public class WebvttSubtitle implements Subtitle {
...
@@ -44,18 +44,17 @@ public class WebvttSubtitle implements Subtitle {
*/
*/
public
WebvttSubtitle
(
List
<
WebvttCue
>
cues
,
long
startTimeUs
)
{
public
WebvttSubtitle
(
List
<
WebvttCue
>
cues
,
long
startTimeUs
)
{
this
.
cues
=
cues
;
this
.
cues
=
cues
;
numCues
=
cues
.
size
();
this
.
startTimeUs
=
startTimeUs
;
this
.
startTimeUs
=
startTimeUs
;
this
.
cueTimesUs
=
new
long
[
2
*
numCues
];
numCues
=
cues
.
size
();
cueTimesUs
=
new
long
[
2
*
numCues
];
for
(
int
cueIndex
=
0
;
cueIndex
<
numCues
;
cueIndex
++)
{
for
(
int
cueIndex
=
0
;
cueIndex
<
numCues
;
cueIndex
++)
{
WebvttCue
cue
=
cues
.
get
(
cueIndex
);
WebvttCue
cue
=
cues
.
get
(
cueIndex
);
int
arrayIndex
=
cueIndex
*
2
;
int
arrayIndex
=
cueIndex
*
2
;
cueTimesUs
[
arrayIndex
]
=
cue
.
startTime
;
cueTimesUs
[
arrayIndex
]
=
cue
.
startTime
;
cueTimesUs
[
arrayIndex
+
1
]
=
cue
.
endTime
;
cueTimesUs
[
arrayIndex
+
1
]
=
cue
.
endTime
;
}
}
sortedCueTimesUs
=
Arrays
.
copyOf
(
cueTimesUs
,
cueTimesUs
.
length
);
this
.
sortedCueTimesUs
=
Arrays
.
copyOf
(
cueTimesUs
,
cueTimesUs
.
length
);
Arrays
.
sort
(
sortedCueTimesUs
);
Arrays
.
sort
(
sortedCueTimesUs
);
}
}
...
...
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