Simplify PGS captions + sync with internal tree

This commit is contained in:
Oliver Woodman 2018-01-15 14:13:30 +00:00
parent dc38e86945
commit 141f3aa836
9 changed files with 317 additions and 336 deletions

View file

@ -37,13 +37,14 @@
HLS source to finish preparation without downloading any chunks, which can
significantly reduce initial buffering time
([#3149](https://github.com/google/ExoPlayer/issues/3149)).
* DefaultTrackSelector: Replace `DefaultTrackSelector.Parameters` copy methods
with a builder.
* DefaultTrackSelector: Support disabling of individual text track selection
flags.
* DefaultTrackSelector:
* Replace `DefaultTrackSelector.Parameters` copy methods with a builder.
* Support disabling of individual text track selection flags.
* New Cast extension: Simplifies toggling between local and Cast playbacks.
* Audio: Support TrueHD passthrough for rechunked samples in Matroska files
([#2147](https://github.com/google/ExoPlayer/issues/2147)).
* Captions: Initial support for PGS subtitles
([#3008](https://github.com/google/ExoPlayer/issues/3008)).
* CacheDataSource: Check periodically if it's possible to read from/write to
cache after deciding to bypass cache.
* IMA extension: Add support for playing non-Extractor content MediaSources in

View file

@ -540,7 +540,7 @@
{
"name": "VMAP pre-, mid- and post-rolls, single ads",
"uri": "https://storage.googleapis.com/exoplayer-test-media-1/mkv/android-screens-lavf-56.36.100-aac-avc-main-1280x720.mkv",
"ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&cmsid=496&vid=short_onecue&correlator="
"ad_tag_uri": "https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dlinear&correlator="
},
{
"name": "VMAP pre-roll single ad, mid-roll standard pod with 3 ads, post-roll single ad",

View file

@ -29,6 +29,7 @@ VP9_EXT_PATH="${EXOPLAYER_ROOT}/extensions/vp9/src/main"
```
* Download the [Android NDK][] and set its location in an environment variable.
```
NDK_PATH="<path to Android NDK>"
```

View file

@ -102,7 +102,10 @@ for i in $(seq 0 ${limit}); do
# configure and make
echo "build_android_configs: "
echo "configure ${config[${i}]} ${common_params}"
../../libvpx/configure ${config[${i}]} ${common_params} --extra-cflags="-isystem $ndk/sysroot/usr/include/arm-linux-androideabi -isystem $ndk/sysroot/usr/include"
../../libvpx/configure ${config[${i}]} ${common_params} --extra-cflags=" \
-isystem $ndk/sysroot/usr/include/arm-linux-androideabi \
-isystem $ndk/sysroot/usr/include \
"
rm -f libvpx_srcs.txt
for f in ${allowed_files}; do
# the build system supports multiple different configurations. avoid

View file

@ -53,67 +53,69 @@ public interface SubtitleDecoderFactory {
/**
* Default {@link SubtitleDecoderFactory} implementation.
* <p>
* The formats supported by this factory are:
*
* <p>The formats supported by this factory are:
*
* <ul>
* <li>WebVTT ({@link WebvttDecoder})</li>
* <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>
* <li>DVB ({@link DvbDecoder})</li>
* <li>WebVTT ({@link WebvttDecoder})
* <li>WebVTT (MP4) ({@link Mp4WebvttDecoder})
* <li>TTML ({@link TtmlDecoder})
* <li>SubRip ({@link SubripDecoder})
* <li>SSA/ASS ({@link SsaDecoder})
* <li>TX3G ({@link Tx3gDecoder})
* <li>Cea608 ({@link Cea608Decoder})
* <li>Cea708 ({@link Cea708Decoder})
* <li>DVB ({@link DvbDecoder})
* <li>PGS ({@link PgsDecoder})
* </ul>
*/
SubtitleDecoderFactory DEFAULT = new SubtitleDecoderFactory() {
SubtitleDecoderFactory DEFAULT =
new SubtitleDecoderFactory() {
@Override
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)
|| MimeTypes.APPLICATION_TX3G.equals(mimeType)
|| MimeTypes.APPLICATION_CEA608.equals(mimeType)
|| MimeTypes.APPLICATION_MP4CEA608.equals(mimeType)
|| MimeTypes.APPLICATION_CEA708.equals(mimeType)
|| MimeTypes.APPLICATION_DVBSUBS.equals(mimeType)
|| MimeTypes.APPLICATION_PGS.equals(mimeType);
}
@Override
public SubtitleDecoder createDecoder(Format format) {
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:
return new TtmlDecoder();
case MimeTypes.APPLICATION_SUBRIP:
return new SubripDecoder();
case MimeTypes.APPLICATION_TX3G:
return new Tx3gDecoder(format.initializationData);
case MimeTypes.APPLICATION_CEA608:
case MimeTypes.APPLICATION_MP4CEA608:
return new Cea608Decoder(format.sampleMimeType, format.accessibilityChannel);
case MimeTypes.APPLICATION_CEA708:
return new Cea708Decoder(format.accessibilityChannel);
case MimeTypes.APPLICATION_DVBSUBS:
return new DvbDecoder(format.initializationData);
case MimeTypes.APPLICATION_PGS:
return new PgsDecoder();
default:
throw new IllegalArgumentException("Attempted to create decoder for unsupported format");
}
}
};
@Override
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)
|| MimeTypes.APPLICATION_TX3G.equals(mimeType)
|| MimeTypes.APPLICATION_CEA608.equals(mimeType)
|| MimeTypes.APPLICATION_MP4CEA608.equals(mimeType)
|| MimeTypes.APPLICATION_CEA708.equals(mimeType)
|| MimeTypes.APPLICATION_DVBSUBS.equals(mimeType)
|| MimeTypes.APPLICATION_PGS.equals(mimeType);
}
@Override
public SubtitleDecoder createDecoder(Format format) {
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:
return new TtmlDecoder();
case MimeTypes.APPLICATION_SUBRIP:
return new SubripDecoder();
case MimeTypes.APPLICATION_TX3G:
return new Tx3gDecoder(format.initializationData);
case MimeTypes.APPLICATION_CEA608:
case MimeTypes.APPLICATION_MP4CEA608:
return new Cea608Decoder(format.sampleMimeType, format.accessibilityChannel);
case MimeTypes.APPLICATION_CEA708:
return new Cea708Decoder(format.accessibilityChannel);
case MimeTypes.APPLICATION_DVBSUBS:
return new DvbDecoder(format.initializationData);
case MimeTypes.APPLICATION_PGS:
return new PgsDecoder();
default:
throw new IllegalArgumentException(
"Attempted to create decoder for unsupported format");
}
}
};
}

View file

@ -19,9 +19,7 @@ import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.util.List;
/**
* A {@link SimpleSubtitleDecoder} for DVB Subtitles.
*/
/** A {@link SimpleSubtitleDecoder} for DVB subtitles. */
public final class DvbDecoder extends SimpleSubtitleDecoder {
private final DvbParser parser;

View file

@ -1,232 +0,0 @@
/*
*
* Sources for this implementation PGS decoding can be founder below
*
* http://exar.ch/suprip/hddvd.php
* http://forum.doom9.org/showthread.php?t=124105
* http://www.equasys.de/colorconversion.html
*/
package com.google.android.exoplayer2.text.pgs;
import android.graphics.Bitmap;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.Subtitle;
import com.google.android.exoplayer2.util.ParsableByteArray;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class PgsBuilder {
private static final int SECTION_PALETTE = 0x14;
private static final int SECTION_BITMAP_PICTURE = 0x15;
private static final int SECTION_IDENTIFIER = 0x16;
private static final int SECTION_END = 0x80;
private List<Holder> list = new ArrayList<>();
private Holder holder = new Holder();
boolean readNextSection(ParsableByteArray buffer) {
if (buffer.bytesLeft() < 3)
return false;
int sectionId = buffer.readUnsignedByte();
int sectionLength = buffer.readUnsignedShort();
switch(sectionId) {
case SECTION_PALETTE:
holder.parsePaletteIndexes(buffer, sectionLength);
break;
case SECTION_BITMAP_PICTURE:
holder.fetchBitmapData(buffer, sectionLength);
break;
case SECTION_IDENTIFIER:
holder.fetchIdentifierData(buffer, sectionLength);
break;
case SECTION_END:
list.add(holder);
holder = new Holder();
break;
default:
buffer.skipBytes(Math.min(sectionLength, buffer.bytesLeft()));
break;
}
return true;
}
public Subtitle build() {
if (list.isEmpty())
return new PgsSubtitle();
Cue[] cues = new Cue[list.size()];
long[] cueStartTimes = new long[list.size()];
int index = 0;
for (Holder curr : list) {
cues[index] = curr.build();
cueStartTimes[index++] = curr.start_time;
}
return new PgsSubtitle(cues, cueStartTimes);
}
private class Holder {
private int[] colors = null;
private ByteBuffer rle = null;
Bitmap bitmap = null;
int plane_width = 0;
int plane_height = 0;
int bitmap_width = 0;
int bitmap_height = 0;
public int x = 0;
public int y = 0;
long start_time = 0;
public Cue build() {
if (rle == null || !createBitmap(new ParsableByteArray(rle.array(), rle.position())))
return null;
float left = (float) x / plane_width;
float top = (float) y / plane_height;
return new Cue(bitmap, left, Cue.ANCHOR_TYPE_START, top, Cue.ANCHOR_TYPE_START,
(float) bitmap_width / plane_width, (float) bitmap_height / plane_height);
}
private void parsePaletteIndexes(ParsableByteArray buffer, int dataSize) {
// must be a multi of 5 for index, y, cb, cr, alpha
if (dataSize == 0 || (dataSize - 2) % 5 != 0)
return;
// skip first two bytes
buffer.skipBytes(2);
dataSize -= 2;
colors = new int[256];
while (dataSize > 0) {
int index = buffer.readUnsignedByte();
int color_y = buffer.readUnsignedByte() - 16;
int color_cr = buffer.readUnsignedByte() - 128;
int color_cb = buffer.readUnsignedByte() - 128;
int color_alpha = buffer.readUnsignedByte();
dataSize -= 5;
if (index >= colors.length)
continue;
int color_r = (int) Math.min(Math.max(Math.round(1.1644 * color_y + 1.793 * color_cr), 0), 255);
int color_g = (int) Math.min(Math.max(Math.round(1.1644 * color_y + (-0.213 * color_cr) + (-0.533 * color_cb)), 0), 255);
int color_b = (int) Math.min(Math.max(Math.round(1.1644 * color_y + 2.112 * color_cb), 0), 255);
//ARGB_8888
colors[index] = (color_alpha << 24) | (color_r << 16) | (color_g << 8) | color_b;
}
}
private void fetchBitmapData(ParsableByteArray buffer, int dataSize) {
if (dataSize <= 4) {
buffer.skipBytes(dataSize);
return;
}
// skip id field (2 bytes)
// skip version field
buffer.skipBytes(3);
dataSize -= 3;
// check to see if this section is an appended section of the base section with
// width and height values
dataSize -= 1; // decrement first
if ((0x80 & buffer.readUnsignedByte()) > 0) {
if (dataSize < 3) {
buffer.skipBytes(dataSize);
return;
}
int full_len = buffer.readUnsignedInt24();
dataSize -= 3;
if (full_len <= 4) {
buffer.skipBytes(dataSize);
return;
}
bitmap_width = buffer.readUnsignedShort();
dataSize -= 2;
bitmap_height = buffer.readUnsignedShort();
dataSize -= 2;
rle = ByteBuffer.allocate(full_len - 4); // don't include width & height
buffer.readBytes(rle, Math.min(dataSize, rle.capacity()));
} else if (rle != null) {
int postSkip = dataSize > rle.capacity() ? dataSize - rle.capacity() : 0;
buffer.readBytes(rle, Math.min(dataSize, rle.capacity()));
buffer.skipBytes(postSkip);
}
}
private void fetchIdentifierData(ParsableByteArray buffer, int dataSize) {
if (dataSize < 4) {
buffer.skipBytes(dataSize);
return;
}
plane_width = buffer.readUnsignedShort();
plane_height = buffer.readUnsignedShort();
dataSize -= 4;
if (dataSize < 15) {
buffer.skipBytes(dataSize);
return;
}
// skip next 11 bytes
buffer.skipBytes(11);
x = buffer.readUnsignedShort();
y = buffer.readUnsignedShort();
dataSize -= 15;
buffer.skipBytes(dataSize);
}
private boolean createBitmap(ParsableByteArray rle) {
if (bitmap_width == 0 || bitmap_height == 0
|| rle == null || rle.bytesLeft() == 0
|| colors == null || colors.length == 0)
return false;
int[] argb = new int[bitmap_width * bitmap_height];
int currPixel = 0;
int nextbits, pixel_code, switchbits;
int number_of_pixels;
int line = 0;
while (rle.bytesLeft() > 0 && line < bitmap_height) {
boolean end_of_line = false;
do {
nextbits = rle.readUnsignedByte();
if (nextbits != 0) {
pixel_code = nextbits;
number_of_pixels = 1;
} else {
switchbits = rle.readUnsignedByte();
if ((switchbits & 0x80) == 0) {
pixel_code = 0;
if ((switchbits & 0x40) == 0) {
if (switchbits > 0) {
number_of_pixels = switchbits;
} else {
end_of_line = true;
++line;
continue;
}
} else {
number_of_pixels = ((switchbits & 0x3f) << 8) | rle.readUnsignedByte();
}
} else {
if ((switchbits & 0x40) == 0) {
number_of_pixels = switchbits & 0x3f;
pixel_code = rle.readUnsignedByte();
} else {
number_of_pixels = ((switchbits & 0x3f) << 8) | rle.readUnsignedByte();
pixel_code = rle.readUnsignedByte();
}
}
}
Arrays.fill(argb, currPixel, currPixel + number_of_pixels, colors[pixel_code]);
currPixel += number_of_pixels;
} while (!end_of_line);
}
bitmap = Bitmap.createBitmap(argb, 0, bitmap_width, bitmap_width, bitmap_height, Bitmap.Config.ARGB_8888);
return bitmap != null;
}
}
}

View file

@ -1,26 +1,237 @@
/*
* Copyright (C) 2018 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.pgs;
import android.graphics.Bitmap;
import com.google.android.exoplayer2.text.Cue;
import com.google.android.exoplayer2.text.SimpleSubtitleDecoder;
import com.google.android.exoplayer2.text.Subtitle;
import com.google.android.exoplayer2.text.SubtitleDecoderException;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@SuppressWarnings("unused")
public class PgsDecoder extends SimpleSubtitleDecoder {
/** A {@link SimpleSubtitleDecoder} for PGS subtitles. */
public final class PgsDecoder extends SimpleSubtitleDecoder {
private static final int SECTION_TYPE_PALETTE = 0x14;
private static final int SECTION_TYPE_BITMAP_PICTURE = 0x15;
private static final int SECTION_TYPE_IDENTIFIER = 0x16;
private static final int SECTION_TYPE_END = 0x80;
private final ParsableByteArray buffer;
private final CueBuilder cueBuilder;
@SuppressWarnings("unused")
public PgsDecoder() {
super("PgsDecoder");
buffer = new ParsableByteArray();
cueBuilder = new CueBuilder();
}
@Override
protected Subtitle decode(byte[] data, int size, boolean reset) throws SubtitleDecoderException {
ParsableByteArray buffer = new ParsableByteArray(data, size);
PgsBuilder builder = new PgsBuilder();
do {
if (!builder.readNextSection(buffer))
buffer.reset(data, size);
cueBuilder.reset();
ArrayList<Cue> cues = new ArrayList<>();
while (buffer.bytesLeft() >= 3) {
Cue cue = readNextSection(buffer, cueBuilder);
if (cue != null) {
cues.add(cue);
}
}
return new PgsSubtitle(Collections.unmodifiableList(cues));
}
private static Cue readNextSection(ParsableByteArray buffer, CueBuilder cueBuilder) {
int limit = buffer.limit();
int sectionType = buffer.readUnsignedByte();
int sectionLength = buffer.readUnsignedShort();
int nextSectionPosition = buffer.getPosition() + sectionLength;
if (nextSectionPosition > limit) {
buffer.setPosition(limit);
return null;
}
Cue cue = null;
switch (sectionType) {
case SECTION_TYPE_PALETTE:
cueBuilder.parsePaletteSection(buffer, sectionLength);
break;
} while (buffer.bytesLeft() > 0);
return builder.build();
case SECTION_TYPE_BITMAP_PICTURE:
cueBuilder.parseBitmapSection(buffer, sectionLength);
break;
case SECTION_TYPE_IDENTIFIER:
cueBuilder.parseIdentifierSection(buffer, sectionLength);
break;
case SECTION_TYPE_END:
cue = cueBuilder.build();
cueBuilder.reset();
break;
default:
break;
}
buffer.setPosition(nextSectionPosition);
return cue;
}
private static final class CueBuilder {
private final ParsableByteArray bitmapData;
private final int[] colors;
private boolean colorsSet;
private int planeWidth;
private int planeHeight;
private int bitmapX;
private int bitmapY;
private int bitmapWidth;
private int bitmapHeight;
public CueBuilder() {
bitmapData = new ParsableByteArray();
colors = new int[256];
}
private void parsePaletteSection(ParsableByteArray buffer, int sectionLength) {
if ((sectionLength % 5) != 2) {
// Section must be two bytes followed by a whole number of (index, y, cb, cr, a) entries.
return;
}
buffer.skipBytes(2);
Arrays.fill(colors, 0);
int entryCount = sectionLength / 5;
for (int i = 0; i < entryCount; i++) {
int index = buffer.readUnsignedByte();
int y = buffer.readUnsignedByte();
int cr = buffer.readUnsignedByte();
int cb = buffer.readUnsignedByte();
int a = buffer.readUnsignedByte();
int r = (int) (y + (1.40200 * (cr - 128)));
int g = (int) (y - (0.34414 * (cb - 128)) - (0.71414 * (cr - 128)));
int b = (int) (y + (1.77200 * (cb - 128)));
colors[index] =
(a << 24)
| (Util.constrainValue(r, 0, 255) << 16)
| (Util.constrainValue(g, 0, 255) << 8)
| Util.constrainValue(b, 0, 255);
}
colorsSet = true;
}
private void parseBitmapSection(ParsableByteArray buffer, int sectionLength) {
if (sectionLength < 4) {
return;
}
buffer.skipBytes(3); // Id (2 bytes), version (1 byte).
boolean isBaseSection = (0x80 & buffer.readUnsignedByte()) != 0;
sectionLength -= 4;
if (isBaseSection) {
if (sectionLength < 7) {
return;
}
int totalLength = buffer.readUnsignedInt24() - 4;
if (totalLength < 4) {
return;
}
bitmapWidth = buffer.readUnsignedShort();
bitmapHeight = buffer.readUnsignedShort();
bitmapData.reset(totalLength - 4);
sectionLength -= 7;
}
int position = bitmapData.getPosition();
int limit = bitmapData.limit();
if (position < limit && sectionLength > 0) {
int bytesToRead = Math.min(sectionLength, limit - position);
buffer.readBytes(bitmapData.data, position, bytesToRead);
bitmapData.setPosition(position + bytesToRead);
}
}
private void parseIdentifierSection(ParsableByteArray buffer, int sectionLength) {
if (sectionLength < 19) {
return;
}
planeWidth = buffer.readUnsignedShort();
planeHeight = buffer.readUnsignedShort();
buffer.skipBytes(11);
bitmapX = buffer.readUnsignedShort();
bitmapY = buffer.readUnsignedShort();
}
public Cue build() {
if (planeWidth == 0
|| planeHeight == 0
|| bitmapWidth == 0
|| bitmapHeight == 0
|| bitmapData.limit() == 0
|| bitmapData.getPosition() != bitmapData.limit()
|| !colorsSet) {
return null;
}
// Build the bitmapData.
bitmapData.setPosition(0);
int[] argbBitmapData = new int[bitmapWidth * bitmapHeight];
int argbBitmapDataIndex = 0;
while (argbBitmapDataIndex < argbBitmapData.length) {
int colorIndex = bitmapData.readUnsignedByte();
if (colorIndex != 0) {
argbBitmapData[argbBitmapDataIndex++] = colors[colorIndex];
} else {
int switchBits = bitmapData.readUnsignedByte();
if (switchBits != 0) {
int runLength =
(switchBits & 0x40) == 0
? (switchBits & 0x3F)
: (((switchBits & 0x3F) << 8) | bitmapData.readUnsignedByte());
int color = (switchBits & 0x80) == 0 ? 0 : colors[bitmapData.readUnsignedByte()];
Arrays.fill(
argbBitmapData, argbBitmapDataIndex, argbBitmapDataIndex + runLength, color);
argbBitmapDataIndex += runLength;
}
}
}
Bitmap bitmap =
Bitmap.createBitmap(argbBitmapData, bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
// Build the cue.
return new Cue(
bitmap,
(float) bitmapX / planeWidth,
Cue.ANCHOR_TYPE_START,
(float) bitmapY / planeHeight,
Cue.ANCHOR_TYPE_START,
(float) bitmapWidth / planeWidth,
(float) bitmapHeight / planeHeight);
}
public void reset() {
planeWidth = 0;
planeHeight = 0;
bitmapX = 0;
bitmapY = 0;
bitmapWidth = 0;
bitmapHeight = 0;
bitmapData.reset(0);
colorsSet = false;
}
}
}

View file

@ -1,54 +1,51 @@
/*
* Copyright (C) 2018 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.pgs;
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;
public class PgsSubtitle implements Subtitle {
/** A representation of a PGS subtitle. */
/* package */ final class PgsSubtitle implements Subtitle {
private final Cue[] cues;
private final long[] cueTimesUs;
private final List<Cue> cues;
PgsSubtitle() {
this.cues = null;
this.cueTimesUs = new long[0];
}
PgsSubtitle(Cue[] cues, long[] cueTimesUs) {
public PgsSubtitle(List<Cue> cues) {
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 : -1;
return C.INDEX_UNSET;
}
@Override
public int getEventTimeCount() {
return cueTimesUs.length;
}
return 1;
}
@Override
public long getEventTime(int index) {
Assertions.checkArgument(index >= 0);
Assertions.checkArgument(index < cueTimesUs.length);
return cueTimesUs[index];
return 0;
}
@Override
public List<Cue> getCues(long timeUs) {
int index = Util.binarySearchFloor(cueTimesUs, timeUs, true, false);
if (index == -1 || cues == null || 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]);
return cues;
}
}