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
71d83f3e
authored
Aug 29, 2016
by
Rik Heijdens
Browse files
Options
_('Browse Files')
Download
Email Patches
Plain Diff
Caption lines as separate cues
parent
7cd94819
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
299 additions
and
244 deletions
library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608CueBuilder.java
library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Decoder.java
library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Subtitle.java
library/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java
library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608CueBuilder.java
0 → 100644
View file @
71d83f3e
package
com
.
google
.
android
.
exoplayer2
.
text
.
eia608
;
import
com.google.android.exoplayer2.text.Cue
;
import
com.google.android.exoplayer2.util.Assertions
;
import
android.text.Layout
;
import
android.text.SpannableStringBuilder
;
import
android.text.Spanned
;
import
android.text.style.CharacterStyle
;
import
android.text.style.ForegroundColorSpan
;
import
android.text.style.StyleSpan
;
import
java.util.Collections
;
import
java.util.HashMap
;
import
java.util.LinkedList
;
import
java.util.List
;
/**
* A Builder for EIA-608 cues.
*/
/* package */
final
class
Eia608CueBuilder
{
private
static
final
int
BASE_ROW
=
15
;
/**
* The caption string.
*/
private
SpannableStringBuilder
captionStringBuilder
;
/**
* The caption styles to apply to the caption string.
*/
private
HashMap
<
Integer
,
CharacterStyle
>
captionStyles
;
/**
* The row on which the Cue should be displayed.
*/
private
int
row
;
/**
* The indent of the cue - horizontal positioning.
*/
private
int
indent
;
/**
* The setTabOffset offset for the cue.
*/
private
int
tabOffset
;
public
Eia608CueBuilder
()
{
row
=
BASE_ROW
;
indent
=
0
;
tabOffset
=
0
;
captionStringBuilder
=
new
SpannableStringBuilder
();
captionStyles
=
new
HashMap
<>();
}
/**
* Sets the row for this cue.
* @param row the row to set.
*/
public
void
setRow
(
int
row
)
{
Assertions
.
checkArgument
(
row
>=
1
&&
row
<=
15
);
this
.
row
=
row
;
}
public
int
getRow
()
{
return
row
;
}
/**
* Rolls up the Cue one row.
* @return true if rolling was possible.
*/
public
boolean
rollUp
()
{
if
(
row
<
1
)
{
return
false
;
}
setRow
(
row
-
1
);
return
true
;
}
/**
* Sets the indent for this cue.
* @param indent an indent value, must be a multiple of 4 within the range [0,28]
*/
public
void
setIndent
(
int
indent
)
{
Assertions
.
checkArgument
(
indent
%
4
==
0
&&
indent
<=
28
);
this
.
indent
=
indent
;
}
public
void
tab
(
int
tabs
)
{
tabOffset
+=
tabs
;
}
/**
* Indents the cue position with amountOfTabs.
* @param tabOffset the amount of tabs the cue position should be indented.
*/
public
void
setTabOffset
(
int
tabOffset
)
{
this
.
tabOffset
=
tabOffset
;
}
/**
* Appends a character to the current Cue.
* @param character the character to append.
*/
public
void
append
(
char
character
)
{
captionStringBuilder
.
append
(
character
);
}
/**
* Removes the last character of the caption string.
*/
public
void
backspace
()
{
if
(
captionStringBuilder
.
length
()
>
0
)
{
captionStringBuilder
.
delete
(
captionStringBuilder
.
length
()
-
1
,
captionStringBuilder
.
length
());
}
}
/**
* Opens a character style at the current cueIndex.
* Takes care of style priorities.
*
* @param style the style to set.
*/
public
void
setCharacterStyle
(
CharacterStyle
style
)
{
int
startIndex
=
getSpanStartIndex
();
// Close all open spans of the same type, and add a new one.
if
(
style
instanceof
ForegroundColorSpan
)
{
// Setting a foreground color clears the italics style.
closeSpan
(
StyleSpan
.
class
);
// Italics is a style span.
}
closeSpan
(
style
.
getClass
());
captionStyles
.
put
(
startIndex
,
style
);
}
/**
* Closes all open spans of the spansToApply class.
* @param spansToClose the class of which the spans should be closed.
*/
private
void
closeSpan
(
Class
<?
extends
CharacterStyle
>
spansToClose
)
{
for
(
Integer
index
:
captionStyles
.
keySet
())
{
CharacterStyle
style
=
captionStyles
.
get
(
index
);
if
(
spansToClose
.
isInstance
(
style
))
{
if
(
index
<
captionStringBuilder
.
length
())
{
captionStringBuilder
.
setSpan
(
style
,
index
,
captionStringBuilder
.
length
(),
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
captionStyles
.
remove
(
index
);
}
}
}
/**
* Applies all currently opened spans to the SpannableStringBuilder.
*/
public
void
closeSpans
()
{
// Check if we have to do anything.
if
(
captionStyles
.
size
()
==
0
)
{
return
;
}
for
(
Integer
startIndex
:
captionStyles
.
keySet
())
{
// There may be cases, e.g. when seeking where the startIndex becomes greater
// than what is actually in the string builder, in that case, just discard the span.
if
(
startIndex
<
captionStringBuilder
.
length
())
{
CharacterStyle
captionStyle
=
captionStyles
.
get
(
startIndex
);
captionStringBuilder
.
setSpan
(
captionStyle
,
startIndex
,
captionStringBuilder
.
length
(),
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
captionStyles
.
remove
(
startIndex
);
}
}
public
Cue
build
()
{
closeSpans
();
float
cueLine
=
10
+
(
5.33f
*
row
);
float
cuePosition
=
10
+
(
2.5f
*
indent
);
cuePosition
=
(
tabOffset
*
2.5f
)
+
cuePosition
;
return
new
Cue
(
captionStringBuilder
,
Layout
.
Alignment
.
ALIGN_NORMAL
,
cueLine
/
100
,
Cue
.
LINE_TYPE_FRACTION
,
Cue
.
ANCHOR_TYPE_START
,
cuePosition
/
100
,
Cue
.
TYPE_UNSET
,
Cue
.
DIMEN_UNSET
);
}
private
int
getSpanStartIndex
()
{
return
captionStringBuilder
.
length
()
>
0
?
captionStringBuilder
.
length
()
-
1
:
0
;
}
public
static
List
<
Cue
>
buildCues
(
List
<
Eia608CueBuilder
>
builders
)
{
if
(
builders
.
isEmpty
())
{
return
Collections
.
emptyList
();
}
LinkedList
<
Cue
>
cues
=
new
LinkedList
<>();
for
(
Eia608CueBuilder
builder
:
builders
)
{
if
(
builder
.
captionStringBuilder
.
length
()
==
0
)
{
continue
;
}
cues
.
add
(
builder
.
build
());
}
return
cues
;
}
}
library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Decoder.java
View file @
71d83f3e
...
...
@@ -26,16 +26,12 @@ import com.google.android.exoplayer2.util.ParsableByteArray;
import
android.graphics.Color
;
import
android.graphics.Typeface
;
import
android.text.Layout
;
import
android.text.SpannableStringBuilder
;
import
android.text.Spanned
;
import
android.text.style.BackgroundColorSpan
;
import
android.text.style.CharacterStyle
;
import
android.text.style.ForegroundColorSpan
;
import
android.text.style.StyleSpan
;
import
android.text.style.UnderlineSpan
;
import
java.util.
HashMap
;
import
java.util.
Collections
;
import
java.util.LinkedList
;
import
java.util.TreeSet
;
...
...
@@ -98,6 +94,7 @@ public final class Eia608Decoder implements SubtitleDecoder {
private
static
final
byte
CTRL_ERASE_DISPLAYED_MEMORY
=
0x2C
;
private
static
final
byte
CTRL_CARRIAGE_RETURN
=
0x2D
;
private
static
final
byte
CTRL_ERASE_NON_DISPLAYED_MEMORY
=
0x2E
;
private
static
final
byte
CTRL_DELETE_TO_END_OF_ROW
=
0x24
;
private
static
final
byte
CTRL_TAB_OFFSET_CHAN_1
=
0x17
;
private
static
final
byte
CTRL_TAB_OFFSET_CHAN_2
=
0x1F
;
...
...
@@ -175,33 +172,13 @@ public final class Eia608Decoder implements SubtitleDecoder {
0xC5
,
0xE5
,
0xD8
,
0xF8
,
0x250C
,
0x2510
,
0x2514
,
0x2518
};
// Maps EIA-608 PAC row numbers to WebVTT cue line settings.
// Adapted from: https://dvcs.w3.org/hg/text-tracks/raw-file/default/608toVTT/608toVTT.html#x1-preamble-address-code-pac
private
static
final
float
[]
CUE_LINE_MAP
=
new
float
[]
{
63.33f
,
// Row 11
10.00f
,
// Row 1
26.00f
,
// Row 4
68.66f
,
// Row 12
79.33f
,
// Row 14
31.33f
,
// Row 5
42.00f
,
// Row 7
52.66f
,
// Row 9
};
// Maps EIA-608 PAC row bits to rows.
private
static
final
int
[]
ROW_INDICES
=
new
int
[]
{
11
,
1
,
3
,
12
,
14
,
5
,
7
,
9
};
// Maps EIA-608 PAC indents to WebVTT cue position values.
// Adapted from: https://dvcs.w3.org/hg/text-tracks/raw-file/default/608toVTT/608toVTT.html#x1-preamble-address-code-pac
// Note that these cue position values may not give the intended result, unless the font size is set
// Maps EIA-608 PAC cursor bits to indents.
// Note that these indents may not give the intended result, unless the font size is set
// to allow for a maximum of 32 (or 41) characters per line.
private
static
final
float
[]
INDENT_MAP
=
new
float
[]
{
10.0f
,
// Indent 0/Column 1
20.0f
,
// Indent 4/Column 5
30.0f
,
// Indent 8/Column 9
40.0f
,
// Indent 12/Column 13
50.0f
,
// Indent 16/Column 17
60.0f
,
// Indent 20/Column 21
70.0f
,
// Indent 24/Column 25
80.0f
,
// Indent 28/Column 29
};
private
static
final
int
[]
CURSOR_INDICES
=
new
int
[]
{
0
,
4
,
8
,
12
,
16
,
20
,
24
,
28
};
private
static
final
int
[]
COLOR_MAP
=
new
int
[]
{
Color
.
WHITE
,
...
...
@@ -216,18 +193,14 @@ public final class Eia608Decoder implements SubtitleDecoder {
// Transparency is defined in the first byte of an integer.
private
static
final
int
TRANSPARENCY_MASK
=
0x80FFFFFF
;
private
static
final
int
STYLE_ITALIC
=
Typeface
.
ITALIC
;
private
static
final
float
DEFAULT_CUE_LINE
=
CUE_LINE_MAP
[
4
];
// Row 11
private
static
final
float
DEFAULT_INDENT
=
INDENT_MAP
[
0
];
// Indent 0
private
final
LinkedList
<
SubtitleInputBuffer
>
availableInputBuffers
;
private
final
LinkedList
<
SubtitleOutputBuffer
>
availableOutputBuffers
;
private
final
TreeSet
<
SubtitleInputBuffer
>
queuedInputBuffers
;
private
final
ParsableByteArray
ccData
;
private
final
SpannableStringBuilder
captionStringBuilder
;
private
final
LinkedList
<
Eia608CueBuilder
>
cues
;
private
long
playbackPositionUs
;
...
...
@@ -235,17 +208,17 @@ public final class Eia608Decoder implements SubtitleDecoder {
private
int
captionMode
;
private
int
captionRowCount
;
boolean
nextRowDown
;
private
LinkedList
<
Cue
>
cues
;
private
HashMap
<
Integer
,
CharacterStyle
>
captionStyles
;
float
cueIndent
;
float
cueLine
;
int
tabOffset
;
// The Cue that's currently being built and decoded.
private
Eia608CueBuilder
currentCue
;
private
boolean
repeatableControlSet
;
private
byte
repeatableControlCc1
;
private
byte
repeatableControlCc2
;
private
Eia608Subtitle
subtitle
;
public
Eia608Decoder
()
{
availableInputBuffers
=
new
LinkedList
<>();
for
(
int
i
=
0
;
i
<
NUM_INPUT_BUFFERS
;
i
++)
{
...
...
@@ -258,15 +231,12 @@ public final class Eia608Decoder implements SubtitleDecoder {
queuedInputBuffers
=
new
TreeSet
<>();
ccData
=
new
ParsableByteArray
();
c
aptionStringBuilder
=
new
SpannableStringBuilder
();
captionStyles
=
new
HashMap
<>()
;
subtitle
=
new
Eia608Subtitle
();
c
ues
=
new
LinkedList
<>
();
nextRowDown
=
false
;
setCaptionMode
(
CC_MODE_UNKNOWN
);
captionRowCount
=
DEFAULT_CAPTIONS_ROW_COUNT
;
cueIndent
=
DEFAULT_INDENT
;
cueLine
=
DEFAULT_CUE_LINE
;
tabOffset
=
0
;
}
@Override
...
...
@@ -322,14 +292,11 @@ public final class Eia608Decoder implements SubtitleDecoder {
decode
(
inputBuffer
);
// check if we have any caption updates to report
if
(!
cues
.
isEmpty
())
{
if
(!
inputBuffer
.
isDecodeOnly
())
{
SubtitleOutputBuffer
outputBuffer
=
availableOutputBuffers
.
pollFirst
();
outputBuffer
.
setContent
(
inputBuffer
.
timeUs
,
new
Eia608Subtitle
(
cues
),
0
);
cues
=
new
LinkedList
<>();
releaseInputBuffer
(
inputBuffer
);
return
outputBuffer
;
}
if
(!
inputBuffer
.
isDecodeOnly
())
{
SubtitleOutputBuffer
outputBuffer
=
availableOutputBuffers
.
pollFirst
();
outputBuffer
.
setContent
(
inputBuffer
.
timeUs
,
subtitle
,
0
);
releaseInputBuffer
(
inputBuffer
);
return
outputBuffer
;
}
releaseInputBuffer
(
inputBuffer
);
...
...
@@ -353,11 +320,9 @@ public final class Eia608Decoder implements SubtitleDecoder {
setCaptionMode
(
CC_MODE_UNKNOWN
);
captionRowCount
=
DEFAULT_CAPTIONS_ROW_COUNT
;
playbackPositionUs
=
0
;
flushCaptionBuilder
();
cues
=
new
LinkedList
<>();
cueIndent
=
DEFAULT_INDENT
;
cueLine
=
DEFAULT_CUE_LINE
;
tabOffset
=
0
;
currentCue
=
new
Eia608CueBuilder
();
cues
.
clear
();
nextRowDown
=
false
;
repeatableControlSet
=
false
;
repeatableControlCc1
=
0
;
repeatableControlCc2
=
0
;
...
...
@@ -393,23 +358,23 @@ public final class Eia608Decoder implements SubtitleDecoder {
// Special North American character set.
// ccData2 - P|0|1|1|X|X|X|X
if
((
ccData1
==
0x11
||
ccData1
==
0x19
)
&&
((
ccData2
&
0x70
)
==
0x30
))
{
c
aptionStringBuilder
.
append
(
getSpecialChar
(
ccData2
));
c
urrentCue
.
append
(
getSpecialChar
(
ccData2
));
continue
;
}
// Extended Spanish/Miscellaneous and French character set.
// ccData2 - P|0|1|X|X|X|X|X
if
((
ccData1
==
0x12
||
ccData1
==
0x1A
)
&&
((
ccData2
&
0x60
)
==
0x20
))
{
backspace
();
// Remove standard equivalent of the special extended char.
c
aptionStringBuilder
.
append
(
getExtendedEsFrChar
(
ccData2
));
currentCue
.
backspace
();
// Remove standard equivalent of the special extended char.
c
urrentCue
.
append
(
getExtendedEsFrChar
(
ccData2
));
continue
;
}
// Extended Portuguese and German/Danish character set.
// ccData2 - P|0|1|X|X|X|X|X
if
((
ccData1
==
0x13
||
ccData1
==
0x1B
)
&&
((
ccData2
&
0x60
)
==
0x20
))
{
backspace
();
// Remove standard equivalent of the special extended char.
c
aptionStringBuilder
.
append
(
getExtendedPtDeChar
(
ccData2
));
currentCue
.
backspace
();
// Remove standard equivalent of the special extended char.
c
urrentCue
.
append
(
getExtendedPtDeChar
(
ccData2
));
continue
;
}
...
...
@@ -425,9 +390,9 @@ public final class Eia608Decoder implements SubtitleDecoder {
}
// Basic North American character set.
c
aptionStringBuilder
.
append
(
getChar
(
ccData1
));
c
urrentCue
.
append
(
getChar
(
ccData1
));
if
(
ccData2
>=
0x20
)
{
c
aptionStringBuilder
.
append
(
getChar
(
ccData2
));
c
urrentCue
.
append
(
getChar
(
ccData2
));
}
}
...
...
@@ -435,8 +400,8 @@ public final class Eia608Decoder implements SubtitleDecoder {
if
(!
isRepeatableControl
)
{
repeatableControlSet
=
false
;
}
if
(
captionMode
==
CC_MODE_
ROLL_UP
||
captionMode
==
CC_MODE_PAINT_ON
)
{
buildCue
();
if
(
captionMode
==
CC_MODE_
PAINT_ON
||
captionMode
==
CC_MODE_ROLL_UP
)
{
renderCues
();
}
}
}
...
...
@@ -456,7 +421,7 @@ public final class Eia608Decoder implements SubtitleDecoder {
if
(
isMiscCode
(
cc1
,
cc2
))
{
handleMiscCode
(
cc2
);
}
else
if
(
isPreambleAddressCode
(
cc1
,
cc2
))
{
handlePreambleCode
(
cc1
,
cc2
);
handlePreamble
Address
Code
(
cc1
,
cc2
);
}
else
if
(
isTabOffset
(
cc1
,
cc2
))
{
handleTabOffset
(
cc2
);
}
...
...
@@ -492,65 +457,75 @@ public final class Eia608Decoder implements SubtitleDecoder {
switch
(
cc2
)
{
case
CTRL_ERASE_DISPLAYED_MEMORY:
if
(
captionMode
==
CC_MODE_ROLL_UP
||
captionMode
==
CC_MODE_PAINT_ON
)
{
flushCaptionBuilder
();
currentCue
=
new
Eia608CueBuilder
();
cues
.
clear
();
}
subtitle
.
setCues
(
Collections
.<
Cue
>
emptyList
());
return
;
case
CTRL_ERASE_NON_DISPLAYED_MEMORY:
flushCaptionBuilder
();
currentCue
=
new
Eia608CueBuilder
();
cues
.
clear
();
return
;
case
CTRL_END_OF_CAPTION:
buildCue
();
flushCaptionBuilde
r
();
renderCues
();
cues
.
clea
r
();
return
;
case
CTRL_CARRIAGE_RETURN:
maybeAppendNewline
();
// Each time a Carriage Return is received, the text in the top row of the window is erased
// from memory and from the display. The remaining rows of text are each rolled up into the
// next highest row in the window, leaving the base row blank and ready to accept new text.
if
(
captionMode
==
CC_MODE_ROLL_UP
)
{
for
(
Eia608CueBuilder
cue
:
cues
)
{
// Roll up all the other rows.
if
(!
cue
.
rollUp
())
{
cues
.
remove
(
cue
);
}
}
currentCue
=
new
Eia608CueBuilder
();
cues
.
add
(
currentCue
);
while
(
cues
.
size
()
>
captionRowCount
)
{
cues
.
pollFirst
();
}
}
return
;
case
CTRL_BACKSPACE:
backspace
();
currentCue
.
backspace
();
return
;
case
CTRL_DELETE_TO_END_OF_ROW:
// TODO: Clear currentCue's captionText.
return
;
}
}
private
void
handlePreambleCode
(
byte
cc1
,
byte
cc2
)
{
private
void
handlePreamble
Address
Code
(
byte
cc1
,
byte
cc2
)
{
// For PAC layout see: https://en.wikipedia.org/wiki/EIA-608#Control_commands
applySpans
();
// Apply any open spans.
// Parse the "next row down" flag.
boolean
nextRowDown
=
(
cc2
&
0x20
)
!=
0
;
if
(
nextRowDown
)
{
// TODO: We should create a new cue instead, this may cause issues when
// the new line receives it's own PAC which we ignore currently.
// As a result of that the new line will be positioned directly below the
// previous line.
maybeAppendNewline
();
// Parse the "next row down" toggle.
nextRowDown
=
(
cc2
&
0x20
)
!=
0
;
int
row
=
ROW_INDICES
[
cc1
&
0x7
];
if
(
row
!=
currentCue
.
getRow
()
||
nextRowDown
)
{
currentCue
=
new
Eia608CueBuilder
();
cues
.
add
(
currentCue
);
}
currentCue
.
setRow
(
nextRowDown
?
++
row
:
row
);
// Go through the bits, starting with the last bit - the underline flag:
boolean
underline
=
(
cc2
&
0x1
)
!=
0
;
if
(
underline
)
{
setCharacterStyle
(
new
UnderlineSpan
());
currentCue
.
setCharacterStyle
(
new
UnderlineSpan
());
}
// Next, parse the attribute bits:
int
attribute
=
cc2
>>
1
&
0xF
;
if
(
attribute
>=
0x0
&&
attribute
<
0x7
)
{
// Attribute is a foreground color
setCharacterStyle
(
new
ForegroundColorSpan
(
COLOR_MAP
[
attribute
]));
currentCue
.
setCharacterStyle
(
new
ForegroundColorSpan
(
COLOR_MAP
[
attribute
]));
}
else
if
(
attribute
==
0x7
)
{
// Attribute is "italics"
setCharacterStyle
(
new
StyleSpan
(
STYLE_ITALIC
));
currentCue
.
setCharacterStyle
(
new
StyleSpan
(
STYLE_ITALIC
));
}
else
if
(
attribute
>=
0x8
&&
attribute
<=
0xF
)
{
// Attribute is an indent
if
(
cueIndent
==
DEFAULT_INDENT
)
{
// Only update the indent, if it's the default indent.
// This is not conform the spec, but otherwise indentations may be off
// because we don't create a new cue when we see the nextRowDown flag.
cueIndent
=
INDENT_MAP
[
attribute
&
0x7
];
}
currentCue
.
setIndent
(
CURSOR_INDICES
[
attribute
&
0x7
]);
}
// Parse the row bits
cueLine
=
CUE_LINE_MAP
[
cc1
&
0x7
];
}
private
void
handleMidrowCode
(
byte
cc1
,
byte
cc2
)
{
...
...
@@ -558,152 +533,20 @@ public final class Eia608Decoder implements SubtitleDecoder {
int
attribute
=
cc2
>>
1
&
0xF
;
if
((
cc1
&
0x1
)
!=
0
)
{
// Background Color
setCharacterStyle
(
new
BackgroundColorSpan
(
transparentOrUnderline
?
currentCue
.
setCharacterStyle
(
new
BackgroundColorSpan
(
transparentOrUnderline
?
COLOR_MAP
[
attribute
]
&
TRANSPARENCY_MASK
:
COLOR_MAP
[
attribute
]));
}
else
{
// Foreground color
setCharacterStyle
(
new
ForegroundColorSpan
(
COLOR_MAP
[
attribute
]));
currentCue
.
setCharacterStyle
(
new
ForegroundColorSpan
(
COLOR_MAP
[
attribute
]));
if
(
transparentOrUnderline
)
{
// Text should be underlined
setCharacterStyle
(
new
UnderlineSpan
());
currentCue
.
setCharacterStyle
(
new
UnderlineSpan
());
}
}
}
private
void
handleTabOffset
(
byte
cc2
)
{
// Formula for tab offset handling adapted from:
// https://dvcs.w3.org/hg/text-tracks/raw-file/default/608toVTT/608toVTT.html#x1-preamble-address-code-pac
// We're ignoring any tab offsets that do not occur at the beginning of a new cue.
// This is not conform the spec, but works in most cases.
if
(
captionStringBuilder
.
length
()
==
0
)
{
tabOffset
=
cc2
-
0x20
;
}
}
private
int
getSpanStartIndex
()
{
return
captionStringBuilder
.
length
()
>
0
?
captionStringBuilder
.
length
()
-
1
:
0
;
}
/**
* Sets a character style at the current cueIndex.
* Takes care of style priorities.
*
* @param style the style to set.
*/
private
void
setCharacterStyle
(
CharacterStyle
style
)
{
int
startIndex
=
getSpanStartIndex
();
// Close all open spans of the same type, and add a new one.
if
(
style
instanceof
ForegroundColorSpan
)
{
// Setting a foreground color clears the italics style.
applySpan
(
StyleSpan
.
class
);
//
}
applySpan
(
style
.
getClass
());
captionStyles
.
put
(
startIndex
,
style
);
}
/**
* Closes all open spans of the spansToApply class.
* @param spansToClose the class of which the spans should be closed.
*/
private
void
applySpan
(
Class
<?
extends
CharacterStyle
>
spansToClose
)
{
for
(
Integer
index
:
captionStyles
.
keySet
())
{
CharacterStyle
style
=
captionStyles
.
get
(
index
);
if
(
spansToClose
.
isInstance
(
style
))
{
if
(
index
<
captionStringBuilder
.
length
())
{
captionStringBuilder
.
setSpan
(
style
,
index
,
captionStringBuilder
.
length
(),
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
captionStyles
.
remove
(
index
);
}
}
}
/**
* Applies all currently opened spans to the SpannableStringBuilder.
*/
private
void
applySpans
()
{
// Check if we have to do anything.
if
(
captionStyles
.
size
()
==
0
)
{
return
;
}
for
(
Integer
startIndex
:
captionStyles
.
keySet
())
{
// There may be cases, e.g. when seeking where the startIndex becomes greater
// than what is actually in the string builder, in that case, just discard the span.
if
(
startIndex
<
captionStringBuilder
.
length
())
{
CharacterStyle
captionStyle
=
captionStyles
.
get
(
startIndex
);
captionStringBuilder
.
setSpan
(
captionStyle
,
startIndex
,
captionStringBuilder
.
length
(),
Spanned
.
SPAN_EXCLUSIVE_EXCLUSIVE
);
}
captionStyles
.
remove
(
startIndex
);
}
}
/**
* Builds a cue from whatever is in the SpannableStringBuilder now.
*/
private
void
buildCue
()
{
applySpans
();
// Apply Spans
CharSequence
captionString
=
getDisplayCaption
();
if
(
captionString
!=
null
)
{
cueIndent
=
tabOffset
*
2.5f
+
cueIndent
;
tabOffset
=
0
;
Cue
cue
=
new
Cue
(
captionString
,
Layout
.
Alignment
.
ALIGN_NORMAL
,
cueLine
/
100
,
Cue
.
LINE_TYPE_FRACTION
,
Cue
.
ANCHOR_TYPE_START
,
cueIndent
/
100
,
Cue
.
TYPE_UNSET
,
Cue
.
DIMEN_UNSET
);
cues
.
add
(
cue
);
if
(
captionMode
==
CC_MODE_POP_ON
)
{
captionStringBuilder
.
clear
();
captionStringBuilder
.
clearSpans
();
cueLine
=
DEFAULT_CUE_LINE
;
}
cueIndent
=
DEFAULT_INDENT
;
}
}
private
void
flushCaptionBuilder
()
{
captionStringBuilder
.
clear
();
captionStringBuilder
.
clearSpans
();
}
private
void
backspace
()
{
if
(
captionStringBuilder
.
length
()
>
0
)
{
captionStringBuilder
.
delete
(
captionStringBuilder
.
length
()
-
1
,
captionStringBuilder
.
length
());
}
}
private
void
maybeAppendNewline
()
{
int
buildLength
=
captionStringBuilder
.
length
();
if
(
buildLength
>
0
&&
captionStringBuilder
.
charAt
(
buildLength
-
1
)
!=
'\n'
)
{
captionStringBuilder
.
append
(
'\n'
);
}
}
private
CharSequence
getDisplayCaption
()
{
int
buildLength
=
captionStringBuilder
.
length
();
if
(
buildLength
==
0
)
{
return
null
;
}
boolean
endsWithNewline
=
captionStringBuilder
.
charAt
(
buildLength
-
1
)
==
'\n'
;
if
(
buildLength
==
1
&&
endsWithNewline
)
{
return
null
;
}
int
endIndex
=
endsWithNewline
?
buildLength
-
1
:
buildLength
;
if
(
captionMode
!=
CC_MODE_ROLL_UP
)
{
return
captionStringBuilder
.
subSequence
(
0
,
endIndex
);
}
int
startIndex
=
0
;
int
searchBackwardFromIndex
=
endIndex
;
for
(
int
i
=
0
;
i
<
captionRowCount
&&
searchBackwardFromIndex
!=
-
1
;
i
++)
{
searchBackwardFromIndex
=
captionStringBuilder
.
toString
().
lastIndexOf
(
"\n"
,
searchBackwardFromIndex
-
1
);
}
if
(
searchBackwardFromIndex
!=
-
1
)
{
startIndex
=
searchBackwardFromIndex
+
1
;
}
captionStringBuilder
.
delete
(
0
,
startIndex
);
return
captionStringBuilder
.
subSequence
(
0
,
endIndex
-
startIndex
);
currentCue
.
tab
(
cc2
-
0x20
);
}
private
void
setCaptionMode
(
int
captionMode
)
{
...
...
@@ -713,14 +556,17 @@ public final class Eia608Decoder implements SubtitleDecoder {
this
.
captionMode
=
captionMode
;
// Clear the working memory.
captionStringBuilder
.
clear
();
captionStringBuilder
.
clearSpans
();
currentCue
=
new
Eia608CueBuilder
();
if
(
captionMode
==
CC_MODE_ROLL_UP
||
captionMode
==
CC_MODE_UNKNOWN
)
{
// When switching to roll-up or unknown, we also need to clear the caption.
cues
=
new
LinkedList
<>
();
cues
.
clear
();
}
}
private
void
renderCues
()
{
subtitle
.
setCues
(
Eia608CueBuilder
.
buildCues
(
cues
));
}
private
static
char
getChar
(
byte
ccData
)
{
int
index
=
(
ccData
&
0x7F
)
-
0x20
;
return
(
char
)
BASIC_CHARACTER_SET
[
index
];
...
...
library/src/main/java/com/google/android/exoplayer2/text/eia608/Eia608Subtitle.java
View file @
71d83f3e
...
...
@@ -18,6 +18,7 @@ package com.google.android.exoplayer2.text.eia608;
import
com.google.android.exoplayer2.text.Cue
;
import
com.google.android.exoplayer2.text.Subtitle
;
import
java.util.LinkedList
;
import
java.util.List
;
/**
...
...
@@ -25,9 +26,13 @@ import java.util.List;
*/
/* package */
final
class
Eia608Subtitle
implements
Subtitle
{
private
final
List
<
Cue
>
cues
;
private
List
<
Cue
>
cues
;
public
Eia608Subtitle
(
List
<
Cue
>
cues
)
{
public
Eia608Subtitle
()
{
cues
=
new
LinkedList
<>();
}
/* package */
void
setCues
(
List
<
Cue
>
cues
)
{
this
.
cues
=
cues
;
}
...
...
library/src/main/java/com/google/android/exoplayer2/ui/SubtitlePainter.java
View file @
71d83f3e
...
...
@@ -15,6 +15,10 @@
*/
package
com
.
google
.
android
.
exoplayer2
.
ui
;
import
com.google.android.exoplayer2.text.CaptionStyleCompat
;
import
com.google.android.exoplayer2.text.Cue
;
import
com.google.android.exoplayer2.util.Util
;
import
android.content.Context
;
import
android.content.res.Resources
;
import
android.content.res.TypedArray
;
...
...
@@ -30,9 +34,6 @@ import android.text.TextPaint;
import
android.text.TextUtils
;
import
android.util.DisplayMetrics
;
import
android.util.Log
;
import
com.google.android.exoplayer2.text.CaptionStyleCompat
;
import
com.google.android.exoplayer2.text.Cue
;
import
com.google.android.exoplayer2.util.Util
;
/**
* Paints subtitle {@link Cue}s.
...
...
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