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
b333169a
authored
Jul 10, 2017
by
Oliver Woodman
Browse files
Options
_('Browse Files')
Download
Plain Diff
Merge branch 'dev-v2' of
git://github.com/jcable/ExoPlayer
into jcable-dev-v2
parents
78e05457
402c985a
Show whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
583 additions
and
44 deletions
library/core/src/androidTest/assets/ssa/empty
library/core/src/androidTest/assets/ssa/invalid_timecodes
library/core/src/androidTest/assets/ssa/no_end_timecodes
library/core/src/androidTest/assets/ssa/typical
library/core/src/androidTest/assets/ssa/typical_dialogue
library/core/src/androidTest/assets/ssa/typical_format
library/core/src/androidTest/assets/ssa/typical_header
library/core/src/androidTest/java/com/google/android/exoplayer2/text/ssa/SsaDecoderTest.java
library/core/src/androidTest/java/com/google/android/exoplayer2/text/subrip/SubripDecoderTest.java
library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java
library/core/src/main/java/com/google/android/exoplayer2/text/SubtitleDecoderFactory.java
library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java
library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaSubtitle.java
library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java
library/core/src/androidTest/assets/ssa/empty
0 → 100644
View file @
b333169a
File mode changed
library/core/src/androidTest/assets/ssa/invalid_timecodes
0 → 100644
View file @
b333169a
[Script Info]
Title: SomeTitle
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Open Sans Semibold,36,&H00FFFFFF,&H000000FF,&H00020713,&H00000000,-1,0,0,0,100,100,0,0,1,1.7,0,2,0,0,28,1
[Events]
Format: Layer, Start, End, Style, Name, Text
Dialogue: 0,Invalid,0:00:01.23,Default,Olly,This is the first subtitle{ignored}.
Dialogue: 0,0:00:02.34,Invalid,Default,Olly,This is the second subtitle \nwith a newline \Nand another.
Dialogue: 0,0:00:04:56,0:00:08:90,Default,Olly,This is the third subtitle, with a comma.
\ No newline at end of file
library/core/src/androidTest/assets/ssa/no_end_timecodes
0 → 100644
View file @
b333169a
[Script Info]
Title: SomeTitle
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Open Sans Semibold,36,&H00FFFFFF,&H000000FF,&H00020713,&H00000000,-1,0,0,0,100,100,0,0,1,1.7,0,2,0,0,28,1
[Events]
Format: Layer, Start, End, Style, Name, Text
Dialogue: 0,0:00:00.00, ,Default,Olly,This is the first subtitle.
Dialogue: 0,0:00:02.34, ,Default,Olly,This is the second subtitle \nwith a newline \Nand another.
Dialogue: 0,0:00:04.56, ,Default,Olly,This is the third subtitle, with a comma.
\ No newline at end of file
library/core/src/androidTest/assets/ssa/typical
0 → 100644
View file @
b333169a
[Script Info]
Title: SomeTitle
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Open Sans Semibold,36,&H00FFFFFF,&H000000FF,&H00020713,&H00000000,-1,0,0,0,100,100,0,0,1,1.7,0,2,0,0,28,1
[Events]
Format: Layer, Start, End, Style, Name, Text
Dialogue: 0,0:00:00.00,0:00:01.23,Default,Olly,This is the first subtitle{ignored}.
Dialogue: 0,0:00:02.34,0:00:03.45,Default,Olly,This is the second subtitle \nwith a newline \Nand another.
Dialogue: 0,0:00:04:56,0:00:08:90,Default,Olly,This is the third subtitle, with a comma.
\ No newline at end of file
library/core/src/androidTest/assets/ssa/typical_dialogue
0 → 100644
View file @
b333169a
Dialogue: 0,0:00:00.00,0:00:01.23,Default,Olly,This is the first subtitle{ignored}.
Dialogue: 0,0:00:02.34,0:00:03.45,Default,Olly,This is the second subtitle \nwith a newline \Nand another.
Dialogue: 0,0:00:04:56,0:00:08:90,Default,Olly,This is the third subtitle, with a comma.
\ No newline at end of file
library/core/src/androidTest/assets/ssa/typical_format
0 → 100644
View file @
b333169a
Format: Layer, Start, End, Style, Name, Text
\ No newline at end of file
library/core/src/androidTest/assets/ssa/typical_header
0 → 100644
View file @
b333169a
[Script Info]
Title: SomeTitle
[V4+ Styles]
Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding
Style: Default,Open Sans Semibold,36,&H00FFFFFF,&H000000FF,&H00020713,&H00000000,-1,0,0,0,100,100,0,0,1,1.7,0,2,0,0,28,1
\ No newline at end of file
library/core/src/androidTest/java/com/google/android/exoplayer2/text/ssa/SsaDecoderTest.java
0 → 100644
View file @
b333169a
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer2
.
text
.
ssa
;
import
android.test.InstrumentationTestCase
;
import
com.google.android.exoplayer2.testutil.TestUtil
;
import
java.io.IOException
;
import
java.util.ArrayList
;
/**
* Unit test for {@link SsaDecoder}.
*/
public
final
class
SsaDecoderTest
extends
InstrumentationTestCase
{
private
static
final
String
EMPTY
=
"ssa/empty"
;
private
static
final
String
TYPICAL
=
"ssa/typical"
;
private
static
final
String
TYPICAL_HEADER_ONLY
=
"ssa/typical_header"
;
private
static
final
String
TYPICAL_DIALOGUE_ONLY
=
"ssa/typical_dialogue"
;
private
static
final
String
TYPICAL_FORMAT_ONLY
=
"ssa/typical_format"
;
private
static
final
String
INVALID_TIMECODES
=
"ssa/invalid_timecodes"
;
private
static
final
String
NO_END_TIMECODES
=
"ssa/no_end_timecodes"
;
public
void
testDecodeEmpty
()
throws
IOException
{
SsaDecoder
decoder
=
new
SsaDecoder
();
byte
[]
bytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
EMPTY
);
SsaSubtitle
subtitle
=
decoder
.
decode
(
bytes
,
bytes
.
length
,
false
);
assertEquals
(
0
,
subtitle
.
getEventTimeCount
());
assertTrue
(
subtitle
.
getCues
(
0
).
isEmpty
());
}
public
void
testDecodeTypical
()
throws
IOException
{
SsaDecoder
decoder
=
new
SsaDecoder
();
byte
[]
bytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
TYPICAL
);
SsaSubtitle
subtitle
=
decoder
.
decode
(
bytes
,
bytes
.
length
,
false
);
assertEquals
(
6
,
subtitle
.
getEventTimeCount
());
assertTypicalCue1
(
subtitle
,
0
);
assertTypicalCue2
(
subtitle
,
2
);
assertTypicalCue3
(
subtitle
,
4
);
}
public
void
testDecodeTypicalWithInitializationData
()
throws
IOException
{
byte
[]
headerBytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
TYPICAL_HEADER_ONLY
);
byte
[]
formatBytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
TYPICAL_FORMAT_ONLY
);
ArrayList
<
byte
[]>
initializationData
=
new
ArrayList
<>();
initializationData
.
add
(
formatBytes
);
initializationData
.
add
(
headerBytes
);
SsaDecoder
decoder
=
new
SsaDecoder
(
initializationData
);
byte
[]
bytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
TYPICAL_DIALOGUE_ONLY
);
SsaSubtitle
subtitle
=
decoder
.
decode
(
bytes
,
bytes
.
length
,
false
);
assertEquals
(
6
,
subtitle
.
getEventTimeCount
());
assertTypicalCue1
(
subtitle
,
0
);
assertTypicalCue2
(
subtitle
,
2
);
assertTypicalCue3
(
subtitle
,
4
);
}
public
void
testDecodeInvalidTimecodes
()
throws
IOException
{
// Parsing should succeed, parsing the third cue only.
SsaDecoder
decoder
=
new
SsaDecoder
();
byte
[]
bytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
INVALID_TIMECODES
);
SsaSubtitle
subtitle
=
decoder
.
decode
(
bytes
,
bytes
.
length
,
false
);
assertEquals
(
2
,
subtitle
.
getEventTimeCount
());
assertTypicalCue3
(
subtitle
,
0
);
}
public
void
testDecodeNoEndTimecodes
()
throws
IOException
{
SsaDecoder
decoder
=
new
SsaDecoder
();
byte
[]
bytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
NO_END_TIMECODES
);
SsaSubtitle
subtitle
=
decoder
.
decode
(
bytes
,
bytes
.
length
,
false
);
assertEquals
(
3
,
subtitle
.
getEventTimeCount
());
assertEquals
(
0
,
subtitle
.
getEventTime
(
0
));
assertEquals
(
"This is the first subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
0
)).
get
(
0
).
text
.
toString
());
assertEquals
(
2340000
,
subtitle
.
getEventTime
(
1
));
assertEquals
(
"This is the second subtitle \nwith a newline \nand another."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
1
)).
get
(
0
).
text
.
toString
());
assertEquals
(
4560000
,
subtitle
.
getEventTime
(
2
));
assertEquals
(
"This is the third subtitle, with a comma."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
2
)).
get
(
0
).
text
.
toString
());
}
private
static
void
assertTypicalCue1
(
SsaSubtitle
subtitle
,
int
eventIndex
)
{
assertEquals
(
0
,
subtitle
.
getEventTime
(
eventIndex
));
assertEquals
(
"This is the first subtitle."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
eventIndex
)).
get
(
0
).
text
.
toString
());
assertEquals
(
1230000
,
subtitle
.
getEventTime
(
eventIndex
+
1
));
}
private
static
void
assertTypicalCue2
(
SsaSubtitle
subtitle
,
int
eventIndex
)
{
assertEquals
(
2340000
,
subtitle
.
getEventTime
(
eventIndex
));
assertEquals
(
"This is the second subtitle \nwith a newline \nand another."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
eventIndex
)).
get
(
0
).
text
.
toString
());
assertEquals
(
3450000
,
subtitle
.
getEventTime
(
eventIndex
+
1
));
}
private
static
void
assertTypicalCue3
(
SsaSubtitle
subtitle
,
int
eventIndex
)
{
assertEquals
(
4560000
,
subtitle
.
getEventTime
(
eventIndex
));
assertEquals
(
"This is the third subtitle, with a comma."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
eventIndex
)).
get
(
0
).
text
.
toString
());
assertEquals
(
8900000
,
subtitle
.
getEventTime
(
eventIndex
+
1
));
}
}
library/core/src/androidTest/java/com/google/android/exoplayer2/text/subrip/SubripDecoderTest.java
View file @
b333169a
...
...
@@ -37,7 +37,7 @@ public final class SubripDecoderTest extends InstrumentationTestCase {
SubripDecoder
decoder
=
new
SubripDecoder
();
byte
[]
bytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
EMPTY_FILE
);
SubripSubtitle
subtitle
=
decoder
.
decode
(
bytes
,
bytes
.
length
,
false
);
// Assert that the subtitle is empty.
assertEquals
(
0
,
subtitle
.
getEventTimeCount
());
assertTrue
(
subtitle
.
getCues
(
0
).
isEmpty
());
}
...
...
@@ -46,6 +46,7 @@ public final class SubripDecoderTest extends InstrumentationTestCase {
SubripDecoder
decoder
=
new
SubripDecoder
();
byte
[]
bytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
TYPICAL_FILE
);
SubripSubtitle
subtitle
=
decoder
.
decode
(
bytes
,
bytes
.
length
,
false
);
assertEquals
(
6
,
subtitle
.
getEventTimeCount
());
assertTypicalCue1
(
subtitle
,
0
);
assertTypicalCue2
(
subtitle
,
2
);
...
...
@@ -56,6 +57,7 @@ public final class SubripDecoderTest extends InstrumentationTestCase {
SubripDecoder
decoder
=
new
SubripDecoder
();
byte
[]
bytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
TYPICAL_WITH_BYTE_ORDER_MARK
);
SubripSubtitle
subtitle
=
decoder
.
decode
(
bytes
,
bytes
.
length
,
false
);
assertEquals
(
6
,
subtitle
.
getEventTimeCount
());
assertTypicalCue1
(
subtitle
,
0
);
assertTypicalCue2
(
subtitle
,
2
);
...
...
@@ -66,6 +68,7 @@ public final class SubripDecoderTest extends InstrumentationTestCase {
SubripDecoder
decoder
=
new
SubripDecoder
();
byte
[]
bytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
TYPICAL_EXTRA_BLANK_LINE
);
SubripSubtitle
subtitle
=
decoder
.
decode
(
bytes
,
bytes
.
length
,
false
);
assertEquals
(
6
,
subtitle
.
getEventTimeCount
());
assertTypicalCue1
(
subtitle
,
0
);
assertTypicalCue2
(
subtitle
,
2
);
...
...
@@ -77,6 +80,7 @@ public final class SubripDecoderTest extends InstrumentationTestCase {
SubripDecoder
decoder
=
new
SubripDecoder
();
byte
[]
bytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
TYPICAL_MISSING_TIMECODE
);
SubripSubtitle
subtitle
=
decoder
.
decode
(
bytes
,
bytes
.
length
,
false
);
assertEquals
(
4
,
subtitle
.
getEventTimeCount
());
assertTypicalCue1
(
subtitle
,
0
);
assertTypicalCue3
(
subtitle
,
2
);
...
...
@@ -87,6 +91,7 @@ public final class SubripDecoderTest extends InstrumentationTestCase {
SubripDecoder
decoder
=
new
SubripDecoder
();
byte
[]
bytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
TYPICAL_MISSING_SEQUENCE
);
SubripSubtitle
subtitle
=
decoder
.
decode
(
bytes
,
bytes
.
length
,
false
);
assertEquals
(
4
,
subtitle
.
getEventTimeCount
());
assertTypicalCue1
(
subtitle
,
0
);
assertTypicalCue3
(
subtitle
,
2
);
...
...
@@ -97,6 +102,7 @@ public final class SubripDecoderTest extends InstrumentationTestCase {
SubripDecoder
decoder
=
new
SubripDecoder
();
byte
[]
bytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
TYPICAL_NEGATIVE_TIMESTAMPS
);
SubripSubtitle
subtitle
=
decoder
.
decode
(
bytes
,
bytes
.
length
,
false
);
assertEquals
(
2
,
subtitle
.
getEventTimeCount
());
assertTypicalCue3
(
subtitle
,
0
);
}
...
...
@@ -106,20 +112,16 @@ public final class SubripDecoderTest extends InstrumentationTestCase {
byte
[]
bytes
=
TestUtil
.
getByteArray
(
getInstrumentation
(),
NO_END_TIMECODES_FILE
);
SubripSubtitle
subtitle
=
decoder
.
decode
(
bytes
,
bytes
.
length
,
false
);
// Test event count.
assertEquals
(
3
,
subtitle
.
getEventTimeCount
());
// Test first cue.
assertEquals
(
0
,
subtitle
.
getEventTime
(
0
));
assertEquals
(
"SubRip doesn't technically allow missing end timecodes."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
0
)).
get
(
0
).
text
.
toString
());
// Test second cue.
assertEquals
(
2345000
,
subtitle
.
getEventTime
(
1
));
assertEquals
(
"We interpret it to mean that a subtitle extends to the start of the next one."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
1
)).
get
(
0
).
text
.
toString
());
// Test third cue.
assertEquals
(
3456000
,
subtitle
.
getEventTime
(
2
));
assertEquals
(
"Or to the end of the media."
,
subtitle
.
getCues
(
subtitle
.
getEventTime
(
2
)).
get
(
0
).
text
.
toString
());
...
...
library/core/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java
View file @
b333169a
...
...
@@ -120,6 +120,7 @@ public final class MatroskaExtractor implements Extractor {
private
static
final
String
CODEC_ID_ACM
=
"A_MS/ACM"
;
private
static
final
String
CODEC_ID_PCM_INT_LIT
=
"A_PCM/INT/LIT"
;
private
static
final
String
CODEC_ID_SUBRIP
=
"S_TEXT/UTF8"
;
private
static
final
String
CODEC_ID_ASS
=
"S_TEXT/ASS"
;
private
static
final
String
CODEC_ID_VOBSUB
=
"S_VOBSUB"
;
private
static
final
String
CODEC_ID_PGS
=
"S_HDMV/PGS"
;
private
static
final
String
CODEC_ID_DVBSUB
=
"S_DVBSUB"
;
...
...
@@ -226,21 +227,62 @@ public final class MatroskaExtractor implements Extractor {
private
static
final
byte
[]
SUBRIP_PREFIX
=
new
byte
[]
{
49
,
10
,
48
,
48
,
58
,
48
,
48
,
58
,
48
,
48
,
44
,
48
,
48
,
48
,
32
,
45
,
45
,
62
,
32
,
48
,
48
,
58
,
48
,
48
,
58
,
48
,
48
,
44
,
48
,
48
,
48
,
10
};
/**
* A special end timecode indicating that a subtitle should be displayed until the next subtitle,
* or until the end of the media in the case of the last subtitle.
* The byte offset of the end timecode in {@link #SUBRIP_PREFIX}.
*/
private
static
final
int
SUBRIP_PREFIX_END_TIMECODE_OFFSET
=
19
;
/**
* A special end timecode indicating that a subrip subtitle should be displayed until the next
* subtitle, or until the end of the media in the case of the last subtitle.
* <p>
* Equivalent to the UTF-8 string: " ".
*/
private
static
final
byte
[]
SUBRIP_TIMECODE_EMPTY
=
new
byte
[]
{
32
,
32
,
32
,
32
,
32
,
32
,
32
,
32
,
32
,
32
,
32
,
32
};
/**
* The byte offset of the end timecode in {@link #SUBRIP_PREFIX}.
* The value by which to divide a time in microseconds to convert it to the unit of the last value
* in a subrip timecode (milliseconds).
*/
private
static
final
int
SUBRIP_PREFIX_END_TIMECODE_OFFSET
=
19
;
private
static
long
SUBRIP_TIMECODE_LAST_VALUE_SCALING_FACTOR
=
1000
;
/**
* The format of a subrip timecode.
*/
private
static
final
String
SUBRIP_TIMECODE_FORMAT
=
"%02d:%02d:%02d,%03d"
;
/**
* Matroska specific format line for SSA subtitles.
*/
private
static
final
byte
[]
SSA_DIALOGUE_FORMAT
=
Util
.
getUtf8Bytes
(
"Format: Start, End, "
+
"ReadOrder, Layer, Style, Name, MarginL, MarginR, MarginV, Effect, Text"
);
/**
* A template for the prefix that must be added to each SSA sample. The 10 byte end timecode
* starting at {@link #SSA_PREFIX_END_TIMECODE_OFFSET} is set to a dummy value, and must be
* replaced with the duration of the subtitle.
* <p>
* Equivalent to the UTF-8 string: "Dialogue: 0:00:00:00,0:00:00:00,".
*/
private
static
final
byte
[]
SSA_PREFIX
=
new
byte
[]
{
68
,
105
,
97
,
108
,
111
,
103
,
117
,
101
,
58
,
32
,
48
,
58
,
48
,
48
,
58
,
48
,
48
,
58
,
48
,
48
,
44
,
48
,
58
,
48
,
48
,
58
,
48
,
48
,
58
,
48
,
48
,
44
};
/**
* The byte offset of the end timecode in {@link #SSA_PREFIX}.
*/
private
static
final
int
SSA_PREFIX_END_TIMECODE_OFFSET
=
21
;
/**
* The value by which to divide a time in microseconds to convert it to the unit of the last value
* in an SSA timecode (1/100ths of a second).
*/
private
static
long
SSA_TIMECODE_LAST_VALUE_SCALING_FACTOR
=
10000
;
/**
* A special end timecode indicating that an SSA subtitle should be displayed until the next
* subtitle, or until the end of the media in the case of the last subtitle.
* <p>
* Equivalent to the UTF-8 string: " ".
*/
private
static
final
byte
[]
SSA_TIMECODE_EMPTY
=
new
byte
[]
{
32
,
32
,
32
,
32
,
32
,
32
,
32
,
32
,
32
,
32
};
/**
* The
length in bytes of a timecode in a subrip prefix
.
* The
format of an SSA timecode
.
*/
private
static
final
int
SUBRIP_TIMECODE_LENGTH
=
12
;
private
static
final
String
SSA_TIMECODE_FORMAT
=
"%01d:%02d:%02d:%02d"
;
/**
* The length in bytes of a WAVEFORMATEX structure.
...
...
@@ -271,7 +313,7 @@ public final class MatroskaExtractor implements Extractor {
private
final
ParsableByteArray
vorbisNumPageSamples
;
private
final
ParsableByteArray
seekEntryIdBytes
;
private
final
ParsableByteArray
sampleStrippedBytes
;
private
final
ParsableByteArray
sub
rip
Sample
;
private
final
ParsableByteArray
sub
title
Sample
;
private
final
ParsableByteArray
encryptionInitializationVector
;
private
final
ParsableByteArray
encryptionSubsampleData
;
private
ByteBuffer
encryptionSubsampleDataBuffer
;
...
...
@@ -349,7 +391,7 @@ public final class MatroskaExtractor implements Extractor {
nalStartCode
=
new
ParsableByteArray
(
NalUnitUtil
.
NAL_START_CODE
);
nalLength
=
new
ParsableByteArray
(
4
);
sampleStrippedBytes
=
new
ParsableByteArray
();
sub
rip
Sample
=
new
ParsableByteArray
();
sub
title
Sample
=
new
ParsableByteArray
();
encryptionInitializationVector
=
new
ParsableByteArray
(
ENCRYPTION_IV_SIZE
);
encryptionSubsampleData
=
new
ParsableByteArray
();
}
...
...
@@ -1016,7 +1058,7 @@ public final class MatroskaExtractor implements Extractor {
// For SimpleBlock, we have metadata for each sample here.
while
(
blockLacingSampleIndex
<
blockLacingSampleCount
)
{
writeSampleData
(
input
,
track
,
blockLacingSampleSizes
[
blockLacingSampleIndex
]);
long
sampleTimeUs
=
this
.
blockTimeUs
long
sampleTimeUs
=
blockTimeUs
+
(
blockLacingSampleIndex
*
track
.
defaultSampleDurationNs
)
/
1000
;
commitSampleToOutput
(
track
,
sampleTimeUs
);
blockLacingSampleIndex
++;
...
...
@@ -1036,7 +1078,11 @@ public final class MatroskaExtractor implements Extractor {
private
void
commitSampleToOutput
(
Track
track
,
long
timeUs
)
{
if
(
CODEC_ID_SUBRIP
.
equals
(
track
.
codecId
))
{
writeSubripSample
(
track
);
commitSubtitleSample
(
track
,
SUBRIP_TIMECODE_FORMAT
,
SUBRIP_PREFIX_END_TIMECODE_OFFSET
,
SUBRIP_TIMECODE_LAST_VALUE_SCALING_FACTOR
,
SUBRIP_TIMECODE_EMPTY
);
}
else
if
(
CODEC_ID_ASS
.
equals
(
track
.
codecId
))
{
commitSubtitleSample
(
track
,
SSA_TIMECODE_FORMAT
,
SSA_PREFIX_END_TIMECODE_OFFSET
,
SSA_TIMECODE_LAST_VALUE_SCALING_FACTOR
,
SSA_TIMECODE_EMPTY
);
}
track
.
output
.
sampleMetadata
(
timeUs
,
blockFlags
,
sampleBytesWritten
,
0
,
track
.
cryptoData
);
sampleRead
=
true
;
...
...
@@ -1076,17 +1122,10 @@ public final class MatroskaExtractor implements Extractor {
private
void
writeSampleData
(
ExtractorInput
input
,
Track
track
,
int
size
)
throws
IOException
,
InterruptedException
{
if
(
CODEC_ID_SUBRIP
.
equals
(
track
.
codecId
))
{
int
sizeWithPrefix
=
SUBRIP_PREFIX
.
length
+
size
;
if
(
subripSample
.
capacity
()
<
sizeWithPrefix
)
{
// Initialize subripSample to contain the required prefix and have space to hold a subtitle
// twice as long as this one.
subripSample
.
data
=
Arrays
.
copyOf
(
SUBRIP_PREFIX
,
sizeWithPrefix
+
size
);
}
input
.
readFully
(
subripSample
.
data
,
SUBRIP_PREFIX
.
length
,
size
);
subripSample
.
setPosition
(
0
);
subripSample
.
setLimit
(
sizeWithPrefix
);
// Defer writing the data to the track output. We need to modify the sample data by setting
// the correct end timecode, which we might not have yet.
writeSubtitleSampleData
(
input
,
SUBRIP_PREFIX
,
size
);
return
;
}
else
if
(
CODEC_ID_ASS
.
equals
(
track
.
codecId
))
{
writeSubtitleSampleData
(
input
,
SSA_PREFIX
,
size
);
return
;
}
...
...
@@ -1230,31 +1269,50 @@ public final class MatroskaExtractor implements Extractor {
}
}
private
void
writeSubripSample
(
Track
track
)
{
setSubripSampleEndTimecode
(
subripSample
.
data
,
blockDurationUs
);
private
void
writeSubtitleSampleData
(
ExtractorInput
input
,
byte
[]
samplePrefix
,
int
size
)
throws
IOException
,
InterruptedException
{
int
sizeWithPrefix
=
samplePrefix
.
length
+
size
;
if
(
subtitleSample
.
capacity
()
<
sizeWithPrefix
)
{
// Initialize subripSample to contain the required prefix and have space to hold a subtitle
// twice as long as this one.
subtitleSample
.
data
=
Arrays
.
copyOf
(
samplePrefix
,
sizeWithPrefix
+
size
);
}
else
{
System
.
arraycopy
(
samplePrefix
,
0
,
subtitleSample
.
data
,
0
,
samplePrefix
.
length
);
}
input
.
readFully
(
subtitleSample
.
data
,
samplePrefix
.
length
,
size
);
subtitleSample
.
reset
(
sizeWithPrefix
);
// Defer writing the data to the track output. We need to modify the sample data by setting
// the correct end timecode, which we might not have yet.
}
private
void
commitSubtitleSample
(
Track
track
,
String
timecodeFormat
,
int
endTimecodeOffset
,
long
lastTimecodeValueScalingFactor
,
byte
[]
emptyTimecode
)
{
setSampleDuration
(
subtitleSample
.
data
,
blockDurationUs
,
timecodeFormat
,
endTimecodeOffset
,
lastTimecodeValueScalingFactor
,
emptyTimecode
);
// Note: If we ever want to support DRM protected subtitles then we'll need to output the
// appropriate encryption data here.
track
.
output
.
sampleData
(
sub
ripSample
,
subrip
Sample
.
limit
());
sampleBytesWritten
+=
sub
rip
Sample
.
limit
();
track
.
output
.
sampleData
(
sub
titleSample
,
subtitle
Sample
.
limit
());
sampleBytesWritten
+=
sub
title
Sample
.
limit
();
}
private
static
void
setSubripSampleEndTimecode
(
byte
[]
subripSampleData
,
long
timeUs
)
{
private
static
void
setSampleDuration
(
byte
[]
subripSampleData
,
long
durationUs
,
String
timecodeFormat
,
int
endTimecodeOffset
,
long
lastTimecodeValueScalingFactor
,
byte
[]
emptyTimecode
)
{
byte
[]
timeCodeData
;
if
(
time
Us
==
C
.
TIME_UNSET
)
{
timeCodeData
=
SUBRIP_TIMECODE_EMPTY
;
if
(
duration
Us
==
C
.
TIME_UNSET
)
{
timeCodeData
=
emptyTimecode
;
}
else
{
int
hours
=
(
int
)
(
timeUs
/
3600000000L
);
timeUs
-=
(
hours
*
3600000000L
);
int
minutes
=
(
int
)
(
timeUs
/
60000000
);
timeUs
-=
(
minutes
*
60000000
);
int
seconds
=
(
int
)
(
timeUs
/
1000000
);
timeUs
-=
(
seconds
*
1000000
);
int
milliseconds
=
(
int
)
(
timeUs
/
1000
);
timeCodeData
=
Util
.
getUtf8Bytes
(
String
.
format
(
Locale
.
US
,
"%02d:%02d:%02d,%03d"
,
hour
s
,
minutes
,
seconds
,
milliseconds
));
int
hours
=
(
int
)
(
durationUs
/
(
3600
*
C
.
MICROS_PER_SECOND
)
);
durationUs
-=
(
hours
*
3600
*
C
.
MICROS_PER_SECOND
);
int
minutes
=
(
int
)
(
durationUs
/
(
60
*
C
.
MICROS_PER_SECOND
)
);
durationUs
-=
(
minutes
*
60
*
C
.
MICROS_PER_SECOND
);
int
seconds
=
(
int
)
(
durationUs
/
C
.
MICROS_PER_SECOND
);
durationUs
-=
(
seconds
*
C
.
MICROS_PER_SECOND
);
int
lastValue
=
(
int
)
(
durationUs
/
lastTimecodeValueScalingFactor
);
timeCodeData
=
Util
.
getUtf8Bytes
(
String
.
format
(
Locale
.
US
,
timecodeFormat
,
hours
,
minute
s
,
seconds
,
lastValue
));
}
System
.
arraycopy
(
timeCodeData
,
0
,
subripSampleData
,
SUBRIP_PREFIX_END_TIMECODE_OFFSET
,
SUBRIP_TIMECODE_LENGTH
);
System
.
arraycopy
(
timeCodeData
,
0
,
subripSampleData
,
endTimecodeOffset
,
emptyTimecode
.
length
);
}
/**
...
...
@@ -1385,6 +1443,7 @@ public final class MatroskaExtractor implements Extractor {
||
CODEC_ID_ACM
.
equals
(
codecId
)
||
CODEC_ID_PCM_INT_LIT
.
equals
(
codecId
)
||
CODEC_ID_SUBRIP
.
equals
(
codecId
)
||
CODEC_ID_ASS
.
equals
(
codecId
)
||
CODEC_ID_VOBSUB
.
equals
(
codecId
)
||
CODEC_ID_PGS
.
equals
(
codecId
)
||
CODEC_ID_DVBSUB
.
equals
(
codecId
);
...
...
@@ -1650,6 +1709,9 @@ public final class MatroskaExtractor implements Extractor {
case
CODEC_ID_SUBRIP:
mimeType
=
MimeTypes
.
APPLICATION_SUBRIP
;
break
;
case
CODEC_ID_ASS:
mimeType
=
MimeTypes
.
TEXT_SSA
;
break
;
case
CODEC_ID_VOBSUB:
mimeType
=
MimeTypes
.
APPLICATION_VOBSUB
;
initializationData
=
Collections
.
singletonList
(
codecPrivate
);
...
...
@@ -1702,6 +1764,14 @@ public final class MatroskaExtractor implements Extractor {
type
=
C
.
TRACK_TYPE_TEXT
;
format
=
Format
.
createTextSampleFormat
(
Integer
.
toString
(
trackId
),
mimeType
,
null
,
Format
.
NO_VALUE
,
selectionFlags
,
language
,
drmInitData
);
}
else
if
(
MimeTypes
.
TEXT_SSA
.
equals
(
mimeType
))
{
type
=
C
.
TRACK_TYPE_TEXT
;
initializationData
=
new
ArrayList
<>(
2
);
initializationData
.
add
(
SSA_DIALOGUE_FORMAT
);
initializationData
.
add
(
codecPrivate
);
format
=
Format
.
createTextSampleFormat
(
Integer
.
toString
(
trackId
),
mimeType
,
null
,
Format
.
NO_VALUE
,
selectionFlags
,
language
,
Format
.
NO_VALUE
,
drmInitData
,
Format
.
OFFSET_SAMPLE_RELATIVE
,
initializationData
);
}
else
if
(
MimeTypes
.
APPLICATION_VOBSUB
.
equals
(
mimeType
)
||
MimeTypes
.
APPLICATION_PGS
.
equals
(
mimeType
)
||
MimeTypes
.
APPLICATION_DVBSUBS
.
equals
(
mimeType
))
{
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/SubtitleDecoderFactory.java
View file @
b333169a
...
...
@@ -19,6 +19,7 @@ import com.google.android.exoplayer2.Format;
import
com.google.android.exoplayer2.text.cea.Cea608Decoder
;
import
com.google.android.exoplayer2.text.cea.Cea708Decoder
;
import
com.google.android.exoplayer2.text.dvb.DvbDecoder
;
import
com.google.android.exoplayer2.text.ssa.SsaDecoder
;
import
com.google.android.exoplayer2.text.subrip.SubripDecoder
;
import
com.google.android.exoplayer2.text.ttml.TtmlDecoder
;
import
com.google.android.exoplayer2.text.tx3g.Tx3gDecoder
;
...
...
@@ -58,6 +59,7 @@ public interface SubtitleDecoderFactory {
* <li>WebVTT (MP4) ({@link Mp4WebvttDecoder})</li>
* <li>TTML ({@link TtmlDecoder})</li>
* <li>SubRip ({@link SubripDecoder})</li>
* <li>SSA/ASS ({@link SsaDecoder})</li>
* <li>TX3G ({@link Tx3gDecoder})</li>
* <li>Cea608 ({@link Cea608Decoder})</li>
* <li>Cea708 ({@link Cea708Decoder})</li>
...
...
@@ -70,6 +72,7 @@ public interface SubtitleDecoderFactory {
public
boolean
supportsFormat
(
Format
format
)
{
String
mimeType
=
format
.
sampleMimeType
;
return
MimeTypes
.
TEXT_VTT
.
equals
(
mimeType
)
||
MimeTypes
.
TEXT_SSA
.
equals
(
mimeType
)
||
MimeTypes
.
APPLICATION_TTML
.
equals
(
mimeType
)
||
MimeTypes
.
APPLICATION_MP4VTT
.
equals
(
mimeType
)
||
MimeTypes
.
APPLICATION_SUBRIP
.
equals
(
mimeType
)
...
...
@@ -85,6 +88,8 @@ public interface SubtitleDecoderFactory {
switch
(
format
.
sampleMimeType
)
{
case
MimeTypes
.
TEXT_VTT
:
return
new
WebvttDecoder
();
case
MimeTypes
.
TEXT_SSA
:
return
new
SsaDecoder
(
format
.
initializationData
);
case
MimeTypes
.
APPLICATION_MP4VTT
:
return
new
Mp4WebvttDecoder
();
case
MimeTypes
.
APPLICATION_TTML
:
...
...
library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaDecoder.java
0 → 100644
View file @
b333169a
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer2
.
text
.
ssa
;
import
android.text.TextUtils
;
import
android.util.Log
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.text.Cue
;
import
com.google.android.exoplayer2.text.SimpleSubtitleDecoder
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.LongArray
;
import
com.google.android.exoplayer2.util.ParsableByteArray
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.regex.Matcher
;
import
java.util.regex.Pattern
;
/**
* A {@link SimpleSubtitleDecoder} for SSA/ASS.
*/
public
final
class
SsaDecoder
extends
SimpleSubtitleDecoder
{
private
static
final
String
TAG
=
"SsaDecoder"
;
private
static
final
Pattern
SSA_TIMECODE_PATTERN
=
Pattern
.
compile
(
"(?:(\\d+):)?(\\d+):(\\d+)(?::|\\.)(\\d+)"
);
private
static
final
String
FORMAT_LINE_PREFIX
=
"Format: "
;
private
static
final
String
DIALOGUE_LINE_PREFIX
=
"Dialogue: "
;
private
final
boolean
haveInitializationData
;
private
int
formatKeyCount
;
private
int
formatStartIndex
;
private
int
formatEndIndex
;
private
int
formatTextIndex
;
public
SsaDecoder
()
{
this
(
null
);
}
/**
* @param initializationData Optional initialization data for the decoder. If not null, the
* initialization data must consist of two byte arrays. The first must contain an SSA format
* line. The second must contain an SSA header that will be assumed common to all samples.
*/
public
SsaDecoder
(
List
<
byte
[]>
initializationData
)
{
super
(
"SsaDecoder"
);
if
(
initializationData
!=
null
)
{
haveInitializationData
=
true
;
String
formatLine
=
new
String
(
initializationData
.
get
(
0
));
Assertions
.
checkArgument
(
formatLine
.
startsWith
(
FORMAT_LINE_PREFIX
));
parseFormatLine
(
formatLine
);
parseHeader
(
new
ParsableByteArray
(
initializationData
.
get
(
1
)));
}
else
{
haveInitializationData
=
false
;
}
}
@Override
protected
SsaSubtitle
decode
(
byte
[]
bytes
,
int
length
,
boolean
reset
)
{
ArrayList
<
Cue
>
cues
=
new
ArrayList
<>();
LongArray
cueTimesUs
=
new
LongArray
();
ParsableByteArray
data
=
new
ParsableByteArray
(
bytes
,
length
);
if
(!
haveInitializationData
)
{
parseHeader
(
data
);
}
parseEventBody
(
data
,
cues
,
cueTimesUs
);
Cue
[]
cuesArray
=
new
Cue
[
cues
.
size
()];
cues
.
toArray
(
cuesArray
);
long
[]
cueTimesUsArray
=
cueTimesUs
.
toArray
();
return
new
SsaSubtitle
(
cuesArray
,
cueTimesUsArray
);
}
/**
* Parses the header of the subtitle.
*
* @param data A {@link ParsableByteArray} from which the header should be read.
*/
private
void
parseHeader
(
ParsableByteArray
data
)
{
String
currentLine
;
while
((
currentLine
=
data
.
readLine
())
!=
null
)
{
// TODO: Parse useful data from the header.
if
(
currentLine
.
startsWith
(
"[Events]"
))
{
// We've reached the event body.
return
;
}
}
}
/**
* Parses the event body of the subtitle.
*
* @param data A {@link ParsableByteArray} from which the body should be read.
* @param cues A list to which parsed cues will be added.
* @param cueTimesUs An array to which parsed cue timestamps will be added.
*/
private
void
parseEventBody
(
ParsableByteArray
data
,
List
<
Cue
>
cues
,
LongArray
cueTimesUs
)
{
String
currentLine
;
while
((
currentLine
=
data
.
readLine
())
!=
null
)
{
if
(!
haveInitializationData
&&
currentLine
.
startsWith
(
FORMAT_LINE_PREFIX
))
{
parseFormatLine
(
currentLine
);
}
else
if
(
currentLine
.
startsWith
(
DIALOGUE_LINE_PREFIX
))
{
parseDialogueLine
(
currentLine
,
cues
,
cueTimesUs
);
}
}
}
/**
* Parses a format line.
*
* @param formatLine The line to parse.
*/
private
void
parseFormatLine
(
String
formatLine
)
{
String
[]
values
=
TextUtils
.
split
(
formatLine
.
substring
(
FORMAT_LINE_PREFIX
.
length
()),
","
);
formatKeyCount
=
values
.
length
;
formatStartIndex
=
C
.
INDEX_UNSET
;
formatEndIndex
=
C
.
INDEX_UNSET
;
formatTextIndex
=
C
.
INDEX_UNSET
;
for
(
int
i
=
0
;
i
<
formatKeyCount
;
i
++)
{
String
key
=
values
[
i
].
trim
().
toLowerCase
();
switch
(
key
)
{
case
"start"
:
formatStartIndex
=
i
;
break
;
case
"end"
:
formatEndIndex
=
i
;
break
;
case
"text"
:
formatTextIndex
=
i
;
break
;
default
:
// Do nothing.
break
;
}
}
}
/**
* Parses a dialogue line.
*
* @param dialogueLine The line to parse.
* @param cues A list to which parsed cues will be added.
* @param cueTimesUs An array to which parsed cue timestamps will be added.
*/
private
void
parseDialogueLine
(
String
dialogueLine
,
List
<
Cue
>
cues
,
LongArray
cueTimesUs
)
{
if
(
formatKeyCount
==
0
)
{
Log
.
w
(
TAG
,
"Skipping dialogue line before format: "
+
dialogueLine
);
return
;
}
String
[]
lineValues
=
dialogueLine
.
substring
(
DIALOGUE_LINE_PREFIX
.
length
())
.
split
(
","
,
formatKeyCount
);
long
startTimeUs
=
SsaDecoder
.
parseTimecodeUs
(
lineValues
[
formatStartIndex
]);
if
(
startTimeUs
==
C
.
TIME_UNSET
)
{
Log
.
w
(
TAG
,
"Skipping invalid timing: "
+
dialogueLine
);
return
;
}
long
endTimeUs
=
C
.
TIME_UNSET
;
String
endTimeString
=
lineValues
[
formatEndIndex
];
if
(!
endTimeString
.
trim
().
isEmpty
())
{
endTimeUs
=
SsaDecoder
.
parseTimecodeUs
(
endTimeString
);
if
(
endTimeUs
==
C
.
TIME_UNSET
)
{
Log
.
w
(
TAG
,
"Skipping invalid timing: "
+
dialogueLine
);
return
;
}
}
String
text
=
lineValues
[
formatTextIndex
]
.
replaceAll
(
"\\{.*?\\}"
,
""
)
.
replaceAll
(
"\\\\N"
,
"\n"
)
.
replaceAll
(
"\\\\n"
,
"\n"
);
cues
.
add
(
new
Cue
(
text
));
cueTimesUs
.
add
(
startTimeUs
);
if
(
endTimeUs
!=
C
.
TIME_UNSET
)
{
cues
.
add
(
null
);
cueTimesUs
.
add
(
endTimeUs
);
}
}
/**
* Parses an SSA timecode string.
*
* @param timeString The string to parse.
* @return The parsed timestamp in microseconds.
*/
public
static
long
parseTimecodeUs
(
String
timeString
)
{
Matcher
matcher
=
SSA_TIMECODE_PATTERN
.
matcher
(
timeString
);
if
(!
matcher
.
matches
())
{
return
C
.
TIME_UNSET
;
}
long
timestampUs
=
Long
.
parseLong
(
matcher
.
group
(
1
))
*
60
*
60
*
C
.
MICROS_PER_SECOND
;
timestampUs
+=
Long
.
parseLong
(
matcher
.
group
(
2
))
*
60
*
C
.
MICROS_PER_SECOND
;
timestampUs
+=
Long
.
parseLong
(
matcher
.
group
(
3
))
*
C
.
MICROS_PER_SECOND
;
timestampUs
+=
Long
.
parseLong
(
matcher
.
group
(
4
))
*
10000
;
// 100ths of a second.
return
timestampUs
;
}
}
library/core/src/main/java/com/google/android/exoplayer2/text/ssa/SsaSubtitle.java
0 → 100644
View file @
b333169a
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package
com
.
google
.
android
.
exoplayer2
.
text
.
ssa
;
import
com.google.android.exoplayer2.C
;
import
com.google.android.exoplayer2.text.Cue
;
import
com.google.android.exoplayer2.text.Subtitle
;
import
com.google.android.exoplayer2.util.Assertions
;
import
com.google.android.exoplayer2.util.Util
;
import
java.util.Collections
;
import
java.util.List
;
/**
* A representation of an SSA/ASS subtitle.
*/
/* package */
final
class
SsaSubtitle
implements
Subtitle
{
private
final
Cue
[]
cues
;
private
final
long
[]
cueTimesUs
;
/**
* @param cues The cues in the subtitle. Null entries may be used to represent empty cues.
* @param cueTimesUs The cue times, in microseconds.
*/
public
SsaSubtitle
(
Cue
[]
cues
,
long
[]
cueTimesUs
)
{
this
.
cues
=
cues
;
this
.
cueTimesUs
=
cueTimesUs
;
}
@Override
public
int
getNextEventTimeIndex
(
long
timeUs
)
{
int
index
=
Util
.
binarySearchCeil
(
cueTimesUs
,
timeUs
,
false
,
false
);
return
index
<
cueTimesUs
.
length
?
index
:
C
.
INDEX_UNSET
;
}
@Override
public
int
getEventTimeCount
()
{
return
cueTimesUs
.
length
;
}
@Override
public
long
getEventTime
(
int
index
)
{
Assertions
.
checkArgument
(
index
>=
0
);
Assertions
.
checkArgument
(
index
<
cueTimesUs
.
length
);
return
cueTimesUs
[
index
];
}
@Override
public
List
<
Cue
>
getCues
(
long
timeUs
)
{
int
index
=
Util
.
binarySearchFloor
(
cueTimesUs
,
timeUs
,
true
,
false
);
if
(
index
==
-
1
||
cues
[
index
]
==
null
)
{
// timeUs is earlier than the start of the first cue, or we have an empty cue.
return
Collections
.
emptyList
();
}
else
{
return
Collections
.
singletonList
(
cues
[
index
]);
}
}
}
library/core/src/main/java/com/google/android/exoplayer2/util/MimeTypes.java
View file @
b333169a
...
...
@@ -65,6 +65,7 @@ public final class MimeTypes {
public
static
final
String
AUDIO_UNKNOWN
=
BASE_TYPE_AUDIO
+
"/x-unknown"
;
public
static
final
String
TEXT_VTT
=
BASE_TYPE_TEXT
+
"/vtt"
;
public
static
final
String
TEXT_SSA
=
BASE_TYPE_TEXT
+
"/x-ssa"
;
public
static
final
String
APPLICATION_MP4
=
BASE_TYPE_APPLICATION
+
"/mp4"
;
public
static
final
String
APPLICATION_WEBM
=
BASE_TYPE_APPLICATION
+
"/webm"
;
...
...
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